1/*-
2 * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/kernel.h>
32#include <sys/systm.h>
33#include <sys/sysctl.h>
34#include <sys/errno.h>
35#include <sys/jail.h>
36#include <sys/malloc.h>
37#include <sys/lock.h>
38#include <sys/mutex.h>
39#include <sys/rmlock.h>
40#include <sys/sx.h>
41#include <sys/queue.h>
42#include <sys/proc.h>
43#include <sys/osd.h>
44
45/* OSD (Object Specific Data) */
46
47/*
48 * Lock key:
49 *  (m) osd_module_lock
50 *  (o) osd_object_lock
51 *  (l) osd_list_lock
52 */
53struct osd_master {
54	struct sx		 osd_module_lock;
55	struct rmlock		 osd_object_lock;
56	struct mtx		 osd_list_lock;
57	LIST_HEAD(, osd)	 osd_list;		/* (l) */
58	osd_destructor_t	*osd_destructors;	/* (o) */
59	osd_method_t		*osd_methods;		/* (m) */
60	u_int			 osd_ntslots;		/* (m) */
61	const u_int		 osd_nmethods;
62};
63
64static MALLOC_DEFINE(M_OSD, "osd", "Object Specific Data");
65
66static int osd_debug = 0;
67TUNABLE_INT("debug.osd", &osd_debug);
68SYSCTL_INT(_debug, OID_AUTO, osd, CTLFLAG_RW, &osd_debug, 0, "OSD debug level");
69
70#define	OSD_DEBUG(...)	do {						\
71	if (osd_debug) {						\
72		printf("OSD (%s:%u): ", __func__, __LINE__);		\
73		printf(__VA_ARGS__);					\
74		printf("\n");						\
75	}								\
76} while (0)
77
78static void do_osd_del(u_int type, struct osd *osd, u_int slot,
79    int list_locked);
80
81/*
82 * List of objects with OSD.
83 */
84struct osd_master osdm[OSD_LAST + 1] = {
85	[OSD_JAIL] = { .osd_nmethods = PR_MAXMETHOD },
86};
87
88static void
89osd_default_destructor(void *value __unused)
90{
91	/* Do nothing. */
92}
93
94int
95osd_register(u_int type, osd_destructor_t destructor, osd_method_t *methods)
96{
97	void *newptr;
98	u_int i, m;
99
100	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
101
102	/*
103	 * If no destructor is given, use default one. We need to use some
104	 * destructor, because NULL destructor means unused slot.
105	 */
106	if (destructor == NULL)
107		destructor = osd_default_destructor;
108
109	sx_xlock(&osdm[type].osd_module_lock);
110	/*
111	 * First, we try to find unused slot.
112	 */
113	for (i = 0; i < osdm[type].osd_ntslots; i++) {
114		if (osdm[type].osd_destructors[i] == NULL) {
115			OSD_DEBUG("Unused slot found (type=%u, slot=%u).",
116			    type, i);
117			break;
118		}
119	}
120	/*
121	 * If no unused slot was found, allocate one.
122	 */
123	if (i == osdm[type].osd_ntslots) {
124		osdm[type].osd_ntslots++;
125		if (osdm[type].osd_nmethods != 0)
126			osdm[type].osd_methods = realloc(osdm[type].osd_methods,
127			    sizeof(osd_method_t) * osdm[type].osd_ntslots *
128			    osdm[type].osd_nmethods, M_OSD, M_WAITOK);
129		newptr = malloc(sizeof(osd_destructor_t) *
130		    osdm[type].osd_ntslots, M_OSD, M_WAITOK);
131		rm_wlock(&osdm[type].osd_object_lock);
132		bcopy(osdm[type].osd_destructors, newptr,
133		    sizeof(osd_destructor_t) * i);
134		free(osdm[type].osd_destructors, M_OSD);
135		osdm[type].osd_destructors = newptr;
136		rm_wunlock(&osdm[type].osd_object_lock);
137		OSD_DEBUG("New slot allocated (type=%u, slot=%u).",
138		    type, i + 1);
139	}
140
141	osdm[type].osd_destructors[i] = destructor;
142	if (osdm[type].osd_nmethods != 0) {
143		for (m = 0; m < osdm[type].osd_nmethods; m++)
144			osdm[type].osd_methods[i * osdm[type].osd_nmethods + m]
145			    = methods != NULL ? methods[m] : NULL;
146	}
147	sx_xunlock(&osdm[type].osd_module_lock);
148	return (i + 1);
149}
150
151void
152osd_deregister(u_int type, u_int slot)
153{
154	struct osd *osd, *tosd;
155
156	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
157	KASSERT(slot > 0, ("Invalid slot."));
158	KASSERT(osdm[type].osd_destructors[slot - 1] != NULL, ("Unused slot."));
159
160	sx_xlock(&osdm[type].osd_module_lock);
161	rm_wlock(&osdm[type].osd_object_lock);
162	/*
163	 * Free all OSD for the given slot.
164	 */
165	mtx_lock(&osdm[type].osd_list_lock);
166	LIST_FOREACH_SAFE(osd, &osdm[type].osd_list, osd_next, tosd)
167		do_osd_del(type, osd, slot, 1);
168	mtx_unlock(&osdm[type].osd_list_lock);
169	/*
170	 * Set destructor to NULL to free the slot.
171	 */
172	osdm[type].osd_destructors[slot - 1] = NULL;
173	if (slot == osdm[type].osd_ntslots) {
174		osdm[type].osd_ntslots--;
175		osdm[type].osd_destructors = realloc(osdm[type].osd_destructors,
176		    sizeof(osd_destructor_t) * osdm[type].osd_ntslots, M_OSD,
177		    M_NOWAIT | M_ZERO);
178		if (osdm[type].osd_nmethods != 0)
179			osdm[type].osd_methods = realloc(osdm[type].osd_methods,
180			    sizeof(osd_method_t) * osdm[type].osd_ntslots *
181			    osdm[type].osd_nmethods, M_OSD, M_NOWAIT | M_ZERO);
182		/*
183		 * We always reallocate to smaller size, so we assume it will
184		 * always succeed.
185		 */
186		KASSERT(osdm[type].osd_destructors != NULL &&
187		    (osdm[type].osd_nmethods == 0 ||
188		     osdm[type].osd_methods != NULL), ("realloc() failed"));
189		OSD_DEBUG("Deregistration of the last slot (type=%u, slot=%u).",
190		    type, slot);
191	} else {
192		OSD_DEBUG("Slot deregistration (type=%u, slot=%u).",
193		    type, slot);
194	}
195	rm_wunlock(&osdm[type].osd_object_lock);
196	sx_xunlock(&osdm[type].osd_module_lock);
197}
198
199int
200osd_set(u_int type, struct osd *osd, u_int slot, void *value)
201{
202
203	return (osd_set_reserved(type, osd, slot, NULL, value));
204}
205
206void *
207osd_reserve(u_int slot)
208{
209
210	KASSERT(slot > 0, ("Invalid slot."));
211
212	OSD_DEBUG("Reserving slot array (slot=%u).", slot);
213	return (malloc(sizeof(void *) * slot, M_OSD, M_WAITOK | M_ZERO));
214}
215
216int
217osd_set_reserved(u_int type, struct osd *osd, u_int slot, void *rsv,
218    void *value)
219{
220	struct rm_priotracker tracker;
221
222	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
223	KASSERT(slot > 0, ("Invalid slot."));
224	KASSERT(osdm[type].osd_destructors[slot - 1] != NULL, ("Unused slot."));
225
226	rm_rlock(&osdm[type].osd_object_lock, &tracker);
227	if (slot > osd->osd_nslots) {
228		void *newptr;
229
230		if (value == NULL) {
231			OSD_DEBUG(
232			    "Not allocating null slot (type=%u, slot=%u).",
233			    type, slot);
234			rm_runlock(&osdm[type].osd_object_lock, &tracker);
235			if (rsv)
236				osd_free_reserved(rsv);
237			return (0);
238		}
239
240		/*
241		 * Too few slots allocated here, so we need to extend or create
242		 * the array.
243		 */
244		if (rsv) {
245			/*
246			 * Use the reserve passed in (assumed to be
247			 * the right size).
248			 */
249			newptr = rsv;
250			if (osd->osd_nslots != 0) {
251				memcpy(newptr, osd->osd_slots,
252				    sizeof(void *) * osd->osd_nslots);
253				free(osd->osd_slots, M_OSD);
254			}
255		} else {
256			newptr = realloc(osd->osd_slots, sizeof(void *) * slot,
257			    M_OSD, M_NOWAIT | M_ZERO);
258			if (newptr == NULL) {
259				rm_runlock(&osdm[type].osd_object_lock,
260				    &tracker);
261				return (ENOMEM);
262			}
263		}
264		if (osd->osd_nslots == 0) {
265			/*
266			 * First OSD for this object, so we need to put it
267			 * onto the list.
268			 */
269			mtx_lock(&osdm[type].osd_list_lock);
270			LIST_INSERT_HEAD(&osdm[type].osd_list, osd, osd_next);
271			mtx_unlock(&osdm[type].osd_list_lock);
272			OSD_DEBUG("Setting first slot (type=%u).", type);
273		} else
274			OSD_DEBUG("Growing slots array (type=%u).", type);
275		osd->osd_slots = newptr;
276		osd->osd_nslots = slot;
277	} else if (rsv)
278		osd_free_reserved(rsv);
279	OSD_DEBUG("Setting slot value (type=%u, slot=%u, value=%p).", type,
280	    slot, value);
281	osd->osd_slots[slot - 1] = value;
282	rm_runlock(&osdm[type].osd_object_lock, &tracker);
283	return (0);
284}
285
286void
287osd_free_reserved(void *rsv)
288{
289
290	OSD_DEBUG("Discarding reserved slot array.");
291	free(rsv, M_OSD);
292}
293
294void *
295osd_get(u_int type, struct osd *osd, u_int slot)
296{
297	struct rm_priotracker tracker;
298	void *value;
299
300	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
301	KASSERT(slot > 0, ("Invalid slot."));
302	KASSERT(osdm[type].osd_destructors[slot - 1] != NULL, ("Unused slot."));
303
304	rm_rlock(&osdm[type].osd_object_lock, &tracker);
305	if (slot > osd->osd_nslots) {
306		value = NULL;
307		OSD_DEBUG("Slot doesn't exist (type=%u, slot=%u).", type, slot);
308	} else {
309		value = osd->osd_slots[slot - 1];
310		OSD_DEBUG("Returning slot value (type=%u, slot=%u, value=%p).",
311		    type, slot, value);
312	}
313	rm_runlock(&osdm[type].osd_object_lock, &tracker);
314	return (value);
315}
316
317void
318osd_del(u_int type, struct osd *osd, u_int slot)
319{
320	struct rm_priotracker tracker;
321
322	rm_rlock(&osdm[type].osd_object_lock, &tracker);
323	do_osd_del(type, osd, slot, 0);
324	rm_runlock(&osdm[type].osd_object_lock, &tracker);
325}
326
327static void
328do_osd_del(u_int type, struct osd *osd, u_int slot, int list_locked)
329{
330	int i;
331
332	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
333	KASSERT(slot > 0, ("Invalid slot."));
334	KASSERT(osdm[type].osd_destructors[slot - 1] != NULL, ("Unused slot."));
335
336	OSD_DEBUG("Deleting slot (type=%u, slot=%u).", type, slot);
337
338	if (slot > osd->osd_nslots) {
339		OSD_DEBUG("Slot doesn't exist (type=%u, slot=%u).", type, slot);
340		return;
341	}
342	if (osd->osd_slots[slot - 1] != NULL) {
343		osdm[type].osd_destructors[slot - 1](osd->osd_slots[slot - 1]);
344		osd->osd_slots[slot - 1] = NULL;
345	}
346	for (i = osd->osd_nslots - 1; i >= 0; i--) {
347		if (osd->osd_slots[i] != NULL) {
348			OSD_DEBUG("Slot still has a value (type=%u, slot=%u).",
349			    type, i + 1);
350			break;
351		}
352	}
353	if (i == -1) {
354		/* No values left for this object. */
355		OSD_DEBUG("No more slots left (type=%u).", type);
356		if (!list_locked)
357			mtx_lock(&osdm[type].osd_list_lock);
358		LIST_REMOVE(osd, osd_next);
359		if (!list_locked)
360			mtx_unlock(&osdm[type].osd_list_lock);
361		free(osd->osd_slots, M_OSD);
362		osd->osd_slots = NULL;
363		osd->osd_nslots = 0;
364	} else if (slot == osd->osd_nslots) {
365		/* This was the last slot. */
366		osd->osd_slots = realloc(osd->osd_slots,
367		    sizeof(void *) * (i + 1), M_OSD, M_NOWAIT | M_ZERO);
368		/*
369		 * We always reallocate to smaller size, so we assume it will
370		 * always succeed.
371		 */
372		KASSERT(osd->osd_slots != NULL, ("realloc() failed"));
373		osd->osd_nslots = i + 1;
374		OSD_DEBUG("Reducing slots array to %u (type=%u).",
375		    osd->osd_nslots, type);
376	}
377}
378
379int
380osd_call(u_int type, u_int method, void *obj, void *data)
381{
382	osd_method_t methodfun;
383	int error, i;
384
385	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
386	KASSERT(method < osdm[type].osd_nmethods, ("Invalid method."));
387
388	/*
389	 * Call this method for every slot that defines it, stopping if an
390	 * error is encountered.
391	 */
392	error = 0;
393	sx_slock(&osdm[type].osd_module_lock);
394	for (i = 0; i < osdm[type].osd_ntslots; i++) {
395		methodfun = osdm[type].osd_methods[i * osdm[type].osd_nmethods +
396		    method];
397		if (methodfun != NULL && (error = methodfun(obj, data)) != 0)
398			break;
399	}
400	sx_sunlock(&osdm[type].osd_module_lock);
401	return (error);
402}
403
404void
405osd_exit(u_int type, struct osd *osd)
406{
407	struct rm_priotracker tracker;
408	u_int i;
409
410	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
411
412	if (osd->osd_nslots == 0) {
413		KASSERT(osd->osd_slots == NULL, ("Non-null osd_slots."));
414		/* No OSD attached, just leave. */
415		return;
416	}
417
418	rm_rlock(&osdm[type].osd_object_lock, &tracker);
419	for (i = 1; i <= osd->osd_nslots; i++) {
420		if (osdm[type].osd_destructors[i - 1] != NULL)
421			do_osd_del(type, osd, i, 0);
422		else
423			OSD_DEBUG("Unused slot (type=%u, slot=%u).", type, i);
424	}
425	rm_runlock(&osdm[type].osd_object_lock, &tracker);
426	OSD_DEBUG("Object exit (type=%u).", type);
427}
428
429static void
430osd_init(void *arg __unused)
431{
432	u_int i;
433
434	for (i = OSD_FIRST; i <= OSD_LAST; i++) {
435		sx_init(&osdm[i].osd_module_lock, "osd_module");
436		rm_init(&osdm[i].osd_object_lock, "osd_object");
437		mtx_init(&osdm[i].osd_list_lock, "osd_list", NULL, MTX_DEF);
438		LIST_INIT(&osdm[i].osd_list);
439		osdm[i].osd_destructors = NULL;
440		osdm[i].osd_ntslots = 0;
441		osdm[i].osd_methods = NULL;
442	}
443}
444SYSINIT(osd, SI_SUB_LOCK, SI_ORDER_ANY, osd_init, NULL);
445