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: stable/11/sys/kern/kern_conf.c 353783 2019-10-20 22:01:35Z kevans $"); 29116182Sobrien 3011126Sjulian#include <sys/param.h> 3148936Sphk#include <sys/kernel.h> 3283366Sjulian#include <sys/systm.h> 33178991Skib#include <sys/bus.h> 34111179Sphk#include <sys/bio.h> 3590737Sgreen#include <sys/lock.h> 3690737Sgreen#include <sys/mutex.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> 42120514Sphk#include <sys/poll.h> 43171181Skib#include <sys/sx.h> 4465374Sphk#include <sys/ctype.h> 45147982Srwatson#include <sys/ucred.h> 46171181Skib#include <sys/taskqueue.h> 4749535Sphk#include <machine/stdarg.h> 4811126Sjulian 49149144Sphk#include <fs/devfs/devfs_int.h> 50193275Sjhb#include <vm/vm.h> 51149144Sphk 52131996Sphkstatic MALLOC_DEFINE(M_DEVT, "cdev", "cdev storage"); 5348936Sphk 54150342Sphkstruct mtx devmtx; 55142242Sphkstatic void destroy_devl(struct cdev *dev); 56171188Skibstatic int destroy_dev_sched_cbl(struct cdev *dev, 57171188Skib void (*cb)(void *), void *arg); 58228804Sjhbstatic void destroy_dev_tq(void *ctx, int pending); 59209106Skibstatic int make_dev_credv(int flags, struct cdev **dres, struct cdevsw *devsw, 60209106Skib int unit, struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, 61171181Skib va_list ap); 62171181Skib 63170950Skibstatic struct cdev_priv_list cdevp_free_list = 64170950Skib TAILQ_HEAD_INITIALIZER(cdevp_free_list); 65177301Skibstatic SLIST_HEAD(free_cdevsw, cdevsw) cdevsw_gt_post_list = 66201145Santoine SLIST_HEAD_INITIALIZER(cdevsw_gt_post_list); 67170950Skib 68135600Sphkvoid 69135600Sphkdev_lock(void) 70126082Sphk{ 71151450Sjhb 72126082Sphk mtx_lock(&devmtx); 73126082Sphk} 74126082Sphk 75177301Skib/* 76177301Skib * Free all the memory collected while the cdev mutex was 77177301Skib * locked. Since devmtx is after the system map mutex, free() cannot 78177301Skib * be called immediately and is postponed until cdev mutex can be 79177301Skib * dropped. 80177301Skib */ 81170950Skibstatic void 82170950Skibdev_unlock_and_free(void) 83170950Skib{ 84177301Skib struct cdev_priv_list cdp_free; 85177301Skib struct free_cdevsw csw_free; 86170950Skib struct cdev_priv *cdp; 87177301Skib struct cdevsw *csw; 88170950Skib 89170950Skib mtx_assert(&devmtx, MA_OWNED); 90177301Skib 91177301Skib /* 92177301Skib * Make the local copy of the list heads while the dev_mtx is 93177301Skib * held. Free it later. 94177301Skib */ 95177301Skib TAILQ_INIT(&cdp_free); 96177301Skib TAILQ_CONCAT(&cdp_free, &cdevp_free_list, cdp_list); 97177301Skib csw_free = cdevsw_gt_post_list; 98177301Skib SLIST_INIT(&cdevsw_gt_post_list); 99177301Skib 100177301Skib mtx_unlock(&devmtx); 101177301Skib 102177301Skib while ((cdp = TAILQ_FIRST(&cdp_free)) != NULL) { 103177301Skib TAILQ_REMOVE(&cdp_free, cdp, cdp_list); 104170950Skib devfs_free(&cdp->cdp_c); 105170950Skib } 106177301Skib while ((csw = SLIST_FIRST(&csw_free)) != NULL) { 107177301Skib SLIST_REMOVE_HEAD(&csw_free, d_postfree_list); 108177301Skib free(csw, M_DEVT); 109177301Skib } 110170950Skib} 111170950Skib 112170950Skibstatic void 113170950Skibdev_free_devlocked(struct cdev *cdev) 114170950Skib{ 115170950Skib struct cdev_priv *cdp; 116170950Skib 117170950Skib mtx_assert(&devmtx, MA_OWNED); 118179828Skib cdp = cdev2priv(cdev); 119277391Skib KASSERT((cdp->cdp_flags & CDP_UNREF_DTR) == 0, 120277391Skib ("destroy_dev() was not called after delist_dev(%p)", cdev)); 121170950Skib TAILQ_INSERT_HEAD(&cdevp_free_list, cdp, cdp_list); 122170950Skib} 123170950Skib 124177301Skibstatic void 125177301Skibcdevsw_free_devlocked(struct cdevsw *csw) 126177301Skib{ 127177301Skib 128177301Skib mtx_assert(&devmtx, MA_OWNED); 129177301Skib SLIST_INSERT_HEAD(&cdevsw_gt_post_list, csw, d_postfree_list); 130177301Skib} 131177301Skib 132135600Sphkvoid 133135600Sphkdev_unlock(void) 134126082Sphk{ 135135704Sphk 136126082Sphk mtx_unlock(&devmtx); 137126082Sphk} 138126082Sphk 139126082Sphkvoid 140144385Sphkdev_ref(struct cdev *dev) 141144385Sphk{ 142144385Sphk 143144385Sphk mtx_assert(&devmtx, MA_NOTOWNED); 144144385Sphk mtx_lock(&devmtx); 145144385Sphk dev->si_refcount++; 146144385Sphk mtx_unlock(&devmtx); 147144385Sphk} 148144385Sphk 149144385Sphkvoid 150144384Sphkdev_refl(struct cdev *dev) 151126082Sphk{ 152135704Sphk 153142232Sphk mtx_assert(&devmtx, MA_OWNED); 154126082Sphk dev->si_refcount++; 155126082Sphk} 156126082Sphk 157126082Sphkvoid 158142242Sphkdev_rel(struct cdev *dev) 159126082Sphk{ 160142242Sphk int flag = 0; 161135600Sphk 162136014Sphk mtx_assert(&devmtx, MA_NOTOWNED); 163136014Sphk dev_lock(); 164126082Sphk dev->si_refcount--; 165126082Sphk KASSERT(dev->si_refcount >= 0, 166126082Sphk ("dev_rel(%s) gave negative count", devtoname(dev))); 167154029Sbz if (dev->si_devsw == NULL && dev->si_refcount == 0) { 168126082Sphk LIST_REMOVE(dev, si_list); 169136014Sphk flag = 1; 170136014Sphk } 171136014Sphk dev_unlock(); 172136014Sphk if (flag) 173150342Sphk devfs_free(dev); 174126082Sphk} 175136014Sphk 176135704Sphkstruct cdevsw * 177210923Skibdev_refthread(struct cdev *dev, int *ref) 178135704Sphk{ 179135704Sphk struct cdevsw *csw; 180171181Skib struct cdev_priv *cdp; 181126082Sphk 182135704Sphk mtx_assert(&devmtx, MA_NOTOWNED); 183210923Skib if ((dev->si_flags & SI_ETERNAL) != 0) { 184210923Skib *ref = 0; 185210923Skib return (dev->si_devsw); 186210923Skib } 187135704Sphk dev_lock(); 188135704Sphk csw = dev->si_devsw; 189171181Skib if (csw != NULL) { 190179828Skib cdp = cdev2priv(dev); 191171181Skib if ((cdp->cdp_flags & CDP_SCHED_DTR) == 0) 192256885Smav atomic_add_long(&dev->si_threadcount, 1); 193171181Skib else 194171181Skib csw = NULL; 195171181Skib } 196135704Sphk dev_unlock(); 197210923Skib *ref = 1; 198135704Sphk return (csw); 199135704Sphk} 200135704Sphk 201163529Skibstruct cdevsw * 202210923Skibdevvn_refthread(struct vnode *vp, struct cdev **devp, int *ref) 203163529Skib{ 204163529Skib struct cdevsw *csw; 205171181Skib struct cdev_priv *cdp; 206210923Skib struct cdev *dev; 207163529Skib 208163529Skib mtx_assert(&devmtx, MA_NOTOWNED); 209210923Skib if ((vp->v_vflag & VV_ETERNALDEV) != 0) { 210210923Skib dev = vp->v_rdev; 211210923Skib if (dev == NULL) 212210923Skib return (NULL); 213210923Skib KASSERT((dev->si_flags & SI_ETERNAL) != 0, 214210923Skib ("Not eternal cdev")); 215210923Skib *ref = 0; 216210923Skib csw = dev->si_devsw; 217210923Skib KASSERT(csw != NULL, ("Eternal cdev is destroyed")); 218210923Skib *devp = dev; 219210923Skib return (csw); 220210923Skib } 221210923Skib 222163529Skib csw = NULL; 223163529Skib dev_lock(); 224210923Skib dev = vp->v_rdev; 225210923Skib if (dev == NULL) { 226210923Skib dev_unlock(); 227210923Skib return (NULL); 228163529Skib } 229210923Skib cdp = cdev2priv(dev); 230210923Skib if ((cdp->cdp_flags & CDP_SCHED_DTR) == 0) { 231210923Skib csw = dev->si_devsw; 232210923Skib if (csw != NULL) 233256885Smav atomic_add_long(&dev->si_threadcount, 1); 234210923Skib } 235163529Skib dev_unlock(); 236210923Skib if (csw != NULL) { 237210923Skib *devp = dev; 238210923Skib *ref = 1; 239210923Skib } 240163529Skib return (csw); 241163529Skib} 242163529Skib 243135704Sphkvoid 244210923Skibdev_relthread(struct cdev *dev, int ref) 245135704Sphk{ 246135704Sphk 247135704Sphk mtx_assert(&devmtx, MA_NOTOWNED); 248210923Skib if (!ref) 249210923Skib return; 250179248Skib KASSERT(dev->si_threadcount > 0, 251179248Skib ("%s threadcount is wrong", dev->si_name)); 252256885Smav atomic_subtract_rel_long(&dev->si_threadcount, 1); 253135704Sphk} 254135704Sphk 255120514Sphkint 256120514Sphknullop(void) 257120514Sphk{ 25885603Sphk 259120514Sphk return (0); 260120514Sphk} 261120514Sphk 262120514Sphkint 263120514Sphkeopnotsupp(void) 264120514Sphk{ 265120514Sphk 266120514Sphk return (EOPNOTSUPP); 267120514Sphk} 268120514Sphk 269111179Sphkstatic int 270111179Sphkenxio(void) 271111179Sphk{ 272111179Sphk return (ENXIO); 273111179Sphk} 274111179Sphk 275120514Sphkstatic int 276120514Sphkenodev(void) 277120514Sphk{ 278120514Sphk return (ENODEV); 279120514Sphk} 280120514Sphk 281120514Sphk/* Define a dead_cdevsw for use when devices leave unexpectedly. */ 282120514Sphk 283111179Sphk#define dead_open (d_open_t *)enxio 284111179Sphk#define dead_close (d_close_t *)enxio 285111179Sphk#define dead_read (d_read_t *)enxio 286111179Sphk#define dead_write (d_write_t *)enxio 287111179Sphk#define dead_ioctl (d_ioctl_t *)enxio 288120514Sphk#define dead_poll (d_poll_t *)enodev 289120514Sphk#define dead_mmap (d_mmap_t *)enodev 290111179Sphk 291111179Sphkstatic void 292111179Sphkdead_strategy(struct bio *bp) 293111179Sphk{ 294111179Sphk 295111179Sphk biofinish(bp, NULL, ENXIO); 296111179Sphk} 297111179Sphk 298111220Sphk#define dead_dump (dumper_t *)enxio 299111179Sphk#define dead_kqfilter (d_kqfilter_t *)enxio 300193275Sjhb#define dead_mmap_single (d_mmap_single_t *)enodev 301111179Sphk 302111179Sphkstatic struct cdevsw dead_cdevsw = { 303126080Sphk .d_version = D_VERSION, 304111815Sphk .d_open = dead_open, 305111815Sphk .d_close = dead_close, 306111815Sphk .d_read = dead_read, 307111815Sphk .d_write = dead_write, 308111815Sphk .d_ioctl = dead_ioctl, 309111815Sphk .d_poll = dead_poll, 310111815Sphk .d_mmap = dead_mmap, 311111815Sphk .d_strategy = dead_strategy, 312111815Sphk .d_name = "dead", 313111815Sphk .d_dump = dead_dump, 314193275Sjhb .d_kqfilter = dead_kqfilter, 315193275Sjhb .d_mmap_single = dead_mmap_single 316111179Sphk}; 317111179Sphk 318120514Sphk/* Default methods if driver does not specify method */ 319111179Sphk 320120514Sphk#define null_open (d_open_t *)nullop 321120514Sphk#define null_close (d_close_t *)nullop 322120514Sphk#define no_read (d_read_t *)enodev 323120514Sphk#define no_write (d_write_t *)enodev 324120514Sphk#define no_ioctl (d_ioctl_t *)enodev 325201223Srnoland#define no_mmap (d_mmap_t *)enodev 326133741Sjmg#define no_kqfilter (d_kqfilter_t *)enodev 327193275Sjhb#define no_mmap_single (d_mmap_single_t *)enodev 328120514Sphk 329120514Sphkstatic void 330120514Sphkno_strategy(struct bio *bp) 331120514Sphk{ 332120514Sphk 333120514Sphk biofinish(bp, NULL, ENODEV); 334120514Sphk} 335120514Sphk 336120514Sphkstatic int 337130585Sphkno_poll(struct cdev *dev __unused, int events, struct thread *td __unused) 338120514Sphk{ 339120514Sphk 340189450Skib return (poll_no_poll(events)); 341120514Sphk} 342120514Sphk 343120514Sphk#define no_dump (dumper_t *)enodev 344120514Sphk 345149177Sphkstatic int 346149177Sphkgiant_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 347149177Sphk{ 348177301Skib struct cdevsw *dsw; 349210923Skib int ref, retval; 350149177Sphk 351210923Skib dsw = dev_refthread(dev, &ref); 352177301Skib if (dsw == NULL) 353177301Skib return (ENXIO); 354149177Sphk mtx_lock(&Giant); 355177301Skib retval = dsw->d_gianttrick->d_open(dev, oflags, devtype, td); 356149177Sphk mtx_unlock(&Giant); 357210923Skib dev_relthread(dev, ref); 358149177Sphk return (retval); 359149177Sphk} 360149177Sphk 361149177Sphkstatic int 362170152Skibgiant_fdopen(struct cdev *dev, int oflags, struct thread *td, struct file *fp) 363149177Sphk{ 364177301Skib struct cdevsw *dsw; 365210923Skib int ref, retval; 366149177Sphk 367210923Skib dsw = dev_refthread(dev, &ref); 368177301Skib if (dsw == NULL) 369177301Skib return (ENXIO); 370149177Sphk mtx_lock(&Giant); 371177301Skib retval = dsw->d_gianttrick->d_fdopen(dev, oflags, td, fp); 372149177Sphk mtx_unlock(&Giant); 373210923Skib dev_relthread(dev, ref); 374149177Sphk return (retval); 375149177Sphk} 376149177Sphk 377149177Sphkstatic int 378149177Sphkgiant_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 379149177Sphk{ 380177301Skib struct cdevsw *dsw; 381210923Skib int ref, retval; 382149177Sphk 383210923Skib dsw = dev_refthread(dev, &ref); 384177301Skib if (dsw == NULL) 385177301Skib return (ENXIO); 386149177Sphk mtx_lock(&Giant); 387177301Skib retval = dsw->d_gianttrick->d_close(dev, fflag, devtype, td); 388149177Sphk mtx_unlock(&Giant); 389210923Skib dev_relthread(dev, ref); 390149177Sphk return (retval); 391149177Sphk} 392149177Sphk 393149177Sphkstatic void 394149177Sphkgiant_strategy(struct bio *bp) 395149177Sphk{ 396177301Skib struct cdevsw *dsw; 397177301Skib struct cdev *dev; 398210923Skib int ref; 399149177Sphk 400177301Skib dev = bp->bio_dev; 401210923Skib dsw = dev_refthread(dev, &ref); 402177301Skib if (dsw == NULL) { 403177301Skib biofinish(bp, NULL, ENXIO); 404177301Skib return; 405177301Skib } 406149177Sphk mtx_lock(&Giant); 407177301Skib dsw->d_gianttrick->d_strategy(bp); 408149177Sphk mtx_unlock(&Giant); 409210923Skib dev_relthread(dev, ref); 410149177Sphk} 411149177Sphk 412149177Sphkstatic int 413149177Sphkgiant_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 414149177Sphk{ 415177301Skib struct cdevsw *dsw; 416210923Skib int ref, retval; 417149177Sphk 418210923Skib dsw = dev_refthread(dev, &ref); 419177301Skib if (dsw == NULL) 420177301Skib return (ENXIO); 421149177Sphk mtx_lock(&Giant); 422177858Skib retval = dsw->d_gianttrick->d_ioctl(dev, cmd, data, fflag, td); 423149177Sphk mtx_unlock(&Giant); 424210923Skib dev_relthread(dev, ref); 425149177Sphk return (retval); 426149177Sphk} 427149177Sphk 428149177Sphkstatic int 429149177Sphkgiant_read(struct cdev *dev, struct uio *uio, int ioflag) 430149177Sphk{ 431177301Skib struct cdevsw *dsw; 432210923Skib int ref, retval; 433149177Sphk 434210923Skib dsw = dev_refthread(dev, &ref); 435177301Skib if (dsw == NULL) 436177301Skib return (ENXIO); 437149177Sphk mtx_lock(&Giant); 438177858Skib retval = dsw->d_gianttrick->d_read(dev, uio, ioflag); 439149177Sphk mtx_unlock(&Giant); 440210923Skib dev_relthread(dev, ref); 441149177Sphk return (retval); 442149177Sphk} 443149177Sphk 444149177Sphkstatic int 445149177Sphkgiant_write(struct cdev *dev, struct uio *uio, int ioflag) 446149177Sphk{ 447177301Skib struct cdevsw *dsw; 448210923Skib int ref, retval; 449149177Sphk 450210923Skib dsw = dev_refthread(dev, &ref); 451177301Skib if (dsw == NULL) 452177301Skib return (ENXIO); 453149177Sphk mtx_lock(&Giant); 454177301Skib retval = dsw->d_gianttrick->d_write(dev, uio, ioflag); 455149177Sphk mtx_unlock(&Giant); 456210923Skib dev_relthread(dev, ref); 457149177Sphk return (retval); 458149177Sphk} 459149177Sphk 460149177Sphkstatic int 461149177Sphkgiant_poll(struct cdev *dev, int events, struct thread *td) 462149177Sphk{ 463177301Skib struct cdevsw *dsw; 464210923Skib int ref, retval; 465149177Sphk 466210923Skib dsw = dev_refthread(dev, &ref); 467177301Skib if (dsw == NULL) 468177301Skib return (ENXIO); 469149177Sphk mtx_lock(&Giant); 470177301Skib retval = dsw->d_gianttrick->d_poll(dev, events, td); 471149177Sphk mtx_unlock(&Giant); 472210923Skib dev_relthread(dev, ref); 473149177Sphk return (retval); 474149177Sphk} 475149177Sphk 476149177Sphkstatic int 477149177Sphkgiant_kqfilter(struct cdev *dev, struct knote *kn) 478149177Sphk{ 479177301Skib struct cdevsw *dsw; 480210923Skib int ref, retval; 481149177Sphk 482210923Skib dsw = dev_refthread(dev, &ref); 483177301Skib if (dsw == NULL) 484177301Skib return (ENXIO); 485149177Sphk mtx_lock(&Giant); 486177301Skib retval = dsw->d_gianttrick->d_kqfilter(dev, kn); 487149177Sphk mtx_unlock(&Giant); 488210923Skib dev_relthread(dev, ref); 489149177Sphk return (retval); 490149177Sphk} 491149177Sphk 492149177Sphkstatic int 493201223Srnolandgiant_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, 494196615Sjhb vm_memattr_t *memattr) 495149177Sphk{ 496177301Skib struct cdevsw *dsw; 497210923Skib int ref, retval; 498149177Sphk 499210923Skib dsw = dev_refthread(dev, &ref); 500177301Skib if (dsw == NULL) 501177301Skib return (ENXIO); 502149177Sphk mtx_lock(&Giant); 503201223Srnoland retval = dsw->d_gianttrick->d_mmap(dev, offset, paddr, nprot, 504201223Srnoland memattr); 505149177Sphk mtx_unlock(&Giant); 506210923Skib dev_relthread(dev, ref); 507149177Sphk return (retval); 508149177Sphk} 509149177Sphk 510193275Sjhbstatic int 511193275Sjhbgiant_mmap_single(struct cdev *dev, vm_ooffset_t *offset, vm_size_t size, 512193275Sjhb vm_object_t *object, int nprot) 513193275Sjhb{ 514193275Sjhb struct cdevsw *dsw; 515210923Skib int ref, retval; 516149177Sphk 517210923Skib dsw = dev_refthread(dev, &ref); 518193275Sjhb if (dsw == NULL) 519193275Sjhb return (ENXIO); 520193275Sjhb mtx_lock(&Giant); 521193275Sjhb retval = dsw->d_gianttrick->d_mmap_single(dev, offset, size, object, 522193275Sjhb nprot); 523193275Sjhb mtx_unlock(&Giant); 524210923Skib dev_relthread(dev, ref); 525193275Sjhb return (retval); 526193275Sjhb} 527193275Sjhb 528178991Skibstatic void 529207729Skibnotify(struct cdev *dev, const char *ev, int flags) 530178991Skib{ 531178991Skib static const char prefix[] = "cdev="; 532178991Skib char *data; 533209105Skib int namelen, mflags; 534178991Skib 535178991Skib if (cold) 536178991Skib return; 537209105Skib mflags = (flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK; 538178991Skib namelen = strlen(dev->si_name); 539209105Skib data = malloc(namelen + sizeof(prefix), M_TEMP, mflags); 540192535Sattilio if (data == NULL) 541192535Sattilio return; 542178991Skib memcpy(data, prefix, sizeof(prefix) - 1); 543178991Skib memcpy(data + sizeof(prefix) - 1, dev->si_name, namelen + 1); 544209105Skib devctl_notify_f("DEVFS", "CDEV", ev, data, mflags); 545178991Skib free(data, M_TEMP); 546178991Skib} 547178991Skib 548178991Skibstatic void 549207729Skibnotify_create(struct cdev *dev, int flags) 550178991Skib{ 551178991Skib 552207729Skib notify(dev, "CREATE", flags); 553178991Skib} 554178991Skib 555178991Skibstatic void 556178991Skibnotify_destroy(struct cdev *dev) 557178991Skib{ 558178991Skib 559207729Skib notify(dev, "DESTROY", MAKEDEV_WAITOK); 560178991Skib} 561178991Skib 562130585Sphkstatic struct cdev * 563293346Skibnewdev(struct make_dev_args *args, struct cdev *si) 56447028Sphk{ 565140733Sphk struct cdev *si2; 566293346Skib struct cdevsw *csw; 56748936Sphk 568140733Sphk mtx_assert(&devmtx, MA_OWNED); 569293346Skib csw = args->mda_devsw; 570353783Skevans si2 = NULL; 571179726Sed if (csw->d_flags & D_NEEDMINOR) { 572179726Sed /* We may want to return an existing device */ 573179726Sed LIST_FOREACH(si2, &csw->d_devs, si_list) { 574293346Skib if (dev2unit(si2) == args->mda_unit) { 575179726Sed dev_free_devlocked(si); 576353783Skevans si = si2; 577353783Skevans break; 578179726Sed } 579140733Sphk } 580353783Skevans 581353783Skevans /* 582353783Skevans * If we're returning an existing device, we should make sure 583353783Skevans * it isn't already initialized. This would have been caught 584353783Skevans * in consumers anyways, but it's good to catch such a case 585353783Skevans * early. We still need to complete initialization of the 586353783Skevans * device, and we'll use whatever make_dev_args were passed in 587353783Skevans * to do so. 588353783Skevans */ 589353783Skevans KASSERT(si2 == NULL || (si2->si_flags & SI_NAMED) == 0, 590353783Skevans ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)", 591353783Skevans args->mda_devsw->d_name, dev2unit(si2), devtoname(si2))); 59248936Sphk } 593293346Skib si->si_drv0 = args->mda_unit; 594293346Skib si->si_drv1 = args->mda_si_drv1; 595293346Skib si->si_drv2 = args->mda_si_drv2; 596353783Skevans /* Only push to csw->d_devs if it's not a cloned device. */ 597353783Skevans if (si2 == NULL) { 598353783Skevans si->si_devsw = csw; 599353783Skevans LIST_INSERT_HEAD(&csw->d_devs, si, si_list); 600353783Skevans } else { 601353783Skevans KASSERT(si->si_devsw == csw, 602353783Skevans ("%s: inconsistent devsw between clone_create() and make_dev()", 603353783Skevans __func__)); 604353783Skevans } 605125850Sbde return (si); 60647028Sphk} 60747028Sphk 608125846Sphkstatic void 609144292Sphkfini_cdevsw(struct cdevsw *devsw) 61049535Sphk{ 611149324Sphk struct cdevsw *gt; 61249535Sphk 613149324Sphk if (devsw->d_gianttrick != NULL) { 614149324Sphk gt = devsw->d_gianttrick; 615149324Sphk memcpy(devsw, gt, sizeof *devsw); 616177301Skib cdevsw_free_devlocked(gt); 617149324Sphk devsw->d_gianttrick = NULL; 618149324Sphk } 619126156Sphk devsw->d_flags &= ~D_INIT; 620126082Sphk} 621126082Sphk 622207729Skibstatic int 623207729Skibprep_cdevsw(struct cdevsw *devsw, int flags) 624126077Sphk{ 625149177Sphk struct cdevsw *dsw2; 626126077Sphk 627177301Skib mtx_assert(&devmtx, MA_OWNED); 628177301Skib if (devsw->d_flags & D_INIT) 629209106Skib return (0); 630177301Skib if (devsw->d_flags & D_NEEDGIANT) { 631177301Skib dev_unlock(); 632207729Skib dsw2 = malloc(sizeof *dsw2, M_DEVT, 633207729Skib (flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK); 634177301Skib dev_lock(); 635207729Skib if (dsw2 == NULL && !(devsw->d_flags & D_INIT)) 636209106Skib return (ENOMEM); 637177301Skib } else 638149177Sphk dsw2 = NULL; 639177301Skib if (devsw->d_flags & D_INIT) { 640177301Skib if (dsw2 != NULL) 641177301Skib cdevsw_free_devlocked(dsw2); 642209106Skib return (0); 643177301Skib } 644126082Sphk 645201223Srnoland if (devsw->d_version != D_VERSION_03) { 646126082Sphk printf( 647126082Sphk "WARNING: Device driver \"%s\" has wrong version %s\n", 648154266Salfred devsw->d_name == NULL ? "???" : devsw->d_name, 649154266Salfred "and is disabled. Recompile KLD module."); 650126082Sphk devsw->d_open = dead_open; 651126082Sphk devsw->d_close = dead_close; 652126082Sphk devsw->d_read = dead_read; 653126082Sphk devsw->d_write = dead_write; 654126082Sphk devsw->d_ioctl = dead_ioctl; 655126082Sphk devsw->d_poll = dead_poll; 656126082Sphk devsw->d_mmap = dead_mmap; 657201223Srnoland devsw->d_mmap_single = dead_mmap_single; 658126082Sphk devsw->d_strategy = dead_strategy; 659126082Sphk devsw->d_dump = dead_dump; 660126082Sphk devsw->d_kqfilter = dead_kqfilter; 661126082Sphk } 662126082Sphk 663149177Sphk if (devsw->d_flags & D_NEEDGIANT) { 664149177Sphk if (devsw->d_gianttrick == NULL) { 665149177Sphk memcpy(dsw2, devsw, sizeof *dsw2); 666149177Sphk devsw->d_gianttrick = dsw2; 667177301Skib dsw2 = NULL; 668177301Skib } 669149177Sphk } 670149177Sphk 671149177Sphk#define FIXUP(member, noop, giant) \ 672149177Sphk do { \ 673149177Sphk if (devsw->member == NULL) { \ 674149177Sphk devsw->member = noop; \ 675149177Sphk } else if (devsw->d_flags & D_NEEDGIANT) \ 676149177Sphk devsw->member = giant; \ 677149177Sphk } \ 678149177Sphk while (0) 679149177Sphk 680149177Sphk FIXUP(d_open, null_open, giant_open); 681149177Sphk FIXUP(d_fdopen, NULL, giant_fdopen); 682149177Sphk FIXUP(d_close, null_close, giant_close); 683149177Sphk FIXUP(d_read, no_read, giant_read); 684149177Sphk FIXUP(d_write, no_write, giant_write); 685149177Sphk FIXUP(d_ioctl, no_ioctl, giant_ioctl); 686149177Sphk FIXUP(d_poll, no_poll, giant_poll); 687201223Srnoland FIXUP(d_mmap, no_mmap, giant_mmap); 688149177Sphk FIXUP(d_strategy, no_strategy, giant_strategy); 689149177Sphk FIXUP(d_kqfilter, no_kqfilter, giant_kqfilter); 690193275Sjhb FIXUP(d_mmap_single, no_mmap_single, giant_mmap_single); 691149177Sphk 692120514Sphk if (devsw->d_dump == NULL) devsw->d_dump = no_dump; 693126082Sphk 694126082Sphk LIST_INIT(&devsw->d_devs); 695126082Sphk 696126082Sphk devsw->d_flags |= D_INIT; 697126082Sphk 698177301Skib if (dsw2 != NULL) 699177301Skib cdevsw_free_devlocked(dsw2); 700209106Skib return (0); 701125846Sphk} 702111622Sphk 703209106Skibstatic int 704213526Sjhprep_devname(struct cdev *dev, const char *fmt, va_list ap) 705213526Sjh{ 706213526Sjh int len; 707213526Sjh char *from, *q, *s, *to; 708213526Sjh 709213526Sjh mtx_assert(&devmtx, MA_OWNED); 710213526Sjh 711231386Sed len = vsnrprintf(dev->si_name, sizeof(dev->si_name), 32, fmt, ap); 712231379Sed if (len > sizeof(dev->si_name) - 1) 713213526Sjh return (ENAMETOOLONG); 714213526Sjh 715213526Sjh /* Strip leading slashes. */ 716231379Sed for (from = dev->si_name; *from == '/'; from++) 717213526Sjh ; 718213526Sjh 719231379Sed for (to = dev->si_name; *from != '\0'; from++, to++) { 720244584Sjh /* 721244584Sjh * Spaces and double quotation marks cause 722244584Sjh * problems for the devctl(4) protocol. 723244584Sjh * Reject names containing those characters. 724244584Sjh */ 725244584Sjh if (isspace(*from) || *from == '"') 726244584Sjh return (EINVAL); 727213526Sjh /* Treat multiple sequential slashes as single. */ 728213526Sjh while (from[0] == '/' && from[1] == '/') 729213526Sjh from++; 730213526Sjh /* Trailing slash is considered invalid. */ 731213526Sjh if (from[0] == '/' && from[1] == '\0') 732213526Sjh return (EINVAL); 733213526Sjh *to = *from; 734213526Sjh } 735213526Sjh *to = '\0'; 736213526Sjh 737231379Sed if (dev->si_name[0] == '\0') 738213526Sjh return (EINVAL); 739213526Sjh 740213526Sjh /* Disallow "." and ".." components. */ 741231379Sed for (s = dev->si_name;;) { 742213526Sjh for (q = s; *q != '/' && *q != '\0'; q++) 743213526Sjh ; 744213526Sjh if (q - s == 1 && s[0] == '.') 745213526Sjh return (EINVAL); 746213526Sjh if (q - s == 2 && s[0] == '.' && s[1] == '.') 747213526Sjh return (EINVAL); 748213526Sjh if (*q != '/') 749213526Sjh break; 750213526Sjh s = q + 1; 751213526Sjh } 752213526Sjh 753231379Sed if (devfs_dev_exists(dev->si_name) != 0) 754213526Sjh return (EEXIST); 755213526Sjh 756213526Sjh return (0); 757213526Sjh} 758213526Sjh 759293346Skibvoid 760293346Skibmake_dev_args_init_impl(struct make_dev_args *args, size_t sz) 761293346Skib{ 762293346Skib 763293346Skib bzero(args, sz); 764293346Skib args->mda_size = sz; 765293346Skib} 766293346Skib 767213526Sjhstatic int 768293346Skibmake_dev_sv(struct make_dev_args *args1, struct cdev **dres, 769293346Skib const char *fmt, va_list ap) 770125846Sphk{ 771213526Sjh struct cdev *dev, *dev_new; 772293346Skib struct make_dev_args args; 773213526Sjh int res; 774125846Sphk 775293346Skib bzero(&args, sizeof(args)); 776293346Skib if (sizeof(args) < args1->mda_size) 777293346Skib return (EINVAL); 778293346Skib bcopy(args1, &args, args1->mda_size); 779293346Skib KASSERT((args.mda_flags & MAKEDEV_WAITOK) == 0 || 780293346Skib (args.mda_flags & MAKEDEV_NOWAIT) == 0, 781293346Skib ("make_dev_sv: both WAITOK and NOWAIT specified")); 782293346Skib dev_new = devfs_alloc(args.mda_flags); 783213526Sjh if (dev_new == NULL) 784209106Skib return (ENOMEM); 785140733Sphk dev_lock(); 786293346Skib res = prep_cdevsw(args.mda_devsw, args.mda_flags); 787209106Skib if (res != 0) { 788207729Skib dev_unlock(); 789213526Sjh devfs_free(dev_new); 790209106Skib return (res); 791207729Skib } 792293346Skib dev = newdev(&args, dev_new); 793214917Sjh if ((dev->si_flags & SI_NAMED) == 0) { 794213526Sjh res = prep_devname(dev, fmt, ap); 795213526Sjh if (res != 0) { 796293346Skib if ((args.mda_flags & MAKEDEV_CHECKNAME) == 0) { 797213526Sjh panic( 798293346Skib "make_dev_sv: bad si_name (error=%d, si_name=%s)", 799213526Sjh res, dev->si_name); 800213526Sjh } 801213526Sjh if (dev == dev_new) { 802213526Sjh LIST_REMOVE(dev, si_list); 803213526Sjh dev_unlock(); 804213526Sjh devfs_free(dev); 805224521Skib } else 806224521Skib dev_unlock(); 807213526Sjh return (res); 808214917Sjh } 809213526Sjh } 810293346Skib if ((args.mda_flags & MAKEDEV_REF) != 0) 811171181Skib dev_refl(dev); 812293346Skib if ((args.mda_flags & MAKEDEV_ETERNAL) != 0) 813210923Skib dev->si_flags |= SI_ETERNAL; 814126082Sphk KASSERT(!(dev->si_flags & SI_NAMED), 815144281Sphk ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)", 816293346Skib args.mda_devsw->d_name, dev2unit(dev), devtoname(dev))); 81765747Sphk dev->si_flags |= SI_NAMED; 818293346Skib if (args.mda_cr != NULL) 819293346Skib dev->si_cred = crhold(args.mda_cr); 820293346Skib dev->si_uid = args.mda_uid; 821293346Skib dev->si_gid = args.mda_gid; 822293346Skib dev->si_mode = args.mda_mode; 82350092Sjulian 824111730Sphk devfs_create(dev); 825171202Skib clean_unrhdrl(devfs_inos); 826177301Skib dev_unlock_and_free(); 827178991Skib 828293346Skib notify_create(dev, args.mda_flags); 829178991Skib 830209106Skib *dres = dev; 831209106Skib return (0); 83249535Sphk} 83349535Sphk 834293346Skibint 835293346Skibmake_dev_s(struct make_dev_args *args, struct cdev **dres, 836293346Skib const char *fmt, ...) 837293346Skib{ 838293346Skib va_list ap; 839293346Skib int res; 840293346Skib 841293346Skib va_start(ap, fmt); 842293346Skib res = make_dev_sv(args, dres, fmt, ap); 843293346Skib va_end(ap); 844293346Skib return (res); 845293346Skib} 846293346Skib 847293346Skibstatic int 848293346Skibmake_dev_credv(int flags, struct cdev **dres, struct cdevsw *devsw, int unit, 849293346Skib struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, 850293346Skib va_list ap) 851293346Skib{ 852293346Skib struct make_dev_args args; 853293346Skib 854293346Skib make_dev_args_init(&args); 855293346Skib args.mda_flags = flags; 856293346Skib args.mda_devsw = devsw; 857293346Skib args.mda_cr = cr; 858293346Skib args.mda_uid = uid; 859293346Skib args.mda_gid = gid; 860293346Skib args.mda_mode = mode; 861293346Skib args.mda_unit = unit; 862293346Skib return (make_dev_sv(&args, dres, fmt, ap)); 863293346Skib} 864293346Skib 865147982Srwatsonstruct cdev * 866183382Sedmake_dev(struct cdevsw *devsw, int unit, uid_t uid, gid_t gid, int mode, 867147982Srwatson const char *fmt, ...) 868147982Srwatson{ 869147982Srwatson struct cdev *dev; 870147982Srwatson va_list ap; 871209106Skib int res; 872147982Srwatson 873147982Srwatson va_start(ap, fmt); 874209106Skib res = make_dev_credv(0, &dev, devsw, unit, NULL, uid, gid, mode, fmt, 875209106Skib ap); 876147982Srwatson va_end(ap); 877213526Sjh KASSERT(res == 0 && dev != NULL, 878213526Sjh ("make_dev: failed make_dev_credv (error=%d)", res)); 879147982Srwatson return (dev); 880147982Srwatson} 881147982Srwatson 882147982Srwatsonstruct cdev * 883183382Sedmake_dev_cred(struct cdevsw *devsw, int unit, struct ucred *cr, uid_t uid, 884147982Srwatson gid_t gid, int mode, const char *fmt, ...) 885147982Srwatson{ 886147982Srwatson struct cdev *dev; 887147982Srwatson va_list ap; 888209106Skib int res; 889147982Srwatson 890147982Srwatson va_start(ap, fmt); 891209106Skib res = make_dev_credv(0, &dev, devsw, unit, cr, uid, gid, mode, fmt, ap); 892147982Srwatson va_end(ap); 893147982Srwatson 894209106Skib KASSERT(res == 0 && dev != NULL, 895213526Sjh ("make_dev_cred: failed make_dev_credv (error=%d)", res)); 896147982Srwatson return (dev); 897147982Srwatson} 898147982Srwatson 899171181Skibstruct cdev * 900209106Skibmake_dev_credf(int flags, struct cdevsw *devsw, int unit, struct ucred *cr, 901209106Skib uid_t uid, gid_t gid, int mode, const char *fmt, ...) 902171181Skib{ 903171181Skib struct cdev *dev; 904171181Skib va_list ap; 905209106Skib int res; 906171181Skib 907171181Skib va_start(ap, fmt); 908209106Skib res = make_dev_credv(flags, &dev, devsw, unit, cr, uid, gid, mode, 909171181Skib fmt, ap); 910171181Skib va_end(ap); 911171181Skib 912213526Sjh KASSERT(((flags & MAKEDEV_NOWAIT) != 0 && res == ENOMEM) || 913213526Sjh ((flags & MAKEDEV_CHECKNAME) != 0 && res != ENOMEM) || res == 0, 914213526Sjh ("make_dev_credf: failed make_dev_credv (error=%d)", res)); 915209106Skib return (res == 0 ? dev : NULL); 916171181Skib} 917171181Skib 918209106Skibint 919209244Sedmake_dev_p(int flags, struct cdev **cdev, struct cdevsw *devsw, 920209106Skib struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, ...) 921209106Skib{ 922209106Skib va_list ap; 923209106Skib int res; 924209106Skib 925209106Skib va_start(ap, fmt); 926209244Sed res = make_dev_credv(flags, cdev, devsw, 0, cr, uid, gid, mode, 927209106Skib fmt, ap); 928209106Skib va_end(ap); 929209106Skib 930213526Sjh KASSERT(((flags & MAKEDEV_NOWAIT) != 0 && res == ENOMEM) || 931213526Sjh ((flags & MAKEDEV_CHECKNAME) != 0 && res != ENOMEM) || res == 0, 932213526Sjh ("make_dev_p: failed make_dev_credv (error=%d)", res)); 933209106Skib return (res); 934209106Skib} 935209106Skib 936150342Sphkstatic void 937150342Sphkdev_dependsl(struct cdev *pdev, struct cdev *cdev) 938150342Sphk{ 939150342Sphk 940150342Sphk cdev->si_parent = pdev; 941150342Sphk cdev->si_flags |= SI_CHILD; 942150342Sphk LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings); 943150342Sphk} 944150342Sphk 945150342Sphk 94677215Sphkvoid 947130585Sphkdev_depends(struct cdev *pdev, struct cdev *cdev) 94877215Sphk{ 94977215Sphk 950135600Sphk dev_lock(); 951150342Sphk dev_dependsl(pdev, cdev); 952135600Sphk dev_unlock(); 95377215Sphk} 95477215Sphk 955221397Saestatic int 956221397Saemake_dev_alias_v(int flags, struct cdev **cdev, struct cdev *pdev, 957221397Sae const char *fmt, va_list ap) 95864880Sphk{ 959130585Sphk struct cdev *dev; 960213526Sjh int error; 96164880Sphk 962221397Sae KASSERT(pdev != NULL, ("make_dev_alias_v: pdev is NULL")); 963221397Sae KASSERT((flags & MAKEDEV_WAITOK) == 0 || (flags & MAKEDEV_NOWAIT) == 0, 964221397Sae ("make_dev_alias_v: both WAITOK and NOWAIT specified")); 965221397Sae KASSERT((flags & ~(MAKEDEV_WAITOK | MAKEDEV_NOWAIT | 966221397Sae MAKEDEV_CHECKNAME)) == 0, 967221397Sae ("make_dev_alias_v: invalid flags specified (flags=%02x)", flags)); 968221397Sae 969221397Sae dev = devfs_alloc(flags); 970221397Sae if (dev == NULL) 971221397Sae return (ENOMEM); 972135600Sphk dev_lock(); 97364880Sphk dev->si_flags |= SI_ALIAS; 974213526Sjh error = prep_devname(dev, fmt, ap); 975213526Sjh if (error != 0) { 976221397Sae if ((flags & MAKEDEV_CHECKNAME) == 0) { 977221397Sae panic("make_dev_alias_v: bad si_name " 978221397Sae "(error=%d, si_name=%s)", error, dev->si_name); 979221397Sae } 980221397Sae dev_unlock(); 981221397Sae devfs_free(dev); 982221397Sae return (error); 983110318Sphk } 984213526Sjh dev->si_flags |= SI_NAMED; 985111730Sphk devfs_create(dev); 986180445Skib dev_dependsl(pdev, dev); 987171202Skib clean_unrhdrl(devfs_inos); 988135600Sphk dev_unlock(); 989178991Skib 990221397Sae notify_create(dev, flags); 991221397Sae *cdev = dev; 992178991Skib 993221397Sae return (0); 994221397Sae} 995221397Sae 996221397Saestruct cdev * 997221397Saemake_dev_alias(struct cdev *pdev, const char *fmt, ...) 998221397Sae{ 999221397Sae struct cdev *dev; 1000221397Sae va_list ap; 1001221397Sae int res; 1002221397Sae 1003221397Sae va_start(ap, fmt); 1004221397Sae res = make_dev_alias_v(MAKEDEV_WAITOK, &dev, pdev, fmt, ap); 1005221397Sae va_end(ap); 1006221397Sae 1007221397Sae KASSERT(res == 0 && dev != NULL, 1008221397Sae ("make_dev_alias: failed make_dev_alias_v (error=%d)", res)); 100964880Sphk return (dev); 101064880Sphk} 101164880Sphk 1012221397Saeint 1013221397Saemake_dev_alias_p(int flags, struct cdev **cdev, struct cdev *pdev, 1014221397Sae const char *fmt, ...) 1015221397Sae{ 1016221397Sae va_list ap; 1017221397Sae int res; 1018221397Sae 1019221397Sae va_start(ap, fmt); 1020221397Sae res = make_dev_alias_v(flags, cdev, pdev, fmt, ap); 1021221397Sae va_end(ap); 1022221397Sae return (res); 1023221397Sae} 1024221397Sae 1025223085Sgibbsint 1026223085Sgibbsmake_dev_physpath_alias(int flags, struct cdev **cdev, struct cdev *pdev, 1027223085Sgibbs struct cdev *old_alias, const char *physpath) 1028223085Sgibbs{ 1029223085Sgibbs char *devfspath; 1030223085Sgibbs int physpath_len; 1031223085Sgibbs int max_parentpath_len; 1032223085Sgibbs int parentpath_len; 1033223085Sgibbs int devfspathbuf_len; 1034223085Sgibbs int mflags; 1035223085Sgibbs int ret; 1036223085Sgibbs 1037223085Sgibbs *cdev = NULL; 1038223085Sgibbs devfspath = NULL; 1039223085Sgibbs physpath_len = strlen(physpath); 1040223085Sgibbs ret = EINVAL; 1041223085Sgibbs if (physpath_len == 0) 1042223085Sgibbs goto out; 1043223085Sgibbs 1044223085Sgibbs if (strncmp("id1,", physpath, 4) == 0) { 1045223085Sgibbs physpath += 4; 1046223085Sgibbs physpath_len -= 4; 1047223085Sgibbs if (physpath_len == 0) 1048223085Sgibbs goto out; 1049223085Sgibbs } 1050223085Sgibbs 1051223085Sgibbs max_parentpath_len = SPECNAMELEN - physpath_len - /*/*/1; 1052223085Sgibbs parentpath_len = strlen(pdev->si_name); 1053223085Sgibbs if (max_parentpath_len < parentpath_len) { 1054235899Smav if (bootverbose) 1055235899Smav printf("WARNING: Unable to alias %s " 1056235899Smav "to %s/%s - path too long\n", 1057235899Smav pdev->si_name, physpath, pdev->si_name); 1058223085Sgibbs ret = ENAMETOOLONG; 1059223085Sgibbs goto out; 1060223085Sgibbs } 1061223085Sgibbs 1062223085Sgibbs mflags = (flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK; 1063223085Sgibbs devfspathbuf_len = physpath_len + /*/*/1 + parentpath_len + /*NUL*/1; 1064223085Sgibbs devfspath = malloc(devfspathbuf_len, M_DEVBUF, mflags); 1065223085Sgibbs if (devfspath == NULL) { 1066223085Sgibbs ret = ENOMEM; 1067223085Sgibbs goto out; 1068223085Sgibbs } 1069223085Sgibbs 1070223085Sgibbs sprintf(devfspath, "%s/%s", physpath, pdev->si_name); 1071227444Skib if (old_alias != NULL && strcmp(old_alias->si_name, devfspath) == 0) { 1072223085Sgibbs /* Retain the existing alias. */ 1073223085Sgibbs *cdev = old_alias; 1074223085Sgibbs old_alias = NULL; 1075223085Sgibbs ret = 0; 1076223085Sgibbs } else { 1077227443Skib ret = make_dev_alias_p(flags, cdev, pdev, "%s", devfspath); 1078223085Sgibbs } 1079223085Sgibbsout: 1080223085Sgibbs if (old_alias != NULL) 1081223085Sgibbs destroy_dev(old_alias); 1082223085Sgibbs if (devfspath != NULL) 1083223085Sgibbs free(devfspath, M_DEVBUF); 1084223085Sgibbs return (ret); 1085223085Sgibbs} 1086223085Sgibbs 1087126082Sphkstatic void 1088142242Sphkdestroy_devl(struct cdev *dev) 108950549Sphk{ 1090135843Sphk struct cdevsw *csw; 1091216371Shselasky struct cdev_privdata *p; 1092277391Skib struct cdev_priv *cdp; 1093135843Sphk 1094142242Sphk mtx_assert(&devmtx, MA_OWNED); 1095135843Sphk KASSERT(dev->si_flags & SI_NAMED, 1096183397Sed ("WARNING: Driver mistake: destroy_dev on %d\n", dev2unit(dev))); 1097210923Skib KASSERT((dev->si_flags & SI_ETERNAL) == 0, 1098210923Skib ("WARNING: Driver mistake: destroy_dev on eternal %d\n", 1099210923Skib dev2unit(dev))); 1100154029Sbz 1101277391Skib cdp = cdev2priv(dev); 1102277391Skib if ((cdp->cdp_flags & CDP_UNREF_DTR) == 0) { 1103277391Skib /* 1104277391Skib * Avoid race with dev_rel(), e.g. from the populate 1105277391Skib * loop. If CDP_UNREF_DTR flag is set, the reference 1106277391Skib * to be dropped at the end of destroy_devl() was 1107277391Skib * already taken by delist_dev_locked(). 1108277391Skib */ 1109277391Skib dev_refl(dev); 1110126082Sphk 1111277391Skib devfs_destroy(dev); 1112277391Skib } 1113277391Skib 1114126082Sphk /* Remove name marking */ 1115126077Sphk dev->si_flags &= ~SI_NAMED; 1116126077Sphk 1117126082Sphk /* If we are a child, remove us from the parents list */ 111877215Sphk if (dev->si_flags & SI_CHILD) { 111977215Sphk LIST_REMOVE(dev, si_siblings); 112077215Sphk dev->si_flags &= ~SI_CHILD; 112177215Sphk } 1122126082Sphk 1123126082Sphk /* Kill our children */ 112477215Sphk while (!LIST_EMPTY(&dev->si_children)) 1125142242Sphk destroy_devl(LIST_FIRST(&dev->si_children)); 1126126082Sphk 1127126082Sphk /* Remove from clone list */ 1128126077Sphk if (dev->si_flags & SI_CLONELIST) { 1129126077Sphk LIST_REMOVE(dev, si_clone); 1130126077Sphk dev->si_flags &= ~SI_CLONELIST; 1131126077Sphk } 1132126082Sphk 1133135843Sphk csw = dev->si_devsw; 1134135934Sgreen dev->si_devsw = NULL; /* already NULL for SI_ALIAS */ 1135135934Sgreen while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) { 1136135843Sphk csw->d_purge(dev); 1137135843Sphk msleep(csw, &devmtx, PRIBIO, "devprg", hz/10); 1138158684Sphk if (dev->si_threadcount) 1139158684Sphk printf("Still %lu threads in %s\n", 1140158684Sphk dev->si_threadcount, devtoname(dev)); 1141135843Sphk } 1142163328Stegge while (dev->si_threadcount != 0) { 1143163328Stegge /* Use unique dummy wait ident */ 1144163328Stegge msleep(&csw, &devmtx, PRIBIO, "devdrn", hz / 10); 1145163328Stegge } 1146135843Sphk 1147179175Skib dev_unlock(); 1148280345Shselasky if ((cdp->cdp_flags & CDP_UNREF_DTR) == 0) { 1149280345Shselasky /* avoid out of order notify events */ 1150280345Shselasky notify_destroy(dev); 1151280345Shselasky } 1152179175Skib mtx_lock(&cdevpriv_mtx); 1153280345Shselasky while ((p = LIST_FIRST(&cdp->cdp_fdpriv)) != NULL) { 1154179175Skib devfs_destroy_cdevpriv(p); 1155179175Skib mtx_lock(&cdevpriv_mtx); 1156179175Skib } 1157179175Skib mtx_unlock(&cdevpriv_mtx); 1158179175Skib dev_lock(); 1159178991Skib 1160135843Sphk dev->si_drv1 = 0; 1161135843Sphk dev->si_drv2 = 0; 1162135843Sphk bzero(&dev->__si_u, sizeof(dev->__si_u)); 1163135843Sphk 1164126082Sphk if (!(dev->si_flags & SI_ALIAS)) { 1165126082Sphk /* Remove from cdevsw list */ 1166126082Sphk LIST_REMOVE(dev, si_list); 1167126082Sphk 1168150342Sphk /* If cdevsw has no more struct cdev *'s, clean it */ 1169171181Skib if (LIST_EMPTY(&csw->d_devs)) { 1170135844Sphk fini_cdevsw(csw); 1171171181Skib wakeup(&csw->d_devs); 1172171181Skib } 1173126082Sphk } 117465747Sphk dev->si_flags &= ~SI_ALIAS; 1175277391Skib cdp->cdp_flags &= ~CDP_UNREF_DTR; 1176277391Skib dev->si_refcount--; 1177135843Sphk 1178277391Skib if (dev->si_refcount > 0) 1179126082Sphk LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list); 1180277391Skib else 1181170950Skib dev_free_devlocked(dev); 118250549Sphk} 118350549Sphk 1184277179Shselaskystatic void 1185277179Shselaskydelist_dev_locked(struct cdev *dev) 1186277179Shselasky{ 1187277391Skib struct cdev_priv *cdp; 1188277179Shselasky struct cdev *child; 1189277391Skib 1190277391Skib mtx_assert(&devmtx, MA_OWNED); 1191277391Skib cdp = cdev2priv(dev); 1192277391Skib if ((cdp->cdp_flags & CDP_UNREF_DTR) != 0) 1193277391Skib return; 1194277391Skib cdp->cdp_flags |= CDP_UNREF_DTR; 1195277391Skib dev_refl(dev); 1196277179Shselasky devfs_destroy(dev); 1197277179Shselasky LIST_FOREACH(child, &dev->si_children, si_siblings) 1198277179Shselasky delist_dev_locked(child); 1199280345Shselasky dev_unlock(); 1200280345Shselasky /* ensure the destroy event is queued in order */ 1201280345Shselasky notify_destroy(dev); 1202280345Shselasky dev_lock(); 1203277179Shselasky} 1204277179Shselasky 1205280345Shselasky/* 1206280345Shselasky * This function will delist a character device and its children from 1207280345Shselasky * the directory listing and create a destroy event without waiting 1208280345Shselasky * for all character device references to go away. At some later point 1209280345Shselasky * destroy_dev() must be called to complete the character device 1210280345Shselasky * destruction. After calling this function the character device name 1211280345Shselasky * can instantly be re-used. 1212280345Shselasky */ 1213126082Sphkvoid 1214277179Shselaskydelist_dev(struct cdev *dev) 1215277179Shselasky{ 1216277391Skib 1217280345Shselasky WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "delist_dev"); 1218277179Shselasky dev_lock(); 1219277179Shselasky delist_dev_locked(dev); 1220277179Shselasky dev_unlock(); 1221277179Shselasky} 1222277179Shselasky 1223277179Shselaskyvoid 1224130585Sphkdestroy_dev(struct cdev *dev) 1225126082Sphk{ 1226126082Sphk 1227185373Skib WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "destroy_dev"); 1228135600Sphk dev_lock(); 1229171251Skib destroy_devl(dev); 1230171251Skib dev_unlock_and_free(); 1231126082Sphk} 1232126082Sphk 123351225Sbdeconst char * 1234130585Sphkdevtoname(struct cdev *dev) 123549982Sbillf{ 123649982Sbillf 123749982Sbillf return (dev->si_name); 123849982Sbillf} 123965374Sphk 124065374Sphkint 124191998Sphkdev_stdclone(char *name, char **namep, const char *stem, int *unit) 124265374Sphk{ 124365374Sphk int u, i; 124465374Sphk 124575519Sbrian i = strlen(stem); 124675519Sbrian if (bcmp(stem, name, i) != 0) 124765374Sphk return (0); 124865374Sphk if (!isdigit(name[i])) 124965374Sphk return (0); 125065374Sphk u = 0; 125186461Sphk if (name[i] == '0' && isdigit(name[i+1])) 125286461Sphk return (0); 125365374Sphk while (isdigit(name[i])) { 125465374Sphk u *= 10; 125565374Sphk u += name[i++] - '0'; 125665374Sphk } 1257104523Sgreen if (u > 0xffffff) 1258104523Sgreen return (0); 125965374Sphk *unit = u; 126065374Sphk if (namep) 126165374Sphk *namep = &name[i]; 126265374Sphk if (name[i]) 126365374Sphk return (2); 126465374Sphk return (1); 126565374Sphk} 126665632Sphk 126765632Sphk/* 1268126077Sphk * Helper functions for cloning device drivers. 1269126077Sphk * 1270126077Sphk * The objective here is to make it unnecessary for the device drivers to 1271126077Sphk * use rman or similar to manage their unit number space. Due to the way 1272126077Sphk * we do "on-demand" devices, using rman or other "private" methods 1273126077Sphk * will be very tricky to lock down properly once we lock down this file. 1274126077Sphk * 1275130936Sle * Instead we give the drivers these routines which puts the struct cdev *'s 1276130936Sle * that are to be managed on their own list, and gives the driver the ability 1277126077Sphk * to ask for the first free unit number or a given specified unit number. 1278126077Sphk * 1279126077Sphk * In addition these routines support paired devices (pty, nmdm and similar) 1280126077Sphk * by respecting a number of "flag" bits in the minor number. 1281126077Sphk * 1282126077Sphk */ 1283126077Sphk 1284126077Sphkstruct clonedevs { 1285126077Sphk LIST_HEAD(,cdev) head; 1286126077Sphk}; 1287126077Sphk 1288126845Sphkvoid 1289126845Sphkclone_setup(struct clonedevs **cdp) 1290126845Sphk{ 1291126845Sphk 1292126845Sphk *cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO); 1293126845Sphk LIST_INIT(&(*cdp)->head); 1294126845Sphk} 1295126845Sphk 1296126077Sphkint 1297204412Skibclone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, 1298204412Skib struct cdev **dp, int extra) 1299126077Sphk{ 1300126077Sphk struct clonedevs *cd; 1301140733Sphk struct cdev *dev, *ndev, *dl, *de; 1302293346Skib struct make_dev_args args; 1303126077Sphk int unit, low, u; 1304126077Sphk 1305126845Sphk KASSERT(*cdp != NULL, 1306126845Sphk ("clone_setup() not called in driver \"%s\"", csw->d_name)); 1307126077Sphk KASSERT(!(extra & CLONE_UNITMASK), 1308126845Sphk ("Illegal extra bits (0x%x) in clone_create", extra)); 1309126077Sphk KASSERT(*up <= CLONE_UNITMASK, 1310126845Sphk ("Too high unit (0x%x) in clone_create", *up)); 1311179726Sed KASSERT(csw->d_flags & D_NEEDMINOR, 1312179726Sed ("clone_create() on cdevsw without minor numbers")); 1313126077Sphk 1314126077Sphk 1315126077Sphk /* 1316126077Sphk * Search the list for a lot of things in one go: 1317126077Sphk * A preexisting match is returned immediately. 1318126077Sphk * The lowest free unit number if we are passed -1, and the place 1319126077Sphk * in the list where we should insert that new element. 1320126077Sphk * The place to insert a specified unit number, if applicable 1321126077Sphk * the end of the list. 1322126077Sphk */ 1323126077Sphk unit = *up; 1324207729Skib ndev = devfs_alloc(MAKEDEV_WAITOK); 1325140733Sphk dev_lock(); 1326207729Skib prep_cdevsw(csw, MAKEDEV_WAITOK); 1327126849Sphk low = extra; 1328126077Sphk de = dl = NULL; 1329126845Sphk cd = *cdp; 1330126077Sphk LIST_FOREACH(dev, &cd->head, si_clone) { 1331140733Sphk KASSERT(dev->si_flags & SI_CLONELIST, 1332140733Sphk ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); 1333126077Sphk u = dev2unit(dev); 1334126077Sphk if (u == (unit | extra)) { 1335126077Sphk *dp = dev; 1336170950Skib dev_unlock(); 1337150342Sphk devfs_free(ndev); 1338126077Sphk return (0); 1339126077Sphk } 1340126077Sphk if (unit == -1 && u == low) { 1341126077Sphk low++; 1342126077Sphk de = dev; 1343126077Sphk continue; 1344150793Sphk } else if (u < (unit | extra)) { 1345150793Sphk de = dev; 1346150793Sphk continue; 1347150793Sphk } else if (u > (unit | extra)) { 1348126077Sphk dl = dev; 1349126077Sphk break; 1350126077Sphk } 1351126077Sphk } 1352126077Sphk if (unit == -1) 1353126849Sphk unit = low & CLONE_UNITMASK; 1354293346Skib make_dev_args_init(&args); 1355293346Skib args.mda_unit = unit | extra; 1356293346Skib args.mda_devsw = csw; 1357293346Skib dev = newdev(&args, ndev); 1358140733Sphk if (dev->si_flags & SI_CLONELIST) { 1359140733Sphk printf("dev %p (%s) is on clonelist\n", dev, dev->si_name); 1360150793Sphk printf("unit=%d, low=%d, extra=0x%x\n", unit, low, extra); 1361140733Sphk LIST_FOREACH(dev, &cd->head, si_clone) { 1362140733Sphk printf("\t%p %s\n", dev, dev->si_name); 1363140733Sphk } 1364140733Sphk panic("foo"); 1365140733Sphk } 1366126077Sphk KASSERT(!(dev->si_flags & SI_CLONELIST), 1367140733Sphk ("Dev %p(%s) should not be on clonelist", dev, dev->si_name)); 1368126077Sphk if (dl != NULL) 1369126077Sphk LIST_INSERT_BEFORE(dl, dev, si_clone); 1370126077Sphk else if (de != NULL) 1371126077Sphk LIST_INSERT_AFTER(de, dev, si_clone); 1372126077Sphk else 1373126077Sphk LIST_INSERT_HEAD(&cd->head, dev, si_clone); 1374126077Sphk dev->si_flags |= SI_CLONELIST; 1375126077Sphk *up = unit; 1376170950Skib dev_unlock_and_free(); 1377126077Sphk return (1); 1378126077Sphk} 1379126077Sphk 1380126077Sphk/* 1381126077Sphk * Kill everything still on the list. The driver should already have 1382130585Sphk * disposed of any softc hung of the struct cdev *'s at this time. 1383126077Sphk */ 1384126077Sphkvoid 1385126077Sphkclone_cleanup(struct clonedevs **cdp) 1386126077Sphk{ 1387171181Skib struct cdev *dev; 1388171181Skib struct cdev_priv *cp; 1389126077Sphk struct clonedevs *cd; 1390126077Sphk 1391126077Sphk cd = *cdp; 1392126077Sphk if (cd == NULL) 1393126077Sphk return; 1394140733Sphk dev_lock(); 1395171181Skib while (!LIST_EMPTY(&cd->head)) { 1396171181Skib dev = LIST_FIRST(&cd->head); 1397171181Skib LIST_REMOVE(dev, si_clone); 1398140733Sphk KASSERT(dev->si_flags & SI_CLONELIST, 1399140733Sphk ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); 1400171181Skib dev->si_flags &= ~SI_CLONELIST; 1401179828Skib cp = cdev2priv(dev); 1402171181Skib if (!(cp->cdp_flags & CDP_SCHED_DTR)) { 1403171181Skib cp->cdp_flags |= CDP_SCHED_DTR; 1404171181Skib KASSERT(dev->si_flags & SI_NAMED, 1405275856Sgleb ("Driver has goofed in cloning underways udev %jx unit %x", 1406275856Sgleb (uintmax_t)dev2udev(dev), dev2unit(dev))); 1407171181Skib destroy_devl(dev); 1408171181Skib } 1409126077Sphk } 1410177301Skib dev_unlock_and_free(); 1411126077Sphk free(cd, M_DEVBUF); 1412126077Sphk *cdp = NULL; 1413126077Sphk} 1414171181Skib 1415171181Skibstatic TAILQ_HEAD(, cdev_priv) dev_ddtr = 1416171181Skib TAILQ_HEAD_INITIALIZER(dev_ddtr); 1417228804Sjhbstatic struct task dev_dtr_task = TASK_INITIALIZER(0, destroy_dev_tq, NULL); 1418171181Skib 1419171181Skibstatic void 1420171181Skibdestroy_dev_tq(void *ctx, int pending) 1421171181Skib{ 1422171181Skib struct cdev_priv *cp; 1423171181Skib struct cdev *dev; 1424171181Skib void (*cb)(void *); 1425171181Skib void *cb_arg; 1426171181Skib 1427171181Skib dev_lock(); 1428171181Skib while (!TAILQ_EMPTY(&dev_ddtr)) { 1429171181Skib cp = TAILQ_FIRST(&dev_ddtr); 1430171181Skib dev = &cp->cdp_c; 1431171181Skib KASSERT(cp->cdp_flags & CDP_SCHED_DTR, 1432171181Skib ("cdev %p in dev_destroy_tq without CDP_SCHED_DTR", cp)); 1433171181Skib TAILQ_REMOVE(&dev_ddtr, cp, cdp_dtr_list); 1434171181Skib cb = cp->cdp_dtr_cb; 1435171181Skib cb_arg = cp->cdp_dtr_cb_arg; 1436171181Skib destroy_devl(dev); 1437177301Skib dev_unlock_and_free(); 1438171181Skib dev_rel(dev); 1439171181Skib if (cb != NULL) 1440171181Skib cb(cb_arg); 1441171181Skib dev_lock(); 1442171181Skib } 1443171181Skib dev_unlock(); 1444171181Skib} 1445171181Skib 1446171188Skib/* 1447171188Skib * devmtx shall be locked on entry. devmtx will be unlocked after 1448171188Skib * function return. 1449171188Skib */ 1450171188Skibstatic int 1451171188Skibdestroy_dev_sched_cbl(struct cdev *dev, void (*cb)(void *), void *arg) 1452171181Skib{ 1453171181Skib struct cdev_priv *cp; 1454171188Skib 1455171188Skib mtx_assert(&devmtx, MA_OWNED); 1456179828Skib cp = cdev2priv(dev); 1457171181Skib if (cp->cdp_flags & CDP_SCHED_DTR) { 1458171181Skib dev_unlock(); 1459171181Skib return (0); 1460171181Skib } 1461171181Skib dev_refl(dev); 1462171181Skib cp->cdp_flags |= CDP_SCHED_DTR; 1463171181Skib cp->cdp_dtr_cb = cb; 1464171181Skib cp->cdp_dtr_cb_arg = arg; 1465171181Skib TAILQ_INSERT_TAIL(&dev_ddtr, cp, cdp_dtr_list); 1466171181Skib dev_unlock(); 1467171181Skib taskqueue_enqueue(taskqueue_swi_giant, &dev_dtr_task); 1468171181Skib return (1); 1469171181Skib} 1470171181Skib 1471171181Skibint 1472171188Skibdestroy_dev_sched_cb(struct cdev *dev, void (*cb)(void *), void *arg) 1473171188Skib{ 1474204412Skib 1475171188Skib dev_lock(); 1476171188Skib return (destroy_dev_sched_cbl(dev, cb, arg)); 1477171188Skib} 1478171188Skib 1479171188Skibint 1480171181Skibdestroy_dev_sched(struct cdev *dev) 1481171181Skib{ 1482204412Skib 1483171181Skib return (destroy_dev_sched_cb(dev, NULL, NULL)); 1484171181Skib} 1485171181Skib 1486171181Skibvoid 1487171181Skibdestroy_dev_drain(struct cdevsw *csw) 1488171181Skib{ 1489171181Skib 1490171181Skib dev_lock(); 1491171181Skib while (!LIST_EMPTY(&csw->d_devs)) { 1492171181Skib msleep(&csw->d_devs, &devmtx, PRIBIO, "devscd", hz/10); 1493171181Skib } 1494171181Skib dev_unlock(); 1495171181Skib} 1496171181Skib 1497171181Skibvoid 1498171181Skibdrain_dev_clone_events(void) 1499171181Skib{ 1500171181Skib 1501171181Skib sx_xlock(&clone_drain_lock); 1502171181Skib sx_xunlock(&clone_drain_lock); 1503171181Skib} 1504171181Skib 1505210924Skib#include "opt_ddb.h" 1506210924Skib#ifdef DDB 1507210924Skib#include <sys/kernel.h> 1508210924Skib 1509210924Skib#include <ddb/ddb.h> 1510210924Skib 1511210924SkibDB_SHOW_COMMAND(cdev, db_show_cdev) 1512210924Skib{ 1513210924Skib struct cdev_priv *cdp; 1514210924Skib struct cdev *dev; 1515210924Skib u_int flags; 1516210924Skib char buf[512]; 1517210924Skib 1518210924Skib if (!have_addr) { 1519210924Skib TAILQ_FOREACH(cdp, &cdevp_list, cdp_list) { 1520210924Skib dev = &cdp->cdp_c; 1521210924Skib db_printf("%s %p\n", dev->si_name, dev); 1522210924Skib if (db_pager_quit) 1523210924Skib break; 1524210924Skib } 1525210924Skib return; 1526210924Skib } 1527210924Skib 1528210924Skib dev = (struct cdev *)addr; 1529210924Skib cdp = cdev2priv(dev); 1530210924Skib db_printf("dev %s ref %d use %ld thr %ld inuse %u fdpriv %p\n", 1531210924Skib dev->si_name, dev->si_refcount, dev->si_usecount, 1532210924Skib dev->si_threadcount, cdp->cdp_inuse, cdp->cdp_fdpriv.lh_first); 1533210924Skib db_printf("devsw %p si_drv0 %d si_drv1 %p si_drv2 %p\n", 1534210924Skib dev->si_devsw, dev->si_drv0, dev->si_drv1, dev->si_drv2); 1535210924Skib flags = dev->si_flags; 1536210924Skib#define SI_FLAG(flag) do { \ 1537210924Skib if (flags & (flag)) { \ 1538210924Skib if (buf[0] != '\0') \ 1539210924Skib strlcat(buf, ", ", sizeof(buf)); \ 1540210924Skib strlcat(buf, (#flag) + 3, sizeof(buf)); \ 1541210924Skib flags &= ~(flag); \ 1542210924Skib } \ 1543210924Skib} while (0) 1544210924Skib buf[0] = '\0'; 1545210924Skib SI_FLAG(SI_ETERNAL); 1546210924Skib SI_FLAG(SI_ALIAS); 1547210924Skib SI_FLAG(SI_NAMED); 1548210924Skib SI_FLAG(SI_CHILD); 1549210924Skib SI_FLAG(SI_DUMPDEV); 1550210924Skib SI_FLAG(SI_CLONELIST); 1551210924Skib db_printf("si_flags %s\n", buf); 1552210924Skib 1553210924Skib flags = cdp->cdp_flags; 1554210924Skib#define CDP_FLAG(flag) do { \ 1555210924Skib if (flags & (flag)) { \ 1556210924Skib if (buf[0] != '\0') \ 1557210924Skib strlcat(buf, ", ", sizeof(buf)); \ 1558210924Skib strlcat(buf, (#flag) + 4, sizeof(buf)); \ 1559210924Skib flags &= ~(flag); \ 1560210924Skib } \ 1561210924Skib} while (0) 1562210924Skib buf[0] = '\0'; 1563210924Skib CDP_FLAG(CDP_ACTIVE); 1564210924Skib CDP_FLAG(CDP_SCHED_DTR); 1565210924Skib db_printf("cdp_flags %s\n", buf); 1566210924Skib} 1567210924Skib#endif 1568