kern_conf.c revision 77215
111126Sjulian/*-
211126Sjulian * Parts Copyright (c) 1995 Terrence R. Lambert
311126Sjulian * Copyright (c) 1995 Julian R. Elischer
411126Sjulian * All rights reserved.
511126Sjulian *
611126Sjulian * Redistribution and use in source and binary forms, with or without
711126Sjulian * modification, are permitted provided that the following conditions
811126Sjulian * are met:
911126Sjulian * 1. Redistributions of source code must retain the above copyright
1011126Sjulian *    notice, this list of conditions and the following disclaimer.
1111126Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1211126Sjulian *    notice, this list of conditions and the following disclaimer in the
1311126Sjulian *    documentation and/or other materials provided with the distribution.
1411126Sjulian * 3. All advertising materials mentioning features or use of this software
1511126Sjulian *    must display the following acknowledgement:
1611126Sjulian *      This product includes software developed by Terrence R. Lambert.
1711126Sjulian * 4. The name Terrence R. Lambert may not be used to endorse or promote
1811126Sjulian *    products derived from this software without specific prior written
1911126Sjulian *    permission.
2011126Sjulian *
2111126Sjulian * THIS SOFTWARE IS PROVIDED BY Julian R. Elischer ``AS IS'' AND ANY
2211126Sjulian * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2311126Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2411126Sjulian * ARE DISCLAIMED.  IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
2511126Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2611126Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2711126Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2811126Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2911126Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3011126Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3111126Sjulian * SUCH DAMAGE.
3211126Sjulian *
3350477Speter * $FreeBSD: head/sys/kern/kern_conf.c 77215 2001-05-26 08:27:58Z phk $
3411126Sjulian */
3511126Sjulian
3611126Sjulian#include <sys/param.h>
3748936Sphk#include <sys/kernel.h>
3850549Sphk#include <sys/sysctl.h>
3911127Sjulian#include <sys/systm.h>
4036735Sdfr#include <sys/module.h>
4148936Sphk#include <sys/malloc.h>
4211126Sjulian#include <sys/conf.h>
4312954Sjulian#include <sys/vnode.h>
4448936Sphk#include <sys/queue.h>
4565374Sphk#include <sys/ctype.h>
4649535Sphk#include <machine/stdarg.h>
4711126Sjulian
4847640Sphk#define cdevsw_ALLOCSTART	(NUMCDEVSW/2)
4912954Sjulian
5047640Sphkstruct cdevsw 	*cdevsw[NUMCDEVSW];
5112954Sjulian
5269774Sphkstatic MALLOC_DEFINE(M_DEVT, "dev_t", "dev_t storage");
5348936Sphk
5450522Sphk/*
5550522Sphk * This is the number of hash-buckets.  Experiements with 'real-life'
5650522Sphk * udev_t's show that a prime halfway between two powers of two works
5750522Sphk * best.
5850522Sphk */
5948936Sphk#define DEVT_HASH 83
6050522Sphk
6150522Sphk/* The number of dev_t's we can create before malloc(9) kick in.  */
6248936Sphk#define DEVT_STASH 50
6348936Sphk
6448936Sphkstatic struct specinfo devt_stash[DEVT_STASH];
6548936Sphk
6660938Sjakestatic LIST_HEAD(, specinfo) dev_hash[DEVT_HASH];
6748936Sphk
6860938Sjakestatic LIST_HEAD(, specinfo) dev_free;
6950549Sphk
7050254Sphkdevfs_create_t *devfs_create_hook;
7165374Sphkdevfs_destroy_t *devfs_destroy_hook;
7265374Sphkint devfs_present;
7350254Sphk
7450549Sphkstatic int free_devt;
7550549SphkSYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, "");
7650549Sphk
7747640Sphkstruct cdevsw *
7847640Sphkdevsw(dev_t dev)
7951927Sphk{
8049679Sphk	if (dev->si_devsw)
8149679Sphk		return (dev->si_devsw);
8247640Sphk        return(cdevsw[major(dev)]);
8347640Sphk}
8447640Sphk
8547640Sphk/*
8647640Sphk *  Add a cdevsw entry
8747640Sphk */
8847640Sphk
8937389Sjulianint
9047640Sphkcdevsw_add(struct cdevsw *newentry)
9117264Sphk{
9217264Sphk
9347640Sphk	if (newentry->d_maj < 0 || newentry->d_maj >= NUMCDEVSW) {
9447640Sphk		printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n",
9547640Sphk		    newentry->d_name, newentry->d_maj);
9651927Sphk		return (EINVAL);
9737389Sjulian	}
9817264Sphk
9948510Sphk	if (cdevsw[newentry->d_maj]) {
10048510Sphk		printf("WARNING: \"%s\" is usurping \"%s\"'s cdevsw[]\n",
10148510Sphk		    newentry->d_name, cdevsw[newentry->d_maj]->d_name);
10248510Sphk	}
10351927Sphk
10447640Sphk	cdevsw[newentry->d_maj] = newentry;
10537389Sjulian
10651927Sphk	return (0);
10751927Sphk}
10847640Sphk
10948211Sgrog/*
11048211Sgrog *  Remove a cdevsw entry
11148211Sgrog */
11248211Sgrog
11336735Sdfrint
11448211Sgrogcdevsw_remove(struct cdevsw *oldentry)
11548211Sgrog{
11648211Sgrog	if (oldentry->d_maj < 0 || oldentry->d_maj >= NUMCDEVSW) {
11748211Sgrog		printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n",
11848211Sgrog		    oldentry->d_name, oldentry->d_maj);
11948211Sgrog		return EINVAL;
12048211Sgrog	}
12148211Sgrog
12248211Sgrog	cdevsw[oldentry->d_maj] = NULL;
12348211Sgrog
12448211Sgrog	return 0;
12551927Sphk}
12648211Sgrog
12747028Sphk/*
12847028Sphk * dev_t and u_dev_t primitives
12947028Sphk */
13047028Sphk
13151927Sphkint
13247028Sphkmajor(dev_t x)
13347028Sphk{
13448936Sphk	if (x == NODEV)
13548936Sphk		return NOUDEV;
13648936Sphk	return((x->si_udev >> 8) & 0xff);
13747028Sphk}
13847028Sphk
13947028Sphkint
14047028Sphkminor(dev_t x)
14147028Sphk{
14248936Sphk	if (x == NODEV)
14348936Sphk		return NOUDEV;
14448936Sphk	return(x->si_udev & 0xffff00ff);
14547028Sphk}
14647028Sphk
14749826Sphkint
14866067Sphkdev2unit(dev_t x)
14949826Sphk{
15049826Sphk	int i;
15149826Sphk
15249826Sphk	if (x == NODEV)
15349826Sphk		return NOUDEV;
15449826Sphk	i = minor(x);
15549826Sphk	return ((i & 0xff) | (i >> 8));
15649826Sphk}
15749826Sphk
15866067Sphkint
15966067Sphkunit2minor(int unit)
16066067Sphk{
16166067Sphk
16274522Sphk	KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit));
16366067Sphk	return ((unit & 0xff) | ((unit << 8) & ~0xffff));
16466067Sphk}
16566067Sphk
16664880Sphkstatic dev_t
16764880Sphkallocdev(void)
16864880Sphk{
16964880Sphk	static int stashed;
17064880Sphk	struct specinfo *si;
17164880Sphk
17264880Sphk	if (stashed >= DEVT_STASH) {
17364880Sphk		MALLOC(si, struct specinfo *, sizeof(*si), M_DEVT,
17469781Sdwmalone		    M_USE_RESERVE | M_ZERO);
17564880Sphk	} else if (LIST_FIRST(&dev_free)) {
17664880Sphk		si = LIST_FIRST(&dev_free);
17764880Sphk		LIST_REMOVE(si, si_hash);
17864880Sphk	} else {
17964880Sphk		si = devt_stash + stashed++;
18077215Sphk		bzero(si, sizeof *si);
18177215Sphk	si->si_flags |= SI_STASHED;
18264880Sphk	}
18377215Sphk	LIST_INIT(&si->si_children);
18473942Smckusick	TAILQ_INIT(&si->si_snapshots);
18564880Sphk	return (si);
18664880Sphk}
18764880Sphk
18847680Sphkdev_t
18947028Sphkmakedev(int x, int y)
19047028Sphk{
19148936Sphk	struct specinfo *si;
19248936Sphk	udev_t	udev;
19348936Sphk	int hash;
19448936Sphk
19555414Sphk	if (x == umajor(NOUDEV) && y == uminor(NOUDEV))
19671342Sphk		panic("makedev of NOUDEV");
19748936Sphk	udev = (x << 8) | y;
19848936Sphk	hash = udev % DEVT_HASH;
19950549Sphk	LIST_FOREACH(si, &dev_hash[hash], si_hash) {
20048936Sphk		if (si->si_udev == udev)
20148936Sphk			return (si);
20248936Sphk	}
20364880Sphk	si = allocdev();
20448936Sphk	si->si_udev = udev;
20550549Sphk	LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash);
20648936Sphk        return (si);
20747028Sphk}
20847028Sphk
20950549Sphkvoid
21050549Sphkfreedev(dev_t dev)
21150549Sphk{
21250549Sphk
21350549Sphk	if (!free_devt)
21450549Sphk		return;
21550549Sphk	if (SLIST_FIRST(&dev->si_hlist))
21650549Sphk		return;
21751927Sphk	if (dev->si_devsw || dev->si_drv1 || dev->si_drv2)
21850549Sphk		return;
21950549Sphk	LIST_REMOVE(dev, si_hash);
22050549Sphk	if (dev->si_flags & SI_STASHED) {
22150549Sphk		bzero(dev, sizeof(*dev));
22277215Sphk		dev->si_flags |= SI_STASHED;
22350549Sphk		LIST_INSERT_HEAD(&dev_free, dev, si_hash);
22450549Sphk	} else {
22550549Sphk		FREE(dev, M_DEVT);
22650549Sphk	}
22750549Sphk}
22850549Sphk
22947028Sphkudev_t
23047028Sphkdev2udev(dev_t x)
23147028Sphk{
23248936Sphk	if (x == NODEV)
23348936Sphk		return NOUDEV;
23448936Sphk	return (x->si_udev);
23547028Sphk}
23647028Sphk
23747028Sphkdev_t
23847028Sphkudev2dev(udev_t x, int b)
23947028Sphk{
24055414Sphk
24155414Sphk	if (x == NOUDEV)
24255414Sphk		return (NODEV);
24348864Sphk	switch (b) {
24448864Sphk		case 0:
24548864Sphk			return makedev(umajor(x), uminor(x));
24648864Sphk		case 1:
24768063Sphk			return (NODEV);
24848864Sphk		default:
24948864Sphk			Debugger("udev2dev(...,X)");
25048864Sphk			return NODEV;
25148864Sphk	}
25247028Sphk}
25347028Sphk
25447028Sphkint
25547028Sphkuminor(udev_t dev)
25647028Sphk{
25747028Sphk	return(dev & 0xffff00ff);
25847028Sphk}
25947028Sphk
26047028Sphkint
26147028Sphkumajor(udev_t dev)
26247028Sphk{
26347028Sphk	return((dev & 0xff00) >> 8);
26447028Sphk}
26547028Sphk
26647028Sphkudev_t
26748859Sphkmakeudev(int x, int y)
26847028Sphk{
26947028Sphk        return ((x << 8) | y);
27047028Sphk}
27147028Sphk
27249535Sphkdev_t
27349535Sphkmake_dev(struct cdevsw *devsw, int minor, uid_t uid, gid_t gid, int perms, char *fmt, ...)
27449535Sphk{
27549535Sphk	dev_t	dev;
27649535Sphk	va_list ap;
27749535Sphk	int i;
27849535Sphk
27971920Sbrian	KASSERT(umajor(makeudev(devsw->d_maj, minor)) == devsw->d_maj,
28071920Sbrian	    ("Invalid minor (%d) in make_dev", minor));
28171920Sbrian
28249535Sphk	dev = makedev(devsw->d_maj, minor);
28365747Sphk	if (dev->si_flags & SI_NAMED) {
28465747Sphk		printf( "WARNING: Driver mistake: repeat make_dev(\"%s\")\n",
28565747Sphk		    dev->si_name);
28665747Sphk		return (dev);
28765747Sphk	}
28849535Sphk	va_start(ap, fmt);
28956465Sbp	i = kvprintf(fmt, NULL, dev->si_name, 32, ap);
29049535Sphk	dev->si_name[i] = '\0';
29149535Sphk	va_end(ap);
29249535Sphk	dev->si_devsw = devsw;
29364880Sphk	dev->si_uid = uid;
29464880Sphk	dev->si_gid = gid;
29564880Sphk	dev->si_mode = perms;
29665747Sphk	dev->si_flags |= SI_NAMED;
29750092Sjulian
29850254Sphk	if (devfs_create_hook)
29964880Sphk		devfs_create_hook(dev);
30049535Sphk	return (dev);
30149535Sphk}
30249535Sphk
30377215Sphkvoid
30477215Sphkdev_depends(dev_t pdev, dev_t cdev)
30577215Sphk{
30677215Sphk
30777215Sphk	cdev->si_parent = pdev;
30877215Sphk	cdev->si_flags |= SI_CHILD;
30977215Sphk	LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings);
31077215Sphk}
31177215Sphk
31264880Sphkdev_t
31364880Sphkmake_dev_alias(dev_t pdev, char *fmt, ...)
31464880Sphk{
31564880Sphk	dev_t	dev;
31664880Sphk	va_list ap;
31764880Sphk	int i;
31864880Sphk
31964880Sphk	dev = allocdev();
32064880Sphk	dev->si_flags |= SI_ALIAS;
32165747Sphk	dev->si_flags |= SI_NAMED;
32277215Sphk	dev_depends(pdev, dev);
32364880Sphk	va_start(ap, fmt);
32464880Sphk	i = kvprintf(fmt, NULL, dev->si_name, 32, ap);
32564880Sphk	dev->si_name[i] = '\0';
32664880Sphk	va_end(ap);
32764880Sphk
32864880Sphk	if (devfs_create_hook)
32964880Sphk		devfs_create_hook(dev);
33064880Sphk	return (dev);
33164880Sphk}
33264880Sphk
33350549Sphkvoid
33453000Sphkdestroy_dev(dev_t dev)
33550549Sphk{
33665747Sphk
33765747Sphk	if (!(dev->si_flags & SI_NAMED)) {
33865747Sphk		printf( "WARNING: Driver mistake: destroy_dev on %d/%d\n",
33965747Sphk		    major(dev), minor(dev));
34065747Sphk		return;
34165747Sphk	}
34265747Sphk
34365374Sphk	if (devfs_destroy_hook)
34465374Sphk		devfs_destroy_hook(dev);
34577215Sphk	if (dev->si_flags & SI_CHILD) {
34677215Sphk		LIST_REMOVE(dev, si_siblings);
34777215Sphk		dev->si_flags &= ~SI_CHILD;
34877215Sphk	}
34977215Sphk	while (!LIST_EMPTY(&dev->si_children))
35077215Sphk		destroy_dev(LIST_FIRST(&dev->si_children));
35150549Sphk	dev->si_drv1 = 0;
35250549Sphk	dev->si_drv2 = 0;
35350549Sphk	dev->si_devsw = 0;
35465747Sphk	dev->si_flags &= ~SI_NAMED;
35565747Sphk	dev->si_flags &= ~SI_ALIAS;
35650549Sphk	freedev(dev);
35750549Sphk}
35850549Sphk
35951225Sbdeconst char *
36049982Sbillfdevtoname(dev_t dev)
36149982Sbillf{
36250549Sphk	char *p;
36351225Sbde	int mynor;
36449982Sbillf
36550549Sphk	if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
36650549Sphk		p = dev->si_name;
36750549Sphk		if (devsw(dev))
36850549Sphk			sprintf(p, "#%s/", devsw(dev)->d_name);
36950549Sphk		else
37050549Sphk			sprintf(p, "#%d/", major(dev));
37150549Sphk		p += strlen(p);
37251225Sbde		mynor = minor(dev);
37351225Sbde		if (mynor < 0 || mynor > 255)
37451225Sbde			sprintf(p, "%#x", (u_int)mynor);
37551225Sbde		else
37651225Sbde			sprintf(p, "%d", mynor);
37750549Sphk	}
37849982Sbillf	return (dev->si_name);
37949982Sbillf}
38065374Sphk
38165374Sphkint
38265374Sphkdev_stdclone(char *name, char **namep, char *stem, int *unit)
38365374Sphk{
38465374Sphk	int u, i;
38565374Sphk
38675519Sbrian	i = strlen(stem);
38775519Sbrian	if (bcmp(stem, name, i) != 0)
38865374Sphk		return (0);
38965374Sphk	if (!isdigit(name[i]))
39065374Sphk		return (0);
39165374Sphk	u = 0;
39265374Sphk	while (isdigit(name[i])) {
39365374Sphk		u *= 10;
39465374Sphk		u += name[i++] - '0';
39565374Sphk	}
39665374Sphk	*unit = u;
39765374Sphk	if (namep)
39865374Sphk		*namep = &name[i];
39965374Sphk	if (name[i])
40065374Sphk		return (2);
40165374Sphk	return (1);
40265374Sphk}
40365632Sphk
40465632Sphk/*
40565632Sphk * Helper sysctl for devname(3).  We're given a {u}dev_t and return
40665632Sphk * the name, if any, registered by the device driver.
40765632Sphk */
40865632Sphkstatic int
40965632Sphksysctl_devname(SYSCTL_HANDLER_ARGS)
41065632Sphk{
41165632Sphk	int error;
41265632Sphk	udev_t ud;
41365632Sphk	dev_t dev;
41465632Sphk
41565632Sphk	error = SYSCTL_IN(req, &ud, sizeof (ud));
41665632Sphk	if (error)
41765632Sphk		return (error);
41871342Sphk	if (ud == NOUDEV)
41971342Sphk		return(EINVAL);
42065632Sphk	dev = makedev(umajor(ud), uminor(ud));
42165632Sphk	if (dev->si_name[0] == '\0')
42265632Sphk		error = ENOENT;
42365632Sphk	else
42465632Sphk		error = SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1);
42565632Sphk	freedev(dev);
42665632Sphk	return (error);
42765632Sphk}
42865632Sphk
42967905SphkSYSCTL_PROC(_kern, OID_AUTO, devname, CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_ANYBODY,
43065632Sphk	NULL, 0, sysctl_devname, "", "devname(3) handler");
43165632Sphk
432