cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

semaphore.c (7733B)


      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
     37static noinline void __down(struct semaphore *sem);
     38static noinline int __down_interruptible(struct semaphore *sem);
     39static noinline int __down_killable(struct semaphore *sem);
     40static noinline int __down_timeout(struct semaphore *sem, long timeout);
     41static 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 */
     54void down(struct semaphore *sem)
     55{
     56	unsigned long flags;
     57
     58	might_sleep();
     59	raw_spin_lock_irqsave(&sem->lock, flags);
     60	if (likely(sem->count > 0))
     61		sem->count--;
     62	else
     63		__down(sem);
     64	raw_spin_unlock_irqrestore(&sem->lock, flags);
     65}
     66EXPORT_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 */
     77int down_interruptible(struct semaphore *sem)
     78{
     79	unsigned long flags;
     80	int result = 0;
     81
     82	might_sleep();
     83	raw_spin_lock_irqsave(&sem->lock, flags);
     84	if (likely(sem->count > 0))
     85		sem->count--;
     86	else
     87		result = __down_interruptible(sem);
     88	raw_spin_unlock_irqrestore(&sem->lock, flags);
     89
     90	return result;
     91}
     92EXPORT_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 */
    104int down_killable(struct semaphore *sem)
    105{
    106	unsigned long flags;
    107	int result = 0;
    108
    109	might_sleep();
    110	raw_spin_lock_irqsave(&sem->lock, flags);
    111	if (likely(sem->count > 0))
    112		sem->count--;
    113	else
    114		result = __down_killable(sem);
    115	raw_spin_unlock_irqrestore(&sem->lock, flags);
    116
    117	return result;
    118}
    119EXPORT_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 */
    134int down_trylock(struct semaphore *sem)
    135{
    136	unsigned long flags;
    137	int count;
    138
    139	raw_spin_lock_irqsave(&sem->lock, flags);
    140	count = sem->count - 1;
    141	if (likely(count >= 0))
    142		sem->count = count;
    143	raw_spin_unlock_irqrestore(&sem->lock, flags);
    144
    145	return (count < 0);
    146}
    147EXPORT_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 */
    159int down_timeout(struct semaphore *sem, long timeout)
    160{
    161	unsigned long flags;
    162	int result = 0;
    163
    164	might_sleep();
    165	raw_spin_lock_irqsave(&sem->lock, flags);
    166	if (likely(sem->count > 0))
    167		sem->count--;
    168	else
    169		result = __down_timeout(sem, timeout);
    170	raw_spin_unlock_irqrestore(&sem->lock, flags);
    171
    172	return result;
    173}
    174EXPORT_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 */
    183void up(struct semaphore *sem)
    184{
    185	unsigned long flags;
    186
    187	raw_spin_lock_irqsave(&sem->lock, flags);
    188	if (likely(list_empty(&sem->wait_list)))
    189		sem->count++;
    190	else
    191		__up(sem);
    192	raw_spin_unlock_irqrestore(&sem->lock, flags);
    193}
    194EXPORT_SYMBOL(up);
    195
    196/* Functions for the contended case */
    197
    198struct 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 */
    209static inline int __sched ___down_common(struct semaphore *sem, long state,
    210								long timeout)
    211{
    212	struct semaphore_waiter waiter;
    213
    214	list_add_tail(&waiter.list, &sem->wait_list);
    215	waiter.task = current;
    216	waiter.up = false;
    217
    218	for (;;) {
    219		if (signal_pending_state(state, current))
    220			goto interrupted;
    221		if (unlikely(timeout <= 0))
    222			goto timed_out;
    223		__set_current_state(state);
    224		raw_spin_unlock_irq(&sem->lock);
    225		timeout = schedule_timeout(timeout);
    226		raw_spin_lock_irq(&sem->lock);
    227		if (waiter.up)
    228			return 0;
    229	}
    230
    231 timed_out:
    232	list_del(&waiter.list);
    233	return -ETIME;
    234
    235 interrupted:
    236	list_del(&waiter.list);
    237	return -EINTR;
    238}
    239
    240static inline int __sched __down_common(struct semaphore *sem, long state,
    241					long timeout)
    242{
    243	int ret;
    244
    245	trace_contention_begin(sem, 0);
    246	ret = ___down_common(sem, state, timeout);
    247	trace_contention_end(sem, ret);
    248
    249	return ret;
    250}
    251
    252static noinline void __sched __down(struct semaphore *sem)
    253{
    254	__down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
    255}
    256
    257static noinline int __sched __down_interruptible(struct semaphore *sem)
    258{
    259	return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
    260}
    261
    262static noinline int __sched __down_killable(struct semaphore *sem)
    263{
    264	return __down_common(sem, TASK_KILLABLE, MAX_SCHEDULE_TIMEOUT);
    265}
    266
    267static noinline int __sched __down_timeout(struct semaphore *sem, long timeout)
    268{
    269	return __down_common(sem, TASK_UNINTERRUPTIBLE, timeout);
    270}
    271
    272static noinline void __sched __up(struct semaphore *sem)
    273{
    274	struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
    275						struct semaphore_waiter, list);
    276	list_del(&waiter->list);
    277	waiter->up = true;
    278	wake_up_process(waiter->task);
    279}