LCOV - code coverage report
Current view: top level - kernel/locking - semaphore.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 13 78 16.7 %
Date: 2023-08-24 13:40:31 Functions: 2 12 16.7 %

          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             : #include <trace/events/lock.h>
      36             : 
      37             : static noinline void __down(struct semaphore *sem);
      38             : static noinline int __down_interruptible(struct semaphore *sem);
      39             : static noinline int __down_killable(struct semaphore *sem);
      40             : static noinline int __down_timeout(struct semaphore *sem, long timeout);
      41             : static noinline void __up(struct semaphore *sem);
      42             : 
      43             : /**
      44             :  * down - acquire the semaphore
      45             :  * @sem: the semaphore to be acquired
      46             :  *
      47             :  * Acquires the semaphore.  If no more tasks are allowed to acquire the
      48             :  * semaphore, calling this function will put the task to sleep until the
      49             :  * semaphore is released.
      50             :  *
      51             :  * Use of this function is deprecated, please use down_interruptible() or
      52             :  * down_killable() instead.
      53             :  */
      54           0 : void __sched down(struct semaphore *sem)
      55             : {
      56             :         unsigned long flags;
      57             : 
      58             :         might_sleep();
      59           0 :         raw_spin_lock_irqsave(&sem->lock, flags);
      60           0 :         if (likely(sem->count > 0))
      61           0 :                 sem->count--;
      62             :         else
      63           0 :                 __down(sem);
      64           0 :         raw_spin_unlock_irqrestore(&sem->lock, flags);
      65           0 : }
      66             : EXPORT_SYMBOL(down);
      67             : 
      68             : /**
      69             :  * down_interruptible - acquire the semaphore unless interrupted
      70             :  * @sem: the semaphore to be acquired
      71             :  *
      72             :  * Attempts to acquire the semaphore.  If no more tasks are allowed to
      73             :  * acquire the semaphore, calling this function will put the task to sleep.
      74             :  * If the sleep is interrupted by a signal, this function will return -EINTR.
      75             :  * If the semaphore is successfully acquired, this function returns 0.
      76             :  */
      77           0 : int __sched down_interruptible(struct semaphore *sem)
      78             : {
      79             :         unsigned long flags;
      80           0 :         int result = 0;
      81             : 
      82             :         might_sleep();
      83           0 :         raw_spin_lock_irqsave(&sem->lock, flags);
      84           0 :         if (likely(sem->count > 0))
      85           0 :                 sem->count--;
      86             :         else
      87           0 :                 result = __down_interruptible(sem);
      88           0 :         raw_spin_unlock_irqrestore(&sem->lock, flags);
      89             : 
      90           0 :         return result;
      91             : }
      92             : EXPORT_SYMBOL(down_interruptible);
      93             : 
      94             : /**
      95             :  * down_killable - acquire the semaphore unless killed
      96             :  * @sem: the semaphore to be acquired
      97             :  *
      98             :  * Attempts to acquire the semaphore.  If no more tasks are allowed to
      99             :  * acquire the semaphore, calling this function will put the task to sleep.
     100             :  * If the sleep is interrupted by a fatal signal, this function will return
     101             :  * -EINTR.  If the semaphore is successfully acquired, this function returns
     102             :  * 0.
     103             :  */
     104           0 : int __sched down_killable(struct semaphore *sem)
     105             : {
     106             :         unsigned long flags;
     107           0 :         int result = 0;
     108             : 
     109             :         might_sleep();
     110           0 :         raw_spin_lock_irqsave(&sem->lock, flags);
     111           0 :         if (likely(sem->count > 0))
     112           0 :                 sem->count--;
     113             :         else
     114           0 :                 result = __down_killable(sem);
     115           0 :         raw_spin_unlock_irqrestore(&sem->lock, flags);
     116             : 
     117           0 :         return result;
     118             : }
     119             : EXPORT_SYMBOL(down_killable);
     120             : 
     121             : /**
     122             :  * down_trylock - try to acquire the semaphore, without waiting
     123             :  * @sem: the semaphore to be acquired
     124             :  *
     125             :  * Try to acquire the semaphore atomically.  Returns 0 if the semaphore has
     126             :  * been acquired successfully or 1 if it cannot be acquired.
     127             :  *
     128             :  * NOTE: This return value is inverted from both spin_trylock and
     129             :  * mutex_trylock!  Be careful about this when converting code.
     130             :  *
     131             :  * Unlike mutex_trylock, this function can be used from interrupt context,
     132             :  * and the semaphore can be released by any task or interrupt.
     133             :  */
     134         284 : int __sched down_trylock(struct semaphore *sem)
     135             : {
     136             :         unsigned long flags;
     137             :         int count;
     138             : 
     139         284 :         raw_spin_lock_irqsave(&sem->lock, flags);
     140         284 :         count = sem->count - 1;
     141         284 :         if (likely(count >= 0))
     142         284 :                 sem->count = count;
     143         568 :         raw_spin_unlock_irqrestore(&sem->lock, flags);
     144             : 
     145         284 :         return (count < 0);
     146             : }
     147             : EXPORT_SYMBOL(down_trylock);
     148             : 
     149             : /**
     150             :  * down_timeout - acquire the semaphore within a specified time
     151             :  * @sem: the semaphore to be acquired
     152             :  * @timeout: how long to wait before failing
     153             :  *
     154             :  * Attempts to acquire the semaphore.  If no more tasks are allowed to
     155             :  * acquire the semaphore, calling this function will put the task to sleep.
     156             :  * If the semaphore is not released within the specified number of jiffies,
     157             :  * this function returns -ETIME.  It returns 0 if the semaphore was acquired.
     158             :  */
     159           0 : int __sched down_timeout(struct semaphore *sem, long timeout)
     160             : {
     161             :         unsigned long flags;
     162           0 :         int result = 0;
     163             : 
     164             :         might_sleep();
     165           0 :         raw_spin_lock_irqsave(&sem->lock, flags);
     166           0 :         if (likely(sem->count > 0))
     167           0 :                 sem->count--;
     168             :         else
     169           0 :                 result = __down_timeout(sem, timeout);
     170           0 :         raw_spin_unlock_irqrestore(&sem->lock, flags);
     171             : 
     172           0 :         return result;
     173             : }
     174             : EXPORT_SYMBOL(down_timeout);
     175             : 
     176             : /**
     177             :  * up - release the semaphore
     178             :  * @sem: the semaphore to release
     179             :  *
     180             :  * Release the semaphore.  Unlike mutexes, up() may be called from any
     181             :  * context and even by tasks which have never called down().
     182             :  */
     183         284 : void __sched up(struct semaphore *sem)
     184             : {
     185             :         unsigned long flags;
     186             : 
     187         284 :         raw_spin_lock_irqsave(&sem->lock, flags);
     188         568 :         if (likely(list_empty(&sem->wait_list)))
     189         284 :                 sem->count++;
     190             :         else
     191           0 :                 __up(sem);
     192         568 :         raw_spin_unlock_irqrestore(&sem->lock, flags);
     193         284 : }
     194             : EXPORT_SYMBOL(up);
     195             : 
     196             : /* Functions for the contended case */
     197             : 
     198             : struct semaphore_waiter {
     199             :         struct list_head list;
     200             :         struct task_struct *task;
     201             :         bool up;
     202             : };
     203             : 
     204             : /*
     205             :  * Because this function is inlined, the 'state' parameter will be
     206             :  * constant, and thus optimised away by the compiler.  Likewise the
     207             :  * 'timeout' parameter for the cases without timeouts.
     208             :  */
     209           0 : static inline int __sched ___down_common(struct semaphore *sem, long state,
     210             :                                                                 long timeout)
     211             : {
     212             :         struct semaphore_waiter waiter;
     213             : 
     214           0 :         list_add_tail(&waiter.list, &sem->wait_list);
     215           0 :         waiter.task = current;
     216           0 :         waiter.up = false;
     217             : 
     218             :         for (;;) {
     219           0 :                 if (signal_pending_state(state, current))
     220             :                         goto interrupted;
     221           0 :                 if (unlikely(timeout <= 0))
     222             :                         goto timed_out;
     223           0 :                 __set_current_state(state);
     224           0 :                 raw_spin_unlock_irq(&sem->lock);
     225           0 :                 timeout = schedule_timeout(timeout);
     226           0 :                 raw_spin_lock_irq(&sem->lock);
     227           0 :                 if (waiter.up)
     228             :                         return 0;
     229             :         }
     230             : 
     231             :  timed_out:
     232           0 :         list_del(&waiter.list);
     233           0 :         return -ETIME;
     234             : 
     235             :  interrupted:
     236           0 :         list_del(&waiter.list);
     237           0 :         return -EINTR;
     238             : }
     239             : 
     240             : static inline int __sched __down_common(struct semaphore *sem, long state,
     241             :                                         long timeout)
     242             : {
     243             :         int ret;
     244             : 
     245           0 :         trace_contention_begin(sem, 0);
     246           0 :         ret = ___down_common(sem, state, timeout);
     247           0 :         trace_contention_end(sem, ret);
     248             : 
     249             :         return ret;
     250             : }
     251             : 
     252           0 : static noinline void __sched __down(struct semaphore *sem)
     253             : {
     254           0 :         __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
     255           0 : }
     256             : 
     257           0 : static noinline int __sched __down_interruptible(struct semaphore *sem)
     258             : {
     259           0 :         return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
     260             : }
     261             : 
     262           0 : static noinline int __sched __down_killable(struct semaphore *sem)
     263             : {
     264           0 :         return __down_common(sem, TASK_KILLABLE, MAX_SCHEDULE_TIMEOUT);
     265             : }
     266             : 
     267           0 : static noinline int __sched __down_timeout(struct semaphore *sem, long timeout)
     268             : {
     269           0 :         return __down_common(sem, TASK_UNINTERRUPTIBLE, timeout);
     270             : }
     271             : 
     272           0 : static noinline void __sched __up(struct semaphore *sem)
     273             : {
     274           0 :         struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
     275             :                                                 struct semaphore_waiter, list);
     276           0 :         list_del(&waiter->list);
     277           0 :         waiter->up = true;
     278           0 :         wake_up_process(waiter->task);
     279           0 : }

Generated by: LCOV version 1.14