xref: /netbsd-src/sys/kern/subr_ipi.c (revision 19ef5b5b0bcb90f63509df6e78769de1b57c2758)
1 /*	$NetBSD: subr_ipi.c,v 1.1 2014/05/19 22:47:54 rmind Exp $	*/
2 
3 /*-
4  * Copyright (c) 2014 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Mindaugas Rasiukevicius.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Inter-processor interrupt (IPI) interface with cross-call support.
34  */
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: subr_ipi.c,v 1.1 2014/05/19 22:47:54 rmind Exp $");
38 
39 #include <sys/param.h>
40 #include <sys/types.h>
41 
42 #include <sys/atomic.h>
43 #include <sys/evcnt.h>
44 #include <sys/cpu.h>
45 #include <sys/ipi.h>
46 #include <sys/kcpuset.h>
47 #include <sys/kmem.h>
48 #include <sys/lock.h>
49 
50 /*
51  * Per-CPU mailbox for IPI messages: it is a single cache line storing
52  * up to IPI_MSG_MAX messages.
53  */
54 
55 #define	IPI_MSG_SLOTS	(CACHE_LINE_SIZE / sizeof(ipi_msg_t *))
56 #define	IPI_MSG_MAX	IPI_MSG_SLOTS
57 
58 typedef struct {
59 	ipi_msg_t *	msg[IPI_MSG_SLOTS];
60 } ipi_mbox_t;
61 
62 static ipi_mbox_t *	ipi_mboxes	__read_mostly;
63 static struct evcnt	ipi_mboxfull_ev	__cacheline_aligned;
64 
65 #ifndef MULTIPROCESSOR
66 #define	cpu_ipi(ci)	KASSERT(ci == NULL)
67 #endif
68 
69 void
70 ipi_sysinit(void)
71 {
72 	const size_t len = ncpu * sizeof(ipi_mbox_t);
73 
74 	/* Allocate per-CPU IPI mailboxes. */
75 	ipi_mboxes = kmem_zalloc(len, KM_SLEEP);
76 	KASSERT(ipi_mboxes != NULL);
77 
78 	evcnt_attach_dynamic(&ipi_mboxfull_ev, EVCNT_TYPE_MISC, NULL,
79 	   "ipi", "full");
80 }
81 
82 /*
83  * put_msg: insert message into the mailbox.
84  */
85 static inline void
86 put_msg(ipi_mbox_t *mbox, ipi_msg_t *msg)
87 {
88 	int count = SPINLOCK_BACKOFF_MIN;
89 again:
90 	for (u_int i = 0; i < IPI_MSG_MAX; i++) {
91 		if (__predict_true(mbox->msg[i] == NULL) &&
92 		    atomic_cas_ptr(&mbox->msg[i], NULL, msg) == NULL) {
93 			return;
94 		}
95 	}
96 
97 	/* All slots are full: we have to spin-wait. */
98 	ipi_mboxfull_ev.ev_count++;
99 	SPINLOCK_BACKOFF(count);
100 	goto again;
101 }
102 
103 /*
104  * ipi_cpu_handler: the IPI handler.
105  */
106 void
107 ipi_cpu_handler(void)
108 {
109 	const struct cpu_info * const ci = curcpu();
110 	ipi_mbox_t *mbox = &ipi_mboxes[cpu_index(ci)];
111 
112 	KASSERT(curcpu() == ci);
113 
114 	for (u_int i = 0; i < IPI_MSG_MAX; i++) {
115 		ipi_msg_t *msg;
116 
117 		/* Get the message. */
118 		if ((msg = mbox->msg[i]) == NULL) {
119 			continue;
120 		}
121 		mbox->msg[i] = NULL;
122 
123 		/* Execute the handler. */
124 		KASSERT(msg->func);
125 		msg->func(msg->arg);
126 
127 		/* Ack the request. */
128 		atomic_dec_uint(&msg->_pending);
129 	}
130 }
131 
132 /*
133  * ipi_unicast: send an IPI to a single CPU.
134  *
135  * => The CPU must be remote; must not be local.
136  * => The caller must ipi_wait() on the message for completion.
137  */
138 void
139 ipi_unicast(ipi_msg_t *msg, struct cpu_info *ci)
140 {
141 	const cpuid_t id = cpu_index(ci);
142 
143 	KASSERT(msg->func != NULL);
144 	KASSERT(kpreempt_disabled());
145 	KASSERT(curcpu() != ci);
146 
147 	msg->_pending = 1;
148 	membar_producer();
149 
150 	put_msg(&ipi_mboxes[id], msg);
151 	cpu_ipi(ci);
152 }
153 
154 /*
155  * ipi_multicast: send an IPI to each CPU in the specified set.
156  *
157  * => The caller must ipi_wait() on the message for completion.
158  */
159 void
160 ipi_multicast(ipi_msg_t *msg, const kcpuset_t *target)
161 {
162 	const struct cpu_info * const self = curcpu();
163 	CPU_INFO_ITERATOR cii;
164 	struct cpu_info *ci;
165 	u_int local;
166 
167 	KASSERT(msg->func != NULL);
168 	KASSERT(kpreempt_disabled());
169 
170 	local = !!kcpuset_isset(target, cpu_index(self));
171 	msg->_pending = kcpuset_countset(target) - local;
172 	membar_producer();
173 
174 	for (CPU_INFO_FOREACH(cii, ci)) {
175 		cpuid_t id;
176 
177 		if (__predict_false(ci == self)) {
178 			continue;
179 		}
180 		id = cpu_index(ci);
181 		if (!kcpuset_isset(target, id)) {
182 			continue;
183 		}
184 		put_msg(&ipi_mboxes[id], msg);
185 		cpu_ipi(ci);
186 	}
187 	if (local) {
188 		msg->func(msg->arg);
189 	}
190 }
191 
192 /*
193  * ipi_broadcast: send an IPI to all CPUs.
194  *
195  * => The caller must ipi_wait() on the message for completion.
196  */
197 void
198 ipi_broadcast(ipi_msg_t *msg)
199 {
200 	const struct cpu_info * const self = curcpu();
201 	CPU_INFO_ITERATOR cii;
202 	struct cpu_info *ci;
203 
204 	KASSERT(msg->func != NULL);
205 	KASSERT(kpreempt_disabled());
206 
207 	msg->_pending = ncpu - 1;
208 	membar_producer();
209 
210 	/* Broadcast IPIs for remote CPUs. */
211 	for (CPU_INFO_FOREACH(cii, ci)) {
212 		cpuid_t id;
213 
214 		if (__predict_false(ci == self)) {
215 			continue;
216 		}
217 		id = cpu_index(ci);
218 		put_msg(&ipi_mboxes[id], msg);
219 	}
220 	cpu_ipi(NULL);
221 
222 	/* Finally, execute locally. */
223 	msg->func(msg->arg);
224 }
225 
226 /*
227  * ipi_wait: spin-wait until the message is processed.
228  */
229 void
230 ipi_wait(ipi_msg_t *msg)
231 {
232 	int count = SPINLOCK_BACKOFF_MIN;
233 
234 	while (msg->_pending) {
235 		KASSERT(msg->_pending < ncpu);
236 		SPINLOCK_BACKOFF(count);
237 	}
238 }
239