Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | */ |
| 25 | static inline int __sem_update_count(struct semaphore *sem, int incr) |
| 26 | { |
| 27 | int old_val, new_val; |
| 28 | |
| 29 | __asm__ __volatile__(" l %0,0(%3)\n" |
| 30 | "0: ltr %1,%0\n" |
| 31 | " jhe 1f\n" |
| 32 | " lhi %1,0\n" |
| 33 | "1: ar %1,%4\n" |
| 34 | " cs %0,%1,0(%3)\n" |
| 35 | " jl 0b\n" |
| 36 | : "=&d" (old_val), "=&d" (new_val), |
| 37 | "=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 | */ |
| 51 | void __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 | */ |
| 64 | void __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 | */ |
| 86 | int __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 | |