]> xenbits.xensource.com Git - qemu-xen.git/commitdiff
util/defer-call: move defer_call() to util/
authorStefan Hajnoczi <stefanha@redhat.com>
Wed, 13 Sep 2023 20:00:43 +0000 (16:00 -0400)
committerKevin Wolf <kwolf@redhat.com>
Tue, 31 Oct 2023 14:41:42 +0000 (15:41 +0100)
The networking subsystem may wish to use defer_call(), so move the code
to util/ where it can be reused.

As a reminder of what defer_call() does:

This API defers a function call within a defer_call_begin()/defer_call_end()
section, allowing multiple calls to batch up. This is a performance
optimization that is used in the block layer to submit several I/O requests
at once instead of individually:

  defer_call_begin(); <-- start of section
  ...
  defer_call(my_func, my_obj); <-- deferred my_func(my_obj) call
  defer_call(my_func, my_obj); <-- another
  defer_call(my_func, my_obj); <-- another
  ...
  defer_call_end(); <-- end of section, my_func(my_obj) is called once

Suggested-by: Ilya Maximets <i.maximets@ovn.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-ID: <20230913200045.1024233-3-stefanha@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14 files changed:
MAINTAINERS
block/blkio.c
block/io_uring.c
block/linux-aio.c
block/meson.build
block/nvme.c
block/plug.c [deleted file]
hw/block/dataplane/xen-block.c
hw/block/virtio-blk.c
hw/scsi/virtio-scsi.c
include/qemu/defer-call.h [new file with mode: 0644]
include/sysemu/block-backend-io.h
util/defer-call.c [new file with mode: 0644]
util/meson.build

index cd8d6b140f4662e0bccde38e4341dc37edd0ada4..018ed62560f94ecfef5ff1d1dbeb7b550b3f8186 100644 (file)
@@ -2755,12 +2755,13 @@ S: Supported
 F: util/async.c
 F: util/aio-*.c
 F: util/aio-*.h
+F: util/defer-call.c
 F: util/fdmon-*.c
 F: block/io.c
-F: block/plug.c
 F: migration/block*
 F: include/block/aio.h
 F: include/block/aio-wait.h
+F: include/qemu/defer-call.h
 F: scripts/qemugdb/aio.py
 F: tests/unit/test-fdmon-epoll.c
 T: git https://github.com/stefanha/qemu.git block
index 7cf6d61f473ee6a99d68ec5e78a7eae3a3c0cff3..0a0a6c0f5fde6ae874982671dbe9842d2d8c70e0 100644 (file)
@@ -13,6 +13,7 @@
 #include "block/block_int.h"
 #include "exec/memory.h"
 #include "exec/cpu-common.h" /* for qemu_ram_get_fd() */
+#include "qemu/defer-call.h"
 #include "qapi/error.h"
 #include "qemu/error-report.h"
 #include "qapi/qmp/qdict.h"
index 8429f341be7c3affcdea11879f353e28cd3204b4..3a1e1f45b3cd318e1c2cc41ab88df580fae3725b 100644 (file)
@@ -15,6 +15,7 @@
 #include "block/block.h"
 #include "block/raw-aio.h"
 #include "qemu/coroutine.h"
+#include "qemu/defer-call.h"
 #include "qapi/error.h"
 #include "sysemu/block-backend.h"
 #include "trace.h"
index 49a37174c273c7f82f4f7d6415d34b9322281512..a2670b3e46269109eb253ac6b89dae1250c8d6a6 100644 (file)
@@ -14,6 +14,7 @@
 #include "block/raw-aio.h"
 #include "qemu/event_notifier.h"
 #include "qemu/coroutine.h"
+#include "qemu/defer-call.h"
 #include "qapi/error.h"
 #include "sysemu/block-backend.h"
 
index f351b9d0d3327554557829c64991a668eb5de12c..59ff6d380c041e415e0e49f127e787c94faa301e 100644 (file)
@@ -21,7 +21,6 @@ block_ss.add(files(
   'mirror.c',
   'nbd.c',
   'null.c',
-  'plug.c',
   'preallocate.c',
   'progress_meter.c',
   'qapi.c',
index dfbd1085fdbb1df9a4de6953772b56a172c7fe00..96b3f8f2fa1dcc19cde155e00d790907119f2380 100644 (file)
@@ -16,6 +16,7 @@
 #include "qapi/error.h"
 #include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qstring.h"
+#include "qemu/defer-call.h"
 #include "qemu/error-report.h"
 #include "qemu/main-loop.h"
 #include "qemu/module.h"
diff --git a/block/plug.c b/block/plug.c
deleted file mode 100644 (file)
index f261735..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Deferred calls
- *
- * Copyright Red Hat.
- *
- * This API defers a function call within a defer_call_begin()/defer_call_end()
- * section, allowing multiple calls to batch up. This is a performance
- * optimization that is used in the block layer to submit several I/O requests
- * at once instead of individually:
- *
- *   defer_call_begin(); <-- start of section
- *   ...
- *   defer_call(my_func, my_obj); <-- deferred my_func(my_obj) call
- *   defer_call(my_func, my_obj); <-- another
- *   defer_call(my_func, my_obj); <-- another
- *   ...
- *   defer_call_end(); <-- end of section, my_func(my_obj) is called once
- */
-
-#include "qemu/osdep.h"
-#include "qemu/coroutine-tls.h"
-#include "qemu/notify.h"
-#include "qemu/thread.h"
-#include "sysemu/block-backend.h"
-
-/* A function call that has been deferred until defer_call_end() */
-typedef struct {
-    void (*fn)(void *);
-    void *opaque;
-} DeferredCall;
-
-/* Per-thread state */
-typedef struct {
-    unsigned nesting_level;
-    GArray *deferred_call_array;
-} DeferCallThreadState;
-
-/* Use get_ptr_defer_call_thread_state() to fetch this thread-local value */
-QEMU_DEFINE_STATIC_CO_TLS(DeferCallThreadState, defer_call_thread_state);
-
-/* Called at thread cleanup time */
-static void defer_call_atexit(Notifier *n, void *value)
-{
-    DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state();
-    g_array_free(thread_state->deferred_call_array, TRUE);
-}
-
-/* This won't involve coroutines, so use __thread */
-static __thread Notifier defer_call_atexit_notifier;
-
-/**
- * defer_call:
- * @fn: a function pointer to be invoked
- * @opaque: a user-defined argument to @fn()
- *
- * Call @fn(@opaque) immediately if not within a
- * defer_call_begin()/defer_call_end() section.
- *
- * Otherwise defer the call until the end of the outermost
- * defer_call_begin()/defer_call_end() section in this thread. If the same
- * @fn/@opaque pair has already been deferred, it will only be called once upon
- * defer_call_end() so that accumulated calls are batched into a single call.
- *
- * The caller must ensure that @opaque is not freed before @fn() is invoked.
- */
-void defer_call(void (*fn)(void *), void *opaque)
-{
-    DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state();
-
-    /* Call immediately if we're not deferring calls */
-    if (thread_state->nesting_level == 0) {
-        fn(opaque);
-        return;
-    }
-
-    GArray *array = thread_state->deferred_call_array;
-    if (!array) {
-        array = g_array_new(FALSE, FALSE, sizeof(DeferredCall));
-        thread_state->deferred_call_array = array;
-        defer_call_atexit_notifier.notify = defer_call_atexit;
-        qemu_thread_atexit_add(&defer_call_atexit_notifier);
-    }
-
-    DeferredCall *fns = (DeferredCall *)array->data;
-    DeferredCall new_fn = {
-        .fn = fn,
-        .opaque = opaque,
-    };
-
-    /*
-     * There won't be many, so do a linear search. If this becomes a bottleneck
-     * then a binary search (glib 2.62+) or different data structure could be
-     * used.
-     */
-    for (guint i = 0; i < array->len; i++) {
-        if (memcmp(&fns[i], &new_fn, sizeof(new_fn)) == 0) {
-            return; /* already exists */
-        }
-    }
-
-    g_array_append_val(array, new_fn);
-}
-
-/**
- * defer_call_begin: Defer defer_call() functions until defer_call_end()
- *
- * defer_call_begin() and defer_call_end() are thread-local operations. The
- * caller must ensure that each defer_call_begin() has a matching
- * defer_call_end() in the same thread.
- *
- * Nesting is supported. defer_call() functions are only called at the
- * outermost defer_call_end().
- */
-void defer_call_begin(void)
-{
-    DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state();
-
-    assert(thread_state->nesting_level < UINT32_MAX);
-
-    thread_state->nesting_level++;
-}
-
-/**
- * defer_call_end: Run any pending defer_call() functions
- *
- * There must have been a matching defer_call_begin() call in the same thread
- * prior to this defer_call_end() call.
- */
-void defer_call_end(void)
-{
-    DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state();
-
-    assert(thread_state->nesting_level > 0);
-
-    if (--thread_state->nesting_level > 0) {
-        return;
-    }
-
-    GArray *array = thread_state->deferred_call_array;
-    if (!array) {
-        return;
-    }
-
-    DeferredCall *fns = (DeferredCall *)array->data;
-
-    for (guint i = 0; i < array->len; i++) {
-        fns[i].fn(fns[i].opaque);
-    }
-
-    /*
-     * This resets the array without freeing memory so that appending is cheap
-     * in the future.
-     */
-    g_array_set_size(array, 0);
-}
index e9dd8f8a996c04ffe7bfcb3e8e107c039c2ac2c9..c4bb28c66fede684b4a6ee02a2b89009f64869bc 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/defer-call.h"
 #include "qemu/error-report.h"
 #include "qemu/main-loop.h"
 #include "qemu/memalign.h"
index 6a45033d151a9dd3d377f1de80f9943096a1be0b..a1f8e155227591fb428f065d11658f133e348a6b 100644 (file)
@@ -12,6 +12,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/defer-call.h"
 #include "qapi/error.h"
 #include "qemu/iov.h"
 #include "qemu/module.h"
index 42fcbcb45f182b88666292199937728665fbf9f3..9c751bf29699697e40a24bc5149583f9380b7cee 100644 (file)
@@ -18,6 +18,7 @@
 #include "standard-headers/linux/virtio_ids.h"
 #include "hw/virtio/virtio-scsi.h"
 #include "migration/qemu-file-types.h"
+#include "qemu/defer-call.h"
 #include "qemu/error-report.h"
 #include "qemu/iov.h"
 #include "qemu/module.h"
diff --git a/include/qemu/defer-call.h b/include/qemu/defer-call.h
new file mode 100644 (file)
index 0000000..e2c1d24
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Deferred calls
+ *
+ * Copyright Red Hat.
+ */
+
+#ifndef QEMU_DEFER_CALL_H
+#define QEMU_DEFER_CALL_H
+
+/* See documentation in util/defer-call.c */
+void defer_call_begin(void);
+void defer_call_end(void);
+void defer_call(void (*fn)(void *), void *opaque);
+
+#endif /* QEMU_DEFER_CALL_H */
index cfcfd85c1d24d1b28ae689d236c9b317ee618938..d174275a5cb6bb5926cee22fc1a2e6afd99f2e5f 100644 (file)
@@ -100,10 +100,6 @@ void blk_iostatus_set_err(BlockBackend *blk, int error);
 int blk_get_max_iov(BlockBackend *blk);
 int blk_get_max_hw_iov(BlockBackend *blk);
 
-void defer_call_begin(void);
-void defer_call_end(void);
-void defer_call(void (*fn)(void *), void *opaque);
-
 AioContext *blk_get_aio_context(BlockBackend *blk);
 BlockAcctStats *blk_get_stats(BlockBackend *blk);
 void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk,
diff --git a/util/defer-call.c b/util/defer-call.c
new file mode 100644 (file)
index 0000000..037dc0a
--- /dev/null
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Deferred calls
+ *
+ * Copyright Red Hat.
+ *
+ * This API defers a function call within a defer_call_begin()/defer_call_end()
+ * section, allowing multiple calls to batch up. This is a performance
+ * optimization that is used in the block layer to submit several I/O requests
+ * at once instead of individually:
+ *
+ *   defer_call_begin(); <-- start of section
+ *   ...
+ *   defer_call(my_func, my_obj); <-- deferred my_func(my_obj) call
+ *   defer_call(my_func, my_obj); <-- another
+ *   defer_call(my_func, my_obj); <-- another
+ *   ...
+ *   defer_call_end(); <-- end of section, my_func(my_obj) is called once
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/coroutine-tls.h"
+#include "qemu/notify.h"
+#include "qemu/thread.h"
+#include "qemu/defer-call.h"
+
+/* A function call that has been deferred until defer_call_end() */
+typedef struct {
+    void (*fn)(void *);
+    void *opaque;
+} DeferredCall;
+
+/* Per-thread state */
+typedef struct {
+    unsigned nesting_level;
+    GArray *deferred_call_array;
+} DeferCallThreadState;
+
+/* Use get_ptr_defer_call_thread_state() to fetch this thread-local value */
+QEMU_DEFINE_STATIC_CO_TLS(DeferCallThreadState, defer_call_thread_state);
+
+/* Called at thread cleanup time */
+static void defer_call_atexit(Notifier *n, void *value)
+{
+    DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state();
+    g_array_free(thread_state->deferred_call_array, TRUE);
+}
+
+/* This won't involve coroutines, so use __thread */
+static __thread Notifier defer_call_atexit_notifier;
+
+/**
+ * defer_call:
+ * @fn: a function pointer to be invoked
+ * @opaque: a user-defined argument to @fn()
+ *
+ * Call @fn(@opaque) immediately if not within a
+ * defer_call_begin()/defer_call_end() section.
+ *
+ * Otherwise defer the call until the end of the outermost
+ * defer_call_begin()/defer_call_end() section in this thread. If the same
+ * @fn/@opaque pair has already been deferred, it will only be called once upon
+ * defer_call_end() so that accumulated calls are batched into a single call.
+ *
+ * The caller must ensure that @opaque is not freed before @fn() is invoked.
+ */
+void defer_call(void (*fn)(void *), void *opaque)
+{
+    DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state();
+
+    /* Call immediately if we're not deferring calls */
+    if (thread_state->nesting_level == 0) {
+        fn(opaque);
+        return;
+    }
+
+    GArray *array = thread_state->deferred_call_array;
+    if (!array) {
+        array = g_array_new(FALSE, FALSE, sizeof(DeferredCall));
+        thread_state->deferred_call_array = array;
+        defer_call_atexit_notifier.notify = defer_call_atexit;
+        qemu_thread_atexit_add(&defer_call_atexit_notifier);
+    }
+
+    DeferredCall *fns = (DeferredCall *)array->data;
+    DeferredCall new_fn = {
+        .fn = fn,
+        .opaque = opaque,
+    };
+
+    /*
+     * There won't be many, so do a linear search. If this becomes a bottleneck
+     * then a binary search (glib 2.62+) or different data structure could be
+     * used.
+     */
+    for (guint i = 0; i < array->len; i++) {
+        if (memcmp(&fns[i], &new_fn, sizeof(new_fn)) == 0) {
+            return; /* already exists */
+        }
+    }
+
+    g_array_append_val(array, new_fn);
+}
+
+/**
+ * defer_call_begin: Defer defer_call() functions until defer_call_end()
+ *
+ * defer_call_begin() and defer_call_end() are thread-local operations. The
+ * caller must ensure that each defer_call_begin() has a matching
+ * defer_call_end() in the same thread.
+ *
+ * Nesting is supported. defer_call() functions are only called at the
+ * outermost defer_call_end().
+ */
+void defer_call_begin(void)
+{
+    DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state();
+
+    assert(thread_state->nesting_level < UINT32_MAX);
+
+    thread_state->nesting_level++;
+}
+
+/**
+ * defer_call_end: Run any pending defer_call() functions
+ *
+ * There must have been a matching defer_call_begin() call in the same thread
+ * prior to this defer_call_end() call.
+ */
+void defer_call_end(void)
+{
+    DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state();
+
+    assert(thread_state->nesting_level > 0);
+
+    if (--thread_state->nesting_level > 0) {
+        return;
+    }
+
+    GArray *array = thread_state->deferred_call_array;
+    if (!array) {
+        return;
+    }
+
+    DeferredCall *fns = (DeferredCall *)array->data;
+
+    for (guint i = 0; i < array->len; i++) {
+        fns[i].fn(fns[i].opaque);
+    }
+
+    /*
+     * This resets the array without freeing memory so that appending is cheap
+     * in the future.
+     */
+    g_array_set_size(array, 0);
+}
index c4827fd70aa241d4c3ba7a868b2e4ed253b26a8e..769b24f2e0df23a755a4ed460c5d67cfc49aa4c4 100644 (file)
@@ -28,6 +28,7 @@ util_ss.add(when: 'CONFIG_WIN32', if_true: pathcch)
 if glib_has_gslice
   util_ss.add(files('qtree.c'))
 endif
+util_ss.add(files('defer-call.c'))
 util_ss.add(files('envlist.c', 'path.c', 'module.c'))
 util_ss.add(files('host-utils.c'))
 util_ss.add(files('bitmap.c', 'bitops.c'))