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