It was _our_ ancient patch, fb7ae3cf4ca1e052335b94b86b9f43f09b9740f0, subj: "fuse: fuse_prepare_write() cannot handle page from killed request"
The patch was entirely crazy, I would veto it a moment I have seen it, but obviously I was distracted and this abomination entered the kernel from behind. :-) So, do not mangle original queued request, only the requestor is allowed do this. Do not put pages hold by original request, only the requestor may do this. Yet, keep unlocking pages with proper error status, this is dirty and requires attention from the requestor to avoid double unlock, yet we do not have any alternative solution. Also, an ancient page leakage has been found, which was difficult to detect, since invalidate_inode_pages2 forcably detached pages from page cache and they live in dormant state. Affects: #VSTOR-100953 Signed-off-by: Alexey Kuznetsov <kuz...@virtuozzo.com> --- fs/fuse/file.c | 31 +++++++++++++++---------------- fs/fuse/fuse_i.h | 5 +---- fs/fuse/inode.c | 5 ----- 3 files changed, 16 insertions(+), 25 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 4b701af..cfc5da8 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1115,7 +1115,7 @@ static void fuse_short_read(struct inode *inode, u64 attr_ver, size_t num_read, } static int fuse_do_readpage(struct file *file, struct page *page, - bool page_needs_release, bool *killed_p) + bool *killed_p) { struct inode *inode = page->mapping->host; struct fuse_mount *fm = get_fuse_mount(inode); @@ -1128,7 +1128,6 @@ static int fuse_do_readpage(struct file *file, struct page *page, .ap.pages = &page, .ap.descs = &desc, .ap.args.page_cache = 1, - .ap.args.page_needs_release = page_needs_release, }; ssize_t res; u64 attr_ver; @@ -1179,7 +1178,7 @@ static int fuse_read_folio(struct file *file, struct folio *folio) if (fuse_is_bad(inode)) goto out; - err = fuse_do_readpage(file, page, false, &killed); + err = fuse_do_readpage(file, page, &killed); fuse_invalidate_atime(inode); out: if (!killed) @@ -1207,14 +1206,15 @@ static void fuse_readpages_end(struct fuse_mount *fm, struct fuse_args *args, int err) { int i; + int 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->inode; - if (args->killed) - goto killed; + if (unlikely(killed)) + err = -EIO; /* * Short read means EOF. If file size is larger, truncate it @@ -1225,14 +1225,15 @@ static void fuse_readpages_end(struct fuse_mount *fm, struct fuse_args *args, for (i = 0; i < ap->num_pages; i++) { struct page *page = ap->pages[i]; - if (!err) - SetPageUptodate(page); - else - SetPageError(page); - unlock_page(page); + if (likely(!killed)) { + if (!err) + SetPageUptodate(page); + else + SetPageError(page); + unlock_page(page); + } put_page(page); } -killed: fuse_invalidate_atime(inode); if (ia->ff) @@ -1255,7 +1256,6 @@ static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file) ap->args.page_zeroing = true; ap->args.page_replace = true; ap->args.page_cache = 1; - ap->args.page_needs_release = false; /* Don't overflow end offset */ if (pos + (count - 1) == LLONG_MAX) { @@ -2732,7 +2732,7 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping, zero_user_segment(page, 0, off); goto success; } - err = fuse_do_readpage(file, page, true, &killed); + err = fuse_do_readpage(file, page, &killed); if (err) goto cleanup; success: @@ -2740,10 +2740,9 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping, return 0; cleanup: - if (!killed) { + if (!killed) unlock_page(page); - put_page(page); - } + put_page(page); error: return err; } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 3548828..28c495c 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -324,10 +324,7 @@ struct fuse_args { /** Request contains pages from page-cache */ unsigned page_cache:1; - /** Request pages need page_cache_release() */ - unsigned page_needs_release:1; - - /** Request was killed -- pages were released */ + /** Request was killed -- pages were unlocked */ unsigned killed:1; struct inode *io_inode; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index a22e0ff..82baa1d 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -502,12 +502,7 @@ void fuse_kill_requests(struct fuse_conn *fc, struct inode *inode, struct page *page = ia->ap.pages[i]; SetPageError(page); unlock_page(page); - if (req->args->page_needs_release) - put_page(page); - ia->ap.pages[i] = NULL; } - - ia->ap.num_pages = 0; } } EXPORT_SYMBOL_GPL(fuse_kill_requests); -- 1.8.3.1 _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel