All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andreas Gruenbacher <agruenba@redhat.com>
To: cluster-devel@redhat.com
Cc: Christoph Hellwig <hch@lst.de>,
	Dave Chinner <dchinner@redhat.com>,
	linux-fsdevel@vger.kernel.org,
	Andreas Gruenbacher <agruenba@redhat.com>
Subject: [PATCH v2 6/6] gfs2: Implement iomap buffered write support (2)
Date: Mon, 29 Jan 2018 23:18:44 +0100	[thread overview]
Message-ID: <20180129221844.19476-7-agruenba@redhat.com> (raw)
In-Reply-To: <20180129221844.19476-1-agruenba@redhat.com>

Instead of falling back to generic_file_write_iter when writing to a
stuffed file that stays stuffed, implement that case separately.  We
eventually want to get rid of the remaining users of gfs2_write_begin +
gfs2_write_end so that those functions can eventually be removed, and
generic_file_write_iter uses that interface.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/gfs2/aops.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 fs/gfs2/aops.h |  1 +
 fs/gfs2/bmap.c |  7 ++---
 fs/gfs2/bmap.h |  1 +
 fs/gfs2/file.c | 10 ++++---
 5 files changed, 104 insertions(+), 10 deletions(-)

diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index 5d843c9f9e05..01f8657905af 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -22,6 +22,7 @@
 #include <linux/backing-dev.h>
 #include <linux/uio.h>
 #include <trace/events/writeback.h>
+#include <linux/sched/signal.h>
 
 #include "gfs2.h"
 #include "incore.h"
@@ -820,7 +821,7 @@ void adjust_fs_space(struct inode *inode)
  * This copies the data from the page into the inode block after
  * the inode data structure itself.
  *
- * Returns: errno
+ * Returns: copied bytes or errno
  */
 static int gfs2_stuffed_write_end(struct inode *inode, struct buffer_head *dibh,
 				  loff_t pos, unsigned copied,
@@ -850,6 +851,96 @@ static int gfs2_stuffed_write_end(struct inode *inode, struct buffer_head *dibh,
 	return copied;
 }
 
+ssize_t gfs2_stuffed_write(struct kiocb *iocb, struct iov_iter *from)
+{
+	struct inode *inode = iocb->ki_filp->f_mapping->host;
+	struct gfs2_inode *ip = GFS2_I(inode);
+	struct gfs2_sbd *sdp = GFS2_SB(inode);
+	loff_t pos = iocb->ki_pos;
+	struct page *page = NULL;
+	ssize_t written = 0, ret;
+
+	BUG_ON(!gfs2_glock_is_locked_by_me(ip->i_gl));
+	BUG_ON(!gfs2_is_stuffed(ip));
+
+	ret = gfs2_trans_begin(sdp, RES_DINODE, 0);
+	if (ret)
+		return ret;
+
+	do {
+		struct buffer_head *dibh;
+		unsigned long offset;
+		unsigned long bytes;
+		size_t copied;
+
+		offset = pos & (PAGE_SIZE - 1);
+		bytes = min_t(unsigned long, PAGE_SIZE - offset,
+					     iov_iter_count(from));
+again:
+		/*
+		 * Bring in the user page that we will copy from _first_.
+		 * Otherwise there's a nasty deadlock on copying from the
+		 * same page as we're writing to, without it being marked
+		 * up-to-date.
+		 *
+		 * Not only is this an optimisation, but it is also required
+		 * to check that the address is actually valid, when atomic
+		 * usercopies are used, below.
+		 */
+		if (unlikely(iov_iter_fault_in_readable(from, bytes))) {
+			ret = -EFAULT;
+			goto out;
+		}
+
+		if (fatal_signal_pending(current)) {
+			ret = -EINTR;
+			goto out;
+		}
+
+		page = grab_cache_page_write_begin(inode->i_mapping, pos >> PAGE_SHIFT, AOP_FLAG_NOFS);
+		if (!page)
+			return -ENOMEM;
+
+		if (!PageUptodate(page)) {
+			ret = stuffed_readpage(ip, page);
+			if (ret)
+				goto out;
+		}
+
+		if (mapping_writably_mapped(inode->i_mapping))
+			flush_dcache_page(page);
+
+		copied = iov_iter_copy_from_user_atomic(page, from, pos, bytes);
+
+		flush_dcache_page(page);
+
+		ret = gfs2_meta_inode_buffer(ip, &dibh);
+		if (ret)
+			goto out;
+		ret = gfs2_stuffed_write_end(inode, dibh, pos, copied, page);
+		page = NULL;
+		brelse(dibh);
+		if (ret < 0)
+			goto out;
+
+		iov_iter_advance(from, copied);
+		if (unlikely(copied == 0)) {
+			bytes = iov_iter_single_seg_count(from);
+			goto again;
+		}
+		pos += copied;
+		written += copied;
+	} while (iov_iter_count(from));
+
+out:
+	if (page) {
+		unlock_page(page);
+		put_page(page);
+	}
+	gfs2_trans_end(sdp);
+	return written ? written : ret;
+}
+
 /**
  * gfs2_write_end
  * @file: The file to write to
@@ -863,7 +954,7 @@ static int gfs2_stuffed_write_end(struct inode *inode, struct buffer_head *dibh,
  * The main write_end function for GFS2. We just put our locking around the VFS
  * provided functions.
  *
- * Returns: errno
+ * Returns: copied bytes or errno
  */
 
 static int gfs2_write_end(struct file *file, struct address_space *mapping,
diff --git a/fs/gfs2/aops.h b/fs/gfs2/aops.h
index 9a2fa61d8ca4..600dbe14c4b7 100644
--- a/fs/gfs2/aops.h
+++ b/fs/gfs2/aops.h
@@ -11,6 +11,7 @@
 
 #include "incore.h"
 
+extern ssize_t gfs2_stuffed_write(struct kiocb *iocb, struct iov_iter *from);
 extern void adjust_fs_space(struct inode *inode);
 extern void gfs2_page_add_databufs(struct gfs2_inode *ip, struct page *page,
 				   unsigned int from, unsigned int len);
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index 0b26982dab3a..05b1a6198d35 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -826,7 +826,7 @@ static int gfs2_write_lock(struct inode *inode)
 	return error;
 }
 
-static void gfs2_write_unlock(struct inode *inode)
+void gfs2_write_unlock(struct inode *inode)
 {
 	struct gfs2_inode *ip = GFS2_I(inode);
 	struct gfs2_sbd *sdp = GFS2_SB(inode);
@@ -855,8 +855,8 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos, loff_t length
 
 	if (gfs2_is_stuffed(ip)) {
 		if (pos + length <= gfs2_max_stuffed_size(ip)) {
-			ret = -ENOTBLK;
-			goto out_unlock;
+			/* Keep the inode locked! */
+			return -ENOTBLK;
 		}
 	}
 
@@ -927,7 +927,6 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos, loff_t length
 		gfs2_quota_unlock(ip);
 out_release:
 	release_metapath(&mp);
-out_unlock:
 	gfs2_write_unlock(inode);
 	return ret;
 }
diff --git a/fs/gfs2/bmap.h b/fs/gfs2/bmap.h
index 5d563c29cb0a..7aa4cf56a6b2 100644
--- a/fs/gfs2/bmap.h
+++ b/fs/gfs2/bmap.h
@@ -62,5 +62,6 @@ extern int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset,
 extern int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd);
 extern void gfs2_free_journal_extents(struct gfs2_jdesc *jd);
 extern int __gfs2_punch_hole(struct file *file, loff_t offset, loff_t length);
+extern void gfs2_write_unlock(struct inode *inode);
 
 #endif /* __BMAP_DOT_H__ */
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index 69096c3c3bfd..1674ed004cb0 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -31,6 +31,7 @@
 #include "gfs2.h"
 #include "incore.h"
 #include "bmap.h"
+#include "aops.h"
 #include "dir.h"
 #include "glock.h"
 #include "glops.h"
@@ -712,6 +713,10 @@ static ssize_t gfs2_file_buffered_write(struct kiocb *iocb, struct iov_iter *fro
 	current->backing_dev_info = inode_to_bdi(inode);
 
 	ret = iomap_file_buffered_write(iocb, from, &gfs2_iomap_ops);
+	if (ret == -ENOTBLK) {
+		ret = gfs2_stuffed_write(iocb, from);
+		gfs2_write_unlock(inode);
+	}
 
 	current->backing_dev_info = NULL;
 
@@ -761,10 +766,7 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 
 	if (iocb->ki_flags & IOCB_DIRECT)
 		return generic_file_write_iter(iocb, from);
-	ret = gfs2_file_buffered_write(iocb, from);
-	if (ret == -ENOTBLK)
-		ret = generic_file_write_iter(iocb, from);
-	return ret;
+	return gfs2_file_buffered_write(iocb, from);
 }
 
 static int fallocate_chunk(struct inode *inode, loff_t offset, loff_t len,
-- 
2.14.3

WARNING: multiple messages have this Message-ID (diff)
From: Andreas Gruenbacher <agruenba@redhat.com>
To: cluster-devel.redhat.com
Subject: [Cluster-devel] [PATCH v2 6/6] gfs2: Implement iomap buffered write support (2)
Date: Mon, 29 Jan 2018 23:18:44 +0100	[thread overview]
Message-ID: <20180129221844.19476-7-agruenba@redhat.com> (raw)
In-Reply-To: <20180129221844.19476-1-agruenba@redhat.com>

Instead of falling back to generic_file_write_iter when writing to a
stuffed file that stays stuffed, implement that case separately.  We
eventually want to get rid of the remaining users of gfs2_write_begin +
gfs2_write_end so that those functions can eventually be removed, and
generic_file_write_iter uses that interface.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/gfs2/aops.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 fs/gfs2/aops.h |  1 +
 fs/gfs2/bmap.c |  7 ++---
 fs/gfs2/bmap.h |  1 +
 fs/gfs2/file.c | 10 ++++---
 5 files changed, 104 insertions(+), 10 deletions(-)

diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index 5d843c9f9e05..01f8657905af 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -22,6 +22,7 @@
 #include <linux/backing-dev.h>
 #include <linux/uio.h>
 #include <trace/events/writeback.h>
+#include <linux/sched/signal.h>
 
 #include "gfs2.h"
 #include "incore.h"
@@ -820,7 +821,7 @@ void adjust_fs_space(struct inode *inode)
  * This copies the data from the page into the inode block after
  * the inode data structure itself.
  *
- * Returns: errno
+ * Returns: copied bytes or errno
  */
 static int gfs2_stuffed_write_end(struct inode *inode, struct buffer_head *dibh,
 				  loff_t pos, unsigned copied,
@@ -850,6 +851,96 @@ static int gfs2_stuffed_write_end(struct inode *inode, struct buffer_head *dibh,
 	return copied;
 }
 
+ssize_t gfs2_stuffed_write(struct kiocb *iocb, struct iov_iter *from)
+{
+	struct inode *inode = iocb->ki_filp->f_mapping->host;
+	struct gfs2_inode *ip = GFS2_I(inode);
+	struct gfs2_sbd *sdp = GFS2_SB(inode);
+	loff_t pos = iocb->ki_pos;
+	struct page *page = NULL;
+	ssize_t written = 0, ret;
+
+	BUG_ON(!gfs2_glock_is_locked_by_me(ip->i_gl));
+	BUG_ON(!gfs2_is_stuffed(ip));
+
+	ret = gfs2_trans_begin(sdp, RES_DINODE, 0);
+	if (ret)
+		return ret;
+
+	do {
+		struct buffer_head *dibh;
+		unsigned long offset;
+		unsigned long bytes;
+		size_t copied;
+
+		offset = pos & (PAGE_SIZE - 1);
+		bytes = min_t(unsigned long, PAGE_SIZE - offset,
+					     iov_iter_count(from));
+again:
+		/*
+		 * Bring in the user page that we will copy from _first_.
+		 * Otherwise there's a nasty deadlock on copying from the
+		 * same page as we're writing to, without it being marked
+		 * up-to-date.
+		 *
+		 * Not only is this an optimisation, but it is also required
+		 * to check that the address is actually valid, when atomic
+		 * usercopies are used, below.
+		 */
+		if (unlikely(iov_iter_fault_in_readable(from, bytes))) {
+			ret = -EFAULT;
+			goto out;
+		}
+
+		if (fatal_signal_pending(current)) {
+			ret = -EINTR;
+			goto out;
+		}
+
+		page = grab_cache_page_write_begin(inode->i_mapping, pos >> PAGE_SHIFT, AOP_FLAG_NOFS);
+		if (!page)
+			return -ENOMEM;
+
+		if (!PageUptodate(page)) {
+			ret = stuffed_readpage(ip, page);
+			if (ret)
+				goto out;
+		}
+
+		if (mapping_writably_mapped(inode->i_mapping))
+			flush_dcache_page(page);
+
+		copied = iov_iter_copy_from_user_atomic(page, from, pos, bytes);
+
+		flush_dcache_page(page);
+
+		ret = gfs2_meta_inode_buffer(ip, &dibh);
+		if (ret)
+			goto out;
+		ret = gfs2_stuffed_write_end(inode, dibh, pos, copied, page);
+		page = NULL;
+		brelse(dibh);
+		if (ret < 0)
+			goto out;
+
+		iov_iter_advance(from, copied);
+		if (unlikely(copied == 0)) {
+			bytes = iov_iter_single_seg_count(from);
+			goto again;
+		}
+		pos += copied;
+		written += copied;
+	} while (iov_iter_count(from));
+
+out:
+	if (page) {
+		unlock_page(page);
+		put_page(page);
+	}
+	gfs2_trans_end(sdp);
+	return written ? written : ret;
+}
+
 /**
  * gfs2_write_end
  * @file: The file to write to
@@ -863,7 +954,7 @@ static int gfs2_stuffed_write_end(struct inode *inode, struct buffer_head *dibh,
  * The main write_end function for GFS2. We just put our locking around the VFS
  * provided functions.
  *
- * Returns: errno
+ * Returns: copied bytes or errno
  */
 
 static int gfs2_write_end(struct file *file, struct address_space *mapping,
diff --git a/fs/gfs2/aops.h b/fs/gfs2/aops.h
index 9a2fa61d8ca4..600dbe14c4b7 100644
--- a/fs/gfs2/aops.h
+++ b/fs/gfs2/aops.h
@@ -11,6 +11,7 @@
 
 #include "incore.h"
 
+extern ssize_t gfs2_stuffed_write(struct kiocb *iocb, struct iov_iter *from);
 extern void adjust_fs_space(struct inode *inode);
 extern void gfs2_page_add_databufs(struct gfs2_inode *ip, struct page *page,
 				   unsigned int from, unsigned int len);
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index 0b26982dab3a..05b1a6198d35 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -826,7 +826,7 @@ static int gfs2_write_lock(struct inode *inode)
 	return error;
 }
 
-static void gfs2_write_unlock(struct inode *inode)
+void gfs2_write_unlock(struct inode *inode)
 {
 	struct gfs2_inode *ip = GFS2_I(inode);
 	struct gfs2_sbd *sdp = GFS2_SB(inode);
@@ -855,8 +855,8 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos, loff_t length
 
 	if (gfs2_is_stuffed(ip)) {
 		if (pos + length <= gfs2_max_stuffed_size(ip)) {
-			ret = -ENOTBLK;
-			goto out_unlock;
+			/* Keep the inode locked! */
+			return -ENOTBLK;
 		}
 	}
 
@@ -927,7 +927,6 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos, loff_t length
 		gfs2_quota_unlock(ip);
 out_release:
 	release_metapath(&mp);
-out_unlock:
 	gfs2_write_unlock(inode);
 	return ret;
 }
diff --git a/fs/gfs2/bmap.h b/fs/gfs2/bmap.h
index 5d563c29cb0a..7aa4cf56a6b2 100644
--- a/fs/gfs2/bmap.h
+++ b/fs/gfs2/bmap.h
@@ -62,5 +62,6 @@ extern int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset,
 extern int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd);
 extern void gfs2_free_journal_extents(struct gfs2_jdesc *jd);
 extern int __gfs2_punch_hole(struct file *file, loff_t offset, loff_t length);
+extern void gfs2_write_unlock(struct inode *inode);
 
 #endif /* __BMAP_DOT_H__ */
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index 69096c3c3bfd..1674ed004cb0 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -31,6 +31,7 @@
 #include "gfs2.h"
 #include "incore.h"
 #include "bmap.h"
+#include "aops.h"
 #include "dir.h"
 #include "glock.h"
 #include "glops.h"
@@ -712,6 +713,10 @@ static ssize_t gfs2_file_buffered_write(struct kiocb *iocb, struct iov_iter *fro
 	current->backing_dev_info = inode_to_bdi(inode);
 
 	ret = iomap_file_buffered_write(iocb, from, &gfs2_iomap_ops);
+	if (ret == -ENOTBLK) {
+		ret = gfs2_stuffed_write(iocb, from);
+		gfs2_write_unlock(inode);
+	}
 
 	current->backing_dev_info = NULL;
 
@@ -761,10 +766,7 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 
 	if (iocb->ki_flags & IOCB_DIRECT)
 		return generic_file_write_iter(iocb, from);
-	ret = gfs2_file_buffered_write(iocb, from);
-	if (ret == -ENOTBLK)
-		ret = generic_file_write_iter(iocb, from);
-	return ret;
+	return gfs2_file_buffered_write(iocb, from);
 }
 
 static int fallocate_chunk(struct inode *inode, loff_t offset, loff_t len,
-- 
2.14.3



  parent reply	other threads:[~2018-01-29 22:19 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-01-29 22:18 [PATCH v2 0/6] gfs2 iomap buffered write support Andreas Gruenbacher
2018-01-29 22:18 ` [Cluster-devel] " Andreas Gruenbacher
2018-01-29 22:18 ` [PATCH v2 1/6] gfs2: gfs2_stuffed_write_end cleanup Andreas Gruenbacher
2018-01-29 22:18   ` [Cluster-devel] " Andreas Gruenbacher
2018-01-29 22:18 ` [PATCH v2 2/6] gfs2: Remove ordered write mode handling from gfs2_trans_add_data Andreas Gruenbacher
2018-01-29 22:18   ` [Cluster-devel] " Andreas Gruenbacher
2018-01-30  9:39   ` Steven Whitehouse
2018-01-30 13:22     ` Andreas Gruenbacher
2018-01-29 22:18 ` [PATCH v2 3/6] gfs2: Iomap cleanups and improvements Andreas Gruenbacher
2018-01-29 22:18   ` [Cluster-devel] " Andreas Gruenbacher
2018-01-29 22:18 ` [PATCH v2 4/6] iomap: Add write_{begin,end} iomap operations Andreas Gruenbacher
2018-01-29 22:18   ` [Cluster-devel] [PATCH v2 4/6] iomap: Add write_{begin, end} " Andreas Gruenbacher
2018-01-29 22:18 ` [PATCH v2 5/6] gfs2: Implement iomap buffered write support (1) Andreas Gruenbacher
2018-01-29 22:18   ` [Cluster-devel] " Andreas Gruenbacher
2018-01-29 22:18 ` Andreas Gruenbacher [this message]
2018-01-29 22:18   ` [Cluster-devel] [PATCH v2 6/6] gfs2: Implement iomap buffered write support (2) Andreas Gruenbacher

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180129221844.19476-7-agruenba@redhat.com \
    --to=agruenba@redhat.com \
    --cc=cluster-devel@redhat.com \
    --cc=dchinner@redhat.com \
    --cc=hch@lst.de \
    --cc=linux-fsdevel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.