1a8d61afdSLawrence Stewart /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 38a36da99SPedro F. Giffuni * 4b1f53277SLawrence Stewart * Copyright (c) 2010,2013 Lawrence Stewart <lstewart@freebsd.org> 5a8d61afdSLawrence Stewart * Copyright (c) 2010 The FreeBSD Foundation 6a8d61afdSLawrence Stewart * All rights reserved. 7a8d61afdSLawrence Stewart * 8a8d61afdSLawrence Stewart * This software was developed by Lawrence Stewart while studying at the Centre 9891b8ed4SLawrence Stewart * for Advanced Internet Architectures, Swinburne University of Technology, 10891b8ed4SLawrence Stewart * made possible in part by grants from the FreeBSD Foundation and Cisco 11891b8ed4SLawrence Stewart * University Research Program Fund at Community Foundation Silicon Valley. 12a8d61afdSLawrence Stewart * 13a8d61afdSLawrence Stewart * Portions of this software were developed at the Centre for Advanced 14a8d61afdSLawrence Stewart * Internet Architectures, Swinburne University of Technology, Melbourne, 15a8d61afdSLawrence Stewart * Australia by Lawrence Stewart under sponsorship from the FreeBSD Foundation. 16a8d61afdSLawrence Stewart * 17a8d61afdSLawrence Stewart * Redistribution and use in source and binary forms, with or without 18a8d61afdSLawrence Stewart * modification, are permitted provided that the following conditions 19a8d61afdSLawrence Stewart * are met: 20a8d61afdSLawrence Stewart * 1. Redistributions of source code must retain the above copyright 21a8d61afdSLawrence Stewart * notice, this list of conditions and the following disclaimer. 22a8d61afdSLawrence Stewart * 2. Redistributions in binary form must reproduce the above copyright 23a8d61afdSLawrence Stewart * notice, this list of conditions and the following disclaimer in the 24a8d61afdSLawrence Stewart * documentation and/or other materials provided with the distribution. 25a8d61afdSLawrence Stewart * 26a8d61afdSLawrence Stewart * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 27a8d61afdSLawrence Stewart * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28a8d61afdSLawrence Stewart * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29a8d61afdSLawrence Stewart * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 30a8d61afdSLawrence Stewart * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31a8d61afdSLawrence Stewart * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32a8d61afdSLawrence Stewart * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33a8d61afdSLawrence Stewart * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34a8d61afdSLawrence Stewart * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35a8d61afdSLawrence Stewart * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36a8d61afdSLawrence Stewart * SUCH DAMAGE. 37a8d61afdSLawrence Stewart */ 38a8d61afdSLawrence Stewart 39a8d61afdSLawrence Stewart #include <sys/param.h> 40a8d61afdSLawrence Stewart #include <sys/kernel.h> 41a8d61afdSLawrence Stewart #include <sys/hhook.h> 42a8d61afdSLawrence Stewart #include <sys/khelp.h> 43a8d61afdSLawrence Stewart #include <sys/lock.h> 44a8d61afdSLawrence Stewart #include <sys/malloc.h> 45a8d61afdSLawrence Stewart #include <sys/module.h> 46a8d61afdSLawrence Stewart #include <sys/module_khelp.h> 47a8d61afdSLawrence Stewart #include <sys/osd.h> 48a8d61afdSLawrence Stewart #include <sys/queue.h> 49a8d61afdSLawrence Stewart #include <sys/refcount.h> 50a8d61afdSLawrence Stewart #include <sys/rwlock.h> 51a8d61afdSLawrence Stewart #include <sys/systm.h> 52a8d61afdSLawrence Stewart 53a8d61afdSLawrence Stewart static struct rwlock khelp_list_lock; 54a8d61afdSLawrence Stewart RW_SYSINIT(khelplistlock, &khelp_list_lock, "helper list lock"); 55a8d61afdSLawrence Stewart 56a8d61afdSLawrence Stewart static TAILQ_HEAD(helper_head, helper) helpers = TAILQ_HEAD_INITIALIZER(helpers); 57a8d61afdSLawrence Stewart 58a8d61afdSLawrence Stewart /* Private function prototypes. */ 59a8d61afdSLawrence Stewart static inline void khelp_remove_osd(struct helper *h, struct osd *hosd); 6058261d30SLawrence Stewart void khelp_new_hhook_registered(struct hhook_head *hhh, uint32_t flags); 61a8d61afdSLawrence Stewart 62a8d61afdSLawrence Stewart #define KHELP_LIST_WLOCK() rw_wlock(&khelp_list_lock) 63a8d61afdSLawrence Stewart #define KHELP_LIST_WUNLOCK() rw_wunlock(&khelp_list_lock) 64a8d61afdSLawrence Stewart #define KHELP_LIST_RLOCK() rw_rlock(&khelp_list_lock) 65a8d61afdSLawrence Stewart #define KHELP_LIST_RUNLOCK() rw_runlock(&khelp_list_lock) 66a8d61afdSLawrence Stewart #define KHELP_LIST_LOCK_ASSERT() rw_assert(&khelp_list_lock, RA_LOCKED) 67a8d61afdSLawrence Stewart 68a8d61afdSLawrence Stewart int 69a8d61afdSLawrence Stewart khelp_register_helper(struct helper *h) 70a8d61afdSLawrence Stewart { 71a8d61afdSLawrence Stewart struct helper *tmph; 72a8d61afdSLawrence Stewart int error, i, inserted; 73a8d61afdSLawrence Stewart 74933a8bffSLawrence Stewart error = inserted = 0; 75a8d61afdSLawrence Stewart refcount_init(&h->h_refcount, 0); 76a8d61afdSLawrence Stewart h->h_id = osd_register(OSD_KHELP, NULL, NULL); 77a8d61afdSLawrence Stewart 78a8d61afdSLawrence Stewart /* It's only safe to add the hooks after osd_register(). */ 79a8d61afdSLawrence Stewart for (i = 0; i < h->h_nhooks && !error; i++) { 80a8d61afdSLawrence Stewart /* We don't require the module to assign hook_helper. */ 81a8d61afdSLawrence Stewart h->h_hooks[i].hook_helper = h; 82933a8bffSLawrence Stewart error = hhook_add_hook_lookup(&h->h_hooks[i], HHOOK_WAITOK); 83933a8bffSLawrence Stewart if (error) 84933a8bffSLawrence Stewart printf("%s: \"%s\" khelp module unable to " 85933a8bffSLawrence Stewart "hook type %d id %d due to error %d\n", __func__, 86933a8bffSLawrence Stewart h->h_name, h->h_hooks[i].hook_type, 87933a8bffSLawrence Stewart h->h_hooks[i].hook_id, error); 88a8d61afdSLawrence Stewart } 89a8d61afdSLawrence Stewart 90a8d61afdSLawrence Stewart if (error) { 91a8d61afdSLawrence Stewart for (i--; i >= 0; i--) 92b1f53277SLawrence Stewart hhook_remove_hook_lookup(&h->h_hooks[i]); 93a8d61afdSLawrence Stewart osd_deregister(OSD_KHELP, h->h_id); 94933a8bffSLawrence Stewart } else { 95a8d61afdSLawrence Stewart KHELP_LIST_WLOCK(); 96a8d61afdSLawrence Stewart /* 97a8d61afdSLawrence Stewart * Keep list of helpers sorted in descending h_id order. Due to 98a8d61afdSLawrence Stewart * the way osd_set() works, a sorted list ensures 99933a8bffSLawrence Stewart * khelp_init_osd() will operate with improved efficiency. 100a8d61afdSLawrence Stewart */ 101a8d61afdSLawrence Stewart TAILQ_FOREACH(tmph, &helpers, h_next) { 102a8d61afdSLawrence Stewart if (tmph->h_id < h->h_id) { 103a8d61afdSLawrence Stewart TAILQ_INSERT_BEFORE(tmph, h, h_next); 104a8d61afdSLawrence Stewart inserted = 1; 105a8d61afdSLawrence Stewart break; 106a8d61afdSLawrence Stewart } 107a8d61afdSLawrence Stewart } 108a8d61afdSLawrence Stewart 109a8d61afdSLawrence Stewart if (!inserted) 110a8d61afdSLawrence Stewart TAILQ_INSERT_TAIL(&helpers, h, h_next); 111a8d61afdSLawrence Stewart KHELP_LIST_WUNLOCK(); 112a8d61afdSLawrence Stewart } 113a8d61afdSLawrence Stewart 114a8d61afdSLawrence Stewart return (error); 115a8d61afdSLawrence Stewart } 116a8d61afdSLawrence Stewart 117a8d61afdSLawrence Stewart int 118a8d61afdSLawrence Stewart khelp_deregister_helper(struct helper *h) 119a8d61afdSLawrence Stewart { 120a8d61afdSLawrence Stewart struct helper *tmph; 121a8d61afdSLawrence Stewart int error, i; 122a8d61afdSLawrence Stewart 123a8d61afdSLawrence Stewart KHELP_LIST_WLOCK(); 124a8d61afdSLawrence Stewart if (h->h_refcount > 0) 125a8d61afdSLawrence Stewart error = EBUSY; 126a8d61afdSLawrence Stewart else { 127a8d61afdSLawrence Stewart error = ENOENT; 128a8d61afdSLawrence Stewart TAILQ_FOREACH(tmph, &helpers, h_next) { 129a8d61afdSLawrence Stewart if (tmph == h) { 130a8d61afdSLawrence Stewart TAILQ_REMOVE(&helpers, h, h_next); 131a8d61afdSLawrence Stewart error = 0; 132a8d61afdSLawrence Stewart break; 133a8d61afdSLawrence Stewart } 134a8d61afdSLawrence Stewart } 135a8d61afdSLawrence Stewart } 136a8d61afdSLawrence Stewart KHELP_LIST_WUNLOCK(); 137a8d61afdSLawrence Stewart 138a8d61afdSLawrence Stewart if (!error) { 139a8d61afdSLawrence Stewart for (i = 0; i < h->h_nhooks; i++) 140b1f53277SLawrence Stewart hhook_remove_hook_lookup(&h->h_hooks[i]); 141a8d61afdSLawrence Stewart osd_deregister(OSD_KHELP, h->h_id); 142a8d61afdSLawrence Stewart } 143a8d61afdSLawrence Stewart 144a8d61afdSLawrence Stewart return (error); 145a8d61afdSLawrence Stewart } 146a8d61afdSLawrence Stewart 147a8d61afdSLawrence Stewart int 148a8d61afdSLawrence Stewart khelp_init_osd(uint32_t classes, struct osd *hosd) 149a8d61afdSLawrence Stewart { 150a8d61afdSLawrence Stewart struct helper *h; 151a8d61afdSLawrence Stewart void *hdata; 152a8d61afdSLawrence Stewart int error; 153a8d61afdSLawrence Stewart 154a8d61afdSLawrence Stewart KASSERT(hosd != NULL, ("struct osd not initialised!")); 155a8d61afdSLawrence Stewart 156a8d61afdSLawrence Stewart error = 0; 157a8d61afdSLawrence Stewart 158a8d61afdSLawrence Stewart KHELP_LIST_RLOCK(); 159a8d61afdSLawrence Stewart TAILQ_FOREACH(h, &helpers, h_next) { 160a8d61afdSLawrence Stewart /* If helper is correct class and needs to store OSD... */ 161a8d61afdSLawrence Stewart if (h->h_classes & classes && h->h_flags & HELPER_NEEDS_OSD) { 162a8d61afdSLawrence Stewart hdata = uma_zalloc(h->h_zone, M_NOWAIT); 163a8d61afdSLawrence Stewart if (hdata == NULL) { 164a8d61afdSLawrence Stewart error = ENOMEM; 165a8d61afdSLawrence Stewart break; 166a8d61afdSLawrence Stewart } 167a8d61afdSLawrence Stewart osd_set(OSD_KHELP, hosd, h->h_id, hdata); 168a8d61afdSLawrence Stewart refcount_acquire(&h->h_refcount); 169a8d61afdSLawrence Stewart } 170a8d61afdSLawrence Stewart } 171a8d61afdSLawrence Stewart 172a8d61afdSLawrence Stewart if (error) { 173a8d61afdSLawrence Stewart /* Delete OSD that was assigned prior to the error. */ 174a8d61afdSLawrence Stewart TAILQ_FOREACH(h, &helpers, h_next) { 175a8d61afdSLawrence Stewart if (h->h_classes & classes) 176a8d61afdSLawrence Stewart khelp_remove_osd(h, hosd); 177a8d61afdSLawrence Stewart } 178a8d61afdSLawrence Stewart } 179a8d61afdSLawrence Stewart KHELP_LIST_RUNLOCK(); 180a8d61afdSLawrence Stewart 181a8d61afdSLawrence Stewart return (error); 182a8d61afdSLawrence Stewart } 183a8d61afdSLawrence Stewart 184a8d61afdSLawrence Stewart int 185a8d61afdSLawrence Stewart khelp_destroy_osd(struct osd *hosd) 186a8d61afdSLawrence Stewart { 187a8d61afdSLawrence Stewart struct helper *h; 188a8d61afdSLawrence Stewart int error; 189a8d61afdSLawrence Stewart 190a8d61afdSLawrence Stewart KASSERT(hosd != NULL, ("struct osd not initialised!")); 191a8d61afdSLawrence Stewart 192a8d61afdSLawrence Stewart error = 0; 193a8d61afdSLawrence Stewart 194a8d61afdSLawrence Stewart KHELP_LIST_RLOCK(); 195a8d61afdSLawrence Stewart /* 196a8d61afdSLawrence Stewart * Clean up all khelp related OSD. 197a8d61afdSLawrence Stewart * 198a8d61afdSLawrence Stewart * XXXLAS: Would be nice to use something like osd_exit() here but it 199a8d61afdSLawrence Stewart * doesn't have the right semantics for this purpose. 200a8d61afdSLawrence Stewart */ 201a8d61afdSLawrence Stewart TAILQ_FOREACH(h, &helpers, h_next) 202a8d61afdSLawrence Stewart khelp_remove_osd(h, hosd); 203a8d61afdSLawrence Stewart KHELP_LIST_RUNLOCK(); 204a8d61afdSLawrence Stewart 205a8d61afdSLawrence Stewart return (error); 206a8d61afdSLawrence Stewart } 207a8d61afdSLawrence Stewart 208a8d61afdSLawrence Stewart static inline void 209a8d61afdSLawrence Stewart khelp_remove_osd(struct helper *h, struct osd *hosd) 210a8d61afdSLawrence Stewart { 211a8d61afdSLawrence Stewart void *hdata; 212a8d61afdSLawrence Stewart 213a8d61afdSLawrence Stewart if (h->h_flags & HELPER_NEEDS_OSD) { 214a8d61afdSLawrence Stewart /* 215a8d61afdSLawrence Stewart * If the current helper uses OSD and calling osd_get() 216a8d61afdSLawrence Stewart * on the helper's h_id returns non-NULL, the helper has 217a8d61afdSLawrence Stewart * OSD attached to 'hosd' which needs to be cleaned up. 218a8d61afdSLawrence Stewart */ 219a8d61afdSLawrence Stewart hdata = osd_get(OSD_KHELP, hosd, h->h_id); 220a8d61afdSLawrence Stewart if (hdata != NULL) { 221a8d61afdSLawrence Stewart uma_zfree(h->h_zone, hdata); 222a8d61afdSLawrence Stewart osd_del(OSD_KHELP, hosd, h->h_id); 223a8d61afdSLawrence Stewart refcount_release(&h->h_refcount); 224a8d61afdSLawrence Stewart } 225a8d61afdSLawrence Stewart } 226a8d61afdSLawrence Stewart } 227a8d61afdSLawrence Stewart 228a8d61afdSLawrence Stewart void * 229a8d61afdSLawrence Stewart khelp_get_osd(struct osd *hosd, int32_t id) 230a8d61afdSLawrence Stewart { 231a8d61afdSLawrence Stewart 232a8d61afdSLawrence Stewart return (osd_get(OSD_KHELP, hosd, id)); 233a8d61afdSLawrence Stewart } 234a8d61afdSLawrence Stewart 235a8d61afdSLawrence Stewart int32_t 236a8d61afdSLawrence Stewart khelp_get_id(char *hname) 237a8d61afdSLawrence Stewart { 238a8d61afdSLawrence Stewart struct helper *h; 239a8d61afdSLawrence Stewart int32_t id; 240a8d61afdSLawrence Stewart 241a8d61afdSLawrence Stewart id = -1; 242a8d61afdSLawrence Stewart 243a8d61afdSLawrence Stewart KHELP_LIST_RLOCK(); 244a8d61afdSLawrence Stewart TAILQ_FOREACH(h, &helpers, h_next) { 245a8d61afdSLawrence Stewart if (strncmp(h->h_name, hname, HELPER_NAME_MAXLEN) == 0) { 246a8d61afdSLawrence Stewart id = h->h_id; 247a8d61afdSLawrence Stewart break; 248a8d61afdSLawrence Stewart } 249a8d61afdSLawrence Stewart } 250a8d61afdSLawrence Stewart KHELP_LIST_RUNLOCK(); 251a8d61afdSLawrence Stewart 252a8d61afdSLawrence Stewart return (id); 253a8d61afdSLawrence Stewart } 254a8d61afdSLawrence Stewart 255a8d61afdSLawrence Stewart int 256*89937323SZhenlei Huang khelp_add_hhook(const struct hookinfo *hki, uint32_t flags) 257a8d61afdSLawrence Stewart { 258a8d61afdSLawrence Stewart int error; 259a8d61afdSLawrence Stewart 260a8d61afdSLawrence Stewart /* 261b1f53277SLawrence Stewart * XXXLAS: Should probably include the functionality to update the 262b1f53277SLawrence Stewart * helper's h_hooks struct member. 263a8d61afdSLawrence Stewart */ 264a8d61afdSLawrence Stewart error = hhook_add_hook_lookup(hki, flags); 265a8d61afdSLawrence Stewart 266a8d61afdSLawrence Stewart return (error); 267a8d61afdSLawrence Stewart } 268a8d61afdSLawrence Stewart 269a8d61afdSLawrence Stewart int 270*89937323SZhenlei Huang khelp_remove_hhook(const struct hookinfo *hki) 271a8d61afdSLawrence Stewart { 272a8d61afdSLawrence Stewart int error; 273a8d61afdSLawrence Stewart 274a8d61afdSLawrence Stewart /* 275b1f53277SLawrence Stewart * XXXLAS: Should probably include the functionality to update the 276b1f53277SLawrence Stewart * helper's h_hooks struct member. 277a8d61afdSLawrence Stewart */ 278a8d61afdSLawrence Stewart error = hhook_remove_hook_lookup(hki); 279a8d61afdSLawrence Stewart 280a8d61afdSLawrence Stewart return (error); 281a8d61afdSLawrence Stewart } 282a8d61afdSLawrence Stewart 28358261d30SLawrence Stewart /* 28458261d30SLawrence Stewart * Private KPI between hhook and khelp that allows khelp modules to insert hook 28558261d30SLawrence Stewart * functions into hhook points which register after the modules were loaded. 28658261d30SLawrence Stewart */ 28758261d30SLawrence Stewart void 28858261d30SLawrence Stewart khelp_new_hhook_registered(struct hhook_head *hhh, uint32_t flags) 28958261d30SLawrence Stewart { 29058261d30SLawrence Stewart struct helper *h; 29158261d30SLawrence Stewart int error, i; 29258261d30SLawrence Stewart 29358261d30SLawrence Stewart KHELP_LIST_RLOCK(); 29458261d30SLawrence Stewart TAILQ_FOREACH(h, &helpers, h_next) { 29558261d30SLawrence Stewart for (i = 0; i < h->h_nhooks; i++) { 29658261d30SLawrence Stewart if (hhh->hhh_type != h->h_hooks[i].hook_type || 29758261d30SLawrence Stewart hhh->hhh_id != h->h_hooks[i].hook_id) 29858261d30SLawrence Stewart continue; 29958261d30SLawrence Stewart error = hhook_add_hook(hhh, &h->h_hooks[i], flags); 30058261d30SLawrence Stewart if (error) { 30158261d30SLawrence Stewart printf("%s: \"%s\" khelp module unable to " 30258261d30SLawrence Stewart "hook type %d id %d due to error %d\n", 30358261d30SLawrence Stewart __func__, h->h_name, 30458261d30SLawrence Stewart h->h_hooks[i].hook_type, 30558261d30SLawrence Stewart h->h_hooks[i].hook_id, error); 30658261d30SLawrence Stewart error = 0; 30758261d30SLawrence Stewart } 30858261d30SLawrence Stewart } 30958261d30SLawrence Stewart } 31058261d30SLawrence Stewart KHELP_LIST_RUNLOCK(); 31158261d30SLawrence Stewart } 31258261d30SLawrence Stewart 313a8d61afdSLawrence Stewart int 314a8d61afdSLawrence Stewart khelp_modevent(module_t mod, int event_type, void *data) 315a8d61afdSLawrence Stewart { 316a8d61afdSLawrence Stewart struct khelp_modevent_data *kmd; 317a8d61afdSLawrence Stewart int error; 318a8d61afdSLawrence Stewart 319a8d61afdSLawrence Stewart kmd = (struct khelp_modevent_data *)data; 320a8d61afdSLawrence Stewart error = 0; 321a8d61afdSLawrence Stewart 322a8d61afdSLawrence Stewart switch(event_type) { 323a8d61afdSLawrence Stewart case MOD_LOAD: 324a8d61afdSLawrence Stewart if (kmd->helper->h_flags & HELPER_NEEDS_OSD) { 325a8d61afdSLawrence Stewart if (kmd->uma_zsize <= 0) { 326a8d61afdSLawrence Stewart printf("Use KHELP_DECLARE_MOD_UMA() instead!\n"); 327a8d61afdSLawrence Stewart error = EDOOFUS; 328a8d61afdSLawrence Stewart break; 329a8d61afdSLawrence Stewart } 330a8d61afdSLawrence Stewart kmd->helper->h_zone = uma_zcreate(kmd->name, 331a8d61afdSLawrence Stewart kmd->uma_zsize, kmd->umactor, kmd->umadtor, NULL, 332a8d61afdSLawrence Stewart NULL, 0, 0); 333a8d61afdSLawrence Stewart } 334a8d61afdSLawrence Stewart strlcpy(kmd->helper->h_name, kmd->name, HELPER_NAME_MAXLEN); 335a8d61afdSLawrence Stewart kmd->helper->h_hooks = kmd->hooks; 336a8d61afdSLawrence Stewart kmd->helper->h_nhooks = kmd->nhooks; 337a8d61afdSLawrence Stewart if (kmd->helper->mod_init != NULL) 338a8d61afdSLawrence Stewart error = kmd->helper->mod_init(); 339a8d61afdSLawrence Stewart if (!error) 340a8d61afdSLawrence Stewart error = khelp_register_helper(kmd->helper); 341a8d61afdSLawrence Stewart break; 342a8d61afdSLawrence Stewart 343a8d61afdSLawrence Stewart case MOD_QUIESCE: 344a8d61afdSLawrence Stewart case MOD_SHUTDOWN: 345a8d61afdSLawrence Stewart case MOD_UNLOAD: 346a8d61afdSLawrence Stewart error = khelp_deregister_helper(kmd->helper); 347a8d61afdSLawrence Stewart if (!error) { 348a8d61afdSLawrence Stewart if (kmd->helper->h_flags & HELPER_NEEDS_OSD) 349a8d61afdSLawrence Stewart uma_zdestroy(kmd->helper->h_zone); 350a8d61afdSLawrence Stewart if (kmd->helper->mod_destroy != NULL) 351a8d61afdSLawrence Stewart kmd->helper->mod_destroy(); 352a8d61afdSLawrence Stewart } else if (error == ENOENT) 353a8d61afdSLawrence Stewart /* Do nothing and allow unload if helper not in list. */ 354a8d61afdSLawrence Stewart error = 0; 355415c1c74SMike Karels else if (error == EBUSY && event_type != MOD_SHUTDOWN) 356a8d61afdSLawrence Stewart printf("Khelp module \"%s\" can't unload until its " 357a8d61afdSLawrence Stewart "refcount drops from %d to 0.\n", kmd->name, 358a8d61afdSLawrence Stewart kmd->helper->h_refcount); 359a8d61afdSLawrence Stewart break; 360a8d61afdSLawrence Stewart 361a8d61afdSLawrence Stewart default: 362a8d61afdSLawrence Stewart error = EINVAL; 363a8d61afdSLawrence Stewart break; 364a8d61afdSLawrence Stewart } 365a8d61afdSLawrence Stewart 366a8d61afdSLawrence Stewart return (error); 367a8d61afdSLawrence Stewart } 368