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

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

Diff for /src/sys/ufs/ufs/ufs_dirhash.c between version 1.10.8.1 and 1.34.22.2

version 1.10.8.1, 2006/04/19 03:54:14 version 1.34.22.2, 2014/08/20 00:04:45
Line 27 
Line 27 
  * $FreeBSD: src/sys/ufs/ufs/ufs_dirhash.c,v 1.3.2.8 2004/12/08 11:54:13 dwmalone Exp $   * $FreeBSD: src/sys/ufs/ufs/ufs_dirhash.c,v 1.3.2.8 2004/12/08 11:54:13 dwmalone Exp $
  */   */
   
   #include <sys/cdefs.h>
   __KERNEL_RCSID(0, "$NetBSD$");
   
 /*  /*
  * This implements a hash-based lookup scheme for UFS directories.   * This implements a hash-based lookup scheme for UFS directories.
  */   */
Line 34 
Line 37 
 #include <sys/param.h>  #include <sys/param.h>
 #include <sys/systm.h>  #include <sys/systm.h>
 #include <sys/kernel.h>  #include <sys/kernel.h>
 #include <sys/malloc.h>  #include <sys/kmem.h>
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/hash.h>  #include <sys/hash.h>
 #include <sys/proc.h>  #include <sys/proc.h>
Line 43 
Line 46 
 #include <sys/mount.h>  #include <sys/mount.h>
 #include <sys/pool.h>  #include <sys/pool.h>
 #include <sys/sysctl.h>  #include <sys/sysctl.h>
   #include <sys/atomic.h>
   
 #include <ufs/ufs/quota.h>  
 #include <ufs/ufs/inode.h>  #include <ufs/ufs/inode.h>
 #include <ufs/ufs/dir.h>  #include <ufs/ufs/dir.h>
 #include <ufs/ufs/dirhash.h>  #include <ufs/ufs/dirhash.h>
Line 57 
Line 60 
 #define OFSFMT(ip)              ((ip)->i_ump->um_maxsymlinklen <= 0)  #define OFSFMT(ip)              ((ip)->i_ump->um_maxsymlinklen <= 0)
 #define BLKFREE2IDX(n)          ((n) > DH_NFSTATS ? DH_NFSTATS : (n))  #define BLKFREE2IDX(n)          ((n) > DH_NFSTATS ? DH_NFSTATS : (n))
   
 static MALLOC_DEFINE(M_DIRHASH, "UFS dirhash", "UFS directory hash tables");  static u_int ufs_dirhashminblks = 5;
   static u_int ufs_dirhashmaxmem = 2 * 1024 * 1024;
 static int ufs_dirhashminblks = 5;  static u_int ufs_dirhashmem;
 static int ufs_dirhashmaxmem = 2 * 1024 * 1024;  static u_int ufs_dirhashcheck = 0;
 static int ufs_dirhashmem;  
 static int ufs_dirhashcheck = 0;  
   
 static int ufsdirhash_hash(struct dirhash *dh, const char *name, int namelen);  static int ufsdirhash_hash(struct dirhash *dh, const char *name, int namelen);
 static void ufsdirhash_adjfree(struct dirhash *dh, doff_t offset, int diff,  static void ufsdirhash_adjfree(struct dirhash *dh, doff_t offset, int diff,
Line 74  static doff_t ufsdirhash_getprev(struct 
Line 75  static doff_t ufsdirhash_getprev(struct 
            int dirblksiz);             int dirblksiz);
 static int ufsdirhash_recycle(int wanted);  static int ufsdirhash_recycle(int wanted);
   
 static POOL_INIT(ufsdirhash_pool, DH_NBLKOFF * sizeof(daddr_t), 0, 0, 0,  static pool_cache_t ufsdirhashblk_cache;
     "ufsdirhash", &pool_allocator_nointr);  static pool_cache_t ufsdirhash_cache;
   
 #define DIRHASHLIST_LOCK()              do { } while (0)  #define DIRHASHLIST_LOCK()              mutex_enter(&ufsdirhash_lock)
 #define DIRHASHLIST_UNLOCK()            do { } while (0)  #define DIRHASHLIST_UNLOCK()            mutex_exit(&ufsdirhash_lock)
 #define DIRHASH_LOCK(dh)                do { } while (0)  #define DIRHASH_LOCK(dh)                mutex_enter(&(dh)->dh_lock)
 #define DIRHASH_UNLOCK(dh)              do { } while (0)  #define DIRHASH_UNLOCK(dh)              mutex_exit(&(dh)->dh_lock)
 #define DIRHASH_BLKALLOC_WAITOK()       pool_get(&ufsdirhash_pool, PR_WAITOK)  #define DIRHASH_BLKALLOC()              \
 #define DIRHASH_BLKFREE(ptr)            pool_put(&ufsdirhash_pool, ptr)      pool_cache_get(ufsdirhashblk_cache, PR_NOWAIT)
   #define DIRHASH_BLKFREE(ptr)            \
       pool_cache_put(ufsdirhashblk_cache, ptr)
   
 /* Dirhash list; recently-used entries are near the tail. */  /* Dirhash list; recently-used entries are near the tail. */
 static TAILQ_HEAD(, dirhash) ufsdirhash_list;  static TAILQ_HEAD(, dirhash) ufsdirhash_list;
   
   /* Protects: ufsdirhash_list, `dh_list' field, ufs_dirhashmem. */
   static kmutex_t ufsdirhash_lock;
   
   static struct sysctllog *ufsdirhash_sysctl_log;
   
   /*
    * Locking order:
    *      ufsdirhash_lock
    *      dh_lock
    *
    * The dh_lock mutex should be acquired either via the inode lock, or via
    * ufsdirhash_lock. Only the owner of the inode may free the associated
    * dirhash, but anything can steal its memory and set dh_hash to NULL.
    */
   
 /*  /*
  * Attempt to build up a hash table for the directory contents in   * Attempt to build up a hash table for the directory contents in
  * inode 'ip'. Returns 0 on success, or -1 of the operation failed.   * inode 'ip'. Returns 0 on success, or -1 of the operation failed.
Line 122  ufsdirhash_build(struct inode *ip)
Line 140  ufsdirhash_build(struct inode *ip)
         }          }
   
         /* Don't hash removed directories. */          /* Don't hash removed directories. */
         if (ip->i_ffs_effnlink == 0)          if (ip->i_nlink == 0)
                 return (-1);                  return (-1);
   
         vp = ip->i_vnode;          vp = ip->i_vnode;
         /* Allocate 50% more entries than this dir size could ever need. */          /* Allocate 50% more entries than this dir size could ever need. */
         KASSERT(ip->i_size >= dirblksiz);          KASSERT(ip->i_size >= dirblksiz);
         nslots = ip->i_size / DIRECTSIZ(1);          nslots = ip->i_size / UFS_DIRECTSIZ(1);
         nslots = (nslots * 3 + 1) / 2;          nslots = (nslots * 3 + 1) / 2;
         narrays = howmany(nslots, DH_NBLKOFF);          narrays = howmany(nslots, DH_NBLKOFF);
         nslots = narrays * DH_NBLKOFF;          nslots = narrays * DH_NBLKOFF;
Line 138  ufsdirhash_build(struct inode *ip)
Line 156  ufsdirhash_build(struct inode *ip)
         memreqd = sizeof(*dh) + narrays * sizeof(*dh->dh_hash) +          memreqd = sizeof(*dh) + narrays * sizeof(*dh->dh_hash) +
             narrays * DH_NBLKOFF * sizeof(**dh->dh_hash) +              narrays * DH_NBLKOFF * sizeof(**dh->dh_hash) +
             nblocks * sizeof(*dh->dh_blkfree);              nblocks * sizeof(*dh->dh_blkfree);
         DIRHASHLIST_LOCK();  
         if (memreqd + ufs_dirhashmem > ufs_dirhashmaxmem) {          while (atomic_add_int_nv(&ufs_dirhashmem, memreqd) >
                 DIRHASHLIST_UNLOCK();              ufs_dirhashmaxmem) {
                   atomic_add_int(&ufs_dirhashmem, -memreqd);
                 if (memreqd > ufs_dirhashmaxmem / 2)                  if (memreqd > ufs_dirhashmaxmem / 2)
                         return (-1);                          return (-1);
   
                 /* Try to free some space. */                  /* Try to free some space. */
                 if (ufsdirhash_recycle(memreqd) != 0)                  if (ufsdirhash_recycle(memreqd) != 0)
                         return (-1);                          return (-1);
                 /* Enough was freed, and list has been locked. */                  else
                           DIRHASHLIST_UNLOCK();
         }          }
         ufs_dirhashmem += memreqd;  
         DIRHASHLIST_UNLOCK();  
   
         /*          /*
          * Use non-blocking mallocs so that we will revert to a linear           * Use non-blocking mallocs so that we will revert to a linear
          * lookup on failure rather than potentially blocking forever.           * lookup on failure rather than potentially blocking forever.
          */           */
         MALLOC(dh, struct dirhash *, sizeof *dh, M_DIRHASH, M_NOWAIT | M_ZERO);          dh = pool_cache_get(ufsdirhash_cache, PR_NOWAIT);
         if (dh == NULL) {          if (dh == NULL) {
                 DIRHASHLIST_LOCK();                  atomic_add_int(&ufs_dirhashmem, -memreqd);
                 ufs_dirhashmem -= memreqd;  
                 DIRHASHLIST_UNLOCK();  
                 return (-1);                  return (-1);
         }          }
         dh->dh_hash = (doff_t **)malloc(narrays * sizeof(dh->dh_hash[0]),          memset(dh, 0, sizeof(*dh));
             M_DIRHASH, M_NOWAIT | M_ZERO);          mutex_init(&dh->dh_lock, MUTEX_DEFAULT, IPL_NONE);
         dh->dh_blkfree = (u_int8_t *)malloc(nblocks * sizeof(dh->dh_blkfree[0]),          DIRHASH_LOCK(dh);
             M_DIRHASH, M_NOWAIT);          dh->dh_hashsz = narrays * sizeof(dh->dh_hash[0]);
           dh->dh_hash = kmem_zalloc(dh->dh_hashsz, KM_NOSLEEP);
           dh->dh_blkfreesz = nblocks * sizeof(dh->dh_blkfree[0]);
           dh->dh_blkfree = kmem_zalloc(dh->dh_blkfreesz, KM_NOSLEEP);
         if (dh->dh_hash == NULL || dh->dh_blkfree == NULL)          if (dh->dh_hash == NULL || dh->dh_blkfree == NULL)
                 goto fail;                  goto fail;
         for (i = 0; i < narrays; i++) {          for (i = 0; i < narrays; i++) {
                 if ((dh->dh_hash[i] = DIRHASH_BLKALLOC_WAITOK()) == NULL)                  if ((dh->dh_hash[i] = DIRHASH_BLKALLOC()) == NULL)
                         goto fail;                          goto fail;
                 for (j = 0; j < DH_NBLKOFF; j++)                  for (j = 0; j < DH_NBLKOFF; j++)
                         dh->dh_hash[i][j] = DIRHASH_EMPTY;                          dh->dh_hash[i][j] = DIRHASH_EMPTY;
Line 196  ufsdirhash_build(struct inode *ip)
Line 214  ufsdirhash_build(struct inode *ip)
         while (pos < ip->i_size) {          while (pos < ip->i_size) {
                 if ((curcpu()->ci_schedstate.spc_flags & SPCF_SHOULDYIELD)                  if ((curcpu()->ci_schedstate.spc_flags & SPCF_SHOULDYIELD)
                     != 0) {                      != 0) {
                         preempt(1);                          preempt();
                 }                  }
                 /* If necessary, get the next directory block. */                  /* If necessary, get the next directory block. */
                 if ((pos & bmask) == 0) {                  if ((pos & bmask) == 0) {
                         if (bp != NULL)                          if (bp != NULL)
                                 brelse(bp);                                  brelse(bp, 0);
                         if (ufs_blkatoff(vp, (off_t)pos, NULL, &bp) != 0)                          if (ufs_blkatoff(vp, (off_t)pos, NULL, &bp, false) != 0)
                                 goto fail;                                  goto fail;
                 }                  }
   
Line 211  ufsdirhash_build(struct inode *ip)
Line 229  ufsdirhash_build(struct inode *ip)
                 if (ep->d_reclen == 0 || ep->d_reclen >                  if (ep->d_reclen == 0 || ep->d_reclen >
                     dirblksiz - (pos & (dirblksiz - 1))) {                      dirblksiz - (pos & (dirblksiz - 1))) {
                         /* Corrupted directory. */                          /* Corrupted directory. */
                         brelse(bp);                          brelse(bp, 0);
                         goto fail;                          goto fail;
                 }                  }
                 if (ep->d_ino != 0) {                  if (ep->d_ino != 0) {
Line 221  ufsdirhash_build(struct inode *ip)
Line 239  ufsdirhash_build(struct inode *ip)
                                 slot = WRAPINCR(slot, dh->dh_hlen);                                  slot = WRAPINCR(slot, dh->dh_hlen);
                         dh->dh_hused++;                          dh->dh_hused++;
                         DH_ENTRY(dh, slot) = pos;                          DH_ENTRY(dh, slot) = pos;
                         ufsdirhash_adjfree(dh, pos, -DIRSIZ(0, ep, needswap),                          ufsdirhash_adjfree(dh, pos, -UFS_DIRSIZ(0, ep, needswap),
                             dirblksiz);                              dirblksiz);
                 }                  }
                 pos += ep->d_reclen;                  pos += ep->d_reclen;
         }          }
   
         if (bp != NULL)          if (bp != NULL)
                 brelse(bp);                  brelse(bp, 0);
         DIRHASHLIST_LOCK();          DIRHASHLIST_LOCK();
         TAILQ_INSERT_TAIL(&ufsdirhash_list, dh, dh_list);          TAILQ_INSERT_TAIL(&ufsdirhash_list, dh, dh_list);
         dh->dh_onlist = 1;          dh->dh_onlist = 1;
           DIRHASH_UNLOCK(dh);
         DIRHASHLIST_UNLOCK();          DIRHASHLIST_UNLOCK();
         return (0);          return (0);
   
 fail:  fail:
           DIRHASH_UNLOCK(dh);
         if (dh->dh_hash != NULL) {          if (dh->dh_hash != NULL) {
                 for (i = 0; i < narrays; i++)                  for (i = 0; i < narrays; i++)
                         if (dh->dh_hash[i] != NULL)                          if (dh->dh_hash[i] != NULL)
                                 DIRHASH_BLKFREE(dh->dh_hash[i]);                                  DIRHASH_BLKFREE(dh->dh_hash[i]);
                 FREE(dh->dh_hash, M_DIRHASH);                  kmem_free(dh->dh_hash, dh->dh_hashsz);
         }          }
         if (dh->dh_blkfree != NULL)          if (dh->dh_blkfree != NULL)
                 FREE(dh->dh_blkfree, M_DIRHASH);                  kmem_free(dh->dh_blkfree, dh->dh_blkfreesz);
         FREE(dh, M_DIRHASH);          mutex_destroy(&dh->dh_lock);
           pool_cache_put(ufsdirhash_cache, dh);
         ip->i_dirhash = NULL;          ip->i_dirhash = NULL;
         DIRHASHLIST_LOCK();          atomic_add_int(&ufs_dirhashmem, -memreqd);
         ufs_dirhashmem -= memreqd;  
         DIRHASHLIST_UNLOCK();  
         return (-1);          return (-1);
 }  }
   
Line 263  ufsdirhash_free(struct inode *ip)
Line 282  ufsdirhash_free(struct inode *ip)
   
         if ((dh = ip->i_dirhash) == NULL)          if ((dh = ip->i_dirhash) == NULL)
                 return;                  return;
         DIRHASHLIST_LOCK();  
         DIRHASH_LOCK(dh);  
         if (dh->dh_onlist)  
                 TAILQ_REMOVE(&ufsdirhash_list, dh, dh_list);  
         DIRHASH_UNLOCK(dh);  
         DIRHASHLIST_UNLOCK();  
   
         /* The dirhash pointed to by 'dh' is exclusively ours now. */          if (dh->dh_onlist) {
                   DIRHASHLIST_LOCK();
                   if (dh->dh_onlist)
                           TAILQ_REMOVE(&ufsdirhash_list, dh, dh_list);
                   DIRHASHLIST_UNLOCK();
           }
   
           /* The dirhash pointed to by 'dh' is exclusively ours now. */
         mem = sizeof(*dh);          mem = sizeof(*dh);
         if (dh->dh_hash != NULL) {          if (dh->dh_hash != NULL) {
                 for (i = 0; i < dh->dh_narrays; i++)                  for (i = 0; i < dh->dh_narrays; i++)
                         DIRHASH_BLKFREE(dh->dh_hash[i]);                          DIRHASH_BLKFREE(dh->dh_hash[i]);
                 FREE(dh->dh_hash, M_DIRHASH);                  kmem_free(dh->dh_hash, dh->dh_hashsz);
                 FREE(dh->dh_blkfree, M_DIRHASH);                  kmem_free(dh->dh_blkfree, dh->dh_blkfreesz);
                 mem += dh->dh_narrays * sizeof(*dh->dh_hash) +                  mem += dh->dh_hashsz;
                     dh->dh_narrays * DH_NBLKOFF * sizeof(**dh->dh_hash) +                  mem += dh->dh_narrays * DH_NBLKOFF * sizeof(**dh->dh_hash);
                     dh->dh_nblk * sizeof(*dh->dh_blkfree);                  mem += dh->dh_nblk * sizeof(*dh->dh_blkfree);
         }          }
         FREE(dh, M_DIRHASH);          mutex_destroy(&dh->dh_lock);
           pool_cache_put(ufsdirhash_cache, dh);
         ip->i_dirhash = NULL;          ip->i_dirhash = NULL;
   
         DIRHASHLIST_LOCK();          atomic_add_int(&ufs_dirhashmem, -mem);
         ufs_dirhashmem -= mem;  
         DIRHASHLIST_UNLOCK();  
 }  }
   
 /*  /*
Line 298  ufsdirhash_free(struct inode *ip)
Line 316  ufsdirhash_free(struct inode *ip)
  * If successful, the directory offset is stored in *offp, and a   * If successful, the directory offset is stored in *offp, and a
  * pointer to a struct buf containing the entry is stored in *bpp. If   * pointer to a struct buf containing the entry is stored in *bpp. If
  * prevoffp is non-NULL, the offset of the previous entry within   * prevoffp is non-NULL, the offset of the previous entry within
  * the DIRBLKSIZ-sized block is stored in *prevoffp (if the entry   * the UFS_DIRBLKSIZ-sized block is stored in *prevoffp (if the entry
  * is the first in a block, the start of the block is used).   * is the first in a block, the start of the block is used).
  */   */
 int  int
Line 316  ufsdirhash_lookup(struct inode *ip, cons
Line 334  ufsdirhash_lookup(struct inode *ip, cons
   
         if ((dh = ip->i_dirhash) == NULL)          if ((dh = ip->i_dirhash) == NULL)
                 return (EJUSTRETURN);                  return (EJUSTRETURN);
   
         /*          /*
          * Move this dirhash towards the end of the list if it has a           * Move this dirhash towards the end of the list if it has a
          * score higher than the next entry, and acquire the dh_mtx.           * score higher than the next entry, and acquire the dh_lock.
          * Optimise the case where it's already the last by performing           * Optimise the case where it's already the last by performing
          * an unlocked read of the TAILQ_NEXT pointer.           * an unlocked read of the TAILQ_NEXT pointer.
          *           *
          * In both cases, end up holding just dh_mtx.           * In both cases, end up holding just dh_lock.
          */           */
         if (TAILQ_NEXT(dh, dh_list) != NULL) {          if (TAILQ_NEXT(dh, dh_list) != NULL) {
                 DIRHASHLIST_LOCK();                  DIRHASHLIST_LOCK();
Line 389  restart:
Line 408  restart:
             slot = WRAPINCR(slot, dh->dh_hlen)) {              slot = WRAPINCR(slot, dh->dh_hlen)) {
                 if (offset == DIRHASH_DEL)                  if (offset == DIRHASH_DEL)
                         continue;                          continue;
                 DIRHASH_UNLOCK(dh);  
   
                 if (offset < 0 || offset >= ip->i_size)                  if (offset < 0 || offset >= ip->i_size)
                         panic("ufsdirhash_lookup: bad offset in hash array");                          panic("ufsdirhash_lookup: bad offset in hash array");
                 if ((offset & ~bmask) != blkoff) {                  if ((offset & ~bmask) != blkoff) {
                         if (bp != NULL)                          if (bp != NULL)
                                 brelse(bp);                                  brelse(bp, 0);
                         blkoff = offset & ~bmask;                          blkoff = offset & ~bmask;
                         if (ufs_blkatoff(vp, (off_t)blkoff, NULL, &bp) != 0)                          if (ufs_blkatoff(vp, (off_t)blkoff,
                               NULL, &bp, false) != 0) {
                                   DIRHASH_UNLOCK(dh);
                                 return (EJUSTRETURN);                                  return (EJUSTRETURN);
                           }
                 }                  }
                 dp = (struct direct *)(bp->b_data + (offset & bmask));                  dp = (struct direct *)((char *)bp->b_data + (offset & bmask));
                 if (dp->d_reclen == 0 || dp->d_reclen >                  if (dp->d_reclen == 0 || dp->d_reclen >
                     dirblksiz - (offset & (dirblksiz - 1))) {                      dirblksiz - (offset & (dirblksiz - 1))) {
                         /* Corrupted directory. */                          /* Corrupted directory. */
                         brelse(bp);                          DIRHASH_UNLOCK(dh);
                           brelse(bp, 0);
                         return (EJUSTRETURN);                          return (EJUSTRETURN);
                 }                  }
                 if (dp->d_namlen == namelen &&                  if (dp->d_namlen == namelen &&
                     bcmp(dp->d_name, name, namelen) == 0) {                      memcmp(dp->d_name, name, namelen) == 0) {
                         /* Found. Get the prev offset if needed. */                          /* Found. Get the prev offset if needed. */
                         if (prevoffp != NULL) {                          if (prevoffp != NULL) {
                                 if (offset & (dirblksiz - 1)) {                                  if (offset & (dirblksiz - 1)) {
                                         prevoff = ufsdirhash_getprev(dp,                                          prevoff = ufsdirhash_getprev(dp,
                                             offset, dirblksiz);                                              offset, dirblksiz);
                                         if (prevoff == -1) {                                          if (prevoff == -1) {
                                                 brelse(bp);                                                  brelse(bp, 0);
                                                 return (EJUSTRETURN);                                                  return (EJUSTRETURN);
                                         }                                          }
                                 } else                                  } else
Line 426  restart:
Line 448  restart:
                         /* Check for sequential access, and update offset. */                          /* Check for sequential access, and update offset. */
                         if (dh->dh_seqopt == 0 && dh->dh_seqoff == offset)                          if (dh->dh_seqopt == 0 && dh->dh_seqoff == offset)
                                 dh->dh_seqopt = 1;                                  dh->dh_seqopt = 1;
                         dh->dh_seqoff = offset + DIRSIZ(0, dp, needswap);                          dh->dh_seqoff = offset + UFS_DIRSIZ(0, dp, needswap);
                           DIRHASH_UNLOCK(dh);
   
                         *bpp = bp;                          *bpp = bp;
                         *offp = offset;                          *offp = offset;
                         return (0);                          return (0);
                 }                  }
   
                 DIRHASH_LOCK(dh);  
                 if (dh->dh_hash == NULL) {                  if (dh->dh_hash == NULL) {
                         DIRHASH_UNLOCK(dh);                          DIRHASH_UNLOCK(dh);
                         if (bp != NULL)                          if (bp != NULL)
                                 brelse(bp);                                  brelse(bp, 0);
                         ufsdirhash_free(ip);                          ufsdirhash_free(ip);
                         return (EJUSTRETURN);                          return (EJUSTRETURN);
                 }                  }
Line 452  restart:
Line 474  restart:
         }          }
         DIRHASH_UNLOCK(dh);          DIRHASH_UNLOCK(dh);
         if (bp != NULL)          if (bp != NULL)
                 brelse(bp);                  brelse(bp, 0);
         return (ENOENT);          return (ENOENT);
 }  }
   
Line 461  restart:
Line 483  restart:
  * the offset of the directory entry that begins the free space.   * the offset of the directory entry that begins the free space.
  * This will either be the offset of an existing entry that has free   * This will either be the offset of an existing entry that has free
  * space at the end, or the offset of an entry with d_ino == 0 at   * space at the end, or the offset of an entry with d_ino == 0 at
  * the start of a DIRBLKSIZ block.   * the start of a UFS_DIRBLKSIZ block.
  *   *
  * To use the space, the caller may need to compact existing entries in   * To use the space, the caller may need to compact existing entries in
  * the directory. The total number of bytes in all of the entries involved   * the directory. The total number of bytes in all of the entries involved
Line 485  ufsdirhash_findfree(struct inode *ip, in
Line 507  ufsdirhash_findfree(struct inode *ip, in
   
         if ((dh = ip->i_dirhash) == NULL)          if ((dh = ip->i_dirhash) == NULL)
                 return (-1);                  return (-1);
   
         DIRHASH_LOCK(dh);          DIRHASH_LOCK(dh);
         if (dh->dh_hash == NULL) {          if (dh->dh_hash == NULL) {
                 DIRHASH_UNLOCK(dh);                  DIRHASH_UNLOCK(dh);
Line 504  ufsdirhash_findfree(struct inode *ip, in
Line 527  ufsdirhash_findfree(struct inode *ip, in
   
         KASSERT(dirblock < dh->dh_nblk &&          KASSERT(dirblock < dh->dh_nblk &&
             dh->dh_blkfree[dirblock] >= howmany(slotneeded, DIRALIGN));              dh->dh_blkfree[dirblock] >= howmany(slotneeded, DIRALIGN));
         DIRHASH_UNLOCK(dh);  
         pos = dirblock * dirblksiz;          pos = dirblock * dirblksiz;
         error = ufs_blkatoff(ip->i_vnode, (off_t)pos, (void *)&dp, &bp);          error = ufs_blkatoff(ip->i_vnode, (off_t)pos, (void *)&dp, &bp, false);
         if (error)          if (error) {
                   DIRHASH_UNLOCK(dh);
                 return (-1);                  return (-1);
           }
         /* Find the first entry with free space. */          /* Find the first entry with free space. */
         for (i = 0; i < dirblksiz; ) {          for (i = 0; i < dirblksiz; ) {
                 if (dp->d_reclen == 0) {                  if (dp->d_reclen == 0) {
                         brelse(bp);                          DIRHASH_UNLOCK(dh);
                           brelse(bp, 0);
                         return (-1);                          return (-1);
                 }                  }
                 if (dp->d_ino == 0 || dp->d_reclen > DIRSIZ(0, dp, needswap))                  if (dp->d_ino == 0 || dp->d_reclen > UFS_DIRSIZ(0, dp, needswap))
                         break;                          break;
                 i += dp->d_reclen;                  i += dp->d_reclen;
                 dp = (struct direct *)((char *)dp + dp->d_reclen);                  dp = (struct direct *)((char *)dp + dp->d_reclen);
         }          }
         if (i > dirblksiz) {          if (i > dirblksiz) {
                 brelse(bp);                  DIRHASH_UNLOCK(dh);
                   brelse(bp, 0);
                 return (-1);                  return (-1);
         }          }
         slotstart = pos + i;          slotstart = pos + i;
Line 531  ufsdirhash_findfree(struct inode *ip, in
Line 557  ufsdirhash_findfree(struct inode *ip, in
         while (i < dirblksiz && freebytes < slotneeded) {          while (i < dirblksiz && freebytes < slotneeded) {
                 freebytes += dp->d_reclen;                  freebytes += dp->d_reclen;
                 if (dp->d_ino != 0)                  if (dp->d_ino != 0)
                         freebytes -= DIRSIZ(0, dp, needswap);                          freebytes -= UFS_DIRSIZ(0, dp, needswap);
                 if (dp->d_reclen == 0) {                  if (dp->d_reclen == 0) {
                         brelse(bp);                          DIRHASH_UNLOCK(dh);
                           brelse(bp, 0);
                         return (-1);                          return (-1);
                 }                  }
                 i += dp->d_reclen;                  i += dp->d_reclen;
                 dp = (struct direct *)((char *)dp + dp->d_reclen);                  dp = (struct direct *)((char *)dp + dp->d_reclen);
         }          }
         if (i > dirblksiz) {          if (i > dirblksiz) {
                 brelse(bp);                  DIRHASH_UNLOCK(dh);
                   brelse(bp, 0);
                 return (-1);                  return (-1);
         }          }
         if (freebytes < slotneeded)          if (freebytes < slotneeded)
                 panic("ufsdirhash_findfree: free mismatch");                  panic("ufsdirhash_findfree: free mismatch");
         brelse(bp);          DIRHASH_UNLOCK(dh);
           brelse(bp, 0);
         *slotsize = pos + i - slotstart;          *slotsize = pos + i - slotstart;
         return (slotstart);          return (slotstart);
 }  }
Line 563  ufsdirhash_enduseful(struct inode *ip)
Line 592  ufsdirhash_enduseful(struct inode *ip)
   
         if ((dh = ip->i_dirhash) == NULL)          if ((dh = ip->i_dirhash) == NULL)
                 return (-1);                  return (-1);
   
         DIRHASH_LOCK(dh);          DIRHASH_LOCK(dh);
         if (dh->dh_hash == NULL) {          if (dh->dh_hash == NULL) {
                 DIRHASH_UNLOCK(dh);                  DIRHASH_UNLOCK(dh);
Line 597  ufsdirhash_add(struct inode *ip, struct 
Line 627  ufsdirhash_add(struct inode *ip, struct 
   
         if ((dh = ip->i_dirhash) == NULL)          if ((dh = ip->i_dirhash) == NULL)
                 return;                  return;
   
         DIRHASH_LOCK(dh);          DIRHASH_LOCK(dh);
         if (dh->dh_hash == NULL) {          if (dh->dh_hash == NULL) {
                 DIRHASH_UNLOCK(dh);                  DIRHASH_UNLOCK(dh);
Line 624  ufsdirhash_add(struct inode *ip, struct 
Line 655  ufsdirhash_add(struct inode *ip, struct 
         DH_ENTRY(dh, slot) = offset;          DH_ENTRY(dh, slot) = offset;
   
         /* Update the per-block summary info. */          /* Update the per-block summary info. */
         ufsdirhash_adjfree(dh, offset, -DIRSIZ(0, dirp, needswap), dirblksiz);          ufsdirhash_adjfree(dh, offset, -UFS_DIRSIZ(0, dirp, needswap), dirblksiz);
         DIRHASH_UNLOCK(dh);          DIRHASH_UNLOCK(dh);
 }  }
   
Line 643  ufsdirhash_remove(struct inode *ip, stru
Line 674  ufsdirhash_remove(struct inode *ip, stru
   
         if ((dh = ip->i_dirhash) == NULL)          if ((dh = ip->i_dirhash) == NULL)
                 return;                  return;
   
         DIRHASH_LOCK(dh);          DIRHASH_LOCK(dh);
         if (dh->dh_hash == NULL) {          if (dh->dh_hash == NULL) {
                 DIRHASH_UNLOCK(dh);                  DIRHASH_UNLOCK(dh);
Line 658  ufsdirhash_remove(struct inode *ip, stru
Line 690  ufsdirhash_remove(struct inode *ip, stru
         ufsdirhash_delslot(dh, slot);          ufsdirhash_delslot(dh, slot);
   
         /* Update the per-block summary info. */          /* Update the per-block summary info. */
         ufsdirhash_adjfree(dh, offset, DIRSIZ(0, dirp, needswap), dirblksiz);          ufsdirhash_adjfree(dh, offset, UFS_DIRSIZ(0, dirp, needswap), dirblksiz);
         DIRHASH_UNLOCK(dh);          DIRHASH_UNLOCK(dh);
 }  }
   
Line 692  ufsdirhash_move(struct inode *ip, struct
Line 724  ufsdirhash_move(struct inode *ip, struct
   
 /*  /*
  * Inform dirhash that the directory has grown by one block that   * Inform dirhash that the directory has grown by one block that
  * begins at offset (i.e. the new length is offset + DIRBLKSIZ).   * begins at offset (i.e. the new length is offset + UFS_DIRBLKSIZ).
  */   */
 void  void
 ufsdirhash_newblk(struct inode *ip, doff_t offset)  ufsdirhash_newblk(struct inode *ip, doff_t offset)
Line 739  ufsdirhash_dirtrunc(struct inode *ip, do
Line 771  ufsdirhash_dirtrunc(struct inode *ip, do
   
         if ((dh = ip->i_dirhash) == NULL)          if ((dh = ip->i_dirhash) == NULL)
                 return;                  return;
   
         DIRHASH_LOCK(dh);          DIRHASH_LOCK(dh);
         if (dh->dh_hash == NULL) {          if (dh->dh_hash == NULL) {
                 DIRHASH_UNLOCK(dh);                  DIRHASH_UNLOCK(dh);
Line 799  ufsdirhash_checkblock(struct inode *ip, 
Line 832  ufsdirhash_checkblock(struct inode *ip, 
                 return;                  return;
         if ((dh = ip->i_dirhash) == NULL)          if ((dh = ip->i_dirhash) == NULL)
                 return;                  return;
   
         DIRHASH_LOCK(dh);          DIRHASH_LOCK(dh);
         if (dh->dh_hash == NULL) {          if (dh->dh_hash == NULL) {
                 DIRHASH_UNLOCK(dh);                  DIRHASH_UNLOCK(dh);
Line 834  ufsdirhash_checkblock(struct inode *ip, 
Line 868  ufsdirhash_checkblock(struct inode *ip, 
                 /* Check that the entry exists (will panic if it doesn't). */                  /* Check that the entry exists (will panic if it doesn't). */
                 ufsdirhash_findslot(dh, dp->d_name, dp->d_namlen, offset + i);                  ufsdirhash_findslot(dh, dp->d_name, dp->d_namlen, offset + i);
   
                 nfree += dp->d_reclen - DIRSIZ(0, dp, needswap);                  nfree += dp->d_reclen - UFS_DIRSIZ(0, dp, needswap);
         }          }
         if (i != dirblksiz)          if (i != dirblksiz)
                 panic("ufsdirhash_checkblock: bad dir end");                  panic("ufsdirhash_checkblock: bad dir end");
Line 875  ufsdirhash_hash(struct dirhash *dh, cons
Line 909  ufsdirhash_hash(struct dirhash *dh, cons
  * by the value specified by `diff'.   * by the value specified by `diff'.
  *   *
  * The caller must ensure we have exclusive access to `dh'; normally   * The caller must ensure we have exclusive access to `dh'; normally
  * that means that dh_mtx should be held, but this is also called   * that means that dh_lock should be held, but this is also called
  * from ufsdirhash_build() where exclusive access can be assumed.   * from ufsdirhash_build() where exclusive access can be assumed.
  */   */
 static void  static void
Line 883  ufsdirhash_adjfree(struct dirhash *dh, d
Line 917  ufsdirhash_adjfree(struct dirhash *dh, d
 {  {
         int block, i, nfidx, ofidx;          int block, i, nfidx, ofidx;
   
           KASSERT(mutex_owned(&dh->dh_lock));
   
         /* Update the per-block summary info. */          /* Update the per-block summary info. */
         block = offset / dirblksiz;          block = offset / dirblksiz;
         KASSERT(block < dh->dh_nblk && block < dh->dh_dirblks);          KASSERT(block < dh->dh_nblk && block < dh->dh_dirblks);
Line 919  ufsdirhash_findslot(struct dirhash *dh, 
Line 955  ufsdirhash_findslot(struct dirhash *dh, 
 {  {
         int slot;          int slot;
   
           KASSERT(mutex_owned(&dh->dh_lock));
   
         /* Find the entry. */          /* Find the entry. */
         KASSERT(dh->dh_hused < dh->dh_hlen);          KASSERT(dh->dh_hused < dh->dh_hlen);
         slot = ufsdirhash_hash(dh, name, namelen);          slot = ufsdirhash_hash(dh, name, namelen);
Line 941  ufsdirhash_delslot(struct dirhash *dh, i
Line 979  ufsdirhash_delslot(struct dirhash *dh, i
 {  {
         int i;          int i;
   
           KASSERT(mutex_owned(&dh->dh_lock));
   
         /* Mark the entry as deleted. */          /* Mark the entry as deleted. */
         DH_ENTRY(dh, slot) = DIRHASH_DEL;          DH_ENTRY(dh, slot) = DIRHASH_DEL;
   
Line 960  ufsdirhash_delslot(struct dirhash *dh, i
Line 1000  ufsdirhash_delslot(struct dirhash *dh, i
   
 /*  /*
  * Given a directory entry and its offset, find the offset of the   * Given a directory entry and its offset, find the offset of the
  * previous entry in the same DIRBLKSIZ-sized block. Returns an   * previous entry in the same UFS_DIRBLKSIZ-sized block. Returns an
  * offset, or -1 if there is no previous entry in the block or some   * offset, or -1 if there is no previous entry in the block or some
  * other problem occurred.   * other problem occurred.
  */   */
Line 1002  ufsdirhash_recycle(int wanted)
Line 1042  ufsdirhash_recycle(int wanted)
         doff_t **hash;          doff_t **hash;
         u_int8_t *blkfree;          u_int8_t *blkfree;
         int i, mem, narrays;          int i, mem, narrays;
           size_t hashsz, blkfreesz;
   
         DIRHASHLIST_LOCK();          DIRHASHLIST_LOCK();
         while (wanted + ufs_dirhashmem > ufs_dirhashmaxmem) {          while (wanted + ufs_dirhashmem > ufs_dirhashmaxmem) {
Line 1024  ufsdirhash_recycle(int wanted)
Line 1065  ufsdirhash_recycle(int wanted)
                 TAILQ_REMOVE(&ufsdirhash_list, dh, dh_list);                  TAILQ_REMOVE(&ufsdirhash_list, dh, dh_list);
                 dh->dh_onlist = 0;                  dh->dh_onlist = 0;
                 hash = dh->dh_hash;                  hash = dh->dh_hash;
                   hashsz = dh->dh_hashsz;
                 dh->dh_hash = NULL;                  dh->dh_hash = NULL;
                 blkfree = dh->dh_blkfree;                  blkfree = dh->dh_blkfree;
                   blkfreesz = dh->dh_blkfreesz;
                 dh->dh_blkfree = NULL;                  dh->dh_blkfree = NULL;
                 narrays = dh->dh_narrays;                  narrays = dh->dh_narrays;
                 mem = narrays * sizeof(*dh->dh_hash) +                  mem = narrays * sizeof(*dh->dh_hash) +
Line 1035  ufsdirhash_recycle(int wanted)
Line 1078  ufsdirhash_recycle(int wanted)
                 /* Unlock everything, free the detached memory. */                  /* Unlock everything, free the detached memory. */
                 DIRHASH_UNLOCK(dh);                  DIRHASH_UNLOCK(dh);
                 DIRHASHLIST_UNLOCK();                  DIRHASHLIST_UNLOCK();
   
                 for (i = 0; i < narrays; i++)                  for (i = 0; i < narrays; i++)
                         DIRHASH_BLKFREE(hash[i]);                          DIRHASH_BLKFREE(hash[i]);
                 FREE(hash, M_DIRHASH);                  kmem_free(hash, hashsz);
                 FREE(blkfree, M_DIRHASH);                  kmem_free(blkfree, blkfreesz);
   
                 /* Account for the returned memory, and repeat if necessary. */                  /* Account for the returned memory, and repeat if necessary. */
                 DIRHASHLIST_LOCK();                  DIRHASHLIST_LOCK();
                 ufs_dirhashmem -= mem;                  atomic_add_int(&ufs_dirhashmem, -mem);
         }          }
         /* Success; return with list locked. */          /* Success. */
         return (0);          return (0);
 }  }
   
 void  static void
 ufsdirhash_init()  ufsdirhash_sysctl_init(void)
 {  
 #ifdef _LKM  
         pool_init(&ufsdirhash_pool, DH_NBLKOFF * sizeof(daddr_t), 0, 0, 0,  
             "ufsdirhash", &pool_allocator_nointr);  
 #endif  
         TAILQ_INIT(&ufsdirhash_list);  
 }  
   
 void  
 ufsdirhash_done(void)  
 {  
         KASSERT(TAILQ_EMPTY(&ufsdirhash_list));  
 #ifdef _LKM  
         pool_destroy(&ufsdirhash_pool);  
 #endif  
 }  
   
 SYSCTL_SETUP(sysctl_vfs_ufs_setup, "sysctl vfs.ufs.dirhash subtree setup")  
 {  {
         const struct sysctlnode *rnode, *cnode;          const struct sysctlnode *rnode, *cnode;
   
         sysctl_createv(clog, 0, NULL, &rnode,          sysctl_createv(&ufsdirhash_sysctl_log, 0, NULL, &rnode,
                        CTLFLAG_PERMANENT,  
                        CTLTYPE_NODE, "vfs", NULL,  
                        NULL, 0, NULL, 0,  
                        CTL_VFS, CTL_EOL);  
   
         sysctl_createv(clog, 0, &rnode, &rnode,  
                        CTLFLAG_PERMANENT,                         CTLFLAG_PERMANENT,
                        CTLTYPE_NODE, "ufs",                         CTLTYPE_NODE, "ufs",
                        SYSCTL_DESCR("ufs"),                         SYSCTL_DESCR("ufs"),
                        NULL, 0, NULL, 0,                         NULL, 0, NULL, 0,
                        CTL_CREATE, CTL_EOL);                         CTL_VFS, CTL_CREATE, CTL_EOL);
   
         sysctl_createv(clog, 0, &rnode, &rnode,          sysctl_createv(&ufsdirhash_sysctl_log, 0, &rnode, &rnode,
                        CTLFLAG_PERMANENT,                         CTLFLAG_PERMANENT,
                        CTLTYPE_NODE, "dirhash",                         CTLTYPE_NODE, "dirhash",
                        SYSCTL_DESCR("dirhash"),                         SYSCTL_DESCR("dirhash"),
                        NULL, 0, NULL, 0,                         NULL, 0, NULL, 0,
                        CTL_CREATE, CTL_EOL);                         CTL_CREATE, CTL_EOL);
   
         sysctl_createv(clog, 0, &rnode, &cnode,          sysctl_createv(&ufsdirhash_sysctl_log, 0, &rnode, &cnode,
                        CTLFLAG_PERMANENT|CTLFLAG_READWRITE,                         CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
                        CTLTYPE_INT, "minblocks",                         CTLTYPE_INT, "minblocks",
                        SYSCTL_DESCR("minimum hashed directory size in blocks"),                         SYSCTL_DESCR("minimum hashed directory size in blocks"),
                        NULL, 0, &ufs_dirhashminblks, 0,                         NULL, 0, &ufs_dirhashminblks, 0,
                        CTL_CREATE, CTL_EOL);                         CTL_CREATE, CTL_EOL);
   
         sysctl_createv(clog, 0, &rnode, &cnode,          sysctl_createv(&ufsdirhash_sysctl_log, 0, &rnode, &cnode,
                        CTLFLAG_PERMANENT|CTLFLAG_READWRITE,                         CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
                        CTLTYPE_INT, "maxmem",                         CTLTYPE_INT, "maxmem",
                        SYSCTL_DESCR("maximum dirhash memory usage"),                         SYSCTL_DESCR("maximum dirhash memory usage"),
                        NULL, 0, &ufs_dirhashmaxmem, 0,                         NULL, 0, &ufs_dirhashmaxmem, 0,
                        CTL_CREATE, CTL_EOL);                         CTL_CREATE, CTL_EOL);
   
         sysctl_createv(clog, 0, &rnode, &cnode,          sysctl_createv(&ufsdirhash_sysctl_log, 0, &rnode, &cnode,
                        CTLFLAG_PERMANENT|CTLFLAG_READONLY,                         CTLFLAG_PERMANENT|CTLFLAG_READONLY,
                        CTLTYPE_INT, "memused",                         CTLTYPE_INT, "memused",
                        SYSCTL_DESCR("current dirhash memory usage"),                         SYSCTL_DESCR("current dirhash memory usage"),
                        NULL, 0, &ufs_dirhashmem, 0,                         NULL, 0, &ufs_dirhashmem, 0,
                        CTL_CREATE, CTL_EOL);                         CTL_CREATE, CTL_EOL);
   
         sysctl_createv(clog, 0, &rnode, &cnode,          sysctl_createv(&ufsdirhash_sysctl_log, 0, &rnode, &cnode,
                        CTLFLAG_PERMANENT|CTLFLAG_READWRITE,                         CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
                        CTLTYPE_INT, "docheck",                         CTLTYPE_INT, "docheck",
                        SYSCTL_DESCR("enable extra sanity checks"),                         SYSCTL_DESCR("enable extra sanity checks"),
                        NULL, 0, &ufs_dirhashcheck, 0,                         NULL, 0, &ufs_dirhashcheck, 0,
                        CTL_CREATE, CTL_EOL);                         CTL_CREATE, CTL_EOL);
 }  }
   
   void
   ufsdirhash_init(void)
   {
   
           mutex_init(&ufsdirhash_lock, MUTEX_DEFAULT, IPL_NONE);
           ufsdirhashblk_cache = pool_cache_init(DH_NBLKOFF * sizeof(daddr_t), 0,
               0, 0, "dirhashblk", NULL, IPL_NONE, NULL, NULL, NULL);
           ufsdirhash_cache = pool_cache_init(sizeof(struct dirhash), 0,
               0, 0, "dirhash", NULL, IPL_NONE, NULL, NULL, NULL);
           TAILQ_INIT(&ufsdirhash_list);
           ufsdirhash_sysctl_init();
   }
   
   void
   ufsdirhash_done(void)
   {
   
           KASSERT(TAILQ_EMPTY(&ufsdirhash_list));
           pool_cache_destroy(ufsdirhashblk_cache);
           pool_cache_destroy(ufsdirhash_cache);
           mutex_destroy(&ufsdirhash_lock);
           sysctl_teardown(&ufsdirhash_sysctl_log);
   }

Legend:
Removed from v.1.10.8.1  
changed lines
  Added in v.1.34.22.2

CVSweb <webmaster@jp.NetBSD.org>