aboutsummaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>2016-10-11 16:54:53 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-10-11 18:06:33 -0400
commitee51636ca54f9d4d01ae49b1740742e9db54d868 (patch)
tree628ce5c597d32f0848ebb5730b642806d1d9a057 /ipc
parent5864a2fd3088db73d47942370d0f7210a807b9bc (diff)
ipc/msg: implement lockless pipelined wakeups
This patch moves the wakeup_process() invocation so it is not done under the ipc global lock by making use of a lockless wake_q. With this change, the waiter is woken up once the message has been assigned and it does not need to loop on SMP if the message points to NULL. In the signal case we still need to check the pointer under the lock to verify the state. This change should also avoid the introduction of preempt_disable() in -RT which avoids a busy-loop which pools for the NULL -> !NULL change if the waiter has a higher priority compared to the waker. By making use of wake_qs, the logic of sysv msg queues is greatly simplified (and very well suited as we can batch lockless wakeups), particularly around the lockless receive algorithm. This has been tested with Manred's pmsg-shared tool on a "AMD A10-7800 Radeon R7, 12 Compute Cores 4C+8G": test | before | after | diff -----------------|------------|------------|---------- pmsg-shared 8 60 | 19,347,422 | 30,442,191 | + ~57.34 % pmsg-shared 4 60 | 21,367,197 | 35,743,458 | + ~67.28 % pmsg-shared 2 60 | 22,884,224 | 24,278,200 | + ~6.09 % Link: http://lkml.kernel.org/r/1469748819-19484-2-git-send-email-dave@stgolabs.net Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Signed-off-by: Davidlohr Bueso <dbueso@suse.de> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Manfred Spraul <manfred@colorfullife.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.c133
1 files changed, 40 insertions, 93 deletions
diff --git a/ipc/msg.c b/ipc/msg.c
index c6521c205cb4..b1fb06a6a75b 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -51,13 +51,7 @@ struct msg_receiver {
51 long r_msgtype; 51 long r_msgtype;
52 long r_maxsize; 52 long r_maxsize;
53 53
54 /* 54 struct msg_msg *r_msg;
55 * Mark r_msg volatile so that the compiler
56 * does not try to get smart and optimize
57 * it. We rely on this for the lockless
58 * receive algorithm.
59 */
60 struct msg_msg *volatile r_msg;
61}; 55};
62 56
63/* one msg_sender for each sleeping sender */ 57/* one msg_sender for each sleeping sender */
@@ -183,21 +177,14 @@ static void ss_wakeup(struct list_head *h, int kill)
183 } 177 }
184} 178}
185 179
186static void expunge_all(struct msg_queue *msq, int res) 180static void expunge_all(struct msg_queue *msq, int res,
181 struct wake_q_head *wake_q)
187{ 182{
188 struct msg_receiver *msr, *t; 183 struct msg_receiver *msr, *t;
189 184
190 list_for_each_entry_safe(msr, t, &msq->q_receivers, r_list) { 185 list_for_each_entry_safe(msr, t, &msq->q_receivers, r_list) {
191 msr->r_msg = NULL; /* initialize expunge ordering */ 186 wake_q_add(wake_q, msr->r_tsk);
192 wake_up_process(msr->r_tsk); 187 WRITE_ONCE(msr->r_msg, ERR_PTR(res));
193 /*
194 * Ensure that the wakeup is visible before setting r_msg as
195 * the receiving end depends on it: either spinning on a nil,
196 * or dealing with -EAGAIN cases. See lockless receive part 1
197 * and 2 in do_msgrcv().
198 */
199 smp_wmb(); /* barrier (B) */
200 msr->r_msg = ERR_PTR(res);
201 } 188 }
202} 189}
203 190
@@ -213,11 +200,13 @@ static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
213{ 200{
214 struct msg_msg *msg, *t; 201 struct msg_msg *msg, *t;
215 struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm); 202 struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm);
203 WAKE_Q(wake_q);
216 204
217 expunge_all(msq, -EIDRM); 205 expunge_all(msq, -EIDRM, &wake_q);
218 ss_wakeup(&msq->q_senders, 1); 206 ss_wakeup(&msq->q_senders, 1);
219 msg_rmid(ns, msq); 207 msg_rmid(ns, msq);
220 ipc_unlock_object(&msq->q_perm); 208 ipc_unlock_object(&msq->q_perm);
209 wake_up_q(&wake_q);
221 rcu_read_unlock(); 210 rcu_read_unlock();
222 211
223 list_for_each_entry_safe(msg, t, &msq->q_messages, m_list) { 212 list_for_each_entry_safe(msg, t, &msq->q_messages, m_list) {
@@ -342,6 +331,7 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
342 struct kern_ipc_perm *ipcp; 331 struct kern_ipc_perm *ipcp;
343 struct msqid64_ds uninitialized_var(msqid64); 332 struct msqid64_ds uninitialized_var(msqid64);
344 struct msg_queue *msq; 333 struct msg_queue *msq;
334 WAKE_Q(wake_q);
345 int err; 335 int err;
346 336
347 if (cmd == IPC_SET) { 337 if (cmd == IPC_SET) {
@@ -389,7 +379,7 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
389 /* sleeping receivers might be excluded by 379 /* sleeping receivers might be excluded by
390 * stricter permissions. 380 * stricter permissions.
391 */ 381 */
392 expunge_all(msq, -EAGAIN); 382 expunge_all(msq, -EAGAIN, &wake_q);
393 /* sleeping senders might be able to send 383 /* sleeping senders might be able to send
394 * due to a larger queue size. 384 * due to a larger queue size.
395 */ 385 */
@@ -402,6 +392,7 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
402 392
403out_unlock0: 393out_unlock0:
404 ipc_unlock_object(&msq->q_perm); 394 ipc_unlock_object(&msq->q_perm);
395 wake_up_q(&wake_q);
405out_unlock1: 396out_unlock1:
406 rcu_read_unlock(); 397 rcu_read_unlock();
407out_up: 398out_up:
@@ -566,7 +557,8 @@ static int testmsg(struct msg_msg *msg, long type, int mode)
566 return 0; 557 return 0;
567} 558}
568 559
569static inline int pipelined_send(struct msg_queue *msq, struct msg_msg *msg) 560static inline int pipelined_send(struct msg_queue *msq, struct msg_msg *msg,
561 struct wake_q_head *wake_q)
570{ 562{
571 struct msg_receiver *msr, *t; 563 struct msg_receiver *msr, *t;
572 564
@@ -577,27 +569,14 @@ static inline int pipelined_send(struct msg_queue *msq, struct msg_msg *msg)
577 569
578 list_del(&msr->r_list); 570 list_del(&msr->r_list);
579 if (msr->r_maxsize < msg->m_ts) { 571 if (msr->r_maxsize < msg->m_ts) {
580 /* initialize pipelined send ordering */ 572 wake_q_add(wake_q, msr->r_tsk);
581 msr->r_msg = NULL; 573 WRITE_ONCE(msr->r_msg, ERR_PTR(-E2BIG));
582 wake_up_process(msr->r_tsk);
583 /* barrier (B) see barrier comment below */
584 smp_wmb();
585 msr->r_msg = ERR_PTR(-E2BIG);
586 } else { 574 } else {
587 msr->r_msg = NULL;
588 msq->q_lrpid = task_pid_vnr(msr->r_tsk); 575 msq->q_lrpid = task_pid_vnr(msr->r_tsk);
589 msq->q_rtime = get_seconds(); 576 msq->q_rtime = get_seconds();
590 wake_up_process(msr->r_tsk);
591 /*
592 * Ensure that the wakeup is visible before
593 * setting r_msg, as the receiving can otherwise
594 * exit - once r_msg is set, the receiver can
595 * continue. See lockless receive part 1 and 2
596 * in do_msgrcv(). Barrier (B).
597 */
598 smp_wmb();
599 msr->r_msg = msg;
600 577
578 wake_q_add(wake_q, msr->r_tsk);
579 WRITE_ONCE(msr->r_msg, msg);
601 return 1; 580 return 1;
602 } 581 }
603 } 582 }
@@ -613,6 +592,7 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
613 struct msg_msg *msg; 592 struct msg_msg *msg;
614 int err; 593 int err;
615 struct ipc_namespace *ns; 594 struct ipc_namespace *ns;
595 WAKE_Q(wake_q);
616 596
617 ns = current->nsproxy->ipc_ns; 597 ns = current->nsproxy->ipc_ns;
618 598
@@ -686,7 +666,6 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
686 err = -EIDRM; 666 err = -EIDRM;
687 goto out_unlock0; 667 goto out_unlock0;
688 } 668 }
689
690 ss_del(&s); 669 ss_del(&s);
691 670
692 if (signal_pending(current)) { 671 if (signal_pending(current)) {
@@ -698,7 +677,7 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
698 msq->q_lspid = task_tgid_vnr(current); 677 msq->q_lspid = task_tgid_vnr(current);
699 msq->q_stime = get_seconds(); 678 msq->q_stime = get_seconds();
700 679
701 if (!pipelined_send(msq, msg)) { 680 if (!pipelined_send(msq, msg, &wake_q)) {
702 /* no one is waiting for this message, enqueue it */ 681 /* no one is waiting for this message, enqueue it */
703 list_add_tail(&msg->m_list, &msq->q_messages); 682 list_add_tail(&msg->m_list, &msq->q_messages);
704 msq->q_cbytes += msgsz; 683 msq->q_cbytes += msgsz;
@@ -712,6 +691,7 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
712 691
713out_unlock0: 692out_unlock0:
714 ipc_unlock_object(&msq->q_perm); 693 ipc_unlock_object(&msq->q_perm);
694 wake_up_q(&wake_q);
715out_unlock1: 695out_unlock1:
716 rcu_read_unlock(); 696 rcu_read_unlock();
717 if (msg != NULL) 697 if (msg != NULL)
@@ -919,71 +899,38 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl
919 rcu_read_unlock(); 899 rcu_read_unlock();
920 schedule(); 900 schedule();
921 901
922 /* Lockless receive, part 1: 902 /*
923 * Disable preemption. We don't hold a reference to the queue 903 * Lockless receive, part 1:
924 * and getting a reference would defeat the idea of a lockless 904 * We don't hold a reference to the queue and getting a
925 * operation, thus the code relies on rcu to guarantee the 905 * reference would defeat the idea of a lockless operation,
926 * existence of msq: 906 * thus the code relies on rcu to guarantee the existence of
907 * msq:
927 * Prior to destruction, expunge_all(-EIRDM) changes r_msg. 908 * Prior to destruction, expunge_all(-EIRDM) changes r_msg.
928 * Thus if r_msg is -EAGAIN, then the queue not yet destroyed. 909 * Thus if r_msg is -EAGAIN, then the queue not yet destroyed.
929 * rcu_read_lock() prevents preemption between reading r_msg
930 * and acquiring the q_perm.lock in ipc_lock_object().
931 */ 910 */
932 rcu_read_lock(); 911 rcu_read_lock();
933 912