diff options
Diffstat (limited to 'fs/xfs/xfs_reflink.c')
-rw-r--r-- | fs/xfs/xfs_reflink.c | 127 |
1 files changed, 77 insertions, 50 deletions
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 38f405415b88..d60d0eeed7b9 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c | |||
@@ -352,6 +352,47 @@ xfs_reflink_convert_cow( | |||
352 | return error; | 352 | return error; |
353 | } | 353 | } |
354 | 354 | ||
355 | /* | ||
356 | * Find the extent that maps the given range in the COW fork. Even if the extent | ||
357 | * is not shared we might have a preallocation for it in the COW fork. If so we | ||
358 | * use it that rather than trigger a new allocation. | ||
359 | */ | ||
360 | static int | ||
361 | xfs_find_trim_cow_extent( | ||
362 | struct xfs_inode *ip, | ||
363 | struct xfs_bmbt_irec *imap, | ||
364 | bool *shared, | ||
365 | bool *found) | ||
366 | { | ||
367 | xfs_fileoff_t offset_fsb = imap->br_startoff; | ||
368 | xfs_filblks_t count_fsb = imap->br_blockcount; | ||
369 | struct xfs_iext_cursor icur; | ||
370 | struct xfs_bmbt_irec got; | ||
371 | bool trimmed; | ||
372 | |||
373 | *found = false; | ||
374 | |||
375 | /* | ||
376 | * If we don't find an overlapping extent, trim the range we need to | ||
377 | * allocate to fit the hole we found. | ||
378 | */ | ||
379 | if (!xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &icur, &got) || | ||
380 | got.br_startoff > offset_fsb) | ||
381 | return xfs_reflink_trim_around_shared(ip, imap, shared, &trimmed); | ||
382 | |||
383 | *shared = true; | ||
384 | if (isnullstartblock(got.br_startblock)) { | ||
385 | xfs_trim_extent(imap, got.br_startoff, got.br_blockcount); | ||
386 | return 0; | ||
387 | } | ||
388 | |||
389 | /* real extent found - no need to allocate */ | ||
390 | xfs_trim_extent(&got, offset_fsb, count_fsb); | ||
391 | *imap = got; | ||
392 | *found = true; | ||
393 | return 0; | ||
394 | } | ||
395 | |||
355 | /* Allocate all CoW reservations covering a range of blocks in a file. */ | 396 | /* Allocate all CoW reservations covering a range of blocks in a file. */ |
356 | int | 397 | int |
357 | xfs_reflink_allocate_cow( | 398 | xfs_reflink_allocate_cow( |
@@ -363,78 +404,64 @@ xfs_reflink_allocate_cow( | |||
363 | struct xfs_mount *mp = ip->i_mount; | 404 | struct xfs_mount *mp = ip->i_mount; |
364 | xfs_fileoff_t offset_fsb = imap->br_startoff; | 405 | xfs_fileoff_t offset_fsb = imap->br_startoff; |
365 | xfs_filblks_t count_fsb = imap->br_blockcount; | 406 | xfs_filblks_t count_fsb = imap->br_blockcount; |
366 | struct xfs_bmbt_irec got; | 407 | struct xfs_trans *tp; |
367 | struct xfs_trans *tp = NULL; | ||
368 | int nimaps, error = 0; | 408 | int nimaps, error = 0; |
369 | bool trimmed; | 409 | bool found; |
370 | xfs_filblks_t resaligned; | 410 | xfs_filblks_t resaligned; |
371 | xfs_extlen_t resblks = 0; | 411 | xfs_extlen_t resblks = 0; |
372 | struct xfs_iext_cursor icur; | ||
373 | 412 | ||
374 | retry: | ||
375 | ASSERT(xfs_is_reflink_inode(ip)); | ||
376 | ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); | 413 | ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); |
414 | ASSERT(xfs_is_reflink_inode(ip)); | ||
377 | 415 | ||
378 | /* | 416 | error = xfs_find_trim_cow_extent(ip, imap, shared, &found); |
379 | * Even if the extent is not shared we might have a preallocation for | 417 | if (error || !*shared) |
380 | * it in the COW fork. If so use it. | 418 | return error; |
381 | */ | 419 | if (found) |
382 | if (xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &icur, &got) && | 420 | goto convert; |
383 | got.br_startoff <= offset_fsb) { | ||
384 | *shared = true; | ||
385 | |||
386 | /* If we have a real allocation in the COW fork we're done. */ | ||
387 | if (!isnullstartblock(got.br_startblock)) { | ||
388 | xfs_trim_extent(&got, offset_fsb, count_fsb); | ||
389 | *imap = got; | ||
390 | goto convert; | ||
391 | } | ||
392 | 421 | ||
393 | xfs_trim_extent(imap, got.br_startoff, got.br_blockcount); | 422 | resaligned = xfs_aligned_fsb_count(imap->br_startoff, |
394 | } else { | 423 | imap->br_blockcount, xfs_get_cowextsz_hint(ip)); |
395 | error = xfs_reflink_trim_around_shared(ip, imap, shared, &trimmed); | 424 | resblks = XFS_DIOSTRAT_SPACE_RES(mp, resaligned); |
396 | if (error || !*shared) | ||
397 | goto out; | ||
398 | } | ||
399 | 425 | ||
400 | if (!tp) { | 426 | xfs_iunlock(ip, *lockmode); |
401 | resaligned = xfs_aligned_fsb_count(imap->br_startoff, | 427 | error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp); |
402 | imap->br_blockcount, xfs_get_cowextsz_hint(ip)); | 428 | *lockmode = XFS_ILOCK_EXCL; |
403 | resblks = XFS_DIOSTRAT_SPACE_RES(mp, resaligned); | 429 | xfs_ilock(ip, *lockmode); |
404 | 430 | ||
405 | xfs_iunlock(ip, *lockmode); | 431 | if (error) |
406 | error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp); | 432 | return error; |
407 | *lockmode = XFS_ILOCK_EXCL; | ||
408 | xfs_ilock(ip, *lockmode); | ||
409 | 433 | ||
410 | if (error) | 434 | error = xfs_qm_dqattach_locked(ip, false); |
411 | return error; | 435 | if (error) |
436 | goto out_trans_cancel; | ||
412 | 437 | ||
413 | error = xfs_qm_dqattach_locked(ip, false); | 438 | /* |
414 | if (error) | 439 | * Check for an overlapping extent again now that we dropped the ilock. |
415 | goto out; | 440 | */ |
416 | goto retry; | 441 | error = xfs_find_trim_cow_extent(ip, imap, shared, &found); |
442 | if (error || !*shared) | ||
443 | goto out_trans_cancel; | ||
444 | if (found) { | ||
445 | xfs_trans_cancel(tp); | ||
446 | goto convert; | ||
417 | } | 447 | } |
418 | 448 | ||
419 | error = xfs_trans_reserve_quota_nblks(tp, ip, resblks, 0, | 449 | error = xfs_trans_reserve_quota_nblks(tp, ip, resblks, 0, |
420 | XFS_QMOPT_RES_REGBLKS); | 450 | XFS_QMOPT_RES_REGBLKS); |
421 | if (error) | 451 | if (error) |
422 | goto out; | 452 | goto out_trans_cancel; |
423 | 453 | ||
424 | xfs_trans_ijoin(tp, ip, 0); | 454 | xfs_trans_ijoin(tp, ip, 0); |
425 | 455 | ||
426 | nimaps = 1; | ||
427 | |||
428 | /* Allocate the entire reservation as unwritten blocks. */ | 456 | /* Allocate the entire reservation as unwritten blocks. */ |
457 | nimaps = 1; | ||
429 | error = xfs_bmapi_write(tp, ip, imap->br_startoff, imap->br_blockcount, | 458 | error = xfs_bmapi_write(tp, ip, imap->br_startoff, imap->br_blockcount, |
430 | XFS_BMAPI_COWFORK | XFS_BMAPI_PREALLOC, | 459 | XFS_BMAPI_COWFORK | XFS_BMAPI_PREALLOC, |
431 | resblks, imap, &nimaps); | 460 | resblks, imap, &nimaps); |
432 | if (error) | 461 | if (error) |
433 | goto out_trans_cancel; | 462 | goto out_unreserve; |
434 | 463 | ||
435 | xfs_inode_set_cowblocks_tag(ip); | 464 | xfs_inode_set_cowblocks_tag(ip); |
436 | |||
437 | /* Finish up. */ | ||
438 | error = xfs_trans_commit(tp); | 465 | error = xfs_trans_commit(tp); |
439 | if (error) | 466 | if (error) |
440 | return error; | 467 | return error; |
@@ -447,12 +474,12 @@ retry: | |||
447 | return -ENOSPC; | 474 | return -ENOSPC; |
448 | convert: | 475 | convert: |
449 | return xfs_reflink_convert_cow_extent(ip, imap, offset_fsb, count_fsb); | 476 | return xfs_reflink_convert_cow_extent(ip, imap, offset_fsb, count_fsb); |
450 | out_trans_cancel: | 477 | |
478 | out_unreserve: | ||
451 | xfs_trans_unreserve_quota_nblks(tp, ip, (long)resblks, 0, | 479 | xfs_trans_unreserve_quota_nblks(tp, ip, (long)resblks, 0, |
452 | XFS_QMOPT_RES_REGBLKS); | 480 | XFS_QMOPT_RES_REGBLKS); |
453 | out: | 481 | out_trans_cancel: |
454 | if (tp) | 482 | xfs_trans_cancel(tp); |
455 | xfs_trans_cancel(tp); | ||
456 | return error; | 483 | return error; |
457 | } | 484 | } |
458 | 485 | ||