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 #include "graph_pcap_private.h" 19 20 static struct graph_head graph_list = STAILQ_HEAD_INITIALIZER(graph_list); 21 static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER; 22 23 /* Private functions */ 24 static struct graph * 25 graph_from_id(rte_graph_t id) 26 { 27 struct graph *graph; 28 STAILQ_FOREACH(graph, &graph_list, next) { 29 if (graph->id == id) 30 return graph; 31 } 32 rte_errno = EINVAL; 33 return NULL; 34 } 35 36 static rte_graph_t 37 graph_next_free_id(void) 38 { 39 struct graph *graph; 40 rte_graph_t id = 0; 41 42 STAILQ_FOREACH(graph, &graph_list, next) { 43 if (id < graph->id) 44 break; 45 id = graph->id + 1; 46 } 47 48 return id; 49 } 50 51 static void 52 graph_insert_ordered(struct graph *graph) 53 { 54 struct graph *after, *g; 55 56 after = NULL; 57 STAILQ_FOREACH(g, &graph_list, next) { 58 if (g->id < graph->id) 59 after = g; 60 else if (g->id > graph->id) 61 break; 62 } 63 if (after == NULL) { 64 STAILQ_INSERT_HEAD(&graph_list, graph, next); 65 } else { 66 STAILQ_INSERT_AFTER(&graph_list, after, graph, next); 67 } 68 } 69 70 struct graph_head * 71 graph_list_head_get(void) 72 { 73 return &graph_list; 74 } 75 76 rte_spinlock_t * 77 graph_spinlock_get(void) 78 { 79 return &graph_lock; 80 } 81 82 void 83 graph_spinlock_lock(void) 84 { 85 rte_spinlock_lock(graph_spinlock_get()); 86 } 87 88 void 89 graph_spinlock_unlock(void) 90 { 91 rte_spinlock_unlock(graph_spinlock_get()); 92 } 93 94 static int 95 graph_node_add(struct graph *graph, struct node *node) 96 { 97 struct graph_node *graph_node; 98 size_t sz; 99 100 /* Skip the duplicate nodes */ 101 STAILQ_FOREACH(graph_node, &graph->node_list, next) 102 if (strncmp(node->name, graph_node->node->name, 103 RTE_NODE_NAMESIZE) == 0) 104 return 0; 105 106 /* Allocate new graph node object */ 107 sz = sizeof(*graph_node) + node->nb_edges * sizeof(struct node *); 108 graph_node = calloc(1, sz); 109 110 if (graph_node == NULL) 111 SET_ERR_JMP(ENOMEM, free, "Failed to calloc %s object", 112 node->name); 113 114 /* Initialize the graph node */ 115 graph_node->node = node; 116 117 /* Add to graph node list */ 118 STAILQ_INSERT_TAIL(&graph->node_list, graph_node, next); 119 return 0; 120 121 free: 122 free(graph_node); 123 return -rte_errno; 124 } 125 126 static struct graph_node * 127 node_to_graph_node(struct graph *graph, struct node *node) 128 { 129 struct graph_node *graph_node; 130 131 STAILQ_FOREACH(graph_node, &graph->node_list, next) 132 if (graph_node->node == node) 133 return graph_node; 134 135 SET_ERR_JMP(ENODEV, fail, "Found isolated node %s", node->name); 136 fail: 137 return NULL; 138 } 139 140 static int 141 graph_node_edges_add(struct graph *graph) 142 { 143 struct graph_node *graph_node; 144 struct node *adjacency; 145 const char *next; 146 rte_edge_t i; 147 148 STAILQ_FOREACH(graph_node, &graph->node_list, next) { 149 for (i = 0; i < graph_node->node->nb_edges; i++) { 150 next = graph_node->node->next_nodes[i]; 151 adjacency = node_from_name(next); 152 if (adjacency == NULL) 153 SET_ERR_JMP(EINVAL, fail, 154 "Node %s not registered", next); 155 if (graph_node_add(graph, adjacency)) 156 goto fail; 157 } 158 } 159 return 0; 160 fail: 161 return -rte_errno; 162 } 163 164 static int 165 graph_adjacency_list_update(struct graph *graph) 166 { 167 struct graph_node *graph_node, *tmp; 168 struct node *adjacency; 169 const char *next; 170 rte_edge_t i; 171 172 STAILQ_FOREACH(graph_node, &graph->node_list, next) { 173 for (i = 0; i < graph_node->node->nb_edges; i++) { 174 next = graph_node->node->next_nodes[i]; 175 adjacency = node_from_name(next); 176 if (adjacency == NULL) 177 SET_ERR_JMP(EINVAL, fail, 178 "Node %s not registered", next); 179 tmp = node_to_graph_node(graph, adjacency); 180 if (tmp == NULL) 181 goto fail; 182 graph_node->adjacency_list[i] = tmp; 183 } 184 } 185 186 return 0; 187 fail: 188 return -rte_errno; 189 } 190 191 static int 192 expand_pattern_to_node(struct graph *graph, const char *pattern) 193 { 194 struct node_head *node_head = node_list_head_get(); 195 bool found = false; 196 struct node *node; 197 198 /* Check for pattern match */ 199 STAILQ_FOREACH(node, node_head, next) { 200 if (fnmatch(pattern, node->name, 0) == 0) { 201 if (graph_node_add(graph, node)) 202 goto fail; 203 found = true; 204 } 205 } 206 if (found == false) 207 SET_ERR_JMP(EFAULT, fail, "Pattern %s node not found", pattern); 208 209 return 0; 210 fail: 211 return -rte_errno; 212 } 213 214 static void 215 graph_cleanup(struct graph *graph) 216 { 217 struct graph_node *graph_node; 218 219 while (!STAILQ_EMPTY(&graph->node_list)) { 220 graph_node = STAILQ_FIRST(&graph->node_list); 221 STAILQ_REMOVE_HEAD(&graph->node_list, next); 222 free(graph_node); 223 } 224 } 225 226 static int 227 graph_node_init(struct graph *graph) 228 { 229 struct graph_node *graph_node; 230 const char *name; 231 int rc; 232 233 STAILQ_FOREACH(graph_node, &graph->node_list, next) { 234 if (graph_node->node->init) { 235 name = graph_node->node->name; 236 rc = graph_node->node->init( 237 graph->graph, 238 graph_node_name_to_ptr(graph->graph, name)); 239 if (rc) 240 SET_ERR_JMP(rc, err, "Node %s init() failed", 241 name); 242 } 243 } 244 245 return 0; 246 err: 247 return -rte_errno; 248 } 249 250 static void 251 graph_node_fini(struct graph *graph) 252 { 253 struct graph_node *graph_node; 254 255 STAILQ_FOREACH(graph_node, &graph->node_list, next) 256 if (graph_node->node->fini) 257 graph_node->node->fini( 258 graph->graph, 259 graph_node_name_to_ptr(graph->graph, 260 graph_node->node->name)); 261 } 262 263 static struct rte_graph * 264 graph_mem_fixup_node_ctx(struct rte_graph *graph) 265 { 266 struct rte_node *node; 267 struct node *node_db; 268 rte_graph_off_t off; 269 rte_node_t count; 270 const char *name; 271 272 rte_graph_foreach_node(count, off, graph, node) { 273 if (node->parent_id == RTE_NODE_ID_INVALID) /* Static node */ 274 name = node->name; 275 else /* Cloned node */ 276 name = node->parent; 277 278 node_db = node_from_name(name); 279 if (node_db == NULL) 280 SET_ERR_JMP(ENOLINK, fail, "Node %s not found", name); 281 282 if (graph->pcap_enable) { 283 node->process = graph_pcap_dispatch; 284 node->original_process = node_db->process; 285 } else 286 node->process = node_db->process; 287 } 288 289 return graph; 290 fail: 291 return NULL; 292 } 293 294 static struct rte_graph * 295 graph_mem_fixup_secondary(struct rte_graph *graph) 296 { 297 if (graph == NULL || rte_eal_process_type() == RTE_PROC_PRIMARY) 298 return graph; 299 300 if (graph_pcap_file_open(graph->pcap_filename) || graph_pcap_mp_init()) 301 graph_pcap_exit(graph); 302 303 return graph_mem_fixup_node_ctx(graph); 304 } 305 306 static bool 307 graph_src_node_avail(struct graph *graph) 308 { 309 struct graph_node *graph_node; 310 311 STAILQ_FOREACH(graph_node, &graph->node_list, next) 312 if ((graph_node->node->flags & RTE_NODE_SOURCE_F) && 313 (graph_node->node->lcore_id == RTE_MAX_LCORE || 314 graph->lcore_id == graph_node->node->lcore_id)) 315 return true; 316 317 return false; 318 } 319 320 int 321 rte_graph_model_mcore_dispatch_core_bind(rte_graph_t id, int lcore) 322 { 323 struct graph *graph; 324 325 if (graph_from_id(id) == NULL) 326 goto fail; 327 if (!rte_lcore_is_enabled(lcore)) 328 SET_ERR_JMP(ENOLINK, fail, "lcore %d not enabled", lcore); 329 330 STAILQ_FOREACH(graph, &graph_list, next) 331 if (graph->id == id) 332 break; 333 334 if (graph->graph->model != RTE_GRAPH_MODEL_MCORE_DISPATCH) 335 goto fail; 336 337 graph->lcore_id = lcore; 338 graph->graph->dispatch.lcore_id = graph->lcore_id; 339 graph->socket = rte_lcore_to_socket_id(lcore); 340 341 /* check the availability of source node */ 342 if (!graph_src_node_avail(graph)) 343 graph->graph->head = 0; 344 345 return 0; 346 347 fail: 348 return -rte_errno; 349 } 350 351 void 352 rte_graph_model_mcore_dispatch_core_unbind(rte_graph_t id) 353 { 354 struct graph *graph; 355 356 if (graph_from_id(id) == NULL) 357 goto fail; 358 STAILQ_FOREACH(graph, &graph_list, next) 359 if (graph->id == id) 360 break; 361 362 graph->lcore_id = RTE_MAX_LCORE; 363 graph->graph->dispatch.lcore_id = RTE_MAX_LCORE; 364 365 fail: 366 return; 367 } 368 369 struct rte_graph * 370 rte_graph_lookup(const char *name) 371 { 372 const struct rte_memzone *mz; 373 struct rte_graph *rc = NULL; 374 375 mz = rte_memzone_lookup(name); 376 if (mz) 377 rc = mz->addr; 378 379 return graph_mem_fixup_secondary(rc); 380 } 381 382 rte_graph_t 383 rte_graph_create(const char *name, struct rte_graph_param *prm) 384 { 385 rte_node_t src_node_count; 386 struct graph *graph; 387 const char *pattern; 388 uint16_t i; 389 390 graph_spinlock_lock(); 391 392 /* Check arguments sanity */ 393 if (prm == NULL) 394 SET_ERR_JMP(EINVAL, fail, "Param should not be NULL"); 395 396 if (name == NULL) 397 SET_ERR_JMP(EINVAL, fail, "Graph name should not be NULL"); 398 399 /* Check for existence of duplicate graph */ 400 STAILQ_FOREACH(graph, &graph_list, next) 401 if (strncmp(name, graph->name, RTE_GRAPH_NAMESIZE) == 0) 402 SET_ERR_JMP(EEXIST, fail, "Found duplicate graph %s", 403 name); 404 405 /* Create graph object */ 406 graph = calloc(1, sizeof(*graph)); 407 if (graph == NULL) 408 SET_ERR_JMP(ENOMEM, fail, "Failed to calloc graph object"); 409 410 /* Initialize the graph object */ 411 STAILQ_INIT(&graph->node_list); 412 if (rte_strscpy(graph->name, name, RTE_GRAPH_NAMESIZE) < 0) 413 SET_ERR_JMP(E2BIG, free, "Too big name=%s", name); 414 415 /* Expand node pattern and add the nodes to the graph */ 416 for (i = 0; i < prm->nb_node_patterns; i++) { 417 pattern = prm->node_patterns[i]; 418 if (expand_pattern_to_node(graph, pattern)) 419 goto graph_cleanup; 420 } 421 422 /* Go over all the nodes edges and add them to the graph */ 423 if (graph_node_edges_add(graph)) 424 goto graph_cleanup; 425 426 /* Update adjacency list of all nodes in the graph */ 427 if (graph_adjacency_list_update(graph)) 428 goto graph_cleanup; 429 430 /* Make sure at least a source node present in the graph */ 431 src_node_count = graph_src_nodes_count(graph); 432 if (src_node_count == 0) 433 goto graph_cleanup; 434 435 /* Make sure no node is pointing to source node */ 436 if (graph_node_has_edge_to_src_node(graph)) 437 goto graph_cleanup; 438 439 /* Don't allow node has loop to self */ 440 if (graph_node_has_loop_edge(graph)) 441 goto graph_cleanup; 442 443 /* Do BFS from src nodes on the graph to find isolated nodes */ 444 if (graph_has_isolated_node(graph)) 445 goto graph_cleanup; 446 447 /* Initialize pcap config. */ 448 graph_pcap_enable(prm->pcap_enable); 449 450 /* Initialize graph object */ 451 graph->socket = prm->socket_id; 452 graph->src_node_count = src_node_count; 453 graph->node_count = graph_nodes_count(graph); 454 graph->id = graph_next_free_id(); 455 graph->parent_id = RTE_GRAPH_ID_INVALID; 456 graph->lcore_id = RTE_MAX_LCORE; 457 graph->num_pkt_to_capture = prm->num_pkt_to_capture; 458 if (prm->pcap_filename) 459 rte_strscpy(graph->pcap_filename, prm->pcap_filename, RTE_GRAPH_PCAP_FILE_SZ); 460 461 /* Allocate the Graph fast path memory and populate the data */ 462 if (graph_fp_mem_create(graph)) 463 goto graph_cleanup; 464 465 /* Call init() of the all the nodes in the graph */ 466 if (graph_node_init(graph)) 467 goto graph_mem_destroy; 468 469 /* All good, Lets add the graph to the list */ 470 graph_insert_ordered(graph); 471 472 graph_spinlock_unlock(); 473 return graph->id; 474 475 graph_mem_destroy: 476 graph_fp_mem_destroy(graph); 477 graph_cleanup: 478 graph_cleanup(graph); 479 free: 480 free(graph); 481 fail: 482 graph_spinlock_unlock(); 483 return RTE_GRAPH_ID_INVALID; 484 } 485 486 int 487 rte_graph_destroy(rte_graph_t id) 488 { 489 struct graph *graph, *tmp; 490 int rc = -ENOENT; 491 492 graph_spinlock_lock(); 493 494 graph = STAILQ_FIRST(&graph_list); 495 while (graph != NULL) { 496 tmp = STAILQ_NEXT(graph, next); 497 if (graph->id == id) { 498 /* Destroy the schedule work queue if has */ 499 if (rte_graph_worker_model_get(graph->graph) == 500 RTE_GRAPH_MODEL_MCORE_DISPATCH) 501 graph_sched_wq_destroy(graph); 502 503 /* Call fini() of the all the nodes in the graph */ 504 graph_node_fini(graph); 505 /* Destroy graph fast path memory */ 506 rc = graph_fp_mem_destroy(graph); 507 if (rc) 508 SET_ERR_JMP(rc, done, "Graph %s destroy failed", 509 graph->name); 510 511 graph_cleanup(graph); 512 STAILQ_REMOVE(&graph_list, graph, graph, next); 513 free(graph); 514 goto done; 515 } 516 graph = tmp; 517 } 518 done: 519 graph_spinlock_unlock(); 520 return rc; 521 } 522 523 static rte_graph_t 524 graph_clone(struct graph *parent_graph, const char *name, struct rte_graph_param *prm) 525 { 526 struct graph_node *graph_node; 527 struct graph *graph; 528 529 graph_spinlock_lock(); 530 531 /* Don't allow to clone a node from a cloned graph */ 532 if (parent_graph->parent_id != RTE_GRAPH_ID_INVALID) 533 SET_ERR_JMP(EEXIST, fail, "A cloned graph is not allowed to be cloned"); 534 535 /* Create graph object */ 536 graph = calloc(1, sizeof(*graph)); 537 if (graph == NULL) 538 SET_ERR_JMP(ENOMEM, fail, "Failed to calloc cloned graph object"); 539 540 /* Naming ceremony of the new graph. name is node->name + "-" + name */ 541 if (clone_name(graph->name, parent_graph->name, name)) 542 goto free; 543 544 /* Check for existence of duplicate graph */ 545 if (rte_graph_from_name(graph->name) != RTE_GRAPH_ID_INVALID) 546 SET_ERR_JMP(EEXIST, free, "Found duplicate graph %s", 547 graph->name); 548 549 /* Clone nodes from parent graph firstly */ 550 STAILQ_INIT(&graph->node_list); 551 STAILQ_FOREACH(graph_node, &parent_graph->node_list, next) { 552 if (graph_node_add(graph, graph_node->node)) 553 goto graph_cleanup; 554 } 555 556 /* Just update adjacency list of all nodes in the graph */ 557 if (graph_adjacency_list_update(graph)) 558 goto graph_cleanup; 559 560 /* Initialize the graph object */ 561 graph->src_node_count = parent_graph->src_node_count; 562 graph->node_count = parent_graph->node_count; 563 graph->parent_id = parent_graph->id; 564 graph->lcore_id = parent_graph->lcore_id; 565 graph->socket = parent_graph->socket; 566 graph->id = graph_next_free_id(); 567 568 /* Allocate the Graph fast path memory and populate the data */ 569 if (graph_fp_mem_create(graph)) 570 goto graph_cleanup; 571 572 /* Clone the graph model */ 573 graph->graph->model = parent_graph->graph->model; 574 575 /* Create the graph schedule work queue */ 576 if (rte_graph_worker_model_get(graph->graph) == RTE_GRAPH_MODEL_MCORE_DISPATCH && 577 graph_sched_wq_create(graph, parent_graph, prm)) 578 goto graph_mem_destroy; 579 580 /* Call init() of the all the nodes in the graph */ 581 if (graph_node_init(graph)) 582 goto graph_mem_destroy; 583 584 /* All good, Lets add the graph to the list */ 585 graph_insert_ordered(graph); 586 587 graph_spinlock_unlock(); 588 return graph->id; 589 590 graph_mem_destroy: 591 graph_fp_mem_destroy(graph); 592 graph_cleanup: 593 graph_cleanup(graph); 594 free: 595 free(graph); 596 fail: 597 graph_spinlock_unlock(); 598 return RTE_GRAPH_ID_INVALID; 599 } 600 601 rte_graph_t 602 rte_graph_clone(rte_graph_t id, const char *name, struct rte_graph_param *prm) 603 { 604 struct graph *graph; 605 606 if (graph_from_id(id) == NULL) 607 goto fail; 608 STAILQ_FOREACH(graph, &graph_list, next) 609 if (graph->id == id) 610 return graph_clone(graph, name, prm); 611 612 fail: 613 return RTE_GRAPH_ID_INVALID; 614 } 615 616 rte_graph_t 617 rte_graph_from_name(const char *name) 618 { 619 struct graph *graph; 620 621 STAILQ_FOREACH(graph, &graph_list, next) 622 if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0) 623 return graph->id; 624 625 return RTE_GRAPH_ID_INVALID; 626 } 627 628 char * 629 rte_graph_id_to_name(rte_graph_t id) 630 { 631 struct graph *graph; 632 633 if (graph_from_id(id) == NULL) 634 goto fail; 635 STAILQ_FOREACH(graph, &graph_list, next) 636 if (graph->id == id) 637 return graph->name; 638 639 fail: 640 return NULL; 641 } 642 643 struct rte_node * 644 rte_graph_node_get(rte_graph_t gid, uint32_t nid) 645 { 646 struct rte_node *node; 647 struct graph *graph; 648 rte_graph_off_t off; 649 rte_node_t count; 650 651 if (graph_from_id(gid) == NULL) 652 goto fail; 653 STAILQ_FOREACH(graph, &graph_list, next) 654 if (graph->id == gid) { 655 rte_graph_foreach_node(count, off, graph->graph, 656 node) { 657 if (node->id == nid) 658 return node; 659 } 660 break; 661 } 662 fail: 663 return NULL; 664 } 665 666 struct rte_node * 667 rte_graph_node_get_by_name(const char *graph_name, const char *node_name) 668 { 669 struct rte_node *node; 670 struct graph *graph; 671 rte_graph_off_t off; 672 rte_node_t count; 673 674 STAILQ_FOREACH(graph, &graph_list, next) 675 if (!strncmp(graph->name, graph_name, RTE_GRAPH_NAMESIZE)) { 676 rte_graph_foreach_node(count, off, graph->graph, 677 node) { 678 if (!strncmp(node->name, node_name, 679 RTE_NODE_NAMESIZE)) 680 return node; 681 } 682 break; 683 } 684 685 return NULL; 686 } 687 688 void __rte_noinline 689 __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node) 690 { 691 uint16_t size = node->size; 692 693 RTE_VERIFY(size != UINT16_MAX); 694 /* Allocate double amount of size to avoid immediate realloc */ 695 size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, size * 2)); 696 node->objs = rte_realloc_socket(node->objs, size * sizeof(void *), 697 RTE_CACHE_LINE_SIZE, graph->socket); 698 RTE_VERIFY(node->objs); 699 node->size = size; 700 node->realloc_count++; 701 } 702 703 void __rte_noinline 704 __rte_node_stream_alloc_size(struct rte_graph *graph, struct rte_node *node, 705 uint16_t req_size) 706 { 707 uint16_t size = node->size; 708 709 RTE_VERIFY(size != UINT16_MAX); 710 /* Allocate double amount of size to avoid immediate realloc */ 711 size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, req_size * 2)); 712 node->objs = rte_realloc_socket(node->objs, size * sizeof(void *), 713 RTE_CACHE_LINE_SIZE, graph->socket); 714 RTE_VERIFY(node->objs); 715 node->size = size; 716 node->realloc_count++; 717 } 718 719 static int 720 graph_to_dot(FILE *f, struct graph *graph) 721 { 722 struct graph_node *graph_node; 723 char *node_name; 724 rte_edge_t i; 725 int rc; 726 727 rc = fprintf(f, "digraph \"%s\" {\n\trankdir=LR;\n", graph->name); 728 if (rc < 0) 729 goto end; 730 731 rc = fprintf(f, "\tnode [margin=0.02 fontsize=11 fontname=sans];\n"); 732 if (rc < 0) 733 goto end; 734 735 STAILQ_FOREACH(graph_node, &graph->node_list, next) { 736 const char *attrs = ""; 737 node_name = graph_node->node->name; 738 739 rc = fprintf(f, "\t\"%s\"", node_name); 740 if (rc < 0) 741 goto end; 742 if (graph_node->node->flags & RTE_NODE_SOURCE_F) { 743 attrs = " [color=blue style=bold]"; 744 rc = fprintf(f, "%s", attrs); 745 if (rc < 0) 746 goto end; 747 } else if (graph_node->node->nb_edges == 0) { 748 rc = fprintf(f, " [fontcolor=darkorange shape=plain]"); 749 if (rc < 0) 750 goto end; 751 } 752 rc = fprintf(f, ";\n"); 753 if (rc < 0) 754 goto end; 755 for (i = 0; i < graph_node->node->nb_edges; i++) { 756 const char *node_attrs = attrs; 757 if (graph_node->adjacency_list[i]->node->nb_edges == 0) 758 node_attrs = " [color=darkorange]"; 759 rc = fprintf(f, "\t\"%s\" -> \"%s\"%s;\n", node_name, 760 graph_node->adjacency_list[i]->node->name, 761 node_attrs); 762 if (rc < 0) 763 goto end; 764 } 765 } 766 rc = fprintf(f, "}\n"); 767 if (rc < 0) 768 goto end; 769 770 return 0; 771 end: 772 rte_errno = EBADF; 773 return -rte_errno; 774 } 775 776 int 777 rte_graph_export(const char *name, FILE *f) 778 { 779 struct graph *graph; 780 int rc = ENOENT; 781 782 STAILQ_FOREACH(graph, &graph_list, next) { 783 if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0) { 784 rc = graph_to_dot(f, graph); 785 goto end; 786 } 787 } 788 end: 789 return -rc; 790 } 791 792 static void 793 graph_scan_dump(FILE *f, rte_graph_t id, bool all) 794 { 795 struct graph *graph; 796 797 RTE_VERIFY(f); 798 if (graph_from_id(id) == NULL) 799 goto fail; 800 801 STAILQ_FOREACH(graph, &graph_list, next) { 802 if (all == true) { 803 graph_dump(f, graph); 804 } else if (graph->id == id) { 805 graph_dump(f, graph); 806 return; 807 } 808 } 809 fail: 810 return; 811 } 812 813 void 814 rte_graph_dump(FILE *f, rte_graph_t id) 815 { 816 graph_scan_dump(f, id, false); 817 } 818 819 void 820 rte_graph_list_dump(FILE *f) 821 { 822 graph_scan_dump(f, 0, true); 823 } 824 825 rte_graph_t 826 rte_graph_max_count(void) 827 { 828 struct graph *graph; 829 rte_graph_t count = 0; 830 831 STAILQ_FOREACH(graph, &graph_list, next) 832 count++; 833 834 return count; 835 } 836 837 RTE_LOG_REGISTER_DEFAULT(rte_graph_logtype, INFO); 838