summaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-07-07 22:38:17 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2017-07-07 22:38:17 -0400
commit088737f44bbf6378745f5b57b035e57ee3dc4750 (patch)
tree86a2b1240ea5f7a0ebca837d17a53c07cd07d62a /include
parent33198c165b7afd500f7b6b7680ef994296805ef0 (diff)
parent333427a505be1e10d8da13427dc0c33ec1976b99 (diff)
Merge tag 'for-linus-v4.13-2' of git://git.kernel.org/pub/scm/linux/kernel/git/jlayton/linux
Pull Writeback error handling updates from Jeff Layton: "This pile represents the bulk of the writeback error handling fixes that I have for this cycle. Some of the earlier patches in this pile may look trivial but they are prerequisites for later patches in the series. The aim of this set is to improve how we track and report writeback errors to userland. Most applications that care about data integrity will periodically call fsync/fdatasync/msync to ensure that their writes have made it to the backing store. For a very long time, we have tracked writeback errors using two flags in the address_space: AS_EIO and AS_ENOSPC. Those flags are set when a writeback error occurs (via mapping_set_error) and are cleared as a side-effect of filemap_check_errors (as you noted yesterday). This model really sucks for userland. Only the first task to call fsync (or msync or fdatasync) will see the error. Any subsequent task calling fsync on a file will get back 0 (unless another writeback error occurs in the interim). If I have several tasks writing to a file and calling fsync to ensure that their writes got stored, then I need to have them coordinate with one another. That's difficult enough, but in a world of containerized setups that coordination may even not be possible. But wait...it gets worse! The calls to filemap_check_errors can be buried pretty far down in the call stack, and there are internal callers of filemap_write_and_wait and the like that also end up clearing those errors. Many of those callers ignore the error return from that function or return it to userland at nonsensical times (e.g. truncate() or stat()). If I get back -EIO on a truncate, there is no reason to think that it was because some previous writeback failed, and a subsequent fsync() will (incorrectly) return 0. This pile aims to do three things: 1) ensure that when a writeback error occurs that that error will be reported to userland on a subsequent fsync/fdatasync/msync call, regardless of what internal callers are doing 2) report writeback errors on all file descriptions that were open at the time that the error occurred. This is a user-visible change, but I think most applications are written to assume this behavior anyway. Those that aren't are unlikely to be hurt by it. 3) document what filesystems should do when there is a writeback error. Today, there is very little consistency between them, and a lot of cargo-cult copying. We need to make it very clear what filesystems should do in this situation. To achieve this, the set adds a new data type (errseq_t) and then builds new writeback error tracking infrastructure around that. Once all of that is in place, we change the filesystems to use the new infrastructure for reporting wb errors to userland. Note that this is just the initial foray into cleaning up this mess. There is a lot of work remaining here: 1) convert the rest of the filesystems in a similar fashion. Once the initial set is in, then I think most other fs' will be fairly simple to convert. Hopefully most of those can in via individual filesystem trees. 2) convert internal waiters on writeback to use errseq_t for detecting errors instead of relying on the AS_* flags. I have some draft patches for this for ext4, but they are not quite ready for prime time yet. This was a discussion topic this year at LSF/MM too. If you're interested in the gory details, LWN has some good articles about this: https://lwn.net/Articles/718734/ https://lwn.net/Articles/724307/" * tag 'for-linus-v4.13-2' of git://git.kernel.org/pub/scm/linux/kernel/git/jlayton/linux: btrfs: minimal conversion to errseq_t writeback error reporting on fsync xfs: minimal conversion to errseq_t writeback error reporting ext4: use errseq_t based error handling for reporting data writeback errors fs: convert __generic_file_fsync to use errseq_t based reporting block: convert to errseq_t based writeback error tracking dax: set errors in mapping when writeback fails Documentation: flesh out the section in vfs.txt on storing and reporting writeback errors mm: set both AS_EIO/AS_ENOSPC and errseq_t in mapping_set_error fs: new infrastructure for writeback error handling and reporting lib: add errseq_t type and infrastructure for handling it mm: don't TestClearPageError in __filemap_fdatawait_range mm: clear AS_EIO/AS_ENOSPC when writeback initiation fails jbd2: don't clear and reset errors after waiting on writeback buffer: set errors in mapping at the time that the error occurs fs: check for writeback errors after syncing out buffers in generic_file_fsync buffer: use mapping_set_error instead of setting the flag mm: fix mapping_set_error call in me_pagecache_dirty
Diffstat (limited to 'include')
-rw-r--r--include/linux/buffer_head.h1
-rw-r--r--include/linux/errseq.h19
-rw-r--r--include/linux/fs.h61
-rw-r--r--include/linux/pagemap.h31
-rw-r--r--include/trace/events/filemap.h57
5 files changed, 162 insertions, 7 deletions
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index bd029e52ef5e..e0abeba3ced7 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -149,6 +149,7 @@ void buffer_check_dirty_writeback(struct page *page,
149 */ 149 */
150 150
151void mark_buffer_dirty(struct buffer_head *bh); 151void mark_buffer_dirty(struct buffer_head *bh);
152void mark_buffer_write_io_error(struct buffer_head *bh);
152void init_buffer(struct buffer_head *, bh_end_io_t *, void *); 153void init_buffer(struct buffer_head *, bh_end_io_t *, void *);
153void touch_buffer(struct buffer_head *bh); 154void touch_buffer(struct buffer_head *bh);
154void set_bh_page(struct buffer_head *bh, 155void set_bh_page(struct buffer_head *bh,
diff --git a/include/linux/errseq.h b/include/linux/errseq.h
new file mode 100644
index 000000000000..9e0d444ac88d
--- /dev/null
+++ b/include/linux/errseq.h
@@ -0,0 +1,19 @@
1#ifndef _LINUX_ERRSEQ_H
2#define _LINUX_ERRSEQ_H
3
4/* See lib/errseq.c for more info */
5
6typedef u32 errseq_t;
7
8errseq_t __errseq_set(errseq_t *eseq, int err);
9static inline void errseq_set(errseq_t *eseq, int err)
10{
11 /* Optimize for the common case of no error */
12 if (unlikely(err))
13 __errseq_set(eseq, err);
14}
15
16errseq_t errseq_sample(errseq_t *eseq);
17int errseq_check(errseq_t *eseq, errseq_t since);
18int errseq_check_and_advance(errseq_t *eseq, errseq_t *since);
19#endif
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 978fb5966a25..0cfa47125d52 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -32,6 +32,7 @@
32#include <linux/workqueue.h> 32#include <linux/workqueue.h>
33#include <linux/delayed_call.h> 33#include <linux/delayed_call.h>
34#include <linux/uuid.h> 34#include <linux/uuid.h>
35#include <linux/errseq.h>
35 36
36#include <asm/byteorder.h> 37#include <asm/byteorder.h>
37#include <uapi/linux/fs.h> 38#include <uapi/linux/fs.h>
@@ -401,6 +402,7 @@ struct address_space {
401 gfp_t gfp_mask; /* implicit gfp mask for allocations */ 402 gfp_t gfp_mask; /* implicit gfp mask for allocations */
402 struct list_head private_list; /* ditto */ 403 struct list_head private_list; /* ditto */
403 void *private_data; /* ditto */ 404 void *private_data; /* ditto */
405 errseq_t wb_err;
404} __attribute__((aligned(sizeof(long)))); 406} __attribute__((aligned(sizeof(long))));
405 /* 407 /*
406 * On most architectures that alignment is already the case; but 408 * On most architectures that alignment is already the case; but
@@ -879,6 +881,7 @@ struct file {
879 struct list_head f_tfile_llink; 881 struct list_head f_tfile_llink;
880#endif /* #ifdef CONFIG_EPOLL */ 882#endif /* #ifdef CONFIG_EPOLL */
881 struct address_space *f_mapping; 883 struct address_space *f_mapping;
884 errseq_t f_wb_err;
882} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */ 885} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
883 886
884struct file_handle { 887struct file_handle {
@@ -2536,7 +2539,7 @@ extern int write_inode_now(struct inode *, int);
2536extern int filemap_fdatawrite(struct address_space *); 2539extern int filemap_fdatawrite(struct address_space *);
2537extern int filemap_flush(struct address_space *); 2540extern int filemap_flush(struct address_space *);
2538extern int filemap_fdatawait(struct address_space *); 2541extern int filemap_fdatawait(struct address_space *);
2539extern void filemap_fdatawait_keep_errors(struct address_space *); 2542extern int filemap_fdatawait_keep_errors(struct address_space *mapping);
2540extern int filemap_fdatawait_range(struct address_space *, loff_t lstart, 2543extern int filemap_fdatawait_range(struct address_space *, loff_t lstart,
2541 loff_t lend); 2544 loff_t lend);
2542extern bool filemap_range_has_page(struct address_space *, loff_t lstart, 2545extern bool filemap_range_has_page(struct address_space *, loff_t lstart,
@@ -2550,6 +2553,62 @@ extern int filemap_fdatawrite_range(struct address_space *mapping,
2550 loff_t start, loff_t end); 2553 loff_t start, loff_t end);
2551extern int filemap_check_errors(struct address_space *mapping); 2554extern int filemap_check_errors(struct address_space *mapping);
2552 2555
2556extern void __filemap_set_wb_err(struct address_space *mapping, int err);
2557extern int __must_check file_check_and_advance_wb_err(struct file *file);
2558extern int __must_check file_write_and_wait_range(struct file *file,
2559 loff_t start, loff_t end);
2560
2561/**
2562 * filemap_set_wb_err - set a writeback error on an address_space
2563 * @mapping: mapping in which to set writeback error
2564 * @err: error to be set in mapping
2565 *
2566 * When writeback fails in some way, we must record that error so that
2567 * userspace can be informed when fsync and the like are called. We endeavor
2568 * to report errors on any file that was open at the time of the error. Some
2569 * internal callers also need to know when writeback errors have occurred.
2570 *
2571 * When a writeback error occurs, most filesystems will want to call
2572 * filemap_set_wb_err to record the error in the mapping so that it will be
2573 * automatically reported whenever fsync is called on the file.
2574 *
2575 * FIXME: mention FS_* flag here?
2576 */
2577static inline void filemap_set_wb_err(struct address_space *mapping, int err)
2578{
2579 /* Fastpath for common case of no error */
2580 if (unlikely(err))
2581 __filemap_set_wb_err(mapping, err);
2582}
2583
2584/**
2585 * filemap_check_wb_error - has an error occurred since the mark was sampled?
2586 * @mapping: mapping to check for writeback errors
2587 * @since: previously-sampled errseq_t
2588 *
2589 * Grab the errseq_t value from the mapping, and see if it has changed "since"
2590 * the given value was sampled.
2591 *
2592 * If it has then report the latest error set, otherwise return 0.
2593 */
2594static inline int filemap_check_wb_err(struct address_space *mapping,
2595 errseq_t since)
2596{
2597 return errseq_check(&mapping->wb_err, since);
2598}
2599
2600/**
2601 * filemap_sample_wb_err - sample the current errseq_t to test for later errors
2602 * @mapping: mapping to be sampled
2603 *
2604 * Writeback errors are always reported relative to a particular sample point
2605 * in the past. This function provides those sample points.
2606 */
2607static inline errseq_t filemap_sample_wb_err(struct address_space *mapping)
2608{
2609 return errseq_sample(&mapping->wb_err);
2610}
2611
2553extern int vfs_fsync_range(struct file *file, loff_t start, loff_t end, 2612extern int vfs_fsync_range(struct file *file, loff_t start, loff_t end,
2554 int datasync); 2613 int datasync);
2555extern int vfs_fsync(struct file *file, int datasync); 2614extern int vfs_fsync(struct file *file, int datasync);
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index e7bbd9d4dc6c..baa9344dcd10 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -28,14 +28,33 @@ enum mapping_flags {
28 AS_NO_WRITEBACK_TAGS = 5, 28 AS_NO_WRITEBACK_TAGS = 5,
29}; 29};
30 30
31/**
32 * mapping_set_error - record a writeback error in the address_space
33 * @mapping - the mapping in which an error should be set
34 * @error - the error to set in the mapping
35 *
36 * When writeback fails in some way, we must record that error so that
37 * userspace can be informed when fsync and the like are called. We endeavor
38 * to report errors on any file that was open at the time of the error. Some
39 * internal callers also need to know when writeback errors have occurred.
40 *
41 * When a writeback error occurs, most filesystems will want to call
42 * mapping_set_error to record the error in the mapping so that it can be
43 * reported when the application calls fsync(2).
44 */
31static inline void mapping_set_error(struct address_space *mapping, int error) 45static inline void mapping_set_error(struct address_space *mapping, int error)
32{ 46{
33 if (unlikely(error)) { 47 if (likely(!error))
34 if (error == -ENOSPC) 48 return;
35 set_bit(AS_ENOSPC, &mapping->flags); 49
36 else 50 /* Record in wb_err for checkers using errseq_t based tracking */
37 set_bit(AS_EIO, &mapping->flags); 51 filemap_set_wb_err(mapping, error);
38 } 52
53 /* Record it in flags for now, for legacy callers */
54 if (error == -ENOSPC)
55 set_bit(AS_ENOSPC, &mapping->flags);
56 else
57 set_bit(AS_EIO, &mapping->flags);
39} 58}
40 59
41static inline void mapping_set_unevictable(struct address_space *mapping) 60static inline void mapping_set_unevictable(struct address_space *mapping)
diff --git a/include/trace/events/filemap.h b/include/trace/events/filemap.h
index 42febb6bc1d5..ff91325b8123 100644
--- a/include/trace/events/filemap.h
+++ b/include/trace/events/filemap.h
@@ -10,6 +10,7 @@
10#include <linux/memcontrol.h> 10#include <linux/memcontrol.h>
11#include <linux/device.h> 11#include <linux/device.h>
12#include <linux/kdev_t.h> 12#include <linux/kdev_t.h>
13#include <linux/errseq.h>
13 14
14DECLARE_EVENT_CLASS(mm_filemap_op_page_cache, 15DECLARE_EVENT_CLASS(mm_filemap_op_page_cache,
15 16
@@ -52,6 +53,62 @@ DEFINE_EVENT(mm_filemap_op_page_cache, mm_filemap_add_to_page_cache,
52 TP_ARGS(page) 53 TP_ARGS(page)
53 ); 54 );
54 55
56TRACE_EVENT(filemap_set_wb_err,
57 TP_PROTO(struct address_space *mapping, errseq_t eseq),
58
59 TP_ARGS(mapping, eseq),
60
61 TP_STRUCT__entry(
62 __field(unsigned long, i_ino)
63 __field(dev_t, s_dev)
64 __field(errseq_t, errseq)
65 ),
66
67 TP_fast_assign(
68 __entry->i_ino = mapping->host->i_ino;
69 __entry->errseq = eseq;
70 if (mapping->host->i_sb)
71 __entry->s_dev = mapping->host->i_sb->s_dev;
72 else
73 __entry->s_dev = mapping->host->i_rdev;
74 ),
75
76 TP_printk("dev=%d:%d ino=0x%lx errseq=0x%x",
77 MAJOR(__entry->s_dev), MINOR(__entry->s_dev),
78 __entry->i_ino, __entry->errseq)
79);
80
81TRACE_EVENT(file_check_and_advance_wb_err,
82 TP_PROTO(struct file *file, errseq_t old),
83
84 TP_ARGS(file, old),
85
86 TP_STRUCT__entry(
87 __field(struct file *, file);
88 __field(unsigned long, i_ino)
89 __field(dev_t, s_dev)
90 __field(errseq_t, old)
91 __field(errseq_t, new)
92 ),
93
94 TP_fast_assign(
95 __entry->file = file;
96 __entry->i_ino = file->f_mapping->host->i_ino;
97 if (file->f_mapping->host->i_sb)
98 __entry->s_dev =
99 file->f_mapping->host->i_sb->s_dev;
100 else
101 __entry->s_dev =
102 file->f_mapping->host->i_rdev;
103 __entry->old = old;
104 __entry->new = file->f_wb_err;
105 ),
106
107 TP_printk("file=%p dev=%d:%d ino=0x%lx old=0x%x new=0x%x",
108 __entry->file, MAJOR(__entry->s_dev),
109 MINOR(__entry->s_dev), __entry->i_ino, __entry->old,
110 __entry->new)
111);
55#endif /* _TRACE_FILEMAP_H */ 112#endif /* _TRACE_FILEMAP_H */
56 113
57/* This part must be outside protection */ 114/* This part must be outside protection */