kern_conf.c revision 74522
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 74522 2001-03-20 13:24:24Z 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++;
18064880Sphk		si->si_flags |= SI_STASHED;
18164880Sphk	}
18264880Sphk	LIST_INIT(&si->si_names);
18373942Smckusick	TAILQ_INIT(&si->si_snapshots);
18464880Sphk	return (si);
18564880Sphk}
18664880Sphk
18747680Sphkdev_t
18847028Sphkmakedev(int x, int y)
18947028Sphk{
19048936Sphk	struct specinfo *si;
19148936Sphk	udev_t	udev;
19248936Sphk	int hash;
19348936Sphk
19455414Sphk	if (x == umajor(NOUDEV) && y == uminor(NOUDEV))
19571342Sphk		panic("makedev of NOUDEV");
19648936Sphk	udev = (x << 8) | y;
19748936Sphk	hash = udev % DEVT_HASH;
19850549Sphk	LIST_FOREACH(si, &dev_hash[hash], si_hash) {
19948936Sphk		if (si->si_udev == udev)
20048936Sphk			return (si);
20148936Sphk	}
20264880Sphk	si = allocdev();
20348936Sphk	si->si_udev = udev;
20450549Sphk	LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash);
20548936Sphk        return (si);
20647028Sphk}
20747028Sphk
20850549Sphkvoid
20950549Sphkfreedev(dev_t dev)
21050549Sphk{
21164880Sphk	dev_t adev;
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;
21964880Sphk	while (!LIST_EMPTY(&dev->si_names)) {
22064880Sphk		adev = LIST_FIRST(&dev->si_names);
22164880Sphk		adev->si_drv1 = NULL;
22264880Sphk		freedev(adev);
22364880Sphk	}
22450549Sphk	LIST_REMOVE(dev, si_hash);
22550549Sphk	if (dev->si_flags & SI_STASHED) {
22650549Sphk		bzero(dev, sizeof(*dev));
22750549Sphk		LIST_INSERT_HEAD(&dev_free, dev, si_hash);
22850549Sphk	} else {
22950549Sphk		FREE(dev, M_DEVT);
23050549Sphk	}
23150549Sphk}
23250549Sphk
23347028Sphkudev_t
23447028Sphkdev2udev(dev_t x)
23547028Sphk{
23648936Sphk	if (x == NODEV)
23748936Sphk		return NOUDEV;
23848936Sphk	return (x->si_udev);
23947028Sphk}
24047028Sphk
24147028Sphkdev_t
24247028Sphkudev2dev(udev_t x, int b)
24347028Sphk{
24455414Sphk
24555414Sphk	if (x == NOUDEV)
24655414Sphk		return (NODEV);
24748864Sphk	switch (b) {
24848864Sphk		case 0:
24948864Sphk			return makedev(umajor(x), uminor(x));
25048864Sphk		case 1:
25168063Sphk			return (NODEV);
25248864Sphk		default:
25348864Sphk			Debugger("udev2dev(...,X)");
25448864Sphk			return NODEV;
25548864Sphk	}
25647028Sphk}
25747028Sphk
25847028Sphkint
25947028Sphkuminor(udev_t dev)
26047028Sphk{
26147028Sphk	return(dev & 0xffff00ff);
26247028Sphk}
26347028Sphk
26447028Sphkint
26547028Sphkumajor(udev_t dev)
26647028Sphk{
26747028Sphk	return((dev & 0xff00) >> 8);
26847028Sphk}
26947028Sphk
27047028Sphkudev_t
27148859Sphkmakeudev(int x, int y)
27247028Sphk{
27347028Sphk        return ((x << 8) | y);
27447028Sphk}
27547028Sphk
27649535Sphkdev_t
27749535Sphkmake_dev(struct cdevsw *devsw, int minor, uid_t uid, gid_t gid, int perms, char *fmt, ...)
27849535Sphk{
27949535Sphk	dev_t	dev;
28049535Sphk	va_list ap;
28149535Sphk	int i;
28249535Sphk
28371920Sbrian	KASSERT(umajor(makeudev(devsw->d_maj, minor)) == devsw->d_maj,
28471920Sbrian	    ("Invalid minor (%d) in make_dev", minor));
28571920Sbrian
28649535Sphk	dev = makedev(devsw->d_maj, minor);
28765747Sphk	if (dev->si_flags & SI_NAMED) {
28865747Sphk		printf( "WARNING: Driver mistake: repeat make_dev(\"%s\")\n",
28965747Sphk		    dev->si_name);
29065747Sphk		return (dev);
29165747Sphk	}
29249535Sphk	va_start(ap, fmt);
29356465Sbp	i = kvprintf(fmt, NULL, dev->si_name, 32, ap);
29449535Sphk	dev->si_name[i] = '\0';
29549535Sphk	va_end(ap);
29649535Sphk	dev->si_devsw = devsw;
29764880Sphk	dev->si_uid = uid;
29864880Sphk	dev->si_gid = gid;
29964880Sphk	dev->si_mode = perms;
30065747Sphk	dev->si_flags |= SI_NAMED;
30150092Sjulian
30250254Sphk	if (devfs_create_hook)
30364880Sphk		devfs_create_hook(dev);
30449535Sphk	return (dev);
30549535Sphk}
30649535Sphk
30764880Sphkdev_t
30864880Sphkmake_dev_alias(dev_t pdev, char *fmt, ...)
30964880Sphk{
31064880Sphk	dev_t	dev;
31164880Sphk	va_list ap;
31264880Sphk	int i;
31364880Sphk
31464880Sphk	dev = allocdev();
31564880Sphk	dev->si_flags |= SI_ALIAS;
31665747Sphk	dev->si_flags |= SI_NAMED;
31764880Sphk	dev->si_drv1 = pdev;
31864880Sphk	LIST_INSERT_HEAD(&pdev->si_names, dev, si_hash);
31964880Sphk
32064880Sphk	va_start(ap, fmt);
32164880Sphk	i = kvprintf(fmt, NULL, dev->si_name, 32, ap);
32264880Sphk	dev->si_name[i] = '\0';
32364880Sphk	va_end(ap);
32464880Sphk
32564880Sphk	if (devfs_create_hook)
32664880Sphk		devfs_create_hook(dev);
32764880Sphk	return (dev);
32864880Sphk}
32964880Sphk
33050549Sphkvoid
33153000Sphkdestroy_dev(dev_t dev)
33250549Sphk{
33365747Sphk
33465747Sphk	if (!(dev->si_flags & SI_NAMED)) {
33565747Sphk		printf( "WARNING: Driver mistake: destroy_dev on %d/%d\n",
33665747Sphk		    major(dev), minor(dev));
33765747Sphk		return;
33865747Sphk	}
33965747Sphk
34065374Sphk	if (devfs_destroy_hook)
34165374Sphk		devfs_destroy_hook(dev);
34250549Sphk	dev->si_drv1 = 0;
34350549Sphk	dev->si_drv2 = 0;
34450549Sphk	dev->si_devsw = 0;
34565747Sphk	dev->si_flags &= ~SI_NAMED;
34665747Sphk	dev->si_flags &= ~SI_ALIAS;
34750549Sphk	freedev(dev);
34850549Sphk}
34950549Sphk
35051225Sbdeconst char *
35149982Sbillfdevtoname(dev_t dev)
35249982Sbillf{
35350549Sphk	char *p;
35451225Sbde	int mynor;
35549982Sbillf
35650549Sphk	if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
35750549Sphk		p = dev->si_name;
35850549Sphk		if (devsw(dev))
35950549Sphk			sprintf(p, "#%s/", devsw(dev)->d_name);
36050549Sphk		else
36150549Sphk			sprintf(p, "#%d/", major(dev));
36250549Sphk		p += strlen(p);
36351225Sbde		mynor = minor(dev);
36451225Sbde		if (mynor < 0 || mynor > 255)
36551225Sbde			sprintf(p, "%#x", (u_int)mynor);
36651225Sbde		else
36751225Sbde			sprintf(p, "%d", mynor);
36850549Sphk	}
36949982Sbillf	return (dev->si_name);
37049982Sbillf}
37165374Sphk
37265374Sphkint
37365374Sphkdev_stdclone(char *name, char **namep, char *stem, int *unit)
37465374Sphk{
37565374Sphk	int u, i;
37665374Sphk
37765374Sphk	if (bcmp(stem, name, strlen(stem)) != 0)
37865374Sphk		return (0);
37965374Sphk	i = strlen(stem);
38065374Sphk	if (!isdigit(name[i]))
38165374Sphk		return (0);
38265374Sphk	u = 0;
38365374Sphk	while (isdigit(name[i])) {
38465374Sphk		u *= 10;
38565374Sphk		u += name[i++] - '0';
38665374Sphk	}
38765374Sphk	*unit = u;
38865374Sphk	if (namep)
38965374Sphk		*namep = &name[i];
39065374Sphk	if (name[i])
39165374Sphk		return (2);
39265374Sphk	return (1);
39365374Sphk}
39465632Sphk
39565632Sphk/*
39665632Sphk * Helper sysctl for devname(3).  We're given a {u}dev_t and return
39765632Sphk * the name, if any, registered by the device driver.
39865632Sphk */
39965632Sphkstatic int
40065632Sphksysctl_devname(SYSCTL_HANDLER_ARGS)
40165632Sphk{
40265632Sphk	int error;
40365632Sphk	udev_t ud;
40465632Sphk	dev_t dev;
40565632Sphk
40665632Sphk	error = SYSCTL_IN(req, &ud, sizeof (ud));
40765632Sphk	if (error)
40865632Sphk		return (error);
40971342Sphk	if (ud == NOUDEV)
41071342Sphk		return(EINVAL);
41165632Sphk	dev = makedev(umajor(ud), uminor(ud));
41265632Sphk	if (dev->si_name[0] == '\0')
41365632Sphk		error = ENOENT;
41465632Sphk	else
41565632Sphk		error = SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1);
41665632Sphk	freedev(dev);
41765632Sphk	return (error);
41865632Sphk}
41965632Sphk
42067905SphkSYSCTL_PROC(_kern, OID_AUTO, devname, CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_ANYBODY,
42165632Sphk	NULL, 0, sysctl_devname, "", "devname(3) handler");
42265632Sphk
423