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_debug.h> 11 #include <rte_errno.h> 12 #include <rte_malloc.h> 13 #include <rte_memzone.h> 14 #include <rte_spinlock.h> 15 #include <rte_string_fns.h> 16 17 #include "graph_private.h" 18 19 static struct graph_head graph_list = STAILQ_HEAD_INITIALIZER(graph_list); 20 static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER; 21 static rte_graph_t graph_id; 22 23 #define GRAPH_ID_CHECK(id) ID_CHECK(id, graph_id) 24 25 /* Private functions */ 26 struct graph_head * 27 graph_list_head_get(void) 28 { 29 return &graph_list; 30 } 31 32 void 33 graph_spinlock_lock(void) 34 { 35 rte_spinlock_lock(&graph_lock); 36 } 37 38 void 39 graph_spinlock_unlock(void) 40 { 41 rte_spinlock_unlock(&graph_lock); 42 } 43 44 static int 45 graph_node_add(struct graph *graph, struct node *node) 46 { 47 struct graph_node *graph_node; 48 size_t sz; 49 50 /* Skip the duplicate nodes */ 51 STAILQ_FOREACH(graph_node, &graph->node_list, next) 52 if (strncmp(node->name, graph_node->node->name, 53 RTE_NODE_NAMESIZE) == 0) 54 return 0; 55 56 /* Allocate new graph node object */ 57 sz = sizeof(*graph_node) + node->nb_edges * sizeof(struct node *); 58 graph_node = calloc(1, sz); 59 60 if (graph_node == NULL) 61 SET_ERR_JMP(ENOMEM, free, "Failed to calloc %s object", 62 node->name); 63 64 /* Initialize the graph node */ 65 graph_node->node = node; 66 67 /* Add to graph node list */ 68 STAILQ_INSERT_TAIL(&graph->node_list, graph_node, next); 69 return 0; 70 71 free: 72 free(graph_node); 73 return -rte_errno; 74 } 75 76 static struct graph_node * 77 node_to_graph_node(struct graph *graph, struct node *node) 78 { 79 struct graph_node *graph_node; 80 81 STAILQ_FOREACH(graph_node, &graph->node_list, next) 82 if (graph_node->node == node) 83 return graph_node; 84 85 SET_ERR_JMP(ENODEV, fail, "Found isolated node %s", node->name); 86 fail: 87 return NULL; 88 } 89 90 static int 91 graph_node_edges_add(struct graph *graph) 92 { 93 struct graph_node *graph_node; 94 struct node *adjacency; 95 const char *next; 96 rte_edge_t i; 97 98 STAILQ_FOREACH(graph_node, &graph->node_list, next) { 99 for (i = 0; i < graph_node->node->nb_edges; i++) { 100 next = graph_node->node->next_nodes[i]; 101 adjacency = node_from_name(next); 102 if (adjacency == NULL) 103 SET_ERR_JMP(EINVAL, fail, 104 "Node %s not registered", next); 105 if (graph_node_add(graph, adjacency)) 106 goto fail; 107 } 108 } 109 return 0; 110 fail: 111 return -rte_errno; 112 } 113 114 static int 115 graph_adjacency_list_update(struct graph *graph) 116 { 117 struct graph_node *graph_node, *tmp; 118 struct node *adjacency; 119 const char *next; 120 rte_edge_t i; 121 122 STAILQ_FOREACH(graph_node, &graph->node_list, next) { 123 for (i = 0; i < graph_node->node->nb_edges; i++) { 124 next = graph_node->node->next_nodes[i]; 125 adjacency = node_from_name(next); 126 if (adjacency == NULL) 127 SET_ERR_JMP(EINVAL, fail, 128 "Node %s not registered", next); 129 tmp = node_to_graph_node(graph, adjacency); 130 if (tmp == NULL) 131 goto fail; 132 graph_node->adjacency_list[i] = tmp; 133 } 134 } 135 136 return 0; 137 fail: 138 return -rte_errno; 139 } 140 141 static int 142 expand_pattern_to_node(struct graph *graph, const char *pattern) 143 { 144 struct node_head *node_head = node_list_head_get(); 145 bool found = false; 146 struct node *node; 147 148 /* Check for pattern match */ 149 STAILQ_FOREACH(node, node_head, next) { 150 if (fnmatch(pattern, node->name, 0) == 0) { 151 if (graph_node_add(graph, node)) 152 goto fail; 153 found = true; 154 } 155 } 156 if (found == false) 157 SET_ERR_JMP(EFAULT, fail, "Pattern %s node not found", pattern); 158 159 return 0; 160 fail: 161 return -rte_errno; 162 } 163 164 static void 165 graph_cleanup(struct graph *graph) 166 { 167 struct graph_node *graph_node; 168 169 while (!STAILQ_EMPTY(&graph->node_list)) { 170 graph_node = STAILQ_FIRST(&graph->node_list); 171 STAILQ_REMOVE_HEAD(&graph->node_list, next); 172 free(graph_node); 173 } 174 } 175 176 static int 177 graph_node_init(struct graph *graph) 178 { 179 struct graph_node *graph_node; 180 const char *name; 181 int rc; 182 183 STAILQ_FOREACH(graph_node, &graph->node_list, next) { 184 if (graph_node->node->init) { 185 name = graph_node->node->name; 186 rc = graph_node->node->init( 187 graph->graph, 188 graph_node_name_to_ptr(graph->graph, name)); 189 if (rc) 190 SET_ERR_JMP(rc, err, "Node %s init() failed", 191 name); 192 } 193 } 194 195 return 0; 196 err: 197 return -rte_errno; 198 } 199 200 static void 201 graph_node_fini(struct graph *graph) 202 { 203 struct graph_node *graph_node; 204 205 STAILQ_FOREACH(graph_node, &graph->node_list, next) 206 if (graph_node->node->fini) 207 graph_node->node->fini( 208 graph->graph, 209 graph_node_name_to_ptr(graph->graph, 210 graph_node->node->name)); 211 } 212 213 static struct rte_graph * 214 graph_mem_fixup_node_ctx(struct rte_graph *graph) 215 { 216 struct rte_node *node; 217 struct node *node_db; 218 rte_graph_off_t off; 219 rte_node_t count; 220 const char *name; 221 222 rte_graph_foreach_node(count, off, graph, node) { 223 if (node->parent_id == RTE_NODE_ID_INVALID) /* Static node */ 224 name = node->name; 225 else /* Cloned node */ 226 name = node->parent; 227 228 node_db = node_from_name(name); 229 if (node_db == NULL) 230 SET_ERR_JMP(ENOLINK, fail, "Node %s not found", name); 231 node->process = node_db->process; 232 } 233 234 return graph; 235 fail: 236 return NULL; 237 } 238 239 static struct rte_graph * 240 graph_mem_fixup_secondary(struct rte_graph *graph) 241 { 242 if (graph == NULL || rte_eal_process_type() == RTE_PROC_PRIMARY) 243 return graph; 244 245 return graph_mem_fixup_node_ctx(graph); 246 } 247 248 struct rte_graph * 249 rte_graph_lookup(const char *name) 250 { 251 const struct rte_memzone *mz; 252 struct rte_graph *rc = NULL; 253 254 mz = rte_memzone_lookup(name); 255 if (mz) 256 rc = mz->addr; 257 258 return graph_mem_fixup_secondary(rc); 259 } 260 261 rte_graph_t 262 rte_graph_create(const char *name, struct rte_graph_param *prm) 263 { 264 rte_node_t src_node_count; 265 struct graph *graph; 266 const char *pattern; 267 uint16_t i; 268 269 graph_spinlock_lock(); 270 271 /* Check arguments sanity */ 272 if (prm == NULL) 273 SET_ERR_JMP(EINVAL, fail, "Param should not be NULL"); 274 275 if (name == NULL) 276 SET_ERR_JMP(EINVAL, fail, "Graph name should not be NULL"); 277 278 /* Check for existence of duplicate graph */ 279 STAILQ_FOREACH(graph, &graph_list, next) 280 if (strncmp(name, graph->name, RTE_GRAPH_NAMESIZE) == 0) 281 SET_ERR_JMP(EEXIST, fail, "Found duplicate graph %s", 282 name); 283 284 /* Create graph object */ 285 graph = calloc(1, sizeof(*graph)); 286 if (graph == NULL) 287 SET_ERR_JMP(ENOMEM, fail, "Failed to calloc graph object"); 288 289 /* Initialize the graph object */ 290 STAILQ_INIT(&graph->node_list); 291 if (rte_strscpy(graph->name, name, RTE_GRAPH_NAMESIZE) < 0) 292 SET_ERR_JMP(E2BIG, free, "Too big name=%s", name); 293 294 /* Expand node pattern and add the nodes to the graph */ 295 for (i = 0; i < prm->nb_node_patterns; i++) { 296 pattern = prm->node_patterns[i]; 297 if (expand_pattern_to_node(graph, pattern)) 298 goto graph_cleanup; 299 } 300 301 /* Go over all the nodes edges and add them to the graph */ 302 if (graph_node_edges_add(graph)) 303 goto graph_cleanup; 304 305 /* Update adjacency list of all nodes in the graph */ 306 if (graph_adjacency_list_update(graph)) 307 goto graph_cleanup; 308 309 /* Make sure at least a source node present in the graph */ 310 src_node_count = graph_src_nodes_count(graph); 311 if (src_node_count == 0) 312 goto graph_cleanup; 313 314 /* Make sure no node is pointing to source node */ 315 if (graph_node_has_edge_to_src_node(graph)) 316 goto graph_cleanup; 317 318 /* Don't allow node has loop to self */ 319 if (graph_node_has_loop_edge(graph)) 320 goto graph_cleanup; 321 322 /* Do BFS from src nodes on the graph to find isolated nodes */ 323 if (graph_has_isolated_node(graph)) 324 goto graph_cleanup; 325 326 /* Initialize graph object */ 327 graph->socket = prm->socket_id; 328 graph->src_node_count = src_node_count; 329 graph->node_count = graph_nodes_count(graph); 330 graph->id = graph_id; 331 332 /* Allocate the Graph fast path memory and populate the data */ 333 if (graph_fp_mem_create(graph)) 334 goto graph_cleanup; 335 336 /* Call init() of the all the nodes in the graph */ 337 if (graph_node_init(graph)) 338 goto graph_mem_destroy; 339 340 /* All good, Lets add the graph to the list */ 341 graph_id++; 342 STAILQ_INSERT_TAIL(&graph_list, graph, next); 343 344 graph_spinlock_unlock(); 345 return graph->id; 346 347 graph_mem_destroy: 348 graph_fp_mem_destroy(graph); 349 graph_cleanup: 350 graph_cleanup(graph); 351 free: 352 free(graph); 353 fail: 354 graph_spinlock_unlock(); 355 return RTE_GRAPH_ID_INVALID; 356 } 357 358 int 359 rte_graph_destroy(rte_graph_t id) 360 { 361 struct graph *graph, *tmp; 362 int rc = -ENOENT; 363 364 graph_spinlock_lock(); 365 366 graph = STAILQ_FIRST(&graph_list); 367 while (graph != NULL) { 368 tmp = STAILQ_NEXT(graph, next); 369 if (graph->id == id) { 370 /* Call fini() of the all the nodes in the graph */ 371 graph_node_fini(graph); 372 /* Destroy graph fast path memory */ 373 rc = graph_fp_mem_destroy(graph); 374 if (rc) 375 SET_ERR_JMP(rc, done, "Graph %s destroy failed", 376 graph->name); 377 378 graph_cleanup(graph); 379 STAILQ_REMOVE(&graph_list, graph, graph, next); 380 free(graph); 381 graph_id--; 382 goto done; 383 } 384 graph = tmp; 385 } 386 done: 387 graph_spinlock_unlock(); 388 return rc; 389 } 390 391 rte_graph_t 392 rte_graph_from_name(const char *name) 393 { 394 struct graph *graph; 395 396 STAILQ_FOREACH(graph, &graph_list, next) 397 if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0) 398 return graph->id; 399 400 return RTE_GRAPH_ID_INVALID; 401 } 402 403 char * 404 rte_graph_id_to_name(rte_graph_t id) 405 { 406 struct graph *graph; 407 408 GRAPH_ID_CHECK(id); 409 STAILQ_FOREACH(graph, &graph_list, next) 410 if (graph->id == id) 411 return graph->name; 412 413 fail: 414 return NULL; 415 } 416 417 struct rte_node * 418 rte_graph_node_get(rte_graph_t gid, uint32_t nid) 419 { 420 struct rte_node *node; 421 struct graph *graph; 422 rte_graph_off_t off; 423 rte_node_t count; 424 425 GRAPH_ID_CHECK(gid); 426 STAILQ_FOREACH(graph, &graph_list, next) 427 if (graph->id == gid) { 428 rte_graph_foreach_node(count, off, graph->graph, 429 node) { 430 if (node->id == nid) 431 return node; 432 } 433 break; 434 } 435 fail: 436 return NULL; 437 } 438 439 struct rte_node * 440 rte_graph_node_get_by_name(const char *graph_name, const char *node_name) 441 { 442 struct rte_node *node; 443 struct graph *graph; 444 rte_graph_off_t off; 445 rte_node_t count; 446 447 STAILQ_FOREACH(graph, &graph_list, next) 448 if (!strncmp(graph->name, graph_name, RTE_GRAPH_NAMESIZE)) { 449 rte_graph_foreach_node(count, off, graph->graph, 450 node) { 451 if (!strncmp(node->name, node_name, 452 RTE_NODE_NAMESIZE)) 453 return node; 454 } 455 break; 456 } 457 458 return NULL; 459 } 460 461 void __rte_noinline 462 __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node) 463 { 464 uint16_t size = node->size; 465 466 RTE_VERIFY(size != UINT16_MAX); 467 /* Allocate double amount of size to avoid immediate realloc */ 468 size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, size * 2)); 469 node->objs = rte_realloc_socket(node->objs, size * sizeof(void *), 470 RTE_CACHE_LINE_SIZE, graph->socket); 471 RTE_VERIFY(node->objs); 472 node->size = size; 473 node->realloc_count++; 474 } 475 476 void __rte_noinline 477 __rte_node_stream_alloc_size(struct rte_graph *graph, struct rte_node *node, 478 uint16_t req_size) 479 { 480 uint16_t size = node->size; 481 482 RTE_VERIFY(size != UINT16_MAX); 483 /* Allocate double amount of size to avoid immediate realloc */ 484 size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, req_size * 2)); 485 node->objs = rte_realloc_socket(node->objs, size * sizeof(void *), 486 RTE_CACHE_LINE_SIZE, graph->socket); 487 RTE_VERIFY(node->objs); 488 node->size = size; 489 node->realloc_count++; 490 } 491 492 static int 493 graph_to_dot(FILE *f, struct graph *graph) 494 { 495 const char *src_edge_color = " [color=blue]\n"; 496 const char *edge_color = "\n"; 497 struct graph_node *graph_node; 498 char *node_name; 499 rte_edge_t i; 500 int rc; 501 502 rc = fprintf(f, "Digraph %s {\n\trankdir=LR;\n", graph->name); 503 if (rc < 0) 504 goto end; 505 506 STAILQ_FOREACH(graph_node, &graph->node_list, next) { 507 node_name = graph_node->node->name; 508 for (i = 0; i < graph_node->node->nb_edges; i++) { 509 rc = fprintf(f, "\t\"%s\"->\"%s\"%s", node_name, 510 graph_node->adjacency_list[i]->node->name, 511 graph_node->node->flags & RTE_NODE_SOURCE_F 512 ? src_edge_color 513 : edge_color); 514 if (rc < 0) 515 goto end; 516 } 517 } 518 rc = fprintf(f, "}\n"); 519 if (rc < 0) 520 goto end; 521 522 return 0; 523 end: 524 rte_errno = EBADF; 525 return -rte_errno; 526 } 527 528 int 529 rte_graph_export(const char *name, FILE *f) 530 { 531 struct graph *graph; 532 int rc = ENOENT; 533 534 STAILQ_FOREACH(graph, &graph_list, next) { 535 if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0) { 536 rc = graph_to_dot(f, graph); 537 goto end; 538 } 539 } 540 end: 541 return -rc; 542 } 543 544 static void 545 graph_scan_dump(FILE *f, rte_graph_t id, bool all) 546 { 547 struct graph *graph; 548 549 RTE_VERIFY(f); 550 GRAPH_ID_CHECK(id); 551 552 STAILQ_FOREACH(graph, &graph_list, next) { 553 if (all == true) { 554 graph_dump(f, graph); 555 } else if (graph->id == id) { 556 graph_dump(f, graph); 557 return; 558 } 559 } 560 fail: 561 return; 562 } 563 564 void 565 rte_graph_dump(FILE *f, rte_graph_t id) 566 { 567 graph_scan_dump(f, id, false); 568 } 569 570 void 571 rte_graph_list_dump(FILE *f) 572 { 573 graph_scan_dump(f, 0, true); 574 } 575 576 rte_graph_t 577 rte_graph_max_count(void) 578 { 579 return graph_id; 580 } 581 582 RTE_LOG_REGISTER_DEFAULT(rte_graph_logtype, INFO); 583