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