ba35a1f7ee2b2ec51349c92f56f267b9c35356db
[openafs.git] / src / rx / LINUX / rx_kmutex.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * rx_kmutex.c - mutex and condition variable macros for kernel environment.
12  *
13  * Linux implementation.
14  */
15
16 #include <afsconfig.h>
17 #include "afs/param.h"
18
19
20 #include "rx/rx_kcommon.h"
21 #include "rx_kmutex.h"
22 #include "rx/rx_kernel.h"
23
24 void
25 afs_mutex_init(afs_kmutex_t * l)
26 {
27 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
28     mutex_init(&l->mutex);
29 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
30     init_MUTEX(&l->sem);
31 #else
32     l->sem = MUTEX;
33 #endif
34     l->owner = 0;
35 }
36
37 void
38 afs_mutex_enter(afs_kmutex_t * l)
39 {
40 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
41     mutex_lock(&l->mutex);
42 #else
43     down(&l->sem);
44 #endif
45     if (l->owner)
46         osi_Panic("mutex_enter: 0x%lx held by %d", (unsigned long)l, l->owner);
47     l->owner = current->pid;
48 }
49
50 int
51 afs_mutex_tryenter(afs_kmutex_t * l)
52 {
53 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
54     if (mutex_trylock(&l->mutex) == 0)
55 #else
56     if (down_trylock(&l->sem))
57 #endif
58         return 0;
59     l->owner = current->pid;
60     return 1;
61 }
62
63 void
64 afs_mutex_exit(afs_kmutex_t * l)
65 {
66     if (l->owner != current->pid)
67         osi_Panic("mutex_exit: 0x%lx held by %d", (unsigned long)l, l->owner);
68     l->owner = 0;
69 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
70     mutex_unlock(&l->mutex);
71 #else
72     up(&l->sem);
73 #endif
74 }
75
76 /* CV_WAIT and CV_TIMEDWAIT sleep until the specified event occurs, or, in the
77  * case of CV_TIMEDWAIT, until the specified timeout occurs.
78  * - NOTE: that on Linux, there are circumstances in which TASK_INTERRUPTIBLE
79  *   can wake up, even if all signals are blocked
80  * - TODO: handle signals correctly by passing an indication back to the
81  *   caller that the wait has been interrupted and the stack should be cleaned
82  *   up preparatory to signal delivery
83  */
84 int
85 afs_cv_wait(afs_kcondvar_t * cv, afs_kmutex_t * l, int sigok)
86 {
87     int seq, isAFSGlocked = ISAFS_GLOCK();
88     sigset_t saved_set;
89 #ifdef DECLARE_WAITQUEUE
90     DECLARE_WAITQUEUE(wait, current);
91 #else
92     struct wait_queue wait = { current, NULL };
93 #endif
94     sigemptyset(&saved_set);
95     seq = cv->seq;
96     
97     set_current_state(TASK_INTERRUPTIBLE);
98     add_wait_queue(&cv->waitq, &wait);
99
100     if (isAFSGlocked)
101         AFS_GUNLOCK();
102     MUTEX_EXIT(l);
103
104     if (!sigok) {
105         SIG_LOCK(current);
106         saved_set = current->blocked;
107         sigfillset(&current->blocked);
108         RECALC_SIGPENDING(current);
109         SIG_UNLOCK(current);
110     }
111
112     while(seq == cv->seq) {
113         schedule();
114 #ifdef AFS_LINUX26_ENV
115 #ifdef CONFIG_PM
116         if (
117 #ifdef PF_FREEZE
118             current->flags & PF_FREEZE
119 #else
120 #if defined(STRUCT_TASK_STRUCT_HAS_TODO)
121             !current->todo
122 #else
123 #if defined(STRUCT_TASK_STRUCT_HAS_THREAD_INFO)
124             test_ti_thread_flag(current->thread_info, TIF_FREEZE)
125 #else
126             test_ti_thread_flag(task_thread_info(current), TIF_FREEZE)
127 #endif
128 #endif
129 #endif
130             )
131 #ifdef LINUX_REFRIGERATOR_TAKES_PF_FREEZE
132             refrigerator(PF_FREEZE);
133 #else
134             refrigerator();
135 #endif
136             set_current_state(TASK_INTERRUPTIBLE);
137 #endif
138 #endif
139     }
140
141     remove_wait_queue(&cv->waitq, &wait);
142     set_current_state(TASK_RUNNING);
143
144     if (!sigok) {
145         SIG_LOCK(current);
146         current->blocked = saved_set;
147         RECALC_SIGPENDING(current);
148         SIG_UNLOCK(current);
149     }
150
151     if (isAFSGlocked)
152         AFS_GLOCK();
153     MUTEX_ENTER(l);
154
155     return (sigok && signal_pending(current)) ? EINTR : 0;
156 }
157
158 void
159 afs_cv_timedwait(afs_kcondvar_t * cv, afs_kmutex_t * l, int waittime)
160 {
161     int seq, isAFSGlocked = ISAFS_GLOCK();
162     long t = waittime * HZ / 1000;
163 #ifdef DECLARE_WAITQUEUE
164     DECLARE_WAITQUEUE(wait, current);
165 #else
166     struct wait_queue wait = { current, NULL };
167 #endif
168     seq = cv->seq;
169
170     set_current_state(TASK_INTERRUPTIBLE);
171     add_wait_queue(&cv->waitq, &wait);
172
173     if (isAFSGlocked)
174         AFS_GUNLOCK();
175     MUTEX_EXIT(l);
176
177     while(seq == cv->seq) {
178         t = schedule_timeout(t);
179         if (!t)         /* timeout */
180             break;
181     }
182     
183     remove_wait_queue(&cv->waitq, &wait);
184     set_current_state(TASK_RUNNING);
185
186     if (isAFSGlocked)
187         AFS_GLOCK();
188     MUTEX_ENTER(l);
189 }