The NFSv4 state manager walks clp->cl_superblocks under RCU in nfs4_handle_migration() and nfs4_handle_lease_moved(), then drops the lock and calls nfs4_try_migration() without holding a reference on the nfs_server or its superblock. A concurrent umount can therefore run nfs_kill_super() -> nfs_free_server() while migration recovery is still inside nfs4_update_server(): the server is removed from cl_superblocks a second time (DEBUG_LIST BUG), and once freed by call_rcu() the migration path still writes server->mig_status and touches server->super->s_root. Pin the superblock with nfs_sb_active() before dropping RCU, as nfs_client_for_each_server() already does, and drop it again with nfs_sb_deactive() on every path: umount then detaches the superblock but cannot reach nfs_kill_super()/nfs_free_server() until migration recovery has finished and restored the server's list membership. nfs4_handle_migration() releases the pin after nfs4_try_migration(); nfs4_handle_lease_moved() releases it on both restart paths and before its final return. Umount may now wait for the migration attempt to finish, bounded by the mount's RPC timeouts, the same trade-off as the other nfs_sb_active() users. Link: https://lore.kernel.org/all/20261025180323.349533-1-teel4res@gmail.com/ Fixes: c9fdeb280b8c ("NFS: Add basic migration support to state manager thread") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Cheng Yongkang --- v2: add Link, Fixes and stable tags, and remove local validation details from the commit message. fs/nfs/nfs4state.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index a5dec04..ff866f6 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -2169,8 +2169,12 @@ restart: &server->mig_status)) continue; + if (!server->super || !nfs_sb_active(server->super)) + continue; + rcu_read_unlock(); status = nfs4_try_migration(server, cred); + nfs_sb_deactive(server->super); if (status < 0) { put_cred(cred); return status; @@ -2212,13 +2216,20 @@ restart: continue; server->mig_gen = clp->cl_mig_gen; + if (!server->super || !nfs_sb_active(server->super)) + continue; + rcu_read_unlock(); inode = d_inode(server->super->s_root); status = nfs4_proc_fsid_present(inode, cred); - if (status != -NFS4ERR_MOVED) + if (status != -NFS4ERR_MOVED) { + nfs_sb_deactive(server->super); goto restart; /* wasn't this one */ - if (nfs4_try_migration(server, cred) == -NFS4ERR_LEASE_MOVED) + } + status = nfs4_try_migration(server, cred); + nfs_sb_deactive(server->super); + if (status == -NFS4ERR_LEASE_MOVED) goto restart; /* there are more */ goto out; }