The commit is pushed to "branch-rh9-5.14.0-427.44.1.vz9.80.x-ovz" and will 
appear at g...@bitbucket.org:openvz/vzkernel.git
after rh9-5.14.0-427.44.1.vz9.80.23
------>
commit b191d0a510fbfcdf9b5a8c9d792a5b924ad6b48d
Author: Alexey Kuznetsov <kuz...@virtuozzo.com>
Date:   Fri Mar 28 20:00:53 2025 +0800

    fs/fuse: keep req page refcnt sane
    
    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.
    
    Fixes: fb7ae3cf4ca1 ("fuse: fuse_prepare_write() cannot handle page from 
killed
    request")
    Affects: #VSTOR-100953
    https://virtuozzo.atlassian.net/browse/VSTOR-100953
    
    Signed-off-by: Alexey Kuznetsov <kuz...@virtuozzo.com>
    
    Feature: vStorage
---
 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 4b701af5a205..cfc5da819438 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 35488283cea3..28c495cb2a46 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 a22e0ffb3a8f..82baa1d0b800 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);
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to