summaryrefslogtreecommitdiffstats
path: root/fs/mbcache2.c
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2016-02-22 18:23:47 -0500
committerTheodore Ts'o <tytso@mit.edu>2016-02-22 18:23:47 -0500
commitf0c8b46238db9d51ef9ea0858259958d0c601cec (patch)
tree67a5822eb2c25bf5c89e6e52a81e6fc8cde7d7ba /fs/mbcache2.c
parentc2f3140fe2eceb3a6c1615b2648b9471544881c6 (diff)
mbcache2: Use referenced bit instead of LRU
Currently we maintain perfect LRU list by moving entry to the tail of the list when it gets used. However these operations on cache-global list are relatively expensive. In this patch we switch to lazy updates of LRU list. Whenever entry gets used, we set a referenced bit in it. When reclaiming entries, we give referenced entries another round in the LRU. Since the list is not a real LRU anymore, rename it to just 'list'. In my testing this logic gives about 30% boost to workloads with mostly unique xattr blocks (e.g. xattr-bench with 10 files and 10000 unique xattr values). Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Diffstat (limited to 'fs/mbcache2.c')
-rw-r--r--fs/mbcache2.c87
1 files changed, 56 insertions, 31 deletions
diff --git a/fs/mbcache2.c b/fs/mbcache2.c
index 3e3198d6b9d6..49f7a6feaa83 100644
--- a/fs/mbcache2.c
+++ b/fs/mbcache2.c
@@ -30,9 +30,9 @@ struct mb2_cache {
30 int c_bucket_bits; 30 int c_bucket_bits;
31 /* Maximum entries in cache to avoid degrading hash too much */ 31 /* Maximum entries in cache to avoid degrading hash too much */
32 int c_max_entries; 32 int c_max_entries;
33 /* Protects c_lru_list, c_entry_count */ 33 /* Protects c_list, c_entry_count */
34 spinlock_t c_lru_list_lock; 34 spinlock_t c_list_lock;
35 struct list_head c_lru_list; 35 struct list_head c_list;
36 /* Number of entries in cache */ 36 /* Number of entries in cache */
37 unsigned long c_entry_count; 37 unsigned long c_entry_count;
38 struct shrinker c_shrink; 38 struct shrinker c_shrink;
@@ -45,6 +45,29 @@ static struct kmem_cache *mb2_entry_cache;
45static unsigned long mb2_cache_shrink(struct mb2_cache *cache, 45static unsigned long mb2_cache_shrink(struct mb2_cache *cache,
46 unsigned int nr_to_scan); 46 unsigned int nr_to_scan);
47 47
48static inline bool mb2_cache_entry_referenced(struct mb2_cache_entry *entry)
49{
50 return entry->_e_hash_list_head & 1;
51}
52
53static inline void mb2_cache_entry_set_referenced(struct mb2_cache_entry *entry)
54{
55 entry->_e_hash_list_head |= 1;
56}
57
58static inline void mb2_cache_entry_clear_referenced(
59 struct mb2_cache_entry *entry)
60{
61 entry->_e_hash_list_head &= ~1;
62}
63
64static inline struct hlist_bl_head *mb2_cache_entry_head(
65 struct mb2_cache_entry *entry)
66{
67 return (struct hlist_bl_head *)
68 (entry->_e_hash_list_head & ~1);
69}
70
48/* 71/*
49 * Number of entries to reclaim synchronously when there are too many entries 72 * Number of entries to reclaim synchronously when there are too many entries
50 * in cache 73 * in cache
@@ -80,13 +103,13 @@ int mb2_cache_entry_create(struct mb2_cache *cache, gfp_t mask, u32 key,
80 if (!entry) 103 if (!entry)
81 return -ENOMEM; 104 return -ENOMEM;
82 105
83 INIT_LIST_HEAD(&entry->e_lru_list); 106 INIT_LIST_HEAD(&entry->e_list);
84 /* One ref for hash, one ref returned */ 107 /* One ref for hash, one ref returned */
85 atomic_set(&entry->e_refcnt, 1); 108 atomic_set(&entry->e_refcnt, 1);
86 entry->e_key = key; 109 entry->e_key = key;
87 entry->e_block = block; 110 entry->e_block = block;
88 head = &cache->c_hash[hash_32(key, cache->c_bucket_bits)]; 111 head = &cache->c_hash[hash_32(key, cache->c_bucket_bits)];
89 entry->e_hash_list_head = head; 112 entry->_e_hash_list_head = (unsigned long)head;
90 hlist_bl_lock(head); 113 hlist_bl_lock(head);
91 hlist_bl_for_each_entry(dup, dup_node, head, e_hash_list) { 114 hlist_bl_for_each_entry(dup, dup_node, head, e_hash_list) {
92 if (dup->e_key == key && dup->e_block == block) { 115 if (dup->e_key == key && dup->e_block == block) {
@@ -98,12 +121,12 @@ int mb2_cache_entry_create(struct mb2_cache *cache, gfp_t mask, u32 key,
98 hlist_bl_add_head(&entry->e_hash_list, head); 121 hlist_bl_add_head(&entry->e_hash_list, head);
99 hlist_bl_unlock(head); 122 hlist_bl_unlock(head);
100 123
101 spin_lock(&cache->c_lru_list_lock); 124 spin_lock(&cache->c_list_lock);
102 list_add_tail(&entry->e_lru_list, &cache->c_lru_list); 125 list_add_tail(&entry->e_list, &cache->c_list);
103 /* Grab ref for LRU list */ 126 /* Grab ref for LRU list */
104 atomic_inc(&entry->e_refcnt); 127 atomic_inc(&entry->e_refcnt);
105 cache->c_entry_count++; 128 cache->c_entry_count++;
106 spin_unlock(&cache->c_lru_list_lock); 129 spin_unlock(&cache->c_list_lock);
107 130
108 return 0; 131 return 0;
109} 132}
@@ -124,7 +147,7 @@ static struct mb2_cache_entry *__entry_find(struct mb2_cache *cache,
124 struct hlist_bl_head *head; 147 struct hlist_bl_head *head;
125 148
126 if (entry) 149 if (entry)
127 head = entry->e_hash_list_head; 150 head = mb2_cache_entry_head(entry);
128 else 151 else
129 head = &cache->c_hash[hash_32(key, cache->c_bucket_bits)]; 152 head = &cache->c_hash[hash_32(key, cache->c_bucket_bits)];
130 hlist_bl_lock(head); 153 hlist_bl_lock(head);
@@ -203,13 +226,13 @@ void mb2_cache_entry_delete_block(struct mb2_cache *cache, u32 key,
203 /* We keep hash list reference to keep entry alive */ 226 /* We keep hash list reference to keep entry alive */
204 hlist_bl_del_init(&entry->e_hash_list); 227 hlist_bl_del_init(&entry->e_hash_list);
205 hlist_bl_unlock(head); 228 hlist_bl_unlock(head);
206 spin_lock(&cache->c_lru_list_lock); 229 spin_lock(&cache->c_list_lock);
207 if (!list_empty(&entry->e_lru_list)) { 230 if (!list_empty(&entry->e_list)) {
208 list_del_init(&entry->e_lru_list); 231 list_del_init(&entry->e_list);
209 cache->c_entry_count--; 232 cache->c_entry_count--;
210 atomic_dec(&entry->e_refcnt); 233 atomic_dec(&entry->e_refcnt);
211 } 234 }
212 spin_unlock(&cache->c_lru_list_lock); 235 spin_unlock(&cache->c_list_lock);
213 mb2_cache_entry_put(cache, entry); 236 mb2_cache_entry_put(cache, entry);
214 return; 237 return;
215 } 238 }
@@ -222,15 +245,12 @@ EXPORT_SYMBOL(mb2_cache_entry_delete_block);
222 * @cache - cache the entry belongs to 245 * @cache - cache the entry belongs to
223 * @entry - entry that got used 246 * @entry - entry that got used
224 * 247 *
225 * Move entry in lru list to reflect the fact that it was used. 248 * Marks entry as used to give hit higher chances of surviving in cache.
226 */ 249 */
227void mb2_cache_entry_touch(struct mb2_cache *cache, 250void mb2_cache_entry_touch(struct mb2_cache *cache,
228 struct mb2_cache_entry *entry) 251 struct mb2_cache_entry *entry)
229{ 252{
230 spin_lock(&cache->c_lru_list_lock); 253 mb2_cache_entry_set_referenced(entry);
231 if (!list_empty(&entry->e_lru_list))
232 list_move_tail(&cache->c_lru_list, &entry->e_lru_list);
233 spin_unlock(&cache->c_lru_list_lock);
234} 254}
235EXPORT_SYMBOL(mb2_cache_entry_touch); 255EXPORT_SYMBOL(mb2_cache_entry_touch);
236 256
@@ -251,18 +271,23 @@ static unsigned long mb2_cache_shrink(struct mb2_cache *cache,
251 struct hlist_bl_head *head; 271 struct hlist_bl_head *head;
252 unsigned int shrunk = 0; 272 unsigned int shrunk = 0;
253 273
254 spin_lock(&cache->c_lru_list_lock); 274 spin_lock(&cache->c_list_lock);
255 while (nr_to_scan-- && !list_empty(&cache->c_lru_list)) { 275 while (nr_to_scan-- && !list_empty(&cache->c_list)) {
256 entry = list_first_entry(&cache->c_lru_list, 276 entry = list_first_entry(&cache->c_list,
257 struct mb2_cache_entry, e_lru_list); 277 struct mb2_cache_entry, e_list);
258 list_del_init(&entry->e_lru_list); 278 if (mb2_cache_entry_referenced(entry)) {
279 mb2_cache_entry_clear_referenced(entry);
280 list_move_tail(&cache->c_list, &entry->e_list);
281 continue;
282 }
283 list_del_init(&entry->e_list);
259 cache->c_entry_count--; 284 cache->c_entry_count--;
260 /* 285 /*
261 * We keep LRU list reference so that entry doesn't go away 286 * We keep LRU list reference so that entry doesn't go away
262 * from under us. 287 * from under us.
263 */ 288 */
264 spin_unlock(&cache->c_lru_list_lock); 289 spin_unlock(&cache->c_list_lock);
265 head = entry->e_hash_list_head; 290 head = mb2_cache_entry_head(entry);
266 hlist_bl_lock(head); 291 hlist_bl_lock(head);
267 if (!hlist_bl_unhashed(&entry->e_hash_list)) { 292 if (!hlist_bl_unhashed(&entry->e_hash_list)) {
268 hlist_bl_del_init(&entry->e_hash_list); 293 hlist_bl_del_init(&entry->e_hash_list);
@@ -272,9 +297,9 @@ static unsigned long mb2_cache_shrink(struct mb2_cache *cache,
272 if (mb2_cache_entry_put(cache, entry)) 297 if (mb2_cache_entry_put(cache, entry))
273 shrunk++; 298 shrunk++;
274 cond_resched(); 299 cond_resched();
275 spin_lock(&cache->c_lru_list_lock); 300 spin_lock(&cache->c_list_lock);
276 } 301 }
277 spin_unlock(&cache->c_lru_list_lock); 302 spin_unlock(&cache->c_list_lock);
278 303
279 return shrunk; 304 return shrunk;
280} 305}
@@ -318,8 +343,8 @@ struct mb2_cache *mb2_cache_create(int bucket_bits)
318 goto err_out; 343 goto err_out;
319 cache->c_bucket_bits = bucket_bits; 344 cache->c_bucket_bits = bucket_bits;
320 cache->c_max_entries = bucket_count << 4; 345 cache->c_max_entries = bucket_count << 4;
321 INIT_LIST_HEAD(&cache->c_lru_list); 346 INIT_LIST_HEAD(&cache->c_list);
322 spin_lock_init(&cache->c_lru_list_lock); 347 spin_lock_init(&cache->c_list_lock);
323 cache->c_hash = kmalloc(bucket_count * sizeof(struct hlist_bl_head), 348 cache->c_hash = kmalloc(bucket_count * sizeof(struct hlist_bl_head),
324 GFP_KERNEL); 349 GFP_KERNEL);
325 if (!cache->c_hash) { 350 if (!cache->c_hash) {
@@ -361,13 +386,13 @@ void mb2_cache_destroy(struct mb2_cache *cache)
361 * We don't bother with any locking. Cache must not be used at this 386 * We don't bother with any locking. Cache must not be used at this
362 * point. 387 * point.
363 */ 388 */
364 list_for_each_entry_safe(entry, next, &cache->c_lru_list, e_lru_list) { 389 list_for_each_entry_safe(entry, next, &cache->c_list, e_list) {
365 if (!hlist_bl_unhashed(&entry->e_hash_list)) { 390 if (!hlist_bl_unhashed(&entry->e_hash_list)) {
366 hlist_bl_del_init(&entry->e_hash_list); 391 hlist_bl_del_init(&entry->e_hash_list);
367 atomic_dec(&entry->e_refcnt); 392 atomic_dec(&entry->e_refcnt);
368 } else 393 } else
369 WARN_ON(1); 394 WARN_ON(1);
370 list_del(&entry->e_lru_list); 395 list_del(&entry->e_list);
371 WARN_ON(atomic_read(&entry->e_refcnt) != 1); 396 WARN_ON(atomic_read(&entry->e_refcnt) != 1);
372 mb2_cache_entry_put(cache, entry); 397 mb2_cache_entry_put(cache, entry);
373 } 398 }