diff options
Diffstat (limited to 'fs/dlm/user.c')
-rw-r--r-- | fs/dlm/user.c | 185 |
1 files changed, 64 insertions, 121 deletions
diff --git a/fs/dlm/user.c b/fs/dlm/user.c index 66d6c16bf440..d5ab3fe7c198 100644 --- a/fs/dlm/user.c +++ b/fs/dlm/user.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include "lock.h" | 24 | #include "lock.h" |
25 | #include "lvb_table.h" | 25 | #include "lvb_table.h" |
26 | #include "user.h" | 26 | #include "user.h" |
27 | #include "ast.h" | ||
27 | 28 | ||
28 | static const char name_prefix[] = "dlm"; | 29 | static const char name_prefix[] = "dlm"; |
29 | static const struct file_operations device_fops; | 30 | static const struct file_operations device_fops; |
@@ -152,19 +153,16 @@ static void compat_output(struct dlm_lock_result *res, | |||
152 | not related to the lifetime of the lkb struct which is managed | 153 | not related to the lifetime of the lkb struct which is managed |
153 | entirely by refcount. */ | 154 | entirely by refcount. */ |
154 | 155 | ||
155 | static int lkb_is_endoflife(struct dlm_lkb *lkb, int sb_status, int type) | 156 | static int lkb_is_endoflife(int mode, int status) |
156 | { | 157 | { |
157 | switch (sb_status) { | 158 | switch (status) { |
158 | case -DLM_EUNLOCK: | 159 | case -DLM_EUNLOCK: |
159 | return 1; | 160 | return 1; |
160 | case -DLM_ECANCEL: | 161 | case -DLM_ECANCEL: |
161 | case -ETIMEDOUT: | 162 | case -ETIMEDOUT: |
162 | case -EDEADLK: | 163 | case -EDEADLK: |
163 | if (lkb->lkb_grmode == DLM_LOCK_IV) | ||
164 | return 1; | ||
165 | break; | ||
166 | case -EAGAIN: | 164 | case -EAGAIN: |
167 | if (type == AST_COMP && lkb->lkb_grmode == DLM_LOCK_IV) | 165 | if (mode == DLM_LOCK_IV) |
168 | return 1; | 166 | return 1; |
169 | break; | 167 | break; |
170 | } | 168 | } |
@@ -174,12 +172,13 @@ static int lkb_is_endoflife(struct dlm_lkb *lkb, int sb_status, int type) | |||
174 | /* we could possibly check if the cancel of an orphan has resulted in the lkb | 172 | /* we could possibly check if the cancel of an orphan has resulted in the lkb |
175 | being removed and then remove that lkb from the orphans list and free it */ | 173 | being removed and then remove that lkb from the orphans list and free it */ |
176 | 174 | ||
177 | void dlm_user_add_ast(struct dlm_lkb *lkb, int type, int mode) | 175 | void dlm_user_add_ast(struct dlm_lkb *lkb, uint32_t flags, int mode, |
176 | int status, uint32_t sbflags, uint64_t seq) | ||
178 | { | 177 | { |
179 | struct dlm_ls *ls; | 178 | struct dlm_ls *ls; |
180 | struct dlm_user_args *ua; | 179 | struct dlm_user_args *ua; |
181 | struct dlm_user_proc *proc; | 180 | struct dlm_user_proc *proc; |
182 | int eol = 0, ast_type; | 181 | int rv; |
183 | 182 | ||
184 | if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD)) | 183 | if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD)) |
185 | return; | 184 | return; |
@@ -200,49 +199,29 @@ void dlm_user_add_ast(struct dlm_lkb *lkb, int type, int mode) | |||
200 | ua = lkb->lkb_ua; | 199 | ua = lkb->lkb_ua; |
201 | proc = ua->proc; | 200 | proc = ua->proc; |
202 | 201 | ||
203 | if (type == AST_BAST && ua->bastaddr == NULL) | 202 | if ((flags & DLM_CB_BAST) && ua->bastaddr == NULL) |
204 | goto out; | 203 | goto out; |
205 | 204 | ||
205 | if ((flags & DLM_CB_CAST) && lkb_is_endoflife(mode, status)) | ||
206 | lkb->lkb_flags |= DLM_IFL_ENDOFLIFE; | ||
207 | |||
206 | spin_lock(&proc->asts_spin); | 208 | spin_lock(&proc->asts_spin); |
207 | 209 | ||
208 | ast_type = lkb->lkb_ast_type; | 210 | rv = dlm_add_lkb_callback(lkb, flags, mode, status, sbflags, seq); |
209 | lkb->lkb_ast_type |= type; | 211 | if (rv < 0) { |
210 | if (type == AST_BAST) | 212 | spin_unlock(&proc->asts_spin); |
211 | lkb->lkb_bastmode = mode; | 213 | goto out; |
212 | else | 214 | } |
213 | lkb->lkb_castmode = mode; | ||
214 | 215 | ||
215 | if (!ast_type) { | 216 | if (list_empty(&lkb->lkb_astqueue)) { |
216 | kref_get(&lkb->lkb_ref); | 217 | kref_get(&lkb->lkb_ref); |
217 | list_add_tail(&lkb->lkb_astqueue, &proc->asts); | 218 | list_add_tail(&lkb->lkb_astqueue, &proc->asts); |
218 | lkb->lkb_ast_first = type; | ||
219 | wake_up_interruptible(&proc->wait); | 219 | wake_up_interruptible(&proc->wait); |
220 | } | 220 | } |
221 | if (type == AST_COMP && (ast_type & AST_COMP)) | ||
222 | log_debug(ls, "ast overlap %x status %x %x", | ||
223 | lkb->lkb_id, ua->lksb.sb_status, lkb->lkb_flags); | ||
224 | |||
225 | eol = lkb_is_endoflife(lkb, ua->lksb.sb_status, type); | ||
226 | if (eol) { | ||
227 | lkb->lkb_flags |= DLM_IFL_ENDOFLIFE; | ||
228 | } | ||
229 | |||
230 | /* We want to copy the lvb to userspace when the completion | ||
231 | ast is read if the status is 0, the lock has an lvb and | ||
232 | lvb_ops says we should. We could probably have set_lvb_lock() | ||
233 | set update_user_lvb instead and not need old_mode */ | ||
234 | |||
235 | if ((lkb->lkb_ast_type & AST_COMP) && | ||
236 | (lkb->lkb_lksb->sb_status == 0) && | ||
237 | lkb->lkb_lksb->sb_lvbptr && | ||
238 | dlm_lvb_operations[ua->old_mode + 1][lkb->lkb_grmode + 1]) | ||
239 | ua->update_user_lvb = 1; | ||
240 | else | ||
241 | ua->update_user_lvb = 0; | ||
242 | |||
243 | spin_unlock(&proc->asts_spin); | 221 | spin_unlock(&proc->asts_spin); |
244 | 222 | ||
245 | if (eol) { | 223 | if (lkb->lkb_flags & DLM_IFL_ENDOFLIFE) { |
224 | /* N.B. spin_lock locks_spin, not asts_spin */ | ||
246 | spin_lock(&proc->locks_spin); | 225 | spin_lock(&proc->locks_spin); |
247 | if (!list_empty(&lkb->lkb_ownqueue)) { | 226 | if (!list_empty(&lkb->lkb_ownqueue)) { |
248 | list_del_init(&lkb->lkb_ownqueue); | 227 | list_del_init(&lkb->lkb_ownqueue); |
@@ -705,8 +684,9 @@ static int device_close(struct inode *inode, struct file *file) | |||
705 | return 0; | 684 | return 0; |
706 | } | 685 | } |
707 | 686 | ||
708 | static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type, | 687 | static int copy_result_to_user(struct dlm_user_args *ua, int compat, |
709 | int mode, char __user *buf, size_t count) | 688 | uint32_t flags, int mode, int copy_lvb, |
689 | char __user *buf, size_t count) | ||
710 | { | 690 | { |
711 | #ifdef CONFIG_COMPAT | 691 | #ifdef CONFIG_COMPAT |
712 | struct dlm_lock_result32 result32; | 692 | struct dlm_lock_result32 result32; |
@@ -730,7 +710,7 @@ static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type, | |||
730 | notes that a new blocking AST address and parameter are set even if | 710 | notes that a new blocking AST address and parameter are set even if |
731 | the conversion fails, so maybe we should just do that. */ | 711 | the conversion fails, so maybe we should just do that. */ |
732 | 712 | ||
733 | if (type == AST_BAST) { | 713 | if (flags & DLM_CB_BAST) { |
734 | result.user_astaddr = ua->bastaddr; | 714 | result.user_astaddr = ua->bastaddr; |
735 | result.user_astparam = ua->bastparam; | 715 | result.user_astparam = ua->bastparam; |
736 | result.bast_mode = mode; | 716 | result.bast_mode = mode; |
@@ -750,8 +730,7 @@ static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type, | |||
750 | /* copy lvb to userspace if there is one, it's been updated, and | 730 | /* copy lvb to userspace if there is one, it's been updated, and |
751 | the user buffer has space for it */ | 731 | the user buffer has space for it */ |
752 | 732 | ||
753 | if (ua->update_user_lvb && ua->lksb.sb_lvbptr && | 733 | if (copy_lvb && ua->lksb.sb_lvbptr && count >= len + DLM_USER_LVB_LEN) { |
754 | count >= len + DLM_USER_LVB_LEN) { | ||
755 | if (copy_to_user(buf+len, ua->lksb.sb_lvbptr, | 734 | if (copy_to_user(buf+len, ua->lksb.sb_lvbptr, |
756 | DLM_USER_LVB_LEN)) { | 735 | DLM_USER_LVB_LEN)) { |
757 | error = -EFAULT; | 736 | error = -EFAULT; |
@@ -801,13 +780,12 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count, | |||
801 | struct dlm_user_proc *proc = file->private_data; | 780 | struct dlm_user_proc *proc = file->private_data; |
802 | struct dlm_lkb *lkb; | 781 | struct dlm_lkb *lkb; |
803 | DECLARE_WAITQUEUE(wait, current); | 782 | DECLARE_WAITQUEUE(wait, current); |
804 | int error = 0, removed; | 783 | struct dlm_callback cb; |
805 | int ret_type, ret_mode; | 784 | int rv, resid, copy_lvb = 0; |
806 | int bastmode, castmode, do_bast, do_cast; | ||
807 | 785 | ||
808 | if (count == sizeof(struct dlm_device_version)) { | 786 | if (count == sizeof(struct dlm_device_version)) { |
809 | error = copy_version_to_user(buf, count); | 787 | rv = copy_version_to_user(buf, count); |
810 | return error; | 788 | return rv; |
811 | } | 789 | } |
812 | 790 | ||
813 | if (!proc) { | 791 | if (!proc) { |
@@ -854,92 +832,57 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count, | |||
854 | } | 832 | } |
855 | } | 833 | } |
856 | 834 | ||
857 | /* there may be both completion and blocking asts to return for | 835 | /* if we empty lkb_callbacks, we don't want to unlock the spinlock |
858 | the lkb, don't remove lkb from asts list unless no asts remain */ | 836 | without removing lkb_astqueue; so empty lkb_astqueue is always |
837 | consistent with empty lkb_callbacks */ | ||
859 | 838 | ||
860 | lkb = list_entry(proc->asts.next, struct dlm_lkb, lkb_astqueue); | 839 | lkb = list_entry(proc->asts.next, struct dlm_lkb, lkb_astqueue); |
861 | 840 | ||
862 | removed = 0; | 841 | rv = dlm_rem_lkb_callback(lkb->lkb_resource->res_ls, lkb, &cb, &resid); |
863 | ret_type = 0; | 842 | if (rv < 0) { |
864 | ret_mode = 0; | 843 | /* this shouldn't happen; lkb should have been removed from |
865 | do_bast = lkb->lkb_ast_type & AST_BAST; | 844 | list when resid was zero */ |
866 | do_cast = lkb->lkb_ast_type & AST_COMP; | 845 | log_print("dlm_rem_lkb_callback empty %x", lkb->lkb_id); |
867 | bastmode = lkb->lkb_bastmode; | 846 | list_del_init(&lkb->lkb_astqueue); |
868 | castmode = lkb->lkb_castmode; | 847 | spin_unlock(&proc->asts_spin); |
869 | 848 | /* removes ref for proc->asts, may cause lkb to be freed */ | |
870 | /* when both are queued figure out which to do first and | 849 | dlm_put_lkb(lkb); |
871 | switch first so the other goes in the next read */ | 850 | goto try_another; |
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 | } | 851 | } |
852 | if (!resid) | ||
853 | list_del_init(&lkb->lkb_astqueue); | ||
854 | spin_unlock(&proc->asts_spin); | ||
891 | 855 | ||
892 | /* if we're doing a bast but the bast is unnecessary, then | 856 | if (cb.flags & DLM_CB_SKIP) { |
893 | switch to do nothing or do a cast if that was needed next */ | 857 | /* removes ref for proc->asts, may cause lkb to be freed */ |
894 | 858 | if (!resid) | |
895 | if ((ret_type == AST_BAST) && | 859 | dlm_put_lkb(lkb); |
896 | dlm_modes_compat(bastmode, lkb->lkb_castmode_done)) { | 860 | goto try_another; |
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 | } | 861 | } |
907 | 862 | ||
908 | if (lkb->lkb_ast_first != lkb->lkb_ast_type) { | 863 | if (cb.flags & DLM_CB_CAST) { |
909 | log_print("device_read %x ast_first %x ast_type %x", | 864 | int old_mode, new_mode; |
910 | lkb->lkb_id, lkb->lkb_ast_first, lkb->lkb_ast_type); | ||
911 | } | ||
912 | 865 | ||
913 | if (!lkb->lkb_ast_type) { | 866 | old_mode = lkb->lkb_last_cast.mode; |
914 | list_del(&lkb->lkb_astqueue); | 867 | new_mode = cb.mode; |
915 | removed = 1; | ||
916 | } | ||
917 | spin_unlock(&proc->asts_spin); | ||
918 | 868 | ||
919 | if (ret_type) { | 869 | if (!cb.sb_status && lkb->lkb_lksb->sb_lvbptr && |
920 | error = copy_result_to_user(lkb->lkb_ua, | 870 | dlm_lvb_operations[old_mode + 1][new_mode + 1]) |
921 | test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags), | 871 | copy_lvb = 1; |
922 | ret_type, ret_mode, buf, count); | ||
923 | 872 | ||
924 | if (ret_type == AST_COMP) | 873 | lkb->lkb_lksb->sb_status = cb.sb_status; |
925 | lkb->lkb_castmode_done = castmode; | 874 | lkb->lkb_lksb->sb_flags = cb.sb_flags; |
926 | if (ret_type == AST_BAST) | ||
927 | lkb->lkb_bastmode_done = bastmode; | ||
928 | } | 875 | } |
929 | 876 | ||
930 | /* removes reference for the proc->asts lists added by | 877 | rv = copy_result_to_user(lkb->lkb_ua, |
931 | dlm_user_add_ast() and may result in the lkb being freed */ | 878 | test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags), |
879 | cb.flags, cb.mode, copy_lvb, buf, count); | ||
932 | 880 | ||
933 | if (removed) | 881 | /* removes ref for proc->asts, may cause lkb to be freed */ |
882 | if (!resid) | ||
934 | dlm_put_lkb(lkb); | 883 | dlm_put_lkb(lkb); |
935 | 884 | ||
936 | /* the bast that was queued was eliminated (see unnecessary above), | 885 | return rv; |
937 | leaving nothing to return */ | ||
938 | |||
939 | if (!ret_type) | ||
940 | goto try_another; | ||
941 | |||
942 | return error; | ||
943 | } | 886 | } |
944 | 887 | ||
945 | static unsigned int device_poll(struct file *file, poll_table *wait) | 888 | static unsigned int device_poll(struct file *file, poll_table *wait) |