FreeBSD/Linux Kernel Cross Reference
sys/kern/subr_prop.c
1 /* $NetBSD: subr_prop.c,v 1.13 2005/02/26 21:34:55 perry Exp $ */
2
3 /*
4 * Copyright (c) 2001 Eduardo Horvath.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Eduardo Horvath.
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: subr_prop.c,v 1.13 2005/02/26 21:34:55 perry Exp $");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/malloc.h>
39 #include <sys/queue.h>
40 #include <sys/sysctl.h>
41 #include <sys/properties.h>
42
43 #ifdef DEBUG
44
45 int propdebug = 0;
46 #define DPRINTF(v, t) if (propdebug) printf t
47 #else
48 #define DPRINTF(v, t)
49 #endif
50
51 /*
52 * Kernel properties database implementation.
53 *
54 * While this could theoretically be flat, lookups
55 * are always done in the following order:
56 *
57 * database, object, name
58 *
59 * So we'll lay out the structures to make this efficient.
60 *
61 */
62 #define KDB_SIZE 32 /* Initial hash table size */
63 #define KDB_MAXLEN 6 /* Max acceptable bucket length */
64 #define KDB_STEP 2 /* Increment size for hash table */
65 #define KDB_HASH(v, s) ((((v)>>16)^((v)>>8))&((s)-1))
66
67 typedef LIST_HEAD(kobj_head, kdbobj) kobj_bucket_t;
68
69 static LIST_HEAD(propdb_list, propdb) propdbs =
70 LIST_HEAD_INITIALIZER(propdbs);
71
72 struct propdb {
73 LIST_ENTRY(propdb) kd_link;
74 char kd_name[MAX_KDBNAME];
75 size_t kd_size;
76
77 /* Hash table of kdbobj structs */
78 kobj_bucket_t *kd_obj;
79 int kd_longest; /* Keep track of collisions */
80 };
81
82 struct kdbobj {
83 LIST_ENTRY(kdbobj) ko_link;
84 opaque_t ko_object;
85 /*
86 * There should only be a dozen props for each object,
87 * so we can keep them in a list.
88 */
89 LIST_HEAD(kprops, kdbprop) ko_props;
90 };
91
92 struct kdbprop {
93 LIST_ENTRY(kdbprop) kp_link;
94 const char *kp_name;
95 const char *kp_val;
96 int kp_len;
97 int kp_type;
98 };
99
100
101 static struct kdbprop *allocprop(const char *name, size_t len, int wait);
102 static void kdb_rehash(struct propdb *db);
103 static struct kdbobj *kdbobj_find(propdb_t db, opaque_t object,
104 int create, int wait);
105 static int prop_insert(struct kdbobj *obj, const char *name, void *val,
106 size_t len, int type, int wait);
107
108 MALLOC_DEFINE(M_PROP, "prop", "Kernel properties structures");
109
110 /*
111 * Allocate a prop structure large enough to hold
112 * `name' and `len' bytes of data. For PROP_CONST
113 * pass in a `len' of 0.
114 */
115 static struct kdbprop *
116 allocprop(const char *name, size_t len, int wait)
117 {
118 struct kdbprop *kp;
119 char *np, *vp;
120 size_t dsize, nsize;
121
122 dsize = ALIGN(len);
123 nsize = ALIGN(strlen(name) + 1);
124
125 DPRINTF(x, ("allocprop: allocating %lu bytes for %s %s\n",
126 (unsigned long)(sizeof(struct kdbprop) + dsize + nsize), name,
127 wait ? "can wait" : "can't wait"));
128
129 kp = (struct kdbprop *)malloc(sizeof(struct kdbprop) + dsize + nsize,
130 M_PROP, wait ? M_WAITOK : M_NOWAIT);
131
132 DPRINTF(x, ("allocprop: got %p for prop\n", kp));
133
134 if (kp) {
135 /* Install name and init pointers */
136 vp = (char *)&kp[1];
137 kp->kp_val = (const char *)vp;
138 np = vp + dsize;
139 strlcpy(np, name, nsize);
140 kp->kp_name = (const char *)np;
141 kp->kp_len = len;
142 }
143 return (kp);
144 }
145
146
147 /*
148 * If the database hash chains grow too long try to resize
149 * the hash table. Failure is not catastrophic.
150 */
151 static void
152 kdb_rehash(struct propdb *db)
153 {
154 struct kdbobj *obj;
155 kobj_bucket_t *new, *old = db->kd_obj;
156 long hash;
157 size_t i, newsize = (db->kd_size << KDB_STEP);
158 int s;
159
160 new = (kobj_bucket_t *)malloc(sizeof(kobj_bucket_t) * newsize,
161 M_PROP, M_NOWAIT);
162 if (new == NULL)
163 return;
164 s = splvm();
165 for (i = 0; i < newsize; i++)
166 LIST_INIT(&new[i]);
167
168 /* Now pop an object from the old table and insert it in the new one. */
169 for (i = 0; i < db->kd_size; i++) {
170 while ((obj = LIST_FIRST(&old[i])) != NULL) {
171 LIST_REMOVE(obj, ko_link);
172 hash = (long)obj->ko_object;
173 hash = KDB_HASH(hash, db->kd_size);
174 LIST_INSERT_HEAD(&new[hash], obj, ko_link);
175 }
176 }
177 db->kd_size = newsize;
178 db->kd_obj = new;
179 splx(s);
180 free(old, M_PROP);
181 }
182
183 /*
184 * For propdb structures we use a simple power-of-2
185 * hash.
186 */
187
188 propdb_t
189 propdb_create(const char *name)
190 {
191 struct propdb *db;
192 size_t i;
193
194 db = (struct propdb *)malloc(sizeof(struct propdb),
195 M_PROP, M_WAITOK);
196
197 strncpy(db->kd_name, name, sizeof(db->kd_name));
198
199 /* Initialize the hash table. */
200 db->kd_size = KDB_SIZE;
201 db->kd_longest = 0;
202 db->kd_obj = (kobj_bucket_t *)malloc(sizeof(kobj_bucket_t) *
203 db->kd_size, M_PROP, M_WAITOK);
204 for (i = 0; i < db->kd_size; i++)
205 LIST_INIT(&db->kd_obj[i]);
206 LIST_INSERT_HEAD(&propdbs, db, kd_link);
207 return (db);
208 }
209
210 void
211 propdb_destroy(propdb_t db)
212 {
213 struct kdbobj *obj;
214 struct kdbprop *prop;
215 size_t i;
216
217 #ifdef DIAGNOSTIC
218 struct propdb *p;
219
220 /* Make sure we have a handle to a valid database */
221 LIST_FOREACH(p, &propdbs, kd_link) {
222 if (p == db) break;
223 }
224 if (p == NULL) panic("propdb_destroy: invalid database");
225 #endif
226 LIST_REMOVE(db, kd_link);
227
228 /* Empty out each hash bucket */
229 for (i = 0; i < db->kd_size; i++) {
230 while ((obj = LIST_FIRST(&db->kd_obj[i]))) {
231 LIST_REMOVE(obj, ko_link);
232 while ((prop = LIST_FIRST(&obj->ko_props))) {
233 LIST_REMOVE(prop, kp_link);
234 free(prop, M_PROP);
235 }
236 free(obj, M_PROP);
237 }
238 }
239 free(db->kd_obj, M_PROP);
240 free(db, M_PROP);
241 }
242
243 /*
244 * Find an object in the database and possibly create it too.
245 */
246 static struct kdbobj *
247 kdbobj_find(propdb_t db, opaque_t object, int create, int wait)
248 {
249 struct kdbobj *obj;
250 long hash = (long)object;
251 int i;
252
253 /* Find our object */
254 hash = KDB_HASH(hash, db->kd_size);
255 i=0;
256 LIST_FOREACH(obj, &db->kd_obj[hash], ko_link) {
257 i++; /* Measure chain depth */
258 if (obj->ko_object == object)
259 break;
260 }
261 if (create && (obj == NULL)) {
262 /* Need a new object. */
263 obj = (struct kdbobj *)malloc(sizeof(struct kdbobj),
264 M_PROP, wait ? M_WAITOK : M_NOWAIT);
265
266 if (!obj) {
267 return (obj);
268 }
269
270 /* Handle hash table growth */
271 if (++i > db->kd_longest)
272 db->kd_longest = i;
273 if (db->kd_longest > KDB_MAXLEN) {
274 /* Increase the size of our hash table */
275 kdb_rehash(db);
276 }
277
278 /* Initialize object */
279 obj->ko_object = object;
280 LIST_INIT(&obj->ko_props);
281 LIST_INSERT_HEAD(&db->kd_obj[hash], obj, ko_link);
282 }
283 return (obj);
284 }
285
286 /*
287 * Internal property insertion routine.
288 */
289 static int
290 prop_insert(struct kdbobj *obj, const char *name, void *val, size_t len,
291 int type, int wait)
292 {
293 struct kdbprop *prop = NULL, *oprop;
294
295 /* Does the prop exist already? */
296 LIST_FOREACH(oprop, &obj->ko_props, kp_link) {
297 if (strcmp(oprop->kp_name, name) == 0)
298 break;
299 }
300 if (oprop) {
301 /* Can is it big enough? */
302 if ((type & PROP_CONST) ||
303 ((ALIGN(len) < ALIGN(oprop->kp_len)) &&
304 (oprop->kp_type & PROP_CONST) == 0)) {
305 /* We can reuse it */
306 prop = oprop;
307 }
308 }
309 if (!prop) {
310 /* Allocate a new prop */
311 if (type & PROP_CONST)
312 prop = allocprop(name, 0, wait);
313 else
314 prop = allocprop(name, len, wait);
315 if (!prop) return (wait ? ENOMEM : EAGAIN);
316 }
317 /* Set the values */
318 if (type & PROP_CONST) {
319 prop->kp_val = val;
320 } else {
321 char *dest = (char *)prop->kp_val;
322 memcpy(dest, val, len);
323 }
324 prop->kp_len = len;
325 prop->kp_type = type;
326
327 /* Now clean up if necessary */
328 if (prop != oprop) {
329 LIST_INSERT_HEAD(&obj->ko_props, prop, kp_link);
330 if (oprop) {
331 LIST_REMOVE(oprop, kp_link);
332 free(oprop, M_PROP);
333 }
334 }
335 return (0);
336 }
337
338 int
339 prop_set(propdb_t db, opaque_t object, const char *name,
340 void *val, size_t len, int type, int wait)
341 {
342 struct kdbobj *obj;
343 struct kdbprop *prop = NULL, *oprop;
344 int s;
345
346 DPRINTF(x, ("prop_set: %p, %p, %s, %p, %lx, %x, %d\n", db, object,
347 name ? name : "NULL", val, (unsigned long)len, type, wait));
348
349 /* Find our object */
350 s = splvm();
351 obj = kdbobj_find(db, object, 1, wait);
352 if (!obj) {
353 splx(s);
354 return (wait ? ENOMEM : EAGAIN);
355 }
356
357 #if 1
358 {
359 int rv;
360
361 oprop = prop; /* XXXX -- use vars to make gcc happy. */
362 rv = prop_insert(obj, name, val, len, type, wait);
363 splx(s);
364 return (rv);
365 }
366 #else
367 /* Does the prop exist already? */
368 LIST_FOREACH(oprop, &obj->ko_props, kp_link) {
369 if (strcmp(oprop->kp_name, name) == 0)
370 break;
371 }
372 if (oprop) {
373 /* Can is it big enough? */
374 if ((type & PROP_CONST) ||
375 ((ALIGN(len) < ALIGN(oprop->kp_len)) &&
376 (oprop->kp_type & PROP_CONST) == 0)) {
377 /* We can reuse it */
378 prop = oprop;
379 }
380 }
381 if (!prop) {
382 /* Allocate a new prop */
383 if (type & PROP_CONST)
384 prop = allocprop(name, 0, wait);
385 else
386 prop = allocprop(name, len, wait);
387 if (!prop) return (wait ? ENOMEM : EAGAIN);
388 }
389 /* Set the values */
390 if (type & PROP_CONST) {
391 prop->kp_val = val;
392 } else {
393 char *dest = (char *)prop->kp_val;
394 memcpy(dest, val, len);
395 }
396 prop->kp_len = len;
397 prop->kp_type = type;
398
399 /* Now clean up if necessary */
400 if (prop != oprop) {
401 LIST_INSERT_HEAD(&obj->ko_props, prop, kp_link);
402 if (oprop) {
403 LIST_REMOVE(oprop, kp_link);
404 free(oprop, M_PROP);
405 }
406 }
407 splx(s);
408 return (0);
409 #endif
410 }
411
412 size_t
413 prop_get(propdb_t db, opaque_t object, const char *name, void *val,
414 size_t len, int *type)
415 {
416 struct kdbobj *obj;
417 struct kdbprop *prop = NULL;
418 int s;
419
420 DPRINTF(x, ("prop_get: %p, %p, %s, %p, %lx, %p\n", db, object,
421 name ? name : "NULL", val, (unsigned long)len, type));
422
423 /* Find our object */
424 s = splvm();
425 obj = kdbobj_find(db, object, 0, 0);
426 if (!obj) {
427 splx(s);
428 return (-1);
429 }
430
431 /* find our prop */
432 LIST_FOREACH(prop, &obj->ko_props, kp_link) {
433 if (strcmp(prop->kp_name, name) == 0)
434 break;
435 }
436 if (!prop) {
437 splx(s);
438 DPRINTF(x, ("prop not found\n"));
439 return (-1);
440 }
441
442 /* Copy out our prop */
443 len = min(len, prop->kp_len);
444 if (val && len) {
445 memcpy(val, prop->kp_val, len);
446 }
447 if (type)
448 *type = prop->kp_type;
449 splx(s);
450 DPRINTF(x, ("copied %ld of %d\n", (long) len, prop->kp_len));
451 return (prop->kp_len);
452 }
453
454 /*
455 * Return the total number of objects in the database and as
456 * many as fit in the buffer.
457 */
458 size_t
459 prop_objs(propdb_t db, opaque_t *objects, size_t len)
460 {
461 struct kdbobj *obj;
462 size_t i, j, nelem = (len / sizeof(opaque_t));
463 int s;
464
465 DPRINTF(x, ("prop_objs: %p, %p, %lx\n", db, objects,
466 (unsigned long)len));
467
468 s = splvm();
469 for (i = 0, j = 0; i < db->kd_size; i++) {
470 LIST_FOREACH(obj, &db->kd_obj[i], ko_link) {
471 if (objects && j < nelem)
472 objects[j] = obj->ko_object;
473 j++;
474 }
475 }
476 splx(s);
477 return (j * sizeof(opaque_t));
478 }
479
480 /*
481 * Return the total number of property names associated with an object
482 * and as many as fit in the buffer.
483 */
484 size_t
485 prop_list(propdb_t db, opaque_t object, char *names, size_t len)
486 {
487 struct kdbobj *obj;
488 struct kdbprop *prop = NULL;
489 int s, i = 0;
490 char *sp, *ep;
491
492 DPRINTF(x, ("prop_list: %p, %p, %p, %lx\n",
493 db, object, names, (unsigned long)len));
494
495 /* Find our source object */
496 s = splvm();
497 obj = kdbobj_find(db, object, 0, 0);
498 if (obj == NULL) {
499 splx(s);
500 return (0);
501 }
502
503 sp = names;
504 ep = names + len;
505 LIST_FOREACH(prop, &obj->ko_props, kp_link) {
506 i = strlen(prop->kp_name) + 1;
507 if (names + i + 1 < ep) {
508 strlcpy(names, prop->kp_name, ep - names);
509 names += i;
510 /* Add an extra NUL */
511 names[i + 1] = 0;
512 }
513 }
514 splx(s);
515 return (names - sp);
516 }
517
518 int
519 prop_delete(propdb_t db, opaque_t object, const char *name)
520 {
521 struct kdbobj *obj;
522 struct kdbprop *prop = NULL;
523 int s, i = 0;
524
525 DPRINTF(x, ("prop_delete: %p, %p, %s\n", db, object,
526 name ? name : "NULL"));
527
528 /* Find our object */
529 s = splvm();
530 obj = kdbobj_find(db, object, 0, 0);
531 if (obj == NULL) {
532 splx(s);
533 return (0);
534 }
535
536 if (name) {
537 /* Find our prop */
538 LIST_FOREACH(prop, &obj->ko_props, kp_link) {
539 if (strcmp(prop->kp_name, name) == 0)
540 break;
541 }
542 if (!prop) {
543 splx(s);
544 return (0);
545 }
546 LIST_REMOVE(prop, kp_link);
547 free(prop, M_PROP);
548 i++;
549 } else {
550 while ((prop = LIST_FIRST(&obj->ko_props))) {
551 LIST_REMOVE(prop, kp_link);
552 free(prop, M_PROP);
553 i++;
554 }
555 }
556 if (LIST_EMPTY(&obj->ko_props)) {
557 /* Free up the empty container. */
558 LIST_REMOVE(obj, ko_link);
559 free(obj, M_PROP);
560 }
561 splx(s);
562 return (i);
563 }
564
565 int
566 prop_copy(propdb_t db, opaque_t source, opaque_t dest, int wait)
567 {
568 struct kdbobj *nobj, *oobj;
569 struct kdbprop *prop, *oprop, *srcp;
570 int s;
571
572 DPRINTF(x, ("prop_copy: %p, %p, %p, %d\n", db, source, dest, wait));
573
574 /* Find our source object */
575 s = splvm();
576 oobj = kdbobj_find(db, source, 0, wait);
577 if (oobj == NULL) {
578 splx(s);
579 return (EINVAL);
580 }
581
582 /* Find our dest object */
583 nobj = kdbobj_find(db, dest, 1, wait);
584 if (!nobj) {
585 splx(s);
586 return (wait ? ENOMEM : EAGAIN);
587 }
588
589 /* Copy these properties over now */
590 LIST_FOREACH(srcp, &oobj->ko_props, kp_link) {
591
592 DPRINTF(x, ("prop_copy: copying prop %s\n",
593 srcp->kp_name));
594
595 #if 1
596 {
597 int rv;
598
599 oprop = prop; /* XXXX -- use vars to make gcc happy. */
600 rv = prop_insert(nobj, srcp->kp_name,
601 (void *)srcp->kp_val, srcp->kp_len,
602 srcp->kp_type, wait);
603 if (rv) {
604 /* Error of some sort */
605 splx(s);
606 return (rv);
607 }
608 }
609 #else
610 /* Does the prop exist already? */
611 prop = NULL;
612 LIST_FOREACH(oprop, &nobj->ko_props, kp_link) {
613 if (strcmp(oprop->kp_name, srcp->kp_name) == 0)
614 break;
615 }
616 if (oprop) {
617
618 DPRINTF(x, ("prop_copy: found old prop %p\n",
619 oprop));
620
621 /* Can is it big enough? */
622 if ((srcp->kp_type & PROP_CONST) ||
623 ((ALIGN(srcp->kp_len) < ALIGN(oprop->kp_len)) &&
624 (oprop->kp_type & PROP_CONST) == 0)) {
625 /* We can reuse it */
626 prop = oprop;
627 DPRINTF(x, ("prop_copy: using old prop\n"));
628 }
629 }
630 if (!prop) {
631 /* Allocate a new prop */
632 if (srcp->kp_type & PROP_CONST)
633 prop = allocprop(srcp->kp_name, 0, wait);
634 else
635 prop = allocprop(srcp->kp_name,
636 srcp->kp_len, wait);
637 if (!prop) {
638 splx(s);
639 return (wait ? ENOMEM : EAGAIN);
640 }
641 }
642 /* Set the values */
643 if (srcp->kp_type & PROP_CONST) {
644 prop->kp_val = srcp->kp_val;
645 } else {
646 char *dest = (char *)prop->kp_val;
647 memcpy(dest, srcp->kp_val, srcp->kp_len);
648 }
649 prop->kp_len = srcp->kp_len;
650 prop->kp_type = srcp->kp_type;
651
652 /* Now clean up if necessary */
653 if (prop != oprop) {
654
655 DPRINTF(x, ("prop_copy: inserting prop %p\n", prop));
656 LIST_INSERT_HEAD(&nobj->ko_props, prop, kp_link);
657 if (oprop) {
658 DPRINTF(x, ("prop_copy: removing prop %p\n",
659 oprop));
660 LIST_REMOVE(oprop, kp_link);
661 free(oprop, M_PROP);
662 }
663 }
664 #endif
665 }
666 DPRINTF(x, ("prop_copy: done\n"));
667 splx(s);
668 return (0);
669
670 }
Cache object: 5cc22e12dbd047d64fb4babe832aaa8e
|