Skip to content

Commit 89d799d

Browse files
committed
dlm: fix ast ordering for user locks
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 <[email protected]>
1 parent 99fb19d commit 89d799d

File tree

1 file changed

+74
-14
lines changed

1 file changed

+74
-14
lines changed

fs/dlm/user.c

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ void dlm_user_add_ast(struct dlm_lkb *lkb, int type, int mode)
215215
if (!ast_type) {
216216
kref_get(&lkb->lkb_ref);
217217
list_add_tail(&lkb->lkb_astqueue, &proc->asts);
218+
lkb->lkb_ast_first = type;
218219
wake_up_interruptible(&proc->wait);
219220
}
220221
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)
223224

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

@@ -706,7 +706,7 @@ static int device_close(struct inode *inode, struct file *file)
706706
}
707707

708708
static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type,
709-
int bmode, char __user *buf, size_t count)
709+
int mode, char __user *buf, size_t count)
710710
{
711711
#ifdef CONFIG_COMPAT
712712
struct dlm_lock_result32 result32;
@@ -733,7 +733,7 @@ static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type,
733733
if (type == AST_BAST) {
734734
result.user_astaddr = ua->bastaddr;
735735
result.user_astparam = ua->bastparam;
736-
result.bast_mode = bmode;
736+
result.bast_mode = mode;
737737
} else {
738738
result.user_astaddr = ua->castaddr;
739739
result.user_astparam = ua->castparam;
@@ -801,7 +801,9 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count,
801801
struct dlm_user_proc *proc = file->private_data;
802802
struct dlm_lkb *lkb;
803803
DECLARE_WAITQUEUE(wait, current);
804-
int error, type=0, bmode=0, removed = 0;
804+
int error = 0, removed;
805+
int ret_type, ret_mode;
806+
int bastmode, castmode, do_bast, do_cast;
805807

806808
if (count == sizeof(struct dlm_device_version)) {
807809
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,
820822
#endif
821823
return -EINVAL;
822824

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

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

858-
if (lkb->lkb_ast_type & AST_COMP) {
859-
lkb->lkb_ast_type &= ~AST_COMP;
860-
type = AST_COMP;
861-
} else if (lkb->lkb_ast_type & AST_BAST) {
862-
lkb->lkb_ast_type &= ~AST_BAST;
863-
type = AST_BAST;
864-
bmode = lkb->lkb_bastmode;
862+
removed = 0;
863+
ret_type = 0;
864+
ret_mode = 0;
865+
do_bast = lkb->lkb_ast_type & AST_BAST;
866+
do_cast = lkb->lkb_ast_type & AST_COMP;
867+
bastmode = lkb->lkb_bastmode;
868+
castmode = lkb->lkb_castmode;
869+
870+
/* when both are queued figure out which to do first and
871+
switch first so the other goes in the next read */
872+
873+
if (do_cast && do_bast) {
874+
if (lkb->lkb_ast_first == AST_COMP) {
875+
ret_type = AST_COMP;
876+
ret_mode = castmode;
877+
lkb->lkb_ast_type &= ~AST_COMP;
878+
lkb->lkb_ast_first = AST_BAST;
879+
} else {
880+
ret_type = AST_BAST;
881+
ret_mode = bastmode;
882+
lkb->lkb_ast_type &= ~AST_BAST;
883+
lkb->lkb_ast_first = AST_COMP;
884+
}
885+
} else {
886+
ret_type = lkb->lkb_ast_first;
887+
ret_mode = (ret_type == AST_COMP) ? castmode : bastmode;
888+
lkb->lkb_ast_type &= ~ret_type;
889+
lkb->lkb_ast_first = 0;
890+
}
891+
892+
/* if we're doing a bast but the bast is unnecessary, then
893+
switch to do nothing or do a cast if that was needed next */
894+
895+
if ((ret_type == AST_BAST) &&
896+
dlm_modes_compat(bastmode, lkb->lkb_castmode_done)) {
897+
ret_type = 0;
898+
ret_mode = 0;
899+
900+
if (do_cast) {
901+
ret_type = AST_COMP;
902+
ret_mode = castmode;
903+
lkb->lkb_ast_type &= ~AST_COMP;
904+
lkb->lkb_ast_first = 0;
905+
}
906+
}
907+
908+
if (lkb->lkb_ast_first != lkb->lkb_ast_type) {
909+
log_print("device_read %x ast_first %x ast_type %x",
910+
lkb->lkb_id, lkb->lkb_ast_first, lkb->lkb_ast_type);
865911
}
866912

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

873-
error = copy_result_to_user(lkb->lkb_ua,
874-
test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags),
875-
type, bmode, buf, count);
919+
if (ret_type) {
920+
error = copy_result_to_user(lkb->lkb_ua,
921+
test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags),
922+
ret_type, ret_mode, buf, count);
923+
924+
if (ret_type == AST_COMP)
925+
lkb->lkb_castmode_done = castmode;
926+
if (ret_type == AST_BAST)
927+
lkb->lkb_bastmode_done = bastmode;
928+
}
876929

877930
/* removes reference for the proc->asts lists added by
878931
dlm_user_add_ast() and may result in the lkb being freed */
932+
879933
if (removed)
880934
dlm_put_lkb(lkb);
881935

936+
/* the bast that was queued was eliminated (see unnecessary above),
937+
leaving nothing to return */
938+
939+
if (!ret_type)
940+
goto try_another;
941+
882942
return error;
883943
}
884944

0 commit comments

Comments
 (0)