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_khelp.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/8.3/sys/kern/kern_khelp.c 222420 2011-05-28 13:54:19Z lstewart $");
   39 
   40 #include <sys/param.h>
   41 #include <sys/kernel.h>
   42 #include <sys/hhook.h>
   43 #include <sys/jail.h>
   44 #include <sys/khelp.h>
   45 #include <sys/lock.h>
   46 #include <sys/malloc.h>
   47 #include <sys/module.h>
   48 #include <sys/module_khelp.h>
   49 #include <sys/osd.h>
   50 #include <sys/queue.h>
   51 #include <sys/refcount.h>
   52 #include <sys/rwlock.h>
   53 #include <sys/systm.h>
   54 
   55 #include <net/vnet.h>
   56 
   57 static struct rwlock khelp_list_lock;
   58 RW_SYSINIT(khelplistlock, &khelp_list_lock, "helper list lock");
   59 
   60 static TAILQ_HEAD(helper_head, helper) helpers = TAILQ_HEAD_INITIALIZER(helpers);
   61 
   62 /* Private function prototypes. */
   63 static inline void khelp_remove_osd(struct helper *h, struct osd *hosd);
   64 
   65 #define KHELP_LIST_WLOCK() rw_wlock(&khelp_list_lock)
   66 #define KHELP_LIST_WUNLOCK() rw_wunlock(&khelp_list_lock)
   67 #define KHELP_LIST_RLOCK() rw_rlock(&khelp_list_lock)
   68 #define KHELP_LIST_RUNLOCK() rw_runlock(&khelp_list_lock)
   69 #define KHELP_LIST_LOCK_ASSERT() rw_assert(&khelp_list_lock, RA_LOCKED)
   70 
   71 int
   72 khelp_register_helper(struct helper *h)
   73 {
   74         struct helper *tmph;
   75         int error, i, inserted;
   76 
   77         error = 0;
   78         inserted = 0;
   79         refcount_init(&h->h_refcount, 0);
   80         h->h_id = osd_register(OSD_KHELP, NULL, NULL);
   81 
   82         /* It's only safe to add the hooks after osd_register(). */
   83         if (h->h_nhooks > 0) {
   84                 for (i = 0; i < h->h_nhooks && !error; i++) {
   85                         /* We don't require the module to assign hook_helper. */
   86                         h->h_hooks[i].hook_helper = h;
   87                         error = khelp_add_hhook(&h->h_hooks[i], HHOOK_NOWAIT);
   88                 }
   89 
   90                 if (error) {
   91                         for (i--; i >= 0; i--)
   92                                 khelp_remove_hhook(&h->h_hooks[i]);
   93 
   94                         osd_deregister(OSD_KHELP, h->h_id);
   95                 }
   96         }
   97 
   98         if (!error) {
   99                 KHELP_LIST_WLOCK();
  100                 /*
  101                  * Keep list of helpers sorted in descending h_id order. Due to
  102                  * the way osd_set() works, a sorted list ensures
  103                  * init_helper_osd() will operate with improved efficiency.
  104                  */
  105                 TAILQ_FOREACH(tmph, &helpers, h_next) {
  106                         if (tmph->h_id < h->h_id) {
  107                                 TAILQ_INSERT_BEFORE(tmph, h, h_next);
  108                                 inserted = 1;
  109                                 break;
  110                         }
  111                 }
  112 
  113                 if (!inserted)
  114                         TAILQ_INSERT_TAIL(&helpers, h, h_next);
  115                 KHELP_LIST_WUNLOCK();
  116         }
  117 
  118         return (error);
  119 }
  120 
  121 int
  122 khelp_deregister_helper(struct helper *h)
  123 {
  124         struct helper *tmph;
  125         int error, i;
  126 
  127         error = 0;
  128 
  129         KHELP_LIST_WLOCK();
  130         if (h->h_refcount > 0)
  131                 error = EBUSY;
  132         else {
  133                 error = ENOENT;
  134                 TAILQ_FOREACH(tmph, &helpers, h_next) {
  135                         if (tmph == h) {
  136                                 TAILQ_REMOVE(&helpers, h, h_next);
  137                                 error = 0;
  138                                 break;
  139                         }
  140                 }
  141         }
  142         KHELP_LIST_WUNLOCK();
  143 
  144         if (!error) {
  145                 if (h->h_nhooks > 0) {
  146                         for (i = 0; i < h->h_nhooks; i++)
  147                                 khelp_remove_hhook(&h->h_hooks[i]);
  148                 }
  149                 osd_deregister(OSD_KHELP, h->h_id);
  150         }
  151 
  152         return (error);
  153 }
  154 
  155 int
  156 khelp_init_osd(uint32_t classes, struct osd *hosd)
  157 {
  158         struct helper *h;
  159         void *hdata;
  160         int error;
  161 
  162         KASSERT(hosd != NULL, ("struct osd not initialised!"));
  163 
  164         error = 0;
  165 
  166         KHELP_LIST_RLOCK();
  167         TAILQ_FOREACH(h, &helpers, h_next) {
  168                 /* If helper is correct class and needs to store OSD... */
  169                 if (h->h_classes & classes && h->h_flags & HELPER_NEEDS_OSD) {
  170                         hdata = uma_zalloc(h->h_zone, M_NOWAIT);
  171                         if (hdata == NULL) {
  172                                 error = ENOMEM;
  173                                 break;
  174                         }
  175                         osd_set(OSD_KHELP, hosd, h->h_id, hdata);
  176                         refcount_acquire(&h->h_refcount);
  177                 }
  178         }
  179 
  180         if (error) {
  181                 /* Delete OSD that was assigned prior to the error. */
  182                 TAILQ_FOREACH(h, &helpers, h_next) {
  183                         if (h->h_classes & classes)
  184                                 khelp_remove_osd(h, hosd);
  185                 }
  186         }
  187         KHELP_LIST_RUNLOCK();
  188 
  189         return (error);
  190 }
  191 
  192 int
  193 khelp_destroy_osd(struct osd *hosd)
  194 {
  195         struct helper *h;
  196         int error;
  197 
  198         KASSERT(hosd != NULL, ("struct osd not initialised!"));
  199 
  200         error = 0;
  201 
  202         KHELP_LIST_RLOCK();
  203         /*
  204          * Clean up all khelp related OSD.
  205          *
  206          * XXXLAS: Would be nice to use something like osd_exit() here but it
  207          * doesn't have the right semantics for this purpose.
  208          */
  209         TAILQ_FOREACH(h, &helpers, h_next)
  210                 khelp_remove_osd(h, hosd);
  211         KHELP_LIST_RUNLOCK();
  212 
  213         return (error);
  214 }
  215 
  216 static inline void
  217 khelp_remove_osd(struct helper *h, struct osd *hosd)
  218 {
  219         void *hdata;
  220 
  221         if (h->h_flags & HELPER_NEEDS_OSD) {
  222                 /*
  223                  * If the current helper uses OSD and calling osd_get()
  224                  * on the helper's h_id returns non-NULL, the helper has
  225                  * OSD attached to 'hosd' which needs to be cleaned up.
  226                  */
  227                 hdata = osd_get(OSD_KHELP, hosd, h->h_id);
  228                 if (hdata != NULL) {
  229                         uma_zfree(h->h_zone, hdata);
  230                         osd_del(OSD_KHELP, hosd, h->h_id);
  231                         refcount_release(&h->h_refcount);
  232                 }
  233         }
  234 }
  235 
  236 void *
  237 khelp_get_osd(struct osd *hosd, int32_t id)
  238 {
  239 
  240         return (osd_get(OSD_KHELP, hosd, id));
  241 }
  242 
  243 int32_t
  244 khelp_get_id(char *hname)
  245 {
  246         struct helper *h;
  247         int32_t id;
  248 
  249         id = -1;
  250 
  251         KHELP_LIST_RLOCK();
  252         TAILQ_FOREACH(h, &helpers, h_next) {
  253                 if (strncmp(h->h_name, hname, HELPER_NAME_MAXLEN) == 0) {
  254                         id = h->h_id;
  255                         break;
  256                 }
  257         }
  258         KHELP_LIST_RUNLOCK();
  259 
  260         return (id);
  261 }
  262 
  263 int
  264 khelp_add_hhook(struct hookinfo *hki, uint32_t flags)
  265 {
  266         VNET_ITERATOR_DECL(vnet_iter);
  267         int error;
  268 
  269         error = 0;
  270 
  271         /*
  272          * XXXLAS: If a helper is dynamically adding a helper hook function at
  273          * runtime using this function, we should update the helper's h_hooks
  274          * struct member to include the additional hookinfo struct.
  275          */
  276 
  277         VNET_LIST_RLOCK_NOSLEEP();
  278         VNET_FOREACH(vnet_iter) {
  279                 CURVNET_SET(vnet_iter);
  280                 error = hhook_add_hook_lookup(hki, flags);
  281                 CURVNET_RESTORE();
  282 #ifdef VIMAGE
  283                 if (error)
  284                         break;
  285 #endif
  286         }
  287         VNET_LIST_RUNLOCK_NOSLEEP();
  288 
  289         return (error);
  290 }
  291 
  292 int
  293 khelp_remove_hhook(struct hookinfo *hki)
  294 {
  295         VNET_ITERATOR_DECL(vnet_iter);
  296         int error;
  297 
  298         error = 0;
  299 
  300         /*
  301          * XXXLAS: If a helper is dynamically removing a helper hook function at
  302          * runtime using this function, we should update the helper's h_hooks
  303          * struct member to remove the defunct hookinfo struct.
  304          */
  305 
  306         VNET_LIST_RLOCK_NOSLEEP();
  307         VNET_FOREACH(vnet_iter) {
  308                 CURVNET_SET(vnet_iter);
  309                 error = hhook_remove_hook_lookup(hki);
  310                 CURVNET_RESTORE();
  311 #ifdef VIMAGE
  312                 if (error)
  313                         break;
  314 #endif
  315         }
  316         VNET_LIST_RUNLOCK_NOSLEEP();
  317 
  318         return (error);
  319 }
  320 
  321 int
  322 khelp_modevent(module_t mod, int event_type, void *data)
  323 {
  324         struct khelp_modevent_data *kmd;
  325         int error;
  326 
  327         kmd = (struct khelp_modevent_data *)data;
  328         error = 0;
  329 
  330         switch(event_type) {
  331         case MOD_LOAD:
  332                 if (kmd->helper->h_flags & HELPER_NEEDS_OSD) {
  333                         if (kmd->uma_zsize <= 0) {
  334                                 printf("Use KHELP_DECLARE_MOD_UMA() instead!\n");
  335                                 error = EDOOFUS;
  336                                 break;
  337                         }
  338                         kmd->helper->h_zone = uma_zcreate(kmd->name,
  339                             kmd->uma_zsize, kmd->umactor, kmd->umadtor, NULL,
  340                             NULL, 0, 0);
  341                         if (kmd->helper->h_zone == NULL) {
  342                                 error = ENOMEM;
  343                                 break;
  344                         }
  345                 }
  346                 strlcpy(kmd->helper->h_name, kmd->name, HELPER_NAME_MAXLEN);
  347                 kmd->helper->h_hooks = kmd->hooks;
  348                 kmd->helper->h_nhooks = kmd->nhooks;
  349                 if (kmd->helper->mod_init != NULL)
  350                         error = kmd->helper->mod_init();
  351                 if (!error)
  352                         error = khelp_register_helper(kmd->helper);
  353                 break;
  354 
  355         case MOD_QUIESCE:
  356         case MOD_SHUTDOWN:
  357         case MOD_UNLOAD:
  358                 error = khelp_deregister_helper(kmd->helper);
  359                 if (!error) {
  360                         if (kmd->helper->h_flags & HELPER_NEEDS_OSD)
  361                                 uma_zdestroy(kmd->helper->h_zone);
  362                         if (kmd->helper->mod_destroy != NULL)
  363                                 kmd->helper->mod_destroy();
  364                 } else if (error == ENOENT)
  365                         /* Do nothing and allow unload if helper not in list. */
  366                         error = 0;
  367                 else if (error == EBUSY)
  368                         printf("Khelp module \"%s\" can't unload until its "
  369                             "refcount drops from %d to 0.\n", kmd->name,
  370                             kmd->helper->h_refcount);
  371                 break;
  372 
  373         default:
  374                 error = EINVAL;
  375                 break;
  376         }
  377 
  378         return (error);
  379 }
  380 
  381 /*
  382  * This function is called in two separate situations:
  383  *
  384  * - When the kernel is booting, it is called directly by the SYSINIT framework
  385  * to allow Khelp modules which were compiled into the kernel or loaded by the
  386  * boot loader to insert their non-virtualised hook functions into the kernel.
  387  *
  388  * - When the kernel is booting or a vnet is created, this function is also
  389  * called indirectly through khelp_vnet_init() by the vnet initialisation code.
  390  * In this situation, Khelp modules are able to insert their virtualised hook
  391  * functions into the virtualised hook points in the vnet which is being
  392  * initialised. In the case where the kernel is not compiled with "options
  393  * VIMAGE", this step is still run once at boot, but the hook functions get
  394  * transparently inserted into the standard unvirtualised network stack.
  395  */
  396 static void
  397 khelp_init(const void *vnet)
  398 {
  399         struct helper *h;
  400         int error, i, vinit;
  401         int32_t htype, hid;
  402 
  403         error = 0;
  404         vinit = vnet != NULL;
  405 
  406         KHELP_LIST_RLOCK();
  407         TAILQ_FOREACH(h, &helpers, h_next) {
  408                 for (i = 0; i < h->h_nhooks && !error; i++) {
  409                         htype = h->h_hooks[i].hook_type;
  410                         hid = h->h_hooks[i].hook_id;
  411 
  412                         /*
  413                          * If we're doing a virtualised init (vinit != 0) and
  414                          * the hook point is virtualised, or we're doing a plain
  415                          * sysinit at boot and the hook point is not
  416                          * virtualised, insert the hook.
  417                          */
  418                         if ((hhook_head_is_virtualised_lookup(htype, hid) ==
  419                             HHOOK_HEADISINVNET && vinit) ||
  420                             (!hhook_head_is_virtualised_lookup(htype, hid) &&
  421                             !vinit)) {
  422                                 error = hhook_add_hook_lookup(&h->h_hooks[i],
  423                                     HHOOK_NOWAIT);
  424                         }
  425                 }
  426 
  427                 if (error) {
  428                          /* Remove any helper's hooks we successfully added. */
  429                         for (i--; i >= 0; i--)
  430                                 hhook_remove_hook_lookup(&h->h_hooks[i]);
  431 
  432                         printf("%s: Failed to add hooks for helper \"%s\" (%p)",
  433                                 __func__, h->h_name, h);
  434                         if (vinit)
  435                                     printf(" to vnet %p.\n", vnet);
  436                         else
  437                                 printf(".\n");
  438 
  439                         error = 0;
  440                 }
  441         }
  442         KHELP_LIST_RUNLOCK();
  443 }
  444 
  445 /*
  446  * Vnet created and being initialised.
  447  */
  448 static void
  449 khelp_vnet_init(const void *unused __unused)
  450 {
  451 
  452         khelp_init(TD_TO_VNET(curthread));
  453 }
  454 
  455 
  456 /*
  457  * As the kernel boots, allow Khelp modules which were compiled into the kernel
  458  * or loaded by the boot loader to insert their non-virtualised hook functions
  459  * into the kernel.
  460  */
  461 SYSINIT(khelp_init, SI_SUB_PROTO_END, SI_ORDER_FIRST, khelp_init, NULL);
  462 
  463 /*
  464  * When a vnet is created and being initialised, we need to insert the helper
  465  * hook functions for all currently registered Khelp modules into the vnet's
  466  * helper hook points.  The hhook KPI provides a mechanism for subsystems which
  467  * export helper hook points to clean up on vnet shutdown, so we don't need a
  468  * VNET_SYSUNINIT for Khelp.
  469  */
  470 VNET_SYSINIT(khelp_vnet_init, SI_SUB_PROTO_END, SI_ORDER_FIRST,
  471     khelp_vnet_init, NULL);

Cache object: 94e0a9ae95961a6c6b9253098967c4a3


[ 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.