aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorConrad Meyer <cem@FreeBSD.org>2016-10-13 02:06:23 +0000
committerConrad Meyer <cem@FreeBSD.org>2016-10-13 02:06:23 +0000
commitd9ce8a41eac90854475a55df01befcbeaf1e9eb1 (patch)
tree675f2f40358bd6df3012d617879e6479ba943731
parentd119e0f7fb7c3f1fc6a244f542339a1c36433f11 (diff)
kern_linker: Handle module-loading failures in preloaded .ko files
The runtime kernel loader, linker_load_file, unloads kernel files that failed to load all of their modules. For consistency, treat preloaded (loader.conf loaded) kernel files in the same way. Reviewed by: kib Sponsored by: Dell EMC Isilon Differential Revision: https://reviews.freebsd.org/D8200
Notes
svn path=/head/; revision=307163
-rw-r--r--sys/kern/kern_linker.c38
-rw-r--r--sys/sys/linker.h1
2 files changed, 38 insertions, 1 deletions
diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c
index 4758cdc2072a..1bd3ad077fdd 100644
--- a/sys/kern/kern_linker.c
+++ b/sys/kern/kern_linker.c
@@ -1599,7 +1599,6 @@ restart:
if (error)
panic("cannot add dependency");
}
- lf->userrefs++; /* so we can (try to) kldunload it */
error = linker_file_lookup_set(lf, MDT_SETNAME, &start,
&stop, NULL);
if (!error) {
@@ -1637,6 +1636,8 @@ restart:
goto fail;
}
linker_file_register_modules(lf);
+ if (!TAILQ_EMPTY(&lf->modules))
+ lf->flags |= LINKER_FILE_MODULES;
if (linker_file_lookup_set(lf, "sysinit_set", &si_start,
&si_stop, NULL) == 0)
sysinit_add(si_start, si_stop);
@@ -1654,6 +1655,41 @@ fail:
SYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, 0);
/*
+ * Handle preload files that failed to load any modules.
+ */
+static void
+linker_preload_finish(void *arg)
+{
+ linker_file_t lf, nlf;
+
+ sx_xlock(&kld_sx);
+ TAILQ_FOREACH_SAFE(lf, &linker_files, link, nlf) {
+ /*
+ * If all of the modules in this file failed to load, unload
+ * the file and return an error of ENOEXEC. (Parity with
+ * linker_load_file.)
+ */
+ if ((lf->flags & LINKER_FILE_MODULES) != 0 &&
+ TAILQ_EMPTY(&lf->modules)) {
+ linker_file_unload(lf, LINKER_UNLOAD_FORCE);
+ continue;
+ }
+
+ lf->flags &= ~LINKER_FILE_MODULES;
+ lf->userrefs++; /* so we can (try to) kldunload it */
+ }
+ sx_xunlock(&kld_sx);
+}
+
+/*
+ * Attempt to run after all DECLARE_MODULE SYSINITs. Unfortunately they can be
+ * scheduled at any subsystem and order, so run this as late as possible. init
+ * becomes runnable in SI_SUB_KTHREAD_INIT, so go slightly before that.
+ */
+SYSINIT(preload_finish, SI_SUB_KTHREAD_INIT - 100, SI_ORDER_MIDDLE,
+ linker_preload_finish, 0);
+
+/*
* Search for a not-loaded module by name.
*
* Modules may be found in the following locations:
diff --git a/sys/sys/linker.h b/sys/sys/linker.h
index 42253a7758ab..e650967cecb1 100644
--- a/sys/sys/linker.h
+++ b/sys/sys/linker.h
@@ -73,6 +73,7 @@ struct linker_file {
int userrefs; /* kldload(2) count */
int flags;
#define LINKER_FILE_LINKED 0x1 /* file has been fully linked */
+#define LINKER_FILE_MODULES 0x2 /* file has >0 modules at preload */
TAILQ_ENTRY(linker_file) link; /* list of all loaded files */
char* filename; /* file which was loaded */
char* pathname; /* file name with full path */