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/contrib/openzfs/module/os/freebsd/spl/callb.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  * CDDL HEADER START
    3  *
    4  * The contents of this file are subject to the terms of the
    5  * Common Development and Distribution License (the "License").
    6  * You may not use this file except in compliance with the License.
    7  *
    8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
    9  * or https://opensource.org/licenses/CDDL-1.0.
   10  * See the License for the specific language governing permissions
   11  * and limitations under the License.
   12  *
   13  * When distributing Covered Code, include this CDDL HEADER in each
   14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
   15  * If applicable, add the following below this CDDL HEADER, with the
   16  * fields enclosed by brackets "[]" replaced with your own identifying
   17  * information: Portions Copyright [yyyy] [name of copyright owner]
   18  *
   19  * CDDL HEADER END
   20  */
   21 /*
   22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   23  * Use is subject to license terms.
   24  */
   25 
   26 #include <sys/types.h>
   27 #include <sys/param.h>
   28 #include <sys/time.h>
   29 #include <sys/sysmacros.h>
   30 #include <sys/systm.h>
   31 #include <sys/proc.h>
   32 #include <sys/mutex.h>
   33 #include <sys/condvar.h>
   34 #include <sys/callb.h>
   35 #include <sys/kmem.h>
   36 #include <sys/cmn_err.h>
   37 #include <sys/debug.h>
   38 #include <sys/kobj.h>
   39 #include <sys/systm.h>  /* for delay() */
   40 #include <sys/taskq.h>  /* For TASKQ_NAMELEN */
   41 #include <sys/kernel.h>
   42 
   43 #define CB_MAXNAME      TASKQ_NAMELEN
   44 
   45 /*
   46  * The callb mechanism provides generic event scheduling/echoing.
   47  * A callb function is registered and called on behalf of the event.
   48  */
   49 typedef struct callb {
   50         struct callb    *c_next;        /* next in class or on freelist */
   51         kthread_id_t    c_thread;       /* ptr to caller's thread struct */
   52         char            c_flag;         /* info about the callb state */
   53         uchar_t         c_class;        /* this callb's class */
   54         kcondvar_t      c_done_cv;      /* signal callb completion */
   55         boolean_t       (*c_func)(void *, int);
   56                                         /* cb function: returns true if ok */
   57         void            *c_arg;         /* arg to c_func */
   58         char            c_name[CB_MAXNAME+1]; /* debug:max func name length */
   59 } callb_t;
   60 
   61 /*
   62  * callb c_flag bitmap definitions
   63  */
   64 #define CALLB_FREE              0x0
   65 #define CALLB_TAKEN             0x1
   66 #define CALLB_EXECUTING         0x2
   67 
   68 /*
   69  * Basic structure for a callb table.
   70  * All callbs are organized into different class groups described
   71  * by ct_class array.
   72  * The callbs within a class are single-linked and normally run by a
   73  * serial execution.
   74  */
   75 typedef struct callb_table {
   76         kmutex_t ct_lock;               /* protect all callb states */
   77         callb_t *ct_freelist;           /* free callb structures */
   78         boolean_t ct_busy;              /* B_TRUE prevents additions */
   79         kcondvar_t ct_busy_cv;          /* to wait for not busy    */
   80         int     ct_ncallb;              /* num of callbs allocated */
   81         callb_t *ct_first_cb[NCBCLASS]; /* ptr to 1st callb in a class */
   82 } callb_table_t;
   83 
   84 int callb_timeout_sec = CPR_KTHREAD_TIMEOUT_SEC;
   85 
   86 static callb_id_t callb_add_common(boolean_t (*)(void *, int),
   87     void *, int, char *, kthread_id_t);
   88 
   89 static callb_table_t callb_table;       /* system level callback table */
   90 static callb_table_t *ct = &callb_table;
   91 static kmutex_t callb_safe_mutex;
   92 callb_cpr_t     callb_cprinfo_safe = {
   93         &callb_safe_mutex, CALLB_CPR_ALWAYS_SAFE, 0, {0, 0} };
   94 
   95 /*
   96  * Init all callb tables in the system.
   97  */
   98 static void
   99 callb_init(void *dummy __unused)
  100 {
  101         callb_table.ct_busy = B_FALSE;  /* mark table open for additions */
  102         mutex_init(&callb_safe_mutex, NULL, MUTEX_DEFAULT, NULL);
  103         mutex_init(&callb_table.ct_lock, NULL, MUTEX_DEFAULT, NULL);
  104 }
  105 
  106 static void
  107 callb_fini(void *dummy __unused)
  108 {
  109         callb_t *cp;
  110         int i;
  111 
  112         mutex_enter(&ct->ct_lock);
  113         for (i = 0; i < 16; i++) {
  114                 while ((cp = ct->ct_freelist) != NULL) {
  115                         ct->ct_freelist = cp->c_next;
  116                         ct->ct_ncallb--;
  117                         kmem_free(cp, sizeof (callb_t));
  118                 }
  119                 if (ct->ct_ncallb == 0)
  120                         break;
  121                 /* Not all callbacks finished, waiting for the rest. */
  122                 mutex_exit(&ct->ct_lock);
  123                 tsleep(ct, 0, "callb", hz / 4);
  124                 mutex_enter(&ct->ct_lock);
  125         }
  126         if (ct->ct_ncallb > 0)
  127                 printf("%s: Leaked %d callbacks!\n", __func__, ct->ct_ncallb);
  128         mutex_exit(&ct->ct_lock);
  129         mutex_destroy(&callb_safe_mutex);
  130         mutex_destroy(&callb_table.ct_lock);
  131 }
  132 
  133 /*
  134  * callout_add() is called to register func() be called later.
  135  */
  136 static callb_id_t
  137 callb_add_common(boolean_t (*func)(void *arg, int code),
  138     void *arg, int class, char *name, kthread_id_t t)
  139 {
  140         callb_t *cp;
  141 
  142         ASSERT3S(class, <, NCBCLASS);
  143 
  144         mutex_enter(&ct->ct_lock);
  145         while (ct->ct_busy)
  146                 cv_wait(&ct->ct_busy_cv, &ct->ct_lock);
  147         if ((cp = ct->ct_freelist) == NULL) {
  148                 ct->ct_ncallb++;
  149                 cp = kmem_zalloc(sizeof (callb_t), KM_SLEEP);
  150         }
  151         ct->ct_freelist = cp->c_next;
  152         cp->c_thread = t;
  153         cp->c_func = func;
  154         cp->c_arg = arg;
  155         cp->c_class = (uchar_t)class;
  156         cp->c_flag |= CALLB_TAKEN;
  157 #ifdef ZFS_DEBUG
  158         if (strlen(name) > CB_MAXNAME)
  159                 cmn_err(CE_WARN, "callb_add: name of callback function '%s' "
  160                     "too long -- truncated to %d chars",
  161                     name, CB_MAXNAME);
  162 #endif
  163         (void) strlcpy(cp->c_name, name, sizeof (cp->c_name));
  164 
  165         /*
  166          * Insert the new callb at the head of its class list.
  167          */
  168         cp->c_next = ct->ct_first_cb[class];
  169         ct->ct_first_cb[class] = cp;
  170 
  171         mutex_exit(&ct->ct_lock);
  172         return ((callb_id_t)cp);
  173 }
  174 
  175 /*
  176  * The default function to add an entry to the callback table.  Since
  177  * it uses curthread as the thread identifier to store in the table,
  178  * it should be used for the normal case of a thread which is calling
  179  * to add ITSELF to the table.
  180  */
  181 callb_id_t
  182 callb_add(boolean_t (*func)(void *arg, int code),
  183     void *arg, int class, char *name)
  184 {
  185         return (callb_add_common(func, arg, class, name, curthread));
  186 }
  187 
  188 /*
  189  * A special version of callb_add() above for use by threads which
  190  * might be adding an entry to the table on behalf of some other
  191  * thread (for example, one which is constructed but not yet running).
  192  * In this version the thread id is an argument.
  193  */
  194 callb_id_t
  195 callb_add_thread(boolean_t (*func)(void *arg, int code),
  196     void *arg, int class, char *name, kthread_id_t t)
  197 {
  198         return (callb_add_common(func, arg, class, name, t));
  199 }
  200 
  201 /*
  202  * callout_delete() is called to remove an entry identified by id
  203  * that was originally placed there by a call to callout_add().
  204  * return -1 if fail to delete a callb entry otherwise return 0.
  205  */
  206 int
  207 callb_delete(callb_id_t id)
  208 {
  209         callb_t **pp;
  210         callb_t *me = (callb_t *)id;
  211 
  212         mutex_enter(&ct->ct_lock);
  213 
  214         for (;;) {
  215                 pp = &ct->ct_first_cb[me->c_class];
  216                 while (*pp != NULL && *pp != me)
  217                         pp = &(*pp)->c_next;
  218 
  219 #ifdef ZFS_DEBUG
  220                 if (*pp != me) {
  221                         cmn_err(CE_WARN, "callb delete bogus entry 0x%p",
  222                             (void *)me);
  223                         mutex_exit(&ct->ct_lock);
  224                         return (-1);
  225                 }
  226 #endif /* DEBUG */
  227 
  228                 /*
  229                  * It is not allowed to delete a callb in the middle of
  230                  * executing otherwise, the callb_execute() will be confused.
  231                  */
  232                 if (!(me->c_flag & CALLB_EXECUTING))
  233                         break;
  234 
  235                 cv_wait(&me->c_done_cv, &ct->ct_lock);
  236         }
  237         /* relink the class list */
  238         *pp = me->c_next;
  239 
  240         /* clean up myself and return the free callb to the head of freelist */
  241         me->c_flag = CALLB_FREE;
  242         me->c_next = ct->ct_freelist;
  243         ct->ct_freelist = me;
  244 
  245         mutex_exit(&ct->ct_lock);
  246         return (0);
  247 }
  248 
  249 /*
  250  * class:       indicates to execute all callbs in the same class;
  251  * code:        optional argument for the callb functions.
  252  * return:       = 0: success
  253  *              != 0: ptr to string supplied when callback was registered
  254  */
  255 void *
  256 callb_execute_class(int class, int code)
  257 {
  258         callb_t *cp;
  259         void *ret = NULL;
  260 
  261         ASSERT3S(class, <, NCBCLASS);
  262 
  263         mutex_enter(&ct->ct_lock);
  264 
  265         for (cp = ct->ct_first_cb[class];
  266             cp != NULL && ret == NULL; cp = cp->c_next) {
  267                 while (cp->c_flag & CALLB_EXECUTING)
  268                         cv_wait(&cp->c_done_cv, &ct->ct_lock);
  269                 /*
  270                  * cont if the callb is deleted while we're sleeping
  271                  */
  272                 if (cp->c_flag == CALLB_FREE)
  273                         continue;
  274                 cp->c_flag |= CALLB_EXECUTING;
  275 
  276 #ifdef CALLB_DEBUG
  277                 printf("callb_execute: name=%s func=%p arg=%p\n",
  278                     cp->c_name, (void *)cp->c_func, (void *)cp->c_arg);
  279 #endif /* CALLB_DEBUG */
  280 
  281                 mutex_exit(&ct->ct_lock);
  282                 /* If callback function fails, pass back client's name */
  283                 if (!(*cp->c_func)(cp->c_arg, code))
  284                         ret = cp->c_name;
  285                 mutex_enter(&ct->ct_lock);
  286 
  287                 cp->c_flag &= ~CALLB_EXECUTING;
  288                 cv_broadcast(&cp->c_done_cv);
  289         }
  290         mutex_exit(&ct->ct_lock);
  291         return (ret);
  292 }
  293 
  294 /*
  295  * callers make sure no recursive entries to this func.
  296  * dp->cc_lockp is registered by callb_add to protect callb_cpr_t structure.
  297  *
  298  * When calling to stop a kernel thread (code == CB_CODE_CPR_CHKPT) we
  299  * use a cv_timedwait() in case the kernel thread is blocked.
  300  *
  301  * Note that this is a generic callback handler for daemon CPR and
  302  * should NOT be changed to accommodate any specific requirement in a daemon.
  303  * Individual daemons that require changes to the handler shall write
  304  * callback routines in their own daemon modules.
  305  */
  306 boolean_t
  307 callb_generic_cpr(void *arg, int code)
  308 {
  309         callb_cpr_t *cp = (callb_cpr_t *)arg;
  310         clock_t ret = 0;                        /* assume success */
  311 
  312         mutex_enter(cp->cc_lockp);
  313 
  314         switch (code) {
  315         case CB_CODE_CPR_CHKPT:
  316                 cp->cc_events |= CALLB_CPR_START;
  317 #ifdef CPR_NOT_THREAD_SAFE
  318                 while (!(cp->cc_events & CALLB_CPR_SAFE))
  319                         /* cv_timedwait() returns -1 if it times out. */
  320                         if ((ret = cv_reltimedwait(&cp->cc_callb_cv,
  321                             cp->cc_lockp, (callb_timeout_sec * hz),
  322                             TR_CLOCK_TICK)) == -1)
  323                                 break;
  324 #endif
  325                 break;
  326 
  327         case CB_CODE_CPR_RESUME:
  328                 cp->cc_events &= ~CALLB_CPR_START;
  329                 cv_signal(&cp->cc_stop_cv);
  330                 break;
  331         }
  332         mutex_exit(cp->cc_lockp);
  333         return (ret != -1);
  334 }
  335 
  336 /*
  337  * The generic callback function associated with kernel threads which
  338  * are always considered safe.
  339  */
  340 boolean_t
  341 callb_generic_cpr_safe(void *arg, int code)
  342 {
  343         (void) arg, (void) code;
  344         return (B_TRUE);
  345 }
  346 /*
  347  * Prevent additions to callback table.
  348  */
  349 void
  350 callb_lock_table(void)
  351 {
  352         mutex_enter(&ct->ct_lock);
  353         ASSERT(!ct->ct_busy);
  354         ct->ct_busy = B_TRUE;
  355         mutex_exit(&ct->ct_lock);
  356 }
  357 
  358 /*
  359  * Allow additions to callback table.
  360  */
  361 void
  362 callb_unlock_table(void)
  363 {
  364         mutex_enter(&ct->ct_lock);
  365         ASSERT(ct->ct_busy);
  366         ct->ct_busy = B_FALSE;
  367         cv_broadcast(&ct->ct_busy_cv);
  368         mutex_exit(&ct->ct_lock);
  369 }
  370 
  371 SYSINIT(sol_callb, SI_SUB_DRIVERS, SI_ORDER_FIRST, callb_init, NULL);
  372 SYSUNINIT(sol_callb, SI_SUB_DRIVERS, SI_ORDER_FIRST, callb_fini, NULL);

Cache object: 6354794b9cfd89a765cc202ca2a200a8


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