[BACK]Return to vfs_subr.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / kern

Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.

Diff for /src/sys/kern/vfs_subr.c between version 1.350.2.2 and 1.350.2.3

version 1.350.2.2, 2008/06/27 15:11:39 version 1.350.2.3, 2008/07/18 16:37:49
Line 67 
Line 67 
  */   */
   
 /*  /*
  * 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)
Line 178  vn_init1(void)
Line 216  vn_init1(void)
                 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. */
Line 1941  done:
Line 2429  done:
 }  }
   
 /*  /*
  * 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 */

Legend:
Removed from v.1.350.2.2  
changed lines
  Added in v.1.350.2.3

CVSweb <webmaster@jp.NetBSD.org>