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); 549887611b1SWarner 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 668*887c753cSMark Johnston if ((devsw->d_flags & D_NEEDGIANT) != 0) { 669*887c753cSMark Johnston if ((devsw->d_flags & D_GIANTOK) == 0) { 670*887c753cSMark Johnston printf( 671*887c753cSMark Johnston "WARNING: Device \"%s\" is Giant locked and may be " 67200065c76SWarner Losh "deleted before FreeBSD 14.0.\n", 673a921c200SWarner Losh devsw->d_name == NULL ? "???" : devsw->d_name); 674*887c753cSMark Johnston } 675516ad423SPoul-Henning Kamp if (devsw->d_gianttrick == NULL) { 676516ad423SPoul-Henning Kamp memcpy(dsw2, devsw, sizeof *dsw2); 677516ad423SPoul-Henning Kamp devsw->d_gianttrick = dsw2; 678aeeb4202SKonstantin Belousov dsw2 = NULL; 679aeeb4202SKonstantin Belousov } 680516ad423SPoul-Henning Kamp } 681516ad423SPoul-Henning Kamp 682516ad423SPoul-Henning Kamp #define FIXUP(member, noop, giant) \ 683516ad423SPoul-Henning Kamp do { \ 684516ad423SPoul-Henning Kamp if (devsw->member == NULL) { \ 685516ad423SPoul-Henning Kamp devsw->member = noop; \ 686516ad423SPoul-Henning Kamp } else if (devsw->d_flags & D_NEEDGIANT) \ 687516ad423SPoul-Henning Kamp devsw->member = giant; \ 688516ad423SPoul-Henning Kamp } \ 689516ad423SPoul-Henning Kamp while (0) 690516ad423SPoul-Henning Kamp 691516ad423SPoul-Henning Kamp FIXUP(d_open, null_open, giant_open); 692516ad423SPoul-Henning Kamp FIXUP(d_fdopen, NULL, giant_fdopen); 693516ad423SPoul-Henning Kamp FIXUP(d_close, null_close, giant_close); 694516ad423SPoul-Henning Kamp FIXUP(d_read, no_read, giant_read); 695516ad423SPoul-Henning Kamp FIXUP(d_write, no_write, giant_write); 696516ad423SPoul-Henning Kamp FIXUP(d_ioctl, no_ioctl, giant_ioctl); 697516ad423SPoul-Henning Kamp FIXUP(d_poll, no_poll, giant_poll); 698cfd7baceSRobert Noland FIXUP(d_mmap, no_mmap, giant_mmap); 699516ad423SPoul-Henning Kamp FIXUP(d_strategy, no_strategy, giant_strategy); 700516ad423SPoul-Henning Kamp FIXUP(d_kqfilter, no_kqfilter, giant_kqfilter); 70164345f0bSJohn Baldwin FIXUP(d_mmap_single, no_mmap_single, giant_mmap_single); 702516ad423SPoul-Henning Kamp 703b2941431SPoul-Henning Kamp if (devsw->d_dump == NULL) devsw->d_dump = no_dump; 704cd690b60SPoul-Henning Kamp 705cd690b60SPoul-Henning Kamp LIST_INIT(&devsw->d_devs); 706cd690b60SPoul-Henning Kamp 707cd690b60SPoul-Henning Kamp devsw->d_flags |= D_INIT; 708cd690b60SPoul-Henning Kamp 709aeeb4202SKonstantin Belousov if (dsw2 != NULL) 710aeeb4202SKonstantin Belousov cdevsw_free_devlocked(dsw2); 711f1bb758dSKonstantin Belousov return (0); 7122a3faf2fSPoul-Henning Kamp } 71311586717SBrian Somers 714f1bb758dSKonstantin Belousov static int 71568f7a013SJaakko Heinonen prep_devname(struct cdev *dev, const char *fmt, va_list ap) 71668f7a013SJaakko Heinonen { 71768f7a013SJaakko Heinonen int len; 71868f7a013SJaakko Heinonen char *from, *q, *s, *to; 71968f7a013SJaakko Heinonen 7200ac9e27bSConrad Meyer dev_lock_assert_locked(); 72168f7a013SJaakko Heinonen 722852b05c5SEd Schouten len = vsnrprintf(dev->si_name, sizeof(dev->si_name), 32, fmt, ap); 7238fac9b7bSEd Schouten if (len > sizeof(dev->si_name) - 1) 72468f7a013SJaakko Heinonen return (ENAMETOOLONG); 72568f7a013SJaakko Heinonen 72668f7a013SJaakko Heinonen /* Strip leading slashes. */ 7278fac9b7bSEd Schouten for (from = dev->si_name; *from == '/'; from++) 72868f7a013SJaakko Heinonen ; 72968f7a013SJaakko Heinonen 7308fac9b7bSEd Schouten for (to = dev->si_name; *from != '\0'; from++, to++) { 731b1e1f725SJaakko Heinonen /* 732b1e1f725SJaakko Heinonen * Spaces and double quotation marks cause 733b1e1f725SJaakko Heinonen * problems for the devctl(4) protocol. 734b1e1f725SJaakko Heinonen * Reject names containing those characters. 735b1e1f725SJaakko Heinonen */ 736b1e1f725SJaakko Heinonen if (isspace(*from) || *from == '"') 737b1e1f725SJaakko Heinonen return (EINVAL); 73868f7a013SJaakko Heinonen /* Treat multiple sequential slashes as single. */ 73968f7a013SJaakko Heinonen while (from[0] == '/' && from[1] == '/') 74068f7a013SJaakko Heinonen from++; 74168f7a013SJaakko Heinonen /* Trailing slash is considered invalid. */ 74268f7a013SJaakko Heinonen if (from[0] == '/' && from[1] == '\0') 74368f7a013SJaakko Heinonen return (EINVAL); 74468f7a013SJaakko Heinonen *to = *from; 74568f7a013SJaakko Heinonen } 74668f7a013SJaakko Heinonen *to = '\0'; 74768f7a013SJaakko Heinonen 7488fac9b7bSEd Schouten if (dev->si_name[0] == '\0') 74968f7a013SJaakko Heinonen return (EINVAL); 75068f7a013SJaakko Heinonen 75168f7a013SJaakko Heinonen /* Disallow "." and ".." components. */ 7528fac9b7bSEd Schouten for (s = dev->si_name;;) { 75368f7a013SJaakko Heinonen for (q = s; *q != '/' && *q != '\0'; q++) 75468f7a013SJaakko Heinonen ; 75568f7a013SJaakko Heinonen if (q - s == 1 && s[0] == '.') 75668f7a013SJaakko Heinonen return (EINVAL); 75768f7a013SJaakko Heinonen if (q - s == 2 && s[0] == '.' && s[1] == '.') 75868f7a013SJaakko Heinonen return (EINVAL); 75968f7a013SJaakko Heinonen if (*q != '/') 76068f7a013SJaakko Heinonen break; 76168f7a013SJaakko Heinonen s = q + 1; 76268f7a013SJaakko Heinonen } 76368f7a013SJaakko Heinonen 7648fac9b7bSEd Schouten if (devfs_dev_exists(dev->si_name) != 0) 76568f7a013SJaakko Heinonen return (EEXIST); 76668f7a013SJaakko Heinonen 76768f7a013SJaakko Heinonen return (0); 76868f7a013SJaakko Heinonen } 76968f7a013SJaakko Heinonen 77048ce5d4cSKonstantin Belousov void 77148ce5d4cSKonstantin Belousov make_dev_args_init_impl(struct make_dev_args *args, size_t sz) 77248ce5d4cSKonstantin Belousov { 77348ce5d4cSKonstantin Belousov 77448ce5d4cSKonstantin Belousov bzero(args, sz); 77548ce5d4cSKonstantin Belousov args->mda_size = sz; 77648ce5d4cSKonstantin Belousov } 77748ce5d4cSKonstantin Belousov 77868f7a013SJaakko Heinonen static int 77948ce5d4cSKonstantin Belousov make_dev_sv(struct make_dev_args *args1, struct cdev **dres, 78048ce5d4cSKonstantin Belousov const char *fmt, va_list ap) 7812a3faf2fSPoul-Henning Kamp { 78268f7a013SJaakko Heinonen struct cdev *dev, *dev_new; 78348ce5d4cSKonstantin Belousov struct make_dev_args args; 78468f7a013SJaakko Heinonen int res; 7852a3faf2fSPoul-Henning Kamp 78648ce5d4cSKonstantin Belousov bzero(&args, sizeof(args)); 78748ce5d4cSKonstantin Belousov if (sizeof(args) < args1->mda_size) 78848ce5d4cSKonstantin Belousov return (EINVAL); 78948ce5d4cSKonstantin Belousov bcopy(args1, &args, args1->mda_size); 79048ce5d4cSKonstantin Belousov KASSERT((args.mda_flags & MAKEDEV_WAITOK) == 0 || 79148ce5d4cSKonstantin Belousov (args.mda_flags & MAKEDEV_NOWAIT) == 0, 79248ce5d4cSKonstantin Belousov ("make_dev_sv: both WAITOK and NOWAIT specified")); 79348ce5d4cSKonstantin Belousov dev_new = devfs_alloc(args.mda_flags); 79468f7a013SJaakko Heinonen if (dev_new == NULL) 795f1bb758dSKonstantin Belousov return (ENOMEM); 796027b1f71SPoul-Henning Kamp dev_lock(); 79748ce5d4cSKonstantin Belousov res = prep_cdevsw(args.mda_devsw, args.mda_flags); 798f1bb758dSKonstantin Belousov if (res != 0) { 799d2ba618aSKonstantin Belousov dev_unlock(); 80068f7a013SJaakko Heinonen devfs_free(dev_new); 801f1bb758dSKonstantin Belousov return (res); 802d2ba618aSKonstantin Belousov } 80348ce5d4cSKonstantin Belousov dev = newdev(&args, dev_new); 804ff91cc99SJaakko Heinonen if ((dev->si_flags & SI_NAMED) == 0) { 80568f7a013SJaakko Heinonen res = prep_devname(dev, fmt, ap); 80668f7a013SJaakko Heinonen if (res != 0) { 80748ce5d4cSKonstantin Belousov if ((args.mda_flags & MAKEDEV_CHECKNAME) == 0) { 80868f7a013SJaakko Heinonen panic( 80948ce5d4cSKonstantin Belousov "make_dev_sv: bad si_name (error=%d, si_name=%s)", 81068f7a013SJaakko Heinonen res, dev->si_name); 81168f7a013SJaakko Heinonen } 81268f7a013SJaakko Heinonen if (dev == dev_new) { 81368f7a013SJaakko Heinonen LIST_REMOVE(dev, si_list); 81468f7a013SJaakko Heinonen dev_unlock(); 81568f7a013SJaakko Heinonen devfs_free(dev); 816889dffbaSKonstantin Belousov } else 817889dffbaSKonstantin Belousov dev_unlock(); 81868f7a013SJaakko Heinonen return (res); 81968f7a013SJaakko Heinonen } 820ff91cc99SJaakko Heinonen } 82148ce5d4cSKonstantin Belousov if ((args.mda_flags & MAKEDEV_REF) != 0) 822de10ffa5SKonstantin Belousov dev_refl(dev); 82348ce5d4cSKonstantin Belousov if ((args.mda_flags & MAKEDEV_ETERNAL) != 0) 8243979450bSKonstantin Belousov dev->si_flags |= SI_ETERNAL; 825cd690b60SPoul-Henning Kamp KASSERT(!(dev->si_flags & SI_NAMED), 826ff7284eeSPoul-Henning Kamp ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)", 82748ce5d4cSKonstantin Belousov args.mda_devsw->d_name, dev2unit(dev), devtoname(dev))); 8285ef2707eSPoul-Henning Kamp dev->si_flags |= SI_NAMED; 82948ce5d4cSKonstantin Belousov if (args.mda_cr != NULL) 83048ce5d4cSKonstantin Belousov dev->si_cred = crhold(args.mda_cr); 83148ce5d4cSKonstantin Belousov dev->si_uid = args.mda_uid; 83248ce5d4cSKonstantin Belousov dev->si_gid = args.mda_gid; 83348ce5d4cSKonstantin Belousov dev->si_mode = args.mda_mode; 8341744fcd0SJulian Elischer 8359285a87eSPoul-Henning Kamp devfs_create(dev); 83609828ba9SKonstantin Belousov clean_unrhdrl(devfs_inos); 837aeeb4202SKonstantin Belousov dev_unlock_and_free(); 83848504cc2SKonstantin Belousov 83948ce5d4cSKonstantin Belousov notify_create(dev, args.mda_flags); 84048504cc2SKonstantin Belousov 841f1bb758dSKonstantin Belousov *dres = dev; 842f1bb758dSKonstantin Belousov return (0); 8433f54a085SPoul-Henning Kamp } 8443f54a085SPoul-Henning Kamp 84548ce5d4cSKonstantin Belousov int 84648ce5d4cSKonstantin Belousov make_dev_s(struct make_dev_args *args, struct cdev **dres, 84748ce5d4cSKonstantin Belousov const char *fmt, ...) 84848ce5d4cSKonstantin Belousov { 84948ce5d4cSKonstantin Belousov va_list ap; 85048ce5d4cSKonstantin Belousov int res; 85148ce5d4cSKonstantin Belousov 85248ce5d4cSKonstantin Belousov va_start(ap, fmt); 85348ce5d4cSKonstantin Belousov res = make_dev_sv(args, dres, fmt, ap); 85448ce5d4cSKonstantin Belousov va_end(ap); 85548ce5d4cSKonstantin Belousov return (res); 85648ce5d4cSKonstantin Belousov } 85748ce5d4cSKonstantin Belousov 85848ce5d4cSKonstantin Belousov static int 85948ce5d4cSKonstantin Belousov make_dev_credv(int flags, struct cdev **dres, struct cdevsw *devsw, int unit, 86048ce5d4cSKonstantin Belousov struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, 86148ce5d4cSKonstantin Belousov va_list ap) 86248ce5d4cSKonstantin Belousov { 86348ce5d4cSKonstantin Belousov struct make_dev_args args; 86448ce5d4cSKonstantin Belousov 86548ce5d4cSKonstantin Belousov make_dev_args_init(&args); 86648ce5d4cSKonstantin Belousov args.mda_flags = flags; 86748ce5d4cSKonstantin Belousov args.mda_devsw = devsw; 86848ce5d4cSKonstantin Belousov args.mda_cr = cr; 86948ce5d4cSKonstantin Belousov args.mda_uid = uid; 87048ce5d4cSKonstantin Belousov args.mda_gid = gid; 87148ce5d4cSKonstantin Belousov args.mda_mode = mode; 87248ce5d4cSKonstantin Belousov args.mda_unit = unit; 87348ce5d4cSKonstantin Belousov return (make_dev_sv(&args, dres, fmt, ap)); 87448ce5d4cSKonstantin Belousov } 87548ce5d4cSKonstantin Belousov 876d26dd2d9SRobert Watson struct cdev * 877edde8745SEd Schouten make_dev(struct cdevsw *devsw, int unit, uid_t uid, gid_t gid, int mode, 878d26dd2d9SRobert Watson const char *fmt, ...) 879d26dd2d9SRobert Watson { 880d26dd2d9SRobert Watson struct cdev *dev; 881d26dd2d9SRobert Watson va_list ap; 8826fa5abfdSMatt Macy int res __unused; 883d26dd2d9SRobert Watson 884d26dd2d9SRobert Watson va_start(ap, fmt); 8856fa5abfdSMatt Macy res = make_dev_credv(0, &dev, devsw, unit, NULL, uid, gid, mode, fmt, 8866fa5abfdSMatt Macy ap); 887d26dd2d9SRobert Watson va_end(ap); 88868f7a013SJaakko Heinonen KASSERT(res == 0 && dev != NULL, 88968f7a013SJaakko Heinonen ("make_dev: failed make_dev_credv (error=%d)", res)); 890d26dd2d9SRobert Watson return (dev); 891d26dd2d9SRobert Watson } 892d26dd2d9SRobert Watson 893d26dd2d9SRobert Watson struct cdev * 894edde8745SEd Schouten make_dev_cred(struct cdevsw *devsw, int unit, struct ucred *cr, uid_t uid, 895d26dd2d9SRobert Watson gid_t gid, int mode, const char *fmt, ...) 896d26dd2d9SRobert Watson { 897d26dd2d9SRobert Watson struct cdev *dev; 898d26dd2d9SRobert Watson va_list ap; 8996fa5abfdSMatt Macy int res __unused; 900d26dd2d9SRobert Watson 901d26dd2d9SRobert Watson va_start(ap, fmt); 9026fa5abfdSMatt Macy res = make_dev_credv(0, &dev, devsw, unit, cr, uid, gid, mode, fmt, ap); 903de10ffa5SKonstantin Belousov va_end(ap); 904de10ffa5SKonstantin Belousov 905f1bb758dSKonstantin Belousov KASSERT(res == 0 && dev != NULL, 90668f7a013SJaakko Heinonen ("make_dev_cred: failed make_dev_credv (error=%d)", res)); 907de10ffa5SKonstantin Belousov return (dev); 908de10ffa5SKonstantin Belousov } 909de10ffa5SKonstantin Belousov 910de10ffa5SKonstantin Belousov struct cdev * 911f1bb758dSKonstantin Belousov make_dev_credf(int flags, struct cdevsw *devsw, int unit, struct ucred *cr, 912f1bb758dSKonstantin Belousov uid_t uid, gid_t gid, int mode, const char *fmt, ...) 913de10ffa5SKonstantin Belousov { 914de10ffa5SKonstantin Belousov struct cdev *dev; 915de10ffa5SKonstantin Belousov va_list ap; 916f1bb758dSKonstantin Belousov int res; 917de10ffa5SKonstantin Belousov 918de10ffa5SKonstantin Belousov va_start(ap, fmt); 919f1bb758dSKonstantin Belousov res = make_dev_credv(flags, &dev, devsw, unit, cr, uid, gid, mode, 920de10ffa5SKonstantin Belousov fmt, ap); 921d26dd2d9SRobert Watson va_end(ap); 922d26dd2d9SRobert Watson 92368f7a013SJaakko Heinonen KASSERT(((flags & MAKEDEV_NOWAIT) != 0 && res == ENOMEM) || 92468f7a013SJaakko Heinonen ((flags & MAKEDEV_CHECKNAME) != 0 && res != ENOMEM) || res == 0, 92568f7a013SJaakko Heinonen ("make_dev_credf: failed make_dev_credv (error=%d)", res)); 926f1bb758dSKonstantin Belousov return (res == 0 ? dev : NULL); 927f1bb758dSKonstantin Belousov } 928f1bb758dSKonstantin Belousov 929f1bb758dSKonstantin Belousov int 9302e983aceSEd Schouten make_dev_p(int flags, struct cdev **cdev, struct cdevsw *devsw, 931f1bb758dSKonstantin Belousov struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, ...) 932f1bb758dSKonstantin Belousov { 933f1bb758dSKonstantin Belousov va_list ap; 934f1bb758dSKonstantin Belousov int res; 935f1bb758dSKonstantin Belousov 936f1bb758dSKonstantin Belousov va_start(ap, fmt); 9372e983aceSEd Schouten res = make_dev_credv(flags, cdev, devsw, 0, cr, uid, gid, mode, 938f1bb758dSKonstantin Belousov fmt, ap); 939f1bb758dSKonstantin Belousov va_end(ap); 940f1bb758dSKonstantin Belousov 94168f7a013SJaakko Heinonen KASSERT(((flags & MAKEDEV_NOWAIT) != 0 && res == ENOMEM) || 94268f7a013SJaakko Heinonen ((flags & MAKEDEV_CHECKNAME) != 0 && res != ENOMEM) || res == 0, 94368f7a013SJaakko Heinonen ("make_dev_p: failed make_dev_credv (error=%d)", res)); 944f1bb758dSKonstantin Belousov return (res); 945d26dd2d9SRobert Watson } 946d26dd2d9SRobert Watson 947e606a3c6SPoul-Henning Kamp static void 948e606a3c6SPoul-Henning Kamp dev_dependsl(struct cdev *pdev, struct cdev *cdev) 949e606a3c6SPoul-Henning Kamp { 950e606a3c6SPoul-Henning Kamp 951e606a3c6SPoul-Henning Kamp cdev->si_parent = pdev; 952e606a3c6SPoul-Henning Kamp cdev->si_flags |= SI_CHILD; 953e606a3c6SPoul-Henning Kamp LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings); 954e606a3c6SPoul-Henning Kamp } 955e606a3c6SPoul-Henning Kamp 9563344c5a1SPoul-Henning Kamp void 95789c9c53dSPoul-Henning Kamp dev_depends(struct cdev *pdev, struct cdev *cdev) 9583344c5a1SPoul-Henning Kamp { 9593344c5a1SPoul-Henning Kamp 960a0e78d2eSPoul-Henning Kamp dev_lock(); 961e606a3c6SPoul-Henning Kamp dev_dependsl(pdev, cdev); 962a0e78d2eSPoul-Henning Kamp dev_unlock(); 9633344c5a1SPoul-Henning Kamp } 9643344c5a1SPoul-Henning Kamp 965b50a7799SAndrey V. Elsukov static int 966b50a7799SAndrey V. Elsukov make_dev_alias_v(int flags, struct cdev **cdev, struct cdev *pdev, 967b50a7799SAndrey V. Elsukov const char *fmt, va_list ap) 9683f54a085SPoul-Henning Kamp { 96989c9c53dSPoul-Henning Kamp struct cdev *dev; 97068f7a013SJaakko Heinonen int error; 9713f54a085SPoul-Henning Kamp 972b50a7799SAndrey V. Elsukov KASSERT(pdev != NULL, ("make_dev_alias_v: pdev is NULL")); 973b50a7799SAndrey V. Elsukov KASSERT((flags & MAKEDEV_WAITOK) == 0 || (flags & MAKEDEV_NOWAIT) == 0, 974b50a7799SAndrey V. Elsukov ("make_dev_alias_v: both WAITOK and NOWAIT specified")); 975b50a7799SAndrey V. Elsukov KASSERT((flags & ~(MAKEDEV_WAITOK | MAKEDEV_NOWAIT | 976b50a7799SAndrey V. Elsukov MAKEDEV_CHECKNAME)) == 0, 977b50a7799SAndrey V. Elsukov ("make_dev_alias_v: invalid flags specified (flags=%02x)", flags)); 978b50a7799SAndrey V. Elsukov 979b50a7799SAndrey V. Elsukov dev = devfs_alloc(flags); 980b50a7799SAndrey V. Elsukov if (dev == NULL) 981b50a7799SAndrey V. Elsukov return (ENOMEM); 982a0e78d2eSPoul-Henning Kamp dev_lock(); 9833f54a085SPoul-Henning Kamp dev->si_flags |= SI_ALIAS; 98468f7a013SJaakko Heinonen error = prep_devname(dev, fmt, ap); 98568f7a013SJaakko Heinonen if (error != 0) { 986b50a7799SAndrey V. Elsukov if ((flags & MAKEDEV_CHECKNAME) == 0) { 987b50a7799SAndrey V. Elsukov panic("make_dev_alias_v: bad si_name " 988b50a7799SAndrey V. Elsukov "(error=%d, si_name=%s)", error, dev->si_name); 989b50a7799SAndrey V. Elsukov } 990b50a7799SAndrey V. Elsukov dev_unlock(); 991b50a7799SAndrey V. Elsukov devfs_free(dev); 992b50a7799SAndrey V. Elsukov return (error); 99368f7a013SJaakko Heinonen } 99468f7a013SJaakko Heinonen dev->si_flags |= SI_NAMED; 9959285a87eSPoul-Henning Kamp devfs_create(dev); 996ae95dc62SKonstantin Belousov dev_dependsl(pdev, dev); 99709828ba9SKonstantin Belousov clean_unrhdrl(devfs_inos); 998a0e78d2eSPoul-Henning Kamp dev_unlock(); 99948504cc2SKonstantin Belousov 1000b50a7799SAndrey V. Elsukov notify_create(dev, flags); 1001b50a7799SAndrey V. Elsukov *cdev = dev; 100248504cc2SKonstantin Belousov 1003b50a7799SAndrey V. Elsukov return (0); 1004b50a7799SAndrey V. Elsukov } 1005b50a7799SAndrey V. Elsukov 1006b50a7799SAndrey V. Elsukov struct cdev * 1007b50a7799SAndrey V. Elsukov make_dev_alias(struct cdev *pdev, const char *fmt, ...) 1008b50a7799SAndrey V. Elsukov { 1009b50a7799SAndrey V. Elsukov struct cdev *dev; 1010b50a7799SAndrey V. Elsukov va_list ap; 10116fa5abfdSMatt Macy int res __unused; 10126fa5abfdSMatt Macy 1013b50a7799SAndrey V. Elsukov va_start(ap, fmt); 10146fa5abfdSMatt Macy res = make_dev_alias_v(MAKEDEV_WAITOK, &dev, pdev, fmt, ap); 1015b50a7799SAndrey V. Elsukov va_end(ap); 1016b50a7799SAndrey V. Elsukov 1017b50a7799SAndrey V. Elsukov KASSERT(res == 0 && dev != NULL, 1018b50a7799SAndrey V. Elsukov ("make_dev_alias: failed make_dev_alias_v (error=%d)", res)); 10190ef1c826SPoul-Henning Kamp return (dev); 10200ef1c826SPoul-Henning Kamp } 10210ef1c826SPoul-Henning Kamp 1022b50a7799SAndrey V. Elsukov int 1023b50a7799SAndrey V. Elsukov make_dev_alias_p(int flags, struct cdev **cdev, struct cdev *pdev, 1024b50a7799SAndrey V. Elsukov const char *fmt, ...) 1025b50a7799SAndrey V. Elsukov { 1026b50a7799SAndrey V. Elsukov va_list ap; 1027b50a7799SAndrey V. Elsukov int res; 1028b50a7799SAndrey V. Elsukov 1029b50a7799SAndrey V. Elsukov va_start(ap, fmt); 1030b50a7799SAndrey V. Elsukov res = make_dev_alias_v(flags, cdev, pdev, fmt, ap); 1031b50a7799SAndrey V. Elsukov va_end(ap); 1032b50a7799SAndrey V. Elsukov return (res); 1033b50a7799SAndrey V. Elsukov } 1034b50a7799SAndrey V. Elsukov 1035aa76615dSJustin T. Gibbs int 1036aa76615dSJustin T. Gibbs make_dev_physpath_alias(int flags, struct cdev **cdev, struct cdev *pdev, 1037aa76615dSJustin T. Gibbs struct cdev *old_alias, const char *physpath) 1038aa76615dSJustin T. Gibbs { 1039aa76615dSJustin T. Gibbs char *devfspath; 1040aa76615dSJustin T. Gibbs int physpath_len; 1041aa76615dSJustin T. Gibbs int max_parentpath_len; 1042aa76615dSJustin T. Gibbs int parentpath_len; 1043aa76615dSJustin T. Gibbs int devfspathbuf_len; 1044aa76615dSJustin T. Gibbs int mflags; 1045aa76615dSJustin T. Gibbs int ret; 1046aa76615dSJustin T. Gibbs 1047aa76615dSJustin T. Gibbs *cdev = NULL; 1048aa76615dSJustin T. Gibbs devfspath = NULL; 1049aa76615dSJustin T. Gibbs physpath_len = strlen(physpath); 1050aa76615dSJustin T. Gibbs ret = EINVAL; 1051aa76615dSJustin T. Gibbs if (physpath_len == 0) 1052aa76615dSJustin T. Gibbs goto out; 1053aa76615dSJustin T. Gibbs 1054aa76615dSJustin T. Gibbs if (strncmp("id1,", physpath, 4) == 0) { 1055aa76615dSJustin T. Gibbs physpath += 4; 1056aa76615dSJustin T. Gibbs physpath_len -= 4; 1057aa76615dSJustin T. Gibbs if (physpath_len == 0) 1058aa76615dSJustin T. Gibbs goto out; 1059aa76615dSJustin T. Gibbs } 1060aa76615dSJustin T. Gibbs 1061aa76615dSJustin T. Gibbs max_parentpath_len = SPECNAMELEN - physpath_len - /*/*/1; 1062aa76615dSJustin T. Gibbs parentpath_len = strlen(pdev->si_name); 1063aa76615dSJustin T. Gibbs if (max_parentpath_len < parentpath_len) { 106420654f4eSAlexander Motin if (bootverbose) 106520654f4eSAlexander Motin printf("WARNING: Unable to alias %s " 1066aa76615dSJustin T. Gibbs "to %s/%s - path too long\n", 1067aa76615dSJustin T. Gibbs pdev->si_name, physpath, pdev->si_name); 1068aa76615dSJustin T. Gibbs ret = ENAMETOOLONG; 1069aa76615dSJustin T. Gibbs goto out; 1070aa76615dSJustin T. Gibbs } 1071aa76615dSJustin T. Gibbs 1072aa76615dSJustin T. Gibbs mflags = (flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK; 1073aa76615dSJustin T. Gibbs devfspathbuf_len = physpath_len + /*/*/1 + parentpath_len + /*NUL*/1; 1074aa76615dSJustin T. Gibbs devfspath = malloc(devfspathbuf_len, M_DEVBUF, mflags); 1075aa76615dSJustin T. Gibbs if (devfspath == NULL) { 1076aa76615dSJustin T. Gibbs ret = ENOMEM; 1077aa76615dSJustin T. Gibbs goto out; 1078aa76615dSJustin T. Gibbs } 1079aa76615dSJustin T. Gibbs 1080aa76615dSJustin T. Gibbs sprintf(devfspath, "%s/%s", physpath, pdev->si_name); 10814d651f4eSKonstantin Belousov if (old_alias != NULL && strcmp(old_alias->si_name, devfspath) == 0) { 1082aa76615dSJustin T. Gibbs /* Retain the existing alias. */ 1083aa76615dSJustin T. Gibbs *cdev = old_alias; 1084aa76615dSJustin T. Gibbs old_alias = NULL; 1085aa76615dSJustin T. Gibbs ret = 0; 1086aa76615dSJustin T. Gibbs } else { 1087f403cfb1SKonstantin Belousov ret = make_dev_alias_p(flags, cdev, pdev, "%s", devfspath); 1088aa76615dSJustin T. Gibbs } 1089aa76615dSJustin T. Gibbs out: 1090aa76615dSJustin T. Gibbs if (old_alias != NULL) 1091aa76615dSJustin T. Gibbs destroy_dev(old_alias); 1092aa76615dSJustin T. Gibbs if (devfspath != NULL) 1093aa76615dSJustin T. Gibbs free(devfspath, M_DEVBUF); 1094aa76615dSJustin T. Gibbs return (ret); 1095aa76615dSJustin T. Gibbs } 1096aa76615dSJustin T. Gibbs 1097cd690b60SPoul-Henning Kamp static void 1098aa2f6ddcSPoul-Henning Kamp destroy_devl(struct cdev *dev) 1099d137acccSPoul-Henning Kamp { 1100743cd76aSPoul-Henning Kamp struct cdevsw *csw; 11010bad52e1SHans Petter Selasky struct cdev_privdata *p; 11023b50dff5SKonstantin Belousov struct cdev_priv *cdp; 1103743cd76aSPoul-Henning Kamp 11040ac9e27bSConrad Meyer dev_lock_assert_locked(); 1105743cd76aSPoul-Henning Kamp KASSERT(dev->si_flags & SI_NAMED, 11066bfa9a2dSEd Schouten ("WARNING: Driver mistake: destroy_dev on %d\n", dev2unit(dev))); 11073979450bSKonstantin Belousov KASSERT((dev->si_flags & SI_ETERNAL) == 0, 11083979450bSKonstantin Belousov ("WARNING: Driver mistake: destroy_dev on eternal %d\n", 11093979450bSKonstantin Belousov dev2unit(dev))); 11105ef2707eSPoul-Henning Kamp 11113b50dff5SKonstantin Belousov cdp = cdev2priv(dev); 11123b50dff5SKonstantin Belousov if ((cdp->cdp_flags & CDP_UNREF_DTR) == 0) { 11133b50dff5SKonstantin Belousov /* 11143b50dff5SKonstantin Belousov * Avoid race with dev_rel(), e.g. from the populate 11153b50dff5SKonstantin Belousov * loop. If CDP_UNREF_DTR flag is set, the reference 11163b50dff5SKonstantin Belousov * to be dropped at the end of destroy_devl() was 11173b50dff5SKonstantin Belousov * already taken by delist_dev_locked(). 11183b50dff5SKonstantin Belousov */ 11193b50dff5SKonstantin Belousov dev_refl(dev); 11203b50dff5SKonstantin Belousov 11219285a87eSPoul-Henning Kamp devfs_destroy(dev); 11223b50dff5SKonstantin Belousov } 1123cd690b60SPoul-Henning Kamp 1124cd690b60SPoul-Henning Kamp /* Remove name marking */ 1125b0b03348SPoul-Henning Kamp dev->si_flags &= ~SI_NAMED; 1126b0b03348SPoul-Henning Kamp 1127cd690b60SPoul-Henning Kamp /* If we are a child, remove us from the parents list */ 11283344c5a1SPoul-Henning Kamp if (dev->si_flags & SI_CHILD) { 11293344c5a1SPoul-Henning Kamp LIST_REMOVE(dev, si_siblings); 11303344c5a1SPoul-Henning Kamp dev->si_flags &= ~SI_CHILD; 11313344c5a1SPoul-Henning Kamp } 1132cd690b60SPoul-Henning Kamp 1133cd690b60SPoul-Henning Kamp /* Kill our children */ 11343344c5a1SPoul-Henning Kamp while (!LIST_EMPTY(&dev->si_children)) 1135aa2f6ddcSPoul-Henning Kamp destroy_devl(LIST_FIRST(&dev->si_children)); 1136cd690b60SPoul-Henning Kamp 1137cd690b60SPoul-Henning Kamp /* Remove from clone list */ 1138b0b03348SPoul-Henning Kamp if (dev->si_flags & SI_CLONELIST) { 1139b0b03348SPoul-Henning Kamp LIST_REMOVE(dev, si_clone); 1140b0b03348SPoul-Henning Kamp dev->si_flags &= ~SI_CLONELIST; 1141b0b03348SPoul-Henning Kamp } 1142cd690b60SPoul-Henning Kamp 1143a02cab33SMateusz Guzik mtx_lock(&cdp->cdp_threadlock); 1144743cd76aSPoul-Henning Kamp csw = dev->si_devsw; 11451abf2c36SBrian Feldman dev->si_devsw = NULL; /* already NULL for SI_ALIAS */ 11461abf2c36SBrian Feldman while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) { 1147743cd76aSPoul-Henning Kamp csw->d_purge(dev); 1148a02cab33SMateusz Guzik mtx_unlock(&cdp->cdp_threadlock); 1149743cd76aSPoul-Henning Kamp msleep(csw, &devmtx, PRIBIO, "devprg", hz/10); 1150a02cab33SMateusz Guzik mtx_lock(&cdp->cdp_threadlock); 1151d595182fSPoul-Henning Kamp if (dev->si_threadcount) 1152d595182fSPoul-Henning Kamp printf("Still %lu threads in %s\n", 1153d595182fSPoul-Henning Kamp dev->si_threadcount, devtoname(dev)); 1154743cd76aSPoul-Henning Kamp } 1155e0c33ad5STor Egge while (dev->si_threadcount != 0) { 1156e0c33ad5STor Egge /* Use unique dummy wait ident */ 1157a02cab33SMateusz Guzik mtx_unlock(&cdp->cdp_threadlock); 1158e0c33ad5STor Egge msleep(&csw, &devmtx, PRIBIO, "devdrn", hz / 10); 1159a02cab33SMateusz Guzik mtx_lock(&cdp->cdp_threadlock); 1160e0c33ad5STor Egge } 1161743cd76aSPoul-Henning Kamp 1162a02cab33SMateusz Guzik mtx_unlock(&cdp->cdp_threadlock); 116382f4d640SKonstantin Belousov dev_unlock(); 11642793ea13SHans Petter Selasky if ((cdp->cdp_flags & CDP_UNREF_DTR) == 0) { 11652793ea13SHans Petter Selasky /* avoid out of order notify events */ 116648504cc2SKonstantin Belousov notify_destroy(dev); 11672793ea13SHans Petter Selasky } 116882f4d640SKonstantin Belousov mtx_lock(&cdevpriv_mtx); 11692793ea13SHans Petter Selasky while ((p = LIST_FIRST(&cdp->cdp_fdpriv)) != NULL) { 117082f4d640SKonstantin Belousov devfs_destroy_cdevpriv(p); 117182f4d640SKonstantin Belousov mtx_lock(&cdevpriv_mtx); 117282f4d640SKonstantin Belousov } 117382f4d640SKonstantin Belousov mtx_unlock(&cdevpriv_mtx); 117482f4d640SKonstantin Belousov dev_lock(); 117548504cc2SKonstantin Belousov 1176743cd76aSPoul-Henning Kamp dev->si_drv1 = 0; 1177743cd76aSPoul-Henning Kamp dev->si_drv2 = 0; 1178743cd76aSPoul-Henning Kamp 1179cd690b60SPoul-Henning Kamp if (!(dev->si_flags & SI_ALIAS)) { 1180cd690b60SPoul-Henning Kamp /* Remove from cdevsw list */ 1181cd690b60SPoul-Henning Kamp LIST_REMOVE(dev, si_list); 1182cd690b60SPoul-Henning Kamp 1183e606a3c6SPoul-Henning Kamp /* If cdevsw has no more struct cdev *'s, clean it */ 1184de10ffa5SKonstantin Belousov if (LIST_EMPTY(&csw->d_devs)) { 1185a5993c33SPoul-Henning Kamp fini_cdevsw(csw); 1186de10ffa5SKonstantin Belousov wakeup(&csw->d_devs); 1187de10ffa5SKonstantin Belousov } 1188cd690b60SPoul-Henning Kamp } 11895ef2707eSPoul-Henning Kamp dev->si_flags &= ~SI_ALIAS; 11903b50dff5SKonstantin Belousov cdp->cdp_flags &= ~CDP_UNREF_DTR; 11913b50dff5SKonstantin Belousov dev->si_refcount--; 1192743cd76aSPoul-Henning Kamp 11933b50dff5SKonstantin Belousov if (dev->si_refcount > 0) 1194cd690b60SPoul-Henning Kamp LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list); 11953b50dff5SKonstantin Belousov else 11969bc911d4SKonstantin Belousov dev_free_devlocked(dev); 1197d137acccSPoul-Henning Kamp } 1198cd690b60SPoul-Henning Kamp 119907dbde67SHans Petter Selasky static void 120007dbde67SHans Petter Selasky delist_dev_locked(struct cdev *dev) 120107dbde67SHans Petter Selasky { 12023b50dff5SKonstantin Belousov struct cdev_priv *cdp; 120307dbde67SHans Petter Selasky struct cdev *child; 12043b50dff5SKonstantin Belousov 12050ac9e27bSConrad Meyer dev_lock_assert_locked(); 12063b50dff5SKonstantin Belousov cdp = cdev2priv(dev); 12073b50dff5SKonstantin Belousov if ((cdp->cdp_flags & CDP_UNREF_DTR) != 0) 12083b50dff5SKonstantin Belousov return; 12093b50dff5SKonstantin Belousov cdp->cdp_flags |= CDP_UNREF_DTR; 12103b50dff5SKonstantin Belousov dev_refl(dev); 121107dbde67SHans Petter Selasky devfs_destroy(dev); 121207dbde67SHans Petter Selasky LIST_FOREACH(child, &dev->si_children, si_siblings) 121307dbde67SHans Petter Selasky delist_dev_locked(child); 12142793ea13SHans Petter Selasky dev_unlock(); 12152793ea13SHans Petter Selasky /* ensure the destroy event is queued in order */ 12162793ea13SHans Petter Selasky notify_destroy(dev); 12172793ea13SHans Petter Selasky dev_lock(); 121807dbde67SHans Petter Selasky } 121907dbde67SHans Petter Selasky 12202793ea13SHans Petter Selasky /* 12212793ea13SHans Petter Selasky * This function will delist a character device and its children from 12222793ea13SHans Petter Selasky * the directory listing and create a destroy event without waiting 12232793ea13SHans Petter Selasky * for all character device references to go away. At some later point 12242793ea13SHans Petter Selasky * destroy_dev() must be called to complete the character device 12252793ea13SHans Petter Selasky * destruction. After calling this function the character device name 12262793ea13SHans Petter Selasky * can instantly be re-used. 12272793ea13SHans Petter Selasky */ 122807dbde67SHans Petter Selasky void 122907dbde67SHans Petter Selasky delist_dev(struct cdev *dev) 123007dbde67SHans Petter Selasky { 12313b50dff5SKonstantin Belousov 12322793ea13SHans Petter Selasky WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "delist_dev"); 123307dbde67SHans Petter Selasky dev_lock(); 123407dbde67SHans Petter Selasky delist_dev_locked(dev); 123507dbde67SHans Petter Selasky dev_unlock(); 123607dbde67SHans Petter Selasky } 123707dbde67SHans Petter Selasky 1238cd690b60SPoul-Henning Kamp void 123989c9c53dSPoul-Henning Kamp destroy_dev(struct cdev *dev) 1240cd690b60SPoul-Henning Kamp { 1241cd690b60SPoul-Henning Kamp 1242b7a813fcSKonstantin Belousov WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "destroy_dev"); 1243a0e78d2eSPoul-Henning Kamp dev_lock(); 1244aa2f6ddcSPoul-Henning Kamp destroy_devl(dev); 12459bc911d4SKonstantin Belousov dev_unlock_and_free(); 1246cd690b60SPoul-Henning Kamp } 1247d137acccSPoul-Henning Kamp 1248c32cc149SBruce Evans const char * 124989c9c53dSPoul-Henning Kamp devtoname(struct cdev *dev) 1250b8e49f68SBill Fumerola { 1251b8e49f68SBill Fumerola 1252b8e49f68SBill Fumerola return (dev->si_name); 1253b8e49f68SBill Fumerola } 1254db901281SPoul-Henning Kamp 1255db901281SPoul-Henning Kamp int 125601de1b13SPoul-Henning Kamp dev_stdclone(char *name, char **namep, const char *stem, int *unit) 1257db901281SPoul-Henning Kamp { 1258db901281SPoul-Henning Kamp int u, i; 1259db901281SPoul-Henning Kamp 1260db901281SPoul-Henning Kamp i = strlen(stem); 12613428b6c0SMark Johnston if (strncmp(stem, name, i) != 0) 126256700d46SBrian Somers return (0); 1263db901281SPoul-Henning Kamp if (!isdigit(name[i])) 1264db901281SPoul-Henning Kamp return (0); 1265db901281SPoul-Henning Kamp u = 0; 126610786074SPoul-Henning Kamp if (name[i] == '0' && isdigit(name[i+1])) 126710786074SPoul-Henning Kamp return (0); 1268db901281SPoul-Henning Kamp while (isdigit(name[i])) { 1269db901281SPoul-Henning Kamp u *= 10; 1270db901281SPoul-Henning Kamp u += name[i++] - '0'; 1271db901281SPoul-Henning Kamp } 1272dab3d85fSBrian Feldman if (u > 0xffffff) 1273dab3d85fSBrian Feldman return (0); 1274db901281SPoul-Henning Kamp *unit = u; 1275db901281SPoul-Henning Kamp if (namep) 1276db901281SPoul-Henning Kamp *namep = &name[i]; 1277db901281SPoul-Henning Kamp if (name[i]) 1278db901281SPoul-Henning Kamp return (2); 1279db901281SPoul-Henning Kamp return (1); 1280db901281SPoul-Henning Kamp } 12818d25eb2cSPoul-Henning Kamp 12828d25eb2cSPoul-Henning Kamp /* 1283b0b03348SPoul-Henning Kamp * Helper functions for cloning device drivers. 1284b0b03348SPoul-Henning Kamp * 1285b0b03348SPoul-Henning Kamp * The objective here is to make it unnecessary for the device drivers to 1286b0b03348SPoul-Henning Kamp * use rman or similar to manage their unit number space. Due to the way 1287b0b03348SPoul-Henning Kamp * we do "on-demand" devices, using rman or other "private" methods 1288b0b03348SPoul-Henning Kamp * will be very tricky to lock down properly once we lock down this file. 1289b0b03348SPoul-Henning Kamp * 12909a98ae94SLukas Ertl * Instead we give the drivers these routines which puts the struct cdev *'s 12919a98ae94SLukas Ertl * that are to be managed on their own list, and gives the driver the ability 1292b0b03348SPoul-Henning Kamp * to ask for the first free unit number or a given specified unit number. 1293b0b03348SPoul-Henning Kamp * 1294b0b03348SPoul-Henning Kamp * In addition these routines support paired devices (pty, nmdm and similar) 1295b0b03348SPoul-Henning Kamp * by respecting a number of "flag" bits in the minor number. 1296b0b03348SPoul-Henning Kamp * 1297b0b03348SPoul-Henning Kamp */ 1298b0b03348SPoul-Henning Kamp 1299b0b03348SPoul-Henning Kamp struct clonedevs { 1300b0b03348SPoul-Henning Kamp LIST_HEAD(,cdev) head; 1301b0b03348SPoul-Henning Kamp }; 1302b0b03348SPoul-Henning Kamp 13039397290eSPoul-Henning Kamp void 13049397290eSPoul-Henning Kamp clone_setup(struct clonedevs **cdp) 13059397290eSPoul-Henning Kamp { 13069397290eSPoul-Henning Kamp 13079397290eSPoul-Henning Kamp *cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO); 13089397290eSPoul-Henning Kamp LIST_INIT(&(*cdp)->head); 13099397290eSPoul-Henning Kamp } 13109397290eSPoul-Henning Kamp 1311b0b03348SPoul-Henning Kamp int 1312cf141467SKonstantin Belousov clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, 1313cf141467SKonstantin Belousov struct cdev **dp, int extra) 1314b0b03348SPoul-Henning Kamp { 1315b0b03348SPoul-Henning Kamp struct clonedevs *cd; 1316027b1f71SPoul-Henning Kamp struct cdev *dev, *ndev, *dl, *de; 131748ce5d4cSKonstantin Belousov struct make_dev_args args; 1318b0b03348SPoul-Henning Kamp int unit, low, u; 1319b0b03348SPoul-Henning Kamp 13209397290eSPoul-Henning Kamp KASSERT(*cdp != NULL, 13219397290eSPoul-Henning Kamp ("clone_setup() not called in driver \"%s\"", csw->d_name)); 1322b0b03348SPoul-Henning Kamp KASSERT(!(extra & CLONE_UNITMASK), 1323b0b03348SPoul-Henning Kamp ("Illegal extra bits (0x%x) in clone_create", extra)); 1324b0b03348SPoul-Henning Kamp KASSERT(*up <= CLONE_UNITMASK, 1325b0b03348SPoul-Henning Kamp ("Too high unit (0x%x) in clone_create", *up)); 132629d4cb24SEd Schouten KASSERT(csw->d_flags & D_NEEDMINOR, 132729d4cb24SEd Schouten ("clone_create() on cdevsw without minor numbers")); 1328b0b03348SPoul-Henning Kamp 1329b0b03348SPoul-Henning Kamp /* 1330b0b03348SPoul-Henning Kamp * Search the list for a lot of things in one go: 1331b0b03348SPoul-Henning Kamp * A preexisting match is returned immediately. 1332b0b03348SPoul-Henning Kamp * The lowest free unit number if we are passed -1, and the place 1333b0b03348SPoul-Henning Kamp * in the list where we should insert that new element. 1334b0b03348SPoul-Henning Kamp * The place to insert a specified unit number, if applicable 1335b0b03348SPoul-Henning Kamp * the end of the list. 1336b0b03348SPoul-Henning Kamp */ 1337b0b03348SPoul-Henning Kamp unit = *up; 1338d2ba618aSKonstantin Belousov ndev = devfs_alloc(MAKEDEV_WAITOK); 1339027b1f71SPoul-Henning Kamp dev_lock(); 1340d2ba618aSKonstantin Belousov prep_cdevsw(csw, MAKEDEV_WAITOK); 13418666b655SPoul-Henning Kamp low = extra; 1342b0b03348SPoul-Henning Kamp de = dl = NULL; 13439397290eSPoul-Henning Kamp cd = *cdp; 1344b0b03348SPoul-Henning Kamp LIST_FOREACH(dev, &cd->head, si_clone) { 1345027b1f71SPoul-Henning Kamp KASSERT(dev->si_flags & SI_CLONELIST, 1346027b1f71SPoul-Henning Kamp ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); 1347b0b03348SPoul-Henning Kamp u = dev2unit(dev); 1348b0b03348SPoul-Henning Kamp if (u == (unit | extra)) { 1349b0b03348SPoul-Henning Kamp *dp = dev; 1350027b1f71SPoul-Henning Kamp dev_unlock(); 13519bc911d4SKonstantin Belousov devfs_free(ndev); 1352b0b03348SPoul-Henning Kamp return (0); 1353b0b03348SPoul-Henning Kamp } 1354b0b03348SPoul-Henning Kamp if (unit == -1 && u == low) { 1355b0b03348SPoul-Henning Kamp low++; 1356b0b03348SPoul-Henning Kamp de = dev; 1357b0b03348SPoul-Henning Kamp continue; 13587bbb3a26SPoul-Henning Kamp } else if (u < (unit | extra)) { 13597bbb3a26SPoul-Henning Kamp de = dev; 13607bbb3a26SPoul-Henning Kamp continue; 13617bbb3a26SPoul-Henning Kamp } else if (u > (unit | extra)) { 1362b0b03348SPoul-Henning Kamp dl = dev; 1363b0b03348SPoul-Henning Kamp break; 1364b0b03348SPoul-Henning Kamp } 1365b0b03348SPoul-Henning Kamp } 1366b0b03348SPoul-Henning Kamp if (unit == -1) 13678666b655SPoul-Henning Kamp unit = low & CLONE_UNITMASK; 136848ce5d4cSKonstantin Belousov make_dev_args_init(&args); 136948ce5d4cSKonstantin Belousov args.mda_unit = unit | extra; 137048ce5d4cSKonstantin Belousov args.mda_devsw = csw; 137148ce5d4cSKonstantin Belousov dev = newdev(&args, ndev); 1372027b1f71SPoul-Henning Kamp if (dev->si_flags & SI_CLONELIST) { 1373027b1f71SPoul-Henning Kamp printf("dev %p (%s) is on clonelist\n", dev, dev->si_name); 13747bbb3a26SPoul-Henning Kamp printf("unit=%d, low=%d, extra=0x%x\n", unit, low, extra); 1375027b1f71SPoul-Henning Kamp LIST_FOREACH(dev, &cd->head, si_clone) { 1376027b1f71SPoul-Henning Kamp printf("\t%p %s\n", dev, dev->si_name); 1377027b1f71SPoul-Henning Kamp } 1378027b1f71SPoul-Henning Kamp panic("foo"); 1379027b1f71SPoul-Henning Kamp } 1380b0b03348SPoul-Henning Kamp KASSERT(!(dev->si_flags & SI_CLONELIST), 1381027b1f71SPoul-Henning Kamp ("Dev %p(%s) should not be on clonelist", dev, dev->si_name)); 1382b0b03348SPoul-Henning Kamp if (dl != NULL) 1383b0b03348SPoul-Henning Kamp LIST_INSERT_BEFORE(dl, dev, si_clone); 1384b0b03348SPoul-Henning Kamp else if (de != NULL) 1385b0b03348SPoul-Henning Kamp LIST_INSERT_AFTER(de, dev, si_clone); 1386b0b03348SPoul-Henning Kamp else 1387b0b03348SPoul-Henning Kamp LIST_INSERT_HEAD(&cd->head, dev, si_clone); 1388b0b03348SPoul-Henning Kamp dev->si_flags |= SI_CLONELIST; 1389b0b03348SPoul-Henning Kamp *up = unit; 13909bc911d4SKonstantin Belousov dev_unlock_and_free(); 1391b0b03348SPoul-Henning Kamp return (1); 1392b0b03348SPoul-Henning Kamp } 1393b0b03348SPoul-Henning Kamp 1394b0b03348SPoul-Henning Kamp /* 1395b0b03348SPoul-Henning Kamp * Kill everything still on the list. The driver should already have 139689c9c53dSPoul-Henning Kamp * disposed of any softc hung of the struct cdev *'s at this time. 1397b0b03348SPoul-Henning Kamp */ 1398b0b03348SPoul-Henning Kamp void 1399b0b03348SPoul-Henning Kamp clone_cleanup(struct clonedevs **cdp) 1400b0b03348SPoul-Henning Kamp { 1401de10ffa5SKonstantin Belousov struct cdev *dev; 1402de10ffa5SKonstantin Belousov struct cdev_priv *cp; 1403b0b03348SPoul-Henning Kamp struct clonedevs *cd; 1404b0b03348SPoul-Henning Kamp 1405b0b03348SPoul-Henning Kamp cd = *cdp; 1406b0b03348SPoul-Henning Kamp if (cd == NULL) 1407b0b03348SPoul-Henning Kamp return; 1408027b1f71SPoul-Henning Kamp dev_lock(); 1409de10ffa5SKonstantin Belousov while (!LIST_EMPTY(&cd->head)) { 1410de10ffa5SKonstantin Belousov dev = LIST_FIRST(&cd->head); 1411de10ffa5SKonstantin Belousov LIST_REMOVE(dev, si_clone); 1412027b1f71SPoul-Henning Kamp KASSERT(dev->si_flags & SI_CLONELIST, 1413027b1f71SPoul-Henning Kamp ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); 1414de10ffa5SKonstantin Belousov dev->si_flags &= ~SI_CLONELIST; 141505427aafSKonstantin Belousov cp = cdev2priv(dev); 1416de10ffa5SKonstantin Belousov if (!(cp->cdp_flags & CDP_SCHED_DTR)) { 1417de10ffa5SKonstantin Belousov cp->cdp_flags |= CDP_SCHED_DTR; 1418b0b03348SPoul-Henning Kamp KASSERT(dev->si_flags & SI_NAMED, 1419dde58752SGleb Kurtsou ("Driver has goofed in cloning underways udev %jx unit %x", 1420dde58752SGleb Kurtsou (uintmax_t)dev2udev(dev), dev2unit(dev))); 1421aa2f6ddcSPoul-Henning Kamp destroy_devl(dev); 1422b0b03348SPoul-Henning Kamp } 1423de10ffa5SKonstantin Belousov } 1424aeeb4202SKonstantin Belousov dev_unlock_and_free(); 1425b0b03348SPoul-Henning Kamp free(cd, M_DEVBUF); 1426b0b03348SPoul-Henning Kamp *cdp = NULL; 1427b0b03348SPoul-Henning Kamp } 1428de10ffa5SKonstantin Belousov 1429de10ffa5SKonstantin Belousov static TAILQ_HEAD(, cdev_priv) dev_ddtr = 1430de10ffa5SKonstantin Belousov TAILQ_HEAD_INITIALIZER(dev_ddtr); 1431268e76d8SJohn Baldwin static struct task dev_dtr_task = TASK_INITIALIZER(0, destroy_dev_tq, NULL); 1432de10ffa5SKonstantin Belousov 1433de10ffa5SKonstantin Belousov static void 1434de10ffa5SKonstantin Belousov destroy_dev_tq(void *ctx, int pending) 1435de10ffa5SKonstantin Belousov { 1436de10ffa5SKonstantin Belousov struct cdev_priv *cp; 1437de10ffa5SKonstantin Belousov struct cdev *dev; 1438de10ffa5SKonstantin Belousov void (*cb)(void *); 1439de10ffa5SKonstantin Belousov void *cb_arg; 1440de10ffa5SKonstantin Belousov 1441de10ffa5SKonstantin Belousov dev_lock(); 1442de10ffa5SKonstantin Belousov while (!TAILQ_EMPTY(&dev_ddtr)) { 1443de10ffa5SKonstantin Belousov cp = TAILQ_FIRST(&dev_ddtr); 1444de10ffa5SKonstantin Belousov dev = &cp->cdp_c; 1445de10ffa5SKonstantin Belousov KASSERT(cp->cdp_flags & CDP_SCHED_DTR, 1446de10ffa5SKonstantin Belousov ("cdev %p in dev_destroy_tq without CDP_SCHED_DTR", cp)); 1447de10ffa5SKonstantin Belousov TAILQ_REMOVE(&dev_ddtr, cp, cdp_dtr_list); 1448de10ffa5SKonstantin Belousov cb = cp->cdp_dtr_cb; 1449de10ffa5SKonstantin Belousov cb_arg = cp->cdp_dtr_cb_arg; 1450de10ffa5SKonstantin Belousov destroy_devl(dev); 1451aeeb4202SKonstantin Belousov dev_unlock_and_free(); 1452de10ffa5SKonstantin Belousov dev_rel(dev); 1453de10ffa5SKonstantin Belousov if (cb != NULL) 1454de10ffa5SKonstantin Belousov cb(cb_arg); 1455de10ffa5SKonstantin Belousov dev_lock(); 1456de10ffa5SKonstantin Belousov } 1457de10ffa5SKonstantin Belousov dev_unlock(); 1458de10ffa5SKonstantin Belousov } 1459de10ffa5SKonstantin Belousov 14609d53363bSKonstantin Belousov /* 14619d53363bSKonstantin Belousov * devmtx shall be locked on entry. devmtx will be unlocked after 14629d53363bSKonstantin Belousov * function return. 14639d53363bSKonstantin Belousov */ 14649d53363bSKonstantin Belousov static int 14659d53363bSKonstantin Belousov destroy_dev_sched_cbl(struct cdev *dev, void (*cb)(void *), void *arg) 1466de10ffa5SKonstantin Belousov { 1467de10ffa5SKonstantin Belousov struct cdev_priv *cp; 1468de10ffa5SKonstantin Belousov 14690ac9e27bSConrad Meyer dev_lock_assert_locked(); 147005427aafSKonstantin Belousov cp = cdev2priv(dev); 1471de10ffa5SKonstantin Belousov if (cp->cdp_flags & CDP_SCHED_DTR) { 1472de10ffa5SKonstantin Belousov dev_unlock(); 1473de10ffa5SKonstantin Belousov return (0); 1474de10ffa5SKonstantin Belousov } 1475de10ffa5SKonstantin Belousov dev_refl(dev); 1476de10ffa5SKonstantin Belousov cp->cdp_flags |= CDP_SCHED_DTR; 1477de10ffa5SKonstantin Belousov cp->cdp_dtr_cb = cb; 1478de10ffa5SKonstantin Belousov cp->cdp_dtr_cb_arg = arg; 1479de10ffa5SKonstantin Belousov TAILQ_INSERT_TAIL(&dev_ddtr, cp, cdp_dtr_list); 1480de10ffa5SKonstantin Belousov dev_unlock(); 1481de10ffa5SKonstantin Belousov taskqueue_enqueue(taskqueue_swi_giant, &dev_dtr_task); 1482de10ffa5SKonstantin Belousov return (1); 1483de10ffa5SKonstantin Belousov } 1484de10ffa5SKonstantin Belousov 1485de10ffa5SKonstantin Belousov int 14869d53363bSKonstantin Belousov destroy_dev_sched_cb(struct cdev *dev, void (*cb)(void *), void *arg) 14879d53363bSKonstantin Belousov { 1488cf141467SKonstantin Belousov 14899d53363bSKonstantin Belousov dev_lock(); 14909d53363bSKonstantin Belousov return (destroy_dev_sched_cbl(dev, cb, arg)); 14919d53363bSKonstantin Belousov } 14929d53363bSKonstantin Belousov 14939d53363bSKonstantin Belousov int 1494de10ffa5SKonstantin Belousov destroy_dev_sched(struct cdev *dev) 1495de10ffa5SKonstantin Belousov { 1496cf141467SKonstantin Belousov 1497de10ffa5SKonstantin Belousov return (destroy_dev_sched_cb(dev, NULL, NULL)); 1498de10ffa5SKonstantin Belousov } 1499de10ffa5SKonstantin Belousov 1500de10ffa5SKonstantin Belousov void 1501de10ffa5SKonstantin Belousov destroy_dev_drain(struct cdevsw *csw) 1502de10ffa5SKonstantin Belousov { 1503de10ffa5SKonstantin Belousov 1504de10ffa5SKonstantin Belousov dev_lock(); 1505de10ffa5SKonstantin Belousov while (!LIST_EMPTY(&csw->d_devs)) { 1506de10ffa5SKonstantin Belousov msleep(&csw->d_devs, &devmtx, PRIBIO, "devscd", hz/10); 1507de10ffa5SKonstantin Belousov } 1508de10ffa5SKonstantin Belousov dev_unlock(); 1509de10ffa5SKonstantin Belousov } 1510de10ffa5SKonstantin Belousov 1511de10ffa5SKonstantin Belousov void 1512de10ffa5SKonstantin Belousov drain_dev_clone_events(void) 1513de10ffa5SKonstantin Belousov { 1514de10ffa5SKonstantin Belousov 1515de10ffa5SKonstantin Belousov sx_xlock(&clone_drain_lock); 1516de10ffa5SKonstantin Belousov sx_xunlock(&clone_drain_lock); 1517de10ffa5SKonstantin Belousov } 1518de10ffa5SKonstantin Belousov 15196c5e633cSKonstantin Belousov #include "opt_ddb.h" 15206c5e633cSKonstantin Belousov #ifdef DDB 15216c5e633cSKonstantin Belousov #include <sys/kernel.h> 15226c5e633cSKonstantin Belousov 15236c5e633cSKonstantin Belousov #include <ddb/ddb.h> 15246c5e633cSKonstantin Belousov 15256c5e633cSKonstantin Belousov DB_SHOW_COMMAND(cdev, db_show_cdev) 15266c5e633cSKonstantin Belousov { 15276c5e633cSKonstantin Belousov struct cdev_priv *cdp; 15286c5e633cSKonstantin Belousov struct cdev *dev; 15296c5e633cSKonstantin Belousov u_int flags; 15306c5e633cSKonstantin Belousov char buf[512]; 15316c5e633cSKonstantin Belousov 15326c5e633cSKonstantin Belousov if (!have_addr) { 15336c5e633cSKonstantin Belousov TAILQ_FOREACH(cdp, &cdevp_list, cdp_list) { 15346c5e633cSKonstantin Belousov dev = &cdp->cdp_c; 15356c5e633cSKonstantin Belousov db_printf("%s %p\n", dev->si_name, dev); 15366c5e633cSKonstantin Belousov if (db_pager_quit) 15376c5e633cSKonstantin Belousov break; 15386c5e633cSKonstantin Belousov } 15396c5e633cSKonstantin Belousov return; 15406c5e633cSKonstantin Belousov } 15416c5e633cSKonstantin Belousov 15426c5e633cSKonstantin Belousov dev = (struct cdev *)addr; 15436c5e633cSKonstantin Belousov cdp = cdev2priv(dev); 15446c5e633cSKonstantin Belousov db_printf("dev %s ref %d use %ld thr %ld inuse %u fdpriv %p\n", 15456c5e633cSKonstantin Belousov dev->si_name, dev->si_refcount, dev->si_usecount, 15466c5e633cSKonstantin Belousov dev->si_threadcount, cdp->cdp_inuse, cdp->cdp_fdpriv.lh_first); 15476c5e633cSKonstantin Belousov db_printf("devsw %p si_drv0 %d si_drv1 %p si_drv2 %p\n", 15486c5e633cSKonstantin Belousov dev->si_devsw, dev->si_drv0, dev->si_drv1, dev->si_drv2); 15496c5e633cSKonstantin Belousov flags = dev->si_flags; 15506c5e633cSKonstantin Belousov #define SI_FLAG(flag) do { \ 15516c5e633cSKonstantin Belousov if (flags & (flag)) { \ 15526c5e633cSKonstantin Belousov if (buf[0] != '\0') \ 15536c5e633cSKonstantin Belousov strlcat(buf, ", ", sizeof(buf)); \ 15546c5e633cSKonstantin Belousov strlcat(buf, (#flag) + 3, sizeof(buf)); \ 15556c5e633cSKonstantin Belousov flags &= ~(flag); \ 15566c5e633cSKonstantin Belousov } \ 15576c5e633cSKonstantin Belousov } while (0) 15586c5e633cSKonstantin Belousov buf[0] = '\0'; 15596c5e633cSKonstantin Belousov SI_FLAG(SI_ETERNAL); 15606c5e633cSKonstantin Belousov SI_FLAG(SI_ALIAS); 15616c5e633cSKonstantin Belousov SI_FLAG(SI_NAMED); 15626c5e633cSKonstantin Belousov SI_FLAG(SI_CHILD); 15636c5e633cSKonstantin Belousov SI_FLAG(SI_DUMPDEV); 15646c5e633cSKonstantin Belousov SI_FLAG(SI_CLONELIST); 15656c5e633cSKonstantin Belousov db_printf("si_flags %s\n", buf); 15666c5e633cSKonstantin Belousov 15676c5e633cSKonstantin Belousov flags = cdp->cdp_flags; 15686c5e633cSKonstantin Belousov #define CDP_FLAG(flag) do { \ 15696c5e633cSKonstantin Belousov if (flags & (flag)) { \ 15706c5e633cSKonstantin Belousov if (buf[0] != '\0') \ 15716c5e633cSKonstantin Belousov strlcat(buf, ", ", sizeof(buf)); \ 15726c5e633cSKonstantin Belousov strlcat(buf, (#flag) + 4, sizeof(buf)); \ 15736c5e633cSKonstantin Belousov flags &= ~(flag); \ 15746c5e633cSKonstantin Belousov } \ 15756c5e633cSKonstantin Belousov } while (0) 15766c5e633cSKonstantin Belousov buf[0] = '\0'; 15776c5e633cSKonstantin Belousov CDP_FLAG(CDP_ACTIVE); 15786c5e633cSKonstantin Belousov CDP_FLAG(CDP_SCHED_DTR); 15796c5e633cSKonstantin Belousov db_printf("cdp_flags %s\n", buf); 15806c5e633cSKonstantin Belousov } 15816c5e633cSKonstantin Belousov #endif 1582