diff options
-rw-r--r-- | ipc/msg.c | 58 |
1 files changed, 32 insertions, 26 deletions
@@ -886,21 +886,19 @@ static struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode) | |||
886 | return found ?: ERR_PTR(-EAGAIN); | 886 | return found ?: ERR_PTR(-EAGAIN); |
887 | } | 887 | } |
888 | 888 | ||
889 | 889 | long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg, | |
890 | long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, | ||
891 | int msgflg, | ||
892 | long (*msg_handler)(void __user *, struct msg_msg *, size_t)) | 890 | long (*msg_handler)(void __user *, struct msg_msg *, size_t)) |
893 | { | 891 | { |
894 | struct msg_queue *msq; | ||
895 | struct msg_msg *msg; | ||
896 | int mode; | 892 | int mode; |
893 | struct msg_queue *msq; | ||
897 | struct ipc_namespace *ns; | 894 | struct ipc_namespace *ns; |
898 | struct msg_msg *copy = NULL; | 895 | struct msg_msg *msg, *copy = NULL; |
899 | 896 | ||
900 | ns = current->nsproxy->ipc_ns; | 897 | ns = current->nsproxy->ipc_ns; |
901 | 898 | ||
902 | if (msqid < 0 || (long) bufsz < 0) | 899 | if (msqid < 0 || (long) bufsz < 0) |
903 | return -EINVAL; | 900 | return -EINVAL; |
901 | |||
904 | if (msgflg & MSG_COPY) { | 902 | if (msgflg & MSG_COPY) { |
905 | copy = prepare_copy(buf, min_t(size_t, bufsz, ns->msg_ctlmax)); | 903 | copy = prepare_copy(buf, min_t(size_t, bufsz, ns->msg_ctlmax)); |
906 | if (IS_ERR(copy)) | 904 | if (IS_ERR(copy)) |
@@ -908,8 +906,10 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, | |||
908 | } | 906 | } |
909 | mode = convert_mode(&msgtyp, msgflg); | 907 | mode = convert_mode(&msgtyp, msgflg); |
910 | 908 | ||
911 | msq = msg_lock_check(ns, msqid); | 909 | rcu_read_lock(); |
910 | msq = msq_obtain_object_check(ns, msqid); | ||
912 | if (IS_ERR(msq)) { | 911 | if (IS_ERR(msq)) { |
912 | rcu_read_unlock(); | ||
913 | free_copy(copy); | 913 | free_copy(copy); |
914 | return PTR_ERR(msq); | 914 | return PTR_ERR(msq); |
915 | } | 915 | } |
@@ -919,10 +919,10 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, | |||
919 | 919 | ||
920 | msg = ERR_PTR(-EACCES); | 920 | msg = ERR_PTR(-EACCES); |
921 | if (ipcperms(ns, &msq->q_perm, S_IRUGO)) | 921 | if (ipcperms(ns, &msq->q_perm, S_IRUGO)) |
922 | goto out_unlock; | 922 | goto out_unlock1; |
923 | 923 | ||
924 | ipc_lock_object(&msq->q_perm); | ||
924 | msg = find_msg(msq, &msgtyp, mode); | 925 | msg = find_msg(msq, &msgtyp, mode); |
925 | |||
926 | if (!IS_ERR(msg)) { | 926 | if (!IS_ERR(msg)) { |
927 | /* | 927 | /* |
928 | * Found a suitable message. | 928 | * Found a suitable message. |
@@ -930,7 +930,7 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, | |||
930 | */ | 930 | */ |
931 | if ((bufsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) { | 931 | if ((bufsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) { |
932 | msg = ERR_PTR(-E2BIG); | 932 | msg = ERR_PTR(-E2BIG); |
933 | goto out_unlock; | 933 | goto out_unlock0; |
934 | } | 934 | } |
935 | /* | 935 | /* |
936 | * If we are copying, then do not unlink message and do | 936 | * If we are copying, then do not unlink message and do |
@@ -938,8 +938,9 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, | |||
938 | */ | 938 | */ |
939 | if (msgflg & MSG_COPY) { | 939 | if (msgflg & MSG_COPY) { |
940 | msg = copy_msg(msg, copy); | 940 | msg = copy_msg(msg, copy); |
941 | goto out_unlock; | 941 | goto out_unlock0; |
942 | } | 942 | } |
943 | |||
943 | list_del(&msg->m_list); | 944 | list_del(&msg->m_list); |
944 | msq->q_qnum--; | 945 | msq->q_qnum--; |
945 | msq->q_rtime = get_seconds(); | 946 | msq->q_rtime = get_seconds(); |
@@ -948,14 +949,16 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, | |||
948 | atomic_sub(msg->m_ts, &ns->msg_bytes); | 949 | atomic_sub(msg->m_ts, &ns->msg_bytes); |
949 | atomic_dec(&ns->msg_hdrs); | 950 | atomic_dec(&ns->msg_hdrs); |
950 | ss_wakeup(&msq->q_senders, 0); | 951 | ss_wakeup(&msq->q_senders, 0); |
951 | msg_unlock(msq); | 952 | |
952 | break; | 953 | goto out_unlock0; |
953 | } | 954 | } |
955 | |||
954 | /* No message waiting. Wait for a message */ | 956 | /* No message waiting. Wait for a message */ |
955 | if (msgflg & IPC_NOWAIT) { | 957 | if (msgflg & IPC_NOWAIT) { |
956 | msg = ERR_PTR(-ENOMSG); | 958 | msg = ERR_PTR(-ENOMSG); |
957 | goto out_unlock; | 959 | goto out_unlock0; |
958 | } | 960 | } |
961 | |||
959 | list_add_tail(&msr_d.r_list, &msq->q_receivers); | 962 | list_add_tail(&msr_d.r_list, &msq->q_receivers); |
960 | msr_d.r_tsk = current; | 963 | msr_d.r_tsk = current; |
961 | msr_d.r_msgtype = msgtyp; | 964 | msr_d.r_msgtype = msgtyp; |
@@ -966,8 +969,9 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, | |||
966 | msr_d.r_maxsize = bufsz; | 969 | msr_d.r_maxsize = bufsz; |
967 | msr_d.r_msg = ERR_PTR(-EAGAIN); | 970 | msr_d.r_msg = ERR_PTR(-EAGAIN); |
968 | current->state = TASK_INTERRUPTIBLE; | 971 | current->state = TASK_INTERRUPTIBLE; |
969 | msg_unlock(msq); | ||
970 | 972 | ||
973 | ipc_unlock_object(&msq->q_perm); | ||
974 | rcu_read_unlock(); | ||
971 | schedule(); | 975 | schedule(); |
972 | 976 | ||
973 | /* Lockless receive, part 1: | 977 | /* Lockless receive, part 1: |
@@ -978,7 +982,7 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, | |||
978 | * Prior to destruction, expunge_all(-EIRDM) changes r_msg. | 982 | * Prior to destruction, expunge_all(-EIRDM) changes r_msg. |
979 | * Thus if r_msg is -EAGAIN, then the queue not yet destroyed. | 983 | * Thus if r_msg is -EAGAIN, then the queue not yet destroyed. |
980 | * rcu_read_lock() prevents preemption between reading r_msg | 984 | * rcu_read_lock() prevents preemption between reading r_msg |
981 | * and the spin_lock() inside ipc_lock_by_ptr(). | 985 | * and acquiring the q_perm.lock in ipc_lock_object(). |
982 | */ | 986 | */ |
983 | rcu_read_lock(); | 987 | rcu_read_lock(); |
984 | 988 | ||
@@ -997,32 +1001,34 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, | |||
997 | * If there is a message or an error then accept it without | 1001 | * If there is a message or an error then accept it without |
998 | * locking. | 1002 | * locking. |
999 | */ | 1003 | */ |
1000 | if (msg != ERR_PTR(-EAGAIN)) { | 1004 | if (msg != ERR_PTR(-EAGAIN)) |
1001 | rcu_read_unlock(); | 1005 | goto out_unlock1; |
1002 | break; | ||
1003 | } | ||
1004 | 1006 | ||
1005 | /* Lockless receive, part 3: | 1007 | /* Lockless receive, part 3: |
1006 | * Acquire the queue spinlock. | 1008 | * Acquire the queue spinlock. |
1007 | */ | 1009 | */ |
1008 | ipc_lock_by_ptr(&msq->q_perm); | 1010 | ipc_lock_object(&msq->q_perm); |
1009 | rcu_read_unlock(); | ||
1010 | 1011 | ||
1011 | /* Lockless receive, part 4: | 1012 | /* Lockless receive, part 4: |
1012 | * Repeat test after acquiring the spinlock. | 1013 | * Repeat test after acquiring the spinlock. |
1013 | */ | 1014 | */ |
1014 | msg = (struct msg_msg*)msr_d.r_msg; | 1015 | msg = (struct msg_msg*)msr_d.r_msg; |
1015 | if (msg != ERR_PTR(-EAGAIN)) | 1016 | if (msg != ERR_PTR(-EAGAIN)) |
1016 | goto out_unlock; | 1017 | goto out_unlock0; |
1017 | 1018 | ||
1018 | list_del(&msr_d.r_list); | 1019 | list_del(&msr_d.r_list); |
1019 | if (signal_pending(current)) { | 1020 | if (signal_pending(current)) { |
1020 | msg = ERR_PTR(-ERESTARTNOHAND); | 1021 | msg = ERR_PTR(-ERESTARTNOHAND); |
1021 | out_unlock: | 1022 | goto out_unlock0; |
1022 | msg_unlock(msq); | ||
1023 | break; | ||
1024 | } | 1023 | } |
1024 | |||
1025 | ipc_unlock_object(&msq->q_perm); | ||
1025 | } | 1026 | } |
1027 | |||
1028 | out_unlock0: | ||
1029 | ipc_unlock_object(&msq->q_perm); | ||
1030 | out_unlock1: | ||
1031 | rcu_read_unlock(); | ||
1026 | if (IS_ERR(msg)) { | 1032 | if (IS_ERR(msg)) { |
1027 | free_copy(copy); | 1033 | free_copy(copy); |
1028 | return PTR_ERR(msg); | 1034 | return PTR_ERR(msg); |