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