@@ -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
@@ -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 */
@@ -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 */
@@ -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)
@@ -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 *);
new file mode 100644
@@ -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);
+}
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