summaryrefslogtreecommitdiffstats
path: root/kernel/vhost_task.c
diff options
context:
space:
mode:
authorMike Christie <michael.christie@oracle.com>2023-06-07 14:23:38 -0500
committerMichael S. Tsirkin <mst@redhat.com>2023-06-08 15:43:09 -0400
commit4b13cbef797048fbb525f8c635a5279e9d209d93 (patch)
tree59de4d4c9dc49411fe09d36c1390f58f08e7ad36 /kernel/vhost_task.c
parenta284f09effea1908db7985dbfb5458c9100038d8 (diff)
downloadlinux-4b13cbef797048fbb525f8c635a5279e9d209d93.tar.gz
linux-4b13cbef797048fbb525f8c635a5279e9d209d93.tar.bz2
linux-4b13cbef797048fbb525f8c635a5279e9d209d93.zip
vhost: Fix worker hangs due to missed wake up calls
We can race where we have added work to the work_list, but vhost_task_fn has passed that check but not yet set us into TASK_INTERRUPTIBLE. wake_up_process will see us in TASK_RUNNING and just return. This bug was intoduced in commit f9010dbdce91 ("fork, vhost: Use CLONE_THREAD to fix freezer/ps regression") when I moved the setting of TASK_INTERRUPTIBLE to simplfy the code and avoid get_signal from logging warnings about being in the wrong state. This moves the setting of TASK_INTERRUPTIBLE back to before we test if we need to stop the task to avoid a possible race there as well. We then have vhost_worker set TASK_RUNNING if it finds work similar to before. Fixes: f9010dbdce91 ("fork, vhost: Use CLONE_THREAD to fix freezer/ps regression") Signed-off-by: Mike Christie <michael.christie@oracle.com> Message-Id: <20230607192338.6041-3-michael.christie@oracle.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Diffstat (limited to 'kernel/vhost_task.c')
-rw-r--r--kernel/vhost_task.c16
1 files changed, 9 insertions, 7 deletions
diff --git a/kernel/vhost_task.c b/kernel/vhost_task.c
index f80d5c51ae67..da35e5b7f047 100644
--- a/kernel/vhost_task.c
+++ b/kernel/vhost_task.c
@@ -28,10 +28,6 @@ static int vhost_task_fn(void *data)
for (;;) {
bool did_work;
- /* mb paired w/ vhost_task_stop */
- if (test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags))
- break;
-
if (!dead && signal_pending(current)) {
struct ksignal ksig;
/*
@@ -48,11 +44,17 @@ static int vhost_task_fn(void *data)
clear_thread_flag(TIF_SIGPENDING);
}
+ /* mb paired w/ vhost_task_stop */
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags)) {
+ __set_current_state(TASK_RUNNING);
+ break;
+ }
+
did_work = vtsk->fn(vtsk->data);
- if (!did_work) {
- set_current_state(TASK_INTERRUPTIBLE);
+ if (!did_work)
schedule();
- }
}
complete(&vtsk->exited);