freebsd-dev/sys/kern/vfs_vnops.c
John Dyson 925a3a419a Fix some vnode management problems, and better mgmt of vnode free list.
Fix the UIO optimization code.
Fix an assumption in vm_map_insert regarding allocation of swap pagers.
Fix an spl problem in the collapse handling in vm_object_deallocate.
When pages are freed from vnode objects, and the criteria for putting
the associated vnode onto the free list is reached, either put the
vnode onto the list, or put it onto an interrupt safe version of the
list, for further transfer onto the actual free list.
Some minor syntax changes changing pre-decs, pre-incs to post versions.
Remove a bogus timeout (that I added for debugging) from vn_lock.

PHK will likely still have problems with the vnode list management, and
so do I, but it is better than it was.
1998-01-12 01:46:33 +00:00

538 lines
12 KiB
C

/*
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 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.
*
* @(#)vfs_vnops.c 8.2 (Berkeley) 1/21/94
* $Id: vfs_vnops.c,v 1.46 1998/01/06 05:16:32 dyson Exp $
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/filio.h>
#include <sys/ttycom.h>
static int vn_closefile __P((struct file *fp, struct proc *p));
static int vn_ioctl __P((struct file *fp, int com, caddr_t data,
struct proc *p));
static int vn_read __P((struct file *fp, struct uio *uio,
struct ucred *cred));
static int vn_poll __P((struct file *fp, int events, struct ucred *cred,
struct proc *p));
static int vn_write __P((struct file *fp, struct uio *uio,
struct ucred *cred));
struct fileops vnops =
{ vn_read, vn_write, vn_ioctl, vn_poll, vn_closefile };
/*
* Common code for vnode open operations.
* Check permissions, and call the VOP_OPEN or VOP_CREATE routine.
*/
int
vn_open(ndp, fmode, cmode)
register struct nameidata *ndp;
int fmode, cmode;
{
register struct vnode *vp;
register struct proc *p = ndp->ni_cnd.cn_proc;
register struct ucred *cred = p->p_ucred;
struct vattr vat;
struct vattr *vap = &vat;
int error;
if (fmode & O_CREAT) {
ndp->ni_cnd.cn_nameiop = CREATE;
ndp->ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
if ((fmode & O_EXCL) == 0)
ndp->ni_cnd.cn_flags |= FOLLOW;
error = namei(ndp);
if (error)
return (error);
if (ndp->ni_vp == NULL) {
VATTR_NULL(vap);
vap->va_type = VREG;
vap->va_mode = cmode;
if (fmode & O_EXCL)
vap->va_vaflags |= VA_EXCLUSIVE;
VOP_LEASE(ndp->ni_dvp, p, cred, LEASE_WRITE);
if (error = VOP_CREATE(ndp->ni_dvp, &ndp->ni_vp,
&ndp->ni_cnd, vap))
return (error);
ASSERT_VOP_UNLOCKED(ndp->ni_dvp, "create");
ASSERT_VOP_LOCKED(ndp->ni_vp, "create");
fmode &= ~O_TRUNC;
vp = ndp->ni_vp;
} else {
VOP_ABORTOP(ndp->ni_dvp, &ndp->ni_cnd);
if (ndp->ni_dvp == ndp->ni_vp)
vrele(ndp->ni_dvp);
else
vput(ndp->ni_dvp);
ndp->ni_dvp = NULL;
vp = ndp->ni_vp;
if (fmode & O_EXCL) {
error = EEXIST;
goto bad;
}
fmode &= ~O_CREAT;
}
} else {
ndp->ni_cnd.cn_nameiop = LOOKUP;
ndp->ni_cnd.cn_flags = FOLLOW | LOCKLEAF;
error = namei(ndp);
if (error)
return (error);
vp = ndp->ni_vp;
}
if (vp->v_type == VSOCK) {
error = EOPNOTSUPP;
goto bad;
}
if ((fmode & O_CREAT) == 0) {
if (fmode & FREAD) {
error = VOP_ACCESS(vp, VREAD, cred, p);
if (error)
goto bad;
}
if (fmode & (FWRITE | O_TRUNC)) {
if (vp->v_type == VDIR) {
error = EISDIR;
goto bad;
}
error = vn_writechk(vp);
if (error)
goto bad;
error = VOP_ACCESS(vp, VWRITE, cred, p);
if (error)
goto bad;
}
}
if (fmode & O_TRUNC) {
VOP_UNLOCK(vp, 0, p); /* XXX */
VOP_LEASE(vp, p, cred, LEASE_WRITE);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); /* XXX */
VATTR_NULL(vap);
vap->va_size = 0;
error = VOP_SETATTR(vp, vap, cred, p);
if (error)
goto bad;
}
error = VOP_OPEN(vp, fmode, cred, p);
if (error)
goto bad;
/*
* Make sure that a VM object is created for VMIO support.
*/
if (vp->v_type == VREG) {
if ((error = vfs_object_create(vp, p, cred, 1)) != 0)
goto bad;
}
if (fmode & FWRITE)
vp->v_writecount++;
return (0);
bad:
vput(vp);
return (error);
}
/*
* Check for write permissions on the specified vnode.
* Prototype text segments cannot be written.
*/
int
vn_writechk(vp)
register struct vnode *vp;
{
/*
* If there's shared text associated with
* the vnode, try to free it up once. If
* we fail, we can't allow writing.
*/
if (vp->v_flag & VTEXT)
return (ETXTBSY);
return (0);
}
/*
* Vnode close call
*/
int
vn_close(vp, flags, cred, p)
register struct vnode *vp;
int flags;
struct ucred *cred;
struct proc *p;
{
int error;
if (flags & FWRITE)
vp->v_writecount--;
error = VOP_CLOSE(vp, flags, cred, p);
vrele(vp);
return (error);
}
/*
* Package up an I/O request on a vnode into a uio and do it.
*/
int
vn_rdwr(rw, vp, base, len, offset, segflg, ioflg, cred, aresid, p)
enum uio_rw rw;
struct vnode *vp;
caddr_t base;
int len;
off_t offset;
enum uio_seg segflg;
int ioflg;
struct ucred *cred;
int *aresid;
struct proc *p;
{
struct uio auio;
struct iovec aiov;
int error;
if ((ioflg & IO_NODELOCKED) == 0)
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
aiov.iov_base = base;
aiov.iov_len = len;
auio.uio_resid = len;
auio.uio_offset = offset;
auio.uio_segflg = segflg;
auio.uio_rw = rw;
auio.uio_procp = p;
if (rw == UIO_READ) {
error = VOP_READ(vp, &auio, ioflg, cred);
} else {
error = VOP_WRITE(vp, &auio, ioflg, cred);
}
if (aresid)
*aresid = auio.uio_resid;
else
if (auio.uio_resid && error == 0)
error = EIO;
if ((ioflg & IO_NODELOCKED) == 0)
VOP_UNLOCK(vp, 0, p);
return (error);
}
/*
* File table vnode read routine.
*/
static int
vn_read(fp, uio, cred)
struct file *fp;
struct uio *uio;
struct ucred *cred;
{
struct vnode *vp = (struct vnode *)fp->f_data;
struct proc *p = uio->uio_procp;
int count, error;
int flag;
VOP_LEASE(vp, p, cred, LEASE_READ);
vn_lock(vp, LK_SHARED | LK_NOPAUSE | LK_RETRY, p);
if (uio->uio_offset == -1)
uio->uio_offset = fp->f_offset;
count = uio->uio_resid;
flag = 0;
if (fp->f_flag & FNONBLOCK)
flag |= IO_NDELAY;
/*
* Sequential read heuristic.
* If we have been doing sequential input,
* a rewind operation doesn't turn off
* sequential input mode.
*/
if (((fp->f_offset == 0) && (fp->f_seqcount > 0)) ||
(fp->f_offset == fp->f_nextread)) {
int tmpseq = fp->f_seqcount;
/*
* XXX we assume that the filesystem block size is
* the default. Not true, but still gives us a pretty
* good indicator of how sequential the read operations
* are.
*/
tmpseq += ((count + BKVASIZE - 1) / BKVASIZE);
if (tmpseq >= 127)
tmpseq = 127;
fp->f_seqcount = tmpseq;
flag |= (fp->f_seqcount << 16);
} else {
if (fp->f_seqcount > 1)
fp->f_seqcount = 1;
else
fp->f_seqcount = 0;
}
error = VOP_READ(vp, uio, flag, cred);
fp->f_offset += count - uio->uio_resid;
fp->f_nextread = fp->f_offset;
VOP_UNLOCK(vp, 0, p);
return (error);
}
/*
* File table vnode write routine.
*/
static int
vn_write(fp, uio, cred)
struct file *fp;
struct uio *uio;
struct ucred *cred;
{
struct vnode *vp = (struct vnode *)fp->f_data;
struct proc *p = uio->uio_procp;
int count, error, ioflag = IO_UNIT;
if (uio->uio_offset == -1 && vp->v_type == VREG && (fp->f_flag & O_APPEND))
ioflag |= IO_APPEND;
if (fp->f_flag & FNONBLOCK)
ioflag |= IO_NDELAY;
if ((fp->f_flag & O_FSYNC) ||
(vp->v_mount && (vp->v_mount->mnt_flag & MNT_SYNCHRONOUS)))
ioflag |= IO_SYNC;
VOP_LEASE(vp, p, cred, LEASE_WRITE);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
uio->uio_offset = fp->f_offset;
count = uio->uio_resid;
error = VOP_WRITE(vp, uio, ioflag, cred);
if (ioflag & IO_APPEND)
fp->f_offset = uio->uio_offset;
else
fp->f_offset += count - uio->uio_resid;
VOP_UNLOCK(vp, 0, p);
return (error);
}
/*
* File table vnode stat routine.
*/
int
vn_stat(vp, sb, p)
struct vnode *vp;
register struct stat *sb;
struct proc *p;
{
struct vattr vattr;
register struct vattr *vap;
int error;
u_short mode;
vap = &vattr;
error = VOP_GETATTR(vp, vap, p->p_ucred, p);
if (error)
return (error);
/*
* Copy from vattr table
*/
sb->st_dev = vap->va_fsid;
sb->st_ino = vap->va_fileid;
mode = vap->va_mode;
switch (vp->v_type) {
case VREG:
mode |= S_IFREG;
break;
case VDIR:
mode |= S_IFDIR;
break;
case VBLK:
mode |= S_IFBLK;
break;
case VCHR:
mode |= S_IFCHR;
break;
case VLNK:
mode |= S_IFLNK;
break;
case VSOCK:
mode |= S_IFSOCK;
break;
case VFIFO:
mode |= S_IFIFO;
break;
default:
return (EBADF);
};
sb->st_mode = mode;
sb->st_nlink = vap->va_nlink;
sb->st_uid = vap->va_uid;
sb->st_gid = vap->va_gid;
sb->st_rdev = vap->va_rdev;
sb->st_size = vap->va_size;
sb->st_atimespec = vap->va_atime;
sb->st_mtimespec = vap->va_mtime;
sb->st_ctimespec = vap->va_ctime;
sb->st_blksize = vap->va_blocksize;
sb->st_flags = vap->va_flags;
if (p->p_ucred->cr_uid != 0)
sb->st_gen = 0;
else
sb->st_gen = vap->va_gen;
#if (S_BLKSIZE == 512)
/* Optimize this case */
sb->st_blocks = vap->va_bytes >> 9;
#else
sb->st_blocks = vap->va_bytes / S_BLKSIZE;
#endif
return (0);
}
/*
* File table vnode ioctl routine.
*/
static int
vn_ioctl(fp, com, data, p)
struct file *fp;
int com;
caddr_t data;
struct proc *p;
{
register struct vnode *vp = ((struct vnode *)fp->f_data);
struct vattr vattr;
int error;
switch (vp->v_type) {
case VREG:
case VDIR:
if (com == FIONREAD) {
error = VOP_GETATTR(vp, &vattr, p->p_ucred, p);
if (error)
return (error);
*(int *)data = vattr.va_size - fp->f_offset;
return (0);
}
if (com == FIONBIO || com == FIOASYNC) /* XXX */
return (0); /* XXX */
/* fall into ... */
default:
#if 0
return (ENOTTY);
#endif
case VFIFO:
case VCHR:
case VBLK:
error = VOP_IOCTL(vp, com, data, fp->f_flag, p->p_ucred, p);
if (error == 0 && com == TIOCSCTTY) {
/* Do nothing if reassigning same control tty */
if (p->p_session->s_ttyvp == vp)
return (0);
/* Get rid of reference to old control tty */
if (p->p_session->s_ttyvp)
vrele(p->p_session->s_ttyvp);
p->p_session->s_ttyvp = vp;
VREF(vp);
}
return (error);
}
}
/*
* File table vnode poll routine.
*/
static int
vn_poll(fp, events, cred, p)
struct file *fp;
int events;
struct ucred *cred;
struct proc *p;
{
return (VOP_POLL(((struct vnode *)fp->f_data), events, cred, p));
}
/*
* Check that the vnode is still valid, and if so
* acquire requested lock.
*/
int
vn_lock(vp, flags, p)
struct vnode *vp;
int flags;
struct proc *p;
{
int error;
do {
if ((flags & LK_INTERLOCK) == 0)
simple_lock(&vp->v_interlock);
if (vp->v_flag & VXLOCK) {
vp->v_flag |= VXWANT;
simple_unlock(&vp->v_interlock);
tsleep((caddr_t)vp, PINOD, "vn_lock", 0);
error = ENOENT;
} else {
error = VOP_LOCK(vp, flags | LK_NOPAUSE | LK_INTERLOCK, p);
if (error == 0)
return (error);
}
flags &= ~LK_INTERLOCK;
} while (flags & LK_RETRY);
return (error);
}
/*
* File table vnode close routine.
*/
static int
vn_closefile(fp, p)
struct file *fp;
struct proc *p;
{
return (vn_close(((struct vnode *)fp->f_data), fp->f_flag,
fp->f_cred, p));
}