]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
afs: Fix cleanup of immediately failed async calls
authorDavid Howells <dhowells@redhat.com>
Mon, 16 Dec 2024 20:41:14 +0000 (20:41 +0000)
committerChristian Brauner <brauner@kernel.org>
Fri, 20 Dec 2024 21:34:08 +0000 (22:34 +0100)
If we manage to begin an async call, but fail to transmit any data on it
due to a signal, we then abort it which causes a race between the
notification of call completion from rxrpc and our attempt to cancel the
notification.  The notification will be necessary, however, for async
FetchData to terminate the netfs subrequest.

However, since we get a notification from rxrpc upon completion of a call
(aborted or otherwise), we can just leave it to that.

This leads to calls not getting cleaned up, but appearing in
/proc/net/rxrpc/calls as being aborted with code 6.

Fix this by making the "error_do_abort:" case of afs_make_call() abort the
call and then abandon it to the notification handler.

Fixes: 34fa47612bfe ("afs: Fix race in async call refcounting")
Reported-by: Marc Dionne <marc.dionne@auristor.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Link: https://lore.kernel.org/r/20241216204124.3752367-25-dhowells@redhat.com
cc: linux-afs@lists.infradead.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/afs/internal.h
fs/afs/rxrpc.c
include/trace/events/afs.h

index 39d2e29ed0e004804e015bc5aa3e8de5e91e4ed3..96fc466efd10b73545cadb43dd37f3e5f173a2ff 100644 (file)
@@ -1336,6 +1336,15 @@ extern void afs_send_simple_reply(struct afs_call *, const void *, size_t);
 extern int afs_extract_data(struct afs_call *, bool);
 extern int afs_protocol_error(struct afs_call *, enum afs_eproto_cause);
 
+static inline void afs_see_call(struct afs_call *call, enum afs_call_trace why)
+{
+       int r = refcount_read(&call->ref);
+
+       trace_afs_call(call->debug_id, why, r,
+                      atomic_read(&call->net->nr_outstanding_calls),
+                      __builtin_return_address(0));
+}
+
 static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *call,
                                    gfp_t gfp)
 {
index 9f2a3bb56ec69e49a1ce4c7a60fe898b322c5e43..a122c6366ce19f242ea24f56ec959aa155d6d371 100644 (file)
@@ -430,11 +430,16 @@ void afs_make_call(struct afs_call *call, gfp_t gfp)
        return;
 
 error_do_abort:
-       if (ret != -ECONNABORTED) {
+       if (ret != -ECONNABORTED)
                rxrpc_kernel_abort_call(call->net->socket, rxcall,
                                        RX_USER_ABORT, ret,
                                        afs_abort_send_data_error);
-       } else {
+       if (call->async) {
+               afs_see_call(call, afs_call_trace_async_abort);
+               return;
+       }
+
+       if (ret == -ECONNABORTED) {
                len = 0;
                iov_iter_kvec(&msg.msg_iter, ITER_DEST, NULL, 0, 0);
                rxrpc_kernel_recv_data(call->net->socket, rxcall,
@@ -445,6 +450,8 @@ error_do_abort:
        call->error = ret;
        trace_afs_call_done(call);
 error_kill_call:
+       if (call->async)
+               afs_see_call(call, afs_call_trace_async_kill);
        if (call->type->done)
                call->type->done(call);
 
@@ -602,7 +609,6 @@ local_abort:
        abort_code = 0;
 call_complete:
        afs_set_call_complete(call, ret, remote_abort);
-       state = AFS_CALL_COMPLETE;
        goto done;
 }
 
index 49a749672e38ee1aa463ddece5cc429cbb972fda..cdb5f2af779926fca1d16c69285c8fa26ec85710 100644 (file)
@@ -118,6 +118,8 @@ enum yfs_cm_operation {
  */
 #define afs_call_traces \
        EM(afs_call_trace_alloc,                "ALLOC") \
+       EM(afs_call_trace_async_abort,          "ASYAB") \
+       EM(afs_call_trace_async_kill,           "ASYKL") \
        EM(afs_call_trace_free,                 "FREE ") \
        EM(afs_call_trace_get,                  "GET  ") \
        EM(afs_call_trace_put,                  "PUT  ") \