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 memset(stats, 0, sz); 123 if (stats) { 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 169 /* Clear the new struct cluster_node area */ 170 cluster = RTE_PTR_ADD(stats, stats->sz), 171 memset(cluster, 0, stats->cluster_node_size); 172 memcpy(cluster->stat.name, graph_node->node->name, RTE_NODE_NAMESIZE); 173 cluster->stat.id = graph_node->node->id; 174 cluster->stat.hz = rte_get_timer_hz(); 175 node = graph_node_id_to_ptr(graph, id); 176 if (node == NULL) 177 SET_ERR_JMP(ENOENT, err, "Failed to find node %s in graph %s", 178 graph_node->node->name, graph->name); 179 cluster->nodes[cluster->nb_nodes++] = node; 180 181 stats->sz += stats->cluster_node_size; 182 stats->max_nodes++; 183 *stats_in = stats; 184 185 return 0; 186 err: 187 return -rte_errno; 188 } 189 190 static void 191 stats_mem_fini(struct rte_graph_cluster_stats *stats) 192 { 193 free(stats); 194 } 195 196 static void 197 cluster_init(struct cluster *cluster) 198 { 199 memset(cluster, 0, sizeof(*cluster)); 200 } 201 202 static int 203 cluster_add(struct cluster *cluster, struct graph *graph) 204 { 205 rte_graph_t count; 206 size_t sz; 207 208 /* Skip the if graph is already added to cluster */ 209 for (count = 0; count < cluster->nb_graphs; count++) 210 if (cluster->graphs[count] == graph) 211 return 0; 212 213 /* Expand the cluster if required to store graph objects */ 214 if (cluster->nb_graphs + 1 > cluster->size) { 215 cluster->size = RTE_MAX(1, cluster->size * 2); 216 sz = sizeof(struct graph *) * cluster->size; 217 cluster->graphs = realloc(cluster->graphs, sz); 218 if (cluster->graphs == NULL) 219 SET_ERR_JMP(ENOMEM, free, "Failed to realloc"); 220 } 221 222 /* Add graph to cluster */ 223 cluster->graphs[cluster->nb_graphs++] = graph; 224 return 0; 225 226 free: 227 return -rte_errno; 228 } 229 230 static void 231 cluster_fini(struct cluster *cluster) 232 { 233 if (cluster->graphs) 234 free(cluster->graphs); 235 } 236 237 static int 238 expand_pattern_to_cluster(struct cluster *cluster, const char *pattern) 239 { 240 struct graph_head *graph_head = graph_list_head_get(); 241 struct graph *graph; 242 bool found = false; 243 244 /* Check for pattern match */ 245 STAILQ_FOREACH(graph, graph_head, next) { 246 if (fnmatch(pattern, graph->name, 0) == 0) { 247 if (cluster_add(cluster, graph)) 248 goto fail; 249 found = true; 250 } 251 } 252 if (found == false) 253 SET_ERR_JMP(EFAULT, fail, "Pattern %s graph not found", 254 pattern); 255 256 return 0; 257 fail: 258 return -rte_errno; 259 } 260 261 struct rte_graph_cluster_stats * 262 rte_graph_cluster_stats_create(const struct rte_graph_cluster_stats_param *prm) 263 { 264 struct rte_graph_cluster_stats *stats, *rc = NULL; 265 struct graph_node *graph_node; 266 struct cluster cluster; 267 struct graph *graph; 268 const char *pattern; 269 rte_graph_t i; 270 271 /* Sanity checks */ 272 if (!rte_graph_has_stats_feature()) 273 SET_ERR_JMP(EINVAL, fail, "Stats feature is not enabled"); 274 275 if (prm == NULL) 276 SET_ERR_JMP(EINVAL, fail, "Invalid param"); 277 278 if (prm->graph_patterns == NULL || prm->nb_graph_patterns == 0) 279 SET_ERR_JMP(EINVAL, fail, "Invalid graph param"); 280 281 cluster_init(&cluster); 282 283 graph_spinlock_lock(); 284 /* Expand graph pattern and add the graph to the cluster */ 285 for (i = 0; i < prm->nb_graph_patterns; i++) { 286 pattern = prm->graph_patterns[i]; 287 if (expand_pattern_to_cluster(&cluster, pattern)) 288 goto bad_pattern; 289 } 290 291 /* Alloc the stats memory */ 292 stats = stats_mem_init(&cluster, prm); 293 if (stats == NULL) 294 SET_ERR_JMP(ENOMEM, bad_pattern, "Failed alloc stats memory"); 295 296 /* Iterate over M(Graph) x N (Nodes in graph) */ 297 for (i = 0; i < cluster.nb_graphs; i++) { 298 graph = cluster.graphs[i]; 299 STAILQ_FOREACH(graph_node, &graph->node_list, next) { 300 struct rte_graph *graph_fp = graph->graph; 301 if (stats_mem_populate(&stats, graph_fp, graph_node)) 302 goto realloc_fail; 303 } 304 } 305 306 /* Finally copy to hugepage memory to avoid pressure on rte_realloc */ 307 rc = rte_malloc_socket(NULL, stats->sz, 0, stats->socket_id); 308 if (rc) 309 rte_memcpy(rc, stats, stats->sz); 310 else 311 SET_ERR_JMP(ENOMEM, realloc_fail, "rte_malloc failed"); 312 313 realloc_fail: 314 stats_mem_fini(stats); 315 bad_pattern: 316 graph_spinlock_unlock(); 317 cluster_fini(&cluster); 318 fail: 319 return rc; 320 } 321 322 void 323 rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat) 324 { 325 return rte_free(stat); 326 } 327 328 static inline void 329 cluster_node_arregate_stats(struct cluster_node *cluster) 330 { 331 uint64_t calls = 0, cycles = 0, objs = 0, realloc_count = 0; 332 struct rte_graph_cluster_node_stats *stat = &cluster->stat; 333 struct rte_node *node; 334 rte_node_t count; 335 336 for (count = 0; count < cluster->nb_nodes; count++) { 337 node = cluster->nodes[count]; 338 339 calls += node->total_calls; 340 objs += node->total_objs; 341 cycles += node->total_cycles; 342 realloc_count += node->realloc_count; 343 } 344 345 stat->calls = calls; 346 stat->objs = objs; 347 stat->cycles = cycles; 348 stat->ts = rte_get_timer_cycles(); 349 stat->realloc_count = realloc_count; 350 } 351 352 static inline void 353 cluster_node_store_prev_stats(struct cluster_node *cluster) 354 { 355 struct rte_graph_cluster_node_stats *stat = &cluster->stat; 356 357 stat->prev_ts = stat->ts; 358 stat->prev_calls = stat->calls; 359 stat->prev_objs = stat->objs; 360 stat->prev_cycles = stat->cycles; 361 } 362 363 void 364 rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat, bool skip_cb) 365 { 366 struct cluster_node *cluster; 367 rte_node_t count; 368 int rc = 0; 369 370 cluster = stat->clusters; 371 372 for (count = 0; count < stat->max_nodes; count++) { 373 cluster_node_arregate_stats(cluster); 374 if (!skip_cb) 375 rc = stat->fn(!count, (count == stat->max_nodes - 1), 376 stat->cookie, &cluster->stat); 377 cluster_node_store_prev_stats(cluster); 378 if (rc) 379 break; 380 cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size); 381 } 382 } 383 384 void 385 rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat) 386 { 387 struct cluster_node *cluster; 388 rte_node_t count; 389 390 cluster = stat->clusters; 391 392 for (count = 0; count < stat->max_nodes; count++) { 393 struct rte_graph_cluster_node_stats *node = &cluster->stat; 394 395 node->ts = 0; 396 node->calls = 0; 397 node->objs = 0; 398 node->cycles = 0; 399 node->prev_ts = 0; 400 node->prev_calls = 0; 401 node->prev_objs = 0; 402 node->prev_cycles = 0; 403 node->realloc_count = 0; 404 cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size); 405 } 406 } 407