kern_conf.c revision 116182
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 */ 2611126Sjulian 27116182Sobrien#include <sys/cdefs.h> 28116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/kern_conf.c 116182 2003-06-11 00:56:59Z obrien $"); 29116182Sobrien 3011126Sjulian#include <sys/param.h> 3148936Sphk#include <sys/kernel.h> 3283366Sjulian#include <sys/systm.h> 33111179Sphk#include <sys/bio.h> 3490737Sgreen#include <sys/lock.h> 3590737Sgreen#include <sys/mutex.h> 3650549Sphk#include <sys/sysctl.h> 3736735Sdfr#include <sys/module.h> 3848936Sphk#include <sys/malloc.h> 3911126Sjulian#include <sys/conf.h> 4012954Sjulian#include <sys/vnode.h> 4148936Sphk#include <sys/queue.h> 4265374Sphk#include <sys/ctype.h> 4349535Sphk#include <machine/stdarg.h> 4411126Sjulian 4569774Sphkstatic MALLOC_DEFINE(M_DEVT, "dev_t", "dev_t storage"); 4648936Sphk 47111622Sphk/* Built at compile time from sys/conf/majors */ 48111622Sphkextern unsigned char reserved_majors[256]; 49111622Sphk 5050522Sphk/* 5150522Sphk * This is the number of hash-buckets. Experiements with 'real-life' 5250522Sphk * udev_t's show that a prime halfway between two powers of two works 5350522Sphk * best. 5450522Sphk */ 5548936Sphk#define DEVT_HASH 83 5650522Sphk 5750522Sphk/* The number of dev_t's we can create before malloc(9) kick in. */ 5848936Sphk#define DEVT_STASH 50 5948936Sphk 60104043Sphkstatic struct cdev devt_stash[DEVT_STASH]; 6148936Sphk 62104043Sphkstatic LIST_HEAD(, cdev) dev_hash[DEVT_HASH]; 6348936Sphk 64104043Sphkstatic LIST_HEAD(, cdev) dev_free; 6550549Sphk 6689118Smsmithstatic int ready_for_devs; 6789118Smsmith 6850549Sphkstatic int free_devt; 6950549SphkSYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, ""); 7050549Sphk 71111179Sphk/* Define a dead_cdevsw for use when devices leave unexpectedly. */ 7285603Sphk 73111179Sphkstatic int 74111179Sphkenxio(void) 75111179Sphk{ 76111179Sphk return (ENXIO); 77111179Sphk} 78111179Sphk 79111179Sphk#define dead_open (d_open_t *)enxio 80111179Sphk#define dead_close (d_close_t *)enxio 81111179Sphk#define dead_read (d_read_t *)enxio 82111179Sphk#define dead_write (d_write_t *)enxio 83111179Sphk#define dead_ioctl (d_ioctl_t *)enxio 84111179Sphk#define dead_poll nopoll 85111179Sphk#define dead_mmap nommap 86111179Sphk 87111179Sphkstatic void 88111179Sphkdead_strategy(struct bio *bp) 89111179Sphk{ 90111179Sphk 91111179Sphk biofinish(bp, NULL, ENXIO); 92111179Sphk} 93111179Sphk 94111220Sphk#define dead_dump (dumper_t *)enxio 95111179Sphk 96111179Sphk#define dead_kqfilter (d_kqfilter_t *)enxio 97111179Sphk 98111179Sphkstatic struct cdevsw dead_cdevsw = { 99111815Sphk .d_open = dead_open, 100111815Sphk .d_close = dead_close, 101111815Sphk .d_read = dead_read, 102111815Sphk .d_write = dead_write, 103111815Sphk .d_ioctl = dead_ioctl, 104111815Sphk .d_poll = dead_poll, 105111815Sphk .d_mmap = dead_mmap, 106111815Sphk .d_strategy = dead_strategy, 107111815Sphk .d_name = "dead", 108111815Sphk .d_maj = 255, 109111815Sphk .d_dump = dead_dump, 110111815Sphk .d_kqfilter = dead_kqfilter 111111179Sphk}; 112111179Sphk 113111179Sphk 11447640Sphkstruct cdevsw * 11547640Sphkdevsw(dev_t dev) 11651927Sphk{ 11749679Sphk if (dev->si_devsw) 11849679Sphk return (dev->si_devsw); 119111179Sphk return (&dead_cdevsw); 12047640Sphk} 12147640Sphk 12247640Sphk/* 12347028Sphk * dev_t and u_dev_t primitives 12447028Sphk */ 12547028Sphk 12651927Sphkint 12747028Sphkmajor(dev_t x) 12847028Sphk{ 12948936Sphk if (x == NODEV) 13048936Sphk return NOUDEV; 13148936Sphk return((x->si_udev >> 8) & 0xff); 13247028Sphk} 13347028Sphk 13447028Sphkint 13547028Sphkminor(dev_t x) 13647028Sphk{ 13748936Sphk if (x == NODEV) 13848936Sphk return NOUDEV; 13948936Sphk return(x->si_udev & 0xffff00ff); 14047028Sphk} 14147028Sphk 14249826Sphkint 14366067Sphkdev2unit(dev_t x) 14449826Sphk{ 14549826Sphk int i; 14649826Sphk 14749826Sphk if (x == NODEV) 14849826Sphk return NOUDEV; 14949826Sphk i = minor(x); 15049826Sphk return ((i & 0xff) | (i >> 8)); 15149826Sphk} 15249826Sphk 15366067Sphkint 15466067Sphkunit2minor(int unit) 15566067Sphk{ 15666067Sphk 15774522Sphk KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit)); 15866067Sphk return ((unit & 0xff) | ((unit << 8) & ~0xffff)); 15966067Sphk} 16066067Sphk 16164880Sphkstatic dev_t 16264880Sphkallocdev(void) 16364880Sphk{ 16464880Sphk static int stashed; 165104043Sphk struct cdev *si; 16664880Sphk 167103101Sphk if (LIST_FIRST(&dev_free)) { 168103101Sphk si = LIST_FIRST(&dev_free); 169103101Sphk LIST_REMOVE(si, si_hash); 170103101Sphk } else if (stashed >= DEVT_STASH) { 171104043Sphk MALLOC(si, struct cdev *, sizeof(*si), M_DEVT, 172111146Sphk M_USE_RESERVE | M_ZERO | M_WAITOK); 17364880Sphk } else { 17464880Sphk si = devt_stash + stashed++; 17577215Sphk bzero(si, sizeof *si); 176103101Sphk si->si_flags |= SI_STASHED; 17764880Sphk } 178110317Sphk si->__si_namebuf[0] = '\0'; 179110317Sphk si->si_name = si->__si_namebuf; 18077215Sphk LIST_INIT(&si->si_children); 18173942Smckusick TAILQ_INIT(&si->si_snapshots); 18264880Sphk return (si); 18364880Sphk} 18464880Sphk 18547680Sphkdev_t 18647028Sphkmakedev(int x, int y) 18747028Sphk{ 188104043Sphk struct cdev *si; 18948936Sphk udev_t udev; 19048936Sphk int hash; 19148936Sphk 19255414Sphk if (x == umajor(NOUDEV) && y == uminor(NOUDEV)) 19371342Sphk panic("makedev of NOUDEV"); 19448936Sphk udev = (x << 8) | y; 19548936Sphk hash = udev % DEVT_HASH; 19650549Sphk LIST_FOREACH(si, &dev_hash[hash], si_hash) { 19748936Sphk if (si->si_udev == udev) 19848936Sphk return (si); 19948936Sphk } 20064880Sphk si = allocdev(); 20148936Sphk si->si_udev = udev; 20250549Sphk LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash); 20348936Sphk return (si); 20447028Sphk} 20547028Sphk 20650549Sphkvoid 20750549Sphkfreedev(dev_t dev) 20850549Sphk{ 20950549Sphk 21050549Sphk if (!free_devt) 21150549Sphk return; 21250549Sphk if (SLIST_FIRST(&dev->si_hlist)) 21350549Sphk return; 21451927Sphk if (dev->si_devsw || dev->si_drv1 || dev->si_drv2) 21550549Sphk return; 21650549Sphk LIST_REMOVE(dev, si_hash); 21750549Sphk if (dev->si_flags & SI_STASHED) { 21850549Sphk bzero(dev, sizeof(*dev)); 21977215Sphk dev->si_flags |= SI_STASHED; 22050549Sphk LIST_INSERT_HEAD(&dev_free, dev, si_hash); 22150549Sphk } else { 22250549Sphk FREE(dev, M_DEVT); 22350549Sphk } 22450549Sphk} 22550549Sphk 22647028Sphkudev_t 22747028Sphkdev2udev(dev_t x) 22847028Sphk{ 22948936Sphk if (x == NODEV) 23048936Sphk return NOUDEV; 23148936Sphk return (x->si_udev); 23247028Sphk} 23347028Sphk 23447028Sphkdev_t 23547028Sphkudev2dev(udev_t x, int b) 23647028Sphk{ 23755414Sphk 23855414Sphk if (x == NOUDEV) 23955414Sphk return (NODEV); 24048864Sphk switch (b) { 24148864Sphk case 0: 24248864Sphk return makedev(umajor(x), uminor(x)); 24348864Sphk case 1: 24468063Sphk return (NODEV); 24548864Sphk default: 24648864Sphk Debugger("udev2dev(...,X)"); 24748864Sphk return NODEV; 24848864Sphk } 24947028Sphk} 25047028Sphk 25147028Sphkint 25247028Sphkuminor(udev_t dev) 25347028Sphk{ 25447028Sphk return(dev & 0xffff00ff); 25547028Sphk} 25647028Sphk 25747028Sphkint 25847028Sphkumajor(udev_t dev) 25947028Sphk{ 26047028Sphk return((dev & 0xff00) >> 8); 26147028Sphk} 26247028Sphk 26347028Sphkudev_t 26448859Sphkmakeudev(int x, int y) 26547028Sphk{ 26647028Sphk return ((x << 8) | y); 26747028Sphk} 26847028Sphk 26949535Sphkdev_t 27081068Simpmake_dev(struct cdevsw *devsw, int minor, uid_t uid, gid_t gid, int perms, const char *fmt, ...) 27149535Sphk{ 27249535Sphk dev_t dev; 27349535Sphk va_list ap; 27449535Sphk int i; 27549535Sphk 276111622Sphk KASSERT((minor & ~0xffff00ff) == 0, 277111622Sphk ("Invalid minor (0x%x) in make_dev", minor)); 27871920Sbrian 279111760Sphk if (devsw->d_open == NULL) devsw->d_open = noopen; 280111760Sphk if (devsw->d_close == NULL) devsw->d_close = noclose; 281111760Sphk if (devsw->d_read == NULL) devsw->d_read = noread; 282111760Sphk if (devsw->d_write == NULL) devsw->d_write = nowrite; 283111760Sphk if (devsw->d_ioctl == NULL) devsw->d_ioctl = noioctl; 284111760Sphk if (devsw->d_poll == NULL) devsw->d_poll = nopoll; 285111760Sphk if (devsw->d_mmap == NULL) devsw->d_mmap = nommap; 286111760Sphk if (devsw->d_strategy == NULL) devsw->d_strategy = nostrategy; 287111760Sphk if (devsw->d_dump == NULL) devsw->d_dump = nodump; 288111760Sphk if (devsw->d_kqfilter == NULL) devsw->d_kqfilter = nokqfilter; 289111760Sphk 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 devsw->d_maj = i; 296111622Sphk reserved_majors[i] = i; 297111626Sphk } else { 298112035Sphk if (devsw->d_maj == 256) /* XXX: tty_cons.c is magic */ 299112035Sphk devsw->d_maj = 0; 300111626Sphk KASSERT(devsw->d_maj >= 0 && devsw->d_maj < 256, 301111626Sphk ("Invalid major (%d) in make_dev", devsw->d_maj)); 302111626Sphk if (reserved_majors[devsw->d_maj] != devsw->d_maj) { 303111626Sphk printf("WARNING: driver \"%s\" used %s %d\n", 304111626Sphk devsw->d_name, "unreserved major device number", 305111626Sphk devsw->d_maj); 306111626Sphk reserved_majors[devsw->d_maj] = devsw->d_maj; 307111626Sphk } 308111622Sphk } 309111622Sphk 31089118Smsmith if (!ready_for_devs) { 31189118Smsmith printf("WARNING: Driver mistake: make_dev(%s) called before SI_SUB_DRIVERS\n", 31289118Smsmith fmt); 31389118Smsmith /* XXX panic here once drivers are cleaned up */ 31489118Smsmith } 31589118Smsmith 31649535Sphk dev = makedev(devsw->d_maj, minor); 31765747Sphk if (dev->si_flags & SI_NAMED) { 31865747Sphk printf( "WARNING: Driver mistake: repeat make_dev(\"%s\")\n", 31965747Sphk dev->si_name); 32085603Sphk panic("don't do that"); 32165747Sphk } 32249535Sphk va_start(ap, fmt); 323110318Sphk i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap); 324110318Sphk if (i > (sizeof dev->__si_namebuf - 1)) { 325110318Sphk printf("WARNING: Device name truncated! (%s)", 326110318Sphk dev->__si_namebuf); 327110318Sphk } 32849535Sphk va_end(ap); 32949535Sphk dev->si_devsw = devsw; 33064880Sphk dev->si_uid = uid; 33164880Sphk dev->si_gid = gid; 33264880Sphk dev->si_mode = perms; 33365747Sphk dev->si_flags |= SI_NAMED; 33450092Sjulian 335111730Sphk devfs_create(dev); 33649535Sphk return (dev); 33749535Sphk} 33849535Sphk 33985076Sjlemonint 34085076Sjlemondev_named(dev_t pdev, const char *name) 34185076Sjlemon{ 34285076Sjlemon dev_t cdev; 34385076Sjlemon 34485076Sjlemon if (strcmp(devtoname(pdev), name) == 0) 34585076Sjlemon return (1); 34685076Sjlemon LIST_FOREACH(cdev, &pdev->si_children, si_siblings) 34785076Sjlemon if (strcmp(devtoname(cdev), name) == 0) 34885076Sjlemon return (1); 34985076Sjlemon return (0); 35085076Sjlemon} 35185076Sjlemon 35277215Sphkvoid 35377215Sphkdev_depends(dev_t pdev, dev_t cdev) 35477215Sphk{ 35577215Sphk 35677215Sphk cdev->si_parent = pdev; 35777215Sphk cdev->si_flags |= SI_CHILD; 35877215Sphk LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings); 35977215Sphk} 36077215Sphk 36164880Sphkdev_t 36281068Simpmake_dev_alias(dev_t pdev, const char *fmt, ...) 36364880Sphk{ 36464880Sphk dev_t dev; 36564880Sphk va_list ap; 36664880Sphk int i; 36764880Sphk 36864880Sphk dev = allocdev(); 36964880Sphk dev->si_flags |= SI_ALIAS; 37065747Sphk dev->si_flags |= SI_NAMED; 37177215Sphk dev_depends(pdev, dev); 37264880Sphk va_start(ap, fmt); 373110318Sphk i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap); 374110318Sphk if (i > (sizeof dev->__si_namebuf - 1)) { 375110318Sphk printf("WARNING: Device name truncated! (%s)", 376110318Sphk dev->__si_namebuf); 377110318Sphk } 37864880Sphk va_end(ap); 37964880Sphk 380111730Sphk devfs_create(dev); 38164880Sphk return (dev); 38264880Sphk} 38364880Sphk 38450549Sphkvoid 38590736Sgreenrevoke_and_destroy_dev(dev_t dev) 38690736Sgreen{ 38790736Sgreen struct vnode *vp; 38890736Sgreen 38990736Sgreen GIANT_REQUIRED; 39090736Sgreen 39190736Sgreen vp = SLIST_FIRST(&dev->si_hlist); 39290736Sgreen if (vp != NULL) 39390736Sgreen VOP_REVOKE(vp, REVOKEALL); 39490736Sgreen destroy_dev(dev); 39590736Sgreen} 39690736Sgreen 39790736Sgreenvoid 39853000Sphkdestroy_dev(dev_t dev) 39950549Sphk{ 40065747Sphk 40165747Sphk if (!(dev->si_flags & SI_NAMED)) { 40265747Sphk printf( "WARNING: Driver mistake: destroy_dev on %d/%d\n", 40365747Sphk major(dev), minor(dev)); 40485603Sphk panic("don't do that"); 40565747Sphk } 40665747Sphk 407111730Sphk devfs_destroy(dev); 40877215Sphk if (dev->si_flags & SI_CHILD) { 40977215Sphk LIST_REMOVE(dev, si_siblings); 41077215Sphk dev->si_flags &= ~SI_CHILD; 41177215Sphk } 41277215Sphk while (!LIST_EMPTY(&dev->si_children)) 41377215Sphk destroy_dev(LIST_FIRST(&dev->si_children)); 41450549Sphk dev->si_drv1 = 0; 41550549Sphk dev->si_drv2 = 0; 41650549Sphk dev->si_devsw = 0; 41795446Sbde bzero(&dev->__si_u, sizeof(dev->__si_u)); 41865747Sphk dev->si_flags &= ~SI_NAMED; 41965747Sphk dev->si_flags &= ~SI_ALIAS; 42050549Sphk freedev(dev); 42150549Sphk} 42250549Sphk 42351225Sbdeconst char * 42449982Sbillfdevtoname(dev_t dev) 42549982Sbillf{ 42650549Sphk char *p; 42751225Sbde int mynor; 42849982Sbillf 42950549Sphk if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') { 43050549Sphk p = dev->si_name; 43150549Sphk if (devsw(dev)) 43250549Sphk sprintf(p, "#%s/", devsw(dev)->d_name); 43350549Sphk else 43450549Sphk sprintf(p, "#%d/", major(dev)); 43550549Sphk p += strlen(p); 43651225Sbde mynor = minor(dev); 43751225Sbde if (mynor < 0 || mynor > 255) 43851225Sbde sprintf(p, "%#x", (u_int)mynor); 43951225Sbde else 44051225Sbde sprintf(p, "%d", mynor); 44150549Sphk } 44249982Sbillf return (dev->si_name); 44349982Sbillf} 44465374Sphk 44565374Sphkint 44691998Sphkdev_stdclone(char *name, char **namep, const char *stem, int *unit) 44765374Sphk{ 44865374Sphk int u, i; 44965374Sphk 45075519Sbrian i = strlen(stem); 45175519Sbrian if (bcmp(stem, name, i) != 0) 45265374Sphk return (0); 45365374Sphk if (!isdigit(name[i])) 45465374Sphk return (0); 45565374Sphk u = 0; 45686461Sphk if (name[i] == '0' && isdigit(name[i+1])) 45786461Sphk return (0); 45865374Sphk while (isdigit(name[i])) { 45965374Sphk u *= 10; 46065374Sphk u += name[i++] - '0'; 46165374Sphk } 462104523Sgreen if (u > 0xffffff) 463104523Sgreen return (0); 46465374Sphk *unit = u; 46565374Sphk if (namep) 46665374Sphk *namep = &name[i]; 46765374Sphk if (name[i]) 46865374Sphk return (2); 46965374Sphk return (1); 47065374Sphk} 47165632Sphk 47265632Sphk/* 47365632Sphk * Helper sysctl for devname(3). We're given a {u}dev_t and return 47465632Sphk * the name, if any, registered by the device driver. 47565632Sphk */ 47665632Sphkstatic int 47765632Sphksysctl_devname(SYSCTL_HANDLER_ARGS) 47865632Sphk{ 47965632Sphk int error; 48065632Sphk udev_t ud; 48165632Sphk dev_t dev; 48265632Sphk 48365632Sphk error = SYSCTL_IN(req, &ud, sizeof (ud)); 48465632Sphk if (error) 48565632Sphk return (error); 48671342Sphk if (ud == NOUDEV) 48771342Sphk return(EINVAL); 48865632Sphk dev = makedev(umajor(ud), uminor(ud)); 48965632Sphk if (dev->si_name[0] == '\0') 49065632Sphk error = ENOENT; 49165632Sphk else 49265632Sphk error = SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1); 49365632Sphk freedev(dev); 49465632Sphk return (error); 49565632Sphk} 49665632Sphk 49767905SphkSYSCTL_PROC(_kern, OID_AUTO, devname, CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_ANYBODY, 49865632Sphk NULL, 0, sysctl_devname, "", "devname(3) handler"); 49989118Smsmith 50089118Smsmith/* 50189118Smsmith * Set ready_for_devs; prior to this point, device creation is not allowed. 50289118Smsmith */ 50389118Smsmithstatic void 50489118Smsmithdev_set_ready(void *junk) 50589118Smsmith{ 50689118Smsmith ready_for_devs = 1; 50789118Smsmith} 50889118Smsmith 50989118SmsmithSYSINIT(dev_ready, SI_SUB_DEVFS, SI_ORDER_FIRST, dev_set_ready, NULL); 510