kern_hhook.c revision 216615
1216615Slstewart/*- 2216615Slstewart * Copyright (c) 2010 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 7216615Slstewart * for Advanced Internet Architectures, Swinburne University, made possible in 8216615Slstewart * part by grants from the FreeBSD Foundation and Cisco University Research 9216615Slstewart * 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: head/sys/kern/kern_hhook.c 216615 2010-12-21 13:45:29Z lstewart $"); 39216615Slstewart 40216615Slstewart#include <sys/param.h> 41216615Slstewart#include <sys/kernel.h> 42216615Slstewart#include <sys/hhook.h> 43216615Slstewart#include <sys/khelp.h> 44216615Slstewart#include <sys/malloc.h> 45216615Slstewart#include <sys/module.h> 46216615Slstewart#include <sys/module_khelp.h> 47216615Slstewart#include <sys/osd.h> 48216615Slstewart#include <sys/queue.h> 49216615Slstewart#include <sys/refcount.h> 50216615Slstewart#include <sys/systm.h> 51216615Slstewart 52216615Slstewart#include <net/vnet.h> 53216615Slstewart 54216615Slstewartstruct hhook { 55216615Slstewart hhook_func_t hhk_func; 56216615Slstewart struct helper *hhk_helper; 57216615Slstewart void *hhk_udata; 58216615Slstewart STAILQ_ENTRY(hhook) hhk_next; 59216615Slstewart}; 60216615Slstewart 61216615SlstewartMALLOC_DECLARE(M_HHOOK); 62216615SlstewartMALLOC_DEFINE(M_HHOOK, "hhook", "Helper hooks are linked off hhook_head lists"); 63216615Slstewart 64216615SlstewartLIST_HEAD(hhookheadhead, hhook_head); 65216615SlstewartVNET_DEFINE(struct hhookheadhead, hhook_head_list); 66216615Slstewart#define V_hhook_head_list VNET(hhook_head_list) 67216615Slstewart 68216615Slstewartstatic struct mtx hhook_head_list_lock; 69216615SlstewartMTX_SYSINIT(hhookheadlistlock, &hhook_head_list_lock, "hhook_head list lock", 70216615Slstewart MTX_DEF); 71216615Slstewart 72216615Slstewart/* Private function prototypes. */ 73216615Slstewartstatic void hhook_head_destroy(struct hhook_head *hhh); 74216615Slstewart 75216615Slstewart#define HHHLIST_LOCK() mtx_lock(&hhook_head_list_lock) 76216615Slstewart#define HHHLIST_UNLOCK() mtx_unlock(&hhook_head_list_lock) 77216615Slstewart#define HHHLIST_LOCK_ASSERT() mtx_assert(&hhook_head_list_lock, MA_OWNED) 78216615Slstewart 79216615Slstewart#define HHH_LOCK_INIT(hhh) rm_init(&(hhh)->hhh_lock, "hhook_head rm lock") 80216615Slstewart#define HHH_LOCK_DESTROY(hhh) rm_destroy(&(hhh)->hhh_lock) 81216615Slstewart#define HHH_WLOCK(hhh) rm_wlock(&(hhh)->hhh_lock) 82216615Slstewart#define HHH_WUNLOCK(hhh) rm_wunlock(&(hhh)->hhh_lock) 83216615Slstewart#define HHH_RLOCK(hhh, rmpt) rm_rlock(&(hhh)->hhh_lock, (rmpt)) 84216615Slstewart#define HHH_RUNLOCK(hhh, rmpt) rm_runlock(&(hhh)->hhh_lock, (rmpt)) 85216615Slstewart 86216615Slstewart/* 87216615Slstewart * Run all helper hook functions for a given hook point. 88216615Slstewart */ 89216615Slstewartvoid 90216615Slstewarthhook_run_hooks(struct hhook_head *hhh, void *ctx_data, struct osd *hosd) 91216615Slstewart{ 92216615Slstewart struct hhook *hhk; 93216615Slstewart void *hdata; 94216615Slstewart struct rm_priotracker rmpt; 95216615Slstewart 96216615Slstewart KASSERT(hhh->hhh_refcount > 0, ("hhook_head %p refcount is 0", hhh)); 97216615Slstewart 98216615Slstewart HHH_RLOCK(hhh, &rmpt); 99216615Slstewart STAILQ_FOREACH(hhk, &hhh->hhh_hooks, hhk_next) { 100216615Slstewart if (hhk->hhk_helper->h_flags & HELPER_NEEDS_OSD) { 101216615Slstewart hdata = osd_get(OSD_KHELP, hosd, hhk->hhk_helper->h_id); 102216615Slstewart if (hdata == NULL) 103216615Slstewart continue; 104216615Slstewart } else 105216615Slstewart hdata = NULL; 106216615Slstewart 107216615Slstewart /* 108216615Slstewart * XXXLAS: We currently ignore the int returned by the hook, 109216615Slstewart * but will likely want to handle it in future to allow hhook to 110216615Slstewart * be used like pfil and effect changes at the hhook calling 111216615Slstewart * site e.g. we could define a new hook type of HHOOK_TYPE_PFIL 112216615Slstewart * and standardise what particular return values mean and set 113216615Slstewart * the context data to pass exactly the same information as pfil 114216615Slstewart * hooks currently receive, thus replicating pfil with hhook. 115216615Slstewart */ 116216615Slstewart hhk->hhk_func(hhh->hhh_type, hhh->hhh_id, hhk->hhk_udata, 117216615Slstewart ctx_data, hdata, hosd); 118216615Slstewart } 119216615Slstewart HHH_RUNLOCK(hhh, &rmpt); 120216615Slstewart} 121216615Slstewart 122216615Slstewart/* 123216615Slstewart * Register a new helper hook function with a helper hook point. 124216615Slstewart */ 125216615Slstewartint 126216615Slstewarthhook_add_hook(struct hhook_head *hhh, struct hookinfo *hki, uint32_t flags) 127216615Slstewart{ 128216615Slstewart struct hhook *hhk, *tmp; 129216615Slstewart int error; 130216615Slstewart 131216615Slstewart error = 0; 132216615Slstewart 133216615Slstewart if (hhh == NULL) 134216615Slstewart return (ENOENT); 135216615Slstewart 136216615Slstewart hhk = malloc(sizeof(struct hhook), M_HHOOK, 137216615Slstewart M_ZERO | ((flags & HHOOK_WAITOK) ? M_WAITOK : M_NOWAIT)); 138216615Slstewart 139216615Slstewart if (hhk == NULL) 140216615Slstewart return (ENOMEM); 141216615Slstewart 142216615Slstewart hhk->hhk_helper = hki->hook_helper; 143216615Slstewart hhk->hhk_func = hki->hook_func; 144216615Slstewart hhk->hhk_udata = hki->hook_udata; 145216615Slstewart 146216615Slstewart HHH_WLOCK(hhh); 147216615Slstewart STAILQ_FOREACH(tmp, &hhh->hhh_hooks, hhk_next) { 148216615Slstewart if (tmp->hhk_func == hki->hook_func && 149216615Slstewart tmp->hhk_udata == hki->hook_udata) { 150216615Slstewart /* The helper hook function is already registered. */ 151216615Slstewart error = EEXIST; 152216615Slstewart break; 153216615Slstewart } 154216615Slstewart } 155216615Slstewart 156216615Slstewart if (!error) { 157216615Slstewart STAILQ_INSERT_TAIL(&hhh->hhh_hooks, hhk, hhk_next); 158216615Slstewart hhh->hhh_nhooks++; 159216615Slstewart } 160216615Slstewart else 161216615Slstewart free(hhk, M_HHOOK); 162216615Slstewart 163216615Slstewart HHH_WUNLOCK(hhh); 164216615Slstewart 165216615Slstewart return (error); 166216615Slstewart} 167216615Slstewart 168216615Slstewart/* 169216615Slstewart * Lookup a helper hook point and register a new helper hook function with it. 170216615Slstewart */ 171216615Slstewartint 172216615Slstewarthhook_add_hook_lookup(struct hookinfo *hki, uint32_t flags) 173216615Slstewart{ 174216615Slstewart struct hhook_head *hhh; 175216615Slstewart int error; 176216615Slstewart 177216615Slstewart hhh = hhook_head_get(hki->hook_type, hki->hook_id); 178216615Slstewart 179216615Slstewart if (hhh == NULL) 180216615Slstewart return (ENOENT); 181216615Slstewart 182216615Slstewart error = hhook_add_hook(hhh, hki, flags); 183216615Slstewart hhook_head_release(hhh); 184216615Slstewart 185216615Slstewart return (error); 186216615Slstewart} 187216615Slstewart 188216615Slstewart/* 189216615Slstewart * Remove a helper hook function from a helper hook point. 190216615Slstewart */ 191216615Slstewartint 192216615Slstewarthhook_remove_hook(struct hhook_head *hhh, struct hookinfo *hki) 193216615Slstewart{ 194216615Slstewart struct hhook *tmp; 195216615Slstewart 196216615Slstewart if (hhh == NULL) 197216615Slstewart return (ENOENT); 198216615Slstewart 199216615Slstewart HHH_WLOCK(hhh); 200216615Slstewart STAILQ_FOREACH(tmp, &hhh->hhh_hooks, hhk_next) { 201216615Slstewart if (tmp->hhk_func == hki->hook_func && 202216615Slstewart tmp->hhk_udata == hki->hook_udata) { 203216615Slstewart STAILQ_REMOVE(&hhh->hhh_hooks, tmp, hhook, hhk_next); 204216615Slstewart free(tmp, M_HHOOK); 205216615Slstewart hhh->hhh_nhooks--; 206216615Slstewart break; 207216615Slstewart } 208216615Slstewart } 209216615Slstewart HHH_WUNLOCK(hhh); 210216615Slstewart 211216615Slstewart return (0); 212216615Slstewart} 213216615Slstewart 214216615Slstewart/* 215216615Slstewart * Lookup a helper hook point and remove a helper hook function from it. 216216615Slstewart */ 217216615Slstewartint 218216615Slstewarthhook_remove_hook_lookup(struct hookinfo *hki) 219216615Slstewart{ 220216615Slstewart struct hhook_head *hhh; 221216615Slstewart 222216615Slstewart hhh = hhook_head_get(hki->hook_type, hki->hook_id); 223216615Slstewart 224216615Slstewart if (hhh == NULL) 225216615Slstewart return (ENOENT); 226216615Slstewart 227216615Slstewart hhook_remove_hook(hhh, hki); 228216615Slstewart hhook_head_release(hhh); 229216615Slstewart 230216615Slstewart return (0); 231216615Slstewart} 232216615Slstewart 233216615Slstewart/* 234216615Slstewart * Register a new helper hook point. 235216615Slstewart */ 236216615Slstewartint 237216615Slstewarthhook_head_register(int32_t hhook_type, int32_t hhook_id, struct hhook_head **hhh, 238216615Slstewart uint32_t flags) 239216615Slstewart{ 240216615Slstewart struct hhook_head *tmphhh; 241216615Slstewart 242216615Slstewart tmphhh = hhook_head_get(hhook_type, hhook_id); 243216615Slstewart 244216615Slstewart if (tmphhh != NULL) { 245216615Slstewart /* Hook point previously registered. */ 246216615Slstewart hhook_head_release(tmphhh); 247216615Slstewart return (EEXIST); 248216615Slstewart } 249216615Slstewart 250216615Slstewart /* XXXLAS: Need to implement support for non-virtualised hooks. */ 251216615Slstewart if ((flags & HHOOK_HEADISINVNET) == 0) { 252216615Slstewart printf("%s: only vnet-style virtualised hooks can be used\n", 253216615Slstewart __func__); 254216615Slstewart return (EINVAL); 255216615Slstewart } 256216615Slstewart 257216615Slstewart tmphhh = malloc(sizeof(struct hhook_head), M_HHOOK, 258216615Slstewart M_ZERO | ((flags & HHOOK_WAITOK) ? M_WAITOK : M_NOWAIT)); 259216615Slstewart 260216615Slstewart if (tmphhh == NULL) 261216615Slstewart return (ENOMEM); 262216615Slstewart 263216615Slstewart tmphhh->hhh_type = hhook_type; 264216615Slstewart tmphhh->hhh_id = hhook_id; 265216615Slstewart tmphhh->hhh_nhooks = 0; 266216615Slstewart STAILQ_INIT(&tmphhh->hhh_hooks); 267216615Slstewart HHH_LOCK_INIT(tmphhh); 268216615Slstewart 269216615Slstewart if (hhh != NULL) 270216615Slstewart refcount_init(&tmphhh->hhh_refcount, 1); 271216615Slstewart else 272216615Slstewart refcount_init(&tmphhh->hhh_refcount, 0); 273216615Slstewart 274216615Slstewart if (flags & HHOOK_HEADISINVNET) { 275216615Slstewart tmphhh->hhh_flags |= HHH_ISINVNET; 276216615Slstewart HHHLIST_LOCK(); 277216615Slstewart LIST_INSERT_HEAD(&V_hhook_head_list, tmphhh, hhh_next); 278216615Slstewart HHHLIST_UNLOCK(); 279216615Slstewart } else { 280216615Slstewart /* XXXLAS: Add tmphhh to the non-virtualised list. */ 281216615Slstewart } 282216615Slstewart 283216615Slstewart *hhh = tmphhh; 284216615Slstewart 285216615Slstewart return (0); 286216615Slstewart} 287216615Slstewart 288216615Slstewartstatic void 289216615Slstewarthhook_head_destroy(struct hhook_head *hhh) 290216615Slstewart{ 291216615Slstewart struct hhook *tmp, *tmp2; 292216615Slstewart 293216615Slstewart HHHLIST_LOCK_ASSERT(); 294216615Slstewart 295216615Slstewart LIST_REMOVE(hhh, hhh_next); 296216615Slstewart HHH_WLOCK(hhh); 297216615Slstewart STAILQ_FOREACH_SAFE(tmp, &hhh->hhh_hooks, hhk_next, tmp2) 298216615Slstewart free(tmp, M_HHOOK); 299216615Slstewart HHH_WUNLOCK(hhh); 300216615Slstewart HHH_LOCK_DESTROY(hhh); 301216615Slstewart free(hhh, M_HHOOK); 302216615Slstewart} 303216615Slstewart 304216615Slstewart/* 305216615Slstewart * Remove a helper hook point. 306216615Slstewart */ 307216615Slstewartint 308216615Slstewarthhook_head_deregister(struct hhook_head *hhh) 309216615Slstewart{ 310216615Slstewart int error; 311216615Slstewart 312216615Slstewart error = 0; 313216615Slstewart 314216615Slstewart HHHLIST_LOCK(); 315216615Slstewart if (hhh == NULL) 316216615Slstewart error = ENOENT; 317216615Slstewart else if (hhh->hhh_refcount > 1) 318216615Slstewart error = EBUSY; 319216615Slstewart else 320216615Slstewart hhook_head_destroy(hhh); 321216615Slstewart HHHLIST_UNLOCK(); 322216615Slstewart 323216615Slstewart return (error); 324216615Slstewart} 325216615Slstewart 326216615Slstewart/* 327216615Slstewart * Remove a helper hook point via a hhook_head lookup. 328216615Slstewart */ 329216615Slstewartint 330216615Slstewarthhook_head_deregister_lookup(int32_t hhook_type, int32_t hhook_id) 331216615Slstewart{ 332216615Slstewart struct hhook_head *hhh; 333216615Slstewart int error; 334216615Slstewart 335216615Slstewart error = 0; 336216615Slstewart hhh = hhook_head_get(hhook_type, hhook_id); 337216615Slstewart error = hhook_head_deregister(hhh); 338216615Slstewart 339216615Slstewart if (error == EBUSY) 340216615Slstewart hhook_head_release(hhh); 341216615Slstewart 342216615Slstewart return (error); 343216615Slstewart} 344216615Slstewart 345216615Slstewart/* 346216615Slstewart * Lookup and return the hhook_head struct associated with the specified type 347216615Slstewart * and id, or NULL if not found. If found, the hhook_head's refcount is bumped. 348216615Slstewart */ 349216615Slstewartstruct hhook_head * 350216615Slstewarthhook_head_get(int32_t hhook_type, int32_t hhook_id) 351216615Slstewart{ 352216615Slstewart struct hhook_head *hhh; 353216615Slstewart 354216615Slstewart /* XXXLAS: Pick hhook_head_list based on hhook_head flags. */ 355216615Slstewart HHHLIST_LOCK(); 356216615Slstewart LIST_FOREACH(hhh, &V_hhook_head_list, hhh_next) { 357216615Slstewart if (hhh->hhh_type == hhook_type && hhh->hhh_id == hhook_id) { 358216615Slstewart refcount_acquire(&hhh->hhh_refcount); 359216615Slstewart HHHLIST_UNLOCK(); 360216615Slstewart return (hhh); 361216615Slstewart } 362216615Slstewart } 363216615Slstewart HHHLIST_UNLOCK(); 364216615Slstewart 365216615Slstewart return (NULL); 366216615Slstewart} 367216615Slstewart 368216615Slstewartvoid 369216615Slstewarthhook_head_release(struct hhook_head *hhh) 370216615Slstewart{ 371216615Slstewart 372216615Slstewart refcount_release(&hhh->hhh_refcount); 373216615Slstewart} 374216615Slstewart 375216615Slstewart/* 376216615Slstewart * Check the hhook_head private flags and return the appropriate public 377216615Slstewart * representation of the flag to the caller. The function is implemented in a 378216615Slstewart * way that allows us to cope with other subsystems becoming virtualised in the 379216615Slstewart * future. 380216615Slstewart */ 381216615Slstewartuint32_t 382216615Slstewarthhook_head_is_virtualised(struct hhook_head *hhh) 383216615Slstewart{ 384216615Slstewart uint32_t ret; 385216615Slstewart 386216615Slstewart if (hhh == NULL) 387216615Slstewart return (0); 388216615Slstewart 389216615Slstewart if (hhh->hhh_flags & HHH_ISINVNET) 390216615Slstewart ret = HHOOK_HEADISINVNET; 391216615Slstewart 392216615Slstewart return (ret); 393216615Slstewart} 394216615Slstewart 395216615Slstewartuint32_t 396216615Slstewarthhook_head_is_virtualised_lookup(int32_t hook_type, int32_t hook_id) 397216615Slstewart{ 398216615Slstewart struct hhook_head *hhh; 399216615Slstewart uint32_t ret; 400216615Slstewart 401216615Slstewart hhh = hhook_head_get(hook_type, hook_id); 402216615Slstewart 403216615Slstewart if (hhh == NULL) 404216615Slstewart return (0); 405216615Slstewart 406216615Slstewart ret = hhook_head_is_virtualised(hhh); 407216615Slstewart hhook_head_release(hhh); 408216615Slstewart 409216615Slstewart return (ret); 410216615Slstewart} 411216615Slstewart 412216615Slstewart/* 413216615Slstewart * Vnet created and being initialised. 414216615Slstewart */ 415216615Slstewartstatic void 416216615Slstewarthhook_vnet_init(const void *unused __unused) 417216615Slstewart{ 418216615Slstewart 419216615Slstewart LIST_INIT(&V_hhook_head_list); 420216615Slstewart} 421216615Slstewart 422216615Slstewart/* 423216615Slstewart * Vnet being torn down and destroyed. 424216615Slstewart */ 425216615Slstewartstatic void 426216615Slstewarthhook_vnet_uninit(const void *unused __unused) 427216615Slstewart{ 428216615Slstewart struct hhook_head *hhh, *tmphhh; 429216615Slstewart 430216615Slstewart /* 431216615Slstewart * If subsystems which export helper hook points use the hhook KPI 432216615Slstewart * correctly, the loop below should have no work to do because the 433216615Slstewart * subsystem should have already called hhook_head_deregister(). 434216615Slstewart */ 435216615Slstewart HHHLIST_LOCK(); 436216615Slstewart LIST_FOREACH_SAFE(hhh, &V_hhook_head_list, hhh_next, tmphhh) { 437216615Slstewart printf("%s: hhook_head type=%d, id=%d cleanup required\n", 438216615Slstewart __func__, hhh->hhh_type, hhh->hhh_id); 439216615Slstewart hhook_head_destroy(hhh); 440216615Slstewart } 441216615Slstewart HHHLIST_UNLOCK(); 442216615Slstewart} 443216615Slstewart 444216615Slstewart 445216615Slstewart/* 446216615Slstewart * When a vnet is created and being initialised, init the V_hhook_head_list. 447216615Slstewart */ 448216615SlstewartVNET_SYSINIT(hhook_vnet_init, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST, 449216615Slstewart hhook_vnet_init, NULL); 450216615Slstewart 451216615Slstewart/* 452216615Slstewart * The hhook KPI provides a mechanism for subsystems which export helper hook 453216615Slstewart * points to clean up on vnet tear down, but in case the KPI is misused, 454216615Slstewart * provide a function to clean up and free memory for a vnet being destroyed. 455216615Slstewart */ 456216615SlstewartVNET_SYSUNINIT(hhook_vnet_uninit, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST, 457216615Slstewart hhook_vnet_uninit, NULL); 458