xfs: implement deferred bmbt map/unmap operations
authorDarrick J. Wong <darrick.wong@oracle.com>
Mon, 3 Oct 2016 16:11:28 +0000 (09:11 -0700)
committerDarrick J. Wong <darrick.wong@oracle.com>
Tue, 4 Oct 2016 18:05:44 +0000 (11:05 -0700)
Implement deferred versions of the inode block map/unmap functions.
These will be used in subsequent patches to make reflink operations
atomic.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/libxfs/xfs_bmap.c
fs/xfs/libxfs/xfs_bmap.h
fs/xfs/libxfs/xfs_defer.h
fs/xfs/xfs_bmap_item.c
fs/xfs/xfs_error.h
fs/xfs/xfs_super.c
fs/xfs/xfs_trace.h
fs/xfs/xfs_trans.h
fs/xfs/xfs_trans_bmap.c

index ee9f54bdf084f7a3ac49f92d47d918b436a1400c..5828814c119c63d0c667c730a8683b45119ecd15 100644 (file)
@@ -6062,3 +6062,146 @@ out:
        xfs_trans_cancel(tp);
        return error;
 }
+
+/* Deferred mapping is only for real extents in the data fork. */
+static bool
+xfs_bmap_is_update_needed(
+       struct xfs_bmbt_irec    *bmap)
+{
+       return  bmap->br_startblock != HOLESTARTBLOCK &&
+               bmap->br_startblock != DELAYSTARTBLOCK;
+}
+
+/* Record a bmap intent. */
+static int
+__xfs_bmap_add(
+       struct xfs_mount                *mp,
+       struct xfs_defer_ops            *dfops,
+       enum xfs_bmap_intent_type       type,
+       struct xfs_inode                *ip,
+       int                             whichfork,
+       struct xfs_bmbt_irec            *bmap)
+{
+       int                             error;
+       struct xfs_bmap_intent          *bi;
+
+       trace_xfs_bmap_defer(mp,
+                       XFS_FSB_TO_AGNO(mp, bmap->br_startblock),
+                       type,
+                       XFS_FSB_TO_AGBNO(mp, bmap->br_startblock),
+                       ip->i_ino, whichfork,
+                       bmap->br_startoff,
+                       bmap->br_blockcount,
+                       bmap->br_state);
+
+       bi = kmem_alloc(sizeof(struct xfs_bmap_intent), KM_SLEEP | KM_NOFS);
+       INIT_LIST_HEAD(&bi->bi_list);
+       bi->bi_type = type;
+       bi->bi_owner = ip;
+       bi->bi_whichfork = whichfork;
+       bi->bi_bmap = *bmap;
+
+       error = xfs_defer_join(dfops, bi->bi_owner);
+       if (error) {
+               kmem_free(bi);
+               return error;
+       }
+
+       xfs_defer_add(dfops, XFS_DEFER_OPS_TYPE_BMAP, &bi->bi_list);
+       return 0;
+}
+
+/* Map an extent into a file. */
+int
+xfs_bmap_map_extent(
+       struct xfs_mount        *mp,
+       struct xfs_defer_ops    *dfops,
+       struct xfs_inode        *ip,
+       struct xfs_bmbt_irec    *PREV)
+{
+       if (!xfs_bmap_is_update_needed(PREV))
+               return 0;
+
+       return __xfs_bmap_add(mp, dfops, XFS_BMAP_MAP, ip,
+                       XFS_DATA_FORK, PREV);
+}
+
+/* Unmap an extent out of a file. */
+int
+xfs_bmap_unmap_extent(
+       struct xfs_mount        *mp,
+       struct xfs_defer_ops    *dfops,
+       struct xfs_inode        *ip,
+       struct xfs_bmbt_irec    *PREV)
+{
+       if (!xfs_bmap_is_update_needed(PREV))
+               return 0;
+
+       return __xfs_bmap_add(mp, dfops, XFS_BMAP_UNMAP, ip,
+                       XFS_DATA_FORK, PREV);
+}
+
+/*
+ * Process one of the deferred bmap operations.  We pass back the
+ * btree cursor to maintain our lock on the bmapbt between calls.
+ */
+int
+xfs_bmap_finish_one(
+       struct xfs_trans                *tp,
+       struct xfs_defer_ops            *dfops,
+       struct xfs_inode                *ip,
+       enum xfs_bmap_intent_type       type,
+       int                             whichfork,
+       xfs_fileoff_t                   startoff,
+       xfs_fsblock_t                   startblock,
+       xfs_filblks_t                   blockcount,
+       xfs_exntst_t                    state)
+{
+       struct xfs_bmbt_irec            bmap;
+       int                             nimaps = 1;
+       xfs_fsblock_t                   firstfsb;
+       int                             flags = XFS_BMAPI_REMAP;
+       int                             done;
+       int                             error = 0;
+
+       bmap.br_startblock = startblock;
+       bmap.br_startoff = startoff;
+       bmap.br_blockcount = blockcount;
+       bmap.br_state = state;
+
+       trace_xfs_bmap_deferred(tp->t_mountp,
+                       XFS_FSB_TO_AGNO(tp->t_mountp, startblock), type,
+                       XFS_FSB_TO_AGBNO(tp->t_mountp, startblock),
+                       ip->i_ino, whichfork, startoff, blockcount, state);
+
+       if (whichfork != XFS_DATA_FORK && whichfork != XFS_ATTR_FORK)
+               return -EFSCORRUPTED;
+       if (whichfork == XFS_ATTR_FORK)
+               flags |= XFS_BMAPI_ATTRFORK;
+
+       if (XFS_TEST_ERROR(false, tp->t_mountp,
+                       XFS_ERRTAG_BMAP_FINISH_ONE,
+                       XFS_RANDOM_BMAP_FINISH_ONE))
+               return -EIO;
+
+       switch (type) {
+       case XFS_BMAP_MAP:
+               firstfsb = bmap.br_startblock;
+               error = xfs_bmapi_write(tp, ip, bmap.br_startoff,
+                                       bmap.br_blockcount, flags, &firstfsb,
+                                       bmap.br_blockcount, &bmap, &nimaps,
+                                       dfops);
+               break;
+       case XFS_BMAP_UNMAP:
+               error = xfs_bunmapi(tp, ip, bmap.br_startoff,
+                               bmap.br_blockcount, flags, 1, &firstfsb,
+                               dfops, &done);
+               ASSERT(done);
+               break;
+       default:
+               ASSERT(0);
+               error = -EFSCORRUPTED;
+       }
+
+       return error;
+}
index 9c0406720d692088e3e079170db164ff02712f07..b40ef797a4bf968ca5ec0f2f06660d980262ac90 100644 (file)
@@ -231,4 +231,13 @@ struct xfs_bmap_intent {
        struct xfs_bmbt_irec                    bi_bmap;
 };
 
+int    xfs_bmap_finish_one(struct xfs_trans *tp, struct xfs_defer_ops *dfops,
+               struct xfs_inode *ip, enum xfs_bmap_intent_type type,
+               int whichfork, xfs_fileoff_t startoff, xfs_fsblock_t startblock,
+               xfs_filblks_t blockcount, xfs_exntst_t state);
+int    xfs_bmap_map_extent(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
+               struct xfs_inode *ip, struct xfs_bmbt_irec *imap);
+int    xfs_bmap_unmap_extent(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
+               struct xfs_inode *ip, struct xfs_bmbt_irec *imap);
+
 #endif /* __XFS_BMAP_H__ */
index 4d94a869e581caff0bdd67b9468b90a760351664..f6e93ef0bffe8a4620a136b1e7f523bb1343a941 100644 (file)
@@ -51,6 +51,7 @@ struct xfs_defer_pending {
  * find all the space it needs.
  */
 enum xfs_defer_ops_type {
+       XFS_DEFER_OPS_TYPE_BMAP,
        XFS_DEFER_OPS_TYPE_REFCOUNT,
        XFS_DEFER_OPS_TYPE_RMAP,
        XFS_DEFER_OPS_TYPE_FREE,
index 9b3a936fb384903e4af15a429c2366bc1412ab20..02e8f93f03152e36d34ab6f481c68dd8a50647ec 100644 (file)
@@ -389,10 +389,19 @@ xfs_bui_recover(
        struct xfs_bui_log_item         *buip)
 {
        int                             error = 0;
+       unsigned int                    bui_type;
        struct xfs_map_extent           *bmap;
        xfs_fsblock_t                   startblock_fsb;
        xfs_fsblock_t                   inode_fsb;
        bool                            op_ok;
+       struct xfs_bud_log_item         *budp;
+       enum xfs_bmap_intent_type       type;
+       int                             whichfork;
+       xfs_exntst_t                    state;
+       struct xfs_trans                *tp;
+       struct xfs_inode                *ip = NULL;
+       struct xfs_defer_ops            dfops;
+       xfs_fsblock_t                   firstfsb;
 
        ASSERT(!test_bit(XFS_BUI_RECOVERED, &buip->bui_flags));
 
@@ -437,7 +446,61 @@ xfs_bui_recover(
                return -EIO;
        }
 
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
+       if (error)
+               return error;
+       budp = xfs_trans_get_bud(tp, buip);
+
+       /* Grab the inode. */
+       error = xfs_iget(mp, tp, bmap->me_owner, 0, XFS_ILOCK_EXCL, &ip);
+       if (error)
+               goto err_inode;
+
+       xfs_defer_init(&dfops, &firstfsb);
+
+       /* Process deferred bmap item. */
+       state = (bmap->me_flags & XFS_BMAP_EXTENT_UNWRITTEN) ?
+                       XFS_EXT_UNWRITTEN : XFS_EXT_NORM;
+       whichfork = (bmap->me_flags & XFS_BMAP_EXTENT_ATTR_FORK) ?
+                       XFS_ATTR_FORK : XFS_DATA_FORK;
+       bui_type = bmap->me_flags & XFS_BMAP_EXTENT_TYPE_MASK;
+       switch (bui_type) {
+       case XFS_BMAP_MAP:
+       case XFS_BMAP_UNMAP:
+               type = bui_type;
+               break;
+       default:
+               error = -EFSCORRUPTED;
+               goto err_dfops;
+       }
+       xfs_trans_ijoin(tp, ip, 0);
+
+       error = xfs_trans_log_finish_bmap_update(tp, budp, &dfops, type,
+                       ip, whichfork, bmap->me_startoff,
+                       bmap->me_startblock, bmap->me_len,
+                       state);
+       if (error)
+               goto err_dfops;
+
+       /* Finish transaction, free inodes. */
+       error = xfs_defer_finish(&tp, &dfops, NULL);
+       if (error)
+               goto err_dfops;
+
        set_bit(XFS_BUI_RECOVERED, &buip->bui_flags);
-       xfs_bui_release(buip);
+       error = xfs_trans_commit(tp);
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       IRELE(ip);
+
+       return error;
+
+err_dfops:
+       xfs_defer_cancel(&dfops);
+err_inode:
+       xfs_trans_cancel(tp);
+       if (ip) {
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+               IRELE(ip);
+       }
        return error;
 }
index 641e090f3bea82d452bc06d0e7dc81ab14d2747b..8d8e1b07d295b9185f7477235cbd9215697470d4 100644 (file)
@@ -94,7 +94,8 @@ extern void xfs_verifier_error(struct xfs_buf *bp);
 #define XFS_ERRTAG_RMAP_FINISH_ONE                     23
 #define XFS_ERRTAG_REFCOUNT_CONTINUE_UPDATE            24
 #define XFS_ERRTAG_REFCOUNT_FINISH_ONE                 25
-#define XFS_ERRTAG_MAX                                 26
+#define XFS_ERRTAG_BMAP_FINISH_ONE                     26
+#define XFS_ERRTAG_MAX                                 27
 
 /*
  * Random factors for above tags, 1 means always, 2 means 1/2 time, etc.
@@ -125,6 +126,7 @@ extern void xfs_verifier_error(struct xfs_buf *bp);
 #define XFS_RANDOM_RMAP_FINISH_ONE                     1
 #define XFS_RANDOM_REFCOUNT_CONTINUE_UPDATE            1
 #define XFS_RANDOM_REFCOUNT_FINISH_ONE                 1
+#define XFS_RANDOM_BMAP_FINISH_ONE                     1
 
 #ifdef DEBUG
 extern int xfs_error_test_active;
index 071bae0ec499734e3b97fec2fc7fe28a3b5b82eb..204b794cebd5123e19840e3b58331a2396b34c51 100644 (file)
@@ -1922,6 +1922,7 @@ init_xfs_fs(void)
        xfs_extent_free_init_defer_op();
        xfs_rmap_update_init_defer_op();
        xfs_refcount_update_init_defer_op();
+       xfs_bmap_update_init_defer_op();
 
        xfs_dir_startup();
 
index e44992a84d7fffddfc64c194802b3a62b3272d63..85468ff279a1317a34a38a3f871c1392dd1070b3 100644 (file)
@@ -2587,6 +2587,11 @@ DEFINE_RMAPBT_EVENT(xfs_rmap_lookup_le_range_result);
 DEFINE_RMAPBT_EVENT(xfs_rmap_find_right_neighbor_result);
 DEFINE_RMAPBT_EVENT(xfs_rmap_find_left_neighbor_result);
 
+/* deferred bmbt updates */
+#define DEFINE_BMAP_DEFERRED_EVENT     DEFINE_RMAP_DEFERRED_EVENT
+DEFINE_BMAP_DEFERRED_EVENT(xfs_bmap_defer);
+DEFINE_BMAP_DEFERRED_EVENT(xfs_bmap_deferred);
+
 /* per-AG reservation */
 DECLARE_EVENT_CLASS(xfs_ag_resv_class,
        TP_PROTO(struct xfs_perag *pag, enum xfs_ag_resv_type resv,
index 460ea79eef6ad0f49be451abd0caf75c6dde7b16..61b7fbdd3ebddfd1ebd20641753a87d292521508 100644 (file)
@@ -40,6 +40,7 @@ struct xfs_cui_log_item;
 struct xfs_cud_log_item;
 struct xfs_defer_ops;
 struct xfs_bui_log_item;
+struct xfs_bud_log_item;
 
 typedef struct xfs_log_item {
        struct list_head                li_ail;         /* AIL pointers */
index 656d6692d207f9d322f289500111cbe27ef531f7..6408e7d7c08c84c2dd050f86be6bdf82a6d2ee1b 100644 (file)
@@ -68,7 +68,8 @@ xfs_trans_log_finish_bmap_update(
 {
        int                             error;
 
-       error = -EFSCORRUPTED;
+       error = xfs_bmap_finish_one(tp, dop, ip, type, whichfork, startoff,
+                       startblock, blockcount, state);
 
        /*
         * Mark the transaction dirty, even on error. This ensures the
@@ -82,3 +83,167 @@ xfs_trans_log_finish_bmap_update(
 
        return error;
 }
+
+/* Sort bmap intents by inode. */
+static int
+xfs_bmap_update_diff_items(
+       void                            *priv,
+       struct list_head                *a,
+       struct list_head                *b)
+{
+       struct xfs_bmap_intent          *ba;
+       struct xfs_bmap_intent          *bb;
+
+       ba = container_of(a, struct xfs_bmap_intent, bi_list);
+       bb = container_of(b, struct xfs_bmap_intent, bi_list);
+       return ba->bi_owner->i_ino - bb->bi_owner->i_ino;
+}
+
+/* Get an BUI. */
+STATIC void *
+xfs_bmap_update_create_intent(
+       struct xfs_trans                *tp,
+       unsigned int                    count)
+{
+       struct xfs_bui_log_item         *buip;
+
+       ASSERT(count == XFS_BUI_MAX_FAST_EXTENTS);
+       ASSERT(tp != NULL);
+
+       buip = xfs_bui_init(tp->t_mountp);
+       ASSERT(buip != NULL);
+
+       /*
+        * Get a log_item_desc to point at the new item.
+        */
+       xfs_trans_add_item(tp, &buip->bui_item);
+       return buip;
+}
+
+/* Set the map extent flags for this mapping. */
+static void
+xfs_trans_set_bmap_flags(
+       struct xfs_map_extent           *bmap,
+       enum xfs_bmap_intent_type       type,
+       int                             whichfork,
+       xfs_exntst_t                    state)
+{
+       bmap->me_flags = 0;
+       switch (type) {
+       case XFS_BMAP_MAP:
+       case XFS_BMAP_UNMAP:
+               bmap->me_flags = type;
+               break;
+       default:
+               ASSERT(0);
+       }
+       if (state == XFS_EXT_UNWRITTEN)
+               bmap->me_flags |= XFS_BMAP_EXTENT_UNWRITTEN;
+       if (whichfork == XFS_ATTR_FORK)
+               bmap->me_flags |= XFS_BMAP_EXTENT_ATTR_FORK;
+}
+
+/* Log bmap updates in the intent item. */
+STATIC void
+xfs_bmap_update_log_item(
+       struct xfs_trans                *tp,
+       void                            *intent,
+       struct list_head                *item)
+{
+       struct xfs_bui_log_item         *buip = intent;
+       struct xfs_bmap_intent          *bmap;
+       uint                            next_extent;
+       struct xfs_map_extent           *map;
+
+       bmap = container_of(item, struct xfs_bmap_intent, bi_list);
+
+       tp->t_flags |= XFS_TRANS_DIRTY;
+       buip->bui_item.li_desc->lid_flags |= XFS_LID_DIRTY;
+
+       /*
+        * atomic_inc_return gives us the value after the increment;
+        * we want to use it as an array index so we need to subtract 1 from
+        * it.
+        */
+       next_extent = atomic_inc_return(&buip->bui_next_extent) - 1;
+       ASSERT(next_extent < buip->bui_format.bui_nextents);
+       map = &buip->bui_format.bui_extents[next_extent];
+       map->me_owner = bmap->bi_owner->i_ino;
+       map->me_startblock = bmap->bi_bmap.br_startblock;
+       map->me_startoff = bmap->bi_bmap.br_startoff;
+       map->me_len = bmap->bi_bmap.br_blockcount;
+       xfs_trans_set_bmap_flags(map, bmap->bi_type, bmap->bi_whichfork,
+                       bmap->bi_bmap.br_state);
+}
+
+/* Get an BUD so we can process all the deferred rmap updates. */
+STATIC void *
+xfs_bmap_update_create_done(
+       struct xfs_trans                *tp,
+       void                            *intent,
+       unsigned int                    count)
+{
+       return xfs_trans_get_bud(tp, intent);
+}
+
+/* Process a deferred rmap update. */
+STATIC int
+xfs_bmap_update_finish_item(
+       struct xfs_trans                *tp,
+       struct xfs_defer_ops            *dop,
+       struct list_head                *item,
+       void                            *done_item,
+       void                            **state)
+{
+       struct xfs_bmap_intent          *bmap;
+       int                             error;
+
+       bmap = container_of(item, struct xfs_bmap_intent, bi_list);
+       error = xfs_trans_log_finish_bmap_update(tp, done_item, dop,
+                       bmap->bi_type,
+                       bmap->bi_owner, bmap->bi_whichfork,
+                       bmap->bi_bmap.br_startoff,
+                       bmap->bi_bmap.br_startblock,
+                       bmap->bi_bmap.br_blockcount,
+                       bmap->bi_bmap.br_state);
+       kmem_free(bmap);
+       return error;
+}
+
+/* Abort all pending BUIs. */
+STATIC void
+xfs_bmap_update_abort_intent(
+       void                            *intent)
+{
+       xfs_bui_release(intent);
+}
+
+/* Cancel a deferred rmap update. */
+STATIC void
+xfs_bmap_update_cancel_item(
+       struct list_head                *item)
+{
+       struct xfs_bmap_intent          *bmap;
+
+       bmap = container_of(item, struct xfs_bmap_intent, bi_list);
+       kmem_free(bmap);
+}
+
+static const struct xfs_defer_op_type xfs_bmap_update_defer_type = {
+       .type           = XFS_DEFER_OPS_TYPE_BMAP,
+       .max_items      = XFS_BUI_MAX_FAST_EXTENTS,
+       .diff_items     = xfs_bmap_update_diff_items,
+       .create_intent  = xfs_bmap_update_create_intent,
+       .abort_intent   = xfs_bmap_update_abort_intent,
+       .log_item       = xfs_bmap_update_log_item,
+       .create_done    = xfs_bmap_update_create_done,
+       .finish_item    = xfs_bmap_update_finish_item,
+       .cancel_item    = xfs_bmap_update_cancel_item,
+};
+
+/* Register the deferred op type. */
+void
+xfs_bmap_update_init_defer_op(void)
+{
+       xfs_defer_init_op_type(&xfs_bmap_update_defer_type);
+}