]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/kern_conf.c
Remove unnecessary locking of Giant around nanotime() in clock_gettime().
[FreeBSD/FreeBSD.git] / sys / kern / kern_conf.c
1 /*-
2  * Copyright (c) 1999-2002 Poul-Henning Kamp
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 AUTHOR 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 AUTHOR 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  * $FreeBSD$
27  */
28
29 #include <sys/param.h>
30 #include <sys/kernel.h>
31 #include <sys/systm.h>
32 #include <sys/lock.h>
33 #include <sys/mutex.h>
34 #include <sys/sysctl.h>
35 #include <sys/module.h>
36 #include <sys/malloc.h>
37 #include <sys/conf.h>
38 #include <sys/vnode.h>
39 #include <sys/queue.h>
40 #include <sys/ctype.h>
41 #include <machine/stdarg.h>
42
43 #ifdef NODEVFS
44 static struct cdevsw    *cdevsw[NUMCDEVSW];
45
46 #endif
47 static MALLOC_DEFINE(M_DEVT, "dev_t", "dev_t storage");
48
49 /*
50  * This is the number of hash-buckets.  Experiements with 'real-life'
51  * udev_t's show that a prime halfway between two powers of two works
52  * best.
53  */
54 #define DEVT_HASH 83
55
56 /* The number of dev_t's we can create before malloc(9) kick in.  */
57 #define DEVT_STASH 50
58
59 static struct cdev devt_stash[DEVT_STASH];
60
61 static LIST_HEAD(, cdev) dev_hash[DEVT_HASH];
62
63 static LIST_HEAD(, cdev) dev_free;
64
65 devfs_create_t *devfs_create_hook;
66 devfs_destroy_t *devfs_destroy_hook;
67
68 static int ready_for_devs;
69
70 static int free_devt;
71 SYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, "");
72
73 /* XXX: This is a hack */
74 void disk_dev_synth(dev_t dev);
75
76 struct cdevsw *
77 devsw(dev_t dev)
78 {
79         if (dev->si_devsw)
80                 return (dev->si_devsw);
81         /* XXX: Hack around our backwards disk code */
82         disk_dev_synth(dev);
83         if (dev->si_devsw)
84                 return (dev->si_devsw);
85 #ifndef NODEVFS
86         return (NULL);
87 #else
88         return(cdevsw[major(dev)]);
89 #endif
90 }
91
92 /*
93  *  Add a cdevsw entry
94  */
95
96 int
97 cdevsw_add(struct cdevsw *newentry)
98 {
99 #ifdef NODEVFS
100         if (newentry->d_maj < 0 || newentry->d_maj >= NUMCDEVSW) {
101                 printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n",
102                     newentry->d_name, newentry->d_maj);
103                 return (EINVAL);
104         }
105
106         if (cdevsw[newentry->d_maj]) {
107                 printf("WARNING: \"%s\" is usurping \"%s\"'s cdevsw[]\n",
108                     newentry->d_name, cdevsw[newentry->d_maj]->d_name);
109         }
110
111         cdevsw[newentry->d_maj] = newentry;
112 #endif
113
114         return (0);
115 }
116
117 /*
118  *  Remove a cdevsw entry
119  */
120
121 int
122 cdevsw_remove(struct cdevsw *oldentry)
123 {
124 #ifdef NODEVFS
125         if (oldentry->d_maj < 0 || oldentry->d_maj >= NUMCDEVSW) {
126                 printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n",
127                     oldentry->d_name, oldentry->d_maj);
128                 return EINVAL;
129         }
130
131         cdevsw[oldentry->d_maj] = NULL;
132 #endif
133
134         return 0;
135 }
136
137 /*
138  * dev_t and u_dev_t primitives
139  */
140
141 int
142 major(dev_t x)
143 {
144         if (x == NODEV)
145                 return NOUDEV;
146         return((x->si_udev >> 8) & 0xff);
147 }
148
149 int
150 minor(dev_t x)
151 {
152         if (x == NODEV)
153                 return NOUDEV;
154         return(x->si_udev & 0xffff00ff);
155 }
156
157 int
158 dev2unit(dev_t x)
159 {
160         int i;
161
162         if (x == NODEV)
163                 return NOUDEV;
164         i = minor(x);
165         return ((i & 0xff) | (i >> 8));
166 }
167
168 int
169 unit2minor(int unit)
170 {
171
172         KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit));
173         return ((unit & 0xff) | ((unit << 8) & ~0xffff));
174 }
175
176 static dev_t
177 allocdev(void)
178 {
179         static int stashed;
180         struct cdev *si;
181
182         if (LIST_FIRST(&dev_free)) {
183                 si = LIST_FIRST(&dev_free);
184                 LIST_REMOVE(si, si_hash);
185         } else if (stashed >= DEVT_STASH) {
186                 MALLOC(si, struct cdev *, sizeof(*si), M_DEVT,
187                     M_USE_RESERVE | M_ZERO);
188         } else {
189                 si = devt_stash + stashed++;
190                 bzero(si, sizeof *si);
191                 si->si_flags |= SI_STASHED;
192         }
193         LIST_INIT(&si->si_children);
194         TAILQ_INIT(&si->si_snapshots);
195         return (si);
196 }
197
198 dev_t
199 makedev(int x, int y)
200 {
201         struct cdev *si;
202         udev_t  udev;
203         int hash;
204
205         if (x == umajor(NOUDEV) && y == uminor(NOUDEV))
206                 panic("makedev of NOUDEV");
207         udev = (x << 8) | y;
208         hash = udev % DEVT_HASH;
209         LIST_FOREACH(si, &dev_hash[hash], si_hash) {
210                 if (si->si_udev == udev)
211                         return (si);
212         }
213         si = allocdev();
214         si->si_udev = udev;
215         LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash);
216         return (si);
217 }
218
219 void
220 freedev(dev_t dev)
221 {
222
223         if (!free_devt)
224                 return;
225         if (SLIST_FIRST(&dev->si_hlist))
226                 return;
227         if (dev->si_devsw || dev->si_drv1 || dev->si_drv2)
228                 return;
229         LIST_REMOVE(dev, si_hash);
230         if (dev->si_flags & SI_STASHED) {
231                 bzero(dev, sizeof(*dev));
232                 dev->si_flags |= SI_STASHED;
233                 LIST_INSERT_HEAD(&dev_free, dev, si_hash);
234         } else {
235                 FREE(dev, M_DEVT);
236         }
237 }
238
239 udev_t
240 dev2udev(dev_t x)
241 {
242         if (x == NODEV)
243                 return NOUDEV;
244         return (x->si_udev);
245 }
246
247 dev_t
248 udev2dev(udev_t x, int b)
249 {
250
251         if (x == NOUDEV)
252                 return (NODEV);
253         switch (b) {
254                 case 0:
255                         return makedev(umajor(x), uminor(x));
256                 case 1:
257                         return (NODEV);
258                 default:
259                         Debugger("udev2dev(...,X)");
260                         return NODEV;
261         }
262 }
263
264 int
265 uminor(udev_t dev)
266 {
267         return(dev & 0xffff00ff);
268 }
269
270 int
271 umajor(udev_t dev)
272 {
273         return((dev & 0xff00) >> 8);
274 }
275
276 udev_t
277 makeudev(int x, int y)
278 {
279         return ((x << 8) | y);
280 }
281
282 dev_t
283 make_dev(struct cdevsw *devsw, int minor, uid_t uid, gid_t gid, int perms, const char *fmt, ...)
284 {
285         dev_t   dev;
286         va_list ap;
287         int i;
288
289         KASSERT(umajor(makeudev(devsw->d_maj, minor)) == devsw->d_maj,
290             ("Invalid minor (%d) in make_dev", minor));
291
292         if (!ready_for_devs) {
293                 printf("WARNING: Driver mistake: make_dev(%s) called before SI_SUB_DRIVERS\n",
294                        fmt);
295                 /* XXX panic here once drivers are cleaned up */
296         }
297
298         dev = makedev(devsw->d_maj, minor);
299         if (dev->si_flags & SI_NAMED) {
300                 printf( "WARNING: Driver mistake: repeat make_dev(\"%s\")\n",
301                     dev->si_name);
302                 panic("don't do that");
303                 return (dev);
304         }
305         va_start(ap, fmt);
306         i = kvprintf(fmt, NULL, dev->si_name, 32, ap);
307         dev->si_name[i] = '\0';
308         va_end(ap);
309         dev->si_devsw = devsw;
310         dev->si_uid = uid;
311         dev->si_gid = gid;
312         dev->si_mode = perms;
313         dev->si_flags |= SI_NAMED;
314
315         if (devfs_create_hook)
316                 devfs_create_hook(dev);
317         return (dev);
318 }
319
320 int
321 dev_named(dev_t pdev, const char *name)
322 {
323         dev_t cdev;
324
325         if (strcmp(devtoname(pdev), name) == 0)
326                 return (1);
327         LIST_FOREACH(cdev, &pdev->si_children, si_siblings)
328                 if (strcmp(devtoname(cdev), name) == 0)
329                         return (1);
330         return (0);
331 }
332
333 void
334 dev_depends(dev_t pdev, dev_t cdev)
335 {
336
337         cdev->si_parent = pdev;
338         cdev->si_flags |= SI_CHILD;
339         LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings);
340 }
341
342 dev_t
343 make_dev_alias(dev_t pdev, const char *fmt, ...)
344 {
345         dev_t   dev;
346         va_list ap;
347         int i;
348
349         dev = allocdev();
350         dev->si_flags |= SI_ALIAS;
351         dev->si_flags |= SI_NAMED;
352         dev_depends(pdev, dev);
353         va_start(ap, fmt);
354         i = kvprintf(fmt, NULL, dev->si_name, 32, ap);
355         dev->si_name[i] = '\0';
356         va_end(ap);
357
358         if (devfs_create_hook)
359                 devfs_create_hook(dev);
360         return (dev);
361 }
362
363 void
364 revoke_and_destroy_dev(dev_t dev)
365 {
366         struct vnode *vp;
367
368         GIANT_REQUIRED;
369
370         vp = SLIST_FIRST(&dev->si_hlist);
371         if (vp != NULL)
372                 VOP_REVOKE(vp, REVOKEALL);
373         destroy_dev(dev);
374 }
375
376 void
377 destroy_dev(dev_t dev)
378 {
379         
380         if (!(dev->si_flags & SI_NAMED)) {
381                 printf( "WARNING: Driver mistake: destroy_dev on %d/%d\n",
382                     major(dev), minor(dev));
383                 panic("don't do that");
384                 return;
385         }
386                 
387         if (devfs_destroy_hook)
388                 devfs_destroy_hook(dev);
389         if (dev->si_flags & SI_CHILD) {
390                 LIST_REMOVE(dev, si_siblings);
391                 dev->si_flags &= ~SI_CHILD;
392         }
393         while (!LIST_EMPTY(&dev->si_children))
394                 destroy_dev(LIST_FIRST(&dev->si_children));
395         dev->si_drv1 = 0;
396         dev->si_drv2 = 0;
397         dev->si_devsw = 0;
398         bzero(&dev->__si_u, sizeof(dev->__si_u));
399         dev->si_flags &= ~SI_NAMED;
400         dev->si_flags &= ~SI_ALIAS;
401         freedev(dev);
402 }
403
404 const char *
405 devtoname(dev_t dev)
406 {
407         char *p;
408         int mynor;
409
410         if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
411                 p = dev->si_name;
412                 if (devsw(dev))
413                         sprintf(p, "#%s/", devsw(dev)->d_name);
414                 else
415                         sprintf(p, "#%d/", major(dev));
416                 p += strlen(p);
417                 mynor = minor(dev);
418                 if (mynor < 0 || mynor > 255)
419                         sprintf(p, "%#x", (u_int)mynor);
420                 else
421                         sprintf(p, "%d", mynor);
422         }
423         return (dev->si_name);
424 }
425
426 int
427 dev_stdclone(char *name, char **namep, const char *stem, int *unit)
428 {
429         int u, i;
430
431         i = strlen(stem);
432         if (bcmp(stem, name, i) != 0)
433                 return (0);
434         if (!isdigit(name[i]))
435                 return (0);
436         u = 0;
437         if (name[i] == '0' && isdigit(name[i+1]))
438                 return (0);
439         while (isdigit(name[i])) {
440                 u *= 10;
441                 u += name[i++] - '0';
442         }
443         if (u > 0xffffff)
444                 return (0);
445         *unit = u;
446         if (namep)
447                 *namep = &name[i];
448         if (name[i]) 
449                 return (2);
450         return (1);
451 }
452
453 /*
454  * Helper sysctl for devname(3).  We're given a {u}dev_t and return
455  * the name, if any, registered by the device driver.
456  */
457 static int
458 sysctl_devname(SYSCTL_HANDLER_ARGS)
459 {
460         int error;
461         udev_t ud;
462         dev_t dev;
463
464         error = SYSCTL_IN(req, &ud, sizeof (ud));
465         if (error)
466                 return (error);
467         if (ud == NOUDEV)
468                 return(EINVAL);
469         dev = makedev(umajor(ud), uminor(ud));
470         if (dev->si_name[0] == '\0')
471                 error = ENOENT;
472         else
473                 error = SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1);
474         freedev(dev);
475         return (error);
476 }
477
478 SYSCTL_PROC(_kern, OID_AUTO, devname, CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_ANYBODY,
479         NULL, 0, sysctl_devname, "", "devname(3) handler");
480
481 /*
482  * Set ready_for_devs; prior to this point, device creation is not allowed.
483  */     
484 static void
485 dev_set_ready(void *junk)
486 {
487         ready_for_devs = 1;
488 }
489
490 SYSINIT(dev_ready, SI_SUB_DEVFS, SI_ORDER_FIRST, dev_set_ready, NULL);