aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/ext4/extents_status.c67
-rw-r--r--fs/ext4/extents_status.h64
-rw-r--r--fs/ext4/inode.c3
-rw-r--r--include/trace/events/ext4.h34
4 files changed, 142 insertions, 26 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
diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h
index 52c923851959..0ee507ff216d 100644
--- a/include/trace/events/ext4.h
+++ b/include/trace/events/ext4.h
@@ -2093,28 +2093,33 @@ TRACE_EVENT(ext4_ext_remove_space_done,
2093); 2093);
2094 2094
2095TRACE_EVENT(ext4_es_insert_extent, 2095TRACE_EVENT(ext4_es_insert_extent,
2096 TP_PROTO(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len), 2096 TP_PROTO(struct inode *inode, struct extent_status *es),
2097 2097
2098 TP_ARGS(inode, lblk, len), 2098 TP_ARGS(inode, es),
2099 2099
2100 TP_STRUCT__entry( 2100 TP_STRUCT__entry(
2101 __field( dev_t, dev ) 2101 __field( dev_t, dev )
2102 __field( ino_t, ino ) 2102 __field( ino_t, ino )
2103 __field( loff_t, lblk ) 2103 __field( ext4_lblk_t, lblk )
2104 __field( loff_t, len ) 2104 __field( ext4_lblk_t, len )
2105 __field( ext4_fsblk_t, pblk )
2106 __field( unsigned long long, status )
2105 ), 2107 ),
2106 2108
2107 TP_fast_assign( 2109 TP_fast_assign(
2108 __entry->dev = inode->i_sb->s_dev; 2110 __entry->dev = inode->i_sb->s_dev;
2109 __entry->ino = inode->i_ino; 2111 __entry->ino = inode->i_ino;
2110 __entry->lblk = lblk; 2112 __entry->lblk = es->es_lblk;
2111 __entry->len = len; 2113 __entry->len = es->es_len;
2114 __entry->pblk = ext4_es_pblock(es);
2115 __entry->status = ext4_es_status(es);
2112 ), 2116 ),
2113 2117
2114 TP_printk("dev %d,%d ino %lu es [%lld/%lld)", 2118 TP_printk("dev %d,%d ino %lu es [%u/%u) mapped %llu status %llx",
2115 MAJOR(__entry->dev), MINOR(__entry->dev), 2119 MAJOR(__entry->dev), MINOR(__entry->dev),
2116 (unsigned long) __entry->ino, 2120 (unsigned long) __entry->ino,
2117 __entry->lblk, __entry->len) 2121 __entry->lblk, __entry->len,
2122 __entry->pblk, __entry->status)
2118); 2123);
2119 2124
2120TRACE_EVENT(ext4_es_remove_extent, 2125TRACE_EVENT(ext4_es_remove_extent,
@@ -2175,6 +2180,8 @@ TRACE_EVENT(ext4_es_find_extent_exit,
2175 __field( ino_t, ino ) 2180 __field( ino_t, ino )
2176 __field( ext4_lblk_t, lblk ) 2181 __field( ext4_lblk_t, lblk )
2177 __field( ext4_lblk_t, len ) 2182 __field( ext4_lblk_t, len )
2183 __field( ext4_fsblk_t, pblk )
2184 __field( unsigned long long, status )
2178 __field( ext4_lblk_t, ret ) 2185 __field( ext4_lblk_t, ret )
2179 ), 2186 ),
2180 2187
@@ -2183,13 +2190,16 @@ TRACE_EVENT(ext4_es_find_extent_exit,
2183 __entry->ino = inode->i_ino; 2190 __entry->ino = inode->i_ino;
2184 __entry->lblk = es->es_lblk; 2191 __entry->lblk = es->es_lblk;
2185 __entry->len = es->es_len; 2192 __entry->len = es->es_len;
2193 __entry->pblk = ext4_es_pblock(es);
2194 __entry->status = ext4_es_status(es);
2186 __entry->ret = ret; 2195 __entry->ret = ret;
2187 ), 2196 ),
2188 2197
2189 TP_printk("dev %d,%d ino %lu es [%u/%u) ret %u", 2198 TP_printk("dev %d,%d ino %lu es [%u/%u) mapped %llu status %llx ret %u",
2190 MAJOR(__entry->dev), MINOR(__entry->dev), 2199 MAJOR(__entry->dev), MINOR(__entry->dev),
2191 (unsigned long) __entry->ino, 2200 (unsigned long) __entry->ino,
2192 __entry->lblk, __entry->len, __entry->ret) 2201 __entry->lblk, __entry->len,
2202 __entry->pblk, __entry->status, __entry->ret)
2193); 2203);
2194 2204
2195#endif /* _TRACE_EXT4_H */ 2205#endif /* _TRACE_EXT4_H */