xref: /openbsd-src/sys/kern/subr_percpu.c (revision e05abaed709313136cac38f892a3127fab9855d9)
1 /*	$OpenBSD: subr_percpu.c,v 1.6 2017/01/11 17:46:28 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 #include <sys/types.h>
24 
25 #include <sys/percpu.h>
26 
27 #ifdef MULTIPROCESSOR
28 struct pool cpumem_pl;
29 
30 void
31 percpu_init(void)
32 {
33 	pool_init(&cpumem_pl, sizeof(struct cpumem) * ncpusfound, 0,
34 	    IPL_NONE, PR_WAITOK, "percpumem", &pool_allocator_single);
35 }
36 
37 struct cpumem *
38 cpumem_get(struct pool *pp)
39 {
40 	struct cpumem *cm;
41 	unsigned int cpu;
42 
43 	cm = pool_get(&cpumem_pl, PR_WAITOK);
44 
45 	for (cpu = 0; cpu < ncpusfound; cpu++)
46 		cm[cpu].mem = pool_get(pp, PR_WAITOK | PR_ZERO);
47 
48 	return (cm);
49 }
50 
51 void
52 cpumem_put(struct pool *pp, struct cpumem *cm)
53 {
54 	unsigned int cpu;
55 
56 	for (cpu = 0; cpu < ncpusfound; cpu++)
57 		pool_put(pp, cm[cpu].mem);
58 
59 	pool_put(&cpumem_pl, cm);
60 }
61 
62 struct cpumem *
63 cpumem_malloc(size_t sz, int type)
64 {
65 	struct cpumem *cm;
66 	unsigned int cpu;
67 
68 	sz = roundup(sz, CACHELINESIZE);
69 
70 	cm = pool_get(&cpumem_pl, PR_WAITOK);
71 
72 	for (cpu = 0; cpu < ncpusfound; cpu++)
73 		cm[cpu].mem = malloc(sz, type, M_WAITOK | M_ZERO);
74 
75 	return (cm);
76 }
77 
78 struct cpumem *
79 cpumem_malloc_ncpus(struct cpumem *bootcm, size_t sz, int type)
80 {
81 	struct cpumem *cm;
82 	unsigned int cpu;
83 
84 	sz = roundup(sz, CACHELINESIZE);
85 
86 	cm = pool_get(&cpumem_pl, PR_WAITOK);
87 
88 	cm[0].mem = bootcm[0].mem;
89 	for (cpu = 1; cpu < ncpusfound; cpu++)
90 		cm[cpu].mem = malloc(sz, type, M_WAITOK | M_ZERO);
91 
92 	return (cm);
93 }
94 
95 void
96 cpumem_free(struct cpumem *cm, int type, size_t sz)
97 {
98 	unsigned int cpu;
99 
100 	sz = roundup(sz, CACHELINESIZE);
101 
102 	for (cpu = 0; cpu < ncpusfound; cpu++)
103 		free(cm[cpu].mem, type, sz);
104 
105 	pool_put(&cpumem_pl, cm);
106 }
107 
108 void *
109 cpumem_first(struct cpumem_iter *i, struct cpumem *cm)
110 {
111 	i->cpu = 0;
112 
113 	return (cm[0].mem);
114 }
115 
116 void *
117 cpumem_next(struct cpumem_iter *i, struct cpumem *cm)
118 {
119 	unsigned int cpu = ++i->cpu;
120 
121 	if (cpu >= ncpusfound)
122 		return (NULL);
123 
124 	return (cm[cpu].mem);
125 }
126 
127 struct cpumem *
128 counters_alloc(unsigned int n, int type)
129 {
130 	struct cpumem *cm;
131 	struct cpumem_iter cmi;
132 	uint64_t *counters;
133 	unsigned int i;
134 
135 	KASSERT(n > 0);
136 
137 	n++; /* add space for a generation number */
138 	cm = cpumem_malloc(n * sizeof(uint64_t), type);
139 
140 	CPUMEM_FOREACH(counters, &cmi, cm) {
141 		for (i = 0; i < n; i++)
142 			counters[i] = 0;
143 	}
144 
145 	return (cm);
146 }
147 
148 struct cpumem *
149 counters_alloc_ncpus(struct cpumem *cm, unsigned int n, int type)
150 {
151 	n++; /* the generation number */
152 	return (cpumem_malloc_ncpus(cm, n * sizeof(uint64_t), type));
153 }
154 
155 void
156 counters_free(struct cpumem *cm, int type, unsigned int n)
157 {
158 	n++; /* generation number */
159 	cpumem_free(cm, type, n * sizeof(uint64_t));
160 }
161 
162 void
163 counters_read(struct cpumem *cm, uint64_t *output, unsigned int n)
164 {
165 	struct cpumem_iter cmi;
166 	uint64_t *gen, *counters, *temp;
167 	uint64_t enter, leave;
168 	unsigned int i;
169 
170 	for (i = 0; i < n; i++)
171 		output[i] = 0;
172 
173 	temp = mallocarray(n, sizeof(uint64_t), M_TEMP, M_WAITOK);
174 
175 	gen = cpumem_first(&cmi, cm);
176 	do {
177 		counters = gen + 1;
178 
179 		enter = *gen;
180 		for (;;) {
181 			/* the generation number is odd during an update */
182 			while (enter & 1) {
183 				yield();
184 				enter = *gen;
185 			}
186 
187 			membar_consumer();
188 			for (i = 0; i < n; i++)
189 				temp[i] = counters[i];
190 
191 			membar_consumer();
192 			leave = *gen;
193 
194 			if (enter == leave)
195 				break;
196 
197 			enter = leave;
198 		}
199 
200 		for (i = 0; i < n; i++)
201 			output[i] += temp[i];
202 
203 		gen = cpumem_next(&cmi, cm);
204 	} while (gen != NULL);
205 
206 	free(temp, M_TEMP, n * sizeof(uint64_t));
207 }
208 
209 void
210 counters_zero(struct cpumem *cm, unsigned int n)
211 {
212 	struct cpumem_iter cmi;
213 	uint64_t *counters;
214 	unsigned int i;
215 
216 	counters = cpumem_first(&cmi, cm);
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, int type)
288 {
289 	KASSERT(n > 0);
290 
291 	return (cpumem_malloc(n * sizeof(uint64_t), type));
292 }
293 
294 struct cpumem *
295 counters_alloc_ncpus(struct cpumem *cm, unsigned int n, int type)
296 {
297 	/* this is unecessary, but symmetrical */
298 	return (cpumem_malloc_ncpus(cm, n * sizeof(uint64_t), type));
299 }
300 
301 void
302 counters_free(struct cpumem *cm, int type, unsigned int n)
303 {
304 	cpumem_free(cm, type, 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