Skip to content

Commit ea78d80

Browse files
djwongdchinner
authored andcommitted
xfs: track log done items directly in the deferred pending work item
Christoph reports slab corruption when a deferred refcount update aborts during _defer_finish(). The cause of this was broken log item state tracking in xfs_defer_pending -- upon an abort, _defer_trans_abort() will call abort_intent on all intent items, including the ones that have already had a done item attached. This is incorrect because each intent item has 2 refcount: the first is released when the intent item is committed to the log; and the second is released when the _done_ item is committed to the log, or by the intent creator if there is no done item. In other words, once we log the done item, responsibility for releasing the intent item's second refcount is transferred to the done item and /must not/ be performed by anything else. The dfp_committed flag should have been tracking whether or not we had a done item so that _defer_trans_abort could decide if it needs to abort the intent item, but due to a thinko this was not the case. Rip it out and track the done item directly so that we do the right thing w.r.t. intent item freeing. Signed-off-by: Darrick J. Wong <[email protected]> Reported-by: Christoph Hellwig <[email protected]> Reviewed-by: Dave Chinner <[email protected]> Signed-off-by: Dave Chinner <[email protected]>
1 parent 17de0a9 commit ea78d80

File tree

3 files changed

+6
-15
lines changed

3 files changed

+6
-15
lines changed

fs/xfs/libxfs/xfs_defer.c

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ xfs_defer_trans_abort(
194194
/* Abort intent items. */
195195
list_for_each_entry(dfp, &dop->dop_pending, dfp_list) {
196196
trace_xfs_defer_pending_abort(tp->t_mountp, dfp);
197-
if (dfp->dfp_committed)
197+
if (!dfp->dfp_done)
198198
dfp->dfp_type->abort_intent(dfp->dfp_intent);
199199
}
200200

@@ -290,7 +290,6 @@ xfs_defer_finish(
290290
struct xfs_defer_pending *dfp;
291291
struct list_head *li;
292292
struct list_head *n;
293-
void *done_item = NULL;
294293
void *state;
295294
int error = 0;
296295
void (*cleanup_fn)(struct xfs_trans *, void *, int);
@@ -309,19 +308,11 @@ xfs_defer_finish(
309308
if (error)
310309
goto out;
311310

312-
/* Mark all pending intents as committed. */
313-
list_for_each_entry_reverse(dfp, &dop->dop_pending, dfp_list) {
314-
if (dfp->dfp_committed)
315-
break;
316-
trace_xfs_defer_pending_commit((*tp)->t_mountp, dfp);
317-
dfp->dfp_committed = true;
318-
}
319-
320311
/* Log an intent-done item for the first pending item. */
321312
dfp = list_first_entry(&dop->dop_pending,
322313
struct xfs_defer_pending, dfp_list);
323314
trace_xfs_defer_pending_finish((*tp)->t_mountp, dfp);
324-
done_item = dfp->dfp_type->create_done(*tp, dfp->dfp_intent,
315+
dfp->dfp_done = dfp->dfp_type->create_done(*tp, dfp->dfp_intent,
325316
dfp->dfp_count);
326317
cleanup_fn = dfp->dfp_type->finish_cleanup;
327318

@@ -331,7 +322,7 @@ xfs_defer_finish(
331322
list_del(li);
332323
dfp->dfp_count--;
333324
error = dfp->dfp_type->finish_item(*tp, dop, li,
334-
done_item, &state);
325+
dfp->dfp_done, &state);
335326
if (error) {
336327
/*
337328
* Clean up after ourselves and jump out.
@@ -428,8 +419,8 @@ xfs_defer_add(
428419
dfp = kmem_alloc(sizeof(struct xfs_defer_pending),
429420
KM_SLEEP | KM_NOFS);
430421
dfp->dfp_type = defer_op_types[type];
431-
dfp->dfp_committed = false;
432422
dfp->dfp_intent = NULL;
423+
dfp->dfp_done = NULL;
433424
dfp->dfp_count = 0;
434425
INIT_LIST_HEAD(&dfp->dfp_work);
435426
list_add_tail(&dfp->dfp_list, &dop->dop_intake);

fs/xfs/libxfs/xfs_defer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ struct xfs_defer_op_type;
3030
struct xfs_defer_pending {
3131
const struct xfs_defer_op_type *dfp_type; /* function pointers */
3232
struct list_head dfp_list; /* pending items */
33-
bool dfp_committed; /* committed trans? */
3433
void *dfp_intent; /* log intent item */
34+
void *dfp_done; /* log done item */
3535
struct list_head dfp_work; /* work items */
3636
unsigned int dfp_count; /* # extent items */
3737
};

fs/xfs/xfs_trace.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2295,7 +2295,7 @@ DECLARE_EVENT_CLASS(xfs_defer_pending_class,
22952295
__entry->dev = mp ? mp->m_super->s_dev : 0;
22962296
__entry->type = dfp->dfp_type->type;
22972297
__entry->intent = dfp->dfp_intent;
2298-
__entry->committed = dfp->dfp_committed;
2298+
__entry->committed = dfp->dfp_done != NULL;
22992299
__entry->nr = dfp->dfp_count;
23002300
),
23012301
TP_printk("dev %d:%d optype %d intent %p committed %d nr %d\n",

0 commit comments

Comments
 (0)