ocfs2: Fix deadlock on umount

In commit ea455f8ab6, we moved the dentry lock
put process into ocfs2_wq. This causes problems during umount because ocfs2_wq
can drop references to inodes while they are being invalidated by
invalidate_inodes() causing all sorts of nasty things (invalidate_inodes()
ending in an infinite loop, "Busy inodes after umount" messages etc.).

We fix the problem by stopping ocfs2_wq from doing any further releasing of
inode references on the superblock being unmounted, wait until it finishes
the current round of releasing and finally cleaning up all the references in
dentry_lock_list from ocfs2_put_super().

The issue was tracked down by Tao Ma <tao.ma@oracle.com>.

Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Joel Becker <joel.becker@oracle.com>
This commit is contained in:
Jan Kara
2009-07-20 12:12:36 +02:00
committed by Joel Becker
parent 3c5e10683e
commit f7b1aa69be
4 changed files with 70 additions and 15 deletions

View File

@@ -310,22 +310,19 @@ out_attach:
return ret;
}
static DEFINE_SPINLOCK(dentry_list_lock);
DEFINE_SPINLOCK(dentry_list_lock);
/* We limit the number of dentry locks to drop in one go. We have
* this limit so that we don't starve other users of ocfs2_wq. */
#define DL_INODE_DROP_COUNT 64
/* Drop inode references from dentry locks */
void ocfs2_drop_dl_inodes(struct work_struct *work)
static void __ocfs2_drop_dl_inodes(struct ocfs2_super *osb, int drop_count)
{
struct ocfs2_super *osb = container_of(work, struct ocfs2_super,
dentry_lock_work);
struct ocfs2_dentry_lock *dl;
int drop_count = DL_INODE_DROP_COUNT;
spin_lock(&dentry_list_lock);
while (osb->dentry_lock_list && drop_count--) {
while (osb->dentry_lock_list && (drop_count < 0 || drop_count--)) {
dl = osb->dentry_lock_list;
osb->dentry_lock_list = dl->dl_next;
spin_unlock(&dentry_list_lock);
@@ -333,11 +330,32 @@ void ocfs2_drop_dl_inodes(struct work_struct *work)
kfree(dl);
spin_lock(&dentry_list_lock);
}
if (osb->dentry_lock_list)
spin_unlock(&dentry_list_lock);
}
void ocfs2_drop_dl_inodes(struct work_struct *work)
{
struct ocfs2_super *osb = container_of(work, struct ocfs2_super,
dentry_lock_work);
__ocfs2_drop_dl_inodes(osb, DL_INODE_DROP_COUNT);
/*
* Don't queue dropping if umount is in progress. We flush the
* list in ocfs2_dismount_volume
*/
spin_lock(&dentry_list_lock);
if (osb->dentry_lock_list &&
!ocfs2_test_osb_flag(osb, OCFS2_OSB_DROP_DENTRY_LOCK_IMMED))
queue_work(ocfs2_wq, &osb->dentry_lock_work);
spin_unlock(&dentry_list_lock);
}
/* Flush the whole work queue */
void ocfs2_drop_all_dl_inodes(struct ocfs2_super *osb)
{
__ocfs2_drop_dl_inodes(osb, -1);
}
/*
* ocfs2_dentry_iput() and friends.
*
@@ -368,7 +386,8 @@ static void ocfs2_drop_dentry_lock(struct ocfs2_super *osb,
/* We leave dropping of inode reference to ocfs2_wq as that can
* possibly lead to inode deletion which gets tricky */
spin_lock(&dentry_list_lock);
if (!osb->dentry_lock_list)
if (!osb->dentry_lock_list &&
!ocfs2_test_osb_flag(osb, OCFS2_OSB_DROP_DENTRY_LOCK_IMMED))
queue_work(ocfs2_wq, &osb->dentry_lock_work);
dl->dl_next = osb->dentry_lock_list;
osb->dentry_lock_list = dl;