Currently we have to scan serveral queues to find all requests carrying locked pages from the page cache and then unlock them. This approach is very bug prone when a request is moved from one queue to another, during which we have to check the fuse_file state to make sure the request is either aborted early or added to the a queue that won't be missed by the function fuse_invalidate_files(), ugly and messy.
Well there is a much simpler approach, we can just maintain a dedicated list just for revocation. Only read requests coming from page cache are added to the list and stay in the list until they are ended. Then we can simply go through the list to find all requests and revoke them from fuse_invalidate_files(), it can also make the normal path mostly revocation agnostic and those special code can be removed. Related to #VSTOR-101450 Signed-off-by: Liu Kui <kui....@virtuozzo.com> --- fs/fuse/file.c | 55 +++++++++++++++++++++++++++++++++++++++++++----- fs/fuse/fuse_i.h | 10 +++++++-- fs/fuse/inode.c | 11 +++++++++- 3 files changed, 68 insertions(+), 8 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 2b92e59ac1db..3796e45e1b71 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -96,6 +96,8 @@ struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release) refcount_set(&ff->count, 1); RB_CLEAR_NODE(&ff->polled_node); init_waitqueue_head(&ff->poll_wait); + spin_lock_init(&ff->lock); + INIT_LIST_HEAD(&ff->revoke_list); ff->kh = atomic64_inc_return(&fm->fc->khctr); spin_lock(&fm->fc->lock); @@ -1139,6 +1141,7 @@ static int fuse_do_readpage(struct file *file, struct page *page, bool *killed_p) { struct inode *inode = page->mapping->host; + struct fuse_file *ff = file->private_data; struct fuse_mount *fm = get_fuse_mount(inode); loff_t pos = page_offset(page); struct fuse_page_desc desc = { .length = PAGE_SIZE }; @@ -1159,9 +1162,13 @@ static int fuse_do_readpage(struct file *file, struct page *page, * page. */ fuse_wait_on_page_writeback(inode, page->index); - - if (fuse_file_fail_immediately(file)) + spin_lock(&ff->lock); + if (unlikely(__fuse_file_fail_immediately(ff))) { + spin_unlock(&ff->lock); return -EIO; + } + list_add(&ia.revoke_entry, &ff->revoke_list); + spin_unlock(&ff->lock); attr_ver = fuse_get_attr_version(fm->fc); @@ -1175,6 +1182,11 @@ static int fuse_do_readpage(struct file *file, struct page *page, *killed_p = ia.ap.args.killed; if (ia.ap.args.killed) res = -EIO; + + spin_lock(&ff->lock); + list_del(&ia.revoke_entry); + spin_unlock(&ff->lock); + if (res < 0) return res; /* @@ -1223,16 +1235,33 @@ void fuse_release_ff(struct inode *inode, struct fuse_file *ff) } EXPORT_SYMBOL_GPL(fuse_release_ff); +void fuse_revoke_readpages(struct fuse_file *ff) +{ + struct fuse_io_args *ia; + int i; + + spin_lock(&ff->lock); + /* revoke all pending read issued from page cache */ + list_for_each_entry(ia, &ff->revoke_list, revoke_entry) { + ia->ap.args.killed = 1; + for (i = 0; i < ia->ap.num_pages; i++) + unlock_page(ia->ap.pages[i]); + } + spin_unlock(&ff->lock); +} +EXPORT_SYMBOL_GPL(fuse_revoke_readpages); + static void fuse_readpages_end(struct fuse_mount *fm, struct fuse_args *args, int err) { int i; - int killed = args->killed; + bool killed = args->killed; struct fuse_io_args *ia = container_of(args, typeof(*ia), ap.args); struct fuse_args_pages *ap = &ia->ap; size_t count = ia->read.in.size; size_t num_read = args->out_args[0].size; struct inode *inode = args->io_inode; + struct fuse_file *ff = ia->ff; if (unlikely(killed)) err = -EIO; @@ -1252,9 +1281,13 @@ static void fuse_readpages_end(struct fuse_mount *fm, struct fuse_args *args, } fuse_invalidate_atime(inode); - if (ia->ff) - fuse_release_ff(inode, ia->ff); + if (ff) { + spin_lock(&ff->lock); + list_del(&ia->revoke_entry); + spin_unlock(&ff->lock); + fuse_release_ff(inode, ff); + } fuse_io_free(ia); } @@ -1285,6 +1318,17 @@ static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file) if (fm->fc->async_read) { ia->ff = fuse_file_get(ff); ap->args.end = fuse_readpages_end; + + spin_lock(&ff->lock); + if (unlikely(__fuse_file_fail_immediately(ff))) { + spin_unlock(&ff->lock); + INIT_LIST_HEAD(&ia->revoke_entry); + err = -EIO; + goto out; + } + list_add(&ia->revoke_entry, &ff->revoke_list); + spin_unlock(&ff->lock); + err = fuse_simple_background(fm, &ap->args, GFP_KERNEL); if (!err) return; @@ -1292,6 +1336,7 @@ static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file) res = fuse_simple_request(fm, &ap->args); err = res < 0 ? res : 0; } +out: fuse_readpages_end(fm, &ap->args, err); } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index b07a18e39487..6b3a66dbf4a2 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -306,6 +306,12 @@ struct fuse_file { bool flock:1; unsigned long ff_state; + + /* Lock to protect the revoke_list*/ + spinlock_t lock; + + /** List of requests that may be killed **/ + struct list_head revoke_list; }; /** FUSE file states (ff_state) */ @@ -1275,6 +1281,7 @@ struct fuse_io_args { struct fuse_args_pages ap; struct fuse_io_priv *io; struct fuse_file *ff; + struct list_head revoke_entry; }; void fuse_read_args_fill(struct fuse_io_args *ia, struct file *file, loff_t pos, @@ -1653,12 +1660,11 @@ int fuse_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, struct fuse_file *__fuse_write_file_get(struct fuse_inode *fi); struct fuse_file *fuse_file_get(struct fuse_file *ff); void fuse_release_ff(struct inode *inode, struct fuse_file *ff); - void fuse_kill_requests(struct fuse_conn *fc, struct inode *inode, struct list_head *req_list); - struct fuse_req *fuse_dev_find_request(unsigned int fd, u64 unique); +void fuse_revoke_readpages(struct fuse_file *ff); /* readdir.c */ int fuse_readdir(struct file *file, struct dir_context *ctx); diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 987b1d3267a3..a25ad82331d9 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -671,7 +671,9 @@ int fuse_invalidate_files(struct fuse_conn *fc, u64 nodeid) spin_lock(&fi->lock); set_bit(FUSE_I_INVAL_FILES, &fi->state); list_for_each_entry(ff, &fi->rw_files, rw_entry) { + spin_lock(&ff->lock); set_bit(FUSE_S_FAIL_IMMEDIATELY, &ff->ff_state); + spin_unlock(&ff->lock); } spin_unlock(&fi->lock); @@ -683,6 +685,7 @@ int fuse_invalidate_files(struct fuse_conn *fc, u64 nodeid) err = filemap_write_and_wait(inode->i_mapping); if (!err || err == -EIO) { /* AS_EIO might trigger -EIO */ +#if 0 struct fuse_dev *fud; spin_lock(&fc->lock); @@ -720,8 +723,14 @@ int fuse_invalidate_files(struct fuse_conn *fc, u64 nodeid) spin_unlock(&fpq->lock); } - wake_up(&fi->page_waitq); /* readpage[s] can wait on fuse wb */ spin_unlock(&fc->lock); +#endif + spin_lock(&fi->lock); + list_for_each_entry(ff, &fi->rw_files, rw_entry) + fuse_revoke_readpages(ff); + spin_unlock(&fi->lock); + + wake_up(&fi->page_waitq); /* readpage[s] can wait on fuse wb */ err = invalidate_inode_pages2(inode->i_mapping); } -- 2.39.5 (Apple Git-154) _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel