Commit 0dc83bd3 authored by Jan Kara's avatar Jan Kara

Revert "writeback: do not sync data dirtied after sync start"

This reverts commit c4a391b5. Dave
Chinner <david@fromorbit.com> has reported the commit may cause some
inodes to be left out from sync(2). This is because we can call
redirty_tail() for some inode (which sets i_dirtied_when to current time)
after sync(2) has started or similarly requeue_inode() can set
i_dirtied_when to current time if writeback had to skip some pages. The
real problem is in the functions clobbering i_dirtied_when but fixing
that isn't trivial so revert is a safer choice for now.

CC: stable@vger.kernel.org # >= 3.13
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent 1362f4ea
...@@ -40,18 +40,13 @@ ...@@ -40,18 +40,13 @@
struct wb_writeback_work { struct wb_writeback_work {
long nr_pages; long nr_pages;
struct super_block *sb; struct super_block *sb;
/* unsigned long *older_than_this;
* Write only inodes dirtied before this time. Don't forget to set
* older_than_this_is_set when you set this.
*/
unsigned long older_than_this;
enum writeback_sync_modes sync_mode; enum writeback_sync_modes sync_mode;
unsigned int tagged_writepages:1; unsigned int tagged_writepages:1;
unsigned int for_kupdate:1; unsigned int for_kupdate:1;
unsigned int range_cyclic:1; unsigned int range_cyclic:1;
unsigned int for_background:1; unsigned int for_background:1;
unsigned int for_sync:1; /* sync(2) WB_SYNC_ALL writeback */ unsigned int for_sync:1; /* sync(2) WB_SYNC_ALL writeback */
unsigned int older_than_this_is_set:1;
enum wb_reason reason; /* why was writeback initiated? */ enum wb_reason reason; /* why was writeback initiated? */
struct list_head list; /* pending work list */ struct list_head list; /* pending work list */
...@@ -252,10 +247,10 @@ static int move_expired_inodes(struct list_head *delaying_queue, ...@@ -252,10 +247,10 @@ static int move_expired_inodes(struct list_head *delaying_queue,
int do_sb_sort = 0; int do_sb_sort = 0;
int moved = 0; int moved = 0;
WARN_ON_ONCE(!work->older_than_this_is_set);
while (!list_empty(delaying_queue)) { while (!list_empty(delaying_queue)) {
inode = wb_inode(delaying_queue->prev); inode = wb_inode(delaying_queue->prev);
if (inode_dirtied_after(inode, work->older_than_this)) if (work->older_than_this &&
inode_dirtied_after(inode, *work->older_than_this))
break; break;
list_move(&inode->i_wb_list, &tmp); list_move(&inode->i_wb_list, &tmp);
moved++; moved++;
...@@ -742,8 +737,6 @@ static long writeback_inodes_wb(struct bdi_writeback *wb, long nr_pages, ...@@ -742,8 +737,6 @@ static long writeback_inodes_wb(struct bdi_writeback *wb, long nr_pages,
.sync_mode = WB_SYNC_NONE, .sync_mode = WB_SYNC_NONE,
.range_cyclic = 1, .range_cyclic = 1,
.reason = reason, .reason = reason,
.older_than_this = jiffies,
.older_than_this_is_set = 1,
}; };
spin_lock(&wb->list_lock); spin_lock(&wb->list_lock);
...@@ -802,13 +795,12 @@ static long wb_writeback(struct bdi_writeback *wb, ...@@ -802,13 +795,12 @@ static long wb_writeback(struct bdi_writeback *wb,
{ {
unsigned long wb_start = jiffies; unsigned long wb_start = jiffies;
long nr_pages = work->nr_pages; long nr_pages = work->nr_pages;
unsigned long oldest_jif;
struct inode *inode; struct inode *inode;
long progress; long progress;
if (!work->older_than_this_is_set) { oldest_jif = jiffies;
work->older_than_this = jiffies; work->older_than_this = &oldest_jif;
work->older_than_this_is_set = 1;
}
spin_lock(&wb->list_lock); spin_lock(&wb->list_lock);
for (;;) { for (;;) {
...@@ -842,10 +834,10 @@ static long wb_writeback(struct bdi_writeback *wb, ...@@ -842,10 +834,10 @@ static long wb_writeback(struct bdi_writeback *wb,
* safe. * safe.
*/ */
if (work->for_kupdate) { if (work->for_kupdate) {
work->older_than_this = jiffies - oldest_jif = jiffies -
msecs_to_jiffies(dirty_expire_interval * 10); msecs_to_jiffies(dirty_expire_interval * 10);
} else if (work->for_background) } else if (work->for_background)
work->older_than_this = jiffies; oldest_jif = jiffies;
trace_writeback_start(wb->bdi, work); trace_writeback_start(wb->bdi, work);
if (list_empty(&wb->b_io)) if (list_empty(&wb->b_io))
...@@ -1357,21 +1349,18 @@ EXPORT_SYMBOL(try_to_writeback_inodes_sb); ...@@ -1357,21 +1349,18 @@ EXPORT_SYMBOL(try_to_writeback_inodes_sb);
/** /**
* sync_inodes_sb - sync sb inode pages * sync_inodes_sb - sync sb inode pages
* @sb: the superblock * @sb: the superblock
* @older_than_this: timestamp
* *
* This function writes and waits on any dirty inode belonging to this * This function writes and waits on any dirty inode belonging to this
* superblock that has been dirtied before given timestamp. * super_block.
*/ */
void sync_inodes_sb(struct super_block *sb, unsigned long older_than_this) void sync_inodes_sb(struct super_block *sb)
{ {
DECLARE_COMPLETION_ONSTACK(done); DECLARE_COMPLETION_ONSTACK(done);
struct wb_writeback_work work = { struct wb_writeback_work work = {
.sb = sb, .sb = sb,
.sync_mode = WB_SYNC_ALL, .sync_mode = WB_SYNC_ALL,
.nr_pages = LONG_MAX, .nr_pages = LONG_MAX,
.older_than_this = older_than_this,
.older_than_this_is_set = 1,
.range_cyclic = 0, .range_cyclic = 0,
.done = &done, .done = &done,
.reason = WB_REASON_SYNC, .reason = WB_REASON_SYNC,
......
...@@ -27,11 +27,10 @@ ...@@ -27,11 +27,10 @@
* wait == 1 case since in that case write_inode() functions do * wait == 1 case since in that case write_inode() functions do
* sync_dirty_buffer() and thus effectively write one block at a time. * sync_dirty_buffer() and thus effectively write one block at a time.
*/ */
static int __sync_filesystem(struct super_block *sb, int wait, static int __sync_filesystem(struct super_block *sb, int wait)
unsigned long start)
{ {
if (wait) if (wait)
sync_inodes_sb(sb, start); sync_inodes_sb(sb);
else else
writeback_inodes_sb(sb, WB_REASON_SYNC); writeback_inodes_sb(sb, WB_REASON_SYNC);
...@@ -48,7 +47,6 @@ static int __sync_filesystem(struct super_block *sb, int wait, ...@@ -48,7 +47,6 @@ static int __sync_filesystem(struct super_block *sb, int wait,
int sync_filesystem(struct super_block *sb) int sync_filesystem(struct super_block *sb)
{ {
int ret; int ret;
unsigned long start = jiffies;
/* /*
* We need to be protected against the filesystem going from * We need to be protected against the filesystem going from
...@@ -62,17 +60,17 @@ int sync_filesystem(struct super_block *sb) ...@@ -62,17 +60,17 @@ int sync_filesystem(struct super_block *sb)
if (sb->s_flags & MS_RDONLY) if (sb->s_flags & MS_RDONLY)
return 0; return 0;
ret = __sync_filesystem(sb, 0, start); ret = __sync_filesystem(sb, 0);
if (ret < 0) if (ret < 0)
return ret; return ret;
return __sync_filesystem(sb, 1, start); return __sync_filesystem(sb, 1);
} }
EXPORT_SYMBOL_GPL(sync_filesystem); EXPORT_SYMBOL_GPL(sync_filesystem);
static void sync_inodes_one_sb(struct super_block *sb, void *arg) static void sync_inodes_one_sb(struct super_block *sb, void *arg)
{ {
if (!(sb->s_flags & MS_RDONLY)) if (!(sb->s_flags & MS_RDONLY))
sync_inodes_sb(sb, *((unsigned long *)arg)); sync_inodes_sb(sb);
} }
static void sync_fs_one_sb(struct super_block *sb, void *arg) static void sync_fs_one_sb(struct super_block *sb, void *arg)
...@@ -104,10 +102,9 @@ static void fdatawait_one_bdev(struct block_device *bdev, void *arg) ...@@ -104,10 +102,9 @@ static void fdatawait_one_bdev(struct block_device *bdev, void *arg)
SYSCALL_DEFINE0(sync) SYSCALL_DEFINE0(sync)
{ {
int nowait = 0, wait = 1; int nowait = 0, wait = 1;
unsigned long start = jiffies;
wakeup_flusher_threads(0, WB_REASON_SYNC); wakeup_flusher_threads(0, WB_REASON_SYNC);
iterate_supers(sync_inodes_one_sb, &start); iterate_supers(sync_inodes_one_sb, NULL);
iterate_supers(sync_fs_one_sb, &nowait); iterate_supers(sync_fs_one_sb, &nowait);
iterate_supers(sync_fs_one_sb, &wait); iterate_supers(sync_fs_one_sb, &wait);
iterate_bdevs(fdatawrite_one_bdev, NULL); iterate_bdevs(fdatawrite_one_bdev, NULL);
......
...@@ -913,7 +913,7 @@ xfs_flush_inodes( ...@@ -913,7 +913,7 @@ xfs_flush_inodes(
struct super_block *sb = mp->m_super; struct super_block *sb = mp->m_super;
if (down_read_trylock(&sb->s_umount)) { if (down_read_trylock(&sb->s_umount)) {
sync_inodes_sb(sb, jiffies); sync_inodes_sb(sb);
up_read(&sb->s_umount); up_read(&sb->s_umount);
} }
} }
......
...@@ -97,7 +97,7 @@ void writeback_inodes_sb_nr(struct super_block *, unsigned long nr, ...@@ -97,7 +97,7 @@ void writeback_inodes_sb_nr(struct super_block *, unsigned long nr,
int try_to_writeback_inodes_sb(struct super_block *, enum wb_reason reason); int try_to_writeback_inodes_sb(struct super_block *, enum wb_reason reason);
int try_to_writeback_inodes_sb_nr(struct super_block *, unsigned long nr, int try_to_writeback_inodes_sb_nr(struct super_block *, unsigned long nr,
enum wb_reason reason); enum wb_reason reason);
void sync_inodes_sb(struct super_block *sb, unsigned long older_than_this); void sync_inodes_sb(struct super_block *);
void wakeup_flusher_threads(long nr_pages, enum wb_reason reason); void wakeup_flusher_threads(long nr_pages, enum wb_reason reason);
void inode_wait_for_writeback(struct inode *inode); void inode_wait_for_writeback(struct inode *inode);
......
...@@ -287,11 +287,11 @@ TRACE_EVENT(writeback_queue_io, ...@@ -287,11 +287,11 @@ TRACE_EVENT(writeback_queue_io,
__field(int, reason) __field(int, reason)
), ),
TP_fast_assign( TP_fast_assign(
unsigned long older_than_this = work->older_than_this; unsigned long *older_than_this = work->older_than_this;
strncpy(__entry->name, dev_name(wb->bdi->dev), 32); strncpy(__entry->name, dev_name(wb->bdi->dev), 32);
__entry->older = older_than_this; __entry->older = older_than_this ? *older_than_this : 0;
__entry->age = older_than_this ? __entry->age = older_than_this ?
(jiffies - older_than_this) * 1000 / HZ : -1; (jiffies - *older_than_this) * 1000 / HZ : -1;
__entry->moved = moved; __entry->moved = moved;
__entry->reason = work->reason; __entry->reason = work->reason;
), ),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment