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