[Devel] [PATCH VZ10 v2] fs/fuse kio: fix hung connection on vstorage-mount exit

Liu Kui kui.liu at virtuozzo.com
Fri Apr 24 08:44:15 MSK 2026


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>
---
 fs/fuse/kio/pcs/pcs_krpc.c | 93 ++++++++++++++++++++++++++++++--------
 fs/fuse/kio/pcs/pcs_krpc.h |  4 +-
 2 files changed, 77 insertions(+), 20 deletions(-)

diff --git a/fs/fuse/kio/pcs/pcs_krpc.c b/fs/fuse/kio/pcs/pcs_krpc.c
index 374cbce9c3c4..8c87feb78a34 100644
--- a/fs/fuse/kio/pcs/pcs_krpc.c
+++ b/fs/fuse/kio/pcs/pcs_krpc.c
@@ -650,12 +650,49 @@ 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_interruptible(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;
 	struct krpc_req *kreq, *kreq_next;
 
+	/* a rare case when creation of the watcher kthread failed */
+	if (!krpcs->mm)
+		return 0;
+
 	kthread_use_mm(krpcs->mm);
 
 	for (;;) {
@@ -683,29 +720,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;
+
+	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->krpc_task = get_task_struct(tsk);
+	krpcs->watcher_task = get_task_struct(watcher);
+	krpcs->watched_pid = watched_pid;
+	krpcs->sender_task = get_task_struct(sender);
 	krpcs->mm = mm;
 	atomic_inc(&current->files->count);
-	tsk->files = current->files;
+	sender->files = current->files;
 
-	return tsk;
+	wake_up_process(watcher);
+
+	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 +767,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 +1246,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 +1276,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 6a090ef66185..427c598f01f7 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.39.5 (Apple Git-154)



More information about the Devel mailing list