* Copyright (c) 1989 The Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* @(#)vfs_subr.c 7.29 (Berkeley) %G%
* External virtual filesystem routines
#define v_hashchain v_specinfo->si_hashchain
#define v_specnext v_specinfo->si_specnext
* 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
->m_prev
->m_next
= mp
->m_next
;
mp
->m_next
->m_prev
= mp
->m_prev
;
mp
->m_vnodecovered
->v_mountedhere
= (struct mount
*)0;
* Used to prevent access to it while mounting and unmounting.
register struct mount
*mp
;
while(mp
->m_flag
& M_MLOCK
) {
sleep((caddr_t
)mp
, PVFS
);
* Unlock a locked filesystem.
* Panic if filesystem is not locked.
register struct mount
*mp
;
if ((mp
->m_flag
& M_MLOCK
) == 0)
panic("vfs_unlock: locked fs");
if (mp
->m_flag
& M_MWAIT
) {
* Lookup a mount point by filesystem identifier.
register struct mount
*mp
;
if (mp
->m_fsid
.val
[0] == fsid
->val
[0] &&
mp
->m_fsid
.val
[1] == fsid
->val
[1]) {
return ((struct mount
*)0);
* Set vnode attributes to VNOVAL
register struct vattr
*vap
;
vap
->va_mode
= vap
->va_nlink
= vap
->va_uid
= vap
->va_gid
=
vap
->va_fsid
= vap
->va_fileid
= vap
->va_size
=
vap
->va_size1
= vap
->va_blocksize
= vap
->va_rdev
=
vap
->va_bytes
= vap
->va_bytes1
=
vap
->va_atime
.tv_sec
= vap
->va_atime
.tv_usec
=
vap
->va_mtime
.tv_sec
= vap
->va_mtime
.tv_usec
=
vap
->va_ctime
.tv_sec
= vap
->va_ctime
.tv_usec
=
vap
->va_flags
= vap
->va_gen
= VNOVAL
;
* Initialize a nameidata structure
register struct nameidata
*ndp
;
bzero((caddr_t
)ndp
, sizeof(struct nameidata
));
ndp
->ni_iov
= &ndp
->ni_nd
.nd_iovec
;
ndp
->ni_base
= (caddr_t
)&ndp
->ni_dent
;
ndp
->ni_uioseg
= UIO_SYSSPACE
;
* Duplicate a nameidata structure
register struct nameidata
*ndp
, *newndp
;
newndp
->ni_cdir
= ndp
->ni_cdir
;
newndp
->ni_rdir
= ndp
->ni_rdir
;
newndp
->ni_cred
= ndp
->ni_cred
;
* Release a nameidata structure
register struct nameidata
*ndp
;
* Routines having to do with the management of the vnode table.
struct vnode
*vfreeh
, **vfreet
;
extern struct vnodeops dead_vnodeops
, spec_vnodeops
;
#if ((SPECHSZ&(SPECHSZ-1)) == 0)
#define SPECHASH(rdev) (((rdev>>5)+(rdev))&(SPECHSZ-1))
#define SPECHASH(rdev) (((unsigned)((rdev>>5)+(rdev)))%SPECHSZ)
struct vnode
*speclisth
[SPECHSZ
];
* Initialize the vnode structures and initialize each file system type.
register struct vnode
*vp
= vnode
;
vp
->v_op
= &dead_vnodeops
;
for (vp
++; vp
< vnodeNVNODE
; vp
++) {
vp
->v_op
= &dead_vnodeops
;
* Initialize the vnode name cache
* Initialize each file system type.
for (vfsp
= &vfssw
[0]; vfsp
<= &vfssw
[MOUNT_MAXTYPE
]; vfsp
++) {
* Return the next vnode from the free list.
getnewvnode(tag
, mp
, vops
, vpp
)
register struct vnode
*vp
, *vq
;
if ((vp
= vfreeh
) == NULL
) {
panic("free vnode isn't");
* Move a vnode from one mount queue to another.
register struct vnode
*vp
;
register struct mount
*mp
;
* 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.
vp
->v_mountf
= mp
->m_mounth
;
vp
->v_mountb
= &mp
->m_mounth
;
mp
->m_mounth
->v_mountb
= &vp
->v_mountf
;
vp
->v_mountb
= &mp
->m_mounth
;
* 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_vnodeops
, &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
)
return ((struct vnode
*)0);
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
);
return ((struct vnode
*)0);
* 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
* 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
;
vprint("vrele: bad ref count", vp
);
if (vfreeh
== (struct vnode
*)0) {
* 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
;
for (vp
= mp
->m_mounth
; vp
; vp
= nvp
) {
* Skip over a selected vnode.
* Used by ufs to skip over the quota structure inode.
* 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 (vp
->v_type
!= VBLK
&& vp
->v_type
!= VCHR
) {
vp
->v_op
= &spec_vnodeops
;
insmntque(vp
, (struct mount
*)0);
vprint("vflush: busy vnode", vp
);
* Disassociate the underlying file system from a vnode.
register struct vnode
*vp
;
struct vnodeops
*origops
;
* 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.
* Prevent any further operations on the vnode from
* being passed through to the old file system.
vp
->v_op
= &dead_vnodeops
;
* If purging an active vnode, it must be unlocked, closed,
* and deactivated before being reclaimed.
(*(origops
->vn_unlock
))(vp
);
(*(origops
->vn_close
))(vp
, 0, NOCRED
);
(*(origops
->vn_inactive
))(vp
);
if ((*(origops
->vn_reclaim
))(vp
))
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
;
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
)
* Eliminate all activity associated with a vnode
* in preparation for reuse.
register struct vnode
*vp
;
register struct vnode
*vq
;
* 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", "VBAD" };
register struct vnode
*vp
;
printf("type %s, usecount %d, refcount %d,", typename
[vp
->v_type
],
vp
->v_usecount
, vp
->v_holdcnt
);
if (vp
->v_flag
& VEXLOCK
)
if (vp
->v_flag
& VSHLOCK
)
if (vp
->v_flag
& VALIASED
)
strcat(buf
, "|VALIASED");
printf(" flags (%s)", &buf
[1]);