add more accessor functions for various struct module fields.
add a mechanism for registering callbacks to be called upon module load/unload.
This commit is contained in:
parent
e406c140eb
commit
d5abe492a8
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: kern_module.c,v 1.130 2017/12/14 22:28:59 pgoyette Exp $ */
|
||||
/* $NetBSD: kern_module.c,v 1.131 2018/05/28 21:04:40 chs Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2008 The NetBSD Foundation, Inc.
|
||||
@ -34,7 +34,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: kern_module.c,v 1.130 2017/12/14 22:28:59 pgoyette Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: kern_module.c,v 1.131 2018/05/28 21:04:40 chs Exp $");
|
||||
|
||||
#define _MODULE_INTERNAL
|
||||
|
||||
@ -65,6 +65,21 @@ struct modlist module_list = TAILQ_HEAD_INITIALIZER(module_list);
|
||||
struct modlist module_builtins = TAILQ_HEAD_INITIALIZER(module_builtins);
|
||||
static struct modlist module_bootlist = TAILQ_HEAD_INITIALIZER(module_bootlist);
|
||||
|
||||
struct module_callbacks {
|
||||
TAILQ_ENTRY(module_callbacks) modcb_list;
|
||||
void (*modcb_load)(struct module *);
|
||||
void (*modcb_unload)(struct module *);
|
||||
};
|
||||
TAILQ_HEAD(modcblist, module_callbacks);
|
||||
static struct modcblist modcblist;
|
||||
|
||||
static module_t *module_netbsd;
|
||||
static const modinfo_t module_netbsd_modinfo = {
|
||||
.mi_version = __NetBSD_Version__,
|
||||
.mi_class = MODULE_CLASS_MISC,
|
||||
.mi_name = "netbsd"
|
||||
};
|
||||
|
||||
static module_t *module_active;
|
||||
bool module_verbose_on;
|
||||
#ifdef MODULAR_DEFAULT_AUTOLOAD
|
||||
@ -84,11 +99,14 @@ int (*module_load_vfs_vec)(const char *, int, bool, module_t *,
|
||||
|
||||
static kauth_listener_t module_listener;
|
||||
|
||||
static specificdata_domain_t module_specificdata_domain;
|
||||
|
||||
/* Ensure that the kernel's link set isn't empty. */
|
||||
static modinfo_t module_dummy;
|
||||
__link_set_add_rodata(modules, module_dummy);
|
||||
|
||||
static module_t *module_newmodule(modsrc_t);
|
||||
static void module_free(module_t *);
|
||||
static void module_require_force(module_t *);
|
||||
static int module_do_load(const char *, bool, int, prop_dictionary_t,
|
||||
module_t **, modclass_t modclass, bool);
|
||||
@ -106,6 +124,9 @@ static bool module_merge_dicts(prop_dictionary_t, const prop_dictionary_t);
|
||||
static void sysctl_module_setup(void);
|
||||
static int sysctl_module_autotime(SYSCTLFN_PROTO);
|
||||
|
||||
static void module_callback_load(struct module *);
|
||||
static void module_callback_unload(struct module *);
|
||||
|
||||
#define MODULE_CLASS_MATCH(mi, modclass) \
|
||||
((modclass) == MODULE_CLASS_ANY || (modclass) == (mi)->mi_class)
|
||||
|
||||
@ -116,6 +137,13 @@ module_incompat(const modinfo_t *mi, int modclass)
|
||||
mi->mi_name, modclass, mi->mi_class);
|
||||
}
|
||||
|
||||
struct module *
|
||||
module_kernel(void)
|
||||
{
|
||||
|
||||
return module_netbsd;
|
||||
}
|
||||
|
||||
/*
|
||||
* module_error:
|
||||
*
|
||||
@ -152,6 +180,30 @@ module_print(const char *fmt, ...)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* module_name:
|
||||
*
|
||||
* Utility function: return the module's name.
|
||||
*/
|
||||
const char *
|
||||
module_name(struct module *mod)
|
||||
{
|
||||
|
||||
return mod->mod_info->mi_name;
|
||||
}
|
||||
|
||||
/*
|
||||
* module_source:
|
||||
*
|
||||
* Utility function: return the module's source.
|
||||
*/
|
||||
modsrc_t
|
||||
module_source(struct module *mod)
|
||||
{
|
||||
|
||||
return mod->mod_source;
|
||||
}
|
||||
|
||||
static int
|
||||
module_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
|
||||
void *arg0, void *arg1, void *arg2, void *arg3)
|
||||
@ -179,11 +231,21 @@ module_newmodule(modsrc_t source)
|
||||
|
||||
mod = kmem_zalloc(sizeof(*mod), KM_SLEEP);
|
||||
mod->mod_source = source;
|
||||
mod->mod_info = NULL;
|
||||
mod->mod_flags = 0;
|
||||
specificdata_init(module_specificdata_domain, &mod->mod_sdref);
|
||||
return mod;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free a module_t
|
||||
*/
|
||||
static void
|
||||
module_free(module_t *mod)
|
||||
{
|
||||
|
||||
specificdata_fini(module_specificdata_domain, &mod->mod_sdref);
|
||||
kmem_free(mod, sizeof(*mod));
|
||||
}
|
||||
|
||||
/*
|
||||
* Require the -f (force) flag to load a module
|
||||
*/
|
||||
@ -280,7 +342,7 @@ module_builtin_add(modinfo_t *const *mip, size_t nmodinfo, bool init)
|
||||
if (rv != 0) {
|
||||
for (i = 0; i < nmodinfo; i++) {
|
||||
if (modp[i])
|
||||
kmem_free(modp[i], sizeof(*modp[i]));
|
||||
module_free(modp[i]);
|
||||
}
|
||||
}
|
||||
kmem_free(modp, sizeof(*modp) * nmodinfo);
|
||||
@ -347,6 +409,7 @@ module_init(void)
|
||||
}
|
||||
cv_init(&module_thread_cv, "mod_unld");
|
||||
mutex_init(&module_thread_lock, MUTEX_DEFAULT, IPL_NONE);
|
||||
TAILQ_INIT(&modcblist);
|
||||
|
||||
#ifdef MODULAR /* XXX */
|
||||
module_init_md();
|
||||
@ -373,6 +436,11 @@ module_init(void)
|
||||
}
|
||||
|
||||
sysctl_module_setup();
|
||||
module_specificdata_domain = specificdata_domain_create();
|
||||
|
||||
module_netbsd = module_newmodule(MODULE_SOURCE_KERNEL);
|
||||
module_netbsd->mod_refcnt = 1;
|
||||
module_netbsd->mod_info = &module_netbsd_modinfo;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -685,21 +753,13 @@ module_lookup(const char *name)
|
||||
* responsibility to ensure that the reference is dropped
|
||||
* later.
|
||||
*/
|
||||
int
|
||||
module_hold(const char *name)
|
||||
void
|
||||
module_hold(module_t *mod)
|
||||
{
|
||||
module_t *mod;
|
||||
|
||||
kernconfig_lock();
|
||||
mod = module_lookup(name);
|
||||
if (mod == NULL) {
|
||||
kernconfig_unlock();
|
||||
return ENOENT;
|
||||
}
|
||||
mod->mod_refcnt++;
|
||||
kernconfig_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -708,16 +768,11 @@ module_hold(const char *name)
|
||||
* Release a reference acquired with module_hold().
|
||||
*/
|
||||
void
|
||||
module_rele(const char *name)
|
||||
module_rele(module_t *mod)
|
||||
{
|
||||
module_t *mod;
|
||||
|
||||
kernconfig_lock();
|
||||
mod = module_lookup(name);
|
||||
if (mod == NULL) {
|
||||
kernconfig_unlock();
|
||||
panic("%s: gone", __func__);
|
||||
}
|
||||
KASSERT(mod->mod_refcnt > 0);
|
||||
mod->mod_refcnt--;
|
||||
kernconfig_unlock();
|
||||
}
|
||||
@ -982,7 +1037,7 @@ module_do_load(const char *name, bool isdep, int flags,
|
||||
module_error("vfs load failed for `%s', "
|
||||
"error %d", name, error);
|
||||
#endif
|
||||
kmem_free(mod, sizeof(*mod));
|
||||
module_free(mod);
|
||||
depth--;
|
||||
return error;
|
||||
}
|
||||
@ -1181,6 +1236,7 @@ module_do_load(const char *name, bool isdep, int flags,
|
||||
}
|
||||
depth--;
|
||||
module_print("module `%s' loaded successfully", mi->mi_name);
|
||||
module_callback_load(mod);
|
||||
return 0;
|
||||
|
||||
fail1:
|
||||
@ -1193,7 +1249,7 @@ module_do_load(const char *name, bool isdep, int flags,
|
||||
filedict = NULL;
|
||||
}
|
||||
TAILQ_REMOVE(pending, mod, mod_chain);
|
||||
kmem_free(mod, sizeof(*mod));
|
||||
module_free(mod);
|
||||
depth--;
|
||||
return error;
|
||||
}
|
||||
@ -1238,6 +1294,7 @@ module_do_unload(const char *name, bool load_requires_force)
|
||||
|
||||
prev_active = module_active;
|
||||
module_active = mod;
|
||||
module_callback_unload(mod);
|
||||
error = (*mod->mod_info->mi_modcmd)(MODULE_CMD_FINI, NULL);
|
||||
module_active = prev_active;
|
||||
if (error != 0) {
|
||||
@ -1261,7 +1318,7 @@ module_do_unload(const char *name, bool load_requires_force)
|
||||
TAILQ_INSERT_TAIL(&module_builtins, mod, mod_chain);
|
||||
module_builtinlist++;
|
||||
} else {
|
||||
kmem_free(mod, sizeof(*mod));
|
||||
module_free(mod);
|
||||
}
|
||||
module_gen++;
|
||||
|
||||
@ -1311,7 +1368,7 @@ module_prime(const char *name, void *base, size_t size)
|
||||
|
||||
error = kobj_load_mem(&mod->mod_kobj, name, base, size);
|
||||
if (error != 0) {
|
||||
kmem_free(mod, sizeof(*mod));
|
||||
module_free(mod);
|
||||
module_error("unable to load `%s' pushed by boot loader, "
|
||||
"error %d", name, error);
|
||||
return error;
|
||||
@ -1319,7 +1376,7 @@ module_prime(const char *name, void *base, size_t size)
|
||||
error = module_fetch_info(mod);
|
||||
if (error != 0) {
|
||||
kobj_unload(mod->mod_kobj);
|
||||
kmem_free(mod, sizeof(*mod));
|
||||
module_free(mod);
|
||||
module_error("unable to fetch_info for `%s' pushed by boot "
|
||||
"loader, error %d", name, error);
|
||||
return error;
|
||||
@ -1565,3 +1622,131 @@ out:
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
/*
|
||||
* module_specific_key_create:
|
||||
*
|
||||
* Create a key for subsystem module-specific data.
|
||||
*/
|
||||
specificdata_key_t
|
||||
module_specific_key_create(specificdata_key_t *keyp, specificdata_dtor_t dtor)
|
||||
{
|
||||
|
||||
return specificdata_key_create(module_specificdata_domain, keyp, dtor);
|
||||
}
|
||||
|
||||
/*
|
||||
* module_specific_key_delete:
|
||||
*
|
||||
* Delete a key for subsystem module-specific data.
|
||||
*/
|
||||
void
|
||||
module_specific_key_delete(specificdata_key_t key)
|
||||
{
|
||||
|
||||
return specificdata_key_delete(module_specificdata_domain, key);
|
||||
}
|
||||
|
||||
/*
|
||||
* module_getspecific:
|
||||
*
|
||||
* Return module-specific data corresponding to the specified key.
|
||||
*/
|
||||
void *
|
||||
module_getspecific(module_t *mod, specificdata_key_t key)
|
||||
{
|
||||
|
||||
return specificdata_getspecific(module_specificdata_domain,
|
||||
&mod->mod_sdref, key);
|
||||
}
|
||||
|
||||
/*
|
||||
* module_setspecific:
|
||||
*
|
||||
* Set module-specific data corresponding to the specified key.
|
||||
*/
|
||||
void
|
||||
module_setspecific(module_t *mod, specificdata_key_t key, void *data)
|
||||
{
|
||||
|
||||
specificdata_setspecific(module_specificdata_domain,
|
||||
&mod->mod_sdref, key, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* module_register_callbacks:
|
||||
*
|
||||
* Register a new set of callbacks to be called on module load/unload.
|
||||
* Call the load callback on each existing module.
|
||||
* Return an opaque handle for unregistering these later.
|
||||
*/
|
||||
void *
|
||||
module_register_callbacks(void (*load)(struct module *),
|
||||
void (*unload)(struct module *))
|
||||
{
|
||||
struct module_callbacks *modcb;
|
||||
struct module *mod;
|
||||
|
||||
modcb = kmem_alloc(sizeof(*modcb), KM_SLEEP);
|
||||
modcb->modcb_load = load;
|
||||
modcb->modcb_unload = unload;
|
||||
|
||||
kernconfig_lock();
|
||||
TAILQ_INSERT_TAIL(&modcblist, modcb, modcb_list);
|
||||
TAILQ_FOREACH(mod, &module_list, mod_chain)
|
||||
load(mod);
|
||||
kernconfig_unlock();
|
||||
|
||||
return modcb;
|
||||
}
|
||||
|
||||
/*
|
||||
* module_unregister_callbacks:
|
||||
*
|
||||
* Unregister a previously-registered set of module load/unload callbacks.
|
||||
* Call the unload callback on each existing module.
|
||||
*/
|
||||
void
|
||||
module_unregister_callbacks(void *opaque)
|
||||
{
|
||||
struct module_callbacks *modcb;
|
||||
struct module *mod;
|
||||
|
||||
modcb = opaque;
|
||||
kernconfig_lock();
|
||||
TAILQ_FOREACH(mod, &module_list, mod_chain)
|
||||
modcb->modcb_unload(mod);
|
||||
TAILQ_REMOVE(&modcblist, modcb, modcb_list);
|
||||
kernconfig_unlock();
|
||||
kmem_free(modcb, sizeof(*modcb));
|
||||
}
|
||||
|
||||
/*
|
||||
* module_callback_load:
|
||||
*
|
||||
* Helper routine: call all load callbacks on a module being loaded.
|
||||
*/
|
||||
static void
|
||||
module_callback_load(struct module *mod)
|
||||
{
|
||||
struct module_callbacks *modcb;
|
||||
|
||||
TAILQ_FOREACH(modcb, &modcblist, modcb_list) {
|
||||
modcb->modcb_load(mod);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* module_callback_unload:
|
||||
*
|
||||
* Helper routine: call all unload callbacks on a module being unloaded.
|
||||
*/
|
||||
static void
|
||||
module_callback_unload(struct module *mod)
|
||||
{
|
||||
struct module_callbacks *modcb;
|
||||
|
||||
TAILQ_FOREACH(modcb, &modcblist, modcb_list) {
|
||||
modcb->modcb_unload(mod);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: module.h,v 1.41 2016/11/16 10:42:14 pgoyette Exp $ */
|
||||
/* $NetBSD: module.h,v 1.42 2018/05/28 21:04:40 chs Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2008 The NetBSD Foundation, Inc.
|
||||
@ -32,7 +32,6 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#define MAXMODNAME 32
|
||||
@ -68,6 +67,8 @@ typedef enum modcmd {
|
||||
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/specificdata.h>
|
||||
|
||||
#include <prop/proplib.h>
|
||||
|
||||
@ -83,6 +84,9 @@ typedef struct modinfo {
|
||||
/* Per module information, maintained by kern_module.c */
|
||||
typedef struct module {
|
||||
u_int mod_refcnt;
|
||||
int mod_flags;
|
||||
#define MODFLG_MUST_FORCE 0x01
|
||||
#define MODFLG_AUTO_LOADED 0x02
|
||||
const modinfo_t *mod_info;
|
||||
struct kobj *mod_kobj;
|
||||
TAILQ_ENTRY(module) mod_chain;
|
||||
@ -90,12 +94,7 @@ typedef struct module {
|
||||
u_int mod_nrequired;
|
||||
modsrc_t mod_source;
|
||||
time_t mod_autotime;
|
||||
void *mod_ctf;
|
||||
u_int mod_fbtentries; /* DTrace FBT entry count */
|
||||
int mod_flags;
|
||||
#define MODFLG_MUST_FORCE 0x01
|
||||
#define MODFLG_AUTO_LOADED 0x02
|
||||
|
||||
specificdata_reference mod_sdref;
|
||||
} module_t;
|
||||
|
||||
/*
|
||||
@ -177,18 +176,29 @@ void module_init_md(void);
|
||||
void module_init_class(modclass_t);
|
||||
int module_prime(const char *, void *, size_t);
|
||||
|
||||
module_t *module_kernel(void);
|
||||
const char *module_name(struct module *);
|
||||
modsrc_t module_source(struct module *);
|
||||
bool module_compatible(int, int);
|
||||
int module_load(const char *, int, prop_dictionary_t, modclass_t);
|
||||
int module_builtin_add(modinfo_t * const *, size_t, bool);
|
||||
int module_builtin_remove(modinfo_t *, bool);
|
||||
int module_autoload(const char *, modclass_t);
|
||||
int module_unload(const char *);
|
||||
int module_hold(const char *);
|
||||
void module_rele(const char *);
|
||||
void module_hold(module_t *);
|
||||
void module_rele(module_t *);
|
||||
int module_find_section(const char *, void **, size_t *);
|
||||
void module_thread_kick(void);
|
||||
void module_load_vfs_init(void);
|
||||
|
||||
specificdata_key_t module_specific_key_create(specificdata_key_t *, specificdata_dtor_t);
|
||||
void module_specific_key_delete(specificdata_key_t);
|
||||
void *module_getspecific(module_t *, specificdata_key_t);
|
||||
void module_setspecific(module_t *, specificdata_key_t, void *);
|
||||
void *module_register_callbacks(void (*)(struct module *),
|
||||
void (*)(struct module *));
|
||||
void module_unregister_callbacks(void *);
|
||||
|
||||
void module_whatis(uintptr_t, void (*)(const char *, ...)
|
||||
__printflike(1, 2));
|
||||
void module_print_list(void (*)(const char *, ...) __printflike(1, 2));
|
||||
@ -224,12 +234,12 @@ typedef struct modctl_load {
|
||||
size_t ml_propslen;
|
||||
} modctl_load_t;
|
||||
|
||||
typedef enum modctl {
|
||||
enum modctl {
|
||||
MODCTL_LOAD, /* modctl_load_t *ml */
|
||||
MODCTL_UNLOAD, /* char *name */
|
||||
MODCTL_STAT, /* struct iovec *buffer */
|
||||
MODCTL_EXISTS /* enum: 0: load, 1: autoload */
|
||||
} modctl_t;
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure intentionally has the same layout for 32 and 64
|
||||
|
Loading…
Reference in New Issue
Block a user