Skip to content

Commit

Permalink
dlm: fix ast ordering for user locks
Browse files Browse the repository at this point in the history
Commit 7fe2b31 fixed possible
misordering of completion asts (casts) and blocking asts (basts)
for kernel locks.  This patch does the same for locks taken by
user space applications.

Signed-off-by: David Teigland <teigland@redhat.com>
  • Loading branch information
David Teigland committed Apr 30, 2010
1 parent 99fb19d commit 89d799d
Showing 1 changed file with 74 additions and 14 deletions.
88 changes: 74 additions & 14 deletions fs/dlm/user.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ void dlm_user_add_ast(struct dlm_lkb *lkb, int type, int mode)
if (!ast_type) {
kref_get(&lkb->lkb_ref);
list_add_tail(&lkb->lkb_astqueue, &proc->asts);
lkb->lkb_ast_first = type;
wake_up_interruptible(&proc->wait);
}
if (type == AST_COMP && (ast_type & AST_COMP))
Expand All @@ -223,7 +224,6 @@ void dlm_user_add_ast(struct dlm_lkb *lkb, int type, int mode)

eol = lkb_is_endoflife(lkb, ua->lksb.sb_status, type);
if (eol) {
lkb->lkb_ast_type &= ~AST_BAST;
lkb->lkb_flags |= DLM_IFL_ENDOFLIFE;
}

Expand Down Expand Up @@ -706,7 +706,7 @@ static int device_close(struct inode *inode, struct file *file)
}

static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type,
int bmode, char __user *buf, size_t count)
int mode, char __user *buf, size_t count)
{
#ifdef CONFIG_COMPAT
struct dlm_lock_result32 result32;
Expand All @@ -733,7 +733,7 @@ static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type,
if (type == AST_BAST) {
result.user_astaddr = ua->bastaddr;
result.user_astparam = ua->bastparam;
result.bast_mode = bmode;
result.bast_mode = mode;
} else {
result.user_astaddr = ua->castaddr;
result.user_astparam = ua->castparam;
Expand Down Expand Up @@ -801,7 +801,9 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count,
struct dlm_user_proc *proc = file->private_data;
struct dlm_lkb *lkb;
DECLARE_WAITQUEUE(wait, current);
int error, type=0, bmode=0, removed = 0;
int error = 0, removed;
int ret_type, ret_mode;
int bastmode, castmode, do_bast, do_cast;

if (count == sizeof(struct dlm_device_version)) {
error = copy_version_to_user(buf, count);
Expand All @@ -820,6 +822,8 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count,
#endif
return -EINVAL;

try_another:

/* do we really need this? can a read happen after a close? */
if (test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))
return -EINVAL;
Expand Down Expand Up @@ -855,13 +859,55 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count,

lkb = list_entry(proc->asts.next, struct dlm_lkb, lkb_astqueue);

if (lkb->lkb_ast_type & AST_COMP) {
lkb->lkb_ast_type &= ~AST_COMP;
type = AST_COMP;
} else if (lkb->lkb_ast_type & AST_BAST) {
lkb->lkb_ast_type &= ~AST_BAST;
type = AST_BAST;
bmode = lkb->lkb_bastmode;
removed = 0;
ret_type = 0;
ret_mode = 0;
do_bast = lkb->lkb_ast_type & AST_BAST;
do_cast = lkb->lkb_ast_type & AST_COMP;
bastmode = lkb->lkb_bastmode;
castmode = lkb->lkb_castmode;

/* when both are queued figure out which to do first and
switch first so the other goes in the next read */

if (do_cast && do_bast) {
if (lkb->lkb_ast_first == AST_COMP) {
ret_type = AST_COMP;
ret_mode = castmode;
lkb->lkb_ast_type &= ~AST_COMP;
lkb->lkb_ast_first = AST_BAST;
} else {
ret_type = AST_BAST;
ret_mode = bastmode;
lkb->lkb_ast_type &= ~AST_BAST;
lkb->lkb_ast_first = AST_COMP;
}
} else {
ret_type = lkb->lkb_ast_first;
ret_mode = (ret_type == AST_COMP) ? castmode : bastmode;
lkb->lkb_ast_type &= ~ret_type;
lkb->lkb_ast_first = 0;
}

/* if we're doing a bast but the bast is unnecessary, then
switch to do nothing or do a cast if that was needed next */

if ((ret_type == AST_BAST) &&
dlm_modes_compat(bastmode, lkb->lkb_castmode_done)) {
ret_type = 0;
ret_mode = 0;

if (do_cast) {
ret_type = AST_COMP;
ret_mode = castmode;
lkb->lkb_ast_type &= ~AST_COMP;
lkb->lkb_ast_first = 0;
}
}

if (lkb->lkb_ast_first != lkb->lkb_ast_type) {
log_print("device_read %x ast_first %x ast_type %x",
lkb->lkb_id, lkb->lkb_ast_first, lkb->lkb_ast_type);
}

if (!lkb->lkb_ast_type) {
Expand All @@ -870,15 +916,29 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count,
}
spin_unlock(&proc->asts_spin);

error = copy_result_to_user(lkb->lkb_ua,
test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags),
type, bmode, buf, count);
if (ret_type) {
error = copy_result_to_user(lkb->lkb_ua,
test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags),
ret_type, ret_mode, buf, count);

if (ret_type == AST_COMP)
lkb->lkb_castmode_done = castmode;
if (ret_type == AST_BAST)
lkb->lkb_bastmode_done = bastmode;
}

/* removes reference for the proc->asts lists added by
dlm_user_add_ast() and may result in the lkb being freed */

if (removed)
dlm_put_lkb(lkb);

/* the bast that was queued was eliminated (see unnecessary above),
leaving nothing to return */

if (!ret_type)
goto try_another;

return error;
}

Expand Down

0 comments on commit 89d799d

Please sign in to comment.