diff options
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/extent_map.c | 94 | ||||
-rw-r--r-- | fs/btrfs/extent_map.h | 15 | ||||
-rw-r--r-- | fs/btrfs/inode.c | 98 |
3 files changed, 193 insertions, 14 deletions
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index a9c7419615b9..6053f9db0739 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c | |||
@@ -57,7 +57,7 @@ void extent_map_tree_init(struct extent_map_tree *tree, | |||
57 | { | 57 | { |
58 | tree->map.rb_node = NULL; | 58 | tree->map.rb_node = NULL; |
59 | tree->state.rb_node = NULL; | 59 | tree->state.rb_node = NULL; |
60 | tree->fill_delalloc = NULL; | 60 | tree->ops = NULL; |
61 | rwlock_init(&tree->lock); | 61 | rwlock_init(&tree->lock); |
62 | tree->mapping = mapping; | 62 | tree->mapping = mapping; |
63 | } | 63 | } |
@@ -93,6 +93,7 @@ struct extent_state *alloc_extent_state(gfp_t mask) | |||
93 | return state; | 93 | return state; |
94 | state->state = 0; | 94 | state->state = 0; |
95 | state->in_tree = 0; | 95 | state->in_tree = 0; |
96 | state->private = 0; | ||
96 | atomic_set(&state->refs, 1); | 97 | atomic_set(&state->refs, 1); |
97 | init_waitqueue_head(&state->wq); | 98 | init_waitqueue_head(&state->wq); |
98 | return state; | 99 | return state; |
@@ -1034,6 +1035,61 @@ int unlock_range(struct extent_map_tree *tree, u64 start, u64 end) | |||
1034 | } | 1035 | } |
1035 | EXPORT_SYMBOL(unlock_range); | 1036 | EXPORT_SYMBOL(unlock_range); |
1036 | 1037 | ||
1038 | int set_state_private(struct extent_map_tree *tree, u64 start, u64 private) | ||
1039 | { | ||
1040 | struct rb_node *node; | ||
1041 | struct extent_state *state; | ||
1042 | int ret = 0; | ||
1043 | |||
1044 | write_lock_irq(&tree->lock); | ||
1045 | /* | ||
1046 | * this search will find all the extents that end after | ||
1047 | * our range starts. | ||
1048 | */ | ||
1049 | node = tree_search(&tree->state, start); | ||
1050 | if (!node || IS_ERR(node)) { | ||
1051 | ret = -ENOENT; | ||
1052 | goto out; | ||
1053 | } | ||
1054 | state = rb_entry(node, struct extent_state, rb_node); | ||
1055 | if (state->start != start) { | ||
1056 | ret = -ENOENT; | ||
1057 | goto out; | ||
1058 | } | ||
1059 | state->private = private; | ||
1060 | out: | ||
1061 | write_unlock_irq(&tree->lock); | ||
1062 | return ret; | ||
1063 | |||
1064 | } | ||
1065 | |||
1066 | int get_state_private(struct extent_map_tree *tree, u64 start, u64 *private) | ||
1067 | { | ||
1068 | struct rb_node *node; | ||
1069 | struct extent_state *state; | ||
1070 | int ret = 0; | ||
1071 | |||
1072 | read_lock_irq(&tree->lock); | ||
1073 | /* | ||
1074 | * this search will find all the extents that end after | ||
1075 | * our range starts. | ||
1076 | */ | ||
1077 | node = tree_search(&tree->state, start); | ||
1078 | if (!node || IS_ERR(node)) { | ||
1079 | ret = -ENOENT; | ||
1080 | goto out; | ||
1081 | } | ||
1082 | state = rb_entry(node, struct extent_state, rb_node); | ||
1083 | if (state->start != start) { | ||
1084 | ret = -ENOENT; | ||
1085 | goto out; | ||
1086 | } | ||
1087 | *private = state->private; | ||
1088 | out: | ||
1089 | read_unlock_irq(&tree->lock); | ||
1090 | return ret; | ||
1091 | } | ||
1092 | |||
1037 | /* | 1093 | /* |
1038 | * searches a range in the state tree for a given mask. | 1094 | * searches a range in the state tree for a given mask. |
1039 | * If 'filled' == 1, this returns 1 only if ever extent in the tree | 1095 | * If 'filled' == 1, this returns 1 only if ever extent in the tree |
@@ -1185,12 +1241,13 @@ static int end_bio_extent_writepage(struct bio *bio, | |||
1185 | static int end_bio_extent_readpage(struct bio *bio, | 1241 | static int end_bio_extent_readpage(struct bio *bio, |
1186 | unsigned int bytes_done, int err) | 1242 | unsigned int bytes_done, int err) |
1187 | { | 1243 | { |
1188 | const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); | 1244 | int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); |
1189 | struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1; | 1245 | struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1; |
1190 | struct extent_map_tree *tree = bio->bi_private; | 1246 | struct extent_map_tree *tree = bio->bi_private; |
1191 | u64 start; | 1247 | u64 start; |
1192 | u64 end; | 1248 | u64 end; |
1193 | int whole_page; | 1249 | int whole_page; |
1250 | int ret; | ||
1194 | 1251 | ||
1195 | if (bio->bi_size) | 1252 | if (bio->bi_size) |
1196 | return 1; | 1253 | return 1; |
@@ -1208,6 +1265,11 @@ static int end_bio_extent_readpage(struct bio *bio, | |||
1208 | if (--bvec >= bio->bi_io_vec) | 1265 | if (--bvec >= bio->bi_io_vec) |
1209 | prefetchw(&bvec->bv_page->flags); | 1266 | prefetchw(&bvec->bv_page->flags); |
1210 | 1267 | ||
1268 | if (uptodate && tree->ops && tree->ops->readpage_end_io_hook) { | ||
1269 | ret = tree->ops->readpage_end_io_hook(page, start, end); | ||
1270 | if (ret) | ||
1271 | uptodate = 0; | ||
1272 | } | ||
1211 | if (uptodate) { | 1273 | if (uptodate) { |
1212 | set_extent_uptodate(tree, start, end, GFP_ATOMIC); | 1274 | set_extent_uptodate(tree, start, end, GFP_ATOMIC); |
1213 | if (whole_page) | 1275 | if (whole_page) |
@@ -1388,9 +1450,16 @@ int extent_read_full_page(struct extent_map_tree *tree, struct page *page, | |||
1388 | continue; | 1450 | continue; |
1389 | } | 1451 | } |
1390 | 1452 | ||
1391 | ret = submit_extent_page(READ, tree, page, | 1453 | ret = 0; |
1392 | sector, iosize, page_offset, bdev, | 1454 | if (tree->ops && tree->ops->readpage_io_hook) { |
1393 | end_bio_extent_readpage); | 1455 | ret = tree->ops->readpage_io_hook(page, cur, |
1456 | cur + iosize - 1); | ||
1457 | } | ||
1458 | if (!ret) { | ||
1459 | ret = submit_extent_page(READ, tree, page, | ||
1460 | sector, iosize, page_offset, | ||
1461 | bdev, end_bio_extent_readpage); | ||
1462 | } | ||
1394 | if (ret) | 1463 | if (ret) |
1395 | SetPageError(page); | 1464 | SetPageError(page); |
1396 | cur = cur + iosize; | 1465 | cur = cur + iosize; |
@@ -1462,7 +1531,7 @@ int extent_write_full_page(struct extent_map_tree *tree, struct page *page, | |||
1462 | &delalloc_end, | 1531 | &delalloc_end, |
1463 | 128 * 1024 * 1024); | 1532 | 128 * 1024 * 1024); |
1464 | if (nr_delalloc) { | 1533 | if (nr_delalloc) { |
1465 | tree->fill_delalloc(inode, start, delalloc_end); | 1534 | tree->ops->fill_delalloc(inode, start, delalloc_end); |
1466 | if (delalloc_end >= page_end + 1) { | 1535 | if (delalloc_end >= page_end + 1) { |
1467 | clear_extent_bit(tree, page_end + 1, delalloc_end, | 1536 | clear_extent_bit(tree, page_end + 1, delalloc_end, |
1468 | EXTENT_LOCKED | EXTENT_DELALLOC, | 1537 | EXTENT_LOCKED | EXTENT_DELALLOC, |
@@ -1528,12 +1597,17 @@ int extent_write_full_page(struct extent_map_tree *tree, struct page *page, | |||
1528 | continue; | 1597 | continue; |
1529 | } | 1598 | } |
1530 | clear_extent_dirty(tree, cur, cur + iosize - 1, GFP_NOFS); | 1599 | clear_extent_dirty(tree, cur, cur + iosize - 1, GFP_NOFS); |
1531 | set_range_writeback(tree, cur, cur + iosize - 1); | 1600 | ret = tree->ops->writepage_io_hook(page, cur, cur + iosize - 1); |
1532 | ret = submit_extent_page(WRITE, tree, page, | ||
1533 | sector, iosize, page_offset, bdev, | ||
1534 | end_bio_extent_writepage); | ||
1535 | if (ret) | 1601 | if (ret) |
1536 | SetPageError(page); | 1602 | SetPageError(page); |
1603 | else { | ||
1604 | set_range_writeback(tree, cur, cur + iosize - 1); | ||
1605 | ret = submit_extent_page(WRITE, tree, page, sector, | ||
1606 | iosize, page_offset, bdev, | ||
1607 | end_bio_extent_writepage); | ||
1608 | if (ret) | ||
1609 | SetPageError(page); | ||
1610 | } | ||
1537 | cur = cur + iosize; | 1611 | cur = cur + iosize; |
1538 | page_offset += iosize; | 1612 | page_offset += iosize; |
1539 | nr++; | 1613 | nr++; |
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h index e91a2e9619ee..0eae6c420acb 100644 --- a/fs/btrfs/extent_map.h +++ b/fs/btrfs/extent_map.h | |||
@@ -6,12 +6,19 @@ | |||
6 | #define EXTENT_MAP_INLINE (u64)-2 | 6 | #define EXTENT_MAP_INLINE (u64)-2 |
7 | #define EXTENT_MAP_DELALLOC (u64)-1 | 7 | #define EXTENT_MAP_DELALLOC (u64)-1 |
8 | 8 | ||
9 | struct extent_map_ops { | ||
10 | int (*fill_delalloc)(struct inode *inode, u64 start, u64 end); | ||
11 | int (*writepage_io_hook)(struct page *page, u64 start, u64 end); | ||
12 | int (*readpage_io_hook)(struct page *page, u64 start, u64 end); | ||
13 | int (*readpage_end_io_hook)(struct page *page, u64 start, u64 end); | ||
14 | }; | ||
15 | |||
9 | struct extent_map_tree { | 16 | struct extent_map_tree { |
10 | struct rb_root map; | 17 | struct rb_root map; |
11 | struct rb_root state; | 18 | struct rb_root state; |
12 | struct address_space *mapping; | 19 | struct address_space *mapping; |
13 | rwlock_t lock; | 20 | rwlock_t lock; |
14 | int (*fill_delalloc)(struct inode *inode, u64 start, u64 end); | 21 | struct extent_map_ops *ops; |
15 | }; | 22 | }; |
16 | 23 | ||
17 | /* note, this must start with the same fields as fs/extent_map.c:tree_entry */ | 24 | /* note, this must start with the same fields as fs/extent_map.c:tree_entry */ |
@@ -36,6 +43,10 @@ struct extent_state { | |||
36 | wait_queue_head_t wq; | 43 | wait_queue_head_t wq; |
37 | atomic_t refs; | 44 | atomic_t refs; |
38 | unsigned long state; | 45 | unsigned long state; |
46 | |||
47 | /* for use by the FS */ | ||
48 | u64 private; | ||
49 | |||
39 | struct list_head list; | 50 | struct list_head list; |
40 | }; | 51 | }; |
41 | 52 | ||
@@ -89,4 +100,6 @@ int extent_commit_write(struct extent_map_tree *tree, | |||
89 | struct inode *inode, struct page *page, | 100 | struct inode *inode, struct page *page, |
90 | unsigned from, unsigned to); | 101 | unsigned from, unsigned to); |
91 | int set_range_dirty(struct extent_map_tree *tree, u64 start, u64 end); | 102 | int set_range_dirty(struct extent_map_tree *tree, u64 start, u64 end); |
103 | int set_state_private(struct extent_map_tree *tree, u64 start, u64 private); | ||
104 | int get_state_private(struct extent_map_tree *tree, u64 start, u64 *private); | ||
92 | #endif | 105 | #endif |
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 483eea69b0a6..3ee6b2fadf58 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -52,6 +52,7 @@ static struct inode_operations btrfs_file_inode_operations; | |||
52 | static struct address_space_operations btrfs_aops; | 52 | static struct address_space_operations btrfs_aops; |
53 | static struct address_space_operations btrfs_symlink_aops; | 53 | static struct address_space_operations btrfs_symlink_aops; |
54 | static struct file_operations btrfs_dir_file_operations; | 54 | static struct file_operations btrfs_dir_file_operations; |
55 | static struct extent_map_ops btrfs_extent_map_ops; | ||
55 | 56 | ||
56 | static struct kmem_cache *btrfs_inode_cachep; | 57 | static struct kmem_cache *btrfs_inode_cachep; |
57 | struct kmem_cache *btrfs_trans_handle_cachep; | 58 | struct kmem_cache *btrfs_trans_handle_cachep; |
@@ -103,6 +104,90 @@ out: | |||
103 | return ret; | 104 | return ret; |
104 | } | 105 | } |
105 | 106 | ||
107 | int btrfs_writepage_io_hook(struct page *page, u64 start, u64 end) | ||
108 | { | ||
109 | struct inode *inode = page->mapping->host; | ||
110 | struct btrfs_root *root = BTRFS_I(inode)->root; | ||
111 | struct btrfs_trans_handle *trans; | ||
112 | char *kaddr; | ||
113 | int ret; | ||
114 | u64 page_start = page->index << PAGE_CACHE_SHIFT; | ||
115 | size_t offset = start - page_start; | ||
116 | |||
117 | mutex_lock(&root->fs_info->fs_mutex); | ||
118 | trans = btrfs_start_transaction(root, 1); | ||
119 | btrfs_set_trans_block_group(trans, inode); | ||
120 | kaddr = kmap(page); | ||
121 | btrfs_csum_file_block(trans, root, inode->i_ino, | ||
122 | start, kaddr + offset, end - start + 1); | ||
123 | kunmap(page); | ||
124 | ret = btrfs_end_transaction(trans, root); | ||
125 | BUG_ON(ret); | ||
126 | mutex_unlock(&root->fs_info->fs_mutex); | ||
127 | return ret; | ||
128 | } | ||
129 | |||
130 | int btrfs_readpage_io_hook(struct page *page, u64 start, u64 end) | ||
131 | { | ||
132 | int ret = 0; | ||
133 | struct inode *inode = page->mapping->host; | ||
134 | struct btrfs_root *root = BTRFS_I(inode)->root; | ||
135 | struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree; | ||
136 | struct btrfs_csum_item *item; | ||
137 | struct btrfs_path *path = NULL; | ||
138 | u64 private; | ||
139 | |||
140 | mutex_lock(&root->fs_info->fs_mutex); | ||
141 | path = btrfs_alloc_path(); | ||
142 | item = btrfs_lookup_csum(NULL, root, path, inode->i_ino, start, 0); | ||
143 | if (IS_ERR(item)) { | ||
144 | ret = PTR_ERR(item); | ||
145 | /* a csum that isn't present is a preallocated region. */ | ||
146 | if (ret == -ENOENT || ret == -EFBIG) | ||
147 | ret = 0; | ||
148 | private = 0; | ||
149 | goto out; | ||
150 | } | ||
151 | memcpy((char *)&private, &item->csum, BTRFS_CRC32_SIZE); | ||
152 | set_state_private(em_tree, start, private); | ||
153 | out: | ||
154 | if (path) | ||
155 | btrfs_free_path(path); | ||
156 | mutex_unlock(&root->fs_info->fs_mutex); | ||
157 | return ret; | ||
158 | } | ||
159 | |||
160 | int btrfs_readpage_end_io_hook(struct page *page, u64 start, u64 end) | ||
161 | { | ||
162 | char csum[BTRFS_CRC32_SIZE]; | ||
163 | size_t offset = start - (page->index << PAGE_CACHE_SHIFT); | ||
164 | struct inode *inode = page->mapping->host; | ||
165 | struct btrfs_root *root = BTRFS_I(inode)->root; | ||
166 | struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree; | ||
167 | char *kaddr; | ||
168 | u64 private; | ||
169 | int ret; | ||
170 | |||
171 | ret = get_state_private(em_tree, start, &private); | ||
172 | kaddr = kmap_atomic(page, KM_IRQ0); | ||
173 | if (ret) { | ||
174 | goto zeroit; | ||
175 | } | ||
176 | ret = btrfs_csum_data(root, kaddr + offset, end - start + 1, csum); | ||
177 | BUG_ON(ret); | ||
178 | if (memcmp(csum, &private, BTRFS_CRC32_SIZE)) { | ||
179 | goto zeroit; | ||
180 | } | ||
181 | kunmap_atomic(kaddr, KM_IRQ0); | ||
182 | return 0; | ||
183 | |||
184 | zeroit: | ||
185 | printk("btrfs csum failed ino %lu off %llu\n", | ||
186 | page->mapping->host->i_ino, (unsigned long long)start); | ||
187 | memset(kaddr + offset, 1, end - start + 1); flush_dcache_page(page); | ||
188 | kunmap_atomic(kaddr, KM_IRQ0); | ||
189 | return 0; | ||
190 | } | ||
106 | 191 | ||
107 | void btrfs_read_locked_inode(struct inode *inode) | 192 | void btrfs_read_locked_inode(struct inode *inode) |
108 | { | 193 | { |
@@ -155,7 +240,7 @@ void btrfs_read_locked_inode(struct inode *inode) | |||
155 | switch (inode->i_mode & S_IFMT) { | 240 | switch (inode->i_mode & S_IFMT) { |
156 | case S_IFREG: | 241 | case S_IFREG: |
157 | inode->i_mapping->a_ops = &btrfs_aops; | 242 | inode->i_mapping->a_ops = &btrfs_aops; |
158 | BTRFS_I(inode)->extent_tree.fill_delalloc = run_delalloc_range; | 243 | BTRFS_I(inode)->extent_tree.ops = &btrfs_extent_map_ops; |
159 | inode->i_fop = &btrfs_file_operations; | 244 | inode->i_fop = &btrfs_file_operations; |
160 | inode->i_op = &btrfs_file_inode_operations; | 245 | inode->i_op = &btrfs_file_inode_operations; |
161 | break; | 246 | break; |
@@ -1148,7 +1233,7 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry, | |||
1148 | inode->i_op = &btrfs_file_inode_operations; | 1233 | inode->i_op = &btrfs_file_inode_operations; |
1149 | extent_map_tree_init(&BTRFS_I(inode)->extent_tree, | 1234 | extent_map_tree_init(&BTRFS_I(inode)->extent_tree, |
1150 | inode->i_mapping, GFP_NOFS); | 1235 | inode->i_mapping, GFP_NOFS); |
1151 | BTRFS_I(inode)->extent_tree.fill_delalloc = run_delalloc_range; | 1236 | BTRFS_I(inode)->extent_tree.ops = &btrfs_extent_map_ops; |
1152 | } | 1237 | } |
1153 | dir->i_sb->s_dirt = 1; | 1238 | dir->i_sb->s_dirt = 1; |
1154 | btrfs_update_inode_block_group(trans, inode); | 1239 | btrfs_update_inode_block_group(trans, inode); |
@@ -2286,7 +2371,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry, | |||
2286 | inode->i_op = &btrfs_file_inode_operations; | 2371 | inode->i_op = &btrfs_file_inode_operations; |
2287 | extent_map_tree_init(&BTRFS_I(inode)->extent_tree, | 2372 | extent_map_tree_init(&BTRFS_I(inode)->extent_tree, |
2288 | inode->i_mapping, GFP_NOFS); | 2373 | inode->i_mapping, GFP_NOFS); |
2289 | BTRFS_I(inode)->extent_tree.fill_delalloc = run_delalloc_range; | 2374 | BTRFS_I(inode)->extent_tree.ops = &btrfs_extent_map_ops; |
2290 | } | 2375 | } |
2291 | dir->i_sb->s_dirt = 1; | 2376 | dir->i_sb->s_dirt = 1; |
2292 | btrfs_update_inode_block_group(trans, inode); | 2377 | btrfs_update_inode_block_group(trans, inode); |
@@ -2362,6 +2447,13 @@ static struct file_operations btrfs_dir_file_operations = { | |||
2362 | #endif | 2447 | #endif |
2363 | }; | 2448 | }; |
2364 | 2449 | ||
2450 | static struct extent_map_ops btrfs_extent_map_ops = { | ||
2451 | .fill_delalloc = run_delalloc_range, | ||
2452 | .writepage_io_hook = btrfs_writepage_io_hook, | ||
2453 | .readpage_io_hook = btrfs_readpage_io_hook, | ||
2454 | .readpage_end_io_hook = btrfs_readpage_end_io_hook, | ||
2455 | }; | ||
2456 | |||
2365 | static struct address_space_operations btrfs_aops = { | 2457 | static struct address_space_operations btrfs_aops = { |
2366 | .readpage = btrfs_readpage, | 2458 | .readpage = btrfs_readpage, |
2367 | .writepage = btrfs_writepage, | 2459 | .writepage = btrfs_writepage, |