version 1.350.2.2, 2008/06/27 15:11:39 |
version 1.350.2.3, 2008/07/18 16:37:49 |
|
|
*/ |
*/ |
|
|
/* |
/* |
* External virtual filesystem routines. |
|
* |
|
* This file contains vfs subroutines which are heavily dependant on |
|
* the kernel and are not suitable for standalone use. Examples include |
|
* routines involved vnode and mountpoint management. |
|
* |
|
* Note on v_usecount and locking: |
* Note on v_usecount and locking: |
* |
* |
* At nearly all points it is known that v_usecount could be zero, the |
* At nearly all points it is known that v_usecount could be zero, the |
Line 123 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 117 __KERNEL_RCSID(0, "$NetBSD$"); |
|
|
|
#include <sys/sysctl.h> |
#include <sys/sysctl.h> |
|
|
|
const enum vtype iftovt_tab[16] = { |
|
VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON, |
|
VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VBAD, |
|
}; |
|
const int vttoif_tab[9] = { |
|
0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, |
|
S_IFSOCK, S_IFIFO, S_IFMT, |
|
}; |
|
|
|
/* |
|
* Insq/Remq for the vnode usage lists. |
|
*/ |
|
#define bufinsvn(bp, dp) LIST_INSERT_HEAD(dp, bp, b_vnbufs) |
|
#define bufremvn(bp) { \ |
|
LIST_REMOVE(bp, b_vnbufs); \ |
|
(bp)->b_vnbufs.le_next = NOLIST; \ |
|
} |
|
|
|
int doforce = 1; /* 1 => permit forcible unmounting */ |
|
int prtactive = 0; /* 1 => print out reclaim of active vnodes */ |
|
|
extern int dovfsusermount; /* 1 => permit any user to mount filesystems */ |
extern int dovfsusermount; /* 1 => permit any user to mount filesystems */ |
extern int vfs_magiclinks; /* 1 => expand "magic" symlinks */ |
extern int vfs_magiclinks; /* 1 => expand "magic" symlinks */ |
|
|
Line 130 static vnodelst_t vnode_free_list = TAIL |
|
Line 145 static vnodelst_t vnode_free_list = TAIL |
|
static vnodelst_t vnode_hold_list = TAILQ_HEAD_INITIALIZER(vnode_hold_list); |
static vnodelst_t vnode_hold_list = TAILQ_HEAD_INITIALIZER(vnode_hold_list); |
static vnodelst_t vrele_list = TAILQ_HEAD_INITIALIZER(vrele_list); |
static vnodelst_t vrele_list = TAILQ_HEAD_INITIALIZER(vrele_list); |
|
|
|
struct mntlist mountlist = /* mounted filesystem list */ |
|
CIRCLEQ_HEAD_INITIALIZER(mountlist); |
|
|
|
u_int numvnodes; |
|
static specificdata_domain_t mount_specificdata_domain; |
|
|
static int vrele_pending; |
static int vrele_pending; |
static int vrele_gen; |
static int vrele_gen; |
static kmutex_t vrele_lock; |
static kmutex_t vrele_lock; |
static kcondvar_t vrele_cv; |
static kcondvar_t vrele_cv; |
static lwp_t *vrele_lwp; |
static lwp_t *vrele_lwp; |
|
|
|
kmutex_t mountlist_lock; |
|
kmutex_t mntid_lock; |
|
kmutex_t mntvnode_lock; |
|
kmutex_t vnode_free_list_lock; |
|
kmutex_t specfs_lock; |
|
kmutex_t vfs_list_lock; |
|
|
static pool_cache_t vnode_cache; |
static pool_cache_t vnode_cache; |
|
|
MALLOC_DEFINE(M_VNODE, "vnodes", "Dynamically allocated vnodes"); |
MALLOC_DEFINE(M_VNODE, "vnodes", "Dynamically allocated vnodes"); |
|
|
/* |
/* |
|
* These define the root filesystem and device. |
|
*/ |
|
struct vnode *rootvnode; |
|
struct device *root_device; /* root device */ |
|
|
|
/* |
* Local declarations. |
* Local declarations. |
*/ |
*/ |
|
|
Line 150 static int getdevvp(dev_t, vnode_t **, e |
|
Line 184 static int getdevvp(dev_t, vnode_t **, e |
|
static vnode_t *getcleanvnode(void);; |
static vnode_t *getcleanvnode(void);; |
void vpanic(vnode_t *, const char *); |
void vpanic(vnode_t *, const char *); |
|
|
|
#ifdef DEBUG |
|
void printlockedvnodes(void); |
|
#endif |
|
|
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
void |
void |
vpanic(vnode_t *vp, const char *msg) |
vpanic(vnode_t *vp, const char *msg) |
|
|
panic("fork vrele"); |
panic("fork vrele"); |
} |
} |
|
|
|
/* |
|
* Initialize the vnode management data structures. |
|
*/ |
|
void |
|
vntblinit(void) |
|
{ |
|
|
|
mutex_init(&mountlist_lock, MUTEX_DEFAULT, IPL_NONE); |
|
mutex_init(&mntid_lock, MUTEX_DEFAULT, IPL_NONE); |
|
mutex_init(&mntvnode_lock, MUTEX_DEFAULT, IPL_NONE); |
|
mutex_init(&vnode_free_list_lock, MUTEX_DEFAULT, IPL_NONE); |
|
mutex_init(&specfs_lock, MUTEX_DEFAULT, IPL_NONE); |
|
mutex_init(&vfs_list_lock, MUTEX_DEFAULT, IPL_NONE); |
|
|
|
mount_specificdata_domain = specificdata_domain_create(); |
|
|
|
/* Initialize the filesystem syncer. */ |
|
vn_initialize_syncerd(); |
|
vn_init1(); |
|
} |
|
|
int |
int |
vfs_drainvnodes(long target, struct lwp *l) |
vfs_drainvnodes(long target, struct lwp *l) |
{ |
{ |
Line 196 vfs_drainvnodes(long target, struct lwp |
|
Line 255 vfs_drainvnodes(long target, struct lwp |
|
} |
} |
|
|
/* |
/* |
|
* Lookup a mount point by filesystem identifier. |
|
* |
|
* XXX Needs to add a reference to the mount point. |
|
*/ |
|
struct mount * |
|
vfs_getvfs(fsid_t *fsid) |
|
{ |
|
struct mount *mp; |
|
|
|
mutex_enter(&mountlist_lock); |
|
CIRCLEQ_FOREACH(mp, &mountlist, mnt_list) { |
|
if (mp->mnt_stat.f_fsidx.__fsid_val[0] == fsid->__fsid_val[0] && |
|
mp->mnt_stat.f_fsidx.__fsid_val[1] == fsid->__fsid_val[1]) { |
|
mutex_exit(&mountlist_lock); |
|
return (mp); |
|
} |
|
} |
|
mutex_exit(&mountlist_lock); |
|
return ((struct mount *)0); |
|
} |
|
|
|
/* |
|
* Drop a reference to a mount structure, freeing if the last reference. |
|
*/ |
|
void |
|
vfs_destroy(struct mount *mp) |
|
{ |
|
|
|
if (__predict_true(atomic_dec_uint_nv(&mp->mnt_refcnt) > 0)) { |
|
return; |
|
} |
|
|
|
/* |
|
* Nothing else has visibility of the mount: we can now |
|
* free the data structures. |
|
*/ |
|
specificdata_fini(mount_specificdata_domain, &mp->mnt_specdataref); |
|
rw_destroy(&mp->mnt_unmounting); |
|
mutex_destroy(&mp->mnt_updating); |
|
mutex_destroy(&mp->mnt_renamelock); |
|
if (mp->mnt_op != NULL) { |
|
vfs_delref(mp->mnt_op); |
|
} |
|
kmem_free(mp, sizeof(*mp)); |
|
} |
|
|
|
/* |
* grab a vnode from freelist and clean it. |
* grab a vnode from freelist and clean it. |
*/ |
*/ |
vnode_t * |
vnode_t * |
Line 662 insmntque(vnode_t *vp, struct mount *mp) |
|
Line 768 insmntque(vnode_t *vp, struct mount *mp) |
|
} |
} |
|
|
/* |
/* |
|
* Wait for a vnode (typically with VI_XLOCK set) to be cleaned or |
|
* recycled. |
|
*/ |
|
void |
|
vwait(vnode_t *vp, int flags) |
|
{ |
|
|
|
KASSERT(mutex_owned(&vp->v_interlock)); |
|
KASSERT(vp->v_usecount != 0); |
|
|
|
while ((vp->v_iflag & flags) != 0) |
|
cv_wait(&vp->v_cv, &vp->v_interlock); |
|
} |
|
|
|
/* |
|
* Insert a marker vnode into a mount's vnode list, after the |
|
* specified vnode. mntvnode_lock must be held. |
|
*/ |
|
void |
|
vmark(vnode_t *mvp, vnode_t *vp) |
|
{ |
|
struct mount *mp; |
|
|
|
mp = mvp->v_mount; |
|
|
|
KASSERT(mutex_owned(&mntvnode_lock)); |
|
KASSERT((mvp->v_iflag & VI_MARKER) != 0); |
|
KASSERT(vp->v_mount == mp); |
|
|
|
TAILQ_INSERT_AFTER(&mp->mnt_vnodelist, vp, mvp, v_mntvnodes); |
|
} |
|
|
|
/* |
|
* Remove a marker vnode from a mount's vnode list, and return |
|
* a pointer to the next vnode in the list. mntvnode_lock must |
|
* be held. |
|
*/ |
|
vnode_t * |
|
vunmark(vnode_t *mvp) |
|
{ |
|
vnode_t *vp; |
|
struct mount *mp; |
|
|
|
mp = mvp->v_mount; |
|
|
|
KASSERT(mutex_owned(&mntvnode_lock)); |
|
KASSERT((mvp->v_iflag & VI_MARKER) != 0); |
|
|
|
vp = TAILQ_NEXT(mvp, v_mntvnodes); |
|
TAILQ_REMOVE(&mp->mnt_vnodelist, mvp, v_mntvnodes); |
|
|
|
KASSERT(vp == NULL || vp->v_mount == mp); |
|
|
|
return vp; |
|
} |
|
|
|
/* |
|
* Update outstanding I/O count and do wakeup if requested. |
|
*/ |
|
void |
|
vwakeup(struct buf *bp) |
|
{ |
|
struct vnode *vp; |
|
|
|
if ((vp = bp->b_vp) == NULL) |
|
return; |
|
|
|
KASSERT(bp->b_objlock == &vp->v_interlock); |
|
KASSERT(mutex_owned(bp->b_objlock)); |
|
|
|
if (--vp->v_numoutput < 0) |
|
panic("vwakeup: neg numoutput, vp %p", vp); |
|
if (vp->v_numoutput == 0) |
|
cv_broadcast(&vp->v_cv); |
|
} |
|
|
|
/* |
|
* Flush out and invalidate all buffers associated with a vnode. |
|
* Called with the underlying vnode locked, which should prevent new dirty |
|
* buffers from being queued. |
|
*/ |
|
int |
|
vinvalbuf(struct vnode *vp, int flags, kauth_cred_t cred, struct lwp *l, |
|
bool catch, int slptimeo) |
|
{ |
|
struct buf *bp, *nbp; |
|
int error; |
|
int flushflags = PGO_ALLPAGES | PGO_FREE | PGO_SYNCIO | |
|
(flags & V_SAVE ? PGO_CLEANIT | PGO_RECLAIM : 0); |
|
|
|
/* XXXUBC this doesn't look at flags or slp* */ |
|
mutex_enter(&vp->v_interlock); |
|
error = VOP_PUTPAGES(vp, 0, 0, flushflags); |
|
if (error) { |
|
return error; |
|
} |
|
|
|
if (flags & V_SAVE) { |
|
error = VOP_FSYNC(vp, cred, FSYNC_WAIT|FSYNC_RECLAIM, 0, 0); |
|
if (error) |
|
return (error); |
|
KASSERT(LIST_EMPTY(&vp->v_dirtyblkhd)); |
|
} |
|
|
|
mutex_enter(&bufcache_lock); |
|
restart: |
|
for (bp = LIST_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) { |
|
nbp = LIST_NEXT(bp, b_vnbufs); |
|
error = bbusy(bp, catch, slptimeo, NULL); |
|
if (error != 0) { |
|
if (error == EPASSTHROUGH) |
|
goto restart; |
|
mutex_exit(&bufcache_lock); |
|
return (error); |
|
} |
|
brelsel(bp, BC_INVAL | BC_VFLUSH); |
|
} |
|
|
|
for (bp = LIST_FIRST(&vp->v_cleanblkhd); bp; bp = nbp) { |
|
nbp = LIST_NEXT(bp, b_vnbufs); |
|
error = bbusy(bp, catch, slptimeo, NULL); |
|
if (error != 0) { |
|
if (error == EPASSTHROUGH) |
|
goto restart; |
|
mutex_exit(&bufcache_lock); |
|
return (error); |
|
} |
|
/* |
|
* XXX Since there are no node locks for NFS, I believe |
|
* there is a slight chance that a delayed write will |
|
* occur while sleeping just above, so check for it. |
|
*/ |
|
if ((bp->b_oflags & BO_DELWRI) && (flags & V_SAVE)) { |
|
#ifdef DEBUG |
|
printf("buffer still DELWRI\n"); |
|
#endif |
|
bp->b_cflags |= BC_BUSY | BC_VFLUSH; |
|
mutex_exit(&bufcache_lock); |
|
VOP_BWRITE(bp); |
|
mutex_enter(&bufcache_lock); |
|
goto restart; |
|
} |
|
brelsel(bp, BC_INVAL | BC_VFLUSH); |
|
} |
|
|
|
#ifdef DIAGNOSTIC |
|
if (!LIST_EMPTY(&vp->v_cleanblkhd) || !LIST_EMPTY(&vp->v_dirtyblkhd)) |
|
panic("vinvalbuf: flush failed, vp %p", vp); |
|
#endif |
|
|
|
mutex_exit(&bufcache_lock); |
|
|
|
return (0); |
|
} |
|
|
|
/* |
|
* Destroy any in core blocks past the truncation length. |
|
* Called with the underlying vnode locked, which should prevent new dirty |
|
* buffers from being queued. |
|
*/ |
|
int |
|
vtruncbuf(struct vnode *vp, daddr_t lbn, bool catch, int slptimeo) |
|
{ |
|
struct buf *bp, *nbp; |
|
int error; |
|
voff_t off; |
|
|
|
off = round_page((voff_t)lbn << vp->v_mount->mnt_fs_bshift); |
|
mutex_enter(&vp->v_interlock); |
|
error = VOP_PUTPAGES(vp, off, 0, PGO_FREE | PGO_SYNCIO); |
|
if (error) { |
|
return error; |
|
} |
|
|
|
mutex_enter(&bufcache_lock); |
|
restart: |
|
for (bp = LIST_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) { |
|
nbp = LIST_NEXT(bp, b_vnbufs); |
|
if (bp->b_lblkno < lbn) |
|
continue; |
|
error = bbusy(bp, catch, slptimeo, NULL); |
|
if (error != 0) { |
|
if (error == EPASSTHROUGH) |
|
goto restart; |
|
mutex_exit(&bufcache_lock); |
|
return (error); |
|
} |
|
brelsel(bp, BC_INVAL | BC_VFLUSH); |
|
} |
|
|
|
for (bp = LIST_FIRST(&vp->v_cleanblkhd); bp; bp = nbp) { |
|
nbp = LIST_NEXT(bp, b_vnbufs); |
|
if (bp->b_lblkno < lbn) |
|
continue; |
|
error = bbusy(bp, catch, slptimeo, NULL); |
|
if (error != 0) { |
|
if (error == EPASSTHROUGH) |
|
goto restart; |
|
mutex_exit(&bufcache_lock); |
|
return (error); |
|
} |
|
brelsel(bp, BC_INVAL | BC_VFLUSH); |
|
} |
|
mutex_exit(&bufcache_lock); |
|
|
|
return (0); |
|
} |
|
|
|
/* |
|
* Flush all dirty buffers from a vnode. |
|
* Called with the underlying vnode locked, which should prevent new dirty |
|
* buffers from being queued. |
|
*/ |
|
void |
|
vflushbuf(struct vnode *vp, int sync) |
|
{ |
|
struct buf *bp, *nbp; |
|
int flags = PGO_CLEANIT | PGO_ALLPAGES | (sync ? PGO_SYNCIO : 0); |
|
bool dirty; |
|
|
|
mutex_enter(&vp->v_interlock); |
|
(void) VOP_PUTPAGES(vp, 0, 0, flags); |
|
|
|
loop: |
|
mutex_enter(&bufcache_lock); |
|
for (bp = LIST_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) { |
|
nbp = LIST_NEXT(bp, b_vnbufs); |
|
if ((bp->b_cflags & BC_BUSY)) |
|
continue; |
|
if ((bp->b_oflags & BO_DELWRI) == 0) |
|
panic("vflushbuf: not dirty, bp %p", bp); |
|
bp->b_cflags |= BC_BUSY | BC_VFLUSH; |
|
mutex_exit(&bufcache_lock); |
|
/* |
|
* Wait for I/O associated with indirect blocks to complete, |
|
* since there is no way to quickly wait for them below. |
|
*/ |
|
if (bp->b_vp == vp || sync == 0) |
|
(void) bawrite(bp); |
|
else |
|
(void) bwrite(bp); |
|
goto loop; |
|
} |
|
mutex_exit(&bufcache_lock); |
|
|
|
if (sync == 0) |
|
return; |
|
|
|
mutex_enter(&vp->v_interlock); |
|
while (vp->v_numoutput != 0) |
|
cv_wait(&vp->v_cv, &vp->v_interlock); |
|
dirty = !LIST_EMPTY(&vp->v_dirtyblkhd); |
|
mutex_exit(&vp->v_interlock); |
|
|
|
if (dirty) { |
|
vprint("vflushbuf: dirty", vp); |
|
goto loop; |
|
} |
|
} |
|
|
|
/* |
* Create a vnode for a block device. |
* Create a vnode for a block device. |
* Used for root filesystem and swap areas. |
* Used for root filesystem and swap areas. |
* Also used for memory file system special devices. |
* Also used for memory file system special devices. |
Line 685 cdevvp(dev_t dev, vnode_t **vpp) |
|
Line 1052 cdevvp(dev_t dev, vnode_t **vpp) |
|
} |
} |
|
|
/* |
/* |
|
* Associate a buffer with a vnode. There must already be a hold on |
|
* the vnode. |
|
*/ |
|
void |
|
bgetvp(struct vnode *vp, struct buf *bp) |
|
{ |
|
|
|
KASSERT(bp->b_vp == NULL); |
|
KASSERT(bp->b_objlock == &buffer_lock); |
|
KASSERT(mutex_owned(&vp->v_interlock)); |
|
KASSERT(mutex_owned(&bufcache_lock)); |
|
KASSERT((bp->b_cflags & BC_BUSY) != 0); |
|
KASSERT(!cv_has_waiters(&bp->b_done)); |
|
|
|
vholdl(vp); |
|
bp->b_vp = vp; |
|
if (vp->v_type == VBLK || vp->v_type == VCHR) |
|
bp->b_dev = vp->v_rdev; |
|
else |
|
bp->b_dev = NODEV; |
|
|
|
/* |
|
* Insert onto list for new vnode. |
|
*/ |
|
bufinsvn(bp, &vp->v_cleanblkhd); |
|
bp->b_objlock = &vp->v_interlock; |
|
} |
|
|
|
/* |
|
* Disassociate a buffer from a vnode. |
|
*/ |
|
void |
|
brelvp(struct buf *bp) |
|
{ |
|
struct vnode *vp = bp->b_vp; |
|
|
|
KASSERT(vp != NULL); |
|
KASSERT(bp->b_objlock == &vp->v_interlock); |
|
KASSERT(mutex_owned(&vp->v_interlock)); |
|
KASSERT(mutex_owned(&bufcache_lock)); |
|
KASSERT((bp->b_cflags & BC_BUSY) != 0); |
|
KASSERT(!cv_has_waiters(&bp->b_done)); |
|
|
|
/* |
|
* Delete from old vnode list, if on one. |
|
*/ |
|
if (LIST_NEXT(bp, b_vnbufs) != NOLIST) |
|
bufremvn(bp); |
|
|
|
if (TAILQ_EMPTY(&vp->v_uobj.memq) && (vp->v_iflag & VI_ONWORKLST) && |
|
LIST_FIRST(&vp->v_dirtyblkhd) == NULL) { |
|
vp->v_iflag &= ~VI_WRMAPDIRTY; |
|
vn_syncer_remove_from_worklist(vp); |
|
} |
|
|
|
bp->b_objlock = &buffer_lock; |
|
bp->b_vp = NULL; |
|
holdrelel(vp); |
|
} |
|
|
|
/* |
|
* Reassign a buffer from one vnode list to another. |
|
* The list reassignment must be within the same vnode. |
|
* Used to assign file specific control information |
|
* (indirect blocks) to the list to which they belong. |
|
*/ |
|
void |
|
reassignbuf(struct buf *bp, struct vnode *vp) |
|
{ |
|
struct buflists *listheadp; |
|
int delayx; |
|
|
|
KASSERT(mutex_owned(&bufcache_lock)); |
|
KASSERT(bp->b_objlock == &vp->v_interlock); |
|
KASSERT(mutex_owned(&vp->v_interlock)); |
|
KASSERT((bp->b_cflags & BC_BUSY) != 0); |
|
|
|
/* |
|
* Delete from old vnode list, if on one. |
|
*/ |
|
if (LIST_NEXT(bp, b_vnbufs) != NOLIST) |
|
bufremvn(bp); |
|
|
|
/* |
|
* If dirty, put on list of dirty buffers; |
|
* otherwise insert onto list of clean buffers. |
|
*/ |
|
if ((bp->b_oflags & BO_DELWRI) == 0) { |
|
listheadp = &vp->v_cleanblkhd; |
|
if (TAILQ_EMPTY(&vp->v_uobj.memq) && |
|
(vp->v_iflag & VI_ONWORKLST) && |
|
LIST_FIRST(&vp->v_dirtyblkhd) == NULL) { |
|
vp->v_iflag &= ~VI_WRMAPDIRTY; |
|
vn_syncer_remove_from_worklist(vp); |
|
} |
|
} else { |
|
listheadp = &vp->v_dirtyblkhd; |
|
if ((vp->v_iflag & VI_ONWORKLST) == 0) { |
|
switch (vp->v_type) { |
|
case VDIR: |
|
delayx = dirdelay; |
|
break; |
|
case VBLK: |
|
if (vp->v_specmountpoint != NULL) { |
|
delayx = metadelay; |
|
break; |
|
} |
|
/* fall through */ |
|
default: |
|
delayx = filedelay; |
|
break; |
|
} |
|
if (!vp->v_mount || |
|
(vp->v_mount->mnt_flag & MNT_ASYNC) == 0) |
|
vn_syncer_add_to_worklist(vp, delayx); |
|
} |
|
} |
|
bufinsvn(bp, listheadp); |
|
} |
|
|
|
/* |
* Create a vnode for a device. |
* Create a vnode for a device. |
* Used by bdevvp (block device) for root file system etc., |
* Used by bdevvp (block device) for root file system etc., |
* and by cdevvp (character device) for console and kernfs. |
* and by cdevvp (character device) for console and kernfs. |
Line 1164 vflush(struct mount *mp, vnode_t *skipvp |
|
Line 1652 vflush(struct mount *mp, vnode_t *skipvp |
|
*/ |
*/ |
mutex_enter(&vrele_lock); |
mutex_enter(&vrele_lock); |
gen = vrele_gen; |
gen = vrele_gen; |
do { |
while (vrele_pending && gen == vrele_gen) { |
cv_broadcast(&vrele_cv); |
cv_broadcast(&vrele_cv); |
cv_wait(&vrele_cv, &vrele_lock); |
cv_wait(&vrele_cv, &vrele_lock); |
} while (gen == vrele_gen); |
} |
mutex_exit(&vrele_lock); |
mutex_exit(&vrele_lock); |
|
|
/* Allocate a marker vnode. */ |
/* Allocate a marker vnode. */ |
|
|
} |
} |
|
|
/* |
/* |
* Sham lock manager for vnodes. This is a temporary measure. |
* Get a new unique fsid |
*/ |
*/ |
int |
void |
vlockmgr(struct vnlock *vl, int flags) |
vfs_getnewfsid(struct mount *mp) |
{ |
{ |
|
static u_short xxxfs_mntid; |
|
fsid_t tfsid; |
|
int mtype; |
|
|
KASSERT((flags & ~(LK_CANRECURSE | LK_NOWAIT | LK_TYPE_MASK)) == 0); |
mutex_enter(&mntid_lock); |
|
mtype = makefstype(mp->mnt_op->vfs_name); |
switch (flags & LK_TYPE_MASK) { |
mp->mnt_stat.f_fsidx.__fsid_val[0] = makedev(mtype, 0); |
case LK_SHARED: |
mp->mnt_stat.f_fsidx.__fsid_val[1] = mtype; |
if (rw_tryenter(&vl->vl_lock, RW_READER)) { |
mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0]; |
return 0; |
if (xxxfs_mntid == 0) |
} |
++xxxfs_mntid; |
if ((flags & LK_NOWAIT) != 0) { |
tfsid.__fsid_val[0] = makedev(mtype & 0xff, xxxfs_mntid); |
return EBUSY; |
tfsid.__fsid_val[1] = mtype; |
|
if (!CIRCLEQ_EMPTY(&mountlist)) { |
|
while (vfs_getvfs(&tfsid)) { |
|
tfsid.__fsid_val[0]++; |
|
xxxfs_mntid++; |
} |
} |
rw_enter(&vl->vl_lock, RW_READER); |
} |
return 0; |
mp->mnt_stat.f_fsidx.__fsid_val[0] = tfsid.__fsid_val[0]; |
|
mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0]; |
|
mutex_exit(&mntid_lock); |
|
} |
|
|
case LK_EXCLUSIVE: |
/* |
if (rw_tryenter(&vl->vl_lock, RW_WRITER)) { |
* Make a 'unique' number from a mount type name. |
return 0; |
*/ |
} |
long |
if ((vl->vl_canrecurse || (flags & LK_CANRECURSE) != 0) && |
makefstype(const char *type) |
rw_write_held(&vl->vl_lock)) { |
{ |
vl->vl_recursecnt++; |
long rv; |
return 0; |
|
} |
|
if ((flags & LK_NOWAIT) != 0) { |
|
return EBUSY; |
|
} |
|
rw_enter(&vl->vl_lock, RW_WRITER); |
|
return 0; |
|
|
|
case LK_RELEASE: |
for (rv = 0; *type; type++) { |
if (vl->vl_recursecnt != 0) { |
rv <<= 2; |
|
rv ^= *type; |
|
} |
|
return rv; |
|
} |
|
|
|
/* |
|
* Set vnode attributes to VNOVAL |
|
*/ |
|
void |
|
vattr_null(struct vattr *vap) |
|
{ |
|
|
|
vap->va_type = VNON; |
|
|
|
/* |
|
* Assign individually so that it is safe even if size and |
|
* sign of each member are varied. |
|
*/ |
|
vap->va_mode = VNOVAL; |
|
vap->va_nlink = VNOVAL; |
|
vap->va_uid = VNOVAL; |
|
vap->va_gid = VNOVAL; |
|
vap->va_fsid = VNOVAL; |
|
vap->va_fileid = VNOVAL; |
|
vap->va_size = VNOVAL; |
|
vap->va_blocksize = VNOVAL; |
|
vap->va_atime.tv_sec = |
|
vap->va_mtime.tv_sec = |
|
vap->va_ctime.tv_sec = |
|
vap->va_birthtime.tv_sec = VNOVAL; |
|
vap->va_atime.tv_nsec = |
|
vap->va_mtime.tv_nsec = |
|
vap->va_ctime.tv_nsec = |
|
vap->va_birthtime.tv_nsec = VNOVAL; |
|
vap->va_gen = VNOVAL; |
|
vap->va_flags = VNOVAL; |
|
vap->va_rdev = VNOVAL; |
|
vap->va_bytes = VNOVAL; |
|
vap->va_vaflags = 0; |
|
} |
|
|
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) |
|
#define ARRAY_PRINT(idx, arr) \ |
|
((idx) > 0 && (idx) < ARRAY_SIZE(arr) ? (arr)[(idx)] : "UNKNOWN") |
|
|
|
const char * const vnode_tags[] = { VNODE_TAGS }; |
|
const char * const vnode_types[] = { VNODE_TYPES }; |
|
const char vnode_flagbits[] = VNODE_FLAGBITS; |
|
|
|
/* |
|
* Print out a description of a vnode. |
|
*/ |
|
void |
|
vprint(const char *label, struct vnode *vp) |
|
{ |
|
struct vnlock *vl; |
|
char bf[96]; |
|
int flag; |
|
|
|
vl = (vp->v_vnlock != NULL ? vp->v_vnlock : &vp->v_lock); |
|
flag = vp->v_iflag | vp->v_vflag | vp->v_uflag; |
|
bitmask_snprintf(flag, vnode_flagbits, bf, sizeof(bf)); |
|
|
|
if (label != NULL) |
|
printf("%s: ", label); |
|
printf("vnode @ %p, flags (%s)\n\ttag %s(%d), type %s(%d), " |
|
"usecount %d, writecount %d, holdcount %d\n" |
|
"\tfreelisthd %p, mount %p, data %p lock %p recursecnt %d\n", |
|
vp, bf, ARRAY_PRINT(vp->v_tag, vnode_tags), vp->v_tag, |
|
ARRAY_PRINT(vp->v_type, vnode_types), vp->v_type, |
|
vp->v_usecount, vp->v_writecount, vp->v_holdcnt, |
|
vp->v_freelisthd, vp->v_mount, vp->v_data, vl, vl->vl_recursecnt); |
|
if (vp->v_data != NULL) { |
|
printf("\t"); |
|
VOP_PRINT(vp); |
|
} |
|
} |
|
|
|
#ifdef DEBUG |
|
/* |
|
* List all of the locked vnodes in the system. |
|
* Called when debugging the kernel. |
|
*/ |
|
void |
|
printlockedvnodes(void) |
|
{ |
|
struct mount *mp, *nmp; |
|
struct vnode *vp; |
|
|
|
printf("Locked vnodes\n"); |
|
mutex_enter(&mountlist_lock); |
|
for (mp = CIRCLEQ_FIRST(&mountlist); mp != (void *)&mountlist; |
|
mp = nmp) { |
|
if (vfs_busy(mp, &nmp)) { |
|
continue; |
|
} |
|
TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) { |
|
if (VOP_ISLOCKED(vp)) |
|
vprint(NULL, vp); |
|
} |
|
mutex_enter(&mountlist_lock); |
|
vfs_unbusy(mp, false, &nmp); |
|
} |
|
mutex_exit(&mountlist_lock); |
|
} |
|
#endif |
|
|
|
/* |
|
* Do the usual access checking. |
|
* file_mode, uid and gid are from the vnode in question, |
|
* while acc_mode and cred are from the VOP_ACCESS parameter list |
|
*/ |
|
int |
|
vaccess(enum vtype type, mode_t file_mode, uid_t uid, gid_t gid, |
|
mode_t acc_mode, kauth_cred_t cred) |
|
{ |
|
mode_t mask; |
|
int error, ismember; |
|
|
|
/* |
|
* Super-user always gets read/write access, but execute access depends |
|
* on at least one execute bit being set. |
|
*/ |
|
if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL) == 0) { |
|
if ((acc_mode & VEXEC) && type != VDIR && |
|
(file_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0) |
|
return (EACCES); |
|
return (0); |
|
} |
|
|
|
mask = 0; |
|
|
|
/* Otherwise, check the owner. */ |
|
if (kauth_cred_geteuid(cred) == uid) { |
|
if (acc_mode & VEXEC) |
|
mask |= S_IXUSR; |
|
if (acc_mode & VREAD) |
|
mask |= S_IRUSR; |
|
if (acc_mode & VWRITE) |
|
mask |= S_IWUSR; |
|
return ((file_mode & mask) == mask ? 0 : EACCES); |
|
} |
|
|
|
/* Otherwise, check the groups. */ |
|
error = kauth_cred_ismember_gid(cred, gid, &ismember); |
|
if (error) |
|
return (error); |
|
if (kauth_cred_getegid(cred) == gid || ismember) { |
|
if (acc_mode & VEXEC) |
|
mask |= S_IXGRP; |
|
if (acc_mode & VREAD) |
|
mask |= S_IRGRP; |
|
if (acc_mode & VWRITE) |
|
mask |= S_IWGRP; |
|
return ((file_mode & mask) == mask ? 0 : EACCES); |
|
} |
|
|
|
/* Otherwise, check everyone else. */ |
|
if (acc_mode & VEXEC) |
|
mask |= S_IXOTH; |
|
if (acc_mode & VREAD) |
|
mask |= S_IROTH; |
|
if (acc_mode & VWRITE) |
|
mask |= S_IWOTH; |
|
return ((file_mode & mask) == mask ? 0 : EACCES); |
|
} |
|
|
|
/* |
|
* Given a file system name, look up the vfsops for that |
|
* file system, or return NULL if file system isn't present |
|
* in the kernel. |
|
*/ |
|
struct vfsops * |
|
vfs_getopsbyname(const char *name) |
|
{ |
|
struct vfsops *v; |
|
|
|
mutex_enter(&vfs_list_lock); |
|
LIST_FOREACH(v, &vfs_list, vfs_list) { |
|
if (strcmp(v->vfs_name, name) == 0) |
|
break; |
|
} |
|
if (v != NULL) |
|
v->vfs_refcount++; |
|
mutex_exit(&vfs_list_lock); |
|
|
|
return (v); |
|
} |
|
|
|
void |
|
copy_statvfs_info(struct statvfs *sbp, const struct mount *mp) |
|
{ |
|
const struct statvfs *mbp; |
|
|
|
if (sbp == (mbp = &mp->mnt_stat)) |
|
return; |
|
|
|
(void)memcpy(&sbp->f_fsidx, &mbp->f_fsidx, sizeof(sbp->f_fsidx)); |
|
sbp->f_fsid = mbp->f_fsid; |
|
sbp->f_owner = mbp->f_owner; |
|
sbp->f_flag = mbp->f_flag; |
|
sbp->f_syncwrites = mbp->f_syncwrites; |
|
sbp->f_asyncwrites = mbp->f_asyncwrites; |
|
sbp->f_syncreads = mbp->f_syncreads; |
|
sbp->f_asyncreads = mbp->f_asyncreads; |
|
(void)memcpy(sbp->f_spare, mbp->f_spare, sizeof(mbp->f_spare)); |
|
(void)memcpy(sbp->f_fstypename, mbp->f_fstypename, |
|
sizeof(sbp->f_fstypename)); |
|
(void)memcpy(sbp->f_mntonname, mbp->f_mntonname, |
|
sizeof(sbp->f_mntonname)); |
|
(void)memcpy(sbp->f_mntfromname, mp->mnt_stat.f_mntfromname, |
|
sizeof(sbp->f_mntfromname)); |
|
sbp->f_namemax = mbp->f_namemax; |
|
} |
|
|
|
int |
|
set_statvfs_info(const char *onp, int ukon, const char *fromp, int ukfrom, |
|
const char *vfsname, struct mount *mp, struct lwp *l) |
|
{ |
|
int error; |
|
size_t size; |
|
struct statvfs *sfs = &mp->mnt_stat; |
|
int (*fun)(const void *, void *, size_t, size_t *); |
|
|
|
(void)strlcpy(mp->mnt_stat.f_fstypename, vfsname, |
|
sizeof(mp->mnt_stat.f_fstypename)); |
|
|
|
if (onp) { |
|
struct cwdinfo *cwdi = l->l_proc->p_cwdi; |
|
fun = (ukon == UIO_SYSSPACE) ? copystr : copyinstr; |
|
if (cwdi->cwdi_rdir != NULL) { |
|
size_t len; |
|
char *bp; |
|
char *path = PNBUF_GET(); |
|
|
|
bp = path + MAXPATHLEN; |
|
*--bp = '\0'; |
|
rw_enter(&cwdi->cwdi_lock, RW_READER); |
|
error = getcwd_common(cwdi->cwdi_rdir, rootvnode, &bp, |
|
path, MAXPATHLEN / 2, 0, l); |
|
rw_exit(&cwdi->cwdi_lock); |
|
if (error) { |
|
PNBUF_PUT(path); |
|
return error; |
|
} |
|
|
|
len = strlen(bp); |
|
if (len > sizeof(sfs->f_mntonname) - 1) |
|
len = sizeof(sfs->f_mntonname) - 1; |
|
(void)strncpy(sfs->f_mntonname, bp, len); |
|
PNBUF_PUT(path); |
|
|
|
if (len < sizeof(sfs->f_mntonname) - 1) { |
|
error = (*fun)(onp, &sfs->f_mntonname[len], |
|
sizeof(sfs->f_mntonname) - len - 1, &size); |
|
if (error) |
|
return error; |
|
size += len; |
|
} else { |
|
size = len; |
|
} |
|
} else { |
|
error = (*fun)(onp, &sfs->f_mntonname, |
|
sizeof(sfs->f_mntonname) - 1, &size); |
|
if (error) |
|
return error; |
|
} |
|
(void)memset(sfs->f_mntonname + size, 0, |
|
sizeof(sfs->f_mntonname) - size); |
|
} |
|
|
|
if (fromp) { |
|
fun = (ukfrom == UIO_SYSSPACE) ? copystr : copyinstr; |
|
error = (*fun)(fromp, sfs->f_mntfromname, |
|
sizeof(sfs->f_mntfromname) - 1, &size); |
|
if (error) |
|
return error; |
|
(void)memset(sfs->f_mntfromname + size, 0, |
|
sizeof(sfs->f_mntfromname) - size); |
|
} |
|
return 0; |
|
} |
|
|
|
void |
|
vfs_timestamp(struct timespec *ts) |
|
{ |
|
|
|
nanotime(ts); |
|
} |
|
|
|
time_t rootfstime; /* recorded root fs time, if known */ |
|
void |
|
setrootfstime(time_t t) |
|
{ |
|
rootfstime = t; |
|
} |
|
|
|
/* |
|
* Sham lock manager for vnodes. This is a temporary measure. |
|
*/ |
|
int |
|
vlockmgr(struct vnlock *vl, int flags) |
|
{ |
|
|
|
KASSERT((flags & ~(LK_CANRECURSE | LK_NOWAIT | LK_TYPE_MASK)) == 0); |
|
|
|
switch (flags & LK_TYPE_MASK) { |
|
case LK_SHARED: |
|
if (rw_tryenter(&vl->vl_lock, RW_READER)) { |
|
return 0; |
|
} |
|
if ((flags & LK_NOWAIT) != 0) { |
|
return EBUSY; |
|
} |
|
rw_enter(&vl->vl_lock, RW_READER); |
|
return 0; |
|
|
|
case LK_EXCLUSIVE: |
|
if (rw_tryenter(&vl->vl_lock, RW_WRITER)) { |
|
return 0; |
|
} |
|
if ((vl->vl_canrecurse || (flags & LK_CANRECURSE) != 0) && |
|
rw_write_held(&vl->vl_lock)) { |
|
vl->vl_recursecnt++; |
|
return 0; |
|
} |
|
if ((flags & LK_NOWAIT) != 0) { |
|
return EBUSY; |
|
} |
|
rw_enter(&vl->vl_lock, RW_WRITER); |
|
return 0; |
|
|
|
case LK_RELEASE: |
|
if (vl->vl_recursecnt != 0) { |
KASSERT(rw_write_held(&vl->vl_lock)); |
KASSERT(rw_write_held(&vl->vl_lock)); |
vl->vl_recursecnt--; |
vl->vl_recursecnt--; |
return 0; |
return 0; |
Line 2001 vlockstatus(struct vnlock *vl) |
|
Line 2826 vlockstatus(struct vnlock *vl) |
|
} |
} |
return 0; |
return 0; |
} |
} |
|
|
|
/* |
|
* mount_specific_key_create -- |
|
* Create a key for subsystem mount-specific data. |
|
*/ |
|
int |
|
mount_specific_key_create(specificdata_key_t *keyp, specificdata_dtor_t dtor) |
|
{ |
|
|
|
return (specificdata_key_create(mount_specificdata_domain, keyp, dtor)); |
|
} |
|
|
|
/* |
|
* mount_specific_key_delete -- |
|
* Delete a key for subsystem mount-specific data. |
|
*/ |
|
void |
|
mount_specific_key_delete(specificdata_key_t key) |
|
{ |
|
|
|
specificdata_key_delete(mount_specificdata_domain, key); |
|
} |
|
|
|
/* |
|
* mount_initspecific -- |
|
* Initialize a mount's specificdata container. |
|
*/ |
|
void |
|
mount_initspecific(struct mount *mp) |
|
{ |
|
int error; |
|
|
|
error = specificdata_init(mount_specificdata_domain, |
|
&mp->mnt_specdataref); |
|
KASSERT(error == 0); |
|
} |
|
|
|
/* |
|
* mount_finispecific -- |
|
* Finalize a mount's specificdata container. |
|
*/ |
|
void |
|
mount_finispecific(struct mount *mp) |
|
{ |
|
|
|
specificdata_fini(mount_specificdata_domain, &mp->mnt_specdataref); |
|
} |
|
|
|
/* |
|
* mount_getspecific -- |
|
* Return mount-specific data corresponding to the specified key. |
|
*/ |
|
void * |
|
mount_getspecific(struct mount *mp, specificdata_key_t key) |
|
{ |
|
|
|
return (specificdata_getspecific(mount_specificdata_domain, |
|
&mp->mnt_specdataref, key)); |
|
} |
|
|
|
/* |
|
* mount_setspecific -- |
|
* Set mount-specific data corresponding to the specified key. |
|
*/ |
|
void |
|
mount_setspecific(struct mount *mp, specificdata_key_t key, void *data) |
|
{ |
|
|
|
specificdata_setspecific(mount_specificdata_domain, |
|
&mp->mnt_specdataref, key, data); |
|
} |
|
|
|
int |
|
VFS_MOUNT(struct mount *mp, const char *a, void *b, size_t *c) |
|
{ |
|
int error; |
|
|
|
KERNEL_LOCK(1, NULL); |
|
error = (*(mp->mnt_op->vfs_mount))(mp, a, b, c); |
|
KERNEL_UNLOCK_ONE(NULL); |
|
|
|
return error; |
|
} |
|
|
|
int |
|
VFS_START(struct mount *mp, int a) |
|
{ |
|
int error; |
|
|
|
if ((mp->mnt_iflag & IMNT_MPSAFE) == 0) { |
|
KERNEL_LOCK(1, NULL); |
|
} |
|
error = (*(mp->mnt_op->vfs_start))(mp, a); |
|
if ((mp->mnt_iflag & IMNT_MPSAFE) == 0) { |
|
KERNEL_UNLOCK_ONE(NULL); |
|
} |
|
|
|
return error; |
|
} |
|
|
|
int |
|
VFS_UNMOUNT(struct mount *mp, int a) |
|
{ |
|
int error; |
|
|
|
KERNEL_LOCK(1, NULL); |
|
error = (*(mp->mnt_op->vfs_unmount))(mp, a); |
|
KERNEL_UNLOCK_ONE(NULL); |
|
|
|
return error; |
|
} |
|
|
|
int |
|
VFS_ROOT(struct mount *mp, struct vnode **a) |
|
{ |
|
int error; |
|
|
|
if ((mp->mnt_iflag & IMNT_MPSAFE) == 0) { |
|
KERNEL_LOCK(1, NULL); |
|
} |
|
error = (*(mp->mnt_op->vfs_root))(mp, a); |
|
if ((mp->mnt_iflag & IMNT_MPSAFE) == 0) { |
|
KERNEL_UNLOCK_ONE(NULL); |
|
} |
|
|
|
return error; |
|
} |
|
|
|
int |
|
VFS_QUOTACTL(struct mount *mp, int a, uid_t b, void *c) |
|
{ |
|
int error; |
|
|
|
if ((mp->mnt_iflag & IMNT_MPSAFE) == 0) { |
|
KERNEL_LOCK(1, NULL); |
|
} |
|
error = (*(mp->mnt_op->vfs_quotactl))(mp, a, b, c); |
|
if ((mp->mnt_iflag & IMNT_MPSAFE) == 0) { |
|
KERNEL_UNLOCK_ONE(NULL); |
|
} |
|
|
|
return error; |
|
} |
|
|
|
int |
|
VFS_STATVFS(struct mount *mp, struct statvfs *a) |
|
{ |
|
int error; |
|
|
|
if ((mp->mnt_iflag & IMNT_MPSAFE) == 0) { |
|
KERNEL_LOCK(1, NULL); |
|
} |
|
error = (*(mp->mnt_op->vfs_statvfs))(mp, a); |
|
if ((mp->mnt_iflag & IMNT_MPSAFE) == 0) { |
|
KERNEL_UNLOCK_ONE(NULL); |
|
} |
|
|
|
return error; |
|
} |
|
|
|
int |
|
VFS_SYNC(struct mount *mp, int a, struct kauth_cred *b) |
|
{ |
|
int error; |
|
|
|
if ((mp->mnt_iflag & IMNT_MPSAFE) == 0) { |
|
KERNEL_LOCK(1, NULL); |
|
} |
|
error = (*(mp->mnt_op->vfs_sync))(mp, a, b); |
|
if ((mp->mnt_iflag & IMNT_MPSAFE) == 0) { |
|
KERNEL_UNLOCK_ONE(NULL); |
|
} |
|
|
|
return error; |
|
} |
|
|
|
int |
|
VFS_FHTOVP(struct mount *mp, struct fid *a, struct vnode **b) |
|
{ |
|
int error; |
|
|
|
if ((mp->mnt_iflag & IMNT_MPSAFE) == 0) { |
|
KERNEL_LOCK(1, NULL); |
|
} |
|
error = (*(mp->mnt_op->vfs_fhtovp))(mp, a, b); |
|
if ((mp->mnt_iflag & IMNT_MPSAFE) == 0) { |
|
KERNEL_UNLOCK_ONE(NULL); |
|
} |
|
|
|
return error; |
|
} |
|
|
|
int |
|
VFS_VPTOFH(struct vnode *vp, struct fid *a, size_t *b) |
|
{ |
|
int error; |
|
|
|
if ((vp->v_vflag & VV_MPSAFE) == 0) { |
|
KERNEL_LOCK(1, NULL); |
|
} |
|
error = (*(vp->v_mount->mnt_op->vfs_vptofh))(vp, a, b); |
|
if ((vp->v_vflag & VV_MPSAFE) == 0) { |
|
KERNEL_UNLOCK_ONE(NULL); |
|
} |
|
|
|
return error; |
|
} |
|
|
|
int |
|
VFS_SNAPSHOT(struct mount *mp, struct vnode *a, struct timespec *b) |
|
{ |
|
int error; |
|
|
|
if ((mp->mnt_iflag & IMNT_MPSAFE) == 0) { |
|
KERNEL_LOCK(1, NULL); |
|
} |
|
error = (*(mp->mnt_op->vfs_snapshot))(mp, a, b); |
|
if ((mp->mnt_iflag & IMNT_MPSAFE) == 0) { |
|
KERNEL_UNLOCK_ONE(NULL); |
|
} |
|
|
|
return error; |
|
} |
|
|
|
int |
|
VFS_EXTATTRCTL(struct mount *mp, int a, struct vnode *b, int c, const char *d) |
|
{ |
|
int error; |
|
|
|
KERNEL_LOCK(1, NULL); /* XXXSMP check ffs */ |
|
error = (*(mp->mnt_op->vfs_extattrctl))(mp, a, b, c, d); |
|
KERNEL_UNLOCK_ONE(NULL); /* XXX */ |
|
|
|
return error; |
|
} |
|
|
|
int |
|
VFS_SUSPENDCTL(struct mount *mp, int a) |
|
{ |
|
int error; |
|
|
|
if ((mp->mnt_iflag & IMNT_MPSAFE) == 0) { |
|
KERNEL_LOCK(1, NULL); |
|
} |
|
error = (*(mp->mnt_op->vfs_suspendctl))(mp, a); |
|
if ((mp->mnt_iflag & IMNT_MPSAFE) == 0) { |
|
KERNEL_UNLOCK_ONE(NULL); |
|
} |
|
|
|
return error; |
|
} |
|
|
|
#ifdef DDB |
|
static const char buf_flagbits[] = BUF_FLAGBITS; |
|
|
|
void |
|
vfs_buf_print(struct buf *bp, int full, void (*pr)(const char *, ...)) |
|
{ |
|
char bf[1024]; |
|
|
|
(*pr)(" vp %p lblkno 0x%"PRIx64" blkno 0x%"PRIx64" rawblkno 0x%" |
|
PRIx64 " dev 0x%x\n", |
|
bp->b_vp, bp->b_lblkno, bp->b_blkno, bp->b_rawblkno, bp->b_dev); |
|
|
|
bitmask_snprintf(bp->b_flags | bp->b_oflags | bp->b_cflags, |
|
buf_flagbits, bf, sizeof(bf)); |
|
(*pr)(" error %d flags 0x%s\n", bp->b_error, bf); |
|
|
|
(*pr)(" bufsize 0x%lx bcount 0x%lx resid 0x%lx\n", |
|
bp->b_bufsize, bp->b_bcount, bp->b_resid); |
|
(*pr)(" data %p saveaddr %p dep %p\n", |
|
bp->b_data, bp->b_saveaddr, LIST_FIRST(&bp->b_dep)); |
|
(*pr)(" iodone %p objlock %p\n", bp->b_iodone, bp->b_objlock); |
|
} |
|
|
|
|
|
void |
|
vfs_vnode_print(struct vnode *vp, int full, void (*pr)(const char *, ...)) |
|
{ |
|
char bf[256]; |
|
|
|
uvm_object_printit(&vp->v_uobj, full, pr); |
|
bitmask_snprintf(vp->v_iflag | vp->v_vflag | vp->v_uflag, |
|
vnode_flagbits, bf, sizeof(bf)); |
|
(*pr)("\nVNODE flags %s\n", bf); |
|
(*pr)("mp %p numoutput %d size 0x%llx writesize 0x%llx\n", |
|
vp->v_mount, vp->v_numoutput, vp->v_size, vp->v_writesize); |
|
|
|
(*pr)("data %p writecount %ld holdcnt %ld\n", |
|
vp->v_data, vp->v_writecount, vp->v_holdcnt); |
|
|
|
(*pr)("tag %s(%d) type %s(%d) mount %p typedata %p\n", |
|
ARRAY_PRINT(vp->v_tag, vnode_tags), vp->v_tag, |
|
ARRAY_PRINT(vp->v_type, vnode_types), vp->v_type, |
|
vp->v_mount, vp->v_mountedhere); |
|
|
|
(*pr)("v_lock %p v_vnlock %p\n", &vp->v_lock, vp->v_vnlock); |
|
|
|
if (full) { |
|
struct buf *bp; |
|
|
|
(*pr)("clean bufs:\n"); |
|
LIST_FOREACH(bp, &vp->v_cleanblkhd, b_vnbufs) { |
|
(*pr)(" bp %p\n", bp); |
|
vfs_buf_print(bp, full, pr); |
|
} |
|
|
|
(*pr)("dirty bufs:\n"); |
|
LIST_FOREACH(bp, &vp->v_dirtyblkhd, b_vnbufs) { |
|
(*pr)(" bp %p\n", bp); |
|
vfs_buf_print(bp, full, pr); |
|
} |
|
} |
|
} |
|
|
|
void |
|
vfs_mount_print(struct mount *mp, int full, void (*pr)(const char *, ...)) |
|
{ |
|
char sbuf[256]; |
|
|
|
(*pr)("vnodecovered = %p syncer = %p data = %p\n", |
|
mp->mnt_vnodecovered,mp->mnt_syncer,mp->mnt_data); |
|
|
|
(*pr)("fs_bshift %d dev_bshift = %d\n", |
|
mp->mnt_fs_bshift,mp->mnt_dev_bshift); |
|
|
|
bitmask_snprintf(mp->mnt_flag, __MNT_FLAG_BITS, sbuf, sizeof(sbuf)); |
|
(*pr)("flag = %s\n", sbuf); |
|
|
|
bitmask_snprintf(mp->mnt_iflag, __IMNT_FLAG_BITS, sbuf, sizeof(sbuf)); |
|
(*pr)("iflag = %s\n", sbuf); |
|
|
|
(*pr)("refcnt = %d unmounting @ %p updating @ %p\n", mp->mnt_refcnt, |
|
&mp->mnt_unmounting, &mp->mnt_updating); |
|
|
|
(*pr)("statvfs cache:\n"); |
|
(*pr)("\tbsize = %lu\n",mp->mnt_stat.f_bsize); |
|
(*pr)("\tfrsize = %lu\n",mp->mnt_stat.f_frsize); |
|
(*pr)("\tiosize = %lu\n",mp->mnt_stat.f_iosize); |
|
|
|
(*pr)("\tblocks = %"PRIu64"\n",mp->mnt_stat.f_blocks); |
|
(*pr)("\tbfree = %"PRIu64"\n",mp->mnt_stat.f_bfree); |
|
(*pr)("\tbavail = %"PRIu64"\n",mp->mnt_stat.f_bavail); |
|
(*pr)("\tbresvd = %"PRIu64"\n",mp->mnt_stat.f_bresvd); |
|
|
|
(*pr)("\tfiles = %"PRIu64"\n",mp->mnt_stat.f_files); |
|
(*pr)("\tffree = %"PRIu64"\n",mp->mnt_stat.f_ffree); |
|
(*pr)("\tfavail = %"PRIu64"\n",mp->mnt_stat.f_favail); |
|
(*pr)("\tfresvd = %"PRIu64"\n",mp->mnt_stat.f_fresvd); |
|
|
|
(*pr)("\tf_fsidx = { 0x%"PRIx32", 0x%"PRIx32" }\n", |
|
mp->mnt_stat.f_fsidx.__fsid_val[0], |
|
mp->mnt_stat.f_fsidx.__fsid_val[1]); |
|
|
|
(*pr)("\towner = %"PRIu32"\n",mp->mnt_stat.f_owner); |
|
(*pr)("\tnamemax = %lu\n",mp->mnt_stat.f_namemax); |
|
|
|
bitmask_snprintf(mp->mnt_stat.f_flag, __MNT_FLAG_BITS, sbuf, |
|
sizeof(sbuf)); |
|
(*pr)("\tflag = %s\n",sbuf); |
|
(*pr)("\tsyncwrites = %" PRIu64 "\n",mp->mnt_stat.f_syncwrites); |
|
(*pr)("\tasyncwrites = %" PRIu64 "\n",mp->mnt_stat.f_asyncwrites); |
|
(*pr)("\tsyncreads = %" PRIu64 "\n",mp->mnt_stat.f_syncreads); |
|
(*pr)("\tasyncreads = %" PRIu64 "\n",mp->mnt_stat.f_asyncreads); |
|
(*pr)("\tfstypename = %s\n",mp->mnt_stat.f_fstypename); |
|
(*pr)("\tmntonname = %s\n",mp->mnt_stat.f_mntonname); |
|
(*pr)("\tmntfromname = %s\n",mp->mnt_stat.f_mntfromname); |
|
|
|
{ |
|
int cnt = 0; |
|
struct vnode *vp; |
|
(*pr)("locked vnodes ="); |
|
TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) { |
|
if (VOP_ISLOCKED(vp)) { |
|
if ((++cnt % 6) == 0) { |
|
(*pr)(" %p,\n\t", vp); |
|
} else { |
|
(*pr)(" %p,", vp); |
|
} |
|
} |
|
} |
|
(*pr)("\n"); |
|
} |
|
|
|
if (full) { |
|
int cnt = 0; |
|
struct vnode *vp; |
|
(*pr)("all vnodes ="); |
|
TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) { |
|
if (!TAILQ_NEXT(vp, v_mntvnodes)) { |
|
(*pr)(" %p", vp); |
|
} else if ((++cnt % 6) == 0) { |
|
(*pr)(" %p,\n\t", vp); |
|
} else { |
|
(*pr)(" %p,", vp); |
|
} |
|
} |
|
(*pr)("\n", vp); |
|
} |
|
} |
|
#endif /* DDB */ |