aboutsummaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authorDavidlohr Bueso <davidlohr.bueso@hp.com>2013-07-08 19:01:18 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-07-09 13:33:27 -0400
commit41a0d523d0f626e9da0dc01de47f1b89058033cf (patch)
treeaf4021f97c49c59784dbab62fb1dad36fe6d6f3c /ipc
parent3dd1f784ed6603d7ab1043e51e6371235edf2313 (diff)
ipc,msg: shorten critical region in msgrcv
do_msgrcv() is the last msg queue function that abuses the ipc lock Take it only when needed when actually updating msq. Signed-off-by: Davidlohr Bueso <davidlohr.bueso@hp.com> Cc: Andi Kleen <andi@firstfloor.org> Cc: Rik van Riel <riel@redhat.com> Tested-by: Sedat Dilek <sedat.dilek@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'ipc')
-rw-r--r--ipc/msg.c58
1 files changed, 32 insertions, 26 deletions
diff --git a/ipc/msg.c b/ipc/msg.c
index f2a1a8f30cd4..a3c0dc40a0cf 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -885,21 +885,19 @@ static struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode)
885 return ERR_PTR(-EAGAIN); 885 return ERR_PTR(-EAGAIN);
886} 886}
887 887
888 888long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg,
889long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
890 int msgflg,
891 long (*msg_handler)(void __user *, struct msg_msg *, size_t)) 889 long (*msg_handler)(void __user *, struct msg_msg *, size_t))
892{ 890{
893 struct msg_queue *msq;
894 struct msg_msg *msg;
895 int mode; 891 int mode;
892 struct msg_queue *msq;
896 struct ipc_namespace *ns; 893 struct ipc_namespace *ns;
897 struct msg_msg *copy = NULL; 894 struct msg_msg *msg, *copy = NULL;
898 895
899 ns = current->nsproxy->ipc_ns; 896 ns = current->nsproxy->ipc_ns;
900 897
901 if (msqid < 0 || (long) bufsz < 0) 898 if (msqid < 0 || (long) bufsz < 0)
902 return -EINVAL; 899 return -EINVAL;
900
903 if (msgflg & MSG_COPY) { 901 if (msgflg & MSG_COPY) {
904 copy = prepare_copy(buf, min_t(size_t, bufsz, ns->msg_ctlmax)); 902 copy = prepare_copy(buf, min_t(size_t, bufsz, ns->msg_ctlmax));
905 if (IS_ERR(copy)) 903 if (IS_ERR(copy))
@@ -907,8 +905,10 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
907 } 905 }
908 mode = convert_mode(&msgtyp, msgflg); 906 mode = convert_mode(&msgtyp, msgflg);
909 907
910 msq = msg_lock_check(ns, msqid); 908 rcu_read_lock();
909 msq = msq_obtain_object_check(ns, msqid);
911 if (IS_ERR(msq)) { 910 if (IS_ERR(msq)) {
911 rcu_read_unlock();
912 free_copy(copy); 912 free_copy(copy);
913 return PTR_ERR(msq); 913 return PTR_ERR(msq);
914 } 914 }
@@ -918,10 +918,10 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
918 918
919 msg = ERR_PTR(-EACCES); 919 msg = ERR_PTR(-EACCES);
920 if (ipcperms(ns, &msq->q_perm, S_IRUGO)) 920 if (ipcperms(ns, &msq->q_perm, S_IRUGO))
921 goto out_unlock; 921 goto out_unlock1;
922 922
923 ipc_lock_object(&msq->q_perm);
923 msg = find_msg(msq, &msgtyp, mode); 924 msg = find_msg(msq, &msgtyp, mode);
924
925 if (!IS_ERR(msg)) { 925 if (!IS_ERR(msg)) {
926 /* 926 /*
927 * Found a suitable message. 927 * Found a suitable message.
@@ -929,7 +929,7 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
929 */ 929 */
930 if ((bufsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) { 930 if ((bufsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) {
931 msg = ERR_PTR(-E2BIG); 931 msg = ERR_PTR(-E2BIG);
932 goto out_unlock; 932 goto out_unlock0;
933 } 933 }
934 /* 934 /*
935 * If we are copying, then do not unlink message and do 935 * If we are copying, then do not unlink message and do
@@ -937,8 +937,9 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
937 */ 937 */
938 if (msgflg & MSG_COPY) { 938 if (msgflg & MSG_COPY) {
939 msg = copy_msg(msg, copy); 939 msg = copy_msg(msg, copy);
940 goto out_unlock; 940 goto out_unlock0;
941 } 941 }
942
942 list_del(&msg->m_list); 943 list_del(&msg->m_list);
943 msq->q_qnum--; 944 msq->q_qnum--;
944 msq->q_rtime = get_seconds(); 945 msq->q_rtime = get_seconds();
@@ -947,14 +948,16 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
947 atomic_sub(msg->m_ts, &ns->msg_bytes); 948 atomic_sub(msg->m_ts, &ns->msg_bytes);
948 atomic_dec(&ns->msg_hdrs); 949 atomic_dec(&ns->msg_hdrs);
949 ss_wakeup(&msq->q_senders, 0); 950 ss_wakeup(&msq->q_senders, 0);
950 msg_unlock(msq); 951
951 break; 952 goto out_unlock0;
952 } 953 }
954
953 /* No message waiting. Wait for a message */ 955 /* No message waiting. Wait for a message */
954 if (msgflg & IPC_NOWAIT) { 956 if (msgflg & IPC_NOWAIT) {
955 msg = ERR_PTR(-ENOMSG); 957 msg = ERR_PTR(-ENOMSG);
956 goto out_unlock; 958 goto out_unlock0;
957 } 959 }
960
958 list_add_tail(&msr_d.r_list, &msq->q_receivers); 961 list_add_tail(&msr_d.r_list, &msq->q_receivers);
959 msr_d.r_tsk = current; 962 msr_d.r_tsk = current;
960 msr_d.r_msgtype = msgtyp; 963 msr_d.r_msgtype = msgtyp;
@@ -965,8 +968,9 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
965 msr_d.r_maxsize = bufsz; 968 msr_d.r_maxsize = bufsz;
966 msr_d.r_msg = ERR_PTR(-EAGAIN); 969 msr_d.r_msg = ERR_PTR(-EAGAIN);
967 current->state = TASK_INTERRUPTIBLE; 970 current->state = TASK_INTERRUPTIBLE;
968 msg_unlock(msq);
969 971
972 ipc_unlock_object(&msq->q_perm);
973 rcu_read_unlock();
970 schedule(); 974 schedule();
971 975
972 /* Lockless receive, part 1: 976 /* Lockless receive, part 1:
@@ -977,7 +981,7 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
977 * Prior to destruction, expunge_all(-EIRDM) changes r_msg. 981 * Prior to destruction, expunge_all(-EIRDM) changes r_msg.
978 * Thus if r_msg is -EAGAIN, then the queue not yet destroyed. 982 * Thus if r_msg is -EAGAIN, then the queue not yet destroyed.
979 * rcu_read_lock() prevents preemption between reading r_msg 983 * rcu_read_lock() prevents preemption between reading r_msg
980 * and the spin_lock() inside ipc_lock_by_ptr(). 984 * and acquiring the q_perm.lock in ipc_lock_object().
981 */ 985 */
982 rcu_read_lock(); 986 rcu_read_lock();
983 987
@@ -996,32 +1000,34 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
996 * If there is a message or an error then accept it without 1000 * If there is a message or an error then accept it without
997 * locking. 1001 * locking.
998 */ 1002 */
999 if (msg != ERR_PTR(-EAGAIN)) { 1003 if (msg != ERR_PTR(-EAGAIN))
1000 rcu_read_unlock(); 1004 goto out_unlock1;
1001 break;
1002 }
1003 1005
1004 /* Lockless receive, part 3: 1006 /* Lockless receive, part 3:
1005 * Acquire the queue spinlock. 1007 * Acquire the queue spinlock.
1006 */ 1008 */
1007 ipc_lock_by_ptr(&msq->q_perm); 1009 ipc_lock_object(&msq->q_perm);
1008 rcu_read_unlock();
1009 1010
1010 /* Lockless receive, part 4: 1011 /* Lockless receive, part 4:
1011 * Repeat test after acquiring the spinlock. 1012 * Repeat test after acquiring the spinlock.
1012 */ 1013 */
1013 msg = (struct msg_msg*)msr_d.r_msg; 1014 msg = (struct msg_msg*)msr_d.r_msg;
1014 if (msg != ERR_PTR(-EAGAIN)) 1015 if (msg != ERR_PTR(-EAGAIN))
1015 goto out_unlock; 1016 goto out_unlock0;
1016 1017
1017 list_del(&msr_d.r_list); 1018 list_del(&msr_d.r_list);
1018 if (signal_pending(current)) { 1019 if (signal_pending(current)) {
1019 msg = ERR_PTR(-ERESTARTNOHAND); 1020 msg = ERR_PTR(-ERESTARTNOHAND);
1020out_unlock: 1021 goto out_unlock0;
1021 msg_unlock(msq);
1022 break;
1023 } 1022 }
1023
1024 ipc_unlock_object(&msq->q_perm);
1024 } 1025 }
1026
1027out_unlock0:
1028 ipc_unlock_object(&msq->q_perm);
1029out_unlock1:
1030 rcu_read_unlock();
1025 if (IS_ERR(msg)) { 1031 if (IS_ERR(msg)) {
1026 free_copy(copy); 1032 free_copy(copy);
1027 return PTR_ERR(msg); 1033 return PTR_ERR(msg);