Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Mike Christie and committed by
Michael S. Tsirkin
4b13cbef a284f09e

+12 -8
+2
drivers/vhost/vhost.c
··· 341 341 342 342 node = llist_del_all(&worker->work_list); 343 343 if (node) { 344 + __set_current_state(TASK_RUNNING); 345 + 344 346 node = llist_reverse_order(node); 345 347 /* make sure flag is seen after deletion */ 346 348 smp_wmb();
+10 -8
kernel/vhost_task.c
··· 28 28 for (;;) { 29 29 bool did_work; 30 30 31 - /* mb paired w/ vhost_task_stop */ 32 - if (test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags)) 33 - break; 34 - 35 31 if (!dead && signal_pending(current)) { 36 32 struct ksignal ksig; 37 33 /* ··· 44 48 clear_thread_flag(TIF_SIGPENDING); 45 49 } 46 50 47 - did_work = vtsk->fn(vtsk->data); 48 - if (!did_work) { 49 - set_current_state(TASK_INTERRUPTIBLE); 50 - schedule(); 51 + /* mb paired w/ vhost_task_stop */ 52 + set_current_state(TASK_INTERRUPTIBLE); 53 + 54 + if (test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags)) { 55 + __set_current_state(TASK_RUNNING); 56 + break; 51 57 } 58 + 59 + did_work = vtsk->fn(vtsk->data); 60 + if (!did_work) 61 + schedule(); 52 62 } 53 63 54 64 complete(&vtsk->exited);