Skip to content

Commit

Permalink
ext4: remove i_mutex lock in ext4_evict_inode to fix lockdep complaining
Browse files Browse the repository at this point in the history
The i_mutex lock and flush_completed_IO() added by commit 2581fdc
in ext4_evict_inode() causes lockdep complaining about potential
deadlock in several places.  In most/all of these LOCKDEP complaints
it looks like it's a false positive, since many of the potential
circular locking cases can't take place by the time the
ext4_evict_inode() is called; but since at the very least it may mask
real problems, we need to address this.

This change removes the flush_completed_IO() and i_mutex lock in
ext4_evict_inode().  Instead, we take a different approach to resolve
the software lockup that commit 2581fdc intends to fix.  Rather
than having ext4-dio-unwritten thread wait for grabing the i_mutex
lock of an inode, we use mutex_trylock() instead, and simply requeue
the work item if we fail to grab the inode's i_mutex lock.

This should speed up work queue processing in general and also
prevents the following deadlock scenario: 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.

Signed-off-by: Jiaying Zhang <[email protected]>
Signed-off-by: "Theodore Ts'o" <[email protected]>
  • Loading branch information
jiayingz authored and tytso committed Aug 31, 2011
1 parent fcb8ce5 commit 8c0bec2
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 4 deletions.
1 change: 1 addition & 0 deletions fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ struct mpage_da_data {
*/
#define EXT4_IO_END_UNWRITTEN 0x0001
#define EXT4_IO_END_ERROR 0x0002
#define EXT4_IO_END_QUEUED 0x0004

struct ext4_io_page {
struct page *p_page;
Expand Down
3 changes: 0 additions & 3 deletions fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,6 @@ void ext4_evict_inode(struct inode *inode)

trace_ext4_evict_inode(inode);

mutex_lock(&inode->i_mutex);
ext4_flush_completed_IO(inode);
mutex_unlock(&inode->i_mutex);
ext4_ioend_wait(inode);

if (inode->i_nlink) {
Expand Down
18 changes: 17 additions & 1 deletion fs/ext4/page-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,23 @@ static void ext4_end_io_work(struct work_struct *work)
unsigned long flags;
int ret;

mutex_lock(&inode->i_mutex);
if (!mutex_trylock(&inode->i_mutex)) {
/*
* Requeue the work instead of waiting so that the work
* items queued after this can be processed.
*/
queue_work(EXT4_SB(inode->i_sb)->dio_unwritten_wq, &io->work);
/*
* To prevent the ext4-dio-unwritten thread from keeping
* requeueing end_io requests and occupying cpu for too long,
* yield the cpu if it sees an end_io request that has already
* been requeued.
*/
if (io->flag & EXT4_IO_END_QUEUED)
yield();
io->flag |= EXT4_IO_END_QUEUED;
return;
}
ret = ext4_end_io_nolock(io);
if (ret < 0) {
mutex_unlock(&inode->i_mutex);
Expand Down

0 comments on commit 8c0bec2

Please sign in to comment.