1/* 2 * linux/arch/s390/kernel/semaphore.c 3 * 4 * S390 version 5 * Copyright (C) 1998-2000 IBM Corporation 6 * Author(s): Martin Schwidefsky 7 * 8 * Derived from "linux/arch/i386/kernel/semaphore.c 9 * Copyright (C) 1999, Linus Torvalds 10 * 11 */ 12#include <linux/sched.h> 13#include <linux/errno.h> 14#include <linux/init.h> 15 16#include <asm/semaphore.h> 17 18/* 19 * Atomically update sem->count. Equivalent to: 20 * old_val = sem->count.counter; 21 * new_val = ((old_val >= 0) ? old_val : 0) + incr; 22 * sem->count.counter = new_val; 23 * return old_val; 24 */ 25static inline int __sem_update_count(struct semaphore *sem, int incr) 26{ 27 int old_val, new_val; 28 29 asm volatile( 30 " l %0,0(%3)\n" 31 "0: ltr %1,%0\n" 32 " jhe 1f\n" 33 " lhi %1,0\n" 34 "1: ar %1,%4\n" 35 " cs %0,%1,0(%3)\n" 36 " jl 0b\n" 37 : "=&d" (old_val), "=&d" (new_val), "=m" (sem->count) 38 : "a" (&sem->count), "d" (incr), "m" (sem->count) 39 : "cc"); 40 return old_val; 41} 42 43/* 44 * The inline function up() incremented count but the result 45 * was <= 0. This indicates that some process is waiting on 46 * the semaphore. The semaphore is free and we'll wake the 47 * first sleeping process, so we set count to 1 unless some 48 * other cpu has called up in the meantime in which case 49 * we just increment count by 1. 50 */ 51void __up(struct semaphore *sem) 52{ 53 __sem_update_count(sem, 1); 54 wake_up(&sem->wait); 55} 56 57/* 58 * The inline function down() decremented count and the result 59 * was < 0. The wait loop will atomically test and update the 60 * semaphore counter following the rules: 61 * count > 0: decrement count, wake up queue and exit. 62 * count <= 0: set count to -1, go to sleep. 63 */ 64void __sched __down(struct semaphore * sem) 65{ 66 struct task_struct *tsk = current; 67 DECLARE_WAITQUEUE(wait, tsk); 68 69 __set_task_state(tsk, TASK_UNINTERRUPTIBLE); 70 add_wait_queue_exclusive(&sem->wait, &wait); 71 while (__sem_update_count(sem, -1) <= 0) { 72 schedule(); 73 set_task_state(tsk, TASK_UNINTERRUPTIBLE); 74 } 75 remove_wait_queue(&sem->wait, &wait); 76 __set_task_state(tsk, TASK_RUNNING); 77 wake_up(&sem->wait); 78} 79 80/* 81 * Same as __down() with an additional test for signals. 82 * If a signal is pending the count is updated as follows: 83 * count > 0: wake up queue and exit. 84 * count <= 0: set count to 0, wake up queue and exit. 85 */ 86int __sched __down_interruptible(struct semaphore * sem) 87{ 88 int retval = 0; 89 struct task_struct *tsk = current; 90 DECLARE_WAITQUEUE(wait, tsk); 91 92 __set_task_state(tsk, TASK_INTERRUPTIBLE); 93 add_wait_queue_exclusive(&sem->wait, &wait); 94 while (__sem_update_count(sem, -1) <= 0) { 95 if (signal_pending(current)) { 96 __sem_update_count(sem, 0); 97 retval = -EINTR; 98 break; 99 } 100 schedule(); 101 set_task_state(tsk, TASK_INTERRUPTIBLE); 102 } 103 remove_wait_queue(&sem->wait, &wait); 104 __set_task_state(tsk, TASK_RUNNING); 105 wake_up(&sem->wait); 106 return retval; 107} 108