diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/xfs/xfs_log_cil.c | 227 |
1 files changed, 100 insertions, 127 deletions
diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c index b20b15761e9c..c1a3384406fd 100644 --- a/fs/xfs/xfs_log_cil.c +++ b/fs/xfs/xfs_log_cil.c | |||
@@ -111,6 +111,53 @@ xlog_cil_lv_item_format( | |||
111 | } | 111 | } |
112 | 112 | ||
113 | /* | 113 | /* |
114 | * Prepare the log item for insertion into the CIL. Calculate the difference in | ||
115 | * log space and vectors it will consume, and if it is a new item pin it as | ||
116 | * well. | ||
117 | */ | ||
118 | STATIC void | ||
119 | xfs_cil_prepare_item( | ||
120 | struct xlog *log, | ||
121 | struct xfs_log_vec *lv, | ||
122 | struct xfs_log_vec *old_lv, | ||
123 | int *diff_len, | ||
124 | int *diff_iovecs) | ||
125 | { | ||
126 | /* Account for the new LV being passed in */ | ||
127 | if (lv->lv_buf_len != XFS_LOG_VEC_ORDERED) { | ||
128 | *diff_len += lv->lv_buf_len; | ||
129 | *diff_iovecs += lv->lv_niovecs; | ||
130 | } | ||
131 | |||
132 | /* | ||
133 | * If there is no old LV, this is the first time we've seen the item in | ||
134 | * this CIL context and so we need to pin it. If we are replacing the | ||
135 | * old_lv, then remove the space it accounts for and free it. | ||
136 | */ | ||
137 | if (!old_lv) | ||
138 | lv->lv_item->li_ops->iop_pin(lv->lv_item); | ||
139 | else if (old_lv != lv) { | ||
140 | ASSERT(lv->lv_buf_len != XFS_LOG_VEC_ORDERED); | ||
141 | |||
142 | *diff_len -= old_lv->lv_buf_len; | ||
143 | *diff_iovecs -= old_lv->lv_niovecs; | ||
144 | kmem_free(old_lv); | ||
145 | } | ||
146 | |||
147 | /* attach new log vector to log item */ | ||
148 | lv->lv_item->li_lv = lv; | ||
149 | |||
150 | /* | ||
151 | * If this is the first time the item is being committed to the | ||
152 | * CIL, store the sequence number on the log item so we can | ||
153 | * tell in future commits whether this is the first checkpoint | ||
154 | * the item is being committed into. | ||
155 | */ | ||
156 | if (!lv->lv_item->li_seq) | ||
157 | lv->lv_item->li_seq = log->l_cilp->xc_ctx->sequence; | ||
158 | } | ||
159 | |||
160 | /* | ||
114 | * Format log item into a flat buffers | 161 | * Format log item into a flat buffers |
115 | * | 162 | * |
116 | * For delayed logging, we need to hold a formatted buffer containing all the | 163 | * For delayed logging, we need to hold a formatted buffer containing all the |
@@ -136,24 +183,26 @@ xlog_cil_lv_item_format( | |||
136 | * format the regions into the iclog as though they are being formatted | 183 | * format the regions into the iclog as though they are being formatted |
137 | * directly out of the objects themselves. | 184 | * directly out of the objects themselves. |
138 | */ | 185 | */ |
139 | static struct xfs_log_vec * | 186 | static void |
140 | xlog_cil_prepare_log_vecs( | 187 | xlog_cil_insert_format_items( |
141 | struct xfs_trans *tp) | 188 | struct xlog *log, |
189 | struct xfs_trans *tp, | ||
190 | int *diff_len, | ||
191 | int *diff_iovecs) | ||
142 | { | 192 | { |
143 | struct xfs_log_item_desc *lidp; | 193 | struct xfs_log_item_desc *lidp; |
144 | struct xfs_log_vec *prev_lv = NULL; | ||
145 | struct xfs_log_vec *ret_lv = NULL; | ||
146 | 194 | ||
147 | 195 | ||
148 | /* Bail out if we didn't find a log item. */ | 196 | /* Bail out if we didn't find a log item. */ |
149 | if (list_empty(&tp->t_items)) { | 197 | if (list_empty(&tp->t_items)) { |
150 | ASSERT(0); | 198 | ASSERT(0); |
151 | return NULL; | 199 | return; |
152 | } | 200 | } |
153 | 201 | ||
154 | list_for_each_entry(lidp, &tp->t_items, lid_trans) { | 202 | list_for_each_entry(lidp, &tp->t_items, lid_trans) { |
155 | struct xfs_log_item *lip = lidp->lid_item; | 203 | struct xfs_log_item *lip = lidp->lid_item; |
156 | struct xfs_log_vec *lv; | 204 | struct xfs_log_vec *lv; |
205 | struct xfs_log_vec *old_lv; | ||
157 | int niovecs = 0; | 206 | int niovecs = 0; |
158 | int nbytes = 0; | 207 | int nbytes = 0; |
159 | int buf_size; | 208 | int buf_size; |
@@ -181,6 +230,9 @@ xlog_cil_prepare_log_vecs( | |||
181 | nbytes = 0; | 230 | nbytes = 0; |
182 | } | 231 | } |
183 | 232 | ||
233 | /* grab the old item if it exists for reservation accounting */ | ||
234 | old_lv = lip->li_lv; | ||
235 | |||
184 | /* calc buffer size */ | 236 | /* calc buffer size */ |
185 | buf_size = sizeof(struct xfs_log_vec) + nbytes + | 237 | buf_size = sizeof(struct xfs_log_vec) + nbytes + |
186 | niovecs * sizeof(struct xfs_log_iovec); | 238 | niovecs * sizeof(struct xfs_log_iovec); |
@@ -194,9 +246,17 @@ xlog_cil_prepare_log_vecs( | |||
194 | if (ordered) | 246 | if (ordered) |
195 | goto insert; | 247 | goto insert; |
196 | 248 | ||
249 | /* | ||
250 | * set the item up as though it is a new insertion so | ||
251 | * that the space reservation accounting is correct. | ||
252 | */ | ||
253 | *diff_iovecs -= lv->lv_niovecs; | ||
254 | *diff_len -= lv->lv_buf_len; | ||
255 | |||
197 | /* Ensure the lv is set up according to ->iop_size */ | 256 | /* Ensure the lv is set up according to ->iop_size */ |
198 | lv->lv_niovecs = niovecs; | 257 | lv->lv_niovecs = niovecs; |
199 | lv->lv_buf = (char *)lv + buf_size - nbytes; | 258 | lv->lv_buf = (char *)lv + buf_size - nbytes; |
259 | |||
200 | lv->lv_buf_len = xlog_cil_lv_item_format(lip, lv); | 260 | lv->lv_buf_len = xlog_cil_lv_item_format(lip, lv); |
201 | goto insert; | 261 | goto insert; |
202 | } | 262 | } |
@@ -222,74 +282,8 @@ xlog_cil_prepare_log_vecs( | |||
222 | lv->lv_buf_len = xlog_cil_lv_item_format(lip, lv); | 282 | lv->lv_buf_len = xlog_cil_lv_item_format(lip, lv); |
223 | insert: | 283 | insert: |
224 | ASSERT(lv->lv_buf_len <= nbytes); | 284 | ASSERT(lv->lv_buf_len <= nbytes); |
225 | if (!ret_lv) | 285 | xfs_cil_prepare_item(log, lv, old_lv, diff_len, diff_iovecs); |
226 | ret_lv = lv; | ||
227 | else | ||
228 | prev_lv->lv_next = lv; | ||
229 | prev_lv = lv; | ||
230 | } | ||
231 | |||
232 | return ret_lv; | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * Prepare the log item for insertion into the CIL. Calculate the difference in | ||
237 | * log space and vectors it will consume, and if it is a new item pin it as | ||
238 | * well. | ||
239 | */ | ||
240 | STATIC void | ||
241 | xfs_cil_prepare_item( | ||
242 | struct xlog *log, | ||
243 | struct xfs_log_vec *lv, | ||
244 | int *len, | ||
245 | int *diff_iovecs) | ||
246 | { | ||
247 | struct xfs_log_vec *old = lv->lv_item->li_lv; | ||
248 | |||
249 | if (!old) { | ||
250 | /* new lv, must pin the log item */ | ||
251 | ASSERT(!lv->lv_item->li_lv); | ||
252 | |||
253 | if (lv->lv_buf_len != XFS_LOG_VEC_ORDERED) { | ||
254 | *len += lv->lv_buf_len; | ||
255 | *diff_iovecs += lv->lv_niovecs; | ||
256 | } | ||
257 | lv->lv_item->li_ops->iop_pin(lv->lv_item); | ||
258 | |||
259 | } else if (old != lv) { | ||
260 | /* existing lv on log item, space used is a delta */ | ||
261 | ASSERT((old->lv_buf && old->lv_buf_len && old->lv_niovecs) || | ||
262 | old->lv_buf_len == XFS_LOG_VEC_ORDERED); | ||
263 | |||
264 | /* | ||
265 | * If the new item is ordered, keep the old one that is already | ||
266 | * tracking dirty or ordered regions | ||
267 | */ | ||
268 | if (lv->lv_buf_len == XFS_LOG_VEC_ORDERED) { | ||
269 | ASSERT(!lv->lv_buf); | ||
270 | kmem_free(lv); | ||
271 | return; | ||
272 | } | ||
273 | |||
274 | *len += lv->lv_buf_len - old->lv_buf_len; | ||
275 | *diff_iovecs += lv->lv_niovecs - old->lv_niovecs; | ||
276 | kmem_free(old); | ||
277 | } else { | ||
278 | /* re-used lv */ | ||
279 | /* XXX: can't account for len/diff_iovecs yet */ | ||
280 | } | 286 | } |
281 | |||
282 | /* attach new log vector to log item */ | ||
283 | lv->lv_item->li_lv = lv; | ||
284 | |||
285 | /* | ||
286 | * If this is the first time the item is being committed to the | ||
287 | * CIL, store the sequence number on the log item so we can | ||
288 | * tell in future commits whether this is the first checkpoint | ||
289 | * the item is being committed into. | ||
290 | */ | ||
291 | if (!lv->lv_item->li_seq) | ||
292 | lv->lv_item->li_seq = log->l_cilp->xc_ctx->sequence; | ||
293 | } | 287 | } |
294 | 288 | ||
295 | /* | 289 | /* |
@@ -302,53 +296,47 @@ xfs_cil_prepare_item( | |||
302 | static void | 296 | static void |
303 | xlog_cil_insert_items( | 297 | xlog_cil_insert_items( |
304 | struct xlog *log, | 298 | struct xlog *log, |
305 | struct xfs_log_vec *log_vector, | 299 | struct xfs_trans *tp) |
306 | struct xlog_ticket *ticket) | ||
307 | { | 300 | { |
308 | struct xfs_cil *cil = log->l_cilp; | 301 | struct xfs_cil *cil = log->l_cilp; |
309 | struct xfs_cil_ctx *ctx = cil->xc_ctx; | 302 | struct xfs_cil_ctx *ctx = cil->xc_ctx; |
310 | struct xfs_log_vec *lv; | 303 | struct xfs_log_item_desc *lidp; |
311 | int len = 0; | 304 | int len = 0; |
312 | int diff_iovecs = 0; | 305 | int diff_iovecs = 0; |
313 | int iclog_space; | 306 | int iclog_space; |
314 | 307 | ||
315 | ASSERT(log_vector); | 308 | ASSERT(tp); |
316 | 309 | ||
317 | /* | 310 | /* |
318 | * Do all the accounting aggregation and switching of log vectors | ||
319 | * around in a separate loop to the insertion of items into the CIL. | ||
320 | * Then we can do a separate loop to update the CIL within a single | ||
321 | * lock/unlock pair. This reduces the number of round trips on the CIL | ||
322 | * lock from O(nr_logvectors) to O(1) and greatly reduces the overall | ||
323 | * hold time for the transaction commit. | ||
324 | * | ||
325 | * If this is the first time the item is being placed into the CIL in | ||
326 | * this context, pin it so it can't be written to disk until the CIL is | ||
327 | * flushed to the iclog and the iclog written to disk. | ||
328 | * | ||
329 | * We can do this safely because the context can't checkpoint until we | 311 | * We can do this safely because the context can't checkpoint until we |
330 | * are done so it doesn't matter exactly how we update the CIL. | 312 | * are done so it doesn't matter exactly how we update the CIL. |
331 | */ | 313 | */ |
314 | xlog_cil_insert_format_items(log, tp, &len, &diff_iovecs); | ||
315 | |||
316 | /* | ||
317 | * Now (re-)position everything modified at the tail of the CIL. | ||
318 | * We do this here so we only need to take the CIL lock once during | ||
319 | * the transaction commit. | ||
320 | */ | ||
332 | spin_lock(&cil->xc_cil_lock); | 321 | spin_lock(&cil->xc_cil_lock); |
333 | for (lv = log_vector; lv; ) { | 322 | list_for_each_entry(lidp, &tp->t_items, lid_trans) { |
334 | struct xfs_log_vec *next = lv->lv_next; | 323 | struct xfs_log_item *lip = lidp->lid_item; |
335 | 324 | ||
336 | ASSERT(lv->lv_item->li_lv || list_empty(&lv->lv_item->li_cil)); | 325 | /* Skip items which aren't dirty in this transaction. */ |
337 | lv->lv_next = NULL; | 326 | if (!(lidp->lid_flags & XFS_LID_DIRTY)) |
327 | continue; | ||
338 | 328 | ||
339 | /* | 329 | list_move_tail(&lip->li_cil, &cil->xc_cil); |
340 | * xfs_cil_prepare_item() may free the lv, so move the item on | ||
341 | * the CIL first. | ||
342 | */ | ||
343 | list_move_tail(&lv->lv_item->li_cil, &cil->xc_cil); | ||
344 | xfs_cil_prepare_item(log, lv, &len, &diff_iovecs); | ||
345 | lv = next; | ||
346 | } | 330 | } |
347 | 331 | ||
348 | /* account for space used by new iovec headers */ | 332 | /* account for space used by new iovec headers */ |
349 | len += diff_iovecs * sizeof(xlog_op_header_t); | 333 | len += diff_iovecs * sizeof(xlog_op_header_t); |
350 | ctx->nvecs += diff_iovecs; | 334 | ctx->nvecs += diff_iovecs; |
351 | 335 | ||
336 | /* attach the transaction to the CIL if it has any busy extents */ | ||
337 | if (!list_empty(&tp->t_busy)) | ||
338 | list_splice_init(&tp->t_busy, &ctx->busy_extents); | ||
339 | |||
352 | /* | 340 | /* |
353 | * Now transfer enough transaction reservation to the context ticket | 341 | * Now transfer enough transaction reservation to the context ticket |
354 | * for the checkpoint. The context ticket is special - the unit | 342 | * for the checkpoint. The context ticket is special - the unit |
@@ -357,10 +345,8 @@ xlog_cil_insert_items( | |||
357 | * during the transaction commit. | 345 | * during the transaction commit. |
358 | */ | 346 | */ |
359 | if (ctx->ticket->t_curr_res == 0) { | 347 | if (ctx->ticket->t_curr_res == 0) { |
360 | /* first commit in checkpoint, steal the header reservation */ | ||
361 | ASSERT(ticket->t_curr_res >= ctx->ticket->t_unit_res + len); | ||
362 | ctx->ticket->t_curr_res = ctx->ticket->t_unit_res; | 348 | ctx->ticket->t_curr_res = ctx->ticket->t_unit_res; |
363 | ticket->t_curr_res -= ctx->ticket->t_unit_res; | 349 | tp->t_ticket->t_curr_res -= ctx->ticket->t_unit_res; |
364 | } | 350 | } |
365 | 351 | ||
366 | /* do we need space for more log record headers? */ | 352 | /* do we need space for more log record headers? */ |
@@ -374,10 +360,10 @@ xlog_cil_insert_items( | |||
374 | hdrs *= log->l_iclog_hsize + sizeof(struct xlog_op_header); | 360 | hdrs *= log->l_iclog_hsize + sizeof(struct xlog_op_header); |
375 | ctx->ticket->t_unit_res += hdrs; | 361 | ctx->ticket->t_unit_res += hdrs; |
376 | ctx->ticket->t_curr_res += hdrs; | 362 | ctx->ticket->t_curr_res += hdrs; |
377 | ticket->t_curr_res -= hdrs; | 363 | tp->t_ticket->t_curr_res -= hdrs; |
378 | ASSERT(ticket->t_curr_res >= len); | 364 | ASSERT(tp->t_ticket->t_curr_res >= len); |
379 | } | 365 | } |
380 | ticket->t_curr_res -= len; | 366 | tp->t_ticket->t_curr_res -= len; |
381 | ctx->space_used += len; | 367 | ctx->space_used += len; |
382 | 368 | ||
383 | spin_unlock(&cil->xc_cil_lock); | 369 | spin_unlock(&cil->xc_cil_lock); |
@@ -746,38 +732,25 @@ xfs_log_commit_cil( | |||
746 | int flags) | 732 | int flags) |
747 | { | 733 | { |
748 | struct xlog *log = mp->m_log; | 734 | struct xlog *log = mp->m_log; |
735 | struct xfs_cil *cil = log->l_cilp; | ||
749 | int log_flags = 0; | 736 | int log_flags = 0; |
750 | struct xfs_log_vec *log_vector; | ||
751 | 737 | ||
752 | if (flags & XFS_TRANS_RELEASE_LOG_RES) | 738 | if (flags & XFS_TRANS_RELEASE_LOG_RES) |
753 | log_flags = XFS_LOG_REL_PERM_RESERV; | 739 | log_flags = XFS_LOG_REL_PERM_RESERV; |
754 | 740 | ||
755 | /* lock out background commit */ | 741 | /* lock out background commit */ |
756 | down_read(&log->l_cilp->xc_ctx_lock); | 742 | down_read(&cil->xc_ctx_lock); |
757 | 743 | ||
758 | log_vector = xlog_cil_prepare_log_vecs(tp); | 744 | xlog_cil_insert_items(log, tp); |
759 | if (!log_vector) | ||
760 | return ENOMEM; | ||
761 | |||
762 | if (commit_lsn) | ||
763 | *commit_lsn = log->l_cilp->xc_ctx->sequence; | ||
764 | |||
765 | /* xlog_cil_insert_items() destroys log_vector list */ | ||
766 | xlog_cil_insert_items(log, log_vector, tp->t_ticket); | ||
767 | 745 | ||
768 | /* check we didn't blow the reservation */ | 746 | /* check we didn't blow the reservation */ |
769 | if (tp->t_ticket->t_curr_res < 0) | 747 | if (tp->t_ticket->t_curr_res < 0) |
770 | xlog_print_tic_res(log->l_mp, tp->t_ticket); | 748 | xlog_print_tic_res(mp, tp->t_ticket); |
771 | 749 | ||
772 | /* attach the transaction to the CIL if it has any busy extents */ | 750 | tp->t_commit_lsn = cil->xc_ctx->sequence; |
773 | if (!list_empty(&tp->t_busy)) { | 751 | if (commit_lsn) |
774 | spin_lock(&log->l_cilp->xc_cil_lock); | 752 | *commit_lsn = tp->t_commit_lsn; |
775 | list_splice_init(&tp->t_busy, | ||
776 | &log->l_cilp->xc_ctx->busy_extents); | ||
777 | spin_unlock(&log->l_cilp->xc_cil_lock); | ||
778 | } | ||
779 | 753 | ||
780 | tp->t_commit_lsn = *commit_lsn; | ||
781 | xfs_log_done(mp, tp->t_ticket, NULL, log_flags); | 754 | xfs_log_done(mp, tp->t_ticket, NULL, log_flags); |
782 | xfs_trans_unreserve_and_mod_sb(tp); | 755 | xfs_trans_unreserve_and_mod_sb(tp); |
783 | 756 | ||
@@ -792,11 +765,11 @@ xfs_log_commit_cil( | |||
792 | * the log items. This affects (at least) processing of stale buffers, | 765 | * the log items. This affects (at least) processing of stale buffers, |
793 | * inodes and EFIs. | 766 | * inodes and EFIs. |
794 | */ | 767 | */ |
795 | xfs_trans_free_items(tp, *commit_lsn, 0); | 768 | xfs_trans_free_items(tp, tp->t_commit_lsn, 0); |
796 | 769 | ||
797 | xlog_cil_push_background(log); | 770 | xlog_cil_push_background(log); |
798 | 771 | ||
799 | up_read(&log->l_cilp->xc_ctx_lock); | 772 | up_read(&cil->xc_ctx_lock); |
800 | return 0; | 773 | return 0; |
801 | } | 774 | } |
802 | 775 | ||