linux/kernel/locking/semaphore.c
<<
>>
Prefs
   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
  36static noinline void __down(struct semaphore *sem);
  37static noinline int __down_interruptible(struct semaphore *sem);
  38static noinline int __down_killable(struct semaphore *sem);
  39static noinline int __down_timeout(struct semaphore *sem, long timeout);
  40static 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 */
  53void down(struct semaphore *sem)
  54{
  55        unsigned long flags;
  56
  57        raw_spin_lock_irqsave(&sem->lock, flags);
  58        if (likely(sem->count > 0))
  59                sem->count--;
  60        else
  61                __down(sem);
  62        raw_spin_unlock_irqrestore(&sem->lock, flags);
  63}
  64EXPORT_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 */
  75int down_interruptible(struct semaphore *sem)
  76{
  77        unsigned long flags;
  78        int result = 0;
  79
  80        raw_spin_lock_irqsave(&sem->lock, flags);
  81        if (likely(sem->count > 0))
  82                sem->count--;
  83        else
  84                result = __down_interruptible(sem);
  85        raw_spin_unlock_irqrestore(&sem->lock, flags);
  86
  87        return result;
  88}
  89EXPORT_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 */
 101int down_killable(struct semaphore *sem)
 102{
 103        unsigned long flags;
 104        int result = 0;
 105
 106        raw_spin_lock_irqsave(&sem->lock, flags);
 107        if (likely(sem->count > 0))
 108                sem->count--;
 109        else
 110                result = __down_killable(sem);
 111        raw_spin_unlock_irqrestore(&sem->lock, flags);
 112
 113        return result;
 114}
 115EXPORT_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 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 */
 130int down_trylock(struct semaphore *sem)
 131{
 132        unsigned long flags;
 133        int count;
 134
 135        raw_spin_lock_irqsave(&sem->lock, flags);
 136        count = sem->count - 1;
 137        if (likely(count >= 0))
 138                sem->count = count;
 139        raw_spin_unlock_irqrestore(&sem->lock, flags);
 140
 141        return (count < 0);
 142}
 143EXPORT_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 */
 155int down_timeout(struct semaphore *sem, long timeout)
 156{
 157        unsigned long flags;
 158        int result = 0;
 159
 160        raw_spin_lock_irqsave(&sem->lock, flags);
 161        if (likely(sem->count > 0))
 162                sem->count--;
 163        else
 164                result = __down_timeout(sem, timeout);
 165        raw_spin_unlock_irqrestore(&sem->lock, flags);
 166
 167        return result;
 168}
 169EXPORT_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 */
 178void up(struct semaphore *sem)
 179{
 180        unsigned long flags;
 181
 182        raw_spin_lock_irqsave(&sem->lock, flags);
 183        if (likely(list_empty(&sem->wait_list)))
 184                sem->count++;
 185        else
 186                __up(sem);
 187        raw_spin_unlock_irqrestore(&sem->lock, flags);
 188}
 189EXPORT_SYMBOL(up);
 190
 191/* Functions for the contended case */
 192
 193struct 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 */
 204static inline int __sched __down_common(struct semaphore *sem, long state,
 205                                                                long timeout)
 206{
 207        struct semaphore_waiter waiter;
 208
 209        list_add_tail(&waiter.list, &sem->wait_list);
 210        waiter.task = current;
 211        waiter.up = false;
 212
 213        for (;;) {
 214                if (signal_pending_state(state, current))
 215                        goto interrupted;
 216                if (unlikely(timeout <= 0))
 217                        goto timed_out;
 218                __set_current_state(state);
 219                raw_spin_unlock_irq(&sem->lock);
 220                timeout = schedule_timeout(timeout);
 221                raw_spin_lock_irq(&sem->lock);
 222                if (waiter.up)
 223                        return 0;
 224        }
 225
 226 timed_out:
 227        list_del(&waiter.list);
 228        return -ETIME;
 229
 230 interrupted:
 231        list_del(&waiter.list);
 232        return -EINTR;
 233}
 234
 235static noinline void __sched __down(struct semaphore *sem)
 236{
 237        __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
 238}
 239
 240static noinline int __sched __down_interruptible(struct semaphore *sem)
 241{
 242        return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
 243}
 244
 245static noinline int __sched __down_killable(struct semaphore *sem)
 246{
 247        return __down_common(sem, TASK_KILLABLE, MAX_SCHEDULE_TIMEOUT);
 248}
 249
 250static noinline int __sched __down_timeout(struct semaphore *sem, long timeout)
 251{
 252        return __down_common(sem, TASK_UNINTERRUPTIBLE, timeout);
 253}
 254
 255static noinline void __sched __up(struct semaphore *sem)
 256{
 257        struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
 258                                                struct semaphore_waiter, list);
 259        list_del(&waiter->list);
 260        waiter->up = true;
 261        wake_up_process(waiter->task);
 262}
 263