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