Commit 89d799d0 authored by David Teigland's avatar David Teigland

dlm: fix ast ordering for user locks

Commit 7fe2b319 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: default avatarDavid Teigland <teigland@redhat.com>
parent 99fb19d4
...@@ -215,6 +215,7 @@ void dlm_user_add_ast(struct dlm_lkb *lkb, int type, int mode) ...@@ -215,6 +215,7 @@ void dlm_user_add_ast(struct dlm_lkb *lkb, int type, int mode)
if (!ast_type) { if (!ast_type) {
kref_get(&lkb->lkb_ref); kref_get(&lkb->lkb_ref);
list_add_tail(&lkb->lkb_astqueue, &proc->asts); list_add_tail(&lkb->lkb_astqueue, &proc->asts);
lkb->lkb_ast_first = type;
wake_up_interruptible(&proc->wait); wake_up_interruptible(&proc->wait);
} }
if (type == AST_COMP && (ast_type & AST_COMP)) if (type == AST_COMP && (ast_type & AST_COMP))
...@@ -223,7 +224,6 @@ void dlm_user_add_ast(struct dlm_lkb *lkb, int type, int mode) ...@@ -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); eol = lkb_is_endoflife(lkb, ua->lksb.sb_status, type);
if (eol) { if (eol) {
lkb->lkb_ast_type &= ~AST_BAST;
lkb->lkb_flags |= DLM_IFL_ENDOFLIFE; lkb->lkb_flags |= DLM_IFL_ENDOFLIFE;
} }
...@@ -706,7 +706,7 @@ static int device_close(struct inode *inode, struct file *file) ...@@ -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, 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 #ifdef CONFIG_COMPAT
struct dlm_lock_result32 result32; struct dlm_lock_result32 result32;
...@@ -733,7 +733,7 @@ static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type, ...@@ -733,7 +733,7 @@ static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type,
if (type == AST_BAST) { if (type == AST_BAST) {
result.user_astaddr = ua->bastaddr; result.user_astaddr = ua->bastaddr;
result.user_astparam = ua->bastparam; result.user_astparam = ua->bastparam;
result.bast_mode = bmode; result.bast_mode = mode;
} else { } else {
result.user_astaddr = ua->castaddr; result.user_astaddr = ua->castaddr;
result.user_astparam = ua->castparam; result.user_astparam = ua->castparam;
...@@ -801,7 +801,9 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count, ...@@ -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_user_proc *proc = file->private_data;
struct dlm_lkb *lkb; struct dlm_lkb *lkb;
DECLARE_WAITQUEUE(wait, current); 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)) { if (count == sizeof(struct dlm_device_version)) {
error = copy_version_to_user(buf, count); error = copy_version_to_user(buf, count);
...@@ -820,6 +822,8 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count, ...@@ -820,6 +822,8 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count,
#endif #endif
return -EINVAL; return -EINVAL;
try_another:
/* do we really need this? can a read happen after a close? */ /* do we really need this? can a read happen after a close? */
if (test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags)) if (test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))
return -EINVAL; return -EINVAL;
...@@ -855,13 +859,55 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count, ...@@ -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); lkb = list_entry(proc->asts.next, struct dlm_lkb, lkb_astqueue);
if (lkb->lkb_ast_type & AST_COMP) { removed = 0;
lkb->lkb_ast_type &= ~AST_COMP; ret_type = 0;
type = AST_COMP; ret_mode = 0;
} else if (lkb->lkb_ast_type & AST_BAST) { do_bast = lkb->lkb_ast_type & AST_BAST;
lkb->lkb_ast_type &= ~AST_BAST; do_cast = lkb->lkb_ast_type & AST_COMP;
type = AST_BAST; bastmode = lkb->lkb_bastmode;
bmode = 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) { if (!lkb->lkb_ast_type) {
...@@ -870,15 +916,29 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count, ...@@ -870,15 +916,29 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count,
} }
spin_unlock(&proc->asts_spin); spin_unlock(&proc->asts_spin);
error = copy_result_to_user(lkb->lkb_ua, if (ret_type) {
test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags), error = copy_result_to_user(lkb->lkb_ua,
type, bmode, buf, count); 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 /* removes reference for the proc->asts lists added by
dlm_user_add_ast() and may result in the lkb being freed */ dlm_user_add_ast() and may result in the lkb being freed */
if (removed) if (removed)
dlm_put_lkb(lkb); 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; return error;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment