[pnfs] [PATCH 4/8] pnfs: pnfs_write_begin

Fred Isaman iisaman at citi.umich.edu
Thu May 1 09:02:13 EDT 2008


Add hooks in the nfs_write_begin path, giving a driver the potential
to read in page data around the data that is about to be copied to the page.

Signed-off-by: Fred Isaman <iisaman at citi.umich.edu>
---
 fs/nfs/file.c             |   16 ++++++++++++----
 fs/nfs/pnfs.c             |   35 +++++++++++++++++++++++++++++++++++
 fs/nfs/pnfs.h             |   31 +++++++++++++++++++++++++++++++
 include/linux/nfs4_pnfs.h |    3 +++
 4 files changed, 81 insertions(+), 4 deletions(-)

diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index cbd08ba..805443b 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -354,10 +354,17 @@ static int nfs_write_begin(struct file *file, struct address_space *mapping,
 	*pagep = page;
 
 	ret = nfs_flush_incompatible(file, page);
-	if (ret) {
-		unlock_page(page);
-		page_cache_release(page);
-	}
+	if (ret)
+		goto out_err;
+	ret = pnfs_write_begin(file, page, pos, len, fsdata);
+	if (ret)
+		goto out_err;
+	return 0;
+
+ out_err:
+	unlock_page(page);
+	page_cache_release(page);
+	*pagep = NULL;
 	return ret;
 }
 
@@ -374,6 +381,7 @@ static int nfs_write_end(struct file *file, struct address_space *mapping,
 
 	unlock_page(page);
 	page_cache_release(page);
+	pnfs_write_end_cleanup(fsdata);
 
 	if (status < 0)
 		return status;
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index e7ef1ac..14760c2 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -1566,6 +1566,36 @@ int _pnfs_try_to_read_data(struct nfs_read_data *data,
 	}
 }
 
+/*
+ * This gives the layout driver an opportunity to read in page "around"
+ * the data to be written.  It returns 0 on success, otherwise an error code
+ * which will either be passed up to user, or ignored if
+ * some previous part of write succeeded.
+ * Note the range [pos, pos+len-1] is entirely within the page.
+ */
+int _pnfs_write_begin(struct inode *inode, struct page *page,
+		      loff_t pos, unsigned len, void **fsdata)
+{
+	struct pnfs_layout_segment *lseg;
+	int status = 0;
+
+	*fsdata = kzalloc(sizeof(struct pnfs_fsdata), GFP_KERNEL);
+	if (!*fsdata)
+		return -ENOMEM;
+	lseg = pnfs_find_get_lseg(inode, pos, len, IOMODE_RW);
+	if (lseg) {
+		struct layoutdriver_io_operations *io_ops =
+			NFS_SERVER(inode)->pnfs_curr_ld->ld_io_ops;
+		status = io_ops->write_begin(lseg, page, pos, len, *fsdata);
+		put_lseg(lseg);
+		if (!status)
+			return 0;
+	}
+	kfree(*fsdata);
+	*fsdata = NULL;
+	return status;
+}
+
 /* Given an nfs request, determine if it should be flushed before proceeding.
  * It should default to returning False, returning True only if there is a
  * specific reason to flush.
@@ -1906,6 +1936,11 @@ void pnfs_free_request_data(struct nfs_page *req)
 		lo->free_request_data(req);
 }
 
+void pnfs_free_fsdata(struct pnfs_fsdata *fsdata)
+{
+	kfree(fsdata);
+}
+
 /* Callback operations for layout drivers.
  */
 struct pnfs_client_operations pnfs_ops = {
diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h
index 04ae8ec..696cdf7 100644
--- a/fs/nfs/pnfs.h
+++ b/fs/nfs/pnfs.h
@@ -55,10 +55,13 @@ void pnfs_pageio_init_write(struct nfs_pageio_descriptor *, struct inode *);
 void pnfs_update_layout_commit(struct inode *, struct list_head *, pgoff_t, unsigned int);
 int pnfs_flush_one(struct inode *, struct list_head *, unsigned int, size_t, int);
 void pnfs_free_request_data(struct nfs_page *req);
+void pnfs_free_fsdata(struct pnfs_fsdata *fsdata);
 ssize_t pnfs_file_write(struct file *, const char __user *, size_t, loff_t *);
 void pnfs_get_layout_done(struct pnfs_layout_type *,
 			  struct nfs4_pnfs_layoutget *, int);
 void pnfs_layout_release(struct pnfs_layout_type *);
+int _pnfs_write_begin(struct inode *inode, struct page *page,
+		      loff_t pos, unsigned len, void **fsdata);
 int _pnfs_do_flush(struct inode *inode, struct nfs_page *req,
 		   struct pnfs_fsdata *fsdata);
 
@@ -111,6 +114,19 @@ static inline int pnfs_try_to_commit(struct nfs_write_data *data)
 	return 1;
 }
 
+static inline int pnfs_write_begin(struct file *filp, struct page *page,
+				   loff_t pos, unsigned len, void **fsdata)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct nfs_server *nfss = NFS_SERVER(inode);
+	int status = 0;
+
+	*fsdata = NULL;
+	if (PNFS_EXISTS_LDIO_OP(nfss, write_begin))
+		status = _pnfs_write_begin(inode, page, pos, len, fsdata);
+	return status;
+}
+
 /* req may not be locked, so we have to be prepared for req->wb_page being
  * set to NULL at any time.
  */
@@ -129,6 +145,11 @@ static inline int pnfs_do_flush(struct nfs_page *req, void *fsdata)
 		return 0;
 }
 
+static inline void pnfs_write_end_cleanup(void *fsdata)
+{
+	pnfs_free_fsdata(fsdata);
+}
+
 #else  /* CONFIG_PNFS */
 
 static inline int pnfs_try_to_read_data(struct nfs_read_data *data,
@@ -154,6 +175,16 @@ static inline int pnfs_do_flush(struct nfs_page *req, void *fsdata)
 	return 0;
 }
 
+static inline int pnfs_write_begin(struct file *filp, struct page *page,
+				   loff_t pos, unsigned len, void **fsdata)
+{
+	return 0;
+}
+
+static inline void pnfs_write_end_cleanup(void *fsdata)
+{
+}
+
 #endif /* CONFIG_PNFS */
 
 #endif /* FS_NFS_PNFS_H */
diff --git a/include/linux/nfs4_pnfs.h b/include/linux/nfs4_pnfs.h
index 4bee636..4233dee 100644
--- a/include/linux/nfs4_pnfs.h
+++ b/include/linux/nfs4_pnfs.h
@@ -138,6 +138,9 @@ struct layoutdriver_io_operations {
 			       unsigned nr_pages, loff_t offset, size_t count,
 			       int sync, struct nfs_write_data *nfs_data);
 	int (*flush_one) (struct pnfs_layout_segment *, struct list_head *head, unsigned int npages, size_t count, int how);
+	int (*write_begin) (struct pnfs_layout_segment *lseg, struct page *page,
+			    loff_t pos, unsigned count,
+			    struct pnfs_fsdata *fsdata);
 	void (*free_request_data) (struct nfs_page *);
 
 
-- 
1.5.3.3



More information about the pNFS mailing list