Signed-off-by: Brian Foster <bfoster@xxxxxxxxxx> --- 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 --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); +} -- 2.20.1