1 /*- 2 * Parts Copyright (c) 1995 Terrence R. Lambert 3 * Copyright (c) 1995 Julian R. Elischer 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Terrence R. Lambert. 17 * 4. The name Terrence R. Lambert may not be used to endorse or promote 18 * products derived from this software without specific prior written 19 * permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY Julian R. Elischer ``AS IS'' AND ANY 22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * $FreeBSD: src/sys/kern/kern_conf.c,v 1.73.2.3 2003/03/10 02:18:25 imp Exp $ 34 * $DragonFly: src/sys/kern/kern_conf.c,v 1.10 2004/09/15 03:21:03 dillon Exp $ 35 */ 36 37 #include <sys/param.h> 38 #include <sys/kernel.h> 39 #include <sys/sysctl.h> 40 #include <sys/systm.h> 41 #include <sys/module.h> 42 #include <sys/malloc.h> 43 #include <sys/conf.h> 44 #include <sys/vnode.h> 45 #include <sys/queue.h> 46 #include <sys/device.h> 47 #include <machine/stdarg.h> 48 49 #define cdevsw_ALLOCSTART (NUMCDEVSW/2) 50 51 MALLOC_DEFINE(M_DEVT, "dev_t", "dev_t storage"); 52 53 /* 54 * This is the number of hash-buckets. Experiements with 'real-life' 55 * udev_t's show that a prime halfway between two powers of two works 56 * best. 57 */ 58 #define DEVT_HASH 83 59 60 /* The number of dev_t's we can create before malloc(9) kick in. */ 61 #define DEVT_STASH 50 62 63 static struct specinfo devt_stash[DEVT_STASH]; 64 static LIST_HEAD(, specinfo) dev_hash[DEVT_HASH]; 65 static LIST_HEAD(, specinfo) dev_free_list; 66 67 static int free_devt; 68 SYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, ""); 69 int dev_ref_debug = 0; 70 SYSCTL_INT(_debug, OID_AUTO, dev_refs, CTLFLAG_RW, &dev_ref_debug, 0, ""); 71 72 /* 73 * dev_t and u_dev_t primitives. Note that the major number is always 74 * extracted from si_udev, not from si_devsw, because si_devsw is replaced 75 * when a device is destroyed. 76 */ 77 int 78 major(dev_t x) 79 { 80 if (x == NODEV) 81 return NOUDEV; 82 return((x->si_udev >> 8) & 0xff); 83 } 84 85 int 86 minor(dev_t x) 87 { 88 if (x == NODEV) 89 return NOUDEV; 90 return(x->si_udev & 0xffff00ff); 91 } 92 93 int 94 lminor(dev_t x) 95 { 96 int i; 97 98 if (x == NODEV) 99 return NOUDEV; 100 i = minor(x); 101 return ((i & 0xff) | (i >> 8)); 102 } 103 104 /* 105 * This is a bit complex because devices are always created relative to 106 * a particular cdevsw, including 'hidden' cdevsw's (such as the raw device 107 * backing a disk subsystem overlay), so we have to compare both the 108 * devsw and udev fields to locate the correct device. 109 * 110 * The device is created if it does not already exist. If SI_ADHOC is not 111 * set the device will be referenced (once) and SI_ADHOC will be set. 112 * The caller must explicitly add additional references to the device if 113 * the caller wishes to track additional references. 114 */ 115 static 116 dev_t 117 hashdev(struct cdevsw *devsw, int x, int y) 118 { 119 struct specinfo *si; 120 udev_t udev; 121 int hash; 122 static int stashed; 123 124 udev = makeudev(x, y); 125 hash = udev % DEVT_HASH; 126 LIST_FOREACH(si, &dev_hash[hash], si_hash) { 127 if (si->si_devsw == devsw && si->si_udev == udev) 128 return (si); 129 } 130 if (stashed >= DEVT_STASH) { 131 MALLOC(si, struct specinfo *, sizeof(*si), M_DEVT, 132 M_WAITOK|M_USE_RESERVE|M_ZERO); 133 } else if (LIST_FIRST(&dev_free_list)) { 134 si = LIST_FIRST(&dev_free_list); 135 LIST_REMOVE(si, si_hash); 136 } else { 137 si = devt_stash + stashed++; 138 si->si_flags |= SI_STASHED; 139 } 140 si->si_devsw = devsw; 141 si->si_flags |= SI_HASHED | SI_ADHOC; 142 si->si_udev = udev; 143 si->si_refs = 1; 144 LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash); 145 si->si_port = devsw->d_port; 146 devsw->d_clone(si); 147 if (devsw != &dead_cdevsw) 148 ++devsw->d_refs; 149 if (dev_ref_debug) { 150 printf("create dev %p %s(minor=%08x) refs=%d\n", 151 si, devtoname(si), uminor(si->si_udev), 152 si->si_refs); 153 } 154 return (si); 155 } 156 157 /* 158 * Convert a device pointer to a device number 159 */ 160 udev_t 161 dev2udev(dev_t x) 162 { 163 if (x == NODEV) 164 return NOUDEV; 165 return (x->si_udev); 166 } 167 168 /* 169 * Convert a device number to a device pointer. The device is referenced 170 * ad-hoc, meaning that the caller should call reference_dev() if it wishes 171 * to keep ahold of the returned structure long term. 172 * 173 * The returned device is associated with the currently installed cdevsw 174 * for the requested major number. NODEV is returned if the major number 175 * has not been registered. 176 */ 177 dev_t 178 udev2dev(udev_t x, int b) 179 { 180 dev_t dev; 181 struct cdevsw *devsw; 182 183 if (x == NOUDEV || b != 0) 184 return(NODEV); 185 devsw = cdevsw_get(umajor(x), uminor(x)); 186 if (devsw == NULL) 187 return(NODEV); 188 dev = hashdev(devsw, umajor(x), uminor(x)); 189 return(dev); 190 } 191 192 int 193 dev_is_good(dev_t dev) 194 { 195 if (dev != NODEV && dev->si_devsw != &dead_cdevsw) 196 return(1); 197 return(0); 198 } 199 200 /* 201 * Various user device number extraction and conversion routines 202 */ 203 int 204 uminor(udev_t dev) 205 { 206 return(dev & 0xffff00ff); 207 } 208 209 int 210 umajor(udev_t dev) 211 { 212 return((dev & 0xff00) >> 8); 213 } 214 215 udev_t 216 makeudev(int x, int y) 217 { 218 return ((x << 8) | y); 219 } 220 221 /* 222 * Create an internal or external device. 223 * 224 * Device majors can be overloaded and used directly by the kernel without 225 * conflict, but userland will only see the particular device major that 226 * has been installed with cdevsw_add(). 227 * 228 * This routine creates and returns an unreferenced ad-hoc entry for the 229 * device which will remain intact until the device is destroyed. If the 230 * caller intends to store the device pointer it must call reference_dev() 231 * to retain a real reference to the device. 232 * 233 * If an entry already exists, this function will set (or override) 234 * its cred requirements and name (XXX DEVFS interface). 235 */ 236 dev_t 237 make_dev(struct cdevsw *devsw, int minor, uid_t uid, gid_t gid, 238 int perms, const char *fmt, ...) 239 { 240 dev_t dev; 241 __va_list ap; 242 int i; 243 244 /* 245 * compile the cdevsw and install the device 246 */ 247 compile_devsw(devsw); 248 dev = hashdev(devsw, devsw->d_maj, minor); 249 250 /* 251 * Set additional fields (XXX DEVFS interface goes here) 252 */ 253 __va_start(ap, fmt); 254 i = kvprintf(fmt, NULL, dev->si_name, 32, ap); 255 dev->si_name[i] = '\0'; 256 __va_end(ap); 257 258 return (dev); 259 } 260 261 /* 262 * This function is similar to make_dev() but no cred information or name 263 * need be specified. 264 */ 265 dev_t 266 make_adhoc_dev(struct cdevsw *devsw, int minor) 267 { 268 dev_t dev; 269 270 dev = hashdev(devsw, devsw->d_maj, minor); 271 return(dev); 272 } 273 274 /* 275 * This function is similar to make_dev() except the new device is created 276 * using an old device as a template. 277 */ 278 dev_t 279 make_sub_dev(dev_t odev, int minor) 280 { 281 dev_t dev; 282 283 dev = hashdev(odev->si_devsw, umajor(odev->si_udev), minor); 284 285 /* 286 * Copy cred requirements and name info XXX DEVFS. 287 */ 288 if (dev->si_name[0] == 0 && odev->si_name[0]) 289 bcopy(odev->si_name, dev->si_name, sizeof(dev->si_name)); 290 return (dev); 291 } 292 293 /* 294 * destroy_dev() removes the adhoc association for a device and revectors 295 * its devsw to &dead_cdevsw. 296 * 297 * This routine releases the reference count associated with the ADHOC 298 * entry, plus releases the reference count held by the caller. What this 299 * means is that you should not call destroy_dev(make_dev(...)), because 300 * make_dev() does not bump the reference count (beyond what it needs to 301 * create the ad-hoc association). Any procedure that intends to destroy 302 * a device must have its own reference to it first. 303 */ 304 void 305 destroy_dev(dev_t dev) 306 { 307 int hash; 308 309 if (dev == NODEV) 310 return; 311 if ((dev->si_flags & SI_ADHOC) == 0) { 312 release_dev(dev); 313 return; 314 } 315 if (dev_ref_debug) { 316 printf("destroy dev %p %s(minor=%08x) refs=%d\n", 317 dev, devtoname(dev), uminor(dev->si_udev), 318 dev->si_refs); 319 } 320 if (dev->si_refs < 2) { 321 printf("destroy_dev(): too few references on device! " 322 "%p %s(minor=%08x) refs=%d\n", 323 dev, devtoname(dev), uminor(dev->si_udev), 324 dev->si_refs); 325 } 326 dev->si_flags &= ~SI_ADHOC; 327 if (dev->si_flags & SI_HASHED) { 328 hash = dev->si_udev % DEVT_HASH; 329 LIST_REMOVE(dev, si_hash); 330 dev->si_flags &= ~SI_HASHED; 331 } 332 333 /* 334 * We have to release the cdevsw reference before we replace the 335 * device switch with dead_cdevsw. 336 */ 337 if (dead_cdevsw.d_port == NULL) 338 compile_devsw(&dead_cdevsw); 339 if (dev->si_devsw && dev->si_devsw != &dead_cdevsw) 340 cdevsw_release(dev->si_devsw); 341 dev->si_drv1 = NULL; 342 dev->si_drv2 = NULL; 343 dev->si_devsw = &dead_cdevsw; 344 dev->si_port = dev->si_devsw->d_port; 345 --dev->si_refs; /* release adhoc association reference */ 346 release_dev(dev); /* release callers reference */ 347 } 348 349 /* 350 * Destroy all ad-hoc device associations associated with a domain within a 351 * device switch. 352 */ 353 void 354 destroy_all_dev(struct cdevsw *devsw, u_int mask, u_int match) 355 { 356 int i; 357 dev_t dev; 358 dev_t ndev; 359 360 for (i = 0; i < DEVT_HASH; ++i) { 361 ndev = LIST_FIRST(&dev_hash[i]); 362 while ((dev = ndev) != NULL) { 363 ndev = LIST_NEXT(dev, si_hash); 364 KKASSERT(dev->si_flags & SI_ADHOC); 365 if (dev->si_devsw == devsw && 366 (dev->si_udev & mask) == match 367 ) { 368 ++dev->si_refs; 369 destroy_dev(dev); 370 } 371 } 372 } 373 } 374 375 /* 376 * Add a reference to a device. Callers generally add their own references 377 * when they are going to store a device node in a variable for long periods 378 * of time, to prevent a disassociation from free()ing the node. 379 * 380 * Also note that a caller that intends to call destroy_dev() must first 381 * obtain a reference on the device. The ad-hoc reference you get with 382 * make_dev() and friends is NOT sufficient to be able to call destroy_dev(). 383 */ 384 dev_t 385 reference_dev(dev_t dev) 386 { 387 if (dev != NODEV) { 388 ++dev->si_refs; 389 if (dev_ref_debug) { 390 printf("reference dev %p %s(minor=%08x) refs=%d\n", 391 dev, devtoname(dev), uminor(dev->si_udev), 392 dev->si_refs); 393 } 394 } 395 return(dev); 396 } 397 398 /* 399 * release a reference on a device. The device will be freed when the last 400 * reference has been released. 401 * 402 * NOTE: we must use si_udev to figure out the original (major, minor), 403 * because si_devsw could already be pointing at dead_cdevsw. 404 */ 405 void 406 release_dev(dev_t dev) 407 { 408 if (dev == NODEV) 409 return; 410 if (free_devt) { 411 KKASSERT(dev->si_refs > 0); 412 } else { 413 if (dev->si_refs <= 0) { 414 printf("Warning: extra release of dev %p(%s)\n", 415 dev, devtoname(dev)); 416 free_devt = 0; /* prevent bad things from occuring */ 417 } 418 } 419 --dev->si_refs; 420 if (dev_ref_debug) { 421 printf("release dev %p %s(minor=%08x) refs=%d\n", 422 dev, devtoname(dev), uminor(dev->si_udev), 423 dev->si_refs); 424 } 425 if (dev->si_refs == 0) { 426 if (dev->si_flags & SI_ADHOC) { 427 printf("Warning: illegal final release on ADHOC" 428 " device %p(%s), the device was never" 429 " destroyed!\n", 430 dev, devtoname(dev)); 431 } 432 if (dev->si_flags & SI_HASHED) { 433 printf("Warning: last release on device, no call" 434 " to destroy_dev() was made! dev %p(%s)\n", 435 dev, devtoname(dev)); 436 dev->si_refs = 3; 437 destroy_dev(dev); 438 dev->si_refs = 0; 439 } 440 if (SLIST_FIRST(&dev->si_hlist) != NULL) { 441 printf("Warning: last release on device, vnode" 442 " associations still exist! dev %p(%s)\n", 443 dev, devtoname(dev)); 444 free_devt = 0; /* prevent bad things from occuring */ 445 } 446 if (dev->si_devsw && dev->si_devsw != &dead_cdevsw) { 447 cdevsw_release(dev->si_devsw); 448 dev->si_devsw = NULL; 449 } 450 if (free_devt) { 451 if (dev->si_flags & SI_STASHED) { 452 bzero(dev, sizeof(*dev)); 453 LIST_INSERT_HEAD(&dev_free_list, dev, si_hash); 454 } else { 455 FREE(dev, M_DEVT); 456 } 457 } 458 } 459 } 460 461 const char * 462 devtoname(dev_t dev) 463 { 464 int mynor; 465 int len; 466 char *p; 467 const char *dname; 468 469 if (dev == NODEV) 470 return("#nodev"); 471 if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') { 472 p = dev->si_name; 473 len = sizeof(dev->si_name); 474 if ((dname = dev_dname(dev)) != NULL) 475 snprintf(p, len, "#%s/", dname); 476 else 477 snprintf(p, len, "#%d/", major(dev)); 478 len -= strlen(p); 479 p += strlen(p); 480 mynor = minor(dev); 481 if (mynor < 0 || mynor > 255) 482 snprintf(p, len, "%#x", (u_int)mynor); 483 else 484 snprintf(p, len, "%d", mynor); 485 } 486 return (dev->si_name); 487 } 488 489