/* * linux/arch/s390/kernel/semaphore.c * * S390 version * Copyright (C) 1998-2000 IBM Corporation * Author(s): Martin Schwidefsky * * Derived from "linux/arch/i386/kernel/semaphore.c * Copyright (C) 1999, Linus Torvalds * */ #include <linux/sched.h> #include <linux/errno.h> #include <linux/init.h> #include <asm/semaphore.h> /* * Atomically update sem->count. Equivalent to: * old_val = sem->count.counter; * new_val = ((old_val >= 0) ? old_val : 0) + incr; * sem->count.counter = new_val; * return old_val; */ static inline int __sem_update_count(struct semaphore *sem, int incr) { int old_val, new_val; __asm__ __volatile__(" l %0,0(%3)\n" "0: ltr %1,%0\n" " jhe 1f\n" " lhi %1,0\n" "1: ar %1,%4\n" " cs %0,%1,0(%3)\n" " jl 0b\n" : "=&d" (old_val), "=&d" (new_val), "=m" (sem->count) : "a" (&sem->count), "d" (incr), "m" (sem->count) : "cc" ); return old_val; } /* * The inline function up() incremented count but the result * was <= 0. This indicates that some process is waiting on * the semaphore. The semaphore is free and we'll wake the * first sleeping process, so we set count to 1 unless some * other cpu has called up in the meantime in which case * we just increment count by 1. */ void __up(struct semaphore *sem) { __sem_update_count(sem, 1); wake_up(&sem->wait); } /* * The inline function down() decremented count and the result * was < 0. The wait loop will atomically test and update the * semaphore counter following the rules: * count > 0: decrement count, wake up queue and exit. * count <= 0: set count to -1, go to sleep. */ void __sched __down(struct semaphore * sem) { struct task_struct *tsk = current; DECLARE_WAITQUEUE(wait, tsk); __set_task_state(tsk, TASK_UNINTERRUPTIBLE); add_wait_queue_exclusive(&sem->wait, &wait); while (__sem_update_count(sem, -1) <= 0) { schedule(); set_task_state(tsk, TASK_UNINTERRUPTIBLE); } remove_wait_queue(&sem->wait, &wait); __set_task_state(tsk, TASK_RUNNING); wake_up(&sem->wait); } /* * Same as __down() with an additional test for signals. * If a signal is pending the count is updated as follows: * count > 0: wake up queue and exit. * count <= 0: set count to 0, wake up queue and exit. */ int __sched __down_interruptible(struct semaphore * sem) { int retval = 0; struct task_struct *tsk = current; DECLARE_WAITQUEUE(wait, tsk); __set_task_state(tsk, TASK_INTERRUPTIBLE); add_wait_queue_exclusive(&sem->wait, &wait); while (__sem_update_count(sem, -1) <= 0) { if (signal_pending(current)) { __sem_update_count(sem, 0); retval = -EINTR; break; } schedule(); set_task_state(tsk, TASK_INTERRUPTIBLE); } remove_wait_queue(&sem->wait, &wait); __set_task_state(tsk, TASK_RUNNING); wake_up(&sem->wait); return retval; }