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