diff options
author | Chris Mason <chris.mason@oracle.com> | 2007-09-10 19:58:16 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@hera.kernel.org> | 2007-09-10 19:58:16 -0400 |
commit | 86479a04eef8f304a13aeb8b64bcc8e506a68268 (patch) | |
tree | cda7e66d469bcdc2f41144ea91405aeb96d1f777 /fs/btrfs | |
parent | 8e21528f87854314792aaef4d279bc9e5a9be997 (diff) |
Add support for defragging files via btrfsctl -d. Avoid OOM on extent tree
defrag.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/ctree.c | 4 | ||||
-rw-r--r-- | fs/btrfs/disk-io.h | 2 | ||||
-rw-r--r-- | fs/btrfs/extent_map.c | 16 | ||||
-rw-r--r-- | fs/btrfs/inode.c | 93 | ||||
-rw-r--r-- | fs/btrfs/tree-defrag.c | 3 |
5 files changed, 100 insertions, 18 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 89e282c35ce2..b41f48ade419 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c | |||
@@ -217,6 +217,9 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans, | |||
217 | root->fs_info->generation); | 217 | root->fs_info->generation); |
218 | WARN_ON(1); | 218 | WARN_ON(1); |
219 | } | 219 | } |
220 | if (buffer_defrag_done(parent)) | ||
221 | return 0; | ||
222 | |||
220 | parent_node = btrfs_buffer_node(parent); | 223 | parent_node = btrfs_buffer_node(parent); |
221 | parent_nritems = btrfs_header_nritems(&parent_node->header); | 224 | parent_nritems = btrfs_header_nritems(&parent_node->header); |
222 | parent_level = btrfs_header_level(&parent_node->header); | 225 | parent_level = btrfs_header_level(&parent_node->header); |
@@ -274,6 +277,7 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans, | |||
274 | *last_ret = search_start; | 277 | *last_ret = search_start; |
275 | if (parent_level == 1) | 278 | if (parent_level == 1) |
276 | clear_buffer_defrag(tmp_bh); | 279 | clear_buffer_defrag(tmp_bh); |
280 | set_buffer_defrag_done(tmp_bh); | ||
277 | brelse(tmp_bh); | 281 | brelse(tmp_bh); |
278 | } | 282 | } |
279 | return err; | 283 | return err; |
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h index da6bb72750f2..714fa877438b 100644 --- a/fs/btrfs/disk-io.h +++ b/fs/btrfs/disk-io.h | |||
@@ -26,9 +26,11 @@ | |||
26 | enum btrfs_bh_state_bits { | 26 | enum btrfs_bh_state_bits { |
27 | BH_Checked = BH_PrivateStart, | 27 | BH_Checked = BH_PrivateStart, |
28 | BH_Defrag, | 28 | BH_Defrag, |
29 | BH_DefragDone, | ||
29 | }; | 30 | }; |
30 | BUFFER_FNS(Checked, checked); | 31 | BUFFER_FNS(Checked, checked); |
31 | BUFFER_FNS(Defrag, defrag); | 32 | BUFFER_FNS(Defrag, defrag); |
33 | BUFFER_FNS(DefragDone, defrag_done); | ||
32 | 34 | ||
33 | static inline struct btrfs_node *btrfs_buffer_node(struct buffer_head *bh) | 35 | static inline struct btrfs_node *btrfs_buffer_node(struct buffer_head *bh) |
34 | { | 36 | { |
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index 8ad6f8efc5a0..33f7a18dddf7 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c | |||
@@ -10,6 +10,12 @@ | |||
10 | #include <linux/blkdev.h> | 10 | #include <linux/blkdev.h> |
11 | #include "extent_map.h" | 11 | #include "extent_map.h" |
12 | 12 | ||
13 | /* temporary define until extent_map moves out of btrfs */ | ||
14 | struct kmem_cache *btrfs_cache_create(const char *name, size_t size, | ||
15 | unsigned long extra_flags, | ||
16 | void (*ctor)(void *, struct kmem_cache *, | ||
17 | unsigned long)); | ||
18 | |||
13 | static struct kmem_cache *extent_map_cache; | 19 | static struct kmem_cache *extent_map_cache; |
14 | static struct kmem_cache *extent_state_cache; | 20 | static struct kmem_cache *extent_state_cache; |
15 | 21 | ||
@@ -32,14 +38,12 @@ struct tree_entry { | |||
32 | 38 | ||
33 | void __init extent_map_init(void) | 39 | void __init extent_map_init(void) |
34 | { | 40 | { |
35 | extent_map_cache = kmem_cache_create("extent_map", | 41 | extent_map_cache = btrfs_cache_create("extent_map", |
36 | sizeof(struct extent_map), 0, | 42 | sizeof(struct extent_map), |
37 | SLAB_RECLAIM_ACCOUNT | | ||
38 | SLAB_DESTROY_BY_RCU, | 43 | SLAB_DESTROY_BY_RCU, |
39 | NULL); | 44 | NULL); |
40 | extent_state_cache = kmem_cache_create("extent_state", | 45 | extent_state_cache = btrfs_cache_create("extent_state", |
41 | sizeof(struct extent_state), 0, | 46 | sizeof(struct extent_state), |
42 | SLAB_RECLAIM_ACCOUNT | | ||
43 | SLAB_DESTROY_BY_RCU, | 47 | SLAB_DESTROY_BY_RCU, |
44 | NULL); | 48 | NULL); |
45 | } | 49 | } |
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 64710fa77d01..6b3e4404dc6a 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -1904,6 +1904,70 @@ fail: | |||
1904 | return ret; | 1904 | return ret; |
1905 | } | 1905 | } |
1906 | 1906 | ||
1907 | static unsigned long force_ra(struct address_space *mapping, | ||
1908 | struct file_ra_state *ra, struct file *file, | ||
1909 | pgoff_t offset, pgoff_t last_index) | ||
1910 | { | ||
1911 | pgoff_t req_size; | ||
1912 | |||
1913 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) | ||
1914 | req_size = last_index - offset + 1; | ||
1915 | offset = page_cache_readahead(mapping, ra, file, offset, req_size); | ||
1916 | return offset; | ||
1917 | #else | ||
1918 | req_size = min(last_index - offset + 1, (pgoff_t)128); | ||
1919 | page_cache_sync_readahead(mapping, ra, file, offset, req_size); | ||
1920 | return offset + req_size; | ||
1921 | #endif | ||
1922 | } | ||
1923 | |||
1924 | int btrfs_defrag_file(struct file *file) { | ||
1925 | struct inode *inode = file->f_path.dentry->d_inode; | ||
1926 | struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree; | ||
1927 | struct page *page; | ||
1928 | unsigned long last_index; | ||
1929 | unsigned long ra_index = 0; | ||
1930 | u64 page_start; | ||
1931 | u64 page_end; | ||
1932 | unsigned long i; | ||
1933 | |||
1934 | mutex_lock(&inode->i_mutex); | ||
1935 | last_index = inode->i_size >> PAGE_CACHE_SHIFT; | ||
1936 | for (i = 0; i <= last_index; i++) { | ||
1937 | if (i == ra_index) { | ||
1938 | ra_index = force_ra(inode->i_mapping, &file->f_ra, | ||
1939 | file, ra_index, last_index); | ||
1940 | } | ||
1941 | page = grab_cache_page(inode->i_mapping, i); | ||
1942 | if (!page) | ||
1943 | goto out_unlock; | ||
1944 | if (!PageUptodate(page)) { | ||
1945 | btrfs_readpage(NULL, page); | ||
1946 | lock_page(page); | ||
1947 | if (!PageUptodate(page)) { | ||
1948 | unlock_page(page); | ||
1949 | page_cache_release(page); | ||
1950 | goto out_unlock; | ||
1951 | } | ||
1952 | } | ||
1953 | page_start = page->index << PAGE_CACHE_SHIFT; | ||
1954 | page_end = page_start + PAGE_CACHE_SIZE - 1; | ||
1955 | |||
1956 | lock_extent(em_tree, page_start, page_end, GFP_NOFS); | ||
1957 | set_extent_delalloc(em_tree, page_start, | ||
1958 | page_end, GFP_NOFS); | ||
1959 | unlock_extent(em_tree, page_start, page_end, GFP_NOFS); | ||
1960 | set_page_dirty(page); | ||
1961 | unlock_page(page); | ||
1962 | page_cache_release(page); | ||
1963 | balance_dirty_pages_ratelimited_nr(inode->i_mapping, 1); | ||
1964 | } | ||
1965 | |||
1966 | out_unlock: | ||
1967 | mutex_unlock(&inode->i_mutex); | ||
1968 | return 0; | ||
1969 | } | ||
1970 | |||
1907 | int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int | 1971 | int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int |
1908 | cmd, unsigned long arg) | 1972 | cmd, unsigned long arg) |
1909 | { | 1973 | { |
@@ -1948,10 +2012,14 @@ int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int | |||
1948 | break; | 2012 | break; |
1949 | 2013 | ||
1950 | case BTRFS_IOC_DEFRAG: | 2014 | case BTRFS_IOC_DEFRAG: |
1951 | mutex_lock(&root->fs_info->fs_mutex); | 2015 | if (S_ISDIR(inode->i_mode)) { |
1952 | btrfs_defrag_root(root, 0); | 2016 | mutex_lock(&root->fs_info->fs_mutex); |
1953 | btrfs_defrag_root(root->fs_info->extent_root, 0); | 2017 | btrfs_defrag_root(root, 0); |
1954 | mutex_unlock(&root->fs_info->fs_mutex); | 2018 | btrfs_defrag_root(root->fs_info->extent_root, 0); |
2019 | mutex_unlock(&root->fs_info->fs_mutex); | ||
2020 | } else if (S_ISREG(inode->i_mode)) { | ||
2021 | btrfs_defrag_file(filp); | ||
2022 | } | ||
1955 | ret = 0; | 2023 | ret = 0; |
1956 | break; | 2024 | break; |
1957 | default: | 2025 | default: |
@@ -2018,7 +2086,7 @@ void btrfs_destroy_cachep(void) | |||
2018 | kmem_cache_destroy(btrfs_path_cachep); | 2086 | kmem_cache_destroy(btrfs_path_cachep); |
2019 | } | 2087 | } |
2020 | 2088 | ||
2021 | static struct kmem_cache *cache_create(const char *name, size_t size, | 2089 | struct kmem_cache *btrfs_cache_create(const char *name, size_t size, |
2022 | unsigned long extra_flags, | 2090 | unsigned long extra_flags, |
2023 | void (*ctor)(void *, struct kmem_cache *, | 2091 | void (*ctor)(void *, struct kmem_cache *, |
2024 | unsigned long)) | 2092 | unsigned long)) |
@@ -2033,27 +2101,28 @@ static struct kmem_cache *cache_create(const char *name, size_t size, | |||
2033 | 2101 | ||
2034 | int btrfs_init_cachep(void) | 2102 | int btrfs_init_cachep(void) |
2035 | { | 2103 | { |
2036 | btrfs_inode_cachep = cache_create("btrfs_inode_cache", | 2104 | btrfs_inode_cachep = btrfs_cache_create("btrfs_inode_cache", |
2037 | sizeof(struct btrfs_inode), | 2105 | sizeof(struct btrfs_inode), |
2038 | 0, init_once); | 2106 | 0, init_once); |
2039 | if (!btrfs_inode_cachep) | 2107 | if (!btrfs_inode_cachep) |
2040 | goto fail; | 2108 | goto fail; |
2041 | btrfs_trans_handle_cachep = cache_create("btrfs_trans_handle_cache", | 2109 | btrfs_trans_handle_cachep = |
2042 | sizeof(struct btrfs_trans_handle), | 2110 | btrfs_cache_create("btrfs_trans_handle_cache", |
2043 | 0, NULL); | 2111 | sizeof(struct btrfs_trans_handle), |
2112 | 0, NULL); | ||
2044 | if (!btrfs_trans_handle_cachep) | 2113 | if (!btrfs_trans_handle_cachep) |
2045 | goto fail; | 2114 | goto fail; |
2046 | btrfs_transaction_cachep = cache_create("btrfs_transaction_cache", | 2115 | btrfs_transaction_cachep = btrfs_cache_create("btrfs_transaction_cache", |
2047 | sizeof(struct btrfs_transaction), | 2116 | sizeof(struct btrfs_transaction), |
2048 | 0, NULL); | 2117 | 0, NULL); |
2049 | if (!btrfs_transaction_cachep) | 2118 | if (!btrfs_transaction_cachep) |
2050 | goto fail; | 2119 | goto fail; |
2051 | btrfs_path_cachep = cache_create("btrfs_path_cache", | 2120 | btrfs_path_cachep = btrfs_cache_create("btrfs_path_cache", |
2052 | sizeof(struct btrfs_transaction), | 2121 | sizeof(struct btrfs_transaction), |
2053 | 0, NULL); | 2122 | 0, NULL); |
2054 | if (!btrfs_path_cachep) | 2123 | if (!btrfs_path_cachep) |
2055 | goto fail; | 2124 | goto fail; |
2056 | btrfs_bit_radix_cachep = cache_create("btrfs_radix", 256, | 2125 | btrfs_bit_radix_cachep = btrfs_cache_create("btrfs_radix", 256, |
2057 | SLAB_DESTROY_BY_RCU, NULL); | 2126 | SLAB_DESTROY_BY_RCU, NULL); |
2058 | if (!btrfs_bit_radix_cachep) | 2127 | if (!btrfs_bit_radix_cachep) |
2059 | goto fail; | 2128 | goto fail; |
diff --git a/fs/btrfs/tree-defrag.c b/fs/btrfs/tree-defrag.c index 35fd20d24645..420597127ed1 100644 --- a/fs/btrfs/tree-defrag.c +++ b/fs/btrfs/tree-defrag.c | |||
@@ -113,6 +113,8 @@ static int defrag_walk_down(struct btrfs_trans_handle *trans, | |||
113 | } | 113 | } |
114 | WARN_ON(*level < 0); | 114 | WARN_ON(*level < 0); |
115 | WARN_ON(*level >= BTRFS_MAX_LEVEL); | 115 | WARN_ON(*level >= BTRFS_MAX_LEVEL); |
116 | clear_buffer_defrag(path->nodes[*level]); | ||
117 | clear_buffer_defrag_done(path->nodes[*level]); | ||
116 | btrfs_block_release(root, path->nodes[*level]); | 118 | btrfs_block_release(root, path->nodes[*level]); |
117 | path->nodes[*level] = NULL; | 119 | path->nodes[*level] = NULL; |
118 | *level += 1; | 120 | *level += 1; |
@@ -143,6 +145,7 @@ static int defrag_walk_up(struct btrfs_trans_handle *trans, | |||
143 | return 0; | 145 | return 0; |
144 | } else { | 146 | } else { |
145 | clear_buffer_defrag(path->nodes[*level]); | 147 | clear_buffer_defrag(path->nodes[*level]); |
148 | clear_buffer_defrag_done(path->nodes[*level]); | ||
146 | btrfs_block_release(root, path->nodes[*level]); | 149 | btrfs_block_release(root, path->nodes[*level]); |
147 | path->nodes[*level] = NULL; | 150 | path->nodes[*level] = NULL; |
148 | *level = i + 1; | 151 | *level = i + 1; |