xref: /dragonfly/sys/vfs/devfs/devfs_vnops.c (revision 3a1032a6)
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