126453f35SJulian Elischer /*- 28a36da99SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 29677b542eSDavid E. O'Brien #include <sys/cdefs.h> 30677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$"); 31677b542eSDavid E. O'Brien 3226453f35SJulian Elischer #include <sys/param.h> 33698bfad7SPoul-Henning Kamp #include <sys/kernel.h> 34f8a760b3SJulian Elischer #include <sys/systm.h> 3502574b19SPoul-Henning Kamp #include <sys/bio.h> 36773e541eSWarner Losh #include <sys/devctl.h> 371b567820SBrian Feldman #include <sys/lock.h> 381b567820SBrian Feldman #include <sys/mutex.h> 39ecbb00a2SDoug Rabson #include <sys/module.h> 40698bfad7SPoul-Henning Kamp #include <sys/malloc.h> 4126453f35SJulian Elischer #include <sys/conf.h> 421dfcbb0cSJulian Elischer #include <sys/vnode.h> 43698bfad7SPoul-Henning Kamp #include <sys/queue.h> 44b2941431SPoul-Henning Kamp #include <sys/poll.h> 45de10ffa5SKonstantin Belousov #include <sys/sx.h> 46db901281SPoul-Henning Kamp #include <sys/ctype.h> 47d26dd2d9SRobert Watson #include <sys/ucred.h> 48de10ffa5SKonstantin Belousov #include <sys/taskqueue.h> 490ef1c826SPoul-Henning Kamp #include <machine/stdarg.h> 501dfcbb0cSJulian Elischer 519c0af131SPoul-Henning Kamp #include <fs/devfs/devfs_int.h> 5264345f0bSJohn Baldwin #include <vm/vm.h> 539c0af131SPoul-Henning Kamp 549ef295b7SPoul-Henning Kamp static MALLOC_DEFINE(M_DEVT, "cdev", "cdev storage"); 55698bfad7SPoul-Henning Kamp 56e606a3c6SPoul-Henning Kamp struct mtx devmtx; 57aa2f6ddcSPoul-Henning Kamp static void destroy_devl(struct cdev *dev); 589d53363bSKonstantin Belousov static int destroy_dev_sched_cbl(struct cdev *dev, 599d53363bSKonstantin Belousov void (*cb)(void *), void *arg); 60268e76d8SJohn Baldwin static void destroy_dev_tq(void *ctx, int pending); 61f1bb758dSKonstantin Belousov static int make_dev_credv(int flags, struct cdev **dres, struct cdevsw *devsw, 62f1bb758dSKonstantin Belousov int unit, struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, 63d26dd2d9SRobert Watson va_list ap); 64f3732fd1SPoul-Henning Kamp 659bc911d4SKonstantin Belousov static struct cdev_priv_list cdevp_free_list = 669bc911d4SKonstantin Belousov TAILQ_HEAD_INITIALIZER(cdevp_free_list); 67aeeb4202SKonstantin Belousov static SLIST_HEAD(free_cdevsw, cdevsw) cdevsw_gt_post_list = 6813e403fdSAntoine Brodin SLIST_HEAD_INITIALIZER(cdevsw_gt_post_list); 699bc911d4SKonstantin Belousov 70a0e78d2eSPoul-Henning Kamp void 71a0e78d2eSPoul-Henning Kamp dev_lock(void) 72cd690b60SPoul-Henning Kamp { 738c4b6380SJohn Baldwin 74cd690b60SPoul-Henning Kamp mtx_lock(&devmtx); 75cd690b60SPoul-Henning Kamp } 76cd690b60SPoul-Henning Kamp 77aeeb4202SKonstantin Belousov /* 78aeeb4202SKonstantin Belousov * Free all the memory collected while the cdev mutex was 79aeeb4202SKonstantin Belousov * locked. Since devmtx is after the system map mutex, free() cannot 80aeeb4202SKonstantin Belousov * be called immediately and is postponed until cdev mutex can be 81aeeb4202SKonstantin Belousov * dropped. 82aeeb4202SKonstantin Belousov */ 839bc911d4SKonstantin Belousov static void 849bc911d4SKonstantin Belousov dev_unlock_and_free(void) 859bc911d4SKonstantin Belousov { 86aeeb4202SKonstantin Belousov struct cdev_priv_list cdp_free; 87aeeb4202SKonstantin Belousov struct free_cdevsw csw_free; 889bc911d4SKonstantin Belousov struct cdev_priv *cdp; 89aeeb4202SKonstantin Belousov struct cdevsw *csw; 909bc911d4SKonstantin Belousov 910ac9e27bSConrad Meyer dev_lock_assert_locked(); 92aeeb4202SKonstantin Belousov 93aeeb4202SKonstantin Belousov /* 94aeeb4202SKonstantin Belousov * Make the local copy of the list heads while the dev_mtx is 95aeeb4202SKonstantin Belousov * held. Free it later. 96aeeb4202SKonstantin Belousov */ 97aeeb4202SKonstantin Belousov TAILQ_INIT(&cdp_free); 98aeeb4202SKonstantin Belousov TAILQ_CONCAT(&cdp_free, &cdevp_free_list, cdp_list); 99aeeb4202SKonstantin Belousov csw_free = cdevsw_gt_post_list; 100aeeb4202SKonstantin Belousov SLIST_INIT(&cdevsw_gt_post_list); 101aeeb4202SKonstantin Belousov 1029bc911d4SKonstantin Belousov mtx_unlock(&devmtx); 103aeeb4202SKonstantin Belousov 104aeeb4202SKonstantin Belousov while ((cdp = TAILQ_FIRST(&cdp_free)) != NULL) { 105aeeb4202SKonstantin Belousov TAILQ_REMOVE(&cdp_free, cdp, cdp_list); 1069bc911d4SKonstantin Belousov devfs_free(&cdp->cdp_c); 1079bc911d4SKonstantin Belousov } 108aeeb4202SKonstantin Belousov while ((csw = SLIST_FIRST(&csw_free)) != NULL) { 109aeeb4202SKonstantin Belousov SLIST_REMOVE_HEAD(&csw_free, d_postfree_list); 110aeeb4202SKonstantin Belousov free(csw, M_DEVT); 111aeeb4202SKonstantin Belousov } 1129bc911d4SKonstantin Belousov } 1139bc911d4SKonstantin Belousov 1149bc911d4SKonstantin Belousov static void 1159bc911d4SKonstantin Belousov dev_free_devlocked(struct cdev *cdev) 1169bc911d4SKonstantin Belousov { 1179bc911d4SKonstantin Belousov struct cdev_priv *cdp; 1189bc911d4SKonstantin Belousov 1190ac9e27bSConrad Meyer dev_lock_assert_locked(); 12005427aafSKonstantin Belousov cdp = cdev2priv(cdev); 1213b50dff5SKonstantin Belousov KASSERT((cdp->cdp_flags & CDP_UNREF_DTR) == 0, 1223b50dff5SKonstantin Belousov ("destroy_dev() was not called after delist_dev(%p)", cdev)); 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 3032c6b49f6SPoul-Henning Kamp #define dead_dump (dumper_t *)enxio 30402574b19SPoul-Henning Kamp #define dead_kqfilter (d_kqfilter_t *)enxio 30564345f0bSJohn Baldwin #define dead_mmap_single (d_mmap_single_t *)enodev 30602574b19SPoul-Henning Kamp 30702574b19SPoul-Henning Kamp static struct cdevsw dead_cdevsw = { 308dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 3097ac40f5fSPoul-Henning Kamp .d_open = dead_open, 3107ac40f5fSPoul-Henning Kamp .d_close = dead_close, 3117ac40f5fSPoul-Henning Kamp .d_read = dead_read, 3127ac40f5fSPoul-Henning Kamp .d_write = dead_write, 3137ac40f5fSPoul-Henning Kamp .d_ioctl = dead_ioctl, 3147ac40f5fSPoul-Henning Kamp .d_poll = dead_poll, 3157ac40f5fSPoul-Henning Kamp .d_mmap = dead_mmap, 3167ac40f5fSPoul-Henning Kamp .d_strategy = dead_strategy, 3177ac40f5fSPoul-Henning Kamp .d_name = "dead", 3187ac40f5fSPoul-Henning Kamp .d_dump = dead_dump, 31964345f0bSJohn Baldwin .d_kqfilter = dead_kqfilter, 32064345f0bSJohn Baldwin .d_mmap_single = dead_mmap_single 32102574b19SPoul-Henning Kamp }; 32202574b19SPoul-Henning Kamp 323b2941431SPoul-Henning Kamp /* Default methods if driver does not specify method */ 324b2941431SPoul-Henning Kamp 325b2941431SPoul-Henning Kamp #define null_open (d_open_t *)nullop 326b2941431SPoul-Henning Kamp #define null_close (d_close_t *)nullop 327b2941431SPoul-Henning Kamp #define no_read (d_read_t *)enodev 328b2941431SPoul-Henning Kamp #define no_write (d_write_t *)enodev 329b2941431SPoul-Henning Kamp #define no_ioctl (d_ioctl_t *)enodev 330cfd7baceSRobert Noland #define no_mmap (d_mmap_t *)enodev 331ad3b9257SJohn-Mark Gurney #define no_kqfilter (d_kqfilter_t *)enodev 33264345f0bSJohn Baldwin #define no_mmap_single (d_mmap_single_t *)enodev 333b2941431SPoul-Henning Kamp 334b2941431SPoul-Henning Kamp static void 335b2941431SPoul-Henning Kamp no_strategy(struct bio *bp) 336b2941431SPoul-Henning Kamp { 337b2941431SPoul-Henning Kamp 338b2941431SPoul-Henning Kamp biofinish(bp, NULL, ENODEV); 339b2941431SPoul-Henning Kamp } 340b2941431SPoul-Henning Kamp 341b2941431SPoul-Henning Kamp static int 34289c9c53dSPoul-Henning Kamp no_poll(struct cdev *dev __unused, int events, struct thread *td __unused) 343b2941431SPoul-Henning Kamp { 344b2941431SPoul-Henning Kamp 345125dcf8cSKonstantin Belousov return (poll_no_poll(events)); 346b2941431SPoul-Henning Kamp } 347b2941431SPoul-Henning Kamp 348b2941431SPoul-Henning Kamp #define no_dump (dumper_t *)enodev 3494e4a7663SPoul-Henning Kamp 350516ad423SPoul-Henning Kamp static int 351516ad423SPoul-Henning Kamp giant_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 352516ad423SPoul-Henning Kamp { 353aeeb4202SKonstantin Belousov struct cdevsw *dsw; 3543979450bSKonstantin Belousov int ref, retval; 355516ad423SPoul-Henning Kamp 3563979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 357aeeb4202SKonstantin Belousov if (dsw == NULL) 358aeeb4202SKonstantin Belousov return (ENXIO); 359516ad423SPoul-Henning Kamp mtx_lock(&Giant); 360aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_open(dev, oflags, devtype, td); 361516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 3623979450bSKonstantin Belousov dev_relthread(dev, ref); 363516ad423SPoul-Henning Kamp return (retval); 364516ad423SPoul-Henning Kamp } 365516ad423SPoul-Henning Kamp 366516ad423SPoul-Henning Kamp static int 3679e223287SKonstantin Belousov giant_fdopen(struct cdev *dev, int oflags, struct thread *td, struct file *fp) 368516ad423SPoul-Henning Kamp { 369aeeb4202SKonstantin Belousov struct cdevsw *dsw; 3703979450bSKonstantin Belousov int ref, retval; 371516ad423SPoul-Henning Kamp 3723979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 373aeeb4202SKonstantin Belousov if (dsw == NULL) 374aeeb4202SKonstantin Belousov return (ENXIO); 375516ad423SPoul-Henning Kamp mtx_lock(&Giant); 376aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_fdopen(dev, oflags, td, fp); 377516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 3783979450bSKonstantin Belousov dev_relthread(dev, ref); 379516ad423SPoul-Henning Kamp return (retval); 380516ad423SPoul-Henning Kamp } 381516ad423SPoul-Henning Kamp 382516ad423SPoul-Henning Kamp static int 383516ad423SPoul-Henning Kamp giant_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 384516ad423SPoul-Henning Kamp { 385aeeb4202SKonstantin Belousov struct cdevsw *dsw; 3863979450bSKonstantin Belousov int ref, retval; 387516ad423SPoul-Henning Kamp 3883979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 389aeeb4202SKonstantin Belousov if (dsw == NULL) 390aeeb4202SKonstantin Belousov return (ENXIO); 391516ad423SPoul-Henning Kamp mtx_lock(&Giant); 392aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_close(dev, fflag, devtype, td); 393516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 3943979450bSKonstantin Belousov dev_relthread(dev, ref); 395516ad423SPoul-Henning Kamp return (retval); 396516ad423SPoul-Henning Kamp } 397516ad423SPoul-Henning Kamp 398516ad423SPoul-Henning Kamp static void 399516ad423SPoul-Henning Kamp giant_strategy(struct bio *bp) 400516ad423SPoul-Henning Kamp { 401aeeb4202SKonstantin Belousov struct cdevsw *dsw; 402aeeb4202SKonstantin Belousov struct cdev *dev; 4033979450bSKonstantin Belousov int ref; 404516ad423SPoul-Henning Kamp 405aeeb4202SKonstantin Belousov dev = bp->bio_dev; 4063979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 407aeeb4202SKonstantin Belousov if (dsw == NULL) { 408aeeb4202SKonstantin Belousov biofinish(bp, NULL, ENXIO); 409aeeb4202SKonstantin Belousov return; 410aeeb4202SKonstantin Belousov } 411516ad423SPoul-Henning Kamp mtx_lock(&Giant); 412aeeb4202SKonstantin Belousov dsw->d_gianttrick->d_strategy(bp); 413516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4143979450bSKonstantin Belousov dev_relthread(dev, ref); 415516ad423SPoul-Henning Kamp } 416516ad423SPoul-Henning Kamp 417516ad423SPoul-Henning Kamp static int 418516ad423SPoul-Henning Kamp giant_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 419516ad423SPoul-Henning Kamp { 420aeeb4202SKonstantin Belousov struct cdevsw *dsw; 4213979450bSKonstantin Belousov int ref, retval; 422516ad423SPoul-Henning Kamp 4233979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 424aeeb4202SKonstantin Belousov if (dsw == NULL) 425aeeb4202SKonstantin Belousov return (ENXIO); 426516ad423SPoul-Henning Kamp mtx_lock(&Giant); 42735b45029SKonstantin Belousov retval = dsw->d_gianttrick->d_ioctl(dev, cmd, data, fflag, td); 428516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4293979450bSKonstantin Belousov dev_relthread(dev, ref); 430516ad423SPoul-Henning Kamp return (retval); 431516ad423SPoul-Henning Kamp } 432516ad423SPoul-Henning Kamp 433516ad423SPoul-Henning Kamp static int 434516ad423SPoul-Henning Kamp giant_read(struct cdev *dev, struct uio *uio, int ioflag) 435516ad423SPoul-Henning Kamp { 436aeeb4202SKonstantin Belousov struct cdevsw *dsw; 4373979450bSKonstantin Belousov int ref, retval; 438516ad423SPoul-Henning Kamp 4393979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 440aeeb4202SKonstantin Belousov if (dsw == NULL) 441aeeb4202SKonstantin Belousov return (ENXIO); 442516ad423SPoul-Henning Kamp mtx_lock(&Giant); 44335b45029SKonstantin Belousov retval = dsw->d_gianttrick->d_read(dev, uio, ioflag); 444516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4453979450bSKonstantin Belousov dev_relthread(dev, ref); 446516ad423SPoul-Henning Kamp return (retval); 447516ad423SPoul-Henning Kamp } 448516ad423SPoul-Henning Kamp 449516ad423SPoul-Henning Kamp static int 450516ad423SPoul-Henning Kamp giant_write(struct cdev *dev, struct uio *uio, int ioflag) 451516ad423SPoul-Henning Kamp { 452aeeb4202SKonstantin Belousov struct cdevsw *dsw; 4533979450bSKonstantin Belousov int ref, retval; 454516ad423SPoul-Henning Kamp 4553979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 456aeeb4202SKonstantin Belousov if (dsw == NULL) 457aeeb4202SKonstantin Belousov return (ENXIO); 458516ad423SPoul-Henning Kamp mtx_lock(&Giant); 459aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_write(dev, uio, ioflag); 460516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4613979450bSKonstantin Belousov dev_relthread(dev, ref); 462516ad423SPoul-Henning Kamp return (retval); 463516ad423SPoul-Henning Kamp } 464516ad423SPoul-Henning Kamp 465516ad423SPoul-Henning Kamp static int 466516ad423SPoul-Henning Kamp giant_poll(struct cdev *dev, int events, struct thread *td) 467516ad423SPoul-Henning Kamp { 468aeeb4202SKonstantin Belousov struct cdevsw *dsw; 4693979450bSKonstantin Belousov int ref, retval; 470516ad423SPoul-Henning Kamp 4713979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 472aeeb4202SKonstantin Belousov if (dsw == NULL) 473aeeb4202SKonstantin Belousov return (ENXIO); 474516ad423SPoul-Henning Kamp mtx_lock(&Giant); 475aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_poll(dev, events, td); 476516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4773979450bSKonstantin Belousov dev_relthread(dev, ref); 478516ad423SPoul-Henning Kamp return (retval); 479516ad423SPoul-Henning Kamp } 480516ad423SPoul-Henning Kamp 481516ad423SPoul-Henning Kamp static int 482516ad423SPoul-Henning Kamp giant_kqfilter(struct cdev *dev, struct knote *kn) 483516ad423SPoul-Henning Kamp { 484aeeb4202SKonstantin Belousov struct cdevsw *dsw; 4853979450bSKonstantin Belousov int ref, retval; 486516ad423SPoul-Henning Kamp 4873979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 488aeeb4202SKonstantin Belousov if (dsw == NULL) 489aeeb4202SKonstantin Belousov return (ENXIO); 490516ad423SPoul-Henning Kamp mtx_lock(&Giant); 491aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_kqfilter(dev, kn); 492516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4933979450bSKonstantin Belousov dev_relthread(dev, ref); 494516ad423SPoul-Henning Kamp return (retval); 495516ad423SPoul-Henning Kamp } 496516ad423SPoul-Henning Kamp 497516ad423SPoul-Henning Kamp static int 498cfd7baceSRobert Noland giant_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, 4992fa8c8d2SJohn Baldwin vm_memattr_t *memattr) 500516ad423SPoul-Henning Kamp { 501aeeb4202SKonstantin Belousov struct cdevsw *dsw; 5023979450bSKonstantin Belousov int ref, retval; 503516ad423SPoul-Henning Kamp 5043979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 505aeeb4202SKonstantin Belousov if (dsw == NULL) 506aeeb4202SKonstantin Belousov return (ENXIO); 507516ad423SPoul-Henning Kamp mtx_lock(&Giant); 508cfd7baceSRobert Noland retval = dsw->d_gianttrick->d_mmap(dev, offset, paddr, nprot, 5092fa8c8d2SJohn Baldwin memattr); 510516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 5113979450bSKonstantin Belousov dev_relthread(dev, ref); 512516ad423SPoul-Henning Kamp return (retval); 513516ad423SPoul-Henning Kamp } 514516ad423SPoul-Henning Kamp 51564345f0bSJohn Baldwin static int 51664345f0bSJohn Baldwin giant_mmap_single(struct cdev *dev, vm_ooffset_t *offset, vm_size_t size, 51764345f0bSJohn Baldwin vm_object_t *object, int nprot) 51864345f0bSJohn Baldwin { 51964345f0bSJohn Baldwin struct cdevsw *dsw; 5203979450bSKonstantin Belousov int ref, retval; 52164345f0bSJohn Baldwin 5223979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 52364345f0bSJohn Baldwin if (dsw == NULL) 52464345f0bSJohn Baldwin return (ENXIO); 52564345f0bSJohn Baldwin mtx_lock(&Giant); 52664345f0bSJohn Baldwin retval = dsw->d_gianttrick->d_mmap_single(dev, offset, size, object, 52764345f0bSJohn Baldwin nprot); 52864345f0bSJohn Baldwin mtx_unlock(&Giant); 5293979450bSKonstantin Belousov dev_relthread(dev, ref); 53064345f0bSJohn Baldwin return (retval); 53164345f0bSJohn Baldwin } 532516ad423SPoul-Henning Kamp 53348504cc2SKonstantin Belousov static void 534d2ba618aSKonstantin Belousov notify(struct cdev *dev, const char *ev, int flags) 53548504cc2SKonstantin Belousov { 53648504cc2SKonstantin Belousov static const char prefix[] = "cdev="; 53748504cc2SKonstantin Belousov char *data; 53876d43557SKonstantin Belousov int namelen, mflags; 53948504cc2SKonstantin Belousov 54048504cc2SKonstantin Belousov if (cold) 54148504cc2SKonstantin Belousov return; 54276d43557SKonstantin Belousov mflags = (flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK; 54348504cc2SKonstantin Belousov namelen = strlen(dev->si_name); 54476d43557SKonstantin Belousov data = malloc(namelen + sizeof(prefix), M_TEMP, mflags); 5459995e57bSAttilio Rao if (data == NULL) 5469995e57bSAttilio Rao return; 54748504cc2SKonstantin Belousov memcpy(data, prefix, sizeof(prefix) - 1); 54848504cc2SKonstantin Belousov memcpy(data + sizeof(prefix) - 1, dev->si_name, namelen + 1); 549*887611b1SWarner Losh devctl_notify("DEVFS", "CDEV", ev, data); 55048504cc2SKonstantin Belousov free(data, M_TEMP); 55148504cc2SKonstantin Belousov } 55248504cc2SKonstantin Belousov 55348504cc2SKonstantin Belousov static void 554d2ba618aSKonstantin Belousov notify_create(struct cdev *dev, int flags) 55548504cc2SKonstantin Belousov { 55648504cc2SKonstantin Belousov 557d2ba618aSKonstantin Belousov notify(dev, "CREATE", flags); 55848504cc2SKonstantin Belousov } 55948504cc2SKonstantin Belousov 56048504cc2SKonstantin Belousov static void 56148504cc2SKonstantin Belousov notify_destroy(struct cdev *dev) 56248504cc2SKonstantin Belousov { 56348504cc2SKonstantin Belousov 564d2ba618aSKonstantin Belousov notify(dev, "DESTROY", MAKEDEV_WAITOK); 56548504cc2SKonstantin Belousov } 56648504cc2SKonstantin Belousov 56789c9c53dSPoul-Henning Kamp static struct cdev * 56848ce5d4cSKonstantin Belousov newdev(struct make_dev_args *args, struct cdev *si) 5693f54a085SPoul-Henning Kamp { 570027b1f71SPoul-Henning Kamp struct cdev *si2; 57148ce5d4cSKonstantin Belousov struct cdevsw *csw; 5723f54a085SPoul-Henning Kamp 5730ac9e27bSConrad Meyer dev_lock_assert_locked(); 57448ce5d4cSKonstantin Belousov csw = args->mda_devsw; 575d42fecb5SKyle Evans si2 = NULL; 57629d4cb24SEd Schouten if (csw->d_flags & D_NEEDMINOR) { 57729d4cb24SEd Schouten /* We may want to return an existing device */ 578ff7284eeSPoul-Henning Kamp LIST_FOREACH(si2, &csw->d_devs, si_list) { 57948ce5d4cSKonstantin Belousov if (dev2unit(si2) == args->mda_unit) { 5809bc911d4SKonstantin Belousov dev_free_devlocked(si); 581d42fecb5SKyle Evans si = si2; 582d42fecb5SKyle Evans break; 5833f54a085SPoul-Henning Kamp } 584027b1f71SPoul-Henning Kamp } 585d42fecb5SKyle Evans 586d42fecb5SKyle Evans /* 587d42fecb5SKyle Evans * If we're returning an existing device, we should make sure 588d42fecb5SKyle Evans * it isn't already initialized. This would have been caught 589d42fecb5SKyle Evans * in consumers anyways, but it's good to catch such a case 590d42fecb5SKyle Evans * early. We still need to complete initialization of the 591d42fecb5SKyle Evans * device, and we'll use whatever make_dev_args were passed in 592d42fecb5SKyle Evans * to do so. 593d42fecb5SKyle Evans */ 594d42fecb5SKyle Evans KASSERT(si2 == NULL || (si2->si_flags & SI_NAMED) == 0, 595d42fecb5SKyle Evans ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)", 596d42fecb5SKyle Evans args->mda_devsw->d_name, dev2unit(si2), devtoname(si2))); 59729d4cb24SEd Schouten } 59848ce5d4cSKonstantin Belousov si->si_drv0 = args->mda_unit; 59948ce5d4cSKonstantin Belousov si->si_drv1 = args->mda_si_drv1; 60048ce5d4cSKonstantin Belousov si->si_drv2 = args->mda_si_drv2; 601d42fecb5SKyle Evans /* Only push to csw->d_devs if it's not a cloned device. */ 602d42fecb5SKyle Evans if (si2 == NULL) { 603d42fecb5SKyle Evans si->si_devsw = csw; 604ff7284eeSPoul-Henning Kamp LIST_INSERT_HEAD(&csw->d_devs, si, si_list); 605d42fecb5SKyle Evans } else { 606d42fecb5SKyle Evans KASSERT(si->si_devsw == csw, 607d42fecb5SKyle Evans ("%s: inconsistent devsw between clone_create() and make_dev()", 608d42fecb5SKyle Evans __func__)); 609d42fecb5SKyle Evans } 610698bfad7SPoul-Henning Kamp return (si); 611bfbb9ce6SPoul-Henning Kamp } 612bfbb9ce6SPoul-Henning Kamp 6132a3faf2fSPoul-Henning Kamp static void 614cd690b60SPoul-Henning Kamp fini_cdevsw(struct cdevsw *devsw) 615cd690b60SPoul-Henning Kamp { 6161d45c50eSPoul-Henning Kamp struct cdevsw *gt; 617b3d82c03SPoul-Henning Kamp 6181d45c50eSPoul-Henning Kamp if (devsw->d_gianttrick != NULL) { 6191d45c50eSPoul-Henning Kamp gt = devsw->d_gianttrick; 6201d45c50eSPoul-Henning Kamp memcpy(devsw, gt, sizeof *devsw); 621aeeb4202SKonstantin Belousov cdevsw_free_devlocked(gt); 622516ad423SPoul-Henning Kamp devsw->d_gianttrick = NULL; 6231d45c50eSPoul-Henning Kamp } 624652d0472SPoul-Henning Kamp devsw->d_flags &= ~D_INIT; 625b0b03348SPoul-Henning Kamp } 626b0b03348SPoul-Henning Kamp 627d2ba618aSKonstantin Belousov static int 628d2ba618aSKonstantin Belousov prep_cdevsw(struct cdevsw *devsw, int flags) 629b0b03348SPoul-Henning Kamp { 630516ad423SPoul-Henning Kamp struct cdevsw *dsw2; 631b0b03348SPoul-Henning Kamp 6320ac9e27bSConrad Meyer dev_lock_assert_locked(); 633aeeb4202SKonstantin Belousov if (devsw->d_flags & D_INIT) 634f1bb758dSKonstantin Belousov return (0); 635aeeb4202SKonstantin Belousov if (devsw->d_flags & D_NEEDGIANT) { 636aeeb4202SKonstantin Belousov dev_unlock(); 637d2ba618aSKonstantin Belousov dsw2 = malloc(sizeof *dsw2, M_DEVT, 638d2ba618aSKonstantin Belousov (flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK); 639a0e78d2eSPoul-Henning Kamp dev_lock(); 640d2ba618aSKonstantin Belousov if (dsw2 == NULL && !(devsw->d_flags & D_INIT)) 641f1bb758dSKonstantin Belousov return (ENOMEM); 642aeeb4202SKonstantin Belousov } else 643aeeb4202SKonstantin Belousov dsw2 = NULL; 644aeeb4202SKonstantin Belousov if (devsw->d_flags & D_INIT) { 645aeeb4202SKonstantin Belousov if (dsw2 != NULL) 646aeeb4202SKonstantin Belousov cdevsw_free_devlocked(dsw2); 647f1bb758dSKonstantin Belousov return (0); 648aeeb4202SKonstantin Belousov } 649cd690b60SPoul-Henning Kamp 650e5ac3049SKonstantin Belousov if (devsw->d_version != D_VERSION_04) { 651cd690b60SPoul-Henning Kamp printf( 652cd690b60SPoul-Henning Kamp "WARNING: Device driver \"%s\" has wrong version %s\n", 6537d7e053cSAlfred Perlstein devsw->d_name == NULL ? "???" : devsw->d_name, 6547d7e053cSAlfred Perlstein "and is disabled. Recompile KLD module."); 655cd690b60SPoul-Henning Kamp devsw->d_open = dead_open; 656cd690b60SPoul-Henning Kamp devsw->d_close = dead_close; 657cd690b60SPoul-Henning Kamp devsw->d_read = dead_read; 658cd690b60SPoul-Henning Kamp devsw->d_write = dead_write; 659cd690b60SPoul-Henning Kamp devsw->d_ioctl = dead_ioctl; 660cd690b60SPoul-Henning Kamp devsw->d_poll = dead_poll; 661cd690b60SPoul-Henning Kamp devsw->d_mmap = dead_mmap; 662cfd7baceSRobert Noland devsw->d_mmap_single = dead_mmap_single; 663cd690b60SPoul-Henning Kamp devsw->d_strategy = dead_strategy; 664cd690b60SPoul-Henning Kamp devsw->d_dump = dead_dump; 665cd690b60SPoul-Henning Kamp devsw->d_kqfilter = dead_kqfilter; 666cd690b60SPoul-Henning Kamp } 667cd690b60SPoul-Henning Kamp 668516ad423SPoul-Henning Kamp if (devsw->d_flags & D_NEEDGIANT) { 669a921c200SWarner Losh printf("WARNING: Device \"%s\" is Giant locked and may be " 670a921c200SWarner Losh "deleted before FreeBSD 13.0.\n", 671a921c200SWarner Losh devsw->d_name == NULL ? "???" : devsw->d_name); 672516ad423SPoul-Henning Kamp if (devsw->d_gianttrick == NULL) { 673516ad423SPoul-Henning Kamp memcpy(dsw2, devsw, sizeof *dsw2); 674516ad423SPoul-Henning Kamp devsw->d_gianttrick = dsw2; 675aeeb4202SKonstantin Belousov dsw2 = NULL; 676aeeb4202SKonstantin Belousov } 677516ad423SPoul-Henning Kamp } 678516ad423SPoul-Henning Kamp 679516ad423SPoul-Henning Kamp #define FIXUP(member, noop, giant) \ 680516ad423SPoul-Henning Kamp do { \ 681516ad423SPoul-Henning Kamp if (devsw->member == NULL) { \ 682516ad423SPoul-Henning Kamp devsw->member = noop; \ 683516ad423SPoul-Henning Kamp } else if (devsw->d_flags & D_NEEDGIANT) \ 684516ad423SPoul-Henning Kamp devsw->member = giant; \ 685516ad423SPoul-Henning Kamp } \ 686516ad423SPoul-Henning Kamp while (0) 687516ad423SPoul-Henning Kamp 688516ad423SPoul-Henning Kamp FIXUP(d_open, null_open, giant_open); 689516ad423SPoul-Henning Kamp FIXUP(d_fdopen, NULL, giant_fdopen); 690516ad423SPoul-Henning Kamp FIXUP(d_close, null_close, giant_close); 691516ad423SPoul-Henning Kamp FIXUP(d_read, no_read, giant_read); 692516ad423SPoul-Henning Kamp FIXUP(d_write, no_write, giant_write); 693516ad423SPoul-Henning Kamp FIXUP(d_ioctl, no_ioctl, giant_ioctl); 694516ad423SPoul-Henning Kamp FIXUP(d_poll, no_poll, giant_poll); 695cfd7baceSRobert Noland FIXUP(d_mmap, no_mmap, giant_mmap); 696516ad423SPoul-Henning Kamp FIXUP(d_strategy, no_strategy, giant_strategy); 697516ad423SPoul-Henning Kamp FIXUP(d_kqfilter, no_kqfilter, giant_kqfilter); 69864345f0bSJohn Baldwin FIXUP(d_mmap_single, no_mmap_single, giant_mmap_single); 699516ad423SPoul-Henning Kamp 700b2941431SPoul-Henning Kamp if (devsw->d_dump == NULL) devsw->d_dump = no_dump; 701cd690b60SPoul-Henning Kamp 702cd690b60SPoul-Henning Kamp LIST_INIT(&devsw->d_devs); 703cd690b60SPoul-Henning Kamp 704cd690b60SPoul-Henning Kamp devsw->d_flags |= D_INIT; 705cd690b60SPoul-Henning Kamp 706aeeb4202SKonstantin Belousov if (dsw2 != NULL) 707aeeb4202SKonstantin Belousov cdevsw_free_devlocked(dsw2); 708f1bb758dSKonstantin Belousov return (0); 7092a3faf2fSPoul-Henning Kamp } 71011586717SBrian Somers 711f1bb758dSKonstantin Belousov static int 71268f7a013SJaakko Heinonen prep_devname(struct cdev *dev, const char *fmt, va_list ap) 71368f7a013SJaakko Heinonen { 71468f7a013SJaakko Heinonen int len; 71568f7a013SJaakko Heinonen char *from, *q, *s, *to; 71668f7a013SJaakko Heinonen 7170ac9e27bSConrad Meyer dev_lock_assert_locked(); 71868f7a013SJaakko Heinonen 719852b05c5SEd Schouten len = vsnrprintf(dev->si_name, sizeof(dev->si_name), 32, fmt, ap); 7208fac9b7bSEd Schouten if (len > sizeof(dev->si_name) - 1) 72168f7a013SJaakko Heinonen return (ENAMETOOLONG); 72268f7a013SJaakko Heinonen 72368f7a013SJaakko Heinonen /* Strip leading slashes. */ 7248fac9b7bSEd Schouten for (from = dev->si_name; *from == '/'; from++) 72568f7a013SJaakko Heinonen ; 72668f7a013SJaakko Heinonen 7278fac9b7bSEd Schouten for (to = dev->si_name; *from != '\0'; from++, to++) { 728b1e1f725SJaakko Heinonen /* 729b1e1f725SJaakko Heinonen * Spaces and double quotation marks cause 730b1e1f725SJaakko Heinonen * problems for the devctl(4) protocol. 731b1e1f725SJaakko Heinonen * Reject names containing those characters. 732b1e1f725SJaakko Heinonen */ 733b1e1f725SJaakko Heinonen if (isspace(*from) || *from == '"') 734b1e1f725SJaakko Heinonen return (EINVAL); 73568f7a013SJaakko Heinonen /* Treat multiple sequential slashes as single. */ 73668f7a013SJaakko Heinonen while (from[0] == '/' && from[1] == '/') 73768f7a013SJaakko Heinonen from++; 73868f7a013SJaakko Heinonen /* Trailing slash is considered invalid. */ 73968f7a013SJaakko Heinonen if (from[0] == '/' && from[1] == '\0') 74068f7a013SJaakko Heinonen return (EINVAL); 74168f7a013SJaakko Heinonen *to = *from; 74268f7a013SJaakko Heinonen } 74368f7a013SJaakko Heinonen *to = '\0'; 74468f7a013SJaakko Heinonen 7458fac9b7bSEd Schouten if (dev->si_name[0] == '\0') 74668f7a013SJaakko Heinonen return (EINVAL); 74768f7a013SJaakko Heinonen 74868f7a013SJaakko Heinonen /* Disallow "." and ".." components. */ 7498fac9b7bSEd Schouten for (s = dev->si_name;;) { 75068f7a013SJaakko Heinonen for (q = s; *q != '/' && *q != '\0'; q++) 75168f7a013SJaakko Heinonen ; 75268f7a013SJaakko Heinonen if (q - s == 1 && s[0] == '.') 75368f7a013SJaakko Heinonen return (EINVAL); 75468f7a013SJaakko Heinonen if (q - s == 2 && s[0] == '.' && s[1] == '.') 75568f7a013SJaakko Heinonen return (EINVAL); 75668f7a013SJaakko Heinonen if (*q != '/') 75768f7a013SJaakko Heinonen break; 75868f7a013SJaakko Heinonen s = q + 1; 75968f7a013SJaakko Heinonen } 76068f7a013SJaakko Heinonen 7618fac9b7bSEd Schouten if (devfs_dev_exists(dev->si_name) != 0) 76268f7a013SJaakko Heinonen return (EEXIST); 76368f7a013SJaakko Heinonen 76468f7a013SJaakko Heinonen return (0); 76568f7a013SJaakko Heinonen } 76668f7a013SJaakko Heinonen 76748ce5d4cSKonstantin Belousov void 76848ce5d4cSKonstantin Belousov make_dev_args_init_impl(struct make_dev_args *args, size_t sz) 76948ce5d4cSKonstantin Belousov { 77048ce5d4cSKonstantin Belousov 77148ce5d4cSKonstantin Belousov bzero(args, sz); 77248ce5d4cSKonstantin Belousov args->mda_size = sz; 77348ce5d4cSKonstantin Belousov } 77448ce5d4cSKonstantin Belousov 77568f7a013SJaakko Heinonen static int 77648ce5d4cSKonstantin Belousov make_dev_sv(struct make_dev_args *args1, struct cdev **dres, 77748ce5d4cSKonstantin Belousov const char *fmt, va_list ap) 7782a3faf2fSPoul-Henning Kamp { 77968f7a013SJaakko Heinonen struct cdev *dev, *dev_new; 78048ce5d4cSKonstantin Belousov struct make_dev_args args; 78168f7a013SJaakko Heinonen int res; 7822a3faf2fSPoul-Henning Kamp 78348ce5d4cSKonstantin Belousov bzero(&args, sizeof(args)); 78448ce5d4cSKonstantin Belousov if (sizeof(args) < args1->mda_size) 78548ce5d4cSKonstantin Belousov return (EINVAL); 78648ce5d4cSKonstantin Belousov bcopy(args1, &args, args1->mda_size); 78748ce5d4cSKonstantin Belousov KASSERT((args.mda_flags & MAKEDEV_WAITOK) == 0 || 78848ce5d4cSKonstantin Belousov (args.mda_flags & MAKEDEV_NOWAIT) == 0, 78948ce5d4cSKonstantin Belousov ("make_dev_sv: both WAITOK and NOWAIT specified")); 79048ce5d4cSKonstantin Belousov dev_new = devfs_alloc(args.mda_flags); 79168f7a013SJaakko Heinonen if (dev_new == NULL) 792f1bb758dSKonstantin Belousov return (ENOMEM); 793027b1f71SPoul-Henning Kamp dev_lock(); 79448ce5d4cSKonstantin Belousov res = prep_cdevsw(args.mda_devsw, args.mda_flags); 795f1bb758dSKonstantin Belousov if (res != 0) { 796d2ba618aSKonstantin Belousov dev_unlock(); 79768f7a013SJaakko Heinonen devfs_free(dev_new); 798f1bb758dSKonstantin Belousov return (res); 799d2ba618aSKonstantin Belousov } 80048ce5d4cSKonstantin Belousov dev = newdev(&args, dev_new); 801ff91cc99SJaakko Heinonen if ((dev->si_flags & SI_NAMED) == 0) { 80268f7a013SJaakko Heinonen res = prep_devname(dev, fmt, ap); 80368f7a013SJaakko Heinonen if (res != 0) { 80448ce5d4cSKonstantin Belousov if ((args.mda_flags & MAKEDEV_CHECKNAME) == 0) { 80568f7a013SJaakko Heinonen panic( 80648ce5d4cSKonstantin Belousov "make_dev_sv: bad si_name (error=%d, si_name=%s)", 80768f7a013SJaakko Heinonen res, dev->si_name); 80868f7a013SJaakko Heinonen } 80968f7a013SJaakko Heinonen if (dev == dev_new) { 81068f7a013SJaakko Heinonen LIST_REMOVE(dev, si_list); 81168f7a013SJaakko Heinonen dev_unlock(); 81268f7a013SJaakko Heinonen devfs_free(dev); 813889dffbaSKonstantin Belousov } else 814889dffbaSKonstantin Belousov dev_unlock(); 81568f7a013SJaakko Heinonen return (res); 81668f7a013SJaakko Heinonen } 817ff91cc99SJaakko Heinonen } 81848ce5d4cSKonstantin Belousov if ((args.mda_flags & MAKEDEV_REF) != 0) 819de10ffa5SKonstantin Belousov dev_refl(dev); 82048ce5d4cSKonstantin Belousov if ((args.mda_flags & MAKEDEV_ETERNAL) != 0) 8213979450bSKonstantin Belousov dev->si_flags |= SI_ETERNAL; 822cd690b60SPoul-Henning Kamp KASSERT(!(dev->si_flags & SI_NAMED), 823ff7284eeSPoul-Henning Kamp ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)", 82448ce5d4cSKonstantin Belousov args.mda_devsw->d_name, dev2unit(dev), devtoname(dev))); 8255ef2707eSPoul-Henning Kamp dev->si_flags |= SI_NAMED; 82648ce5d4cSKonstantin Belousov if (args.mda_cr != NULL) 82748ce5d4cSKonstantin Belousov dev->si_cred = crhold(args.mda_cr); 82848ce5d4cSKonstantin Belousov dev->si_uid = args.mda_uid; 82948ce5d4cSKonstantin Belousov dev->si_gid = args.mda_gid; 83048ce5d4cSKonstantin Belousov dev->si_mode = args.mda_mode; 8311744fcd0SJulian Elischer 8329285a87eSPoul-Henning Kamp devfs_create(dev); 83309828ba9SKonstantin Belousov clean_unrhdrl(devfs_inos); 834aeeb4202SKonstantin Belousov dev_unlock_and_free(); 83548504cc2SKonstantin Belousov 83648ce5d4cSKonstantin Belousov notify_create(dev, args.mda_flags); 83748504cc2SKonstantin Belousov 838f1bb758dSKonstantin Belousov *dres = dev; 839f1bb758dSKonstantin Belousov return (0); 8403f54a085SPoul-Henning Kamp } 8413f54a085SPoul-Henning Kamp 84248ce5d4cSKonstantin Belousov int 84348ce5d4cSKonstantin Belousov make_dev_s(struct make_dev_args *args, struct cdev **dres, 84448ce5d4cSKonstantin Belousov const char *fmt, ...) 84548ce5d4cSKonstantin Belousov { 84648ce5d4cSKonstantin Belousov va_list ap; 84748ce5d4cSKonstantin Belousov int res; 84848ce5d4cSKonstantin Belousov 84948ce5d4cSKonstantin Belousov va_start(ap, fmt); 85048ce5d4cSKonstantin Belousov res = make_dev_sv(args, dres, fmt, ap); 85148ce5d4cSKonstantin Belousov va_end(ap); 85248ce5d4cSKonstantin Belousov return (res); 85348ce5d4cSKonstantin Belousov } 85448ce5d4cSKonstantin Belousov 85548ce5d4cSKonstantin Belousov static int 85648ce5d4cSKonstantin Belousov make_dev_credv(int flags, struct cdev **dres, struct cdevsw *devsw, int unit, 85748ce5d4cSKonstantin Belousov struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, 85848ce5d4cSKonstantin Belousov va_list ap) 85948ce5d4cSKonstantin Belousov { 86048ce5d4cSKonstantin Belousov struct make_dev_args args; 86148ce5d4cSKonstantin Belousov 86248ce5d4cSKonstantin Belousov make_dev_args_init(&args); 86348ce5d4cSKonstantin Belousov args.mda_flags = flags; 86448ce5d4cSKonstantin Belousov args.mda_devsw = devsw; 86548ce5d4cSKonstantin Belousov args.mda_cr = cr; 86648ce5d4cSKonstantin Belousov args.mda_uid = uid; 86748ce5d4cSKonstantin Belousov args.mda_gid = gid; 86848ce5d4cSKonstantin Belousov args.mda_mode = mode; 86948ce5d4cSKonstantin Belousov args.mda_unit = unit; 87048ce5d4cSKonstantin Belousov return (make_dev_sv(&args, dres, fmt, ap)); 87148ce5d4cSKonstantin Belousov } 87248ce5d4cSKonstantin Belousov 873d26dd2d9SRobert Watson struct cdev * 874edde8745SEd Schouten make_dev(struct cdevsw *devsw, int unit, uid_t uid, gid_t gid, int mode, 875d26dd2d9SRobert Watson const char *fmt, ...) 876d26dd2d9SRobert Watson { 877d26dd2d9SRobert Watson struct cdev *dev; 878d26dd2d9SRobert Watson va_list ap; 8796fa5abfdSMatt Macy int res __unused; 880d26dd2d9SRobert Watson 881d26dd2d9SRobert Watson va_start(ap, fmt); 8826fa5abfdSMatt Macy res = make_dev_credv(0, &dev, devsw, unit, NULL, uid, gid, mode, fmt, 8836fa5abfdSMatt Macy ap); 884d26dd2d9SRobert Watson va_end(ap); 88568f7a013SJaakko Heinonen KASSERT(res == 0 && dev != NULL, 88668f7a013SJaakko Heinonen ("make_dev: failed make_dev_credv (error=%d)", res)); 887d26dd2d9SRobert Watson return (dev); 888d26dd2d9SRobert Watson } 889d26dd2d9SRobert Watson 890d26dd2d9SRobert Watson struct cdev * 891edde8745SEd Schouten make_dev_cred(struct cdevsw *devsw, int unit, struct ucred *cr, uid_t uid, 892d26dd2d9SRobert Watson gid_t gid, int mode, const char *fmt, ...) 893d26dd2d9SRobert Watson { 894d26dd2d9SRobert Watson struct cdev *dev; 895d26dd2d9SRobert Watson va_list ap; 8966fa5abfdSMatt Macy int res __unused; 897d26dd2d9SRobert Watson 898d26dd2d9SRobert Watson va_start(ap, fmt); 8996fa5abfdSMatt Macy res = make_dev_credv(0, &dev, devsw, unit, cr, uid, gid, mode, fmt, ap); 900de10ffa5SKonstantin Belousov va_end(ap); 901de10ffa5SKonstantin Belousov 902f1bb758dSKonstantin Belousov KASSERT(res == 0 && dev != NULL, 90368f7a013SJaakko Heinonen ("make_dev_cred: failed make_dev_credv (error=%d)", res)); 904de10ffa5SKonstantin Belousov return (dev); 905de10ffa5SKonstantin Belousov } 906de10ffa5SKonstantin Belousov 907de10ffa5SKonstantin Belousov struct cdev * 908f1bb758dSKonstantin Belousov make_dev_credf(int flags, struct cdevsw *devsw, int unit, struct ucred *cr, 909f1bb758dSKonstantin Belousov uid_t uid, gid_t gid, int mode, const char *fmt, ...) 910de10ffa5SKonstantin Belousov { 911de10ffa5SKonstantin Belousov struct cdev *dev; 912de10ffa5SKonstantin Belousov va_list ap; 913f1bb758dSKonstantin Belousov int res; 914de10ffa5SKonstantin Belousov 915de10ffa5SKonstantin Belousov va_start(ap, fmt); 916f1bb758dSKonstantin Belousov res = make_dev_credv(flags, &dev, devsw, unit, cr, uid, gid, mode, 917de10ffa5SKonstantin Belousov fmt, ap); 918d26dd2d9SRobert Watson va_end(ap); 919d26dd2d9SRobert Watson 92068f7a013SJaakko Heinonen KASSERT(((flags & MAKEDEV_NOWAIT) != 0 && res == ENOMEM) || 92168f7a013SJaakko Heinonen ((flags & MAKEDEV_CHECKNAME) != 0 && res != ENOMEM) || res == 0, 92268f7a013SJaakko Heinonen ("make_dev_credf: failed make_dev_credv (error=%d)", res)); 923f1bb758dSKonstantin Belousov return (res == 0 ? dev : NULL); 924f1bb758dSKonstantin Belousov } 925f1bb758dSKonstantin Belousov 926f1bb758dSKonstantin Belousov int 9272e983aceSEd Schouten make_dev_p(int flags, struct cdev **cdev, struct cdevsw *devsw, 928f1bb758dSKonstantin Belousov struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, ...) 929f1bb758dSKonstantin Belousov { 930f1bb758dSKonstantin Belousov va_list ap; 931f1bb758dSKonstantin Belousov int res; 932f1bb758dSKonstantin Belousov 933f1bb758dSKonstantin Belousov va_start(ap, fmt); 9342e983aceSEd Schouten res = make_dev_credv(flags, cdev, devsw, 0, cr, uid, gid, mode, 935f1bb758dSKonstantin Belousov fmt, ap); 936f1bb758dSKonstantin Belousov va_end(ap); 937f1bb758dSKonstantin Belousov 93868f7a013SJaakko Heinonen KASSERT(((flags & MAKEDEV_NOWAIT) != 0 && res == ENOMEM) || 93968f7a013SJaakko Heinonen ((flags & MAKEDEV_CHECKNAME) != 0 && res != ENOMEM) || res == 0, 94068f7a013SJaakko Heinonen ("make_dev_p: failed make_dev_credv (error=%d)", res)); 941f1bb758dSKonstantin Belousov return (res); 942d26dd2d9SRobert Watson } 943d26dd2d9SRobert Watson 944e606a3c6SPoul-Henning Kamp static void 945e606a3c6SPoul-Henning Kamp dev_dependsl(struct cdev *pdev, struct cdev *cdev) 946e606a3c6SPoul-Henning Kamp { 947e606a3c6SPoul-Henning Kamp 948e606a3c6SPoul-Henning Kamp cdev->si_parent = pdev; 949e606a3c6SPoul-Henning Kamp cdev->si_flags |= SI_CHILD; 950e606a3c6SPoul-Henning Kamp LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings); 951e606a3c6SPoul-Henning Kamp } 952e606a3c6SPoul-Henning Kamp 9533344c5a1SPoul-Henning Kamp void 95489c9c53dSPoul-Henning Kamp dev_depends(struct cdev *pdev, struct cdev *cdev) 9553344c5a1SPoul-Henning Kamp { 9563344c5a1SPoul-Henning Kamp 957a0e78d2eSPoul-Henning Kamp dev_lock(); 958e606a3c6SPoul-Henning Kamp dev_dependsl(pdev, cdev); 959a0e78d2eSPoul-Henning Kamp dev_unlock(); 9603344c5a1SPoul-Henning Kamp } 9613344c5a1SPoul-Henning Kamp 962b50a7799SAndrey V. Elsukov static int 963b50a7799SAndrey V. Elsukov make_dev_alias_v(int flags, struct cdev **cdev, struct cdev *pdev, 964b50a7799SAndrey V. Elsukov const char *fmt, va_list ap) 9653f54a085SPoul-Henning Kamp { 96689c9c53dSPoul-Henning Kamp struct cdev *dev; 96768f7a013SJaakko Heinonen int error; 9683f54a085SPoul-Henning Kamp 969b50a7799SAndrey V. Elsukov KASSERT(pdev != NULL, ("make_dev_alias_v: pdev is NULL")); 970b50a7799SAndrey V. Elsukov KASSERT((flags & MAKEDEV_WAITOK) == 0 || (flags & MAKEDEV_NOWAIT) == 0, 971b50a7799SAndrey V. Elsukov ("make_dev_alias_v: both WAITOK and NOWAIT specified")); 972b50a7799SAndrey V. Elsukov KASSERT((flags & ~(MAKEDEV_WAITOK | MAKEDEV_NOWAIT | 973b50a7799SAndrey V. Elsukov MAKEDEV_CHECKNAME)) == 0, 974b50a7799SAndrey V. Elsukov ("make_dev_alias_v: invalid flags specified (flags=%02x)", flags)); 975b50a7799SAndrey V. Elsukov 976b50a7799SAndrey V. Elsukov dev = devfs_alloc(flags); 977b50a7799SAndrey V. Elsukov if (dev == NULL) 978b50a7799SAndrey V. Elsukov return (ENOMEM); 979a0e78d2eSPoul-Henning Kamp dev_lock(); 9803f54a085SPoul-Henning Kamp dev->si_flags |= SI_ALIAS; 98168f7a013SJaakko Heinonen error = prep_devname(dev, fmt, ap); 98268f7a013SJaakko Heinonen if (error != 0) { 983b50a7799SAndrey V. Elsukov if ((flags & MAKEDEV_CHECKNAME) == 0) { 984b50a7799SAndrey V. Elsukov panic("make_dev_alias_v: bad si_name " 985b50a7799SAndrey V. Elsukov "(error=%d, si_name=%s)", error, dev->si_name); 986b50a7799SAndrey V. Elsukov } 987b50a7799SAndrey V. Elsukov dev_unlock(); 988b50a7799SAndrey V. Elsukov devfs_free(dev); 989b50a7799SAndrey V. Elsukov return (error); 99068f7a013SJaakko Heinonen } 99168f7a013SJaakko Heinonen dev->si_flags |= SI_NAMED; 9929285a87eSPoul-Henning Kamp devfs_create(dev); 993ae95dc62SKonstantin Belousov dev_dependsl(pdev, dev); 99409828ba9SKonstantin Belousov clean_unrhdrl(devfs_inos); 995a0e78d2eSPoul-Henning Kamp dev_unlock(); 99648504cc2SKonstantin Belousov 997b50a7799SAndrey V. Elsukov notify_create(dev, flags); 998b50a7799SAndrey V. Elsukov *cdev = dev; 99948504cc2SKonstantin Belousov 1000b50a7799SAndrey V. Elsukov return (0); 1001b50a7799SAndrey V. Elsukov } 1002b50a7799SAndrey V. Elsukov 1003b50a7799SAndrey V. Elsukov struct cdev * 1004b50a7799SAndrey V. Elsukov make_dev_alias(struct cdev *pdev, const char *fmt, ...) 1005b50a7799SAndrey V. Elsukov { 1006b50a7799SAndrey V. Elsukov struct cdev *dev; 1007b50a7799SAndrey V. Elsukov va_list ap; 10086fa5abfdSMatt Macy int res __unused; 10096fa5abfdSMatt Macy 1010b50a7799SAndrey V. Elsukov va_start(ap, fmt); 10116fa5abfdSMatt Macy res = make_dev_alias_v(MAKEDEV_WAITOK, &dev, pdev, fmt, ap); 1012b50a7799SAndrey V. Elsukov va_end(ap); 1013b50a7799SAndrey V. Elsukov 1014b50a7799SAndrey V. Elsukov KASSERT(res == 0 && dev != NULL, 1015b50a7799SAndrey V. Elsukov ("make_dev_alias: failed make_dev_alias_v (error=%d)", res)); 10160ef1c826SPoul-Henning Kamp return (dev); 10170ef1c826SPoul-Henning Kamp } 10180ef1c826SPoul-Henning Kamp 1019b50a7799SAndrey V. Elsukov int 1020b50a7799SAndrey V. Elsukov make_dev_alias_p(int flags, struct cdev **cdev, struct cdev *pdev, 1021b50a7799SAndrey V. Elsukov const char *fmt, ...) 1022b50a7799SAndrey V. Elsukov { 1023b50a7799SAndrey V. Elsukov va_list ap; 1024b50a7799SAndrey V. Elsukov int res; 1025b50a7799SAndrey V. Elsukov 1026b50a7799SAndrey V. Elsukov va_start(ap, fmt); 1027b50a7799SAndrey V. Elsukov res = make_dev_alias_v(flags, cdev, pdev, fmt, ap); 1028b50a7799SAndrey V. Elsukov va_end(ap); 1029b50a7799SAndrey V. Elsukov return (res); 1030b50a7799SAndrey V. Elsukov } 1031b50a7799SAndrey V. Elsukov 1032aa76615dSJustin T. Gibbs int 1033aa76615dSJustin T. Gibbs make_dev_physpath_alias(int flags, struct cdev **cdev, struct cdev *pdev, 1034aa76615dSJustin T. Gibbs struct cdev *old_alias, const char *physpath) 1035aa76615dSJustin T. Gibbs { 1036aa76615dSJustin T. Gibbs char *devfspath; 1037aa76615dSJustin T. Gibbs int physpath_len; 1038aa76615dSJustin T. Gibbs int max_parentpath_len; 1039aa76615dSJustin T. Gibbs int parentpath_len; 1040aa76615dSJustin T. Gibbs int devfspathbuf_len; 1041aa76615dSJustin T. Gibbs int mflags; 1042aa76615dSJustin T. Gibbs int ret; 1043aa76615dSJustin T. Gibbs 1044aa76615dSJustin T. Gibbs *cdev = NULL; 1045aa76615dSJustin T. Gibbs devfspath = NULL; 1046aa76615dSJustin T. Gibbs physpath_len = strlen(physpath); 1047aa76615dSJustin T. Gibbs ret = EINVAL; 1048aa76615dSJustin T. Gibbs if (physpath_len == 0) 1049aa76615dSJustin T. Gibbs goto out; 1050aa76615dSJustin T. Gibbs 1051aa76615dSJustin T. Gibbs if (strncmp("id1,", physpath, 4) == 0) { 1052aa76615dSJustin T. Gibbs physpath += 4; 1053aa76615dSJustin T. Gibbs physpath_len -= 4; 1054aa76615dSJustin T. Gibbs if (physpath_len == 0) 1055aa76615dSJustin T. Gibbs goto out; 1056aa76615dSJustin T. Gibbs } 1057aa76615dSJustin T. Gibbs 1058aa76615dSJustin T. Gibbs max_parentpath_len = SPECNAMELEN - physpath_len - /*/*/1; 1059aa76615dSJustin T. Gibbs parentpath_len = strlen(pdev->si_name); 1060aa76615dSJustin T. Gibbs if (max_parentpath_len < parentpath_len) { 106120654f4eSAlexander Motin if (bootverbose) 106220654f4eSAlexander Motin printf("WARNING: Unable to alias %s " 1063aa76615dSJustin T. Gibbs "to %s/%s - path too long\n", 1064aa76615dSJustin T. Gibbs pdev->si_name, physpath, pdev->si_name); 1065aa76615dSJustin T. Gibbs ret = ENAMETOOLONG; 1066aa76615dSJustin T. Gibbs goto out; 1067aa76615dSJustin T. Gibbs } 1068aa76615dSJustin T. Gibbs 1069aa76615dSJustin T. Gibbs mflags = (flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK; 1070aa76615dSJustin T. Gibbs devfspathbuf_len = physpath_len + /*/*/1 + parentpath_len + /*NUL*/1; 1071aa76615dSJustin T. Gibbs devfspath = malloc(devfspathbuf_len, M_DEVBUF, mflags); 1072aa76615dSJustin T. Gibbs if (devfspath == NULL) { 1073aa76615dSJustin T. Gibbs ret = ENOMEM; 1074aa76615dSJustin T. Gibbs goto out; 1075aa76615dSJustin T. Gibbs } 1076aa76615dSJustin T. Gibbs 1077aa76615dSJustin T. Gibbs sprintf(devfspath, "%s/%s", physpath, pdev->si_name); 10784d651f4eSKonstantin Belousov if (old_alias != NULL && strcmp(old_alias->si_name, devfspath) == 0) { 1079aa76615dSJustin T. Gibbs /* Retain the existing alias. */ 1080aa76615dSJustin T. Gibbs *cdev = old_alias; 1081aa76615dSJustin T. Gibbs old_alias = NULL; 1082aa76615dSJustin T. Gibbs ret = 0; 1083aa76615dSJustin T. Gibbs } else { 1084f403cfb1SKonstantin Belousov ret = make_dev_alias_p(flags, cdev, pdev, "%s", devfspath); 1085aa76615dSJustin T. Gibbs } 1086aa76615dSJustin T. Gibbs out: 1087aa76615dSJustin T. Gibbs if (old_alias != NULL) 1088aa76615dSJustin T. Gibbs destroy_dev(old_alias); 1089aa76615dSJustin T. Gibbs if (devfspath != NULL) 1090aa76615dSJustin T. Gibbs free(devfspath, M_DEVBUF); 1091aa76615dSJustin T. Gibbs return (ret); 1092aa76615dSJustin T. Gibbs } 1093aa76615dSJustin T. Gibbs 1094cd690b60SPoul-Henning Kamp static void 1095aa2f6ddcSPoul-Henning Kamp destroy_devl(struct cdev *dev) 1096d137acccSPoul-Henning Kamp { 1097743cd76aSPoul-Henning Kamp struct cdevsw *csw; 10980bad52e1SHans Petter Selasky struct cdev_privdata *p; 10993b50dff5SKonstantin Belousov struct cdev_priv *cdp; 1100743cd76aSPoul-Henning Kamp 11010ac9e27bSConrad Meyer dev_lock_assert_locked(); 1102743cd76aSPoul-Henning Kamp KASSERT(dev->si_flags & SI_NAMED, 11036bfa9a2dSEd Schouten ("WARNING: Driver mistake: destroy_dev on %d\n", dev2unit(dev))); 11043979450bSKonstantin Belousov KASSERT((dev->si_flags & SI_ETERNAL) == 0, 11053979450bSKonstantin Belousov ("WARNING: Driver mistake: destroy_dev on eternal %d\n", 11063979450bSKonstantin Belousov dev2unit(dev))); 11075ef2707eSPoul-Henning Kamp 11083b50dff5SKonstantin Belousov cdp = cdev2priv(dev); 11093b50dff5SKonstantin Belousov if ((cdp->cdp_flags & CDP_UNREF_DTR) == 0) { 11103b50dff5SKonstantin Belousov /* 11113b50dff5SKonstantin Belousov * Avoid race with dev_rel(), e.g. from the populate 11123b50dff5SKonstantin Belousov * loop. If CDP_UNREF_DTR flag is set, the reference 11133b50dff5SKonstantin Belousov * to be dropped at the end of destroy_devl() was 11143b50dff5SKonstantin Belousov * already taken by delist_dev_locked(). 11153b50dff5SKonstantin Belousov */ 11163b50dff5SKonstantin Belousov dev_refl(dev); 11173b50dff5SKonstantin Belousov 11189285a87eSPoul-Henning Kamp devfs_destroy(dev); 11193b50dff5SKonstantin Belousov } 1120cd690b60SPoul-Henning Kamp 1121cd690b60SPoul-Henning Kamp /* Remove name marking */ 1122b0b03348SPoul-Henning Kamp dev->si_flags &= ~SI_NAMED; 1123b0b03348SPoul-Henning Kamp 1124cd690b60SPoul-Henning Kamp /* If we are a child, remove us from the parents list */ 11253344c5a1SPoul-Henning Kamp if (dev->si_flags & SI_CHILD) { 11263344c5a1SPoul-Henning Kamp LIST_REMOVE(dev, si_siblings); 11273344c5a1SPoul-Henning Kamp dev->si_flags &= ~SI_CHILD; 11283344c5a1SPoul-Henning Kamp } 1129cd690b60SPoul-Henning Kamp 1130cd690b60SPoul-Henning Kamp /* Kill our children */ 11313344c5a1SPoul-Henning Kamp while (!LIST_EMPTY(&dev->si_children)) 1132aa2f6ddcSPoul-Henning Kamp destroy_devl(LIST_FIRST(&dev->si_children)); 1133cd690b60SPoul-Henning Kamp 1134cd690b60SPoul-Henning Kamp /* Remove from clone list */ 1135b0b03348SPoul-Henning Kamp if (dev->si_flags & SI_CLONELIST) { 1136b0b03348SPoul-Henning Kamp LIST_REMOVE(dev, si_clone); 1137b0b03348SPoul-Henning Kamp dev->si_flags &= ~SI_CLONELIST; 1138b0b03348SPoul-Henning Kamp } 1139cd690b60SPoul-Henning Kamp 1140a02cab33SMateusz Guzik mtx_lock(&cdp->cdp_threadlock); 1141743cd76aSPoul-Henning Kamp csw = dev->si_devsw; 11421abf2c36SBrian Feldman dev->si_devsw = NULL; /* already NULL for SI_ALIAS */ 11431abf2c36SBrian Feldman while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) { 1144743cd76aSPoul-Henning Kamp csw->d_purge(dev); 1145a02cab33SMateusz Guzik mtx_unlock(&cdp->cdp_threadlock); 1146743cd76aSPoul-Henning Kamp msleep(csw, &devmtx, PRIBIO, "devprg", hz/10); 1147a02cab33SMateusz Guzik mtx_lock(&cdp->cdp_threadlock); 1148d595182fSPoul-Henning Kamp if (dev->si_threadcount) 1149d595182fSPoul-Henning Kamp printf("Still %lu threads in %s\n", 1150d595182fSPoul-Henning Kamp dev->si_threadcount, devtoname(dev)); 1151743cd76aSPoul-Henning Kamp } 1152e0c33ad5STor Egge while (dev->si_threadcount != 0) { 1153e0c33ad5STor Egge /* Use unique dummy wait ident */ 1154a02cab33SMateusz Guzik mtx_unlock(&cdp->cdp_threadlock); 1155e0c33ad5STor Egge msleep(&csw, &devmtx, PRIBIO, "devdrn", hz / 10); 1156a02cab33SMateusz Guzik mtx_lock(&cdp->cdp_threadlock); 1157e0c33ad5STor Egge } 1158743cd76aSPoul-Henning Kamp 1159a02cab33SMateusz Guzik mtx_unlock(&cdp->cdp_threadlock); 116082f4d640SKonstantin Belousov dev_unlock(); 11612793ea13SHans Petter Selasky if ((cdp->cdp_flags & CDP_UNREF_DTR) == 0) { 11622793ea13SHans Petter Selasky /* avoid out of order notify events */ 116348504cc2SKonstantin Belousov notify_destroy(dev); 11642793ea13SHans Petter Selasky } 116582f4d640SKonstantin Belousov mtx_lock(&cdevpriv_mtx); 11662793ea13SHans Petter Selasky while ((p = LIST_FIRST(&cdp->cdp_fdpriv)) != NULL) { 116782f4d640SKonstantin Belousov devfs_destroy_cdevpriv(p); 116882f4d640SKonstantin Belousov mtx_lock(&cdevpriv_mtx); 116982f4d640SKonstantin Belousov } 117082f4d640SKonstantin Belousov mtx_unlock(&cdevpriv_mtx); 117182f4d640SKonstantin Belousov dev_lock(); 117248504cc2SKonstantin Belousov 1173743cd76aSPoul-Henning Kamp dev->si_drv1 = 0; 1174743cd76aSPoul-Henning Kamp dev->si_drv2 = 0; 1175743cd76aSPoul-Henning Kamp bzero(&dev->__si_u, sizeof(dev->__si_u)); 1176743cd76aSPoul-Henning Kamp 1177cd690b60SPoul-Henning Kamp if (!(dev->si_flags & SI_ALIAS)) { 1178cd690b60SPoul-Henning Kamp /* Remove from cdevsw list */ 1179cd690b60SPoul-Henning Kamp LIST_REMOVE(dev, si_list); 1180cd690b60SPoul-Henning Kamp 1181e606a3c6SPoul-Henning Kamp /* If cdevsw has no more struct cdev *'s, clean it */ 1182de10ffa5SKonstantin Belousov if (LIST_EMPTY(&csw->d_devs)) { 1183a5993c33SPoul-Henning Kamp fini_cdevsw(csw); 1184de10ffa5SKonstantin Belousov wakeup(&csw->d_devs); 1185de10ffa5SKonstantin Belousov } 1186cd690b60SPoul-Henning Kamp } 11875ef2707eSPoul-Henning Kamp dev->si_flags &= ~SI_ALIAS; 11883b50dff5SKonstantin Belousov cdp->cdp_flags &= ~CDP_UNREF_DTR; 11893b50dff5SKonstantin Belousov dev->si_refcount--; 1190743cd76aSPoul-Henning Kamp 11913b50dff5SKonstantin Belousov if (dev->si_refcount > 0) 1192cd690b60SPoul-Henning Kamp LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list); 11933b50dff5SKonstantin Belousov else 11949bc911d4SKonstantin Belousov dev_free_devlocked(dev); 1195d137acccSPoul-Henning Kamp } 1196cd690b60SPoul-Henning Kamp 119707dbde67SHans Petter Selasky static void 119807dbde67SHans Petter Selasky delist_dev_locked(struct cdev *dev) 119907dbde67SHans Petter Selasky { 12003b50dff5SKonstantin Belousov struct cdev_priv *cdp; 120107dbde67SHans Petter Selasky struct cdev *child; 12023b50dff5SKonstantin Belousov 12030ac9e27bSConrad Meyer dev_lock_assert_locked(); 12043b50dff5SKonstantin Belousov cdp = cdev2priv(dev); 12053b50dff5SKonstantin Belousov if ((cdp->cdp_flags & CDP_UNREF_DTR) != 0) 12063b50dff5SKonstantin Belousov return; 12073b50dff5SKonstantin Belousov cdp->cdp_flags |= CDP_UNREF_DTR; 12083b50dff5SKonstantin Belousov dev_refl(dev); 120907dbde67SHans Petter Selasky devfs_destroy(dev); 121007dbde67SHans Petter Selasky LIST_FOREACH(child, &dev->si_children, si_siblings) 121107dbde67SHans Petter Selasky delist_dev_locked(child); 12122793ea13SHans Petter Selasky dev_unlock(); 12132793ea13SHans Petter Selasky /* ensure the destroy event is queued in order */ 12142793ea13SHans Petter Selasky notify_destroy(dev); 12152793ea13SHans Petter Selasky dev_lock(); 121607dbde67SHans Petter Selasky } 121707dbde67SHans Petter Selasky 12182793ea13SHans Petter Selasky /* 12192793ea13SHans Petter Selasky * This function will delist a character device and its children from 12202793ea13SHans Petter Selasky * the directory listing and create a destroy event without waiting 12212793ea13SHans Petter Selasky * for all character device references to go away. At some later point 12222793ea13SHans Petter Selasky * destroy_dev() must be called to complete the character device 12232793ea13SHans Petter Selasky * destruction. After calling this function the character device name 12242793ea13SHans Petter Selasky * can instantly be re-used. 12252793ea13SHans Petter Selasky */ 122607dbde67SHans Petter Selasky void 122707dbde67SHans Petter Selasky delist_dev(struct cdev *dev) 122807dbde67SHans Petter Selasky { 12293b50dff5SKonstantin Belousov 12302793ea13SHans Petter Selasky WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "delist_dev"); 123107dbde67SHans Petter Selasky dev_lock(); 123207dbde67SHans Petter Selasky delist_dev_locked(dev); 123307dbde67SHans Petter Selasky dev_unlock(); 123407dbde67SHans Petter Selasky } 123507dbde67SHans Petter Selasky 1236cd690b60SPoul-Henning Kamp void 123789c9c53dSPoul-Henning Kamp destroy_dev(struct cdev *dev) 1238cd690b60SPoul-Henning Kamp { 1239cd690b60SPoul-Henning Kamp 1240b7a813fcSKonstantin Belousov WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "destroy_dev"); 1241a0e78d2eSPoul-Henning Kamp dev_lock(); 1242aa2f6ddcSPoul-Henning Kamp destroy_devl(dev); 12439bc911d4SKonstantin Belousov dev_unlock_and_free(); 1244cd690b60SPoul-Henning Kamp } 1245d137acccSPoul-Henning Kamp 1246c32cc149SBruce Evans const char * 124789c9c53dSPoul-Henning Kamp devtoname(struct cdev *dev) 1248b8e49f68SBill Fumerola { 1249b8e49f68SBill Fumerola 1250b8e49f68SBill Fumerola return (dev->si_name); 1251b8e49f68SBill Fumerola } 1252db901281SPoul-Henning Kamp 1253db901281SPoul-Henning Kamp int 125401de1b13SPoul-Henning Kamp dev_stdclone(char *name, char **namep, const char *stem, int *unit) 1255db901281SPoul-Henning Kamp { 1256db901281SPoul-Henning Kamp int u, i; 1257db901281SPoul-Henning Kamp 1258db901281SPoul-Henning Kamp i = strlen(stem); 125956700d46SBrian Somers if (bcmp(stem, name, i) != 0) 126056700d46SBrian Somers return (0); 1261db901281SPoul-Henning Kamp if (!isdigit(name[i])) 1262db901281SPoul-Henning Kamp return (0); 1263db901281SPoul-Henning Kamp u = 0; 126410786074SPoul-Henning Kamp if (name[i] == '0' && isdigit(name[i+1])) 126510786074SPoul-Henning Kamp return (0); 1266db901281SPoul-Henning Kamp while (isdigit(name[i])) { 1267db901281SPoul-Henning Kamp u *= 10; 1268db901281SPoul-Henning Kamp u += name[i++] - '0'; 1269db901281SPoul-Henning Kamp } 1270dab3d85fSBrian Feldman if (u > 0xffffff) 1271dab3d85fSBrian Feldman return (0); 1272db901281SPoul-Henning Kamp *unit = u; 1273db901281SPoul-Henning Kamp if (namep) 1274db901281SPoul-Henning Kamp *namep = &name[i]; 1275db901281SPoul-Henning Kamp if (name[i]) 1276db901281SPoul-Henning Kamp return (2); 1277db901281SPoul-Henning Kamp return (1); 1278db901281SPoul-Henning Kamp } 12798d25eb2cSPoul-Henning Kamp 12808d25eb2cSPoul-Henning Kamp /* 1281b0b03348SPoul-Henning Kamp * Helper functions for cloning device drivers. 1282b0b03348SPoul-Henning Kamp * 1283b0b03348SPoul-Henning Kamp * The objective here is to make it unnecessary for the device drivers to 1284b0b03348SPoul-Henning Kamp * use rman or similar to manage their unit number space. Due to the way 1285b0b03348SPoul-Henning Kamp * we do "on-demand" devices, using rman or other "private" methods 1286b0b03348SPoul-Henning Kamp * will be very tricky to lock down properly once we lock down this file. 1287b0b03348SPoul-Henning Kamp * 12889a98ae94SLukas Ertl * Instead we give the drivers these routines which puts the struct cdev *'s 12899a98ae94SLukas Ertl * that are to be managed on their own list, and gives the driver the ability 1290b0b03348SPoul-Henning Kamp * to ask for the first free unit number or a given specified unit number. 1291b0b03348SPoul-Henning Kamp * 1292b0b03348SPoul-Henning Kamp * In addition these routines support paired devices (pty, nmdm and similar) 1293b0b03348SPoul-Henning Kamp * by respecting a number of "flag" bits in the minor number. 1294b0b03348SPoul-Henning Kamp * 1295b0b03348SPoul-Henning Kamp */ 1296b0b03348SPoul-Henning Kamp 1297b0b03348SPoul-Henning Kamp struct clonedevs { 1298b0b03348SPoul-Henning Kamp LIST_HEAD(,cdev) head; 1299b0b03348SPoul-Henning Kamp }; 1300b0b03348SPoul-Henning Kamp 13019397290eSPoul-Henning Kamp void 13029397290eSPoul-Henning Kamp clone_setup(struct clonedevs **cdp) 13039397290eSPoul-Henning Kamp { 13049397290eSPoul-Henning Kamp 13059397290eSPoul-Henning Kamp *cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO); 13069397290eSPoul-Henning Kamp LIST_INIT(&(*cdp)->head); 13079397290eSPoul-Henning Kamp } 13089397290eSPoul-Henning Kamp 1309b0b03348SPoul-Henning Kamp int 1310cf141467SKonstantin Belousov clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, 1311cf141467SKonstantin Belousov struct cdev **dp, int extra) 1312b0b03348SPoul-Henning Kamp { 1313b0b03348SPoul-Henning Kamp struct clonedevs *cd; 1314027b1f71SPoul-Henning Kamp struct cdev *dev, *ndev, *dl, *de; 131548ce5d4cSKonstantin Belousov struct make_dev_args args; 1316b0b03348SPoul-Henning Kamp int unit, low, u; 1317b0b03348SPoul-Henning Kamp 13189397290eSPoul-Henning Kamp KASSERT(*cdp != NULL, 13199397290eSPoul-Henning Kamp ("clone_setup() not called in driver \"%s\"", csw->d_name)); 1320b0b03348SPoul-Henning Kamp KASSERT(!(extra & CLONE_UNITMASK), 1321b0b03348SPoul-Henning Kamp ("Illegal extra bits (0x%x) in clone_create", extra)); 1322b0b03348SPoul-Henning Kamp KASSERT(*up <= CLONE_UNITMASK, 1323b0b03348SPoul-Henning Kamp ("Too high unit (0x%x) in clone_create", *up)); 132429d4cb24SEd Schouten KASSERT(csw->d_flags & D_NEEDMINOR, 132529d4cb24SEd Schouten ("clone_create() on cdevsw without minor numbers")); 1326b0b03348SPoul-Henning Kamp 1327b0b03348SPoul-Henning Kamp /* 1328b0b03348SPoul-Henning Kamp * Search the list for a lot of things in one go: 1329b0b03348SPoul-Henning Kamp * A preexisting match is returned immediately. 1330b0b03348SPoul-Henning Kamp * The lowest free unit number if we are passed -1, and the place 1331b0b03348SPoul-Henning Kamp * in the list where we should insert that new element. 1332b0b03348SPoul-Henning Kamp * The place to insert a specified unit number, if applicable 1333b0b03348SPoul-Henning Kamp * the end of the list. 1334b0b03348SPoul-Henning Kamp */ 1335b0b03348SPoul-Henning Kamp unit = *up; 1336d2ba618aSKonstantin Belousov ndev = devfs_alloc(MAKEDEV_WAITOK); 1337027b1f71SPoul-Henning Kamp dev_lock(); 1338d2ba618aSKonstantin Belousov prep_cdevsw(csw, MAKEDEV_WAITOK); 13398666b655SPoul-Henning Kamp low = extra; 1340b0b03348SPoul-Henning Kamp de = dl = NULL; 13419397290eSPoul-Henning Kamp cd = *cdp; 1342b0b03348SPoul-Henning Kamp LIST_FOREACH(dev, &cd->head, si_clone) { 1343027b1f71SPoul-Henning Kamp KASSERT(dev->si_flags & SI_CLONELIST, 1344027b1f71SPoul-Henning Kamp ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); 1345b0b03348SPoul-Henning Kamp u = dev2unit(dev); 1346b0b03348SPoul-Henning Kamp if (u == (unit | extra)) { 1347b0b03348SPoul-Henning Kamp *dp = dev; 1348027b1f71SPoul-Henning Kamp dev_unlock(); 13499bc911d4SKonstantin Belousov devfs_free(ndev); 1350b0b03348SPoul-Henning Kamp return (0); 1351b0b03348SPoul-Henning Kamp } 1352b0b03348SPoul-Henning Kamp if (unit == -1 && u == low) { 1353b0b03348SPoul-Henning Kamp low++; 1354b0b03348SPoul-Henning Kamp de = dev; 1355b0b03348SPoul-Henning Kamp continue; 13567bbb3a26SPoul-Henning Kamp } else if (u < (unit | extra)) { 13577bbb3a26SPoul-Henning Kamp de = dev; 13587bbb3a26SPoul-Henning Kamp continue; 13597bbb3a26SPoul-Henning Kamp } else if (u > (unit | extra)) { 1360b0b03348SPoul-Henning Kamp dl = dev; 1361b0b03348SPoul-Henning Kamp break; 1362b0b03348SPoul-Henning Kamp } 1363b0b03348SPoul-Henning Kamp } 1364b0b03348SPoul-Henning Kamp if (unit == -1) 13658666b655SPoul-Henning Kamp unit = low & CLONE_UNITMASK; 136648ce5d4cSKonstantin Belousov make_dev_args_init(&args); 136748ce5d4cSKonstantin Belousov args.mda_unit = unit | extra; 136848ce5d4cSKonstantin Belousov args.mda_devsw = csw; 136948ce5d4cSKonstantin Belousov dev = newdev(&args, ndev); 1370027b1f71SPoul-Henning Kamp if (dev->si_flags & SI_CLONELIST) { 1371027b1f71SPoul-Henning Kamp printf("dev %p (%s) is on clonelist\n", dev, dev->si_name); 13727bbb3a26SPoul-Henning Kamp printf("unit=%d, low=%d, extra=0x%x\n", unit, low, extra); 1373027b1f71SPoul-Henning Kamp LIST_FOREACH(dev, &cd->head, si_clone) { 1374027b1f71SPoul-Henning Kamp printf("\t%p %s\n", dev, dev->si_name); 1375027b1f71SPoul-Henning Kamp } 1376027b1f71SPoul-Henning Kamp panic("foo"); 1377027b1f71SPoul-Henning Kamp } 1378b0b03348SPoul-Henning Kamp KASSERT(!(dev->si_flags & SI_CLONELIST), 1379027b1f71SPoul-Henning Kamp ("Dev %p(%s) should not be on clonelist", dev, dev->si_name)); 1380b0b03348SPoul-Henning Kamp if (dl != NULL) 1381b0b03348SPoul-Henning Kamp LIST_INSERT_BEFORE(dl, dev, si_clone); 1382b0b03348SPoul-Henning Kamp else if (de != NULL) 1383b0b03348SPoul-Henning Kamp LIST_INSERT_AFTER(de, dev, si_clone); 1384b0b03348SPoul-Henning Kamp else 1385b0b03348SPoul-Henning Kamp LIST_INSERT_HEAD(&cd->head, dev, si_clone); 1386b0b03348SPoul-Henning Kamp dev->si_flags |= SI_CLONELIST; 1387b0b03348SPoul-Henning Kamp *up = unit; 13889bc911d4SKonstantin Belousov dev_unlock_and_free(); 1389b0b03348SPoul-Henning Kamp return (1); 1390b0b03348SPoul-Henning Kamp } 1391b0b03348SPoul-Henning Kamp 1392b0b03348SPoul-Henning Kamp /* 1393b0b03348SPoul-Henning Kamp * Kill everything still on the list. The driver should already have 139489c9c53dSPoul-Henning Kamp * disposed of any softc hung of the struct cdev *'s at this time. 1395b0b03348SPoul-Henning Kamp */ 1396b0b03348SPoul-Henning Kamp void 1397b0b03348SPoul-Henning Kamp clone_cleanup(struct clonedevs **cdp) 1398b0b03348SPoul-Henning Kamp { 1399de10ffa5SKonstantin Belousov struct cdev *dev; 1400de10ffa5SKonstantin Belousov struct cdev_priv *cp; 1401b0b03348SPoul-Henning Kamp struct clonedevs *cd; 1402b0b03348SPoul-Henning Kamp 1403b0b03348SPoul-Henning Kamp cd = *cdp; 1404b0b03348SPoul-Henning Kamp if (cd == NULL) 1405b0b03348SPoul-Henning Kamp return; 1406027b1f71SPoul-Henning Kamp dev_lock(); 1407de10ffa5SKonstantin Belousov while (!LIST_EMPTY(&cd->head)) { 1408de10ffa5SKonstantin Belousov dev = LIST_FIRST(&cd->head); 1409de10ffa5SKonstantin Belousov LIST_REMOVE(dev, si_clone); 1410027b1f71SPoul-Henning Kamp KASSERT(dev->si_flags & SI_CLONELIST, 1411027b1f71SPoul-Henning Kamp ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); 1412de10ffa5SKonstantin Belousov dev->si_flags &= ~SI_CLONELIST; 141305427aafSKonstantin Belousov cp = cdev2priv(dev); 1414de10ffa5SKonstantin Belousov if (!(cp->cdp_flags & CDP_SCHED_DTR)) { 1415de10ffa5SKonstantin Belousov cp->cdp_flags |= CDP_SCHED_DTR; 1416b0b03348SPoul-Henning Kamp KASSERT(dev->si_flags & SI_NAMED, 1417dde58752SGleb Kurtsou ("Driver has goofed in cloning underways udev %jx unit %x", 1418dde58752SGleb Kurtsou (uintmax_t)dev2udev(dev), dev2unit(dev))); 1419aa2f6ddcSPoul-Henning Kamp destroy_devl(dev); 1420b0b03348SPoul-Henning Kamp } 1421de10ffa5SKonstantin Belousov } 1422aeeb4202SKonstantin Belousov dev_unlock_and_free(); 1423b0b03348SPoul-Henning Kamp free(cd, M_DEVBUF); 1424b0b03348SPoul-Henning Kamp *cdp = NULL; 1425b0b03348SPoul-Henning Kamp } 1426de10ffa5SKonstantin Belousov 1427de10ffa5SKonstantin Belousov static TAILQ_HEAD(, cdev_priv) dev_ddtr = 1428de10ffa5SKonstantin Belousov TAILQ_HEAD_INITIALIZER(dev_ddtr); 1429268e76d8SJohn Baldwin static struct task dev_dtr_task = TASK_INITIALIZER(0, destroy_dev_tq, NULL); 1430de10ffa5SKonstantin Belousov 1431de10ffa5SKonstantin Belousov static void 1432de10ffa5SKonstantin Belousov destroy_dev_tq(void *ctx, int pending) 1433de10ffa5SKonstantin Belousov { 1434de10ffa5SKonstantin Belousov struct cdev_priv *cp; 1435de10ffa5SKonstantin Belousov struct cdev *dev; 1436de10ffa5SKonstantin Belousov void (*cb)(void *); 1437de10ffa5SKonstantin Belousov void *cb_arg; 1438de10ffa5SKonstantin Belousov 1439de10ffa5SKonstantin Belousov dev_lock(); 1440de10ffa5SKonstantin Belousov while (!TAILQ_EMPTY(&dev_ddtr)) { 1441de10ffa5SKonstantin Belousov cp = TAILQ_FIRST(&dev_ddtr); 1442de10ffa5SKonstantin Belousov dev = &cp->cdp_c; 1443de10ffa5SKonstantin Belousov KASSERT(cp->cdp_flags & CDP_SCHED_DTR, 1444de10ffa5SKonstantin Belousov ("cdev %p in dev_destroy_tq without CDP_SCHED_DTR", cp)); 1445de10ffa5SKonstantin Belousov TAILQ_REMOVE(&dev_ddtr, cp, cdp_dtr_list); 1446de10ffa5SKonstantin Belousov cb = cp->cdp_dtr_cb; 1447de10ffa5SKonstantin Belousov cb_arg = cp->cdp_dtr_cb_arg; 1448de10ffa5SKonstantin Belousov destroy_devl(dev); 1449aeeb4202SKonstantin Belousov dev_unlock_and_free(); 1450de10ffa5SKonstantin Belousov dev_rel(dev); 1451de10ffa5SKonstantin Belousov if (cb != NULL) 1452de10ffa5SKonstantin Belousov cb(cb_arg); 1453de10ffa5SKonstantin Belousov dev_lock(); 1454de10ffa5SKonstantin Belousov } 1455de10ffa5SKonstantin Belousov dev_unlock(); 1456de10ffa5SKonstantin Belousov } 1457de10ffa5SKonstantin Belousov 14589d53363bSKonstantin Belousov /* 14599d53363bSKonstantin Belousov * devmtx shall be locked on entry. devmtx will be unlocked after 14609d53363bSKonstantin Belousov * function return. 14619d53363bSKonstantin Belousov */ 14629d53363bSKonstantin Belousov static int 14639d53363bSKonstantin Belousov destroy_dev_sched_cbl(struct cdev *dev, void (*cb)(void *), void *arg) 1464de10ffa5SKonstantin Belousov { 1465de10ffa5SKonstantin Belousov struct cdev_priv *cp; 1466de10ffa5SKonstantin Belousov 14670ac9e27bSConrad Meyer dev_lock_assert_locked(); 146805427aafSKonstantin Belousov cp = cdev2priv(dev); 1469de10ffa5SKonstantin Belousov if (cp->cdp_flags & CDP_SCHED_DTR) { 1470de10ffa5SKonstantin Belousov dev_unlock(); 1471de10ffa5SKonstantin Belousov return (0); 1472de10ffa5SKonstantin Belousov } 1473de10ffa5SKonstantin Belousov dev_refl(dev); 1474de10ffa5SKonstantin Belousov cp->cdp_flags |= CDP_SCHED_DTR; 1475de10ffa5SKonstantin Belousov cp->cdp_dtr_cb = cb; 1476de10ffa5SKonstantin Belousov cp->cdp_dtr_cb_arg = arg; 1477de10ffa5SKonstantin Belousov TAILQ_INSERT_TAIL(&dev_ddtr, cp, cdp_dtr_list); 1478de10ffa5SKonstantin Belousov dev_unlock(); 1479de10ffa5SKonstantin Belousov taskqueue_enqueue(taskqueue_swi_giant, &dev_dtr_task); 1480de10ffa5SKonstantin Belousov return (1); 1481de10ffa5SKonstantin Belousov } 1482de10ffa5SKonstantin Belousov 1483de10ffa5SKonstantin Belousov int 14849d53363bSKonstantin Belousov destroy_dev_sched_cb(struct cdev *dev, void (*cb)(void *), void *arg) 14859d53363bSKonstantin Belousov { 1486cf141467SKonstantin Belousov 14879d53363bSKonstantin Belousov dev_lock(); 14889d53363bSKonstantin Belousov return (destroy_dev_sched_cbl(dev, cb, arg)); 14899d53363bSKonstantin Belousov } 14909d53363bSKonstantin Belousov 14919d53363bSKonstantin Belousov int 1492de10ffa5SKonstantin Belousov destroy_dev_sched(struct cdev *dev) 1493de10ffa5SKonstantin Belousov { 1494cf141467SKonstantin Belousov 1495de10ffa5SKonstantin Belousov return (destroy_dev_sched_cb(dev, NULL, NULL)); 1496de10ffa5SKonstantin Belousov } 1497de10ffa5SKonstantin Belousov 1498de10ffa5SKonstantin Belousov void 1499de10ffa5SKonstantin Belousov destroy_dev_drain(struct cdevsw *csw) 1500de10ffa5SKonstantin Belousov { 1501de10ffa5SKonstantin Belousov 1502de10ffa5SKonstantin Belousov dev_lock(); 1503de10ffa5SKonstantin Belousov while (!LIST_EMPTY(&csw->d_devs)) { 1504de10ffa5SKonstantin Belousov msleep(&csw->d_devs, &devmtx, PRIBIO, "devscd", hz/10); 1505de10ffa5SKonstantin Belousov } 1506de10ffa5SKonstantin Belousov dev_unlock(); 1507de10ffa5SKonstantin Belousov } 1508de10ffa5SKonstantin Belousov 1509de10ffa5SKonstantin Belousov void 1510de10ffa5SKonstantin Belousov drain_dev_clone_events(void) 1511de10ffa5SKonstantin Belousov { 1512de10ffa5SKonstantin Belousov 1513de10ffa5SKonstantin Belousov sx_xlock(&clone_drain_lock); 1514de10ffa5SKonstantin Belousov sx_xunlock(&clone_drain_lock); 1515de10ffa5SKonstantin Belousov } 1516de10ffa5SKonstantin Belousov 15176c5e633cSKonstantin Belousov #include "opt_ddb.h" 15186c5e633cSKonstantin Belousov #ifdef DDB 15196c5e633cSKonstantin Belousov #include <sys/kernel.h> 15206c5e633cSKonstantin Belousov 15216c5e633cSKonstantin Belousov #include <ddb/ddb.h> 15226c5e633cSKonstantin Belousov 15236c5e633cSKonstantin Belousov DB_SHOW_COMMAND(cdev, db_show_cdev) 15246c5e633cSKonstantin Belousov { 15256c5e633cSKonstantin Belousov struct cdev_priv *cdp; 15266c5e633cSKonstantin Belousov struct cdev *dev; 15276c5e633cSKonstantin Belousov u_int flags; 15286c5e633cSKonstantin Belousov char buf[512]; 15296c5e633cSKonstantin Belousov 15306c5e633cSKonstantin Belousov if (!have_addr) { 15316c5e633cSKonstantin Belousov TAILQ_FOREACH(cdp, &cdevp_list, cdp_list) { 15326c5e633cSKonstantin Belousov dev = &cdp->cdp_c; 15336c5e633cSKonstantin Belousov db_printf("%s %p\n", dev->si_name, dev); 15346c5e633cSKonstantin Belousov if (db_pager_quit) 15356c5e633cSKonstantin Belousov break; 15366c5e633cSKonstantin Belousov } 15376c5e633cSKonstantin Belousov return; 15386c5e633cSKonstantin Belousov } 15396c5e633cSKonstantin Belousov 15406c5e633cSKonstantin Belousov dev = (struct cdev *)addr; 15416c5e633cSKonstantin Belousov cdp = cdev2priv(dev); 15426c5e633cSKonstantin Belousov db_printf("dev %s ref %d use %ld thr %ld inuse %u fdpriv %p\n", 15436c5e633cSKonstantin Belousov dev->si_name, dev->si_refcount, dev->si_usecount, 15446c5e633cSKonstantin Belousov dev->si_threadcount, cdp->cdp_inuse, cdp->cdp_fdpriv.lh_first); 15456c5e633cSKonstantin Belousov db_printf("devsw %p si_drv0 %d si_drv1 %p si_drv2 %p\n", 15466c5e633cSKonstantin Belousov dev->si_devsw, dev->si_drv0, dev->si_drv1, dev->si_drv2); 15476c5e633cSKonstantin Belousov flags = dev->si_flags; 15486c5e633cSKonstantin Belousov #define SI_FLAG(flag) do { \ 15496c5e633cSKonstantin Belousov if (flags & (flag)) { \ 15506c5e633cSKonstantin Belousov if (buf[0] != '\0') \ 15516c5e633cSKonstantin Belousov strlcat(buf, ", ", sizeof(buf)); \ 15526c5e633cSKonstantin Belousov strlcat(buf, (#flag) + 3, sizeof(buf)); \ 15536c5e633cSKonstantin Belousov flags &= ~(flag); \ 15546c5e633cSKonstantin Belousov } \ 15556c5e633cSKonstantin Belousov } while (0) 15566c5e633cSKonstantin Belousov buf[0] = '\0'; 15576c5e633cSKonstantin Belousov SI_FLAG(SI_ETERNAL); 15586c5e633cSKonstantin Belousov SI_FLAG(SI_ALIAS); 15596c5e633cSKonstantin Belousov SI_FLAG(SI_NAMED); 15606c5e633cSKonstantin Belousov SI_FLAG(SI_CHILD); 15616c5e633cSKonstantin Belousov SI_FLAG(SI_DUMPDEV); 15626c5e633cSKonstantin Belousov SI_FLAG(SI_CLONELIST); 15636c5e633cSKonstantin Belousov db_printf("si_flags %s\n", buf); 15646c5e633cSKonstantin Belousov 15656c5e633cSKonstantin Belousov flags = cdp->cdp_flags; 15666c5e633cSKonstantin Belousov #define CDP_FLAG(flag) do { \ 15676c5e633cSKonstantin Belousov if (flags & (flag)) { \ 15686c5e633cSKonstantin Belousov if (buf[0] != '\0') \ 15696c5e633cSKonstantin Belousov strlcat(buf, ", ", sizeof(buf)); \ 15706c5e633cSKonstantin Belousov strlcat(buf, (#flag) + 4, sizeof(buf)); \ 15716c5e633cSKonstantin Belousov flags &= ~(flag); \ 15726c5e633cSKonstantin Belousov } \ 15736c5e633cSKonstantin Belousov } while (0) 15746c5e633cSKonstantin Belousov buf[0] = '\0'; 15756c5e633cSKonstantin Belousov CDP_FLAG(CDP_ACTIVE); 15766c5e633cSKonstantin Belousov CDP_FLAG(CDP_SCHED_DTR); 15776c5e633cSKonstantin Belousov db_printf("cdp_flags %s\n", buf); 15786c5e633cSKonstantin Belousov } 15796c5e633cSKonstantin Belousov #endif 1580