diff --git a/sys/dev/digi/digi.c b/sys/dev/digi/digi.c index 283c2cfa8c3f..2eb72b049a75 100644 --- a/sys/dev/digi/digi.c +++ b/sys/dev/digi/digi.c @@ -795,7 +795,7 @@ digi_loadmoduledata(struct digi_softc *sc) free(sym, M_TEMP); if (symptr == NULL) { printf("digi_%s.ko: Symbol `%s' not found\n", sc->module, sym); - linker_file_unload(lf, LINKER_UNLOAD_FORCE); + linker_release_module(NULL, NULL, lf); return (EINVAL); } @@ -803,7 +803,7 @@ digi_loadmoduledata(struct digi_softc *sc) if (digi_mod->dm_version != DIGI_MOD_VERSION) { printf("digi_%s.ko: Invalid version %d (need %d)\n", sc->module, digi_mod->dm_version, DIGI_MOD_VERSION); - linker_file_unload(lf, LINKER_UNLOAD_FORCE); + linker_release_module(NULL, NULL, lf); return (EINVAL); } @@ -825,7 +825,7 @@ digi_loadmoduledata(struct digi_softc *sc) bcopy(digi_mod->dm_link.data, sc->link.data, sc->link.size); } - linker_file_unload(lf, LINKER_UNLOAD_FORCE); + linker_release_module(NULL, NULL, lf); return (0); } diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c index 003b70816878..6eb62b6cfda5 100644 --- a/sys/kern/kern_linker.c +++ b/sys/kern/kern_linker.c @@ -415,14 +415,44 @@ linker_reference_module(const char *modname, struct mod_depend *verinfo, linker_file_t *result) { modlist_t mod; + int error; + mtx_lock(&Giant); if ((mod = modlist_lookup2(modname, verinfo)) != NULL) { *result = mod->container; (*result)->refs++; + mtx_unlock(&Giant); return (0); } - return (linker_load_module(NULL, modname, NULL, verinfo, result)); + error = linker_load_module(NULL, modname, NULL, verinfo, result); + mtx_unlock(&Giant); + return (error); +} + +int +linker_release_module(const char *modname, struct mod_depend *verinfo, + linker_file_t lf) +{ + modlist_t mod; + int error; + + mtx_lock(&Giant); + if (lf == NULL) { + KASSERT(modname != NULL, + ("linker_release_module: no file or name")); + mod = modlist_lookup2(modname, verinfo); + if (mod == NULL) { + mtx_unlock(&Giant); + return (ESRCH); + } + lf = mod->container; + } else + KASSERT(modname == NULL && verinfo == NULL, + ("linker_release_module: both file and name")); + error = linker_file_unload(lf, LINKER_UNLOAD_NORMAL); + mtx_unlock(&Giant); + return (error); } static linker_file_t diff --git a/sys/kern/subr_firmware.c b/sys/kern/subr_firmware.c index 0c238bf03fef..3f7fb64145ec 100644 --- a/sys/kern/subr_firmware.c +++ b/sys/kern/subr_firmware.c @@ -195,9 +195,7 @@ again: "load firmware image %s\n", __func__, imagename); return NULL; } - mtx_lock(&Giant); /* XXX */ (void) linker_reference_module(imagename, NULL, &result); - mtx_unlock(&Giant); /* XXX */ requested_load = 1; goto again; /* sort of an Algol-style for loop */ } @@ -227,7 +225,7 @@ unloadentry(void *unused1, int unused2) fp->file = NULL; mtx_unlock(&firmware_mtx); - linker_file_unload(file, LINKER_UNLOAD_NORMAL); + linker_release_module(NULL, NULL, file); mtx_lock(&firmware_mtx); } diff --git a/sys/sys/linker.h b/sys/sys/linker.h index 8e3c1faffb21..39311ff66509 100644 --- a/sys/sys/linker.h +++ b/sys/sys/linker.h @@ -115,6 +115,14 @@ int linker_add_class(linker_class_t _cls); int linker_reference_module(const char* _modname, struct mod_depend *_verinfo, linker_file_t* _result); +/* + * Release a reference to a module, unloading it if there are no more + * references. Note that one should either provide a module name and + * optional version info or a linker file, but not both. + */ +int linker_release_module(const char *_modname, struct mod_depend *_verinfo, + linker_file_t _file); + /* * Iterate over all of the currently loaded linker files calling the * predicate function while the function returns 0. Returns the value