aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2014-11-25 11:55:24 -0500
committerTheodore Ts'o <tytso@mit.edu>2014-11-25 11:55:24 -0500
commit2be12de98a1cc21c4de4e2d6fb2bf5aa0a279947 (patch)
tree4cd9da2b11616ee1a38bb1ac677ca7ec90bef2b7
parent624d0f1dd7c80d2bac4fc3066b2ff3947f890883 (diff)
ext4: introduce aging to extent status tree
Introduce a simple aging to extent status tree. Each extent has a REFERENCED bit which gets set when the extent is used. Shrinker then skips entries with referenced bit set and clears the bit. Thus frequently used extents have higher chances of staying in memory. Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
-rw-r--r--fs/ext4/extents_status.c22
-rw-r--r--fs/ext4/extents_status.h35
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)
382static int ext4_es_can_be_merged(struct extent_status *es1, 382static 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)++;
1271next:
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
48struct ext4_sb_info; 55struct ext4_sb_info;
49struct ext4_extent; 56struct 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
103static inline unsigned int ext4_es_type(struct extent_status *es)
104{
105 return (es->es_pblk & ES_TYPE_MASK) >> ES_SHIFT;
106}
107
96static inline int ext4_es_is_written(struct extent_status *es) 108static 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
101static inline int ext4_es_is_unwritten(struct extent_status *es) 113static 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
106static inline int ext4_es_is_delayed(struct extent_status *es) 118static 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
111static inline int ext4_es_is_hole(struct extent_status *es) 123static 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
128static 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
133static 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
138static 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
116static inline ext4_fsblk_t ext4_es_pblock(struct extent_status *es) 143static inline ext4_fsblk_t ext4_es_pblock(struct extent_status *es)