From ff7c5a4880a6505d329fe6d45d405d6945a53003 Mon Sep 17 00:00:00 2001 From: Poul-Henning Kamp Date: Fri, 22 Oct 2004 09:59:37 +0000 Subject: [PATCH] Alas, poor SPECFS! -- I knew him, Horatio; A filesystem of infinite jest, of most excellent fancy: he hath taught me lessons a thousand times; and now, how abhorred in my imagination it is! my gorge rises at it. Here were those hacks that I have curs'd I know not how oft. Where be your kludges now? your workarounds? your layering violations, that were wont to set the table on a roar? Move the skeleton of specfs into devfs where it now belongs and bury the rest. --- sys/conf/files | 1 - sys/fs/devfs/devfs_vnops.c | 577 +++++++++++++++++++++++++++++++++-- sys/fs/specfs/spec_vnops.c | 598 ------------------------------------- sys/kern/vfs_subr.c | 6 +- sys/sys/vnode.h | 3 +- 5 files changed, 555 insertions(+), 630 deletions(-) delete mode 100644 sys/fs/specfs/spec_vnops.c diff --git a/sys/conf/files b/sys/conf/files index 9aa26331734..2cd151267b5 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -912,7 +912,6 @@ fs/smbfs/smbfs_smb.c optional smbfs fs/smbfs/smbfs_subr.c optional smbfs fs/smbfs/smbfs_vfsops.c optional smbfs fs/smbfs/smbfs_vnops.c optional smbfs -fs/specfs/spec_vnops.c standard fs/udf/udf_iconv.c optional udf_iconv fs/udf/udf_vfsops.c optional udf fs/udf/udf_vnops.c optional udf diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c index 08a7abe713c..ded05a9b735 100644 --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 1992, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 2000 + * Copyright (c) 2000-2004 * Poul-Henning Kamp. All rights reserved. + * Copyright (c) 1989, 1992-1993, 1995 + * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * Jan-Simon Pendry. @@ -46,8 +46,11 @@ #include #include +#include +#include #include #include +#include #include #include #include @@ -55,6 +58,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -62,25 +68,36 @@ #include static int devfs_access(struct vop_access_args *ap); +static int devfs_advlock(struct vop_advlock_args *ap); +static int devfs_close(struct vop_close_args *ap); +static int devfs_fsync(struct vop_fsync_args *ap); static int devfs_getattr(struct vop_getattr_args *ap); static int devfs_ioctl(struct vop_ioctl_args *ap); +static int devfs_kqfilter(struct vop_kqfilter_args *ap); static int devfs_lookupx(struct vop_lookup_args *ap); static int devfs_mknod(struct vop_mknod_args *ap); +static int devfs_open(struct vop_open_args *ap); static int devfs_pathconf(struct vop_pathconf_args *ap); +static int devfs_poll(struct vop_poll_args *ap); +static int devfs_print(struct vop_print_args *ap); static int devfs_read(struct vop_read_args *ap); static int devfs_readdir(struct vop_readdir_args *ap); static int devfs_readlink(struct vop_readlink_args *ap); static int devfs_reclaim(struct vop_reclaim_args *ap); static int devfs_remove(struct vop_remove_args *ap); static int devfs_revoke(struct vop_revoke_args *ap); +static int devfs_rioctl(struct vop_ioctl_args *ap); +static int devfs_rread(struct vop_read_args *ap); static int devfs_setattr(struct vop_setattr_args *ap); #ifdef MAC static int devfs_setlabel(struct vop_setlabel_args *ap); #endif +static int devfs_specstrategy(struct vop_specstrategy_args *); static int devfs_symlink(struct vop_symlink_args *ap); +static int devfs_write(struct vop_write_args *ap); static vop_t **devfs_vnodeop_p; -static vop_t **devfs_specop_p; +vop_t **devfs_specop_p; /* * Construct the fully qualified path name relative to the mountpoint @@ -199,6 +216,127 @@ devfs_access(ap) return (error); } +/* + * Special device advisory byte-level locks. + */ +/* ARGSUSED */ +static int +devfs_advlock(ap) + struct vop_advlock_args /* { + struct vnode *a_vp; + caddr_t a_id; + int a_op; + struct flock *a_fl; + int a_flags; + } */ *ap; +{ + + return (ap->a_flags & F_FLOCK ? EOPNOTSUPP : EINVAL); +} + +/* + * Device close routine + */ +/* ARGSUSED */ +static int +devfs_close(ap) + struct vop_close_args /* { + struct vnode *a_vp; + int a_fflag; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap; +{ + struct vnode *vp = ap->a_vp, *oldvp; + struct thread *td = ap->a_td; + struct cdev *dev = vp->v_rdev; + struct cdevsw *dsw; + int error; + + /* + * Hack: a tty device that is a controlling terminal + * has a reference from the session structure. + * We cannot easily tell that a character device is + * a controlling terminal, unless it is the closing + * process' controlling terminal. In that case, + * if the reference count is 2 (this last descriptor + * plus the session), release the reference from the session. + */ + + /* + * This needs to be rewritten to take the vp interlock into + * consideration. + */ + + oldvp = NULL; + sx_xlock(&proctree_lock); + if (td && vp == td->td_proc->p_session->s_ttyvp) { + SESS_LOCK(td->td_proc->p_session); + VI_LOCK(vp); + if (count_dev(dev) == 2 && (vp->v_iflag & VI_XLOCK) == 0) { + td->td_proc->p_session->s_ttyvp = NULL; + oldvp = vp; + } + VI_UNLOCK(vp); + SESS_UNLOCK(td->td_proc->p_session); + } + sx_xunlock(&proctree_lock); + if (oldvp != NULL) + vrele(oldvp); + /* + * We do not want to really close the device if it + * is still in use unless we are trying to close it + * forcibly. Since every use (buffer, vnode, swap, cmap) + * holds a reference to the vnode, and because we mark + * any other vnodes that alias this device, when the + * sum of the reference counts on all the aliased + * vnodes descends to one, we are on last close. + */ + dsw = dev_refthread(dev); + if (dsw == NULL) + return (ENXIO); + VI_LOCK(vp); + if (vp->v_iflag & VI_XLOCK) { + /* Forced close. */ + } else if (dsw->d_flags & D_TRACKCLOSE) { + /* Keep device updated on status. */ + } else if (count_dev(dev) > 1) { + VI_UNLOCK(vp); + dev_relthread(dev); + return (0); + } + VI_UNLOCK(vp); + KASSERT(dev->si_refcount > 0, + ("devfs_close() on un-referenced struct cdev *(%s)", devtoname(dev))); + if (!(dsw->d_flags & D_NEEDGIANT)) { + DROP_GIANT(); + error = dsw->d_close(dev, ap->a_fflag, S_IFCHR, td); + PICKUP_GIANT(); + } else + error = dsw->d_close(dev, ap->a_fflag, S_IFCHR, td); + dev_relthread(dev); + return (error); +} + +/* + * Synch buffers associated with a block device + */ +/* ARGSUSED */ +static int +devfs_fsync(ap) + struct vop_fsync_args /* { + struct vnode *a_vp; + struct ucred *a_cred; + int a_waitfor; + struct thread *a_td; + } */ *ap; +{ + if (!vn_isdisk(ap->a_vp, NULL)) + return (0); + + return (vop_stdfsync(ap)); +} + static int devfs_getattr(ap) struct vop_getattr_args /* { @@ -266,6 +404,10 @@ devfs_getattr(ap) return (error); } +/* + * Device ioctl operation. + */ +/* ARGSUSED */ static int devfs_ioctl(ap) struct vop_ioctl_args /* { @@ -277,15 +419,55 @@ devfs_ioctl(ap) struct thread *a_td; } */ *ap; { + struct cdev *dev; int error; - struct devfs_mount *dmp; + struct cdevsw *dsw; - dmp = VFSTODEVFS(ap->a_vp->v_mount); - lockmgr(&dmp->dm_lock, LK_SHARED, 0, curthread); - devfs_populate(dmp); - lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curthread); - error = devfs_rules_ioctl(ap->a_vp->v_mount, ap->a_command, ap->a_data, - ap->a_td); + dev = ap->a_vp->v_rdev; + dsw = dev_refthread(dev); + if (dsw == NULL) + return (ENXIO); + KASSERT(dev->si_refcount > 0, + ("devfs_ioctl() on un-referenced struct cdev *(%s)", devtoname(dev))); + if (!(dsw->d_flags & D_NEEDGIANT)) { + DROP_GIANT(); + error = dsw->d_ioctl(dev, ap->a_command, + ap->a_data, ap->a_fflag, ap->a_td); + PICKUP_GIANT(); + } else + error = dsw->d_ioctl(dev, ap->a_command, + ap->a_data, ap->a_fflag, ap->a_td); + dev_relthread(dev); + if (error == ENOIOCTL) + error = ENOTTY; + return (error); +} + +/* ARGSUSED */ +static int +devfs_kqfilter(ap) + struct vop_kqfilter_args /* { + struct vnode *a_vp; + struct knote *a_kn; + } */ *ap; +{ + struct cdev *dev; + struct cdevsw *dsw; + int error; + + dev = ap->a_vp->v_rdev; + dsw = dev_refthread(dev); + if (dsw == NULL) + return(0); + KASSERT(dev->si_refcount > 0, + ("devfs_kqfilter() on un-referenced struct cdev *(%s)", devtoname(dev))); + if (!(dsw->d_flags & D_NEEDGIANT)) { + DROP_GIANT(); + error = dsw->d_kqfilter(dev, ap->a_kn); + PICKUP_GIANT(); + } else + error = dsw->d_kqfilter(dev, ap->a_kn); + dev_relthread(dev); return (error); } @@ -458,15 +640,14 @@ devfs_lookup(struct vop_lookup_args *ap) static int devfs_mknod(struct vop_mknod_args *ap) -/* -struct vop_mknod_args { - struct vnodeop_desc *a_desc; - struct vnode *a_dvp; - struct vnode **a_vpp; - struct componentname *a_cnp; - struct vattr *a_vap; -}; -*/ + /* + struct vop_mknod_args { + struct vnodeop_desc *a_desc; + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + }; */ { struct componentname *cnp; struct vnode *dvp, **vpp; @@ -504,6 +685,107 @@ notfound: return (error); } +/* + * Open a special file. + */ +/* ARGSUSED */ +static int +devfs_open(ap) + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap; +{ + struct thread *td = ap->a_td; + struct vnode *vp = ap->a_vp; + struct cdev *dev = vp->v_rdev; + int error; + struct cdevsw *dsw; + + if (vp->v_type == VBLK) + return (ENXIO); + + /* Don't allow open if fs is mounted -nodev. */ + if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV)) + return (ENXIO); + + if (dev == NULL) + return (ENXIO); + + /* Make this field valid before any I/O in d_open. */ + if (dev->si_iosize_max == 0) + dev->si_iosize_max = DFLTPHYS; + + /* + * XXX: Disks get special billing here, but it is mostly wrong. + * XXX: Disk partitions can overlap and the real checks should + * XXX: take this into account, and consequently they need to + * XXX: live in the disk slice code. Some checks do. + */ + if (vn_isdisk(vp, NULL) && ap->a_cred != FSCRED && + (ap->a_mode & FWRITE)) { + /* + * Never allow opens for write if the disk is mounted R/W. + */ + if (vp->v_rdev->si_mountpoint != NULL && + !(vp->v_rdev->si_mountpoint->mnt_flag & MNT_RDONLY)) + return (EBUSY); + + /* + * When running in secure mode, do not allow opens + * for writing if the disk is mounted. + */ + error = securelevel_ge(td->td_ucred, 1); + if (error && vfs_mountedon(vp)) + return (error); + + /* + * When running in very secure mode, do not allow + * opens for writing of any disks. + */ + error = securelevel_ge(td->td_ucred, 2); + if (error) + return (error); + } + + dsw = dev_refthread(dev); + if (dsw == NULL) + return (ENXIO); + + /* XXX: Special casing of ttys for deadfs. Probably redundant. */ + if (dsw->d_flags & D_TTY) + vp->v_vflag |= VV_ISTTY; + + VOP_UNLOCK(vp, 0, td); + + if(!(dsw->d_flags & D_NEEDGIANT)) { + DROP_GIANT(); + if (dsw->d_fdopen != NULL) + error = dsw->d_fdopen(dev, ap->a_mode, td, ap->a_fdidx); + else + error = dsw->d_open(dev, ap->a_mode, S_IFCHR, td); + PICKUP_GIANT(); + } else if (dsw->d_fdopen != NULL) + error = dsw->d_fdopen(dev, ap->a_mode, td, ap->a_fdidx); + else + error = dsw->d_open(dev, ap->a_mode, S_IFCHR, td); + + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); + + dev_relthread(dev); + + if (error) + return (error); + + if (vn_isdisk(vp, NULL)) { + if (!dev->si_bsize_phys) + dev->si_bsize_phys = DEV_BSIZE; + } + return (error); +} + static int devfs_pathconf(ap) @@ -538,6 +820,54 @@ devfs_pathconf(ap) /* NOTREACHED */ } +/* ARGSUSED */ +static int +devfs_poll(ap) + struct vop_poll_args /* { + struct vnode *a_vp; + int a_events; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap; +{ + struct cdev *dev; + struct cdevsw *dsw; + int error; + + dev = ap->a_vp->v_rdev; + dsw = dev_refthread(dev); + if (dsw == NULL) + return(0); + KASSERT(dev->si_refcount > 0, + ("devfs_poll() on un-referenced struct cdev *(%s)", devtoname(dev))); + if (!(dsw->d_flags & D_NEEDGIANT)) { + /* XXX: not yet DROP_GIANT(); */ + error = dsw->d_poll(dev, ap->a_events, ap->a_td); + /* XXX: not yet PICKUP_GIANT(); */ + } else + error = dsw->d_poll(dev, ap->a_events, ap->a_td); + dev_relthread(dev); + return(error); +} + +/* + * Print out the contents of a special device vnode. + */ +static int +devfs_print(ap) + struct vop_print_args /* { + struct vnode *a_vp; + } */ *ap; +{ + + printf("\tdev %s\n", devtoname(ap->a_vp->v_rdev)); + return (0); +} + +/* + * Vnode op for read + */ +/* ARGSUSED */ static int devfs_read(ap) struct vop_read_args /* { @@ -547,10 +877,39 @@ devfs_read(ap) struct ucred *a_cred; } */ *ap; { + struct vnode *vp; + struct thread *td; + struct uio *uio; + struct cdev *dev; + int error, resid; + struct cdevsw *dsw; - if (ap->a_vp->v_type != VDIR) - return (EINVAL); - return (VOP_READDIR(ap->a_vp, ap->a_uio, ap->a_cred, NULL, NULL, NULL)); + vp = ap->a_vp; + dev = vp->v_rdev; + uio = ap->a_uio; + td = uio->uio_td; + resid = uio->uio_resid; + + if (resid == 0) + return (0); + + KASSERT(dev->si_refcount > 0, + ("specread() on un-referenced struct cdev *(%s)", devtoname(dev))); + dsw = dev_refthread(dev); + if (dsw == NULL) + return (ENXIO); + VOP_UNLOCK(vp, 0, td); + if (!(dsw->d_flags & D_NEEDGIANT)) { + DROP_GIANT(); + error = dsw->d_read(dev, uio, ap->a_ioflag); + PICKUP_GIANT(); + } else + error = dsw->d_read(dev, uio, ap->a_ioflag); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); + dev_relthread(dev); + if (uio->uio_resid != resid || (error == 0 && resid != 0)) + vfs_timestamp(&dev->si_atime); + return (error); } static int @@ -720,6 +1079,44 @@ devfs_revoke(ap) return (0); } +static int +devfs_rioctl(ap) + struct vop_ioctl_args /* { + struct vnode *a_vp; + u_long a_command; + caddr_t a_data; + int a_fflag; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap; +{ + int error; + struct devfs_mount *dmp; + + dmp = VFSTODEVFS(ap->a_vp->v_mount); + lockmgr(&dmp->dm_lock, LK_SHARED, 0, curthread); + devfs_populate(dmp); + lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curthread); + error = devfs_rules_ioctl(ap->a_vp->v_mount, ap->a_command, ap->a_data, + ap->a_td); + return (error); +} + +static int +devfs_rread(ap) + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; +{ + + if (ap->a_vp->v_type != VDIR) + return (EINVAL); + return (VOP_READDIR(ap->a_vp, ap->a_uio, ap->a_cred, NULL, NULL, NULL)); +} + static int devfs_setattr(ap) struct vop_setattr_args /* { @@ -834,6 +1231,58 @@ devfs_setlabel(ap) } #endif +static int doslowdown = 0; +SYSCTL_INT(_debug, OID_AUTO, doslowdown, CTLFLAG_RW, &doslowdown, 0, ""); + +static int +devfs_specstrategy(ap) + struct vop_specstrategy_args /* { + struct vnode *a_vp; + struct buf *a_bp; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct buf *bp = ap->a_bp; + struct mount *mp; + struct thread *td = curthread; + + KASSERT(ap->a_vp->v_rdev == ap->a_bp->b_dev, + ("%s, dev %s != %s", __func__, + devtoname(ap->a_vp->v_rdev), + devtoname(ap->a_bp->b_dev))); + KASSERT(bp->b_iocmd == BIO_READ || bp->b_iocmd == BIO_WRITE, + ("Wrong b_iocmd buf=%p cmd=%d", bp, bp->b_iocmd)); + + /* + * Slow down disk requests for niced processes. + */ + if (doslowdown && td && td->td_proc->p_nice > 0) { + msleep(td, NULL, PPAUSE | PCATCH, "ioslow", + td->td_proc->p_nice); + } + /* + * Collect statistics on synchronous and asynchronous read + * and write counts for disks that have associated filesystems. + */ + if (vn_isdisk(vp, NULL) && (mp = vp->v_rdev->si_mountpoint) != NULL) { + if (bp->b_iocmd == BIO_WRITE) { + if (bp->b_lock.lk_lockholder == LK_KERNPROC) + mp->mnt_stat.f_asyncwrites++; + else + mp->mnt_stat.f_syncwrites++; + } else { + if (bp->b_lock.lk_lockholder == LK_KERNPROC) + mp->mnt_stat.f_asyncreads++; + else + mp->mnt_stat.f_syncreads++; + } + } + + dev_strategy(bp); + + return (0); +} + static int devfs_symlink(ap) struct vop_symlink_args /* { @@ -876,15 +1325,64 @@ devfs_symlink(ap) return (0); } +/* + * Vnode op for write + */ +/* ARGSUSED */ +static int +devfs_write(ap) + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; +{ + struct vnode *vp; + struct thread *td; + struct uio *uio; + struct cdev *dev; + int error, resid; + struct cdevsw *dsw; + + vp = ap->a_vp; + dev = vp->v_rdev; + uio = ap->a_uio; + td = uio->uio_td; + resid = uio->uio_resid; + + dsw = dev_refthread(dev); + if (dsw == NULL) + return (ENXIO); + VOP_UNLOCK(vp, 0, td); + KASSERT(dev->si_refcount > 0, + ("devfs_write() on un-referenced struct cdev *(%s)", + devtoname(dev))); + if (!(dsw->d_flags & D_NEEDGIANT)) { + DROP_GIANT(); + error = dsw->d_write(dev, uio, ap->a_ioflag); + PICKUP_GIANT(); + } else + error = dsw->d_write(dev, uio, ap->a_ioflag); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); + dev_relthread(dev); + if (uio->uio_resid != resid || (error == 0 && resid != 0)) { + vfs_timestamp(&dev->si_ctime); + dev->si_mtime = dev->si_ctime; + } + return (error); +} + + static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *) vop_defaultop }, { &vop_access_desc, (vop_t *) devfs_access }, { &vop_getattr_desc, (vop_t *) devfs_getattr }, - { &vop_ioctl_desc, (vop_t *) devfs_ioctl }, + { &vop_ioctl_desc, (vop_t *) devfs_rioctl }, { &vop_lookup_desc, (vop_t *) devfs_lookup }, { &vop_mknod_desc, (vop_t *) devfs_mknod }, { &vop_pathconf_desc, (vop_t *) devfs_pathconf }, - { &vop_read_desc, (vop_t *) devfs_read }, + { &vop_read_desc, (vop_t *) devfs_rread }, { &vop_readdir_desc, (vop_t *) devfs_readdir }, { &vop_readlink_desc, (vop_t *) devfs_readlink }, { &vop_reclaim_desc, (vop_t *) devfs_reclaim }, @@ -897,25 +1395,52 @@ static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = { { &vop_symlink_desc, (vop_t *) devfs_symlink }, { NULL, NULL } }; + static struct vnodeopv_desc devfs_vnodeop_opv_desc = { &devfs_vnodeop_p, devfs_vnodeop_entries }; VNODEOP_SET(devfs_vnodeop_opv_desc); static struct vnodeopv_entry_desc devfs_specop_entries[] = { - { &vop_default_desc, (vop_t *) spec_vnoperate }, + { &vop_default_desc, (vop_t *) vop_defaultop }, { &vop_access_desc, (vop_t *) devfs_access }, + { &vop_advlock_desc, (vop_t *) devfs_advlock }, + { &vop_bmap_desc, (vop_t *) vop_panic }, + { &vop_close_desc, (vop_t *) devfs_close }, + { &vop_create_desc, (vop_t *) vop_panic }, + { &vop_fsync_desc, (vop_t *) devfs_fsync }, { &vop_getattr_desc, (vop_t *) devfs_getattr }, + { &vop_getwritemount_desc, (vop_t *) vop_stdgetwritemount }, + { &vop_ioctl_desc, (vop_t *) devfs_ioctl }, + { &vop_kqfilter_desc, (vop_t *) devfs_kqfilter }, + { &vop_lease_desc, (vop_t *) vop_null }, + { &vop_link_desc, (vop_t *) vop_panic }, + { &vop_mkdir_desc, (vop_t *) vop_panic }, + { &vop_mknod_desc, (vop_t *) vop_panic }, + { &vop_open_desc, (vop_t *) devfs_open }, { &vop_pathconf_desc, (vop_t *) devfs_pathconf }, + { &vop_poll_desc, (vop_t *) devfs_poll }, + { &vop_print_desc, (vop_t *) devfs_print }, + { &vop_read_desc, (vop_t *) devfs_read }, + { &vop_readdir_desc, (vop_t *) vop_panic }, + { &vop_readlink_desc, (vop_t *) vop_panic }, + { &vop_reallocblks_desc, (vop_t *) vop_panic }, { &vop_reclaim_desc, (vop_t *) devfs_reclaim }, { &vop_remove_desc, (vop_t *) devfs_remove }, + { &vop_rename_desc, (vop_t *) vop_panic }, { &vop_revoke_desc, (vop_t *) devfs_revoke }, + { &vop_rmdir_desc, (vop_t *) vop_panic }, { &vop_setattr_desc, (vop_t *) devfs_setattr }, #ifdef MAC { &vop_setlabel_desc, (vop_t *) devfs_setlabel }, #endif + { &vop_specstrategy_desc, (vop_t *) devfs_specstrategy }, + { &vop_strategy_desc, (vop_t *) vop_panic }, + { &vop_symlink_desc, (vop_t *) vop_panic }, + { &vop_write_desc, (vop_t *) devfs_write }, { NULL, NULL } }; + static struct vnodeopv_desc devfs_specop_opv_desc = { &devfs_specop_p, devfs_specop_entries }; diff --git a/sys/fs/specfs/spec_vnops.c b/sys/fs/specfs/spec_vnops.c deleted file mode 100644 index 67edcdc2d5d..00000000000 --- a/sys/fs/specfs/spec_vnops.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - * Copyright (c) 1989, 1993, 1995 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)spec_vnops.c 8.14 (Berkeley) 5/21/95 - * $FreeBSD$ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int spec_advlock(struct vop_advlock_args *); -static int spec_close(struct vop_close_args *); -static int spec_fsync(struct vop_fsync_args *); -static int spec_ioctl(struct vop_ioctl_args *); -static int spec_kqfilter(struct vop_kqfilter_args *); -static int spec_open(struct vop_open_args *); -static int spec_poll(struct vop_poll_args *); -static int spec_print(struct vop_print_args *); -static int spec_read(struct vop_read_args *); -static int spec_specstrategy(struct vop_specstrategy_args *); -static int spec_write(struct vop_write_args *); - -vop_t **spec_vnodeop_p; -static struct vnodeopv_entry_desc spec_vnodeop_entries[] = { - { &vop_default_desc, (vop_t *) vop_defaultop }, - { &vop_access_desc, (vop_t *) vop_ebadf }, - { &vop_advlock_desc, (vop_t *) spec_advlock }, - { &vop_bmap_desc, (vop_t *) vop_panic }, - { &vop_close_desc, (vop_t *) spec_close }, - { &vop_create_desc, (vop_t *) vop_panic }, - { &vop_fsync_desc, (vop_t *) spec_fsync }, - { &vop_getwritemount_desc, (vop_t *) vop_stdgetwritemount }, - { &vop_ioctl_desc, (vop_t *) spec_ioctl }, - { &vop_kqfilter_desc, (vop_t *) spec_kqfilter }, - { &vop_lease_desc, (vop_t *) vop_null }, - { &vop_link_desc, (vop_t *) vop_panic }, - { &vop_mkdir_desc, (vop_t *) vop_panic }, - { &vop_mknod_desc, (vop_t *) vop_panic }, - { &vop_open_desc, (vop_t *) spec_open }, - { &vop_pathconf_desc, (vop_t *) vop_stdpathconf }, - { &vop_poll_desc, (vop_t *) spec_poll }, - { &vop_print_desc, (vop_t *) spec_print }, - { &vop_read_desc, (vop_t *) spec_read }, - { &vop_readdir_desc, (vop_t *) vop_panic }, - { &vop_readlink_desc, (vop_t *) vop_panic }, - { &vop_reallocblks_desc, (vop_t *) vop_panic }, - { &vop_reclaim_desc, (vop_t *) vop_null }, - { &vop_remove_desc, (vop_t *) vop_panic }, - { &vop_rename_desc, (vop_t *) vop_panic }, - { &vop_rmdir_desc, (vop_t *) vop_panic }, - { &vop_setattr_desc, (vop_t *) vop_ebadf }, - { &vop_specstrategy_desc, (vop_t *) spec_specstrategy }, - { &vop_strategy_desc, (vop_t *) vop_panic }, - { &vop_symlink_desc, (vop_t *) vop_panic }, - { &vop_write_desc, (vop_t *) spec_write }, - { NULL, NULL } -}; -static struct vnodeopv_desc spec_vnodeop_opv_desc = - { &spec_vnodeop_p, spec_vnodeop_entries }; - -VNODEOP_SET(spec_vnodeop_opv_desc); - -int -spec_vnoperate(ap) - struct vop_generic_args /* { - struct vnodeop_desc *a_desc; - - } */ *ap; -{ - return (VOCALL(spec_vnodeop_p, ap->a_desc->vdesc_offset, ap)); -} - -/* - * Open a special file. - */ -/* ARGSUSED */ -static int -spec_open(ap) - struct vop_open_args /* { - struct vnode *a_vp; - int a_mode; - struct ucred *a_cred; - struct thread *a_td; - } */ *ap; -{ - struct thread *td = ap->a_td; - struct vnode *vp = ap->a_vp; - struct cdev *dev = vp->v_rdev; - int error; - struct cdevsw *dsw; - - if (vp->v_type == VBLK) - return (ENXIO); - - /* Don't allow open if fs is mounted -nodev. */ - if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV)) - return (ENXIO); - - if (dev == NULL) - return (ENXIO); - - /* Make this field valid before any I/O in d_open. */ - if (dev->si_iosize_max == 0) - dev->si_iosize_max = DFLTPHYS; - - /* - * XXX: Disks get special billing here, but it is mostly wrong. - * XXX: Disk partitions can overlap and the real checks should - * XXX: take this into account, and consequently they need to - * XXX: live in the disk slice code. Some checks do. - */ - if (vn_isdisk(vp, NULL) && ap->a_cred != FSCRED && - (ap->a_mode & FWRITE)) { - /* - * Never allow opens for write if the disk is mounted R/W. - */ - if (vp->v_rdev->si_mountpoint != NULL && - !(vp->v_rdev->si_mountpoint->mnt_flag & MNT_RDONLY)) - return (EBUSY); - - /* - * When running in secure mode, do not allow opens - * for writing if the disk is mounted. - */ - error = securelevel_ge(td->td_ucred, 1); - if (error && vfs_mountedon(vp)) - return (error); - - /* - * When running in very secure mode, do not allow - * opens for writing of any disks. - */ - error = securelevel_ge(td->td_ucred, 2); - if (error) - return (error); - } - - dsw = dev_refthread(dev); - if (dsw == NULL) - return (ENXIO); - - /* XXX: Special casing of ttys for deadfs. Probably redundant. */ - if (dsw->d_flags & D_TTY) - vp->v_vflag |= VV_ISTTY; - - VOP_UNLOCK(vp, 0, td); - - if(!(dsw->d_flags & D_NEEDGIANT)) { - DROP_GIANT(); - if (dsw->d_fdopen != NULL) - error = dsw->d_fdopen(dev, ap->a_mode, td, ap->a_fdidx); - else - error = dsw->d_open(dev, ap->a_mode, S_IFCHR, td); - PICKUP_GIANT(); - } else if (dsw->d_fdopen != NULL) - error = dsw->d_fdopen(dev, ap->a_mode, td, ap->a_fdidx); - else - error = dsw->d_open(dev, ap->a_mode, S_IFCHR, td); - - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - - dev_relthread(dev); - - if (error) - return (error); - - if (vn_isdisk(vp, NULL)) { - if (!dev->si_bsize_phys) - dev->si_bsize_phys = DEV_BSIZE; - } - return (error); -} - -/* - * Vnode op for read - */ -/* ARGSUSED */ -static int -spec_read(ap) - struct vop_read_args /* { - struct vnode *a_vp; - struct uio *a_uio; - int a_ioflag; - struct ucred *a_cred; - } */ *ap; -{ - struct vnode *vp; - struct thread *td; - struct uio *uio; - struct cdev *dev; - int error, resid; - struct cdevsw *dsw; - - vp = ap->a_vp; - dev = vp->v_rdev; - uio = ap->a_uio; - td = uio->uio_td; - resid = uio->uio_resid; - - if (resid == 0) - return (0); - - KASSERT(dev->si_refcount > 0, - ("specread() on un-referenced struct cdev *(%s)", devtoname(dev))); - dsw = dev_refthread(dev); - if (dsw == NULL) - return (ENXIO); - VOP_UNLOCK(vp, 0, td); - if (!(dsw->d_flags & D_NEEDGIANT)) { - DROP_GIANT(); - error = dsw->d_read(dev, uio, ap->a_ioflag); - PICKUP_GIANT(); - } else - error = dsw->d_read(dev, uio, ap->a_ioflag); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - dev_relthread(dev); - if (uio->uio_resid != resid || (error == 0 && resid != 0)) - vfs_timestamp(&dev->si_atime); - return (error); -} - -/* - * Vnode op for write - */ -/* ARGSUSED */ -static int -spec_write(ap) - struct vop_write_args /* { - struct vnode *a_vp; - struct uio *a_uio; - int a_ioflag; - struct ucred *a_cred; - } */ *ap; -{ - struct vnode *vp; - struct thread *td; - struct uio *uio; - struct cdev *dev; - int error, resid; - struct cdevsw *dsw; - - vp = ap->a_vp; - dev = vp->v_rdev; - uio = ap->a_uio; - td = uio->uio_td; - resid = uio->uio_resid; - - dsw = dev_refthread(dev); - if (dsw == NULL) - return (ENXIO); - VOP_UNLOCK(vp, 0, td); - KASSERT(dev->si_refcount > 0, - ("spec_write() on un-referenced struct cdev *(%s)", devtoname(dev))); - if (!(dsw->d_flags & D_NEEDGIANT)) { - DROP_GIANT(); - error = dsw->d_write(dev, uio, ap->a_ioflag); - PICKUP_GIANT(); - } else - error = dsw->d_write(dev, uio, ap->a_ioflag); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - dev_relthread(dev); - if (uio->uio_resid != resid || (error == 0 && resid != 0)) { - vfs_timestamp(&dev->si_ctime); - dev->si_mtime = dev->si_ctime; - } - return (error); -} - -/* - * Device ioctl operation. - */ -/* ARGSUSED */ -static int -spec_ioctl(ap) - struct vop_ioctl_args /* { - struct vnode *a_vp; - u_long a_command; - caddr_t a_data; - int a_fflag; - struct ucred *a_cred; - struct thread *a_td; - } */ *ap; -{ - struct cdev *dev; - int error; - struct cdevsw *dsw; - - dev = ap->a_vp->v_rdev; - dsw = dev_refthread(dev); - if (dsw == NULL) - return (ENXIO); - KASSERT(dev->si_refcount > 0, - ("spec_ioctl() on un-referenced struct cdev *(%s)", devtoname(dev))); - if (!(dsw->d_flags & D_NEEDGIANT)) { - DROP_GIANT(); - error = dsw->d_ioctl(dev, ap->a_command, - ap->a_data, ap->a_fflag, ap->a_td); - PICKUP_GIANT(); - } else - error = dsw->d_ioctl(dev, ap->a_command, - ap->a_data, ap->a_fflag, ap->a_td); - dev_relthread(dev); - if (error == ENOIOCTL) - error = ENOTTY; - return (error); -} - -/* ARGSUSED */ -static int -spec_poll(ap) - struct vop_poll_args /* { - struct vnode *a_vp; - int a_events; - struct ucred *a_cred; - struct thread *a_td; - } */ *ap; -{ - struct cdev *dev; - struct cdevsw *dsw; - int error; - - dev = ap->a_vp->v_rdev; - dsw = dev_refthread(dev); - if (dsw == NULL) - return(0); - KASSERT(dev->si_refcount > 0, - ("spec_poll() on un-referenced struct cdev *(%s)", devtoname(dev))); - if (!(dsw->d_flags & D_NEEDGIANT)) { - /* XXX: not yet DROP_GIANT(); */ - error = dsw->d_poll(dev, ap->a_events, ap->a_td); - /* XXX: not yet PICKUP_GIANT(); */ - } else - error = dsw->d_poll(dev, ap->a_events, ap->a_td); - dev_relthread(dev); - return(error); -} - -/* ARGSUSED */ -static int -spec_kqfilter(ap) - struct vop_kqfilter_args /* { - struct vnode *a_vp; - struct knote *a_kn; - } */ *ap; -{ - struct cdev *dev; - struct cdevsw *dsw; - int error; - - dev = ap->a_vp->v_rdev; - dsw = dev_refthread(dev); - if (dsw == NULL) - return(0); - KASSERT(dev->si_refcount > 0, - ("spec_kqfilter() on un-referenced struct cdev *(%s)", devtoname(dev))); - if (!(dsw->d_flags & D_NEEDGIANT)) { - DROP_GIANT(); - error = dsw->d_kqfilter(dev, ap->a_kn); - PICKUP_GIANT(); - } else - error = dsw->d_kqfilter(dev, ap->a_kn); - dev_relthread(dev); - return (error); -} - -/* - * Synch buffers associated with a block device - */ -/* ARGSUSED */ -static int -spec_fsync(ap) - struct vop_fsync_args /* { - struct vnode *a_vp; - struct ucred *a_cred; - int a_waitfor; - struct thread *a_td; - } */ *ap; -{ - if (!vn_isdisk(ap->a_vp, NULL)) - return (0); - - return (vop_stdfsync(ap)); -} - -static int doslowdown = 0; -SYSCTL_INT(_debug, OID_AUTO, doslowdown, CTLFLAG_RW, &doslowdown, 0, ""); - -/* - * Just call the device strategy routine - */ -static int -spec_specstrategy(ap) - struct vop_specstrategy_args /* { - struct vnode *a_vp; - struct buf *a_bp; - } */ *ap; -{ - struct vnode *vp = ap->a_vp; - struct buf *bp = ap->a_bp; - struct mount *mp; - struct thread *td = curthread; - - KASSERT(ap->a_vp->v_rdev == ap->a_bp->b_dev, - ("%s, dev %s != %s", __func__, - devtoname(ap->a_vp->v_rdev), - devtoname(ap->a_bp->b_dev))); - KASSERT(bp->b_iocmd == BIO_READ || bp->b_iocmd == BIO_WRITE, - ("Wrong b_iocmd buf=%p cmd=%d", bp, bp->b_iocmd)); - - /* - * Slow down disk requests for niced processes. - */ - if (doslowdown && td && td->td_proc->p_nice > 0) { - msleep(td, NULL, PPAUSE | PCATCH, "ioslow", - td->td_proc->p_nice); - } - /* - * Collect statistics on synchronous and asynchronous read - * and write counts for disks that have associated filesystems. - */ - if (vn_isdisk(vp, NULL) && (mp = vp->v_rdev->si_mountpoint) != NULL) { - if (bp->b_iocmd == BIO_WRITE) { - if (bp->b_lock.lk_lockholder == LK_KERNPROC) - mp->mnt_stat.f_asyncwrites++; - else - mp->mnt_stat.f_syncwrites++; - } else { - if (bp->b_lock.lk_lockholder == LK_KERNPROC) - mp->mnt_stat.f_asyncreads++; - else - mp->mnt_stat.f_syncreads++; - } - } - - dev_strategy(bp); - - return (0); -} - -/* - * Device close routine - */ -/* ARGSUSED */ -static int -spec_close(ap) - struct vop_close_args /* { - struct vnode *a_vp; - int a_fflag; - struct ucred *a_cred; - struct thread *a_td; - } */ *ap; -{ - struct vnode *vp = ap->a_vp, *oldvp; - struct thread *td = ap->a_td; - struct cdev *dev = vp->v_rdev; - struct cdevsw *dsw; - int error; - - /* - * Hack: a tty device that is a controlling terminal - * has a reference from the session structure. - * We cannot easily tell that a character device is - * a controlling terminal, unless it is the closing - * process' controlling terminal. In that case, - * if the reference count is 2 (this last descriptor - * plus the session), release the reference from the session. - */ - - /* - * This needs to be rewritten to take the vp interlock into - * consideration. - */ - - oldvp = NULL; - sx_xlock(&proctree_lock); - if (td && vp == td->td_proc->p_session->s_ttyvp) { - SESS_LOCK(td->td_proc->p_session); - VI_LOCK(vp); - if (count_dev(dev) == 2 && (vp->v_iflag & VI_XLOCK) == 0) { - td->td_proc->p_session->s_ttyvp = NULL; - oldvp = vp; - } - VI_UNLOCK(vp); - SESS_UNLOCK(td->td_proc->p_session); - } - sx_xunlock(&proctree_lock); - if (oldvp != NULL) - vrele(oldvp); - /* - * We do not want to really close the device if it - * is still in use unless we are trying to close it - * forcibly. Since every use (buffer, vnode, swap, cmap) - * holds a reference to the vnode, and because we mark - * any other vnodes that alias this device, when the - * sum of the reference counts on all the aliased - * vnodes descends to one, we are on last close. - */ - dsw = dev_refthread(dev); - if (dsw == NULL) - return (ENXIO); - VI_LOCK(vp); - if (vp->v_iflag & VI_XLOCK) { - /* Forced close. */ - } else if (dsw->d_flags & D_TRACKCLOSE) { - /* Keep device updated on status. */ - } else if (count_dev(dev) > 1) { - VI_UNLOCK(vp); - dev_relthread(dev); - return (0); - } - VI_UNLOCK(vp); - KASSERT(dev->si_refcount > 0, - ("spec_close() on un-referenced struct cdev *(%s)", devtoname(dev))); - if (!(dsw->d_flags & D_NEEDGIANT)) { - DROP_GIANT(); - error = dsw->d_close(dev, ap->a_fflag, S_IFCHR, td); - PICKUP_GIANT(); - } else - error = dsw->d_close(dev, ap->a_fflag, S_IFCHR, td); - dev_relthread(dev); - return (error); -} - -/* - * Print out the contents of a special device vnode. - */ -static int -spec_print(ap) - struct vop_print_args /* { - struct vnode *a_vp; - } */ *ap; -{ - - printf("\tdev %s\n", devtoname(ap->a_vp->v_rdev)); - return (0); -} - -/* - * Special device advisory byte-level locks. - */ -/* ARGSUSED */ -static int -spec_advlock(ap) - struct vop_advlock_args /* { - struct vnode *a_vp; - caddr_t a_id; - int a_op; - struct flock *a_fl; - int a_flags; - } */ *ap; -{ - - return (ap->a_flags & F_FLOCK ? EOPNOTSUPP : EINVAL); -} diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c index eccaee4c08d..ebd8c29bd70 100644 --- a/sys/kern/vfs_subr.c +++ b/sys/kern/vfs_subr.c @@ -1806,7 +1806,7 @@ bdevvp(dev, vpp) if (vfinddev(dev, vpp)) return (0); - error = getnewvnode("none", (struct mount *)0, spec_vnodeop_p, &nvp); + error = getnewvnode("none", (struct mount *)0, devfs_specop_p, &nvp); if (error) { *vpp = NULLVP; return (error); @@ -2587,14 +2587,14 @@ vgonechrl(struct vnode *vp, struct thread *td) VOP_UNLOCK(vp, 0, td); vp->v_vnlock = &vp->v_lock; vp->v_tag = "orphanchr"; - vp->v_op = spec_vnodeop_p; + vp->v_op = devfs_specop_p; delmntque(vp); cache_purge(vp); vrele(vp); VI_LOCK(vp); } else vclean(vp, 0, td); - vp->v_op = spec_vnodeop_p; + vp->v_op = devfs_specop_p; vx_unlock(vp); VI_UNLOCK(vp); } diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index 5f0dd38be02..6c9af71cc23 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -622,7 +622,6 @@ int getnewvnode(const char *tag, struct mount *mp, vop_t **vops, struct vnode **vpp); u_quad_t init_va_filerev(void); int lease_check(struct vop_lease_args *ap); -int spec_vnoperate(struct vop_generic_args *); int speedup_syncer(void); #define textvp_fullpath(p, rb, rfb) \ vn_fullpath(FIRST_THREAD_IN_PROC(p), (p)->p_textvp, rb, rfb) @@ -729,8 +728,8 @@ void vbusy(struct vnode *vp); void v_addpollinfo(struct vnode *vp); extern vop_t **default_vnodeop_p; -extern vop_t **spec_vnodeop_p; extern vop_t **dead_vnodeop_p; +extern vop_t **devfs_specop_p; #endif /* _KERNEL */