Merge branch 'xfs-sparse-inode' into for-next
authorDave Chinner <david@fromorbit.com>
Mon, 1 Jun 2015 00:51:38 +0000 (10:51 +1000)
committerDave Chinner <david@fromorbit.com>
Mon, 1 Jun 2015 00:51:38 +0000 (10:51 +1000)
1  2 
fs/xfs/libxfs/xfs_format.h
fs/xfs/libxfs/xfs_ialloc.c
fs/xfs/xfs_inode.c
fs/xfs/xfs_mount.c

index ff22a4d9ad0c9bca88810b33b165f2bab891eda7,5ccc891cdd1a0a51eec7c1a33b88423c0956b2cc..815f61b02bc189902f783319b6f647cf9dcbda17
@@@ -170,7 -170,7 +170,7 @@@ typedef struct xfs_sb 
        __uint32_t      sb_features_log_incompat;
  
        __uint32_t      sb_crc;         /* superblock crc */
-       __uint32_t      sb_pad;
+       xfs_extlen_t    sb_spino_align; /* sparse inode chunk alignment */
  
        xfs_ino_t       sb_pquotino;    /* project quota inode */
        xfs_lsn_t       sb_lsn;         /* last write sequence */
@@@ -256,7 -256,7 +256,7 @@@ typedef struct xfs_dsb 
        __be32          sb_features_log_incompat;
  
        __le32          sb_crc;         /* superblock crc */
-       __be32          sb_pad;
+       __be32          sb_spino_align; /* sparse inode chunk alignment */
  
        __be64          sb_pquotino;    /* project quota inode */
        __be64          sb_lsn;         /* last write sequence */
@@@ -457,8 -457,10 +457,10 @@@ xfs_sb_has_ro_compat_feature
  }
  
  #define XFS_SB_FEAT_INCOMPAT_FTYPE    (1 << 0)        /* filetype in dirent */
+ #define XFS_SB_FEAT_INCOMPAT_SPINODES (1 << 1)        /* sparse inode chunks */
  #define XFS_SB_FEAT_INCOMPAT_ALL \
-               (XFS_SB_FEAT_INCOMPAT_FTYPE)
+               (XFS_SB_FEAT_INCOMPAT_FTYPE|    \
+                XFS_SB_FEAT_INCOMPAT_SPINODES)
  
  #define XFS_SB_FEAT_INCOMPAT_UNKNOWN  ~XFS_SB_FEAT_INCOMPAT_ALL
  static inline bool
@@@ -506,6 -508,12 +508,12 @@@ static inline int xfs_sb_version_hasfin
                (sbp->sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_FINOBT);
  }
  
+ static inline bool xfs_sb_version_hassparseinodes(struct xfs_sb *sbp)
+ {
+       return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5 &&
+               xfs_sb_has_incompat_feature(sbp, XFS_SB_FEAT_INCOMPAT_SPINODES);
+ }
  /*
   * end of superblock version macros
   */
@@@ -1216,26 -1224,54 +1224,54 @@@ typedef      __uint64_t      xfs_inofree_t
  #define       XFS_INOBT_ALL_FREE              ((xfs_inofree_t)-1)
  #define       XFS_INOBT_MASK(i)               ((xfs_inofree_t)1 << (i))
  
+ #define XFS_INOBT_HOLEMASK_FULL               0       /* holemask for full chunk */
+ #define XFS_INOBT_HOLEMASK_BITS               (NBBY * sizeof(__uint16_t))
+ #define XFS_INODES_PER_HOLEMASK_BIT   \
+       (XFS_INODES_PER_CHUNK / (NBBY * sizeof(__uint16_t)))
  static inline xfs_inofree_t xfs_inobt_maskn(int i, int n)
  {
        return ((n >= XFS_INODES_PER_CHUNK ? 0 : XFS_INOBT_MASK(n)) - 1) << i;
  }
  
  /*
-  * Data record structure
+  * The on-disk inode record structure has two formats. The original "full"
+  * format uses a 4-byte freecount. The "sparse" format uses a 1-byte freecount
+  * and replaces the 3 high-order freecount bytes wth the holemask and inode
+  * count.
+  *
+  * The holemask of the sparse record format allows an inode chunk to have holes
+  * that refer to blocks not owned by the inode record. This facilitates inode
+  * allocation in the event of severe free space fragmentation.
   */
  typedef struct xfs_inobt_rec {
        __be32          ir_startino;    /* starting inode number */
-       __be32          ir_freecount;   /* count of free inodes (set bits) */
+       union {
+               struct {
+                       __be32  ir_freecount;   /* count of free inodes */
+               } f;
+               struct {
+                       __be16  ir_holemask;/* hole mask for sparse chunks */
+                       __u8    ir_count;       /* total inode count */
+                       __u8    ir_freecount;   /* count of free inodes */
+               } sp;
+       } ir_u;
        __be64          ir_free;        /* free inode mask */
  } xfs_inobt_rec_t;
  
  typedef struct xfs_inobt_rec_incore {
        xfs_agino_t     ir_startino;    /* starting inode number */
-       __int32_t       ir_freecount;   /* count of free inodes (set bits) */
+       __uint16_t      ir_holemask;    /* hole mask for sparse chunks */
+       __uint8_t       ir_count;       /* total inode count */
+       __uint8_t       ir_freecount;   /* count of free inodes (set bits) */
        xfs_inofree_t   ir_free;        /* free inode mask */
  } xfs_inobt_rec_incore_t;
  
+ static inline bool xfs_inobt_issparse(uint16_t holemask)
+ {
+       /* non-zero holemask represents a sparse rec. */
+       return holemask;
+ }
  
  /*
   * Key structure
@@@ -1453,8 -1489,8 +1489,8 @@@ struct xfs_acl 
                sizeof(struct xfs_acl_entry) * XFS_ACL_MAX_ENTRIES((mp)))
  
  /* On-disk XFS extended attribute names */
 -#define SGI_ACL_FILE          (unsigned char *)"SGI_ACL_FILE"
 -#define SGI_ACL_DEFAULT               (unsigned char *)"SGI_ACL_DEFAULT"
 +#define SGI_ACL_FILE          "SGI_ACL_FILE"
 +#define SGI_ACL_DEFAULT               "SGI_ACL_DEFAULT"
  #define SGI_ACL_FILE_SIZE     (sizeof(SGI_ACL_FILE)-1)
  #define SGI_ACL_DEFAULT_SIZE  (sizeof(SGI_ACL_DEFAULT)-1)
  
index 1c9e75521250ecf606639578ce79696b6ff4a682,c6d684ed84d0d1685c02f8209c42b3bfcf0626b9..a18bc75cc2160af82945523b1112eb9f68625052
@@@ -65,6 -65,8 +65,8 @@@ xfs_inobt_lookup
        int                     *stat)  /* success/failure */
  {
        cur->bc_rec.i.ir_startino = ino;
+       cur->bc_rec.i.ir_holemask = 0;
+       cur->bc_rec.i.ir_count = 0;
        cur->bc_rec.i.ir_freecount = 0;
        cur->bc_rec.i.ir_free = 0;
        return xfs_btree_lookup(cur, dir, stat);
@@@ -82,7 -84,14 +84,14 @@@ xfs_inobt_update
        union xfs_btree_rec     rec;
  
        rec.inobt.ir_startino = cpu_to_be32(irec->ir_startino);
-       rec.inobt.ir_freecount = cpu_to_be32(irec->ir_freecount);
+       if (xfs_sb_version_hassparseinodes(&cur->bc_mp->m_sb)) {
+               rec.inobt.ir_u.sp.ir_holemask = cpu_to_be16(irec->ir_holemask);
+               rec.inobt.ir_u.sp.ir_count = irec->ir_count;
+               rec.inobt.ir_u.sp.ir_freecount = irec->ir_freecount;
+       } else {
+               /* ir_holemask/ir_count not supported on-disk */
+               rec.inobt.ir_u.f.ir_freecount = cpu_to_be32(irec->ir_freecount);
+       }
        rec.inobt.ir_free = cpu_to_be64(irec->ir_free);
        return xfs_btree_update(cur, &rec);
  }
@@@ -100,12 -109,27 +109,27 @@@ xfs_inobt_get_rec
        int                     error;
  
        error = xfs_btree_get_rec(cur, &rec, stat);
-       if (!error && *stat == 1) {
-               irec->ir_startino = be32_to_cpu(rec->inobt.ir_startino);
-               irec->ir_freecount = be32_to_cpu(rec->inobt.ir_freecount);
-               irec->ir_free = be64_to_cpu(rec->inobt.ir_free);
+       if (error || *stat == 0)
+               return error;
+       irec->ir_startino = be32_to_cpu(rec->inobt.ir_startino);
+       if (xfs_sb_version_hassparseinodes(&cur->bc_mp->m_sb)) {
+               irec->ir_holemask = be16_to_cpu(rec->inobt.ir_u.sp.ir_holemask);
+               irec->ir_count = rec->inobt.ir_u.sp.ir_count;
+               irec->ir_freecount = rec->inobt.ir_u.sp.ir_freecount;
+       } else {
+               /*
+                * ir_holemask/ir_count not supported on-disk. Fill in hardcoded
+                * values for full inode chunks.
+                */
+               irec->ir_holemask = XFS_INOBT_HOLEMASK_FULL;
+               irec->ir_count = XFS_INODES_PER_CHUNK;
+               irec->ir_freecount =
+                               be32_to_cpu(rec->inobt.ir_u.f.ir_freecount);
        }
-       return error;
+       irec->ir_free = be64_to_cpu(rec->inobt.ir_free);
+       return 0;
  }
  
  /*
  STATIC int
  xfs_inobt_insert_rec(
        struct xfs_btree_cur    *cur,
+       __uint16_t              holemask,
+       __uint8_t               count,
        __int32_t               freecount,
        xfs_inofree_t           free,
        int                     *stat)
  {
+       cur->bc_rec.i.ir_holemask = holemask;
+       cur->bc_rec.i.ir_count = count;
        cur->bc_rec.i.ir_freecount = freecount;
        cur->bc_rec.i.ir_free = free;
        return xfs_btree_insert(cur, stat);
@@@ -154,7 -182,9 +182,9 @@@ xfs_inobt_insert
                }
                ASSERT(i == 0);
  
-               error = xfs_inobt_insert_rec(cur, XFS_INODES_PER_CHUNK,
+               error = xfs_inobt_insert_rec(cur, XFS_INOBT_HOLEMASK_FULL,
+                                            XFS_INODES_PER_CHUNK,
+                                            XFS_INODES_PER_CHUNK,
                                             XFS_INOBT_ALL_FREE, &i);
                if (error) {
                        xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
@@@ -220,6 -250,7 +250,7 @@@ xfs_ialloc_inode_init
        struct xfs_mount        *mp,
        struct xfs_trans        *tp,
        struct list_head        *buffer_list,
+       int                     icount,
        xfs_agnumber_t          agno,
        xfs_agblock_t           agbno,
        xfs_agblock_t           length,
                 * they track in the AIL as if they were physically logged.
                 */
                if (tp)
-                       xfs_icreate_log(tp, agno, agbno, mp->m_ialloc_inos,
+                       xfs_icreate_log(tp, agno, agbno, icount,
                                        mp->m_sb.sb_inodesize, length, gen);
        } else
                version = 2;
        return 0;
  }
  
+ /*
+  * Align startino and allocmask for a recently allocated sparse chunk such that
+  * they are fit for insertion (or merge) into the on-disk inode btrees.
+  *
+  * Background:
+  *
+  * When enabled, sparse inode support increases the inode alignment from cluster
+  * size to inode chunk size. This means that the minimum range between two
+  * non-adjacent inode records in the inobt is large enough for a full inode
+  * record. This allows for cluster sized, cluster aligned block allocation
+  * without need to worry about whether the resulting inode record overlaps with
+  * another record in the tree. Without this basic rule, we would have to deal
+  * with the consequences of overlap by potentially undoing recent allocations in
+  * the inode allocation codepath.
+  *
+  * Because of this alignment rule (which is enforced on mount), there are two
+  * inobt possibilities for newly allocated sparse chunks. One is that the
+  * aligned inode record for the chunk covers a range of inodes not already
+  * covered in the inobt (i.e., it is safe to insert a new sparse record). The
+  * other is that a record already exists at the aligned startino that considers
+  * the newly allocated range as sparse. In the latter case, record content is
+  * merged in hope that sparse inode chunks fill to full chunks over time.
+  */
+ STATIC void
+ xfs_align_sparse_ino(
+       struct xfs_mount                *mp,
+       xfs_agino_t                     *startino,
+       uint16_t                        *allocmask)
+ {
+       xfs_agblock_t                   agbno;
+       xfs_agblock_t                   mod;
+       int                             offset;
+       agbno = XFS_AGINO_TO_AGBNO(mp, *startino);
+       mod = agbno % mp->m_sb.sb_inoalignmt;
+       if (!mod)
+               return;
+       /* calculate the inode offset and align startino */
+       offset = mod << mp->m_sb.sb_inopblog;
+       *startino -= offset;
+       /*
+        * Since startino has been aligned down, left shift allocmask such that
+        * it continues to represent the same physical inodes relative to the
+        * new startino.
+        */
+       *allocmask <<= offset / XFS_INODES_PER_HOLEMASK_BIT;
+ }
+ /*
+  * Determine whether the source inode record can merge into the target. Both
+  * records must be sparse, the inode ranges must match and there must be no
+  * allocation overlap between the records.
+  */
+ STATIC bool
+ __xfs_inobt_can_merge(
+       struct xfs_inobt_rec_incore     *trec,  /* tgt record */
+       struct xfs_inobt_rec_incore     *srec)  /* src record */
+ {
+       uint64_t                        talloc;
+       uint64_t                        salloc;
+       /* records must cover the same inode range */
+       if (trec->ir_startino != srec->ir_startino)
+               return false;
+       /* both records must be sparse */
+       if (!xfs_inobt_issparse(trec->ir_holemask) ||
+           !xfs_inobt_issparse(srec->ir_holemask))
+               return false;
+       /* both records must track some inodes */
+       if (!trec->ir_count || !srec->ir_count)
+               return false;
+       /* can't exceed capacity of a full record */
+       if (trec->ir_count + srec->ir_count > XFS_INODES_PER_CHUNK)
+               return false;
+       /* verify there is no allocation overlap */
+       talloc = xfs_inobt_irec_to_allocmask(trec);
+       salloc = xfs_inobt_irec_to_allocmask(srec);
+       if (talloc & salloc)
+               return false;
+       return true;
+ }
+ /*
+  * Merge the source inode record into the target. The caller must call
+  * __xfs_inobt_can_merge() to ensure the merge is valid.
+  */
+ STATIC void
+ __xfs_inobt_rec_merge(
+       struct xfs_inobt_rec_incore     *trec,  /* target */
+       struct xfs_inobt_rec_incore     *srec)  /* src */
+ {
+       ASSERT(trec->ir_startino == srec->ir_startino);
+       /* combine the counts */
+       trec->ir_count += srec->ir_count;
+       trec->ir_freecount += srec->ir_freecount;
+       /*
+        * Merge the holemask and free mask. For both fields, 0 bits refer to
+        * allocated inodes. We combine the allocated ranges with bitwise AND.
+        */
+       trec->ir_holemask &= srec->ir_holemask;
+       trec->ir_free &= srec->ir_free;
+ }
+ /*
+  * Insert a new sparse inode chunk into the associated inode btree. The inode
+  * record for the sparse chunk is pre-aligned to a startino that should match
+  * any pre-existing sparse inode record in the tree. This allows sparse chunks
+  * to fill over time.
+  *
+  * This function supports two modes of handling preexisting records depending on
+  * the merge flag. If merge is true, the provided record is merged with the
+  * existing record and updated in place. The merged record is returned in nrec.
+  * If merge is false, an existing record is replaced with the provided record.
+  * If no preexisting record exists, the provided record is always inserted.
+  *
+  * It is considered corruption if a merge is requested and not possible. Given
+  * the sparse inode alignment constraints, this should never happen.
+  */
+ STATIC int
+ xfs_inobt_insert_sprec(
+       struct xfs_mount                *mp,
+       struct xfs_trans                *tp,
+       struct xfs_buf                  *agbp,
+       int                             btnum,
+       struct xfs_inobt_rec_incore     *nrec,  /* in/out: new/merged rec. */
+       bool                            merge)  /* merge or replace */
+ {
+       struct xfs_btree_cur            *cur;
+       struct xfs_agi                  *agi = XFS_BUF_TO_AGI(agbp);
+       xfs_agnumber_t                  agno = be32_to_cpu(agi->agi_seqno);
+       int                             error;
+       int                             i;
+       struct xfs_inobt_rec_incore     rec;
+       cur = xfs_inobt_init_cursor(mp, tp, agbp, agno, btnum);
+       /* the new record is pre-aligned so we know where to look */
+       error = xfs_inobt_lookup(cur, nrec->ir_startino, XFS_LOOKUP_EQ, &i);
+       if (error)
+               goto error;
+       /* if nothing there, insert a new record and return */
+       if (i == 0) {
+               error = xfs_inobt_insert_rec(cur, nrec->ir_holemask,
+                                            nrec->ir_count, nrec->ir_freecount,
+                                            nrec->ir_free, &i);
+               if (error)
+                       goto error;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, error);
+               goto out;
+       }
+       /*
+        * A record exists at this startino. Merge or replace the record
+        * depending on what we've been asked to do.
+        */
+       if (merge) {
+               error = xfs_inobt_get_rec(cur, &rec, &i);
+               if (error)
+                       goto error;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, error);
+               XFS_WANT_CORRUPTED_GOTO(mp,
+                                       rec.ir_startino == nrec->ir_startino,
+                                       error);
+               /*
+                * This should never fail. If we have coexisting records that
+                * cannot merge, something is seriously wrong.
+                */
+               XFS_WANT_CORRUPTED_GOTO(mp, __xfs_inobt_can_merge(nrec, &rec),
+                                       error);
+               trace_xfs_irec_merge_pre(mp, agno, rec.ir_startino,
+                                        rec.ir_holemask, nrec->ir_startino,
+                                        nrec->ir_holemask);
+               /* merge to nrec to output the updated record */
+               __xfs_inobt_rec_merge(nrec, &rec);
+               trace_xfs_irec_merge_post(mp, agno, nrec->ir_startino,
+                                         nrec->ir_holemask);
+               error = xfs_inobt_rec_check_count(mp, nrec);
+               if (error)
+                       goto error;
+       }
+       error = xfs_inobt_update(cur, nrec);
+       if (error)
+               goto error;
+ out:
+       xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+       return 0;
+ error:
+       xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+       return error;
+ }
  /*
   * Allocate new inodes in the allocation group specified by agbp.
   * Return 0 for success, else error code.
@@@ -364,11 -603,22 +603,22 @@@ xfs_ialloc_ag_alloc
        xfs_agino_t     newlen;         /* new number of inodes */
        int             isaligned = 0;  /* inode allocation at stripe unit */
                                        /* boundary */
+       uint16_t        allocmask = (uint16_t) -1; /* init. to full chunk */
+       struct xfs_inobt_rec_incore rec;
        struct xfs_perag *pag;
  
+       int             do_sparse = 0;
+ #ifdef DEBUG
+       /* randomly do sparse inode allocations */
+       if (xfs_sb_version_hassparseinodes(&tp->t_mountp->m_sb))
+               do_sparse = prandom_u32() & 1;
+ #endif
        memset(&args, 0, sizeof(args));
        args.tp = tp;
        args.mp = tp->t_mountp;
+       args.fsbno = NULLFSBLOCK;
  
        /*
         * Locking will ensure that we don't have two callers in here
         */
        newlen = args.mp->m_ialloc_inos;
        if (args.mp->m_maxicount &&
 -          percpu_counter_read(&args.mp->m_icount) + newlen >
 +          percpu_counter_read_positive(&args.mp->m_icount) + newlen >
                                                        args.mp->m_maxicount)
                return -ENOSPC;
        args.minlen = args.maxlen = args.mp->m_ialloc_blks;
        agno = be32_to_cpu(agi->agi_seqno);
        args.agbno = XFS_AGINO_TO_AGBNO(args.mp, newino) +
                     args.mp->m_ialloc_blks;
+       if (do_sparse)
+               goto sparse_alloc;
        if (likely(newino != NULLAGINO &&
                  (args.agbno < be32_to_cpu(agi->agi_length)))) {
                args.fsbno = XFS_AGB_TO_FSB(args.mp, agno, args.agbno);
                 * subsequent requests.
                 */
                args.minalignslop = 0;
-       } else
-               args.fsbno = NULLFSBLOCK;
+       }
  
        if (unlikely(args.fsbno == NULLFSBLOCK)) {
                /*
                        return error;
        }
  
+       /*
+        * Finally, try a sparse allocation if the filesystem supports it and
+        * the sparse allocation length is smaller than a full chunk.
+        */
+       if (xfs_sb_version_hassparseinodes(&args.mp->m_sb) &&
+           args.mp->m_ialloc_min_blks < args.mp->m_ialloc_blks &&
+           args.fsbno == NULLFSBLOCK) {
+ sparse_alloc:
+               args.type = XFS_ALLOCTYPE_NEAR_BNO;
+               args.agbno = be32_to_cpu(agi->agi_root);
+               args.fsbno = XFS_AGB_TO_FSB(args.mp, agno, args.agbno);
+               args.alignment = args.mp->m_sb.sb_spino_align;
+               args.prod = 1;
+               args.minlen = args.mp->m_ialloc_min_blks;
+               args.maxlen = args.minlen;
+               /*
+                * The inode record will be aligned to full chunk size. We must
+                * prevent sparse allocation from AG boundaries that result in
+                * invalid inode records, such as records that start at agbno 0
+                * or extend beyond the AG.
+                *
+                * Set min agbno to the first aligned, non-zero agbno and max to
+                * the last aligned agbno that is at least one full chunk from
+                * the end of the AG.
+                */
+               args.min_agbno = args.mp->m_sb.sb_inoalignmt;
+               args.max_agbno = round_down(args.mp->m_sb.sb_agblocks,
+                                           args.mp->m_sb.sb_inoalignmt) -
+                                args.mp->m_ialloc_blks;
+               error = xfs_alloc_vextent(&args);
+               if (error)
+                       return error;
+               newlen = args.len << args.mp->m_sb.sb_inopblog;
+               allocmask = (1 << (newlen / XFS_INODES_PER_HOLEMASK_BIT)) - 1;
+       }
        if (args.fsbno == NULLFSBLOCK) {
                *alloc = 0;
                return 0;
         * rather than a linear progression to prevent the next generation
         * number from being easily guessable.
         */
-       error = xfs_ialloc_inode_init(args.mp, tp, NULL, agno, args.agbno,
-                       args.len, prandom_u32());
+       error = xfs_ialloc_inode_init(args.mp, tp, NULL, newlen, agno,
+                       args.agbno, args.len, prandom_u32());
  
        if (error)
                return error;
         * Convert the results.
         */
        newino = XFS_OFFBNO_TO_AGINO(args.mp, args.agbno, 0);
+       if (xfs_inobt_issparse(~allocmask)) {
+               /*
+                * We've allocated a sparse chunk. Align the startino and mask.
+                */
+               xfs_align_sparse_ino(args.mp, &newino, &allocmask);
+               rec.ir_startino = newino;
+               rec.ir_holemask = ~allocmask;
+               rec.ir_count = newlen;
+               rec.ir_freecount = newlen;
+               rec.ir_free = XFS_INOBT_ALL_FREE;
+               /*
+                * Insert the sparse record into the inobt and allow for a merge
+                * if necessary. If a merge does occur, rec is updated to the
+                * merged record.
+                */
+               error = xfs_inobt_insert_sprec(args.mp, tp, agbp, XFS_BTNUM_INO,
+                                              &rec, true);
+               if (error == -EFSCORRUPTED) {
+                       xfs_alert(args.mp,
+       "invalid sparse inode record: ino 0x%llx holemask 0x%x count %u",
+                                 XFS_AGINO_TO_INO(args.mp, agno,
+                                                  rec.ir_startino),
+                                 rec.ir_holemask, rec.ir_count);
+                       xfs_force_shutdown(args.mp, SHUTDOWN_CORRUPT_INCORE);
+               }
+               if (error)
+                       return error;
+               /*
+                * We can't merge the part we've just allocated as for the inobt
+                * due to finobt semantics. The original record may or may not
+                * exist independent of whether physical inodes exist in this
+                * sparse chunk.
+                *
+                * We must update the finobt record based on the inobt record.
+                * rec contains the fully merged and up to date inobt record
+                * from the previous call. Set merge false to replace any
+                * existing record with this one.
+                */
+               if (xfs_sb_version_hasfinobt(&args.mp->m_sb)) {
+                       error = xfs_inobt_insert_sprec(args.mp, tp, agbp,
+                                                      XFS_BTNUM_FINO, &rec,
+                                                      false);
+                       if (error)
+                               return error;
+               }
+       } else {
+               /* full chunk - insert new records to both btrees */
+               error = xfs_inobt_insert(args.mp, tp, agbp, newino, newlen,
+                                        XFS_BTNUM_INO);
+               if (error)
+                       return error;
+               if (xfs_sb_version_hasfinobt(&args.mp->m_sb)) {
+                       error = xfs_inobt_insert(args.mp, tp, agbp, newino,
+                                                newlen, XFS_BTNUM_FINO);
+                       if (error)
+                               return error;
+               }
+       }
+       /*
+        * Update AGI counts and newino.
+        */
        be32_add_cpu(&agi->agi_count, newlen);
        be32_add_cpu(&agi->agi_freecount, newlen);
        pag = xfs_perag_get(args.mp, agno);
        xfs_perag_put(pag);
        agi->agi_newino = cpu_to_be32(newino);
  
-       /*
-        * Insert records describing the new inode chunk into the btrees.
-        */
-       error = xfs_inobt_insert(args.mp, tp, agbp, newino, newlen,
-                                XFS_BTNUM_INO);
-       if (error)
-               return error;
-       if (xfs_sb_version_hasfinobt(&args.mp->m_sb)) {
-               error = xfs_inobt_insert(args.mp, tp, agbp, newino, newlen,
-                                        XFS_BTNUM_FINO);
-               if (error)
-                       return error;
-       }
        /*
         * Log allocation group header fields
         */
@@@ -645,7 -989,7 +989,7 @@@ xfs_ialloc_ag_select
                 * if we fail allocation due to alignment issues then it is most
                 * likely a real ENOSPC condition.
                 */
-               ineed = mp->m_ialloc_blks;
+               ineed = mp->m_ialloc_min_blks;
                if (flags && ineed > 1)
                        ineed += xfs_ialloc_cluster_alignment(mp);
                longest = pag->pagf_longest;
@@@ -731,6 -1075,27 +1075,27 @@@ xfs_ialloc_get_rec
        return 0;
  }
  
+ /*
+  * Return the offset of the first free inode in the record. If the inode chunk
+  * is sparsely allocated, we convert the record holemask to inode granularity
+  * and mask off the unallocated regions from the inode free mask.
+  */
+ STATIC int
+ xfs_inobt_first_free_inode(
+       struct xfs_inobt_rec_incore     *rec)
+ {
+       xfs_inofree_t                   realfree;
+       /* if there are no holes, return the first available offset */
+       if (!xfs_inobt_issparse(rec->ir_holemask))
+               return xfs_lowbit64(rec->ir_free);
+       realfree = xfs_inobt_irec_to_allocmask(rec);
+       realfree &= rec->ir_free;
+       return xfs_lowbit64(realfree);
+ }
  /*
   * Allocate an inode using the inobt-only algorithm.
   */
@@@ -961,7 -1326,7 +1326,7 @@@ newino
        }
  
  alloc_inode:
-       offset = xfs_lowbit64(rec.ir_free);
+       offset = xfs_inobt_first_free_inode(&rec);
        ASSERT(offset >= 0);
        ASSERT(offset < XFS_INODES_PER_CHUNK);
        ASSERT((XFS_AGINO_TO_OFFSET(mp, rec.ir_startino) %
@@@ -1210,7 -1575,7 +1575,7 @@@ xfs_dialloc_ag
        if (error)
                goto error_cur;
  
-       offset = xfs_lowbit64(rec.ir_free);
+       offset = xfs_inobt_first_free_inode(&rec);
        ASSERT(offset >= 0);
        ASSERT(offset < XFS_INODES_PER_CHUNK);
        ASSERT((XFS_AGINO_TO_OFFSET(mp, rec.ir_startino) %
@@@ -1339,13 -1704,10 +1704,13 @@@ xfs_dialloc
         * If we have already hit the ceiling of inode blocks then clear
         * okalloc so we scan all available agi structures for a free
         * inode.
 +       *
 +       * Read rough value of mp->m_icount by percpu_counter_read_positive,
 +       * which will sacrifice the preciseness but improve the performance.
         */
        if (mp->m_maxicount &&
 -          percpu_counter_read(&mp->m_icount) + mp->m_ialloc_inos >
 -                                                      mp->m_maxicount) {
 +          percpu_counter_read_positive(&mp->m_icount) + mp->m_ialloc_inos
 +                                                      mp->m_maxicount) {
                noroom = 1;
                okalloc = 0;
        }
@@@ -1439,6 -1801,83 +1804,83 @@@ out_error
        return error;
  }
  
+ /*
+  * Free the blocks of an inode chunk. We must consider that the inode chunk
+  * might be sparse and only free the regions that are allocated as part of the
+  * chunk.
+  */
+ STATIC void
+ xfs_difree_inode_chunk(
+       struct xfs_mount                *mp,
+       xfs_agnumber_t                  agno,
+       struct xfs_inobt_rec_incore     *rec,
+       struct xfs_bmap_free            *flist)
+ {
+       xfs_agblock_t   sagbno = XFS_AGINO_TO_AGBNO(mp, rec->ir_startino);
+       int             startidx, endidx;
+       int             nextbit;
+       xfs_agblock_t   agbno;
+       int             contigblk;
+       DECLARE_BITMAP(holemask, XFS_INOBT_HOLEMASK_BITS);
+       if (!xfs_inobt_issparse(rec->ir_holemask)) {
+               /* not sparse, calculate extent info directly */
+               xfs_bmap_add_free(XFS_AGB_TO_FSB(mp, agno,
+                                 XFS_AGINO_TO_AGBNO(mp, rec->ir_startino)),
+                                 mp->m_ialloc_blks, flist, mp);
+               return;
+       }
+       /* holemask is only 16-bits (fits in an unsigned long) */
+       ASSERT(sizeof(rec->ir_holemask) <= sizeof(holemask[0]));
+       holemask[0] = rec->ir_holemask;
+       /*
+        * Find contiguous ranges of zeroes (i.e., allocated regions) in the
+        * holemask and convert the start/end index of each range to an extent.
+        * We start with the start and end index both pointing at the first 0 in
+        * the mask.
+        */
+       startidx = endidx = find_first_zero_bit(holemask,
+                                               XFS_INOBT_HOLEMASK_BITS);
+       nextbit = startidx + 1;
+       while (startidx < XFS_INOBT_HOLEMASK_BITS) {
+               nextbit = find_next_zero_bit(holemask, XFS_INOBT_HOLEMASK_BITS,
+                                            nextbit);
+               /*
+                * If the next zero bit is contiguous, update the end index of
+                * the current range and continue.
+                */
+               if (nextbit != XFS_INOBT_HOLEMASK_BITS &&
+                   nextbit == endidx + 1) {
+                       endidx = nextbit;
+                       goto next;
+               }
+               /*
+                * nextbit is not contiguous with the current end index. Convert
+                * the current start/end to an extent and add it to the free
+                * list.
+                */
+               agbno = sagbno + (startidx * XFS_INODES_PER_HOLEMASK_BIT) /
+                                 mp->m_sb.sb_inopblock;
+               contigblk = ((endidx - startidx + 1) *
+                            XFS_INODES_PER_HOLEMASK_BIT) /
+                           mp->m_sb.sb_inopblock;
+               ASSERT(agbno % mp->m_sb.sb_spino_align == 0);
+               ASSERT(contigblk % mp->m_sb.sb_spino_align == 0);
+               xfs_bmap_add_free(XFS_AGB_TO_FSB(mp, agno, agbno), contigblk,
+                                 flist, mp);
+               /* reset range to current bit and carry on... */
+               startidx = endidx = nextbit;
+ next:
+               nextbit++;
+       }
+ }
  STATIC int
  xfs_difree_inobt(
        struct xfs_mount                *mp,
        struct xfs_buf                  *agbp,
        xfs_agino_t                     agino,
        struct xfs_bmap_free            *flist,
-       int                             *deleted,
-       xfs_ino_t                       *first_ino,
+       struct xfs_icluster             *xic,
        struct xfs_inobt_rec_incore     *orec)
  {
        struct xfs_agi                  *agi = XFS_BUF_TO_AGI(agbp);
        rec.ir_freecount++;
  
        /*
-        * When an inode cluster is free, it becomes eligible for removal
+        * When an inode chunk is free, it becomes eligible for removal. Don't
+        * remove the chunk if the block size is large enough for multiple inode
+        * chunks (that might not be free).
         */
        if (!(mp->m_flags & XFS_MOUNT_IKEEP) &&
-           (rec.ir_freecount == mp->m_ialloc_inos)) {
-               *deleted = 1;
-               *first_ino = XFS_AGINO_TO_INO(mp, agno, rec.ir_startino);
+           rec.ir_free == XFS_INOBT_ALL_FREE &&
+           mp->m_sb.sb_inopblock <= XFS_INODES_PER_CHUNK) {
+               xic->deleted = 1;
+               xic->first_ino = XFS_AGINO_TO_INO(mp, agno, rec.ir_startino);
+               xic->alloc = xfs_inobt_irec_to_allocmask(&rec);
  
                /*
                 * Remove the inode cluster from the AGI B+Tree, adjust the
                 * AGI and Superblock inode counts, and mark the disk space
                 * to be freed when the transaction is committed.
                 */
-               ilen = mp->m_ialloc_inos;
+               ilen = rec.ir_freecount;
                be32_add_cpu(&agi->agi_count, -ilen);
                be32_add_cpu(&agi->agi_freecount, -(ilen - 1));
                xfs_ialloc_log_agi(tp, agbp, XFS_AGI_COUNT | XFS_AGI_FREECOUNT);
                        goto error0;
                }
  
-               xfs_bmap_add_free(XFS_AGB_TO_FSB(mp, agno,
-                                 XFS_AGINO_TO_AGBNO(mp, rec.ir_startino)),
-                                 mp->m_ialloc_blks, flist, mp);
+               xfs_difree_inode_chunk(mp, agno, &rec, flist);
        } else {
-               *deleted = 0;
+               xic->deleted = 0;
  
                error = xfs_inobt_update(cur, &rec);
                if (error) {
@@@ -1599,7 -2038,9 +2041,9 @@@ xfs_difree_finobt
                 */
                XFS_WANT_CORRUPTED_GOTO(mp, ibtrec->ir_freecount == 1, error);
  
-               error = xfs_inobt_insert_rec(cur, ibtrec->ir_freecount,
+               error = xfs_inobt_insert_rec(cur, ibtrec->ir_holemask,
+                                            ibtrec->ir_count,
+                                            ibtrec->ir_freecount,
                                             ibtrec->ir_free, &i);
                if (error)
                        goto error;
         * free inode. Hence, if all of the inodes are free and we aren't
         * keeping inode chunks permanently on disk, remove the record.
         * Otherwise, update the record with the new information.
+        *
+        * Note that we currently can't free chunks when the block size is large
+        * enough for multiple chunks. Leave the finobt record to remain in sync
+        * with the inobt.
         */
-       if (rec.ir_freecount == mp->m_ialloc_inos &&
+       if (rec.ir_free == XFS_INOBT_ALL_FREE &&
+           mp->m_sb.sb_inopblock <= XFS_INODES_PER_CHUNK &&
            !(mp->m_flags & XFS_MOUNT_IKEEP)) {
                error = xfs_btree_delete(cur, &i);
                if (error)
@@@ -1671,8 -2117,7 +2120,7 @@@ xfs_difree
        struct xfs_trans        *tp,            /* transaction pointer */
        xfs_ino_t               inode,          /* inode to be freed */
        struct xfs_bmap_free    *flist,         /* extents to free */
-       int                     *deleted,/* set if inode cluster was deleted */
-       xfs_ino_t               *first_ino)/* first inode in deleted cluster */
+       struct xfs_icluster     *xic)   /* cluster info if deleted */
  {
        /* REFERENCED */
        xfs_agblock_t           agbno;  /* block number containing inode */
        /*
         * Fix up the inode allocation btree.
         */
-       error = xfs_difree_inobt(mp, tp, agbp, agino, flist, deleted, first_ino,
-                                &rec);
+       error = xfs_difree_inobt(mp, tp, agbp, agino, flist, xic, &rec);
        if (error)
                goto error0;
  
diff --combined fs/xfs/xfs_inode.c
index 539a85fddbc26864004e80f5fb229c6c2de565b8,11a8c28c47bd432d6cf28d89d228521cc381cb2c..4c054f6634b92872dad6ae31578e180185ecc036
@@@ -1946,17 -1946,21 +1946,17 @@@ xfs_inactive
        /*
         * If there are attributes associated with the file then blow them away
         * now.  The code calls a routine that recursively deconstructs the
 -       * attribute fork.  We need to just commit the current transaction
 -       * because we can't use it for xfs_attr_inactive().
 +       * attribute fork. If also blows away the in-core attribute fork.
         */
 -      if (ip->i_d.di_anextents > 0) {
 -              ASSERT(ip->i_d.di_forkoff != 0);
 -
 +      if (XFS_IFORK_Q(ip)) {
                error = xfs_attr_inactive(ip);
                if (error)
                        return;
        }
  
 -      if (ip->i_afp)
 -              xfs_idestroy_fork(ip, XFS_ATTR_FORK);
 -
 +      ASSERT(!ip->i_afp);
        ASSERT(ip->i_d.di_anextents == 0);
 +      ASSERT(ip->i_d.di_forkoff == 0);
  
        /*
         * Free the inode.
@@@ -2235,9 -2239,9 +2235,9 @@@ xfs_iunlink_remove
   */
  STATIC int
  xfs_ifree_cluster(
-       xfs_inode_t     *free_ip,
-       xfs_trans_t     *tp,
-       xfs_ino_t       inum)
+       xfs_inode_t             *free_ip,
+       xfs_trans_t             *tp,
+       struct xfs_icluster     *xic)
  {
        xfs_mount_t             *mp = free_ip->i_mount;
        int                     blks_per_cluster;
        xfs_inode_log_item_t    *iip;
        xfs_log_item_t          *lip;
        struct xfs_perag        *pag;
+       xfs_ino_t               inum;
  
+       inum = xic->first_ino;
        pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, inum));
        blks_per_cluster = xfs_icluster_size_fsb(mp);
        inodes_per_cluster = blks_per_cluster << mp->m_sb.sb_inopblog;
        nbufs = mp->m_ialloc_blks / blks_per_cluster;
  
        for (j = 0; j < nbufs; j++, inum += inodes_per_cluster) {
+               /*
+                * The allocation bitmap tells us which inodes of the chunk were
+                * physically allocated. Skip the cluster if an inode falls into
+                * a sparse region.
+                */
+               if ((xic->alloc & XFS_INOBT_MASK(inum - xic->first_ino)) == 0) {
+                       ASSERT(((inum - xic->first_ino) %
+                               inodes_per_cluster) == 0);
+                       continue;
+               }
                blkno = XFS_AGB_TO_DADDR(mp, XFS_INO_TO_AGNO(mp, inum),
                                         XFS_INO_TO_AGBNO(mp, inum));
  
@@@ -2414,8 -2431,7 +2427,7 @@@ xfs_ifree
        xfs_bmap_free_t *flist)
  {
        int                     error;
-       int                     delete;
-       xfs_ino_t               first_ino;
+       struct xfs_icluster     xic = { 0 };
  
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
        ASSERT(ip->i_d.di_nlink == 0);
        if (error)
                return error;
  
-       error = xfs_difree(tp, ip->i_ino, flist, &delete, &first_ino);
+       error = xfs_difree(tp, ip->i_ino, flist, &xic);
        if (error)
                return error;
  
        ip->i_d.di_gen++;
        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
  
-       if (delete)
-               error = xfs_ifree_cluster(ip, tp, first_ino);
+       if (xic.deleted)
+               error = xfs_ifree_cluster(ip, tp, &xic);
  
        return error;
  }
@@@ -2879,13 -2895,7 +2891,13 @@@ xfs_rename_alloc_whiteout
        if (error)
                return error;
  
 -      /* Satisfy xfs_bumplink that this is a real tmpfile */
 +      /*
 +       * Prepare the tmpfile inode as if it were created through the VFS.
 +       * Otherwise, the link increment paths will complain about nlink 0->1.
 +       * Drop the link count as done by d_tmpfile(), complete the inode setup
 +       * and flag it as linkable.
 +       */
 +      drop_nlink(VFS_I(tmpfile));
        xfs_finish_inode_setup(tmpfile);
        VFS_I(tmpfile)->i_state |= I_LINKABLE;
  
@@@ -3153,7 -3163,7 +3165,7 @@@ xfs_rename
         * intermediate state on disk.
         */
        if (wip) {
 -              ASSERT(wip->i_d.di_nlink == 0);
 +              ASSERT(VFS_I(wip)->i_nlink == 0 && wip->i_d.di_nlink == 0);
                error = xfs_bumplink(tp, wip);
                if (error)
                        goto out_trans_abort;
diff --combined fs/xfs/xfs_mount.c
index 6f23fbdfb365adca1571eadece38b77a619c50ad,02f827fab829250da11c89c4f5ebad6409d40975..461e791efad71d66f1ffa50930be9fba6ee9f352
@@@ -724,6 -724,22 +724,22 @@@ xfs_mountfs
                        mp->m_inode_cluster_size = new_size;
        }
  
+       /*
+        * If enabled, sparse inode chunk alignment is expected to match the
+        * cluster size. Full inode chunk alignment must match the chunk size,
+        * but that is checked on sb read verification...
+        */
+       if (xfs_sb_version_hassparseinodes(&mp->m_sb) &&
+           mp->m_sb.sb_spino_align !=
+                       XFS_B_TO_FSBT(mp, mp->m_inode_cluster_size)) {
+               xfs_warn(mp,
+       "Sparse inode block alignment (%u) must match cluster size (%llu).",
+                        mp->m_sb.sb_spino_align,
+                        XFS_B_TO_FSBT(mp, mp->m_inode_cluster_size));
+               error = -EINVAL;
+               goto out_remove_uuid;
+       }
        /*
         * Set inode alignment fields
         */
@@@ -1084,18 -1100,14 +1100,18 @@@ xfs_log_sbcount(xfs_mount_t *mp
        return xfs_sync_sb(mp, true);
  }
  
 +/*
 + * Deltas for the inode count are +/-64, hence we use a large batch size
 + * of 128 so we don't need to take the counter lock on every update.
 + */
 +#define XFS_ICOUNT_BATCH      128
  int
  xfs_mod_icount(
        struct xfs_mount        *mp,
        int64_t                 delta)
  {
 -      /* deltas are +/-64, hence the large batch size of 128. */
 -      __percpu_counter_add(&mp->m_icount, delta, 128);
 -      if (percpu_counter_compare(&mp->m_icount, 0) < 0) {
 +      __percpu_counter_add(&mp->m_icount, delta, XFS_ICOUNT_BATCH);
 +      if (__percpu_counter_compare(&mp->m_icount, 0, XFS_ICOUNT_BATCH) < 0) {
                ASSERT(0);
                percpu_counter_add(&mp->m_icount, -delta);
                return -EINVAL;
@@@ -1117,14 -1129,6 +1133,14 @@@ xfs_mod_ifree
        return 0;
  }
  
 +/*
 + * Deltas for the block count can vary from 1 to very large, but lock contention
 + * only occurs on frequent small block count updates such as in the delayed
 + * allocation path for buffered writes (page a time updates). Hence we set
 + * a large batch count (1024) to minimise global counter updates except when
 + * we get near to ENOSPC and we have to be very accurate with our updates.
 + */
 +#define XFS_FDBLOCKS_BATCH    1024
  int
  xfs_mod_fdblocks(
        struct xfs_mount        *mp,
         * Taking blocks away, need to be more accurate the closer we
         * are to zero.
         *
 -       * batch size is set to a maximum of 1024 blocks - if we are
 -       * allocating of freeing extents larger than this then we aren't
 -       * going to be hammering the counter lock so a lock per update
 -       * is not a problem.
 -       *
         * If the counter has a value of less than 2 * max batch size,
         * then make everything serialise as we are real close to
         * ENOSPC.
         */
 -#define __BATCH       1024
 -      if (percpu_counter_compare(&mp->m_fdblocks, 2 * __BATCH) < 0)
 +      if (__percpu_counter_compare(&mp->m_fdblocks, 2 * XFS_FDBLOCKS_BATCH,
 +                                   XFS_FDBLOCKS_BATCH) < 0)
                batch = 1;
        else
 -              batch = __BATCH;
 -#undef __BATCH
 +              batch = XFS_FDBLOCKS_BATCH;
  
        __percpu_counter_add(&mp->m_fdblocks, delta, batch);
 -      if (percpu_counter_compare(&mp->m_fdblocks,
 -                                 XFS_ALLOC_SET_ASIDE(mp)) >= 0) {
 +      if (__percpu_counter_compare(&mp->m_fdblocks, XFS_ALLOC_SET_ASIDE(mp),
 +                                   XFS_FDBLOCKS_BATCH) >= 0) {
                /* we had space! */
                return 0;
        }