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/subr_specificdata.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 /*      $NetBSD: subr_specificdata.c,v 1.7 2006/11/01 10:17:59 yamt Exp $       */
    2 
    3 /*-
    4  * Copyright (c) 2006 The NetBSD Foundation, Inc.
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The NetBSD Foundation
    8  * by Jason R. Thorpe.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  * 3. All advertising materials mentioning features or use of this software
   19  *    must display the following acknowledgement:
   20  *      This product includes software developed by the NetBSD
   21  *      Foundation, Inc. and its contributors.
   22  * 4. Neither the name of The NetBSD Foundation nor the names of its
   23  *    contributors may be used to endorse or promote products derived
   24  *    from this software without specific prior written permission.
   25  *
   26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   36  * POSSIBILITY OF SUCH DAMAGE.
   37  */
   38 
   39 /*-
   40  * Copyright (c) 2006 YAMAMOTO Takashi.
   41  * All rights reserved.
   42  *
   43  * Redistribution and use in source and binary forms, with or without
   44  * modification, are permitted provided that the following conditions
   45  * are met:
   46  * 1. Redistributions of source code must retain the above copyright
   47  *    notice, this list of conditions and the following disclaimer.
   48  * 2. Redistributions in binary form must reproduce the above copyright
   49  *    notice, this list of conditions and the following disclaimer in the
   50  *    documentation and/or other materials provided with the distribution.
   51  *
   52  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   54  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   55  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   56  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   57  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   58  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   59  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   60  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   61  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   62  * SUCH DAMAGE.
   63  */
   64 
   65 #include <sys/cdefs.h>
   66 __KERNEL_RCSID(0, "$NetBSD: subr_specificdata.c,v 1.7 2006/11/01 10:17:59 yamt Exp $");
   67 
   68 #include <sys/param.h>
   69 #include <sys/kmem.h>
   70 #include <sys/proc.h>
   71 #include <sys/specificdata.h>
   72 #include <sys/queue.h>
   73 
   74 /*
   75  * Locking notes:
   76  *
   77  * The specdataref_container pointer in the specificdata_reference
   78  * is volatile.  To read it, you must hold EITHER the domain lock
   79  * or the ref lock.  To write it, you must hold BOTH the domain lock
   80  * and the ref lock.  The locks must be acquired in the following
   81  * order:
   82  *      domain -> ref
   83  */
   84 
   85 typedef struct {
   86         specificdata_dtor_t     ski_dtor;
   87 } specificdata_key_impl;
   88 
   89 struct specificdata_container {
   90         size_t          sc_nkey;
   91         LIST_ENTRY(specificdata_container) sc_list;
   92         void *          sc_data[];      /* variable length */
   93 };
   94 
   95 #define SPECIFICDATA_CONTAINER_BYTESIZE(n)              \
   96         (sizeof(struct specificdata_container) + ((n) * sizeof(void *)))
   97 
   98 struct specificdata_domain {
   99         struct lock     sd_lock;
  100         unsigned int    sd_nkey;
  101         LIST_HEAD(, specificdata_container) sd_list;
  102         specificdata_key_impl *sd_keys;
  103 };
  104 
  105 #define specdataref_lock_init(ref)                      \
  106                                 simple_lock_init(&(ref)->specdataref_slock)
  107 #define specdataref_lock(ref)   simple_lock(&(ref)->specdataref_slock)
  108 #define specdataref_unlock(ref) simple_unlock(&(ref)->specdataref_slock)
  109 
  110 static void
  111 specificdata_domain_lock(specificdata_domain_t sd)
  112 {
  113 
  114         ASSERT_SLEEPABLE(NULL, __func__);
  115         lockmgr(&sd->sd_lock, LK_EXCLUSIVE, 0);
  116 }
  117 
  118 static void
  119 specificdata_domain_unlock(specificdata_domain_t sd)
  120 {
  121 
  122         lockmgr(&sd->sd_lock, LK_RELEASE, 0);
  123 }
  124 
  125 static void
  126 specificdata_container_link(specificdata_domain_t sd,
  127                             specificdata_container_t sc)
  128 {
  129 
  130         LIST_INSERT_HEAD(&sd->sd_list, sc, sc_list);
  131 }
  132 
  133 static void
  134 specificdata_container_unlink(specificdata_domain_t sd,
  135                               specificdata_container_t sc)
  136 {
  137 
  138         LIST_REMOVE(sc, sc_list);
  139 }
  140 
  141 static void
  142 specificdata_destroy_datum(specificdata_domain_t sd,
  143                            specificdata_container_t sc, specificdata_key_t key)
  144 {
  145         specificdata_dtor_t dtor;
  146         void *data;
  147 
  148         if (key >= sc->sc_nkey)
  149                 return;
  150 
  151         KASSERT(key < sd->sd_nkey);
  152         
  153         data = sc->sc_data[key];
  154         dtor = sd->sd_keys[key].ski_dtor;
  155 
  156         if (dtor != NULL) {
  157                 if (data != NULL) {
  158                         sc->sc_data[key] = NULL;
  159                         (*dtor)(data);
  160                 }
  161         } else {
  162                 KASSERT(data == NULL);
  163         }
  164 }
  165 
  166 static void
  167 specificdata_noop_dtor(void *data)
  168 {
  169 
  170         /* nothing */
  171 }
  172 
  173 /*
  174  * specificdata_domain_create --
  175  *      Create a specifidata domain.
  176  */
  177 specificdata_domain_t
  178 specificdata_domain_create(void)
  179 {
  180         specificdata_domain_t sd;
  181 
  182         sd = kmem_zalloc(sizeof(*sd), KM_SLEEP);
  183         KASSERT(sd != NULL);
  184         lockinit(&sd->sd_lock, PLOCK, "specdata", 0, 0);
  185         LIST_INIT(&sd->sd_list);
  186 
  187         return (sd);
  188 }
  189 
  190 /*
  191  * specificdata_domain_delete --
  192  *      Destroy a specifidata domain.
  193  */
  194 void
  195 specificdata_domain_delete(specificdata_domain_t sd)
  196 {
  197 
  198         panic("specificdata_domain_delete: not implemented");
  199 }
  200 
  201 /*
  202  * specificdata_key_create --
  203  *      Create a specificdata key for a domain.
  204  *
  205  *      Note: This is a rare operation.
  206  */
  207 int
  208 specificdata_key_create(specificdata_domain_t sd, specificdata_key_t *keyp,
  209                         specificdata_dtor_t dtor)
  210 {
  211         specificdata_key_impl *newkeys;
  212         specificdata_key_t key = 0;
  213         size_t nsz;
  214 
  215         ASSERT_SLEEPABLE(NULL, __func__);
  216 
  217         if (dtor == NULL)
  218                 dtor = specificdata_noop_dtor;
  219         
  220         specificdata_domain_lock(sd);
  221 
  222         if (sd->sd_keys == NULL)
  223                 goto needalloc;
  224 
  225         for (; key < sd->sd_nkey; key++) {
  226                 if (sd->sd_keys[key].ski_dtor == NULL)
  227                         goto gotit;
  228         }
  229 
  230  needalloc:
  231         nsz = (sd->sd_nkey + 1) * sizeof(*newkeys);
  232         newkeys = kmem_zalloc(nsz, KM_SLEEP);
  233         KASSERT(newkeys != NULL);
  234         if (sd->sd_keys != NULL) {
  235                 size_t osz = sd->sd_nkey * sizeof(*newkeys);
  236                 memcpy(newkeys, sd->sd_keys, osz);
  237                 kmem_free(sd->sd_keys, osz);
  238         }
  239         sd->sd_keys = newkeys;
  240         sd->sd_nkey++;
  241  gotit:
  242         sd->sd_keys[key].ski_dtor = dtor;
  243 
  244         specificdata_domain_unlock(sd);
  245 
  246         *keyp = key;
  247         return (0);
  248 }
  249 
  250 /*
  251  * specificdata_key_delete --
  252  *      Destroy a specificdata key for a domain.
  253  *
  254  *      Note: This is a rare operation.
  255  */
  256 void
  257 specificdata_key_delete(specificdata_domain_t sd, specificdata_key_t key)
  258 {
  259         specificdata_container_t sc;
  260 
  261         specificdata_domain_lock(sd);
  262 
  263         if (key >= sd->sd_nkey)
  264                 goto out;
  265 
  266         /*
  267          * Traverse all of the specificdata containers in the domain
  268          * and the destroy the datum for the dying key.
  269          */
  270         LIST_FOREACH(sc, &sd->sd_list, sc_list) {
  271                 specificdata_destroy_datum(sd, sc, key);
  272         }
  273 
  274         sd->sd_keys[key].ski_dtor = NULL;
  275 
  276  out:
  277         specificdata_domain_unlock(sd);
  278 }
  279 
  280 /*
  281  * specificdata_init --
  282  *      Initialize a specificdata container for operation in the
  283  *      specified domain.
  284  */
  285 int
  286 specificdata_init(specificdata_domain_t sd, specificdata_reference *ref)
  287 {
  288 
  289         /*
  290          * Just NULL-out the container pointer; we'll allocate the
  291          * container the first time specificdata is put into it.
  292          */
  293         ref->specdataref_container = NULL;
  294         specdataref_lock_init(ref);
  295 
  296         return (0);
  297 }
  298 
  299 /*
  300  * specificdata_fini --
  301  *      Destroy a specificdata container.  We destroy all of the datums
  302  *      stuffed into the container just as if the key were destroyed.
  303  */
  304 void
  305 specificdata_fini(specificdata_domain_t sd, specificdata_reference *ref)
  306 {
  307         specificdata_container_t sc;
  308         specificdata_key_t key;
  309 
  310         ASSERT_SLEEPABLE(NULL, __func__);
  311 
  312         sc = ref->specdataref_container;
  313         if (sc == NULL)
  314                 return;
  315         ref->specdataref_container = NULL;
  316         
  317         specificdata_domain_lock(sd);
  318 
  319         specificdata_container_unlink(sd, sc);
  320         for (key = 0; key < sc->sc_nkey; key++) {
  321                 specificdata_destroy_datum(sd, sc, key);
  322         }
  323 
  324         specificdata_domain_unlock(sd);
  325 
  326         kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey));
  327 }
  328 
  329 /*
  330  * specificdata_getspecific --
  331  *      Get a datum from a container.
  332  *
  333  *      Note: This routine is guaranteed not to sleep.
  334  */
  335 void *
  336 specificdata_getspecific(specificdata_domain_t sd, specificdata_reference *ref,
  337                          specificdata_key_t key)
  338 {
  339         specificdata_container_t sc;
  340         void *data = NULL;
  341 
  342         specdataref_lock(ref);
  343 
  344         sc = ref->specdataref_container;
  345         if (sc != NULL && key < sc->sc_nkey)
  346                 data = sc->sc_data[key];
  347 
  348         specdataref_unlock(ref);
  349 
  350         return (data);
  351 }
  352 
  353 /*
  354  * specificdata_getspecific_unlocked --
  355  *      Get a datum from a container in a lockless fashion.
  356  *
  357  *      Note: When using this routine, care must be taken to ensure
  358  *      that no other thread could cause the specificdata_reference
  359  *      to become invalid (i.e. point at the wrong container) by
  360  *      issuing a setspecific call or destroying the container.
  361  *
  362  *      Note #2: This routine is guaranteed not to sleep.
  363  */
  364 void *
  365 specificdata_getspecific_unlocked(specificdata_domain_t sd,
  366                                   specificdata_reference *ref,
  367                                   specificdata_key_t key)
  368 {
  369         specificdata_container_t sc;
  370         
  371         sc = ref->specdataref_container;
  372         if (sc != NULL && key < sc->sc_nkey)
  373                 return (sc->sc_data[key]);
  374 
  375         return (NULL);
  376 }
  377 
  378 /*
  379  * specificdata_setspecific --
  380  *      Put a datum into a container.
  381  */
  382 void
  383 specificdata_setspecific(specificdata_domain_t sd,
  384                          specificdata_reference *ref,
  385                          specificdata_key_t key, void *data)
  386 {
  387         specificdata_container_t sc, newsc;
  388         size_t newnkey, sz;
  389 
  390         ASSERT_SLEEPABLE(NULL, __func__);
  391 
  392         specdataref_lock(ref);
  393 
  394         sc = ref->specdataref_container;
  395         if (__predict_true(sc != NULL && key < sc->sc_nkey)) {
  396                 sc->sc_data[key] = data;
  397                 specdataref_unlock(ref);
  398                 return;
  399         }
  400 
  401         specdataref_unlock(ref);
  402 
  403         /*
  404          * Slow path: need to resize.
  405          */
  406         
  407         specificdata_domain_lock(sd);
  408         newnkey = sd->sd_nkey;
  409         if (key >= newnkey) {
  410                 specificdata_domain_unlock(sd);
  411                 panic("specificdata_setspecific");
  412         }
  413         sz = SPECIFICDATA_CONTAINER_BYTESIZE(newnkey);
  414         newsc = kmem_zalloc(sz, KM_SLEEP);
  415         KASSERT(newsc != NULL);
  416         newsc->sc_nkey = newnkey;
  417 
  418         specdataref_lock(ref);
  419 
  420         sc = ref->specdataref_container;
  421         if (sc != NULL) {
  422                 if (key < sc->sc_nkey) {
  423                         /*
  424                          * Someone beat us to the punch.  Unwind and put
  425                          * the object into the now large enough container.
  426                          */
  427                         sc->sc_data[key] = data;
  428                         specdataref_unlock(ref);
  429                         specificdata_domain_unlock(sd);
  430                         kmem_free(newsc, sz);
  431                         return;
  432                 }
  433                 specificdata_container_unlink(sd, sc);
  434                 memcpy(newsc->sc_data, sc->sc_data,
  435                        sc->sc_nkey * sizeof(void *));
  436         }
  437         newsc->sc_data[key] = data;
  438         specificdata_container_link(sd, newsc);
  439         ref->specdataref_container = newsc;
  440 
  441         specdataref_unlock(ref);
  442         specificdata_domain_unlock(sd);
  443 
  444         if (sc != NULL)
  445                 kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey));
  446 }

Cache object: 0c04517897024542db51a6b83c11011a


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