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