* Copyright (c) 1989 The Regents of the University of California.
* %sccs.include.redist.c%
* @(#)vfs_subr.c 7.81 (Berkeley) %G%
* External virtual filesystem routines
enum vtype iftovt_tab
[16] = {
VNON
, VFIFO
, VCHR
, VNON
, VDIR
, VNON
, VBLK
, VNON
,
VREG
, VNON
, VLNK
, VNON
, VSOCK
, VNON
, VNON
, VBAD
,
0, S_IFREG
, S_IFDIR
, S_IFBLK
, S_IFCHR
, S_IFLNK
,
S_IFSOCK
, S_IFIFO
, S_IFMT
,
* Remove a mount point from the list of mounted filesystems.
* Unmount of the root is illegal.
register struct mount
*mp
;
panic("vfs_remove: unmounting root");
mp
->mnt_prev
->mnt_next
= mp
->mnt_next
;
mp
->mnt_next
->mnt_prev
= mp
->mnt_prev
;
mp
->mnt_vnodecovered
->v_mountedhere
= (struct mount
*)0;
* Used to prevent access to it while mounting and unmounting.
register struct mount
*mp
;
while(mp
->mnt_flag
& MNT_MLOCK
) {
mp
->mnt_flag
|= MNT_MWAIT
;
sleep((caddr_t
)mp
, PVFS
);
mp
->mnt_flag
|= MNT_MLOCK
;
* Unlock a locked filesystem.
* Panic if filesystem is not locked.
register struct mount
*mp
;
if ((mp
->mnt_flag
& MNT_MLOCK
) == 0)
panic("vfs_unlock: not locked");
mp
->mnt_flag
&= ~MNT_MLOCK
;
if (mp
->mnt_flag
& MNT_MWAIT
) {
mp
->mnt_flag
&= ~MNT_MWAIT
;
* Mark a mount point as busy.
* Used to synchronize access and to delay unmounting.
register struct mount
*mp
;
while(mp
->mnt_flag
& MNT_MPBUSY
) {
mp
->mnt_flag
|= MNT_MPWANT
;
sleep((caddr_t
)&mp
->mnt_flag
, PVFS
);
if (mp
->mnt_flag
& MNT_UNMOUNT
)
mp
->mnt_flag
|= MNT_MPBUSY
;
* Free a busy filesystem.
* Panic if filesystem is not busy.
register struct mount
*mp
;
if ((mp
->mnt_flag
& MNT_MPBUSY
) == 0)
panic("vfs_unbusy: not busy");
mp
->mnt_flag
&= ~MNT_MPBUSY
;
if (mp
->mnt_flag
& MNT_MPWANT
) {
mp
->mnt_flag
&= ~MNT_MPWANT
;
wakeup((caddr_t
)&mp
->mnt_flag
);
* Lookup a mount point by filesystem identifier.
register struct mount
*mp
;
if (mp
->mnt_stat
.f_fsid
.val
[0] == fsid
->val
[0] &&
mp
->mnt_stat
.f_fsid
.val
[1] == fsid
->val
[1]) {
return ((struct mount
*)0);
static u_short xxxfs_mntid
;
mp
->mnt_stat
.f_fsid
.val
[0] = makedev(nblkdev
+ 11, 0); /* XXX */
mp
->mnt_stat
.f_fsid
.val
[1] = mtype
;
tfsid
.val
[0] = makedev(nblkdev
, xxxfs_mntid
);
mp
->mnt_stat
.f_fsid
.val
[0] = tfsid
.val
[0];
* Set vnode attributes to VNOVAL
register struct vattr
*vap
;
vap
->va_size
= vap
->va_bytes
= VNOVAL
;
vap
->va_mode
= vap
->va_nlink
= vap
->va_uid
= vap
->va_gid
=
vap
->va_fsid
= vap
->va_fileid
=
vap
->va_blocksize
= vap
->va_rdev
=
vap
->va_atime
.ts_sec
= vap
->va_atime
.ts_nsec
=
vap
->va_mtime
.ts_sec
= vap
->va_mtime
.ts_nsec
=
vap
->va_ctime
.ts_sec
= vap
->va_ctime
.ts_nsec
=
vap
->va_flags
= vap
->va_gen
= VNOVAL
;
* Routines having to do with the management of the vnode table.
extern struct vnode
*vfreeh
, **vfreet
;
extern int (**dead_vnodeop_p
)();
extern int (**spec_vnodeop_p
)();
extern struct vattr va_null
;
* Return the next vnode from the free list.
getnewvnode(tag
, mp
, vops
, vpp
)
register struct vnode
*vp
, *vq
;
if ((vfreeh
== NULL
&& numvnodes
< 2 * desiredvnodes
) ||
numvnodes
< desiredvnodes
) {
vp
= (struct vnode
*)malloc((u_long
)sizeof *vp
,
bzero((char *)vp
, sizeof *vp
);
if ((vp
= vfreeh
) == NULL
) {
panic("free vnode isn't");
panic("cleaned vnode isn't");
* Move a vnode from one mount queue to another.
register struct vnode
*vp
;
register struct mount
*mp
;
register struct vnode
*vq
;
* Delete from old mount point vnode list, if on one.
vq
->v_mountb
= vp
->v_mountb
;
* Insert into list of vnodes for the new mount point, if available.
vq
->v_mountb
= &vp
->v_mountf
;
vp
->v_mountb
= &mp
->mnt_mounth
;
* Update outstanding I/O count and do wakeup if requested.
register struct vnode
*vp
;
bp
->b_dirtyoff
= bp
->b_dirtyend
= 0;
if ((vp
->v_flag
& VBWAIT
) && vp
->v_numoutput
<= 0) {
panic("vwakeup: neg numoutput");
wakeup((caddr_t
)&vp
->v_numoutput
);
* Flush out and invalidate all buffers associated with a vnode.
* Called with the underlying object locked.
vinvalbuf(vp
, save
, cred
, p
)
register struct vnode
*vp
;
if (error
= VOP_FSYNC(vp
, cred
, MNT_WAIT
, p
))
if (vp
->v_dirtyblkhd
!= NULL
)
panic("vinvalbuf: dirty bufs");
if (blist
= vp
->v_cleanblkhd
)
else if (blist
= vp
->v_dirtyblkhd
)
for (bp
= blist
; bp
; bp
= nbp
) {
if (bp
->b_flags
& B_BUSY
) {
sleep((caddr_t
)bp
, PRIBIO
+ 1);
reassignbuf(bp
, bp
->b_vp
);
if (vp
->v_dirtyblkhd
|| vp
->v_cleanblkhd
)
panic("vinvalbuf: flush failed");
* Associate a buffer with a vnode.
register struct vnode
*vp
;
register struct vnode
*vq
;
panic("bgetvp: not free");
if (vp
->v_type
== VBLK
|| vp
->v_type
== VCHR
)
* Insert onto list for new vnode.
if (bq
= vp
->v_cleanblkhd
)
bq
->b_blockb
= &bp
->b_blockf
;
bp
->b_blockb
= &vp
->v_cleanblkhd
;
* Disassociate a buffer from a vnode.
if (bp
->b_vp
== (struct vnode
*) 0)
* Delete from old vnode list, if on one.
bq
->b_blockb
= bp
->b_blockb
;
bp
->b_vp
= (struct vnode
*) 0;
* Reassign a buffer from one vnode to another.
* Used to assign file specific control information
* (indirect blocks) to the vnode to which they belong.
register struct vnode
*newvp
;
register struct buf
*bq
, **listheadp
;
printf("reassignbuf: NULL");
* Delete from old vnode list, if on one.
bq
->b_blockb
= bp
->b_blockb
;
* If dirty, put on list of dirty buffers;
* otherwise insert onto list of clean buffers.
if (bp
->b_flags
& B_DELWRI
)
listheadp
= &newvp
->v_dirtyblkhd
;
listheadp
= &newvp
->v_cleanblkhd
;
bq
->b_blockb
= &bp
->b_blockf
;
bp
->b_blockb
= listheadp
;
* Create a vnode for a block device.
* Used for root filesystem, argdev, and swap areas.
* Also used for memory file system special devices.
register struct vnode
*vp
;
error
= getnewvnode(VT_NON
, (struct mount
*)0, spec_vnodeop_p
, &nvp
);
if (nvp
= checkalias(vp
, dev
, (struct mount
*)0)) {
* Check to see if the new vnode represents a special device
* for which we already have a vnode (either because of
* bdevvp() or because of a different vnode representing
* the same block device). If such an alias exists, deallocate
* the existing contents and return the aliased vnode. The
* caller is responsible for filling it with its new contents.
checkalias(nvp
, nvp_rdev
, mp
)
register struct vnode
*nvp
;
register struct vnode
*vp
;
if (nvp
->v_type
!= VBLK
&& nvp
->v_type
!= VCHR
)
vpp
= &speclisth
[SPECHASH(nvp_rdev
)];
for (vp
= *vpp
; vp
; vp
= vp
->v_specnext
) {
if (nvp_rdev
!= vp
->v_rdev
|| nvp
->v_type
!= vp
->v_type
)
* Alias, but not in use, so flush it out.
if (vp
->v_usecount
== 0) {
if (vp
== NULL
|| vp
->v_tag
!= VT_NON
) {
MALLOC(nvp
->v_specinfo
, struct specinfo
*,
sizeof(struct specinfo
), M_VNODE
, M_WAITOK
);
* Grab a particular vnode from the free list, increment its
* reference count and lock it. The vnode lock bit is set the
* vnode is being eliminated in vgone. The process is awakened
* when the transition is completed, and an error returned to
* indicate that the vnode is no longer usable (possibly having
* been changed to a new file system type).
register struct vnode
*vp
;
register struct vnode
*vq
;
if (vp
->v_flag
& VXLOCK
) {
sleep((caddr_t
)vp
, PINOD
);
if (vp
->v_usecount
== 0) {
vq
->v_freeb
= vp
->v_freeb
;
* Vnode reference, just increment the count
if (vp
->v_type
!= VBLK
&& curproc
)
* vput(), just unlock and vrele()
register struct vnode
*vp
;
* If count drops to zero, call inactive routine and return to freelist.
register struct vnode
*vp
;
if (vp
->v_type
!= VBLK
&& curproc
)
if (vp
->v_usecount
!= 0 || vp
->v_writecount
!= 0) {
vprint("vrele: bad ref count", vp
);
* Page or buffer structure gets a reference.
register struct vnode
*vp
;
* Page or buffer structure frees a reference.
register struct vnode
*vp
;
panic("holdrele: holdcnt");
* Remove any vnodes in the vnode table belonging to mount point mp.
* If MNT_NOFORCE is specified, there should not be any active ones,
* return error if any are found (nb: this is a user error, not a
* system error). If MNT_FORCE is specified, detach any active vnodes
int busyprt
= 0; /* patch to print out busy vnodes */
vflush(mp
, skipvp
, flags
)
register struct vnode
*vp
, *nvp
;
if ((mp
->mnt_flag
& MNT_MPBUSY
) == 0)
panic("vflush: not busy");
for (vp
= mp
->mnt_mounth
; vp
; vp
= nvp
) {
* Skip over a selected vnode.
* Skip over a vnodes marked VSYSTEM.
if ((flags
& SKIPSYSTEM
) && (vp
->v_flag
& VSYSTEM
))
* With v_usecount == 0, all we need to do is clear
* out the vnode data structures and we are done.
if (vp
->v_usecount
== 0) {
* For block or character devices, revert to an
* anonymous device. For all other files, just kill them.
if (flags
& FORCECLOSE
) {
if (vp
->v_type
!= VBLK
&& vp
->v_type
!= VCHR
) {
vp
->v_op
= spec_vnodeop_p
;
insmntque(vp
, (struct mount
*)0);
vprint("vflush: busy vnode", vp
);
* Disassociate the underlying file system from a vnode.
register struct vnode
*vp
;
* Check to see if the vnode is in use.
* If so we have to reference it before we clean it out
* so that its count cannot fall to zero and generate a
* race against ourselves to recycle it.
if (active
= vp
->v_usecount
)
* Prevent the vnode from being recycled or
* brought into use while we clean it out.
panic("vclean: deadlock");
* Even if the count is zero, the VOP_INACTIVE routine may still
* have the object locked while it cleans it out. The VOP_LOCK
* ensures that the VOP_INACTIVE routine is done with its work.
* For active vnodes, it ensures that no other activity can
* occur while the buffer list is being cleaned out.
vinvalbuf(vp
, 1, NOCRED
, NULL
);
* Prevent any further operations on the vnode from
* being passed through to the old file system.
vp
->v_op
= dead_vnodeop_p
;
* If purging an active vnode, it must be unlocked, closed,
* and deactivated before being reclaimed.
vop_unlock_a
.a_desc
= VDESC(vop_unlock
);
VOCALL(origops
,VOFFSET(vop_unlock
),&vop_unlock_a
);
* Note: these next two calls imply
* that vop_close and vop_inactive implementations
* cannot count on the ops vector being correctly
vop_close_a
.a_desc
= VDESC(vop_close
);
vop_close_a
.a_fflag
= IO_NDELAY
;
VOCALL(origops
,VOFFSET(vop_close
),&vop_close_a
);
vop_inactive_a
.a_desc
= VDESC(vop_inactive
);
vop_inactive_a
.a_vp
= vp
;
VOCALL(origops
,VOFFSET(vop_inactive
),&vop_inactive_a
);
vop_reclaim_a
.a_desc
= VDESC(vop_reclaim
);
if (VOCALL(origops
,VOFFSET(vop_reclaim
),&vop_reclaim_a
))
panic("vclean: cannot reclaim");
* Done with purge, notify sleepers in vget of the grim news.
if (vp
->v_flag
& VXWANT
) {
* Eliminate all activity associated with the requested vnode
* and with all vnodes aliased to the requested vnode.
register struct vnode
*vp
;
register struct vnode
*vq
;
if (vp
->v_flag
& VALIASED
) {
* If a vgone (or vclean) is already in progress,
* wait until it is done and return.
if (vp
->v_flag
& VXLOCK
) {
sleep((caddr_t
)vp
, PINOD
);
* Ensure that vp will not be vgone'd while we
* are eliminating its aliases.
while (vp
->v_flag
& VALIASED
) {
for (vq
= *vp
->v_hashchain
; vq
; vq
= vq
->v_specnext
) {
if (vq
->v_rdev
!= vp
->v_rdev
||
vq
->v_type
!= vp
->v_type
|| vp
== vq
)
* Remove the lock so that vgone below will
* really eliminate the vnode after which time
* vgone will awaken any sleepers.
* Eliminate all activity associated with a vnode
* in preparation for reuse.
register struct vnode
*vp
;
register struct vnode
*vq
;
* If a vgone (or vclean) is already in progress,
* wait until it is done and return.
if (vp
->v_flag
& VXLOCK
) {
sleep((caddr_t
)vp
, PINOD
);
* Clean out the filesystem specific data.
* Delete from old mount point vnode list, if on one.
vq
->v_mountb
= vp
->v_mountb
;
* If special device, remove it from special device alias list.
if (vp
->v_type
== VBLK
|| vp
->v_type
== VCHR
) {
if (*vp
->v_hashchain
== vp
) {
*vp
->v_hashchain
= vp
->v_specnext
;
for (vq
= *vp
->v_hashchain
; vq
; vq
= vq
->v_specnext
) {
if (vq
->v_specnext
!= vp
)
vq
->v_specnext
= vp
->v_specnext
;
if (vp
->v_flag
& VALIASED
) {
for (vq
= *vp
->v_hashchain
; vq
; vq
= vq
->v_specnext
) {
if (vq
->v_rdev
!= vp
->v_rdev
||
vq
->v_type
!= vp
->v_type
)
FREE(vp
->v_specinfo
, M_VNODE
);
* If it is on the freelist, move it to the head of the list.
vq
->v_freeb
= vp
->v_freeb
;
vfreeh
->v_freeb
= &vp
->v_freef
;
* Lookup a vnode by device number.
register struct vnode
*vp
;
for (vp
= speclisth
[SPECHASH(dev
)]; vp
; vp
= vp
->v_specnext
) {
if (dev
!= vp
->v_rdev
|| type
!= vp
->v_type
)
* Calculate the total number of references to a special device.
register struct vnode
*vp
;
register struct vnode
*vq
;
if ((vp
->v_flag
& VALIASED
) == 0)
for (count
= 0, vq
= *vp
->v_hashchain
; vq
; vq
= vq
->v_specnext
) {
if (vq
->v_rdev
!= vp
->v_rdev
|| vq
->v_type
!= vp
->v_type
)
* Alias, but not in use, so flush it out.
if (vq
->v_usecount
== 0) {
* Print out a description of a vnode.
static char *typename
[] =
{ "VNON", "VREG", "VDIR", "VBLK", "VCHR", "VLNK", "VSOCK", "VFIFO", "VBAD" };
register struct vnode
*vp
;
printf("type %s, usecount %d, writecount %d, refcount %d,",
typename
[vp
->v_type
], vp
->v_usecount
, vp
->v_writecount
,
if (vp
->v_flag
& VSYSTEM
)
if (vp
->v_flag
& VALIASED
)
strcat(buf
, "|VALIASED");
printf(" flags (%s)", &buf
[1]);
* List all of the locked vnodes in the system.
* Called when debugging the kernel.
register struct mount
*mp
;
register struct vnode
*vp
;
printf("Locked vnodes\n");
for (vp
= mp
->mnt_mounth
; vp
; vp
= vp
->v_mountf
)
#define KINFO_VNODESLOP 10
* Dump vnode list (via kinfo).
* Copyout address of vnode followed by vnode.
kinfo_vnode(op
, where
, acopysize
, arg
, aneeded
)
int *acopysize
, arg
, *aneeded
;
register struct mount
*mp
= rootfs
;
register char *bp
= where
, *savebp
;
#define VPTRSZ sizeof (struct vnode *)
#define VNODESZ sizeof (struct vnode)
*aneeded
= (numvnodes
+ KINFO_VNODESLOP
) * (VPTRSZ
+ VNODESZ
);
ewhere
= where
+ *acopysize
;
for (vp
= mp
->mnt_mounth
; vp
; vp
= vp
->v_mountf
) {
* Check that the vp is still associated with
* this filesystem. RACE: could have been
* recycled onto the same filesystem.
printf("kinfo: vp changed\n");
if ((bp
+ VPTRSZ
+ VNODESZ
<= ewhere
) &&
((error
= copyout((caddr_t
)&vp
, bp
, VPTRSZ
)) ||
(error
= copyout((caddr_t
)vp
, bp
+ VPTRSZ
,
*acopysize
= ewhere
- where
;