Files
seta75D 2e8a93c394 Init
2021-10-11 18:20:23 -03:00

941 lines
21 KiB
C

#ident "@(#)vfs.c 1.1 92/07/30 SMI"
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
#include <sys/param.h>
#include <sys/user.h>
#include <sys/uio.h>
#include <sys/file.h>
#include <sys/vfs.h>
#include <sys/vfs_stat.h>
#include <sys/buf.h>
#include <sys/vm.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/systm.h>
#include <sys/termios.h>
#include <sys/time.h>
#include <sys/kernel.h>
#undef NFSSERVER
#include <sys/mount.h>
#include <sys/pathname.h>
#include <sys/unistd.h>
#include <sys/vnode.h>
#ifdef UFS
#include <ufs/inode.h>
#endif
#include <sys/bootconf.h>
#include <vm/page.h>
#include <vm/swap.h>
/*
* vfs global data
*/
struct vnode *rootdir; /* pointer to root vnode */
struct vfs *rootvfs; /* pointer to root vfs. This is */
/* also the head of the vfs list */
/*
* Convert mode formats to vnode types
*/
enum vtype mftovt_tab[] = {
VFIFO, VCHR, VDIR, VBLK, VREG, VLNK, VSOCK, VBAD
};
/*
* System calls
*/
/*
* Translate mount ype numbers from old order to new order for backward
* compatability with old mount command
*/
static int fixtype[] =
{ MOUNT_UFS, MOUNT_NFS, MOUNT_PC, MOUNT_UFS};
/*
* mount system call
*/
mount(uap)
register struct a {
char *type;
char *dir;
int flags;
caddr_t data;
} *uap;
{
struct pathname pn;
struct vnode *vp;
struct vfs *vfsp;
struct vfssw *vs;
extern struct vnodeops ufs_vnodeops;
int saved_flag;
/*
* Must be super user
*/
if (!suser())
return;
/*
* Get vnode to be covered
*/
u.u_error = lookupname(uap->dir, UIO_USERSPACE, FOLLOW_LINK,
(struct vnode **)0, &vp);
if (u.u_error)
return;
if (vp->v_vfsp->vfs_flag & VFS_NOSUB) {
VN_RELE(vp);
u.u_error = EREMOTE;
return;
}
dnlc_purge();
if ((uap->flags & M_SYS5) && !(uap->flags & M_REMOUNT)) {
/*
* This test is sort of bogus as you can cover up a
* directory by mounting at the next higher level spot
* in the directory tree. If we want to keep this
* somewhat bogus test, then we will need to add
* a call to a routine which gets rid of any current
* pages / segmap mappings for this vnode. This
* routine will need to exist for unmount anyway.
*/
if (vp->v_count != 1) {
VN_RELE(vp);
u.u_error = EBUSY;
return;
}
}
if (vp->v_type != VDIR) {
VN_RELE(vp);
u.u_error = ENOTDIR;
return;
}
if ((uap->flags & M_NEWTYPE) || (uap->flags & M_REMOUNT)) {
u.u_error = pn_get(uap->type, UIO_USERSPACE, &pn);
if (u.u_error) {
VN_RELE(vp);
return;
}
for (vs = vfssw; vs < vfsNVFS; vs++) {
if (vs->vsw_name != 0 &&
strcmp(pn.pn_path, vs->vsw_name) == 0) {
break;
}
}
if (vs == vfsNVFS) {
u.u_error = ENODEV;
VN_RELE(vp);
pn_free(&pn);
return;
}
pn_free(&pn);
} else if ((int)uap->type >= sizeof (fixtype)/sizeof (int) ||
(vs = &vfssw[fixtype[(int)uap->type]])->vsw_ops == 0) {
u.u_error = ENODEV;
VN_RELE(vp);
return;
}
/*
* If this is a remount, we don't want to create a new VFS.
* Instead, we pass the existing one with a remount flag.
*/
if (uap->flags & M_REMOUNT) {
for (vfsp = rootvfs;
vfsp != (struct vfs *)0; vfsp = vfsp->vfs_next) {
if ((vp->v_vfsp == vfsp) && (vp->v_flag & VROOT) &&
(vp->v_vfsmountedhere == (struct vfs *)0))
break;
}
if (vfsp == (struct vfs *)0) {
printf ("mount: no entry\n");
u.u_error = ENOENT;
VN_RELE(vp);
return;
}
u.u_error = vfs_lock(vfsp);
if (u.u_error) {
VN_RELE(vp);
return;
}
u.u_error = pn_get(uap->dir, UIO_USERSPACE, &pn);
if (u.u_error) {
VN_RELE(vp);
vfs_unlock(vfsp);
return;
}
/*
* Disallow making file systems read-only.
* Ignore other flags.
*/
if (uap->flags & M_RDONLY) {
printf ("mount: can't remount ro\n");
u.u_error = EINVAL;
vfs_unlock(vfsp);
VN_RELE(vp);
pn_free(&pn);
return;
}
saved_flag = vfsp->vfs_flag;
vfsp->vfs_flag |= VFS_REMOUNT;
vfsp->vfs_flag &= ~VFS_RDONLY;
#ifdef UFS
if (vp->v_op == &ufs_vnodeops)
ILOCK(VTOI(vp));
#endif
} else {
u.u_error = pn_get(uap->dir, UIO_USERSPACE, &pn);
if (u.u_error) {
VN_RELE(vp);
return;
}
vfsp = (struct vfs *)new_kmem_alloc(
sizeof (struct vfs), KMEM_SLEEP);
VFS_INIT(vfsp, vs->vsw_ops, (caddr_t)0);
/*
* Mount the filesystem.
* Lock covered vnode (XXX this currently only works if
* it is type ufs)
*/
#ifdef UFS
if (vp->v_op == &ufs_vnodeops)
ILOCK(VTOI(vp));
#endif
u.u_error = vfs_add(vp, vfsp, uap->flags);
if (u.u_error) {
#ifdef UFS
if (vp->v_op == &ufs_vnodeops)
IUNLOCK(VTOI(vp));
#endif
pn_free(&pn);
kmem_free((caddr_t)vfsp, sizeof (struct vfs));
VN_RELE(vp);
return;
}
}
u.u_error = VFS_MOUNT(vfsp, pn.pn_path, uap->data);
#ifdef UFS
if (vp->v_op == &ufs_vnodeops)
IUNLOCK(VTOI(vp));
#endif
pn_free(&pn);
if (!u.u_error) {
vfs_unlock(vfsp);
if (uap->flags & M_REMOUNT) {
vfsp->vfs_flag &= ~VFS_REMOUNT;
VN_RELE(vp);
}
#ifdef VFSSTATS
else {
struct vfsstats *vfsstats;
vfsstats = (struct vfsstats *)new_kmem_zalloc(
sizeof (struct vfsstats), KMEM_SLEEP);
vfsstats->vs_time = time.tv_sec;
vfsp->vfs_stats = (caddr_t)vfsstats;
}
#endif
} else {
/*
* There was an error, return filesystem to previous
* state if remount, otherwise clean up vfsp
*/
if (uap->flags & M_REMOUNT) {
vfsp->vfs_flag = saved_flag;
vfs_unlock(vfsp);
} else {
vfs_remove(vfsp);
kmem_free((caddr_t)vfsp, sizeof (struct vfs));
}
VN_RELE(vp);
}
}
/*
* Sync system call - sync all vfs types
*/
sync()
{
register struct vfssw *vfsswp;
for (vfsswp = vfssw; vfsswp < vfsNVFS; vfsswp++) {
if (vfsswp->vsw_ops == (struct vfsops *)NULL)
continue; /* not configured in */
/*
* Call the vfs sync operation with a NULL
* pointer to force all vfs's of the given
* type to be flushed back to main storage.
*/
(*vfsswp->vsw_ops->vfs_sync)((struct vfs *)NULL);
}
}
/*
* get pathconf info
*/
pathconf(uap)
struct a {
char *path;
int what;
} *uap;
{
struct vnode *vp;
int ans = -1;
u.u_error = lookupname(uap->path, UIO_USERSPACE, FOLLOW_LINK,
(struct vnode **)0, &vp);
if (u.u_error)
return;
u.u_error = VOP_CNTL(vp, uap->what, 0, &ans, 0, CNTL_INT32);
u.u_rval1 = ans;
VN_RELE(vp);
}
fpathconf(uap)
struct a {
int fd;
int what;
} *uap;
{
struct file *fp;
int ans = -1;
/*
* Forgive me, oh lord, for surely I sin.
* Pipes (unnamed) have no vnode_ops,
* so I have to catch this here.
*/
if (uap->what == _PC_PIPE_BUF) {
if ((fp = getf(uap->fd)) == NULL)
return;
if (fp->f_type == DTYPE_SOCKET &&
((struct socket *)fp->f_data)->so_state & SS_PIPE) {
u.u_rval1 = PIPE_BUF;
return;
}
}
u.u_error = getvnodefp(uap->fd, &fp);
if (u.u_error == 0) {
struct vnode *vp = (struct vnode*)fp->f_data;
u.u_error = VOP_CNTL(vp, uap->what, 0, &ans, 0, CNTL_INT32);
}
u.u_rval1 = ans;
}
/*
* get filesystem statistics
*/
statfs(uap)
struct a {
char *path;
struct statfs *buf;
} *uap;
{
struct vnode *vp;
u.u_error = lookupname(uap->path, UIO_USERSPACE, FOLLOW_LINK,
(struct vnode **)0, &vp);
if (u.u_error)
return;
cstatfs(vp->v_vfsp, uap->buf);
VN_RELE(vp);
}
fstatfs(uap)
struct a {
int fd;
struct statfs *buf;
} *uap;
{
struct file *fp;
u.u_error = getvnodefp(uap->fd, &fp);
if (u.u_error == 0)
cstatfs(((struct vnode *)fp->f_data)->v_vfsp, uap->buf);
}
cstatfs(vfsp, ubuf)
struct vfs *vfsp;
struct statfs *ubuf;
{
struct statfs sb;
bzero((caddr_t)&sb, sizeof (sb));
u.u_error = VFS_STATFS(vfsp, &sb);
if (u.u_error)
return;
/*
* XXX FIX IN 5.0
*
* Ugly hack to cater to programs that multiply # of blocks
* by block size and thus overflow when the FS size exceeds 2GB.
* Copy the true size information to the spares, then put a
* ceiling on the returned sizes.
*/
#define INT_MAX 0x7fffffff
sb.f_spare[0] = sb.f_blocks;
sb.f_spare[1] = sb.f_bfree;
sb.f_spare[2] = sb.f_bavail;
if (sb.f_bsize != 0) {
sb.f_blocks = MIN(sb.f_blocks, INT_MAX / sb.f_bsize);
sb.f_bfree = MIN(sb.f_bfree, INT_MAX / sb.f_bsize);
sb.f_bavail = MIN(sb.f_bavail, INT_MAX / sb.f_bsize);
}
u.u_error = copyout((caddr_t)&sb, (caddr_t)ubuf, sizeof (sb));
}
/*
* Unmount system call.
*
* Note: unmount takes a path to the vnode mounted on as argument,
* not special file (as before).
*/
unmount(uap)
struct a {
char *pathp;
} *uap;
{
struct vnode *fsrootvp;
register struct vfs *vfsp;
if (!suser())
return;
/*
* lookup root of fs
*/
u.u_error = lookupname(uap->pathp, UIO_USERSPACE, FOLLOW_LINK,
(struct vnode **)0, &fsrootvp);
if (u.u_error)
return;
/*
* make sure this is a root
*/
if ((fsrootvp->v_flag & VROOT) == 0) {
u.u_error = EINVAL;
VN_RELE(fsrootvp);
return;
}
/*
* get vfs
*/
vfsp = fsrootvp->v_vfsp;
VN_RELE(fsrootvp);
/*
* Do the unmount.
*/
dounmount(vfsp);
}
/*
* XXX - subroutine so the old 4.2/S5-style
* "umount" call can use this code as well.
*/
dounmount(vfsp)
register struct vfs *vfsp;
{
register struct vnode *coveredvp;
/*
* Get covered vnode.
*/
coveredvp = vfsp->vfs_vnodecovered;
/*
* lock vnode to maintain fs status quo during unmount
*/
u.u_error = vfs_lock(vfsp);
if (u.u_error)
return;
/*
* XXX - need to have to have something to remove all cached
* pages and segmap mappings to vnodes in this vfs.
*/
dnlc_purge(); /* remove dnlc entries for this file sys */
VFS_SYNC(vfsp);
u.u_error = VFS_UNMOUNT(vfsp);
if (u.u_error) {
vfs_unlock(vfsp);
} else {
vfs_remove(vfsp);
VN_RELE(coveredvp);
#ifdef VFSSTATS
if (vfsp->vfs_stats) {
kmem_free((caddr_t)vfsp->vfs_stats,
sizeof (struct vfsstats));
}
#endif
kmem_free((caddr_t)vfsp, sizeof (*vfsp));
}
}
/*
* External routines
*/
/*
* vfs_mountroot is called by main (init_main.c) to
* mount the root filesystem.
*/
void
vfs_mountroot()
{
register int error;
struct vfssw *vsw;
extern char *strcpy();
rootvfs = (struct vfs *)new_kmem_alloc(sizeof (struct vfs), KMEM_SLEEP);
retry:
vsw = getfstype("root", rootfs.bo_fstype);
if (vsw) {
VFS_INIT(rootvfs, vsw->vsw_ops, (caddr_t)0);
error = VFS_MOUNTROOT(rootvfs, &rootvp, rootfs.bo_name);
if (error) {
printf("Couldn't mount root from %s, error %d\n",
vsw->vsw_name, error);
goto retry;
}
} else {
/*
* Step through the filesystem types til we find one that
* will mount a root filesystem. If error panic.
*/
for (vsw = vfssw; vsw < vfsNVFS; vsw++) {
if (vsw->vsw_ops) {
VFS_INIT(rootvfs, vsw->vsw_ops, (caddr_t)0);
error = VFS_MOUNTROOT(rootvfs, &rootvp,
rootfs.bo_name);
if (!error) {
break;
}
}
}
}
if (error) {
panic("vfs_mountroot: cannot mount root");
}
#ifdef VFSSTATS
{
struct vfsstats *vfsstats;
vfsstats = (struct vfsstats *)
new_kmem_zalloc(sizeof (struct vfsstats), KMEM_SLEEP);
vfsstats->vs_time = time.tv_sec;
rootvfs->vfs_stats = (caddr_t)vfsstats;
}
#endif
/*
* Get vnode for '/'.
* Setup rootdir, u.u_rdir and u.u_cdir to point to it.
* These are used by lookuppn so that it knows where
* to start from '/' or '.'.
*/
error = VFS_ROOT(rootvfs, &rootdir);
if (error)
panic("vfs_mountroot: no root vnode");
u.u_cdir = rootdir;
VN_HOLD(u.u_cdir);
u.u_rdir = NULL;
rootfs.bo_vp = rootvp;
(void) strcpy(rootfs.bo_fstype, vsw->vsw_name);
rootfs.bo_size = 0;
rootfs.bo_flags = BO_VALID;
printf("root on %s fstype %s\n", rootfs.bo_name, rootfs.bo_fstype);
}
/*
* vfs_add is called by a specific filesystem's mount routine to add
* the new vfs into the vfs list and to cover the mounted on vnode.
* The vfs is also locked so that lookuppn will not venture into the
* covered vnodes subtree. coveredvp is zero if this is the root.
*/
int
vfs_add(coveredvp, vfsp, mflag)
register struct vnode *coveredvp;
register struct vfs *vfsp;
int mflag;
{
register int error;
error = vfs_lock(vfsp);
if (error)
return (error);
if (coveredvp != (struct vnode *)0) {
/*
* Return EBUSY if the covered vp is already mounted on.
*/
if (coveredvp->v_vfsmountedhere != (struct vfs *)0) {
vfs_unlock(vfsp);
return (EBUSY);
}
/*
* Put the new vfs on the vfs list after root.
* Point the covered vnode at the new vfs so lookuppn
* (vfs_lookup.c) can work its way into the new file system.
*/
vfsp->vfs_next = rootvfs->vfs_next;
rootvfs->vfs_next = vfsp;
coveredvp->v_vfsmountedhere = vfsp;
} else {
/*
* This is the root of the whole world.
*/
rootvfs = vfsp;
vfsp->vfs_next = (struct vfs *)0;
}
vfsp->vfs_vnodecovered = coveredvp;
if (mflag & M_RDONLY) {
vfsp->vfs_flag |= VFS_RDONLY;
} else {
vfsp->vfs_flag &= ~VFS_RDONLY;
}
if (mflag & M_NOSUID) {
vfsp->vfs_flag |= VFS_NOSUID;
} else {
vfsp->vfs_flag &= ~VFS_NOSUID;
}
if (mflag & M_GRPID) {
vfsp->vfs_flag |= VFS_GRPID;
} else {
vfsp->vfs_flag &= ~VFS_GRPID;
}
if (mflag & M_NOSUB) {
vfsp->vfs_flag |= VFS_NOSUB;
} else {
vfsp->vfs_flag &= ~VFS_NOSUB;
}
if (mflag & M_MULTI) {
vfsp->vfs_flag |= VFS_MULTI;
} else {
vfsp->vfs_flag &= ~VFS_MULTI;
}
return (0);
}
/*
* Remove a vfs from the vfs list, and destory pointers to it.
* Should be called by filesystem implementation after it determines
* that an unmount is legal but before it destroys the vfs.
*/
void
vfs_remove(vfsp)
register struct vfs *vfsp;
{
register struct vfs *tvfsp;
register struct vnode *vp;
/*
* can't unmount root. Should never happen, because fs will be busy.
*/
if (vfsp == rootvfs)
panic("vfs_remove: unmounting root");
for (tvfsp = rootvfs;
tvfsp != (struct vfs *)0; tvfsp = tvfsp->vfs_next) {
if (tvfsp->vfs_next == vfsp) {
/*
* remove vfs from list, unmount covered vp.
*/
tvfsp->vfs_next = vfsp->vfs_next;
vp = vfsp->vfs_vnodecovered;
vp->v_vfsmountedhere = (struct vfs *)0;
/*
* release lock and wakeup anybody waiting
*/
vfs_unlock(vfsp);
return;
}
}
/*
* can't find vfs to remove
*/
panic("vfs_remove: vfs not found");
}
/*
* Lock a filesystem to prevent access to it while mounting and unmounting.
* Returns error if already locked.
* XXX This totally inadequate for unmount right now - srk
*/
int
vfs_lock(vfsp)
register struct vfs *vfsp;
{
if (vfsp->vfs_flag & VFS_MLOCK)
return (EBUSY);
vfsp->vfs_flag |= VFS_MLOCK;
return (0);
}
/*
* Unlock a locked filesystem.
* Panics if not locked
*/
void
vfs_unlock(vfsp)
register struct vfs *vfsp;
{
if ((vfsp->vfs_flag & VFS_MLOCK) == 0)
panic("vfs_unlock");
vfsp->vfs_flag &= ~VFS_MLOCK;
/*
* Wake anybody waiting for the lock to clear
*/
if (vfsp->vfs_flag & VFS_MWAIT) {
vfsp->vfs_flag &= ~VFS_MWAIT;
wakeup((caddr_t)vfsp);
}
}
struct vfs *
getvfs(fsid)
fsid_t *fsid;
{
register struct vfs *vfsp;
for (vfsp = rootvfs; vfsp; vfsp = vfsp->vfs_next) {
if (vfsp->vfs_fsid.val[0] == fsid->val[0] &&
vfsp->vfs_fsid.val[1] == fsid->val[1]) {
break;
}
}
return (vfsp);
}
/*
* Take a file system ID of the sort that appears in a "struct vattr"
* and find the VFS that has that file system ID. This is done by
* finding the root directory for each VFS, finding its attributes,
* and comparing its file system ID with the given file system ID;
* if we have a match, we've found the right VFS. It's slow, but it
* works, and it's only used for the System V "ustat" call which is
* a crock anyway ("statfs" is better).
*/
int
vafsidtovfs(vafsid, vfspp)
long vafsid;
struct vfs **vfspp;
{
register struct vfs *vfsp;
struct vnode *rootvn; /* pointer to root vnode of vfs */
struct vattr vattr;
register int error;
for (vfsp = rootvfs; vfsp != (struct vfs *)0; vfsp = vfsp->vfs_next) {
error = VFS_ROOT(vfsp, &rootvn);
if (error)
if (error == ESTALE)
continue;
else
return (error);
error = VOP_GETATTR(rootvn, &vattr, u.u_cred);
VN_RELE(rootvn);
if (error)
if (error == ESTALE)
continue;
else
return (error);
if (vafsid == vattr.va_fsid) {
*vfspp = vfsp;
return (0);
}
}
return (EINVAL); /* not found */
}
/*
* The policies and routines below guarantee that a unique device number
* (as returned by the stat() system call) is associated with each mounted
* filesystem and they must be adhered to by all filesystem types.
* Local filesystems (i.e., those with associated Unix devices)
* do not use the routines below. Their device number is the device number
* of the Unix device associated with the filesystem. The range
* 0x0000 - 0x7fff is reserved for filesystems of this type.
* Non-local filesystems use the range 0x8000-0xffff. For the major
* device number, filesystem types which only require one major device number
* for all mounts use their reserved number which is 0x80 + the index of
* their filesystem type in the filesystem type table (vfs_conf.c). This
* number may be obtained by calling the routine vfs_fixedmajor(). Filesystem
* types requiring more than one major device number should obtain these
* numbers via calls to vfs_getmajor() and release them via calls to
* vfs_putmajor(). Minor device numbers are under the control of
* individual filesystem types. Any filesystem types that wishes may
* allocate and de-allocate minor device numbers using the routines
* vfs_getnum() and vfs_putnum() and its own private minor device number map.
*/
#define LOGBBY 3
#define MAJOR_MIN 128
#define MAJOR_MAX 255
#define NUM_MAJOR (MAJOR_MAX - MAJOR_MIN + 1)
#define MAJOR_MAPSIZE (NUM_MAJOR/NBBY)
static char devmap[MAJOR_MAPSIZE]; /* Bitmap storing used major device #s */
/*
* Return the next available major device number from the range 128-255.
* If a free number is available it is returned. Otherwise the reserved
* number for the filesystem type is returned.
*/
int
vfs_getmajor(vfsp)
struct vfs *vfsp;
{
register int i;
/* Load the filesystem-reserved numbers */
for (i = 0; i < (vfsNVFS - vfssw); i++)
devmap[i >> LOGBBY] |= (1 << (i - ((i >> LOGBBY) << LOGBBY)));
/* Get the first avalaible number */
i = vfs_getnum(devmap, MAJOR_MAPSIZE);
/* If none are available, return the reserved # for this fs type */
if (i == -1)
i = vfs_fixedmajor(vfsp);
else
i += MAJOR_MIN;
return (i);
}
/*
* Return the reserved major device number for this filesystem type
* defined as its position in the filesystem type table.
*/
int
vfs_fixedmajor(vfsp)
struct vfs* vfsp;
{
register struct vfssw *vs;
for (vs = vfssw; vs < vfsNVFS; vs++) {
if (vs->vsw_ops == vfsp->vfs_op)
break;
}
return ((vs - vfssw) + MAJOR_MIN);
}
/*
* Free the major number "num". "num" must have been allocated by a
* call to vfs_getmajor().
*/
void
vfs_putmajor(vfsp, num)
struct vfs *vfsp;
register int num;
{
num -= MAJOR_MIN;
if (vfsp->vfs_op == vfssw[num].vsw_ops)
return;
vfs_putnum(devmap, num);
}
/*
* Set and return the first free position from the bitmap "map".
* Return -1 if no position found.
*/
int
vfs_getnum(map, mapsize)
register char *map;
int mapsize;
{
register int i;
register char *mp;
for (mp = map; mp < &map[mapsize]; mp++) {
if (*mp != (char)0xff) {
for (i=0; i < NBBY; i++) {
if (!((*mp >> i) & 0x1)) {
*mp |= (1 << i);
return ((mp - map) * NBBY + i);
}
}
}
}
return (-1);
}
/*
* Clear the designated position "n" in bitmap "map".
*/
void
vfs_putnum(map, n)
register char *map;
int n;
{
if (n >= 0)
map[n >> LOGBBY] &= ~(1 << (n - ((n >> LOGBBY) << LOGBBY)));
}
/*
* "sync" all file systems, and return only when all writes have been
* completed. For use by the reboot code; it's verbose.
*/
void
vfs_syncall()
{
register struct buf *bp;
int iter;
int nppbusy, nbpbusy;
struct page *pp;
#ifdef UFS
struct inode *ip;
extern struct inode *ifreeh;
#endif
printf("syncing file systems... ");
/*
* XXX - do we need to have a way to have sync
* force the writeback in the case of keep'ed pages?
*/
sync();
#ifdef UFS
cleanup();
/*
* A bug in vm/vm_pvn.c (1042607) could prevent us from
* syncing all the dirty pages to an inode when we release
* the inode to the free list. So here we try to capture these
* dirty pages and sync them out before the final shutdown.
*/
for (ip = ifreeh; ip != NULL; ip = ip->i_freef) {
if (ip->i_fs == NULL || ITOV(ip)->v_pages == NULL ||
ITOV(ip)->v_type == VCHR || ITOV(ip)->v_type == VSOCK)
continue;
(void) VOP_PUTPAGE(ITOV(ip), 0, 0, B_ASYNC, (struct ucred *)0);
}
#endif
for (iter = 0; iter < 20; iter++) {
nbpbusy = 0;
for (bp = bufchain; bp != (struct buf *)0;
bp = bp->b_chain) {
if ((bp->b_flags & (B_BUSY|B_INVAL)) == B_BUSY)
nbpbusy++;
}
nppbusy = 0;
for (pp = epages; --pp >= pages; )
if (((pp->p_intrans && !pp->p_pagein) ||
pp->p_mod) && !pp->p_free &&
pp->p_vnode != NULL &&
!IS_SWAPVP(pp->p_vnode)) {
nppbusy++;
}
if (nbpbusy == 0 && nppbusy == 0)
break;
if (nbpbusy != 0)
printf("[%d] ", nbpbusy);
if (nppbusy != 0)
printf("%d ", nppbusy);
DELAY(36000 * iter);
cleanup();
}
if (iter < 20)
printf("done\n");
else
printf("give up!\n");
}