ocfs2: Push out dropping of dentry lock to ocfs2_wq

Dropping of last reference to dentry lock is a complicated operation involving
dropping of reference to inode. This can get complicated and quota code in
particular needs to obtain some quota locks which leads to potential deadlock.
Thus we defer dropping of inode reference to ocfs2_wq.

Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
This commit is contained in:
Jan Kara 2009-01-12 23:20:31 +01:00 committed by Mark Fasheh
parent 27421e211a
commit ea455f8ab6
4 changed files with 56 additions and 4 deletions

View File

@ -38,6 +38,7 @@
#include "dlmglue.h"
#include "file.h"
#include "inode.h"
#include "super.h"
static int ocfs2_dentry_revalidate(struct dentry *dentry,
@ -294,6 +295,34 @@ out_attach:
return ret;
}
static 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)
{
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--) {
dl = osb->dentry_lock_list;
osb->dentry_lock_list = dl->dl_next;
spin_unlock(&dentry_list_lock);
iput(dl->dl_inode);
kfree(dl);
spin_lock(&dentry_list_lock);
}
if (osb->dentry_lock_list)
queue_work(ocfs2_wq, &osb->dentry_lock_work);
spin_unlock(&dentry_list_lock);
}
/*
* ocfs2_dentry_iput() and friends.
*
@ -318,16 +347,23 @@ out_attach:
static void ocfs2_drop_dentry_lock(struct ocfs2_super *osb,
struct ocfs2_dentry_lock *dl)
{
iput(dl->dl_inode);
ocfs2_simple_drop_lockres(osb, &dl->dl_lockres);
ocfs2_lock_res_free(&dl->dl_lockres);
kfree(dl);
/* 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)
queue_work(ocfs2_wq, &osb->dentry_lock_work);
dl->dl_next = osb->dentry_lock_list;
osb->dentry_lock_list = dl;
spin_unlock(&dentry_list_lock);
}
void ocfs2_dentry_lock_put(struct ocfs2_super *osb,
struct ocfs2_dentry_lock *dl)
{
int unlock = 0;
int unlock;
BUG_ON(dl->dl_count == 0);

View File

@ -29,8 +29,13 @@
extern struct dentry_operations ocfs2_dentry_ops;
struct ocfs2_dentry_lock {
/* Use count of dentry lock */
unsigned int dl_count;
u64 dl_parent_blkno;
union {
/* Linked list of dentry locks to release */
struct ocfs2_dentry_lock *dl_next;
u64 dl_parent_blkno;
};
/*
* The ocfs2_dentry_lock keeps an inode reference until
@ -47,6 +52,8 @@ int ocfs2_dentry_attach_lock(struct dentry *dentry, struct inode *inode,
void ocfs2_dentry_lock_put(struct ocfs2_super *osb,
struct ocfs2_dentry_lock *dl);
void ocfs2_drop_dl_inodes(struct work_struct *work);
struct dentry *ocfs2_find_local_alias(struct inode *inode, u64 parent_blkno,
int skip_unhashed);

View File

@ -210,6 +210,7 @@ struct ocfs2_journal;
struct ocfs2_slot_info;
struct ocfs2_recovery_map;
struct ocfs2_quota_recovery;
struct ocfs2_dentry_lock;
struct ocfs2_super
{
struct task_struct *commit_task;
@ -325,6 +326,11 @@ struct ocfs2_super
struct list_head blocked_lock_list;
unsigned long blocked_lock_count;
/* List of dentry locks to release. Anyone can add locks to
* the list, ocfs2_wq processes the list */
struct ocfs2_dentry_lock *dentry_lock_list;
struct work_struct dentry_lock_work;
wait_queue_head_t osb_mount_event;
/* Truncate log info */

View File

@ -1887,6 +1887,9 @@ static int ocfs2_initialize_super(struct super_block *sb,
INIT_WORK(&journal->j_recovery_work, ocfs2_complete_recovery);
journal->j_state = OCFS2_JOURNAL_FREE;
INIT_WORK(&osb->dentry_lock_work, ocfs2_drop_dl_inodes);
osb->dentry_lock_list = NULL;
/* get some pseudo constants for clustersize bits */
osb->s_clustersize_bits =
le32_to_cpu(di->id2.i_super.s_clustersize_bits);