[XFS] implement generic xfs_btree_update
authorChristoph Hellwig <hch@infradead.org>
Thu, 30 Oct 2008 05:56:32 +0000 (16:56 +1100)
committerLachlan McIlroy <lachlan@sgi.com>
Thu, 30 Oct 2008 05:56:32 +0000 (16:56 +1100)
From: Dave Chinner <dgc@sgi.com>

The most complicated part here is the lastrec tracking for the alloc
btree. Most logic is in the update_lastrec method which has to do some
hopefully good enough dirty magic to maintain it.

[hch: split out from bigger patch and a rework of the lastrec

logic]

SGI-PV: 985583

SGI-Modid: xfs-linux-melb:xfs-kern:32194a

Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
Signed-off-by: Bill O'Donnell <billodo@sgi.com>
Signed-off-by: David Chinner <david@fromorbit.com>
fs/xfs/xfs_alloc.c
fs/xfs/xfs_alloc_btree.c
fs/xfs/xfs_alloc_btree.h
fs/xfs/xfs_bmap.c
fs/xfs/xfs_bmap_btree.c
fs/xfs/xfs_bmap_btree.h
fs/xfs/xfs_btree.c
fs/xfs/xfs_btree.h
fs/xfs/xfs_ialloc.c
fs/xfs/xfs_ialloc_btree.c
fs/xfs/xfs_ialloc_btree.h

index 6bda0ae..875e1ba 100644 (file)
@@ -136,6 +136,23 @@ xfs_alloc_lookup_le(
        return xfs_btree_lookup(cur, XFS_LOOKUP_LE, stat);
 }
 
+/*
+ * Update the record referred to by cur to the value given
+ * by [bno, len].
+ * This either works (return 0) or gets an EFSCORRUPTED error.
+ */
+STATIC int                             /* error */
+xfs_alloc_update(
+       struct xfs_btree_cur    *cur,   /* btree cursor */
+       xfs_agblock_t           bno,    /* starting block of extent */
+       xfs_extlen_t            len)    /* length of extent */
+{
+       union xfs_btree_rec     rec;
+
+       rec.alloc.ar_startblock = cpu_to_be32(bno);
+       rec.alloc.ar_blockcount = cpu_to_be32(len);
+       return xfs_btree_update(cur, &rec);
+}
 
 /*
  * Compute aligned version of the found extent.
index 28c6a69..c5c3299 100644 (file)
@@ -1661,83 +1661,50 @@ xfs_alloc_insert(
        return 0;
 }
 
+STATIC struct xfs_btree_cur *
+xfs_allocbt_dup_cursor(
+       struct xfs_btree_cur    *cur)
+{
+       return xfs_allocbt_init_cursor(cur->bc_mp, cur->bc_tp,
+                       cur->bc_private.a.agbp, cur->bc_private.a.agno,
+                       cur->bc_btnum);
+}
+
 /*
- * Update the record referred to by cur, to the value given by [bno, len].
- * This either works (return 0) or gets an EFSCORRUPTED error.
+ * Update the longest extent in the AGF
  */
-int                                    /* error */
-xfs_alloc_update(
-       xfs_btree_cur_t         *cur,   /* btree cursor */
-       xfs_agblock_t           bno,    /* starting block of extent */
-       xfs_extlen_t            len)    /* length of extent */
+STATIC void
+xfs_allocbt_update_lastrec(
+       struct xfs_btree_cur    *cur,
+       struct xfs_btree_block  *block,
+       union xfs_btree_rec     *rec,
+       int                     ptr,
+       int                     reason)
 {
-       xfs_alloc_block_t       *block; /* btree block to update */
-       int                     error;  /* error return value */
-       int                     ptr;    /* current record number (updating) */
+       struct xfs_agf          *agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
+       xfs_agnumber_t          seqno = be32_to_cpu(agf->agf_seqno);
+       __be32                  len;
 
-       ASSERT(len > 0);
-       /*
-        * Pick up the a.g. freelist struct and the current block.
-        */
-       block = XFS_BUF_TO_ALLOC_BLOCK(cur->bc_bufs[0]);
-#ifdef DEBUG
-       if ((error = xfs_btree_check_sblock(cur, block, 0, cur->bc_bufs[0])))
-               return error;
-#endif
-       /*
-        * Get the address of the rec to be updated.
-        */
-       ptr = cur->bc_ptrs[0];
-       {
-               xfs_alloc_rec_t         *rp;    /* pointer to updated record */
+       ASSERT(cur->bc_btnum == XFS_BTNUM_CNT);
 
-               rp = XFS_ALLOC_REC_ADDR(block, ptr, cur);
+       switch (reason) {
+       case LASTREC_UPDATE:
                /*
-                * Fill in the new contents and log them.
+                * If this is the last leaf block and it's the last record,
+                * then update the size of the longest extent in the AG.
                 */
-               rp->ar_startblock = cpu_to_be32(bno);
-               rp->ar_blockcount = cpu_to_be32(len);
-               xfs_alloc_log_recs(cur, cur->bc_bufs[0], ptr, ptr);
-       }
-       /*
-        * If it's the by-size btree and it's the last leaf block and
-        * it's the last record... then update the size of the longest
-        * extent in the a.g., which we cache in the a.g. freelist header.
-        */
-       if (cur->bc_btnum == XFS_BTNUM_CNT &&
-           be32_to_cpu(block->bb_rightsib) == NULLAGBLOCK &&
-           ptr == be16_to_cpu(block->bb_numrecs)) {
-               xfs_agf_t       *agf;   /* a.g. freespace header */
-               xfs_agnumber_t  seqno;
-
-               agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
-               seqno = be32_to_cpu(agf->agf_seqno);
-               cur->bc_mp->m_perag[seqno].pagf_longest = len;
-               agf->agf_longest = cpu_to_be32(len);
-               xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp,
-                       XFS_AGF_LONGEST);
-       }
-       /*
-        * Updating first record in leaf. Pass new key value up to our parent.
-        */
-       if (ptr == 1) {
-               xfs_alloc_key_t key;    /* key containing [bno, len] */
-
-               key.ar_startblock = cpu_to_be32(bno);
-               key.ar_blockcount = cpu_to_be32(len);
-               if ((error = xfs_btree_updkey(cur, (union xfs_btree_key *)&key, 1)))
-                       return error;
+               if (ptr != xfs_btree_get_numrecs(block))
+                       return;
+               len = rec->alloc.ar_blockcount;
+               break;
+       default:
+               ASSERT(0);
+               return;
        }
-       return 0;
-}
 
-STATIC struct xfs_btree_cur *
-xfs_allocbt_dup_cursor(
-       struct xfs_btree_cur    *cur)
-{
-       return xfs_allocbt_init_cursor(cur->bc_mp, cur->bc_tp,
-                       cur->bc_private.a.agbp, cur->bc_private.a.agno,
-                       cur->bc_btnum);
+       agf->agf_longest = len;
+       cur->bc_mp->m_perag[seqno].pagf_longest = be32_to_cpu(len);
+       xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp, XFS_AGF_LONGEST);
 }
 
 STATIC int
@@ -1864,6 +1831,7 @@ static const struct xfs_btree_ops xfs_allocbt_ops = {
        .key_len                = sizeof(xfs_alloc_key_t),
 
        .dup_cursor             = xfs_allocbt_dup_cursor,
+       .update_lastrec         = xfs_allocbt_update_lastrec,
        .get_maxrecs            = xfs_allocbt_get_maxrecs,
        .init_key_from_rec      = xfs_allocbt_init_key_from_rec,
        .init_ptr_from_cur      = xfs_allocbt_init_ptr_from_cur,
@@ -1902,6 +1870,8 @@ xfs_allocbt_init_cursor(
        cur->bc_blocklog = mp->m_sb.sb_blocklog;
 
        cur->bc_ops = &xfs_allocbt_ops;
+       if (btnum == XFS_BTNUM_CNT)
+               cur->bc_flags = XFS_BTREE_LASTREC_UPDATE;
 
        cur->bc_private.a.agbp = agbp;
        cur->bc_private.a.agno = agno;
index aa110ff..81e2f36 100644 (file)
@@ -113,13 +113,6 @@ extern int xfs_alloc_get_rec(struct xfs_btree_cur *cur,    xfs_agblock_t *bno,
  */
 extern int xfs_alloc_insert(struct xfs_btree_cur *cur, int *stat);
 
-/*
- * Update the record referred to by cur, to the value given by [bno, len].
- * This either works (return 0) or gets an EFSCORRUPTED error.
- */
-extern int xfs_alloc_update(struct xfs_btree_cur *cur, xfs_agblock_t bno,
-                               xfs_extlen_t len);
-
 
 extern struct xfs_btree_cur *xfs_allocbt_init_cursor(struct xfs_mount *,
                struct xfs_trans *, struct xfs_buf *,
index 1296b41..7d6c4ac 100644 (file)
@@ -430,6 +430,24 @@ xfs_bmbt_lookup_ge(
        return xfs_btree_lookup(cur, XFS_LOOKUP_GE, stat);
 }
 
+/*
+* Update the record referred to by cur to the value given
+ * by [off, bno, len, state].
+ * This either works (return 0) or gets an EFSCORRUPTED error.
+ */
+STATIC int
+xfs_bmbt_update(
+       struct xfs_btree_cur    *cur,
+       xfs_fileoff_t           off,
+       xfs_fsblock_t           bno,
+       xfs_filblks_t           len,
+       xfs_exntst_t            state)
+{
+       union xfs_btree_rec     rec;
+
+       xfs_bmbt_disk_set_allf(&rec.bmbt, off, bno, len, state);
+       return xfs_btree_update(cur, &rec);
+}
 
 /*
  * Called from xfs_bmap_add_attrfork to handle btree format files.
index 0a56257..99200a9 100644 (file)
@@ -1963,51 +1963,6 @@ xfs_bmbt_to_bmdr(
        memcpy(tpp, fpp, sizeof(*fpp) * dmxr);
 }
 
-/*
- * Update the record to the passed values.
- */
-int
-xfs_bmbt_update(
-       xfs_btree_cur_t         *cur,
-       xfs_fileoff_t           off,
-       xfs_fsblock_t           bno,
-       xfs_filblks_t           len,
-       xfs_exntst_t            state)
-{
-       xfs_bmbt_block_t        *block;
-       xfs_buf_t               *bp;
-       int                     error;
-       xfs_bmbt_key_t          key;
-       int                     ptr;
-       xfs_bmbt_rec_t          *rp;
-
-       XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
-       XFS_BMBT_TRACE_ARGFFFI(cur, (xfs_dfiloff_t)off, (xfs_dfsbno_t)bno,
-               (xfs_dfilblks_t)len, (int)state);
-       block = xfs_bmbt_get_block(cur, 0, &bp);
-#ifdef DEBUG
-       if ((error = xfs_btree_check_lblock(cur, block, 0, bp))) {
-               XFS_BMBT_TRACE_CURSOR(cur, ERROR);
-               return error;
-       }
-#endif
-       ptr = cur->bc_ptrs[0];
-       rp = XFS_BMAP_REC_IADDR(block, ptr, cur);
-       xfs_bmbt_disk_set_allf(rp, off, bno, len, state);
-       xfs_bmbt_log_recs(cur, bp, ptr, ptr);
-       if (ptr > 1) {
-               XFS_BMBT_TRACE_CURSOR(cur, EXIT);
-               return 0;
-       }
-       key.br_startoff = cpu_to_be64(off);
-       if ((error = xfs_btree_updkey(cur, (union xfs_btree_key *)&key, 1))) {
-               XFS_BMBT_TRACE_CURSOR(cur, ERROR);
-               return error;
-       }
-       XFS_BMBT_TRACE_CURSOR(cur, EXIT);
-       return 0;
-}
-
 /*
  * Check extent records, which have just been read, for
  * any bit in the extent flag field. ASSERT on debug
index d04198c..6bfd62e 100644 (file)
@@ -274,8 +274,6 @@ extern void xfs_bmbt_disk_set_allf(xfs_bmbt_rec_t *r, xfs_fileoff_t o,
                        xfs_fsblock_t b, xfs_filblks_t c, xfs_exntst_t v);
 
 extern void xfs_bmbt_to_bmdr(xfs_bmbt_block_t *, int, xfs_bmdr_block_t *, int);
-extern int xfs_bmbt_update(struct xfs_btree_cur *, xfs_fileoff_t,
-                               xfs_fsblock_t, xfs_filblks_t, xfs_exntst_t);
 
 extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
                struct xfs_trans *, struct xfs_inode *, int);
index 1459a2b..205272f 100644 (file)
@@ -988,6 +988,30 @@ xfs_btree_get_sibling(
        }
 }
 
+/*
+ * Return true if ptr is the last record in the btree and
+ * we need to track updateѕ to this record.  The decision
+ * will be further refined in the update_lastrec method.
+ */
+STATIC int
+xfs_btree_is_lastrec(
+       struct xfs_btree_cur    *cur,
+       struct xfs_btree_block  *block,
+       int                     level)
+{
+       union xfs_btree_ptr     ptr;
+
+       if (level > 0)
+               return 0;
+       if (!(cur->bc_flags & XFS_BTREE_LASTREC_UPDATE))
+               return 0;
+
+       xfs_btree_get_sibling(cur, block, &ptr, XFS_BB_RIGHTSIB);
+       if (!xfs_btree_ptr_is_null(cur, &ptr))
+               return 0;
+       return 1;
+}
+
 STATIC xfs_daddr_t
 xfs_btree_ptr_to_daddr(
        struct xfs_btree_cur    *cur,
@@ -1079,6 +1103,20 @@ xfs_btree_copy_keys(
        memcpy(dst_key, src_key, numkeys * cur->bc_ops->key_len);
 }
 
+/*
+ * Copy records from one btree block to another.
+ */
+STATIC void
+xfs_btree_copy_recs(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_rec     *dst_rec,
+       union xfs_btree_rec     *src_rec,
+       int                     numrecs)
+{
+       ASSERT(numrecs >= 0);
+       memcpy(dst_rec, src_rec, numrecs * cur->bc_ops->rec_len);
+}
+
 /*
  * Log key values from the btree block.
  */
@@ -1104,6 +1142,26 @@ xfs_btree_log_keys(
        XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
 }
 
+/*
+ * Log record values from the btree block.
+ */
+STATIC void
+xfs_btree_log_recs(
+       struct xfs_btree_cur    *cur,
+       struct xfs_buf          *bp,
+       int                     first,
+       int                     last)
+{
+       XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+       XFS_BTREE_TRACE_ARGBII(cur, bp, first, last);
+
+       xfs_trans_log_buf(cur->bc_tp, bp,
+                         xfs_btree_rec_offset(cur, first),
+                         xfs_btree_rec_offset(cur, last + 1) - 1);
+
+       XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+}
+
 /*
  * Increment cursor by one record at the level.
  * For nonzero levels the leaf-ward information is untouched.
@@ -1576,3 +1634,66 @@ xfs_btree_updkey(
        XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
        return 0;
 }
+
+/*
+ * Update the record referred to by cur to the value in the
+ * given record. This either works (return 0) or gets an
+ * EFSCORRUPTED error.
+ */
+int
+xfs_btree_update(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_rec     *rec)
+{
+       struct xfs_btree_block  *block;
+       struct xfs_buf          *bp;
+       int                     error;
+       int                     ptr;
+       union xfs_btree_rec     *rp;
+
+       XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+       XFS_BTREE_TRACE_ARGR(cur, rec);
+
+       /* Pick up the current block. */
+       block = xfs_btree_get_block(cur, 0, &bp);
+
+#ifdef DEBUG
+       error = xfs_btree_check_block(cur, block, 0, bp);
+       if (error)
+               goto error0;
+#endif
+       /* Get the address of the rec to be updated. */
+       ptr = cur->bc_ptrs[0];
+       rp = xfs_btree_rec_addr(cur, ptr, block);
+
+       /* Fill in the new contents and log them. */
+       xfs_btree_copy_recs(cur, rp, rec, 1);
+       xfs_btree_log_recs(cur, bp, ptr, ptr);
+
+       /*
+        * If we are tracking the last record in the tree and
+        * we are at the far right edge of the tree, update it.
+        */
+       if (xfs_btree_is_lastrec(cur, block, 0)) {
+               cur->bc_ops->update_lastrec(cur, block, rec,
+                                           ptr, LASTREC_UPDATE);
+       }
+
+       /* Updating first rec in leaf. Pass new key value up to our parent. */
+       if (ptr == 1) {
+               union xfs_btree_key     key;
+
+               cur->bc_ops->init_key_from_rec(&key, rec);
+               error = xfs_btree_updkey(cur, &key, 1);
+               if (error)
+                       goto error0;
+       }
+
+       XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+       return 0;
+
+error0:
+       XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+       return error;
+}
+
index ac3f527..c3bfa55 100644 (file)
@@ -187,6 +187,12 @@ struct xfs_btree_ops {
        /* cursor operations */
        struct xfs_btree_cur *(*dup_cursor)(struct xfs_btree_cur *);
 
+       /* update last record information */
+       void    (*update_lastrec)(struct xfs_btree_cur *cur,
+                                 struct xfs_btree_block *block,
+                                 union xfs_btree_rec *rec,
+                                 int ptr, int reason);
+
        /* records in block/level */
        int     (*get_maxrecs)(struct xfs_btree_cur *cur, int level);
 
@@ -220,6 +226,12 @@ struct xfs_btree_ops {
 #endif
 };
 
+/*
+ * Reasons for the update_lastrec method to be called.
+ */
+#define LASTREC_UPDATE 0
+
+
 /*
  * Btree cursor structure.
  * This collects all information needed by the btree code in one place.
@@ -264,6 +276,7 @@ typedef struct xfs_btree_cur
 /* cursor flags */
 #define XFS_BTREE_LONG_PTRS            (1<<0)  /* pointers are 64bits long */
 #define XFS_BTREE_ROOT_IN_INODE                (1<<1)  /* root may be variable size */
+#define XFS_BTREE_LASTREC_UPDATE       (1<<2)  /* track last rec externally */
 
 
 #define        XFS_BTREE_NOERROR       0
@@ -519,6 +532,7 @@ int xfs_btree_increment(struct xfs_btree_cur *, int, int *);
 int xfs_btree_decrement(struct xfs_btree_cur *, int, int *);
 int xfs_btree_lookup(struct xfs_btree_cur *, xfs_lookup_t, int *);
 int xfs_btree_updkey(struct xfs_btree_cur *, union xfs_btree_key *, int);
+int xfs_btree_update(struct xfs_btree_cur *, union xfs_btree_rec *);
 
 /*
  * Helpers.
index bbf537f..138651a 100644 (file)
@@ -171,6 +171,26 @@ xfs_inobt_lookup_le(
        return xfs_btree_lookup(cur, XFS_LOOKUP_LE, stat);
 }
 
+/*
+ * Update the record referred to by cur to the value given
+ * by [ino, fcnt, free].
+ * This either works (return 0) or gets an EFSCORRUPTED error.
+ */
+STATIC int                             /* error */
+xfs_inobt_update(
+       struct xfs_btree_cur    *cur,   /* btree cursor */
+       xfs_agino_t             ino,    /* starting inode of chunk */
+       __int32_t               fcnt,   /* free inode count */
+       xfs_inofree_t           free)   /* free inode mask */
+{
+       union xfs_btree_rec     rec;
+
+       rec.inobt.ir_startino = cpu_to_be32(ino);
+       rec.inobt.ir_freecount = cpu_to_be32(fcnt);
+       rec.inobt.ir_free = cpu_to_be64(free);
+       return xfs_btree_update(cur, &rec);
+}
+
 /*
  * Allocate new inodes in the allocation group specified by agbp.
  * Return 0 for success, else error code.
index cd8bb51..d080a68 100644 (file)
@@ -1551,58 +1551,6 @@ xfs_inobt_insert(
        return 0;
 }
 
-/*
- * Update the record referred to by cur, to the value given
- * by [ino, fcnt, free].
- * This either works (return 0) or gets an EFSCORRUPTED error.
- */
-int                                    /* error */
-xfs_inobt_update(
-       xfs_btree_cur_t         *cur,   /* btree cursor */
-       xfs_agino_t             ino,    /* starting inode of chunk */
-       __int32_t               fcnt,   /* free inode count */
-       xfs_inofree_t           free)   /* free inode mask */
-{
-       xfs_inobt_block_t       *block; /* btree block to update */
-       xfs_buf_t               *bp;    /* buffer containing btree block */
-       int                     error;  /* error return value */
-       int                     ptr;    /* current record number (updating) */
-       xfs_inobt_rec_t         *rp;    /* pointer to updated record */
-
-       /*
-        * Pick up the current block.
-        */
-       bp = cur->bc_bufs[0];
-       block = XFS_BUF_TO_INOBT_BLOCK(bp);
-#ifdef DEBUG
-       if ((error = xfs_btree_check_sblock(cur, block, 0, bp)))
-               return error;
-#endif
-       /*
-        * Get the address of the rec to be updated.
-        */
-       ptr = cur->bc_ptrs[0];
-       rp = XFS_INOBT_REC_ADDR(block, ptr, cur);
-       /*
-        * Fill in the new contents and log them.
-        */
-       rp->ir_startino = cpu_to_be32(ino);
-       rp->ir_freecount = cpu_to_be32(fcnt);
-       rp->ir_free = cpu_to_be64(free);
-       xfs_inobt_log_recs(cur, bp, ptr, ptr);
-       /*
-        * Updating first record in leaf. Pass new key value up to our parent.
-        */
-       if (ptr == 1) {
-               xfs_inobt_key_t key;    /* key containing [ino] */
-
-               key.ir_startino = cpu_to_be32(ino);
-               if ((error = xfs_btree_updkey(cur, (union xfs_btree_key *)&key, 1)))
-                       return error;
-       }
-       return 0;
-}
-
 STATIC struct xfs_btree_cur *
 xfs_inobt_dup_cursor(
        struct xfs_btree_cur    *cur)
index 674b459..7f77549 100644 (file)
@@ -135,14 +135,6 @@ extern int xfs_inobt_get_rec(struct xfs_btree_cur *cur, xfs_agino_t *ino,
  */
 extern int xfs_inobt_insert(struct xfs_btree_cur *cur, int *stat);
 
-/*
- * Update the record referred to by cur, to the value given
- * by [ino, fcnt, free].
- * This either works (return 0) or gets an EFSCORRUPTED error.
- */
-extern int xfs_inobt_update(struct xfs_btree_cur *cur, xfs_agino_t ino,
-                               __int32_t fcnt, xfs_inofree_t free);
-
 
 extern struct xfs_btree_cur *xfs_inobt_init_cursor(struct xfs_mount *,
                struct xfs_trans *, struct xfs_buf *, xfs_agnumber_t);