From: Christoph Hellwig <hch@lst.de>
To: xfs@oss.sgi.com
Subject: Re: [RFC] btree test harness
Date: Mon, 1 Sep 2008 00:24:20 +0200 [thread overview]
Message-ID: <20080831222420.GA2301@lst.de> (raw)
In-Reply-To: <20080813223022.GA15025@lst.de>
[-- Attachment #1: Type: text/plain, Size: 602 bytes --]
New version below. Now we use an ioctl to driver the tests for
a given maximum of levels. There's a small program for xfstests
that calls it. No real testcases yet because the setup is still a
little weird:
mkfs.xfs -f \
-b size=512 \
-d agcount=1 \
-l logdev=/dev/hdb,size=65000b,lazy-count=1 \
/dev/hdd
mount -t xfs -o logdev=/dev/hdb /dev/hdd /mnt/
./src/btree-test /mnt 0 <levels to test>
Next step would be to find the largest span of free blocks using real
allocator functions, which should make this a lot safer, and might also
get rid of the requirement of an external log.
[-- Attachment #2: xfs-btree-test-harness --]
[-- Type: text/plain, Size: 34423 bytes --]
Index: linux-2.6-xfs/fs/xfs/xfs_btree_test.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6-xfs/fs/xfs/xfs_btree_test.c 2008-08-31 19:18:01.000000000 -0300
@@ -0,0 +1,1283 @@
+/*
+ * Copyright (c) 2008 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_types.h"
+#include "xfs_bit.h"
+#include "xfs_log.h"
+#include "xfs_inum.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_btree.h"
+#include "xfs_btree_trace.h"
+#include "xfs_ialloc.h"
+#include "xfs_alloc.h"
+#include "xfs_error.h"
+#include "xfs_trans_space.h"
+#include "xfs_rw.h"
+
+/*
+ * Btree test harness.
+ *
+ * The idea is we implement a btree not that we can freely excercise
+ * without the rest of the filesystem involed. It basically mirrors
+ * the alloc by block number btree, just with a separate root block
+ * and dummy agf, and a "special" allocator. The allocator here
+ * just takes over the whole allocation group from a start offset
+ * and uses a simple bitmap - that way we can stree the btree without
+ * having to call into the XFS allocator (and this another btree).
+ * This is of course very dangerous on a real live filesystem, but
+ * this code shouldn't be used on those anyway.
+ *
+ * Initially this is based on the alloc btree code because it is
+ * the simplest. Ideally this also needs to be expanded to handle
+ * inode rooted btrees and all the other features of the btrees
+ * (e.g. longest extent tracking).
+ *
+ * The test harness itself is at the bottom of the file. It
+ * gets invoked in a debug build on XFS initialisation, and
+ * when it is complete it is never referenced again.
+ *
+ * Test harness
+ *
+ * The following tests are necessary:
+ *
+ * - split test
+ * - merge test
+ * - sparse tree test
+ * - depth test
+ *
+ * Ideally the tests should grow and shrink the tree, thereby
+ * exercising root splitting and merging.
+ *
+ * what we need:
+ *
+ * - a fake struct xfs_mount
+ * - a fake struct xfs_trans
+ * - a fake agf
+ * - a fake agf struct xfs_buf
+ * - special cursor init
+ */
+
+/* We need to start a little after 0 to avoid hardcoded assumptions */
+#define XFS_TBT_FIRST_FREE_BLOCK 8
+
+
+static unsigned long *xfs_tbt_allocmap;
+static size_t xfs_tbt_allomap_size;
+
+STATIC int
+xfs_tbt_init_freelist(
+ struct xfs_mount *mp,
+ xfs_agnumber_t agno)
+{
+ struct xfs_buf *agbp;
+ struct xfs_agf *agf;
+ int error;
+ __uint32_t freeblks, startblk;
+ int i;
+
+ error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
+ if (error || agbp == NULL) {
+ cmn_err(CE_WARN, "xfs_alloc_read_agf failed (%d).\n", error);
+ return error ? error : ENOMEM;
+ }
+
+ /*
+ * See what the largest free space is and used for us.
+ *
+ * XXX(hch): this assumes it's clustered at the end..
+ */
+ agf = XFS_BUF_TO_AGF(agbp);
+ freeblks = be32_to_cpu(agf->agf_longest);
+ startblk = be32_to_cpu(agf->agf_length) - freeblks;
+ xfs_buf_relse(agbp);
+
+ cmn_err(CE_NOTE, "%s: using %d blocks, starting at %d\n",
+ __func__, freeblks, startblk);
+
+ /* just us a simple bitmap array indexed by blockno */
+ xfs_tbt_allomap_size = freeblks / NBBY;
+ xfs_tbt_allocmap = kmalloc(xfs_tbt_allomap_size, GFP_KERNEL);
+ if (!xfs_tbt_allocmap) {
+ cmn_err(CE_WARN, "xfs_tbt_init_freelist: ENOMEM");
+ return ENOMEM;
+ }
+
+ memset(xfs_tbt_allocmap, 0xff, xfs_tbt_allomap_size); /* all free */
+ for (i = 0; i < startblk; i++)
+ clear_bit(i, xfs_tbt_allocmap); /* except for used blocks */
+
+ return 0;
+}
+
+STATIC void
+xfs_tbt_destroy_freelist(
+ struct xfs_mount *mp)
+{
+ kfree(xfs_tbt_allocmap);
+}
+
+STATIC int
+xfs_tbt_alloc(
+ xfs_agblock_t *bnop)
+{
+ xfs_agblock_t bno;
+ int val;
+
+ bno = find_first_bit(xfs_tbt_allocmap,
+ xfs_tbt_allomap_size/sizeof(long));
+ if (bno >= xfs_tbt_allomap_size/sizeof(long)) {
+ cmn_err(CE_WARN, "%s: ran out of space\n", __func__);
+ return ENOSPC;
+ }
+
+ val = test_and_clear_bit(bno, xfs_tbt_allocmap);
+
+ ASSERT(val);
+ ASSERT(find_first_bit(xfs_tbt_allocmap,
+ xfs_tbt_allomap_size/sizeof(long)) > bno);
+ ASSERT(bno != NULLAGBLOCK);
+
+ *bnop = bno;
+ return 0;
+}
+
+STATIC int
+xfs_tbt_free(
+ xfs_agblock_t bno)
+{
+ int val;
+
+ ASSERT(bno <= xfs_tbt_allomap_size);
+
+ val = test_and_set_bit(bno, xfs_tbt_allocmap);
+ ASSERT(val == 0);
+
+ return 0;
+}
+
+STATIC int
+xfs_tbt_alloc_block(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *start,
+ union xfs_btree_ptr *new,
+ int length,
+ int *stat)
+{
+ xfs_agblock_t bno;
+ int error;
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+
+ error = xfs_tbt_alloc(&bno);
+ if (error) {
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+ }
+
+ new->s = cpu_to_be32(bno);
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 1;
+ return 0;
+}
+
+STATIC int
+xfs_tbt_free_block(
+ struct xfs_btree_cur *cur,
+ struct xfs_buf *bp)
+{
+ xfs_tbt_free(XFS_DADDR_TO_AGBNO(cur->bc_mp, XFS_BUF_ADDR(bp)));
+ return 0;
+}
+
+STATIC struct xfs_btree_cur *
+xfs_tbt_dup_cursor(
+ struct xfs_btree_cur *cur)
+{
+ struct xfs_btree_cur *new;
+
+ new = kmem_zone_zalloc(xfs_btree_cur_zone, KM_SLEEP);
+
+ new->bc_mp = cur->bc_mp;
+ new->bc_tp = cur->bc_tp;
+ new->bc_nlevels = cur->bc_nlevels;
+ new->bc_btnum = XFS_BTNUM_BNO;
+ new->bc_blocklog = cur->bc_mp->m_sb.sb_blocklog;
+
+ new->bc_ops = cur->bc_ops;
+
+ new->bc_private.a.agbp = cur->bc_private.a.agbp;
+ new->bc_private.a.agno = cur->bc_private.a.agno;
+
+ return new;
+}
+
+STATIC void
+xfs_tbt_set_root(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *ptr,
+ int inc)
+{
+ struct xfs_buf *agbp = cur->bc_private.a.agbp;
+ struct xfs_agf *agf = XFS_BUF_TO_AGF(agbp);
+ xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
+ int btnum = cur->bc_btnum;
+
+ ASSERT(ptr->s != 0);
+
+ agf->agf_roots[btnum] = ptr->s;
+ be32_add_cpu(&agf->agf_levels[btnum], inc);
+ cur->bc_mp->m_perag[seqno].pagf_levels[btnum] += inc;
+}
+
+STATIC int
+xfs_tbt_get_minrecs(
+ struct xfs_btree_cur *cur,
+ int level)
+{
+ return cur->bc_mp->m_alloc_mnr[level != 0];
+}
+
+STATIC int
+xfs_tbt_get_maxrecs(
+ struct xfs_btree_cur *cur,
+ int level)
+{
+ return cur->bc_mp->m_alloc_mxr[level != 0];
+}
+
+STATIC void
+xfs_tbt_init_key_from_rec(
+ union xfs_btree_key *key,
+ union xfs_btree_rec *rec)
+{
+ key->alloc.ar_startblock = rec->alloc.ar_startblock;
+ key->alloc.ar_blockcount = rec->alloc.ar_blockcount;
+}
+
+STATIC void
+xfs_tbt_init_rec_from_key(
+ union xfs_btree_key *key,
+ union xfs_btree_rec *rec)
+{
+ rec->alloc.ar_startblock = key->alloc.ar_startblock;
+ rec->alloc.ar_blockcount = key->alloc.ar_blockcount;
+}
+
+STATIC void
+xfs_tbt_init_rec_from_cur(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *rec)
+{
+ rec->alloc.ar_startblock = cpu_to_be32(cur->bc_rec.a.ar_startblock);
+ rec->alloc.ar_blockcount = cpu_to_be32(cur->bc_rec.a.ar_blockcount);
+}
+
+STATIC void
+xfs_tbt_init_ptr_from_cur(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *ptr)
+{
+ struct xfs_agf *agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
+
+ ASSERT(cur->bc_private.a.agno == be32_to_cpu(agf->agf_seqno));
+ ASSERT(agf->agf_roots[cur->bc_btnum] != 0);
+
+ ptr->s = agf->agf_roots[cur->bc_btnum];
+}
+
+STATIC __int64_t
+xfs_tbt_key_diff(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *key)
+{
+ xfs_alloc_rec_incore_t *rec = &cur->bc_rec.a;
+ xfs_alloc_key_t *kp = &key->alloc;
+
+ return (__int64_t)be32_to_cpu(kp->ar_startblock) - rec->ar_startblock;
+}
+
+STATIC int
+xfs_tbt_kill_root(
+ struct xfs_btree_cur *cur,
+ struct xfs_buf *bp,
+ int level,
+ union xfs_btree_ptr *newroot)
+{
+ int error;
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_STATS_INC(cur, killroot);
+
+ /*
+ * Update the root pointer, decreasing the level by 1 and then
+ * free the old root.
+ */
+ xfs_tbt_set_root(cur, newroot, -1);
+ error = xfs_tbt_free_block(cur, bp);
+ if (error) {
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+ }
+
+ XFS_BTREE_STATS_INC(cur, free);
+
+ xfs_btree_setbuf(cur, level, NULL);
+ cur->bc_nlevels--;
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ return 0;
+}
+
+#ifdef DEBUG
+STATIC int
+xfs_tbt_keys_inorder(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *k1,
+ union xfs_btree_key *k2)
+{
+ return be32_to_cpu(k1->alloc.ar_startblock) <
+ be32_to_cpu(k2->alloc.ar_startblock);
+}
+
+STATIC int
+xfs_tbt_recs_inorder(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *r1,
+ union xfs_btree_rec *r2)
+{
+ return be32_to_cpu(r1->alloc.ar_startblock) +
+ be32_to_cpu(r1->alloc.ar_blockcount) <=
+ be32_to_cpu(r2->alloc.ar_startblock);
+}
+#endif /* DEBUG */
+
+#ifdef XFS_BTREE_TRACE
+ktrace_t *xfs_tbt_trace_buf;
+
+STATIC void
+xfs_tbt_trace_enter(
+ struct xfs_btree_cur *cur,
+ const char *func,
+ char *s,
+ int type,
+ int line,
+ __psunsigned_t a0,
+ __psunsigned_t a1,
+ __psunsigned_t a2,
+ __psunsigned_t a3,
+ __psunsigned_t a4,
+ __psunsigned_t a5,
+ __psunsigned_t a6,
+ __psunsigned_t a7,
+ __psunsigned_t a8,
+ __psunsigned_t a9,
+ __psunsigned_t a10)
+{
+ /* do nothing for now */
+}
+
+STATIC void
+xfs_tbt_trace_cursor(
+ struct xfs_btree_cur *cur,
+ __uint32_t *s0,
+ __uint64_t *l0,
+ __uint64_t *l1)
+{
+ *s0 = cur->bc_private.a.agno;
+ *l0 = cur->bc_rec.a.ar_startblock;
+ *l1 = cur->bc_rec.a.ar_blockcount;
+}
+
+STATIC void
+xfs_tbt_trace_key(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *key,
+ __uint64_t *l0,
+ __uint64_t *l1)
+{
+ *l0 = be32_to_cpu(key->alloc.ar_startblock);
+ *l1 = be32_to_cpu(key->alloc.ar_blockcount);
+}
+
+STATIC void
+xfs_tbt_trace_record(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *rec,
+ __uint64_t *l0,
+ __uint64_t *l1,
+ __uint64_t *l2)
+{
+ *l0 = be32_to_cpu(rec->alloc.ar_startblock);
+ *l1 = be32_to_cpu(rec->alloc.ar_blockcount);
+ *l2 = 0;
+}
+#endif /* XFS_BTREE_TRACE */
+
+static const struct xfs_btree_ops xfs_tbt_ops = {
+ .rec_len = sizeof(xfs_alloc_rec_t),
+ .key_len = sizeof(xfs_alloc_key_t),
+
+ .dup_cursor = xfs_tbt_dup_cursor,
+ .set_root = xfs_tbt_set_root,
+ .kill_root = xfs_tbt_kill_root,
+ .alloc_block = xfs_tbt_alloc_block,
+ .free_block = xfs_tbt_free_block,
+ .get_minrecs = xfs_tbt_get_minrecs,
+ .get_maxrecs = xfs_tbt_get_maxrecs,
+ .init_key_from_rec = xfs_tbt_init_key_from_rec,
+ .init_rec_from_key = xfs_tbt_init_rec_from_key,
+ .init_rec_from_cur = xfs_tbt_init_rec_from_cur,
+ .init_ptr_from_cur = xfs_tbt_init_ptr_from_cur,
+ .key_diff = xfs_tbt_key_diff,
+
+#ifdef DEBUG
+ .keys_inorder = xfs_tbt_keys_inorder,
+ .recs_inorder = xfs_tbt_recs_inorder,
+#endif
+
+#ifdef XFS_BTREE_TRACE
+ .trace_enter = xfs_tbt_trace_enter,
+ .trace_cursor = xfs_tbt_trace_cursor,
+ .trace_key = xfs_tbt_trace_key,
+ .trace_record = xfs_tbt_trace_record,
+#endif
+};
+
+/*
+ * Allocate a new allocation btree cursor.
+ */
+STATIC struct xfs_btree_cur *
+xfs_tbt_init_cursor(
+ struct xfs_mount *mp,
+ xfs_agnumber_t agno,
+ struct xfs_buf *agbp)
+{
+ struct xfs_agf *agf = XFS_BUF_TO_AGF(agbp);
+ struct xfs_btree_cur *cur;
+ struct xfs_trans *tp;
+ int error;
+ uint resblks;
+
+ resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0) << 1;
+ tp = xfs_trans_alloc(mp, XFS_TRANS_STRAT_WRITE);
+ tp->t_flags |= XFS_TRANS_RESERVE;
+ error = xfs_trans_reserve(tp, resblks,
+ XFS_WRITE_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES,
+ XFS_WRITE_LOG_COUNT);
+ if (error) {
+ xfs_trans_cancel(tp, 0);
+ return NULL;
+ }
+
+ cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_SLEEP);
+
+ cur->bc_mp = mp;
+ cur->bc_tp = tp;
+ cur->bc_nlevels = be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]);
+ cur->bc_btnum = XFS_BTNUM_BNO;
+ cur->bc_blocklog = mp->m_sb.sb_blocklog;
+
+ cur->bc_ops = &xfs_tbt_ops;
+
+ cur->bc_private.a.agbp = agbp;
+ cur->bc_private.a.agno = agno;
+
+ return cur;
+}
+
+/*
+ * Lookup the record equal to [bno, len] in the btree given by cur.
+ */
+STATIC int /* error */
+xfs_tbt_lookup_eq(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len, /* length of extent */
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.a.ar_startblock = bno;
+ cur->bc_rec.a.ar_blockcount = len;
+ return xfs_btree_lookup(cur, XFS_LOOKUP_EQ, stat);
+}
+
+/*
+ * Lookup the first record greater than or equal to [bno, len]
+ * in the btree given by cur.
+ */
+STATIC int /* error */
+xfs_tbt_lookup_ge(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len, /* length of extent */
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.a.ar_startblock = bno;
+ cur->bc_rec.a.ar_blockcount = len;
+ return xfs_btree_lookup(cur, XFS_LOOKUP_GE, stat);
+}
+
+/*
+ * Lookup the first record less than or equal to [bno, len]
+ * in the btree given by cur.
+ */
+STATIC int /* error */
+xfs_tbt_lookup_le(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len, /* length of extent */
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.a.ar_startblock = bno;
+ cur->bc_rec.a.ar_blockcount = len;
+ 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_tbt_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);
+}
+
+/*
+ * Get the data from the pointed-to record.
+ */
+STATIC int /* error */
+xfs_tbt_get_rec(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t *bno, /* output: starting block of extent */
+ xfs_extlen_t *len, /* output: length of extent */
+ int *stat) /* output: success/failure */
+{
+ union xfs_btree_rec *rec;
+ int error;
+
+ error = xfs_btree_get_rec(cur, &rec, stat);
+ if (!error && *stat == 1) {
+ *bno = be32_to_cpu(rec->alloc.ar_startblock);
+ *len = be32_to_cpu(rec->alloc.ar_blockcount);
+ }
+ return error;
+}
+
+STATIC int
+xfs_tbt_destroy_cursor(
+ struct xfs_btree_cur *cur)
+{
+ struct xfs_trans *tp = cur->bc_tp;
+
+ xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+ return xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
+}
+
+/*
+ * Build a full tree of the required depth of single block extents
+ * separated by a single block. We want discrete reecords to be
+ * built here.
+ *
+ * XXX: we probably want to create larger holes as well to be able
+ * to do more coverage on left and right merging and splitting.
+ * However, that is really a function of the specific implementation,
+ * not the core btree code which
+ */
+STATIC int
+xfs_tbt_build_sparse_tree(
+ struct xfs_mount *mp,
+ xfs_agnumber_t agno,
+ struct xfs_buf *agbp,
+ int levels)
+{
+ struct xfs_btree_cur *cur;
+ int i;
+ xfs_agblock_t offset = XFS_TBT_FIRST_FREE_BLOCK;
+ xfs_extlen_t len = 1;
+ struct xfs_agf *agf;
+ int error = 0, error2;
+
+ agf = XFS_BUF_TO_AGF(agbp);
+ while (be32_to_cpu(agf->agf_levels[0]) < levels) {
+ cur = xfs_tbt_init_cursor(mp, agno, agbp);
+ if (!cur)
+ return ENOMEM;
+
+ i = 0;
+ /* Check the extent does not exist */
+ error = xfs_tbt_lookup_eq(cur, offset, len, &i);
+ if (error) {
+ cmn_err(CE_ALERT, "%s: lookup error at offset %u (%d)",
+ __func__, offset, error);
+ goto out_error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 0);
+
+ /* Insert the extent */
+ cur->bc_rec.a.ar_startblock = offset;
+ cur->bc_rec.a.ar_blockcount = len;
+ i = 0;
+ error = xfs_btree_insert(cur, &i);
+ if (error) {
+ cmn_err(CE_ALERT, "%s: insert failed at offset %u (%d)",
+ __func__, offset, error);
+ goto out_error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+
+ /* keep a count of records in the tree */
+ be32_add_cpu(&agf->agf_spare0, 1);
+
+ /* move on to new extent */
+ offset += 2;
+
+out_error:
+ error2 = xfs_tbt_destroy_cursor(cur);
+ if (error2) {
+ cmn_err(CE_ALERT,
+ "%s: failed to commit transaction (%d)",
+ __func__, error);
+ if (!error)
+ error = error2;
+ }
+
+ if (error)
+ return error;
+ }
+
+ /* record largest offset added */
+ agf->agf_spare1 = cpu_to_be32(offset);
+ return error;
+}
+
+STATIC int
+xfs_tbt_empty_sparse_tree(
+ struct xfs_mount *mp,
+ xfs_agnumber_t agno,
+ struct xfs_buf *agbp,
+ int levels)
+{
+ int error = 0, error2;
+ int i;
+ struct xfs_btree_cur *cur;
+ struct xfs_agf *agf;
+
+ agf = XFS_BUF_TO_AGF(agbp);
+ while (be32_to_cpu(agf->agf_spare0) > 0) {
+ cur = xfs_tbt_init_cursor(mp, agno, agbp);
+ if (!cur)
+ return ENOMEM;
+ i = 0;
+
+ /* find the extent that spans this offset/len */
+ error = xfs_tbt_lookup_ge(cur, XFS_TBT_FIRST_FREE_BLOCK, 1, &i);
+ if (error) {
+ cmn_err(CE_ALERT, "%s: lookup error at offset %u (%d)",
+ __func__, XFS_TBT_FIRST_FREE_BLOCK, error);
+ goto out_error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+
+ error = xfs_btree_delete(cur, &i);
+ if (error) {
+ cmn_err(CE_ALERT, "%s: "
+ "%s error at offset %u (%d)",
+ __func__, "xfs_btree_delete",
+ XFS_TBT_FIRST_FREE_BLOCK, error);
+ goto out_error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+ be32_add_cpu(&agf->agf_spare0, -1);
+out_error:
+ error2 = xfs_tbt_destroy_cursor(cur);
+ if (error2) {
+ cmn_err(CE_ALERT,
+ "%s: failed to commit transaction (%d)",
+ __func__, error);
+ if (!error)
+ error = error2;
+ }
+
+ if (error)
+ return error;
+ }
+
+ return error;
+}
+
+/*
+ * Take a tree and punch alternate blocks out of it until it
+ * reaches the required depth. The will split records apart.
+ *
+ * Hacked out of xfs_tbt_fixup_trees()
+ */
+STATIC int
+xfs_tbt_punch_sparse_tree(
+ struct xfs_mount *mp,
+ xfs_agnumber_t agno,
+ struct xfs_buf *agbp,
+ int levels,
+ int dir)
+{
+ int error = 0, error2;
+ int i;
+ xfs_agblock_t fbno;
+ xfs_agblock_t nfbno1;
+ xfs_agblock_t nfbno2;
+ xfs_extlen_t flen = 0;
+ xfs_extlen_t nflen1 = 0;
+ xfs_extlen_t nflen2 = 0;
+ xfs_agblock_t offset;
+ xfs_extlen_t len;
+ xfs_agblock_t delta;
+ struct xfs_btree_cur *cur;
+ struct xfs_agf *agf;
+
+ agf = XFS_BUF_TO_AGF(agbp);
+
+ switch (dir) {
+ default:
+ case 0: /* r to l */
+ offset = XFS_TBT_FIRST_FREE_BLOCK + 1;
+ len = 1;
+ delta = 2;
+ break;
+ case 1: /* l to r */
+ offset = be32_to_cpu(agf->agf_spare1) - 3; // why??
+ len = 1;
+ delta = -2;
+ break;
+ case 2: /* middle to r/l */
+ /* XXX: not implemented yet */
+ return 0;
+ break;
+ }
+
+ while (be32_to_cpu(agf->agf_levels[0]) < levels) {
+ cur = xfs_tbt_init_cursor(mp, agno, agbp);
+ if (!cur)
+ return ENOMEM;
+
+ i = 0;
+ /* find the extent that spans this offset/len */
+ error = xfs_tbt_lookup_le(cur, offset, 1, &i);
+ if (error) {
+ cmn_err(CE_ALERT, "%s: lookup error at offset %u (%d)",
+ __func__, offset, error);
+ goto out_error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+
+ /* get the range of the extent */
+ error = xfs_tbt_get_rec(cur, &fbno, &flen, &i);
+ if (error) {
+ cmn_err(CE_ALERT, "%s: get_rec error at offset %u (%d)",
+ __func__, offset, error);
+ goto out_error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 1 && fbno <= offset && flen >= len);
+
+ if (fbno == offset && flen == len)
+ /* just delete the record under the cursor */
+ nfbno1 = nfbno2 = NULLAGBLOCK;
+ else if (fbno == offset) {
+ /* punching out left edge */
+ nfbno1 = fbno + len;
+ nflen1 = flen - len;
+ nfbno2 = NULLAGBLOCK;
+ } else if (fbno + flen == offset + len) {
+ /* punching out right edge */
+ nfbno1 = fbno;
+ nflen1 = flen - len;
+ nfbno2 = NULLAGBLOCK;
+ } else {
+ /* punching out left and right edge */
+ nfbno1 = fbno;
+ nflen1 = offset - fbno;
+ nfbno2 = offset + len;
+ nflen2 = (fbno + flen) - nfbno2;
+ }
+
+ if (nfbno1 == NULLAGBLOCK) {
+ error = xfs_btree_delete(cur, &i);
+ if (error) {
+ cmn_err(CE_ALERT, "%s: "
+ "%s error at offset %u (%d)",
+ __func__, "xfs_btree_delete",
+ offset, error);
+ goto out_error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+ be32_add_cpu(&agf->agf_spare0, -1);
+ } else {
+ /*
+ * Update the by-block entry to start later|be shorter.
+ */
+ error = xfs_tbt_update(cur, nfbno1, nflen1);
+ if (error) {
+ cmn_err(CE_ALERT, "%s: "
+ "xfs_btree_update error at offset %u (%d)",
+ __func__, offset, error);
+ goto out_error;
+ }
+ }
+ if (nfbno2 != NULLAGBLOCK) {
+ /*
+ * Need to add the second entry. Confirm it does not
+ * exist first
+ */
+ error = xfs_tbt_lookup_eq(cur, nfbno2, nflen2, &i);
+ if (error) {
+ cmn_err(CE_ALERT, "%s: "
+ "lookup equal error at nfbno2 %u (%d)",
+ __func__, nfbno2, error);
+ goto out_error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 0);
+ error = xfs_btree_insert(cur, &i);
+ if (error) {
+ cmn_err(CE_ALERT,
+ "%s: insert error at nfbno2 %u (%d)",
+ __func__, nfbno2, error);
+ goto out_error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+
+ /* keep a count of records in the tree */
+ be32_add_cpu(&agf->agf_spare0, 1);
+ }
+
+ /* move on to new extent */
+ offset += delta;
+
+out_error:
+ error2 = xfs_tbt_destroy_cursor(cur);
+ if (error2) {
+ cmn_err(CE_ALERT,
+ "%s: failed to commit transaction (%d)",
+ __func__, error);
+ if (!error)
+ error = error2;
+ }
+
+ if (error)
+ return error;
+ }
+
+ return error;
+}
+
+/*
+ * Take a sparse tree and fill the holes in it until there are
+ * no holes left. This requires finding the hole and it's adjacent
+ * extent(s) and updating extents in place and deleting old
+ * overlapping extents.
+ *
+ * XXX: only handles single extent holes in the tree
+ */
+STATIC int
+xfs_tbt_fill_sparse_tree(
+ struct xfs_mount *mp,
+ xfs_agnumber_t agno,
+ struct xfs_buf *agbp,
+ int levels,
+ int dir)
+{
+ int error = 0, error2;
+ int i;
+ xfs_agblock_t lbno;
+ xfs_agblock_t rbno;
+ xfs_extlen_t llen = 0;
+ xfs_extlen_t rlen = 0;
+ xfs_agblock_t offset;
+ xfs_extlen_t len;
+ struct xfs_btree_cur *cur;
+ struct xfs_agf *agf;
+ int delta;
+
+ agf = XFS_BUF_TO_AGF(agbp);
+
+ switch (dir) {
+ default:
+ case 0: /* r to l */
+ offset = XFS_TBT_FIRST_FREE_BLOCK + 1;
+ len = 1;
+ delta = 2;
+ break;
+ case 1: /* l to r */
+ offset = be32_to_cpu(agf->agf_spare1) - 3; // why??
+ len = 1;
+ delta = -2;
+ break;
+ case 2: /* middle to r/l */
+ /* XXX: not implemented yet */
+ return 0;
+ break;
+ }
+
+ while (be32_to_cpu(agf->agf_spare0) > 1) {
+ cur = xfs_tbt_init_cursor(mp, agno, agbp);
+ if (!cur)
+ return ENOMEM;
+
+ /* are we in a hole? (should be) */
+ error = xfs_tbt_lookup_eq(cur, offset, len, &i);
+ if (error) {
+ cmn_err(CE_ALERT,
+ "%s: eqlookup error at offset %u (%d)",
+ __func__, offset, error);
+ goto out_error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 0);
+
+ /* find left neighbour */
+ error = xfs_tbt_lookup_le(cur, offset, len, &i);
+ if (error) {
+ cmn_err(CE_ALERT,
+ "%s: lelookup error at offset %u (%d)",
+ __func__, offset, error);
+ goto out_error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+
+ /* get the range of the extent */
+ error = xfs_tbt_get_rec(cur, &lbno, &llen, &i);
+ if (error) {
+ cmn_err(CE_ALERT,
+ "%s: leget_rec error at offset %u (%d)",
+ __func__, offset, error);
+ goto out_error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+
+ /* find right neighbour */
+ error = xfs_tbt_lookup_ge(cur, offset, len, &i);
+ if (error) {
+ cmn_err(CE_ALERT,
+ "%s: gelookup error at offset %u (%d)",
+ __func__, offset, error);
+ goto out_error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+
+ /* get the range of the extent */
+ error = xfs_tbt_get_rec(cur, &rbno, &rlen, &i);
+ if (error) {
+ cmn_err(CE_ALERT,
+ "%s: geget_rec error at offset %u (%d)",
+ __func__, offset, error);
+ goto out_error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+
+ error = EIO;
+ if (lbno + llen != offset) {
+ cmn_err(CE_ALERT,
+ "%s: left record not correct at %u (%u,%u)",
+ __func__, offset, lbno, llen);
+ goto out_error;
+ } else if (offset + len != rbno) {
+ cmn_err(CE_ALERT,
+ "%s: right record not correct at %u (%u,%u)",
+ __func__, offset, rbno, rlen);
+ goto out_error;
+ }
+
+ /*
+ * fill hole: update one record, delete the other.
+ * The cursor currently points at the right record,
+ * so delete it and update the left record. Technically
+ * this is correct as the index of the left record does
+ * not change - only it's length
+ */
+ llen = llen + len + rlen;
+ error = xfs_btree_delete(cur, &i);
+ if (error) {
+ cmn_err(CE_ALERT,
+ "%s: xfs_btree_delete error at offset %u (%d)",
+ __func__, rbno, error);
+ goto out_error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+ be32_add_cpu(&agf->agf_spare0, -1);
+
+ /* find left neighbour (again) */
+ error = xfs_tbt_lookup_le(cur, offset, len, &i);
+ if (error) {
+ cmn_err(CE_ALERT,
+ "%s: lelookup error at offset %u (%d)",
+ __func__, offset, error);
+ goto out_error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+
+ /* Update the left entry */
+ error = xfs_tbt_update(cur, lbno, llen);
+ if (error) {
+ cmn_err(CE_ALERT,
+ "%s: xfs_btree_update error at offset %u (%d)",
+ __func__, lbno, error);
+ goto out_error;
+ }
+
+ /* move on to new extent */
+ offset += delta;
+
+out_error:
+ error2 = xfs_tbt_destroy_cursor(cur);
+ if (error2) {
+ cmn_err(CE_ALERT,
+ "%s: failed to commit transaction (%d)",
+ __func__, error);
+ if (!error)
+ error = error2;
+ }
+
+ if (error)
+ return error;
+ }
+
+ return error;
+}
+
+STATIC int
+xfs_tbt_init_ag(
+ struct xfs_mount *mp,
+ xfs_agnumber_t agno,
+ xfs_agblock_t *agbnop)
+{
+ xfs_extlen_t agsize = mp->m_sb.sb_agblocks;
+ struct xfs_buf *bp;
+ struct xfs_btree_block *block;
+ struct xfs_alloc_rec *arec;
+ xfs_agblock_t agbno;
+ xfs_agblock_t rbno;
+ int error;
+ struct xfs_agf *agf;
+
+ /* Allocate a btree root block */
+ error = xfs_tbt_alloc(&rbno);
+ if (error) {
+ printk("xfs_tbt_alloc failed\n");
+ return error;
+ }
+
+ bp = xfs_buf_get(mp->m_ddev_targp,
+ XFS_AGB_TO_DADDR(mp, agno, rbno),
+ BTOBB(mp->m_sb.sb_blocksize), 0);
+
+ block = XFS_BUF_TO_BLOCK(bp);
+ memset(block, 0, mp->m_sb.sb_blocksize);
+ block->bb_magic = cpu_to_be32(XFS_ABTB_MAGIC);
+ block->bb_level = 0;
+ block->bb_numrecs = cpu_to_be16(1);
+ block->bb_u.s.bb_leftsib = cpu_to_be32(NULLAGBLOCK);
+ block->bb_u.s.bb_rightsib = cpu_to_be32(NULLAGBLOCK);
+
+ arec = XFS_BTREE_REC_ADDR(xfs_alloc, block, 1);
+ arec->ar_startblock = cpu_to_be32(2); /* don't start at the beginning */
+ arec->ar_blockcount = cpu_to_be32(mp->m_sb.sb_agblocks -
+ be32_to_cpu(arec->ar_startblock));
+
+ error = xfs_bwrite(mp, bp);
+ if (error)
+ return error;
+
+ /*
+ * AG freelist header block
+ */
+ error = xfs_tbt_alloc(&agbno);
+ if (error) {
+ printk("xfs_tbt_alloc failed\n");
+ return error;
+ }
+
+ bp = xfs_buf_get(mp->m_ddev_targp,
+ XFS_AG_DADDR(mp, agno, agbno),
+ XFS_FSS_TO_BB(mp, 1), 0);
+
+ agf = XFS_BUF_TO_AGF(bp);
+ memset(agf, 0, mp->m_sb.sb_sectsize);
+ agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
+ agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
+ agf->agf_seqno = cpu_to_be32(agno);
+ agf->agf_length = cpu_to_be32(agsize);
+ agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(rbno); //
+ agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(rbno); //
+ agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
+ agf->agf_levels[XFS_BTNUM_CNTi] = cpu_to_be32(1);
+ agf->agf_flfirst = 0;
+ agf->agf_fllast = cpu_to_be32(XFS_AGFL_SIZE(mp) - 1);
+ agf->agf_flcount = 0;
+ /* don't start at the beginning */
+ agf->agf_freeblks = cpu_to_be32(agsize - 2);
+ agf->agf_longest = cpu_to_be32(agsize - 2);
+
+ error = xfs_bwrite(mp, bp);
+ if (error)
+ return error;
+
+ *agbnop = agbno;
+ return 0;
+}
+
+STATIC int
+xfs_tbt_read_agf(
+ struct xfs_mount *mp,
+ struct xfs_trans *tp,
+ xfs_agnumber_t agno,
+ xfs_agblock_t agbno,
+ struct xfs_buf **bpp)
+{
+ int error;
+
+ ASSERT(agno != NULLAGNUMBER);
+ error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
+ XFS_AG_DADDR(mp, agno, agbno),
+ XFS_FSS_TO_BB(mp, 1), 0, bpp);
+ if (error)
+ return error;
+
+ ASSERT(*bpp);
+ ASSERT(!XFS_BUF_GETERROR(*bpp));
+ return 0;
+}
+
+int
+xfs_tbt_test_one(
+ struct xfs_mount *mp,
+ xfs_agnumber_t agno,
+ int levels)
+{
+ struct xfs_buf *agbp = NULL;
+ int error;
+ xfs_agblock_t agbno;
+
+ if (levels > XFS_AG_MAXLEVELS(mp)) {
+ cmn_err(CE_WARN, "filesystem only supports %d btree levels\n",
+ XFS_AG_MAXLEVELS(mp));
+ return -EINVAL;
+ }
+
+ cmn_err(CE_NOTE, "xfs_tbt_test_all: Testing %d levels", levels);
+
+ error = xfs_tbt_init_freelist(mp, agno);
+ if (error) {
+ cmn_err(CE_WARN, "xfs_tbt_init_freelist failed (%d).\n", error);
+ goto out;
+ }
+
+ error = xfs_tbt_init_ag(mp, agno, &agbno);
+ if (error) {
+ cmn_err(CE_WARN, "xfs_tbt_init_ag failed (%d).\n", error);
+ goto out_free_freelist;
+ }
+
+ error = xfs_tbt_read_agf(mp, NULL, agno, agbno, &agbp);
+ if (error || agbp == NULL) {
+ cmn_err(CE_WARN, "xfs_alloc_read_agf failed (%d).\n", error);
+ goto out_free_ag;
+ }
+
+
+ error = xfs_tbt_build_sparse_tree(mp, agno, agbp, levels);
+ if (error)
+ goto out_free_ag;
+ error = xfs_tbt_fill_sparse_tree(mp, agno, agbp, levels,
+ 0 /* left to right */);
+ if (error)
+ goto out_free_ag;
+ error = xfs_tbt_punch_sparse_tree(mp, agno, agbp, levels,
+ 0 /* l to r */);
+ if (error)
+ goto out_free_ag;
+ error = xfs_tbt_fill_sparse_tree(mp, agno, agbp, levels,
+ 1 /* r to l */);
+ if (error)
+ goto out_free_ag;
+ error = xfs_tbt_punch_sparse_tree(mp, agno, agbp, levels,
+ 1 /* r to l */);
+ if (error)
+ goto out_free_ag;
+#ifdef notyet
+ error = xfs_tbt_fill_sparse_tree(mp, agno, agbp, levels,
+ 2 /* middle to r/l */);
+ if (error)
+ goto out_free_ag;
+ error = xfs_tbt_punch_sparse_tree(mp, agno, agbp, levels,
+ 2 /* middle to r/l */);
+ if (error)
+ goto out_free_ag;
+#endif
+
+ error = xfs_tbt_empty_sparse_tree(mp, agno, agbp, levels);
+ if (error)
+ goto out_free_ag;
+
+ cmn_err(CE_NOTE, "xfs_tbt_test_all: Tested %d levels OK", levels);
+
+ out_free_ag:
+ xfs_buf_relse(agbp);
+ out_free_freelist:
+ xfs_tbt_destroy_freelist(mp);
+ out:
+ if (error) {
+ cmn_err(CE_WARN,
+ "xfs_tbt_test_all: fail with error %d\n", error);
+ return error;
+ }
+ cmn_err(CE_NOTE, "xfs_tbt_test_all: passed successfully\n");
+ return 0;
+}
+
+int
+xfs_ioc_test_btree(
+ struct xfs_mount *mp,
+ void __user *argp)
+{
+ struct xfs_ioc_test_btree tb;
+
+ if (copy_from_user(&tb, argp, sizeof(tb)))
+ return -EFAULT;
+ if (tb.flags != 0)
+ return -EINVAL;
+ return xfs_tbt_test_one(mp, tb.agno, tb.levels);
+}
Index: linux-2.6-xfs/fs/xfs/Makefile
===================================================================
--- linux-2.6-xfs.orig/fs/xfs/Makefile 2008-08-30 18:28:46.000000000 -0300
+++ linux-2.6-xfs/fs/xfs/Makefile 2008-08-30 18:29:03.000000000 -0300
@@ -84,6 +84,8 @@ xfs-y += xfs_alloc.o \
xfs-$(CONFIG_XFS_TRACE) += xfs_btree_trace.o \
xfs_dir2_trace.o
+xfs-$(CONFIG_XFS_DEBUG) += xfs_btree_test.o
+
# Objects in linux/
xfs-y += $(addprefix $(XFS_LINUX)/, \
kmem.o \
Index: linux-2.6-xfs/fs/xfs/linux-2.6/xfs_ioctl.c
===================================================================
--- linux-2.6-xfs.orig/fs/xfs/linux-2.6/xfs_ioctl.c 2008-08-31 19:17:54.000000000 -0300
+++ linux-2.6-xfs/fs/xfs/linux-2.6/xfs_ioctl.c 2008-08-31 19:18:01.000000000 -0300
@@ -1578,7 +1578,13 @@ xfs_ioctl(
error = xfs_errortag_clearall(mp, 1);
return -error;
+#ifdef DEBUG
+ case XFS_IOC_TEST_BTREE:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return xfs_ioc_test_btree(mp, (void __user *)arg);
+#endif
default:
return -ENOTTY;
}
Index: linux-2.6-xfs/fs/xfs/linux-2.6/xfs_lrw.h
===================================================================
--- linux-2.6-xfs.orig/fs/xfs/linux-2.6/xfs_lrw.h 2008-08-31 19:17:54.000000000 -0300
+++ linux-2.6-xfs/fs/xfs/linux-2.6/xfs_lrw.h 2008-08-31 19:18:01.000000000 -0300
@@ -74,4 +74,6 @@ extern int xfs_dev_is_read_only(struct x
extern int xfs_zero_eof(struct xfs_inode *, xfs_off_t, xfs_fsize_t);
+extern int xfs_ioc_test_btree(struct xfs_mount *, void __user *);
+
#endif /* __XFS_LRW_H__ */
Index: linux-2.6-xfs/fs/xfs/xfs_fs.h
===================================================================
--- linux-2.6-xfs.orig/fs/xfs/xfs_fs.h 2008-08-31 19:17:54.000000000 -0300
+++ linux-2.6-xfs/fs/xfs/xfs_fs.h 2008-08-31 19:18:01.000000000 -0300
@@ -421,6 +421,15 @@ typedef struct xfs_handle {
#define XFS_FSOP_GOING_FLAGS_NOLOGFLUSH 0x2 /* don't flush log nor data */
/*
+ * Arguments for the btree test suite (debug builds only).
+ */
+struct xfs_ioc_test_btree {
+ __u32 agno;
+ __u32 levels;
+ __u32 flags;
+};
+
+/*
* ioctl commands that are used by Linux filesystems
*/
#define XFS_IOC_GETXFLAGS FS_IOC_GETFLAGS
@@ -485,6 +494,7 @@ typedef struct xfs_handle {
#define XFS_IOC_FSGEOMETRY _IOR ('X', 124, struct xfs_fsop_geom)
#define XFS_IOC_GOINGDOWN _IOR ('X', 125, __uint32_t)
/* XFS_IOC_GETFSUUID ---------- deprecated 140 */
+#define XFS_IOC_TEST_BTREE _IOW ('X', 126, struct xfs_ioc_test_btree)
#ifndef HAVE_BBMACROS
[-- Attachment #3: fsqa-add-btree-testharness --]
[-- Type: text/plain, Size: 1760 bytes --]
Index: xfstests/src/Makefile
===================================================================
--- xfstests.orig/src/Makefile 2008-08-31 19:09:03.000000000 -0300
+++ xfstests/src/Makefile 2008-08-31 19:15:00.000000000 -0300
@@ -15,7 +15,7 @@ TARGETS = dirstress fill fill2 getpagesi
LINUX_TARGETS = loggen xfsctl bstat t_mtab getdevicesize \
preallo_rw_pattern_reader preallo_rw_pattern_writer ftrunc trunc \
fs_perms testx looptest locktest unwritten_mmap \
- bulkstat_unlink_test bulkstat_unlink_test_modified
+ bulkstat_unlink_test bulkstat_unlink_test_modified btree-test
IRIX_TARGETS = open_unlink
Index: xfstests/src/btree-test.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ xfstests/src/btree-test.c 2008-08-31 19:15:14.000000000 -0300
@@ -0,0 +1,48 @@
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <xfs/xfs.h>
+
+/*
+ * Until headers are ready.
+ */
+#ifndef XFS_IOC_TEST_BTREE
+struct xfs_ioc_test_btree {
+ __u32 agno;
+ __u32 levels;
+ __u32 flags;
+};
+#define XFS_IOC_TEST_BTREE _IOW ('X', 126, struct xfs_ioc_test_btree)
+#endif /* XFS_IOC_TEST_BTREE */
+
+int main(int argc, char **argv)
+{
+ struct xfs_ioc_test_btree tb;
+ int fd;
+
+ if (argc != 4) {
+ fprintf(stderr, "usage: %s path agno levels\n", argv[0]);
+ return 1;
+ }
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "can't open %s\n", argv[1]);
+ return 1;
+ }
+
+ tb.agno = atoi(argv[2]);
+ tb.levels = atoi(argv[3]);
+ tb.flags = 0;
+
+ if (ioctl(fd, XFS_IOC_TEST_BTREE, &tb)) {
+ fprintf(stderr, "btree test failed: %s\n",
+ strerror(errno));
+ return 1;
+ }
+
+ return 0;
+}
prev parent reply other threads:[~2008-08-31 22:23 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <20080813223022.GA15025@lst.de>
[not found] ` <20080814005755.GF6119@disturbed>
2008-08-15 22:02 ` [RFC] btree test harness Christoph Hellwig
2008-08-31 22:24 ` Christoph Hellwig [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20080831222420.GA2301@lst.de \
--to=hch@lst.de \
--cc=xfs@oss.sgi.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox