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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2010,2013 Lawrence Stewart <lstewart@freebsd.org>
    5  * Copyright (c) 2010 The FreeBSD Foundation
    6  * All rights reserved.
    7  *
    8  * This software was developed by Lawrence Stewart while studying at the Centre
    9  * for Advanced Internet Architectures, Swinburne University of Technology,
   10  * made possible in part by grants from the FreeBSD Foundation and Cisco
   11  * University Research Program Fund at Community Foundation Silicon Valley.
   12  *
   13  * Portions of this software were developed at the Centre for Advanced
   14  * Internet Architectures, Swinburne University of Technology, Melbourne,
   15  * Australia by Lawrence Stewart under sponsorship from the FreeBSD Foundation.
   16  *
   17  * Redistribution and use in source and binary forms, with or without
   18  * modification, are permitted provided that the following conditions
   19  * are met:
   20  * 1. Redistributions of source code must retain the above copyright
   21  *    notice, this list of conditions and the following disclaimer.
   22  * 2. Redistributions in binary form must reproduce the above copyright
   23  *    notice, this list of conditions and the following disclaimer in the
   24  *    documentation and/or other materials provided with the distribution.
   25  *
   26  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   29  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   36  * SUCH DAMAGE.
   37  */
   38 
   39 #include <sys/cdefs.h>
   40 __FBSDID("$FreeBSD: releng/12.0/sys/kern/kern_khelp.c 326271 2017-11-27 15:20:12Z pfg $");
   41 
   42 #include <sys/param.h>
   43 #include <sys/kernel.h>
   44 #include <sys/hhook.h>
   45 #include <sys/khelp.h>
   46 #include <sys/lock.h>
   47 #include <sys/malloc.h>
   48 #include <sys/module.h>
   49 #include <sys/module_khelp.h>
   50 #include <sys/osd.h>
   51 #include <sys/queue.h>
   52 #include <sys/refcount.h>
   53 #include <sys/rwlock.h>
   54 #include <sys/systm.h>
   55 
   56 static struct rwlock khelp_list_lock;
   57 RW_SYSINIT(khelplistlock, &khelp_list_lock, "helper list lock");
   58 
   59 static TAILQ_HEAD(helper_head, helper) helpers = TAILQ_HEAD_INITIALIZER(helpers);
   60 
   61 /* Private function prototypes. */
   62 static inline void khelp_remove_osd(struct helper *h, struct osd *hosd);
   63 void khelp_new_hhook_registered(struct hhook_head *hhh, uint32_t flags);
   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 = inserted = 0;
   78         refcount_init(&h->h_refcount, 0);
   79         h->h_id = osd_register(OSD_KHELP, NULL, NULL);
   80 
   81         /* It's only safe to add the hooks after osd_register(). */
   82         for (i = 0; i < h->h_nhooks && !error; i++) {
   83                 /* We don't require the module to assign hook_helper. */
   84                 h->h_hooks[i].hook_helper = h;
   85                 error = hhook_add_hook_lookup(&h->h_hooks[i], HHOOK_WAITOK);
   86                 if (error)
   87                         printf("%s: \"%s\" khelp module unable to "
   88                             "hook type %d id %d due to error %d\n", __func__,
   89                             h->h_name, h->h_hooks[i].hook_type,
   90                             h->h_hooks[i].hook_id, error);
   91         }
   92 
   93         if (error) {
   94                 for (i--; i >= 0; i--)
   95                         hhook_remove_hook_lookup(&h->h_hooks[i]);
   96                 osd_deregister(OSD_KHELP, h->h_id);
   97         } else {
   98                 KHELP_LIST_WLOCK();
   99                 /*
  100                  * Keep list of helpers sorted in descending h_id order. Due to
  101                  * the way osd_set() works, a sorted list ensures
  102                  * khelp_init_osd() will operate with improved efficiency.
  103                  */
  104                 TAILQ_FOREACH(tmph, &helpers, h_next) {
  105                         if (tmph->h_id < h->h_id) {
  106                                 TAILQ_INSERT_BEFORE(tmph, h, h_next);
  107                                 inserted = 1;
  108                                 break;
  109                         }
  110                 }
  111 
  112                 if (!inserted)
  113                         TAILQ_INSERT_TAIL(&helpers, h, h_next);
  114                 KHELP_LIST_WUNLOCK();
  115         }
  116 
  117         return (error);
  118 }
  119 
  120 int
  121 khelp_deregister_helper(struct helper *h)
  122 {
  123         struct helper *tmph;
  124         int error, i;
  125 
  126         KHELP_LIST_WLOCK();
  127         if (h->h_refcount > 0)
  128                 error = EBUSY;
  129         else {
  130                 error = ENOENT;
  131                 TAILQ_FOREACH(tmph, &helpers, h_next) {
  132                         if (tmph == h) {
  133                                 TAILQ_REMOVE(&helpers, h, h_next);
  134                                 error = 0;
  135                                 break;
  136                         }
  137                 }
  138         }
  139         KHELP_LIST_WUNLOCK();
  140 
  141         if (!error) {
  142                 for (i = 0; i < h->h_nhooks; i++)
  143                         hhook_remove_hook_lookup(&h->h_hooks[i]);
  144                 osd_deregister(OSD_KHELP, h->h_id);
  145         }
  146 
  147         return (error);
  148 }
  149 
  150 int
  151 khelp_init_osd(uint32_t classes, struct osd *hosd)
  152 {
  153         struct helper *h;
  154         void *hdata;
  155         int error;
  156 
  157         KASSERT(hosd != NULL, ("struct osd not initialised!"));
  158 
  159         error = 0;
  160 
  161         KHELP_LIST_RLOCK();
  162         TAILQ_FOREACH(h, &helpers, h_next) {
  163                 /* If helper is correct class and needs to store OSD... */
  164                 if (h->h_classes & classes && h->h_flags & HELPER_NEEDS_OSD) {
  165                         hdata = uma_zalloc(h->h_zone, M_NOWAIT);
  166                         if (hdata == NULL) {
  167                                 error = ENOMEM;
  168                                 break;
  169                         }
  170                         osd_set(OSD_KHELP, hosd, h->h_id, hdata);
  171                         refcount_acquire(&h->h_refcount);
  172                 }
  173         }
  174 
  175         if (error) {
  176                 /* Delete OSD that was assigned prior to the error. */
  177                 TAILQ_FOREACH(h, &helpers, h_next) {
  178                         if (h->h_classes & classes)
  179                                 khelp_remove_osd(h, hosd);
  180                 }
  181         }
  182         KHELP_LIST_RUNLOCK();
  183 
  184         return (error);
  185 }
  186 
  187 int
  188 khelp_destroy_osd(struct osd *hosd)
  189 {
  190         struct helper *h;
  191         int error;
  192 
  193         KASSERT(hosd != NULL, ("struct osd not initialised!"));
  194 
  195         error = 0;
  196 
  197         KHELP_LIST_RLOCK();
  198         /*
  199          * Clean up all khelp related OSD.
  200          *
  201          * XXXLAS: Would be nice to use something like osd_exit() here but it
  202          * doesn't have the right semantics for this purpose.
  203          */
  204         TAILQ_FOREACH(h, &helpers, h_next)
  205                 khelp_remove_osd(h, hosd);
  206         KHELP_LIST_RUNLOCK();
  207 
  208         return (error);
  209 }
  210 
  211 static inline void
  212 khelp_remove_osd(struct helper *h, struct osd *hosd)
  213 {
  214         void *hdata;
  215 
  216         if (h->h_flags & HELPER_NEEDS_OSD) {
  217                 /*
  218                  * If the current helper uses OSD and calling osd_get()
  219                  * on the helper's h_id returns non-NULL, the helper has
  220                  * OSD attached to 'hosd' which needs to be cleaned up.
  221                  */
  222                 hdata = osd_get(OSD_KHELP, hosd, h->h_id);
  223                 if (hdata != NULL) {
  224                         uma_zfree(h->h_zone, hdata);
  225                         osd_del(OSD_KHELP, hosd, h->h_id);
  226                         refcount_release(&h->h_refcount);
  227                 }
  228         }
  229 }
  230 
  231 void *
  232 khelp_get_osd(struct osd *hosd, int32_t id)
  233 {
  234 
  235         return (osd_get(OSD_KHELP, hosd, id));
  236 }
  237 
  238 int32_t
  239 khelp_get_id(char *hname)
  240 {
  241         struct helper *h;
  242         int32_t id;
  243 
  244         id = -1;
  245 
  246         KHELP_LIST_RLOCK();
  247         TAILQ_FOREACH(h, &helpers, h_next) {
  248                 if (strncmp(h->h_name, hname, HELPER_NAME_MAXLEN) == 0) {
  249                         id = h->h_id;
  250                         break;
  251                 }
  252         }
  253         KHELP_LIST_RUNLOCK();
  254 
  255         return (id);
  256 }
  257 
  258 int
  259 khelp_add_hhook(struct hookinfo *hki, uint32_t flags)
  260 {
  261         int error;
  262 
  263         /*
  264          * XXXLAS: Should probably include the functionality to update the
  265          * helper's h_hooks struct member.
  266          */
  267         error = hhook_add_hook_lookup(hki, flags);
  268 
  269         return (error);
  270 }
  271 
  272 int
  273 khelp_remove_hhook(struct hookinfo *hki)
  274 {
  275         int error;
  276 
  277         /*
  278          * XXXLAS: Should probably include the functionality to update the
  279          * helper's h_hooks struct member.
  280          */
  281         error = hhook_remove_hook_lookup(hki);
  282 
  283         return (error);
  284 }
  285 
  286 /*
  287  * Private KPI between hhook and khelp that allows khelp modules to insert hook
  288  * functions into hhook points which register after the modules were loaded.
  289  */
  290 void
  291 khelp_new_hhook_registered(struct hhook_head *hhh, uint32_t flags)
  292 {
  293         struct helper *h;
  294         int error, i;
  295 
  296         KHELP_LIST_RLOCK();
  297         TAILQ_FOREACH(h, &helpers, h_next) {
  298                 for (i = 0; i < h->h_nhooks; i++) {
  299                         if (hhh->hhh_type != h->h_hooks[i].hook_type ||
  300                             hhh->hhh_id != h->h_hooks[i].hook_id)
  301                                 continue;
  302                         error = hhook_add_hook(hhh, &h->h_hooks[i], flags);
  303                         if (error) {
  304                                 printf("%s: \"%s\" khelp module unable to "
  305                                     "hook type %d id %d due to error %d\n",
  306                                     __func__, h->h_name,
  307                                     h->h_hooks[i].hook_type,
  308                                     h->h_hooks[i].hook_id, error);
  309                                 error = 0;
  310                         }
  311                 }
  312         }
  313         KHELP_LIST_RUNLOCK();
  314 }
  315 
  316 int
  317 khelp_modevent(module_t mod, int event_type, void *data)
  318 {
  319         struct khelp_modevent_data *kmd;
  320         int error;
  321 
  322         kmd = (struct khelp_modevent_data *)data;
  323         error = 0;
  324 
  325         switch(event_type) {
  326         case MOD_LOAD:
  327                 if (kmd->helper->h_flags & HELPER_NEEDS_OSD) {
  328                         if (kmd->uma_zsize <= 0) {
  329                                 printf("Use KHELP_DECLARE_MOD_UMA() instead!\n");
  330                                 error = EDOOFUS;
  331                                 break;
  332                         }
  333                         kmd->helper->h_zone = uma_zcreate(kmd->name,
  334                             kmd->uma_zsize, kmd->umactor, kmd->umadtor, NULL,
  335                             NULL, 0, 0);
  336                         if (kmd->helper->h_zone == NULL) {
  337                                 error = ENOMEM;
  338                                 break;
  339                         }
  340                 }
  341                 strlcpy(kmd->helper->h_name, kmd->name, HELPER_NAME_MAXLEN);
  342                 kmd->helper->h_hooks = kmd->hooks;
  343                 kmd->helper->h_nhooks = kmd->nhooks;
  344                 if (kmd->helper->mod_init != NULL)
  345                         error = kmd->helper->mod_init();
  346                 if (!error)
  347                         error = khelp_register_helper(kmd->helper);
  348                 break;
  349 
  350         case MOD_QUIESCE:
  351         case MOD_SHUTDOWN:
  352         case MOD_UNLOAD:
  353                 error = khelp_deregister_helper(kmd->helper);
  354                 if (!error) {
  355                         if (kmd->helper->h_flags & HELPER_NEEDS_OSD)
  356                                 uma_zdestroy(kmd->helper->h_zone);
  357                         if (kmd->helper->mod_destroy != NULL)
  358                                 kmd->helper->mod_destroy();
  359                 } else if (error == ENOENT)
  360                         /* Do nothing and allow unload if helper not in list. */
  361                         error = 0;
  362                 else if (error == EBUSY)
  363                         printf("Khelp module \"%s\" can't unload until its "
  364                             "refcount drops from %d to 0.\n", kmd->name,
  365                             kmd->helper->h_refcount);
  366                 break;
  367 
  368         default:
  369                 error = EINVAL;
  370                 break;
  371         }
  372 
  373         return (error);
  374 }

Cache object: a305ef24fcb398d6fc7e3784f08fa050


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