1/* 2 * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved. 3 * Copyright (c) 2002-2008 Mellanox Technologies LTD. All rights reserved. 4 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved. 5 * Copyright (c) 2007 Simula Research Laboratory. All rights reserved. 6 * Copyright (c) 2007 Silicon Graphics Inc. All rights reserved. 7 * 8 * This software is available to you under a choice of one of two 9 * licenses. You may choose to be licensed under the terms of the GNU 10 * General Public License (GPL) Version 2, available from the file 11 * COPYING in the main directory of this source tree, or the 12 * OpenIB.org BSD license below: 13 * 14 * Redistribution and use in source and binary forms, with or 15 * without modification, are permitted provided that the following 16 * conditions are met: 17 * 18 * - Redistributions of source code must retain the above 19 * copyright notice, this list of conditions and the following 20 * disclaimer. 21 * 22 * - Redistributions in binary form must reproduce the above 23 * copyright notice, this list of conditions and the following 24 * disclaimer in the documentation and/or other materials 25 * provided with the distribution. 26 * 27 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 28 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 29 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 30 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 31 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 32 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 33 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 34 * SOFTWARE. 35 * 36 */ 37 38/* 39 * Abstract: 40 * Implementation of LASH algorithm Calculation functions 41 */ 42 43#if HAVE_CONFIG_H 44# include <config.h> 45#endif /* HAVE_CONFIG_H */ 46 47#include <stdlib.h> 48#include <stdio.h> 49#include <errno.h> 50#include <complib/cl_debug.h> 51#include <complib/cl_qmap.h> 52#include <opensm/osm_switch.h> 53#include <opensm/osm_opensm.h> 54#include <opensm/osm_log.h> 55 56/* //////////////////////////// */ 57/* Local types */ 58/* //////////////////////////// */ 59 60enum { 61 UNQUEUED, 62 Q_MEMBER, 63 MST_MEMBER, 64 MAX_INT = 9999, 65 NONE = MAX_INT 66}; 67 68typedef struct _cdg_vertex { 69 int num_dependencies; 70 struct _cdg_vertex **dependency; 71 int from; 72 int to; 73 int seen; 74 int temp; 75 int visiting_number; 76 struct _cdg_vertex *next; 77 int num_temp_depend; 78 int num_using_vertex; 79 int *num_using_this_depend; 80} cdg_vertex_t; 81 82typedef struct _reachable_dest { 83 int switch_id; 84 struct _reachable_dest *next; 85} reachable_dest_t; 86 87typedef struct _switch { 88 osm_switch_t *p_sw; 89 int *dij_channels; 90 int id; 91 int used_channels; 92 int q_state; 93 struct routing_table { 94 unsigned out_link; 95 unsigned lane; 96 } *routing_table; 97 unsigned int num_connections; 98 int *virtual_physical_port_table; 99 int *phys_connections; 100} switch_t; 101 102typedef struct _lash { 103 osm_opensm_t *p_osm; 104 int num_switches; 105 uint8_t vl_min; 106 int balance_limit; 107 switch_t **switches; 108 cdg_vertex_t ****cdg_vertex_matrix; 109 int *num_mst_in_lane; 110 int ***virtual_location; 111} lash_t; 112 113static cdg_vertex_t *create_cdg_vertex(unsigned num_switches) 114{ 115 cdg_vertex_t *cdg_vertex = (cdg_vertex_t *) malloc(sizeof(cdg_vertex_t)); 116 117 cdg_vertex->dependency = malloc((num_switches - 1) * sizeof(cdg_vertex_t *)); 118 cdg_vertex->num_using_this_depend = (int *)malloc((num_switches - 1) * sizeof(int)); 119 return cdg_vertex; 120} 121 122static void connect_switches(lash_t * p_lash, int sw1, int sw2, int phy_port_1) 123{ 124 osm_log_t *p_log = &p_lash->p_osm->log; 125 unsigned num = p_lash->switches[sw1]->num_connections; 126 127 p_lash->switches[sw1]->phys_connections[num] = sw2; 128 p_lash->switches[sw1]->virtual_physical_port_table[num] = phy_port_1; 129 p_lash->switches[sw1]->num_connections++; 130 131 OSM_LOG(p_log, OSM_LOG_VERBOSE, 132 "LASH connect: %d, %d, %d\n", sw1, sw2, 133 phy_port_1); 134 135} 136 137static osm_switch_t *get_osm_switch_from_port(osm_port_t * port) 138{ 139 osm_physp_t *p = port->p_physp; 140 if (p->p_node->sw) 141 return p->p_node->sw; 142 else if (p->p_remote_physp->p_node->sw) 143 return p->p_remote_physp->p_node->sw; 144 return NULL; 145} 146 147#if 0 148static int randint(int high) 149{ 150 int r; 151 152 if (high == 0) 153 return 0; 154 r = rand(); 155 high++; 156 return (r % high); 157} 158#endif 159 160static int cycle_exists(cdg_vertex_t * start, cdg_vertex_t * current, 161 cdg_vertex_t * prev, int visit_num) 162{ 163 cdg_vertex_t *h; 164 int i, new_visit_num; 165 int cycle_found = 0; 166 167 if (current != NULL && current->visiting_number > 0) { 168 if (visit_num > current->visiting_number && current->seen == 0) { 169 h = start; 170 cycle_found = 1; 171 } 172 } else { 173 if (current == NULL) { 174 current = start; 175 CL_ASSERT(prev == NULL); 176 } 177 178 current->visiting_number = visit_num; 179 180 if (prev != NULL) { 181 prev->next = current; 182 CL_ASSERT(prev->to == current->from); 183 CL_ASSERT(prev->visiting_number > 0); 184 } 185 186 new_visit_num = visit_num + 1; 187 188 for (i = 0; i < current->num_dependencies; i++) { 189 cycle_found = 190 cycle_exists(start, current->dependency[i], current, 191 new_visit_num); 192 if (cycle_found == 1) 193 i = current->num_dependencies; 194 } 195 196 current->seen = 1; 197 if (prev != NULL) 198 prev->next = NULL; 199 } 200 201 return cycle_found; 202} 203 204static void remove_semipermanent_depend_for_sp(lash_t * p_lash, int sw, 205 int dest_switch, int lane) 206{ 207 switch_t **switches = p_lash->switches; 208 cdg_vertex_t ****cdg_vertex_matrix = p_lash->cdg_vertex_matrix; 209 int i_next_switch, output_link, i, next_link, i_next_next_switch, 210 depend = 0; 211 cdg_vertex_t *v; 212 int found; 213 214 output_link = switches[sw]->routing_table[dest_switch].out_link; 215 i_next_switch = switches[sw]->phys_connections[output_link]; 216 217 while (sw != dest_switch) { 218 v = cdg_vertex_matrix[lane][sw][i_next_switch]; 219 CL_ASSERT(v != NULL); 220 221 if (v->num_using_vertex == 1) { 222 223 cdg_vertex_matrix[lane][sw][i_next_switch] = NULL; 224 225 free(v); 226 } else { 227 v->num_using_vertex--; 228 if (i_next_switch != dest_switch) { 229 next_link = 230 switches[i_next_switch]->routing_table[dest_switch].out_link; 231 i_next_next_switch = 232 switches[i_next_switch]->phys_connections[next_link]; 233 found = 0; 234 235 for (i = 0; i < v->num_dependencies; i++) 236 if (v->dependency[i] == 237 cdg_vertex_matrix[lane][i_next_switch] 238 [i_next_next_switch]) { 239 found = 1; 240 depend = i; 241 } 242 243 CL_ASSERT(found); 244 245 if (v->num_using_this_depend[depend] == 1) { 246 for (i = depend; 247 i < v->num_dependencies - 1; i++) { 248 v->dependency[i] = 249 v->dependency[i + 1]; 250 v->num_using_this_depend[i] = 251 v->num_using_this_depend[i + 252 1]; 253 } 254 255 v->num_dependencies--; 256 } else 257 v->num_using_this_depend[depend]--; 258 } 259 } 260 261 sw = i_next_switch; 262 output_link = switches[sw]->routing_table[dest_switch].out_link; 263 264 if (sw != dest_switch) 265 i_next_switch = 266 switches[sw]->phys_connections[output_link]; 267 } 268} 269 270inline static void enqueue(cl_list_t * bfsq, switch_t * sw) 271{ 272 CL_ASSERT(sw->q_state == UNQUEUED); 273 sw->q_state = Q_MEMBER; 274 cl_list_insert_tail(bfsq, sw); 275} 276 277inline static void dequeue(cl_list_t * bfsq, switch_t ** sw) 278{ 279 *sw = (switch_t *) cl_list_remove_head(bfsq); 280 CL_ASSERT((*sw)->q_state == Q_MEMBER); 281 (*sw)->q_state = MST_MEMBER; 282} 283 284static int get_phys_connection(switch_t *sw, int switch_to) 285{ 286 unsigned int i = 0; 287 288 for (i = 0; i < sw->num_connections; i++) 289 if (sw->phys_connections[i] == switch_to) 290 return i; 291 return i; 292} 293 294static void shortest_path(lash_t * p_lash, int ir) 295{ 296 switch_t **switches = p_lash->switches, *sw, *swi; 297 unsigned int i; 298 cl_list_t bfsq; 299 300 cl_list_construct(&bfsq); 301 cl_list_init(&bfsq, 20); 302 303 enqueue(&bfsq, switches[ir]); 304 305 while (!cl_is_list_empty(&bfsq)) { 306 dequeue(&bfsq, &sw); 307 for (i = 0; i < sw->num_connections; i++) { 308 swi = switches[sw->phys_connections[i]]; 309 if (swi->q_state == UNQUEUED) { 310 enqueue(&bfsq, swi); 311 sw->dij_channels[sw->used_channels++] = swi->id; 312 } 313 } 314 } 315 316 cl_list_destroy(&bfsq); 317} 318 319static void generate_routing_func_for_mst(lash_t * p_lash, int sw_id, 320 reachable_dest_t ** destinations) 321{ 322 int i, next_switch; 323 switch_t *sw = p_lash->switches[sw_id]; 324 int num_channels = sw->used_channels; 325 reachable_dest_t *dest, *i_dest, *concat_dest = NULL, *prev; 326 327 for (i = 0; i < num_channels; i++) { 328 next_switch = sw->dij_channels[i]; 329 generate_routing_func_for_mst(p_lash, next_switch, &dest); 330 331 i_dest = dest; 332 prev = i_dest; 333 334 while (i_dest != NULL) { 335 if (sw->routing_table[i_dest->switch_id].out_link == 336 NONE) { 337 sw->routing_table[i_dest->switch_id].out_link = 338 get_phys_connection(sw, next_switch); 339 } 340 341 prev = i_dest; 342 i_dest = i_dest->next; 343 } 344 345 CL_ASSERT(prev->next == NULL); 346 prev->next = concat_dest; 347 concat_dest = dest; 348 } 349 350 i_dest = (reachable_dest_t *) malloc(sizeof(reachable_dest_t)); 351 i_dest->switch_id = sw->id; 352 i_dest->next = concat_dest; 353 *destinations = i_dest; 354} 355 356static void generate_cdg_for_sp(lash_t * p_lash, int sw, int dest_switch, 357 int lane) 358{ 359 unsigned num_switches = p_lash->num_switches; 360 switch_t **switches = p_lash->switches; 361 cdg_vertex_t ****cdg_vertex_matrix = p_lash->cdg_vertex_matrix; 362 int next_switch, output_link, j, exists; 363 cdg_vertex_t *v, *prev = NULL; 364 365 output_link = switches[sw]->routing_table[dest_switch].out_link; 366 next_switch = switches[sw]->phys_connections[output_link]; 367 368 while (sw != dest_switch) { 369 370 if (cdg_vertex_matrix[lane][sw][next_switch] == NULL) { 371 unsigned i; 372 v = create_cdg_vertex(num_switches); 373 374 for (i = 0; i < num_switches - 1; i++) { 375 v->dependency[i] = NULL; 376 v->num_using_this_depend[i] = 0; 377 } 378 379 v->num_using_vertex = 0; 380 v->num_dependencies = 0; 381 v->from = sw; 382 v->to = next_switch; 383 v->seen = 0; 384 v->visiting_number = 0; 385 v->next = NULL; 386 v->temp = 1; 387 v->num_temp_depend = 0; 388 389 cdg_vertex_matrix[lane][sw][next_switch] = v; 390 } else 391 v = cdg_vertex_matrix[lane][sw][next_switch]; 392 393 v->num_using_vertex++; 394 395 if (prev != NULL) { 396 exists = 0; 397 398 for (j = 0; j < prev->num_dependencies; j++) 399 if (prev->dependency[j] == v) { 400 exists = 1; 401 prev->num_using_this_depend[j]++; 402 } 403 404 if (exists == 0) { 405 prev->dependency[prev->num_dependencies] = v; 406 prev->num_using_this_depend[prev->num_dependencies]++; 407 prev->num_dependencies++; 408 409 CL_ASSERT(prev->num_dependencies < (int)num_switches); 410 411 if (prev->temp == 0) 412 prev->num_temp_depend++; 413 414 } 415 } 416 417 sw = next_switch; 418 output_link = switches[sw]->routing_table[dest_switch].out_link; 419 420 if (sw != dest_switch) { 421 CL_ASSERT(output_link != NONE); 422 next_switch = switches[sw]->phys_connections[output_link]; 423 } 424 425 prev = v; 426 } 427} 428 429static void set_temp_depend_to_permanent_for_sp(lash_t * p_lash, int sw, 430 int dest_switch, int lane) 431{ 432 switch_t **switches = p_lash->switches; 433 cdg_vertex_t ****cdg_vertex_matrix = p_lash->cdg_vertex_matrix; 434 int next_switch, output_link; 435 cdg_vertex_t *v; 436 437 output_link = switches[sw]->routing_table[dest_switch].out_link; 438 next_switch = switches[sw]->phys_connections[output_link]; 439 440 while (sw != dest_switch) { 441 v = cdg_vertex_matrix[lane][sw][next_switch]; 442 CL_ASSERT(v != NULL); 443 444 if (v->temp == 1) 445 v->temp = 0; 446 else 447 v->num_temp_depend = 0; 448 449 sw = next_switch; 450 output_link = switches[sw]->routing_table[dest_switch].out_link; 451 452 if (sw != dest_switch) 453 next_switch = 454 switches[sw]->phys_connections[output_link]; 455 } 456 457} 458 459static void remove_temp_depend_for_sp(lash_t * p_lash, int sw, int dest_switch, 460 int lane) 461{ 462 switch_t **switches = p_lash->switches; 463 cdg_vertex_t ****cdg_vertex_matrix = p_lash->cdg_vertex_matrix; 464 int next_switch, output_link, i; 465 cdg_vertex_t *v; 466 467 output_link = switches[sw]->routing_table[dest_switch].out_link; 468 next_switch = switches[sw]->phys_connections[output_link]; 469 470 while (sw != dest_switch) { 471 v = cdg_vertex_matrix[lane][sw][next_switch]; 472 CL_ASSERT(v != NULL); 473 474 if (v->temp == 1) { 475 cdg_vertex_matrix[lane][sw][next_switch] = NULL; 476 free(v); 477 } else { 478 CL_ASSERT(v->num_temp_depend <= v->num_dependencies); 479 v->num_dependencies = 480 v->num_dependencies - v->num_temp_depend; 481 v->num_temp_depend = 0; 482 v->num_using_vertex--; 483 484 for (i = v->num_dependencies; 485 i < p_lash->num_switches - 1; i++) 486 v->num_using_this_depend[i] = 0; 487 } 488 489 sw = next_switch; 490 output_link = switches[sw]->routing_table[dest_switch].out_link; 491 492 if (sw != dest_switch) 493 next_switch = 494 switches[sw]->phys_connections[output_link]; 495 496 } 497} 498 499static void balance_virtual_lanes(lash_t * p_lash, unsigned lanes_needed) 500{ 501 unsigned num_switches = p_lash->num_switches; 502 cdg_vertex_t ****cdg_vertex_matrix = p_lash->cdg_vertex_matrix; 503 int *num_mst_in_lane = p_lash->num_mst_in_lane; 504 int ***virtual_location = p_lash->virtual_location; 505 int min_filled_lane, max_filled_lane, medium_filled_lane, trials; 506 int old_min_filled_lane, old_max_filled_lane, new_num_min_lane, 507 new_num_max_lane; 508 unsigned int i, j; 509 int src, dest, start, next_switch, output_link; 510 int next_switch2, output_link2; 511 int stop = 0, cycle_found; 512 int cycle_found2; 513 514 max_filled_lane = 0; 515 min_filled_lane = lanes_needed - 1; 516 517 if (max_filled_lane > 1) 518 medium_filled_lane = max_filled_lane - 1; 519 520 trials = num_mst_in_lane[max_filled_lane]; 521 if (lanes_needed == 1) 522 stop = 1; 523 524 while (stop == 0) { 525 src = abs(rand()) % (num_switches); 526 dest = abs(rand()) % (num_switches); 527 528 while (virtual_location[src][dest][max_filled_lane] != 1) { 529 start = dest; 530 if (dest == num_switches - 1) 531 dest = 0; 532 else 533 dest++; 534 535 while (dest != start 536 && virtual_location[src][dest][max_filled_lane] 537 != 1) { 538 if (dest == num_switches - 1) 539 dest = 0; 540 else 541 dest++; 542 } 543 544 if (virtual_location[src][dest][max_filled_lane] != 1) { 545 if (src == num_switches - 1) 546 src = 0; 547 else 548 src++; 549 } 550 } 551 552 generate_cdg_for_sp(p_lash, src, dest, min_filled_lane); 553 generate_cdg_for_sp(p_lash, dest, src, min_filled_lane); 554 555 output_link = p_lash->switches[src]->routing_table[dest].out_link; 556 next_switch = p_lash->switches[src]->phys_connections[output_link]; 557 558 output_link2 = p_lash->switches[dest]->routing_table[src].out_link; 559 next_switch2 = p_lash->switches[dest]->phys_connections[output_link2]; 560 561 CL_ASSERT(cdg_vertex_matrix[min_filled_lane][src][next_switch] != NULL); 562 CL_ASSERT(cdg_vertex_matrix[min_filled_lane][dest][next_switch2] != NULL); 563 564 cycle_found = 565 cycle_exists(cdg_vertex_matrix[min_filled_lane][src][next_switch], NULL, NULL, 566 1); 567 cycle_found2 = 568 cycle_exists(cdg_vertex_matrix[min_filled_lane][dest][next_switch2], NULL, NULL, 569 1); 570 571 for (i = 0; i < num_switches; i++) 572 for (j = 0; j < num_switches; j++) 573 if (cdg_vertex_matrix[min_filled_lane][i][j] != NULL) { 574 cdg_vertex_matrix[min_filled_lane][i][j]->visiting_number = 575 0; 576 cdg_vertex_matrix[min_filled_lane][i][j]->seen = 0; 577 } 578 579 if (cycle_found == 1 || cycle_found2 == 1) { 580 remove_temp_depend_for_sp(p_lash, src, dest, min_filled_lane); 581 remove_temp_depend_for_sp(p_lash, dest, src, min_filled_lane); 582 583 virtual_location[src][dest][max_filled_lane] = 2; 584 virtual_location[dest][src][max_filled_lane] = 2; 585 trials--; 586 trials--; 587 } else { 588 set_temp_depend_to_permanent_for_sp(p_lash, src, dest, min_filled_lane); 589 set_temp_depend_to_permanent_for_sp(p_lash, dest, src, min_filled_lane); 590 591 num_mst_in_lane[max_filled_lane]--; 592 num_mst_in_lane[max_filled_lane]--; 593 num_mst_in_lane[min_filled_lane]++; 594 num_mst_in_lane[min_filled_lane]++; 595 596 remove_semipermanent_depend_for_sp(p_lash, src, dest, max_filled_lane); 597 remove_semipermanent_depend_for_sp(p_lash, dest, src, max_filled_lane); 598 virtual_location[src][dest][max_filled_lane] = 0; 599 virtual_location[dest][src][max_filled_lane] = 0; 600 virtual_location[src][dest][min_filled_lane] = 1; 601 virtual_location[dest][src][min_filled_lane] = 1; 602 p_lash->switches[src]->routing_table[dest].lane = min_filled_lane; 603 p_lash->switches[dest]->routing_table[src].lane = min_filled_lane; 604 } 605 606 if (trials == 0) 607 stop = 1; 608 else { 609 if (num_mst_in_lane[max_filled_lane] - num_mst_in_lane[min_filled_lane] < 610 p_lash->balance_limit) 611 stop = 1; 612 } 613 614 old_min_filled_lane = min_filled_lane; 615 old_max_filled_lane = max_filled_lane; 616 617 new_num_min_lane = MAX_INT; 618 new_num_max_lane = 0; 619 620 for (i = 0; i < lanes_needed; i++) { 621 622 if (num_mst_in_lane[i] < new_num_min_lane) { 623 new_num_min_lane = num_mst_in_lane[i]; 624 min_filled_lane = i; 625 } 626 627 if (num_mst_in_lane[i] > new_num_max_lane) { 628 new_num_max_lane = num_mst_in_lane[i]; 629 max_filled_lane = i; 630 } 631 } 632 633 if (old_min_filled_lane != min_filled_lane) { 634 trials = num_mst_in_lane[max_filled_lane]; 635 for (i = 0; i < num_switches; i++) 636 for (j = 0; j < num_switches; j++) 637 if (virtual_location[i][j][max_filled_lane] == 2) 638 virtual_location[i][j][max_filled_lane] = 1; 639 } 640 641 if (old_max_filled_lane != max_filled_lane) { 642 trials = num_mst_in_lane[max_filled_lane]; 643 for (i = 0; i < num_switches; i++) 644 for (j = 0; j < num_switches; j++) 645 if (virtual_location[i][j][old_max_filled_lane] == 2) 646 virtual_location[i][j][old_max_filled_lane] = 1; 647 } 648 } 649} 650 651static switch_t *switch_create(lash_t * p_lash, unsigned id, osm_switch_t * p_sw) 652{ 653 unsigned num_switches = p_lash->num_switches; 654 unsigned num_ports = p_sw->num_ports; 655 switch_t *sw; 656 unsigned int i; 657 658 sw = malloc(sizeof(*sw)); 659 if (!sw) 660 return NULL; 661 662 memset(sw, 0, sizeof(*sw)); 663 664 sw->id = id; 665 sw->dij_channels = malloc(num_ports * sizeof(int)); 666 if (!sw->dij_channels) { 667 free(sw); 668 return NULL; 669 } 670 671 sw->virtual_physical_port_table = malloc(num_ports * sizeof(int)); 672 if (!sw->virtual_physical_port_table) { 673 free(sw->dij_channels); 674 free(sw); 675 return NULL; 676 } 677 678 sw->phys_connections = malloc(num_ports * sizeof(int)); 679 if (!sw->phys_connections) { 680 free(sw->virtual_physical_port_table); 681 free(sw->dij_channels); 682 free(sw); 683 return NULL; 684 } 685 686 sw->routing_table = malloc(num_switches * sizeof(sw->routing_table[0])); 687 if (!sw->routing_table) { 688 free(sw->phys_connections); 689 free(sw->virtual_physical_port_table); 690 free(sw->dij_channels); 691 free(sw); 692 return NULL; 693 } 694 695 for (i = 0; i < num_switches; i++) { 696 sw->routing_table[i].out_link = NONE; 697 sw->routing_table[i].lane = NONE; 698 } 699 700 for (i = 0; i < num_ports; i++) { 701 sw->virtual_physical_port_table[i] = -1; 702 sw->phys_connections[i] = NONE; 703 } 704 705 sw->p_sw = p_sw; 706 if (p_sw) 707 p_sw->priv = sw; 708 709 return sw; 710} 711 712static void switch_delete(switch_t * sw) 713{ 714 if (sw->dij_channels) 715 free(sw->dij_channels); 716 if (sw->virtual_physical_port_table) 717 free(sw->virtual_physical_port_table); 718 if (sw->phys_connections) 719 free(sw->phys_connections); 720 if (sw->routing_table) 721 free(sw->routing_table); 722 free(sw); 723} 724 725static void free_lash_structures(lash_t * p_lash) 726{ 727 unsigned int i, j, k; 728 unsigned num_switches = p_lash->num_switches; 729 osm_log_t *p_log = &p_lash->p_osm->log; 730 731 OSM_LOG_ENTER(p_log); 732 733 // free cdg_vertex_matrix 734 for (i = 0; i < p_lash->vl_min; i++) { 735 for (j = 0; j < num_switches; j++) { 736 for (k = 0; k < num_switches; k++) { 737 if (p_lash->cdg_vertex_matrix[i][j][k]) { 738 739 if (p_lash->cdg_vertex_matrix[i][j][k]->dependency) 740 free(p_lash->cdg_vertex_matrix[i][j][k]-> 741 dependency); 742 743 if (p_lash->cdg_vertex_matrix[i][j][k]-> 744 num_using_this_depend) 745 free(p_lash->cdg_vertex_matrix[i][j][k]-> 746 num_using_this_depend); 747 748 free(p_lash->cdg_vertex_matrix[i][j][k]); 749 } 750 } 751 if (p_lash->cdg_vertex_matrix[i][j]) 752 free(p_lash->cdg_vertex_matrix[i][j]); 753 } 754 if (p_lash->cdg_vertex_matrix[i]) 755 free(p_lash->cdg_vertex_matrix[i]); 756 } 757 758 if (p_lash->cdg_vertex_matrix) 759 free(p_lash->cdg_vertex_matrix); 760 761 // free virtual_location 762 for (i = 0; i < num_switches; i++) { 763 for (j = 0; j < num_switches; j++) { 764 if (p_lash->virtual_location[i][j]) 765 free(p_lash->virtual_location[i][j]); 766 } 767 if (p_lash->virtual_location[i]) 768 free(p_lash->virtual_location[i]); 769 } 770 if (p_lash->virtual_location) 771 free(p_lash->virtual_location); 772 773 if (p_lash->num_mst_in_lane) 774 free(p_lash->num_mst_in_lane); 775 776 OSM_LOG_EXIT(p_log); 777} 778 779static int init_lash_structures(lash_t * p_lash) 780{ 781 unsigned vl_min = p_lash->vl_min; 782 unsigned num_switches = p_lash->num_switches; 783 osm_log_t *p_log = &p_lash->p_osm->log; 784 int status = 0; 785 unsigned int i, j, k; 786 787 OSM_LOG_ENTER(p_log); 788 789 // initialise cdg_vertex_matrix[num_switches][num_switches][num_switches] 790 p_lash->cdg_vertex_matrix = 791 (cdg_vertex_t ****) malloc(vl_min * sizeof(cdg_vertex_t ****)); 792 for (i = 0; i < vl_min; i++) { 793 p_lash->cdg_vertex_matrix[i] = 794 (cdg_vertex_t ***) malloc(num_switches * 795 sizeof(cdg_vertex_t ***)); 796 797 if (p_lash->cdg_vertex_matrix[i] == NULL) 798 goto Exit_Mem_Error; 799 } 800 801 for (i = 0; i < vl_min; i++) { 802 for (j = 0; j < num_switches; j++) { 803 p_lash->cdg_vertex_matrix[i][j] = 804 (cdg_vertex_t **) malloc(num_switches * 805 sizeof(cdg_vertex_t **)); 806 if (p_lash->cdg_vertex_matrix[i][j] == NULL) 807 goto Exit_Mem_Error; 808 809 for (k = 0; k < num_switches; k++) { 810 p_lash->cdg_vertex_matrix[i][j][k] = NULL; 811 } 812 } 813 } 814 815 // initialise virtual_location[num_switches][num_switches][num_layers], 816 // default value = 0 817 p_lash->virtual_location = 818 (int ***)malloc(num_switches * sizeof(int ***)); 819 if (p_lash->virtual_location == NULL) 820 goto Exit_Mem_Error; 821 822 for (i = 0; i < num_switches; i++) { 823 p_lash->virtual_location[i] = 824 (int **)malloc(num_switches * sizeof(int **)); 825 if (p_lash->virtual_location[i] == NULL) 826 goto Exit_Mem_Error; 827 } 828 829 for (i = 0; i < num_switches; i++) { 830 for (j = 0; j < num_switches; j++) { 831 p_lash->virtual_location[i][j] = 832 (int *)malloc(vl_min * sizeof(int *)); 833 if (p_lash->virtual_location[i][j] == NULL) 834 goto Exit_Mem_Error; 835 for (k = 0; k < vl_min; k++) { 836 p_lash->virtual_location[i][j][k] = 0; 837 } 838 } 839 } 840 841 // initialise num_mst_in_lane[num_switches], default 0 842 p_lash->num_mst_in_lane = (int *)malloc(num_switches * sizeof(int)); 843 if (p_lash->num_mst_in_lane == NULL) 844 goto Exit_Mem_Error; 845 memset(p_lash->num_mst_in_lane, 0, 846 num_switches * sizeof(p_lash->num_mst_in_lane[0])); 847 848 goto Exit; 849 850Exit_Mem_Error: 851 status = -1; 852 OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 4D01: " 853 "Could not allocate required memory for LASH errno %d, errno %d for lack of memory\n", 854 errno, ENOMEM); 855 856Exit: 857 OSM_LOG_EXIT(p_log); 858 return status; 859} 860 861static int lash_core(lash_t * p_lash) 862{ 863 osm_log_t *p_log = &p_lash->p_osm->log; 864 unsigned num_switches = p_lash->num_switches; 865 switch_t **switches = p_lash->switches; 866 unsigned lanes_needed = 1; 867 unsigned int i, j, k, dest_switch = 0; 868 reachable_dest_t *dests, *idest; 869 int cycle_found = 0; 870 unsigned v_lane; 871 int stop = 0, output_link, i_next_switch; 872 int output_link2, i_next_switch2; 873 int cycle_found2 = 0; 874 int status = 0; 875 int *switch_bitmap; /* Bitmap to check if we have processed this pair */ 876 877 OSM_LOG_ENTER(p_log); 878 879 for (i = 0; i < num_switches; i++) { 880 881 shortest_path(p_lash, i); 882 generate_routing_func_for_mst(p_lash, i, &dests); 883 884 idest = dests; 885 while (idest != NULL) { 886 dests = dests->next; 887 free(idest); 888 idest = dests; 889 } 890 891 for (j = 0; j < num_switches; j++) { 892 switches[j]->used_channels = 0; 893 switches[j]->q_state = UNQUEUED; 894 } 895 } 896 897 switch_bitmap = malloc(num_switches * num_switches * sizeof(int)); 898 if (!switch_bitmap) { 899 OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 4D04: " 900 "Failed allocating switch_bitmap - out of memory\n"); 901 goto Exit; 902 } 903 memset(switch_bitmap, 0, num_switches * num_switches * sizeof(int)); 904 905 for (i = 0; i < num_switches; i++) { 906 for (dest_switch = 0; dest_switch < num_switches; dest_switch++) 907 if (dest_switch != i && switch_bitmap[i * num_switches + dest_switch] == 0) { 908 v_lane = 0; 909 stop = 0; 910 while (v_lane < lanes_needed && stop == 0) { 911 generate_cdg_for_sp(p_lash, i, dest_switch, v_lane); 912 generate_cdg_for_sp(p_lash, dest_switch, i, v_lane); 913 914 output_link = 915 switches[i]->routing_table[dest_switch].out_link; 916 output_link2 = 917 switches[dest_switch]->routing_table[i].out_link; 918 919 i_next_switch = switches[i]->phys_connections[output_link]; 920 i_next_switch2 = 921 switches[dest_switch]->phys_connections[output_link2]; 922 923 CL_ASSERT(p_lash-> 924 cdg_vertex_matrix[v_lane][i][i_next_switch] != 925 NULL); 926 CL_ASSERT(p_lash-> 927 cdg_vertex_matrix[v_lane][dest_switch] 928 [i_next_switch2] != NULL); 929 930 cycle_found = 931 cycle_exists(p_lash-> 932 cdg_vertex_matrix[v_lane][i] 933 [i_next_switch], NULL, NULL, 1); 934 cycle_found2 = 935 cycle_exists(p_lash-> 936 cdg_vertex_matrix[v_lane][dest_switch] 937 [i_next_switch2], NULL, NULL, 1); 938 939 for (j = 0; j < num_switches; j++) 940 for (k = 0; k < num_switches; k++) 941 if (p_lash-> 942 cdg_vertex_matrix[v_lane][j][k] != 943 NULL) { 944 p_lash-> 945 cdg_vertex_matrix[v_lane][j] 946 [k]->visiting_number = 0; 947 p_lash-> 948 cdg_vertex_matrix[v_lane][j] 949 [k]->seen = 0; 950 } 951 952 if (cycle_found == 1 || cycle_found2 == 1) { 953 remove_temp_depend_for_sp(p_lash, i, dest_switch, 954 v_lane); 955 remove_temp_depend_for_sp(p_lash, dest_switch, i, 956 v_lane); 957 v_lane++; 958 } else { 959 set_temp_depend_to_permanent_for_sp(p_lash, i, 960 dest_switch, 961 v_lane); 962 set_temp_depend_to_permanent_for_sp(p_lash, 963 dest_switch, i, 964 v_lane); 965 stop = 1; 966 p_lash->num_mst_in_lane[v_lane]++; 967 p_lash->num_mst_in_lane[v_lane]++; 968 } 969 } 970 971 switches[i]->routing_table[dest_switch].lane = v_lane; 972 switches[dest_switch]->routing_table[i].lane = v_lane; 973 974 if (cycle_found == 1 || cycle_found2 == 1) { 975 if (++lanes_needed > p_lash->vl_min) 976 goto Error_Not_Enough_Lanes; 977 978 generate_cdg_for_sp(p_lash, i, dest_switch, v_lane); 979 generate_cdg_for_sp(p_lash, dest_switch, i, v_lane); 980 981 set_temp_depend_to_permanent_for_sp(p_lash, i, dest_switch, 982 v_lane); 983 set_temp_depend_to_permanent_for_sp(p_lash, dest_switch, i, 984 v_lane); 985 986 p_lash->num_mst_in_lane[v_lane]++; 987 p_lash->num_mst_in_lane[v_lane]++; 988 } 989 p_lash->virtual_location[i][dest_switch][v_lane] = 1; 990 p_lash->virtual_location[dest_switch][i][v_lane] = 1; 991 992 switch_bitmap[i * num_switches + dest_switch] = 1; 993 switch_bitmap[dest_switch * num_switches + i] = 1; 994 } 995 } 996 997 OSM_LOG(p_log, OSM_LOG_INFO, 998 "Lanes needed: %d, Balancing\n", lanes_needed); 999 1000 for (i = 0; i < lanes_needed; i++) { 1001 OSM_LOG(p_log, OSM_LOG_INFO, "Lanes in layer %d: %d\n", 1002 i, p_lash->num_mst_in_lane[i]); 1003 } 1004 1005 balance_virtual_lanes(p_lash, lanes_needed); 1006 1007 for (i = 0; i < lanes_needed; i++) { 1008 OSM_LOG(p_log, OSM_LOG_INFO, "Lanes in layer %d: %d\n", 1009 i, p_lash->num_mst_in_lane[i]); 1010 } 1011 1012 goto Exit; 1013 1014Error_Not_Enough_Lanes: 1015 status = -1; 1016 OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 4D02: " 1017 "Lane requirements (%d) exceed available lanes (%d)\n", 1018 p_lash->vl_min, lanes_needed); 1019Exit: 1020 if (switch_bitmap) 1021 free(switch_bitmap); 1022 OSM_LOG_EXIT(p_log); 1023 return status; 1024} 1025 1026static unsigned get_lash_id(osm_switch_t * p_sw) 1027{ 1028 return ((switch_t *) p_sw->priv)->id; 1029} 1030 1031static void populate_fwd_tbls(lash_t * p_lash) 1032{ 1033 osm_log_t *p_log = &p_lash->p_osm->log; 1034 osm_subn_t *p_subn = &p_lash->p_osm->subn; 1035 osm_opensm_t *p_osm = p_lash->p_osm; 1036 osm_switch_t *p_sw, *p_next_sw, *p_dst_sw; 1037 osm_port_t *port; 1038 uint16_t max_lid_ho, lid; 1039 1040 OSM_LOG_ENTER(p_log); 1041 1042 p_next_sw = (osm_switch_t *) cl_qmap_head(&p_subn->sw_guid_tbl); 1043 1044 // Go through each swtich individually 1045 while (p_next_sw != (osm_switch_t *) cl_qmap_end(&p_subn->sw_guid_tbl)) { 1046 uint64_t current_guid; 1047 switch_t *sw; 1048 p_sw = p_next_sw; 1049 p_next_sw = (osm_switch_t *) cl_qmap_next(&p_sw->map_item); 1050 1051 max_lid_ho = p_sw->max_lid_ho; 1052 current_guid = p_sw->p_node->node_info.port_guid; 1053 sw = p_sw->priv; 1054 1055 memset(p_sw->new_lft, OSM_NO_PATH, IB_LID_UCAST_END_HO + 1); 1056 1057 for (lid = 1; lid <= max_lid_ho; lid++) { 1058 port = cl_ptr_vector_get(&p_subn->port_lid_tbl, lid); 1059 if (!port) 1060 continue; 1061 1062 p_dst_sw = get_osm_switch_from_port(port); 1063 if (p_dst_sw == p_sw) { 1064 uint8_t egress_port = port->p_node->sw ? 0 : 1065 port->p_physp->p_remote_physp->port_num; 1066 p_sw->new_lft[lid] = egress_port; 1067 OSM_LOG(p_log, OSM_LOG_VERBOSE, 1068 "LASH fwd MY SRC SRC GUID 0x%016" PRIx64 1069 " src lash id (%d), src lid no (%u) src lash port (%d) " 1070 "DST GUID 0x%016" PRIx64 1071 " src lash id (%d), src lash port (%d)\n", 1072 cl_ntoh64(current_guid), -1, lid, 1073 egress_port, cl_ntoh64(current_guid), 1074 -1, egress_port); 1075 } else if (p_dst_sw) { 1076 unsigned dst_lash_switch_id = 1077 get_lash_id(p_dst_sw); 1078 uint8_t lash_egress_port = 1079 (uint8_t) sw-> 1080 routing_table[dst_lash_switch_id].out_link; 1081 uint8_t physical_egress_port = 1082 (uint8_t) sw-> 1083 virtual_physical_port_table 1084 [lash_egress_port]; 1085 1086 p_sw->new_lft[lid] = physical_egress_port; 1087 OSM_LOG(p_log, OSM_LOG_VERBOSE, 1088 "LASH fwd SRC GUID 0x%016" PRIx64 1089 " src lash id (%d), " 1090 "src lid no (%u) src lash port (%d) " 1091 "DST GUID 0x%016" PRIx64 1092 " src lash id (%d), src lash port (%d)\n", 1093 cl_ntoh64(current_guid), sw->id, lid, 1094 lash_egress_port, 1095 cl_ntoh64(p_dst_sw->p_node->node_info. 1096 port_guid), 1097 dst_lash_switch_id, 1098 physical_egress_port); 1099 } 1100 } // for 1101 osm_ucast_mgr_set_fwd_table(&p_osm->sm.ucast_mgr, p_sw); 1102 } 1103 OSM_LOG_EXIT(p_log); 1104} 1105 1106static void osm_lash_process_switch(lash_t * p_lash, osm_switch_t * p_sw) 1107{ 1108 osm_log_t *p_log = &p_lash->p_osm->log; 1109 int i, port_count; 1110 osm_physp_t *p_current_physp, *p_remote_physp; 1111 unsigned switch_a_lash_id, switch_b_lash_id; 1112 1113 OSM_LOG_ENTER(p_log); 1114 1115 switch_a_lash_id = get_lash_id(p_sw); 1116 port_count = osm_node_get_num_physp(p_sw->p_node); 1117 1118 // starting at port 1, ignoring management port on switch 1119 for (i = 1; i < port_count; i++) { 1120 1121 p_current_physp = osm_node_get_physp_ptr(p_sw->p_node, i); 1122 if (p_current_physp) { 1123 p_remote_physp = p_current_physp->p_remote_physp; 1124 if (p_remote_physp && p_remote_physp->p_node->sw) { 1125 int physical_port_a_num = 1126 osm_physp_get_port_num(p_current_physp); 1127 int physical_port_b_num = 1128 osm_physp_get_port_num(p_remote_physp); 1129 switch_b_lash_id = 1130 get_lash_id(p_remote_physp->p_node->sw); 1131 1132 connect_switches(p_lash, switch_a_lash_id, 1133 switch_b_lash_id, 1134 physical_port_a_num); 1135 OSM_LOG(p_log, OSM_LOG_VERBOSE, 1136 "LASH SUCCESS connected G 0x%016" PRIx64 1137 " , lash_id(%u), P(%u) " " to G 0x%016" 1138 PRIx64 " , lash_id(%u) , P(%u)\n", 1139 cl_ntoh64(osm_physp_get_port_guid 1140 (p_current_physp)), 1141 switch_a_lash_id, physical_port_a_num, 1142 cl_ntoh64(osm_physp_get_port_guid 1143 (p_remote_physp)), 1144 switch_b_lash_id, physical_port_b_num); 1145 } 1146 } 1147 } 1148 1149 OSM_LOG_EXIT(p_log); 1150} 1151 1152static void lash_cleanup(lash_t * p_lash) 1153{ 1154 osm_subn_t *p_subn = &p_lash->p_osm->subn; 1155 osm_switch_t *p_next_sw, *p_sw; 1156 1157 /* drop any existing references to old lash switches */ 1158 p_next_sw = (osm_switch_t *) cl_qmap_head(&p_subn->sw_guid_tbl); 1159 while (p_next_sw != (osm_switch_t *) cl_qmap_end(&p_subn->sw_guid_tbl)) { 1160 p_sw = p_next_sw; 1161 p_next_sw = (osm_switch_t *) cl_qmap_next(&p_sw->map_item); 1162 p_sw->priv = NULL; 1163 } 1164 1165 if (p_lash->switches) { 1166 unsigned id; 1167 for (id = 0; ((int)id) < p_lash->num_switches; id++) 1168 if (p_lash->switches[id]) 1169 switch_delete(p_lash->switches[id]); 1170 free(p_lash->switches); 1171 } 1172 p_lash->switches = NULL; 1173} 1174 1175/* 1176 static int discover_network_properties() 1177 Traverse the topology of the network in order to determine 1178 - the maximum number of switches, 1179 - the minimum number of virtual layers 1180*/ 1181 1182static int discover_network_properties(lash_t * p_lash) 1183{ 1184 int i = 0, id = 0; 1185 uint8_t vl_min; 1186 osm_subn_t *p_subn = &p_lash->p_osm->subn; 1187 osm_switch_t *p_next_sw, *p_sw; 1188 osm_log_t *p_log = &p_lash->p_osm->log; 1189 1190 p_lash->num_switches = cl_qmap_count(&p_subn->sw_guid_tbl); 1191 1192 p_lash->switches = malloc(p_lash->num_switches * sizeof(switch_t *)); 1193 if (!p_lash->switches) 1194 return -1; 1195 memset(p_lash->switches, 0, p_lash->num_switches * sizeof(switch_t *)); 1196 1197 vl_min = 5; // set to a high value 1198 1199 p_next_sw = (osm_switch_t *) cl_qmap_head(&p_subn->sw_guid_tbl); 1200 while (p_next_sw != (osm_switch_t *) cl_qmap_end(&p_subn->sw_guid_tbl)) { 1201 uint16_t port_count; 1202 p_sw = p_next_sw; 1203 p_next_sw = (osm_switch_t *) cl_qmap_next(&p_sw->map_item); 1204 1205 p_lash->switches[id] = switch_create(p_lash, id, p_sw); 1206 if (!p_lash->switches[id]) 1207 return -1; 1208 id++; 1209 1210 port_count = osm_node_get_num_physp(p_sw->p_node); 1211 1212 // Note, ignoring port 0. management port 1213 for (i = 1; i < port_count; i++) { 1214 osm_physp_t *p_current_physp = 1215 osm_node_get_physp_ptr(p_sw->p_node, i); 1216 1217 if (p_current_physp 1218 && p_current_physp->p_remote_physp) { 1219 1220 ib_port_info_t *p_port_info = 1221 &p_current_physp->port_info; 1222 uint8_t port_vl_min = 1223 ib_port_info_get_op_vls(p_port_info); 1224 if (port_vl_min && port_vl_min < vl_min) 1225 vl_min = port_vl_min; 1226 } 1227 } // for 1228 } // while 1229 1230 vl_min = 1 << (vl_min - 1); 1231 if (vl_min > 15) 1232 vl_min = 15; 1233 1234 p_lash->vl_min = vl_min; 1235 1236 OSM_LOG(p_log, OSM_LOG_INFO, 1237 "min operational vl(%d) max_switches(%d)\n", p_lash->vl_min, 1238 p_lash->num_switches); 1239 return 0; 1240} 1241 1242static void process_switches(lash_t * p_lash) 1243{ 1244 osm_switch_t *p_sw, *p_next_sw; 1245 osm_subn_t *p_subn = &p_lash->p_osm->subn; 1246 1247 /* Go through each swithc and process it. i.e build the connection 1248 structure required by LASH */ 1249 p_next_sw = (osm_switch_t *) cl_qmap_head(&p_subn->sw_guid_tbl); 1250 while (p_next_sw != (osm_switch_t *) cl_qmap_end(&p_subn->sw_guid_tbl)) { 1251 p_sw = p_next_sw; 1252 p_next_sw = (osm_switch_t *) cl_qmap_next(&p_sw->map_item); 1253 1254 osm_lash_process_switch(p_lash, p_sw); 1255 } 1256} 1257 1258static int lash_process(void *context) 1259{ 1260 lash_t *p_lash = context; 1261 osm_log_t *p_log = &p_lash->p_osm->log; 1262 int return_status = IB_SUCCESS; 1263 1264 OSM_LOG_ENTER(p_log); 1265 1266 p_lash->balance_limit = 6; 1267 1268 // everything starts here 1269 lash_cleanup(p_lash); 1270 1271 discover_network_properties(p_lash); 1272 1273 return_status = init_lash_structures(p_lash); 1274 if (return_status != IB_SUCCESS) 1275 goto Exit; 1276 1277 process_switches(p_lash); 1278 1279 return_status = lash_core(p_lash); 1280 if (return_status != IB_SUCCESS) 1281 goto Exit; 1282 1283 populate_fwd_tbls(p_lash); 1284 1285Exit: 1286 free_lash_structures(p_lash); 1287 OSM_LOG_EXIT(p_log); 1288 1289 return return_status; 1290} 1291 1292static lash_t *lash_create(osm_opensm_t * p_osm) 1293{ 1294 lash_t *p_lash; 1295 1296 p_lash = malloc(sizeof(lash_t)); 1297 if (!p_lash) 1298 return NULL; 1299 1300 memset(p_lash, 0, sizeof(lash_t)); 1301 p_lash->p_osm = p_osm; 1302 1303 return (p_lash); 1304} 1305 1306static void lash_delete(void *context) 1307{ 1308 lash_t *p_lash = context; 1309 if (p_lash->switches) { 1310 unsigned id; 1311 for (id = 0; ((int)id) < p_lash->num_switches; id++) 1312 if (p_lash->switches[id]) 1313 switch_delete(p_lash->switches[id]); 1314 free(p_lash->switches); 1315 } 1316 free(p_lash); 1317} 1318 1319uint8_t osm_get_lash_sl(osm_opensm_t * p_osm, osm_port_t * p_src_port, 1320 osm_port_t * p_dst_port) 1321{ 1322 unsigned dst_id; 1323 unsigned src_id; 1324 osm_switch_t *p_sw; 1325 1326 if (p_osm->routing_engine_used != OSM_ROUTING_ENGINE_TYPE_LASH) 1327 return OSM_DEFAULT_SL; 1328 1329 p_sw = get_osm_switch_from_port(p_dst_port); 1330 if (!p_sw || !p_sw->priv) 1331 return OSM_DEFAULT_SL; 1332 dst_id = get_lash_id(p_sw); 1333 1334 p_sw = get_osm_switch_from_port(p_src_port); 1335 if (!p_sw || !p_sw->priv) 1336 return OSM_DEFAULT_SL; 1337 1338 src_id = get_lash_id(p_sw); 1339 if (src_id == dst_id) 1340 return OSM_DEFAULT_SL; 1341 1342 return (uint8_t) ((switch_t *) p_sw->priv)->routing_table[dst_id].lane; 1343} 1344 1345int osm_ucast_lash_setup(struct osm_routing_engine *r, osm_opensm_t *p_osm) 1346{ 1347 lash_t *p_lash = lash_create(p_osm); 1348 if (!p_lash) 1349 return -1; 1350 1351 r->context = p_lash; 1352 r->ucast_build_fwd_tables = lash_process; 1353 r->delete = lash_delete; 1354 1355 return 0; 1356} 1357