Skip to content

Commit 8c0bec2

Browse files
jiayingztytso
authored andcommitted
ext4: remove i_mutex lock in ext4_evict_inode to fix lockdep complaining
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]>
1 parent fcb8ce5 commit 8c0bec2

File tree

3 files changed

+18
-4
lines changed

3 files changed

+18
-4
lines changed

fs/ext4/ext4.h

+1
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ struct mpage_da_data {
175175
*/
176176
#define EXT4_IO_END_UNWRITTEN 0x0001
177177
#define EXT4_IO_END_ERROR 0x0002
178+
#define EXT4_IO_END_QUEUED 0x0004
178179

179180
struct ext4_io_page {
180181
struct page *p_page;

fs/ext4/inode.c

-3
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,6 @@ void ext4_evict_inode(struct inode *inode)
121121

122122
trace_ext4_evict_inode(inode);
123123

124-
mutex_lock(&inode->i_mutex);
125-
ext4_flush_completed_IO(inode);
126-
mutex_unlock(&inode->i_mutex);
127124
ext4_ioend_wait(inode);
128125

129126
if (inode->i_nlink) {

fs/ext4/page-io.c

+17-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,23 @@ static void ext4_end_io_work(struct work_struct *work)
142142
unsigned long flags;
143143
int ret;
144144

145-
mutex_lock(&inode->i_mutex);
145+
if (!mutex_trylock(&inode->i_mutex)) {
146+
/*
147+
* Requeue the work instead of waiting so that the work
148+
* items queued after this can be processed.
149+
*/
150+
queue_work(EXT4_SB(inode->i_sb)->dio_unwritten_wq, &io->work);
151+
/*
152+
* To prevent the ext4-dio-unwritten thread from keeping
153+
* requeueing end_io requests and occupying cpu for too long,
154+
* yield the cpu if it sees an end_io request that has already
155+
* been requeued.
156+
*/
157+
if (io->flag & EXT4_IO_END_QUEUED)
158+
yield();
159+
io->flag |= EXT4_IO_END_QUEUED;
160+
return;
161+
}
146162
ret = ext4_end_io_nolock(io);
147163
if (ret < 0) {
148164
mutex_unlock(&inode->i_mutex);

0 commit comments

Comments
 (0)