Merge branch 'gup_flag-cleanups'
[cascardo/linux.git] / fs / xfs / xfs_log_recover.c
index 846483d..9b3d7c7 100644 (file)
@@ -45,6 +45,8 @@
 #include "xfs_dir2.h"
 #include "xfs_rmap_item.h"
 #include "xfs_buf_item.h"
+#include "xfs_refcount_item.h"
+#include "xfs_bmap_item.h"
 
 #define BLK_AVG(blk1, blk2)    ((blk1+blk2) >> 1)
 
@@ -1924,6 +1926,10 @@ xlog_recover_reorder_trans(
                case XFS_LI_EFI:
                case XFS_LI_RUI:
                case XFS_LI_RUD:
+               case XFS_LI_CUI:
+               case XFS_LI_CUD:
+               case XFS_LI_BUI:
+               case XFS_LI_BUD:
                        trace_xfs_log_recover_item_reorder_tail(log,
                                                        trans, item, pass);
                        list_move_tail(&item->ri_list, &inode_list);
@@ -2242,6 +2248,7 @@ xlog_recover_get_buf_lsn(
        case XFS_ABTB_MAGIC:
        case XFS_ABTC_MAGIC:
        case XFS_RMAP_CRC_MAGIC:
+       case XFS_REFC_CRC_MAGIC:
        case XFS_IBT_CRC_MAGIC:
        case XFS_IBT_MAGIC: {
                struct xfs_btree_block *btb = blk;
@@ -2415,6 +2422,9 @@ xlog_recover_validate_buf_type(
                case XFS_RMAP_CRC_MAGIC:
                        bp->b_ops = &xfs_rmapbt_buf_ops;
                        break;
+               case XFS_REFC_CRC_MAGIC:
+                       bp->b_ops = &xfs_refcountbt_buf_ops;
+                       break;
                default:
                        warnmsg = "Bad btree block magic!";
                        break;
@@ -3546,6 +3556,242 @@ xlog_recover_rud_pass2(
        return 0;
 }
 
+/*
+ * Copy an CUI format buffer from the given buf, and into the destination
+ * CUI format structure.  The CUI/CUD items were designed not to need any
+ * special alignment handling.
+ */
+static int
+xfs_cui_copy_format(
+       struct xfs_log_iovec            *buf,
+       struct xfs_cui_log_format       *dst_cui_fmt)
+{
+       struct xfs_cui_log_format       *src_cui_fmt;
+       uint                            len;
+
+       src_cui_fmt = buf->i_addr;
+       len = xfs_cui_log_format_sizeof(src_cui_fmt->cui_nextents);
+
+       if (buf->i_len == len) {
+               memcpy(dst_cui_fmt, src_cui_fmt, len);
+               return 0;
+       }
+       return -EFSCORRUPTED;
+}
+
+/*
+ * This routine is called to create an in-core extent refcount update
+ * item from the cui format structure which was logged on disk.
+ * It allocates an in-core cui, copies the extents from the format
+ * structure into it, and adds the cui to the AIL with the given
+ * LSN.
+ */
+STATIC int
+xlog_recover_cui_pass2(
+       struct xlog                     *log,
+       struct xlog_recover_item        *item,
+       xfs_lsn_t                       lsn)
+{
+       int                             error;
+       struct xfs_mount                *mp = log->l_mp;
+       struct xfs_cui_log_item         *cuip;
+       struct xfs_cui_log_format       *cui_formatp;
+
+       cui_formatp = item->ri_buf[0].i_addr;
+
+       cuip = xfs_cui_init(mp, cui_formatp->cui_nextents);
+       error = xfs_cui_copy_format(&item->ri_buf[0], &cuip->cui_format);
+       if (error) {
+               xfs_cui_item_free(cuip);
+               return error;
+       }
+       atomic_set(&cuip->cui_next_extent, cui_formatp->cui_nextents);
+
+       spin_lock(&log->l_ailp->xa_lock);
+       /*
+        * The CUI has two references. One for the CUD and one for CUI to ensure
+        * it makes it into the AIL. Insert the CUI into the AIL directly and
+        * drop the CUI reference. Note that xfs_trans_ail_update() drops the
+        * AIL lock.
+        */
+       xfs_trans_ail_update(log->l_ailp, &cuip->cui_item, lsn);
+       xfs_cui_release(cuip);
+       return 0;
+}
+
+
+/*
+ * This routine is called when an CUD format structure is found in a committed
+ * transaction in the log. Its purpose is to cancel the corresponding CUI if it
+ * was still in the log. To do this it searches the AIL for the CUI with an id
+ * equal to that in the CUD format structure. If we find it we drop the CUD
+ * reference, which removes the CUI from the AIL and frees it.
+ */
+STATIC int
+xlog_recover_cud_pass2(
+       struct xlog                     *log,
+       struct xlog_recover_item        *item)
+{
+       struct xfs_cud_log_format       *cud_formatp;
+       struct xfs_cui_log_item         *cuip = NULL;
+       struct xfs_log_item             *lip;
+       __uint64_t                      cui_id;
+       struct xfs_ail_cursor           cur;
+       struct xfs_ail                  *ailp = log->l_ailp;
+
+       cud_formatp = item->ri_buf[0].i_addr;
+       if (item->ri_buf[0].i_len != sizeof(struct xfs_cud_log_format))
+               return -EFSCORRUPTED;
+       cui_id = cud_formatp->cud_cui_id;
+
+       /*
+        * Search for the CUI with the id in the CUD format structure in the
+        * AIL.
+        */
+       spin_lock(&ailp->xa_lock);
+       lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
+       while (lip != NULL) {
+               if (lip->li_type == XFS_LI_CUI) {
+                       cuip = (struct xfs_cui_log_item *)lip;
+                       if (cuip->cui_format.cui_id == cui_id) {
+                               /*
+                                * Drop the CUD reference to the CUI. This
+                                * removes the CUI from the AIL and frees it.
+                                */
+                               spin_unlock(&ailp->xa_lock);
+                               xfs_cui_release(cuip);
+                               spin_lock(&ailp->xa_lock);
+                               break;
+                       }
+               }
+               lip = xfs_trans_ail_cursor_next(ailp, &cur);
+       }
+
+       xfs_trans_ail_cursor_done(&cur);
+       spin_unlock(&ailp->xa_lock);
+
+       return 0;
+}
+
+/*
+ * Copy an BUI format buffer from the given buf, and into the destination
+ * BUI format structure.  The BUI/BUD items were designed not to need any
+ * special alignment handling.
+ */
+static int
+xfs_bui_copy_format(
+       struct xfs_log_iovec            *buf,
+       struct xfs_bui_log_format       *dst_bui_fmt)
+{
+       struct xfs_bui_log_format       *src_bui_fmt;
+       uint                            len;
+
+       src_bui_fmt = buf->i_addr;
+       len = xfs_bui_log_format_sizeof(src_bui_fmt->bui_nextents);
+
+       if (buf->i_len == len) {
+               memcpy(dst_bui_fmt, src_bui_fmt, len);
+               return 0;
+       }
+       return -EFSCORRUPTED;
+}
+
+/*
+ * This routine is called to create an in-core extent bmap update
+ * item from the bui format structure which was logged on disk.
+ * It allocates an in-core bui, copies the extents from the format
+ * structure into it, and adds the bui to the AIL with the given
+ * LSN.
+ */
+STATIC int
+xlog_recover_bui_pass2(
+       struct xlog                     *log,
+       struct xlog_recover_item        *item,
+       xfs_lsn_t                       lsn)
+{
+       int                             error;
+       struct xfs_mount                *mp = log->l_mp;
+       struct xfs_bui_log_item         *buip;
+       struct xfs_bui_log_format       *bui_formatp;
+
+       bui_formatp = item->ri_buf[0].i_addr;
+
+       if (bui_formatp->bui_nextents != XFS_BUI_MAX_FAST_EXTENTS)
+               return -EFSCORRUPTED;
+       buip = xfs_bui_init(mp);
+       error = xfs_bui_copy_format(&item->ri_buf[0], &buip->bui_format);
+       if (error) {
+               xfs_bui_item_free(buip);
+               return error;
+       }
+       atomic_set(&buip->bui_next_extent, bui_formatp->bui_nextents);
+
+       spin_lock(&log->l_ailp->xa_lock);
+       /*
+        * The RUI has two references. One for the RUD and one for RUI to ensure
+        * it makes it into the AIL. Insert the RUI into the AIL directly and
+        * drop the RUI reference. Note that xfs_trans_ail_update() drops the
+        * AIL lock.
+        */
+       xfs_trans_ail_update(log->l_ailp, &buip->bui_item, lsn);
+       xfs_bui_release(buip);
+       return 0;
+}
+
+
+/*
+ * This routine is called when an BUD format structure is found in a committed
+ * transaction in the log. Its purpose is to cancel the corresponding BUI if it
+ * was still in the log. To do this it searches the AIL for the BUI with an id
+ * equal to that in the BUD format structure. If we find it we drop the BUD
+ * reference, which removes the BUI from the AIL and frees it.
+ */
+STATIC int
+xlog_recover_bud_pass2(
+       struct xlog                     *log,
+       struct xlog_recover_item        *item)
+{
+       struct xfs_bud_log_format       *bud_formatp;
+       struct xfs_bui_log_item         *buip = NULL;
+       struct xfs_log_item             *lip;
+       __uint64_t                      bui_id;
+       struct xfs_ail_cursor           cur;
+       struct xfs_ail                  *ailp = log->l_ailp;
+
+       bud_formatp = item->ri_buf[0].i_addr;
+       if (item->ri_buf[0].i_len != sizeof(struct xfs_bud_log_format))
+               return -EFSCORRUPTED;
+       bui_id = bud_formatp->bud_bui_id;
+
+       /*
+        * Search for the BUI with the id in the BUD format structure in the
+        * AIL.
+        */
+       spin_lock(&ailp->xa_lock);
+       lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
+       while (lip != NULL) {
+               if (lip->li_type == XFS_LI_BUI) {
+                       buip = (struct xfs_bui_log_item *)lip;
+                       if (buip->bui_format.bui_id == bui_id) {
+                               /*
+                                * Drop the BUD reference to the BUI. This
+                                * removes the BUI from the AIL and frees it.
+                                */
+                               spin_unlock(&ailp->xa_lock);
+                               xfs_bui_release(buip);
+                               spin_lock(&ailp->xa_lock);
+                               break;
+                       }
+               }
+               lip = xfs_trans_ail_cursor_next(ailp, &cur);
+       }
+
+       xfs_trans_ail_cursor_done(&cur);
+       spin_unlock(&ailp->xa_lock);
+
+       return 0;
+}
+
 /*
  * This routine is called when an inode create format structure is found in a
  * committed transaction in the log.  It's purpose is to initialise the inodes
@@ -3773,6 +4019,10 @@ xlog_recover_ra_pass2(
        case XFS_LI_QUOTAOFF:
        case XFS_LI_RUI:
        case XFS_LI_RUD:
+       case XFS_LI_CUI:
+       case XFS_LI_CUD:
+       case XFS_LI_BUI:
+       case XFS_LI_BUD:
        default:
                break;
        }
@@ -3798,6 +4048,10 @@ xlog_recover_commit_pass1(
        case XFS_LI_ICREATE:
        case XFS_LI_RUI:
        case XFS_LI_RUD:
+       case XFS_LI_CUI:
+       case XFS_LI_CUD:
+       case XFS_LI_BUI:
+       case XFS_LI_BUD:
                /* nothing to do in pass 1 */
                return 0;
        default:
@@ -3832,6 +4086,14 @@ xlog_recover_commit_pass2(
                return xlog_recover_rui_pass2(log, item, trans->r_lsn);
        case XFS_LI_RUD:
                return xlog_recover_rud_pass2(log, item);
+       case XFS_LI_CUI:
+               return xlog_recover_cui_pass2(log, item, trans->r_lsn);
+       case XFS_LI_CUD:
+               return xlog_recover_cud_pass2(log, item);
+       case XFS_LI_BUI:
+               return xlog_recover_bui_pass2(log, item, trans->r_lsn);
+       case XFS_LI_BUD:
+               return xlog_recover_bud_pass2(log, item);
        case XFS_LI_DQUOT:
                return xlog_recover_dquot_pass2(log, buffer_list, item,
                                                trans->r_lsn);
@@ -4419,12 +4681,94 @@ xlog_recover_cancel_rui(
        spin_lock(&ailp->xa_lock);
 }
 
+/* Recover the CUI if necessary. */
+STATIC int
+xlog_recover_process_cui(
+       struct xfs_mount                *mp,
+       struct xfs_ail                  *ailp,
+       struct xfs_log_item             *lip)
+{
+       struct xfs_cui_log_item         *cuip;
+       int                             error;
+
+       /*
+        * Skip CUIs that we've already processed.
+        */
+       cuip = container_of(lip, struct xfs_cui_log_item, cui_item);
+       if (test_bit(XFS_CUI_RECOVERED, &cuip->cui_flags))
+               return 0;
+
+       spin_unlock(&ailp->xa_lock);
+       error = xfs_cui_recover(mp, cuip);
+       spin_lock(&ailp->xa_lock);
+
+       return error;
+}
+
+/* Release the CUI since we're cancelling everything. */
+STATIC void
+xlog_recover_cancel_cui(
+       struct xfs_mount                *mp,
+       struct xfs_ail                  *ailp,
+       struct xfs_log_item             *lip)
+{
+       struct xfs_cui_log_item         *cuip;
+
+       cuip = container_of(lip, struct xfs_cui_log_item, cui_item);
+
+       spin_unlock(&ailp->xa_lock);
+       xfs_cui_release(cuip);
+       spin_lock(&ailp->xa_lock);
+}
+
+/* Recover the BUI if necessary. */
+STATIC int
+xlog_recover_process_bui(
+       struct xfs_mount                *mp,
+       struct xfs_ail                  *ailp,
+       struct xfs_log_item             *lip)
+{
+       struct xfs_bui_log_item         *buip;
+       int                             error;
+
+       /*
+        * Skip BUIs that we've already processed.
+        */
+       buip = container_of(lip, struct xfs_bui_log_item, bui_item);
+       if (test_bit(XFS_BUI_RECOVERED, &buip->bui_flags))
+               return 0;
+
+       spin_unlock(&ailp->xa_lock);
+       error = xfs_bui_recover(mp, buip);
+       spin_lock(&ailp->xa_lock);
+
+       return error;
+}
+
+/* Release the BUI since we're cancelling everything. */
+STATIC void
+xlog_recover_cancel_bui(
+       struct xfs_mount                *mp,
+       struct xfs_ail                  *ailp,
+       struct xfs_log_item             *lip)
+{
+       struct xfs_bui_log_item         *buip;
+
+       buip = container_of(lip, struct xfs_bui_log_item, bui_item);
+
+       spin_unlock(&ailp->xa_lock);
+       xfs_bui_release(buip);
+       spin_lock(&ailp->xa_lock);
+}
+
 /* Is this log item a deferred action intent? */
 static inline bool xlog_item_is_intent(struct xfs_log_item *lip)
 {
        switch (lip->li_type) {
        case XFS_LI_EFI:
        case XFS_LI_RUI:
+       case XFS_LI_CUI:
+       case XFS_LI_BUI:
                return true;
        default:
                return false;
@@ -4488,6 +4832,12 @@ xlog_recover_process_intents(
                case XFS_LI_RUI:
                        error = xlog_recover_process_rui(log->l_mp, ailp, lip);
                        break;
+               case XFS_LI_CUI:
+                       error = xlog_recover_process_cui(log->l_mp, ailp, lip);
+                       break;
+               case XFS_LI_BUI:
+                       error = xlog_recover_process_bui(log->l_mp, ailp, lip);
+                       break;
                }
                if (error)
                        goto out;
@@ -4535,6 +4885,12 @@ xlog_recover_cancel_intents(
                case XFS_LI_RUI:
                        xlog_recover_cancel_rui(log->l_mp, ailp, lip);
                        break;
+               case XFS_LI_CUI:
+                       xlog_recover_cancel_cui(log->l_mp, ailp, lip);
+                       break;
+               case XFS_LI_BUI:
+                       xlog_recover_cancel_bui(log->l_mp, ailp, lip);
+                       break;
                }
 
                lip = xfs_trans_ail_cursor_next(ailp, &cur);
@@ -4613,6 +4969,7 @@ xlog_recover_process_one_iunlink(
        if (error)
                goto fail_iput;
 
+       xfs_iflags_clear(ip, XFS_IRECOVERY);
        ASSERT(VFS_I(ip)->i_nlink == 0);
        ASSERT(VFS_I(ip)->i_mode != 0);