diff options
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/extents_status.c | 22 | ||||
-rw-r--r-- | fs/ext4/extents_status.h | 35 |
2 files changed, 48 insertions, 9 deletions
diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index 30596498ed0b..e04d45733976 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c | |||
@@ -382,7 +382,7 @@ static void ext4_es_free_extent(struct inode *inode, struct extent_status *es) | |||
382 | static int ext4_es_can_be_merged(struct extent_status *es1, | 382 | static int ext4_es_can_be_merged(struct extent_status *es1, |
383 | struct extent_status *es2) | 383 | struct extent_status *es2) |
384 | { | 384 | { |
385 | if (ext4_es_status(es1) != ext4_es_status(es2)) | 385 | if (ext4_es_type(es1) != ext4_es_type(es2)) |
386 | return 0; | 386 | return 0; |
387 | 387 | ||
388 | if (((__u64) es1->es_len) + es2->es_len > EXT_MAX_BLOCKS) { | 388 | if (((__u64) es1->es_len) + es2->es_len > EXT_MAX_BLOCKS) { |
@@ -425,6 +425,8 @@ ext4_es_try_to_merge_left(struct inode *inode, struct extent_status *es) | |||
425 | es1 = rb_entry(node, struct extent_status, rb_node); | 425 | es1 = rb_entry(node, struct extent_status, rb_node); |
426 | if (ext4_es_can_be_merged(es1, es)) { | 426 | if (ext4_es_can_be_merged(es1, es)) { |
427 | es1->es_len += es->es_len; | 427 | es1->es_len += es->es_len; |
428 | if (ext4_es_is_referenced(es)) | ||
429 | ext4_es_set_referenced(es1); | ||
428 | rb_erase(&es->rb_node, &tree->root); | 430 | rb_erase(&es->rb_node, &tree->root); |
429 | ext4_es_free_extent(inode, es); | 431 | ext4_es_free_extent(inode, es); |
430 | es = es1; | 432 | es = es1; |
@@ -447,6 +449,8 @@ ext4_es_try_to_merge_right(struct inode *inode, struct extent_status *es) | |||
447 | es1 = rb_entry(node, struct extent_status, rb_node); | 449 | es1 = rb_entry(node, struct extent_status, rb_node); |
448 | if (ext4_es_can_be_merged(es, es1)) { | 450 | if (ext4_es_can_be_merged(es, es1)) { |
449 | es->es_len += es1->es_len; | 451 | es->es_len += es1->es_len; |
452 | if (ext4_es_is_referenced(es1)) | ||
453 | ext4_es_set_referenced(es); | ||
450 | rb_erase(node, &tree->root); | 454 | rb_erase(node, &tree->root); |
451 | ext4_es_free_extent(inode, es1); | 455 | ext4_es_free_extent(inode, es1); |
452 | } | 456 | } |
@@ -813,6 +817,8 @@ out: | |||
813 | es->es_lblk = es1->es_lblk; | 817 | es->es_lblk = es1->es_lblk; |
814 | es->es_len = es1->es_len; | 818 | es->es_len = es1->es_len; |
815 | es->es_pblk = es1->es_pblk; | 819 | es->es_pblk = es1->es_pblk; |
820 | if (!ext4_es_is_referenced(es)) | ||
821 | ext4_es_set_referenced(es); | ||
816 | stats->es_stats_cache_hits++; | 822 | stats->es_stats_cache_hits++; |
817 | } else { | 823 | } else { |
818 | stats->es_stats_cache_misses++; | 824 | stats->es_stats_cache_misses++; |
@@ -1252,11 +1258,17 @@ static int es_do_reclaim_extents(struct ext4_inode_info *ei, ext4_lblk_t end, | |||
1252 | * We can't reclaim delayed extent from status tree because | 1258 | * We can't reclaim delayed extent from status tree because |
1253 | * fiemap, bigallic, and seek_data/hole need to use it. | 1259 | * fiemap, bigallic, and seek_data/hole need to use it. |
1254 | */ | 1260 | */ |
1255 | if (!ext4_es_is_delayed(es)) { | 1261 | if (ext4_es_is_delayed(es)) |
1256 | rb_erase(&es->rb_node, &tree->root); | 1262 | goto next; |
1257 | ext4_es_free_extent(inode, es); | 1263 | if (ext4_es_is_referenced(es)) { |
1258 | (*nr_shrunk)++; | 1264 | ext4_es_clear_referenced(es); |
1265 | goto next; | ||
1259 | } | 1266 | } |
1267 | |||
1268 | rb_erase(&es->rb_node, &tree->root); | ||
1269 | ext4_es_free_extent(inode, es); | ||
1270 | (*nr_shrunk)++; | ||
1271 | next: | ||
1260 | if (!node) | 1272 | if (!node) |
1261 | goto out_wrap; | 1273 | goto out_wrap; |
1262 | es = rb_entry(node, struct extent_status, rb_node); | 1274 | es = rb_entry(node, struct extent_status, rb_node); |
diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h index e86b1f34cfec..691b52613ce4 100644 --- a/fs/ext4/extents_status.h +++ b/fs/ext4/extents_status.h | |||
@@ -34,6 +34,7 @@ enum { | |||
34 | ES_UNWRITTEN_B, | 34 | ES_UNWRITTEN_B, |
35 | ES_DELAYED_B, | 35 | ES_DELAYED_B, |
36 | ES_HOLE_B, | 36 | ES_HOLE_B, |
37 | ES_REFERENCED_B, | ||
37 | ES_FLAGS | 38 | ES_FLAGS |
38 | }; | 39 | }; |
39 | 40 | ||
@@ -44,6 +45,12 @@ enum { | |||
44 | #define EXTENT_STATUS_UNWRITTEN (1 << ES_UNWRITTEN_B) | 45 | #define EXTENT_STATUS_UNWRITTEN (1 << ES_UNWRITTEN_B) |
45 | #define EXTENT_STATUS_DELAYED (1 << ES_DELAYED_B) | 46 | #define EXTENT_STATUS_DELAYED (1 << ES_DELAYED_B) |
46 | #define EXTENT_STATUS_HOLE (1 << ES_HOLE_B) | 47 | #define EXTENT_STATUS_HOLE (1 << ES_HOLE_B) |
48 | #define EXTENT_STATUS_REFERENCED (1 << ES_REFERENCED_B) | ||
49 | |||
50 | #define ES_TYPE_MASK ((ext4_fsblk_t)(EXTENT_STATUS_WRITTEN | \ | ||
51 | EXTENT_STATUS_UNWRITTEN | \ | ||
52 | EXTENT_STATUS_DELAYED | \ | ||
53 | EXTENT_STATUS_HOLE) << ES_SHIFT) | ||
47 | 54 | ||
48 | struct ext4_sb_info; | 55 | struct ext4_sb_info; |
49 | struct ext4_extent; | 56 | struct ext4_extent; |
@@ -93,24 +100,44 @@ static inline unsigned int ext4_es_status(struct extent_status *es) | |||
93 | return es->es_pblk >> ES_SHIFT; | 100 | return es->es_pblk >> ES_SHIFT; |
94 | } | 101 | } |
95 | 102 | ||
103 | static inline unsigned int ext4_es_type(struct extent_status *es) | ||
104 | { | ||
105 | return (es->es_pblk & ES_TYPE_MASK) >> ES_SHIFT; | ||
106 | } | ||
107 | |||
96 | static inline int ext4_es_is_written(struct extent_status *es) | 108 | static inline int ext4_es_is_written(struct extent_status *es) |
97 | { | 109 | { |
98 | return (ext4_es_status(es) & EXTENT_STATUS_WRITTEN) != 0; | 110 | return (ext4_es_type(es) & EXTENT_STATUS_WRITTEN) != 0; |
99 | } | 111 | } |
100 | 112 | ||
101 | static inline int ext4_es_is_unwritten(struct extent_status *es) | 113 | static inline int ext4_es_is_unwritten(struct extent_status *es) |
102 | { | 114 | { |
103 | return (ext4_es_status(es) & EXTENT_STATUS_UNWRITTEN) != 0; | 115 | return (ext4_es_type(es) & EXTENT_STATUS_UNWRITTEN) != 0; |
104 | } | 116 | } |
105 | 117 | ||
106 | static inline int ext4_es_is_delayed(struct extent_status *es) | 118 | static inline int ext4_es_is_delayed(struct extent_status *es) |
107 | { | 119 | { |
108 | return (ext4_es_status(es) & EXTENT_STATUS_DELAYED) != 0; | 120 | return (ext4_es_type(es) & EXTENT_STATUS_DELAYED) != 0; |
109 | } | 121 | } |
110 | 122 | ||
111 | static inline int ext4_es_is_hole(struct extent_status *es) | 123 | static inline int ext4_es_is_hole(struct extent_status *es) |
112 | { | 124 | { |
113 | return (ext4_es_status(es) & EXTENT_STATUS_HOLE) != 0; | 125 | return (ext4_es_type(es) & EXTENT_STATUS_HOLE) != 0; |
126 | } | ||
127 | |||
128 | static inline void ext4_es_set_referenced(struct extent_status *es) | ||
129 | { | ||
130 | es->es_pblk |= ((ext4_fsblk_t)EXTENT_STATUS_REFERENCED) << ES_SHIFT; | ||
131 | } | ||
132 | |||
133 | static inline void ext4_es_clear_referenced(struct extent_status *es) | ||
134 | { | ||
135 | es->es_pblk &= ~(((ext4_fsblk_t)EXTENT_STATUS_REFERENCED) << ES_SHIFT); | ||
136 | } | ||
137 | |||
138 | static inline int ext4_es_is_referenced(struct extent_status *es) | ||
139 | { | ||
140 | return (ext4_es_status(es) & EXTENT_STATUS_REFERENCED) != 0; | ||
114 | } | 141 | } |
115 | 142 | ||
116 | static inline ext4_fsblk_t ext4_es_pblock(struct extent_status *es) | 143 | static inline ext4_fsblk_t ext4_es_pblock(struct extent_status *es) |