xref: /openbmc/linux/fs/dlm/ast.c (revision 7fe2f639)
1 /******************************************************************************
2 *******************************************************************************
3 **
4 **  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
5 **  Copyright (C) 2004-2010 Red Hat, Inc.  All rights reserved.
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"
16 #include "user.h"
17 #include "ast.h"
18 
19 #define WAKE_ASTS  0
20 
21 static uint64_t			ast_seq_count;
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;
26 static struct mutex		astd_running;
27 
28 
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 
60 void dlm_del_ast(struct dlm_lkb *lkb)
61 {
62 	spin_lock(&ast_queue_lock);
63 	if (!list_empty(&lkb->lkb_astqueue))
64 		list_del_init(&lkb->lkb_astqueue);
65 	spin_unlock(&ast_queue_lock);
66 }
67 
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)
128 {
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 
193 	if (lkb->lkb_flags & DLM_IFL_USER) {
194 		spin_unlock(&ast_queue_lock);
195 		dlm_user_add_ast(lkb, flags, mode, status, sbflags, seq);
196 		return;
197 	}
198 
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)) {
206 		kref_get(&lkb->lkb_ref);
207 		list_add_tail(&lkb->lkb_astqueue, &ast_queue);
208 	}
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;
220 	void (*castfn) (void *astparam);
221 	void (*bastfn) (void *astparam, int mode);
222 	struct dlm_callback callbacks[DLM_CALLBACKS_SIZE];
223 	int i, rv, resid;
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;
230 
231 		if (dlm_locking_stopped(ls))
232 			continue;
233 
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 
240 		castfn = lkb->lkb_astfn;
241 		bastfn = lkb->lkb_bastfn;
242 
243 		memset(&callbacks, 0, sizeof(callbacks));
244 
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);
251 
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);
256 		}
257 
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;
268 				castfn(lkb->lkb_astparam);
269 			}
270 		}
271 
272 		/* removes ref for ast_queue, may cause lkb to be freed */
273 		dlm_put_lkb(lkb);
274 
275 		cond_resched();
276 		goto repeat;
277 	}
278 	spin_unlock(&ast_queue_lock);
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 
299 		mutex_lock(&astd_running);
300 		if (test_and_clear_bit(WAKE_ASTS, &astd_wakeflags))
301 			process_asts();
302 		mutex_unlock(&astd_running);
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);
322 	mutex_init(&astd_running);
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 {
339 	mutex_lock(&astd_running);
340 }
341 
342 void dlm_astd_resume(void)
343 {
344 	mutex_unlock(&astd_running);
345 }
346 
347