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 */
62 typedef enum updn_switch_dir {
63 	UP = 0,
64 	DOWN
65 } updn_switch_dir_t;
66 
67 /* updn structure */
68 typedef struct updn {
69 	unsigned num_roots;
70 	osm_opensm_t *p_osm;
71 } updn_t;
72 
73 struct 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 */
84 static 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  **********************************************************************/
111 static 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 */
209 static 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 */
285 static 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 
300 static 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 
346 static 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 
387 static 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 
401 static 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 */
408 static 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 
511 static 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 
519 static 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 
548 static 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 */
570 static 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 
656 static void updn_delete(void *context)
657 {
658 	free(context);
659 }
660 
661 int 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