xref: /openbsd-src/sys/kern/subr_percpu.c (revision 3374c67d44f9b75b98444cbf63020f777792342e)
1 /*	$OpenBSD: subr_percpu.c,v 1.10 2022/10/03 14:10:53 bluhm Exp $ */
2 
3 /*
4  * Copyright (c) 2016 David Gwynne <dlg@openbsd.org>
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/pool.h>
22 #include <sys/malloc.h>
23 
24 #include <sys/percpu.h>
25 
26 #ifdef MULTIPROCESSOR
27 struct pool cpumem_pl;
28 
29 void
30 percpu_init(void)
31 {
32 	pool_init(&cpumem_pl, sizeof(struct cpumem) * ncpusfound, 0,
33 	    IPL_NONE, PR_WAITOK, "percpumem", &pool_allocator_single);
34 }
35 
36 struct cpumem *
37 cpumem_get(struct pool *pp)
38 {
39 	struct cpumem *cm;
40 	unsigned int cpu;
41 
42 	cm = pool_get(&cpumem_pl, PR_WAITOK);
43 
44 	for (cpu = 0; cpu < ncpusfound; cpu++)
45 		cm[cpu].mem = pool_get(pp, PR_WAITOK | PR_ZERO);
46 
47 	return (cm);
48 }
49 
50 void
51 cpumem_put(struct pool *pp, struct cpumem *cm)
52 {
53 	unsigned int cpu;
54 
55 	for (cpu = 0; cpu < ncpusfound; cpu++)
56 		pool_put(pp, cm[cpu].mem);
57 
58 	pool_put(&cpumem_pl, cm);
59 }
60 
61 struct cpumem *
62 cpumem_malloc(size_t sz, int type)
63 {
64 	struct cpumem *cm;
65 	unsigned int cpu;
66 
67 	sz = roundup(sz, CACHELINESIZE);
68 
69 	cm = pool_get(&cpumem_pl, PR_WAITOK);
70 
71 	for (cpu = 0; cpu < ncpusfound; cpu++)
72 		cm[cpu].mem = malloc(sz, type, M_WAITOK | M_ZERO);
73 
74 	return (cm);
75 }
76 
77 struct cpumem *
78 cpumem_malloc_ncpus(struct cpumem *bootcm, size_t sz, int type)
79 {
80 	struct cpumem *cm;
81 	unsigned int cpu;
82 
83 	sz = roundup(sz, CACHELINESIZE);
84 
85 	cm = pool_get(&cpumem_pl, PR_WAITOK);
86 
87 	cm[0].mem = bootcm[0].mem;
88 	for (cpu = 1; cpu < ncpusfound; cpu++)
89 		cm[cpu].mem = malloc(sz, type, M_WAITOK | M_ZERO);
90 
91 	return (cm);
92 }
93 
94 void
95 cpumem_free(struct cpumem *cm, int type, size_t sz)
96 {
97 	unsigned int cpu;
98 
99 	sz = roundup(sz, CACHELINESIZE);
100 
101 	for (cpu = 0; cpu < ncpusfound; cpu++)
102 		free(cm[cpu].mem, type, sz);
103 
104 	pool_put(&cpumem_pl, cm);
105 }
106 
107 void *
108 cpumem_first(struct cpumem_iter *i, struct cpumem *cm)
109 {
110 	i->cpu = 0;
111 
112 	return (cm[0].mem);
113 }
114 
115 void *
116 cpumem_next(struct cpumem_iter *i, struct cpumem *cm)
117 {
118 	unsigned int cpu = ++i->cpu;
119 
120 	if (cpu >= ncpusfound)
121 		return (NULL);
122 
123 	return (cm[cpu].mem);
124 }
125 
126 struct cpumem *
127 counters_alloc(unsigned int n)
128 {
129 	struct cpumem *cm;
130 	struct cpumem_iter cmi;
131 	uint64_t *counters;
132 	unsigned int i;
133 
134 	KASSERT(n > 0);
135 
136 	n++; /* add space for a generation number */
137 	cm = cpumem_malloc(n * sizeof(uint64_t), M_COUNTERS);
138 
139 	CPUMEM_FOREACH(counters, &cmi, cm) {
140 		for (i = 0; i < n; i++)
141 			counters[i] = 0;
142 	}
143 
144 	return (cm);
145 }
146 
147 struct cpumem *
148 counters_alloc_ncpus(struct cpumem *cm, unsigned int n)
149 {
150 	n++; /* the generation number */
151 	return (cpumem_malloc_ncpus(cm, n * sizeof(uint64_t), M_COUNTERS));
152 }
153 
154 void
155 counters_free(struct cpumem *cm, unsigned int n)
156 {
157 	n++; /* generation number */
158 	cpumem_free(cm, M_COUNTERS, n * sizeof(uint64_t));
159 }
160 
161 void
162 counters_read(struct cpumem *cm, uint64_t *output, unsigned int n)
163 {
164 	struct cpumem_iter cmi;
165 	uint64_t *gen, *counters, *temp;
166 	uint64_t enter, leave;
167 	unsigned int i;
168 
169 	for (i = 0; i < n; i++)
170 		output[i] = 0;
171 
172 	temp = mallocarray(n, sizeof(uint64_t), M_TEMP, M_WAITOK);
173 
174 	gen = cpumem_first(&cmi, cm);
175 	do {
176 		counters = gen + 1;
177 
178 		enter = *gen;
179 		for (;;) {
180 			/* the generation number is odd during an update */
181 			while (enter & 1) {
182 				yield();
183 				enter = *gen;
184 			}
185 
186 			membar_consumer();
187 			for (i = 0; i < n; i++)
188 				temp[i] = counters[i];
189 
190 			membar_consumer();
191 			leave = *gen;
192 
193 			if (enter == leave)
194 				break;
195 
196 			enter = leave;
197 		}
198 
199 		for (i = 0; i < n; i++)
200 			output[i] += temp[i];
201 
202 		gen = cpumem_next(&cmi, cm);
203 	} while (gen != NULL);
204 
205 	free(temp, M_TEMP, n * sizeof(uint64_t));
206 }
207 
208 void
209 counters_zero(struct cpumem *cm, unsigned int n)
210 {
211 	struct cpumem_iter cmi;
212 	uint64_t *counters;
213 	unsigned int i;
214 
215 	counters = cpumem_first(&cmi, cm);
216 	membar_producer();
217 	do {
218 		for (i = 0; i < n; i++)
219 			counters[i] = 0;
220 		/* zero the generation numbers too */
221 		membar_producer();
222 		counters[i] = 0;
223 
224 		counters = cpumem_next(&cmi, cm);
225 	} while (counters != NULL);
226 }
227 
228 #else /* MULTIPROCESSOR */
229 
230 /*
231  * Uniprocessor implementation of per-CPU data structures.
232  *
233  * UP percpu memory is a single memory allocation cast to/from the
234  * cpumem struct. It is not scaled up to the size of cacheline because
235  * there's no other cache to contend with.
236  */
237 
238 void
239 percpu_init(void)
240 {
241 	/* nop */
242 }
243 
244 struct cpumem *
245 cpumem_get(struct pool *pp)
246 {
247 	return (pool_get(pp, PR_WAITOK | PR_ZERO));
248 }
249 
250 void
251 cpumem_put(struct pool *pp, struct cpumem *cm)
252 {
253 	pool_put(pp, cm);
254 }
255 
256 struct cpumem *
257 cpumem_malloc(size_t sz, int type)
258 {
259 	return (malloc(sz, type, M_WAITOK | M_ZERO));
260 }
261 
262 struct cpumem *
263 cpumem_malloc_ncpus(struct cpumem *cm, size_t sz, int type)
264 {
265 	return (cm);
266 }
267 
268 void
269 cpumem_free(struct cpumem *cm, int type, size_t sz)
270 {
271 	free(cm, type, sz);
272 }
273 
274 void *
275 cpumem_first(struct cpumem_iter *i, struct cpumem *cm)
276 {
277 	return (cm);
278 }
279 
280 void *
281 cpumem_next(struct cpumem_iter *i, struct cpumem *cm)
282 {
283 	return (NULL);
284 }
285 
286 struct cpumem *
287 counters_alloc(unsigned int n)
288 {
289 	KASSERT(n > 0);
290 
291 	return (cpumem_malloc(n * sizeof(uint64_t), M_COUNTERS));
292 }
293 
294 struct cpumem *
295 counters_alloc_ncpus(struct cpumem *cm, unsigned int n)
296 {
297 	/* this is unnecessary, but symmetrical */
298 	return (cpumem_malloc_ncpus(cm, n * sizeof(uint64_t), M_COUNTERS));
299 }
300 
301 void
302 counters_free(struct cpumem *cm, unsigned int n)
303 {
304 	cpumem_free(cm, M_COUNTERS, n * sizeof(uint64_t));
305 }
306 
307 void
308 counters_read(struct cpumem *cm, uint64_t *output, unsigned int n)
309 {
310 	uint64_t *counters;
311 	unsigned int i;
312 	int s;
313 
314 	counters = (uint64_t *)cm;
315 
316 	s = splhigh();
317 	for (i = 0; i < n; i++)
318 		output[i] = counters[i];
319 	splx(s);
320 }
321 
322 void
323 counters_zero(struct cpumem *cm, unsigned int n)
324 {
325 	uint64_t *counters;
326 	unsigned int i;
327 	int s;
328 
329 	counters = (uint64_t *)cm;
330 
331 	s = splhigh();
332 	for (i = 0; i < n; i++)
333 		counters[i] = 0;
334 	splx(s);
335 }
336 
337 #endif /* MULTIPROCESSOR */
338