summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2016-08-29 23:51:39 -0400
committerDave Chinner <david@fromorbit.com>2016-08-29 23:51:39 -0400
commitea78d80866ce375defb2fdd1c8a3aafec95e0f85 (patch)
tree5d0f6cbaf755b530f5044f2aee0f37bb7a3b5ff5 /fs
parent17de0a9ff3df8f54f2f47746d118112d4e61d973 (diff)
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 <darrick.wong@oracle.com> Reported-by: Christoph Hellwig <hch@infradead.org> Reviewed-by: Dave Chinner <dchinner@redhat.com> Signed-off-by: Dave Chinner <david@fromorbit.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/xfs/libxfs/xfs_defer.c17
-rw-r--r--fs/xfs/libxfs/xfs_defer.h2
-rw-r--r--fs/xfs/xfs_trace.h2
3 files changed, 6 insertions, 15 deletions
diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c
index 054a2032fdb3..c221d0ecd52e 100644
--- a/fs/xfs/libxfs/xfs_defer.c
+++ b/fs/xfs/libxfs/xfs_defer.c
@@ -194,7 +194,7 @@ xfs_defer_trans_abort(
194 /* Abort intent items. */ 194 /* Abort intent items. */
195 list_for_each_entry(dfp, &dop->dop_pending, dfp_list) { 195 list_for_each_entry(dfp, &dop->dop_pending, dfp_list) {
196 trace_xfs_defer_pending_abort(tp->t_mountp, dfp); 196 trace_xfs_defer_pending_abort(tp->t_mountp, dfp);
197 if (dfp->dfp_committed) 197 if (!dfp->dfp_done)
198 dfp->dfp_type->abort_intent(dfp->dfp_intent); 198 dfp->dfp_type->abort_intent(dfp->dfp_intent);
199 } 199 }
200 200
@@ -290,7 +290,6 @@ xfs_defer_finish(
290 struct xfs_defer_pending *dfp; 290 struct xfs_defer_pending *dfp;
291 struct list_head *li; 291 struct list_head *li;
292 struct list_head *n; 292 struct list_head *n;
293 void *done_item = NULL;
294 void *state; 293 void *state;
295 int error = 0; 294 int error = 0;
296 void (*cleanup_fn)(struct xfs_trans *, void *, int); 295 void (*cleanup_fn)(struct xfs_trans *, void *, int);
@@ -309,19 +308,11 @@ xfs_defer_finish(
309 if (error) 308 if (error)
310 goto out; 309 goto out;
311 310
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
320 /* Log an intent-done item for the first pending item. */ 311 /* Log an intent-done item for the first pending item. */
321 dfp = list_first_entry(&dop->dop_pending, 312 dfp = list_first_entry(&dop->dop_pending,
322 struct xfs_defer_pending, dfp_list); 313 struct xfs_defer_pending, dfp_list);
323 trace_xfs_defer_pending_finish((*tp)->t_mountp, dfp); 314 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,
325 dfp->dfp_count); 316 dfp->dfp_count);
326 cleanup_fn = dfp->dfp_type->finish_cleanup; 317 cleanup_fn = dfp->dfp_type->finish_cleanup;
327 318
@@ -331,7 +322,7 @@ xfs_defer_finish(
331 list_del(li); 322 list_del(li);
332 dfp->dfp_count--; 323 dfp->dfp_count--;
333 error = dfp->dfp_type->finish_item(*tp, dop, li, 324 error = dfp->dfp_type->finish_item(*tp, dop, li,
334 done_item, &state); 325 dfp->dfp_done, &state);
335 if (error) { 326 if (error) {
336 /* 327 /*
337 * Clean up after ourselves and jump out. 328 * Clean up after ourselves and jump out.
@@ -428,8 +419,8 @@ xfs_defer_add(
428 dfp = kmem_alloc(sizeof(struct xfs_defer_pending), 419 dfp = kmem_alloc(sizeof(struct xfs_defer_pending),
429 KM_SLEEP | KM_NOFS); 420 KM_SLEEP | KM_NOFS);
430 dfp->dfp_type = defer_op_types[type]; 421 dfp->dfp_type = defer_op_types[type];
431 dfp->dfp_committed = false;
432 dfp->dfp_intent = NULL; 422 dfp->dfp_intent = NULL;
423 dfp->dfp_done = NULL;
433 dfp->dfp_count = 0; 424 dfp->dfp_count = 0;
434 INIT_LIST_HEAD(&dfp->dfp_work); 425 INIT_LIST_HEAD(&dfp->dfp_work);
435 list_add_tail(&dfp->dfp_list, &dop->dop_intake); 426 list_add_tail(&dfp->dfp_list, &dop->dop_intake);
diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h
index cc3981c48296..e96533d178cf 100644
--- a/fs/xfs/libxfs/xfs_defer.h
+++ b/fs/xfs/libxfs/xfs_defer.h
@@ -30,8 +30,8 @@ struct xfs_defer_op_type;
30struct xfs_defer_pending { 30struct xfs_defer_pending {
31 const struct xfs_defer_op_type *dfp_type; /* function pointers */ 31 const struct xfs_defer_op_type *dfp_type; /* function pointers */
32 struct list_head dfp_list; /* pending items */ 32 struct list_head dfp_list; /* pending items */
33 bool dfp_committed; /* committed trans? */
34 void *dfp_intent; /* log intent item */ 33 void *dfp_intent; /* log intent item */
34 void *dfp_done; /* log done item */
35 struct list_head dfp_work; /* work items */ 35 struct list_head dfp_work; /* work items */
36 unsigned int dfp_count; /* # extent items */ 36 unsigned int dfp_count; /* # extent items */
37}; 37};
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 7e88bec3f359..d303a665dba9 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -2295,7 +2295,7 @@ DECLARE_EVENT_CLASS(xfs_defer_pending_class,
2295 __entry->dev = mp ? mp->m_super->s_dev : 0; 2295 __entry->dev = mp ? mp->m_super->s_dev : 0;
2296 __entry->type = dfp->dfp_type->type; 2296 __entry->type = dfp->dfp_type->type;
2297 __entry->intent = dfp->dfp_intent; 2297 __entry->intent = dfp->dfp_intent;
2298 __entry->committed = dfp->dfp_committed; 2298 __entry->committed = dfp->dfp_done != NULL;
2299 __entry->nr = dfp->dfp_count; 2299 __entry->nr = dfp->dfp_count;
2300 ), 2300 ),
2301 TP_printk("dev %d:%d optype %d intent %p committed %d nr %d\n", 2301 TP_printk("dev %d:%d optype %d intent %p committed %d nr %d\n",