Skip to content

Commit

Permalink
ocfs2: Push out dropping of dentry lock to ocfs2_wq
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
jankara authored and Mark Fasheh committed Feb 2, 2009
1 parent 27421e2 commit ea455f8
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 4 deletions.
42 changes: 39 additions & 3 deletions fs/ocfs2/dcache.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "dlmglue.h"
#include "file.h"
#include "inode.h"
#include "super.h"


static int ocfs2_dentry_revalidate(struct dentry *dentry,
Expand Down Expand Up @@ -294,6 +295,34 @@ int ocfs2_dentry_attach_lock(struct dentry *dentry,
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.
*
Expand All @@ -318,16 +347,23 @@ int ocfs2_dentry_attach_lock(struct dentry *dentry,
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);

Expand Down
9 changes: 8 additions & 1 deletion fs/ocfs2/dcache.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);

Expand Down
6 changes: 6 additions & 0 deletions fs/ocfs2/ocfs2.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down
3 changes: 3 additions & 0 deletions fs/ocfs2/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit ea455f8

Please sign in to comment.