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