From nobody Sun May 24 17:48:31 2026 Received: from mail-qk1-f176.google.com (mail-qk1-f176.google.com [209.85.222.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D969339732C for ; Sun, 24 May 2026 13:07:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779628033; cv=none; b=FyyM9mwLE8M7m2oEgQ7AkCg07DuYuIbFZ7yOkFX8o2YxolRxslOxjkOORcJkLLTurr/4kibtZ3eouj2KG9wsQDtzE1Kbq+MAJ++OnqyZxIYpBkf7uhn0FPqF8onOx1vTTvkxhcvyu0SXHqaDtDxBnijPUBlT7q2CNyLj9bx70Zw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779628033; c=relaxed/simple; bh=whGDVgr9DaXIVCL7I9QLzspFILVLDI6f7xiyqYli2N8=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=tT1BAjWbG7auJ4NySOZdJj96Kxq0/RkLNSKjOLqeZ44T95RX9gqIRo/sgO4SbKNj8SpcXo00AySyZke4zLOofRzO0SWqQF1GfRQ7Ary0OriSP5FAbOJ3c69kXULg9lBby5TzasFVrHzB1O6rRkeCLudD8wG3WClDhk0OI0xT/6g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=cFT1mcaB; arc=none smtp.client-ip=209.85.222.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="cFT1mcaB" Received: by mail-qk1-f176.google.com with SMTP id af79cd13be357-90caad2e944so914684885a.2 for ; Sun, 24 May 2026 06:07:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779628031; x=1780232831; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=wDUj+XtKVNzBDF85yQS5JKEq71LY2fdGuiqbA9Dq4O8=; b=cFT1mcaBOPEIM862F7eTf21DbE0DPTiI8kP55Vh6Ac7oM6LFlsp0/d3SE48w8FW6TX UMPwC62yy6xQXXAIGzQWxoJmQbXE9TrE/Wueo16J7V3CTcwS+aqdg3tqecjtTT97pK3n f75KLUxRdKkVShXaNYqhVmT8WgUK9txSD/Piw5KUM+k1SiKvnJucdKq/VDWam8E+Whjo Rhbtexal+1srMsQ1TdTHPVEeozAKqgU/hSCaB1Fbm0Nil1TnsGWRcdPakVyd4MIUPcYJ tasNC1aH256jAu5uLQwHiKv2eobsAr4SdxqXy/EK9Oh23Dpb4jSJISk3bTnMfl0l00zm jwMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779628031; x=1780232831; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=wDUj+XtKVNzBDF85yQS5JKEq71LY2fdGuiqbA9Dq4O8=; b=Vk3oRG6ciKhlA6NlvEMDmG7w0z9rez5YvzIF8dywIpUMwQdileeD09yQJoRLRqAd/c utu+e8j1u0k/NWTlSYlCCSnXE8qzVpqHQkCj6CrVB0SS4SC62tE5E7wZG4LIr57ZRo3d nzf2ptk9pedKHP8+Eu3rNNrmYxVwfY9gkVhpqL5lQwWC8CtaGLWEmXSu/biihDEk+iw/ 0ItIpDC3W0fNubYKOm2FdOowNoJST0CVy2FM2coWsDNbkWp4ykqxPe+lWLiPjEmuEcnx 2RwpdRV+AoXUqyxmMx0py9dExKFd7CpovM8YO6SdSm6xG0m/HKezsMi/YPOUoQiYDc8n dPaw== X-Forwarded-Encrypted: i=1; AFNElJ/hkHEi7FCUF4Bd3DIDjPcZPwHQF7tV3DvmPIJ62TQAmQqI2dJYt+Rj6tP9W9n0URzVfECwFb9GdPIfHSg=@vger.kernel.org X-Gm-Message-State: AOJu0YzjJALDA+sbsXpa+1+z06Q83A/EVGDbWCP4GJXK5sEUggzFeSy7 G+5ypir7EeDIxwG6QNF4+bIqKtn1Ws6FXJPt7NB3iVJaMJncLZg8Sbeh X-Gm-Gg: Acq92OGfZQb1bSNJ5g0qAebz+iccer51TqTYfiKbSgFL4BiGSm26eDqGmWo5F27YHLI /O1vda1MS6CVh0XMtKspQcJv0mL/3V8u3s7u9nLRmohpiOea5ezhjNAaxSVThNlWWhKqINieMUl AAZNeB5qpbjzw9Cdcw2ne7P8ZAUvs/ycZG2PwdD9/xvR1+w+AXiWW+JGsmTtzAmlOMA/75FYwjB dF/IJMgzYosAQPd+95kDrfMh9oVtqip/X+uShwaJP3kjefrFfRLpFOdc9wJe2T8pWsdScTJR89t 5UGVQRnwOdmipIredE6PF4HJapkO1hjJ8gy76FLs1Jvl6Tsegw6thKfdJdjjVgljBhNj5fcT4Dp fz+eaZhky4lTIHXzUwJZUkmsO2YYMQxEXlLyzHIMq5SoQTbNgZxJmrUJynrYkH4t9vQ+taYm9Y4 Jk2OIOJUxx4cAotsIWH4SzzDOkFxle1/hllVS05gwG8MnFoM0AWay8xbp3YS6LVwNn8oeJKyap5 /cdXrQua0yZ3FXuUL6P X-Received: by 2002:a05:620a:6504:b0:8cf:c272:9721 with SMTP id af79cd13be357-914b48b4d38mr1503642085a.6.1779628030696; Sun, 24 May 2026 06:07:10 -0700 (PDT) Received: from server0 (c-68-48-65-54.hsd1.mi.comcast.net. [68.48.65.54]) by smtp.gmail.com with ESMTPSA id af79cd13be357-914bb905677sm817575885a.18.2026.05.24.06.07.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 24 May 2026 06:07:09 -0700 (PDT) From: Michael Bommarito To: Chuck Lever , Jeff Layton Cc: NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, stable@vger.kernel.org Subject: [PATCH v2] NFSD: restart ssc_expire_umount walk after dropping nfsd_ssc_lock Date: Sun, 24 May 2026 09:06:54 -0400 Message-ID: <20260524130654.1924556-1-michael.bommarito@gmail.com> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" nfsd4_ssc_expire_umount() walks nn->nfsd_ssc_mount_list with list_for_each_entry_safe(ni, tmp, ...). For each expired entry it sets nsui_busy =3D true, drops nfsd_ssc_lock to run mntput() on the source vfsmount, then reacquires the lock to list_del + kfree the entry and continue iterating via the macro's saved tmp pointer. The nsui_busy flag protects the current ni from concurrent nfsd4_ssc_setup_dul() finders during the lock-drop window, but it does not pin tmp. Another nfsd RPC thread that fails its source- server mount and reaches nfsd4_ssc_cancel_dul() will, during that same window, take nfsd_ssc_lock, list_del + kfree its own ssc_umount item, and release the lock. If that item is the saved tmp of the expire walk, the next iteration dereferences a freed nfsd4_ssc_umount_item. Restart the walk from the head after the mntput() unlock window so no saved next pointer survives the lock-drop. The list is bounded by the number of active inter-server source mounts (typically small) and the expire delayed-work runs periodically rather than per-IO, so the restart is cheap. Cc: stable@vger.kernel.org Fixes: f4e44b393389 ("NFSD: delay unmount source's export after inter-serve= r copy completed.") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito --- fs/nfsd/nfs4state.c | 44 +++++++++++++++++++++++++------------------- Changes since v1: - Rewrote the in-code comment above goto-restart to describe the current code (concurrent cancel_dul can free any list item while the lock is dropped) rather than referencing the removed list_for_each_entry_safe() iterator. (Chuck Lever.) 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 6b9c399b89dfb..33f72fcf65458 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -6728,30 +6728,36 @@ static void nfsd4_ssc_shutdown_umount(struct nfsd_n= et *nn) static void nfsd4_ssc_expire_umount(struct nfsd_net *nn) { bool do_wakeup =3D false; - struct nfsd4_ssc_umount_item *ni =3D NULL; - struct nfsd4_ssc_umount_item *tmp; + struct nfsd4_ssc_umount_item *ni; =20 +restart: spin_lock(&nn->nfsd_ssc_lock); - list_for_each_entry_safe(ni, tmp, &nn->nfsd_ssc_mount_list, nsui_list) { - if (time_after(jiffies, ni->nsui_expire)) { - if (refcount_read(&ni->nsui_refcnt) > 1) - continue; + list_for_each_entry(ni, &nn->nfsd_ssc_mount_list, nsui_list) { + if (!time_after(jiffies, ni->nsui_expire)) + break; + if (refcount_read(&ni->nsui_refcnt) > 1) + continue; =20 - /* mark being unmount */ - ni->nsui_busy =3D true; - spin_unlock(&nn->nfsd_ssc_lock); - mntput(ni->nsui_vfsmount); - spin_lock(&nn->nfsd_ssc_lock); + /* mark being unmount */ + ni->nsui_busy =3D true; + spin_unlock(&nn->nfsd_ssc_lock); + mntput(ni->nsui_vfsmount); + spin_lock(&nn->nfsd_ssc_lock); =20 - /* waiters need to start from begin of list */ - list_del(&ni->nsui_list); - kfree(ni); + /* waiters need to start from begin of list */ + list_del(&ni->nsui_list); + kfree(ni); =20 - /* wakeup ssc_connect waiters */ - do_wakeup =3D true; - continue; - } - break; + /* wakeup ssc_connect waiters */ + do_wakeup =3D true; + /* + * Concurrent nfsd4_ssc_cancel_dul() can free any item + * on the list under nfsd_ssc_lock while mntput() runs + * above. Restart from the head; the list is short and + * the expire worker is periodic, so this is cheap. + */ + spin_unlock(&nn->nfsd_ssc_lock); + goto restart; } if (do_wakeup) wake_up_all(&nn->nfsd_ssc_waitq); --=20 2.53.0