1a8d61afdSLawrence Stewart /*- 2*4d846d26SWarner 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/cdefs.h> 40a8d61afdSLawrence Stewart __FBSDID("$FreeBSD$"); 41a8d61afdSLawrence Stewart 42a8d61afdSLawrence Stewart #include <sys/param.h> 43a8d61afdSLawrence Stewart #include <sys/kernel.h> 44a8d61afdSLawrence Stewart #include <sys/hhook.h> 45a8d61afdSLawrence Stewart #include <sys/khelp.h> 46a8d61afdSLawrence Stewart #include <sys/lock.h> 47a8d61afdSLawrence Stewart #include <sys/malloc.h> 48a8d61afdSLawrence Stewart #include <sys/module.h> 49a8d61afdSLawrence Stewart #include <sys/module_khelp.h> 50a8d61afdSLawrence Stewart #include <sys/osd.h> 51a8d61afdSLawrence Stewart #include <sys/queue.h> 52a8d61afdSLawrence Stewart #include <sys/refcount.h> 53a8d61afdSLawrence Stewart #include <sys/rwlock.h> 54a8d61afdSLawrence Stewart #include <sys/systm.h> 55a8d61afdSLawrence Stewart 56a8d61afdSLawrence Stewart static struct rwlock khelp_list_lock; 57a8d61afdSLawrence Stewart RW_SYSINIT(khelplistlock, &khelp_list_lock, "helper list lock"); 58a8d61afdSLawrence Stewart 59a8d61afdSLawrence Stewart static TAILQ_HEAD(helper_head, helper) helpers = TAILQ_HEAD_INITIALIZER(helpers); 60a8d61afdSLawrence Stewart 61a8d61afdSLawrence Stewart /* Private function prototypes. */ 62a8d61afdSLawrence Stewart static inline void khelp_remove_osd(struct helper *h, struct osd *hosd); 6358261d30SLawrence Stewart void khelp_new_hhook_registered(struct hhook_head *hhh, uint32_t flags); 64a8d61afdSLawrence Stewart 65a8d61afdSLawrence Stewart #define KHELP_LIST_WLOCK() rw_wlock(&khelp_list_lock) 66a8d61afdSLawrence Stewart #define KHELP_LIST_WUNLOCK() rw_wunlock(&khelp_list_lock) 67a8d61afdSLawrence Stewart #define KHELP_LIST_RLOCK() rw_rlock(&khelp_list_lock) 68a8d61afdSLawrence Stewart #define KHELP_LIST_RUNLOCK() rw_runlock(&khelp_list_lock) 69a8d61afdSLawrence Stewart #define KHELP_LIST_LOCK_ASSERT() rw_assert(&khelp_list_lock, RA_LOCKED) 70a8d61afdSLawrence Stewart 71a8d61afdSLawrence Stewart int 72a8d61afdSLawrence Stewart khelp_register_helper(struct helper *h) 73a8d61afdSLawrence Stewart { 74a8d61afdSLawrence Stewart struct helper *tmph; 75a8d61afdSLawrence Stewart int error, i, inserted; 76a8d61afdSLawrence Stewart 77933a8bffSLawrence Stewart error = inserted = 0; 78a8d61afdSLawrence Stewart refcount_init(&h->h_refcount, 0); 79a8d61afdSLawrence Stewart h->h_id = osd_register(OSD_KHELP, NULL, NULL); 80a8d61afdSLawrence Stewart 81a8d61afdSLawrence Stewart /* It's only safe to add the hooks after osd_register(). */ 82a8d61afdSLawrence Stewart for (i = 0; i < h->h_nhooks && !error; i++) { 83a8d61afdSLawrence Stewart /* We don't require the module to assign hook_helper. */ 84a8d61afdSLawrence Stewart h->h_hooks[i].hook_helper = h; 85933a8bffSLawrence Stewart error = hhook_add_hook_lookup(&h->h_hooks[i], HHOOK_WAITOK); 86933a8bffSLawrence Stewart if (error) 87933a8bffSLawrence Stewart printf("%s: \"%s\" khelp module unable to " 88933a8bffSLawrence Stewart "hook type %d id %d due to error %d\n", __func__, 89933a8bffSLawrence Stewart h->h_name, h->h_hooks[i].hook_type, 90933a8bffSLawrence Stewart h->h_hooks[i].hook_id, error); 91a8d61afdSLawrence Stewart } 92a8d61afdSLawrence Stewart 93a8d61afdSLawrence Stewart if (error) { 94a8d61afdSLawrence Stewart for (i--; i >= 0; i--) 95b1f53277SLawrence Stewart hhook_remove_hook_lookup(&h->h_hooks[i]); 96a8d61afdSLawrence Stewart osd_deregister(OSD_KHELP, h->h_id); 97933a8bffSLawrence Stewart } else { 98a8d61afdSLawrence Stewart KHELP_LIST_WLOCK(); 99a8d61afdSLawrence Stewart /* 100a8d61afdSLawrence Stewart * Keep list of helpers sorted in descending h_id order. Due to 101a8d61afdSLawrence Stewart * the way osd_set() works, a sorted list ensures 102933a8bffSLawrence Stewart * khelp_init_osd() will operate with improved efficiency. 103a8d61afdSLawrence Stewart */ 104a8d61afdSLawrence Stewart TAILQ_FOREACH(tmph, &helpers, h_next) { 105a8d61afdSLawrence Stewart if (tmph->h_id < h->h_id) { 106a8d61afdSLawrence Stewart TAILQ_INSERT_BEFORE(tmph, h, h_next); 107a8d61afdSLawrence Stewart inserted = 1; 108a8d61afdSLawrence Stewart break; 109a8d61afdSLawrence Stewart } 110a8d61afdSLawrence Stewart } 111a8d61afdSLawrence Stewart 112a8d61afdSLawrence Stewart if (!inserted) 113a8d61afdSLawrence Stewart TAILQ_INSERT_TAIL(&helpers, h, h_next); 114a8d61afdSLawrence Stewart KHELP_LIST_WUNLOCK(); 115a8d61afdSLawrence Stewart } 116a8d61afdSLawrence Stewart 117a8d61afdSLawrence Stewart return (error); 118a8d61afdSLawrence Stewart } 119a8d61afdSLawrence Stewart 120a8d61afdSLawrence Stewart int 121a8d61afdSLawrence Stewart khelp_deregister_helper(struct helper *h) 122a8d61afdSLawrence Stewart { 123a8d61afdSLawrence Stewart struct helper *tmph; 124a8d61afdSLawrence Stewart int error, i; 125a8d61afdSLawrence Stewart 126a8d61afdSLawrence Stewart KHELP_LIST_WLOCK(); 127a8d61afdSLawrence Stewart if (h->h_refcount > 0) 128a8d61afdSLawrence Stewart error = EBUSY; 129a8d61afdSLawrence Stewart else { 130a8d61afdSLawrence Stewart error = ENOENT; 131a8d61afdSLawrence Stewart TAILQ_FOREACH(tmph, &helpers, h_next) { 132a8d61afdSLawrence Stewart if (tmph == h) { 133a8d61afdSLawrence Stewart TAILQ_REMOVE(&helpers, h, h_next); 134a8d61afdSLawrence Stewart error = 0; 135a8d61afdSLawrence Stewart break; 136a8d61afdSLawrence Stewart } 137a8d61afdSLawrence Stewart } 138a8d61afdSLawrence Stewart } 139a8d61afdSLawrence Stewart KHELP_LIST_WUNLOCK(); 140a8d61afdSLawrence Stewart 141a8d61afdSLawrence Stewart if (!error) { 142a8d61afdSLawrence Stewart for (i = 0; i < h->h_nhooks; i++) 143b1f53277SLawrence Stewart hhook_remove_hook_lookup(&h->h_hooks[i]); 144a8d61afdSLawrence Stewart osd_deregister(OSD_KHELP, h->h_id); 145a8d61afdSLawrence Stewart } 146a8d61afdSLawrence Stewart 147a8d61afdSLawrence Stewart return (error); 148a8d61afdSLawrence Stewart } 149a8d61afdSLawrence Stewart 150a8d61afdSLawrence Stewart int 151a8d61afdSLawrence Stewart khelp_init_osd(uint32_t classes, struct osd *hosd) 152a8d61afdSLawrence Stewart { 153a8d61afdSLawrence Stewart struct helper *h; 154a8d61afdSLawrence Stewart void *hdata; 155a8d61afdSLawrence Stewart int error; 156a8d61afdSLawrence Stewart 157a8d61afdSLawrence Stewart KASSERT(hosd != NULL, ("struct osd not initialised!")); 158a8d61afdSLawrence Stewart 159a8d61afdSLawrence Stewart error = 0; 160a8d61afdSLawrence Stewart 161a8d61afdSLawrence Stewart KHELP_LIST_RLOCK(); 162a8d61afdSLawrence Stewart TAILQ_FOREACH(h, &helpers, h_next) { 163a8d61afdSLawrence Stewart /* If helper is correct class and needs to store OSD... */ 164a8d61afdSLawrence Stewart if (h->h_classes & classes && h->h_flags & HELPER_NEEDS_OSD) { 165a8d61afdSLawrence Stewart hdata = uma_zalloc(h->h_zone, M_NOWAIT); 166a8d61afdSLawrence Stewart if (hdata == NULL) { 167a8d61afdSLawrence Stewart error = ENOMEM; 168a8d61afdSLawrence Stewart break; 169a8d61afdSLawrence Stewart } 170a8d61afdSLawrence Stewart osd_set(OSD_KHELP, hosd, h->h_id, hdata); 171a8d61afdSLawrence Stewart refcount_acquire(&h->h_refcount); 172a8d61afdSLawrence Stewart } 173a8d61afdSLawrence Stewart } 174a8d61afdSLawrence Stewart 175a8d61afdSLawrence Stewart if (error) { 176a8d61afdSLawrence Stewart /* Delete OSD that was assigned prior to the error. */ 177a8d61afdSLawrence Stewart TAILQ_FOREACH(h, &helpers, h_next) { 178a8d61afdSLawrence Stewart if (h->h_classes & classes) 179a8d61afdSLawrence Stewart khelp_remove_osd(h, hosd); 180a8d61afdSLawrence Stewart } 181a8d61afdSLawrence Stewart } 182a8d61afdSLawrence Stewart KHELP_LIST_RUNLOCK(); 183a8d61afdSLawrence Stewart 184a8d61afdSLawrence Stewart return (error); 185a8d61afdSLawrence Stewart } 186a8d61afdSLawrence Stewart 187a8d61afdSLawrence Stewart int 188a8d61afdSLawrence Stewart khelp_destroy_osd(struct osd *hosd) 189a8d61afdSLawrence Stewart { 190a8d61afdSLawrence Stewart struct helper *h; 191a8d61afdSLawrence Stewart int error; 192a8d61afdSLawrence Stewart 193a8d61afdSLawrence Stewart KASSERT(hosd != NULL, ("struct osd not initialised!")); 194a8d61afdSLawrence Stewart 195a8d61afdSLawrence Stewart error = 0; 196a8d61afdSLawrence Stewart 197a8d61afdSLawrence Stewart KHELP_LIST_RLOCK(); 198a8d61afdSLawrence Stewart /* 199a8d61afdSLawrence Stewart * Clean up all khelp related OSD. 200a8d61afdSLawrence Stewart * 201a8d61afdSLawrence Stewart * XXXLAS: Would be nice to use something like osd_exit() here but it 202a8d61afdSLawrence Stewart * doesn't have the right semantics for this purpose. 203a8d61afdSLawrence Stewart */ 204a8d61afdSLawrence Stewart TAILQ_FOREACH(h, &helpers, h_next) 205a8d61afdSLawrence Stewart khelp_remove_osd(h, hosd); 206a8d61afdSLawrence Stewart KHELP_LIST_RUNLOCK(); 207a8d61afdSLawrence Stewart 208a8d61afdSLawrence Stewart return (error); 209a8d61afdSLawrence Stewart } 210a8d61afdSLawrence Stewart 211a8d61afdSLawrence Stewart static inline void 212a8d61afdSLawrence Stewart khelp_remove_osd(struct helper *h, struct osd *hosd) 213a8d61afdSLawrence Stewart { 214a8d61afdSLawrence Stewart void *hdata; 215a8d61afdSLawrence Stewart 216a8d61afdSLawrence Stewart if (h->h_flags & HELPER_NEEDS_OSD) { 217a8d61afdSLawrence Stewart /* 218a8d61afdSLawrence Stewart * If the current helper uses OSD and calling osd_get() 219a8d61afdSLawrence Stewart * on the helper's h_id returns non-NULL, the helper has 220a8d61afdSLawrence Stewart * OSD attached to 'hosd' which needs to be cleaned up. 221a8d61afdSLawrence Stewart */ 222a8d61afdSLawrence Stewart hdata = osd_get(OSD_KHELP, hosd, h->h_id); 223a8d61afdSLawrence Stewart if (hdata != NULL) { 224a8d61afdSLawrence Stewart uma_zfree(h->h_zone, hdata); 225a8d61afdSLawrence Stewart osd_del(OSD_KHELP, hosd, h->h_id); 226a8d61afdSLawrence Stewart refcount_release(&h->h_refcount); 227a8d61afdSLawrence Stewart } 228a8d61afdSLawrence Stewart } 229a8d61afdSLawrence Stewart } 230a8d61afdSLawrence Stewart 231a8d61afdSLawrence Stewart void * 232a8d61afdSLawrence Stewart khelp_get_osd(struct osd *hosd, int32_t id) 233a8d61afdSLawrence Stewart { 234a8d61afdSLawrence Stewart 235a8d61afdSLawrence Stewart return (osd_get(OSD_KHELP, hosd, id)); 236a8d61afdSLawrence Stewart } 237a8d61afdSLawrence Stewart 238a8d61afdSLawrence Stewart int32_t 239a8d61afdSLawrence Stewart khelp_get_id(char *hname) 240a8d61afdSLawrence Stewart { 241a8d61afdSLawrence Stewart struct helper *h; 242a8d61afdSLawrence Stewart int32_t id; 243a8d61afdSLawrence Stewart 244a8d61afdSLawrence Stewart id = -1; 245a8d61afdSLawrence Stewart 246a8d61afdSLawrence Stewart KHELP_LIST_RLOCK(); 247a8d61afdSLawrence Stewart TAILQ_FOREACH(h, &helpers, h_next) { 248a8d61afdSLawrence Stewart if (strncmp(h->h_name, hname, HELPER_NAME_MAXLEN) == 0) { 249a8d61afdSLawrence Stewart id = h->h_id; 250a8d61afdSLawrence Stewart break; 251a8d61afdSLawrence Stewart } 252a8d61afdSLawrence Stewart } 253a8d61afdSLawrence Stewart KHELP_LIST_RUNLOCK(); 254a8d61afdSLawrence Stewart 255a8d61afdSLawrence Stewart return (id); 256a8d61afdSLawrence Stewart } 257a8d61afdSLawrence Stewart 258a8d61afdSLawrence Stewart int 259a8d61afdSLawrence Stewart khelp_add_hhook(struct hookinfo *hki, uint32_t flags) 260a8d61afdSLawrence Stewart { 261a8d61afdSLawrence Stewart int error; 262a8d61afdSLawrence Stewart 263a8d61afdSLawrence Stewart /* 264b1f53277SLawrence Stewart * XXXLAS: Should probably include the functionality to update the 265b1f53277SLawrence Stewart * helper's h_hooks struct member. 266a8d61afdSLawrence Stewart */ 267a8d61afdSLawrence Stewart error = hhook_add_hook_lookup(hki, flags); 268a8d61afdSLawrence Stewart 269a8d61afdSLawrence Stewart return (error); 270a8d61afdSLawrence Stewart } 271a8d61afdSLawrence Stewart 272a8d61afdSLawrence Stewart int 273a8d61afdSLawrence Stewart khelp_remove_hhook(struct hookinfo *hki) 274a8d61afdSLawrence Stewart { 275a8d61afdSLawrence Stewart int error; 276a8d61afdSLawrence Stewart 277a8d61afdSLawrence Stewart /* 278b1f53277SLawrence Stewart * XXXLAS: Should probably include the functionality to update the 279b1f53277SLawrence Stewart * helper's h_hooks struct member. 280a8d61afdSLawrence Stewart */ 281a8d61afdSLawrence Stewart error = hhook_remove_hook_lookup(hki); 282a8d61afdSLawrence Stewart 283a8d61afdSLawrence Stewart return (error); 284a8d61afdSLawrence Stewart } 285a8d61afdSLawrence Stewart 28658261d30SLawrence Stewart /* 28758261d30SLawrence Stewart * Private KPI between hhook and khelp that allows khelp modules to insert hook 28858261d30SLawrence Stewart * functions into hhook points which register after the modules were loaded. 28958261d30SLawrence Stewart */ 29058261d30SLawrence Stewart void 29158261d30SLawrence Stewart khelp_new_hhook_registered(struct hhook_head *hhh, uint32_t flags) 29258261d30SLawrence Stewart { 29358261d30SLawrence Stewart struct helper *h; 29458261d30SLawrence Stewart int error, i; 29558261d30SLawrence Stewart 29658261d30SLawrence Stewart KHELP_LIST_RLOCK(); 29758261d30SLawrence Stewart TAILQ_FOREACH(h, &helpers, h_next) { 29858261d30SLawrence Stewart for (i = 0; i < h->h_nhooks; i++) { 29958261d30SLawrence Stewart if (hhh->hhh_type != h->h_hooks[i].hook_type || 30058261d30SLawrence Stewart hhh->hhh_id != h->h_hooks[i].hook_id) 30158261d30SLawrence Stewart continue; 30258261d30SLawrence Stewart error = hhook_add_hook(hhh, &h->h_hooks[i], flags); 30358261d30SLawrence Stewart if (error) { 30458261d30SLawrence Stewart printf("%s: \"%s\" khelp module unable to " 30558261d30SLawrence Stewart "hook type %d id %d due to error %d\n", 30658261d30SLawrence Stewart __func__, h->h_name, 30758261d30SLawrence Stewart h->h_hooks[i].hook_type, 30858261d30SLawrence Stewart h->h_hooks[i].hook_id, error); 30958261d30SLawrence Stewart error = 0; 31058261d30SLawrence Stewart } 31158261d30SLawrence Stewart } 31258261d30SLawrence Stewart } 31358261d30SLawrence Stewart KHELP_LIST_RUNLOCK(); 31458261d30SLawrence Stewart } 31558261d30SLawrence Stewart 316a8d61afdSLawrence Stewart int 317a8d61afdSLawrence Stewart khelp_modevent(module_t mod, int event_type, void *data) 318a8d61afdSLawrence Stewart { 319a8d61afdSLawrence Stewart struct khelp_modevent_data *kmd; 320a8d61afdSLawrence Stewart int error; 321a8d61afdSLawrence Stewart 322a8d61afdSLawrence Stewart kmd = (struct khelp_modevent_data *)data; 323a8d61afdSLawrence Stewart error = 0; 324a8d61afdSLawrence Stewart 325a8d61afdSLawrence Stewart switch(event_type) { 326a8d61afdSLawrence Stewart case MOD_LOAD: 327a8d61afdSLawrence Stewart if (kmd->helper->h_flags & HELPER_NEEDS_OSD) { 328a8d61afdSLawrence Stewart if (kmd->uma_zsize <= 0) { 329a8d61afdSLawrence Stewart printf("Use KHELP_DECLARE_MOD_UMA() instead!\n"); 330a8d61afdSLawrence Stewart error = EDOOFUS; 331a8d61afdSLawrence Stewart break; 332a8d61afdSLawrence Stewart } 333a8d61afdSLawrence Stewart kmd->helper->h_zone = uma_zcreate(kmd->name, 334a8d61afdSLawrence Stewart kmd->uma_zsize, kmd->umactor, kmd->umadtor, NULL, 335a8d61afdSLawrence Stewart NULL, 0, 0); 336a8d61afdSLawrence Stewart if (kmd->helper->h_zone == NULL) { 337a8d61afdSLawrence Stewart error = ENOMEM; 338a8d61afdSLawrence Stewart break; 339a8d61afdSLawrence Stewart } 340a8d61afdSLawrence Stewart } 341a8d61afdSLawrence Stewart strlcpy(kmd->helper->h_name, kmd->name, HELPER_NAME_MAXLEN); 342a8d61afdSLawrence Stewart kmd->helper->h_hooks = kmd->hooks; 343a8d61afdSLawrence Stewart kmd->helper->h_nhooks = kmd->nhooks; 344a8d61afdSLawrence Stewart if (kmd->helper->mod_init != NULL) 345a8d61afdSLawrence Stewart error = kmd->helper->mod_init(); 346a8d61afdSLawrence Stewart if (!error) 347a8d61afdSLawrence Stewart error = khelp_register_helper(kmd->helper); 348a8d61afdSLawrence Stewart break; 349a8d61afdSLawrence Stewart 350a8d61afdSLawrence Stewart case MOD_QUIESCE: 351a8d61afdSLawrence Stewart case MOD_SHUTDOWN: 352a8d61afdSLawrence Stewart case MOD_UNLOAD: 353a8d61afdSLawrence Stewart error = khelp_deregister_helper(kmd->helper); 354a8d61afdSLawrence Stewart if (!error) { 355a8d61afdSLawrence Stewart if (kmd->helper->h_flags & HELPER_NEEDS_OSD) 356a8d61afdSLawrence Stewart uma_zdestroy(kmd->helper->h_zone); 357a8d61afdSLawrence Stewart if (kmd->helper->mod_destroy != NULL) 358a8d61afdSLawrence Stewart kmd->helper->mod_destroy(); 359a8d61afdSLawrence Stewart } else if (error == ENOENT) 360a8d61afdSLawrence Stewart /* Do nothing and allow unload if helper not in list. */ 361a8d61afdSLawrence Stewart error = 0; 362a8d61afdSLawrence Stewart else if (error == EBUSY) 363a8d61afdSLawrence Stewart printf("Khelp module \"%s\" can't unload until its " 364a8d61afdSLawrence Stewart "refcount drops from %d to 0.\n", kmd->name, 365a8d61afdSLawrence Stewart kmd->helper->h_refcount); 366a8d61afdSLawrence Stewart break; 367a8d61afdSLawrence Stewart 368a8d61afdSLawrence Stewart default: 369a8d61afdSLawrence Stewart error = EINVAL; 370a8d61afdSLawrence Stewart break; 371a8d61afdSLawrence Stewart } 372a8d61afdSLawrence Stewart 373a8d61afdSLawrence Stewart return (error); 374a8d61afdSLawrence Stewart } 375