From nobody Thu Nov 5 05:12:14 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=209.85.128.98 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793848034; b=twqO8cC0GAfVmGMIu0MYYzl6ECwdRdtIfE3ClATKRwp13k9ir32WAKXPlcGz2ISiihhT ch8vEMlq/W1mqgzHQ17A2Q9qSqK1WSFWbvACH7eqKGjj64wsezTBGWEog0NsclYqjyoaL NYY+srKtkAlMXEpQXnj0slJVPlW6M1ykh74Ek0VmD7KCEoSIyCsQ9i8OQV0HBIBzjaEGn Zjwbihl2ZocTKExRSpqSd+nZv1d2nPYprZUSqLlVDEFevbzF+oEjZg5x4sGmisssUtHC2 2QE8s9SRNmNwKgcLzHk13EC5l9Z9/3bAlDNLu0mJYKJw/qK/MDCnlUh8a1Bvg0GvaeQ== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793848034; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Forwarded-Encrypted:X-Gm-Message-State:X-Gm-Gg:X-Received:Received: X-Relaying-Domain:Received:From:To:Cc:Subject:Date:Message-ID: X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=8Mu+ctl0F6aoJsinZtlfKG6JUy3jrYZ2HArMXjUBfyI=; b=CbuXaKQopDrABuO+Z35Xkd7pc47OMq2QNGqQ0TFjCV/2yCA68KuxwV7kUnJmZ2Pic7Vg oau2bAnGQapWzRBrNqhQWG0xeJ3CiOEs0OYmrGigWzYqd7sgFSO5nSoUvE9WLTg0eYqzw S/JU+gvoAn7azdSdcV+wvhQ88RFeU1m6gZQKdMcsoUXyG1+pz7oyu90vUmL3HKz9nGYX0 PeJlsTkWRLfhCszjN1YOq3hxEFTl2fM1jWEQ2AuYioN9Cw1fvsiNpfqw2/my4yrPRCF4t iaouh+njyQW9CiGHBQKn7zKEWxz4GETqSUNWc4qi9igEBvNiSc8oglxNSBRUh9bncqQ== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=everpuredata.com; spf=pass smtp.mailfrom=everpuredata.com; dkim=pass header.d=everpuredata.com header.i=@everpuredata.com header.a=rsa-sha256 header.s=google header.b=EnPqUY30; arc=none smtp.remote-ip=209.85.128.98 Received: from mail-wm1-f98.google.com (mail-wm1-f98.google.com [209.85.128.98]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 56136606A32D for ; Thu, 5 Nov 2026 03:07:13 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 56136606A32D Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=everpuredata.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=everpuredata.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=everpuredata.com header.i=@everpuredata.com header.a=rsa-sha256 header.s=google header.b=EnPqUY30 Received: by mail-wm1-f98.google.com with SMTP id 5b1f17b1804b1-4a5177d78c7so12131015e9.2 for ; Wed, 04 Nov 2026 19:07:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=everpuredata.com; s=google; t=1793848032; x=1794452832; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=8Mu+ctl0F6aoJsinZtlfKG6JUy3jrYZ2HArMXjUBfyI=; b=EnPqUY30yLpDdsF9qJe12aCwrUsrc3yTq9Q7JV5zh5N1NhDxctt6MfrZdqBlX/aSsP A5G/AgJ9V7dCE01dl2ql+Wy6++RyB9XknBntm4JlBrF9R+gUzpZK9IlEnx5GQMdq27R9 vWr7qVLZSbxB24I5fzRSoYTiRU0HXdqNqRjRdWDKr4KobUHccfPDIO/3fRGNTrzAMR3X 1aDlAB477tv+qAE0BxJX+sftiL18b3Rf9Askpt7e0nBamsr1cJ1Fov7i9768qRt0AzKY Wmj87ntwgAd8X16Ah5utrEATbHlxbENQlSD5T09RJCCQsYmxAdQrzM/eQO8ubdzGXaQO 3xfw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793848032; x=1794452832; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=8Mu+ctl0F6aoJsinZtlfKG6JUy3jrYZ2HArMXjUBfyI=; b=Bv1AVc2M3s1AmvBJ0DvZk/MInLaXMcUim1hfCXiq4y+eymKJiM1GVoIcqG9vkGE/PX 9zf3Ou6Rr/r9rlzJ8fXsbwFb1AOldxcyH01WAgJ4p+vEWTJBSK+qnNbbNMcSw9CTt+2H kVjjKl30YvDlfaoMjcs/dkv+iZ/VvT8yKvEIni9zafC3eui6aVY941q35ZiP0HwZXpF8 eiE2i+h/rAI29ccMeznlpu3APM8p0owkVtuW8ni6UexXXzSKwmI9E1rrDZiI+Lf3SLR5 9uzP1edft0AaJMkEom+oFQJYUCpE6nhhTS28aZSoSZNgx5U71l2CfH+uwGhQ1kktqmTW 3giw== X-Forwarded-Encrypted: i=1; AKwUvBzrz9P5funkomXmrOzcYE6V/SXeLSeXdYetOwGkenBdeF7dxVZcf23B29OVRYyr1XxPbwgLOsjgLitwF94=@vger.kernel.org X-Gm-Message-State: APuU4Z+ZH8AXmpYhWsS/sq8y5Xul1H87tEp0rYBWc2xNH937R5f69Mh1 bRqyVv9Nr6izW5GE0oLHDwX+K/FH4RYxVR8sJsLpB7/6QLqxZlSO4lerjuwfAtKf1T/p6HrZDUQ w0Jm+jVTPoY4pvNifHsYcCyJQo9++5P12qVKD X-Gm-Gg: AYBFou0jaKaDC3C/i80qvDMvgm1VjF3eqT5UDQgofJS6JnPwv6dkU9oZfUkEnqRH8xM Nc7nNrt4+Cvymg87Now8Y1PJTqunQACJ8Oyhg7ZYUcRtpuTmv1goSot4lY8a81uPyHfXHtvnyhJ ohFi0Uvr4prfIsEiYoYIAvTx/bk/HXN7e/SL6BT/k8+HZE2kETMqUj3t+m6KgMu53+0tTeu30Hx KzjdkHN/74IZmgzQ0EeQKcrfnYo9R1B7Vh8i9h2QNz2koJJrEivuKaGbTwUZ1a1/UItropqYPtP UNuI8lsYGWH7Jx0QYFwdFJXdc3hQJx7PxttECkmymIjZ8oVKqXoED3rChuk69qZ5ljhcjGnCUGx AXwnPrCRWq/aI3n2stPuEM9BffhsDunoP4bBVNMg= X-Received: by 2002:a05:600c:6306:b0:4aa:15b1:d4fa with SMTP id 5b1f17b1804b1-4ac17b2ce91mr426370325e9.5.1793848031575; Wed, 04 Nov 2026 19:07:11 -0800 (PST) Received: from c14-smtp-2023.dev.purestorage.com ([208.88.158.128]) by smtp-relay.gmail.com with ESMTPS id 5b1f17b1804b1-4b57317c6a9sm2336755e9.8.2026.11.04.19.07.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 04 Nov 2026 19:07:11 -0800 (PST) X-Relaying-Domain: everpuredata.com Received: from irdv-tmenninger.dev.purestorage.com (irdv-tmenninger.dev.purestorage.com [10.32.149.15]) by c14-smtp-2023.dev.purestorage.com (Postfix) with ESMTPS id 11FF634058C; Wed, 4 Nov 2026 19:07:10 -0800 (PST) From: Tim Menninger To: Trond Myklebust , Anna Schumaker Cc: Chuck Lever , Jeff Layton , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, Eric Badger , Jon Curley Subject: [PATCH 1/2] SUNRPC: maintain an rpc_task signalled flag Date: Thu, 5 Nov 2026 03:07:08 +0000 Message-ID: <20261105030710.2652306-2-tmenninger@everpuredata.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20261105030710.2652306-1-tmenninger@everpuredata.com> References: <20261105030710.2652306-1-tmenninger@everpuredata.com> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Commit 5bbd6e863b15a ("SUNRPC: Prevent looping due to rpc_signal_task() races") removed the RPC_TASK_SIGNALLED runstate flag because maintaining it independently from the errno in tk_rpc_status introduced races between an errno being recorded and the task being in a SIGNALLED state. A signalled task is inferred by tk_rpc_status containing -ERESTARTSYS. This reintroduces the concept by coupling a "task signalled" bit with the errno. The tk_rpc_status int becomes a tk_rpc_error_state atomic64_t whose low 32 bits are a signed errno representing the first observed RPC error, as tk_rpc_status previously held, and bit 32 is 1 if the task is signalled and 0 otherwise. With this change, we can record an RPC status and later escalate the status to "signalled" without overwriting (and losing) the first recorded error. Signed-off-by: Tim Menninger --- fs/nfs/nfs4proc.c | 12 ++++++++---- fs/nfs/pnfs.c | 11 +++++++---- include/linux/sunrpc/sched.h | 21 +++++++++++++++++++-- net/sunrpc/clnt.c | 2 +- net/sunrpc/sched.c | 34 +++++++++++++++++++++++++++------- 5 files changed, 62 insertions(+), 18 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index e1018b03c1cbd..1face07f409b4 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -688,9 +688,10 @@ nfs4_async_handle_exception(struct rpc_task *task, str= uct nfs_server *server, { struct nfs_client *clp =3D server->nfs_client; int ret; + int task_rpc_status =3D rpc_task_rpc_status(task); =20 - if ((task->tk_rpc_status =3D=3D -ENETDOWN || - task->tk_rpc_status =3D=3D -ENETUNREACH) && + if ((task_rpc_status =3D=3D -ENETDOWN || + task_rpc_status =3D=3D -ENETUNREACH) && task->tk_flags & RPC_TASK_NETUNREACH_FATAL) { exception->delay =3D 0; exception->recovering =3D 0; @@ -9845,12 +9846,15 @@ static void nfs4_layoutreturn_done(struct rpc_task = *task, void *calldata) { struct nfs4_layoutreturn *lrp =3D calldata; struct nfs_server *server; + int task_rpc_status; =20 if (!nfs41_sequence_process(task, &lrp->res.seq_res)) return; =20 - if (task->tk_rpc_status < 0) { - switch (task->tk_rpc_status) { + task_rpc_status =3D rpc_task_rpc_status(task); + + if (task_rpc_status < 0) { + switch (task_rpc_status) { case -EACCES: case -EIO: case -EKEYEXPIRED: diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index a057a06318c76..c51654e9d32eb 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1687,6 +1687,7 @@ int pnfs_roc_done(struct rpc_task *task, struct nfs4_= layoutreturn_args **argpp, { struct nfs4_layoutreturn_args *arg =3D *argpp; int retval =3D -EAGAIN; + int task_rpc_status; =20 if (!arg) return 0; @@ -1696,15 +1697,17 @@ int pnfs_roc_done(struct rpc_task *task, struct nfs= 4_layoutreturn_args **argpp, retval =3D 0; break; case -NFS4ERR_NOMATCHING_LAYOUT: + task_rpc_status =3D rpc_task_rpc_status(task); + /* Was there an RPC level error? If not, retry */ - if (task->tk_rpc_status =3D=3D 0) + if (task_rpc_status =3D=3D 0) break; /* * Is there a fatal network level error? * If so release the layout, but flag the error. */ - if ((task->tk_rpc_status =3D=3D -ENETDOWN || - task->tk_rpc_status =3D=3D -ENETUNREACH) && + if ((task_rpc_status =3D=3D -ENETDOWN || + task_rpc_status =3D=3D -ENETUNREACH) && task->tk_flags & RPC_TASK_NETUNREACH_FATAL) { *ret =3D 0; (*respp)->lrs_present =3D 0; @@ -1714,7 +1717,7 @@ int pnfs_roc_done(struct rpc_task *task, struct nfs4_= layoutreturn_args **argpp, /* If the call was not sent, let caller handle it */ if (!RPC_WAS_SENT(task)) return 0; - switch (task->tk_rpc_status) { + switch (task_rpc_status) { default: /* * Defer the layoutreturn if it was due diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index dbaad8b5f181c..db322e26703a4 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -10,6 +10,7 @@ #ifndef _LINUX_SUNRPC_SCHED_H_ #define _LINUX_SUNRPC_SCHED_H_ =20 +#include #include #include #include @@ -92,7 +93,10 @@ struct rpc_task { =20 pid_t tk_owner; /* Process id for batching tasks */ =20 - int tk_rpc_status; /* Result of last RPC operation */ + atomic64_t tk_rpc_error_state; + /* Low 32 bits: first recorded RPC error. + * Bit 32: signal requested. + */ unsigned short tk_flags; /* misc flags */ unsigned short tk_timeouts; /* maj timeouts */ unsigned short tk_pid; /* debugging aid */ @@ -158,6 +162,18 @@ enum { RPC_TASK_MSG_PIN_WAIT, }; =20 +#define RPC_TASK_ERR_STATE_SIGNALLED (1LL << 32) + +static inline s64 rpc_task_read_error_state(const struct rpc_task *task) +{ + return atomic64_read(&task->tk_rpc_error_state); +} + +static inline int rpc_task_rpc_status(const struct rpc_task *task) +{ + return (int)(s32)rpc_task_read_error_state(task); +} + #define rpc_test_and_set_running(t) \ test_and_set_bit(RPC_TASK_RUNNING, &(t)->tk_runstate) #define rpc_clear_running(t) clear_bit(RPC_TASK_RUNNING, &(t)->tk_runstate) @@ -168,7 +184,8 @@ enum { =20 #define RPC_IS_ACTIVATED(t) test_bit(RPC_TASK_ACTIVE, &(t)->tk_runstate) =20 -#define RPC_SIGNALLED(t) (READ_ONCE(task->tk_rpc_status) =3D=3D -ERESTARTS= YS) +#define RPC_SIGNALLED(t) (!!(atomic64_read(&(t)->tk_rpc_error_state) & \ + RPC_TASK_ERR_STATE_SIGNALLED)) =20 /* * Task priorities. diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 5c33bf3fb4c34..cf17dfff57e90 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1758,7 +1758,7 @@ static int __rpc_restart_call(struct rpc_task *task, void (*action)(struct rpc_task *= )) { task->tk_status =3D 0; - task->tk_rpc_status =3D 0; + atomic64_set(&task->tk_rpc_error_state, 0); task->tk_action =3D action; return 1; } diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 016f16ca57799..81890327d81a9 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -67,9 +67,17 @@ EXPORT_SYMBOL_GPL(rpc_task_gfp_mask); =20 bool rpc_task_set_rpc_status(struct rpc_task *task, int rpc_status) { - if (cmpxchg(&task->tk_rpc_status, 0, rpc_status) =3D=3D 0) - return true; - return false; + s64 old =3D atomic64_read(&task->tk_rpc_error_state); + s64 new; + + for (;;) { + if ((s32)old) + return false; + new =3D (old & RPC_TASK_ERR_STATE_SIGNALLED) | + ((s64)(u32)rpc_status); + if (atomic64_try_cmpxchg(&task->tk_rpc_error_state, &old, new)) + return true; + } } =20 unsigned long @@ -857,12 +865,24 @@ void rpc_exit_task(struct rpc_task *task) void rpc_signal_task(struct rpc_task *task) { struct rpc_wait_queue *queue; + s64 old, new; =20 if (!RPC_IS_ACTIVATED(task)) return; =20 - if (!rpc_task_set_rpc_status(task, -ERESTARTSYS)) - return; + old =3D atomic64_read(&task->tk_rpc_error_state); + for (;;) { + if (old & RPC_TASK_ERR_STATE_SIGNALLED) + return; + + new =3D old | RPC_TASK_ERR_STATE_SIGNALLED; + if (!((s32)old)) + new |=3D (s64)(u32)-ERESTARTSYS; + + if (atomic64_try_cmpxchg(&task->tk_rpc_error_state, &old, new)) + break; + } + trace_rpc_task_signalled(task, task->tk_action); queue =3D READ_ONCE(task->tk_waitqueue); if (queue) @@ -928,7 +948,7 @@ static void __rpc_execute(struct rpc_task *task) do_action =3D task->tk_action; /* Tasks with an RPC error status should exit */ if (do_action && do_action !=3D rpc_exit_task && - (status =3D READ_ONCE(task->tk_rpc_status)) !=3D 0) { + (status =3D rpc_task_rpc_status(task)) !=3D 0) { task->tk_status =3D status; do_action =3D rpc_exit_task; } @@ -970,7 +990,7 @@ static void __rpc_execute(struct rpc_task *task) continue; } /* Wake up any task that has an exit status */ - if (READ_ONCE(task->tk_rpc_status) !=3D 0) { + if (rpc_task_rpc_status(task) !=3D 0) { rpc_wake_up_task_queue_locked(queue, task); spin_unlock(&queue->lock); continue; --=20 2.53.0 From nobody Thu Nov 5 05:12:14 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=209.85.128.99 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793848034; b=n2ROAp8mDMdedfPx6AMyuIKbIppWOKIx9wPJV8xT6c1wUs+1/2bRVfwN4nuk+XmlNZOc oCOZc6tBO19M67N/F2FS/B3DRRaezP3kSe/mvNTsR9Qt0x9+hNRmCDkzfU5tKbvjpFhfM JXezNQwpam2oOJ2mphEEPMzgGLh4Nz49x6ASwO6h92R0ECnVjQAMa6tCOf9+tVkdl2xYP vRPWfc0kPflwvGtg4fSOukukna+2HaXa3byqEicymiQDZDfqEjrwISzCUT4P3fo6ymFkz Xfp3Ig3Z3jbG0ECw4w9p9JAM4JTbCoy6i4Vdz1+YzwbVSMnnBu/Wp5RPsEgEJKTcOWQ== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793848034; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Forwarded-Encrypted:X-Gm-Message-State:X-Gm-Gg:X-Received:Received: X-Relaying-Domain:Received:From:To:Cc:Subject:Date:Message-ID: X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=gofCgoStwL+GGmvkx772mdWpX9NH6aCBFh77V0tI1nk=; b=AKozZorGn9Oh3I1gDjMUHAEToRZN8ITmiOZmKhz7th5pCyMzMVkdAa1+DTbHvGUkae/6 +a49kBbWaKkQim3TQ8DgjYTfkgAh9ayvL/fQ3kB4sNGLodsdJKMhrmZFoM6t0hZ8VCccu hTFjK2v5o1JoRX5ORFHA1XxtiLtda0dYMgGL4cp/GChS8vfIzgaYTsEP5pSD667gsVX2R o2FnuSoUkzyCQX4Xby4vsb13giFr/Ox0MYuQ6eIEJ+1dhar7iwDchhvQYfWabqQLbeBu7 0ozSmJTRWNtdCNe4XF5QuxKhZbkz5glesPOTlEvnV9dnrJkEfise2G7V1k4BG07vILg== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=everpuredata.com; spf=pass smtp.mailfrom=everpuredata.com; dkim=pass header.d=everpuredata.com header.i=@everpuredata.com header.a=rsa-sha256 header.s=google header.b=an9lTztR; arc=none smtp.remote-ip=209.85.128.99 Received: from mail-wm1-f99.google.com (mail-wm1-f99.google.com [209.85.128.99]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5C603606A32F for ; Thu, 5 Nov 2026 03:07:13 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 5C603606A32F Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=everpuredata.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=everpuredata.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=everpuredata.com header.i=@everpuredata.com header.a=rsa-sha256 header.s=google header.b=an9lTztR Received: by mail-wm1-f99.google.com with SMTP id 5b1f17b1804b1-4b4571ff080so14732175e9.1 for ; Wed, 04 Nov 2026 19:07:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=everpuredata.com; s=google; t=1793848032; x=1794452832; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=gofCgoStwL+GGmvkx772mdWpX9NH6aCBFh77V0tI1nk=; b=an9lTztRrSc8awuaGF7XSnJfGHMtpLoc/LpdJwS+6MezKB6Cx+0JkzH9dUjJkN5/8V OyNxbLdogCKoHIxRjUis/Sh34jLMiPu9Li6mSD0+nPh5QrdnZZkkEaX+fvU0AhFXsaDE JXgEsL+ljsc3XZSnaZuhHpFpyhVTO0VbGuJq0NnQxUQYAwS7FzGcSnUJ8wHmGuAFXCia QxTj25JnuztSIz8lSTiSOacTIZxBpkceDKlBS1a6KPMgsN5y74zSDaJerO7hXc3KL4qa WGCjcTTiAj1vcVWDS2pKtVXXsQN8uQBVMhYisZqCT1UUR8zMWAJxSCq5pqxrUKOmOi0N W7mw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793848032; x=1794452832; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=gofCgoStwL+GGmvkx772mdWpX9NH6aCBFh77V0tI1nk=; b=1Peh5Fi8ZZgxCE5sqrlkTmdzQSO1+Temux5VvYOZA6ePUhn4SJzHGMuDJGkZUfFERO T8vbqvrc28sQh1uHjeA/3AIC8w0rfLZpLYfRakbksS+o8rRWjUL+V7MGfxstnh17xoyp PQEmftBIWowtygPkkGIKH4/BhNDmGzUtwjbhwutpP4js8BPi85boFoyIlgxe1mWEqb64 ig5gzf1ZhHWmqIK21GKhwEE3kJDHV55Yb6kdCZINdyT0Bta/QxNca8pdFp0RCZRmFsab U0Qev+GQKfGoBJpH1tNY9F4f2n52IuCyWIgz3XM3ODiOLM8uSlGUZKdJ/In8s/ks27jU 38HA== X-Forwarded-Encrypted: i=1; AKwUvBwn8p8cZjkgGwnbWZxqXMd0oiw+Qo+4V42sSSERnx4+qj8f0W+8ieOKxNB2huTqvBv8+0jp8tc+iAh6tNQ=@vger.kernel.org X-Gm-Message-State: APuU4Z8p8gl7JLozW/EzrKhttCT5q1Zi2few5m4xnVjHSbl0xLnCvqyR D6sm8TfLugo14u7tFUlj9NKSVKtATbJqRbvrfxNp6imjTl++39Kdqxkw8GAgky01YAfyD6QaySA Ad7Fvn//Zm6GBKR6FwLnYvCpR+1TUWAAeUXx+ X-Gm-Gg: AYBFou3Nw51fv4lnOflRTQUK+x0krLcBU9hyITToOOGSxJF6ko/YxQInqnnfWVsy7Bb c4eQMngjLPrQkbWwepiTEcV63jQrRFV8zgu2l+xEWlTezw5DTXJ7iFunbLwGI8uRTy9LUOSaEKH KLrNhKv1MJGCWX6esXAPa2+F8P4peCVwpBVtuzgNL3yN7NOuMfoAsbouJrKQhwqywTmoIhrqt05 wm8b3Bs8Up7N15zZYvQSyOrmtvxSpNxYVfJdPPPXbZQR35RaA4OcCwHH9p9CEeSpZePaH6afN+0 tla0DN/XvmUlRzTw56m4f3TAHo35pgXOqJfzK3W6YBAoDoPIPZkeIgM+OIAn3wTtIHX++4/bAlZ Z1PB5wXItNzhj5CIiBmq5OUTqVlgSnmmPVCZZXS0= X-Received: by 2002:a05:600c:c48e:b0:4a3:2f35:38b4 with SMTP id 5b1f17b1804b1-4ac16bc4829mr402811145e9.10.1793848031815; Wed, 04 Nov 2026 19:07:11 -0800 (PST) Received: from c14-smtp-2023.dev.purestorage.com ([208.88.158.128]) by smtp-relay.gmail.com with ESMTPS id 5b1f17b1804b1-4b573b15f60sm2391255e9.11.2026.11.04.19.07.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 04 Nov 2026 19:07:11 -0800 (PST) X-Relaying-Domain: everpuredata.com Received: from irdv-tmenninger.dev.purestorage.com (irdv-tmenninger.dev.purestorage.com [10.32.149.15]) by c14-smtp-2023.dev.purestorage.com (Postfix) with ESMTPS id 198933406CC; Wed, 4 Nov 2026 19:07:10 -0800 (PST) From: Tim Menninger To: Trond Myklebust , Anna Schumaker Cc: Chuck Lever , Jeff Layton , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, Eric Badger , Jon Curley Subject: [PATCH 2/2] SUNRPC: let cancelled RPC/RDMA requests drain before release Date: Thu, 5 Nov 2026 03:07:09 +0000 Message-ID: <20261105030710.2652306-3-tmenninger@everpuredata.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20261105030710.2652306-1-tmenninger@everpuredata.com> References: <20261105030710.2652306-1-tmenninger@everpuredata.com> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" During pNFS/flexfiles recovery, cancellation can release a posted RPC/RDMA request before the server has finished accessing its Read or Write chunks. xprt_rdma_free() can then invalidate advertised rkeys while the peer still uses them. Resulting QP errors disrupt other RPCs and can sustain a cancellation/reconnect loop after the data server recovers. This was observed on Linux 6.8.0, 6.18.34, and 7.3-rc4. Allow transports to defer exit of cancelled asynchronous tasks while their requests can still complete normally. RPC/RDMA opts in while registered MRs remain on the same live connection. Keep these requests available for reply-driven cleanup, preserving the cancellation error while wake callbacks process their transport status. Use the existing request timeout and transport timer. After normal no-reply timeout handling, finish cancellation instead of re-entering draining. Suppress a requested conditional disconnect when the reply completed before release. Synchronous and signalled tasks skip the optional wait; remaining reclamation uses the existing release path. Signed-off-by: Tim Menninger --- include/linux/sunrpc/xprt.h | 3 + include/trace/events/sunrpc.h | 1 + net/sunrpc/sched.c | 21 +++++- net/sunrpc/xprt.c | 129 +++++++++++++++++++++++++++++++- net/sunrpc/xprtrdma/transport.c | 33 ++++++++ 5 files changed, 180 insertions(+), 7 deletions(-) diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index ad8ecf8ccfb48..8b5b99ee34207 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -177,6 +177,7 @@ struct rpc_xprt_ops { void (*wait_for_reply_request)(struct rpc_task *task); void (*timer)(struct rpc_xprt *xprt, struct rpc_task *task); void (*release_request)(struct rpc_task *task); + bool (*release_needs_drain)(struct rpc_rqst *req); void (*close)(struct rpc_xprt *xprt); void (*destroy)(struct rpc_xprt *xprt); void (*set_connect_timeout)(struct rpc_xprt *xprt, @@ -395,6 +396,8 @@ bool xprt_prepare_transmit(struct rpc_task *task); void xprt_request_enqueue_transmit(struct rpc_task *task); int xprt_request_enqueue_receive(struct rpc_task *task); void xprt_request_wait_receive(struct rpc_task *task); +bool xprt_request_release_needs_wait(struct rpc_task *task); +void xprt_request_wait_for_drain(struct rpc_task *task); void xprt_request_dequeue_xprt(struct rpc_task *task); bool xprt_request_need_retransmit(struct rpc_task *task); void xprt_transmit(struct rpc_task *task); diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index ff855197880de..263e8f6520ee3 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h @@ -410,6 +410,7 @@ DEFINE_RPC_RUNNING_EVENT(sync_wake); DEFINE_RPC_RUNNING_EVENT(complete); DEFINE_RPC_RUNNING_EVENT(timeout); DEFINE_RPC_RUNNING_EVENT(signalled); +DEFINE_RPC_RUNNING_EVENT(exit_wait); DEFINE_RPC_RUNNING_EVENT(end); DEFINE_RPC_RUNNING_EVENT(call_done); =20 diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 81890327d81a9..f1806f530fab7 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -932,6 +932,8 @@ static void __rpc_execute(struct rpc_task *task) int task_is_async =3D RPC_IS_ASYNC(task); int status =3D 0; unsigned long pflags =3D current->flags; + bool task_draining; + s64 error_state; =20 WARN_ON_ONCE(RPC_IS_QUEUED(task)); if (RPC_IS_QUEUED(task)) @@ -939,6 +941,7 @@ static void __rpc_execute(struct rpc_task *task) =20 for (;;) { void (*do_action)(struct rpc_task *); + task_draining =3D false; =20 /* * Perform the next FSM step or a pending callback. @@ -949,8 +952,13 @@ static void __rpc_execute(struct rpc_task *task) /* Tasks with an RPC error status should exit */ if (do_action && do_action !=3D rpc_exit_task && (status =3D rpc_task_rpc_status(task)) !=3D 0) { - task->tk_status =3D status; - do_action =3D rpc_exit_task; + if (xprt_request_release_needs_wait(task)) { + task_draining =3D true; + do_action =3D xprt_request_wait_for_drain; + } else { + task->tk_status =3D status; + do_action =3D rpc_exit_task; + } } /* Callbacks override all actions */ if (task->tk_callback) { @@ -989,8 +997,13 @@ static void __rpc_execute(struct rpc_task *task) spin_unlock(&queue->lock); continue; } - /* Wake up any task that has an exit status */ - if (rpc_task_rpc_status(task) !=3D 0) { + /* Wake up any task that has an exit status. A task that has + * deliberately gone to sleep to drain its request is exempted, + * unless the task was signalled (i.e. urgently terminated). + */ + error_state =3D rpc_task_read_error_state(task); + if ((error_state & RPC_TASK_ERR_STATE_SIGNALLED) || + ((s32)error_state !=3D 0 && !task_draining)) { rpc_wake_up_task_queue_locked(queue, task); spin_unlock(&queue->lock); continue; diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 66eca8e4690b6..cf24cb52667da 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -1237,6 +1237,7 @@ static void xprt_timer(struct rpc_task *task) { struct rpc_rqst *req =3D task->tk_rqstp; struct rpc_xprt *xprt =3D req->rq_xprt; + int status; =20 if (task->tk_status !=3D -ETIMEDOUT) return; @@ -1245,6 +1246,13 @@ static void xprt_timer(struct rpc_task *task) if (!req->rq_reply_bytes_recvd) { if (xprt->ops->timer) xprt->ops->timer(xprt, task); + + /* An already-cancelled task should not drain again */ + if (xprt->ops->release_needs_drain) { + status =3D rpc_task_rpc_status(task); + if (status) + rpc_exit(task, status); + } } else task->tk_status =3D 0; } @@ -1528,6 +1536,114 @@ xprt_request_dequeue_xprt(struct rpc_task *task) } } =20 +/** + * xprt_request_release_needs_wait - check whether request release must wa= it + * @task: RPC task being terminated + * + * Returns true if the request is pinned or its transport resources must + * drain before release. Removes pending transmissions when safe to do so. + * + * Context: Called from RPC task execution context. Acquires and releases + * xprt->queue_lock. The transport's ->release_needs_drain() must= not + * sleep. + */ +bool xprt_request_release_needs_wait(struct rpc_task *task) +{ + struct rpc_rqst *req =3D task->tk_rqstp; + struct rpc_xprt *xprt; + bool defer =3D false; + + if (!RPC_IS_ASYNC(task) || RPC_SIGNALLED(task)) + return false; + + if (!req) + return false; + xprt =3D req->rq_xprt; + if (!xprt->ops->release_needs_drain) + return false; + + /* Has not been transmitted yet, no need to defer */ + if (!test_bit(RPC_TASK_NEED_RECV, &task->tk_runstate)) + return false; + + spin_lock(&xprt->queue_lock); + if (xprt_is_pinned_rqst(req)) + defer =3D true; + else if (test_bit(RPC_TASK_NEED_XMIT, &task->tk_runstate)) + xprt_request_dequeue_transmit_locked(task); + else + defer =3D xprt->ops->release_needs_drain(req); + spin_unlock(&xprt->queue_lock); + + return defer; +} + +/** + * xprt_request_wait_for_drain - wait until a failed request can be releas= ed + * @task: RPC task whose request may still be in flight + * + * Like xprt_request_wait_receive(), but for a task that has already + * been cancelled. The task keeps its request on the receive queue and + * sleeps on the pending queue until the reply arrives or the connection + * is lost. The wait is bounded by the request's retransmit timeout, at + * which point the transport's timer callback runs exactly as it would + * for an uncancelled request. + * + * Context: Called from RPC task execution context. May sleep while waiting + * for a pinned request, with xprt->queue_lock released. Holds + * queue_lock when checking request state and queuing the task. + */ +void xprt_request_wait_for_drain(struct rpc_task *task) +{ + struct rpc_rqst *req =3D task->tk_rqstp; + struct rpc_xprt *xprt =3D req->rq_xprt; + + trace_rpc_task_exit_wait(task, task->tk_action); + + /* + * Drop any send ownership handed to this task before waiting. + */ + xprt_release_write(xprt, task); + + /* + * Signals stop optional reply draining, but do not interrupt an + * in-progress wait for request pins to be released. + */ + spin_lock(&xprt->queue_lock); + while (xprt_is_pinned_rqst(req) && !RPC_SIGNALLED(task)) { + set_bit(RPC_TASK_MSG_PIN_WAIT, &task->tk_runstate); + spin_unlock(&xprt->queue_lock); + xprt_wait_on_pinned_rqst(req); + spin_lock(&xprt->queue_lock); + clear_bit(RPC_TASK_MSG_PIN_WAIT, &task->tk_runstate); + } + + if (xprt_is_pinned_rqst(req) || RPC_SIGNALLED(task)) + goto out_unlock; + + /* + * The pinned operation may have completed or failed while we waited. + * Recheck whether the request needs to drain or can be dequeued. + */ + if (test_bit(RPC_TASK_NEED_XMIT, &task->tk_runstate)) { + xprt_request_dequeue_transmit_locked(task); + } else if (test_bit(RPC_TASK_NEED_RECV, &task->tk_runstate) && + xprt->ops->release_needs_drain(req) && + !RPC_SIGNALLED(task)) { + rpc_sleep_on_timeout(&xprt->pending, task, xprt_timer, + xprt_request_timeout(req)); + + if (RPC_SIGNALLED(task)) + rpc_wake_up_queued_task(&xprt->pending, task); + else if (xprt_request_retransmit_after_disconnect(task)) + rpc_wake_up_queued_task_set_status(&xprt->pending, task, + -ENOTCONN); + } + +out_unlock: + spin_unlock(&xprt->queue_lock); +} + /** * xprt_request_prepare - prepare an encoded request for transport * @req: pointer to rpc_rqst @@ -1609,11 +1725,13 @@ xprt_request_transmit(struct rpc_rqst *req, struct = rpc_task *snd_task) unsigned int connect_cookie; int is_retrans =3D RPC_WAS_SENT(task); int status; + s64 error_state; =20 if (test_bit(XPRT_CLOSE_WAIT, &xprt->state)) return -ENOTCONN; =20 if (!req->rq_bytes_sent) { + error_state =3D rpc_task_read_error_state(task); if (xprt_request_data_received(task)) { status =3D 0; goto out_dequeue; @@ -1623,10 +1741,14 @@ xprt_request_transmit(struct rpc_rqst *req, struct = rpc_task *snd_task) status =3D -EBADMSG; goto out_dequeue; } - if (RPC_SIGNALLED(task)) { + if (error_state & RPC_TASK_ERR_STATE_SIGNALLED) { status =3D -ERESTARTSYS; goto out_dequeue; } + if (xprt->ops->release_needs_drain && (s32)error_state !=3D 0) { + status =3D -ECANCELED; + goto out_dequeue; + } } =20 /* @@ -1695,7 +1817,7 @@ xprt_transmit(struct rpc_task *task) xprt_pin_rqst(next); spin_unlock(&xprt->queue_lock); status =3D xprt_request_transmit(next, task); - if (status =3D=3D -EBADMSG && next !=3D req) + if ((status =3D=3D -EBADMSG || status =3D=3D -ECANCELED) && next !=3D re= q) status =3D 0; spin_lock(&xprt->queue_lock); xprt_unpin_rqst(next); @@ -2061,7 +2183,8 @@ void xprt_release(struct rpc_task *task) req->rq_disconnect_on_release =3D false; spin_unlock(&xprt->queue_lock); =20 - if (unlikely(disconnect && req->rq_ntrans > 0)) + if (unlikely(disconnect && req->rq_ntrans > 0 && + !(xprt->ops->release_needs_drain && req->rq_reply_bytes_recvd))) xprt_conditional_disconnect(xprt, req->rq_connect_cookie); =20 spin_lock(&xprt->transport_lock); diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transpor= t.c index e38637c2ec787..ed521d9aa8953 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -434,6 +434,35 @@ xprt_rdma_timer(struct rpc_xprt *xprt, struct rpc_task= *task) xprt_force_disconnect(xprt); } =20 +/** + * xprt_rdma_release_needs_drain - decide whether a cancelled request must= drain + * @rqst: transmitted RPC request whose task has been cancelled + * + * A Call posted on the current connection may have advertised chunks that= the + * server is still reading or writing. Invalidating those MRs before the R= eply + * arrives provokes a remote access error that tears down the QP. Keep a + * cancelled request available for reply-driven MR cleanup while its conne= ction + * remains live. A false result leaves any remaining reclamation to the no= rmal + * release path. + * + * Context: Called with xprt->queue_lock held and @rqst unpinned. + * Must not sleep. + * + * Returns true if @rqst still has registered memory on the current connec= tion. + */ +static bool +xprt_rdma_release_needs_drain(struct rpc_rqst *rqst) +{ + struct rpc_xprt *xprt =3D rqst->rq_xprt; + struct rpcrdma_req *req =3D rpcr_to_rdmar(rqst); + + if (!xprt_connected(xprt) || + rqst->rq_connect_cookie !=3D READ_ONCE(xprt->connect_cookie)) + return false; + + return !list_empty(&req->rl_registered); +} + /** * xprt_rdma_set_connect_timeout - set timeouts for establishing a connect= ion * @xprt: controlling transport instance @@ -655,6 +684,9 @@ xprt_rdma_free(struct rpc_task *task) struct rpc_rqst *rqst =3D task->tk_rqstp; struct rpcrdma_req *req =3D rpcr_to_rdmar(rqst); =20 + /* Graceful draining can be skipped or end before all MRs are reclaimed. + * Complete any remaining cleanup through the normal release path. + */ if (unlikely(!list_empty(&req->rl_registered))) { trace_xprtrdma_mrs_zap(task); frwr_unmap_sync(rpcx_to_rdmax(rqst->rq_xprt), req); @@ -795,6 +827,7 @@ static const struct rpc_xprt_ops xprt_rdma_procs =3D { .release_request =3D xprt_release_rqst_cong, /* ditto */ .wait_for_reply_request =3D xprt_wait_for_reply_request_def, /* ditto */ .timer =3D xprt_rdma_timer, + .release_needs_drain =3D xprt_rdma_release_needs_drain, .rpcbind =3D rpcb_getport_async, /* sunrpc/rpcb_clnt.c */ .set_port =3D xprt_rdma_set_port, .connect =3D xprt_rdma_connect, --=20 2.53.0