121864bc5SMatthew Dillon /* 221864bc5SMatthew Dillon * Copyright (c) 2009 The DragonFly Project. All rights reserved. 321864bc5SMatthew Dillon * 421864bc5SMatthew Dillon * This code is derived from software contributed to The DragonFly Project 521864bc5SMatthew Dillon * by Alex Hornung <ahornung@gmail.com> 621864bc5SMatthew Dillon * 721864bc5SMatthew Dillon * Redistribution and use in source and binary forms, with or without 821864bc5SMatthew Dillon * modification, are permitted provided that the following conditions 921864bc5SMatthew Dillon * are met: 1021864bc5SMatthew Dillon * 1121864bc5SMatthew Dillon * 1. Redistributions of source code must retain the above copyright 1221864bc5SMatthew Dillon * notice, this list of conditions and the following disclaimer. 1321864bc5SMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright 1421864bc5SMatthew Dillon * notice, this list of conditions and the following disclaimer in 1521864bc5SMatthew Dillon * the documentation and/or other materials provided with the 1621864bc5SMatthew Dillon * distribution. 1721864bc5SMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its 1821864bc5SMatthew Dillon * contributors may be used to endorse or promote products derived 1921864bc5SMatthew Dillon * from this software without specific, prior written permission. 2021864bc5SMatthew Dillon * 2121864bc5SMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2221864bc5SMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2321864bc5SMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 2421864bc5SMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 2521864bc5SMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2621864bc5SMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 2721864bc5SMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2821864bc5SMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2921864bc5SMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 3021864bc5SMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 3121864bc5SMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3221864bc5SMatthew Dillon * SUCH DAMAGE. 3321864bc5SMatthew Dillon */ 3421864bc5SMatthew Dillon #include <sys/param.h> 3521864bc5SMatthew Dillon #include <sys/systm.h> 3621864bc5SMatthew Dillon #include <sys/time.h> 3721864bc5SMatthew Dillon #include <sys/kernel.h> 3821864bc5SMatthew Dillon #include <sys/lock.h> 3921864bc5SMatthew Dillon #include <sys/fcntl.h> 4021864bc5SMatthew Dillon #include <sys/proc.h> 4121864bc5SMatthew Dillon #include <sys/priv.h> 4221864bc5SMatthew Dillon #include <sys/signalvar.h> 4321864bc5SMatthew Dillon #include <sys/vnode.h> 4421864bc5SMatthew Dillon #include <sys/uio.h> 4521864bc5SMatthew Dillon #include <sys/mount.h> 4621864bc5SMatthew Dillon #include <sys/file.h> 4721864bc5SMatthew Dillon #include <sys/fcntl.h> 4821864bc5SMatthew Dillon #include <sys/namei.h> 4921864bc5SMatthew Dillon #include <sys/dirent.h> 5021864bc5SMatthew Dillon #include <sys/malloc.h> 5121864bc5SMatthew Dillon #include <sys/stat.h> 5221864bc5SMatthew Dillon #include <sys/reg.h> 5321864bc5SMatthew Dillon #include <vm/vm_pager.h> 5421864bc5SMatthew Dillon #include <vm/vm_zone.h> 5521864bc5SMatthew Dillon #include <vm/vm_object.h> 5621864bc5SMatthew Dillon #include <sys/filio.h> 5721864bc5SMatthew Dillon #include <sys/ttycom.h> 5821864bc5SMatthew Dillon #include <sys/tty.h> 592d076755SAlex Hornung #include <sys/diskslice.h> 603a1032a6SAlex Hornung #include <sys/sysctl.h> 612c1e28ddSAlex Hornung #include <sys/devfs.h> 6221864bc5SMatthew Dillon #include <sys/pioctl.h> 6321864bc5SMatthew Dillon 6421864bc5SMatthew Dillon #include <machine/limits.h> 65684a93c4SMatthew Dillon 661a54183bSMatthew Dillon #include <sys/buf2.h> 671a54183bSMatthew Dillon #include <sys/sysref2.h> 68684a93c4SMatthew Dillon #include <sys/mplock2.h> 69684a93c4SMatthew Dillon #include <vm/vm_page2.h> 7021864bc5SMatthew Dillon 7121864bc5SMatthew Dillon MALLOC_DECLARE(M_DEVFS); 7221864bc5SMatthew Dillon #define DEVFS_BADOP (void *)devfs_badop 7321864bc5SMatthew Dillon 7421864bc5SMatthew Dillon static int devfs_badop(struct vop_generic_args *); 7521864bc5SMatthew Dillon static int devfs_access(struct vop_access_args *); 7621864bc5SMatthew Dillon static int devfs_inactive(struct vop_inactive_args *); 7721864bc5SMatthew Dillon static int devfs_reclaim(struct vop_reclaim_args *); 7821864bc5SMatthew Dillon static int devfs_readdir(struct vop_readdir_args *); 7921864bc5SMatthew Dillon static int devfs_getattr(struct vop_getattr_args *); 8021864bc5SMatthew Dillon static int devfs_setattr(struct vop_setattr_args *); 8121864bc5SMatthew Dillon static int devfs_readlink(struct vop_readlink_args *); 8221864bc5SMatthew Dillon static int devfs_print(struct vop_print_args *); 8321864bc5SMatthew Dillon 8421864bc5SMatthew Dillon static int devfs_nresolve(struct vop_nresolve_args *); 8521864bc5SMatthew Dillon static int devfs_nlookupdotdot(struct vop_nlookupdotdot_args *); 8621864bc5SMatthew Dillon static int devfs_nsymlink(struct vop_nsymlink_args *); 8721864bc5SMatthew Dillon static int devfs_nremove(struct vop_nremove_args *); 8821864bc5SMatthew Dillon 8921864bc5SMatthew Dillon static int devfs_spec_open(struct vop_open_args *); 9021864bc5SMatthew Dillon static int devfs_spec_close(struct vop_close_args *); 9121864bc5SMatthew Dillon static int devfs_spec_fsync(struct vop_fsync_args *); 9221864bc5SMatthew Dillon 9321864bc5SMatthew Dillon static int devfs_spec_read(struct vop_read_args *); 9421864bc5SMatthew Dillon static int devfs_spec_write(struct vop_write_args *); 9521864bc5SMatthew Dillon static int devfs_spec_ioctl(struct vop_ioctl_args *); 9621864bc5SMatthew Dillon static int devfs_spec_poll(struct vop_poll_args *); 9721864bc5SMatthew Dillon static int devfs_spec_kqfilter(struct vop_kqfilter_args *); 9821864bc5SMatthew Dillon static int devfs_spec_strategy(struct vop_strategy_args *); 9921864bc5SMatthew Dillon static void devfs_spec_strategy_done(struct bio *); 10021864bc5SMatthew Dillon static int devfs_spec_freeblks(struct vop_freeblks_args *); 10121864bc5SMatthew Dillon static int devfs_spec_bmap(struct vop_bmap_args *); 10221864bc5SMatthew Dillon static int devfs_spec_advlock(struct vop_advlock_args *); 10321864bc5SMatthew Dillon static void devfs_spec_getpages_iodone(struct bio *); 10421864bc5SMatthew Dillon static int devfs_spec_getpages(struct vop_getpages_args *); 10521864bc5SMatthew Dillon 10621864bc5SMatthew Dillon 10721864bc5SMatthew Dillon static int devfs_specf_close(struct file *); 10821864bc5SMatthew Dillon static int devfs_specf_read(struct file *, struct uio *, struct ucred *, int); 10921864bc5SMatthew Dillon static int devfs_specf_write(struct file *, struct uio *, struct ucred *, int); 11021864bc5SMatthew Dillon static int devfs_specf_stat(struct file *, struct stat *, struct ucred *); 11121864bc5SMatthew Dillon static int devfs_specf_kqfilter(struct file *, struct knote *); 11221864bc5SMatthew Dillon static int devfs_specf_poll(struct file *, int, struct ucred *); 11387baaf0cSMatthew Dillon static int devfs_specf_ioctl(struct file *, u_long, caddr_t, 11487baaf0cSMatthew Dillon struct ucred *, struct sysmsg *); 11521864bc5SMatthew Dillon static __inline int sequential_heuristic(struct uio *, struct file *); 11687baaf0cSMatthew Dillon 11721864bc5SMatthew Dillon extern struct lock devfs_lock; 11821864bc5SMatthew Dillon 1193a1032a6SAlex Hornung static int mpsafe_reads, mpsafe_writes, mplock_reads, mplock_writes; 1203a1032a6SAlex Hornung 12121864bc5SMatthew Dillon /* 12221864bc5SMatthew Dillon * devfs vnode operations for regular files 12321864bc5SMatthew Dillon */ 12421864bc5SMatthew Dillon struct vop_ops devfs_vnode_norm_vops = { 12521864bc5SMatthew Dillon .vop_default = vop_defaultop, 12621864bc5SMatthew Dillon .vop_access = devfs_access, 12721864bc5SMatthew Dillon .vop_advlock = DEVFS_BADOP, 12821864bc5SMatthew Dillon .vop_bmap = DEVFS_BADOP, 12921864bc5SMatthew Dillon .vop_close = vop_stdclose, 13021864bc5SMatthew Dillon .vop_getattr = devfs_getattr, 13121864bc5SMatthew Dillon .vop_inactive = devfs_inactive, 13221864bc5SMatthew Dillon .vop_ncreate = DEVFS_BADOP, 13321864bc5SMatthew Dillon .vop_nresolve = devfs_nresolve, 13421864bc5SMatthew Dillon .vop_nlookupdotdot = devfs_nlookupdotdot, 13521864bc5SMatthew Dillon .vop_nlink = DEVFS_BADOP, 13621864bc5SMatthew Dillon .vop_nmkdir = DEVFS_BADOP, 13721864bc5SMatthew Dillon .vop_nmknod = DEVFS_BADOP, 13821864bc5SMatthew Dillon .vop_nremove = devfs_nremove, 13921864bc5SMatthew Dillon .vop_nrename = DEVFS_BADOP, 14021864bc5SMatthew Dillon .vop_nrmdir = DEVFS_BADOP, 14121864bc5SMatthew Dillon .vop_nsymlink = devfs_nsymlink, 14221864bc5SMatthew Dillon .vop_open = vop_stdopen, 14321864bc5SMatthew Dillon .vop_pathconf = vop_stdpathconf, 14421864bc5SMatthew Dillon .vop_print = devfs_print, 14521864bc5SMatthew Dillon .vop_read = DEVFS_BADOP, 14621864bc5SMatthew Dillon .vop_readdir = devfs_readdir, 14721864bc5SMatthew Dillon .vop_readlink = devfs_readlink, 14821864bc5SMatthew Dillon .vop_reclaim = devfs_reclaim, 14921864bc5SMatthew Dillon .vop_setattr = devfs_setattr, 15021864bc5SMatthew Dillon .vop_write = DEVFS_BADOP, 15121864bc5SMatthew Dillon .vop_ioctl = DEVFS_BADOP 15221864bc5SMatthew Dillon }; 15321864bc5SMatthew Dillon 15421864bc5SMatthew Dillon /* 15521864bc5SMatthew Dillon * devfs vnode operations for character devices 15621864bc5SMatthew Dillon */ 15721864bc5SMatthew Dillon struct vop_ops devfs_vnode_dev_vops = { 15821864bc5SMatthew Dillon .vop_default = vop_defaultop, 15921864bc5SMatthew Dillon .vop_access = devfs_access, 16021864bc5SMatthew Dillon .vop_advlock = devfs_spec_advlock, 16121864bc5SMatthew Dillon .vop_bmap = devfs_spec_bmap, 16221864bc5SMatthew Dillon .vop_close = devfs_spec_close, 16321864bc5SMatthew Dillon .vop_freeblks = devfs_spec_freeblks, 16421864bc5SMatthew Dillon .vop_fsync = devfs_spec_fsync, 16521864bc5SMatthew Dillon .vop_getattr = devfs_getattr, 16621864bc5SMatthew Dillon .vop_getpages = devfs_spec_getpages, 16721864bc5SMatthew Dillon .vop_inactive = devfs_inactive, 16821864bc5SMatthew Dillon .vop_open = devfs_spec_open, 16921864bc5SMatthew Dillon .vop_pathconf = vop_stdpathconf, 17021864bc5SMatthew Dillon .vop_print = devfs_print, 17121864bc5SMatthew Dillon .vop_poll = devfs_spec_poll, 17221864bc5SMatthew Dillon .vop_kqfilter = devfs_spec_kqfilter, 17321864bc5SMatthew Dillon .vop_read = devfs_spec_read, 17421864bc5SMatthew Dillon .vop_readdir = DEVFS_BADOP, 17521864bc5SMatthew Dillon .vop_readlink = DEVFS_BADOP, 17621864bc5SMatthew Dillon .vop_reclaim = devfs_reclaim, 17721864bc5SMatthew Dillon .vop_setattr = devfs_setattr, 17821864bc5SMatthew Dillon .vop_strategy = devfs_spec_strategy, 17921864bc5SMatthew Dillon .vop_write = devfs_spec_write, 18021864bc5SMatthew Dillon .vop_ioctl = devfs_spec_ioctl 18121864bc5SMatthew Dillon }; 18221864bc5SMatthew Dillon 18321864bc5SMatthew Dillon struct vop_ops *devfs_vnode_dev_vops_p = &devfs_vnode_dev_vops; 18421864bc5SMatthew Dillon 18521864bc5SMatthew Dillon struct fileops devfs_dev_fileops = { 18621864bc5SMatthew Dillon .fo_read = devfs_specf_read, 18721864bc5SMatthew Dillon .fo_write = devfs_specf_write, 18821864bc5SMatthew Dillon .fo_ioctl = devfs_specf_ioctl, 18921864bc5SMatthew Dillon .fo_poll = devfs_specf_poll, 19021864bc5SMatthew Dillon .fo_kqfilter = devfs_specf_kqfilter, 19121864bc5SMatthew Dillon .fo_stat = devfs_specf_stat, 19221864bc5SMatthew Dillon .fo_close = devfs_specf_close, 19321864bc5SMatthew Dillon .fo_shutdown = nofo_shutdown 19421864bc5SMatthew Dillon }; 19521864bc5SMatthew Dillon 1964062d050SMatthew Dillon /* 1974062d050SMatthew Dillon * These two functions are possibly temporary hacks for 1984062d050SMatthew Dillon * devices (aka the pty code) which want to control the 1994062d050SMatthew Dillon * node attributes themselves. 2004062d050SMatthew Dillon * 2014062d050SMatthew Dillon * XXX we may ultimately desire to simply remove the uid/gid/mode 2024062d050SMatthew Dillon * from the node entirely. 2034062d050SMatthew Dillon */ 2044062d050SMatthew Dillon static __inline void 2054062d050SMatthew Dillon node_sync_dev_get(struct devfs_node *node) 2064062d050SMatthew Dillon { 2074062d050SMatthew Dillon cdev_t dev; 2084062d050SMatthew Dillon 2094062d050SMatthew Dillon if ((dev = node->d_dev) && (dev->si_flags & SI_OVERRIDE)) { 2104062d050SMatthew Dillon node->uid = dev->si_uid; 2114062d050SMatthew Dillon node->gid = dev->si_gid; 2124062d050SMatthew Dillon node->mode = dev->si_perms; 2134062d050SMatthew Dillon } 2144062d050SMatthew Dillon } 2154062d050SMatthew Dillon 2164062d050SMatthew Dillon static __inline void 2174062d050SMatthew Dillon node_sync_dev_set(struct devfs_node *node) 2184062d050SMatthew Dillon { 2194062d050SMatthew Dillon cdev_t dev; 2204062d050SMatthew Dillon 2214062d050SMatthew Dillon if ((dev = node->d_dev) && (dev->si_flags & SI_OVERRIDE)) { 2224062d050SMatthew Dillon dev->si_uid = node->uid; 2234062d050SMatthew Dillon dev->si_gid = node->gid; 2244062d050SMatthew Dillon dev->si_perms = node->mode; 2254062d050SMatthew Dillon } 2264062d050SMatthew Dillon } 22721864bc5SMatthew Dillon 22821864bc5SMatthew Dillon /* 22921864bc5SMatthew Dillon * generic entry point for unsupported operations 23021864bc5SMatthew Dillon */ 23121864bc5SMatthew Dillon static int 23221864bc5SMatthew Dillon devfs_badop(struct vop_generic_args *ap) 23321864bc5SMatthew Dillon { 23421864bc5SMatthew Dillon return (EIO); 23521864bc5SMatthew Dillon } 23621864bc5SMatthew Dillon 23721864bc5SMatthew Dillon 23821864bc5SMatthew Dillon static int 23921864bc5SMatthew Dillon devfs_access(struct vop_access_args *ap) 24021864bc5SMatthew Dillon { 24121864bc5SMatthew Dillon struct devfs_node *node = DEVFS_NODE(ap->a_vp); 242898c91eeSMatthew Dillon int error; 24321864bc5SMatthew Dillon 244894bbb25SAlex Hornung if (!devfs_node_is_accessible(node)) 245894bbb25SAlex Hornung return ENOENT; 2464062d050SMatthew Dillon node_sync_dev_get(node); 24721864bc5SMatthew Dillon error = vop_helper_access(ap, node->uid, node->gid, 24821864bc5SMatthew Dillon node->mode, node->flags); 24921864bc5SMatthew Dillon 25021864bc5SMatthew Dillon return error; 25121864bc5SMatthew Dillon } 25221864bc5SMatthew Dillon 25321864bc5SMatthew Dillon 25421864bc5SMatthew Dillon static int 25521864bc5SMatthew Dillon devfs_inactive(struct vop_inactive_args *ap) 25621864bc5SMatthew Dillon { 257ca8d7677SMatthew Dillon struct devfs_node *node = DEVFS_NODE(ap->a_vp); 25821864bc5SMatthew Dillon 259ca8d7677SMatthew Dillon if (node == NULL || (node->flags & DEVFS_NODE_LINKED) == 0) 26021864bc5SMatthew Dillon vrecycle(ap->a_vp); 26121864bc5SMatthew Dillon return 0; 26221864bc5SMatthew Dillon } 26321864bc5SMatthew Dillon 26421864bc5SMatthew Dillon 26521864bc5SMatthew Dillon static int 26621864bc5SMatthew Dillon devfs_reclaim(struct vop_reclaim_args *ap) 26721864bc5SMatthew Dillon { 268be6f2e86SMatthew Dillon struct devfs_node *node; 269be6f2e86SMatthew Dillon struct vnode *vp; 270be6f2e86SMatthew Dillon int locked; 271be6f2e86SMatthew Dillon 272be6f2e86SMatthew Dillon /* 273be6f2e86SMatthew Dillon * Check if it is locked already. if not, we acquire the devfs lock 274be6f2e86SMatthew Dillon */ 27521864bc5SMatthew Dillon if (!(lockstatus(&devfs_lock, curthread)) == LK_EXCLUSIVE) { 27621864bc5SMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE); 27721864bc5SMatthew Dillon locked = 1; 278be6f2e86SMatthew Dillon } else { 279be6f2e86SMatthew Dillon locked = 0; 28021864bc5SMatthew Dillon } 28121864bc5SMatthew Dillon 282be6f2e86SMatthew Dillon /* 283be6f2e86SMatthew Dillon * Get rid of the devfs_node if it is no longer linked into the 284be6f2e86SMatthew Dillon * topology. 285be6f2e86SMatthew Dillon */ 286be6f2e86SMatthew Dillon vp = ap->a_vp; 287be6f2e86SMatthew Dillon if ((node = DEVFS_NODE(vp)) != NULL) { 288be6f2e86SMatthew Dillon node->v_node = NULL; 2894062d050SMatthew Dillon if ((node->flags & DEVFS_NODE_LINKED) == 0) 2904062d050SMatthew Dillon devfs_freep(node); 29121864bc5SMatthew Dillon } 29221864bc5SMatthew Dillon 29321864bc5SMatthew Dillon if (locked) 29421864bc5SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE); 29521864bc5SMatthew Dillon 296be6f2e86SMatthew Dillon /* 2979b823501SAlex Hornung * v_rdev needs to be properly released using v_release_rdev 2989b823501SAlex Hornung * Make sure v_data is NULL as well. 299be6f2e86SMatthew Dillon */ 300be6f2e86SMatthew Dillon vp->v_data = NULL; 3019b823501SAlex Hornung v_release_rdev(vp); 30221864bc5SMatthew Dillon return 0; 30321864bc5SMatthew Dillon } 30421864bc5SMatthew Dillon 30521864bc5SMatthew Dillon 30621864bc5SMatthew Dillon static int 30721864bc5SMatthew Dillon devfs_readdir(struct vop_readdir_args *ap) 30821864bc5SMatthew Dillon { 309898c91eeSMatthew Dillon struct devfs_node *dnode = DEVFS_NODE(ap->a_vp); 31021864bc5SMatthew Dillon struct devfs_node *node; 31121864bc5SMatthew Dillon int cookie_index; 31221864bc5SMatthew Dillon int ncookies; 313898c91eeSMatthew Dillon int error2; 314898c91eeSMatthew Dillon int error; 315898c91eeSMatthew Dillon int r; 31621864bc5SMatthew Dillon off_t *cookies; 31721864bc5SMatthew Dillon off_t saveoff; 31821864bc5SMatthew Dillon 31921864bc5SMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, "devfs_readdir() called!\n"); 32021864bc5SMatthew Dillon 32121864bc5SMatthew Dillon if (ap->a_uio->uio_offset < 0 || ap->a_uio->uio_offset > INT_MAX) 32221864bc5SMatthew Dillon return (EINVAL); 32321864bc5SMatthew Dillon if ((error = vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY)) != 0) 32421864bc5SMatthew Dillon return (error); 32521864bc5SMatthew Dillon 326c512ab96SMatthew Dillon if (!devfs_node_is_accessible(dnode)) { 327c512ab96SMatthew Dillon vn_unlock(ap->a_vp); 328ca8d7677SMatthew Dillon return ENOENT; 329c512ab96SMatthew Dillon } 330ca8d7677SMatthew Dillon 331ca8d7677SMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE); 332ca8d7677SMatthew Dillon 33321864bc5SMatthew Dillon saveoff = ap->a_uio->uio_offset; 33421864bc5SMatthew Dillon 33521864bc5SMatthew Dillon if (ap->a_ncookies) { 33621864bc5SMatthew Dillon ncookies = ap->a_uio->uio_resid / 16 + 1; /* Why / 16 ?? */ 33721864bc5SMatthew Dillon if (ncookies > 256) 33821864bc5SMatthew Dillon ncookies = 256; 33921864bc5SMatthew Dillon cookies = kmalloc(256 * sizeof(off_t), M_TEMP, M_WAITOK); 34021864bc5SMatthew Dillon cookie_index = 0; 34121864bc5SMatthew Dillon } else { 34221864bc5SMatthew Dillon ncookies = -1; 34321864bc5SMatthew Dillon cookies = NULL; 34421864bc5SMatthew Dillon cookie_index = 0; 34521864bc5SMatthew Dillon } 34621864bc5SMatthew Dillon 347898c91eeSMatthew Dillon nanotime(&dnode->atime); 34821864bc5SMatthew Dillon 34921864bc5SMatthew Dillon if (saveoff == 0) { 350898c91eeSMatthew Dillon r = vop_write_dirent(&error, ap->a_uio, dnode->d_dir.d_ino, 351898c91eeSMatthew Dillon DT_DIR, 1, "."); 35221864bc5SMatthew Dillon if (r) 35321864bc5SMatthew Dillon goto done; 35421864bc5SMatthew Dillon if (cookies) 35521864bc5SMatthew Dillon cookies[cookie_index] = saveoff; 35621864bc5SMatthew Dillon saveoff++; 35721864bc5SMatthew Dillon cookie_index++; 35821864bc5SMatthew Dillon if (cookie_index == ncookies) 35921864bc5SMatthew Dillon goto done; 36021864bc5SMatthew Dillon } 36121864bc5SMatthew Dillon 36221864bc5SMatthew Dillon if (saveoff == 1) { 363898c91eeSMatthew Dillon if (dnode->parent) { 36421864bc5SMatthew Dillon r = vop_write_dirent(&error, ap->a_uio, 365898c91eeSMatthew Dillon dnode->parent->d_dir.d_ino, 36621864bc5SMatthew Dillon DT_DIR, 2, ".."); 36721864bc5SMatthew Dillon } else { 36821864bc5SMatthew Dillon r = vop_write_dirent(&error, ap->a_uio, 369898c91eeSMatthew Dillon dnode->d_dir.d_ino, 370898c91eeSMatthew Dillon DT_DIR, 2, ".."); 37121864bc5SMatthew Dillon } 37221864bc5SMatthew Dillon if (r) 37321864bc5SMatthew Dillon goto done; 37421864bc5SMatthew Dillon if (cookies) 37521864bc5SMatthew Dillon cookies[cookie_index] = saveoff; 37621864bc5SMatthew Dillon saveoff++; 37721864bc5SMatthew Dillon cookie_index++; 37821864bc5SMatthew Dillon if (cookie_index == ncookies) 37921864bc5SMatthew Dillon goto done; 38021864bc5SMatthew Dillon } 38121864bc5SMatthew Dillon 382898c91eeSMatthew Dillon TAILQ_FOREACH(node, DEVFS_DENODE_HEAD(dnode), link) { 383898c91eeSMatthew Dillon if ((node->flags & DEVFS_HIDDEN) || 384898c91eeSMatthew Dillon (node->flags & DEVFS_INVISIBLE)) { 38521864bc5SMatthew Dillon continue; 386898c91eeSMatthew Dillon } 38721864bc5SMatthew Dillon 388f7e8960cSAlex Hornung /* 389f7e8960cSAlex Hornung * If the node type is a valid devfs alias, then we make sure that the 390f7e8960cSAlex Hornung * target isn't hidden. If it is, we don't show the link in the 391f7e8960cSAlex Hornung * directory listing. 392f7e8960cSAlex Hornung */ 393f7e8960cSAlex Hornung if ((node->node_type == Plink) && (node->link_target != NULL) && 394f7e8960cSAlex Hornung (node->link_target->flags & DEVFS_HIDDEN)) 395f7e8960cSAlex Hornung continue; 396f7e8960cSAlex Hornung 39721864bc5SMatthew Dillon if (node->cookie < saveoff) 39821864bc5SMatthew Dillon continue; 399f7e8960cSAlex Hornung 40021864bc5SMatthew Dillon saveoff = node->cookie; 40121864bc5SMatthew Dillon 402898c91eeSMatthew Dillon error2 = vop_write_dirent(&error, ap->a_uio, node->d_dir.d_ino, 403898c91eeSMatthew Dillon node->d_dir.d_type, 404898c91eeSMatthew Dillon node->d_dir.d_namlen, 405898c91eeSMatthew Dillon node->d_dir.d_name); 40621864bc5SMatthew Dillon 40721864bc5SMatthew Dillon if (error2) 40821864bc5SMatthew Dillon break; 40921864bc5SMatthew Dillon 41021864bc5SMatthew Dillon saveoff++; 41121864bc5SMatthew Dillon 41221864bc5SMatthew Dillon if (cookies) 41321864bc5SMatthew Dillon cookies[cookie_index] = node->cookie; 41421864bc5SMatthew Dillon ++cookie_index; 41521864bc5SMatthew Dillon if (cookie_index == ncookies) 41621864bc5SMatthew Dillon break; 41721864bc5SMatthew Dillon } 41821864bc5SMatthew Dillon 41921864bc5SMatthew Dillon done: 420ca8d7677SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE); 42121864bc5SMatthew Dillon vn_unlock(ap->a_vp); 42221864bc5SMatthew Dillon 42321864bc5SMatthew Dillon ap->a_uio->uio_offset = saveoff; 42421864bc5SMatthew Dillon if (error && cookie_index == 0) { 42521864bc5SMatthew Dillon if (cookies) { 42621864bc5SMatthew Dillon kfree(cookies, M_TEMP); 42721864bc5SMatthew Dillon *ap->a_ncookies = 0; 42821864bc5SMatthew Dillon *ap->a_cookies = NULL; 42921864bc5SMatthew Dillon } 43021864bc5SMatthew Dillon } else { 43121864bc5SMatthew Dillon if (cookies) { 43221864bc5SMatthew Dillon *ap->a_ncookies = cookie_index; 43321864bc5SMatthew Dillon *ap->a_cookies = cookies; 43421864bc5SMatthew Dillon } 43521864bc5SMatthew Dillon } 43621864bc5SMatthew Dillon return (error); 43721864bc5SMatthew Dillon } 43821864bc5SMatthew Dillon 43921864bc5SMatthew Dillon 44021864bc5SMatthew Dillon static int 44121864bc5SMatthew Dillon devfs_nresolve(struct vop_nresolve_args *ap) 44221864bc5SMatthew Dillon { 443898c91eeSMatthew Dillon struct devfs_node *dnode = DEVFS_NODE(ap->a_dvp); 44421864bc5SMatthew Dillon struct devfs_node *node, *found = NULL; 44521864bc5SMatthew Dillon struct namecache *ncp; 44621864bc5SMatthew Dillon struct vnode *vp = NULL; 44721864bc5SMatthew Dillon int error = 0; 44821864bc5SMatthew Dillon int len; 449260e4e8bSAlex Hornung int depth; 45021864bc5SMatthew Dillon 45121864bc5SMatthew Dillon ncp = ap->a_nch->ncp; 45221864bc5SMatthew Dillon len = ncp->nc_nlen; 45321864bc5SMatthew Dillon 454898c91eeSMatthew Dillon if (!devfs_node_is_accessible(dnode)) 455ca8d7677SMatthew Dillon return ENOENT; 456ca8d7677SMatthew Dillon 45721864bc5SMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE); 45821864bc5SMatthew Dillon 459898c91eeSMatthew Dillon if ((dnode->node_type != Proot) && (dnode->node_type != Pdir)) { 460e23485a5SMatthew Dillon error = ENOENT; 46121864bc5SMatthew Dillon cache_setvp(ap->a_nch, NULL); 46221864bc5SMatthew Dillon goto out; 46321864bc5SMatthew Dillon } 46421864bc5SMatthew Dillon 465898c91eeSMatthew Dillon TAILQ_FOREACH(node, DEVFS_DENODE_HEAD(dnode), link) { 46621864bc5SMatthew Dillon if (len == node->d_dir.d_namlen) { 46721864bc5SMatthew Dillon if (!memcmp(ncp->nc_name, node->d_dir.d_name, len)) { 46821864bc5SMatthew Dillon found = node; 46921864bc5SMatthew Dillon break; 47021864bc5SMatthew Dillon } 47121864bc5SMatthew Dillon } 47221864bc5SMatthew Dillon } 47321864bc5SMatthew Dillon 47421864bc5SMatthew Dillon if (found) { 475260e4e8bSAlex Hornung depth = 0; 476260e4e8bSAlex Hornung while ((found->node_type == Plink) && (found->link_target)) { 477260e4e8bSAlex Hornung if (depth >= 8) { 478260e4e8bSAlex Hornung devfs_debug(DEVFS_DEBUG_SHOW, "Recursive link or depth >= 8"); 479260e4e8bSAlex Hornung break; 480260e4e8bSAlex Hornung } 481260e4e8bSAlex Hornung 48221864bc5SMatthew Dillon found = found->link_target; 483260e4e8bSAlex Hornung ++depth; 484260e4e8bSAlex Hornung } 48521864bc5SMatthew Dillon 48621864bc5SMatthew Dillon if (!(found->flags & DEVFS_HIDDEN)) 48721864bc5SMatthew Dillon devfs_allocv(/*ap->a_dvp->v_mount, */ &vp, found); 48821864bc5SMatthew Dillon } 48921864bc5SMatthew Dillon 49021864bc5SMatthew Dillon if (vp == NULL) { 49121864bc5SMatthew Dillon error = ENOENT; 49221864bc5SMatthew Dillon cache_setvp(ap->a_nch, NULL); 49321864bc5SMatthew Dillon goto out; 49421864bc5SMatthew Dillon 49521864bc5SMatthew Dillon } 49621864bc5SMatthew Dillon KKASSERT(vp); 49721864bc5SMatthew Dillon vn_unlock(vp); 49821864bc5SMatthew Dillon cache_setvp(ap->a_nch, vp); 49921864bc5SMatthew Dillon vrele(vp); 50021864bc5SMatthew Dillon out: 50121864bc5SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE); 502898c91eeSMatthew Dillon 50321864bc5SMatthew Dillon return error; 50421864bc5SMatthew Dillon } 50521864bc5SMatthew Dillon 50621864bc5SMatthew Dillon 50721864bc5SMatthew Dillon static int 50821864bc5SMatthew Dillon devfs_nlookupdotdot(struct vop_nlookupdotdot_args *ap) 50921864bc5SMatthew Dillon { 510898c91eeSMatthew Dillon struct devfs_node *dnode = DEVFS_NODE(ap->a_dvp); 51121864bc5SMatthew Dillon 512898c91eeSMatthew Dillon *ap->a_vpp = NULL; 513898c91eeSMatthew Dillon if (!devfs_node_is_accessible(dnode)) 514894bbb25SAlex Hornung return ENOENT; 515894bbb25SAlex Hornung 51621864bc5SMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE); 517898c91eeSMatthew Dillon if (dnode->parent != NULL) { 518898c91eeSMatthew Dillon devfs_allocv(ap->a_vpp, dnode->parent); 51921864bc5SMatthew Dillon vn_unlock(*ap->a_vpp); 52021864bc5SMatthew Dillon } 52121864bc5SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE); 52221864bc5SMatthew Dillon 52321864bc5SMatthew Dillon return ((*ap->a_vpp == NULL) ? ENOENT : 0); 52421864bc5SMatthew Dillon } 52521864bc5SMatthew Dillon 52621864bc5SMatthew Dillon 52721864bc5SMatthew Dillon static int 52821864bc5SMatthew Dillon devfs_getattr(struct vop_getattr_args *ap) 52921864bc5SMatthew Dillon { 53021864bc5SMatthew Dillon struct devfs_node *node = DEVFS_NODE(ap->a_vp); 531898c91eeSMatthew Dillon struct vattr *vap = ap->a_vap; 5322d076755SAlex Hornung struct partinfo pinfo; 53321864bc5SMatthew Dillon int error = 0; 53421864bc5SMatthew Dillon 535952f0188SAlex Hornung #if 0 536894bbb25SAlex Hornung if (!devfs_node_is_accessible(node)) 537ca8d7677SMatthew Dillon return ENOENT; 538952f0188SAlex Hornung #endif 5394062d050SMatthew Dillon node_sync_dev_get(node); 540ca8d7677SMatthew Dillon 541ca8d7677SMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE); 54221864bc5SMatthew Dillon 54321864bc5SMatthew Dillon /* start by zeroing out the attributes */ 54421864bc5SMatthew Dillon VATTR_NULL(vap); 54521864bc5SMatthew Dillon 54621864bc5SMatthew Dillon /* next do all the common fields */ 54721864bc5SMatthew Dillon vap->va_type = ap->a_vp->v_type; 54821864bc5SMatthew Dillon vap->va_mode = node->mode; 54921864bc5SMatthew Dillon vap->va_fileid = DEVFS_NODE(ap->a_vp)->d_dir.d_ino ; 550894bbb25SAlex Hornung vap->va_flags = 0; /* XXX: what should this be? */ 55121864bc5SMatthew Dillon vap->va_blocksize = DEV_BSIZE; 55221864bc5SMatthew Dillon vap->va_bytes = vap->va_size = sizeof(struct devfs_node); 55321864bc5SMatthew Dillon 55421864bc5SMatthew Dillon vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; 55521864bc5SMatthew Dillon 55621864bc5SMatthew Dillon vap->va_atime = node->atime; 55721864bc5SMatthew Dillon vap->va_mtime = node->mtime; 55821864bc5SMatthew Dillon vap->va_ctime = node->ctime; 55921864bc5SMatthew Dillon 56021864bc5SMatthew Dillon vap->va_nlink = 1; /* number of references to file */ 56121864bc5SMatthew Dillon 56221864bc5SMatthew Dillon vap->va_uid = node->uid; 56321864bc5SMatthew Dillon vap->va_gid = node->gid; 56421864bc5SMatthew Dillon 56521864bc5SMatthew Dillon vap->va_rmajor = 0; 56621864bc5SMatthew Dillon vap->va_rminor = 0; 56721864bc5SMatthew Dillon 568898c91eeSMatthew Dillon if ((node->node_type == Pdev) && node->d_dev) { 569898c91eeSMatthew Dillon reference_dev(node->d_dev); 570898c91eeSMatthew Dillon vap->va_rminor = node->d_dev->si_uminor; 571898c91eeSMatthew Dillon release_dev(node->d_dev); 57221864bc5SMatthew Dillon } 57321864bc5SMatthew Dillon 57421864bc5SMatthew Dillon /* For a softlink the va_size is the length of the softlink */ 575898c91eeSMatthew Dillon if (node->symlink_name != 0) { 5762d076755SAlex Hornung vap->va_bytes = vap->va_size = node->symlink_namelen; 57721864bc5SMatthew Dillon } 5782d076755SAlex Hornung 5792d076755SAlex Hornung /* 5802d076755SAlex Hornung * For a disk-type device, va_size is the size of the underlying 5812d076755SAlex Hornung * device, so that lseek() works properly. 5822d076755SAlex Hornung */ 5832d076755SAlex Hornung if ((node->d_dev) && (dev_dflags(node->d_dev) & D_DISK)) { 5842d076755SAlex Hornung bzero(&pinfo, sizeof(pinfo)); 5852d076755SAlex Hornung error = dev_dioctl(node->d_dev, DIOCGPART, (void *)&pinfo, 5862d076755SAlex Hornung 0, proc0.p_ucred, NULL); 5872d076755SAlex Hornung if ((error == 0) && (pinfo.media_blksize != 0)) { 5882d076755SAlex Hornung vap->va_size = pinfo.media_size; 5892d076755SAlex Hornung } else { 5902d076755SAlex Hornung vap->va_size = 0; 5912d076755SAlex Hornung error = 0; 5922d076755SAlex Hornung } 5932d076755SAlex Hornung } 5942d076755SAlex Hornung 595ca8d7677SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE); 596898c91eeSMatthew Dillon 597894bbb25SAlex Hornung return (error); 59821864bc5SMatthew Dillon } 59921864bc5SMatthew Dillon 60021864bc5SMatthew Dillon 60121864bc5SMatthew Dillon static int 60221864bc5SMatthew Dillon devfs_setattr(struct vop_setattr_args *ap) 60321864bc5SMatthew Dillon { 604898c91eeSMatthew Dillon struct devfs_node *node = DEVFS_NODE(ap->a_vp); 60521864bc5SMatthew Dillon struct vattr *vap; 60621864bc5SMatthew Dillon int error = 0; 60721864bc5SMatthew Dillon 608894bbb25SAlex Hornung if (!devfs_node_is_accessible(node)) 609ca8d7677SMatthew Dillon return ENOENT; 6104062d050SMatthew Dillon node_sync_dev_get(node); 61121864bc5SMatthew Dillon 61221864bc5SMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE); 61321864bc5SMatthew Dillon 61421864bc5SMatthew Dillon vap = ap->a_vap; 61521864bc5SMatthew Dillon 61621864bc5SMatthew Dillon if (vap->va_uid != (uid_t)VNOVAL) { 61721864bc5SMatthew Dillon if ((ap->a_cred->cr_uid != node->uid) && 61821864bc5SMatthew Dillon (!groupmember(node->gid, ap->a_cred))) { 61921864bc5SMatthew Dillon error = priv_check(curthread, PRIV_VFS_CHOWN); 620898c91eeSMatthew Dillon if (error) 62121864bc5SMatthew Dillon goto out; 62221864bc5SMatthew Dillon } 62321864bc5SMatthew Dillon node->uid = vap->va_uid; 62421864bc5SMatthew Dillon } 62521864bc5SMatthew Dillon 62621864bc5SMatthew Dillon if (vap->va_gid != (uid_t)VNOVAL) { 62721864bc5SMatthew Dillon if ((ap->a_cred->cr_uid != node->uid) && 62821864bc5SMatthew Dillon (!groupmember(node->gid, ap->a_cred))) { 62921864bc5SMatthew Dillon error = priv_check(curthread, PRIV_VFS_CHOWN); 630898c91eeSMatthew Dillon if (error) 63121864bc5SMatthew Dillon goto out; 63221864bc5SMatthew Dillon } 63321864bc5SMatthew Dillon node->gid = vap->va_gid; 63421864bc5SMatthew Dillon } 63521864bc5SMatthew Dillon 63621864bc5SMatthew Dillon if (vap->va_mode != (mode_t)VNOVAL) { 63721864bc5SMatthew Dillon if (ap->a_cred->cr_uid != node->uid) { 63821864bc5SMatthew Dillon error = priv_check(curthread, PRIV_VFS_ADMIN); 639898c91eeSMatthew Dillon if (error) 64021864bc5SMatthew Dillon goto out; 64121864bc5SMatthew Dillon } 64221864bc5SMatthew Dillon node->mode = vap->va_mode; 64321864bc5SMatthew Dillon } 64421864bc5SMatthew Dillon 64521864bc5SMatthew Dillon out: 6464062d050SMatthew Dillon node_sync_dev_set(node); 64707dfa375SAlex Hornung nanotime(&node->ctime); 64821864bc5SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE); 649898c91eeSMatthew Dillon 65021864bc5SMatthew Dillon return error; 65121864bc5SMatthew Dillon } 65221864bc5SMatthew Dillon 65321864bc5SMatthew Dillon 65421864bc5SMatthew Dillon static int 65521864bc5SMatthew Dillon devfs_readlink(struct vop_readlink_args *ap) 65621864bc5SMatthew Dillon { 65721864bc5SMatthew Dillon struct devfs_node *node = DEVFS_NODE(ap->a_vp); 658ca8d7677SMatthew Dillon int ret; 659ca8d7677SMatthew Dillon 660894bbb25SAlex Hornung if (!devfs_node_is_accessible(node)) 661ca8d7677SMatthew Dillon return ENOENT; 66221864bc5SMatthew Dillon 663ca8d7677SMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE); 664ca8d7677SMatthew Dillon ret = uiomove(node->symlink_name, node->symlink_namelen, ap->a_uio); 665ca8d7677SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE); 666ca8d7677SMatthew Dillon 667ca8d7677SMatthew Dillon return ret; 66821864bc5SMatthew Dillon } 66921864bc5SMatthew Dillon 67021864bc5SMatthew Dillon 67121864bc5SMatthew Dillon static int 67221864bc5SMatthew Dillon devfs_print(struct vop_print_args *ap) 67321864bc5SMatthew Dillon { 67421864bc5SMatthew Dillon return (0); 67521864bc5SMatthew Dillon } 67621864bc5SMatthew Dillon 67721864bc5SMatthew Dillon 67821864bc5SMatthew Dillon static int 67921864bc5SMatthew Dillon devfs_nsymlink(struct vop_nsymlink_args *ap) 68021864bc5SMatthew Dillon { 681898c91eeSMatthew Dillon struct devfs_node *dnode = DEVFS_NODE(ap->a_dvp); 682898c91eeSMatthew Dillon struct devfs_node *node; 683898c91eeSMatthew Dillon size_t targetlen; 68421864bc5SMatthew Dillon 685898c91eeSMatthew Dillon if (!devfs_node_is_accessible(dnode)) 686ca8d7677SMatthew Dillon return ENOENT; 687ca8d7677SMatthew Dillon 688894bbb25SAlex Hornung ap->a_vap->va_type = VLNK; 689894bbb25SAlex Hornung 690898c91eeSMatthew Dillon if ((dnode->node_type != Proot) && (dnode->node_type != Pdir)) 69121864bc5SMatthew Dillon goto out; 692898c91eeSMatthew Dillon 69321864bc5SMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE); 69421864bc5SMatthew Dillon devfs_allocvp(ap->a_dvp->v_mount, ap->a_vpp, Plink, 695898c91eeSMatthew Dillon ap->a_nch->ncp->nc_name, dnode, NULL); 69621864bc5SMatthew Dillon 697898c91eeSMatthew Dillon targetlen = strlen(ap->a_target); 69821864bc5SMatthew Dillon if (*ap->a_vpp) { 699898c91eeSMatthew Dillon node = DEVFS_NODE(*ap->a_vpp); 700898c91eeSMatthew Dillon node->flags |= DEVFS_USER_CREATED; 701898c91eeSMatthew Dillon node->symlink_namelen = targetlen; 702898c91eeSMatthew Dillon node->symlink_name = kmalloc(targetlen + 1, M_DEVFS, M_WAITOK); 703898c91eeSMatthew Dillon memcpy(node->symlink_name, ap->a_target, targetlen); 704898c91eeSMatthew Dillon node->symlink_name[targetlen] = '\0'; 70521864bc5SMatthew Dillon cache_setunresolved(ap->a_nch); 70621864bc5SMatthew Dillon cache_setvp(ap->a_nch, *ap->a_vpp); 70721864bc5SMatthew Dillon } 70821864bc5SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE); 70921864bc5SMatthew Dillon out: 71021864bc5SMatthew Dillon return ((*ap->a_vpp == NULL) ? ENOTDIR : 0); 71121864bc5SMatthew Dillon } 71221864bc5SMatthew Dillon 71321864bc5SMatthew Dillon 71421864bc5SMatthew Dillon static int 71521864bc5SMatthew Dillon devfs_nremove(struct vop_nremove_args *ap) 71621864bc5SMatthew Dillon { 717898c91eeSMatthew Dillon struct devfs_node *dnode = DEVFS_NODE(ap->a_dvp); 71821864bc5SMatthew Dillon struct devfs_node *node; 71921864bc5SMatthew Dillon struct namecache *ncp; 72021864bc5SMatthew Dillon int error = ENOENT; 72121864bc5SMatthew Dillon 72221864bc5SMatthew Dillon ncp = ap->a_nch->ncp; 72321864bc5SMatthew Dillon 724898c91eeSMatthew Dillon if (!devfs_node_is_accessible(dnode)) 725ca8d7677SMatthew Dillon return ENOENT; 726ca8d7677SMatthew Dillon 72721864bc5SMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE); 72821864bc5SMatthew Dillon 729898c91eeSMatthew Dillon if ((dnode->node_type != Proot) && (dnode->node_type != Pdir)) 73021864bc5SMatthew Dillon goto out; 73121864bc5SMatthew Dillon 732898c91eeSMatthew Dillon TAILQ_FOREACH(node, DEVFS_DENODE_HEAD(dnode), link) { 733898c91eeSMatthew Dillon if (ncp->nc_nlen != node->d_dir.d_namlen) 734898c91eeSMatthew Dillon continue; 735898c91eeSMatthew Dillon if (memcmp(ncp->nc_name, node->d_dir.d_name, ncp->nc_nlen)) 736898c91eeSMatthew Dillon continue; 737898c91eeSMatthew Dillon 738898c91eeSMatthew Dillon /* 739898c91eeSMatthew Dillon * only allow removal of user created stuff (e.g. symlinks) 740898c91eeSMatthew Dillon */ 74121864bc5SMatthew Dillon if ((node->flags & DEVFS_USER_CREATED) == 0) { 74221864bc5SMatthew Dillon error = EPERM; 74321864bc5SMatthew Dillon goto out; 74421864bc5SMatthew Dillon } else { 74521864bc5SMatthew Dillon if (node->v_node) 74621864bc5SMatthew Dillon cache_inval_vp(node->v_node, CINV_DESTROY); 74721864bc5SMatthew Dillon devfs_unlinkp(node); 74821864bc5SMatthew Dillon error = 0; 74921864bc5SMatthew Dillon break; 75021864bc5SMatthew Dillon } 75121864bc5SMatthew Dillon } 75221864bc5SMatthew Dillon 75321864bc5SMatthew Dillon cache_setunresolved(ap->a_nch); 75421864bc5SMatthew Dillon cache_setvp(ap->a_nch, NULL); 75521864bc5SMatthew Dillon 75621864bc5SMatthew Dillon out: 75721864bc5SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE); 75821864bc5SMatthew Dillon return error; 75921864bc5SMatthew Dillon } 76021864bc5SMatthew Dillon 76121864bc5SMatthew Dillon 76221864bc5SMatthew Dillon static int 76321864bc5SMatthew Dillon devfs_spec_open(struct vop_open_args *ap) 76421864bc5SMatthew Dillon { 76521864bc5SMatthew Dillon struct vnode *vp = ap->a_vp; 766ca8d7677SMatthew Dillon struct vnode *orig_vp = NULL; 767898c91eeSMatthew Dillon struct devfs_node *node = DEVFS_NODE(vp); 768898c91eeSMatthew Dillon struct devfs_node *newnode; 76921864bc5SMatthew Dillon cdev_t dev, ndev = NULL; 77021864bc5SMatthew Dillon int error = 0; 77121864bc5SMatthew Dillon 772898c91eeSMatthew Dillon if (node) { 773898c91eeSMatthew Dillon if (node->d_dev == NULL) 77421864bc5SMatthew Dillon return ENXIO; 775898c91eeSMatthew Dillon if (!devfs_node_is_accessible(node)) 776894bbb25SAlex Hornung return ENOENT; 77721864bc5SMatthew Dillon } 77821864bc5SMatthew Dillon 77921864bc5SMatthew Dillon if ((dev = vp->v_rdev) == NULL) 78021864bc5SMatthew Dillon return ENXIO; 78121864bc5SMatthew Dillon 782898c91eeSMatthew Dillon if (node && ap->a_fp) { 78321864bc5SMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, "devfs_spec_open: -1.1-\n"); 78421864bc5SMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE); 78507dfa375SAlex Hornung 78607dfa375SAlex Hornung ndev = devfs_clone(dev, node->d_dir.d_name, node->d_dir.d_namlen, 78707dfa375SAlex Hornung ap->a_mode, ap->a_cred); 78807dfa375SAlex Hornung if (ndev != NULL) { 789898c91eeSMatthew Dillon newnode = devfs_create_device_node( 790898c91eeSMatthew Dillon DEVFS_MNTDATA(vp->v_mount)->root_node, 79107dfa375SAlex Hornung ndev, NULL, NULL); 79207dfa375SAlex Hornung /* XXX: possibly destroy device if this happens */ 79307dfa375SAlex Hornung 79407dfa375SAlex Hornung if (newnode != NULL) { 79507dfa375SAlex Hornung dev = ndev; 79607dfa375SAlex Hornung devfs_link_dev(dev); 79721864bc5SMatthew Dillon 798898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 799898c91eeSMatthew Dillon "parent here is: %s, node is: |%s|\n", 800898c91eeSMatthew Dillon ((node->parent->node_type == Proot) ? 801898c91eeSMatthew Dillon "ROOT!" : node->parent->d_dir.d_name), 802898c91eeSMatthew Dillon newnode->d_dir.d_name); 803898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 804898c91eeSMatthew Dillon "test: %s\n", 805898c91eeSMatthew Dillon ((struct devfs_node *)(TAILQ_LAST(DEVFS_DENODE_HEAD(node->parent), devfs_node_head)))->d_dir.d_name); 80621864bc5SMatthew Dillon 807ca8d7677SMatthew Dillon /* 808ca8d7677SMatthew Dillon * orig_vp is set to the original vp if we cloned. 809ca8d7677SMatthew Dillon */ 810ca8d7677SMatthew Dillon /* node->flags |= DEVFS_CLONED; */ 811898c91eeSMatthew Dillon devfs_allocv(&vp, newnode); 812ca8d7677SMatthew Dillon orig_vp = ap->a_vp; 81321864bc5SMatthew Dillon ap->a_vp = vp; 81421864bc5SMatthew Dillon } 81507dfa375SAlex Hornung } 81621864bc5SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE); 81721864bc5SMatthew Dillon } 81821864bc5SMatthew Dillon 819898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 820898c91eeSMatthew Dillon "devfs_spec_open() called on %s! \n", 821898c91eeSMatthew Dillon dev->si_name); 822898c91eeSMatthew Dillon 82321864bc5SMatthew Dillon /* 82421864bc5SMatthew Dillon * Make this field valid before any I/O in ->d_open 82521864bc5SMatthew Dillon */ 82621864bc5SMatthew Dillon if (!dev->si_iosize_max) 82721864bc5SMatthew Dillon dev->si_iosize_max = DFLTPHYS; 82821864bc5SMatthew Dillon 82921864bc5SMatthew Dillon if (dev_dflags(dev) & D_TTY) 83021864bc5SMatthew Dillon vp->v_flag |= VISTTY; 83121864bc5SMatthew Dillon 83221864bc5SMatthew Dillon vn_unlock(vp); 83321864bc5SMatthew Dillon error = dev_dopen(dev, ap->a_mode, S_IFCHR, ap->a_cred); 83421864bc5SMatthew Dillon vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 83521864bc5SMatthew Dillon 836ca8d7677SMatthew Dillon /* 837ca8d7677SMatthew Dillon * Clean up any cloned vp if we error out. 838ca8d7677SMatthew Dillon */ 83921864bc5SMatthew Dillon if (error) { 840ca8d7677SMatthew Dillon if (orig_vp) { 84121864bc5SMatthew Dillon vput(vp); 842ca8d7677SMatthew Dillon ap->a_vp = orig_vp; 843ca8d7677SMatthew Dillon /* orig_vp = NULL; */ 844ca8d7677SMatthew Dillon } 84521864bc5SMatthew Dillon return error; 84621864bc5SMatthew Dillon } 84721864bc5SMatthew Dillon 848d894b0ebSAntonio Huete /* 849*ddd7de82SAntonio Huete * This checks if the disk device is going to be opened for writing. 850*ddd7de82SAntonio Huete * It will be only allowed in the cases where securelevel permits it 851*ddd7de82SAntonio Huete * and it's not mounted R/W. 852d894b0ebSAntonio Huete */ 853*ddd7de82SAntonio Huete if ((dev_dflags(dev) & D_DISK) && (ap->a_mode & FWRITE) && 854d894b0ebSAntonio Huete (ap->a_cred != FSCRED)) { 855*ddd7de82SAntonio Huete 856*ddd7de82SAntonio Huete /* Very secure mode. No open for writing allowed */ 857d894b0ebSAntonio Huete if (securelevel >= 2) 858d894b0ebSAntonio Huete return EPERM; 859*ddd7de82SAntonio Huete 860*ddd7de82SAntonio Huete /* 861*ddd7de82SAntonio Huete * If it is mounted R/W, do not allow to open for writing. 862*ddd7de82SAntonio Huete * In the case it's mounted read-only but securelevel 863*ddd7de82SAntonio Huete * is >= 1, then do not allow opening for writing either. 864*ddd7de82SAntonio Huete */ 865*ddd7de82SAntonio Huete if (vfs_mountedon(vp)) { 866*ddd7de82SAntonio Huete if (!(dev->si_mountpoint->mnt_flag & MNT_RDONLY)) 867*ddd7de82SAntonio Huete return EBUSY; 868*ddd7de82SAntonio Huete else if (securelevel >= 1) 869*ddd7de82SAntonio Huete return EPERM; 870d894b0ebSAntonio Huete } 871d894b0ebSAntonio Huete } 87221864bc5SMatthew Dillon 87321864bc5SMatthew Dillon if (dev_dflags(dev) & D_TTY) { 87421864bc5SMatthew Dillon if (dev->si_tty) { 87521864bc5SMatthew Dillon struct tty *tp; 87621864bc5SMatthew Dillon tp = dev->si_tty; 87721864bc5SMatthew Dillon if (!tp->t_stop) { 878898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 879898c91eeSMatthew Dillon "devfs: no t_stop\n"); 88021864bc5SMatthew Dillon tp->t_stop = nottystop; 88121864bc5SMatthew Dillon } 88221864bc5SMatthew Dillon } 88321864bc5SMatthew Dillon } 88421864bc5SMatthew Dillon 88521864bc5SMatthew Dillon 88621864bc5SMatthew Dillon if (vn_isdisk(vp, NULL)) { 88721864bc5SMatthew Dillon if (!dev->si_bsize_phys) 88821864bc5SMatthew Dillon dev->si_bsize_phys = DEV_BSIZE; 88921864bc5SMatthew Dillon vinitvmio(vp, IDX_TO_OFF(INT_MAX)); 89021864bc5SMatthew Dillon } 89121864bc5SMatthew Dillon 89221864bc5SMatthew Dillon vop_stdopen(ap); 89307dfa375SAlex Hornung #if 0 894898c91eeSMatthew Dillon if (node) 895898c91eeSMatthew Dillon nanotime(&node->atime); 89607dfa375SAlex Hornung #endif 89721864bc5SMatthew Dillon 898ca8d7677SMatthew Dillon if (orig_vp) 89921864bc5SMatthew Dillon vn_unlock(vp); 90021864bc5SMatthew Dillon 90121864bc5SMatthew Dillon /* Ugly pty magic, to make pty devices appear once they are opened */ 902898c91eeSMatthew Dillon if (node && (node->flags & DEVFS_PTY) == DEVFS_PTY) 903898c91eeSMatthew Dillon node->flags &= ~DEVFS_INVISIBLE; 90421864bc5SMatthew Dillon 90521864bc5SMatthew Dillon if (ap->a_fp) { 90621864bc5SMatthew Dillon ap->a_fp->f_type = DTYPE_VNODE; 90721864bc5SMatthew Dillon ap->a_fp->f_flag = ap->a_mode & FMASK; 90821864bc5SMatthew Dillon ap->a_fp->f_ops = &devfs_dev_fileops; 90921864bc5SMatthew Dillon ap->a_fp->f_data = vp; 91021864bc5SMatthew Dillon } 91121864bc5SMatthew Dillon 91221864bc5SMatthew Dillon return 0; 91321864bc5SMatthew Dillon } 91421864bc5SMatthew Dillon 91521864bc5SMatthew Dillon 91621864bc5SMatthew Dillon static int 91721864bc5SMatthew Dillon devfs_spec_close(struct vop_close_args *ap) 91821864bc5SMatthew Dillon { 919898c91eeSMatthew Dillon struct devfs_node *node = DEVFS_NODE(ap->a_vp); 92021864bc5SMatthew Dillon struct proc *p = curproc; 92121864bc5SMatthew Dillon struct vnode *vp = ap->a_vp; 92221864bc5SMatthew Dillon cdev_t dev = vp->v_rdev; 92321864bc5SMatthew Dillon int error = 0; 92421864bc5SMatthew Dillon int needrelock; 92521864bc5SMatthew Dillon 926898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 927898c91eeSMatthew Dillon "devfs_spec_close() called on %s! \n", 928898c91eeSMatthew Dillon dev->si_name); 92921864bc5SMatthew Dillon 93021864bc5SMatthew Dillon /* 93121864bc5SMatthew Dillon * A couple of hacks for devices and tty devices. The 93221864bc5SMatthew Dillon * vnode ref count cannot be used to figure out the 93321864bc5SMatthew Dillon * last close, but we can use v_opencount now that 93421864bc5SMatthew Dillon * revoke works properly. 93521864bc5SMatthew Dillon * 93621864bc5SMatthew Dillon * Detect the last close on a controlling terminal and clear 93721864bc5SMatthew Dillon * the session (half-close). 93821864bc5SMatthew Dillon */ 93921864bc5SMatthew Dillon if (dev) 94021864bc5SMatthew Dillon reference_dev(dev); 94121864bc5SMatthew Dillon 94221864bc5SMatthew Dillon if (p && vp->v_opencount <= 1 && vp == p->p_session->s_ttyvp) { 94321864bc5SMatthew Dillon p->p_session->s_ttyvp = NULL; 94421864bc5SMatthew Dillon vrele(vp); 94521864bc5SMatthew Dillon } 94621864bc5SMatthew Dillon 94721864bc5SMatthew Dillon /* 94821864bc5SMatthew Dillon * Vnodes can be opened and closed multiple times. Do not really 94921864bc5SMatthew Dillon * close the device unless (1) it is being closed forcibly, 95021864bc5SMatthew Dillon * (2) the device wants to track closes, or (3) this is the last 95121864bc5SMatthew Dillon * vnode doing its last close on the device. 95221864bc5SMatthew Dillon * 95321864bc5SMatthew Dillon * XXX the VXLOCK (force close) case can leave vnodes referencing 95421864bc5SMatthew Dillon * a closed device. This might not occur now that our revoke is 95521864bc5SMatthew Dillon * fixed. 95621864bc5SMatthew Dillon */ 95721864bc5SMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, "devfs_spec_close() -1- \n"); 95821864bc5SMatthew Dillon if (dev && ((vp->v_flag & VRECLAIMED) || 95921864bc5SMatthew Dillon (dev_dflags(dev) & D_TRACKCLOSE) || 96021864bc5SMatthew Dillon (vp->v_opencount == 1))) { 961898c91eeSMatthew Dillon /* 962898c91eeSMatthew Dillon * Unlock around dev_dclose() 963898c91eeSMatthew Dillon */ 96421864bc5SMatthew Dillon needrelock = 0; 96521864bc5SMatthew Dillon if (vn_islocked(vp)) { 96621864bc5SMatthew Dillon needrelock = 1; 96721864bc5SMatthew Dillon vn_unlock(vp); 96821864bc5SMatthew Dillon } 96921864bc5SMatthew Dillon error = dev_dclose(dev, ap->a_fflag, S_IFCHR); 970898c91eeSMatthew Dillon 971898c91eeSMatthew Dillon /* 972898c91eeSMatthew Dillon * Ugly pty magic, to make pty devices disappear again once 973898c91eeSMatthew Dillon * they are closed 974898c91eeSMatthew Dillon */ 975898c91eeSMatthew Dillon if (node && (node->flags & DEVFS_PTY) == DEVFS_PTY) 976898c91eeSMatthew Dillon node->flags |= DEVFS_INVISIBLE; 97721864bc5SMatthew Dillon 97821864bc5SMatthew Dillon if (needrelock) 97921864bc5SMatthew Dillon vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 98021864bc5SMatthew Dillon } else { 98121864bc5SMatthew Dillon error = 0; 98221864bc5SMatthew Dillon } 98321864bc5SMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, "devfs_spec_close() -2- \n"); 984898c91eeSMatthew Dillon 98521864bc5SMatthew Dillon /* 98621864bc5SMatthew Dillon * Track the actual opens and closes on the vnode. The last close 987898c91eeSMatthew Dillon * disassociates the rdev. If the rdev is already disassociated or 988898c91eeSMatthew Dillon * the opencount is already 0, the vnode might have been revoked 989898c91eeSMatthew Dillon * and no further opencount tracking occurs. 99021864bc5SMatthew Dillon */ 991898c91eeSMatthew Dillon if (dev) 99221864bc5SMatthew Dillon release_dev(dev); 993898c91eeSMatthew Dillon if (vp->v_opencount > 0) 99421864bc5SMatthew Dillon vop_stdclose(ap); 99521864bc5SMatthew Dillon return(error); 99621864bc5SMatthew Dillon 99721864bc5SMatthew Dillon } 99821864bc5SMatthew Dillon 99921864bc5SMatthew Dillon 100021864bc5SMatthew Dillon static int 100121864bc5SMatthew Dillon devfs_specf_close(struct file *fp) 100221864bc5SMatthew Dillon { 100321864bc5SMatthew Dillon struct vnode *vp = (struct vnode *)fp->f_data; 1004898c91eeSMatthew Dillon int error; 100521864bc5SMatthew Dillon 100621864bc5SMatthew Dillon get_mplock(); 100721864bc5SMatthew Dillon fp->f_ops = &badfileops; 100821864bc5SMatthew Dillon error = vn_close(vp, fp->f_flag); 100921864bc5SMatthew Dillon rel_mplock(); 101021864bc5SMatthew Dillon 101121864bc5SMatthew Dillon return (error); 101221864bc5SMatthew Dillon } 101321864bc5SMatthew Dillon 101421864bc5SMatthew Dillon 101521864bc5SMatthew Dillon /* 101621864bc5SMatthew Dillon * Device-optimized file table vnode read routine. 101721864bc5SMatthew Dillon * 101821864bc5SMatthew Dillon * This bypasses the VOP table and talks directly to the device. Most 101921864bc5SMatthew Dillon * filesystems just route to specfs and can make this optimization. 102021864bc5SMatthew Dillon * 102121864bc5SMatthew Dillon * MPALMOSTSAFE - acquires mplock 102221864bc5SMatthew Dillon */ 102321864bc5SMatthew Dillon static int 1024898c91eeSMatthew Dillon devfs_specf_read(struct file *fp, struct uio *uio, 1025898c91eeSMatthew Dillon struct ucred *cred, int flags) 102621864bc5SMatthew Dillon { 1027898c91eeSMatthew Dillon struct devfs_node *node; 102821864bc5SMatthew Dillon struct vnode *vp; 102921864bc5SMatthew Dillon int ioflag; 103021864bc5SMatthew Dillon int error; 103121864bc5SMatthew Dillon cdev_t dev; 103221864bc5SMatthew Dillon 103321864bc5SMatthew Dillon KASSERT(uio->uio_td == curthread, 103421864bc5SMatthew Dillon ("uio_td %p is not td %p", uio->uio_td, curthread)); 103521864bc5SMatthew Dillon 10363a1032a6SAlex Hornung if (uio->uio_resid == 0) 10373a1032a6SAlex Hornung return 0; 10383a1032a6SAlex Hornung 103921864bc5SMatthew Dillon vp = (struct vnode *)fp->f_data; 10403a1032a6SAlex Hornung if (vp == NULL || vp->v_type == VBAD) 10413a1032a6SAlex Hornung return EBADF; 10423a1032a6SAlex Hornung 1043898c91eeSMatthew Dillon node = DEVFS_NODE(vp); 104421864bc5SMatthew Dillon 10453a1032a6SAlex Hornung if ((dev = vp->v_rdev) == NULL) 10463a1032a6SAlex Hornung return EBADF; 10473a1032a6SAlex Hornung 10483a1032a6SAlex Hornung /* only acquire mplock for devices that require it */ 10493a1032a6SAlex Hornung if (!(dev_dflags(dev) & D_MPSAFE_READ)) { 10503a1032a6SAlex Hornung atomic_add_int(&mplock_reads, 1); 10513a1032a6SAlex Hornung get_mplock(); 10523a1032a6SAlex Hornung } else { 10533a1032a6SAlex Hornung atomic_add_int(&mpsafe_reads, 1); 105421864bc5SMatthew Dillon } 105521864bc5SMatthew Dillon 105621864bc5SMatthew Dillon reference_dev(dev); 105721864bc5SMatthew Dillon 105821864bc5SMatthew Dillon if ((flags & O_FOFFSET) == 0) 105921864bc5SMatthew Dillon uio->uio_offset = fp->f_offset; 106021864bc5SMatthew Dillon 106121864bc5SMatthew Dillon ioflag = 0; 106221864bc5SMatthew Dillon if (flags & O_FBLOCKING) { 106321864bc5SMatthew Dillon /* ioflag &= ~IO_NDELAY; */ 106421864bc5SMatthew Dillon } else if (flags & O_FNONBLOCKING) { 106521864bc5SMatthew Dillon ioflag |= IO_NDELAY; 106621864bc5SMatthew Dillon } else if (fp->f_flag & FNONBLOCK) { 106721864bc5SMatthew Dillon ioflag |= IO_NDELAY; 106821864bc5SMatthew Dillon } 106921864bc5SMatthew Dillon if (flags & O_FBUFFERED) { 107021864bc5SMatthew Dillon /* ioflag &= ~IO_DIRECT; */ 107121864bc5SMatthew Dillon } else if (flags & O_FUNBUFFERED) { 107221864bc5SMatthew Dillon ioflag |= IO_DIRECT; 107321864bc5SMatthew Dillon } else if (fp->f_flag & O_DIRECT) { 107421864bc5SMatthew Dillon ioflag |= IO_DIRECT; 107521864bc5SMatthew Dillon } 107621864bc5SMatthew Dillon ioflag |= sequential_heuristic(uio, fp); 107721864bc5SMatthew Dillon 107821864bc5SMatthew Dillon error = dev_dread(dev, uio, ioflag); 107921864bc5SMatthew Dillon 108021864bc5SMatthew Dillon release_dev(dev); 1081898c91eeSMatthew Dillon if (node) 1082898c91eeSMatthew Dillon nanotime(&node->atime); 108321864bc5SMatthew Dillon if ((flags & O_FOFFSET) == 0) 108421864bc5SMatthew Dillon fp->f_offset = uio->uio_offset; 108521864bc5SMatthew Dillon fp->f_nextoff = uio->uio_offset; 10863a1032a6SAlex Hornung 10873a1032a6SAlex Hornung if (!(dev_dflags(dev) & D_MPSAFE_READ)) 108821864bc5SMatthew Dillon rel_mplock(); 10893a1032a6SAlex Hornung 109021864bc5SMatthew Dillon return (error); 109121864bc5SMatthew Dillon } 109221864bc5SMatthew Dillon 109321864bc5SMatthew Dillon 109421864bc5SMatthew Dillon static int 1095898c91eeSMatthew Dillon devfs_specf_write(struct file *fp, struct uio *uio, 1096898c91eeSMatthew Dillon struct ucred *cred, int flags) 109721864bc5SMatthew Dillon { 1098898c91eeSMatthew Dillon struct devfs_node *node; 109921864bc5SMatthew Dillon struct vnode *vp; 110021864bc5SMatthew Dillon int ioflag; 110121864bc5SMatthew Dillon int error; 110221864bc5SMatthew Dillon cdev_t dev; 110321864bc5SMatthew Dillon 110421864bc5SMatthew Dillon KASSERT(uio->uio_td == curthread, 110521864bc5SMatthew Dillon ("uio_td %p is not p %p", uio->uio_td, curthread)); 110621864bc5SMatthew Dillon 110721864bc5SMatthew Dillon vp = (struct vnode *)fp->f_data; 11083a1032a6SAlex Hornung if (vp == NULL || vp->v_type == VBAD) 11093a1032a6SAlex Hornung return EBADF; 11103a1032a6SAlex Hornung 1111898c91eeSMatthew Dillon node = DEVFS_NODE(vp); 11123a1032a6SAlex Hornung 111321864bc5SMatthew Dillon if (vp->v_type == VREG) 111421864bc5SMatthew Dillon bwillwrite(uio->uio_resid); 11153a1032a6SAlex Hornung 111621864bc5SMatthew Dillon vp = (struct vnode *)fp->f_data; 111721864bc5SMatthew Dillon 11183a1032a6SAlex Hornung if ((dev = vp->v_rdev) == NULL) 11193a1032a6SAlex Hornung return EBADF; 11203a1032a6SAlex Hornung 11213a1032a6SAlex Hornung /* only acquire mplock for devices that require it */ 11223a1032a6SAlex Hornung if (!(dev_dflags(dev) & D_MPSAFE_WRITE)) { 11233a1032a6SAlex Hornung atomic_add_int(&mplock_writes, 1); 11243a1032a6SAlex Hornung get_mplock(); 11253a1032a6SAlex Hornung } else { 11263a1032a6SAlex Hornung atomic_add_int(&mpsafe_writes, 1); 112721864bc5SMatthew Dillon } 11283a1032a6SAlex Hornung 112921864bc5SMatthew Dillon reference_dev(dev); 113021864bc5SMatthew Dillon 113121864bc5SMatthew Dillon if ((flags & O_FOFFSET) == 0) 113221864bc5SMatthew Dillon uio->uio_offset = fp->f_offset; 113321864bc5SMatthew Dillon 113421864bc5SMatthew Dillon ioflag = IO_UNIT; 113521864bc5SMatthew Dillon if (vp->v_type == VREG && 113621864bc5SMatthew Dillon ((fp->f_flag & O_APPEND) || (flags & O_FAPPEND))) { 113721864bc5SMatthew Dillon ioflag |= IO_APPEND; 113821864bc5SMatthew Dillon } 113921864bc5SMatthew Dillon 114021864bc5SMatthew Dillon if (flags & O_FBLOCKING) { 114121864bc5SMatthew Dillon /* ioflag &= ~IO_NDELAY; */ 114221864bc5SMatthew Dillon } else if (flags & O_FNONBLOCKING) { 114321864bc5SMatthew Dillon ioflag |= IO_NDELAY; 114421864bc5SMatthew Dillon } else if (fp->f_flag & FNONBLOCK) { 114521864bc5SMatthew Dillon ioflag |= IO_NDELAY; 114621864bc5SMatthew Dillon } 114721864bc5SMatthew Dillon if (flags & O_FBUFFERED) { 114821864bc5SMatthew Dillon /* ioflag &= ~IO_DIRECT; */ 114921864bc5SMatthew Dillon } else if (flags & O_FUNBUFFERED) { 115021864bc5SMatthew Dillon ioflag |= IO_DIRECT; 115121864bc5SMatthew Dillon } else if (fp->f_flag & O_DIRECT) { 115221864bc5SMatthew Dillon ioflag |= IO_DIRECT; 115321864bc5SMatthew Dillon } 115421864bc5SMatthew Dillon if (flags & O_FASYNCWRITE) { 115521864bc5SMatthew Dillon /* ioflag &= ~IO_SYNC; */ 115621864bc5SMatthew Dillon } else if (flags & O_FSYNCWRITE) { 115721864bc5SMatthew Dillon ioflag |= IO_SYNC; 115821864bc5SMatthew Dillon } else if (fp->f_flag & O_FSYNC) { 115921864bc5SMatthew Dillon ioflag |= IO_SYNC; 116021864bc5SMatthew Dillon } 116121864bc5SMatthew Dillon 116221864bc5SMatthew Dillon if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_SYNCHRONOUS)) 116321864bc5SMatthew Dillon ioflag |= IO_SYNC; 116421864bc5SMatthew Dillon ioflag |= sequential_heuristic(uio, fp); 116521864bc5SMatthew Dillon 116621864bc5SMatthew Dillon error = dev_dwrite(dev, uio, ioflag); 116721864bc5SMatthew Dillon 116821864bc5SMatthew Dillon release_dev(dev); 116907dfa375SAlex Hornung if (node) { 117007dfa375SAlex Hornung nanotime(&node->atime); 1171898c91eeSMatthew Dillon nanotime(&node->mtime); 117207dfa375SAlex Hornung } 117321864bc5SMatthew Dillon 117421864bc5SMatthew Dillon if ((flags & O_FOFFSET) == 0) 117521864bc5SMatthew Dillon fp->f_offset = uio->uio_offset; 117621864bc5SMatthew Dillon fp->f_nextoff = uio->uio_offset; 11773a1032a6SAlex Hornung 11783a1032a6SAlex Hornung if (!(dev_dflags(dev) & D_MPSAFE_WRITE)) 117921864bc5SMatthew Dillon rel_mplock(); 118021864bc5SMatthew Dillon return (error); 118121864bc5SMatthew Dillon } 118221864bc5SMatthew Dillon 118321864bc5SMatthew Dillon 118421864bc5SMatthew Dillon static int 118521864bc5SMatthew Dillon devfs_specf_stat(struct file *fp, struct stat *sb, struct ucred *cred) 118621864bc5SMatthew Dillon { 118721864bc5SMatthew Dillon struct vnode *vp; 118821864bc5SMatthew Dillon struct vattr vattr; 118921864bc5SMatthew Dillon struct vattr *vap; 119021864bc5SMatthew Dillon u_short mode; 119121864bc5SMatthew Dillon cdev_t dev; 11923a1032a6SAlex Hornung int error; 11933a1032a6SAlex Hornung 11943a1032a6SAlex Hornung vp = (struct vnode *)fp->f_data; 11953a1032a6SAlex Hornung if (vp == NULL || vp->v_type == VBAD) 11963a1032a6SAlex Hornung return EBADF; 11973a1032a6SAlex Hornung 11983a1032a6SAlex Hornung error = vn_stat(vp, sb, cred); 11993a1032a6SAlex Hornung if (error) 12003a1032a6SAlex Hornung return (error); 120121864bc5SMatthew Dillon 120221864bc5SMatthew Dillon vap = &vattr; 120321864bc5SMatthew Dillon error = VOP_GETATTR(vp, vap); 12043a1032a6SAlex Hornung if (error) 120521864bc5SMatthew Dillon return (error); 120621864bc5SMatthew Dillon 120721864bc5SMatthew Dillon /* 120821864bc5SMatthew Dillon * Zero the spare stat fields 120921864bc5SMatthew Dillon */ 121021864bc5SMatthew Dillon sb->st_lspare = 0; 1211d98152a8SMatthew Dillon sb->st_qspare1 = 0; 1212d98152a8SMatthew Dillon sb->st_qspare2 = 0; 121321864bc5SMatthew Dillon 121421864bc5SMatthew Dillon /* 121521864bc5SMatthew Dillon * Copy from vattr table ... or not in case it's a cloned device 121621864bc5SMatthew Dillon */ 121721864bc5SMatthew Dillon if (vap->va_fsid != VNOVAL) 121821864bc5SMatthew Dillon sb->st_dev = vap->va_fsid; 121921864bc5SMatthew Dillon else 122021864bc5SMatthew Dillon sb->st_dev = vp->v_mount->mnt_stat.f_fsid.val[0]; 122121864bc5SMatthew Dillon 122221864bc5SMatthew Dillon sb->st_ino = vap->va_fileid; 122321864bc5SMatthew Dillon 122421864bc5SMatthew Dillon mode = vap->va_mode; 122521864bc5SMatthew Dillon mode |= S_IFCHR; 122621864bc5SMatthew Dillon sb->st_mode = mode; 122721864bc5SMatthew Dillon 122821864bc5SMatthew Dillon if (vap->va_nlink > (nlink_t)-1) 122921864bc5SMatthew Dillon sb->st_nlink = (nlink_t)-1; 123021864bc5SMatthew Dillon else 123121864bc5SMatthew Dillon sb->st_nlink = vap->va_nlink; 12323a1032a6SAlex Hornung 123321864bc5SMatthew Dillon sb->st_uid = vap->va_uid; 123421864bc5SMatthew Dillon sb->st_gid = vap->va_gid; 1235ca8d7677SMatthew Dillon sb->st_rdev = dev2udev(DEVFS_NODE(vp)->d_dev); 12362d076755SAlex Hornung sb->st_size = vap->va_bytes; 123721864bc5SMatthew Dillon sb->st_atimespec = vap->va_atime; 123821864bc5SMatthew Dillon sb->st_mtimespec = vap->va_mtime; 123921864bc5SMatthew Dillon sb->st_ctimespec = vap->va_ctime; 124021864bc5SMatthew Dillon 124121864bc5SMatthew Dillon /* 124221864bc5SMatthew Dillon * A VCHR and VBLK device may track the last access and last modified 124321864bc5SMatthew Dillon * time independantly of the filesystem. This is particularly true 124421864bc5SMatthew Dillon * because device read and write calls may bypass the filesystem. 124521864bc5SMatthew Dillon */ 124621864bc5SMatthew Dillon if (vp->v_type == VCHR || vp->v_type == VBLK) { 124721864bc5SMatthew Dillon dev = vp->v_rdev; 124821864bc5SMatthew Dillon if (dev != NULL) { 124921864bc5SMatthew Dillon if (dev->si_lastread) { 125021864bc5SMatthew Dillon sb->st_atimespec.tv_sec = dev->si_lastread; 125121864bc5SMatthew Dillon sb->st_atimespec.tv_nsec = 0; 125221864bc5SMatthew Dillon } 125321864bc5SMatthew Dillon if (dev->si_lastwrite) { 125421864bc5SMatthew Dillon sb->st_atimespec.tv_sec = dev->si_lastwrite; 125521864bc5SMatthew Dillon sb->st_atimespec.tv_nsec = 0; 125621864bc5SMatthew Dillon } 125721864bc5SMatthew Dillon } 125821864bc5SMatthew Dillon } 125921864bc5SMatthew Dillon 126021864bc5SMatthew Dillon /* 126121864bc5SMatthew Dillon * According to www.opengroup.org, the meaning of st_blksize is 126221864bc5SMatthew Dillon * "a filesystem-specific preferred I/O block size for this 126321864bc5SMatthew Dillon * object. In some filesystem types, this may vary from file 126421864bc5SMatthew Dillon * to file" 126521864bc5SMatthew Dillon * Default to PAGE_SIZE after much discussion. 126621864bc5SMatthew Dillon */ 126721864bc5SMatthew Dillon 126821864bc5SMatthew Dillon sb->st_blksize = PAGE_SIZE; 126921864bc5SMatthew Dillon 127021864bc5SMatthew Dillon sb->st_flags = vap->va_flags; 127121864bc5SMatthew Dillon 127221864bc5SMatthew Dillon error = priv_check_cred(cred, PRIV_VFS_GENERATION, 0); 127321864bc5SMatthew Dillon if (error) 127421864bc5SMatthew Dillon sb->st_gen = 0; 127521864bc5SMatthew Dillon else 127621864bc5SMatthew Dillon sb->st_gen = (u_int32_t)vap->va_gen; 127721864bc5SMatthew Dillon 127821864bc5SMatthew Dillon sb->st_blocks = vap->va_bytes / S_BLKSIZE; 127921864bc5SMatthew Dillon 128021864bc5SMatthew Dillon return (0); 128121864bc5SMatthew Dillon } 128221864bc5SMatthew Dillon 128321864bc5SMatthew Dillon 128421864bc5SMatthew Dillon static int 128521864bc5SMatthew Dillon devfs_specf_kqfilter(struct file *fp, struct knote *kn) 128621864bc5SMatthew Dillon { 128721864bc5SMatthew Dillon struct vnode *vp; 128821864bc5SMatthew Dillon int error; 128921864bc5SMatthew Dillon cdev_t dev; 129021864bc5SMatthew Dillon 129121864bc5SMatthew Dillon get_mplock(); 129221864bc5SMatthew Dillon 129321864bc5SMatthew Dillon vp = (struct vnode *)fp->f_data; 129421864bc5SMatthew Dillon if (vp == NULL || vp->v_type == VBAD) { 129521864bc5SMatthew Dillon error = EBADF; 129621864bc5SMatthew Dillon goto done; 129721864bc5SMatthew Dillon } 129821864bc5SMatthew Dillon if ((dev = vp->v_rdev) == NULL) { 129921864bc5SMatthew Dillon error = EBADF; 130021864bc5SMatthew Dillon goto done; 130121864bc5SMatthew Dillon } 130221864bc5SMatthew Dillon reference_dev(dev); 130321864bc5SMatthew Dillon 130421864bc5SMatthew Dillon error = dev_dkqfilter(dev, kn); 130521864bc5SMatthew Dillon 130621864bc5SMatthew Dillon release_dev(dev); 130721864bc5SMatthew Dillon 130821864bc5SMatthew Dillon done: 130921864bc5SMatthew Dillon rel_mplock(); 131021864bc5SMatthew Dillon return (error); 131121864bc5SMatthew Dillon } 131221864bc5SMatthew Dillon 131321864bc5SMatthew Dillon 131421864bc5SMatthew Dillon static int 131521864bc5SMatthew Dillon devfs_specf_poll(struct file *fp, int events, struct ucred *cred) 131621864bc5SMatthew Dillon { 1317898c91eeSMatthew Dillon struct devfs_node *node; 131821864bc5SMatthew Dillon struct vnode *vp; 131921864bc5SMatthew Dillon int error; 132021864bc5SMatthew Dillon cdev_t dev; 132121864bc5SMatthew Dillon 132221864bc5SMatthew Dillon get_mplock(); 132321864bc5SMatthew Dillon 132421864bc5SMatthew Dillon vp = (struct vnode *)fp->f_data; 132521864bc5SMatthew Dillon if (vp == NULL || vp->v_type == VBAD) { 132621864bc5SMatthew Dillon error = EBADF; 132721864bc5SMatthew Dillon goto done; 132821864bc5SMatthew Dillon } 1329898c91eeSMatthew Dillon node = DEVFS_NODE(vp); 133021864bc5SMatthew Dillon 133121864bc5SMatthew Dillon if ((dev = vp->v_rdev) == NULL) { 133221864bc5SMatthew Dillon error = EBADF; 133321864bc5SMatthew Dillon goto done; 133421864bc5SMatthew Dillon } 133521864bc5SMatthew Dillon reference_dev(dev); 133621864bc5SMatthew Dillon error = dev_dpoll(dev, events); 133721864bc5SMatthew Dillon 133821864bc5SMatthew Dillon release_dev(dev); 133921864bc5SMatthew Dillon 134007dfa375SAlex Hornung #if 0 1341898c91eeSMatthew Dillon if (node) 1342898c91eeSMatthew Dillon nanotime(&node->atime); 134307dfa375SAlex Hornung #endif 134421864bc5SMatthew Dillon done: 134521864bc5SMatthew Dillon rel_mplock(); 134621864bc5SMatthew Dillon return (error); 134721864bc5SMatthew Dillon } 134821864bc5SMatthew Dillon 134921864bc5SMatthew Dillon 135021864bc5SMatthew Dillon /* 135121864bc5SMatthew Dillon * MPALMOSTSAFE - acquires mplock 135221864bc5SMatthew Dillon */ 135321864bc5SMatthew Dillon static int 135487baaf0cSMatthew Dillon devfs_specf_ioctl(struct file *fp, u_long com, caddr_t data, 135587baaf0cSMatthew Dillon struct ucred *ucred, struct sysmsg *msg) 135621864bc5SMatthew Dillon { 1357898c91eeSMatthew Dillon struct devfs_node *node; 1358898c91eeSMatthew Dillon struct vnode *vp; 135921864bc5SMatthew Dillon struct vnode *ovp; 136021864bc5SMatthew Dillon cdev_t dev; 136121864bc5SMatthew Dillon int error; 136221864bc5SMatthew Dillon struct fiodname_args *name_args; 136321864bc5SMatthew Dillon size_t namlen; 136421864bc5SMatthew Dillon const char *name; 136521864bc5SMatthew Dillon 1366898c91eeSMatthew Dillon vp = ((struct vnode *)fp->f_data); 13673a1032a6SAlex Hornung 13683a1032a6SAlex Hornung if ((dev = vp->v_rdev) == NULL) 13693a1032a6SAlex Hornung return EBADF; /* device was revoked */ 13703a1032a6SAlex Hornung 13713a1032a6SAlex Hornung reference_dev(dev); 137221864bc5SMatthew Dillon 1373898c91eeSMatthew Dillon node = DEVFS_NODE(vp); 1374898c91eeSMatthew Dillon 1375898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 1376898c91eeSMatthew Dillon "devfs_specf_ioctl() called! for dev %s\n", 1377898c91eeSMatthew Dillon dev->si_name); 137821864bc5SMatthew Dillon 137921864bc5SMatthew Dillon if (com == FIODTYPE) { 138021864bc5SMatthew Dillon *(int *)data = dev_dflags(dev) & D_TYPEMASK; 138121864bc5SMatthew Dillon error = 0; 138221864bc5SMatthew Dillon goto out; 138321864bc5SMatthew Dillon } else if (com == FIODNAME) { 138421864bc5SMatthew Dillon name_args = (struct fiodname_args *)data; 138521864bc5SMatthew Dillon name = dev->si_name; 138621864bc5SMatthew Dillon namlen = strlen(name) + 1; 138721864bc5SMatthew Dillon 1388898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 1389898c91eeSMatthew Dillon "ioctl, got: FIODNAME for %s\n", name); 139021864bc5SMatthew Dillon 139121864bc5SMatthew Dillon if (namlen <= name_args->len) 139221864bc5SMatthew Dillon error = copyout(dev->si_name, name_args->name, namlen); 139321864bc5SMatthew Dillon else 139421864bc5SMatthew Dillon error = EINVAL; 139521864bc5SMatthew Dillon 1396898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 1397898c91eeSMatthew Dillon "ioctl stuff: error: %d\n", error); 139821864bc5SMatthew Dillon goto out; 139921864bc5SMatthew Dillon } 14003a1032a6SAlex Hornung 14013a1032a6SAlex Hornung /* only acquire mplock for devices that require it */ 14023a1032a6SAlex Hornung if (!(dev_dflags(dev) & D_MPSAFE_IOCTL)) 14033a1032a6SAlex Hornung get_mplock(); 14043a1032a6SAlex Hornung 140587baaf0cSMatthew Dillon error = dev_dioctl(dev, com, data, fp->f_flag, ucred, msg); 14063a1032a6SAlex Hornung 140707dfa375SAlex Hornung #if 0 1408898c91eeSMatthew Dillon if (node) { 1409898c91eeSMatthew Dillon nanotime(&node->atime); 1410898c91eeSMatthew Dillon nanotime(&node->mtime); 141121864bc5SMatthew Dillon } 141207dfa375SAlex Hornung #endif 141321864bc5SMatthew Dillon 14143a1032a6SAlex Hornung if (!(dev_dflags(dev) & D_MPSAFE_IOCTL)) 14153a1032a6SAlex Hornung rel_mplock(); 14163a1032a6SAlex Hornung 1417898c91eeSMatthew Dillon if (com == TIOCSCTTY) { 1418898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 1419898c91eeSMatthew Dillon "devfs_specf_ioctl: got TIOCSCTTY on %s\n", 1420898c91eeSMatthew Dillon dev->si_name); 1421898c91eeSMatthew Dillon } 142221864bc5SMatthew Dillon if (error == 0 && com == TIOCSCTTY) { 142321864bc5SMatthew Dillon struct proc *p = curthread->td_proc; 142421864bc5SMatthew Dillon struct session *sess; 1425898c91eeSMatthew Dillon 1426898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 1427898c91eeSMatthew Dillon "devfs_specf_ioctl: dealing with TIOCSCTTY on %s\n", 1428898c91eeSMatthew Dillon dev->si_name); 142921864bc5SMatthew Dillon if (p == NULL) { 143021864bc5SMatthew Dillon error = ENOTTY; 143121864bc5SMatthew Dillon goto out; 143221864bc5SMatthew Dillon } 143321864bc5SMatthew Dillon sess = p->p_session; 1434898c91eeSMatthew Dillon 1435898c91eeSMatthew Dillon /* 1436898c91eeSMatthew Dillon * Do nothing if reassigning same control tty 1437898c91eeSMatthew Dillon */ 143821864bc5SMatthew Dillon if (sess->s_ttyvp == vp) { 143921864bc5SMatthew Dillon error = 0; 144021864bc5SMatthew Dillon goto out; 144121864bc5SMatthew Dillon } 1442898c91eeSMatthew Dillon 1443898c91eeSMatthew Dillon /* 1444898c91eeSMatthew Dillon * Get rid of reference to old control tty 1445898c91eeSMatthew Dillon */ 144621864bc5SMatthew Dillon ovp = sess->s_ttyvp; 144721864bc5SMatthew Dillon vref(vp); 144821864bc5SMatthew Dillon sess->s_ttyvp = vp; 144921864bc5SMatthew Dillon if (ovp) 145021864bc5SMatthew Dillon vrele(ovp); 145121864bc5SMatthew Dillon } 145221864bc5SMatthew Dillon 145321864bc5SMatthew Dillon out: 14543a1032a6SAlex Hornung release_dev(dev); 145521864bc5SMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, "devfs_specf_ioctl() finished! \n"); 145621864bc5SMatthew Dillon return (error); 145721864bc5SMatthew Dillon } 145821864bc5SMatthew Dillon 145921864bc5SMatthew Dillon 146021864bc5SMatthew Dillon static int 146121864bc5SMatthew Dillon devfs_spec_fsync(struct vop_fsync_args *ap) 146221864bc5SMatthew Dillon { 146321864bc5SMatthew Dillon struct vnode *vp = ap->a_vp; 146421864bc5SMatthew Dillon int error; 146521864bc5SMatthew Dillon 146621864bc5SMatthew Dillon if (!vn_isdisk(vp, NULL)) 146721864bc5SMatthew Dillon return (0); 146821864bc5SMatthew Dillon 146921864bc5SMatthew Dillon /* 147021864bc5SMatthew Dillon * Flush all dirty buffers associated with a block device. 147121864bc5SMatthew Dillon */ 147221864bc5SMatthew Dillon error = vfsync(vp, ap->a_waitfor, 10000, NULL, NULL); 147321864bc5SMatthew Dillon return (error); 147421864bc5SMatthew Dillon } 147521864bc5SMatthew Dillon 147621864bc5SMatthew Dillon static int 147721864bc5SMatthew Dillon devfs_spec_read(struct vop_read_args *ap) 147821864bc5SMatthew Dillon { 1479898c91eeSMatthew Dillon struct devfs_node *node; 148021864bc5SMatthew Dillon struct vnode *vp; 148121864bc5SMatthew Dillon struct uio *uio; 148221864bc5SMatthew Dillon cdev_t dev; 148321864bc5SMatthew Dillon int error; 148421864bc5SMatthew Dillon 148521864bc5SMatthew Dillon vp = ap->a_vp; 148621864bc5SMatthew Dillon dev = vp->v_rdev; 148721864bc5SMatthew Dillon uio = ap->a_uio; 1488898c91eeSMatthew Dillon node = DEVFS_NODE(vp); 148921864bc5SMatthew Dillon 149021864bc5SMatthew Dillon if (dev == NULL) /* device was revoked */ 149121864bc5SMatthew Dillon return (EBADF); 149221864bc5SMatthew Dillon if (uio->uio_resid == 0) 149321864bc5SMatthew Dillon return (0); 149421864bc5SMatthew Dillon 149521864bc5SMatthew Dillon vn_unlock(vp); 149621864bc5SMatthew Dillon error = dev_dread(dev, uio, ap->a_ioflag); 149721864bc5SMatthew Dillon vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 149821864bc5SMatthew Dillon 1499898c91eeSMatthew Dillon if (node) 1500898c91eeSMatthew Dillon nanotime(&node->atime); 150121864bc5SMatthew Dillon 150221864bc5SMatthew Dillon return (error); 150321864bc5SMatthew Dillon } 150421864bc5SMatthew Dillon 150521864bc5SMatthew Dillon /* 150621864bc5SMatthew Dillon * Vnode op for write 150721864bc5SMatthew Dillon * 150821864bc5SMatthew Dillon * spec_write(struct vnode *a_vp, struct uio *a_uio, int a_ioflag, 150921864bc5SMatthew Dillon * struct ucred *a_cred) 151021864bc5SMatthew Dillon */ 151121864bc5SMatthew Dillon static int 151221864bc5SMatthew Dillon devfs_spec_write(struct vop_write_args *ap) 151321864bc5SMatthew Dillon { 1514898c91eeSMatthew Dillon struct devfs_node *node; 151521864bc5SMatthew Dillon struct vnode *vp; 151621864bc5SMatthew Dillon struct uio *uio; 151721864bc5SMatthew Dillon cdev_t dev; 151821864bc5SMatthew Dillon int error; 151921864bc5SMatthew Dillon 152021864bc5SMatthew Dillon vp = ap->a_vp; 152121864bc5SMatthew Dillon dev = vp->v_rdev; 152221864bc5SMatthew Dillon uio = ap->a_uio; 1523898c91eeSMatthew Dillon node = DEVFS_NODE(vp); 152421864bc5SMatthew Dillon 152521864bc5SMatthew Dillon KKASSERT(uio->uio_segflg != UIO_NOCOPY); 152621864bc5SMatthew Dillon 152721864bc5SMatthew Dillon if (dev == NULL) /* device was revoked */ 152821864bc5SMatthew Dillon return (EBADF); 152921864bc5SMatthew Dillon 153021864bc5SMatthew Dillon vn_unlock(vp); 153121864bc5SMatthew Dillon error = dev_dwrite(dev, uio, ap->a_ioflag); 153221864bc5SMatthew Dillon vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 153321864bc5SMatthew Dillon 153407dfa375SAlex Hornung if (node) { 153507dfa375SAlex Hornung nanotime(&node->atime); 1536898c91eeSMatthew Dillon nanotime(&node->mtime); 153707dfa375SAlex Hornung } 153821864bc5SMatthew Dillon 153921864bc5SMatthew Dillon return (error); 154021864bc5SMatthew Dillon } 154121864bc5SMatthew Dillon 154221864bc5SMatthew Dillon /* 154321864bc5SMatthew Dillon * Device ioctl operation. 154421864bc5SMatthew Dillon * 154521864bc5SMatthew Dillon * spec_ioctl(struct vnode *a_vp, int a_command, caddr_t a_data, 154687baaf0cSMatthew Dillon * int a_fflag, struct ucred *a_cred, struct sysmsg *msg) 154721864bc5SMatthew Dillon */ 154821864bc5SMatthew Dillon static int 154921864bc5SMatthew Dillon devfs_spec_ioctl(struct vop_ioctl_args *ap) 155021864bc5SMatthew Dillon { 155121864bc5SMatthew Dillon struct vnode *vp = ap->a_vp; 1552898c91eeSMatthew Dillon struct devfs_node *node; 1553898c91eeSMatthew Dillon cdev_t dev; 155421864bc5SMatthew Dillon 155521864bc5SMatthew Dillon if ((dev = vp->v_rdev) == NULL) 155621864bc5SMatthew Dillon return (EBADF); /* device was revoked */ 1557898c91eeSMatthew Dillon node = DEVFS_NODE(vp); 155821864bc5SMatthew Dillon 155907dfa375SAlex Hornung #if 0 1560898c91eeSMatthew Dillon if (node) { 1561898c91eeSMatthew Dillon nanotime(&node->atime); 1562898c91eeSMatthew Dillon nanotime(&node->mtime); 156321864bc5SMatthew Dillon } 156407dfa375SAlex Hornung #endif 156521864bc5SMatthew Dillon 156687baaf0cSMatthew Dillon return (dev_dioctl(dev, ap->a_command, ap->a_data, ap->a_fflag, 156787baaf0cSMatthew Dillon ap->a_cred, ap->a_sysmsg)); 156821864bc5SMatthew Dillon } 156921864bc5SMatthew Dillon 157021864bc5SMatthew Dillon /* 157121864bc5SMatthew Dillon * spec_poll(struct vnode *a_vp, int a_events, struct ucred *a_cred) 157221864bc5SMatthew Dillon */ 157321864bc5SMatthew Dillon /* ARGSUSED */ 157421864bc5SMatthew Dillon static int 157521864bc5SMatthew Dillon devfs_spec_poll(struct vop_poll_args *ap) 157621864bc5SMatthew Dillon { 157721864bc5SMatthew Dillon struct vnode *vp = ap->a_vp; 1578898c91eeSMatthew Dillon struct devfs_node *node; 1579898c91eeSMatthew Dillon cdev_t dev; 158021864bc5SMatthew Dillon 158121864bc5SMatthew Dillon if ((dev = vp->v_rdev) == NULL) 158221864bc5SMatthew Dillon return (EBADF); /* device was revoked */ 1583898c91eeSMatthew Dillon node = DEVFS_NODE(vp); 158421864bc5SMatthew Dillon 158507dfa375SAlex Hornung #if 0 1586898c91eeSMatthew Dillon if (node) 1587898c91eeSMatthew Dillon nanotime(&node->atime); 158807dfa375SAlex Hornung #endif 158921864bc5SMatthew Dillon 159021864bc5SMatthew Dillon return (dev_dpoll(dev, ap->a_events)); 159121864bc5SMatthew Dillon } 159221864bc5SMatthew Dillon 159321864bc5SMatthew Dillon /* 159421864bc5SMatthew Dillon * spec_kqfilter(struct vnode *a_vp, struct knote *a_kn) 159521864bc5SMatthew Dillon */ 159621864bc5SMatthew Dillon /* ARGSUSED */ 159721864bc5SMatthew Dillon static int 159821864bc5SMatthew Dillon devfs_spec_kqfilter(struct vop_kqfilter_args *ap) 159921864bc5SMatthew Dillon { 160021864bc5SMatthew Dillon struct vnode *vp = ap->a_vp; 1601898c91eeSMatthew Dillon struct devfs_node *node; 1602898c91eeSMatthew Dillon cdev_t dev; 160321864bc5SMatthew Dillon 160421864bc5SMatthew Dillon if ((dev = vp->v_rdev) == NULL) 160521864bc5SMatthew Dillon return (EBADF); /* device was revoked */ 1606898c91eeSMatthew Dillon node = DEVFS_NODE(vp); 160721864bc5SMatthew Dillon 160807dfa375SAlex Hornung #if 0 1609898c91eeSMatthew Dillon if (node) 1610898c91eeSMatthew Dillon nanotime(&node->atime); 161107dfa375SAlex Hornung #endif 161221864bc5SMatthew Dillon 161321864bc5SMatthew Dillon return (dev_dkqfilter(dev, ap->a_kn)); 161421864bc5SMatthew Dillon } 161521864bc5SMatthew Dillon 161621864bc5SMatthew Dillon /* 161721864bc5SMatthew Dillon * Convert a vnode strategy call into a device strategy call. Vnode strategy 161821864bc5SMatthew Dillon * calls are not limited to device DMA limits so we have to deal with the 161921864bc5SMatthew Dillon * case. 162021864bc5SMatthew Dillon * 162121864bc5SMatthew Dillon * spec_strategy(struct vnode *a_vp, struct bio *a_bio) 162221864bc5SMatthew Dillon */ 162321864bc5SMatthew Dillon static int 162421864bc5SMatthew Dillon devfs_spec_strategy(struct vop_strategy_args *ap) 162521864bc5SMatthew Dillon { 162621864bc5SMatthew Dillon struct bio *bio = ap->a_bio; 162721864bc5SMatthew Dillon struct buf *bp = bio->bio_buf; 162821864bc5SMatthew Dillon struct buf *nbp; 162921864bc5SMatthew Dillon struct vnode *vp; 163021864bc5SMatthew Dillon struct mount *mp; 163121864bc5SMatthew Dillon int chunksize; 163221864bc5SMatthew Dillon int maxiosize; 163321864bc5SMatthew Dillon 163421864bc5SMatthew Dillon if (bp->b_cmd != BUF_CMD_READ && LIST_FIRST(&bp->b_dep) != NULL) 163521864bc5SMatthew Dillon buf_start(bp); 163621864bc5SMatthew Dillon 163721864bc5SMatthew Dillon /* 163821864bc5SMatthew Dillon * Collect statistics on synchronous and asynchronous read 163921864bc5SMatthew Dillon * and write counts for disks that have associated filesystems. 164021864bc5SMatthew Dillon */ 164121864bc5SMatthew Dillon vp = ap->a_vp; 164221864bc5SMatthew Dillon KKASSERT(vp->v_rdev != NULL); /* XXX */ 164321864bc5SMatthew Dillon if (vn_isdisk(vp, NULL) && (mp = vp->v_rdev->si_mountpoint) != NULL) { 164421864bc5SMatthew Dillon if (bp->b_cmd == BUF_CMD_READ) { 164521864bc5SMatthew Dillon if (bp->b_flags & BIO_SYNC) 164621864bc5SMatthew Dillon mp->mnt_stat.f_syncreads++; 164721864bc5SMatthew Dillon else 164821864bc5SMatthew Dillon mp->mnt_stat.f_asyncreads++; 164921864bc5SMatthew Dillon } else { 165021864bc5SMatthew Dillon if (bp->b_flags & BIO_SYNC) 165121864bc5SMatthew Dillon mp->mnt_stat.f_syncwrites++; 165221864bc5SMatthew Dillon else 165321864bc5SMatthew Dillon mp->mnt_stat.f_asyncwrites++; 165421864bc5SMatthew Dillon } 165521864bc5SMatthew Dillon } 165621864bc5SMatthew Dillon 165721864bc5SMatthew Dillon /* 165821864bc5SMatthew Dillon * Device iosize limitations only apply to read and write. Shortcut 165921864bc5SMatthew Dillon * the I/O if it fits. 166021864bc5SMatthew Dillon */ 166121864bc5SMatthew Dillon if ((maxiosize = vp->v_rdev->si_iosize_max) == 0) { 1662898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 1663898c91eeSMatthew Dillon "%s: si_iosize_max not set!\n", 1664898c91eeSMatthew Dillon dev_dname(vp->v_rdev)); 166521864bc5SMatthew Dillon maxiosize = MAXPHYS; 166621864bc5SMatthew Dillon } 166721864bc5SMatthew Dillon #if SPEC_CHAIN_DEBUG & 2 166821864bc5SMatthew Dillon maxiosize = 4096; 166921864bc5SMatthew Dillon #endif 167021864bc5SMatthew Dillon if (bp->b_bcount <= maxiosize || 167121864bc5SMatthew Dillon (bp->b_cmd != BUF_CMD_READ && bp->b_cmd != BUF_CMD_WRITE)) { 167221864bc5SMatthew Dillon dev_dstrategy_chain(vp->v_rdev, bio); 167321864bc5SMatthew Dillon return (0); 167421864bc5SMatthew Dillon } 167521864bc5SMatthew Dillon 167621864bc5SMatthew Dillon /* 167721864bc5SMatthew Dillon * Clone the buffer and set up an I/O chain to chunk up the I/O. 167821864bc5SMatthew Dillon */ 167921864bc5SMatthew Dillon nbp = kmalloc(sizeof(*bp), M_DEVBUF, M_INTWAIT|M_ZERO); 168021864bc5SMatthew Dillon initbufbio(nbp); 168121864bc5SMatthew Dillon buf_dep_init(nbp); 168221864bc5SMatthew Dillon BUF_LOCKINIT(nbp); 168321864bc5SMatthew Dillon BUF_LOCK(nbp, LK_EXCLUSIVE); 168421864bc5SMatthew Dillon BUF_KERNPROC(nbp); 168521864bc5SMatthew Dillon nbp->b_vp = vp; 168621864bc5SMatthew Dillon nbp->b_flags = B_PAGING | (bp->b_flags & B_BNOCLIP); 168721864bc5SMatthew Dillon nbp->b_data = bp->b_data; 168821864bc5SMatthew Dillon nbp->b_bio1.bio_done = devfs_spec_strategy_done; 168921864bc5SMatthew Dillon nbp->b_bio1.bio_offset = bio->bio_offset; 169021864bc5SMatthew Dillon nbp->b_bio1.bio_caller_info1.ptr = bio; 169121864bc5SMatthew Dillon 169221864bc5SMatthew Dillon /* 169321864bc5SMatthew Dillon * Start the first transfer 169421864bc5SMatthew Dillon */ 169521864bc5SMatthew Dillon if (vn_isdisk(vp, NULL)) 169621864bc5SMatthew Dillon chunksize = vp->v_rdev->si_bsize_phys; 169721864bc5SMatthew Dillon else 169821864bc5SMatthew Dillon chunksize = DEV_BSIZE; 169921864bc5SMatthew Dillon chunksize = maxiosize / chunksize * chunksize; 170021864bc5SMatthew Dillon #if SPEC_CHAIN_DEBUG & 1 1701898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 1702898c91eeSMatthew Dillon "spec_strategy chained I/O chunksize=%d\n", 1703898c91eeSMatthew Dillon chunksize); 170421864bc5SMatthew Dillon #endif 170521864bc5SMatthew Dillon nbp->b_cmd = bp->b_cmd; 170621864bc5SMatthew Dillon nbp->b_bcount = chunksize; 170721864bc5SMatthew Dillon nbp->b_bufsize = chunksize; /* used to detect a short I/O */ 170821864bc5SMatthew Dillon nbp->b_bio1.bio_caller_info2.index = chunksize; 170921864bc5SMatthew Dillon 171021864bc5SMatthew Dillon #if SPEC_CHAIN_DEBUG & 1 1711898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 1712898c91eeSMatthew Dillon "spec_strategy: chain %p offset %d/%d bcount %d\n", 171321864bc5SMatthew Dillon bp, 0, bp->b_bcount, nbp->b_bcount); 171421864bc5SMatthew Dillon #endif 171521864bc5SMatthew Dillon 171621864bc5SMatthew Dillon dev_dstrategy(vp->v_rdev, &nbp->b_bio1); 171721864bc5SMatthew Dillon 171821864bc5SMatthew Dillon if (DEVFS_NODE(vp)) { 171921864bc5SMatthew Dillon nanotime(&DEVFS_NODE(vp)->atime); 172021864bc5SMatthew Dillon nanotime(&DEVFS_NODE(vp)->mtime); 172121864bc5SMatthew Dillon } 172221864bc5SMatthew Dillon 172321864bc5SMatthew Dillon return (0); 172421864bc5SMatthew Dillon } 172521864bc5SMatthew Dillon 172621864bc5SMatthew Dillon /* 172721864bc5SMatthew Dillon * Chunked up transfer completion routine - chain transfers until done 172821864bc5SMatthew Dillon */ 172921864bc5SMatthew Dillon static 173021864bc5SMatthew Dillon void 173121864bc5SMatthew Dillon devfs_spec_strategy_done(struct bio *nbio) 173221864bc5SMatthew Dillon { 173321864bc5SMatthew Dillon struct buf *nbp = nbio->bio_buf; 173421864bc5SMatthew Dillon struct bio *bio = nbio->bio_caller_info1.ptr; /* original bio */ 173521864bc5SMatthew Dillon struct buf *bp = bio->bio_buf; /* original bp */ 173621864bc5SMatthew Dillon int chunksize = nbio->bio_caller_info2.index; /* chunking */ 173721864bc5SMatthew Dillon int boffset = nbp->b_data - bp->b_data; 173821864bc5SMatthew Dillon 173921864bc5SMatthew Dillon if (nbp->b_flags & B_ERROR) { 174021864bc5SMatthew Dillon /* 174121864bc5SMatthew Dillon * An error terminates the chain, propogate the error back 174221864bc5SMatthew Dillon * to the original bp 174321864bc5SMatthew Dillon */ 174421864bc5SMatthew Dillon bp->b_flags |= B_ERROR; 174521864bc5SMatthew Dillon bp->b_error = nbp->b_error; 174621864bc5SMatthew Dillon bp->b_resid = bp->b_bcount - boffset + 174721864bc5SMatthew Dillon (nbp->b_bcount - nbp->b_resid); 174821864bc5SMatthew Dillon #if SPEC_CHAIN_DEBUG & 1 1749898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 1750898c91eeSMatthew Dillon "spec_strategy: chain %p error %d bcount %d/%d\n", 175121864bc5SMatthew Dillon bp, bp->b_error, bp->b_bcount, 175221864bc5SMatthew Dillon bp->b_bcount - bp->b_resid); 175321864bc5SMatthew Dillon #endif 175421864bc5SMatthew Dillon kfree(nbp, M_DEVBUF); 175521864bc5SMatthew Dillon biodone(bio); 175621864bc5SMatthew Dillon } else if (nbp->b_resid) { 175721864bc5SMatthew Dillon /* 175821864bc5SMatthew Dillon * A short read or write terminates the chain 175921864bc5SMatthew Dillon */ 176021864bc5SMatthew Dillon bp->b_error = nbp->b_error; 176121864bc5SMatthew Dillon bp->b_resid = bp->b_bcount - boffset + 176221864bc5SMatthew Dillon (nbp->b_bcount - nbp->b_resid); 176321864bc5SMatthew Dillon #if SPEC_CHAIN_DEBUG & 1 1764898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 1765898c91eeSMatthew Dillon "spec_strategy: chain %p short read(1) " 1766898c91eeSMatthew Dillon "bcount %d/%d\n", 176721864bc5SMatthew Dillon bp, bp->b_bcount - bp->b_resid, bp->b_bcount); 176821864bc5SMatthew Dillon #endif 176921864bc5SMatthew Dillon kfree(nbp, M_DEVBUF); 177021864bc5SMatthew Dillon biodone(bio); 177121864bc5SMatthew Dillon } else if (nbp->b_bcount != nbp->b_bufsize) { 177221864bc5SMatthew Dillon /* 177321864bc5SMatthew Dillon * A short read or write can also occur by truncating b_bcount 177421864bc5SMatthew Dillon */ 177521864bc5SMatthew Dillon #if SPEC_CHAIN_DEBUG & 1 1776898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 1777898c91eeSMatthew Dillon "spec_strategy: chain %p short read(2) " 1778898c91eeSMatthew Dillon "bcount %d/%d\n", 177921864bc5SMatthew Dillon bp, nbp->b_bcount + boffset, bp->b_bcount); 178021864bc5SMatthew Dillon #endif 178121864bc5SMatthew Dillon bp->b_error = 0; 178221864bc5SMatthew Dillon bp->b_bcount = nbp->b_bcount + boffset; 178321864bc5SMatthew Dillon bp->b_resid = nbp->b_resid; 178421864bc5SMatthew Dillon kfree(nbp, M_DEVBUF); 178521864bc5SMatthew Dillon biodone(bio); 178621864bc5SMatthew Dillon } else if (nbp->b_bcount + boffset == bp->b_bcount) { 178721864bc5SMatthew Dillon /* 178821864bc5SMatthew Dillon * No more data terminates the chain 178921864bc5SMatthew Dillon */ 179021864bc5SMatthew Dillon #if SPEC_CHAIN_DEBUG & 1 1791898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 1792898c91eeSMatthew Dillon "spec_strategy: chain %p finished bcount %d\n", 179321864bc5SMatthew Dillon bp, bp->b_bcount); 179421864bc5SMatthew Dillon #endif 179521864bc5SMatthew Dillon bp->b_error = 0; 179621864bc5SMatthew Dillon bp->b_resid = 0; 179721864bc5SMatthew Dillon kfree(nbp, M_DEVBUF); 179821864bc5SMatthew Dillon biodone(bio); 179921864bc5SMatthew Dillon } else { 180021864bc5SMatthew Dillon /* 180121864bc5SMatthew Dillon * Continue the chain 180221864bc5SMatthew Dillon */ 180321864bc5SMatthew Dillon boffset += nbp->b_bcount; 180421864bc5SMatthew Dillon nbp->b_data = bp->b_data + boffset; 180521864bc5SMatthew Dillon nbp->b_bcount = bp->b_bcount - boffset; 180621864bc5SMatthew Dillon if (nbp->b_bcount > chunksize) 180721864bc5SMatthew Dillon nbp->b_bcount = chunksize; 180821864bc5SMatthew Dillon nbp->b_bio1.bio_done = devfs_spec_strategy_done; 180921864bc5SMatthew Dillon nbp->b_bio1.bio_offset = bio->bio_offset + boffset; 181021864bc5SMatthew Dillon 181121864bc5SMatthew Dillon #if SPEC_CHAIN_DEBUG & 1 1812898c91eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, 1813898c91eeSMatthew Dillon "spec_strategy: chain %p offset %d/%d bcount %d\n", 181421864bc5SMatthew Dillon bp, boffset, bp->b_bcount, nbp->b_bcount); 181521864bc5SMatthew Dillon #endif 181621864bc5SMatthew Dillon 181721864bc5SMatthew Dillon dev_dstrategy(nbp->b_vp->v_rdev, &nbp->b_bio1); 181821864bc5SMatthew Dillon } 181921864bc5SMatthew Dillon } 182021864bc5SMatthew Dillon 182121864bc5SMatthew Dillon /* 182221864bc5SMatthew Dillon * spec_freeblks(struct vnode *a_vp, daddr_t a_addr, daddr_t a_length) 182321864bc5SMatthew Dillon */ 182421864bc5SMatthew Dillon static int 182521864bc5SMatthew Dillon devfs_spec_freeblks(struct vop_freeblks_args *ap) 182621864bc5SMatthew Dillon { 182721864bc5SMatthew Dillon struct buf *bp; 182821864bc5SMatthew Dillon 182921864bc5SMatthew Dillon /* 183021864bc5SMatthew Dillon * XXX: This assumes that strategy does the deed right away. 183121864bc5SMatthew Dillon * XXX: this may not be TRTTD. 183221864bc5SMatthew Dillon */ 183321864bc5SMatthew Dillon KKASSERT(ap->a_vp->v_rdev != NULL); 183421864bc5SMatthew Dillon if ((dev_dflags(ap->a_vp->v_rdev) & D_CANFREE) == 0) 183521864bc5SMatthew Dillon return (0); 183621864bc5SMatthew Dillon bp = geteblk(ap->a_length); 183721864bc5SMatthew Dillon bp->b_cmd = BUF_CMD_FREEBLKS; 183821864bc5SMatthew Dillon bp->b_bio1.bio_offset = ap->a_offset; 183921864bc5SMatthew Dillon bp->b_bcount = ap->a_length; 184021864bc5SMatthew Dillon dev_dstrategy(ap->a_vp->v_rdev, &bp->b_bio1); 184121864bc5SMatthew Dillon return (0); 184221864bc5SMatthew Dillon } 184321864bc5SMatthew Dillon 184421864bc5SMatthew Dillon /* 184521864bc5SMatthew Dillon * Implement degenerate case where the block requested is the block 184621864bc5SMatthew Dillon * returned, and assume that the entire device is contiguous in regards 184721864bc5SMatthew Dillon * to the contiguous block range (runp and runb). 184821864bc5SMatthew Dillon * 184921864bc5SMatthew Dillon * spec_bmap(struct vnode *a_vp, off_t a_loffset, 185021864bc5SMatthew Dillon * off_t *a_doffsetp, int *a_runp, int *a_runb) 185121864bc5SMatthew Dillon */ 185221864bc5SMatthew Dillon static int 185321864bc5SMatthew Dillon devfs_spec_bmap(struct vop_bmap_args *ap) 185421864bc5SMatthew Dillon { 185521864bc5SMatthew Dillon if (ap->a_doffsetp != NULL) 185621864bc5SMatthew Dillon *ap->a_doffsetp = ap->a_loffset; 185721864bc5SMatthew Dillon if (ap->a_runp != NULL) 185821864bc5SMatthew Dillon *ap->a_runp = MAXBSIZE; 185921864bc5SMatthew Dillon if (ap->a_runb != NULL) { 186021864bc5SMatthew Dillon if (ap->a_loffset < MAXBSIZE) 186121864bc5SMatthew Dillon *ap->a_runb = (int)ap->a_loffset; 186221864bc5SMatthew Dillon else 186321864bc5SMatthew Dillon *ap->a_runb = MAXBSIZE; 186421864bc5SMatthew Dillon } 186521864bc5SMatthew Dillon return (0); 186621864bc5SMatthew Dillon } 186721864bc5SMatthew Dillon 186821864bc5SMatthew Dillon 186921864bc5SMatthew Dillon /* 187021864bc5SMatthew Dillon * Special device advisory byte-level locks. 187121864bc5SMatthew Dillon * 187221864bc5SMatthew Dillon * spec_advlock(struct vnode *a_vp, caddr_t a_id, int a_op, 187321864bc5SMatthew Dillon * struct flock *a_fl, int a_flags) 187421864bc5SMatthew Dillon */ 187521864bc5SMatthew Dillon /* ARGSUSED */ 187621864bc5SMatthew Dillon static int 187721864bc5SMatthew Dillon devfs_spec_advlock(struct vop_advlock_args *ap) 187821864bc5SMatthew Dillon { 187921864bc5SMatthew Dillon return ((ap->a_flags & F_POSIX) ? EINVAL : EOPNOTSUPP); 188021864bc5SMatthew Dillon } 188121864bc5SMatthew Dillon 188221864bc5SMatthew Dillon static void 188321864bc5SMatthew Dillon devfs_spec_getpages_iodone(struct bio *bio) 188421864bc5SMatthew Dillon { 188521864bc5SMatthew Dillon bio->bio_buf->b_cmd = BUF_CMD_DONE; 188621864bc5SMatthew Dillon wakeup(bio->bio_buf); 188721864bc5SMatthew Dillon } 188821864bc5SMatthew Dillon 188921864bc5SMatthew Dillon /* 189021864bc5SMatthew Dillon * spec_getpages() - get pages associated with device vnode. 189121864bc5SMatthew Dillon * 189221864bc5SMatthew Dillon * Note that spec_read and spec_write do not use the buffer cache, so we 189321864bc5SMatthew Dillon * must fully implement getpages here. 189421864bc5SMatthew Dillon */ 189521864bc5SMatthew Dillon static int 189621864bc5SMatthew Dillon devfs_spec_getpages(struct vop_getpages_args *ap) 189721864bc5SMatthew Dillon { 189821864bc5SMatthew Dillon vm_offset_t kva; 189921864bc5SMatthew Dillon int error; 190021864bc5SMatthew Dillon int i, pcount, size; 190121864bc5SMatthew Dillon struct buf *bp; 190221864bc5SMatthew Dillon vm_page_t m; 190321864bc5SMatthew Dillon vm_ooffset_t offset; 190421864bc5SMatthew Dillon int toff, nextoff, nread; 190521864bc5SMatthew Dillon struct vnode *vp = ap->a_vp; 190621864bc5SMatthew Dillon int blksiz; 190721864bc5SMatthew Dillon int gotreqpage; 190821864bc5SMatthew Dillon 190921864bc5SMatthew Dillon error = 0; 191021864bc5SMatthew Dillon pcount = round_page(ap->a_count) / PAGE_SIZE; 191121864bc5SMatthew Dillon 191221864bc5SMatthew Dillon /* 191321864bc5SMatthew Dillon * Calculate the offset of the transfer and do sanity check. 191421864bc5SMatthew Dillon */ 191521864bc5SMatthew Dillon offset = IDX_TO_OFF(ap->a_m[0]->pindex) + ap->a_offset; 191621864bc5SMatthew Dillon 191721864bc5SMatthew Dillon /* 191821864bc5SMatthew Dillon * Round up physical size for real devices. We cannot round using 191921864bc5SMatthew Dillon * v_mount's block size data because v_mount has nothing to do with 192021864bc5SMatthew Dillon * the device. i.e. it's usually '/dev'. We need the physical block 192121864bc5SMatthew Dillon * size for the device itself. 192221864bc5SMatthew Dillon * 192321864bc5SMatthew Dillon * We can't use v_rdev->si_mountpoint because it only exists when the 192421864bc5SMatthew Dillon * block device is mounted. However, we can use v_rdev. 192521864bc5SMatthew Dillon */ 192621864bc5SMatthew Dillon if (vn_isdisk(vp, NULL)) 192721864bc5SMatthew Dillon blksiz = vp->v_rdev->si_bsize_phys; 192821864bc5SMatthew Dillon else 192921864bc5SMatthew Dillon blksiz = DEV_BSIZE; 193021864bc5SMatthew Dillon 193121864bc5SMatthew Dillon size = (ap->a_count + blksiz - 1) & ~(blksiz - 1); 193221864bc5SMatthew Dillon 193321864bc5SMatthew Dillon bp = getpbuf(NULL); 193421864bc5SMatthew Dillon kva = (vm_offset_t)bp->b_data; 193521864bc5SMatthew Dillon 193621864bc5SMatthew Dillon /* 193721864bc5SMatthew Dillon * Map the pages to be read into the kva. 193821864bc5SMatthew Dillon */ 193921864bc5SMatthew Dillon pmap_qenter(kva, ap->a_m, pcount); 194021864bc5SMatthew Dillon 194121864bc5SMatthew Dillon /* Build a minimal buffer header. */ 194221864bc5SMatthew Dillon bp->b_cmd = BUF_CMD_READ; 194321864bc5SMatthew Dillon bp->b_bcount = size; 194421864bc5SMatthew Dillon bp->b_resid = 0; 194521864bc5SMatthew Dillon bp->b_runningbufspace = size; 194621864bc5SMatthew Dillon if (size) { 194721864bc5SMatthew Dillon runningbufspace += bp->b_runningbufspace; 194821864bc5SMatthew Dillon ++runningbufcount; 194921864bc5SMatthew Dillon } 195021864bc5SMatthew Dillon 195121864bc5SMatthew Dillon bp->b_bio1.bio_offset = offset; 195221864bc5SMatthew Dillon bp->b_bio1.bio_done = devfs_spec_getpages_iodone; 195321864bc5SMatthew Dillon 195421864bc5SMatthew Dillon mycpu->gd_cnt.v_vnodein++; 195521864bc5SMatthew Dillon mycpu->gd_cnt.v_vnodepgsin += pcount; 195621864bc5SMatthew Dillon 195721864bc5SMatthew Dillon /* Do the input. */ 195821864bc5SMatthew Dillon vn_strategy(ap->a_vp, &bp->b_bio1); 195921864bc5SMatthew Dillon 196021864bc5SMatthew Dillon crit_enter(); 196121864bc5SMatthew Dillon 196221864bc5SMatthew Dillon /* We definitely need to be at splbio here. */ 196321864bc5SMatthew Dillon while (bp->b_cmd != BUF_CMD_DONE) 196421864bc5SMatthew Dillon tsleep(bp, 0, "spread", 0); 196521864bc5SMatthew Dillon 196621864bc5SMatthew Dillon crit_exit(); 196721864bc5SMatthew Dillon 196821864bc5SMatthew Dillon if (bp->b_flags & B_ERROR) { 196921864bc5SMatthew Dillon if (bp->b_error) 197021864bc5SMatthew Dillon error = bp->b_error; 197121864bc5SMatthew Dillon else 197221864bc5SMatthew Dillon error = EIO; 197321864bc5SMatthew Dillon } 197421864bc5SMatthew Dillon 197521864bc5SMatthew Dillon /* 197621864bc5SMatthew Dillon * If EOF is encountered we must zero-extend the result in order 197721864bc5SMatthew Dillon * to ensure that the page does not contain garabge. When no 197821864bc5SMatthew Dillon * error occurs, an early EOF is indicated if b_bcount got truncated. 197921864bc5SMatthew Dillon * b_resid is relative to b_bcount and should be 0, but some devices 198021864bc5SMatthew Dillon * might indicate an EOF with b_resid instead of truncating b_bcount. 198121864bc5SMatthew Dillon */ 198221864bc5SMatthew Dillon nread = bp->b_bcount - bp->b_resid; 198321864bc5SMatthew Dillon if (nread < ap->a_count) 198421864bc5SMatthew Dillon bzero((caddr_t)kva + nread, ap->a_count - nread); 198521864bc5SMatthew Dillon pmap_qremove(kva, pcount); 198621864bc5SMatthew Dillon 198721864bc5SMatthew Dillon gotreqpage = 0; 198821864bc5SMatthew Dillon for (i = 0, toff = 0; i < pcount; i++, toff = nextoff) { 198921864bc5SMatthew Dillon nextoff = toff + PAGE_SIZE; 199021864bc5SMatthew Dillon m = ap->a_m[i]; 199121864bc5SMatthew Dillon 199221864bc5SMatthew Dillon m->flags &= ~PG_ZERO; 199321864bc5SMatthew Dillon 1994cb1cf930SMatthew Dillon /* 1995cb1cf930SMatthew Dillon * NOTE: vm_page_undirty/clear_dirty etc do not clear the 1996cb1cf930SMatthew Dillon * pmap modified bit. pmap modified bit should have 1997cb1cf930SMatthew Dillon * already been cleared. 1998cb1cf930SMatthew Dillon */ 199921864bc5SMatthew Dillon if (nextoff <= nread) { 200021864bc5SMatthew Dillon m->valid = VM_PAGE_BITS_ALL; 200121864bc5SMatthew Dillon vm_page_undirty(m); 200221864bc5SMatthew Dillon } else if (toff < nread) { 200321864bc5SMatthew Dillon /* 200421864bc5SMatthew Dillon * Since this is a VM request, we have to supply the 2005cb1cf930SMatthew Dillon * unaligned offset to allow vm_page_set_valid() 200621864bc5SMatthew Dillon * to zero sub-DEV_BSIZE'd portions of the page. 200721864bc5SMatthew Dillon */ 20081a54183bSMatthew Dillon vm_page_set_valid(m, 0, nread - toff); 20091a54183bSMatthew Dillon vm_page_clear_dirty_end_nonincl(m, 0, nread - toff); 201021864bc5SMatthew Dillon } else { 201121864bc5SMatthew Dillon m->valid = 0; 201221864bc5SMatthew Dillon vm_page_undirty(m); 201321864bc5SMatthew Dillon } 201421864bc5SMatthew Dillon 201521864bc5SMatthew Dillon if (i != ap->a_reqpage) { 201621864bc5SMatthew Dillon /* 201721864bc5SMatthew Dillon * Just in case someone was asking for this page we 201821864bc5SMatthew Dillon * now tell them that it is ok to use. 201921864bc5SMatthew Dillon */ 202021864bc5SMatthew Dillon if (!error || (m->valid == VM_PAGE_BITS_ALL)) { 202121864bc5SMatthew Dillon if (m->valid) { 202221864bc5SMatthew Dillon if (m->flags & PG_WANTED) { 202321864bc5SMatthew Dillon vm_page_activate(m); 202421864bc5SMatthew Dillon } else { 202521864bc5SMatthew Dillon vm_page_deactivate(m); 202621864bc5SMatthew Dillon } 202721864bc5SMatthew Dillon vm_page_wakeup(m); 202821864bc5SMatthew Dillon } else { 202921864bc5SMatthew Dillon vm_page_free(m); 203021864bc5SMatthew Dillon } 203121864bc5SMatthew Dillon } else { 203221864bc5SMatthew Dillon vm_page_free(m); 203321864bc5SMatthew Dillon } 203421864bc5SMatthew Dillon } else if (m->valid) { 203521864bc5SMatthew Dillon gotreqpage = 1; 203621864bc5SMatthew Dillon /* 203721864bc5SMatthew Dillon * Since this is a VM request, we need to make the 203821864bc5SMatthew Dillon * entire page presentable by zeroing invalid sections. 203921864bc5SMatthew Dillon */ 204021864bc5SMatthew Dillon if (m->valid != VM_PAGE_BITS_ALL) 204121864bc5SMatthew Dillon vm_page_zero_invalid(m, FALSE); 204221864bc5SMatthew Dillon } 204321864bc5SMatthew Dillon } 204421864bc5SMatthew Dillon if (!gotreqpage) { 204521864bc5SMatthew Dillon m = ap->a_m[ap->a_reqpage]; 204621864bc5SMatthew Dillon devfs_debug(DEVFS_DEBUG_WARNING, 204721864bc5SMatthew Dillon "spec_getpages:(%s) I/O read failure: (error=%d) bp %p vp %p\n", 204821864bc5SMatthew Dillon devtoname(vp->v_rdev), error, bp, bp->b_vp); 204921864bc5SMatthew Dillon devfs_debug(DEVFS_DEBUG_WARNING, 205021864bc5SMatthew Dillon " size: %d, resid: %d, a_count: %d, valid: 0x%x\n", 205121864bc5SMatthew Dillon size, bp->b_resid, ap->a_count, m->valid); 205221864bc5SMatthew Dillon devfs_debug(DEVFS_DEBUG_WARNING, 205321864bc5SMatthew Dillon " nread: %d, reqpage: %d, pindex: %lu, pcount: %d\n", 205421864bc5SMatthew Dillon nread, ap->a_reqpage, (u_long)m->pindex, pcount); 205521864bc5SMatthew Dillon /* 205621864bc5SMatthew Dillon * Free the buffer header back to the swap buffer pool. 205721864bc5SMatthew Dillon */ 205821864bc5SMatthew Dillon relpbuf(bp, NULL); 205921864bc5SMatthew Dillon return VM_PAGER_ERROR; 206021864bc5SMatthew Dillon } 206121864bc5SMatthew Dillon /* 206221864bc5SMatthew Dillon * Free the buffer header back to the swap buffer pool. 206321864bc5SMatthew Dillon */ 206421864bc5SMatthew Dillon relpbuf(bp, NULL); 206507dfa375SAlex Hornung if (DEVFS_NODE(ap->a_vp)) 206607dfa375SAlex Hornung nanotime(&DEVFS_NODE(ap->a_vp)->mtime); 206721864bc5SMatthew Dillon return VM_PAGER_OK; 206821864bc5SMatthew Dillon } 206921864bc5SMatthew Dillon 207021864bc5SMatthew Dillon static __inline 207121864bc5SMatthew Dillon int 207221864bc5SMatthew Dillon sequential_heuristic(struct uio *uio, struct file *fp) 207321864bc5SMatthew Dillon { 207421864bc5SMatthew Dillon /* 207521864bc5SMatthew Dillon * Sequential heuristic - detect sequential operation 207621864bc5SMatthew Dillon */ 207721864bc5SMatthew Dillon if ((uio->uio_offset == 0 && fp->f_seqcount > 0) || 207821864bc5SMatthew Dillon uio->uio_offset == fp->f_nextoff) { 207921864bc5SMatthew Dillon /* 208021864bc5SMatthew Dillon * XXX we assume that the filesystem block size is 208121864bc5SMatthew Dillon * the default. Not true, but still gives us a pretty 208221864bc5SMatthew Dillon * good indicator of how sequential the read operations 208321864bc5SMatthew Dillon * are. 208421864bc5SMatthew Dillon */ 2085898c91eeSMatthew Dillon int tmpseq = fp->f_seqcount; 2086898c91eeSMatthew Dillon 208721864bc5SMatthew Dillon tmpseq += (uio->uio_resid + BKVASIZE - 1) / BKVASIZE; 208821864bc5SMatthew Dillon if (tmpseq > IO_SEQMAX) 208921864bc5SMatthew Dillon tmpseq = IO_SEQMAX; 209021864bc5SMatthew Dillon fp->f_seqcount = tmpseq; 209121864bc5SMatthew Dillon return(fp->f_seqcount << IO_SEQSHIFT); 209221864bc5SMatthew Dillon } 209321864bc5SMatthew Dillon 209421864bc5SMatthew Dillon /* 209521864bc5SMatthew Dillon * Not sequential, quick draw-down of seqcount 209621864bc5SMatthew Dillon */ 209721864bc5SMatthew Dillon if (fp->f_seqcount > 1) 209821864bc5SMatthew Dillon fp->f_seqcount = 1; 209921864bc5SMatthew Dillon else 210021864bc5SMatthew Dillon fp->f_seqcount = 0; 210121864bc5SMatthew Dillon return(0); 210221864bc5SMatthew Dillon } 21033a1032a6SAlex Hornung 21043a1032a6SAlex Hornung extern SYSCTL_NODE(_vfs, OID_AUTO, devfs, CTLFLAG_RW, 0, "devfs"); 21053a1032a6SAlex Hornung 21063a1032a6SAlex Hornung SYSCTL_INT(_vfs_devfs, OID_AUTO, mpsafe_writes, CTLFLAG_RD, &mpsafe_writes, 21073a1032a6SAlex Hornung 0, "mpsafe writes"); 21083a1032a6SAlex Hornung SYSCTL_INT(_vfs_devfs, OID_AUTO, mplock_writes, CTLFLAG_RD, &mplock_writes, 21093a1032a6SAlex Hornung 0, "non-mpsafe writes"); 21103a1032a6SAlex Hornung SYSCTL_INT(_vfs_devfs, OID_AUTO, mpsafe_reads, CTLFLAG_RD, &mpsafe_reads, 21113a1032a6SAlex Hornung 0, "mpsafe reads"); 21123a1032a6SAlex Hornung SYSCTL_INT(_vfs_devfs, OID_AUTO, mplock_reads, CTLFLAG_RD, &mplock_reads, 21133a1032a6SAlex Hornung 0, "non-mpsafe reads"); 2114