diff mbox

[v2,2/4] mm: add file_fdatawait_range and file_write_and_wait

Message ID 20170726175538.13885-3-jlayton@kernel.org (mailing list archive)
State New, archived
Headers show

Commit Message

Jeff Layton July 26, 2017, 5:55 p.m. UTC
From: Jeff Layton <jlayton@redhat.com>

Some filesystem fsync routines will need these.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 include/linux/fs.h |  7 ++++++-
 mm/filemap.c       | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 62 insertions(+), 1 deletion(-)

Comments

Matthew Wilcox July 26, 2017, 7:13 p.m. UTC | #1
On Wed, Jul 26, 2017 at 01:55:36PM -0400, Jeff Layton wrote:
> +int file_write_and_wait(struct file *file)
> +{
> +	int err = 0, err2;
> +	struct address_space *mapping = file->f_mapping;
> +
> +	if ((!dax_mapping(mapping) && mapping->nrpages) ||
> +	    (dax_mapping(mapping) && mapping->nrexceptional)) {

Since patch 1 exists, shouldn't this use the new helper?

> +		err = filemap_fdatawrite(mapping);
> +		/* See comment of filemap_write_and_wait() */
> +		if (err != -EIO) {
> +			loff_t i_size = i_size_read(mapping->host);
> +
> +			if (i_size != 0)
> +				__filemap_fdatawait_range(mapping, 0,
> +							  i_size - 1);
> +		}
> +	}
> +	err2 = file_check_and_advance_wb_err(file);
> +	if (!err)
> +		err = err2;
> +	return err;

Would this be clearer written as:

	if (err)
		return err;
	return err2;

or even ...

	return err ? err : err2;
Bob Peterson July 26, 2017, 7:50 p.m. UTC | #2
----- Original Message -----
| From: Jeff Layton <jlayton@redhat.com>
| 
| Some filesystem fsync routines will need these.
| 
| Signed-off-by: Jeff Layton <jlayton@redhat.com>
| ---
|  include/linux/fs.h |  7 ++++++-
|  mm/filemap.c       | 56
|  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
|  2 files changed, 62 insertions(+), 1 deletion(-)
(snip)
| diff --git a/mm/filemap.c b/mm/filemap.c
| index 72e46e6f0d9a..b904a8dfa43d 100644
| --- a/mm/filemap.c
| +++ b/mm/filemap.c
(snip)
| @@ -675,6 +698,39 @@ int file_write_and_wait_range(struct file *file, loff_t
| lstart, loff_t lend)
|  EXPORT_SYMBOL(file_write_and_wait_range);
|  
|  /**
| + * file_write_and_wait - write out whole file and wait on it and return any
| + * 			 writeback errors since we last checked
| + * @file: file to write back and wait on
| + *
| + * Write back the whole file and wait on its mapping. Afterward, check for
| + * errors that may have occurred since our file->f_wb_err cursor was last
| + * updated.
| + */
| +int file_write_and_wait(struct file *file)
| +{
| +	int err = 0, err2;
| +	struct address_space *mapping = file->f_mapping;
| +
| +	if ((!dax_mapping(mapping) && mapping->nrpages) ||
| +	    (dax_mapping(mapping) && mapping->nrexceptional)) {

Seems like we should make the new function mapping_needs_writeback more
central (mm.h or fs.h?) and call it here ^.

| +		err = filemap_fdatawrite(mapping);
| +		/* See comment of filemap_write_and_wait() */
| +		if (err != -EIO) {
| +			loff_t i_size = i_size_read(mapping->host);
| +
| +			if (i_size != 0)
| +				__filemap_fdatawait_range(mapping, 0,
| +							  i_size - 1);
| +		}
| +	}
| +	err2 = file_check_and_advance_wb_err(file);
| +	if (!err)
| +		err = err2;
| +	return err;

In the past, I've seen more elegant constructs like:
        return (err ? err : err2);
but I don't know what's considered more ugly or hackish.

Regards,

Bob Peterson
Red Hat File Systems
Jeff Layton July 26, 2017, 10:18 p.m. UTC | #3
On Wed, 2017-07-26 at 12:13 -0700, Matthew Wilcox wrote:
> On Wed, Jul 26, 2017 at 01:55:36PM -0400, Jeff Layton wrote:
> > +int file_write_and_wait(struct file *file)
> > +{
> > +	int err = 0, err2;
> > +	struct address_space *mapping = file->f_mapping;
> > +
> > +	if ((!dax_mapping(mapping) && mapping->nrpages) ||
> > +	    (dax_mapping(mapping) && mapping->nrexceptional)) {
> 
> Since patch 1 exists, shouldn't this use the new helper?
> 

<facepalm>

yes, will fix


> > +		err = filemap_fdatawrite(mapping);
> > +		/* See comment of filemap_write_and_wait() */
> > +		if (err != -EIO) {
> > +			loff_t i_size = i_size_read(mapping->host);
> > +
> > +			if (i_size != 0)
> > +				__filemap_fdatawait_range(mapping, 0,
> > +							  i_size - 1);
> > +		}
> > +	}
> > +	err2 = file_check_and_advance_wb_err(file);
> > +	if (!err)
> > +		err = err2;
> > +	return err;
> 
> Would this be clearer written as:
> 
> 	if (err)
> 		return err;
> 	return err2;
> 
> or even ...
> 
> 	return err ? err : err2;
> 

Meh -- I like it the way I have it. If we don't have an error already,
then just take the one from the check and advance.

That said, I don't have a terribly strong preference here, so if anyone
does, then I can be easily persuaded.
Jan Kara July 27, 2017, 8:49 a.m. UTC | #4
On Wed 26-07-17 13:55:36, Jeff Layton wrote:
> +int file_write_and_wait(struct file *file)
> +{
> +	int err = 0, err2;
> +	struct address_space *mapping = file->f_mapping;
> +
> +	if ((!dax_mapping(mapping) && mapping->nrpages) ||
> +	    (dax_mapping(mapping) && mapping->nrexceptional)) {
> +		err = filemap_fdatawrite(mapping);
> +		/* See comment of filemap_write_and_wait() */
> +		if (err != -EIO) {
> +			loff_t i_size = i_size_read(mapping->host);
> +
> +			if (i_size != 0)
> +				__filemap_fdatawait_range(mapping, 0,
> +							  i_size - 1);
> +		}
> +	}

Err, what's the i_size check doing here? I'd just pass ~0 as the end of the
range and ignore i_size. It is much easier than trying to wrap your head
around possible races with file operations modifying i_size.

								Honza
Jeff Layton July 27, 2017, 12:48 p.m. UTC | #5
On Thu, 2017-07-27 at 10:49 +0200, Jan Kara wrote:
> On Wed 26-07-17 13:55:36, Jeff Layton wrote:
> > +int file_write_and_wait(struct file *file)
> > +{
> > +	int err = 0, err2;
> > +	struct address_space *mapping = file->f_mapping;
> > +
> > +	if ((!dax_mapping(mapping) && mapping->nrpages) ||
> > +	    (dax_mapping(mapping) && mapping->nrexceptional)) {
> > +		err = filemap_fdatawrite(mapping);
> > +		/* See comment of filemap_write_and_wait() */
> > +		if (err != -EIO) {
> > +			loff_t i_size = i_size_read(mapping->host);
> > +
> > +			if (i_size != 0)
> > +				__filemap_fdatawait_range(mapping, 0,
> > +							  i_size - 1);
> > +		}
> > +	}
> 
> Err, what's the i_size check doing here? I'd just pass ~0 as the end of the
> range and ignore i_size. It is much easier than trying to wrap your head
> around possible races with file operations modifying i_size.
> 
> 								Honza

I'm basically emulating _exactly_ what filemap_write_and_wait does here,
as I'm leery of making subtle behavior changes in the actual writeback
behavior. For example:

-----------------8<----------------
static inline int __filemap_fdatawrite(struct address_space *mapping,
        int sync_mode)
{
        return __filemap_fdatawrite_range(mapping, 0, LLONG_MAX, sync_mode);
}

int filemap_fdatawrite(struct address_space *mapping)
{
        return __filemap_fdatawrite(mapping, WB_SYNC_ALL);
}
EXPORT_SYMBOL(filemap_fdatawrite);
-----------------8<----------------

...which then sets up the wbc with the right ranges and sync mode and
kicks off writepages. But then, it does the i_size_read to figure out
what range it should wait on (with the shortcut for the size == 0 case).

My assumption was that it was intentionally designed that way, but I'm
guessing from your comments that it wasn't? If so, then we can turn
file_write_and_wait a static inline wrapper around
file_write_and_wait_range.
diff mbox

Patch

diff --git a/include/linux/fs.h b/include/linux/fs.h
index 21e7df1ad613..bc57a79294f0 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2544,6 +2544,8 @@  extern int filemap_fdatawait_range(struct address_space *, loff_t lstart,
 				   loff_t lend);
 extern bool filemap_range_has_page(struct address_space *, loff_t lstart,
 				  loff_t lend);
+extern int __must_check file_fdatawait_range(struct file *file, loff_t lstart,
+						loff_t lend);
 extern int filemap_write_and_wait(struct address_space *mapping);
 extern int filemap_write_and_wait_range(struct address_space *mapping,
 				        loff_t lstart, loff_t lend);
@@ -2552,11 +2554,14 @@  extern int __filemap_fdatawrite_range(struct address_space *mapping,
 extern int filemap_fdatawrite_range(struct address_space *mapping,
 				loff_t start, loff_t end);
 extern int filemap_check_errors(struct address_space *mapping);
-
 extern void __filemap_set_wb_err(struct address_space *mapping, int err);
+
+extern int __must_check file_fdatawait_range(struct file *file, loff_t lstart,
+						loff_t lend);
 extern int __must_check file_check_and_advance_wb_err(struct file *file);
 extern int __must_check file_write_and_wait_range(struct file *file,
 						loff_t start, loff_t end);
+extern int __must_check file_write_and_wait(struct file *file);
 
 /**
  * filemap_set_wb_err - set a writeback error on an address_space
diff --git a/mm/filemap.c b/mm/filemap.c
index 72e46e6f0d9a..b904a8dfa43d 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -476,6 +476,29 @@  int filemap_fdatawait_range(struct address_space *mapping, loff_t start_byte,
 EXPORT_SYMBOL(filemap_fdatawait_range);
 
 /**
+ * file_fdatawait_range - wait for writeback to complete
+ * @file:		file pointing to address space structure to wait for
+ * @start_byte:		offset in bytes where the range starts
+ * @end_byte:		offset in bytes where the range ends (inclusive)
+ *
+ * Walk the list of under-writeback pages of the address space that file
+ * refers to, in the given range and wait for all of them.  Check error
+ * status of the address space vs. the file->f_wb_err cursor and return it.
+ *
+ * Since the error status of the file is advanced by this function,
+ * callers are responsible for checking the return value and handling and/or
+ * reporting the error.
+ */
+int file_fdatawait_range(struct file *file, loff_t start_byte, loff_t end_byte)
+{
+	struct address_space *mapping = file->f_mapping;
+
+	__filemap_fdatawait_range(mapping, start_byte, end_byte);
+	return file_check_and_advance_wb_err(file);
+}
+EXPORT_SYMBOL(file_fdatawait_range);
+
+/**
  * filemap_fdatawait_keep_errors - wait for writeback without clearing errors
  * @mapping: address space structure to wait for
  *
@@ -675,6 +698,39 @@  int file_write_and_wait_range(struct file *file, loff_t lstart, loff_t lend)
 EXPORT_SYMBOL(file_write_and_wait_range);
 
 /**
+ * file_write_and_wait - write out whole file and wait on it and return any
+ * 			 writeback errors since we last checked
+ * @file: file to write back and wait on
+ *
+ * Write back the whole file and wait on its mapping. Afterward, check for
+ * errors that may have occurred since our file->f_wb_err cursor was last
+ * updated.
+ */
+int file_write_and_wait(struct file *file)
+{
+	int err = 0, err2;
+	struct address_space *mapping = file->f_mapping;
+
+	if ((!dax_mapping(mapping) && mapping->nrpages) ||
+	    (dax_mapping(mapping) && mapping->nrexceptional)) {
+		err = filemap_fdatawrite(mapping);
+		/* See comment of filemap_write_and_wait() */
+		if (err != -EIO) {
+			loff_t i_size = i_size_read(mapping->host);
+
+			if (i_size != 0)
+				__filemap_fdatawait_range(mapping, 0,
+							  i_size - 1);
+		}
+	}
+	err2 = file_check_and_advance_wb_err(file);
+	if (!err)
+		err = err2;
+	return err;
+}
+EXPORT_SYMBOL(file_write_and_wait);
+
+/**
  * replace_page_cache_page - replace a pagecache page with a new one
  * @old:	page to be replaced
  * @new:	page to replace with