Blame view

fs/dlm/ast.c 8.03 KB
e7fd41792   David Teigland   [DLM] The core of...
1
2
3
4
  /******************************************************************************
  *******************************************************************************
  **
  **  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
7fe2b3190   David Teigland   dlm: fix ordering...
5
  **  Copyright (C) 2004-2010 Red Hat, Inc.  All rights reserved.
e7fd41792   David Teigland   [DLM] The core of...
6
7
8
9
10
11
12
13
14
15
  **
  **  This copyrighted material is made available to anyone wishing to use,
  **  modify, copy, or redistribute it subject to the terms and conditions
  **  of the GNU General Public License v.2.
  **
  *******************************************************************************
  ******************************************************************************/
  
  #include "dlm_internal.h"
  #include "lock.h"
597d0cae0   David Teigland   [DLM] dlm: user l...
16
  #include "user.h"
e7fd41792   David Teigland   [DLM] The core of...
17

23e8e1aaa   David Teigland   dlm: use workqueu...
18
19
  static uint64_t			dlm_cb_seq;
  static spinlock_t		dlm_cb_seq_spin;
e7fd41792   David Teigland   [DLM] The core of...
20

8304d6f24   David Teigland   dlm: record full ...
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  static void dlm_dump_lkb_callbacks(struct dlm_lkb *lkb)
  {
  	int i;
  
  	log_print("last_bast %x %llu flags %x mode %d sb %d %x",
  		  lkb->lkb_id,
  		  (unsigned long long)lkb->lkb_last_bast.seq,
  		  lkb->lkb_last_bast.flags,
  		  lkb->lkb_last_bast.mode,
  		  lkb->lkb_last_bast.sb_status,
  		  lkb->lkb_last_bast.sb_flags);
  
  	log_print("last_cast %x %llu flags %x mode %d sb %d %x",
  		  lkb->lkb_id,
  		  (unsigned long long)lkb->lkb_last_cast.seq,
  		  lkb->lkb_last_cast.flags,
  		  lkb->lkb_last_cast.mode,
  		  lkb->lkb_last_cast.sb_status,
  		  lkb->lkb_last_cast.sb_flags);
  
  	for (i = 0; i < DLM_CALLBACKS_SIZE; i++) {
  		log_print("cb %x %llu flags %x mode %d sb %d %x",
  			  lkb->lkb_id,
  			  (unsigned long long)lkb->lkb_callbacks[i].seq,
  			  lkb->lkb_callbacks[i].flags,
  			  lkb->lkb_callbacks[i].mode,
  			  lkb->lkb_callbacks[i].sb_status,
  			  lkb->lkb_callbacks[i].sb_flags);
  	}
  }
8304d6f24   David Teigland   dlm: record full ...
51
52
53
54
55
56
  int dlm_add_lkb_callback(struct dlm_lkb *lkb, uint32_t flags, int mode,
  			 int status, uint32_t sbflags, uint64_t seq)
  {
  	struct dlm_ls *ls = lkb->lkb_resource->res_ls;
  	uint64_t prev_seq;
  	int prev_mode;
23e8e1aaa   David Teigland   dlm: use workqueu...
57
  	int i, rv;
8304d6f24   David Teigland   dlm: record full ...
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  
  	for (i = 0; i < DLM_CALLBACKS_SIZE; i++) {
  		if (lkb->lkb_callbacks[i].seq)
  			continue;
  
  		/*
  		 * Suppress some redundant basts here, do more on removal.
  		 * Don't even add a bast if the callback just before it
  		 * is a bast for the same mode or a more restrictive mode.
  		 * (the addional > PR check is needed for PR/CW inversion)
  		 */
  
  		if ((i > 0) && (flags & DLM_CB_BAST) &&
  		    (lkb->lkb_callbacks[i-1].flags & DLM_CB_BAST)) {
  
  			prev_seq = lkb->lkb_callbacks[i-1].seq;
  			prev_mode = lkb->lkb_callbacks[i-1].mode;
  
  			if ((prev_mode == mode) ||
  			    (prev_mode > mode && prev_mode > DLM_LOCK_PR)) {
  
  				log_debug(ls, "skip %x add bast %llu mode %d "
  					  "for bast %llu mode %d",
  					  lkb->lkb_id,
  					  (unsigned long long)seq,
  					  mode,
  					  (unsigned long long)prev_seq,
  					  prev_mode);
23e8e1aaa   David Teigland   dlm: use workqueu...
86
87
  				rv = 0;
  				goto out;
8304d6f24   David Teigland   dlm: record full ...
88
89
90
91
92
93
94
95
  			}
  		}
  
  		lkb->lkb_callbacks[i].seq = seq;
  		lkb->lkb_callbacks[i].flags = flags;
  		lkb->lkb_callbacks[i].mode = mode;
  		lkb->lkb_callbacks[i].sb_status = status;
  		lkb->lkb_callbacks[i].sb_flags = (sbflags & 0x000000FF);
23e8e1aaa   David Teigland   dlm: use workqueu...
96
  		rv = 0;
8304d6f24   David Teigland   dlm: record full ...
97
98
99
100
101
102
103
104
  		break;
  	}
  
  	if (i == DLM_CALLBACKS_SIZE) {
  		log_error(ls, "no callbacks %x %llu flags %x mode %d sb %d %x",
  			  lkb->lkb_id, (unsigned long long)seq,
  			  flags, mode, status, sbflags);
  		dlm_dump_lkb_callbacks(lkb);
23e8e1aaa   David Teigland   dlm: use workqueu...
105
106
  		rv = -1;
  		goto out;
8304d6f24   David Teigland   dlm: record full ...
107
  	}
23e8e1aaa   David Teigland   dlm: use workqueu...
108
109
   out:
  	return rv;
8304d6f24   David Teigland   dlm: record full ...
110
111
112
113
  }
  
  int dlm_rem_lkb_callback(struct dlm_ls *ls, struct dlm_lkb *lkb,
  			 struct dlm_callback *cb, int *resid)
e7fd41792   David Teigland   [DLM] The core of...
114
  {
23e8e1aaa   David Teigland   dlm: use workqueu...
115
  	int i, rv;
8304d6f24   David Teigland   dlm: record full ...
116
117
  
  	*resid = 0;
23e8e1aaa   David Teigland   dlm: use workqueu...
118
119
120
121
  	if (!lkb->lkb_callbacks[0].seq) {
  		rv = -ENOENT;
  		goto out;
  	}
8304d6f24   David Teigland   dlm: record full ...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
  
  	/* oldest undelivered cb is callbacks[0] */
  
  	memcpy(cb, &lkb->lkb_callbacks[0], sizeof(struct dlm_callback));
  	memset(&lkb->lkb_callbacks[0], 0, sizeof(struct dlm_callback));
  
  	/* shift others down */
  
  	for (i = 1; i < DLM_CALLBACKS_SIZE; i++) {
  		if (!lkb->lkb_callbacks[i].seq)
  			break;
  		memcpy(&lkb->lkb_callbacks[i-1], &lkb->lkb_callbacks[i],
  		       sizeof(struct dlm_callback));
  		memset(&lkb->lkb_callbacks[i], 0, sizeof(struct dlm_callback));
  		(*resid)++;
  	}
  
  	/* if cb is a bast, it should be skipped if the blocking mode is
  	   compatible with the last granted mode */
  
  	if ((cb->flags & DLM_CB_BAST) && lkb->lkb_last_cast.seq) {
  		if (dlm_modes_compat(cb->mode, lkb->lkb_last_cast.mode)) {
  			cb->flags |= DLM_CB_SKIP;
  
  			log_debug(ls, "skip %x bast %llu mode %d "
  				  "for cast %llu mode %d",
  				  lkb->lkb_id,
  				  (unsigned long long)cb->seq,
  				  cb->mode,
  				  (unsigned long long)lkb->lkb_last_cast.seq,
  				  lkb->lkb_last_cast.mode);
23e8e1aaa   David Teigland   dlm: use workqueu...
153
154
  			rv = 0;
  			goto out;
8304d6f24   David Teigland   dlm: record full ...
155
156
157
158
159
160
161
162
163
164
165
166
  		}
  	}
  
  	if (cb->flags & DLM_CB_CAST) {
  		memcpy(&lkb->lkb_last_cast, cb, sizeof(struct dlm_callback));
  		lkb->lkb_last_cast_time = ktime_get();
  	}
  
  	if (cb->flags & DLM_CB_BAST) {
  		memcpy(&lkb->lkb_last_bast, cb, sizeof(struct dlm_callback));
  		lkb->lkb_last_bast_time = ktime_get();
  	}
23e8e1aaa   David Teigland   dlm: use workqueu...
167
168
169
  	rv = 0;
   out:
  	return rv;
8304d6f24   David Teigland   dlm: record full ...
170
  }
23e8e1aaa   David Teigland   dlm: use workqueu...
171
172
  void dlm_add_cb(struct dlm_lkb *lkb, uint32_t flags, int mode, int status,
  		uint32_t sbflags)
8304d6f24   David Teigland   dlm: record full ...
173
  {
23e8e1aaa   David Teigland   dlm: use workqueu...
174
175
  	struct dlm_ls *ls = lkb->lkb_resource->res_ls;
  	uint64_t new_seq, prev_seq;
8304d6f24   David Teigland   dlm: record full ...
176
  	int rv;
23e8e1aaa   David Teigland   dlm: use workqueu...
177
178
179
  	spin_lock(&dlm_cb_seq_spin);
  	new_seq = ++dlm_cb_seq;
  	spin_unlock(&dlm_cb_seq_spin);
8304d6f24   David Teigland   dlm: record full ...
180

597d0cae0   David Teigland   [DLM] dlm: user l...
181
  	if (lkb->lkb_flags & DLM_IFL_USER) {
23e8e1aaa   David Teigland   dlm: use workqueu...
182
  		dlm_user_add_ast(lkb, flags, mode, status, sbflags, new_seq);
597d0cae0   David Teigland   [DLM] dlm: user l...
183
184
  		return;
  	}
23e8e1aaa   David Teigland   dlm: use workqueu...
185
186
  	mutex_lock(&lkb->lkb_cb_mutex);
  	prev_seq = lkb->lkb_callbacks[0].seq;
8304d6f24   David Teigland   dlm: record full ...
187

23e8e1aaa   David Teigland   dlm: use workqueu...
188
189
190
191
192
  	rv = dlm_add_lkb_callback(lkb, flags, mode, status, sbflags, new_seq);
  	if (rv < 0)
  		goto out;
  
  	if (!prev_seq) {
e7fd41792   David Teigland   [DLM] The core of...
193
  		kref_get(&lkb->lkb_ref);
e7fd41792   David Teigland   [DLM] The core of...
194

23e8e1aaa   David Teigland   dlm: use workqueu...
195
196
197
198
199
200
201
202
203
204
  		if (test_bit(LSFL_CB_DELAY, &ls->ls_flags)) {
  			mutex_lock(&ls->ls_cb_mutex);
  			list_add(&lkb->lkb_cb_list, &ls->ls_cb_delay);
  			mutex_unlock(&ls->ls_cb_mutex);
  		} else {
  			queue_work(ls->ls_callback_wq, &lkb->lkb_cb_work);
  		}
  	}
   out:
  	mutex_unlock(&lkb->lkb_cb_mutex);
e7fd41792   David Teigland   [DLM] The core of...
205
  }
23e8e1aaa   David Teigland   dlm: use workqueu...
206
  void dlm_callback_work(struct work_struct *work)
e7fd41792   David Teigland   [DLM] The core of...
207
  {
23e8e1aaa   David Teigland   dlm: use workqueu...
208
209
  	struct dlm_lkb *lkb = container_of(work, struct dlm_lkb, lkb_cb_work);
  	struct dlm_ls *ls = lkb->lkb_resource->res_ls;
7fe2b3190   David Teigland   dlm: fix ordering...
210
211
  	void (*castfn) (void *astparam);
  	void (*bastfn) (void *astparam, int mode);
8304d6f24   David Teigland   dlm: record full ...
212
213
  	struct dlm_callback callbacks[DLM_CALLBACKS_SIZE];
  	int i, rv, resid;
722d74219   Andrew Morton   dlm: fs/dlm/ast.c...
214

23e8e1aaa   David Teigland   dlm: use workqueu...
215
  	memset(&callbacks, 0, sizeof(callbacks));
e7fd41792   David Teigland   [DLM] The core of...
216

23e8e1aaa   David Teigland   dlm: use workqueu...
217
218
219
220
221
222
223
  	mutex_lock(&lkb->lkb_cb_mutex);
  	if (!lkb->lkb_callbacks[0].seq) {
  		/* no callback work exists, shouldn't happen */
  		log_error(ls, "dlm_callback_work %x no work", lkb->lkb_id);
  		dlm_print_lkb(lkb);
  		dlm_dump_lkb_callbacks(lkb);
  	}
e7fd41792   David Teigland   [DLM] The core of...
224

23e8e1aaa   David Teigland   dlm: use workqueu...
225
226
227
228
229
  	for (i = 0; i < DLM_CALLBACKS_SIZE; i++) {
  		rv = dlm_rem_lkb_callback(ls, lkb, &callbacks[i], &resid);
  		if (rv < 0)
  			break;
  	}
7fe2b3190   David Teigland   dlm: fix ordering...
230

23e8e1aaa   David Teigland   dlm: use workqueu...
231
232
233
234
235
236
237
238
  	if (resid) {
  		/* cbs remain, loop should have removed all, shouldn't happen */
  		log_error(ls, "dlm_callback_work %x resid %d", lkb->lkb_id,
  			  resid);
  		dlm_print_lkb(lkb);
  		dlm_dump_lkb_callbacks(lkb);
  	}
  	mutex_unlock(&lkb->lkb_cb_mutex);
7fe2b3190   David Teigland   dlm: fix ordering...
239

23e8e1aaa   David Teigland   dlm: use workqueu...
240
241
  	castfn = lkb->lkb_astfn;
  	bastfn = lkb->lkb_bastfn;
7fe2b3190   David Teigland   dlm: fix ordering...
242

23e8e1aaa   David Teigland   dlm: use workqueu...
243
244
245
246
247
248
249
250
251
252
253
  	for (i = 0; i < DLM_CALLBACKS_SIZE; i++) {
  		if (!callbacks[i].seq)
  			break;
  		if (callbacks[i].flags & DLM_CB_SKIP) {
  			continue;
  		} else if (callbacks[i].flags & DLM_CB_BAST) {
  			bastfn(lkb->lkb_astparam, callbacks[i].mode);
  		} else if (callbacks[i].flags & DLM_CB_CAST) {
  			lkb->lkb_lksb->sb_status = callbacks[i].sb_status;
  			lkb->lkb_lksb->sb_flags = callbacks[i].sb_flags;
  			castfn(lkb->lkb_astparam);
7fe2b3190   David Teigland   dlm: fix ordering...
254
  		}
e7fd41792   David Teigland   [DLM] The core of...
255
  	}
e7fd41792   David Teigland   [DLM] The core of...
256

23e8e1aaa   David Teigland   dlm: use workqueu...
257
258
  	/* undo kref_get from dlm_add_callback, may cause lkb to be freed */
  	dlm_put_lkb(lkb);
e7fd41792   David Teigland   [DLM] The core of...
259
  }
23e8e1aaa   David Teigland   dlm: use workqueu...
260
  int dlm_callback_start(struct dlm_ls *ls)
e7fd41792   David Teigland   [DLM] The core of...
261
  {
23e8e1aaa   David Teigland   dlm: use workqueu...
262
  	ls->ls_callback_wq = alloc_workqueue("dlm_callback",
10d1459fa   David Teigland   dlm: don't limit ...
263
264
265
266
  					     WQ_UNBOUND |
  					     WQ_MEM_RECLAIM |
  					     WQ_NON_REENTRANT,
  					     0);
23e8e1aaa   David Teigland   dlm: use workqueu...
267
268
269
  	if (!ls->ls_callback_wq) {
  		log_print("can't start dlm_callback workqueue");
  		return -ENOMEM;
e7fd41792   David Teigland   [DLM] The core of...
270
271
272
  	}
  	return 0;
  }
23e8e1aaa   David Teigland   dlm: use workqueu...
273
  void dlm_callback_stop(struct dlm_ls *ls)
e7fd41792   David Teigland   [DLM] The core of...
274
  {
23e8e1aaa   David Teigland   dlm: use workqueu...
275
276
  	if (ls->ls_callback_wq)
  		destroy_workqueue(ls->ls_callback_wq);
e7fd41792   David Teigland   [DLM] The core of...
277
  }
23e8e1aaa   David Teigland   dlm: use workqueu...
278
  void dlm_callback_suspend(struct dlm_ls *ls)
e7fd41792   David Teigland   [DLM] The core of...
279
  {
23e8e1aaa   David Teigland   dlm: use workqueu...
280
  	set_bit(LSFL_CB_DELAY, &ls->ls_flags);
e7fd41792   David Teigland   [DLM] The core of...
281

23e8e1aaa   David Teigland   dlm: use workqueu...
282
283
  	if (ls->ls_callback_wq)
  		flush_workqueue(ls->ls_callback_wq);
e7fd41792   David Teigland   [DLM] The core of...
284
  }
23e8e1aaa   David Teigland   dlm: use workqueu...
285
  void dlm_callback_resume(struct dlm_ls *ls)
e7fd41792   David Teigland   [DLM] The core of...
286
  {
23e8e1aaa   David Teigland   dlm: use workqueu...
287
288
  	struct dlm_lkb *lkb, *safe;
  	int count = 0;
e7fd41792   David Teigland   [DLM] The core of...
289

23e8e1aaa   David Teigland   dlm: use workqueu...
290
291
292
293
294
295
296
297
298
299
300
301
302
303
  	clear_bit(LSFL_CB_DELAY, &ls->ls_flags);
  
  	if (!ls->ls_callback_wq)
  		return;
  
  	mutex_lock(&ls->ls_cb_mutex);
  	list_for_each_entry_safe(lkb, safe, &ls->ls_cb_delay, lkb_cb_list) {
  		list_del_init(&lkb->lkb_cb_list);
  		queue_work(ls->ls_callback_wq, &lkb->lkb_cb_work);
  		count++;
  	}
  	mutex_unlock(&ls->ls_cb_mutex);
  
  	log_debug(ls, "dlm_callback_resume %d", count);
e7fd41792   David Teigland   [DLM] The core of...
304
  }