aboutsummaryrefslogtreecommitdiffstats
path: root/fs/mbcache.c
diff options
context:
space:
mode:
authorAndreas Gruenbacher <agruenba@redhat.com>2016-02-22 22:44:04 -0500
committerTheodore Ts'o <tytso@mit.edu>2016-02-22 22:44:04 -0500
commit6048c64b26097a0ffbd966866b599f990e674e9b (patch)
tree412b3e62328842563ef70e6ab68a1dd539038a90 /fs/mbcache.c
parent3fd164629d25b04f291a79a013dcc7ce1a301269 (diff)
mbcache: add reusable flag to cache entries
To reduce amount of damage caused by single bad block, we limit number of inodes sharing an xattr block to 1024. Thus there can be more xattr blocks with the same contents when there are lots of files with the same extended attributes. These xattr blocks naturally result in hash collisions and can form long hash chains and we unnecessarily check each such block only to find out we cannot use it because it is already shared by too many inodes. Add a reusable flag to cache entries which is cleared when a cache entry has reached its maximum refcount. Cache entries which are not marked reusable are skipped by mb_cache_entry_find_{first,next}. This significantly speeds up mbcache when there are many same xattr blocks. For example for xattr-bench with 5 values and each process handling 20000 files, the run for 64 processes is 25x faster with this patch. Even for 8 processes the speedup is almost 3x. We have also verified that for situations where there is only one xattr block of each kind, the patch doesn't have a measurable cost. [JK: Remove handling of setting the same value since it is not needed anymore, check for races in e_reusable setting, improve changelog, add measurements] Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Diffstat (limited to 'fs/mbcache.c')
-rw-r--r--fs/mbcache.c38
1 files changed, 34 insertions, 4 deletions
diff --git a/fs/mbcache.c b/fs/mbcache.c
index 903be151dcfe..eccda3a02de6 100644
--- a/fs/mbcache.c
+++ b/fs/mbcache.c
@@ -63,13 +63,14 @@ static inline struct hlist_bl_head *mb_cache_entry_head(struct mb_cache *cache,
63 * @mask - gfp mask with which the entry should be allocated 63 * @mask - gfp mask with which the entry should be allocated
64 * @key - key of the entry 64 * @key - key of the entry
65 * @block - block that contains data 65 * @block - block that contains data
66 * @reusable - is the block reusable by other inodes?
66 * 67 *
67 * Creates entry in @cache with key @key and records that data is stored in 68 * Creates entry in @cache with key @key and records that data is stored in
68 * block @block. The function returns -EBUSY if entry with the same key 69 * block @block. The function returns -EBUSY if entry with the same key
69 * and for the same block already exists in cache. Otherwise 0 is returned. 70 * and for the same block already exists in cache. Otherwise 0 is returned.
70 */ 71 */
71int mb_cache_entry_create(struct mb_cache *cache, gfp_t mask, u32 key, 72int mb_cache_entry_create(struct mb_cache *cache, gfp_t mask, u32 key,
72 sector_t block) 73 sector_t block, bool reusable)
73{ 74{
74 struct mb_cache_entry *entry, *dup; 75 struct mb_cache_entry *entry, *dup;
75 struct hlist_bl_node *dup_node; 76 struct hlist_bl_node *dup_node;
@@ -91,6 +92,7 @@ int mb_cache_entry_create(struct mb_cache *cache, gfp_t mask, u32 key,
91 atomic_set(&entry->e_refcnt, 1); 92 atomic_set(&entry->e_refcnt, 1);
92 entry->e_key = key; 93 entry->e_key = key;
93 entry->e_block = block; 94 entry->e_block = block;
95 entry->e_reusable = reusable;
94 head = mb_cache_entry_head(cache, key); 96 head = mb_cache_entry_head(cache, key);
95 hlist_bl_lock(head); 97 hlist_bl_lock(head);
96 hlist_bl_for_each_entry(dup, dup_node, head, e_hash_list) { 98 hlist_bl_for_each_entry(dup, dup_node, head, e_hash_list) {
@@ -137,7 +139,7 @@ static struct mb_cache_entry *__entry_find(struct mb_cache *cache,
137 while (node) { 139 while (node) {
138 entry = hlist_bl_entry(node, struct mb_cache_entry, 140 entry = hlist_bl_entry(node, struct mb_cache_entry,
139 e_hash_list); 141 e_hash_list);
140 if (entry->e_key == key) { 142 if (entry->e_key == key && entry->e_reusable) {
141 atomic_inc(&entry->e_refcnt); 143 atomic_inc(&entry->e_refcnt);
142 goto out; 144 goto out;
143 } 145 }
@@ -184,10 +186,38 @@ struct mb_cache_entry *mb_cache_entry_find_next(struct mb_cache *cache,
184} 186}
185EXPORT_SYMBOL(mb_cache_entry_find_next); 187EXPORT_SYMBOL(mb_cache_entry_find_next);
186 188
189/*
190 * mb_cache_entry_get - get a cache entry by block number (and key)
191 * @cache - cache we work with
192 * @key - key of block number @block
193 * @block - block number
194 */
195struct mb_cache_entry *mb_cache_entry_get(struct mb_cache *cache, u32 key,
196 sector_t block)
197{
198 struct hlist_bl_node *node;
199 struct hlist_bl_head *head;
200 struct mb_cache_entry *entry;
201
202 head = mb_cache_entry_head(cache, key);
203 hlist_bl_lock(head);
204 hlist_bl_for_each_entry(entry, node, head, e_hash_list) {
205 if (entry->e_key == key && entry->e_block == block) {
206 atomic_inc(&entry->e_refcnt);
207 goto out;
208 }
209 }
210 entry = NULL;
211out:
212 hlist_bl_unlock(head);
213 return entry;
214}
215EXPORT_SYMBOL(mb_cache_entry_get);
216
187/* mb_cache_entry_delete_block - remove information about block from cache 217/* mb_cache_entry_delete_block - remove information about block from cache
188 * @cache - cache we work with 218 * @cache - cache we work with
189 * @key - key of the entry to remove 219 * @key - key of block @block
190 * @block - block containing data for @key 220 * @block - block number
191 * 221 *
192 * Remove entry from cache @cache with key @key with data stored in @block. 222 * Remove entry from cache @cache with key @key with data stored in @block.
193 */ 223 */