aboutsummaryrefslogtreecommitdiffstats
path: root/fs/afs
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2019-01-10 10:40:50 -0500
committerDavid Howells <dhowells@redhat.com>2019-01-17 10:17:28 -0500
commit34fa47612bfe5d7de7fcaf658a6952b6aeec3b13 (patch)
treeda3d177e153d5cf07192e355bb885d4f9a799279 /fs/afs
parent7a75b0079a1d54e342c502c3c8107ba97e05d3d3 (diff)
afs: Fix race in async call refcounting
There's a race between afs_make_call() and afs_wake_up_async_call() in the case that an error is returned from rxrpc_kernel_send_data() after it has queued the final packet. afs_make_call() will try and clean up the mess, but the call state may have been moved on thereby causing afs_process_async_call() to also try and to delete the call. Fix this by: (1) Getting an extra ref for an asynchronous call for the call itself to hold. This makes sure the call doesn't evaporate on us accidentally and will allow the call to be retained by the caller in a future patch. The ref is released on leaving afs_make_call() or afs_wait_for_call_to_complete(). (2) In the event of an error from rxrpc_kernel_send_data(): (a) Don't set the call state to AFS_CALL_COMPLETE until *after* the call has been aborted and ended. This prevents afs_deliver_to_call() from doing anything with any notifications it gets. (b) Explicitly end the call immediately to prevent further callbacks. (c) Cancel any queued async_work and wait for the work if it's executing. This allows us to be sure the race won't recur when we change the state. We put the work queue's ref on the call if we managed to cancel it. (d) Put the call's ref that we got in (1). This belongs to us as long as the call is in state AFS_CALL_CL_REQUESTING. Fixes: 341f741f04be ("afs: Refcount the afs_call struct") Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'fs/afs')
-rw-r--r--fs/afs/rxrpc.c35
1 files changed, 30 insertions, 5 deletions
diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c
index 4830e0a6bf1d..2c588f9bbbda 100644
--- a/fs/afs/rxrpc.c
+++ b/fs/afs/rxrpc.c
@@ -23,6 +23,7 @@ struct workqueue_struct *afs_async_calls;
23static void afs_wake_up_call_waiter(struct sock *, struct rxrpc_call *, unsigned long); 23static void afs_wake_up_call_waiter(struct sock *, struct rxrpc_call *, unsigned long);
24static long afs_wait_for_call_to_complete(struct afs_call *, struct afs_addr_cursor *); 24static long afs_wait_for_call_to_complete(struct afs_call *, struct afs_addr_cursor *);
25static void afs_wake_up_async_call(struct sock *, struct rxrpc_call *, unsigned long); 25static void afs_wake_up_async_call(struct sock *, struct rxrpc_call *, unsigned long);
26static void afs_delete_async_call(struct work_struct *);
26static void afs_process_async_call(struct work_struct *); 27static void afs_process_async_call(struct work_struct *);
27static void afs_rx_new_call(struct sock *, struct rxrpc_call *, unsigned long); 28static void afs_rx_new_call(struct sock *, struct rxrpc_call *, unsigned long);
28static void afs_rx_discard_new_call(struct rxrpc_call *, unsigned long); 29static void afs_rx_discard_new_call(struct rxrpc_call *, unsigned long);
@@ -404,6 +405,12 @@ long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call,
404 } 405 }
405 } 406 }
406 407
408 /* If the call is going to be asynchronous, we need an extra ref for
409 * the call to hold itself so the caller need not hang on to its ref.
410 */
411 if (call->async)
412 afs_get_call(call, afs_call_trace_get);
413
407 /* create a call */ 414 /* create a call */
408 rxcall = rxrpc_kernel_begin_call(call->net->socket, srx, call->key, 415 rxcall = rxrpc_kernel_begin_call(call->net->socket, srx, call->key,
409 (unsigned long)call, 416 (unsigned long)call,
@@ -444,15 +451,17 @@ long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call,
444 goto error_do_abort; 451 goto error_do_abort;
445 } 452 }
446 453
447 /* at this point, an async call may no longer exist as it may have 454 /* Note that at this point, we may have received the reply or an abort
448 * already completed */ 455 * - and an asynchronous call may already have completed.
449 if (call->async) 456 */
457 if (call->async) {
458 afs_put_call(call);
450 return -EINPROGRESS; 459 return -EINPROGRESS;
460 }
451 461
452 return afs_wait_for_call_to_complete(call, ac); 462 return afs_wait_for_call_to_complete(call, ac);
453 463
454error_do_abort: 464error_do_abort:
455 call->state = AFS_CALL_COMPLETE;
456 if (ret != -ECONNABORTED) { 465 if (ret != -ECONNABORTED) {
457 rxrpc_kernel_abort_call(call->net->socket, rxcall, 466 rxrpc_kernel_abort_call(call->net->socket, rxcall,
458 RX_USER_ABORT, ret, "KSD"); 467 RX_USER_ABORT, ret, "KSD");
@@ -469,8 +478,24 @@ error_do_abort:
469error_kill_call: 478error_kill_call:
470 if (call->type->done) 479 if (call->type->done)
471 call->type->done(call); 480 call->type->done(call);
472 afs_put_call(call); 481
482 /* We need to dispose of the extra ref we grabbed for an async call.
483 * The call, however, might be queued on afs_async_calls and we need to
484 * make sure we don't get any more notifications that might requeue it.
485 */
486 if (call->rxcall) {
487 rxrpc_kernel_end_call(call->net->socket, call->rxcall);
488 call->rxcall = NULL;
489 }
490 if (call->async) {
491 if (cancel_work_sync(&call->async_work))
492 afs_put_call(call);
493 afs_put_call(call);
494 }
495
473 ac->error = ret; 496 ac->error = ret;
497 call->state = AFS_CALL_COMPLETE;
498 afs_put_call(call);
474 _leave(" = %d", ret); 499 _leave(" = %d", ret);
475 return ret; 500 return ret;
476} 501}