diff options
author | Dave Chinner <david@fromorbit.com> | 2014-09-23 01:51:14 -0400 |
---|---|---|
committer | Dave Chinner <david@fromorbit.com> | 2014-09-23 01:51:14 -0400 |
commit | f6d31f4b0462898896ba68e491662958ce37d095 (patch) | |
tree | 6655823e16402769f61fc20edb730b9c7c84a82a /fs/xfs | |
parent | a4241aebe924136d6838fd516da6daa727fcd728 (diff) | |
parent | 8b5279e33f241a074a9c8649bba8f77a2167b798 (diff) |
Merge branch 'xfs-shift-extents-rework' into for-next
Diffstat (limited to 'fs/xfs')
-rw-r--r-- | fs/xfs/libxfs/xfs_bmap.c | 365 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_bmap.h | 7 | ||||
-rw-r--r-- | fs/xfs/xfs_aops.c | 16 | ||||
-rw-r--r-- | fs/xfs/xfs_bmap_util.c | 54 |
4 files changed, 289 insertions, 153 deletions
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index 86df952d3e24..79c981984dca 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c | |||
@@ -5404,22 +5404,223 @@ error0: | |||
5404 | } | 5404 | } |
5405 | 5405 | ||
5406 | /* | 5406 | /* |
5407 | * Determine whether an extent shift can be accomplished by a merge with the | ||
5408 | * extent that precedes the target hole of the shift. | ||
5409 | */ | ||
5410 | STATIC bool | ||
5411 | xfs_bmse_can_merge( | ||
5412 | struct xfs_bmbt_irec *left, /* preceding extent */ | ||
5413 | struct xfs_bmbt_irec *got, /* current extent to shift */ | ||
5414 | xfs_fileoff_t shift) /* shift fsb */ | ||
5415 | { | ||
5416 | xfs_fileoff_t startoff; | ||
5417 | |||
5418 | startoff = got->br_startoff - shift; | ||
5419 | |||
5420 | /* | ||
5421 | * The extent, once shifted, must be adjacent in-file and on-disk with | ||
5422 | * the preceding extent. | ||
5423 | */ | ||
5424 | if ((left->br_startoff + left->br_blockcount != startoff) || | ||
5425 | (left->br_startblock + left->br_blockcount != got->br_startblock) || | ||
5426 | (left->br_state != got->br_state) || | ||
5427 | (left->br_blockcount + got->br_blockcount > MAXEXTLEN)) | ||
5428 | return false; | ||
5429 | |||
5430 | return true; | ||
5431 | } | ||
5432 | |||
5433 | /* | ||
5434 | * A bmap extent shift adjusts the file offset of an extent to fill a preceding | ||
5435 | * hole in the file. If an extent shift would result in the extent being fully | ||
5436 | * adjacent to the extent that currently precedes the hole, we can merge with | ||
5437 | * the preceding extent rather than do the shift. | ||
5438 | * | ||
5439 | * This function assumes the caller has verified a shift-by-merge is possible | ||
5440 | * with the provided extents via xfs_bmse_can_merge(). | ||
5441 | */ | ||
5442 | STATIC int | ||
5443 | xfs_bmse_merge( | ||
5444 | struct xfs_inode *ip, | ||
5445 | int whichfork, | ||
5446 | xfs_fileoff_t shift, /* shift fsb */ | ||
5447 | int current_ext, /* idx of gotp */ | ||
5448 | struct xfs_bmbt_rec_host *gotp, /* extent to shift */ | ||
5449 | struct xfs_bmbt_rec_host *leftp, /* preceding extent */ | ||
5450 | struct xfs_btree_cur *cur, | ||
5451 | int *logflags) /* output */ | ||
5452 | { | ||
5453 | struct xfs_ifork *ifp; | ||
5454 | struct xfs_bmbt_irec got; | ||
5455 | struct xfs_bmbt_irec left; | ||
5456 | xfs_filblks_t blockcount; | ||
5457 | int error, i; | ||
5458 | |||
5459 | ifp = XFS_IFORK_PTR(ip, whichfork); | ||
5460 | xfs_bmbt_get_all(gotp, &got); | ||
5461 | xfs_bmbt_get_all(leftp, &left); | ||
5462 | blockcount = left.br_blockcount + got.br_blockcount; | ||
5463 | |||
5464 | ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL)); | ||
5465 | ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); | ||
5466 | ASSERT(xfs_bmse_can_merge(&left, &got, shift)); | ||
5467 | |||
5468 | /* | ||
5469 | * Merge the in-core extents. Note that the host record pointers and | ||
5470 | * current_ext index are invalid once the extent has been removed via | ||
5471 | * xfs_iext_remove(). | ||
5472 | */ | ||
5473 | xfs_bmbt_set_blockcount(leftp, blockcount); | ||
5474 | xfs_iext_remove(ip, current_ext, 1, 0); | ||
5475 | |||
5476 | /* | ||
5477 | * Update the on-disk extent count, the btree if necessary and log the | ||
5478 | * inode. | ||
5479 | */ | ||
5480 | XFS_IFORK_NEXT_SET(ip, whichfork, | ||
5481 | XFS_IFORK_NEXTENTS(ip, whichfork) - 1); | ||
5482 | *logflags |= XFS_ILOG_CORE; | ||
5483 | if (!cur) { | ||
5484 | *logflags |= XFS_ILOG_DEXT; | ||
5485 | return 0; | ||
5486 | } | ||
5487 | |||
5488 | /* lookup and remove the extent to merge */ | ||
5489 | error = xfs_bmbt_lookup_eq(cur, got.br_startoff, got.br_startblock, | ||
5490 | got.br_blockcount, &i); | ||
5491 | if (error) | ||
5492 | goto out_error; | ||
5493 | XFS_WANT_CORRUPTED_GOTO(i == 1, out_error); | ||
5494 | |||
5495 | error = xfs_btree_delete(cur, &i); | ||
5496 | if (error) | ||
5497 | goto out_error; | ||
5498 | XFS_WANT_CORRUPTED_GOTO(i == 1, out_error); | ||
5499 | |||
5500 | /* lookup and update size of the previous extent */ | ||
5501 | error = xfs_bmbt_lookup_eq(cur, left.br_startoff, left.br_startblock, | ||
5502 | left.br_blockcount, &i); | ||
5503 | if (error) | ||
5504 | goto out_error; | ||
5505 | XFS_WANT_CORRUPTED_GOTO(i == 1, out_error); | ||
5506 | |||
5507 | left.br_blockcount = blockcount; | ||
5508 | |||
5509 | error = xfs_bmbt_update(cur, left.br_startoff, left.br_startblock, | ||
5510 | left.br_blockcount, left.br_state); | ||
5511 | if (error) | ||
5512 | goto out_error; | ||
5513 | |||
5514 | return 0; | ||
5515 | |||
5516 | out_error: | ||
5517 | return error; | ||
5518 | } | ||
5519 | |||
5520 | /* | ||
5521 | * Shift a single extent. | ||
5522 | */ | ||
5523 | STATIC int | ||
5524 | xfs_bmse_shift_one( | ||
5525 | struct xfs_inode *ip, | ||
5526 | int whichfork, | ||
5527 | xfs_fileoff_t offset_shift_fsb, | ||
5528 | int *current_ext, | ||
5529 | struct xfs_bmbt_rec_host *gotp, | ||
5530 | struct xfs_btree_cur *cur, | ||
5531 | int *logflags) | ||
5532 | { | ||
5533 | struct xfs_ifork *ifp; | ||
5534 | xfs_fileoff_t startoff; | ||
5535 | struct xfs_bmbt_rec_host *leftp; | ||
5536 | struct xfs_bmbt_irec got; | ||
5537 | struct xfs_bmbt_irec left; | ||
5538 | int error; | ||
5539 | int i; | ||
5540 | |||
5541 | ifp = XFS_IFORK_PTR(ip, whichfork); | ||
5542 | |||
5543 | xfs_bmbt_get_all(gotp, &got); | ||
5544 | startoff = got.br_startoff - offset_shift_fsb; | ||
5545 | |||
5546 | /* delalloc extents should be prevented by caller */ | ||
5547 | XFS_WANT_CORRUPTED_GOTO(!isnullstartblock(got.br_startblock), | ||
5548 | out_error); | ||
5549 | |||
5550 | /* | ||
5551 | * If this is the first extent in the file, make sure there's enough | ||
5552 | * room at the start of the file and jump right to the shift as there's | ||
5553 | * no left extent to merge. | ||
5554 | */ | ||
5555 | if (*current_ext == 0) { | ||
5556 | if (got.br_startoff < offset_shift_fsb) | ||
5557 | return -EINVAL; | ||
5558 | goto shift_extent; | ||
5559 | } | ||
5560 | |||
5561 | /* grab the left extent and check for a large enough hole */ | ||
5562 | leftp = xfs_iext_get_ext(ifp, *current_ext - 1); | ||
5563 | xfs_bmbt_get_all(leftp, &left); | ||
5564 | |||
5565 | if (startoff < left.br_startoff + left.br_blockcount) | ||
5566 | return -EINVAL; | ||
5567 | |||
5568 | /* check whether to merge the extent or shift it down */ | ||
5569 | if (!xfs_bmse_can_merge(&left, &got, offset_shift_fsb)) | ||
5570 | goto shift_extent; | ||
5571 | |||
5572 | return xfs_bmse_merge(ip, whichfork, offset_shift_fsb, *current_ext, | ||
5573 | gotp, leftp, cur, logflags); | ||
5574 | |||
5575 | shift_extent: | ||
5576 | /* | ||
5577 | * Increment the extent index for the next iteration, update the start | ||
5578 | * offset of the in-core extent and update the btree if applicable. | ||
5579 | */ | ||
5580 | (*current_ext)++; | ||
5581 | xfs_bmbt_set_startoff(gotp, startoff); | ||
5582 | *logflags |= XFS_ILOG_CORE; | ||
5583 | if (!cur) { | ||
5584 | *logflags |= XFS_ILOG_DEXT; | ||
5585 | return 0; | ||
5586 | } | ||
5587 | |||
5588 | error = xfs_bmbt_lookup_eq(cur, got.br_startoff, got.br_startblock, | ||
5589 | got.br_blockcount, &i); | ||
5590 | if (error) | ||
5591 | return error; | ||
5592 | XFS_WANT_CORRUPTED_GOTO(i == 1, out_error); | ||
5593 | |||
5594 | got.br_startoff = startoff; | ||
5595 | error = xfs_bmbt_update(cur, got.br_startoff, got.br_startblock, | ||
5596 | got.br_blockcount, got.br_state); | ||
5597 | if (error) | ||
5598 | return error; | ||
5599 | |||
5600 | return 0; | ||
5601 | |||
5602 | out_error: | ||
5603 | return error; | ||
5604 | } | ||
5605 | |||
5606 | /* | ||
5407 | * Shift extent records to the left to cover a hole. | 5607 | * Shift extent records to the left to cover a hole. |
5408 | * | 5608 | * |
5409 | * The maximum number of extents to be shifted in a single operation | 5609 | * The maximum number of extents to be shifted in a single operation is |
5410 | * is @num_exts, and @current_ext keeps track of the current extent | 5610 | * @num_exts. @start_fsb specifies the file offset to start the shift and the |
5411 | * index we have shifted. @offset_shift_fsb is the length by which each | 5611 | * file offset where we've left off is returned in @next_fsb. @offset_shift_fsb |
5412 | * extent is shifted. If there is no hole to shift the extents | 5612 | * is the length by which each extent is shifted. If there is no hole to shift |
5413 | * into, this will be considered invalid operation and we abort immediately. | 5613 | * the extents into, this will be considered invalid operation and we abort |
5614 | * immediately. | ||
5414 | */ | 5615 | */ |
5415 | int | 5616 | int |
5416 | xfs_bmap_shift_extents( | 5617 | xfs_bmap_shift_extents( |
5417 | struct xfs_trans *tp, | 5618 | struct xfs_trans *tp, |
5418 | struct xfs_inode *ip, | 5619 | struct xfs_inode *ip, |
5419 | int *done, | ||
5420 | xfs_fileoff_t start_fsb, | 5620 | xfs_fileoff_t start_fsb, |
5421 | xfs_fileoff_t offset_shift_fsb, | 5621 | xfs_fileoff_t offset_shift_fsb, |
5422 | xfs_extnum_t *current_ext, | 5622 | int *done, |
5623 | xfs_fileoff_t *next_fsb, | ||
5423 | xfs_fsblock_t *firstblock, | 5624 | xfs_fsblock_t *firstblock, |
5424 | struct xfs_bmap_free *flist, | 5625 | struct xfs_bmap_free *flist, |
5425 | int num_exts) | 5626 | int num_exts) |
@@ -5427,16 +5628,13 @@ xfs_bmap_shift_extents( | |||
5427 | struct xfs_btree_cur *cur = NULL; | 5628 | struct xfs_btree_cur *cur = NULL; |
5428 | struct xfs_bmbt_rec_host *gotp; | 5629 | struct xfs_bmbt_rec_host *gotp; |
5429 | struct xfs_bmbt_irec got; | 5630 | struct xfs_bmbt_irec got; |
5430 | struct xfs_bmbt_irec left; | ||
5431 | struct xfs_mount *mp = ip->i_mount; | 5631 | struct xfs_mount *mp = ip->i_mount; |
5432 | struct xfs_ifork *ifp; | 5632 | struct xfs_ifork *ifp; |
5433 | xfs_extnum_t nexts = 0; | 5633 | xfs_extnum_t nexts = 0; |
5434 | xfs_fileoff_t startoff; | 5634 | xfs_extnum_t current_ext; |
5435 | int error = 0; | 5635 | int error = 0; |
5436 | int i; | ||
5437 | int whichfork = XFS_DATA_FORK; | 5636 | int whichfork = XFS_DATA_FORK; |
5438 | int logflags = 0; | 5637 | int logflags = 0; |
5439 | xfs_filblks_t blockcount = 0; | ||
5440 | int total_extents; | 5638 | int total_extents; |
5441 | 5639 | ||
5442 | if (unlikely(XFS_TEST_ERROR( | 5640 | if (unlikely(XFS_TEST_ERROR( |
@@ -5451,7 +5649,8 @@ xfs_bmap_shift_extents( | |||
5451 | if (XFS_FORCED_SHUTDOWN(mp)) | 5649 | if (XFS_FORCED_SHUTDOWN(mp)) |
5452 | return -EIO; | 5650 | return -EIO; |
5453 | 5651 | ||
5454 | ASSERT(current_ext != NULL); | 5652 | ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL)); |
5653 | ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); | ||
5455 | 5654 | ||
5456 | ifp = XFS_IFORK_PTR(ip, whichfork); | 5655 | ifp = XFS_IFORK_PTR(ip, whichfork); |
5457 | if (!(ifp->if_flags & XFS_IFEXTENTS)) { | 5656 | if (!(ifp->if_flags & XFS_IFEXTENTS)) { |
@@ -5461,23 +5660,6 @@ xfs_bmap_shift_extents( | |||
5461 | return error; | 5660 | return error; |
5462 | } | 5661 | } |
5463 | 5662 | ||
5464 | /* | ||
5465 | * If *current_ext is 0, we would need to lookup the extent | ||
5466 | * from where we would start shifting and store it in gotp. | ||
5467 | */ | ||
5468 | if (!*current_ext) { | ||
5469 | gotp = xfs_iext_bno_to_ext(ifp, start_fsb, current_ext); | ||
5470 | /* | ||
5471 | * gotp can be null in 2 cases: 1) if there are no extents | ||
5472 | * or 2) start_fsb lies in a hole beyond which there are | ||
5473 | * no extents. Either way, we are done. | ||
5474 | */ | ||
5475 | if (!gotp) { | ||
5476 | *done = 1; | ||
5477 | return 0; | ||
5478 | } | ||
5479 | } | ||
5480 | |||
5481 | if (ifp->if_flags & XFS_IFBROOT) { | 5663 | if (ifp->if_flags & XFS_IFBROOT) { |
5482 | cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork); | 5664 | cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork); |
5483 | cur->bc_private.b.firstblock = *firstblock; | 5665 | cur->bc_private.b.firstblock = *firstblock; |
@@ -5486,112 +5668,46 @@ xfs_bmap_shift_extents( | |||
5486 | } | 5668 | } |
5487 | 5669 | ||
5488 | /* | 5670 | /* |
5671 | * Look up the extent index for the fsb where we start shifting. We can | ||
5672 | * henceforth iterate with current_ext as extent list changes are locked | ||
5673 | * out via ilock. | ||
5674 | * | ||
5675 | * gotp can be null in 2 cases: 1) if there are no extents or 2) | ||
5676 | * start_fsb lies in a hole beyond which there are no extents. Either | ||
5677 | * way, we are done. | ||
5678 | */ | ||
5679 | gotp = xfs_iext_bno_to_ext(ifp, start_fsb, ¤t_ext); | ||
5680 | if (!gotp) { | ||
5681 | *done = 1; | ||
5682 | goto del_cursor; | ||
5683 | } | ||
5684 | |||
5685 | /* | ||
5489 | * There may be delalloc extents in the data fork before the range we | 5686 | * There may be delalloc extents in the data fork before the range we |
5490 | * are collapsing out, so we cannot | 5687 | * are collapsing out, so we cannot use the count of real extents here. |
5491 | * use the count of real extents here. Instead we have to calculate it | 5688 | * Instead we have to calculate it from the incore fork. |
5492 | * from the incore fork. | ||
5493 | */ | 5689 | */ |
5494 | total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t); | 5690 | total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t); |
5495 | while (nexts++ < num_exts && *current_ext < total_extents) { | 5691 | while (nexts++ < num_exts && current_ext < total_extents) { |
5496 | 5692 | error = xfs_bmse_shift_one(ip, whichfork, offset_shift_fsb, | |
5497 | gotp = xfs_iext_get_ext(ifp, *current_ext); | 5693 | ¤t_ext, gotp, cur, &logflags); |
5498 | xfs_bmbt_get_all(gotp, &got); | ||
5499 | startoff = got.br_startoff - offset_shift_fsb; | ||
5500 | |||
5501 | /* | ||
5502 | * Before shifting extent into hole, make sure that the hole | ||
5503 | * is large enough to accomodate the shift. | ||
5504 | */ | ||
5505 | if (*current_ext) { | ||
5506 | xfs_bmbt_get_all(xfs_iext_get_ext(ifp, | ||
5507 | *current_ext - 1), &left); | ||
5508 | |||
5509 | if (startoff < left.br_startoff + left.br_blockcount) | ||
5510 | error = -EINVAL; | ||
5511 | } else if (offset_shift_fsb > got.br_startoff) { | ||
5512 | /* | ||
5513 | * When first extent is shifted, offset_shift_fsb | ||
5514 | * should be less than the stating offset of | ||
5515 | * the first extent. | ||
5516 | */ | ||
5517 | error = -EINVAL; | ||
5518 | } | ||
5519 | |||
5520 | if (error) | 5694 | if (error) |
5521 | goto del_cursor; | 5695 | goto del_cursor; |
5522 | 5696 | ||
5523 | if (cur) { | 5697 | /* update total extent count and grab the next record */ |
5524 | error = xfs_bmbt_lookup_eq(cur, got.br_startoff, | ||
5525 | got.br_startblock, | ||
5526 | got.br_blockcount, | ||
5527 | &i); | ||
5528 | if (error) | ||
5529 | goto del_cursor; | ||
5530 | XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor); | ||
5531 | } | ||
5532 | |||
5533 | /* Check if we can merge 2 adjacent extents */ | ||
5534 | if (*current_ext && | ||
5535 | left.br_startoff + left.br_blockcount == startoff && | ||
5536 | left.br_startblock + left.br_blockcount == | ||
5537 | got.br_startblock && | ||
5538 | left.br_state == got.br_state && | ||
5539 | left.br_blockcount + got.br_blockcount <= MAXEXTLEN) { | ||
5540 | blockcount = left.br_blockcount + | ||
5541 | got.br_blockcount; | ||
5542 | xfs_iext_remove(ip, *current_ext, 1, 0); | ||
5543 | logflags |= XFS_ILOG_CORE; | ||
5544 | if (cur) { | ||
5545 | error = xfs_btree_delete(cur, &i); | ||
5546 | if (error) | ||
5547 | goto del_cursor; | ||
5548 | XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor); | ||
5549 | } else { | ||
5550 | logflags |= XFS_ILOG_DEXT; | ||
5551 | } | ||
5552 | XFS_IFORK_NEXT_SET(ip, whichfork, | ||
5553 | XFS_IFORK_NEXTENTS(ip, whichfork) - 1); | ||
5554 | gotp = xfs_iext_get_ext(ifp, --*current_ext); | ||
5555 | xfs_bmbt_get_all(gotp, &got); | ||
5556 | |||
5557 | /* Make cursor point to the extent we will update */ | ||
5558 | if (cur) { | ||
5559 | error = xfs_bmbt_lookup_eq(cur, got.br_startoff, | ||
5560 | got.br_startblock, | ||
5561 | got.br_blockcount, | ||
5562 | &i); | ||
5563 | if (error) | ||
5564 | goto del_cursor; | ||
5565 | XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor); | ||
5566 | } | ||
5567 | |||
5568 | xfs_bmbt_set_blockcount(gotp, blockcount); | ||
5569 | got.br_blockcount = blockcount; | ||
5570 | } else { | ||
5571 | /* We have to update the startoff */ | ||
5572 | xfs_bmbt_set_startoff(gotp, startoff); | ||
5573 | got.br_startoff = startoff; | ||
5574 | } | ||
5575 | |||
5576 | logflags |= XFS_ILOG_CORE; | ||
5577 | if (cur) { | ||
5578 | error = xfs_bmbt_update(cur, got.br_startoff, | ||
5579 | got.br_startblock, | ||
5580 | got.br_blockcount, | ||
5581 | got.br_state); | ||
5582 | if (error) | ||
5583 | goto del_cursor; | ||
5584 | } else { | ||
5585 | logflags |= XFS_ILOG_DEXT; | ||
5586 | } | ||
5587 | |||
5588 | (*current_ext)++; | ||
5589 | total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t); | 5698 | total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t); |
5699 | if (current_ext >= total_extents) | ||
5700 | break; | ||
5701 | gotp = xfs_iext_get_ext(ifp, current_ext); | ||
5590 | } | 5702 | } |
5591 | 5703 | ||
5592 | /* Check if we are done */ | 5704 | /* Check if we are done */ |
5593 | if (*current_ext == total_extents) | 5705 | if (current_ext == total_extents) { |
5594 | *done = 1; | 5706 | *done = 1; |
5707 | } else if (next_fsb) { | ||
5708 | xfs_bmbt_get_all(gotp, &got); | ||
5709 | *next_fsb = got.br_startoff; | ||
5710 | } | ||
5595 | 5711 | ||
5596 | del_cursor: | 5712 | del_cursor: |
5597 | if (cur) | 5713 | if (cur) |
@@ -5600,5 +5716,6 @@ del_cursor: | |||
5600 | 5716 | ||
5601 | if (logflags) | 5717 | if (logflags) |
5602 | xfs_trans_log_inode(tp, ip, logflags); | 5718 | xfs_trans_log_inode(tp, ip, logflags); |
5719 | |||
5603 | return error; | 5720 | return error; |
5604 | } | 5721 | } |
diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h index b879ca56a64c..44db6db86402 100644 --- a/fs/xfs/libxfs/xfs_bmap.h +++ b/fs/xfs/libxfs/xfs_bmap.h | |||
@@ -178,9 +178,8 @@ int xfs_check_nostate_extents(struct xfs_ifork *ifp, xfs_extnum_t idx, | |||
178 | xfs_extnum_t num); | 178 | xfs_extnum_t num); |
179 | uint xfs_default_attroffset(struct xfs_inode *ip); | 179 | uint xfs_default_attroffset(struct xfs_inode *ip); |
180 | int xfs_bmap_shift_extents(struct xfs_trans *tp, struct xfs_inode *ip, | 180 | int xfs_bmap_shift_extents(struct xfs_trans *tp, struct xfs_inode *ip, |
181 | int *done, xfs_fileoff_t start_fsb, | 181 | xfs_fileoff_t start_fsb, xfs_fileoff_t offset_shift_fsb, |
182 | xfs_fileoff_t offset_shift_fsb, xfs_extnum_t *current_ext, | 182 | int *done, xfs_fileoff_t *next_fsb, xfs_fsblock_t *firstblock, |
183 | xfs_fsblock_t *firstblock, struct xfs_bmap_free *flist, | 183 | struct xfs_bmap_free *flist, int num_exts); |
184 | int num_exts); | ||
185 | 184 | ||
186 | #endif /* __XFS_BMAP_H__ */ | 185 | #endif /* __XFS_BMAP_H__ */ |
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index b984647c24db..2f502537a39c 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c | |||
@@ -434,10 +434,22 @@ xfs_start_page_writeback( | |||
434 | { | 434 | { |
435 | ASSERT(PageLocked(page)); | 435 | ASSERT(PageLocked(page)); |
436 | ASSERT(!PageWriteback(page)); | 436 | ASSERT(!PageWriteback(page)); |
437 | if (clear_dirty) | 437 | |
438 | /* | ||
439 | * if the page was not fully cleaned, we need to ensure that the higher | ||
440 | * layers come back to it correctly. That means we need to keep the page | ||
441 | * dirty, and for WB_SYNC_ALL writeback we need to ensure the | ||
442 | * PAGECACHE_TAG_TOWRITE index mark is not removed so another attempt to | ||
443 | * write this page in this writeback sweep will be made. | ||
444 | */ | ||
445 | if (clear_dirty) { | ||
438 | clear_page_dirty_for_io(page); | 446 | clear_page_dirty_for_io(page); |
439 | set_page_writeback(page); | 447 | set_page_writeback(page); |
448 | } else | ||
449 | set_page_writeback_keepwrite(page); | ||
450 | |||
440 | unlock_page(page); | 451 | unlock_page(page); |
452 | |||
441 | /* If no buffers on the page are to be written, finish it here */ | 453 | /* If no buffers on the page are to be written, finish it here */ |
442 | if (!buffers) | 454 | if (!buffers) |
443 | end_page_writeback(page); | 455 | end_page_writeback(page); |
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index 1707980f9a4b..809ae7d395c3 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c | |||
@@ -1205,6 +1205,7 @@ xfs_free_file_space( | |||
1205 | xfs_bmap_free_t free_list; | 1205 | xfs_bmap_free_t free_list; |
1206 | xfs_bmbt_irec_t imap; | 1206 | xfs_bmbt_irec_t imap; |
1207 | xfs_off_t ioffset; | 1207 | xfs_off_t ioffset; |
1208 | xfs_off_t iendoffset; | ||
1208 | xfs_extlen_t mod=0; | 1209 | xfs_extlen_t mod=0; |
1209 | xfs_mount_t *mp; | 1210 | xfs_mount_t *mp; |
1210 | int nimap; | 1211 | int nimap; |
@@ -1233,12 +1234,13 @@ xfs_free_file_space( | |||
1233 | inode_dio_wait(VFS_I(ip)); | 1234 | inode_dio_wait(VFS_I(ip)); |
1234 | 1235 | ||
1235 | rounding = max_t(xfs_off_t, 1 << mp->m_sb.sb_blocklog, PAGE_CACHE_SIZE); | 1236 | rounding = max_t(xfs_off_t, 1 << mp->m_sb.sb_blocklog, PAGE_CACHE_SIZE); |
1236 | ioffset = offset & ~(rounding - 1); | 1237 | ioffset = round_down(offset, rounding); |
1237 | error = filemap_write_and_wait_range(VFS_I(ip)->i_mapping, | 1238 | iendoffset = round_up(offset + len, rounding) - 1; |
1238 | ioffset, -1); | 1239 | error = filemap_write_and_wait_range(VFS_I(ip)->i_mapping, ioffset, |
1240 | iendoffset); | ||
1239 | if (error) | 1241 | if (error) |
1240 | goto out; | 1242 | goto out; |
1241 | truncate_pagecache_range(VFS_I(ip), ioffset, -1); | 1243 | truncate_pagecache_range(VFS_I(ip), ioffset, iendoffset); |
1242 | 1244 | ||
1243 | /* | 1245 | /* |
1244 | * Need to zero the stuff we're not freeing, on disk. | 1246 | * Need to zero the stuff we're not freeing, on disk. |
@@ -1456,41 +1458,47 @@ xfs_collapse_file_space( | |||
1456 | struct xfs_mount *mp = ip->i_mount; | 1458 | struct xfs_mount *mp = ip->i_mount; |
1457 | struct xfs_trans *tp; | 1459 | struct xfs_trans *tp; |
1458 | int error; | 1460 | int error; |
1459 | xfs_extnum_t current_ext = 0; | ||
1460 | struct xfs_bmap_free free_list; | 1461 | struct xfs_bmap_free free_list; |
1461 | xfs_fsblock_t first_block; | 1462 | xfs_fsblock_t first_block; |
1462 | int committed; | 1463 | int committed; |
1463 | xfs_fileoff_t start_fsb; | 1464 | xfs_fileoff_t start_fsb; |
1465 | xfs_fileoff_t next_fsb; | ||
1464 | xfs_fileoff_t shift_fsb; | 1466 | xfs_fileoff_t shift_fsb; |
1465 | 1467 | ||
1466 | ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL)); | 1468 | ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL)); |
1467 | 1469 | ||
1468 | trace_xfs_collapse_file_space(ip); | 1470 | trace_xfs_collapse_file_space(ip); |
1469 | 1471 | ||
1470 | start_fsb = XFS_B_TO_FSB(mp, offset + len); | 1472 | next_fsb = XFS_B_TO_FSB(mp, offset + len); |
1471 | shift_fsb = XFS_B_TO_FSB(mp, len); | 1473 | shift_fsb = XFS_B_TO_FSB(mp, len); |
1472 | 1474 | ||
1473 | /* | 1475 | error = xfs_free_file_space(ip, offset, len); |
1474 | * Writeback the entire file and force remove any post-eof blocks. The | ||
1475 | * writeback prevents changes to the extent list via concurrent | ||
1476 | * writeback and the eofblocks trim prevents the extent shift algorithm | ||
1477 | * from running into a post-eof delalloc extent. | ||
1478 | * | ||
1479 | * XXX: This is a temporary fix until the extent shift loop below is | ||
1480 | * converted to use offsets and lookups within the ILOCK rather than | ||
1481 | * carrying around the index into the extent list for the next | ||
1482 | * iteration. | ||
1483 | */ | ||
1484 | error = filemap_write_and_wait(VFS_I(ip)->i_mapping); | ||
1485 | if (error) | 1476 | if (error) |
1486 | return error; | 1477 | return error; |
1478 | |||
1479 | /* | ||
1480 | * Trim eofblocks to avoid shifting uninitialized post-eof preallocation | ||
1481 | * into the accessible region of the file. | ||
1482 | */ | ||
1487 | if (xfs_can_free_eofblocks(ip, true)) { | 1483 | if (xfs_can_free_eofblocks(ip, true)) { |
1488 | error = xfs_free_eofblocks(mp, ip, false); | 1484 | error = xfs_free_eofblocks(mp, ip, false); |
1489 | if (error) | 1485 | if (error) |
1490 | return error; | 1486 | return error; |
1491 | } | 1487 | } |
1492 | 1488 | ||
1493 | error = xfs_free_file_space(ip, offset, len); | 1489 | /* |
1490 | * Writeback and invalidate cache for the remainder of the file as we're | ||
1491 | * about to shift down every extent from the collapse range to EOF. The | ||
1492 | * free of the collapse range above might have already done some of | ||
1493 | * this, but we shouldn't rely on it to do anything outside of the range | ||
1494 | * that was freed. | ||
1495 | */ | ||
1496 | error = filemap_write_and_wait_range(VFS_I(ip)->i_mapping, | ||
1497 | offset + len, -1); | ||
1498 | if (error) | ||
1499 | return error; | ||
1500 | error = invalidate_inode_pages2_range(VFS_I(ip)->i_mapping, | ||
1501 | (offset + len) >> PAGE_CACHE_SHIFT, -1); | ||
1494 | if (error) | 1502 | if (error) |
1495 | return error; | 1503 | return error; |
1496 | 1504 | ||
@@ -1525,10 +1533,10 @@ xfs_collapse_file_space( | |||
1525 | * We are using the write transaction in which max 2 bmbt | 1533 | * We are using the write transaction in which max 2 bmbt |
1526 | * updates are allowed | 1534 | * updates are allowed |
1527 | */ | 1535 | */ |
1528 | error = xfs_bmap_shift_extents(tp, ip, &done, start_fsb, | 1536 | start_fsb = next_fsb; |
1529 | shift_fsb, ¤t_ext, | 1537 | error = xfs_bmap_shift_extents(tp, ip, start_fsb, shift_fsb, |
1530 | &first_block, &free_list, | 1538 | &done, &next_fsb, &first_block, &free_list, |
1531 | XFS_BMAP_MAX_SHIFT_EXTENTS); | 1539 | XFS_BMAP_MAX_SHIFT_EXTENTS); |
1532 | if (error) | 1540 | if (error) |
1533 | goto out; | 1541 | goto out; |
1534 | 1542 | ||