aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2016-09-18 20:26:25 -0400
committerDave Chinner <david@fromorbit.com>2016-09-18 20:26:25 -0400
commit385d655861d221bb43ae69a9cfa9adbefe31ad00 (patch)
treedfca3c3c000bed9dcabddc6fea374aa8611f27f2
parentc611cc0360cd924448c23ccd70ce8be703fcb4a6 (diff)
xfs: defer should allow ->finish_item to request a new transaction
When xfs_defer_finish calls ->finish_item, it's possible that (refcount) won't be able to finish all the work in a single transaction. When this happens, the ->finish_item handler should shorten the log done item's list count, update the work item to reflect where work should continue, and return -EAGAIN so that defer_finish knows to retain the pending item on the pending list, roll the transaction, and restart processing where we left off. Plumb in the code and document how this mechanism is supposed to work. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
-rw-r--r--fs/xfs/libxfs/xfs_defer.c79
1 files changed, 72 insertions, 7 deletions
diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c
index c221d0ecd52e..613c5cf19436 100644
--- a/fs/xfs/libxfs/xfs_defer.c
+++ b/fs/xfs/libxfs/xfs_defer.c
@@ -81,6 +81,10 @@
81 * - For each work item attached to the log intent item, 81 * - For each work item attached to the log intent item,
82 * * Perform the described action. 82 * * Perform the described action.
83 * * Attach the work item to the log done item. 83 * * Attach the work item to the log done item.
84 * * If the result of doing the work was -EAGAIN, ->finish work
85 * wants a new transaction. See the "Requesting a Fresh
86 * Transaction while Finishing Deferred Work" section below for
87 * details.
84 * 88 *
85 * The key here is that we must log an intent item for all pending 89 * The key here is that we must log an intent item for all pending
86 * work items every time we roll the transaction, and that we must log 90 * work items every time we roll the transaction, and that we must log
@@ -88,6 +92,34 @@
88 * we can perform complex remapping operations, chaining intent items 92 * we can perform complex remapping operations, chaining intent items
89 * as needed. 93 * as needed.
90 * 94 *
95 * Requesting a Fresh Transaction while Finishing Deferred Work
96 *
97 * If ->finish_item decides that it needs a fresh transaction to
98 * finish the work, it must ask its caller (xfs_defer_finish) for a
99 * continuation. The most likely cause of this circumstance are the
100 * refcount adjust functions deciding that they've logged enough items
101 * to be at risk of exceeding the transaction reservation.
102 *
103 * To get a fresh transaction, we want to log the existing log done
104 * item to prevent the log intent item from replaying, immediately log
105 * a new log intent item with the unfinished work items, roll the
106 * transaction, and re-call ->finish_item wherever it left off. The
107 * log done item and the new log intent item must be in the same
108 * transaction or atomicity cannot be guaranteed; defer_finish ensures
109 * that this happens.
110 *
111 * This requires some coordination between ->finish_item and
112 * defer_finish. Upon deciding to request a new transaction,
113 * ->finish_item should update the current work item to reflect the
114 * unfinished work. Next, it should reset the log done item's list
115 * count to the number of items finished, and return -EAGAIN.
116 * defer_finish sees the -EAGAIN, logs the new log intent item
117 * with the remaining work items, and leaves the xfs_defer_pending
118 * item at the head of the dop_work queue. Then it rolls the
119 * transaction and picks up processing where it left off. It is
120 * required that ->finish_item must be careful to leave enough
121 * transaction reservation to fit the new log intent item.
122 *
91 * This is an example of remapping the extent (E, E+B) into file X at 123 * This is an example of remapping the extent (E, E+B) into file X at
92 * offset A and dealing with the extent (C, C+B) already being mapped 124 * offset A and dealing with the extent (C, C+B) already being mapped
93 * there: 125 * there:
@@ -104,21 +136,26 @@
104 * | Intent to add rmap (X, E, A, B) | 136 * | Intent to add rmap (X, E, A, B) |
105 * +-------------------------------------------------+ 137 * +-------------------------------------------------+
106 * | Reduce refcount for extent (C, B) | t2 138 * | Reduce refcount for extent (C, B) | t2
107 * | Done reducing refcount for extent (C, B) | 139 * | Done reducing refcount for extent (C, 9) |
140 * | Intent to reduce refcount for extent (C+9, B-9) |
141 * | (ran out of space after 9 refcount updates) |
142 * +-------------------------------------------------+
143 * | Reduce refcount for extent (C+9, B+9) | t3
144 * | Done reducing refcount for extent (C+9, B-9) |
108 * | Increase refcount for extent (E, B) | 145 * | Increase refcount for extent (E, B) |
109 * | Done increasing refcount for extent (E, B) | 146 * | Done increasing refcount for extent (E, B) |
110 * | Intent to free extent (C, B) | 147 * | Intent to free extent (C, B) |
111 * | Intent to free extent (F, 1) (refcountbt block) | 148 * | Intent to free extent (F, 1) (refcountbt block) |
112 * | Intent to remove rmap (F, 1, REFC) | 149 * | Intent to remove rmap (F, 1, REFC) |
113 * +-------------------------------------------------+ 150 * +-------------------------------------------------+
114 * | Remove rmap (X, C, A, B) | t3 151 * | Remove rmap (X, C, A, B) | t4
115 * | Done removing rmap (X, C, A, B) | 152 * | Done removing rmap (X, C, A, B) |
116 * | Add rmap (X, E, A, B) | 153 * | Add rmap (X, E, A, B) |
117 * | Done adding rmap (X, E, A, B) | 154 * | Done adding rmap (X, E, A, B) |
118 * | Remove rmap (F, 1, REFC) | 155 * | Remove rmap (F, 1, REFC) |
119 * | Done removing rmap (F, 1, REFC) | 156 * | Done removing rmap (F, 1, REFC) |
120 * +-------------------------------------------------+ 157 * +-------------------------------------------------+
121 * | Free extent (C, B) | t4 158 * | Free extent (C, B) | t5
122 * | Done freeing extent (C, B) | 159 * | Done freeing extent (C, B) |
123 * | Free extent (D, 1) | 160 * | Free extent (D, 1) |
124 * | Done freeing extent (D, 1) | 161 * | Done freeing extent (D, 1) |
@@ -141,6 +178,9 @@
141 * - Intent to free extent (C, B) 178 * - Intent to free extent (C, B)
142 * - Intent to free extent (F, 1) (refcountbt block) 179 * - Intent to free extent (F, 1) (refcountbt block)
143 * - Intent to remove rmap (F, 1, REFC) 180 * - Intent to remove rmap (F, 1, REFC)
181 *
182 * Note that the continuation requested between t2 and t3 is likely to
183 * reoccur.
144 */ 184 */
145 185
146static const struct xfs_defer_op_type *defer_op_types[XFS_DEFER_OPS_TYPE_MAX]; 186static const struct xfs_defer_op_type *defer_op_types[XFS_DEFER_OPS_TYPE_MAX];
@@ -323,7 +363,16 @@ xfs_defer_finish(
323 dfp->dfp_count--; 363 dfp->dfp_count--;
324 error = dfp->dfp_type->finish_item(*tp, dop, li, 364 error = dfp->dfp_type->finish_item(*tp, dop, li,
325 dfp->dfp_done, &state); 365 dfp->dfp_done, &state);
326 if (error) { 366 if (error == -EAGAIN) {
367 /*
368 * Caller wants a fresh transaction;
369 * put the work item back on the list
370 * and jump out.
371 */
372 list_add(li, &dfp->dfp_work);
373 dfp->dfp_count++;
374 break;
375 } else if (error) {
327 /* 376 /*
328 * Clean up after ourselves and jump out. 377 * Clean up after ourselves and jump out.
329 * xfs_defer_cancel will take care of freeing 378 * xfs_defer_cancel will take care of freeing
@@ -335,9 +384,25 @@ xfs_defer_finish(
335 goto out; 384 goto out;
336 } 385 }
337 } 386 }
338 /* Done with the dfp, free it. */ 387 if (error == -EAGAIN) {
339 list_del(&dfp->dfp_list); 388 /*
340 kmem_free(dfp); 389 * Caller wants a fresh transaction, so log a
390 * new log intent item to replace the old one
391 * and roll the transaction. See "Requesting
392 * a Fresh Transaction while Finishing
393 * Deferred Work" above.
394 */
395 dfp->dfp_intent = dfp->dfp_type->create_intent(*tp,
396 dfp->dfp_count);
397 dfp->dfp_done = NULL;
398 list_for_each(li, &dfp->dfp_work)
399 dfp->dfp_type->log_item(*tp, dfp->dfp_intent,
400 li);
401 } else {
402 /* Done with the dfp, free it. */
403 list_del(&dfp->dfp_list);
404 kmem_free(dfp);
405 }
341 406
342 if (cleanup_fn) 407 if (cleanup_fn)
343 cleanup_fn(*tp, state, error); 408 cleanup_fn(*tp, state, error);