summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/kern/kern_linker.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/kern/kern_linker.c')
-rw-r--r--freebsd/sys/kern/kern_linker.c197
1 files changed, 84 insertions, 113 deletions
diff --git a/freebsd/sys/kern/kern_linker.c b/freebsd/sys/kern/kern_linker.c
index b1b46d7a..39664a85 100644
--- a/freebsd/sys/kern/kern_linker.c
+++ b/freebsd/sys/kern/kern_linker.c
@@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/mount.h>
#include <sys/linker.h>
+#include <sys/eventhandler.h>
#include <sys/fcntl.h>
#include <sys/jail.h>
#include <sys/libkern.h>
@@ -71,17 +72,6 @@ SYSCTL_INT(_debug, OID_AUTO, kld_debug, CTLFLAG_RW,
&kld_debug, 0, "Set various levels of KLD debug");
#endif
-#define KLD_LOCK() sx_xlock(&kld_sx)
-#define KLD_UNLOCK() sx_xunlock(&kld_sx)
-#define KLD_DOWNGRADE() sx_downgrade(&kld_sx)
-#define KLD_LOCK_READ() sx_slock(&kld_sx)
-#define KLD_UNLOCK_READ() sx_sunlock(&kld_sx)
-#define KLD_LOCKED() sx_xlocked(&kld_sx)
-#define KLD_LOCK_ASSERT() do { \
- if (!cold) \
- sx_assert(&kld_sx, SX_XLOCKED); \
-} while (0)
-
/*
* static char *linker_search_path(const char *name, struct mod_depend
* *verinfo);
@@ -123,7 +113,8 @@ static int linker_no_more_classes = 0;
#define LINKER_GET_NEXT_FILE_ID(a) do { \
linker_file_t lftmp; \
\
- KLD_LOCK_ASSERT(); \
+ if (!cold) \
+ sx_assert(&kld_sx, SA_XLOCKED); \
retry: \
TAILQ_FOREACH(lftmp, &linker_files, link) { \
if (next_file_id == lftmp->id) { \
@@ -210,6 +201,8 @@ linker_file_sysinit(linker_file_t lf)
KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n",
lf->filename));
+ sx_assert(&kld_sx, SA_XLOCKED);
+
if (linker_file_lookup_set(lf, "sysinit_set", &start, &stop, NULL) != 0)
return;
/*
@@ -235,6 +228,7 @@ linker_file_sysinit(linker_file_t lf)
* Traverse the (now) ordered list of system initialization tasks.
* Perform each task, and continue on to the next task.
*/
+ sx_xunlock(&kld_sx);
mtx_lock(&Giant);
for (sipp = start; sipp < stop; sipp++) {
if ((*sipp)->subsystem == SI_SUB_DUMMY)
@@ -244,6 +238,7 @@ linker_file_sysinit(linker_file_t lf)
(*((*sipp)->func)) ((*sipp)->udata);
}
mtx_unlock(&Giant);
+ sx_xlock(&kld_sx);
}
static void
@@ -254,6 +249,8 @@ linker_file_sysuninit(linker_file_t lf)
KLD_DPF(FILE, ("linker_file_sysuninit: calling SYSUNINITs for %s\n",
lf->filename));
+ sx_assert(&kld_sx, SA_XLOCKED);
+
if (linker_file_lookup_set(lf, "sysuninit_set", &start, &stop,
NULL) != 0)
return;
@@ -281,6 +278,7 @@ linker_file_sysuninit(linker_file_t lf)
* Traverse the (now) ordered list of system initialization tasks.
* Perform each task, and continue on to the next task.
*/
+ sx_xunlock(&kld_sx);
mtx_lock(&Giant);
for (sipp = start; sipp < stop; sipp++) {
if ((*sipp)->subsystem == SI_SUB_DUMMY)
@@ -290,6 +288,7 @@ linker_file_sysuninit(linker_file_t lf)
(*((*sipp)->func)) ((*sipp)->udata);
}
mtx_unlock(&Giant);
+ sx_xlock(&kld_sx);
}
static void
@@ -301,13 +300,17 @@ linker_file_register_sysctls(linker_file_t lf)
("linker_file_register_sysctls: registering SYSCTLs for %s\n",
lf->filename));
+ sx_assert(&kld_sx, SA_XLOCKED);
+
if (linker_file_lookup_set(lf, "sysctl_set", &start, &stop, NULL) != 0)
return;
+ sx_xunlock(&kld_sx);
sysctl_lock();
for (oidp = start; oidp < stop; oidp++)
sysctl_register_oid(*oidp);
sysctl_unlock();
+ sx_xlock(&kld_sx);
}
static void
@@ -318,13 +321,17 @@ linker_file_unregister_sysctls(linker_file_t lf)
KLD_DPF(FILE, ("linker_file_unregister_sysctls: registering SYSCTLs"
" for %s\n", lf->filename));
+ sx_assert(&kld_sx, SA_XLOCKED);
+
if (linker_file_lookup_set(lf, "sysctl_set", &start, &stop, NULL) != 0)
return;
+ sx_xunlock(&kld_sx);
sysctl_lock();
for (oidp = start; oidp < stop; oidp++)
sysctl_unregister_oid(*oidp);
sysctl_unlock();
+ sx_xlock(&kld_sx);
}
#endif /* __rtems__ */
@@ -339,6 +346,8 @@ linker_file_register_modules(linker_file_t lf)
" in %s\n", lf->filename));
#ifndef __rtems__
+ sx_assert(&kld_sx, SA_XLOCKED);
+
if (linker_file_lookup_set(lf, "modmetadata_set", &start,
&stop, NULL) != 0) {
/*
@@ -379,7 +388,9 @@ linker_init_kernel_modules(void)
{
#ifndef __rtems__
+ sx_xlock(&kld_sx);
linker_file_register_modules(linker_kernel_file);
+ sx_xunlock(&kld_sx);
#else /* __rtems__ */
linker_file_register_modules(NULL);
#endif /* __rtems__ */
@@ -400,7 +411,7 @@ linker_load_file(const char *filename, linker_file_t *result)
if (prison0.pr_securelevel > 0)
return (EPERM);
- KLD_LOCK_ASSERT();
+ sx_assert(&kld_sx, SA_XLOCKED);
lf = linker_find_file_by_name(filename);
if (lf) {
KLD_DPF(FILE, ("linker_load_file: file %s is already loaded,"
@@ -434,10 +445,8 @@ linker_load_file(const char *filename, linker_file_t *result)
return (error);
}
modules = !TAILQ_EMPTY(&lf->modules);
- KLD_UNLOCK();
linker_file_register_sysctls(lf);
linker_file_sysinit(lf);
- KLD_LOCK();
lf->flags |= LINKER_FILE_LINKED;
/*
@@ -449,6 +458,7 @@ linker_load_file(const char *filename, linker_file_t *result)
linker_file_unload(lf, LINKER_UNLOAD_FORCE);
return (ENOEXEC);
}
+ EVENTHANDLER_INVOKE(kld_load, lf);
*result = lf;
return (0);
}
@@ -488,16 +498,16 @@ linker_reference_module(const char *modname, struct mod_depend *verinfo,
modlist_t mod;
int error;
- KLD_LOCK();
+ sx_xlock(&kld_sx);
if ((mod = modlist_lookup2(modname, verinfo)) != NULL) {
*result = mod->container;
(*result)->refs++;
- KLD_UNLOCK();
+ sx_xunlock(&kld_sx);
return (0);
}
error = linker_load_module(NULL, modname, NULL, verinfo, result);
- KLD_UNLOCK();
+ sx_xunlock(&kld_sx);
return (error);
}
@@ -508,13 +518,13 @@ linker_release_module(const char *modname, struct mod_depend *verinfo,
modlist_t mod;
int error;
- KLD_LOCK();
+ sx_xlock(&kld_sx);
if (lf == NULL) {
KASSERT(modname != NULL,
("linker_release_module: no file or name"));
mod = modlist_lookup2(modname, verinfo);
if (mod == NULL) {
- KLD_UNLOCK();
+ sx_xunlock(&kld_sx);
return (ESRCH);
}
lf = mod->container;
@@ -522,7 +532,7 @@ linker_release_module(const char *modname, struct mod_depend *verinfo,
KASSERT(modname == NULL && verinfo == NULL,
("linker_release_module: both file and name"));
error = linker_file_unload(lf, LINKER_UNLOAD_NORMAL);
- KLD_UNLOCK();
+ sx_xunlock(&kld_sx);
return (error);
}
@@ -535,7 +545,7 @@ linker_find_file_by_name(const char *filename)
koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK);
sprintf(koname, "%s.ko", filename);
- KLD_LOCK_ASSERT();
+ sx_assert(&kld_sx, SA_XLOCKED);
TAILQ_FOREACH(lf, &linker_files, link) {
if (strcmp(lf->filename, koname) == 0)
break;
@@ -551,7 +561,7 @@ linker_find_file_by_id(int fileid)
{
linker_file_t lf;
- KLD_LOCK_ASSERT();
+ sx_assert(&kld_sx, SA_XLOCKED);
TAILQ_FOREACH(lf, &linker_files, link)
if (lf->id == fileid && lf->flags & LINKER_FILE_LINKED)
break;
@@ -564,13 +574,13 @@ linker_file_foreach(linker_predicate_t *predicate, void *context)
linker_file_t lf;
int retval = 0;
- KLD_LOCK();
+ sx_xlock(&kld_sx);
TAILQ_FOREACH(lf, &linker_files, link) {
retval = predicate(lf, context);
if (retval != 0)
break;
}
- KLD_UNLOCK();
+ sx_xunlock(&kld_sx);
return (retval);
}
@@ -580,7 +590,8 @@ linker_make_file(const char *pathname, linker_class_t lc)
linker_file_t lf;
const char *filename;
- KLD_LOCK_ASSERT();
+ if (!cold)
+ sx_assert(&kld_sx, SA_XLOCKED);
filename = linker_basename(pathname);
KLD_DPF(FILE, ("linker_make_file: new file, filename='%s' for pathname='%s'\n", filename, pathname));
@@ -596,8 +607,6 @@ linker_make_file(const char *pathname, linker_class_t lc)
lf->ndeps = 0;
lf->deps = NULL;
lf->loadcnt = ++loadcnt;
- lf->sdt_probes = NULL;
- lf->sdt_nprobes = 0;
STAILQ_INIT(&lf->common);
TAILQ_INIT(&lf->modules);
TAILQ_INSERT_TAIL(&linker_files, lf, link);
@@ -616,7 +625,7 @@ linker_file_unload(linker_file_t file, int flags)
if (prison0.pr_securelevel > 0)
return (EPERM);
- KLD_LOCK_ASSERT();
+ sx_assert(&kld_sx, SA_XLOCKED);
KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs));
/* Easy case of just dropping a reference. */
@@ -625,6 +634,12 @@ linker_file_unload(linker_file_t file, int flags)
return (0);
}
+ /* Give eventhandlers a chance to prevent the unload. */
+ error = 0;
+ EVENTHANDLER_INVOKE(kld_unload_try, file, &error);
+ if (error != 0)
+ return (EBUSY);
+
KLD_DPF(FILE, ("linker_file_unload: file is unloading,"
" informing modules\n"));
@@ -689,10 +704,8 @@ linker_file_unload(linker_file_t file, int flags)
*/
if (file->flags & LINKER_FILE_LINKED) {
file->flags &= ~LINKER_FILE_LINKED;
- KLD_UNLOCK();
linker_file_sysuninit(file);
linker_file_unregister_sysctls(file);
- KLD_LOCK();
}
TAILQ_REMOVE(&linker_files, file, link);
@@ -708,6 +721,10 @@ linker_file_unload(linker_file_t file, int flags)
}
LINKER_UNLOAD(file);
+
+ EVENTHANDLER_INVOKE(kld_unload, file->filename, file->address,
+ file->size);
+
if (file->filename) {
free(file->filename, M_LINKER);
file->filename = NULL;
@@ -731,18 +748,9 @@ linker_file_add_dependency(linker_file_t file, linker_file_t dep)
{
linker_file_t *newdeps;
- KLD_LOCK_ASSERT();
- newdeps = malloc((file->ndeps + 1) * sizeof(linker_file_t *),
+ sx_assert(&kld_sx, SA_XLOCKED);
+ file->deps = realloc(file->deps, (file->ndeps + 1) * sizeof(*newdeps),
M_LINKER, M_WAITOK | M_ZERO);
- if (newdeps == NULL)
- return (ENOMEM);
-
- if (file->deps) {
- bcopy(file->deps, newdeps,
- file->ndeps * sizeof(linker_file_t *));
- free(file->deps, M_LINKER);
- }
- file->deps = newdeps;
file->deps[file->ndeps] = dep;
file->ndeps++;
KLD_DPF(FILE, ("linker_file_add_dependency:"
@@ -761,15 +769,9 @@ int
linker_file_lookup_set(linker_file_t file, const char *name,
void *firstp, void *lastp, int *countp)
{
- int error, locked;
- locked = KLD_LOCKED();
- if (!locked)
- KLD_LOCK();
- error = LINKER_LOOKUP_SET(file, name, firstp, lastp, countp);
- if (!locked)
- KLD_UNLOCK();
- return (error);
+ sx_assert(&kld_sx, SA_LOCKED);
+ return (LINKER_LOOKUP_SET(file, name, firstp, lastp, countp));
}
/*
@@ -788,12 +790,12 @@ linker_file_lookup_symbol(linker_file_t file, const char *name, int deps)
caddr_t sym;
int locked;
- locked = KLD_LOCKED();
+ locked = sx_xlocked(&kld_sx);
if (!locked)
- KLD_LOCK();
+ sx_xlock(&kld_sx);
sym = linker_file_lookup_symbol_internal(file, name, deps);
if (!locked)
- KLD_UNLOCK();
+ sx_xunlock(&kld_sx);
return (sym);
}
@@ -807,7 +809,7 @@ linker_file_lookup_symbol_internal(linker_file_t file, const char *name,
size_t common_size = 0;
int i;
- KLD_LOCK_ASSERT();
+ sx_assert(&kld_sx, SA_XLOCKED);
KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%p, name=%s, deps=%d\n",
file, name, deps));
@@ -1007,9 +1009,9 @@ linker_search_symbol_name(caddr_t value, char *buf, u_int buflen,
{
int error;
- KLD_LOCK();
+ sx_xlock(&kld_sx);
error = linker_debug_search_symbol_name(value, buf, buflen, offset);
- KLD_UNLOCK();
+ sx_xunlock(&kld_sx);
return (error);
}
@@ -1019,9 +1021,6 @@ linker_search_symbol_name(caddr_t value, char *buf, u_int buflen,
int
kern_kldload(struct thread *td, const char *file, int *fileid)
{
-#ifdef HWPMC_HOOKS
- struct pmckern_map_in pkm;
-#endif
const char *kldname, *modname;
linker_file_t lf;
int error;
@@ -1051,24 +1050,16 @@ kern_kldload(struct thread *td, const char *file, int *fileid)
modname = file;
}
- KLD_LOCK();
+ sx_xlock(&kld_sx);
error = linker_load_module(kldname, modname, NULL, NULL, &lf);
if (error) {
- KLD_UNLOCK();
+ sx_xunlock(&kld_sx);
goto done;
}
lf->userrefs++;
if (fileid != NULL)
*fileid = lf->id;
-#ifdef HWPMC_HOOKS
- KLD_DOWNGRADE();
- pkm.pm_file = lf->filename;
- pkm.pm_address = (uintptr_t) lf->address;
- PMC_CALL_HOOK(td, PMC_FN_KLD_LOAD, (void *) &pkm);
- KLD_UNLOCK_READ();
-#else
- KLD_UNLOCK();
-#endif
+ sx_xunlock(&kld_sx);
done:
CURVNET_RESTORE();
@@ -1097,9 +1088,6 @@ sys_kldload(struct thread *td, struct kldload_args *uap)
int
kern_kldunload(struct thread *td, int fileid, int flags)
{
-#ifdef HWPMC_HOOKS
- struct pmckern_map_out pkm;
-#endif
linker_file_t lf;
int error = 0;
@@ -1110,17 +1098,12 @@ kern_kldunload(struct thread *td, int fileid, int flags)
return (error);
CURVNET_SET(TD_TO_VNET(td));
- KLD_LOCK();
+ sx_xlock(&kld_sx);
lf = linker_find_file_by_id(fileid);
if (lf) {
KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs));
- /* Check if there are DTrace probes enabled on this file. */
- if (lf->nenabled > 0) {
- printf("kldunload: attempt to unload file that has"
- " DTrace probes enabled\n");
- error = EBUSY;
- } else if (lf->userrefs == 0) {
+ if (lf->userrefs == 0) {
/*
* XXX: maybe LINKER_UNLOAD_FORCE should override ?
*/
@@ -1128,11 +1111,6 @@ kern_kldunload(struct thread *td, int fileid, int flags)
" loaded by the kernel\n");
error = EBUSY;
} else {
-#ifdef HWPMC_HOOKS
- /* Save data needed by hwpmc(4) before unloading. */
- pkm.pm_address = (uintptr_t) lf->address;
- pkm.pm_size = lf->size;
-#endif
lf->userrefs--;
error = linker_file_unload(lf, flags);
if (error)
@@ -1140,17 +1118,8 @@ kern_kldunload(struct thread *td, int fileid, int flags)
}
} else
error = ENOENT;
+ sx_xunlock(&kld_sx);
-#ifdef HWPMC_HOOKS
- if (error == 0) {
- KLD_DOWNGRADE();
- PMC_CALL_HOOK(td, PMC_FN_KLD_UNLOAD, (void *) &pkm);
- KLD_UNLOCK_READ();
- } else
- KLD_UNLOCK();
-#else
- KLD_UNLOCK();
-#endif
CURVNET_RESTORE();
return (error);
}
@@ -1193,13 +1162,13 @@ sys_kldfind(struct thread *td, struct kldfind_args *uap)
goto out;
filename = linker_basename(pathname);
- KLD_LOCK();
+ sx_xlock(&kld_sx);
lf = linker_find_file_by_name(filename);
if (lf)
td->td_retval[0] = lf->id;
else
error = ENOENT;
- KLD_UNLOCK();
+ sx_xunlock(&kld_sx);
out:
free(pathname, M_TEMP);
return (error);
@@ -1217,7 +1186,7 @@ sys_kldnext(struct thread *td, struct kldnext_args *uap)
return (error);
#endif
- KLD_LOCK();
+ sx_xlock(&kld_sx);
if (uap->fileid == 0)
lf = TAILQ_FIRST(&linker_files);
else {
@@ -1238,7 +1207,7 @@ sys_kldnext(struct thread *td, struct kldnext_args *uap)
else
td->td_retval[0] = 0;
out:
- KLD_UNLOCK();
+ sx_xunlock(&kld_sx);
return (error);
}
@@ -1277,10 +1246,10 @@ kern_kldstat(struct thread *td, int fileid, struct kld_file_stat *stat)
return (error);
#endif
- KLD_LOCK();
+ sx_xlock(&kld_sx);
lf = linker_find_file_by_id(fileid);
if (lf == NULL) {
- KLD_UNLOCK();
+ sx_xunlock(&kld_sx);
return (ENOENT);
}
@@ -1298,7 +1267,7 @@ kern_kldstat(struct thread *td, int fileid, struct kld_file_stat *stat)
if (namelen > MAXPATHLEN)
namelen = MAXPATHLEN;
bcopy(lf->pathname, &stat->pathname[0], namelen);
- KLD_UNLOCK();
+ sx_xunlock(&kld_sx);
td->td_retval[0] = 0;
return (0);
@@ -1317,7 +1286,7 @@ sys_kldfirstmod(struct thread *td, struct kldfirstmod_args *uap)
return (error);
#endif
- KLD_LOCK();
+ sx_xlock(&kld_sx);
lf = linker_find_file_by_id(uap->fileid);
if (lf) {
MOD_SLOCK;
@@ -1329,7 +1298,7 @@ sys_kldfirstmod(struct thread *td, struct kldfirstmod_args *uap)
MOD_SUNLOCK;
} else
error = ENOENT;
- KLD_UNLOCK();
+ sx_xunlock(&kld_sx);
return (error);
}
@@ -1357,7 +1326,7 @@ sys_kldsym(struct thread *td, struct kldsym_args *uap)
symstr = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
if ((error = copyinstr(lookup.symname, symstr, MAXPATHLEN, NULL)) != 0)
goto out;
- KLD_LOCK();
+ sx_xlock(&kld_sx);
if (uap->fileid != 0) {
lf = linker_find_file_by_id(uap->fileid);
if (lf == NULL)
@@ -1383,7 +1352,7 @@ sys_kldsym(struct thread *td, struct kldsym_args *uap)
if (lf == NULL)
error = ENOENT;
}
- KLD_UNLOCK();
+ sx_xunlock(&kld_sx);
out:
free(symstr, M_TEMP);
return (error);
@@ -1492,6 +1461,7 @@ linker_preload(void *arg)
error = 0;
modptr = NULL;
+ sx_xlock(&kld_sx);
while ((modptr = preload_search_next_name(modptr)) != NULL) {
modname = (char *)preload_search_info(modptr, MODINFO_NAME);
modtype = (char *)preload_search_info(modptr, MODINFO_TYPE);
@@ -1673,6 +1643,7 @@ fail:
TAILQ_REMOVE(&depended_files, lf, loaded);
linker_file_unload(lf, LINKER_UNLOAD_FORCE);
}
+ sx_xunlock(&kld_sx);
/* woohoo! we made it! */
}
@@ -1978,7 +1949,7 @@ linker_hwpmc_list_objects(void)
int i, nmappings;
nmappings = 0;
- KLD_LOCK_READ();
+ sx_slock(&kld_sx);
TAILQ_FOREACH(lf, &linker_files, link)
nmappings++;
@@ -1993,7 +1964,7 @@ linker_hwpmc_list_objects(void)
kobase[i].pm_address = (uintptr_t)lf->address;
i++;
}
- KLD_UNLOCK_READ();
+ sx_sunlock(&kld_sx);
KASSERT(i > 0, ("linker_hpwmc_list_objects: no kernel objects?"));
@@ -2019,7 +1990,7 @@ linker_load_module(const char *kldname, const char *modname,
char *pathname;
int error;
- KLD_LOCK_ASSERT();
+ sx_assert(&kld_sx, SA_XLOCKED);
if (modname == NULL) {
/*
* We have to load KLD
@@ -2093,7 +2064,7 @@ linker_load_dependencies(linker_file_t lf)
/*
* All files are dependant on /kernel.
*/
- KLD_LOCK_ASSERT();
+ sx_assert(&kld_sx, SA_XLOCKED);
if (linker_kernel_file) {
linker_kernel_file->refs++;
error = linker_file_add_dependency(lf, linker_kernel_file);
@@ -2185,16 +2156,16 @@ sysctl_kern_function_list(SYSCTL_HANDLER_ARGS)
error = sysctl_wire_old_buffer(req, 0);
if (error != 0)
return (error);
- KLD_LOCK();
+ sx_xlock(&kld_sx);
TAILQ_FOREACH(lf, &linker_files, link) {
error = LINKER_EACH_FUNCTION_NAME(lf,
sysctl_kern_function_list_iterate, req);
if (error) {
- KLD_UNLOCK();
+ sx_xunlock(&kld_sx);
return (error);
}
}
- KLD_UNLOCK();
+ sx_xunlock(&kld_sx);
return (SYSCTL_OUT(req, "", 1));
}