Commit b7b2846f authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: add the ability to join a held buffer to a defer_ops

In certain cases, defer_ops callers will lock a buffer and want to hold
the lock across transaction rolls.  Similar to ijoined inodes, we want
to dirty & join the buffer with each transaction roll in defer_finish so
that afterwards the caller still owns the buffer lock and we haven't
inadvertently pinned the log.
Signed-off-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent b7e0b6ff
...@@ -249,6 +249,10 @@ xfs_defer_trans_roll( ...@@ -249,6 +249,10 @@ xfs_defer_trans_roll(
for (i = 0; i < XFS_DEFER_OPS_NR_INODES && dop->dop_inodes[i]; i++) for (i = 0; i < XFS_DEFER_OPS_NR_INODES && dop->dop_inodes[i]; i++)
xfs_trans_log_inode(*tp, dop->dop_inodes[i], XFS_ILOG_CORE); xfs_trans_log_inode(*tp, dop->dop_inodes[i], XFS_ILOG_CORE);
/* Hold the (previously bjoin'd) buffer locked across the roll. */
for (i = 0; i < XFS_DEFER_OPS_NR_BUFS && dop->dop_bufs[i]; i++)
xfs_trans_dirty_buf(*tp, dop->dop_bufs[i]);
trace_xfs_defer_trans_roll((*tp)->t_mountp, dop); trace_xfs_defer_trans_roll((*tp)->t_mountp, dop);
/* Roll the transaction. */ /* Roll the transaction. */
...@@ -264,6 +268,12 @@ xfs_defer_trans_roll( ...@@ -264,6 +268,12 @@ xfs_defer_trans_roll(
for (i = 0; i < XFS_DEFER_OPS_NR_INODES && dop->dop_inodes[i]; i++) for (i = 0; i < XFS_DEFER_OPS_NR_INODES && dop->dop_inodes[i]; i++)
xfs_trans_ijoin(*tp, dop->dop_inodes[i], 0); xfs_trans_ijoin(*tp, dop->dop_inodes[i], 0);
/* Rejoin the buffers and dirty them so the log moves forward. */
for (i = 0; i < XFS_DEFER_OPS_NR_BUFS && dop->dop_bufs[i]; i++) {
xfs_trans_bjoin(*tp, dop->dop_bufs[i]);
xfs_trans_bhold(*tp, dop->dop_bufs[i]);
}
return error; return error;
} }
...@@ -295,6 +305,31 @@ xfs_defer_ijoin( ...@@ -295,6 +305,31 @@ xfs_defer_ijoin(
} }
} }
ASSERT(0);
return -EFSCORRUPTED;
}
/*
* Add this buffer to the deferred op. Each joined buffer is relogged
* each time we roll the transaction.
*/
int
xfs_defer_bjoin(
struct xfs_defer_ops *dop,
struct xfs_buf *bp)
{
int i;
for (i = 0; i < XFS_DEFER_OPS_NR_BUFS; i++) {
if (dop->dop_bufs[i] == bp)
return 0;
else if (dop->dop_bufs[i] == NULL) {
dop->dop_bufs[i] = bp;
return 0;
}
}
ASSERT(0);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
...@@ -493,9 +528,7 @@ xfs_defer_init( ...@@ -493,9 +528,7 @@ xfs_defer_init(
struct xfs_defer_ops *dop, struct xfs_defer_ops *dop,
xfs_fsblock_t *fbp) xfs_fsblock_t *fbp)
{ {
dop->dop_committed = false; memset(dop, 0, sizeof(struct xfs_defer_ops));
dop->dop_low = false;
memset(&dop->dop_inodes, 0, sizeof(dop->dop_inodes));
*fbp = NULLFSBLOCK; *fbp = NULLFSBLOCK;
INIT_LIST_HEAD(&dop->dop_intake); INIT_LIST_HEAD(&dop->dop_intake);
INIT_LIST_HEAD(&dop->dop_pending); INIT_LIST_HEAD(&dop->dop_pending);
......
...@@ -59,6 +59,7 @@ enum xfs_defer_ops_type { ...@@ -59,6 +59,7 @@ enum xfs_defer_ops_type {
}; };
#define XFS_DEFER_OPS_NR_INODES 2 /* join up to two inodes */ #define XFS_DEFER_OPS_NR_INODES 2 /* join up to two inodes */
#define XFS_DEFER_OPS_NR_BUFS 2 /* join up to two buffers */
struct xfs_defer_ops { struct xfs_defer_ops {
bool dop_committed; /* did any trans commit? */ bool dop_committed; /* did any trans commit? */
...@@ -66,8 +67,9 @@ struct xfs_defer_ops { ...@@ -66,8 +67,9 @@ struct xfs_defer_ops {
struct list_head dop_intake; /* unlogged pending work */ struct list_head dop_intake; /* unlogged pending work */
struct list_head dop_pending; /* logged pending work */ struct list_head dop_pending; /* logged pending work */
/* relog these inodes with each roll */ /* relog these with each roll */
struct xfs_inode *dop_inodes[XFS_DEFER_OPS_NR_INODES]; struct xfs_inode *dop_inodes[XFS_DEFER_OPS_NR_INODES];
struct xfs_buf *dop_bufs[XFS_DEFER_OPS_NR_BUFS];
}; };
void xfs_defer_add(struct xfs_defer_ops *dop, enum xfs_defer_ops_type type, void xfs_defer_add(struct xfs_defer_ops *dop, enum xfs_defer_ops_type type,
...@@ -77,6 +79,7 @@ void xfs_defer_cancel(struct xfs_defer_ops *dop); ...@@ -77,6 +79,7 @@ void xfs_defer_cancel(struct xfs_defer_ops *dop);
void xfs_defer_init(struct xfs_defer_ops *dop, xfs_fsblock_t *fbp); void xfs_defer_init(struct xfs_defer_ops *dop, xfs_fsblock_t *fbp);
bool xfs_defer_has_unfinished_work(struct xfs_defer_ops *dop); bool xfs_defer_has_unfinished_work(struct xfs_defer_ops *dop);
int xfs_defer_ijoin(struct xfs_defer_ops *dop, struct xfs_inode *ip); int xfs_defer_ijoin(struct xfs_defer_ops *dop, struct xfs_inode *ip);
int xfs_defer_bjoin(struct xfs_defer_ops *dop, struct xfs_buf *bp);
/* Description of a deferred type. */ /* Description of a deferred type. */
struct xfs_defer_op_type { struct xfs_defer_op_type {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment