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