Blame view

fs/afs/callback.c 11.3 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
2
   * Copyright (c) 2002, 2007 Red Hat, Inc. All rights reserved.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
5
6
7
8
9
10
   *
   * This software may be freely redistributed under the terms of the
   * GNU General Public License.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   *
44d1b980c   David Woodhouse   Fix various old e...
11
   * Authors: David Woodhouse <dwmw2@infradead.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
15
16
17
18
   *          David Howells <dhowells@redhat.com>
   *
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/init.h>
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
19
  #include <linux/circ_buf.h>
e8edc6e03   Alexey Dobriyan   Detach sched.h fr...
20
  #include <linux/sched.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
  #include "internal.h"
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
22

c1206a2c6   Adrian Bunk   fs/afs/: possible...
23
  #if 0
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
24
  unsigned afs_vnode_update_timeout = 10;
c1206a2c6   Adrian Bunk   fs/afs/: possible...
25
  #endif  /*  0  */
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
26
27
28
29
30
31
32
33
  
  #define afs_breakring_space(server) \
  	CIRC_SPACE((server)->cb_break_head, (server)->cb_break_tail,	\
  		   ARRAY_SIZE((server)->cb_break))
  
  //static void afs_callback_updater(struct work_struct *);
  
  static struct workqueue_struct *afs_callback_update_worker;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
36
37
  /*
   * allow the fileserver to request callback state (re-)initialisation
   */
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
38
  void afs_init_callback_state(struct afs_server *server)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
  {
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
40
  	struct afs_vnode *vnode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
42
  	_enter("{%p}", server);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
  	spin_lock(&server->cb_lock);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
45
46
47
48
  	/* kill all the promises on record from this server */
  	while (!RB_EMPTY_ROOT(&server->cb_promises)) {
  		vnode = rb_entry(server->cb_promises.rb_node,
  				 struct afs_vnode, cb_promise);
416351f28   David Howells   AFS: AFS fixups
49
  		_debug("UNPROMISE { vid=%x:%u uq=%u}",
260a98031   David Howells   [AFS]: Add "direc...
50
  		       vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
51
52
53
  		rb_erase(&vnode->cb_promise, &server->cb_promises);
  		vnode->cb_promised = false;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
55
56
57
  	spin_unlock(&server->cb_lock);
  	_leave("");
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
59
60
61
62
63
64
65
  /*
   * handle the data invalidation side of a callback being broken
   */
  void afs_broken_callback_work(struct work_struct *work)
  {
  	struct afs_vnode *vnode =
  		container_of(work, struct afs_vnode, cb_broken_work);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
67
  	_enter("");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
69
70
  	if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
72
73
  	/* we're only interested in dealing with a broken callback on *this*
  	 * vnode and only if no-one else has dealt with it yet */
260a98031   David Howells   [AFS]: Add "direc...
74
  	if (!mutex_trylock(&vnode->validate_lock))
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
75
  		return; /* someone else is dealing with it */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
77
  	if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) {
00d3b7a45   David Howells   [AFS]: Add securi...
78
79
80
81
  		if (S_ISDIR(vnode->vfs_inode.i_mode))
  			afs_clear_permits(vnode);
  
  		if (afs_vnode_fetch_status(vnode, NULL, NULL) < 0)
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
82
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
84
85
  		if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
87
88
  		/* if the vnode's data version number changed then its contents
  		 * are different */
416351f28   David Howells   AFS: AFS fixups
89
90
  		if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags))
  			afs_zap_data(vnode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
  	}
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
92
  out:
260a98031   David Howells   [AFS]: Add "direc...
93
  	mutex_unlock(&vnode->validate_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  	/* avoid the potential race whereby the mutex_trylock() in this
  	 * function happens again between the clear_bit() and the
  	 * mutex_unlock() */
  	if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) {
  		_debug("requeue");
  		queue_work(afs_callback_update_worker, &vnode->cb_broken_work);
  	}
  	_leave("");
  }
  
  /*
   * actually break a callback
   */
  static void afs_break_callback(struct afs_server *server,
  			       struct afs_vnode *vnode)
  {
  	_enter("");
  
  	set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
  
  	if (vnode->cb_promised) {
  		spin_lock(&vnode->lock);
  
  		_debug("break callback");
  
  		spin_lock(&server->cb_lock);
  		if (vnode->cb_promised) {
  			rb_erase(&vnode->cb_promise, &server->cb_promises);
  			vnode->cb_promised = false;
  		}
  		spin_unlock(&server->cb_lock);
  
  		queue_work(afs_callback_update_worker, &vnode->cb_broken_work);
e8d6c5541   David Howells   AFS: implement fi...
128
129
130
  		if (list_empty(&vnode->granted_locks) &&
  		    !list_empty(&vnode->pending_locks))
  			afs_lock_may_be_available(vnode);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  		spin_unlock(&vnode->lock);
  	}
  }
  
  /*
   * allow the fileserver to explicitly break one callback
   * - happens when
   *   - the backing file is changed
   *   - a lock is released
   */
  static void afs_break_one_callback(struct afs_server *server,
  				   struct afs_fid *fid)
  {
  	struct afs_vnode *vnode;
  	struct rb_node *p;
  
  	_debug("find");
  	spin_lock(&server->fs_lock);
  	p = server->fs_vnodes.rb_node;
  	while (p) {
  		vnode = rb_entry(p, struct afs_vnode, server_rb);
  		if (fid->vid < vnode->fid.vid)
  			p = p->rb_left;
  		else if (fid->vid > vnode->fid.vid)
  			p = p->rb_right;
  		else if (fid->vnode < vnode->fid.vnode)
  			p = p->rb_left;
  		else if (fid->vnode > vnode->fid.vnode)
  			p = p->rb_right;
  		else if (fid->unique < vnode->fid.unique)
  			p = p->rb_left;
  		else if (fid->unique > vnode->fid.unique)
  			p = p->rb_right;
  		else
  			goto found;
  	}
  
  	/* not found so we just ignore it (it may have moved to another
  	 * server) */
  not_available:
  	_debug("not avail");
  	spin_unlock(&server->fs_lock);
  	_leave("");
  	return;
  
  found:
  	_debug("found");
  	ASSERTCMP(server, ==, vnode->server);
  
  	if (!igrab(AFS_VNODE_TO_I(vnode)))
  		goto not_available;
  	spin_unlock(&server->fs_lock);
  
  	afs_break_callback(server, vnode);
  	iput(&vnode->vfs_inode);
  	_leave("");
ec26815ad   David Howells   [AFS]: Clean up t...
187
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
191
  /*
   * allow the fileserver to break callback promises
   */
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
192
193
  void afs_break_callbacks(struct afs_server *server, size_t count,
  			 struct afs_callback callbacks[])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
  {
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
195
  	_enter("%p,%zu,", server, count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
197
198
  	ASSERT(server != NULL);
  	ASSERTCMP(count, <=, AFSCBMAX);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
200
  	for (; count > 0; callbacks++, count--) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
202
203
204
205
206
207
208
  		_debug("- Fid { vl=%08x n=%u u=%u }  CB { v=%u x=%u t=%u }",
  		       callbacks->fid.vid,
  		       callbacks->fid.vnode,
  		       callbacks->fid.unique,
  		       callbacks->version,
  		       callbacks->expiry,
  		       callbacks->type
  		       );
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
209
210
211
212
213
214
  		afs_break_one_callback(server, &callbacks->fid);
  	}
  
  	_leave("");
  	return;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
216
217
218
219
220
221
222
223
  /*
   * record the callback for breaking
   * - the caller must hold server->cb_lock
   */
  static void afs_do_give_up_callback(struct afs_server *server,
  				    struct afs_vnode *vnode)
  {
  	struct afs_callback *cb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
225
  	_enter("%p,%p", server, vnode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
227
228
229
230
231
232
233
234
235
  	cb = &server->cb_break[server->cb_break_head];
  	cb->fid		= vnode->fid;
  	cb->version	= vnode->cb_version;
  	cb->expiry	= vnode->cb_expiry;
  	cb->type	= vnode->cb_type;
  	smp_wmb();
  	server->cb_break_head =
  		(server->cb_break_head + 1) &
  		(ARRAY_SIZE(server->cb_break) - 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
  	/* defer the breaking of callbacks to try and collect as many as
  	 * possible to ship in one operation */
  	switch (atomic_inc_return(&server->cb_break_n)) {
  	case 1 ... AFSCBMAX - 1:
  		queue_delayed_work(afs_callback_update_worker,
  				   &server->cb_break_work, HZ * 2);
  		break;
  	case AFSCBMAX:
  		afs_flush_callback_breaks(server);
  		break;
  	default:
  		break;
  	}
  
  	ASSERT(server->cb_promises.rb_node != NULL);
  	rb_erase(&vnode->cb_promise, &server->cb_promises);
  	vnode->cb_promised = false;
  	_leave("");
  }
  
  /*
260a98031   David Howells   [AFS]: Add "direc...
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
   * discard the callback on a deleted item
   */
  void afs_discard_callback_on_delete(struct afs_vnode *vnode)
  {
  	struct afs_server *server = vnode->server;
  
  	_enter("%d", vnode->cb_promised);
  
  	if (!vnode->cb_promised) {
  		_leave(" [not promised]");
  		return;
  	}
  
  	ASSERT(server != NULL);
  
  	spin_lock(&server->cb_lock);
  	if (vnode->cb_promised) {
  		ASSERT(server->cb_promises.rb_node != NULL);
  		rb_erase(&vnode->cb_promise, &server->cb_promises);
  		vnode->cb_promised = false;
  	}
  	spin_unlock(&server->cb_lock);
  	_leave("");
  }
  
  /*
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
   * give up the callback registered for a vnode on the file server when the
   * inode is being cleared
   */
  void afs_give_up_callback(struct afs_vnode *vnode)
  {
  	struct afs_server *server = vnode->server;
  
  	DECLARE_WAITQUEUE(myself, current);
  
  	_enter("%d", vnode->cb_promised);
  
  	_debug("GIVE UP INODE %p", &vnode->vfs_inode);
  
  	if (!vnode->cb_promised) {
  		_leave(" [not promised]");
  		return;
  	}
  
  	ASSERT(server != NULL);
  
  	spin_lock(&server->cb_lock);
  	if (vnode->cb_promised && afs_breakring_space(server) == 0) {
  		add_wait_queue(&server->cb_break_waitq, &myself);
  		for (;;) {
  			set_current_state(TASK_UNINTERRUPTIBLE);
  			if (!vnode->cb_promised ||
  			    afs_breakring_space(server) != 0)
  				break;
  			spin_unlock(&server->cb_lock);
  			schedule();
  			spin_lock(&server->cb_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
  		}
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
  		remove_wait_queue(&server->cb_break_waitq, &myself);
  		__set_current_state(TASK_RUNNING);
  	}
  
  	/* of course, it's always possible for the server to break this vnode's
  	 * callback first... */
  	if (vnode->cb_promised)
  		afs_do_give_up_callback(server, vnode);
  
  	spin_unlock(&server->cb_lock);
  	_leave("");
  }
  
  /*
   * dispatch a deferred give up callbacks operation
   */
  void afs_dispatch_give_up_callbacks(struct work_struct *work)
  {
  	struct afs_server *server =
  		container_of(work, struct afs_server, cb_break_work.work);
  
  	_enter("");
  
  	/* tell the fileserver to discard the callback promises it has
  	 * - in the event of ENOMEM or some other error, we just forget that we
  	 *   had callbacks entirely, and the server will call us later to break
  	 *   them
  	 */
  	afs_fs_give_up_callbacks(server, &afs_async_call);
  }
  
  /*
   * flush the outstanding callback breaks on a server
   */
  void afs_flush_callback_breaks(struct afs_server *server)
  {
41f63c535   Tejun Heo   workqueue: use mo...
352
  	mod_delayed_work(afs_callback_update_worker, &server->cb_break_work, 0);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
  }
  
  #if 0
  /*
   * update a bunch of callbacks
   */
  static void afs_callback_updater(struct work_struct *work)
  {
  	struct afs_server *server;
  	struct afs_vnode *vnode, *xvnode;
  	time_t now;
  	long timeout;
  	int ret;
  
  	server = container_of(work, struct afs_server, updater);
  
  	_enter("");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
371
372
373
374
375
376
377
378
379
  	now = get_seconds();
  
  	/* find the first vnode to update */
  	spin_lock(&server->cb_lock);
  	for (;;) {
  		if (RB_EMPTY_ROOT(&server->cb_promises)) {
  			spin_unlock(&server->cb_lock);
  			_leave(" [nothing]");
  			return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
  		}
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
  
  		vnode = rb_entry(rb_first(&server->cb_promises),
  				 struct afs_vnode, cb_promise);
  		if (atomic_read(&vnode->usage) > 0)
  			break;
  		rb_erase(&vnode->cb_promise, &server->cb_promises);
  		vnode->cb_promised = false;
  	}
  
  	timeout = vnode->update_at - now;
  	if (timeout > 0) {
  		queue_delayed_work(afs_vnode_update_worker,
  				   &afs_vnode_update, timeout * HZ);
  		spin_unlock(&server->cb_lock);
  		_leave(" [nothing]");
  		return;
  	}
  
  	list_del_init(&vnode->update);
  	atomic_inc(&vnode->usage);
  	spin_unlock(&server->cb_lock);
  
  	/* we can now perform the update */
  	_debug("update %s", vnode->vldb.name);
  	vnode->state = AFS_VL_UPDATING;
  	vnode->upd_rej_cnt = 0;
  	vnode->upd_busy_cnt = 0;
  
  	ret = afs_vnode_update_record(vl, &vldb);
  	switch (ret) {
  	case 0:
  		afs_vnode_apply_update(vl, &vldb);
  		vnode->state = AFS_VL_UPDATING;
  		break;
  	case -ENOMEDIUM:
  		vnode->state = AFS_VL_VOLUME_DELETED;
  		break;
  	default:
  		vnode->state = AFS_VL_UNCERTAIN;
  		break;
  	}
  
  	/* and then reschedule */
  	_debug("reschedule");
  	vnode->update_at = get_seconds() + afs_vnode_update_timeout;
  
  	spin_lock(&server->cb_lock);
  
  	if (!list_empty(&server->cb_promises)) {
  		/* next update in 10 minutes, but wait at least 1 second more
  		 * than the newest record already queued so that we don't spam
  		 * the VL server suddenly with lots of requests
  		 */
  		xvnode = list_entry(server->cb_promises.prev,
  				    struct afs_vnode, update);
  		if (vnode->update_at <= xvnode->update_at)
  			vnode->update_at = xvnode->update_at + 1;
  		xvnode = list_entry(server->cb_promises.next,
  				    struct afs_vnode, update);
  		timeout = xvnode->update_at - now;
  		if (timeout < 0)
  			timeout = 0;
  	} else {
  		timeout = afs_vnode_update_timeout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445
  	}
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
  	list_add_tail(&vnode->update, &server->cb_promises);
  
  	_debug("timeout %ld", timeout);
  	queue_delayed_work(afs_vnode_update_worker,
  			   &afs_vnode_update, timeout * HZ);
  	spin_unlock(&server->cb_lock);
  	afs_put_vnode(vl);
  }
  #endif
  
  /*
   * initialise the callback update process
   */
  int __init afs_callback_update_init(void)
  {
4c136dae6   Bhaktipriya Shridhar   fs/afs/callback: ...
461
462
  	afs_callback_update_worker = alloc_ordered_workqueue("kafs_callbackd",
  							     WQ_MEM_RECLAIM);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
463
  	return afs_callback_update_worker ? 0 : -ENOMEM;
ec26815ad   David Howells   [AFS]: Clean up t...
464
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466
  /*
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
467
   * shut down the callback update process
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
   */
fbb3fcba7   David Howells   [AFS]: Fix use of...
469
  void afs_callback_update_kill(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
470
  {
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
471
  	destroy_workqueue(afs_callback_update_worker);
ec26815ad   David Howells   [AFS]: Clean up t...
472
  }