linux/kernel/semaphore.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2008 Intel Corporation
   3 * Author: Matthew Wilcox <willy@linux.intel.com>
   4 *
   5 * Distributed under the terms of the GNU GPL, version 2
   6 *
   7 * This file implements counting semaphores.
   8 * A counting semaphore may be acquired 'n' times before sleeping.
   9 * See mutex.c for single-acquisition sleeping locks which enforce
  10 * rules which allow code to be debugged more easily.
  11 */
  12
  13/*
  14 * Some notes on the implementation:
  15 *
  16 * The spinlock controls access to the other members of the semaphore.
  17 * down_trylock() and up() can be called from interrupt context, so we
  18 * have to disable interrupts when taking the lock.  It turns out various
  19 * parts of the kernel expect to be able to use down() on a semaphore in
  20 * interrupt context when they know it will succeed, so we have to use
  21 * irqsave variants for down(), down_interruptible() and down_killable()
  22 * too.
  23 *
  24 * The ->count variable represents how many more tasks can acquire this
  25 * semaphore.  If it's zero, there may be tasks waiting on the wait_list.
  26 */
  27
  28#include <linux/compiler.h>
  29#include <linux/kernel.h>
  30#include <linux/export.h>
  31#include <linux/sched.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 jiffies);
  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 * @jiffies: 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 jiffies)
 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, jiffies);
 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 task_struct *task = current;
 208        struct semaphore_waiter waiter;
 209
 210        list_add_tail(&waiter.list, &sem->wait_list);
 211        waiter.task = task;
 212        waiter.up = false;
 213
 214        for (;;) {
 215                if (signal_pending_state(state, task))
 216                        goto interrupted;
 217                if (unlikely(timeout <= 0))
 218                        goto timed_out;
 219                __set_task_state(task, state);
 220                raw_spin_unlock_irq(&sem->lock);
 221                timeout = schedule_timeout(timeout);
 222                raw_spin_lock_irq(&sem->lock);
 223                if (waiter.up)
 224                        return 0;
 225        }
 226
 227 timed_out:
 228        list_del(&waiter.list);
 229        return -ETIME;
 230
 231 interrupted:
 232        list_del(&waiter.list);
 233        return -EINTR;
 234}
 235
 236static noinline void __sched __down(struct semaphore *sem)
 237{
 238        __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
 239}
 240
 241static noinline int __sched __down_interruptible(struct semaphore *sem)
 242{
 243        return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
 244}
 245
 246static noinline int __sched __down_killable(struct semaphore *sem)
 247{
 248        return __down_common(sem, TASK_KILLABLE, MAX_SCHEDULE_TIMEOUT);
 249}
 250
 251static noinline int __sched __down_timeout(struct semaphore *sem, long jiffies)
 252{
 253        return __down_common(sem, TASK_UNINTERRUPTIBLE, jiffies);
 254}
 255
 256static noinline void __sched __up(struct semaphore *sem)
 257{
 258        struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
 259                                                struct semaphore_waiter, list);
 260        list_del(&waiter->list);
 261        waiter->up = true;
 262        wake_up_process(waiter->task);
 263}
 264
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.