]> git.hungrycats.org Git - linux/commitdiff
ceph: fix readpage from fscache
authorYan, Zheng <zyan@redhat.com>
Fri, 4 Aug 2017 03:22:31 +0000 (11:22 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 7 Sep 2017 06:35:40 +0000 (08:35 +0200)
commit dd2bc473482eedc60c29cf00ad12568ce40ce511 upstream.

ceph_readpage() unlocks page prematurely prematurely in the case
that page is reading from fscache. Caller of readpage expects that
page is uptodate when it get unlocked. So page shoule get locked
by completion callback of fscache_read_or_alloc_pages()

Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/ceph/addr.c
fs/ceph/cache.c

index 900ffafb85cafb31245d91eccb49e1950cd55324..7b79a54a2789899f1ffc323858d1fa0b6bf52fbd 100644 (file)
@@ -188,7 +188,7 @@ static int ceph_releasepage(struct page *page, gfp_t g)
 /*
  * read a single page, without unlocking it.
  */
-static int readpage_nounlock(struct file *filp, struct page *page)
+static int ceph_do_readpage(struct file *filp, struct page *page)
 {
        struct inode *inode = file_inode(filp);
        struct ceph_inode_info *ci = ceph_inode(inode);
@@ -218,7 +218,7 @@ static int readpage_nounlock(struct file *filp, struct page *page)
 
        err = ceph_readpage_from_fscache(inode, page);
        if (err == 0)
-               goto out;
+               return -EINPROGRESS;
 
        dout("readpage inode %p file %p page %p index %lu\n",
             inode, filp, page, page->index);
@@ -248,8 +248,11 @@ out:
 
 static int ceph_readpage(struct file *filp, struct page *page)
 {
-       int r = readpage_nounlock(filp, page);
-       unlock_page(page);
+       int r = ceph_do_readpage(filp, page);
+       if (r != -EINPROGRESS)
+               unlock_page(page);
+       else
+               r = 0;
        return r;
 }
 
@@ -1235,7 +1238,7 @@ retry_locked:
                        goto retry_locked;
                r = writepage_nounlock(page, NULL);
                if (r < 0)
-                       goto fail_nosnap;
+                       goto fail_unlock;
                goto retry_locked;
        }
 
@@ -1263,11 +1266,14 @@ retry_locked:
        }
 
        /* we need to read it. */
-       r = readpage_nounlock(file, page);
-       if (r < 0)
-               goto fail_nosnap;
+       r = ceph_do_readpage(file, page);
+       if (r < 0) {
+               if (r == -EINPROGRESS)
+                       return -EAGAIN;
+               goto fail_unlock;
+       }
        goto retry_locked;
-fail_nosnap:
+fail_unlock:
        unlock_page(page);
        return r;
 }
index 5bc5d37b121712a2f288ede38b46420d13a2f0e5..a2d7997afd94edc8fda8c1e149b43d879dfb640a 100644 (file)
@@ -240,13 +240,7 @@ void ceph_fscache_file_set_cookie(struct inode *inode, struct file *filp)
        }
 }
 
-static void ceph_vfs_readpage_complete(struct page *page, void *data, int error)
-{
-       if (!error)
-               SetPageUptodate(page);
-}
-
-static void ceph_vfs_readpage_complete_unlock(struct page *page, void *data, int error)
+static void ceph_readpage_from_fscache_complete(struct page *page, void *data, int error)
 {
        if (!error)
                SetPageUptodate(page);
@@ -274,7 +268,7 @@ int ceph_readpage_from_fscache(struct inode *inode, struct page *page)
                return -ENOBUFS;
 
        ret = fscache_read_or_alloc_page(ci->fscache, page,
-                                        ceph_vfs_readpage_complete, NULL,
+                                        ceph_readpage_from_fscache_complete, NULL,
                                         GFP_KERNEL);
 
        switch (ret) {
@@ -303,7 +297,7 @@ int ceph_readpages_from_fscache(struct inode *inode,
                return -ENOBUFS;
 
        ret = fscache_read_or_alloc_pages(ci->fscache, mapping, pages, nr_pages,
-                                         ceph_vfs_readpage_complete_unlock,
+                                         ceph_readpage_from_fscache_complete,
                                          NULL, mapping_gfp_mask(mapping));
 
        switch (ret) {