Message ID | 20170726175538.13885-3-jlayton@kernel.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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;
----- 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
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.
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
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 --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