xref: /dpdk/lib/graph/graph_stats.c (revision 4ed890495594a71e618d62fd975c5868915eb4a1)
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 #include <stdlib.h>
8 
9 #include <rte_common.h>
10 #include <rte_errno.h>
11 #include <rte_malloc.h>
12 
13 #include "graph_private.h"
14 
15 /* Capture all graphs of cluster */
16 struct cluster {
17 	rte_graph_t nb_graphs;
18 	rte_graph_t size;
19 
20 	struct graph **graphs;
21 };
22 
23 /* Capture same node ID across cluster  */
24 struct cluster_node {
25 	struct rte_graph_cluster_node_stats stat;
26 	rte_node_t nb_nodes;
27 
28 	struct rte_node *nodes[];
29 };
30 
31 struct rte_graph_cluster_stats {
32 	/* Header */
33 	rte_graph_cluster_stats_cb_t fn;
34 	uint32_t cluster_node_size; /* Size of struct cluster_node */
35 	rte_node_t max_nodes;
36 	int socket_id;
37 	void *cookie;
38 	size_t sz;
39 
40 	struct cluster_node clusters[];
41 } __rte_cache_aligned;
42 
43 #define boarder()                                                              \
44 	fprintf(f, "+-------------------------------+---------------+--------" \
45 		   "-------+---------------+---------------+---------------+-" \
46 		   "----------+\n")
47 
48 static inline void
49 print_banner(FILE *f)
50 {
51 	boarder();
52 	fprintf(f, "%-32s%-16s%-16s%-16s%-16s%-16s%-16s\n", "|Node", "|calls",
53 		"|objs", "|realloc_count", "|objs/call", "|objs/sec(10E6)",
54 		"|cycles/call|");
55 	boarder();
56 }
57 
58 static inline void
59 print_node(FILE *f, const struct rte_graph_cluster_node_stats *stat)
60 {
61 	double objs_per_call, objs_per_sec, cycles_per_call, ts_per_hz;
62 	const uint64_t prev_calls = stat->prev_calls;
63 	const uint64_t prev_objs = stat->prev_objs;
64 	const uint64_t cycles = stat->cycles;
65 	const uint64_t calls = stat->calls;
66 	const uint64_t objs = stat->objs;
67 	uint64_t call_delta;
68 
69 	call_delta = calls - prev_calls;
70 	objs_per_call =
71 		call_delta ? (double)((objs - prev_objs) / call_delta) : 0;
72 	cycles_per_call =
73 		call_delta ? (double)((cycles - stat->prev_cycles) / call_delta)
74 			   : 0;
75 	ts_per_hz = (double)((stat->ts - stat->prev_ts) / stat->hz);
76 	objs_per_sec = ts_per_hz ? (objs - prev_objs) / ts_per_hz : 0;
77 	objs_per_sec /= 1000000;
78 
79 	fprintf(f,
80 		"|%-31s|%-15" PRIu64 "|%-15" PRIu64 "|%-15" PRIu64
81 		"|%-15.3f|%-15.6f|%-11.4f|\n",
82 		stat->name, calls, objs, stat->realloc_count, objs_per_call,
83 		objs_per_sec, cycles_per_call);
84 }
85 
86 static int
87 graph_cluster_stats_cb(bool is_first, bool is_last, void *cookie,
88 		       const struct rte_graph_cluster_node_stats *stat)
89 {
90 	FILE *f = cookie;
91 
92 	if (unlikely(is_first))
93 		print_banner(f);
94 	if (stat->objs)
95 		print_node(f, stat);
96 	if (unlikely(is_last))
97 		boarder();
98 
99 	return 0;
100 };
101 
102 static struct rte_graph_cluster_stats *
103 stats_mem_init(struct cluster *cluster,
104 	       const struct rte_graph_cluster_stats_param *prm)
105 {
106 	size_t sz = sizeof(struct rte_graph_cluster_stats);
107 	struct rte_graph_cluster_stats *stats;
108 	rte_graph_cluster_stats_cb_t fn;
109 	int socket_id = prm->socket_id;
110 	uint32_t cluster_node_size;
111 
112 	/* Fix up callback */
113 	fn = prm->fn;
114 	if (fn == NULL)
115 		fn = graph_cluster_stats_cb;
116 
117 	cluster_node_size = sizeof(struct cluster_node);
118 	/* For a given cluster, max nodes will be the max number of graphs */
119 	cluster_node_size += cluster->nb_graphs * sizeof(struct rte_node *);
120 	cluster_node_size = RTE_ALIGN(cluster_node_size, RTE_CACHE_LINE_SIZE);
121 
122 	stats = realloc(NULL, sz);
123 	if (stats) {
124 		memset(stats, 0, sz);
125 		stats->fn = fn;
126 		stats->cluster_node_size = cluster_node_size;
127 		stats->max_nodes = 0;
128 		stats->socket_id = socket_id;
129 		stats->cookie = prm->cookie;
130 		stats->sz = sz;
131 	}
132 
133 	return stats;
134 }
135 
136 static int
137 stats_mem_populate(struct rte_graph_cluster_stats **stats_in,
138 		   struct rte_graph *graph, struct graph_node *graph_node)
139 {
140 	struct rte_graph_cluster_stats *stats = *stats_in;
141 	rte_node_t id = graph_node->node->id;
142 	struct cluster_node *cluster;
143 	struct rte_node *node;
144 	rte_node_t count;
145 
146 	cluster = stats->clusters;
147 
148 	/* Iterate over cluster node array to find node ID match */
149 	for (count = 0; count < stats->max_nodes; count++) {
150 		/* Found an existing node in the reel */
151 		if (cluster->stat.id == id) {
152 			node = graph_node_id_to_ptr(graph, id);
153 			if (node == NULL)
154 				SET_ERR_JMP(
155 					ENOENT, err,
156 					"Failed to find node %s in graph %s",
157 					graph_node->node->name, graph->name);
158 
159 			cluster->nodes[cluster->nb_nodes++] = node;
160 			return 0;
161 		}
162 		cluster = RTE_PTR_ADD(cluster, stats->cluster_node_size);
163 	}
164 
165 	/* Hey, it is a new node, allocate space for it in the reel */
166 	stats = realloc(stats, stats->sz + stats->cluster_node_size);
167 	if (stats == NULL)
168 		SET_ERR_JMP(ENOMEM, err, "Realloc failed");
169 	*stats_in = NULL;
170 
171 	/* Clear the new struct cluster_node area */
172 	cluster = RTE_PTR_ADD(stats, stats->sz),
173 	memset(cluster, 0, stats->cluster_node_size);
174 	memcpy(cluster->stat.name, graph_node->node->name, RTE_NODE_NAMESIZE);
175 	cluster->stat.id = graph_node->node->id;
176 	cluster->stat.hz = rte_get_timer_hz();
177 	node = graph_node_id_to_ptr(graph, id);
178 	if (node == NULL)
179 		SET_ERR_JMP(ENOENT, free, "Failed to find node %s in graph %s",
180 			    graph_node->node->name, graph->name);
181 	cluster->nodes[cluster->nb_nodes++] = node;
182 
183 	stats->sz += stats->cluster_node_size;
184 	stats->max_nodes++;
185 	*stats_in = stats;
186 
187 	return 0;
188 free:
189 	free(stats);
190 err:
191 	return -rte_errno;
192 }
193 
194 static void
195 stats_mem_fini(struct rte_graph_cluster_stats *stats)
196 {
197 	free(stats);
198 }
199 
200 static void
201 cluster_init(struct cluster *cluster)
202 {
203 	memset(cluster, 0, sizeof(*cluster));
204 }
205 
206 static int
207 cluster_add(struct cluster *cluster, struct graph *graph)
208 {
209 	rte_graph_t count;
210 	size_t sz;
211 
212 	/* Skip the if graph is already added to cluster */
213 	for (count = 0; count < cluster->nb_graphs; count++)
214 		if (cluster->graphs[count] == graph)
215 			return 0;
216 
217 	/* Expand the cluster if required to store graph objects */
218 	if (cluster->nb_graphs + 1 > cluster->size) {
219 		cluster->size = RTE_MAX(1, cluster->size * 2);
220 		sz = sizeof(struct graph *) * cluster->size;
221 		cluster->graphs = realloc(cluster->graphs, sz);
222 		if (cluster->graphs == NULL)
223 			SET_ERR_JMP(ENOMEM, free, "Failed to realloc");
224 	}
225 
226 	/* Add graph to cluster */
227 	cluster->graphs[cluster->nb_graphs++] = graph;
228 	return 0;
229 
230 free:
231 	return -rte_errno;
232 }
233 
234 static void
235 cluster_fini(struct cluster *cluster)
236 {
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 }
410