1216615Slstewart/*- 2251770Slstewart * Copyright (c) 2010,2013 Lawrence Stewart <lstewart@freebsd.org> 3216615Slstewart * Copyright (c) 2010 The FreeBSD Foundation 4216615Slstewart * All rights reserved. 5216615Slstewart * 6216615Slstewart * This software was developed by Lawrence Stewart while studying at the Centre 7220560Slstewart * for Advanced Internet Architectures, Swinburne University of Technology, 8220560Slstewart * made possible in part by grants from the FreeBSD Foundation and Cisco 9220560Slstewart * University Research Program Fund at Community Foundation Silicon Valley. 10216615Slstewart * 11216615Slstewart * Portions of this software were developed at the Centre for Advanced 12216615Slstewart * Internet Architectures, Swinburne University of Technology, Melbourne, 13216615Slstewart * Australia by Lawrence Stewart under sponsorship from the FreeBSD Foundation. 14216615Slstewart * 15216615Slstewart * Redistribution and use in source and binary forms, with or without 16216615Slstewart * modification, are permitted provided that the following conditions 17216615Slstewart * are met: 18216615Slstewart * 1. Redistributions of source code must retain the above copyright 19216615Slstewart * notice, this list of conditions and the following disclaimer. 20216615Slstewart * 2. Redistributions in binary form must reproduce the above copyright 21216615Slstewart * notice, this list of conditions and the following disclaimer in the 22216615Slstewart * documentation and/or other materials provided with the distribution. 23216615Slstewart * 24216615Slstewart * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 25216615Slstewart * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26216615Slstewart * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27216615Slstewart * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 28216615Slstewart * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29216615Slstewart * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30216615Slstewart * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31216615Slstewart * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32216615Slstewart * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33216615Slstewart * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34216615Slstewart * SUCH DAMAGE. 35216615Slstewart */ 36216615Slstewart 37216615Slstewart#include <sys/cdefs.h> 38216615Slstewart__FBSDID("$FreeBSD$"); 39216615Slstewart 40216615Slstewart#include <sys/param.h> 41216615Slstewart#include <sys/kernel.h> 42216615Slstewart#include <sys/hhook.h> 43216615Slstewart#include <sys/khelp.h> 44216615Slstewart#include <sys/lock.h> 45216615Slstewart#include <sys/malloc.h> 46216615Slstewart#include <sys/module.h> 47216615Slstewart#include <sys/module_khelp.h> 48216615Slstewart#include <sys/osd.h> 49216615Slstewart#include <sys/queue.h> 50216615Slstewart#include <sys/refcount.h> 51216615Slstewart#include <sys/rwlock.h> 52216615Slstewart#include <sys/systm.h> 53216615Slstewart 54216615Slstewartstatic struct rwlock khelp_list_lock; 55216615SlstewartRW_SYSINIT(khelplistlock, &khelp_list_lock, "helper list lock"); 56216615Slstewart 57216615Slstewartstatic TAILQ_HEAD(helper_head, helper) helpers = TAILQ_HEAD_INITIALIZER(helpers); 58216615Slstewart 59216615Slstewart/* Private function prototypes. */ 60216615Slstewartstatic inline void khelp_remove_osd(struct helper *h, struct osd *hosd); 61251774Slstewartvoid khelp_new_hhook_registered(struct hhook_head *hhh, uint32_t flags); 62216615Slstewart 63216615Slstewart#define KHELP_LIST_WLOCK() rw_wlock(&khelp_list_lock) 64216615Slstewart#define KHELP_LIST_WUNLOCK() rw_wunlock(&khelp_list_lock) 65216615Slstewart#define KHELP_LIST_RLOCK() rw_rlock(&khelp_list_lock) 66216615Slstewart#define KHELP_LIST_RUNLOCK() rw_runlock(&khelp_list_lock) 67216615Slstewart#define KHELP_LIST_LOCK_ASSERT() rw_assert(&khelp_list_lock, RA_LOCKED) 68216615Slstewart 69216615Slstewartint 70216615Slstewartkhelp_register_helper(struct helper *h) 71216615Slstewart{ 72216615Slstewart struct helper *tmph; 73216615Slstewart int error, i, inserted; 74216615Slstewart 75251778Slstewart error = inserted = 0; 76216615Slstewart refcount_init(&h->h_refcount, 0); 77216615Slstewart h->h_id = osd_register(OSD_KHELP, NULL, NULL); 78216615Slstewart 79216615Slstewart /* It's only safe to add the hooks after osd_register(). */ 80251778Slstewart for (i = 0; i < h->h_nhooks && !error; i++) { 81251778Slstewart /* We don't require the module to assign hook_helper. */ 82251778Slstewart h->h_hooks[i].hook_helper = h; 83251778Slstewart error = hhook_add_hook_lookup(&h->h_hooks[i], HHOOK_WAITOK); 84251778Slstewart if (error) 85251778Slstewart printf("%s: \"%s\" khelp module unable to " 86251778Slstewart "hook type %d id %d due to error %d\n", __func__, 87251778Slstewart h->h_name, h->h_hooks[i].hook_type, 88251778Slstewart h->h_hooks[i].hook_id, error); 89216615Slstewart } 90216615Slstewart 91251778Slstewart if (error) { 92251778Slstewart for (i--; i >= 0; i--) 93251778Slstewart hhook_remove_hook_lookup(&h->h_hooks[i]); 94251778Slstewart osd_deregister(OSD_KHELP, h->h_id); 95251778Slstewart } else { 96216615Slstewart KHELP_LIST_WLOCK(); 97216615Slstewart /* 98216615Slstewart * Keep list of helpers sorted in descending h_id order. Due to 99216615Slstewart * the way osd_set() works, a sorted list ensures 100251778Slstewart * khelp_init_osd() will operate with improved efficiency. 101216615Slstewart */ 102216615Slstewart TAILQ_FOREACH(tmph, &helpers, h_next) { 103216615Slstewart if (tmph->h_id < h->h_id) { 104216615Slstewart TAILQ_INSERT_BEFORE(tmph, h, h_next); 105216615Slstewart inserted = 1; 106216615Slstewart break; 107216615Slstewart } 108216615Slstewart } 109216615Slstewart 110216615Slstewart if (!inserted) 111216615Slstewart TAILQ_INSERT_TAIL(&helpers, h, h_next); 112216615Slstewart KHELP_LIST_WUNLOCK(); 113216615Slstewart } 114216615Slstewart 115216615Slstewart return (error); 116216615Slstewart} 117216615Slstewart 118216615Slstewartint 119216615Slstewartkhelp_deregister_helper(struct helper *h) 120216615Slstewart{ 121216615Slstewart struct helper *tmph; 122216615Slstewart int error, i; 123216615Slstewart 124216615Slstewart KHELP_LIST_WLOCK(); 125216615Slstewart if (h->h_refcount > 0) 126216615Slstewart error = EBUSY; 127216615Slstewart else { 128216615Slstewart error = ENOENT; 129216615Slstewart TAILQ_FOREACH(tmph, &helpers, h_next) { 130216615Slstewart if (tmph == h) { 131216615Slstewart TAILQ_REMOVE(&helpers, h, h_next); 132216615Slstewart error = 0; 133216615Slstewart break; 134216615Slstewart } 135216615Slstewart } 136216615Slstewart } 137216615Slstewart KHELP_LIST_WUNLOCK(); 138216615Slstewart 139216615Slstewart if (!error) { 140251778Slstewart for (i = 0; i < h->h_nhooks; i++) 141251778Slstewart hhook_remove_hook_lookup(&h->h_hooks[i]); 142216615Slstewart osd_deregister(OSD_KHELP, h->h_id); 143216615Slstewart } 144216615Slstewart 145216615Slstewart return (error); 146216615Slstewart} 147216615Slstewart 148216615Slstewartint 149216615Slstewartkhelp_init_osd(uint32_t classes, struct osd *hosd) 150216615Slstewart{ 151216615Slstewart struct helper *h; 152216615Slstewart void *hdata; 153216615Slstewart int error; 154216615Slstewart 155216615Slstewart KASSERT(hosd != NULL, ("struct osd not initialised!")); 156216615Slstewart 157216615Slstewart error = 0; 158216615Slstewart 159216615Slstewart KHELP_LIST_RLOCK(); 160216615Slstewart TAILQ_FOREACH(h, &helpers, h_next) { 161216615Slstewart /* If helper is correct class and needs to store OSD... */ 162216615Slstewart if (h->h_classes & classes && h->h_flags & HELPER_NEEDS_OSD) { 163216615Slstewart hdata = uma_zalloc(h->h_zone, M_NOWAIT); 164216615Slstewart if (hdata == NULL) { 165216615Slstewart error = ENOMEM; 166216615Slstewart break; 167216615Slstewart } 168216615Slstewart osd_set(OSD_KHELP, hosd, h->h_id, hdata); 169216615Slstewart refcount_acquire(&h->h_refcount); 170216615Slstewart } 171216615Slstewart } 172216615Slstewart 173216615Slstewart if (error) { 174216615Slstewart /* Delete OSD that was assigned prior to the error. */ 175216615Slstewart TAILQ_FOREACH(h, &helpers, h_next) { 176216615Slstewart if (h->h_classes & classes) 177216615Slstewart khelp_remove_osd(h, hosd); 178216615Slstewart } 179216615Slstewart } 180216615Slstewart KHELP_LIST_RUNLOCK(); 181216615Slstewart 182216615Slstewart return (error); 183216615Slstewart} 184216615Slstewart 185216615Slstewartint 186216615Slstewartkhelp_destroy_osd(struct osd *hosd) 187216615Slstewart{ 188216615Slstewart struct helper *h; 189216615Slstewart int error; 190216615Slstewart 191216615Slstewart KASSERT(hosd != NULL, ("struct osd not initialised!")); 192216615Slstewart 193216615Slstewart error = 0; 194216615Slstewart 195216615Slstewart KHELP_LIST_RLOCK(); 196216615Slstewart /* 197216615Slstewart * Clean up all khelp related OSD. 198216615Slstewart * 199216615Slstewart * XXXLAS: Would be nice to use something like osd_exit() here but it 200216615Slstewart * doesn't have the right semantics for this purpose. 201216615Slstewart */ 202216615Slstewart TAILQ_FOREACH(h, &helpers, h_next) 203216615Slstewart khelp_remove_osd(h, hosd); 204216615Slstewart KHELP_LIST_RUNLOCK(); 205216615Slstewart 206216615Slstewart return (error); 207216615Slstewart} 208216615Slstewart 209216615Slstewartstatic inline void 210216615Slstewartkhelp_remove_osd(struct helper *h, struct osd *hosd) 211216615Slstewart{ 212216615Slstewart void *hdata; 213216615Slstewart 214216615Slstewart if (h->h_flags & HELPER_NEEDS_OSD) { 215216615Slstewart /* 216216615Slstewart * If the current helper uses OSD and calling osd_get() 217216615Slstewart * on the helper's h_id returns non-NULL, the helper has 218216615Slstewart * OSD attached to 'hosd' which needs to be cleaned up. 219216615Slstewart */ 220216615Slstewart hdata = osd_get(OSD_KHELP, hosd, h->h_id); 221216615Slstewart if (hdata != NULL) { 222216615Slstewart uma_zfree(h->h_zone, hdata); 223216615Slstewart osd_del(OSD_KHELP, hosd, h->h_id); 224216615Slstewart refcount_release(&h->h_refcount); 225216615Slstewart } 226216615Slstewart } 227216615Slstewart} 228216615Slstewart 229216615Slstewartvoid * 230216615Slstewartkhelp_get_osd(struct osd *hosd, int32_t id) 231216615Slstewart{ 232216615Slstewart 233216615Slstewart return (osd_get(OSD_KHELP, hosd, id)); 234216615Slstewart} 235216615Slstewart 236216615Slstewartint32_t 237216615Slstewartkhelp_get_id(char *hname) 238216615Slstewart{ 239216615Slstewart struct helper *h; 240216615Slstewart int32_t id; 241216615Slstewart 242216615Slstewart id = -1; 243216615Slstewart 244216615Slstewart KHELP_LIST_RLOCK(); 245216615Slstewart TAILQ_FOREACH(h, &helpers, h_next) { 246216615Slstewart if (strncmp(h->h_name, hname, HELPER_NAME_MAXLEN) == 0) { 247216615Slstewart id = h->h_id; 248216615Slstewart break; 249216615Slstewart } 250216615Slstewart } 251216615Slstewart KHELP_LIST_RUNLOCK(); 252216615Slstewart 253216615Slstewart return (id); 254216615Slstewart} 255216615Slstewart 256216615Slstewartint 257216615Slstewartkhelp_add_hhook(struct hookinfo *hki, uint32_t flags) 258216615Slstewart{ 259216615Slstewart int error; 260216615Slstewart 261216615Slstewart /* 262251770Slstewart * XXXLAS: Should probably include the functionality to update the 263251770Slstewart * helper's h_hooks struct member. 264216615Slstewart */ 265251770Slstewart error = hhook_add_hook_lookup(hki, flags); 266216615Slstewart 267216615Slstewart return (error); 268216615Slstewart} 269216615Slstewart 270216615Slstewartint 271216615Slstewartkhelp_remove_hhook(struct hookinfo *hki) 272216615Slstewart{ 273216615Slstewart int error; 274216615Slstewart 275216615Slstewart /* 276251770Slstewart * XXXLAS: Should probably include the functionality to update the 277251770Slstewart * helper's h_hooks struct member. 278216615Slstewart */ 279251770Slstewart error = hhook_remove_hook_lookup(hki); 280216615Slstewart 281216615Slstewart return (error); 282216615Slstewart} 283216615Slstewart 284251774Slstewart/* 285251774Slstewart * Private KPI between hhook and khelp that allows khelp modules to insert hook 286251774Slstewart * functions into hhook points which register after the modules were loaded. 287251774Slstewart */ 288251774Slstewartvoid 289251774Slstewartkhelp_new_hhook_registered(struct hhook_head *hhh, uint32_t flags) 290251774Slstewart{ 291251774Slstewart struct helper *h; 292251774Slstewart int error, i; 293251774Slstewart 294251774Slstewart KHELP_LIST_RLOCK(); 295251774Slstewart TAILQ_FOREACH(h, &helpers, h_next) { 296251774Slstewart for (i = 0; i < h->h_nhooks; i++) { 297251774Slstewart if (hhh->hhh_type != h->h_hooks[i].hook_type || 298251774Slstewart hhh->hhh_id != h->h_hooks[i].hook_id) 299251774Slstewart continue; 300251774Slstewart error = hhook_add_hook(hhh, &h->h_hooks[i], flags); 301251774Slstewart if (error) { 302251774Slstewart printf("%s: \"%s\" khelp module unable to " 303251774Slstewart "hook type %d id %d due to error %d\n", 304251774Slstewart __func__, h->h_name, 305251774Slstewart h->h_hooks[i].hook_type, 306251774Slstewart h->h_hooks[i].hook_id, error); 307251774Slstewart error = 0; 308251774Slstewart } 309251774Slstewart } 310251774Slstewart } 311251774Slstewart KHELP_LIST_RUNLOCK(); 312251774Slstewart} 313251774Slstewart 314216615Slstewartint 315216615Slstewartkhelp_modevent(module_t mod, int event_type, void *data) 316216615Slstewart{ 317216615Slstewart struct khelp_modevent_data *kmd; 318216615Slstewart int error; 319216615Slstewart 320216615Slstewart kmd = (struct khelp_modevent_data *)data; 321216615Slstewart error = 0; 322216615Slstewart 323216615Slstewart switch(event_type) { 324216615Slstewart case MOD_LOAD: 325216615Slstewart if (kmd->helper->h_flags & HELPER_NEEDS_OSD) { 326216615Slstewart if (kmd->uma_zsize <= 0) { 327216615Slstewart printf("Use KHELP_DECLARE_MOD_UMA() instead!\n"); 328216615Slstewart error = EDOOFUS; 329216615Slstewart break; 330216615Slstewart } 331216615Slstewart kmd->helper->h_zone = uma_zcreate(kmd->name, 332216615Slstewart kmd->uma_zsize, kmd->umactor, kmd->umadtor, NULL, 333216615Slstewart NULL, 0, 0); 334216615Slstewart if (kmd->helper->h_zone == NULL) { 335216615Slstewart error = ENOMEM; 336216615Slstewart break; 337216615Slstewart } 338216615Slstewart } 339216615Slstewart strlcpy(kmd->helper->h_name, kmd->name, HELPER_NAME_MAXLEN); 340216615Slstewart kmd->helper->h_hooks = kmd->hooks; 341216615Slstewart kmd->helper->h_nhooks = kmd->nhooks; 342216615Slstewart if (kmd->helper->mod_init != NULL) 343216615Slstewart error = kmd->helper->mod_init(); 344216615Slstewart if (!error) 345216615Slstewart error = khelp_register_helper(kmd->helper); 346216615Slstewart break; 347216615Slstewart 348216615Slstewart case MOD_QUIESCE: 349216615Slstewart case MOD_SHUTDOWN: 350216615Slstewart case MOD_UNLOAD: 351216615Slstewart error = khelp_deregister_helper(kmd->helper); 352216615Slstewart if (!error) { 353216615Slstewart if (kmd->helper->h_flags & HELPER_NEEDS_OSD) 354216615Slstewart uma_zdestroy(kmd->helper->h_zone); 355216615Slstewart if (kmd->helper->mod_destroy != NULL) 356216615Slstewart kmd->helper->mod_destroy(); 357216615Slstewart } else if (error == ENOENT) 358216615Slstewart /* Do nothing and allow unload if helper not in list. */ 359216615Slstewart error = 0; 360216615Slstewart else if (error == EBUSY) 361216615Slstewart printf("Khelp module \"%s\" can't unload until its " 362216615Slstewart "refcount drops from %d to 0.\n", kmd->name, 363216615Slstewart kmd->helper->h_refcount); 364216615Slstewart break; 365216615Slstewart 366216615Slstewart default: 367216615Slstewart error = EINVAL; 368216615Slstewart break; 369216615Slstewart } 370216615Slstewart 371216615Slstewart return (error); 372216615Slstewart} 373