17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 55e3986cbScth * Common Development and Distribution License (the "License"). 65e3986cbScth * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22a204de77Scth * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* 277c478bd9Sstevel@tonic-gate * This is the device filesystem. 287c478bd9Sstevel@tonic-gate * 297c478bd9Sstevel@tonic-gate * It is a combination of a namer to drive autoconfiguration, 307c478bd9Sstevel@tonic-gate * plus the access methods for the device drivers of the system. 317c478bd9Sstevel@tonic-gate * 327c478bd9Sstevel@tonic-gate * The prototype is fairly dependent on specfs for the latter part 337c478bd9Sstevel@tonic-gate * of its implementation, though a final version would integrate the two. 347c478bd9Sstevel@tonic-gate */ 357c478bd9Sstevel@tonic-gate #include <sys/types.h> 367c478bd9Sstevel@tonic-gate #include <sys/param.h> 377c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 387c478bd9Sstevel@tonic-gate #include <sys/systm.h> 397c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 407c478bd9Sstevel@tonic-gate #include <sys/time.h> 417c478bd9Sstevel@tonic-gate #include <sys/pathname.h> 427c478bd9Sstevel@tonic-gate #include <sys/vfs.h> 43aa59c4cbSrsb #include <sys/vfs_opreg.h> 447c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 457c478bd9Sstevel@tonic-gate #include <sys/stat.h> 467c478bd9Sstevel@tonic-gate #include <sys/uio.h> 477c478bd9Sstevel@tonic-gate #include <sys/stat.h> 487c478bd9Sstevel@tonic-gate #include <sys/errno.h> 497c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 507c478bd9Sstevel@tonic-gate #include <sys/cred.h> 517c478bd9Sstevel@tonic-gate #include <sys/statvfs.h> 527c478bd9Sstevel@tonic-gate #include <sys/mount.h> 537c478bd9Sstevel@tonic-gate #include <sys/debug.h> 547c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 557c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h> 567c478bd9Sstevel@tonic-gate #include <sys/fs/dv_node.h> 577c478bd9Sstevel@tonic-gate #include <sys/fs/snode.h> 587c478bd9Sstevel@tonic-gate #include <sys/sunndi.h> 597c478bd9Sstevel@tonic-gate #include <sys/policy.h> 605e3986cbScth #include <sys/sunmdi.h> 617c478bd9Sstevel@tonic-gate 627c478bd9Sstevel@tonic-gate /* 637c478bd9Sstevel@tonic-gate * devfs vfs operations. 647c478bd9Sstevel@tonic-gate */ 657c478bd9Sstevel@tonic-gate static int devfs_mount(struct vfs *, struct vnode *, struct mounta *, 667c478bd9Sstevel@tonic-gate struct cred *); 677c478bd9Sstevel@tonic-gate static int devfs_unmount(struct vfs *, int, struct cred *); 687c478bd9Sstevel@tonic-gate static int devfs_root(struct vfs *, struct vnode **); 697c478bd9Sstevel@tonic-gate static int devfs_statvfs(struct vfs *, struct statvfs64 *); 707c478bd9Sstevel@tonic-gate static int devfs_mountroot(struct vfs *, enum whymountroot); 717c478bd9Sstevel@tonic-gate 727c478bd9Sstevel@tonic-gate static int devfsinit(int, char *); 737c478bd9Sstevel@tonic-gate 747c478bd9Sstevel@tonic-gate static vfsdef_t devfs_vfssw = { 757c478bd9Sstevel@tonic-gate VFSDEF_VERSION, 767c478bd9Sstevel@tonic-gate "devfs", /* type name string */ 777c478bd9Sstevel@tonic-gate devfsinit, /* init routine */ 787c478bd9Sstevel@tonic-gate 0, /* flags */ 797c478bd9Sstevel@tonic-gate NULL /* mount options table prototype */ 807c478bd9Sstevel@tonic-gate }; 817c478bd9Sstevel@tonic-gate 827c478bd9Sstevel@tonic-gate static kmutex_t devfs_lock; /* protects global data */ 837c478bd9Sstevel@tonic-gate static int devfstype; /* fstype */ 847c478bd9Sstevel@tonic-gate static dev_t devfsdev; /* the fictious 'device' we live on */ 857c478bd9Sstevel@tonic-gate static struct devfs_data *devfs_mntinfo; /* linked list of instances */ 867c478bd9Sstevel@tonic-gate 877c478bd9Sstevel@tonic-gate /* 887c478bd9Sstevel@tonic-gate * Module linkage information 897c478bd9Sstevel@tonic-gate */ 907c478bd9Sstevel@tonic-gate static struct modlfs modlfs = { 91*39b361b2SRichard Bean &mod_fsops, "devices filesystem", &devfs_vfssw 927c478bd9Sstevel@tonic-gate }; 937c478bd9Sstevel@tonic-gate 947c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = { 957c478bd9Sstevel@tonic-gate MODREV_1, (void *)&modlfs, NULL 967c478bd9Sstevel@tonic-gate }; 977c478bd9Sstevel@tonic-gate 987c478bd9Sstevel@tonic-gate int 997c478bd9Sstevel@tonic-gate _init(void) 1007c478bd9Sstevel@tonic-gate { 1017c478bd9Sstevel@tonic-gate int e; 1027c478bd9Sstevel@tonic-gate 1037c478bd9Sstevel@tonic-gate mutex_init(&devfs_lock, "devfs lock", MUTEX_DEFAULT, NULL); 1047c478bd9Sstevel@tonic-gate dv_node_cache_init(); 1057c478bd9Sstevel@tonic-gate if ((e = mod_install(&modlinkage)) != 0) { 1067c478bd9Sstevel@tonic-gate dv_node_cache_fini(); 1077c478bd9Sstevel@tonic-gate mutex_destroy(&devfs_lock); 1087c478bd9Sstevel@tonic-gate return (e); 1097c478bd9Sstevel@tonic-gate } 1107c478bd9Sstevel@tonic-gate dcmn_err(("devfs loaded\n")); 1117c478bd9Sstevel@tonic-gate return (0); 1127c478bd9Sstevel@tonic-gate } 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate int 1157c478bd9Sstevel@tonic-gate _fini(void) 1167c478bd9Sstevel@tonic-gate { 1177c478bd9Sstevel@tonic-gate return (EBUSY); 1187c478bd9Sstevel@tonic-gate } 1197c478bd9Sstevel@tonic-gate 1207c478bd9Sstevel@tonic-gate int 1217c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop) 1227c478bd9Sstevel@tonic-gate { 1237c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 1247c478bd9Sstevel@tonic-gate } 1257c478bd9Sstevel@tonic-gate 1267c478bd9Sstevel@tonic-gate /*ARGSUSED1*/ 1277c478bd9Sstevel@tonic-gate static int 1287c478bd9Sstevel@tonic-gate devfsinit(int fstype, char *name) 1297c478bd9Sstevel@tonic-gate { 1307c478bd9Sstevel@tonic-gate static const fs_operation_def_t devfs_vfsops_template[] = { 131aa59c4cbSrsb VFSNAME_MOUNT, { .vfs_mount = devfs_mount }, 132aa59c4cbSrsb VFSNAME_UNMOUNT, { .vfs_unmount = devfs_unmount }, 133aa59c4cbSrsb VFSNAME_ROOT, { .vfs_root = devfs_root }, 134aa59c4cbSrsb VFSNAME_STATVFS, { .vfs_statvfs = devfs_statvfs }, 135aa59c4cbSrsb VFSNAME_SYNC, { .vfs_sync = fs_sync }, 136aa59c4cbSrsb VFSNAME_MOUNTROOT, { .vfs_mountroot = devfs_mountroot }, 1377c478bd9Sstevel@tonic-gate NULL, NULL 1387c478bd9Sstevel@tonic-gate }; 1397c478bd9Sstevel@tonic-gate int error; 1407c478bd9Sstevel@tonic-gate int dev; 1417c478bd9Sstevel@tonic-gate extern major_t getudev(void); /* gack - what a function */ 1427c478bd9Sstevel@tonic-gate 1437c478bd9Sstevel@tonic-gate devfstype = fstype; 1447c478bd9Sstevel@tonic-gate /* 1457c478bd9Sstevel@tonic-gate * Associate VFS ops vector with this fstype 1467c478bd9Sstevel@tonic-gate */ 1477c478bd9Sstevel@tonic-gate error = vfs_setfsops(fstype, devfs_vfsops_template, NULL); 1487c478bd9Sstevel@tonic-gate if (error != 0) { 1497c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "devfsinit: bad vfs ops template"); 1507c478bd9Sstevel@tonic-gate return (error); 1517c478bd9Sstevel@tonic-gate } 1527c478bd9Sstevel@tonic-gate 1537c478bd9Sstevel@tonic-gate error = vn_make_ops("dev fs", dv_vnodeops_template, &dv_vnodeops); 1547c478bd9Sstevel@tonic-gate if (error != 0) { 1557c478bd9Sstevel@tonic-gate (void) vfs_freevfsops_by_type(fstype); 1567c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "devfsinit: bad vnode ops template"); 1577c478bd9Sstevel@tonic-gate return (error); 1587c478bd9Sstevel@tonic-gate } 1597c478bd9Sstevel@tonic-gate 1607c478bd9Sstevel@tonic-gate /* 1617c478bd9Sstevel@tonic-gate * Invent a dev_t (sigh). 1627c478bd9Sstevel@tonic-gate */ 163a204de77Scth if ((dev = getudev()) == DDI_MAJOR_T_NONE) { 1647c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "%s: can't get unique dev", devfs_vfssw.name); 1657c478bd9Sstevel@tonic-gate dev = 0; 1667c478bd9Sstevel@tonic-gate } 1677c478bd9Sstevel@tonic-gate devfsdev = makedevice(dev, 0); 1687c478bd9Sstevel@tonic-gate 1697c478bd9Sstevel@tonic-gate return (0); 1707c478bd9Sstevel@tonic-gate } 1717c478bd9Sstevel@tonic-gate 1727c478bd9Sstevel@tonic-gate /* 1737c478bd9Sstevel@tonic-gate * The name of the mount point and the name of the attribute 1747c478bd9Sstevel@tonic-gate * filesystem are passed down from userland for now. 1757c478bd9Sstevel@tonic-gate */ 1767c478bd9Sstevel@tonic-gate static int 1777c478bd9Sstevel@tonic-gate devfs_mount(struct vfs *vfsp, struct vnode *mvp, struct mounta *uap, 1787c478bd9Sstevel@tonic-gate struct cred *cr) 1797c478bd9Sstevel@tonic-gate { 1807c478bd9Sstevel@tonic-gate struct devfs_data *devfs_data; 1817c478bd9Sstevel@tonic-gate struct vnode *avp; 1827c478bd9Sstevel@tonic-gate struct dv_node *dv; 1837c478bd9Sstevel@tonic-gate struct vattr va; 1847c478bd9Sstevel@tonic-gate 1857c478bd9Sstevel@tonic-gate dcmn_err(("devfs_mount\n")); 1867c478bd9Sstevel@tonic-gate 1877c478bd9Sstevel@tonic-gate if (secpolicy_fs_mount(cr, mvp, vfsp) != 0) 1887c478bd9Sstevel@tonic-gate return (EPERM); 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate /* 1917c478bd9Sstevel@tonic-gate * check that the mount point is sane 1927c478bd9Sstevel@tonic-gate */ 1937c478bd9Sstevel@tonic-gate if (mvp->v_type != VDIR) 1947c478bd9Sstevel@tonic-gate return (ENOTDIR); 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate ASSERT(uap->flags & MS_SYSSPACE); 1977c478bd9Sstevel@tonic-gate /* 1987c478bd9Sstevel@tonic-gate * Devfs can only be mounted from kernel during boot. 1997c478bd9Sstevel@tonic-gate * avp is the existing /devices, the same as the mount point. 2007c478bd9Sstevel@tonic-gate */ 2017c478bd9Sstevel@tonic-gate avp = mvp; 2027c478bd9Sstevel@tonic-gate 2037c478bd9Sstevel@tonic-gate /* 2047c478bd9Sstevel@tonic-gate * Create and initialize the vfs-private data. 2057c478bd9Sstevel@tonic-gate * This includes a hand-crafted root vnode (we build 2067c478bd9Sstevel@tonic-gate * this here mostly so that traverse() doesn't sleep 2077c478bd9Sstevel@tonic-gate * in VFS_ROOT()). 2087c478bd9Sstevel@tonic-gate */ 2097c478bd9Sstevel@tonic-gate mutex_enter(&devfs_lock); 2107c478bd9Sstevel@tonic-gate ASSERT(devfs_mntinfo == NULL); 2117c478bd9Sstevel@tonic-gate dv = dv_mkroot(vfsp, devfsdev); 2127c478bd9Sstevel@tonic-gate dv->dv_attrvp = avp; /* attribute root vp */ 2137c478bd9Sstevel@tonic-gate 2147c478bd9Sstevel@tonic-gate ASSERT(dv == dv->dv_dotdot); 2157c478bd9Sstevel@tonic-gate 2167c478bd9Sstevel@tonic-gate devfs_data = kmem_zalloc(sizeof (struct devfs_data), KM_SLEEP); 2177c478bd9Sstevel@tonic-gate devfs_data->devfs_vfsp = vfsp; 2187c478bd9Sstevel@tonic-gate devfs_data->devfs_root = dv; 2197c478bd9Sstevel@tonic-gate 2207c478bd9Sstevel@tonic-gate vfsp->vfs_data = (caddr_t)devfs_data; 2217c478bd9Sstevel@tonic-gate vfsp->vfs_fstype = devfstype; 2227c478bd9Sstevel@tonic-gate vfsp->vfs_dev = devfsdev; 2237c478bd9Sstevel@tonic-gate vfsp->vfs_bsize = DEV_BSIZE; 2247c478bd9Sstevel@tonic-gate vfsp->vfs_mtime = ddi_get_time(); 2257c478bd9Sstevel@tonic-gate vfs_make_fsid(&vfsp->vfs_fsid, vfsp->vfs_dev, devfstype); 2267c478bd9Sstevel@tonic-gate 2277c478bd9Sstevel@tonic-gate /* We're there. */ 2287c478bd9Sstevel@tonic-gate devfs_mntinfo = devfs_data; 2297c478bd9Sstevel@tonic-gate mutex_exit(&devfs_lock); 2307c478bd9Sstevel@tonic-gate 2317c478bd9Sstevel@tonic-gate va.va_mask = AT_ATIME|AT_MTIME; 2327c478bd9Sstevel@tonic-gate gethrestime(&va.va_atime); 2337c478bd9Sstevel@tonic-gate gethrestime(&va.va_mtime); 2347c478bd9Sstevel@tonic-gate (void) VOP_SETATTR(DVTOV(dv), &va, 0, cr, NULL); 2357c478bd9Sstevel@tonic-gate return (0); 2367c478bd9Sstevel@tonic-gate } 2377c478bd9Sstevel@tonic-gate 2387c478bd9Sstevel@tonic-gate 2397c478bd9Sstevel@tonic-gate /* 2407c478bd9Sstevel@tonic-gate * We never unmount devfs in a real production system. 2417c478bd9Sstevel@tonic-gate */ 2427c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 2437c478bd9Sstevel@tonic-gate static int 2447c478bd9Sstevel@tonic-gate devfs_unmount(struct vfs *vfsp, int flag, struct cred *cr) 2457c478bd9Sstevel@tonic-gate { 2467c478bd9Sstevel@tonic-gate return (EBUSY); 2477c478bd9Sstevel@tonic-gate } 2487c478bd9Sstevel@tonic-gate 2497c478bd9Sstevel@tonic-gate /* 2507c478bd9Sstevel@tonic-gate * return root vnode for given vfs 2517c478bd9Sstevel@tonic-gate */ 2527c478bd9Sstevel@tonic-gate static int 2537c478bd9Sstevel@tonic-gate devfs_root(struct vfs *vfsp, struct vnode **vpp) 2547c478bd9Sstevel@tonic-gate { 2557c478bd9Sstevel@tonic-gate dcmn_err(("devfs_root\n")); 2567c478bd9Sstevel@tonic-gate *vpp = DVTOV(VFSTODVFS(vfsp)->devfs_root); 2577c478bd9Sstevel@tonic-gate VN_HOLD(*vpp); 2587c478bd9Sstevel@tonic-gate return (0); 2597c478bd9Sstevel@tonic-gate } 2607c478bd9Sstevel@tonic-gate 2617c478bd9Sstevel@tonic-gate /* 2627c478bd9Sstevel@tonic-gate * return 'generic superblock' information to userland. 2637c478bd9Sstevel@tonic-gate * 2647c478bd9Sstevel@tonic-gate * not much that we can usefully admit to here 2657c478bd9Sstevel@tonic-gate */ 2667c478bd9Sstevel@tonic-gate static int 2677c478bd9Sstevel@tonic-gate devfs_statvfs(struct vfs *vfsp, struct statvfs64 *sbp) 2687c478bd9Sstevel@tonic-gate { 2697c478bd9Sstevel@tonic-gate extern kmem_cache_t *dv_node_cache; 2707c478bd9Sstevel@tonic-gate 2717c478bd9Sstevel@tonic-gate dev32_t d32; 2727c478bd9Sstevel@tonic-gate 2737c478bd9Sstevel@tonic-gate dcmn_err(("devfs_statvfs\n")); 2747c478bd9Sstevel@tonic-gate bzero(sbp, sizeof (*sbp)); 2757c478bd9Sstevel@tonic-gate sbp->f_frsize = sbp->f_bsize = vfsp->vfs_bsize; 2767c478bd9Sstevel@tonic-gate /* 2777c478bd9Sstevel@tonic-gate * We could compute the number of devfsnodes here .. but since 2787c478bd9Sstevel@tonic-gate * it's dynamic anyway, it's not clear how useful this is. 2797c478bd9Sstevel@tonic-gate */ 2807c478bd9Sstevel@tonic-gate sbp->f_files = kmem_cache_stat(dv_node_cache, "alloc"); 2817c478bd9Sstevel@tonic-gate 2827c478bd9Sstevel@tonic-gate /* no illusions that free/avail files is relevant to devfs */ 2837c478bd9Sstevel@tonic-gate sbp->f_ffree = 0; 2847c478bd9Sstevel@tonic-gate sbp->f_favail = 0; 2857c478bd9Sstevel@tonic-gate 2867c478bd9Sstevel@tonic-gate /* no illusions that blocks are relevant to devfs */ 2877c478bd9Sstevel@tonic-gate sbp->f_bfree = 0; 2887c478bd9Sstevel@tonic-gate sbp->f_bavail = 0; 2897c478bd9Sstevel@tonic-gate sbp->f_blocks = 0; 2907c478bd9Sstevel@tonic-gate 2917c478bd9Sstevel@tonic-gate (void) cmpldev(&d32, vfsp->vfs_dev); 2927c478bd9Sstevel@tonic-gate sbp->f_fsid = d32; 2937c478bd9Sstevel@tonic-gate (void) strcpy(sbp->f_basetype, vfssw[devfstype].vsw_name); 2947c478bd9Sstevel@tonic-gate sbp->f_flag = vf_to_stf(vfsp->vfs_flag); 2957c478bd9Sstevel@tonic-gate sbp->f_namemax = MAXNAMELEN - 1; 2967c478bd9Sstevel@tonic-gate (void) strcpy(sbp->f_fstr, "devices"); 2977c478bd9Sstevel@tonic-gate 2987c478bd9Sstevel@tonic-gate return (0); 2997c478bd9Sstevel@tonic-gate } 3007c478bd9Sstevel@tonic-gate 3017c478bd9Sstevel@tonic-gate /* 3027c478bd9Sstevel@tonic-gate * devfs always mount after root is mounted, so this should never 3037c478bd9Sstevel@tonic-gate * be invoked. 3047c478bd9Sstevel@tonic-gate */ 3057c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 3067c478bd9Sstevel@tonic-gate static int 3077c478bd9Sstevel@tonic-gate devfs_mountroot(struct vfs *vfsp, enum whymountroot why) 3087c478bd9Sstevel@tonic-gate { 3097c478bd9Sstevel@tonic-gate dcmn_err(("devfs_mountroot\n")); 3107c478bd9Sstevel@tonic-gate 3117c478bd9Sstevel@tonic-gate return (EINVAL); 3127c478bd9Sstevel@tonic-gate } 3137c478bd9Sstevel@tonic-gate 3147c478bd9Sstevel@tonic-gate struct dv_node * 3157c478bd9Sstevel@tonic-gate devfs_dip_to_dvnode(dev_info_t *dip) 3167c478bd9Sstevel@tonic-gate { 3177c478bd9Sstevel@tonic-gate char *dirpath; 3187c478bd9Sstevel@tonic-gate struct vnode *dirvp; 3197c478bd9Sstevel@tonic-gate 3207c478bd9Sstevel@tonic-gate ASSERT(dip != NULL); 3217c478bd9Sstevel@tonic-gate 3227c478bd9Sstevel@tonic-gate /* no-op if devfs not mounted yet */ 3237c478bd9Sstevel@tonic-gate if (devfs_mntinfo == NULL) 3247c478bd9Sstevel@tonic-gate return (NULL); 3257c478bd9Sstevel@tonic-gate 3267c478bd9Sstevel@tonic-gate /* 3277c478bd9Sstevel@tonic-gate * The lookupname below only looks up cached dv_nodes 3287c478bd9Sstevel@tonic-gate * because devfs_clean_key is set in thread specific data. 3297c478bd9Sstevel@tonic-gate */ 3307c478bd9Sstevel@tonic-gate dirpath = kmem_alloc(MAXPATHLEN, KM_SLEEP); 3317c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, dirpath); 3327c478bd9Sstevel@tonic-gate if (devfs_lookupname(dirpath, NULLVPP, &dirvp)) { 3337c478bd9Sstevel@tonic-gate dcmn_err(("directory %s not found\n", dirpath)); 3347c478bd9Sstevel@tonic-gate kmem_free(dirpath, MAXPATHLEN); 3357c478bd9Sstevel@tonic-gate return (NULL); 3367c478bd9Sstevel@tonic-gate } 3377c478bd9Sstevel@tonic-gate 3387c478bd9Sstevel@tonic-gate kmem_free(dirpath, MAXPATHLEN); 3397c478bd9Sstevel@tonic-gate return (VTODV(dirvp)); 3407c478bd9Sstevel@tonic-gate } 3417c478bd9Sstevel@tonic-gate 3427c478bd9Sstevel@tonic-gate /* 3435e3986cbScth * If DV_CLEAN_FORCE devfs_clean is issued with a dip that is not the root 3445e3986cbScth * and not a vHCI we also need to clean any vHCI branches because they 3455e3986cbScth * may contain pHCI nodes. A detach_node() of a pHCI will fail if its 3465e3986cbScth * mdi_devi_offline() fails, and the mdi_devi_offline() of the last 3475e3986cbScth * pHCI will fail unless an ndi_devi_offline() of the Client nodes under 3485e3986cbScth * the vHCI is successful - which requires a clean vHCI branch to removed 3495e3986cbScth * the devi_refs associated with devfs vnodes. 3505e3986cbScth */ 3515e3986cbScth static int 3525e3986cbScth devfs_clean_vhci(dev_info_t *dip, void *args) 3535e3986cbScth { 3545e3986cbScth struct dv_node *dvp; 3555e3986cbScth uint_t flags = (uint_t)(uintptr_t)args; 3565e3986cbScth 3575e3986cbScth (void) tsd_set(devfs_clean_key, (void *)1); 3585e3986cbScth dvp = devfs_dip_to_dvnode(dip); 3595e3986cbScth if (dvp) { 3605e3986cbScth (void) dv_cleandir(dvp, NULL, flags); 3615e3986cbScth VN_RELE(DVTOV(dvp)); 3625e3986cbScth } 363f94a2171Sdf125853 (void) tsd_set(devfs_clean_key, NULL); 3645e3986cbScth return (DDI_WALK_CONTINUE); 3655e3986cbScth } 3665e3986cbScth 3675e3986cbScth /* 3687c478bd9Sstevel@tonic-gate * devfs_clean() 3697c478bd9Sstevel@tonic-gate * 3707c478bd9Sstevel@tonic-gate * Destroy unreferenced dv_node's and detach devices. 37119174f18Svikram * 37219174f18Svikram * devfs_clean will try its best to clean up unused nodes. It is 37319174f18Svikram * no longer valid to assume that just because devfs_clean fails, 37419174f18Svikram * the device is not removable. This is because device contracts 37519174f18Svikram * can result in userland processes releasing a device during the 37619174f18Svikram * device offline process in the kernel. Thus it is no longer 37719174f18Svikram * correct to fail an offline just because devfs_clean finds 37819174f18Svikram * referenced dv_nodes. To enforce this, devfs_clean() always 37919174f18Svikram * returns success i.e. 0. 3807c478bd9Sstevel@tonic-gate * 381f94a2171Sdf125853 * devfs_clean() may return before removing all possible nodes if 382f94a2171Sdf125853 * we cannot acquire locks in areas of the code where potential for 383f94a2171Sdf125853 * deadlock exists (see comments in dv_find() and dv_cleandir() for 384f94a2171Sdf125853 * examples of this). 385f94a2171Sdf125853 * 3867c478bd9Sstevel@tonic-gate * devfs caches unreferenced dv_node to speed by the performance 3877c478bd9Sstevel@tonic-gate * of ls, find, etc. devfs_clean() is invoked to cleanup cached 3887c478bd9Sstevel@tonic-gate * dv_nodes to reclaim memory as well as to facilitate device 3897c478bd9Sstevel@tonic-gate * removal (dv_node reference devinfo nodes, which prevents driver 3907c478bd9Sstevel@tonic-gate * detach). 3917c478bd9Sstevel@tonic-gate * 3927c478bd9Sstevel@tonic-gate * If a shell parks in a /devices directory, the dv_node will be 3937c478bd9Sstevel@tonic-gate * held, preventing the corresponding device to be detached. 3947c478bd9Sstevel@tonic-gate * This would be a denial of service against DR. To prevent this, 3957c478bd9Sstevel@tonic-gate * DR code calls devfs_clean() with the DV_CLEAN_FORCE flag. 3967c478bd9Sstevel@tonic-gate * The dv_cleandir() implementation does the right thing to ensure 3977c478bd9Sstevel@tonic-gate * successful DR. 3987c478bd9Sstevel@tonic-gate */ 3997c478bd9Sstevel@tonic-gate int 4007c478bd9Sstevel@tonic-gate devfs_clean(dev_info_t *dip, char *devnm, uint_t flags) 4017c478bd9Sstevel@tonic-gate { 4027c478bd9Sstevel@tonic-gate struct dv_node *dvp; 4037c478bd9Sstevel@tonic-gate 4047c478bd9Sstevel@tonic-gate dcmn_err(("devfs_unconfigure: dip = 0x%p, flags = 0x%x", 4057c478bd9Sstevel@tonic-gate (void *)dip, flags)); 4067c478bd9Sstevel@tonic-gate 4077c478bd9Sstevel@tonic-gate /* avoid recursion back into the device tree */ 4087c478bd9Sstevel@tonic-gate (void) tsd_set(devfs_clean_key, (void *)1); 4097c478bd9Sstevel@tonic-gate dvp = devfs_dip_to_dvnode(dip); 410f94a2171Sdf125853 if (dvp == NULL) { 4117c478bd9Sstevel@tonic-gate (void) tsd_set(devfs_clean_key, NULL); 4127c478bd9Sstevel@tonic-gate return (0); 413f94a2171Sdf125853 } 4147c478bd9Sstevel@tonic-gate 41519174f18Svikram (void) dv_cleandir(dvp, devnm, flags); 416f94a2171Sdf125853 (void) tsd_set(devfs_clean_key, NULL); 4177c478bd9Sstevel@tonic-gate VN_RELE(DVTOV(dvp)); 4185e3986cbScth 4195e3986cbScth /* 4205e3986cbScth * If we are doing a DV_CLEAN_FORCE, and we did not start at the 4215e3986cbScth * root, and we did not start at a vHCI node then clean vHCI 4225e3986cbScth * branches too. Failure to clean vHCI branch does not cause EBUSY. 4235e3986cbScth * 4245e3986cbScth * Also, to accommodate nexus callers that clean 'self' to DR 'child' 4255e3986cbScth * (like pcihp) we clean vHCIs even when dv_cleandir() of dip branch 4265e3986cbScth * above fails - this prevents a busy DR 'child' sibling from causing 4275e3986cbScth * the DR of 'child' to fail because a vHCI branch was not cleaned. 4285e3986cbScth */ 4295e3986cbScth if ((flags & DV_CLEAN_FORCE) && (dip != ddi_root_node()) && 4305e3986cbScth (mdi_component_is_vhci(dip, NULL) != MDI_SUCCESS)) { 4315e3986cbScth /* 4325e3986cbScth * NOTE: for backport the following is recommended 4335e3986cbScth * (void) devfs_clean_vhci(scsi_vhci_dip, 4345e3986cbScth * (void *)(uintptr_t)flags); 4355e3986cbScth */ 4365e3986cbScth mdi_walk_vhcis(devfs_clean_vhci, (void *)(uintptr_t)flags); 4377c478bd9Sstevel@tonic-gate } 4385e3986cbScth 43919174f18Svikram return (0); 4407c478bd9Sstevel@tonic-gate } 4417c478bd9Sstevel@tonic-gate 4427c478bd9Sstevel@tonic-gate /* 4437c478bd9Sstevel@tonic-gate * lookup a devfs relative pathname, returning held vnodes for the final 4447c478bd9Sstevel@tonic-gate * component and the containing directory (if requested). 4457c478bd9Sstevel@tonic-gate * 4467c478bd9Sstevel@tonic-gate * NOTE: We can't use lookupname because this would use the current 4477c478bd9Sstevel@tonic-gate * processes credentials (CRED) in the call lookuppnvp instead 4487c478bd9Sstevel@tonic-gate * of kcred. It also does not give you the flexibility so 4497c478bd9Sstevel@tonic-gate * specify the directory to start the resolution in (devicesdir). 4507c478bd9Sstevel@tonic-gate */ 4517c478bd9Sstevel@tonic-gate int 4527c478bd9Sstevel@tonic-gate devfs_lookupname( 4537c478bd9Sstevel@tonic-gate char *pathname, /* user pathname */ 4547c478bd9Sstevel@tonic-gate vnode_t **dirvpp, /* ret for ptr to parent dir vnode */ 4557c478bd9Sstevel@tonic-gate vnode_t **compvpp) /* ret for ptr to component vnode */ 4567c478bd9Sstevel@tonic-gate { 4577c478bd9Sstevel@tonic-gate struct pathname pn; 4587c478bd9Sstevel@tonic-gate int error; 4597c478bd9Sstevel@tonic-gate 4607c478bd9Sstevel@tonic-gate ASSERT(devicesdir); /* devfs must be initialized */ 4617c478bd9Sstevel@tonic-gate ASSERT(pathname); /* must have some path */ 4627c478bd9Sstevel@tonic-gate 4637c478bd9Sstevel@tonic-gate if (error = pn_get(pathname, UIO_SYSSPACE, &pn)) 4647c478bd9Sstevel@tonic-gate return (error); 4657c478bd9Sstevel@tonic-gate 4667c478bd9Sstevel@tonic-gate /* make the path relative to /devices. */ 4677c478bd9Sstevel@tonic-gate pn_skipslash(&pn); 4687c478bd9Sstevel@tonic-gate if (pn_pathleft(&pn) == 0) { 4697c478bd9Sstevel@tonic-gate /* all we had was "\0" or "/" (which skipslash skiped) */ 4707c478bd9Sstevel@tonic-gate if (dirvpp) 4717c478bd9Sstevel@tonic-gate *dirvpp = NULL; 4727c478bd9Sstevel@tonic-gate if (compvpp) { 4737c478bd9Sstevel@tonic-gate VN_HOLD(devicesdir); 4747c478bd9Sstevel@tonic-gate *compvpp = devicesdir; 4757c478bd9Sstevel@tonic-gate } 4767c478bd9Sstevel@tonic-gate } else { 4777c478bd9Sstevel@tonic-gate /* 4787c478bd9Sstevel@tonic-gate * Use devfs lookup to resolve pathname to the vnode for 4797c478bd9Sstevel@tonic-gate * the device via relative lookup in devfs. Extra holds for 4807c478bd9Sstevel@tonic-gate * using devicesdir as directory we are searching and for 4817c478bd9Sstevel@tonic-gate * being our root without being == rootdir. 4827c478bd9Sstevel@tonic-gate */ 4837c478bd9Sstevel@tonic-gate VN_HOLD(devicesdir); 4847c478bd9Sstevel@tonic-gate VN_HOLD(devicesdir); 4857c478bd9Sstevel@tonic-gate error = lookuppnvp(&pn, NULL, FOLLOW, dirvpp, compvpp, 4867c478bd9Sstevel@tonic-gate devicesdir, devicesdir, kcred); 4877c478bd9Sstevel@tonic-gate } 4887c478bd9Sstevel@tonic-gate pn_free(&pn); 4897c478bd9Sstevel@tonic-gate 4907c478bd9Sstevel@tonic-gate return (error); 4917c478bd9Sstevel@tonic-gate } 4927c478bd9Sstevel@tonic-gate 4937c478bd9Sstevel@tonic-gate /* 4947c478bd9Sstevel@tonic-gate * Given a devfs path (without the /devices prefix), walk 4957c478bd9Sstevel@tonic-gate * the dv_node sub-tree rooted at the path. 4967c478bd9Sstevel@tonic-gate */ 4977c478bd9Sstevel@tonic-gate int 4987c478bd9Sstevel@tonic-gate devfs_walk( 4997c478bd9Sstevel@tonic-gate char *path, 5007c478bd9Sstevel@tonic-gate void (*callback)(struct dv_node *, void *), 5017c478bd9Sstevel@tonic-gate void *arg) 5027c478bd9Sstevel@tonic-gate { 5037c478bd9Sstevel@tonic-gate char *dirpath, *devnm; 5047c478bd9Sstevel@tonic-gate struct vnode *dirvp; 5057c478bd9Sstevel@tonic-gate 5067c478bd9Sstevel@tonic-gate ASSERT(path && callback); 5077c478bd9Sstevel@tonic-gate 5087c478bd9Sstevel@tonic-gate if (*path != '/' || devfs_mntinfo == NULL) 5097c478bd9Sstevel@tonic-gate return (ENXIO); 5107c478bd9Sstevel@tonic-gate 5117c478bd9Sstevel@tonic-gate dcmn_err(("devfs_walk: path = %s", path)); 5127c478bd9Sstevel@tonic-gate 5137c478bd9Sstevel@tonic-gate dirpath = kmem_alloc(MAXPATHLEN, KM_SLEEP); 5147c478bd9Sstevel@tonic-gate 5157c478bd9Sstevel@tonic-gate (void) snprintf(dirpath, MAXPATHLEN, "/devices%s", path); 5167c478bd9Sstevel@tonic-gate 5177c478bd9Sstevel@tonic-gate devnm = strrchr(dirpath, '/'); 5187c478bd9Sstevel@tonic-gate 5197c478bd9Sstevel@tonic-gate ASSERT(devnm); 5207c478bd9Sstevel@tonic-gate 5217c478bd9Sstevel@tonic-gate *devnm++ = '\0'; 5227c478bd9Sstevel@tonic-gate 5237c478bd9Sstevel@tonic-gate if (lookupname(dirpath, UIO_SYSSPACE, 0, NULL, &dirvp)) { 5247c478bd9Sstevel@tonic-gate dcmn_err(("directory %s not found\n", dirpath)); 5257c478bd9Sstevel@tonic-gate kmem_free(dirpath, MAXPATHLEN); 5267c478bd9Sstevel@tonic-gate return (ENXIO); 5277c478bd9Sstevel@tonic-gate } 5287c478bd9Sstevel@tonic-gate 5297c478bd9Sstevel@tonic-gate /* 5307c478bd9Sstevel@tonic-gate * if path == "/", visit the root dv_node 5317c478bd9Sstevel@tonic-gate */ 5327c478bd9Sstevel@tonic-gate if (*devnm == '\0') { 5337c478bd9Sstevel@tonic-gate callback(VTODV(dirvp), arg); 5347c478bd9Sstevel@tonic-gate devnm = NULL; 5357c478bd9Sstevel@tonic-gate } 5367c478bd9Sstevel@tonic-gate 5377c478bd9Sstevel@tonic-gate dv_walk(VTODV(dirvp), devnm, callback, arg); 5387c478bd9Sstevel@tonic-gate 5397c478bd9Sstevel@tonic-gate VN_RELE(dirvp); 5407c478bd9Sstevel@tonic-gate 5417c478bd9Sstevel@tonic-gate kmem_free(dirpath, MAXPATHLEN); 5427c478bd9Sstevel@tonic-gate 5437c478bd9Sstevel@tonic-gate return (0); 5447c478bd9Sstevel@tonic-gate } 5457c478bd9Sstevel@tonic-gate 5467c478bd9Sstevel@tonic-gate int 5477c478bd9Sstevel@tonic-gate devfs_devpolicy(vnode_t *vp, devplcy_t **dpp) 5487c478bd9Sstevel@tonic-gate { 5497c478bd9Sstevel@tonic-gate struct vnode *rvp; 5507c478bd9Sstevel@tonic-gate struct dv_node *dvp; 5517c478bd9Sstevel@tonic-gate int rval = -1; 5527c478bd9Sstevel@tonic-gate 5537c478bd9Sstevel@tonic-gate /* fail if devfs not mounted yet */ 5547c478bd9Sstevel@tonic-gate if (devfs_mntinfo == NULL) 5557c478bd9Sstevel@tonic-gate return (rval); 5567c478bd9Sstevel@tonic-gate 557da6c28aaSamw if (VOP_REALVP(vp, &rvp, NULL) == 0 && vn_matchops(rvp, dv_vnodeops)) { 5587c478bd9Sstevel@tonic-gate dvp = VTODV(rvp); 5597c478bd9Sstevel@tonic-gate rw_enter(&dvp->dv_contents, RW_READER); 5607c478bd9Sstevel@tonic-gate if (dvp->dv_priv) { 5617c478bd9Sstevel@tonic-gate dphold(dvp->dv_priv); 5627c478bd9Sstevel@tonic-gate *dpp = dvp->dv_priv; 5637c478bd9Sstevel@tonic-gate rval = 0; 5647c478bd9Sstevel@tonic-gate } 5657c478bd9Sstevel@tonic-gate rw_exit(&dvp->dv_contents); 5667c478bd9Sstevel@tonic-gate } 5677c478bd9Sstevel@tonic-gate return (rval); 5687c478bd9Sstevel@tonic-gate } 569