FreeBSD kernel kern code
kern_osd.c
Go to the documentation of this file.
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("$BSDSUniX$");
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
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);
static struct mtx osd_list_lock[OSD_LAST+1]
Definition: kern_osd.c:82
static void osd_default_destructor(void *value __unused)
Definition: kern_osd.c:85
void * realloc(void *addr, unsigned long size, struct malloc_type *mtp, int flags)
Definition: kern_malloc.c:616
static MALLOC_DEFINE(M_OSD,"osd","Object Specific Data")
static void osd_init(void *arg __unused)
Definition: kern_osd.c:389
caddr_t value
Definition: linker_if.m:51
int osd_call(u_int type, u_int method, void *obj, void *data)
Definition: kern_osd.c:339
void * malloc(unsigned long size, struct malloc_type *mtp, int flags)
Definition: kern_malloc.c:454
SYSINIT(osd, SI_SUB_LOCK, SI_ORDER_ANY, osd_init, NULL)
void osd_deregister(u_int type, u_int slot)
Definition: kern_osd.c:148
SYSCTL_INT(_debug, OID_AUTO, osd, CTLFLAG_RW,&osd_debug, 0,"OSD debug level")
int * type
Definition: cpufreq_if.m:98
static struct sx osd_module_lock[OSD_LAST+1]
Definition: kern_osd.c:80
static int osd_debug
Definition: kern_osd.c:49
static void do_osd_del(u_int type, struct osd *osd, u_int slot, int list_locked)
Definition: kern_osd.c:287
#define OSD_DEBUG(...)
Definition: kern_osd.c:53
void * osd_get(u_int type, struct osd *osd, u_int slot)
Definition: kern_osd.c:254
void rm_init(struct rmlock *rm, const char *name)
Definition: kern_rmlock.c:275
int osd_set(u_int type, struct osd *osd, u_int slot, void *value)
Definition: kern_osd.c:196
static LIST_HEAD(osd)
Definition: kern_osd.c:72
void free(void *addr, struct malloc_type *mtp)
Definition: kern_malloc.c:554
int osd_register(u_int type, osd_destructor_t destructor, osd_method_t *methods)
Definition: kern_osd.c:91
void mtx_init(struct mtx *m, const char *name, const char *type, int opts)
Definition: kern_mutex.c:837
TUNABLE_INT("debug.osd",&osd_debug)
static struct rmlock osd_object_lock[OSD_LAST+1]
Definition: kern_osd.c:81
void osd_del(u_int type, struct osd *osd, u_int slot)
Definition: kern_osd.c:277
__FBSDID("$BSDSUniX$")
void osd_exit(u_int type, struct osd *osd)
Definition: kern_osd.c:364