[RFC v6 PATCH 10/10] xfs: relog random buffers based on errortag

Brian Foster
2020-04-06T12:36:43Z

Since there is currently no specific use case for buffer relogging,
add some hacky and experimental code to relog random buffers when
the associated errortag is enabled. Use fixed termination logic
regardless of the user-specified error rate to help ensure that the
relog queue doesn't grow indefinitely.

Note that this patch was useful in causing log reservation deadlocks
on an fsstress workload if the relog mechanism code is modified to
acquire its own log reservation rather than rely on the
pre-reservation mechanism. In other words, this helps prove that the
relog reservation management code effectively avoids log reservation
deadlocks.

Signed-off-by: Brian Foster <bfoster@redhat.com>
---
fs/xfs/xfs_buf_item.c | 1 +
fs/xfs/xfs_trans.h | 4 +++-
fs/xfs/xfs_trans_ail.c | 33 +++++++++++++++++++++++++++++++++
fs/xfs/xfs_trans_buf.c | 14 ++++++++++++++
4 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c
index 762359e6ab65..ed91de33278b 100644
--- a/fs/xfs/xfs_buf_item.c
+++ b/fs/xfs/xfs_buf_item.c
@@ -467,6 +467,7 @@ xfs_buf_item_unpin(
ASSERT(!test_bit(XFS_LI_RELOG_QUEUED, &lip->li_flags));
if (test_bit(XFS_LI_RELOG, &lip->li_flags)) {
atomic_dec(&bp->b_pin_count);
+ clear_bit(XFS_LI_RELOG_RAND, &bip->bli_item.li_flags);
xfs_trans_relog_item_cancel(NULL, lip, true);
}

diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h
index a7a70430b8b2..0f40ce784367 100644
--- a/fs/xfs/xfs_trans.h
+++ b/fs/xfs/xfs_trans.h
@@ -64,6 +64,7 @@ struct xfs_log_item {
#define XFS_LI_DIRTY 3 /* log item dirty in transaction */
#define XFS_LI_RELOG 4 /* automatically relog item */
#define XFS_LI_RELOG_QUEUED 5 /* queued for relog */
+#define XFS_LI_RELOG_RAND 6

#define XFS_LI_FLAGS \
{ (1 << XFS_LI_IN_AIL), "IN_AIL" }, \
@@ -71,7 +72,8 @@ struct xfs_log_item {
{ (1 << XFS_LI_FAILED), "FAILED" }, \
{ (1 << XFS_LI_DIRTY), "DIRTY" }, \
{ (1 << XFS_LI_RELOG), "RELOG" }, \
- { (1 << XFS_LI_RELOG_QUEUED), "RELOG_QUEUED" }
+ { (1 << XFS_LI_RELOG_QUEUED), "RELOG_QUEUED" }, \
+ { (1 << XFS_LI_RELOG_RAND), "RELOG_RAND" }

struct xfs_item_ops {
unsigned flags;
diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c
index b78d026d6564..a34a5e73427e 100644
--- a/fs/xfs/xfs_trans_ail.c
+++ b/fs/xfs/xfs_trans_ail.c
@@ -18,6 +18,7 @@
#include "xfs_error.h"
#include "xfs_log.h"
#include "xfs_log_priv.h"
+#include "xfs_buf_item.h"

#ifdef DEBUG
/*
@@ -176,6 +177,7 @@ xfs_ail_relog(
struct xfs_trans_res tres = {};
struct xfs_trans *tp;
struct xfs_log_item *lip, *lipp;
+ int cancelres;
int error;
LIST_HEAD(relog_list);

@@ -209,6 +211,37 @@ xfs_ail_relog(
ASSERT(lip->li_ops->iop_relog);
if (lip->li_ops->iop_relog)
lip->li_ops->iop_relog(lip, tp);
+
+ /*
+ * Cancel random buffer relogs at a fixed rate to
+ * prevent too much buildup.
+ */
+ if (test_bit(XFS_LI_RELOG_RAND, &lip->li_flags) &&
+ ((prandom_u32() & 1) ||
+ (mp->m_flags & XFS_MOUNT_UNMOUNTING))) {
+ struct xfs_buf_log_item *bli;
+ bli = container_of(lip, struct xfs_buf_log_item,
+ bli_item);
+ xfs_trans_relog_buf_cancel(tp, bli->bli_buf);
+ }
+ }
+
+ /*
+ * Cancelling relog reservation in the same transaction as
+ * consuming it means the current transaction over releases
+ * reservation on commit and the next transaction reservation
+ * restores the grant heads to even. To avoid this behavior,
+ * remove surplus reservation (->t_curr_res) from the committing
+ * transaction and replace it with a reduction in the
+ * reservation requirement (->t_unit_res) for the next. This has
+ * no net effect on reservation accounting, but ensures we don't
+ * cause problems elsewhere with odd reservation behavior.
+ */
+ cancelres = tp->t_ticket->t_curr_res - tp->t_ticket->t_unit_res;
+ if (cancelres) {
+ tp->t_ticket->t_curr_res -= cancelres;
+ tp->t_ticket->t_unit_res -= cancelres;
+ tp->t_log_res -= cancelres;
}

error = xfs_trans_roll(&tp);
diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c
index 2bed5d615541..9dadf39a40bb 100644
--- a/fs/xfs/xfs_trans_buf.c
+++ b/fs/xfs/xfs_trans_buf.c
@@ -14,6 +14,8 @@
#include "xfs_buf_item.h"
#include "xfs_trans_priv.h"
#include "xfs_trace.h"
+#include "xfs_error.h"
+#include "xfs_errortag.h"

/*
* Check to see if a buffer matching the given parameters is already
@@ -527,6 +529,17 @@ xfs_trans_log_buf(

trace_xfs_trans_log_buf(bip);
xfs_buf_item_log(bip, first, last);
+
+ /*
+ * Relog random buffers so long as the transaction is relog enabled and
+ * the buffer wasn't already relogged explicitly.
+ */
+ if (XFS_TEST_ERROR(false, tp->t_mountp, XFS_ERRTAG_RELOG) &&
+ (tp->t_flags & XFS_TRANS_RELOG) &&
+ !test_bit(XFS_LI_RELOG, &bip->bli_item.li_flags)) {
+ if (xfs_trans_relog_buf(tp, bp))
+ set_bit(XFS_LI_RELOG_RAND, &bip->bli_item.li_flags);
+ }
}

@@ -845,4 +858,5 @@ xfs_trans_relog_buf_cancel(

atomic_dec(&bp->b_pin_count);
xfs_trans_relog_item_cancel(tp, &bip->bli_item, false);
+ clear_bit(XFS_LI_RELOG_RAND, &bip->bli_item.li_flags);
}
--
2.21.1