Module Name: src Committed By: pgoyette Date: Sat Aug 21 13:17:32 UTC 2010
Modified Files: src/share/man/man9: module.9 src/sys/conf: files src/sys/kern: init_main.c kern_module.c src/sys/sys: module.h param.h systm.h Added Files: src/sys/kern: kern_cfglock.c Log Message: Define a set of new kernel locking primitives to implement the recursive kernconfig_mutex. Update module subsystem to use this mutex rather than its own internal (non-recursive) mutex. Make module_autoload() do its own locking to be consistent with the rest of the module_xxx() calls. Update module(9) man page appropriately. As discussed on tech-kern over the last few weeks. Welcome to NetBSD 5.99.39 ! To generate a diff of this commit: cvs rdiff -u -r1.17 -r1.18 src/share/man/man9/module.9 cvs rdiff -u -r1.992 -r1.993 src/sys/conf/files cvs rdiff -u -r1.422 -r1.423 src/sys/kern/init_main.c cvs rdiff -u -r0 -r1.1 src/sys/kern/kern_cfglock.c cvs rdiff -u -r1.71 -r1.72 src/sys/kern/kern_module.c cvs rdiff -u -r1.24 -r1.25 src/sys/sys/module.h cvs rdiff -u -r1.373 -r1.374 src/sys/sys/param.h cvs rdiff -u -r1.239 -r1.240 src/sys/sys/systm.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/share/man/man9/module.9 diff -u src/share/man/man9/module.9:1.17 src/share/man/man9/module.9:1.18 --- src/share/man/man9/module.9:1.17 Wed Aug 18 01:56:45 2010 +++ src/share/man/man9/module.9 Sat Aug 21 13:17:32 2010 @@ -1,4 +1,4 @@ -.\" $NetBSD: module.9,v 1.17 2010/08/18 01:56:45 pgoyette Exp $ +.\" $NetBSD: module.9,v 1.18 2010/08/21 13:17:32 pgoyette Exp $ .\" .\" Copyright (c) 2010 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -27,7 +27,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd August 17, 2010 +.Dd August XXX, 2010 .Dt MODULE 9 .Os .Sh NAME @@ -86,6 +86,8 @@ .Vt modinfo_t type resides within the module itself, and contains module header info. .El +.Pp +The module subsystem is protected by the global kernconfig_mutex. .Sh FUNCTIONS .Bl -tag -width abcd .It Fn MODULE "class" "name" "required" @@ -362,21 +364,37 @@ Until this routine is called, modules can only be loaded if they were built-in to the kernel image or provided by the boot loader. .El -.Sh LOCK PROTOCOL -The -.Nm -subsystem is protected with the global -.Vt module_mutex . -This -.Xr mutex 9 -must be acquired before calling any of these routines. -As an exception, the +.Sh PROGRAMMING CONSIDERATIONS +The module subsystem is designed to be called recursively, but only within +a single lwp. +This permits one module's +.Fn modcmd +routine to load or unload other modules. +.Pp +A module is not permitted to load or unload itself. +Attempts to load or unload a module from within its own +.Fn modcmd +routine will fail with +.Er EEXIST +or +.Er EBUSY +respectively. +.Pp +Although a module can be loaded by either .Fn module_load -routine acquires the mutex itself, so one does not need to acquire it -before calling -.Fn module_load . -Loading of a module and its required modules occurs as an atomic -operation, and either completely succeeds or completely fails. +or +.Fn module_autoload +it is not possible for the module's +.Fn modcmd +routine to distinguish between the two methods. +Any module which needs to ensure that it does not get auto-unloaded must +either handle the +.Dv MODULE_CMD_AUTOUNLOAD +command in its +.Fn modcmd +routine, or use +.Fn module_hold +to increment its reference count. .Sh CODE REFERENCES This section describes places within the .Nx Index: src/sys/conf/files diff -u src/sys/conf/files:1.992 src/sys/conf/files:1.993 --- src/sys/conf/files:1.992 Wed Jul 7 01:09:39 2010 +++ src/sys/conf/files Sat Aug 21 13:17:32 2010 @@ -1,4 +1,4 @@ -# $NetBSD: files,v 1.992 2010/07/07 01:09:39 chs Exp $ +# $NetBSD: files,v 1.993 2010/08/21 13:17:32 pgoyette Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 version 20100430 @@ -1434,6 +1434,7 @@ file kern/init_sysent.c file kern/kern_acct.c file kern/kern_auth.c +file kern/kern_cfglock.c file kern/kern_clock.c file kern/kern_condvar.c file kern/kern_core.c coredump Index: src/sys/kern/init_main.c diff -u src/sys/kern/init_main.c:1.422 src/sys/kern/init_main.c:1.423 --- src/sys/kern/init_main.c:1.422 Sat Jun 26 07:23:57 2010 +++ src/sys/kern/init_main.c Sat Aug 21 13:17:31 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: init_main.c,v 1.422 2010/06/26 07:23:57 pgoyette Exp $ */ +/* $NetBSD: init_main.c,v 1.423 2010/08/21 13:17:31 pgoyette Exp $ */ /*- * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc. @@ -97,7 +97,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: init_main.c,v 1.422 2010/06/26 07:23:57 pgoyette Exp $"); +__KERNEL_RCSID(0, "$NetBSD: init_main.c,v 1.423 2010/08/21 13:17:31 pgoyette Exp $"); #include "opt_ddb.h" #include "opt_ipsec.h" @@ -303,6 +303,7 @@ kernel_lock_init(); once_init(); mutex_init(&cpu_lock, MUTEX_DEFAULT, IPL_NONE); + kernconfig_lock_init(); /* Initialize the device switch tables. */ devsw_init(); Index: src/sys/kern/kern_module.c diff -u src/sys/kern/kern_module.c:1.71 src/sys/kern/kern_module.c:1.72 --- src/sys/kern/kern_module.c:1.71 Wed Aug 11 12:04:49 2010 +++ src/sys/kern/kern_module.c Sat Aug 21 13:17:31 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_module.c,v 1.71 2010/08/11 12:04:49 pgoyette Exp $ */ +/* $NetBSD: kern_module.c,v 1.72 2010/08/21 13:17:31 pgoyette 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.71 2010/08/11 12:04:49 pgoyette Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_module.c,v 1.72 2010/08/21 13:17:31 pgoyette Exp $"); #define _MODULE_INTERNAL @@ -72,7 +72,6 @@ static int module_autoload_on = 1; u_int module_count; u_int module_builtinlist; -kmutex_t module_lock; u_int module_autotime = 10; u_int module_gen = 1; static kcondvar_t module_thread_cv; @@ -223,7 +222,7 @@ modp[i] = module_newmodule(MODULE_SOURCE_KERNEL); modp[i]->mod_info = mip[i+mipskip]; } - mutex_enter(&module_lock); + kernconfig_lock(); /* do this in three stages for error recovery and atomicity */ @@ -263,7 +262,7 @@ } out: - mutex_exit(&module_lock); + kernconfig_unlock(); if (rv != 0) { for (i = 0; i < nmodinfo; i++) { if (modp[i]) @@ -291,13 +290,13 @@ if (rv) return rv; - mutex_enter(&module_lock); + kernconfig_lock(); rv = module_do_unload(mi->mi_name, true); if (rv) { goto out; } } else { - mutex_enter(&module_lock); + kernconfig_lock(); } TAILQ_FOREACH(mod, &module_builtins, mod_chain) { if (strcmp(mod->mod_info->mi_name, mi->mi_name) == 0) @@ -312,7 +311,7 @@ } out: - mutex_exit(&module_lock); + kernconfig_unlock(); return rv; } @@ -332,7 +331,6 @@ if (module_map == NULL) { module_map = kernel_map; } - mutex_init(&module_lock, MUTEX_DEFAULT, IPL_NONE); cv_init(&module_thread_cv, "mod_unld"); mutex_init(&module_thread_lock, MUTEX_DEFAULT, IPL_NONE); @@ -388,11 +386,11 @@ { module_t *mod; - mutex_enter(&module_lock); + kernconfig_lock(); TAILQ_FOREACH(mod, &module_builtins, mod_chain) { module_require_force(mod); } - mutex_exit(&module_lock); + kernconfig_unlock(); } static struct sysctllog *module_sysctllog; @@ -444,7 +442,7 @@ module_t *mod; modinfo_t *mi; - mutex_enter(&module_lock); + kernconfig_lock(); /* * Builtins first. These will not depend on pre-loaded modules * (because the kernel would not link). @@ -493,7 +491,7 @@ TAILQ_INSERT_TAIL(&module_builtins, mod, mod_chain); } - mutex_exit(&module_lock); + kernconfig_unlock(); } /* @@ -534,10 +532,10 @@ return error; } - mutex_enter(&module_lock); + kernconfig_lock(); error = module_do_load(filename, false, flags, props, NULL, class, false); - mutex_exit(&module_lock); + kernconfig_unlock(); return error; } @@ -552,27 +550,31 @@ { int error; - KASSERT(mutex_owned(&module_lock)); + kernconfig_lock(); /* Nothing if the user has disabled it. */ if (!module_autoload_on) { + kernconfig_unlock(); return EPERM; } /* Disallow path separators and magic symlinks. */ if (strchr(filename, '/') != NULL || strchr(filename, '@') != NULL || strchr(filename, '.') != NULL) { + kernconfig_unlock(); return EPERM; } /* Authorize. */ error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE, 0, (void *)(uintptr_t)MODCTL_LOAD, (void *)(uintptr_t)1, NULL); - if (error != 0) { - return error; - } - return module_do_load(filename, false, 0, NULL, NULL, class, true); + if (error == 0) + error = module_do_load(filename, false, 0, NULL, NULL, class, + true); + + kernconfig_unlock(); + return error; } /* @@ -592,9 +594,9 @@ return error; } - mutex_enter(&module_lock); + kernconfig_lock(); error = module_do_unload(name, true); - mutex_exit(&module_lock); + kernconfig_unlock(); return error; } @@ -609,7 +611,7 @@ { module_t *mod; - KASSERT(mutex_owned(&module_lock)); + KASSERT(kernconfig_is_held()); TAILQ_FOREACH(mod, &module_list, mod_chain) { if (strcmp(mod->mod_info->mi_name, name) == 0) { @@ -632,14 +634,14 @@ { module_t *mod; - mutex_enter(&module_lock); + kernconfig_lock(); mod = module_lookup(name); if (mod == NULL) { - mutex_exit(&module_lock); + kernconfig_unlock(); return ENOENT; } mod->mod_refcnt++; - mutex_exit(&module_lock); + kernconfig_unlock(); return 0; } @@ -654,14 +656,14 @@ { module_t *mod; - mutex_enter(&module_lock); + kernconfig_lock(); mod = module_lookup(name); if (mod == NULL) { - mutex_exit(&module_lock); + kernconfig_unlock(); panic("module_rele: gone"); } mod->mod_refcnt--; - mutex_exit(&module_lock); + kernconfig_unlock(); } /* @@ -674,7 +676,7 @@ { int i; - KASSERT(mutex_owned(&module_lock)); + KASSERT(kernconfig_is_held()); /* * If there are requisite modules, put at the head of the queue. @@ -708,11 +710,11 @@ const char *p, *s; char buf[MAXMODNAME]; modinfo_t *mi = NULL; - module_t *mod, *mod2, *mod_loaded; + module_t *mod, *mod2, *mod_loaded, *prev_active; size_t len; int error; - KASSERT(mutex_owned(&module_lock)); + KASSERT(kernconfig_is_held()); /* * Search the list to see if we have a module by this name. @@ -775,10 +777,10 @@ /* * Try to initialize the module. */ - KASSERT(module_active == NULL); + prev_active = module_active; module_active = mod; error = (*mi->mi_modcmd)(MODULE_CMD_INIT, NULL); - module_active = NULL; + module_active = prev_active; if (error != 0) { module_error("builtin module `%s' " "failed to init", mi->mi_name); @@ -809,18 +811,22 @@ prop_dictionary_t props, module_t **modp, modclass_t class, bool autoload) { - static TAILQ_HEAD(,module) pending = TAILQ_HEAD_INITIALIZER(pending); - static int depth; - const int maxdepth = 6; +#define MODULE_MAX_DEPTH 6 + + TAILQ_HEAD(pending_t, module); + static int depth = 0; + static struct pending_t *pending_lists[MODULE_MAX_DEPTH]; + struct pending_t *pending; + struct pending_t new_pending = TAILQ_HEAD_INITIALIZER(new_pending); modinfo_t *mi; - module_t *mod, *mod2; + module_t *mod, *mod2, *prev_active; prop_dictionary_t filedict; char buf[MAXMODNAME]; const char *s, *p; int error; size_t len; - KASSERT(mutex_owned(&module_lock)); + KASSERT(kernconfig_is_held()); filedict = NULL; error = 0; @@ -828,13 +834,26 @@ /* * Avoid recursing too far. */ - if (++depth > maxdepth) { - module_error("too many required modules"); + if (++depth > MODULE_MAX_DEPTH) { + module_error("recursion too deep"); depth--; return EMLINK; } /* + * Set up the pending list for this depth. If this is a + * recursive entry, then use same list as for outer call, + * else use the locally allocated list. In either case, + * remember which one we're using. + */ + if (isdep) { + KASSERT(depth > 1); + pending = pending_lists[depth - 2]; + } else + pending = &new_pending; + pending_lists[depth - 1] = pending; + + /* * Search the list of disabled builtins first. */ TAILQ_FOREACH(mod, &module_builtins, mod_chain) { @@ -869,18 +888,14 @@ } } if (mod != NULL) { - TAILQ_INSERT_TAIL(&pending, mod, mod_chain); + TAILQ_INSERT_TAIL(pending, mod, mod_chain); } else { /* * If a requisite module, check to see if it is * already present. */ if (isdep) { - TAILQ_FOREACH(mod, &module_list, mod_chain) { - if (strcmp(mod->mod_info->mi_name, name) == 0) { - break; - } - } + mod = module_lookup(name); if (mod != NULL) { if (modp != NULL) { *modp = mod; @@ -903,7 +918,7 @@ depth--; return error; } - TAILQ_INSERT_TAIL(&pending, mod, mod_chain); + TAILQ_INSERT_TAIL(pending, mod, mod_chain); error = module_fetch_info(mod); if (error != 0) { @@ -971,7 +986,7 @@ /* * Block circular dependencies. */ - TAILQ_FOREACH(mod2, &pending, mod_chain) { + TAILQ_FOREACH(mod2, pending, mod_chain) { if (mod == mod2) { continue; } @@ -1041,10 +1056,10 @@ goto fail; } } - KASSERT(module_active == NULL); + prev_active = module_active; module_active = mod; error = (*mi->mi_modcmd)(MODULE_CMD_INIT, filedict ? filedict : props); - module_active = NULL; + module_active = prev_active; if (filedict) { prop_object_release(filedict); filedict = NULL; @@ -1062,7 +1077,7 @@ * Good, the module loaded successfully. Put it onto the * list and add references to its requisite modules. */ - TAILQ_REMOVE(&pending, mod, mod_chain); + TAILQ_REMOVE(pending, mod, mod_chain); module_enqueue(mod); if (modp != NULL) { *modp = mod; @@ -1085,7 +1100,7 @@ prop_object_release(filedict); filedict = NULL; } - TAILQ_REMOVE(&pending, mod, mod_chain); + TAILQ_REMOVE(pending, mod, mod_chain); kmem_free(mod, sizeof(*mod)); depth--; return error; @@ -1099,11 +1114,11 @@ static int module_do_unload(const char *name, bool load_requires_force) { - module_t *mod; + module_t *mod, *prev_active; int error; u_int i; - KASSERT(mutex_owned(&module_lock)); + KASSERT(kernconfig_is_held()); mod = module_lookup(name); if (mod == NULL) { @@ -1114,10 +1129,10 @@ module_print("module `%s' busy", name); return EBUSY; } - KASSERT(module_active == NULL); + prev_active = module_active; module_active = mod; error = (*mod->mod_info->mi_modcmd)(MODULE_CMD_FINI, NULL); - module_active = NULL; + module_active = prev_active; if (error != 0) { module_print("cannot unload module `%s' error=%d", name, error); @@ -1223,7 +1238,7 @@ module_find_section(const char *name, void **addr, size_t *size) { - KASSERT(mutex_owned(&module_lock)); + KASSERT(kernconfig_is_held()); KASSERT(module_active != NULL); return kobj_find_section(module_active->mod_kobj, name, addr, size); @@ -1244,7 +1259,7 @@ int error; for (;;) { - mutex_enter(&module_lock); + kernconfig_lock(); for (mod = TAILQ_FIRST(&module_list); mod != NULL; mod = next) { next = TAILQ_NEXT(mod, mod_chain); if (mod->mod_source == MODULE_SOURCE_KERNEL) @@ -1271,7 +1286,7 @@ (void)module_do_unload(mi->mi_name, false); } } - mutex_exit(&module_lock); + kernconfig_unlock(); mutex_enter(&module_thread_lock); (void)cv_timedwait(&module_thread_cv, &module_thread_lock, Index: src/sys/sys/module.h diff -u src/sys/sys/module.h:1.24 src/sys/sys/module.h:1.25 --- src/sys/sys/module.h:1.24 Sat Jun 26 07:23:57 2010 +++ src/sys/sys/module.h Sat Aug 21 13:17:32 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: module.h,v 1.24 2010/06/26 07:23:57 pgoyette Exp $ */ +/* $NetBSD: module.h,v 1.25 2010/08/21 13:17:32 pgoyette Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -115,7 +115,6 @@ TAILQ_HEAD(modlist, module); extern struct vm_map *module_map; -extern kmutex_t module_lock; extern u_int module_count; extern u_int module_builtinlist; extern struct modlist module_list; Index: src/sys/sys/param.h diff -u src/sys/sys/param.h:1.373 src/sys/sys/param.h:1.374 --- src/sys/sys/param.h:1.373 Wed Jul 28 11:03:47 2010 +++ src/sys/sys/param.h Sat Aug 21 13:17:32 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: param.h,v 1.373 2010/07/28 11:03:47 hannken Exp $ */ +/* $NetBSD: param.h,v 1.374 2010/08/21 13:17:32 pgoyette Exp $ */ /*- * Copyright (c) 1982, 1986, 1989, 1993 @@ -63,7 +63,7 @@ * 2.99.9 (299000900) */ -#define __NetBSD_Version__ 599003800 /* NetBSD 5.99.38 */ +#define __NetBSD_Version__ 599003900 /* NetBSD 5.99.39 */ #define __NetBSD_Prereq__(M,m,p) (((((M) * 100000000) + \ (m) * 1000000) + (p) * 100) <= __NetBSD_Version__) Index: src/sys/sys/systm.h diff -u src/sys/sys/systm.h:1.239 src/sys/sys/systm.h:1.240 --- src/sys/sys/systm.h:1.239 Sun Jan 31 02:04:43 2010 +++ src/sys/sys/systm.h Sat Aug 21 13:17:32 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: systm.h,v 1.239 2010/01/31 02:04:43 pooka Exp $ */ +/* $NetBSD: systm.h,v 1.240 2010/08/21 13:17:32 pgoyette Exp $ */ /*- * Copyright (c) 1982, 1988, 1991, 1993 @@ -492,6 +492,11 @@ void _kernel_lock(int); void _kernel_unlock(int, int *); +void kernconfig_lock_init(void); +void kernconfig_lock(void); +void kernconfig_unlock(void); +bool kernconfig_is_held(void); + #if defined(MULTIPROCESSOR) || defined(_MODULE) #define KERNEL_LOCK(count, lwp) \ do { \ Added files: Index: src/sys/kern/kern_cfglock.c diff -u /dev/null src/sys/kern/kern_cfglock.c:1.1 --- /dev/null Sat Aug 21 13:17:33 2010 +++ src/sys/kern/kern_cfglock.c Sat Aug 21 13:17:31 2010 @@ -0,0 +1,101 @@ +/* $NetBSD: kern_cfglock.c,v 1.1 2010/08/21 13:17:31 pgoyette Exp $ */ + +/*- + * Copyright (c) 2002, 2006, 2007, 2008, 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center, and by Andrew Doran. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: kern_cfglock.c,v 1.1 2010/08/21 13:17:31 pgoyette Exp $"); + +#include <sys/param.h> +#include <sys/cpu.h> +#include <sys/mutex.h> +#include <sys/lwp.h> +#include <sys/systm.h> + +static kmutex_t kernconfig_mutex; +static lwp_t *kernconfig_lwp; +static int kernconfig_recurse; + +/* + * Functions for manipulating the kernel configuration lock. This + * recursive lock should be used to protect all additions and removals + * of kernel functionality, such as device configuration and loading + * of modular kernel components. + */ + +void +kernconfig_lock_init(void) +{ + + mutex_init(&kernconfig_mutex, MUTEX_DEFAULT, IPL_NONE); + kernconfig_lwp = NULL; + kernconfig_recurse = 0; +} + +void +kernconfig_lock(void) +{ + lwp_t *my_lwp; + + /* + * It's OK to check this unlocked, since it could only be set to + * curlwp by the current thread itself, and not by an interrupt + * or any other LWP. + */ + KASSERT(!cpu_intr_p()); + my_lwp = curlwp; + if (kernconfig_lwp == my_lwp) { + kernconfig_recurse++; + KASSERT(kernconfig_recurse > 1); + } else { + mutex_enter(&kernconfig_mutex); + kernconfig_lwp = my_lwp; + kernconfig_recurse = 1; + } +} + +void +kernconfig_unlock(void) +{ + + KASSERT(kernconfig_is_held()); + KASSERT(kernconfig_recurse != 0); + if (--kernconfig_recurse == 0) { + kernconfig_lwp = NULL; + mutex_exit(&kernconfig_mutex); + } +} + +bool +kernconfig_is_held(void) +{ + + return mutex_owned(&kernconfig_mutex); +}