1185029Spjd/*-
2185029Spjd * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3185029Spjd * All rights reserved.
4185029Spjd *
5185029Spjd * Redistribution and use in source and binary forms, with or without
6185029Spjd * modification, are permitted provided that the following conditions
7185029Spjd * are met:
8185029Spjd * 1. Redistributions of source code must retain the above copyright
9185029Spjd *    notice, this list of conditions and the following disclaimer.
10185029Spjd * 2. Redistributions in binary form must reproduce the above copyright
11185029Spjd *    notice, this list of conditions and the following disclaimer in the
12185029Spjd *    documentation and/or other materials provided with the distribution.
13185029Spjd *
14185029Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15185029Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16185029Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17185029Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18185029Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19185029Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20185029Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21185029Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22185029Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23185029Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24185029Spjd * SUCH DAMAGE.
25185029Spjd */
26185029Spjd
27185029Spjd#include <sys/cdefs.h>
28185029Spjd__FBSDID("$FreeBSD$");
29185029Spjd
30185029Spjd#include <sys/param.h>
31185029Spjd#include <sys/kernel.h>
32185029Spjd#include <sys/systm.h>
33185029Spjd#include <sys/sysctl.h>
34185029Spjd#include <sys/errno.h>
35191806Sjamie#include <sys/jail.h>
36185029Spjd#include <sys/malloc.h>
37185029Spjd#include <sys/lock.h>
38185029Spjd#include <sys/mutex.h>
39188894Sjamie#include <sys/rmlock.h>
40188894Sjamie#include <sys/sx.h>
41185029Spjd#include <sys/queue.h>
42185029Spjd#include <sys/proc.h>
43185029Spjd#include <sys/osd.h>
44185029Spjd
45185029Spjd/* OSD (Object Specific Data) */
46185029Spjd
47185029Spjdstatic MALLOC_DEFINE(M_OSD, "osd", "Object Specific Data");
48185029Spjd
49185029Spjdstatic int osd_debug = 0;
50185029SpjdTUNABLE_INT("debug.osd", &osd_debug);
51185029SpjdSYSCTL_INT(_debug, OID_AUTO, osd, CTLFLAG_RW, &osd_debug, 0, "OSD debug level");
52185029Spjd
53185029Spjd#define	OSD_DEBUG(...)	do {						\
54185029Spjd	if (osd_debug) {						\
55185029Spjd		printf("OSD (%s:%u): ", __func__, __LINE__);		\
56185029Spjd		printf(__VA_ARGS__);					\
57185029Spjd		printf("\n");						\
58185029Spjd	}								\
59185029Spjd} while (0)
60185029Spjd
61188894Sjamiestatic void do_osd_del(u_int type, struct osd *osd, u_int slot,
62188894Sjamie    int list_locked);
63188894Sjamie
64185029Spjd/*
65185029Spjd * Lists of objects with OSD.
66188894Sjamie *
67188894Sjamie * Lock key:
68188894Sjamie *  (m) osd_module_lock
69188894Sjamie *  (o) osd_object_lock
70188894Sjamie *  (l) osd_list_lock
71185029Spjd */
72188894Sjamiestatic LIST_HEAD(, osd)	osd_list[OSD_LAST + 1];		/* (m) */
73188894Sjamiestatic osd_method_t *osd_methods[OSD_LAST + 1];		/* (m) */
74188894Sjamiestatic u_int osd_nslots[OSD_LAST + 1];			/* (m) */
75188894Sjamiestatic osd_destructor_t *osd_destructors[OSD_LAST + 1];	/* (o) */
76191673Sjamiestatic const u_int osd_nmethods[OSD_LAST + 1] = {
77191806Sjamie	[OSD_JAIL] = PR_MAXMETHOD,
78191673Sjamie};
79185029Spjd
80188894Sjamiestatic struct sx osd_module_lock[OSD_LAST + 1];
81188894Sjamiestatic struct rmlock osd_object_lock[OSD_LAST + 1];
82188894Sjamiestatic struct mtx osd_list_lock[OSD_LAST + 1];
83188894Sjamie
84185029Spjdstatic void
85185029Spjdosd_default_destructor(void *value __unused)
86185029Spjd{
87185029Spjd	/* Do nothing. */
88185029Spjd}
89185029Spjd
90185029Spjdint
91188894Sjamieosd_register(u_int type, osd_destructor_t destructor, osd_method_t *methods)
92185029Spjd{
93185029Spjd	void *newptr;
94188894Sjamie	u_int i, m;
95185029Spjd
96185029Spjd	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
97185029Spjd
98185029Spjd	/*
99185029Spjd	 * If no destructor is given, use default one. We need to use some
100185029Spjd	 * destructor, because NULL destructor means unused slot.
101185029Spjd	 */
102185029Spjd	if (destructor == NULL)
103185029Spjd		destructor = osd_default_destructor;
104185029Spjd
105188894Sjamie	sx_xlock(&osd_module_lock[type]);
106185029Spjd	/*
107185029Spjd	 * First, we try to find unused slot.
108185029Spjd	 */
109185029Spjd	for (i = 0; i < osd_nslots[type]; i++) {
110185029Spjd		if (osd_destructors[type][i] == NULL) {
111185029Spjd			OSD_DEBUG("Unused slot found (type=%u, slot=%u).",
112185029Spjd			    type, i);
113185029Spjd			break;
114185029Spjd		}
115185029Spjd	}
116185029Spjd	/*
117185029Spjd	 * If no unused slot was found, allocate one.
118185029Spjd	 */
119185029Spjd	if (i == osd_nslots[type]) {
120185029Spjd		osd_nslots[type]++;
121188894Sjamie		if (osd_nmethods[type] != 0)
122188894Sjamie			osd_methods[type] = realloc(osd_methods[type],
123188894Sjamie			    sizeof(osd_method_t) * osd_nslots[type] *
124188894Sjamie			    osd_nmethods[type], M_OSD, M_WAITOK);
125188894Sjamie		newptr = malloc(sizeof(osd_destructor_t) * osd_nslots[type],
126188894Sjamie		    M_OSD, M_WAITOK);
127188894Sjamie		rm_wlock(&osd_object_lock[type]);
128188894Sjamie		bcopy(osd_destructors[type], newptr,
129188894Sjamie		    sizeof(osd_destructor_t) * i);
130188894Sjamie		free(osd_destructors[type], M_OSD);
131185029Spjd		osd_destructors[type] = newptr;
132188894Sjamie		rm_wunlock(&osd_object_lock[type]);
133185029Spjd		OSD_DEBUG("New slot allocated (type=%u, slot=%u).",
134185029Spjd		    type, i + 1);
135185029Spjd	}
136188894Sjamie
137185029Spjd	osd_destructors[type][i] = destructor;
138188894Sjamie	if (osd_nmethods[type] != 0) {
139188894Sjamie		for (m = 0; m < osd_nmethods[type]; m++)
140188894Sjamie			osd_methods[type][i * osd_nmethods[type] + m] =
141188894Sjamie			    methods != NULL ? methods[m] : NULL;
142188894Sjamie	}
143188894Sjamie	sx_xunlock(&osd_module_lock[type]);
144185029Spjd	return (i + 1);
145185029Spjd}
146185029Spjd
147185029Spjdvoid
148185029Spjdosd_deregister(u_int type, u_int slot)
149185029Spjd{
150185029Spjd	struct osd *osd, *tosd;
151185029Spjd
152185029Spjd	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
153185029Spjd	KASSERT(slot > 0, ("Invalid slot."));
154185029Spjd	KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot."));
155185029Spjd
156188894Sjamie	sx_xlock(&osd_module_lock[type]);
157188894Sjamie	rm_wlock(&osd_object_lock[type]);
158185029Spjd	/*
159185029Spjd	 * Free all OSD for the given slot.
160185029Spjd	 */
161188894Sjamie	mtx_lock(&osd_list_lock[type]);
162188894Sjamie	LIST_FOREACH_SAFE(osd, &osd_list[type], osd_next, tosd)
163188894Sjamie		do_osd_del(type, osd, slot, 1);
164188894Sjamie	mtx_unlock(&osd_list_lock[type]);
165185029Spjd	/*
166185029Spjd	 * Set destructor to NULL to free the slot.
167185029Spjd	 */
168185029Spjd	osd_destructors[type][slot - 1] = NULL;
169185029Spjd	if (slot == osd_nslots[type]) {
170185029Spjd		osd_nslots[type]--;
171185029Spjd		osd_destructors[type] = realloc(osd_destructors[type],
172185029Spjd		    sizeof(osd_destructor_t) * osd_nslots[type], M_OSD,
173185029Spjd		    M_NOWAIT | M_ZERO);
174188894Sjamie		if (osd_nmethods[type] != 0)
175188894Sjamie			osd_methods[type] = realloc(osd_methods[type],
176188894Sjamie			    sizeof(osd_method_t) * osd_nslots[type] *
177188894Sjamie			    osd_nmethods[type], M_OSD, M_NOWAIT | M_ZERO);
178185029Spjd		/*
179185029Spjd		 * We always reallocate to smaller size, so we assume it will
180185029Spjd		 * always succeed.
181185029Spjd		 */
182188894Sjamie		KASSERT(osd_destructors[type] != NULL &&
183188894Sjamie		    (osd_nmethods[type] == 0 || osd_methods[type] != NULL),
184188894Sjamie		    ("realloc() failed"));
185185029Spjd		OSD_DEBUG("Deregistration of the last slot (type=%u, slot=%u).",
186185029Spjd		    type, slot);
187185029Spjd	} else {
188185029Spjd		OSD_DEBUG("Slot deregistration (type=%u, slot=%u).",
189185029Spjd		    type, slot);
190185029Spjd	}
191188894Sjamie	rm_wunlock(&osd_object_lock[type]);
192188894Sjamie	sx_xunlock(&osd_module_lock[type]);
193185029Spjd}
194185029Spjd
195185029Spjdint
196185029Spjdosd_set(u_int type, struct osd *osd, u_int slot, void *value)
197185029Spjd{
198188894Sjamie	struct rm_priotracker tracker;
199185029Spjd
200185029Spjd	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
201185029Spjd	KASSERT(slot > 0, ("Invalid slot."));
202185029Spjd	KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot."));
203185029Spjd
204188894Sjamie	rm_rlock(&osd_object_lock[type], &tracker);
205188894Sjamie	if (slot > osd->osd_nslots) {
206188894Sjamie		if (value == NULL) {
207188894Sjamie			OSD_DEBUG(
208188894Sjamie			    "Not allocating null slot (type=%u, slot=%u).",
209188894Sjamie			    type, slot);
210188894Sjamie			rm_runlock(&osd_object_lock[type], &tracker);
211188894Sjamie			return (0);
212188894Sjamie		} else if (osd->osd_nslots == 0) {
213188894Sjamie			/*
214188894Sjamie			 * First OSD for this object, so we need to allocate
215188894Sjamie			 * space and put it onto the list.
216188894Sjamie			 */
217188894Sjamie			osd->osd_slots = malloc(sizeof(void *) * slot, M_OSD,
218188894Sjamie			    M_NOWAIT | M_ZERO);
219188894Sjamie			if (osd->osd_slots == NULL) {
220188894Sjamie				rm_runlock(&osd_object_lock[type], &tracker);
221188894Sjamie				return (ENOMEM);
222188894Sjamie			}
223188894Sjamie			osd->osd_nslots = slot;
224188894Sjamie			mtx_lock(&osd_list_lock[type]);
225188894Sjamie			LIST_INSERT_HEAD(&osd_list[type], osd, osd_next);
226188894Sjamie			mtx_unlock(&osd_list_lock[type]);
227188894Sjamie			OSD_DEBUG("Setting first slot (type=%u).", type);
228188894Sjamie		} else {
229188894Sjamie			void *newptr;
230185029Spjd
231188894Sjamie			/*
232188894Sjamie			 * Too few slots allocated here, needs to extend
233188894Sjamie			 * the array.
234188894Sjamie			 */
235188894Sjamie			newptr = realloc(osd->osd_slots, sizeof(void *) * slot,
236188894Sjamie			    M_OSD, M_NOWAIT | M_ZERO);
237188894Sjamie			if (newptr == NULL) {
238188894Sjamie				rm_runlock(&osd_object_lock[type], &tracker);
239188894Sjamie				return (ENOMEM);
240188894Sjamie			}
241188894Sjamie			osd->osd_slots = newptr;
242188894Sjamie			osd->osd_nslots = slot;
243188894Sjamie			OSD_DEBUG("Growing slots array (type=%u).", type);
244188894Sjamie		}
245185029Spjd	}
246185029Spjd	OSD_DEBUG("Setting slot value (type=%u, slot=%u, value=%p).", type,
247185029Spjd	    slot, value);
248185029Spjd	osd->osd_slots[slot - 1] = value;
249188894Sjamie	rm_runlock(&osd_object_lock[type], &tracker);
250185029Spjd	return (0);
251185029Spjd}
252185029Spjd
253185029Spjdvoid *
254185029Spjdosd_get(u_int type, struct osd *osd, u_int slot)
255185029Spjd{
256188894Sjamie	struct rm_priotracker tracker;
257188894Sjamie	void *value;
258185029Spjd
259185029Spjd	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
260185029Spjd	KASSERT(slot > 0, ("Invalid slot."));
261185029Spjd	KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot."));
262185029Spjd
263188894Sjamie	rm_rlock(&osd_object_lock[type], &tracker);
264185029Spjd	if (slot > osd->osd_nslots) {
265188894Sjamie		value = NULL;
266185029Spjd		OSD_DEBUG("Slot doesn't exist (type=%u, slot=%u).", type, slot);
267188894Sjamie	} else {
268188894Sjamie		value = osd->osd_slots[slot - 1];
269188894Sjamie		OSD_DEBUG("Returning slot value (type=%u, slot=%u, value=%p).",
270188894Sjamie		    type, slot, value);
271185029Spjd	}
272188894Sjamie	rm_runlock(&osd_object_lock[type], &tracker);
273188894Sjamie	return (value);
274185029Spjd}
275185029Spjd
276185029Spjdvoid
277185029Spjdosd_del(u_int type, struct osd *osd, u_int slot)
278185029Spjd{
279188894Sjamie	struct rm_priotracker tracker;
280188894Sjamie
281188894Sjamie	rm_rlock(&osd_object_lock[type], &tracker);
282188894Sjamie	do_osd_del(type, osd, slot, 0);
283188894Sjamie	rm_runlock(&osd_object_lock[type], &tracker);
284188894Sjamie}
285188894Sjamie
286188894Sjamiestatic void
287188894Sjamiedo_osd_del(u_int type, struct osd *osd, u_int slot, int list_locked)
288188894Sjamie{
289185029Spjd	int i;
290185029Spjd
291185029Spjd	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
292185029Spjd	KASSERT(slot > 0, ("Invalid slot."));
293185029Spjd	KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot."));
294185029Spjd
295185029Spjd	OSD_DEBUG("Deleting slot (type=%u, slot=%u).", type, slot);
296185029Spjd
297185029Spjd	if (slot > osd->osd_nslots) {
298185029Spjd		OSD_DEBUG("Slot doesn't exist (type=%u, slot=%u).", type, slot);
299185029Spjd		return;
300185029Spjd	}
301191711Sjamie	if (osd->osd_slots[slot - 1] != NULL) {
302191711Sjamie		osd_destructors[type][slot - 1](osd->osd_slots[slot - 1]);
303191711Sjamie		osd->osd_slots[slot - 1] = NULL;
304191711Sjamie	}
305185029Spjd	for (i = osd->osd_nslots - 1; i >= 0; i--) {
306185029Spjd		if (osd->osd_slots[i] != NULL) {
307188894Sjamie			OSD_DEBUG("Slot still has a value (type=%u, slot=%u).",
308188894Sjamie			    type, i + 1);
309185029Spjd			break;
310185029Spjd		}
311185029Spjd	}
312185029Spjd	if (i == -1) {
313185029Spjd		/* No values left for this object. */
314185029Spjd		OSD_DEBUG("No more slots left (type=%u).", type);
315188894Sjamie		if (!list_locked)
316188894Sjamie			mtx_lock(&osd_list_lock[type]);
317185029Spjd		LIST_REMOVE(osd, osd_next);
318188894Sjamie		if (!list_locked)
319188894Sjamie			mtx_unlock(&osd_list_lock[type]);
320185029Spjd		free(osd->osd_slots, M_OSD);
321185029Spjd		osd->osd_slots = NULL;
322185029Spjd		osd->osd_nslots = 0;
323185029Spjd	} else if (slot == osd->osd_nslots) {
324185029Spjd		/* This was the last slot. */
325185029Spjd		osd->osd_slots = realloc(osd->osd_slots,
326185029Spjd		    sizeof(void *) * (i + 1), M_OSD, M_NOWAIT | M_ZERO);
327185029Spjd		/*
328185029Spjd		 * We always reallocate to smaller size, so we assume it will
329185029Spjd		 * always succeed.
330185029Spjd		 */
331185029Spjd		KASSERT(osd->osd_slots != NULL, ("realloc() failed"));
332185029Spjd		osd->osd_nslots = i + 1;
333185029Spjd		OSD_DEBUG("Reducing slots array to %u (type=%u).",
334185029Spjd		    osd->osd_nslots, type);
335185029Spjd	}
336185029Spjd}
337185029Spjd
338188894Sjamieint
339188894Sjamieosd_call(u_int type, u_int method, void *obj, void *data)
340188894Sjamie{
341188894Sjamie	osd_method_t methodfun;
342188894Sjamie	int error, i;
343188894Sjamie
344188894Sjamie	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
345188894Sjamie	KASSERT(method < osd_nmethods[type], ("Invalid method."));
346188894Sjamie
347188894Sjamie	/*
348188894Sjamie	 * Call this method for every slot that defines it, stopping if an
349188894Sjamie	 * error is encountered.
350188894Sjamie	 */
351188894Sjamie	error = 0;
352188894Sjamie	sx_slock(&osd_module_lock[type]);
353191673Sjamie	for (i = 0; i < osd_nslots[type]; i++) {
354188894Sjamie		methodfun =
355191673Sjamie		    osd_methods[type][i * osd_nmethods[type] + method];
356188894Sjamie		if (methodfun != NULL && (error = methodfun(obj, data)) != 0)
357188894Sjamie			break;
358188894Sjamie	}
359188894Sjamie	sx_sunlock(&osd_module_lock[type]);
360188894Sjamie	return (error);
361188894Sjamie}
362188894Sjamie
363185029Spjdvoid
364185029Spjdosd_exit(u_int type, struct osd *osd)
365185029Spjd{
366188894Sjamie	struct rm_priotracker tracker;
367185029Spjd	u_int i;
368185029Spjd
369185029Spjd	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
370185029Spjd
371185029Spjd	if (osd->osd_nslots == 0) {
372185029Spjd		KASSERT(osd->osd_slots == NULL, ("Non-null osd_slots."));
373185029Spjd		/* No OSD attached, just leave. */
374185029Spjd		return;
375185029Spjd	}
376185029Spjd
377188894Sjamie	rm_rlock(&osd_object_lock[type], &tracker);
378188894Sjamie	for (i = 1; i <= osd->osd_nslots; i++) {
379188894Sjamie		if (osd_destructors[type][i - 1] != NULL)
380188894Sjamie			do_osd_del(type, osd, i, 0);
381188894Sjamie		else
382188894Sjamie			OSD_DEBUG("Unused slot (type=%u, slot=%u).", type, i);
383188894Sjamie	}
384188894Sjamie	rm_runlock(&osd_object_lock[type], &tracker);
385185029Spjd	OSD_DEBUG("Object exit (type=%u).", type);
386185029Spjd}
387185029Spjd
388185029Spjdstatic void
389185029Spjdosd_init(void *arg __unused)
390185029Spjd{
391185029Spjd	u_int i;
392185029Spjd
393185029Spjd	for (i = OSD_FIRST; i <= OSD_LAST; i++) {
394185029Spjd		osd_nslots[i] = 0;
395185029Spjd		LIST_INIT(&osd_list[i]);
396188894Sjamie		sx_init(&osd_module_lock[i], "osd_module");
397193030Srwatson		rm_init(&osd_object_lock[i], "osd_object");
398188894Sjamie		mtx_init(&osd_list_lock[i], "osd_list", NULL, MTX_DEF);
399185029Spjd		osd_destructors[i] = NULL;
400188894Sjamie		osd_methods[i] = NULL;
401185029Spjd	}
402185029Spjd}
403185029SpjdSYSINIT(osd, SI_SUB_LOCK, SI_ORDER_ANY, osd_init, NULL);
404