]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/kern/kern_osd.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / kern / kern_osd.c
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 static MALLOC_DEFINE(M_OSD, "osd", "Object Specific Data");
48
49 static int osd_debug = 0;
50 TUNABLE_INT("debug.osd", &osd_debug);
51 SYSCTL_INT(_debug, OID_AUTO, osd, CTLFLAG_RW, &osd_debug, 0, "OSD debug level");
52
53 #define OSD_DEBUG(...)  do {                                            \
54         if (osd_debug) {                                                \
55                 printf("OSD (%s:%u): ", __func__, __LINE__);            \
56                 printf(__VA_ARGS__);                                    \
57                 printf("\n");                                           \
58         }                                                               \
59 } while (0)
60
61 static void do_osd_del(u_int type, struct osd *osd, u_int slot,
62     int list_locked);
63
64 /*
65  * Lists of objects with OSD.
66  *
67  * Lock key:
68  *  (m) osd_module_lock
69  *  (o) osd_object_lock
70  *  (l) osd_list_lock
71  */
72 static LIST_HEAD(, osd) osd_list[OSD_LAST + 1];         /* (m) */
73 static osd_method_t *osd_methods[OSD_LAST + 1];         /* (m) */
74 static u_int osd_nslots[OSD_LAST + 1];                  /* (m) */
75 static osd_destructor_t *osd_destructors[OSD_LAST + 1]; /* (o) */
76 static const u_int osd_nmethods[OSD_LAST + 1] = {
77         [OSD_JAIL] = PR_MAXMETHOD,
78 };
79
80 static struct sx osd_module_lock[OSD_LAST + 1];
81 static struct rmlock osd_object_lock[OSD_LAST + 1];
82 static struct mtx osd_list_lock[OSD_LAST + 1];
83
84 static void
85 osd_default_destructor(void *value __unused)
86 {
87         /* Do nothing. */
88 }
89
90 int
91 osd_register(u_int type, osd_destructor_t destructor, osd_method_t *methods)
92 {
93         void *newptr;
94         u_int i, m;
95
96         KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
97
98         /*
99          * If no destructor is given, use default one. We need to use some
100          * destructor, because NULL destructor means unused slot.
101          */
102         if (destructor == NULL)
103                 destructor = osd_default_destructor;
104
105         sx_xlock(&osd_module_lock[type]);
106         /*
107          * First, we try to find unused slot.
108          */
109         for (i = 0; i < osd_nslots[type]; i++) {
110                 if (osd_destructors[type][i] == NULL) {
111                         OSD_DEBUG("Unused slot found (type=%u, slot=%u).",
112                             type, i);
113                         break;
114                 }
115         }
116         /*
117          * If no unused slot was found, allocate one.
118          */
119         if (i == osd_nslots[type]) {
120                 osd_nslots[type]++;
121                 if (osd_nmethods[type] != 0)
122                         osd_methods[type] = realloc(osd_methods[type],
123                             sizeof(osd_method_t) * osd_nslots[type] *
124                             osd_nmethods[type], M_OSD, M_WAITOK);
125                 newptr = malloc(sizeof(osd_destructor_t) * osd_nslots[type],
126                     M_OSD, M_WAITOK);
127                 rm_wlock(&osd_object_lock[type]);
128                 bcopy(osd_destructors[type], newptr,
129                     sizeof(osd_destructor_t) * i);
130                 free(osd_destructors[type], M_OSD);
131                 osd_destructors[type] = newptr;
132                 rm_wunlock(&osd_object_lock[type]);
133                 OSD_DEBUG("New slot allocated (type=%u, slot=%u).",
134                     type, i + 1);
135         }
136
137         osd_destructors[type][i] = destructor;
138         if (osd_nmethods[type] != 0) {
139                 for (m = 0; m < osd_nmethods[type]; m++)
140                         osd_methods[type][i * osd_nmethods[type] + m] =
141                             methods != NULL ? methods[m] : NULL;
142         }
143         sx_xunlock(&osd_module_lock[type]);
144         return (i + 1);
145 }
146
147 void
148 osd_deregister(u_int type, u_int slot)
149 {
150         struct osd *osd, *tosd;
151
152         KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
153         KASSERT(slot > 0, ("Invalid slot."));
154         KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot."));
155
156         sx_xlock(&osd_module_lock[type]);
157         rm_wlock(&osd_object_lock[type]);
158         /*
159          * Free all OSD for the given slot.
160          */
161         mtx_lock(&osd_list_lock[type]);
162         LIST_FOREACH_SAFE(osd, &osd_list[type], osd_next, tosd)
163                 do_osd_del(type, osd, slot, 1);
164         mtx_unlock(&osd_list_lock[type]);
165         /*
166          * Set destructor to NULL to free the slot.
167          */
168         osd_destructors[type][slot - 1] = NULL;
169         if (slot == osd_nslots[type]) {
170                 osd_nslots[type]--;
171                 osd_destructors[type] = realloc(osd_destructors[type],
172                     sizeof(osd_destructor_t) * osd_nslots[type], M_OSD,
173                     M_NOWAIT | M_ZERO);
174                 if (osd_nmethods[type] != 0)
175                         osd_methods[type] = realloc(osd_methods[type],
176                             sizeof(osd_method_t) * osd_nslots[type] *
177                             osd_nmethods[type], M_OSD, M_NOWAIT | M_ZERO);
178                 /*
179                  * We always reallocate to smaller size, so we assume it will
180                  * always succeed.
181                  */
182                 KASSERT(osd_destructors[type] != NULL &&
183                     (osd_nmethods[type] == 0 || osd_methods[type] != NULL),
184                     ("realloc() failed"));
185                 OSD_DEBUG("Deregistration of the last slot (type=%u, slot=%u).",
186                     type, slot);
187         } else {
188                 OSD_DEBUG("Slot deregistration (type=%u, slot=%u).",
189                     type, slot);
190         }
191         rm_wunlock(&osd_object_lock[type]);
192         sx_xunlock(&osd_module_lock[type]);
193 }
194
195 int
196 osd_set(u_int type, struct osd *osd, u_int slot, void *value)
197 {
198         struct rm_priotracker tracker;
199
200         KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
201         KASSERT(slot > 0, ("Invalid slot."));
202         KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot."));
203
204         rm_rlock(&osd_object_lock[type], &tracker);
205         if (slot > osd->osd_nslots) {
206                 if (value == NULL) {
207                         OSD_DEBUG(
208                             "Not allocating null slot (type=%u, slot=%u).",
209                             type, slot);
210                         rm_runlock(&osd_object_lock[type], &tracker);
211                         return (0);
212                 } else if (osd->osd_nslots == 0) {
213                         /*
214                          * First OSD for this object, so we need to allocate
215                          * space and put it onto the list.
216                          */
217                         osd->osd_slots = malloc(sizeof(void *) * slot, M_OSD,
218                             M_NOWAIT | M_ZERO);
219                         if (osd->osd_slots == NULL) {
220                                 rm_runlock(&osd_object_lock[type], &tracker);
221                                 return (ENOMEM);
222                         }
223                         osd->osd_nslots = slot;
224                         mtx_lock(&osd_list_lock[type]);
225                         LIST_INSERT_HEAD(&osd_list[type], osd, osd_next);
226                         mtx_unlock(&osd_list_lock[type]);
227                         OSD_DEBUG("Setting first slot (type=%u).", type);
228                 } else {
229                         void *newptr;
230
231                         /*
232                          * Too few slots allocated here, needs to extend
233                          * the array.
234                          */
235                         newptr = realloc(osd->osd_slots, sizeof(void *) * slot,
236                             M_OSD, M_NOWAIT | M_ZERO);
237                         if (newptr == NULL) {
238                                 rm_runlock(&osd_object_lock[type], &tracker);
239                                 return (ENOMEM);
240                         }
241                         osd->osd_slots = newptr;
242                         osd->osd_nslots = slot;
243                         OSD_DEBUG("Growing slots array (type=%u).", type);
244                 }
245         }
246         OSD_DEBUG("Setting slot value (type=%u, slot=%u, value=%p).", type,
247             slot, value);
248         osd->osd_slots[slot - 1] = value;
249         rm_runlock(&osd_object_lock[type], &tracker);
250         return (0);
251 }
252
253 void *
254 osd_get(u_int type, struct osd *osd, u_int slot)
255 {
256         struct rm_priotracker tracker;
257         void *value;
258
259         KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
260         KASSERT(slot > 0, ("Invalid slot."));
261         KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot."));
262
263         rm_rlock(&osd_object_lock[type], &tracker);
264         if (slot > osd->osd_nslots) {
265                 value = NULL;
266                 OSD_DEBUG("Slot doesn't exist (type=%u, slot=%u).", type, slot);
267         } else {
268                 value = osd->osd_slots[slot - 1];
269                 OSD_DEBUG("Returning slot value (type=%u, slot=%u, value=%p).",
270                     type, slot, value);
271         }
272         rm_runlock(&osd_object_lock[type], &tracker);
273         return (value);
274 }
275
276 void
277 osd_del(u_int type, struct osd *osd, u_int slot)
278 {
279         struct rm_priotracker tracker;
280
281         rm_rlock(&osd_object_lock[type], &tracker);
282         do_osd_del(type, osd, slot, 0);
283         rm_runlock(&osd_object_lock[type], &tracker);
284 }
285
286 static void
287 do_osd_del(u_int type, struct osd *osd, u_int slot, int list_locked)
288 {
289         int i;
290
291         KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
292         KASSERT(slot > 0, ("Invalid slot."));
293         KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot."));
294
295         OSD_DEBUG("Deleting slot (type=%u, slot=%u).", type, slot);
296
297         if (slot > osd->osd_nslots) {
298                 OSD_DEBUG("Slot doesn't exist (type=%u, slot=%u).", type, slot);
299                 return;
300         }
301         if (osd->osd_slots[slot - 1] != NULL) {
302                 osd_destructors[type][slot - 1](osd->osd_slots[slot - 1]);
303                 osd->osd_slots[slot - 1] = NULL;
304         }
305         for (i = osd->osd_nslots - 1; i >= 0; i--) {
306                 if (osd->osd_slots[i] != NULL) {
307                         OSD_DEBUG("Slot still has a value (type=%u, slot=%u).",
308                             type, i + 1);
309                         break;
310                 }
311         }
312         if (i == -1) {
313                 /* No values left for this object. */
314                 OSD_DEBUG("No more slots left (type=%u).", type);
315                 if (!list_locked)
316                         mtx_lock(&osd_list_lock[type]);
317                 LIST_REMOVE(osd, osd_next);
318                 if (!list_locked)
319                         mtx_unlock(&osd_list_lock[type]);
320                 free(osd->osd_slots, M_OSD);
321                 osd->osd_slots = NULL;
322                 osd->osd_nslots = 0;
323         } else if (slot == osd->osd_nslots) {
324                 /* This was the last slot. */
325                 osd->osd_slots = realloc(osd->osd_slots,
326                     sizeof(void *) * (i + 1), M_OSD, M_NOWAIT | M_ZERO);
327                 /*
328                  * We always reallocate to smaller size, so we assume it will
329                  * always succeed.
330                  */
331                 KASSERT(osd->osd_slots != NULL, ("realloc() failed"));
332                 osd->osd_nslots = i + 1;
333                 OSD_DEBUG("Reducing slots array to %u (type=%u).",
334                     osd->osd_nslots, type);
335         }
336 }
337
338 int
339 osd_call(u_int type, u_int method, void *obj, void *data)
340 {
341         osd_method_t methodfun;
342         int error, i;
343
344         KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
345         KASSERT(method < osd_nmethods[type], ("Invalid method."));
346
347         /*
348          * Call this method for every slot that defines it, stopping if an
349          * error is encountered.
350          */
351         error = 0;
352         sx_slock(&osd_module_lock[type]);
353         for (i = 0; i < osd_nslots[type]; i++) {
354                 methodfun =
355                     osd_methods[type][i * osd_nmethods[type] + method];
356                 if (methodfun != NULL && (error = methodfun(obj, data)) != 0)
357                         break;
358         }
359         sx_sunlock(&osd_module_lock[type]);
360         return (error);
361 }
362
363 void
364 osd_exit(u_int type, struct osd *osd)
365 {
366         struct rm_priotracker tracker;
367         u_int i;
368
369         KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
370
371         if (osd->osd_nslots == 0) {
372                 KASSERT(osd->osd_slots == NULL, ("Non-null osd_slots."));
373                 /* No OSD attached, just leave. */
374                 return;
375         }
376
377         rm_rlock(&osd_object_lock[type], &tracker);
378         for (i = 1; i <= osd->osd_nslots; i++) {
379                 if (osd_destructors[type][i - 1] != NULL)
380                         do_osd_del(type, osd, i, 0);
381                 else
382                         OSD_DEBUG("Unused slot (type=%u, slot=%u).", type, i);
383         }
384         rm_runlock(&osd_object_lock[type], &tracker);
385         OSD_DEBUG("Object exit (type=%u).", type);
386 }
387
388 static void
389 osd_init(void *arg __unused)
390 {
391         u_int i;
392
393         for (i = OSD_FIRST; i <= OSD_LAST; i++) {
394                 osd_nslots[i] = 0;
395                 LIST_INIT(&osd_list[i]);
396                 sx_init(&osd_module_lock[i], "osd_module");
397                 rm_init(&osd_object_lock[i], "osd_object");
398                 mtx_init(&osd_list_lock[i], "osd_list", NULL, MTX_DEF);
399                 osd_destructors[i] = NULL;
400                 osd_methods[i] = NULL;
401         }
402 }
403 SYSINIT(osd, SI_SUB_LOCK, SI_ORDER_ANY, osd_init, NULL);