2 * Copyright (c) 2007 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * System resource control module for all cluster-addressable system resource
38 * This module implements the core ref counting, sysid registration, and
39 * objcache-backed allocation mechanism for all major system resource
42 * sysid registrations operate via the objcache ctor/dtor mechanism and
43 * sysids will be reused if the resource is not explicitly accessed via
44 * its sysid. This removes all RB tree handling overhead from the critical
45 * path for locally used resources.
48 #include <sys/param.h>
49 #include <sys/systm.h>
50 #include <sys/kernel.h>
52 #include <sys/spinlock.h>
53 #include <machine/atomic.h>
54 #include <machine/cpufunc.h>
56 #include <sys/spinlock2.h>
57 #include <sys/sysref2.h>
59 static boolean_t
sysref_ctor(void *data
, void *privdata
, int ocflags
);
60 static void sysref_dtor(void *data
, void *privdata
);
63 * Red-Black tree support
65 static int rb_sysref_compare(struct sysref
*sr1
, struct sysref
*sr2
);
66 RB_GENERATE2(sysref_rb_tree
, sysref
, rbnode
, rb_sysref_compare
, sysid_t
, sysid
);
68 static struct srpercpu
{
69 struct sysref_rb_tree rbtree
;
71 } sysref_array
[MAXCPU
];
74 sysrefbootinit(void *dummy __unused
)
79 for (i
= 0; i
< ncpus
; ++i
) {
80 sa
= &sysref_array
[i
];
81 spin_init(&sa
->spin
, "sysrefbootinit");
86 SYSINIT(sysref
, SI_BOOT2_MACHDEP
, SI_ORDER_ANY
, sysrefbootinit
, NULL
);
90 rb_sysref_compare(struct sysref
*sr1
, struct sysref
*sr2
)
92 if (sr1
->sysid
< sr2
->sysid
)
94 if (sr1
->sysid
> sr2
->sysid
)
100 * Manual initialization of a resource structure's sysref, only used during
101 * booting to set up certain statically declared resources which cannot
105 sysref_init(struct sysref
*sr
, struct sysref_class
*srclass
)
112 gd
->gd_sysid_alloc
+= ncpus_fit
; /* next unique sysid */
113 sr
->sysid
= gd
->gd_sysid_alloc
;
114 KKASSERT(((int)sr
->sysid
& ncpus_fit_mask
) == gd
->gd_cpuid
);
115 sr
->refcnt
= -0x40000000;
117 sr
->srclass
= srclass
;
119 sa
= &sysref_array
[gd
->gd_cpuid
];
120 spin_lock(&sa
->spin
);
121 sysref_rb_tree_RB_INSERT(&sa
->rbtree
, sr
);
122 spin_unlock(&sa
->spin
);
127 * Allocate a resource structure of the specified class, initialize a
128 * sysid and add the resource to the RB tree. The caller must complete
129 * initialization of the resource and call sysref_activate() to activate it.
132 sysref_alloc(struct sysref_class
*srclass
)
139 * Create the object cache backing store.
141 if (srclass
->oc
== NULL
) {
142 KKASSERT(srclass
->mtype
!= NULL
);
143 srclass
->oc
= objcache_create_mbacked(
144 srclass
->mtype
, srclass
->objsize
,
145 0, srclass
->nom_cache
,
146 sysref_ctor
, sysref_dtor
, srclass
);
150 * Allocate the resource.
152 data
= objcache_get(srclass
->oc
, M_WAITOK
);
153 sr
= (struct sysref
*)(data
+ srclass
->offset
);
154 KKASSERT(sr
->flags
& SRF_PUTAWAY
);
155 sr
->flags
&= ~SRF_PUTAWAY
;
158 * Refcnt isn't touched while it is zero. The objcache ctor
159 * function has already allocated a sysid and emplaced the
160 * structure in the RB tree.
162 KKASSERT(sr
->refcnt
== 0);
163 sr
->refcnt
= -0x40000000;
166 * Clean out the structure unless the caller wants to deal with
167 * it (e.g. like the vmspace code).
169 if ((srclass
->flags
& SRC_MANAGEDINIT
) == 0) {
170 if (srclass
->offset
!= 0)
171 bzero(data
, srclass
->offset
);
172 n
= srclass
->offset
+ sizeof(struct sysref
);
173 KKASSERT(n
<= srclass
->objsize
);
174 if (n
!= srclass
->objsize
)
175 bzero(data
+ n
, srclass
->objsize
- n
);
181 * Object cache backing store ctor function.
183 * This allocates the sysid and associates the structure with the
184 * red-black tree, allowing it to be looked up. The actual resource
185 * structure has NOT yet been allocated so it is marked free.
187 * If the sysid is not used to access the resource, we will just
188 * allow the sysid to be reused when the resource structure is reused,
189 * allowing the RB tree operation to be 'cached'. This results in
190 * virtually no performance penalty for using the sysref facility.
194 sysref_ctor(void *data
, void *privdata
, int ocflags
)
198 struct sysref_class
*srclass
= privdata
;
199 struct sysref
*sr
= (void *)((char *)data
+ srclass
->offset
);
202 * Resource structures need to be cleared when allocating from
203 * malloc backing store. This is different from the zeroing
204 * that we do in sysref_alloc().
206 bzero(data
, srclass
->objsize
);
209 * Resources managed by our objcache do the sysid and RB tree
210 * handling in the objcache ctor/dtor, so we can reuse the
211 * structure without re-treeing it over and over again.
215 gd
->gd_sysid_alloc
+= ncpus_fit
; /* next unique sysid */
216 sr
->sysid
= gd
->gd_sysid_alloc
;
217 KKASSERT(((int)sr
->sysid
& ncpus_fit_mask
) == gd
->gd_cpuid
);
218 /* sr->refcnt= 0; already zero */
219 sr
->flags
= SRF_ALLOCATED
| SRF_PUTAWAY
;
220 sr
->srclass
= srclass
;
222 sa
= &sysref_array
[gd
->gd_cpuid
];
223 spin_lock(&sa
->spin
);
224 sysref_rb_tree_RB_INSERT(&sa
->rbtree
, sr
);
225 spin_unlock(&sa
->spin
);
229 * Execute the class's ctor function, if any. NOTE: The class
230 * should not try to zero out the structure, we've already handled
231 * that and preinitialized the sysref.
233 * XXX ignores return value for now
236 srclass
->ctor(data
, privdata
, ocflags
);
241 * Object cache destructor, allowing the structure to be returned
242 * to the system memory pool. The resource structure must be
243 * removed from the RB tree. All other references have already
244 * been destroyed and the RB tree will not create any new references
245 * to the structure in its current state.
249 sysref_dtor(void *data
, void *privdata
)
252 struct sysref_class
*srclass
= privdata
;
253 struct sysref
*sr
= (void *)((char *)data
+ srclass
->offset
);
255 KKASSERT(sr
->refcnt
== 0);
256 sa
= &sysref_array
[(int)sr
->sysid
& ncpus_fit_mask
];
257 spin_lock(&sa
->spin
);
258 sysref_rb_tree_RB_REMOVE(&sa
->rbtree
, sr
);
259 spin_unlock(&sa
->spin
);
261 srclass
->dtor(data
, privdata
);
265 * Activate or reactivate a resource. 0x40000001 is added to the ref count
266 * so -0x40000000 (during initialization) will translate to a ref count of 1.
267 * Any references made during initialization will translate to additional
268 * positive ref counts.
273 sysref_activate(struct sysref
*sr
)
279 KASSERT(count
< 0 && count
+ 0x40000001 > 0,
280 ("sysref_activate: bad count %08x", count
));
281 if (atomic_cmpset_int(&sr
->refcnt
, count
, count
+ 0x40000001))
288 * Release a reference under special circumstances. This call is made
289 * from the sysref_put() inline from sys/sysref2.h for any 1->0 transitions,
290 * negative->negative 'termination in progress' transitions, and when the
291 * cmpset instruction fails during a normal transition.
293 * This function is called from the sysref_put() inline in sys/sysref2.h,
294 * but handles all cases regardless.
297 _sysref_put(struct sysref
*sr
)
302 KKASSERT((sr
->flags
& SRF_PUTAWAY
) == 0);
308 * release 1 count, nominal case, active resource
309 * structure, no other action required.
311 if (atomic_cmpset_int(&sr
->refcnt
, count
, count
- 1))
313 } else if (count
== 1) {
315 * 1->0 transitions transition to -0x40000000 instead,
316 * placing the resource structure into a termination-
317 * in-progress state. The termination function is
320 data
= (char *)sr
- sr
->srclass
->offset
;
321 sr
->srclass
->ops
.lock(data
);
322 if (atomic_cmpset_int(&sr
->refcnt
, count
, -0x40000000)) {
323 sr
->srclass
->ops
.terminate(data
);
326 sr
->srclass
->ops
.unlock(data
);
327 } else if (count
> -0x40000000) {
329 * release 1 count, nominal case, resource undergoing
330 * termination. The Resource can be ref'd and
331 * deref'd while undergoing termination.
333 if (atomic_cmpset_int(&sr
->refcnt
, count
, count
- 1))
337 * Final release, set refcnt to 0.
338 * Resource must have been allocated.
340 * If SRF_SYSIDUSED is not set just objcache_put() the
341 * resource, otherwise objcache_dtor() the resource.
343 KKASSERT(count
== -0x40000000);
344 if (atomic_cmpset_int(&sr
->refcnt
, count
, 0)) {
345 KKASSERT(sr
->flags
& SRF_ALLOCATED
);
346 sr
->flags
|= SRF_PUTAWAY
;
347 data
= (char *)sr
- sr
->srclass
->offset
;
348 if (sr
->flags
& SRF_SYSIDUSED
)
349 objcache_dtor(sr
->srclass
->oc
, data
);
351 objcache_put(sr
->srclass
->oc
, data
);
355 /* loop until the cmpset succeeds */
363 globaldata_t gd
= mycpu
;
367 gd
->gd_sysid_alloc
+= ncpus_fit
;
368 sysid
= gd
->gd_sysid_alloc
;