gfs2: gfs2_evict_inode: Put glocks asynchronously
gfs2_evict_inode is called to free inodes under memory pressure. The function calls into DLM when an inode's last cluster-wide reference goes away (remote unlink) and to release the glock and associated DLM lock before finally destroying the inode. However, if DLM is blocked on memory to become available, calling into DLM again will deadlock. Avoid that by decoupling releasing glocks from destroying inodes in that case: with gfs2_glock_queue_put, glocks will be dequeued asynchronously in work queue context, when the associated inodes have likely already been destroyed. With this change, inodes can end up being unlinked, remote-unlink can be triggered, and then the inode can be reallocated before all remote-unlink callbacks are processed. To detect that, revalidate the link count in gfs2_evict_inode to make sure we're not deleting an allocated, referenced inode. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Signed-off-by: Bob Peterson <rpeterso@redhat.com>
This commit is contained in:

committed by
Bob Peterson

parent
eebd2e813f
commit
71c1b21368
@@ -1501,6 +1501,22 @@ out_qs:
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_glock_put_eventually
|
||||
* @gl: The glock to put
|
||||
*
|
||||
* When under memory pressure, trigger a deferred glock put to make sure we
|
||||
* won't call into DLM and deadlock. Otherwise, put the glock directly.
|
||||
*/
|
||||
|
||||
static void gfs2_glock_put_eventually(struct gfs2_glock *gl)
|
||||
{
|
||||
if (current->flags & PF_MEMALLOC)
|
||||
gfs2_glock_queue_put(gl);
|
||||
else
|
||||
gfs2_glock_put(gl);
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_evict_inode - Remove an inode from cache
|
||||
* @inode: The inode to evict
|
||||
@@ -1564,6 +1580,12 @@ static void gfs2_evict_inode(struct inode *inode)
|
||||
goto out_truncate;
|
||||
}
|
||||
|
||||
/*
|
||||
* The inode may have been recreated in the meantime.
|
||||
*/
|
||||
if (inode->i_nlink)
|
||||
goto out_truncate;
|
||||
|
||||
alloc_failed:
|
||||
if (gfs2_holder_initialized(&ip->i_iopen_gh) &&
|
||||
test_bit(HIF_HOLDER, &ip->i_iopen_gh.gh_iflags)) {
|
||||
@@ -1653,12 +1675,16 @@ out:
|
||||
glock_clear_object(ip->i_gl, ip);
|
||||
wait_on_bit_io(&ip->i_flags, GIF_GLOP_PENDING, TASK_UNINTERRUPTIBLE);
|
||||
gfs2_glock_add_to_lru(ip->i_gl);
|
||||
gfs2_glock_put(ip->i_gl);
|
||||
gfs2_glock_put_eventually(ip->i_gl);
|
||||
ip->i_gl = NULL;
|
||||
if (gfs2_holder_initialized(&ip->i_iopen_gh)) {
|
||||
glock_clear_object(ip->i_iopen_gh.gh_gl, ip);
|
||||
struct gfs2_glock *gl = ip->i_iopen_gh.gh_gl;
|
||||
|
||||
glock_clear_object(gl, ip);
|
||||
ip->i_iopen_gh.gh_flags |= GL_NOCACHE;
|
||||
gfs2_glock_hold(gl);
|
||||
gfs2_glock_dq_uninit(&ip->i_iopen_gh);
|
||||
gfs2_glock_put_eventually(gl);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user