Commit e97fedb9 authored by Dave Chinner's avatar Dave Chinner Committed by Josef Bacik

sync: serialise per-superblock sync operations

When competing sync(2) calls walk the same filesystem, they need to
walk the list of inodes on the superblock to find all the inodes
that we need to wait for IO completion on. However, when multiple
wait_sb_inodes() calls do this at the same time, they contend on the
the inode_sb_list_lock and the contention causes system wide
slowdowns. In effect, concurrent sync(2) calls can take longer and
burn more CPU than if they were serialised.

Stop the worst of the contention by adding a per-sb mutex to wrap
around wait_sb_inodes() so that we only execute one sync(2) IO
completion walk per superblock superblock at a time and hence avoid
contention being triggered by concurrent sync(2) calls.
Signed-off-by: default avatarDave Chinner <dchinner@redhat.com>
Signed-off-by: default avatarJosef Bacik <jbacik@fb.com>
Reviewed-by: default avatarJan Kara <jack@suse.cz>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Tested-by: default avatarDave Chinner <dchinner@redhat.com>
parent 74278da9
...@@ -2114,6 +2114,15 @@ void __mark_inode_dirty(struct inode *inode, int flags) ...@@ -2114,6 +2114,15 @@ void __mark_inode_dirty(struct inode *inode, int flags)
} }
EXPORT_SYMBOL(__mark_inode_dirty); EXPORT_SYMBOL(__mark_inode_dirty);
/*
* The @s_sync_lock is used to serialise concurrent sync operations
* to avoid lock contention problems with concurrent wait_sb_inodes() calls.
* Concurrent callers will block on the s_sync_lock rather than doing contending
* walks. The queueing maintains sync(2) required behaviour as all the IO that
* has been issued up to the time this function is enter is guaranteed to be
* completed by the time we have gained the lock and waited for all IO that is
* in progress regardless of the order callers are granted the lock.
*/
static void wait_sb_inodes(struct super_block *sb) static void wait_sb_inodes(struct super_block *sb)
{ {
struct inode *inode, *old_inode = NULL; struct inode *inode, *old_inode = NULL;
...@@ -2124,6 +2133,7 @@ static void wait_sb_inodes(struct super_block *sb) ...@@ -2124,6 +2133,7 @@ static void wait_sb_inodes(struct super_block *sb)
*/ */
WARN_ON(!rwsem_is_locked(&sb->s_umount)); WARN_ON(!rwsem_is_locked(&sb->s_umount));
mutex_lock(&sb->s_sync_lock);
spin_lock(&sb->s_inode_list_lock); spin_lock(&sb->s_inode_list_lock);
/* /*
...@@ -2165,6 +2175,7 @@ static void wait_sb_inodes(struct super_block *sb) ...@@ -2165,6 +2175,7 @@ static void wait_sb_inodes(struct super_block *sb)
} }
spin_unlock(&sb->s_inode_list_lock); spin_unlock(&sb->s_inode_list_lock);
iput(old_inode); iput(old_inode);
mutex_unlock(&sb->s_sync_lock);
} }
static void __writeback_inodes_sb_nr(struct super_block *sb, unsigned long nr, static void __writeback_inodes_sb_nr(struct super_block *sb, unsigned long nr,
......
...@@ -190,6 +190,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags) ...@@ -190,6 +190,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags)
s->s_flags = flags; s->s_flags = flags;
INIT_HLIST_NODE(&s->s_instances); INIT_HLIST_NODE(&s->s_instances);
INIT_HLIST_BL_HEAD(&s->s_anon); INIT_HLIST_BL_HEAD(&s->s_anon);
mutex_init(&s->s_sync_lock);
INIT_LIST_HEAD(&s->s_inodes); INIT_LIST_HEAD(&s->s_inodes);
spin_lock_init(&s->s_inode_list_lock); spin_lock_init(&s->s_inode_list_lock);
......
...@@ -1375,6 +1375,8 @@ struct super_block { ...@@ -1375,6 +1375,8 @@ struct super_block {
struct list_lru s_inode_lru ____cacheline_aligned_in_smp; struct list_lru s_inode_lru ____cacheline_aligned_in_smp;
struct rcu_head rcu; struct rcu_head rcu;
struct mutex s_sync_lock; /* sync serialisation lock */
/* /*
* Indicates how deep in a filesystem stack this SB is * Indicates how deep in a filesystem stack this SB is
*/ */
......
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