|
@@ -309,6 +309,16 @@ int filemap_check_errors(struct address_space *mapping)
|
|
|
}
|
|
|
EXPORT_SYMBOL(filemap_check_errors);
|
|
|
|
|
|
+static int filemap_check_and_keep_errors(struct address_space *mapping)
|
|
|
+{
|
|
|
+ /* Check for outstanding write errors */
|
|
|
+ if (test_bit(AS_EIO, &mapping->flags))
|
|
|
+ return -EIO;
|
|
|
+ if (test_bit(AS_ENOSPC, &mapping->flags))
|
|
|
+ return -ENOSPC;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* __filemap_fdatawrite_range - start writeback on mapping dirty pages in range
|
|
|
* @mapping: address space structure to write
|
|
@@ -408,17 +418,16 @@ bool filemap_range_has_page(struct address_space *mapping,
|
|
|
}
|
|
|
EXPORT_SYMBOL(filemap_range_has_page);
|
|
|
|
|
|
-static int __filemap_fdatawait_range(struct address_space *mapping,
|
|
|
+static void __filemap_fdatawait_range(struct address_space *mapping,
|
|
|
loff_t start_byte, loff_t end_byte)
|
|
|
{
|
|
|
pgoff_t index = start_byte >> PAGE_SHIFT;
|
|
|
pgoff_t end = end_byte >> PAGE_SHIFT;
|
|
|
struct pagevec pvec;
|
|
|
int nr_pages;
|
|
|
- int ret = 0;
|
|
|
|
|
|
if (end_byte < start_byte)
|
|
|
- goto out;
|
|
|
+ return;
|
|
|
|
|
|
pagevec_init(&pvec, 0);
|
|
|
while ((index <= end) &&
|
|
@@ -435,14 +444,11 @@ static int __filemap_fdatawait_range(struct address_space *mapping,
|
|
|
continue;
|
|
|
|
|
|
wait_on_page_writeback(page);
|
|
|
- if (TestClearPageError(page))
|
|
|
- ret = -EIO;
|
|
|
+ ClearPageError(page);
|
|
|
}
|
|
|
pagevec_release(&pvec);
|
|
|
cond_resched();
|
|
|
}
|
|
|
-out:
|
|
|
- return ret;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -462,14 +468,8 @@ out:
|
|
|
int filemap_fdatawait_range(struct address_space *mapping, loff_t start_byte,
|
|
|
loff_t end_byte)
|
|
|
{
|
|
|
- int ret, ret2;
|
|
|
-
|
|
|
- ret = __filemap_fdatawait_range(mapping, start_byte, end_byte);
|
|
|
- ret2 = filemap_check_errors(mapping);
|
|
|
- if (!ret)
|
|
|
- ret = ret2;
|
|
|
-
|
|
|
- return ret;
|
|
|
+ __filemap_fdatawait_range(mapping, start_byte, end_byte);
|
|
|
+ return filemap_check_errors(mapping);
|
|
|
}
|
|
|
EXPORT_SYMBOL(filemap_fdatawait_range);
|
|
|
|
|
@@ -485,15 +485,17 @@ EXPORT_SYMBOL(filemap_fdatawait_range);
|
|
|
* call sites are system-wide / filesystem-wide data flushers: e.g. sync(2),
|
|
|
* fsfreeze(8)
|
|
|
*/
|
|
|
-void filemap_fdatawait_keep_errors(struct address_space *mapping)
|
|
|
+int filemap_fdatawait_keep_errors(struct address_space *mapping)
|
|
|
{
|
|
|
loff_t i_size = i_size_read(mapping->host);
|
|
|
|
|
|
if (i_size == 0)
|
|
|
- return;
|
|
|
+ return 0;
|
|
|
|
|
|
__filemap_fdatawait_range(mapping, 0, i_size - 1);
|
|
|
+ return filemap_check_and_keep_errors(mapping);
|
|
|
}
|
|
|
+EXPORT_SYMBOL(filemap_fdatawait_keep_errors);
|
|
|
|
|
|
/**
|
|
|
* filemap_fdatawait - wait for all under-writeback pages to complete
|
|
@@ -535,6 +537,9 @@ int filemap_write_and_wait(struct address_space *mapping)
|
|
|
int err2 = filemap_fdatawait(mapping);
|
|
|
if (!err)
|
|
|
err = err2;
|
|
|
+ } else {
|
|
|
+ /* Clear any previously stored errors */
|
|
|
+ filemap_check_errors(mapping);
|
|
|
}
|
|
|
} else {
|
|
|
err = filemap_check_errors(mapping);
|
|
@@ -569,6 +574,9 @@ int filemap_write_and_wait_range(struct address_space *mapping,
|
|
|
lstart, lend);
|
|
|
if (!err)
|
|
|
err = err2;
|
|
|
+ } else {
|
|
|
+ /* Clear any previously stored errors */
|
|
|
+ filemap_check_errors(mapping);
|
|
|
}
|
|
|
} else {
|
|
|
err = filemap_check_errors(mapping);
|
|
@@ -577,6 +585,90 @@ int filemap_write_and_wait_range(struct address_space *mapping,
|
|
|
}
|
|
|
EXPORT_SYMBOL(filemap_write_and_wait_range);
|
|
|
|
|
|
+void __filemap_set_wb_err(struct address_space *mapping, int err)
|
|
|
+{
|
|
|
+ errseq_t eseq = __errseq_set(&mapping->wb_err, err);
|
|
|
+
|
|
|
+ trace_filemap_set_wb_err(mapping, eseq);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(__filemap_set_wb_err);
|
|
|
+
|
|
|
+/**
|
|
|
+ * file_check_and_advance_wb_err - report wb error (if any) that was previously
|
|
|
+ * and advance wb_err to current one
|
|
|
+ * @file: struct file on which the error is being reported
|
|
|
+ *
|
|
|
+ * When userland calls fsync (or something like nfsd does the equivalent), we
|
|
|
+ * want to report any writeback errors that occurred since the last fsync (or
|
|
|
+ * since the file was opened if there haven't been any).
|
|
|
+ *
|
|
|
+ * Grab the wb_err from the mapping. If it matches what we have in the file,
|
|
|
+ * then just quickly return 0. The file is all caught up.
|
|
|
+ *
|
|
|
+ * If it doesn't match, then take the mapping value, set the "seen" flag in
|
|
|
+ * it and try to swap it into place. If it works, or another task beat us
|
|
|
+ * to it with the new value, then update the f_wb_err and return the error
|
|
|
+ * portion. The error at this point must be reported via proper channels
|
|
|
+ * (a'la fsync, or NFS COMMIT operation, etc.).
|
|
|
+ *
|
|
|
+ * While we handle mapping->wb_err with atomic operations, the f_wb_err
|
|
|
+ * value is protected by the f_lock since we must ensure that it reflects
|
|
|
+ * the latest value swapped in for this file descriptor.
|
|
|
+ */
|
|
|
+int file_check_and_advance_wb_err(struct file *file)
|
|
|
+{
|
|
|
+ int err = 0;
|
|
|
+ errseq_t old = READ_ONCE(file->f_wb_err);
|
|
|
+ struct address_space *mapping = file->f_mapping;
|
|
|
+
|
|
|
+ /* Locklessly handle the common case where nothing has changed */
|
|
|
+ if (errseq_check(&mapping->wb_err, old)) {
|
|
|
+ /* Something changed, must use slow path */
|
|
|
+ spin_lock(&file->f_lock);
|
|
|
+ old = file->f_wb_err;
|
|
|
+ err = errseq_check_and_advance(&mapping->wb_err,
|
|
|
+ &file->f_wb_err);
|
|
|
+ trace_file_check_and_advance_wb_err(file, old);
|
|
|
+ spin_unlock(&file->f_lock);
|
|
|
+ }
|
|
|
+ return err;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(file_check_and_advance_wb_err);
|
|
|
+
|
|
|
+/**
|
|
|
+ * file_write_and_wait_range - write out & wait on a file range
|
|
|
+ * @file: file pointing to address_space with pages
|
|
|
+ * @lstart: offset in bytes where the range starts
|
|
|
+ * @lend: offset in bytes where the range ends (inclusive)
|
|
|
+ *
|
|
|
+ * Write out and wait upon file offsets lstart->lend, inclusive.
|
|
|
+ *
|
|
|
+ * Note that @lend is inclusive (describes the last byte to be written) so
|
|
|
+ * that this function can be used to write to the very end-of-file (end = -1).
|
|
|
+ *
|
|
|
+ * After writing out and waiting on the data, we check and advance the
|
|
|
+ * f_wb_err cursor to the latest value, and return any errors detected there.
|
|
|
+ */
|
|
|
+int file_write_and_wait_range(struct file *file, loff_t lstart, loff_t lend)
|
|
|
+{
|
|
|
+ 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_range(mapping, lstart, lend,
|
|
|
+ WB_SYNC_ALL);
|
|
|
+ /* See comment of filemap_write_and_wait() */
|
|
|
+ if (err != -EIO)
|
|
|
+ __filemap_fdatawait_range(mapping, lstart, lend);
|
|
|
+ }
|
|
|
+ err2 = file_check_and_advance_wb_err(file);
|
|
|
+ if (!err)
|
|
|
+ err = err2;
|
|
|
+ return err;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(file_write_and_wait_range);
|
|
|
+
|
|
|
/**
|
|
|
* replace_page_cache_page - replace a pagecache page with a new one
|
|
|
* @old: page to be replaced
|