[Devel] [PATCH vz10 v3] fs/fuse kio: fix hung connection on vstorage-mount exit
Konstantin Khorenko
khorenko at virtuozzo.com
Fri May 15 17:02:20 MSK 2026
From: Liu Kui <kui.liu at virtuozzo.com>
krpc_sender kthread holds a reference to vstorage-mount's file_struct,
preventing /dev/fuse from closing after the process exits. This leaves
the fuse connection without proper teardown and prevents systemd from
restarting the vstorage-fs service.
Fix this by adding a krpc_watcher kthread that monitors the userspace
vstorage-mount process via its pidfd wait queue and stops the krpc_sender
kthread when that process exits, triggering a clean fuse connection
teardown and allowing systemd to restart the vstorage-fs service.
Related to
https://virtuozzo.atlassian.net/browse/VSTOR-128451
Signed-off-by: Liu Kui <kui.liu at virtuozzo.com>
Acked-by: Alexey Kuznetsov <kuznet at virtuozzo.com>
Feature: fuse: kRPC - single RPC for kernel and userspace
---
Changes v2 -> v3:
- Use wait_event() instead of wait_event_interruptible() in
krpc_watcher_fn(). A kthread has no reason to handle signals here,
and wait_event avoids a spurious wakeup that would tear down the
sender unconditionally.
- Remove the dead "!krpcs->mm" early-return check in krpc_sender_fn().
When kthread_create(watcher) fails, kthread_stop(sender) is called
on a not-yet-woken kthread; the kthread infrastructure sees
KTHREAD_SHOULD_STOP and skips calling the thread function entirely,
so krpc_sender_fn() is never invoked in that scenario.
---
fs/fuse/kio/pcs/pcs_krpc.c | 89 ++++++++++++++++++++++++++++++--------
fs/fuse/kio/pcs/pcs_krpc.h | 4 +-
2 files changed, 73 insertions(+), 20 deletions(-)
diff --git a/fs/fuse/kio/pcs/pcs_krpc.c b/fs/fuse/kio/pcs/pcs_krpc.c
index 374cbce9c3c4a..7d5a9619276df 100644
--- a/fs/fuse/kio/pcs/pcs_krpc.c
+++ b/fs/fuse/kio/pcs/pcs_krpc.c
@@ -650,7 +650,40 @@ static void kreq_submit(struct krpc_req *kreq)
}
}
-static int krpc_threadfn(void *data)
+static bool process_has_exited(struct pid *pid)
+{
+ struct task_struct *t;
+ bool exited = false;
+
+ rcu_read_lock();
+ t = pid_task(pid, PIDTYPE_TGID);
+
+ if (!t || READ_ONCE(t->exit_state) != 0)
+ exited = true;
+ rcu_read_unlock();
+
+ return exited;
+}
+
+static int krpc_watcher_fn(void *data)
+{
+ struct pcs_krpc_set *krpcs = data;
+
+ wait_event(krpcs->watched_pid->wait_pidfd,
+ process_has_exited(krpcs->watched_pid) || kthread_should_stop());
+
+ kthread_stop(krpcs->sender_task);
+ put_task_struct(krpcs->sender_task);
+ krpcs->sender_task = NULL;
+ mmput(krpcs->mm);
+ krpcs->mm = NULL;
+ put_pid(krpcs->watched_pid);
+ krpcs->watched_pid = NULL;
+
+ return 0;
+}
+
+static int krpc_sender_fn(void *data)
{
struct pcs_krpc_set *krpcs = data;
struct llist_node *ll;
@@ -683,29 +716,45 @@ static int krpc_threadfn(void *data)
return 0;
}
-static struct task_struct *pcs_krpc_task_create(struct pcs_krpc_set *krpcs)
+static struct task_struct *krpc_task_create(struct pcs_krpc_set *krpcs)
{
- struct task_struct *tsk;
+ struct task_struct *sender, *watcher;
struct mm_struct *mm;
+ struct pid *watched_pid;
- WARN_ON_ONCE(krpcs->krpc_task);
+ WARN_ON_ONCE(krpcs->sender_task);
mm = get_task_mm(current);
if (!mm)
return NULL;
- tsk = kthread_create(krpc_threadfn, krpcs, "krpc_send");
- if (!tsk || IS_ERR(tsk)) {
- mmput(mm);
- return NULL;
- }
+ sender = kthread_create(krpc_sender_fn, krpcs, "krpc_sender");
+ if (IS_ERR(sender))
+ goto err_sender;
- krpcs->krpc_task = get_task_struct(tsk);
+ watched_pid = get_task_pid(current, PIDTYPE_TGID);
+
+ watcher = kthread_create(krpc_watcher_fn, krpcs, "krpc_watcher");
+ if (IS_ERR(watcher))
+ goto err_watcher;
+
+ krpcs->watcher_task = get_task_struct(watcher);
+ krpcs->watched_pid = watched_pid;
+ krpcs->sender_task = get_task_struct(sender);
krpcs->mm = mm;
atomic_inc(¤t->files->count);
- tsk->files = current->files;
+ sender->files = current->files;
+
+ wake_up_process(watcher);
- return tsk;
+ return sender;
+
+err_watcher:
+ put_pid(watched_pid);
+ kthread_stop(sender);
+err_sender:
+ mmput(mm);
+ return NULL;
}
static int pcs_krpc_ioctl_send_msg(struct krpc_req *kreq)
@@ -714,9 +763,9 @@ static int pcs_krpc_ioctl_send_msg(struct krpc_req *kreq)
struct pcs_krpc_set *krpcs = kreq->krpc->krpcs;
if (pcs_krpc_use_thread) {
- tsk = krpcs->krpc_task;
+ tsk = krpcs->sender_task;
if (unlikely(!tsk))
- tsk = pcs_krpc_task_create(krpcs);
+ tsk = krpc_task_create(krpcs);
if (likely(tsk)) {
llist_add(&kreq->llist_link, &krpcs->req_llist);
@@ -1193,8 +1242,10 @@ void pcs_krpcset_init(struct pcs_krpc_set *krpcs)
INIT_LIST_HEAD(&krpcs->list);
krpcs->nkrpc = 0;
- krpcs->krpc_task = NULL;
+ krpcs->sender_task = NULL;
krpcs->mm = NULL;
+ krpcs->watcher_task = NULL;
+ krpcs->watched_pid = NULL;
init_llist_head(&krpcs->req_llist);
spin_lock_init(&krpcs->lock);
}
@@ -1221,10 +1272,10 @@ void pcs_krpcset_fini(struct pcs_krpc_set *krpcs)
}
spin_unlock(&krpcs->lock);
- if (krpcs->krpc_task) {
- kthread_stop(krpcs->krpc_task);
- put_task_struct(krpcs->krpc_task);
- mmput(krpcs->mm);
+ if (krpcs->watcher_task) {
+ kthread_stop(krpcs->watcher_task);
+ put_task_struct(krpcs->watcher_task);
+ krpcs->watcher_task = NULL;
}
BUG_ON(!list_empty(&krpcs->list));
BUG_ON(krpcs->nkrpc != 0);
diff --git a/fs/fuse/kio/pcs/pcs_krpc.h b/fs/fuse/kio/pcs/pcs_krpc.h
index 6a090ef66185b..427c598f01f7e 100644
--- a/fs/fuse/kio/pcs/pcs_krpc.h
+++ b/fs/fuse/kio/pcs/pcs_krpc.h
@@ -37,8 +37,10 @@ struct pcs_krpc_set {
unsigned int nkrpc;
spinlock_t lock;
- struct task_struct *krpc_task;
+ struct task_struct *sender_task;
struct mm_struct *mm;
+ struct task_struct *watcher_task;
+ struct pid *watched_pid;
struct llist_head req_llist;
};
--
2.43.0
More information about the Devel
mailing list