aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorZheng Liu <wenqing.lz@taobao.com>2013-02-18 00:26:51 -0500
committerTheodore Ts'o <tytso@mit.edu>2013-02-18 00:26:51 -0500
commitfdc0212e86ca15c5cfed77088af7cc5eb79ccbc7 (patch)
tree58919d4c7e42dc0edba714c3dcedd88135560c6b /fs/ext4
parent06b0c886214a223dde7b21cbfc3008fd20a8ce16 (diff)
ext4: add physical block and status member into extent status tree
This commit adds two members in extent_status structure to let it record physical block and extent status. Here es_pblk is used to record both of them because physical block only has 48 bits. So extent status could be stashed into it so that we can save some memory. Now written, unwritten, delayed and hole are defined as status. Due to new member is added into extent status tree, all interfaces need to be adjusted. Signed-off-by: Zheng Liu <wenqing.lz@taobao.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Reviewed-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/extents_status.c67
-rw-r--r--fs/ext4/extents_status.h64
-rw-r--r--fs/ext4/inode.c3
3 files changed, 120 insertions, 14 deletions
diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c
index c9921cf4f817..1f5fd44993e9 100644
--- a/fs/ext4/extents_status.c
+++ b/fs/ext4/extents_status.c
@@ -179,7 +179,9 @@ static void ext4_es_print_tree(struct inode *inode)
179 while (node) { 179 while (node) {
180 struct extent_status *es; 180 struct extent_status *es;
181 es = rb_entry(node, struct extent_status, rb_node); 181 es = rb_entry(node, struct extent_status, rb_node);
182 printk(KERN_DEBUG " [%u/%u)", es->es_lblk, es->es_len); 182 printk(KERN_DEBUG " [%u/%u) %llu %llx",
183 es->es_lblk, es->es_len,
184 ext4_es_pblock(es), ext4_es_status(es));
183 node = rb_next(node); 185 node = rb_next(node);
184 } 186 }
185 printk(KERN_DEBUG "\n"); 187 printk(KERN_DEBUG "\n");
@@ -234,7 +236,7 @@ static struct extent_status *__es_tree_search(struct rb_root *root,
234 * @es: delayed extent that we found 236 * @es: delayed extent that we found
235 * 237 *
236 * Returns the first block of the next extent after es, otherwise 238 * Returns the first block of the next extent after es, otherwise
237 * EXT_MAX_BLOCKS if no delay extent is found. 239 * EXT_MAX_BLOCKS if no extent is found.
238 * Delayed extent is returned via @es. 240 * Delayed extent is returned via @es.
239 */ 241 */
240ext4_lblk_t ext4_es_find_extent(struct inode *inode, struct extent_status *es) 242ext4_lblk_t ext4_es_find_extent(struct inode *inode, struct extent_status *es)
@@ -249,17 +251,18 @@ ext4_lblk_t ext4_es_find_extent(struct inode *inode, struct extent_status *es)
249 read_lock(&EXT4_I(inode)->i_es_lock); 251 read_lock(&EXT4_I(inode)->i_es_lock);
250 tree = &EXT4_I(inode)->i_es_tree; 252 tree = &EXT4_I(inode)->i_es_tree;
251 253
252 /* find delay extent in cache firstly */ 254 /* find extent in cache firstly */
255 es->es_len = es->es_pblk = 0;
253 if (tree->cache_es) { 256 if (tree->cache_es) {
254 es1 = tree->cache_es; 257 es1 = tree->cache_es;
255 if (in_range(es->es_lblk, es1->es_lblk, es1->es_len)) { 258 if (in_range(es->es_lblk, es1->es_lblk, es1->es_len)) {
256 es_debug("%u cached by [%u/%u)\n", 259 es_debug("%u cached by [%u/%u) %llu %llx\n",
257 es->es_lblk, es1->es_lblk, es1->es_len); 260 es->es_lblk, es1->es_lblk, es1->es_len,
261 ext4_es_pblock(es1), ext4_es_status(es1));
258 goto out; 262 goto out;
259 } 263 }
260 } 264 }
261 265
262 es->es_len = 0;
263 es1 = __es_tree_search(&tree->root, es->es_lblk); 266 es1 = __es_tree_search(&tree->root, es->es_lblk);
264 267
265out: 268out:
@@ -267,6 +270,7 @@ out:
267 tree->cache_es = es1; 270 tree->cache_es = es1;
268 es->es_lblk = es1->es_lblk; 271 es->es_lblk = es1->es_lblk;
269 es->es_len = es1->es_len; 272 es->es_len = es1->es_len;
273 es->es_pblk = es1->es_pblk;
270 node = rb_next(&es1->rb_node); 274 node = rb_next(&es1->rb_node);
271 if (node) { 275 if (node) {
272 es1 = rb_entry(node, struct extent_status, rb_node); 276 es1 = rb_entry(node, struct extent_status, rb_node);
@@ -281,7 +285,7 @@ out:
281} 285}
282 286
283static struct extent_status * 287static struct extent_status *
284ext4_es_alloc_extent(ext4_lblk_t lblk, ext4_lblk_t len) 288ext4_es_alloc_extent(ext4_lblk_t lblk, ext4_lblk_t len, ext4_fsblk_t pblk)
285{ 289{
286 struct extent_status *es; 290 struct extent_status *es;
287 es = kmem_cache_alloc(ext4_es_cachep, GFP_ATOMIC); 291 es = kmem_cache_alloc(ext4_es_cachep, GFP_ATOMIC);
@@ -289,6 +293,7 @@ ext4_es_alloc_extent(ext4_lblk_t lblk, ext4_lblk_t len)
289 return NULL; 293 return NULL;
290 es->es_lblk = lblk; 294 es->es_lblk = lblk;
291 es->es_len = len; 295 es->es_len = len;
296 es->es_pblk = pblk;
292 return es; 297 return es;
293} 298}
294 299
@@ -301,6 +306,8 @@ static void ext4_es_free_extent(struct extent_status *es)
301 * Check whether or not two extents can be merged 306 * Check whether or not two extents can be merged
302 * Condition: 307 * Condition:
303 * - logical block number is contiguous 308 * - logical block number is contiguous
309 * - physical block number is contiguous
310 * - status is equal
304 */ 311 */
305static int ext4_es_can_be_merged(struct extent_status *es1, 312static int ext4_es_can_be_merged(struct extent_status *es1,
306 struct extent_status *es2) 313 struct extent_status *es2)
@@ -308,6 +315,13 @@ static int ext4_es_can_be_merged(struct extent_status *es1,
308 if (es1->es_lblk + es1->es_len != es2->es_lblk) 315 if (es1->es_lblk + es1->es_len != es2->es_lblk)
309 return 0; 316 return 0;
310 317
318 if (ext4_es_status(es1) != ext4_es_status(es2))
319 return 0;
320
321 if ((ext4_es_is_written(es1) || ext4_es_is_unwritten(es1)) &&
322 (ext4_es_pblock(es1) + es1->es_len != ext4_es_pblock(es2)))
323 return 0;
324
311 return 1; 325 return 1;
312} 326}
313 327
@@ -371,6 +385,10 @@ static int __es_insert_extent(struct ext4_es_tree *tree,
371 */ 385 */
372 es->es_lblk = newes->es_lblk; 386 es->es_lblk = newes->es_lblk;
373 es->es_len += newes->es_len; 387 es->es_len += newes->es_len;
388 if (ext4_es_is_written(es) ||
389 ext4_es_is_unwritten(es))
390 ext4_es_store_pblock(es,
391 newes->es_pblk);
374 es = ext4_es_try_to_merge_left(tree, es); 392 es = ext4_es_try_to_merge_left(tree, es);
375 goto out; 393 goto out;
376 } 394 }
@@ -388,7 +406,8 @@ static int __es_insert_extent(struct ext4_es_tree *tree,
388 } 406 }
389 } 407 }
390 408
391 es = ext4_es_alloc_extent(newes->es_lblk, newes->es_len); 409 es = ext4_es_alloc_extent(newes->es_lblk, newes->es_len,
410 newes->es_pblk);
392 if (!es) 411 if (!es)
393 return -ENOMEM; 412 return -ENOMEM;
394 rb_link_node(&es->rb_node, parent, p); 413 rb_link_node(&es->rb_node, parent, p);
@@ -408,21 +427,24 @@ out:
408 * Return 0 on success, error code on failure. 427 * Return 0 on success, error code on failure.
409 */ 428 */
410int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, 429int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
411 ext4_lblk_t len) 430 ext4_lblk_t len, ext4_fsblk_t pblk,
431 unsigned long long status)
412{ 432{
413 struct ext4_es_tree *tree; 433 struct ext4_es_tree *tree;
414 struct extent_status newes; 434 struct extent_status newes;
415 ext4_lblk_t end = lblk + len - 1; 435 ext4_lblk_t end = lblk + len - 1;
416 int err = 0; 436 int err = 0;
417 437
418 trace_ext4_es_insert_extent(inode, lblk, len); 438 es_debug("add [%u/%u) %llu %llx to extent status tree of inode %lu\n",
419 es_debug("add [%u/%u) to extent status tree of inode %lu\n", 439 lblk, len, pblk, status, inode->i_ino);
420 lblk, len, inode->i_ino);
421 440
422 BUG_ON(end < lblk); 441 BUG_ON(end < lblk);
423 442
424 newes.es_lblk = lblk; 443 newes.es_lblk = lblk;
425 newes.es_len = len; 444 newes.es_len = len;
445 ext4_es_store_pblock(&newes, pblk);
446 ext4_es_store_status(&newes, status);
447 trace_ext4_es_insert_extent(inode, &newes);
426 448
427 write_lock(&EXT4_I(inode)->i_es_lock); 449 write_lock(&EXT4_I(inode)->i_es_lock);
428 tree = &EXT4_I(inode)->i_es_tree; 450 tree = &EXT4_I(inode)->i_es_tree;
@@ -446,6 +468,7 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk,
446 struct extent_status *es; 468 struct extent_status *es;
447 struct extent_status orig_es; 469 struct extent_status orig_es;
448 ext4_lblk_t len1, len2; 470 ext4_lblk_t len1, len2;
471 ext4_fsblk_t block;
449 int err = 0; 472 int err = 0;
450 473
451 es = __es_tree_search(&tree->root, lblk); 474 es = __es_tree_search(&tree->root, lblk);
@@ -459,6 +482,8 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk,
459 482
460 orig_es.es_lblk = es->es_lblk; 483 orig_es.es_lblk = es->es_lblk;
461 orig_es.es_len = es->es_len; 484 orig_es.es_len = es->es_len;
485 orig_es.es_pblk = es->es_pblk;
486
462 len1 = lblk > es->es_lblk ? lblk - es->es_lblk : 0; 487 len1 = lblk > es->es_lblk ? lblk - es->es_lblk : 0;
463 len2 = ext4_es_end(es) > end ? ext4_es_end(es) - end : 0; 488 len2 = ext4_es_end(es) > end ? ext4_es_end(es) - end : 0;
464 if (len1 > 0) 489 if (len1 > 0)
@@ -469,6 +494,13 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk,
469 494
470 newes.es_lblk = end + 1; 495 newes.es_lblk = end + 1;
471 newes.es_len = len2; 496 newes.es_len = len2;
497 if (ext4_es_is_written(&orig_es) ||
498 ext4_es_is_unwritten(&orig_es)) {
499 block = ext4_es_pblock(&orig_es) +
500 orig_es.es_len - len2;
501 ext4_es_store_pblock(&newes, block);
502 }
503 ext4_es_store_status(&newes, ext4_es_status(&orig_es));
472 err = __es_insert_extent(tree, &newes); 504 err = __es_insert_extent(tree, &newes);
473 if (err) { 505 if (err) {
474 es->es_lblk = orig_es.es_lblk; 506 es->es_lblk = orig_es.es_lblk;
@@ -478,6 +510,11 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk,
478 } else { 510 } else {
479 es->es_lblk = end + 1; 511 es->es_lblk = end + 1;
480 es->es_len = len2; 512 es->es_len = len2;
513 if (ext4_es_is_written(es) ||
514 ext4_es_is_unwritten(es)) {
515 block = orig_es.es_pblk + orig_es.es_len - len2;
516 ext4_es_store_pblock(es, block);
517 }
481 } 518 }
482 goto out; 519 goto out;
483 } 520 }
@@ -502,9 +539,15 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk,
502 } 539 }
503 540
504 if (es && es->es_lblk < end + 1) { 541 if (es && es->es_lblk < end + 1) {
542 ext4_lblk_t orig_len = es->es_len;
543
505 len1 = ext4_es_end(es) - end; 544 len1 = ext4_es_end(es) - end;
506 es->es_lblk = end + 1; 545 es->es_lblk = end + 1;
507 es->es_len = len1; 546 es->es_len = len1;
547 if (ext4_es_is_written(es) || ext4_es_is_unwritten(es)) {
548 block = es->es_pblk + orig_len - len1;
549 ext4_es_store_pblock(es, block);
550 }
508 } 551 }
509 552
510out: 553out:
diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h
index 81e9339f23f1..3cad83303adb 100644
--- a/fs/ext4/extents_status.h
+++ b/fs/ext4/extents_status.h
@@ -20,10 +20,21 @@
20#define es_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__) 20#define es_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
21#endif 21#endif
22 22
23#define EXTENT_STATUS_WRITTEN 0x80000000 /* written extent */
24#define EXTENT_STATUS_UNWRITTEN 0x40000000 /* unwritten extent */
25#define EXTENT_STATUS_DELAYED 0x20000000 /* delayed extent */
26#define EXTENT_STATUS_HOLE 0x10000000 /* hole */
27
28#define EXTENT_STATUS_FLAGS (EXTENT_STATUS_WRITTEN | \
29 EXTENT_STATUS_UNWRITTEN | \
30 EXTENT_STATUS_DELAYED | \
31 EXTENT_STATUS_HOLE)
32
23struct extent_status { 33struct extent_status {
24 struct rb_node rb_node; 34 struct rb_node rb_node;
25 ext4_lblk_t es_lblk; /* first logical block extent covers */ 35 ext4_lblk_t es_lblk; /* first logical block extent covers */
26 ext4_lblk_t es_len; /* length of extent in block */ 36 ext4_lblk_t es_len; /* length of extent in block */
37 ext4_fsblk_t es_pblk; /* first physical block */
27}; 38};
28 39
29struct ext4_es_tree { 40struct ext4_es_tree {
@@ -36,10 +47,61 @@ extern void ext4_exit_es(void);
36extern void ext4_es_init_tree(struct ext4_es_tree *tree); 47extern void ext4_es_init_tree(struct ext4_es_tree *tree);
37 48
38extern int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, 49extern int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
39 ext4_lblk_t len); 50 ext4_lblk_t len, ext4_fsblk_t pblk,
51 unsigned long long status);
40extern int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk, 52extern int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
41 ext4_lblk_t len); 53 ext4_lblk_t len);
42extern ext4_lblk_t ext4_es_find_extent(struct inode *inode, 54extern ext4_lblk_t ext4_es_find_extent(struct inode *inode,
43 struct extent_status *es); 55 struct extent_status *es);
44 56
57static inline int ext4_es_is_written(struct extent_status *es)
58{
59 return (es->es_pblk & EXTENT_STATUS_WRITTEN);
60}
61
62static inline int ext4_es_is_unwritten(struct extent_status *es)
63{
64 return (es->es_pblk & EXTENT_STATUS_UNWRITTEN);
65}
66
67static inline int ext4_es_is_delayed(struct extent_status *es)
68{
69 return (es->es_pblk & EXTENT_STATUS_DELAYED);
70}
71
72static inline int ext4_es_is_hole(struct extent_status *es)
73{
74 return (es->es_pblk & EXTENT_STATUS_HOLE);
75}
76
77static inline ext4_fsblk_t ext4_es_status(struct extent_status *es)
78{
79 return (es->es_pblk & EXTENT_STATUS_FLAGS);
80}
81
82static inline ext4_fsblk_t ext4_es_pblock(struct extent_status *es)
83{
84 return (es->es_pblk & ~EXTENT_STATUS_FLAGS);
85}
86
87static inline void ext4_es_store_pblock(struct extent_status *es,
88 ext4_fsblk_t pb)
89{
90 ext4_fsblk_t block;
91
92 block = (pb & ~EXTENT_STATUS_FLAGS) |
93 (es->es_pblk & EXTENT_STATUS_FLAGS);
94 es->es_pblk = block;
95}
96
97static inline void ext4_es_store_status(struct extent_status *es,
98 unsigned long long status)
99{
100 ext4_fsblk_t block;
101
102 block = (status & EXTENT_STATUS_FLAGS) |
103 (es->es_pblk & ~EXTENT_STATUS_FLAGS);
104 es->es_pblk = block;
105}
106
45#endif /* _EXT4_EXTENTS_STATUS_H */ 107#endif /* _EXT4_EXTENTS_STATUS_H */
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index f4466c3650dc..e0e1cb0863f4 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1784,7 +1784,8 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
1784 goto out_unlock; 1784 goto out_unlock;
1785 } 1785 }
1786 1786
1787 retval = ext4_es_insert_extent(inode, map->m_lblk, map->m_len); 1787 retval = ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
1788 ~0, EXTENT_STATUS_DELAYED);
1788 if (retval) 1789 if (retval)
1789 goto out_unlock; 1790 goto out_unlock;
1790 1791