diff options
Diffstat (limited to 'fs/afs/callback.c')
-rw-r--r-- | fs/afs/callback.c | 509 |
1 files changed, 407 insertions, 102 deletions
diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 9cb206e9d4be..639399f0ab6f 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) 2002 Red Hat, Inc. All rights reserved. | 2 | * Copyright (c) 2002, 2007 Red Hat, Inc. All rights reserved. |
3 | * | 3 | * |
4 | * This software may be freely redistributed under the terms of the | 4 | * This software may be freely redistributed under the terms of the |
5 | * GNU General Public License. | 5 | * GNU General Public License. |
@@ -16,85 +16,187 @@ | |||
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <linux/init.h> | 18 | #include <linux/init.h> |
19 | #include "server.h" | 19 | #include <linux/circ_buf.h> |
20 | #include "vnode.h" | ||
21 | #include "internal.h" | 20 | #include "internal.h" |
22 | #include "cmservice.h" | ||
23 | 21 | ||
24 | /*****************************************************************************/ | 22 | unsigned afs_vnode_update_timeout = 10; |
23 | |||
24 | #define afs_breakring_space(server) \ | ||
25 | CIRC_SPACE((server)->cb_break_head, (server)->cb_break_tail, \ | ||
26 | ARRAY_SIZE((server)->cb_break)) | ||
27 | |||
28 | //static void afs_callback_updater(struct work_struct *); | ||
29 | |||
30 | static struct workqueue_struct *afs_callback_update_worker; | ||
31 | |||
25 | /* | 32 | /* |
26 | * allow the fileserver to request callback state (re-)initialisation | 33 | * allow the fileserver to request callback state (re-)initialisation |
27 | */ | 34 | */ |
28 | int SRXAFSCM_InitCallBackState(struct afs_server *server) | 35 | void afs_init_callback_state(struct afs_server *server) |
29 | { | 36 | { |
30 | struct list_head callbacks; | 37 | struct afs_vnode *vnode; |
31 | 38 | ||
32 | _enter("%p", server); | 39 | _enter("{%p}", server); |
33 | 40 | ||
34 | INIT_LIST_HEAD(&callbacks); | ||
35 | |||
36 | /* transfer the callback list from the server to a temp holding area */ | ||
37 | spin_lock(&server->cb_lock); | 41 | spin_lock(&server->cb_lock); |
38 | 42 | ||
39 | list_add(&callbacks, &server->cb_promises); | 43 | /* kill all the promises on record from this server */ |
40 | list_del_init(&server->cb_promises); | 44 | while (!RB_EMPTY_ROOT(&server->cb_promises)) { |
45 | vnode = rb_entry(server->cb_promises.rb_node, | ||
46 | struct afs_vnode, cb_promise); | ||
47 | _debug("UNPROMISE { vid=%x vn=%u uq=%u}", | ||
48 | vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); | ||
49 | rb_erase(&vnode->cb_promise, &server->cb_promises); | ||
50 | vnode->cb_promised = false; | ||
51 | } | ||
41 | 52 | ||
42 | /* munch our way through the list, grabbing the inode, dropping all the | 53 | spin_unlock(&server->cb_lock); |
43 | * locks and regetting them in the right order | 54 | _leave(""); |
44 | */ | 55 | } |
45 | while (!list_empty(&callbacks)) { | ||
46 | struct afs_vnode *vnode; | ||
47 | struct inode *inode; | ||
48 | 56 | ||
49 | vnode = list_entry(callbacks.next, struct afs_vnode, cb_link); | 57 | /* |
50 | list_del_init(&vnode->cb_link); | 58 | * handle the data invalidation side of a callback being broken |
59 | */ | ||
60 | void afs_broken_callback_work(struct work_struct *work) | ||
61 | { | ||
62 | struct afs_vnode *vnode = | ||
63 | container_of(work, struct afs_vnode, cb_broken_work); | ||
51 | 64 | ||
52 | /* try and grab the inode - may fail */ | 65 | _enter(""); |
53 | inode = igrab(AFS_VNODE_TO_I(vnode)); | ||
54 | if (inode) { | ||
55 | int release = 0; | ||
56 | 66 | ||
57 | spin_unlock(&server->cb_lock); | 67 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) |
58 | spin_lock(&vnode->lock); | 68 | return; |
59 | 69 | ||
60 | if (vnode->cb_server == server) { | 70 | /* we're only interested in dealing with a broken callback on *this* |
61 | vnode->cb_server = NULL; | 71 | * vnode and only if no-one else has dealt with it yet */ |
62 | afs_kafstimod_del_timer(&vnode->cb_timeout); | 72 | if (!mutex_trylock(&vnode->validate_lock)) |
63 | spin_lock(&afs_cb_hash_lock); | 73 | return; /* someone else is dealing with it */ |
64 | list_del_init(&vnode->cb_hash_link); | ||
65 | spin_unlock(&afs_cb_hash_lock); | ||
66 | release = 1; | ||
67 | } | ||
68 | 74 | ||
69 | spin_unlock(&vnode->lock); | 75 | if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) { |
76 | if (S_ISDIR(vnode->vfs_inode.i_mode)) | ||
77 | afs_clear_permits(vnode); | ||
70 | 78 | ||
71 | iput(inode); | 79 | if (afs_vnode_fetch_status(vnode, NULL, NULL) < 0) |
72 | afs_put_server(server); | 80 | goto out; |
73 | 81 | ||
74 | spin_lock(&server->cb_lock); | 82 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) |
83 | goto out; | ||
84 | |||
85 | /* if the vnode's data version number changed then its contents | ||
86 | * are different */ | ||
87 | if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { | ||
88 | _debug("zap data {%x:%u}", | ||
89 | vnode->fid.vid, vnode->fid.vnode); | ||
90 | invalidate_remote_inode(&vnode->vfs_inode); | ||
75 | } | 91 | } |
76 | } | 92 | } |
77 | 93 | ||
78 | spin_unlock(&server->cb_lock); | 94 | out: |
95 | mutex_unlock(&vnode->validate_lock); | ||
79 | 96 | ||
80 | _leave(" = 0"); | 97 | /* avoid the potential race whereby the mutex_trylock() in this |
81 | return 0; | 98 | * function happens again between the clear_bit() and the |
82 | } /* end SRXAFSCM_InitCallBackState() */ | 99 | * mutex_unlock() */ |
100 | if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) { | ||
101 | _debug("requeue"); | ||
102 | queue_work(afs_callback_update_worker, &vnode->cb_broken_work); | ||
103 | } | ||
104 | _leave(""); | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | * actually break a callback | ||
109 | */ | ||
110 | static void afs_break_callback(struct afs_server *server, | ||
111 | struct afs_vnode *vnode) | ||
112 | { | ||
113 | _enter(""); | ||
114 | |||
115 | set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); | ||
116 | |||
117 | if (vnode->cb_promised) { | ||
118 | spin_lock(&vnode->lock); | ||
119 | |||
120 | _debug("break callback"); | ||
121 | |||
122 | spin_lock(&server->cb_lock); | ||
123 | if (vnode->cb_promised) { | ||
124 | rb_erase(&vnode->cb_promise, &server->cb_promises); | ||
125 | vnode->cb_promised = false; | ||
126 | } | ||
127 | spin_unlock(&server->cb_lock); | ||
128 | |||
129 | queue_work(afs_callback_update_worker, &vnode->cb_broken_work); | ||
130 | spin_unlock(&vnode->lock); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * allow the fileserver to explicitly break one callback | ||
136 | * - happens when | ||
137 | * - the backing file is changed | ||
138 | * - a lock is released | ||
139 | */ | ||
140 | static void afs_break_one_callback(struct afs_server *server, | ||
141 | struct afs_fid *fid) | ||
142 | { | ||
143 | struct afs_vnode *vnode; | ||
144 | struct rb_node *p; | ||
145 | |||
146 | _debug("find"); | ||
147 | spin_lock(&server->fs_lock); | ||
148 | p = server->fs_vnodes.rb_node; | ||
149 | while (p) { | ||
150 | vnode = rb_entry(p, struct afs_vnode, server_rb); | ||
151 | if (fid->vid < vnode->fid.vid) | ||
152 | p = p->rb_left; | ||
153 | else if (fid->vid > vnode->fid.vid) | ||
154 | p = p->rb_right; | ||
155 | else if (fid->vnode < vnode->fid.vnode) | ||
156 | p = p->rb_left; | ||
157 | else if (fid->vnode > vnode->fid.vnode) | ||
158 | p = p->rb_right; | ||
159 | else if (fid->unique < vnode->fid.unique) | ||
160 | p = p->rb_left; | ||
161 | else if (fid->unique > vnode->fid.unique) | ||
162 | p = p->rb_right; | ||
163 | else | ||
164 | goto found; | ||
165 | } | ||
166 | |||
167 | /* not found so we just ignore it (it may have moved to another | ||
168 | * server) */ | ||
169 | not_available: | ||
170 | _debug("not avail"); | ||
171 | spin_unlock(&server->fs_lock); | ||
172 | _leave(""); | ||
173 | return; | ||
174 | |||
175 | found: | ||
176 | _debug("found"); | ||
177 | ASSERTCMP(server, ==, vnode->server); | ||
178 | |||
179 | if (!igrab(AFS_VNODE_TO_I(vnode))) | ||
180 | goto not_available; | ||
181 | spin_unlock(&server->fs_lock); | ||
182 | |||
183 | afs_break_callback(server, vnode); | ||
184 | iput(&vnode->vfs_inode); | ||
185 | _leave(""); | ||
186 | } | ||
83 | 187 | ||
84 | /*****************************************************************************/ | ||
85 | /* | 188 | /* |
86 | * allow the fileserver to break callback promises | 189 | * allow the fileserver to break callback promises |
87 | */ | 190 | */ |
88 | int SRXAFSCM_CallBack(struct afs_server *server, size_t count, | 191 | void afs_break_callbacks(struct afs_server *server, size_t count, |
89 | struct afs_callback callbacks[]) | 192 | struct afs_callback callbacks[]) |
90 | { | 193 | { |
91 | _enter("%p,%u,", server, count); | 194 | _enter("%p,%zu,", server, count); |
92 | 195 | ||
93 | for (; count > 0; callbacks++, count--) { | 196 | ASSERT(server != NULL); |
94 | struct afs_vnode *vnode = NULL; | 197 | ASSERTCMP(count, <=, AFSCBMAX); |
95 | struct inode *inode = NULL; | ||
96 | int valid = 0; | ||
97 | 198 | ||
199 | for (; count > 0; callbacks++, count--) { | ||
98 | _debug("- Fid { vl=%08x n=%u u=%u } CB { v=%u x=%u t=%u }", | 200 | _debug("- Fid { vl=%08x n=%u u=%u } CB { v=%u x=%u t=%u }", |
99 | callbacks->fid.vid, | 201 | callbacks->fid.vid, |
100 | callbacks->fid.vnode, | 202 | callbacks->fid.vnode, |
@@ -103,67 +205,270 @@ int SRXAFSCM_CallBack(struct afs_server *server, size_t count, | |||
103 | callbacks->expiry, | 205 | callbacks->expiry, |
104 | callbacks->type | 206 | callbacks->type |
105 | ); | 207 | ); |
208 | afs_break_one_callback(server, &callbacks->fid); | ||
209 | } | ||
106 | 210 | ||
107 | /* find the inode for this fid */ | 211 | _leave(""); |
108 | spin_lock(&afs_cb_hash_lock); | 212 | return; |
213 | } | ||
109 | 214 | ||
110 | list_for_each_entry(vnode, | 215 | /* |
111 | &afs_cb_hash(server, &callbacks->fid), | 216 | * record the callback for breaking |
112 | cb_hash_link) { | 217 | * - the caller must hold server->cb_lock |
113 | if (memcmp(&vnode->fid, &callbacks->fid, | 218 | */ |
114 | sizeof(struct afs_fid)) != 0) | 219 | static void afs_do_give_up_callback(struct afs_server *server, |
115 | continue; | 220 | struct afs_vnode *vnode) |
221 | { | ||
222 | struct afs_callback *cb; | ||
116 | 223 | ||
117 | /* right vnode, but is it same server? */ | 224 | _enter("%p,%p", server, vnode); |
118 | if (vnode->cb_server != server) | ||
119 | break; /* no */ | ||
120 | 225 | ||
121 | /* try and nail the inode down */ | 226 | cb = &server->cb_break[server->cb_break_head]; |
122 | inode = igrab(AFS_VNODE_TO_I(vnode)); | 227 | cb->fid = vnode->fid; |
123 | break; | 228 | cb->version = vnode->cb_version; |
229 | cb->expiry = vnode->cb_expiry; | ||
230 | cb->type = vnode->cb_type; | ||
231 | smp_wmb(); | ||
232 | server->cb_break_head = | ||
233 | (server->cb_break_head + 1) & | ||
234 | (ARRAY_SIZE(server->cb_break) - 1); | ||
235 | |||
236 | /* defer the breaking of callbacks to try and collect as many as | ||
237 | * possible to ship in one operation */ | ||
238 | switch (atomic_inc_return(&server->cb_break_n)) { | ||
239 | case 1 ... AFSCBMAX - 1: | ||
240 | queue_delayed_work(afs_callback_update_worker, | ||
241 | &server->cb_break_work, HZ * 2); | ||
242 | break; | ||
243 | case AFSCBMAX: | ||
244 | afs_flush_callback_breaks(server); | ||
245 | break; | ||
246 | default: | ||
247 | break; | ||
248 | } | ||
249 | |||
250 | ASSERT(server->cb_promises.rb_node != NULL); | ||
251 | rb_erase(&vnode->cb_promise, &server->cb_promises); | ||
252 | vnode->cb_promised = false; | ||
253 | _leave(""); | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * discard the callback on a deleted item | ||
258 | */ | ||
259 | void afs_discard_callback_on_delete(struct afs_vnode *vnode) | ||
260 | { | ||
261 | struct afs_server *server = vnode->server; | ||
262 | |||
263 | _enter("%d", vnode->cb_promised); | ||
264 | |||
265 | if (!vnode->cb_promised) { | ||
266 | _leave(" [not promised]"); | ||
267 | return; | ||
268 | } | ||
269 | |||
270 | ASSERT(server != NULL); | ||
271 | |||
272 | spin_lock(&server->cb_lock); | ||
273 | if (vnode->cb_promised) { | ||
274 | ASSERT(server->cb_promises.rb_node != NULL); | ||
275 | rb_erase(&vnode->cb_promise, &server->cb_promises); | ||
276 | vnode->cb_promised = false; | ||
277 | } | ||
278 | spin_unlock(&server->cb_lock); | ||
279 | _leave(""); | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * give up the callback registered for a vnode on the file server when the | ||
284 | * inode is being cleared | ||
285 | */ | ||
286 | void afs_give_up_callback(struct afs_vnode *vnode) | ||
287 | { | ||
288 | struct afs_server *server = vnode->server; | ||
289 | |||
290 | DECLARE_WAITQUEUE(myself, current); | ||
291 | |||
292 | _enter("%d", vnode->cb_promised); | ||
293 | |||
294 | _debug("GIVE UP INODE %p", &vnode->vfs_inode); | ||
295 | |||
296 | if (!vnode->cb_promised) { | ||
297 | _leave(" [not promised]"); | ||
298 | return; | ||
299 | } | ||
300 | |||
301 | ASSERT(server != NULL); | ||
302 | |||
303 | spin_lock(&server->cb_lock); | ||
304 | if (vnode->cb_promised && afs_breakring_space(server) == 0) { | ||
305 | add_wait_queue(&server->cb_break_waitq, &myself); | ||
306 | for (;;) { | ||
307 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
308 | if (!vnode->cb_promised || | ||
309 | afs_breakring_space(server) != 0) | ||
310 | break; | ||
311 | spin_unlock(&server->cb_lock); | ||
312 | schedule(); | ||
313 | spin_lock(&server->cb_lock); | ||
124 | } | 314 | } |
315 | remove_wait_queue(&server->cb_break_waitq, &myself); | ||
316 | __set_current_state(TASK_RUNNING); | ||
317 | } | ||
318 | |||
319 | /* of course, it's always possible for the server to break this vnode's | ||
320 | * callback first... */ | ||
321 | if (vnode->cb_promised) | ||
322 | afs_do_give_up_callback(server, vnode); | ||
323 | |||
324 | spin_unlock(&server->cb_lock); | ||
325 | _leave(""); | ||
326 | } | ||
327 | |||
328 | /* | ||
329 | * dispatch a deferred give up callbacks operation | ||
330 | */ | ||
331 | void afs_dispatch_give_up_callbacks(struct work_struct *work) | ||
332 | { | ||
333 | struct afs_server *server = | ||
334 | container_of(work, struct afs_server, cb_break_work.work); | ||
335 | |||
336 | _enter(""); | ||
337 | |||
338 | /* tell the fileserver to discard the callback promises it has | ||
339 | * - in the event of ENOMEM or some other error, we just forget that we | ||
340 | * had callbacks entirely, and the server will call us later to break | ||
341 | * them | ||
342 | */ | ||
343 | afs_fs_give_up_callbacks(server, &afs_async_call); | ||
344 | } | ||
345 | |||
346 | /* | ||
347 | * flush the outstanding callback breaks on a server | ||
348 | */ | ||
349 | void afs_flush_callback_breaks(struct afs_server *server) | ||
350 | { | ||
351 | cancel_delayed_work(&server->cb_break_work); | ||
352 | queue_delayed_work(afs_callback_update_worker, | ||
353 | &server->cb_break_work, 0); | ||
354 | } | ||
125 | 355 | ||
126 | spin_unlock(&afs_cb_hash_lock); | 356 | #if 0 |
127 | 357 | /* | |
128 | if (inode) { | 358 | * update a bunch of callbacks |
129 | /* we've found the record for this vnode */ | 359 | */ |
130 | spin_lock(&vnode->lock); | 360 | static void afs_callback_updater(struct work_struct *work) |
131 | if (vnode->cb_server == server) { | 361 | { |
132 | /* the callback _is_ on the calling server */ | 362 | struct afs_server *server; |
133 | vnode->cb_server = NULL; | 363 | struct afs_vnode *vnode, *xvnode; |
134 | valid = 1; | 364 | time_t now; |
135 | 365 | long timeout; | |
136 | afs_kafstimod_del_timer(&vnode->cb_timeout); | 366 | int ret; |
137 | vnode->flags |= AFS_VNODE_CHANGED; | 367 | |
138 | 368 | server = container_of(work, struct afs_server, updater); | |
139 | spin_lock(&server->cb_lock); | 369 | |
140 | list_del_init(&vnode->cb_link); | 370 | _enter(""); |
141 | spin_unlock(&server->cb_lock); | 371 | |
142 | 372 | now = get_seconds(); | |
143 | spin_lock(&afs_cb_hash_lock); | 373 | |
144 | list_del_init(&vnode->cb_hash_link); | 374 | /* find the first vnode to update */ |
145 | spin_unlock(&afs_cb_hash_lock); | 375 | spin_lock(&server->cb_lock); |
146 | } | 376 | for (;;) { |
147 | spin_unlock(&vnode->lock); | 377 | if (RB_EMPTY_ROOT(&server->cb_promises)) { |
148 | 378 | spin_unlock(&server->cb_lock); | |
149 | if (valid) { | 379 | _leave(" [nothing]"); |
150 | invalidate_remote_inode(inode); | 380 | return; |
151 | afs_put_server(server); | ||
152 | } | ||
153 | iput(inode); | ||
154 | } | 381 | } |
382 | |||
383 | vnode = rb_entry(rb_first(&server->cb_promises), | ||
384 | struct afs_vnode, cb_promise); | ||
385 | if (atomic_read(&vnode->usage) > 0) | ||
386 | break; | ||
387 | rb_erase(&vnode->cb_promise, &server->cb_promises); | ||
388 | vnode->cb_promised = false; | ||
155 | } | 389 | } |
156 | 390 | ||
157 | _leave(" = 0"); | 391 | timeout = vnode->update_at - now; |
158 | return 0; | 392 | if (timeout > 0) { |
159 | } /* end SRXAFSCM_CallBack() */ | 393 | queue_delayed_work(afs_vnode_update_worker, |
394 | &afs_vnode_update, timeout * HZ); | ||
395 | spin_unlock(&server->cb_lock); | ||
396 | _leave(" [nothing]"); | ||
397 | return; | ||
398 | } | ||
399 | |||
400 | list_del_init(&vnode->update); | ||
401 | atomic_inc(&vnode->usage); | ||
402 | spin_unlock(&server->cb_lock); | ||
403 | |||
404 | /* we can now perform the update */ | ||
405 | _debug("update %s", vnode->vldb.name); | ||
406 | vnode->state = AFS_VL_UPDATING; | ||
407 | vnode->upd_rej_cnt = 0; | ||
408 | vnode->upd_busy_cnt = 0; | ||
409 | |||
410 | ret = afs_vnode_update_record(vl, &vldb); | ||
411 | switch (ret) { | ||
412 | case 0: | ||
413 | afs_vnode_apply_update(vl, &vldb); | ||
414 | vnode->state = AFS_VL_UPDATING; | ||
415 | break; | ||
416 | case -ENOMEDIUM: | ||
417 | vnode->state = AFS_VL_VOLUME_DELETED; | ||
418 | break; | ||
419 | default: | ||
420 | vnode->state = AFS_VL_UNCERTAIN; | ||
421 | break; | ||
422 | } | ||
423 | |||
424 | /* and then reschedule */ | ||
425 | _debug("reschedule"); | ||
426 | vnode->update_at = get_seconds() + afs_vnode_update_timeout; | ||
427 | |||
428 | spin_lock(&server->cb_lock); | ||
429 | |||
430 | if (!list_empty(&server->cb_promises)) { | ||
431 | /* next update in 10 minutes, but wait at least 1 second more | ||
432 | * than the newest record already queued so that we don't spam | ||
433 | * the VL server suddenly with lots of requests | ||
434 | */ | ||
435 | xvnode = list_entry(server->cb_promises.prev, | ||
436 | struct afs_vnode, update); | ||
437 | if (vnode->update_at <= xvnode->update_at) | ||
438 | vnode->update_at = xvnode->update_at + 1; | ||
439 | xvnode = list_entry(server->cb_promises.next, | ||
440 | struct afs_vnode, update); | ||
441 | timeout = xvnode->update_at - now; | ||
442 | if (timeout < 0) | ||
443 | timeout = 0; | ||
444 | } else { | ||
445 | timeout = afs_vnode_update_timeout; | ||
446 | } | ||
447 | |||
448 | list_add_tail(&vnode->update, &server->cb_promises); | ||
449 | |||
450 | _debug("timeout %ld", timeout); | ||
451 | queue_delayed_work(afs_vnode_update_worker, | ||
452 | &afs_vnode_update, timeout * HZ); | ||
453 | spin_unlock(&server->cb_lock); | ||
454 | afs_put_vnode(vl); | ||
455 | } | ||
456 | #endif | ||
457 | |||
458 | /* | ||
459 | * initialise the callback update process | ||
460 | */ | ||
461 | int __init afs_callback_update_init(void) | ||
462 | { | ||
463 | afs_callback_update_worker = | ||
464 | create_singlethread_workqueue("kafs_callbackd"); | ||
465 | return afs_callback_update_worker ? 0 : -ENOMEM; | ||
466 | } | ||
160 | 467 | ||
161 | /*****************************************************************************/ | ||
162 | /* | 468 | /* |
163 | * allow the fileserver to see if the cache manager is still alive | 469 | * shut down the callback update process |
164 | */ | 470 | */ |
165 | int SRXAFSCM_Probe(struct afs_server *server) | 471 | void __exit afs_callback_update_kill(void) |
166 | { | 472 | { |
167 | _debug("SRXAFSCM_Probe(%p)\n", server); | 473 | destroy_workqueue(afs_callback_update_worker); |
168 | return 0; | 474 | } |
169 | } /* end SRXAFSCM_Probe() */ | ||