diff options
-rw-r--r-- | fs/xfs/libxfs/xfs_defer.c | 79 |
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 | ||
146 | static const struct xfs_defer_op_type *defer_op_types[XFS_DEFER_OPS_TYPE_MAX]; | 186 | static 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); |