LCOV - code coverage report
Current view: top level - kernel/locking - semaphore.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 23 88 26.1 %
Date: 2021-04-22 12:43:58 Functions: 3 12 25.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-only
       2             : /*
       3             :  * Copyright (c) 2008 Intel Corporation
       4             :  * Author: Matthew Wilcox <willy@linux.intel.com>
       5             :  *
       6             :  * This file implements counting semaphores.
       7             :  * A counting semaphore may be acquired 'n' times before sleeping.
       8             :  * See mutex.c for single-acquisition sleeping locks which enforce
       9             :  * rules which allow code to be debugged more easily.
      10             :  */
      11             : 
      12             : /*
      13             :  * Some notes on the implementation:
      14             :  *
      15             :  * The spinlock controls access to the other members of the semaphore.
      16             :  * down_trylock() and up() can be called from interrupt context, so we
      17             :  * have to disable interrupts when taking the lock.  It turns out various
      18             :  * parts of the kernel expect to be able to use down() on a semaphore in
      19             :  * interrupt context when they know it will succeed, so we have to use
      20             :  * irqsave variants for down(), down_interruptible() and down_killable()
      21             :  * too.
      22             :  *
      23             :  * The ->count variable represents how many more tasks can acquire this
      24             :  * semaphore.  If it's zero, there may be tasks waiting on the wait_list.
      25             :  */
      26             : 
      27             : #include <linux/compiler.h>
      28             : #include <linux/kernel.h>
      29             : #include <linux/export.h>
      30             : #include <linux/sched.h>
      31             : #include <linux/sched/debug.h>
      32             : #include <linux/semaphore.h>
      33             : #include <linux/spinlock.h>
      34             : #include <linux/ftrace.h>
      35             : 
      36             : static noinline void __down(struct semaphore *sem);
      37             : static noinline int __down_interruptible(struct semaphore *sem);
      38             : static noinline int __down_killable(struct semaphore *sem);
      39             : static noinline int __down_timeout(struct semaphore *sem, long timeout);
      40             : static noinline void __up(struct semaphore *sem);
      41             : 
      42             : /**
      43             :  * down - acquire the semaphore
      44             :  * @sem: the semaphore to be acquired
      45             :  *
      46             :  * Acquires the semaphore.  If no more tasks are allowed to acquire the
      47             :  * semaphore, calling this function will put the task to sleep until the
      48             :  * semaphore is released.
      49             :  *
      50             :  * Use of this function is deprecated, please use down_interruptible() or
      51             :  * down_killable() instead.
      52             :  */
      53         215 : void down(struct semaphore *sem)
      54             : {
      55         215 :         unsigned long flags;
      56             : 
      57         215 :         raw_spin_lock_irqsave(&sem->lock, flags);
      58         215 :         if (likely(sem->count > 0))
      59         215 :                 sem->count--;
      60             :         else
      61           0 :                 __down(sem);
      62         215 :         raw_spin_unlock_irqrestore(&sem->lock, flags);
      63         215 : }
      64             : EXPORT_SYMBOL(down);
      65             : 
      66             : /**
      67             :  * down_interruptible - acquire the semaphore unless interrupted
      68             :  * @sem: the semaphore to be acquired
      69             :  *
      70             :  * Attempts to acquire the semaphore.  If no more tasks are allowed to
      71             :  * acquire the semaphore, calling this function will put the task to sleep.
      72             :  * If the sleep is interrupted by a signal, this function will return -EINTR.
      73             :  * If the semaphore is successfully acquired, this function returns 0.
      74             :  */
      75           0 : int down_interruptible(struct semaphore *sem)
      76             : {
      77           0 :         unsigned long flags;
      78           0 :         int result = 0;
      79             : 
      80           0 :         raw_spin_lock_irqsave(&sem->lock, flags);
      81           0 :         if (likely(sem->count > 0))
      82           0 :                 sem->count--;
      83             :         else
      84           0 :                 result = __down_interruptible(sem);
      85           0 :         raw_spin_unlock_irqrestore(&sem->lock, flags);
      86             : 
      87           0 :         return result;
      88             : }
      89             : EXPORT_SYMBOL(down_interruptible);
      90             : 
      91             : /**
      92             :  * down_killable - acquire the semaphore unless killed
      93             :  * @sem: the semaphore to be acquired
      94             :  *
      95             :  * Attempts to acquire the semaphore.  If no more tasks are allowed to
      96             :  * acquire the semaphore, calling this function will put the task to sleep.
      97             :  * If the sleep is interrupted by a fatal signal, this function will return
      98             :  * -EINTR.  If the semaphore is successfully acquired, this function returns
      99             :  * 0.
     100             :  */
     101           0 : int down_killable(struct semaphore *sem)
     102             : {
     103           0 :         unsigned long flags;
     104           0 :         int result = 0;
     105             : 
     106           0 :         raw_spin_lock_irqsave(&sem->lock, flags);
     107           0 :         if (likely(sem->count > 0))
     108           0 :                 sem->count--;
     109             :         else
     110           0 :                 result = __down_killable(sem);
     111           0 :         raw_spin_unlock_irqrestore(&sem->lock, flags);
     112             : 
     113           0 :         return result;
     114             : }
     115             : EXPORT_SYMBOL(down_killable);
     116             : 
     117             : /**
     118             :  * down_trylock - try to acquire the semaphore, without waiting
     119             :  * @sem: the semaphore to be acquired
     120             :  *
     121             :  * Try to acquire the semaphore atomically.  Returns 0 if the semaphore has
     122             :  * been acquired successfully or 1 if it cannot be acquired.
     123             :  *
     124             :  * NOTE: This return value is inverted from both spin_trylock and
     125             :  * mutex_trylock!  Be careful about this when converting code.
     126             :  *
     127             :  * Unlike mutex_trylock, this function can be used from interrupt context,
     128             :  * and the semaphore can be released by any task or interrupt.
     129             :  */
     130         307 : int down_trylock(struct semaphore *sem)
     131             : {
     132         307 :         unsigned long flags;
     133         307 :         int count;
     134             : 
     135         307 :         raw_spin_lock_irqsave(&sem->lock, flags);
     136         307 :         count = sem->count - 1;
     137         307 :         if (likely(count >= 0))
     138         306 :                 sem->count = count;
     139         307 :         raw_spin_unlock_irqrestore(&sem->lock, flags);
     140             : 
     141         307 :         return (count < 0);
     142             : }
     143             : EXPORT_SYMBOL(down_trylock);
     144             : 
     145             : /**
     146             :  * down_timeout - acquire the semaphore within a specified time
     147             :  * @sem: the semaphore to be acquired
     148             :  * @timeout: how long to wait before failing
     149             :  *
     150             :  * Attempts to acquire the semaphore.  If no more tasks are allowed to
     151             :  * acquire the semaphore, calling this function will put the task to sleep.
     152             :  * If the semaphore is not released within the specified number of jiffies,
     153             :  * this function returns -ETIME.  It returns 0 if the semaphore was acquired.
     154             :  */
     155           0 : int down_timeout(struct semaphore *sem, long timeout)
     156             : {
     157           0 :         unsigned long flags;
     158           0 :         int result = 0;
     159             : 
     160           0 :         raw_spin_lock_irqsave(&sem->lock, flags);
     161           0 :         if (likely(sem->count > 0))
     162           0 :                 sem->count--;
     163             :         else
     164           0 :                 result = __down_timeout(sem, timeout);
     165           0 :         raw_spin_unlock_irqrestore(&sem->lock, flags);
     166             : 
     167           0 :         return result;
     168             : }
     169             : EXPORT_SYMBOL(down_timeout);
     170             : 
     171             : /**
     172             :  * up - release the semaphore
     173             :  * @sem: the semaphore to release
     174             :  *
     175             :  * Release the semaphore.  Unlike mutexes, up() may be called from any
     176             :  * context and even by tasks which have never called down().
     177             :  */
     178         521 : void up(struct semaphore *sem)
     179             : {
     180         521 :         unsigned long flags;
     181             : 
     182         521 :         raw_spin_lock_irqsave(&sem->lock, flags);
     183         521 :         if (likely(list_empty(&sem->wait_list)))
     184         521 :                 sem->count++;
     185             :         else
     186           0 :                 __up(sem);
     187         521 :         raw_spin_unlock_irqrestore(&sem->lock, flags);
     188         521 : }
     189             : EXPORT_SYMBOL(up);
     190             : 
     191             : /* Functions for the contended case */
     192             : 
     193             : struct semaphore_waiter {
     194             :         struct list_head list;
     195             :         struct task_struct *task;
     196             :         bool up;
     197             : };
     198             : 
     199             : /*
     200             :  * Because this function is inlined, the 'state' parameter will be
     201             :  * constant, and thus optimised away by the compiler.  Likewise the
     202             :  * 'timeout' parameter for the cases without timeouts.
     203             :  */
     204           0 : static inline int __sched __down_common(struct semaphore *sem, long state,
     205             :                                                                 long timeout)
     206             : {
     207           0 :         struct semaphore_waiter waiter;
     208             : 
     209           0 :         list_add_tail(&waiter.list, &sem->wait_list);
     210           0 :         waiter.task = current;
     211           0 :         waiter.up = false;
     212             : 
     213           0 :         for (;;) {
     214           0 :                 if (signal_pending_state(state, current))
     215           0 :                         goto interrupted;
     216           0 :                 if (unlikely(timeout <= 0))
     217           0 :                         goto timed_out;
     218           0 :                 __set_current_state(state);
     219           0 :                 raw_spin_unlock_irq(&sem->lock);
     220           0 :                 timeout = schedule_timeout(timeout);
     221           0 :                 raw_spin_lock_irq(&sem->lock);
     222           0 :                 if (waiter.up)
     223             :                         return 0;
     224             :         }
     225             : 
     226           0 :  timed_out:
     227           0 :         list_del(&waiter.list);
     228           0 :         return -ETIME;
     229             : 
     230           0 :  interrupted:
     231           0 :         list_del(&waiter.list);
     232           0 :         return -EINTR;
     233             : }
     234             : 
     235           0 : static noinline void __sched __down(struct semaphore *sem)
     236             : {
     237           0 :         __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
     238           0 : }
     239             : 
     240           0 : static noinline int __sched __down_interruptible(struct semaphore *sem)
     241             : {
     242           0 :         return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
     243             : }
     244             : 
     245           0 : static noinline int __sched __down_killable(struct semaphore *sem)
     246             : {
     247           0 :         return __down_common(sem, TASK_KILLABLE, MAX_SCHEDULE_TIMEOUT);
     248             : }
     249             : 
     250           0 : static noinline int __sched __down_timeout(struct semaphore *sem, long timeout)
     251             : {
     252           0 :         return __down_common(sem, TASK_UNINTERRUPTIBLE, timeout);
     253             : }
     254             : 
     255           0 : static noinline void __sched __up(struct semaphore *sem)
     256             : {
     257           0 :         struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
     258             :                                                 struct semaphore_waiter, list);
     259           0 :         list_del(&waiter->list);
     260           0 :         waiter->up = true;
     261           0 :         wake_up_process(waiter->task);
     262           0 : }

Generated by: LCOV version 1.14