perfmon: added intel internal io pmu support
[vpp.git] / src / plugins / perfmon / intel / uncore.c
1 /*
2  * Copyright (c) 2020 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/vnet.h>
17 #include <vppinfra/linux/sysfs.h>
18 #include <perfmon/perfmon.h>
19 #include <perfmon/intel/core.h>
20 #include <perfmon/intel/uncore.h>
21
22 VLIB_REGISTER_LOG_CLASS (if_intel_uncore_log, static) = {
23   .class_name = "perfmon",
24   .subclass_name = "intel-uncore",
25 };
26
27 #define log_debug(fmt, ...)                                                   \
28   vlib_log_debug (if_intel_uncore_log.class, fmt, __VA_ARGS__)
29 #define log_warn(fmt, ...)                                                    \
30   vlib_log_warn (if_intel_uncore_log.class, fmt, __VA_ARGS__)
31 #define log_err(fmt, ...)                                                     \
32   vlib_log_err (if_intel_uncore_log.class, fmt, __VA_ARGS__)
33
34 #define PERF_INTEL_CODE(event, umask, edge, any, inv, cmask)                  \
35   ((event) | (umask) << 8 | (edge) << 18 | (any) << 21 | (inv) << 23 |        \
36    (cmask) << 24)
37
38 static intel_uncore_unit_type_names_t uncore_unit_names[] = {
39   { INTEL_UNCORE_UNIT_IIO,
40     PERFMON_STRINGS ("PCIe0", "PCIe1", "MCP", "PCIe2", "PCIe3", "CBDMA/DMI") }
41 };
42
43 static perfmon_event_t intel_uncore_events[] = {
44 #define _(unit, event, umask, ch_mask, fc_mask, n, suffix, desc)              \
45   [INTEL_UNCORE_E_##unit##_##n##_##suffix] = {                                \
46     .config =                                                                 \
47       (event) | (umask) << 8 | (u64) (ch_mask) << 36 | (u64) (fc_mask) << 48, \
48     .name = #n "." #suffix,                                                   \
49     .description = desc,                                                      \
50     .type_from_instance = 1,                                                  \
51     .instance_type = INTEL_UNCORE_UNIT_##unit,                                \
52   },
53
54   foreach_intel_uncore_event
55 #undef _
56 };
57
58 static int
59 intel_uncore_instance_name_cmp (void *v1, void *v2)
60 {
61   perfmon_instance_t *i1 = v1;
62   perfmon_instance_t *i2 = v2;
63   return strcmp (i1->name, i2->name);
64 }
65
66 static_always_inline u8 *
67 format_instance_name (intel_uncore_unit_type_t u, char *unit_fmt, u8 socket_id,
68                       u8 ubox)
69 {
70   u8 *s = 0;
71
72   /* uncore ubox may have specific names */
73   for (u8 i = 0; i < ARRAY_LEN (uncore_unit_names); i++)
74     {
75       intel_uncore_unit_type_names_t *n = &uncore_unit_names[i];
76
77       if (n->unit_type == u)
78         {
79           u8 *fmt = 0;
80
81           fmt = format (0, "%s (%s)", unit_fmt, (n->unit_names[ubox]));
82           s = format (0, (char *) fmt, socket_id, ubox);
83           vec_free (fmt);
84
85           return s;
86         }
87     }
88
89   return format (0, unit_fmt, socket_id, ubox);
90 }
91
92 static void
93 intel_uncore_add_unit (perfmon_source_t *src, intel_uncore_unit_type_t u,
94                        char *name, char *type_str, char *fmt,
95                        int *socket_by_cpu_id)
96 {
97   static char *base_path = "/sys/bus/event_source/devices/uncore";
98   clib_error_t *err;
99   clib_bitmap_t *cpumask = 0;
100   perfmon_instance_t *in;
101   perfmon_instance_type_t *it;
102   u8 *s = 0;
103   int i = 0, j;
104   u32 perf_type;
105
106   vec_validate (src->instances_by_type, u);
107   it = vec_elt_at_index (src->instances_by_type, u);
108   it->name = type_str;
109
110   while (1)
111     {
112       s = format (s, "%s_%s_%u/type%c", base_path, name, i, 0);
113       if ((err = clib_sysfs_read ((char *) s, "%u", &perf_type)))
114         break;
115       vec_reset_length (s);
116
117       s = format (s, "%s_%s_%u/cpumask%c", base_path, name, i, 0);
118       if ((err = clib_sysfs_read ((char *) s, "%U", unformat_bitmap_list,
119                                   &cpumask)))
120         break;
121       vec_reset_length (s);
122
123       clib_bitmap_foreach (j, cpumask)
124         {
125           vec_add2 (it->instances, in, 1);
126           in->type = perf_type;
127           in->cpu = j;
128           in->pid = -1;
129           in->name =
130             (char *) format_instance_name (u, fmt, socket_by_cpu_id[j], i);
131           vec_terminate_c_string (in->name);
132           log_debug ("found %s %s", type_str, in->name);
133         }
134       i++;
135     };
136   clib_error_free (err);
137   clib_bitmap_free (cpumask);
138   vec_free (s);
139 }
140
141 static clib_error_t *
142 intel_uncore_init (vlib_main_t *vm, perfmon_source_t *src)
143 {
144   clib_error_t *err = 0;
145   clib_bitmap_t *node_bitmap = 0, *cpumask = 0;
146   int *numa_by_cpu_id = 0;
147   u32 i, j;
148   u8 *s = 0;
149
150   if ((err = clib_sysfs_read ("/sys/devices/system/node/has_cpu", "%U",
151                               unformat_bitmap_list, &node_bitmap)))
152     {
153       clib_error_free (err);
154       return clib_error_return (0, "failed to discover numa topology");
155     }
156
157   clib_bitmap_foreach (i, node_bitmap)
158     {
159       s = format (s, "/sys/devices/system/node/node%u/cpulist%c", i, 0);
160       if ((err = clib_sysfs_read ((char *) s, "%U", unformat_bitmap_list,
161                                   &cpumask)))
162         {
163           clib_error_free (err);
164           err = clib_error_return (0, "failed to discover numa topology");
165           goto done;
166         }
167
168       if (!cpumask)
169         {
170           clib_error_free (err);
171           err = clib_error_return (
172             0, "while discovering numa topology: cpumask unexpectedly NULL");
173           goto done;
174         }
175
176       clib_bitmap_foreach (j, cpumask)
177         {
178           vec_validate_init_empty (numa_by_cpu_id, j, -1);
179           numa_by_cpu_id[j] = i;
180         }
181       clib_bitmap_free (cpumask);
182       vec_reset_length (s);
183     }
184
185 #define _(t, n, name, fmt)                                                    \
186   intel_uncore_add_unit (src, INTEL_UNCORE_UNIT_##t, n, name, fmt,            \
187                          numa_by_cpu_id);
188   foreach_intel_uncore_unit_type;
189 #undef _
190
191   for (i = 0, j = 0; i < vec_len (src->instances_by_type); i++)
192     {
193       perfmon_instance_type_t *it;
194
195       it = vec_elt_at_index (src->instances_by_type, i);
196       vec_sort_with_function (it->instances, intel_uncore_instance_name_cmp);
197       j += vec_len (it->instances);
198     }
199
200   if (j == 0)
201     {
202       vec_free (src->instances_by_type);
203       return clib_error_return (0, "no uncore units found");
204     }
205
206 done:
207   vec_free (s);
208   vec_free (cpumask);
209   vec_free (node_bitmap);
210   vec_free (numa_by_cpu_id);
211   return err;
212 }
213
214 format_function_t format_intel_core_config;
215
216 PERFMON_REGISTER_SOURCE (intel_uncore) = {
217   .name = "intel-uncore",
218   .description = "intel uncore events",
219   .events = intel_uncore_events,
220   .n_events = INTEL_UNCORE_N_EVENTS,
221   .init_fn = intel_uncore_init,
222   .format_config = format_intel_core_config,
223 };