Skip to content

Commit

Permalink
ceph: fix readpage from fscache
Browse files Browse the repository at this point in the history
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()

Cc: stable@vger.kernel.org # 4.1+, needs backporting for < 4.7
Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
  • Loading branch information
Yan, Zheng authored and Ilya Dryomov committed Aug 31, 2017
1 parent cc4a41f commit dd2bc47
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 18 deletions.
24 changes: 15 additions & 9 deletions fs/ceph/addr.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,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);
Expand Down Expand Up @@ -219,7 +219,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);
Expand Down Expand Up @@ -249,8 +249,11 @@ static int readpage_nounlock(struct file *filp, struct page *page)

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;
}

Expand Down Expand Up @@ -1237,7 +1240,7 @@ static int ceph_update_writeable_page(struct file *file,
goto retry_locked;
r = writepage_nounlock(page, NULL);
if (r < 0)
goto fail_nosnap;
goto fail_unlock;
goto retry_locked;
}

Expand Down Expand Up @@ -1265,11 +1268,14 @@ static int ceph_update_writeable_page(struct file *file,
}

/* 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;
}
Expand Down
12 changes: 3 additions & 9 deletions fs/ceph/cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,13 +297,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);
Expand Down Expand Up @@ -331,7 +325,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) {
Expand Down Expand Up @@ -360,7 +354,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) {
Expand Down

0 comments on commit dd2bc47

Please sign in to comment.