qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v2 0/2] Dynamic module support for block drivers
@ 2015-09-08 13:53 Marc Marí
  2015-09-08 13:53 ` [Qemu-devel] [PATCH v2 1/2] Add dynamic module loading " Marc Marí
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Marc Marí @ 2015-09-08 13:53 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Fam Zheng, Stefan Hajnoczi, Michael Tokarev,
	Marc Marí

The current module infrastructure has been improved to enable dynamic module
loading.

This reduces the load time for very simple guests. For the following
configuration (very loaded)

./configure --enable-sdl --enable-gtk --enable-vte --enable-curses \
    --enable-vnc --enable-vnc-{jpeg,tls,sasl,png} --enable-virtfs \
    --enable-brlapi --enable-curl --enable-fdt --enable-bluez \
    --enable-kvm --enable-rdma --enable-uuid --enable-vde \
    --enable-linux-aio --enable-cap-ng --enable-attr --enable-vhost-net \
    --enable-vhost-scsi --enable-spice --enable-rbd --enable-libiscsi \
    --enable-smartcard-nss --enable-guest-agent --enable-libusb \
    --enable-usb-redir --enable-lzo --enable-snappy --enable-bzip2 \
    --enable-seccomp --enable-coroutine-pool --enable-glusterfs \
    --enable-tpm --enable-libssh2 --enable-vhdx --enable-numa \
    --enable-tcmalloc --target-list=x86_64-softmmu

With modules disabled, there are 142 libraries loaded at startup. Time is
the following:
 LD time: 0.065 seconds
 QEMU time: 0.02 seconds
 Total time: 0.085 seconds

With this patch series and modules enabled, there are 128 libraries loaded
at startup. Time is the following:
 LD time: 0.02 seconds
 QEMU time: 0.02 seconds
 Total time: 0.04 seconds

Where LD time is the time between the program startup and the jump to main,
and QEMU time is the time between the start of main and the first kvm_entry.

These results are just with a few block drivers, that were already a module.
Adding more modules (block or not block) should be easy, and will reduce
the load time even more.

Marc Marí (2):
  Add dynamic module loading for block drivers
  Add dynamic generation of module_block.h

 .gitignore                      |   1 +
 Makefile                        |  10 ++-
 block.c                         |  70 +++++++++++++++++++++
 configure                       |   2 +-
 include/qemu/module.h           |   3 +
 scripts/modules/module_block.py | 134 ++++++++++++++++++++++++++++++++++++++++
 util/module.c                   |  38 ++++--------
 7 files changed, 227 insertions(+), 31 deletions(-)
 create mode 100755 scripts/modules/module_block.py

-- 
2.4.3

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH v2 1/2] Add dynamic module loading for block drivers
  2015-09-08 13:53 [Qemu-devel] [PATCH v2 0/2] Dynamic module support for block drivers Marc Marí
@ 2015-09-08 13:53 ` Marc Marí
  2015-09-21 13:49   ` Denis V. Lunev
  2015-09-08 13:53 ` [Qemu-devel] [PATCH v2 2/2] Add dynamic generation of module_block.h Marc Marí
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 10+ messages in thread
From: Marc Marí @ 2015-09-08 13:53 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Fam Zheng, Stefan Hajnoczi, Michael Tokarev,
	Marc Marí

Extend the current module interface to allow for block drivers to be loaded
dynamically on request.

The only block drivers that can be converted into modules are the drivers
that don't perform any init operation except for registering themselves. This
is why libiscsi has been disabled as a module.

All the necessary module information is located in a new structure found in
include/qemu/module_block.h

Signed-off-by: Marc Marí <markmb@redhat.com>
---
 block.c                     | 70 +++++++++++++++++++++++++++++++++++
 configure                   |  2 +-
 include/qemu/module.h       |  3 ++
 include/qemu/module_block.h | 90 +++++++++++++++++++++++++++++++++++++++++++++
 util/module.c               | 38 ++++++-------------
 5 files changed, 175 insertions(+), 28 deletions(-)
 create mode 100644 include/qemu/module_block.h

diff --git a/block.c b/block.c
index 090923c..814429a 100644
--- a/block.c
+++ b/block.c
@@ -27,6 +27,7 @@
 #include "block/block_int.h"
 #include "block/blockjob.h"
 #include "qemu/error-report.h"
+#include "qemu/module_block.h"
 #include "qemu/module.h"
 #include "qapi/qmp/qerror.h"
 #include "qapi/qmp/qjson.h"
@@ -277,11 +278,30 @@ void bdrv_add_close_notifier(BlockDriverState *bs, Notifier *notify)
 BlockDriver *bdrv_find_format(const char *format_name)
 {
     BlockDriver *drv1;
+    size_t i;
+
     QLIST_FOREACH(drv1, &bdrv_drivers, list) {
         if (!strcmp(drv1->format_name, format_name)) {
             return drv1;
         }
     }
+
+    for (i = 0; i < ARRAY_SIZE(block_driver_modules); ++i) {
+        if (!strcmp(block_driver_modules[i].format_name, format_name)) {
+            block_module_load_one(block_driver_modules[i].library_name);
+            /* Copying code is not nice, but this way the current discovery is
+             * not modified. Calling recursively could fail if the library
+             * has been deleted.
+             */
+            QLIST_FOREACH(drv1, &bdrv_drivers, list) {
+                if (!strcmp(drv1->format_name, format_name)) {
+                    return drv1;
+                }
+            }
+        }
+    }
+
+
     return NULL;
 }
 
@@ -484,8 +504,15 @@ int get_tmp_filename(char *filename, int size)
 static BlockDriver *find_hdev_driver(const char *filename)
 {
     int score_max = 0, score;
+    size_t i;
     BlockDriver *drv = NULL, *d;
 
+    for (i = 0; i < ARRAY_SIZE(block_driver_modules); ++i) {
+        if (block_driver_modules[i].has_probe_device) {
+            block_module_load_one(block_driver_modules[i].library_name);
+        }
+    }
+
     QLIST_FOREACH(d, &bdrv_drivers, list) {
         if (d->bdrv_probe_device) {
             score = d->bdrv_probe_device(filename);
@@ -507,6 +534,7 @@ BlockDriver *bdrv_find_protocol(const char *filename,
     char protocol[128];
     int len;
     const char *p;
+    size_t i;
 
     /* TODO Drivers without bdrv_file_open must be specified explicitly */
 
@@ -533,6 +561,7 @@ BlockDriver *bdrv_find_protocol(const char *filename,
         len = sizeof(protocol) - 1;
     memcpy(protocol, filename, len);
     protocol[len] = '\0';
+
     QLIST_FOREACH(drv1, &bdrv_drivers, list) {
         if (drv1->protocol_name &&
             !strcmp(drv1->protocol_name, protocol)) {
@@ -540,6 +569,23 @@ BlockDriver *bdrv_find_protocol(const char *filename,
         }
     }
 
+    for (i = 0; i < ARRAY_SIZE(block_driver_modules); ++i) {
+        if (block_driver_modules[i].protocol_name &&
+            !strcmp(block_driver_modules[i].protocol_name, protocol)) {
+            block_module_load_one(block_driver_modules[i].library_name);
+            /* Copying code is not nice, but this way the current discovery is
+             * not modified. Calling recursively could fail if the library
+             * has been deleted.
+             */
+            QLIST_FOREACH(drv1, &bdrv_drivers, list) {
+                if (drv1->protocol_name &&
+                    !strcmp(drv1->protocol_name, protocol)) {
+                    return drv1;
+                }
+            }
+        }
+    }
+
     error_setg(errp, "Unknown protocol '%s'", protocol);
     return NULL;
 }
@@ -562,8 +608,15 @@ BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size,
                             const char *filename)
 {
     int score_max = 0, score;
+    size_t i;
     BlockDriver *drv = NULL, *d;
 
+    for (i = 0; i < ARRAY_SIZE(block_driver_modules); ++i) {
+        if (block_driver_modules[i].has_probe) {
+            block_module_load_one(block_driver_modules[i].library_name);
+        }
+    }
+
     QLIST_FOREACH(d, &bdrv_drivers, list) {
         if (d->bdrv_probe) {
             score = d->bdrv_probe(buf, buf_size, filename);
@@ -2784,6 +2837,7 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
     BlockDriver *drv;
     int count = 0;
     int i;
+    size_t n;
     const char **formats = NULL;
 
     QLIST_FOREACH(drv, &bdrv_drivers, list) {
@@ -2801,6 +2855,22 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
         }
     }
 
+    for (n = 0; n < ARRAY_SIZE(block_driver_modules); ++n) {
+        if (block_driver_modules[n].format_name) {
+            bool found = false;
+            int i = count;
+            while (formats && i && !found) {
+                found = !strcmp(formats[--i],
+                                block_driver_modules[n].format_name);
+            }
+
+            if (!found) {
+                formats = g_renew(const char *, formats, count + 1);
+                formats[count++] = block_driver_modules[n].format_name;
+            }
+        }
+    }
+
     qsort(formats, count, sizeof(formats[0]), qsort_strcmp);
 
     for (i = 0; i < count; i++) {
diff --git a/configure b/configure
index d854936..72ac564 100755
--- a/configure
+++ b/configure
@@ -4984,7 +4984,7 @@ if test "$bzip2" = "yes" ; then
 fi
 
 if test "$libiscsi" = "yes" ; then
-  echo "CONFIG_LIBISCSI=m" >> $config_host_mak
+  echo "CONFIG_LIBISCSI=y" >> $config_host_mak
   echo "LIBISCSI_CFLAGS=$libiscsi_cflags" >> $config_host_mak
   echo "LIBISCSI_LIBS=$libiscsi_libs" >> $config_host_mak
 fi
diff --git a/include/qemu/module.h b/include/qemu/module.h
index 72d9498..0ad4bb7 100644
--- a/include/qemu/module.h
+++ b/include/qemu/module.h
@@ -53,9 +53,12 @@ typedef enum {
 #define qapi_init(function) module_init(function, MODULE_INIT_QAPI)
 #define type_init(function) module_init(function, MODULE_INIT_QOM)
 
+#define block_module_load_one(lib) module_load_one("block-", lib);
+
 void register_module_init(void (*fn)(void), module_init_type type);
 void register_dso_module_init(void (*fn)(void), module_init_type type);
 
 void module_call_init(module_init_type type);
+void module_load_one(const char *prefix, const char *lib_name);
 
 #endif
diff --git a/include/qemu/module_block.h b/include/qemu/module_block.h
new file mode 100644
index 0000000..d725db8
--- /dev/null
+++ b/include/qemu/module_block.h
@@ -0,0 +1,90 @@
+/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
+/*
+ * QEMU Block Module Infrastructure
+ *
+ * Copyright Red Hat, Inc. 2015
+ *
+ * Authors:
+ *  Marc Mari       <markmb@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_MODULE_BLOCK_H
+#define QEMU_MODULE_BLOCK_H
+
+#include "qemu-common.h"
+
+static const struct {
+    const char *format_name;
+    const char *protocol_name;
+    const char *library_name;
+    bool has_probe;
+    bool has_probe_device;
+} block_driver_modules[] = {
+	{
+	.library_name = "curl",
+	.format_name = "http",
+	.protocol_name = "http",
+	},
+	{
+	.library_name = "curl",
+	.format_name = "https",
+	.protocol_name = "https",
+	},
+	{
+	.library_name = "curl",
+	.format_name = "ftp",
+	.protocol_name = "ftp",
+	},
+	{
+	.library_name = "curl",
+	.format_name = "ftps",
+	.protocol_name = "ftps",
+	},
+	{
+	.library_name = "curl",
+	.format_name = "tftp",
+	.protocol_name = "tftp",
+	},
+	{
+	.library_name = "rbd",
+	.format_name = "rbd",
+	.protocol_name = "rbd",
+	},
+	{
+	.library_name = "gluster",
+	.format_name = "gluster",
+	.protocol_name = "gluster",
+	},
+	{
+	.library_name = "gluster",
+	.format_name = "gluster",
+	.protocol_name = "gluster+tcp",
+	},
+	{
+	.library_name = "gluster",
+	.format_name = "gluster",
+	.protocol_name = "gluster+unix",
+	},
+	{
+	.library_name = "gluster",
+	.format_name = "gluster",
+	.protocol_name = "gluster+rdma",
+	},
+	{
+	.library_name = "ssh",
+	.format_name = "ssh",
+	.protocol_name = "ssh",
+	},
+	{
+	.library_name = "dmg",
+	.format_name = "dmg",
+	.has_probe = true,
+	},
+};
+
+#endif
+
diff --git a/util/module.c b/util/module.c
index 4bd4a94..992d317 100644
--- a/util/module.c
+++ b/util/module.c
@@ -91,14 +91,11 @@ void register_dso_module_init(void (*fn)(void), module_init_type type)
     QTAILQ_INSERT_TAIL(&dso_init_list, e, node);
 }
 
-static void module_load(module_init_type type);
-
 void module_call_init(module_init_type type)
 {
     ModuleTypeList *l;
     ModuleEntry *e;
 
-    module_load(type);
     l = find_type(type);
 
     QTAILQ_FOREACH(e, l, node) {
@@ -149,6 +146,7 @@ static int module_load_file(const char *fname)
         ret = -EINVAL;
     } else {
         QTAILQ_FOREACH(e, &dso_init_list, node) {
+            e->init();
             register_module_init(e->init, e->type);
         }
         ret = 0;
@@ -163,14 +161,10 @@ out:
 }
 #endif
 
-static void module_load(module_init_type type)
+void module_load_one(const char *prefix, const char *lib_name)
 {
 #ifdef CONFIG_MODULES
     char *fname = NULL;
-    const char **mp;
-    static const char *block_modules[] = {
-        CONFIG_BLOCK_MODULES
-    };
     char *exec_dir;
     char *dirs[3];
     int i = 0;
@@ -181,15 +175,6 @@ static void module_load(module_init_type type)
         return;
     }
 
-    switch (type) {
-    case MODULE_INIT_BLOCK:
-        mp = block_modules;
-        break;
-    default:
-        /* no other types have dynamic modules for now*/
-        return;
-    }
-
     exec_dir = qemu_get_exec_dir();
     dirs[i++] = g_strdup_printf("%s", CONFIG_QEMU_MODDIR);
     dirs[i++] = g_strdup_printf("%s/..", exec_dir ? : "");
@@ -198,16 +183,15 @@ static void module_load(module_init_type type)
     g_free(exec_dir);
     exec_dir = NULL;
 
-    for ( ; *mp; mp++) {
-        for (i = 0; i < ARRAY_SIZE(dirs); i++) {
-            fname = g_strdup_printf("%s/%s%s", dirs[i], *mp, HOST_DSOSUF);
-            ret = module_load_file(fname);
-            g_free(fname);
-            fname = NULL;
-            /* Try loading until loaded a module file */
-            if (!ret) {
-                break;
-            }
+    for (i = 0; i < ARRAY_SIZE(dirs); i++) {
+        fname = g_strdup_printf("%s/%s%s%s",
+                dirs[i], prefix, lib_name, HOST_DSOSUF);
+        ret = module_load_file(fname);
+        g_free(fname);
+        fname = NULL;
+        /* Try loading until loaded a module file */
+        if (!ret) {
+            break;
         }
     }
 
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH v2 2/2] Add dynamic generation of module_block.h
  2015-09-08 13:53 [Qemu-devel] [PATCH v2 0/2] Dynamic module support for block drivers Marc Marí
  2015-09-08 13:53 ` [Qemu-devel] [PATCH v2 1/2] Add dynamic module loading " Marc Marí
@ 2015-09-08 13:53 ` Marc Marí
  2015-09-09  2:27   ` Fam Zheng
  2015-09-21 13:27 ` [Qemu-devel] [PATCH v2 0/2] Dynamic module support for block drivers Marc Marí
  2015-09-21 13:43 ` Denis V. Lunev
  3 siblings, 1 reply; 10+ messages in thread
From: Marc Marí @ 2015-09-08 13:53 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Fam Zheng, Stefan Hajnoczi, Michael Tokarev,
	Marc Marí

To simplify the addition of new block modules, add a script that generates
include/qemu/module_block.h automatically from the modules' source code.

This script assumes that the QEMU coding style rules are followed.

Signed-off-by: Marc Marí <markmb@redhat.com>
---
 .gitignore                      |   1 +
 Makefile                        |  10 ++-
 include/qemu/module_block.h     |  90 ---------------------------
 scripts/modules/module_block.py | 134 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 142 insertions(+), 93 deletions(-)
 delete mode 100644 include/qemu/module_block.h
 create mode 100755 scripts/modules/module_block.py

diff --git a/.gitignore b/.gitignore
index cb4b8ec..8428dd0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -106,3 +106,4 @@ cscope.*
 tags
 TAGS
 *~
+/include/qemu/module_block.h
diff --git a/Makefile b/Makefile
index 9ce3972..6b23e14 100644
--- a/Makefile
+++ b/Makefile
@@ -73,6 +73,8 @@ GENERATED_HEADERS += trace/generated-ust-provider.h
 GENERATED_SOURCES += trace/generated-ust.c
 endif
 
+GENERATED_HEADERS += include/qemu/module_block.h
+
 # Don't try to regenerate Makefile or configure
 # We don't generate any of them
 Makefile: ;
@@ -220,9 +222,6 @@ Makefile: $(version-obj-y) $(version-lobj-y)
 libqemustub.a: $(stub-obj-y)
 libqemuutil.a: $(util-obj-y)
 
-block-modules = $(foreach o,$(block-obj-m),"$(basename $(subst /,-,$o))",) NULL
-util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)'
-
 ######################################################################
 
 qemu-img.o: qemu-img-cmds.h
@@ -313,6 +312,11 @@ msi:
 	@echo "MSI build not configured or dependency resolution failed (reconfigure with --enable-guest-agent-msi option)"
 endif
 
+include/qemu/module_block.h: $(SRC_PATH)/scripts/modules/module_block.py
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/modules/module_block.py \
+		"./include/qemu/" $(patsubst %.mo,%.c,$(block-obj-m)), \
+		"  GEN   $@")
+
 clean:
 # avoid old build problems by removing potentially incorrect old files
 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
diff --git a/include/qemu/module_block.h b/include/qemu/module_block.h
deleted file mode 100644
index d725db8..0000000
--- a/include/qemu/module_block.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
-/*
- * QEMU Block Module Infrastructure
- *
- * Copyright Red Hat, Inc. 2015
- *
- * Authors:
- *  Marc Mari       <markmb@redhat.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
- *
- */
-
-#ifndef QEMU_MODULE_BLOCK_H
-#define QEMU_MODULE_BLOCK_H
-
-#include "qemu-common.h"
-
-static const struct {
-    const char *format_name;
-    const char *protocol_name;
-    const char *library_name;
-    bool has_probe;
-    bool has_probe_device;
-} block_driver_modules[] = {
-	{
-	.library_name = "curl",
-	.format_name = "http",
-	.protocol_name = "http",
-	},
-	{
-	.library_name = "curl",
-	.format_name = "https",
-	.protocol_name = "https",
-	},
-	{
-	.library_name = "curl",
-	.format_name = "ftp",
-	.protocol_name = "ftp",
-	},
-	{
-	.library_name = "curl",
-	.format_name = "ftps",
-	.protocol_name = "ftps",
-	},
-	{
-	.library_name = "curl",
-	.format_name = "tftp",
-	.protocol_name = "tftp",
-	},
-	{
-	.library_name = "rbd",
-	.format_name = "rbd",
-	.protocol_name = "rbd",
-	},
-	{
-	.library_name = "gluster",
-	.format_name = "gluster",
-	.protocol_name = "gluster",
-	},
-	{
-	.library_name = "gluster",
-	.format_name = "gluster",
-	.protocol_name = "gluster+tcp",
-	},
-	{
-	.library_name = "gluster",
-	.format_name = "gluster",
-	.protocol_name = "gluster+unix",
-	},
-	{
-	.library_name = "gluster",
-	.format_name = "gluster",
-	.protocol_name = "gluster+rdma",
-	},
-	{
-	.library_name = "ssh",
-	.format_name = "ssh",
-	.protocol_name = "ssh",
-	},
-	{
-	.library_name = "dmg",
-	.format_name = "dmg",
-	.has_probe = true,
-	},
-};
-
-#endif
-
diff --git a/scripts/modules/module_block.py b/scripts/modules/module_block.py
new file mode 100755
index 0000000..0846362
--- /dev/null
+++ b/scripts/modules/module_block.py
@@ -0,0 +1,134 @@
+#!/usr/bin/python
+#
+# Module information generator
+#
+# Copyright Red Hat, Inc. 2015
+#
+# Authors:
+#  Marc Mari <markmb@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2.
+# See the COPYING file in the top-level directory.
+
+from __future__ import print_function
+import sys
+import os
+
+def get_string_struct(line):
+    data = line.split()
+
+    # data[0] -> struct element name
+    # data[1] -> =
+    # data[2] -> value
+
+    return data[2].replace('"', '')[:-1]
+
+def add_module(fhader, library, format_name, protocol_name,
+                probe, probe_device):
+    lines = []
+    lines.append('.library_name = "' + library + '",')
+    if format_name != "":
+        lines.append('.format_name = "' + format_name + '",')
+    if protocol_name != "":
+        lines.append('.protocol_name = "' + protocol_name + '",')
+    if probe:
+        lines.append('.has_probe = true,')
+    if probe_device:
+        lines.append('.has_probe_device = true,')
+
+    text = '\n\t'.join(lines)
+    fheader.write('\n\t{\n\t' + text + '\n\t},')
+
+def process_file(fheader, filename):
+    # This parser assumes the coding style rules are being followed
+    with open(filename, "r") as cfile:
+        found_something = False
+        found_start = False
+        library, _ = os.path.splitext(os.path.basename(filename))
+        for line in cfile:
+            if found_start:
+                line = line.replace('\n', '')
+                if line.find(".format_name") != -1:
+                    format_name = get_string_struct(line)
+                elif line.find(".protocol_name") != -1:
+                    protocol_name = get_string_struct(line)
+                elif line.find(".bdrv_probe") != -1:
+                    probe = True
+                elif line.find(".bdrv_probe_device") != -1:
+                    probe_device = True
+                elif line == "};":
+                    add_module(fheader, library, format_name, protocol_name,
+                                probe, probe_device)
+                    found_start = False
+            elif line.find("static BlockDriver") != -1:
+                found_something = True
+                found_start = True
+                format_name = ""
+                protocol_name = ""
+                probe = False
+                probe_device = False
+
+        if not found_something:
+            print("No BlockDriver struct found in " + filename + ". \
+                    Is this really a module?", file=sys.stderr)
+            sys.exit(1)
+
+def print_top(fheader):
+    fheader.write('''/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
+/*
+ * QEMU Block Module Infrastructure
+ *
+ * Copyright Red Hat, Inc. 2015
+ *
+ * Authors:
+ *  Marc Mari       <markmb@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+''')
+
+    fheader.write('''#ifndef QEMU_MODULE_BLOCK_H
+#define QEMU_MODULE_BLOCK_H
+
+#include "qemu-common.h"
+
+static const struct {
+    const char *format_name;
+    const char *protocol_name;
+    const char *library_name;
+    bool has_probe;
+    bool has_probe_device;
+} block_driver_modules[] = {''')
+
+def print_bottom(fheader):
+    fheader.write('''
+};
+
+#endif
+''')
+
+# First argument: output folder
+# All other arguments: modules source files (.c)
+output_dir = sys.argv[1]
+if not os.path.isdir(output_dir):
+    print("Folder " + output_dir + " does not exist", file=sys.stderr)
+    sys.exit(1)
+
+path = output_dir + 'module_block.h'
+
+with open(path, 'w') as fheader:
+    print_top(fheader)
+
+    for filename in sys.argv[2:]:
+        if os.path.isfile(filename):
+            process_file(fheader, filename)
+        else:
+            print("File " + filename + " does not exist.", file=sys.stderr)
+            sys.exit(1)
+
+    print_bottom(fheader)
+
+sys.exit(0)
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v2 2/2] Add dynamic generation of module_block.h
  2015-09-08 13:53 ` [Qemu-devel] [PATCH v2 2/2] Add dynamic generation of module_block.h Marc Marí
@ 2015-09-09  2:27   ` Fam Zheng
  2015-09-09  7:37     ` Marc Marí
  0 siblings, 1 reply; 10+ messages in thread
From: Fam Zheng @ 2015-09-09  2:27 UTC (permalink / raw)
  To: Marc Marí
  Cc: Stefan Hajnoczi, Michael Tokarev, qemu-devel, Peter Maydell

On Tue, 09/08 15:53, Marc Marí wrote:
> diff --git a/include/qemu/module_block.h b/include/qemu/module_block.h
> deleted file mode 100644
> index d725db8..0000000
> --- a/include/qemu/module_block.h
> +++ /dev/null
> @@ -1,90 +0,0 @@
> -/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
> -/*
> - * QEMU Block Module Infrastructure
> - *
> - * Copyright Red Hat, Inc. 2015
> - *
> - * Authors:
> - *  Marc Mari       <markmb@redhat.com>
> - *
> - * This work is licensed under the terms of the GNU GPL, version 2.  See
> - * the COPYING file in the top-level directory.
> - *
> - */
> -

Could you reorder the patches so you don't need to add them remove the
generated header?

> diff --git a/scripts/modules/module_block.py b/scripts/modules/module_block.py
> new file mode 100755
> index 0000000..0846362
> --- /dev/null
> +++ b/scripts/modules/module_block.py
> @@ -0,0 +1,134 @@
> +#!/usr/bin/python
> +#
> +# Module information generator
> +#
> +# Copyright Red Hat, Inc. 2015
> +#
> +# Authors:
> +#  Marc Mari <markmb@redhat.com>
> +#
> +# This work is licensed under the terms of the GNU GPL, version 2.
> +# See the COPYING file in the top-level directory.
> +
> +from __future__ import print_function
> +import sys
> +import os
> +
> +def get_string_struct(line):
> +    data = line.split()
> +
> +    # data[0] -> struct element name
> +    # data[1] -> =
> +    # data[2] -> value
> +
> +    return data[2].replace('"', '')[:-1]
> +
> +def add_module(fhader, library, format_name, protocol_name,
> +                probe, probe_device):
> +    lines = []
> +    lines.append('.library_name = "' + library + '",')
> +    if format_name != "":
> +        lines.append('.format_name = "' + format_name + '",')
> +    if protocol_name != "":
> +        lines.append('.protocol_name = "' + protocol_name + '",')
> +    if probe:
> +        lines.append('.has_probe = true,')
> +    if probe_device:
> +        lines.append('.has_probe_device = true,')
> +
> +    text = '\n\t'.join(lines)
> +    fheader.write('\n\t{\n\t' + text + '\n\t},')
> +
> +def process_file(fheader, filename):
> +    # This parser assumes the coding style rules are being followed
> +    with open(filename, "r") as cfile:
> +        found_something = False
> +        found_start = False
> +        library, _ = os.path.splitext(os.path.basename(filename))
> +        for line in cfile:
> +            if found_start:
> +                line = line.replace('\n', '')
> +                if line.find(".format_name") != -1:
> +                    format_name = get_string_struct(line)
> +                elif line.find(".protocol_name") != -1:
> +                    protocol_name = get_string_struct(line)
> +                elif line.find(".bdrv_probe") != -1:
> +                    probe = True
> +                elif line.find(".bdrv_probe_device") != -1:
> +                    probe_device = True
> +                elif line == "};":
> +                    add_module(fheader, library, format_name, protocol_name,
> +                                probe, probe_device)
> +                    found_start = False
> +            elif line.find("static BlockDriver") != -1:
> +                found_something = True
> +                found_start = True
> +                format_name = ""
> +                protocol_name = ""
> +                probe = False
> +                probe_device = False
> +
> +        if not found_something:
> +            print("No BlockDriver struct found in " + filename + ". \
> +                    Is this really a module?", file=sys.stderr)
> +            sys.exit(1)
> +
> +def print_top(fheader):
> +    fheader.write('''/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
> +/*
> + * QEMU Block Module Infrastructure
> + *
> + * Copyright Red Hat, Inc. 2015
> + *
> + * Authors:
> + *  Marc Mari       <markmb@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2.  See
> + * the COPYING file in the top-level directory.
> + *
> + */
> +
> +''')
> +
> +    fheader.write('''#ifndef QEMU_MODULE_BLOCK_H
> +#define QEMU_MODULE_BLOCK_H
> +
> +#include "qemu-common.h"
> +
> +static const struct {
> +    const char *format_name;
> +    const char *protocol_name;
> +    const char *library_name;
> +    bool has_probe;
> +    bool has_probe_device;
> +} block_driver_modules[] = {''')
> +
> +def print_bottom(fheader):
> +    fheader.write('''
> +};
> +
> +#endif
> +''')
> +
> +# First argument: output folder
> +# All other arguments: modules source files (.c)
> +output_dir = sys.argv[1]
> +if not os.path.isdir(output_dir):
> +    print("Folder " + output_dir + " does not exist", file=sys.stderr)
> +    sys.exit(1)
> +
> +path = output_dir + 'module_block.h'
> +
> +with open(path, 'w') as fheader:
> +    print_top(fheader)
> +
> +    for filename in sys.argv[2:]:
> +        if os.path.isfile(filename):
> +            process_file(fheader, filename)
> +        else:
> +            print("File " + filename + " does not exist.", file=sys.stderr)
> +            sys.exit(1)
> +
> +    print_bottom(fheader)
> +
> +sys.exit(0)

If we decide to parse c sources, I really want the parser as simple as
possible, while keeping the syntax explicit:

in block_int.h:

    /* In order to allow dynamical loading, block modules should specify which
     * protocols and formats are supported with one or more of following
     * macros, which are parsed by code generator to create mappings from
     * format or protocol name to module name.
     */
    #define BLOCK_PROTOCOL_MODULE(protocol, has_probe)
    #define BLOCK_FORMAT_MODULE(format, has_probe)

in curl.c:
    BLOCK_PROTOCOL_MODULE(http, false)
    BLOCK_PROTOCOL_MODULE(https, false)
    BLOCK_PROTOCOL_MODULE(ftp, false)
    BLOCK_PROTOCOL_MODULE(ftps, false)
    BLOCK_PROTOCOL_MODULE(tftp, false)

in dmg.c:
    BLOCK_FORMAT_MODULE(dmg, true)

In generator.py, you can grep for "BLOCK_PROTOCOL_MODULE(*)" and
"BLOCK_FORMAT_MODULE(*)".

Fam

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v2 2/2] Add dynamic generation of module_block.h
  2015-09-09  2:27   ` Fam Zheng
@ 2015-09-09  7:37     ` Marc Marí
  0 siblings, 0 replies; 10+ messages in thread
From: Marc Marí @ 2015-09-09  7:37 UTC (permalink / raw)
  To: Fam Zheng; +Cc: Stefan Hajnoczi, Michael Tokarev, qemu-devel, Peter Maydell

On Wed, 9 Sep 2015 10:27:13 +0800
Fam Zheng <famz@redhat.com> wrote:

> On Tue, 09/08 15:53, Marc Marí wrote:
> > diff --git a/include/qemu/module_block.h
> > b/include/qemu/module_block.h deleted file mode 100644
> > index d725db8..0000000
> > --- a/include/qemu/module_block.h
> > +++ /dev/null
> > @@ -1,90 +0,0 @@
> > -/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
> > -/*
> > - * QEMU Block Module Infrastructure
> > - *
> > - * Copyright Red Hat, Inc. 2015
> > - *
> > - * Authors:
> > - *  Marc Mari       <markmb@redhat.com>
> > - *
> > - * This work is licensed under the terms of the GNU GPL, version
> > 2.  See
> > - * the COPYING file in the top-level directory.
> > - *
> > - */
> > -
> 
> Could you reorder the patches so you don't need to add them remove the
> generated header?
> 
> > diff --git a/scripts/modules/module_block.py
> > b/scripts/modules/module_block.py new file mode 100755
> > index 0000000..0846362
> > --- /dev/null
> > +++ b/scripts/modules/module_block.py
> > @@ -0,0 +1,134 @@
> > +#!/usr/bin/python
> > +#
> > +# Module information generator
> > +#
> > +# Copyright Red Hat, Inc. 2015
> > +#
> > +# Authors:
> > +#  Marc Mari <markmb@redhat.com>
> > +#
> > +# This work is licensed under the terms of the GNU GPL, version 2.
> > +# See the COPYING file in the top-level directory.
> > +
> > +from __future__ import print_function
> > +import sys
> > +import os
> > +
> > +def get_string_struct(line):
> > +    data = line.split()
> > +
> > +    # data[0] -> struct element name
> > +    # data[1] -> =
> > +    # data[2] -> value
> > +
> > +    return data[2].replace('"', '')[:-1]
> > +
> > +def add_module(fhader, library, format_name, protocol_name,
> > +                probe, probe_device):
> > +    lines = []
> > +    lines.append('.library_name = "' + library + '",')
> > +    if format_name != "":
> > +        lines.append('.format_name = "' + format_name + '",')
> > +    if protocol_name != "":
> > +        lines.append('.protocol_name = "' + protocol_name + '",')
> > +    if probe:
> > +        lines.append('.has_probe = true,')
> > +    if probe_device:
> > +        lines.append('.has_probe_device = true,')
> > +
> > +    text = '\n\t'.join(lines)
> > +    fheader.write('\n\t{\n\t' + text + '\n\t},')
> > +
> > +def process_file(fheader, filename):
> > +    # This parser assumes the coding style rules are being followed
> > +    with open(filename, "r") as cfile:
> > +        found_something = False
> > +        found_start = False
> > +        library, _ = os.path.splitext(os.path.basename(filename))
> > +        for line in cfile:
> > +            if found_start:
> > +                line = line.replace('\n', '')
> > +                if line.find(".format_name") != -1:
> > +                    format_name = get_string_struct(line)
> > +                elif line.find(".protocol_name") != -1:
> > +                    protocol_name = get_string_struct(line)
> > +                elif line.find(".bdrv_probe") != -1:
> > +                    probe = True
> > +                elif line.find(".bdrv_probe_device") != -1:
> > +                    probe_device = True
> > +                elif line == "};":
> > +                    add_module(fheader, library, format_name,
> > protocol_name,
> > +                                probe, probe_device)
> > +                    found_start = False
> > +            elif line.find("static BlockDriver") != -1:
> > +                found_something = True
> > +                found_start = True
> > +                format_name = ""
> > +                protocol_name = ""
> > +                probe = False
> > +                probe_device = False
> > +
> > +        if not found_something:
> > +            print("No BlockDriver struct found in " + filename +
> > ". \
> > +                    Is this really a module?", file=sys.stderr)
> > +            sys.exit(1)
> > +
> > +def print_top(fheader):
> > +    fheader.write('''/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
> > +/*
> > + * QEMU Block Module Infrastructure
> > + *
> > + * Copyright Red Hat, Inc. 2015
> > + *
> > + * Authors:
> > + *  Marc Mari       <markmb@redhat.com>
> > + *
> > + * This work is licensed under the terms of the GNU GPL, version
> > 2.  See
> > + * the COPYING file in the top-level directory.
> > + *
> > + */
> > +
> > +''')
> > +
> > +    fheader.write('''#ifndef QEMU_MODULE_BLOCK_H
> > +#define QEMU_MODULE_BLOCK_H
> > +
> > +#include "qemu-common.h"
> > +
> > +static const struct {
> > +    const char *format_name;
> > +    const char *protocol_name;
> > +    const char *library_name;
> > +    bool has_probe;
> > +    bool has_probe_device;
> > +} block_driver_modules[] = {''')
> > +
> > +def print_bottom(fheader):
> > +    fheader.write('''
> > +};
> > +
> > +#endif
> > +''')
> > +
> > +# First argument: output folder
> > +# All other arguments: modules source files (.c)
> > +output_dir = sys.argv[1]
> > +if not os.path.isdir(output_dir):
> > +    print("Folder " + output_dir + " does not exist",
> > file=sys.stderr)
> > +    sys.exit(1)
> > +
> > +path = output_dir + 'module_block.h'
> > +
> > +with open(path, 'w') as fheader:
> > +    print_top(fheader)
> > +
> > +    for filename in sys.argv[2:]:
> > +        if os.path.isfile(filename):
> > +            process_file(fheader, filename)
> > +        else:
> > +            print("File " + filename + " does not exist.",
> > file=sys.stderr)
> > +            sys.exit(1)
> > +
> > +    print_bottom(fheader)
> > +
> > +sys.exit(0)
> 
> If we decide to parse c sources, I really want the parser as simple as
> possible, while keeping the syntax explicit:
> 
> in block_int.h:
> 
>     /* In order to allow dynamical loading, block modules should
> specify which
>      * protocols and formats are supported with one or more of
> following
>      * macros, which are parsed by code generator to create mappings
> from
>      * format or protocol name to module name.
>      */
>     #define BLOCK_PROTOCOL_MODULE(protocol, has_probe)
>     #define BLOCK_FORMAT_MODULE(format, has_probe)
> 
> in curl.c:
>     BLOCK_PROTOCOL_MODULE(http, false)
>     BLOCK_PROTOCOL_MODULE(https, false)
>     BLOCK_PROTOCOL_MODULE(ftp, false)
>     BLOCK_PROTOCOL_MODULE(ftps, false)
>     BLOCK_PROTOCOL_MODULE(tftp, false)
> 
> in dmg.c:
>     BLOCK_FORMAT_MODULE(dmg, true)
> 
> In generator.py, you can grep for "BLOCK_PROTOCOL_MODULE(*)" and
> "BLOCK_FORMAT_MODULE(*)".
> 

Yes, that makes it simpler.

The only drawback I see is that all drivers that want to be converted
into modules will have to be touched, and the information from the
struct duplicated. It is not a big drawback because this will make the
parser really simple.

Thanks
Marc

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v2 0/2] Dynamic module support for block drivers
  2015-09-08 13:53 [Qemu-devel] [PATCH v2 0/2] Dynamic module support for block drivers Marc Marí
  2015-09-08 13:53 ` [Qemu-devel] [PATCH v2 1/2] Add dynamic module loading " Marc Marí
  2015-09-08 13:53 ` [Qemu-devel] [PATCH v2 2/2] Add dynamic generation of module_block.h Marc Marí
@ 2015-09-21 13:27 ` Marc Marí
  2015-09-21 13:43 ` Denis V. Lunev
  3 siblings, 0 replies; 10+ messages in thread
From: Marc Marí @ 2015-09-21 13:27 UTC (permalink / raw)
  To: qemu-devel; +Cc: Fam Zheng, Stefan Hajnoczi, Michael Tokarev, Peter Maydell

Ping!

Anyone has more comments for the next version?

Thanks
Marc

>On Tue,  8 Sep 2015 15:53:20 +0200
>Marc Marí <markmb@redhat.com> wrote:
>
> The current module infrastructure has been improved to enable dynamic
> module loading.
> 
> This reduces the load time for very simple guests. For the following
> configuration (very loaded)
> 
> ./configure --enable-sdl --enable-gtk --enable-vte --enable-curses \
>     --enable-vnc --enable-vnc-{jpeg,tls,sasl,png} --enable-virtfs \
>     --enable-brlapi --enable-curl --enable-fdt --enable-bluez \
>     --enable-kvm --enable-rdma --enable-uuid --enable-vde \
>     --enable-linux-aio --enable-cap-ng --enable-attr
> --enable-vhost-net \ --enable-vhost-scsi --enable-spice --enable-rbd
> --enable-libiscsi \ --enable-smartcard-nss --enable-guest-agent
> --enable-libusb \ --enable-usb-redir --enable-lzo --enable-snappy
> --enable-bzip2 \ --enable-seccomp --enable-coroutine-pool
> --enable-glusterfs \ --enable-tpm --enable-libssh2 --enable-vhdx
> --enable-numa \ --enable-tcmalloc --target-list=x86_64-softmmu
> 
> With modules disabled, there are 142 libraries loaded at startup.
> Time is the following:
>  LD time: 0.065 seconds
>  QEMU time: 0.02 seconds
>  Total time: 0.085 seconds
> 
> With this patch series and modules enabled, there are 128 libraries
> loaded at startup. Time is the following:
>  LD time: 0.02 seconds
>  QEMU time: 0.02 seconds
>  Total time: 0.04 seconds
> 
> Where LD time is the time between the program startup and the jump to
> main, and QEMU time is the time between the start of main and the
> first kvm_entry.
> 
> These results are just with a few block drivers, that were already a
> module. Adding more modules (block or not block) should be easy, and
> will reduce the load time even more.
> 
> Marc Marí (2):
>   Add dynamic module loading for block drivers
>   Add dynamic generation of module_block.h
> 
>  .gitignore                      |   1 +
>  Makefile                        |  10 ++-
>  block.c                         |  70 +++++++++++++++++++++
>  configure                       |   2 +-
>  include/qemu/module.h           |   3 +
>  scripts/modules/module_block.py | 134
> ++++++++++++++++++++++++++++++++++++++++
> util/module.c                   |  38 ++++-------- 7 files changed,
> 227 insertions(+), 31 deletions(-) create mode 100755
> scripts/modules/module_block.py
> 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v2 0/2] Dynamic module support for block drivers
  2015-09-08 13:53 [Qemu-devel] [PATCH v2 0/2] Dynamic module support for block drivers Marc Marí
                   ` (2 preceding siblings ...)
  2015-09-21 13:27 ` [Qemu-devel] [PATCH v2 0/2] Dynamic module support for block drivers Marc Marí
@ 2015-09-21 13:43 ` Denis V. Lunev
  2015-09-21 14:44   ` Marc Marí
  3 siblings, 1 reply; 10+ messages in thread
From: Denis V. Lunev @ 2015-09-21 13:43 UTC (permalink / raw)
  To: Marc Marí, qemu-devel
  Cc: Peter Maydell, Michael Tokarev, Fam Zheng, Stefan Hajnoczi

On 09/08/2015 04:53 PM, Marc Marí wrote:
> The current module infrastructure has been improved to enable dynamic module
> loading.
>
> This reduces the load time for very simple guests. For the following
> configuration (very loaded)
>
> ./configure --enable-sdl --enable-gtk --enable-vte --enable-curses \
>      --enable-vnc --enable-vnc-{jpeg,tls,sasl,png} --enable-virtfs \
>      --enable-brlapi --enable-curl --enable-fdt --enable-bluez \
>      --enable-kvm --enable-rdma --enable-uuid --enable-vde \
>      --enable-linux-aio --enable-cap-ng --enable-attr --enable-vhost-net \
>      --enable-vhost-scsi --enable-spice --enable-rbd --enable-libiscsi \
>      --enable-smartcard-nss --enable-guest-agent --enable-libusb \
>      --enable-usb-redir --enable-lzo --enable-snappy --enable-bzip2 \
>      --enable-seccomp --enable-coroutine-pool --enable-glusterfs \
>      --enable-tpm --enable-libssh2 --enable-vhdx --enable-numa \
>      --enable-tcmalloc --target-list=x86_64-softmmu
>
> With modules disabled, there are 142 libraries loaded at startup. Time is
> the following:
>   LD time: 0.065 seconds
>   QEMU time: 0.02 seconds
>   Total time: 0.085 seconds
>
> With this patch series and modules enabled, there are 128 libraries loaded
> at startup. Time is the following:
>   LD time: 0.02 seconds
>   QEMU time: 0.02 seconds
>   Total time: 0.04 seconds
>
> Where LD time is the time between the program startup and the jump to main,
> and QEMU time is the time between the start of main and the first kvm_entry.
>
> These results are just with a few block drivers, that were already a module.
> Adding more modules (block or not block) should be easy, and will reduce
> the load time even more.
>
> Marc Marí (2):
>    Add dynamic module loading for block drivers
>    Add dynamic generation of module_block.h
>
>   .gitignore                      |   1 +
>   Makefile                        |  10 ++-
>   block.c                         |  70 +++++++++++++++++++++
>   configure                       |   2 +-
>   include/qemu/module.h           |   3 +
>   scripts/modules/module_block.py | 134 ++++++++++++++++++++++++++++++++++++++++
>   util/module.c                   |  38 ++++--------
>   7 files changed, 227 insertions(+), 31 deletions(-)
>   create mode 100755 scripts/modules/module_block.py
>

 From my point of view the design looks a bit complex.
The approach should be quite similar to one used
in Linux kernel.

If the block driver is configured as a module, block_init
should register proper hooks and create generic lists.
C code parsing does not look like a good approach
to me.

If block_init is a bad name, we could use something like
module_init.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v2 1/2] Add dynamic module loading for block drivers
  2015-09-08 13:53 ` [Qemu-devel] [PATCH v2 1/2] Add dynamic module loading " Marc Marí
@ 2015-09-21 13:49   ` Denis V. Lunev
  0 siblings, 0 replies; 10+ messages in thread
From: Denis V. Lunev @ 2015-09-21 13:49 UTC (permalink / raw)
  To: Marc Marí, qemu-devel
  Cc: Peter Maydell, Michael Tokarev, Fam Zheng, Stefan Hajnoczi

On 09/08/2015 04:53 PM, Marc Marí wrote:
> Extend the current module interface to allow for block drivers to be loaded
> dynamically on request.
>
> The only block drivers that can be converted into modules are the drivers
> that don't perform any init operation except for registering themselves. This
> is why libiscsi has been disabled as a module.
>
> All the necessary module information is located in a new structure found in
> include/qemu/module_block.h
>
> Signed-off-by: Marc Marí <markmb@redhat.com>
> ---
>   block.c                     | 70 +++++++++++++++++++++++++++++++++++
>   configure                   |  2 +-
>   include/qemu/module.h       |  3 ++
>   include/qemu/module_block.h | 90 +++++++++++++++++++++++++++++++++++++++++++++
>   util/module.c               | 38 ++++++-------------
>   5 files changed, 175 insertions(+), 28 deletions(-)
>   create mode 100644 include/qemu/module_block.h
>
> diff --git a/block.c b/block.c
> index 090923c..814429a 100644
> --- a/block.c
> +++ b/block.c
> @@ -27,6 +27,7 @@
>   #include "block/block_int.h"
>   #include "block/blockjob.h"
>   #include "qemu/error-report.h"
> +#include "qemu/module_block.h"
>   #include "qemu/module.h"
>   #include "qapi/qmp/qerror.h"
>   #include "qapi/qmp/qjson.h"
> @@ -277,11 +278,30 @@ void bdrv_add_close_notifier(BlockDriverState *bs, Notifier *notify)
>   BlockDriver *bdrv_find_format(const char *format_name)
>   {
>       BlockDriver *drv1;
> +    size_t i;
> +
>       QLIST_FOREACH(drv1, &bdrv_drivers, list) {
>           if (!strcmp(drv1->format_name, format_name)) {
>               return drv1;
>           }
>       }
> +
> +    for (i = 0; i < ARRAY_SIZE(block_driver_modules); ++i) {
> +        if (!strcmp(block_driver_modules[i].format_name, format_name)) {
> +            block_module_load_one(block_driver_modules[i].library_name);
> +            /* Copying code is not nice, but this way the current discovery is
> +             * not modified. Calling recursively could fail if the library
> +             * has been deleted.
> +             */
> +            QLIST_FOREACH(drv1, &bdrv_drivers, list) {
> +                if (!strcmp(drv1->format_name, format_name)) {
> +                    return drv1;
> +                }
> +            }
> +        }
> +    }
> +
> +
>       return NULL;
>   }
>   
> @@ -484,8 +504,15 @@ int get_tmp_filename(char *filename, int size)
>   static BlockDriver *find_hdev_driver(const char *filename)
>   {
>       int score_max = 0, score;
> +    size_t i;
>       BlockDriver *drv = NULL, *d;
>   
> +    for (i = 0; i < ARRAY_SIZE(block_driver_modules); ++i) {
> +        if (block_driver_modules[i].has_probe_device) {
> +            block_module_load_one(block_driver_modules[i].library_name);
> +        }
> +    }
> +
>       QLIST_FOREACH(d, &bdrv_drivers, list) {
>           if (d->bdrv_probe_device) {
>               score = d->bdrv_probe_device(filename);
> @@ -507,6 +534,7 @@ BlockDriver *bdrv_find_protocol(const char *filename,
>       char protocol[128];
>       int len;
>       const char *p;
> +    size_t i;
>   
>       /* TODO Drivers without bdrv_file_open must be specified explicitly */
>   
> @@ -533,6 +561,7 @@ BlockDriver *bdrv_find_protocol(const char *filename,
>           len = sizeof(protocol) - 1;
>       memcpy(protocol, filename, len);
>       protocol[len] = '\0';
> +
>       QLIST_FOREACH(drv1, &bdrv_drivers, list) {
>           if (drv1->protocol_name &&
>               !strcmp(drv1->protocol_name, protocol)) {
> @@ -540,6 +569,23 @@ BlockDriver *bdrv_find_protocol(const char *filename,
>           }
>       }
>   
> +    for (i = 0; i < ARRAY_SIZE(block_driver_modules); ++i) {
> +        if (block_driver_modules[i].protocol_name &&
> +            !strcmp(block_driver_modules[i].protocol_name, protocol)) {
> +            block_module_load_one(block_driver_modules[i].library_name);
> +            /* Copying code is not nice, but this way the current discovery is
> +             * not modified. Calling recursively could fail if the library
> +             * has been deleted.
> +             */
> +            QLIST_FOREACH(drv1, &bdrv_drivers, list) {
> +                if (drv1->protocol_name &&
> +                    !strcmp(drv1->protocol_name, protocol)) {
> +                    return drv1;
> +                }
> +            }
> +        }
> +    }
> +
>       error_setg(errp, "Unknown protocol '%s'", protocol);
>       return NULL;
>   }
> @@ -562,8 +608,15 @@ BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size,
>                               const char *filename)
>   {
>       int score_max = 0, score;
> +    size_t i;
>       BlockDriver *drv = NULL, *d;
>   
> +    for (i = 0; i < ARRAY_SIZE(block_driver_modules); ++i) {
> +        if (block_driver_modules[i].has_probe) {
> +            block_module_load_one(block_driver_modules[i].library_name);
> +        }
> +    }
> +
>       QLIST_FOREACH(d, &bdrv_drivers, list) {
>           if (d->bdrv_probe) {
>               score = d->bdrv_probe(buf, buf_size, filename);
> @@ -2784,6 +2837,7 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
>       BlockDriver *drv;
>       int count = 0;
>       int i;
> +    size_t n;
>       const char **formats = NULL;
>   
>       QLIST_FOREACH(drv, &bdrv_drivers, list) {
> @@ -2801,6 +2855,22 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
>           }
>       }
>   
> +    for (n = 0; n < ARRAY_SIZE(block_driver_modules); ++n) {
> +        if (block_driver_modules[n].format_name) {
> +            bool found = false;
> +            int i = count;
> +            while (formats && i && !found) {
> +                found = !strcmp(formats[--i],
> +                                block_driver_modules[n].format_name);
> +            }
> +
> +            if (!found) {
> +                formats = g_renew(const char *, formats, count + 1);
> +                formats[count++] = block_driver_modules[n].format_name;
> +            }
> +        }
> +    }
> +
>       qsort(formats, count, sizeof(formats[0]), qsort_strcmp);
>   
>       for (i = 0; i < count; i++) {
> diff --git a/configure b/configure
> index d854936..72ac564 100755
> --- a/configure
> +++ b/configure
> @@ -4984,7 +4984,7 @@ if test "$bzip2" = "yes" ; then
>   fi
>   
>   if test "$libiscsi" = "yes" ; then
> -  echo "CONFIG_LIBISCSI=m" >> $config_host_mak
> +  echo "CONFIG_LIBISCSI=y" >> $config_host_mak
>     echo "LIBISCSI_CFLAGS=$libiscsi_cflags" >> $config_host_mak
>     echo "LIBISCSI_LIBS=$libiscsi_libs" >> $config_host_mak
>   fi
> diff --git a/include/qemu/module.h b/include/qemu/module.h
> index 72d9498..0ad4bb7 100644
> --- a/include/qemu/module.h
> +++ b/include/qemu/module.h
> @@ -53,9 +53,12 @@ typedef enum {
>   #define qapi_init(function) module_init(function, MODULE_INIT_QAPI)
>   #define type_init(function) module_init(function, MODULE_INIT_QOM)
>   
> +#define block_module_load_one(lib) module_load_one("block-", lib);
> +
>   void register_module_init(void (*fn)(void), module_init_type type);
>   void register_dso_module_init(void (*fn)(void), module_init_type type);
>   
>   void module_call_init(module_init_type type);
> +void module_load_one(const char *prefix, const char *lib_name);
>   
>   #endif
> diff --git a/include/qemu/module_block.h b/include/qemu/module_block.h
> new file mode 100644
> index 0000000..d725db8
> --- /dev/null
> +++ b/include/qemu/module_block.h
> @@ -0,0 +1,90 @@
> +/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
> +/*
> + * QEMU Block Module Infrastructure
> + *
> + * Copyright Red Hat, Inc. 2015
> + *
> + * Authors:
> + *  Marc Mari       <markmb@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2.  See
> + * the COPYING file in the top-level directory.
> + *
> + */
> +
> +#ifndef QEMU_MODULE_BLOCK_H
> +#define QEMU_MODULE_BLOCK_H
> +
> +#include "qemu-common.h"
> +
> +static const struct {
> +    const char *format_name;
> +    const char *protocol_name;
> +    const char *library_name;
> +    bool has_probe;
> +    bool has_probe_device;
> +} block_driver_modules[] = {
> +	{
> +	.library_name = "curl",
> +	.format_name = "http",
> +	.protocol_name = "http",
> +	},
> +	{
> +	.library_name = "curl",
> +	.format_name = "https",
> +	.protocol_name = "https",
> +	},
> +	{
> +	.library_name = "curl",
> +	.format_name = "ftp",
> +	.protocol_name = "ftp",
> +	},
> +	{
> +	.library_name = "curl",
> +	.format_name = "ftps",
> +	.protocol_name = "ftps",
> +	},
> +	{
> +	.library_name = "curl",
> +	.format_name = "tftp",
> +	.protocol_name = "tftp",
> +	},
> +	{
> +	.library_name = "rbd",
> +	.format_name = "rbd",
> +	.protocol_name = "rbd",
> +	},
> +	{
> +	.library_name = "gluster",
> +	.format_name = "gluster",
> +	.protocol_name = "gluster",
> +	},
> +	{
> +	.library_name = "gluster",
> +	.format_name = "gluster",
> +	.protocol_name = "gluster+tcp",
> +	},
> +	{
> +	.library_name = "gluster",
> +	.format_name = "gluster",
> +	.protocol_name = "gluster+unix",
> +	},
> +	{
> +	.library_name = "gluster",
> +	.format_name = "gluster",
> +	.protocol_name = "gluster+rdma",
> +	},
> +	{
> +	.library_name = "ssh",
> +	.format_name = "ssh",
> +	.protocol_name = "ssh",
> +	},
> +	{
> +	.library_name = "dmg",
> +	.format_name = "dmg",
> +	.has_probe = true,
> +	},
> +};
> +
> +#endif
> +
> diff --git a/util/module.c b/util/module.c
> index 4bd4a94..992d317 100644
> --- a/util/module.c
> +++ b/util/module.c
> @@ -91,14 +91,11 @@ void register_dso_module_init(void (*fn)(void), module_init_type type)
>       QTAILQ_INSERT_TAIL(&dso_init_list, e, node);
>   }
>   
> -static void module_load(module_init_type type);
> -
>   void module_call_init(module_init_type type)
>   {
>       ModuleTypeList *l;
>       ModuleEntry *e;
>   
> -    module_load(type);
>       l = find_type(type);
>   
>       QTAILQ_FOREACH(e, l, node) {
> @@ -149,6 +146,7 @@ static int module_load_file(const char *fname)
>           ret = -EINVAL;
>       } else {
>           QTAILQ_FOREACH(e, &dso_init_list, node) {
> +            e->init();
>               register_module_init(e->init, e->type);
>           }
>           ret = 0;
> @@ -163,14 +161,10 @@ out:
>   }
>   #endif
>   
> -static void module_load(module_init_type type)
> +void module_load_one(const char *prefix, const char *lib_name)
>   {
>   #ifdef CONFIG_MODULES
>       char *fname = NULL;
> -    const char **mp;
> -    static const char *block_modules[] = {
> -        CONFIG_BLOCK_MODULES
> -    };
>       char *exec_dir;
>       char *dirs[3];
>       int i = 0;
> @@ -181,15 +175,6 @@ static void module_load(module_init_type type)
>           return;
>       }
>   
> -    switch (type) {
> -    case MODULE_INIT_BLOCK:
> -        mp = block_modules;
> -        break;
> -    default:
> -        /* no other types have dynamic modules for now*/
> -        return;
> -    }
> -
>       exec_dir = qemu_get_exec_dir();
>       dirs[i++] = g_strdup_printf("%s", CONFIG_QEMU_MODDIR);
>       dirs[i++] = g_strdup_printf("%s/..", exec_dir ? : "");
> @@ -198,16 +183,15 @@ static void module_load(module_init_type type)
>       g_free(exec_dir);
>       exec_dir = NULL;
>   
> -    for ( ; *mp; mp++) {
> -        for (i = 0; i < ARRAY_SIZE(dirs); i++) {
> -            fname = g_strdup_printf("%s/%s%s", dirs[i], *mp, HOST_DSOSUF);
> -            ret = module_load_file(fname);
> -            g_free(fname);
> -            fname = NULL;
> -            /* Try loading until loaded a module file */
> -            if (!ret) {
> -                break;
> -            }
> +    for (i = 0; i < ARRAY_SIZE(dirs); i++) {
> +        fname = g_strdup_printf("%s/%s%s%s",
> +                dirs[i], prefix, lib_name, HOST_DSOSUF);
> +        ret = module_load_file(fname);
> +        g_free(fname);
> +        fname = NULL;
> +        /* Try loading until loaded a module file */
> +        if (!ret) {
> +            break;
>           }
>       }
>   
I think that the API will be more convenient if block_module_load_one
will return just loaded driver. Second loop will not be needed in this
case.

Den

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v2 0/2] Dynamic module support for block drivers
  2015-09-21 13:43 ` Denis V. Lunev
@ 2015-09-21 14:44   ` Marc Marí
  2015-09-21 14:58     ` Denis V. Lunev
  0 siblings, 1 reply; 10+ messages in thread
From: Marc Marí @ 2015-09-21 14:44 UTC (permalink / raw)
  To: Denis V. Lunev
  Cc: Fam Zheng, Peter Maydell, Michael Tokarev, qemu-devel,
	Stefan Hajnoczi

On Mon, 21 Sep 2015 16:43:27 +0300
"Denis V. Lunev" <den-lists@parallels.com> wrote:

> On 09/08/2015 04:53 PM, Marc Marí wrote:
> > The current module infrastructure has been improved to enable
> > dynamic module loading.
> >
> > This reduces the load time for very simple guests. For the following
> > configuration (very loaded)
> >
> > ./configure --enable-sdl --enable-gtk --enable-vte --enable-curses \
> >      --enable-vnc --enable-vnc-{jpeg,tls,sasl,png} --enable-virtfs \
> >      --enable-brlapi --enable-curl --enable-fdt --enable-bluez \
> >      --enable-kvm --enable-rdma --enable-uuid --enable-vde \
> >      --enable-linux-aio --enable-cap-ng --enable-attr
> > --enable-vhost-net \ --enable-vhost-scsi --enable-spice
> > --enable-rbd --enable-libiscsi \ --enable-smartcard-nss
> > --enable-guest-agent --enable-libusb \ --enable-usb-redir
> > --enable-lzo --enable-snappy --enable-bzip2 \ --enable-seccomp
> > --enable-coroutine-pool --enable-glusterfs \ --enable-tpm
> > --enable-libssh2 --enable-vhdx --enable-numa \ --enable-tcmalloc
> > --target-list=x86_64-softmmu
> >
> > With modules disabled, there are 142 libraries loaded at startup.
> > Time is the following:
> >   LD time: 0.065 seconds
> >   QEMU time: 0.02 seconds
> >   Total time: 0.085 seconds
> >
> > With this patch series and modules enabled, there are 128 libraries
> > loaded at startup. Time is the following:
> >   LD time: 0.02 seconds
> >   QEMU time: 0.02 seconds
> >   Total time: 0.04 seconds
> >
> > Where LD time is the time between the program startup and the jump
> > to main, and QEMU time is the time between the start of main and
> > the first kvm_entry.
> >
> > These results are just with a few block drivers, that were already
> > a module. Adding more modules (block or not block) should be easy,
> > and will reduce the load time even more.
> >
> > Marc Marí (2):
> >    Add dynamic module loading for block drivers
> >    Add dynamic generation of module_block.h
> >
> >   .gitignore                      |   1 +
> >   Makefile                        |  10 ++-
> >   block.c                         |  70 +++++++++++++++++++++
> >   configure                       |   2 +-
> >   include/qemu/module.h           |   3 +
> >   scripts/modules/module_block.py | 134
> > ++++++++++++++++++++++++++++++++++++++++
> > util/module.c                   |  38 ++++-------- 7 files changed,
> > 227 insertions(+), 31 deletions(-) create mode 100755
> > scripts/modules/module_block.py
> >
> 
>  From my point of view the design looks a bit complex.
> The approach should be quite similar to one used
> in Linux kernel.
> 
> If the block driver is configured as a module, block_init
> should register proper hooks and create generic lists.
> C code parsing does not look like a good approach
> to me.
> 
> If block_init is a bad name, we could use something like
> module_init.
> 

I think applying the kind of modules of the Linux kernel here would
mean reworking an important part of the drivers that want to be
converted into modules.

If that's the case, I think it's better to have a less good solution
that is still good enough, and doesn't mean reworking half of the QEMU
codebase. Correct me if I'm wrong.

But of course, this is not the only valid approach.

Thanks
Marc

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v2 0/2] Dynamic module support for block drivers
  2015-09-21 14:44   ` Marc Marí
@ 2015-09-21 14:58     ` Denis V. Lunev
  0 siblings, 0 replies; 10+ messages in thread
From: Denis V. Lunev @ 2015-09-21 14:58 UTC (permalink / raw)
  To: Marc Marí
  Cc: Fam Zheng, Peter Maydell, Michael Tokarev, qemu-devel,
	Stefan Hajnoczi

On 09/21/2015 05:44 PM, Marc Marí wrote:
> On Mon, 21 Sep 2015 16:43:27 +0300
> "Denis V. Lunev" <den-lists@parallels.com> wrote:
>
>> On 09/08/2015 04:53 PM, Marc Marí wrote:
>>> The current module infrastructure has been improved to enable
>>> dynamic module loading.
>>>
>>> This reduces the load time for very simple guests. For the following
>>> configuration (very loaded)
>>>
>>> ./configure --enable-sdl --enable-gtk --enable-vte --enable-curses \
>>>       --enable-vnc --enable-vnc-{jpeg,tls,sasl,png} --enable-virtfs \
>>>       --enable-brlapi --enable-curl --enable-fdt --enable-bluez \
>>>       --enable-kvm --enable-rdma --enable-uuid --enable-vde \
>>>       --enable-linux-aio --enable-cap-ng --enable-attr
>>> --enable-vhost-net \ --enable-vhost-scsi --enable-spice
>>> --enable-rbd --enable-libiscsi \ --enable-smartcard-nss
>>> --enable-guest-agent --enable-libusb \ --enable-usb-redir
>>> --enable-lzo --enable-snappy --enable-bzip2 \ --enable-seccomp
>>> --enable-coroutine-pool --enable-glusterfs \ --enable-tpm
>>> --enable-libssh2 --enable-vhdx --enable-numa \ --enable-tcmalloc
>>> --target-list=x86_64-softmmu
>>>
>>> With modules disabled, there are 142 libraries loaded at startup.
>>> Time is the following:
>>>    LD time: 0.065 seconds
>>>    QEMU time: 0.02 seconds
>>>    Total time: 0.085 seconds
>>>
>>> With this patch series and modules enabled, there are 128 libraries
>>> loaded at startup. Time is the following:
>>>    LD time: 0.02 seconds
>>>    QEMU time: 0.02 seconds
>>>    Total time: 0.04 seconds
>>>
>>> Where LD time is the time between the program startup and the jump
>>> to main, and QEMU time is the time between the start of main and
>>> the first kvm_entry.
>>>
>>> These results are just with a few block drivers, that were already
>>> a module. Adding more modules (block or not block) should be easy,
>>> and will reduce the load time even more.
>>>
>>> Marc Marí (2):
>>>     Add dynamic module loading for block drivers
>>>     Add dynamic generation of module_block.h
>>>
>>>    .gitignore                      |   1 +
>>>    Makefile                        |  10 ++-
>>>    block.c                         |  70 +++++++++++++++++++++
>>>    configure                       |   2 +-
>>>    include/qemu/module.h           |   3 +
>>>    scripts/modules/module_block.py | 134
>>> ++++++++++++++++++++++++++++++++++++++++
>>> util/module.c                   |  38 ++++-------- 7 files changed,
>>> 227 insertions(+), 31 deletions(-) create mode 100755
>>> scripts/modules/module_block.py
>>>
>>   From my point of view the design looks a bit complex.
>> The approach should be quite similar to one used
>> in Linux kernel.
>>
>> If the block driver is configured as a module, block_init
>> should register proper hooks and create generic lists.
>> C code parsing does not look like a good approach
>> to me.
>>
>> If block_init is a bad name, we could use something like
>> module_init.
>>
> I think applying the kind of modules of the Linux kernel here would
> mean reworking an important part of the drivers that want to be
> converted into modules.
>
> If that's the case, I think it's better to have a less good solution
> that is still good enough, and doesn't mean reworking half of the QEMU
> codebase. Correct me if I'm wrong.
>
> But of course, this is not the only valid approach.
>
> Thanks
> Marc
Frankly speaking this does not look to me like a very big deal
and a huge rework. Build system should create 2 object files
for a modularized driver:
- one with driver stub which has required properties aka has_probe, name etc
   and a bit meaning that the driver is not present
- one with driver code
and from my point of view changes will not be big with this
approach.

Anyway, this is my opinion about this stuff. That is all, I not
against your change, I would like to note that there is a better
way which will allow further improvements.

Den

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2015-09-21 14:58 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-09-08 13:53 [Qemu-devel] [PATCH v2 0/2] Dynamic module support for block drivers Marc Marí
2015-09-08 13:53 ` [Qemu-devel] [PATCH v2 1/2] Add dynamic module loading " Marc Marí
2015-09-21 13:49   ` Denis V. Lunev
2015-09-08 13:53 ` [Qemu-devel] [PATCH v2 2/2] Add dynamic generation of module_block.h Marc Marí
2015-09-09  2:27   ` Fam Zheng
2015-09-09  7:37     ` Marc Marí
2015-09-21 13:27 ` [Qemu-devel] [PATCH v2 0/2] Dynamic module support for block drivers Marc Marí
2015-09-21 13:43 ` Denis V. Lunev
2015-09-21 14:44   ` Marc Marí
2015-09-21 14:58     ` Denis V. Lunev

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).