[PATCH RFC] pnfs/blocklayout: fix lost wakeup in bl_resolve_deviceid()
FAN YE <[email protected]>
| Newsgroups | org.kernel.feeds.b4-sent,org.kernel.vger.linux-kernel,org.kernel.vger.linux-nfs |
|---|---|
| Message-ID | <[email protected]> |
bl_resolve_deviceid() queues itself on nn->bl_wq and calls
rpc_queue_upcall(), but sets TASK_UNINTERRUPTIBLE only after that call
returns. If blkmapd answers on another CPU in between, the wake_up()
from bl_pipe_downcall() finds the task runnable and the assignment that
follows overwrites it, so schedule() never returns. The caller holds
nn->bl_mutex, so every later blocklayout device resolution blocks behind
it.
Set the state before queueing the upcall and restore TASK_RUNNING on the
error path. Of the four rpc_queue_upcall() callers this is the only
open-coded waiter; __cld_pipe_upcall() uses wait_for_completion(), which
cannot lose a wakeup this way.
Fixes: fe0a9b740881 ("pnfsblock: add device operations")
Assisted-by: Claude:claude-opus-5
Signed-off-by: FAN YE <[email protected]>
---
Not seen on a real pNFS mount. In a VM I reached bl_resolve_deviceid()
through a debugfs hook with a fake blkmapd and an mdelay() widening the
window: the caller sticks in D state at schedule(), and the next caller
then blocks on nn->bl_mutex. Both go away with this patch.
Two questions:
- Would you rather have bl_wq converted to a completion, like
__cld_pipe_upcall() in fs/nfsd/nfs4recover.c?
- rpc_queue_upcall() calls dput(). If the pipe were unlinked meanwhile,
that could sleep with the task already in TASK_UNINTERRUPTIBLE. Is that
acceptable here?
---
fs/nfs/blocklayout/rpc_pipefs.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/nfs/blocklayout/rpc_pipefs.c b/fs/nfs/blocklayout/rpc_pipefs.c
index d526f5ba7887..15972ca0daf1 100644
--- a/fs/nfs/blocklayout/rpc_pipefs.c
+++ b/fs/nfs/blocklayout/rpc_pipefs.c
@@ -84,14 +84,16 @@ bl_resolve_deviceid(struct nfs_server *server, struct pnfs_block_volume *b,
dprintk("%s CALLING USERSPACE DAEMON\n", __func__);
add_wait_queue(&nn->bl_wq, &wq);
+ set_current_state(TASK_UNINTERRUPTIBLE);
rc = rpc_queue_upcall(nn->bl_device_pipe, msg);
if (rc < 0) {
+ __set_current_state(TASK_RUNNING);
remove_wait_queue(&nn->bl_wq, &wq);
goto out_free_data;
}
- set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
+ __set_current_state(TASK_RUNNING);
remove_wait_queue(&nn->bl_wq, &wq);
if (reply->status != BL_DEVICE_REQUEST_PROC) {
---
base-commit: 818bebeb63dd6bf5f4e07e145f6cdbace520a34c
change-id: 20260821-rpc-pipefs-blwake-b57669bfd26c
Best regards,
--
FAN YE <[email protected]>