[v2,2/5] ceph: pass unlocked page to ceph_uninline_data
diff mbox series

Message ID 20190711184136.19779-3-jlayton@kernel.org
State New
Headers show
Series
  • ceph: fix races when uninlining data
Related show

Commit Message

Jeff Layton July 11, 2019, 6:41 p.m. UTC
The only caller locks the page and then has to unlock it again once it
returns. Just have ceph_uninline_data do that itself. Also, in the case
where we are allocating a local page for this, lock it to help simplify
the code a bit.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/addr.c  | 27 ++++++++++++---------------
 fs/ceph/super.h |  2 +-
 2 files changed, 13 insertions(+), 16 deletions(-)

Patch
diff mbox series

diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index 859d2cbfeccb..038678963cf9 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -1542,14 +1542,7 @@  static vm_fault_t ceph_page_mkwrite(struct vm_fault *vmf)
 	ceph_block_sigs(&oldset);
 
 	if (ci->i_inline_version != CEPH_INLINE_NONE) {
-		struct page *locked_page = NULL;
-		if (off == 0) {
-			lock_page(page);
-			locked_page = page;
-		}
-		err = ceph_uninline_data(inode, locked_page);
-		if (locked_page)
-			unlock_page(locked_page);
+		err = ceph_uninline_data(inode, off == 0 ? page : NULL);
 		if (err < 0)
 			goto out_free;
 	}
@@ -1663,7 +1656,7 @@  void ceph_fill_inline_data(struct inode *inode, struct page *locked_page,
 	}
 }
 
-int ceph_uninline_data(struct inode *inode, struct page *locked_page)
+int ceph_uninline_data(struct inode *inode, struct page *provided_page)
 {
 	struct ceph_inode_info *ci = ceph_inode(inode);
 	struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
@@ -1672,6 +1665,7 @@  int ceph_uninline_data(struct inode *inode, struct page *locked_page)
 	u64 len, inline_version;
 	int err = 0;
 	bool from_pagecache = false;
+	bool allocated_page = false;
 
 	spin_lock(&ci->i_ceph_lock);
 	inline_version = ci->i_inline_version;
@@ -1684,8 +1678,9 @@  int ceph_uninline_data(struct inode *inode, struct page *locked_page)
 	    inline_version == CEPH_INLINE_NONE)
 		goto out;
 
-	if (locked_page) {
-		page = locked_page;
+	if (provided_page) {
+		page = provided_page;
+		lock_page(page);
 		WARN_ON(!PageUptodate(page));
 	} else if (ceph_caps_issued(ci) &
 		   (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) {
@@ -1711,6 +1706,8 @@  int ceph_uninline_data(struct inode *inode, struct page *locked_page)
 			err = -ENOMEM;
 			goto out;
 		}
+		allocated_page = true;
+		lock_page(page);
 		err = __ceph_do_getattr(inode, page,
 					CEPH_STAT_CAP_INLINE_DATA, true);
 		if (err < 0) {
@@ -1782,11 +1779,11 @@  int ceph_uninline_data(struct inode *inode, struct page *locked_page)
 	if (err == -ECANCELED)
 		err = 0;
 out:
-	if (page && page != locked_page) {
-		if (from_pagecache) {
-			unlock_page(page);
+	if (page) {
+		unlock_page(page);
+		if (from_pagecache)
 			put_page(page);
-		} else
+		else if (allocated_page)
 			__free_pages(page, 0);
 	}
 
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index dd2a242d5d22..0182577e6dae 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -1070,7 +1070,7 @@  extern void ceph_put_fmode(struct ceph_inode_info *ci, int mode);
 /* addr.c */
 extern const struct address_space_operations ceph_aops;
 extern int ceph_mmap(struct file *file, struct vm_area_struct *vma);
-extern int ceph_uninline_data(struct inode *inode, struct page *locked_page);
+extern int ceph_uninline_data(struct inode *inode, struct page *provided_page);
 extern int ceph_pool_perm_check(struct ceph_inode_info *ci, int need);
 extern void ceph_pool_perm_destroy(struct ceph_mds_client* mdsc);