diff options
author | Davidlohr Bueso <davidlohr.bueso@hp.com> | 2013-07-08 19:01:18 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-09 13:33:27 -0400 |
commit | 41a0d523d0f626e9da0dc01de47f1b89058033cf (patch) | |
tree | af4021f97c49c59784dbab62fb1dad36fe6d6f3c | |
parent | 3dd1f784ed6603d7ab1043e51e6371235edf2313 (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>
-rw-r--r-- | ipc/msg.c | 58 |
1 files changed, 32 insertions, 26 deletions
@@ -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 | 888 | long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg, | |
889 | long 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); |
1020 | out_unlock: | 1021 | goto out_unlock0; |
1021 | msg_unlock(msq); | ||
1022 | break; | ||
1023 | } | 1022 | } |
1023 | |||
1024 | ipc_unlock_object(&msq->q_perm); | ||
1024 | } | 1025 | } |
1026 | |||
1027 | out_unlock0: | ||
1028 | ipc_unlock_object(&msq->q_perm); | ||
1029 | out_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); |