]>
Commit | Line | Data |
---|---|---|
e7fd4179 DT |
1 | /****************************************************************************** |
2 | ******************************************************************************* | |
3 | ** | |
4 | ** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. | |
7fe2b319 | 5 | ** Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved. |
e7fd4179 DT |
6 | ** |
7 | ** This copyrighted material is made available to anyone wishing to use, | |
8 | ** modify, copy, or redistribute it subject to the terms and conditions | |
9 | ** of the GNU General Public License v.2. | |
10 | ** | |
11 | ******************************************************************************* | |
12 | ******************************************************************************/ | |
13 | ||
14 | #include "dlm_internal.h" | |
15 | #include "lock.h" | |
597d0cae | 16 | #include "user.h" |
8fa1de38 | 17 | #include "ast.h" |
e7fd4179 DT |
18 | |
19 | #define WAKE_ASTS 0 | |
20 | ||
8304d6f2 | 21 | static uint64_t ast_seq_count; |
e7fd4179 DT |
22 | static struct list_head ast_queue; |
23 | static spinlock_t ast_queue_lock; | |
24 | static struct task_struct * astd_task; | |
25 | static unsigned long astd_wakeflags; | |
90135925 | 26 | static struct mutex astd_running; |
e7fd4179 DT |
27 | |
28 | ||
8304d6f2 DT |
29 | static void dlm_dump_lkb_callbacks(struct dlm_lkb *lkb) |
30 | { | |
31 | int i; | |
32 | ||
33 | log_print("last_bast %x %llu flags %x mode %d sb %d %x", | |
34 | lkb->lkb_id, | |
35 | (unsigned long long)lkb->lkb_last_bast.seq, | |
36 | lkb->lkb_last_bast.flags, | |
37 | lkb->lkb_last_bast.mode, | |
38 | lkb->lkb_last_bast.sb_status, | |
39 | lkb->lkb_last_bast.sb_flags); | |
40 | ||
41 | log_print("last_cast %x %llu flags %x mode %d sb %d %x", | |
42 | lkb->lkb_id, | |
43 | (unsigned long long)lkb->lkb_last_cast.seq, | |
44 | lkb->lkb_last_cast.flags, | |
45 | lkb->lkb_last_cast.mode, | |
46 | lkb->lkb_last_cast.sb_status, | |
47 | lkb->lkb_last_cast.sb_flags); | |
48 | ||
49 | for (i = 0; i < DLM_CALLBACKS_SIZE; i++) { | |
50 | log_print("cb %x %llu flags %x mode %d sb %d %x", | |
51 | lkb->lkb_id, | |
52 | (unsigned long long)lkb->lkb_callbacks[i].seq, | |
53 | lkb->lkb_callbacks[i].flags, | |
54 | lkb->lkb_callbacks[i].mode, | |
55 | lkb->lkb_callbacks[i].sb_status, | |
56 | lkb->lkb_callbacks[i].sb_flags); | |
57 | } | |
58 | } | |
59 | ||
e7fd4179 DT |
60 | void dlm_del_ast(struct dlm_lkb *lkb) |
61 | { | |
62 | spin_lock(&ast_queue_lock); | |
8304d6f2 DT |
63 | if (!list_empty(&lkb->lkb_astqueue)) |
64 | list_del_init(&lkb->lkb_astqueue); | |
e7fd4179 DT |
65 | spin_unlock(&ast_queue_lock); |
66 | } | |
67 | ||
8304d6f2 DT |
68 | int dlm_add_lkb_callback(struct dlm_lkb *lkb, uint32_t flags, int mode, |
69 | int status, uint32_t sbflags, uint64_t seq) | |
70 | { | |
71 | struct dlm_ls *ls = lkb->lkb_resource->res_ls; | |
72 | uint64_t prev_seq; | |
73 | int prev_mode; | |
74 | int i; | |
75 | ||
76 | for (i = 0; i < DLM_CALLBACKS_SIZE; i++) { | |
77 | if (lkb->lkb_callbacks[i].seq) | |
78 | continue; | |
79 | ||
80 | /* | |
81 | * Suppress some redundant basts here, do more on removal. | |
82 | * Don't even add a bast if the callback just before it | |
83 | * is a bast for the same mode or a more restrictive mode. | |
84 | * (the addional > PR check is needed for PR/CW inversion) | |
85 | */ | |
86 | ||
87 | if ((i > 0) && (flags & DLM_CB_BAST) && | |
88 | (lkb->lkb_callbacks[i-1].flags & DLM_CB_BAST)) { | |
89 | ||
90 | prev_seq = lkb->lkb_callbacks[i-1].seq; | |
91 | prev_mode = lkb->lkb_callbacks[i-1].mode; | |
92 | ||
93 | if ((prev_mode == mode) || | |
94 | (prev_mode > mode && prev_mode > DLM_LOCK_PR)) { | |
95 | ||
96 | log_debug(ls, "skip %x add bast %llu mode %d " | |
97 | "for bast %llu mode %d", | |
98 | lkb->lkb_id, | |
99 | (unsigned long long)seq, | |
100 | mode, | |
101 | (unsigned long long)prev_seq, | |
102 | prev_mode); | |
103 | return 0; | |
104 | } | |
105 | } | |
106 | ||
107 | lkb->lkb_callbacks[i].seq = seq; | |
108 | lkb->lkb_callbacks[i].flags = flags; | |
109 | lkb->lkb_callbacks[i].mode = mode; | |
110 | lkb->lkb_callbacks[i].sb_status = status; | |
111 | lkb->lkb_callbacks[i].sb_flags = (sbflags & 0x000000FF); | |
112 | break; | |
113 | } | |
114 | ||
115 | if (i == DLM_CALLBACKS_SIZE) { | |
116 | log_error(ls, "no callbacks %x %llu flags %x mode %d sb %d %x", | |
117 | lkb->lkb_id, (unsigned long long)seq, | |
118 | flags, mode, status, sbflags); | |
119 | dlm_dump_lkb_callbacks(lkb); | |
120 | return -1; | |
121 | } | |
122 | ||
123 | return 0; | |
124 | } | |
125 | ||
126 | int dlm_rem_lkb_callback(struct dlm_ls *ls, struct dlm_lkb *lkb, | |
127 | struct dlm_callback *cb, int *resid) | |
e7fd4179 | 128 | { |
8304d6f2 DT |
129 | int i; |
130 | ||
131 | *resid = 0; | |
132 | ||
133 | if (!lkb->lkb_callbacks[0].seq) | |
134 | return -ENOENT; | |
135 | ||
136 | /* oldest undelivered cb is callbacks[0] */ | |
137 | ||
138 | memcpy(cb, &lkb->lkb_callbacks[0], sizeof(struct dlm_callback)); | |
139 | memset(&lkb->lkb_callbacks[0], 0, sizeof(struct dlm_callback)); | |
140 | ||
141 | /* shift others down */ | |
142 | ||
143 | for (i = 1; i < DLM_CALLBACKS_SIZE; i++) { | |
144 | if (!lkb->lkb_callbacks[i].seq) | |
145 | break; | |
146 | memcpy(&lkb->lkb_callbacks[i-1], &lkb->lkb_callbacks[i], | |
147 | sizeof(struct dlm_callback)); | |
148 | memset(&lkb->lkb_callbacks[i], 0, sizeof(struct dlm_callback)); | |
149 | (*resid)++; | |
150 | } | |
151 | ||
152 | /* if cb is a bast, it should be skipped if the blocking mode is | |
153 | compatible with the last granted mode */ | |
154 | ||
155 | if ((cb->flags & DLM_CB_BAST) && lkb->lkb_last_cast.seq) { | |
156 | if (dlm_modes_compat(cb->mode, lkb->lkb_last_cast.mode)) { | |
157 | cb->flags |= DLM_CB_SKIP; | |
158 | ||
159 | log_debug(ls, "skip %x bast %llu mode %d " | |
160 | "for cast %llu mode %d", | |
161 | lkb->lkb_id, | |
162 | (unsigned long long)cb->seq, | |
163 | cb->mode, | |
164 | (unsigned long long)lkb->lkb_last_cast.seq, | |
165 | lkb->lkb_last_cast.mode); | |
166 | return 0; | |
167 | } | |
168 | } | |
169 | ||
170 | if (cb->flags & DLM_CB_CAST) { | |
171 | memcpy(&lkb->lkb_last_cast, cb, sizeof(struct dlm_callback)); | |
172 | lkb->lkb_last_cast_time = ktime_get(); | |
173 | } | |
174 | ||
175 | if (cb->flags & DLM_CB_BAST) { | |
176 | memcpy(&lkb->lkb_last_bast, cb, sizeof(struct dlm_callback)); | |
177 | lkb->lkb_last_bast_time = ktime_get(); | |
178 | } | |
179 | ||
180 | return 0; | |
181 | } | |
182 | ||
183 | void dlm_add_ast(struct dlm_lkb *lkb, uint32_t flags, int mode, int status, | |
184 | uint32_t sbflags) | |
185 | { | |
186 | uint64_t seq; | |
187 | int rv; | |
188 | ||
189 | spin_lock(&ast_queue_lock); | |
190 | ||
191 | seq = ++ast_seq_count; | |
192 | ||
597d0cae | 193 | if (lkb->lkb_flags & DLM_IFL_USER) { |
8304d6f2 DT |
194 | spin_unlock(&ast_queue_lock); |
195 | dlm_user_add_ast(lkb, flags, mode, status, sbflags, seq); | |
597d0cae DT |
196 | return; |
197 | } | |
198 | ||
8304d6f2 DT |
199 | rv = dlm_add_lkb_callback(lkb, flags, mode, status, sbflags, seq); |
200 | if (rv < 0) { | |
201 | spin_unlock(&ast_queue_lock); | |
202 | return; | |
203 | } | |
204 | ||
205 | if (list_empty(&lkb->lkb_astqueue)) { | |
e7fd4179 DT |
206 | kref_get(&lkb->lkb_ref); |
207 | list_add_tail(&lkb->lkb_astqueue, &ast_queue); | |
208 | } | |
e7fd4179 DT |
209 | spin_unlock(&ast_queue_lock); |
210 | ||
211 | set_bit(WAKE_ASTS, &astd_wakeflags); | |
212 | wake_up_process(astd_task); | |
213 | } | |
214 | ||
215 | static void process_asts(void) | |
216 | { | |
217 | struct dlm_ls *ls = NULL; | |
218 | struct dlm_rsb *r = NULL; | |
219 | struct dlm_lkb *lkb; | |
7fe2b319 DT |
220 | void (*castfn) (void *astparam); |
221 | void (*bastfn) (void *astparam, int mode); | |
8304d6f2 DT |
222 | struct dlm_callback callbacks[DLM_CALLBACKS_SIZE]; |
223 | int i, rv, resid; | |
722d7421 AM |
224 | |
225 | repeat: | |
226 | spin_lock(&ast_queue_lock); | |
227 | list_for_each_entry(lkb, &ast_queue, lkb_astqueue) { | |
228 | r = lkb->lkb_resource; | |
229 | ls = r->res_ls; | |
e7fd4179 | 230 | |
722d7421 AM |
231 | if (dlm_locking_stopped(ls)) |
232 | continue; | |
e7fd4179 | 233 | |
8304d6f2 DT |
234 | /* we remove from astqueue list and remove everything in |
235 | lkb_callbacks before releasing the spinlock so empty | |
236 | lkb_astqueue is always consistent with empty lkb_callbacks */ | |
237 | ||
238 | list_del_init(&lkb->lkb_astqueue); | |
239 | ||
7fe2b319 DT |
240 | castfn = lkb->lkb_astfn; |
241 | bastfn = lkb->lkb_bastfn; | |
e7fd4179 | 242 | |
8304d6f2 | 243 | memset(&callbacks, 0, sizeof(callbacks)); |
7fe2b319 | 244 | |
8304d6f2 DT |
245 | for (i = 0; i < DLM_CALLBACKS_SIZE; i++) { |
246 | rv = dlm_rem_lkb_callback(ls, lkb, &callbacks[i], &resid); | |
247 | if (rv < 0) | |
248 | break; | |
249 | } | |
250 | spin_unlock(&ast_queue_lock); | |
7fe2b319 | 251 | |
8304d6f2 DT |
252 | if (resid) { |
253 | /* shouldn't happen, for loop should have removed all */ | |
254 | log_error(ls, "callback resid %d lkb %x", | |
255 | resid, lkb->lkb_id); | |
7fe2b319 DT |
256 | } |
257 | ||
8304d6f2 DT |
258 | for (i = 0; i < DLM_CALLBACKS_SIZE; i++) { |
259 | if (!callbacks[i].seq) | |
260 | break; | |
261 | if (callbacks[i].flags & DLM_CB_SKIP) { | |
262 | continue; | |
263 | } else if (callbacks[i].flags & DLM_CB_BAST) { | |
264 | bastfn(lkb->lkb_astparam, callbacks[i].mode); | |
265 | } else if (callbacks[i].flags & DLM_CB_CAST) { | |
266 | lkb->lkb_lksb->sb_status = callbacks[i].sb_status; | |
267 | lkb->lkb_lksb->sb_flags = callbacks[i].sb_flags; | |
7fe2b319 | 268 | castfn(lkb->lkb_astparam); |
8304d6f2 | 269 | } |
7fe2b319 DT |
270 | } |
271 | ||
8304d6f2 | 272 | /* removes ref for ast_queue, may cause lkb to be freed */ |
e7fd4179 DT |
273 | dlm_put_lkb(lkb); |
274 | ||
d61e9aac | 275 | cond_resched(); |
722d7421 | 276 | goto repeat; |
e7fd4179 | 277 | } |
722d7421 | 278 | spin_unlock(&ast_queue_lock); |
e7fd4179 DT |
279 | } |
280 | ||
281 | static inline int no_asts(void) | |
282 | { | |
283 | int ret; | |
284 | ||
285 | spin_lock(&ast_queue_lock); | |
286 | ret = list_empty(&ast_queue); | |
287 | spin_unlock(&ast_queue_lock); | |
288 | return ret; | |
289 | } | |
290 | ||
291 | static int dlm_astd(void *data) | |
292 | { | |
293 | while (!kthread_should_stop()) { | |
294 | set_current_state(TASK_INTERRUPTIBLE); | |
295 | if (!test_bit(WAKE_ASTS, &astd_wakeflags)) | |
296 | schedule(); | |
297 | set_current_state(TASK_RUNNING); | |
298 | ||
90135925 | 299 | mutex_lock(&astd_running); |
e7fd4179 DT |
300 | if (test_and_clear_bit(WAKE_ASTS, &astd_wakeflags)) |
301 | process_asts(); | |
90135925 | 302 | mutex_unlock(&astd_running); |
e7fd4179 DT |
303 | } |
304 | return 0; | |
305 | } | |
306 | ||
307 | void dlm_astd_wake(void) | |
308 | { | |
309 | if (!no_asts()) { | |
310 | set_bit(WAKE_ASTS, &astd_wakeflags); | |
311 | wake_up_process(astd_task); | |
312 | } | |
313 | } | |
314 | ||
315 | int dlm_astd_start(void) | |
316 | { | |
317 | struct task_struct *p; | |
318 | int error = 0; | |
319 | ||
320 | INIT_LIST_HEAD(&ast_queue); | |
321 | spin_lock_init(&ast_queue_lock); | |
90135925 | 322 | mutex_init(&astd_running); |
e7fd4179 DT |
323 | |
324 | p = kthread_run(dlm_astd, NULL, "dlm_astd"); | |
325 | if (IS_ERR(p)) | |
326 | error = PTR_ERR(p); | |
327 | else | |
328 | astd_task = p; | |
329 | return error; | |
330 | } | |
331 | ||
332 | void dlm_astd_stop(void) | |
333 | { | |
334 | kthread_stop(astd_task); | |
335 | } | |
336 | ||
337 | void dlm_astd_suspend(void) | |
338 | { | |
90135925 | 339 | mutex_lock(&astd_running); |
e7fd4179 DT |
340 | } |
341 | ||
342 | void dlm_astd_resume(void) | |
343 | { | |
90135925 | 344 | mutex_unlock(&astd_running); |
e7fd4179 DT |
345 | } |
346 |