aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2018-10-01 04:07:04 -0400
committerMiklos Szeredi <mszeredi@redhat.com>2018-10-01 04:07:04 -0400
commit3494927e090bf511e54eecaf33a8e56e5c0463db (patch)
tree0015407dc519f878d5dfc5233cfb1dc50e7ff7bd
parent5d7bc7e8680c7ca4c8a4f139ce2a54ccb8131ef0 (diff)
fuse: add readdir cache version
Allow the cache to be invalidated when page(s) have gone missing. In this case increment the version of the cache and reset to an empty state. Add a version number to the directory stream in struct fuse_file as well, indicating the version of the cache it's supposed to be reading. If the cache version doesn't match the stream's version, then reset the stream to the beginning of the cache. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/fuse/fuse_i.h7
-rw-r--r--fs/fuse/inode.c1
-rw-r--r--fs/fuse/readdir.c45
3 files changed, 52 insertions, 1 deletions
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 49e42635e3ac..8b24805e62ee 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -114,6 +114,9 @@ struct fuse_inode {
114 /* position at end of cache (position of next entry) */ 114 /* position at end of cache (position of next entry) */
115 loff_t pos; 115 loff_t pos;
116 116
117 /* version of the cache */
118 u64 version;
119
117 /* protects above fields */ 120 /* protects above fields */
118 spinlock_t lock; 121 spinlock_t lock;
119 } rdc; 122 } rdc;
@@ -176,6 +179,10 @@ struct fuse_file {
176 179
177 /* Offset in cache */ 180 /* Offset in cache */
178 loff_t cache_off; 181 loff_t cache_off;
182
183 /* Version of cache we are reading */
184 u64 version;
185
179 } readdir; 186 } readdir;
180 187
181 /** RB node to be linked on fuse_conn->polled_files */ 188 /** RB node to be linked on fuse_conn->polled_files */
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 892efe6351eb..eef2ae713f75 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -104,6 +104,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
104 fi->rdc.cached = false; 104 fi->rdc.cached = false;
105 fi->rdc.size = 0; 105 fi->rdc.size = 0;
106 fi->rdc.pos = 0; 106 fi->rdc.pos = 0;
107 fi->rdc.version = 0;
107 mutex_init(&fi->mutex); 108 mutex_init(&fi->mutex);
108 fi->forget = fuse_alloc_forget(); 109 fi->forget = fuse_alloc_forget();
109 if (!fi->forget) { 110 if (!fi->forget) {
diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c
index 5bdc0b945d72..18318cc31c05 100644
--- a/fs/fuse/readdir.c
+++ b/fs/fuse/readdir.c
@@ -36,6 +36,7 @@ static void fuse_add_dirent_to_cache(struct file *file,
36 pgoff_t index; 36 pgoff_t index;
37 struct page *page; 37 struct page *page;
38 loff_t size; 38 loff_t size;
39 u64 version;
39 unsigned int offset; 40 unsigned int offset;
40 void *addr; 41 void *addr;
41 42
@@ -48,6 +49,7 @@ static void fuse_add_dirent_to_cache(struct file *file,
48 spin_unlock(&fi->rdc.lock); 49 spin_unlock(&fi->rdc.lock);
49 return; 50 return;
50 } 51 }
52 version = fi->rdc.version;
51 size = fi->rdc.size; 53 size = fi->rdc.size;
52 offset = size & ~PAGE_MASK; 54 offset = size & ~PAGE_MASK;
53 index = size >> PAGE_SHIFT; 55 index = size >> PAGE_SHIFT;
@@ -69,7 +71,8 @@ static void fuse_add_dirent_to_cache(struct file *file,
69 71
70 spin_lock(&fi->rdc.lock); 72 spin_lock(&fi->rdc.lock);
71 /* Raced with another readdir */ 73 /* Raced with another readdir */
72 if (fi->rdc.size != size || WARN_ON(fi->rdc.pos != pos)) 74 if (fi->rdc.version != version || fi->rdc.size != size ||
75 WARN_ON(fi->rdc.pos != pos))
73 goto unlock; 76 goto unlock;
74 77
75 addr = kmap_atomic(page); 78 addr = kmap_atomic(page);
@@ -396,6 +399,14 @@ static enum fuse_parse_result fuse_parse_cache(struct fuse_file *ff,
396 return res; 399 return res;
397} 400}
398 401
402static void fuse_rdc_reset(struct fuse_inode *fi)
403{
404 fi->rdc.cached = false;
405 fi->rdc.version++;
406 fi->rdc.size = 0;
407 fi->rdc.pos = 0;
408}
409
399#define UNCACHED 1 410#define UNCACHED 1
400 411
401static int fuse_readdir_cached(struct file *file, struct dir_context *ctx) 412static int fuse_readdir_cached(struct file *file, struct dir_context *ctx)
@@ -421,6 +432,21 @@ retry:
421 spin_unlock(&fi->rdc.lock); 432 spin_unlock(&fi->rdc.lock);
422 return UNCACHED; 433 return UNCACHED;
423 } 434 }
435 /*
436 * If cache version changed since the last getdents() call, then reset
437 * the cache stream.
438 */
439 if (ff->readdir.version != fi->rdc.version) {
440 ff->readdir.pos = 0;
441 ff->readdir.cache_off = 0;
442 }
443 /*
444 * If at the beginning of the cache, than reset version to
445 * current.
446 */
447 if (ff->readdir.pos == 0)
448 ff->readdir.version = fi->rdc.version;
449
424 WARN_ON(fi->rdc.size < ff->readdir.cache_off); 450 WARN_ON(fi->rdc.size < ff->readdir.cache_off);
425 451
426 index = ff->readdir.cache_off >> PAGE_SHIFT; 452 index = ff->readdir.cache_off >> PAGE_SHIFT;
@@ -437,13 +463,30 @@ retry:
437 463
438 page = find_get_page_flags(file->f_mapping, index, 464 page = find_get_page_flags(file->f_mapping, index,
439 FGP_ACCESSED | FGP_LOCK); 465 FGP_ACCESSED | FGP_LOCK);
466 spin_lock(&fi->rdc.lock);
440 if (!page) { 467 if (!page) {
441 /* 468 /*
442 * Uh-oh: page gone missing, cache is useless 469 * Uh-oh: page gone missing, cache is useless
443 */ 470 */
471 if (fi->rdc.version == ff->readdir.version)
472 fuse_rdc_reset(fi);
473 spin_unlock(&fi->rdc.lock);
444 return UNCACHED; 474 return UNCACHED;
445 } 475 }
446 476
477 /* Make sure it's still the same version after getting the page. */
478 if (ff->readdir.version != fi->rdc.version) {
479 spin_unlock(&fi->rdc.lock);
480 unlock_page(page);
481 put_page(page);
482 goto retry;
483 }
484 spin_unlock(&fi->rdc.lock);
485
486 /*
487 * Contents of the page are now protected against changing by holding
488 * the page lock.
489 */
447 addr = kmap(page); 490 addr = kmap(page);
448 res = fuse_parse_cache(ff, addr, size, ctx); 491 res = fuse_parse_cache(ff, addr, size, ctx);
449 kunmap(page); 492 kunmap(page);