The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/kern_hhook.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2010 Lawrence Stewart <lstewart@freebsd.org>
    3  * Copyright (c) 2010 The FreeBSD Foundation
    4  * All rights reserved.
    5  *
    6  * This software was developed by Lawrence Stewart while studying at the Centre
    7  * for Advanced Internet Architectures, Swinburne University of Technology,
    8  * made possible in part by grants from the FreeBSD Foundation and Cisco
    9  * University Research Program Fund at Community Foundation Silicon Valley.
   10  *
   11  * Portions of this software were developed at the Centre for Advanced
   12  * Internet Architectures, Swinburne University of Technology, Melbourne,
   13  * Australia by Lawrence Stewart under sponsorship from the FreeBSD Foundation.
   14  *
   15  * Redistribution and use in source and binary forms, with or without
   16  * modification, are permitted provided that the following conditions
   17  * are met:
   18  * 1. Redistributions of source code must retain the above copyright
   19  *    notice, this list of conditions and the following disclaimer.
   20  * 2. Redistributions in binary form must reproduce the above copyright
   21  *    notice, this list of conditions and the following disclaimer in the
   22  *    documentation and/or other materials provided with the distribution.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   27  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   34  * SUCH DAMAGE.
   35  */
   36 
   37 #include <sys/cdefs.h>
   38 __FBSDID("$FreeBSD: releng/9.0/sys/kern/kern_hhook.c 220592 2011-04-13 11:28:46Z pluknet $");
   39 
   40 #include <sys/param.h>
   41 #include <sys/kernel.h>
   42 #include <sys/hhook.h>
   43 #include <sys/khelp.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/systm.h>
   51 
   52 #include <net/vnet.h>
   53 
   54 struct hhook {
   55         hhook_func_t            hhk_func;
   56         struct helper           *hhk_helper;
   57         void                    *hhk_udata;
   58         STAILQ_ENTRY(hhook)     hhk_next;
   59 };
   60 
   61 static MALLOC_DEFINE(M_HHOOK, "hhook", "Helper hooks are linked off hhook_head lists");
   62 
   63 LIST_HEAD(hhookheadhead, hhook_head);
   64 VNET_DEFINE(struct hhookheadhead, hhook_head_list);
   65 #define V_hhook_head_list VNET(hhook_head_list)
   66 
   67 static struct mtx hhook_head_list_lock;
   68 MTX_SYSINIT(hhookheadlistlock, &hhook_head_list_lock, "hhook_head list lock",
   69     MTX_DEF);
   70 
   71 /* Private function prototypes. */
   72 static void hhook_head_destroy(struct hhook_head *hhh);
   73 
   74 #define HHHLIST_LOCK() mtx_lock(&hhook_head_list_lock)
   75 #define HHHLIST_UNLOCK() mtx_unlock(&hhook_head_list_lock)
   76 #define HHHLIST_LOCK_ASSERT() mtx_assert(&hhook_head_list_lock, MA_OWNED)
   77 
   78 #define HHH_LOCK_INIT(hhh) rm_init(&(hhh)->hhh_lock, "hhook_head rm lock")
   79 #define HHH_LOCK_DESTROY(hhh) rm_destroy(&(hhh)->hhh_lock)
   80 #define HHH_WLOCK(hhh) rm_wlock(&(hhh)->hhh_lock)
   81 #define HHH_WUNLOCK(hhh) rm_wunlock(&(hhh)->hhh_lock)
   82 #define HHH_RLOCK(hhh, rmpt) rm_rlock(&(hhh)->hhh_lock, (rmpt))
   83 #define HHH_RUNLOCK(hhh, rmpt) rm_runlock(&(hhh)->hhh_lock, (rmpt))
   84 
   85 /*
   86  * Run all helper hook functions for a given hook point.
   87  */
   88 void
   89 hhook_run_hooks(struct hhook_head *hhh, void *ctx_data, struct osd *hosd)
   90 {
   91         struct hhook *hhk;
   92         void *hdata;
   93         struct rm_priotracker rmpt;
   94 
   95         KASSERT(hhh->hhh_refcount > 0, ("hhook_head %p refcount is 0", hhh));
   96 
   97         HHH_RLOCK(hhh, &rmpt);
   98         STAILQ_FOREACH(hhk, &hhh->hhh_hooks, hhk_next) {
   99                 if (hhk->hhk_helper->h_flags & HELPER_NEEDS_OSD) {
  100                         hdata = osd_get(OSD_KHELP, hosd, hhk->hhk_helper->h_id);
  101                         if (hdata == NULL)
  102                                 continue;
  103                 } else
  104                         hdata = NULL;
  105 
  106                 /*
  107                  * XXXLAS: We currently ignore the int returned by the hook,
  108                  * but will likely want to handle it in future to allow hhook to
  109                  * be used like pfil and effect changes at the hhook calling
  110                  * site e.g. we could define a new hook type of HHOOK_TYPE_PFIL
  111                  * and standardise what particular return values mean and set
  112                  * the context data to pass exactly the same information as pfil
  113                  * hooks currently receive, thus replicating pfil with hhook.
  114                  */
  115                 hhk->hhk_func(hhh->hhh_type, hhh->hhh_id, hhk->hhk_udata,
  116                     ctx_data, hdata, hosd);
  117         }
  118         HHH_RUNLOCK(hhh, &rmpt);
  119 }
  120 
  121 /*
  122  * Register a new helper hook function with a helper hook point.
  123  */
  124 int
  125 hhook_add_hook(struct hhook_head *hhh, struct hookinfo *hki, uint32_t flags)
  126 {
  127         struct hhook *hhk, *tmp;
  128         int error;
  129 
  130         error = 0;
  131 
  132         if (hhh == NULL)
  133                 return (ENOENT);
  134 
  135         hhk = malloc(sizeof(struct hhook), M_HHOOK,
  136             M_ZERO | ((flags & HHOOK_WAITOK) ? M_WAITOK : M_NOWAIT));
  137 
  138         if (hhk == NULL)
  139                 return (ENOMEM);
  140 
  141         hhk->hhk_helper = hki->hook_helper;
  142         hhk->hhk_func = hki->hook_func;
  143         hhk->hhk_udata = hki->hook_udata;
  144 
  145         HHH_WLOCK(hhh);
  146         STAILQ_FOREACH(tmp, &hhh->hhh_hooks, hhk_next) {
  147                 if (tmp->hhk_func == hki->hook_func &&
  148                     tmp->hhk_udata == hki->hook_udata) {
  149                         /* The helper hook function is already registered. */
  150                         error = EEXIST;
  151                         break;
  152                 }
  153         }
  154 
  155         if (!error) {
  156                 STAILQ_INSERT_TAIL(&hhh->hhh_hooks, hhk, hhk_next);
  157                 hhh->hhh_nhooks++;
  158         } else
  159                 free(hhk, M_HHOOK);
  160 
  161         HHH_WUNLOCK(hhh);
  162 
  163         return (error);
  164 }
  165 
  166 /*
  167  * Lookup a helper hook point and register a new helper hook function with it.
  168  */
  169 int
  170 hhook_add_hook_lookup(struct hookinfo *hki, uint32_t flags)
  171 {
  172         struct hhook_head *hhh;
  173         int error;
  174 
  175         hhh = hhook_head_get(hki->hook_type, hki->hook_id);
  176 
  177         if (hhh == NULL)
  178                 return (ENOENT);
  179 
  180         error = hhook_add_hook(hhh, hki, flags);
  181         hhook_head_release(hhh);
  182 
  183         return (error);
  184 }
  185 
  186 /*
  187  * Remove a helper hook function from a helper hook point.
  188  */
  189 int
  190 hhook_remove_hook(struct hhook_head *hhh, struct hookinfo *hki)
  191 {
  192         struct hhook *tmp;
  193 
  194         if (hhh == NULL)
  195                 return (ENOENT);
  196 
  197         HHH_WLOCK(hhh);
  198         STAILQ_FOREACH(tmp, &hhh->hhh_hooks, hhk_next) {
  199                 if (tmp->hhk_func == hki->hook_func &&
  200                     tmp->hhk_udata == hki->hook_udata) {
  201                         STAILQ_REMOVE(&hhh->hhh_hooks, tmp, hhook, hhk_next);
  202                         free(tmp, M_HHOOK);
  203                         hhh->hhh_nhooks--;
  204                         break;
  205                 }
  206         }
  207         HHH_WUNLOCK(hhh);
  208 
  209         return (0);
  210 }
  211 
  212 /*
  213  * Lookup a helper hook point and remove a helper hook function from it.
  214  */
  215 int
  216 hhook_remove_hook_lookup(struct hookinfo *hki)
  217 {
  218         struct hhook_head *hhh;
  219 
  220         hhh = hhook_head_get(hki->hook_type, hki->hook_id);
  221 
  222         if (hhh == NULL)
  223                 return (ENOENT);
  224 
  225         hhook_remove_hook(hhh, hki);
  226         hhook_head_release(hhh);
  227 
  228         return (0);
  229 }
  230 
  231 /*
  232  * Register a new helper hook point.
  233  */
  234 int
  235 hhook_head_register(int32_t hhook_type, int32_t hhook_id, struct hhook_head **hhh,
  236     uint32_t flags)
  237 {
  238         struct hhook_head *tmphhh;
  239 
  240         tmphhh = hhook_head_get(hhook_type, hhook_id);
  241 
  242         if (tmphhh != NULL) {
  243                 /* Hook point previously registered. */
  244                 hhook_head_release(tmphhh);
  245                 return (EEXIST);
  246         }
  247 
  248         /* XXXLAS: Need to implement support for non-virtualised hooks. */
  249         if ((flags & HHOOK_HEADISINVNET) == 0) {
  250                 printf("%s: only vnet-style virtualised hooks can be used\n",
  251                     __func__);
  252                 return (EINVAL);
  253         }
  254 
  255         tmphhh = malloc(sizeof(struct hhook_head), M_HHOOK,
  256             M_ZERO | ((flags & HHOOK_WAITOK) ? M_WAITOK : M_NOWAIT));
  257 
  258         if (tmphhh == NULL)
  259                 return (ENOMEM);
  260 
  261         tmphhh->hhh_type = hhook_type;
  262         tmphhh->hhh_id = hhook_id;
  263         tmphhh->hhh_nhooks = 0;
  264         STAILQ_INIT(&tmphhh->hhh_hooks);
  265         HHH_LOCK_INIT(tmphhh);
  266 
  267         if (hhh != NULL)
  268                 refcount_init(&tmphhh->hhh_refcount, 1);
  269         else
  270                 refcount_init(&tmphhh->hhh_refcount, 0);
  271 
  272         if (flags & HHOOK_HEADISINVNET) {
  273                 tmphhh->hhh_flags |= HHH_ISINVNET;
  274                 HHHLIST_LOCK();
  275                 LIST_INSERT_HEAD(&V_hhook_head_list, tmphhh, hhh_next);
  276                 HHHLIST_UNLOCK();
  277         } else {
  278                 /* XXXLAS: Add tmphhh to the non-virtualised list. */
  279         }
  280 
  281         *hhh = tmphhh;
  282 
  283         return (0);
  284 }
  285 
  286 static void
  287 hhook_head_destroy(struct hhook_head *hhh)
  288 {
  289         struct hhook *tmp, *tmp2;
  290 
  291         HHHLIST_LOCK_ASSERT();
  292 
  293         LIST_REMOVE(hhh, hhh_next);
  294         HHH_WLOCK(hhh);
  295         STAILQ_FOREACH_SAFE(tmp, &hhh->hhh_hooks, hhk_next, tmp2)
  296                 free(tmp, M_HHOOK);
  297         HHH_WUNLOCK(hhh);
  298         HHH_LOCK_DESTROY(hhh);
  299         free(hhh, M_HHOOK);
  300 }
  301 
  302 /*
  303  * Remove a helper hook point.
  304  */
  305 int
  306 hhook_head_deregister(struct hhook_head *hhh)
  307 {
  308         int error;
  309 
  310         error = 0;
  311 
  312         HHHLIST_LOCK();
  313         if (hhh == NULL)
  314                 error = ENOENT;
  315         else if (hhh->hhh_refcount > 1)
  316                 error = EBUSY;
  317         else
  318                 hhook_head_destroy(hhh);
  319         HHHLIST_UNLOCK();
  320 
  321         return (error);
  322 }
  323 
  324 /*
  325  * Remove a helper hook point via a hhook_head lookup.
  326  */
  327 int
  328 hhook_head_deregister_lookup(int32_t hhook_type, int32_t hhook_id)
  329 {
  330         struct hhook_head *hhh;
  331         int error;
  332 
  333         hhh = hhook_head_get(hhook_type, hhook_id);
  334         error = hhook_head_deregister(hhh);
  335 
  336         if (error == EBUSY)
  337                 hhook_head_release(hhh);
  338 
  339         return (error);
  340 }
  341 
  342 /*
  343  * Lookup and return the hhook_head struct associated with the specified type
  344  * and id, or NULL if not found. If found, the hhook_head's refcount is bumped.
  345  */
  346 struct hhook_head *
  347 hhook_head_get(int32_t hhook_type, int32_t hhook_id)
  348 {
  349         struct hhook_head *hhh;
  350 
  351         /* XXXLAS: Pick hhook_head_list based on hhook_head flags. */
  352         HHHLIST_LOCK();
  353         LIST_FOREACH(hhh, &V_hhook_head_list, hhh_next) {
  354                 if (hhh->hhh_type == hhook_type && hhh->hhh_id == hhook_id) {
  355                         refcount_acquire(&hhh->hhh_refcount);
  356                         break;
  357                 }
  358         }
  359         HHHLIST_UNLOCK();
  360 
  361         return (hhh);
  362 }
  363 
  364 void
  365 hhook_head_release(struct hhook_head *hhh)
  366 {
  367 
  368         refcount_release(&hhh->hhh_refcount);
  369 }
  370 
  371 /*
  372  * Check the hhook_head private flags and return the appropriate public
  373  * representation of the flag to the caller. The function is implemented in a
  374  * way that allows us to cope with other subsystems becoming virtualised in the
  375  * future.
  376  */
  377 uint32_t
  378 hhook_head_is_virtualised(struct hhook_head *hhh)
  379 {
  380         uint32_t ret;
  381 
  382         ret = 0;
  383 
  384         if (hhh != NULL) {
  385                 if (hhh->hhh_flags & HHH_ISINVNET)
  386                         ret = HHOOK_HEADISINVNET;
  387         }
  388 
  389         return (ret);
  390 }
  391 
  392 uint32_t
  393 hhook_head_is_virtualised_lookup(int32_t hook_type, int32_t hook_id)
  394 {
  395         struct hhook_head *hhh;
  396         uint32_t ret;
  397 
  398         hhh = hhook_head_get(hook_type, hook_id);
  399 
  400         if (hhh == NULL)
  401                 return (0);
  402 
  403         ret = hhook_head_is_virtualised(hhh);
  404         hhook_head_release(hhh);
  405 
  406         return (ret);
  407 }
  408 
  409 /*
  410  * Vnet created and being initialised.
  411  */
  412 static void
  413 hhook_vnet_init(const void *unused __unused)
  414 {
  415 
  416         LIST_INIT(&V_hhook_head_list);
  417 }
  418 
  419 /*
  420  * Vnet being torn down and destroyed.
  421  */
  422 static void
  423 hhook_vnet_uninit(const void *unused __unused)
  424 {
  425         struct hhook_head *hhh, *tmphhh;
  426 
  427         /*
  428          * If subsystems which export helper hook points use the hhook KPI
  429          * correctly, the loop below should have no work to do because the
  430          * subsystem should have already called hhook_head_deregister().
  431          */
  432         HHHLIST_LOCK();
  433         LIST_FOREACH_SAFE(hhh, &V_hhook_head_list, hhh_next, tmphhh) {
  434                 printf("%s: hhook_head type=%d, id=%d cleanup required\n",
  435                     __func__, hhh->hhh_type, hhh->hhh_id);
  436                 hhook_head_destroy(hhh);
  437         }
  438         HHHLIST_UNLOCK();
  439 }
  440 
  441 
  442 /*
  443  * When a vnet is created and being initialised, init the V_hhook_head_list.
  444  */
  445 VNET_SYSINIT(hhook_vnet_init, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST,
  446     hhook_vnet_init, NULL);
  447 
  448 /*
  449  * The hhook KPI provides a mechanism for subsystems which export helper hook
  450  * points to clean up on vnet tear down, but in case the KPI is misused,
  451  * provide a function to clean up and free memory for a vnet being destroyed.
  452  */
  453 VNET_SYSUNINIT(hhook_vnet_uninit, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST,
  454     hhook_vnet_uninit, NULL);

Cache object: 3063b0a2ef09897f523c1ef4e5b1a325


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.