kern_conf.c revision 111622
111126Sjulian/*-
2103722Sphk * Copyright (c) 1999-2002 Poul-Henning Kamp
311126Sjulian * All rights reserved.
411126Sjulian *
511126Sjulian * Redistribution and use in source and binary forms, with or without
611126Sjulian * modification, are permitted provided that the following conditions
711126Sjulian * are met:
811126Sjulian * 1. Redistributions of source code must retain the above copyright
911126Sjulian *    notice, this list of conditions and the following disclaimer.
1011126Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1111126Sjulian *    notice, this list of conditions and the following disclaimer in the
1211126Sjulian *    documentation and/or other materials provided with the distribution.
1311126Sjulian *
14103722Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15103722Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1611126Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17103722Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1811126Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1911126Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2011126Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2111126Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2211126Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2311126Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2411126Sjulian * SUCH DAMAGE.
2511126Sjulian *
2650477Speter * $FreeBSD: head/sys/kern/kern_conf.c 111622 2003-02-27 14:46:51Z phk $
2711126Sjulian */
2811126Sjulian
2911126Sjulian#include <sys/param.h>
3048936Sphk#include <sys/kernel.h>
3183366Sjulian#include <sys/systm.h>
32111179Sphk#include <sys/bio.h>
3390737Sgreen#include <sys/lock.h>
3490737Sgreen#include <sys/mutex.h>
3550549Sphk#include <sys/sysctl.h>
3636735Sdfr#include <sys/module.h>
3748936Sphk#include <sys/malloc.h>
3811126Sjulian#include <sys/conf.h>
3912954Sjulian#include <sys/vnode.h>
4048936Sphk#include <sys/queue.h>
4165374Sphk#include <sys/ctype.h>
4249535Sphk#include <machine/stdarg.h>
4311126Sjulian
4469774Sphkstatic MALLOC_DEFINE(M_DEVT, "dev_t", "dev_t storage");
4548936Sphk
46111622Sphk/* Built at compile time from sys/conf/majors */
47111622Sphkextern unsigned char reserved_majors[256];
48111622Sphk
4950522Sphk/*
5050522Sphk * This is the number of hash-buckets.  Experiements with 'real-life'
5150522Sphk * udev_t's show that a prime halfway between two powers of two works
5250522Sphk * best.
5350522Sphk */
5448936Sphk#define DEVT_HASH 83
5550522Sphk
5650522Sphk/* The number of dev_t's we can create before malloc(9) kick in.  */
5748936Sphk#define DEVT_STASH 50
5848936Sphk
59104043Sphkstatic struct cdev devt_stash[DEVT_STASH];
6048936Sphk
61104043Sphkstatic LIST_HEAD(, cdev) dev_hash[DEVT_HASH];
6248936Sphk
63104043Sphkstatic LIST_HEAD(, cdev) dev_free;
6450549Sphk
6550254Sphkdevfs_create_t *devfs_create_hook;
6665374Sphkdevfs_destroy_t *devfs_destroy_hook;
6750254Sphk
6889118Smsmithstatic int ready_for_devs;
6989118Smsmith
7050549Sphkstatic int free_devt;
7150549SphkSYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, "");
7250549Sphk
73111179Sphk/* Define a dead_cdevsw for use when devices leave unexpectedly. */
7485603Sphk
75111179Sphkstatic int
76111179Sphkenxio(void)
77111179Sphk{
78111179Sphk	return (ENXIO);
79111179Sphk}
80111179Sphk
81111179Sphk#define dead_open	(d_open_t *)enxio
82111179Sphk#define dead_close	(d_close_t *)enxio
83111179Sphk#define dead_read	(d_read_t *)enxio
84111179Sphk#define dead_write	(d_write_t *)enxio
85111179Sphk#define dead_ioctl	(d_ioctl_t *)enxio
86111179Sphk#define dead_poll	nopoll
87111179Sphk#define dead_mmap	nommap
88111179Sphk
89111179Sphkstatic void
90111179Sphkdead_strategy(struct bio *bp)
91111179Sphk{
92111179Sphk
93111179Sphk	biofinish(bp, NULL, ENXIO);
94111179Sphk}
95111179Sphk
96111220Sphk#define dead_dump	(dumper_t *)enxio
97111179Sphk
98111179Sphkstatic int
99111179Sphkdead_psize(dev_t dev)
100111179Sphk{
101111179Sphk
102111179Sphk	return (-1);
103111179Sphk}
104111179Sphk
105111179Sphk#define dead_kqfilter	(d_kqfilter_t *)enxio
106111179Sphk
107111179Sphkstatic struct cdevsw dead_cdevsw = {
108111179Sphk	/* open */	dead_open,
109111179Sphk	/* close */	dead_close,
110111179Sphk	/* read */	dead_read,
111111179Sphk	/* write */	dead_write,
112111179Sphk	/* ioctl */	dead_ioctl,
113111179Sphk	/* poll */	dead_poll,
114111179Sphk	/* mmap */	dead_mmap,
115111179Sphk	/* strategy */	dead_strategy,
116111179Sphk	/* name */	"dead",
117111179Sphk	/* maj */	255,
118111179Sphk	/* dump */	dead_dump,
119111179Sphk	/* psize */	dead_psize,
120111179Sphk	/* flags */	0,
121111179Sphk	/* kqfilter */	dead_kqfilter
122111179Sphk};
123111179Sphk
124111179Sphk
12547640Sphkstruct cdevsw *
12647640Sphkdevsw(dev_t dev)
12751927Sphk{
12849679Sphk	if (dev->si_devsw)
12949679Sphk		return (dev->si_devsw);
130111179Sphk	return (&dead_cdevsw);
13147640Sphk}
13247640Sphk
13347640Sphk/*
13447028Sphk * dev_t and u_dev_t primitives
13547028Sphk */
13647028Sphk
13751927Sphkint
13847028Sphkmajor(dev_t x)
13947028Sphk{
14048936Sphk	if (x == NODEV)
14148936Sphk		return NOUDEV;
14248936Sphk	return((x->si_udev >> 8) & 0xff);
14347028Sphk}
14447028Sphk
14547028Sphkint
14647028Sphkminor(dev_t x)
14747028Sphk{
14848936Sphk	if (x == NODEV)
14948936Sphk		return NOUDEV;
15048936Sphk	return(x->si_udev & 0xffff00ff);
15147028Sphk}
15247028Sphk
15349826Sphkint
15466067Sphkdev2unit(dev_t x)
15549826Sphk{
15649826Sphk	int i;
15749826Sphk
15849826Sphk	if (x == NODEV)
15949826Sphk		return NOUDEV;
16049826Sphk	i = minor(x);
16149826Sphk	return ((i & 0xff) | (i >> 8));
16249826Sphk}
16349826Sphk
16466067Sphkint
16566067Sphkunit2minor(int unit)
16666067Sphk{
16766067Sphk
16874522Sphk	KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit));
16966067Sphk	return ((unit & 0xff) | ((unit << 8) & ~0xffff));
17066067Sphk}
17166067Sphk
17264880Sphkstatic dev_t
17364880Sphkallocdev(void)
17464880Sphk{
17564880Sphk	static int stashed;
176104043Sphk	struct cdev *si;
17764880Sphk
178103101Sphk	if (LIST_FIRST(&dev_free)) {
179103101Sphk		si = LIST_FIRST(&dev_free);
180103101Sphk		LIST_REMOVE(si, si_hash);
181103101Sphk	} else if (stashed >= DEVT_STASH) {
182104043Sphk		MALLOC(si, struct cdev *, sizeof(*si), M_DEVT,
183111146Sphk		    M_USE_RESERVE | M_ZERO | M_WAITOK);
18464880Sphk	} else {
18564880Sphk		si = devt_stash + stashed++;
18677215Sphk		bzero(si, sizeof *si);
187103101Sphk		si->si_flags |= SI_STASHED;
18864880Sphk	}
189110317Sphk	si->__si_namebuf[0] = '\0';
190110317Sphk	si->si_name = si->__si_namebuf;
19177215Sphk	LIST_INIT(&si->si_children);
19273942Smckusick	TAILQ_INIT(&si->si_snapshots);
19364880Sphk	return (si);
19464880Sphk}
19564880Sphk
19647680Sphkdev_t
19747028Sphkmakedev(int x, int y)
19847028Sphk{
199104043Sphk	struct cdev *si;
20048936Sphk	udev_t	udev;
20148936Sphk	int hash;
20248936Sphk
20355414Sphk	if (x == umajor(NOUDEV) && y == uminor(NOUDEV))
20471342Sphk		panic("makedev of NOUDEV");
20548936Sphk	udev = (x << 8) | y;
20648936Sphk	hash = udev % DEVT_HASH;
20750549Sphk	LIST_FOREACH(si, &dev_hash[hash], si_hash) {
20848936Sphk		if (si->si_udev == udev)
20948936Sphk			return (si);
21048936Sphk	}
21164880Sphk	si = allocdev();
21248936Sphk	si->si_udev = udev;
21350549Sphk	LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash);
21448936Sphk        return (si);
21547028Sphk}
21647028Sphk
21750549Sphkvoid
21850549Sphkfreedev(dev_t dev)
21950549Sphk{
22050549Sphk
22150549Sphk	if (!free_devt)
22250549Sphk		return;
22350549Sphk	if (SLIST_FIRST(&dev->si_hlist))
22450549Sphk		return;
22551927Sphk	if (dev->si_devsw || dev->si_drv1 || dev->si_drv2)
22650549Sphk		return;
22750549Sphk	LIST_REMOVE(dev, si_hash);
22850549Sphk	if (dev->si_flags & SI_STASHED) {
22950549Sphk		bzero(dev, sizeof(*dev));
23077215Sphk		dev->si_flags |= SI_STASHED;
23150549Sphk		LIST_INSERT_HEAD(&dev_free, dev, si_hash);
23250549Sphk	} else {
23350549Sphk		FREE(dev, M_DEVT);
23450549Sphk	}
23550549Sphk}
23650549Sphk
23747028Sphkudev_t
23847028Sphkdev2udev(dev_t x)
23947028Sphk{
24048936Sphk	if (x == NODEV)
24148936Sphk		return NOUDEV;
24248936Sphk	return (x->si_udev);
24347028Sphk}
24447028Sphk
24547028Sphkdev_t
24647028Sphkudev2dev(udev_t x, int b)
24747028Sphk{
24855414Sphk
24955414Sphk	if (x == NOUDEV)
25055414Sphk		return (NODEV);
25148864Sphk	switch (b) {
25248864Sphk		case 0:
25348864Sphk			return makedev(umajor(x), uminor(x));
25448864Sphk		case 1:
25568063Sphk			return (NODEV);
25648864Sphk		default:
25748864Sphk			Debugger("udev2dev(...,X)");
25848864Sphk			return NODEV;
25948864Sphk	}
26047028Sphk}
26147028Sphk
26247028Sphkint
26347028Sphkuminor(udev_t dev)
26447028Sphk{
26547028Sphk	return(dev & 0xffff00ff);
26647028Sphk}
26747028Sphk
26847028Sphkint
26947028Sphkumajor(udev_t dev)
27047028Sphk{
27147028Sphk	return((dev & 0xff00) >> 8);
27247028Sphk}
27347028Sphk
27447028Sphkudev_t
27548859Sphkmakeudev(int x, int y)
27647028Sphk{
27747028Sphk        return ((x << 8) | y);
27847028Sphk}
27947028Sphk
28049535Sphkdev_t
28181068Simpmake_dev(struct cdevsw *devsw, int minor, uid_t uid, gid_t gid, int perms, const char *fmt, ...)
28249535Sphk{
28349535Sphk	dev_t	dev;
28449535Sphk	va_list ap;
28549535Sphk	int i;
28649535Sphk
287111622Sphk	KASSERT((minor & ~0xffff00ff) == 0,
288111622Sphk	    ("Invalid minor (0x%x) in make_dev", minor));
28971920Sbrian
290111622Sphk	if (devsw->d_maj == MAJOR_AUTO) {
291111622Sphk		for (i = NUMCDEVSW - 1; i > 0; i--)
292111622Sphk			if (reserved_majors[i] != i)
293111622Sphk				break;
294111622Sphk		KASSERT(i > 0, ("Out of major numbers (%s)", devsw->d_name));
295111622Sphk		printf("Allocating major#%d to \"%s\"\n", i, devsw->d_name);
296111622Sphk		devsw->d_maj = i;
297111622Sphk		reserved_majors[i] = i;
298111622Sphk	}
299111622Sphk
30089118Smsmith	if (!ready_for_devs) {
30189118Smsmith		printf("WARNING: Driver mistake: make_dev(%s) called before SI_SUB_DRIVERS\n",
30289118Smsmith		       fmt);
30389118Smsmith		/* XXX panic here once drivers are cleaned up */
30489118Smsmith	}
30589118Smsmith
30649535Sphk	dev = makedev(devsw->d_maj, minor);
30765747Sphk	if (dev->si_flags & SI_NAMED) {
30865747Sphk		printf( "WARNING: Driver mistake: repeat make_dev(\"%s\")\n",
30965747Sphk		    dev->si_name);
31085603Sphk		panic("don't do that");
31165747Sphk		return (dev);
31265747Sphk	}
31349535Sphk	va_start(ap, fmt);
314110318Sphk	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
315110318Sphk	if (i > (sizeof dev->__si_namebuf - 1)) {
316110318Sphk		printf("WARNING: Device name truncated! (%s)",
317110318Sphk		    dev->__si_namebuf);
318110318Sphk	}
31949535Sphk	va_end(ap);
32049535Sphk	dev->si_devsw = devsw;
32164880Sphk	dev->si_uid = uid;
32264880Sphk	dev->si_gid = gid;
32364880Sphk	dev->si_mode = perms;
32465747Sphk	dev->si_flags |= SI_NAMED;
32550092Sjulian
32650254Sphk	if (devfs_create_hook)
32764880Sphk		devfs_create_hook(dev);
32849535Sphk	return (dev);
32949535Sphk}
33049535Sphk
33185076Sjlemonint
33285076Sjlemondev_named(dev_t pdev, const char *name)
33385076Sjlemon{
33485076Sjlemon	dev_t cdev;
33585076Sjlemon
33685076Sjlemon	if (strcmp(devtoname(pdev), name) == 0)
33785076Sjlemon		return (1);
33885076Sjlemon	LIST_FOREACH(cdev, &pdev->si_children, si_siblings)
33985076Sjlemon		if (strcmp(devtoname(cdev), name) == 0)
34085076Sjlemon			return (1);
34185076Sjlemon	return (0);
34285076Sjlemon}
34385076Sjlemon
34477215Sphkvoid
34577215Sphkdev_depends(dev_t pdev, dev_t cdev)
34677215Sphk{
34777215Sphk
34877215Sphk	cdev->si_parent = pdev;
34977215Sphk	cdev->si_flags |= SI_CHILD;
35077215Sphk	LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings);
35177215Sphk}
35277215Sphk
35364880Sphkdev_t
35481068Simpmake_dev_alias(dev_t pdev, const char *fmt, ...)
35564880Sphk{
35664880Sphk	dev_t	dev;
35764880Sphk	va_list ap;
35864880Sphk	int i;
35964880Sphk
36064880Sphk	dev = allocdev();
36164880Sphk	dev->si_flags |= SI_ALIAS;
36265747Sphk	dev->si_flags |= SI_NAMED;
36377215Sphk	dev_depends(pdev, dev);
36464880Sphk	va_start(ap, fmt);
365110318Sphk	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
366110318Sphk	if (i > (sizeof dev->__si_namebuf - 1)) {
367110318Sphk		printf("WARNING: Device name truncated! (%s)",
368110318Sphk		    dev->__si_namebuf);
369110318Sphk	}
37064880Sphk	va_end(ap);
37164880Sphk
37264880Sphk	if (devfs_create_hook)
37364880Sphk		devfs_create_hook(dev);
37464880Sphk	return (dev);
37564880Sphk}
37664880Sphk
37750549Sphkvoid
37890736Sgreenrevoke_and_destroy_dev(dev_t dev)
37990736Sgreen{
38090736Sgreen	struct vnode *vp;
38190736Sgreen
38290736Sgreen	GIANT_REQUIRED;
38390736Sgreen
38490736Sgreen	vp = SLIST_FIRST(&dev->si_hlist);
38590736Sgreen	if (vp != NULL)
38690736Sgreen		VOP_REVOKE(vp, REVOKEALL);
38790736Sgreen	destroy_dev(dev);
38890736Sgreen}
38990736Sgreen
39090736Sgreenvoid
39153000Sphkdestroy_dev(dev_t dev)
39250549Sphk{
39365747Sphk
39465747Sphk	if (!(dev->si_flags & SI_NAMED)) {
39565747Sphk		printf( "WARNING: Driver mistake: destroy_dev on %d/%d\n",
39665747Sphk		    major(dev), minor(dev));
39785603Sphk		panic("don't do that");
39865747Sphk		return;
39965747Sphk	}
40065747Sphk
40165374Sphk	if (devfs_destroy_hook)
40265374Sphk		devfs_destroy_hook(dev);
40377215Sphk	if (dev->si_flags & SI_CHILD) {
40477215Sphk		LIST_REMOVE(dev, si_siblings);
40577215Sphk		dev->si_flags &= ~SI_CHILD;
40677215Sphk	}
40777215Sphk	while (!LIST_EMPTY(&dev->si_children))
40877215Sphk		destroy_dev(LIST_FIRST(&dev->si_children));
40950549Sphk	dev->si_drv1 = 0;
41050549Sphk	dev->si_drv2 = 0;
41150549Sphk	dev->si_devsw = 0;
41295446Sbde	bzero(&dev->__si_u, sizeof(dev->__si_u));
41365747Sphk	dev->si_flags &= ~SI_NAMED;
41465747Sphk	dev->si_flags &= ~SI_ALIAS;
41550549Sphk	freedev(dev);
41650549Sphk}
41750549Sphk
41851225Sbdeconst char *
41949982Sbillfdevtoname(dev_t dev)
42049982Sbillf{
42150549Sphk	char *p;
42251225Sbde	int mynor;
42349982Sbillf
42450549Sphk	if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
42550549Sphk		p = dev->si_name;
42650549Sphk		if (devsw(dev))
42750549Sphk			sprintf(p, "#%s/", devsw(dev)->d_name);
42850549Sphk		else
42950549Sphk			sprintf(p, "#%d/", major(dev));
43050549Sphk		p += strlen(p);
43151225Sbde		mynor = minor(dev);
43251225Sbde		if (mynor < 0 || mynor > 255)
43351225Sbde			sprintf(p, "%#x", (u_int)mynor);
43451225Sbde		else
43551225Sbde			sprintf(p, "%d", mynor);
43650549Sphk	}
43749982Sbillf	return (dev->si_name);
43849982Sbillf}
43965374Sphk
44065374Sphkint
44191998Sphkdev_stdclone(char *name, char **namep, const char *stem, int *unit)
44265374Sphk{
44365374Sphk	int u, i;
44465374Sphk
44575519Sbrian	i = strlen(stem);
44675519Sbrian	if (bcmp(stem, name, i) != 0)
44765374Sphk		return (0);
44865374Sphk	if (!isdigit(name[i]))
44965374Sphk		return (0);
45065374Sphk	u = 0;
45186461Sphk	if (name[i] == '0' && isdigit(name[i+1]))
45286461Sphk		return (0);
45365374Sphk	while (isdigit(name[i])) {
45465374Sphk		u *= 10;
45565374Sphk		u += name[i++] - '0';
45665374Sphk	}
457104523Sgreen	if (u > 0xffffff)
458104523Sgreen		return (0);
45965374Sphk	*unit = u;
46065374Sphk	if (namep)
46165374Sphk		*namep = &name[i];
46265374Sphk	if (name[i])
46365374Sphk		return (2);
46465374Sphk	return (1);
46565374Sphk}
46665632Sphk
46765632Sphk/*
46865632Sphk * Helper sysctl for devname(3).  We're given a {u}dev_t and return
46965632Sphk * the name, if any, registered by the device driver.
47065632Sphk */
47165632Sphkstatic int
47265632Sphksysctl_devname(SYSCTL_HANDLER_ARGS)
47365632Sphk{
47465632Sphk	int error;
47565632Sphk	udev_t ud;
47665632Sphk	dev_t dev;
47765632Sphk
47865632Sphk	error = SYSCTL_IN(req, &ud, sizeof (ud));
47965632Sphk	if (error)
48065632Sphk		return (error);
48171342Sphk	if (ud == NOUDEV)
48271342Sphk		return(EINVAL);
48365632Sphk	dev = makedev(umajor(ud), uminor(ud));
48465632Sphk	if (dev->si_name[0] == '\0')
48565632Sphk		error = ENOENT;
48665632Sphk	else
48765632Sphk		error = SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1);
48865632Sphk	freedev(dev);
48965632Sphk	return (error);
49065632Sphk}
49165632Sphk
49267905SphkSYSCTL_PROC(_kern, OID_AUTO, devname, CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_ANYBODY,
49365632Sphk	NULL, 0, sysctl_devname, "", "devname(3) handler");
49489118Smsmith
49589118Smsmith/*
49689118Smsmith * Set ready_for_devs; prior to this point, device creation is not allowed.
49789118Smsmith */
49889118Smsmithstatic void
49989118Smsmithdev_set_ready(void *junk)
50089118Smsmith{
50189118Smsmith	ready_for_devs = 1;
50289118Smsmith}
50389118Smsmith
50489118SmsmithSYSINIT(dev_ready, SI_SUB_DEVFS, SI_ORDER_FIRST, dev_set_ready, NULL);
505