1/*
2 * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2002-2007,2009 Mellanox Technologies LTD. All rights reserved.
4 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5 * Copyright (c) 2009 HNR Consulting. All rights reserved.
6 *
7 * This software is available to you under a choice of one of two
8 * licenses.  You may choose to be licensed under the terms of the GNU
9 * General Public License (GPL) Version 2, available from the file
10 * COPYING in the main directory of this source tree, or the
11 * OpenIB.org BSD license below:
12 *
13 *     Redistribution and use in source and binary forms, with or
14 *     without modification, are permitted provided that the following
15 *     conditions are met:
16 *
17 *      - Redistributions of source code must retain the above
18 *        copyright notice, this list of conditions and the following
19 *        disclaimer.
20 *
21 *      - Redistributions in binary form must reproduce the above
22 *        copyright notice, this list of conditions and the following
23 *        disclaimer in the documentation and/or other materials
24 *        provided with the distribution.
25 *
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
30 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
31 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
32 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33 * SOFTWARE.
34 *
35 */
36
37/*
38 * Abstract:
39 *      Implementation of Up Down Algorithm using ranking & Min Hop
40 *      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 <ctype.h>
49#include <complib/cl_debug.h>
50#include <complib/cl_qmap.h>
51#include <opensm/osm_file_ids.h>
52#define FILE_ID OSM_FILE_UCAST_UPDN_C
53#include <opensm/osm_switch.h>
54#include <opensm/osm_opensm.h>
55#include <opensm/osm_ucast_mgr.h>
56
57/* //////////////////////////// */
58/*  Local types                 */
59/* //////////////////////////// */
60
61/* direction */
62typedef enum updn_switch_dir {
63	UP = 0,
64	DOWN
65} updn_switch_dir_t;
66
67/* updn structure */
68typedef struct updn {
69	unsigned num_roots;
70	osm_opensm_t *p_osm;
71} updn_t;
72
73struct updn_node {
74	cl_list_item_t list;
75	osm_switch_t *sw;
76	uint64_t id;
77	updn_switch_dir_t dir;
78	unsigned rank;
79	unsigned visited;
80};
81
82/* This function returns direction based on rank and guid info of current &
83   remote ports */
84static updn_switch_dir_t updn_get_dir(unsigned cur_rank, unsigned rem_rank,
85				      uint64_t cur_id, uint64_t rem_id)
86{
87	/* HACK: comes to solve root nodes connection, in a classic subnet root nodes do not connect
88	   directly, but in case they are we assign to root node an UP direction to allow UPDN to discover
89	   the subnet correctly (and not from the point of view of the last root node).
90	 */
91	if (!cur_rank && !rem_rank)
92		return UP;
93
94	if (cur_rank < rem_rank)
95		return DOWN;
96	else if (cur_rank > rem_rank)
97		return UP;
98	else {
99		/* Equal rank, decide by id number, bigger == UP direction */
100		if (cur_id > rem_id)
101			return UP;
102		else
103			return DOWN;
104	}
105}
106
107/**********************************************************************
108 * This function does the bfs of min hop table calculation by guid index
109 * as a starting point.
110 **********************************************************************/
111static int updn_bfs_by_node(IN osm_log_t * p_log, IN osm_subn_t * p_subn,
112			    IN osm_switch_t * p_sw)
113{
114	uint8_t pn, pn_rem;
115	cl_qlist_t list;
116	uint16_t lid;
117	struct updn_node *u;
118	updn_switch_dir_t next_dir, current_dir;
119
120	OSM_LOG_ENTER(p_log);
121
122	lid = osm_node_get_base_lid(p_sw->p_node, 0);
123	lid = cl_ntoh16(lid);
124	osm_switch_set_hops(p_sw, lid, 0, 0);
125
126	OSM_LOG(p_log, OSM_LOG_DEBUG,
127		"Starting from switch - port GUID 0x%" PRIx64 " lid %u\n",
128		cl_ntoh64(p_sw->p_node->node_info.port_guid), lid);
129
130	u = p_sw->priv;
131	u->dir = UP;
132
133	/* Update list with the new element */
134	cl_qlist_init(&list);
135	cl_qlist_insert_tail(&list, &u->list);
136
137	/* BFS the list till no next element */
138	while (!cl_is_qlist_empty(&list)) {
139		u = (struct updn_node *)cl_qlist_remove_head(&list);
140		u->visited = 0;	/* cleanup */
141		current_dir = u->dir;
142		/* Go over all ports of the switch and find unvisited remote nodes */
143		for (pn = 1; pn < u->sw->num_ports; pn++) {
144			osm_node_t *p_remote_node;
145			struct updn_node *rem_u;
146			uint8_t current_min_hop, remote_min_hop,
147			    set_hop_return_value;
148			osm_switch_t *p_remote_sw;
149
150			p_remote_node =
151			    osm_node_get_remote_node(u->sw->p_node, pn,
152						     &pn_rem);
153			/* If no remote node OR remote node is not a SWITCH
154			   continue to next pn */
155			if (!p_remote_node || !p_remote_node->sw)
156				continue;
157			/* Fetch remote guid only after validation of remote node */
158			p_remote_sw = p_remote_node->sw;
159			rem_u = p_remote_sw->priv;
160			/* Decide which direction to mark it (UP/DOWN) */
161			next_dir = updn_get_dir(u->rank, rem_u->rank,
162						u->id, rem_u->id);
163
164			/* Check if this is a legal step : the only illegal step is going
165			   from DOWN to UP */
166			if ((current_dir == DOWN) && (next_dir == UP)) {
167				OSM_LOG(p_log, OSM_LOG_DEBUG,
168					"Avoiding move from 0x%016" PRIx64
169					" to 0x%016" PRIx64 "\n",
170					cl_ntoh64(osm_node_get_node_guid(u->sw->p_node)),
171					cl_ntoh64(osm_node_get_node_guid(p_remote_node)));
172				/* Illegal step */
173				continue;
174			}
175			/* Set MinHop value for the current lid */
176			current_min_hop = osm_switch_get_least_hops(u->sw, lid);
177			/* Check hop count if better insert into list && update
178			   the remote node Min Hop Table */
179			remote_min_hop =
180			    osm_switch_get_hop_count(p_remote_sw, lid, pn_rem);
181			if (current_min_hop + 1 < remote_min_hop) {
182				set_hop_return_value =
183				    osm_switch_set_hops(p_remote_sw, lid,
184							pn_rem,
185							current_min_hop + 1);
186				if (set_hop_return_value) {
187					OSM_LOG(p_log, OSM_LOG_ERROR, "ERR AA01: "
188						"Invalid value returned from set min hop is: %d\n",
189						set_hop_return_value);
190				}
191				/* Check if remote port has already been visited */
192				if (!rem_u->visited) {
193					/* Insert updn_switch item into the list */
194					rem_u->dir = next_dir;
195					rem_u->visited = 1;
196					cl_qlist_insert_tail(&list,
197							     &rem_u->list);
198				}
199			}
200		}
201	}
202
203	OSM_LOG_EXIT(p_log);
204	return 0;
205}
206
207/* NOTE : PLS check if we need to decide that the first */
208/*        rank is a SWITCH for BFS purpose */
209static int updn_subn_rank(IN updn_t * p_updn)
210{
211	osm_switch_t *p_sw;
212	osm_physp_t *p_physp, *p_remote_physp;
213	cl_qlist_t list;
214	cl_map_item_t *item;
215	struct updn_node *u, *remote_u;
216	uint8_t num_ports, port_num;
217	osm_log_t *p_log = &p_updn->p_osm->log;
218	unsigned max_rank = 0;
219
220	OSM_LOG_ENTER(p_log);
221	cl_qlist_init(&list);
222
223	/* add all roots to the list */
224	for (item = cl_qmap_head(&p_updn->p_osm->subn.sw_guid_tbl);
225	     item != cl_qmap_end(&p_updn->p_osm->subn.sw_guid_tbl);
226	     item = cl_qmap_next(item)) {
227		p_sw = (osm_switch_t *)item;
228		u = p_sw->priv;
229		if (!u->rank)
230			cl_qlist_insert_tail(&list, &u->list);
231	}
232
233	/* BFS the list till it's empty */
234	while (!cl_is_qlist_empty(&list)) {
235		u = (struct updn_node *)cl_qlist_remove_head(&list);
236		/* Go over all remote nodes and rank them (if not already visited) */
237		p_sw = u->sw;
238		num_ports = p_sw->num_ports;
239		OSM_LOG(p_log, OSM_LOG_DEBUG,
240			"Handling switch GUID 0x%" PRIx64 "\n",
241			cl_ntoh64(osm_node_get_node_guid(p_sw->p_node)));
242		for (port_num = 1; port_num < num_ports; port_num++) {
243			ib_net64_t port_guid;
244
245			/* Current port fetched in order to get remote side */
246			p_physp =
247			    osm_node_get_physp_ptr(p_sw->p_node, port_num);
248
249			if (!p_physp)
250				continue;
251
252			p_remote_physp = p_physp->p_remote_physp;
253
254			/*
255			   make sure that all the following occur on p_remote_physp:
256			   1. The port isn't NULL
257			   2. It is a switch
258			 */
259			if (p_remote_physp && p_remote_physp->p_node->sw) {
260				remote_u = p_remote_physp->p_node->sw->priv;
261				port_guid = p_remote_physp->port_guid;
262
263				if (remote_u->rank > u->rank + 1) {
264					remote_u->rank = u->rank + 1;
265					max_rank = remote_u->rank;
266					cl_qlist_insert_tail(&list,
267							     &remote_u->list);
268					OSM_LOG(p_log, OSM_LOG_DEBUG,
269						"Rank of port GUID 0x%" PRIx64
270						" = %u\n", cl_ntoh64(port_guid),
271						remote_u->rank);
272				}
273			}
274		}
275	}
276
277	/* Print Summary of ranking */
278	OSM_LOG(p_log, OSM_LOG_VERBOSE,
279		"Subnet ranking completed. Max Node Rank = %d\n", max_rank);
280	OSM_LOG_EXIT(p_log);
281	return 0;
282}
283
284/* hack: preserve min hops entries to any other root switches */
285static void updn_clear_non_root_hops(updn_t * updn, osm_switch_t * sw)
286{
287	osm_port_t *port;
288	unsigned i;
289
290	for (i = 0; i < sw->num_hops; i++)
291		if (sw->hops[i]) {
292			port = osm_get_port_by_lid_ho(&updn->p_osm->subn, i);
293			if (!port || !port->p_node->sw
294			    || ((struct updn_node *)port->p_node->sw->priv)->
295			    rank != 0)
296				memset(sw->hops[i], 0xff, sw->num_ports);
297		}
298}
299
300static int updn_set_min_hop_table(IN updn_t * p_updn)
301{
302	osm_subn_t *p_subn = &p_updn->p_osm->subn;
303	osm_log_t *p_log = &p_updn->p_osm->log;
304	osm_switch_t *p_sw;
305	cl_map_item_t *item;
306
307	OSM_LOG_ENTER(p_log);
308
309	/* Go over all the switches in the subnet - for each init their Min Hop
310	   Table */
311	OSM_LOG(p_log, OSM_LOG_VERBOSE,
312		"Init Min Hop Table of all switches [\n");
313
314	for (item = cl_qmap_head(&p_updn->p_osm->subn.sw_guid_tbl);
315	     item != cl_qmap_end(&p_updn->p_osm->subn.sw_guid_tbl);
316	     item = cl_qmap_next(item)) {
317		p_sw = (osm_switch_t *)item;
318		/* Clear Min Hop Table */
319		if (p_subn->opt.connect_roots)
320			updn_clear_non_root_hops(p_updn, p_sw);
321		else
322			osm_switch_clear_hops(p_sw);
323	}
324
325	OSM_LOG(p_log, OSM_LOG_VERBOSE,
326		"Init Min Hop Table of all switches ]\n");
327
328	/* Now do the BFS for each port  in the subnet */
329	OSM_LOG(p_log, OSM_LOG_VERBOSE,
330		"BFS through all port guids in the subnet [\n");
331
332	for (item = cl_qmap_head(&p_updn->p_osm->subn.sw_guid_tbl);
333	     item != cl_qmap_end(&p_updn->p_osm->subn.sw_guid_tbl);
334	     item = cl_qmap_next(item)) {
335		p_sw = (osm_switch_t *)item;
336		updn_bfs_by_node(p_log, p_subn, p_sw);
337	}
338
339	OSM_LOG(p_log, OSM_LOG_VERBOSE,
340		"BFS through all port guids in the subnet ]\n");
341	/* Cleanup */
342	OSM_LOG_EXIT(p_log);
343	return 0;
344}
345
346static int updn_build_lid_matrices(IN updn_t * p_updn)
347{
348	int status;
349
350	OSM_LOG_ENTER(&p_updn->p_osm->log);
351
352	OSM_LOG(&p_updn->p_osm->log, OSM_LOG_VERBOSE,
353		"Ranking all port guids in the list\n");
354	if (!p_updn->num_roots) {
355		OSM_LOG(&p_updn->p_osm->log, OSM_LOG_ERROR, "ERR AA0A: "
356			"No guids were provided or number of guids is 0\n");
357		status = -1;
358		goto _exit;
359	}
360
361	/* Check if it's not a switched subnet */
362	if (cl_is_qmap_empty(&p_updn->p_osm->subn.sw_guid_tbl)) {
363		OSM_LOG(&p_updn->p_osm->log, OSM_LOG_ERROR, "ERR AA0B: "
364			"This is not a switched subnet, cannot perform UPDN algorithm\n");
365		status = -1;
366		goto _exit;
367	}
368
369	/* Rank the subnet switches */
370	if (updn_subn_rank(p_updn)) {
371		OSM_LOG(&p_updn->p_osm->log, OSM_LOG_ERROR, "ERR AA0E: "
372			"Failed to assign ranks\n");
373		status = -1;
374		goto _exit;
375	}
376
377	/* After multiple ranking need to set Min Hop Table by UpDn algorithm  */
378	OSM_LOG(&p_updn->p_osm->log, OSM_LOG_VERBOSE,
379		"Setting all switches' Min Hop Table\n");
380	status = updn_set_min_hop_table(p_updn);
381
382_exit:
383	OSM_LOG_EXIT(&p_updn->p_osm->log);
384	return status;
385}
386
387static struct updn_node *create_updn_node(osm_switch_t * sw)
388{
389	struct updn_node *u;
390
391	u = malloc(sizeof(*u));
392	if (!u)
393		return NULL;
394	memset(u, 0, sizeof(*u));
395	u->sw = sw;
396	u->id = cl_ntoh64(osm_node_get_node_guid(sw->p_node));
397	u->rank = 0xffffffff;
398	return u;
399}
400
401static void delete_updn_node(struct updn_node *u)
402{
403	u->sw->priv = NULL;
404	free(u);
405}
406
407/* Find Root nodes automatically by Min Hop Table info */
408static void updn_find_root_nodes_by_min_hop(OUT updn_t * p_updn)
409{
410	osm_opensm_t *p_osm = p_updn->p_osm;
411	osm_switch_t *p_sw;
412	osm_port_t *p_port;
413	osm_physp_t *p_physp;
414	cl_map_item_t *item;
415	double thd1, thd2;
416	unsigned i, cas_num = 0;
417	unsigned *cas_per_sw;
418	uint16_t lid_ho;
419
420	OSM_LOG_ENTER(&p_osm->log);
421
422	OSM_LOG(&p_osm->log, OSM_LOG_DEBUG,
423		"Current number of ports in the subnet is %d\n",
424		cl_qmap_count(&p_osm->subn.port_guid_tbl));
425
426	lid_ho = (uint16_t) cl_ptr_vector_get_size(&p_updn->p_osm->subn.port_lid_tbl) + 1;
427	cas_per_sw = malloc(lid_ho * sizeof(*cas_per_sw));
428	if (!cas_per_sw) {
429		OSM_LOG(&p_osm->log, OSM_LOG_ERROR, "ERR AA14: "
430			"cannot alloc mem for CAs per switch counter array\n");
431		goto _exit;
432	}
433	memset(cas_per_sw, 0, lid_ho * sizeof(*cas_per_sw));
434
435	/* Find the Maximum number of CAs (and routers) for histogram normalization */
436	OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
437		"Finding the number of CAs and storing them in cl_map\n");
438	for (item = cl_qmap_head(&p_updn->p_osm->subn.port_guid_tbl);
439	     item != cl_qmap_end(&p_updn->p_osm->subn.port_guid_tbl);
440	     item = cl_qmap_next(item)) {
441		p_port = (osm_port_t *)item;
442		if (!p_port->p_node->sw) {
443			p_physp = p_port->p_physp->p_remote_physp;
444			if (!p_physp || !p_physp->p_node->sw)
445				continue;
446			lid_ho = osm_node_get_base_lid(p_physp->p_node, 0);
447			lid_ho = cl_ntoh16(lid_ho);
448			cas_per_sw[lid_ho]++;
449			cas_num++;
450		}
451	}
452
453	thd1 = cas_num * 0.9;
454	thd2 = cas_num * 0.05;
455	OSM_LOG(&p_osm->log, OSM_LOG_DEBUG,
456		"Found %u CAs and RTRs, %u SWs in the subnet. "
457		"Thresholds are thd1 = %f && thd2 = %f\n",
458		cas_num, cl_qmap_count(&p_osm->subn.sw_guid_tbl), thd1, thd2);
459
460	OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
461		"Passing through all switches to collect Min Hop info\n");
462	for (item = cl_qmap_head(&p_updn->p_osm->subn.sw_guid_tbl);
463	     item != cl_qmap_end(&p_updn->p_osm->subn.sw_guid_tbl);
464	     item = cl_qmap_next(item)) {
465		unsigned hop_hist[IB_SUBNET_PATH_HOPS_MAX];
466		uint16_t max_lid_ho;
467		uint8_t hop_val;
468		uint16_t numHopBarsOverThd1 = 0;
469		uint16_t numHopBarsOverThd2 = 0;
470
471		p_sw = (osm_switch_t *) item;
472
473		memset(hop_hist, 0, sizeof(hop_hist));
474
475		max_lid_ho = p_sw->max_lid_ho;
476		for (lid_ho = 1; lid_ho <= max_lid_ho; lid_ho++)
477			if (cas_per_sw[lid_ho]) {
478				hop_val =
479				    osm_switch_get_least_hops(p_sw, lid_ho);
480				if (hop_val >= IB_SUBNET_PATH_HOPS_MAX)
481					continue;
482
483				hop_hist[hop_val] += cas_per_sw[lid_ho];
484			}
485
486		/* Now recognize the spines by requiring one bar to be
487		   above 90% of the number of CAs and RTRs */
488		for (i = 0; i < IB_SUBNET_PATH_HOPS_MAX; i++) {
489			if (hop_hist[i] > thd1)
490				numHopBarsOverThd1++;
491			if (hop_hist[i] > thd2)
492				numHopBarsOverThd2++;
493		}
494
495		/* If thd conditions are valid - rank the root node */
496		if (numHopBarsOverThd1 == 1 && numHopBarsOverThd2 == 1) {
497			OSM_LOG(&p_osm->log, OSM_LOG_DEBUG,
498				"Ranking GUID 0x%" PRIx64 " as root node\n",
499				cl_ntoh64(osm_node_get_node_guid(p_sw->p_node)));
500			((struct updn_node *)p_sw->priv)->rank = 0;
501			p_updn->num_roots++;
502		}
503	}
504
505	free(cas_per_sw);
506_exit:
507	OSM_LOG_EXIT(&p_osm->log);
508	return;
509}
510
511static void dump_roots(cl_map_item_t *item, FILE *file, void *cxt)
512{
513	osm_switch_t *sw = (osm_switch_t *)item;
514	if (!((struct updn_node *)sw->priv)->rank)
515		fprintf(file, "0x%" PRIx64 "\n",
516			cl_ntoh64(osm_node_get_node_guid(sw->p_node)));
517}
518
519static int update_id(void *cxt, uint64_t guid, char *p)
520{
521	osm_opensm_t *osm = cxt;
522	osm_switch_t *sw;
523	uint64_t id;
524	char *e;
525
526	sw = osm_get_switch_by_guid(&osm->subn, cl_hton64(guid));
527	if (!sw) {
528		OSM_LOG(&osm->log, OSM_LOG_VERBOSE,
529			"switch with guid 0x%" PRIx64 " is not found\n", guid);
530		return 0;
531	}
532
533	id = strtoull(p, &e, 0);
534	if (*e && !isspace(*e)) {
535		OSM_LOG(&osm->log, OSM_LOG_ERROR,
536			"ERR AA05: cannot parse node id \'%s\'", p);
537		return -1;
538	}
539
540	OSM_LOG(&osm->log, OSM_LOG_DEBUG,
541		"update node 0x%" PRIx64 " id to 0x%" PRIx64 "\n", guid, id);
542
543	((struct updn_node *)sw->priv)->id = id;
544
545	return 0;
546}
547
548static int rank_root_node(void *cxt, uint64_t guid, char *p)
549{
550	updn_t *updn = cxt;
551	osm_switch_t *sw;
552
553	sw = osm_get_switch_by_guid(&updn->p_osm->subn, cl_hton64(guid));
554	if (!sw) {
555		OSM_LOG(&updn->p_osm->log, OSM_LOG_VERBOSE,
556			"switch with guid 0x%" PRIx64 " is not found\n", guid);
557		return 0;
558	}
559
560	OSM_LOG(&updn->p_osm->log, OSM_LOG_DEBUG,
561		"Ranking root port GUID 0x%" PRIx64 "\n", guid);
562
563	((struct updn_node *)sw->priv)->rank = 0;
564	updn->num_roots++;
565
566	return 0;
567}
568
569/* UPDN callback function */
570static int updn_lid_matrices(void *ctx)
571{
572	updn_t *p_updn = ctx;
573	cl_map_item_t *item;
574	osm_switch_t *p_sw;
575	int ret = 0;
576
577	OSM_LOG_ENTER(&p_updn->p_osm->log);
578
579	for (item = cl_qmap_head(&p_updn->p_osm->subn.sw_guid_tbl);
580	     item != cl_qmap_end(&p_updn->p_osm->subn.sw_guid_tbl);
581	     item = cl_qmap_next(item)) {
582		p_sw = (osm_switch_t *)item;
583		p_sw->priv = create_updn_node(p_sw);
584		if (!p_sw->priv) {
585			OSM_LOG(&(p_updn->p_osm->log), OSM_LOG_ERROR, "ERR AA0C: "
586				"cannot create updn node\n");
587			OSM_LOG_EXIT(&p_updn->p_osm->log);
588			return -1;
589		}
590	}
591
592	/* First setup root nodes */
593	p_updn->num_roots = 0;
594
595	if (p_updn->p_osm->subn.opt.root_guid_file) {
596		OSM_LOG(&p_updn->p_osm->log, OSM_LOG_DEBUG,
597			"UPDN - Fetching root nodes from file \'%s\'\n",
598			p_updn->p_osm->subn.opt.root_guid_file);
599
600		ret = parse_node_map(p_updn->p_osm->subn.opt.root_guid_file,
601				     rank_root_node, p_updn);
602		if (ret) {
603			OSM_LOG(&p_updn->p_osm->log, OSM_LOG_ERROR, "ERR AA02: "
604				"cannot parse root guids file \'%s\'\n",
605				p_updn->p_osm->subn.opt.root_guid_file);
606			osm_ucast_mgr_build_lid_matrices(&p_updn->p_osm->sm.ucast_mgr);
607			updn_find_root_nodes_by_min_hop(p_updn);
608		} else if (p_updn->p_osm->subn.opt.connect_roots &&
609			   p_updn->num_roots > 1)
610			osm_ucast_mgr_build_lid_matrices(&p_updn->p_osm->sm.ucast_mgr);
611	} else {
612		osm_ucast_mgr_build_lid_matrices(&p_updn->p_osm->sm.ucast_mgr);
613		updn_find_root_nodes_by_min_hop(p_updn);
614	}
615
616	if (p_updn->p_osm->subn.opt.ids_guid_file) {
617		OSM_LOG(&p_updn->p_osm->log, OSM_LOG_DEBUG,
618			"UPDN - update node ids from file \'%s\'\n",
619			p_updn->p_osm->subn.opt.ids_guid_file);
620
621		ret = parse_node_map(p_updn->p_osm->subn.opt.ids_guid_file,
622				     update_id, p_updn->p_osm);
623		if (ret)
624			OSM_LOG(&p_updn->p_osm->log, OSM_LOG_ERROR, "ERR AA03: "
625				"cannot parse node ids file \'%s\'\n",
626				p_updn->p_osm->subn.opt.ids_guid_file);
627	}
628
629	/* Only if there are assigned root nodes do the algorithm, otherwise perform do nothing */
630	if (p_updn->num_roots) {
631		OSM_LOG(&p_updn->p_osm->log, OSM_LOG_DEBUG,
632			"activating UPDN algorithm\n");
633		ret = updn_build_lid_matrices(p_updn);
634	} else {
635		OSM_LOG(&p_updn->p_osm->log, OSM_LOG_INFO,
636			"disabling UPDN algorithm, no root nodes were found\n");
637		ret = -1;
638	}
639
640	if (OSM_LOG_IS_ACTIVE_V2(&p_updn->p_osm->log, OSM_LOG_ROUTING))
641		osm_dump_qmap_to_file(p_updn->p_osm, "opensm-updn-roots.dump",
642				      &p_updn->p_osm->subn.sw_guid_tbl,
643				      dump_roots, NULL);
644
645	for (item = cl_qmap_head(&p_updn->p_osm->subn.sw_guid_tbl);
646	     item != cl_qmap_end(&p_updn->p_osm->subn.sw_guid_tbl);
647	     item = cl_qmap_next(item)) {
648		p_sw = (osm_switch_t *) item;
649		delete_updn_node(p_sw->priv);
650	}
651
652	OSM_LOG_EXIT(&p_updn->p_osm->log);
653	return ret;
654}
655
656static void updn_delete(void *context)
657{
658	free(context);
659}
660
661int osm_ucast_updn_setup(struct osm_routing_engine *r, osm_opensm_t *osm)
662{
663	updn_t *updn;
664
665	updn = malloc(sizeof(updn_t));
666	if (!updn)
667		return -1;
668	memset(updn, 0, sizeof(updn_t));
669
670	updn->p_osm = osm;
671
672	r->context = updn;
673	r->destroy = updn_delete;
674	r->build_lid_matrices = updn_lid_matrices;
675
676	return 0;
677}
678