| /* | 
 |  *  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; | 
 | } | 
 |  |