126453f35SJulian Elischer /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 38a36da99SPedro F. Giffuni * 466cdbc28SPoul-Henning Kamp * Copyright (c) 1999-2002 Poul-Henning Kamp 526453f35SJulian Elischer * All rights reserved. 626453f35SJulian Elischer * 726453f35SJulian Elischer * Redistribution and use in source and binary forms, with or without 826453f35SJulian Elischer * modification, are permitted provided that the following conditions 926453f35SJulian Elischer * are met: 1026453f35SJulian Elischer * 1. Redistributions of source code must retain the above copyright 1126453f35SJulian Elischer * notice, this list of conditions and the following disclaimer. 1226453f35SJulian Elischer * 2. Redistributions in binary form must reproduce the above copyright 1326453f35SJulian Elischer * notice, this list of conditions and the following disclaimer in the 1426453f35SJulian Elischer * documentation and/or other materials provided with the distribution. 1526453f35SJulian Elischer * 1666cdbc28SPoul-Henning Kamp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1766cdbc28SPoul-Henning Kamp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1826453f35SJulian Elischer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1966cdbc28SPoul-Henning Kamp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2026453f35SJulian Elischer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2126453f35SJulian Elischer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2226453f35SJulian Elischer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2326453f35SJulian Elischer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2426453f35SJulian Elischer * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2526453f35SJulian Elischer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2626453f35SJulian Elischer * SUCH DAMAGE. 2726453f35SJulian Elischer */ 2826453f35SJulian Elischer 2926453f35SJulian Elischer #include <sys/param.h> 30698bfad7SPoul-Henning Kamp #include <sys/kernel.h> 31f8a760b3SJulian Elischer #include <sys/systm.h> 3202574b19SPoul-Henning Kamp #include <sys/bio.h> 33773e541eSWarner Losh #include <sys/devctl.h> 341b567820SBrian Feldman #include <sys/lock.h> 351b567820SBrian Feldman #include <sys/mutex.h> 36ecbb00a2SDoug Rabson #include <sys/module.h> 37698bfad7SPoul-Henning Kamp #include <sys/malloc.h> 3826453f35SJulian Elischer #include <sys/conf.h> 391dfcbb0cSJulian Elischer #include <sys/vnode.h> 40698bfad7SPoul-Henning Kamp #include <sys/queue.h> 41b2941431SPoul-Henning Kamp #include <sys/poll.h> 42de10ffa5SKonstantin Belousov #include <sys/sx.h> 43db901281SPoul-Henning Kamp #include <sys/ctype.h> 44d26dd2d9SRobert Watson #include <sys/ucred.h> 45de10ffa5SKonstantin Belousov #include <sys/taskqueue.h> 460ef1c826SPoul-Henning Kamp #include <machine/stdarg.h> 471dfcbb0cSJulian Elischer 489c0af131SPoul-Henning Kamp #include <fs/devfs/devfs_int.h> 4964345f0bSJohn Baldwin #include <vm/vm.h> 509c0af131SPoul-Henning Kamp 519ef295b7SPoul-Henning Kamp static MALLOC_DEFINE(M_DEVT, "cdev", "cdev storage"); 52698bfad7SPoul-Henning Kamp 53e606a3c6SPoul-Henning Kamp struct mtx devmtx; 54aa2f6ddcSPoul-Henning Kamp static void destroy_devl(struct cdev *dev); 559d53363bSKonstantin Belousov static int destroy_dev_sched_cbl(struct cdev *dev, 569d53363bSKonstantin Belousov void (*cb)(void *), void *arg); 57268e76d8SJohn Baldwin static void destroy_dev_tq(void *ctx, int pending); 58d139909dSJohn Baldwin static void destroy_dev_tq_giant(void *ctx, int pending); 59f1bb758dSKonstantin Belousov static int make_dev_credv(int flags, struct cdev **dres, struct cdevsw *devsw, 60f1bb758dSKonstantin Belousov int unit, struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, 61d26dd2d9SRobert Watson va_list ap); 62f3732fd1SPoul-Henning Kamp 639bc911d4SKonstantin Belousov static struct cdev_priv_list cdevp_free_list = 649bc911d4SKonstantin Belousov TAILQ_HEAD_INITIALIZER(cdevp_free_list); 65aeeb4202SKonstantin Belousov static SLIST_HEAD(free_cdevsw, cdevsw) cdevsw_gt_post_list = 6613e403fdSAntoine Brodin SLIST_HEAD_INITIALIZER(cdevsw_gt_post_list); 679bc911d4SKonstantin Belousov 68a0e78d2eSPoul-Henning Kamp void 69a0e78d2eSPoul-Henning Kamp dev_lock(void) 70cd690b60SPoul-Henning Kamp { 718c4b6380SJohn Baldwin 72cd690b60SPoul-Henning Kamp mtx_lock(&devmtx); 73cd690b60SPoul-Henning Kamp } 74cd690b60SPoul-Henning Kamp 75aeeb4202SKonstantin Belousov /* 76aeeb4202SKonstantin Belousov * Free all the memory collected while the cdev mutex was 77aeeb4202SKonstantin Belousov * locked. Since devmtx is after the system map mutex, free() cannot 78aeeb4202SKonstantin Belousov * be called immediately and is postponed until cdev mutex can be 79aeeb4202SKonstantin Belousov * dropped. 80aeeb4202SKonstantin Belousov */ 819bc911d4SKonstantin Belousov static void 829bc911d4SKonstantin Belousov dev_unlock_and_free(void) 839bc911d4SKonstantin Belousov { 84aeeb4202SKonstantin Belousov struct cdev_priv_list cdp_free; 85aeeb4202SKonstantin Belousov struct free_cdevsw csw_free; 869bc911d4SKonstantin Belousov struct cdev_priv *cdp; 87aeeb4202SKonstantin Belousov struct cdevsw *csw; 889bc911d4SKonstantin Belousov 890ac9e27bSConrad Meyer dev_lock_assert_locked(); 90aeeb4202SKonstantin Belousov 91aeeb4202SKonstantin Belousov /* 92aeeb4202SKonstantin Belousov * Make the local copy of the list heads while the dev_mtx is 93aeeb4202SKonstantin Belousov * held. Free it later. 94aeeb4202SKonstantin Belousov */ 95aeeb4202SKonstantin Belousov TAILQ_INIT(&cdp_free); 96aeeb4202SKonstantin Belousov TAILQ_CONCAT(&cdp_free, &cdevp_free_list, cdp_list); 97aeeb4202SKonstantin Belousov csw_free = cdevsw_gt_post_list; 98aeeb4202SKonstantin Belousov SLIST_INIT(&cdevsw_gt_post_list); 99aeeb4202SKonstantin Belousov 1009bc911d4SKonstantin Belousov mtx_unlock(&devmtx); 101aeeb4202SKonstantin Belousov 102aeeb4202SKonstantin Belousov while ((cdp = TAILQ_FIRST(&cdp_free)) != NULL) { 103aeeb4202SKonstantin Belousov TAILQ_REMOVE(&cdp_free, cdp, cdp_list); 1049bc911d4SKonstantin Belousov devfs_free(&cdp->cdp_c); 1059bc911d4SKonstantin Belousov } 106aeeb4202SKonstantin Belousov while ((csw = SLIST_FIRST(&csw_free)) != NULL) { 107aeeb4202SKonstantin Belousov SLIST_REMOVE_HEAD(&csw_free, d_postfree_list); 108aeeb4202SKonstantin Belousov free(csw, M_DEVT); 109aeeb4202SKonstantin Belousov } 1109bc911d4SKonstantin Belousov } 1119bc911d4SKonstantin Belousov 1129bc911d4SKonstantin Belousov static void 1139bc911d4SKonstantin Belousov dev_free_devlocked(struct cdev *cdev) 1149bc911d4SKonstantin Belousov { 1159bc911d4SKonstantin Belousov struct cdev_priv *cdp; 1169bc911d4SKonstantin Belousov 1170ac9e27bSConrad Meyer dev_lock_assert_locked(); 11805427aafSKonstantin Belousov cdp = cdev2priv(cdev); 1193b50dff5SKonstantin Belousov KASSERT((cdp->cdp_flags & CDP_UNREF_DTR) == 0, 1203b50dff5SKonstantin Belousov ("destroy_dev() was not called after delist_dev(%p)", cdev)); 12167864268SJason A. Harmening KASSERT((cdp->cdp_flags & CDP_ON_ACTIVE_LIST) == 0, 12267864268SJason A. Harmening ("%s: cdp %p (%s) on active list", __func__, cdp, cdev->si_name)); 1239bc911d4SKonstantin Belousov TAILQ_INSERT_HEAD(&cdevp_free_list, cdp, cdp_list); 1249bc911d4SKonstantin Belousov } 1259bc911d4SKonstantin Belousov 126aeeb4202SKonstantin Belousov static void 127aeeb4202SKonstantin Belousov cdevsw_free_devlocked(struct cdevsw *csw) 128aeeb4202SKonstantin Belousov { 129aeeb4202SKonstantin Belousov 1300ac9e27bSConrad Meyer dev_lock_assert_locked(); 131aeeb4202SKonstantin Belousov SLIST_INSERT_HEAD(&cdevsw_gt_post_list, csw, d_postfree_list); 132aeeb4202SKonstantin Belousov } 133aeeb4202SKonstantin Belousov 134a0e78d2eSPoul-Henning Kamp void 135a0e78d2eSPoul-Henning Kamp dev_unlock(void) 136cd690b60SPoul-Henning Kamp { 1372c15afd8SPoul-Henning Kamp 138cd690b60SPoul-Henning Kamp mtx_unlock(&devmtx); 139cd690b60SPoul-Henning Kamp } 140cd690b60SPoul-Henning Kamp 141cd690b60SPoul-Henning Kamp void 1429477d73eSPoul-Henning Kamp dev_ref(struct cdev *dev) 1439477d73eSPoul-Henning Kamp { 1449477d73eSPoul-Henning Kamp 1450ac9e27bSConrad Meyer dev_lock_assert_unlocked(); 1469477d73eSPoul-Henning Kamp mtx_lock(&devmtx); 1479477d73eSPoul-Henning Kamp dev->si_refcount++; 1489477d73eSPoul-Henning Kamp mtx_unlock(&devmtx); 1499477d73eSPoul-Henning Kamp } 1509477d73eSPoul-Henning Kamp 1519477d73eSPoul-Henning Kamp void 152eb151cb9SPoul-Henning Kamp dev_refl(struct cdev *dev) 153cd690b60SPoul-Henning Kamp { 1542c15afd8SPoul-Henning Kamp 1550ac9e27bSConrad Meyer dev_lock_assert_locked(); 156cd690b60SPoul-Henning Kamp dev->si_refcount++; 157cd690b60SPoul-Henning Kamp } 158cd690b60SPoul-Henning Kamp 159cd690b60SPoul-Henning Kamp void 160aa2f6ddcSPoul-Henning Kamp dev_rel(struct cdev *dev) 161cd690b60SPoul-Henning Kamp { 162aa2f6ddcSPoul-Henning Kamp int flag = 0; 163a0e78d2eSPoul-Henning Kamp 1640ac9e27bSConrad Meyer dev_lock_assert_unlocked(); 165ba285125SPoul-Henning Kamp dev_lock(); 166cd690b60SPoul-Henning Kamp dev->si_refcount--; 167cd690b60SPoul-Henning Kamp KASSERT(dev->si_refcount >= 0, 168cd690b60SPoul-Henning Kamp ("dev_rel(%s) gave negative count", devtoname(dev))); 169cd690b60SPoul-Henning Kamp if (dev->si_devsw == NULL && dev->si_refcount == 0) { 170cd690b60SPoul-Henning Kamp LIST_REMOVE(dev, si_list); 171ba285125SPoul-Henning Kamp flag = 1; 172ba285125SPoul-Henning Kamp } 173ba285125SPoul-Henning Kamp dev_unlock(); 174ba285125SPoul-Henning Kamp if (flag) 175e606a3c6SPoul-Henning Kamp devfs_free(dev); 176cd690b60SPoul-Henning Kamp } 177ba285125SPoul-Henning Kamp 1782c15afd8SPoul-Henning Kamp struct cdevsw * 1793979450bSKonstantin Belousov dev_refthread(struct cdev *dev, int *ref) 1802c15afd8SPoul-Henning Kamp { 1812c15afd8SPoul-Henning Kamp struct cdevsw *csw; 182de10ffa5SKonstantin Belousov struct cdev_priv *cdp; 1832c15afd8SPoul-Henning Kamp 1840ac9e27bSConrad Meyer dev_lock_assert_unlocked(); 1853979450bSKonstantin Belousov if ((dev->si_flags & SI_ETERNAL) != 0) { 1863979450bSKonstantin Belousov *ref = 0; 1873979450bSKonstantin Belousov return (dev->si_devsw); 1883979450bSKonstantin Belousov } 189a02cab33SMateusz Guzik cdp = cdev2priv(dev); 190a02cab33SMateusz Guzik mtx_lock(&cdp->cdp_threadlock); 1912c15afd8SPoul-Henning Kamp csw = dev->si_devsw; 192de10ffa5SKonstantin Belousov if (csw != NULL) { 193de10ffa5SKonstantin Belousov if ((cdp->cdp_flags & CDP_SCHED_DTR) == 0) 1949e8bd2acSAlexander Motin atomic_add_long(&dev->si_threadcount, 1); 195de10ffa5SKonstantin Belousov else 196de10ffa5SKonstantin Belousov csw = NULL; 197de10ffa5SKonstantin Belousov } 198a02cab33SMateusz Guzik mtx_unlock(&cdp->cdp_threadlock); 199ec86f8b2SConrad Meyer if (csw != NULL) 2003979450bSKonstantin Belousov *ref = 1; 2012c15afd8SPoul-Henning Kamp return (csw); 2022c15afd8SPoul-Henning Kamp } 2032c15afd8SPoul-Henning Kamp 2041663075cSKonstantin Belousov struct cdevsw * 2053979450bSKonstantin Belousov devvn_refthread(struct vnode *vp, struct cdev **devp, int *ref) 2061663075cSKonstantin Belousov { 2071663075cSKonstantin Belousov struct cdevsw *csw; 208de10ffa5SKonstantin Belousov struct cdev_priv *cdp; 2093979450bSKonstantin Belousov struct cdev *dev; 2101663075cSKonstantin Belousov 2110ac9e27bSConrad Meyer dev_lock_assert_unlocked(); 2123979450bSKonstantin Belousov if ((vp->v_vflag & VV_ETERNALDEV) != 0) { 2133979450bSKonstantin Belousov dev = vp->v_rdev; 2143979450bSKonstantin Belousov if (dev == NULL) 2153979450bSKonstantin Belousov return (NULL); 2163979450bSKonstantin Belousov KASSERT((dev->si_flags & SI_ETERNAL) != 0, 2173979450bSKonstantin Belousov ("Not eternal cdev")); 2183979450bSKonstantin Belousov *ref = 0; 2193979450bSKonstantin Belousov csw = dev->si_devsw; 2203979450bSKonstantin Belousov KASSERT(csw != NULL, ("Eternal cdev is destroyed")); 2213979450bSKonstantin Belousov *devp = dev; 2223979450bSKonstantin Belousov return (csw); 2233979450bSKonstantin Belousov } 2243979450bSKonstantin Belousov 2251663075cSKonstantin Belousov csw = NULL; 226a02cab33SMateusz Guzik VI_LOCK(vp); 2273979450bSKonstantin Belousov dev = vp->v_rdev; 2283979450bSKonstantin Belousov if (dev == NULL) { 229a02cab33SMateusz Guzik VI_UNLOCK(vp); 2303979450bSKonstantin Belousov return (NULL); 2311663075cSKonstantin Belousov } 2323979450bSKonstantin Belousov cdp = cdev2priv(dev); 233a02cab33SMateusz Guzik mtx_lock(&cdp->cdp_threadlock); 2343979450bSKonstantin Belousov if ((cdp->cdp_flags & CDP_SCHED_DTR) == 0) { 2353979450bSKonstantin Belousov csw = dev->si_devsw; 2363979450bSKonstantin Belousov if (csw != NULL) 2379e8bd2acSAlexander Motin atomic_add_long(&dev->si_threadcount, 1); 238de10ffa5SKonstantin Belousov } 239a02cab33SMateusz Guzik mtx_unlock(&cdp->cdp_threadlock); 240a02cab33SMateusz Guzik VI_UNLOCK(vp); 2413979450bSKonstantin Belousov if (csw != NULL) { 2423979450bSKonstantin Belousov *devp = dev; 2433979450bSKonstantin Belousov *ref = 1; 2443979450bSKonstantin Belousov } 2451663075cSKonstantin Belousov return (csw); 2461663075cSKonstantin Belousov } 2471663075cSKonstantin Belousov 2482c15afd8SPoul-Henning Kamp void 2493979450bSKonstantin Belousov dev_relthread(struct cdev *dev, int ref) 2502c15afd8SPoul-Henning Kamp { 2512c15afd8SPoul-Henning Kamp 2520ac9e27bSConrad Meyer dev_lock_assert_unlocked(); 2533979450bSKonstantin Belousov if (!ref) 2543979450bSKonstantin Belousov return; 25506fe1129SKonstantin Belousov KASSERT(dev->si_threadcount > 0, 25606fe1129SKonstantin Belousov ("%s threadcount is wrong", dev->si_name)); 2579e8bd2acSAlexander Motin atomic_subtract_rel_long(&dev->si_threadcount, 1); 2582c15afd8SPoul-Henning Kamp } 259cd690b60SPoul-Henning Kamp 260b2941431SPoul-Henning Kamp int 261b2941431SPoul-Henning Kamp nullop(void) 262b2941431SPoul-Henning Kamp { 263b2941431SPoul-Henning Kamp 264b2941431SPoul-Henning Kamp return (0); 265b2941431SPoul-Henning Kamp } 266b2941431SPoul-Henning Kamp 267b2941431SPoul-Henning Kamp int 268b2941431SPoul-Henning Kamp eopnotsupp(void) 269b2941431SPoul-Henning Kamp { 270b2941431SPoul-Henning Kamp 271b2941431SPoul-Henning Kamp return (EOPNOTSUPP); 272b2941431SPoul-Henning Kamp } 27302574b19SPoul-Henning Kamp 27402574b19SPoul-Henning Kamp static int 27502574b19SPoul-Henning Kamp enxio(void) 27602574b19SPoul-Henning Kamp { 27702574b19SPoul-Henning Kamp return (ENXIO); 27802574b19SPoul-Henning Kamp } 27902574b19SPoul-Henning Kamp 280b2941431SPoul-Henning Kamp static int 281b2941431SPoul-Henning Kamp enodev(void) 282b2941431SPoul-Henning Kamp { 283b2941431SPoul-Henning Kamp return (ENODEV); 284b2941431SPoul-Henning Kamp } 285b2941431SPoul-Henning Kamp 286b2941431SPoul-Henning Kamp /* Define a dead_cdevsw for use when devices leave unexpectedly. */ 287b2941431SPoul-Henning Kamp 28802574b19SPoul-Henning Kamp #define dead_open (d_open_t *)enxio 28902574b19SPoul-Henning Kamp #define dead_close (d_close_t *)enxio 29002574b19SPoul-Henning Kamp #define dead_read (d_read_t *)enxio 29102574b19SPoul-Henning Kamp #define dead_write (d_write_t *)enxio 29202574b19SPoul-Henning Kamp #define dead_ioctl (d_ioctl_t *)enxio 293b2941431SPoul-Henning Kamp #define dead_poll (d_poll_t *)enodev 294b2941431SPoul-Henning Kamp #define dead_mmap (d_mmap_t *)enodev 29502574b19SPoul-Henning Kamp 29602574b19SPoul-Henning Kamp static void 29702574b19SPoul-Henning Kamp dead_strategy(struct bio *bp) 29802574b19SPoul-Henning Kamp { 29902574b19SPoul-Henning Kamp 30002574b19SPoul-Henning Kamp biofinish(bp, NULL, ENXIO); 30102574b19SPoul-Henning Kamp } 30202574b19SPoul-Henning Kamp 30302574b19SPoul-Henning Kamp #define dead_kqfilter (d_kqfilter_t *)enxio 30464345f0bSJohn Baldwin #define dead_mmap_single (d_mmap_single_t *)enodev 30502574b19SPoul-Henning Kamp 30602574b19SPoul-Henning Kamp static struct cdevsw dead_cdevsw = { 307dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 3087ac40f5fSPoul-Henning Kamp .d_open = dead_open, 3097ac40f5fSPoul-Henning Kamp .d_close = dead_close, 3107ac40f5fSPoul-Henning Kamp .d_read = dead_read, 3117ac40f5fSPoul-Henning Kamp .d_write = dead_write, 3127ac40f5fSPoul-Henning Kamp .d_ioctl = dead_ioctl, 3137ac40f5fSPoul-Henning Kamp .d_poll = dead_poll, 3147ac40f5fSPoul-Henning Kamp .d_mmap = dead_mmap, 3157ac40f5fSPoul-Henning Kamp .d_strategy = dead_strategy, 3167ac40f5fSPoul-Henning Kamp .d_name = "dead", 31764345f0bSJohn Baldwin .d_kqfilter = dead_kqfilter, 31864345f0bSJohn Baldwin .d_mmap_single = dead_mmap_single 31902574b19SPoul-Henning Kamp }; 32002574b19SPoul-Henning Kamp 321b2941431SPoul-Henning Kamp /* Default methods if driver does not specify method */ 322b2941431SPoul-Henning Kamp 323b2941431SPoul-Henning Kamp #define null_open (d_open_t *)nullop 324b2941431SPoul-Henning Kamp #define null_close (d_close_t *)nullop 325b2941431SPoul-Henning Kamp #define no_read (d_read_t *)enodev 326b2941431SPoul-Henning Kamp #define no_write (d_write_t *)enodev 327b2941431SPoul-Henning Kamp #define no_ioctl (d_ioctl_t *)enodev 328cfd7baceSRobert Noland #define no_mmap (d_mmap_t *)enodev 329ad3b9257SJohn-Mark Gurney #define no_kqfilter (d_kqfilter_t *)enodev 33064345f0bSJohn Baldwin #define no_mmap_single (d_mmap_single_t *)enodev 331b2941431SPoul-Henning Kamp 332b2941431SPoul-Henning Kamp static void 333b2941431SPoul-Henning Kamp no_strategy(struct bio *bp) 334b2941431SPoul-Henning Kamp { 335b2941431SPoul-Henning Kamp 336b2941431SPoul-Henning Kamp biofinish(bp, NULL, ENODEV); 337b2941431SPoul-Henning Kamp } 338b2941431SPoul-Henning Kamp 339b2941431SPoul-Henning Kamp static int 34089c9c53dSPoul-Henning Kamp no_poll(struct cdev *dev __unused, int events, struct thread *td __unused) 341b2941431SPoul-Henning Kamp { 342b2941431SPoul-Henning Kamp 343125dcf8cSKonstantin Belousov return (poll_no_poll(events)); 344b2941431SPoul-Henning Kamp } 345b2941431SPoul-Henning Kamp 346516ad423SPoul-Henning Kamp static int 347516ad423SPoul-Henning Kamp giant_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 348516ad423SPoul-Henning Kamp { 349aeeb4202SKonstantin Belousov struct cdevsw *dsw; 3503979450bSKonstantin Belousov int ref, retval; 351516ad423SPoul-Henning Kamp 3523979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 353aeeb4202SKonstantin Belousov if (dsw == NULL) 354aeeb4202SKonstantin Belousov return (ENXIO); 355516ad423SPoul-Henning Kamp mtx_lock(&Giant); 356aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_open(dev, oflags, devtype, td); 357516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 3583979450bSKonstantin Belousov dev_relthread(dev, ref); 359516ad423SPoul-Henning Kamp return (retval); 360516ad423SPoul-Henning Kamp } 361516ad423SPoul-Henning Kamp 362516ad423SPoul-Henning Kamp static int 3639e223287SKonstantin Belousov giant_fdopen(struct cdev *dev, int oflags, struct thread *td, struct file *fp) 364516ad423SPoul-Henning Kamp { 365aeeb4202SKonstantin Belousov struct cdevsw *dsw; 3663979450bSKonstantin Belousov int ref, retval; 367516ad423SPoul-Henning Kamp 3683979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 369aeeb4202SKonstantin Belousov if (dsw == NULL) 370aeeb4202SKonstantin Belousov return (ENXIO); 371516ad423SPoul-Henning Kamp mtx_lock(&Giant); 372aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_fdopen(dev, oflags, td, fp); 373516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 3743979450bSKonstantin Belousov dev_relthread(dev, ref); 375516ad423SPoul-Henning Kamp return (retval); 376516ad423SPoul-Henning Kamp } 377516ad423SPoul-Henning Kamp 378516ad423SPoul-Henning Kamp static int 379516ad423SPoul-Henning Kamp giant_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 380516ad423SPoul-Henning Kamp { 381aeeb4202SKonstantin Belousov struct cdevsw *dsw; 3823979450bSKonstantin Belousov int ref, retval; 383516ad423SPoul-Henning Kamp 3843979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 385aeeb4202SKonstantin Belousov if (dsw == NULL) 386aeeb4202SKonstantin Belousov return (ENXIO); 387516ad423SPoul-Henning Kamp mtx_lock(&Giant); 388aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_close(dev, fflag, devtype, td); 389516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 3903979450bSKonstantin Belousov dev_relthread(dev, ref); 391516ad423SPoul-Henning Kamp return (retval); 392516ad423SPoul-Henning Kamp } 393516ad423SPoul-Henning Kamp 394516ad423SPoul-Henning Kamp static void 395516ad423SPoul-Henning Kamp giant_strategy(struct bio *bp) 396516ad423SPoul-Henning Kamp { 397aeeb4202SKonstantin Belousov struct cdevsw *dsw; 398aeeb4202SKonstantin Belousov struct cdev *dev; 3993979450bSKonstantin Belousov int ref; 400516ad423SPoul-Henning Kamp 401aeeb4202SKonstantin Belousov dev = bp->bio_dev; 4023979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 403aeeb4202SKonstantin Belousov if (dsw == NULL) { 404aeeb4202SKonstantin Belousov biofinish(bp, NULL, ENXIO); 405aeeb4202SKonstantin Belousov return; 406aeeb4202SKonstantin Belousov } 407516ad423SPoul-Henning Kamp mtx_lock(&Giant); 408aeeb4202SKonstantin Belousov dsw->d_gianttrick->d_strategy(bp); 409516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4103979450bSKonstantin Belousov dev_relthread(dev, ref); 411516ad423SPoul-Henning Kamp } 412516ad423SPoul-Henning Kamp 413516ad423SPoul-Henning Kamp static int 414516ad423SPoul-Henning Kamp giant_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 415516ad423SPoul-Henning Kamp { 416aeeb4202SKonstantin Belousov struct cdevsw *dsw; 4173979450bSKonstantin Belousov int ref, retval; 418516ad423SPoul-Henning Kamp 4193979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 420aeeb4202SKonstantin Belousov if (dsw == NULL) 421aeeb4202SKonstantin Belousov return (ENXIO); 422516ad423SPoul-Henning Kamp mtx_lock(&Giant); 42335b45029SKonstantin Belousov retval = dsw->d_gianttrick->d_ioctl(dev, cmd, data, fflag, td); 424516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4253979450bSKonstantin Belousov dev_relthread(dev, ref); 426516ad423SPoul-Henning Kamp return (retval); 427516ad423SPoul-Henning Kamp } 428516ad423SPoul-Henning Kamp 429516ad423SPoul-Henning Kamp static int 430516ad423SPoul-Henning Kamp giant_read(struct cdev *dev, struct uio *uio, int ioflag) 431516ad423SPoul-Henning Kamp { 432aeeb4202SKonstantin Belousov struct cdevsw *dsw; 4333979450bSKonstantin Belousov int ref, retval; 434516ad423SPoul-Henning Kamp 4353979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 436aeeb4202SKonstantin Belousov if (dsw == NULL) 437aeeb4202SKonstantin Belousov return (ENXIO); 438516ad423SPoul-Henning Kamp mtx_lock(&Giant); 43935b45029SKonstantin Belousov retval = dsw->d_gianttrick->d_read(dev, uio, ioflag); 440516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4413979450bSKonstantin Belousov dev_relthread(dev, ref); 442516ad423SPoul-Henning Kamp return (retval); 443516ad423SPoul-Henning Kamp } 444516ad423SPoul-Henning Kamp 445516ad423SPoul-Henning Kamp static int 446516ad423SPoul-Henning Kamp giant_write(struct cdev *dev, struct uio *uio, int ioflag) 447516ad423SPoul-Henning Kamp { 448aeeb4202SKonstantin Belousov struct cdevsw *dsw; 4493979450bSKonstantin Belousov int ref, retval; 450516ad423SPoul-Henning Kamp 4513979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 452aeeb4202SKonstantin Belousov if (dsw == NULL) 453aeeb4202SKonstantin Belousov return (ENXIO); 454516ad423SPoul-Henning Kamp mtx_lock(&Giant); 455aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_write(dev, uio, ioflag); 456516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4573979450bSKonstantin Belousov dev_relthread(dev, ref); 458516ad423SPoul-Henning Kamp return (retval); 459516ad423SPoul-Henning Kamp } 460516ad423SPoul-Henning Kamp 461516ad423SPoul-Henning Kamp static int 462516ad423SPoul-Henning Kamp giant_poll(struct cdev *dev, int events, struct thread *td) 463516ad423SPoul-Henning Kamp { 464aeeb4202SKonstantin Belousov struct cdevsw *dsw; 4653979450bSKonstantin Belousov int ref, retval; 466516ad423SPoul-Henning Kamp 4673979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 468aeeb4202SKonstantin Belousov if (dsw == NULL) 469aeeb4202SKonstantin Belousov return (ENXIO); 470516ad423SPoul-Henning Kamp mtx_lock(&Giant); 471aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_poll(dev, events, td); 472516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4733979450bSKonstantin Belousov dev_relthread(dev, ref); 474516ad423SPoul-Henning Kamp return (retval); 475516ad423SPoul-Henning Kamp } 476516ad423SPoul-Henning Kamp 477516ad423SPoul-Henning Kamp static int 478516ad423SPoul-Henning Kamp giant_kqfilter(struct cdev *dev, struct knote *kn) 479516ad423SPoul-Henning Kamp { 480aeeb4202SKonstantin Belousov struct cdevsw *dsw; 4813979450bSKonstantin Belousov int ref, retval; 482516ad423SPoul-Henning Kamp 4833979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 484aeeb4202SKonstantin Belousov if (dsw == NULL) 485aeeb4202SKonstantin Belousov return (ENXIO); 486516ad423SPoul-Henning Kamp mtx_lock(&Giant); 487aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_kqfilter(dev, kn); 488516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4893979450bSKonstantin Belousov dev_relthread(dev, ref); 490516ad423SPoul-Henning Kamp return (retval); 491516ad423SPoul-Henning Kamp } 492516ad423SPoul-Henning Kamp 493516ad423SPoul-Henning Kamp static int 494cfd7baceSRobert Noland giant_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, 4952fa8c8d2SJohn Baldwin vm_memattr_t *memattr) 496516ad423SPoul-Henning Kamp { 497aeeb4202SKonstantin Belousov struct cdevsw *dsw; 4983979450bSKonstantin Belousov int ref, retval; 499516ad423SPoul-Henning Kamp 5003979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 501aeeb4202SKonstantin Belousov if (dsw == NULL) 502aeeb4202SKonstantin Belousov return (ENXIO); 503516ad423SPoul-Henning Kamp mtx_lock(&Giant); 504cfd7baceSRobert Noland retval = dsw->d_gianttrick->d_mmap(dev, offset, paddr, nprot, 5052fa8c8d2SJohn Baldwin memattr); 506516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 5073979450bSKonstantin Belousov dev_relthread(dev, ref); 508516ad423SPoul-Henning Kamp return (retval); 509516ad423SPoul-Henning Kamp } 510516ad423SPoul-Henning Kamp 51164345f0bSJohn Baldwin static int 51264345f0bSJohn Baldwin giant_mmap_single(struct cdev *dev, vm_ooffset_t *offset, vm_size_t size, 51364345f0bSJohn Baldwin vm_object_t *object, int nprot) 51464345f0bSJohn Baldwin { 51564345f0bSJohn Baldwin struct cdevsw *dsw; 5163979450bSKonstantin Belousov int ref, retval; 51764345f0bSJohn Baldwin 5183979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 51964345f0bSJohn Baldwin if (dsw == NULL) 52064345f0bSJohn Baldwin return (ENXIO); 52164345f0bSJohn Baldwin mtx_lock(&Giant); 52264345f0bSJohn Baldwin retval = dsw->d_gianttrick->d_mmap_single(dev, offset, size, object, 52364345f0bSJohn Baldwin nprot); 52464345f0bSJohn Baldwin mtx_unlock(&Giant); 5253979450bSKonstantin Belousov dev_relthread(dev, ref); 52664345f0bSJohn Baldwin return (retval); 52764345f0bSJohn Baldwin } 528516ad423SPoul-Henning Kamp 52948504cc2SKonstantin Belousov static void 530d2ba618aSKonstantin Belousov notify(struct cdev *dev, const char *ev, int flags) 53148504cc2SKonstantin Belousov { 53248504cc2SKonstantin Belousov static const char prefix[] = "cdev="; 53348504cc2SKonstantin Belousov char *data; 53476d43557SKonstantin Belousov int namelen, mflags; 53548504cc2SKonstantin Belousov 53648504cc2SKonstantin Belousov if (cold) 53748504cc2SKonstantin Belousov return; 53876d43557SKonstantin Belousov mflags = (flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK; 53948504cc2SKonstantin Belousov namelen = strlen(dev->si_name); 54076d43557SKonstantin Belousov data = malloc(namelen + sizeof(prefix), M_TEMP, mflags); 5419995e57bSAttilio Rao if (data == NULL) 5429995e57bSAttilio Rao return; 54348504cc2SKonstantin Belousov memcpy(data, prefix, sizeof(prefix) - 1); 54448504cc2SKonstantin Belousov memcpy(data + sizeof(prefix) - 1, dev->si_name, namelen + 1); 545887611b1SWarner Losh devctl_notify("DEVFS", "CDEV", ev, data); 54648504cc2SKonstantin Belousov free(data, M_TEMP); 54748504cc2SKonstantin Belousov } 54848504cc2SKonstantin Belousov 54948504cc2SKonstantin Belousov static void 550d2ba618aSKonstantin Belousov notify_create(struct cdev *dev, int flags) 55148504cc2SKonstantin Belousov { 55248504cc2SKonstantin Belousov 553d2ba618aSKonstantin Belousov notify(dev, "CREATE", flags); 55448504cc2SKonstantin Belousov } 55548504cc2SKonstantin Belousov 55648504cc2SKonstantin Belousov static void 55748504cc2SKonstantin Belousov notify_destroy(struct cdev *dev) 55848504cc2SKonstantin Belousov { 55948504cc2SKonstantin Belousov 560d2ba618aSKonstantin Belousov notify(dev, "DESTROY", MAKEDEV_WAITOK); 56148504cc2SKonstantin Belousov } 56248504cc2SKonstantin Belousov 56389c9c53dSPoul-Henning Kamp static struct cdev * 56448ce5d4cSKonstantin Belousov newdev(struct make_dev_args *args, struct cdev *si) 5653f54a085SPoul-Henning Kamp { 566027b1f71SPoul-Henning Kamp struct cdev *si2; 56748ce5d4cSKonstantin Belousov struct cdevsw *csw; 5683f54a085SPoul-Henning Kamp 5690ac9e27bSConrad Meyer dev_lock_assert_locked(); 57048ce5d4cSKonstantin Belousov csw = args->mda_devsw; 571d42fecb5SKyle Evans si2 = NULL; 57229d4cb24SEd Schouten if (csw->d_flags & D_NEEDMINOR) { 57329d4cb24SEd Schouten /* We may want to return an existing device */ 574ff7284eeSPoul-Henning Kamp LIST_FOREACH(si2, &csw->d_devs, si_list) { 57548ce5d4cSKonstantin Belousov if (dev2unit(si2) == args->mda_unit) { 5769bc911d4SKonstantin Belousov dev_free_devlocked(si); 577d42fecb5SKyle Evans si = si2; 578d42fecb5SKyle Evans break; 5793f54a085SPoul-Henning Kamp } 580027b1f71SPoul-Henning Kamp } 581d42fecb5SKyle Evans 582d42fecb5SKyle Evans /* 583d42fecb5SKyle Evans * If we're returning an existing device, we should make sure 584d42fecb5SKyle Evans * it isn't already initialized. This would have been caught 585d42fecb5SKyle Evans * in consumers anyways, but it's good to catch such a case 586d42fecb5SKyle Evans * early. We still need to complete initialization of the 587d42fecb5SKyle Evans * device, and we'll use whatever make_dev_args were passed in 588d42fecb5SKyle Evans * to do so. 589d42fecb5SKyle Evans */ 590d42fecb5SKyle Evans KASSERT(si2 == NULL || (si2->si_flags & SI_NAMED) == 0, 591d42fecb5SKyle Evans ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)", 592d42fecb5SKyle Evans args->mda_devsw->d_name, dev2unit(si2), devtoname(si2))); 59329d4cb24SEd Schouten } 59448ce5d4cSKonstantin Belousov si->si_drv0 = args->mda_unit; 59548ce5d4cSKonstantin Belousov si->si_drv1 = args->mda_si_drv1; 59648ce5d4cSKonstantin Belousov si->si_drv2 = args->mda_si_drv2; 597d42fecb5SKyle Evans /* Only push to csw->d_devs if it's not a cloned device. */ 598d42fecb5SKyle Evans if (si2 == NULL) { 599d42fecb5SKyle Evans si->si_devsw = csw; 600ff7284eeSPoul-Henning Kamp LIST_INSERT_HEAD(&csw->d_devs, si, si_list); 601d42fecb5SKyle Evans } else { 602d42fecb5SKyle Evans KASSERT(si->si_devsw == csw, 603d42fecb5SKyle Evans ("%s: inconsistent devsw between clone_create() and make_dev()", 604d42fecb5SKyle Evans __func__)); 605d42fecb5SKyle Evans } 606698bfad7SPoul-Henning Kamp return (si); 607bfbb9ce6SPoul-Henning Kamp } 608bfbb9ce6SPoul-Henning Kamp 6092a3faf2fSPoul-Henning Kamp static void 610cd690b60SPoul-Henning Kamp fini_cdevsw(struct cdevsw *devsw) 611cd690b60SPoul-Henning Kamp { 6121d45c50eSPoul-Henning Kamp struct cdevsw *gt; 613b3d82c03SPoul-Henning Kamp 6141d45c50eSPoul-Henning Kamp if (devsw->d_gianttrick != NULL) { 6151d45c50eSPoul-Henning Kamp gt = devsw->d_gianttrick; 6161d45c50eSPoul-Henning Kamp memcpy(devsw, gt, sizeof *devsw); 617aeeb4202SKonstantin Belousov cdevsw_free_devlocked(gt); 618516ad423SPoul-Henning Kamp devsw->d_gianttrick = NULL; 6191d45c50eSPoul-Henning Kamp } 620652d0472SPoul-Henning Kamp devsw->d_flags &= ~D_INIT; 621b0b03348SPoul-Henning Kamp } 622b0b03348SPoul-Henning Kamp 623d2ba618aSKonstantin Belousov static int 624d2ba618aSKonstantin Belousov prep_cdevsw(struct cdevsw *devsw, int flags) 625b0b03348SPoul-Henning Kamp { 626516ad423SPoul-Henning Kamp struct cdevsw *dsw2; 627b0b03348SPoul-Henning Kamp 6280ac9e27bSConrad Meyer dev_lock_assert_locked(); 629aeeb4202SKonstantin Belousov if (devsw->d_flags & D_INIT) 630f1bb758dSKonstantin Belousov return (0); 631aeeb4202SKonstantin Belousov if (devsw->d_flags & D_NEEDGIANT) { 632aeeb4202SKonstantin Belousov dev_unlock(); 633d2ba618aSKonstantin Belousov dsw2 = malloc(sizeof *dsw2, M_DEVT, 634d2ba618aSKonstantin Belousov (flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK); 635a0e78d2eSPoul-Henning Kamp dev_lock(); 636d2ba618aSKonstantin Belousov if (dsw2 == NULL && !(devsw->d_flags & D_INIT)) 637f1bb758dSKonstantin Belousov return (ENOMEM); 638aeeb4202SKonstantin Belousov } else 639aeeb4202SKonstantin Belousov dsw2 = NULL; 640aeeb4202SKonstantin Belousov if (devsw->d_flags & D_INIT) { 641aeeb4202SKonstantin Belousov if (dsw2 != NULL) 642aeeb4202SKonstantin Belousov cdevsw_free_devlocked(dsw2); 643f1bb758dSKonstantin Belousov return (0); 644aeeb4202SKonstantin Belousov } 645cd690b60SPoul-Henning Kamp 646e5ac3049SKonstantin Belousov if (devsw->d_version != D_VERSION_04) { 647cd690b60SPoul-Henning Kamp printf( 648cd690b60SPoul-Henning Kamp "WARNING: Device driver \"%s\" has wrong version %s\n", 6497d7e053cSAlfred Perlstein devsw->d_name == NULL ? "???" : devsw->d_name, 6507d7e053cSAlfred Perlstein "and is disabled. Recompile KLD module."); 651cd690b60SPoul-Henning Kamp devsw->d_open = dead_open; 652cd690b60SPoul-Henning Kamp devsw->d_close = dead_close; 653cd690b60SPoul-Henning Kamp devsw->d_read = dead_read; 654cd690b60SPoul-Henning Kamp devsw->d_write = dead_write; 655cd690b60SPoul-Henning Kamp devsw->d_ioctl = dead_ioctl; 656cd690b60SPoul-Henning Kamp devsw->d_poll = dead_poll; 657cd690b60SPoul-Henning Kamp devsw->d_mmap = dead_mmap; 658cfd7baceSRobert Noland devsw->d_mmap_single = dead_mmap_single; 659cd690b60SPoul-Henning Kamp devsw->d_strategy = dead_strategy; 660cd690b60SPoul-Henning Kamp devsw->d_kqfilter = dead_kqfilter; 661cd690b60SPoul-Henning Kamp } 662cd690b60SPoul-Henning Kamp 663887c753cSMark Johnston if ((devsw->d_flags & D_NEEDGIANT) != 0) { 664887c753cSMark Johnston if ((devsw->d_flags & D_GIANTOK) == 0) { 665887c753cSMark Johnston printf( 666887c753cSMark Johnston "WARNING: Device \"%s\" is Giant locked and may be " 667a8bd34bfSZhenlei Huang "deleted before FreeBSD 15.0.\n", 668a921c200SWarner Losh devsw->d_name == NULL ? "???" : devsw->d_name); 669887c753cSMark Johnston } 670516ad423SPoul-Henning Kamp if (devsw->d_gianttrick == NULL) { 671516ad423SPoul-Henning Kamp memcpy(dsw2, devsw, sizeof *dsw2); 672516ad423SPoul-Henning Kamp devsw->d_gianttrick = dsw2; 673aeeb4202SKonstantin Belousov dsw2 = NULL; 674aeeb4202SKonstantin Belousov } 675516ad423SPoul-Henning Kamp } 676516ad423SPoul-Henning Kamp 677516ad423SPoul-Henning Kamp #define FIXUP(member, noop, giant) \ 678516ad423SPoul-Henning Kamp do { \ 679516ad423SPoul-Henning Kamp if (devsw->member == NULL) { \ 680516ad423SPoul-Henning Kamp devsw->member = noop; \ 681516ad423SPoul-Henning Kamp } else if (devsw->d_flags & D_NEEDGIANT) \ 682516ad423SPoul-Henning Kamp devsw->member = giant; \ 683516ad423SPoul-Henning Kamp } \ 684516ad423SPoul-Henning Kamp while (0) 685516ad423SPoul-Henning Kamp 686516ad423SPoul-Henning Kamp FIXUP(d_open, null_open, giant_open); 687516ad423SPoul-Henning Kamp FIXUP(d_fdopen, NULL, giant_fdopen); 688516ad423SPoul-Henning Kamp FIXUP(d_close, null_close, giant_close); 689516ad423SPoul-Henning Kamp FIXUP(d_read, no_read, giant_read); 690516ad423SPoul-Henning Kamp FIXUP(d_write, no_write, giant_write); 691516ad423SPoul-Henning Kamp FIXUP(d_ioctl, no_ioctl, giant_ioctl); 692516ad423SPoul-Henning Kamp FIXUP(d_poll, no_poll, giant_poll); 693cfd7baceSRobert Noland FIXUP(d_mmap, no_mmap, giant_mmap); 694516ad423SPoul-Henning Kamp FIXUP(d_strategy, no_strategy, giant_strategy); 695516ad423SPoul-Henning Kamp FIXUP(d_kqfilter, no_kqfilter, giant_kqfilter); 69664345f0bSJohn Baldwin FIXUP(d_mmap_single, no_mmap_single, giant_mmap_single); 697516ad423SPoul-Henning Kamp 698cd690b60SPoul-Henning Kamp LIST_INIT(&devsw->d_devs); 699cd690b60SPoul-Henning Kamp 700cd690b60SPoul-Henning Kamp devsw->d_flags |= D_INIT; 701cd690b60SPoul-Henning Kamp 702aeeb4202SKonstantin Belousov if (dsw2 != NULL) 703aeeb4202SKonstantin Belousov cdevsw_free_devlocked(dsw2); 704f1bb758dSKonstantin Belousov return (0); 7052a3faf2fSPoul-Henning Kamp } 70611586717SBrian Somers 707f1bb758dSKonstantin Belousov static int 70868f7a013SJaakko Heinonen prep_devname(struct cdev *dev, const char *fmt, va_list ap) 70968f7a013SJaakko Heinonen { 71068f7a013SJaakko Heinonen int len; 71168f7a013SJaakko Heinonen char *from, *q, *s, *to; 71268f7a013SJaakko Heinonen 7130ac9e27bSConrad Meyer dev_lock_assert_locked(); 71468f7a013SJaakko Heinonen 715852b05c5SEd Schouten len = vsnrprintf(dev->si_name, sizeof(dev->si_name), 32, fmt, ap); 7168fac9b7bSEd Schouten if (len > sizeof(dev->si_name) - 1) 71768f7a013SJaakko Heinonen return (ENAMETOOLONG); 71868f7a013SJaakko Heinonen 71968f7a013SJaakko Heinonen /* Strip leading slashes. */ 7208fac9b7bSEd Schouten for (from = dev->si_name; *from == '/'; from++) 72168f7a013SJaakko Heinonen ; 72268f7a013SJaakko Heinonen 7238fac9b7bSEd Schouten for (to = dev->si_name; *from != '\0'; from++, to++) { 724b1e1f725SJaakko Heinonen /* 725b1e1f725SJaakko Heinonen * Spaces and double quotation marks cause 726b1e1f725SJaakko Heinonen * problems for the devctl(4) protocol. 727b1e1f725SJaakko Heinonen * Reject names containing those characters. 728b1e1f725SJaakko Heinonen */ 729b1e1f725SJaakko Heinonen if (isspace(*from) || *from == '"') 730b1e1f725SJaakko Heinonen return (EINVAL); 73168f7a013SJaakko Heinonen /* Treat multiple sequential slashes as single. */ 73268f7a013SJaakko Heinonen while (from[0] == '/' && from[1] == '/') 73368f7a013SJaakko Heinonen from++; 73468f7a013SJaakko Heinonen /* Trailing slash is considered invalid. */ 73568f7a013SJaakko Heinonen if (from[0] == '/' && from[1] == '\0') 73668f7a013SJaakko Heinonen return (EINVAL); 73768f7a013SJaakko Heinonen *to = *from; 73868f7a013SJaakko Heinonen } 73968f7a013SJaakko Heinonen *to = '\0'; 74068f7a013SJaakko Heinonen 7418fac9b7bSEd Schouten if (dev->si_name[0] == '\0') 74268f7a013SJaakko Heinonen return (EINVAL); 74368f7a013SJaakko Heinonen 74468f7a013SJaakko Heinonen /* Disallow "." and ".." components. */ 7458fac9b7bSEd Schouten for (s = dev->si_name;;) { 74668f7a013SJaakko Heinonen for (q = s; *q != '/' && *q != '\0'; q++) 74768f7a013SJaakko Heinonen ; 74868f7a013SJaakko Heinonen if (q - s == 1 && s[0] == '.') 74968f7a013SJaakko Heinonen return (EINVAL); 75068f7a013SJaakko Heinonen if (q - s == 2 && s[0] == '.' && s[1] == '.') 75168f7a013SJaakko Heinonen return (EINVAL); 75268f7a013SJaakko Heinonen if (*q != '/') 75368f7a013SJaakko Heinonen break; 75468f7a013SJaakko Heinonen s = q + 1; 75568f7a013SJaakko Heinonen } 75668f7a013SJaakko Heinonen 7578fac9b7bSEd Schouten if (devfs_dev_exists(dev->si_name) != 0) 75868f7a013SJaakko Heinonen return (EEXIST); 75968f7a013SJaakko Heinonen 76068f7a013SJaakko Heinonen return (0); 76168f7a013SJaakko Heinonen } 76268f7a013SJaakko Heinonen 76348ce5d4cSKonstantin Belousov void 76448ce5d4cSKonstantin Belousov make_dev_args_init_impl(struct make_dev_args *args, size_t sz) 76548ce5d4cSKonstantin Belousov { 76648ce5d4cSKonstantin Belousov 76748ce5d4cSKonstantin Belousov bzero(args, sz); 76848ce5d4cSKonstantin Belousov args->mda_size = sz; 76948ce5d4cSKonstantin Belousov } 77048ce5d4cSKonstantin Belousov 77168f7a013SJaakko Heinonen static int 77248ce5d4cSKonstantin Belousov make_dev_sv(struct make_dev_args *args1, struct cdev **dres, 77348ce5d4cSKonstantin Belousov const char *fmt, va_list ap) 7742a3faf2fSPoul-Henning Kamp { 77568f7a013SJaakko Heinonen struct cdev *dev, *dev_new; 77648ce5d4cSKonstantin Belousov struct make_dev_args args; 77768f7a013SJaakko Heinonen int res; 7782a3faf2fSPoul-Henning Kamp 77948ce5d4cSKonstantin Belousov bzero(&args, sizeof(args)); 78048ce5d4cSKonstantin Belousov if (sizeof(args) < args1->mda_size) 78148ce5d4cSKonstantin Belousov return (EINVAL); 78248ce5d4cSKonstantin Belousov bcopy(args1, &args, args1->mda_size); 78348ce5d4cSKonstantin Belousov KASSERT((args.mda_flags & MAKEDEV_WAITOK) == 0 || 78448ce5d4cSKonstantin Belousov (args.mda_flags & MAKEDEV_NOWAIT) == 0, 78548ce5d4cSKonstantin Belousov ("make_dev_sv: both WAITOK and NOWAIT specified")); 78648ce5d4cSKonstantin Belousov dev_new = devfs_alloc(args.mda_flags); 78768f7a013SJaakko Heinonen if (dev_new == NULL) 788f1bb758dSKonstantin Belousov return (ENOMEM); 789027b1f71SPoul-Henning Kamp dev_lock(); 79048ce5d4cSKonstantin Belousov res = prep_cdevsw(args.mda_devsw, args.mda_flags); 791f1bb758dSKonstantin Belousov if (res != 0) { 792d2ba618aSKonstantin Belousov dev_unlock(); 79368f7a013SJaakko Heinonen devfs_free(dev_new); 794f1bb758dSKonstantin Belousov return (res); 795d2ba618aSKonstantin Belousov } 79648ce5d4cSKonstantin Belousov dev = newdev(&args, dev_new); 797ff91cc99SJaakko Heinonen if ((dev->si_flags & SI_NAMED) == 0) { 79868f7a013SJaakko Heinonen res = prep_devname(dev, fmt, ap); 79968f7a013SJaakko Heinonen if (res != 0) { 80048ce5d4cSKonstantin Belousov if ((args.mda_flags & MAKEDEV_CHECKNAME) == 0) { 80168f7a013SJaakko Heinonen panic( 80248ce5d4cSKonstantin Belousov "make_dev_sv: bad si_name (error=%d, si_name=%s)", 80368f7a013SJaakko Heinonen res, dev->si_name); 80468f7a013SJaakko Heinonen } 80568f7a013SJaakko Heinonen if (dev == dev_new) { 80668f7a013SJaakko Heinonen LIST_REMOVE(dev, si_list); 80768f7a013SJaakko Heinonen dev_unlock(); 80868f7a013SJaakko Heinonen devfs_free(dev); 809889dffbaSKonstantin Belousov } else 810889dffbaSKonstantin Belousov dev_unlock(); 81168f7a013SJaakko Heinonen return (res); 81268f7a013SJaakko Heinonen } 813ff91cc99SJaakko Heinonen } 81448ce5d4cSKonstantin Belousov if ((args.mda_flags & MAKEDEV_REF) != 0) 815de10ffa5SKonstantin Belousov dev_refl(dev); 81648ce5d4cSKonstantin Belousov if ((args.mda_flags & MAKEDEV_ETERNAL) != 0) 8173979450bSKonstantin Belousov dev->si_flags |= SI_ETERNAL; 818cd690b60SPoul-Henning Kamp KASSERT(!(dev->si_flags & SI_NAMED), 819ff7284eeSPoul-Henning Kamp ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)", 82048ce5d4cSKonstantin Belousov args.mda_devsw->d_name, dev2unit(dev), devtoname(dev))); 8215ef2707eSPoul-Henning Kamp dev->si_flags |= SI_NAMED; 82248ce5d4cSKonstantin Belousov if (args.mda_cr != NULL) 82348ce5d4cSKonstantin Belousov dev->si_cred = crhold(args.mda_cr); 82448ce5d4cSKonstantin Belousov dev->si_uid = args.mda_uid; 82548ce5d4cSKonstantin Belousov dev->si_gid = args.mda_gid; 82648ce5d4cSKonstantin Belousov dev->si_mode = args.mda_mode; 8271744fcd0SJulian Elischer 8289285a87eSPoul-Henning Kamp devfs_create(dev); 82909828ba9SKonstantin Belousov clean_unrhdrl(devfs_inos); 830aeeb4202SKonstantin Belousov dev_unlock_and_free(); 83148504cc2SKonstantin Belousov 83248ce5d4cSKonstantin Belousov notify_create(dev, args.mda_flags); 83348504cc2SKonstantin Belousov 834f1bb758dSKonstantin Belousov *dres = dev; 835f1bb758dSKonstantin Belousov return (0); 8363f54a085SPoul-Henning Kamp } 8373f54a085SPoul-Henning Kamp 83848ce5d4cSKonstantin Belousov int 83948ce5d4cSKonstantin Belousov make_dev_s(struct make_dev_args *args, struct cdev **dres, 84048ce5d4cSKonstantin Belousov const char *fmt, ...) 84148ce5d4cSKonstantin Belousov { 84248ce5d4cSKonstantin Belousov va_list ap; 84348ce5d4cSKonstantin Belousov int res; 84448ce5d4cSKonstantin Belousov 84548ce5d4cSKonstantin Belousov va_start(ap, fmt); 84648ce5d4cSKonstantin Belousov res = make_dev_sv(args, dres, fmt, ap); 84748ce5d4cSKonstantin Belousov va_end(ap); 84848ce5d4cSKonstantin Belousov return (res); 84948ce5d4cSKonstantin Belousov } 85048ce5d4cSKonstantin Belousov 85148ce5d4cSKonstantin Belousov static int 85248ce5d4cSKonstantin Belousov make_dev_credv(int flags, struct cdev **dres, struct cdevsw *devsw, int unit, 85348ce5d4cSKonstantin Belousov struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, 85448ce5d4cSKonstantin Belousov va_list ap) 85548ce5d4cSKonstantin Belousov { 85648ce5d4cSKonstantin Belousov struct make_dev_args args; 85748ce5d4cSKonstantin Belousov 85848ce5d4cSKonstantin Belousov make_dev_args_init(&args); 85948ce5d4cSKonstantin Belousov args.mda_flags = flags; 86048ce5d4cSKonstantin Belousov args.mda_devsw = devsw; 86148ce5d4cSKonstantin Belousov args.mda_cr = cr; 86248ce5d4cSKonstantin Belousov args.mda_uid = uid; 86348ce5d4cSKonstantin Belousov args.mda_gid = gid; 86448ce5d4cSKonstantin Belousov args.mda_mode = mode; 86548ce5d4cSKonstantin Belousov args.mda_unit = unit; 86648ce5d4cSKonstantin Belousov return (make_dev_sv(&args, dres, fmt, ap)); 86748ce5d4cSKonstantin Belousov } 86848ce5d4cSKonstantin Belousov 869d26dd2d9SRobert Watson struct cdev * 870edde8745SEd Schouten make_dev(struct cdevsw *devsw, int unit, uid_t uid, gid_t gid, int mode, 871d26dd2d9SRobert Watson const char *fmt, ...) 872d26dd2d9SRobert Watson { 873d26dd2d9SRobert Watson struct cdev *dev; 874d26dd2d9SRobert Watson va_list ap; 8756fa5abfdSMatt Macy int res __unused; 876d26dd2d9SRobert Watson 877d26dd2d9SRobert Watson va_start(ap, fmt); 8786fa5abfdSMatt Macy res = make_dev_credv(0, &dev, devsw, unit, NULL, uid, gid, mode, fmt, 8796fa5abfdSMatt Macy ap); 880d26dd2d9SRobert Watson va_end(ap); 88168f7a013SJaakko Heinonen KASSERT(res == 0 && dev != NULL, 88268f7a013SJaakko Heinonen ("make_dev: failed make_dev_credv (error=%d)", res)); 883d26dd2d9SRobert Watson return (dev); 884d26dd2d9SRobert Watson } 885d26dd2d9SRobert Watson 886d26dd2d9SRobert Watson struct cdev * 887edde8745SEd Schouten make_dev_cred(struct cdevsw *devsw, int unit, struct ucred *cr, uid_t uid, 888d26dd2d9SRobert Watson gid_t gid, int mode, const char *fmt, ...) 889d26dd2d9SRobert Watson { 890d26dd2d9SRobert Watson struct cdev *dev; 891d26dd2d9SRobert Watson va_list ap; 8926fa5abfdSMatt Macy int res __unused; 893d26dd2d9SRobert Watson 894d26dd2d9SRobert Watson va_start(ap, fmt); 8956fa5abfdSMatt Macy res = make_dev_credv(0, &dev, devsw, unit, cr, uid, gid, mode, fmt, ap); 896de10ffa5SKonstantin Belousov va_end(ap); 897de10ffa5SKonstantin Belousov 898f1bb758dSKonstantin Belousov KASSERT(res == 0 && dev != NULL, 89968f7a013SJaakko Heinonen ("make_dev_cred: failed make_dev_credv (error=%d)", res)); 900de10ffa5SKonstantin Belousov return (dev); 901de10ffa5SKonstantin Belousov } 902de10ffa5SKonstantin Belousov 903de10ffa5SKonstantin Belousov struct cdev * 904f1bb758dSKonstantin Belousov make_dev_credf(int flags, struct cdevsw *devsw, int unit, struct ucred *cr, 905f1bb758dSKonstantin Belousov uid_t uid, gid_t gid, int mode, const char *fmt, ...) 906de10ffa5SKonstantin Belousov { 907de10ffa5SKonstantin Belousov struct cdev *dev; 908de10ffa5SKonstantin Belousov va_list ap; 909f1bb758dSKonstantin Belousov int res; 910de10ffa5SKonstantin Belousov 911de10ffa5SKonstantin Belousov va_start(ap, fmt); 912f1bb758dSKonstantin Belousov res = make_dev_credv(flags, &dev, devsw, unit, cr, uid, gid, mode, 913de10ffa5SKonstantin Belousov fmt, ap); 914d26dd2d9SRobert Watson va_end(ap); 915d26dd2d9SRobert Watson 91668f7a013SJaakko Heinonen KASSERT(((flags & MAKEDEV_NOWAIT) != 0 && res == ENOMEM) || 91768f7a013SJaakko Heinonen ((flags & MAKEDEV_CHECKNAME) != 0 && res != ENOMEM) || res == 0, 91868f7a013SJaakko Heinonen ("make_dev_credf: failed make_dev_credv (error=%d)", res)); 919f1bb758dSKonstantin Belousov return (res == 0 ? dev : NULL); 920f1bb758dSKonstantin Belousov } 921f1bb758dSKonstantin Belousov 922f1bb758dSKonstantin Belousov int 9232e983aceSEd Schouten make_dev_p(int flags, struct cdev **cdev, struct cdevsw *devsw, 924f1bb758dSKonstantin Belousov struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, ...) 925f1bb758dSKonstantin Belousov { 926f1bb758dSKonstantin Belousov va_list ap; 927f1bb758dSKonstantin Belousov int res; 928f1bb758dSKonstantin Belousov 929f1bb758dSKonstantin Belousov va_start(ap, fmt); 9302e983aceSEd Schouten res = make_dev_credv(flags, cdev, devsw, 0, cr, uid, gid, mode, 931f1bb758dSKonstantin Belousov fmt, ap); 932f1bb758dSKonstantin Belousov va_end(ap); 933f1bb758dSKonstantin Belousov 93468f7a013SJaakko Heinonen KASSERT(((flags & MAKEDEV_NOWAIT) != 0 && res == ENOMEM) || 93568f7a013SJaakko Heinonen ((flags & MAKEDEV_CHECKNAME) != 0 && res != ENOMEM) || res == 0, 93668f7a013SJaakko Heinonen ("make_dev_p: failed make_dev_credv (error=%d)", res)); 937f1bb758dSKonstantin Belousov return (res); 938d26dd2d9SRobert Watson } 939d26dd2d9SRobert Watson 940e606a3c6SPoul-Henning Kamp static void 941e606a3c6SPoul-Henning Kamp dev_dependsl(struct cdev *pdev, struct cdev *cdev) 942e606a3c6SPoul-Henning Kamp { 943e606a3c6SPoul-Henning Kamp 944e606a3c6SPoul-Henning Kamp cdev->si_parent = pdev; 945e606a3c6SPoul-Henning Kamp cdev->si_flags |= SI_CHILD; 946e606a3c6SPoul-Henning Kamp LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings); 947e606a3c6SPoul-Henning Kamp } 948e606a3c6SPoul-Henning Kamp 9493344c5a1SPoul-Henning Kamp void 95089c9c53dSPoul-Henning Kamp dev_depends(struct cdev *pdev, struct cdev *cdev) 9513344c5a1SPoul-Henning Kamp { 9523344c5a1SPoul-Henning Kamp 953a0e78d2eSPoul-Henning Kamp dev_lock(); 954e606a3c6SPoul-Henning Kamp dev_dependsl(pdev, cdev); 955a0e78d2eSPoul-Henning Kamp dev_unlock(); 9563344c5a1SPoul-Henning Kamp } 9573344c5a1SPoul-Henning Kamp 958b50a7799SAndrey V. Elsukov static int 959b50a7799SAndrey V. Elsukov make_dev_alias_v(int flags, struct cdev **cdev, struct cdev *pdev, 960b50a7799SAndrey V. Elsukov const char *fmt, va_list ap) 9613f54a085SPoul-Henning Kamp { 96289c9c53dSPoul-Henning Kamp struct cdev *dev; 96368f7a013SJaakko Heinonen int error; 9643f54a085SPoul-Henning Kamp 965b50a7799SAndrey V. Elsukov KASSERT(pdev != NULL, ("make_dev_alias_v: pdev is NULL")); 966b50a7799SAndrey V. Elsukov KASSERT((flags & MAKEDEV_WAITOK) == 0 || (flags & MAKEDEV_NOWAIT) == 0, 967b50a7799SAndrey V. Elsukov ("make_dev_alias_v: both WAITOK and NOWAIT specified")); 968b50a7799SAndrey V. Elsukov KASSERT((flags & ~(MAKEDEV_WAITOK | MAKEDEV_NOWAIT | 969b50a7799SAndrey V. Elsukov MAKEDEV_CHECKNAME)) == 0, 970b50a7799SAndrey V. Elsukov ("make_dev_alias_v: invalid flags specified (flags=%02x)", flags)); 971b50a7799SAndrey V. Elsukov 972b50a7799SAndrey V. Elsukov dev = devfs_alloc(flags); 973b50a7799SAndrey V. Elsukov if (dev == NULL) 974b50a7799SAndrey V. Elsukov return (ENOMEM); 975a0e78d2eSPoul-Henning Kamp dev_lock(); 9763f54a085SPoul-Henning Kamp dev->si_flags |= SI_ALIAS; 97768f7a013SJaakko Heinonen error = prep_devname(dev, fmt, ap); 97868f7a013SJaakko Heinonen if (error != 0) { 979b50a7799SAndrey V. Elsukov if ((flags & MAKEDEV_CHECKNAME) == 0) { 980b50a7799SAndrey V. Elsukov panic("make_dev_alias_v: bad si_name " 981b50a7799SAndrey V. Elsukov "(error=%d, si_name=%s)", error, dev->si_name); 982b50a7799SAndrey V. Elsukov } 983b50a7799SAndrey V. Elsukov dev_unlock(); 984b50a7799SAndrey V. Elsukov devfs_free(dev); 985b50a7799SAndrey V. Elsukov return (error); 98668f7a013SJaakko Heinonen } 98768f7a013SJaakko Heinonen dev->si_flags |= SI_NAMED; 9889285a87eSPoul-Henning Kamp devfs_create(dev); 989ae95dc62SKonstantin Belousov dev_dependsl(pdev, dev); 99009828ba9SKonstantin Belousov clean_unrhdrl(devfs_inos); 991a0e78d2eSPoul-Henning Kamp dev_unlock(); 99248504cc2SKonstantin Belousov 993b50a7799SAndrey V. Elsukov notify_create(dev, flags); 994b50a7799SAndrey V. Elsukov *cdev = dev; 99548504cc2SKonstantin Belousov 996b50a7799SAndrey V. Elsukov return (0); 997b50a7799SAndrey V. Elsukov } 998b50a7799SAndrey V. Elsukov 999b50a7799SAndrey V. Elsukov struct cdev * 1000b50a7799SAndrey V. Elsukov make_dev_alias(struct cdev *pdev, const char *fmt, ...) 1001b50a7799SAndrey V. Elsukov { 1002b50a7799SAndrey V. Elsukov struct cdev *dev; 1003b50a7799SAndrey V. Elsukov va_list ap; 10046fa5abfdSMatt Macy int res __unused; 10056fa5abfdSMatt Macy 1006b50a7799SAndrey V. Elsukov va_start(ap, fmt); 10076fa5abfdSMatt Macy res = make_dev_alias_v(MAKEDEV_WAITOK, &dev, pdev, fmt, ap); 1008b50a7799SAndrey V. Elsukov va_end(ap); 1009b50a7799SAndrey V. Elsukov 1010b50a7799SAndrey V. Elsukov KASSERT(res == 0 && dev != NULL, 1011b50a7799SAndrey V. Elsukov ("make_dev_alias: failed make_dev_alias_v (error=%d)", res)); 10120ef1c826SPoul-Henning Kamp return (dev); 10130ef1c826SPoul-Henning Kamp } 10140ef1c826SPoul-Henning Kamp 1015b50a7799SAndrey V. Elsukov int 1016b50a7799SAndrey V. Elsukov make_dev_alias_p(int flags, struct cdev **cdev, struct cdev *pdev, 1017b50a7799SAndrey V. Elsukov const char *fmt, ...) 1018b50a7799SAndrey V. Elsukov { 1019b50a7799SAndrey V. Elsukov va_list ap; 1020b50a7799SAndrey V. Elsukov int res; 1021b50a7799SAndrey V. Elsukov 1022b50a7799SAndrey V. Elsukov va_start(ap, fmt); 1023b50a7799SAndrey V. Elsukov res = make_dev_alias_v(flags, cdev, pdev, fmt, ap); 1024b50a7799SAndrey V. Elsukov va_end(ap); 1025b50a7799SAndrey V. Elsukov return (res); 1026b50a7799SAndrey V. Elsukov } 1027b50a7799SAndrey V. Elsukov 1028aa76615dSJustin T. Gibbs int 1029aa76615dSJustin T. Gibbs make_dev_physpath_alias(int flags, struct cdev **cdev, struct cdev *pdev, 1030aa76615dSJustin T. Gibbs struct cdev *old_alias, const char *physpath) 1031aa76615dSJustin T. Gibbs { 1032aa76615dSJustin T. Gibbs char *devfspath; 1033aa76615dSJustin T. Gibbs int physpath_len; 1034aa76615dSJustin T. Gibbs int max_parentpath_len; 1035aa76615dSJustin T. Gibbs int parentpath_len; 1036aa76615dSJustin T. Gibbs int devfspathbuf_len; 1037aa76615dSJustin T. Gibbs int mflags; 1038aa76615dSJustin T. Gibbs int ret; 1039aa76615dSJustin T. Gibbs 1040aa76615dSJustin T. Gibbs *cdev = NULL; 1041aa76615dSJustin T. Gibbs devfspath = NULL; 1042aa76615dSJustin T. Gibbs physpath_len = strlen(physpath); 1043aa76615dSJustin T. Gibbs ret = EINVAL; 1044aa76615dSJustin T. Gibbs if (physpath_len == 0) 1045aa76615dSJustin T. Gibbs goto out; 1046aa76615dSJustin T. Gibbs 1047aa76615dSJustin T. Gibbs if (strncmp("id1,", physpath, 4) == 0) { 1048aa76615dSJustin T. Gibbs physpath += 4; 1049aa76615dSJustin T. Gibbs physpath_len -= 4; 1050aa76615dSJustin T. Gibbs if (physpath_len == 0) 1051aa76615dSJustin T. Gibbs goto out; 1052aa76615dSJustin T. Gibbs } 1053aa76615dSJustin T. Gibbs 1054aa76615dSJustin T. Gibbs max_parentpath_len = SPECNAMELEN - physpath_len - /*/*/1; 1055aa76615dSJustin T. Gibbs parentpath_len = strlen(pdev->si_name); 1056aa76615dSJustin T. Gibbs if (max_parentpath_len < parentpath_len) { 105720654f4eSAlexander Motin if (bootverbose) 105820654f4eSAlexander Motin printf("WARNING: Unable to alias %s " 1059aa76615dSJustin T. Gibbs "to %s/%s - path too long\n", 1060aa76615dSJustin T. Gibbs pdev->si_name, physpath, pdev->si_name); 1061aa76615dSJustin T. Gibbs ret = ENAMETOOLONG; 1062aa76615dSJustin T. Gibbs goto out; 1063aa76615dSJustin T. Gibbs } 1064aa76615dSJustin T. Gibbs 1065aa76615dSJustin T. Gibbs mflags = (flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK; 1066aa76615dSJustin T. Gibbs devfspathbuf_len = physpath_len + /*/*/1 + parentpath_len + /*NUL*/1; 1067aa76615dSJustin T. Gibbs devfspath = malloc(devfspathbuf_len, M_DEVBUF, mflags); 1068aa76615dSJustin T. Gibbs if (devfspath == NULL) { 1069aa76615dSJustin T. Gibbs ret = ENOMEM; 1070aa76615dSJustin T. Gibbs goto out; 1071aa76615dSJustin T. Gibbs } 1072aa76615dSJustin T. Gibbs 1073aa76615dSJustin T. Gibbs sprintf(devfspath, "%s/%s", physpath, pdev->si_name); 10744d651f4eSKonstantin Belousov if (old_alias != NULL && strcmp(old_alias->si_name, devfspath) == 0) { 1075aa76615dSJustin T. Gibbs /* Retain the existing alias. */ 1076aa76615dSJustin T. Gibbs *cdev = old_alias; 1077aa76615dSJustin T. Gibbs old_alias = NULL; 1078aa76615dSJustin T. Gibbs ret = 0; 1079aa76615dSJustin T. Gibbs } else { 1080f403cfb1SKonstantin Belousov ret = make_dev_alias_p(flags, cdev, pdev, "%s", devfspath); 1081aa76615dSJustin T. Gibbs } 1082aa76615dSJustin T. Gibbs out: 1083aa76615dSJustin T. Gibbs if (old_alias != NULL) 1084aa76615dSJustin T. Gibbs destroy_dev(old_alias); 1085aa76615dSJustin T. Gibbs if (devfspath != NULL) 1086aa76615dSJustin T. Gibbs free(devfspath, M_DEVBUF); 1087aa76615dSJustin T. Gibbs return (ret); 1088aa76615dSJustin T. Gibbs } 1089aa76615dSJustin T. Gibbs 1090cd690b60SPoul-Henning Kamp static void 1091aa2f6ddcSPoul-Henning Kamp destroy_devl(struct cdev *dev) 1092d137acccSPoul-Henning Kamp { 1093743cd76aSPoul-Henning Kamp struct cdevsw *csw; 10940bad52e1SHans Petter Selasky struct cdev_privdata *p; 10953b50dff5SKonstantin Belousov struct cdev_priv *cdp; 1096743cd76aSPoul-Henning Kamp 10970ac9e27bSConrad Meyer dev_lock_assert_locked(); 1098743cd76aSPoul-Henning Kamp KASSERT(dev->si_flags & SI_NAMED, 10996bfa9a2dSEd Schouten ("WARNING: Driver mistake: destroy_dev on %d\n", dev2unit(dev))); 11003979450bSKonstantin Belousov KASSERT((dev->si_flags & SI_ETERNAL) == 0, 11013979450bSKonstantin Belousov ("WARNING: Driver mistake: destroy_dev on eternal %d\n", 11023979450bSKonstantin Belousov dev2unit(dev))); 11035ef2707eSPoul-Henning Kamp 11043b50dff5SKonstantin Belousov cdp = cdev2priv(dev); 11053b50dff5SKonstantin Belousov if ((cdp->cdp_flags & CDP_UNREF_DTR) == 0) { 11063b50dff5SKonstantin Belousov /* 11073b50dff5SKonstantin Belousov * Avoid race with dev_rel(), e.g. from the populate 11083b50dff5SKonstantin Belousov * loop. If CDP_UNREF_DTR flag is set, the reference 11093b50dff5SKonstantin Belousov * to be dropped at the end of destroy_devl() was 11103b50dff5SKonstantin Belousov * already taken by delist_dev_locked(). 11113b50dff5SKonstantin Belousov */ 11123b50dff5SKonstantin Belousov dev_refl(dev); 11133b50dff5SKonstantin Belousov 11149285a87eSPoul-Henning Kamp devfs_destroy(dev); 11153b50dff5SKonstantin Belousov } 1116cd690b60SPoul-Henning Kamp 1117cd690b60SPoul-Henning Kamp /* Remove name marking */ 1118b0b03348SPoul-Henning Kamp dev->si_flags &= ~SI_NAMED; 1119b0b03348SPoul-Henning Kamp 1120cd690b60SPoul-Henning Kamp /* If we are a child, remove us from the parents list */ 11213344c5a1SPoul-Henning Kamp if (dev->si_flags & SI_CHILD) { 11223344c5a1SPoul-Henning Kamp LIST_REMOVE(dev, si_siblings); 11233344c5a1SPoul-Henning Kamp dev->si_flags &= ~SI_CHILD; 11243344c5a1SPoul-Henning Kamp } 1125cd690b60SPoul-Henning Kamp 1126cd690b60SPoul-Henning Kamp /* Kill our children */ 11273344c5a1SPoul-Henning Kamp while (!LIST_EMPTY(&dev->si_children)) 1128aa2f6ddcSPoul-Henning Kamp destroy_devl(LIST_FIRST(&dev->si_children)); 1129cd690b60SPoul-Henning Kamp 1130cd690b60SPoul-Henning Kamp /* Remove from clone list */ 1131b0b03348SPoul-Henning Kamp if (dev->si_flags & SI_CLONELIST) { 1132b0b03348SPoul-Henning Kamp LIST_REMOVE(dev, si_clone); 1133b0b03348SPoul-Henning Kamp dev->si_flags &= ~SI_CLONELIST; 1134b0b03348SPoul-Henning Kamp } 1135cd690b60SPoul-Henning Kamp 1136a02cab33SMateusz Guzik mtx_lock(&cdp->cdp_threadlock); 1137743cd76aSPoul-Henning Kamp csw = dev->si_devsw; 11381abf2c36SBrian Feldman dev->si_devsw = NULL; /* already NULL for SI_ALIAS */ 11391abf2c36SBrian Feldman while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) { 1140743cd76aSPoul-Henning Kamp csw->d_purge(dev); 1141a02cab33SMateusz Guzik mtx_unlock(&cdp->cdp_threadlock); 1142743cd76aSPoul-Henning Kamp msleep(csw, &devmtx, PRIBIO, "devprg", hz/10); 1143a02cab33SMateusz Guzik mtx_lock(&cdp->cdp_threadlock); 1144d595182fSPoul-Henning Kamp if (dev->si_threadcount) 1145d595182fSPoul-Henning Kamp printf("Still %lu threads in %s\n", 1146d595182fSPoul-Henning Kamp dev->si_threadcount, devtoname(dev)); 1147743cd76aSPoul-Henning Kamp } 1148e0c33ad5STor Egge while (dev->si_threadcount != 0) { 1149e0c33ad5STor Egge /* Use unique dummy wait ident */ 1150a02cab33SMateusz Guzik mtx_unlock(&cdp->cdp_threadlock); 1151e0c33ad5STor Egge msleep(&csw, &devmtx, PRIBIO, "devdrn", hz / 10); 1152a02cab33SMateusz Guzik mtx_lock(&cdp->cdp_threadlock); 1153e0c33ad5STor Egge } 1154743cd76aSPoul-Henning Kamp 1155a02cab33SMateusz Guzik mtx_unlock(&cdp->cdp_threadlock); 115682f4d640SKonstantin Belousov dev_unlock(); 11572793ea13SHans Petter Selasky if ((cdp->cdp_flags & CDP_UNREF_DTR) == 0) { 11582793ea13SHans Petter Selasky /* avoid out of order notify events */ 115948504cc2SKonstantin Belousov notify_destroy(dev); 11602793ea13SHans Petter Selasky } 116182f4d640SKonstantin Belousov mtx_lock(&cdevpriv_mtx); 11622793ea13SHans Petter Selasky while ((p = LIST_FIRST(&cdp->cdp_fdpriv)) != NULL) { 116382f4d640SKonstantin Belousov devfs_destroy_cdevpriv(p); 116482f4d640SKonstantin Belousov mtx_lock(&cdevpriv_mtx); 116582f4d640SKonstantin Belousov } 116682f4d640SKonstantin Belousov mtx_unlock(&cdevpriv_mtx); 116782f4d640SKonstantin Belousov dev_lock(); 116848504cc2SKonstantin Belousov 1169743cd76aSPoul-Henning Kamp dev->si_drv1 = 0; 1170743cd76aSPoul-Henning Kamp dev->si_drv2 = 0; 1171743cd76aSPoul-Henning Kamp 1172cd690b60SPoul-Henning Kamp if (!(dev->si_flags & SI_ALIAS)) { 1173cd690b60SPoul-Henning Kamp /* Remove from cdevsw list */ 1174cd690b60SPoul-Henning Kamp LIST_REMOVE(dev, si_list); 1175cd690b60SPoul-Henning Kamp 1176e606a3c6SPoul-Henning Kamp /* If cdevsw has no more struct cdev *'s, clean it */ 1177de10ffa5SKonstantin Belousov if (LIST_EMPTY(&csw->d_devs)) { 1178a5993c33SPoul-Henning Kamp fini_cdevsw(csw); 1179de10ffa5SKonstantin Belousov wakeup(&csw->d_devs); 1180de10ffa5SKonstantin Belousov } 1181cd690b60SPoul-Henning Kamp } 11825ef2707eSPoul-Henning Kamp dev->si_flags &= ~SI_ALIAS; 11833b50dff5SKonstantin Belousov cdp->cdp_flags &= ~CDP_UNREF_DTR; 11843b50dff5SKonstantin Belousov dev->si_refcount--; 1185743cd76aSPoul-Henning Kamp 11863b50dff5SKonstantin Belousov if (dev->si_refcount > 0) 1187cd690b60SPoul-Henning Kamp LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list); 11883b50dff5SKonstantin Belousov else 11899bc911d4SKonstantin Belousov dev_free_devlocked(dev); 1190d137acccSPoul-Henning Kamp } 1191cd690b60SPoul-Henning Kamp 119207dbde67SHans Petter Selasky static void 119307dbde67SHans Petter Selasky delist_dev_locked(struct cdev *dev) 119407dbde67SHans Petter Selasky { 11953b50dff5SKonstantin Belousov struct cdev_priv *cdp; 119607dbde67SHans Petter Selasky struct cdev *child; 11973b50dff5SKonstantin Belousov 11980ac9e27bSConrad Meyer dev_lock_assert_locked(); 11993b50dff5SKonstantin Belousov cdp = cdev2priv(dev); 12003b50dff5SKonstantin Belousov if ((cdp->cdp_flags & CDP_UNREF_DTR) != 0) 12013b50dff5SKonstantin Belousov return; 12023b50dff5SKonstantin Belousov cdp->cdp_flags |= CDP_UNREF_DTR; 12033b50dff5SKonstantin Belousov dev_refl(dev); 120407dbde67SHans Petter Selasky devfs_destroy(dev); 120507dbde67SHans Petter Selasky LIST_FOREACH(child, &dev->si_children, si_siblings) 120607dbde67SHans Petter Selasky delist_dev_locked(child); 12072793ea13SHans Petter Selasky dev_unlock(); 12082793ea13SHans Petter Selasky /* ensure the destroy event is queued in order */ 12092793ea13SHans Petter Selasky notify_destroy(dev); 12102793ea13SHans Petter Selasky dev_lock(); 121107dbde67SHans Petter Selasky } 121207dbde67SHans Petter Selasky 12132793ea13SHans Petter Selasky /* 12142793ea13SHans Petter Selasky * This function will delist a character device and its children from 12152793ea13SHans Petter Selasky * the directory listing and create a destroy event without waiting 12162793ea13SHans Petter Selasky * for all character device references to go away. At some later point 12172793ea13SHans Petter Selasky * destroy_dev() must be called to complete the character device 12182793ea13SHans Petter Selasky * destruction. After calling this function the character device name 12192793ea13SHans Petter Selasky * can instantly be re-used. 12202793ea13SHans Petter Selasky */ 122107dbde67SHans Petter Selasky void 122207dbde67SHans Petter Selasky delist_dev(struct cdev *dev) 122307dbde67SHans Petter Selasky { 12243b50dff5SKonstantin Belousov 12252793ea13SHans Petter Selasky WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "delist_dev"); 122607dbde67SHans Petter Selasky dev_lock(); 122707dbde67SHans Petter Selasky delist_dev_locked(dev); 122807dbde67SHans Petter Selasky dev_unlock(); 122907dbde67SHans Petter Selasky } 123007dbde67SHans Petter Selasky 1231cd690b60SPoul-Henning Kamp void 123289c9c53dSPoul-Henning Kamp destroy_dev(struct cdev *dev) 1233cd690b60SPoul-Henning Kamp { 1234cd690b60SPoul-Henning Kamp 1235b7a813fcSKonstantin Belousov WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "destroy_dev"); 1236a0e78d2eSPoul-Henning Kamp dev_lock(); 1237aa2f6ddcSPoul-Henning Kamp destroy_devl(dev); 12389bc911d4SKonstantin Belousov dev_unlock_and_free(); 1239cd690b60SPoul-Henning Kamp } 1240d137acccSPoul-Henning Kamp 1241c32cc149SBruce Evans const char * 124289c9c53dSPoul-Henning Kamp devtoname(struct cdev *dev) 1243b8e49f68SBill Fumerola { 1244b8e49f68SBill Fumerola 1245b8e49f68SBill Fumerola return (dev->si_name); 1246b8e49f68SBill Fumerola } 1247db901281SPoul-Henning Kamp 1248*e3f66987SJohn Baldwin void 1249*e3f66987SJohn Baldwin dev_copyname(struct cdev *dev, char *path, size_t len) 1250*e3f66987SJohn Baldwin { 1251*e3f66987SJohn Baldwin struct cdevsw *csw; 1252*e3f66987SJohn Baldwin int ref; 1253*e3f66987SJohn Baldwin 1254*e3f66987SJohn Baldwin csw = dev_refthread(dev, &ref); 1255*e3f66987SJohn Baldwin if (csw != NULL) { 1256*e3f66987SJohn Baldwin strlcpy(path, dev->si_name, len); 1257*e3f66987SJohn Baldwin dev_relthread(dev, ref); 1258*e3f66987SJohn Baldwin } 1259*e3f66987SJohn Baldwin } 1260*e3f66987SJohn Baldwin 1261db901281SPoul-Henning Kamp int 126201de1b13SPoul-Henning Kamp dev_stdclone(char *name, char **namep, const char *stem, int *unit) 1263db901281SPoul-Henning Kamp { 1264db901281SPoul-Henning Kamp int u, i; 1265db901281SPoul-Henning Kamp 1266db901281SPoul-Henning Kamp i = strlen(stem); 12673428b6c0SMark Johnston if (strncmp(stem, name, i) != 0) 126856700d46SBrian Somers return (0); 1269db901281SPoul-Henning Kamp if (!isdigit(name[i])) 1270db901281SPoul-Henning Kamp return (0); 1271db901281SPoul-Henning Kamp u = 0; 127210786074SPoul-Henning Kamp if (name[i] == '0' && isdigit(name[i+1])) 127310786074SPoul-Henning Kamp return (0); 1274db901281SPoul-Henning Kamp while (isdigit(name[i])) { 1275db901281SPoul-Henning Kamp u *= 10; 1276db901281SPoul-Henning Kamp u += name[i++] - '0'; 1277db901281SPoul-Henning Kamp } 1278dab3d85fSBrian Feldman if (u > 0xffffff) 1279dab3d85fSBrian Feldman return (0); 1280db901281SPoul-Henning Kamp *unit = u; 1281db901281SPoul-Henning Kamp if (namep) 1282db901281SPoul-Henning Kamp *namep = &name[i]; 1283db901281SPoul-Henning Kamp if (name[i]) 1284db901281SPoul-Henning Kamp return (2); 1285db901281SPoul-Henning Kamp return (1); 1286db901281SPoul-Henning Kamp } 12878d25eb2cSPoul-Henning Kamp 12888d25eb2cSPoul-Henning Kamp /* 1289b0b03348SPoul-Henning Kamp * Helper functions for cloning device drivers. 1290b0b03348SPoul-Henning Kamp * 1291b0b03348SPoul-Henning Kamp * The objective here is to make it unnecessary for the device drivers to 1292b0b03348SPoul-Henning Kamp * use rman or similar to manage their unit number space. Due to the way 1293b0b03348SPoul-Henning Kamp * we do "on-demand" devices, using rman or other "private" methods 1294b0b03348SPoul-Henning Kamp * will be very tricky to lock down properly once we lock down this file. 1295b0b03348SPoul-Henning Kamp * 12969a98ae94SLukas Ertl * Instead we give the drivers these routines which puts the struct cdev *'s 12979a98ae94SLukas Ertl * that are to be managed on their own list, and gives the driver the ability 1298b0b03348SPoul-Henning Kamp * to ask for the first free unit number or a given specified unit number. 1299b0b03348SPoul-Henning Kamp * 1300b0b03348SPoul-Henning Kamp * In addition these routines support paired devices (pty, nmdm and similar) 1301b0b03348SPoul-Henning Kamp * by respecting a number of "flag" bits in the minor number. 1302b0b03348SPoul-Henning Kamp * 1303b0b03348SPoul-Henning Kamp */ 1304b0b03348SPoul-Henning Kamp 1305b0b03348SPoul-Henning Kamp struct clonedevs { 1306b0b03348SPoul-Henning Kamp LIST_HEAD(,cdev) head; 1307b0b03348SPoul-Henning Kamp }; 1308b0b03348SPoul-Henning Kamp 13099397290eSPoul-Henning Kamp void 13109397290eSPoul-Henning Kamp clone_setup(struct clonedevs **cdp) 13119397290eSPoul-Henning Kamp { 13129397290eSPoul-Henning Kamp 13139397290eSPoul-Henning Kamp *cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO); 13149397290eSPoul-Henning Kamp LIST_INIT(&(*cdp)->head); 13159397290eSPoul-Henning Kamp } 13169397290eSPoul-Henning Kamp 1317b0b03348SPoul-Henning Kamp int 1318cf141467SKonstantin Belousov clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, 1319cf141467SKonstantin Belousov struct cdev **dp, int extra) 1320b0b03348SPoul-Henning Kamp { 1321b0b03348SPoul-Henning Kamp struct clonedevs *cd; 1322027b1f71SPoul-Henning Kamp struct cdev *dev, *ndev, *dl, *de; 132348ce5d4cSKonstantin Belousov struct make_dev_args args; 1324b0b03348SPoul-Henning Kamp int unit, low, u; 1325b0b03348SPoul-Henning Kamp 13269397290eSPoul-Henning Kamp KASSERT(*cdp != NULL, 13279397290eSPoul-Henning Kamp ("clone_setup() not called in driver \"%s\"", csw->d_name)); 1328b0b03348SPoul-Henning Kamp KASSERT(!(extra & CLONE_UNITMASK), 1329b0b03348SPoul-Henning Kamp ("Illegal extra bits (0x%x) in clone_create", extra)); 1330b0b03348SPoul-Henning Kamp KASSERT(*up <= CLONE_UNITMASK, 1331b0b03348SPoul-Henning Kamp ("Too high unit (0x%x) in clone_create", *up)); 133229d4cb24SEd Schouten KASSERT(csw->d_flags & D_NEEDMINOR, 133329d4cb24SEd Schouten ("clone_create() on cdevsw without minor numbers")); 1334b0b03348SPoul-Henning Kamp 1335b0b03348SPoul-Henning Kamp /* 1336b0b03348SPoul-Henning Kamp * Search the list for a lot of things in one go: 1337b0b03348SPoul-Henning Kamp * A preexisting match is returned immediately. 1338b0b03348SPoul-Henning Kamp * The lowest free unit number if we are passed -1, and the place 1339b0b03348SPoul-Henning Kamp * in the list where we should insert that new element. 1340b0b03348SPoul-Henning Kamp * The place to insert a specified unit number, if applicable 1341b0b03348SPoul-Henning Kamp * the end of the list. 1342b0b03348SPoul-Henning Kamp */ 1343b0b03348SPoul-Henning Kamp unit = *up; 1344d2ba618aSKonstantin Belousov ndev = devfs_alloc(MAKEDEV_WAITOK); 1345027b1f71SPoul-Henning Kamp dev_lock(); 1346d2ba618aSKonstantin Belousov prep_cdevsw(csw, MAKEDEV_WAITOK); 13478666b655SPoul-Henning Kamp low = extra; 1348b0b03348SPoul-Henning Kamp de = dl = NULL; 13499397290eSPoul-Henning Kamp cd = *cdp; 1350b0b03348SPoul-Henning Kamp LIST_FOREACH(dev, &cd->head, si_clone) { 1351027b1f71SPoul-Henning Kamp KASSERT(dev->si_flags & SI_CLONELIST, 1352027b1f71SPoul-Henning Kamp ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); 1353b0b03348SPoul-Henning Kamp u = dev2unit(dev); 1354b0b03348SPoul-Henning Kamp if (u == (unit | extra)) { 1355b0b03348SPoul-Henning Kamp *dp = dev; 1356027b1f71SPoul-Henning Kamp dev_unlock(); 13579bc911d4SKonstantin Belousov devfs_free(ndev); 1358b0b03348SPoul-Henning Kamp return (0); 1359b0b03348SPoul-Henning Kamp } 1360b0b03348SPoul-Henning Kamp if (unit == -1 && u == low) { 1361b0b03348SPoul-Henning Kamp low++; 1362b0b03348SPoul-Henning Kamp de = dev; 1363b0b03348SPoul-Henning Kamp continue; 13647bbb3a26SPoul-Henning Kamp } else if (u < (unit | extra)) { 13657bbb3a26SPoul-Henning Kamp de = dev; 13667bbb3a26SPoul-Henning Kamp continue; 13677bbb3a26SPoul-Henning Kamp } else if (u > (unit | extra)) { 1368b0b03348SPoul-Henning Kamp dl = dev; 1369b0b03348SPoul-Henning Kamp break; 1370b0b03348SPoul-Henning Kamp } 1371b0b03348SPoul-Henning Kamp } 1372b0b03348SPoul-Henning Kamp if (unit == -1) 13738666b655SPoul-Henning Kamp unit = low & CLONE_UNITMASK; 137448ce5d4cSKonstantin Belousov make_dev_args_init(&args); 137548ce5d4cSKonstantin Belousov args.mda_unit = unit | extra; 137648ce5d4cSKonstantin Belousov args.mda_devsw = csw; 137748ce5d4cSKonstantin Belousov dev = newdev(&args, ndev); 1378027b1f71SPoul-Henning Kamp if (dev->si_flags & SI_CLONELIST) { 1379027b1f71SPoul-Henning Kamp printf("dev %p (%s) is on clonelist\n", dev, dev->si_name); 13807bbb3a26SPoul-Henning Kamp printf("unit=%d, low=%d, extra=0x%x\n", unit, low, extra); 1381027b1f71SPoul-Henning Kamp LIST_FOREACH(dev, &cd->head, si_clone) { 1382027b1f71SPoul-Henning Kamp printf("\t%p %s\n", dev, dev->si_name); 1383027b1f71SPoul-Henning Kamp } 1384027b1f71SPoul-Henning Kamp panic("foo"); 1385027b1f71SPoul-Henning Kamp } 1386b0b03348SPoul-Henning Kamp KASSERT(!(dev->si_flags & SI_CLONELIST), 1387027b1f71SPoul-Henning Kamp ("Dev %p(%s) should not be on clonelist", dev, dev->si_name)); 1388b0b03348SPoul-Henning Kamp if (dl != NULL) 1389b0b03348SPoul-Henning Kamp LIST_INSERT_BEFORE(dl, dev, si_clone); 1390b0b03348SPoul-Henning Kamp else if (de != NULL) 1391b0b03348SPoul-Henning Kamp LIST_INSERT_AFTER(de, dev, si_clone); 1392b0b03348SPoul-Henning Kamp else 1393b0b03348SPoul-Henning Kamp LIST_INSERT_HEAD(&cd->head, dev, si_clone); 1394b0b03348SPoul-Henning Kamp dev->si_flags |= SI_CLONELIST; 1395b0b03348SPoul-Henning Kamp *up = unit; 13969bc911d4SKonstantin Belousov dev_unlock_and_free(); 1397b0b03348SPoul-Henning Kamp return (1); 1398b0b03348SPoul-Henning Kamp } 1399b0b03348SPoul-Henning Kamp 1400b0b03348SPoul-Henning Kamp /* 1401b0b03348SPoul-Henning Kamp * Kill everything still on the list. The driver should already have 140289c9c53dSPoul-Henning Kamp * disposed of any softc hung of the struct cdev *'s at this time. 1403b0b03348SPoul-Henning Kamp */ 1404b0b03348SPoul-Henning Kamp void 1405b0b03348SPoul-Henning Kamp clone_cleanup(struct clonedevs **cdp) 1406b0b03348SPoul-Henning Kamp { 1407de10ffa5SKonstantin Belousov struct cdev *dev; 1408de10ffa5SKonstantin Belousov struct cdev_priv *cp; 1409b0b03348SPoul-Henning Kamp struct clonedevs *cd; 1410b0b03348SPoul-Henning Kamp 1411b0b03348SPoul-Henning Kamp cd = *cdp; 1412b0b03348SPoul-Henning Kamp if (cd == NULL) 1413b0b03348SPoul-Henning Kamp return; 1414027b1f71SPoul-Henning Kamp dev_lock(); 1415de10ffa5SKonstantin Belousov while (!LIST_EMPTY(&cd->head)) { 1416de10ffa5SKonstantin Belousov dev = LIST_FIRST(&cd->head); 1417de10ffa5SKonstantin Belousov LIST_REMOVE(dev, si_clone); 1418027b1f71SPoul-Henning Kamp KASSERT(dev->si_flags & SI_CLONELIST, 1419027b1f71SPoul-Henning Kamp ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); 1420de10ffa5SKonstantin Belousov dev->si_flags &= ~SI_CLONELIST; 142105427aafSKonstantin Belousov cp = cdev2priv(dev); 1422de10ffa5SKonstantin Belousov if (!(cp->cdp_flags & CDP_SCHED_DTR)) { 1423de10ffa5SKonstantin Belousov cp->cdp_flags |= CDP_SCHED_DTR; 1424b0b03348SPoul-Henning Kamp KASSERT(dev->si_flags & SI_NAMED, 1425dde58752SGleb Kurtsou ("Driver has goofed in cloning underways udev %jx unit %x", 1426dde58752SGleb Kurtsou (uintmax_t)dev2udev(dev), dev2unit(dev))); 1427aa2f6ddcSPoul-Henning Kamp destroy_devl(dev); 1428b0b03348SPoul-Henning Kamp } 1429de10ffa5SKonstantin Belousov } 1430aeeb4202SKonstantin Belousov dev_unlock_and_free(); 1431b0b03348SPoul-Henning Kamp free(cd, M_DEVBUF); 1432b0b03348SPoul-Henning Kamp *cdp = NULL; 1433b0b03348SPoul-Henning Kamp } 1434de10ffa5SKonstantin Belousov 1435de10ffa5SKonstantin Belousov static TAILQ_HEAD(, cdev_priv) dev_ddtr = 1436de10ffa5SKonstantin Belousov TAILQ_HEAD_INITIALIZER(dev_ddtr); 1437d139909dSJohn Baldwin static TAILQ_HEAD(, cdev_priv) dev_ddtr_giant = 1438d139909dSJohn Baldwin TAILQ_HEAD_INITIALIZER(dev_ddtr_giant); 1439d139909dSJohn Baldwin static struct task dev_dtr_task = TASK_INITIALIZER(0, destroy_dev_tq, &dev_ddtr); 1440d139909dSJohn Baldwin static struct task dev_dtr_task_giant = TASK_INITIALIZER(0, destroy_dev_tq_giant, 1441d139909dSJohn Baldwin &dev_ddtr_giant); 1442de10ffa5SKonstantin Belousov 1443de10ffa5SKonstantin Belousov static void 1444de10ffa5SKonstantin Belousov destroy_dev_tq(void *ctx, int pending) 1445de10ffa5SKonstantin Belousov { 1446d139909dSJohn Baldwin TAILQ_HEAD(, cdev_priv) *ddtr = ctx; 1447de10ffa5SKonstantin Belousov struct cdev_priv *cp; 1448de10ffa5SKonstantin Belousov struct cdev *dev; 1449de10ffa5SKonstantin Belousov void (*cb)(void *); 1450de10ffa5SKonstantin Belousov void *cb_arg; 1451de10ffa5SKonstantin Belousov 1452de10ffa5SKonstantin Belousov dev_lock(); 1453d139909dSJohn Baldwin while (!TAILQ_EMPTY(ddtr)) { 1454d139909dSJohn Baldwin cp = TAILQ_FIRST(ddtr); 1455de10ffa5SKonstantin Belousov dev = &cp->cdp_c; 1456de10ffa5SKonstantin Belousov KASSERT(cp->cdp_flags & CDP_SCHED_DTR, 1457de10ffa5SKonstantin Belousov ("cdev %p in dev_destroy_tq without CDP_SCHED_DTR", cp)); 1458d139909dSJohn Baldwin TAILQ_REMOVE(ddtr, cp, cdp_dtr_list); 1459de10ffa5SKonstantin Belousov cb = cp->cdp_dtr_cb; 1460de10ffa5SKonstantin Belousov cb_arg = cp->cdp_dtr_cb_arg; 1461de10ffa5SKonstantin Belousov destroy_devl(dev); 1462aeeb4202SKonstantin Belousov dev_unlock_and_free(); 1463de10ffa5SKonstantin Belousov dev_rel(dev); 1464de10ffa5SKonstantin Belousov if (cb != NULL) 1465de10ffa5SKonstantin Belousov cb(cb_arg); 1466de10ffa5SKonstantin Belousov dev_lock(); 1467de10ffa5SKonstantin Belousov } 1468de10ffa5SKonstantin Belousov dev_unlock(); 1469de10ffa5SKonstantin Belousov } 1470de10ffa5SKonstantin Belousov 1471d139909dSJohn Baldwin static void 1472d139909dSJohn Baldwin destroy_dev_tq_giant(void *ctx, int pending) 1473d139909dSJohn Baldwin { 1474d139909dSJohn Baldwin mtx_lock(&Giant); 1475d139909dSJohn Baldwin destroy_dev_tq(ctx, pending); 1476d139909dSJohn Baldwin mtx_unlock(&Giant); 1477d139909dSJohn Baldwin } 1478d139909dSJohn Baldwin 14799d53363bSKonstantin Belousov /* 14809d53363bSKonstantin Belousov * devmtx shall be locked on entry. devmtx will be unlocked after 14819d53363bSKonstantin Belousov * function return. 14829d53363bSKonstantin Belousov */ 14839d53363bSKonstantin Belousov static int 14849d53363bSKonstantin Belousov destroy_dev_sched_cbl(struct cdev *dev, void (*cb)(void *), void *arg) 1485de10ffa5SKonstantin Belousov { 1486de10ffa5SKonstantin Belousov struct cdev_priv *cp; 1487d139909dSJohn Baldwin bool need_giant; 1488de10ffa5SKonstantin Belousov 14890ac9e27bSConrad Meyer dev_lock_assert_locked(); 149005427aafSKonstantin Belousov cp = cdev2priv(dev); 1491de10ffa5SKonstantin Belousov if (cp->cdp_flags & CDP_SCHED_DTR) { 1492de10ffa5SKonstantin Belousov dev_unlock(); 1493de10ffa5SKonstantin Belousov return (0); 1494de10ffa5SKonstantin Belousov } 1495de10ffa5SKonstantin Belousov dev_refl(dev); 1496de10ffa5SKonstantin Belousov cp->cdp_flags |= CDP_SCHED_DTR; 1497de10ffa5SKonstantin Belousov cp->cdp_dtr_cb = cb; 1498de10ffa5SKonstantin Belousov cp->cdp_dtr_cb_arg = arg; 1499d139909dSJohn Baldwin need_giant = (dev->si_devsw->d_flags & D_NEEDGIANT) != 0; 1500d139909dSJohn Baldwin if (need_giant) 1501d139909dSJohn Baldwin TAILQ_INSERT_TAIL(&dev_ddtr_giant, cp, cdp_dtr_list); 1502d139909dSJohn Baldwin else 1503de10ffa5SKonstantin Belousov TAILQ_INSERT_TAIL(&dev_ddtr, cp, cdp_dtr_list); 1504de10ffa5SKonstantin Belousov dev_unlock(); 1505d139909dSJohn Baldwin if (need_giant) 1506d139909dSJohn Baldwin taskqueue_enqueue(taskqueue_thread, &dev_dtr_task_giant); 1507d139909dSJohn Baldwin else 1508d139909dSJohn Baldwin taskqueue_enqueue(taskqueue_thread, &dev_dtr_task); 1509de10ffa5SKonstantin Belousov return (1); 1510de10ffa5SKonstantin Belousov } 1511de10ffa5SKonstantin Belousov 1512de10ffa5SKonstantin Belousov int 15139d53363bSKonstantin Belousov destroy_dev_sched_cb(struct cdev *dev, void (*cb)(void *), void *arg) 15149d53363bSKonstantin Belousov { 1515cf141467SKonstantin Belousov 15169d53363bSKonstantin Belousov dev_lock(); 15179d53363bSKonstantin Belousov return (destroy_dev_sched_cbl(dev, cb, arg)); 15189d53363bSKonstantin Belousov } 15199d53363bSKonstantin Belousov 15209d53363bSKonstantin Belousov int 1521de10ffa5SKonstantin Belousov destroy_dev_sched(struct cdev *dev) 1522de10ffa5SKonstantin Belousov { 1523cf141467SKonstantin Belousov 1524de10ffa5SKonstantin Belousov return (destroy_dev_sched_cb(dev, NULL, NULL)); 1525de10ffa5SKonstantin Belousov } 1526de10ffa5SKonstantin Belousov 1527de10ffa5SKonstantin Belousov void 1528de10ffa5SKonstantin Belousov destroy_dev_drain(struct cdevsw *csw) 1529de10ffa5SKonstantin Belousov { 1530de10ffa5SKonstantin Belousov 1531de10ffa5SKonstantin Belousov dev_lock(); 1532de10ffa5SKonstantin Belousov while (!LIST_EMPTY(&csw->d_devs)) { 1533de10ffa5SKonstantin Belousov msleep(&csw->d_devs, &devmtx, PRIBIO, "devscd", hz/10); 1534de10ffa5SKonstantin Belousov } 1535de10ffa5SKonstantin Belousov dev_unlock(); 1536de10ffa5SKonstantin Belousov } 1537de10ffa5SKonstantin Belousov 15386c5e633cSKonstantin Belousov #include "opt_ddb.h" 15396c5e633cSKonstantin Belousov #ifdef DDB 15406c5e633cSKonstantin Belousov #include <sys/kernel.h> 15416c5e633cSKonstantin Belousov 15426c5e633cSKonstantin Belousov #include <ddb/ddb.h> 15436c5e633cSKonstantin Belousov 15446c5e633cSKonstantin Belousov DB_SHOW_COMMAND(cdev, db_show_cdev) 15456c5e633cSKonstantin Belousov { 15466c5e633cSKonstantin Belousov struct cdev_priv *cdp; 15476c5e633cSKonstantin Belousov struct cdev *dev; 15486c5e633cSKonstantin Belousov u_int flags; 15496c5e633cSKonstantin Belousov char buf[512]; 15506c5e633cSKonstantin Belousov 15516c5e633cSKonstantin Belousov if (!have_addr) { 15526c5e633cSKonstantin Belousov TAILQ_FOREACH(cdp, &cdevp_list, cdp_list) { 15536c5e633cSKonstantin Belousov dev = &cdp->cdp_c; 15546c5e633cSKonstantin Belousov db_printf("%s %p\n", dev->si_name, dev); 15556c5e633cSKonstantin Belousov if (db_pager_quit) 15566c5e633cSKonstantin Belousov break; 15576c5e633cSKonstantin Belousov } 15586c5e633cSKonstantin Belousov return; 15596c5e633cSKonstantin Belousov } 15606c5e633cSKonstantin Belousov 15616c5e633cSKonstantin Belousov dev = (struct cdev *)addr; 15626c5e633cSKonstantin Belousov cdp = cdev2priv(dev); 15636c5e633cSKonstantin Belousov db_printf("dev %s ref %d use %ld thr %ld inuse %u fdpriv %p\n", 15646c5e633cSKonstantin Belousov dev->si_name, dev->si_refcount, dev->si_usecount, 15656c5e633cSKonstantin Belousov dev->si_threadcount, cdp->cdp_inuse, cdp->cdp_fdpriv.lh_first); 15666c5e633cSKonstantin Belousov db_printf("devsw %p si_drv0 %d si_drv1 %p si_drv2 %p\n", 15676c5e633cSKonstantin Belousov dev->si_devsw, dev->si_drv0, dev->si_drv1, dev->si_drv2); 15686c5e633cSKonstantin Belousov flags = dev->si_flags; 15696c5e633cSKonstantin Belousov #define SI_FLAG(flag) do { \ 15706c5e633cSKonstantin Belousov if (flags & (flag)) { \ 15716c5e633cSKonstantin Belousov if (buf[0] != '\0') \ 15726c5e633cSKonstantin Belousov strlcat(buf, ", ", sizeof(buf)); \ 15736c5e633cSKonstantin Belousov strlcat(buf, (#flag) + 3, sizeof(buf)); \ 15746c5e633cSKonstantin Belousov flags &= ~(flag); \ 15756c5e633cSKonstantin Belousov } \ 15766c5e633cSKonstantin Belousov } while (0) 15776c5e633cSKonstantin Belousov buf[0] = '\0'; 15786c5e633cSKonstantin Belousov SI_FLAG(SI_ETERNAL); 15796c5e633cSKonstantin Belousov SI_FLAG(SI_ALIAS); 15806c5e633cSKonstantin Belousov SI_FLAG(SI_NAMED); 15816c5e633cSKonstantin Belousov SI_FLAG(SI_CHILD); 15826c5e633cSKonstantin Belousov SI_FLAG(SI_DUMPDEV); 15836c5e633cSKonstantin Belousov SI_FLAG(SI_CLONELIST); 15846c5e633cSKonstantin Belousov db_printf("si_flags %s\n", buf); 15856c5e633cSKonstantin Belousov 15866c5e633cSKonstantin Belousov flags = cdp->cdp_flags; 15876c5e633cSKonstantin Belousov #define CDP_FLAG(flag) do { \ 15886c5e633cSKonstantin Belousov if (flags & (flag)) { \ 15896c5e633cSKonstantin Belousov if (buf[0] != '\0') \ 15906c5e633cSKonstantin Belousov strlcat(buf, ", ", sizeof(buf)); \ 15916c5e633cSKonstantin Belousov strlcat(buf, (#flag) + 4, sizeof(buf)); \ 15926c5e633cSKonstantin Belousov flags &= ~(flag); \ 15936c5e633cSKonstantin Belousov } \ 15946c5e633cSKonstantin Belousov } while (0) 15956c5e633cSKonstantin Belousov buf[0] = '\0'; 15966c5e633cSKonstantin Belousov CDP_FLAG(CDP_ACTIVE); 15976c5e633cSKonstantin Belousov CDP_FLAG(CDP_SCHED_DTR); 15986c5e633cSKonstantin Belousov db_printf("cdp_flags %s\n", buf); 15996c5e633cSKonstantin Belousov } 16006c5e633cSKonstantin Belousov #endif 1601