doc: announce behavior change in bus probing
[dpdk.git] / lib / graph / graph_stats.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(C) 2020 Marvell International Ltd.
3  */
4
5 #include <fnmatch.h>
6 #include <stdbool.h>
7
8 #include <rte_common.h>
9 #include <rte_errno.h>
10 #include <rte_malloc.h>
11
12 #include "graph_private.h"
13
14 /* Capture all graphs of cluster */
15 struct cluster {
16         rte_graph_t nb_graphs;
17         rte_graph_t size;
18
19         struct graph **graphs;
20 };
21
22 /* Capture same node ID across cluster  */
23 struct cluster_node {
24         struct rte_graph_cluster_node_stats stat;
25         rte_node_t nb_nodes;
26
27         struct rte_node *nodes[];
28 };
29
30 struct rte_graph_cluster_stats {
31         /* Header */
32         rte_graph_cluster_stats_cb_t fn;
33         uint32_t cluster_node_size; /* Size of struct cluster_node */
34         rte_node_t max_nodes;
35         int socket_id;
36         void *cookie;
37         size_t sz;
38
39         struct cluster_node clusters[];
40 } __rte_cache_aligned;
41
42 #define boarder()                                                              \
43         fprintf(f, "+-------------------------------+---------------+--------" \
44                    "-------+---------------+---------------+---------------+-" \
45                    "----------+\n")
46
47 static inline void
48 print_banner(FILE *f)
49 {
50         boarder();
51         fprintf(f, "%-32s%-16s%-16s%-16s%-16s%-16s%-16s\n", "|Node", "|calls",
52                 "|objs", "|realloc_count", "|objs/call", "|objs/sec(10E6)",
53                 "|cycles/call|");
54         boarder();
55 }
56
57 static inline void
58 print_node(FILE *f, const struct rte_graph_cluster_node_stats *stat)
59 {
60         double objs_per_call, objs_per_sec, cycles_per_call, ts_per_hz;
61         const uint64_t prev_calls = stat->prev_calls;
62         const uint64_t prev_objs = stat->prev_objs;
63         const uint64_t cycles = stat->cycles;
64         const uint64_t calls = stat->calls;
65         const uint64_t objs = stat->objs;
66         uint64_t call_delta;
67
68         call_delta = calls - prev_calls;
69         objs_per_call =
70                 call_delta ? (double)((objs - prev_objs) / call_delta) : 0;
71         cycles_per_call =
72                 call_delta ? (double)((cycles - stat->prev_cycles) / call_delta)
73                            : 0;
74         ts_per_hz = (double)((stat->ts - stat->prev_ts) / stat->hz);
75         objs_per_sec = ts_per_hz ? (objs - prev_objs) / ts_per_hz : 0;
76         objs_per_sec /= 1000000;
77
78         fprintf(f,
79                 "|%-31s|%-15" PRIu64 "|%-15" PRIu64 "|%-15" PRIu64
80                 "|%-15.3f|%-15.6f|%-11.4f|\n",
81                 stat->name, calls, objs, stat->realloc_count, objs_per_call,
82                 objs_per_sec, cycles_per_call);
83 }
84
85 static int
86 graph_cluster_stats_cb(bool is_first, bool is_last, void *cookie,
87                        const struct rte_graph_cluster_node_stats *stat)
88 {
89         FILE *f = cookie;
90
91         if (unlikely(is_first))
92                 print_banner(f);
93         if (stat->objs)
94                 print_node(f, stat);
95         if (unlikely(is_last))
96                 boarder();
97
98         return 0;
99 };
100
101 static struct rte_graph_cluster_stats *
102 stats_mem_init(struct cluster *cluster,
103                const struct rte_graph_cluster_stats_param *prm)
104 {
105         size_t sz = sizeof(struct rte_graph_cluster_stats);
106         struct rte_graph_cluster_stats *stats;
107         rte_graph_cluster_stats_cb_t fn;
108         int socket_id = prm->socket_id;
109         uint32_t cluster_node_size;
110
111         /* Fix up callback */
112         fn = prm->fn;
113         if (fn == NULL)
114                 fn = graph_cluster_stats_cb;
115
116         cluster_node_size = sizeof(struct cluster_node);
117         /* For a given cluster, max nodes will be the max number of graphs */
118         cluster_node_size += cluster->nb_graphs * sizeof(struct rte_node *);
119         cluster_node_size = RTE_ALIGN(cluster_node_size, RTE_CACHE_LINE_SIZE);
120
121         stats = realloc(NULL, sz);
122         if (stats) {
123                 memset(stats, 0, sz);
124                 stats->fn = fn;
125                 stats->cluster_node_size = cluster_node_size;
126                 stats->max_nodes = 0;
127                 stats->socket_id = socket_id;
128                 stats->cookie = prm->cookie;
129                 stats->sz = sz;
130         }
131
132         return stats;
133 }
134
135 static int
136 stats_mem_populate(struct rte_graph_cluster_stats **stats_in,
137                    struct rte_graph *graph, struct graph_node *graph_node)
138 {
139         struct rte_graph_cluster_stats *stats = *stats_in;
140         rte_node_t id = graph_node->node->id;
141         struct cluster_node *cluster;
142         struct rte_node *node;
143         rte_node_t count;
144
145         cluster = stats->clusters;
146
147         /* Iterate over cluster node array to find node ID match */
148         for (count = 0; count < stats->max_nodes; count++) {
149                 /* Found an existing node in the reel */
150                 if (cluster->stat.id == id) {
151                         node = graph_node_id_to_ptr(graph, id);
152                         if (node == NULL)
153                                 SET_ERR_JMP(
154                                         ENOENT, err,
155                                         "Failed to find node %s in graph %s",
156                                         graph_node->node->name, graph->name);
157
158                         cluster->nodes[cluster->nb_nodes++] = node;
159                         return 0;
160                 }
161                 cluster = RTE_PTR_ADD(cluster, stats->cluster_node_size);
162         }
163
164         /* Hey, it is a new node, allocate space for it in the reel */
165         stats = realloc(stats, stats->sz + stats->cluster_node_size);
166         if (stats == NULL)
167                 SET_ERR_JMP(ENOMEM, err, "Realloc failed");
168         *stats_in = NULL;
169
170         /* Clear the new struct cluster_node area */
171         cluster = RTE_PTR_ADD(stats, stats->sz),
172         memset(cluster, 0, stats->cluster_node_size);
173         memcpy(cluster->stat.name, graph_node->node->name, RTE_NODE_NAMESIZE);
174         cluster->stat.id = graph_node->node->id;
175         cluster->stat.hz = rte_get_timer_hz();
176         node = graph_node_id_to_ptr(graph, id);
177         if (node == NULL)
178                 SET_ERR_JMP(ENOENT, free, "Failed to find node %s in graph %s",
179                             graph_node->node->name, graph->name);
180         cluster->nodes[cluster->nb_nodes++] = node;
181
182         stats->sz += stats->cluster_node_size;
183         stats->max_nodes++;
184         *stats_in = stats;
185
186         return 0;
187 free:
188         free(stats);
189 err:
190         return -rte_errno;
191 }
192
193 static void
194 stats_mem_fini(struct rte_graph_cluster_stats *stats)
195 {
196         free(stats);
197 }
198
199 static void
200 cluster_init(struct cluster *cluster)
201 {
202         memset(cluster, 0, sizeof(*cluster));
203 }
204
205 static int
206 cluster_add(struct cluster *cluster, struct graph *graph)
207 {
208         rte_graph_t count;
209         size_t sz;
210
211         /* Skip the if graph is already added to cluster */
212         for (count = 0; count < cluster->nb_graphs; count++)
213                 if (cluster->graphs[count] == graph)
214                         return 0;
215
216         /* Expand the cluster if required to store graph objects */
217         if (cluster->nb_graphs + 1 > cluster->size) {
218                 cluster->size = RTE_MAX(1, cluster->size * 2);
219                 sz = sizeof(struct graph *) * cluster->size;
220                 cluster->graphs = realloc(cluster->graphs, sz);
221                 if (cluster->graphs == NULL)
222                         SET_ERR_JMP(ENOMEM, free, "Failed to realloc");
223         }
224
225         /* Add graph to cluster */
226         cluster->graphs[cluster->nb_graphs++] = graph;
227         return 0;
228
229 free:
230         return -rte_errno;
231 }
232
233 static void
234 cluster_fini(struct cluster *cluster)
235 {
236         if (cluster->graphs)
237                 free(cluster->graphs);
238 }
239
240 static int
241 expand_pattern_to_cluster(struct cluster *cluster, const char *pattern)
242 {
243         struct graph_head *graph_head = graph_list_head_get();
244         struct graph *graph;
245         bool found = false;
246
247         /* Check for pattern match */
248         STAILQ_FOREACH(graph, graph_head, next) {
249                 if (fnmatch(pattern, graph->name, 0) == 0) {
250                         if (cluster_add(cluster, graph))
251                                 goto fail;
252                         found = true;
253                 }
254         }
255         if (found == false)
256                 SET_ERR_JMP(EFAULT, fail, "Pattern %s graph not found",
257                             pattern);
258
259         return 0;
260 fail:
261         return -rte_errno;
262 }
263
264 struct rte_graph_cluster_stats *
265 rte_graph_cluster_stats_create(const struct rte_graph_cluster_stats_param *prm)
266 {
267         struct rte_graph_cluster_stats *stats, *rc = NULL;
268         struct graph_node *graph_node;
269         struct cluster cluster;
270         struct graph *graph;
271         const char *pattern;
272         rte_graph_t i;
273
274         /* Sanity checks */
275         if (!rte_graph_has_stats_feature())
276                 SET_ERR_JMP(EINVAL, fail, "Stats feature is not enabled");
277
278         if (prm == NULL)
279                 SET_ERR_JMP(EINVAL, fail, "Invalid param");
280
281         if (prm->graph_patterns == NULL || prm->nb_graph_patterns == 0)
282                 SET_ERR_JMP(EINVAL, fail, "Invalid graph param");
283
284         cluster_init(&cluster);
285
286         graph_spinlock_lock();
287         /* Expand graph pattern and add the graph to the cluster */
288         for (i = 0; i < prm->nb_graph_patterns; i++) {
289                 pattern = prm->graph_patterns[i];
290                 if (expand_pattern_to_cluster(&cluster, pattern))
291                         goto bad_pattern;
292         }
293
294         /* Alloc the stats memory */
295         stats = stats_mem_init(&cluster, prm);
296         if (stats == NULL)
297                 SET_ERR_JMP(ENOMEM, bad_pattern, "Failed alloc stats memory");
298
299         /* Iterate over M(Graph) x N (Nodes in graph) */
300         for (i = 0; i < cluster.nb_graphs; i++) {
301                 graph = cluster.graphs[i];
302                 STAILQ_FOREACH(graph_node, &graph->node_list, next) {
303                         struct rte_graph *graph_fp = graph->graph;
304                         if (stats_mem_populate(&stats, graph_fp, graph_node))
305                                 goto realloc_fail;
306                 }
307         }
308
309         /* Finally copy to hugepage memory to avoid pressure on rte_realloc */
310         rc = rte_malloc_socket(NULL, stats->sz, 0, stats->socket_id);
311         if (rc)
312                 rte_memcpy(rc, stats, stats->sz);
313         else
314                 SET_ERR_JMP(ENOMEM, realloc_fail, "rte_malloc failed");
315
316 realloc_fail:
317         stats_mem_fini(stats);
318 bad_pattern:
319         graph_spinlock_unlock();
320         cluster_fini(&cluster);
321 fail:
322         return rc;
323 }
324
325 void
326 rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat)
327 {
328         return rte_free(stat);
329 }
330
331 static inline void
332 cluster_node_arregate_stats(struct cluster_node *cluster)
333 {
334         uint64_t calls = 0, cycles = 0, objs = 0, realloc_count = 0;
335         struct rte_graph_cluster_node_stats *stat = &cluster->stat;
336         struct rte_node *node;
337         rte_node_t count;
338
339         for (count = 0; count < cluster->nb_nodes; count++) {
340                 node = cluster->nodes[count];
341
342                 calls += node->total_calls;
343                 objs += node->total_objs;
344                 cycles += node->total_cycles;
345                 realloc_count += node->realloc_count;
346         }
347
348         stat->calls = calls;
349         stat->objs = objs;
350         stat->cycles = cycles;
351         stat->ts = rte_get_timer_cycles();
352         stat->realloc_count = realloc_count;
353 }
354
355 static inline void
356 cluster_node_store_prev_stats(struct cluster_node *cluster)
357 {
358         struct rte_graph_cluster_node_stats *stat = &cluster->stat;
359
360         stat->prev_ts = stat->ts;
361         stat->prev_calls = stat->calls;
362         stat->prev_objs = stat->objs;
363         stat->prev_cycles = stat->cycles;
364 }
365
366 void
367 rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat, bool skip_cb)
368 {
369         struct cluster_node *cluster;
370         rte_node_t count;
371         int rc = 0;
372
373         cluster = stat->clusters;
374
375         for (count = 0; count < stat->max_nodes; count++) {
376                 cluster_node_arregate_stats(cluster);
377                 if (!skip_cb)
378                         rc = stat->fn(!count, (count == stat->max_nodes - 1),
379                                       stat->cookie, &cluster->stat);
380                 cluster_node_store_prev_stats(cluster);
381                 if (rc)
382                         break;
383                 cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);
384         }
385 }
386
387 void
388 rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat)
389 {
390         struct cluster_node *cluster;
391         rte_node_t count;
392
393         cluster = stat->clusters;
394
395         for (count = 0; count < stat->max_nodes; count++) {
396                 struct rte_graph_cluster_node_stats *node = &cluster->stat;
397
398                 node->ts = 0;
399                 node->calls = 0;
400                 node->objs = 0;
401                 node->cycles = 0;
402                 node->prev_ts = 0;
403                 node->prev_calls = 0;
404                 node->prev_objs = 0;
405                 node->prev_cycles = 0;
406                 node->realloc_count = 0;
407                 cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);
408         }
409 }