subr_kobj.c revision 227343
150477Speter/*-
21817Sdg * Copyright (c) 2000,2003 Doug Rabson
31817Sdg * All rights reserved.
41541Srgrimes *
51541Srgrimes * Redistribution and use in source and binary forms, with or without
61541Srgrimes * modification, are permitted provided that the following conditions
7160798Sjhb * are met:
81541Srgrimes * 1. Redistributions of source code must retain the above copyright
9146806Srwatson *    notice, this list of conditions and the following disclaimer.
10146806Srwatson * 2. Redistributions in binary form must reproduce the above copyright
11146806Srwatson *    notice, this list of conditions and the following disclaimer in the
12146806Srwatson *    documentation and/or other materials provided with the distribution.
13146806Srwatson *
14160798Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15160798Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1611294Sswallace * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1710905Sbde * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1910905Sbde * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2010905Sbde * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241541Srgrimes * SUCH DAMAGE.
251541Srgrimes */
2699855Salfred
271541Srgrimes#include <sys/cdefs.h>
281541Srgrimes__FBSDID("$FreeBSD: head/sys/kern/subr_kobj.c 227343 2011-11-08 15:38:21Z ed $");
291541Srgrimes
3069449Salfred#include <sys/param.h>
31160797Sjhb#include <sys/kernel.h>
32160797Sjhb#include <sys/kobj.h>
33104747Srwatson#include <sys/lock.h>
34104747Srwatson#include <sys/malloc.h>
35123408Speter#include <sys/mutex.h>
36123408Speter#include <sys/sysctl.h>
371541Srgrimes#ifndef TEST
381541Srgrimes#include <sys/systm.h>
3911294Sswallace#endif
4011294Sswallace
4111294Sswallace#ifdef TEST
4211294Sswallace#include "usertest.h"
431541Srgrimes#endif
441541Srgrimes
451541Srgrimesstatic MALLOC_DEFINE(M_KOBJ, "kobj", "Kernel object structures");
461541Srgrimes
471541Srgrimes#ifdef KOBJ_STATS
481541Srgrimes
49160798Sjhbu_int kobj_lookup_hits;
50160798Sjhbu_int kobj_lookup_misses;
51146806Srwatson
52160798SjhbSYSCTL_UINT(_kern, OID_AUTO, kobj_hits, CTLFLAG_RD,
53160798Sjhb	   &kobj_lookup_hits, 0, "");
54146806SrwatsonSYSCTL_UINT(_kern, OID_AUTO, kobj_misses, CTLFLAG_RD,
55160798Sjhb	   &kobj_lookup_misses, 0, "");
56146806Srwatson
57160798Sjhb#endif
5812216Sbde
5912216Sbdestatic struct mtx kobj_mtx;
6012216Sbdestatic int kobj_mutex_inited;
61160798Sjhbstatic int kobj_next_id = 1;
62160798Sjhb
63146806Srwatson/*
64146806Srwatson * In the event that kobj_mtx has not been initialized yet,
65160798Sjhb * we will ignore it, and run without locks in order to support
66160798Sjhb * use of KOBJ before mutexes are available. This early in the boot
67160798Sjhb * process, everything is single threaded and so races should not
68146806Srwatson * happen. This is used to provide the PMAP layer on PowerPC, as well
69160798Sjhb * as board support.
70160798Sjhb */
71160798Sjhb
72160798Sjhb#define KOBJ_LOCK()	if (kobj_mutex_inited) mtx_lock(&kobj_mtx);
73160798Sjhb#define KOBJ_UNLOCK()	if (kobj_mutex_inited) mtx_unlock(&kobj_mtx);
74160798Sjhb#define KOBJ_ASSERT(what) if (kobj_mutex_inited) mtx_assert(&kobj_mtx,what);
75146806Srwatson
76160798SjhbSYSCTL_INT(_kern, OID_AUTO, kobj_methodcount, CTLFLAG_RD,
77146806Srwatson	   &kobj_next_id, 0, "");
78160798Sjhb
79146806Srwatsonstatic void
80160798Sjhbkobj_init_mutex(void *arg)
81160798Sjhb{
82146806Srwatson	if (!kobj_mutex_inited) {
8312216Sbde		mtx_init(&kobj_mtx, "kobj", NULL, MTX_DEF);
84160798Sjhb		kobj_mutex_inited = 1;
85160798Sjhb	}
86160798Sjhb}
87160798Sjhb
88160798SjhbSYSINIT(kobj, SI_SUB_LOCK, SI_ORDER_ANY, kobj_init_mutex, NULL);
89146806Srwatson
90160798Sjhb/*
91146806Srwatson * This method structure is used to initialise new caches. Since the
92160798Sjhb * desc pointer is NULL, it is guaranteed never to match any read
93146806Srwatson * descriptors.
94160798Sjhb */
95146806Srwatsonstatic const struct kobj_method null_method = {
96146806Srwatson	0, 0,
97146806Srwatson};
98160798Sjhb
99146806Srwatsonint
100146806Srwatsonkobj_error_method(void)
101160798Sjhb{
102146806Srwatson
103146806Srwatson	return ENXIO;
104160798Sjhb}
105146806Srwatson
106146806Srwatsonstatic void
107160798Sjhbkobj_register_method(struct kobjop_desc *desc)
108160798Sjhb{
109160798Sjhb	KOBJ_ASSERT(MA_OWNED);
110160798Sjhb
111160798Sjhb	if (desc->id == 0) {
112160798Sjhb		desc->id = kobj_next_id++;
113160798Sjhb	}
114160798Sjhb}
115160798Sjhb
116160798Sjhbstatic void
117160798Sjhbkobj_unregister_method(struct kobjop_desc *desc)
118160798Sjhb{
119146806Srwatson}
120160798Sjhb
121146806Srwatsonstatic void
122160798Sjhbkobj_class_compile_common(kobj_class_t cls, kobj_ops_t ops)
123146806Srwatson{
124146806Srwatson	kobj_method_t *m;
125160798Sjhb	int i;
126160798Sjhb
12721776Sbde	KOBJ_ASSERT(MA_OWNED);
12821776Sbde
12921776Sbde	/*
130160798Sjhb	 * Don't do anything if we are already compiled.
131146806Srwatson	 */
132160798Sjhb	if (cls->ops)
133160798Sjhb		return;
134160798Sjhb
135162373Srwatson	/*
136146806Srwatson	 * First register any methods which need it.
137160798Sjhb	 */
138146806Srwatson	for (i = 0, m = cls->methods; m->desc; i++, m++)
139160798Sjhb		kobj_register_method(m->desc);
140160798Sjhb
141160798Sjhb	/*
142160798Sjhb	 * Then initialise the ops table.
143146806Srwatson	 */
144160798Sjhb	for (i = 0; i < KOBJ_CACHE_SIZE; i++)
145146806Srwatson		ops->cache[i] = &null_method;
146160798Sjhb	ops->cls = cls;
147146806Srwatson	cls->ops = ops;
148160798Sjhb}
149160798Sjhb
150160798Sjhbvoid
151146806Srwatsonkobj_class_compile(kobj_class_t cls)
152146806Srwatson{
153160798Sjhb	kobj_ops_t ops;
154146806Srwatson
155160798Sjhb	KOBJ_ASSERT(MA_NOTOWNED);
156146806Srwatson
157160798Sjhb	/*
158146806Srwatson	 * Allocate space for the compiled ops table.
159146806Srwatson	 */
160160798Sjhb	ops = malloc(sizeof(struct kobj_ops), M_KOBJ, M_NOWAIT);
161160798Sjhb	if (!ops)
162160798Sjhb		panic("kobj_compile_methods: out of memory");
163146806Srwatson
164160798Sjhb	KOBJ_LOCK();
165146806Srwatson
166160798Sjhb	/*
167160798Sjhb	 * We may have lost a race for kobj_class_compile here - check
168146806Srwatson	 * to make sure someone else hasn't already compiled this
169160798Sjhb	 * class.
170146806Srwatson	 */
171146806Srwatson	if (cls->ops) {
172146806Srwatson		KOBJ_UNLOCK();
173160798Sjhb		free(ops, M_KOBJ);
174146806Srwatson		return;
175160798Sjhb	}
176146806Srwatson
177160798Sjhb	kobj_class_compile_common(cls, ops);
178146806Srwatson	KOBJ_UNLOCK();
179160798Sjhb}
180160798Sjhb
181160798Sjhbvoid
182146806Srwatsonkobj_class_compile_static(kobj_class_t cls, kobj_ops_t ops)
183160798Sjhb{
184160798Sjhb
185160798Sjhb	KOBJ_ASSERT(MA_NOTOWNED);
186146806Srwatson
187160798Sjhb	/*
188146806Srwatson	 * Increment refs to make sure that the ops table is not freed.
189146806Srwatson	 */
190160798Sjhb	KOBJ_LOCK();
191146806Srwatson
192146806Srwatson	cls->refs++;
193160798Sjhb	kobj_class_compile_common(cls, ops);
194160798Sjhb
195146806Srwatson	KOBJ_UNLOCK();
196160798Sjhb}
197123750Speter
19812216Sbdestatic kobj_method_t*
199160798Sjhbkobj_lookup_method_class(kobj_class_t cls, kobjop_desc_t desc)
200146806Srwatson{
201146806Srwatson	kobj_method_t *methods = cls->methods;
202160798Sjhb	kobj_method_t *ce;
203160798Sjhb
204146806Srwatson	for (ce = methods; ce && ce->desc; ce++) {
205160798Sjhb		if (ce->desc == desc) {
206146806Srwatson			return ce;
207160798Sjhb		}
208146806Srwatson	}
209160798Sjhb
210146806Srwatson	return NULL;
211160798Sjhb}
212160798Sjhb
213146806Srwatsonstatic kobj_method_t*
214160798Sjhbkobj_lookup_method_mi(kobj_class_t cls,
215146806Srwatson		      kobjop_desc_t desc)
216160798Sjhb{
217146806Srwatson	kobj_method_t *ce;
218160798Sjhb	kobj_class_t *basep;
219146806Srwatson
220160798Sjhb	ce = kobj_lookup_method_class(cls, desc);
221146806Srwatson	if (ce)
222160798Sjhb		return ce;
223146806Srwatson
224160798Sjhb	basep = cls->baseclasses;
225146806Srwatson	if (basep) {
226160798Sjhb		for (; *basep; basep++) {
227160798Sjhb			ce = kobj_lookup_method_mi(*basep, desc);
228160798Sjhb			if (ce)
22921776Sbde				return ce;
23021776Sbde		}
231160798Sjhb	}
232146806Srwatson
233160798Sjhb	return NULL;
234146806Srwatson}
235160798Sjhb
236146806Srwatsonkobj_method_t*
237146806Srwatsonkobj_lookup_method(kobj_class_t cls,
238160798Sjhb		   kobj_method_t **cep,
239146806Srwatson		   kobjop_desc_t desc)
240160798Sjhb{
241146806Srwatson	kobj_method_t *ce;
242160798Sjhb
243146806Srwatson#ifdef KOBJ_STATS
244146806Srwatson	/*
245160798Sjhb	 * Correct for the 'hit' assumption in KOBJOPLOOKUP and record
246146806Srwatson	 * a 'miss'.
247160798Sjhb	 */
248146806Srwatson	kobj_lookup_hits--;
249160798Sjhb	kobj_lookup_misses++;
250146806Srwatson#endif
251160798Sjhb
252160798Sjhb	ce = kobj_lookup_method_mi(cls, desc);
253160798Sjhb	if (!ce)
254146806Srwatson		ce = desc->deflt;
255146806Srwatson	*cep = ce;
256146806Srwatson	return ce;
257160798Sjhb}
258160798Sjhb
259160798Sjhbvoid
260160798Sjhbkobj_class_free(kobj_class_t cls)
261160798Sjhb{
262160798Sjhb	int i;
263160798Sjhb	kobj_method_t *m;
264160798Sjhb	void* ops = NULL;
265146806Srwatson
266160798Sjhb	KOBJ_ASSERT(MA_NOTOWNED);
267160798Sjhb	KOBJ_LOCK();
268146806Srwatson
269160798Sjhb	/*
270160798Sjhb	 * Protect against a race between kobj_create and
271160798Sjhb	 * kobj_delete.
272146806Srwatson	 */
273146806Srwatson	if (cls->refs == 0) {
274160798Sjhb		/*
275146806Srwatson		 * Unregister any methods which are no longer used.
276160798Sjhb		 */
277146806Srwatson		for (i = 0, m = cls->methods; m->desc; i++, m++)
278160798Sjhb			kobj_unregister_method(m->desc);
279160798Sjhb
280160798Sjhb		/*
281146806Srwatson		 * Free memory and clean up.
282160798Sjhb		 */
283146806Srwatson		ops = cls->ops;
284160798Sjhb		cls->ops = NULL;
285160798Sjhb	}
286160798Sjhb
287146806Srwatson	KOBJ_UNLOCK();
288160798Sjhb
289160798Sjhb	if (ops)
290146806Srwatson		free(ops, M_KOBJ);
291146806Srwatson}
2921541Srgrimes
2931541Srgrimeskobj_t
2941541Srgrimeskobj_create(kobj_class_t cls,
2951541Srgrimes	    struct malloc_type *mtype,
2961541Srgrimes	    int mflags)
297146806Srwatson{
298146806Srwatson	kobj_t obj;
299146806Srwatson
300146806Srwatson	/*
30130740Sphk	 * Allocate and initialise the new object.
302161325Sjhb	 */
303160798Sjhb	obj = malloc(cls->size, mtype, mflags | M_ZERO);
304146806Srwatson	if (!obj)
305160798Sjhb		return NULL;
306146806Srwatson	kobj_init(obj, cls);
307160798Sjhb
308146806Srwatson	return obj;
309146806Srwatson}
310160798Sjhb
311146806Srwatsonvoid
312160798Sjhbkobj_init(kobj_t obj, kobj_class_t cls)
313146806Srwatson{
314160798Sjhb	KOBJ_ASSERT(MA_NOTOWNED);
315146806Srwatson  retry:
316160798Sjhb	KOBJ_LOCK();
317146806Srwatson
318160798Sjhb	/*
319161952Srwatson	 * Consider compiling the class' method table.
320161952Srwatson	 */
321146806Srwatson	if (!cls->ops) {
322146806Srwatson		/*
323146806Srwatson		 * kobj_class_compile doesn't want the lock held
32469449Salfred		 * because of the call to malloc - we drop the lock
325160798Sjhb		 * and re-try.
326146806Srwatson		 */
32769449Salfred		KOBJ_UNLOCK();
328123750Speter		kobj_class_compile(cls);
329160798Sjhb		goto retry;
330146806Srwatson	}
33169449Salfred
332123750Speter	obj->ops = cls->ops;
333160798Sjhb	cls->refs++;
334146806Srwatson
335123750Speter	KOBJ_UNLOCK();
336146806Srwatson}
337160798Sjhb
338146806Srwatsonvoid
339160798Sjhbkobj_delete(kobj_t obj, struct malloc_type *mtype)
340146806Srwatson{
341146806Srwatson	kobj_class_t cls = obj->ops->cls;
342161946Srwatson	int refs;
343146806Srwatson
344146806Srwatson	/*
345146806Srwatson	 * Consider freeing the compiled method table for the class
346146806Srwatson	 * after its last instance is deleted. As an optimisation, we
3471541Srgrimes	 * should defer this for a short while to avoid thrashing.
34849428Sjkh	 */
349160798Sjhb	KOBJ_ASSERT(MA_NOTOWNED);
350160798Sjhb	KOBJ_LOCK();
351160798Sjhb	cls->refs--;
352146806Srwatson	refs = cls->refs;
353146806Srwatson	KOBJ_UNLOCK();
354146806Srwatson
355146806Srwatson	if (!refs)
356160798Sjhb		kobj_class_free(cls);
357160798Sjhb
358160798Sjhb	obj->ops = NULL;
359160798Sjhb	if (mtype)
360160798Sjhb		free(obj, mtype);
361146806Srwatson}
362160798Sjhb