aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);