diff mbox series

[RFC,v2,2/3] xfs: prototype automatic intent relogging mechanism

Message ID 20191122181927.32870-3-bfoster@redhat.com (mailing list archive)
State New, archived
Headers show
Series xfs: automatic relogging experiment | expand

Commit Message

Brian Foster Nov. 22, 2019, 6:19 p.m. UTC
Signed-off-by: Brian Foster <bfoster@redhat.com>
---
 fs/xfs/Makefile          |   1 +
 fs/xfs/xfs_log.c         |   9 +++
 fs/xfs/xfs_log_priv.h    |   1 +
 fs/xfs/xfs_trans.c       |   2 +-
 fs/xfs/xfs_trans.h       |  13 ++++
 fs/xfs/xfs_trans_relog.c | 130 +++++++++++++++++++++++++++++++++++++++
 6 files changed, 155 insertions(+), 1 deletion(-)
 create mode 100644 fs/xfs/xfs_trans_relog.c
diff mbox series

Patch

diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index aceca2f9a3db..c4664a972e50 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -92,6 +92,7 @@  xfs-y				+= xfs_aops.o \
 				   xfs_symlink.o \
 				   xfs_sysfs.o \
 				   xfs_trans.o \
+				   xfs_trans_relog.o \
 				   xfs_xattr.o \
 				   kmem.o
 
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index 0c0c035c5be0..4f4c6b38621a 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -1531,12 +1531,20 @@  xlog_alloc_log(
 	if (!log->l_ioend_workqueue)
 		goto out_free_iclog;
 
+	log->l_relog_workqueue = alloc_workqueue("xfs-relog/%s",
+			WQ_MEM_RECLAIM | WQ_FREEZABLE | WQ_HIGHPRI, 0,
+			mp->m_super->s_id);
+	if (!log->l_relog_workqueue)
+		goto out_destroy_workqueue;
+
 	error = xlog_cil_init(log);
 	if (error)
 		goto out_destroy_workqueue;
 	return log;
 
 out_destroy_workqueue:
+	if (log->l_relog_workqueue)
+		destroy_workqueue(log->l_relog_workqueue);
 	destroy_workqueue(log->l_ioend_workqueue);
 out_free_iclog:
 	for (iclog = log->l_iclog; iclog; iclog = prev_iclog) {
@@ -2008,6 +2016,7 @@  xlog_dealloc_log(
 	}
 
 	log->l_mp->m_log = NULL;
+	destroy_workqueue(log->l_relog_workqueue);
 	destroy_workqueue(log->l_ioend_workqueue);
 	kmem_free(log);
 }	/* xlog_dealloc_log */
diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h
index b192c5a9f9fd..4d557a82eb73 100644
--- a/fs/xfs/xfs_log_priv.h
+++ b/fs/xfs/xfs_log_priv.h
@@ -349,6 +349,7 @@  struct xlog {
 	struct xfs_cil		*l_cilp;	/* CIL log is working with */
 	struct xfs_buftarg	*l_targ;        /* buftarg of log */
 	struct workqueue_struct	*l_ioend_workqueue; /* for I/O completions */
+	struct workqueue_struct	*l_relog_workqueue; /* for auto relog */
 	struct delayed_work	l_work;		/* background flush work */
 	uint			l_flags;
 	uint			l_quotaoffs_flag; /* XFS_DQ_*, for QUOTAOFFs */
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
index 3b208f9a865c..37011fc803fc 100644
--- a/fs/xfs/xfs_trans.c
+++ b/fs/xfs/xfs_trans.c
@@ -923,7 +923,7 @@  xfs_trans_committed_bulk(
  * have already been unlocked as if the commit had succeeded.
  * Do not reference the transaction structure after this call.
  */
-static int
+int
 __xfs_trans_commit(
 	struct xfs_trans	*tp,
 	bool			regrant)
diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h
index 64d7f171ebd3..066d4aca8416 100644
--- a/fs/xfs/xfs_trans.h
+++ b/fs/xfs/xfs_trans.h
@@ -48,6 +48,7 @@  struct xfs_log_item {
 	struct xfs_log_vec		*li_lv;		/* active log vector */
 	struct xfs_log_vec		*li_lv_shadow;	/* standby vector */
 	xfs_lsn_t			li_seq;		/* CIL commit seq */
+	void				*li_priv;
 };
 
 /*
@@ -143,6 +144,14 @@  typedef struct xfs_trans {
 	unsigned long		t_pflags;	/* saved process flags state */
 } xfs_trans_t;
 
+struct xfs_trans_relog {
+	struct work_struct	tr_work;
+	unsigned int		tr_log_res;
+	unsigned int		tr_log_count;
+	struct xlog_ticket	*tr_tic;
+	struct xfs_log_item	*tr_lip;
+};
+
 /*
  * XFS transaction mechanism exported interfaces that are
  * actually macros.
@@ -231,7 +240,11 @@  bool		xfs_trans_buf_is_dirty(struct xfs_buf *bp);
 void		xfs_trans_log_inode(xfs_trans_t *, struct xfs_inode *, uint);
 
 int		xfs_trans_commit(struct xfs_trans *);
+int		__xfs_trans_commit(struct xfs_trans *, bool);
 int		xfs_trans_roll(struct xfs_trans **);
+int		xfs_trans_relog(struct xfs_trans *, struct xfs_log_item *);
+void		xfs_trans_relog_cancel(struct xfs_log_item *);
+void		xfs_trans_relog_queue(struct xfs_log_item *);
 int		xfs_trans_roll_inode(struct xfs_trans **, struct xfs_inode *);
 void		xfs_trans_cancel(xfs_trans_t *);
 int		xfs_trans_ail_init(struct xfs_mount *);
diff --git a/fs/xfs/xfs_trans_relog.c b/fs/xfs/xfs_trans_relog.c
new file mode 100644
index 000000000000..af139aa501f6
--- /dev/null
+++ b/fs/xfs/xfs_trans_relog.c
@@ -0,0 +1,130 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Red Hat, Inc.
+ * All Rights Reserved.
+ */
+#include "xfs.h"
+#include "xfs_log_format.h"
+#include "xfs_trans.h"
+#include "xfs_trans_priv.h"
+#include "xfs_trans_resv.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_mount.h"
+#include "xfs_log_priv.h"
+#include "xfs_log.h"
+
+/*
+ * Helper to commit and regrant the log ticket as if the transaction is being
+ * rolled. The ticket is expected to be held by the caller and can be reused in
+ * a subsequent transaction.
+ */
+static int
+xfs_trans_commit_regrant(
+	struct xfs_trans	*tp)
+{
+	struct xfs_mount	*mp = tp->t_mountp;
+	struct xlog_ticket	*tic = tp->t_ticket;
+	int			error;
+
+	ASSERT(atomic_read(&tic->t_ref) > 1);
+	error = __xfs_trans_commit(tp, true);
+	if (!error)
+		error = xfs_log_regrant(mp, tic);
+	return error;
+}
+
+static void
+xfs_relog_worker(
+	struct work_struct	*work)
+{
+	struct xfs_trans_relog	*trp = container_of(work,
+					struct xfs_trans_relog, tr_work);
+	struct xfs_log_item	*lip = trp->tr_lip;
+	struct xfs_mount	*mp = lip->li_mountp;
+	struct xfs_trans_res	resv = {};
+	struct xfs_trans	*tp;
+	int			error;
+
+	error = xfs_trans_alloc(mp, &resv, 0, 0, 0, &tp);
+	if (error)
+		return;
+
+	/* associate the caller ticket with our empty transaction */
+	tp->t_log_res = trp->tr_log_res;
+	tp->t_log_count = trp->tr_log_count;
+	tp->t_flags |= XFS_TRANS_PERM_LOG_RES;
+	tp->t_ticket = xfs_log_ticket_get(trp->tr_tic);
+
+	xfs_trans_add_item(tp, lip);
+	tp->t_flags |= XFS_TRANS_DIRTY;
+	set_bit(XFS_LI_DIRTY, &lip->li_flags);
+
+	/* commit the transaction and regrant the ticket for the next time */
+	error = xfs_trans_commit_regrant(tp);
+	ASSERT(!error);
+}
+
+void
+xfs_trans_relog_queue(
+	struct xfs_log_item	*lip)
+{
+	struct xfs_mount	*mp = lip->li_mountp;
+	struct xfs_trans_relog	*trp = lip->li_priv;
+
+	if (!trp)
+		return;
+
+	queue_work(mp->m_log->l_relog_workqueue, &trp->tr_work);
+}
+
+/*
+ * Commit the caller transaction, regrant the ticket and use it for automatic
+ * relogging of the provided log item.
+ */
+int
+xfs_trans_relog(
+	struct xfs_trans	*tp,
+	struct xfs_log_item	*lip)
+{
+	struct xfs_trans_relog	*trp;
+	int			error;
+
+	trp = kmem_zalloc(sizeof(struct xfs_trans_relog), KM_MAYFAIL);
+	if (!trp) {
+		xfs_trans_cancel(tp);
+		return -ENOMEM;
+	}
+
+	INIT_WORK(&trp->tr_work, xfs_relog_worker);
+	trp->tr_lip = lip;
+	trp->tr_tic = xfs_log_ticket_get(tp->t_ticket);
+	trp->tr_log_res = tp->t_log_res;
+	trp->tr_log_count = tp->t_log_count;
+	lip->li_priv = trp;
+
+	error = xfs_trans_commit_regrant(tp);
+	if (error) {
+		xfs_log_ticket_put(trp->tr_tic);
+		kmem_free(trp);
+	}
+
+	return error;
+}
+
+void
+xfs_trans_relog_cancel(
+	struct xfs_log_item	*lip)
+{
+	struct xfs_trans_relog	*trp = lip->li_priv;
+	struct xfs_mount	*mp = lip->li_mountp;
+
+	/* cancel queued relog task */
+	cancel_work_sync(&trp->tr_work);
+	lip->li_priv = NULL;
+
+	/* release log reservation and free ticket */
+	ASSERT(atomic_read(&trp->tr_tic->t_ref) == 1);
+	xfs_log_done(mp, trp->tr_tic, NULL, false);
+	kmem_free(trp);
+}