kern_osd.c revision 185029
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: head/sys/kern/kern_osd.c 185029 2008-11-17 20:49:29Z pjd $");
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/malloc.h>
36#include <sys/lock.h>
37#include <sys/mutex.h>
38#include <sys/queue.h>
39#include <sys/proc.h>
40#include <sys/osd.h>
41
42/* OSD (Object Specific Data) */
43
44static MALLOC_DEFINE(M_OSD, "osd", "Object Specific Data");
45
46static int osd_debug = 0;
47TUNABLE_INT("debug.osd", &osd_debug);
48SYSCTL_INT(_debug, OID_AUTO, osd, CTLFLAG_RW, &osd_debug, 0, "OSD debug level");
49
50#define	OSD_DEBUG(...)	do {						\
51	if (osd_debug) {						\
52		printf("OSD (%s:%u): ", __func__, __LINE__);		\
53		printf(__VA_ARGS__);					\
54		printf("\n");						\
55	}								\
56} while (0)
57
58/*
59 * Lists of objects with OSD.
60 */
61static LIST_HEAD(, osd)	osd_list[OSD_LAST + 1];
62static osd_destructor_t *osd_destructors[OSD_LAST + 1];
63static u_int osd_nslots[OSD_LAST + 1];
64static struct mtx osd_lock[OSD_LAST + 1];
65
66static void
67osd_default_destructor(void *value __unused)
68{
69	/* Do nothing. */
70}
71
72int
73osd_register(u_int type, osd_destructor_t destructor)
74{
75	void *newptr;
76	u_int i;
77
78	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
79
80	/*
81	 * If no destructor is given, use default one. We need to use some
82	 * destructor, because NULL destructor means unused slot.
83	 */
84	if (destructor == NULL)
85		destructor = osd_default_destructor;
86
87	mtx_lock(&osd_lock[type]);
88	/*
89	 * First, we try to find unused slot.
90	 */
91	for (i = 0; i < osd_nslots[type]; i++) {
92		if (osd_destructors[type][i] == NULL) {
93			OSD_DEBUG("Unused slot found (type=%u, slot=%u).",
94			    type, i);
95			break;
96		}
97	}
98	/*
99	 * If no unused slot was found, allocate one.
100	 */
101	if (i == osd_nslots[type]) {
102		osd_nslots[type]++;
103		newptr = realloc(osd_destructors[type],
104		    sizeof(osd_destructor_t) * osd_nslots[type], M_OSD,
105		    M_NOWAIT | M_ZERO);
106		if (newptr == NULL) {
107			mtx_unlock(&osd_lock[type]);
108			return (0);
109		}
110		osd_destructors[type] = newptr;
111		OSD_DEBUG("New slot allocated (type=%u, slot=%u).",
112		    type, i + 1);
113	}
114	osd_destructors[type][i] = destructor;
115	mtx_unlock(&osd_lock[type]);
116	return (i + 1);
117}
118
119void
120osd_deregister(u_int type, u_int slot)
121{
122	struct osd *osd, *tosd;
123
124	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
125	KASSERT(slot > 0, ("Invalid slot."));
126	KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot."));
127
128	mtx_lock(&osd_lock[type]);
129	/*
130	 * Free all OSD for the given slot.
131	 */
132	LIST_FOREACH_SAFE(osd, &osd_list[type], osd_next, tosd) {
133		osd_del(type, osd, slot);
134	}
135	/*
136	 * Set destructor to NULL to free the slot.
137	 */
138	osd_destructors[type][slot - 1] = NULL;
139	if (slot == osd_nslots[type]) {
140		osd_nslots[type]--;
141		osd_destructors[type] = realloc(osd_destructors[type],
142		    sizeof(osd_destructor_t) * osd_nslots[type], M_OSD,
143		    M_NOWAIT | M_ZERO);
144		/*
145		 * We always reallocate to smaller size, so we assume it will
146		 * always succeed.
147		 */
148		KASSERT(osd_destructors[type] != NULL, ("realloc() failed"));
149		OSD_DEBUG("Deregistration of the last slot (type=%u, slot=%u).",
150		    type, slot);
151	} else {
152		OSD_DEBUG("Slot deregistration (type=%u, slot=%u).",
153		    type, slot);
154	}
155	mtx_unlock(&osd_lock[type]);
156}
157
158int
159osd_set(u_int type, struct osd *osd, u_int slot, void *value)
160{
161
162	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
163	KASSERT(slot > 0, ("Invalid slot."));
164	KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot."));
165
166	if (osd->osd_nslots == 0) {
167		/*
168		 * First OSD for this object, so we need to allocate space and
169		 * put it onto the list.
170		 */
171		osd->osd_slots = malloc(sizeof(void *) * slot, M_OSD,
172		    M_NOWAIT | M_ZERO);
173		if (osd->osd_slots == NULL)
174			return (ENOMEM);
175		osd->osd_nslots = slot;
176		mtx_lock(&osd_lock[type]);
177		LIST_INSERT_HEAD(&osd_list[type], osd, osd_next);
178		mtx_unlock(&osd_lock[type]);
179		OSD_DEBUG("Setting first slot (type=%u).", type);
180	} else if (slot > osd->osd_nslots) {
181		void *newptr;
182
183		/*
184		 * Too few slots allocated here, needs to extend the array.
185		 */
186		newptr = realloc(osd->osd_slots, sizeof(void *) * slot, M_OSD,
187		    M_NOWAIT | M_ZERO);
188		if (newptr == NULL)
189			return (ENOMEM);
190		osd->osd_slots = newptr;
191		osd->osd_nslots = slot;
192		OSD_DEBUG("Growing slots array (type=%u).", type);
193	}
194	OSD_DEBUG("Setting slot value (type=%u, slot=%u, value=%p).", type,
195	    slot, value);
196	osd->osd_slots[slot - 1] = value;
197	return (0);
198}
199
200void *
201osd_get(u_int type, struct osd *osd, u_int slot)
202{
203
204	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
205	KASSERT(slot > 0, ("Invalid slot."));
206	KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot."));
207
208	if (slot > osd->osd_nslots) {
209		OSD_DEBUG("Slot doesn't exist (type=%u, slot=%u).", type, slot);
210		return (NULL);
211	}
212
213	OSD_DEBUG("Returning slot value (type=%u, slot=%u, value=%p).", type,
214	    slot, osd->osd_slots[slot - 1]);
215	return (osd->osd_slots[slot - 1]);
216}
217
218void
219osd_del(u_int type, struct osd *osd, u_int slot)
220{
221	int i;
222
223	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
224	KASSERT(slot > 0, ("Invalid slot."));
225	KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot."));
226
227	OSD_DEBUG("Deleting slot (type=%u, slot=%u).", type, slot);
228
229	if (slot > osd->osd_nslots) {
230		OSD_DEBUG("Slot doesn't exist (type=%u, slot=%u).", type, slot);
231		return;
232	}
233	osd_destructors[type][slot - 1](osd->osd_slots[slot - 1]);
234	osd->osd_slots[slot - 1] = NULL;
235	for (i = osd->osd_nslots - 1; i >= 0; i--) {
236		if (osd->osd_slots[i] != NULL) {
237			OSD_DEBUG("Slot still has a value (type=%u, slot=%u).", type, i + 1);
238			break;
239		}
240	}
241	if (i == -1) {
242		int unlock;
243
244		/* No values left for this object. */
245		OSD_DEBUG("No more slots left (type=%u).", type);
246		if ((unlock = !mtx_owned(&osd_lock[type])))
247			mtx_lock(&osd_lock[type]);
248		LIST_REMOVE(osd, osd_next);
249		if (unlock)
250			mtx_unlock(&osd_lock[type]);
251		free(osd->osd_slots, M_OSD);
252		osd->osd_slots = NULL;
253		osd->osd_nslots = 0;
254	} else if (slot == osd->osd_nslots) {
255		/* This was the last slot. */
256		osd->osd_slots = realloc(osd->osd_slots,
257		    sizeof(void *) * (i + 1), M_OSD, M_NOWAIT | M_ZERO);
258		/*
259		 * We always reallocate to smaller size, so we assume it will
260		 * always succeed.
261		 */
262		KASSERT(osd->osd_slots != NULL, ("realloc() failed"));
263		osd->osd_nslots = i + 1;
264		OSD_DEBUG("Reducing slots array to %u (type=%u).",
265		    osd->osd_nslots, type);
266	}
267}
268
269void
270osd_exit(u_int type, struct osd *osd)
271{
272	u_int i;
273
274	KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
275
276	if (osd->osd_nslots == 0) {
277		KASSERT(osd->osd_slots == NULL, ("Non-null osd_slots."));
278		/* No OSD attached, just leave. */
279		return;
280	}
281
282	mtx_lock(&osd_lock[type]);
283	for (i = 1; i <= osd->osd_nslots; i++)
284		osd_del(type, osd, i);
285	mtx_unlock(&osd_lock[type]);
286	OSD_DEBUG("Object exit (type=%u).", type);
287}
288
289static void
290osd_init(void *arg __unused)
291{
292	u_int i;
293
294	for (i = OSD_FIRST; i <= OSD_LAST; i++) {
295		osd_nslots[i] = 0;
296		LIST_INIT(&osd_list[i]);
297		mtx_init(&osd_lock[i], "osd", NULL, MTX_DEF);
298		osd_destructors[i] = NULL;
299	}
300}
301SYSINIT(osd, SI_SUB_LOCK, SI_ORDER_ANY, osd_init, NULL);
302