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