xfs
[Top] [All Lists]

[PATCH 16/51] libxfs: enhance rmapbt definition to support reflink

To: david@xxxxxxxxxxxxx, darrick.wong@xxxxxxxxxx
Subject: [PATCH 16/51] libxfs: enhance rmapbt definition to support reflink
From: "Darrick J. Wong" <darrick.wong@xxxxxxxxxx>
Date: Tue, 06 Oct 2015 22:06:56 -0700
Cc: xfs@xxxxxxxxxxx
Delivered-to: xfs@xxxxxxxxxxx
In-reply-to: <20151007050513.1504.28089.stgit@xxxxxxxxxxxxxxxx>
References: <20151007050513.1504.28089.stgit@xxxxxxxxxxxxxxxx>
User-agent: StGit/0.17.1-dirty
Enlarge the rmapbt records to support reflink operation.

Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx>
---
 include/xfs_trace.h       |   12 +
 libxfs/util.c             |    2 
 libxfs/xfs_alloc.c        |   45 ++-
 libxfs/xfs_alloc.h        |    5 
 libxfs/xfs_bmap.c         |  281 +++++++++++++++++++--
 libxfs/xfs_bmap.h         |    5 
 libxfs/xfs_bmap_btree.c   |    7 -
 libxfs/xfs_format.h       |  120 +++++++++
 libxfs/xfs_ialloc.c       |    8 -
 libxfs/xfs_ialloc_btree.c |    6 
 libxfs/xfs_rmap.c         |  610 +++++++++++++++++++++++++++++++++++++++------
 libxfs/xfs_rmap_btree.c   |   85 ++++--
 libxfs/xfs_rmap_btree.h   |   38 ++-
 13 files changed, 1055 insertions(+), 169 deletions(-)


diff --git a/include/xfs_trace.h b/include/xfs_trace.h
index ebdf778..2c8d34e 100644
--- a/include/xfs_trace.h
+++ b/include/xfs_trace.h
@@ -178,4 +178,16 @@
 #define trace_xfs_rmap_free_extent_done(a,b,c,d,e)     ((void) 0)
 #define trace_xfs_rmap_free_extent_error(a,b,c,d,e)    ((void) 0)
 
+#define trace_xfs_rmapbt_delete(a...)                  ((void) 0)
+#define trace_xfs_rmapbt_insert(a...)                  ((void) 0)
+#define trace_xfs_rmap_insert(a...)                    ((void) 0)
+#define trace_xfs_rmap_delete(a...)                    ((void) 0)
+#define trace_xfs_rmap_move(a...)                      ((void) 0)
+#define trace_xfs_rmap_slide(a...)                     ((void) 0)
+#define trace_xfs_rmap_resize(a...)                    ((void) 0)
+#define trace_xfs_rmapbt_update(a...)                  ((void) 0)
+#define trace_xfs_rmap_combine(a...)                   ((void) 0)
+#define trace_xfs_rmap_lcombine(a...)                  ((void) 0)
+#define trace_xfs_rmap_rcombine(a...)                  ((void) 0)
+
 #endif /* __TRACE_H__ */
diff --git a/libxfs/util.c b/libxfs/util.c
index c9f9175..32c3623 100644
--- a/libxfs/util.c
+++ b/libxfs/util.c
@@ -507,7 +507,7 @@ libxfs_bmap_finish(
        for (free = flist->xbf_first; free != NULL; free = next) {
                next = free->xbfi_next;
                if ((error = xfs_free_extent(*tp, free->xbfi_startblock,
-                               free->xbfi_blockcount)))
+                               free->xbfi_blockcount, &free->xbfi_oinfo)))
                        return error;
                xfs_bmap_del_free(flist, NULL, free);
        }
diff --git a/libxfs/xfs_alloc.c b/libxfs/xfs_alloc.c
index 40cb20a..7bb3a88 100644
--- a/libxfs/xfs_alloc.c
+++ b/libxfs/xfs_alloc.c
@@ -707,11 +707,13 @@ xfs_alloc_ag_vextent(
        ASSERT(!args->wasfromfl || !args->isfl);
        ASSERT(args->agbno % args->alignment == 0);
 
-       /* insert new block into the reverse map btree */
-       error = xfs_rmap_alloc(args->tp, args->agbp, args->agno,
-                              args->agbno, args->len, args->owner);
-       if (error)
-               return error;
+       /* if not file data, insert new block into the reverse map btree */
+       if (args->oinfo.oi_owner) {
+               error = xfs_rmap_alloc(args->tp, args->agbp, args->agno,
+                                      args->agbno, args->len, &args->oinfo);
+               if (error)
+                       return error;
+       }
 
        if (!args->wasfromfl) {
                error = xfs_alloc_update_counters(args->tp, args->pag,
@@ -1661,6 +1663,7 @@ xfs_free_ag_extent(
        xfs_agnumber_t  agno,   /* allocation group number */
        xfs_agblock_t   bno,    /* starting block number */
        xfs_extlen_t    len,    /* length of extent */
+       struct xfs_owner_info   *oinfo, /* extent owner */
        int             isfl)   /* set if is freelist blocks - no sb acctg */
 {
        xfs_btree_cur_t *bno_cur;       /* cursor for by-block btree */
@@ -1678,6 +1681,12 @@ xfs_free_ag_extent(
        xfs_extlen_t    nlen;           /* new length of freespace */
        xfs_perag_t     *pag;           /* per allocation group data */
 
+       if (oinfo->oi_owner) {
+               error = xfs_rmap_free(tp, agbp, agno, bno, len, oinfo);
+               if (error)
+                       goto error0;
+       }
+
        mp = tp->t_mountp;
        /*
         * Allocate and initialize a cursor for the by-block btree.
@@ -2086,13 +2095,15 @@ xfs_alloc_fix_freelist(
         * back on the free list? Maybe we should only do this when space is
         * getting low or the AGFL is more than half full?
         */
+       XFS_RMAP_AG_OWNER(&targs.oinfo, XFS_RMAP_OWN_AG);
        while (pag->pagf_flcount > need) {
                struct xfs_buf  *bp;
 
                error = xfs_alloc_get_freelist(tp, agbp, &bno, 0);
                if (error)
                        goto out_agbp_relse;
-               error = xfs_free_ag_extent(tp, agbp, args->agno, bno, 1, 1);
+               error = xfs_free_ag_extent(tp, agbp, args->agno, bno, 1,
+                                          &targs.oinfo, 1);
                if (error)
                        goto out_agbp_relse;
                bp = xfs_btree_get_bufs(mp, tp, args->agno, bno, 0);
@@ -2102,7 +2113,7 @@ xfs_alloc_fix_freelist(
        memset(&targs, 0, sizeof(targs));
        targs.tp = tp;
        targs.mp = mp;
-       targs.owner = XFS_RMAP_OWN_AG;
+       XFS_RMAP_AG_OWNER(&targs.oinfo, XFS_RMAP_OWN_AG);
        targs.agbp = agbp;
        targs.agno = args->agno;
        targs.alignment = targs.minlen = targs.prod = targs.isfl = 1;
@@ -2361,6 +2372,10 @@ xfs_agf_verify(
            be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]) > XFS_BTREE_MAXLEVELS)
                return false;
 
+       if (xfs_sb_version_hasrmapbt(&mp->m_sb) &&
+           be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]) > XFS_BTREE_MAXLEVELS)
+               return false;
+
        /*
         * during growfs operations, the perag is not fully initialised,
         * so we can't use it for any useful checking. growfs ensures we can't
@@ -2491,6 +2506,8 @@ xfs_alloc_read_agf(
                        be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNOi]);
                pag->pagf_levels[XFS_BTNUM_CNTi] =
                        be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNTi]);
+               pag->pagf_levels[XFS_BTNUM_RMAPi] =
+                       be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAPi]);
                spin_lock_init(&pag->pagb_lock);
                pag->pagb_count = 0;
                /* XXX: pagb_tree doesn't exist in userspace */
@@ -2733,14 +2750,13 @@ error0:
  * Free an extent.
  * Just break up the extent address and hand off to xfs_free_ag_extent
  * after fixing up the freelist.
- *
- * XXX: need owner of extent being freed
  */
 int                            /* error */
 xfs_free_extent(
        xfs_trans_t     *tp,    /* transaction pointer */
        xfs_fsblock_t   bno,    /* starting block number of extent */
-       xfs_extlen_t    len)    /* length of extent */
+       xfs_extlen_t    len,    /* length of extent */
+       struct xfs_owner_info   *oinfo) /* extent owner */
 {
        xfs_alloc_arg_t args;
        int             error;
@@ -2776,13 +2792,8 @@ xfs_free_extent(
                goto error0;
        }
 
-       /* XXX: need owner */
-       error = xfs_rmap_free(tp, args.agbp, args.agno, args.agbno, len, 0);
-       if (error)
-               goto error0;
-
-       /* XXX: initially no multiple references, so just free it */
-       error = xfs_free_ag_extent(tp, args.agbp, args.agno, args.agbno, len, 
0);
+       error = xfs_free_ag_extent(tp, args.agbp, args.agno, args.agbno,
+                                  len, oinfo, 0);
        if (!error)
                xfs_extent_busy_insert(tp, args.agno, args.agbno, len, 0);
 error0:
diff --git a/libxfs/xfs_alloc.h b/libxfs/xfs_alloc.h
index 5b2b616..f78ce53 100644
--- a/libxfs/xfs_alloc.h
+++ b/libxfs/xfs_alloc.h
@@ -87,7 +87,7 @@ typedef struct xfs_alloc_arg {
        char            isfl;           /* set if is freelist blocks - !acctg */
        char            userdata;       /* set if this is user data */
        xfs_fsblock_t   firstblock;     /* io first block allocated */
-       uint64_t        owner;          /* owner of blocks being allocated */
+       struct xfs_owner_info   oinfo;  /* owner of blocks being allocated */
 } xfs_alloc_arg_t;
 
 /*
@@ -179,7 +179,8 @@ int                         /* error */
 xfs_free_extent(
        struct xfs_trans *tp,   /* transaction pointer */
        xfs_fsblock_t   bno,    /* starting block number of extent */
-       xfs_extlen_t    len);   /* length of extent */
+       xfs_extlen_t    len,    /* length of extent */
+       struct xfs_owner_info   *oinfo);        /* extent owner */
 
 int                                    /* error */
 xfs_alloc_lookup_le(
diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c
index 73fbdf0..14934eb 100644
--- a/libxfs/xfs_bmap.c
+++ b/libxfs/xfs_bmap.c
@@ -37,6 +37,7 @@
 #include "xfs_trace.h"
 #include "xfs_attr_leaf.h"
 #include "xfs_quota_defs.h"
+#include "xfs_rmap_btree.h"
 
 
 kmem_zone_t            *xfs_bmap_free_item_zone;
@@ -562,7 +563,8 @@ xfs_bmap_add_free(
        struct xfs_mount        *mp,            /* mount point structure */
        struct xfs_bmap_free    *flist,         /* list of extents */
        xfs_fsblock_t           bno,            /* fs block number of extent */
-       xfs_filblks_t           len)            /* length of extent */
+       xfs_filblks_t           len,            /* length of extent */
+       struct xfs_owner_info   *oinfo)         /* extent owner */
 {
        xfs_bmap_free_item_t    *cur;           /* current (next) element */
        xfs_bmap_free_item_t    *new;           /* new element */
@@ -583,9 +585,14 @@ xfs_bmap_add_free(
        ASSERT(agbno + len <= mp->m_sb.sb_agblocks);
 #endif
        ASSERT(xfs_bmap_free_item_zone != NULL);
+
        new = kmem_zone_alloc(xfs_bmap_free_item_zone, KM_SLEEP);
        new->xbfi_startblock = bno;
        new->xbfi_blockcount = (xfs_extlen_t)len;
+       if (oinfo)
+               memcpy(&new->xbfi_oinfo, oinfo, sizeof(struct xfs_owner_info));
+       else
+               memset(&new->xbfi_oinfo, 0, sizeof(struct xfs_owner_info));
        for (prev = NULL, cur = flist->xbf_first;
             cur != NULL;
             prev = cur, cur = cur->xbfi_next) {
@@ -665,6 +672,7 @@ xfs_bmap_btree_to_extents(
        xfs_mount_t             *mp;    /* mount point structure */
        __be64                  *pp;    /* ptr to block address */
        struct xfs_btree_block  *rblock;/* root btree block */
+       struct xfs_owner_info   oinfo;
 
        mp = ip->i_mount;
        ifp = XFS_IFORK_PTR(ip, whichfork);
@@ -688,7 +696,8 @@ xfs_bmap_btree_to_extents(
        cblock = XFS_BUF_TO_BLOCK(cbp);
        if ((error = xfs_btree_check_block(cur, cblock, 0, cbp)))
                return error;
-       xfs_bmap_add_free(mp, cur->bc_private.b.flist, cbno, 1);
+       XFS_RMAP_INO_BMBT_OWNER(&oinfo, ip->i_ino, whichfork);
+       xfs_bmap_add_free(mp, cur->bc_private.b.flist, cbno, 1, &oinfo);
        ip->i_d.di_nblocks--;
        xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, -1L);
        xfs_trans_binval(tp, cbp);
@@ -769,7 +778,7 @@ xfs_bmap_extents_to_btree(
        memset(&args, 0, sizeof(args));
        args.tp = tp;
        args.mp = mp;
-       args.owner = ip->i_ino;
+       XFS_RMAP_INO_BMBT_OWNER(&args.oinfo, ip->i_ino, whichfork);
        args.firstblock = *firstblock;
        if (*firstblock == NULLFSBLOCK) {
                args.type = XFS_ALLOCTYPE_START_BNO;
@@ -916,7 +925,7 @@ xfs_bmap_local_to_extents(
        memset(&args, 0, sizeof(args));
        args.tp = tp;
        args.mp = ip->i_mount;
-       args.owner = ip->i_ino;
+       XFS_RMAP_INO_OWNER(&args.oinfo, ip->i_ino, whichfork, 0);
        args.firstblock = *firstblock;
        /*
         * Allocate a block.  We know we need only one, since the
@@ -1845,6 +1854,10 @@ xfs_bmap_add_extent_delay_real(
                        if (error)
                                goto done;
                }
+               error = xfs_rmap_combine(bma->rcur, bma->ip->i_ino,
+                               XFS_DATA_FORK, &LEFT, &RIGHT, &PREV);
+               if (error)
+                       goto done;
                break;
 
        case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG:
@@ -1877,6 +1890,10 @@ xfs_bmap_add_extent_delay_real(
                        if (error)
                                goto done;
                }
+               error = xfs_rmap_lcombine(bma->rcur, bma->ip->i_ino,
+                               XFS_DATA_FORK, &LEFT, &PREV);
+               if (error)
+                       goto done;
                break;
 
        case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
@@ -1908,6 +1925,10 @@ xfs_bmap_add_extent_delay_real(
                        if (error)
                                goto done;
                }
+               error = xfs_rmap_rcombine(bma->rcur, bma->ip->i_ino,
+                               XFS_DATA_FORK, &RIGHT, &PREV, new);
+               if (error)
+                       goto done;
                break;
 
        case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
@@ -1937,6 +1958,10 @@ xfs_bmap_add_extent_delay_real(
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
+               error = xfs_rmap_insert(bma->rcur, bma->ip->i_ino,
+                               XFS_DATA_FORK, new);
+               if (error)
+                       goto done;
                break;
 
        case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG:
@@ -1972,6 +1997,10 @@ xfs_bmap_add_extent_delay_real(
                        if (error)
                                goto done;
                }
+               error = xfs_rmap_lcombine(bma->rcur, bma->ip->i_ino,
+                               XFS_DATA_FORK, &LEFT, new);
+               if (error)
+                       goto done;
                da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(bma->ip, temp),
                        startblockval(PREV.br_startblock));
                xfs_bmbt_set_startblock(ep, nullstartblock(da_new));
@@ -2007,6 +2036,10 @@ xfs_bmap_add_extent_delay_real(
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
+               error = xfs_rmap_insert(bma->rcur, bma->ip->i_ino,
+                               XFS_DATA_FORK, new);
+               if (error)
+                       goto done;
 
                if (xfs_bmap_needs_btree(bma->ip, XFS_DATA_FORK)) {
                        error = xfs_bmap_extents_to_btree(bma->tp, bma->ip,
@@ -2055,6 +2088,8 @@ xfs_bmap_add_extent_delay_real(
                        if (error)
                                goto done;
                }
+               error = xfs_rmap_rcombine(bma->rcur, bma->ip->i_ino,
+                               XFS_DATA_FORK, &RIGHT, new, new);
 
                da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(bma->ip, temp),
                        startblockval(PREV.br_startblock));
@@ -2091,6 +2126,10 @@ xfs_bmap_add_extent_delay_real(
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
+               error = xfs_rmap_insert(bma->rcur, bma->ip->i_ino,
+                               XFS_DATA_FORK, new);
+               if (error)
+                       goto done;
 
                if (xfs_bmap_needs_btree(bma->ip, XFS_DATA_FORK)) {
                        error = xfs_bmap_extents_to_btree(bma->tp, bma->ip,
@@ -2160,6 +2199,10 @@ xfs_bmap_add_extent_delay_real(
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
+               error = xfs_rmap_insert(bma->rcur, bma->ip->i_ino,
+                               XFS_DATA_FORK, new);
+               if (error)
+                       goto done;
 
                if (xfs_bmap_needs_btree(bma->ip, XFS_DATA_FORK)) {
                        error = xfs_bmap_extents_to_btree(bma->tp, bma->ip,
@@ -2255,7 +2298,8 @@ xfs_bmap_add_extent_unwritten_real(
        xfs_bmbt_irec_t         *new,   /* new data to add to file extents */
        xfs_fsblock_t           *first, /* pointer to firstblock variable */
        xfs_bmap_free_t         *flist, /* list of extents to be freed */
-       int                     *logflagsp) /* inode logging flags */
+       int                     *logflagsp, /* inode logging flags */
+       struct xfs_btree_cur    *rcur)/* rmap btree pointer */
 {
        xfs_btree_cur_t         *cur;   /* btree cursor */
        xfs_bmbt_rec_host_t     *ep;    /* extent entry for idx */
@@ -2401,6 +2445,10 @@ xfs_bmap_add_extent_unwritten_real(
                                RIGHT.br_blockcount, LEFT.br_state)))
                                goto done;
                }
+               error = xfs_rmap_combine(rcur, ip->i_ino,
+                               XFS_DATA_FORK, &LEFT, &RIGHT, &PREV);
+               if (error)
+                       goto done;
                break;
 
        case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG:
@@ -2438,6 +2486,10 @@ xfs_bmap_add_extent_unwritten_real(
                                LEFT.br_state)))
                                goto done;
                }
+               error = xfs_rmap_lcombine(rcur, ip->i_ino,
+                               XFS_DATA_FORK, &LEFT, &PREV);
+               if (error)
+                       goto done;
                break;
 
        case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
@@ -2473,6 +2525,10 @@ xfs_bmap_add_extent_unwritten_real(
                                newext)))
                                goto done;
                }
+               error = xfs_rmap_rcombine(rcur, ip->i_ino,
+                               XFS_DATA_FORK, &RIGHT, &PREV, new);
+               if (error)
+                       goto done;
                break;
 
        case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
@@ -2546,6 +2602,14 @@ xfs_bmap_add_extent_unwritten_real(
                        if (error)
                                goto done;
                }
+               error = xfs_rmap_move(rcur, ip->i_ino,
+                               XFS_DATA_FORK, &PREV, new->br_blockcount);
+               if (error)
+                       goto done;
+               error = xfs_rmap_resize(rcur, ip->i_ino,
+                               XFS_DATA_FORK, &LEFT, -new->br_blockcount);
+               if (error)
+                       goto done;
                break;
 
        case BMAP_LEFT_FILLING:
@@ -2584,6 +2648,14 @@ xfs_bmap_add_extent_unwritten_real(
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
+               error = xfs_rmap_move(rcur, ip->i_ino,
+                               XFS_DATA_FORK, &PREV, new->br_blockcount);
+               if (error)
+                       goto done;
+               error = xfs_rmap_insert(rcur, ip->i_ino,
+                               XFS_DATA_FORK, new);
+               if (error)
+                       goto done;
                break;
 
        case BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
@@ -2626,6 +2698,14 @@ xfs_bmap_add_extent_unwritten_real(
                                newext)))
                                goto done;
                }
+               error = xfs_rmap_resize(rcur, ip->i_ino,
+                               XFS_DATA_FORK, &PREV, -new->br_blockcount);
+               if (error)
+                       goto done;
+               error = xfs_rmap_move(rcur, ip->i_ino,
+                               XFS_DATA_FORK, &RIGHT, -new->br_blockcount);
+               if (error)
+                       goto done;
                break;
 
        case BMAP_RIGHT_FILLING:
@@ -2666,6 +2746,14 @@ xfs_bmap_add_extent_unwritten_real(
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
+               error = xfs_rmap_resize(rcur, ip->i_ino,
+                               XFS_DATA_FORK, &PREV, -new->br_blockcount);
+               if (error)
+                       goto done;
+               error = xfs_rmap_insert(rcur, ip->i_ino,
+                               XFS_DATA_FORK, new);
+               if (error)
+                       goto done;
                break;
 
        case 0:
@@ -2727,6 +2815,17 @@ xfs_bmap_add_extent_unwritten_real(
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
+               error = xfs_rmap_resize(rcur, ip->i_ino, XFS_DATA_FORK, &PREV,
+                               new->br_startoff - PREV.br_startoff -
+                               PREV.br_blockcount);
+               if (error)
+                       goto done;
+               error = xfs_rmap_insert(rcur, ip->i_ino, XFS_DATA_FORK, new);
+               if (error)
+                       goto done;
+               error = xfs_rmap_insert(rcur, ip->i_ino, XFS_DATA_FORK, &r[1]);
+               if (error)
+                       goto done;
                break;
 
        case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG:
@@ -2930,6 +3029,7 @@ xfs_bmap_add_extent_hole_real(
        int                     rval=0; /* return value (logging flags) */
        int                     state;  /* state bits, accessed thru macros */
        struct xfs_mount        *mp;
+       struct xfs_bmbt_irec    prev;   /* fake previous extent entry */
 
        mp = bma->tp ? bma->tp->t_mountp : NULL;
        ifp = XFS_IFORK_PTR(bma->ip, whichfork);
@@ -3037,6 +3137,12 @@ xfs_bmap_add_extent_hole_real(
                        if (error)
                                goto done;
                }
+               prev = *new;
+               prev.br_startblock = nullstartblock(0);
+               error = xfs_rmap_combine(bma->rcur, bma->ip->i_ino,
+                               whichfork, &left, &right, &prev);
+               if (error)
+                       goto done;
                break;
 
        case BMAP_LEFT_CONTIG:
@@ -3069,6 +3175,10 @@ xfs_bmap_add_extent_hole_real(
                        if (error)
                                goto done;
                }
+               error = xfs_rmap_resize(bma->rcur, bma->ip->i_ino,
+                               whichfork, &left, new->br_blockcount);
+               if (error)
+                       goto done;
                break;
 
        case BMAP_RIGHT_CONTIG:
@@ -3103,6 +3213,10 @@ xfs_bmap_add_extent_hole_real(
                        if (error)
                                goto done;
                }
+               error = xfs_rmap_move(bma->rcur, bma->ip->i_ino,
+                               whichfork, &right, -new->br_blockcount);
+               if (error)
+                       goto done;
                break;
 
        case 0:
@@ -3131,6 +3245,10 @@ xfs_bmap_add_extent_hole_real(
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
+               error = xfs_rmap_insert(bma->rcur, bma->ip->i_ino,
+                               whichfork, new);
+               if (error)
+                       goto done;
                break;
        }
 
@@ -3697,7 +3815,6 @@ xfs_bmap_btalloc(
        memset(&args, 0, sizeof(args));
        args.tp = ap->tp;
        args.mp = mp;
-       args.owner = ap->ip->i_ino;
        args.fsbno = ap->blkno;
 
        /* Trim the allocation back to the maximum an AG can fit. */
@@ -4261,6 +4378,59 @@ xfs_bmapi_delay(
        return 0;
 }
 
+static int
+alloc_rcur(
+       struct xfs_mount        *mp,
+       struct xfs_trans        *tp,
+       struct xfs_btree_cur    **pcur,
+       xfs_fsblock_t           fsblock)
+{
+       struct xfs_btree_cur    *cur = *pcur;
+       struct xfs_buf          *agbp;
+       int                     error;
+       xfs_agnumber_t          agno;
+
+       agno = XFS_FSB_TO_AGNO(mp, fsblock);
+       if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
+               return 0;
+       if (cur && cur->bc_private.a.agno == agno)
+               return 0;
+       if (isnullstartblock(fsblock))
+               return 0;
+
+       error = xfs_alloc_read_agf(mp, tp, agno, 0, &agbp);
+       if (error)
+               return error;
+
+       cur = xfs_rmapbt_init_cursor(mp, tp, agbp, agno);
+       if (!cur) {
+               xfs_trans_brelse(tp, agbp);
+               return -ENOMEM;
+       }
+
+       *pcur = cur;
+       return 0;
+}
+
+static void
+free_rcur(
+       struct xfs_btree_cur    **pcur,
+       int                     bt_error)
+{
+       struct xfs_btree_cur    *cur = *pcur;
+       struct xfs_buf          *agbp;
+       struct xfs_trans        *tp;
+
+       if (cur == NULL)
+               return;
+
+       agbp = cur->bc_private.a.agbp;
+       tp = cur->bc_tp;
+       xfs_btree_del_cursor(cur, bt_error);
+       xfs_trans_brelse(tp, agbp);
+
+       *pcur = NULL;
+}
 
 static int
 xfs_bmapi_allocate(
@@ -4353,6 +4523,10 @@ xfs_bmapi_allocate(
            xfs_sb_version_hasextflgbit(&mp->m_sb))
                bma->got.br_state = XFS_EXT_UNWRITTEN;
 
+       error = alloc_rcur(mp, bma->tp, &bma->rcur, bma->got.br_startblock);
+       if (error)
+               return error;
+
        if (bma->wasdel)
                error = xfs_bmap_add_extent_delay_real(bma);
        else
@@ -4414,9 +4588,13 @@ xfs_bmapi_convert_unwritten(
        mval->br_state = (mval->br_state == XFS_EXT_UNWRITTEN)
                                ? XFS_EXT_NORM : XFS_EXT_UNWRITTEN;
 
+       error = alloc_rcur(bma->ip->i_mount, bma->tp, &bma->rcur, 
mval->br_startblock);
+       if (error)
+               return error;
+
        error = xfs_bmap_add_extent_unwritten_real(bma->tp, bma->ip, &bma->idx,
                        &bma->cur, mval, bma->firstblock, bma->flist,
-                       &tmp_logflags);
+                       &tmp_logflags, bma->rcur);
        /*
         * Log the inode core unconditionally in the unwritten extent conversion
         * path because the conversion might not have done so (e.g., if the
@@ -4618,6 +4796,7 @@ xfs_bmapi_write(
        }
        *nmap = n;
 
+       free_rcur(&bma.rcur, XFS_BTREE_NOERROR);
        /*
         * Transform from btree to extents, give it cur.
         */
@@ -4637,6 +4816,7 @@ xfs_bmapi_write(
                XFS_IFORK_MAXEXT(ip, whichfork));
        error = 0;
 error0:
+       free_rcur(&bma.rcur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
        /*
         * Log everything.  Do this after conversion, there's no point in
         * logging the extent records if we've converted to btree format.
@@ -4689,7 +4869,8 @@ xfs_bmap_del_extent(
        xfs_btree_cur_t         *cur,   /* if null, not a btree */
        xfs_bmbt_irec_t         *del,   /* data to remove from extents */
        int                     *logflagsp, /* inode logging flags */
-       int                     whichfork) /* data or attr fork */
+       int                     whichfork, /* data or attr fork */
+       struct xfs_btree_cur    *rcur)  /* rmap btree */
 {
        xfs_filblks_t           da_new; /* new delay-alloc indirect blocks */
        xfs_filblks_t           da_old; /* old delay-alloc indirect blocks */
@@ -4787,6 +4968,7 @@ xfs_bmap_del_extent(
                nblks = 0;
                do_fx = 0;
        }
+
        /*
         * Set flag value to use in switch statement.
         * Left-contig is 2, right-contig is 1.
@@ -4806,6 +4988,9 @@ xfs_bmap_del_extent(
                XFS_IFORK_NEXT_SET(ip, whichfork,
                        XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
                flags |= XFS_ILOG_CORE;
+               error = xfs_rmap_delete(rcur, ip->i_ino, whichfork, &got);
+               if (error)
+                       goto done;
                if (!cur) {
                        flags |= xfs_ilog_fext(whichfork);
                        break;
@@ -4833,6 +5018,10 @@ xfs_bmap_del_extent(
                }
                xfs_bmbt_set_startblock(ep, del_endblock);
                trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               error = xfs_rmap_move(rcur, ip->i_ino, whichfork,
+                               &got, del->br_blockcount);
+               if (error)
+                       goto done;
                if (!cur) {
                        flags |= xfs_ilog_fext(whichfork);
                        break;
@@ -4859,6 +5048,10 @@ xfs_bmap_del_extent(
                        break;
                }
                trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               error = xfs_rmap_resize(rcur, ip->i_ino, whichfork,
+                               &got, -del->br_blockcount);
+               if (error)
+                       goto done;
                if (!cur) {
                        flags |= xfs_ilog_fext(whichfork);
                        break;
@@ -4884,6 +5077,15 @@ xfs_bmap_del_extent(
                if (!delay) {
                        new.br_startblock = del_endblock;
                        flags |= XFS_ILOG_CORE;
+                       error = xfs_rmap_resize(rcur, ip->i_ino,
+                                       whichfork, &got,
+                                       temp - got.br_blockcount);
+                       if (error)
+                               goto done;
+                       error = xfs_rmap_insert(rcur, ip->i_ino,
+                                       whichfork, &new);
+                       if (error)
+                               goto done;
                        if (cur) {
                                if ((error = xfs_bmbt_update(cur,
                                                got.br_startoff,
@@ -4973,7 +5175,7 @@ xfs_bmap_del_extent(
         */
        if (do_fx)
                xfs_bmap_add_free(mp, flist, del->br_startblock,
-                       del->br_blockcount);
+                       del->br_blockcount, NULL);
        /*
         * Adjust inode # blocks in the file.
         */
@@ -5036,6 +5238,7 @@ xfs_bunmapi(
        int                     wasdel;         /* was a delayed alloc extent */
        int                     whichfork;      /* data or attribute fork */
        xfs_fsblock_t           sum;
+       struct xfs_btree_cur    *rcur = NULL;   /* rmap btree */
 
        trace_xfs_bunmap(ip, bno, len, flags, _RET_IP_);
 
@@ -5120,6 +5323,11 @@ xfs_bunmapi(
                        got.br_startoff + got.br_blockcount - 1);
                if (bno < start)
                        break;
+
+               error = alloc_rcur(mp, tp, &rcur, got.br_startblock);
+               if (error)
+                       goto error0;
+
                /*
                 * Then deal with the (possibly delayed) allocated space
                 * we found.
@@ -5179,7 +5387,7 @@ xfs_bunmapi(
                        del.br_state = XFS_EXT_UNWRITTEN;
                        error = xfs_bmap_add_extent_unwritten_real(tp, ip,
                                        &lastx, &cur, &del, firstblock, flist,
-                                       &logflags);
+                                       &logflags, rcur);
                        if (error)
                                goto error0;
                        goto nodelete;
@@ -5237,7 +5445,8 @@ xfs_bunmapi(
                                lastx--;
                                error = xfs_bmap_add_extent_unwritten_real(tp,
                                                ip, &lastx, &cur, &prev,
-                                               firstblock, flist, &logflags);
+                                               firstblock, flist, &logflags,
+                                               rcur);
                                if (error)
                                        goto error0;
                                goto nodelete;
@@ -5246,7 +5455,8 @@ xfs_bunmapi(
                                del.br_state = XFS_EXT_UNWRITTEN;
                                error = xfs_bmap_add_extent_unwritten_real(tp,
                                                ip, &lastx, &cur, &del,
-                                               firstblock, flist, &logflags);
+                                               firstblock, flist, &logflags,
+                                               rcur);
                                if (error)
                                        goto error0;
                                goto nodelete;
@@ -5299,7 +5509,7 @@ xfs_bunmapi(
                        goto error0;
                }
                error = xfs_bmap_del_extent(ip, tp, &lastx, flist, cur, &del,
-                               &tmp_logflags, whichfork);
+                               &tmp_logflags, whichfork, rcur);
                logflags |= tmp_logflags;
                if (error)
                        goto error0;
@@ -5323,6 +5533,7 @@ nodelete:
        }
        *done = bno == (xfs_fileoff_t)-1 || bno < start || lastx < 0;
 
+       free_rcur(&rcur, XFS_BTREE_NOERROR);
        /*
         * Convert to a btree if necessary.
         */
@@ -5350,6 +5561,7 @@ nodelete:
         */
        error = 0;
 error0:
+       free_rcur(&rcur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
        /*
         * Log everything.  Do this after conversion, there's no point in
         * logging the extent records if we've converted to btree format.
@@ -5422,7 +5634,8 @@ xfs_bmse_merge(
        struct xfs_bmbt_rec_host        *gotp,          /* extent to shift */
        struct xfs_bmbt_rec_host        *leftp,         /* preceding extent */
        struct xfs_btree_cur            *cur,
-       int                             *logflags)      /* output */
+       int                             *logflags,      /* output */
+       struct xfs_btree_cur            *rcur)          /* rmap btree */
 {
        struct xfs_bmbt_irec            got;
        struct xfs_bmbt_irec            left;
@@ -5453,6 +5666,13 @@ xfs_bmse_merge(
        XFS_IFORK_NEXT_SET(ip, whichfork,
                           XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
        *logflags |= XFS_ILOG_CORE;
+       error = xfs_rmap_resize(rcur, ip->i_ino, whichfork, &left,
+                       blockcount - left.br_blockcount);
+       if (error)
+               return error;
+       error = xfs_rmap_delete(rcur, ip->i_ino, whichfork, &got);
+       if (error)
+               return error;
        if (!cur) {
                *logflags |= XFS_ILOG_DEXT;
                return 0;
@@ -5495,7 +5715,8 @@ xfs_bmse_shift_one(
        struct xfs_bmbt_rec_host        *gotp,
        struct xfs_btree_cur            *cur,
        int                             *logflags,
-       enum shift_direction            direction)
+       enum shift_direction            direction,
+       struct xfs_btree_cur            *rcur)
 {
        struct xfs_ifork                *ifp;
        struct xfs_mount                *mp;
@@ -5545,7 +5766,7 @@ xfs_bmse_shift_one(
                                       offset_shift_fsb)) {
                        return xfs_bmse_merge(ip, whichfork, offset_shift_fsb,
                                              *current_ext, gotp, adj_irecp,
-                                             cur, logflags);
+                                             cur, logflags, rcur);
                }
        } else {
                startoff = got.br_startoff + offset_shift_fsb;
@@ -5582,6 +5803,10 @@ update_current_ext:
                (*current_ext)--;
        xfs_bmbt_set_startoff(gotp, startoff);
        *logflags |= XFS_ILOG_CORE;
+       error = xfs_rmap_slide(rcur, ip->i_ino, whichfork,
+                       &got, startoff - got.br_startoff);
+       if (error)
+               return error;
        if (!cur) {
                *logflags |= XFS_ILOG_DEXT;
                return 0;
@@ -5633,6 +5858,7 @@ xfs_bmap_shift_extents(
        int                             error = 0;
        int                             whichfork = XFS_DATA_FORK;
        int                             logflags = 0;
+       struct xfs_btree_cur            *rcur = NULL;
 
        if (unlikely(XFS_TEST_ERROR(
            (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
@@ -5721,9 +5947,14 @@ xfs_bmap_shift_extents(
        }
 
        while (nexts++ < num_exts) {
+               xfs_bmbt_get_all(gotp, &got);
+               error = alloc_rcur(mp, tp, &rcur, got.br_startblock);
+               if (error)
+                       return error;
+
                error = xfs_bmse_shift_one(ip, whichfork, offset_shift_fsb,
                                           &current_ext, gotp, cur, &logflags,
-                                          direction);
+                                          direction, rcur);
                if (error)
                        goto del_cursor;
                /*
@@ -5749,6 +5980,7 @@ xfs_bmap_shift_extents(
        }
 
 del_cursor:
+       free_rcur(&rcur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
        if (cur)
                xfs_btree_del_cursor(cur,
                        error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
@@ -5785,6 +6017,7 @@ xfs_bmap_split_extent_at(
        int                             error = 0;
        int                             logflags = 0;
        int                             i = 0;
+       struct xfs_btree_cur            *rcur = NULL;
 
        if (unlikely(XFS_TEST_ERROR(
            (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
@@ -5879,6 +6112,18 @@ xfs_bmap_split_extent_at(
                XFS_WANT_CORRUPTED_GOTO(mp, i == 1, del_cursor);
        }
 
+       /* update rmapbt */
+       error = alloc_rcur(mp, tp, &rcur, new.br_startblock);
+       if (error)
+               goto del_cursor;
+       error = xfs_rmap_resize(rcur, ip->i_ino, whichfork, &got, -gotblkcnt);
+       if (error)
+               goto del_cursor;
+       error = xfs_rmap_insert(rcur, ip->i_ino, whichfork, &new);
+       if (error)
+               goto del_cursor;
+       free_rcur(&rcur, XFS_BTREE_NOERROR);
+
        /*
         * Convert to a btree if necessary.
         */
@@ -5892,6 +6137,8 @@ xfs_bmap_split_extent_at(
        }
 
 del_cursor:
+       free_rcur(&rcur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+
        if (cur) {
                cur->bc_private.b.allocated = 0;
                xfs_btree_del_cursor(cur,
diff --git a/libxfs/xfs_bmap.h b/libxfs/xfs_bmap.h
index d3daf6d..da73d59 100644
--- a/libxfs/xfs_bmap.h
+++ b/libxfs/xfs_bmap.h
@@ -56,6 +56,7 @@ struct xfs_bmalloca {
        bool                    aeof;   /* allocated space at eof */
        bool                    conv;   /* overwriting unwritten extents */
        int                     flags;
+       struct xfs_btree_cur    *rcur;  /* rmap btree cursor */
 };
 
 /*
@@ -66,6 +67,7 @@ typedef struct xfs_bmap_free_item
 {
        xfs_fsblock_t           xbfi_startblock;/* starting fs block number */
        xfs_extlen_t            xbfi_blockcount;/* number of blocks in extent */
+       struct xfs_owner_info   xbfi_oinfo;     /* extent owner */
        struct xfs_bmap_free_item *xbfi_next;   /* link to next entry */
 } xfs_bmap_free_item_t;
 
@@ -183,7 +185,8 @@ void        xfs_bmap_trace_exlist(struct xfs_inode *ip, 
xfs_extnum_t cnt,
 int    xfs_bmap_add_attrfork(struct xfs_inode *ip, int size, int rsvd);
 void   xfs_bmap_local_to_extents_empty(struct xfs_inode *ip, int whichfork);
 void   xfs_bmap_add_free(struct xfs_mount *mp, struct xfs_bmap_free *flist,
-                         xfs_fsblock_t bno, xfs_filblks_t len);
+                         xfs_fsblock_t bno, xfs_filblks_t len,
+                         struct xfs_owner_info *oinfo);
 void   xfs_bmap_cancel(struct xfs_bmap_free *flist);
 int    xfs_bmap_finish(struct xfs_trans **tp, struct xfs_bmap_free *flist,
                        int *committed);
diff --git a/libxfs/xfs_bmap_btree.c b/libxfs/xfs_bmap_btree.c
index c928abf..02ebbae 100644
--- a/libxfs/xfs_bmap_btree.c
+++ b/libxfs/xfs_bmap_btree.c
@@ -443,7 +443,8 @@ xfs_bmbt_alloc_block(
        args.mp = cur->bc_mp;
        args.fsbno = cur->bc_private.b.firstblock;
        args.firstblock = args.fsbno;
-       args.owner = cur->bc_private.b.ip->i_ino;
+       XFS_RMAP_INO_BMBT_OWNER(&args.oinfo, cur->bc_private.b.ip->i_ino,
+                       cur->bc_private.b.whichfork);
 
        if (args.fsbno == NULLFSBLOCK) {
                args.fsbno = be64_to_cpu(start->l);
@@ -523,8 +524,10 @@ xfs_bmbt_free_block(
        struct xfs_inode        *ip = cur->bc_private.b.ip;
        struct xfs_trans        *tp = cur->bc_tp;
        xfs_fsblock_t           fsbno = XFS_DADDR_TO_FSB(mp, XFS_BUF_ADDR(bp));
+       struct xfs_owner_info   oinfo;
 
-       xfs_bmap_add_free(mp, cur->bc_private.b.flist, fsbno, 1);
+       XFS_RMAP_INO_BMBT_OWNER(&oinfo, ip->i_ino, cur->bc_private.b.whichfork);
+       xfs_bmap_add_free(mp, cur->bc_private.b.flist, fsbno, 1, &oinfo);
        ip->i_d.di_nblocks--;
 
        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
diff --git a/libxfs/xfs_format.h b/libxfs/xfs_format.h
index afa9885..ead7f30 100644
--- a/libxfs/xfs_format.h
+++ b/libxfs/xfs_format.h
@@ -1306,6 +1306,55 @@ typedef __be32 xfs_inobt_ptr_t;
 #define        XFS_RMAP_CRC_MAGIC      0x524d4233      /* 'RMB3' */
 
 /*
+ * Ownership info for an extent.  This is used to create reverse-mapping
+ * entries.
+ */
+#define XFS_RMAP_INO_ATTR_FORK (1)
+#define XFS_RMAP_BMBT_BLOCK    (2)
+struct xfs_owner_info {
+       uint64_t                oi_owner;
+       xfs_fileoff_t           oi_offset;
+       unsigned int            oi_flags;
+};
+
+static inline void
+XFS_RMAP_AG_OWNER(
+       struct xfs_owner_info   *oi,
+       uint64_t                owner)
+{
+       oi->oi_owner = owner;
+       oi->oi_offset = 0;
+       oi->oi_flags = 0;
+}
+
+static inline void
+XFS_RMAP_INO_BMBT_OWNER(
+       struct xfs_owner_info   *oi,
+       xfs_ino_t               ino,
+       int                     whichfork)
+{
+       oi->oi_owner = ino;
+       oi->oi_offset = 0;
+       oi->oi_flags = XFS_RMAP_BMBT_BLOCK;
+       if (whichfork == XFS_ATTR_FORK)
+               oi->oi_flags |= XFS_RMAP_INO_ATTR_FORK;
+}
+
+static inline void
+XFS_RMAP_INO_OWNER(
+       struct xfs_owner_info   *oi,
+       xfs_ino_t               ino,
+       int                     whichfork,
+       xfs_fileoff_t           offset)
+{
+       oi->oi_owner = ino;
+       oi->oi_offset = offset;
+       oi->oi_flags = 0;
+       if (whichfork == XFS_ATTR_FORK)
+               oi->oi_flags |= XFS_RMAP_INO_ATTR_FORK;
+}
+
+/*
  * Special owner types.
  *
  * Seeing as we only support up to 8EB, we have the upper bit of the owner 
field
@@ -1321,6 +1370,8 @@ typedef __be32 xfs_inobt_ptr_t;
 #define XFS_RMAP_OWN_INODES    (-7ULL) /* Inode chunk */
 #define XFS_RMAP_OWN_MIN       (-8ULL) /* guard */
 
+#define XFS_RMAP_NON_INODE_OWNER(owner)        (!!((owner) & (1ULL << 63)))
+
 /*
  * Data record structure
  */
@@ -1328,12 +1379,44 @@ struct xfs_rmap_rec {
        __be32          rm_startblock;  /* extent start block */
        __be32          rm_blockcount;  /* extent length */
        __be64          rm_owner;       /* extent owner */
+       __be64          rm_offset;      /* offset within the owner */
 };
 
+/*
+ * rmap btree record
+ *  rm_blockcount:31 is the unwritten extent flag (same as l0:63 in bmbt)
+ *  rm_blockcount:0-30 are the extent length
+ *  rm_offset:63 is the attribute fork flag
+ *  rm_offset:62 is the bmbt block flag
+ *  rm_offset:0-61 is the block offset within the inode
+ */
+#define XFS_RMAP_OFF_ATTR      ((__uint64_t)1ULL << 63)
+#define XFS_RMAP_OFF_BMBT      ((__uint64_t)1ULL << 62)
+#define XFS_RMAP_LEN_UNWRITTEN ((xfs_extlen_t)1U << 31)
+
+#define XFS_RMAP_OFF_MASK      ~(XFS_RMAP_OFF_ATTR | XFS_RMAP_OFF_BMBT)
+#define XFS_RMAP_LEN_MASK      ~XFS_RMAP_LEN_UNWRITTEN
+
+#define XFS_RMAP_OFF(off)              ((off) & XFS_RMAP_OFF_MASK)
+#define XFS_RMAP_LEN(len)              ((len) & XFS_RMAP_LEN_MASK)
+
+#define XFS_RMAP_IS_BMBT(off)          (!!((off) & XFS_RMAP_OFF_BMBT))
+#define XFS_RMAP_IS_ATTR_FORK(off)     (!!((off) & XFS_RMAP_OFF_ATTR))
+#define XFS_RMAP_IS_UNWRITTEN(len)     (!!((len) & XFS_RMAP_LEN_UNWRITTEN))
+
+#define RMAPBT_STARTBLOCK_BITLEN       32
+#define RMAPBT_EXNTFLAG_BITLEN         1
+#define RMAPBT_BLOCKCOUNT_BITLEN       31
+#define RMAPBT_OWNER_BITLEN            64
+#define RMAPBT_ATTRFLAG_BITLEN         1
+#define RMAPBT_BMBTFLAG_BITLEN         1
+#define RMAPBT_OFFSET_BITLEN           62
+
 struct xfs_rmap_irec {
        xfs_agblock_t   rm_startblock;  /* extent start block */
        xfs_extlen_t    rm_blockcount;  /* extent length */
        __uint64_t      rm_owner;       /* extent owner */
+       __uint64_t      rm_offset;      /* offset within the owner */
 };
 
 /*
@@ -1343,19 +1426,50 @@ struct xfs_rmap_irec {
  */
 struct xfs_rmap_key {
        __be32          rm_startblock;  /* extent start block */
+       __be64          rm_owner;       /* extent owner */
+       __be64          rm_offset;      /* offset within the owner */
 };
 
 /* btree pointer type */
 typedef __be32 xfs_rmap_ptr_t;
 
-/*
- * block numbers in the AG.
- */
 #define        XFS_RMAP_BLOCK(mp) \
        (xfs_sb_version_hasfinobt(&((mp)->m_sb)) ? \
         XFS_FIBT_BLOCK(mp) + 1 : \
         XFS_IBT_BLOCK(mp) + 1)
 
+static inline void
+xfs_owner_info_unpack(
+       struct xfs_owner_info   *oinfo,
+       uint64_t                *owner,
+       uint64_t                *offset)
+{
+       __uint64_t              r;
+
+       *owner = oinfo->oi_owner;
+       r = oinfo->oi_offset;
+       if (oinfo->oi_flags & XFS_RMAP_INO_ATTR_FORK)
+               r |= XFS_RMAP_OFF_ATTR;
+       if (oinfo->oi_flags & XFS_RMAP_BMBT_BLOCK)
+               r |= XFS_RMAP_OFF_BMBT;
+       *offset = r;
+}
+
+static inline void
+xfs_owner_info_pack(
+       struct xfs_owner_info   *oinfo,
+       uint64_t                owner,
+       uint64_t                offset)
+{
+       oinfo->oi_owner = owner;
+       oinfo->oi_offset = XFS_RMAP_OFF(offset);
+       oinfo->oi_flags = 0;
+       if (XFS_RMAP_IS_ATTR_FORK(offset))
+               oinfo->oi_flags |= XFS_RMAP_INO_ATTR_FORK;
+       if (XFS_RMAP_IS_BMBT(offset))
+               oinfo->oi_flags |= XFS_RMAP_BMBT_BLOCK;
+}
+
 /*
  * BMAP Btree format definitions
  *
diff --git a/libxfs/xfs_ialloc.c b/libxfs/xfs_ialloc.c
index b22ec2f..2d78962 100644
--- a/libxfs/xfs_ialloc.c
+++ b/libxfs/xfs_ialloc.c
@@ -608,6 +608,7 @@ xfs_ialloc_ag_alloc(
        args.tp = tp;
        args.mp = tp->t_mountp;
        args.fsbno = NULLFSBLOCK;
+       XFS_RMAP_AG_OWNER(&args.oinfo, XFS_RMAP_OWN_INODES);
 
 #ifdef DEBUG
        /* randomly do sparse inode allocations */
@@ -615,7 +616,6 @@ xfs_ialloc_ag_alloc(
            args.mp->m_ialloc_min_blks < args.mp->m_ialloc_blks)
                do_sparse = prandom_u32() & 1;
 #endif
-       args.owner = XFS_RMAP_OWN_INODES;
 
        /*
         * Locking will ensure that we don't have two callers in here
@@ -1819,13 +1819,15 @@ xfs_difree_inode_chunk(
        int             nextbit;
        xfs_agblock_t   agbno;
        int             contigblk;
+       struct xfs_owner_info   oinfo;
        DECLARE_BITMAP(holemask, XFS_INOBT_HOLEMASK_BITS);
+       XFS_RMAP_AG_OWNER(&oinfo, XFS_RMAP_OWN_INODES);
 
        if (!xfs_inobt_issparse(rec->ir_holemask)) {
                /* not sparse, calculate extent info directly */
                xfs_bmap_add_free(mp, flist, XFS_AGB_TO_FSB(mp, agno,
                                  XFS_AGINO_TO_AGBNO(mp, rec->ir_startino)),
-                                 mp->m_ialloc_blks);
+                                 mp->m_ialloc_blks, &oinfo);
                return;
        }
 
@@ -1869,7 +1871,7 @@ xfs_difree_inode_chunk(
                ASSERT(agbno % mp->m_sb.sb_spino_align == 0);
                ASSERT(contigblk % mp->m_sb.sb_spino_align == 0);
                xfs_bmap_add_free(mp, flist, XFS_AGB_TO_FSB(mp, agno, agbno),
-                                 contigblk);
+                                 contigblk, &oinfo);
 
                /* reset range to current bit and carry on... */
                startidx = endidx = nextbit;
diff --git a/libxfs/xfs_ialloc_btree.c b/libxfs/xfs_ialloc_btree.c
index ffeab1e..1d3b9e7 100644
--- a/libxfs/xfs_ialloc_btree.c
+++ b/libxfs/xfs_ialloc_btree.c
@@ -95,7 +95,7 @@ xfs_inobt_alloc_block(
        memset(&args, 0, sizeof(args));
        args.tp = cur->bc_tp;
        args.mp = cur->bc_mp;
-       args.owner = XFS_RMAP_OWN_INOBT;
+       XFS_RMAP_AG_OWNER(&args.oinfo, XFS_RMAP_OWN_INOBT);
        args.fsbno = XFS_AGB_TO_FSB(args.mp, cur->bc_private.a.agno, sbno);
        args.minlen = 1;
        args.maxlen = 1;
@@ -127,9 +127,11 @@ xfs_inobt_free_block(
 {
        xfs_fsblock_t           fsbno;
        int                     error;
+       struct xfs_owner_info   oinfo;
 
+       XFS_RMAP_AG_OWNER(&oinfo, XFS_RMAP_OWN_INOBT);
        fsbno = XFS_DADDR_TO_FSB(cur->bc_mp, XFS_BUF_ADDR(bp));
-       error = xfs_free_extent(cur->bc_tp, fsbno, 1);
+       error = xfs_free_extent(cur->bc_tp, fsbno, 1, &oinfo);
        if (error)
                return error;
 
diff --git a/libxfs/xfs_rmap.c b/libxfs/xfs_rmap.c
index b2a3330..12aac59 100644
--- a/libxfs/xfs_rmap.c
+++ b/libxfs/xfs_rmap.c
@@ -34,28 +34,49 @@
 #include "xfs_trans_space.h"
 #include "xfs_trace.h"
 
-
 /*
- * Lookup the first record less than or equal to [bno, len]
+ * Lookup the first record less than or equal to [bno, len, owner, offset]
  * in the btree given by cur.
  */
-STATIC int
+int
 xfs_rmap_lookup_le(
        struct xfs_btree_cur    *cur,
        xfs_agblock_t           bno,
        xfs_extlen_t            len,
        uint64_t                owner,
+       uint64_t                offset,
        int                     *stat)
 {
        cur->bc_rec.r.rm_startblock = bno;
        cur->bc_rec.r.rm_blockcount = len;
        cur->bc_rec.r.rm_owner = owner;
+       cur->bc_rec.r.rm_offset = offset;
        return xfs_btree_lookup(cur, XFS_LOOKUP_LE, stat);
 }
 
 /*
+ * Lookup the record exactly matching [bno, len, owner, offset]
+ * in the btree given by cur.
+ */
+int
+xfs_rmap_lookup_eq(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           bno,
+       xfs_extlen_t            len,
+       uint64_t                owner,
+       uint64_t                offset,
+       int                     *stat)
+{
+       cur->bc_rec.r.rm_startblock = bno;
+       cur->bc_rec.r.rm_blockcount = len;
+       cur->bc_rec.r.rm_owner = owner;
+       cur->bc_rec.r.rm_offset = offset;
+       return xfs_btree_lookup(cur, XFS_LOOKUP_EQ, stat);
+}
+
+/*
  * Update the record referred to by cur to the value given
- * by [bno, len, ref].
+ * by [bno, len, owner, offset].
  * This either works (return 0) or gets an EFSCORRUPTED error.
  */
 STATIC int
@@ -65,16 +86,79 @@ xfs_rmap_update(
 {
        union xfs_btree_rec     rec;
 
+       trace_xfs_rmapbt_update(cur->bc_mp, cur->bc_private.a.agno,
+                       irec->rm_startblock, irec->rm_blockcount,
+                       irec->rm_owner, irec->rm_offset);
+
        rec.rmap.rm_startblock = cpu_to_be32(irec->rm_startblock);
        rec.rmap.rm_blockcount = cpu_to_be32(irec->rm_blockcount);
        rec.rmap.rm_owner = cpu_to_be64(irec->rm_owner);
+       rec.rmap.rm_offset = cpu_to_be64(irec->rm_offset);
        return xfs_btree_update(cur, &rec);
 }
 
+int
+xfs_rmapbt_insert(
+       struct xfs_btree_cur    *rcur,
+       xfs_agblock_t           agbno,
+       xfs_extlen_t            len,
+       uint64_t                owner,
+       uint64_t                offset)
+{
+       int                     i;
+       int                     error;
+
+       trace_xfs_rmapbt_insert(rcur->bc_mp, rcur->bc_private.a.agno, agbno,
+                       len, owner, offset);
+
+       error = xfs_rmap_lookup_eq(rcur, agbno, len, owner, offset, &i);
+       if (error)
+               goto done;
+       XFS_WANT_CORRUPTED_GOTO(rcur->bc_mp, i == 0, done);
+
+       rcur->bc_rec.r.rm_startblock = agbno;
+       rcur->bc_rec.r.rm_blockcount = len;
+       rcur->bc_rec.r.rm_owner = owner;
+       rcur->bc_rec.r.rm_offset = offset;
+       error = xfs_btree_insert(rcur, &i);
+       if (error)
+               goto done;
+       XFS_WANT_CORRUPTED_GOTO(rcur->bc_mp, i == 1, done);
+done:
+       return error;
+}
+
+STATIC int
+xfs_rmapbt_delete(
+       struct xfs_btree_cur    *rcur,
+       xfs_agblock_t           agbno,
+       xfs_extlen_t            len,
+       uint64_t                owner,
+       uint64_t                offset)
+{
+       int                     i;
+       int                     error;
+
+       trace_xfs_rmapbt_delete(rcur->bc_mp, rcur->bc_private.a.agno, agbno,
+                       len, owner, offset);
+
+       error = xfs_rmap_lookup_eq(rcur, agbno, len, owner, offset, &i);
+       if (error)
+               goto done;
+       XFS_WANT_CORRUPTED_GOTO(rcur->bc_mp, i == 1, done);
+
+       error = xfs_btree_delete(rcur, &i);
+       if (error)
+               goto done;
+       XFS_WANT_CORRUPTED_GOTO(rcur->bc_mp, i == 1, done);
+done:
+       return error;
+}
+
 /*
  * Get the data from the pointed-to record.
  */
-STATIC int
+int
 xfs_rmap_get_rec(
        struct xfs_btree_cur    *cur,
        struct xfs_rmap_irec    *irec,
@@ -90,31 +174,27 @@ xfs_rmap_get_rec(
        irec->rm_startblock = be32_to_cpu(rec->rmap.rm_startblock);
        irec->rm_blockcount = be32_to_cpu(rec->rmap.rm_blockcount);
        irec->rm_owner = be64_to_cpu(rec->rmap.rm_owner);
+       irec->rm_offset = be64_to_cpu(rec->rmap.rm_offset);
        return 0;
 }
 
 /*
  * Find the extent in the rmap btree and remove it.
  *
- * The record we find should always span a range greater than or equal to the
- * the extent being freed. This makes the code simple as, in theory, we do not
- * have to handle ranges that are split across multiple records as extents that
- * result in bmap btree extent merges should also result in rmap btree extent
- * merges.  The owner field ensures we don't merge extents from different
- * structures into the same record, hence this property should always hold true
- * if we ensure that the rmap btree supports at least the same size maximum
- * extent as the bmap btree (2^21 blocks at present).
+ * The record we find should always be an exact match for the extent that we're
+ * looking for, since we insert them into the btree without modification.
  *
- * Complexity: when growing the filesystem, we "free" an extent when growing 
the
- * last AG. This extent is new space and so it is not tracked as used space in
- * the btree. The growfs code will pass in an owner of XFS_RMAP_OWN_NULL to
- * indicate that it expected that there is no owner of this extent. We verify
- * that - the extent lookup result in a record that does not overlap.
+ * Special Case #1: when growing the filesystem, we "free" an extent when
+ * growing the last AG. This extent is new space and so it is not tracked as
+ * used space in the btree. The growfs code will pass in an owner of
+ * XFS_RMAP_OWN_NULL to indicate that it expected that there is no owner of 
this
+ * extent. We verify that - the extent lookup result in a record that does not
+ * overlap.
  *
- * Complexity #2: EFIs do not record the owner of the extent, so when 
recovering
- * EFIs from the log we pass in XFS_RMAP_OWN_UNKNOWN to tell the rmap btree to
- * ignore the owner (i.e. wildcard match) so we don't trigger corruption checks
- * during log recovery.
+ * Special Case #2: EFIs do not record the owner of the extent, so when
+ * recovering EFIs from the log we pass in XFS_RMAP_OWN_UNKNOWN to tell the 
rmap
+ * btree to ignore the owner (i.e. wildcard match) so we don't trigger
+ * corruption checks during log recovery.
  */
 int
 xfs_rmap_free(
@@ -123,29 +203,32 @@ xfs_rmap_free(
        xfs_agnumber_t          agno,
        xfs_agblock_t           bno,
        xfs_extlen_t            len,
-       uint64_t                owner)
+       struct xfs_owner_info   *oinfo)
 {
-       struct xfs_btree_cur    *cur;
        struct xfs_mount        *mp = tp->t_mountp;
+       struct xfs_btree_cur    *cur;
        struct xfs_rmap_irec    ltrec;
-       int                     error;
+       uint64_t                ltoff;
+       int                     error = 0;
        int                     i;
+       uint64_t                owner;
+       uint64_t                offset;
 
-       /*
-        * if rmap btree is not supported, then just return success without
-        * doing anything.
-        */
-       if (!xfs_sb_version_hasrmapbt(&tp->t_mountp->m_sb))
+       if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
                return 0;
 
-       trace_xfs_rmap_free_extent(mp, agno, bno, len, owner);
+       trace_xfs_rmap_free_extent(mp, agno, bno, len, oinfo);
        cur = xfs_rmapbt_init_cursor(mp, tp, agbp, agno);
 
+       xfs_owner_info_unpack(oinfo, &owner, &offset);
+       ASSERT(XFS_RMAP_NON_INODE_OWNER(owner) || XFS_RMAP_IS_BMBT(offset));
+       ltoff = ltrec.rm_offset & ~XFS_RMAP_OFF_BMBT;
        /*
-        * We always have a left record because there's a static record
-        * for the AG headers at rm_startblock == 0.
+        * We should always have a left record because there's a static record
+        * for the AG headers at rm_startblock == 0 created by mkfs/growfs that
+        * will not ever be removed from the tree.
         */
-       error = xfs_rmap_lookup_le(cur, bno, len, owner, &i);
+       error = xfs_rmap_lookup_le(cur, bno, len, owner, offset, &i);
        if (error)
                goto out_error;
        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
@@ -155,17 +238,18 @@ xfs_rmap_free(
                goto out_error;
        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
 
-       /* special growfs case - bno is beyond last record */
+       /*
+        * For growfs, the incoming extent must be beyond the left record we
+        * just found as it is new space and won't be used by anyone. This is
+        * just a corruption check as we don't actually do anything with this
+        * extent.
+        */
        if (owner == XFS_RMAP_OWN_NULL) {
                XFS_WANT_CORRUPTED_GOTO(mp, bno > ltrec.rm_startblock +
                                                ltrec.rm_blockcount, out_error);
                goto out_done;
        }
 
-       /* make sure the extent we found covers the entire freeing range. */
-       XFS_WANT_CORRUPTED_GOTO(mp, ltrec.rm_startblock <= bno, out_error);
-       XFS_WANT_CORRUPTED_GOTO(mp, ltrec.rm_blockcount >= len, out_error);
-
 /*
        if (owner != ltrec.rm_owner ||
            bno > ltrec.rm_startblock + ltrec.rm_blockcount)
@@ -173,16 +257,36 @@ xfs_rmap_free(
        //printk("rmfree  ag %d bno 0x%x/0x%x/0x%llx, ltrec 0x%x/0x%x/0x%llx\n",
        //              agno, bno, len, owner, ltrec.rm_startblock,
        //              ltrec.rm_blockcount, ltrec.rm_owner);
-       XFS_WANT_CORRUPTED_GOTO(mp, bno <= ltrec.rm_startblock + 
ltrec.rm_blockcount,
-                               out_error);
+
+       /* make sure the extent we found covers the entire freeing range. */
+       XFS_WANT_CORRUPTED_GOTO(mp, !XFS_RMAP_IS_UNWRITTEN(ltrec.rm_blockcount),
+               out_error);
+       XFS_WANT_CORRUPTED_GOTO(mp, ltrec.rm_startblock <= bno &&
+               ltrec.rm_startblock + XFS_RMAP_LEN(ltrec.rm_blockcount) >=
+               bno + len, out_error);
+
+       /* make sure the owner matches what we expect to find in the tree */
        XFS_WANT_CORRUPTED_GOTO(mp, owner == ltrec.rm_owner ||
-                               (owner < XFS_RMAP_OWN_NULL &&
-                                owner >= XFS_RMAP_OWN_MIN), out_error);
+                                   XFS_RMAP_NON_INODE_OWNER(owner), out_error);
+
+       /* check the offset, if necessary */
+       if (!XFS_RMAP_NON_INODE_OWNER(owner)) {
+               if (XFS_RMAP_IS_BMBT(offset)) {
+                       XFS_WANT_CORRUPTED_GOTO(mp,
+                                       XFS_RMAP_IS_BMBT(ltrec.rm_offset),
+                                       out_error);
+               } else {
+                       XFS_WANT_CORRUPTED_GOTO(mp,
+                                       ltrec.rm_offset <= offset, out_error);
+                       XFS_WANT_CORRUPTED_GOTO(mp,
+                                       offset <= ltoff + ltrec.rm_blockcount,
+                                       out_error);
+               }
+       }
 
-       /* exact match is easy */
        if (ltrec.rm_startblock == bno && ltrec.rm_blockcount == len) {
        //printk("remove exact\n");
-               /* remove extent from rmap tree */
+               /* exact match, simply remove the record from rmap tree */
                error = xfs_btree_delete(cur, &i);
                if (error)
                        goto out_error;
@@ -190,7 +294,8 @@ xfs_rmap_free(
        } else if (ltrec.rm_startblock == bno) {
        //printk("remove left\n");
                /*
-                * overlap left hand side of extent
+                * overlap left hand side of extent: move the start, trim the
+                * length and update the current record.
                 *
                 *       ltbno                ltlen
                 * Orig:    |oooooooooooooooooooo|
@@ -206,7 +311,8 @@ xfs_rmap_free(
        } else if (ltrec.rm_startblock + ltrec.rm_blockcount == bno + len) {
        //printk("remove right\n");
                /*
-                * overlap right hand side of extent
+                * overlap right hand side of extent: trim the length and update
+                * the current record.
                 *
                 *       ltbno                ltlen
                 * Orig:    |oooooooooooooooooooo|
@@ -219,8 +325,12 @@ xfs_rmap_free(
                if (error)
                        goto out_error;
        } else {
+
                /*
-                * overlap middle of extent
+                * overlap middle of extent: trim the length of the existing
+                * record to the length of the new left-extent size, increment
+                * the insertion position so we can insert a new record
+                * containing the remaining right-extent space.
                 *
                 *       ltbno                ltlen
                 * Orig:    |oooooooooooooooooooo|
@@ -231,7 +341,7 @@ xfs_rmap_free(
                xfs_extlen_t    orig_len = ltrec.rm_blockcount;
        //printk("remove middle\n");
 
-               ltrec.rm_blockcount = bno - ltrec.rm_startblock;;
+               ltrec.rm_blockcount = bno - ltrec.rm_startblock;
                error = xfs_rmap_update(cur, &ltrec);
                if (error)
                        goto out_error;
@@ -244,33 +354,52 @@ xfs_rmap_free(
                cur->bc_rec.r.rm_blockcount = orig_len - len -
                                                     ltrec.rm_blockcount;
                cur->bc_rec.r.rm_owner = ltrec.rm_owner;
+               cur->bc_rec.r.rm_offset = offset;
                error = xfs_btree_insert(cur, &i);
                if (error)
                        goto out_error;
        }
 
 out_done:
-       trace_xfs_rmap_free_extent_done(mp, agno, bno, len, owner);
+       trace_xfs_rmap_free_extent_done(mp, agno, bno, len, oinfo);
        xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
        return 0;
 
 out_error:
-       trace_xfs_rmap_free_extent_error(mp, agno, bno, len, owner);
+       trace_xfs_rmap_free_extent_error(mp, agno, bno, len, oinfo);
        xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
        return error;
 }
 
 /*
- * When we allocate a new block, the first thing we do is add a reference to 
the
- * extent in the rmap btree. This is how we track the owner of the extent and 
th
- * enumber of references to it.
- *
- * Initially, we do not have shared extents, and so the extent can only have a
- * single reference count and owner. This makes the initial implementation 
easy,
- * but does not allow us to use the rmap tree for tracking reflink shared 
files.
- * Hence the initial implementation is simply a lookup to find the place to
- * insert (and checking we don't find a duplicate/overlap) and then insertng 
the
- * appropriate record.
+ * A mergeable rmap should have the same owner, cannot be unwritten, and
+ * must be a bmbt rmap if we're asking about a bmbt rmap.
+ */
+static bool
+is_mergeable_rmap(
+       struct xfs_rmap_irec    *irec,
+       uint64_t                owner,
+       uint64_t                offset)
+{
+       if (irec->rm_owner == XFS_RMAP_OWN_NULL)
+               return false;
+       if (irec->rm_owner != owner)
+               return false;
+       if (XFS_RMAP_IS_UNWRITTEN(irec->rm_blockcount))
+               return false;
+       if (XFS_RMAP_IS_ATTR_FORK(offset) ^
+           XFS_RMAP_IS_ATTR_FORK(irec->rm_offset))
+               return false;
+       if (XFS_RMAP_IS_BMBT(offset) ^ XFS_RMAP_IS_BMBT(irec->rm_offset))
+               return false;
+       return true;
+}
+
+/*
+ * When we allocate a new block, the first thing we do is add a reference to
+ * the extent in the rmap btree. This takes the form of a [agbno, length,
+ * owner, offset] record.  Flags are encoded in the high bits of the offset
+ * field.
  */
 int
 xfs_rmap_alloc(
@@ -279,31 +408,32 @@ xfs_rmap_alloc(
        xfs_agnumber_t          agno,
        xfs_agblock_t           bno,
        xfs_extlen_t            len,
-       uint64_t                owner)
+       struct xfs_owner_info   *oinfo)
 {
-       struct xfs_btree_cur    *cur;
        struct xfs_mount        *mp = tp->t_mountp;
+       struct xfs_btree_cur    *cur;
        struct xfs_rmap_irec    ltrec;
        struct xfs_rmap_irec    gtrec;
        int                     have_gt;
-       int                     error;
+       int                     error = 0;
        int                     i;
+       uint64_t                owner;
+       uint64_t                offset;
 
-       /*
-        * if rmap btree is not supported, then just return success without
-        * doing anything.
-        */
-       if (!xfs_sb_version_hasrmapbt(&tp->t_mountp->m_sb))
+       if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
                return 0;
 
-       trace_xfs_rmap_alloc_extent(mp, agno, bno, len, owner);
+       trace_xfs_rmap_alloc_extent(mp, agno, bno, len, oinfo);
        cur = xfs_rmapbt_init_cursor(mp, tp, agbp, agno);
 
+       xfs_owner_info_unpack(oinfo, &owner, &offset);
+       ASSERT(XFS_RMAP_NON_INODE_OWNER(owner) || XFS_RMAP_IS_BMBT(offset));
        /*
-        * chekc to see if we find an existing record for this extent rather
-        * than just the location for insert.
+        * For the initial lookup, look for and exact match or the left-adjacent
+        * record for our insertion point. This will also give us the record for
+        * start block contiguity tests.
         */
-       error = xfs_rmap_lookup_le(cur, bno, len, owner, &i);
+       error = xfs_rmap_lookup_le(cur, bno, len, owner, offset, &i);
        if (error)
                goto out_error;
        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
@@ -315,10 +445,18 @@ xfs_rmap_alloc(
        //printk("rmalloc ag %d bno 0x%x/0x%x/0x%llx, ltrec 0x%x/0x%x/0x%llx\n",
        //              agno, bno, len, owner, ltrec.rm_startblock,
        //              ltrec.rm_blockcount, ltrec.rm_owner);
+       if (!is_mergeable_rmap(&ltrec, owner, offset))
+               ltrec.rm_owner = XFS_RMAP_OWN_NULL;
 
-       XFS_WANT_CORRUPTED_GOTO(mp, ltrec.rm_startblock + ltrec.rm_blockcount 
<= bno,
-                               out_error);
+       XFS_WANT_CORRUPTED_GOTO(mp,
+               ltrec.rm_owner == XFS_RMAP_OWN_NULL ||
+               ltrec.rm_startblock + ltrec.rm_blockcount <= bno, out_error);
 
+       /*
+        * Increment the cursor to see if we have a right-adjacent record to our
+        * insertion point. This will give us the record for end block
+        * contiguity tests.
+        */
        error = xfs_btree_increment(cur, 0, &have_gt);
        if (error)
                goto out_error;
@@ -335,12 +473,17 @@ xfs_rmap_alloc(
        } else {
                gtrec.rm_owner = XFS_RMAP_OWN_NULL;
        }
+       if (!is_mergeable_rmap(&gtrec, owner, offset))
+               gtrec.rm_owner = XFS_RMAP_OWN_NULL;
 
-       /* cursor currently points one record past ltrec */
+       /*
+        * Note: cursor currently points one record to the right of ltrec, even
+        * if there is no record in the tree to the right.
+        */
        if (ltrec.rm_owner == owner &&
            ltrec.rm_startblock + ltrec.rm_blockcount == bno) {
                /*
-                * left edge contiguous
+                * left edge contiguous, merge into left record.
                 *
                 *       ltbno     ltlen
                 * orig:   |ooooooooo|
@@ -354,7 +497,8 @@ xfs_rmap_alloc(
                    bno + len == gtrec.rm_startblock) {
                        //printk("add middle\n");
                        /*
-                        * right edge also contiguous
+                        * right edge also contiguous, delete right record
+                        * and merge into left record.
                         *
                         *       ltbno     ltlen    gtbno     gtlen
                         * orig:   |ooooooooo|         |ooooooooo|
@@ -368,6 +512,7 @@ xfs_rmap_alloc(
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
                }
 
+               /* point the cursor back to the left record and update */
                error = xfs_btree_decrement(cur, 0, &have_gt);
                if (error)
                        goto out_error;
@@ -377,7 +522,7 @@ xfs_rmap_alloc(
        } else if (gtrec.rm_owner == owner &&
                   bno + len == gtrec.rm_startblock) {
                /*
-                * right edge contiguous
+                * right edge contiguous, merge into right record.
                 *
                 *                 gtbno     gtlen
                 * Orig:             |ooooooooo|
@@ -393,21 +538,322 @@ xfs_rmap_alloc(
                        goto out_error;
        } else {
                //printk("add no match\n");
-               /* no contiguous edge with identical owner */
+               /*
+                * no contiguous edge with identical owner, insert
+                * new record at current cursor position.
+                */
                cur->bc_rec.r.rm_startblock = bno;
                cur->bc_rec.r.rm_blockcount = len;
                cur->bc_rec.r.rm_owner = owner;
+               cur->bc_rec.r.rm_offset = offset;
                error = xfs_btree_insert(cur, &i);
                if (error)
                        goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
        }
 
-       trace_xfs_rmap_alloc_extent_done(mp, agno, bno, len, owner);
+       trace_xfs_rmap_alloc_extent_done(mp, agno, bno, len, oinfo);
        xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
        return 0;
 
 out_error:
-       trace_xfs_rmap_alloc_extent_error(mp, agno, bno, len, owner);
+       trace_xfs_rmap_alloc_extent_error(mp, agno, bno, len, oinfo);
        xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
        return error;
 }
+
+/* Encode logical offset for a rmapbt record */
+STATIC uint64_t
+b2r_off(
+       int             whichfork,
+       xfs_fileoff_t   off)
+{
+       uint64_t        x;
+
+       x = off;
+       if (whichfork == XFS_ATTR_FORK)
+               x |= XFS_RMAP_OFF_ATTR;
+       return x;
+}
+
+/* Encode blockcount for a rmapbt record */
+STATIC xfs_extlen_t
+b2r_len(
+       struct xfs_bmbt_irec    *irec)
+{
+       xfs_extlen_t            x;
+
+       x = irec->br_blockcount;
+       if (irec->br_state == XFS_EXT_UNWRITTEN)
+               x |= XFS_RMAP_LEN_UNWRITTEN;
+       return x;
+}
+
+/* Combine two adjacent rmap extents */
+int
+xfs_rmap_combine(
+       struct xfs_btree_cur    *rcur,
+       xfs_ino_t               ino,
+       int                     whichfork,
+       struct xfs_bmbt_irec    *LEFT,
+       struct xfs_bmbt_irec    *RIGHT,
+       struct xfs_bmbt_irec    *PREV)
+{
+       int                     error;
+
+       if (!rcur)
+               return 0;
+
+       trace_xfs_rmap_combine(rcur->bc_mp, rcur->bc_private.a.agno, ino,
+                       whichfork, LEFT, PREV, RIGHT);
+
+       /* Delete right rmap */
+       error = xfs_rmapbt_delete(rcur,
+                       XFS_FSB_TO_AGBNO(rcur->bc_mp, RIGHT->br_startblock),
+                       b2r_len(RIGHT), ino,
+                       b2r_off(whichfork, RIGHT->br_startoff));
+       if (error)
+               goto done;
+
+       /* Delete prev rmap */
+       if (!isnullstartblock(PREV->br_startblock)) {
+               error = xfs_rmapbt_delete(rcur,
+                               XFS_FSB_TO_AGBNO(rcur->bc_mp,
+                                               PREV->br_startblock),
+                               b2r_len(PREV), ino,
+                               b2r_off(whichfork, PREV->br_startoff));
+               if (error)
+                       goto done;
+       }
+
+       /* Enlarge left rmap */
+       return xfs_rmap_resize(rcur, ino, whichfork, LEFT,
+                       PREV->br_blockcount + RIGHT->br_blockcount);
+done:
+       return error;
+}
+
+/* Extend a left rmap extent */
+int
+xfs_rmap_lcombine(
+       struct xfs_btree_cur    *rcur,
+       xfs_ino_t               ino,
+       int                     whichfork,
+       struct xfs_bmbt_irec    *LEFT,
+       struct xfs_bmbt_irec    *PREV)
+{
+       int                     error;
+
+       if (!rcur)
+               return 0;
+
+       trace_xfs_rmap_lcombine(rcur->bc_mp, rcur->bc_private.a.agno, ino,
+                       whichfork, LEFT, PREV);
+
+       /* Delete prev rmap */
+       if (!isnullstartblock(PREV->br_startblock)) {
+               error = xfs_rmapbt_delete(rcur,
+                               XFS_FSB_TO_AGBNO(rcur->bc_mp,
+                                               PREV->br_startblock),
+                               b2r_len(PREV), ino,
+                               b2r_off(whichfork, PREV->br_startoff));
+               if (error)
+                       goto done;
+       }
+
+       /* Enlarge left rmap */
+       return xfs_rmap_resize(rcur, ino, whichfork, LEFT, PREV->br_blockcount);
+done:
+       return error;
+}
+
+/* Extend a right rmap extent */
+int
+xfs_rmap_rcombine(
+       struct xfs_btree_cur    *rcur,
+       xfs_ino_t               ino,
+       int                     whichfork,
+       struct xfs_bmbt_irec    *RIGHT,
+       struct xfs_bmbt_irec    *PREV,
+       struct xfs_bmbt_irec    *new)
+{
+       int                     error;
+
+       if (!rcur)
+               return 0;
+       ASSERT(PREV->br_startoff == new->br_startoff);
+
+       trace_xfs_rmap_rcombine(rcur->bc_mp, rcur->bc_private.a.agno, ino,
+                       whichfork, RIGHT, PREV);
+
+       /* Delete prev rmap */
+       if (!isnullstartblock(PREV->br_startblock)) {
+               error = xfs_rmapbt_delete(rcur,
+                               XFS_FSB_TO_AGBNO(rcur->bc_mp,
+                                               PREV->br_startblock),
+                               b2r_len(PREV), ino,
+                               b2r_off(whichfork, PREV->br_startoff));
+               if (error)
+                       goto done;
+       }
+
+       /* Enlarge right rmap */
+       return xfs_rmap_resize(rcur, ino, whichfork, RIGHT,
+                       -PREV->br_blockcount);
+done:
+       return error;
+}
+
+/* Insert a rmap extent */
+int
+xfs_rmap_insert(
+       struct xfs_btree_cur    *rcur,
+       xfs_ino_t               ino,
+       int                     whichfork,
+       struct xfs_bmbt_irec    *new)
+{
+       if (!rcur)
+               return 0;
+
+       trace_xfs_rmap_insert(rcur->bc_mp, rcur->bc_private.a.agno, ino,
+                       whichfork, new);
+
+       return xfs_rmapbt_insert(rcur,
+                       XFS_FSB_TO_AGBNO(rcur->bc_mp, new->br_startblock),
+                       b2r_len(new), ino,
+                       b2r_off(whichfork, new->br_startoff));
+}
+
+/* Delete a rmap extent */
+int
+xfs_rmap_delete(
+       struct xfs_btree_cur    *rcur,
+       xfs_ino_t               ino,
+       int                     whichfork,
+       struct xfs_bmbt_irec    *new)
+{
+       if (!rcur)
+               return 0;
+
+       trace_xfs_rmap_delete(rcur->bc_mp, rcur->bc_private.a.agno, ino,
+                       whichfork, new);
+
+       return xfs_rmapbt_delete(rcur,
+                       XFS_FSB_TO_AGBNO(rcur->bc_mp, new->br_startblock),
+                       b2r_len(new), ino,
+                       b2r_off(whichfork, new->br_startoff));
+}
+
+/* Change the start of an rmap */
+int
+xfs_rmap_move(
+       struct xfs_btree_cur    *rcur,
+       xfs_ino_t               ino,
+       int                     whichfork,
+       struct xfs_bmbt_irec    *PREV,
+       long                    start_adj)
+{
+       int                     error;
+       struct xfs_bmbt_irec    irec;
+
+       if (!rcur)
+               return 0;
+
+       trace_xfs_rmap_move(rcur->bc_mp, rcur->bc_private.a.agno, ino,
+                       whichfork, PREV, start_adj);
+
+       /* Delete prev rmap */
+       error = xfs_rmapbt_delete(rcur,
+                       XFS_FSB_TO_AGBNO(rcur->bc_mp, PREV->br_startblock),
+                       b2r_len(PREV), ino,
+                       b2r_off(whichfork, PREV->br_startoff));
+       if (error)
+               goto done;
+
+       /* Re-add rmap with new start */
+       irec = *PREV;
+       irec.br_startblock += start_adj;
+       irec.br_startoff += start_adj;
+       irec.br_blockcount -= start_adj;
+       return xfs_rmapbt_insert(rcur,
+                       XFS_FSB_TO_AGBNO(rcur->bc_mp, irec.br_startblock),
+                       b2r_len(&irec), ino,
+                       b2r_off(whichfork, irec.br_startoff));
+done:
+       return error;
+}
+
+/* Change the logical offset of an rmap */
+int
+xfs_rmap_slide(
+       struct xfs_btree_cur    *rcur,
+       xfs_ino_t               ino,
+       int                     whichfork,
+       struct xfs_bmbt_irec    *PREV,
+       long                    start_adj)
+{
+       int                     error;
+
+       if (!rcur)
+               return 0;
+
+       trace_xfs_rmap_slide(rcur->bc_mp, rcur->bc_private.a.agno, ino,
+                       whichfork, PREV, start_adj);
+
+       /* Delete prev rmap */
+       error = xfs_rmapbt_delete(rcur,
+                       XFS_FSB_TO_AGBNO(rcur->bc_mp, PREV->br_startblock),
+                       b2r_len(PREV), ino,
+                       b2r_off(whichfork, PREV->br_startoff));
+       if (error)
+               goto done;
+
+       /* Re-add rmap with new logical offset */
+       return xfs_rmapbt_insert(rcur,
+                       XFS_FSB_TO_AGBNO(rcur->bc_mp, PREV->br_startblock),
+                       b2r_len(PREV), ino,
+                       b2r_off(whichfork, PREV->br_startoff + start_adj));
+done:
+       return error;
+}
+
+/* Change the size of an rmap */
+int
+xfs_rmap_resize(
+       struct xfs_btree_cur    *rcur,
+       xfs_ino_t               ino,
+       int                     whichfork,
+       struct xfs_bmbt_irec    *PREV,
+       long                    size_adj)
+{
+       int                     i;
+       int                     error;
+       struct xfs_bmbt_irec    irec;
+       struct xfs_rmap_irec    rrec;
+
+       if (!rcur)
+               return 0;
+
+       trace_xfs_rmap_resize(rcur->bc_mp, rcur->bc_private.a.agno, ino,
+                       whichfork, PREV, size_adj);
+
+       error = xfs_rmap_lookup_eq(rcur,
+                       XFS_FSB_TO_AGBNO(rcur->bc_mp, PREV->br_startblock),
+                       b2r_len(PREV), ino,
+                       b2r_off(whichfork, PREV->br_startoff), &i);
+       if (error)
+               goto done;
+       XFS_WANT_CORRUPTED_GOTO(rcur->bc_mp, i == 1, done);
+       error = xfs_rmap_get_rec(rcur, &rrec, &i);
+       if (error)
+               goto done;
+       XFS_WANT_CORRUPTED_GOTO(rcur->bc_mp, i == 1, done);
+       irec = *PREV;
+       irec.br_blockcount += size_adj;
+       rrec.rm_blockcount = b2r_len(&irec);
+       error = xfs_rmap_update(rcur, &rrec);
+       if (error)
+               goto done;
+done:
+       return error;
+}
diff --git a/libxfs/xfs_rmap_btree.c b/libxfs/xfs_rmap_btree.c
index ed1792d..c02a46f 100644
--- a/libxfs/xfs_rmap_btree.c
+++ b/libxfs/xfs_rmap_btree.c
@@ -36,37 +36,29 @@
 /*
  * Reverse map btree.
  *
- * This is a per-ag tree used to track the owner of a given extent. Owner
- * records are inserted when an extent is allocated, and removed when an extent
- * is freed. For existing filesystems, there can only be one owner of an 
extent,
- * usually an inode or some other metadata structure like a AG btree.
- *
- * Initial thoughts are that the
- * value of the owner field needs external flags to define what it means, and
- * hence we need a flags field in the record. This means the record is going to
- * be larger than 16 bytes (agbno,len,owner = 16 bytes), so maybe this isn't 
the
- * best idea. Initially just implement the owner field - we can probably steal
- * bits from the extent length field for type descriptors given that MAXEXTLEN
- * is only 21 bits if we want to store the type as well. Keep in mind that if 
we
- * want to do this there are still restrictions on the length of extents we
- * track in the rmap btree (see comments on xfs_rmap_free()).
+ * This is a per-ag tree used to track the owner(s) of a given extent. With
+ * reflink it is possible for there to be multiple owners, which is a departure
+ * from classic XFS. Owner records for data extents are inserted when the
+ * extent is mapped and removed when an extent is unmapped.  Owner records for
+ * all other block types (i.e. metadata) are inserted when an extent is
+ * allocated and removed when an extent is freed. There can only be one owner
+ * of a metadata extent, usually an inode or some other metadata structure like
+ * an AG btree.
  *
  * The rmap btree is part of the free space management, so blocks for the tree
  * are sourced from the agfl. Hence we need transaction reservation support for
  * this tree so that the freelist is always large enough. This also impacts on
  * the minimum space we need to leave free in the AG.
  *
- * The tree is ordered by block number - there's no need to order/search by
- * extent size for  online updating/management of the tree, and the reverse
- * lookups are going to be "who owns this block" and so are by-block ordering 
is
- * perfect for this.
- *
- * XXX: open question is how to handle blocks that are owned by the freespace
- * tree blocks. Right now they will be classified when they are moved to the
- * freelist or removed from the freelist. i.e. the extent allocation/freeing
- * will mark the extents allocated as owned by the AG.
+ * The tree is ordered by [ag block, owner, offset]. This is a large key size,
+ * but it is the only way to enforce unique keys when a block can be owned by
+ * multiple files at any offset. There's no need to order/search by extent
+ * size for online updating/management of the tree. It is intended that most
+ * reverse lookups will be to find the owner(s) of a particular block, or to
+ * try to recover tree and file data from corrupt primary metadata.
  */
-STATIC struct xfs_btree_cur *
+
+static struct xfs_btree_cur *
 xfs_rmapbt_dup_cursor(
        struct xfs_btree_cur    *cur)
 {
@@ -177,6 +169,8 @@ xfs_rmapbt_init_key_from_rec(
        union xfs_btree_rec     *rec)
 {
        key->rmap.rm_startblock = rec->rmap.rm_startblock;
+       key->rmap.rm_owner = rec->rmap.rm_owner;
+       key->rmap.rm_offset = rec->rmap.rm_offset;
 }
 
 STATIC void
@@ -185,6 +179,8 @@ xfs_rmapbt_init_rec_from_key(
        union xfs_btree_rec     *rec)
 {
        rec->rmap.rm_startblock = key->rmap.rm_startblock;
+       rec->rmap.rm_owner = key->rmap.rm_owner;
+       rec->rmap.rm_offset = key->rmap.rm_offset;
 }
 
 STATIC void
@@ -195,6 +191,7 @@ xfs_rmapbt_init_rec_from_cur(
        rec->rmap.rm_startblock = cpu_to_be32(cur->bc_rec.r.rm_startblock);
        rec->rmap.rm_blockcount = cpu_to_be32(cur->bc_rec.r.rm_blockcount);
        rec->rmap.rm_owner = cpu_to_be64(cur->bc_rec.r.rm_owner);
+       rec->rmap.rm_offset = cpu_to_be64(cur->bc_rec.r.rm_offset);
 }
 
 STATIC void
@@ -217,8 +214,16 @@ xfs_rmapbt_key_diff(
 {
        struct xfs_rmap_irec    *rec = &cur->bc_rec.r;
        struct xfs_rmap_key     *kp = &key->rmap;
-
-       return (__int64_t)be32_to_cpu(kp->rm_startblock) - rec->rm_startblock;
+       __int64_t               d;
+
+       d = (__int64_t)be32_to_cpu(kp->rm_startblock) - rec->rm_startblock;
+       if (d)
+               return d;
+       d = (__int64_t)be64_to_cpu(kp->rm_owner) - rec->rm_owner;
+       if (d)
+               return d;
+       d = (__int64_t)be64_to_cpu(kp->rm_offset) - rec->rm_offset;
+       return d;
 }
 
 static bool
@@ -242,7 +247,7 @@ xfs_rmapbt_verify(
         * from the on disk AGF. Again, we can only check against maximum limits
         * in this case.
         */
-       if (block->bb_magic!= cpu_to_be32(XFS_RMAP_CRC_MAGIC))
+       if (block->bb_magic != cpu_to_be32(XFS_RMAP_CRC_MAGIC))
                return false;
 
        if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
@@ -312,7 +317,6 @@ const struct xfs_buf_ops xfs_rmapbt_buf_ops = {
        .verify_write = xfs_rmapbt_write_verify,
 };
 
-
 #if defined(DEBUG) || defined(XFS_WARN)
 STATIC int
 xfs_rmapbt_keys_inorder(
@@ -320,8 +324,16 @@ xfs_rmapbt_keys_inorder(
        union xfs_btree_key     *k1,
        union xfs_btree_key     *k2)
 {
-       return be32_to_cpu(k1->rmap.rm_startblock) <
-              be32_to_cpu(k2->rmap.rm_startblock);
+       if (be32_to_cpu(k1->rmap.rm_startblock) <
+           be32_to_cpu(k2->rmap.rm_startblock))
+               return 1;
+       if (be64_to_cpu(k1->rmap.rm_owner) <
+           be64_to_cpu(k2->rmap.rm_owner))
+               return 1;
+       if (be64_to_cpu(k1->rmap.rm_offset) <=
+           be64_to_cpu(k2->rmap.rm_offset))
+               return 1;
+       return 0;
 }
 
 STATIC int
@@ -330,9 +342,16 @@ xfs_rmapbt_recs_inorder(
        union xfs_btree_rec     *r1,
        union xfs_btree_rec     *r2)
 {
-       return be32_to_cpu(r1->rmap.rm_startblock) +
-               be32_to_cpu(r1->rmap.rm_blockcount) <=
-               be32_to_cpu(r2->rmap.rm_startblock);
+       if (be32_to_cpu(r1->rmap.rm_startblock) <
+           be32_to_cpu(r2->rmap.rm_startblock))
+               return 1;
+       if (be64_to_cpu(r1->rmap.rm_offset) <
+           be64_to_cpu(r2->rmap.rm_offset))
+               return 1;
+       if (be64_to_cpu(r1->rmap.rm_owner) <=
+           be64_to_cpu(r2->rmap.rm_owner))
+               return 1;
+       return 0;
 }
 #endif /* DEBUG */
 
diff --git a/libxfs/xfs_rmap_btree.h b/libxfs/xfs_rmap_btree.h
index 9ad65e5..0131d9a 100644
--- a/libxfs/xfs_rmap_btree.h
+++ b/libxfs/xfs_rmap_btree.h
@@ -18,10 +18,6 @@
 #ifndef __XFS_RMAP_BTREE_H__
 #define        __XFS_RMAP_BTREE_H__
 
-/*
- * Freespace on-disk structures
- */
-
 struct xfs_buf;
 struct xfs_btree_cur;
 struct xfs_mount;
@@ -55,11 +51,41 @@ struct xfs_btree_cur *xfs_rmapbt_init_cursor(struct 
xfs_mount *mp,
                                xfs_agnumber_t agno);
 int xfs_rmapbt_maxrecs(struct xfs_mount *mp, int blocklen, int leaf);
 
+int xfs_rmap_lookup_le(struct xfs_btree_cur *cur, xfs_agblock_t        bno,
+               xfs_extlen_t len, uint64_t owner, uint64_t offset, int *stat);
+int xfs_rmap_lookup_eq(struct xfs_btree_cur *cur, xfs_agblock_t        bno,
+               xfs_extlen_t len, uint64_t owner, uint64_t offset, int *stat);
+int xfs_rmapbt_insert(struct xfs_btree_cur *rcur, xfs_agblock_t        agbno,
+               xfs_extlen_t len, uint64_t owner, uint64_t offset);
+int xfs_rmap_get_rec(struct xfs_btree_cur *cur, struct xfs_rmap_irec *irec,
+               int *stat);
+
+/* functions for updating the rmapbt for bmbt blocks and AG btree blocks */
 int xfs_rmap_alloc(struct xfs_trans *tp, struct xfs_buf *agbp,
                   xfs_agnumber_t agno, xfs_agblock_t bno, xfs_extlen_t len,
-                  uint64_t owner);
+                  struct xfs_owner_info *oinfo);
 int xfs_rmap_free(struct xfs_trans *tp, struct xfs_buf *agbp,
                  xfs_agnumber_t agno, xfs_agblock_t bno, xfs_extlen_t len,
-                 uint64_t owner);
+                 struct xfs_owner_info *oinfo);
+
+/* functions for updating the rmapbt based on bmbt map/unmap operations */
+int xfs_rmap_combine(struct xfs_btree_cur *rcur, xfs_ino_t ino, int whichfork,
+               struct xfs_bmbt_irec *LEFT, struct xfs_bmbt_irec *RIGHT,
+               struct xfs_bmbt_irec *PREV);
+int xfs_rmap_lcombine(struct xfs_btree_cur *rcur, xfs_ino_t ino, int whichfork,
+               struct xfs_bmbt_irec *LEFT, struct xfs_bmbt_irec *PREV);
+int xfs_rmap_rcombine(struct xfs_btree_cur *rcur, xfs_ino_t ino, int whichfork,
+               struct xfs_bmbt_irec *RIGHT, struct xfs_bmbt_irec *PREV,
+               struct xfs_bmbt_irec *new);
+int xfs_rmap_insert(struct xfs_btree_cur *rcur, xfs_ino_t ino, int whichfork,
+               struct xfs_bmbt_irec *new);
+int xfs_rmap_delete(struct xfs_btree_cur *rcur, xfs_ino_t ino, int whichfork,
+               struct xfs_bmbt_irec *new);
+int xfs_rmap_move(struct xfs_btree_cur *rcur, xfs_ino_t ino, int whichfork,
+               struct xfs_bmbt_irec *PREV, long start_adj);
+int xfs_rmap_slide(struct xfs_btree_cur *rcur, xfs_ino_t ino, int whichfork,
+               struct xfs_bmbt_irec *PREV, long start_adj);
+int xfs_rmap_resize(struct xfs_btree_cur *rcur, xfs_ino_t ino, int whichfork,
+               struct xfs_bmbt_irec *PREV, long size_adj);
 
 #endif /* __XFS_RMAP_BTREE_H__ */

<Prev in Thread] Current Thread [Next in Thread>