writeback: sync livelock - throttle mapping dirties if it's being synced The AS_SYNC_WAITER flag will be set to indicate an active sync waiter. It's not perfect with respect to file ranges and concurrent syncs. And do not guarantee that the application writes on this mapping can be throttled enough to avoid livelock, especially when there are many background writebacks. Just my 2 cents. CC: Jan Kara CC: Peter Staubach CC: Myklebust Trond Signed-off-by: Wu Fengguang --- include/linux/pagemap.h | 3 +++ mm/filemap.c | 2 ++ mm/page-writeback.c | 5 +++++ 3 files changed, 10 insertions(+) --- linux.orig/mm/page-writeback.c 2009-10-08 08:11:19.000000000 +0800 +++ linux/mm/page-writeback.c 2009-10-08 08:48:13.000000000 +0800 @@ -480,7 +480,12 @@ static void balance_dirty_pages(struct a /* * If sync() is in progress, curb the to-be-synced inodes regardless * of dirty limits, so that a fast dirtier won't livelock the sync. + * In perticular, NFS syncs the mapping before many file operations. */ + if (unlikely(test_bit(AS_SYNC_WAITER, &mapping->flags))) { + write_chunk *= 8; + goto throttle; + } if (unlikely(bdi->sync_time && S_ISREG(mapping->host->i_mode) && time_after_eq(bdi->sync_time, --- linux.orig/include/linux/pagemap.h 2009-10-08 08:11:19.000000000 +0800 +++ linux/include/linux/pagemap.h 2009-10-08 08:11:20.000000000 +0800 @@ -23,6 +23,9 @@ enum mapping_flags { AS_ENOSPC = __GFP_BITS_SHIFT + 1, /* ENOSPC on async write */ AS_MM_ALL_LOCKS = __GFP_BITS_SHIFT + 2, /* under mm_take_all_locks() */ AS_UNEVICTABLE = __GFP_BITS_SHIFT + 3, /* e.g., ramdisk, SHM_LOCK */ + AS_SYNC_WAITER = __GFP_BITS_SHIFT + 4, /* sync&wait under way, page + * dirtying will be throttled + */ }; static inline void mapping_set_error(struct address_space *mapping, int error) --- linux.orig/mm/filemap.c 2009-10-08 08:11:19.000000000 +0800 +++ linux/mm/filemap.c 2009-10-08 08:11:20.000000000 +0800 @@ -356,6 +356,7 @@ int filemap_write_and_wait(struct addres int err = 0; if (mapping->nrpages) { + set_bit(AS_SYNC_WAITER, &mapping->flags); err = filemap_fdatawrite(mapping); /* * Even if the above returned error, the pages may be @@ -368,6 +369,7 @@ int filemap_write_and_wait(struct addres if (!err) err = err2; } + clear_bit(AS_SYNC_WAITER, &mapping->flags); } return err; }