xref: /openbsd-src/sys/kern/kern_smr.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /*	$OpenBSD: kern_smr.c,v 1.7 2020/02/25 16:55:33 visa Exp $	*/
2 
3 /*
4  * Copyright (c) 2019 Visa Hankala
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/kernel.h>
22 #include <sys/kthread.h>
23 #include <sys/mutex.h>
24 #include <sys/percpu.h>
25 #include <sys/proc.h>
26 #include <sys/smr.h>
27 #include <sys/time.h>
28 #include <sys/witness.h>
29 
30 #include <machine/cpu.h>
31 
32 #define SMR_PAUSE	100		/* pause between rounds in msec */
33 
34 void	smr_dispatch(struct schedstate_percpu *);
35 void	smr_grace_wait(void);
36 void	smr_thread(void *);
37 void	smr_wakeup(void *);
38 
39 struct mutex		smr_lock = MUTEX_INITIALIZER(IPL_HIGH);
40 struct smr_entry_list	smr_deferred;
41 struct timeout		smr_wakeup_tmo;
42 unsigned int		smr_expedite;
43 unsigned int		smr_ndeferred;
44 
45 #ifdef WITNESS
46 static const char smr_lock_name[] = "smr";
47 struct lock_object smr_lock_obj = {
48 	.lo_name = smr_lock_name,
49 	.lo_flags = LO_WITNESS | LO_INITIALIZED | LO_SLEEPABLE |
50 	    (LO_CLASS_RWLOCK << LO_CLASSSHIFT)
51 };
52 struct lock_type smr_lock_type = {
53 	.lt_name = smr_lock_name
54 };
55 #endif
56 
57 static inline int
58 smr_cpu_is_idle(struct cpu_info *ci)
59 {
60 	return ci->ci_curproc == ci->ci_schedstate.spc_idleproc;
61 }
62 
63 void
64 smr_startup(void)
65 {
66 	SIMPLEQ_INIT(&smr_deferred);
67 	WITNESS_INIT(&smr_lock_obj, &smr_lock_type);
68 	timeout_set(&smr_wakeup_tmo, smr_wakeup, NULL);
69 }
70 
71 void
72 smr_startup_thread(void)
73 {
74 	if (kthread_create(smr_thread, NULL, NULL, "smr") != 0)
75 		panic("could not create smr thread");
76 }
77 
78 struct timeval smr_logintvl = { 300, 0 };
79 
80 void
81 smr_thread(void *arg)
82 {
83 	struct timeval elapsed, end, loglast, start;
84 	struct smr_entry_list deferred;
85 	struct smr_entry *smr;
86 
87 	KERNEL_ASSERT_LOCKED();
88 	KERNEL_UNLOCK();
89 
90 	memset(&loglast, 0, sizeof(loglast));
91 	SIMPLEQ_INIT(&deferred);
92 
93 	for (;;) {
94 		mtx_enter(&smr_lock);
95 		if (smr_ndeferred == 0) {
96 			while (smr_ndeferred == 0)
97 				msleep_nsec(&smr_ndeferred, &smr_lock, PVM,
98 				    "bored", INFSLP);
99 		} else {
100 			if (smr_expedite == 0)
101 				msleep_nsec(&smr_ndeferred, &smr_lock, PVM,
102 				    "pause", MSEC_TO_NSEC(SMR_PAUSE));
103 		}
104 
105 		SIMPLEQ_CONCAT(&deferred, &smr_deferred);
106 		smr_ndeferred = 0;
107 		smr_expedite = 0;
108 		mtx_leave(&smr_lock);
109 
110 		getmicrouptime(&start);
111 
112 		smr_grace_wait();
113 
114 		WITNESS_CHECKORDER(&smr_lock_obj, LOP_NEWORDER, NULL);
115 		WITNESS_LOCK(&smr_lock_obj, 0);
116 
117 		while ((smr = SIMPLEQ_FIRST(&deferred)) != NULL) {
118 			SIMPLEQ_REMOVE_HEAD(&deferred, smr_list);
119 			smr->smr_func(smr->smr_arg);
120 		}
121 
122 		WITNESS_UNLOCK(&smr_lock_obj, 0);
123 
124 		getmicrouptime(&end);
125 		timersub(&end, &start, &elapsed);
126 		if (elapsed.tv_sec >= 5 &&
127 		    ratecheck(&loglast, &smr_logintvl))
128 			printf("smr: dispatch took %ld seconds\n",
129 			    (long)elapsed.tv_sec);
130 	}
131 }
132 
133 /*
134  * Block until all CPUs have crossed quiescent state.
135  */
136 void
137 smr_grace_wait(void)
138 {
139 #ifdef MULTIPROCESSOR
140 	CPU_INFO_ITERATOR cii;
141 	struct cpu_info *ci, *ci_start;
142 
143 	ci_start = curcpu();
144 	CPU_INFO_FOREACH(cii, ci) {
145 		if (ci == ci_start)
146 			continue;
147 		sched_peg_curproc(ci);
148 	}
149 	atomic_clearbits_int(&curproc->p_flag, P_CPUPEG);
150 #endif /* MULTIPROCESSOR */
151 }
152 
153 void
154 smr_wakeup(void *arg)
155 {
156 	wakeup(&smr_ndeferred);
157 }
158 
159 void
160 smr_read_enter(void)
161 {
162 #ifdef DIAGNOSTIC
163 	struct schedstate_percpu *spc = &curcpu()->ci_schedstate;
164 
165 	spc->spc_smrdepth++;
166 #endif
167 }
168 
169 void
170 smr_read_leave(void)
171 {
172 #ifdef DIAGNOSTIC
173 	struct schedstate_percpu *spc = &curcpu()->ci_schedstate;
174 
175 	KASSERT(spc->spc_smrdepth > 0);
176 	spc->spc_smrdepth--;
177 #endif
178 }
179 
180 /*
181  * Move SMR entries from the local queue to the system-wide queue.
182  */
183 void
184 smr_dispatch(struct schedstate_percpu *spc)
185 {
186 	int expedite = 0, wake = 0;
187 
188 	mtx_enter(&smr_lock);
189 	if (smr_ndeferred == 0)
190 		wake = 1;
191 	SIMPLEQ_CONCAT(&smr_deferred, &spc->spc_deferred);
192 	smr_ndeferred += spc->spc_ndeferred;
193 	spc->spc_ndeferred = 0;
194 	smr_expedite |= spc->spc_smrexpedite;
195 	spc->spc_smrexpedite = 0;
196 	expedite = smr_expedite;
197 	mtx_leave(&smr_lock);
198 
199 	if (expedite)
200 		wakeup(&smr_ndeferred);
201 	else if (wake)
202 		timeout_add_msec(&smr_wakeup_tmo, SMR_PAUSE);
203 }
204 
205 /*
206  * Signal that the current CPU is in quiescent state.
207  */
208 void
209 smr_idle(void)
210 {
211 	struct schedstate_percpu *spc = &curcpu()->ci_schedstate;
212 
213 	SMR_ASSERT_NONCRITICAL();
214 
215 	if (spc->spc_ndeferred > 0)
216 		smr_dispatch(spc);
217 }
218 
219 #ifdef DIAGNOSTIC
220 
221 void
222 smr_assert_critical(void)
223 {
224 	struct schedstate_percpu *spc = &curcpu()->ci_schedstate;
225 
226 	if (panicstr == NULL && !db_active)
227 		KASSERT(spc->spc_smrdepth > 0);
228 }
229 
230 void
231 smr_assert_noncritical(void)
232 {
233 	struct schedstate_percpu *spc = &curcpu()->ci_schedstate;
234 
235 	if (panicstr == NULL && !db_active)
236 		KASSERT(spc->spc_smrdepth == 0);
237 }
238 
239 #endif /* DIAGNOSTIC */
240 
241 void
242 smr_call_impl(struct smr_entry *smr, void (*func)(void *), void *arg,
243     int expedite)
244 {
245 	struct cpu_info *ci = curcpu();
246 	struct schedstate_percpu *spc = &ci->ci_schedstate;
247 	int s;
248 
249 	KASSERT(smr->smr_func == NULL);
250 
251 	smr->smr_func = func;
252 	smr->smr_arg = arg;
253 
254 	s = splhigh();
255 	SIMPLEQ_INSERT_TAIL(&spc->spc_deferred, smr, smr_list);
256 	spc->spc_ndeferred++;
257 	spc->spc_smrexpedite |= expedite;
258 	splx(s);
259 
260 	/*
261 	 * If this call was made from an interrupt context that
262 	 * preempted idle state, dispatch the local queue to the shared
263 	 * queue immediately.
264 	 * The entries would linger in the local queue long if the CPU
265 	 * went to sleep without calling smr_idle().
266 	 */
267 	if (smr_cpu_is_idle(ci))
268 		smr_dispatch(spc);
269 }
270 
271 void
272 smr_barrier_func(void *arg)
273 {
274 	struct cond *c = arg;
275 
276 	cond_signal(c);
277 }
278 
279 void
280 smr_barrier_impl(int expedite)
281 {
282 	struct cond c = COND_INITIALIZER();
283 	struct smr_entry smr;
284 
285 	if (panicstr != NULL || db_active)
286 		return;
287 
288 	WITNESS_CHECKORDER(&smr_lock_obj, LOP_NEWORDER, NULL);
289 
290 	smr_init(&smr);
291 	smr_call_impl(&smr, smr_barrier_func, &c, expedite);
292 	cond_wait(&c, "smrbar");
293 }
294