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> 3548504cc2SKonstantin Belousov #include <sys/bus.h> 3602574b19SPoul-Henning Kamp #include <sys/bio.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 919bc911d4SKonstantin Belousov mtx_assert(&devmtx, MA_OWNED); 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 1199bc911d4SKonstantin Belousov mtx_assert(&devmtx, MA_OWNED); 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 130aeeb4202SKonstantin Belousov mtx_assert(&devmtx, MA_OWNED); 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 1459477d73eSPoul-Henning Kamp mtx_assert(&devmtx, MA_NOTOWNED); 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 1551a1457d4SPoul-Henning Kamp mtx_assert(&devmtx, MA_OWNED); 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 164ba285125SPoul-Henning Kamp mtx_assert(&devmtx, MA_NOTOWNED); 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))); 169e606a3c6SPoul-Henning Kamp #if 0 170aa2f6ddcSPoul-Henning Kamp if (dev->si_usecount == 0 && 171aa2f6ddcSPoul-Henning Kamp (dev->si_flags & SI_CHEAPCLONE) && (dev->si_flags & SI_NAMED)) 172e606a3c6SPoul-Henning Kamp ; 173e606a3c6SPoul-Henning Kamp else 174e606a3c6SPoul-Henning Kamp #endif 175cd690b60SPoul-Henning Kamp if (dev->si_devsw == NULL && dev->si_refcount == 0) { 176cd690b60SPoul-Henning Kamp LIST_REMOVE(dev, si_list); 177ba285125SPoul-Henning Kamp flag = 1; 178ba285125SPoul-Henning Kamp } 179ba285125SPoul-Henning Kamp dev_unlock(); 180ba285125SPoul-Henning Kamp if (flag) 181e606a3c6SPoul-Henning Kamp devfs_free(dev); 182cd690b60SPoul-Henning Kamp } 183ba285125SPoul-Henning Kamp 1842c15afd8SPoul-Henning Kamp struct cdevsw * 1853979450bSKonstantin Belousov dev_refthread(struct cdev *dev, int *ref) 1862c15afd8SPoul-Henning Kamp { 1872c15afd8SPoul-Henning Kamp struct cdevsw *csw; 188de10ffa5SKonstantin Belousov struct cdev_priv *cdp; 1892c15afd8SPoul-Henning Kamp 1902c15afd8SPoul-Henning Kamp mtx_assert(&devmtx, MA_NOTOWNED); 1913979450bSKonstantin Belousov if ((dev->si_flags & SI_ETERNAL) != 0) { 1923979450bSKonstantin Belousov *ref = 0; 1933979450bSKonstantin Belousov return (dev->si_devsw); 1943979450bSKonstantin Belousov } 1952c15afd8SPoul-Henning Kamp dev_lock(); 1962c15afd8SPoul-Henning Kamp csw = dev->si_devsw; 197de10ffa5SKonstantin Belousov if (csw != NULL) { 19805427aafSKonstantin Belousov cdp = cdev2priv(dev); 199de10ffa5SKonstantin Belousov if ((cdp->cdp_flags & CDP_SCHED_DTR) == 0) 2009e8bd2acSAlexander Motin atomic_add_long(&dev->si_threadcount, 1); 201de10ffa5SKonstantin Belousov else 202de10ffa5SKonstantin Belousov csw = NULL; 203de10ffa5SKonstantin Belousov } 2042c15afd8SPoul-Henning Kamp dev_unlock(); 205ec86f8b2SConrad Meyer if (csw != NULL) 2063979450bSKonstantin Belousov *ref = 1; 2072c15afd8SPoul-Henning Kamp return (csw); 2082c15afd8SPoul-Henning Kamp } 2092c15afd8SPoul-Henning Kamp 2101663075cSKonstantin Belousov struct cdevsw * 2113979450bSKonstantin Belousov devvn_refthread(struct vnode *vp, struct cdev **devp, int *ref) 2121663075cSKonstantin Belousov { 2131663075cSKonstantin Belousov struct cdevsw *csw; 214de10ffa5SKonstantin Belousov struct cdev_priv *cdp; 2153979450bSKonstantin Belousov struct cdev *dev; 2161663075cSKonstantin Belousov 2171663075cSKonstantin Belousov mtx_assert(&devmtx, MA_NOTOWNED); 2183979450bSKonstantin Belousov if ((vp->v_vflag & VV_ETERNALDEV) != 0) { 2193979450bSKonstantin Belousov dev = vp->v_rdev; 2203979450bSKonstantin Belousov if (dev == NULL) 2213979450bSKonstantin Belousov return (NULL); 2223979450bSKonstantin Belousov KASSERT((dev->si_flags & SI_ETERNAL) != 0, 2233979450bSKonstantin Belousov ("Not eternal cdev")); 2243979450bSKonstantin Belousov *ref = 0; 2253979450bSKonstantin Belousov csw = dev->si_devsw; 2263979450bSKonstantin Belousov KASSERT(csw != NULL, ("Eternal cdev is destroyed")); 2273979450bSKonstantin Belousov *devp = dev; 2283979450bSKonstantin Belousov return (csw); 2293979450bSKonstantin Belousov } 2303979450bSKonstantin Belousov 2311663075cSKonstantin Belousov csw = NULL; 2321663075cSKonstantin Belousov dev_lock(); 2333979450bSKonstantin Belousov dev = vp->v_rdev; 2343979450bSKonstantin Belousov if (dev == NULL) { 2353979450bSKonstantin Belousov dev_unlock(); 2363979450bSKonstantin Belousov return (NULL); 2371663075cSKonstantin Belousov } 2383979450bSKonstantin Belousov cdp = cdev2priv(dev); 2393979450bSKonstantin Belousov if ((cdp->cdp_flags & CDP_SCHED_DTR) == 0) { 2403979450bSKonstantin Belousov csw = dev->si_devsw; 2413979450bSKonstantin Belousov if (csw != NULL) 2429e8bd2acSAlexander Motin atomic_add_long(&dev->si_threadcount, 1); 243de10ffa5SKonstantin Belousov } 2441663075cSKonstantin Belousov dev_unlock(); 2453979450bSKonstantin Belousov if (csw != NULL) { 2463979450bSKonstantin Belousov *devp = dev; 2473979450bSKonstantin Belousov *ref = 1; 2483979450bSKonstantin Belousov } 2491663075cSKonstantin Belousov return (csw); 2501663075cSKonstantin Belousov } 2511663075cSKonstantin Belousov 2522c15afd8SPoul-Henning Kamp void 2533979450bSKonstantin Belousov dev_relthread(struct cdev *dev, int ref) 2542c15afd8SPoul-Henning Kamp { 2552c15afd8SPoul-Henning Kamp 2562c15afd8SPoul-Henning Kamp mtx_assert(&devmtx, MA_NOTOWNED); 2573979450bSKonstantin Belousov if (!ref) 2583979450bSKonstantin Belousov return; 25906fe1129SKonstantin Belousov KASSERT(dev->si_threadcount > 0, 26006fe1129SKonstantin Belousov ("%s threadcount is wrong", dev->si_name)); 2619e8bd2acSAlexander Motin atomic_subtract_rel_long(&dev->si_threadcount, 1); 2622c15afd8SPoul-Henning Kamp } 263cd690b60SPoul-Henning Kamp 264b2941431SPoul-Henning Kamp int 265b2941431SPoul-Henning Kamp nullop(void) 266b2941431SPoul-Henning Kamp { 267b2941431SPoul-Henning Kamp 268b2941431SPoul-Henning Kamp return (0); 269b2941431SPoul-Henning Kamp } 270b2941431SPoul-Henning Kamp 271b2941431SPoul-Henning Kamp int 272b2941431SPoul-Henning Kamp eopnotsupp(void) 273b2941431SPoul-Henning Kamp { 274b2941431SPoul-Henning Kamp 275b2941431SPoul-Henning Kamp return (EOPNOTSUPP); 276b2941431SPoul-Henning Kamp } 27702574b19SPoul-Henning Kamp 27802574b19SPoul-Henning Kamp static int 27902574b19SPoul-Henning Kamp enxio(void) 28002574b19SPoul-Henning Kamp { 28102574b19SPoul-Henning Kamp return (ENXIO); 28202574b19SPoul-Henning Kamp } 28302574b19SPoul-Henning Kamp 284b2941431SPoul-Henning Kamp static int 285b2941431SPoul-Henning Kamp enodev(void) 286b2941431SPoul-Henning Kamp { 287b2941431SPoul-Henning Kamp return (ENODEV); 288b2941431SPoul-Henning Kamp } 289b2941431SPoul-Henning Kamp 290b2941431SPoul-Henning Kamp /* Define a dead_cdevsw for use when devices leave unexpectedly. */ 291b2941431SPoul-Henning Kamp 29202574b19SPoul-Henning Kamp #define dead_open (d_open_t *)enxio 29302574b19SPoul-Henning Kamp #define dead_close (d_close_t *)enxio 29402574b19SPoul-Henning Kamp #define dead_read (d_read_t *)enxio 29502574b19SPoul-Henning Kamp #define dead_write (d_write_t *)enxio 29602574b19SPoul-Henning Kamp #define dead_ioctl (d_ioctl_t *)enxio 297b2941431SPoul-Henning Kamp #define dead_poll (d_poll_t *)enodev 298b2941431SPoul-Henning Kamp #define dead_mmap (d_mmap_t *)enodev 29902574b19SPoul-Henning Kamp 30002574b19SPoul-Henning Kamp static void 30102574b19SPoul-Henning Kamp dead_strategy(struct bio *bp) 30202574b19SPoul-Henning Kamp { 30302574b19SPoul-Henning Kamp 30402574b19SPoul-Henning Kamp biofinish(bp, NULL, ENXIO); 30502574b19SPoul-Henning Kamp } 30602574b19SPoul-Henning Kamp 3072c6b49f6SPoul-Henning Kamp #define dead_dump (dumper_t *)enxio 30802574b19SPoul-Henning Kamp #define dead_kqfilter (d_kqfilter_t *)enxio 30964345f0bSJohn Baldwin #define dead_mmap_single (d_mmap_single_t *)enodev 31002574b19SPoul-Henning Kamp 31102574b19SPoul-Henning Kamp static struct cdevsw dead_cdevsw = { 312dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 3137ac40f5fSPoul-Henning Kamp .d_open = dead_open, 3147ac40f5fSPoul-Henning Kamp .d_close = dead_close, 3157ac40f5fSPoul-Henning Kamp .d_read = dead_read, 3167ac40f5fSPoul-Henning Kamp .d_write = dead_write, 3177ac40f5fSPoul-Henning Kamp .d_ioctl = dead_ioctl, 3187ac40f5fSPoul-Henning Kamp .d_poll = dead_poll, 3197ac40f5fSPoul-Henning Kamp .d_mmap = dead_mmap, 3207ac40f5fSPoul-Henning Kamp .d_strategy = dead_strategy, 3217ac40f5fSPoul-Henning Kamp .d_name = "dead", 3227ac40f5fSPoul-Henning Kamp .d_dump = dead_dump, 32364345f0bSJohn Baldwin .d_kqfilter = dead_kqfilter, 32464345f0bSJohn Baldwin .d_mmap_single = dead_mmap_single 32502574b19SPoul-Henning Kamp }; 32602574b19SPoul-Henning Kamp 327b2941431SPoul-Henning Kamp /* Default methods if driver does not specify method */ 328b2941431SPoul-Henning Kamp 329b2941431SPoul-Henning Kamp #define null_open (d_open_t *)nullop 330b2941431SPoul-Henning Kamp #define null_close (d_close_t *)nullop 331b2941431SPoul-Henning Kamp #define no_read (d_read_t *)enodev 332b2941431SPoul-Henning Kamp #define no_write (d_write_t *)enodev 333b2941431SPoul-Henning Kamp #define no_ioctl (d_ioctl_t *)enodev 334cfd7baceSRobert Noland #define no_mmap (d_mmap_t *)enodev 335ad3b9257SJohn-Mark Gurney #define no_kqfilter (d_kqfilter_t *)enodev 33664345f0bSJohn Baldwin #define no_mmap_single (d_mmap_single_t *)enodev 337b2941431SPoul-Henning Kamp 338b2941431SPoul-Henning Kamp static void 339b2941431SPoul-Henning Kamp no_strategy(struct bio *bp) 340b2941431SPoul-Henning Kamp { 341b2941431SPoul-Henning Kamp 342b2941431SPoul-Henning Kamp biofinish(bp, NULL, ENODEV); 343b2941431SPoul-Henning Kamp } 344b2941431SPoul-Henning Kamp 345b2941431SPoul-Henning Kamp static int 34689c9c53dSPoul-Henning Kamp no_poll(struct cdev *dev __unused, int events, struct thread *td __unused) 347b2941431SPoul-Henning Kamp { 348b2941431SPoul-Henning Kamp 349125dcf8cSKonstantin Belousov return (poll_no_poll(events)); 350b2941431SPoul-Henning Kamp } 351b2941431SPoul-Henning Kamp 352b2941431SPoul-Henning Kamp #define no_dump (dumper_t *)enodev 3534e4a7663SPoul-Henning Kamp 354516ad423SPoul-Henning Kamp static int 355516ad423SPoul-Henning Kamp giant_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 356516ad423SPoul-Henning Kamp { 357aeeb4202SKonstantin Belousov struct cdevsw *dsw; 3583979450bSKonstantin Belousov int ref, retval; 359516ad423SPoul-Henning Kamp 3603979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 361aeeb4202SKonstantin Belousov if (dsw == NULL) 362aeeb4202SKonstantin Belousov return (ENXIO); 363516ad423SPoul-Henning Kamp mtx_lock(&Giant); 364aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_open(dev, oflags, devtype, td); 365516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 3663979450bSKonstantin Belousov dev_relthread(dev, ref); 367516ad423SPoul-Henning Kamp return (retval); 368516ad423SPoul-Henning Kamp } 369516ad423SPoul-Henning Kamp 370516ad423SPoul-Henning Kamp static int 3719e223287SKonstantin Belousov giant_fdopen(struct cdev *dev, int oflags, struct thread *td, struct file *fp) 372516ad423SPoul-Henning Kamp { 373aeeb4202SKonstantin Belousov struct cdevsw *dsw; 3743979450bSKonstantin Belousov int ref, retval; 375516ad423SPoul-Henning Kamp 3763979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 377aeeb4202SKonstantin Belousov if (dsw == NULL) 378aeeb4202SKonstantin Belousov return (ENXIO); 379516ad423SPoul-Henning Kamp mtx_lock(&Giant); 380aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_fdopen(dev, oflags, td, fp); 381516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 3823979450bSKonstantin Belousov dev_relthread(dev, ref); 383516ad423SPoul-Henning Kamp return (retval); 384516ad423SPoul-Henning Kamp } 385516ad423SPoul-Henning Kamp 386516ad423SPoul-Henning Kamp static int 387516ad423SPoul-Henning Kamp giant_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 388516ad423SPoul-Henning Kamp { 389aeeb4202SKonstantin Belousov struct cdevsw *dsw; 3903979450bSKonstantin Belousov int ref, retval; 391516ad423SPoul-Henning Kamp 3923979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 393aeeb4202SKonstantin Belousov if (dsw == NULL) 394aeeb4202SKonstantin Belousov return (ENXIO); 395516ad423SPoul-Henning Kamp mtx_lock(&Giant); 396aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_close(dev, fflag, devtype, td); 397516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 3983979450bSKonstantin Belousov dev_relthread(dev, ref); 399516ad423SPoul-Henning Kamp return (retval); 400516ad423SPoul-Henning Kamp } 401516ad423SPoul-Henning Kamp 402516ad423SPoul-Henning Kamp static void 403516ad423SPoul-Henning Kamp giant_strategy(struct bio *bp) 404516ad423SPoul-Henning Kamp { 405aeeb4202SKonstantin Belousov struct cdevsw *dsw; 406aeeb4202SKonstantin Belousov struct cdev *dev; 4073979450bSKonstantin Belousov int ref; 408516ad423SPoul-Henning Kamp 409aeeb4202SKonstantin Belousov dev = bp->bio_dev; 4103979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 411aeeb4202SKonstantin Belousov if (dsw == NULL) { 412aeeb4202SKonstantin Belousov biofinish(bp, NULL, ENXIO); 413aeeb4202SKonstantin Belousov return; 414aeeb4202SKonstantin Belousov } 415516ad423SPoul-Henning Kamp mtx_lock(&Giant); 416aeeb4202SKonstantin Belousov dsw->d_gianttrick->d_strategy(bp); 417516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4183979450bSKonstantin Belousov dev_relthread(dev, ref); 419516ad423SPoul-Henning Kamp } 420516ad423SPoul-Henning Kamp 421516ad423SPoul-Henning Kamp static int 422516ad423SPoul-Henning Kamp giant_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 423516ad423SPoul-Henning Kamp { 424aeeb4202SKonstantin Belousov struct cdevsw *dsw; 4253979450bSKonstantin Belousov int ref, retval; 426516ad423SPoul-Henning Kamp 4273979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 428aeeb4202SKonstantin Belousov if (dsw == NULL) 429aeeb4202SKonstantin Belousov return (ENXIO); 430516ad423SPoul-Henning Kamp mtx_lock(&Giant); 43135b45029SKonstantin Belousov retval = dsw->d_gianttrick->d_ioctl(dev, cmd, data, fflag, td); 432516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4333979450bSKonstantin Belousov dev_relthread(dev, ref); 434516ad423SPoul-Henning Kamp return (retval); 435516ad423SPoul-Henning Kamp } 436516ad423SPoul-Henning Kamp 437516ad423SPoul-Henning Kamp static int 438516ad423SPoul-Henning Kamp giant_read(struct cdev *dev, struct uio *uio, int ioflag) 439516ad423SPoul-Henning Kamp { 440aeeb4202SKonstantin Belousov struct cdevsw *dsw; 4413979450bSKonstantin Belousov int ref, retval; 442516ad423SPoul-Henning Kamp 4433979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 444aeeb4202SKonstantin Belousov if (dsw == NULL) 445aeeb4202SKonstantin Belousov return (ENXIO); 446516ad423SPoul-Henning Kamp mtx_lock(&Giant); 44735b45029SKonstantin Belousov retval = dsw->d_gianttrick->d_read(dev, uio, ioflag); 448516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4493979450bSKonstantin Belousov dev_relthread(dev, ref); 450516ad423SPoul-Henning Kamp return (retval); 451516ad423SPoul-Henning Kamp } 452516ad423SPoul-Henning Kamp 453516ad423SPoul-Henning Kamp static int 454516ad423SPoul-Henning Kamp giant_write(struct cdev *dev, struct uio *uio, int ioflag) 455516ad423SPoul-Henning Kamp { 456aeeb4202SKonstantin Belousov struct cdevsw *dsw; 4573979450bSKonstantin Belousov int ref, retval; 458516ad423SPoul-Henning Kamp 4593979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 460aeeb4202SKonstantin Belousov if (dsw == NULL) 461aeeb4202SKonstantin Belousov return (ENXIO); 462516ad423SPoul-Henning Kamp mtx_lock(&Giant); 463aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_write(dev, uio, ioflag); 464516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4653979450bSKonstantin Belousov dev_relthread(dev, ref); 466516ad423SPoul-Henning Kamp return (retval); 467516ad423SPoul-Henning Kamp } 468516ad423SPoul-Henning Kamp 469516ad423SPoul-Henning Kamp static int 470516ad423SPoul-Henning Kamp giant_poll(struct cdev *dev, int events, struct thread *td) 471516ad423SPoul-Henning Kamp { 472aeeb4202SKonstantin Belousov struct cdevsw *dsw; 4733979450bSKonstantin Belousov int ref, retval; 474516ad423SPoul-Henning Kamp 4753979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 476aeeb4202SKonstantin Belousov if (dsw == NULL) 477aeeb4202SKonstantin Belousov return (ENXIO); 478516ad423SPoul-Henning Kamp mtx_lock(&Giant); 479aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_poll(dev, events, td); 480516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4813979450bSKonstantin Belousov dev_relthread(dev, ref); 482516ad423SPoul-Henning Kamp return (retval); 483516ad423SPoul-Henning Kamp } 484516ad423SPoul-Henning Kamp 485516ad423SPoul-Henning Kamp static int 486516ad423SPoul-Henning Kamp giant_kqfilter(struct cdev *dev, struct knote *kn) 487516ad423SPoul-Henning Kamp { 488aeeb4202SKonstantin Belousov struct cdevsw *dsw; 4893979450bSKonstantin Belousov int ref, retval; 490516ad423SPoul-Henning Kamp 4913979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 492aeeb4202SKonstantin Belousov if (dsw == NULL) 493aeeb4202SKonstantin Belousov return (ENXIO); 494516ad423SPoul-Henning Kamp mtx_lock(&Giant); 495aeeb4202SKonstantin Belousov retval = dsw->d_gianttrick->d_kqfilter(dev, kn); 496516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 4973979450bSKonstantin Belousov dev_relthread(dev, ref); 498516ad423SPoul-Henning Kamp return (retval); 499516ad423SPoul-Henning Kamp } 500516ad423SPoul-Henning Kamp 501516ad423SPoul-Henning Kamp static int 502cfd7baceSRobert Noland giant_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, 5032fa8c8d2SJohn Baldwin vm_memattr_t *memattr) 504516ad423SPoul-Henning Kamp { 505aeeb4202SKonstantin Belousov struct cdevsw *dsw; 5063979450bSKonstantin Belousov int ref, retval; 507516ad423SPoul-Henning Kamp 5083979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 509aeeb4202SKonstantin Belousov if (dsw == NULL) 510aeeb4202SKonstantin Belousov return (ENXIO); 511516ad423SPoul-Henning Kamp mtx_lock(&Giant); 512cfd7baceSRobert Noland retval = dsw->d_gianttrick->d_mmap(dev, offset, paddr, nprot, 5132fa8c8d2SJohn Baldwin memattr); 514516ad423SPoul-Henning Kamp mtx_unlock(&Giant); 5153979450bSKonstantin Belousov dev_relthread(dev, ref); 516516ad423SPoul-Henning Kamp return (retval); 517516ad423SPoul-Henning Kamp } 518516ad423SPoul-Henning Kamp 51964345f0bSJohn Baldwin static int 52064345f0bSJohn Baldwin giant_mmap_single(struct cdev *dev, vm_ooffset_t *offset, vm_size_t size, 52164345f0bSJohn Baldwin vm_object_t *object, int nprot) 52264345f0bSJohn Baldwin { 52364345f0bSJohn Baldwin struct cdevsw *dsw; 5243979450bSKonstantin Belousov int ref, retval; 52564345f0bSJohn Baldwin 5263979450bSKonstantin Belousov dsw = dev_refthread(dev, &ref); 52764345f0bSJohn Baldwin if (dsw == NULL) 52864345f0bSJohn Baldwin return (ENXIO); 52964345f0bSJohn Baldwin mtx_lock(&Giant); 53064345f0bSJohn Baldwin retval = dsw->d_gianttrick->d_mmap_single(dev, offset, size, object, 53164345f0bSJohn Baldwin nprot); 53264345f0bSJohn Baldwin mtx_unlock(&Giant); 5333979450bSKonstantin Belousov dev_relthread(dev, ref); 53464345f0bSJohn Baldwin return (retval); 53564345f0bSJohn Baldwin } 536516ad423SPoul-Henning Kamp 53748504cc2SKonstantin Belousov static void 538d2ba618aSKonstantin Belousov notify(struct cdev *dev, const char *ev, int flags) 53948504cc2SKonstantin Belousov { 54048504cc2SKonstantin Belousov static const char prefix[] = "cdev="; 54148504cc2SKonstantin Belousov char *data; 54276d43557SKonstantin Belousov int namelen, mflags; 54348504cc2SKonstantin Belousov 54448504cc2SKonstantin Belousov if (cold) 54548504cc2SKonstantin Belousov return; 54676d43557SKonstantin Belousov mflags = (flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK; 54748504cc2SKonstantin Belousov namelen = strlen(dev->si_name); 54876d43557SKonstantin Belousov data = malloc(namelen + sizeof(prefix), M_TEMP, mflags); 5499995e57bSAttilio Rao if (data == NULL) 5509995e57bSAttilio Rao return; 55148504cc2SKonstantin Belousov memcpy(data, prefix, sizeof(prefix) - 1); 55248504cc2SKonstantin Belousov memcpy(data + sizeof(prefix) - 1, dev->si_name, namelen + 1); 55376d43557SKonstantin Belousov devctl_notify_f("DEVFS", "CDEV", ev, data, mflags); 55448504cc2SKonstantin Belousov free(data, M_TEMP); 55548504cc2SKonstantin Belousov } 55648504cc2SKonstantin Belousov 55748504cc2SKonstantin Belousov static void 558d2ba618aSKonstantin Belousov notify_create(struct cdev *dev, int flags) 55948504cc2SKonstantin Belousov { 56048504cc2SKonstantin Belousov 561d2ba618aSKonstantin Belousov notify(dev, "CREATE", flags); 56248504cc2SKonstantin Belousov } 56348504cc2SKonstantin Belousov 56448504cc2SKonstantin Belousov static void 56548504cc2SKonstantin Belousov notify_destroy(struct cdev *dev) 56648504cc2SKonstantin Belousov { 56748504cc2SKonstantin Belousov 568d2ba618aSKonstantin Belousov notify(dev, "DESTROY", MAKEDEV_WAITOK); 56948504cc2SKonstantin Belousov } 57048504cc2SKonstantin Belousov 57189c9c53dSPoul-Henning Kamp static struct cdev * 57248ce5d4cSKonstantin Belousov newdev(struct make_dev_args *args, struct cdev *si) 5733f54a085SPoul-Henning Kamp { 574027b1f71SPoul-Henning Kamp struct cdev *si2; 57548ce5d4cSKonstantin Belousov struct cdevsw *csw; 5763f54a085SPoul-Henning Kamp 577027b1f71SPoul-Henning Kamp mtx_assert(&devmtx, MA_OWNED); 57848ce5d4cSKonstantin Belousov csw = args->mda_devsw; 579*d42fecb5SKyle Evans si2 = NULL; 58029d4cb24SEd Schouten if (csw->d_flags & D_NEEDMINOR) { 58129d4cb24SEd Schouten /* We may want to return an existing device */ 582ff7284eeSPoul-Henning Kamp LIST_FOREACH(si2, &csw->d_devs, si_list) { 58348ce5d4cSKonstantin Belousov if (dev2unit(si2) == args->mda_unit) { 5849bc911d4SKonstantin Belousov dev_free_devlocked(si); 585*d42fecb5SKyle Evans si = si2; 586*d42fecb5SKyle Evans break; 5873f54a085SPoul-Henning Kamp } 588027b1f71SPoul-Henning Kamp } 589*d42fecb5SKyle Evans 590*d42fecb5SKyle Evans /* 591*d42fecb5SKyle Evans * If we're returning an existing device, we should make sure 592*d42fecb5SKyle Evans * it isn't already initialized. This would have been caught 593*d42fecb5SKyle Evans * in consumers anyways, but it's good to catch such a case 594*d42fecb5SKyle Evans * early. We still need to complete initialization of the 595*d42fecb5SKyle Evans * device, and we'll use whatever make_dev_args were passed in 596*d42fecb5SKyle Evans * to do so. 597*d42fecb5SKyle Evans */ 598*d42fecb5SKyle Evans KASSERT(si2 == NULL || (si2->si_flags & SI_NAMED) == 0, 599*d42fecb5SKyle Evans ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)", 600*d42fecb5SKyle Evans args->mda_devsw->d_name, dev2unit(si2), devtoname(si2))); 60129d4cb24SEd Schouten } 60248ce5d4cSKonstantin Belousov si->si_drv0 = args->mda_unit; 60348ce5d4cSKonstantin Belousov si->si_drv1 = args->mda_si_drv1; 60448ce5d4cSKonstantin Belousov si->si_drv2 = args->mda_si_drv2; 605*d42fecb5SKyle Evans /* Only push to csw->d_devs if it's not a cloned device. */ 606*d42fecb5SKyle Evans if (si2 == NULL) { 607*d42fecb5SKyle Evans si->si_devsw = csw; 608ff7284eeSPoul-Henning Kamp LIST_INSERT_HEAD(&csw->d_devs, si, si_list); 609*d42fecb5SKyle Evans } else { 610*d42fecb5SKyle Evans KASSERT(si->si_devsw == csw, 611*d42fecb5SKyle Evans ("%s: inconsistent devsw between clone_create() and make_dev()", 612*d42fecb5SKyle Evans __func__)); 613*d42fecb5SKyle Evans } 614698bfad7SPoul-Henning Kamp return (si); 615bfbb9ce6SPoul-Henning Kamp } 616bfbb9ce6SPoul-Henning Kamp 6172a3faf2fSPoul-Henning Kamp static void 618cd690b60SPoul-Henning Kamp fini_cdevsw(struct cdevsw *devsw) 619cd690b60SPoul-Henning Kamp { 6201d45c50eSPoul-Henning Kamp struct cdevsw *gt; 621b3d82c03SPoul-Henning Kamp 6221d45c50eSPoul-Henning Kamp if (devsw->d_gianttrick != NULL) { 6231d45c50eSPoul-Henning Kamp gt = devsw->d_gianttrick; 6241d45c50eSPoul-Henning Kamp memcpy(devsw, gt, sizeof *devsw); 625aeeb4202SKonstantin Belousov cdevsw_free_devlocked(gt); 626516ad423SPoul-Henning Kamp devsw->d_gianttrick = NULL; 6271d45c50eSPoul-Henning Kamp } 628652d0472SPoul-Henning Kamp devsw->d_flags &= ~D_INIT; 629b0b03348SPoul-Henning Kamp } 630b0b03348SPoul-Henning Kamp 631d2ba618aSKonstantin Belousov static int 632d2ba618aSKonstantin Belousov prep_cdevsw(struct cdevsw *devsw, int flags) 633b0b03348SPoul-Henning Kamp { 634516ad423SPoul-Henning Kamp struct cdevsw *dsw2; 635b0b03348SPoul-Henning Kamp 636aeeb4202SKonstantin Belousov mtx_assert(&devmtx, MA_OWNED); 637aeeb4202SKonstantin Belousov if (devsw->d_flags & D_INIT) 638f1bb758dSKonstantin Belousov return (0); 639aeeb4202SKonstantin Belousov if (devsw->d_flags & D_NEEDGIANT) { 640aeeb4202SKonstantin Belousov dev_unlock(); 641d2ba618aSKonstantin Belousov dsw2 = malloc(sizeof *dsw2, M_DEVT, 642d2ba618aSKonstantin Belousov (flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK); 643a0e78d2eSPoul-Henning Kamp dev_lock(); 644d2ba618aSKonstantin Belousov if (dsw2 == NULL && !(devsw->d_flags & D_INIT)) 645f1bb758dSKonstantin Belousov return (ENOMEM); 646aeeb4202SKonstantin Belousov } else 647aeeb4202SKonstantin Belousov dsw2 = NULL; 648aeeb4202SKonstantin Belousov if (devsw->d_flags & D_INIT) { 649aeeb4202SKonstantin Belousov if (dsw2 != NULL) 650aeeb4202SKonstantin Belousov cdevsw_free_devlocked(dsw2); 651f1bb758dSKonstantin Belousov return (0); 652aeeb4202SKonstantin Belousov } 653cd690b60SPoul-Henning Kamp 654e5ac3049SKonstantin Belousov if (devsw->d_version != D_VERSION_04) { 655cd690b60SPoul-Henning Kamp printf( 656cd690b60SPoul-Henning Kamp "WARNING: Device driver \"%s\" has wrong version %s\n", 6577d7e053cSAlfred Perlstein devsw->d_name == NULL ? "???" : devsw->d_name, 6587d7e053cSAlfred Perlstein "and is disabled. Recompile KLD module."); 659cd690b60SPoul-Henning Kamp devsw->d_open = dead_open; 660cd690b60SPoul-Henning Kamp devsw->d_close = dead_close; 661cd690b60SPoul-Henning Kamp devsw->d_read = dead_read; 662cd690b60SPoul-Henning Kamp devsw->d_write = dead_write; 663cd690b60SPoul-Henning Kamp devsw->d_ioctl = dead_ioctl; 664cd690b60SPoul-Henning Kamp devsw->d_poll = dead_poll; 665cd690b60SPoul-Henning Kamp devsw->d_mmap = dead_mmap; 666cfd7baceSRobert Noland devsw->d_mmap_single = dead_mmap_single; 667cd690b60SPoul-Henning Kamp devsw->d_strategy = dead_strategy; 668cd690b60SPoul-Henning Kamp devsw->d_dump = dead_dump; 669cd690b60SPoul-Henning Kamp devsw->d_kqfilter = dead_kqfilter; 670cd690b60SPoul-Henning Kamp } 671cd690b60SPoul-Henning Kamp 672516ad423SPoul-Henning Kamp if (devsw->d_flags & D_NEEDGIANT) { 673516ad423SPoul-Henning Kamp if (devsw->d_gianttrick == NULL) { 674516ad423SPoul-Henning Kamp memcpy(dsw2, devsw, sizeof *dsw2); 675516ad423SPoul-Henning Kamp devsw->d_gianttrick = dsw2; 676aeeb4202SKonstantin Belousov dsw2 = NULL; 677aeeb4202SKonstantin Belousov } 678516ad423SPoul-Henning Kamp } 679516ad423SPoul-Henning Kamp 680516ad423SPoul-Henning Kamp #define FIXUP(member, noop, giant) \ 681516ad423SPoul-Henning Kamp do { \ 682516ad423SPoul-Henning Kamp if (devsw->member == NULL) { \ 683516ad423SPoul-Henning Kamp devsw->member = noop; \ 684516ad423SPoul-Henning Kamp } else if (devsw->d_flags & D_NEEDGIANT) \ 685516ad423SPoul-Henning Kamp devsw->member = giant; \ 686516ad423SPoul-Henning Kamp } \ 687516ad423SPoul-Henning Kamp while (0) 688516ad423SPoul-Henning Kamp 689516ad423SPoul-Henning Kamp FIXUP(d_open, null_open, giant_open); 690516ad423SPoul-Henning Kamp FIXUP(d_fdopen, NULL, giant_fdopen); 691516ad423SPoul-Henning Kamp FIXUP(d_close, null_close, giant_close); 692516ad423SPoul-Henning Kamp FIXUP(d_read, no_read, giant_read); 693516ad423SPoul-Henning Kamp FIXUP(d_write, no_write, giant_write); 694516ad423SPoul-Henning Kamp FIXUP(d_ioctl, no_ioctl, giant_ioctl); 695516ad423SPoul-Henning Kamp FIXUP(d_poll, no_poll, giant_poll); 696cfd7baceSRobert Noland FIXUP(d_mmap, no_mmap, giant_mmap); 697516ad423SPoul-Henning Kamp FIXUP(d_strategy, no_strategy, giant_strategy); 698516ad423SPoul-Henning Kamp FIXUP(d_kqfilter, no_kqfilter, giant_kqfilter); 69964345f0bSJohn Baldwin FIXUP(d_mmap_single, no_mmap_single, giant_mmap_single); 700516ad423SPoul-Henning Kamp 701b2941431SPoul-Henning Kamp if (devsw->d_dump == NULL) devsw->d_dump = no_dump; 702cd690b60SPoul-Henning Kamp 703cd690b60SPoul-Henning Kamp LIST_INIT(&devsw->d_devs); 704cd690b60SPoul-Henning Kamp 705cd690b60SPoul-Henning Kamp devsw->d_flags |= D_INIT; 706cd690b60SPoul-Henning Kamp 707aeeb4202SKonstantin Belousov if (dsw2 != NULL) 708aeeb4202SKonstantin Belousov cdevsw_free_devlocked(dsw2); 709f1bb758dSKonstantin Belousov return (0); 7102a3faf2fSPoul-Henning Kamp } 71111586717SBrian Somers 712f1bb758dSKonstantin Belousov static int 71368f7a013SJaakko Heinonen prep_devname(struct cdev *dev, const char *fmt, va_list ap) 71468f7a013SJaakko Heinonen { 71568f7a013SJaakko Heinonen int len; 71668f7a013SJaakko Heinonen char *from, *q, *s, *to; 71768f7a013SJaakko Heinonen 71868f7a013SJaakko Heinonen mtx_assert(&devmtx, MA_OWNED); 71968f7a013SJaakko Heinonen 720852b05c5SEd Schouten len = vsnrprintf(dev->si_name, sizeof(dev->si_name), 32, fmt, ap); 7218fac9b7bSEd Schouten if (len > sizeof(dev->si_name) - 1) 72268f7a013SJaakko Heinonen return (ENAMETOOLONG); 72368f7a013SJaakko Heinonen 72468f7a013SJaakko Heinonen /* Strip leading slashes. */ 7258fac9b7bSEd Schouten for (from = dev->si_name; *from == '/'; from++) 72668f7a013SJaakko Heinonen ; 72768f7a013SJaakko Heinonen 7288fac9b7bSEd Schouten for (to = dev->si_name; *from != '\0'; from++, to++) { 729b1e1f725SJaakko Heinonen /* 730b1e1f725SJaakko Heinonen * Spaces and double quotation marks cause 731b1e1f725SJaakko Heinonen * problems for the devctl(4) protocol. 732b1e1f725SJaakko Heinonen * Reject names containing those characters. 733b1e1f725SJaakko Heinonen */ 734b1e1f725SJaakko Heinonen if (isspace(*from) || *from == '"') 735b1e1f725SJaakko Heinonen return (EINVAL); 73668f7a013SJaakko Heinonen /* Treat multiple sequential slashes as single. */ 73768f7a013SJaakko Heinonen while (from[0] == '/' && from[1] == '/') 73868f7a013SJaakko Heinonen from++; 73968f7a013SJaakko Heinonen /* Trailing slash is considered invalid. */ 74068f7a013SJaakko Heinonen if (from[0] == '/' && from[1] == '\0') 74168f7a013SJaakko Heinonen return (EINVAL); 74268f7a013SJaakko Heinonen *to = *from; 74368f7a013SJaakko Heinonen } 74468f7a013SJaakko Heinonen *to = '\0'; 74568f7a013SJaakko Heinonen 7468fac9b7bSEd Schouten if (dev->si_name[0] == '\0') 74768f7a013SJaakko Heinonen return (EINVAL); 74868f7a013SJaakko Heinonen 74968f7a013SJaakko Heinonen /* Disallow "." and ".." components. */ 7508fac9b7bSEd Schouten for (s = dev->si_name;;) { 75168f7a013SJaakko Heinonen for (q = s; *q != '/' && *q != '\0'; q++) 75268f7a013SJaakko Heinonen ; 75368f7a013SJaakko Heinonen if (q - s == 1 && s[0] == '.') 75468f7a013SJaakko Heinonen return (EINVAL); 75568f7a013SJaakko Heinonen if (q - s == 2 && s[0] == '.' && s[1] == '.') 75668f7a013SJaakko Heinonen return (EINVAL); 75768f7a013SJaakko Heinonen if (*q != '/') 75868f7a013SJaakko Heinonen break; 75968f7a013SJaakko Heinonen s = q + 1; 76068f7a013SJaakko Heinonen } 76168f7a013SJaakko Heinonen 7628fac9b7bSEd Schouten if (devfs_dev_exists(dev->si_name) != 0) 76368f7a013SJaakko Heinonen return (EEXIST); 76468f7a013SJaakko Heinonen 76568f7a013SJaakko Heinonen return (0); 76668f7a013SJaakko Heinonen } 76768f7a013SJaakko Heinonen 76848ce5d4cSKonstantin Belousov void 76948ce5d4cSKonstantin Belousov make_dev_args_init_impl(struct make_dev_args *args, size_t sz) 77048ce5d4cSKonstantin Belousov { 77148ce5d4cSKonstantin Belousov 77248ce5d4cSKonstantin Belousov bzero(args, sz); 77348ce5d4cSKonstantin Belousov args->mda_size = sz; 77448ce5d4cSKonstantin Belousov } 77548ce5d4cSKonstantin Belousov 77668f7a013SJaakko Heinonen static int 77748ce5d4cSKonstantin Belousov make_dev_sv(struct make_dev_args *args1, struct cdev **dres, 77848ce5d4cSKonstantin Belousov const char *fmt, va_list ap) 7792a3faf2fSPoul-Henning Kamp { 78068f7a013SJaakko Heinonen struct cdev *dev, *dev_new; 78148ce5d4cSKonstantin Belousov struct make_dev_args args; 78268f7a013SJaakko Heinonen int res; 7832a3faf2fSPoul-Henning Kamp 78448ce5d4cSKonstantin Belousov bzero(&args, sizeof(args)); 78548ce5d4cSKonstantin Belousov if (sizeof(args) < args1->mda_size) 78648ce5d4cSKonstantin Belousov return (EINVAL); 78748ce5d4cSKonstantin Belousov bcopy(args1, &args, args1->mda_size); 78848ce5d4cSKonstantin Belousov KASSERT((args.mda_flags & MAKEDEV_WAITOK) == 0 || 78948ce5d4cSKonstantin Belousov (args.mda_flags & MAKEDEV_NOWAIT) == 0, 79048ce5d4cSKonstantin Belousov ("make_dev_sv: both WAITOK and NOWAIT specified")); 79148ce5d4cSKonstantin Belousov dev_new = devfs_alloc(args.mda_flags); 79268f7a013SJaakko Heinonen if (dev_new == NULL) 793f1bb758dSKonstantin Belousov return (ENOMEM); 794027b1f71SPoul-Henning Kamp dev_lock(); 79548ce5d4cSKonstantin Belousov res = prep_cdevsw(args.mda_devsw, args.mda_flags); 796f1bb758dSKonstantin Belousov if (res != 0) { 797d2ba618aSKonstantin Belousov dev_unlock(); 79868f7a013SJaakko Heinonen devfs_free(dev_new); 799f1bb758dSKonstantin Belousov return (res); 800d2ba618aSKonstantin Belousov } 80148ce5d4cSKonstantin Belousov dev = newdev(&args, dev_new); 802ff91cc99SJaakko Heinonen if ((dev->si_flags & SI_NAMED) == 0) { 80368f7a013SJaakko Heinonen res = prep_devname(dev, fmt, ap); 80468f7a013SJaakko Heinonen if (res != 0) { 80548ce5d4cSKonstantin Belousov if ((args.mda_flags & MAKEDEV_CHECKNAME) == 0) { 80668f7a013SJaakko Heinonen panic( 80748ce5d4cSKonstantin Belousov "make_dev_sv: bad si_name (error=%d, si_name=%s)", 80868f7a013SJaakko Heinonen res, dev->si_name); 80968f7a013SJaakko Heinonen } 81068f7a013SJaakko Heinonen if (dev == dev_new) { 81168f7a013SJaakko Heinonen LIST_REMOVE(dev, si_list); 81268f7a013SJaakko Heinonen dev_unlock(); 81368f7a013SJaakko Heinonen devfs_free(dev); 814889dffbaSKonstantin Belousov } else 815889dffbaSKonstantin Belousov dev_unlock(); 81668f7a013SJaakko Heinonen return (res); 81768f7a013SJaakko Heinonen } 818ff91cc99SJaakko Heinonen } 81948ce5d4cSKonstantin Belousov if ((args.mda_flags & MAKEDEV_REF) != 0) 820de10ffa5SKonstantin Belousov dev_refl(dev); 82148ce5d4cSKonstantin Belousov if ((args.mda_flags & MAKEDEV_ETERNAL) != 0) 8223979450bSKonstantin Belousov dev->si_flags |= SI_ETERNAL; 82398c469d4SPoul-Henning Kamp if (dev->si_flags & SI_CHEAPCLONE && 824e606a3c6SPoul-Henning Kamp dev->si_flags & SI_NAMED) { 82598c469d4SPoul-Henning Kamp /* 82698c469d4SPoul-Henning Kamp * This is allowed as it removes races and generally 82798c469d4SPoul-Henning Kamp * simplifies cloning devices. 828cd690b60SPoul-Henning Kamp * XXX: still ?? 82998c469d4SPoul-Henning Kamp */ 8309bc911d4SKonstantin Belousov dev_unlock_and_free(); 831f1bb758dSKonstantin Belousov *dres = dev; 832f1bb758dSKonstantin Belousov return (0); 83398c469d4SPoul-Henning Kamp } 834cd690b60SPoul-Henning Kamp KASSERT(!(dev->si_flags & SI_NAMED), 835ff7284eeSPoul-Henning Kamp ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)", 83648ce5d4cSKonstantin Belousov args.mda_devsw->d_name, dev2unit(dev), devtoname(dev))); 8375ef2707eSPoul-Henning Kamp dev->si_flags |= SI_NAMED; 83848ce5d4cSKonstantin Belousov if (args.mda_cr != NULL) 83948ce5d4cSKonstantin Belousov dev->si_cred = crhold(args.mda_cr); 84048ce5d4cSKonstantin Belousov dev->si_uid = args.mda_uid; 84148ce5d4cSKonstantin Belousov dev->si_gid = args.mda_gid; 84248ce5d4cSKonstantin Belousov dev->si_mode = args.mda_mode; 8431744fcd0SJulian Elischer 8449285a87eSPoul-Henning Kamp devfs_create(dev); 84509828ba9SKonstantin Belousov clean_unrhdrl(devfs_inos); 846aeeb4202SKonstantin Belousov dev_unlock_and_free(); 84748504cc2SKonstantin Belousov 84848ce5d4cSKonstantin Belousov notify_create(dev, args.mda_flags); 84948504cc2SKonstantin Belousov 850f1bb758dSKonstantin Belousov *dres = dev; 851f1bb758dSKonstantin Belousov return (0); 8523f54a085SPoul-Henning Kamp } 8533f54a085SPoul-Henning Kamp 85448ce5d4cSKonstantin Belousov int 85548ce5d4cSKonstantin Belousov make_dev_s(struct make_dev_args *args, struct cdev **dres, 85648ce5d4cSKonstantin Belousov const char *fmt, ...) 85748ce5d4cSKonstantin Belousov { 85848ce5d4cSKonstantin Belousov va_list ap; 85948ce5d4cSKonstantin Belousov int res; 86048ce5d4cSKonstantin Belousov 86148ce5d4cSKonstantin Belousov va_start(ap, fmt); 86248ce5d4cSKonstantin Belousov res = make_dev_sv(args, dres, fmt, ap); 86348ce5d4cSKonstantin Belousov va_end(ap); 86448ce5d4cSKonstantin Belousov return (res); 86548ce5d4cSKonstantin Belousov } 86648ce5d4cSKonstantin Belousov 86748ce5d4cSKonstantin Belousov static int 86848ce5d4cSKonstantin Belousov make_dev_credv(int flags, struct cdev **dres, struct cdevsw *devsw, int unit, 86948ce5d4cSKonstantin Belousov struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, 87048ce5d4cSKonstantin Belousov va_list ap) 87148ce5d4cSKonstantin Belousov { 87248ce5d4cSKonstantin Belousov struct make_dev_args args; 87348ce5d4cSKonstantin Belousov 87448ce5d4cSKonstantin Belousov make_dev_args_init(&args); 87548ce5d4cSKonstantin Belousov args.mda_flags = flags; 87648ce5d4cSKonstantin Belousov args.mda_devsw = devsw; 87748ce5d4cSKonstantin Belousov args.mda_cr = cr; 87848ce5d4cSKonstantin Belousov args.mda_uid = uid; 87948ce5d4cSKonstantin Belousov args.mda_gid = gid; 88048ce5d4cSKonstantin Belousov args.mda_mode = mode; 88148ce5d4cSKonstantin Belousov args.mda_unit = unit; 88248ce5d4cSKonstantin Belousov return (make_dev_sv(&args, dres, fmt, ap)); 88348ce5d4cSKonstantin Belousov } 88448ce5d4cSKonstantin Belousov 885d26dd2d9SRobert Watson struct cdev * 886edde8745SEd Schouten make_dev(struct cdevsw *devsw, int unit, uid_t uid, gid_t gid, int mode, 887d26dd2d9SRobert Watson const char *fmt, ...) 888d26dd2d9SRobert Watson { 889d26dd2d9SRobert Watson struct cdev *dev; 890d26dd2d9SRobert Watson va_list ap; 8916fa5abfdSMatt Macy int res __unused; 892d26dd2d9SRobert Watson 893d26dd2d9SRobert Watson va_start(ap, fmt); 8946fa5abfdSMatt Macy res = make_dev_credv(0, &dev, devsw, unit, NULL, uid, gid, mode, fmt, 8956fa5abfdSMatt Macy ap); 896d26dd2d9SRobert Watson va_end(ap); 89768f7a013SJaakko Heinonen KASSERT(res == 0 && dev != NULL, 89868f7a013SJaakko Heinonen ("make_dev: failed make_dev_credv (error=%d)", res)); 899d26dd2d9SRobert Watson return (dev); 900d26dd2d9SRobert Watson } 901d26dd2d9SRobert Watson 902d26dd2d9SRobert Watson struct cdev * 903edde8745SEd Schouten make_dev_cred(struct cdevsw *devsw, int unit, struct ucred *cr, uid_t uid, 904d26dd2d9SRobert Watson gid_t gid, int mode, const char *fmt, ...) 905d26dd2d9SRobert Watson { 906d26dd2d9SRobert Watson struct cdev *dev; 907d26dd2d9SRobert Watson va_list ap; 9086fa5abfdSMatt Macy int res __unused; 909d26dd2d9SRobert Watson 910d26dd2d9SRobert Watson va_start(ap, fmt); 9116fa5abfdSMatt Macy res = make_dev_credv(0, &dev, devsw, unit, cr, uid, gid, mode, fmt, ap); 912de10ffa5SKonstantin Belousov va_end(ap); 913de10ffa5SKonstantin Belousov 914f1bb758dSKonstantin Belousov KASSERT(res == 0 && dev != NULL, 91568f7a013SJaakko Heinonen ("make_dev_cred: failed make_dev_credv (error=%d)", res)); 916de10ffa5SKonstantin Belousov return (dev); 917de10ffa5SKonstantin Belousov } 918de10ffa5SKonstantin Belousov 919de10ffa5SKonstantin Belousov struct cdev * 920f1bb758dSKonstantin Belousov make_dev_credf(int flags, struct cdevsw *devsw, int unit, struct ucred *cr, 921f1bb758dSKonstantin Belousov uid_t uid, gid_t gid, int mode, const char *fmt, ...) 922de10ffa5SKonstantin Belousov { 923de10ffa5SKonstantin Belousov struct cdev *dev; 924de10ffa5SKonstantin Belousov va_list ap; 925f1bb758dSKonstantin Belousov int res; 926de10ffa5SKonstantin Belousov 927de10ffa5SKonstantin Belousov va_start(ap, fmt); 928f1bb758dSKonstantin Belousov res = make_dev_credv(flags, &dev, devsw, unit, cr, uid, gid, mode, 929de10ffa5SKonstantin Belousov fmt, ap); 930d26dd2d9SRobert Watson va_end(ap); 931d26dd2d9SRobert Watson 93268f7a013SJaakko Heinonen KASSERT(((flags & MAKEDEV_NOWAIT) != 0 && res == ENOMEM) || 93368f7a013SJaakko Heinonen ((flags & MAKEDEV_CHECKNAME) != 0 && res != ENOMEM) || res == 0, 93468f7a013SJaakko Heinonen ("make_dev_credf: failed make_dev_credv (error=%d)", res)); 935f1bb758dSKonstantin Belousov return (res == 0 ? dev : NULL); 936f1bb758dSKonstantin Belousov } 937f1bb758dSKonstantin Belousov 938f1bb758dSKonstantin Belousov int 9392e983aceSEd Schouten make_dev_p(int flags, struct cdev **cdev, struct cdevsw *devsw, 940f1bb758dSKonstantin Belousov struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, ...) 941f1bb758dSKonstantin Belousov { 942f1bb758dSKonstantin Belousov va_list ap; 943f1bb758dSKonstantin Belousov int res; 944f1bb758dSKonstantin Belousov 945f1bb758dSKonstantin Belousov va_start(ap, fmt); 9462e983aceSEd Schouten res = make_dev_credv(flags, cdev, devsw, 0, cr, uid, gid, mode, 947f1bb758dSKonstantin Belousov fmt, ap); 948f1bb758dSKonstantin Belousov va_end(ap); 949f1bb758dSKonstantin Belousov 95068f7a013SJaakko Heinonen KASSERT(((flags & MAKEDEV_NOWAIT) != 0 && res == ENOMEM) || 95168f7a013SJaakko Heinonen ((flags & MAKEDEV_CHECKNAME) != 0 && res != ENOMEM) || res == 0, 95268f7a013SJaakko Heinonen ("make_dev_p: failed make_dev_credv (error=%d)", res)); 953f1bb758dSKonstantin Belousov return (res); 954d26dd2d9SRobert Watson } 955d26dd2d9SRobert Watson 956e606a3c6SPoul-Henning Kamp static void 957e606a3c6SPoul-Henning Kamp dev_dependsl(struct cdev *pdev, struct cdev *cdev) 958e606a3c6SPoul-Henning Kamp { 959e606a3c6SPoul-Henning Kamp 960e606a3c6SPoul-Henning Kamp cdev->si_parent = pdev; 961e606a3c6SPoul-Henning Kamp cdev->si_flags |= SI_CHILD; 962e606a3c6SPoul-Henning Kamp LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings); 963e606a3c6SPoul-Henning Kamp } 964e606a3c6SPoul-Henning Kamp 965e606a3c6SPoul-Henning Kamp 9663344c5a1SPoul-Henning Kamp void 96789c9c53dSPoul-Henning Kamp dev_depends(struct cdev *pdev, struct cdev *cdev) 9683344c5a1SPoul-Henning Kamp { 9693344c5a1SPoul-Henning Kamp 970a0e78d2eSPoul-Henning Kamp dev_lock(); 971e606a3c6SPoul-Henning Kamp dev_dependsl(pdev, cdev); 972a0e78d2eSPoul-Henning Kamp dev_unlock(); 9733344c5a1SPoul-Henning Kamp } 9743344c5a1SPoul-Henning Kamp 975b50a7799SAndrey V. Elsukov static int 976b50a7799SAndrey V. Elsukov make_dev_alias_v(int flags, struct cdev **cdev, struct cdev *pdev, 977b50a7799SAndrey V. Elsukov const char *fmt, va_list ap) 9783f54a085SPoul-Henning Kamp { 97989c9c53dSPoul-Henning Kamp struct cdev *dev; 98068f7a013SJaakko Heinonen int error; 9813f54a085SPoul-Henning Kamp 982b50a7799SAndrey V. Elsukov KASSERT(pdev != NULL, ("make_dev_alias_v: pdev is NULL")); 983b50a7799SAndrey V. Elsukov KASSERT((flags & MAKEDEV_WAITOK) == 0 || (flags & MAKEDEV_NOWAIT) == 0, 984b50a7799SAndrey V. Elsukov ("make_dev_alias_v: both WAITOK and NOWAIT specified")); 985b50a7799SAndrey V. Elsukov KASSERT((flags & ~(MAKEDEV_WAITOK | MAKEDEV_NOWAIT | 986b50a7799SAndrey V. Elsukov MAKEDEV_CHECKNAME)) == 0, 987b50a7799SAndrey V. Elsukov ("make_dev_alias_v: invalid flags specified (flags=%02x)", flags)); 988b50a7799SAndrey V. Elsukov 989b50a7799SAndrey V. Elsukov dev = devfs_alloc(flags); 990b50a7799SAndrey V. Elsukov if (dev == NULL) 991b50a7799SAndrey V. Elsukov return (ENOMEM); 992a0e78d2eSPoul-Henning Kamp dev_lock(); 9933f54a085SPoul-Henning Kamp dev->si_flags |= SI_ALIAS; 99468f7a013SJaakko Heinonen error = prep_devname(dev, fmt, ap); 99568f7a013SJaakko Heinonen if (error != 0) { 996b50a7799SAndrey V. Elsukov if ((flags & MAKEDEV_CHECKNAME) == 0) { 997b50a7799SAndrey V. Elsukov panic("make_dev_alias_v: bad si_name " 998b50a7799SAndrey V. Elsukov "(error=%d, si_name=%s)", error, dev->si_name); 999b50a7799SAndrey V. Elsukov } 1000b50a7799SAndrey V. Elsukov dev_unlock(); 1001b50a7799SAndrey V. Elsukov devfs_free(dev); 1002b50a7799SAndrey V. Elsukov return (error); 100368f7a013SJaakko Heinonen } 100468f7a013SJaakko Heinonen dev->si_flags |= SI_NAMED; 10059285a87eSPoul-Henning Kamp devfs_create(dev); 1006ae95dc62SKonstantin Belousov dev_dependsl(pdev, dev); 100709828ba9SKonstantin Belousov clean_unrhdrl(devfs_inos); 1008a0e78d2eSPoul-Henning Kamp dev_unlock(); 100948504cc2SKonstantin Belousov 1010b50a7799SAndrey V. Elsukov notify_create(dev, flags); 1011b50a7799SAndrey V. Elsukov *cdev = dev; 101248504cc2SKonstantin Belousov 1013b50a7799SAndrey V. Elsukov return (0); 1014b50a7799SAndrey V. Elsukov } 1015b50a7799SAndrey V. Elsukov 1016b50a7799SAndrey V. Elsukov struct cdev * 1017b50a7799SAndrey V. Elsukov make_dev_alias(struct cdev *pdev, const char *fmt, ...) 1018b50a7799SAndrey V. Elsukov { 1019b50a7799SAndrey V. Elsukov struct cdev *dev; 1020b50a7799SAndrey V. Elsukov va_list ap; 10216fa5abfdSMatt Macy int res __unused; 10226fa5abfdSMatt Macy 1023b50a7799SAndrey V. Elsukov va_start(ap, fmt); 10246fa5abfdSMatt Macy res = make_dev_alias_v(MAKEDEV_WAITOK, &dev, pdev, fmt, ap); 1025b50a7799SAndrey V. Elsukov va_end(ap); 1026b50a7799SAndrey V. Elsukov 1027b50a7799SAndrey V. Elsukov KASSERT(res == 0 && dev != NULL, 1028b50a7799SAndrey V. Elsukov ("make_dev_alias: failed make_dev_alias_v (error=%d)", res)); 10290ef1c826SPoul-Henning Kamp return (dev); 10300ef1c826SPoul-Henning Kamp } 10310ef1c826SPoul-Henning Kamp 1032b50a7799SAndrey V. Elsukov int 1033b50a7799SAndrey V. Elsukov make_dev_alias_p(int flags, struct cdev **cdev, struct cdev *pdev, 1034b50a7799SAndrey V. Elsukov const char *fmt, ...) 1035b50a7799SAndrey V. Elsukov { 1036b50a7799SAndrey V. Elsukov va_list ap; 1037b50a7799SAndrey V. Elsukov int res; 1038b50a7799SAndrey V. Elsukov 1039b50a7799SAndrey V. Elsukov va_start(ap, fmt); 1040b50a7799SAndrey V. Elsukov res = make_dev_alias_v(flags, cdev, pdev, fmt, ap); 1041b50a7799SAndrey V. Elsukov va_end(ap); 1042b50a7799SAndrey V. Elsukov return (res); 1043b50a7799SAndrey V. Elsukov } 1044b50a7799SAndrey V. Elsukov 1045aa76615dSJustin T. Gibbs int 1046aa76615dSJustin T. Gibbs make_dev_physpath_alias(int flags, struct cdev **cdev, struct cdev *pdev, 1047aa76615dSJustin T. Gibbs struct cdev *old_alias, const char *physpath) 1048aa76615dSJustin T. Gibbs { 1049aa76615dSJustin T. Gibbs char *devfspath; 1050aa76615dSJustin T. Gibbs int physpath_len; 1051aa76615dSJustin T. Gibbs int max_parentpath_len; 1052aa76615dSJustin T. Gibbs int parentpath_len; 1053aa76615dSJustin T. Gibbs int devfspathbuf_len; 1054aa76615dSJustin T. Gibbs int mflags; 1055aa76615dSJustin T. Gibbs int ret; 1056aa76615dSJustin T. Gibbs 1057aa76615dSJustin T. Gibbs *cdev = NULL; 1058aa76615dSJustin T. Gibbs devfspath = NULL; 1059aa76615dSJustin T. Gibbs physpath_len = strlen(physpath); 1060aa76615dSJustin T. Gibbs ret = EINVAL; 1061aa76615dSJustin T. Gibbs if (physpath_len == 0) 1062aa76615dSJustin T. Gibbs goto out; 1063aa76615dSJustin T. Gibbs 1064aa76615dSJustin T. Gibbs if (strncmp("id1,", physpath, 4) == 0) { 1065aa76615dSJustin T. Gibbs physpath += 4; 1066aa76615dSJustin T. Gibbs physpath_len -= 4; 1067aa76615dSJustin T. Gibbs if (physpath_len == 0) 1068aa76615dSJustin T. Gibbs goto out; 1069aa76615dSJustin T. Gibbs } 1070aa76615dSJustin T. Gibbs 1071aa76615dSJustin T. Gibbs max_parentpath_len = SPECNAMELEN - physpath_len - /*/*/1; 1072aa76615dSJustin T. Gibbs parentpath_len = strlen(pdev->si_name); 1073aa76615dSJustin T. Gibbs if (max_parentpath_len < parentpath_len) { 107420654f4eSAlexander Motin if (bootverbose) 107520654f4eSAlexander Motin printf("WARNING: Unable to alias %s " 1076aa76615dSJustin T. Gibbs "to %s/%s - path too long\n", 1077aa76615dSJustin T. Gibbs pdev->si_name, physpath, pdev->si_name); 1078aa76615dSJustin T. Gibbs ret = ENAMETOOLONG; 1079aa76615dSJustin T. Gibbs goto out; 1080aa76615dSJustin T. Gibbs } 1081aa76615dSJustin T. Gibbs 1082aa76615dSJustin T. Gibbs mflags = (flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK; 1083aa76615dSJustin T. Gibbs devfspathbuf_len = physpath_len + /*/*/1 + parentpath_len + /*NUL*/1; 1084aa76615dSJustin T. Gibbs devfspath = malloc(devfspathbuf_len, M_DEVBUF, mflags); 1085aa76615dSJustin T. Gibbs if (devfspath == NULL) { 1086aa76615dSJustin T. Gibbs ret = ENOMEM; 1087aa76615dSJustin T. Gibbs goto out; 1088aa76615dSJustin T. Gibbs } 1089aa76615dSJustin T. Gibbs 1090aa76615dSJustin T. Gibbs sprintf(devfspath, "%s/%s", physpath, pdev->si_name); 10914d651f4eSKonstantin Belousov if (old_alias != NULL && strcmp(old_alias->si_name, devfspath) == 0) { 1092aa76615dSJustin T. Gibbs /* Retain the existing alias. */ 1093aa76615dSJustin T. Gibbs *cdev = old_alias; 1094aa76615dSJustin T. Gibbs old_alias = NULL; 1095aa76615dSJustin T. Gibbs ret = 0; 1096aa76615dSJustin T. Gibbs } else { 1097f403cfb1SKonstantin Belousov ret = make_dev_alias_p(flags, cdev, pdev, "%s", devfspath); 1098aa76615dSJustin T. Gibbs } 1099aa76615dSJustin T. Gibbs out: 1100aa76615dSJustin T. Gibbs if (old_alias != NULL) 1101aa76615dSJustin T. Gibbs destroy_dev(old_alias); 1102aa76615dSJustin T. Gibbs if (devfspath != NULL) 1103aa76615dSJustin T. Gibbs free(devfspath, M_DEVBUF); 1104aa76615dSJustin T. Gibbs return (ret); 1105aa76615dSJustin T. Gibbs } 1106aa76615dSJustin T. Gibbs 1107cd690b60SPoul-Henning Kamp static void 1108aa2f6ddcSPoul-Henning Kamp destroy_devl(struct cdev *dev) 1109d137acccSPoul-Henning Kamp { 1110743cd76aSPoul-Henning Kamp struct cdevsw *csw; 11110bad52e1SHans Petter Selasky struct cdev_privdata *p; 11123b50dff5SKonstantin Belousov struct cdev_priv *cdp; 1113743cd76aSPoul-Henning Kamp 1114aa2f6ddcSPoul-Henning Kamp mtx_assert(&devmtx, MA_OWNED); 1115743cd76aSPoul-Henning Kamp KASSERT(dev->si_flags & SI_NAMED, 11166bfa9a2dSEd Schouten ("WARNING: Driver mistake: destroy_dev on %d\n", dev2unit(dev))); 11173979450bSKonstantin Belousov KASSERT((dev->si_flags & SI_ETERNAL) == 0, 11183979450bSKonstantin Belousov ("WARNING: Driver mistake: destroy_dev on eternal %d\n", 11193979450bSKonstantin Belousov dev2unit(dev))); 11205ef2707eSPoul-Henning Kamp 11213b50dff5SKonstantin Belousov cdp = cdev2priv(dev); 11223b50dff5SKonstantin Belousov if ((cdp->cdp_flags & CDP_UNREF_DTR) == 0) { 11233b50dff5SKonstantin Belousov /* 11243b50dff5SKonstantin Belousov * Avoid race with dev_rel(), e.g. from the populate 11253b50dff5SKonstantin Belousov * loop. If CDP_UNREF_DTR flag is set, the reference 11263b50dff5SKonstantin Belousov * to be dropped at the end of destroy_devl() was 11273b50dff5SKonstantin Belousov * already taken by delist_dev_locked(). 11283b50dff5SKonstantin Belousov */ 11293b50dff5SKonstantin Belousov dev_refl(dev); 11303b50dff5SKonstantin Belousov 11319285a87eSPoul-Henning Kamp devfs_destroy(dev); 11323b50dff5SKonstantin Belousov } 1133cd690b60SPoul-Henning Kamp 1134cd690b60SPoul-Henning Kamp /* Remove name marking */ 1135b0b03348SPoul-Henning Kamp dev->si_flags &= ~SI_NAMED; 1136b0b03348SPoul-Henning Kamp 1137cd690b60SPoul-Henning Kamp /* If we are a child, remove us from the parents list */ 11383344c5a1SPoul-Henning Kamp if (dev->si_flags & SI_CHILD) { 11393344c5a1SPoul-Henning Kamp LIST_REMOVE(dev, si_siblings); 11403344c5a1SPoul-Henning Kamp dev->si_flags &= ~SI_CHILD; 11413344c5a1SPoul-Henning Kamp } 1142cd690b60SPoul-Henning Kamp 1143cd690b60SPoul-Henning Kamp /* Kill our children */ 11443344c5a1SPoul-Henning Kamp while (!LIST_EMPTY(&dev->si_children)) 1145aa2f6ddcSPoul-Henning Kamp destroy_devl(LIST_FIRST(&dev->si_children)); 1146cd690b60SPoul-Henning Kamp 1147cd690b60SPoul-Henning Kamp /* Remove from clone list */ 1148b0b03348SPoul-Henning Kamp if (dev->si_flags & SI_CLONELIST) { 1149b0b03348SPoul-Henning Kamp LIST_REMOVE(dev, si_clone); 1150b0b03348SPoul-Henning Kamp dev->si_flags &= ~SI_CLONELIST; 1151b0b03348SPoul-Henning Kamp } 1152cd690b60SPoul-Henning Kamp 1153743cd76aSPoul-Henning Kamp csw = dev->si_devsw; 11541abf2c36SBrian Feldman dev->si_devsw = NULL; /* already NULL for SI_ALIAS */ 11551abf2c36SBrian Feldman while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) { 1156743cd76aSPoul-Henning Kamp csw->d_purge(dev); 1157743cd76aSPoul-Henning Kamp msleep(csw, &devmtx, PRIBIO, "devprg", hz/10); 1158d595182fSPoul-Henning Kamp if (dev->si_threadcount) 1159d595182fSPoul-Henning Kamp printf("Still %lu threads in %s\n", 1160d595182fSPoul-Henning Kamp dev->si_threadcount, devtoname(dev)); 1161743cd76aSPoul-Henning Kamp } 1162e0c33ad5STor Egge while (dev->si_threadcount != 0) { 1163e0c33ad5STor Egge /* Use unique dummy wait ident */ 1164e0c33ad5STor Egge msleep(&csw, &devmtx, PRIBIO, "devdrn", hz / 10); 1165e0c33ad5STor Egge } 1166743cd76aSPoul-Henning Kamp 116782f4d640SKonstantin Belousov dev_unlock(); 11682793ea13SHans Petter Selasky if ((cdp->cdp_flags & CDP_UNREF_DTR) == 0) { 11692793ea13SHans Petter Selasky /* avoid out of order notify events */ 117048504cc2SKonstantin Belousov notify_destroy(dev); 11712793ea13SHans Petter Selasky } 117282f4d640SKonstantin Belousov mtx_lock(&cdevpriv_mtx); 11732793ea13SHans Petter Selasky while ((p = LIST_FIRST(&cdp->cdp_fdpriv)) != NULL) { 117482f4d640SKonstantin Belousov devfs_destroy_cdevpriv(p); 117582f4d640SKonstantin Belousov mtx_lock(&cdevpriv_mtx); 117682f4d640SKonstantin Belousov } 117782f4d640SKonstantin Belousov mtx_unlock(&cdevpriv_mtx); 117882f4d640SKonstantin Belousov dev_lock(); 117948504cc2SKonstantin Belousov 1180743cd76aSPoul-Henning Kamp dev->si_drv1 = 0; 1181743cd76aSPoul-Henning Kamp dev->si_drv2 = 0; 1182743cd76aSPoul-Henning Kamp bzero(&dev->__si_u, sizeof(dev->__si_u)); 1183743cd76aSPoul-Henning Kamp 1184cd690b60SPoul-Henning Kamp if (!(dev->si_flags & SI_ALIAS)) { 1185cd690b60SPoul-Henning Kamp /* Remove from cdevsw list */ 1186cd690b60SPoul-Henning Kamp LIST_REMOVE(dev, si_list); 1187cd690b60SPoul-Henning Kamp 1188e606a3c6SPoul-Henning Kamp /* If cdevsw has no more struct cdev *'s, clean it */ 1189de10ffa5SKonstantin Belousov if (LIST_EMPTY(&csw->d_devs)) { 1190a5993c33SPoul-Henning Kamp fini_cdevsw(csw); 1191de10ffa5SKonstantin Belousov wakeup(&csw->d_devs); 1192de10ffa5SKonstantin Belousov } 1193cd690b60SPoul-Henning Kamp } 11945ef2707eSPoul-Henning Kamp dev->si_flags &= ~SI_ALIAS; 11953b50dff5SKonstantin Belousov cdp->cdp_flags &= ~CDP_UNREF_DTR; 11963b50dff5SKonstantin Belousov dev->si_refcount--; 1197743cd76aSPoul-Henning Kamp 11983b50dff5SKonstantin Belousov if (dev->si_refcount > 0) 1199cd690b60SPoul-Henning Kamp LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list); 12003b50dff5SKonstantin Belousov else 12019bc911d4SKonstantin Belousov dev_free_devlocked(dev); 1202d137acccSPoul-Henning Kamp } 1203cd690b60SPoul-Henning Kamp 120407dbde67SHans Petter Selasky static void 120507dbde67SHans Petter Selasky delist_dev_locked(struct cdev *dev) 120607dbde67SHans Petter Selasky { 12073b50dff5SKonstantin Belousov struct cdev_priv *cdp; 120807dbde67SHans Petter Selasky struct cdev *child; 12093b50dff5SKonstantin Belousov 12103b50dff5SKonstantin Belousov mtx_assert(&devmtx, MA_OWNED); 12113b50dff5SKonstantin Belousov cdp = cdev2priv(dev); 12123b50dff5SKonstantin Belousov if ((cdp->cdp_flags & CDP_UNREF_DTR) != 0) 12133b50dff5SKonstantin Belousov return; 12143b50dff5SKonstantin Belousov cdp->cdp_flags |= CDP_UNREF_DTR; 12153b50dff5SKonstantin Belousov dev_refl(dev); 121607dbde67SHans Petter Selasky devfs_destroy(dev); 121707dbde67SHans Petter Selasky LIST_FOREACH(child, &dev->si_children, si_siblings) 121807dbde67SHans Petter Selasky delist_dev_locked(child); 12192793ea13SHans Petter Selasky dev_unlock(); 12202793ea13SHans Petter Selasky /* ensure the destroy event is queued in order */ 12212793ea13SHans Petter Selasky notify_destroy(dev); 12222793ea13SHans Petter Selasky dev_lock(); 122307dbde67SHans Petter Selasky } 122407dbde67SHans Petter Selasky 12252793ea13SHans Petter Selasky /* 12262793ea13SHans Petter Selasky * This function will delist a character device and its children from 12272793ea13SHans Petter Selasky * the directory listing and create a destroy event without waiting 12282793ea13SHans Petter Selasky * for all character device references to go away. At some later point 12292793ea13SHans Petter Selasky * destroy_dev() must be called to complete the character device 12302793ea13SHans Petter Selasky * destruction. After calling this function the character device name 12312793ea13SHans Petter Selasky * can instantly be re-used. 12322793ea13SHans Petter Selasky */ 123307dbde67SHans Petter Selasky void 123407dbde67SHans Petter Selasky delist_dev(struct cdev *dev) 123507dbde67SHans Petter Selasky { 12363b50dff5SKonstantin Belousov 12372793ea13SHans Petter Selasky WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "delist_dev"); 123807dbde67SHans Petter Selasky dev_lock(); 123907dbde67SHans Petter Selasky delist_dev_locked(dev); 124007dbde67SHans Petter Selasky dev_unlock(); 124107dbde67SHans Petter Selasky } 124207dbde67SHans Petter Selasky 1243cd690b60SPoul-Henning Kamp void 124489c9c53dSPoul-Henning Kamp destroy_dev(struct cdev *dev) 1245cd690b60SPoul-Henning Kamp { 1246cd690b60SPoul-Henning Kamp 1247b7a813fcSKonstantin Belousov WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "destroy_dev"); 1248a0e78d2eSPoul-Henning Kamp dev_lock(); 1249aa2f6ddcSPoul-Henning Kamp destroy_devl(dev); 12509bc911d4SKonstantin Belousov dev_unlock_and_free(); 1251cd690b60SPoul-Henning Kamp } 1252d137acccSPoul-Henning Kamp 1253c32cc149SBruce Evans const char * 125489c9c53dSPoul-Henning Kamp devtoname(struct cdev *dev) 1255b8e49f68SBill Fumerola { 1256b8e49f68SBill Fumerola 1257b8e49f68SBill Fumerola return (dev->si_name); 1258b8e49f68SBill Fumerola } 1259db901281SPoul-Henning Kamp 1260db901281SPoul-Henning Kamp int 126101de1b13SPoul-Henning Kamp dev_stdclone(char *name, char **namep, const char *stem, int *unit) 1262db901281SPoul-Henning Kamp { 1263db901281SPoul-Henning Kamp int u, i; 1264db901281SPoul-Henning Kamp 1265db901281SPoul-Henning Kamp i = strlen(stem); 126656700d46SBrian Somers if (bcmp(stem, name, i) != 0) 126756700d46SBrian Somers return (0); 1268db901281SPoul-Henning Kamp if (!isdigit(name[i])) 1269db901281SPoul-Henning Kamp return (0); 1270db901281SPoul-Henning Kamp u = 0; 127110786074SPoul-Henning Kamp if (name[i] == '0' && isdigit(name[i+1])) 127210786074SPoul-Henning Kamp return (0); 1273db901281SPoul-Henning Kamp while (isdigit(name[i])) { 1274db901281SPoul-Henning Kamp u *= 10; 1275db901281SPoul-Henning Kamp u += name[i++] - '0'; 1276db901281SPoul-Henning Kamp } 1277dab3d85fSBrian Feldman if (u > 0xffffff) 1278dab3d85fSBrian Feldman return (0); 1279db901281SPoul-Henning Kamp *unit = u; 1280db901281SPoul-Henning Kamp if (namep) 1281db901281SPoul-Henning Kamp *namep = &name[i]; 1282db901281SPoul-Henning Kamp if (name[i]) 1283db901281SPoul-Henning Kamp return (2); 1284db901281SPoul-Henning Kamp return (1); 1285db901281SPoul-Henning Kamp } 12868d25eb2cSPoul-Henning Kamp 12878d25eb2cSPoul-Henning Kamp /* 1288b0b03348SPoul-Henning Kamp * Helper functions for cloning device drivers. 1289b0b03348SPoul-Henning Kamp * 1290b0b03348SPoul-Henning Kamp * The objective here is to make it unnecessary for the device drivers to 1291b0b03348SPoul-Henning Kamp * use rman or similar to manage their unit number space. Due to the way 1292b0b03348SPoul-Henning Kamp * we do "on-demand" devices, using rman or other "private" methods 1293b0b03348SPoul-Henning Kamp * will be very tricky to lock down properly once we lock down this file. 1294b0b03348SPoul-Henning Kamp * 12959a98ae94SLukas Ertl * Instead we give the drivers these routines which puts the struct cdev *'s 12969a98ae94SLukas Ertl * that are to be managed on their own list, and gives the driver the ability 1297b0b03348SPoul-Henning Kamp * to ask for the first free unit number or a given specified unit number. 1298b0b03348SPoul-Henning Kamp * 1299b0b03348SPoul-Henning Kamp * In addition these routines support paired devices (pty, nmdm and similar) 1300b0b03348SPoul-Henning Kamp * by respecting a number of "flag" bits in the minor number. 1301b0b03348SPoul-Henning Kamp * 1302b0b03348SPoul-Henning Kamp */ 1303b0b03348SPoul-Henning Kamp 1304b0b03348SPoul-Henning Kamp struct clonedevs { 1305b0b03348SPoul-Henning Kamp LIST_HEAD(,cdev) head; 1306b0b03348SPoul-Henning Kamp }; 1307b0b03348SPoul-Henning Kamp 13089397290eSPoul-Henning Kamp void 13099397290eSPoul-Henning Kamp clone_setup(struct clonedevs **cdp) 13109397290eSPoul-Henning Kamp { 13119397290eSPoul-Henning Kamp 13129397290eSPoul-Henning Kamp *cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO); 13139397290eSPoul-Henning Kamp LIST_INIT(&(*cdp)->head); 13149397290eSPoul-Henning Kamp } 13159397290eSPoul-Henning Kamp 1316b0b03348SPoul-Henning Kamp int 1317cf141467SKonstantin Belousov clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, 1318cf141467SKonstantin Belousov struct cdev **dp, int extra) 1319b0b03348SPoul-Henning Kamp { 1320b0b03348SPoul-Henning Kamp struct clonedevs *cd; 1321027b1f71SPoul-Henning Kamp struct cdev *dev, *ndev, *dl, *de; 132248ce5d4cSKonstantin Belousov struct make_dev_args args; 1323b0b03348SPoul-Henning Kamp int unit, low, u; 1324b0b03348SPoul-Henning Kamp 13259397290eSPoul-Henning Kamp KASSERT(*cdp != NULL, 13269397290eSPoul-Henning Kamp ("clone_setup() not called in driver \"%s\"", csw->d_name)); 1327b0b03348SPoul-Henning Kamp KASSERT(!(extra & CLONE_UNITMASK), 1328b0b03348SPoul-Henning Kamp ("Illegal extra bits (0x%x) in clone_create", extra)); 1329b0b03348SPoul-Henning Kamp KASSERT(*up <= CLONE_UNITMASK, 1330b0b03348SPoul-Henning Kamp ("Too high unit (0x%x) in clone_create", *up)); 133129d4cb24SEd Schouten KASSERT(csw->d_flags & D_NEEDMINOR, 133229d4cb24SEd Schouten ("clone_create() on cdevsw without minor numbers")); 1333b0b03348SPoul-Henning Kamp 1334b0b03348SPoul-Henning Kamp 1335b0b03348SPoul-Henning Kamp /* 1336b0b03348SPoul-Henning Kamp * Search the list for a lot of things in one go: 1337b0b03348SPoul-Henning Kamp * A preexisting match is returned immediately. 1338b0b03348SPoul-Henning Kamp * The lowest free unit number if we are passed -1, and the place 1339b0b03348SPoul-Henning Kamp * in the list where we should insert that new element. 1340b0b03348SPoul-Henning Kamp * The place to insert a specified unit number, if applicable 1341b0b03348SPoul-Henning Kamp * the end of the list. 1342b0b03348SPoul-Henning Kamp */ 1343b0b03348SPoul-Henning Kamp unit = *up; 1344d2ba618aSKonstantin Belousov ndev = devfs_alloc(MAKEDEV_WAITOK); 1345027b1f71SPoul-Henning Kamp dev_lock(); 1346d2ba618aSKonstantin Belousov prep_cdevsw(csw, MAKEDEV_WAITOK); 13478666b655SPoul-Henning Kamp low = extra; 1348b0b03348SPoul-Henning Kamp de = dl = NULL; 13499397290eSPoul-Henning Kamp cd = *cdp; 1350b0b03348SPoul-Henning Kamp LIST_FOREACH(dev, &cd->head, si_clone) { 1351027b1f71SPoul-Henning Kamp KASSERT(dev->si_flags & SI_CLONELIST, 1352027b1f71SPoul-Henning Kamp ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); 1353b0b03348SPoul-Henning Kamp u = dev2unit(dev); 1354b0b03348SPoul-Henning Kamp if (u == (unit | extra)) { 1355b0b03348SPoul-Henning Kamp *dp = dev; 1356027b1f71SPoul-Henning Kamp dev_unlock(); 13579bc911d4SKonstantin Belousov devfs_free(ndev); 1358b0b03348SPoul-Henning Kamp return (0); 1359b0b03348SPoul-Henning Kamp } 1360b0b03348SPoul-Henning Kamp if (unit == -1 && u == low) { 1361b0b03348SPoul-Henning Kamp low++; 1362b0b03348SPoul-Henning Kamp de = dev; 1363b0b03348SPoul-Henning Kamp continue; 13647bbb3a26SPoul-Henning Kamp } else if (u < (unit | extra)) { 13657bbb3a26SPoul-Henning Kamp de = dev; 13667bbb3a26SPoul-Henning Kamp continue; 13677bbb3a26SPoul-Henning Kamp } else if (u > (unit | extra)) { 1368b0b03348SPoul-Henning Kamp dl = dev; 1369b0b03348SPoul-Henning Kamp break; 1370b0b03348SPoul-Henning Kamp } 1371b0b03348SPoul-Henning Kamp } 1372b0b03348SPoul-Henning Kamp if (unit == -1) 13738666b655SPoul-Henning Kamp unit = low & CLONE_UNITMASK; 137448ce5d4cSKonstantin Belousov make_dev_args_init(&args); 137548ce5d4cSKonstantin Belousov args.mda_unit = unit | extra; 137648ce5d4cSKonstantin Belousov args.mda_devsw = csw; 137748ce5d4cSKonstantin Belousov dev = newdev(&args, ndev); 1378027b1f71SPoul-Henning Kamp if (dev->si_flags & SI_CLONELIST) { 1379027b1f71SPoul-Henning Kamp printf("dev %p (%s) is on clonelist\n", dev, dev->si_name); 13807bbb3a26SPoul-Henning Kamp printf("unit=%d, low=%d, extra=0x%x\n", unit, low, extra); 1381027b1f71SPoul-Henning Kamp LIST_FOREACH(dev, &cd->head, si_clone) { 1382027b1f71SPoul-Henning Kamp printf("\t%p %s\n", dev, dev->si_name); 1383027b1f71SPoul-Henning Kamp } 1384027b1f71SPoul-Henning Kamp panic("foo"); 1385027b1f71SPoul-Henning Kamp } 1386b0b03348SPoul-Henning Kamp KASSERT(!(dev->si_flags & SI_CLONELIST), 1387027b1f71SPoul-Henning Kamp ("Dev %p(%s) should not be on clonelist", dev, dev->si_name)); 1388b0b03348SPoul-Henning Kamp if (dl != NULL) 1389b0b03348SPoul-Henning Kamp LIST_INSERT_BEFORE(dl, dev, si_clone); 1390b0b03348SPoul-Henning Kamp else if (de != NULL) 1391b0b03348SPoul-Henning Kamp LIST_INSERT_AFTER(de, dev, si_clone); 1392b0b03348SPoul-Henning Kamp else 1393b0b03348SPoul-Henning Kamp LIST_INSERT_HEAD(&cd->head, dev, si_clone); 1394b0b03348SPoul-Henning Kamp dev->si_flags |= SI_CLONELIST; 1395b0b03348SPoul-Henning Kamp *up = unit; 13969bc911d4SKonstantin Belousov dev_unlock_and_free(); 1397b0b03348SPoul-Henning Kamp return (1); 1398b0b03348SPoul-Henning Kamp } 1399b0b03348SPoul-Henning Kamp 1400b0b03348SPoul-Henning Kamp /* 1401b0b03348SPoul-Henning Kamp * Kill everything still on the list. The driver should already have 140289c9c53dSPoul-Henning Kamp * disposed of any softc hung of the struct cdev *'s at this time. 1403b0b03348SPoul-Henning Kamp */ 1404b0b03348SPoul-Henning Kamp void 1405b0b03348SPoul-Henning Kamp clone_cleanup(struct clonedevs **cdp) 1406b0b03348SPoul-Henning Kamp { 1407de10ffa5SKonstantin Belousov struct cdev *dev; 1408de10ffa5SKonstantin Belousov struct cdev_priv *cp; 1409b0b03348SPoul-Henning Kamp struct clonedevs *cd; 1410b0b03348SPoul-Henning Kamp 1411b0b03348SPoul-Henning Kamp cd = *cdp; 1412b0b03348SPoul-Henning Kamp if (cd == NULL) 1413b0b03348SPoul-Henning Kamp return; 1414027b1f71SPoul-Henning Kamp dev_lock(); 1415de10ffa5SKonstantin Belousov while (!LIST_EMPTY(&cd->head)) { 1416de10ffa5SKonstantin Belousov dev = LIST_FIRST(&cd->head); 1417de10ffa5SKonstantin Belousov LIST_REMOVE(dev, si_clone); 1418027b1f71SPoul-Henning Kamp KASSERT(dev->si_flags & SI_CLONELIST, 1419027b1f71SPoul-Henning Kamp ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); 1420de10ffa5SKonstantin Belousov dev->si_flags &= ~SI_CLONELIST; 142105427aafSKonstantin Belousov cp = cdev2priv(dev); 1422de10ffa5SKonstantin Belousov if (!(cp->cdp_flags & CDP_SCHED_DTR)) { 1423de10ffa5SKonstantin Belousov cp->cdp_flags |= CDP_SCHED_DTR; 1424b0b03348SPoul-Henning Kamp KASSERT(dev->si_flags & SI_NAMED, 1425dde58752SGleb Kurtsou ("Driver has goofed in cloning underways udev %jx unit %x", 1426dde58752SGleb Kurtsou (uintmax_t)dev2udev(dev), dev2unit(dev))); 1427aa2f6ddcSPoul-Henning Kamp destroy_devl(dev); 1428b0b03348SPoul-Henning Kamp } 1429de10ffa5SKonstantin Belousov } 1430aeeb4202SKonstantin Belousov dev_unlock_and_free(); 1431b0b03348SPoul-Henning Kamp free(cd, M_DEVBUF); 1432b0b03348SPoul-Henning Kamp *cdp = NULL; 1433b0b03348SPoul-Henning Kamp } 1434de10ffa5SKonstantin Belousov 1435de10ffa5SKonstantin Belousov static TAILQ_HEAD(, cdev_priv) dev_ddtr = 1436de10ffa5SKonstantin Belousov TAILQ_HEAD_INITIALIZER(dev_ddtr); 1437268e76d8SJohn Baldwin static struct task dev_dtr_task = TASK_INITIALIZER(0, destroy_dev_tq, NULL); 1438de10ffa5SKonstantin Belousov 1439de10ffa5SKonstantin Belousov static void 1440de10ffa5SKonstantin Belousov destroy_dev_tq(void *ctx, int pending) 1441de10ffa5SKonstantin Belousov { 1442de10ffa5SKonstantin Belousov struct cdev_priv *cp; 1443de10ffa5SKonstantin Belousov struct cdev *dev; 1444de10ffa5SKonstantin Belousov void (*cb)(void *); 1445de10ffa5SKonstantin Belousov void *cb_arg; 1446de10ffa5SKonstantin Belousov 1447de10ffa5SKonstantin Belousov dev_lock(); 1448de10ffa5SKonstantin Belousov while (!TAILQ_EMPTY(&dev_ddtr)) { 1449de10ffa5SKonstantin Belousov cp = TAILQ_FIRST(&dev_ddtr); 1450de10ffa5SKonstantin Belousov dev = &cp->cdp_c; 1451de10ffa5SKonstantin Belousov KASSERT(cp->cdp_flags & CDP_SCHED_DTR, 1452de10ffa5SKonstantin Belousov ("cdev %p in dev_destroy_tq without CDP_SCHED_DTR", cp)); 1453de10ffa5SKonstantin Belousov TAILQ_REMOVE(&dev_ddtr, cp, cdp_dtr_list); 1454de10ffa5SKonstantin Belousov cb = cp->cdp_dtr_cb; 1455de10ffa5SKonstantin Belousov cb_arg = cp->cdp_dtr_cb_arg; 1456de10ffa5SKonstantin Belousov destroy_devl(dev); 1457aeeb4202SKonstantin Belousov dev_unlock_and_free(); 1458de10ffa5SKonstantin Belousov dev_rel(dev); 1459de10ffa5SKonstantin Belousov if (cb != NULL) 1460de10ffa5SKonstantin Belousov cb(cb_arg); 1461de10ffa5SKonstantin Belousov dev_lock(); 1462de10ffa5SKonstantin Belousov } 1463de10ffa5SKonstantin Belousov dev_unlock(); 1464de10ffa5SKonstantin Belousov } 1465de10ffa5SKonstantin Belousov 14669d53363bSKonstantin Belousov /* 14679d53363bSKonstantin Belousov * devmtx shall be locked on entry. devmtx will be unlocked after 14689d53363bSKonstantin Belousov * function return. 14699d53363bSKonstantin Belousov */ 14709d53363bSKonstantin Belousov static int 14719d53363bSKonstantin Belousov destroy_dev_sched_cbl(struct cdev *dev, void (*cb)(void *), void *arg) 1472de10ffa5SKonstantin Belousov { 1473de10ffa5SKonstantin Belousov struct cdev_priv *cp; 1474de10ffa5SKonstantin Belousov 14759d53363bSKonstantin Belousov mtx_assert(&devmtx, MA_OWNED); 147605427aafSKonstantin Belousov cp = cdev2priv(dev); 1477de10ffa5SKonstantin Belousov if (cp->cdp_flags & CDP_SCHED_DTR) { 1478de10ffa5SKonstantin Belousov dev_unlock(); 1479de10ffa5SKonstantin Belousov return (0); 1480de10ffa5SKonstantin Belousov } 1481de10ffa5SKonstantin Belousov dev_refl(dev); 1482de10ffa5SKonstantin Belousov cp->cdp_flags |= CDP_SCHED_DTR; 1483de10ffa5SKonstantin Belousov cp->cdp_dtr_cb = cb; 1484de10ffa5SKonstantin Belousov cp->cdp_dtr_cb_arg = arg; 1485de10ffa5SKonstantin Belousov TAILQ_INSERT_TAIL(&dev_ddtr, cp, cdp_dtr_list); 1486de10ffa5SKonstantin Belousov dev_unlock(); 1487de10ffa5SKonstantin Belousov taskqueue_enqueue(taskqueue_swi_giant, &dev_dtr_task); 1488de10ffa5SKonstantin Belousov return (1); 1489de10ffa5SKonstantin Belousov } 1490de10ffa5SKonstantin Belousov 1491de10ffa5SKonstantin Belousov int 14929d53363bSKonstantin Belousov destroy_dev_sched_cb(struct cdev *dev, void (*cb)(void *), void *arg) 14939d53363bSKonstantin Belousov { 1494cf141467SKonstantin Belousov 14959d53363bSKonstantin Belousov dev_lock(); 14969d53363bSKonstantin Belousov return (destroy_dev_sched_cbl(dev, cb, arg)); 14979d53363bSKonstantin Belousov } 14989d53363bSKonstantin Belousov 14999d53363bSKonstantin Belousov int 1500de10ffa5SKonstantin Belousov destroy_dev_sched(struct cdev *dev) 1501de10ffa5SKonstantin Belousov { 1502cf141467SKonstantin Belousov 1503de10ffa5SKonstantin Belousov return (destroy_dev_sched_cb(dev, NULL, NULL)); 1504de10ffa5SKonstantin Belousov } 1505de10ffa5SKonstantin Belousov 1506de10ffa5SKonstantin Belousov void 1507de10ffa5SKonstantin Belousov destroy_dev_drain(struct cdevsw *csw) 1508de10ffa5SKonstantin Belousov { 1509de10ffa5SKonstantin Belousov 1510de10ffa5SKonstantin Belousov dev_lock(); 1511de10ffa5SKonstantin Belousov while (!LIST_EMPTY(&csw->d_devs)) { 1512de10ffa5SKonstantin Belousov msleep(&csw->d_devs, &devmtx, PRIBIO, "devscd", hz/10); 1513de10ffa5SKonstantin Belousov } 1514de10ffa5SKonstantin Belousov dev_unlock(); 1515de10ffa5SKonstantin Belousov } 1516de10ffa5SKonstantin Belousov 1517de10ffa5SKonstantin Belousov void 1518de10ffa5SKonstantin Belousov drain_dev_clone_events(void) 1519de10ffa5SKonstantin Belousov { 1520de10ffa5SKonstantin Belousov 1521de10ffa5SKonstantin Belousov sx_xlock(&clone_drain_lock); 1522de10ffa5SKonstantin Belousov sx_xunlock(&clone_drain_lock); 1523de10ffa5SKonstantin Belousov } 1524de10ffa5SKonstantin Belousov 15256c5e633cSKonstantin Belousov #include "opt_ddb.h" 15266c5e633cSKonstantin Belousov #ifdef DDB 15276c5e633cSKonstantin Belousov #include <sys/kernel.h> 15286c5e633cSKonstantin Belousov 15296c5e633cSKonstantin Belousov #include <ddb/ddb.h> 15306c5e633cSKonstantin Belousov 15316c5e633cSKonstantin Belousov DB_SHOW_COMMAND(cdev, db_show_cdev) 15326c5e633cSKonstantin Belousov { 15336c5e633cSKonstantin Belousov struct cdev_priv *cdp; 15346c5e633cSKonstantin Belousov struct cdev *dev; 15356c5e633cSKonstantin Belousov u_int flags; 15366c5e633cSKonstantin Belousov char buf[512]; 15376c5e633cSKonstantin Belousov 15386c5e633cSKonstantin Belousov if (!have_addr) { 15396c5e633cSKonstantin Belousov TAILQ_FOREACH(cdp, &cdevp_list, cdp_list) { 15406c5e633cSKonstantin Belousov dev = &cdp->cdp_c; 15416c5e633cSKonstantin Belousov db_printf("%s %p\n", dev->si_name, dev); 15426c5e633cSKonstantin Belousov if (db_pager_quit) 15436c5e633cSKonstantin Belousov break; 15446c5e633cSKonstantin Belousov } 15456c5e633cSKonstantin Belousov return; 15466c5e633cSKonstantin Belousov } 15476c5e633cSKonstantin Belousov 15486c5e633cSKonstantin Belousov dev = (struct cdev *)addr; 15496c5e633cSKonstantin Belousov cdp = cdev2priv(dev); 15506c5e633cSKonstantin Belousov db_printf("dev %s ref %d use %ld thr %ld inuse %u fdpriv %p\n", 15516c5e633cSKonstantin Belousov dev->si_name, dev->si_refcount, dev->si_usecount, 15526c5e633cSKonstantin Belousov dev->si_threadcount, cdp->cdp_inuse, cdp->cdp_fdpriv.lh_first); 15536c5e633cSKonstantin Belousov db_printf("devsw %p si_drv0 %d si_drv1 %p si_drv2 %p\n", 15546c5e633cSKonstantin Belousov dev->si_devsw, dev->si_drv0, dev->si_drv1, dev->si_drv2); 15556c5e633cSKonstantin Belousov flags = dev->si_flags; 15566c5e633cSKonstantin Belousov #define SI_FLAG(flag) do { \ 15576c5e633cSKonstantin Belousov if (flags & (flag)) { \ 15586c5e633cSKonstantin Belousov if (buf[0] != '\0') \ 15596c5e633cSKonstantin Belousov strlcat(buf, ", ", sizeof(buf)); \ 15606c5e633cSKonstantin Belousov strlcat(buf, (#flag) + 3, sizeof(buf)); \ 15616c5e633cSKonstantin Belousov flags &= ~(flag); \ 15626c5e633cSKonstantin Belousov } \ 15636c5e633cSKonstantin Belousov } while (0) 15646c5e633cSKonstantin Belousov buf[0] = '\0'; 15656c5e633cSKonstantin Belousov SI_FLAG(SI_ETERNAL); 15666c5e633cSKonstantin Belousov SI_FLAG(SI_ALIAS); 15676c5e633cSKonstantin Belousov SI_FLAG(SI_NAMED); 15686c5e633cSKonstantin Belousov SI_FLAG(SI_CHEAPCLONE); 15696c5e633cSKonstantin Belousov SI_FLAG(SI_CHILD); 15706c5e633cSKonstantin Belousov SI_FLAG(SI_DUMPDEV); 15716c5e633cSKonstantin Belousov SI_FLAG(SI_CLONELIST); 15726c5e633cSKonstantin Belousov db_printf("si_flags %s\n", buf); 15736c5e633cSKonstantin Belousov 15746c5e633cSKonstantin Belousov flags = cdp->cdp_flags; 15756c5e633cSKonstantin Belousov #define CDP_FLAG(flag) do { \ 15766c5e633cSKonstantin Belousov if (flags & (flag)) { \ 15776c5e633cSKonstantin Belousov if (buf[0] != '\0') \ 15786c5e633cSKonstantin Belousov strlcat(buf, ", ", sizeof(buf)); \ 15796c5e633cSKonstantin Belousov strlcat(buf, (#flag) + 4, sizeof(buf)); \ 15806c5e633cSKonstantin Belousov flags &= ~(flag); \ 15816c5e633cSKonstantin Belousov } \ 15826c5e633cSKonstantin Belousov } while (0) 15836c5e633cSKonstantin Belousov buf[0] = '\0'; 15846c5e633cSKonstantin Belousov CDP_FLAG(CDP_ACTIVE); 15856c5e633cSKonstantin Belousov CDP_FLAG(CDP_SCHED_DTR); 15866c5e633cSKonstantin Belousov db_printf("cdp_flags %s\n", buf); 15876c5e633cSKonstantin Belousov } 15886c5e633cSKonstantin Belousov #endif 1589