Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | ||
94c12cc7 MS |
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"); | |
1da177e4 LT |
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 |