diff options
| author | Jiaying Zhang <jiayingz@google.com> | 2011-08-13 12:17:13 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-08-29 16:29:10 -0400 |
| commit | 2526f368949bccda6e8ed1bf74a4e955e3af42af (patch) | |
| tree | 2f449cf41e4ebd9c5282c30b1b8c23af1179e4b7 /fs/ext4 | |
| parent | 2fb522e963f57a6c0206f67a355b8131b16ef607 (diff) | |
ext4: call ext4_ioend_wait and ext4_flush_completed_IO in ext4_evict_inode
commit 2581fdc810889fdea97689cb62481201d579c796 upstream.
Flush inode's i_completed_io_list before calling ext4_io_wait to
prevent the following deadlock scenario: A page fault happens while
some process is writing inode A. During page fault,
shrink_icache_memory is called that in turn evicts another inode
B. Inode B has some pending io_end work so it calls ext4_ioend_wait()
that waits for inode B's i_ioend_count to become zero. However, inode
B's ioend work was queued behind some of inode A's ioend work on the
same cpu's ext4-dio-unwritten workqueue. As the ext4-dio-unwritten
thread on that cpu is processing inode A's ioend work, it tries to
grab inode A's i_mutex lock. Since the i_mutex lock of inode A is
still hold before the page fault happened, we enter a deadlock.
Also moves ext4_flush_completed_IO and ext4_ioend_wait from
ext4_destroy_inode() to ext4_evict_inode(). During inode deleteion,
ext4_evict_inode() is called before ext4_destroy_inode() and in
ext4_evict_inode(), we may call ext4_truncate() without holding
i_mutex lock. As a result, there is a race between flush_completed_IO
that is called from ext4_ext_truncate() and ext4_end_io_work, which
may cause corruption on an io_end structure. This change moves
ext4_flush_completed_IO and ext4_ioend_wait from ext4_destroy_inode()
to ext4_evict_inode() to resolve the race between ext4_truncate() and
ext4_end_io_work during inode deletion.
Signed-off-by: Jiaying Zhang <jiayingz@google.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs/ext4')
| -rw-r--r-- | fs/ext4/inode.c | 6 | ||||
| -rw-r--r-- | fs/ext4/super.c | 1 |
2 files changed, 6 insertions, 1 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 0b60a461e06..773de46d7f0 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
| @@ -189,6 +189,12 @@ void ext4_evict_inode(struct inode *inode) | |||
| 189 | int err; | 189 | int err; |
| 190 | 190 | ||
| 191 | trace_ext4_evict_inode(inode); | 191 | trace_ext4_evict_inode(inode); |
| 192 | |||
| 193 | mutex_lock(&inode->i_mutex); | ||
| 194 | ext4_flush_completed_IO(inode); | ||
| 195 | mutex_unlock(&inode->i_mutex); | ||
| 196 | ext4_ioend_wait(inode); | ||
| 197 | |||
| 192 | if (inode->i_nlink) { | 198 | if (inode->i_nlink) { |
| 193 | truncate_inode_pages(&inode->i_data, 0); | 199 | truncate_inode_pages(&inode->i_data, 0); |
| 194 | goto no_delete; | 200 | goto no_delete; |
diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 9ea71aa864b..111ed9d3c54 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c | |||
| @@ -892,7 +892,6 @@ static void ext4_i_callback(struct rcu_head *head) | |||
| 892 | 892 | ||
| 893 | static void ext4_destroy_inode(struct inode *inode) | 893 | static void ext4_destroy_inode(struct inode *inode) |
| 894 | { | 894 | { |
| 895 | ext4_ioend_wait(inode); | ||
| 896 | if (!list_empty(&(EXT4_I(inode)->i_orphan))) { | 895 | if (!list_empty(&(EXT4_I(inode)->i_orphan))) { |
| 897 | ext4_msg(inode->i_sb, KERN_ERR, | 896 | ext4_msg(inode->i_sb, KERN_ERR, |
| 898 | "Inode %lu (%p): orphan list check failed!", | 897 | "Inode %lu (%p): orphan list check failed!", |
