Skip to content

Commit

Permalink
module: add Error arguments to module_load and module_load_qom
Browse files Browse the repository at this point in the history
improve error handling during module load, by changing:

bool module_load(const char *prefix, const char *lib_name);
void module_load_qom(const char *type);

to:

int module_load(const char *prefix, const char *name, Error **errp);
int module_load_qom(const char *type, Error **errp);

where the return value is:

 -1 on module load error, and errp is set with the error
  0 on module or one of its dependencies are not installed
  1 on module load success
  2 on module load success (module already loaded or built-in)

module_load_qom_one has been introduced in:

commit 2845774 ("module: qom module support"), which built on top of
module_load_one, but discarded the bool return value. Restore it.

Adapt all callers to emit errors, or ignore them, or fail hard,
as appropriate in each context.

Replace the previous emission of errors via fprintf in _some_ error
conditions with Error and error_report, so as to emit to the appropriate
target.

A memory leak is also fixed as part of the module_load changes.

audio: when attempting to load an audio module, report module load errors.
Note that still for some callers, a single issue may generate multiple
error reports, and this could be improved further.
Regarding the audio code itself, audio_add() seems to ignore errors,
and this should probably be improved.

block: when attempting to load a block module, report module load errors.
For the code paths that already use the Error API, take advantage of those
to report module load errors into the Error parameter.
For the other code paths, we currently emit the error, but this could be
improved further by adding Error parameters to all possible code paths.

console: when attempting to load a display module, report module load errors.

qdev: when creating a new qdev Device object (DeviceState), report load errors.
      If a module cannot be loaded to create that device, now abort execution
      (if no CONFIG_MODULE) or exit (if CONFIG_MODULE).

qom/object.c: when initializing a QOM object, or looking up class_by_name,
              report module load errors.

qtest: when processing the "module_load" qtest command, report errors
       in the load of the module.

Signed-off-by: Claudio Fontana <cfontana@suse.de>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-Id: <20220929093035.4231-4-cfontana@suse.de>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
  • Loading branch information
Claudio Fontana authored and bonzini committed Nov 6, 2022
1 parent dbc0e80 commit c551fb0
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 113 deletions.
16 changes: 10 additions & 6 deletions audio/audio.c
Expand Up @@ -73,20 +73,24 @@ void audio_driver_register(audio_driver *drv)
audio_driver *audio_driver_lookup(const char *name)
{
struct audio_driver *d;
Error *local_err = NULL;
int rv;

QLIST_FOREACH(d, &audio_drivers, next) {
if (strcmp(name, d->name) == 0) {
return d;
}
}

audio_module_load(name);
QLIST_FOREACH(d, &audio_drivers, next) {
if (strcmp(name, d->name) == 0) {
return d;
rv = audio_module_load(name, &local_err);
if (rv > 0) {
QLIST_FOREACH(d, &audio_drivers, next) {
if (strcmp(name, d->name) == 0) {
return d;
}
}
} else if (rv < 0) {
error_report_err(local_err);
}

return NULL;
}

Expand Down
20 changes: 15 additions & 5 deletions block.c
Expand Up @@ -464,12 +464,18 @@ BlockDriver *bdrv_find_format(const char *format_name)
/* 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(block_driver_modules[i].library_name);
Error *local_err = NULL;
int rv = block_module_load(block_driver_modules[i].library_name,
&local_err);
if (rv > 0) {
return bdrv_do_find_format(format_name);
} else if (rv < 0) {
error_report_err(local_err);
}
break;
}
}

return bdrv_do_find_format(format_name);
return NULL;
}

static int bdrv_format_is_whitelisted(const char *format_name, bool read_only)
Expand Down Expand Up @@ -981,12 +987,16 @@ BlockDriver *bdrv_find_protocol(const char *filename,
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(block_driver_modules[i].library_name);
int rv = block_module_load(block_driver_modules[i].library_name, errp);
if (rv > 0) {
drv1 = bdrv_do_find_protocol(protocol);
} else if (rv < 0) {
return NULL;
}
break;
}
}

drv1 = bdrv_do_find_protocol(protocol);
if (!drv1) {
error_setg(errp, "Unknown protocol '%s'", protocol);
}
Expand Down
14 changes: 11 additions & 3 deletions block/dmg.c
Expand Up @@ -444,9 +444,17 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
if (ret < 0) {
return ret;
}

block_module_load("dmg-bz2");
block_module_load("dmg-lzfse");
/*
* NB: if uncompress submodules are absent,
* ie block_module_load return value == 0, the function pointers
* dmg_uncompress_bz2 and dmg_uncompress_lzfse will be NULL.
*/
if (block_module_load("dmg-bz2", errp) < 0) {
return -EINVAL;
}
if (block_module_load("dmg-lzfse", errp) < 0) {
return -EINVAL;
}

s->n_chunks = 0;
s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;
Expand Down
17 changes: 15 additions & 2 deletions hw/core/qdev.c
Expand Up @@ -147,8 +147,21 @@ bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp)

DeviceState *qdev_new(const char *name)
{
if (!object_class_by_name(name)) {
module_load_qom(name);
ObjectClass *oc = object_class_by_name(name);
#ifdef CONFIG_MODULES
if (!oc) {
int rv = module_load_qom(name, &error_fatal);
if (rv > 0) {
oc = object_class_by_name(name);
} else {
error_report("could not find a module for type '%s'", name);
exit(1);
}
}
#endif
if (!oc) {
error_report("unknown type '%s'", name);
abort();
}
return DEVICE(object_new(name));
}
Expand Down
37 changes: 32 additions & 5 deletions include/qemu/module.h
Expand Up @@ -61,16 +61,43 @@ typedef enum {
#define fuzz_target_init(function) module_init(function, \
MODULE_INIT_FUZZ_TARGET)
#define migration_init(function) module_init(function, MODULE_INIT_MIGRATION)
#define block_module_load(lib) module_load("block-", lib)
#define ui_module_load(lib) module_load("ui-", lib)
#define audio_module_load(lib) module_load("audio-", lib)
#define block_module_load(lib, errp) module_load("block-", lib, errp)
#define ui_module_load(lib, errp) module_load("ui-", lib, errp)
#define audio_module_load(lib, errp) module_load("audio-", lib, errp)

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);
bool module_load(const char *prefix, const char *lib_name);
void module_load_qom(const char *type);

/*
* module_load: attempt to load a module from a set of directories
*
* directories searched are:
* - getenv("QEMU_MODULE_DIR")
* - get_relocated_path(CONFIG_QEMU_MODDIR);
* - /var/run/qemu/${version_dir}
*
* prefix: a subsystem prefix, or the empty string ("audio-", ..., "")
* name: name of the module
* errp: error to set in case the module is found, but load failed.
*
* Return value: -1 on error (errp set if not NULL).
* 0 if module or one of its dependencies are not installed,
* 1 if the module is found and loaded,
* 2 if the module is already loaded, or module is built-in.
*/
int module_load(const char *prefix, const char *name, Error **errp);

/*
* module_load_qom: attempt to load a module to provide a QOM type
*
* type: the type to be provided
* errp: error to set.
*
* Return value: as per module_load.
*/
int module_load_qom(const char *type, Error **errp);
void module_load_qom_all(void);
void module_allow_arch(const char *arch);

Expand Down
18 changes: 14 additions & 4 deletions qom/object.c
Expand Up @@ -526,8 +526,13 @@ void object_initialize(void *data, size_t size, const char *typename)

#ifdef CONFIG_MODULES
if (!type) {
module_load_qom(typename);
type = type_get_by_name(typename);
int rv = module_load_qom(typename, &error_fatal);
if (rv > 0) {
type = type_get_by_name(typename);
} else {
error_report("missing object type '%s'", typename);
exit(1);
}
}
#endif
if (!type) {
Expand Down Expand Up @@ -1033,8 +1038,13 @@ ObjectClass *module_object_class_by_name(const char *typename)
oc = object_class_by_name(typename);
#ifdef CONFIG_MODULES
if (!oc) {
module_load_qom(typename);
oc = object_class_by_name(typename);
Error *local_err = NULL;
int rv = module_load_qom(typename, &local_err);
if (rv > 0) {
oc = object_class_by_name(typename);
} else if (rv < 0) {
error_report_err(local_err);
}
}
#endif
return oc;
Expand Down
8 changes: 7 additions & 1 deletion softmmu/qtest.c
Expand Up @@ -753,12 +753,18 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
qtest_sendf(chr, "OK %"PRIi64"\n",
(int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
} else if (strcmp(words[0], "module_load") == 0) {
Error *local_err = NULL;
int rv;
g_assert(words[1] && words[2]);

qtest_send_prefix(chr);
if (module_load(words[1], words[2])) {
rv = module_load(words[1], words[2], &local_err);
if (rv > 0) {
qtest_sendf(chr, "OK\n");
} else {
if (rv < 0) {
error_report_err(local_err);
}
qtest_sendf(chr, "FAIL\n");
}
} else if (qtest_enabled() && strcmp(words[0], "clock_set") == 0) {
Expand Down
18 changes: 15 additions & 3 deletions ui/console.c
Expand Up @@ -2632,7 +2632,11 @@ bool qemu_display_find_default(DisplayOptions *opts)

for (i = 0; i < (int)ARRAY_SIZE(prio); i++) {
if (dpys[prio[i]] == NULL) {
ui_module_load(DisplayType_str(prio[i]));
Error *local_err = NULL;
int rv = ui_module_load(DisplayType_str(prio[i]), &local_err);
if (rv < 0) {
error_report_err(local_err);
}
}
if (dpys[prio[i]] == NULL) {
continue;
Expand All @@ -2650,7 +2654,11 @@ void qemu_display_early_init(DisplayOptions *opts)
return;
}
if (dpys[opts->type] == NULL) {
ui_module_load(DisplayType_str(opts->type));
Error *local_err = NULL;
int rv = ui_module_load(DisplayType_str(opts->type), &local_err);
if (rv < 0) {
error_report_err(local_err);
}
}
if (dpys[opts->type] == NULL) {
error_report("Display '%s' is not available.",
Expand Down Expand Up @@ -2680,7 +2688,11 @@ void qemu_display_help(void)
printf("none\n");
for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) {
if (!dpys[idx]) {
ui_module_load(DisplayType_str(idx));
Error *local_err = NULL;
int rv = ui_module_load(DisplayType_str(idx), &local_err);
if (rv < 0) {
error_report_err(local_err);
}
}
if (dpys[idx]) {
printf("%s\n", DisplayType_str(dpys[idx]->type));
Expand Down

0 comments on commit c551fb0

Please sign in to comment.