Skip to contentSkip to conversation

qemu_thread_create: propagate the error to callers to handle

PATCH In review Fei Li 9 participants 74 messages Dec 25, 2018 – Jan 16, 2019 RISC-V

lore
Share & tools
lore

Fei Li

author
#0 lore raw

v9 cover qemu_thread_create: propagate the error to callers to handle

Hi,

This idea comes from BiteSizedTasks, and this patch series implement the error checking of qemu_thread_create: make qemu_thread_create return a flag to indicate if it succeeded rather than failing with an error; make all callers check it.

The first and the last patch fixes some segmentation faults occured
during the debugging. The 6/7 patch modifies the
qemu_thread_create() by passing &error_abort and makes it return a
bool to all direct callers to indicate if it succeeds. The next 9
patches will improve on &error_abort for callers who can handle more
properly. The middle four fix some migration issues.

BTW, I am leaving my current company now, and will use "Fei Li <[email protected]>" to continue with this patch series.

Please help to review, thanks a lot! :)

v9:
- To ease the review and involve the appropriate maintainers, split the previous 6/7 patch into 10 patches: the 6/16 patch passes the &error_abort to qemu_thread_create() everywhere, and the next 9 patches will improve on &error_abort for callers who need.
- Add a new patch 5/7 to unify error handling for process_incoming_migration_co().
- Merge the previous 2/7 to current 7/16 to collaboratively handle for qemu_X_start_vcpu and for the qemu_init_vpcu in each arch.
- Add comment for multifd_recv_new_channel() in current patch 2/7.

v8:
- Remove previous two patches trying to fix the multifd issue on the source side, as we are still waiting for maintainer's opinions.
- Use atomic_read to get multifd_recv_state->count in patch 3/7.
- Get three more "Reviewed-by:".

v7:
- Split the previous multifd-migration into two patches: the src and the dst. For the dst, only dump the error instead of quitting.
- Safely do the cleanup for postcopy_ram_enable_notify().
- Split the previous migration-error-handling patch into two patches.

v6:
- Add a new migration-multifd related patch. BTW, delete the previous vnc related patch as it has been upstreamed.
- Use error_setg_errno() to set the errno when qemu_thread_create() fails for both Linux and Windows implementation.
- Optimize the first patch, less codes are needed

v5:
- Remove errno = err in qemu_thread_create() for Linux, and change return errno to return -1 in qemu_signal_init() to indicate the error in case qemu_thread_create() fails.
- Delete the v4-added qemu_cond/mutex_destroy() in iothread_complete() as the destroy() will be done by its callers' object_unref().

v4:
- Separate the migration compression patch from this series
- Add one more error handling patch related with migration
- Add more cleaning up code for touched functions

v3:
- Add two migration related patches to fix the segmentaion fault
- Extract the segmentation fault fix from v2's last patch to be a separate patch

v2:
- Pass errp straightly instead of using a local_err & error_propagate
- Return a bool: false/true to indicate if one function succeeds
- Merge v1's last two patches into one to avoid the compile error
- Fix one omitted error in patch1 and update some error messages

Fei Li (16):
Fix segmentation fault when qemu_signal_init fails
migration: fix the multifd code when receiving less channels
migration: remove unused &local_err parameter in multifd_save_cleanup
migration: add more error handling for postcopy_ram_enable_notify
migration: unify error handling for process_incoming_migration_co
qemu_thread: Make qemu_thread_create() handle errors properly
qemu_thread: supplement error handling for qemu_X_start_vcpu
qemu_thread: supplement error handling for qmp_dump_guest_memory
qemu_thread: supplement error handling for pci_edu_realize
qemu_thread: supplement error handling for h_resize_hpt_prepare
qemu_thread: supplement error handling for emulated_realize
qemu_thread: supplement error handling for
iothread_complete/qemu_signalfd_compat
qemu_thread: supplement error handling for migration
qemu_thread: supplement error handling for vnc_start_worker_thread
qemu_thread: supplement error handling for touch_all_pages
qemu_thread_join: fix segmentation fault

 accel/tcg/user-exec-stub.c      |  3 +-
 cpus.c                          | 79 ++++++++++++++++++++++++++---------------
 dump.c                          |  6 ++--
 hw/misc/edu.c                   |  7 ++--
 hw/ppc/spapr_hcall.c            | 10 ++++--
 hw/rdma/rdma_backend.c          |  3 +-
 hw/usb/ccid-card-emulated.c     | 14 +++++---
 include/qemu/thread.h           |  4 +--
 include/qom/cpu.h               |  2 +-
 io/task.c                       |  3 +-
 iothread.c                      | 16 ++++++---
 migration/channel.c             | 11 +++---
 migration/migration.c           | 68 ++++++++++++++++++++++-------------
 migration/migration.h           |  2 +-
 migration/postcopy-ram.c        | 15 ++++++--
 migration/ram.c                 | 68 ++++++++++++++++++++++++-----------
 migration/ram.h                 |  4 +--
 migration/savevm.c              | 12 +++++--
 target/alpha/cpu.c              |  4 ++-
 target/arm/cpu.c                |  4 ++-
 target/cris/cpu.c               |  4 ++-
 target/hppa/cpu.c               |  4 ++-
 target/i386/cpu.c               |  4 ++-
 target/lm32/cpu.c               |  4 ++-
 target/m68k/cpu.c               |  4 ++-
 target/microblaze/cpu.c         |  4 ++-
 target/mips/cpu.c               |  4 ++-
 target/moxie/cpu.c              |  4 ++-
 target/nios2/cpu.c              |  4 ++-
 target/openrisc/cpu.c           |  4 ++-
 target/ppc/translate_init.inc.c |  4 ++-
 target/riscv/cpu.c              |  4 ++-
 target/s390x/cpu.c              |  4 ++-
 target/sh4/cpu.c                |  4 ++-
 target/sparc/cpu.c              |  4 ++-
 target/tilegx/cpu.c             |  4 ++-
 target/tricore/cpu.c            |  4 ++-
 target/unicore32/cpu.c          |  4 ++-
 target/xtensa/cpu.c             |  4 ++-
 tests/atomic_add-bench.c        |  3 +-
 tests/iothread.c                |  2 +-
 tests/qht-bench.c               |  3 +-
 tests/rcutorture.c              |  3 +-
 tests/test-aio.c                |  2 +-
 tests/test-rcu-list.c           |  3 +-
 ui/vnc-jobs.c                   | 17 ++++++---
 ui/vnc-jobs.h                   |  2 +-
 ui/vnc.c                        |  4 ++-
 util/compatfd.c                 | 11 ++++--
 util/main-loop.c                |  8 ++---
 util/oslib-posix.c              | 24 ++++++++-----
 util/qemu-thread-posix.c        | 30 ++++++++++++----
 util/qemu-thread-win32.c        | 18 +++++++---
 util/rcu.c                      |  3 +-
 util/thread-pool.c              |  4 ++-
 55 files changed, 378 insertions(+), 170 deletions(-)
Signature · 1 line
2.13.7
author
#1 lore raw

PATCH v9 1/16 Fix segmentation fault when qemu_signal_init fails

When qemu_signal_init() fails in qemu_init_main_loop(), we return without setting an error. Its callers crash then when they try to report the error with error_report_err().

To avoid such segmentation fault, add a new Error parameter to make the call trace to propagate the err to the final caller.

Fixes 2f78e491d7b46542158ce0b8132ee4e05bc0ade4
Cc Paolo Bonzini <[email protected]>
---
 util/main-loop.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
util/main-loop.c +4 -4
index affe0403c5..443cb4cfe8 100644
@@ -71,7 +71,7 @@ static void sigfd_handler(void *opaque)
     }
 }
 
-static int qemu_signal_init(void)
+static int qemu_signal_init(Error **errp)
 {
     int sigfd;
     sigset_t set;
@@ -96,7 +96,7 @@ static int qemu_signal_init(void)
     sigdelset(&set, SIG_IPI);
     sigfd = qemu_signalfd(&set);
     if (sigfd == -1) {
-        fprintf(stderr, "failed to create signalfd\n");
+        error_setg_errno(errp, errno, "failed to create signalfd");
         return -errno;
     }
 
@@ -109,7 +109,7 @@ static int qemu_signal_init(void)
 
 #else /* _WIN32 */
 
-static int qemu_signal_init(void)
+static int qemu_signal_init(Error **errp)
 {
     return 0;
 }
@@ -148,7 +148,7 @@ int qemu_init_main_loop(Error **errp)
 
     init_clocks(qemu_timer_notify_cb);
 
-    ret = qemu_signal_init();
+    ret = qemu_signal_init(errp);
     if (ret) {
         return ret;
     }
Signature · 1 line
2.13.7
author
#2 lore raw

PATCH v9 2/16 migration: fix the multifd code when receiving less channels

In reply to Fei Li

In our current code, when multifd is used during migration, if there is an error before the destination receives all new channels, the source keeps running, however the destination does not exit but keeps waiting until the source is killed deliberately.

Fix this by dumping the specific error and let users decide whether to quit from the destination side when failing to receive packet via some channel. And update the comment for multifd_recv_new_channel().

Cc Dr. David Alan Gilbert <[email protected]>
Cc Peter Xu <[email protected]>
Cc Markus Armbruster <[email protected]>
---
 migration/channel.c   | 11 ++++++-----
 migration/migration.c |  9 +++++++--
 migration/migration.h |  2 +-
 migration/ram.c       | 17 ++++++++++++++---
 migration/ram.h       |  2 +-
 5 files changed, 29 insertions(+), 12 deletions(-)
migration/channel.c +6 -5
index 33e0e9b82f..20e4c8e2dc 100644
@@ -30,6 +30,7 @@
 void migration_channel_process_incoming(QIOChannel *ioc)
 {
     MigrationState *s = migrate_get_current();
+    Error *local_err = NULL;
 
     trace_migration_set_incoming_channel(
         ioc, object_get_typename(OBJECT(ioc)));
@@ -38,13 +39,13 @@ void migration_channel_process_incoming(QIOChannel *ioc)
         *s->parameters.tls_creds &&
         !object_dynamic_cast(OBJECT(ioc),
                              TYPE_QIO_CHANNEL_TLS)) {
-        Error *local_err = NULL;
         migration_tls_channel_process_incoming(s, ioc, &local_err);
-        if (local_err) {
-            error_report_err(local_err);
-        }
     } else {
-        migration_ioc_process_incoming(ioc);
+        migration_ioc_process_incoming(ioc, &local_err);
+    }
+
+    if (local_err) {
+        error_report_err(local_err);
     }
 }
 
migration/migration.c +7 -2
index ffc4d9e556..24cb4b9d0d 100644
@@ -541,7 +541,7 @@ void migration_fd_process_incoming(QEMUFile *f)
     migration_incoming_process();
 }
 
-void migration_ioc_process_incoming(QIOChannel *ioc)
+void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp)
 {
     MigrationIncomingState *mis = migration_incoming_get_current();
     bool start_migration;
@@ -563,9 +563,14 @@ void migration_ioc_process_incoming(QIOChannel *ioc)
          */
         start_migration = !migrate_use_multifd();
     } else {
+        Error *local_err = NULL;
         /* Multiple connections */
         assert(migrate_use_multifd());
-        start_migration = multifd_recv_new_channel(ioc);
+        start_migration = multifd_recv_new_channel(ioc, &local_err);
+        if (local_err) {
+            error_propagate(errp, local_err);
+            return;
+        }
     }
 
     if (start_migration) {
migration/migration.h +1 -1
index e413d4d8b6..02b7304610 100644
@@ -229,7 +229,7 @@ struct MigrationState
 void migrate_set_state(int *state, int old_state, int new_state);
 
 void migration_fd_process_incoming(QEMUFile *f);
-void migration_ioc_process_incoming(QIOChannel *ioc);
+void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp);
 void migration_incoming_process(void);
 
 bool  migration_has_all_channels(void);
migration/ram.c +14 -3
index 7e7deec4d8..1671dedc97 100644
@@ -1322,8 +1322,13 @@ bool multifd_recv_all_channels_created(void)
     return thread_count == atomic_read(&multifd_recv_state->count);
 }
 
-/* Return true if multifd is ready for the migration, otherwise false */
-bool multifd_recv_new_channel(QIOChannel *ioc)
+/*
+ * Try to receive all multifd channels to get ready for the migration.
+ * - Return true and do not set @errp when correctly receving all channels;
+ * - Return false and do not set @errp when correctly receiving the current one;
+ * - Return false and set @errp when failing to receive the current channel.
+ */
+bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
 {
     MultiFDRecvParams *p;
     Error *local_err = NULL;
@@ -1332,6 +1337,10 @@ bool multifd_recv_new_channel(QIOChannel *ioc)
     id = multifd_recv_initial_packet(ioc, &local_err);
     if (id < 0) {
         multifd_recv_terminate_threads(local_err);
+        error_propagate_prepend(errp, local_err,
+                                "failed to receive packet"
+                                " via multifd channel %d: ",
+                                atomic_read(&multifd_recv_state->count));
         return false;
     }
 
@@ -1340,6 +1349,7 @@ bool multifd_recv_new_channel(QIOChannel *ioc)
         error_setg(&local_err, "multifd: received id '%d' already setup'",
                    id);
         multifd_recv_terminate_threads(local_err);
+        error_propagate(errp, local_err);
         return false;
     }
     p->c = ioc;
@@ -1351,7 +1361,8 @@ bool multifd_recv_new_channel(QIOChannel *ioc)
     qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p,
                        QEMU_THREAD_JOINABLE);
     atomic_inc(&multifd_recv_state->count);
-    return multifd_recv_state->count == migrate_multifd_channels();
+    return atomic_read(&multifd_recv_state->count) ==
+           migrate_multifd_channels();
 }
 
 /**
migration/ram.h +1 -1
index 83ff1bc11a..046d3074be 100644
@@ -47,7 +47,7 @@ int multifd_save_cleanup(Error **errp);
 int multifd_load_setup(void);
 int multifd_load_cleanup(Error **errp);
 bool multifd_recv_all_channels_created(void);
-bool multifd_recv_new_channel(QIOChannel *ioc);
+bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp);
 
 uint64_t ram_pagesize_summary(void);
 int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len);
Signature · 1 line
2.13.7
author
#3 lore raw

PATCH v9 3/16 migration: remove unused &local_err parameter in multifd_save_cleanup

In reply to Fei Li

Always call migrate_set_error() to set the error state without relying on whether multifd_save_cleanup() succeeds. As the passed &local_err is never used in multifd_save_cleanup(), remove it. And make the function be: void multifd_save_cleanup(void).

Cc Dr. David Alan Gilbert <[email protected]>
---
 migration/migration.c |  5 +----
 migration/ram.c       | 11 ++++-------
 migration/ram.h       |  2 +-
 3 files changed, 6 insertions(+), 12 deletions(-)
migration/migration.c +1 -4
index 24cb4b9d0d..5d322eb9d6 100644
@@ -1386,7 +1386,6 @@ static void migrate_fd_cleanup(void *opaque)
     qemu_savevm_state_cleanup();
 
     if (s->to_dst_file) {
-        Error *local_err = NULL;
         QEMUFile *tmp;
 
         trace_migrate_fd_cleanup();
@@ -1397,9 +1396,7 @@ static void migrate_fd_cleanup(void *opaque)
         }
         qemu_mutex_lock_iothread();
 
-        if (multifd_save_cleanup(&local_err) != 0) {
-            error_report_err(local_err);
-        }
+        multifd_save_cleanup();
         qemu_mutex_lock(&s->qemu_file_lock);
         tmp = s->to_dst_file;
         s->to_dst_file = NULL;
migration/ram.c +4 -7
index 1671dedc97..435a8d2946 100644
@@ -917,13 +917,12 @@ static void multifd_send_terminate_threads(Error *err)
     }
 }
 
-int multifd_save_cleanup(Error **errp)
+void multifd_save_cleanup(void)
 {
     int i;
-    int ret = 0;
 
     if (!migrate_use_multifd()) {
-        return 0;
+        return;
     }
     multifd_send_terminate_threads(NULL);
     for (i = 0; i < migrate_multifd_channels(); i++) {
@@ -953,7 +952,6 @@ int multifd_save_cleanup(Error **errp)
     multifd_send_state->pages = NULL;
     g_free(multifd_send_state);
     multifd_send_state = NULL;
-    return ret;
 }
 
 static void multifd_send_sync_main(void)
@@ -1071,9 +1069,8 @@ static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque)
     Error *local_err = NULL;
 
     if (qio_task_propagate_error(task, &local_err)) {
-        if (multifd_save_cleanup(&local_err) != 0) {
-            migrate_set_error(migrate_get_current(), local_err);
-        }
+        migrate_set_error(migrate_get_current(), local_err);
+        multifd_save_cleanup();
     } else {
         p->c = QIO_CHANNEL(sioc);
         qio_channel_set_delay(p->c, false);
migration/ram.h +1 -1
index 046d3074be..936177b3e9 100644
@@ -43,7 +43,7 @@ uint64_t ram_bytes_remaining(void);
 uint64_t ram_bytes_total(void);
 
 int multifd_save_setup(void);
-int multifd_save_cleanup(Error **errp);
+void multifd_save_cleanup(void);
 int multifd_load_setup(void);
 int multifd_load_cleanup(Error **errp);
 bool multifd_recv_all_channels_created(void);
Signature · 1 line
2.13.7
#4 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 03/16] migration: remove unused &local_err parameter in multifd_save_cleanup

Quoted 8 lines · Fei Li

Always call migrate_set_error() to set the error state without relying on whether multifd_save_cleanup() succeeds. As the passed &local_err is never used in multifd_save_cleanup(), remove it. And make the function be: void multifd_save_cleanup(void).

Cc Dr. David Alan Gilbert <[email protected]>

The commit message is confusing. Suggest:

migration: multifd_save_cleanup() can't fail, simplify

multifd_save_cleanup() takes an Error ** argument and returns an error code even though it can't actually fail. Its callers dutifully check for failure. Remove the useless argument and return value, and simplify the callers.

I think multifd_load_cleanup() has exactly the same issue. Should we clean it up, too? Juan, what do you think?

#5 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 03/16] migration: remove unused &local_err parameter in multifd_save_cleanup

Quoted 15 lines · 在 2019年1月8日,00:50,Markus Armbruster <[email protected]> 写道:
Quoted 8 lines · Fei Li

Always call migrate_set_error() to set the error state without relying on whether multifd_save_cleanup() succeeds. As the passed &local_err is never used in multifd_save_cleanup(), remove it. And make the function be: void multifd_save_cleanup(void).

Cc Dr. David Alan Gilbert <[email protected]>
Quoted 8 lines

The commit message is confusing. Suggest:

migration: multifd_save_cleanup() can't fail, simplify

multifd_save_cleanup() takes an Error ** argument and returns an error code even though it can't actually fail. Its callers dutifully check for failure. Remove the useless argument and return value, and simplify the callers.

Nice, thanks for the clearer comment. :)

Have a nice day
Fei

Quoted 3 lines

I think multifd_load_cleanup() has exactly the same issue. Should we clean it up, too? Juan, what do you think?

author
#6 lore raw

PATCH v9 4/16 migration: add more error handling for postcopy_ram_enable_notify

In reply to Fei Li

Call postcopy_ram_incoming_cleanup() to do the cleanup when postcopy_ram_enable_notify fails. Besides, report the error message when qemu_ram_foreach_migratable_block() fails.

---
 migration/postcopy-ram.c | 1 +
 migration/savevm.c       | 1 +
 2 files changed, 2 insertions(+)
migration/postcopy-ram.c +1 -0
index e5c02a32c5..fa09dba534 100644
@@ -1117,6 +1117,7 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis)
 
     /* Mark so that we get notified of accesses to unwritten areas */
     if (qemu_ram_foreach_migratable_block(ram_block_enable_notify, mis)) {
+        error_report("ram_block_enable_notify failed");
         return -1;
     }
 
migration/savevm.c +1 -0
index 9e45fb4f3f..d784e8aa40 100644
@@ -1729,6 +1729,7 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
      */
     if (migrate_postcopy_ram()) {
         if (postcopy_ram_enable_notify(mis)) {
+            postcopy_ram_incoming_cleanup(mis);
             return -1;
         }
     }
Signature · 1 line
2.13.7
author
#7 lore raw

PATCH v9 5/16 migration: unify error handling for process_incoming_migration_co

In reply to Fei Li

In the current code, if process_incoming_migration_co() fails we do the same error handing: set the error state, close the source file, do the cleanup for multifd, and then exit(EXIT_FAILURE). To make the code clearer, add a "goto fail" to unify the error handling.

Cc Markus Armbruster <[email protected]>
Cc Dr. David Alan Gilbert <[email protected]>
Cc Peter Xu <[email protected]>
---
 migration/migration.c | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)
migration/migration.c +13 -13
index 5d322eb9d6..ded151b1bf 100644
@@ -438,15 +438,13 @@ static void process_incoming_migration_co(void *opaque)
         /* Make sure all file formats flush their mutable metadata */
         bdrv_invalidate_cache_all(&local_err);
         if (local_err) {
-            migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
-                    MIGRATION_STATUS_FAILED);
             error_report_err(local_err);
-            exit(EXIT_FAILURE);
+            goto fail;
         }
 
         if (colo_init_ram_cache() < 0) {
             error_report("Init ram cache failed");
-            exit(EXIT_FAILURE);
+            goto fail;
         }
 
         qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming",
@@ -461,20 +459,22 @@ static void process_incoming_migration_co(void *opaque)
     }
 
     if (ret < 0) {
-        Error *local_err = NULL;
-
-        migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
-                          MIGRATION_STATUS_FAILED);
         error_report("load of migration failed: %s", strerror(-ret));
-        qemu_fclose(mis->from_src_file);
-        if (multifd_load_cleanup(&local_err) != 0) {
-            error_report_err(local_err);
-        }
-        exit(EXIT_FAILURE);
+        goto fail;
     }
     mis->bh = qemu_bh_new(process_incoming_migration_bh, mis);
     qemu_bh_schedule(mis->bh);
     mis->migration_incoming_co = NULL;
+    return;
+fail:
+    local_err = NULL;
+    migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
+                      MIGRATION_STATUS_FAILED);
+    qemu_fclose(mis->from_src_file);
+    if (multifd_load_cleanup(&local_err) != 0) {
+        error_report_err(local_err);
+    }
+    exit(EXIT_FAILURE);
 }
 
 static void migration_incoming_setup(QEMUFile *f)
Signature · 1 line
2.13.7
Reviewed-by on v9 5/16
#8 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 05/16] migration: unify error handling for process_incoming_migration_co

Quoted 57 lines · * Fei Li ([email protected]) wrote:

In the current code, if process_incoming_migration_co() fails we do the same error handing: set the error state, close the source file, do the cleanup for multifd, and then exit(EXIT_FAILURE). To make the code clearer, add a "goto fail" to unify the error handling.

Cc Markus Armbruster <[email protected]>
Cc Dr. David Alan Gilbert <[email protected]>
Cc Peter Xu <[email protected]>
---
 migration/migration.c | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)
migration/migration.c +6 -13
index 5d322eb9d6..ded151b1bf 100644
@@ -438,15 +438,13 @@ static void process_incoming_migration_co(void *opaque)
         /* Make sure all file formats flush their mutable metadata */
         bdrv_invalidate_cache_all(&local_err);
         if (local_err) {
-            migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
-                    MIGRATION_STATUS_FAILED);
             error_report_err(local_err);
-            exit(EXIT_FAILURE);
+            goto fail;
         }
 
         if (colo_init_ram_cache() < 0) {
             error_report("Init ram cache failed");
-            exit(EXIT_FAILURE);
+            goto fail;
         }
 
         qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming",
@@ -461,20 +459,22 @@ static void process_incoming_migration_co(void *opaque)
     }
 
     if (ret < 0) {
-        Error *local_err = NULL;
-
-        migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
-                          MIGRATION_STATUS_FAILED);
         error_report("load of migration failed: %s", strerror(-ret));
-        qemu_fclose(mis->from_src_file);
-        if (multifd_load_cleanup(&local_err) != 0) {
-            error_report_err(local_err);
-        }
-        exit(EXIT_FAILURE);
+        goto fail;
     }
     mis->bh = qemu_bh_new(process_incoming_migration_bh, mis);
     qemu_bh_schedule(mis->bh);
     mis->migration_incoming_co = NULL;
+    return;
+fail:
+    local_err = NULL;

migration/migration.c static void process_incoming_migration_co(void *opaque) · line 480

Quoted 10 lines
migration/migration.c +7 -0
+    migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
+                      MIGRATION_STATUS_FAILED);
+    qemu_fclose(mis->from_src_file);
+    if (multifd_load_cleanup(&local_err) != 0) {
+        error_report_err(local_err);
+    }
+    exit(EXIT_FAILURE);
 }
 
 static void migration_incoming_setup(QEMUFile *f)

OK, so this is really unifying the normal error case and the two colo-incoming error cases; so I think that's fine.

Quoted 3 lines
Signature · 1 line
2.13.7

--
Dr. David Alan Gilbert / [email protected] / Manchester, UK

#9 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 05/16] migration: unify error handling for process_incoming_migration_co

Quoted 67 lines · Dr. David Alan Gilbert
Quoted 66 lines · * Fei Li ([email protected]) wrote:

In the current code, if process_incoming_migration_co() fails we do the same error handing: set the error state, close the source file, do the cleanup for multifd, and then exit(EXIT_FAILURE). To make the code clearer, add a "goto fail" to unify the error handling.

Cc Markus Armbruster <[email protected]>
Cc Dr. David Alan Gilbert <[email protected]>
Cc Peter Xu <[email protected]>
---
  migration/migration.c | 26 +++++++++++++-------------
  1 file changed, 13 insertions(+), 13 deletions(-)
migration/migration.c +13 -13
index 5d322eb9d6..ded151b1bf 100644
@@ -438,15 +438,13 @@ static void process_incoming_migration_co(void *opaque)
          /* Make sure all file formats flush their mutable metadata */
          bdrv_invalidate_cache_all(&local_err);
          if (local_err) {
-            migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
-                    MIGRATION_STATUS_FAILED);
              error_report_err(local_err);
-            exit(EXIT_FAILURE);
+            goto fail;
          }
 
          if (colo_init_ram_cache() < 0) {
              error_report("Init ram cache failed");
-            exit(EXIT_FAILURE);
+            goto fail;
          }
 
          qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming",
@@ -461,20 +459,22 @@ static void process_incoming_migration_co(void *opaque)
      }
 
      if (ret < 0) {
-        Error *local_err = NULL;
-
-        migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
-                          MIGRATION_STATUS_FAILED);
          error_report("load of migration failed: %s", strerror(-ret));
-        qemu_fclose(mis->from_src_file);
-        if (multifd_load_cleanup(&local_err) != 0) {
-            error_report_err(local_err);
-        }
-        exit(EXIT_FAILURE);
+        goto fail;
      }
      mis->bh = qemu_bh_new(process_incoming_migration_bh, mis);
      qemu_bh_schedule(mis->bh);
      mis->migration_incoming_co = NULL;
+    return;
+fail:
+    local_err = NULL;
+    migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
+                      MIGRATION_STATUS_FAILED);
+    qemu_fclose(mis->from_src_file);
+    if (multifd_load_cleanup(&local_err) != 0) {
+        error_report_err(local_err);
+    }
+    exit(EXIT_FAILURE);
  }
 
Quoted 6 lines

migration/migration.c static void process_incoming_migration_co(void *opaque) · line 480

Quoted 1 line
migration/migration.c +0 -0
  static void migration_incoming_setup(QEMUFile *f)

OK, so this is really unifying the normal error case and the two colo-incoming error cases; so I think that's fine.

Thanks for the review :)

Fei

Quoted 6 lines
Quoted 4 lines
Signature · 2 lines
-- 
2.13.7

--
Dr. David Alan Gilbert / [email protected] / Manchester, UK

author
#10 lore raw

PATCH v9 6/16 qemu_thread: Make qemu_thread_create() handle errors properly

In reply to Fei Li

qemu_thread_create() abort()s on error. Not nice. Give it a return value and an Error ** argument, so it can return success/failure.

Considering qemu_thread_create() is quite widely used in qemu, split this into two steps: this patch passes the &error_abort to qemu_thread_create() everywhere, and the next 9 patches will improve on &error_abort for callers who need.

Cc Markus Armbruster <[email protected]>
Cc Paolo Bonzini <[email protected]>
---
 cpus.c                      | 23 +++++++++++++++--------
 dump.c                      |  3 ++-
 hw/misc/edu.c               |  4 +++-
 hw/ppc/spapr_hcall.c        |  4 +++-
 hw/rdma/rdma_backend.c      |  3 ++-
 hw/usb/ccid-card-emulated.c |  5 +++--
 include/qemu/thread.h       |  4 ++--
 io/task.c                   |  3 ++-
 iothread.c                  |  3 ++-
 migration/migration.c       | 11 ++++++++---
 migration/postcopy-ram.c    |  4 +++-
 migration/ram.c             | 12 ++++++++----
 migration/savevm.c          |  3 ++-
 tests/atomic_add-bench.c    |  3 ++-
 tests/iothread.c            |  2 +-
 tests/qht-bench.c           |  3 ++-
 tests/rcutorture.c          |  3 ++-
 tests/test-aio.c            |  2 +-
 tests/test-rcu-list.c       |  3 ++-
 ui/vnc-jobs.c               |  6 ++++--
 util/compatfd.c             |  6 ++++--
 util/oslib-posix.c          |  3 ++-
 util/qemu-thread-posix.c    | 27 ++++++++++++++++++++-------
 util/qemu-thread-win32.c    | 16 ++++++++++++----
 util/rcu.c                  |  3 ++-
 util/thread-pool.c          |  4 +++-
 26 files changed, 112 insertions(+), 51 deletions(-)
cpus.c +15 -8
index 0ddeeefc14..25df03326b 100644
@@ -1961,15 +1961,17 @@ static void qemu_tcg_init_vcpu(CPUState *cpu)
             snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG",
                  cpu->cpu_index);
 
+            /* TODO: let the callers handle the error instead of abort() here */
             qemu_thread_create(cpu->thread, thread_name, qemu_tcg_cpu_thread_fn,
-                               cpu, QEMU_THREAD_JOINABLE);
+                               cpu, QEMU_THREAD_JOINABLE, &error_abort);
 
         } else {
             /* share a single thread for all cpus with TCG */
             snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "ALL CPUs/TCG");
+            /* TODO: let the callers handle the error instead of abort() here */
             qemu_thread_create(cpu->thread, thread_name,
                                qemu_tcg_rr_cpu_thread_fn,
-                               cpu, QEMU_THREAD_JOINABLE);
+                               cpu, QEMU_THREAD_JOINABLE, &error_abort);
 
             single_tcg_halt_cond = cpu->halt_cond;
             single_tcg_cpu_thread = cpu->thread;
@@ -1997,8 +1999,9 @@ static void qemu_hax_start_vcpu(CPUState *cpu)
 
     snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HAX",
              cpu->cpu_index);
+    /* TODO: let the further caller handle the error instead of abort() here */
     qemu_thread_create(cpu->thread, thread_name, qemu_hax_cpu_thread_fn,
-                       cpu, QEMU_THREAD_JOINABLE);
+                       cpu, QEMU_THREAD_JOINABLE, &error_abort);
 #ifdef _WIN32
     cpu->hThread = qemu_thread_get_handle(cpu->thread);
 #endif
@@ -2013,8 +2016,9 @@ static void qemu_kvm_start_vcpu(CPUState *cpu)
     qemu_cond_init(cpu->halt_cond);
     snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/KVM",
              cpu->cpu_index);
+    /* TODO: let the further caller handle the error instead of abort() here */
     qemu_thread_create(cpu->thread, thread_name, qemu_kvm_cpu_thread_fn,
-                       cpu, QEMU_THREAD_JOINABLE);
+                       cpu, QEMU_THREAD_JOINABLE, &error_abort);
 }
 
 static void qemu_hvf_start_vcpu(CPUState *cpu)
@@ -2031,8 +2035,9 @@ static void qemu_hvf_start_vcpu(CPUState *cpu)
 
     snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HVF",
              cpu->cpu_index);
+    /* TODO: let the further caller handle the error instead of abort() here */
     qemu_thread_create(cpu->thread, thread_name, qemu_hvf_cpu_thread_fn,
-                       cpu, QEMU_THREAD_JOINABLE);
+                       cpu, QEMU_THREAD_JOINABLE, &error_abort);
 }
 
 static void qemu_whpx_start_vcpu(CPUState *cpu)
@@ -2044,8 +2049,9 @@ static void qemu_whpx_start_vcpu(CPUState *cpu)
     qemu_cond_init(cpu->halt_cond);
     snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/WHPX",
              cpu->cpu_index);
+    /* TODO: let the further caller handle the error instead of abort() here */
     qemu_thread_create(cpu->thread, thread_name, qemu_whpx_cpu_thread_fn,
-                       cpu, QEMU_THREAD_JOINABLE);
+                       cpu, QEMU_THREAD_JOINABLE, &error_abort);
 #ifdef _WIN32
     cpu->hThread = qemu_thread_get_handle(cpu->thread);
 #endif
@@ -2060,8 +2066,9 @@ static void qemu_dummy_start_vcpu(CPUState *cpu)
     qemu_cond_init(cpu->halt_cond);
     snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/DUMMY",
              cpu->cpu_index);
-    qemu_thread_create(cpu->thread, thread_name, qemu_dummy_cpu_thread_fn, cpu,
-                       QEMU_THREAD_JOINABLE);
+    /* TODO: let the further caller handle the error instead of abort() here */
+    qemu_thread_create(cpu->thread, thread_name, qemu_dummy_cpu_thread_fn,
+                       cpu, QEMU_THREAD_JOINABLE, &error_abort);
 }
 
 void qemu_init_vcpu(CPUState *cpu)
dump.c +2 -1
index 4ec94c5e25..c35d6ddd22 100644
@@ -2020,8 +2020,9 @@ void qmp_dump_guest_memory(bool paging, const char *file,
     if (detach_p) {
         /* detached dump */
         s->detached = true;
+        /* TODO: let the further caller handle the error instead of abort() */
         qemu_thread_create(&s->dump_thread, "dump_thread", dump_thread,
-                           s, QEMU_THREAD_DETACHED);
+                           s, QEMU_THREAD_DETACHED, &error_abort);
     } else {
         /* sync dump */
         dump_process(s, errp);
hw/misc/edu.c +3 -1
index cdcf550dd7..3f4ba7ded3 100644
@@ -28,6 +28,7 @@
 #include "hw/pci/msi.h"
 #include "qemu/timer.h"
 #include "qemu/main-loop.h" /* iothread mutex */
+#include "qapi/error.h"
 #include "qapi/visitor.h"
 
 #define TYPE_PCI_EDU_DEVICE "edu"
@@ -355,8 +356,9 @@ static void pci_edu_realize(PCIDevice *pdev, Error **errp)
 
     qemu_mutex_init(&edu->thr_mutex);
     qemu_cond_init(&edu->thr_cond);
+    /* TODO: let the further caller handle the error instead of abort() here */
     qemu_thread_create(&edu->thread, "edu", edu_fact_thread,
-                       edu, QEMU_THREAD_JOINABLE);
+                       edu, QEMU_THREAD_JOINABLE, &error_abort);
 
     memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu,
                     "edu-mmio", 1 * MiB);
hw/ppc/spapr_hcall.c +3 -1
index ae913d070f..5bc2cf4540 100644
@@ -538,8 +538,10 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu,
     pending->shift = shift;
     pending->ret = H_HARDWARE;
 
+    /* TODO: let the further caller handle the error instead of abort() here */
     qemu_thread_create(&pending->thread, "sPAPR HPT prepare",
-                       hpt_prepare_thread, pending, QEMU_THREAD_DETACHED);
+                       hpt_prepare_thread, pending,
+                       QEMU_THREAD_DETACHED, &error_abort);
 
     spapr->pending_hpt = pending;
 
hw/rdma/rdma_backend.c +2 -1
index c28bfbd44d..25e0c7d0f5 100644
@@ -263,7 +263,8 @@ static void start_comp_thread(RdmaBackendDev *backend_dev)
              ibv_get_device_name(backend_dev->ib_dev));
     backend_dev->comp_thread.run = true;
     qemu_thread_create(&backend_dev->comp_thread.thread, thread_name,
-                       comp_handler_thread, backend_dev, QEMU_THREAD_DETACHED);
+                       comp_handler_thread, backend_dev,
+                       QEMU_THREAD_DETACHED, &error_abort);
 }
 
 void rdma_backend_register_comp_handler(void (*handler)(void *ctx,
hw/usb/ccid-card-emulated.c +3 -2
index 25976ed84f..f8ff7ff4a3 100644
@@ -544,10 +544,11 @@ static void emulated_realize(CCIDCardState *base, Error **errp)
         error_setg(errp, "%s: failed to initialize vcard", TYPE_EMULATED_CCID);
         goto out2;
     }
+    /* TODO: let the further caller handle the error instead of abort() here */
     qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread,
-                       card, QEMU_THREAD_JOINABLE);
+                       card, QEMU_THREAD_JOINABLE, &error_abort);
     qemu_thread_create(&card->apdu_thread_id, "ccid/apdu", handle_apdu_thread,
-                       card, QEMU_THREAD_JOINABLE);
+                       card, QEMU_THREAD_JOINABLE, &error_abort);
 
 out2:
     clean_event_notifier(card);
include/qemu/thread.h +2 -2
index 55d83a907c..12291f4ccd 100644
@@ -152,9 +152,9 @@ void qemu_event_reset(QemuEvent *ev);
 void qemu_event_wait(QemuEvent *ev);
 void qemu_event_destroy(QemuEvent *ev);
 
-void qemu_thread_create(QemuThread *thread, const char *name,
+bool qemu_thread_create(QemuThread *thread, const char *name,
                         void *(*start_routine)(void *),
-                        void *arg, int mode);
+                        void *arg, int mode, Error **errp);
 void *qemu_thread_join(QemuThread *thread);
 void qemu_thread_get_self(QemuThread *thread);
 bool qemu_thread_is_self(QemuThread *thread);
io/task.c +2 -1
index 2886a2c1bc..6d3a18ab80 100644
@@ -149,7 +149,8 @@ void qio_task_run_in_thread(QIOTask *task,
                        "io-task-worker",
                        qio_task_thread_worker,
                        data,
-                       QEMU_THREAD_DETACHED);
+                       QEMU_THREAD_DETACHED,
+                       &error_abort);
 }
 
 
iothread.c +2 -1
index 2fb1cdf55d..8e8aa01999 100644
@@ -178,8 +178,9 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
      */
     name = object_get_canonical_path_component(OBJECT(obj));
     thread_name = g_strdup_printf("IO %s", name);
+    /* TODO: let the further caller handle the error instead of abort() here */
     qemu_thread_create(&iothread->thread, thread_name, iothread_run,
-                       iothread, QEMU_THREAD_JOINABLE);
+                       iothread, QEMU_THREAD_JOINABLE, &error_abort);
     g_free(thread_name);
     g_free(name);
 
migration/migration.c +8 -3
index ded151b1bf..ea5839ff0d 100644
@@ -447,8 +447,10 @@ static void process_incoming_migration_co(void *opaque)
             goto fail;
         }
 
+        /* TODO: let the further caller handle the error instead of abort() */
         qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming",
-             colo_process_incoming_thread, mis, QEMU_THREAD_JOINABLE);
+                           colo_process_incoming_thread, mis,
+                           QEMU_THREAD_JOINABLE, &error_abort);
         mis->have_colo_incoming_thread = true;
         qemu_coroutine_yield();
 
@@ -2358,8 +2360,10 @@ static int open_return_path_on_source(MigrationState *ms,
         return 0;
     }
 
+    /* TODO: let the further caller handle the error instead of abort() here */
     qemu_thread_create(&ms->rp_state.rp_thread, "return path",
-                       source_return_path_thread, ms, QEMU_THREAD_JOINABLE);
+                       source_return_path_thread, ms,
+                       QEMU_THREAD_JOINABLE, &error_abort);
 
     trace_open_return_path_on_source_continue();
 
@@ -3189,8 +3193,9 @@ void migrate_fd_connect(MigrationState *s, Error *error_in)
         migrate_fd_cleanup(s);
         return;
     }
+    /* TODO: let the further caller handle the error instead of abort() here */
     qemu_thread_create(&s->thread, "live_migration", migration_thread, s,
-                       QEMU_THREAD_JOINABLE);
+                       QEMU_THREAD_JOINABLE, &error_abort);
     s->migration_thread_running = true;
 }
 
migration/postcopy-ram.c +3 -1
index fa09dba534..221ea24919 100644
@@ -1109,8 +1109,10 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis)
     }
 
     qemu_sem_init(&mis->fault_thread_sem, 0);
+    /* TODO: let the further caller handle the error instead of abort() here */
     qemu_thread_create(&mis->fault_thread, "postcopy/fault",
-                       postcopy_ram_fault_thread, mis, QEMU_THREAD_JOINABLE);
+                       postcopy_ram_fault_thread, mis,
+                       QEMU_THREAD_JOINABLE, &error_abort);
     qemu_sem_wait(&mis->fault_thread_sem);
     qemu_sem_destroy(&mis->fault_thread_sem);
     mis->have_fault_thread = true;
migration/ram.c +8 -4
index 435a8d2946..eed1daf302 100644
@@ -502,9 +502,10 @@ static int compress_threads_save_setup(void)
         comp_param[i].quit = false;
         qemu_mutex_init(&comp_param[i].mutex);
         qemu_cond_init(&comp_param[i].cond);
+        /* TODO: let the further caller handle the error instead of abort() */
         qemu_thread_create(compress_threads + i, "compress",
                            do_data_compress, comp_param + i,
-                           QEMU_THREAD_JOINABLE);
+                           QEMU_THREAD_JOINABLE, &error_abort);
     }
     return 0;
 
@@ -1075,8 +1076,9 @@ static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque)
         p->c = QIO_CHANNEL(sioc);
         qio_channel_set_delay(p->c, false);
         p->running = true;
+        /* TODO: let the further caller handle the error instead of abort() */
         qemu_thread_create(&p->thread, p->name, multifd_send_thread, p,
-                           QEMU_THREAD_JOINABLE);
+                           QEMU_THREAD_JOINABLE, &error_abort);
 
         atomic_inc(&multifd_send_state->count);
     }
@@ -1355,8 +1357,9 @@ bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
     p->num_packets = 1;
 
     p->running = true;
+    /* TODO: let the further caller handle the error instead of abort() here */
     qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p,
-                       QEMU_THREAD_JOINABLE);
+                       QEMU_THREAD_JOINABLE, &error_abort);
     atomic_inc(&multifd_recv_state->count);
     return atomic_read(&multifd_recv_state->count) ==
            migrate_multifd_channels();
@@ -3643,9 +3646,10 @@ static int compress_threads_load_setup(QEMUFile *f)
         qemu_cond_init(&decomp_param[i].cond);
         decomp_param[i].done = true;
         decomp_param[i].quit = false;
+        /* TODO: let the further caller handle the error instead of abort() */
         qemu_thread_create(decompress_threads + i, "decompress",
                            do_data_decompress, decomp_param + i,
-                           QEMU_THREAD_JOINABLE);
+                           QEMU_THREAD_JOINABLE, &error_abort);
     }
     return 0;
 exit:
migration/savevm.c +2 -1
index d784e8aa40..46ce7af239 100644
@@ -1747,9 +1747,10 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
     mis->have_listen_thread = true;
     /* Start up the listening thread and wait for it to signal ready */
     qemu_sem_init(&mis->listen_thread_sem, 0);
+    /* TODO: let the further caller handle the error instead of abort() here */
     qemu_thread_create(&mis->listen_thread, "postcopy/listen",
                        postcopy_ram_listen_thread, NULL,
-                       QEMU_THREAD_DETACHED);
+                       QEMU_THREAD_DETACHED, &error_abort);
     qemu_sem_wait(&mis->listen_thread_sem);
     qemu_sem_destroy(&mis->listen_thread_sem);
 
tests/atomic_add-bench.c +2 -1
index 2f6c72f63a..338b9563e3 100644
@@ -2,6 +2,7 @@
 #include "qemu/thread.h"
 #include "qemu/host-utils.h"
 #include "qemu/processor.h"
+#include "qapi/error.h"
 
 struct thread_info {
     uint64_t r;
@@ -110,7 +111,7 @@ static void create_threads(void)
 
         info->r = (i + 1) ^ time(NULL);
         qemu_thread_create(&threads[i], NULL, thread_func, info,
-                           QEMU_THREAD_JOINABLE);
+                           QEMU_THREAD_JOINABLE, &error_abort);
     }
 }
 
tests/iothread.c +1 -1
index 777d9eea46..f4ad992e61 100644
@@ -73,7 +73,7 @@ IOThread *iothread_new(void)
     qemu_mutex_init(&iothread->init_done_lock);
     qemu_cond_init(&iothread->init_done_cond);
     qemu_thread_create(&iothread->thread, NULL, iothread_run,
-                       iothread, QEMU_THREAD_JOINABLE);
+                       iothread, QEMU_THREAD_JOINABLE, &error_abort);
 
     /* Wait for initialization to complete */
     qemu_mutex_lock(&iothread->init_done_lock);
tests/qht-bench.c +2 -1
index ab4e708180..85e1af2f84 100644
@@ -10,6 +10,7 @@
 #include "qemu/qht.h"
 #include "qemu/rcu.h"
 #include "qemu/xxhash.h"
+#include "qapi/error.h"
 
 struct thread_stats {
     size_t rd;
@@ -248,7 +249,7 @@ th_create_n(QemuThread **threads, struct thread_info **infos, const char *name,
         prepare_thread_info(&info[i], offset + i);
         info[i].func = func;
         qemu_thread_create(&th[i], name, thread_func, &info[i],
-                           QEMU_THREAD_JOINABLE);
+                           QEMU_THREAD_JOINABLE, &error_abort);
     }
 }
 
tests/rcutorture.c +2 -1
index 49311c82ea..0e799ff256 100644
@@ -64,6 +64,7 @@
 #include "qemu/atomic.h"
 #include "qemu/rcu.h"
 #include "qemu/thread.h"
+#include "qapi/error.h"
 
 long long n_reads = 0LL;
 long n_updates = 0L;
@@ -90,7 +91,7 @@ static void create_thread(void *(*func)(void *))
         exit(-1);
     }
     qemu_thread_create(&threads[n_threads], "test", func, &data[n_threads],
-                       QEMU_THREAD_JOINABLE);
+                       QEMU_THREAD_JOINABLE, &error_abort);
     n_threads++;
 }
 
tests/test-aio.c +1 -1
index 86fb73b3d5..b3ac261724 100644
@@ -154,7 +154,7 @@ static void test_acquire(void)
 
     qemu_thread_create(&thread, "test_acquire_thread",
                        test_acquire_thread,
-                       &data, QEMU_THREAD_JOINABLE);
+                       &data, QEMU_THREAD_JOINABLE, &error_abort);
 
     /* Block in aio_poll(), let other thread kick us and acquire context */
     aio_context_acquire(ctx);
tests/test-rcu-list.c +2 -1
index 2e6f70bd59..0f7da81291 100644
@@ -25,6 +25,7 @@
 #include "qemu/rcu.h"
 #include "qemu/thread.h"
 #include "qemu/rcu_queue.h"
+#include "qapi/error.h"
 
 /*
  * Test variables.
@@ -68,7 +69,7 @@ static void create_thread(void *(*func)(void *))
         exit(-1);
     }
     qemu_thread_create(&threads[n_threads], "test", func, &data[n_threads],
-                       QEMU_THREAD_JOINABLE);
+                       QEMU_THREAD_JOINABLE, &error_abort);
     n_threads++;
 }
 
ui/vnc-jobs.c +4 -2
index 929391f85d..5712f1f501 100644
@@ -31,6 +31,7 @@
 #include "vnc-jobs.h"
 #include "qemu/sockets.h"
 #include "qemu/main-loop.h"
+#include "qapi/error.h"
 #include "block/aio.h"
 
 /*
@@ -339,7 +340,8 @@ void vnc_start_worker_thread(void)
         return ;
 
     q = vnc_queue_init();
-    qemu_thread_create(&q->thread, "vnc_worker", vnc_worker_thread, q,
-                       QEMU_THREAD_DETACHED);
+    /* TODO: let the further caller handle the error instead of abort() here */
+    qemu_thread_create(&q->thread, "vnc_worker", vnc_worker_thread,
+                       q, QEMU_THREAD_DETACHED, &error_abort);
     queue = q; /* Set global queue */
 }
util/compatfd.c +4 -2
index 980bd33e52..c3d8448264 100644
@@ -16,6 +16,7 @@
 #include "qemu/osdep.h"
 #include "qemu-common.h"
 #include "qemu/thread.h"
+#include "qapi/error.h"
 
 #include <sys/syscall.h>
 
@@ -88,8 +89,9 @@ static int qemu_signalfd_compat(const sigset_t *mask)
     memcpy(&info->mask, mask, sizeof(*mask));
     info->fd = fds[1];
 
-    qemu_thread_create(&thread, "signalfd_compat", sigwait_compat, info,
-                       QEMU_THREAD_DETACHED);
+    /* TODO: let the further caller handle the error instead of abort() here */
+    qemu_thread_create(&thread, "signalfd_compat", sigwait_compat,
+                       info, QEMU_THREAD_DETACHED, &error_abort);
 
     return fds[0];
 }
util/oslib-posix.c +2 -1
index c1bee2a581..251e2f1aea 100644
@@ -448,9 +448,10 @@ static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages,
         memset_thread[i].numpages = (i == (memset_num_threads - 1)) ?
                                     numpages : numpages_per_thread;
         memset_thread[i].hpagesize = hpagesize;
+        /* TODO: let the callers handle the error instead of abort() here */
         qemu_thread_create(&memset_thread[i].pgthread, "touch_pages",
                            do_touch_pages, &memset_thread[i],
-                           QEMU_THREAD_JOINABLE);
+                           QEMU_THREAD_JOINABLE, &error_abort);
         addr += size_per_thread;
         numpages -= numpages_per_thread;
     }
util/qemu-thread-posix.c +20 -7
index 865e476df5..39834b0551 100644
@@ -15,6 +15,7 @@
 #include "qemu/atomic.h"
 #include "qemu/notify.h"
 #include "qemu-thread-common.h"
+#include "qapi/error.h"
 
 static bool name_threads;
 
@@ -500,9 +501,13 @@ static void *qemu_thread_start(void *args)
     return r;
 }
 
-void qemu_thread_create(QemuThread *thread, const char *name,
-                       void *(*start_routine)(void*),
-                       void *arg, int mode)
+/*
+ * Return a boolean: true/false to indicate whether it succeeds.
+ * If fails, propagate the error to Error **errp and set the errno.
+ */
+bool qemu_thread_create(QemuThread *thread, const char *name,
+                        void *(*start_routine)(void *),
+                        void *arg, int mode, Error **errp)
 {
     sigset_t set, oldset;
     int err;
@@ -511,7 +516,9 @@ void qemu_thread_create(QemuThread *thread, const char *name,
 
     err = pthread_attr_init(&attr);
     if (err) {
-        error_exit(err, __func__);
+        errno = err;
+        error_setg_errno(errp, errno, "pthread_attr_init failed");
+        return false;
     }
 
     if (mode == QEMU_THREAD_DETACHED) {
@@ -529,13 +536,19 @@ void qemu_thread_create(QemuThread *thread, const char *name,
 
     err = pthread_create(&thread->thread, &attr,
                          qemu_thread_start, qemu_thread_args);
-
-    if (err)
-        error_exit(err, __func__);
+    if (err) {
+        errno = err;
+        error_setg_errno(errp, errno, "pthread_create failed");
+        pthread_attr_destroy(&attr);
+        g_free(qemu_thread_args->name);
+        g_free(qemu_thread_args);
+        return false;
+    }
 
     pthread_sigmask(SIG_SETMASK, &oldset, NULL);
 
     pthread_attr_destroy(&attr);
+    return true;
 }
 
 void qemu_thread_get_self(QemuThread *thread)
util/qemu-thread-win32.c +12 -4
index 4a363ca675..57b1143e97 100644
@@ -20,6 +20,7 @@
 #include "qemu/thread.h"
 #include "qemu/notify.h"
 #include "qemu-thread-common.h"
+#include "qapi/error.h"
 #include <process.h>
 
 static bool name_threads;
@@ -388,9 +389,9 @@ void *qemu_thread_join(QemuThread *thread)
     return ret;
 }
 
-void qemu_thread_create(QemuThread *thread, const char *name,
-                       void *(*start_routine)(void *),
-                       void *arg, int mode)
+bool qemu_thread_create(QemuThread *thread, const char *name,
+                        void *(*start_routine)(void *),
+                        void *arg, int mode, Error **errp)
 {
     HANDLE hThread;
     struct QemuThreadData *data;
@@ -409,10 +410,17 @@ void qemu_thread_create(QemuThread *thread, const char *name,
     hThread = (HANDLE) _beginthreadex(NULL, 0, win32_start_routine,
                                       data, 0, &thread->tid);
     if (!hThread) {
-        error_exit(GetLastError(), __func__);
+        if (data->mode != QEMU_THREAD_DETACHED) {
+            DeleteCriticalSection(&data->cs);
+        }
+        error_setg_errno(errp, errno,
+                         "failed to create win32_start_routine");
+        g_free(data);
+        return false;
     }
     CloseHandle(hThread);
     thread->data = data;
+    return true;
 }
 
 void qemu_thread_get_self(QemuThread *thread)
util/rcu.c +2 -1
index 5676c22bd1..145dcdb0c6 100644
@@ -32,6 +32,7 @@
 #include "qemu/atomic.h"
 #include "qemu/thread.h"
 #include "qemu/main-loop.h"
+#include "qapi/error.h"
 #if defined(CONFIG_MALLOC_TRIM)
 #include <malloc.h>
 #endif
@@ -325,7 +326,7 @@ static void rcu_init_complete(void)
      * must have been quiescent even after forking, just recreate it.
      */
     qemu_thread_create(&thread, "call_rcu", call_rcu_thread,
-                       NULL, QEMU_THREAD_DETACHED);
+                       NULL, QEMU_THREAD_DETACHED, &error_abort);
 
     rcu_register_thread();
 }
util/thread-pool.c +3 -1
index 610646d131..ad0f980783 100644
@@ -22,6 +22,7 @@
 #include "trace.h"
 #include "block/thread-pool.h"
 #include "qemu/main-loop.h"
+#include "qapi/error.h"
 
 static void do_spawn_thread(ThreadPool *pool);
 
@@ -132,7 +133,8 @@ static void do_spawn_thread(ThreadPool *pool)
     pool->new_threads--;
     pool->pending_threads++;
 
-    qemu_thread_create(&t, "worker", worker_thread, pool, QEMU_THREAD_DETACHED);
+    qemu_thread_create(&t, "worker", worker_thread, pool,
+                       QEMU_THREAD_DETACHED, &error_abort);
 }
 
 static void spawn_thread_bh_fn(void *opaque)
Signature · 1 line
2.13.7
#11 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 06/16] qemu_thread: Make qemu_thread_create() handle errors properly

Quoted 11 lines · Fei Li

qemu_thread_create() abort()s on error. Not nice. Give it a return value and an Error ** argument, so it can return success/failure.

Considering qemu_thread_create() is quite widely used in qemu, split this into two steps: this patch passes the &error_abort to qemu_thread_create() everywhere, and the next 9 patches will improve on &error_abort for callers who need.

Cc Markus Armbruster <[email protected]>
Cc Paolo Bonzini <[email protected]>

The commit message's title promises more than the patch delivers. Suggest:

qemu_thread: Make qemu_thread_create() take Error ** argument

The rest of the commit message is fine.

Quoted 43 lines
---
 cpus.c                      | 23 +++++++++++++++--------
 dump.c                      |  3 ++-
 hw/misc/edu.c               |  4 +++-
 hw/ppc/spapr_hcall.c        |  4 +++-
 hw/rdma/rdma_backend.c      |  3 ++-
 hw/usb/ccid-card-emulated.c |  5 +++--
 include/qemu/thread.h       |  4 ++--
 io/task.c                   |  3 ++-
 iothread.c                  |  3 ++-
 migration/migration.c       | 11 ++++++++---
 migration/postcopy-ram.c    |  4 +++-
 migration/ram.c             | 12 ++++++++----
 migration/savevm.c          |  3 ++-
 tests/atomic_add-bench.c    |  3 ++-
 tests/iothread.c            |  2 +-
 tests/qht-bench.c           |  3 ++-
 tests/rcutorture.c          |  3 ++-
 tests/test-aio.c            |  2 +-
 tests/test-rcu-list.c       |  3 ++-
 ui/vnc-jobs.c               |  6 ++++--
 util/compatfd.c             |  6 ++++--
 util/oslib-posix.c          |  3 ++-
 util/qemu-thread-posix.c    | 27 ++++++++++++++++++++-------
 util/qemu-thread-win32.c    | 16 ++++++++++++----
 util/rcu.c                  |  3 ++-
 util/thread-pool.c          |  4 +++-
 26 files changed, 112 insertions(+), 51 deletions(-)
cpus.c +2 -1
index 0ddeeefc14..25df03326b 100644
@@ -1961,15 +1961,17 @@ static void qemu_tcg_init_vcpu(CPUState *cpu)
             snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG",
                  cpu->cpu_index);
 
+            /* TODO: let the callers handle the error instead of abort() here */
             qemu_thread_create(cpu->thread, thread_name, qemu_tcg_cpu_thread_fn,
-                               cpu, QEMU_THREAD_JOINABLE);
+                               cpu, QEMU_THREAD_JOINABLE, &error_abort);
 
         } else {

cpus.c static void qemu_tcg_init_vcpu(CPUState *cpu) · line 1977

Quoted 10 lines
cpus.c +2 -1
             /* share a single thread for all cpus with TCG */
             snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "ALL CPUs/TCG");
+            /* TODO: let the callers handle the error instead of abort() here */
             qemu_thread_create(cpu->thread, thread_name,
                                qemu_tcg_rr_cpu_thread_fn,
-                               cpu, QEMU_THREAD_JOINABLE);
+                               cpu, QEMU_THREAD_JOINABLE, &error_abort);
 
             single_tcg_halt_cond = cpu->halt_cond;
             single_tcg_cpu_thread = cpu->thread;

You add this TODO comment to 24 out of 37 calls. Can you give your reasons for adding it to some calls, but not to others?

[...]

Quoted 16 lines
include/qemu/thread.h +2 -2
index 55d83a907c..12291f4ccd 100644
@@ -152,9 +152,9 @@ void qemu_event_reset(QemuEvent *ev);
 void qemu_event_wait(QemuEvent *ev);
 void qemu_event_destroy(QemuEvent *ev);
 
-void qemu_thread_create(QemuThread *thread, const char *name,
+bool qemu_thread_create(QemuThread *thread, const char *name,
                         void *(*start_routine)(void *),
-                        void *arg, int mode);
+                        void *arg, int mode, Error **errp);
 void *qemu_thread_join(QemuThread *thread);
 void qemu_thread_get_self(QemuThread *thread);
 bool qemu_thread_is_self(QemuThread *thread);

[...]

Quoted 13 lines
util/qemu-thread-posix.c +1 -0
index 865e476df5..39834b0551 100644
@@ -15,6 +15,7 @@
 #include "qemu/atomic.h"
 #include "qemu/notify.h"
 #include "qemu-thread-common.h"
+#include "qapi/error.h"
 
 static bool name_threads;
 

util/qemu-thread-posix.c static void *qemu_thread_start(void *args) · line 507

Quoted 10 lines
util/qemu-thread-posix.c +4 -3
@@ -500,9 +501,13 @@ static void *qemu_thread_start(void *args)
     return r;
 }
 
-void qemu_thread_create(QemuThread *thread, const char *name,
-                       void *(*start_routine)(void*),
-                       void *arg, int mode)
+/*
+ * Return a boolean: true/false to indicate whether it succeeds.
+ * If fails, propagate the error to Error **errp and set the errno.
+ */

Let's write something that can pass as a function contract:

/*
* Create a new thread with name @name
* The thread executes @start_routine() with argument @arg.
* The thread will be created in a detached state if @mode is
* QEMU_THREAD_DETACHED, and in a jounable state if it's
* QEMU_THREAD_JOINABLE.
* On success, return true.
* On failure, set @errno, store an error through @errp and return
* false.
*/

Personally, I'd return negative errno instead of false, and dispense with setting errno.

Quoted 84 lines
util/qemu-thread-posix.c +15 -4
+bool qemu_thread_create(QemuThread *thread, const char *name,
+                        void *(*start_routine)(void *),
+                        void *arg, int mode, Error **errp)
 {
     sigset_t set, oldset;
     int err;
@@ -511,7 +516,9 @@ void qemu_thread_create(QemuThread *thread, const char *name,
 
     err = pthread_attr_init(&attr);
     if (err) {
-        error_exit(err, __func__);
+        errno = err;
+        error_setg_errno(errp, errno, "pthread_attr_init failed");
+        return false;
     }
 
     if (mode == QEMU_THREAD_DETACHED) {
@@ -529,13 +536,19 @@ void qemu_thread_create(QemuThread *thread, const char *name,
 
     err = pthread_create(&thread->thread, &attr,
                          qemu_thread_start, qemu_thread_args);
-
-    if (err)
-        error_exit(err, __func__);
+    if (err) {
+        errno = err;
+        error_setg_errno(errp, errno, "pthread_create failed");
+        pthread_attr_destroy(&attr);
+        g_free(qemu_thread_args->name);
+        g_free(qemu_thread_args);
+        return false;
+    }
 
     pthread_sigmask(SIG_SETMASK, &oldset, NULL);
 
     pthread_attr_destroy(&attr);
+    return true;
 }
 
 void qemu_thread_get_self(QemuThread *thread)
util/qemu-thread-win32.c +12 -4
index 4a363ca675..57b1143e97 100644
@@ -20,6 +20,7 @@
 #include "qemu/thread.h"
 #include "qemu/notify.h"
 #include "qemu-thread-common.h"
+#include "qapi/error.h"
 #include <process.h>
 
 static bool name_threads;
@@ -388,9 +389,9 @@ void *qemu_thread_join(QemuThread *thread)
     return ret;
 }
 
-void qemu_thread_create(QemuThread *thread, const char *name,
-                       void *(*start_routine)(void *),
-                       void *arg, int mode)
+bool qemu_thread_create(QemuThread *thread, const char *name,
+                        void *(*start_routine)(void *),
+                        void *arg, int mode, Error **errp)
 {
     HANDLE hThread;
     struct QemuThreadData *data;
@@ -409,10 +410,17 @@ void qemu_thread_create(QemuThread *thread, const char *name,
     hThread = (HANDLE) _beginthreadex(NULL, 0, win32_start_routine,
                                       data, 0, &thread->tid);
     if (!hThread) {
-        error_exit(GetLastError(), __func__);
+        if (data->mode != QEMU_THREAD_DETACHED) {
+            DeleteCriticalSection(&data->cs);
+        }
+        error_setg_errno(errp, errno,
+                         "failed to create win32_start_routine");
+        g_free(data);
+        return false;
     }
     CloseHandle(hThread);
     thread->data = data;
+    return true;
 }
 
 void qemu_thread_get_self(QemuThread *thread)

[...]

#12 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 06/16] qemu_thread: Make qemu_thread_create() handle errors properly

Quoted 14 lines · 在 2019年1月8日,01:18,Markus Armbruster <[email protected]> 写道:
Quoted 8 lines · Fei Li

qemu_thread_create() abort()s on error. Not nice. Give it a return value and an Error ** argument, so it can return success/failure.

Considering qemu_thread_create() is quite widely used in qemu, split this into two steps: this patch passes the &error_abort to qemu_thread_create() everywhere, and the next 9 patches will improve on &error_abort for callers who need.

Quoted 8 lines
Quoted 3 lines
Cc Markus Armbruster <[email protected]>
Cc Paolo Bonzini <[email protected]>

The commit message's title promises more than the patch delivers. Suggest:

qemu_thread: Make qemu_thread_create() take Error ** argument

Ok, thanks for the suggestion. :)

Quoted 54 lines

The rest of the commit message is fine.

Quoted 51 lines

---

cpus.c                      | 23 +++++++++++++++--------
dump.c                      |  3 ++-
hw/misc/edu.c               |  4 +++-
hw/ppc/spapr_hcall.c        |  4 +++-
hw/rdma/rdma_backend.c      |  3 ++-
hw/usb/ccid-card-emulated.c |  5 +++--
include/qemu/thread.h       |  4 ++--
io/task.c                   |  3 ++-
iothread.c                  |  3 ++-
migration/migration.c       | 11 ++++++++---
migration/postcopy-ram.c    |  4 +++-
migration/ram.c             | 12 ++++++++----
migration/savevm.c          |  3 ++-
tests/atomic_add-bench.c    |  3 ++-
tests/iothread.c            |  2 +-
tests/qht-bench.c           |  3 ++-
tests/rcutorture.c          |  3 ++-
tests/test-aio.c            |  2 +-
tests/test-rcu-list.c       |  3 ++-
ui/vnc-jobs.c               |  6 ++++--
util/compatfd.c             |  6 ++++--
util/oslib-posix.c          |  3 ++-
util/qemu-thread-posix.c    | 27 ++++++++++++++++++++-------
util/qemu-thread-win32.c    | 16 ++++++++++++----
util/rcu.c                  |  3 ++-
util/thread-pool.c          |  4 +++-
26 files changed, 112 insertions(+), 51 deletions(-)
cpus.c +4 -2
index 0ddeeefc14..25df03326b 100644
@@ -1961,15 +1961,17 @@ static void qemu_tcg_init_vcpu(CPUState *cpu)
            snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG",
                 cpu->cpu_index);
 
+            /* TODO: let the callers handle the error instead of abort() here */
            qemu_thread_create(cpu->thread, thread_name, qemu_tcg_cpu_thread_fn,
-                               cpu, QEMU_THREAD_JOINABLE);
+                               cpu, QEMU_THREAD_JOINABLE, &error_abort);
 
        } else {
            /* share a single thread for all cpus with TCG */
            snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "ALL CPUs/TCG");
+            /* TODO: let the callers handle the error instead of abort() here */
            qemu_thread_create(cpu->thread, thread_name,
                               qemu_tcg_rr_cpu_thread_fn,
-                               cpu, QEMU_THREAD_JOINABLE);
+                               cpu, QEMU_THREAD_JOINABLE, &error_abort);
 
Quoted 5 lines

cpus.c static void qemu_tcg_init_vcpu(CPUState *cpu) · line 1977

Quoted 2 lines
cpus.c +0 -0
            single_tcg_halt_cond = cpu->halt_cond;
            single_tcg_cpu_thread = cpu->thread;

You add this TODO comment to 24 out of 37 calls. Can you give your reasons for adding it to some calls, but not to others?

For those have TODO, I polish them in the next following patches, and for those do not have TODO I just let them use &error_abort.

Quoted 45 lines

[...]

Quoted 16 lines
include/qemu/thread.h +2 -2
index 55d83a907c..12291f4ccd 100644
@@ -152,9 +152,9 @@ void qemu_event_reset(QemuEvent *ev);
 void qemu_event_wait(QemuEvent *ev);
 void qemu_event_destroy(QemuEvent *ev);
 
-void qemu_thread_create(QemuThread *thread, const char *name,
+bool qemu_thread_create(QemuThread *thread, const char *name,
                        void *(*start_routine)(void *),
-                        void *arg, int mode);
+                        void *arg, int mode, Error **errp);
 void *qemu_thread_join(QemuThread *thread);
 void qemu_thread_get_self(QemuThread *thread);
 bool qemu_thread_is_self(QemuThread *thread);

[...]

Quoted 13 lines
util/qemu-thread-posix.c +1 -0
index 865e476df5..39834b0551 100644
@@ -15,6 +15,7 @@
 #include "qemu/atomic.h"
 #include "qemu/notify.h"
 #include "qemu-thread-common.h"
+#include "qapi/error.h"
 
 static bool name_threads;
 

util/qemu-thread-posix.c static void *qemu_thread_start(void *args) · line 507

Quoted 10 lines
util/qemu-thread-posix.c +4 -3
@@ -500,9 +501,13 @@ static void *qemu_thread_start(void *args)
    return r;
 }
 
-void qemu_thread_create(QemuThread *thread, const char *name,
-                       void *(*start_routine)(void*),
-                       void *arg, int mode)
+/*
+ * Return a boolean: true/false to indicate whether it succeeds.
+ * If fails, propagate the error to Error **errp and set the errno.
+ */

Let's write something that can pass as a function contract:

Quoted 10 lines

/*
* Create a new thread with name @name
* The thread executes @start_routine() with argument @arg.
* The thread will be created in a detached state if @mode is
* QEMU_THREAD_DETACHED, and in a jounable state if it's
* QEMU_THREAD_JOINABLE.
* On success, return true.
* On failure, set @errno, store an error through @errp and return
* false.
*/

Thanks so much for amending! :)

Quoted 2 lines

Personally, I'd return negative errno instead of false, and dispense with setting errno.

Emm, I think I have replied this in last version, but due to several reasons I did not wait for your feedback and sent the v9. Sorry for that.. And I like to paste my two considerations here:
“- Actually only one caller needs the errno, that is the above qemu_signalfd_compat().
- For the returning value, I remember there's once a email thread talking about it: returning a bool (and let the passed errp hold the error message) is to keep the consistency with glib.
”
So IMO I am wondering whether it is really needed to change the bool (true/false) to int (0/-errno), just for that sole function: qemu_signalfd_compat() which needs the errno. Besides if we return -errno, for each caller we need add a local variable like “ret= qemu_thread_create()” to store the -errno.

Have a nice day, thanks
Fei

Quoted 86 lines
Quoted 85 lines
util/qemu-thread-posix.c +15 -4
+bool qemu_thread_create(QemuThread *thread, const char *name,
+                        void *(*start_routine)(void *),
+                        void *arg, int mode, Error **errp)
 {
    sigset_t set, oldset;
    int err;
@@ -511,7 +516,9 @@ void qemu_thread_create(QemuThread *thread, const char *name,
 
    err = pthread_attr_init(&attr);
    if (err) {
-        error_exit(err, __func__);
+        errno = err;
+        error_setg_errno(errp, errno, "pthread_attr_init failed");
+        return false;
    }
 
    if (mode == QEMU_THREAD_DETACHED) {
@@ -529,13 +536,19 @@ void qemu_thread_create(QemuThread *thread, const char *name,
 
    err = pthread_create(&thread->thread, &attr,
                         qemu_thread_start, qemu_thread_args);
-
-    if (err)
-        error_exit(err, __func__);
+    if (err) {
+        errno = err;
+        error_setg_errno(errp, errno, "pthread_create failed");
+        pthread_attr_destroy(&attr);
+        g_free(qemu_thread_args->name);
+        g_free(qemu_thread_args);
+        return false;
+    }
 
    pthread_sigmask(SIG_SETMASK, &oldset, NULL);
 
    pthread_attr_destroy(&attr);
+    return true;
 }
 
 void qemu_thread_get_self(QemuThread *thread)
util/qemu-thread-win32.c +12 -4
index 4a363ca675..57b1143e97 100644
@@ -20,6 +20,7 @@
 #include "qemu/thread.h"
 #include "qemu/notify.h"
 #include "qemu-thread-common.h"
+#include "qapi/error.h"
 #include <process.h>
 
 static bool name_threads;
@@ -388,9 +389,9 @@ void *qemu_thread_join(QemuThread *thread)
    return ret;
 }
 
-void qemu_thread_create(QemuThread *thread, const char *name,
-                       void *(*start_routine)(void *),
-                       void *arg, int mode)
+bool qemu_thread_create(QemuThread *thread, const char *name,
+                        void *(*start_routine)(void *),
+                        void *arg, int mode, Error **errp)
 {
    HANDLE hThread;
    struct QemuThreadData *data;
@@ -409,10 +410,17 @@ void qemu_thread_create(QemuThread *thread, const char *name,
    hThread = (HANDLE) _beginthreadex(NULL, 0, win32_start_routine,
                                      data, 0, &thread->tid);
    if (!hThread) {
-        error_exit(GetLastError(), __func__);
+        if (data->mode != QEMU_THREAD_DETACHED) {
+            DeleteCriticalSection(&data->cs);
+        }
+        error_setg_errno(errp, errno,
+                         "failed to create win32_start_routine");
+        g_free(data);
+        return false;
    }
    CloseHandle(hThread);
    thread->data = data;
+    return true;
 }
 
 void qemu_thread_get_self(QemuThread *thread)

[...]

#13 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 06/16] qemu_thread: Make qemu_thread_create() handle errors properly

Quoted 75 lines · fei
Quoted 12 lines · 在 2019年1月8日,01:18,Markus Armbruster <[email protected]> 写道:
Quoted 8 lines · Fei Li

qemu_thread_create() abort()s on error. Not nice. Give it a return value and an Error ** argument, so it can return success/failure.

Considering qemu_thread_create() is quite widely used in qemu, split this into two steps: this patch passes the &error_abort to qemu_thread_create() everywhere, and the next 9 patches will improve on &error_abort for callers who need.

Quoted 8 lines
Quoted 3 lines
Cc Markus Armbruster <[email protected]>
Cc Paolo Bonzini <[email protected]>

The commit message's title promises more than the patch delivers. Suggest:

qemu_thread: Make qemu_thread_create() take Error ** argument

Ok, thanks for the suggestion. :)

Quoted 54 lines

The rest of the commit message is fine.

Quoted 51 lines

---

cpus.c                      | 23 +++++++++++++++--------
dump.c                      |  3 ++-
hw/misc/edu.c               |  4 +++-
hw/ppc/spapr_hcall.c        |  4 +++-
hw/rdma/rdma_backend.c      |  3 ++-
hw/usb/ccid-card-emulated.c |  5 +++--
include/qemu/thread.h       |  4 ++--
io/task.c                   |  3 ++-
iothread.c                  |  3 ++-
migration/migration.c       | 11 ++++++++---
migration/postcopy-ram.c    |  4 +++-
migration/ram.c             | 12 ++++++++----
migration/savevm.c          |  3 ++-
tests/atomic_add-bench.c    |  3 ++-
tests/iothread.c            |  2 +-
tests/qht-bench.c           |  3 ++-
tests/rcutorture.c          |  3 ++-
tests/test-aio.c            |  2 +-
tests/test-rcu-list.c       |  3 ++-
ui/vnc-jobs.c               |  6 ++++--
util/compatfd.c             |  6 ++++--
util/oslib-posix.c          |  3 ++-
util/qemu-thread-posix.c    | 27 ++++++++++++++++++++-------
util/qemu-thread-win32.c    | 16 ++++++++++++----
util/rcu.c                  |  3 ++-
util/thread-pool.c          |  4 +++-
26 files changed, 112 insertions(+), 51 deletions(-)
cpus.c +4 -2
index 0ddeeefc14..25df03326b 100644
@@ -1961,15 +1961,17 @@ static void qemu_tcg_init_vcpu(CPUState *cpu)
            snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG",
                 cpu->cpu_index);
 
+            /* TODO: let the callers handle the error instead of abort() here */
            qemu_thread_create(cpu->thread, thread_name, qemu_tcg_cpu_thread_fn,
-                               cpu, QEMU_THREAD_JOINABLE);
+                               cpu, QEMU_THREAD_JOINABLE, &error_abort);
 
        } else {
            /* share a single thread for all cpus with TCG */
            snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "ALL CPUs/TCG");
+            /* TODO: let the callers handle the error instead of abort() here */
            qemu_thread_create(cpu->thread, thread_name,
                               qemu_tcg_rr_cpu_thread_fn,
-                               cpu, QEMU_THREAD_JOINABLE);
+                               cpu, QEMU_THREAD_JOINABLE, &error_abort);
 
Quoted 6 lines
Quoted 5 lines

cpus.c static void qemu_tcg_init_vcpu(CPUState *cpu) · line 1977

Quoted 2 lines
cpus.c +0 -0
            single_tcg_halt_cond = cpu->halt_cond;
            single_tcg_cpu_thread = cpu->thread;

You add this TODO comment to 24 out of 37 calls. Can you give your reasons for adding it to some calls, but not to others?

For those have TODO, I polish them in the next following patches, and for those do not have TODO I just let them use &error_abort.

Please mention that in the commit message.

Quoted 55 lines
Quoted 45 lines

[...]

Quoted 16 lines
include/qemu/thread.h +2 -2
index 55d83a907c..12291f4ccd 100644
@@ -152,9 +152,9 @@ void qemu_event_reset(QemuEvent *ev);
 void qemu_event_wait(QemuEvent *ev);
 void qemu_event_destroy(QemuEvent *ev);
 
-void qemu_thread_create(QemuThread *thread, const char *name,
+bool qemu_thread_create(QemuThread *thread, const char *name,
                        void *(*start_routine)(void *),
-                        void *arg, int mode);
+                        void *arg, int mode, Error **errp);
 void *qemu_thread_join(QemuThread *thread);
 void qemu_thread_get_self(QemuThread *thread);
 bool qemu_thread_is_self(QemuThread *thread);

[...]

Quoted 13 lines
util/qemu-thread-posix.c +1 -0
index 865e476df5..39834b0551 100644
@@ -15,6 +15,7 @@
 #include "qemu/atomic.h"
 #include "qemu/notify.h"
 #include "qemu-thread-common.h"
+#include "qapi/error.h"
 
 static bool name_threads;
 

util/qemu-thread-posix.c static void *qemu_thread_start(void *args) · line 507

Quoted 10 lines
util/qemu-thread-posix.c +4 -3
@@ -500,9 +501,13 @@ static void *qemu_thread_start(void *args)
    return r;
 }
 
-void qemu_thread_create(QemuThread *thread, const char *name,
-                       void *(*start_routine)(void*),
-                       void *arg, int mode)
+/*
+ * Return a boolean: true/false to indicate whether it succeeds.
+ * If fails, propagate the error to Error **errp and set the errno.
+ */

Let's write something that can pass as a function contract:

Quoted 10 lines

/*
* Create a new thread with name @name
* The thread executes @start_routine() with argument @arg.
* The thread will be created in a detached state if @mode is
* QEMU_THREAD_DETACHED, and in a jounable state if it's
* QEMU_THREAD_JOINABLE.
* On success, return true.
* On failure, set @errno, store an error through @errp and return
* false.
*/

Quoted 5 lines

Thanks so much for amending! :)

Quoted 2 lines

Personally, I'd return negative errno instead of false, and dispense with setting errno.

Emm, I think I have replied this in last version, but due to several reasons I did not wait for your feedback and sent the v9. Sorry for that.. And I like to paste my two considerations here:
“- Actually only one caller needs the errno, that is the above qemu_signalfd_compat().

Yes.

Quoted 1 line

- For the returning value, I remember there's once a email thread talking about it: returning a bool (and let the passed errp hold the error message) is to keep the consistency with glib.

GLib doesn't discourage return types other than boolean. It only asks that if you return boolean, then true should mean success and false should mean failure. See

https://developer.gnome.org/glib/stable/glib-Error-Reporting.html

under "Rules for use of GError", item "By convention, if you return a boolean value".

The discussion you remember was about a convention we used to enforce in QEMU, namely to avoid returning boolean success, and return void instead. That was basically a bad idea.

Quoted 1 line

So IMO I am wondering whether it is really needed to change the bool (true/false) to int (0/-errno), just for that sole function: qemu_signalfd_compat() which needs the errno. Besides if we return -errno, for each caller we need add a local variable like “ret= qemu_thread_create()” to store the -errno.

Well, you either assign the error code to errno just for that caller, or you return the error code just for that caller. I'd do the latter because I consider it slightly simpler. Compare

* On success, return true.
* On failure, set @errno, store an error through @errp and return
* false.

to

* On success, return zero.
* On failure, store an error through @errp and return negative errno.

where the second sentence describes just two instead of three actions.

[...]

#14 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 06/16] qemu_thread: Make qemu_thread_create() handle errors properly

Quoted 75 lines · Markus Armbruster
Quoted 73 lines · fei
Quoted 12 lines · 在 2019年1月8日,01:18,Markus Armbruster <[email protected]> 写道:
Quoted 8 lines · Fei Li

qemu_thread_create() abort()s on error. Not nice. Give it a return value and an Error ** argument, so it can return success/failure.

Considering qemu_thread_create() is quite widely used in qemu, split this into two steps: this patch passes the &error_abort to qemu_thread_create() everywhere, and the next 9 patches will improve on &error_abort for callers who need.

Quoted 7 lines
Quoted 3 lines
Cc Markus Armbruster <[email protected]>
Cc Paolo Bonzini <[email protected]>

The commit message's title promises more than the patch delivers. Suggest:

qemu_thread: Make qemu_thread_create() take Error ** argument

Ok, thanks for the suggestion. :)

Quoted 53 lines

The rest of the commit message is fine.

Quoted 51 lines

---

cpus.c                      | 23 +++++++++++++++--------
dump.c                      |  3 ++-
hw/misc/edu.c               |  4 +++-
hw/ppc/spapr_hcall.c        |  4 +++-
hw/rdma/rdma_backend.c      |  3 ++-
hw/usb/ccid-card-emulated.c |  5 +++--
include/qemu/thread.h       |  4 ++--
io/task.c                   |  3 ++-
iothread.c                  |  3 ++-
migration/migration.c       | 11 ++++++++---
migration/postcopy-ram.c    |  4 +++-
migration/ram.c             | 12 ++++++++----
migration/savevm.c          |  3 ++-
tests/atomic_add-bench.c    |  3 ++-
tests/iothread.c            |  2 +-
tests/qht-bench.c           |  3 ++-
tests/rcutorture.c          |  3 ++-
tests/test-aio.c            |  2 +-
tests/test-rcu-list.c       |  3 ++-
ui/vnc-jobs.c               |  6 ++++--
util/compatfd.c             |  6 ++++--
util/oslib-posix.c          |  3 ++-
util/qemu-thread-posix.c    | 27 ++++++++++++++++++++-------
util/qemu-thread-win32.c    | 16 ++++++++++++----
util/rcu.c                  |  3 ++-
util/thread-pool.c          |  4 +++-
26 files changed, 112 insertions(+), 51 deletions(-)
cpus.c +4 -2
index 0ddeeefc14..25df03326b 100644
@@ -1961,15 +1961,17 @@ static void qemu_tcg_init_vcpu(CPUState *cpu)
             snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG",
                  cpu->cpu_index);
 
+            /* TODO: let the callers handle the error instead of abort() here */
             qemu_thread_create(cpu->thread, thread_name, qemu_tcg_cpu_thread_fn,
-                               cpu, QEMU_THREAD_JOINABLE);
+                               cpu, QEMU_THREAD_JOINABLE, &error_abort);
 
         } else {
             /* share a single thread for all cpus with TCG */
             snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "ALL CPUs/TCG");
+            /* TODO: let the callers handle the error instead of abort() here */
             qemu_thread_create(cpu->thread, thread_name,
                                qemu_tcg_rr_cpu_thread_fn,
-                               cpu, QEMU_THREAD_JOINABLE);
+                               cpu, QEMU_THREAD_JOINABLE, &error_abort);
 
Quoted 6 lines
Quoted 5 lines
Quoted 4 lines

cpus.c static void qemu_tcg_init_vcpu(CPUState *cpu) · line 1977

Quoted 2 lines
cpus.c +0 -0
             single_tcg_halt_cond = cpu->halt_cond;
             single_tcg_cpu_thread = cpu->thread;

You add this TODO comment to 24 out of 37 calls. Can you give your reasons for adding it to some calls, but not to others?

For those have TODO, I polish them in the next following patches, and for those do not have TODO I just let them use &error_abort.

Please mention that in the commit message.

ok.

Quoted 84 lines
Quoted 54 lines
Quoted 44 lines

[...]

Quoted 16 lines
include/qemu/thread.h +2 -2
index 55d83a907c..12291f4ccd 100644
@@ -152,9 +152,9 @@ void qemu_event_reset(QemuEvent *ev);
 void qemu_event_wait(QemuEvent *ev);
 void qemu_event_destroy(QemuEvent *ev);
 
-void qemu_thread_create(QemuThread *thread, const char *name,
+bool qemu_thread_create(QemuThread *thread, const char *name,
                         void *(*start_routine)(void *),
-                        void *arg, int mode);
+                        void *arg, int mode, Error **errp);
 void *qemu_thread_join(QemuThread *thread);
 void qemu_thread_get_self(QemuThread *thread);
 bool qemu_thread_is_self(QemuThread *thread);

[...]

Quoted 13 lines
util/qemu-thread-posix.c +1 -0
index 865e476df5..39834b0551 100644
@@ -15,6 +15,7 @@
 #include "qemu/atomic.h"
 #include "qemu/notify.h"
 #include "qemu-thread-common.h"
+#include "qapi/error.h"
 
 static bool name_threads;
 

util/qemu-thread-posix.c static void *qemu_thread_start(void *args) · line 507

Quoted 10 lines
util/qemu-thread-posix.c +4 -3
@@ -500,9 +501,13 @@ static void *qemu_thread_start(void *args)
     return r;
 }
 
-void qemu_thread_create(QemuThread *thread, const char *name,
-                       void *(*start_routine)(void*),
-                       void *arg, int mode)
+/*
+ * Return a boolean: true/false to indicate whether it succeeds.
+ * If fails, propagate the error to Error **errp and set the errno.
+ */

Let's write something that can pass as a function contract:

Quoted 10 lines

/*
* Create a new thread with name @name
* The thread executes @start_routine() with argument @arg.
* The thread will be created in a detached state if @mode is
* QEMU_THREAD_DETACHED, and in a jounable state if it's
* QEMU_THREAD_JOINABLE.
* On success, return true.
* On failure, set @errno, store an error through @errp and return
* false.
*/

Quoted 5 lines

Thanks so much for amending! :)

Quoted 2 lines

Personally, I'd return negative errno instead of false, and dispense with setting errno.

Emm, I think I have replied this in last version, but due to several reasons I did not wait for your feedback and sent the v9. Sorry for that.. And I like to paste my two considerations here:
“- Actually only one caller needs the errno, that is the above qemu_signalfd_compat().

Yes.

Quoted 1 line

- For the returning value, I remember there's once a email thread talking about it: returning a bool (and let the passed errp hold the error message) is to keep the consistency with glib.

GLib doesn't discourage return types other than boolean. It only asks that if you return boolean, then true should mean success and false should mean failure. See

https://developer.gnome.org/glib/stable/glib-Error-Reporting.html

under "Rules for use of GError", item "By convention, if you return a boolean value".

The discussion you remember was about a convention we used to enforce in QEMU, namely to avoid returning boolean success, and return void instead. That was basically a bad idea.

Quoted 1 line

So IMO I am wondering whether it is really needed to change the bool (true/false) to int (0/-errno), just for that sole function: qemu_signalfd_compat() which needs the errno. Besides if we return -errno, for each caller we need add a local variable like “ret= qemu_thread_create()” to store the -errno.

Well, you either assign the error code to errno just for that caller, or you return the error code just for that caller. I'd do the latter because I consider it slightly simpler. Compare

* On success, return true.
* On failure, set @errno, store an error through @errp and return
* false.

Quoted 8 lines

to

* On success, return zero.
* On failure, store an error through @errp and return negative errno.

where the second sentence describes just two instead of three actions.

[...]

Ok, decribing two actions than three is indeed simpler. But I still have one uncertain:
for those callers do not need the errno value, could we just check the return value to see whether it is negative, but not cache the unused return value? I mean

In the caller:

{...
    if (qemu_thread_create() < 0) {// do some cleanup} ...}

instead of

{    int ret;
...
    ret = qemu_thread_create();
    if (ret < 0) { //do some cleanup }

...}

As the first one can lessen quite a lot of codes. :)

Have a nice day, thanks

Fei

#15 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 06/16] qemu_thread: Make qemu_thread_create() handle errors properly

Quoted 6 lines · Fei Li
Quoted 5 lines · Markus Armbruster
Quoted 3 lines · fei
Quoted 3 lines · 在 2019年1月8日,01:18,Markus Armbruster <[email protected]> 写道:

Fei Li <[email protected]> writes:

[...]

Quoted 79 lines
Quoted 65 lines
Quoted 35 lines
Quoted 25 lines
Quoted 13 lines
util/qemu-thread-posix.c +1 -0
index 865e476df5..39834b0551 100644
@@ -15,6 +15,7 @@
 #include "qemu/atomic.h"
 #include "qemu/notify.h"
 #include "qemu-thread-common.h"
+#include "qapi/error.h"
 
 static bool name_threads;
 

util/qemu-thread-posix.c static void *qemu_thread_start(void *args) · line 507

Quoted 10 lines
util/qemu-thread-posix.c +4 -3
@@ -500,9 +501,13 @@ static void *qemu_thread_start(void *args)
     return r;
 }
 
-void qemu_thread_create(QemuThread *thread, const char *name,
-                       void *(*start_routine)(void*),
-                       void *arg, int mode)
+/*
+ * Return a boolean: true/false to indicate whether it succeeds.
+ * If fails, propagate the error to Error **errp and set the errno.
+ */

Let's write something that can pass as a function contract:

Quoted 10 lines

/*
* Create a new thread with name @name
* The thread executes @start_routine() with argument @arg.
* The thread will be created in a detached state if @mode is
* QEMU_THREAD_DETACHED, and in a jounable state if it's
* QEMU_THREAD_JOINABLE.
* On success, return true.
* On failure, set @errno, store an error through @errp and return
* false.
*/

Quoted 5 lines

Thanks so much for amending! :)

Quoted 2 lines

Personally, I'd return negative errno instead of false, and dispense with setting errno.

Emm, I think I have replied this in last version, but due to several reasons I did not wait for your feedback and sent the v9. Sorry for that.. And I like to paste my two considerations here:
“- Actually only one caller needs the errno, that is the above qemu_signalfd_compat().

Yes.

Quoted 1 line

- For the returning value, I remember there's once a email thread talking about it: returning a bool (and let the passed errp hold the error message) is to keep the consistency with glib.

GLib doesn't discourage return types other than boolean. It only asks that if you return boolean, then true should mean success and false should mean failure. See

https://developer.gnome.org/glib/stable/glib-Error-Reporting.html

under "Rules for use of GError", item "By convention, if you return a boolean value".

The discussion you remember was about a convention we used to enforce in QEMU, namely to avoid returning boolean success, and return void instead. That was basically a bad idea.

Quoted 1 line

So IMO I am wondering whether it is really needed to change the bool (true/false) to int (0/-errno), just for that sole function: qemu_signalfd_compat() which needs the errno. Besides if we return -errno, for each caller we need add a local variable like “ret= qemu_thread_create()” to store the -errno.

Well, you either assign the error code to errno just for that caller, or you return the error code just for that caller. I'd do the latter because I consider it slightly simpler. Compare

* On success, return true.
* On failure, set @errno, store an error through @errp and return
* false.

Quoted 8 lines

to

* On success, return zero.
* On failure, store an error through @errp and return negative errno.

where the second sentence describes just two instead of three actions.

[...]

Ok, decribing two actions than three is indeed simpler. But I still have one uncertain:
for those callers do not need the errno value, could we just check the return value
to see whether it is negative, but not cache the unused return value? I mean

Quoted 5 lines

In the caller:

{...
    if (qemu_thread_create() < 0) {// do some cleanup} ...}

This is just fine when you handle all errors the same.

Quoted 8 lines

instead of

{    int ret;
...
    ret = qemu_thread_create();
    if (ret < 0) { //do some cleanup }

...}

I'd object to this one unless the value of @ret gets used elsewhere.

Quoted 5 lines

As the first one can lessen quite a lot of codes. :)

Have a nice day, thanks

Fei

#16 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 06/16] qemu_thread: Make qemu_thread_create() handle errors properly

Quoted 102 lines · 在 2019年1月9日,22:36,Markus Armbruster <[email protected]> 写道:
Quoted 6 lines · Fei Li
Quoted 6 lines · Markus Armbruster
Quoted 3 lines · fei
Quoted 3 lines · 在 2019年1月8日,01:18,Markus Armbruster <[email protected]> 写道:

Fei Li <[email protected]> writes:

[...]

Quoted 79 lines
Quoted 65 lines
Quoted 35 lines
Quoted 25 lines
Quoted 13 lines
util/qemu-thread-posix.c +1 -0
index 865e476df5..39834b0551 100644
@@ -15,6 +15,7 @@
 #include "qemu/atomic.h"
 #include "qemu/notify.h"
 #include "qemu-thread-common.h"
+#include "qapi/error.h"
 
 static bool name_threads;
 

util/qemu-thread-posix.c static void *qemu_thread_start(void *args) · line 507

Quoted 10 lines
util/qemu-thread-posix.c +4 -3
@@ -500,9 +501,13 @@ static void *qemu_thread_start(void *args)
    return r;
 }
 
-void qemu_thread_create(QemuThread *thread, const char *name,
-                       void *(*start_routine)(void*),
-                       void *arg, int mode)
+/*
+ * Return a boolean: true/false to indicate whether it succeeds.
+ * If fails, propagate the error to Error **errp and set the errno.
+ */

Let's write something that can pass as a function contract:

Quoted 10 lines

/*
* Create a new thread with name @name
* The thread executes @start_routine() with argument @arg.
* The thread will be created in a detached state if @mode is
* QEMU_THREAD_DETACHED, and in a jounable state if it's
* QEMU_THREAD_JOINABLE.
* On success, return true.
* On failure, set @errno, store an error through @errp and return
* false.
*/

Quoted 5 lines

Thanks so much for amending! :)

Quoted 2 lines

Personally, I'd return negative errno instead of false, and dispense with setting errno.

Emm, I think I have replied this in last version, but due to several reasons I did not wait for your feedback and sent the v9. Sorry for that.. And I like to paste my two considerations here:
“- Actually only one caller needs the errno, that is the above qemu_signalfd_compat().

Yes.

Quoted 1 line

- For the returning value, I remember there's once a email thread talking about it: returning a bool (and let the passed errp hold the error message) is to keep the consistency with glib.

GLib doesn't discourage return types other than boolean. It only asks that if you return boolean, then true should mean success and false should mean failure. See

https://developer.gnome.org/glib/stable/glib-Error-Reporting.html

under "Rules for use of GError", item "By convention, if you return a boolean value".

The discussion you remember was about a convention we used to enforce in QEMU, namely to avoid returning boolean success, and return void instead. That was basically a bad idea.

Quoted 1 line

So IMO I am wondering whether it is really needed to change the bool (true/false) to int (0/-errno), just for that sole function: qemu_signalfd_compat() which needs the errno. Besides if we return -errno, for each caller we need add a local variable like “ret= qemu_thread_create()” to store the -errno.

Well, you either assign the error code to errno just for that caller, or you return the error code just for that caller. I'd do the latter because I consider it slightly simpler. Compare

* On success, return true.
* On failure, set @errno, store an error through @errp and return
* false.

Quoted 8 lines

to

* On success, return zero.
* On failure, store an error through @errp and return negative errno.

where the second sentence describes just two instead of three actions.

[...]

Ok, decribing two actions than three is indeed simpler. But I still have one uncertain:
for those callers do not need the errno value, could we just check the return value
to see whether it is negative, but not cache the unused return value? I mean

Quoted 5 lines

In the caller:

{...

    if (qemu_thread_create() < 0) {// do some cleanup}

...}

This is just fine when you handle all errors the same.

Quoted 2 lines

instead of

Quoted 8 lines
Quoted 6 lines

{ int ret;
...

    ret = qemu_thread_create();
    if (ret < 0) { //do some cleanup }

...}

I'd object to this one unless the value of @ret gets used elsewhere.

Ok, thanks for the review :)

Have a nice day
Fei

Quoted 6 lines
Quoted 6 lines

As the first one can lessen quite a lot of codes. :)

Have a nice day, thanks

Fei

author
#17 lore raw

PATCH v9 7/16 qemu_thread: supplement error handling for qemu_X_start_vcpu

In reply to Fei Li

The callers of qemu_init_vcpu() already passed the **errp to handle errors. In view of this, add a new Error parameter to qemu_init_vcpu() and all qemu_X_start_vcpu() functions called by qemu_init_vcpu() to propagate the error and let the further callers check it.

Besides, make qemu_init_vcpu() return a Boolean value to let its callers know whether it succeeds.

---
 accel/tcg/user-exec-stub.c      |  3 +-
 cpus.c                          | 86 ++++++++++++++++++++++++-----------------
 include/qom/cpu.h               |  2 +-
 target/alpha/cpu.c              |  4 +-
 target/arm/cpu.c                |  4 +-
 target/cris/cpu.c               |  4 +-
 target/hppa/cpu.c               |  4 +-
 target/i386/cpu.c               |  4 +-
 target/lm32/cpu.c               |  4 +-
 target/m68k/cpu.c               |  4 +-
 target/microblaze/cpu.c         |  4 +-
 target/mips/cpu.c               |  4 +-
 target/moxie/cpu.c              |  4 +-
 target/nios2/cpu.c              |  4 +-
 target/openrisc/cpu.c           |  4 +-
 target/ppc/translate_init.inc.c |  4 +-
 target/riscv/cpu.c              |  4 +-
 target/s390x/cpu.c              |  4 +-
 target/sh4/cpu.c                |  4 +-
 target/sparc/cpu.c              |  4 +-
 target/tilegx/cpu.c             |  4 +-
 target/tricore/cpu.c            |  4 +-
 target/unicore32/cpu.c          |  4 +-
 target/xtensa/cpu.c             |  4 +-
 24 files changed, 117 insertions(+), 58 deletions(-)
accel/tcg/user-exec-stub.c +2 -1
index a32b4496af..f8c38a375c 100644
@@ -10,8 +10,9 @@ void cpu_resume(CPUState *cpu)
 {
 }
 
-void qemu_init_vcpu(CPUState *cpu)
+bool qemu_init_vcpu(CPUState *cpu, Error **errp)
 {
+    return true;
 }
 
 /* User mode emulation does not support record/replay yet.  */
cpus.c +51 -35

174 lines not on this page · raw message

include/qom/cpu.h +1 -1
index 1396f53e5b..696c3608d2 100644
@@ -1006,7 +1006,7 @@ void end_exclusive(void);
  *
  * Initializes a vCPU.
  */
-void qemu_init_vcpu(CPUState *cpu);
+bool qemu_init_vcpu(CPUState *cpu, Error **errp);
 
 #define SSTEP_ENABLE  0x1  /* Enable simulated HW single stepping */
 #define SSTEP_NOIRQ   0x2  /* Do not use IRQ while single stepping */
target/alpha/cpu.c +3 -1
index a953897fcc..bf3c34516d 100644
@@ -66,7 +66,9 @@ static void alpha_cpu_realizefn(DeviceState *dev, Error **errp)
         return;
     }
 
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
 
     acc->parent_realize(dev, errp);
 }
target/arm/cpu.c +3 -1
index c8505eaaee..ef745ff4c7 100644
@@ -1125,7 +1125,9 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
     }
 #endif
 
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
     cpu_reset(cs);
 
     acc->parent_realize(dev, errp);
target/cris/cpu.c +3 -1
index a23aba2688..ec92d69781 100644
@@ -140,7 +140,9 @@ static void cris_cpu_realizefn(DeviceState *dev, Error **errp)
     }
 
     cpu_reset(cs);
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
 
     ccc->parent_realize(dev, errp);
 }
target/hppa/cpu.c +3 -1
index 00bf444620..08f600ced9 100644
@@ -98,7 +98,9 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp)
         return;
     }
 
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
     acc->parent_realize(dev, errp);
 
 #ifndef CONFIG_USER_ONLY
target/i386/cpu.c +3 -1
index 677a3bd5fb..21fc8122eb 100644
@@ -5279,7 +5279,9 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
     }
 #endif
 
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
 
     /*
      * Most Intel and certain AMD CPUs support hyperthreading. Even though QEMU
target/lm32/cpu.c +3 -1
index b7499cb627..d50b1e4a43 100644
@@ -139,7 +139,9 @@ static void lm32_cpu_realizefn(DeviceState *dev, Error **errp)
 
     cpu_reset(cs);
 
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
 
     lcc->parent_realize(dev, errp);
 }
target/m68k/cpu.c +3 -1
index 582e3a73b3..4ab53f2d58 100644
@@ -231,7 +231,9 @@ static void m68k_cpu_realizefn(DeviceState *dev, Error **errp)
     m68k_cpu_init_gdb(cpu);
 
     cpu_reset(cs);
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
 
     mcc->parent_realize(dev, errp);
 }
target/microblaze/cpu.c +3 -1
index 9b546a2c18..3906c864a3 100644
@@ -161,7 +161,9 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp)
         return;
     }
 
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
 
     env->pvr.regs[0] = PVR0_USE_EXC_MASK \
                        | PVR0_USE_ICACHE_MASK \
target/mips/cpu.c +3 -1
index e217fb3e36..1e5aa69c57 100644
@@ -145,7 +145,9 @@ static void mips_cpu_realizefn(DeviceState *dev, Error **errp)
     cpu_mips_realize_env(&cpu->env);
 
     cpu_reset(cs);
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
 
     mcc->parent_realize(dev, errp);
 }
target/moxie/cpu.c +3 -1
index 8d67eb6727..8581a6d922 100644
@@ -66,7 +66,9 @@ static void moxie_cpu_realizefn(DeviceState *dev, Error **errp)
         return;
     }
 
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
     cpu_reset(cs);
 
     mcc->parent_realize(dev, errp);
target/nios2/cpu.c +3 -1
index fbfaa2ce26..5c7b4b486e 100644
@@ -94,7 +94,9 @@ static void nios2_cpu_realizefn(DeviceState *dev, Error **errp)
         return;
     }
 
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
     cpu_reset(cs);
 
     ncc->parent_realize(dev, errp);
target/openrisc/cpu.c +3 -1
index fb7cb5c507..a6dcdb9df9 100644
@@ -83,7 +83,9 @@ static void openrisc_cpu_realizefn(DeviceState *dev, Error **errp)
         return;
     }
 
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
     cpu_reset(cs);
 
     occ->parent_realize(dev, errp);
target/ppc/translate_init.inc.c +3 -1
index 03f1d34a97..2fba2e4741 100644
@@ -9711,7 +9711,9 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp)
                                  32, "power-vsx.xml", 0);
     }
 
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        goto unrealize;
+    }
 
     pcc->parent_realize(dev, errp);
 
target/riscv/cpu.c +3 -1
index a025a0a3ba..9829fd9bc4 100644
@@ -305,7 +305,9 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
         return;
     }
 
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
     cpu_reset(cs);
 
     mcc->parent_realize(dev, errp);
target/s390x/cpu.c +3 -1
index 18ba7f85a5..2a3eac9761 100644
@@ -222,7 +222,9 @@ static void s390_cpu_realizefn(DeviceState *dev, Error **errp)
     qemu_register_reset(s390_cpu_machine_reset_cb, cpu);
 #endif
     s390_cpu_gdb_init(cs);
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
 
     /*
      * KVM requires the initial CPU reset ioctl to be executed on the target
target/sh4/cpu.c +3 -1
index b9f393b7c7..d32ef2e1cb 100644
@@ -196,7 +196,9 @@ static void superh_cpu_realizefn(DeviceState *dev, Error **errp)
     }
 
     cpu_reset(cs);
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
 
     scc->parent_realize(dev, errp);
 }
target/sparc/cpu.c +3 -1
index 0f090ece54..9c22f6a7df 100644
@@ -773,7 +773,9 @@ static void sparc_cpu_realizefn(DeviceState *dev, Error **errp)
         return;
     }
 
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
 
     scc->parent_realize(dev, errp);
 }
target/tilegx/cpu.c +3 -1
index bfe9be59b5..234148fabd 100644
@@ -92,7 +92,9 @@ static void tilegx_cpu_realizefn(DeviceState *dev, Error **errp)
     }
 
     cpu_reset(cs);
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
 
     tcc->parent_realize(dev, errp);
 }
target/tricore/cpu.c +3 -1
index 2edaef1aef..5482d6ea3f 100644
@@ -96,7 +96,9 @@ static void tricore_cpu_realizefn(DeviceState *dev, Error **errp)
         set_feature(env, TRICORE_FEATURE_13);
     }
     cpu_reset(cs);
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
 
     tcc->parent_realize(dev, errp);
 }
target/unicore32/cpu.c +3 -1
index 2b49d1ca40..0c737c3187 100644
@@ -96,7 +96,9 @@ static void uc32_cpu_realizefn(DeviceState *dev, Error **errp)
         return;
     }
 
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
 
     ucc->parent_realize(dev, errp);
 }
target/xtensa/cpu.c +3 -1
index a54dbe4260..d2351c9b20 100644
@@ -131,7 +131,9 @@ static void xtensa_cpu_realizefn(DeviceState *dev, Error **errp)
 
     cs->gdb_num_regs = xcc->config->gdb_regmap.num_regs;
 
-    qemu_init_vcpu(cs);
+    if (!qemu_init_vcpu(cs, errp)) {
+        return;
+    }
 
     xcc->parent_realize(dev, errp);
 }
Signature · 1 line
2.13.7
author
#18 lore raw

PATCH v9 8/16 qemu_thread: supplement error handling for qmp_dump_guest_memory

In reply to Fei Li

Utilize the existed errp to propagate the error instead of the temporary &error_abort.

Cc Markus Armbruster <[email protected]>
Cc Marc-André Lureau <[email protected]>
---
 dump.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
dump.c +4 -3
index c35d6ddd22..ef5ea324fa 100644
@@ -2020,9 +2020,10 @@ void qmp_dump_guest_memory(bool paging, const char *file,
     if (detach_p) {
         /* detached dump */
         s->detached = true;
-        /* TODO: let the further caller handle the error instead of abort() */
-        qemu_thread_create(&s->dump_thread, "dump_thread", dump_thread,
-                           s, QEMU_THREAD_DETACHED, &error_abort);
+        if (!qemu_thread_create(&s->dump_thread, "dump_thread", dump_thread,
+                           s, QEMU_THREAD_DETACHED, errp)) {
+            /* keep 'if' here in case there is further error handling logic */
+        }
     } else {
         /* sync dump */
         dump_process(s, errp);
Signature · 1 line
2.13.7
#19 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 08/16] qemu_thread: supplement error handling for qmp_dump_guest_memory

Quoted 15 lines · Fei Li

Utilize the existed errp to propagate the error instead of the temporary &error_abort.

Cc Markus Armbruster <[email protected]>
Cc Marc-André Lureau <[email protected]>
---
 dump.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
dump.c +0 -0
index c35d6ddd22..ef5ea324fa 100644

dump.c void qmp_dump_guest_memory(bool paging, const char *file, · line 2026

Quoted 10 lines
dump.c +4 -3
@@ -2020,9 +2020,10 @@ void qmp_dump_guest_memory(bool paging, const char *file,
     if (detach_p) {
         /* detached dump */
         s->detached = true;
-        /* TODO: let the further caller handle the error instead of abort() */
-        qemu_thread_create(&s->dump_thread, "dump_thread", dump_thread,
-                           s, QEMU_THREAD_DETACHED, &error_abort);
+        if (!qemu_thread_create(&s->dump_thread, "dump_thread", dump_thread,
+                           s, QEMU_THREAD_DETACHED, errp)) {
+            /* keep 'if' here in case there is further error handling logic */
+        }

I don't think keeping the conditional "just in case" is worthwhile. Plain

           qemu_thread_create(&s->dump_thread, "dump_thread", dump_thread,
                              s, QEMU_THREAD_DETACHED, errp);

should do fine.

Quoted 3 lines
dump.c +0 -0
     } else {
         /* sync dump */
         dump_process(s, errp);
#20 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 08/16] qemu_thread: supplement error handling for qmp_dump_guest_memory

Quoted 35 lines · 在 2019年1月8日,01:21,Markus Armbruster <[email protected]> 写道:
Quoted 15 lines · Fei Li

Utilize the existed errp to propagate the error instead of the temporary &error_abort.

Cc Markus Armbruster <[email protected]>
Cc Marc-André Lureau <[email protected]>

---
dump.c | 7 ++++---

1 file changed, 4 insertions(+), 3 deletions(-)
dump.c +0 -0
index c35d6ddd22..ef5ea324fa 100644

dump.c void qmp_dump_guest_memory(bool paging, const char *file, · line 2026

Quoted 10 lines
dump.c +4 -3
@@ -2020,9 +2020,10 @@ void qmp_dump_guest_memory(bool paging, const char *file,
    if (detach_p) {
        /* detached dump */
        s->detached = true;
-        /* TODO: let the further caller handle the error instead of abort() */
-        qemu_thread_create(&s->dump_thread, "dump_thread", dump_thread,
-                           s, QEMU_THREAD_DETACHED, &error_abort);
+        if (!qemu_thread_create(&s->dump_thread, "dump_thread", dump_thread,
+                           s, QEMU_THREAD_DETACHED, errp)) {
+            /* keep 'if' here in case there is further error handling logic */
+        }

I don't think keeping the conditional "just in case" is worthwhile. Plain

Quoted 4 lines
          qemu_thread_create(&s->dump_thread, "dump_thread", dump_thread,
                             s, QEMU_THREAD_DETACHED, errp);

should do fine.

Ok, will simplify this in next version.

Have a nice day, thanks
Fei

Quoted 4 lines
Quoted 4 lines
dump.c +0 -0
    } else {
        /* sync dump */
        dump_process(s, errp);
author
#21 lore raw

PATCH v9 9/16 qemu_thread: supplement error handling for pci_edu_realize

In reply to Fei Li

Utilize the existed errp to propagate the error instead of the temporary &error_abort.

Cc Markus Armbruster <[email protected]>
Cc Jiri Slaby <[email protected]>
---
 hw/misc/edu.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
hw/misc/edu.c +4 -3
index 3f4ba7ded3..011fe6e0b7 100644
@@ -356,9 +356,10 @@ static void pci_edu_realize(PCIDevice *pdev, Error **errp)
 
     qemu_mutex_init(&edu->thr_mutex);
     qemu_cond_init(&edu->thr_cond);
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&edu->thread, "edu", edu_fact_thread,
-                       edu, QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&edu->thread, "edu", edu_fact_thread,
+                            edu, QEMU_THREAD_JOINABLE, errp)) {
+        return;
+    }
 
     memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu,
                     "edu-mmio", 1 * MiB);
Signature · 1 line
2.13.7
#22 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 09/16] qemu_thread: supplement error handling for pci_edu_realize

Quoted 15 lines · Fei Li

Utilize the existed errp to propagate the error instead of the temporary &error_abort.

Cc Markus Armbruster <[email protected]>
Cc Jiri Slaby <[email protected]>
---
 hw/misc/edu.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
hw/misc/edu.c +0 -0
index 3f4ba7ded3..011fe6e0b7 100644

hw/misc/edu.c static void pci_edu_realize(PCIDevice *pdev, Error **errp) · line 361

Quoted 9 lines
hw/misc/edu.c +3 -3
@@ -356,9 +356,10 @@ static void pci_edu_realize(PCIDevice *pdev, Error **errp)
 
     qemu_mutex_init(&edu->thr_mutex);
     qemu_cond_init(&edu->thr_cond);
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&edu->thread, "edu", edu_fact_thread,
-                       edu, QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&edu->thread, "edu", edu_fact_thread,
+                            edu, QEMU_THREAD_JOINABLE, errp)) {
+        return;

You need to clean up everything that got initialized so far. You might want to call qemu_thread_create() earlier so you have less to clean up.

hw/misc/edu.c static void pci_edu_realize(PCIDevice *pdev, Error **errp) · line 365

Quoted 4 lines
hw/misc/edu.c +1 -0
+    }
 
     memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu,
                     "edu-mmio", 1 * MiB);
       pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &edu->mmio);

}

   static void pci_edu_uninit(PCIDevice *pdev)
   {
       EduState *edu = EDU(pdev);
       qemu_mutex_lock(&edu->thr_mutex);
       edu->stopping = true;
       qemu_mutex_unlock(&edu->thr_mutex);
       qemu_cond_signal(&edu->thr_cond);
       qemu_thread_join(&edu->thread);

       qemu_cond_destroy(&edu->thr_cond);
       qemu_mutex_destroy(&edu->thr_mutex);

       timer_del(&edu->dma_timer);

}

Preexisting: pci_edu_uninit() neglects to call msi_uninit(). Jiri?

#23 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 09/16] qemu_thread: supplement error handling for pci_edu_realize

Quoted 14 lines · Markus Armbruster
   static void pci_edu_uninit(PCIDevice *pdev)
   {
       EduState *edu = EDU(pdev);
       qemu_mutex_lock(&edu->thr_mutex);
       edu->stopping = true;
       qemu_mutex_unlock(&edu->thr_mutex);
       qemu_cond_signal(&edu->thr_cond);
       qemu_thread_join(&edu->thread);

       qemu_cond_destroy(&edu->thr_cond);
       qemu_mutex_destroy(&edu->thr_mutex);

       timer_del(&edu->dma_timer);
Quoted 3 lines

}

Preexisting: pci_edu_uninit() neglects to call msi_uninit(). Jiri?\

I don't know, the MSI support was added in:
commit eabb5782f70b4a10975b24ccd7129929a05ac932

Author: Peter Xu <[email protected]>
Date:   Wed Sep 28 21:03:39 2016 +0800

hw/misc/edu: support MSI interrupt

Hence CCing Peter.

thanks,

Signature · 2 lines
js
suse labs
#24 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 09/16] qemu_thread: supplement error handling for pci_edu_realize

Quoted 19 lines · Jiri Slaby
Quoted 14 lines · Markus Armbruster
   static void pci_edu_uninit(PCIDevice *pdev)
   {
       EduState *edu = EDU(pdev);
       qemu_mutex_lock(&edu->thr_mutex);
       edu->stopping = true;
       qemu_mutex_unlock(&edu->thr_mutex);
       qemu_cond_signal(&edu->thr_cond);
       qemu_thread_join(&edu->thread);

       qemu_cond_destroy(&edu->thr_cond);
       qemu_mutex_destroy(&edu->thr_mutex);

       timer_del(&edu->dma_timer);
Quoted 3 lines

}

Preexisting: pci_edu_uninit() neglects to call msi_uninit(). Jiri?\

Quoted 8 lines

I don't know, the MSI support was added in:
commit eabb5782f70b4a10975b24ccd7129929a05ac932

Author: Peter Xu <[email protected]>
Date:   Wed Sep 28 21:03:39 2016 +0800

hw/misc/edu: support MSI interrupt

Hence CCing Peter.

Hi, Jiri, Markus, Fei,

IMHO msi_uninit() is optional since it only operates on the config space of the device to remove the capability or fix up the flags without really doing any real destruction of objects so nothing will be leaked (unlike msix_uninit, which should be required). But I do agree that calling msi_uninit() could be even nicer here.

Anyone would like to post a patch? Or should I?

Thanks,

Signature · 1 line
Peter Xu
#25 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 09/16] qemu_thread: supplement error handling for pci_edu_realize

Quoted 27 lines · Peter Xu
Quoted 26 lines · Jiri Slaby
Quoted 14 lines · Markus Armbruster
   static void pci_edu_uninit(PCIDevice *pdev)
   {
       EduState *edu = EDU(pdev);
       qemu_mutex_lock(&edu->thr_mutex);
       edu->stopping = true;
       qemu_mutex_unlock(&edu->thr_mutex);
       qemu_cond_signal(&edu->thr_cond);
       qemu_thread_join(&edu->thread);

       qemu_cond_destroy(&edu->thr_cond);
       qemu_mutex_destroy(&edu->thr_mutex);

       timer_del(&edu->dma_timer);
Quoted 3 lines

}

Preexisting: pci_edu_uninit() neglects to call msi_uninit(). Jiri?\

I don't know, the MSI support was added in:
commit eabb5782f70b4a10975b24ccd7129929a05ac932

Author: Peter Xu <[email protected]>
Date:   Wed Sep 28 21:03:39 2016 +0800

hw/misc/edu: support MSI interrupt

Quoted 8 lines
Quoted 1 line

Hence CCing Peter.

Hi, Jiri, Markus, Fei,

IMHO msi_uninit() is optional since it only operates on the config space of the device to remove the capability or fix up the flags without really doing any real destruction of objects so nothing will be leaked (unlike msix_uninit, which should be required).

Michael, Marcel, is neglecting to call msi_uninit() okay, a harmless bug, or a harmful bug?

Quoted 4 lines

But I do
agree that calling msi_uninit() could be even nicer here.

Anyone would like to post a patch? Or should I?

Please coordinate fixing this with Fei Li.

#26 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 09/16] qemu_thread: supplement error handling for pci_edu_realize

Quoted 29 lines · Markus Armbruster
Quoted 27 lines · Peter Xu
Quoted 26 lines · Jiri Slaby
Quoted 16 lines · Markus Armbruster
    static void pci_edu_uninit(PCIDevice *pdev)
    {
        EduState *edu = EDU(pdev);

        qemu_mutex_lock(&edu->thr_mutex);
        edu->stopping = true;
        qemu_mutex_unlock(&edu->thr_mutex);
        qemu_cond_signal(&edu->thr_cond);
        qemu_thread_join(&edu->thread);

        qemu_cond_destroy(&edu->thr_cond);
        qemu_mutex_destroy(&edu->thr_mutex);

        timer_del(&edu->dma_timer);
    }
Quoted 1 line

Preexisting: pci_edu_uninit() neglects to call msi_uninit(). Jiri?\

I don't know, the MSI support was added in:
commit eabb5782f70b4a10975b24ccd7129929a05ac932

Author: Peter Xu <[email protected]>
Date:   Wed Sep 28 21:03:39 2016 +0800

hw/misc/edu: support MSI interrupt

Hence CCing Peter.

Quoted 8 lines
Quoted 6 lines

Hi, Jiri, Markus, Fei,

IMHO msi_uninit() is optional since it only operates on the config space of the device to remove the capability or fix up the flags without really doing any real destruction of objects so nothing will be leaked (unlike msix_uninit, which should be required).

Michael, Marcel, is neglecting to call msi_uninit() okay, a harmless bug, or a harmful bug?

Kindly ping. :)

If corresponding change is needed, I'd like to do the update in the next version.

Quoted 5 lines
Quoted 4 lines

But I do
agree that calling msi_uninit() could be even nicer here.

Anyone would like to post a patch? Or should I?

Please coordinate fixing this with Fei Li.

Have a nice day, thanks all
Fei

#27 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 09/16] qemu_thread: supplement error handling for pci_edu_realize

Quoted 37 lines · Fei Li
Quoted 35 lines · Markus Armbruster
Quoted 33 lines · Peter Xu
Quoted 18 lines · Jiri Slaby
Quoted 16 lines · Markus Armbruster
    static void pci_edu_uninit(PCIDevice *pdev)
    {
        EduState *edu = EDU(pdev);

        qemu_mutex_lock(&edu->thr_mutex);
        edu->stopping = true;
        qemu_mutex_unlock(&edu->thr_mutex);
        qemu_cond_signal(&edu->thr_cond);
        qemu_thread_join(&edu->thread);

        qemu_cond_destroy(&edu->thr_cond);
        qemu_mutex_destroy(&edu->thr_mutex);

        timer_del(&edu->dma_timer);
    }
Quoted 1 line

Preexisting: pci_edu_uninit() neglects to call msi_uninit(). Jiri?\

Quoted 8 lines

I don't know, the MSI support was added in:
commit eabb5782f70b4a10975b24ccd7129929a05ac932

Author: Peter Xu <[email protected]>
Date:   Wed Sep 28 21:03:39 2016 +0800

hw/misc/edu: support MSI interrupt

Hence CCing Peter.

Hi, Jiri, Markus, Fei,

IMHO msi_uninit() is optional since it only operates on the config space of the device to remove the capability or fix up the flags without really doing any real destruction of objects so nothing will be leaked (unlike msix_uninit, which should be required).

Quoted 7 lines
Quoted 2 lines

Michael, Marcel, is neglecting to call msi_uninit() okay, a harmless bug, or a harmful bug?

Kindly ping. :)

If corresponding change is needed, I'd like to do the update in the next version.

Fei,

If you're going to post the edu patch, please post it as a standalone patch. More patches mean harder that the series could be accepted quickly. So it would be good to split patches sometimes especially if they are irrelevant.

Regards,

Signature · 1 line
Peter Xu
#28 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 09/16] qemu_thread: supplement error handling for pci_edu_realize

Quoted 43 lines · Peter Xu
Quoted 36 lines · Fei Li
Quoted 35 lines · Markus Armbruster
Quoted 33 lines · Peter Xu
Quoted 18 lines · Jiri Slaby
Quoted 16 lines · Markus Armbruster
     static void pci_edu_uninit(PCIDevice *pdev)
     {
         EduState *edu = EDU(pdev);

         qemu_mutex_lock(&edu->thr_mutex);
         edu->stopping = true;
         qemu_mutex_unlock(&edu->thr_mutex);
         qemu_cond_signal(&edu->thr_cond);
         qemu_thread_join(&edu->thread);

         qemu_cond_destroy(&edu->thr_cond);
         qemu_mutex_destroy(&edu->thr_mutex);

         timer_del(&edu->dma_timer);
     }
Quoted 1 line

Preexisting: pci_edu_uninit() neglects to call msi_uninit(). Jiri?\

Quoted 8 lines

I don't know, the MSI support was added in:
commit eabb5782f70b4a10975b24ccd7129929a05ac932

Author: Peter Xu <[email protected]>
Date:   Wed Sep 28 21:03:39 2016 +0800

hw/misc/edu: support MSI interrupt

Hence CCing Peter.

Hi, Jiri, Markus, Fei,

IMHO msi_uninit() is optional since it only operates on the config space of the device to remove the capability or fix up the flags without really doing any real destruction of objects so nothing will be leaked (unlike msix_uninit, which should be required).

Quoted 6 lines
Quoted 2 lines

Michael, Marcel, is neglecting to call msi_uninit() okay, a harmless bug, or a harmful bug?

Kindly ping. :)

If corresponding change is needed, I'd like to do the update in the next version.

Quoted 9 lines

Fei,

If you're going to post the edu patch, please post it as a standalone patch. More patches mean harder that the series could be accepted quickly. So it would be good to split patches sometimes especially if they are irrelevant.

Regards,

Ok, thanks for this helpful suggestion. Will send this patch alone in the next version. :)

Have a nice day
Fei

#29 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 09/16] qemu_thread: supplement error handling for pci_edu_realize

In reply to Markus Armbruster
Quoted 18 lines · Markus Armbruster
Quoted 16 lines · Fei Li

Utilize the existed errp to propagate the error instead of the temporary &error_abort.

Cc Markus Armbruster <[email protected]>
Cc Jiri Slaby <[email protected]>
---
  hw/misc/edu.c | 7 ++++---
  1 file changed, 4 insertions(+), 3 deletions(-)
hw/misc/edu.c +0 -0
index 3f4ba7ded3..011fe6e0b7 100644
@@ -356,9 +356,10 @@ static void pci_edu_realize(PCIDevice *pdev, Error **errp)
 
Quoted 10 lines

hw/misc/edu.c static void pci_edu_realize(PCIDevice *pdev, Error **errp) · line 361

Quoted 8 lines
hw/misc/edu.c +3 -3
      qemu_mutex_init(&edu->thr_mutex);
      qemu_cond_init(&edu->thr_cond);
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&edu->thread, "edu", edu_fact_thread,
-                       edu, QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&edu->thread, "edu", edu_fact_thread,
+                            edu, QEMU_THREAD_JOINABLE, errp)) {
+        return;

You need to clean up everything that got initialized so far. You might want to call qemu_thread_create() earlier so you have less to clean up.

Just to make sure about how to do the cleanup. I notice that in device_set_realized(), the current code does not call "dc->unrealize(dev, NULL);" when dc->realize() fails.

        if (dc->realize) {
            dc->realize(dev, &local_err);
        }

        if (local_err != NULL) {
            goto fail;
        }

Is this on purpose? (Maybe due to some devices' realize() do their own cleanup
when fails? Sorry for the unsure, it is such a common function that I did not
check all. :( ) Or else, I prefer to do the cleanup in a unified manner, e.g. call "dc->unrealize(dev, NULL);" which is the pci_qdev_unrealize() for pci devices.

Have a nice day
Fei

#30 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 09/16] qemu_thread: supplement error handling for pci_edu_realize

Quoted 7 lines · Fei Li

Just to make sure about how to do the cleanup. I notice that in device_set_realized(), the current code does not call "dc->unrealize(dev, NULL);" when dc->realize() fails.

        if (dc->realize) {
            dc->realize(dev, &local_err);
        }

Quoted 7 lines

        if (local_err != NULL) {
            goto fail;
        }

Is this on purpose? (Maybe due to some devices' realize() do their own cleanup
when fails? Sorry for the unsure, it is such a common function that I did not
check all. :( ) Or else, I prefer to do the cleanup in a unified manner, e.g. call "dc->unrealize(dev, NULL);" which is the pci_qdev_unrealize() for pci devices.

Yes, this is on purpose.

When a realize() method fails, it must revert everything it has done so far. Results in sane "either succeed completely, or fail and do nothing" semantics.

#31 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 09/16] qemu_thread: supplement error handling for pci_edu_realize

Quoted 4 lines · Markus Armbruster
Quoted 2 lines · Fei Li

Just to make sure about how to do the cleanup. I notice that in device_set_realized(), the current code does not call "dc->unrealize(dev, NULL);" when dc->realize() fails.

Sorry that I am still uncertain.. I guess the code below I pasted was misleading, actually I want to stress the *dc->unrealize() is not called when dc->realize() fails* and the incomplete below "goto fail" does not include the dc->unrealize(),
but instead the dc->unrealize() is included in later child_realize_fail: & post_realize_fail:.

Emm, IMHO, I think when dc->realize() fails, the dc->unrealize() is either should be called in the common function: device_set_realized() in a unified way, that is

        if (local_err != NULL) {
+          if (dc->unrealize) {
+              dc->unrealize(dev, local_err);
+          }
            goto fail;
        }

or do the unrealize() locally for each device earlier when dc->realize() fails. But I checked several dc->realize() function, they did not call unrealize() when fails. Besides, it may mean verbose code if unrealize() locally. Thus I think the above way is the right way to do the cleanup when realize() fails.

Quoted 9 lines
Quoted 9 lines

        if (dc->realize) {
            dc->realize(dev, &local_err);
        }

        if (local_err != NULL) {
            goto fail;
        }

Quoted 8 lines
Quoted 3 lines

Is this on purpose? (Maybe due to some devices' realize() do their own cleanup
when fails? Sorry for the unsure, it is such a common function that I did not
check all. :( ) Or else, I prefer to do the cleanup in a unified manner, e.g. call "dc->unrealize(dev, NULL);" which is the pci_qdev_unrealize() for pci devices.

Yes, this is on purpose.

When a realize() method fails, it must revert everything it has done so far. Results in sane "either succeed completely, or fail and do nothing" semantics.

Have a nice day, thanks

Fei

#32 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 09/16] qemu_thread: supplement error handling for pci_edu_realize

Quoted 26 lines · Fei Li
Quoted 4 lines · Markus Armbruster
Quoted 2 lines · Fei Li

Just to make sure about how to do the cleanup. I notice that in device_set_realized(), the current code does not call "dc->unrealize(dev, NULL);" when dc->realize() fails.

Sorry that I am still uncertain.. I guess the code below I pasted was misleading,
actually I want to stress the *dc->unrealize() is not called when dc->realize() fails*
and the incomplete below "goto fail" does not include the dc->unrealize(), but instead the dc->unrealize() is included in later
child_realize_fail: & post_realize_fail:.

Emm, IMHO, I think when dc->realize() fails, the dc->unrealize() is either should be
called in the common function: device_set_realized() in a unified way, that is

        if (local_err != NULL) {
+          if (dc->unrealize) {
+              dc->unrealize(dev, local_err);
+          }
            goto fail;
        }

Quoted 7 lines

or do the unrealize() locally for each device earlier when dc->realize() fails.

But I checked several dc->realize() function, they did not call unrealize() when fails. Besides, it may mean verbose code if unrealize() locally. Thus I think the above way is the right way to do the cleanup when realize() fails.

The realize() method is specified to either succeed completely or fail completely, i.e. fail and do nothing. The "either succeed completely or fail completely" aspect of the specification is sane and perfectly ordinary.

How a concrete implementation accomplishes "fail completely" is up to the implementation.

An implementation may choose to structure its FOO_realize() and FOO_unrealize() in a way that lets FOO_realize() call FOO_unrealize() to clean up on failure.

An implementation may also choose to clean up differently.

This freedom of choice is by design.

Changing the specification now would involve auditing and updating all realize() and unrealize() methods. Not going to happen without an extremely compelling reason.

Quoted 21 lines
Quoted 9 lines
Quoted 9 lines

        if (dc->realize) {
            dc->realize(dev, &local_err);
        }

        if (local_err != NULL) {
            goto fail;
        }

Quoted 8 lines
Quoted 3 lines

Is this on purpose? (Maybe due to some devices' realize() do their own cleanup
when fails? Sorry for the unsure, it is such a common function that I did not
check all. :( ) Or else, I prefer to do the cleanup in a unified manner, e.g. call "dc->unrealize(dev, NULL);" which is the pci_qdev_unrealize() for pci devices.

Yes, this is on purpose.

When a realize() method fails, it must revert everything it has done so far. Results in sane "either succeed completely, or fail and do nothing" semantics.

Have a nice day, thanks

Fei

#33 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 09/16] qemu_thread: supplement error handling for pci_edu_realize

Quoted 47 lines · Markus Armbruster
Quoted 26 lines · Fei Li
Quoted 4 lines · Markus Armbruster
Quoted 2 lines · Fei Li

Just to make sure about how to do the cleanup. I notice that in device_set_realized(), the current code does not call "dc->unrealize(dev, NULL);" when dc->realize() fails.

Sorry that I am still uncertain.. I guess the code below I pasted was misleading,
actually I want to stress the *dc->unrealize() is not called when dc->realize() fails*
and the incomplete below "goto fail" does not include the dc->unrealize(), but instead the dc->unrealize() is included in later
child_realize_fail: & post_realize_fail:.

Emm, IMHO, I think when dc->realize() fails, the dc->unrealize() is either should be
called in the common function: device_set_realized() in a unified way, that is

        if (local_err != NULL) {
+          if (dc->unrealize) {
+              dc->unrealize(dev, local_err);
+          }
            goto fail;
        }

Quoted 7 lines

or do the unrealize() locally for each device earlier when dc->realize() fails.

But I checked several dc->realize() function, they did not call unrealize() when fails. Besides, it may mean verbose code if unrealize() locally. Thus I think the above way is the right way to do the cleanup when realize() fails.

The realize() method is specified to either succeed completely or fail completely, i.e. fail and do nothing. The "either succeed completely or fail completely" aspect of the specification is sane and perfectly ordinary.

How a concrete implementation accomplishes "fail completely" is up to the implementation.

An implementation may choose to structure its FOO_realize() and FOO_unrealize() in a way that lets FOO_realize() call FOO_unrealize() to clean up on failure.

Quoted 7 lines

An implementation may also choose to clean up differently.

This freedom of choice is by design.

Changing the specification now would involve auditing and updating all realize() and unrealize() methods. Not going to happen without an extremely compelling reason.

Ok, now I see. Thanks for the detail explanation. :)

Will add the cleanup in the next version.

Have a nice day
Fei

Quoted 19 lines
Quoted 19 lines
Quoted 8 lines
Quoted 8 lines
          if (dc->realize) {
              dc->realize(dev, &local_err);
          }

          if (local_err != NULL) {
              goto fail;
          }
Quoted 8 lines
Quoted 3 lines

Is this on purpose? (Maybe due to some devices' realize() do their own cleanup
when fails? Sorry for the unsure, it is such a common function that I did not
check all. :( ) Or else, I prefer to do the cleanup in a unified manner, e.g. call "dc->unrealize(dev, NULL);" which is the pci_qdev_unrealize() for pci devices.

Yes, this is on purpose.

When a realize() method fails, it must revert everything it has done so far. Results in sane "either succeed completely, or fail and do nothing" semantics.

Have a nice day, thanks

Fei

author
#34 lore raw

PATCH v9 10/16 qemu_thread: supplement error handling for h_resize_hpt_prepare

In reply to Fei Li

Add a local_err to hold the error, and return the corresponding error code to replace the temporary &error_abort.

Cc Markus Armbruster <[email protected]>
Cc David Gibson <[email protected]>
---
 hw/ppc/spapr_hcall.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)
hw/ppc/spapr_hcall.c +8 -4
index 5bc2cf4540..7c16ade04a 100644
@@ -478,6 +478,7 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu,
     sPAPRPendingHPT *pending = spapr->pending_hpt;
     uint64_t current_ram_size;
     int rc;
+    Error *local_err = NULL;
 
     if (spapr->resize_hpt == SPAPR_RESIZE_HPT_DISABLED) {
         return H_AUTHORITY;
@@ -538,10 +539,13 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu,
     pending->shift = shift;
     pending->ret = H_HARDWARE;
 
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&pending->thread, "sPAPR HPT prepare",
-                       hpt_prepare_thread, pending,
-                       QEMU_THREAD_DETACHED, &error_abort);
+    if (!qemu_thread_create(&pending->thread, "sPAPR HPT prepare",
+                            hpt_prepare_thread, pending,
+                            QEMU_THREAD_DETACHED, &local_err)) {
+        error_reportf_err(local_err, "failed to create hpt_prepare_thread: ");
+        g_free(pending);
+        return H_RESOURCE;
+    }
 
     spapr->pending_hpt = pending;
 
Signature · 1 line
2.13.7
#35 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 10/16] qemu_thread: supplement error handling for h_resize_hpt_prepare

Quoted 6 lines · Fei Li

Add a local_err to hold the error, and return the corresponding error code to replace the temporary &error_abort.

Cc Markus Armbruster <[email protected]>
Cc David Gibson <[email protected]>

This looks like a good change, but it no longer applies due to a change in the qemu_thread_create() signature.

Quoted 20 lines
---
 hw/ppc/spapr_hcall.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)
hw/ppc/spapr_hcall.c +1 -0
index 5bc2cf4540..7c16ade04a 100644
@@ -478,6 +478,7 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu,
     sPAPRPendingHPT *pending = spapr->pending_hpt;
     uint64_t current_ram_size;
     int rc;
+    Error *local_err = NULL;
 
     if (spapr->resize_hpt == SPAPR_RESIZE_HPT_DISABLED) {
         return H_AUTHORITY;
@@ -538,10 +539,13 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu,
     pending->shift = shift;
     pending->ret = H_HARDWARE;
 

hw/ppc/spapr_hcall.c static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu, · line 547

Quoted 10 lines
hw/ppc/spapr_hcall.c +6 -4
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&pending->thread, "sPAPR HPT prepare",
-                       hpt_prepare_thread, pending,
-                       QEMU_THREAD_DETACHED, &error_abort);
+    if (!qemu_thread_create(&pending->thread, "sPAPR HPT prepare",
+                            hpt_prepare_thread, pending,
+                            QEMU_THREAD_DETACHED, &local_err)) {
+        error_reportf_err(local_err, "failed to create hpt_prepare_thread: ");
+        g_free(pending);
+        return H_RESOURCE;

I also think H_HARDWARE would be a better choice here. Although the failure is due to a resource constraint, it's not because the guest asked for too much, just because the host is in dire straits. From the guest's point of view it's basically a hardware failure.

Quoted 4 lines
hw/ppc/spapr_hcall.c +1 -0
+    }
 
     spapr->pending_hpt = pending;
 
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#36 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 10/16] qemu_thread: supplement error handling for h_resize_hpt_prepare

Quoted 9 lines · David Gibson
Quoted 6 lines · Fei Li

Add a local_err to hold the error, and return the corresponding error code to replace the temporary &error_abort.

Cc Markus Armbruster <[email protected]>
Cc David Gibson <[email protected]>

This looks like a good change, but it no longer applies due to a change in the qemu_thread_create() signature.

Sorry that I am not sure whether I understand. Do you mean using &error_abort is more suitable for this handling, rather than report the &local_err & return a failure reason?

Quoted 21 lines
Quoted 21 lines
---
  hw/ppc/spapr_hcall.c | 12 ++++++++----
  1 file changed, 8 insertions(+), 4 deletions(-)
hw/ppc/spapr_hcall.c +1 -0
index 5bc2cf4540..7c16ade04a 100644
@@ -478,6 +478,7 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu,
      sPAPRPendingHPT *pending = spapr->pending_hpt;
      uint64_t current_ram_size;
      int rc;
+    Error *local_err = NULL;
 
      if (spapr->resize_hpt == SPAPR_RESIZE_HPT_DISABLED) {
          return H_AUTHORITY;
@@ -538,10 +539,13 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu,
      pending->shift = shift;
      pending->ret = H_HARDWARE;
 
Quoted 14 lines

hw/ppc/spapr_hcall.c static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu, · line 547

Quoted 10 lines
hw/ppc/spapr_hcall.c +6 -4
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&pending->thread, "sPAPR HPT prepare",
-                       hpt_prepare_thread, pending,
-                       QEMU_THREAD_DETACHED, &error_abort);
+    if (!qemu_thread_create(&pending->thread, "sPAPR HPT prepare",
+                            hpt_prepare_thread, pending,
+                            QEMU_THREAD_DETACHED, &local_err)) {
+        error_reportf_err(local_err, "failed to create hpt_prepare_thread: ");
+        g_free(pending);
+        return H_RESOURCE;

I also think H_HARDWARE would be a better choice here. Although the failure is due to a resource constraint, it's not because the guest asked for too much, just because the host is in dire straits. From the guest's point of view it's basically a hardware failure.

Ok, thanks. Will use H_HARDWARE instead.

Have a nice day, thanks for the review. :)
Fei

Quoted 5 lines
Quoted 5 lines
hw/ppc/spapr_hcall.c +1 -0
+    }
 
      spapr->pending_hpt = pending;
 
#37 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 10/16] qemu_thread: supplement error handling for h_resize_hpt_prepare

Quoted 6 lines · 李菲
Quoted 4 lines · David Gibson
Quoted 3 lines · Fei Li

Add a local_err to hold the error, and return the corresponding error code to replace the temporary &error_abort.

Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Cc Markus Armbruster <[email protected]>
Cc David Gibson <[email protected]>

This looks like a good change, but it no longer applies due to a change in the qemu_thread_create() signature.

Sorry that I am not sure whether I understand. Do you mean using &error_abort is more suitable for this handling, rather than report the &local_err & return a failure reason?

No, I just mean that context has been altered by a global change and the patch will need to be fixed up to cope with that.

Quoted 42 lines
Quoted 25 lines
Quoted 25 lines
---
  hw/ppc/spapr_hcall.c | 12 ++++++++----
  1 file changed, 8 insertions(+), 4 deletions(-)
hw/ppc/spapr_hcall.c +3 -4
index 5bc2cf4540..7c16ade04a 100644
@@ -478,6 +478,7 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu,
      sPAPRPendingHPT *pending = spapr->pending_hpt;
      uint64_t current_ram_size;
      int rc;
+    Error *local_err = NULL;
      if (spapr->resize_hpt == SPAPR_RESIZE_HPT_DISABLED) {
          return H_AUTHORITY;
@@ -538,10 +539,13 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu,
      pending->shift = shift;
      pending->ret = H_HARDWARE;
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&pending->thread, "sPAPR HPT prepare",
-                       hpt_prepare_thread, pending,
-                       QEMU_THREAD_DETACHED, &error_abort);
+    if (!qemu_thread_create(&pending->thread, "sPAPR HPT prepare",
+                            hpt_prepare_thread, pending,
Quoted 8 lines

hw/ppc/spapr_hcall.c static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu, · line 547

Quoted 4 lines
hw/ppc/spapr_hcall.c +4 -0
+                            QEMU_THREAD_DETACHED, &local_err)) {
+        error_reportf_err(local_err, "failed to create hpt_prepare_thread: ");
+        g_free(pending);
+        return H_RESOURCE;

I also think H_HARDWARE would be a better choice here. Although the failure is due to a resource constraint, it's not because the guest asked for too much, just because the host is in dire straits. From the guest's point of view it's basically a hardware failure.

Ok, thanks. Will use H_HARDWARE instead.

Have a nice day, thanks for the review. :)
Fei

Quoted 3 lines
Quoted 3 lines
hw/ppc/spapr_hcall.c +1 -0
+    }
      spapr->pending_hpt = pending;
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#38 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 10/16] qemu_thread: supplement error handling for h_resize_hpt_prepare

Quoted 9 lines · David Gibson
Quoted 8 lines · 李菲
Quoted 7 lines · David Gibson
Quoted 6 lines · Fei Li

Add a local_err to hold the error, and return the corresponding error code to replace the temporary &error_abort.

Cc Markus Armbruster <[email protected]>
Cc David Gibson <[email protected]>
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

This looks like a good change, but it no longer applies due to a change in the qemu_thread_create() signature.

Sorry that I am not sure whether I understand. Do you mean using &error_abort is more suitable for this handling, rather than report the &local_err & return a failure reason?

No, I just mean that context has been altered by a global change and the patch will need to be fixed up to cope with that.

Just to be clearer: does the "global change" mean the "[patch 06/16] qemu_thread: Make qemu_thread_create() handle errors properly", or another patch not in this patch series?

If it means the [patch 06/16], I want to explain more: the 06/16 handles all qemu_thread_create() by passing &error_abort as the parameter, and the following patches are to improve on the &error_abort for callers who can handle more properly. E.g. if qemu_thread_create() fails in h_resize_hpt_prepare(), I think reporting the &local_err & returning the failure reason is more proper than just abort() inside qemu_thread_create() when calls error_setg_errno().

In other words, this patch is actually written to apply to patch 06. And I have no clue where it needs to be fixed up. Please correct me if I understand wrong.

Have a nice day, thanks :)
Fei

Quoted 39 lines
Quoted 39 lines
Quoted 25 lines
Quoted 25 lines
---
   hw/ppc/spapr_hcall.c | 12 ++++++++----
   1 file changed, 8 insertions(+), 4 deletions(-)
hw/ppc/spapr_hcall.c +3 -4
index 5bc2cf4540..7c16ade04a 100644
@@ -478,6 +478,7 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu,
       sPAPRPendingHPT *pending = spapr->pending_hpt;
       uint64_t current_ram_size;
       int rc;
+    Error *local_err = NULL;
       if (spapr->resize_hpt == SPAPR_RESIZE_HPT_DISABLED) {
           return H_AUTHORITY;
@@ -538,10 +539,13 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu,
       pending->shift = shift;
       pending->ret = H_HARDWARE;
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&pending->thread, "sPAPR HPT prepare",
-                       hpt_prepare_thread, pending,
-                       QEMU_THREAD_DETACHED, &error_abort);
+    if (!qemu_thread_create(&pending->thread, "sPAPR HPT prepare",
+                            hpt_prepare_thread, pending,
Quoted 8 lines

hw/ppc/spapr_hcall.c static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu, · line 547

Quoted 4 lines
hw/ppc/spapr_hcall.c +4 -0
+                            QEMU_THREAD_DETACHED, &local_err)) {
+        error_reportf_err(local_err, "failed to create hpt_prepare_thread: ");
+        g_free(pending);
+        return H_RESOURCE;

I also think H_HARDWARE would be a better choice here. Although the failure is due to a resource constraint, it's not because the guest asked for too much, just because the host is in dire straits. From the guest's point of view it's basically a hardware failure.

Ok, thanks. Will use H_HARDWARE instead.

Have a nice day, thanks for the review. :)
Fei

Quoted 2 lines
Quoted 2 lines
hw/ppc/spapr_hcall.c +1 -0
+    }
       spapr->pending_hpt = pending;
Acked-by
#39 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 10/16] qemu_thread: supplement error handling for h_resize_hpt_prepare

Quoted 32 lines · Fei Li
Quoted 9 lines · David Gibson
Quoted 8 lines · 李菲
Quoted 7 lines · David Gibson
Quoted 6 lines · Fei Li

Add a local_err to hold the error, and return the corresponding error code to replace the temporary &error_abort.

Cc Markus Armbruster <[email protected]>
Cc David Gibson <[email protected]>
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

This looks like a good change, but it no longer applies due to a change in the qemu_thread_create() signature.

Sorry that I am not sure whether I understand. Do you mean using &error_abort is more suitable for this handling, rather than report the &local_err & return a failure reason?

No, I just mean that context has been altered by a global change and the patch will need to be fixed up to cope with that.

Just to be clearer: does the "global change" mean the "[patch 06/16]
qemu_thread: Make qemu_thread_create() handle errors properly", or another patch not in this patch series?

If it means the [patch 06/16], I want to explain more: the 06/16 handles all qemu_thread_create() by passing &error_abort as the parameter, and the following patches are to improve on the &error_abort for callers who can handle more properly. E.g. if qemu_thread_create() fails in h_resize_hpt_prepare(),
I think reporting the &local_err & returning the failure reason is more proper
than just abort() inside qemu_thread_create() when calls error_setg_errno().

Quoted 7 lines

In other words, this patch is actually written to apply to patch 06. And I have
no clue where it needs to be fixed up. Please correct me if I understand wrong.

Have a nice day, thanks :)

Ah, sorry. Since I was only CCed on this patch, not the rest of the series, I assumed it was independent and didn't think to check the earlier patches of the series.

So, yes, I think the global change I'm referring to is 6/16, which I didn't have, so that explains the problem.

In that case it's probably best if this goes in via the same tree the rest of the series is going to. So, with the H_HARDWARE change made:

Quoted 43 lines

Fei

Quoted 39 lines
Quoted 39 lines
Quoted 25 lines
Quoted 25 lines
---
   hw/ppc/spapr_hcall.c | 12 ++++++++----
   1 file changed, 8 insertions(+), 4 deletions(-)
hw/ppc/spapr_hcall.c +3 -4
index 5bc2cf4540..7c16ade04a 100644
@@ -478,6 +478,7 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu,
       sPAPRPendingHPT *pending = spapr->pending_hpt;
       uint64_t current_ram_size;
       int rc;
+    Error *local_err = NULL;
       if (spapr->resize_hpt == SPAPR_RESIZE_HPT_DISABLED) {
           return H_AUTHORITY;
@@ -538,10 +539,13 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu,
       pending->shift = shift;
       pending->ret = H_HARDWARE;
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&pending->thread, "sPAPR HPT prepare",
-                       hpt_prepare_thread, pending,
-                       QEMU_THREAD_DETACHED, &error_abort);
+    if (!qemu_thread_create(&pending->thread, "sPAPR HPT prepare",
+                            hpt_prepare_thread, pending,
Quoted 8 lines

hw/ppc/spapr_hcall.c static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu, · line 547

Quoted 4 lines
hw/ppc/spapr_hcall.c +4 -0
+                            QEMU_THREAD_DETACHED, &local_err)) {
+        error_reportf_err(local_err, "failed to create hpt_prepare_thread: ");
+        g_free(pending);
+        return H_RESOURCE;

I also think H_HARDWARE would be a better choice here. Although the failure is due to a resource constraint, it's not because the guest asked for too much, just because the host is in dire straits. From the guest's point of view it's basically a hardware failure.

Ok, thanks. Will use H_HARDWARE instead.

Have a nice day, thanks for the review. :)
Fei

Quoted 2 lines
Quoted 2 lines
hw/ppc/spapr_hcall.c +1 -0
+    }
       spapr->pending_hpt = pending;
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#40 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 10/16] qemu_thread: supplement error handling for h_resize_hpt_prepare

Quoted 37 lines · David Gibson
Quoted 36 lines · Fei Li
Quoted 9 lines · David Gibson
Quoted 8 lines · 李菲
Quoted 7 lines · David Gibson
Quoted 6 lines · Fei Li

Add a local_err to hold the error, and return the corresponding error code to replace the temporary &error_abort.

Cc Markus Armbruster <[email protected]>
Cc David Gibson <[email protected]>
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

This looks like a good change, but it no longer applies due to a change in the qemu_thread_create() signature.

Sorry that I am not sure whether I understand. Do you mean using &error_abort is more suitable for this handling, rather than report the &local_err & return a failure reason?

No, I just mean that context has been altered by a global change and the patch will need to be fixed up to cope with that.

Just to be clearer: does the "global change" mean the "[patch 06/16]
qemu_thread: Make qemu_thread_create() handle errors properly", or another patch not in this patch series?

If it means the [patch 06/16], I want to explain more: the 06/16 handles all qemu_thread_create() by passing &error_abort as the parameter, and the following patches are to improve on the &error_abort for callers who can handle more properly. E.g. if qemu_thread_create() fails in h_resize_hpt_prepare(),
I think reporting the &local_err & returning the failure reason is more proper
than just abort() inside qemu_thread_create() when calls error_setg_errno().

In other words, this patch is actually written to apply to patch 06. And I have
no clue where it needs to be fixed up. Please correct me if I understand wrong.

Quoted 4 lines
Quoted 1 line

Have a nice day, thanks :)

Ah, sorry. Since I was only CCed on this patch, not the rest of the series, I assumed it was independent and didn't think to check the earlier patches of the series.

A good reminder, CCing all during the review stage seems more reasonable. :)

Quoted 8 lines

So, yes, I think the global change I'm referring to is 6/16, which I didn't have, so that explains the problem.

In that case it's probably best if this goes in via the same tree the rest of the series is going to. So, with the H_HARDWARE change made:

Thanks for the review!

Have a nice day ;)
Fei

Quoted 43 lines
Quoted 43 lines

Fei

Quoted 38 lines
Quoted 38 lines
Quoted 24 lines
Quoted 24 lines
---
    hw/ppc/spapr_hcall.c | 12 ++++++++----
    1 file changed, 8 insertions(+), 4 deletions(-)
hw/ppc/spapr_hcall.c +3 -4
index 5bc2cf4540..7c16ade04a 100644
@@ -478,6 +478,7 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu,
        sPAPRPendingHPT *pending = spapr->pending_hpt;
        uint64_t current_ram_size;
        int rc;
+    Error *local_err = NULL;
        if (spapr->resize_hpt == SPAPR_RESIZE_HPT_DISABLED) {
            return H_AUTHORITY;
@@ -538,10 +539,13 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu,
        pending->shift = shift;
        pending->ret = H_HARDWARE;
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&pending->thread, "sPAPR HPT prepare",
-                       hpt_prepare_thread, pending,
-                       QEMU_THREAD_DETACHED, &error_abort);
+    if (!qemu_thread_create(&pending->thread, "sPAPR HPT prepare",
+                            hpt_prepare_thread, pending,
Quoted 8 lines

hw/ppc/spapr_hcall.c static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu, · line 547

Quoted 4 lines
hw/ppc/spapr_hcall.c +4 -0
+                            QEMU_THREAD_DETACHED, &local_err)) {
+        error_reportf_err(local_err, "failed to create hpt_prepare_thread: ");
+        g_free(pending);
+        return H_RESOURCE;

I also think H_HARDWARE would be a better choice here. Although the failure is due to a resource constraint, it's not because the guest asked for too much, just because the host is in dire straits. From the guest's point of view it's basically a hardware failure.

Ok, thanks. Will use H_HARDWARE instead.

Have a nice day, thanks for the review. :)
Fei

Quoted 2 lines
Quoted 2 lines
hw/ppc/spapr_hcall.c +1 -0
+    }
        spapr->pending_hpt = pending;
author
#41 lore raw

PATCH v9 11/16 qemu_thread: supplement error handling for emulated_realize

In reply to Fei Li

Utilize the existed errp to propagate the error and do the corresponding cleanup to replace the temporary &error_abort.

Cc Cc: Markus Armbruster <[email protected]>
Cc Gerd Hoffmann <[email protected]>
---
 hw/usb/ccid-card-emulated.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)
hw/usb/ccid-card-emulated.c +9 -6
index f8ff7ff4a3..9245b4fcad 100644
@@ -32,7 +32,6 @@
 #include "qemu/thread.h"
 #include "qemu/main-loop.h"
 #include "ccid.h"
-#include "qapi/error.h"
 
 #define DPRINTF(card, lvl, fmt, ...) \
 do {\
@@ -544,11 +543,15 @@ static void emulated_realize(CCIDCardState *base, Error **errp)
         error_setg(errp, "%s: failed to initialize vcard", TYPE_EMULATED_CCID);
         goto out2;
     }
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread,
-                       card, QEMU_THREAD_JOINABLE, &error_abort);
-    qemu_thread_create(&card->apdu_thread_id, "ccid/apdu", handle_apdu_thread,
-                       card, QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread,
+                            card, QEMU_THREAD_JOINABLE, errp)) {
+        goto out2;
+    }
+    if (!qemu_thread_create(&card->apdu_thread_id, "ccid/apdu",
+                            handle_apdu_thread, card,
+                            QEMU_THREAD_JOINABLE, errp)) {
+        goto out2;
+    }
 
 out2:
     clean_event_notifier(card);
Signature · 1 line
2.13.7
#42 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 11/16] qemu_thread: supplement error handling for emulated_realize

Quoted 29 lines · Fei Li

Utilize the existed errp to propagate the error and do the corresponding cleanup to replace the temporary &error_abort.

Cc Cc: Markus Armbruster <[email protected]>
Cc Gerd Hoffmann <[email protected]>
---
 hw/usb/ccid-card-emulated.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)
hw/usb/ccid-card-emulated.c +0 -4
index f8ff7ff4a3..9245b4fcad 100644
@@ -32,7 +32,6 @@
 #include "qemu/thread.h"
 #include "qemu/main-loop.h"
 #include "ccid.h"
-#include "qapi/error.h"
 
 #define DPRINTF(card, lvl, fmt, ...) \
 do {\
@@ -544,11 +543,15 @@ static void emulated_realize(CCIDCardState *base, Error **errp)
         error_setg(errp, "%s: failed to initialize vcard", TYPE_EMULATED_CCID);
         goto out2;
     }
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread,
-                       card, QEMU_THREAD_JOINABLE, &error_abort);

hw/usb/ccid-card-emulated.c static void emulated_realize(CCIDCardState *base, Error **errp) · line 553

Quoted 10 lines
hw/usb/ccid-card-emulated.c +8 -2
-    qemu_thread_create(&card->apdu_thread_id, "ccid/apdu", handle_apdu_thread,
-                       card, QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread,
+                            card, QEMU_THREAD_JOINABLE, errp)) {
+        goto out2;
+    }
+    if (!qemu_thread_create(&card->apdu_thread_id, "ccid/apdu",
+                            handle_apdu_thread, card,
+                            QEMU_THREAD_JOINABLE, errp)) {
+        goto out2;

You need to stop and join the first thread.

Quoted 4 lines
hw/usb/ccid-card-emulated.c +1 -0
+    }
 
 out2:
     clean_event_notifier(card);
#43 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 11/16] qemu_thread: supplement error handling for emulated_realize

Quoted 34 lines · Markus Armbruster
Quoted 32 lines · Fei Li

Utilize the existed errp to propagate the error and do the corresponding cleanup to replace the temporary &error_abort.

Cc Cc: Markus Armbruster <[email protected]>
Cc Gerd Hoffmann <[email protected]>
---
  hw/usb/ccid-card-emulated.c | 15 +++++++++------
  1 file changed, 9 insertions(+), 6 deletions(-)
hw/usb/ccid-card-emulated.c +1 -6
index f8ff7ff4a3..9245b4fcad 100644
@@ -32,7 +32,6 @@
  #include "qemu/thread.h"
  #include "qemu/main-loop.h"
  #include "ccid.h"
-#include "qapi/error.h"
 
  #define DPRINTF(card, lvl, fmt, ...) \
  do {\
@@ -544,11 +543,15 @@ static void emulated_realize(CCIDCardState *base, Error **errp)
          error_setg(errp, "%s: failed to initialize vcard", TYPE_EMULATED_CCID);
          goto out2;
      }
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread,
-                       card, QEMU_THREAD_JOINABLE, &error_abort);
-    qemu_thread_create(&card->apdu_thread_id, "ccid/apdu", handle_apdu_thread,
-                       card, QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread,
Quoted 8 lines

hw/usb/ccid-card-emulated.c static void emulated_realize(CCIDCardState *base, Error **errp) · line 553

Quoted 7 lines
hw/usb/ccid-card-emulated.c +7 -0
+                            card, QEMU_THREAD_JOINABLE, errp)) {
+        goto out2;
+    }
+    if (!qemu_thread_create(&card->apdu_thread_id, "ccid/apdu",
+                            handle_apdu_thread, card,
+                            QEMU_THREAD_JOINABLE, errp)) {
+        goto out2;

You need to stop and join the first thread.

Thanks for pointing this out!

Have a nice day
Fei

Quoted 5 lines
Quoted 5 lines
hw/usb/ccid-card-emulated.c +1 -0
+    }
 
  out2:
      clean_event_notifier(card);
author
#44 lore raw

PATCH v9 12/16 qemu_thread: supplement error handling for iothread_complete/qemu_signalfd_compat

In reply to Fei Li

For iothread_complete: utilize the existed errp to propagate the error and do the corresponding cleanup to replace the temporary &error_abort.

For qemu_signalfd_compat: add a local_err to hold the error, and return the corresponding error code to replace the temporary &error_abort.

Cc Markus Armbruster <[email protected]>
Cc Eric Blake <[email protected]>
---
 iothread.c      | 17 +++++++++++------
 util/compatfd.c | 11 ++++++++---
 2 files changed, 19 insertions(+), 9 deletions(-)
iothread.c +11 -6
index 8e8aa01999..7335dacf0b 100644
@@ -164,9 +164,7 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
                                 &local_error);
     if (local_error) {
         error_propagate(errp, local_error);
-        aio_context_unref(iothread->ctx);
-        iothread->ctx = NULL;
-        return;
+        goto fail;
     }
 
     qemu_mutex_init(&iothread->init_done_lock);
@@ -178,9 +176,12 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
      */
     name = object_get_canonical_path_component(OBJECT(obj));
     thread_name = g_strdup_printf("IO %s", name);
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&iothread->thread, thread_name, iothread_run,
-                       iothread, QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&iothread->thread, thread_name, iothread_run,
+                            iothread, QEMU_THREAD_JOINABLE, errp)) {
+        g_free(thread_name);
+        g_free(name);
+        goto fail;
+    }
     g_free(thread_name);
     g_free(name);
 
@@ -191,6 +192,10 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
                        &iothread->init_done_lock);
     }
     qemu_mutex_unlock(&iothread->init_done_lock);
+    return;
+fail:
+    aio_context_unref(iothread->ctx);
+    iothread->ctx = NULL;
 }
 
 typedef struct {
util/compatfd.c +8 -3
index c3d8448264..9cb13381e4 100644
@@ -71,6 +71,7 @@ static int qemu_signalfd_compat(const sigset_t *mask)
     struct sigfd_compat_info *info;
     QemuThread thread;
     int fds[2];
+    Error *local_err = NULL;
 
     info = malloc(sizeof(*info));
     if (info == NULL) {
@@ -89,9 +90,13 @@ static int qemu_signalfd_compat(const sigset_t *mask)
     memcpy(&info->mask, mask, sizeof(*mask));
     info->fd = fds[1];
 
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&thread, "signalfd_compat", sigwait_compat,
-                       info, QEMU_THREAD_DETACHED, &error_abort);
+    if (!qemu_thread_create(&thread, "signalfd_compat", sigwait_compat,
+                            info, QEMU_THREAD_DETACHED, &local_err)) {
+        close(fds[0]);
+        close(fds[1]);
+        free(info);
+        return -1;
+    }
 
     return fds[0];
 }
Signature · 1 line
2.13.7
#45 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 12/16] qemu_thread: supplement error handling for iothread_complete/qemu_signalfd_compat

Quoted 7 lines · Fei Li

For iothread_complete: utilize the existed errp to propagate the error and do the corresponding cleanup to replace the temporary &error_abort.

For qemu_signalfd_compat: add a local_err to hold the error, and return the corresponding error code to replace the temporary &error_abort.

I'd split the patch.

Quoted 25 lines
Cc Markus Armbruster <[email protected]>
Cc Eric Blake <[email protected]>
---
 iothread.c      | 17 +++++++++++------
 util/compatfd.c | 11 ++++++++---
 2 files changed, 19 insertions(+), 9 deletions(-)
iothread.c +1 -3
index 8e8aa01999..7335dacf0b 100644
@@ -164,9 +164,7 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
                                 &local_error);
     if (local_error) {
         error_propagate(errp, local_error);
-        aio_context_unref(iothread->ctx);
-        iothread->ctx = NULL;
-        return;
+        goto fail;
     }
 
     qemu_mutex_init(&iothread->init_done_lock);

iothread.c static void iothread_complete(UserCreatable *obj, Error **errp) · line 182

Quoted 10 lines
iothread.c +4 -3
@@ -178,9 +176,12 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
      */
     name = object_get_canonical_path_component(OBJECT(obj));
     thread_name = g_strdup_printf("IO %s", name);
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&iothread->thread, thread_name, iothread_run,
-                       iothread, QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&iothread->thread, thread_name, iothread_run,
+                            iothread, QEMU_THREAD_JOINABLE, errp)) {
+        g_free(thread_name);
+        g_free(name);

I suspect you're missing cleanup here:

           qemu_cond_destroy(&iothread->init_done_cond);
           qemu_mutex_destroy(&iothread->init_done_lock);

But I'm not 100% sure, to be honest. Stefan, can you help?

iothread.c static void iothread_complete(UserCreatable *obj, Error **errp) · line 187

Quoted 5 lines
iothread.c +2 -0
+        goto fail;
+    }
     g_free(thread_name);
     g_free(name);
 

I'd avoid the code duplication like this:

       thread_ok = qemu_thread_create(&iothread->thread, thread_name,
                                      iothread_run, iothread,
                                      QEMU_THREAD_JOINABLE, errp);
       g_free(thread_name);
       g_free(name);
       if (!thread_ok) {
           qemu_cond_destroy(&iothread->init_done_cond);
           qemu_mutex_destroy(&iothread->init_done_lock);
           goto fail;
       }

Matter of taste.

Hmm, iothread.c has no maintainer. Stefan, you created it, would you be willing to serve as maintainer?

Quoted 26 lines
iothread.c +4 -0
@@ -191,6 +192,10 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
                        &iothread->init_done_lock);
     }
     qemu_mutex_unlock(&iothread->init_done_lock);
+    return;
+fail:
+    aio_context_unref(iothread->ctx);
+    iothread->ctx = NULL;
 }
 
 typedef struct {
util/compatfd.c +1 -0
index c3d8448264..9cb13381e4 100644
@@ -71,6 +71,7 @@ static int qemu_signalfd_compat(const sigset_t *mask)
     struct sigfd_compat_info *info;
     QemuThread thread;
     int fds[2];
+    Error *local_err = NULL;
 
     info = malloc(sizeof(*info));
     if (info == NULL) {
@@ -89,9 +90,13 @@ static int qemu_signalfd_compat(const sigset_t *mask)
     memcpy(&info->mask, mask, sizeof(*mask));
     info->fd = fds[1];

util/compatfd.c static int qemu_signalfd_compat(const sigset_t *mask) · line 98

Quoted 10 lines
util/compatfd.c +6 -3
 
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&thread, "signalfd_compat", sigwait_compat,
-                       info, QEMU_THREAD_DETACHED, &error_abort);
+    if (!qemu_thread_create(&thread, "signalfd_compat", sigwait_compat,
+                            info, QEMU_THREAD_DETACHED, &local_err)) {
+        close(fds[0]);
+        close(fds[1]);
+        free(info);
+        return -1;

Leaks @local_err. Pass NULL instead.

Quoted 4 lines
util/compatfd.c +1 -0
+    }
 
     return fds[0];
 }
#46 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 12/16] qemu_thread: supplement error handling for iothread_complete/qemu_signalfd_compat

Quoted 10 lines · 在 2019年1月8日,01:50,Markus Armbruster <[email protected]> 写道:
Quoted 4 lines · Fei Li

For iothread_complete: utilize the existed errp to propagate the error and do the corresponding cleanup to replace the temporary &error_abort.

Quoted 5 lines
Quoted 3 lines

For qemu_signalfd_compat: add a local_err to hold the error, and return the corresponding error code to replace the temporary &error_abort.

I'd split the patch.

Ok.

Quoted 39 lines
Quoted 26 lines
Cc Markus Armbruster <[email protected]>
Cc Eric Blake <[email protected]>

---
iothread.c | 17 +++++++++++------
util/compatfd.c | 11 ++++++++---

2 files changed, 19 insertions(+), 9 deletions(-)
iothread.c +1 -3
index 8e8aa01999..7335dacf0b 100644
@@ -164,9 +164,7 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
                                &local_error);
    if (local_error) {
        error_propagate(errp, local_error);
-        aio_context_unref(iothread->ctx);
-        iothread->ctx = NULL;
-        return;
+        goto fail;
    }
 
    qemu_mutex_init(&iothread->init_done_lock);

iothread.c static void iothread_complete(UserCreatable *obj, Error **errp) · line 182

Quoted 10 lines
iothread.c +4 -3
@@ -178,9 +176,12 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
     */
    name = object_get_canonical_path_component(OBJECT(obj));
    thread_name = g_strdup_printf("IO %s", name);
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&iothread->thread, thread_name, iothread_run,
-                       iothread, QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&iothread->thread, thread_name, iothread_run,
+                            iothread, QEMU_THREAD_JOINABLE, errp)) {
+        g_free(thread_name);
+        g_free(name);

I suspect you're missing cleanup here:

Quoted 2 lines
          qemu_cond_destroy(&iothread->init_done_cond);
          qemu_mutex_destroy(&iothread->init_done_lock);

I remember I checked the code, when ucc->complete() fails, there’s a finalize() function to do the destroy. But did not test all the callers, so let’s wait for Stefan’s feedback. :)

Quoted 55 lines

But I'm not 100% sure, to be honest. Stefan, can you help?

iothread.c static void iothread_complete(UserCreatable *obj, Error **errp) · line 187

Quoted 5 lines
iothread.c +2 -0
+        goto fail;
+    }
    g_free(thread_name);
    g_free(name);
 

I'd avoid the code duplication like this:

      thread_ok = qemu_thread_create(&iothread->thread, thread_name,
                                     iothread_run, iothread,
                                     QEMU_THREAD_JOINABLE, errp);
      g_free(thread_name);
      g_free(name);
      if (!thread_ok) {
          qemu_cond_destroy(&iothread->init_done_cond);
          qemu_mutex_destroy(&iothread->init_done_lock);
          goto fail;
      }

Matter of taste.

Hmm, iothread.c has no maintainer. Stefan, you created it, would you be willing to serve as maintainer?

Quoted 27 lines
iothread.c +4 -0
@@ -191,6 +192,10 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
                       &iothread->init_done_lock);
    }
    qemu_mutex_unlock(&iothread->init_done_lock);
+    return;
+fail:
+    aio_context_unref(iothread->ctx);
+    iothread->ctx = NULL;
 }
 
 typedef struct {
util/compatfd.c +1 -0
index c3d8448264..9cb13381e4 100644
@@ -71,6 +71,7 @@ static int qemu_signalfd_compat(const sigset_t *mask)
    struct sigfd_compat_info *info;
    QemuThread thread;
    int fds[2];
+    Error *local_err = NULL;
 
    info = malloc(sizeof(*info));
    if (info == NULL) {
@@ -89,9 +90,13 @@ static int qemu_signalfd_compat(const sigset_t *mask)
    memcpy(&info->mask, mask, sizeof(*mask));
    info->fd = fds[1];
 
Quoted 11 lines

util/compatfd.c static int qemu_signalfd_compat(const sigset_t *mask) · line 98

Quoted 9 lines
util/compatfd.c +6 -3
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&thread, "signalfd_compat", sigwait_compat,
-                       info, QEMU_THREAD_DETACHED, &error_abort);
+    if (!qemu_thread_create(&thread, "signalfd_compat", sigwait_compat,
+                            info, QEMU_THREAD_DETACHED, &local_err)) {
+        close(fds[0]);
+        close(fds[1]);
+        free(info);
+        return -1;

Leaks @local_err. Pass NULL instead.

Ok, thanks!

Have a nice day
Fei

Quoted 5 lines
Quoted 5 lines
util/compatfd.c +1 -0
+    }
 
    return fds[0];
 }
#47 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 12/16] qemu_thread: supplement error handling for iothread_complete/qemu_signalfd_compat

Quoted 50 lines · fei
Quoted 13 lines · 在 2019年1月8日,01:50,Markus Armbruster <[email protected]> 写道:
Quoted 7 lines · Fei Li

For iothread_complete: utilize the existed errp to propagate the error and do the corresponding cleanup to replace the temporary &error_abort.

For qemu_signalfd_compat: add a local_err to hold the error, and return the corresponding error code to replace the temporary &error_abort.

I'd split the patch.

Ok.

Quoted 36 lines
Quoted 24 lines
Cc Markus Armbruster <[email protected]>
Cc Eric Blake <[email protected]>

---
iothread.c | 17 +++++++++++------
util/compatfd.c | 11 ++++++++---

2 files changed, 19 insertions(+), 9 deletions(-)
iothread.c +1 -3
index 8e8aa01999..7335dacf0b 100644
@@ -164,9 +164,7 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
                                 &local_error);
     if (local_error) {
         error_propagate(errp, local_error);
-        aio_context_unref(iothread->ctx);
-        iothread->ctx = NULL;
-        return;
+        goto fail;
     }
 
     qemu_mutex_init(&iothread->init_done_lock);

iothread.c static void iothread_complete(UserCreatable *obj, Error **errp) · line 182

Quoted 10 lines
iothread.c +4 -3
@@ -178,9 +176,12 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
      */
     name = object_get_canonical_path_component(OBJECT(obj));
     thread_name = g_strdup_printf("IO %s", name);
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&iothread->thread, thread_name, iothread_run,
-                       iothread, QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&iothread->thread, thread_name, iothread_run,
+                            iothread, QEMU_THREAD_JOINABLE, errp)) {
+        g_free(thread_name);
+        g_free(name);

I suspect you're missing cleanup here:

Quoted 3 lines
Quoted 2 lines
           qemu_cond_destroy(&iothread->init_done_cond);
           qemu_mutex_destroy(&iothread->init_done_lock);

I remember I checked the code, when ucc->complete() fails, there’s a finalize() function to do the destroy.

To be specific, the qemu_xxx_destroy() is called by

object_unref() => object_finalize() => object_deinit() => type->instance_finalize(obj); (that is, iothread_instance_finalize).

For the iothread_complete(), it is only called in user_creatable_complete() as ucc->complete(). I checked the code, when callers of user_creatable_complete() fails, all of them will call object_unref() to call the qemu_xxx_destroy(), except one &error_abort case (e.i. desugar_shm()).

Quoted 1 line

But did not test all the callers, so let’s wait for Stefan’s feedback. :)

But again, I did not do all the test. Correct me if I am wrong. :)

Quoted 10 lines
Quoted 10 lines

But I'm not 100% sure, to be honest. Stefan, can you help?

iothread.c static void iothread_complete(UserCreatable *obj, Error **errp) · line 187

Quoted 5 lines
iothread.c +2 -0
+        goto fail;
+    }
     g_free(thread_name);
     g_free(name);
 

I'd avoid the code duplication like this:

Quoted 10 lines
Quoted 10 lines
       thread_ok = qemu_thread_create(&iothread->thread, thread_name,
                                      iothread_run, iothread,
                                      QEMU_THREAD_JOINABLE, errp);
       g_free(thread_name);
       g_free(name);
       if (!thread_ok) {
           qemu_cond_destroy(&iothread->init_done_cond);
           qemu_mutex_destroy(&iothread->init_done_lock);
           goto fail;
       }

Ok, thanks.

Have a nice day
Fei

Quoted 5 lines
Quoted 5 lines

Matter of taste.

Hmm, iothread.c has no maintainer. Stefan, you created it, would you be willing to serve as maintainer?

#48 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 12/16] qemu_thread: supplement error handling for iothread_complete/qemu_signalfd_compat

Quoted 60 lines · Fei Li
Quoted 50 lines · fei
Quoted 13 lines · 在 2019年1月8日,01:50,Markus Armbruster <[email protected]> 写道:
Quoted 7 lines · Fei Li

For iothread_complete: utilize the existed errp to propagate the error and do the corresponding cleanup to replace the temporary &error_abort.

For qemu_signalfd_compat: add a local_err to hold the error, and return the corresponding error code to replace the temporary &error_abort.

I'd split the patch.

Ok.

Quoted 36 lines
Quoted 24 lines
Cc Markus Armbruster <[email protected]>
Cc Eric Blake <[email protected]>

---
iothread.c | 17 +++++++++++------
util/compatfd.c | 11 ++++++++---

2 files changed, 19 insertions(+), 9 deletions(-)
iothread.c +1 -3
index 8e8aa01999..7335dacf0b 100644
@@ -164,9 +164,7 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
                                 &local_error);
     if (local_error) {
         error_propagate(errp, local_error);
-        aio_context_unref(iothread->ctx);
-        iothread->ctx = NULL;
-        return;
+        goto fail;
     }
 
     qemu_mutex_init(&iothread->init_done_lock);

iothread.c static void iothread_complete(UserCreatable *obj, Error **errp) · line 182

Quoted 10 lines
iothread.c +4 -3
@@ -178,9 +176,12 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
      */
     name = object_get_canonical_path_component(OBJECT(obj));
     thread_name = g_strdup_printf("IO %s", name);
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&iothread->thread, thread_name, iothread_run,
-                       iothread, QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&iothread->thread, thread_name, iothread_run,
+                            iothread, QEMU_THREAD_JOINABLE, errp)) {
+        g_free(thread_name);
+        g_free(name);

I suspect you're missing cleanup here:

Quoted 3 lines
Quoted 2 lines
           qemu_cond_destroy(&iothread->init_done_cond);
           qemu_mutex_destroy(&iothread->init_done_lock);

I remember I checked the code, when ucc->complete() fails, there’s a finalize() function to do the destroy.

To be specific, the qemu_xxx_destroy() is called by

object_unref() => object_finalize() => object_deinit() => type->instance_finalize(obj); (that is, iothread_instance_finalize).

Quoted 6 lines

For the iothread_complete(), it is only called in user_creatable_complete() as ucc->complete().
I checked the code, when callers of user_creatable_complete() fails, all of them will call
object_unref() to call the qemu_xxx_destroy(), except one &error_abort case (e.i. desugar_shm()).

I'm not familiar with iothread.c. But like anyone capable of reading C, I can find out stuff.

iothread_instance_finalize() guards its cleanups. In particular, it cleans up ->init_done_cond and init_done_lock only when ->thread_id != -1.

iothread_instance_init() initializes ->thread_id = -1.

iothread_run() sets it to the actual thread ID.

When iothread_instance_complete() succeeds, it has waited for ->thread_id to become != -1, in the /* Wait for initialization to complete */ loop.

When it fails, ->thread_id is still -1.

Therefore, you cannot rely on iothread_instance_finalize() for cleaning up ->init_done_lock and ->init_done_cond on iothread_instance_complete() failure.

I'm pretty sure you could've figured this out yourself instead of relying on me.

[...]

#49 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 12/16] qemu_thread: supplement error handling for iothread_complete/qemu_signalfd_compat

Quoted 66 lines · Markus Armbruster
Quoted 58 lines · Fei Li
Quoted 49 lines · fei
Quoted 12 lines · 在 2019年1月8日,01:50,Markus Armbruster <[email protected]> 写道:
Quoted 7 lines · Fei Li

For iothread_complete: utilize the existed errp to propagate the error and do the corresponding cleanup to replace the temporary &error_abort.

For qemu_signalfd_compat: add a local_err to hold the error, and return the corresponding error code to replace the temporary &error_abort.

I'd split the patch.

Ok.

Quoted 36 lines
Quoted 24 lines
Cc Markus Armbruster <[email protected]>
Cc Eric Blake <[email protected]>

---
iothread.c | 17 +++++++++++------
util/compatfd.c | 11 ++++++++---

2 files changed, 19 insertions(+), 9 deletions(-)
iothread.c +1 -3
index 8e8aa01999..7335dacf0b 100644
@@ -164,9 +164,7 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
                                  &local_error);
      if (local_error) {
          error_propagate(errp, local_error);
-        aio_context_unref(iothread->ctx);
-        iothread->ctx = NULL;
-        return;
+        goto fail;
      }
 
      qemu_mutex_init(&iothread->init_done_lock);

iothread.c static void iothread_complete(UserCreatable *obj, Error **errp) · line 182

Quoted 10 lines
iothread.c +4 -3
@@ -178,9 +176,12 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
       */
      name = object_get_canonical_path_component(OBJECT(obj));
      thread_name = g_strdup_printf("IO %s", name);
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&iothread->thread, thread_name, iothread_run,
-                       iothread, QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&iothread->thread, thread_name, iothread_run,
+                            iothread, QEMU_THREAD_JOINABLE, errp)) {
+        g_free(thread_name);
+        g_free(name);

I suspect you're missing cleanup here:

Quoted 3 lines
Quoted 2 lines
            qemu_cond_destroy(&iothread->init_done_cond);
            qemu_mutex_destroy(&iothread->init_done_lock);

I remember I checked the code, when ucc->complete() fails, there’s a finalize() function to do the destroy.

To be specific, the qemu_xxx_destroy() is called by

object_unref() => object_finalize() => object_deinit() => type->instance_finalize(obj); (that is, iothread_instance_finalize).

Quoted 6 lines

For the iothread_complete(), it is only called in user_creatable_complete() as ucc->complete().
I checked the code, when callers of user_creatable_complete() fails, all of them will call
object_unref() to call the qemu_xxx_destroy(), except one &error_abort case (e.i. desugar_shm()).

Quoted 6 lines

I'm not familiar with iothread.c. But like anyone capable of reading C, I can find out stuff.

iothread_instance_finalize() guards its cleanups. In particular, it cleans up ->init_done_cond and init_done_lock only when ->thread_id != -1.

Ah, sorry that I overlooked the "if (iothread->thread_id != -1)". So embarrassed, and sorry for the trouble.. You are right, and I will add the qemu_xxx_destroy() in the next version. ;)

Have a nice day, thanks so much!
Fei

Quoted 19 lines

iothread_instance_init() initializes ->thread_id = -1.

iothread_run() sets it to the actual thread ID.

When iothread_instance_complete() succeeds, it has waited for ->thread_id to become != -1, in the /* Wait for initialization to complete */ loop.

When it fails, ->thread_id is still -1.

Therefore, you cannot rely on iothread_instance_finalize() for cleaning up ->init_done_lock and ->init_done_cond on iothread_instance_complete() failure.

I'm pretty sure you could've figured this out yourself instead of relying on me.

[...]

author
#50 lore raw

PATCH v9 13/16 qemu_thread: supplement error handling for migration

In reply to Fei Li

Update qemu_thread_create()'s callers by
- setting an error on qemu_thread_create() failure for callers that set an error on failure;
- reporting the error and returning failure for callers that return an error code on failure;
- reporting the error and setting some state for callers that just report errors and choose not to continue on.

Cc Markus Armbruster <[email protected]>
Cc Dr. David Alan Gilbert <[email protected]>
Cc Peter Xu <[email protected]>
---
 migration/migration.c    | 33 ++++++++++++++++++++++-----------
 migration/postcopy-ram.c | 16 ++++++++++++----
 migration/ram.c          | 44 ++++++++++++++++++++++++++++++--------------
 migration/savevm.c       | 12 ++++++++----
 4 files changed, 72 insertions(+), 33 deletions(-)
migration/migration.c +22 -11
index ea5839ff0d..9654bde101 100644
@@ -447,10 +447,13 @@ static void process_incoming_migration_co(void *opaque)
             goto fail;
         }
 
-        /* TODO: let the further caller handle the error instead of abort() */
-        qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming",
-                           colo_process_incoming_thread, mis,
-                           QEMU_THREAD_JOINABLE, &error_abort);
+        if (!qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming",
+                                colo_process_incoming_thread, mis,
+                                QEMU_THREAD_JOINABLE, &local_err)) {
+            error_reportf_err(local_err, "failed to create "
+                              "colo_process_incoming_thread: ");
+            goto fail;
+        }
         mis->have_colo_incoming_thread = true;
         qemu_coroutine_yield();
 
@@ -2347,6 +2350,7 @@ out:
 static int open_return_path_on_source(MigrationState *ms,
                                       bool create_thread)
 {
+    Error *local_err = NULL;
 
     ms->rp_state.from_dst_file = qemu_file_get_return_path(ms->to_dst_file);
     if (!ms->rp_state.from_dst_file) {
@@ -2360,10 +2364,13 @@ static int open_return_path_on_source(MigrationState *ms,
         return 0;
     }
 
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&ms->rp_state.rp_thread, "return path",
-                       source_return_path_thread, ms,
-                       QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&ms->rp_state.rp_thread, "return path",
+                            source_return_path_thread, ms,
+                            QEMU_THREAD_JOINABLE, &local_err)) {
+        error_reportf_err(local_err,
+                          "failed to create source_return_path_thread: ");
+        return -1;
+     }
 
     trace_open_return_path_on_source_continue();
 
@@ -3193,9 +3200,13 @@ void migrate_fd_connect(MigrationState *s, Error *error_in)
         migrate_fd_cleanup(s);
         return;
     }
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&s->thread, "live_migration", migration_thread, s,
-                       QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&s->thread, "live_migration", migration_thread, s,
+                            QEMU_THREAD_JOINABLE, &error_in)) {
+        error_reportf_err(error_in, "failed to create migration_thread: ");
+        migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
+        migrate_fd_cleanup(s);
+        return;
+    }
     s->migration_thread_running = true;
 }
 
migration/postcopy-ram.c +12 -4
index 221ea24919..80bfa9c4a2 100644
@@ -1083,6 +1083,8 @@ retry:
 
 int postcopy_ram_enable_notify(MigrationIncomingState *mis)
 {
+    Error *local_err = NULL;
+
     /* Open the fd for the kernel to give us userfaults */
     mis->userfault_fd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
     if (mis->userfault_fd == -1) {
@@ -1109,10 +1111,16 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis)
     }
 
     qemu_sem_init(&mis->fault_thread_sem, 0);
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&mis->fault_thread, "postcopy/fault",
-                       postcopy_ram_fault_thread, mis,
-                       QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&mis->fault_thread, "postcopy/fault",
+                            postcopy_ram_fault_thread, mis,
+                            QEMU_THREAD_JOINABLE, &local_err)) {
+        error_reportf_err(local_err,
+                          "failed to create postcopy_ram_fault_thread: ");
+        close(mis->userfault_event_fd);
+        close(mis->userfault_fd);
+        qemu_sem_destroy(&mis->fault_thread_sem);
+        return -1;
+    }
     qemu_sem_wait(&mis->fault_thread_sem);
     qemu_sem_destroy(&mis->fault_thread_sem);
     mis->have_fault_thread = true;
migration/ram.c +30 -14

80 lines not on this page · raw message

migration/savevm.c +8 -4
index 46ce7af239..b8bdcde5d8 100644
@@ -1747,10 +1747,14 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
     mis->have_listen_thread = true;
     /* Start up the listening thread and wait for it to signal ready */
     qemu_sem_init(&mis->listen_thread_sem, 0);
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&mis->listen_thread, "postcopy/listen",
-                       postcopy_ram_listen_thread, NULL,
-                       QEMU_THREAD_DETACHED, &error_abort);
+    if (!qemu_thread_create(&mis->listen_thread, "postcopy/listen",
+                            postcopy_ram_listen_thread, NULL,
+                            QEMU_THREAD_DETACHED, &local_err)) {
+        error_reportf_err(local_err,
+                          "failed to create postcopy_ram_listen_thread: ");
+        qemu_sem_destroy(&mis->listen_thread_sem);
+        return -1;
+    }
     qemu_sem_wait(&mis->listen_thread_sem);
     qemu_sem_destroy(&mis->listen_thread_sem);
 
Signature · 1 line
2.13.7
Reviewed-by on v9 13/16
#51 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 13/16] qemu_thread: supplement error handling for migration

Quoted 30 lines · * Fei Li ([email protected]) wrote:

Update qemu_thread_create()'s callers by
- setting an error on qemu_thread_create() failure for callers that set an error on failure;
- reporting the error and returning failure for callers that return an error code on failure;
- reporting the error and setting some state for callers that just report errors and choose not to continue on.

Cc Markus Armbruster <[email protected]>
Cc Dr. David Alan Gilbert <[email protected]>
Cc Peter Xu <[email protected]>
---
 migration/migration.c    | 33 ++++++++++++++++++++++-----------
 migration/postcopy-ram.c | 16 ++++++++++++----
 migration/ram.c          | 44 ++++++++++++++++++++++++++++++--------------
 migration/savevm.c       | 12 ++++++++----
 4 files changed, 72 insertions(+), 33 deletions(-)
migration/migration.c +0 -3
index ea5839ff0d..9654bde101 100644
@@ -447,10 +447,13 @@ static void process_incoming_migration_co(void *opaque)
             goto fail;
         }
 
-        /* TODO: let the further caller handle the error instead of abort() */
-        qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming",
-                           colo_process_incoming_thread, mis,

migration/migration.c static void process_incoming_migration_co(void *opaque) · line 458

Quoted 10 lines
migration/migration.c +7 -1
-                           QEMU_THREAD_JOINABLE, &error_abort);
+        if (!qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming",
+                                colo_process_incoming_thread, mis,
+                                QEMU_THREAD_JOINABLE, &local_err)) {
+            error_reportf_err(local_err, "failed to create "
+                              "colo_process_incoming_thread: ");
+            goto fail;
+        }
         mis->have_colo_incoming_thread = true;
         qemu_coroutine_yield();

OK

Quoted 13 lines
migration/migration.c +1 -1
@@ -2347,6 +2350,7 @@ out:
 static int open_return_path_on_source(MigrationState *ms,
                                       bool create_thread)
 {
+    Error *local_err = NULL;
 
     ms->rp_state.from_dst_file = qemu_file_get_return_path(ms->to_dst_file);
     if (!ms->rp_state.from_dst_file) {
@@ -2360,10 +2364,13 @@ static int open_return_path_on_source(MigrationState *ms,
         return 0;
     }
 
-    /* TODO: let the further caller handle the error instead of abort() here */

migration/migration.c static int open_return_path_on_source(MigrationState *ms, · line 2373

Quoted 10 lines
migration/migration.c +7 -3
-    qemu_thread_create(&ms->rp_state.rp_thread, "return path",
-                       source_return_path_thread, ms,
-                       QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&ms->rp_state.rp_thread, "return path",
+                            source_return_path_thread, ms,
+                            QEMU_THREAD_JOINABLE, &local_err)) {
+        error_reportf_err(local_err,
+                          "failed to create source_return_path_thread: ");
+        return -1;
+     }

I think that has to close the from_dst_file and set the from_dst_file=NULL. That file is owned by the thread, and it's normally the thread that cleans it up.

I think other than that missing close it's fine; and we can do that as a fix later, so:

Quoted 7 lines
migration/migration.c +0 -0
     trace_open_return_path_on_source_continue();
 
@@ -3193,9 +3200,13 @@ void migrate_fd_connect(MigrationState *s, Error *error_in)
         migrate_fd_cleanup(s);
         return;
     }

migration/migration.c void migrate_fd_connect(MigrationState *s, Error *error_in) · line 3209

Quoted 10 lines
migration/migration.c +7 -3
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&s->thread, "live_migration", migration_thread, s,
-                       QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&s->thread, "live_migration", migration_thread, s,
+                            QEMU_THREAD_JOINABLE, &error_in)) {
+        error_reportf_err(error_in, "failed to create migration_thread: ");
+        migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
+        migrate_fd_cleanup(s);
+        return;
+    }

OK

Quoted 27 lines
migration/migration.c +0 -0
     s->migration_thread_running = true;
 }
 
migration/postcopy-ram.c +5 -4
index 221ea24919..80bfa9c4a2 100644
@@ -1083,6 +1083,8 @@ retry:
 
 int postcopy_ram_enable_notify(MigrationIncomingState *mis)
 {
+    Error *local_err = NULL;
+
     /* Open the fd for the kernel to give us userfaults */
     mis->userfault_fd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
     if (mis->userfault_fd == -1) {
@@ -1109,10 +1111,16 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis)
     }
 
     qemu_sem_init(&mis->fault_thread_sem, 0);
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&mis->fault_thread, "postcopy/fault",
-                       postcopy_ram_fault_thread, mis,
-                       QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&mis->fault_thread, "postcopy/fault",
+                            postcopy_ram_fault_thread, mis,
+                            QEMU_THREAD_JOINABLE, &local_err)) {

migration/postcopy-ram.c int postcopy_ram_enable_notify(MigrationIncomingState *mis) · line 1126

Quoted 10 lines
migration/postcopy-ram.c +7 -0
+        error_reportf_err(local_err,
+                          "failed to create postcopy_ram_fault_thread: ");
+        close(mis->userfault_event_fd);
+        close(mis->userfault_fd);
+        qemu_sem_destroy(&mis->fault_thread_sem);
+        return -1;
+    }
     qemu_sem_wait(&mis->fault_thread_sem);
     qemu_sem_destroy(&mis->fault_thread_sem);
     mis->have_fault_thread = true;

OK

Quoted 16 lines
migration/ram.c +1 -0
index eed1daf302..1e24a78eaa 100644
@@ -473,6 +473,7 @@ static void compress_threads_save_cleanup(void)
 static int compress_threads_save_setup(void)
 {
     int i, thread_count;
+    Error *local_err = NULL;
 
     if (!migrate_use_compression()) {
         return 0;
@@ -502,10 +503,12 @@ static int compress_threads_save_setup(void)
         comp_param[i].quit = false;
         qemu_mutex_init(&comp_param[i].mutex);
         qemu_cond_init(&comp_param[i].cond);

migration/ram.c static int compress_threads_save_setup(void) · line 511

Quoted 10 lines
migration/ram.c +6 -4
-        /* TODO: let the further caller handle the error instead of abort() */
-        qemu_thread_create(compress_threads + i, "compress",
-                           do_data_compress, comp_param + i,
-                           QEMU_THREAD_JOINABLE, &error_abort);
+        if (!qemu_thread_create(compress_threads + i, "compress",
+                                do_data_compress, comp_param + i,
+                                QEMU_THREAD_JOINABLE, &local_err)) {
+            error_reportf_err(local_err, "failed to create do_data_compress: ");
+            goto exit;
+        }

OK

Quoted 90 lines
migration/ram.c +23 -10
     }
     return 0;
 
@@ -1076,9 +1079,14 @@ static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque)
         p->c = QIO_CHANNEL(sioc);
         qio_channel_set_delay(p->c, false);
         p->running = true;
-        /* TODO: let the further caller handle the error instead of abort() */
-        qemu_thread_create(&p->thread, p->name, multifd_send_thread, p,
-                           QEMU_THREAD_JOINABLE, &error_abort);
+        if (!qemu_thread_create(&p->thread, p->name, multifd_send_thread, p,
+                                QEMU_THREAD_JOINABLE, &local_err)) {
+            migrate_set_error(migrate_get_current(), local_err);
+            error_reportf_err(local_err,
+                              "failed to create multifd_send_thread: ");
+            multifd_save_cleanup();
+            return;
+        }
 
         atomic_inc(&multifd_send_state->count);
     }
@@ -1357,9 +1365,13 @@ bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
     p->num_packets = 1;
 
     p->running = true;
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p,
-                       QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p,
+                            QEMU_THREAD_JOINABLE, &local_err)) {
+        error_propagate_prepend(errp, local_err,
+                                "failed to create multifd_recv_thread: ");
+        multifd_recv_terminate_threads(local_err);
+        return false;
+    }
     atomic_inc(&multifd_recv_state->count);
     return atomic_read(&multifd_recv_state->count) ==
            migrate_multifd_channels();
@@ -3625,6 +3637,7 @@ static void compress_threads_load_cleanup(void)
 static int compress_threads_load_setup(QEMUFile *f)
 {
     int i, thread_count;
+    Error *local_err = NULL;
 
     if (!migrate_use_compression()) {
         return 0;
@@ -3646,10 +3659,13 @@ static int compress_threads_load_setup(QEMUFile *f)
         qemu_cond_init(&decomp_param[i].cond);
         decomp_param[i].done = true;
         decomp_param[i].quit = false;
-        /* TODO: let the further caller handle the error instead of abort() */
-        qemu_thread_create(decompress_threads + i, "decompress",
-                           do_data_decompress, decomp_param + i,
-                           QEMU_THREAD_JOINABLE, &error_abort);
+        if (!qemu_thread_create(decompress_threads + i, "decompress",
+                                do_data_decompress, decomp_param + i,
+                                QEMU_THREAD_JOINABLE, &local_err)) {
+            error_reportf_err(local_err,
+                              "failed to create do_data_decompress: ");
+            goto exit;
+        }
     }
     return 0;
 exit:
migration/savevm.c +8 -4
index 46ce7af239..b8bdcde5d8 100644
@@ -1747,10 +1747,14 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
     mis->have_listen_thread = true;
     /* Start up the listening thread and wait for it to signal ready */
     qemu_sem_init(&mis->listen_thread_sem, 0);
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&mis->listen_thread, "postcopy/listen",
-                       postcopy_ram_listen_thread, NULL,
-                       QEMU_THREAD_DETACHED, &error_abort);
+    if (!qemu_thread_create(&mis->listen_thread, "postcopy/listen",
+                            postcopy_ram_listen_thread, NULL,
+                            QEMU_THREAD_DETACHED, &local_err)) {
+        error_reportf_err(local_err,
+                          "failed to create postcopy_ram_listen_thread: ");
+        qemu_sem_destroy(&mis->listen_thread_sem);
+        return -1;
+    }
     qemu_sem_wait(&mis->listen_thread_sem);
     qemu_sem_destroy(&mis->listen_thread_sem);
 
Signature · 1 line
2.13.7

--
Dr. David Alan Gilbert / [email protected] / Manchester, UK

#52 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 13/16] qemu_thread: supplement error handling for migration

Quoted 70 lines · Dr. David Alan Gilbert
Quoted 30 lines · * Fei Li ([email protected]) wrote:

Update qemu_thread_create()'s callers by
- setting an error on qemu_thread_create() failure for callers that set an error on failure;
- reporting the error and returning failure for callers that return an error code on failure;
- reporting the error and setting some state for callers that just report errors and choose not to continue on.

Cc Markus Armbruster <[email protected]>
Cc Dr. David Alan Gilbert <[email protected]>
Cc Peter Xu <[email protected]>
---
  migration/migration.c    | 33 ++++++++++++++++++++++-----------
  migration/postcopy-ram.c | 16 ++++++++++++----
  migration/ram.c          | 44 ++++++++++++++++++++++++++++++--------------
  migration/savevm.c       | 12 ++++++++----
  4 files changed, 72 insertions(+), 33 deletions(-)
migration/migration.c +0 -3
index ea5839ff0d..9654bde101 100644
@@ -447,10 +447,13 @@ static void process_incoming_migration_co(void *opaque)
              goto fail;
          }
 
-        /* TODO: let the further caller handle the error instead of abort() */
-        qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming",
-                           colo_process_incoming_thread, mis,

migration/migration.c static void process_incoming_migration_co(void *opaque) · line 458

Quoted 10 lines
migration/migration.c +7 -1
-                           QEMU_THREAD_JOINABLE, &error_abort);
+        if (!qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming",
+                                colo_process_incoming_thread, mis,
+                                QEMU_THREAD_JOINABLE, &local_err)) {
+            error_reportf_err(local_err, "failed to create "
+                              "colo_process_incoming_thread: ");
+            goto fail;
+        }
          mis->have_colo_incoming_thread = true;
          qemu_coroutine_yield();

OK

Quoted 13 lines
migration/migration.c +1 -1
@@ -2347,6 +2350,7 @@ out:
  static int open_return_path_on_source(MigrationState *ms,
                                        bool create_thread)
  {
+    Error *local_err = NULL;
 
      ms->rp_state.from_dst_file = qemu_file_get_return_path(ms->to_dst_file);
      if (!ms->rp_state.from_dst_file) {
@@ -2360,10 +2364,13 @@ static int open_return_path_on_source(MigrationState *ms,
          return 0;
      }
 
-    /* TODO: let the further caller handle the error instead of abort() here */

migration/migration.c static int open_return_path_on_source(MigrationState *ms, · line 2373

Quoted 10 lines
migration/migration.c +7 -3
-    qemu_thread_create(&ms->rp_state.rp_thread, "return path",
-                       source_return_path_thread, ms,
-                       QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&ms->rp_state.rp_thread, "return path",
+                            source_return_path_thread, ms,
+                            QEMU_THREAD_JOINABLE, &local_err)) {
+        error_reportf_err(local_err,
+                          "failed to create source_return_path_thread: ");
+        return -1;
+     }

I think that has to close the from_dst_file and set the from_dst_file=NULL. That file is owned by the thread, and it's normally the thread that cleans it up.

Quoted 5 lines

I think other than that missing close it's fine; and we can do that as a fix later, so:

Ok, I will add the cleanup for the from_dst_file in the next version.

Thanks for the review! Have a nice day :)

Fei

Quoted 179 lines
Quoted 8 lines
migration/migration.c +0 -0
      trace_open_return_path_on_source_continue();
 
@@ -3193,9 +3200,13 @@ void migrate_fd_connect(MigrationState *s, Error *error_in)
          migrate_fd_cleanup(s);
          return;
      }

migration/migration.c void migrate_fd_connect(MigrationState *s, Error *error_in) · line 3209

Quoted 10 lines
migration/migration.c +7 -3
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&s->thread, "live_migration", migration_thread, s,
-                       QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&s->thread, "live_migration", migration_thread, s,
+                            QEMU_THREAD_JOINABLE, &error_in)) {
+        error_reportf_err(error_in, "failed to create migration_thread: ");
+        migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
+        migrate_fd_cleanup(s);
+        return;
+    }

OK

Quoted 27 lines
migration/migration.c +0 -0
      s->migration_thread_running = true;
  }
 
migration/postcopy-ram.c +5 -4
index 221ea24919..80bfa9c4a2 100644
@@ -1083,6 +1083,8 @@ retry:
 
  int postcopy_ram_enable_notify(MigrationIncomingState *mis)
  {
+    Error *local_err = NULL;
+
      /* Open the fd for the kernel to give us userfaults */
      mis->userfault_fd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
      if (mis->userfault_fd == -1) {
@@ -1109,10 +1111,16 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis)
      }
 
      qemu_sem_init(&mis->fault_thread_sem, 0);
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&mis->fault_thread, "postcopy/fault",
-                       postcopy_ram_fault_thread, mis,
-                       QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&mis->fault_thread, "postcopy/fault",
+                            postcopy_ram_fault_thread, mis,
+                            QEMU_THREAD_JOINABLE, &local_err)) {

migration/postcopy-ram.c int postcopy_ram_enable_notify(MigrationIncomingState *mis) · line 1126

Quoted 10 lines
migration/postcopy-ram.c +7 -0
+        error_reportf_err(local_err,
+                          "failed to create postcopy_ram_fault_thread: ");
+        close(mis->userfault_event_fd);
+        close(mis->userfault_fd);
+        qemu_sem_destroy(&mis->fault_thread_sem);
+        return -1;
+    }
      qemu_sem_wait(&mis->fault_thread_sem);
      qemu_sem_destroy(&mis->fault_thread_sem);
      mis->have_fault_thread = true;

OK

Quoted 16 lines
migration/ram.c +1 -0
index eed1daf302..1e24a78eaa 100644
@@ -473,6 +473,7 @@ static void compress_threads_save_cleanup(void)
  static int compress_threads_save_setup(void)
  {
      int i, thread_count;
+    Error *local_err = NULL;
 
      if (!migrate_use_compression()) {
          return 0;
@@ -502,10 +503,12 @@ static int compress_threads_save_setup(void)
          comp_param[i].quit = false;
          qemu_mutex_init(&comp_param[i].mutex);
          qemu_cond_init(&comp_param[i].cond);

migration/ram.c static int compress_threads_save_setup(void) · line 511

Quoted 10 lines
migration/ram.c +6 -4
-        /* TODO: let the further caller handle the error instead of abort() */
-        qemu_thread_create(compress_threads + i, "compress",
-                           do_data_compress, comp_param + i,
-                           QEMU_THREAD_JOINABLE, &error_abort);
+        if (!qemu_thread_create(compress_threads + i, "compress",
+                                do_data_compress, comp_param + i,
+                                QEMU_THREAD_JOINABLE, &local_err)) {
+            error_reportf_err(local_err, "failed to create do_data_compress: ");
+            goto exit;
+        }

OK

Quoted 90 lines
migration/ram.c +23 -10
      }
      return 0;
 
@@ -1076,9 +1079,14 @@ static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque)
          p->c = QIO_CHANNEL(sioc);
          qio_channel_set_delay(p->c, false);
          p->running = true;
-        /* TODO: let the further caller handle the error instead of abort() */
-        qemu_thread_create(&p->thread, p->name, multifd_send_thread, p,
-                           QEMU_THREAD_JOINABLE, &error_abort);
+        if (!qemu_thread_create(&p->thread, p->name, multifd_send_thread, p,
+                                QEMU_THREAD_JOINABLE, &local_err)) {
+            migrate_set_error(migrate_get_current(), local_err);
+            error_reportf_err(local_err,
+                              "failed to create multifd_send_thread: ");
+            multifd_save_cleanup();
+            return;
+        }
 
          atomic_inc(&multifd_send_state->count);
      }
@@ -1357,9 +1365,13 @@ bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
      p->num_packets = 1;
 
      p->running = true;
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p,
-                       QEMU_THREAD_JOINABLE, &error_abort);
+    if (!qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p,
+                            QEMU_THREAD_JOINABLE, &local_err)) {
+        error_propagate_prepend(errp, local_err,
+                                "failed to create multifd_recv_thread: ");
+        multifd_recv_terminate_threads(local_err);
+        return false;
+    }
      atomic_inc(&multifd_recv_state->count);
      return atomic_read(&multifd_recv_state->count) ==
             migrate_multifd_channels();
@@ -3625,6 +3637,7 @@ static void compress_threads_load_cleanup(void)
  static int compress_threads_load_setup(QEMUFile *f)
  {
      int i, thread_count;
+    Error *local_err = NULL;
 
      if (!migrate_use_compression()) {
          return 0;
@@ -3646,10 +3659,13 @@ static int compress_threads_load_setup(QEMUFile *f)
          qemu_cond_init(&decomp_param[i].cond);
          decomp_param[i].done = true;
          decomp_param[i].quit = false;
-        /* TODO: let the further caller handle the error instead of abort() */
-        qemu_thread_create(decompress_threads + i, "decompress",
-                           do_data_decompress, decomp_param + i,
-                           QEMU_THREAD_JOINABLE, &error_abort);
+        if (!qemu_thread_create(decompress_threads + i, "decompress",
+                                do_data_decompress, decomp_param + i,
+                                QEMU_THREAD_JOINABLE, &local_err)) {
+            error_reportf_err(local_err,
+                              "failed to create do_data_decompress: ");
+            goto exit;
+        }
      }
      return 0;
  exit:
migration/savevm.c +8 -4
index 46ce7af239..b8bdcde5d8 100644
@@ -1747,10 +1747,14 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
      mis->have_listen_thread = true;
      /* Start up the listening thread and wait for it to signal ready */
      qemu_sem_init(&mis->listen_thread_sem, 0);
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&mis->listen_thread, "postcopy/listen",
-                       postcopy_ram_listen_thread, NULL,
-                       QEMU_THREAD_DETACHED, &error_abort);
+    if (!qemu_thread_create(&mis->listen_thread, "postcopy/listen",
+                            postcopy_ram_listen_thread, NULL,
+                            QEMU_THREAD_DETACHED, &local_err)) {
+        error_reportf_err(local_err,
+                          "failed to create postcopy_ram_listen_thread: ");
+        qemu_sem_destroy(&mis->listen_thread_sem);
+        return -1;
+    }
      qemu_sem_wait(&mis->listen_thread_sem);
      qemu_sem_destroy(&mis->listen_thread_sem);
 
Signature · 1 line
2.13.7

--
Dr. David Alan Gilbert / [email protected] / Manchester, UK

#53 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 13/16] qemu_thread: supplement error handling for migration

In reply to Fei Li
Quoted 12 lines · Fei Li

Update qemu_thread_create()'s callers by
- setting an error on qemu_thread_create() failure for callers that set an error on failure;
- reporting the error and returning failure for callers that return an error code on failure;
- reporting the error and setting some state for callers that just report errors and choose not to continue on.

Cc Markus Armbruster <[email protected]>
Cc Dr. David Alan Gilbert <[email protected]>
Cc Peter Xu <[email protected]>

[...]

Quoted 4 lines
migration/ram.c +0 -0
index eed1daf302..1e24a78eaa 100644

[...]

Quoted 12 lines
migration/ram.c +1 -0
@@ -3625,6 +3637,7 @@ static void compress_threads_load_cleanup(void)
 static int compress_threads_load_setup(QEMUFile *f)
 {
     int i, thread_count;
+    Error *local_err = NULL;
 
     if (!migrate_use_compression()) {
         return 0;
@@ -3646,10 +3659,13 @@ static int compress_threads_load_setup(QEMUFile *f)
         qemu_cond_init(&decomp_param[i].cond);
         decomp_param[i].done = true;
         decomp_param[i].quit = false;

migration/ram.c static int compress_threads_load_setup(QEMUFile *f) · line 3667

Quoted 10 lines
migration/ram.c +6 -4
-        /* TODO: let the further caller handle the error instead of abort() */
-        qemu_thread_create(decompress_threads + i, "decompress",
-                           do_data_decompress, decomp_param + i,
-                           QEMU_THREAD_JOINABLE, &error_abort);
+        if (!qemu_thread_create(decompress_threads + i, "decompress",
+                                do_data_decompress, decomp_param + i,
+                                QEMU_THREAD_JOINABLE, &local_err)) {
+            error_reportf_err(local_err,
+                              "failed to create do_data_decompress: ");
+            goto exit;

Broken error handling, see my review of PATCH 16.

Quoted 4 lines
migration/ram.c +1 -0
+        }
     }
     return 0;
 exit:

[...]

#54 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 13/16] qemu_thread: supplement error handling for migration

Quoted 40 lines · 在 2019年1月9日,23:26,Markus Armbruster <[email protected]> 写道:
Quoted 12 lines · Fei Li

Update qemu_thread_create()'s callers by
- setting an error on qemu_thread_create() failure for callers that set an error on failure;
- reporting the error and returning failure for callers that return an error code on failure;
- reporting the error and setting some state for callers that just report errors and choose not to continue on.

Cc Markus Armbruster <[email protected]>
Cc Dr. David Alan Gilbert <[email protected]>
Cc Peter Xu <[email protected]>

[...]

Quoted 4 lines
migration/ram.c +0 -0
index eed1daf302..1e24a78eaa 100644

[...]

Quoted 16 lines
migration/ram.c +1 -4
@@ -3625,6 +3637,7 @@ static void compress_threads_load_cleanup(void)
 static int compress_threads_load_setup(QEMUFile *f)
 {
    int i, thread_count;
+    Error *local_err = NULL;
 
    if (!migrate_use_compression()) {
        return 0;
@@ -3646,10 +3659,13 @@ static int compress_threads_load_setup(QEMUFile *f)
        qemu_cond_init(&decomp_param[i].cond);
        decomp_param[i].done = true;
        decomp_param[i].quit = false;
-        /* TODO: let the further caller handle the error instead of abort() */
-        qemu_thread_create(decompress_threads + i, "decompress",
-                           do_data_decompress, decomp_param + i,
-                           QEMU_THREAD_JOINABLE, &error_abort);
Quoted 8 lines

migration/ram.c static int compress_threads_load_setup(QEMUFile *f) · line 3667

Quoted 6 lines
migration/ram.c +6 -0
+        if (!qemu_thread_create(decompress_threads + i, "decompress",
+                                do_data_decompress, decomp_param + i,
+                                QEMU_THREAD_JOINABLE, &local_err)) {
+            error_reportf_err(local_err,
+                              "failed to create do_data_decompress: ");
+            goto exit;

Broken error handling, see my review of PATCH 16.

Yep, seems both the compress_threads_save_setup() and compress_threads_load_setup() have such problem.

Quoted 6 lines
Quoted 5 lines
migration/ram.c +1 -0
+        }
    }
    return 0;

exit:

[...]

author
#55 lore raw

PATCH v9 14/16 qemu_thread: supplement error handling for vnc_start_worker_thread

In reply to Fei Li

Supplement the error handling for vnc_thread_worker_thread: add an Error parameter for it to propagate the error to its caller to handle in case it fails, and make it return a Boolean to indicate whether it succeeds.

Cc Markus Armbruster <[email protected]>
Cc Gerd Hoffmann <[email protected]>
---
 ui/vnc-jobs.c | 17 +++++++++++------
 ui/vnc-jobs.h |  2 +-
 ui/vnc.c      |  4 +++-
 3 files changed, 15 insertions(+), 8 deletions(-)
ui/vnc-jobs.c +11 -6
index 5712f1f501..35a652d1fd 100644
@@ -332,16 +332,21 @@ static bool vnc_worker_thread_running(void)
     return queue; /* Check global queue */
 }
 
-void vnc_start_worker_thread(void)
+bool vnc_start_worker_thread(Error **errp)
 {
     VncJobQueue *q;
 
-    if (vnc_worker_thread_running())
-        return ;
+    if (vnc_worker_thread_running()) {
+        goto out;
+    }
 
     q = vnc_queue_init();
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&q->thread, "vnc_worker", vnc_worker_thread,
-                       q, QEMU_THREAD_DETACHED, &error_abort);
+    if (!qemu_thread_create(&q->thread, "vnc_worker", vnc_worker_thread,
+                            q, QEMU_THREAD_DETACHED, errp)) {
+        vnc_queue_clear(q);
+        return false;
+    }
     queue = q; /* Set global queue */
+out:
+    return true;
 }
ui/vnc-jobs.h +1 -1
index 59f66bcc35..14640593db 100644
@@ -37,7 +37,7 @@ void vnc_job_push(VncJob *job);
 void vnc_jobs_join(VncState *vs);
 
 void vnc_jobs_consume_buffer(VncState *vs);
-void vnc_start_worker_thread(void);
+bool vnc_start_worker_thread(Error **errp);
 
 /* Locks */
 static inline int vnc_trylock_display(VncDisplay *vd)
ui/vnc.c +3 -1
index 0c1b477425..0ffe9e6a5d 100644
@@ -3236,7 +3236,9 @@ void vnc_display_init(const char *id, Error **errp)
     vd->connections_limit = 32;
 
     qemu_mutex_init(&vd->mutex);
-    vnc_start_worker_thread();
+    if (!vnc_start_worker_thread(errp)) {
+        return;
+    }
 
     vd->dcl.ops = &dcl_ops;
     register_displaychangelistener(&vd->dcl);
Signature · 1 line
2.13.7
#56 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 14/16] qemu_thread: supplement error handling for vnc_start_worker_thread

Quoted 21 lines · Fei Li

Supplement the error handling for vnc_thread_worker_thread: add an Error parameter for it to propagate the error to its caller to handle in case it fails, and make it return a Boolean to indicate whether it succeeds.

Cc Markus Armbruster <[email protected]>
Cc Gerd Hoffmann <[email protected]>
---
 ui/vnc-jobs.c | 17 +++++++++++------
 ui/vnc-jobs.h |  2 +-
 ui/vnc.c      |  4 +++-
 3 files changed, 15 insertions(+), 8 deletions(-)
ui/vnc-jobs.c +0 -0
index 5712f1f501..35a652d1fd 100644
@@ -332,16 +332,21 @@ static bool vnc_worker_thread_running(void)
     return queue; /* Check global queue */
 }

ui/vnc-jobs.c static bool vnc_worker_thread_running(void) · line 340

Quoted 10 lines
ui/vnc-jobs.c +3 -3
 
-void vnc_start_worker_thread(void)
+bool vnc_start_worker_thread(Error **errp)
 {
     VncJobQueue *q;
 
-    if (vnc_worker_thread_running())
-        return ;
+    if (vnc_worker_thread_running()) {
+        goto out;

Why not simply return true?

Quoted 43 lines
ui/vnc-jobs.c +8 -3
+    }
 
     q = vnc_queue_init();
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&q->thread, "vnc_worker", vnc_worker_thread,
-                       q, QEMU_THREAD_DETACHED, &error_abort);
+    if (!qemu_thread_create(&q->thread, "vnc_worker", vnc_worker_thread,
+                            q, QEMU_THREAD_DETACHED, errp)) {
+        vnc_queue_clear(q);
+        return false;
+    }
     queue = q; /* Set global queue */
+out:
+    return true;
 }
ui/vnc-jobs.h +1 -1
index 59f66bcc35..14640593db 100644
@@ -37,7 +37,7 @@ void vnc_job_push(VncJob *job);
 void vnc_jobs_join(VncState *vs);
 
 void vnc_jobs_consume_buffer(VncState *vs);
-void vnc_start_worker_thread(void);
+bool vnc_start_worker_thread(Error **errp);
 
 /* Locks */
 static inline int vnc_trylock_display(VncDisplay *vd)
ui/vnc.c +3 -1
index 0c1b477425..0ffe9e6a5d 100644
@@ -3236,7 +3236,9 @@ void vnc_display_init(const char *id, Error **errp)
     vd->connections_limit = 32;
 
     qemu_mutex_init(&vd->mutex);
-    vnc_start_worker_thread();
+    if (!vnc_start_worker_thread(errp)) {
+        return;
+    }
 
     vd->dcl.ops = &dcl_ops;
     register_displaychangelistener(&vd->dcl);
#57 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 14/16] qemu_thread: supplement error handling for vnc_start_worker_thread

Quoted 33 lines · 在 2019年1月8日,01:54,Markus Armbruster <[email protected]> 写道:
Quoted 27 lines · Fei Li

Supplement the error handling for vnc_thread_worker_thread: add an Error parameter for it to propagate the error to its caller to handle in case it fails, and make it return a Boolean to indicate whether it succeeds.

Cc Markus Armbruster <[email protected]>
Cc Gerd Hoffmann <[email protected]>

---

ui/vnc-jobs.c | 17 +++++++++++------
ui/vnc-jobs.h |  2 +-
ui/vnc.c      |  4 +++-
3 files changed, 15 insertions(+), 8 deletions(-)
ui/vnc-jobs.c +1 -1
index 5712f1f501..35a652d1fd 100644
@@ -332,16 +332,21 @@ static bool vnc_worker_thread_running(void)
    return queue; /* Check global queue */
 }
 
-void vnc_start_worker_thread(void)
+bool vnc_start_worker_thread(Error **errp)
 {
    VncJobQueue *q;
 
Quoted 6 lines

ui/vnc-jobs.c static bool vnc_worker_thread_running(void) · line 340

Quoted 4 lines
ui/vnc-jobs.c +2 -2
-    if (vnc_worker_thread_running())
-        return ;
+    if (vnc_worker_thread_running()) {
+        goto out;

Why not simply return true?

Sounds right.. Will remove the below “out:” too.

Have a nice day, thanks
Fei

Quoted 44 lines
Quoted 44 lines
ui/vnc-jobs.c +8 -3
+    }
 
    q = vnc_queue_init();
-    /* TODO: let the further caller handle the error instead of abort() here */
-    qemu_thread_create(&q->thread, "vnc_worker", vnc_worker_thread,
-                       q, QEMU_THREAD_DETACHED, &error_abort);
+    if (!qemu_thread_create(&q->thread, "vnc_worker", vnc_worker_thread,
+                            q, QEMU_THREAD_DETACHED, errp)) {
+        vnc_queue_clear(q);
+        return false;
+    }
    queue = q; /* Set global queue */
+out:
+    return true;
 }
ui/vnc-jobs.h +1 -1
index 59f66bcc35..14640593db 100644
@@ -37,7 +37,7 @@ void vnc_job_push(VncJob *job);
 void vnc_jobs_join(VncState *vs);
 
 void vnc_jobs_consume_buffer(VncState *vs);
-void vnc_start_worker_thread(void);
+bool vnc_start_worker_thread(Error **errp);
 
 /* Locks */
 static inline int vnc_trylock_display(VncDisplay *vd)
ui/vnc.c +3 -1
index 0c1b477425..0ffe9e6a5d 100644
@@ -3236,7 +3236,9 @@ void vnc_display_init(const char *id, Error **errp)
    vd->connections_limit = 32;
 
    qemu_mutex_init(&vd->mutex);
-    vnc_start_worker_thread();
+    if (!vnc_start_worker_thread(errp)) {
+        return;
+    }
 
    vd->dcl.ops = &dcl_ops;
    register_displaychangelistener(&vd->dcl);
author
#58 lore raw

PATCH v9 15/16 qemu_thread: supplement error handling for touch_all_pages

In reply to Fei Li

Supplement the error handling for touch_all_pages: add an Error parameter for it to propagate the error to its caller to do the handling in case it fails.

Cc Markus Armbruster <[email protected]>
---
 util/oslib-posix.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)
util/oslib-posix.c +16 -9
index 251e2f1aea..afc1d99093 100644
@@ -431,15 +431,17 @@ static inline int get_memset_num_threads(int smp_cpus)
 }
 
 static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages,
-                            int smp_cpus)
+                            int smp_cpus, Error **errp)
 {
     size_t numpages_per_thread;
     size_t size_per_thread;
     char *addr = area;
     int i = 0;
+    int started_thread = 0;
 
     memset_thread_failed = false;
     memset_num_threads = get_memset_num_threads(smp_cpus);
+    started_thread = memset_num_threads;
     memset_thread = g_new0(MemsetThread, memset_num_threads);
     numpages_per_thread = (numpages / memset_num_threads);
     size_per_thread = (hpagesize * numpages_per_thread);
@@ -448,14 +450,18 @@ static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages,
         memset_thread[i].numpages = (i == (memset_num_threads - 1)) ?
                                     numpages : numpages_per_thread;
         memset_thread[i].hpagesize = hpagesize;
-        /* TODO: let the callers handle the error instead of abort() here */
-        qemu_thread_create(&memset_thread[i].pgthread, "touch_pages",
-                           do_touch_pages, &memset_thread[i],
-                           QEMU_THREAD_JOINABLE, &error_abort);
+        if (!qemu_thread_create(&memset_thread[i].pgthread, "touch_pages",
+                                do_touch_pages, &memset_thread[i],
+                                QEMU_THREAD_JOINABLE, errp)) {
+            memset_thread_failed = true;
+            started_thread = i;
+            goto out;
+        }
         addr += size_per_thread;
         numpages -= numpages_per_thread;
     }
-    for (i = 0; i < memset_num_threads; i++) {
+out:
+    for (i = 0; i < started_thread; i++) {
         qemu_thread_join(&memset_thread[i].pgthread);
     }
     g_free(memset_thread);
@@ -471,6 +477,7 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus,
     struct sigaction act, oldact;
     size_t hpagesize = qemu_fd_getpagesize(fd);
     size_t numpages = DIV_ROUND_UP(memory, hpagesize);
+    Error *local_err = NULL;
 
     memset(&act, 0, sizeof(act));
     act.sa_handler = &sigbus_handler;
@@ -484,9 +491,9 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus,
     }
 
     /* touch pages simultaneously */
-    if (touch_all_pages(area, hpagesize, numpages, smp_cpus)) {
-        error_setg(errp, "os_mem_prealloc: Insufficient free host memory "
-            "pages available to allocate guest RAM");
+    if (touch_all_pages(area, hpagesize, numpages, smp_cpus, &local_err)) {
+        error_propagate_prepend(errp, local_err, "os_mem_prealloc: Insufficient"
+            " free host memory pages available to allocate guest RAM: ");
     }
 
     ret = sigaction(SIGBUS, &oldact, NULL);
Signature · 1 line
2.13.7
#59 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 15/16] qemu_thread: supplement error handling for touch_all_pages

Quoted 37 lines · Fei Li

Supplement the error handling for touch_all_pages: add an Error parameter for it to propagate the error to its caller to do the handling in case it fails.

Cc Markus Armbruster <[email protected]>
---
 util/oslib-posix.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)
util/oslib-posix.c +3 -1
index 251e2f1aea..afc1d99093 100644
@@ -431,15 +431,17 @@ static inline int get_memset_num_threads(int smp_cpus)
 }
 
 static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages,
-                            int smp_cpus)
+                            int smp_cpus, Error **errp)
 {
     size_t numpages_per_thread;
     size_t size_per_thread;
     char *addr = area;
     int i = 0;
+    int started_thread = 0;
 
     memset_thread_failed = false;
     memset_num_threads = get_memset_num_threads(smp_cpus);
+    started_thread = memset_num_threads;
     memset_thread = g_new0(MemsetThread, memset_num_threads);
     numpages_per_thread = (numpages / memset_num_threads);
     size_per_thread = (hpagesize * numpages_per_thread);
@@ -448,14 +450,18 @@ static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages,
         memset_thread[i].numpages = (i == (memset_num_threads - 1)) ?
                                     numpages : numpages_per_thread;
         memset_thread[i].hpagesize = hpagesize;

util/oslib-posix.c static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages, · line 458

Quoted 10 lines
util/oslib-posix.c +6 -4
-        /* TODO: let the callers handle the error instead of abort() here */
-        qemu_thread_create(&memset_thread[i].pgthread, "touch_pages",
-                           do_touch_pages, &memset_thread[i],
-                           QEMU_THREAD_JOINABLE, &error_abort);
+        if (!qemu_thread_create(&memset_thread[i].pgthread, "touch_pages",
+                                do_touch_pages, &memset_thread[i],
+                                QEMU_THREAD_JOINABLE, errp)) {
+            memset_thread_failed = true;
+            started_thread = i;
+            goto out;

break rather than goto, please.

util/oslib-posix.c static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages, · line 466

Quoted 9 lines
util/oslib-posix.c +3 -1
+        }
         addr += size_per_thread;
         numpages -= numpages_per_thread;
     }
-    for (i = 0; i < memset_num_threads; i++) {
+out:
+    for (i = 0; i < started_thread; i++) {
         qemu_thread_join(&memset_thread[i].pgthread);
     }

I don't like how @started_thread is computed. The name suggests it's the number of threads started so far. That's the case when you initialize it to zero. But then you immediately set it to memset_thread(). It again becomes the case only when you break the loop on error, or when you complete it successfully.

There's no need for @started_thread, since the number of threads created is readily available as @i:

       memset_num_threads = i;
       for (i = 0; i < memset_num_threads; i++) {
           qemu_thread_join(&memset_thread[i].pgthread);
       }

Rest of the function:

util/oslib-posix.c static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages, · line 467

Quoted 1 line
util/oslib-posix.c +0 -0
     g_free(memset_thread);
       memset_thread = NULL;

       return memset_thread_failed;

}

If do_touch_pages() set memset_thread_failed(), we return false without setting an error. I believe you should

       if (memset_thread_failed) {
           error_setg(errp, "os_mem_prealloc: Insufficient free host memory "
               "pages available to allocate guest RAM");
           return false;
       }
       return true;

here, and ...

Quoted 9 lines
util/oslib-posix.c +1 -0
@@ -471,6 +477,7 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus,
     struct sigaction act, oldact;
     size_t hpagesize = qemu_fd_getpagesize(fd);
     size_t numpages = DIV_ROUND_UP(memory, hpagesize);
+    Error *local_err = NULL;
 
     memset(&act, 0, sizeof(act));
     act.sa_handler = &sigbus_handler;

util/oslib-posix.c void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus, · line 497

Quoted 10 lines
util/oslib-posix.c +3 -3
@@ -484,9 +491,9 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus,
     }
 
     /* touch pages simultaneously */
-    if (touch_all_pages(area, hpagesize, numpages, smp_cpus)) {
-        error_setg(errp, "os_mem_prealloc: Insufficient free host memory "
-            "pages available to allocate guest RAM");
+    if (touch_all_pages(area, hpagesize, numpages, smp_cpus, &local_err)) {
+        error_propagate_prepend(errp, local_err, "os_mem_prealloc: Insufficient"
+            " free host memory pages available to allocate guest RAM: ");
     }

... not mess with the error message here, i.e.

       touch_all_pages(area, hpagesize, numpages, smp_cpus), errp);
Quoted 2 lines
util/oslib-posix.c +0 -0
     ret = sigaction(SIGBUS, &oldact, NULL);
#60 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 15/16] qemu_thread: supplement error handling for touch_all_pages

Quoted 47 lines · 在 2019年1月8日,02:13,Markus Armbruster <[email protected]> 写道:
Quoted 41 lines · Fei Li

Supplement the error handling for touch_all_pages: add an Error parameter for it to propagate the error to its caller to do the handling in case it fails.

Cc Markus Armbruster <[email protected]>

---
util/oslib-posix.c | 25 ++++++++++++++++---------

1 file changed, 16 insertions(+), 9 deletions(-)
util/oslib-posix.c +3 -5
index 251e2f1aea..afc1d99093 100644
@@ -431,15 +431,17 @@ static inline int get_memset_num_threads(int smp_cpus)
 }
 
 static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages,
-                            int smp_cpus)
+                            int smp_cpus, Error **errp)
 {
    size_t numpages_per_thread;
    size_t size_per_thread;
    char *addr = area;
    int i = 0;
+    int started_thread = 0;
 
    memset_thread_failed = false;
    memset_num_threads = get_memset_num_threads(smp_cpus);
+    started_thread = memset_num_threads;
    memset_thread = g_new0(MemsetThread, memset_num_threads);
    numpages_per_thread = (numpages / memset_num_threads);
    size_per_thread = (hpagesize * numpages_per_thread);
@@ -448,14 +450,18 @@ static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages,
        memset_thread[i].numpages = (i == (memset_num_threads - 1)) ?
                                    numpages : numpages_per_thread;
        memset_thread[i].hpagesize = hpagesize;
-        /* TODO: let the callers handle the error instead of abort() here */
-        qemu_thread_create(&memset_thread[i].pgthread, "touch_pages",
-                           do_touch_pages, &memset_thread[i],
-                           QEMU_THREAD_JOINABLE, &error_abort);
Quoted 8 lines

util/oslib-posix.c static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages, · line 458

Quoted 6 lines
util/oslib-posix.c +6 -0
+        if (!qemu_thread_create(&memset_thread[i].pgthread, "touch_pages",
+                                do_touch_pages, &memset_thread[i],
+                                QEMU_THREAD_JOINABLE, errp)) {
+            memset_thread_failed = true;
+            started_thread = i;
+            goto out;

break rather than goto, please.

Ok

Quoted 17 lines

util/oslib-posix.c static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages, · line 466

Quoted 10 lines
util/oslib-posix.c +3 -1
+        }
        addr += size_per_thread;
        numpages -= numpages_per_thread;
    }
-    for (i = 0; i < memset_num_threads; i++) {
+out:
+    for (i = 0; i < started_thread; i++) {
        qemu_thread_join(&memset_thread[i].pgthread);
    }

I don't like how @started_thread is computed. The name suggests it's the number of threads started so far. That's the case when you initialize it to zero. But then you immediately set it to memset_thread(). It again becomes the case only when you break the loop on error, or when you complete it successfully.

Quoted 4 lines

There's no need for @started_thread, since the number of threads created is readily available as @i:

      memset_num_threads = i;

Thanks for this wonderful suggestion! This helps a lot! ;)

Quoted 15 lines
      for (i = 0; i < memset_num_threads; i++) {
          qemu_thread_join(&memset_thread[i].pgthread);
      }

Rest of the function:

util/oslib-posix.c static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages, · line 467

Quoted 1 line
util/oslib-posix.c +0 -0
    g_free(memset_thread);
      memset_thread = NULL;

      return memset_thread_failed;

}

If do_touch_pages() set memset_thread_failed(), we return false without setting an error. I believe you should

Quoted 7 lines
      if (memset_thread_failed) {
          error_setg(errp, "os_mem_prealloc: Insufficient free host memory "
              "pages available to allocate guest RAM");
          return false;
      }
      return true;

Ok

Quoted 24 lines

here, and ...

Quoted 9 lines
util/oslib-posix.c +1 -0
@@ -471,6 +477,7 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus,
    struct sigaction act, oldact;
    size_t hpagesize = qemu_fd_getpagesize(fd);
    size_t numpages = DIV_ROUND_UP(memory, hpagesize);
+    Error *local_err = NULL;
 
    memset(&act, 0, sizeof(act));
    act.sa_handler = &sigbus_handler;

util/oslib-posix.c void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus, · line 497

Quoted 10 lines
util/oslib-posix.c +3 -3
@@ -484,9 +491,9 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus,
    }
 
    /* touch pages simultaneously */
-    if (touch_all_pages(area, hpagesize, numpages, smp_cpus)) {
-        error_setg(errp, "os_mem_prealloc: Insufficient free host memory "
-            "pages available to allocate guest RAM");
+    if (touch_all_pages(area, hpagesize, numpages, smp_cpus, &local_err)) {
+        error_propagate_prepend(errp, local_err, "os_mem_prealloc: Insufficient"
+            " free host memory pages available to allocate guest RAM: ");
    }

... not mess with the error message here, i.e.

Quoted 1 line
      touch_all_pages(area, hpagesize, numpages, smp_cpus), errp);

Ok, will amend this in the next version.

Have a nice day, thanks again
Fei

Quoted 3 lines
Quoted 3 lines
util/oslib-posix.c +0 -0
    ret = sigaction(SIGBUS, &oldact, NULL);
author
#61 lore raw

PATCH v9 16/16 qemu_thread_join: fix segmentation fault

In reply to Fei Li

To avoid the segmentation fault in qemu_thread_join(), just directly return when the QemuThread *thread failed to be created in either qemu-thread-posix.c or qemu-thread-win32.c.

---
 util/qemu-thread-posix.c | 3 +++
 util/qemu-thread-win32.c | 2 +-
 2 files changed, 4 insertions(+), 1 deletion(-)
util/qemu-thread-posix.c +3 -0
index 39834b0551..3548935dac 100644
@@ -571,6 +571,9 @@ void *qemu_thread_join(QemuThread *thread)
     int err;
     void *ret;
 
+    if (!thread->thread) {
+        return NULL;
+    }
     err = pthread_join(thread->thread, &ret);
     if (err) {
         error_exit(err, __func__);
util/qemu-thread-win32.c +1 -1
index 57b1143e97..ca4d5329e3 100644
@@ -367,7 +367,7 @@ void *qemu_thread_join(QemuThread *thread)
     HANDLE handle;
 
     data = thread->data;
-    if (data->mode == QEMU_THREAD_DETACHED) {
+    if (data == NULL || data->mode == QEMU_THREAD_DETACHED) {
         return NULL;
     }
 
Signature · 1 line
2.13.7
#62 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 16/16] qemu_thread_join: fix segmentation fault

Quoted 17 lines · Fei Li

To avoid the segmentation fault in qemu_thread_join(), just directly return when the QemuThread *thread failed to be created in either qemu-thread-posix.c or qemu-thread-win32.c.

---
 util/qemu-thread-posix.c | 3 +++
 util/qemu-thread-win32.c | 2 +-
 2 files changed, 4 insertions(+), 1 deletion(-)
util/qemu-thread-posix.c +0 -0
index 39834b0551..3548935dac 100644

util/qemu-thread-posix.c void *qemu_thread_join(QemuThread *thread) · line 576

Quoted 6 lines
util/qemu-thread-posix.c +3 -0
@@ -571,6 +571,9 @@ void *qemu_thread_join(QemuThread *thread)
     int err;
     void *ret;
 
+    if (!thread->thread) {
+        return NULL;
+    }

How can this happen?

Quoted 15 lines
util/qemu-thread-posix.c +0 -0
     err = pthread_join(thread->thread, &ret);
     if (err) {
         error_exit(err, __func__);
util/qemu-thread-win32.c +1 -1
index 57b1143e97..ca4d5329e3 100644
@@ -367,7 +367,7 @@ void *qemu_thread_join(QemuThread *thread)
     HANDLE handle;
 
     data = thread->data;
-    if (data->mode == QEMU_THREAD_DETACHED) {
+    if (data == NULL || data->mode == QEMU_THREAD_DETACHED) {
         return NULL;
     }
#63 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 16/16] qemu_thread_join: fix segmentation fault

Quoted 26 lines · 在 2019年1月8日,01:55,Markus Armbruster <[email protected]> 写道:
Quoted 20 lines · Fei Li

To avoid the segmentation fault in qemu_thread_join(), just directly return when the QemuThread *thread failed to be created in either qemu-thread-posix.c or qemu-thread-win32.c.

---
util/qemu-thread-posix.c | 3 +++
util/qemu-thread-win32.c | 2 +-

2 files changed, 4 insertions(+), 1 deletion(-)
util/qemu-thread-posix.c +0 -0
index 39834b0551..3548935dac 100644
@@ -571,6 +571,9 @@ void *qemu_thread_join(QemuThread *thread)
    int err;
    void *ret;
 
Quoted 5 lines

util/qemu-thread-posix.c void *qemu_thread_join(QemuThread *thread) · line 576

Quoted 3 lines
util/qemu-thread-posix.c +3 -0
+    if (!thread->thread) {
+        return NULL;
+    }

How can this happen?

I think I have answered this earlier, please check the following link to see whether it helps:
http://lists.nongnu.org/archive/html/qemu-devel/2018-11/msg06554.html

Have a nice day, thanks
Fei

Quoted 16 lines
Quoted 16 lines
util/qemu-thread-posix.c +0 -0
    err = pthread_join(thread->thread, &ret);
    if (err) {
        error_exit(err, __func__);
util/qemu-thread-win32.c +1 -1
index 57b1143e97..ca4d5329e3 100644
@@ -367,7 +367,7 @@ void *qemu_thread_join(QemuThread *thread)
    HANDLE handle;
 
    data = thread->data;
-    if (data->mode == QEMU_THREAD_DETACHED) {
+    if (data == NULL || data->mode == QEMU_THREAD_DETACHED) {
        return NULL;
    }
#64 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 16/16] qemu_thread_join: fix segmentation fault

Quoted 24 lines · fei
Quoted 24 lines · 在 2019年1月8日,01:55,Markus Armbruster <[email protected]> 写道:
Quoted 20 lines · Fei Li

To avoid the segmentation fault in qemu_thread_join(), just directly return when the QemuThread *thread failed to be created in either qemu-thread-posix.c or qemu-thread-win32.c.

---
util/qemu-thread-posix.c | 3 +++
util/qemu-thread-win32.c | 2 +-

2 files changed, 4 insertions(+), 1 deletion(-)
util/qemu-thread-posix.c +0 -0
index 39834b0551..3548935dac 100644
@@ -571,6 +571,9 @@ void *qemu_thread_join(QemuThread *thread)
    int err;
    void *ret;
 
Quoted 7 lines
Quoted 5 lines

util/qemu-thread-posix.c void *qemu_thread_join(QemuThread *thread) · line 576

Quoted 3 lines
util/qemu-thread-posix.c +3 -0
+    if (!thread->thread) {
+        return NULL;
+    }

How can this happen?

I think I have answered this earlier, please check the following link to see whether it helps:
http://lists.nongnu.org/archive/html/qemu-devel/2018-11/msg06554.html

Thanks for the pointer. Unfortunately, I don't understand your explanation. You also wrote there "I will remove this patch in next version"; looks like you've since changed your mind.

What exactly breaks if we omit this patch? Assuming something does
break: imagine we did omit this patch, then forgot we ever saw it, and now you've discovered the breakage. Write us the bug report, complete with reproducer.

[...]

#65 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 16/16] qemu_thread_join: fix segmentation fault

Quoted 30 lines · Markus Armbruster
Quoted 28 lines · fei
Quoted 28 lines · 在 2019年1月8日,01:55,Markus Armbruster <[email protected]> 写道:
Quoted 17 lines · Fei Li

To avoid the segmentation fault in qemu_thread_join(), just directly return when the QemuThread *thread failed to be created in either qemu-thread-posix.c or qemu-thread-win32.c.

---
util/qemu-thread-posix.c | 3 +++
util/qemu-thread-win32.c | 2 +-

2 files changed, 4 insertions(+), 1 deletion(-)
util/qemu-thread-posix.c +0 -0
index 39834b0551..3548935dac 100644

util/qemu-thread-posix.c void *qemu_thread_join(QemuThread *thread) · line 576

Quoted 6 lines
util/qemu-thread-posix.c +3 -0
@@ -571,6 +571,9 @@ void *qemu_thread_join(QemuThread *thread)
     int err;
     void *ret;
 
+    if (!thread->thread) {
+        return NULL;
+    }

How can this happen?

Quoted 5 lines
Quoted 2 lines

I think I have answered this earlier, please check the following link to see whether it helps:
http://lists.nongnu.org/archive/html/qemu-devel/2018-11/msg06554.html

Thanks for the pointer. Unfortunately, I don't understand your explanation. You also wrote there "I will remove this patch in next version"; looks like you've since changed your mind.

Emm, issues left over from history.. The background is I was hurry to make those five Reviewed-by patches be merged, including this v9 16/16 patch but not the real qemu_thread_create() modification. But actually this patch is to fix the segmentation fault after we modified qemu_thread_create() related functions although it has got a Reviewed-by earlier. :) Thus to not make troube, I wrote the "remove..." sentence to separate it from those 5 Reviewed-by patches, and were plan to send only four patches. But later I got a message that these five patches are not that urgent to catch qemu v3.1, thus I joined the earlier 5 R-b patches into the later v8 & v9 to have a better review.

Sorry for the trouble, I need to explain it without involving too much background..

Back at the farm: in our current qemu code, some cleanups use a loop to join() the total number of threads if caller fails. This is not a problem until applying the qemu_thread_create() modification. E.g. when compress_threads_save_setup() fails while trying to create the last do_data_compress thread, segmentation fault will occur when join() is called (sadly there's not enough condition to filter this unsuccessful created thread) as this thread is actually not be created.

Hope the above makes it clear. :)

Have a nice day
Fei

Quoted 7 lines

What exactly breaks if we omit this patch? Assuming something does
break: imagine we did omit this patch, then forgot we ever saw it, and now you've discovered the breakage. Write us the bug report, complete with reproducer.

[...]

#66 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 16/16] qemu_thread_join: fix segmentation fault

Quoted 67 lines · Fei Li
Quoted 30 lines · Markus Armbruster
Quoted 28 lines · fei
Quoted 28 lines · 在 2019年1月8日,01:55,Markus Armbruster <[email protected]> 写道:
Quoted 17 lines · Fei Li

To avoid the segmentation fault in qemu_thread_join(), just directly return when the QemuThread *thread failed to be created in either qemu-thread-posix.c or qemu-thread-win32.c.

---
util/qemu-thread-posix.c | 3 +++
util/qemu-thread-win32.c | 2 +-

2 files changed, 4 insertions(+), 1 deletion(-)
util/qemu-thread-posix.c +0 -0
index 39834b0551..3548935dac 100644

util/qemu-thread-posix.c void *qemu_thread_join(QemuThread *thread) · line 576

Quoted 6 lines
util/qemu-thread-posix.c +3 -0
@@ -571,6 +571,9 @@ void *qemu_thread_join(QemuThread *thread)
     int err;
     void *ret;
 
+    if (!thread->thread) {
+        return NULL;
+    }

How can this happen?

Quoted 5 lines
Quoted 2 lines

I think I have answered this earlier, please check the following link to see whether it helps:
http://lists.nongnu.org/archive/html/qemu-devel/2018-11/msg06554.html

Thanks for the pointer. Unfortunately, I don't understand your explanation. You also wrote there "I will remove this patch in next version"; looks like you've since changed your mind.

Emm, issues left over from history.. The background is I was hurry to make those five
Reviewed-by patches be merged, including this v9 16/16 patch but not the real
qemu_thread_create() modification. But actually this patch is to fix the segmentation
fault after we modified qemu_thread_create() related functions although it has got a
Reviewed-by earlier. :) Thus to not make troube, I wrote the "remove..." sentence
to separate it from those 5 Reviewed-by patches, and were plan to send only four patches.
But later I got a message that these five patches are not that urgent to catch qemu v3.1,
thus I joined the earlier 5 R-b patches into the later v8 & v9 to have a better review.

Sorry for the trouble, I need to explain it without involving too much background..

Back at the farm: in our current qemu code, some cleanups use a loop to join()
the total number of threads if caller fails. This is not a problem until applying the
qemu_thread_create() modification. E.g. when compress_threads_save_setup() fails while trying to create the last do_data_compress thread, segmentation fault
will occur when join() is called (sadly there's not enough condition to filter this
unsuccessful created thread) as this thread is actually not be created.

Quoted 1 line

Hope the above makes it clear. :)

Alright, let's have a look at compress_threads_save_setup():

    static int compress_threads_save_setup(void)
    {
        int i, thread_count;

        if (!migrate_use_compression()) {
            return 0;
        }
        thread_count = migrate_compress_threads();
        compress_threads = g_new0(QemuThread, thread_count);
        comp_param = g_new0(CompressParam, thread_count);
        qemu_cond_init(&comp_done_cond);
        qemu_mutex_init(&comp_done_lock);
        for (i = 0; i < thread_count; i++) {
            comp_param[i].originbuf = g_try_malloc(TARGET_PAGE_SIZE);
            if (!comp_param[i].originbuf) {
                goto exit;
            }

            if (deflateInit(&comp_param[i].stream,
                            migrate_compress_level()) != Z_OK) {
                g_free(comp_param[i].originbuf);
                goto exit;
            }

            /* comp_param[i].file is just used as a dummy buffer to save data,
             * set its ops to empty.
             */
            comp_param[i].file = qemu_fopen_ops(NULL, &empty_ops);
            comp_param[i].done = true;
            comp_param[i].quit = false;
            qemu_mutex_init(&comp_param[i].mutex);
            qemu_cond_init(&comp_param[i].cond);
            qemu_thread_create(compress_threads + i, "compress",
                               do_data_compress, comp_param + i,
                               QEMU_THREAD_JOINABLE);
        }
        return 0;

    exit:
        compress_threads_save_cleanup();
        return -1;
    }

At label exit, we have @i threads, all fully initialized. That's an invariant.

compress_threads_save_cleanup() finds the threads to clean up by checking comp_param[i].file:

    static void compress_threads_save_cleanup(void)
    {
        int i, thread_count;

        if (!migrate_use_compression() || !comp_param) {
            return;
        }

        thread_count = migrate_compress_threads();
        for (i = 0; i < thread_count; i++) {
            /*
             * we use it as a indicator which shows if the thread is
             * properly init'd or not
             */

---> if (!comp_param[i].file) {
---> break;
---> }

            qemu_mutex_lock(&comp_param[i].mutex);
            comp_param[i].quit = true;
            qemu_cond_signal(&comp_param[i].cond);
            qemu_mutex_unlock(&comp_param[i].mutex);

            qemu_thread_join(compress_threads + i);
            qemu_mutex_destroy(&comp_param[i].mutex);
            qemu_cond_destroy(&comp_param[i].cond);
            deflateEnd(&comp_param[i].stream);
            g_free(comp_param[i].originbuf);
            qemu_fclose(comp_param[i].file);
            comp_param[i].file = NULL;
        }
        qemu_mutex_destroy(&comp_done_lock);
        qemu_cond_destroy(&comp_done_cond);
        g_free(compress_threads);
        g_free(comp_param);
        compress_threads = NULL;
        comp_param = NULL;
    }

Due to the invariant, a comp_param[i] with a null .file doesn't need *any* cleanup.

To maintain the invariant, compress_threads_save_setup() carefully cleans up any partial initializations itself before a goto exit. Since the code is arranged smartly, the only such cleanup is the g_free(comp_param[i].originbuf) before the second goto exit.

Your PATCH 13 adds a third goto exit, but neglects to clean up partial initializations. Breaks the invariant.

I see two sane solutions:

1. compress_threads_save_setup() carefully cleans up partial initializations itself. compress_threads_save_cleanup() copes only with fully initialized comp_param[i]. This is how things work before your series.

2. compress_threads_save_cleanup() copes with partially initialized comp_param[i], i.e. does the right thing for each goto exit in compress_threads_save_setup(). compress_threads_save_setup() doesn't clean up partial initializations.

Your PATCH 13 together with the fixup PATCH 16 does

3. A confusing mix of the two.

Don't.

Quoted 9 lines

Have a nice day
Fei

Quoted 7 lines

What exactly breaks if we omit this patch? Assuming something does
break: imagine we did omit this patch, then forgot we ever saw it, and now you've discovered the breakage. Write us the bug report, complete with reproducer.

[...]

#67 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 16/16] qemu_thread_join: fix segmentation fault

Quoted 188 lines · 在 2019年1月9日,23:24,Markus Armbruster <[email protected]> 写道:
Quoted 67 lines · Fei Li
Quoted 31 lines · Markus Armbruster
Quoted 28 lines · fei
Quoted 28 lines · 在 2019年1月8日,01:55,Markus Armbruster <[email protected]> 写道:
Quoted 17 lines · Fei Li

To avoid the segmentation fault in qemu_thread_join(), just directly return when the QemuThread *thread failed to be created in either qemu-thread-posix.c or qemu-thread-win32.c.

---
util/qemu-thread-posix.c | 3 +++
util/qemu-thread-win32.c | 2 +-

2 files changed, 4 insertions(+), 1 deletion(-)
util/qemu-thread-posix.c +0 -0
index 39834b0551..3548935dac 100644

util/qemu-thread-posix.c void *qemu_thread_join(QemuThread *thread) · line 576

Quoted 6 lines
util/qemu-thread-posix.c +3 -0
@@ -571,6 +571,9 @@ void *qemu_thread_join(QemuThread *thread)
    int err;
    void *ret;
 
+    if (!thread->thread) {
+        return NULL;
+    }

How can this happen?

Quoted 5 lines
Quoted 2 lines

I think I have answered this earlier, please check the following link to see whether it helps:
http://lists.nongnu.org/archive/html/qemu-devel/2018-11/msg06554.html

Thanks for the pointer. Unfortunately, I don't understand your explanation. You also wrote there "I will remove this patch in next version"; looks like you've since changed your mind.

Emm, issues left over from history.. The background is I was hurry to make those five
Reviewed-by patches be merged, including this v9 16/16 patch but not the real
qemu_thread_create() modification. But actually this patch is to fix the segmentation
fault after we modified qemu_thread_create() related functions although it has got a
Reviewed-by earlier. :) Thus to not make troube, I wrote the "remove..." sentence
to separate it from those 5 Reviewed-by patches, and were plan to send only four patches.
But later I got a message that these five patches are not that urgent to catch qemu v3.1,
thus I joined the earlier 5 R-b patches into the later v8 & v9 to have a better review.

Sorry for the trouble, I need to explain it without involving too much background..

Back at the farm: in our current qemu code, some cleanups use a loop to join()
the total number of threads if caller fails. This is not a problem until applying the
qemu_thread_create() modification. E.g. when compress_threads_save_setup() fails while trying to create the last do_data_compress thread, segmentation fault
will occur when join() is called (sadly there's not enough condition to filter this
unsuccessful created thread) as this thread is actually not be created.

Quoted 1 line

Hope the above makes it clear. :)

Alright, let's have a look at compress_threads_save_setup():

   static int compress_threads_save_setup(void)
   {
       int i, thread_count;
       if (!migrate_use_compression()) {
           return 0;
       }
       thread_count = migrate_compress_threads();
       compress_threads = g_new0(QemuThread, thread_count);
       comp_param = g_new0(CompressParam, thread_count);
       qemu_cond_init(&comp_done_cond);
       qemu_mutex_init(&comp_done_lock);
       for (i = 0; i < thread_count; i++) {
           comp_param[i].originbuf = g_try_malloc(TARGET_PAGE_SIZE);
           if (!comp_param[i].originbuf) {
               goto exit;
           }

           if (deflateInit(&comp_param[i].stream,
                           migrate_compress_level()) != Z_OK) {
               g_free(comp_param[i].originbuf);
               goto exit;
           }

           /* comp_param[i].file is just used as a dummy buffer to save data,
            * set its ops to empty.
            */
           comp_param[i].file = qemu_fopen_ops(NULL, &empty_ops);
           comp_param[i].done = true;
           comp_param[i].quit = false;
           qemu_mutex_init(&comp_param[i].mutex);
           qemu_cond_init(&comp_param[i].cond);
           qemu_thread_create(compress_threads + i, "compress",
                              do_data_compress, comp_param + i,
                              QEMU_THREAD_JOINABLE);
       }
       return 0;
   exit:
       compress_threads_save_cleanup();
       return -1;
   }

At label exit, we have @i threads, all fully initialized. That's an invariant.

compress_threads_save_cleanup() finds the threads to clean up by checking comp_param[i].file:

   static void compress_threads_save_cleanup(void)
   {
       int i, thread_count;
       if (!migrate_use_compression() || !comp_param) {
           return;
       }

       thread_count = migrate_compress_threads();
       for (i = 0; i < thread_count; i++) {
           /*
            * we use it as a indicator which shows if the thread is
            * properly init'd or not
            */

---> if (!comp_param[i].file) {
---> break;
---> }

           qemu_mutex_lock(&comp_param[i].mutex);
           comp_param[i].quit = true;
           qemu_cond_signal(&comp_param[i].cond);
           qemu_mutex_unlock(&comp_param[i].mutex);

           qemu_thread_join(compress_threads + i);
           qemu_mutex_destroy(&comp_param[i].mutex);
           qemu_cond_destroy(&comp_param[i].cond);
           deflateEnd(&comp_param[i].stream);
           g_free(comp_param[i].originbuf);
           qemu_fclose(comp_param[i].file);
           comp_param[i].file = NULL;
       }
       qemu_mutex_destroy(&comp_done_lock);
       qemu_cond_destroy(&comp_done_cond);
       g_free(compress_threads);
       g_free(comp_param);
       compress_threads = NULL;
       comp_param = NULL;

}

Due to the invariant, a comp_param[i] with a null .file doesn't need *any* cleanup.

To maintain the invariant, compress_threads_save_setup() carefully cleans up any partial initializations itself before a goto exit. Since the code is arranged smartly, the only such cleanup is the g_free(comp_param[i].originbuf) before the second goto exit.

Your PATCH 13 adds a third goto exit, but neglects to clean up partial initializations. Breaks the invariant.

I see two sane solutions:

1. compress_threads_save_setup() carefully cleans up partial initializations itself. compress_threads_save_cleanup() copes only with fully initialized comp_param[i]. This is how things work before your series.

2. compress_threads_save_cleanup() copes with partially initialized comp_param[i], i.e. does the right thing for each goto exit in compress_threads_save_setup(). compress_threads_save_setup() doesn't clean up partial initializations.

Quoted 5 lines

Your PATCH 13 together with the fixup PATCH 16 does

3. A confusing mix of the two.

Don't.

Thanks for the detail analysis! :)
Emm.. Actually I have thought to do the cleanup in the setup() function for the third ‘goto exit’ [1], which is a partial initialization.
But due to the below [1] is too long and seems not neat (I notice that most cleanups for each thread are in the xxx_cleanup() function), I turned to modify the join() function.. Is the long [1] acceptable when the third ‘goto exit’ is called, or is there any other better way to do the cleanup?

[1]

qemu_mutex_lock(&comp_param[i].mutex);
           comp_param[i].quit = true;
           qemu_cond_signal(&comp_param[i].cond);
           qemu_mutex_unlock(&comp_param[i].mutex);
qemu_mutex_destroy(&comp_param[i].mutex);
           qemu_cond_destroy(&comp_param[i].cond);
           deflateEnd(&comp_param[i].stream);
           g_free(comp_param[i].originbuf);
           qemu_fclose(comp_param[i].file);
           comp_param[i].file = NULL;

Have a nice day, thanks
Fei

Quoted 10 lines
Quoted 10 lines

Have a nice day
Fei

Quoted 7 lines

What exactly breaks if we omit this patch? Assuming something does
break: imagine we did omit this patch, then forgot we ever saw it, and now you've discovered the breakage. Write us the bug report, complete with reproducer.

[...]

#68 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 16/16] qemu_thread_join: fix segmentation fault

Quoted 202 lines · fei
Quoted 186 lines · 在 2019年1月9日,23:24,Markus Armbruster <[email protected]> 写道:
Quoted 67 lines · Fei Li
Quoted 31 lines · Markus Armbruster
Quoted 28 lines · fei
Quoted 28 lines · 在 2019年1月8日,01:55,Markus Armbruster <[email protected]> 写道:
Quoted 17 lines · Fei Li

To avoid the segmentation fault in qemu_thread_join(), just directly return when the QemuThread *thread failed to be created in either qemu-thread-posix.c or qemu-thread-win32.c.

---
util/qemu-thread-posix.c | 3 +++
util/qemu-thread-win32.c | 2 +-

2 files changed, 4 insertions(+), 1 deletion(-)
util/qemu-thread-posix.c +0 -0
index 39834b0551..3548935dac 100644

util/qemu-thread-posix.c void *qemu_thread_join(QemuThread *thread) · line 576

Quoted 6 lines
util/qemu-thread-posix.c +3 -0
@@ -571,6 +571,9 @@ void *qemu_thread_join(QemuThread *thread)
    int err;
    void *ret;
 
+    if (!thread->thread) {
+        return NULL;
+    }

How can this happen?

Quoted 5 lines
Quoted 2 lines

I think I have answered this earlier, please check the following link to see whether it helps:
http://lists.nongnu.org/archive/html/qemu-devel/2018-11/msg06554.html

Thanks for the pointer. Unfortunately, I don't understand your explanation. You also wrote there "I will remove this patch in next version"; looks like you've since changed your mind.

Emm, issues left over from history.. The background is I was hurry to make those five
Reviewed-by patches be merged, including this v9 16/16 patch but not the real
qemu_thread_create() modification. But actually this patch is to fix the segmentation
fault after we modified qemu_thread_create() related functions although it has got a
Reviewed-by earlier. :) Thus to not make troube, I wrote the "remove..." sentence
to separate it from those 5 Reviewed-by patches, and were plan to send only four patches.
But later I got a message that these five patches are not that urgent to catch qemu v3.1,
thus I joined the earlier 5 R-b patches into the later v8 & v9 to have a better review.

Sorry for the trouble, I need to explain it without involving too much background..

Back at the farm: in our current qemu code, some cleanups use a loop to join()
the total number of threads if caller fails. This is not a problem until applying the
qemu_thread_create() modification. E.g. when compress_threads_save_setup() fails while trying to create the last do_data_compress thread, segmentation fault
will occur when join() is called (sadly there's not enough condition to filter this
unsuccessful created thread) as this thread is actually not be created.

Quoted 1 line

Hope the above makes it clear. :)

Alright, let's have a look at compress_threads_save_setup():

   static int compress_threads_save_setup(void)
   {
       int i, thread_count;
       if (!migrate_use_compression()) {
           return 0;
       }
       thread_count = migrate_compress_threads();
       compress_threads = g_new0(QemuThread, thread_count);
       comp_param = g_new0(CompressParam, thread_count);
       qemu_cond_init(&comp_done_cond);
       qemu_mutex_init(&comp_done_lock);
       for (i = 0; i < thread_count; i++) {
           comp_param[i].originbuf = g_try_malloc(TARGET_PAGE_SIZE);
           if (!comp_param[i].originbuf) {
               goto exit;
           }

           if (deflateInit(&comp_param[i].stream,
                           migrate_compress_level()) != Z_OK) {
               g_free(comp_param[i].originbuf);
               goto exit;
           }

           /* comp_param[i].file is just used as a dummy buffer to save data,
            * set its ops to empty.
            */
           comp_param[i].file = qemu_fopen_ops(NULL, &empty_ops);
           comp_param[i].done = true;
           comp_param[i].quit = false;
           qemu_mutex_init(&comp_param[i].mutex);
           qemu_cond_init(&comp_param[i].cond);
           qemu_thread_create(compress_threads + i, "compress",
                              do_data_compress, comp_param + i,
                              QEMU_THREAD_JOINABLE);
       }
       return 0;
   exit:
       compress_threads_save_cleanup();
       return -1;
   }

At label exit, we have @i threads, all fully initialized. That's an invariant.

compress_threads_save_cleanup() finds the threads to clean up by checking comp_param[i].file:

   static void compress_threads_save_cleanup(void)
   {
       int i, thread_count;
       if (!migrate_use_compression() || !comp_param) {
           return;
       }

       thread_count = migrate_compress_threads();
       for (i = 0; i < thread_count; i++) {
           /*
            * we use it as a indicator which shows if the thread is
            * properly init'd or not
            */

---> if (!comp_param[i].file) {
---> break;
---> }

           qemu_mutex_lock(&comp_param[i].mutex);
           comp_param[i].quit = true;
           qemu_cond_signal(&comp_param[i].cond);
           qemu_mutex_unlock(&comp_param[i].mutex);

           qemu_thread_join(compress_threads + i);
           qemu_mutex_destroy(&comp_param[i].mutex);
           qemu_cond_destroy(&comp_param[i].cond);
           deflateEnd(&comp_param[i].stream);
           g_free(comp_param[i].originbuf);
           qemu_fclose(comp_param[i].file);
           comp_param[i].file = NULL;
       }
       qemu_mutex_destroy(&comp_done_lock);
       qemu_cond_destroy(&comp_done_cond);
       g_free(compress_threads);
       g_free(comp_param);
       compress_threads = NULL;
       comp_param = NULL;

}

Due to the invariant, a comp_param[i] with a null .file doesn't need *any* cleanup.

To maintain the invariant, compress_threads_save_setup() carefully cleans up any partial initializations itself before a goto exit. Since the code is arranged smartly, the only such cleanup is the g_free(comp_param[i].originbuf) before the second goto exit.

Your PATCH 13 adds a third goto exit, but neglects to clean up partial initializations. Breaks the invariant.

I see two sane solutions:

1. compress_threads_save_setup() carefully cleans up partial initializations itself. compress_threads_save_cleanup() copes only with fully initialized comp_param[i]. This is how things work before your series.

2. compress_threads_save_cleanup() copes with partially initialized comp_param[i], i.e. does the right thing for each goto exit in compress_threads_save_setup(). compress_threads_save_setup() doesn't clean up partial initializations.

Quoted 5 lines

Your PATCH 13 together with the fixup PATCH 16 does

3. A confusing mix of the two.

Don't.

Thanks for the detail analysis! :)
Emm.. Actually I have thought to do the cleanup in the setup() function for the third ‘goto exit’ [1], which is a partial initialization.
But due to the below [1] is too long and seems not neat (I notice that most cleanups for each thread are in the xxx_cleanup() function), I turned to modify the join() function.. Is the long [1] acceptable when the third ‘goto exit’ is called, or is there any other better way to do the cleanup?

[1]

qemu_mutex_lock(&comp_param[i].mutex);
           comp_param[i].quit = true;
           qemu_cond_signal(&comp_param[i].cond);
           qemu_mutex_unlock(&comp_param[i].mutex);
Quoted 6 lines
qemu_mutex_destroy(&comp_param[i].mutex);
           qemu_cond_destroy(&comp_param[i].cond);
           deflateEnd(&comp_param[i].stream);
           g_free(comp_param[i].originbuf);
           qemu_fclose(comp_param[i].file);
           comp_param[i].file = NULL;

Have you considered creating the thread earlier, e.g. right after initializing compression with deflateInit()?

#69 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 16/16] qemu_thread_join: fix segmentation fault

Quoted 203 lines · Markus Armbruster
Quoted 201 lines · fei
Quoted 185 lines · 在 2019年1月9日,23:24,Markus Armbruster <[email protected]> 写道:
Quoted 67 lines · Fei Li
Quoted 31 lines · Markus Armbruster
Quoted 28 lines · fei
Quoted 28 lines · 在 2019年1月8日,01:55,Markus Armbruster <[email protected]> 写道:
Quoted 17 lines · Fei Li

To avoid the segmentation fault in qemu_thread_join(), just directly return when the QemuThread *thread failed to be created in either qemu-thread-posix.c or qemu-thread-win32.c.

---
util/qemu-thread-posix.c | 3 +++
util/qemu-thread-win32.c | 2 +-

2 files changed, 4 insertions(+), 1 deletion(-)
util/qemu-thread-posix.c +0 -0
index 39834b0551..3548935dac 100644

util/qemu-thread-posix.c void *qemu_thread_join(QemuThread *thread) · line 576

Quoted 6 lines
util/qemu-thread-posix.c +3 -0
@@ -571,6 +571,9 @@ void *qemu_thread_join(QemuThread *thread)
     int err;
     void *ret;
 
+    if (!thread->thread) {
+        return NULL;
+    }

How can this happen?

Quoted 5 lines
Quoted 2 lines

I think I have answered this earlier, please check the following link to see whether it helps:
http://lists.nongnu.org/archive/html/qemu-devel/2018-11/msg06554.html

Thanks for the pointer. Unfortunately, I don't understand your explanation. You also wrote there "I will remove this patch in next version"; looks like you've since changed your mind.

Emm, issues left over from history.. The background is I was hurry to make those five
Reviewed-by patches be merged, including this v9 16/16 patch but not the real
qemu_thread_create() modification. But actually this patch is to fix the segmentation
fault after we modified qemu_thread_create() related functions although it has got a
Reviewed-by earlier. :) Thus to not make troube, I wrote the "remove..." sentence
to separate it from those 5 Reviewed-by patches, and were plan to send only four patches.
But later I got a message that these five patches are not that urgent to catch qemu v3.1,
thus I joined the earlier 5 R-b patches into the later v8 & v9 to have a better review.

Sorry for the trouble, I need to explain it without involving too much background..

Back at the farm: in our current qemu code, some cleanups use a loop to join()
the total number of threads if caller fails. This is not a problem until applying the
qemu_thread_create() modification. E.g. when compress_threads_save_setup() fails while trying to create the last do_data_compress thread, segmentation fault
will occur when join() is called (sadly there's not enough condition to filter this
unsuccessful created thread) as this thread is actually not be created.

Quoted 1 line

Hope the above makes it clear. :)

Alright, let's have a look at compress_threads_save_setup():

    static int compress_threads_save_setup(void)
    {
        int i, thread_count;

        if (!migrate_use_compression()) {
            return 0;
        }
        thread_count = migrate_compress_threads();
        compress_threads = g_new0(QemuThread, thread_count);
        comp_param = g_new0(CompressParam, thread_count);
        qemu_cond_init(&comp_done_cond);
        qemu_mutex_init(&comp_done_lock);
        for (i = 0; i < thread_count; i++) {
            comp_param[i].originbuf = g_try_malloc(TARGET_PAGE_SIZE);
            if (!comp_param[i].originbuf) {
                goto exit;
            }

            if (deflateInit(&comp_param[i].stream,
                            migrate_compress_level()) != Z_OK) {
                g_free(comp_param[i].originbuf);
                goto exit;
            }

            /* comp_param[i].file is just used as a dummy buffer to save data,
             * set its ops to empty.
             */
            comp_param[i].file = qemu_fopen_ops(NULL, &empty_ops);
            comp_param[i].done = true;
            comp_param[i].quit = false;
            qemu_mutex_init(&comp_param[i].mutex);
            qemu_cond_init(&comp_param[i].cond);
            qemu_thread_create(compress_threads + i, "compress",
                               do_data_compress, comp_param + i,
                               QEMU_THREAD_JOINABLE);
        }
        return 0;

    exit:
        compress_threads_save_cleanup();
        return -1;
    }

At label exit, we have @i threads, all fully initialized. That's an invariant.

compress_threads_save_cleanup() finds the threads to clean up by checking comp_param[i].file:

    static void compress_threads_save_cleanup(void)
    {
        int i, thread_count;

        if (!migrate_use_compression() || !comp_param) {
            return;
        }

        thread_count = migrate_compress_threads();
        for (i = 0; i < thread_count; i++) {
            /*
             * we use it as a indicator which shows if the thread is
             * properly init'd or not
             */

---> if (!comp_param[i].file) {
---> break;
---> }

            qemu_mutex_lock(&comp_param[i].mutex);
            comp_param[i].quit = true;
            qemu_cond_signal(&comp_param[i].cond);
            qemu_mutex_unlock(&comp_param[i].mutex);

            qemu_thread_join(compress_threads + i);
            qemu_mutex_destroy(&comp_param[i].mutex);
            qemu_cond_destroy(&comp_param[i].cond);
            deflateEnd(&comp_param[i].stream);
            g_free(comp_param[i].originbuf);
            qemu_fclose(comp_param[i].file);
            comp_param[i].file = NULL;
        }
        qemu_mutex_destroy(&comp_done_lock);
        qemu_cond_destroy(&comp_done_cond);
        g_free(compress_threads);
        g_free(comp_param);
        compress_threads = NULL;
        comp_param = NULL;
    }

Due to the invariant, a comp_param[i] with a null .file doesn't need *any* cleanup.

To maintain the invariant, compress_threads_save_setup() carefully cleans up any partial initializations itself before a goto exit. Since the code is arranged smartly, the only such cleanup is the g_free(comp_param[i].originbuf) before the second goto exit.

Your PATCH 13 adds a third goto exit, but neglects to clean up partial initializations. Breaks the invariant.

I see two sane solutions:

1. compress_threads_save_setup() carefully cleans up partial initializations itself. compress_threads_save_cleanup() copes only with fully initialized comp_param[i]. This is how things work before your series.

2. compress_threads_save_cleanup() copes with partially initialized comp_param[i], i.e. does the right thing for each goto exit in compress_threads_save_setup(). compress_threads_save_setup() doesn't clean up partial initializations.

Quoted 5 lines

Your PATCH 13 together with the fixup PATCH 16 does

3. A confusing mix of the two.

Don't.

Thanks for the detail analysis! :)
Emm.. Actually I have thought to do the cleanup in the setup() function for the third ‘goto exit’ [1], which is a partial initialization.
But due to the below [1] is too long and seems not neat (I notice that most cleanups for each thread are in the xxx_cleanup() function), I turned to modify the join() function.. Is the long [1] acceptable when the third ‘goto exit’ is called, or is there any other better way to do the cleanup?

[1]

qemu_mutex_lock(&comp_param[i].mutex);
            comp_param[i].quit = true;
            qemu_cond_signal(&comp_param[i].cond);
            qemu_mutex_unlock(&comp_param[i].mutex);
Quoted 8 lines
Quoted 6 lines
qemu_mutex_destroy(&comp_param[i].mutex);
            qemu_cond_destroy(&comp_param[i].cond);
            deflateEnd(&comp_param[i].stream);
            g_free(comp_param[i].originbuf);
            qemu_fclose(comp_param[i].file);
            comp_param[i].file = NULL;

Have you considered creating the thread earlier, e.g. right after initializing compression with deflateInit()?

I am afraid we can not do this, as the members of comp_param[i], like file/done/quit/mutex/cond will be used later in the new created thread: do_data_[de]compress via qemu_thread_create().

Thus it seems we have to accept the above long [1] if we do want to clean up partial initialization in xxx_setup(). :(

BTW, there is no other argument can be used except the "(compress_threads+i)->thread" to differentiate whether should we join() the thread, just in case we want to change the xxx_cleanup() function.

*

*

Have a nice day, thanks
Fei

#70 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 16/16] qemu_thread_join: fix segmentation fault

Quoted 210 lines · Fei Li
Quoted 209 lines · Markus Armbruster
Quoted 207 lines · fei
Quoted 185 lines · 在 2019年1月9日,23:24,Markus Armbruster <[email protected]> 写道:
Quoted 67 lines · Fei Li
Quoted 31 lines · Markus Armbruster
Quoted 28 lines · fei
Quoted 28 lines · 在 2019年1月8日,01:55,Markus Armbruster <[email protected]> 写道:
Quoted 17 lines · Fei Li

To avoid the segmentation fault in qemu_thread_join(), just directly return when the QemuThread *thread failed to be created in either qemu-thread-posix.c or qemu-thread-win32.c.

---
util/qemu-thread-posix.c | 3 +++
util/qemu-thread-win32.c | 2 +-

2 files changed, 4 insertions(+), 1 deletion(-)
util/qemu-thread-posix.c +0 -0
index 39834b0551..3548935dac 100644

util/qemu-thread-posix.c void *qemu_thread_join(QemuThread *thread) · line 576

Quoted 6 lines
util/qemu-thread-posix.c +3 -0
@@ -571,6 +571,9 @@ void *qemu_thread_join(QemuThread *thread)
     int err;
     void *ret;
 
+    if (!thread->thread) {
+        return NULL;
+    }

How can this happen?

Quoted 5 lines
Quoted 2 lines

I think I have answered this earlier, please check the following link to see whether it helps:
http://lists.nongnu.org/archive/html/qemu-devel/2018-11/msg06554.html

Thanks for the pointer. Unfortunately, I don't understand your explanation. You also wrote there "I will remove this patch in next version"; looks like you've since changed your mind.

Emm, issues left over from history.. The background is I was hurry to make those five
Reviewed-by patches be merged, including this v9 16/16 patch but not the real
qemu_thread_create() modification. But actually this patch is to fix the segmentation
fault after we modified qemu_thread_create() related functions although it has got a
Reviewed-by earlier. :) Thus to not make troube, I wrote the "remove..." sentence
to separate it from those 5 Reviewed-by patches, and were plan to send only four patches.
But later I got a message that these five patches are not that urgent to catch qemu v3.1,
thus I joined the earlier 5 R-b patches into the later v8 & v9 to have a better review.

Sorry for the trouble, I need to explain it without involving too much background..

Back at the farm: in our current qemu code, some cleanups use a loop to join()
the total number of threads if caller fails. This is not a problem until applying the
qemu_thread_create() modification. E.g. when compress_threads_save_setup() fails while trying to create the last do_data_compress thread, segmentation fault
will occur when join() is called (sadly there's not enough condition to filter this
unsuccessful created thread) as this thread is actually not be created.

Quoted 1 line

Hope the above makes it clear. :)

Alright, let's have a look at compress_threads_save_setup():

    static int compress_threads_save_setup(void)
    {
        int i, thread_count;

        if (!migrate_use_compression()) {
            return 0;
        }
        thread_count = migrate_compress_threads();
        compress_threads = g_new0(QemuThread, thread_count);
        comp_param = g_new0(CompressParam, thread_count);
        qemu_cond_init(&comp_done_cond);
        qemu_mutex_init(&comp_done_lock);
        for (i = 0; i < thread_count; i++) {
            comp_param[i].originbuf = g_try_malloc(TARGET_PAGE_SIZE);
            if (!comp_param[i].originbuf) {
                goto exit;
            }

            if (deflateInit(&comp_param[i].stream,
                            migrate_compress_level()) != Z_OK) {
                g_free(comp_param[i].originbuf);
                goto exit;
            }

            /* comp_param[i].file is just used as a dummy buffer to save data,
             * set its ops to empty.
             */
            comp_param[i].file = qemu_fopen_ops(NULL, &empty_ops);
            comp_param[i].done = true;
            comp_param[i].quit = false;
            qemu_mutex_init(&comp_param[i].mutex);
            qemu_cond_init(&comp_param[i].cond);
            qemu_thread_create(compress_threads + i, "compress",
                               do_data_compress, comp_param + i,
                               QEMU_THREAD_JOINABLE);
        }
        return 0;

    exit:
        compress_threads_save_cleanup();
        return -1;
    }

At label exit, we have @i threads, all fully initialized. That's an invariant.

compress_threads_save_cleanup() finds the threads to clean up by checking comp_param[i].file:

    static void compress_threads_save_cleanup(void)
    {
        int i, thread_count;

        if (!migrate_use_compression() || !comp_param) {
            return;
        }

        thread_count = migrate_compress_threads();
        for (i = 0; i < thread_count; i++) {
            /*
             * we use it as a indicator which shows if the thread is
             * properly init'd or not
             */

---> if (!comp_param[i].file) {
---> break;
---> }

            qemu_mutex_lock(&comp_param[i].mutex);
            comp_param[i].quit = true;
            qemu_cond_signal(&comp_param[i].cond);
            qemu_mutex_unlock(&comp_param[i].mutex);

            qemu_thread_join(compress_threads + i);
            qemu_mutex_destroy(&comp_param[i].mutex);
            qemu_cond_destroy(&comp_param[i].cond);
            deflateEnd(&comp_param[i].stream);
            g_free(comp_param[i].originbuf);
            qemu_fclose(comp_param[i].file);
            comp_param[i].file = NULL;
        }
        qemu_mutex_destroy(&comp_done_lock);
        qemu_cond_destroy(&comp_done_cond);
        g_free(compress_threads);
        g_free(comp_param);
        compress_threads = NULL;
        comp_param = NULL;
    }

Due to the invariant, a comp_param[i] with a null .file doesn't need *any* cleanup.

To maintain the invariant, compress_threads_save_setup() carefully cleans up any partial initializations itself before a goto exit. Since the code is arranged smartly, the only such cleanup is the g_free(comp_param[i].originbuf) before the second goto exit.

Your PATCH 13 adds a third goto exit, but neglects to clean up partial initializations. Breaks the invariant.

I see two sane solutions:

1. compress_threads_save_setup() carefully cleans up partial initializations itself. compress_threads_save_cleanup() copes only with fully initialized comp_param[i]. This is how things work before your series.

2. compress_threads_save_cleanup() copes with partially initialized comp_param[i], i.e. does the right thing for each goto exit in compress_threads_save_setup(). compress_threads_save_setup() doesn't clean up partial initializations.

Quoted 5 lines

Your PATCH 13 together with the fixup PATCH 16 does

3. A confusing mix of the two.

Don't.

Thanks for the detail analysis! :)
Emm.. Actually I have thought to do the cleanup in the setup() function for the third ‘goto exit’ [1], which is a partial initialization.
But due to the below [1] is too long and seems not neat (I notice that most cleanups for each thread are in the xxx_cleanup() function), I turned to modify the join() function.. Is the long [1] acceptable when the third ‘goto exit’ is called, or is there any other better way to do the cleanup?

[1]

qemu_mutex_lock(&comp_param[i].mutex);
            comp_param[i].quit = true;
            qemu_cond_signal(&comp_param[i].cond);
            qemu_mutex_unlock(&comp_param[i].mutex);
qemu_mutex_destroy(&comp_param[i].mutex);
            qemu_cond_destroy(&comp_param[i].cond);
            deflateEnd(&comp_param[i].stream);
            g_free(comp_param[i].originbuf);
            qemu_fclose(comp_param[i].file);
            comp_param[i].file = NULL;
Quoted 6 lines
Quoted 2 lines

Have you considered creating the thread earlier, e.g. right after initializing compression with deflateInit()?

I am afraid we can not do this, as the members of comp_param[i], like file/done/quit/mutex/cond
will be used later in the new created thread: do_data_[de]compress via qemu_thread_create().

You're right.

Quoted 9 lines

Thus it seems we have to accept the above long [1] if we do want to clean up partial initialization
in xxx_setup(). :(

BTW, there is no other argument can be used except the "(compress_threads+i)->thread" to
differentiate whether should we join() the thread, just in case we want to change the
xxx_cleanup() function.

We can try to make compress_threads_save_cleanup() cope with partially initialized comp_param[i]. Let's have a look at its members:

    bool done;                          // no cleanup
    bool quit;                          // see [2]
    bool zero_page;                     // no cleanup
    QEMUFile *file;                     // qemu_fclose() if non-null
    QemuMutex mutex;                    // see [1]
    QemuCond cond;                      // see [1]
    RAMBlock *block;                    // no cleanup (must be null)
    ram_addr_t offset;                  // no cleanup

/* internally used fields */
z_stream stream; // see [3]
uint8_t *originbuf; // unconditional g_free()

[1]: we could do something like

    if (comp_param[i].mutex.initialized) {
        qemu_mutex_destroy(&comp_param[i].mutex);
    }
    if (comp_param[i].cond.initialized) {
        qemu_cond_destroy(&comp_param[i].cond);
    }

but that would be unclean. Instead, I'd initialize these guys first, so we can clean them up unconditionally.

[2] This is used to make the thread terminate. Must be done before we call qemu_thread_join(). I think it can safely be done always, as long as long as .mutex and .cond are initialized. Trivial if we initialize them first.

[3]: I can't see a squeaky clean way to detect whether .stream has been initialized with deflateInit(). Here's a slightly unclean way:
deflateInit() sets .stream.msg to null on success, and to non-null on failure. We can make it non-null until we're ready to call deflateInit(), then have compress_threads_save_cleanup() clean up .stream when it's null. If that's too unclean for your or your reviewers' taste, add a boolean @stream_initialized flag.

#71 lore raw

Re: [Qemu-devel] [PATCH for-4.0 v9 16/16] qemu_thread_join: fix segmentation fault

Quoted 254 lines · Markus Armbruster
Quoted 210 lines · Fei Li
Quoted 209 lines · Markus Armbruster
Quoted 207 lines · fei
Quoted 185 lines · 在 2019年1月9日,23:24,Markus Armbruster <[email protected]> 写道:
Quoted 67 lines · Fei Li
Quoted 31 lines · Markus Armbruster
Quoted 28 lines · fei
Quoted 28 lines · 在 2019年1月8日,01:55,Markus Armbruster <[email protected]> 写道:
Quoted 17 lines · Fei Li

To avoid the segmentation fault in qemu_thread_join(), just directly return when the QemuThread *thread failed to be created in either qemu-thread-posix.c or qemu-thread-win32.c.

---
util/qemu-thread-posix.c | 3 +++
util/qemu-thread-win32.c | 2 +-

2 files changed, 4 insertions(+), 1 deletion(-)
util/qemu-thread-posix.c +0 -0
index 39834b0551..3548935dac 100644

util/qemu-thread-posix.c void *qemu_thread_join(QemuThread *thread) · line 576

Quoted 6 lines
util/qemu-thread-posix.c +3 -0
@@ -571,6 +571,9 @@ void *qemu_thread_join(QemuThread *thread)
      int err;
      void *ret;
 
+    if (!thread->thread) {
+        return NULL;
+    }

How can this happen?

Quoted 5 lines
Quoted 2 lines

I think I have answered this earlier, please check the following link to see whether it helps:
http://lists.nongnu.org/archive/html/qemu-devel/2018-11/msg06554.html

Thanks for the pointer. Unfortunately, I don't understand your explanation. You also wrote there "I will remove this patch in next version"; looks like you've since changed your mind.

Emm, issues left over from history.. The background is I was hurry to make those five
Reviewed-by patches be merged, including this v9 16/16 patch but not the real
qemu_thread_create() modification. But actually this patch is to fix the segmentation
fault after we modified qemu_thread_create() related functions although it has got a
Reviewed-by earlier. :) Thus to not make troube, I wrote the "remove..." sentence
to separate it from those 5 Reviewed-by patches, and were plan to send only four patches.
But later I got a message that these five patches are not that urgent to catch qemu v3.1,
thus I joined the earlier 5 R-b patches into the later v8 & v9 to have a better review.

Sorry for the trouble, I need to explain it without involving too much background..

Back at the farm: in our current qemu code, some cleanups use a loop to join()
the total number of threads if caller fails. This is not a problem until applying the
qemu_thread_create() modification. E.g. when compress_threads_save_setup() fails while trying to create the last do_data_compress thread, segmentation fault
will occur when join() is called (sadly there's not enough condition to filter this
unsuccessful created thread) as this thread is actually not be created.

Quoted 1 line

Hope the above makes it clear. :)

Alright, let's have a look at compress_threads_save_setup():

     static int compress_threads_save_setup(void)
     {
         int i, thread_count;

         if (!migrate_use_compression()) {
             return 0;
         }
         thread_count = migrate_compress_threads();
         compress_threads = g_new0(QemuThread, thread_count);
         comp_param = g_new0(CompressParam, thread_count);
         qemu_cond_init(&comp_done_cond);
         qemu_mutex_init(&comp_done_lock);
         for (i = 0; i < thread_count; i++) {
             comp_param[i].originbuf = g_try_malloc(TARGET_PAGE_SIZE);
             if (!comp_param[i].originbuf) {
                 goto exit;
             }

             if (deflateInit(&comp_param[i].stream,
                             migrate_compress_level()) != Z_OK) {
                 g_free(comp_param[i].originbuf);
                 goto exit;
             }

             /* comp_param[i].file is just used as a dummy buffer to save data,
              * set its ops to empty.
              */
             comp_param[i].file = qemu_fopen_ops(NULL, &empty_ops);
             comp_param[i].done = true;
             comp_param[i].quit = false;
             qemu_mutex_init(&comp_param[i].mutex);
             qemu_cond_init(&comp_param[i].cond);
             qemu_thread_create(compress_threads + i, "compress",
                                do_data_compress, comp_param + i,
                                QEMU_THREAD_JOINABLE);
         }
         return 0;

     exit:
         compress_threads_save_cleanup();
         return -1;
     }

At label exit, we have @i threads, all fully initialized. That's an invariant.

compress_threads_save_cleanup() finds the threads to clean up by checking comp_param[i].file:

     static void compress_threads_save_cleanup(void)
     {
         int i, thread_count;

         if (!migrate_use_compression() || !comp_param) {
             return;
         }

         thread_count = migrate_compress_threads();
         for (i = 0; i < thread_count; i++) {
             /*
              * we use it as a indicator which shows if the thread is
              * properly init'd or not
              */

---> if (!comp_param[i].file) {
---> break;
---> }

             qemu_mutex_lock(&comp_param[i].mutex);
             comp_param[i].quit = true;
             qemu_cond_signal(&comp_param[i].cond);
             qemu_mutex_unlock(&comp_param[i].mutex);

             qemu_thread_join(compress_threads + i);
             qemu_mutex_destroy(&comp_param[i].mutex);
             qemu_cond_destroy(&comp_param[i].cond);
             deflateEnd(&comp_param[i].stream);
             g_free(comp_param[i].originbuf);
             qemu_fclose(comp_param[i].file);
             comp_param[i].file = NULL;
         }
         qemu_mutex_destroy(&comp_done_lock);
         qemu_cond_destroy(&comp_done_cond);
         g_free(compress_threads);
         g_free(comp_param);
         compress_threads = NULL;
         comp_param = NULL;
     }

Due to the invariant, a comp_param[i] with a null .file doesn't need *any* cleanup.

To maintain the invariant, compress_threads_save_setup() carefully cleans up any partial initializations itself before a goto exit. Since the code is arranged smartly, the only such cleanup is the g_free(comp_param[i].originbuf) before the second goto exit.

Your PATCH 13 adds a third goto exit, but neglects to clean up partial initializations. Breaks the invariant.

I see two sane solutions:

1. compress_threads_save_setup() carefully cleans up partial initializations itself. compress_threads_save_cleanup() copes only with fully initialized comp_param[i]. This is how things work before your series.

2. compress_threads_save_cleanup() copes with partially initialized comp_param[i], i.e. does the right thing for each goto exit in compress_threads_save_setup(). compress_threads_save_setup() doesn't clean up partial initializations.

Quoted 5 lines

Your PATCH 13 together with the fixup PATCH 16 does

3. A confusing mix of the two.

Don't.

Thanks for the detail analysis! :)
Emm.. Actually I have thought to do the cleanup in the setup() function for the third ‘goto exit’ [1], which is a partial initialization.
But due to the below [1] is too long and seems not neat (I notice that most cleanups for each thread are in the xxx_cleanup() function), I turned to modify the join() function.. Is the long [1] acceptable when the third ‘goto exit’ is called, or is there any other better way to do the cleanup?

[1]

qemu_mutex_lock(&comp_param[i].mutex);
             comp_param[i].quit = true;
             qemu_cond_signal(&comp_param[i].cond);
             qemu_mutex_unlock(&comp_param[i].mutex);
qemu_mutex_destroy(&comp_param[i].mutex);
             qemu_cond_destroy(&comp_param[i].cond);
             deflateEnd(&comp_param[i].stream);
             g_free(comp_param[i].originbuf);
             qemu_fclose(comp_param[i].file);
             comp_param[i].file = NULL;
Quoted 6 lines
Quoted 2 lines

Have you considered creating the thread earlier, e.g. right after initializing compression with deflateInit()?

I am afraid we can not do this, as the members of comp_param[i], like file/done/quit/mutex/cond
will be used later in the new created thread: do_data_[de]compress via qemu_thread_create().

You're right.

Quoted 9 lines

Thus it seems we have to accept the above long [1] if we do want to clean up partial initialization
in xxx_setup(). :(

BTW, there is no other argument can be used except the "(compress_threads+i)->thread" to
differentiate whether should we join() the thread, just in case we want to change the
xxx_cleanup() function.

We can try to make compress_threads_save_cleanup() cope with partially initialized comp_param[i]. Let's have a look at its members:

     bool done;                          // no cleanup
     bool quit;                          // see [2]
     bool zero_page;                     // no cleanup
     QEMUFile *file;                     // qemu_fclose() if non-null
     QemuMutex mutex;                    // see [1]
     QemuCond cond;                      // see [1]
     RAMBlock *block;                    // no cleanup (must be null)
     ram_addr_t offset;                  // no cleanup

/* internally used fields */
z_stream stream; // see [3]
uint8_t *originbuf; // unconditional g_free()

[1]: we could do something like

     if (comp_param[i].mutex.initialized) {
         qemu_mutex_destroy(&comp_param[i].mutex);
     }
     if (comp_param[i].cond.initialized) {
         qemu_cond_destroy(&comp_param[i].cond);
     }
Quoted 7 lines

but that would be unclean. Instead, I'd initialize these guys first, so we can clean them up unconditionally.

[2] This is used to make the thread terminate. Must be done before we call qemu_thread_join(). I think it can safely be done always, as long as long as .mutex and .cond are initialized. Trivial if we initialize them first.

Thanks for the detail analysis, it helps a lot! I translate the above [1] & [2] to "move the below three '+' ahead of the initialization of comp_param[i].file" for xxx_setup():

+        qemu_mutex_init(&comp_param[i].mutex);
+        qemu_cond_init(&comp_param[i].cond);
+        comp_param[i].quit = false;
         /* comp_param[i].file is just used as a dummy buffer to save data,
          * set its ops to empty.
          */
         comp_param[i].file = qemu_fopen_ops(NULL, &empty_ops); comp_param[i].done = true;
         if (!qemu_thread_create(compress_threads + i, "compress",
         ...

And accordingly, do the corresponding change for the xxx_cleanup().

Quoted 7 lines

[3]: I can't see a squeaky clean way to detect whether .stream has been initialized with deflateInit(). Here's a slightly unclean way:
deflateInit() sets .stream.msg to null on success, and to non-null on failure. We can make it non-null until we're ready to call deflateInit(), then have compress_threads_save_cleanup() clean up .stream when it's null. If that's too unclean for your or your reviewers' taste, add a boolean @stream_initialized flag.

Emm, I am not sure either. Let's cc the migration maintainers to have their opinions.

Have a nice day, thanks for the review. :)
Fei

#72 lore raw
In reply to Fei Li
Patchew URL: https://patchew.org/QEMU/[email protected]/

Patchew URL: https://patchew.org/QEMU/[email protected]/

Hi,

This series failed the docker-quick@centos7 build test. Please find the testing commands and their output below. If you have Docker installed, you can probably reproduce it locally.

=== TEST SCRIPT BEGIN ===
#!/bin/bash
time make docker-test-quick@centos7 SHOW_ENV=1 J=8 === TEST SCRIPT END ===

libpmem support no
libudev no

WARNING: Use of SDL 1.2 is deprecated and will be removed in
WARNING: future releases. Please switch to using SDL 2.0

NOTE: cross-compilers enabled: 'cc'
GEN x86_64-softmmu/config-devices.mak.tmp
---
CC hw/usb/host-stub.o
CC hw/virtio/virtio-bus.o
/tmp/qemu-test/src/hw/usb/ccid-card-emulated.c: In function 'init_event_notifier':
/tmp/qemu-test/src/hw/usb/ccid-card-emulated.c:404:9: error: implicit declaration of function 'error_setg' [-Werror=implicit-function-declaration]

         error_setg(errp, "ccid-card-emul: event notifier creation failed");
         ^
/tmp/qemu-test/src/hw/usb/ccid-card-emulated.c:404:9: error: nested extern declaration of 'error_setg' [-Werror=nested-externs]
/tmp/qemu-test/src/hw/usb/ccid-card-emulated.c: In function 'emulated_realize':
/tmp/qemu-test/src/hw/usb/ccid-card-emulated.c:513:13: error: implicit declaration of function 'error_append_hint' [-Werror=implicit-function-declaration]
             error_append_hint(errp, "%s\n", ptable->name);
             ^

/tmp/qemu-test/src/hw/usb/ccid-card-emulated.c:513:13: error: nested extern declaration of 'error_append_hint' [-Werror=nested-externs] /tmp/qemu-test/src/hw/usb/ccid-card-emulated.c: At top level:
cc1: error: unrecognized command line option "-Wno-format-truncation" [-Werror]
cc1: all warnings being treated as errors
make: *** [hw/usb/ccid-card-emulated.o] Error 1
make: *** Waiting for unfinished jobs....

The full log is available at
http://patchew.org/logs/[email protected]/testing.docker-quick@centos7/?type=message.
---
Email generated automatically by Patchew [http://patchew.org/].
Please send your feedback to [email protected]

#73 lore raw

Hi all,

Sorry for the mistakenly deleted " include "qapi/error.h" " for
[PATCH for-4.0 v9 11/16] qemu_thread: supplement error handling for emulated_realize, will add this #include back in next version.

Have a nice day, and again sorry for the trouble.
Fei

Quoted 48 lines · 在 2019/1/2 下午9:46, [email protected] 写道:

Patchew URL: https://patchew.org/QEMU/[email protected]/

Hi,

This series failed the docker-quick@centos7 build test. Please find the testing commands and their output below. If you have Docker installed, you can probably reproduce it locally.

=== TEST SCRIPT BEGIN ===
#!/bin/bash
time make docker-test-quick@centos7 SHOW_ENV=1 J=8 === TEST SCRIPT END ===

libpmem support no
libudev no

WARNING: Use of SDL 1.2 is deprecated and will be removed in
WARNING: future releases. Please switch to using SDL 2.0

NOTE: cross-compilers enabled: 'cc'
GEN x86_64-softmmu/config-devices.mak.tmp
---
CC hw/usb/host-stub.o
CC hw/virtio/virtio-bus.o
/tmp/qemu-test/src/hw/usb/ccid-card-emulated.c: In function 'init_event_notifier':
/tmp/qemu-test/src/hw/usb/ccid-card-emulated.c:404:9: error: implicit declaration of function 'error_setg' [-Werror=implicit-function-declaration]

          error_setg(errp, "ccid-card-emul: event notifier creation failed");
          ^
/tmp/qemu-test/src/hw/usb/ccid-card-emulated.c:404:9: error: nested extern declaration of 'error_setg' [-Werror=nested-externs]
/tmp/qemu-test/src/hw/usb/ccid-card-emulated.c: In function 'emulated_realize':
/tmp/qemu-test/src/hw/usb/ccid-card-emulated.c:513:13: error: implicit declaration of function 'error_append_hint' [-Werror=implicit-function-declaration]
              error_append_hint(errp, "%s\n", ptable->name);
              ^

/tmp/qemu-test/src/hw/usb/ccid-card-emulated.c:513:13: error: nested extern declaration of 'error_append_hint' [-Werror=nested-externs] /tmp/qemu-test/src/hw/usb/ccid-card-emulated.c: At top level:
cc1: error: unrecognized command line option "-Wno-format-truncation" [-Werror]
cc1: all warnings being treated as errors
make: *** [hw/usb/ccid-card-emulated.o] Error 1
make: *** Waiting for unfinished jobs....

The full log is available at
http://patchew.org/logs/[email protected]/testing.docker-quick@centos7/?type=message.
---
Email generated automatically by Patchew [http://patchew.org/].
Please send your feedback to [email protected]