Loading...
1#include <stddef.h>
2#include <stdlib.h>
3#include <string.h>
4#include <errno.h>
5#include <sys/types.h>
6#include <sys/stat.h>
7#include <unistd.h>
8#include <api/fs/fs.h>
9#include "mem-events.h"
10#include "debug.h"
11#include "symbol.h"
12
13#define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
14
15struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = {
16 E("ldlat-loads", "cpu/mem-loads,ldlat=30/P", "mem-loads"),
17 E("ldlat-stores", "cpu/mem-stores/P", "mem-stores"),
18};
19#undef E
20
21#undef E
22
23char *perf_mem_events__name(int i)
24{
25 return (char *)perf_mem_events[i].name;
26}
27
28int perf_mem_events__parse(const char *str)
29{
30 char *tok, *saveptr = NULL;
31 bool found = false;
32 char *buf;
33 int j;
34
35 /* We need buffer that we know we can write to. */
36 buf = malloc(strlen(str) + 1);
37 if (!buf)
38 return -ENOMEM;
39
40 strcpy(buf, str);
41
42 tok = strtok_r((char *)buf, ",", &saveptr);
43
44 while (tok) {
45 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
46 struct perf_mem_event *e = &perf_mem_events[j];
47
48 if (strstr(e->tag, tok))
49 e->record = found = true;
50 }
51
52 tok = strtok_r(NULL, ",", &saveptr);
53 }
54
55 free(buf);
56
57 if (found)
58 return 0;
59
60 pr_err("failed: event '%s' not found, use '-e list' to get list of available events\n", str);
61 return -1;
62}
63
64int perf_mem_events__init(void)
65{
66 const char *mnt = sysfs__mount();
67 bool found = false;
68 int j;
69
70 if (!mnt)
71 return -ENOENT;
72
73 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
74 char path[PATH_MAX];
75 struct perf_mem_event *e = &perf_mem_events[j];
76 struct stat st;
77
78 scnprintf(path, PATH_MAX, "%s/devices/cpu/events/%s",
79 mnt, e->sysfs_name);
80
81 if (!stat(path, &st))
82 e->supported = found = true;
83 }
84
85 return found ? 0 : -ENOENT;
86}
87
88static const char * const tlb_access[] = {
89 "N/A",
90 "HIT",
91 "MISS",
92 "L1",
93 "L2",
94 "Walker",
95 "Fault",
96};
97
98int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
99{
100 size_t l = 0, i;
101 u64 m = PERF_MEM_TLB_NA;
102 u64 hit, miss;
103
104 sz -= 1; /* -1 for null termination */
105 out[0] = '\0';
106
107 if (mem_info)
108 m = mem_info->data_src.mem_dtlb;
109
110 hit = m & PERF_MEM_TLB_HIT;
111 miss = m & PERF_MEM_TLB_MISS;
112
113 /* already taken care of */
114 m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
115
116 for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
117 if (!(m & 0x1))
118 continue;
119 if (l) {
120 strcat(out, " or ");
121 l += 4;
122 }
123 l += scnprintf(out + l, sz - l, tlb_access[i]);
124 }
125 if (*out == '\0')
126 l += scnprintf(out, sz - l, "N/A");
127 if (hit)
128 l += scnprintf(out + l, sz - l, " hit");
129 if (miss)
130 l += scnprintf(out + l, sz - l, " miss");
131
132 return l;
133}
134
135static const char * const mem_lvl[] = {
136 "N/A",
137 "HIT",
138 "MISS",
139 "L1",
140 "LFB",
141 "L2",
142 "L3",
143 "Local RAM",
144 "Remote RAM (1 hop)",
145 "Remote RAM (2 hops)",
146 "Remote Cache (1 hop)",
147 "Remote Cache (2 hops)",
148 "I/O",
149 "Uncached",
150};
151
152int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
153{
154 size_t i, l = 0;
155 u64 m = PERF_MEM_LVL_NA;
156 u64 hit, miss;
157
158 if (mem_info)
159 m = mem_info->data_src.mem_lvl;
160
161 sz -= 1; /* -1 for null termination */
162 out[0] = '\0';
163
164 hit = m & PERF_MEM_LVL_HIT;
165 miss = m & PERF_MEM_LVL_MISS;
166
167 /* already taken care of */
168 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
169
170 for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
171 if (!(m & 0x1))
172 continue;
173 if (l) {
174 strcat(out, " or ");
175 l += 4;
176 }
177 l += scnprintf(out + l, sz - l, mem_lvl[i]);
178 }
179 if (*out == '\0')
180 l += scnprintf(out, sz - l, "N/A");
181 if (hit)
182 l += scnprintf(out + l, sz - l, " hit");
183 if (miss)
184 l += scnprintf(out + l, sz - l, " miss");
185
186 return l;
187}
188
189static const char * const snoop_access[] = {
190 "N/A",
191 "None",
192 "Miss",
193 "Hit",
194 "HitM",
195};
196
197int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
198{
199 size_t i, l = 0;
200 u64 m = PERF_MEM_SNOOP_NA;
201
202 sz -= 1; /* -1 for null termination */
203 out[0] = '\0';
204
205 if (mem_info)
206 m = mem_info->data_src.mem_snoop;
207
208 for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
209 if (!(m & 0x1))
210 continue;
211 if (l) {
212 strcat(out, " or ");
213 l += 4;
214 }
215 l += scnprintf(out + l, sz - l, snoop_access[i]);
216 }
217
218 if (*out == '\0')
219 l += scnprintf(out, sz - l, "N/A");
220
221 return l;
222}
223
224int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
225{
226 u64 mask = PERF_MEM_LOCK_NA;
227 int l;
228
229 if (mem_info)
230 mask = mem_info->data_src.mem_lock;
231
232 if (mask & PERF_MEM_LOCK_NA)
233 l = scnprintf(out, sz, "N/A");
234 else if (mask & PERF_MEM_LOCK_LOCKED)
235 l = scnprintf(out, sz, "Yes");
236 else
237 l = scnprintf(out, sz, "No");
238
239 return l;
240}
241
242int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
243{
244 int i = 0;
245
246 i += perf_mem__lvl_scnprintf(out, sz, mem_info);
247 i += scnprintf(out + i, sz - i, "|SNP ");
248 i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
249 i += scnprintf(out + i, sz - i, "|TLB ");
250 i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
251 i += scnprintf(out + i, sz - i, "|LCK ");
252 i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
253
254 return i;
255}
1// SPDX-License-Identifier: GPL-2.0
2#include <stddef.h>
3#include <stdlib.h>
4#include <string.h>
5#include <errno.h>
6#include <sys/types.h>
7#include <sys/stat.h>
8#include <unistd.h>
9#include <api/fs/fs.h>
10#include <linux/kernel.h>
11#include "map_symbol.h"
12#include "mem-events.h"
13#include "debug.h"
14#include "symbol.h"
15#include "pmu.h"
16#include "pmus.h"
17
18unsigned int perf_mem_events__loads_ldlat = 30;
19
20#define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
21
22static struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = {
23 E("ldlat-loads", "cpu/mem-loads,ldlat=%u/P", "cpu/events/mem-loads"),
24 E("ldlat-stores", "cpu/mem-stores/P", "cpu/events/mem-stores"),
25 E(NULL, NULL, NULL),
26};
27#undef E
28
29static char mem_loads_name[100];
30static bool mem_loads_name__init;
31
32struct perf_mem_event * __weak perf_mem_events__ptr(int i)
33{
34 if (i >= PERF_MEM_EVENTS__MAX)
35 return NULL;
36
37 return &perf_mem_events[i];
38}
39
40const char * __weak perf_mem_events__name(int i, const char *pmu_name __maybe_unused)
41{
42 struct perf_mem_event *e = perf_mem_events__ptr(i);
43
44 if (!e)
45 return NULL;
46
47 if (i == PERF_MEM_EVENTS__LOAD) {
48 if (!mem_loads_name__init) {
49 mem_loads_name__init = true;
50 scnprintf(mem_loads_name, sizeof(mem_loads_name),
51 e->name, perf_mem_events__loads_ldlat);
52 }
53 return mem_loads_name;
54 }
55
56 return e->name;
57}
58
59__weak bool is_mem_loads_aux_event(struct evsel *leader __maybe_unused)
60{
61 return false;
62}
63
64int perf_mem_events__parse(const char *str)
65{
66 char *tok, *saveptr = NULL;
67 bool found = false;
68 char *buf;
69 int j;
70
71 /* We need buffer that we know we can write to. */
72 buf = malloc(strlen(str) + 1);
73 if (!buf)
74 return -ENOMEM;
75
76 strcpy(buf, str);
77
78 tok = strtok_r((char *)buf, ",", &saveptr);
79
80 while (tok) {
81 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
82 struct perf_mem_event *e = perf_mem_events__ptr(j);
83
84 if (!e->tag)
85 continue;
86
87 if (strstr(e->tag, tok))
88 e->record = found = true;
89 }
90
91 tok = strtok_r(NULL, ",", &saveptr);
92 }
93
94 free(buf);
95
96 if (found)
97 return 0;
98
99 pr_err("failed: event '%s' not found, use '-e list' to get list of available events\n", str);
100 return -1;
101}
102
103static bool perf_mem_event__supported(const char *mnt, struct perf_pmu *pmu,
104 struct perf_mem_event *e)
105{
106 char sysfs_name[100];
107 char path[PATH_MAX];
108 struct stat st;
109
110 scnprintf(sysfs_name, sizeof(sysfs_name), e->sysfs_name, pmu->name);
111 scnprintf(path, PATH_MAX, "%s/devices/%s", mnt, sysfs_name);
112 return !stat(path, &st);
113}
114
115int perf_mem_events__init(void)
116{
117 const char *mnt = sysfs__mount();
118 bool found = false;
119 int j;
120
121 if (!mnt)
122 return -ENOENT;
123
124 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
125 struct perf_mem_event *e = perf_mem_events__ptr(j);
126 struct perf_pmu *pmu = NULL;
127
128 /*
129 * If the event entry isn't valid, skip initialization
130 * and "e->supported" will keep false.
131 */
132 if (!e->tag)
133 continue;
134
135 /*
136 * Scan all PMUs not just core ones, since perf mem/c2c on
137 * platforms like AMD uses IBS OP PMU which is independent
138 * of core PMU.
139 */
140 while ((pmu = perf_pmus__scan(pmu)) != NULL) {
141 e->supported |= perf_mem_event__supported(mnt, pmu, e);
142 if (e->supported) {
143 found = true;
144 break;
145 }
146 }
147 }
148
149 return found ? 0 : -ENOENT;
150}
151
152void perf_mem_events__list(void)
153{
154 int j;
155
156 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
157 struct perf_mem_event *e = perf_mem_events__ptr(j);
158
159 fprintf(stderr, "%-*s%-*s%s",
160 e->tag ? 13 : 0,
161 e->tag ? : "",
162 e->tag && verbose > 0 ? 25 : 0,
163 e->tag && verbose > 0 ? perf_mem_events__name(j, NULL) : "",
164 e->supported ? ": available\n" : "");
165 }
166}
167
168static void perf_mem_events__print_unsupport_hybrid(struct perf_mem_event *e,
169 int idx)
170{
171 const char *mnt = sysfs__mount();
172 struct perf_pmu *pmu = NULL;
173
174 while ((pmu = perf_pmus__scan(pmu)) != NULL) {
175 if (!perf_mem_event__supported(mnt, pmu, e)) {
176 pr_err("failed: event '%s' not supported\n",
177 perf_mem_events__name(idx, pmu->name));
178 }
179 }
180}
181
182int perf_mem_events__record_args(const char **rec_argv, int *argv_nr,
183 char **rec_tmp, int *tmp_nr)
184{
185 const char *mnt = sysfs__mount();
186 int i = *argv_nr, k = 0;
187 struct perf_mem_event *e;
188
189 for (int j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
190 e = perf_mem_events__ptr(j);
191 if (!e->record)
192 continue;
193
194 if (perf_pmus__num_mem_pmus() == 1) {
195 if (!e->supported) {
196 pr_err("failed: event '%s' not supported\n",
197 perf_mem_events__name(j, NULL));
198 return -1;
199 }
200
201 rec_argv[i++] = "-e";
202 rec_argv[i++] = perf_mem_events__name(j, NULL);
203 } else {
204 struct perf_pmu *pmu = NULL;
205
206 if (!e->supported) {
207 perf_mem_events__print_unsupport_hybrid(e, j);
208 return -1;
209 }
210
211 while ((pmu = perf_pmus__scan(pmu)) != NULL) {
212 const char *s = perf_mem_events__name(j, pmu->name);
213
214 if (!perf_mem_event__supported(mnt, pmu, e))
215 continue;
216
217 rec_argv[i++] = "-e";
218 if (s) {
219 char *copy = strdup(s);
220 if (!copy)
221 return -1;
222
223 rec_argv[i++] = copy;
224 rec_tmp[k++] = copy;
225 }
226 }
227 }
228 }
229
230 *argv_nr = i;
231 *tmp_nr = k;
232 return 0;
233}
234
235static const char * const tlb_access[] = {
236 "N/A",
237 "HIT",
238 "MISS",
239 "L1",
240 "L2",
241 "Walker",
242 "Fault",
243};
244
245int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
246{
247 size_t l = 0, i;
248 u64 m = PERF_MEM_TLB_NA;
249 u64 hit, miss;
250
251 sz -= 1; /* -1 for null termination */
252 out[0] = '\0';
253
254 if (mem_info)
255 m = mem_info->data_src.mem_dtlb;
256
257 hit = m & PERF_MEM_TLB_HIT;
258 miss = m & PERF_MEM_TLB_MISS;
259
260 /* already taken care of */
261 m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
262
263 for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
264 if (!(m & 0x1))
265 continue;
266 if (l) {
267 strcat(out, " or ");
268 l += 4;
269 }
270 l += scnprintf(out + l, sz - l, tlb_access[i]);
271 }
272 if (*out == '\0')
273 l += scnprintf(out, sz - l, "N/A");
274 if (hit)
275 l += scnprintf(out + l, sz - l, " hit");
276 if (miss)
277 l += scnprintf(out + l, sz - l, " miss");
278
279 return l;
280}
281
282static const char * const mem_lvl[] = {
283 "N/A",
284 "HIT",
285 "MISS",
286 "L1",
287 "LFB/MAB",
288 "L2",
289 "L3",
290 "Local RAM",
291 "Remote RAM (1 hop)",
292 "Remote RAM (2 hops)",
293 "Remote Cache (1 hop)",
294 "Remote Cache (2 hops)",
295 "I/O",
296 "Uncached",
297};
298
299static const char * const mem_lvlnum[] = {
300 [PERF_MEM_LVLNUM_UNC] = "Uncached",
301 [PERF_MEM_LVLNUM_CXL] = "CXL",
302 [PERF_MEM_LVLNUM_IO] = "I/O",
303 [PERF_MEM_LVLNUM_ANY_CACHE] = "Any cache",
304 [PERF_MEM_LVLNUM_LFB] = "LFB/MAB",
305 [PERF_MEM_LVLNUM_RAM] = "RAM",
306 [PERF_MEM_LVLNUM_PMEM] = "PMEM",
307 [PERF_MEM_LVLNUM_NA] = "N/A",
308};
309
310static const char * const mem_hops[] = {
311 "N/A",
312 /*
313 * While printing, 'Remote' will be added to represent
314 * 'Remote core, same node' accesses as remote field need
315 * to be set with mem_hops field.
316 */
317 "core, same node",
318 "node, same socket",
319 "socket, same board",
320 "board",
321};
322
323static int perf_mem__op_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
324{
325 u64 op = PERF_MEM_LOCK_NA;
326 int l;
327
328 if (mem_info)
329 op = mem_info->data_src.mem_op;
330
331 if (op & PERF_MEM_OP_NA)
332 l = scnprintf(out, sz, "N/A");
333 else if (op & PERF_MEM_OP_LOAD)
334 l = scnprintf(out, sz, "LOAD");
335 else if (op & PERF_MEM_OP_STORE)
336 l = scnprintf(out, sz, "STORE");
337 else if (op & PERF_MEM_OP_PFETCH)
338 l = scnprintf(out, sz, "PFETCH");
339 else if (op & PERF_MEM_OP_EXEC)
340 l = scnprintf(out, sz, "EXEC");
341 else
342 l = scnprintf(out, sz, "No");
343
344 return l;
345}
346
347int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
348{
349 union perf_mem_data_src data_src;
350 int printed = 0;
351 size_t l = 0;
352 size_t i;
353 int lvl;
354 char hit_miss[5] = {0};
355
356 sz -= 1; /* -1 for null termination */
357 out[0] = '\0';
358
359 if (!mem_info)
360 goto na;
361
362 data_src = mem_info->data_src;
363
364 if (data_src.mem_lvl & PERF_MEM_LVL_HIT)
365 memcpy(hit_miss, "hit", 3);
366 else if (data_src.mem_lvl & PERF_MEM_LVL_MISS)
367 memcpy(hit_miss, "miss", 4);
368
369 lvl = data_src.mem_lvl_num;
370 if (lvl && lvl != PERF_MEM_LVLNUM_NA) {
371 if (data_src.mem_remote) {
372 strcat(out, "Remote ");
373 l += 7;
374 }
375
376 if (data_src.mem_hops)
377 l += scnprintf(out + l, sz - l, "%s ", mem_hops[data_src.mem_hops]);
378
379 if (mem_lvlnum[lvl])
380 l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]);
381 else
382 l += scnprintf(out + l, sz - l, "L%d", lvl);
383
384 l += scnprintf(out + l, sz - l, " %s", hit_miss);
385 return l;
386 }
387
388 lvl = data_src.mem_lvl;
389 if (!lvl)
390 goto na;
391
392 lvl &= ~(PERF_MEM_LVL_NA | PERF_MEM_LVL_HIT | PERF_MEM_LVL_MISS);
393 if (!lvl)
394 goto na;
395
396 for (i = 0; lvl && i < ARRAY_SIZE(mem_lvl); i++, lvl >>= 1) {
397 if (!(lvl & 0x1))
398 continue;
399 if (printed++) {
400 strcat(out, " or ");
401 l += 4;
402 }
403 l += scnprintf(out + l, sz - l, mem_lvl[i]);
404 }
405
406 if (printed) {
407 l += scnprintf(out + l, sz - l, " %s", hit_miss);
408 return l;
409 }
410
411na:
412 strcat(out, "N/A");
413 return 3;
414}
415
416static const char * const snoop_access[] = {
417 "N/A",
418 "None",
419 "Hit",
420 "Miss",
421 "HitM",
422};
423
424static const char * const snoopx_access[] = {
425 "Fwd",
426 "Peer",
427};
428
429int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
430{
431 size_t i, l = 0;
432 u64 m = PERF_MEM_SNOOP_NA;
433
434 sz -= 1; /* -1 for null termination */
435 out[0] = '\0';
436
437 if (mem_info)
438 m = mem_info->data_src.mem_snoop;
439
440 for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
441 if (!(m & 0x1))
442 continue;
443 if (l) {
444 strcat(out, " or ");
445 l += 4;
446 }
447 l += scnprintf(out + l, sz - l, snoop_access[i]);
448 }
449
450 m = 0;
451 if (mem_info)
452 m = mem_info->data_src.mem_snoopx;
453
454 for (i = 0; m && i < ARRAY_SIZE(snoopx_access); i++, m >>= 1) {
455 if (!(m & 0x1))
456 continue;
457
458 if (l) {
459 strcat(out, " or ");
460 l += 4;
461 }
462 l += scnprintf(out + l, sz - l, snoopx_access[i]);
463 }
464
465 if (*out == '\0')
466 l += scnprintf(out, sz - l, "N/A");
467
468 return l;
469}
470
471int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
472{
473 u64 mask = PERF_MEM_LOCK_NA;
474 int l;
475
476 if (mem_info)
477 mask = mem_info->data_src.mem_lock;
478
479 if (mask & PERF_MEM_LOCK_NA)
480 l = scnprintf(out, sz, "N/A");
481 else if (mask & PERF_MEM_LOCK_LOCKED)
482 l = scnprintf(out, sz, "Yes");
483 else
484 l = scnprintf(out, sz, "No");
485
486 return l;
487}
488
489int perf_mem__blk_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
490{
491 size_t l = 0;
492 u64 mask = PERF_MEM_BLK_NA;
493
494 sz -= 1; /* -1 for null termination */
495 out[0] = '\0';
496
497 if (mem_info)
498 mask = mem_info->data_src.mem_blk;
499
500 if (!mask || (mask & PERF_MEM_BLK_NA)) {
501 l += scnprintf(out + l, sz - l, " N/A");
502 return l;
503 }
504 if (mask & PERF_MEM_BLK_DATA)
505 l += scnprintf(out + l, sz - l, " Data");
506 if (mask & PERF_MEM_BLK_ADDR)
507 l += scnprintf(out + l, sz - l, " Addr");
508
509 return l;
510}
511
512int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
513{
514 int i = 0;
515
516 i += scnprintf(out, sz, "|OP ");
517 i += perf_mem__op_scnprintf(out + i, sz - i, mem_info);
518 i += scnprintf(out + i, sz - i, "|LVL ");
519 i += perf_mem__lvl_scnprintf(out + i, sz, mem_info);
520 i += scnprintf(out + i, sz - i, "|SNP ");
521 i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
522 i += scnprintf(out + i, sz - i, "|TLB ");
523 i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
524 i += scnprintf(out + i, sz - i, "|LCK ");
525 i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
526 i += scnprintf(out + i, sz - i, "|BLK ");
527 i += perf_mem__blk_scnprintf(out + i, sz - i, mem_info);
528
529 return i;
530}
531
532int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi)
533{
534 union perf_mem_data_src *data_src = &mi->data_src;
535 u64 daddr = mi->daddr.addr;
536 u64 op = data_src->mem_op;
537 u64 lvl = data_src->mem_lvl;
538 u64 snoop = data_src->mem_snoop;
539 u64 snoopx = data_src->mem_snoopx;
540 u64 lock = data_src->mem_lock;
541 u64 blk = data_src->mem_blk;
542 /*
543 * Skylake might report unknown remote level via this
544 * bit, consider it when evaluating remote HITMs.
545 *
546 * Incase of power, remote field can also be used to denote cache
547 * accesses from the another core of same node. Hence, setting
548 * mrem only when HOPS is zero along with set remote field.
549 */
550 bool mrem = (data_src->mem_remote && !data_src->mem_hops);
551 int err = 0;
552
553#define HITM_INC(__f) \
554do { \
555 stats->__f++; \
556 stats->tot_hitm++; \
557} while (0)
558
559#define PEER_INC(__f) \
560do { \
561 stats->__f++; \
562 stats->tot_peer++; \
563} while (0)
564
565#define P(a, b) PERF_MEM_##a##_##b
566
567 stats->nr_entries++;
568
569 if (lock & P(LOCK, LOCKED)) stats->locks++;
570
571 if (blk & P(BLK, DATA)) stats->blk_data++;
572 if (blk & P(BLK, ADDR)) stats->blk_addr++;
573
574 if (op & P(OP, LOAD)) {
575 /* load */
576 stats->load++;
577
578 if (!daddr) {
579 stats->ld_noadrs++;
580 return -1;
581 }
582
583 if (lvl & P(LVL, HIT)) {
584 if (lvl & P(LVL, UNC)) stats->ld_uncache++;
585 if (lvl & P(LVL, IO)) stats->ld_io++;
586 if (lvl & P(LVL, LFB)) stats->ld_fbhit++;
587 if (lvl & P(LVL, L1 )) stats->ld_l1hit++;
588 if (lvl & P(LVL, L2)) {
589 stats->ld_l2hit++;
590
591 if (snoopx & P(SNOOPX, PEER))
592 PEER_INC(lcl_peer);
593 }
594 if (lvl & P(LVL, L3 )) {
595 if (snoop & P(SNOOP, HITM))
596 HITM_INC(lcl_hitm);
597 else
598 stats->ld_llchit++;
599
600 if (snoopx & P(SNOOPX, PEER))
601 PEER_INC(lcl_peer);
602 }
603
604 if (lvl & P(LVL, LOC_RAM)) {
605 stats->lcl_dram++;
606 if (snoop & P(SNOOP, HIT))
607 stats->ld_shared++;
608 else
609 stats->ld_excl++;
610 }
611
612 if ((lvl & P(LVL, REM_RAM1)) ||
613 (lvl & P(LVL, REM_RAM2)) ||
614 mrem) {
615 stats->rmt_dram++;
616 if (snoop & P(SNOOP, HIT))
617 stats->ld_shared++;
618 else
619 stats->ld_excl++;
620 }
621 }
622
623 if ((lvl & P(LVL, REM_CCE1)) ||
624 (lvl & P(LVL, REM_CCE2)) ||
625 mrem) {
626 if (snoop & P(SNOOP, HIT)) {
627 stats->rmt_hit++;
628 } else if (snoop & P(SNOOP, HITM)) {
629 HITM_INC(rmt_hitm);
630 } else if (snoopx & P(SNOOPX, PEER)) {
631 stats->rmt_hit++;
632 PEER_INC(rmt_peer);
633 }
634 }
635
636 if ((lvl & P(LVL, MISS)))
637 stats->ld_miss++;
638
639 } else if (op & P(OP, STORE)) {
640 /* store */
641 stats->store++;
642
643 if (!daddr) {
644 stats->st_noadrs++;
645 return -1;
646 }
647
648 if (lvl & P(LVL, HIT)) {
649 if (lvl & P(LVL, UNC)) stats->st_uncache++;
650 if (lvl & P(LVL, L1 )) stats->st_l1hit++;
651 }
652 if (lvl & P(LVL, MISS))
653 if (lvl & P(LVL, L1)) stats->st_l1miss++;
654 if (lvl & P(LVL, NA))
655 stats->st_na++;
656 } else {
657 /* unparsable data_src? */
658 stats->noparse++;
659 return -1;
660 }
661
662 if (!mi->daddr.ms.map || !mi->iaddr.ms.map) {
663 stats->nomap++;
664 return -1;
665 }
666
667#undef P
668#undef HITM_INC
669 return err;
670}
671
672void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add)
673{
674 stats->nr_entries += add->nr_entries;
675
676 stats->locks += add->locks;
677 stats->store += add->store;
678 stats->st_uncache += add->st_uncache;
679 stats->st_noadrs += add->st_noadrs;
680 stats->st_l1hit += add->st_l1hit;
681 stats->st_l1miss += add->st_l1miss;
682 stats->st_na += add->st_na;
683 stats->load += add->load;
684 stats->ld_excl += add->ld_excl;
685 stats->ld_shared += add->ld_shared;
686 stats->ld_uncache += add->ld_uncache;
687 stats->ld_io += add->ld_io;
688 stats->ld_miss += add->ld_miss;
689 stats->ld_noadrs += add->ld_noadrs;
690 stats->ld_fbhit += add->ld_fbhit;
691 stats->ld_l1hit += add->ld_l1hit;
692 stats->ld_l2hit += add->ld_l2hit;
693 stats->ld_llchit += add->ld_llchit;
694 stats->lcl_hitm += add->lcl_hitm;
695 stats->rmt_hitm += add->rmt_hitm;
696 stats->tot_hitm += add->tot_hitm;
697 stats->lcl_peer += add->lcl_peer;
698 stats->rmt_peer += add->rmt_peer;
699 stats->tot_peer += add->tot_peer;
700 stats->rmt_hit += add->rmt_hit;
701 stats->lcl_dram += add->lcl_dram;
702 stats->rmt_dram += add->rmt_dram;
703 stats->blk_data += add->blk_data;
704 stats->blk_addr += add->blk_addr;
705 stats->nomap += add->nomap;
706 stats->noparse += add->noparse;
707}