Blame SOURCES/kvm-iothread-fix-iothread_stop-race-condition.patch

4a2fec
From 9750c7d9d35ac49262848a0996667f8e6c782dc8 Mon Sep 17 00:00:00 2001
4a2fec
From: Stefan Hajnoczi <stefanha@redhat.com>
4a2fec
Date: Fri, 22 Dec 2017 11:08:59 +0100
4a2fec
Subject: [PATCH 41/42] iothread: fix iothread_stop() race condition
4a2fec
4a2fec
RH-Author: Stefan Hajnoczi <stefanha@redhat.com>
4a2fec
Message-id: <20171222110900.24813-20-stefanha@redhat.com>
4a2fec
Patchwork-id: 78501
4a2fec
O-Subject: [RHV7.5 qemu-kvm-rhev PATCH 19/20] iothread: fix iothread_stop() race condition
4a2fec
Bugzilla: 1519721
4a2fec
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
4a2fec
RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
4a2fec
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
4a2fec
4a2fec
There is a small chance that iothread_stop() hangs as follows:
4a2fec
4a2fec
  Thread 3 (Thread 0x7f63eba5f700 (LWP 16105)):
4a2fec
  #0  0x00007f64012c09b6 in ppoll () at /lib64/libc.so.6
4a2fec
  #1  0x000055959992eac9 in ppoll (__ss=0x0, __timeout=0x0, __nfds=<optimized out>, __fds=<optimized out>) at /usr/include/bits/poll2.h:77
4a2fec
  #2  0x000055959992eac9 in qemu_poll_ns (fds=<optimized out>, nfds=<optimized out>, timeout=<optimized out>) at util/qemu-timer.c:322
4a2fec
  #3  0x0000559599930711 in aio_poll (ctx=0x55959bdb83c0, blocking=blocking@entry=true) at util/aio-posix.c:629
4a2fec
  #4  0x00005595996806fe in iothread_run (opaque=0x55959bd78400) at iothread.c:59
4a2fec
  #5  0x00007f640159f609 in start_thread () at /lib64/libpthread.so.0
4a2fec
  #6  0x00007f64012cce6f in clone () at /lib64/libc.so.6
4a2fec
4a2fec
  Thread 1 (Thread 0x7f640b45b280 (LWP 16103)):
4a2fec
  #0  0x00007f64015a0b6d in pthread_join () at /lib64/libpthread.so.0
4a2fec
  #1  0x00005595999332ef in qemu_thread_join (thread=<optimized out>) at util/qemu-thread-posix.c:547
4a2fec
  #2  0x00005595996808ae in iothread_stop (iothread=<optimized out>) at iothread.c:91
4a2fec
  #3  0x000055959968094d in iothread_stop_iter (object=<optimized out>, opaque=<optimized out>) at iothread.c:102
4a2fec
  #4  0x0000559599857d97 in do_object_child_foreach (obj=obj@entry=0x55959bdb8100, fn=fn@entry=0x559599680930 <iothread_stop_iter>, opaque=opaque@entry=0x0, recurse=recurse@entry=false) at qom/object.c:852
4a2fec
  #5  0x0000559599859477 in object_child_foreach (obj=obj@entry=0x55959bdb8100, fn=fn@entry=0x559599680930 <iothread_stop_iter>, opaque=opaque@entry=0x0) at qom/object.c:867
4a2fec
  #6  0x0000559599680a6e in iothread_stop_all () at iothread.c:341
4a2fec
  #7  0x000055959955b1d5 in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at vl.c:4913
4a2fec
4a2fec
The relevant code from iothread_run() is:
4a2fec
4a2fec
  while (!atomic_read(&iothread->stopping)) {
4a2fec
      aio_poll(iothread->ctx, true);
4a2fec
4a2fec
and iothread_stop():
4a2fec
4a2fec
  iothread->stopping = true;
4a2fec
  aio_notify(iothread->ctx);
4a2fec
  ...
4a2fec
  qemu_thread_join(&iothread->thread);
4a2fec
4a2fec
The following scenario can occur:
4a2fec
4a2fec
1. IOThread:
4a2fec
  while (!atomic_read(&iothread->stopping)) -> stopping=false
4a2fec
4a2fec
2. Main loop:
4a2fec
  iothread->stopping = true;
4a2fec
  aio_notify(iothread->ctx);
4a2fec
4a2fec
3. IOThread:
4a2fec
  aio_poll(iothread->ctx, true); -> hang
4a2fec
4a2fec
The bug is explained by the AioContext->notify_me doc comments:
4a2fec
4a2fec
  "If this field is 0, everything (file descriptors, bottom halves,
4a2fec
  timers) will be re-evaluated before the next blocking poll(), thus the
4a2fec
  event_notifier_set call can be skipped."
4a2fec
4a2fec
The problem is that "everything" does not include checking
4a2fec
iothread->stopping.  This means iothread_run() will block in aio_poll()
4a2fec
if aio_notify() was called just before aio_poll().
4a2fec
4a2fec
This patch fixes the hang by replacing aio_notify() with
4a2fec
aio_bh_schedule_oneshot().  This makes aio_poll() or g_main_loop_run()
4a2fec
to return.
4a2fec
4a2fec
Implementing this properly required a new bool running flag.  The new
4a2fec
flag prevents races that are tricky if we try to use iothread->stopping.
4a2fec
Now iothread->stopping is purely for iothread_stop() and
4a2fec
iothread->running is purely for the iothread_run() thread.
4a2fec
4a2fec
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
4a2fec
Reviewed-by: Eric Blake <eblake@redhat.com>
4a2fec
Message-id: 20171207201320.19284-6-stefanha@redhat.com
4a2fec
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
4a2fec
(cherry picked from commit 2362a28ea11c145e1a13ae79342d76dc118a72a6)
4a2fec
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
4a2fec
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
4a2fec
---
4a2fec
 include/sysemu/iothread.h |  3 ++-
4a2fec
 iothread.c                | 20 +++++++++++++++-----
4a2fec
 2 files changed, 17 insertions(+), 6 deletions(-)
4a2fec
4a2fec
diff --git a/include/sysemu/iothread.h b/include/sysemu/iothread.h
4a2fec
index 55de171..799614f 100644
4a2fec
--- a/include/sysemu/iothread.h
4a2fec
+++ b/include/sysemu/iothread.h
4a2fec
@@ -29,7 +29,8 @@ typedef struct {
4a2fec
     GOnce once;
4a2fec
     QemuMutex init_done_lock;
4a2fec
     QemuCond init_done_cond;    /* is thread initialization done? */
4a2fec
-    bool stopping;
4a2fec
+    bool stopping;              /* has iothread_stop() been called? */
4a2fec
+    bool running;               /* should iothread_run() continue? */
4a2fec
     int thread_id;
4a2fec
 
4a2fec
     /* AioContext poll parameters */
4a2fec
diff --git a/iothread.c b/iothread.c
4a2fec
index e7b93e0..d8b6c1f 100644
4a2fec
--- a/iothread.c
4a2fec
+++ b/iothread.c
4a2fec
@@ -55,7 +55,7 @@ static void *iothread_run(void *opaque)
4a2fec
     qemu_cond_signal(&iothread->init_done_cond);
4a2fec
     qemu_mutex_unlock(&iothread->init_done_lock);
4a2fec
 
4a2fec
-    while (!atomic_read(&iothread->stopping)) {
4a2fec
+    while (iothread->running) {
4a2fec
         aio_poll(iothread->ctx, true);
4a2fec
 
4a2fec
         if (atomic_read(&iothread->worker_context)) {
4a2fec
@@ -78,16 +78,25 @@ static void *iothread_run(void *opaque)
4a2fec
     return NULL;
4a2fec
 }
4a2fec
 
4a2fec
+/* Runs in iothread_run() thread */
4a2fec
+static void iothread_stop_bh(void *opaque)
4a2fec
+{
4a2fec
+    IOThread *iothread = opaque;
4a2fec
+
4a2fec
+    iothread->running = false; /* stop iothread_run() */
4a2fec
+
4a2fec
+    if (iothread->main_loop) {
4a2fec
+        g_main_loop_quit(iothread->main_loop);
4a2fec
+    }
4a2fec
+}
4a2fec
+
4a2fec
 void iothread_stop(IOThread *iothread)
4a2fec
 {
4a2fec
     if (!iothread->ctx || iothread->stopping) {
4a2fec
         return;
4a2fec
     }
4a2fec
     iothread->stopping = true;
4a2fec
-    aio_notify(iothread->ctx);
4a2fec
-    if (atomic_read(&iothread->main_loop)) {
4a2fec
-        g_main_loop_quit(iothread->main_loop);
4a2fec
-    }
4a2fec
+    aio_bh_schedule_oneshot(iothread->ctx, iothread_stop_bh, iothread);
4a2fec
     qemu_thread_join(&iothread->thread);
4a2fec
 }
4a2fec
 
4a2fec
@@ -134,6 +143,7 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
4a2fec
     char *name, *thread_name;
4a2fec
 
4a2fec
     iothread->stopping = false;
4a2fec
+    iothread->running = true;
4a2fec
     iothread->thread_id = -1;
4a2fec
     iothread->ctx = aio_context_new(&local_error);
4a2fec
     if (!iothread->ctx) {
4a2fec
-- 
4a2fec
1.8.3.1
4a2fec