The following patches made over Linus's tree fix the 2 bugs: 1. vhost worker task shows up as a process forked from the parent that did VHOST_SET_OWNER ioctl instead of a process under root/kthreadd. This was causing breaking scripts. 2. vhost_tasks didn't disable or add support for freeze requests. The following patches fix these issues by making the vhost_task task a thread under the process that did the VHOST_SET_OWNER and uses get_signal() to handle freeze and SIGSTOP/KILL signals which is required when using CLONE_THREAD (really CLONE_THREAD requires CLONE_SIGHAND which requires SIGKILL/STOP to be supported).
Mike Christie
2023-May-22 02:51 UTC
[PATCH 1/3] signal: Don't always put SIGKILL in shared_pending
When get_pending detects the task has been marked to be killed we try to clean up the SIGKLL by doing a sigdelset and recalc_sigpending, but we still leave it in shared_pending. If the signal is being short circuit delivered there is no need to put in shared_pending so this adds a check in complete_signal. This patch was modified from Eric Biederman <ebiederm at xmission.com> original patch. Signed-off-by: Mike Christie <michael.christie at oracle.com> --- kernel/signal.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kernel/signal.c b/kernel/signal.c index 8f6330f0e9ca..3dc99b9aec7f 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1052,6 +1052,14 @@ static void complete_signal(int sig, struct task_struct *p, enum pid_type type) signal->flags = SIGNAL_GROUP_EXIT; signal->group_exit_code = sig; signal->group_stop_count = 0; + + /* + * The signal is being short circuit delivered so + * don't set pending. + */ + if (type != PIDTYPE_PID) + sigdelset(&signal->shared_pending.signal, sig); + t = p; do { task_clear_jobctl_pending(t, JOBCTL_PENDING_MASK); -- 2.25.1
Mike Christie
2023-May-22 02:51 UTC
[PATCH 2/3] signal: Don't exit for PF_USER_WORKER tasks
vhost_tasks also need to perform cleanup before exiting so this changes the check in get_signal to be more generic so both io thread and vhost tasks can do their cleanup. Signed-off-by: Mike Christie <michael.christie at oracle.com> --- kernel/signal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/signal.c b/kernel/signal.c index 3dc99b9aec7f..8050fe23c732 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2869,11 +2869,11 @@ bool get_signal(struct ksignal *ksig) } /* - * PF_IO_WORKER threads will catch and exit on fatal signals + * PF_USER_WORKER threads will catch and exit on fatal signals * themselves. They have cleanup that must be performed, so * we cannot call do_exit() on their behalf. */ - if (current->flags & PF_IO_WORKER) + if (current->flags & PF_USER_WORKER) goto out; /* -- 2.25.1
Mike Christie
2023-May-22 02:51 UTC
[PATCH 3/3] fork, vhost: Use CLONE_THREAD to fix freezer/ps regression
When switching from kthreads to vhost_tasks two bugs were added: 1. The vhost worker tasks's now show up as processes so scripts doing ps or ps a would not incorrectly detect the vhost task as another process. 2. kthreads disabled freeze by setting PF_NOFREEZE, but vhost tasks's didn't disable or add support for them. To fix both bugs, this switches the vhost task to be thread in the process that does the VHOST_SET_OWNER ioctl, and has vhost_worker call get_signal to support SIGKILL/SIGSTOP and freeze signals. Note that SIGKILL/STOP support is required because CLONE_THREAD requires CLONE_SIGHAND which requires those 2 signals to be suppported. This a modified version of patch originally written by Linus which handles his review comment to himself to rename ignore_signals to block_signals to better represent what it now does. And it includes a change to vhost_worker() to support SIGSTOP/KILL and freeze, and it drops the wait use per Oleg's review comment that it's no longer needed when using CLONE_THREAD. Fixes: 6e890c5d5021 ("vhost: use vhost_tasks for worker threads") Signed-off-by: Mike Christie <michael.christie at oracle.com> --- drivers/vhost/vhost.c | 17 ++++++++++++++++- include/linux/sched/task.h | 2 +- kernel/fork.c | 12 +++--------- kernel/signal.c | 1 + kernel/vhost_task.c | 16 ++++------------ 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index a92af08e7864..bf83e9340e40 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -338,6 +338,7 @@ static int vhost_worker(void *data) struct vhost_worker *worker = data; struct vhost_work *work, *work_next; struct llist_node *node; + bool dead = false; for (;;) { /* mb paired w/ kthread_stop */ @@ -349,8 +350,22 @@ static int vhost_worker(void *data) } node = llist_del_all(&worker->work_list); - if (!node) + if (!node) { schedule(); + /* + * When we get a SIGKILL our release function will + * be called. That will stop new IOs from being queued + * and check for outstanding cmd responses. It will then + * call vhost_task_stop to tell us to return and exit. + */ + if (!dead && signal_pending(current)) { + struct ksignal ksig; + + dead = get_signal(&ksig); + if (dead) + clear_thread_flag(TIF_SIGPENDING); + } + } node = llist_reverse_order(node); /* make sure flag is seen after deletion */ diff --git a/include/linux/sched/task.h b/include/linux/sched/task.h index 537cbf9a2ade..249a5ece9def 100644 --- a/include/linux/sched/task.h +++ b/include/linux/sched/task.h @@ -29,7 +29,7 @@ struct kernel_clone_args { u32 io_thread:1; u32 user_worker:1; u32 no_files:1; - u32 ignore_signals:1; + u32 block_signals:1; unsigned long stack; unsigned long stack_size; unsigned long tls; diff --git a/kernel/fork.c b/kernel/fork.c index ed4e01daccaa..9e04ab5c3946 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2338,14 +2338,10 @@ __latent_entropy struct task_struct *copy_process( p->flags |= PF_KTHREAD; if (args->user_worker) p->flags |= PF_USER_WORKER; - if (args->io_thread) { - /* - * Mark us an IO worker, and block any signal that isn't - * fatal or STOP - */ + if (args->io_thread) p->flags |= PF_IO_WORKER; + if (args->block_signals) siginitsetinv(&p->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP)); - } if (args->name) strscpy_pad(p->comm, args->name, sizeof(p->comm)); @@ -2517,9 +2513,6 @@ __latent_entropy struct task_struct *copy_process( if (retval) goto bad_fork_cleanup_io; - if (args->ignore_signals) - ignore_signals(p); - stackleak_task_init(p); if (pid != &init_struct_pid) { @@ -2861,6 +2854,7 @@ struct task_struct *create_io_thread(int (*fn)(void *), void *arg, int node) .fn_arg = arg, .io_thread = 1, .user_worker = 1, + .block_signals = 1, }; return copy_process(NULL, 0, node, &args); diff --git a/kernel/signal.c b/kernel/signal.c index 8050fe23c732..a0f00a078cbb 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2891,6 +2891,7 @@ bool get_signal(struct ksignal *ksig) return ksig->sig > 0; } +EXPORT_SYMBOL_GPL(get_signal); /** * signal_delivered - called after signal delivery to update blocked signals diff --git a/kernel/vhost_task.c b/kernel/vhost_task.c index b7cbd66f889e..7a2d7d9fe772 100644 --- a/kernel/vhost_task.c +++ b/kernel/vhost_task.c @@ -31,22 +31,13 @@ static int vhost_task_fn(void *data) */ void vhost_task_stop(struct vhost_task *vtsk) { - pid_t pid = vtsk->task->pid; - set_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags); wake_up_process(vtsk->task); /* * Make sure vhost_task_fn is no longer accessing the vhost_task before - * freeing it below. If userspace crashed or exited without closing, - * then the vhost_task->task could already be marked dead so - * kernel_wait will return early. + * freeing it below. */ wait_for_completion(&vtsk->exited); - /* - * If we are just closing/removing a device and the parent process is - * not exiting then reap the task. - */ - kernel_wait4(pid, NULL, __WCLONE, NULL); kfree(vtsk); } EXPORT_SYMBOL_GPL(vhost_task_stop); @@ -75,13 +66,14 @@ struct vhost_task *vhost_task_create(int (*fn)(void *), void *arg, const char *name) { struct kernel_clone_args args = { - .flags = CLONE_FS | CLONE_UNTRACED | CLONE_VM, + .flags = CLONE_FS | CLONE_UNTRACED | CLONE_VM | + CLONE_THREAD | CLONE_SIGHAND, .exit_signal = 0, .fn = vhost_task_fn, .name = name, .user_worker = 1, .no_files = 1, - .ignore_signals = 1, + .block_signals = 1, }; struct vhost_task *vtsk; struct task_struct *tsk; -- 2.25.1