blockdev: Add dynamic module loading for block drivers

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.

In addition, only the protocol drivers are being modularized, as they
are the only ones which see significant performance benefits. The format
drivers do not generally link to external libraries, so modularizing
them is of no benefit from a performance perspective.

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

This spoils the purpose of 5505e8b76f (block/dmg: make it modular).

Before this patch, if module build is enabled, block-dmg.so is linked to
libbz2, whereas the main binary is not. In downstream, theoretically, it
means only the qemu-block-extra package depends on libbz2, while the
main QEMU package needn't to. With this patch, we (temporarily) change
the case so that the main QEMU depends on libbz2 again.

Signed-off-by: Marc Marí <markmb@redhat.com>
Signed-off-by: Colin Lord <clord@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-id: 1471008424-16465-4-git-send-email-clord@redhat.com
Reviewed-by: Max Reitz <mreitz@redhat.com>
[mreitz: Do a signed comparison against the length of
 block_driver_modules[], so it will not cause a compile error when
 empty]
Signed-off-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
Marc Mari 2016-08-12 09:27:03 -04:00 committed by Max Reitz
parent 0c0c1fd973
commit 88d88798b7
5 changed files with 70 additions and 39 deletions

View File

@ -247,9 +247,6 @@ Makefile: $(version-obj-y) $(version-lobj-y)
libqemustub.a: $(stub-obj-y) libqemustub.a: $(stub-obj-y)
libqemuutil.a: $(util-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 qemu-img.o: qemu-img-cmds.h

62
block.c
View File

@ -27,6 +27,7 @@
#include "block/blockjob.h" #include "block/blockjob.h"
#include "block/nbd.h" #include "block/nbd.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "module_block.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qapi/qmp/qbool.h" #include "qapi/qmp/qbool.h"
@ -242,17 +243,40 @@ BlockDriverState *bdrv_new(void)
return bs; return bs;
} }
BlockDriver *bdrv_find_format(const char *format_name) static BlockDriver *bdrv_do_find_format(const char *format_name)
{ {
BlockDriver *drv1; BlockDriver *drv1;
QLIST_FOREACH(drv1, &bdrv_drivers, list) { QLIST_FOREACH(drv1, &bdrv_drivers, list) {
if (!strcmp(drv1->format_name, format_name)) { if (!strcmp(drv1->format_name, format_name)) {
return drv1; return drv1;
} }
} }
return NULL; return NULL;
} }
BlockDriver *bdrv_find_format(const char *format_name)
{
BlockDriver *drv1;
int i;
drv1 = bdrv_do_find_format(format_name);
if (drv1) {
return drv1;
}
/* The driver isn't registered, maybe we need to load a module */
for (i = 0; i < (int)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);
break;
}
}
return bdrv_do_find_format(format_name);
}
static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only) static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
{ {
static const char *whitelist_rw[] = { static const char *whitelist_rw[] = {
@ -461,6 +485,19 @@ static BlockDriver *find_hdev_driver(const char *filename)
return drv; return drv;
} }
static BlockDriver *bdrv_do_find_protocol(const char *protocol)
{
BlockDriver *drv1;
QLIST_FOREACH(drv1, &bdrv_drivers, list) {
if (drv1->protocol_name && !strcmp(drv1->protocol_name, protocol)) {
return drv1;
}
}
return NULL;
}
BlockDriver *bdrv_find_protocol(const char *filename, BlockDriver *bdrv_find_protocol(const char *filename,
bool allow_protocol_prefix, bool allow_protocol_prefix,
Error **errp) Error **errp)
@ -469,6 +506,7 @@ BlockDriver *bdrv_find_protocol(const char *filename,
char protocol[128]; char protocol[128];
int len; int len;
const char *p; const char *p;
int i;
/* TODO Drivers without bdrv_file_open must be specified explicitly */ /* TODO Drivers without bdrv_file_open must be specified explicitly */
@ -495,15 +533,25 @@ BlockDriver *bdrv_find_protocol(const char *filename,
len = sizeof(protocol) - 1; len = sizeof(protocol) - 1;
memcpy(protocol, filename, len); memcpy(protocol, filename, len);
protocol[len] = '\0'; protocol[len] = '\0';
QLIST_FOREACH(drv1, &bdrv_drivers, list) {
if (drv1->protocol_name && drv1 = bdrv_do_find_protocol(protocol);
!strcmp(drv1->protocol_name, protocol)) { if (drv1) {
return drv1; return drv1;
}
for (i = 0; i < (int)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);
break;
} }
} }
error_setg(errp, "Unknown protocol '%s'", protocol); drv1 = bdrv_do_find_protocol(protocol);
return NULL; if (!drv1) {
error_setg(errp, "Unknown protocol '%s'", protocol);
}
return drv1;
} }
/* /*

View File

@ -1,4 +1,4 @@
block-obj-y += raw_bsd.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o block-obj-y += raw_bsd.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o dmg.o
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
block-obj-y += qed-check.o block-obj-y += qed-check.o
@ -40,7 +40,6 @@ gluster.o-libs := $(GLUSTERFS_LIBS)
ssh.o-cflags := $(LIBSSH2_CFLAGS) ssh.o-cflags := $(LIBSSH2_CFLAGS)
ssh.o-libs := $(LIBSSH2_LIBS) ssh.o-libs := $(LIBSSH2_LIBS)
archipelago.o-libs := $(ARCHIPELAGO_LIBS) archipelago.o-libs := $(ARCHIPELAGO_LIBS)
block-obj-m += dmg.o
dmg.o-libs := $(BZIP2_LIBS) dmg.o-libs := $(BZIP2_LIBS)
qcow.o-libs := -lz qcow.o-libs := -lz
linux-aio.o-libs := -laio linux-aio.o-libs := -laio

View File

@ -52,9 +52,12 @@ typedef enum {
#define qapi_init(function) module_init(function, MODULE_INIT_QAPI) #define qapi_init(function) module_init(function, MODULE_INIT_QAPI)
#define type_init(function) module_init(function, MODULE_INIT_QOM) #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_module_init(void (*fn)(void), module_init_type type);
void register_dso_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_call_init(module_init_type type);
void module_load_one(const char *prefix, const char *lib_name);
#endif #endif

View File

@ -87,14 +87,11 @@ void register_dso_module_init(void (*fn)(void), module_init_type type)
QTAILQ_INSERT_TAIL(&dso_init_list, e, node); QTAILQ_INSERT_TAIL(&dso_init_list, e, node);
} }
static void module_load(module_init_type type);
void module_call_init(module_init_type type) void module_call_init(module_init_type type)
{ {
ModuleTypeList *l; ModuleTypeList *l;
ModuleEntry *e; ModuleEntry *e;
module_load(type);
l = find_type(type); l = find_type(type);
QTAILQ_FOREACH(e, l, node) { QTAILQ_FOREACH(e, l, node) {
@ -145,6 +142,7 @@ static int module_load_file(const char *fname)
ret = -EINVAL; ret = -EINVAL;
} else { } else {
QTAILQ_FOREACH(e, &dso_init_list, node) { QTAILQ_FOREACH(e, &dso_init_list, node) {
e->init();
register_module_init(e->init, e->type); register_module_init(e->init, e->type);
} }
ret = 0; ret = 0;
@ -159,14 +157,10 @@ out:
} }
#endif #endif
static void module_load(module_init_type type) void module_load_one(const char *prefix, const char *lib_name)
{ {
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
char *fname = NULL; char *fname = NULL;
const char **mp;
static const char *block_modules[] = {
CONFIG_BLOCK_MODULES
};
char *exec_dir; char *exec_dir;
char *dirs[3]; char *dirs[3];
int i = 0; int i = 0;
@ -177,15 +171,6 @@ static void module_load(module_init_type type)
return; 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(); exec_dir = qemu_get_exec_dir();
dirs[i++] = g_strdup_printf("%s", CONFIG_QEMU_MODDIR); dirs[i++] = g_strdup_printf("%s", CONFIG_QEMU_MODDIR);
dirs[i++] = g_strdup_printf("%s/..", exec_dir ? : ""); dirs[i++] = g_strdup_printf("%s/..", exec_dir ? : "");
@ -194,16 +179,15 @@ static void module_load(module_init_type type)
g_free(exec_dir); g_free(exec_dir);
exec_dir = NULL; exec_dir = NULL;
for ( ; *mp; mp++) { for (i = 0; i < ARRAY_SIZE(dirs); i++) {
for (i = 0; i < ARRAY_SIZE(dirs); i++) { fname = g_strdup_printf("%s/%s%s%s",
fname = g_strdup_printf("%s/%s%s", dirs[i], *mp, HOST_DSOSUF); dirs[i], prefix, lib_name, HOST_DSOSUF);
ret = module_load_file(fname); ret = module_load_file(fname);
g_free(fname); g_free(fname);
fname = NULL; fname = NULL;
/* Try loading until loaded a module file */ /* Try loading until loaded a module file */
if (!ret) { if (!ret) {
break; break;
}
} }
} }