Commit 278d0ca1 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Lachlan McIlroy

[XFS] implement generic xfs_btree_update

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: default avatarChristoph Hellwig <hch@infradead.org>
Signed-off-by: default avatarLachlan McIlroy <lachlan@sgi.com>
Signed-off-by: default avatarBill O'Donnell <billodo@sgi.com>
Signed-off-by: default avatarDavid Chinner <david@fromorbit.com>
parent 38bb7423
......@@ -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.
......
......@@ -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;
......
......@@ -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 *,
......
......@@ -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.
......
......@@ -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
......
......@@ -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);
......
......@@ -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;
}
......@@ -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.
......
......@@ -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.
......
......@@ -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)
......
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment