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