* [PATCH 01/28] xfs: convert xfs_log_recover_item_t to struct xfs_log_recover_item
2020-05-05 1:10 [PATCH v3 00/28] xfs: refactor log recovery Darrick J. Wong
@ 2020-05-05 1:10 ` Darrick J. Wong
2020-05-05 3:33 ` Chandan Babu R
2020-05-06 14:59 ` Christoph Hellwig
2020-05-05 1:10 ` [PATCH 02/28] xfs: refactor log recovery item sorting into a generic dispatch structure Darrick J. Wong
` (26 subsequent siblings)
27 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2020-05-05 1:10 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Remove the old typedefs.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_log_recover.h | 4 ++--
fs/xfs/xfs_log_recover.c | 26 ++++++++++++++------------
2 files changed, 16 insertions(+), 14 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h
index 3bf671637a91..148e0cb5d379 100644
--- a/fs/xfs/libxfs/xfs_log_recover.h
+++ b/fs/xfs/libxfs/xfs_log_recover.h
@@ -22,13 +22,13 @@
/*
* item headers are in ri_buf[0]. Additional buffers follow.
*/
-typedef struct xlog_recover_item {
+struct xlog_recover_item {
struct list_head ri_list;
int ri_type;
int ri_cnt; /* count of regions found */
int ri_total; /* total regions */
xfs_log_iovec_t *ri_buf; /* ptr to regions buffer */
-} xlog_recover_item_t;
+};
struct xlog_recover {
struct hlist_node r_list;
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index d0e2dd81de53..c2c06f70fb8a 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -1841,7 +1841,7 @@ xlog_recover_reorder_trans(
struct xlog_recover *trans,
int pass)
{
- xlog_recover_item_t *item, *n;
+ struct xlog_recover_item *item, *n;
int error = 0;
LIST_HEAD(sort_list);
LIST_HEAD(cancel_list);
@@ -2056,7 +2056,7 @@ xlog_recover_buffer_pass1(
STATIC int
xlog_recover_do_inode_buffer(
struct xfs_mount *mp,
- xlog_recover_item_t *item,
+ struct xlog_recover_item *item,
struct xfs_buf *bp,
xfs_buf_log_format_t *buf_f)
{
@@ -2561,7 +2561,7 @@ xlog_recover_validate_buf_type(
STATIC void
xlog_recover_do_reg_buffer(
struct xfs_mount *mp,
- xlog_recover_item_t *item,
+ struct xlog_recover_item *item,
struct xfs_buf *bp,
xfs_buf_log_format_t *buf_f,
xfs_lsn_t current_lsn)
@@ -3759,7 +3759,7 @@ STATIC int
xlog_recover_do_icreate_pass2(
struct xlog *log,
struct list_head *buffer_list,
- xlog_recover_item_t *item)
+ struct xlog_recover_item *item)
{
struct xfs_mount *mp = log->l_mp;
struct xfs_icreate_log *icl;
@@ -4134,9 +4134,9 @@ STATIC void
xlog_recover_add_item(
struct list_head *head)
{
- xlog_recover_item_t *item;
+ struct xlog_recover_item *item;
- item = kmem_zalloc(sizeof(xlog_recover_item_t), 0);
+ item = kmem_zalloc(sizeof(struct xlog_recover_item), 0);
INIT_LIST_HEAD(&item->ri_list);
list_add_tail(&item->ri_list, head);
}
@@ -4148,7 +4148,7 @@ xlog_recover_add_to_cont_trans(
char *dp,
int len)
{
- xlog_recover_item_t *item;
+ struct xlog_recover_item *item;
char *ptr, *old_ptr;
int old_len;
@@ -4171,7 +4171,8 @@ xlog_recover_add_to_cont_trans(
}
/* take the tail entry */
- item = list_entry(trans->r_itemq.prev, xlog_recover_item_t, ri_list);
+ item = list_entry(trans->r_itemq.prev, struct xlog_recover_item,
+ ri_list);
old_ptr = item->ri_buf[item->ri_cnt-1].i_addr;
old_len = item->ri_buf[item->ri_cnt-1].i_len;
@@ -4205,7 +4206,7 @@ xlog_recover_add_to_trans(
int len)
{
struct xfs_inode_log_format *in_f; /* any will do */
- xlog_recover_item_t *item;
+ struct xlog_recover_item *item;
char *ptr;
if (!len)
@@ -4241,13 +4242,14 @@ xlog_recover_add_to_trans(
in_f = (struct xfs_inode_log_format *)ptr;
/* take the tail entry */
- item = list_entry(trans->r_itemq.prev, xlog_recover_item_t, ri_list);
+ item = list_entry(trans->r_itemq.prev, struct xlog_recover_item,
+ ri_list);
if (item->ri_total != 0 &&
item->ri_total == item->ri_cnt) {
/* tail item is in use, get a new one */
xlog_recover_add_item(&trans->r_itemq);
item = list_entry(trans->r_itemq.prev,
- xlog_recover_item_t, ri_list);
+ struct xlog_recover_item, ri_list);
}
if (item->ri_total == 0) { /* first region to be added */
@@ -4293,7 +4295,7 @@ STATIC void
xlog_recover_free_trans(
struct xlog_recover *trans)
{
- xlog_recover_item_t *item, *n;
+ struct xlog_recover_item *item, *n;
int i;
hlist_del_init(&trans->r_list);
^ permalink raw reply related [flat|nested] 94+ messages in thread* Re: [PATCH 01/28] xfs: convert xfs_log_recover_item_t to struct xfs_log_recover_item
2020-05-05 1:10 ` [PATCH 01/28] xfs: convert xfs_log_recover_item_t to struct xfs_log_recover_item Darrick J. Wong
@ 2020-05-05 3:33 ` Chandan Babu R
2020-05-06 14:59 ` Christoph Hellwig
1 sibling, 0 replies; 94+ messages in thread
From: Chandan Babu R @ 2020-05-05 3:33 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-xfs
On Tuesday 5 May 2020 6:40:39 AM IST Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Remove the old typedefs.
>
Straight forward change.
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
> fs/xfs/libxfs/xfs_log_recover.h | 4 ++--
> fs/xfs/xfs_log_recover.c | 26 ++++++++++++++------------
> 2 files changed, 16 insertions(+), 14 deletions(-)
>
>
> diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h
> index 3bf671637a91..148e0cb5d379 100644
> --- a/fs/xfs/libxfs/xfs_log_recover.h
> +++ b/fs/xfs/libxfs/xfs_log_recover.h
> @@ -22,13 +22,13 @@
> /*
> * item headers are in ri_buf[0]. Additional buffers follow.
> */
> -typedef struct xlog_recover_item {
> +struct xlog_recover_item {
> struct list_head ri_list;
> int ri_type;
> int ri_cnt; /* count of regions found */
> int ri_total; /* total regions */
> xfs_log_iovec_t *ri_buf; /* ptr to regions buffer */
> -} xlog_recover_item_t;
> +};
>
> struct xlog_recover {
> struct hlist_node r_list;
> diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
> index d0e2dd81de53..c2c06f70fb8a 100644
> --- a/fs/xfs/xfs_log_recover.c
> +++ b/fs/xfs/xfs_log_recover.c
> @@ -1841,7 +1841,7 @@ xlog_recover_reorder_trans(
> struct xlog_recover *trans,
> int pass)
> {
> - xlog_recover_item_t *item, *n;
> + struct xlog_recover_item *item, *n;
> int error = 0;
> LIST_HEAD(sort_list);
> LIST_HEAD(cancel_list);
> @@ -2056,7 +2056,7 @@ xlog_recover_buffer_pass1(
> STATIC int
> xlog_recover_do_inode_buffer(
> struct xfs_mount *mp,
> - xlog_recover_item_t *item,
> + struct xlog_recover_item *item,
> struct xfs_buf *bp,
> xfs_buf_log_format_t *buf_f)
> {
> @@ -2561,7 +2561,7 @@ xlog_recover_validate_buf_type(
> STATIC void
> xlog_recover_do_reg_buffer(
> struct xfs_mount *mp,
> - xlog_recover_item_t *item,
> + struct xlog_recover_item *item,
> struct xfs_buf *bp,
> xfs_buf_log_format_t *buf_f,
> xfs_lsn_t current_lsn)
> @@ -3759,7 +3759,7 @@ STATIC int
> xlog_recover_do_icreate_pass2(
> struct xlog *log,
> struct list_head *buffer_list,
> - xlog_recover_item_t *item)
> + struct xlog_recover_item *item)
> {
> struct xfs_mount *mp = log->l_mp;
> struct xfs_icreate_log *icl;
> @@ -4134,9 +4134,9 @@ STATIC void
> xlog_recover_add_item(
> struct list_head *head)
> {
> - xlog_recover_item_t *item;
> + struct xlog_recover_item *item;
>
> - item = kmem_zalloc(sizeof(xlog_recover_item_t), 0);
> + item = kmem_zalloc(sizeof(struct xlog_recover_item), 0);
> INIT_LIST_HEAD(&item->ri_list);
> list_add_tail(&item->ri_list, head);
> }
> @@ -4148,7 +4148,7 @@ xlog_recover_add_to_cont_trans(
> char *dp,
> int len)
> {
> - xlog_recover_item_t *item;
> + struct xlog_recover_item *item;
> char *ptr, *old_ptr;
> int old_len;
>
> @@ -4171,7 +4171,8 @@ xlog_recover_add_to_cont_trans(
> }
>
> /* take the tail entry */
> - item = list_entry(trans->r_itemq.prev, xlog_recover_item_t, ri_list);
> + item = list_entry(trans->r_itemq.prev, struct xlog_recover_item,
> + ri_list);
>
> old_ptr = item->ri_buf[item->ri_cnt-1].i_addr;
> old_len = item->ri_buf[item->ri_cnt-1].i_len;
> @@ -4205,7 +4206,7 @@ xlog_recover_add_to_trans(
> int len)
> {
> struct xfs_inode_log_format *in_f; /* any will do */
> - xlog_recover_item_t *item;
> + struct xlog_recover_item *item;
> char *ptr;
>
> if (!len)
> @@ -4241,13 +4242,14 @@ xlog_recover_add_to_trans(
> in_f = (struct xfs_inode_log_format *)ptr;
>
> /* take the tail entry */
> - item = list_entry(trans->r_itemq.prev, xlog_recover_item_t, ri_list);
> + item = list_entry(trans->r_itemq.prev, struct xlog_recover_item,
> + ri_list);
> if (item->ri_total != 0 &&
> item->ri_total == item->ri_cnt) {
> /* tail item is in use, get a new one */
> xlog_recover_add_item(&trans->r_itemq);
> item = list_entry(trans->r_itemq.prev,
> - xlog_recover_item_t, ri_list);
> + struct xlog_recover_item, ri_list);
> }
>
> if (item->ri_total == 0) { /* first region to be added */
> @@ -4293,7 +4295,7 @@ STATIC void
> xlog_recover_free_trans(
> struct xlog_recover *trans)
> {
> - xlog_recover_item_t *item, *n;
> + struct xlog_recover_item *item, *n;
> int i;
>
> hlist_del_init(&trans->r_list);
>
>
--
chandan
^ permalink raw reply [flat|nested] 94+ messages in thread* Re: [PATCH 01/28] xfs: convert xfs_log_recover_item_t to struct xfs_log_recover_item
2020-05-05 1:10 ` [PATCH 01/28] xfs: convert xfs_log_recover_item_t to struct xfs_log_recover_item Darrick J. Wong
2020-05-05 3:33 ` Chandan Babu R
@ 2020-05-06 14:59 ` Christoph Hellwig
1 sibling, 0 replies; 94+ messages in thread
From: Christoph Hellwig @ 2020-05-06 14:59 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-xfs
On Mon, May 04, 2020 at 06:10:39PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Remove the old typedefs.
>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Looks good,
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 94+ messages in thread
* [PATCH 02/28] xfs: refactor log recovery item sorting into a generic dispatch structure
2020-05-05 1:10 [PATCH v3 00/28] xfs: refactor log recovery Darrick J. Wong
2020-05-05 1:10 ` [PATCH 01/28] xfs: convert xfs_log_recover_item_t to struct xfs_log_recover_item Darrick J. Wong
@ 2020-05-05 1:10 ` Darrick J. Wong
2020-05-05 4:11 ` Chandan Babu R
2020-05-06 15:03 ` Christoph Hellwig
2020-05-05 1:10 ` [PATCH 03/28] xfs: refactor log recovery item dispatch for pass2 readhead functions Darrick J. Wong
` (25 subsequent siblings)
27 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2020-05-05 1:10 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Create a generic dispatch structure to delegate recovery of different
log item types into various code modules. This will enable us to move
code specific to a particular log item type out of xfs_log_recover.c and
into the log item source.
The first operation we virtualize is the log item sorting.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/Makefile | 3 +
fs/xfs/libxfs/xfs_log_recover.h | 45 ++++++++++++++++++-
fs/xfs/xfs_bmap_item.c | 9 ++++
fs/xfs/xfs_buf_item_recover.c | 38 ++++++++++++++++
fs/xfs/xfs_dquot_item_recover.c | 29 ++++++++++++
fs/xfs/xfs_extfree_item.c | 9 ++++
fs/xfs/xfs_icreate_item.c | 20 ++++++++
fs/xfs/xfs_inode_item_recover.c | 26 +++++++++++
fs/xfs/xfs_log_recover.c | 93 +++++++++++++++++++++++----------------
fs/xfs/xfs_refcount_item.c | 9 ++++
fs/xfs/xfs_rmap_item.c | 9 ++++
11 files changed, 251 insertions(+), 39 deletions(-)
create mode 100644 fs/xfs/xfs_buf_item_recover.c
create mode 100644 fs/xfs/xfs_dquot_item_recover.c
create mode 100644 fs/xfs/xfs_inode_item_recover.c
diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index ff94fb90a2ee..04611a1068b4 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -99,9 +99,12 @@ xfs-y += xfs_log.o \
xfs_log_cil.o \
xfs_bmap_item.o \
xfs_buf_item.o \
+ xfs_buf_item_recover.o \
+ xfs_dquot_item_recover.o \
xfs_extfree_item.o \
xfs_icreate_item.o \
xfs_inode_item.o \
+ xfs_inode_item_recover.o \
xfs_refcount_item.o \
xfs_rmap_item.o \
xfs_log_recover.o \
diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h
index 148e0cb5d379..271b0741f1e1 100644
--- a/fs/xfs/libxfs/xfs_log_recover.h
+++ b/fs/xfs/libxfs/xfs_log_recover.h
@@ -6,6 +6,47 @@
#ifndef __XFS_LOG_RECOVER_H__
#define __XFS_LOG_RECOVER_H__
+/*
+ * Each log item type (XFS_LI_*) gets its own xlog_recover_item_ops to
+ * define how recovery should work for that type of log item.
+ */
+struct xlog_recover_item;
+
+/* Sorting hat for log items as they're read in. */
+enum xlog_recover_reorder {
+ XLOG_REORDER_BUFFER_LIST,
+ XLOG_REORDER_ITEM_LIST,
+ XLOG_REORDER_INODE_BUFFER_LIST,
+ XLOG_REORDER_CANCEL_LIST,
+};
+
+struct xlog_recover_item_ops {
+ uint16_t item_type; /* XFS_LI_* type code. */
+
+ /*
+ * Help sort recovered log items into the order required to replay them
+ * correctly. Log item types that always use XLOG_REORDER_ITEM_LIST do
+ * not have to supply a function here. See the comment preceding
+ * xlog_recover_reorder_trans for more details about what the return
+ * values mean.
+ */
+ enum xlog_recover_reorder (*reorder)(struct xlog_recover_item *item);
+};
+
+extern const struct xlog_recover_item_ops xlog_icreate_item_ops;
+extern const struct xlog_recover_item_ops xlog_buf_item_ops;
+extern const struct xlog_recover_item_ops xlog_inode_item_ops;
+extern const struct xlog_recover_item_ops xlog_dquot_item_ops;
+extern const struct xlog_recover_item_ops xlog_quotaoff_item_ops;
+extern const struct xlog_recover_item_ops xlog_bmap_intent_item_ops;
+extern const struct xlog_recover_item_ops xlog_bmap_done_item_ops;
+extern const struct xlog_recover_item_ops xlog_extfree_intent_item_ops;
+extern const struct xlog_recover_item_ops xlog_extfree_done_item_ops;
+extern const struct xlog_recover_item_ops xlog_rmap_intent_item_ops;
+extern const struct xlog_recover_item_ops xlog_rmap_done_item_ops;
+extern const struct xlog_recover_item_ops xlog_refcount_intent_item_ops;
+extern const struct xlog_recover_item_ops xlog_refcount_done_item_ops;
+
/*
* Macros, structures, prototypes for internal log manager use.
*/
@@ -24,10 +65,10 @@
*/
struct xlog_recover_item {
struct list_head ri_list;
- int ri_type;
int ri_cnt; /* count of regions found */
int ri_total; /* total regions */
- xfs_log_iovec_t *ri_buf; /* ptr to regions buffer */
+ struct xfs_log_iovec *ri_buf; /* ptr to regions buffer */
+ const struct xlog_recover_item_ops *ri_ops;
};
struct xlog_recover {
diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c
index 7768fb2b7135..42354403fec7 100644
--- a/fs/xfs/xfs_bmap_item.c
+++ b/fs/xfs/xfs_bmap_item.c
@@ -22,6 +22,7 @@
#include "xfs_bmap_btree.h"
#include "xfs_trans_space.h"
#include "xfs_error.h"
+#include "xfs_log_recover.h"
kmem_zone_t *xfs_bui_zone;
kmem_zone_t *xfs_bud_zone;
@@ -557,3 +558,11 @@ xfs_bui_recover(
}
return error;
}
+
+const struct xlog_recover_item_ops xlog_bmap_intent_item_ops = {
+ .item_type = XFS_LI_BUI,
+};
+
+const struct xlog_recover_item_ops xlog_bmap_done_item_ops = {
+ .item_type = XFS_LI_BUD,
+};
diff --git a/fs/xfs/xfs_buf_item_recover.c b/fs/xfs/xfs_buf_item_recover.c
new file mode 100644
index 000000000000..def19025512e
--- /dev/null
+++ b/fs/xfs/xfs_buf_item_recover.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_bit.h"
+#include "xfs_mount.h"
+#include "xfs_trans.h"
+#include "xfs_buf_item.h"
+#include "xfs_trans_priv.h"
+#include "xfs_trace.h"
+#include "xfs_log.h"
+#include "xfs_log_priv.h"
+#include "xfs_log_recover.h"
+
+STATIC enum xlog_recover_reorder
+xlog_recover_buf_reorder(
+ struct xlog_recover_item *item)
+{
+ struct xfs_buf_log_format *buf_f = item->ri_buf[0].i_addr;
+
+ if (buf_f->blf_flags & XFS_BLF_CANCEL)
+ return XLOG_REORDER_CANCEL_LIST;
+ if (buf_f->blf_flags & XFS_BLF_INODE_BUF)
+ return XLOG_REORDER_INODE_BUFFER_LIST;
+ return XLOG_REORDER_BUFFER_LIST;
+}
+
+const struct xlog_recover_item_ops xlog_buf_item_ops = {
+ .item_type = XFS_LI_BUF,
+ .reorder = xlog_recover_buf_reorder,
+};
diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c
new file mode 100644
index 000000000000..78fe644e9907
--- /dev/null
+++ b/fs/xfs/xfs_dquot_item_recover.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_inode.h"
+#include "xfs_quota.h"
+#include "xfs_trans.h"
+#include "xfs_buf_item.h"
+#include "xfs_trans_priv.h"
+#include "xfs_qm.h"
+#include "xfs_log.h"
+#include "xfs_log_priv.h"
+#include "xfs_log_recover.h"
+
+const struct xlog_recover_item_ops xlog_dquot_item_ops = {
+ .item_type = XFS_LI_DQUOT,
+};
+
+const struct xlog_recover_item_ops xlog_quotaoff_item_ops = {
+ .item_type = XFS_LI_QUOTAOFF,
+};
diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c
index c8cde4122a0f..b43bb087aef3 100644
--- a/fs/xfs/xfs_extfree_item.c
+++ b/fs/xfs/xfs_extfree_item.c
@@ -22,6 +22,7 @@
#include "xfs_bmap.h"
#include "xfs_trace.h"
#include "xfs_error.h"
+#include "xfs_log_recover.h"
kmem_zone_t *xfs_efi_zone;
kmem_zone_t *xfs_efd_zone;
@@ -644,3 +645,11 @@ xfs_efi_recover(
xfs_trans_cancel(tp);
return error;
}
+
+const struct xlog_recover_item_ops xlog_extfree_intent_item_ops = {
+ .item_type = XFS_LI_EFI,
+};
+
+const struct xlog_recover_item_ops xlog_extfree_done_item_ops = {
+ .item_type = XFS_LI_EFD,
+};
diff --git a/fs/xfs/xfs_icreate_item.c b/fs/xfs/xfs_icreate_item.c
index 490fee22b878..366c1e722a29 100644
--- a/fs/xfs/xfs_icreate_item.c
+++ b/fs/xfs/xfs_icreate_item.c
@@ -11,6 +11,8 @@
#include "xfs_trans_priv.h"
#include "xfs_icreate_item.h"
#include "xfs_log.h"
+#include "xfs_log_priv.h"
+#include "xfs_log_recover.h"
kmem_zone_t *xfs_icreate_zone; /* inode create item zone */
@@ -107,3 +109,21 @@ xfs_icreate_log(
tp->t_flags |= XFS_TRANS_DIRTY;
set_bit(XFS_LI_DIRTY, &icp->ic_item.li_flags);
}
+
+static enum xlog_recover_reorder
+xlog_recover_icreate_reorder(
+ struct xlog_recover_item *item)
+{
+ /*
+ * Inode allocation buffers must be replayed before subsequent inode
+ * items try to modify those buffers. ICREATE items are the logical
+ * equivalent of logging a newly initialized inode buffer, so recover
+ * these at the same time that we recover logged buffers.
+ */
+ return XLOG_REORDER_BUFFER_LIST;
+}
+
+const struct xlog_recover_item_ops xlog_icreate_item_ops = {
+ .item_type = XFS_LI_ICREATE,
+ .reorder = xlog_recover_icreate_reorder,
+};
diff --git a/fs/xfs/xfs_inode_item_recover.c b/fs/xfs/xfs_inode_item_recover.c
new file mode 100644
index 000000000000..b19a151efb10
--- /dev/null
+++ b/fs/xfs/xfs_inode_item_recover.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_inode.h"
+#include "xfs_trans.h"
+#include "xfs_inode_item.h"
+#include "xfs_trace.h"
+#include "xfs_trans_priv.h"
+#include "xfs_buf_item.h"
+#include "xfs_log.h"
+#include "xfs_error.h"
+#include "xfs_log_priv.h"
+#include "xfs_log_recover.h"
+
+const struct xlog_recover_item_ops xlog_inode_item_ops = {
+ .item_type = XFS_LI_INODE,
+};
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index c2c06f70fb8a..0ef0d81fd190 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -1785,6 +1785,34 @@ xlog_clear_stale_blocks(
*
******************************************************************************
*/
+static const struct xlog_recover_item_ops *xlog_recover_item_ops[] = {
+ &xlog_buf_item_ops,
+ &xlog_inode_item_ops,
+ &xlog_dquot_item_ops,
+ &xlog_quotaoff_item_ops,
+ &xlog_icreate_item_ops,
+ &xlog_extfree_intent_item_ops,
+ &xlog_extfree_done_item_ops,
+ &xlog_rmap_intent_item_ops,
+ &xlog_rmap_done_item_ops,
+ &xlog_refcount_intent_item_ops,
+ &xlog_refcount_done_item_ops,
+ &xlog_bmap_intent_item_ops,
+ &xlog_bmap_done_item_ops,
+};
+
+static const struct xlog_recover_item_ops *
+xlog_find_item_ops(
+ struct xlog_recover_item *item)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(xlog_recover_item_ops); i++)
+ if (ITEM_TYPE(item) == xlog_recover_item_ops[i]->item_type)
+ return xlog_recover_item_ops[i];
+
+ return NULL;
+}
/*
* Sort the log items in the transaction.
@@ -1851,41 +1879,10 @@ xlog_recover_reorder_trans(
list_splice_init(&trans->r_itemq, &sort_list);
list_for_each_entry_safe(item, n, &sort_list, ri_list) {
- xfs_buf_log_format_t *buf_f = item->ri_buf[0].i_addr;
+ enum xlog_recover_reorder fate = XLOG_REORDER_ITEM_LIST;
- switch (ITEM_TYPE(item)) {
- case XFS_LI_ICREATE:
- list_move_tail(&item->ri_list, &buffer_list);
- break;
- case XFS_LI_BUF:
- if (buf_f->blf_flags & XFS_BLF_CANCEL) {
- trace_xfs_log_recover_item_reorder_head(log,
- trans, item, pass);
- list_move(&item->ri_list, &cancel_list);
- break;
- }
- if (buf_f->blf_flags & XFS_BLF_INODE_BUF) {
- list_move(&item->ri_list, &inode_buffer_list);
- break;
- }
- list_move_tail(&item->ri_list, &buffer_list);
- break;
- case XFS_LI_INODE:
- case XFS_LI_DQUOT:
- case XFS_LI_QUOTAOFF:
- case XFS_LI_EFD:
- 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, &item_list);
- break;
- default:
+ item->ri_ops = xlog_find_item_ops(item);
+ if (!item->ri_ops) {
xfs_warn(log->l_mp,
"%s: unrecognized type of log operation (%d)",
__func__, ITEM_TYPE(item));
@@ -1896,11 +1893,33 @@ xlog_recover_reorder_trans(
*/
if (!list_empty(&sort_list))
list_splice_init(&sort_list, &trans->r_itemq);
- error = -EIO;
- goto out;
+ error = -EFSCORRUPTED;
+ break;
+ }
+
+ if (item->ri_ops->reorder)
+ fate = item->ri_ops->reorder(item);
+
+ switch (fate) {
+ case XLOG_REORDER_BUFFER_LIST:
+ list_move_tail(&item->ri_list, &buffer_list);
+ break;
+ case XLOG_REORDER_CANCEL_LIST:
+ trace_xfs_log_recover_item_reorder_head(log,
+ trans, item, pass);
+ list_move(&item->ri_list, &cancel_list);
+ break;
+ case XLOG_REORDER_INODE_BUFFER_LIST:
+ list_move(&item->ri_list, &inode_buffer_list);
+ break;
+ case XLOG_REORDER_ITEM_LIST:
+ trace_xfs_log_recover_item_reorder_tail(log,
+ trans, item, pass);
+ list_move_tail(&item->ri_list, &item_list);
+ break;
}
}
-out:
+
ASSERT(list_empty(&sort_list));
if (!list_empty(&buffer_list))
list_splice(&buffer_list, &trans->r_itemq);
diff --git a/fs/xfs/xfs_refcount_item.c b/fs/xfs/xfs_refcount_item.c
index 0316eab2fc35..0e8e8bab4344 100644
--- a/fs/xfs/xfs_refcount_item.c
+++ b/fs/xfs/xfs_refcount_item.c
@@ -18,6 +18,7 @@
#include "xfs_log.h"
#include "xfs_refcount.h"
#include "xfs_error.h"
+#include "xfs_log_recover.h"
kmem_zone_t *xfs_cui_zone;
kmem_zone_t *xfs_cud_zone;
@@ -570,3 +571,11 @@ xfs_cui_recover(
xfs_trans_cancel(tp);
return error;
}
+
+const struct xlog_recover_item_ops xlog_refcount_intent_item_ops = {
+ .item_type = XFS_LI_CUI,
+};
+
+const struct xlog_recover_item_ops xlog_refcount_done_item_ops = {
+ .item_type = XFS_LI_CUD,
+};
diff --git a/fs/xfs/xfs_rmap_item.c b/fs/xfs/xfs_rmap_item.c
index e3bba2aec868..3eb538674cb9 100644
--- a/fs/xfs/xfs_rmap_item.c
+++ b/fs/xfs/xfs_rmap_item.c
@@ -18,6 +18,7 @@
#include "xfs_log.h"
#include "xfs_rmap.h"
#include "xfs_error.h"
+#include "xfs_log_recover.h"
kmem_zone_t *xfs_rui_zone;
kmem_zone_t *xfs_rud_zone;
@@ -585,3 +586,11 @@ xfs_rui_recover(
xfs_trans_cancel(tp);
return error;
}
+
+const struct xlog_recover_item_ops xlog_rmap_intent_item_ops = {
+ .item_type = XFS_LI_RUI,
+};
+
+const struct xlog_recover_item_ops xlog_rmap_done_item_ops = {
+ .item_type = XFS_LI_RUD,
+};
^ permalink raw reply related [flat|nested] 94+ messages in thread* Re: [PATCH 02/28] xfs: refactor log recovery item sorting into a generic dispatch structure
2020-05-05 1:10 ` [PATCH 02/28] xfs: refactor log recovery item sorting into a generic dispatch structure Darrick J. Wong
@ 2020-05-05 4:11 ` Chandan Babu R
2020-05-06 15:03 ` Christoph Hellwig
1 sibling, 0 replies; 94+ messages in thread
From: Chandan Babu R @ 2020-05-05 4:11 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-xfs
On Tuesday 5 May 2020 6:40:45 AM IST Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Create a generic dispatch structure to delegate recovery of different
> log item types into various code modules. This will enable us to move
> code specific to a particular log item type out of xfs_log_recover.c and
> into the log item source.
>
> The first operation we virtualize is the log item sorting.
>
The sorted list order is the maintained as it was done before.
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
> fs/xfs/Makefile | 3 +
> fs/xfs/libxfs/xfs_log_recover.h | 45 ++++++++++++++++++-
> fs/xfs/xfs_bmap_item.c | 9 ++++
> fs/xfs/xfs_buf_item_recover.c | 38 ++++++++++++++++
> fs/xfs/xfs_dquot_item_recover.c | 29 ++++++++++++
> fs/xfs/xfs_extfree_item.c | 9 ++++
> fs/xfs/xfs_icreate_item.c | 20 ++++++++
> fs/xfs/xfs_inode_item_recover.c | 26 +++++++++++
> fs/xfs/xfs_log_recover.c | 93 +++++++++++++++++++++++----------------
> fs/xfs/xfs_refcount_item.c | 9 ++++
> fs/xfs/xfs_rmap_item.c | 9 ++++
> 11 files changed, 251 insertions(+), 39 deletions(-)
> create mode 100644 fs/xfs/xfs_buf_item_recover.c
> create mode 100644 fs/xfs/xfs_dquot_item_recover.c
> create mode 100644 fs/xfs/xfs_inode_item_recover.c
>
>
> diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
> index ff94fb90a2ee..04611a1068b4 100644
> --- a/fs/xfs/Makefile
> +++ b/fs/xfs/Makefile
> @@ -99,9 +99,12 @@ xfs-y += xfs_log.o \
> xfs_log_cil.o \
> xfs_bmap_item.o \
> xfs_buf_item.o \
> + xfs_buf_item_recover.o \
> + xfs_dquot_item_recover.o \
> xfs_extfree_item.o \
> xfs_icreate_item.o \
> xfs_inode_item.o \
> + xfs_inode_item_recover.o \
> xfs_refcount_item.o \
> xfs_rmap_item.o \
> xfs_log_recover.o \
> diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h
> index 148e0cb5d379..271b0741f1e1 100644
> --- a/fs/xfs/libxfs/xfs_log_recover.h
> +++ b/fs/xfs/libxfs/xfs_log_recover.h
> @@ -6,6 +6,47 @@
> #ifndef __XFS_LOG_RECOVER_H__
> #define __XFS_LOG_RECOVER_H__
>
> +/*
> + * Each log item type (XFS_LI_*) gets its own xlog_recover_item_ops to
> + * define how recovery should work for that type of log item.
> + */
> +struct xlog_recover_item;
> +
> +/* Sorting hat for log items as they're read in. */
> +enum xlog_recover_reorder {
> + XLOG_REORDER_BUFFER_LIST,
> + XLOG_REORDER_ITEM_LIST,
> + XLOG_REORDER_INODE_BUFFER_LIST,
> + XLOG_REORDER_CANCEL_LIST,
> +};
> +
> +struct xlog_recover_item_ops {
> + uint16_t item_type; /* XFS_LI_* type code. */
> +
> + /*
> + * Help sort recovered log items into the order required to replay them
> + * correctly. Log item types that always use XLOG_REORDER_ITEM_LIST do
> + * not have to supply a function here. See the comment preceding
> + * xlog_recover_reorder_trans for more details about what the return
> + * values mean.
> + */
> + enum xlog_recover_reorder (*reorder)(struct xlog_recover_item *item);
> +};
> +
> +extern const struct xlog_recover_item_ops xlog_icreate_item_ops;
> +extern const struct xlog_recover_item_ops xlog_buf_item_ops;
> +extern const struct xlog_recover_item_ops xlog_inode_item_ops;
> +extern const struct xlog_recover_item_ops xlog_dquot_item_ops;
> +extern const struct xlog_recover_item_ops xlog_quotaoff_item_ops;
> +extern const struct xlog_recover_item_ops xlog_bmap_intent_item_ops;
> +extern const struct xlog_recover_item_ops xlog_bmap_done_item_ops;
> +extern const struct xlog_recover_item_ops xlog_extfree_intent_item_ops;
> +extern const struct xlog_recover_item_ops xlog_extfree_done_item_ops;
> +extern const struct xlog_recover_item_ops xlog_rmap_intent_item_ops;
> +extern const struct xlog_recover_item_ops xlog_rmap_done_item_ops;
> +extern const struct xlog_recover_item_ops xlog_refcount_intent_item_ops;
> +extern const struct xlog_recover_item_ops xlog_refcount_done_item_ops;
> +
> /*
> * Macros, structures, prototypes for internal log manager use.
> */
> @@ -24,10 +65,10 @@
> */
> struct xlog_recover_item {
> struct list_head ri_list;
> - int ri_type;
> int ri_cnt; /* count of regions found */
> int ri_total; /* total regions */
> - xfs_log_iovec_t *ri_buf; /* ptr to regions buffer */
> + struct xfs_log_iovec *ri_buf; /* ptr to regions buffer */
> + const struct xlog_recover_item_ops *ri_ops;
> };
>
> struct xlog_recover {
> diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c
> index 7768fb2b7135..42354403fec7 100644
> --- a/fs/xfs/xfs_bmap_item.c
> +++ b/fs/xfs/xfs_bmap_item.c
> @@ -22,6 +22,7 @@
> #include "xfs_bmap_btree.h"
> #include "xfs_trans_space.h"
> #include "xfs_error.h"
> +#include "xfs_log_recover.h"
>
> kmem_zone_t *xfs_bui_zone;
> kmem_zone_t *xfs_bud_zone;
> @@ -557,3 +558,11 @@ xfs_bui_recover(
> }
> return error;
> }
> +
> +const struct xlog_recover_item_ops xlog_bmap_intent_item_ops = {
> + .item_type = XFS_LI_BUI,
> +};
> +
> +const struct xlog_recover_item_ops xlog_bmap_done_item_ops = {
> + .item_type = XFS_LI_BUD,
> +};
> diff --git a/fs/xfs/xfs_buf_item_recover.c b/fs/xfs/xfs_buf_item_recover.c
> new file mode 100644
> index 000000000000..def19025512e
> --- /dev/null
> +++ b/fs/xfs/xfs_buf_item_recover.c
> @@ -0,0 +1,38 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2000-2006 Silicon Graphics, Inc.
> + * All Rights Reserved.
> + */
> +#include "xfs.h"
> +#include "xfs_fs.h"
> +#include "xfs_shared.h"
> +#include "xfs_format.h"
> +#include "xfs_log_format.h"
> +#include "xfs_trans_resv.h"
> +#include "xfs_bit.h"
> +#include "xfs_mount.h"
> +#include "xfs_trans.h"
> +#include "xfs_buf_item.h"
> +#include "xfs_trans_priv.h"
> +#include "xfs_trace.h"
> +#include "xfs_log.h"
> +#include "xfs_log_priv.h"
> +#include "xfs_log_recover.h"
> +
> +STATIC enum xlog_recover_reorder
> +xlog_recover_buf_reorder(
> + struct xlog_recover_item *item)
> +{
> + struct xfs_buf_log_format *buf_f = item->ri_buf[0].i_addr;
> +
> + if (buf_f->blf_flags & XFS_BLF_CANCEL)
> + return XLOG_REORDER_CANCEL_LIST;
> + if (buf_f->blf_flags & XFS_BLF_INODE_BUF)
> + return XLOG_REORDER_INODE_BUFFER_LIST;
> + return XLOG_REORDER_BUFFER_LIST;
> +}
> +
> +const struct xlog_recover_item_ops xlog_buf_item_ops = {
> + .item_type = XFS_LI_BUF,
> + .reorder = xlog_recover_buf_reorder,
> +};
> diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c
> new file mode 100644
> index 000000000000..78fe644e9907
> --- /dev/null
> +++ b/fs/xfs/xfs_dquot_item_recover.c
> @@ -0,0 +1,29 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2000-2006 Silicon Graphics, Inc.
> + * All Rights Reserved.
> + */
> +#include "xfs.h"
> +#include "xfs_fs.h"
> +#include "xfs_shared.h"
> +#include "xfs_format.h"
> +#include "xfs_log_format.h"
> +#include "xfs_trans_resv.h"
> +#include "xfs_mount.h"
> +#include "xfs_inode.h"
> +#include "xfs_quota.h"
> +#include "xfs_trans.h"
> +#include "xfs_buf_item.h"
> +#include "xfs_trans_priv.h"
> +#include "xfs_qm.h"
> +#include "xfs_log.h"
> +#include "xfs_log_priv.h"
> +#include "xfs_log_recover.h"
> +
> +const struct xlog_recover_item_ops xlog_dquot_item_ops = {
> + .item_type = XFS_LI_DQUOT,
> +};
> +
> +const struct xlog_recover_item_ops xlog_quotaoff_item_ops = {
> + .item_type = XFS_LI_QUOTAOFF,
> +};
> diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c
> index c8cde4122a0f..b43bb087aef3 100644
> --- a/fs/xfs/xfs_extfree_item.c
> +++ b/fs/xfs/xfs_extfree_item.c
> @@ -22,6 +22,7 @@
> #include "xfs_bmap.h"
> #include "xfs_trace.h"
> #include "xfs_error.h"
> +#include "xfs_log_recover.h"
>
> kmem_zone_t *xfs_efi_zone;
> kmem_zone_t *xfs_efd_zone;
> @@ -644,3 +645,11 @@ xfs_efi_recover(
> xfs_trans_cancel(tp);
> return error;
> }
> +
> +const struct xlog_recover_item_ops xlog_extfree_intent_item_ops = {
> + .item_type = XFS_LI_EFI,
> +};
> +
> +const struct xlog_recover_item_ops xlog_extfree_done_item_ops = {
> + .item_type = XFS_LI_EFD,
> +};
> diff --git a/fs/xfs/xfs_icreate_item.c b/fs/xfs/xfs_icreate_item.c
> index 490fee22b878..366c1e722a29 100644
> --- a/fs/xfs/xfs_icreate_item.c
> +++ b/fs/xfs/xfs_icreate_item.c
> @@ -11,6 +11,8 @@
> #include "xfs_trans_priv.h"
> #include "xfs_icreate_item.h"
> #include "xfs_log.h"
> +#include "xfs_log_priv.h"
> +#include "xfs_log_recover.h"
>
> kmem_zone_t *xfs_icreate_zone; /* inode create item zone */
>
> @@ -107,3 +109,21 @@ xfs_icreate_log(
> tp->t_flags |= XFS_TRANS_DIRTY;
> set_bit(XFS_LI_DIRTY, &icp->ic_item.li_flags);
> }
> +
> +static enum xlog_recover_reorder
> +xlog_recover_icreate_reorder(
> + struct xlog_recover_item *item)
> +{
> + /*
> + * Inode allocation buffers must be replayed before subsequent inode
> + * items try to modify those buffers. ICREATE items are the logical
> + * equivalent of logging a newly initialized inode buffer, so recover
> + * these at the same time that we recover logged buffers.
> + */
> + return XLOG_REORDER_BUFFER_LIST;
> +}
> +
> +const struct xlog_recover_item_ops xlog_icreate_item_ops = {
> + .item_type = XFS_LI_ICREATE,
> + .reorder = xlog_recover_icreate_reorder,
> +};
> diff --git a/fs/xfs/xfs_inode_item_recover.c b/fs/xfs/xfs_inode_item_recover.c
> new file mode 100644
> index 000000000000..b19a151efb10
> --- /dev/null
> +++ b/fs/xfs/xfs_inode_item_recover.c
> @@ -0,0 +1,26 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2000-2006 Silicon Graphics, Inc.
> + * All Rights Reserved.
> + */
> +#include "xfs.h"
> +#include "xfs_fs.h"
> +#include "xfs_shared.h"
> +#include "xfs_format.h"
> +#include "xfs_log_format.h"
> +#include "xfs_trans_resv.h"
> +#include "xfs_mount.h"
> +#include "xfs_inode.h"
> +#include "xfs_trans.h"
> +#include "xfs_inode_item.h"
> +#include "xfs_trace.h"
> +#include "xfs_trans_priv.h"
> +#include "xfs_buf_item.h"
> +#include "xfs_log.h"
> +#include "xfs_error.h"
> +#include "xfs_log_priv.h"
> +#include "xfs_log_recover.h"
> +
> +const struct xlog_recover_item_ops xlog_inode_item_ops = {
> + .item_type = XFS_LI_INODE,
> +};
> diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
> index c2c06f70fb8a..0ef0d81fd190 100644
> --- a/fs/xfs/xfs_log_recover.c
> +++ b/fs/xfs/xfs_log_recover.c
> @@ -1785,6 +1785,34 @@ xlog_clear_stale_blocks(
> *
> ******************************************************************************
> */
> +static const struct xlog_recover_item_ops *xlog_recover_item_ops[] = {
> + &xlog_buf_item_ops,
> + &xlog_inode_item_ops,
> + &xlog_dquot_item_ops,
> + &xlog_quotaoff_item_ops,
> + &xlog_icreate_item_ops,
> + &xlog_extfree_intent_item_ops,
> + &xlog_extfree_done_item_ops,
> + &xlog_rmap_intent_item_ops,
> + &xlog_rmap_done_item_ops,
> + &xlog_refcount_intent_item_ops,
> + &xlog_refcount_done_item_ops,
> + &xlog_bmap_intent_item_ops,
> + &xlog_bmap_done_item_ops,
> +};
> +
> +static const struct xlog_recover_item_ops *
> +xlog_find_item_ops(
> + struct xlog_recover_item *item)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < ARRAY_SIZE(xlog_recover_item_ops); i++)
> + if (ITEM_TYPE(item) == xlog_recover_item_ops[i]->item_type)
> + return xlog_recover_item_ops[i];
> +
> + return NULL;
> +}
>
> /*
> * Sort the log items in the transaction.
> @@ -1851,41 +1879,10 @@ xlog_recover_reorder_trans(
>
> list_splice_init(&trans->r_itemq, &sort_list);
> list_for_each_entry_safe(item, n, &sort_list, ri_list) {
> - xfs_buf_log_format_t *buf_f = item->ri_buf[0].i_addr;
> + enum xlog_recover_reorder fate = XLOG_REORDER_ITEM_LIST;
>
> - switch (ITEM_TYPE(item)) {
> - case XFS_LI_ICREATE:
> - list_move_tail(&item->ri_list, &buffer_list);
> - break;
> - case XFS_LI_BUF:
> - if (buf_f->blf_flags & XFS_BLF_CANCEL) {
> - trace_xfs_log_recover_item_reorder_head(log,
> - trans, item, pass);
> - list_move(&item->ri_list, &cancel_list);
> - break;
> - }
> - if (buf_f->blf_flags & XFS_BLF_INODE_BUF) {
> - list_move(&item->ri_list, &inode_buffer_list);
> - break;
> - }
> - list_move_tail(&item->ri_list, &buffer_list);
> - break;
> - case XFS_LI_INODE:
> - case XFS_LI_DQUOT:
> - case XFS_LI_QUOTAOFF:
> - case XFS_LI_EFD:
> - 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, &item_list);
> - break;
> - default:
> + item->ri_ops = xlog_find_item_ops(item);
> + if (!item->ri_ops) {
> xfs_warn(log->l_mp,
> "%s: unrecognized type of log operation (%d)",
> __func__, ITEM_TYPE(item));
> @@ -1896,11 +1893,33 @@ xlog_recover_reorder_trans(
> */
> if (!list_empty(&sort_list))
> list_splice_init(&sort_list, &trans->r_itemq);
> - error = -EIO;
> - goto out;
> + error = -EFSCORRUPTED;
> + break;
> + }
> +
> + if (item->ri_ops->reorder)
> + fate = item->ri_ops->reorder(item);
> +
> + switch (fate) {
> + case XLOG_REORDER_BUFFER_LIST:
> + list_move_tail(&item->ri_list, &buffer_list);
> + break;
> + case XLOG_REORDER_CANCEL_LIST:
> + trace_xfs_log_recover_item_reorder_head(log,
> + trans, item, pass);
> + list_move(&item->ri_list, &cancel_list);
> + break;
> + case XLOG_REORDER_INODE_BUFFER_LIST:
> + list_move(&item->ri_list, &inode_buffer_list);
> + break;
> + case XLOG_REORDER_ITEM_LIST:
> + trace_xfs_log_recover_item_reorder_tail(log,
> + trans, item, pass);
> + list_move_tail(&item->ri_list, &item_list);
> + break;
> }
> }
> -out:
> +
> ASSERT(list_empty(&sort_list));
> if (!list_empty(&buffer_list))
> list_splice(&buffer_list, &trans->r_itemq);
> diff --git a/fs/xfs/xfs_refcount_item.c b/fs/xfs/xfs_refcount_item.c
> index 0316eab2fc35..0e8e8bab4344 100644
> --- a/fs/xfs/xfs_refcount_item.c
> +++ b/fs/xfs/xfs_refcount_item.c
> @@ -18,6 +18,7 @@
> #include "xfs_log.h"
> #include "xfs_refcount.h"
> #include "xfs_error.h"
> +#include "xfs_log_recover.h"
>
> kmem_zone_t *xfs_cui_zone;
> kmem_zone_t *xfs_cud_zone;
> @@ -570,3 +571,11 @@ xfs_cui_recover(
> xfs_trans_cancel(tp);
> return error;
> }
> +
> +const struct xlog_recover_item_ops xlog_refcount_intent_item_ops = {
> + .item_type = XFS_LI_CUI,
> +};
> +
> +const struct xlog_recover_item_ops xlog_refcount_done_item_ops = {
> + .item_type = XFS_LI_CUD,
> +};
> diff --git a/fs/xfs/xfs_rmap_item.c b/fs/xfs/xfs_rmap_item.c
> index e3bba2aec868..3eb538674cb9 100644
> --- a/fs/xfs/xfs_rmap_item.c
> +++ b/fs/xfs/xfs_rmap_item.c
> @@ -18,6 +18,7 @@
> #include "xfs_log.h"
> #include "xfs_rmap.h"
> #include "xfs_error.h"
> +#include "xfs_log_recover.h"
>
> kmem_zone_t *xfs_rui_zone;
> kmem_zone_t *xfs_rud_zone;
> @@ -585,3 +586,11 @@ xfs_rui_recover(
> xfs_trans_cancel(tp);
> return error;
> }
> +
> +const struct xlog_recover_item_ops xlog_rmap_intent_item_ops = {
> + .item_type = XFS_LI_RUI,
> +};
> +
> +const struct xlog_recover_item_ops xlog_rmap_done_item_ops = {
> + .item_type = XFS_LI_RUD,
> +};
>
>
--
chandan
^ permalink raw reply [flat|nested] 94+ messages in thread* Re: [PATCH 02/28] xfs: refactor log recovery item sorting into a generic dispatch structure
2020-05-05 1:10 ` [PATCH 02/28] xfs: refactor log recovery item sorting into a generic dispatch structure Darrick J. Wong
2020-05-05 4:11 ` Chandan Babu R
@ 2020-05-06 15:03 ` Christoph Hellwig
2020-05-06 18:36 ` Darrick J. Wong
1 sibling, 1 reply; 94+ messages in thread
From: Christoph Hellwig @ 2020-05-06 15:03 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-xfs
On Mon, May 04, 2020 at 06:10:45PM -0700, Darrick J. Wong wrote:
> +const struct xlog_recover_item_ops xlog_bmap_intent_item_ops = {
> + .item_type = XFS_LI_BUI,
> +};
> +
> +const struct xlog_recover_item_ops xlog_bmap_done_item_ops = {
> + .item_type = XFS_LI_BUD,
> +};
Pretty much everything else in this file seems to use bui/bud names.
The same also applies to the four other intent/done pairs and their
shortnames. Not really a major thing, but it might be worth fixing
to fit the flow.
> +STATIC enum xlog_recover_reorder
> +xlog_recover_buf_reorder(
> + struct xlog_recover_item *item)
> +{
> + struct xfs_buf_log_format *buf_f = item->ri_buf[0].i_addr;
> +
> + if (buf_f->blf_flags & XFS_BLF_CANCEL)
> + return XLOG_REORDER_CANCEL_LIST;
> + if (buf_f->blf_flags & XFS_BLF_INODE_BUF)
> + return XLOG_REORDER_INODE_BUFFER_LIST;
> + return XLOG_REORDER_BUFFER_LIST;
> +}
While you split this out a comment explaining the reordering would
be nice here.
Otherwise this looks great:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 94+ messages in thread* Re: [PATCH 02/28] xfs: refactor log recovery item sorting into a generic dispatch structure
2020-05-06 15:03 ` Christoph Hellwig
@ 2020-05-06 18:36 ` Darrick J. Wong
0 siblings, 0 replies; 94+ messages in thread
From: Darrick J. Wong @ 2020-05-06 18:36 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: linux-xfs
On Wed, May 06, 2020 at 08:03:24AM -0700, Christoph Hellwig wrote:
> On Mon, May 04, 2020 at 06:10:45PM -0700, Darrick J. Wong wrote:
> > +const struct xlog_recover_item_ops xlog_bmap_intent_item_ops = {
> > + .item_type = XFS_LI_BUI,
> > +};
> > +
> > +const struct xlog_recover_item_ops xlog_bmap_done_item_ops = {
> > + .item_type = XFS_LI_BUD,
> > +};
>
> Pretty much everything else in this file seems to use bui/bud names.
> The same also applies to the four other intent/done pairs and their
> shortnames. Not really a major thing, but it might be worth fixing
> to fit the flow.
Ok.
> > +STATIC enum xlog_recover_reorder
> > +xlog_recover_buf_reorder(
> > + struct xlog_recover_item *item)
> > +{
> > + struct xfs_buf_log_format *buf_f = item->ri_buf[0].i_addr;
> > +
> > + if (buf_f->blf_flags & XFS_BLF_CANCEL)
> > + return XLOG_REORDER_CANCEL_LIST;
> > + if (buf_f->blf_flags & XFS_BLF_INODE_BUF)
> > + return XLOG_REORDER_INODE_BUFFER_LIST;
> > + return XLOG_REORDER_BUFFER_LIST;
> > +}
>
> While you split this out a comment explaining the reordering would
> be nice here.
Ok.
/*
* Sort buffer items for log recovery. Most buffer items should end up
* on the buffer list and are recovered first, with the following
* exceptions:
*
* 1. XFS_BLF_CANCEL buffers must be processed last because some log
* items might depend on the incor ecancellation record, and
* replaying a cancelled buffer item can remove the incore record.
*
* 2. XFS_BLF_INODE_BUF buffers are handled after most regular items so
* that we replay di_next_unlinked only after flushing the inode
* 'free' state to the inode buffer.
*
* See xlog_recover_reorder_trans for more details.
*/
--D
> Otherwise this looks great:
>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 94+ messages in thread
* [PATCH 03/28] xfs: refactor log recovery item dispatch for pass2 readhead functions
2020-05-05 1:10 [PATCH v3 00/28] xfs: refactor log recovery Darrick J. Wong
2020-05-05 1:10 ` [PATCH 01/28] xfs: convert xfs_log_recover_item_t to struct xfs_log_recover_item Darrick J. Wong
2020-05-05 1:10 ` [PATCH 02/28] xfs: refactor log recovery item sorting into a generic dispatch structure Darrick J. Wong
@ 2020-05-05 1:10 ` Darrick J. Wong
2020-05-05 4:32 ` Chandan Babu R
2020-05-06 15:04 ` Christoph Hellwig
2020-05-05 1:10 ` [PATCH 04/28] xfs: refactor log recovery item dispatch for pass1 commit functions Darrick J. Wong
` (24 subsequent siblings)
27 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2020-05-05 1:10 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Move the pass2 readhead code into the per-item source code files and use
the dispatch function to call them.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_log_recover.h | 6 ++
fs/xfs/xfs_buf_item_recover.c | 11 +++++
fs/xfs/xfs_dquot_item_recover.c | 34 ++++++++++++++
fs/xfs/xfs_inode_item_recover.c | 19 ++++++++
fs/xfs/xfs_log_recover.c | 95 +--------------------------------------
5 files changed, 73 insertions(+), 92 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h
index 271b0741f1e1..ff80871138bb 100644
--- a/fs/xfs/libxfs/xfs_log_recover.h
+++ b/fs/xfs/libxfs/xfs_log_recover.h
@@ -31,6 +31,9 @@ struct xlog_recover_item_ops {
* values mean.
*/
enum xlog_recover_reorder (*reorder)(struct xlog_recover_item *item);
+
+ /* Start readahead for pass2, if provided. */
+ void (*ra_pass2)(struct xlog *log, struct xlog_recover_item *item);
};
extern const struct xlog_recover_item_ops xlog_icreate_item_ops;
@@ -92,4 +95,7 @@ struct xlog_recover {
#define XLOG_RECOVER_PASS1 1
#define XLOG_RECOVER_PASS2 2
+void xlog_buf_readahead(struct xlog *log, xfs_daddr_t blkno, uint len,
+ const struct xfs_buf_ops *ops);
+
#endif /* __XFS_LOG_RECOVER_H__ */
diff --git a/fs/xfs/xfs_buf_item_recover.c b/fs/xfs/xfs_buf_item_recover.c
index def19025512e..a1327196b690 100644
--- a/fs/xfs/xfs_buf_item_recover.c
+++ b/fs/xfs/xfs_buf_item_recover.c
@@ -32,7 +32,18 @@ xlog_recover_buf_reorder(
return XLOG_REORDER_BUFFER_LIST;
}
+STATIC void
+xlog_recover_buf_ra_pass2(
+ struct xlog *log,
+ struct xlog_recover_item *item)
+{
+ struct xfs_buf_log_format *buf_f = item->ri_buf[0].i_addr;
+
+ xlog_buf_readahead(log, buf_f->blf_blkno, buf_f->blf_len, NULL);
+}
+
const struct xlog_recover_item_ops xlog_buf_item_ops = {
.item_type = XFS_LI_BUF,
.reorder = xlog_recover_buf_reorder,
+ .ra_pass2 = xlog_recover_buf_ra_pass2,
};
diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c
index 78fe644e9907..215274173b70 100644
--- a/fs/xfs/xfs_dquot_item_recover.c
+++ b/fs/xfs/xfs_dquot_item_recover.c
@@ -20,8 +20,42 @@
#include "xfs_log_priv.h"
#include "xfs_log_recover.h"
+STATIC void
+xlog_recover_dquot_ra_pass2(
+ struct xlog *log,
+ struct xlog_recover_item *item)
+{
+ struct xfs_mount *mp = log->l_mp;
+ struct xfs_disk_dquot *recddq;
+ struct xfs_dq_logformat *dq_f;
+ uint type;
+
+ if (mp->m_qflags == 0)
+ return;
+
+ recddq = item->ri_buf[1].i_addr;
+ if (recddq == NULL)
+ return;
+ if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot))
+ return;
+
+ type = recddq->d_flags & (XFS_DQ_USER | XFS_DQ_PROJ | XFS_DQ_GROUP);
+ ASSERT(type);
+ if (log->l_quotaoffs_flag & type)
+ return;
+
+ dq_f = item->ri_buf[0].i_addr;
+ ASSERT(dq_f);
+ ASSERT(dq_f->qlf_len == 1);
+
+ xlog_buf_readahead(log, dq_f->qlf_blkno,
+ XFS_FSB_TO_BB(mp, dq_f->qlf_len),
+ &xfs_dquot_buf_ra_ops);
+}
+
const struct xlog_recover_item_ops xlog_dquot_item_ops = {
.item_type = XFS_LI_DQUOT,
+ .ra_pass2 = xlog_recover_dquot_ra_pass2,
};
const struct xlog_recover_item_ops xlog_quotaoff_item_ops = {
diff --git a/fs/xfs/xfs_inode_item_recover.c b/fs/xfs/xfs_inode_item_recover.c
index b19a151efb10..a132cacd8d48 100644
--- a/fs/xfs/xfs_inode_item_recover.c
+++ b/fs/xfs/xfs_inode_item_recover.c
@@ -21,6 +21,25 @@
#include "xfs_log_priv.h"
#include "xfs_log_recover.h"
+STATIC void
+xlog_recover_inode_ra_pass2(
+ struct xlog *log,
+ struct xlog_recover_item *item)
+{
+ if (item->ri_buf[0].i_len == sizeof(struct xfs_inode_log_format)) {
+ struct xfs_inode_log_format *ilfp = item->ri_buf[0].i_addr;
+
+ xlog_buf_readahead(log, ilfp->ilf_blkno, ilfp->ilf_len,
+ &xfs_inode_buf_ra_ops);
+ } else {
+ struct xfs_inode_log_format_32 *ilfp = item->ri_buf[0].i_addr;
+
+ xlog_buf_readahead(log, ilfp->ilf_blkno, ilfp->ilf_len,
+ &xfs_inode_buf_ra_ops);
+ }
+}
+
const struct xlog_recover_item_ops xlog_inode_item_ops = {
.item_type = XFS_LI_INODE,
+ .ra_pass2 = xlog_recover_inode_ra_pass2,
};
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 0ef0d81fd190..ea566747d8e1 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -2023,7 +2023,7 @@ xlog_put_buffer_cancelled(
return true;
}
-static void
+void
xlog_buf_readahead(
struct xlog *log,
xfs_daddr_t blkno,
@@ -3890,96 +3890,6 @@ xlog_recover_do_icreate_pass2(
length, be32_to_cpu(icl->icl_gen));
}
-STATIC void
-xlog_recover_buffer_ra_pass2(
- struct xlog *log,
- struct xlog_recover_item *item)
-{
- struct xfs_buf_log_format *buf_f = item->ri_buf[0].i_addr;
-
- xlog_buf_readahead(log, buf_f->blf_blkno, buf_f->blf_len, NULL);
-}
-
-STATIC void
-xlog_recover_inode_ra_pass2(
- struct xlog *log,
- struct xlog_recover_item *item)
-{
- if (item->ri_buf[0].i_len == sizeof(struct xfs_inode_log_format)) {
- struct xfs_inode_log_format *ilfp = item->ri_buf[0].i_addr;
-
- xlog_buf_readahead(log, ilfp->ilf_blkno, ilfp->ilf_len,
- &xfs_inode_buf_ra_ops);
- } else {
- struct xfs_inode_log_format_32 *ilfp = item->ri_buf[0].i_addr;
-
- xlog_buf_readahead(log, ilfp->ilf_blkno, ilfp->ilf_len,
- &xfs_inode_buf_ra_ops);
- }
-}
-
-STATIC void
-xlog_recover_dquot_ra_pass2(
- struct xlog *log,
- struct xlog_recover_item *item)
-{
- struct xfs_mount *mp = log->l_mp;
- struct xfs_disk_dquot *recddq;
- struct xfs_dq_logformat *dq_f;
- uint type;
-
- if (mp->m_qflags == 0)
- return;
-
- recddq = item->ri_buf[1].i_addr;
- if (recddq == NULL)
- return;
- if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot))
- return;
-
- type = recddq->d_flags & (XFS_DQ_USER | XFS_DQ_PROJ | XFS_DQ_GROUP);
- ASSERT(type);
- if (log->l_quotaoffs_flag & type)
- return;
-
- dq_f = item->ri_buf[0].i_addr;
- ASSERT(dq_f);
- ASSERT(dq_f->qlf_len == 1);
-
- xlog_buf_readahead(log, dq_f->qlf_blkno,
- XFS_FSB_TO_BB(mp, dq_f->qlf_len),
- &xfs_dquot_buf_ra_ops);
-}
-
-STATIC void
-xlog_recover_ra_pass2(
- struct xlog *log,
- struct xlog_recover_item *item)
-{
- switch (ITEM_TYPE(item)) {
- case XFS_LI_BUF:
- xlog_recover_buffer_ra_pass2(log, item);
- break;
- case XFS_LI_INODE:
- xlog_recover_inode_ra_pass2(log, item);
- break;
- case XFS_LI_DQUOT:
- xlog_recover_dquot_ra_pass2(log, item);
- break;
- case XFS_LI_EFI:
- case XFS_LI_EFD:
- 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;
- }
-}
-
STATIC int
xlog_recover_commit_pass1(
struct xlog *log,
@@ -4116,7 +4026,8 @@ xlog_recover_commit_trans(
error = xlog_recover_commit_pass1(log, trans, item);
break;
case XLOG_RECOVER_PASS2:
- xlog_recover_ra_pass2(log, item);
+ if (item->ri_ops->ra_pass2)
+ item->ri_ops->ra_pass2(log, item);
list_move_tail(&item->ri_list, &ra_list);
items_queued++;
if (items_queued >= XLOG_RECOVER_COMMIT_QUEUE_MAX) {
^ permalink raw reply related [flat|nested] 94+ messages in thread* Re: [PATCH 03/28] xfs: refactor log recovery item dispatch for pass2 readhead functions
2020-05-05 1:10 ` [PATCH 03/28] xfs: refactor log recovery item dispatch for pass2 readhead functions Darrick J. Wong
@ 2020-05-05 4:32 ` Chandan Babu R
2020-05-06 15:04 ` Christoph Hellwig
1 sibling, 0 replies; 94+ messages in thread
From: Chandan Babu R @ 2020-05-05 4:32 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-xfs
On Tuesday 5 May 2020 6:40:51 AM IST Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Move the pass2 readhead code into the per-item source code files and use
> the dispatch function to call them.
>
Buf, Inode and Dquot items were read ahead before the patch modifications.
These are the only items on which readahead is started in this patch.
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
> fs/xfs/libxfs/xfs_log_recover.h | 6 ++
> fs/xfs/xfs_buf_item_recover.c | 11 +++++
> fs/xfs/xfs_dquot_item_recover.c | 34 ++++++++++++++
> fs/xfs/xfs_inode_item_recover.c | 19 ++++++++
> fs/xfs/xfs_log_recover.c | 95 +--------------------------------------
> 5 files changed, 73 insertions(+), 92 deletions(-)
>
>
> diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h
> index 271b0741f1e1..ff80871138bb 100644
> --- a/fs/xfs/libxfs/xfs_log_recover.h
> +++ b/fs/xfs/libxfs/xfs_log_recover.h
> @@ -31,6 +31,9 @@ struct xlog_recover_item_ops {
> * values mean.
> */
> enum xlog_recover_reorder (*reorder)(struct xlog_recover_item *item);
> +
> + /* Start readahead for pass2, if provided. */
> + void (*ra_pass2)(struct xlog *log, struct xlog_recover_item *item);
> };
>
> extern const struct xlog_recover_item_ops xlog_icreate_item_ops;
> @@ -92,4 +95,7 @@ struct xlog_recover {
> #define XLOG_RECOVER_PASS1 1
> #define XLOG_RECOVER_PASS2 2
>
> +void xlog_buf_readahead(struct xlog *log, xfs_daddr_t blkno, uint len,
> + const struct xfs_buf_ops *ops);
> +
> #endif /* __XFS_LOG_RECOVER_H__ */
> diff --git a/fs/xfs/xfs_buf_item_recover.c b/fs/xfs/xfs_buf_item_recover.c
> index def19025512e..a1327196b690 100644
> --- a/fs/xfs/xfs_buf_item_recover.c
> +++ b/fs/xfs/xfs_buf_item_recover.c
> @@ -32,7 +32,18 @@ xlog_recover_buf_reorder(
> return XLOG_REORDER_BUFFER_LIST;
> }
>
> +STATIC void
> +xlog_recover_buf_ra_pass2(
> + struct xlog *log,
> + struct xlog_recover_item *item)
> +{
> + struct xfs_buf_log_format *buf_f = item->ri_buf[0].i_addr;
> +
> + xlog_buf_readahead(log, buf_f->blf_blkno, buf_f->blf_len, NULL);
> +}
> +
> const struct xlog_recover_item_ops xlog_buf_item_ops = {
> .item_type = XFS_LI_BUF,
> .reorder = xlog_recover_buf_reorder,
> + .ra_pass2 = xlog_recover_buf_ra_pass2,
> };
> diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c
> index 78fe644e9907..215274173b70 100644
> --- a/fs/xfs/xfs_dquot_item_recover.c
> +++ b/fs/xfs/xfs_dquot_item_recover.c
> @@ -20,8 +20,42 @@
> #include "xfs_log_priv.h"
> #include "xfs_log_recover.h"
>
> +STATIC void
> +xlog_recover_dquot_ra_pass2(
> + struct xlog *log,
> + struct xlog_recover_item *item)
> +{
> + struct xfs_mount *mp = log->l_mp;
> + struct xfs_disk_dquot *recddq;
> + struct xfs_dq_logformat *dq_f;
> + uint type;
> +
> + if (mp->m_qflags == 0)
> + return;
> +
> + recddq = item->ri_buf[1].i_addr;
> + if (recddq == NULL)
> + return;
> + if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot))
> + return;
> +
> + type = recddq->d_flags & (XFS_DQ_USER | XFS_DQ_PROJ | XFS_DQ_GROUP);
> + ASSERT(type);
> + if (log->l_quotaoffs_flag & type)
> + return;
> +
> + dq_f = item->ri_buf[0].i_addr;
> + ASSERT(dq_f);
> + ASSERT(dq_f->qlf_len == 1);
> +
> + xlog_buf_readahead(log, dq_f->qlf_blkno,
> + XFS_FSB_TO_BB(mp, dq_f->qlf_len),
> + &xfs_dquot_buf_ra_ops);
> +}
> +
> const struct xlog_recover_item_ops xlog_dquot_item_ops = {
> .item_type = XFS_LI_DQUOT,
> + .ra_pass2 = xlog_recover_dquot_ra_pass2,
> };
>
> const struct xlog_recover_item_ops xlog_quotaoff_item_ops = {
> diff --git a/fs/xfs/xfs_inode_item_recover.c b/fs/xfs/xfs_inode_item_recover.c
> index b19a151efb10..a132cacd8d48 100644
> --- a/fs/xfs/xfs_inode_item_recover.c
> +++ b/fs/xfs/xfs_inode_item_recover.c
> @@ -21,6 +21,25 @@
> #include "xfs_log_priv.h"
> #include "xfs_log_recover.h"
>
> +STATIC void
> +xlog_recover_inode_ra_pass2(
> + struct xlog *log,
> + struct xlog_recover_item *item)
> +{
> + if (item->ri_buf[0].i_len == sizeof(struct xfs_inode_log_format)) {
> + struct xfs_inode_log_format *ilfp = item->ri_buf[0].i_addr;
> +
> + xlog_buf_readahead(log, ilfp->ilf_blkno, ilfp->ilf_len,
> + &xfs_inode_buf_ra_ops);
> + } else {
> + struct xfs_inode_log_format_32 *ilfp = item->ri_buf[0].i_addr;
> +
> + xlog_buf_readahead(log, ilfp->ilf_blkno, ilfp->ilf_len,
> + &xfs_inode_buf_ra_ops);
> + }
> +}
> +
> const struct xlog_recover_item_ops xlog_inode_item_ops = {
> .item_type = XFS_LI_INODE,
> + .ra_pass2 = xlog_recover_inode_ra_pass2,
> };
> diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
> index 0ef0d81fd190..ea566747d8e1 100644
> --- a/fs/xfs/xfs_log_recover.c
> +++ b/fs/xfs/xfs_log_recover.c
> @@ -2023,7 +2023,7 @@ xlog_put_buffer_cancelled(
> return true;
> }
>
> -static void
> +void
> xlog_buf_readahead(
> struct xlog *log,
> xfs_daddr_t blkno,
> @@ -3890,96 +3890,6 @@ xlog_recover_do_icreate_pass2(
> length, be32_to_cpu(icl->icl_gen));
> }
>
> -STATIC void
> -xlog_recover_buffer_ra_pass2(
> - struct xlog *log,
> - struct xlog_recover_item *item)
> -{
> - struct xfs_buf_log_format *buf_f = item->ri_buf[0].i_addr;
> -
> - xlog_buf_readahead(log, buf_f->blf_blkno, buf_f->blf_len, NULL);
> -}
> -
> -STATIC void
> -xlog_recover_inode_ra_pass2(
> - struct xlog *log,
> - struct xlog_recover_item *item)
> -{
> - if (item->ri_buf[0].i_len == sizeof(struct xfs_inode_log_format)) {
> - struct xfs_inode_log_format *ilfp = item->ri_buf[0].i_addr;
> -
> - xlog_buf_readahead(log, ilfp->ilf_blkno, ilfp->ilf_len,
> - &xfs_inode_buf_ra_ops);
> - } else {
> - struct xfs_inode_log_format_32 *ilfp = item->ri_buf[0].i_addr;
> -
> - xlog_buf_readahead(log, ilfp->ilf_blkno, ilfp->ilf_len,
> - &xfs_inode_buf_ra_ops);
> - }
> -}
> -
> -STATIC void
> -xlog_recover_dquot_ra_pass2(
> - struct xlog *log,
> - struct xlog_recover_item *item)
> -{
> - struct xfs_mount *mp = log->l_mp;
> - struct xfs_disk_dquot *recddq;
> - struct xfs_dq_logformat *dq_f;
> - uint type;
> -
> - if (mp->m_qflags == 0)
> - return;
> -
> - recddq = item->ri_buf[1].i_addr;
> - if (recddq == NULL)
> - return;
> - if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot))
> - return;
> -
> - type = recddq->d_flags & (XFS_DQ_USER | XFS_DQ_PROJ | XFS_DQ_GROUP);
> - ASSERT(type);
> - if (log->l_quotaoffs_flag & type)
> - return;
> -
> - dq_f = item->ri_buf[0].i_addr;
> - ASSERT(dq_f);
> - ASSERT(dq_f->qlf_len == 1);
> -
> - xlog_buf_readahead(log, dq_f->qlf_blkno,
> - XFS_FSB_TO_BB(mp, dq_f->qlf_len),
> - &xfs_dquot_buf_ra_ops);
> -}
> -
> -STATIC void
> -xlog_recover_ra_pass2(
> - struct xlog *log,
> - struct xlog_recover_item *item)
> -{
> - switch (ITEM_TYPE(item)) {
> - case XFS_LI_BUF:
> - xlog_recover_buffer_ra_pass2(log, item);
> - break;
> - case XFS_LI_INODE:
> - xlog_recover_inode_ra_pass2(log, item);
> - break;
> - case XFS_LI_DQUOT:
> - xlog_recover_dquot_ra_pass2(log, item);
> - break;
> - case XFS_LI_EFI:
> - case XFS_LI_EFD:
> - 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;
> - }
> -}
> -
> STATIC int
> xlog_recover_commit_pass1(
> struct xlog *log,
> @@ -4116,7 +4026,8 @@ xlog_recover_commit_trans(
> error = xlog_recover_commit_pass1(log, trans, item);
> break;
> case XLOG_RECOVER_PASS2:
> - xlog_recover_ra_pass2(log, item);
> + if (item->ri_ops->ra_pass2)
> + item->ri_ops->ra_pass2(log, item);
> list_move_tail(&item->ri_list, &ra_list);
> items_queued++;
> if (items_queued >= XLOG_RECOVER_COMMIT_QUEUE_MAX) {
>
>
--
chandan
^ permalink raw reply [flat|nested] 94+ messages in thread* Re: [PATCH 03/28] xfs: refactor log recovery item dispatch for pass2 readhead functions
2020-05-05 1:10 ` [PATCH 03/28] xfs: refactor log recovery item dispatch for pass2 readhead functions Darrick J. Wong
2020-05-05 4:32 ` Chandan Babu R
@ 2020-05-06 15:04 ` Christoph Hellwig
1 sibling, 0 replies; 94+ messages in thread
From: Christoph Hellwig @ 2020-05-06 15:04 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-xfs
On Mon, May 04, 2020 at 06:10:51PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Move the pass2 readhead code into the per-item source code files and use
> the dispatch function to call them.
>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Looks good,
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 94+ messages in thread
* [PATCH 04/28] xfs: refactor log recovery item dispatch for pass1 commit functions
2020-05-05 1:10 [PATCH v3 00/28] xfs: refactor log recovery Darrick J. Wong
` (2 preceding siblings ...)
2020-05-05 1:10 ` [PATCH 03/28] xfs: refactor log recovery item dispatch for pass2 readhead functions Darrick J. Wong
@ 2020-05-05 1:10 ` Darrick J. Wong
2020-05-05 4:40 ` Chandan Babu R
2020-05-06 15:07 ` Christoph Hellwig
2020-05-05 1:11 ` [PATCH 05/28] xfs: refactor log recovery buffer item dispatch for pass2 " Darrick J. Wong
` (23 subsequent siblings)
27 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2020-05-05 1:10 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Move the pass1 commit code into the per-item source code files and use
the dispatch function to call them.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_log_recover.h | 4 ++
fs/xfs/xfs_buf_item_recover.c | 27 ++++++++++
fs/xfs/xfs_dquot_item_recover.c | 28 +++++++++++
fs/xfs/xfs_log_recover.c | 101 +++++----------------------------------
4 files changed, 71 insertions(+), 89 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h
index ff80871138bb..384b70d58993 100644
--- a/fs/xfs/libxfs/xfs_log_recover.h
+++ b/fs/xfs/libxfs/xfs_log_recover.h
@@ -34,6 +34,9 @@ struct xlog_recover_item_ops {
/* Start readahead for pass2, if provided. */
void (*ra_pass2)(struct xlog *log, struct xlog_recover_item *item);
+
+ /* Do whatever work we need to do for pass1, if provided. */
+ int (*commit_pass1)(struct xlog *log, struct xlog_recover_item *item);
};
extern const struct xlog_recover_item_ops xlog_icreate_item_ops;
@@ -97,5 +100,6 @@ struct xlog_recover {
void xlog_buf_readahead(struct xlog *log, xfs_daddr_t blkno, uint len,
const struct xfs_buf_ops *ops);
+bool xlog_add_buffer_cancelled(struct xlog *log, xfs_daddr_t blkno, uint len);
#endif /* __XFS_LOG_RECOVER_H__ */
diff --git a/fs/xfs/xfs_buf_item_recover.c b/fs/xfs/xfs_buf_item_recover.c
index a1327196b690..802f2206516d 100644
--- a/fs/xfs/xfs_buf_item_recover.c
+++ b/fs/xfs/xfs_buf_item_recover.c
@@ -42,8 +42,35 @@ xlog_recover_buf_ra_pass2(
xlog_buf_readahead(log, buf_f->blf_blkno, buf_f->blf_len, NULL);
}
+/*
+ * Build up the table of buf cancel records so that we don't replay cancelled
+ * data in the second pass.
+ */
+static int
+xlog_recover_buf_commit_pass1(
+ struct xlog *log,
+ struct xlog_recover_item *item)
+{
+ struct xfs_buf_log_format *bf = item->ri_buf[0].i_addr;
+
+ if (!xfs_buf_log_check_iovec(&item->ri_buf[0])) {
+ xfs_err(log->l_mp, "bad buffer log item size (%d)",
+ item->ri_buf[0].i_len);
+ return -EFSCORRUPTED;
+ }
+
+ if (!(bf->blf_flags & XFS_BLF_CANCEL))
+ trace_xfs_log_recover_buf_not_cancel(log, bf);
+ else if (xlog_add_buffer_cancelled(log, bf->blf_blkno, bf->blf_len))
+ trace_xfs_log_recover_buf_cancel_add(log, bf);
+ else
+ trace_xfs_log_recover_buf_cancel_ref_inc(log, bf);
+ return 0;
+}
+
const struct xlog_recover_item_ops xlog_buf_item_ops = {
.item_type = XFS_LI_BUF,
.reorder = xlog_recover_buf_reorder,
.ra_pass2 = xlog_recover_buf_ra_pass2,
+ .commit_pass1 = xlog_recover_buf_commit_pass1,
};
diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c
index 215274173b70..ebc44c1bc2b1 100644
--- a/fs/xfs/xfs_dquot_item_recover.c
+++ b/fs/xfs/xfs_dquot_item_recover.c
@@ -58,6 +58,34 @@ const struct xlog_recover_item_ops xlog_dquot_item_ops = {
.ra_pass2 = xlog_recover_dquot_ra_pass2,
};
+/*
+ * Recover QUOTAOFF records. We simply make a note of it in the xlog
+ * structure, so that we know not to do any dquot item or dquot buffer recovery,
+ * of that type.
+ */
+STATIC int
+xlog_recover_quotaoff_commit_pass1(
+ struct xlog *log,
+ struct xlog_recover_item *item)
+{
+ struct xfs_qoff_logformat *qoff_f = item->ri_buf[0].i_addr;
+ ASSERT(qoff_f);
+
+ /*
+ * The logitem format's flag tells us if this was user quotaoff,
+ * group/project quotaoff or both.
+ */
+ if (qoff_f->qf_flags & XFS_UQUOTA_ACCT)
+ log->l_quotaoffs_flag |= XFS_DQ_USER;
+ if (qoff_f->qf_flags & XFS_PQUOTA_ACCT)
+ log->l_quotaoffs_flag |= XFS_DQ_PROJ;
+ if (qoff_f->qf_flags & XFS_GQUOTA_ACCT)
+ log->l_quotaoffs_flag |= XFS_DQ_GROUP;
+
+ return 0;
+}
+
const struct xlog_recover_item_ops xlog_quotaoff_item_ops = {
.item_type = XFS_LI_QUOTAOFF,
+ .commit_pass1 = xlog_recover_quotaoff_commit_pass1,
};
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index ea566747d8e1..b3627ebf870e 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -1953,7 +1953,7 @@ xlog_find_buffer_cancelled(
return NULL;
}
-static bool
+bool
xlog_add_buffer_cancelled(
struct xlog *log,
xfs_daddr_t blkno,
@@ -2034,32 +2034,6 @@ xlog_buf_readahead(
xfs_buf_readahead(log->l_mp->m_ddev_targp, blkno, len, ops);
}
-/*
- * Build up the table of buf cancel records so that we don't replay cancelled
- * data in the second pass.
- */
-static int
-xlog_recover_buffer_pass1(
- struct xlog *log,
- struct xlog_recover_item *item)
-{
- struct xfs_buf_log_format *bf = item->ri_buf[0].i_addr;
-
- if (!xfs_buf_log_check_iovec(&item->ri_buf[0])) {
- xfs_err(log->l_mp, "bad buffer log item size (%d)",
- item->ri_buf[0].i_len);
- return -EFSCORRUPTED;
- }
-
- if (!(bf->blf_flags & XFS_BLF_CANCEL))
- trace_xfs_log_recover_buf_not_cancel(log, bf);
- else if (xlog_add_buffer_cancelled(log, bf->blf_blkno, bf->blf_len))
- trace_xfs_log_recover_buf_cancel_add(log, bf);
- else
- trace_xfs_log_recover_buf_cancel_ref_inc(log, bf);
- return 0;
-}
-
/*
* Perform recovery for a buffer full of inodes. In these buffers, the only
* data which should be recovered is that which corresponds to the
@@ -3197,33 +3171,6 @@ xlog_recover_inode_pass2(
return error;
}
-/*
- * Recover QUOTAOFF records. We simply make a note of it in the xlog
- * structure, so that we know not to do any dquot item or dquot buffer recovery,
- * of that type.
- */
-STATIC int
-xlog_recover_quotaoff_pass1(
- struct xlog *log,
- struct xlog_recover_item *item)
-{
- xfs_qoff_logformat_t *qoff_f = item->ri_buf[0].i_addr;
- ASSERT(qoff_f);
-
- /*
- * The logitem format's flag tells us if this was user quotaoff,
- * group/project quotaoff or both.
- */
- if (qoff_f->qf_flags & XFS_UQUOTA_ACCT)
- log->l_quotaoffs_flag |= XFS_DQ_USER;
- if (qoff_f->qf_flags & XFS_PQUOTA_ACCT)
- log->l_quotaoffs_flag |= XFS_DQ_PROJ;
- if (qoff_f->qf_flags & XFS_GQUOTA_ACCT)
- log->l_quotaoffs_flag |= XFS_DQ_GROUP;
-
- return 0;
-}
-
/*
* Recover a dquot record
*/
@@ -3890,40 +3837,6 @@ xlog_recover_do_icreate_pass2(
length, be32_to_cpu(icl->icl_gen));
}
-STATIC int
-xlog_recover_commit_pass1(
- struct xlog *log,
- struct xlog_recover *trans,
- struct xlog_recover_item *item)
-{
- trace_xfs_log_recover_item_recover(log, trans, item, XLOG_RECOVER_PASS1);
-
- switch (ITEM_TYPE(item)) {
- case XFS_LI_BUF:
- return xlog_recover_buffer_pass1(log, item);
- case XFS_LI_QUOTAOFF:
- return xlog_recover_quotaoff_pass1(log, item);
- case XFS_LI_INODE:
- case XFS_LI_EFI:
- case XFS_LI_EFD:
- case XFS_LI_DQUOT:
- 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:
- xfs_warn(log->l_mp, "%s: invalid item type (%d)",
- __func__, ITEM_TYPE(item));
- ASSERT(0);
- return -EFSCORRUPTED;
- }
-}
-
STATIC int
xlog_recover_commit_pass2(
struct xlog *log,
@@ -4021,9 +3934,19 @@ xlog_recover_commit_trans(
return error;
list_for_each_entry_safe(item, next, &trans->r_itemq, ri_list) {
+ trace_xfs_log_recover_item_recover(log, trans, item, pass);
+
+ if (!item->ri_ops) {
+ xfs_warn(log->l_mp, "%s: invalid item type (%d)",
+ __func__, ITEM_TYPE(item));
+ ASSERT(0);
+ return -EFSCORRUPTED;
+ }
+
switch (pass) {
case XLOG_RECOVER_PASS1:
- error = xlog_recover_commit_pass1(log, trans, item);
+ if (item->ri_ops->commit_pass1)
+ error = item->ri_ops->commit_pass1(log, item);
break;
case XLOG_RECOVER_PASS2:
if (item->ri_ops->ra_pass2)
^ permalink raw reply related [flat|nested] 94+ messages in thread* Re: [PATCH 04/28] xfs: refactor log recovery item dispatch for pass1 commit functions
2020-05-05 1:10 ` [PATCH 04/28] xfs: refactor log recovery item dispatch for pass1 commit functions Darrick J. Wong
@ 2020-05-05 4:40 ` Chandan Babu R
2020-05-06 15:07 ` Christoph Hellwig
1 sibling, 0 replies; 94+ messages in thread
From: Chandan Babu R @ 2020-05-05 4:40 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-xfs
On Tuesday 5 May 2020 6:40:57 AM IST Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Move the pass1 commit code into the per-item source code files and use
> the dispatch function to call them.
>
Buf and Quotaoff items need to be processed during pass1's commit phase. This
is correctly done in this patch.
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
> fs/xfs/libxfs/xfs_log_recover.h | 4 ++
> fs/xfs/xfs_buf_item_recover.c | 27 ++++++++++
> fs/xfs/xfs_dquot_item_recover.c | 28 +++++++++++
> fs/xfs/xfs_log_recover.c | 101 +++++----------------------------------
> 4 files changed, 71 insertions(+), 89 deletions(-)
>
>
> diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h
> index ff80871138bb..384b70d58993 100644
> --- a/fs/xfs/libxfs/xfs_log_recover.h
> +++ b/fs/xfs/libxfs/xfs_log_recover.h
> @@ -34,6 +34,9 @@ struct xlog_recover_item_ops {
>
> /* Start readahead for pass2, if provided. */
> void (*ra_pass2)(struct xlog *log, struct xlog_recover_item *item);
> +
> + /* Do whatever work we need to do for pass1, if provided. */
> + int (*commit_pass1)(struct xlog *log, struct xlog_recover_item *item);
> };
>
> extern const struct xlog_recover_item_ops xlog_icreate_item_ops;
> @@ -97,5 +100,6 @@ struct xlog_recover {
>
> void xlog_buf_readahead(struct xlog *log, xfs_daddr_t blkno, uint len,
> const struct xfs_buf_ops *ops);
> +bool xlog_add_buffer_cancelled(struct xlog *log, xfs_daddr_t blkno, uint len);
>
> #endif /* __XFS_LOG_RECOVER_H__ */
> diff --git a/fs/xfs/xfs_buf_item_recover.c b/fs/xfs/xfs_buf_item_recover.c
> index a1327196b690..802f2206516d 100644
> --- a/fs/xfs/xfs_buf_item_recover.c
> +++ b/fs/xfs/xfs_buf_item_recover.c
> @@ -42,8 +42,35 @@ xlog_recover_buf_ra_pass2(
> xlog_buf_readahead(log, buf_f->blf_blkno, buf_f->blf_len, NULL);
> }
>
> +/*
> + * Build up the table of buf cancel records so that we don't replay cancelled
> + * data in the second pass.
> + */
> +static int
> +xlog_recover_buf_commit_pass1(
> + struct xlog *log,
> + struct xlog_recover_item *item)
> +{
> + struct xfs_buf_log_format *bf = item->ri_buf[0].i_addr;
> +
> + if (!xfs_buf_log_check_iovec(&item->ri_buf[0])) {
> + xfs_err(log->l_mp, "bad buffer log item size (%d)",
> + item->ri_buf[0].i_len);
> + return -EFSCORRUPTED;
> + }
> +
> + if (!(bf->blf_flags & XFS_BLF_CANCEL))
> + trace_xfs_log_recover_buf_not_cancel(log, bf);
> + else if (xlog_add_buffer_cancelled(log, bf->blf_blkno, bf->blf_len))
> + trace_xfs_log_recover_buf_cancel_add(log, bf);
> + else
> + trace_xfs_log_recover_buf_cancel_ref_inc(log, bf);
> + return 0;
> +}
> +
> const struct xlog_recover_item_ops xlog_buf_item_ops = {
> .item_type = XFS_LI_BUF,
> .reorder = xlog_recover_buf_reorder,
> .ra_pass2 = xlog_recover_buf_ra_pass2,
> + .commit_pass1 = xlog_recover_buf_commit_pass1,
> };
> diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c
> index 215274173b70..ebc44c1bc2b1 100644
> --- a/fs/xfs/xfs_dquot_item_recover.c
> +++ b/fs/xfs/xfs_dquot_item_recover.c
> @@ -58,6 +58,34 @@ const struct xlog_recover_item_ops xlog_dquot_item_ops = {
> .ra_pass2 = xlog_recover_dquot_ra_pass2,
> };
>
> +/*
> + * Recover QUOTAOFF records. We simply make a note of it in the xlog
> + * structure, so that we know not to do any dquot item or dquot buffer recovery,
> + * of that type.
> + */
> +STATIC int
> +xlog_recover_quotaoff_commit_pass1(
> + struct xlog *log,
> + struct xlog_recover_item *item)
> +{
> + struct xfs_qoff_logformat *qoff_f = item->ri_buf[0].i_addr;
> + ASSERT(qoff_f);
> +
> + /*
> + * The logitem format's flag tells us if this was user quotaoff,
> + * group/project quotaoff or both.
> + */
> + if (qoff_f->qf_flags & XFS_UQUOTA_ACCT)
> + log->l_quotaoffs_flag |= XFS_DQ_USER;
> + if (qoff_f->qf_flags & XFS_PQUOTA_ACCT)
> + log->l_quotaoffs_flag |= XFS_DQ_PROJ;
> + if (qoff_f->qf_flags & XFS_GQUOTA_ACCT)
> + log->l_quotaoffs_flag |= XFS_DQ_GROUP;
> +
> + return 0;
> +}
> +
> const struct xlog_recover_item_ops xlog_quotaoff_item_ops = {
> .item_type = XFS_LI_QUOTAOFF,
> + .commit_pass1 = xlog_recover_quotaoff_commit_pass1,
> };
> diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
> index ea566747d8e1..b3627ebf870e 100644
> --- a/fs/xfs/xfs_log_recover.c
> +++ b/fs/xfs/xfs_log_recover.c
> @@ -1953,7 +1953,7 @@ xlog_find_buffer_cancelled(
> return NULL;
> }
>
> -static bool
> +bool
> xlog_add_buffer_cancelled(
> struct xlog *log,
> xfs_daddr_t blkno,
> @@ -2034,32 +2034,6 @@ xlog_buf_readahead(
> xfs_buf_readahead(log->l_mp->m_ddev_targp, blkno, len, ops);
> }
>
> -/*
> - * Build up the table of buf cancel records so that we don't replay cancelled
> - * data in the second pass.
> - */
> -static int
> -xlog_recover_buffer_pass1(
> - struct xlog *log,
> - struct xlog_recover_item *item)
> -{
> - struct xfs_buf_log_format *bf = item->ri_buf[0].i_addr;
> -
> - if (!xfs_buf_log_check_iovec(&item->ri_buf[0])) {
> - xfs_err(log->l_mp, "bad buffer log item size (%d)",
> - item->ri_buf[0].i_len);
> - return -EFSCORRUPTED;
> - }
> -
> - if (!(bf->blf_flags & XFS_BLF_CANCEL))
> - trace_xfs_log_recover_buf_not_cancel(log, bf);
> - else if (xlog_add_buffer_cancelled(log, bf->blf_blkno, bf->blf_len))
> - trace_xfs_log_recover_buf_cancel_add(log, bf);
> - else
> - trace_xfs_log_recover_buf_cancel_ref_inc(log, bf);
> - return 0;
> -}
> -
> /*
> * Perform recovery for a buffer full of inodes. In these buffers, the only
> * data which should be recovered is that which corresponds to the
> @@ -3197,33 +3171,6 @@ xlog_recover_inode_pass2(
> return error;
> }
>
> -/*
> - * Recover QUOTAOFF records. We simply make a note of it in the xlog
> - * structure, so that we know not to do any dquot item or dquot buffer recovery,
> - * of that type.
> - */
> -STATIC int
> -xlog_recover_quotaoff_pass1(
> - struct xlog *log,
> - struct xlog_recover_item *item)
> -{
> - xfs_qoff_logformat_t *qoff_f = item->ri_buf[0].i_addr;
> - ASSERT(qoff_f);
> -
> - /*
> - * The logitem format's flag tells us if this was user quotaoff,
> - * group/project quotaoff or both.
> - */
> - if (qoff_f->qf_flags & XFS_UQUOTA_ACCT)
> - log->l_quotaoffs_flag |= XFS_DQ_USER;
> - if (qoff_f->qf_flags & XFS_PQUOTA_ACCT)
> - log->l_quotaoffs_flag |= XFS_DQ_PROJ;
> - if (qoff_f->qf_flags & XFS_GQUOTA_ACCT)
> - log->l_quotaoffs_flag |= XFS_DQ_GROUP;
> -
> - return 0;
> -}
> -
> /*
> * Recover a dquot record
> */
> @@ -3890,40 +3837,6 @@ xlog_recover_do_icreate_pass2(
> length, be32_to_cpu(icl->icl_gen));
> }
>
> -STATIC int
> -xlog_recover_commit_pass1(
> - struct xlog *log,
> - struct xlog_recover *trans,
> - struct xlog_recover_item *item)
> -{
> - trace_xfs_log_recover_item_recover(log, trans, item, XLOG_RECOVER_PASS1);
> -
> - switch (ITEM_TYPE(item)) {
> - case XFS_LI_BUF:
> - return xlog_recover_buffer_pass1(log, item);
> - case XFS_LI_QUOTAOFF:
> - return xlog_recover_quotaoff_pass1(log, item);
> - case XFS_LI_INODE:
> - case XFS_LI_EFI:
> - case XFS_LI_EFD:
> - case XFS_LI_DQUOT:
> - 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:
> - xfs_warn(log->l_mp, "%s: invalid item type (%d)",
> - __func__, ITEM_TYPE(item));
> - ASSERT(0);
> - return -EFSCORRUPTED;
> - }
> -}
> -
> STATIC int
> xlog_recover_commit_pass2(
> struct xlog *log,
> @@ -4021,9 +3934,19 @@ xlog_recover_commit_trans(
> return error;
>
> list_for_each_entry_safe(item, next, &trans->r_itemq, ri_list) {
> + trace_xfs_log_recover_item_recover(log, trans, item, pass);
> +
> + if (!item->ri_ops) {
> + xfs_warn(log->l_mp, "%s: invalid item type (%d)",
> + __func__, ITEM_TYPE(item));
> + ASSERT(0);
> + return -EFSCORRUPTED;
> + }
> +
> switch (pass) {
> case XLOG_RECOVER_PASS1:
> - error = xlog_recover_commit_pass1(log, trans, item);
> + if (item->ri_ops->commit_pass1)
> + error = item->ri_ops->commit_pass1(log, item);
> break;
> case XLOG_RECOVER_PASS2:
> if (item->ri_ops->ra_pass2)
>
>
--
chandan
^ permalink raw reply [flat|nested] 94+ messages in thread* Re: [PATCH 04/28] xfs: refactor log recovery item dispatch for pass1 commit functions
2020-05-05 1:10 ` [PATCH 04/28] xfs: refactor log recovery item dispatch for pass1 commit functions Darrick J. Wong
2020-05-05 4:40 ` Chandan Babu R
@ 2020-05-06 15:07 ` Christoph Hellwig
1 sibling, 0 replies; 94+ messages in thread
From: Christoph Hellwig @ 2020-05-06 15:07 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-xfs
> list_for_each_entry_safe(item, next, &trans->r_itemq, ri_list) {
> + trace_xfs_log_recover_item_recover(log, trans, item, pass);
> +
> + if (!item->ri_ops) {
> + xfs_warn(log->l_mp, "%s: invalid item type (%d)",
> + __func__, ITEM_TYPE(item));
> + ASSERT(0);
> + return -EFSCORRUPTED;
> + }
Given that we check for ri_ops during the reorder phase this can't
happen. I think we should remove this check.
Otherwise looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 94+ messages in thread
* [PATCH 05/28] xfs: refactor log recovery buffer item dispatch for pass2 commit functions
2020-05-05 1:10 [PATCH v3 00/28] xfs: refactor log recovery Darrick J. Wong
` (3 preceding siblings ...)
2020-05-05 1:10 ` [PATCH 04/28] xfs: refactor log recovery item dispatch for pass1 commit functions Darrick J. Wong
@ 2020-05-05 1:11 ` Darrick J. Wong
2020-05-05 5:03 ` Chandan Babu R
2020-05-06 15:09 ` Christoph Hellwig
2020-05-05 1:11 ` [PATCH 06/28] xfs: refactor log recovery inode " Darrick J. Wong
` (22 subsequent siblings)
27 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2020-05-05 1:11 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Move the log buffer item pass2 commit code into the per-item source code
files and use the dispatch function to call it. We do these one at a
time because there's a lot of code to move. No functional changes.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_log_recover.h | 23 +
fs/xfs/xfs_buf_item_recover.c | 790 +++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_log_recover.c | 798 ---------------------------------------
3 files changed, 820 insertions(+), 791 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h
index 384b70d58993..a45f6e9fa47b 100644
--- a/fs/xfs/libxfs/xfs_log_recover.h
+++ b/fs/xfs/libxfs/xfs_log_recover.h
@@ -37,6 +37,26 @@ struct xlog_recover_item_ops {
/* Do whatever work we need to do for pass1, if provided. */
int (*commit_pass1)(struct xlog *log, struct xlog_recover_item *item);
+
+ /*
+ * This function should do whatever work is needed for pass2 of log
+ * recovery, if provided.
+ *
+ * If the recovered item is an intent item, this function should parse
+ * the recovered item to construct an in-core log intent item and
+ * insert it into the AIL. The in-core log intent item should have 1
+ * refcount so that the item is freed either (a) when we commit the
+ * recovered log item for the intent-done item; (b) replay the work and
+ * log a new intent-done item; or (c) recovery fails and we have to
+ * abort.
+ *
+ * If the recovered item is an intent-done item, this function should
+ * parse the recovered item to find the id of the corresponding intent
+ * log item. Next, it should find the in-core log intent item in the
+ * AIL and release it.
+ */
+ int (*commit_pass2)(struct xlog *log, struct list_head *buffer_list,
+ struct xlog_recover_item *item, xfs_lsn_t lsn);
};
extern const struct xlog_recover_item_ops xlog_icreate_item_ops;
@@ -101,5 +121,8 @@ struct xlog_recover {
void xlog_buf_readahead(struct xlog *log, xfs_daddr_t blkno, uint len,
const struct xfs_buf_ops *ops);
bool xlog_add_buffer_cancelled(struct xlog *log, xfs_daddr_t blkno, uint len);
+bool xlog_is_buffer_cancelled(struct xlog *log, xfs_daddr_t blkno, uint len);
+bool xlog_put_buffer_cancelled(struct xlog *log, xfs_daddr_t blkno, uint len);
+void xlog_recover_iodone(struct xfs_buf *bp);
#endif /* __XFS_LOG_RECOVER_H__ */
diff --git a/fs/xfs/xfs_buf_item_recover.c b/fs/xfs/xfs_buf_item_recover.c
index 802f2206516d..4ca6d47d6c95 100644
--- a/fs/xfs/xfs_buf_item_recover.c
+++ b/fs/xfs/xfs_buf_item_recover.c
@@ -18,6 +18,10 @@
#include "xfs_log.h"
#include "xfs_log_priv.h"
#include "xfs_log_recover.h"
+#include "xfs_error.h"
+#include "xfs_inode.h"
+#include "xfs_dir2.h"
+#include "xfs_quota.h"
STATIC enum xlog_recover_reorder
xlog_recover_buf_reorder(
@@ -68,9 +72,795 @@ xlog_recover_buf_commit_pass1(
return 0;
}
+/*
+ * Validate the recovered buffer is of the correct type and attach the
+ * appropriate buffer operations to them for writeback. Magic numbers are in a
+ * few places:
+ * the first 16 bits of the buffer (inode buffer, dquot buffer),
+ * the first 32 bits of the buffer (most blocks),
+ * inside a struct xfs_da_blkinfo at the start of the buffer.
+ */
+static void
+xlog_recover_validate_buf_type(
+ struct xfs_mount *mp,
+ struct xfs_buf *bp,
+ struct xfs_buf_log_format *buf_f,
+ xfs_lsn_t current_lsn)
+{
+ struct xfs_da_blkinfo *info = bp->b_addr;
+ uint32_t magic32;
+ uint16_t magic16;
+ uint16_t magicda;
+ char *warnmsg = NULL;
+
+ /*
+ * We can only do post recovery validation on items on CRC enabled
+ * fielsystems as we need to know when the buffer was written to be able
+ * to determine if we should have replayed the item. If we replay old
+ * metadata over a newer buffer, then it will enter a temporarily
+ * inconsistent state resulting in verification failures. Hence for now
+ * just avoid the verification stage for non-crc filesystems
+ */
+ if (!xfs_sb_version_hascrc(&mp->m_sb))
+ return;
+
+ magic32 = be32_to_cpu(*(__be32 *)bp->b_addr);
+ magic16 = be16_to_cpu(*(__be16*)bp->b_addr);
+ magicda = be16_to_cpu(info->magic);
+ switch (xfs_blft_from_flags(buf_f)) {
+ case XFS_BLFT_BTREE_BUF:
+ switch (magic32) {
+ case XFS_ABTB_CRC_MAGIC:
+ case XFS_ABTB_MAGIC:
+ bp->b_ops = &xfs_bnobt_buf_ops;
+ break;
+ case XFS_ABTC_CRC_MAGIC:
+ case XFS_ABTC_MAGIC:
+ bp->b_ops = &xfs_cntbt_buf_ops;
+ break;
+ case XFS_IBT_CRC_MAGIC:
+ case XFS_IBT_MAGIC:
+ bp->b_ops = &xfs_inobt_buf_ops;
+ break;
+ case XFS_FIBT_CRC_MAGIC:
+ case XFS_FIBT_MAGIC:
+ bp->b_ops = &xfs_finobt_buf_ops;
+ break;
+ case XFS_BMAP_CRC_MAGIC:
+ case XFS_BMAP_MAGIC:
+ bp->b_ops = &xfs_bmbt_buf_ops;
+ break;
+ 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;
+ }
+ break;
+ case XFS_BLFT_AGF_BUF:
+ if (magic32 != XFS_AGF_MAGIC) {
+ warnmsg = "Bad AGF block magic!";
+ break;
+ }
+ bp->b_ops = &xfs_agf_buf_ops;
+ break;
+ case XFS_BLFT_AGFL_BUF:
+ if (magic32 != XFS_AGFL_MAGIC) {
+ warnmsg = "Bad AGFL block magic!";
+ break;
+ }
+ bp->b_ops = &xfs_agfl_buf_ops;
+ break;
+ case XFS_BLFT_AGI_BUF:
+ if (magic32 != XFS_AGI_MAGIC) {
+ warnmsg = "Bad AGI block magic!";
+ break;
+ }
+ bp->b_ops = &xfs_agi_buf_ops;
+ break;
+ case XFS_BLFT_UDQUOT_BUF:
+ case XFS_BLFT_PDQUOT_BUF:
+ case XFS_BLFT_GDQUOT_BUF:
+#ifdef CONFIG_XFS_QUOTA
+ if (magic16 != XFS_DQUOT_MAGIC) {
+ warnmsg = "Bad DQUOT block magic!";
+ break;
+ }
+ bp->b_ops = &xfs_dquot_buf_ops;
+#else
+ xfs_alert(mp,
+ "Trying to recover dquots without QUOTA support built in!");
+ ASSERT(0);
+#endif
+ break;
+ case XFS_BLFT_DINO_BUF:
+ if (magic16 != XFS_DINODE_MAGIC) {
+ warnmsg = "Bad INODE block magic!";
+ break;
+ }
+ bp->b_ops = &xfs_inode_buf_ops;
+ break;
+ case XFS_BLFT_SYMLINK_BUF:
+ if (magic32 != XFS_SYMLINK_MAGIC) {
+ warnmsg = "Bad symlink block magic!";
+ break;
+ }
+ bp->b_ops = &xfs_symlink_buf_ops;
+ break;
+ case XFS_BLFT_DIR_BLOCK_BUF:
+ if (magic32 != XFS_DIR2_BLOCK_MAGIC &&
+ magic32 != XFS_DIR3_BLOCK_MAGIC) {
+ warnmsg = "Bad dir block magic!";
+ break;
+ }
+ bp->b_ops = &xfs_dir3_block_buf_ops;
+ break;
+ case XFS_BLFT_DIR_DATA_BUF:
+ if (magic32 != XFS_DIR2_DATA_MAGIC &&
+ magic32 != XFS_DIR3_DATA_MAGIC) {
+ warnmsg = "Bad dir data magic!";
+ break;
+ }
+ bp->b_ops = &xfs_dir3_data_buf_ops;
+ break;
+ case XFS_BLFT_DIR_FREE_BUF:
+ if (magic32 != XFS_DIR2_FREE_MAGIC &&
+ magic32 != XFS_DIR3_FREE_MAGIC) {
+ warnmsg = "Bad dir3 free magic!";
+ break;
+ }
+ bp->b_ops = &xfs_dir3_free_buf_ops;
+ break;
+ case XFS_BLFT_DIR_LEAF1_BUF:
+ if (magicda != XFS_DIR2_LEAF1_MAGIC &&
+ magicda != XFS_DIR3_LEAF1_MAGIC) {
+ warnmsg = "Bad dir leaf1 magic!";
+ break;
+ }
+ bp->b_ops = &xfs_dir3_leaf1_buf_ops;
+ break;
+ case XFS_BLFT_DIR_LEAFN_BUF:
+ if (magicda != XFS_DIR2_LEAFN_MAGIC &&
+ magicda != XFS_DIR3_LEAFN_MAGIC) {
+ warnmsg = "Bad dir leafn magic!";
+ break;
+ }
+ bp->b_ops = &xfs_dir3_leafn_buf_ops;
+ break;
+ case XFS_BLFT_DA_NODE_BUF:
+ if (magicda != XFS_DA_NODE_MAGIC &&
+ magicda != XFS_DA3_NODE_MAGIC) {
+ warnmsg = "Bad da node magic!";
+ break;
+ }
+ bp->b_ops = &xfs_da3_node_buf_ops;
+ break;
+ case XFS_BLFT_ATTR_LEAF_BUF:
+ if (magicda != XFS_ATTR_LEAF_MAGIC &&
+ magicda != XFS_ATTR3_LEAF_MAGIC) {
+ warnmsg = "Bad attr leaf magic!";
+ break;
+ }
+ bp->b_ops = &xfs_attr3_leaf_buf_ops;
+ break;
+ case XFS_BLFT_ATTR_RMT_BUF:
+ if (magic32 != XFS_ATTR3_RMT_MAGIC) {
+ warnmsg = "Bad attr remote magic!";
+ break;
+ }
+ bp->b_ops = &xfs_attr3_rmt_buf_ops;
+ break;
+ case XFS_BLFT_SB_BUF:
+ if (magic32 != XFS_SB_MAGIC) {
+ warnmsg = "Bad SB block magic!";
+ break;
+ }
+ bp->b_ops = &xfs_sb_buf_ops;
+ break;
+#ifdef CONFIG_XFS_RT
+ case XFS_BLFT_RTBITMAP_BUF:
+ case XFS_BLFT_RTSUMMARY_BUF:
+ /* no magic numbers for verification of RT buffers */
+ bp->b_ops = &xfs_rtbuf_ops;
+ break;
+#endif /* CONFIG_XFS_RT */
+ default:
+ xfs_warn(mp, "Unknown buffer type %d!",
+ xfs_blft_from_flags(buf_f));
+ break;
+ }
+
+ /*
+ * Nothing else to do in the case of a NULL current LSN as this means
+ * the buffer is more recent than the change in the log and will be
+ * skipped.
+ */
+ if (current_lsn == NULLCOMMITLSN)
+ return;
+
+ if (warnmsg) {
+ xfs_warn(mp, warnmsg);
+ ASSERT(0);
+ }
+
+ /*
+ * We must update the metadata LSN of the buffer as it is written out to
+ * ensure that older transactions never replay over this one and corrupt
+ * the buffer. This can occur if log recovery is interrupted at some
+ * point after the current transaction completes, at which point a
+ * subsequent mount starts recovery from the beginning.
+ *
+ * Write verifiers update the metadata LSN from log items attached to
+ * the buffer. Therefore, initialize a bli purely to carry the LSN to
+ * the verifier. We'll clean it up in our ->iodone() callback.
+ */
+ if (bp->b_ops) {
+ struct xfs_buf_log_item *bip;
+
+ ASSERT(!bp->b_iodone || bp->b_iodone == xlog_recover_iodone);
+ bp->b_iodone = xlog_recover_iodone;
+ xfs_buf_item_init(bp, mp);
+ bip = bp->b_log_item;
+ bip->bli_item.li_lsn = current_lsn;
+ }
+}
+
+/*
+ * Perform a 'normal' buffer recovery. Each logged region of the
+ * buffer should be copied over the corresponding region in the
+ * given buffer. The bitmap in the buf log format structure indicates
+ * where to place the logged data.
+ */
+STATIC void
+xlog_recover_do_reg_buffer(
+ struct xfs_mount *mp,
+ struct xlog_recover_item *item,
+ struct xfs_buf *bp,
+ struct xfs_buf_log_format *buf_f,
+ xfs_lsn_t current_lsn)
+{
+ int i;
+ int bit;
+ int nbits;
+ xfs_failaddr_t fa;
+ const size_t size_disk_dquot = sizeof(struct xfs_disk_dquot);
+
+ trace_xfs_log_recover_buf_reg_buf(mp->m_log, buf_f);
+
+ bit = 0;
+ i = 1; /* 0 is the buf format structure */
+ while (1) {
+ bit = xfs_next_bit(buf_f->blf_data_map,
+ buf_f->blf_map_size, bit);
+ if (bit == -1)
+ break;
+ nbits = xfs_contig_bits(buf_f->blf_data_map,
+ buf_f->blf_map_size, bit);
+ ASSERT(nbits > 0);
+ ASSERT(item->ri_buf[i].i_addr != NULL);
+ ASSERT(item->ri_buf[i].i_len % XFS_BLF_CHUNK == 0);
+ ASSERT(BBTOB(bp->b_length) >=
+ ((uint)bit << XFS_BLF_SHIFT) + (nbits << XFS_BLF_SHIFT));
+
+ /*
+ * The dirty regions logged in the buffer, even though
+ * contiguous, may span multiple chunks. This is because the
+ * dirty region may span a physical page boundary in a buffer
+ * and hence be split into two separate vectors for writing into
+ * the log. Hence we need to trim nbits back to the length of
+ * the current region being copied out of the log.
+ */
+ if (item->ri_buf[i].i_len < (nbits << XFS_BLF_SHIFT))
+ nbits = item->ri_buf[i].i_len >> XFS_BLF_SHIFT;
+
+ /*
+ * Do a sanity check if this is a dquot buffer. Just checking
+ * the first dquot in the buffer should do. XXXThis is
+ * probably a good thing to do for other buf types also.
+ */
+ fa = NULL;
+ if (buf_f->blf_flags &
+ (XFS_BLF_UDQUOT_BUF|XFS_BLF_PDQUOT_BUF|XFS_BLF_GDQUOT_BUF)) {
+ if (item->ri_buf[i].i_addr == NULL) {
+ xfs_alert(mp,
+ "XFS: NULL dquot in %s.", __func__);
+ goto next;
+ }
+ if (item->ri_buf[i].i_len < size_disk_dquot) {
+ xfs_alert(mp,
+ "XFS: dquot too small (%d) in %s.",
+ item->ri_buf[i].i_len, __func__);
+ goto next;
+ }
+ fa = xfs_dquot_verify(mp, item->ri_buf[i].i_addr,
+ -1, 0);
+ if (fa) {
+ xfs_alert(mp,
+ "dquot corrupt at %pS trying to replay into block 0x%llx",
+ fa, bp->b_bn);
+ goto next;
+ }
+ }
+
+ memcpy(xfs_buf_offset(bp,
+ (uint)bit << XFS_BLF_SHIFT), /* dest */
+ item->ri_buf[i].i_addr, /* source */
+ nbits<<XFS_BLF_SHIFT); /* length */
+ next:
+ i++;
+ bit += nbits;
+ }
+
+ /* Shouldn't be any more regions */
+ ASSERT(i == item->ri_total);
+
+ xlog_recover_validate_buf_type(mp, bp, buf_f, current_lsn);
+}
+
+/*
+ * Perform a dquot buffer recovery.
+ * Simple algorithm: if we have found a QUOTAOFF log item of the same type
+ * (ie. USR or GRP), then just toss this buffer away; don't recover it.
+ * Else, treat it as a regular buffer and do recovery.
+ *
+ * Return false if the buffer was tossed and true if we recovered the buffer to
+ * indicate to the caller if the buffer needs writing.
+ */
+STATIC bool
+xlog_recover_do_dquot_buffer(
+ struct xfs_mount *mp,
+ struct xlog *log,
+ struct xlog_recover_item *item,
+ struct xfs_buf *bp,
+ struct xfs_buf_log_format *buf_f)
+{
+ uint type;
+
+ trace_xfs_log_recover_buf_dquot_buf(log, buf_f);
+
+ /*
+ * Filesystems are required to send in quota flags at mount time.
+ */
+ if (!mp->m_qflags)
+ return false;
+
+ type = 0;
+ if (buf_f->blf_flags & XFS_BLF_UDQUOT_BUF)
+ type |= XFS_DQ_USER;
+ if (buf_f->blf_flags & XFS_BLF_PDQUOT_BUF)
+ type |= XFS_DQ_PROJ;
+ if (buf_f->blf_flags & XFS_BLF_GDQUOT_BUF)
+ type |= XFS_DQ_GROUP;
+ /*
+ * This type of quotas was turned off, so ignore this buffer
+ */
+ if (log->l_quotaoffs_flag & type)
+ return false;
+
+ xlog_recover_do_reg_buffer(mp, item, bp, buf_f, NULLCOMMITLSN);
+ return true;
+}
+
+/*
+ * Perform recovery for a buffer full of inodes. In these buffers, the only
+ * data which should be recovered is that which corresponds to the
+ * di_next_unlinked pointers in the on disk inode structures. The rest of the
+ * data for the inodes is always logged through the inodes themselves rather
+ * than the inode buffer and is recovered in xlog_recover_inode_pass2().
+ *
+ * The only time when buffers full of inodes are fully recovered is when the
+ * buffer is full of newly allocated inodes. In this case the buffer will
+ * not be marked as an inode buffer and so will be sent to
+ * xlog_recover_do_reg_buffer() below during recovery.
+ */
+STATIC int
+xlog_recover_do_inode_buffer(
+ struct xfs_mount *mp,
+ struct xlog_recover_item *item,
+ struct xfs_buf *bp,
+ struct xfs_buf_log_format *buf_f)
+{
+ int i;
+ int item_index = 0;
+ int bit = 0;
+ int nbits = 0;
+ int reg_buf_offset = 0;
+ int reg_buf_bytes = 0;
+ int next_unlinked_offset;
+ int inodes_per_buf;
+ xfs_agino_t *logged_nextp;
+ xfs_agino_t *buffer_nextp;
+
+ trace_xfs_log_recover_buf_inode_buf(mp->m_log, buf_f);
+
+ /*
+ * Post recovery validation only works properly on CRC enabled
+ * filesystems.
+ */
+ if (xfs_sb_version_hascrc(&mp->m_sb))
+ bp->b_ops = &xfs_inode_buf_ops;
+
+ inodes_per_buf = BBTOB(bp->b_length) >> mp->m_sb.sb_inodelog;
+ for (i = 0; i < inodes_per_buf; i++) {
+ next_unlinked_offset = (i * mp->m_sb.sb_inodesize) +
+ offsetof(xfs_dinode_t, di_next_unlinked);
+
+ while (next_unlinked_offset >=
+ (reg_buf_offset + reg_buf_bytes)) {
+ /*
+ * The next di_next_unlinked field is beyond
+ * the current logged region. Find the next
+ * logged region that contains or is beyond
+ * the current di_next_unlinked field.
+ */
+ bit += nbits;
+ bit = xfs_next_bit(buf_f->blf_data_map,
+ buf_f->blf_map_size, bit);
+
+ /*
+ * If there are no more logged regions in the
+ * buffer, then we're done.
+ */
+ if (bit == -1)
+ return 0;
+
+ nbits = xfs_contig_bits(buf_f->blf_data_map,
+ buf_f->blf_map_size, bit);
+ ASSERT(nbits > 0);
+ reg_buf_offset = bit << XFS_BLF_SHIFT;
+ reg_buf_bytes = nbits << XFS_BLF_SHIFT;
+ item_index++;
+ }
+
+ /*
+ * If the current logged region starts after the current
+ * di_next_unlinked field, then move on to the next
+ * di_next_unlinked field.
+ */
+ if (next_unlinked_offset < reg_buf_offset)
+ continue;
+
+ ASSERT(item->ri_buf[item_index].i_addr != NULL);
+ ASSERT((item->ri_buf[item_index].i_len % XFS_BLF_CHUNK) == 0);
+ ASSERT((reg_buf_offset + reg_buf_bytes) <= BBTOB(bp->b_length));
+
+ /*
+ * The current logged region contains a copy of the
+ * current di_next_unlinked field. Extract its value
+ * and copy it to the buffer copy.
+ */
+ logged_nextp = item->ri_buf[item_index].i_addr +
+ next_unlinked_offset - reg_buf_offset;
+ if (XFS_IS_CORRUPT(mp, *logged_nextp == 0)) {
+ xfs_alert(mp,
+ "Bad inode buffer log record (ptr = "PTR_FMT", bp = "PTR_FMT"). "
+ "Trying to replay bad (0) inode di_next_unlinked field.",
+ item, bp);
+ return -EFSCORRUPTED;
+ }
+
+ buffer_nextp = xfs_buf_offset(bp, next_unlinked_offset);
+ *buffer_nextp = *logged_nextp;
+
+ /*
+ * If necessary, recalculate the CRC in the on-disk inode. We
+ * have to leave the inode in a consistent state for whoever
+ * reads it next....
+ */
+ xfs_dinode_calc_crc(mp,
+ xfs_buf_offset(bp, i * mp->m_sb.sb_inodesize));
+
+ }
+
+ return 0;
+}
+
+/*
+ * V5 filesystems know the age of the buffer on disk being recovered. We can
+ * have newer objects on disk than we are replaying, and so for these cases we
+ * don't want to replay the current change as that will make the buffer contents
+ * temporarily invalid on disk.
+ *
+ * The magic number might not match the buffer type we are going to recover
+ * (e.g. reallocated blocks), so we ignore the xfs_buf_log_format flags. Hence
+ * extract the LSN of the existing object in the buffer based on it's current
+ * magic number. If we don't recognise the magic number in the buffer, then
+ * return a LSN of -1 so that the caller knows it was an unrecognised block and
+ * so can recover the buffer.
+ *
+ * Note: we cannot rely solely on magic number matches to determine that the
+ * buffer has a valid LSN - we also need to verify that it belongs to this
+ * filesystem, so we need to extract the object's LSN and compare it to that
+ * which we read from the superblock. If the UUIDs don't match, then we've got a
+ * stale metadata block from an old filesystem instance that we need to recover
+ * over the top of.
+ */
+static xfs_lsn_t
+xlog_recover_get_buf_lsn(
+ struct xfs_mount *mp,
+ struct xfs_buf *bp)
+{
+ uint32_t magic32;
+ uint16_t magic16;
+ uint16_t magicda;
+ void *blk = bp->b_addr;
+ uuid_t *uuid;
+ xfs_lsn_t lsn = -1;
+
+ /* v4 filesystems always recover immediately */
+ if (!xfs_sb_version_hascrc(&mp->m_sb))
+ goto recover_immediately;
+
+ magic32 = be32_to_cpu(*(__be32 *)blk);
+ switch (magic32) {
+ case XFS_ABTB_CRC_MAGIC:
+ case XFS_ABTC_CRC_MAGIC:
+ 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;
+
+ lsn = be64_to_cpu(btb->bb_u.s.bb_lsn);
+ uuid = &btb->bb_u.s.bb_uuid;
+ break;
+ }
+ case XFS_BMAP_CRC_MAGIC:
+ case XFS_BMAP_MAGIC: {
+ struct xfs_btree_block *btb = blk;
+
+ lsn = be64_to_cpu(btb->bb_u.l.bb_lsn);
+ uuid = &btb->bb_u.l.bb_uuid;
+ break;
+ }
+ case XFS_AGF_MAGIC:
+ lsn = be64_to_cpu(((struct xfs_agf *)blk)->agf_lsn);
+ uuid = &((struct xfs_agf *)blk)->agf_uuid;
+ break;
+ case XFS_AGFL_MAGIC:
+ lsn = be64_to_cpu(((struct xfs_agfl *)blk)->agfl_lsn);
+ uuid = &((struct xfs_agfl *)blk)->agfl_uuid;
+ break;
+ case XFS_AGI_MAGIC:
+ lsn = be64_to_cpu(((struct xfs_agi *)blk)->agi_lsn);
+ uuid = &((struct xfs_agi *)blk)->agi_uuid;
+ break;
+ case XFS_SYMLINK_MAGIC:
+ lsn = be64_to_cpu(((struct xfs_dsymlink_hdr *)blk)->sl_lsn);
+ uuid = &((struct xfs_dsymlink_hdr *)blk)->sl_uuid;
+ break;
+ case XFS_DIR3_BLOCK_MAGIC:
+ case XFS_DIR3_DATA_MAGIC:
+ case XFS_DIR3_FREE_MAGIC:
+ lsn = be64_to_cpu(((struct xfs_dir3_blk_hdr *)blk)->lsn);
+ uuid = &((struct xfs_dir3_blk_hdr *)blk)->uuid;
+ break;
+ case XFS_ATTR3_RMT_MAGIC:
+ /*
+ * Remote attr blocks are written synchronously, rather than
+ * being logged. That means they do not contain a valid LSN
+ * (i.e. transactionally ordered) in them, and hence any time we
+ * see a buffer to replay over the top of a remote attribute
+ * block we should simply do so.
+ */
+ goto recover_immediately;
+ case XFS_SB_MAGIC:
+ /*
+ * superblock uuids are magic. We may or may not have a
+ * sb_meta_uuid on disk, but it will be set in the in-core
+ * superblock. We set the uuid pointer for verification
+ * according to the superblock feature mask to ensure we check
+ * the relevant UUID in the superblock.
+ */
+ lsn = be64_to_cpu(((struct xfs_dsb *)blk)->sb_lsn);
+ if (xfs_sb_version_hasmetauuid(&mp->m_sb))
+ uuid = &((struct xfs_dsb *)blk)->sb_meta_uuid;
+ else
+ uuid = &((struct xfs_dsb *)blk)->sb_uuid;
+ break;
+ default:
+ break;
+ }
+
+ if (lsn != (xfs_lsn_t)-1) {
+ if (!uuid_equal(&mp->m_sb.sb_meta_uuid, uuid))
+ goto recover_immediately;
+ return lsn;
+ }
+
+ magicda = be16_to_cpu(((struct xfs_da_blkinfo *)blk)->magic);
+ switch (magicda) {
+ case XFS_DIR3_LEAF1_MAGIC:
+ case XFS_DIR3_LEAFN_MAGIC:
+ case XFS_DA3_NODE_MAGIC:
+ lsn = be64_to_cpu(((struct xfs_da3_blkinfo *)blk)->lsn);
+ uuid = &((struct xfs_da3_blkinfo *)blk)->uuid;
+ break;
+ default:
+ break;
+ }
+
+ if (lsn != (xfs_lsn_t)-1) {
+ if (!uuid_equal(&mp->m_sb.sb_uuid, uuid))
+ goto recover_immediately;
+ return lsn;
+ }
+
+ /*
+ * We do individual object checks on dquot and inode buffers as they
+ * have their own individual LSN records. Also, we could have a stale
+ * buffer here, so we have to at least recognise these buffer types.
+ *
+ * A notd complexity here is inode unlinked list processing - it logs
+ * the inode directly in the buffer, but we don't know which inodes have
+ * been modified, and there is no global buffer LSN. Hence we need to
+ * recover all inode buffer types immediately. This problem will be
+ * fixed by logical logging of the unlinked list modifications.
+ */
+ magic16 = be16_to_cpu(*(__be16 *)blk);
+ switch (magic16) {
+ case XFS_DQUOT_MAGIC:
+ case XFS_DINODE_MAGIC:
+ goto recover_immediately;
+ default:
+ break;
+ }
+
+ /* unknown buffer contents, recover immediately */
+
+recover_immediately:
+ return (xfs_lsn_t)-1;
+
+}
+
+/*
+ * This routine replays a modification made to a buffer at runtime.
+ * There are actually two types of buffer, regular and inode, which
+ * are handled differently. Inode buffers are handled differently
+ * in that we only recover a specific set of data from them, namely
+ * the inode di_next_unlinked fields. This is because all other inode
+ * data is actually logged via inode records and any data we replay
+ * here which overlaps that may be stale.
+ *
+ * When meta-data buffers are freed at run time we log a buffer item
+ * with the XFS_BLF_CANCEL bit set to indicate that previous copies
+ * of the buffer in the log should not be replayed at recovery time.
+ * This is so that if the blocks covered by the buffer are reused for
+ * file data before we crash we don't end up replaying old, freed
+ * meta-data into a user's file.
+ *
+ * To handle the cancellation of buffer log items, we make two passes
+ * over the log during recovery. During the first we build a table of
+ * those buffers which have been cancelled, and during the second we
+ * only replay those buffers which do not have corresponding cancel
+ * records in the table. See xlog_recover_buf_pass[1,2] above
+ * for more details on the implementation of the table of cancel records.
+ */
+STATIC int
+xlog_recover_buf_commit_pass2(
+ struct xlog *log,
+ struct list_head *buffer_list,
+ struct xlog_recover_item *item,
+ xfs_lsn_t current_lsn)
+{
+ struct xfs_buf_log_format *buf_f = item->ri_buf[0].i_addr;
+ struct xfs_mount *mp = log->l_mp;
+ struct xfs_buf *bp;
+ int error;
+ uint buf_flags;
+ xfs_lsn_t lsn;
+
+ /*
+ * In this pass we only want to recover all the buffers which have
+ * not been cancelled and are not cancellation buffers themselves.
+ */
+ if (buf_f->blf_flags & XFS_BLF_CANCEL) {
+ if (xlog_put_buffer_cancelled(log, buf_f->blf_blkno,
+ buf_f->blf_len))
+ goto cancelled;
+ } else {
+
+ if (xlog_is_buffer_cancelled(log, buf_f->blf_blkno,
+ buf_f->blf_len))
+ goto cancelled;
+ }
+
+ trace_xfs_log_recover_buf_recover(log, buf_f);
+
+ buf_flags = 0;
+ if (buf_f->blf_flags & XFS_BLF_INODE_BUF)
+ buf_flags |= XBF_UNMAPPED;
+
+ error = xfs_buf_read(mp->m_ddev_targp, buf_f->blf_blkno, buf_f->blf_len,
+ buf_flags, &bp, NULL);
+ if (error)
+ return error;
+
+ /*
+ * Recover the buffer only if we get an LSN from it and it's less than
+ * the lsn of the transaction we are replaying.
+ *
+ * Note that we have to be extremely careful of readahead here.
+ * Readahead does not attach verfiers to the buffers so if we don't
+ * actually do any replay after readahead because of the LSN we found
+ * in the buffer if more recent than that current transaction then we
+ * need to attach the verifier directly. Failure to do so can lead to
+ * future recovery actions (e.g. EFI and unlinked list recovery) can
+ * operate on the buffers and they won't get the verifier attached. This
+ * can lead to blocks on disk having the correct content but a stale
+ * CRC.
+ *
+ * It is safe to assume these clean buffers are currently up to date.
+ * If the buffer is dirtied by a later transaction being replayed, then
+ * the verifier will be reset to match whatever recover turns that
+ * buffer into.
+ */
+ lsn = xlog_recover_get_buf_lsn(mp, bp);
+ if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) >= 0) {
+ trace_xfs_log_recover_buf_skip(log, buf_f);
+ xlog_recover_validate_buf_type(mp, bp, buf_f, NULLCOMMITLSN);
+ goto out_release;
+ }
+
+ if (buf_f->blf_flags & XFS_BLF_INODE_BUF) {
+ error = xlog_recover_do_inode_buffer(mp, item, bp, buf_f);
+ if (error)
+ goto out_release;
+ } else if (buf_f->blf_flags &
+ (XFS_BLF_UDQUOT_BUF|XFS_BLF_PDQUOT_BUF|XFS_BLF_GDQUOT_BUF)) {
+ bool dirty;
+
+ dirty = xlog_recover_do_dquot_buffer(mp, log, item, bp, buf_f);
+ if (!dirty)
+ goto out_release;
+ } else {
+ xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn);
+ }
+
+ /*
+ * Perform delayed write on the buffer. Asynchronous writes will be
+ * slower when taking into account all the buffers to be flushed.
+ *
+ * Also make sure that only inode buffers with good sizes stay in
+ * the buffer cache. The kernel moves inodes in buffers of 1 block
+ * or inode_cluster_size bytes, whichever is bigger. The inode
+ * buffers in the log can be a different size if the log was generated
+ * by an older kernel using unclustered inode buffers or a newer kernel
+ * running with a different inode cluster size. Regardless, if the
+ * the inode buffer size isn't max(blocksize, inode_cluster_size)
+ * for *our* value of inode_cluster_size, then we need to keep
+ * the buffer out of the buffer cache so that the buffer won't
+ * overlap with future reads of those inodes.
+ */
+ if (XFS_DINODE_MAGIC ==
+ be16_to_cpu(*((__be16 *)xfs_buf_offset(bp, 0))) &&
+ (BBTOB(bp->b_length) != M_IGEO(log->l_mp)->inode_cluster_size)) {
+ xfs_buf_stale(bp);
+ error = xfs_bwrite(bp);
+ } else {
+ ASSERT(bp->b_mount == mp);
+ bp->b_iodone = xlog_recover_iodone;
+ xfs_buf_delwri_queue(bp, buffer_list);
+ }
+
+out_release:
+ xfs_buf_relse(bp);
+ return error;
+cancelled:
+ trace_xfs_log_recover_buf_cancel(log, buf_f);
+ return 0;
+}
+
const struct xlog_recover_item_ops xlog_buf_item_ops = {
.item_type = XFS_LI_BUF,
.reorder = xlog_recover_buf_reorder,
.ra_pass2 = xlog_recover_buf_ra_pass2,
.commit_pass1 = xlog_recover_buf_commit_pass1,
+ .commit_pass2 = xlog_recover_buf_commit_pass2,
};
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index b3627ebf870e..d65dc3895a62 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -284,7 +284,7 @@ xlog_header_check_mount(
return 0;
}
-STATIC void
+void
xlog_recover_iodone(
struct xfs_buf *bp)
{
@@ -1985,7 +1985,7 @@ xlog_add_buffer_cancelled(
/*
* Check if there is and entry for blkno, len in the buffer cancel record table.
*/
-static bool
+bool
xlog_is_buffer_cancelled(
struct xlog *log,
xfs_daddr_t blkno,
@@ -2002,7 +2002,7 @@ xlog_is_buffer_cancelled(
* buffer is re-used again after its last cancellation we actually replay the
* changes made at that point.
*/
-static bool
+bool
xlog_put_buffer_cancelled(
struct xlog *log,
xfs_daddr_t blkno,
@@ -2034,791 +2034,6 @@ xlog_buf_readahead(
xfs_buf_readahead(log->l_mp->m_ddev_targp, blkno, len, ops);
}
-/*
- * Perform recovery for a buffer full of inodes. In these buffers, the only
- * data which should be recovered is that which corresponds to the
- * di_next_unlinked pointers in the on disk inode structures. The rest of the
- * data for the inodes is always logged through the inodes themselves rather
- * than the inode buffer and is recovered in xlog_recover_inode_pass2().
- *
- * The only time when buffers full of inodes are fully recovered is when the
- * buffer is full of newly allocated inodes. In this case the buffer will
- * not be marked as an inode buffer and so will be sent to
- * xlog_recover_do_reg_buffer() below during recovery.
- */
-STATIC int
-xlog_recover_do_inode_buffer(
- struct xfs_mount *mp,
- struct xlog_recover_item *item,
- struct xfs_buf *bp,
- xfs_buf_log_format_t *buf_f)
-{
- int i;
- int item_index = 0;
- int bit = 0;
- int nbits = 0;
- int reg_buf_offset = 0;
- int reg_buf_bytes = 0;
- int next_unlinked_offset;
- int inodes_per_buf;
- xfs_agino_t *logged_nextp;
- xfs_agino_t *buffer_nextp;
-
- trace_xfs_log_recover_buf_inode_buf(mp->m_log, buf_f);
-
- /*
- * Post recovery validation only works properly on CRC enabled
- * filesystems.
- */
- if (xfs_sb_version_hascrc(&mp->m_sb))
- bp->b_ops = &xfs_inode_buf_ops;
-
- inodes_per_buf = BBTOB(bp->b_length) >> mp->m_sb.sb_inodelog;
- for (i = 0; i < inodes_per_buf; i++) {
- next_unlinked_offset = (i * mp->m_sb.sb_inodesize) +
- offsetof(xfs_dinode_t, di_next_unlinked);
-
- while (next_unlinked_offset >=
- (reg_buf_offset + reg_buf_bytes)) {
- /*
- * The next di_next_unlinked field is beyond
- * the current logged region. Find the next
- * logged region that contains or is beyond
- * the current di_next_unlinked field.
- */
- bit += nbits;
- bit = xfs_next_bit(buf_f->blf_data_map,
- buf_f->blf_map_size, bit);
-
- /*
- * If there are no more logged regions in the
- * buffer, then we're done.
- */
- if (bit == -1)
- return 0;
-
- nbits = xfs_contig_bits(buf_f->blf_data_map,
- buf_f->blf_map_size, bit);
- ASSERT(nbits > 0);
- reg_buf_offset = bit << XFS_BLF_SHIFT;
- reg_buf_bytes = nbits << XFS_BLF_SHIFT;
- item_index++;
- }
-
- /*
- * If the current logged region starts after the current
- * di_next_unlinked field, then move on to the next
- * di_next_unlinked field.
- */
- if (next_unlinked_offset < reg_buf_offset)
- continue;
-
- ASSERT(item->ri_buf[item_index].i_addr != NULL);
- ASSERT((item->ri_buf[item_index].i_len % XFS_BLF_CHUNK) == 0);
- ASSERT((reg_buf_offset + reg_buf_bytes) <= BBTOB(bp->b_length));
-
- /*
- * The current logged region contains a copy of the
- * current di_next_unlinked field. Extract its value
- * and copy it to the buffer copy.
- */
- logged_nextp = item->ri_buf[item_index].i_addr +
- next_unlinked_offset - reg_buf_offset;
- if (XFS_IS_CORRUPT(mp, *logged_nextp == 0)) {
- xfs_alert(mp,
- "Bad inode buffer log record (ptr = "PTR_FMT", bp = "PTR_FMT"). "
- "Trying to replay bad (0) inode di_next_unlinked field.",
- item, bp);
- return -EFSCORRUPTED;
- }
-
- buffer_nextp = xfs_buf_offset(bp, next_unlinked_offset);
- *buffer_nextp = *logged_nextp;
-
- /*
- * If necessary, recalculate the CRC in the on-disk inode. We
- * have to leave the inode in a consistent state for whoever
- * reads it next....
- */
- xfs_dinode_calc_crc(mp,
- xfs_buf_offset(bp, i * mp->m_sb.sb_inodesize));
-
- }
-
- return 0;
-}
-
-/*
- * V5 filesystems know the age of the buffer on disk being recovered. We can
- * have newer objects on disk than we are replaying, and so for these cases we
- * don't want to replay the current change as that will make the buffer contents
- * temporarily invalid on disk.
- *
- * The magic number might not match the buffer type we are going to recover
- * (e.g. reallocated blocks), so we ignore the xfs_buf_log_format flags. Hence
- * extract the LSN of the existing object in the buffer based on it's current
- * magic number. If we don't recognise the magic number in the buffer, then
- * return a LSN of -1 so that the caller knows it was an unrecognised block and
- * so can recover the buffer.
- *
- * Note: we cannot rely solely on magic number matches to determine that the
- * buffer has a valid LSN - we also need to verify that it belongs to this
- * filesystem, so we need to extract the object's LSN and compare it to that
- * which we read from the superblock. If the UUIDs don't match, then we've got a
- * stale metadata block from an old filesystem instance that we need to recover
- * over the top of.
- */
-static xfs_lsn_t
-xlog_recover_get_buf_lsn(
- struct xfs_mount *mp,
- struct xfs_buf *bp)
-{
- uint32_t magic32;
- uint16_t magic16;
- uint16_t magicda;
- void *blk = bp->b_addr;
- uuid_t *uuid;
- xfs_lsn_t lsn = -1;
-
- /* v4 filesystems always recover immediately */
- if (!xfs_sb_version_hascrc(&mp->m_sb))
- goto recover_immediately;
-
- magic32 = be32_to_cpu(*(__be32 *)blk);
- switch (magic32) {
- case XFS_ABTB_CRC_MAGIC:
- case XFS_ABTC_CRC_MAGIC:
- 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;
-
- lsn = be64_to_cpu(btb->bb_u.s.bb_lsn);
- uuid = &btb->bb_u.s.bb_uuid;
- break;
- }
- case XFS_BMAP_CRC_MAGIC:
- case XFS_BMAP_MAGIC: {
- struct xfs_btree_block *btb = blk;
-
- lsn = be64_to_cpu(btb->bb_u.l.bb_lsn);
- uuid = &btb->bb_u.l.bb_uuid;
- break;
- }
- case XFS_AGF_MAGIC:
- lsn = be64_to_cpu(((struct xfs_agf *)blk)->agf_lsn);
- uuid = &((struct xfs_agf *)blk)->agf_uuid;
- break;
- case XFS_AGFL_MAGIC:
- lsn = be64_to_cpu(((struct xfs_agfl *)blk)->agfl_lsn);
- uuid = &((struct xfs_agfl *)blk)->agfl_uuid;
- break;
- case XFS_AGI_MAGIC:
- lsn = be64_to_cpu(((struct xfs_agi *)blk)->agi_lsn);
- uuid = &((struct xfs_agi *)blk)->agi_uuid;
- break;
- case XFS_SYMLINK_MAGIC:
- lsn = be64_to_cpu(((struct xfs_dsymlink_hdr *)blk)->sl_lsn);
- uuid = &((struct xfs_dsymlink_hdr *)blk)->sl_uuid;
- break;
- case XFS_DIR3_BLOCK_MAGIC:
- case XFS_DIR3_DATA_MAGIC:
- case XFS_DIR3_FREE_MAGIC:
- lsn = be64_to_cpu(((struct xfs_dir3_blk_hdr *)blk)->lsn);
- uuid = &((struct xfs_dir3_blk_hdr *)blk)->uuid;
- break;
- case XFS_ATTR3_RMT_MAGIC:
- /*
- * Remote attr blocks are written synchronously, rather than
- * being logged. That means they do not contain a valid LSN
- * (i.e. transactionally ordered) in them, and hence any time we
- * see a buffer to replay over the top of a remote attribute
- * block we should simply do so.
- */
- goto recover_immediately;
- case XFS_SB_MAGIC:
- /*
- * superblock uuids are magic. We may or may not have a
- * sb_meta_uuid on disk, but it will be set in the in-core
- * superblock. We set the uuid pointer for verification
- * according to the superblock feature mask to ensure we check
- * the relevant UUID in the superblock.
- */
- lsn = be64_to_cpu(((struct xfs_dsb *)blk)->sb_lsn);
- if (xfs_sb_version_hasmetauuid(&mp->m_sb))
- uuid = &((struct xfs_dsb *)blk)->sb_meta_uuid;
- else
- uuid = &((struct xfs_dsb *)blk)->sb_uuid;
- break;
- default:
- break;
- }
-
- if (lsn != (xfs_lsn_t)-1) {
- if (!uuid_equal(&mp->m_sb.sb_meta_uuid, uuid))
- goto recover_immediately;
- return lsn;
- }
-
- magicda = be16_to_cpu(((struct xfs_da_blkinfo *)blk)->magic);
- switch (magicda) {
- case XFS_DIR3_LEAF1_MAGIC:
- case XFS_DIR3_LEAFN_MAGIC:
- case XFS_DA3_NODE_MAGIC:
- lsn = be64_to_cpu(((struct xfs_da3_blkinfo *)blk)->lsn);
- uuid = &((struct xfs_da3_blkinfo *)blk)->uuid;
- break;
- default:
- break;
- }
-
- if (lsn != (xfs_lsn_t)-1) {
- if (!uuid_equal(&mp->m_sb.sb_uuid, uuid))
- goto recover_immediately;
- return lsn;
- }
-
- /*
- * We do individual object checks on dquot and inode buffers as they
- * have their own individual LSN records. Also, we could have a stale
- * buffer here, so we have to at least recognise these buffer types.
- *
- * A notd complexity here is inode unlinked list processing - it logs
- * the inode directly in the buffer, but we don't know which inodes have
- * been modified, and there is no global buffer LSN. Hence we need to
- * recover all inode buffer types immediately. This problem will be
- * fixed by logical logging of the unlinked list modifications.
- */
- magic16 = be16_to_cpu(*(__be16 *)blk);
- switch (magic16) {
- case XFS_DQUOT_MAGIC:
- case XFS_DINODE_MAGIC:
- goto recover_immediately;
- default:
- break;
- }
-
- /* unknown buffer contents, recover immediately */
-
-recover_immediately:
- return (xfs_lsn_t)-1;
-
-}
-
-/*
- * Validate the recovered buffer is of the correct type and attach the
- * appropriate buffer operations to them for writeback. Magic numbers are in a
- * few places:
- * the first 16 bits of the buffer (inode buffer, dquot buffer),
- * the first 32 bits of the buffer (most blocks),
- * inside a struct xfs_da_blkinfo at the start of the buffer.
- */
-static void
-xlog_recover_validate_buf_type(
- struct xfs_mount *mp,
- struct xfs_buf *bp,
- xfs_buf_log_format_t *buf_f,
- xfs_lsn_t current_lsn)
-{
- struct xfs_da_blkinfo *info = bp->b_addr;
- uint32_t magic32;
- uint16_t magic16;
- uint16_t magicda;
- char *warnmsg = NULL;
-
- /*
- * We can only do post recovery validation on items on CRC enabled
- * fielsystems as we need to know when the buffer was written to be able
- * to determine if we should have replayed the item. If we replay old
- * metadata over a newer buffer, then it will enter a temporarily
- * inconsistent state resulting in verification failures. Hence for now
- * just avoid the verification stage for non-crc filesystems
- */
- if (!xfs_sb_version_hascrc(&mp->m_sb))
- return;
-
- magic32 = be32_to_cpu(*(__be32 *)bp->b_addr);
- magic16 = be16_to_cpu(*(__be16*)bp->b_addr);
- magicda = be16_to_cpu(info->magic);
- switch (xfs_blft_from_flags(buf_f)) {
- case XFS_BLFT_BTREE_BUF:
- switch (magic32) {
- case XFS_ABTB_CRC_MAGIC:
- case XFS_ABTB_MAGIC:
- bp->b_ops = &xfs_bnobt_buf_ops;
- break;
- case XFS_ABTC_CRC_MAGIC:
- case XFS_ABTC_MAGIC:
- bp->b_ops = &xfs_cntbt_buf_ops;
- break;
- case XFS_IBT_CRC_MAGIC:
- case XFS_IBT_MAGIC:
- bp->b_ops = &xfs_inobt_buf_ops;
- break;
- case XFS_FIBT_CRC_MAGIC:
- case XFS_FIBT_MAGIC:
- bp->b_ops = &xfs_finobt_buf_ops;
- break;
- case XFS_BMAP_CRC_MAGIC:
- case XFS_BMAP_MAGIC:
- bp->b_ops = &xfs_bmbt_buf_ops;
- break;
- 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;
- }
- break;
- case XFS_BLFT_AGF_BUF:
- if (magic32 != XFS_AGF_MAGIC) {
- warnmsg = "Bad AGF block magic!";
- break;
- }
- bp->b_ops = &xfs_agf_buf_ops;
- break;
- case XFS_BLFT_AGFL_BUF:
- if (magic32 != XFS_AGFL_MAGIC) {
- warnmsg = "Bad AGFL block magic!";
- break;
- }
- bp->b_ops = &xfs_agfl_buf_ops;
- break;
- case XFS_BLFT_AGI_BUF:
- if (magic32 != XFS_AGI_MAGIC) {
- warnmsg = "Bad AGI block magic!";
- break;
- }
- bp->b_ops = &xfs_agi_buf_ops;
- break;
- case XFS_BLFT_UDQUOT_BUF:
- case XFS_BLFT_PDQUOT_BUF:
- case XFS_BLFT_GDQUOT_BUF:
-#ifdef CONFIG_XFS_QUOTA
- if (magic16 != XFS_DQUOT_MAGIC) {
- warnmsg = "Bad DQUOT block magic!";
- break;
- }
- bp->b_ops = &xfs_dquot_buf_ops;
-#else
- xfs_alert(mp,
- "Trying to recover dquots without QUOTA support built in!");
- ASSERT(0);
-#endif
- break;
- case XFS_BLFT_DINO_BUF:
- if (magic16 != XFS_DINODE_MAGIC) {
- warnmsg = "Bad INODE block magic!";
- break;
- }
- bp->b_ops = &xfs_inode_buf_ops;
- break;
- case XFS_BLFT_SYMLINK_BUF:
- if (magic32 != XFS_SYMLINK_MAGIC) {
- warnmsg = "Bad symlink block magic!";
- break;
- }
- bp->b_ops = &xfs_symlink_buf_ops;
- break;
- case XFS_BLFT_DIR_BLOCK_BUF:
- if (magic32 != XFS_DIR2_BLOCK_MAGIC &&
- magic32 != XFS_DIR3_BLOCK_MAGIC) {
- warnmsg = "Bad dir block magic!";
- break;
- }
- bp->b_ops = &xfs_dir3_block_buf_ops;
- break;
- case XFS_BLFT_DIR_DATA_BUF:
- if (magic32 != XFS_DIR2_DATA_MAGIC &&
- magic32 != XFS_DIR3_DATA_MAGIC) {
- warnmsg = "Bad dir data magic!";
- break;
- }
- bp->b_ops = &xfs_dir3_data_buf_ops;
- break;
- case XFS_BLFT_DIR_FREE_BUF:
- if (magic32 != XFS_DIR2_FREE_MAGIC &&
- magic32 != XFS_DIR3_FREE_MAGIC) {
- warnmsg = "Bad dir3 free magic!";
- break;
- }
- bp->b_ops = &xfs_dir3_free_buf_ops;
- break;
- case XFS_BLFT_DIR_LEAF1_BUF:
- if (magicda != XFS_DIR2_LEAF1_MAGIC &&
- magicda != XFS_DIR3_LEAF1_MAGIC) {
- warnmsg = "Bad dir leaf1 magic!";
- break;
- }
- bp->b_ops = &xfs_dir3_leaf1_buf_ops;
- break;
- case XFS_BLFT_DIR_LEAFN_BUF:
- if (magicda != XFS_DIR2_LEAFN_MAGIC &&
- magicda != XFS_DIR3_LEAFN_MAGIC) {
- warnmsg = "Bad dir leafn magic!";
- break;
- }
- bp->b_ops = &xfs_dir3_leafn_buf_ops;
- break;
- case XFS_BLFT_DA_NODE_BUF:
- if (magicda != XFS_DA_NODE_MAGIC &&
- magicda != XFS_DA3_NODE_MAGIC) {
- warnmsg = "Bad da node magic!";
- break;
- }
- bp->b_ops = &xfs_da3_node_buf_ops;
- break;
- case XFS_BLFT_ATTR_LEAF_BUF:
- if (magicda != XFS_ATTR_LEAF_MAGIC &&
- magicda != XFS_ATTR3_LEAF_MAGIC) {
- warnmsg = "Bad attr leaf magic!";
- break;
- }
- bp->b_ops = &xfs_attr3_leaf_buf_ops;
- break;
- case XFS_BLFT_ATTR_RMT_BUF:
- if (magic32 != XFS_ATTR3_RMT_MAGIC) {
- warnmsg = "Bad attr remote magic!";
- break;
- }
- bp->b_ops = &xfs_attr3_rmt_buf_ops;
- break;
- case XFS_BLFT_SB_BUF:
- if (magic32 != XFS_SB_MAGIC) {
- warnmsg = "Bad SB block magic!";
- break;
- }
- bp->b_ops = &xfs_sb_buf_ops;
- break;
-#ifdef CONFIG_XFS_RT
- case XFS_BLFT_RTBITMAP_BUF:
- case XFS_BLFT_RTSUMMARY_BUF:
- /* no magic numbers for verification of RT buffers */
- bp->b_ops = &xfs_rtbuf_ops;
- break;
-#endif /* CONFIG_XFS_RT */
- default:
- xfs_warn(mp, "Unknown buffer type %d!",
- xfs_blft_from_flags(buf_f));
- break;
- }
-
- /*
- * Nothing else to do in the case of a NULL current LSN as this means
- * the buffer is more recent than the change in the log and will be
- * skipped.
- */
- if (current_lsn == NULLCOMMITLSN)
- return;
-
- if (warnmsg) {
- xfs_warn(mp, warnmsg);
- ASSERT(0);
- }
-
- /*
- * We must update the metadata LSN of the buffer as it is written out to
- * ensure that older transactions never replay over this one and corrupt
- * the buffer. This can occur if log recovery is interrupted at some
- * point after the current transaction completes, at which point a
- * subsequent mount starts recovery from the beginning.
- *
- * Write verifiers update the metadata LSN from log items attached to
- * the buffer. Therefore, initialize a bli purely to carry the LSN to
- * the verifier. We'll clean it up in our ->iodone() callback.
- */
- if (bp->b_ops) {
- struct xfs_buf_log_item *bip;
-
- ASSERT(!bp->b_iodone || bp->b_iodone == xlog_recover_iodone);
- bp->b_iodone = xlog_recover_iodone;
- xfs_buf_item_init(bp, mp);
- bip = bp->b_log_item;
- bip->bli_item.li_lsn = current_lsn;
- }
-}
-
-/*
- * Perform a 'normal' buffer recovery. Each logged region of the
- * buffer should be copied over the corresponding region in the
- * given buffer. The bitmap in the buf log format structure indicates
- * where to place the logged data.
- */
-STATIC void
-xlog_recover_do_reg_buffer(
- struct xfs_mount *mp,
- struct xlog_recover_item *item,
- struct xfs_buf *bp,
- xfs_buf_log_format_t *buf_f,
- xfs_lsn_t current_lsn)
-{
- int i;
- int bit;
- int nbits;
- xfs_failaddr_t fa;
- const size_t size_disk_dquot = sizeof(struct xfs_disk_dquot);
-
- trace_xfs_log_recover_buf_reg_buf(mp->m_log, buf_f);
-
- bit = 0;
- i = 1; /* 0 is the buf format structure */
- while (1) {
- bit = xfs_next_bit(buf_f->blf_data_map,
- buf_f->blf_map_size, bit);
- if (bit == -1)
- break;
- nbits = xfs_contig_bits(buf_f->blf_data_map,
- buf_f->blf_map_size, bit);
- ASSERT(nbits > 0);
- ASSERT(item->ri_buf[i].i_addr != NULL);
- ASSERT(item->ri_buf[i].i_len % XFS_BLF_CHUNK == 0);
- ASSERT(BBTOB(bp->b_length) >=
- ((uint)bit << XFS_BLF_SHIFT) + (nbits << XFS_BLF_SHIFT));
-
- /*
- * The dirty regions logged in the buffer, even though
- * contiguous, may span multiple chunks. This is because the
- * dirty region may span a physical page boundary in a buffer
- * and hence be split into two separate vectors for writing into
- * the log. Hence we need to trim nbits back to the length of
- * the current region being copied out of the log.
- */
- if (item->ri_buf[i].i_len < (nbits << XFS_BLF_SHIFT))
- nbits = item->ri_buf[i].i_len >> XFS_BLF_SHIFT;
-
- /*
- * Do a sanity check if this is a dquot buffer. Just checking
- * the first dquot in the buffer should do. XXXThis is
- * probably a good thing to do for other buf types also.
- */
- fa = NULL;
- if (buf_f->blf_flags &
- (XFS_BLF_UDQUOT_BUF|XFS_BLF_PDQUOT_BUF|XFS_BLF_GDQUOT_BUF)) {
- if (item->ri_buf[i].i_addr == NULL) {
- xfs_alert(mp,
- "XFS: NULL dquot in %s.", __func__);
- goto next;
- }
- if (item->ri_buf[i].i_len < size_disk_dquot) {
- xfs_alert(mp,
- "XFS: dquot too small (%d) in %s.",
- item->ri_buf[i].i_len, __func__);
- goto next;
- }
- fa = xfs_dquot_verify(mp, item->ri_buf[i].i_addr,
- -1, 0);
- if (fa) {
- xfs_alert(mp,
- "dquot corrupt at %pS trying to replay into block 0x%llx",
- fa, bp->b_bn);
- goto next;
- }
- }
-
- memcpy(xfs_buf_offset(bp,
- (uint)bit << XFS_BLF_SHIFT), /* dest */
- item->ri_buf[i].i_addr, /* source */
- nbits<<XFS_BLF_SHIFT); /* length */
- next:
- i++;
- bit += nbits;
- }
-
- /* Shouldn't be any more regions */
- ASSERT(i == item->ri_total);
-
- xlog_recover_validate_buf_type(mp, bp, buf_f, current_lsn);
-}
-
-/*
- * Perform a dquot buffer recovery.
- * Simple algorithm: if we have found a QUOTAOFF log item of the same type
- * (ie. USR or GRP), then just toss this buffer away; don't recover it.
- * Else, treat it as a regular buffer and do recovery.
- *
- * Return false if the buffer was tossed and true if we recovered the buffer to
- * indicate to the caller if the buffer needs writing.
- */
-STATIC bool
-xlog_recover_do_dquot_buffer(
- struct xfs_mount *mp,
- struct xlog *log,
- struct xlog_recover_item *item,
- struct xfs_buf *bp,
- struct xfs_buf_log_format *buf_f)
-{
- uint type;
-
- trace_xfs_log_recover_buf_dquot_buf(log, buf_f);
-
- /*
- * Filesystems are required to send in quota flags at mount time.
- */
- if (!mp->m_qflags)
- return false;
-
- type = 0;
- if (buf_f->blf_flags & XFS_BLF_UDQUOT_BUF)
- type |= XFS_DQ_USER;
- if (buf_f->blf_flags & XFS_BLF_PDQUOT_BUF)
- type |= XFS_DQ_PROJ;
- if (buf_f->blf_flags & XFS_BLF_GDQUOT_BUF)
- type |= XFS_DQ_GROUP;
- /*
- * This type of quotas was turned off, so ignore this buffer
- */
- if (log->l_quotaoffs_flag & type)
- return false;
-
- xlog_recover_do_reg_buffer(mp, item, bp, buf_f, NULLCOMMITLSN);
- return true;
-}
-
-/*
- * This routine replays a modification made to a buffer at runtime.
- * There are actually two types of buffer, regular and inode, which
- * are handled differently. Inode buffers are handled differently
- * in that we only recover a specific set of data from them, namely
- * the inode di_next_unlinked fields. This is because all other inode
- * data is actually logged via inode records and any data we replay
- * here which overlaps that may be stale.
- *
- * When meta-data buffers are freed at run time we log a buffer item
- * with the XFS_BLF_CANCEL bit set to indicate that previous copies
- * of the buffer in the log should not be replayed at recovery time.
- * This is so that if the blocks covered by the buffer are reused for
- * file data before we crash we don't end up replaying old, freed
- * meta-data into a user's file.
- *
- * To handle the cancellation of buffer log items, we make two passes
- * over the log during recovery. During the first we build a table of
- * those buffers which have been cancelled, and during the second we
- * only replay those buffers which do not have corresponding cancel
- * records in the table. See xlog_recover_buffer_pass[1,2] above
- * for more details on the implementation of the table of cancel records.
- */
-STATIC int
-xlog_recover_buffer_pass2(
- struct xlog *log,
- struct list_head *buffer_list,
- struct xlog_recover_item *item,
- xfs_lsn_t current_lsn)
-{
- xfs_buf_log_format_t *buf_f = item->ri_buf[0].i_addr;
- xfs_mount_t *mp = log->l_mp;
- xfs_buf_t *bp;
- int error;
- uint buf_flags;
- xfs_lsn_t lsn;
-
- /*
- * In this pass we only want to recover all the buffers which have
- * not been cancelled and are not cancellation buffers themselves.
- */
- if (buf_f->blf_flags & XFS_BLF_CANCEL) {
- if (xlog_put_buffer_cancelled(log, buf_f->blf_blkno,
- buf_f->blf_len))
- goto cancelled;
- } else {
-
- if (xlog_is_buffer_cancelled(log, buf_f->blf_blkno,
- buf_f->blf_len))
- goto cancelled;
- }
-
- trace_xfs_log_recover_buf_recover(log, buf_f);
-
- buf_flags = 0;
- if (buf_f->blf_flags & XFS_BLF_INODE_BUF)
- buf_flags |= XBF_UNMAPPED;
-
- error = xfs_buf_read(mp->m_ddev_targp, buf_f->blf_blkno, buf_f->blf_len,
- buf_flags, &bp, NULL);
- if (error)
- return error;
-
- /*
- * Recover the buffer only if we get an LSN from it and it's less than
- * the lsn of the transaction we are replaying.
- *
- * Note that we have to be extremely careful of readahead here.
- * Readahead does not attach verfiers to the buffers so if we don't
- * actually do any replay after readahead because of the LSN we found
- * in the buffer if more recent than that current transaction then we
- * need to attach the verifier directly. Failure to do so can lead to
- * future recovery actions (e.g. EFI and unlinked list recovery) can
- * operate on the buffers and they won't get the verifier attached. This
- * can lead to blocks on disk having the correct content but a stale
- * CRC.
- *
- * It is safe to assume these clean buffers are currently up to date.
- * If the buffer is dirtied by a later transaction being replayed, then
- * the verifier will be reset to match whatever recover turns that
- * buffer into.
- */
- lsn = xlog_recover_get_buf_lsn(mp, bp);
- if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) >= 0) {
- trace_xfs_log_recover_buf_skip(log, buf_f);
- xlog_recover_validate_buf_type(mp, bp, buf_f, NULLCOMMITLSN);
- goto out_release;
- }
-
- if (buf_f->blf_flags & XFS_BLF_INODE_BUF) {
- error = xlog_recover_do_inode_buffer(mp, item, bp, buf_f);
- if (error)
- goto out_release;
- } else if (buf_f->blf_flags &
- (XFS_BLF_UDQUOT_BUF|XFS_BLF_PDQUOT_BUF|XFS_BLF_GDQUOT_BUF)) {
- bool dirty;
-
- dirty = xlog_recover_do_dquot_buffer(mp, log, item, bp, buf_f);
- if (!dirty)
- goto out_release;
- } else {
- xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn);
- }
-
- /*
- * Perform delayed write on the buffer. Asynchronous writes will be
- * slower when taking into account all the buffers to be flushed.
- *
- * Also make sure that only inode buffers with good sizes stay in
- * the buffer cache. The kernel moves inodes in buffers of 1 block
- * or inode_cluster_size bytes, whichever is bigger. The inode
- * buffers in the log can be a different size if the log was generated
- * by an older kernel using unclustered inode buffers or a newer kernel
- * running with a different inode cluster size. Regardless, if the
- * the inode buffer size isn't max(blocksize, inode_cluster_size)
- * for *our* value of inode_cluster_size, then we need to keep
- * the buffer out of the buffer cache so that the buffer won't
- * overlap with future reads of those inodes.
- */
- if (XFS_DINODE_MAGIC ==
- be16_to_cpu(*((__be16 *)xfs_buf_offset(bp, 0))) &&
- (BBTOB(bp->b_length) != M_IGEO(log->l_mp)->inode_cluster_size)) {
- xfs_buf_stale(bp);
- error = xfs_bwrite(bp);
- } else {
- ASSERT(bp->b_mount == mp);
- bp->b_iodone = xlog_recover_iodone;
- xfs_buf_delwri_queue(bp, buffer_list);
- }
-
-out_release:
- xfs_buf_relse(bp);
- return error;
-cancelled:
- trace_xfs_log_recover_buf_cancel(log, buf_f);
- return 0;
-}
-
/*
* Inode fork owner changes
*
@@ -3846,10 +3061,11 @@ xlog_recover_commit_pass2(
{
trace_xfs_log_recover_item_recover(log, trans, item, XLOG_RECOVER_PASS2);
+ if (item->ri_ops && item->ri_ops->commit_pass2)
+ return item->ri_ops->commit_pass2(log, buffer_list, item,
+ trans->r_lsn);
+
switch (ITEM_TYPE(item)) {
- case XFS_LI_BUF:
- return xlog_recover_buffer_pass2(log, buffer_list, item,
- trans->r_lsn);
case XFS_LI_INODE:
return xlog_recover_inode_pass2(log, buffer_list, item,
trans->r_lsn);
^ permalink raw reply related [flat|nested] 94+ messages in thread* Re: [PATCH 05/28] xfs: refactor log recovery buffer item dispatch for pass2 commit functions
2020-05-05 1:11 ` [PATCH 05/28] xfs: refactor log recovery buffer item dispatch for pass2 " Darrick J. Wong
@ 2020-05-05 5:03 ` Chandan Babu R
2020-05-06 15:09 ` Christoph Hellwig
1 sibling, 0 replies; 94+ messages in thread
From: Chandan Babu R @ 2020-05-05 5:03 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-xfs
On Tuesday 5 May 2020 6:41:03 AM IST Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Move the log buffer item pass2 commit code into the per-item source code
> files and use the dispatch function to call it. We do these one at a
> time because there's a lot of code to move. No functional changes.
>
Buffer item pass2 processing is functionally consistent with what was done
before this patch is applied.
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
> fs/xfs/libxfs/xfs_log_recover.h | 23 +
> fs/xfs/xfs_buf_item_recover.c | 790 +++++++++++++++++++++++++++++++++++++++
> fs/xfs/xfs_log_recover.c | 798 ---------------------------------------
> 3 files changed, 820 insertions(+), 791 deletions(-)
>
>
> diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h
> index 384b70d58993..a45f6e9fa47b 100644
> --- a/fs/xfs/libxfs/xfs_log_recover.h
> +++ b/fs/xfs/libxfs/xfs_log_recover.h
> @@ -37,6 +37,26 @@ struct xlog_recover_item_ops {
>
> /* Do whatever work we need to do for pass1, if provided. */
> int (*commit_pass1)(struct xlog *log, struct xlog_recover_item *item);
> +
> + /*
> + * This function should do whatever work is needed for pass2 of log
> + * recovery, if provided.
> + *
> + * If the recovered item is an intent item, this function should parse
> + * the recovered item to construct an in-core log intent item and
> + * insert it into the AIL. The in-core log intent item should have 1
> + * refcount so that the item is freed either (a) when we commit the
> + * recovered log item for the intent-done item; (b) replay the work and
> + * log a new intent-done item; or (c) recovery fails and we have to
> + * abort.
> + *
> + * If the recovered item is an intent-done item, this function should
> + * parse the recovered item to find the id of the corresponding intent
> + * log item. Next, it should find the in-core log intent item in the
> + * AIL and release it.
> + */
> + int (*commit_pass2)(struct xlog *log, struct list_head *buffer_list,
> + struct xlog_recover_item *item, xfs_lsn_t lsn);
> };
>
> extern const struct xlog_recover_item_ops xlog_icreate_item_ops;
> @@ -101,5 +121,8 @@ struct xlog_recover {
> void xlog_buf_readahead(struct xlog *log, xfs_daddr_t blkno, uint len,
> const struct xfs_buf_ops *ops);
> bool xlog_add_buffer_cancelled(struct xlog *log, xfs_daddr_t blkno, uint len);
> +bool xlog_is_buffer_cancelled(struct xlog *log, xfs_daddr_t blkno, uint len);
> +bool xlog_put_buffer_cancelled(struct xlog *log, xfs_daddr_t blkno, uint len);
> +void xlog_recover_iodone(struct xfs_buf *bp);
>
> #endif /* __XFS_LOG_RECOVER_H__ */
> diff --git a/fs/xfs/xfs_buf_item_recover.c b/fs/xfs/xfs_buf_item_recover.c
> index 802f2206516d..4ca6d47d6c95 100644
> --- a/fs/xfs/xfs_buf_item_recover.c
> +++ b/fs/xfs/xfs_buf_item_recover.c
> @@ -18,6 +18,10 @@
> #include "xfs_log.h"
> #include "xfs_log_priv.h"
> #include "xfs_log_recover.h"
> +#include "xfs_error.h"
> +#include "xfs_inode.h"
> +#include "xfs_dir2.h"
> +#include "xfs_quota.h"
>
> STATIC enum xlog_recover_reorder
> xlog_recover_buf_reorder(
> @@ -68,9 +72,795 @@ xlog_recover_buf_commit_pass1(
> return 0;
> }
>
> +/*
> + * Validate the recovered buffer is of the correct type and attach the
> + * appropriate buffer operations to them for writeback. Magic numbers are in a
> + * few places:
> + * the first 16 bits of the buffer (inode buffer, dquot buffer),
> + * the first 32 bits of the buffer (most blocks),
> + * inside a struct xfs_da_blkinfo at the start of the buffer.
> + */
> +static void
> +xlog_recover_validate_buf_type(
> + struct xfs_mount *mp,
> + struct xfs_buf *bp,
> + struct xfs_buf_log_format *buf_f,
> + xfs_lsn_t current_lsn)
> +{
> + struct xfs_da_blkinfo *info = bp->b_addr;
> + uint32_t magic32;
> + uint16_t magic16;
> + uint16_t magicda;
> + char *warnmsg = NULL;
> +
> + /*
> + * We can only do post recovery validation on items on CRC enabled
> + * fielsystems as we need to know when the buffer was written to be able
> + * to determine if we should have replayed the item. If we replay old
> + * metadata over a newer buffer, then it will enter a temporarily
> + * inconsistent state resulting in verification failures. Hence for now
> + * just avoid the verification stage for non-crc filesystems
> + */
> + if (!xfs_sb_version_hascrc(&mp->m_sb))
> + return;
> +
> + magic32 = be32_to_cpu(*(__be32 *)bp->b_addr);
> + magic16 = be16_to_cpu(*(__be16*)bp->b_addr);
> + magicda = be16_to_cpu(info->magic);
> + switch (xfs_blft_from_flags(buf_f)) {
> + case XFS_BLFT_BTREE_BUF:
> + switch (magic32) {
> + case XFS_ABTB_CRC_MAGIC:
> + case XFS_ABTB_MAGIC:
> + bp->b_ops = &xfs_bnobt_buf_ops;
> + break;
> + case XFS_ABTC_CRC_MAGIC:
> + case XFS_ABTC_MAGIC:
> + bp->b_ops = &xfs_cntbt_buf_ops;
> + break;
> + case XFS_IBT_CRC_MAGIC:
> + case XFS_IBT_MAGIC:
> + bp->b_ops = &xfs_inobt_buf_ops;
> + break;
> + case XFS_FIBT_CRC_MAGIC:
> + case XFS_FIBT_MAGIC:
> + bp->b_ops = &xfs_finobt_buf_ops;
> + break;
> + case XFS_BMAP_CRC_MAGIC:
> + case XFS_BMAP_MAGIC:
> + bp->b_ops = &xfs_bmbt_buf_ops;
> + break;
> + 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;
> + }
> + break;
> + case XFS_BLFT_AGF_BUF:
> + if (magic32 != XFS_AGF_MAGIC) {
> + warnmsg = "Bad AGF block magic!";
> + break;
> + }
> + bp->b_ops = &xfs_agf_buf_ops;
> + break;
> + case XFS_BLFT_AGFL_BUF:
> + if (magic32 != XFS_AGFL_MAGIC) {
> + warnmsg = "Bad AGFL block magic!";
> + break;
> + }
> + bp->b_ops = &xfs_agfl_buf_ops;
> + break;
> + case XFS_BLFT_AGI_BUF:
> + if (magic32 != XFS_AGI_MAGIC) {
> + warnmsg = "Bad AGI block magic!";
> + break;
> + }
> + bp->b_ops = &xfs_agi_buf_ops;
> + break;
> + case XFS_BLFT_UDQUOT_BUF:
> + case XFS_BLFT_PDQUOT_BUF:
> + case XFS_BLFT_GDQUOT_BUF:
> +#ifdef CONFIG_XFS_QUOTA
> + if (magic16 != XFS_DQUOT_MAGIC) {
> + warnmsg = "Bad DQUOT block magic!";
> + break;
> + }
> + bp->b_ops = &xfs_dquot_buf_ops;
> +#else
> + xfs_alert(mp,
> + "Trying to recover dquots without QUOTA support built in!");
> + ASSERT(0);
> +#endif
> + break;
> + case XFS_BLFT_DINO_BUF:
> + if (magic16 != XFS_DINODE_MAGIC) {
> + warnmsg = "Bad INODE block magic!";
> + break;
> + }
> + bp->b_ops = &xfs_inode_buf_ops;
> + break;
> + case XFS_BLFT_SYMLINK_BUF:
> + if (magic32 != XFS_SYMLINK_MAGIC) {
> + warnmsg = "Bad symlink block magic!";
> + break;
> + }
> + bp->b_ops = &xfs_symlink_buf_ops;
> + break;
> + case XFS_BLFT_DIR_BLOCK_BUF:
> + if (magic32 != XFS_DIR2_BLOCK_MAGIC &&
> + magic32 != XFS_DIR3_BLOCK_MAGIC) {
> + warnmsg = "Bad dir block magic!";
> + break;
> + }
> + bp->b_ops = &xfs_dir3_block_buf_ops;
> + break;
> + case XFS_BLFT_DIR_DATA_BUF:
> + if (magic32 != XFS_DIR2_DATA_MAGIC &&
> + magic32 != XFS_DIR3_DATA_MAGIC) {
> + warnmsg = "Bad dir data magic!";
> + break;
> + }
> + bp->b_ops = &xfs_dir3_data_buf_ops;
> + break;
> + case XFS_BLFT_DIR_FREE_BUF:
> + if (magic32 != XFS_DIR2_FREE_MAGIC &&
> + magic32 != XFS_DIR3_FREE_MAGIC) {
> + warnmsg = "Bad dir3 free magic!";
> + break;
> + }
> + bp->b_ops = &xfs_dir3_free_buf_ops;
> + break;
> + case XFS_BLFT_DIR_LEAF1_BUF:
> + if (magicda != XFS_DIR2_LEAF1_MAGIC &&
> + magicda != XFS_DIR3_LEAF1_MAGIC) {
> + warnmsg = "Bad dir leaf1 magic!";
> + break;
> + }
> + bp->b_ops = &xfs_dir3_leaf1_buf_ops;
> + break;
> + case XFS_BLFT_DIR_LEAFN_BUF:
> + if (magicda != XFS_DIR2_LEAFN_MAGIC &&
> + magicda != XFS_DIR3_LEAFN_MAGIC) {
> + warnmsg = "Bad dir leafn magic!";
> + break;
> + }
> + bp->b_ops = &xfs_dir3_leafn_buf_ops;
> + break;
> + case XFS_BLFT_DA_NODE_BUF:
> + if (magicda != XFS_DA_NODE_MAGIC &&
> + magicda != XFS_DA3_NODE_MAGIC) {
> + warnmsg = "Bad da node magic!";
> + break;
> + }
> + bp->b_ops = &xfs_da3_node_buf_ops;
> + break;
> + case XFS_BLFT_ATTR_LEAF_BUF:
> + if (magicda != XFS_ATTR_LEAF_MAGIC &&
> + magicda != XFS_ATTR3_LEAF_MAGIC) {
> + warnmsg = "Bad attr leaf magic!";
> + break;
> + }
> + bp->b_ops = &xfs_attr3_leaf_buf_ops;
> + break;
> + case XFS_BLFT_ATTR_RMT_BUF:
> + if (magic32 != XFS_ATTR3_RMT_MAGIC) {
> + warnmsg = "Bad attr remote magic!";
> + break;
> + }
> + bp->b_ops = &xfs_attr3_rmt_buf_ops;
> + break;
> + case XFS_BLFT_SB_BUF:
> + if (magic32 != XFS_SB_MAGIC) {
> + warnmsg = "Bad SB block magic!";
> + break;
> + }
> + bp->b_ops = &xfs_sb_buf_ops;
> + break;
> +#ifdef CONFIG_XFS_RT
> + case XFS_BLFT_RTBITMAP_BUF:
> + case XFS_BLFT_RTSUMMARY_BUF:
> + /* no magic numbers for verification of RT buffers */
> + bp->b_ops = &xfs_rtbuf_ops;
> + break;
> +#endif /* CONFIG_XFS_RT */
> + default:
> + xfs_warn(mp, "Unknown buffer type %d!",
> + xfs_blft_from_flags(buf_f));
> + break;
> + }
> +
> + /*
> + * Nothing else to do in the case of a NULL current LSN as this means
> + * the buffer is more recent than the change in the log and will be
> + * skipped.
> + */
> + if (current_lsn == NULLCOMMITLSN)
> + return;
> +
> + if (warnmsg) {
> + xfs_warn(mp, warnmsg);
> + ASSERT(0);
> + }
> +
> + /*
> + * We must update the metadata LSN of the buffer as it is written out to
> + * ensure that older transactions never replay over this one and corrupt
> + * the buffer. This can occur if log recovery is interrupted at some
> + * point after the current transaction completes, at which point a
> + * subsequent mount starts recovery from the beginning.
> + *
> + * Write verifiers update the metadata LSN from log items attached to
> + * the buffer. Therefore, initialize a bli purely to carry the LSN to
> + * the verifier. We'll clean it up in our ->iodone() callback.
> + */
> + if (bp->b_ops) {
> + struct xfs_buf_log_item *bip;
> +
> + ASSERT(!bp->b_iodone || bp->b_iodone == xlog_recover_iodone);
> + bp->b_iodone = xlog_recover_iodone;
> + xfs_buf_item_init(bp, mp);
> + bip = bp->b_log_item;
> + bip->bli_item.li_lsn = current_lsn;
> + }
> +}
> +
> +/*
> + * Perform a 'normal' buffer recovery. Each logged region of the
> + * buffer should be copied over the corresponding region in the
> + * given buffer. The bitmap in the buf log format structure indicates
> + * where to place the logged data.
> + */
> +STATIC void
> +xlog_recover_do_reg_buffer(
> + struct xfs_mount *mp,
> + struct xlog_recover_item *item,
> + struct xfs_buf *bp,
> + struct xfs_buf_log_format *buf_f,
> + xfs_lsn_t current_lsn)
> +{
> + int i;
> + int bit;
> + int nbits;
> + xfs_failaddr_t fa;
> + const size_t size_disk_dquot = sizeof(struct xfs_disk_dquot);
> +
> + trace_xfs_log_recover_buf_reg_buf(mp->m_log, buf_f);
> +
> + bit = 0;
> + i = 1; /* 0 is the buf format structure */
> + while (1) {
> + bit = xfs_next_bit(buf_f->blf_data_map,
> + buf_f->blf_map_size, bit);
> + if (bit == -1)
> + break;
> + nbits = xfs_contig_bits(buf_f->blf_data_map,
> + buf_f->blf_map_size, bit);
> + ASSERT(nbits > 0);
> + ASSERT(item->ri_buf[i].i_addr != NULL);
> + ASSERT(item->ri_buf[i].i_len % XFS_BLF_CHUNK == 0);
> + ASSERT(BBTOB(bp->b_length) >=
> + ((uint)bit << XFS_BLF_SHIFT) + (nbits << XFS_BLF_SHIFT));
> +
> + /*
> + * The dirty regions logged in the buffer, even though
> + * contiguous, may span multiple chunks. This is because the
> + * dirty region may span a physical page boundary in a buffer
> + * and hence be split into two separate vectors for writing into
> + * the log. Hence we need to trim nbits back to the length of
> + * the current region being copied out of the log.
> + */
> + if (item->ri_buf[i].i_len < (nbits << XFS_BLF_SHIFT))
> + nbits = item->ri_buf[i].i_len >> XFS_BLF_SHIFT;
> +
> + /*
> + * Do a sanity check if this is a dquot buffer. Just checking
> + * the first dquot in the buffer should do. XXXThis is
> + * probably a good thing to do for other buf types also.
> + */
> + fa = NULL;
> + if (buf_f->blf_flags &
> + (XFS_BLF_UDQUOT_BUF|XFS_BLF_PDQUOT_BUF|XFS_BLF_GDQUOT_BUF)) {
> + if (item->ri_buf[i].i_addr == NULL) {
> + xfs_alert(mp,
> + "XFS: NULL dquot in %s.", __func__);
> + goto next;
> + }
> + if (item->ri_buf[i].i_len < size_disk_dquot) {
> + xfs_alert(mp,
> + "XFS: dquot too small (%d) in %s.",
> + item->ri_buf[i].i_len, __func__);
> + goto next;
> + }
> + fa = xfs_dquot_verify(mp, item->ri_buf[i].i_addr,
> + -1, 0);
> + if (fa) {
> + xfs_alert(mp,
> + "dquot corrupt at %pS trying to replay into block 0x%llx",
> + fa, bp->b_bn);
> + goto next;
> + }
> + }
> +
> + memcpy(xfs_buf_offset(bp,
> + (uint)bit << XFS_BLF_SHIFT), /* dest */
> + item->ri_buf[i].i_addr, /* source */
> + nbits<<XFS_BLF_SHIFT); /* length */
> + next:
> + i++;
> + bit += nbits;
> + }
> +
> + /* Shouldn't be any more regions */
> + ASSERT(i == item->ri_total);
> +
> + xlog_recover_validate_buf_type(mp, bp, buf_f, current_lsn);
> +}
> +
> +/*
> + * Perform a dquot buffer recovery.
> + * Simple algorithm: if we have found a QUOTAOFF log item of the same type
> + * (ie. USR or GRP), then just toss this buffer away; don't recover it.
> + * Else, treat it as a regular buffer and do recovery.
> + *
> + * Return false if the buffer was tossed and true if we recovered the buffer to
> + * indicate to the caller if the buffer needs writing.
> + */
> +STATIC bool
> +xlog_recover_do_dquot_buffer(
> + struct xfs_mount *mp,
> + struct xlog *log,
> + struct xlog_recover_item *item,
> + struct xfs_buf *bp,
> + struct xfs_buf_log_format *buf_f)
> +{
> + uint type;
> +
> + trace_xfs_log_recover_buf_dquot_buf(log, buf_f);
> +
> + /*
> + * Filesystems are required to send in quota flags at mount time.
> + */
> + if (!mp->m_qflags)
> + return false;
> +
> + type = 0;
> + if (buf_f->blf_flags & XFS_BLF_UDQUOT_BUF)
> + type |= XFS_DQ_USER;
> + if (buf_f->blf_flags & XFS_BLF_PDQUOT_BUF)
> + type |= XFS_DQ_PROJ;
> + if (buf_f->blf_flags & XFS_BLF_GDQUOT_BUF)
> + type |= XFS_DQ_GROUP;
> + /*
> + * This type of quotas was turned off, so ignore this buffer
> + */
> + if (log->l_quotaoffs_flag & type)
> + return false;
> +
> + xlog_recover_do_reg_buffer(mp, item, bp, buf_f, NULLCOMMITLSN);
> + return true;
> +}
> +
> +/*
> + * Perform recovery for a buffer full of inodes. In these buffers, the only
> + * data which should be recovered is that which corresponds to the
> + * di_next_unlinked pointers in the on disk inode structures. The rest of the
> + * data for the inodes is always logged through the inodes themselves rather
> + * than the inode buffer and is recovered in xlog_recover_inode_pass2().
> + *
> + * The only time when buffers full of inodes are fully recovered is when the
> + * buffer is full of newly allocated inodes. In this case the buffer will
> + * not be marked as an inode buffer and so will be sent to
> + * xlog_recover_do_reg_buffer() below during recovery.
> + */
> +STATIC int
> +xlog_recover_do_inode_buffer(
> + struct xfs_mount *mp,
> + struct xlog_recover_item *item,
> + struct xfs_buf *bp,
> + struct xfs_buf_log_format *buf_f)
> +{
> + int i;
> + int item_index = 0;
> + int bit = 0;
> + int nbits = 0;
> + int reg_buf_offset = 0;
> + int reg_buf_bytes = 0;
> + int next_unlinked_offset;
> + int inodes_per_buf;
> + xfs_agino_t *logged_nextp;
> + xfs_agino_t *buffer_nextp;
> +
> + trace_xfs_log_recover_buf_inode_buf(mp->m_log, buf_f);
> +
> + /*
> + * Post recovery validation only works properly on CRC enabled
> + * filesystems.
> + */
> + if (xfs_sb_version_hascrc(&mp->m_sb))
> + bp->b_ops = &xfs_inode_buf_ops;
> +
> + inodes_per_buf = BBTOB(bp->b_length) >> mp->m_sb.sb_inodelog;
> + for (i = 0; i < inodes_per_buf; i++) {
> + next_unlinked_offset = (i * mp->m_sb.sb_inodesize) +
> + offsetof(xfs_dinode_t, di_next_unlinked);
> +
> + while (next_unlinked_offset >=
> + (reg_buf_offset + reg_buf_bytes)) {
> + /*
> + * The next di_next_unlinked field is beyond
> + * the current logged region. Find the next
> + * logged region that contains or is beyond
> + * the current di_next_unlinked field.
> + */
> + bit += nbits;
> + bit = xfs_next_bit(buf_f->blf_data_map,
> + buf_f->blf_map_size, bit);
> +
> + /*
> + * If there are no more logged regions in the
> + * buffer, then we're done.
> + */
> + if (bit == -1)
> + return 0;
> +
> + nbits = xfs_contig_bits(buf_f->blf_data_map,
> + buf_f->blf_map_size, bit);
> + ASSERT(nbits > 0);
> + reg_buf_offset = bit << XFS_BLF_SHIFT;
> + reg_buf_bytes = nbits << XFS_BLF_SHIFT;
> + item_index++;
> + }
> +
> + /*
> + * If the current logged region starts after the current
> + * di_next_unlinked field, then move on to the next
> + * di_next_unlinked field.
> + */
> + if (next_unlinked_offset < reg_buf_offset)
> + continue;
> +
> + ASSERT(item->ri_buf[item_index].i_addr != NULL);
> + ASSERT((item->ri_buf[item_index].i_len % XFS_BLF_CHUNK) == 0);
> + ASSERT((reg_buf_offset + reg_buf_bytes) <= BBTOB(bp->b_length));
> +
> + /*
> + * The current logged region contains a copy of the
> + * current di_next_unlinked field. Extract its value
> + * and copy it to the buffer copy.
> + */
> + logged_nextp = item->ri_buf[item_index].i_addr +
> + next_unlinked_offset - reg_buf_offset;
> + if (XFS_IS_CORRUPT(mp, *logged_nextp == 0)) {
> + xfs_alert(mp,
> + "Bad inode buffer log record (ptr = "PTR_FMT", bp = "PTR_FMT"). "
> + "Trying to replay bad (0) inode di_next_unlinked field.",
> + item, bp);
> + return -EFSCORRUPTED;
> + }
> +
> + buffer_nextp = xfs_buf_offset(bp, next_unlinked_offset);
> + *buffer_nextp = *logged_nextp;
> +
> + /*
> + * If necessary, recalculate the CRC in the on-disk inode. We
> + * have to leave the inode in a consistent state for whoever
> + * reads it next....
> + */
> + xfs_dinode_calc_crc(mp,
> + xfs_buf_offset(bp, i * mp->m_sb.sb_inodesize));
> +
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * V5 filesystems know the age of the buffer on disk being recovered. We can
> + * have newer objects on disk than we are replaying, and so for these cases we
> + * don't want to replay the current change as that will make the buffer contents
> + * temporarily invalid on disk.
> + *
> + * The magic number might not match the buffer type we are going to recover
> + * (e.g. reallocated blocks), so we ignore the xfs_buf_log_format flags. Hence
> + * extract the LSN of the existing object in the buffer based on it's current
> + * magic number. If we don't recognise the magic number in the buffer, then
> + * return a LSN of -1 so that the caller knows it was an unrecognised block and
> + * so can recover the buffer.
> + *
> + * Note: we cannot rely solely on magic number matches to determine that the
> + * buffer has a valid LSN - we also need to verify that it belongs to this
> + * filesystem, so we need to extract the object's LSN and compare it to that
> + * which we read from the superblock. If the UUIDs don't match, then we've got a
> + * stale metadata block from an old filesystem instance that we need to recover
> + * over the top of.
> + */
> +static xfs_lsn_t
> +xlog_recover_get_buf_lsn(
> + struct xfs_mount *mp,
> + struct xfs_buf *bp)
> +{
> + uint32_t magic32;
> + uint16_t magic16;
> + uint16_t magicda;
> + void *blk = bp->b_addr;
> + uuid_t *uuid;
> + xfs_lsn_t lsn = -1;
> +
> + /* v4 filesystems always recover immediately */
> + if (!xfs_sb_version_hascrc(&mp->m_sb))
> + goto recover_immediately;
> +
> + magic32 = be32_to_cpu(*(__be32 *)blk);
> + switch (magic32) {
> + case XFS_ABTB_CRC_MAGIC:
> + case XFS_ABTC_CRC_MAGIC:
> + 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;
> +
> + lsn = be64_to_cpu(btb->bb_u.s.bb_lsn);
> + uuid = &btb->bb_u.s.bb_uuid;
> + break;
> + }
> + case XFS_BMAP_CRC_MAGIC:
> + case XFS_BMAP_MAGIC: {
> + struct xfs_btree_block *btb = blk;
> +
> + lsn = be64_to_cpu(btb->bb_u.l.bb_lsn);
> + uuid = &btb->bb_u.l.bb_uuid;
> + break;
> + }
> + case XFS_AGF_MAGIC:
> + lsn = be64_to_cpu(((struct xfs_agf *)blk)->agf_lsn);
> + uuid = &((struct xfs_agf *)blk)->agf_uuid;
> + break;
> + case XFS_AGFL_MAGIC:
> + lsn = be64_to_cpu(((struct xfs_agfl *)blk)->agfl_lsn);
> + uuid = &((struct xfs_agfl *)blk)->agfl_uuid;
> + break;
> + case XFS_AGI_MAGIC:
> + lsn = be64_to_cpu(((struct xfs_agi *)blk)->agi_lsn);
> + uuid = &((struct xfs_agi *)blk)->agi_uuid;
> + break;
> + case XFS_SYMLINK_MAGIC:
> + lsn = be64_to_cpu(((struct xfs_dsymlink_hdr *)blk)->sl_lsn);
> + uuid = &((struct xfs_dsymlink_hdr *)blk)->sl_uuid;
> + break;
> + case XFS_DIR3_BLOCK_MAGIC:
> + case XFS_DIR3_DATA_MAGIC:
> + case XFS_DIR3_FREE_MAGIC:
> + lsn = be64_to_cpu(((struct xfs_dir3_blk_hdr *)blk)->lsn);
> + uuid = &((struct xfs_dir3_blk_hdr *)blk)->uuid;
> + break;
> + case XFS_ATTR3_RMT_MAGIC:
> + /*
> + * Remote attr blocks are written synchronously, rather than
> + * being logged. That means they do not contain a valid LSN
> + * (i.e. transactionally ordered) in them, and hence any time we
> + * see a buffer to replay over the top of a remote attribute
> + * block we should simply do so.
> + */
> + goto recover_immediately;
> + case XFS_SB_MAGIC:
> + /*
> + * superblock uuids are magic. We may or may not have a
> + * sb_meta_uuid on disk, but it will be set in the in-core
> + * superblock. We set the uuid pointer for verification
> + * according to the superblock feature mask to ensure we check
> + * the relevant UUID in the superblock.
> + */
> + lsn = be64_to_cpu(((struct xfs_dsb *)blk)->sb_lsn);
> + if (xfs_sb_version_hasmetauuid(&mp->m_sb))
> + uuid = &((struct xfs_dsb *)blk)->sb_meta_uuid;
> + else
> + uuid = &((struct xfs_dsb *)blk)->sb_uuid;
> + break;
> + default:
> + break;
> + }
> +
> + if (lsn != (xfs_lsn_t)-1) {
> + if (!uuid_equal(&mp->m_sb.sb_meta_uuid, uuid))
> + goto recover_immediately;
> + return lsn;
> + }
> +
> + magicda = be16_to_cpu(((struct xfs_da_blkinfo *)blk)->magic);
> + switch (magicda) {
> + case XFS_DIR3_LEAF1_MAGIC:
> + case XFS_DIR3_LEAFN_MAGIC:
> + case XFS_DA3_NODE_MAGIC:
> + lsn = be64_to_cpu(((struct xfs_da3_blkinfo *)blk)->lsn);
> + uuid = &((struct xfs_da3_blkinfo *)blk)->uuid;
> + break;
> + default:
> + break;
> + }
> +
> + if (lsn != (xfs_lsn_t)-1) {
> + if (!uuid_equal(&mp->m_sb.sb_uuid, uuid))
> + goto recover_immediately;
> + return lsn;
> + }
> +
> + /*
> + * We do individual object checks on dquot and inode buffers as they
> + * have their own individual LSN records. Also, we could have a stale
> + * buffer here, so we have to at least recognise these buffer types.
> + *
> + * A notd complexity here is inode unlinked list processing - it logs
> + * the inode directly in the buffer, but we don't know which inodes have
> + * been modified, and there is no global buffer LSN. Hence we need to
> + * recover all inode buffer types immediately. This problem will be
> + * fixed by logical logging of the unlinked list modifications.
> + */
> + magic16 = be16_to_cpu(*(__be16 *)blk);
> + switch (magic16) {
> + case XFS_DQUOT_MAGIC:
> + case XFS_DINODE_MAGIC:
> + goto recover_immediately;
> + default:
> + break;
> + }
> +
> + /* unknown buffer contents, recover immediately */
> +
> +recover_immediately:
> + return (xfs_lsn_t)-1;
> +
> +}
> +
> +/*
> + * This routine replays a modification made to a buffer at runtime.
> + * There are actually two types of buffer, regular and inode, which
> + * are handled differently. Inode buffers are handled differently
> + * in that we only recover a specific set of data from them, namely
> + * the inode di_next_unlinked fields. This is because all other inode
> + * data is actually logged via inode records and any data we replay
> + * here which overlaps that may be stale.
> + *
> + * When meta-data buffers are freed at run time we log a buffer item
> + * with the XFS_BLF_CANCEL bit set to indicate that previous copies
> + * of the buffer in the log should not be replayed at recovery time.
> + * This is so that if the blocks covered by the buffer are reused for
> + * file data before we crash we don't end up replaying old, freed
> + * meta-data into a user's file.
> + *
> + * To handle the cancellation of buffer log items, we make two passes
> + * over the log during recovery. During the first we build a table of
> + * those buffers which have been cancelled, and during the second we
> + * only replay those buffers which do not have corresponding cancel
> + * records in the table. See xlog_recover_buf_pass[1,2] above
> + * for more details on the implementation of the table of cancel records.
> + */
> +STATIC int
> +xlog_recover_buf_commit_pass2(
> + struct xlog *log,
> + struct list_head *buffer_list,
> + struct xlog_recover_item *item,
> + xfs_lsn_t current_lsn)
> +{
> + struct xfs_buf_log_format *buf_f = item->ri_buf[0].i_addr;
> + struct xfs_mount *mp = log->l_mp;
> + struct xfs_buf *bp;
> + int error;
> + uint buf_flags;
> + xfs_lsn_t lsn;
> +
> + /*
> + * In this pass we only want to recover all the buffers which have
> + * not been cancelled and are not cancellation buffers themselves.
> + */
> + if (buf_f->blf_flags & XFS_BLF_CANCEL) {
> + if (xlog_put_buffer_cancelled(log, buf_f->blf_blkno,
> + buf_f->blf_len))
> + goto cancelled;
> + } else {
> +
> + if (xlog_is_buffer_cancelled(log, buf_f->blf_blkno,
> + buf_f->blf_len))
> + goto cancelled;
> + }
> +
> + trace_xfs_log_recover_buf_recover(log, buf_f);
> +
> + buf_flags = 0;
> + if (buf_f->blf_flags & XFS_BLF_INODE_BUF)
> + buf_flags |= XBF_UNMAPPED;
> +
> + error = xfs_buf_read(mp->m_ddev_targp, buf_f->blf_blkno, buf_f->blf_len,
> + buf_flags, &bp, NULL);
> + if (error)
> + return error;
> +
> + /*
> + * Recover the buffer only if we get an LSN from it and it's less than
> + * the lsn of the transaction we are replaying.
> + *
> + * Note that we have to be extremely careful of readahead here.
> + * Readahead does not attach verfiers to the buffers so if we don't
> + * actually do any replay after readahead because of the LSN we found
> + * in the buffer if more recent than that current transaction then we
> + * need to attach the verifier directly. Failure to do so can lead to
> + * future recovery actions (e.g. EFI and unlinked list recovery) can
> + * operate on the buffers and they won't get the verifier attached. This
> + * can lead to blocks on disk having the correct content but a stale
> + * CRC.
> + *
> + * It is safe to assume these clean buffers are currently up to date.
> + * If the buffer is dirtied by a later transaction being replayed, then
> + * the verifier will be reset to match whatever recover turns that
> + * buffer into.
> + */
> + lsn = xlog_recover_get_buf_lsn(mp, bp);
> + if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) >= 0) {
> + trace_xfs_log_recover_buf_skip(log, buf_f);
> + xlog_recover_validate_buf_type(mp, bp, buf_f, NULLCOMMITLSN);
> + goto out_release;
> + }
> +
> + if (buf_f->blf_flags & XFS_BLF_INODE_BUF) {
> + error = xlog_recover_do_inode_buffer(mp, item, bp, buf_f);
> + if (error)
> + goto out_release;
> + } else if (buf_f->blf_flags &
> + (XFS_BLF_UDQUOT_BUF|XFS_BLF_PDQUOT_BUF|XFS_BLF_GDQUOT_BUF)) {
> + bool dirty;
> +
> + dirty = xlog_recover_do_dquot_buffer(mp, log, item, bp, buf_f);
> + if (!dirty)
> + goto out_release;
> + } else {
> + xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn);
> + }
> +
> + /*
> + * Perform delayed write on the buffer. Asynchronous writes will be
> + * slower when taking into account all the buffers to be flushed.
> + *
> + * Also make sure that only inode buffers with good sizes stay in
> + * the buffer cache. The kernel moves inodes in buffers of 1 block
> + * or inode_cluster_size bytes, whichever is bigger. The inode
> + * buffers in the log can be a different size if the log was generated
> + * by an older kernel using unclustered inode buffers or a newer kernel
> + * running with a different inode cluster size. Regardless, if the
> + * the inode buffer size isn't max(blocksize, inode_cluster_size)
> + * for *our* value of inode_cluster_size, then we need to keep
> + * the buffer out of the buffer cache so that the buffer won't
> + * overlap with future reads of those inodes.
> + */
> + if (XFS_DINODE_MAGIC ==
> + be16_to_cpu(*((__be16 *)xfs_buf_offset(bp, 0))) &&
> + (BBTOB(bp->b_length) != M_IGEO(log->l_mp)->inode_cluster_size)) {
> + xfs_buf_stale(bp);
> + error = xfs_bwrite(bp);
> + } else {
> + ASSERT(bp->b_mount == mp);
> + bp->b_iodone = xlog_recover_iodone;
> + xfs_buf_delwri_queue(bp, buffer_list);
> + }
> +
> +out_release:
> + xfs_buf_relse(bp);
> + return error;
> +cancelled:
> + trace_xfs_log_recover_buf_cancel(log, buf_f);
> + return 0;
> +}
> +
> const struct xlog_recover_item_ops xlog_buf_item_ops = {
> .item_type = XFS_LI_BUF,
> .reorder = xlog_recover_buf_reorder,
> .ra_pass2 = xlog_recover_buf_ra_pass2,
> .commit_pass1 = xlog_recover_buf_commit_pass1,
> + .commit_pass2 = xlog_recover_buf_commit_pass2,
> };
> diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
> index b3627ebf870e..d65dc3895a62 100644
> --- a/fs/xfs/xfs_log_recover.c
> +++ b/fs/xfs/xfs_log_recover.c
> @@ -284,7 +284,7 @@ xlog_header_check_mount(
> return 0;
> }
>
> -STATIC void
> +void
> xlog_recover_iodone(
> struct xfs_buf *bp)
> {
> @@ -1985,7 +1985,7 @@ xlog_add_buffer_cancelled(
> /*
> * Check if there is and entry for blkno, len in the buffer cancel record table.
> */
> -static bool
> +bool
> xlog_is_buffer_cancelled(
> struct xlog *log,
> xfs_daddr_t blkno,
> @@ -2002,7 +2002,7 @@ xlog_is_buffer_cancelled(
> * buffer is re-used again after its last cancellation we actually replay the
> * changes made at that point.
> */
> -static bool
> +bool
> xlog_put_buffer_cancelled(
> struct xlog *log,
> xfs_daddr_t blkno,
> @@ -2034,791 +2034,6 @@ xlog_buf_readahead(
> xfs_buf_readahead(log->l_mp->m_ddev_targp, blkno, len, ops);
> }
>
> -/*
> - * Perform recovery for a buffer full of inodes. In these buffers, the only
> - * data which should be recovered is that which corresponds to the
> - * di_next_unlinked pointers in the on disk inode structures. The rest of the
> - * data for the inodes is always logged through the inodes themselves rather
> - * than the inode buffer and is recovered in xlog_recover_inode_pass2().
> - *
> - * The only time when buffers full of inodes are fully recovered is when the
> - * buffer is full of newly allocated inodes. In this case the buffer will
> - * not be marked as an inode buffer and so will be sent to
> - * xlog_recover_do_reg_buffer() below during recovery.
> - */
> -STATIC int
> -xlog_recover_do_inode_buffer(
> - struct xfs_mount *mp,
> - struct xlog_recover_item *item,
> - struct xfs_buf *bp,
> - xfs_buf_log_format_t *buf_f)
> -{
> - int i;
> - int item_index = 0;
> - int bit = 0;
> - int nbits = 0;
> - int reg_buf_offset = 0;
> - int reg_buf_bytes = 0;
> - int next_unlinked_offset;
> - int inodes_per_buf;
> - xfs_agino_t *logged_nextp;
> - xfs_agino_t *buffer_nextp;
> -
> - trace_xfs_log_recover_buf_inode_buf(mp->m_log, buf_f);
> -
> - /*
> - * Post recovery validation only works properly on CRC enabled
> - * filesystems.
> - */
> - if (xfs_sb_version_hascrc(&mp->m_sb))
> - bp->b_ops = &xfs_inode_buf_ops;
> -
> - inodes_per_buf = BBTOB(bp->b_length) >> mp->m_sb.sb_inodelog;
> - for (i = 0; i < inodes_per_buf; i++) {
> - next_unlinked_offset = (i * mp->m_sb.sb_inodesize) +
> - offsetof(xfs_dinode_t, di_next_unlinked);
> -
> - while (next_unlinked_offset >=
> - (reg_buf_offset + reg_buf_bytes)) {
> - /*
> - * The next di_next_unlinked field is beyond
> - * the current logged region. Find the next
> - * logged region that contains or is beyond
> - * the current di_next_unlinked field.
> - */
> - bit += nbits;
> - bit = xfs_next_bit(buf_f->blf_data_map,
> - buf_f->blf_map_size, bit);
> -
> - /*
> - * If there are no more logged regions in the
> - * buffer, then we're done.
> - */
> - if (bit == -1)
> - return 0;
> -
> - nbits = xfs_contig_bits(buf_f->blf_data_map,
> - buf_f->blf_map_size, bit);
> - ASSERT(nbits > 0);
> - reg_buf_offset = bit << XFS_BLF_SHIFT;
> - reg_buf_bytes = nbits << XFS_BLF_SHIFT;
> - item_index++;
> - }
> -
> - /*
> - * If the current logged region starts after the current
> - * di_next_unlinked field, then move on to the next
> - * di_next_unlinked field.
> - */
> - if (next_unlinked_offset < reg_buf_offset)
> - continue;
> -
> - ASSERT(item->ri_buf[item_index].i_addr != NULL);
> - ASSERT((item->ri_buf[item_index].i_len % XFS_BLF_CHUNK) == 0);
> - ASSERT((reg_buf_offset + reg_buf_bytes) <= BBTOB(bp->b_length));
> -
> - /*
> - * The current logged region contains a copy of the
> - * current di_next_unlinked field. Extract its value
> - * and copy it to the buffer copy.
> - */
> - logged_nextp = item->ri_buf[item_index].i_addr +
> - next_unlinked_offset - reg_buf_offset;
> - if (XFS_IS_CORRUPT(mp, *logged_nextp == 0)) {
> - xfs_alert(mp,
> - "Bad inode buffer log record (ptr = "PTR_FMT", bp = "PTR_FMT"). "
> - "Trying to replay bad (0) inode di_next_unlinked field.",
> - item, bp);
> - return -EFSCORRUPTED;
> - }
> -
> - buffer_nextp = xfs_buf_offset(bp, next_unlinked_offset);
> - *buffer_nextp = *logged_nextp;
> -
> - /*
> - * If necessary, recalculate the CRC in the on-disk inode. We
> - * have to leave the inode in a consistent state for whoever
> - * reads it next....
> - */
> - xfs_dinode_calc_crc(mp,
> - xfs_buf_offset(bp, i * mp->m_sb.sb_inodesize));
> -
> - }
> -
> - return 0;
> -}
> -
> -/*
> - * V5 filesystems know the age of the buffer on disk being recovered. We can
> - * have newer objects on disk than we are replaying, and so for these cases we
> - * don't want to replay the current change as that will make the buffer contents
> - * temporarily invalid on disk.
> - *
> - * The magic number might not match the buffer type we are going to recover
> - * (e.g. reallocated blocks), so we ignore the xfs_buf_log_format flags. Hence
> - * extract the LSN of the existing object in the buffer based on it's current
> - * magic number. If we don't recognise the magic number in the buffer, then
> - * return a LSN of -1 so that the caller knows it was an unrecognised block and
> - * so can recover the buffer.
> - *
> - * Note: we cannot rely solely on magic number matches to determine that the
> - * buffer has a valid LSN - we also need to verify that it belongs to this
> - * filesystem, so we need to extract the object's LSN and compare it to that
> - * which we read from the superblock. If the UUIDs don't match, then we've got a
> - * stale metadata block from an old filesystem instance that we need to recover
> - * over the top of.
> - */
> -static xfs_lsn_t
> -xlog_recover_get_buf_lsn(
> - struct xfs_mount *mp,
> - struct xfs_buf *bp)
> -{
> - uint32_t magic32;
> - uint16_t magic16;
> - uint16_t magicda;
> - void *blk = bp->b_addr;
> - uuid_t *uuid;
> - xfs_lsn_t lsn = -1;
> -
> - /* v4 filesystems always recover immediately */
> - if (!xfs_sb_version_hascrc(&mp->m_sb))
> - goto recover_immediately;
> -
> - magic32 = be32_to_cpu(*(__be32 *)blk);
> - switch (magic32) {
> - case XFS_ABTB_CRC_MAGIC:
> - case XFS_ABTC_CRC_MAGIC:
> - 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;
> -
> - lsn = be64_to_cpu(btb->bb_u.s.bb_lsn);
> - uuid = &btb->bb_u.s.bb_uuid;
> - break;
> - }
> - case XFS_BMAP_CRC_MAGIC:
> - case XFS_BMAP_MAGIC: {
> - struct xfs_btree_block *btb = blk;
> -
> - lsn = be64_to_cpu(btb->bb_u.l.bb_lsn);
> - uuid = &btb->bb_u.l.bb_uuid;
> - break;
> - }
> - case XFS_AGF_MAGIC:
> - lsn = be64_to_cpu(((struct xfs_agf *)blk)->agf_lsn);
> - uuid = &((struct xfs_agf *)blk)->agf_uuid;
> - break;
> - case XFS_AGFL_MAGIC:
> - lsn = be64_to_cpu(((struct xfs_agfl *)blk)->agfl_lsn);
> - uuid = &((struct xfs_agfl *)blk)->agfl_uuid;
> - break;
> - case XFS_AGI_MAGIC:
> - lsn = be64_to_cpu(((struct xfs_agi *)blk)->agi_lsn);
> - uuid = &((struct xfs_agi *)blk)->agi_uuid;
> - break;
> - case XFS_SYMLINK_MAGIC:
> - lsn = be64_to_cpu(((struct xfs_dsymlink_hdr *)blk)->sl_lsn);
> - uuid = &((struct xfs_dsymlink_hdr *)blk)->sl_uuid;
> - break;
> - case XFS_DIR3_BLOCK_MAGIC:
> - case XFS_DIR3_DATA_MAGIC:
> - case XFS_DIR3_FREE_MAGIC:
> - lsn = be64_to_cpu(((struct xfs_dir3_blk_hdr *)blk)->lsn);
> - uuid = &((struct xfs_dir3_blk_hdr *)blk)->uuid;
> - break;
> - case XFS_ATTR3_RMT_MAGIC:
> - /*
> - * Remote attr blocks are written synchronously, rather than
> - * being logged. That means they do not contain a valid LSN
> - * (i.e. transactionally ordered) in them, and hence any time we
> - * see a buffer to replay over the top of a remote attribute
> - * block we should simply do so.
> - */
> - goto recover_immediately;
> - case XFS_SB_MAGIC:
> - /*
> - * superblock uuids are magic. We may or may not have a
> - * sb_meta_uuid on disk, but it will be set in the in-core
> - * superblock. We set the uuid pointer for verification
> - * according to the superblock feature mask to ensure we check
> - * the relevant UUID in the superblock.
> - */
> - lsn = be64_to_cpu(((struct xfs_dsb *)blk)->sb_lsn);
> - if (xfs_sb_version_hasmetauuid(&mp->m_sb))
> - uuid = &((struct xfs_dsb *)blk)->sb_meta_uuid;
> - else
> - uuid = &((struct xfs_dsb *)blk)->sb_uuid;
> - break;
> - default:
> - break;
> - }
> -
> - if (lsn != (xfs_lsn_t)-1) {
> - if (!uuid_equal(&mp->m_sb.sb_meta_uuid, uuid))
> - goto recover_immediately;
> - return lsn;
> - }
> -
> - magicda = be16_to_cpu(((struct xfs_da_blkinfo *)blk)->magic);
> - switch (magicda) {
> - case XFS_DIR3_LEAF1_MAGIC:
> - case XFS_DIR3_LEAFN_MAGIC:
> - case XFS_DA3_NODE_MAGIC:
> - lsn = be64_to_cpu(((struct xfs_da3_blkinfo *)blk)->lsn);
> - uuid = &((struct xfs_da3_blkinfo *)blk)->uuid;
> - break;
> - default:
> - break;
> - }
> -
> - if (lsn != (xfs_lsn_t)-1) {
> - if (!uuid_equal(&mp->m_sb.sb_uuid, uuid))
> - goto recover_immediately;
> - return lsn;
> - }
> -
> - /*
> - * We do individual object checks on dquot and inode buffers as they
> - * have their own individual LSN records. Also, we could have a stale
> - * buffer here, so we have to at least recognise these buffer types.
> - *
> - * A notd complexity here is inode unlinked list processing - it logs
> - * the inode directly in the buffer, but we don't know which inodes have
> - * been modified, and there is no global buffer LSN. Hence we need to
> - * recover all inode buffer types immediately. This problem will be
> - * fixed by logical logging of the unlinked list modifications.
> - */
> - magic16 = be16_to_cpu(*(__be16 *)blk);
> - switch (magic16) {
> - case XFS_DQUOT_MAGIC:
> - case XFS_DINODE_MAGIC:
> - goto recover_immediately;
> - default:
> - break;
> - }
> -
> - /* unknown buffer contents, recover immediately */
> -
> -recover_immediately:
> - return (xfs_lsn_t)-1;
> -
> -}
> -
> -/*
> - * Validate the recovered buffer is of the correct type and attach the
> - * appropriate buffer operations to them for writeback. Magic numbers are in a
> - * few places:
> - * the first 16 bits of the buffer (inode buffer, dquot buffer),
> - * the first 32 bits of the buffer (most blocks),
> - * inside a struct xfs_da_blkinfo at the start of the buffer.
> - */
> -static void
> -xlog_recover_validate_buf_type(
> - struct xfs_mount *mp,
> - struct xfs_buf *bp,
> - xfs_buf_log_format_t *buf_f,
> - xfs_lsn_t current_lsn)
> -{
> - struct xfs_da_blkinfo *info = bp->b_addr;
> - uint32_t magic32;
> - uint16_t magic16;
> - uint16_t magicda;
> - char *warnmsg = NULL;
> -
> - /*
> - * We can only do post recovery validation on items on CRC enabled
> - * fielsystems as we need to know when the buffer was written to be able
> - * to determine if we should have replayed the item. If we replay old
> - * metadata over a newer buffer, then it will enter a temporarily
> - * inconsistent state resulting in verification failures. Hence for now
> - * just avoid the verification stage for non-crc filesystems
> - */
> - if (!xfs_sb_version_hascrc(&mp->m_sb))
> - return;
> -
> - magic32 = be32_to_cpu(*(__be32 *)bp->b_addr);
> - magic16 = be16_to_cpu(*(__be16*)bp->b_addr);
> - magicda = be16_to_cpu(info->magic);
> - switch (xfs_blft_from_flags(buf_f)) {
> - case XFS_BLFT_BTREE_BUF:
> - switch (magic32) {
> - case XFS_ABTB_CRC_MAGIC:
> - case XFS_ABTB_MAGIC:
> - bp->b_ops = &xfs_bnobt_buf_ops;
> - break;
> - case XFS_ABTC_CRC_MAGIC:
> - case XFS_ABTC_MAGIC:
> - bp->b_ops = &xfs_cntbt_buf_ops;
> - break;
> - case XFS_IBT_CRC_MAGIC:
> - case XFS_IBT_MAGIC:
> - bp->b_ops = &xfs_inobt_buf_ops;
> - break;
> - case XFS_FIBT_CRC_MAGIC:
> - case XFS_FIBT_MAGIC:
> - bp->b_ops = &xfs_finobt_buf_ops;
> - break;
> - case XFS_BMAP_CRC_MAGIC:
> - case XFS_BMAP_MAGIC:
> - bp->b_ops = &xfs_bmbt_buf_ops;
> - break;
> - 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;
> - }
> - break;
> - case XFS_BLFT_AGF_BUF:
> - if (magic32 != XFS_AGF_MAGIC) {
> - warnmsg = "Bad AGF block magic!";
> - break;
> - }
> - bp->b_ops = &xfs_agf_buf_ops;
> - break;
> - case XFS_BLFT_AGFL_BUF:
> - if (magic32 != XFS_AGFL_MAGIC) {
> - warnmsg = "Bad AGFL block magic!";
> - break;
> - }
> - bp->b_ops = &xfs_agfl_buf_ops;
> - break;
> - case XFS_BLFT_AGI_BUF:
> - if (magic32 != XFS_AGI_MAGIC) {
> - warnmsg = "Bad AGI block magic!";
> - break;
> - }
> - bp->b_ops = &xfs_agi_buf_ops;
> - break;
> - case XFS_BLFT_UDQUOT_BUF:
> - case XFS_BLFT_PDQUOT_BUF:
> - case XFS_BLFT_GDQUOT_BUF:
> -#ifdef CONFIG_XFS_QUOTA
> - if (magic16 != XFS_DQUOT_MAGIC) {
> - warnmsg = "Bad DQUOT block magic!";
> - break;
> - }
> - bp->b_ops = &xfs_dquot_buf_ops;
> -#else
> - xfs_alert(mp,
> - "Trying to recover dquots without QUOTA support built in!");
> - ASSERT(0);
> -#endif
> - break;
> - case XFS_BLFT_DINO_BUF:
> - if (magic16 != XFS_DINODE_MAGIC) {
> - warnmsg = "Bad INODE block magic!";
> - break;
> - }
> - bp->b_ops = &xfs_inode_buf_ops;
> - break;
> - case XFS_BLFT_SYMLINK_BUF:
> - if (magic32 != XFS_SYMLINK_MAGIC) {
> - warnmsg = "Bad symlink block magic!";
> - break;
> - }
> - bp->b_ops = &xfs_symlink_buf_ops;
> - break;
> - case XFS_BLFT_DIR_BLOCK_BUF:
> - if (magic32 != XFS_DIR2_BLOCK_MAGIC &&
> - magic32 != XFS_DIR3_BLOCK_MAGIC) {
> - warnmsg = "Bad dir block magic!";
> - break;
> - }
> - bp->b_ops = &xfs_dir3_block_buf_ops;
> - break;
> - case XFS_BLFT_DIR_DATA_BUF:
> - if (magic32 != XFS_DIR2_DATA_MAGIC &&
> - magic32 != XFS_DIR3_DATA_MAGIC) {
> - warnmsg = "Bad dir data magic!";
> - break;
> - }
> - bp->b_ops = &xfs_dir3_data_buf_ops;
> - break;
> - case XFS_BLFT_DIR_FREE_BUF:
> - if (magic32 != XFS_DIR2_FREE_MAGIC &&
> - magic32 != XFS_DIR3_FREE_MAGIC) {
> - warnmsg = "Bad dir3 free magic!";
> - break;
> - }
> - bp->b_ops = &xfs_dir3_free_buf_ops;
> - break;
> - case XFS_BLFT_DIR_LEAF1_BUF:
> - if (magicda != XFS_DIR2_LEAF1_MAGIC &&
> - magicda != XFS_DIR3_LEAF1_MAGIC) {
> - warnmsg = "Bad dir leaf1 magic!";
> - break;
> - }
> - bp->b_ops = &xfs_dir3_leaf1_buf_ops;
> - break;
> - case XFS_BLFT_DIR_LEAFN_BUF:
> - if (magicda != XFS_DIR2_LEAFN_MAGIC &&
> - magicda != XFS_DIR3_LEAFN_MAGIC) {
> - warnmsg = "Bad dir leafn magic!";
> - break;
> - }
> - bp->b_ops = &xfs_dir3_leafn_buf_ops;
> - break;
> - case XFS_BLFT_DA_NODE_BUF:
> - if (magicda != XFS_DA_NODE_MAGIC &&
> - magicda != XFS_DA3_NODE_MAGIC) {
> - warnmsg = "Bad da node magic!";
> - break;
> - }
> - bp->b_ops = &xfs_da3_node_buf_ops;
> - break;
> - case XFS_BLFT_ATTR_LEAF_BUF:
> - if (magicda != XFS_ATTR_LEAF_MAGIC &&
> - magicda != XFS_ATTR3_LEAF_MAGIC) {
> - warnmsg = "Bad attr leaf magic!";
> - break;
> - }
> - bp->b_ops = &xfs_attr3_leaf_buf_ops;
> - break;
> - case XFS_BLFT_ATTR_RMT_BUF:
> - if (magic32 != XFS_ATTR3_RMT_MAGIC) {
> - warnmsg = "Bad attr remote magic!";
> - break;
> - }
> - bp->b_ops = &xfs_attr3_rmt_buf_ops;
> - break;
> - case XFS_BLFT_SB_BUF:
> - if (magic32 != XFS_SB_MAGIC) {
> - warnmsg = "Bad SB block magic!";
> - break;
> - }
> - bp->b_ops = &xfs_sb_buf_ops;
> - break;
> -#ifdef CONFIG_XFS_RT
> - case XFS_BLFT_RTBITMAP_BUF:
> - case XFS_BLFT_RTSUMMARY_BUF:
> - /* no magic numbers for verification of RT buffers */
> - bp->b_ops = &xfs_rtbuf_ops;
> - break;
> -#endif /* CONFIG_XFS_RT */
> - default:
> - xfs_warn(mp, "Unknown buffer type %d!",
> - xfs_blft_from_flags(buf_f));
> - break;
> - }
> -
> - /*
> - * Nothing else to do in the case of a NULL current LSN as this means
> - * the buffer is more recent than the change in the log and will be
> - * skipped.
> - */
> - if (current_lsn == NULLCOMMITLSN)
> - return;
> -
> - if (warnmsg) {
> - xfs_warn(mp, warnmsg);
> - ASSERT(0);
> - }
> -
> - /*
> - * We must update the metadata LSN of the buffer as it is written out to
> - * ensure that older transactions never replay over this one and corrupt
> - * the buffer. This can occur if log recovery is interrupted at some
> - * point after the current transaction completes, at which point a
> - * subsequent mount starts recovery from the beginning.
> - *
> - * Write verifiers update the metadata LSN from log items attached to
> - * the buffer. Therefore, initialize a bli purely to carry the LSN to
> - * the verifier. We'll clean it up in our ->iodone() callback.
> - */
> - if (bp->b_ops) {
> - struct xfs_buf_log_item *bip;
> -
> - ASSERT(!bp->b_iodone || bp->b_iodone == xlog_recover_iodone);
> - bp->b_iodone = xlog_recover_iodone;
> - xfs_buf_item_init(bp, mp);
> - bip = bp->b_log_item;
> - bip->bli_item.li_lsn = current_lsn;
> - }
> -}
> -
> -/*
> - * Perform a 'normal' buffer recovery. Each logged region of the
> - * buffer should be copied over the corresponding region in the
> - * given buffer. The bitmap in the buf log format structure indicates
> - * where to place the logged data.
> - */
> -STATIC void
> -xlog_recover_do_reg_buffer(
> - struct xfs_mount *mp,
> - struct xlog_recover_item *item,
> - struct xfs_buf *bp,
> - xfs_buf_log_format_t *buf_f,
> - xfs_lsn_t current_lsn)
> -{
> - int i;
> - int bit;
> - int nbits;
> - xfs_failaddr_t fa;
> - const size_t size_disk_dquot = sizeof(struct xfs_disk_dquot);
> -
> - trace_xfs_log_recover_buf_reg_buf(mp->m_log, buf_f);
> -
> - bit = 0;
> - i = 1; /* 0 is the buf format structure */
> - while (1) {
> - bit = xfs_next_bit(buf_f->blf_data_map,
> - buf_f->blf_map_size, bit);
> - if (bit == -1)
> - break;
> - nbits = xfs_contig_bits(buf_f->blf_data_map,
> - buf_f->blf_map_size, bit);
> - ASSERT(nbits > 0);
> - ASSERT(item->ri_buf[i].i_addr != NULL);
> - ASSERT(item->ri_buf[i].i_len % XFS_BLF_CHUNK == 0);
> - ASSERT(BBTOB(bp->b_length) >=
> - ((uint)bit << XFS_BLF_SHIFT) + (nbits << XFS_BLF_SHIFT));
> -
> - /*
> - * The dirty regions logged in the buffer, even though
> - * contiguous, may span multiple chunks. This is because the
> - * dirty region may span a physical page boundary in a buffer
> - * and hence be split into two separate vectors for writing into
> - * the log. Hence we need to trim nbits back to the length of
> - * the current region being copied out of the log.
> - */
> - if (item->ri_buf[i].i_len < (nbits << XFS_BLF_SHIFT))
> - nbits = item->ri_buf[i].i_len >> XFS_BLF_SHIFT;
> -
> - /*
> - * Do a sanity check if this is a dquot buffer. Just checking
> - * the first dquot in the buffer should do. XXXThis is
> - * probably a good thing to do for other buf types also.
> - */
> - fa = NULL;
> - if (buf_f->blf_flags &
> - (XFS_BLF_UDQUOT_BUF|XFS_BLF_PDQUOT_BUF|XFS_BLF_GDQUOT_BUF)) {
> - if (item->ri_buf[i].i_addr == NULL) {
> - xfs_alert(mp,
> - "XFS: NULL dquot in %s.", __func__);
> - goto next;
> - }
> - if (item->ri_buf[i].i_len < size_disk_dquot) {
> - xfs_alert(mp,
> - "XFS: dquot too small (%d) in %s.",
> - item->ri_buf[i].i_len, __func__);
> - goto next;
> - }
> - fa = xfs_dquot_verify(mp, item->ri_buf[i].i_addr,
> - -1, 0);
> - if (fa) {
> - xfs_alert(mp,
> - "dquot corrupt at %pS trying to replay into block 0x%llx",
> - fa, bp->b_bn);
> - goto next;
> - }
> - }
> -
> - memcpy(xfs_buf_offset(bp,
> - (uint)bit << XFS_BLF_SHIFT), /* dest */
> - item->ri_buf[i].i_addr, /* source */
> - nbits<<XFS_BLF_SHIFT); /* length */
> - next:
> - i++;
> - bit += nbits;
> - }
> -
> - /* Shouldn't be any more regions */
> - ASSERT(i == item->ri_total);
> -
> - xlog_recover_validate_buf_type(mp, bp, buf_f, current_lsn);
> -}
> -
> -/*
> - * Perform a dquot buffer recovery.
> - * Simple algorithm: if we have found a QUOTAOFF log item of the same type
> - * (ie. USR or GRP), then just toss this buffer away; don't recover it.
> - * Else, treat it as a regular buffer and do recovery.
> - *
> - * Return false if the buffer was tossed and true if we recovered the buffer to
> - * indicate to the caller if the buffer needs writing.
> - */
> -STATIC bool
> -xlog_recover_do_dquot_buffer(
> - struct xfs_mount *mp,
> - struct xlog *log,
> - struct xlog_recover_item *item,
> - struct xfs_buf *bp,
> - struct xfs_buf_log_format *buf_f)
> -{
> - uint type;
> -
> - trace_xfs_log_recover_buf_dquot_buf(log, buf_f);
> -
> - /*
> - * Filesystems are required to send in quota flags at mount time.
> - */
> - if (!mp->m_qflags)
> - return false;
> -
> - type = 0;
> - if (buf_f->blf_flags & XFS_BLF_UDQUOT_BUF)
> - type |= XFS_DQ_USER;
> - if (buf_f->blf_flags & XFS_BLF_PDQUOT_BUF)
> - type |= XFS_DQ_PROJ;
> - if (buf_f->blf_flags & XFS_BLF_GDQUOT_BUF)
> - type |= XFS_DQ_GROUP;
> - /*
> - * This type of quotas was turned off, so ignore this buffer
> - */
> - if (log->l_quotaoffs_flag & type)
> - return false;
> -
> - xlog_recover_do_reg_buffer(mp, item, bp, buf_f, NULLCOMMITLSN);
> - return true;
> -}
> -
> -/*
> - * This routine replays a modification made to a buffer at runtime.
> - * There are actually two types of buffer, regular and inode, which
> - * are handled differently. Inode buffers are handled differently
> - * in that we only recover a specific set of data from them, namely
> - * the inode di_next_unlinked fields. This is because all other inode
> - * data is actually logged via inode records and any data we replay
> - * here which overlaps that may be stale.
> - *
> - * When meta-data buffers are freed at run time we log a buffer item
> - * with the XFS_BLF_CANCEL bit set to indicate that previous copies
> - * of the buffer in the log should not be replayed at recovery time.
> - * This is so that if the blocks covered by the buffer are reused for
> - * file data before we crash we don't end up replaying old, freed
> - * meta-data into a user's file.
> - *
> - * To handle the cancellation of buffer log items, we make two passes
> - * over the log during recovery. During the first we build a table of
> - * those buffers which have been cancelled, and during the second we
> - * only replay those buffers which do not have corresponding cancel
> - * records in the table. See xlog_recover_buffer_pass[1,2] above
> - * for more details on the implementation of the table of cancel records.
> - */
> -STATIC int
> -xlog_recover_buffer_pass2(
> - struct xlog *log,
> - struct list_head *buffer_list,
> - struct xlog_recover_item *item,
> - xfs_lsn_t current_lsn)
> -{
> - xfs_buf_log_format_t *buf_f = item->ri_buf[0].i_addr;
> - xfs_mount_t *mp = log->l_mp;
> - xfs_buf_t *bp;
> - int error;
> - uint buf_flags;
> - xfs_lsn_t lsn;
> -
> - /*
> - * In this pass we only want to recover all the buffers which have
> - * not been cancelled and are not cancellation buffers themselves.
> - */
> - if (buf_f->blf_flags & XFS_BLF_CANCEL) {
> - if (xlog_put_buffer_cancelled(log, buf_f->blf_blkno,
> - buf_f->blf_len))
> - goto cancelled;
> - } else {
> -
> - if (xlog_is_buffer_cancelled(log, buf_f->blf_blkno,
> - buf_f->blf_len))
> - goto cancelled;
> - }
> -
> - trace_xfs_log_recover_buf_recover(log, buf_f);
> -
> - buf_flags = 0;
> - if (buf_f->blf_flags & XFS_BLF_INODE_BUF)
> - buf_flags |= XBF_UNMAPPED;
> -
> - error = xfs_buf_read(mp->m_ddev_targp, buf_f->blf_blkno, buf_f->blf_len,
> - buf_flags, &bp, NULL);
> - if (error)
> - return error;
> -
> - /*
> - * Recover the buffer only if we get an LSN from it and it's less than
> - * the lsn of the transaction we are replaying.
> - *
> - * Note that we have to be extremely careful of readahead here.
> - * Readahead does not attach verfiers to the buffers so if we don't
> - * actually do any replay after readahead because of the LSN we found
> - * in the buffer if more recent than that current transaction then we
> - * need to attach the verifier directly. Failure to do so can lead to
> - * future recovery actions (e.g. EFI and unlinked list recovery) can
> - * operate on the buffers and they won't get the verifier attached. This
> - * can lead to blocks on disk having the correct content but a stale
> - * CRC.
> - *
> - * It is safe to assume these clean buffers are currently up to date.
> - * If the buffer is dirtied by a later transaction being replayed, then
> - * the verifier will be reset to match whatever recover turns that
> - * buffer into.
> - */
> - lsn = xlog_recover_get_buf_lsn(mp, bp);
> - if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) >= 0) {
> - trace_xfs_log_recover_buf_skip(log, buf_f);
> - xlog_recover_validate_buf_type(mp, bp, buf_f, NULLCOMMITLSN);
> - goto out_release;
> - }
> -
> - if (buf_f->blf_flags & XFS_BLF_INODE_BUF) {
> - error = xlog_recover_do_inode_buffer(mp, item, bp, buf_f);
> - if (error)
> - goto out_release;
> - } else if (buf_f->blf_flags &
> - (XFS_BLF_UDQUOT_BUF|XFS_BLF_PDQUOT_BUF|XFS_BLF_GDQUOT_BUF)) {
> - bool dirty;
> -
> - dirty = xlog_recover_do_dquot_buffer(mp, log, item, bp, buf_f);
> - if (!dirty)
> - goto out_release;
> - } else {
> - xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn);
> - }
> -
> - /*
> - * Perform delayed write on the buffer. Asynchronous writes will be
> - * slower when taking into account all the buffers to be flushed.
> - *
> - * Also make sure that only inode buffers with good sizes stay in
> - * the buffer cache. The kernel moves inodes in buffers of 1 block
> - * or inode_cluster_size bytes, whichever is bigger. The inode
> - * buffers in the log can be a different size if the log was generated
> - * by an older kernel using unclustered inode buffers or a newer kernel
> - * running with a different inode cluster size. Regardless, if the
> - * the inode buffer size isn't max(blocksize, inode_cluster_size)
> - * for *our* value of inode_cluster_size, then we need to keep
> - * the buffer out of the buffer cache so that the buffer won't
> - * overlap with future reads of those inodes.
> - */
> - if (XFS_DINODE_MAGIC ==
> - be16_to_cpu(*((__be16 *)xfs_buf_offset(bp, 0))) &&
> - (BBTOB(bp->b_length) != M_IGEO(log->l_mp)->inode_cluster_size)) {
> - xfs_buf_stale(bp);
> - error = xfs_bwrite(bp);
> - } else {
> - ASSERT(bp->b_mount == mp);
> - bp->b_iodone = xlog_recover_iodone;
> - xfs_buf_delwri_queue(bp, buffer_list);
> - }
> -
> -out_release:
> - xfs_buf_relse(bp);
> - return error;
> -cancelled:
> - trace_xfs_log_recover_buf_cancel(log, buf_f);
> - return 0;
> -}
> -
> /*
> * Inode fork owner changes
> *
> @@ -3846,10 +3061,11 @@ xlog_recover_commit_pass2(
> {
> trace_xfs_log_recover_item_recover(log, trans, item, XLOG_RECOVER_PASS2);
>
> + if (item->ri_ops && item->ri_ops->commit_pass2)
> + return item->ri_ops->commit_pass2(log, buffer_list, item,
> + trans->r_lsn);
> +
> switch (ITEM_TYPE(item)) {
> - case XFS_LI_BUF:
> - return xlog_recover_buffer_pass2(log, buffer_list, item,
> - trans->r_lsn);
> case XFS_LI_INODE:
> return xlog_recover_inode_pass2(log, buffer_list, item,
> trans->r_lsn);
>
>
--
chandan
^ permalink raw reply [flat|nested] 94+ messages in thread* Re: [PATCH 05/28] xfs: refactor log recovery buffer item dispatch for pass2 commit functions
2020-05-05 1:11 ` [PATCH 05/28] xfs: refactor log recovery buffer item dispatch for pass2 " Darrick J. Wong
2020-05-05 5:03 ` Chandan Babu R
@ 2020-05-06 15:09 ` Christoph Hellwig
1 sibling, 0 replies; 94+ messages in thread
From: Christoph Hellwig @ 2020-05-06 15:09 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-xfs
On Mon, May 04, 2020 at 06:11:03PM -0700, Darrick J. Wong wrote:
> + if (item->ri_ops && item->ri_ops->commit_pass2)
> + return item->ri_ops->commit_pass2(log, buffer_list, item,
> + trans->r_lsn);
> +
I don't think ri_ops can ever be NULL here, so the check should be
removed.
Otherwise looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 94+ messages in thread
* [PATCH 06/28] xfs: refactor log recovery inode item dispatch for pass2 commit functions
2020-05-05 1:10 [PATCH v3 00/28] xfs: refactor log recovery Darrick J. Wong
` (4 preceding siblings ...)
2020-05-05 1:11 ` [PATCH 05/28] xfs: refactor log recovery buffer item dispatch for pass2 " Darrick J. Wong
@ 2020-05-05 1:11 ` Darrick J. Wong
2020-05-05 5:09 ` Chandan Babu R
2020-05-06 15:10 ` Christoph Hellwig
2020-05-05 1:11 ` [PATCH 07/28] xfs: refactor log recovery dquot " Darrick J. Wong
` (21 subsequent siblings)
27 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2020-05-05 1:11 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Move the log inode item pass2 commit code into the per-item source code
files and use the dispatch function to call it. We do these one at a
time because there's a lot of code to move. No functional changes.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/xfs_inode_item_recover.c | 355 +++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_log_recover.c | 355 ---------------------------------------
2 files changed, 355 insertions(+), 355 deletions(-)
diff --git a/fs/xfs/xfs_inode_item_recover.c b/fs/xfs/xfs_inode_item_recover.c
index a132cacd8d48..2bdba612aa71 100644
--- a/fs/xfs/xfs_inode_item_recover.c
+++ b/fs/xfs/xfs_inode_item_recover.c
@@ -20,6 +20,8 @@
#include "xfs_error.h"
#include "xfs_log_priv.h"
#include "xfs_log_recover.h"
+#include "xfs_icache.h"
+#include "xfs_bmap_btree.h"
STATIC void
xlog_recover_inode_ra_pass2(
@@ -39,7 +41,360 @@ xlog_recover_inode_ra_pass2(
}
}
+/*
+ * Inode fork owner changes
+ *
+ * If we have been told that we have to reparent the inode fork, it's because an
+ * extent swap operation on a CRC enabled filesystem has been done and we are
+ * replaying it. We need to walk the BMBT of the appropriate fork and change the
+ * owners of it.
+ *
+ * The complexity here is that we don't have an inode context to work with, so
+ * after we've replayed the inode we need to instantiate one. This is where the
+ * fun begins.
+ *
+ * We are in the middle of log recovery, so we can't run transactions. That
+ * means we cannot use cache coherent inode instantiation via xfs_iget(), as
+ * that will result in the corresponding iput() running the inode through
+ * xfs_inactive(). If we've just replayed an inode core that changes the link
+ * count to zero (i.e. it's been unlinked), then xfs_inactive() will run
+ * transactions (bad!).
+ *
+ * So, to avoid this, we instantiate an inode directly from the inode core we've
+ * just recovered. We have the buffer still locked, and all we really need to
+ * instantiate is the inode core and the forks being modified. We can do this
+ * manually, then run the inode btree owner change, and then tear down the
+ * xfs_inode without having to run any transactions at all.
+ *
+ * Also, because we don't have a transaction context available here but need to
+ * gather all the buffers we modify for writeback so we pass the buffer_list
+ * instead for the operation to use.
+ */
+
+STATIC int
+xfs_recover_inode_owner_change(
+ struct xfs_mount *mp,
+ struct xfs_dinode *dip,
+ struct xfs_inode_log_format *in_f,
+ struct list_head *buffer_list)
+{
+ struct xfs_inode *ip;
+ int error;
+
+ ASSERT(in_f->ilf_fields & (XFS_ILOG_DOWNER|XFS_ILOG_AOWNER));
+
+ ip = xfs_inode_alloc(mp, in_f->ilf_ino);
+ if (!ip)
+ return -ENOMEM;
+
+ /* instantiate the inode */
+ ASSERT(dip->di_version >= 3);
+ xfs_inode_from_disk(ip, dip);
+
+ error = xfs_iformat_fork(ip, dip);
+ if (error)
+ goto out_free_ip;
+
+ if (!xfs_inode_verify_forks(ip)) {
+ error = -EFSCORRUPTED;
+ goto out_free_ip;
+ }
+
+ if (in_f->ilf_fields & XFS_ILOG_DOWNER) {
+ ASSERT(in_f->ilf_fields & XFS_ILOG_DBROOT);
+ error = xfs_bmbt_change_owner(NULL, ip, XFS_DATA_FORK,
+ ip->i_ino, buffer_list);
+ if (error)
+ goto out_free_ip;
+ }
+
+ if (in_f->ilf_fields & XFS_ILOG_AOWNER) {
+ ASSERT(in_f->ilf_fields & XFS_ILOG_ABROOT);
+ error = xfs_bmbt_change_owner(NULL, ip, XFS_ATTR_FORK,
+ ip->i_ino, buffer_list);
+ if (error)
+ goto out_free_ip;
+ }
+
+out_free_ip:
+ xfs_inode_free(ip);
+ return error;
+}
+
+STATIC int
+xlog_recover_inode_commit_pass2(
+ struct xlog *log,
+ struct list_head *buffer_list,
+ struct xlog_recover_item *item,
+ xfs_lsn_t current_lsn)
+{
+ struct xfs_inode_log_format *in_f;
+ struct xfs_mount *mp = log->l_mp;
+ struct xfs_buf *bp;
+ struct xfs_dinode *dip;
+ int len;
+ char *src;
+ char *dest;
+ int error;
+ int attr_index;
+ uint fields;
+ struct xfs_log_dinode *ldip;
+ uint isize;
+ int need_free = 0;
+
+ if (item->ri_buf[0].i_len == sizeof(struct xfs_inode_log_format)) {
+ in_f = item->ri_buf[0].i_addr;
+ } else {
+ in_f = kmem_alloc(sizeof(struct xfs_inode_log_format), 0);
+ need_free = 1;
+ error = xfs_inode_item_format_convert(&item->ri_buf[0], in_f);
+ if (error)
+ goto error;
+ }
+
+ /*
+ * Inode buffers can be freed, look out for it,
+ * and do not replay the inode.
+ */
+ if (xlog_is_buffer_cancelled(log, in_f->ilf_blkno, in_f->ilf_len)) {
+ error = 0;
+ trace_xfs_log_recover_inode_cancel(log, in_f);
+ goto error;
+ }
+ trace_xfs_log_recover_inode_recover(log, in_f);
+
+ error = xfs_buf_read(mp->m_ddev_targp, in_f->ilf_blkno, in_f->ilf_len,
+ 0, &bp, &xfs_inode_buf_ops);
+ if (error)
+ goto error;
+ ASSERT(in_f->ilf_fields & XFS_ILOG_CORE);
+ dip = xfs_buf_offset(bp, in_f->ilf_boffset);
+
+ /*
+ * Make sure the place we're flushing out to really looks
+ * like an inode!
+ */
+ if (XFS_IS_CORRUPT(mp, !xfs_verify_magic16(bp, dip->di_magic))) {
+ xfs_alert(mp,
+ "%s: Bad inode magic number, dip = "PTR_FMT", dino bp = "PTR_FMT", ino = %Ld",
+ __func__, dip, bp, in_f->ilf_ino);
+ error = -EFSCORRUPTED;
+ goto out_release;
+ }
+ ldip = item->ri_buf[1].i_addr;
+ if (XFS_IS_CORRUPT(mp, ldip->di_magic != XFS_DINODE_MAGIC)) {
+ xfs_alert(mp,
+ "%s: Bad inode log record, rec ptr "PTR_FMT", ino %Ld",
+ __func__, item, in_f->ilf_ino);
+ error = -EFSCORRUPTED;
+ goto out_release;
+ }
+
+ /*
+ * If the inode has an LSN in it, recover the inode only if it's less
+ * than the lsn of the transaction we are replaying. Note: we still
+ * need to replay an owner change even though the inode is more recent
+ * than the transaction as there is no guarantee that all the btree
+ * blocks are more recent than this transaction, too.
+ */
+ if (dip->di_version >= 3) {
+ xfs_lsn_t lsn = be64_to_cpu(dip->di_lsn);
+
+ if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) >= 0) {
+ trace_xfs_log_recover_inode_skip(log, in_f);
+ error = 0;
+ goto out_owner_change;
+ }
+ }
+
+ /*
+ * di_flushiter is only valid for v1/2 inodes. All changes for v3 inodes
+ * are transactional and if ordering is necessary we can determine that
+ * more accurately by the LSN field in the V3 inode core. Don't trust
+ * the inode versions we might be changing them here - use the
+ * superblock flag to determine whether we need to look at di_flushiter
+ * to skip replay when the on disk inode is newer than the log one
+ */
+ if (!xfs_sb_version_has_v3inode(&mp->m_sb) &&
+ ldip->di_flushiter < be16_to_cpu(dip->di_flushiter)) {
+ /*
+ * Deal with the wrap case, DI_MAX_FLUSH is less
+ * than smaller numbers
+ */
+ if (be16_to_cpu(dip->di_flushiter) == DI_MAX_FLUSH &&
+ ldip->di_flushiter < (DI_MAX_FLUSH >> 1)) {
+ /* do nothing */
+ } else {
+ trace_xfs_log_recover_inode_skip(log, in_f);
+ error = 0;
+ goto out_release;
+ }
+ }
+
+ /* Take the opportunity to reset the flush iteration count */
+ ldip->di_flushiter = 0;
+
+ if (unlikely(S_ISREG(ldip->di_mode))) {
+ if ((ldip->di_format != XFS_DINODE_FMT_EXTENTS) &&
+ (ldip->di_format != XFS_DINODE_FMT_BTREE)) {
+ XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(3)",
+ XFS_ERRLEVEL_LOW, mp, ldip,
+ sizeof(*ldip));
+ xfs_alert(mp,
+ "%s: Bad regular inode log record, rec ptr "PTR_FMT", "
+ "ino ptr = "PTR_FMT", ino bp = "PTR_FMT", ino %Ld",
+ __func__, item, dip, bp, in_f->ilf_ino);
+ error = -EFSCORRUPTED;
+ goto out_release;
+ }
+ } else if (unlikely(S_ISDIR(ldip->di_mode))) {
+ if ((ldip->di_format != XFS_DINODE_FMT_EXTENTS) &&
+ (ldip->di_format != XFS_DINODE_FMT_BTREE) &&
+ (ldip->di_format != XFS_DINODE_FMT_LOCAL)) {
+ XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(4)",
+ XFS_ERRLEVEL_LOW, mp, ldip,
+ sizeof(*ldip));
+ xfs_alert(mp,
+ "%s: Bad dir inode log record, rec ptr "PTR_FMT", "
+ "ino ptr = "PTR_FMT", ino bp = "PTR_FMT", ino %Ld",
+ __func__, item, dip, bp, in_f->ilf_ino);
+ error = -EFSCORRUPTED;
+ goto out_release;
+ }
+ }
+ if (unlikely(ldip->di_nextents + ldip->di_anextents > ldip->di_nblocks)){
+ XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(5)",
+ XFS_ERRLEVEL_LOW, mp, ldip,
+ sizeof(*ldip));
+ xfs_alert(mp,
+ "%s: Bad inode log record, rec ptr "PTR_FMT", dino ptr "PTR_FMT", "
+ "dino bp "PTR_FMT", ino %Ld, total extents = %d, nblocks = %Ld",
+ __func__, item, dip, bp, in_f->ilf_ino,
+ ldip->di_nextents + ldip->di_anextents,
+ ldip->di_nblocks);
+ error = -EFSCORRUPTED;
+ goto out_release;
+ }
+ if (unlikely(ldip->di_forkoff > mp->m_sb.sb_inodesize)) {
+ XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(6)",
+ XFS_ERRLEVEL_LOW, mp, ldip,
+ sizeof(*ldip));
+ xfs_alert(mp,
+ "%s: Bad inode log record, rec ptr "PTR_FMT", dino ptr "PTR_FMT", "
+ "dino bp "PTR_FMT", ino %Ld, forkoff 0x%x", __func__,
+ item, dip, bp, in_f->ilf_ino, ldip->di_forkoff);
+ error = -EFSCORRUPTED;
+ goto out_release;
+ }
+ isize = xfs_log_dinode_size(mp);
+ if (unlikely(item->ri_buf[1].i_len > isize)) {
+ XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(7)",
+ XFS_ERRLEVEL_LOW, mp, ldip,
+ sizeof(*ldip));
+ xfs_alert(mp,
+ "%s: Bad inode log record length %d, rec ptr "PTR_FMT,
+ __func__, item->ri_buf[1].i_len, item);
+ error = -EFSCORRUPTED;
+ goto out_release;
+ }
+
+ /* recover the log dinode inode into the on disk inode */
+ xfs_log_dinode_to_disk(ldip, dip);
+
+ fields = in_f->ilf_fields;
+ if (fields & XFS_ILOG_DEV)
+ xfs_dinode_put_rdev(dip, in_f->ilf_u.ilfu_rdev);
+
+ if (in_f->ilf_size == 2)
+ goto out_owner_change;
+ len = item->ri_buf[2].i_len;
+ src = item->ri_buf[2].i_addr;
+ ASSERT(in_f->ilf_size <= 4);
+ ASSERT((in_f->ilf_size == 3) || (fields & XFS_ILOG_AFORK));
+ ASSERT(!(fields & XFS_ILOG_DFORK) ||
+ (len == in_f->ilf_dsize));
+
+ switch (fields & XFS_ILOG_DFORK) {
+ case XFS_ILOG_DDATA:
+ case XFS_ILOG_DEXT:
+ memcpy(XFS_DFORK_DPTR(dip), src, len);
+ break;
+
+ case XFS_ILOG_DBROOT:
+ xfs_bmbt_to_bmdr(mp, (struct xfs_btree_block *)src, len,
+ (struct xfs_bmdr_block *)XFS_DFORK_DPTR(dip),
+ XFS_DFORK_DSIZE(dip, mp));
+ break;
+
+ default:
+ /*
+ * There are no data fork flags set.
+ */
+ ASSERT((fields & XFS_ILOG_DFORK) == 0);
+ break;
+ }
+
+ /*
+ * If we logged any attribute data, recover it. There may or
+ * may not have been any other non-core data logged in this
+ * transaction.
+ */
+ if (in_f->ilf_fields & XFS_ILOG_AFORK) {
+ if (in_f->ilf_fields & XFS_ILOG_DFORK) {
+ attr_index = 3;
+ } else {
+ attr_index = 2;
+ }
+ len = item->ri_buf[attr_index].i_len;
+ src = item->ri_buf[attr_index].i_addr;
+ ASSERT(len == in_f->ilf_asize);
+
+ switch (in_f->ilf_fields & XFS_ILOG_AFORK) {
+ case XFS_ILOG_ADATA:
+ case XFS_ILOG_AEXT:
+ dest = XFS_DFORK_APTR(dip);
+ ASSERT(len <= XFS_DFORK_ASIZE(dip, mp));
+ memcpy(dest, src, len);
+ break;
+
+ case XFS_ILOG_ABROOT:
+ dest = XFS_DFORK_APTR(dip);
+ xfs_bmbt_to_bmdr(mp, (struct xfs_btree_block *)src,
+ len, (struct xfs_bmdr_block *)dest,
+ XFS_DFORK_ASIZE(dip, mp));
+ break;
+
+ default:
+ xfs_warn(log->l_mp, "%s: Invalid flag", __func__);
+ ASSERT(0);
+ error = -EFSCORRUPTED;
+ goto out_release;
+ }
+ }
+
+out_owner_change:
+ /* Recover the swapext owner change unless inode has been deleted */
+ if ((in_f->ilf_fields & (XFS_ILOG_DOWNER|XFS_ILOG_AOWNER)) &&
+ (dip->di_mode != 0))
+ error = xfs_recover_inode_owner_change(mp, dip, in_f,
+ buffer_list);
+ /* re-generate the checksum. */
+ xfs_dinode_calc_crc(log->l_mp, dip);
+
+ ASSERT(bp->b_mount == mp);
+ bp->b_iodone = xlog_recover_iodone;
+ xfs_buf_delwri_queue(bp, buffer_list);
+
+out_release:
+ xfs_buf_relse(bp);
+error:
+ if (need_free)
+ kmem_free(in_f);
+ return error;
+}
+
const struct xlog_recover_item_ops xlog_inode_item_ops = {
.item_type = XFS_LI_INODE,
.ra_pass2 = xlog_recover_inode_ra_pass2,
+ .commit_pass2 = xlog_recover_inode_commit_pass2,
};
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index d65dc3895a62..cb5902550e8c 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -2034,358 +2034,6 @@ xlog_buf_readahead(
xfs_buf_readahead(log->l_mp->m_ddev_targp, blkno, len, ops);
}
-/*
- * Inode fork owner changes
- *
- * If we have been told that we have to reparent the inode fork, it's because an
- * extent swap operation on a CRC enabled filesystem has been done and we are
- * replaying it. We need to walk the BMBT of the appropriate fork and change the
- * owners of it.
- *
- * The complexity here is that we don't have an inode context to work with, so
- * after we've replayed the inode we need to instantiate one. This is where the
- * fun begins.
- *
- * We are in the middle of log recovery, so we can't run transactions. That
- * means we cannot use cache coherent inode instantiation via xfs_iget(), as
- * that will result in the corresponding iput() running the inode through
- * xfs_inactive(). If we've just replayed an inode core that changes the link
- * count to zero (i.e. it's been unlinked), then xfs_inactive() will run
- * transactions (bad!).
- *
- * So, to avoid this, we instantiate an inode directly from the inode core we've
- * just recovered. We have the buffer still locked, and all we really need to
- * instantiate is the inode core and the forks being modified. We can do this
- * manually, then run the inode btree owner change, and then tear down the
- * xfs_inode without having to run any transactions at all.
- *
- * Also, because we don't have a transaction context available here but need to
- * gather all the buffers we modify for writeback so we pass the buffer_list
- * instead for the operation to use.
- */
-
-STATIC int
-xfs_recover_inode_owner_change(
- struct xfs_mount *mp,
- struct xfs_dinode *dip,
- struct xfs_inode_log_format *in_f,
- struct list_head *buffer_list)
-{
- struct xfs_inode *ip;
- int error;
-
- ASSERT(in_f->ilf_fields & (XFS_ILOG_DOWNER|XFS_ILOG_AOWNER));
-
- ip = xfs_inode_alloc(mp, in_f->ilf_ino);
- if (!ip)
- return -ENOMEM;
-
- /* instantiate the inode */
- ASSERT(dip->di_version >= 3);
- xfs_inode_from_disk(ip, dip);
-
- error = xfs_iformat_fork(ip, dip);
- if (error)
- goto out_free_ip;
-
- if (!xfs_inode_verify_forks(ip)) {
- error = -EFSCORRUPTED;
- goto out_free_ip;
- }
-
- if (in_f->ilf_fields & XFS_ILOG_DOWNER) {
- ASSERT(in_f->ilf_fields & XFS_ILOG_DBROOT);
- error = xfs_bmbt_change_owner(NULL, ip, XFS_DATA_FORK,
- ip->i_ino, buffer_list);
- if (error)
- goto out_free_ip;
- }
-
- if (in_f->ilf_fields & XFS_ILOG_AOWNER) {
- ASSERT(in_f->ilf_fields & XFS_ILOG_ABROOT);
- error = xfs_bmbt_change_owner(NULL, ip, XFS_ATTR_FORK,
- ip->i_ino, buffer_list);
- if (error)
- goto out_free_ip;
- }
-
-out_free_ip:
- xfs_inode_free(ip);
- return error;
-}
-
-STATIC int
-xlog_recover_inode_pass2(
- struct xlog *log,
- struct list_head *buffer_list,
- struct xlog_recover_item *item,
- xfs_lsn_t current_lsn)
-{
- struct xfs_inode_log_format *in_f;
- xfs_mount_t *mp = log->l_mp;
- xfs_buf_t *bp;
- xfs_dinode_t *dip;
- int len;
- char *src;
- char *dest;
- int error;
- int attr_index;
- uint fields;
- struct xfs_log_dinode *ldip;
- uint isize;
- int need_free = 0;
-
- if (item->ri_buf[0].i_len == sizeof(struct xfs_inode_log_format)) {
- in_f = item->ri_buf[0].i_addr;
- } else {
- in_f = kmem_alloc(sizeof(struct xfs_inode_log_format), 0);
- need_free = 1;
- error = xfs_inode_item_format_convert(&item->ri_buf[0], in_f);
- if (error)
- goto error;
- }
-
- /*
- * Inode buffers can be freed, look out for it,
- * and do not replay the inode.
- */
- if (xlog_is_buffer_cancelled(log, in_f->ilf_blkno, in_f->ilf_len)) {
- error = 0;
- trace_xfs_log_recover_inode_cancel(log, in_f);
- goto error;
- }
- trace_xfs_log_recover_inode_recover(log, in_f);
-
- error = xfs_buf_read(mp->m_ddev_targp, in_f->ilf_blkno, in_f->ilf_len,
- 0, &bp, &xfs_inode_buf_ops);
- if (error)
- goto error;
- ASSERT(in_f->ilf_fields & XFS_ILOG_CORE);
- dip = xfs_buf_offset(bp, in_f->ilf_boffset);
-
- /*
- * Make sure the place we're flushing out to really looks
- * like an inode!
- */
- if (XFS_IS_CORRUPT(mp, !xfs_verify_magic16(bp, dip->di_magic))) {
- xfs_alert(mp,
- "%s: Bad inode magic number, dip = "PTR_FMT", dino bp = "PTR_FMT", ino = %Ld",
- __func__, dip, bp, in_f->ilf_ino);
- error = -EFSCORRUPTED;
- goto out_release;
- }
- ldip = item->ri_buf[1].i_addr;
- if (XFS_IS_CORRUPT(mp, ldip->di_magic != XFS_DINODE_MAGIC)) {
- xfs_alert(mp,
- "%s: Bad inode log record, rec ptr "PTR_FMT", ino %Ld",
- __func__, item, in_f->ilf_ino);
- error = -EFSCORRUPTED;
- goto out_release;
- }
-
- /*
- * If the inode has an LSN in it, recover the inode only if it's less
- * than the lsn of the transaction we are replaying. Note: we still
- * need to replay an owner change even though the inode is more recent
- * than the transaction as there is no guarantee that all the btree
- * blocks are more recent than this transaction, too.
- */
- if (dip->di_version >= 3) {
- xfs_lsn_t lsn = be64_to_cpu(dip->di_lsn);
-
- if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) >= 0) {
- trace_xfs_log_recover_inode_skip(log, in_f);
- error = 0;
- goto out_owner_change;
- }
- }
-
- /*
- * di_flushiter is only valid for v1/2 inodes. All changes for v3 inodes
- * are transactional and if ordering is necessary we can determine that
- * more accurately by the LSN field in the V3 inode core. Don't trust
- * the inode versions we might be changing them here - use the
- * superblock flag to determine whether we need to look at di_flushiter
- * to skip replay when the on disk inode is newer than the log one
- */
- if (!xfs_sb_version_has_v3inode(&mp->m_sb) &&
- ldip->di_flushiter < be16_to_cpu(dip->di_flushiter)) {
- /*
- * Deal with the wrap case, DI_MAX_FLUSH is less
- * than smaller numbers
- */
- if (be16_to_cpu(dip->di_flushiter) == DI_MAX_FLUSH &&
- ldip->di_flushiter < (DI_MAX_FLUSH >> 1)) {
- /* do nothing */
- } else {
- trace_xfs_log_recover_inode_skip(log, in_f);
- error = 0;
- goto out_release;
- }
- }
-
- /* Take the opportunity to reset the flush iteration count */
- ldip->di_flushiter = 0;
-
- if (unlikely(S_ISREG(ldip->di_mode))) {
- if ((ldip->di_format != XFS_DINODE_FMT_EXTENTS) &&
- (ldip->di_format != XFS_DINODE_FMT_BTREE)) {
- XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(3)",
- XFS_ERRLEVEL_LOW, mp, ldip,
- sizeof(*ldip));
- xfs_alert(mp,
- "%s: Bad regular inode log record, rec ptr "PTR_FMT", "
- "ino ptr = "PTR_FMT", ino bp = "PTR_FMT", ino %Ld",
- __func__, item, dip, bp, in_f->ilf_ino);
- error = -EFSCORRUPTED;
- goto out_release;
- }
- } else if (unlikely(S_ISDIR(ldip->di_mode))) {
- if ((ldip->di_format != XFS_DINODE_FMT_EXTENTS) &&
- (ldip->di_format != XFS_DINODE_FMT_BTREE) &&
- (ldip->di_format != XFS_DINODE_FMT_LOCAL)) {
- XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(4)",
- XFS_ERRLEVEL_LOW, mp, ldip,
- sizeof(*ldip));
- xfs_alert(mp,
- "%s: Bad dir inode log record, rec ptr "PTR_FMT", "
- "ino ptr = "PTR_FMT", ino bp = "PTR_FMT", ino %Ld",
- __func__, item, dip, bp, in_f->ilf_ino);
- error = -EFSCORRUPTED;
- goto out_release;
- }
- }
- if (unlikely(ldip->di_nextents + ldip->di_anextents > ldip->di_nblocks)){
- XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(5)",
- XFS_ERRLEVEL_LOW, mp, ldip,
- sizeof(*ldip));
- xfs_alert(mp,
- "%s: Bad inode log record, rec ptr "PTR_FMT", dino ptr "PTR_FMT", "
- "dino bp "PTR_FMT", ino %Ld, total extents = %d, nblocks = %Ld",
- __func__, item, dip, bp, in_f->ilf_ino,
- ldip->di_nextents + ldip->di_anextents,
- ldip->di_nblocks);
- error = -EFSCORRUPTED;
- goto out_release;
- }
- if (unlikely(ldip->di_forkoff > mp->m_sb.sb_inodesize)) {
- XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(6)",
- XFS_ERRLEVEL_LOW, mp, ldip,
- sizeof(*ldip));
- xfs_alert(mp,
- "%s: Bad inode log record, rec ptr "PTR_FMT", dino ptr "PTR_FMT", "
- "dino bp "PTR_FMT", ino %Ld, forkoff 0x%x", __func__,
- item, dip, bp, in_f->ilf_ino, ldip->di_forkoff);
- error = -EFSCORRUPTED;
- goto out_release;
- }
- isize = xfs_log_dinode_size(mp);
- if (unlikely(item->ri_buf[1].i_len > isize)) {
- XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(7)",
- XFS_ERRLEVEL_LOW, mp, ldip,
- sizeof(*ldip));
- xfs_alert(mp,
- "%s: Bad inode log record length %d, rec ptr "PTR_FMT,
- __func__, item->ri_buf[1].i_len, item);
- error = -EFSCORRUPTED;
- goto out_release;
- }
-
- /* recover the log dinode inode into the on disk inode */
- xfs_log_dinode_to_disk(ldip, dip);
-
- fields = in_f->ilf_fields;
- if (fields & XFS_ILOG_DEV)
- xfs_dinode_put_rdev(dip, in_f->ilf_u.ilfu_rdev);
-
- if (in_f->ilf_size == 2)
- goto out_owner_change;
- len = item->ri_buf[2].i_len;
- src = item->ri_buf[2].i_addr;
- ASSERT(in_f->ilf_size <= 4);
- ASSERT((in_f->ilf_size == 3) || (fields & XFS_ILOG_AFORK));
- ASSERT(!(fields & XFS_ILOG_DFORK) ||
- (len == in_f->ilf_dsize));
-
- switch (fields & XFS_ILOG_DFORK) {
- case XFS_ILOG_DDATA:
- case XFS_ILOG_DEXT:
- memcpy(XFS_DFORK_DPTR(dip), src, len);
- break;
-
- case XFS_ILOG_DBROOT:
- xfs_bmbt_to_bmdr(mp, (struct xfs_btree_block *)src, len,
- (xfs_bmdr_block_t *)XFS_DFORK_DPTR(dip),
- XFS_DFORK_DSIZE(dip, mp));
- break;
-
- default:
- /*
- * There are no data fork flags set.
- */
- ASSERT((fields & XFS_ILOG_DFORK) == 0);
- break;
- }
-
- /*
- * If we logged any attribute data, recover it. There may or
- * may not have been any other non-core data logged in this
- * transaction.
- */
- if (in_f->ilf_fields & XFS_ILOG_AFORK) {
- if (in_f->ilf_fields & XFS_ILOG_DFORK) {
- attr_index = 3;
- } else {
- attr_index = 2;
- }
- len = item->ri_buf[attr_index].i_len;
- src = item->ri_buf[attr_index].i_addr;
- ASSERT(len == in_f->ilf_asize);
-
- switch (in_f->ilf_fields & XFS_ILOG_AFORK) {
- case XFS_ILOG_ADATA:
- case XFS_ILOG_AEXT:
- dest = XFS_DFORK_APTR(dip);
- ASSERT(len <= XFS_DFORK_ASIZE(dip, mp));
- memcpy(dest, src, len);
- break;
-
- case XFS_ILOG_ABROOT:
- dest = XFS_DFORK_APTR(dip);
- xfs_bmbt_to_bmdr(mp, (struct xfs_btree_block *)src,
- len, (xfs_bmdr_block_t*)dest,
- XFS_DFORK_ASIZE(dip, mp));
- break;
-
- default:
- xfs_warn(log->l_mp, "%s: Invalid flag", __func__);
- ASSERT(0);
- error = -EFSCORRUPTED;
- goto out_release;
- }
- }
-
-out_owner_change:
- /* Recover the swapext owner change unless inode has been deleted */
- if ((in_f->ilf_fields & (XFS_ILOG_DOWNER|XFS_ILOG_AOWNER)) &&
- (dip->di_mode != 0))
- error = xfs_recover_inode_owner_change(mp, dip, in_f,
- buffer_list);
- /* re-generate the checksum. */
- xfs_dinode_calc_crc(log->l_mp, dip);
-
- ASSERT(bp->b_mount == mp);
- bp->b_iodone = xlog_recover_iodone;
- xfs_buf_delwri_queue(bp, buffer_list);
-
-out_release:
- xfs_buf_relse(bp);
-error:
- if (need_free)
- kmem_free(in_f);
- return error;
-}
-
/*
* Recover a dquot record
*/
@@ -3066,9 +2714,6 @@ xlog_recover_commit_pass2(
trans->r_lsn);
switch (ITEM_TYPE(item)) {
- case XFS_LI_INODE:
- return xlog_recover_inode_pass2(log, buffer_list, item,
- trans->r_lsn);
case XFS_LI_EFI:
return xlog_recover_efi_pass2(log, item, trans->r_lsn);
case XFS_LI_EFD:
^ permalink raw reply related [flat|nested] 94+ messages in thread* Re: [PATCH 06/28] xfs: refactor log recovery inode item dispatch for pass2 commit functions
2020-05-05 1:11 ` [PATCH 06/28] xfs: refactor log recovery inode " Darrick J. Wong
@ 2020-05-05 5:09 ` Chandan Babu R
2020-05-06 15:10 ` Christoph Hellwig
1 sibling, 0 replies; 94+ messages in thread
From: Chandan Babu R @ 2020-05-05 5:09 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-xfs
On Tuesday 5 May 2020 6:41:10 AM IST Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Move the log inode item pass2 commit code into the per-item source code
> files and use the dispatch function to call it. We do these one at a
> time because there's a lot of code to move. No functional changes.
>
Inode item pass2 processing is functionally consistent with what was done
before this patch is applied.
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
> fs/xfs/xfs_inode_item_recover.c | 355 +++++++++++++++++++++++++++++++++++++++
> fs/xfs/xfs_log_recover.c | 355 ---------------------------------------
> 2 files changed, 355 insertions(+), 355 deletions(-)
>
>
> diff --git a/fs/xfs/xfs_inode_item_recover.c b/fs/xfs/xfs_inode_item_recover.c
> index a132cacd8d48..2bdba612aa71 100644
> --- a/fs/xfs/xfs_inode_item_recover.c
> +++ b/fs/xfs/xfs_inode_item_recover.c
> @@ -20,6 +20,8 @@
> #include "xfs_error.h"
> #include "xfs_log_priv.h"
> #include "xfs_log_recover.h"
> +#include "xfs_icache.h"
> +#include "xfs_bmap_btree.h"
>
> STATIC void
> xlog_recover_inode_ra_pass2(
> @@ -39,7 +41,360 @@ xlog_recover_inode_ra_pass2(
> }
> }
>
> +/*
> + * Inode fork owner changes
> + *
> + * If we have been told that we have to reparent the inode fork, it's because an
> + * extent swap operation on a CRC enabled filesystem has been done and we are
> + * replaying it. We need to walk the BMBT of the appropriate fork and change the
> + * owners of it.
> + *
> + * The complexity here is that we don't have an inode context to work with, so
> + * after we've replayed the inode we need to instantiate one. This is where the
> + * fun begins.
> + *
> + * We are in the middle of log recovery, so we can't run transactions. That
> + * means we cannot use cache coherent inode instantiation via xfs_iget(), as
> + * that will result in the corresponding iput() running the inode through
> + * xfs_inactive(). If we've just replayed an inode core that changes the link
> + * count to zero (i.e. it's been unlinked), then xfs_inactive() will run
> + * transactions (bad!).
> + *
> + * So, to avoid this, we instantiate an inode directly from the inode core we've
> + * just recovered. We have the buffer still locked, and all we really need to
> + * instantiate is the inode core and the forks being modified. We can do this
> + * manually, then run the inode btree owner change, and then tear down the
> + * xfs_inode without having to run any transactions at all.
> + *
> + * Also, because we don't have a transaction context available here but need to
> + * gather all the buffers we modify for writeback so we pass the buffer_list
> + * instead for the operation to use.
> + */
> +
> +STATIC int
> +xfs_recover_inode_owner_change(
> + struct xfs_mount *mp,
> + struct xfs_dinode *dip,
> + struct xfs_inode_log_format *in_f,
> + struct list_head *buffer_list)
> +{
> + struct xfs_inode *ip;
> + int error;
> +
> + ASSERT(in_f->ilf_fields & (XFS_ILOG_DOWNER|XFS_ILOG_AOWNER));
> +
> + ip = xfs_inode_alloc(mp, in_f->ilf_ino);
> + if (!ip)
> + return -ENOMEM;
> +
> + /* instantiate the inode */
> + ASSERT(dip->di_version >= 3);
> + xfs_inode_from_disk(ip, dip);
> +
> + error = xfs_iformat_fork(ip, dip);
> + if (error)
> + goto out_free_ip;
> +
> + if (!xfs_inode_verify_forks(ip)) {
> + error = -EFSCORRUPTED;
> + goto out_free_ip;
> + }
> +
> + if (in_f->ilf_fields & XFS_ILOG_DOWNER) {
> + ASSERT(in_f->ilf_fields & XFS_ILOG_DBROOT);
> + error = xfs_bmbt_change_owner(NULL, ip, XFS_DATA_FORK,
> + ip->i_ino, buffer_list);
> + if (error)
> + goto out_free_ip;
> + }
> +
> + if (in_f->ilf_fields & XFS_ILOG_AOWNER) {
> + ASSERT(in_f->ilf_fields & XFS_ILOG_ABROOT);
> + error = xfs_bmbt_change_owner(NULL, ip, XFS_ATTR_FORK,
> + ip->i_ino, buffer_list);
> + if (error)
> + goto out_free_ip;
> + }
> +
> +out_free_ip:
> + xfs_inode_free(ip);
> + return error;
> +}
> +
> +STATIC int
> +xlog_recover_inode_commit_pass2(
> + struct xlog *log,
> + struct list_head *buffer_list,
> + struct xlog_recover_item *item,
> + xfs_lsn_t current_lsn)
> +{
> + struct xfs_inode_log_format *in_f;
> + struct xfs_mount *mp = log->l_mp;
> + struct xfs_buf *bp;
> + struct xfs_dinode *dip;
> + int len;
> + char *src;
> + char *dest;
> + int error;
> + int attr_index;
> + uint fields;
> + struct xfs_log_dinode *ldip;
> + uint isize;
> + int need_free = 0;
> +
> + if (item->ri_buf[0].i_len == sizeof(struct xfs_inode_log_format)) {
> + in_f = item->ri_buf[0].i_addr;
> + } else {
> + in_f = kmem_alloc(sizeof(struct xfs_inode_log_format), 0);
> + need_free = 1;
> + error = xfs_inode_item_format_convert(&item->ri_buf[0], in_f);
> + if (error)
> + goto error;
> + }
> +
> + /*
> + * Inode buffers can be freed, look out for it,
> + * and do not replay the inode.
> + */
> + if (xlog_is_buffer_cancelled(log, in_f->ilf_blkno, in_f->ilf_len)) {
> + error = 0;
> + trace_xfs_log_recover_inode_cancel(log, in_f);
> + goto error;
> + }
> + trace_xfs_log_recover_inode_recover(log, in_f);
> +
> + error = xfs_buf_read(mp->m_ddev_targp, in_f->ilf_blkno, in_f->ilf_len,
> + 0, &bp, &xfs_inode_buf_ops);
> + if (error)
> + goto error;
> + ASSERT(in_f->ilf_fields & XFS_ILOG_CORE);
> + dip = xfs_buf_offset(bp, in_f->ilf_boffset);
> +
> + /*
> + * Make sure the place we're flushing out to really looks
> + * like an inode!
> + */
> + if (XFS_IS_CORRUPT(mp, !xfs_verify_magic16(bp, dip->di_magic))) {
> + xfs_alert(mp,
> + "%s: Bad inode magic number, dip = "PTR_FMT", dino bp = "PTR_FMT", ino = %Ld",
> + __func__, dip, bp, in_f->ilf_ino);
> + error = -EFSCORRUPTED;
> + goto out_release;
> + }
> + ldip = item->ri_buf[1].i_addr;
> + if (XFS_IS_CORRUPT(mp, ldip->di_magic != XFS_DINODE_MAGIC)) {
> + xfs_alert(mp,
> + "%s: Bad inode log record, rec ptr "PTR_FMT", ino %Ld",
> + __func__, item, in_f->ilf_ino);
> + error = -EFSCORRUPTED;
> + goto out_release;
> + }
> +
> + /*
> + * If the inode has an LSN in it, recover the inode only if it's less
> + * than the lsn of the transaction we are replaying. Note: we still
> + * need to replay an owner change even though the inode is more recent
> + * than the transaction as there is no guarantee that all the btree
> + * blocks are more recent than this transaction, too.
> + */
> + if (dip->di_version >= 3) {
> + xfs_lsn_t lsn = be64_to_cpu(dip->di_lsn);
> +
> + if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) >= 0) {
> + trace_xfs_log_recover_inode_skip(log, in_f);
> + error = 0;
> + goto out_owner_change;
> + }
> + }
> +
> + /*
> + * di_flushiter is only valid for v1/2 inodes. All changes for v3 inodes
> + * are transactional and if ordering is necessary we can determine that
> + * more accurately by the LSN field in the V3 inode core. Don't trust
> + * the inode versions we might be changing them here - use the
> + * superblock flag to determine whether we need to look at di_flushiter
> + * to skip replay when the on disk inode is newer than the log one
> + */
> + if (!xfs_sb_version_has_v3inode(&mp->m_sb) &&
> + ldip->di_flushiter < be16_to_cpu(dip->di_flushiter)) {
> + /*
> + * Deal with the wrap case, DI_MAX_FLUSH is less
> + * than smaller numbers
> + */
> + if (be16_to_cpu(dip->di_flushiter) == DI_MAX_FLUSH &&
> + ldip->di_flushiter < (DI_MAX_FLUSH >> 1)) {
> + /* do nothing */
> + } else {
> + trace_xfs_log_recover_inode_skip(log, in_f);
> + error = 0;
> + goto out_release;
> + }
> + }
> +
> + /* Take the opportunity to reset the flush iteration count */
> + ldip->di_flushiter = 0;
> +
> + if (unlikely(S_ISREG(ldip->di_mode))) {
> + if ((ldip->di_format != XFS_DINODE_FMT_EXTENTS) &&
> + (ldip->di_format != XFS_DINODE_FMT_BTREE)) {
> + XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(3)",
> + XFS_ERRLEVEL_LOW, mp, ldip,
> + sizeof(*ldip));
> + xfs_alert(mp,
> + "%s: Bad regular inode log record, rec ptr "PTR_FMT", "
> + "ino ptr = "PTR_FMT", ino bp = "PTR_FMT", ino %Ld",
> + __func__, item, dip, bp, in_f->ilf_ino);
> + error = -EFSCORRUPTED;
> + goto out_release;
> + }
> + } else if (unlikely(S_ISDIR(ldip->di_mode))) {
> + if ((ldip->di_format != XFS_DINODE_FMT_EXTENTS) &&
> + (ldip->di_format != XFS_DINODE_FMT_BTREE) &&
> + (ldip->di_format != XFS_DINODE_FMT_LOCAL)) {
> + XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(4)",
> + XFS_ERRLEVEL_LOW, mp, ldip,
> + sizeof(*ldip));
> + xfs_alert(mp,
> + "%s: Bad dir inode log record, rec ptr "PTR_FMT", "
> + "ino ptr = "PTR_FMT", ino bp = "PTR_FMT", ino %Ld",
> + __func__, item, dip, bp, in_f->ilf_ino);
> + error = -EFSCORRUPTED;
> + goto out_release;
> + }
> + }
> + if (unlikely(ldip->di_nextents + ldip->di_anextents > ldip->di_nblocks)){
> + XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(5)",
> + XFS_ERRLEVEL_LOW, mp, ldip,
> + sizeof(*ldip));
> + xfs_alert(mp,
> + "%s: Bad inode log record, rec ptr "PTR_FMT", dino ptr "PTR_FMT", "
> + "dino bp "PTR_FMT", ino %Ld, total extents = %d, nblocks = %Ld",
> + __func__, item, dip, bp, in_f->ilf_ino,
> + ldip->di_nextents + ldip->di_anextents,
> + ldip->di_nblocks);
> + error = -EFSCORRUPTED;
> + goto out_release;
> + }
> + if (unlikely(ldip->di_forkoff > mp->m_sb.sb_inodesize)) {
> + XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(6)",
> + XFS_ERRLEVEL_LOW, mp, ldip,
> + sizeof(*ldip));
> + xfs_alert(mp,
> + "%s: Bad inode log record, rec ptr "PTR_FMT", dino ptr "PTR_FMT", "
> + "dino bp "PTR_FMT", ino %Ld, forkoff 0x%x", __func__,
> + item, dip, bp, in_f->ilf_ino, ldip->di_forkoff);
> + error = -EFSCORRUPTED;
> + goto out_release;
> + }
> + isize = xfs_log_dinode_size(mp);
> + if (unlikely(item->ri_buf[1].i_len > isize)) {
> + XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(7)",
> + XFS_ERRLEVEL_LOW, mp, ldip,
> + sizeof(*ldip));
> + xfs_alert(mp,
> + "%s: Bad inode log record length %d, rec ptr "PTR_FMT,
> + __func__, item->ri_buf[1].i_len, item);
> + error = -EFSCORRUPTED;
> + goto out_release;
> + }
> +
> + /* recover the log dinode inode into the on disk inode */
> + xfs_log_dinode_to_disk(ldip, dip);
> +
> + fields = in_f->ilf_fields;
> + if (fields & XFS_ILOG_DEV)
> + xfs_dinode_put_rdev(dip, in_f->ilf_u.ilfu_rdev);
> +
> + if (in_f->ilf_size == 2)
> + goto out_owner_change;
> + len = item->ri_buf[2].i_len;
> + src = item->ri_buf[2].i_addr;
> + ASSERT(in_f->ilf_size <= 4);
> + ASSERT((in_f->ilf_size == 3) || (fields & XFS_ILOG_AFORK));
> + ASSERT(!(fields & XFS_ILOG_DFORK) ||
> + (len == in_f->ilf_dsize));
> +
> + switch (fields & XFS_ILOG_DFORK) {
> + case XFS_ILOG_DDATA:
> + case XFS_ILOG_DEXT:
> + memcpy(XFS_DFORK_DPTR(dip), src, len);
> + break;
> +
> + case XFS_ILOG_DBROOT:
> + xfs_bmbt_to_bmdr(mp, (struct xfs_btree_block *)src, len,
> + (struct xfs_bmdr_block *)XFS_DFORK_DPTR(dip),
> + XFS_DFORK_DSIZE(dip, mp));
> + break;
> +
> + default:
> + /*
> + * There are no data fork flags set.
> + */
> + ASSERT((fields & XFS_ILOG_DFORK) == 0);
> + break;
> + }
> +
> + /*
> + * If we logged any attribute data, recover it. There may or
> + * may not have been any other non-core data logged in this
> + * transaction.
> + */
> + if (in_f->ilf_fields & XFS_ILOG_AFORK) {
> + if (in_f->ilf_fields & XFS_ILOG_DFORK) {
> + attr_index = 3;
> + } else {
> + attr_index = 2;
> + }
> + len = item->ri_buf[attr_index].i_len;
> + src = item->ri_buf[attr_index].i_addr;
> + ASSERT(len == in_f->ilf_asize);
> +
> + switch (in_f->ilf_fields & XFS_ILOG_AFORK) {
> + case XFS_ILOG_ADATA:
> + case XFS_ILOG_AEXT:
> + dest = XFS_DFORK_APTR(dip);
> + ASSERT(len <= XFS_DFORK_ASIZE(dip, mp));
> + memcpy(dest, src, len);
> + break;
> +
> + case XFS_ILOG_ABROOT:
> + dest = XFS_DFORK_APTR(dip);
> + xfs_bmbt_to_bmdr(mp, (struct xfs_btree_block *)src,
> + len, (struct xfs_bmdr_block *)dest,
> + XFS_DFORK_ASIZE(dip, mp));
> + break;
> +
> + default:
> + xfs_warn(log->l_mp, "%s: Invalid flag", __func__);
> + ASSERT(0);
> + error = -EFSCORRUPTED;
> + goto out_release;
> + }
> + }
> +
> +out_owner_change:
> + /* Recover the swapext owner change unless inode has been deleted */
> + if ((in_f->ilf_fields & (XFS_ILOG_DOWNER|XFS_ILOG_AOWNER)) &&
> + (dip->di_mode != 0))
> + error = xfs_recover_inode_owner_change(mp, dip, in_f,
> + buffer_list);
> + /* re-generate the checksum. */
> + xfs_dinode_calc_crc(log->l_mp, dip);
> +
> + ASSERT(bp->b_mount == mp);
> + bp->b_iodone = xlog_recover_iodone;
> + xfs_buf_delwri_queue(bp, buffer_list);
> +
> +out_release:
> + xfs_buf_relse(bp);
> +error:
> + if (need_free)
> + kmem_free(in_f);
> + return error;
> +}
> +
> const struct xlog_recover_item_ops xlog_inode_item_ops = {
> .item_type = XFS_LI_INODE,
> .ra_pass2 = xlog_recover_inode_ra_pass2,
> + .commit_pass2 = xlog_recover_inode_commit_pass2,
> };
> diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
> index d65dc3895a62..cb5902550e8c 100644
> --- a/fs/xfs/xfs_log_recover.c
> +++ b/fs/xfs/xfs_log_recover.c
> @@ -2034,358 +2034,6 @@ xlog_buf_readahead(
> xfs_buf_readahead(log->l_mp->m_ddev_targp, blkno, len, ops);
> }
>
> -/*
> - * Inode fork owner changes
> - *
> - * If we have been told that we have to reparent the inode fork, it's because an
> - * extent swap operation on a CRC enabled filesystem has been done and we are
> - * replaying it. We need to walk the BMBT of the appropriate fork and change the
> - * owners of it.
> - *
> - * The complexity here is that we don't have an inode context to work with, so
> - * after we've replayed the inode we need to instantiate one. This is where the
> - * fun begins.
> - *
> - * We are in the middle of log recovery, so we can't run transactions. That
> - * means we cannot use cache coherent inode instantiation via xfs_iget(), as
> - * that will result in the corresponding iput() running the inode through
> - * xfs_inactive(). If we've just replayed an inode core that changes the link
> - * count to zero (i.e. it's been unlinked), then xfs_inactive() will run
> - * transactions (bad!).
> - *
> - * So, to avoid this, we instantiate an inode directly from the inode core we've
> - * just recovered. We have the buffer still locked, and all we really need to
> - * instantiate is the inode core and the forks being modified. We can do this
> - * manually, then run the inode btree owner change, and then tear down the
> - * xfs_inode without having to run any transactions at all.
> - *
> - * Also, because we don't have a transaction context available here but need to
> - * gather all the buffers we modify for writeback so we pass the buffer_list
> - * instead for the operation to use.
> - */
> -
> -STATIC int
> -xfs_recover_inode_owner_change(
> - struct xfs_mount *mp,
> - struct xfs_dinode *dip,
> - struct xfs_inode_log_format *in_f,
> - struct list_head *buffer_list)
> -{
> - struct xfs_inode *ip;
> - int error;
> -
> - ASSERT(in_f->ilf_fields & (XFS_ILOG_DOWNER|XFS_ILOG_AOWNER));
> -
> - ip = xfs_inode_alloc(mp, in_f->ilf_ino);
> - if (!ip)
> - return -ENOMEM;
> -
> - /* instantiate the inode */
> - ASSERT(dip->di_version >= 3);
> - xfs_inode_from_disk(ip, dip);
> -
> - error = xfs_iformat_fork(ip, dip);
> - if (error)
> - goto out_free_ip;
> -
> - if (!xfs_inode_verify_forks(ip)) {
> - error = -EFSCORRUPTED;
> - goto out_free_ip;
> - }
> -
> - if (in_f->ilf_fields & XFS_ILOG_DOWNER) {
> - ASSERT(in_f->ilf_fields & XFS_ILOG_DBROOT);
> - error = xfs_bmbt_change_owner(NULL, ip, XFS_DATA_FORK,
> - ip->i_ino, buffer_list);
> - if (error)
> - goto out_free_ip;
> - }
> -
> - if (in_f->ilf_fields & XFS_ILOG_AOWNER) {
> - ASSERT(in_f->ilf_fields & XFS_ILOG_ABROOT);
> - error = xfs_bmbt_change_owner(NULL, ip, XFS_ATTR_FORK,
> - ip->i_ino, buffer_list);
> - if (error)
> - goto out_free_ip;
> - }
> -
> -out_free_ip:
> - xfs_inode_free(ip);
> - return error;
> -}
> -
> -STATIC int
> -xlog_recover_inode_pass2(
> - struct xlog *log,
> - struct list_head *buffer_list,
> - struct xlog_recover_item *item,
> - xfs_lsn_t current_lsn)
> -{
> - struct xfs_inode_log_format *in_f;
> - xfs_mount_t *mp = log->l_mp;
> - xfs_buf_t *bp;
> - xfs_dinode_t *dip;
> - int len;
> - char *src;
> - char *dest;
> - int error;
> - int attr_index;
> - uint fields;
> - struct xfs_log_dinode *ldip;
> - uint isize;
> - int need_free = 0;
> -
> - if (item->ri_buf[0].i_len == sizeof(struct xfs_inode_log_format)) {
> - in_f = item->ri_buf[0].i_addr;
> - } else {
> - in_f = kmem_alloc(sizeof(struct xfs_inode_log_format), 0);
> - need_free = 1;
> - error = xfs_inode_item_format_convert(&item->ri_buf[0], in_f);
> - if (error)
> - goto error;
> - }
> -
> - /*
> - * Inode buffers can be freed, look out for it,
> - * and do not replay the inode.
> - */
> - if (xlog_is_buffer_cancelled(log, in_f->ilf_blkno, in_f->ilf_len)) {
> - error = 0;
> - trace_xfs_log_recover_inode_cancel(log, in_f);
> - goto error;
> - }
> - trace_xfs_log_recover_inode_recover(log, in_f);
> -
> - error = xfs_buf_read(mp->m_ddev_targp, in_f->ilf_blkno, in_f->ilf_len,
> - 0, &bp, &xfs_inode_buf_ops);
> - if (error)
> - goto error;
> - ASSERT(in_f->ilf_fields & XFS_ILOG_CORE);
> - dip = xfs_buf_offset(bp, in_f->ilf_boffset);
> -
> - /*
> - * Make sure the place we're flushing out to really looks
> - * like an inode!
> - */
> - if (XFS_IS_CORRUPT(mp, !xfs_verify_magic16(bp, dip->di_magic))) {
> - xfs_alert(mp,
> - "%s: Bad inode magic number, dip = "PTR_FMT", dino bp = "PTR_FMT", ino = %Ld",
> - __func__, dip, bp, in_f->ilf_ino);
> - error = -EFSCORRUPTED;
> - goto out_release;
> - }
> - ldip = item->ri_buf[1].i_addr;
> - if (XFS_IS_CORRUPT(mp, ldip->di_magic != XFS_DINODE_MAGIC)) {
> - xfs_alert(mp,
> - "%s: Bad inode log record, rec ptr "PTR_FMT", ino %Ld",
> - __func__, item, in_f->ilf_ino);
> - error = -EFSCORRUPTED;
> - goto out_release;
> - }
> -
> - /*
> - * If the inode has an LSN in it, recover the inode only if it's less
> - * than the lsn of the transaction we are replaying. Note: we still
> - * need to replay an owner change even though the inode is more recent
> - * than the transaction as there is no guarantee that all the btree
> - * blocks are more recent than this transaction, too.
> - */
> - if (dip->di_version >= 3) {
> - xfs_lsn_t lsn = be64_to_cpu(dip->di_lsn);
> -
> - if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) >= 0) {
> - trace_xfs_log_recover_inode_skip(log, in_f);
> - error = 0;
> - goto out_owner_change;
> - }
> - }
> -
> - /*
> - * di_flushiter is only valid for v1/2 inodes. All changes for v3 inodes
> - * are transactional and if ordering is necessary we can determine that
> - * more accurately by the LSN field in the V3 inode core. Don't trust
> - * the inode versions we might be changing them here - use the
> - * superblock flag to determine whether we need to look at di_flushiter
> - * to skip replay when the on disk inode is newer than the log one
> - */
> - if (!xfs_sb_version_has_v3inode(&mp->m_sb) &&
> - ldip->di_flushiter < be16_to_cpu(dip->di_flushiter)) {
> - /*
> - * Deal with the wrap case, DI_MAX_FLUSH is less
> - * than smaller numbers
> - */
> - if (be16_to_cpu(dip->di_flushiter) == DI_MAX_FLUSH &&
> - ldip->di_flushiter < (DI_MAX_FLUSH >> 1)) {
> - /* do nothing */
> - } else {
> - trace_xfs_log_recover_inode_skip(log, in_f);
> - error = 0;
> - goto out_release;
> - }
> - }
> -
> - /* Take the opportunity to reset the flush iteration count */
> - ldip->di_flushiter = 0;
> -
> - if (unlikely(S_ISREG(ldip->di_mode))) {
> - if ((ldip->di_format != XFS_DINODE_FMT_EXTENTS) &&
> - (ldip->di_format != XFS_DINODE_FMT_BTREE)) {
> - XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(3)",
> - XFS_ERRLEVEL_LOW, mp, ldip,
> - sizeof(*ldip));
> - xfs_alert(mp,
> - "%s: Bad regular inode log record, rec ptr "PTR_FMT", "
> - "ino ptr = "PTR_FMT", ino bp = "PTR_FMT", ino %Ld",
> - __func__, item, dip, bp, in_f->ilf_ino);
> - error = -EFSCORRUPTED;
> - goto out_release;
> - }
> - } else if (unlikely(S_ISDIR(ldip->di_mode))) {
> - if ((ldip->di_format != XFS_DINODE_FMT_EXTENTS) &&
> - (ldip->di_format != XFS_DINODE_FMT_BTREE) &&
> - (ldip->di_format != XFS_DINODE_FMT_LOCAL)) {
> - XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(4)",
> - XFS_ERRLEVEL_LOW, mp, ldip,
> - sizeof(*ldip));
> - xfs_alert(mp,
> - "%s: Bad dir inode log record, rec ptr "PTR_FMT", "
> - "ino ptr = "PTR_FMT", ino bp = "PTR_FMT", ino %Ld",
> - __func__, item, dip, bp, in_f->ilf_ino);
> - error = -EFSCORRUPTED;
> - goto out_release;
> - }
> - }
> - if (unlikely(ldip->di_nextents + ldip->di_anextents > ldip->di_nblocks)){
> - XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(5)",
> - XFS_ERRLEVEL_LOW, mp, ldip,
> - sizeof(*ldip));
> - xfs_alert(mp,
> - "%s: Bad inode log record, rec ptr "PTR_FMT", dino ptr "PTR_FMT", "
> - "dino bp "PTR_FMT", ino %Ld, total extents = %d, nblocks = %Ld",
> - __func__, item, dip, bp, in_f->ilf_ino,
> - ldip->di_nextents + ldip->di_anextents,
> - ldip->di_nblocks);
> - error = -EFSCORRUPTED;
> - goto out_release;
> - }
> - if (unlikely(ldip->di_forkoff > mp->m_sb.sb_inodesize)) {
> - XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(6)",
> - XFS_ERRLEVEL_LOW, mp, ldip,
> - sizeof(*ldip));
> - xfs_alert(mp,
> - "%s: Bad inode log record, rec ptr "PTR_FMT", dino ptr "PTR_FMT", "
> - "dino bp "PTR_FMT", ino %Ld,