]>
Commit | Line | Data |
---|---|---|
b4e1e09c MH |
1 | /* -*- linux-c -*- |
2 | * Map Functions | |
73fcca6f | 3 | * Copyright (C) 2005-2018 Red Hat Inc. |
b4e1e09c MH |
4 | * |
5 | * This file is part of systemtap, and is free software. You can | |
6 | * redistribute it and/or modify it under the terms of the GNU General | |
7 | * Public License (GPL); either version 2, or (at your option) any | |
8 | * later version. | |
9 | */ | |
10 | ||
11 | #ifndef _MAP_C_ | |
e32551b1 MH |
12 | #define _MAP_C_ |
13 | ||
b9c556e4 MH |
14 | /** @file map.c |
15 | * @brief Implements maps (associative arrays) and lists | |
16 | */ | |
17 | ||
549b9c3b MH |
18 | #include "stat-common.c" |
19 | #include "map-stat.c" | |
e32551b1 | 20 | |
4c2732a1 | 21 | static int int64_eq_p (int64_t key1, int64_t key2) |
43614f5d MH |
22 | { |
23 | return key1 == key2; | |
24 | } | |
43614f5d | 25 | |
4c2732a1 | 26 | static void str_copy(char *dest, char *src) |
43614f5d | 27 | { |
4aa2079a JS |
28 | if (src) |
29 | strlcpy(dest, src, MAP_STRING_LENGTH); | |
30 | else | |
31 | *dest = 0; | |
43614f5d | 32 | } |
43614f5d | 33 | |
4c2732a1 | 34 | static void str_add(void *dest, char *val) |
66f95fac MH |
35 | { |
36 | char *dst = (char *)dest; | |
4aa2079a | 37 | strlcat(dst, val, MAP_STRING_LENGTH); |
66f95fac MH |
38 | } |
39 | ||
4c2732a1 | 40 | static int str_eq_p (char *key1, char *key2) |
43614f5d MH |
41 | { |
42 | return strncmp(key1, key2, MAP_STRING_LENGTH - 1) == 0; | |
43 | } | |
44 | ||
204b456c | 45 | |
e5d2abb5 MH |
46 | /** @addtogroup maps |
47 | * Implements maps (associative arrays) and lists | |
48 | * @{ | |
49 | */ | |
50 | ||
7ba70af1 | 51 | |
204b456c MH |
52 | /** Get the first element in a map. |
53 | * @param map | |
54 | * @returns a pointer to the first element. | |
55 | * This is typically used with _stp_map_iter(). See the foreach() macro | |
56 | * for typical usage. It probably does what you want anyway. | |
57 | * @sa foreach | |
58 | */ | |
59 | ||
4c2732a1 | 60 | static struct map_node *_stp_map_start(MAP map) |
204b456c | 61 | { |
d4d2275a | 62 | //dbug ("%lx\n", (long)mlist_next(&map->head)); |
204b456c | 63 | |
d4d2275a | 64 | if (mlist_empty(&map->head)) |
204b456c MH |
65 | return NULL; |
66 | ||
d4d2275a | 67 | return mlist_map_node(mlist_next(&map->head)); |
204b456c MH |
68 | } |
69 | ||
70 | /** Get the next element in a map. | |
71 | * @param map | |
72 | * @param m a pointer to the current element, returned from _stp_map_start() | |
73 | * or _stp_map_iter(). | |
74 | * @returns a pointer to the next element. | |
75 | * This is typically used with _stp_map_start(). See the foreach() macro | |
76 | * for typical usage. It probably does what you want anyway. | |
77 | * @sa foreach | |
78 | */ | |
79 | ||
4c2732a1 | 80 | static struct map_node *_stp_map_iter(MAP map, struct map_node *m) |
204b456c | 81 | { |
d4d2275a | 82 | if (mlist_next(&m->lnode) == &map->head) |
204b456c MH |
83 | return NULL; |
84 | ||
d4d2275a | 85 | return mlist_map_node(mlist_next(&m->lnode)); |
204b456c MH |
86 | } |
87 | ||
a98c930b AJ |
88 | static struct map_node *_stp_map_iterdel(MAP map, struct map_node *m) |
89 | { | |
90 | struct map_node *r_map; | |
3a7fec94 | 91 | |
3a7fec94 AJ |
92 | r_map = _stp_map_iter(map, m); |
93 | _new_map_del_node(map, m); | |
a98c930b | 94 | |
a98c930b AJ |
95 | return r_map; |
96 | } | |
97 | ||
89ad3fb0 MH |
98 | /** Clears all the elements in a map. |
99 | * @param map | |
100 | */ | |
101 | ||
4c2732a1 | 102 | static void _stp_map_clear(MAP map) |
89ad3fb0 MH |
103 | { |
104 | struct map_node *m; | |
105 | ||
89ad3fb0 MH |
106 | map->num = 0; |
107 | ||
d4d2275a JS |
108 | while (!mlist_empty(&map->head)) { |
109 | m = mlist_map_node(mlist_next(&map->head)); | |
110 | ||
89ad3fb0 | 111 | /* remove node from old hash list */ |
d4d2275a JS |
112 | mhlist_del_init(&m->hnode); |
113 | ||
89ad3fb0 | 114 | /* remove from entry list */ |
d4d2275a JS |
115 | mlist_del(&m->lnode); |
116 | ||
89ad3fb0 | 117 | /* add to free pool */ |
d4d2275a | 118 | mlist_add(&m->lnode, &map->pool); |
89ad3fb0 MH |
119 | } |
120 | } | |
121 | ||
4c2732a1 | 122 | static void _stp_pmap_clear(PMAP pmap) |
c5a2dde1 MH |
123 | { |
124 | int i; | |
125 | ||
1d0e697d | 126 | for_each_possible_cpu(i) { |
db45dfde | 127 | MAP m = _stp_pmap_get_map (pmap, i); |
9f804a24 YZ |
128 | if (likely(m != NULL)) |
129 | _stp_map_clear(m); | |
e3575828 | 130 | } |
db45dfde | 131 | _stp_map_clear(_stp_pmap_get_agg(pmap)); |
c5a2dde1 | 132 | } |
953c5ad1 | 133 | |
953c5ad1 | 134 | |
9262e814 | 135 | /* sort keynum values */ |
e9b2a22d | 136 | #define SORT_COUNT -5 /* see also translate.cxx:visit_foreach_loop */ |
9262e814 MH |
137 | #define SORT_SUM -4 |
138 | #define SORT_MIN -3 | |
139 | #define SORT_MAX -2 | |
140 | #define SORT_AVG -1 | |
141 | ||
4a61d600 | 142 | /* comparison function for sorts. */ |
1841d2dd JS |
143 | static int _stp_cmp (struct mlist_head *h1, struct mlist_head *h2, |
144 | int keynum, int dir, map_get_key_fn get_key) | |
145 | { | |
146 | int64_t a = 0, b = 0; | |
147 | int type = END; | |
148 | key_data k1 = (*get_key)(mlist_map_node(h1), keynum, &type); | |
149 | key_data k2 = (*get_key)(mlist_map_node(h2), keynum, NULL); | |
150 | if (type == INT64) { | |
151 | a = k1.val; | |
152 | b = k2.val; | |
153 | } else if (type == STRING) { | |
154 | a = strcmp(k1.strp, k2.strp); | |
155 | b = 0; | |
156 | } else if (type == STAT) { | |
157 | stat_data *sd1 = k1.statp; | |
158 | stat_data *sd2 = k2.statp; | |
159 | switch (keynum) { | |
160 | case SORT_COUNT: | |
161 | a = sd1->count; | |
162 | b = sd2->count; | |
163 | break; | |
164 | case SORT_SUM: | |
165 | a = sd1->sum; | |
166 | b = sd2->sum; | |
167 | break; | |
168 | case SORT_MIN: | |
169 | a = sd1->min; | |
170 | b = sd2->min; | |
171 | break; | |
172 | case SORT_MAX: | |
173 | a = sd1->max; | |
174 | b = sd2->max; | |
175 | break; | |
176 | case SORT_AVG: | |
22563440 MC |
177 | a = _stp_div64 (NULL, sd1->sum, sd1->count); |
178 | b = _stp_div64 (NULL, sd2->sum, sd2->count); | |
1841d2dd JS |
179 | break; |
180 | default: | |
181 | /* should never happen */ | |
182 | a = b = 0; | |
4a61d600 | 183 | } |
4a61d600 | 184 | } |
1841d2dd JS |
185 | if ((a < b && dir > 0) || (a > b && dir < 0)) |
186 | return 1; | |
187 | return 0; | |
4a61d600 MH |
188 | } |
189 | ||
190 | /* swap function for bubble sort */ | |
d4d2275a | 191 | static inline void _stp_swap (struct mlist_head *a, struct mlist_head *b) |
4a61d600 | 192 | { |
d4d2275a JS |
193 | mlist_del(a); |
194 | mlist_add(a, b); | |
4a61d600 MH |
195 | } |
196 | ||
4a61d600 MH |
197 | |
198 | /** Sort an entire array. | |
199 | * Sorts an entire array using merge sort. | |
200 | * | |
201 | * @param map Map | |
202 | * @param keynum 0 for the value, or a positive number for the key number to sort on. | |
203 | * @param dir Sort Direction. -1 for low-to-high. 1 for high-to-low. | |
204 | * @sa _stp_map_sortn() | |
205 | */ | |
206 | ||
2473ac1b JS |
207 | static void _stp_map_sort (MAP map, int keynum, int dir, |
208 | map_get_key_fn get_key) | |
4a61d600 | 209 | { |
d4d2275a | 210 | struct mlist_head *p, *q, *e, *tail; |
1841d2dd | 211 | int nmerges, psize, qsize, i, insize = 1; |
d4d2275a | 212 | struct mlist_head *head = &map->head; |
4a61d600 | 213 | |
d4d2275a | 214 | if (mlist_empty(head)) |
4a61d600 MH |
215 | return; |
216 | ||
4a61d600 MH |
217 | do { |
218 | tail = head; | |
d4d2275a | 219 | p = mlist_next(head); |
4a61d600 MH |
220 | nmerges = 0; |
221 | ||
222 | while (p) { | |
223 | nmerges++; | |
224 | q = p; | |
225 | psize = 0; | |
226 | for (i = 0; i < insize; i++) { | |
227 | psize++; | |
d4d2275a | 228 | q = mlist_next(q) == head ? NULL : mlist_next(q); |
4a61d600 MH |
229 | if (!q) |
230 | break; | |
231 | } | |
232 | ||
233 | qsize = insize; | |
234 | while (psize > 0 || (qsize > 0 && q)) { | |
2473ac1b | 235 | if (psize && (!qsize || !q || |
1841d2dd | 236 | !_stp_cmp(p, q, keynum, dir, get_key))) { |
4a61d600 | 237 | e = p; |
d4d2275a | 238 | p = mlist_next(p) == head ? NULL : mlist_next(p); |
4a61d600 MH |
239 | psize--; |
240 | } else { | |
241 | e = q; | |
d4d2275a | 242 | q = mlist_next(q) == head ? NULL : mlist_next(q); |
4a61d600 MH |
243 | qsize--; |
244 | } | |
d4d2275a | 245 | |
4a61d600 | 246 | /* now put 'e' on tail of list and make it our new tail */ |
d4d2275a JS |
247 | mlist_del(e); |
248 | mlist_add(e, tail); | |
4a61d600 MH |
249 | tail = e; |
250 | } | |
251 | p = q; | |
252 | } | |
253 | insize += insize; | |
254 | } while (nmerges > 1); | |
255 | } | |
256 | ||
fbb24b89 MH |
257 | /** Get the top values from an array. |
258 | * Sorts an array such that the start of the array contains the top | |
259 | * or bottom 'n' values. Use this when sorting the entire array | |
260 | * would be too time-consuming and you are only interested in the | |
261 | * highest or lowest values. | |
262 | * | |
263 | * @param map Map | |
264 | * @param n Top (or bottom) number of elements. 0 sorts the entire array. | |
265 | * @param keynum 0 for the value, or a positive number for the key number to sort on. | |
266 | * @param dir Sort Direction. -1 for low-to-high. 1 for high-to-low. | |
267 | * @sa _stp_map_sort() | |
268 | */ | |
2473ac1b JS |
269 | static void _stp_map_sortn(MAP map, int n, int keynum, int dir, |
270 | map_get_key_fn get_key) | |
fbb24b89 | 271 | { |
083384f0 | 272 | if (n == 0 || n > 30) { |
2473ac1b | 273 | _stp_map_sort(map, keynum, dir, get_key); |
fbb24b89 | 274 | } else { |
d4d2275a | 275 | struct mlist_head *head = &map->head; |
97a9ceb2 | 276 | struct mlist_head *c, *a = 0, *last, *tmp; |
1841d2dd | 277 | int num, swaps = 1; |
d4d2275a JS |
278 | |
279 | if (mlist_empty(head)) | |
fbb24b89 | 280 | return; |
d4d2275a | 281 | |
fbb24b89 MH |
282 | /* start off with a modified bubble sort of the first n elements */ |
283 | while (swaps) { | |
284 | num = n; | |
285 | swaps = 0; | |
d4d2275a JS |
286 | a = mlist_next(head); |
287 | c = mlist_next(mlist_next(a)); | |
288 | while ((mlist_next(a) != head) && (--num > 0)) { | |
1841d2dd | 289 | if (_stp_cmp(a, mlist_next(a), keynum, dir, get_key)) { |
fbb24b89 | 290 | swaps++; |
d4d2275a | 291 | _stp_swap(a, mlist_next(a)); |
fbb24b89 | 292 | } |
d4d2275a JS |
293 | a = mlist_prev(c); |
294 | c = mlist_next(c); | |
fbb24b89 MH |
295 | } |
296 | } | |
d4d2275a | 297 | |
fbb24b89 MH |
298 | /* Now use a kind of insertion sort for the rest of the array. */ |
299 | /* Each element is tested to see if it should be be in the top 'n' */ | |
300 | last = a; | |
d4d2275a | 301 | a = mlist_next(a); |
fbb24b89 | 302 | while (a != head) { |
d4d2275a | 303 | tmp = mlist_next(a); |
fbb24b89 | 304 | c = last; |
1841d2dd | 305 | while (c != head && _stp_cmp(c, a, keynum, dir, get_key)) |
d4d2275a | 306 | c = mlist_prev(c); |
fbb24b89 | 307 | if (c != last) { |
d4d2275a JS |
308 | mlist_del(a); |
309 | mlist_add(a, c); | |
310 | last = mlist_prev(last); | |
fbb24b89 MH |
311 | } |
312 | a = tmp; | |
313 | } | |
314 | } | |
315 | } | |
4a61d600 | 316 | |
2473ac1b | 317 | static struct map_node *_stp_new_agg(MAP agg, struct mhlist_head *ahead, |
b17e1e1e | 318 | struct map_node *ptr, map_update_fn update) |
7ba70af1 MH |
319 | { |
320 | struct map_node *aptr; | |
321 | /* copy keys and aggregate */ | |
7ba70af1 MH |
322 | aptr = _new_map_create(agg, ahead); |
323 | if (aptr == NULL) | |
51761586 | 324 | return NULL; |
b17e1e1e | 325 | (*update)(agg, aptr, ptr, 0); |
51761586 | 326 | return aptr; |
7ba70af1 MH |
327 | } |
328 | ||
8d948813 MH |
329 | /** Aggregate per-cpu maps. |
330 | * This function aggregates the per-cpu maps into an aggregated | |
331 | * map. A pointer to that aggregated map is returned. | |
332 | * | |
333 | * A write lock must be held on the map during this function. | |
334 | * | |
335 | * @param map A pointer to a pmap. | |
f903d01c | 336 | * @returns a pointer to the aggregated map. Null on failure. |
8d948813 | 337 | */ |
b17e1e1e | 338 | static MAP _stp_pmap_agg (PMAP pmap, map_update_fn update, map_cmp_fn cmp) |
7ba70af1 MH |
339 | { |
340 | int i, hash; | |
341 | MAP m, agg; | |
508e476d | 342 | struct map_node *ptr, *aptr = NULL; |
d4d2275a JS |
343 | struct mhlist_head *head, *ahead; |
344 | struct mhlist_node *e, *f; | |
82523f19 | 345 | int quit = 0; |
7ba70af1 | 346 | |
db45dfde | 347 | agg = _stp_pmap_get_agg(pmap); |
b17e1e1e | 348 | |
7ba70af1 MH |
349 | /* FIXME. we either clear the aggregation map or clear each local map */ |
350 | /* every time we aggregate. which would be best? */ | |
351 | _stp_map_clear (agg); | |
352 | ||
1d0e697d | 353 | for_each_possible_cpu(i) { |
db45dfde | 354 | m = _stp_pmap_get_map (pmap, i); |
9f804a24 YZ |
355 | if (unlikely(m == NULL)) { |
356 | /* offline CPU or a newly-added online CPU */ | |
357 | continue; | |
358 | } | |
359 | ||
7ba70af1 | 360 | /* walk the hash chains. */ |
9a38b89c | 361 | for (hash = 0; hash <= m->hash_table_mask; hash++) { |
7ba70af1 MH |
362 | head = &m->hashes[hash]; |
363 | ahead = &agg->hashes[hash]; | |
d4d2275a | 364 | mhlist_for_each_entry(ptr, e, head, hnode) { |
7ba70af1 | 365 | int match = 0; |
d4d2275a | 366 | mhlist_for_each_entry(aptr, f, ahead, hnode) { |
2473ac1b | 367 | if ((*cmp)(ptr, aptr)) { |
7ba70af1 MH |
368 | match = 1; |
369 | break; | |
370 | } | |
371 | } | |
372 | if (match) | |
b17e1e1e | 373 | (*update)(agg, aptr, ptr, 1); |
f903d01c | 374 | else { |
b17e1e1e | 375 | if (!_stp_new_agg(agg, ahead, ptr, update)) { |
961f1254 FCE |
376 | agg = NULL; |
377 | goto out; | |
378 | // NB: break would head out to the for (hash...) | |
379 | // loop, which behaves badly with an agg==NULL. | |
f903d01c MH |
380 | } |
381 | } | |
7ba70af1 MH |
382 | } |
383 | } | |
384 | } | |
961f1254 | 385 | |
810762b4 | 386 | out: |
8d948813 | 387 | return agg; |
7ba70af1 MH |
388 | } |
389 | ||
d4d2275a | 390 | static struct map_node *_new_map_create (MAP map, struct mhlist_head *head) |
af5dd76b MH |
391 | { |
392 | struct map_node *m; | |
d4d2275a | 393 | if (mlist_empty(&map->pool)) { |
af5dd76b MH |
394 | if (!map->wrap) { |
395 | /* ERROR. no space left */ | |
396 | return NULL; | |
397 | } | |
d4d2275a JS |
398 | m = mlist_map_node(mlist_next(&map->head)); |
399 | mhlist_del_init(&m->hnode); | |
af5dd76b | 400 | } else { |
d4d2275a | 401 | m = mlist_map_node(mlist_next(&map->pool)); |
99675700 | 402 | map->num++; |
af5dd76b | 403 | } |
d4d2275a JS |
404 | mlist_move_tail(&m->lnode, &map->head); |
405 | ||
af5dd76b | 406 | /* add node to new hash list */ |
d4d2275a | 407 | mhlist_add_head(&m->hnode, head); |
af5dd76b MH |
408 | return m; |
409 | } | |
410 | ||
411 | static void _new_map_del_node (MAP map, struct map_node *n) | |
412 | { | |
413 | /* remove node from old hash list */ | |
d4d2275a JS |
414 | mhlist_del_init(&n->hnode); |
415 | ||
af5dd76b | 416 | /* remove from entry list */ |
d4d2275a JS |
417 | mlist_del(&n->lnode); |
418 | ||
af5dd76b | 419 | /* add it back to the pool */ |
d4d2275a JS |
420 | mlist_add(&n->lnode, &map->pool); |
421 | ||
af5dd76b MH |
422 | map->num--; |
423 | } | |
424 | ||
b17e1e1e | 425 | static int _new_map_set_int64 (MAP map, int64_t *dst, int64_t val, int add) |
af5dd76b | 426 | { |
3c2a749a | 427 | if (add) |
b17e1e1e | 428 | *dst += val; |
3c2a749a | 429 | else |
b17e1e1e | 430 | *dst = val; |
3c2a749a | 431 | |
af5dd76b MH |
432 | return 0; |
433 | } | |
434 | ||
b17e1e1e | 435 | static int _new_map_set_str (MAP map, char *dst, char *val, int add) |
af5dd76b | 436 | { |
3c2a749a | 437 | if (add) |
b17e1e1e | 438 | str_add(dst, val); |
3c2a749a | 439 | else |
b17e1e1e | 440 | str_copy(dst, val); |
3c2a749a | 441 | |
af5dd76b MH |
442 | return 0; |
443 | } | |
444 | ||
26382d61 | 445 | static int _new_map_set_stat (MAP map, struct stat_data *sd, int64_t val, int add, int s1, int s2, int s3, int s4, int s5) |
af5dd76b | 446 | { |
7ba70af1 MH |
447 | if (!add) { |
448 | Hist st = &map->hist; | |
449 | sd->count = 0; | |
450 | if (st->type != HIST_NONE) { | |
451 | int j; | |
452 | for (j = 0; j < st->buckets; j++) | |
af5dd76b MH |
453 | sd->histogram[j] = 0; |
454 | } | |
455 | } | |
63ead7fa MC |
456 | (&map->hist)->bit_shift = map->bit_shift; |
457 | (&map->hist)->stat_ops = map->stat_ops; | |
26382d61 | 458 | __stp_stat_add (&map->hist, sd, val, s1, s2, s3, s4, s5); |
af5dd76b MH |
459 | return 0; |
460 | } | |
461 | ||
b17e1e1e JS |
462 | static int _new_map_copy_stat (MAP map, struct stat_data *sd1, struct stat_data *sd2, int add) |
463 | { | |
464 | Hist st = &map->hist; | |
63ead7fa MC |
465 | int64_t S11, S12, S21, S22; |
466 | int64_t sd1_count, sd1_avg_s; | |
467 | S11 = S12 = S21 = S22 = 0; | |
468 | sd1_count = sd1_avg_s = 0; | |
b17e1e1e | 469 | |
226188f2 FCE |
470 | if (sd2 == NULL) { |
471 | sd1->count = 0; | |
472 | if (st->type != HIST_NONE) { | |
473 | int j; | |
474 | for (j = 0; j < st->buckets; j++) | |
475 | sd1->histogram[j] = 0; | |
476 | } | |
477 | } else if (add && sd1->count > 0 && sd2->count > 0) { | |
63ead7fa MC |
478 | sd1_count = sd1->count; |
479 | sd1_avg_s = sd1->avg_s; | |
480 | ||
b17e1e1e JS |
481 | sd1->count += sd2->count; |
482 | sd1->sum += sd2->sum; | |
483 | if (sd2->min < sd1->min) | |
484 | sd1->min = sd2->min; | |
485 | if (sd2->max > sd1->max) | |
486 | sd1->max = sd2->max; | |
0ec5ddf5 FCE |
487 | |
488 | if (sd2->stat_ops & STAT_OP_VARIANCE) { | |
489 | sd1->shift = sd2->shift; | |
490 | sd1->avg_s = _stp_div64(NULL, sd1->sum << sd2->shift, sd1->count); | |
0ec5ddf5 FCE |
491 | |
492 | /* | |
493 | * For aggregating variance over available CPUs, the Total Variance | |
494 | * formula is being used. This formula is mentioned in following | |
495 | * paper: Niranjan Kamat, Arnab Nandi: A Closer Look at Variance | |
496 | * Implementations In Modern Database Systems: SIGMOD Record 2015. | |
497 | * Available at: http://web.cse.ohio-state.edu/~kamatn/variance.pdf | |
498 | */ | |
499 | S11 = sd1_count * (sd1_avg_s - sd1->avg_s) * (sd1_avg_s - sd1->avg_s); | |
500 | S12 = (sd1_count - 1) * sd1->variance_s; | |
501 | S21 = sd2->count * (sd2->avg_s - sd1->avg_s) * (sd2->avg_s - sd1->avg_s); | |
502 | S22 = (sd2->count - 1) * sd2->variance_s; | |
503 | ||
504 | sd1->variance_s = _stp_div64(NULL, (S11 + S12 + S21 + S22), (sd1->count - 1)); | |
505 | sd1->variance = sd1->variance_s >> (2 * sd2->shift); | |
506 | } | |
b17e1e1e JS |
507 | if (st->type != HIST_NONE) { |
508 | int j; | |
509 | for (j = 0; j < st->buckets; j++) | |
510 | sd1->histogram[j] += sd2->histogram[j]; | |
511 | } | |
512 | } else { | |
513 | sd1->count = sd2->count; | |
514 | sd1->sum = sd2->sum; | |
515 | sd1->min = sd2->min; | |
516 | sd1->max = sd2->max; | |
0ec5ddf5 FCE |
517 | if (sd2->stat_ops & STAT_OP_VARIANCE) { |
518 | sd1->shift = sd2->shift; | |
519 | sd1->avg_s = sd2->avg_s; | |
0ec5ddf5 FCE |
520 | sd1->variance_s = sd2->variance_s; |
521 | sd1->variance = sd2->variance_s >> (2 * sd2->shift); | |
522 | } | |
b17e1e1e JS |
523 | if (st->type != HIST_NONE) { |
524 | int j; | |
525 | for (j = 0; j < st->buckets; j++) | |
526 | sd1->histogram[j] = sd2->histogram[j]; | |
527 | } | |
528 | } | |
529 | return 0; | |
530 | } | |
531 | ||
99675700 MH |
532 | /** Return the number of elements in a map |
533 | * This function will return the number of active elements | |
534 | * in a map. | |
535 | * @param map | |
536 | * @returns an int | |
537 | */ | |
538 | #define _stp_map_size(map) (map->num) | |
539 | ||
540 | /** Return the number of elements in a pmap | |
541 | * This function will return the number of active elements | |
542 | * in all the per-cpu maps in a pmap. This is a quick sum and is | |
543 | * not the same as the number of unique elements that would | |
544 | * be in the aggragated map. | |
545 | * @param pmap | |
546 | * @returns an int | |
547 | */ | |
4c2732a1 | 548 | static int _stp_pmap_size (PMAP pmap) |
99675700 MH |
549 | { |
550 | int i, num = 0; | |
551 | ||
1d0e697d | 552 | for_each_possible_cpu(i) { |
db45dfde | 553 | MAP m = _stp_pmap_get_map (pmap, i); |
9f804a24 YZ |
554 | if (unlikely(m == NULL)) { |
555 | /* offline CPU or a newly-added online CPU */ | |
556 | continue; | |
557 | } | |
e3575828 DS |
558 | num += m->num; |
559 | } | |
99675700 MH |
560 | return num; |
561 | } | |
af5dd76b | 562 | #endif /* _MAP_C_ */ |
204b456c | 563 |