diff options
author | Yan, Zheng <zheng.yan@oracle.com> | 2010-05-16 10:49:59 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2010-05-25 10:34:53 -0400 |
commit | efa56464562991b8c24f965199888806bd8c4b38 (patch) | |
tree | e7c7e69e2931674ddf4f14ac08dfdf43b45de0f4 /fs | |
parent | 4a500fd178c89b96fa166a2d9e7855df33429841 (diff) |
Btrfs: Pre-allocate space for data relocation
Pre-allocate space for data relocation. This can detect ENOPSC
condition caused by fragmentation of free space.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/ctree.h | 3 | ||||
-rw-r--r-- | fs/btrfs/inode.c | 44 | ||||
-rw-r--r-- | fs/btrfs/relocation.c | 90 |
3 files changed, 92 insertions, 45 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index fc324f9fcb42..65530837d04b 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -2420,6 +2420,9 @@ int btrfs_cont_expand(struct inode *inode, loff_t size); | |||
2420 | int btrfs_invalidate_inodes(struct btrfs_root *root); | 2420 | int btrfs_invalidate_inodes(struct btrfs_root *root); |
2421 | void btrfs_add_delayed_iput(struct inode *inode); | 2421 | void btrfs_add_delayed_iput(struct inode *inode); |
2422 | void btrfs_run_delayed_iputs(struct btrfs_root *root); | 2422 | void btrfs_run_delayed_iputs(struct btrfs_root *root); |
2423 | int btrfs_prealloc_file_range(struct inode *inode, int mode, | ||
2424 | u64 start, u64 num_bytes, u64 min_size, | ||
2425 | loff_t actual_len, u64 *alloc_hint); | ||
2423 | extern const struct dentry_operations btrfs_dentry_operations; | 2426 | extern const struct dentry_operations btrfs_dentry_operations; |
2424 | 2427 | ||
2425 | /* ioctl.c */ | 2428 | /* ioctl.c */ |
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index bef69bedf3cf..460dd512eebd 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -1175,6 +1175,13 @@ out_check: | |||
1175 | num_bytes, num_bytes, type); | 1175 | num_bytes, num_bytes, type); |
1176 | BUG_ON(ret); | 1176 | BUG_ON(ret); |
1177 | 1177 | ||
1178 | if (root->root_key.objectid == | ||
1179 | BTRFS_DATA_RELOC_TREE_OBJECTID) { | ||
1180 | ret = btrfs_reloc_clone_csums(inode, cur_offset, | ||
1181 | num_bytes); | ||
1182 | BUG_ON(ret); | ||
1183 | } | ||
1184 | |||
1178 | extent_clear_unlock_delalloc(inode, &BTRFS_I(inode)->io_tree, | 1185 | extent_clear_unlock_delalloc(inode, &BTRFS_I(inode)->io_tree, |
1179 | cur_offset, cur_offset + num_bytes - 1, | 1186 | cur_offset, cur_offset + num_bytes - 1, |
1180 | locked_page, EXTENT_CLEAR_UNLOCK_PAGE | | 1187 | locked_page, EXTENT_CLEAR_UNLOCK_PAGE | |
@@ -6080,16 +6087,15 @@ out_unlock: | |||
6080 | return err; | 6087 | return err; |
6081 | } | 6088 | } |
6082 | 6089 | ||
6083 | static int prealloc_file_range(struct inode *inode, u64 start, u64 end, | 6090 | int btrfs_prealloc_file_range(struct inode *inode, int mode, |
6084 | u64 alloc_hint, int mode, loff_t actual_len) | 6091 | u64 start, u64 num_bytes, u64 min_size, |
6092 | loff_t actual_len, u64 *alloc_hint) | ||
6085 | { | 6093 | { |
6086 | struct btrfs_trans_handle *trans; | 6094 | struct btrfs_trans_handle *trans; |
6087 | struct btrfs_root *root = BTRFS_I(inode)->root; | 6095 | struct btrfs_root *root = BTRFS_I(inode)->root; |
6088 | struct btrfs_key ins; | 6096 | struct btrfs_key ins; |
6089 | u64 cur_offset = start; | 6097 | u64 cur_offset = start; |
6090 | u64 num_bytes = end - start; | ||
6091 | int ret = 0; | 6098 | int ret = 0; |
6092 | u64 i_size; | ||
6093 | 6099 | ||
6094 | while (num_bytes > 0) { | 6100 | while (num_bytes > 0) { |
6095 | trans = btrfs_start_transaction(root, 3); | 6101 | trans = btrfs_start_transaction(root, 3); |
@@ -6098,9 +6104,8 @@ static int prealloc_file_range(struct inode *inode, u64 start, u64 end, | |||
6098 | break; | 6104 | break; |
6099 | } | 6105 | } |
6100 | 6106 | ||
6101 | ret = btrfs_reserve_extent(trans, root, num_bytes, | 6107 | ret = btrfs_reserve_extent(trans, root, num_bytes, min_size, |
6102 | root->sectorsize, 0, alloc_hint, | 6108 | 0, *alloc_hint, (u64)-1, &ins, 1); |
6103 | (u64)-1, &ins, 1); | ||
6104 | if (ret) { | 6109 | if (ret) { |
6105 | btrfs_end_transaction(trans, root); | 6110 | btrfs_end_transaction(trans, root); |
6106 | break; | 6111 | break; |
@@ -6117,20 +6122,19 @@ static int prealloc_file_range(struct inode *inode, u64 start, u64 end, | |||
6117 | 6122 | ||
6118 | num_bytes -= ins.offset; | 6123 | num_bytes -= ins.offset; |
6119 | cur_offset += ins.offset; | 6124 | cur_offset += ins.offset; |
6120 | alloc_hint = ins.objectid + ins.offset; | 6125 | *alloc_hint = ins.objectid + ins.offset; |
6121 | 6126 | ||
6122 | inode->i_ctime = CURRENT_TIME; | 6127 | inode->i_ctime = CURRENT_TIME; |
6123 | BTRFS_I(inode)->flags |= BTRFS_INODE_PREALLOC; | 6128 | BTRFS_I(inode)->flags |= BTRFS_INODE_PREALLOC; |
6124 | if (!(mode & FALLOC_FL_KEEP_SIZE) && | 6129 | if (!(mode & FALLOC_FL_KEEP_SIZE) && |
6125 | (actual_len > inode->i_size) && | 6130 | (actual_len > inode->i_size) && |
6126 | (cur_offset > inode->i_size)) { | 6131 | (cur_offset > inode->i_size)) { |
6127 | |||
6128 | if (cur_offset > actual_len) | 6132 | if (cur_offset > actual_len) |
6129 | i_size = actual_len; | 6133 | i_size_write(inode, actual_len); |
6130 | else | 6134 | else |
6131 | i_size = cur_offset; | 6135 | i_size_write(inode, cur_offset); |
6132 | i_size_write(inode, i_size); | 6136 | i_size_write(inode, cur_offset); |
6133 | btrfs_ordered_update_i_size(inode, i_size, NULL); | 6137 | btrfs_ordered_update_i_size(inode, cur_offset, NULL); |
6134 | } | 6138 | } |
6135 | 6139 | ||
6136 | ret = btrfs_update_inode(trans, root, inode); | 6140 | ret = btrfs_update_inode(trans, root, inode); |
@@ -6216,16 +6220,16 @@ static long btrfs_fallocate(struct inode *inode, int mode, | |||
6216 | if (em->block_start == EXTENT_MAP_HOLE || | 6220 | if (em->block_start == EXTENT_MAP_HOLE || |
6217 | (cur_offset >= inode->i_size && | 6221 | (cur_offset >= inode->i_size && |
6218 | !test_bit(EXTENT_FLAG_PREALLOC, &em->flags))) { | 6222 | !test_bit(EXTENT_FLAG_PREALLOC, &em->flags))) { |
6219 | ret = prealloc_file_range(inode, | 6223 | ret = btrfs_prealloc_file_range(inode, 0, cur_offset, |
6220 | cur_offset, last_byte, | 6224 | last_byte - cur_offset, |
6221 | alloc_hint, mode, offset+len); | 6225 | 1 << inode->i_blkbits, |
6226 | offset + len, | ||
6227 | &alloc_hint); | ||
6222 | if (ret < 0) { | 6228 | if (ret < 0) { |
6223 | free_extent_map(em); | 6229 | free_extent_map(em); |
6224 | break; | 6230 | break; |
6225 | } | 6231 | } |
6226 | } | 6232 | } |
6227 | if (em->block_start <= EXTENT_MAP_LAST_BYTE) | ||
6228 | alloc_hint = em->block_start; | ||
6229 | free_extent_map(em); | 6233 | free_extent_map(em); |
6230 | 6234 | ||
6231 | cur_offset = last_byte; | 6235 | cur_offset = last_byte; |
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 145a468c300d..3943526b7348 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c | |||
@@ -2546,6 +2546,50 @@ out: | |||
2546 | } | 2546 | } |
2547 | 2547 | ||
2548 | static noinline_for_stack | 2548 | static noinline_for_stack |
2549 | int prealloc_file_extent_cluster(struct inode *inode, | ||
2550 | struct file_extent_cluster *cluster) | ||
2551 | { | ||
2552 | u64 alloc_hint = 0; | ||
2553 | u64 start; | ||
2554 | u64 end; | ||
2555 | u64 offset = BTRFS_I(inode)->index_cnt; | ||
2556 | u64 num_bytes; | ||
2557 | int nr = 0; | ||
2558 | int ret = 0; | ||
2559 | |||
2560 | BUG_ON(cluster->start != cluster->boundary[0]); | ||
2561 | mutex_lock(&inode->i_mutex); | ||
2562 | |||
2563 | ret = btrfs_check_data_free_space(inode, cluster->end + | ||
2564 | 1 - cluster->start); | ||
2565 | if (ret) | ||
2566 | goto out; | ||
2567 | |||
2568 | while (nr < cluster->nr) { | ||
2569 | start = cluster->boundary[nr] - offset; | ||
2570 | if (nr + 1 < cluster->nr) | ||
2571 | end = cluster->boundary[nr + 1] - 1 - offset; | ||
2572 | else | ||
2573 | end = cluster->end - offset; | ||
2574 | |||
2575 | lock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS); | ||
2576 | num_bytes = end + 1 - start; | ||
2577 | ret = btrfs_prealloc_file_range(inode, 0, start, | ||
2578 | num_bytes, num_bytes, | ||
2579 | end + 1, &alloc_hint); | ||
2580 | unlock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS); | ||
2581 | if (ret) | ||
2582 | break; | ||
2583 | nr++; | ||
2584 | } | ||
2585 | btrfs_free_reserved_data_space(inode, cluster->end + | ||
2586 | 1 - cluster->start); | ||
2587 | out: | ||
2588 | mutex_unlock(&inode->i_mutex); | ||
2589 | return ret; | ||
2590 | } | ||
2591 | |||
2592 | static noinline_for_stack | ||
2549 | int setup_extent_mapping(struct inode *inode, u64 start, u64 end, | 2593 | int setup_extent_mapping(struct inode *inode, u64 start, u64 end, |
2550 | u64 block_start) | 2594 | u64 block_start) |
2551 | { | 2595 | { |
@@ -2588,7 +2632,6 @@ static int relocate_file_extent_cluster(struct inode *inode, | |||
2588 | u64 offset = BTRFS_I(inode)->index_cnt; | 2632 | u64 offset = BTRFS_I(inode)->index_cnt; |
2589 | unsigned long index; | 2633 | unsigned long index; |
2590 | unsigned long last_index; | 2634 | unsigned long last_index; |
2591 | unsigned int dirty_page = 0; | ||
2592 | struct page *page; | 2635 | struct page *page; |
2593 | struct file_ra_state *ra; | 2636 | struct file_ra_state *ra; |
2594 | int nr = 0; | 2637 | int nr = 0; |
@@ -2601,21 +2644,24 @@ static int relocate_file_extent_cluster(struct inode *inode, | |||
2601 | if (!ra) | 2644 | if (!ra) |
2602 | return -ENOMEM; | 2645 | return -ENOMEM; |
2603 | 2646 | ||
2604 | index = (cluster->start - offset) >> PAGE_CACHE_SHIFT; | 2647 | ret = prealloc_file_extent_cluster(inode, cluster); |
2605 | last_index = (cluster->end - offset) >> PAGE_CACHE_SHIFT; | 2648 | if (ret) |
2649 | goto out; | ||
2606 | 2650 | ||
2607 | mutex_lock(&inode->i_mutex); | 2651 | file_ra_state_init(ra, inode->i_mapping); |
2608 | 2652 | ||
2609 | i_size_write(inode, cluster->end + 1 - offset); | ||
2610 | ret = setup_extent_mapping(inode, cluster->start - offset, | 2653 | ret = setup_extent_mapping(inode, cluster->start - offset, |
2611 | cluster->end - offset, cluster->start); | 2654 | cluster->end - offset, cluster->start); |
2612 | if (ret) | 2655 | if (ret) |
2613 | goto out_unlock; | 2656 | goto out; |
2614 | |||
2615 | file_ra_state_init(ra, inode->i_mapping); | ||
2616 | 2657 | ||
2617 | WARN_ON(cluster->start != cluster->boundary[0]); | 2658 | index = (cluster->start - offset) >> PAGE_CACHE_SHIFT; |
2659 | last_index = (cluster->end - offset) >> PAGE_CACHE_SHIFT; | ||
2618 | while (index <= last_index) { | 2660 | while (index <= last_index) { |
2661 | ret = btrfs_delalloc_reserve_metadata(inode, PAGE_CACHE_SIZE); | ||
2662 | if (ret) | ||
2663 | goto out; | ||
2664 | |||
2619 | page = find_lock_page(inode->i_mapping, index); | 2665 | page = find_lock_page(inode->i_mapping, index); |
2620 | if (!page) { | 2666 | if (!page) { |
2621 | page_cache_sync_readahead(inode->i_mapping, | 2667 | page_cache_sync_readahead(inode->i_mapping, |
@@ -2623,8 +2669,10 @@ static int relocate_file_extent_cluster(struct inode *inode, | |||
2623 | last_index + 1 - index); | 2669 | last_index + 1 - index); |
2624 | page = grab_cache_page(inode->i_mapping, index); | 2670 | page = grab_cache_page(inode->i_mapping, index); |
2625 | if (!page) { | 2671 | if (!page) { |
2672 | btrfs_delalloc_release_metadata(inode, | ||
2673 | PAGE_CACHE_SIZE); | ||
2626 | ret = -ENOMEM; | 2674 | ret = -ENOMEM; |
2627 | goto out_unlock; | 2675 | goto out; |
2628 | } | 2676 | } |
2629 | } | 2677 | } |
2630 | 2678 | ||
@@ -2640,8 +2688,10 @@ static int relocate_file_extent_cluster(struct inode *inode, | |||
2640 | if (!PageUptodate(page)) { | 2688 | if (!PageUptodate(page)) { |
2641 | unlock_page(page); | 2689 | unlock_page(page); |
2642 | page_cache_release(page); | 2690 | page_cache_release(page); |
2691 | btrfs_delalloc_release_metadata(inode, | ||
2692 | PAGE_CACHE_SIZE); | ||
2643 | ret = -EIO; | 2693 | ret = -EIO; |
2644 | goto out_unlock; | 2694 | goto out; |
2645 | } | 2695 | } |
2646 | } | 2696 | } |
2647 | 2697 | ||
@@ -2660,10 +2710,9 @@ static int relocate_file_extent_cluster(struct inode *inode, | |||
2660 | EXTENT_BOUNDARY, GFP_NOFS); | 2710 | EXTENT_BOUNDARY, GFP_NOFS); |
2661 | nr++; | 2711 | nr++; |
2662 | } | 2712 | } |
2663 | btrfs_set_extent_delalloc(inode, page_start, page_end, NULL); | ||
2664 | 2713 | ||
2714 | btrfs_set_extent_delalloc(inode, page_start, page_end, NULL); | ||
2665 | set_page_dirty(page); | 2715 | set_page_dirty(page); |
2666 | dirty_page++; | ||
2667 | 2716 | ||
2668 | unlock_extent(&BTRFS_I(inode)->io_tree, | 2717 | unlock_extent(&BTRFS_I(inode)->io_tree, |
2669 | page_start, page_end, GFP_NOFS); | 2718 | page_start, page_end, GFP_NOFS); |
@@ -2671,20 +2720,11 @@ static int relocate_file_extent_cluster(struct inode *inode, | |||
2671 | page_cache_release(page); | 2720 | page_cache_release(page); |
2672 | 2721 | ||
2673 | index++; | 2722 | index++; |
2674 | if (nr < cluster->nr && | 2723 | balance_dirty_pages_ratelimited(inode->i_mapping); |
2675 | page_end + 1 + offset == cluster->boundary[nr]) { | 2724 | btrfs_throttle(BTRFS_I(inode)->root); |
2676 | balance_dirty_pages_ratelimited_nr(inode->i_mapping, | ||
2677 | dirty_page); | ||
2678 | dirty_page = 0; | ||
2679 | } | ||
2680 | } | ||
2681 | if (dirty_page) { | ||
2682 | balance_dirty_pages_ratelimited_nr(inode->i_mapping, | ||
2683 | dirty_page); | ||
2684 | } | 2725 | } |
2685 | WARN_ON(nr != cluster->nr); | 2726 | WARN_ON(nr != cluster->nr); |
2686 | out_unlock: | 2727 | out: |
2687 | mutex_unlock(&inode->i_mutex); | ||
2688 | kfree(ra); | 2728 | kfree(ra); |
2689 | return ret; | 2729 | return ret; |
2690 | } | 2730 | } |