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: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
parent
b7e0b6ff54
commit
b7b2846fe2
|
@ -249,6 +249,10 @@ xfs_defer_trans_roll(
|
|||
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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Roll the transaction. */
|
||||
|
@ -264,6 +268,12 @@ xfs_defer_trans_roll(
|
|||
for (i = 0; i < XFS_DEFER_OPS_NR_INODES && dop->dop_inodes[i]; i++)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -493,9 +528,7 @@ xfs_defer_init(
|
|||
struct xfs_defer_ops *dop,
|
||||
xfs_fsblock_t *fbp)
|
||||
{
|
||||
dop->dop_committed = false;
|
||||
dop->dop_low = false;
|
||||
memset(&dop->dop_inodes, 0, sizeof(dop->dop_inodes));
|
||||
memset(dop, 0, sizeof(struct xfs_defer_ops));
|
||||
*fbp = NULLFSBLOCK;
|
||||
INIT_LIST_HEAD(&dop->dop_intake);
|
||||
INIT_LIST_HEAD(&dop->dop_pending);
|
||||
|
|
|
@ -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_BUFS 2 /* join up to two buffers */
|
||||
|
||||
struct xfs_defer_ops {
|
||||
bool dop_committed; /* did any trans commit? */
|
||||
|
@ -66,8 +67,9 @@ struct xfs_defer_ops {
|
|||
struct list_head dop_intake; /* unlogged 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_buf *dop_bufs[XFS_DEFER_OPS_NR_BUFS];
|
||||
};
|
||||
|
||||
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);
|
|||
void xfs_defer_init(struct xfs_defer_ops *dop, xfs_fsblock_t *fbp);
|
||||
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_bjoin(struct xfs_defer_ops *dop, struct xfs_buf *bp);
|
||||
|
||||
/* Description of a deferred type. */
|
||||
struct xfs_defer_op_type {
|
||||
|
|
Loading…
Reference in New Issue