1 /*
2  * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2008 Mellanox Technologies LTD. All rights reserved.
4  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5  *
6  * This software is available to you under a choice of one of two
7  * licenses.  You may choose to be licensed under the terms of the GNU
8  * General Public License (GPL) Version 2, available from the file
9  * COPYING in the main directory of this source tree, or the
10  * OpenIB.org BSD license below:
11  *
12  *     Redistribution and use in source and binary forms, with or
13  *     without modification, are permitted provided that the following
14  *     conditions are met:
15  *
16  *      - Redistributions of source code must retain the above
17  *        copyright notice, this list of conditions and the following
18  *        disclaimer.
19  *
20  *      - Redistributions in binary form must reproduce the above
21  *        copyright notice, this list of conditions and the following
22  *        disclaimer in the documentation and/or other materials
23  *        provided with the distribution.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32  * SOFTWARE.
33  *
34  */
35 
36 /*
37  * Abstract:
38  *    Implementation of osm_lid_mgr_t.
39  * This file implements the LID Manager object which is responsible for
40  * assigning LIDs to all ports on the subnet.
41  *
42  * DATA STRUCTURES:
43  *  p_subn->port_lid_tbl : a vector pointing from lid to its port.
44  *  osm db guid2lid domain : a hash from guid to lid (min lid).
45  *  p_subn->port_guid_tbl : a map from guid to discovered port obj.
46  *
47  * ALGORITHM:
48  *
49  * 0. we define a function to obtain the correct port lid:
50  *    lid_mgr_get_port_lid( p_mgr, port, &min_lid, &max_lid ):
51  *    0.1 if the port info lid matches the guid2lid return 0
52  *    0.2 if the port info has a lid and that range is empty in
53  *        port_lid_tbl, return 0 and update the port_lid_tbl and
54  *        guid2lid
55  *    0.3 else find an empty space in port_lid_tbl, update the
56  *    port_lid_tbl and guid2lid, return 1 to flag a change required.
57  *
58  * 1. During initialization:
59  *   1.1 initialize the guid2lid database domain.
60  *   1.2 if reassign_lid is not set:
61  *   1.2.1 read the persistent data for the domain.
62  *   1.2.2 validate no duplicate use of lids and lids are 2^(lmc-1)
63  *
64  * 2. During SM port lid assignment:
65  *   2.1 if reassign_lids is set, make it 2^lmc
66  *   2.2 cleanup all port_lid_tbl and re-fill it according to guid2lid
67  *   2.3 call lid_mgr_get_port_lid for the SM port
68  *   2.4 set the port info
69  *
70  * 3. During all other ports lid assignment:
71  *   3.1 go through all ports in the subnet
72  *   3.1.1 call lid_mgr_get_port_lid
73  *   3.1.2 if a change required send the port info
74  *   3.2 if any change send the signal PENDING...
75  *
76  * 4. Store the guid2lid
77  */
78 
79 #if HAVE_CONFIG_H
80 #  include <config.h>
81 #endif				/* HAVE_CONFIG_H */
82 
83 #include <stdlib.h>
84 #include <string.h>
85 #include <iba/ib_types.h>
86 #include <complib/cl_qmap.h>
87 #include <complib/cl_debug.h>
88 #include <opensm/osm_file_ids.h>
89 #define FILE_ID OSM_FILE_LID_MGR_C
90 #include <opensm/osm_lid_mgr.h>
91 #include <opensm/osm_sm.h>
92 #include <opensm/osm_log.h>
93 #include <opensm/osm_node.h>
94 #include <opensm/osm_switch.h>
95 #include <opensm/osm_helper.h>
96 #include <opensm/osm_msgdef.h>
97 #include <vendor/osm_vendor_api.h>
98 #include <opensm/osm_db_pack.h>
99 
100 /**********************************************************************
101   lid range item of qlist
102  **********************************************************************/
103 typedef struct osm_lid_mgr_range {
104 	cl_list_item_t item;
105 	uint16_t min_lid;
106 	uint16_t max_lid;
107 } osm_lid_mgr_range_t;
108 
osm_lid_mgr_construct(IN osm_lid_mgr_t * p_mgr)109 void osm_lid_mgr_construct(IN osm_lid_mgr_t * p_mgr)
110 {
111 	memset(p_mgr, 0, sizeof(*p_mgr));
112 }
113 
osm_lid_mgr_destroy(IN osm_lid_mgr_t * p_mgr)114 void osm_lid_mgr_destroy(IN osm_lid_mgr_t * p_mgr)
115 {
116 	cl_list_item_t *p_item;
117 
118 	OSM_LOG_ENTER(p_mgr->p_log);
119 
120 	while ((p_item = cl_qlist_remove_head(&p_mgr->free_ranges)) !=
121 	       cl_qlist_end(&p_mgr->free_ranges))
122 		free((osm_lid_mgr_range_t *) p_item);
123 	OSM_LOG_EXIT(p_mgr->p_log);
124 }
125 
126 /**********************************************************************
127 Validate the guid to lid data by making sure that under the current
128 LMC we did not get duplicates. If we do flag them as errors and remove
129 the entry.
130 **********************************************************************/
lid_mgr_validate_db(IN osm_lid_mgr_t * p_mgr)131 static void lid_mgr_validate_db(IN osm_lid_mgr_t * p_mgr)
132 {
133 	cl_qlist_t guids;
134 	osm_db_guid_elem_t *p_item;
135 	uint16_t lid;
136 	uint16_t min_lid;
137 	uint16_t max_lid;
138 	uint16_t lmc_mask;
139 	boolean_t lids_ok;
140 	uint8_t lmc_num_lids = (uint8_t) (1 << p_mgr->p_subn->opt.lmc);
141 
142 	OSM_LOG_ENTER(p_mgr->p_log);
143 
144 	lmc_mask = ~(lmc_num_lids - 1);
145 
146 	cl_qlist_init(&guids);
147 
148 	if (osm_db_guid2lid_guids(p_mgr->p_g2l, &guids)) {
149 		OSM_LOG(p_mgr->p_log, OSM_LOG_ERROR, "ERR 0310: "
150 			"could not get guid list\n");
151 		goto Exit;
152 	}
153 
154 	while ((p_item = (osm_db_guid_elem_t *) cl_qlist_remove_head(&guids))
155 	       != (osm_db_guid_elem_t *) cl_qlist_end(&guids)) {
156 		if (osm_db_guid2lid_get(p_mgr->p_g2l, p_item->guid,
157 					&min_lid, &max_lid))
158 			OSM_LOG(p_mgr->p_log, OSM_LOG_ERROR, "ERR 0311: "
159 				"could not get lid for guid:0x%016" PRIx64 "\n",
160 				p_item->guid);
161 		else {
162 			lids_ok = TRUE;
163 
164 			if (min_lid > max_lid || min_lid == 0
165 			    || p_item->guid == 0
166 			    || max_lid > p_mgr->p_subn->max_ucast_lid_ho) {
167 				OSM_LOG(p_mgr->p_log, OSM_LOG_ERROR,
168 					"ERR 0312: "
169 					"Illegal LID range [%u:%u] for "
170 					"guid:0x%016" PRIx64 "\n", min_lid,
171 					max_lid, p_item->guid);
172 				lids_ok = FALSE;
173 			} else if (min_lid != max_lid
174 				   && (min_lid & lmc_mask) != min_lid) {
175 				/* check that if the lids define a range that is
176 				   valid for the current LMC mask */
177 				OSM_LOG(p_mgr->p_log, OSM_LOG_ERROR,
178 					"ERR 0313: "
179 					"LID range [%u:%u] for guid:0x%016"
180 					PRIx64
181 					" is not aligned according to mask:0x%04x\n",
182 					min_lid, max_lid, p_item->guid,
183 					lmc_mask);
184 				lids_ok = FALSE;
185 			} else {
186 				/* check if the lids were not previously assigned */
187 				for (lid = min_lid; lid <= max_lid; lid++) {
188 					if (p_mgr->used_lids[lid]) {
189 						OSM_LOG(p_mgr->p_log,
190 							OSM_LOG_ERROR,
191 							"ERR 0314: "
192 							"0x%04x for guid:0x%016"
193 							PRIx64
194 							" was previously used\n",
195 							lid, p_item->guid);
196 						lids_ok = FALSE;
197 					}
198 				}
199 			}
200 
201 			if (lids_ok)
202 				/* mark that it was visited */
203 				for (lid = min_lid; lid <= max_lid; lid++) {
204 					if (lid < min_lid + lmc_num_lids)
205 						p_mgr->used_lids[lid] = 1;
206 				}
207 			else if (osm_db_guid2lid_delete(p_mgr->p_g2l,
208 							p_item->guid))
209 				OSM_LOG(p_mgr->p_log, OSM_LOG_ERROR,
210 					"ERR 0315: failed to delete entry for "
211 					"guid:0x%016" PRIx64 "\n",
212 					p_item->guid);
213 		}		/* got a lid */
214 		free(p_item);
215 	}			/* all guids */
216 Exit:
217 	OSM_LOG_EXIT(p_mgr->p_log);
218 }
219 
osm_lid_mgr_init(IN osm_lid_mgr_t * p_mgr,IN osm_sm_t * sm)220 ib_api_status_t osm_lid_mgr_init(IN osm_lid_mgr_t * p_mgr, IN osm_sm_t * sm)
221 {
222 	ib_api_status_t status = IB_SUCCESS;
223 
224 	OSM_LOG_ENTER(sm->p_log);
225 
226 	osm_lid_mgr_construct(p_mgr);
227 
228 	p_mgr->sm = sm;
229 	p_mgr->p_log = sm->p_log;
230 	p_mgr->p_subn = sm->p_subn;
231 	p_mgr->p_db = sm->p_db;
232 	p_mgr->p_lock = sm->p_lock;
233 
234 	/* we initialize and restore the db domain of guid to lid map */
235 	p_mgr->p_g2l = osm_db_domain_init(p_mgr->p_db, "guid2lid");
236 	if (!p_mgr->p_g2l) {
237 		OSM_LOG(p_mgr->p_log, OSM_LOG_ERROR, "ERR 0316: "
238 			"Error initializing Guid-to-Lid persistent database\n");
239 		status = IB_ERROR;
240 		goto Exit;
241 	}
242 
243 	cl_qlist_init(&p_mgr->free_ranges);
244 
245 	/* we use the stored guid to lid table if not forced to reassign */
246 	if (!p_mgr->p_subn->opt.reassign_lids) {
247 		if (osm_db_restore(p_mgr->p_g2l)) {
248 #ifndef __WIN__
249 			/*
250 			 * When Windows is BSODing, it might corrupt files that
251 			 * were previously opened for writing, even if the files
252 			 * are closed, so we might see corrupted guid2lid file.
253 			 */
254 			if (p_mgr->p_subn->opt.exit_on_fatal) {
255 				osm_log_v2(p_mgr->p_log, OSM_LOG_SYS, FILE_ID,
256 					   "FATAL: Error restoring Guid-to-Lid "
257 					   "persistent database\n");
258 				status = IB_ERROR;
259 				goto Exit;
260 			} else
261 #endif
262 				OSM_LOG(p_mgr->p_log, OSM_LOG_ERROR,
263 					"ERR 0317: Error restoring Guid-to-Lid "
264 					"persistent database\n");
265 		}
266 
267 		/* we need to make sure we did not get duplicates with
268 		   current lmc */
269 		lid_mgr_validate_db(p_mgr);
270 	}
271 
272 Exit:
273 	OSM_LOG_EXIT(p_mgr->p_log);
274 	return status;
275 }
276 
trim_lid(IN uint16_t lid)277 static uint16_t trim_lid(IN uint16_t lid)
278 {
279 	if (lid > IB_LID_UCAST_END_HO || lid < IB_LID_UCAST_START_HO)
280 		return 0;
281 	return lid;
282 }
283 
284 /**********************************************************************
285  initialize the manager for a new sweep:
286  scans the known persistent assignment and port_lid_tbl
287  re-calculate all empty ranges.
288  cleanup invalid port_lid_tbl entries
289 **********************************************************************/
lid_mgr_init_sweep(IN osm_lid_mgr_t * p_mgr)290 static int lid_mgr_init_sweep(IN osm_lid_mgr_t * p_mgr)
291 {
292 	cl_ptr_vector_t *p_discovered_vec = &p_mgr->p_subn->port_lid_tbl;
293 	uint16_t max_defined_lid, max_persistent_lid, max_discovered_lid;
294 	uint16_t disc_min_lid, disc_max_lid, db_min_lid, db_max_lid;
295 	int status = 0;
296 	cl_list_item_t *p_item;
297 	boolean_t is_free;
298 	osm_lid_mgr_range_t *p_range = NULL;
299 	osm_port_t *p_port;
300 	cl_qmap_t *p_port_guid_tbl;
301 	uint8_t lmc_num_lids = (uint8_t) (1 << p_mgr->p_subn->opt.lmc);
302 	uint16_t lmc_mask, req_lid, num_lids, lid;
303 
304 	OSM_LOG_ENTER(p_mgr->p_log);
305 
306 	lmc_mask = ~((1 << p_mgr->p_subn->opt.lmc) - 1);
307 
308 	/* We must discard previous guid2lid db if this is the first master
309 	 * sweep and reassign_lids option is TRUE.
310 	 * If we came out of standby and honor_guid2lid_file option is TRUE, we
311 	 * must restore guid2lid db. Otherwise if honor_guid2lid_file option is
312 	 * FALSE we must discard previous guid2lid db.
313 	 */
314 	if (p_mgr->p_subn->first_time_master_sweep == TRUE &&
315 	    p_mgr->p_subn->opt.reassign_lids == TRUE) {
316 		osm_db_clear(p_mgr->p_g2l);
317 		memset(p_mgr->used_lids, 0, sizeof(p_mgr->used_lids));
318 	} else if (p_mgr->p_subn->coming_out_of_standby == TRUE) {
319 		osm_db_clear(p_mgr->p_g2l);
320 		memset(p_mgr->used_lids, 0, sizeof(p_mgr->used_lids));
321 		if (p_mgr->p_subn->opt.honor_guid2lid_file == FALSE)
322 			OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
323 				"Ignore guid2lid file when coming out of standby\n");
324 		else {
325 			OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
326 				"Honor current guid2lid file when coming out "
327 				"of standby\n");
328 			if (osm_db_restore(p_mgr->p_g2l))
329 				OSM_LOG(p_mgr->p_log, OSM_LOG_ERROR,
330 					"ERR 0306: "
331 					"Error restoring Guid-to-Lid "
332 					"persistent database. Ignoring it\n");
333 			lid_mgr_validate_db(p_mgr);
334 		}
335 	}
336 
337 	/* we need to cleanup the empty ranges list */
338 	while ((p_item = cl_qlist_remove_head(&p_mgr->free_ranges)) !=
339 	       cl_qlist_end(&p_mgr->free_ranges))
340 		free((osm_lid_mgr_range_t *) p_item);
341 
342 	/* first clean up the port_by_lid_tbl */
343 	for (lid = 0; lid < cl_ptr_vector_get_size(p_discovered_vec); lid++)
344 		cl_ptr_vector_set(p_discovered_vec, lid, NULL);
345 
346 	/* we if are in the first sweep and in reassign lids mode
347 	   we should ignore all the available info and simply define one
348 	   huge empty range */
349 	if (p_mgr->p_subn->first_time_master_sweep == TRUE &&
350 	    p_mgr->p_subn->opt.reassign_lids == TRUE) {
351 		OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
352 			"Skipping all lids as we are reassigning them\n");
353 		p_range = malloc(sizeof(osm_lid_mgr_range_t));
354 		if (p_range)
355 			p_range->min_lid = 1;
356 		goto AfterScanningLids;
357 	}
358 
359 	/* go over all discovered ports and mark their entries */
360 	p_port_guid_tbl = &p_mgr->p_subn->port_guid_tbl;
361 
362 	for (p_port = (osm_port_t *) cl_qmap_head(p_port_guid_tbl);
363 	     p_port != (osm_port_t *) cl_qmap_end(p_port_guid_tbl);
364 	     p_port = (osm_port_t *) cl_qmap_next(&p_port->map_item)) {
365 		osm_port_get_lid_range_ho(p_port, &disc_min_lid, &disc_max_lid);
366 		disc_min_lid = trim_lid(disc_min_lid);
367 		disc_max_lid = trim_lid(disc_max_lid);
368 		for (lid = disc_min_lid; lid <= disc_max_lid; lid++) {
369 			if (lid < disc_min_lid + lmc_num_lids)
370 				cl_ptr_vector_set(p_discovered_vec, lid, p_port);
371 			else
372 				cl_ptr_vector_set(p_discovered_vec, lid, NULL);
373 		}
374 		/* make sure the guid2lid entry is valid. If not, clean it. */
375 		if (osm_db_guid2lid_get(p_mgr->p_g2l,
376 					cl_ntoh64(osm_port_get_guid(p_port)),
377 					&db_min_lid, &db_max_lid))
378 			continue;
379 
380 		if (!p_port->p_node->sw ||
381 		    osm_switch_sp0_is_lmc_capable(p_port->p_node->sw,
382 						  p_mgr->p_subn))
383 			num_lids = lmc_num_lids;
384 		else
385 			num_lids = 1;
386 
387 		if (num_lids != 1 &&
388 		    ((db_min_lid & lmc_mask) != db_min_lid ||
389 		     db_max_lid - db_min_lid + 1 < num_lids)) {
390 			/* Not aligned, or not wide enough, then remove the entry */
391 			OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
392 				"Cleaning persistent entry for guid:"
393 				"0x%016" PRIx64 " illegal range:[0x%x:0x%x]\n",
394 				cl_ntoh64(osm_port_get_guid(p_port)),
395 				db_min_lid, db_max_lid);
396 			osm_db_guid2lid_delete(p_mgr->p_g2l,
397 					       cl_ntoh64
398 					       (osm_port_get_guid(p_port)));
399 			for (lid = db_min_lid; lid <= db_max_lid; lid++)
400 				p_mgr->used_lids[lid] = 0;
401 		}
402 	}
403 
404 	/*
405 	   Our task is to find free lid ranges.
406 	   A lid can be used if
407 	   1. a persistent assignment exists
408 	   2. the lid is used by a discovered port that does not have a
409 	   persistent assignment.
410 
411 	   scan through all lid values of both the persistent table and
412 	   discovered table.
413 	   If the lid has an assigned port in the discovered table:
414 	   * make sure the lid matches the persistent table, or
415 	   * there is no other persistent assignment for that lid.
416 	   * else cleanup the port_by_lid_tbl, mark this as empty range.
417 	   Else if the lid does not have an entry in the persistent table
418 	   mark it as free.
419 	 */
420 
421 	/* find the range of lids to scan */
422 	max_discovered_lid =
423 	    (uint16_t) cl_ptr_vector_get_size(p_discovered_vec);
424 	max_persistent_lid = sizeof(p_mgr->used_lids) - 1;
425 
426 	/* but the vectors have one extra entry for lid=0 */
427 	if (max_discovered_lid)
428 		max_discovered_lid--;
429 
430 	if (max_persistent_lid > max_discovered_lid)
431 		max_defined_lid = max_persistent_lid;
432 	else
433 		max_defined_lid = max_discovered_lid;
434 
435 	for (lid = 1; lid <= max_defined_lid; lid++) {
436 		is_free = TRUE;
437 		/* first check to see if the lid is used by a persistent assignment */
438 		if (lid <= max_persistent_lid && p_mgr->used_lids[lid]) {
439 			OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
440 				"0x%04x is not free as its mapped by the "
441 				"persistent db\n", lid);
442 			is_free = FALSE;
443 			/* check this is a discovered port */
444 		} else if (lid <= max_discovered_lid &&
445 			   (p_port = cl_ptr_vector_get(p_discovered_vec,
446 						       lid))) {
447 			/* we have a port. Now lets see if we can preserve its lid range. */
448 			/* For that, we need to make sure:
449 			   1. The port has a (legal) persistency entry. Then the
450 			   local lid is free (we will use the persistency value).
451 			   2. Can the port keep its local assignment?
452 			   a. Make sure the lid is aligned.
453 			   b. Make sure all needed lids (for the lmc) are free
454 			   according to persistency table.
455 			 */
456 			/* qualify the guid of the port is not persistently
457 			   mapped to another range */
458 			if (!osm_db_guid2lid_get(p_mgr->p_g2l,
459 						 cl_ntoh64
460 						 (osm_port_get_guid(p_port)),
461 						 &db_min_lid, &db_max_lid)) {
462 				OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
463 					"0x%04x is free as it was "
464 					"discovered but mapped by the "
465 					"persistent db to [0x%04x:0x%04x]\n",
466 					lid, db_min_lid, db_max_lid);
467 			} else {
468 				/* can the port keep its assignment ? */
469 				/* get the lid range of that port, and the
470 				   required number of lids we are about to
471 				   assign to it */
472 				osm_port_get_lid_range_ho(p_port,
473 							  &disc_min_lid,
474 							  &disc_max_lid);
475 				if (!p_port->p_node->sw ||
476 				    osm_switch_sp0_is_lmc_capable
477 				    (p_port->p_node->sw, p_mgr->p_subn)) {
478 					disc_max_lid =
479 					    disc_min_lid + lmc_num_lids - 1;
480 					num_lids = lmc_num_lids;
481 				} else
482 					num_lids = 1;
483 
484 				/* Make sure the lid is aligned */
485 				if (num_lids != 1
486 				    && (disc_min_lid & lmc_mask) !=
487 				    disc_min_lid) {
488 					/* The lid cannot be used */
489 					OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
490 						"0x%04x is free as it was "
491 						"discovered but not aligned\n",
492 						lid);
493 				} else {
494 					/* check that all needed lids are not persistently mapped */
495 					is_free = FALSE;
496 					for (req_lid = disc_min_lid + 1;
497 					     req_lid <= disc_max_lid;
498 					     req_lid++) {
499 						if (req_lid <=
500 						    max_persistent_lid &&
501 						    p_mgr->used_lids[req_lid]) {
502 							OSM_LOG(p_mgr->p_log,
503 								OSM_LOG_DEBUG,
504 								"0x%04x is free as it was discovered "
505 								"but mapped\n",
506 								lid);
507 							is_free = TRUE;
508 							break;
509 						}
510 					}
511 
512 					if (is_free == FALSE) {
513 						/* This port will use its local lid, and consume the entire required lid range.
514 						   Thus we can skip that range. */
515 						/* If the disc_max_lid is greater then lid, we can skip right to it,
516 						   since we've done all neccessary checks on the lids in between. */
517 						if (disc_max_lid > lid)
518 							lid = disc_max_lid;
519 					}
520 				}
521 			}
522 		}
523 
524 		if (is_free) {
525 			if (p_range)
526 				p_range->max_lid = lid;
527 			else {
528 				p_range = malloc(sizeof(osm_lid_mgr_range_t));
529 				if (p_range) {
530 					p_range->min_lid = lid;
531 					p_range->max_lid = lid;
532 				}
533 			}
534 		/* this lid is used so we need to finalize the previous free range */
535 		} else if (p_range) {
536 			cl_qlist_insert_tail(&p_mgr->free_ranges,
537 					     &p_range->item);
538 			OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
539 				"new free lid range [%u:%u]\n",
540 				p_range->min_lid, p_range->max_lid);
541 			p_range = NULL;
542 		}
543 	}
544 
545 AfterScanningLids:
546 	/* after scanning all known lids we need to extend the last range
547 	   to the max allowed lid */
548 	if (!p_range) {
549 		p_range = malloc(sizeof(osm_lid_mgr_range_t));
550 		/*
551 		   The p_range can be NULL in one of 2 cases:
552 		   1. If max_defined_lid == 0. In this case, we want the
553 		   entire range.
554 		   2. If all lids discovered in the loop where mapped. In this
555 		   case, no free range exists and we want to define it after the
556 		   last mapped lid.
557 		 */
558 		if (p_range)
559 			p_range->min_lid = lid;
560 	}
561 	if (p_range) {
562 		p_range->max_lid = p_mgr->p_subn->max_ucast_lid_ho;
563 		cl_qlist_insert_tail(&p_mgr->free_ranges, &p_range->item);
564 		OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
565 			"final free lid range [%u:%u]\n",
566 			p_range->min_lid, p_range->max_lid);
567 	}
568 
569 	OSM_LOG_EXIT(p_mgr->p_log);
570 	return status;
571 }
572 
573 /**********************************************************************
574  check if the given range of lids is free
575 **********************************************************************/
lid_mgr_is_range_not_persistent(IN osm_lid_mgr_t * p_mgr,IN uint16_t lid,IN uint16_t num_lids)576 static boolean_t lid_mgr_is_range_not_persistent(IN osm_lid_mgr_t * p_mgr,
577 						 IN uint16_t lid,
578 						 IN uint16_t num_lids)
579 {
580 	uint16_t i;
581 
582 	for (i = lid; i < lid + num_lids; i++)
583 		if (p_mgr->used_lids[i])
584 			return FALSE;
585 
586 	return TRUE;
587 }
588 
589 /**********************************************************************
590 find a free lid range
591 **********************************************************************/
lid_mgr_find_free_lid_range(IN osm_lid_mgr_t * p_mgr,IN uint8_t num_lids,OUT uint16_t * p_min_lid,OUT uint16_t * p_max_lid)592 static void lid_mgr_find_free_lid_range(IN osm_lid_mgr_t * p_mgr,
593 					IN uint8_t num_lids,
594 					OUT uint16_t * p_min_lid,
595 					OUT uint16_t * p_max_lid)
596 {
597 	uint16_t lid;
598 	cl_list_item_t *p_item;
599 	cl_list_item_t *p_next_item;
600 	osm_lid_mgr_range_t *p_range = NULL;
601 	uint8_t lmc_num_lids;
602 	uint16_t lmc_mask;
603 
604 	OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG, "LMC = %u, number LIDs = %u\n",
605 		p_mgr->p_subn->opt.lmc, num_lids);
606 
607 	lmc_num_lids = (1 << p_mgr->p_subn->opt.lmc);
608 	lmc_mask = ~((1 << p_mgr->p_subn->opt.lmc) - 1);
609 
610 	/*
611 	   Search the list of free lid ranges for a range which is big enough
612 	 */
613 	p_item = cl_qlist_head(&p_mgr->free_ranges);
614 	while (p_item != cl_qlist_end(&p_mgr->free_ranges)) {
615 		p_next_item = cl_qlist_next(p_item);
616 		p_range = (osm_lid_mgr_range_t *) p_item;
617 
618 		lid = p_range->min_lid;
619 
620 		/* if we require more then one lid we must align to LMC */
621 		if (num_lids > 1) {
622 			if ((lid & lmc_mask) != lid)
623 				lid = (lid + lmc_num_lids) & lmc_mask;
624 		}
625 
626 		/* but we can be out of the range */
627 		if (lid + num_lids - 1 <= p_range->max_lid) {
628 			/* ok let us use that range */
629 			if (lid + num_lids - 1 == p_range->max_lid) {
630 				/* we consumed the entire range */
631 				cl_qlist_remove_item(&p_mgr->free_ranges,
632 						     p_item);
633 				free(p_item);
634 			} else
635 				/* only update the available range */
636 				p_range->min_lid = lid + num_lids;
637 
638 			*p_min_lid = lid;
639 			*p_max_lid = (uint16_t) (lid + num_lids - 1);
640 			return;
641 		}
642 		p_item = p_next_item;
643 	}
644 
645 	/*
646 	   Couldn't find a free range of lids.
647 	 */
648 	*p_min_lid = *p_max_lid = 0;
649 	/* if we run out of lids, give an error and abort! */
650 	OSM_LOG(p_mgr->p_log, OSM_LOG_ERROR, "ERR 0307: "
651 		"OPENSM RAN OUT OF LIDS!!!\n");
652 	CL_ASSERT(0);
653 }
654 
lid_mgr_cleanup_discovered_port_lid_range(IN osm_lid_mgr_t * p_mgr,IN osm_port_t * p_port)655 static void lid_mgr_cleanup_discovered_port_lid_range(IN osm_lid_mgr_t * p_mgr,
656 						      IN osm_port_t * p_port)
657 {
658 	cl_ptr_vector_t *p_discovered_vec = &p_mgr->p_subn->port_lid_tbl;
659 	uint16_t lid, min_lid, max_lid;
660 	uint16_t max_tbl_lid =
661 	    (uint16_t) (cl_ptr_vector_get_size(p_discovered_vec));
662 
663 	osm_port_get_lid_range_ho(p_port, &min_lid, &max_lid);
664 	min_lid = trim_lid(min_lid);
665 	max_lid = trim_lid(max_lid);
666 	for (lid = min_lid; lid <= max_lid; lid++)
667 		if (lid < max_tbl_lid &&
668 		    p_port == cl_ptr_vector_get(p_discovered_vec, lid))
669 			cl_ptr_vector_set(p_discovered_vec, lid, NULL);
670 }
671 
672 /**********************************************************************
673  0.1 if the port info lid matches the guid2lid return 0
674  0.2 if the port info has a lid and that range is empty in
675      port_lid_tbl, return 0 and update the port_lid_tbl and
676      guid2lid
677  0.3 else find an empty space in port_lid_tbl, update the
678  port_lid_tbl and guid2lid, return 1 to flag a change required.
679 **********************************************************************/
lid_mgr_get_port_lid(IN osm_lid_mgr_t * p_mgr,IN osm_port_t * p_port,OUT uint16_t * p_min_lid,OUT uint16_t * p_max_lid)680 static int lid_mgr_get_port_lid(IN osm_lid_mgr_t * p_mgr,
681 				IN osm_port_t * p_port,
682 				OUT uint16_t * p_min_lid,
683 				OUT uint16_t * p_max_lid)
684 {
685 	uint16_t lid, min_lid, max_lid;
686 	uint64_t guid;
687 	uint8_t num_lids = (1 << p_mgr->p_subn->opt.lmc);
688 	int lid_changed = 0;
689 	uint16_t lmc_mask;
690 
691 	OSM_LOG_ENTER(p_mgr->p_log);
692 
693 	/* get the lid from the guid2lid */
694 	guid = cl_ntoh64(osm_port_get_guid(p_port));
695 
696 	/* if the port is a base switch port 0 then we only need one lid */
697 	if (p_port->p_node->sw &&
698 	    !osm_switch_sp0_is_lmc_capable(p_port->p_node->sw, p_mgr->p_subn))
699 		num_lids = 1;
700 
701 	if (p_mgr->p_subn->first_time_master_sweep == TRUE &&
702 	    p_mgr->p_subn->opt.reassign_lids == TRUE)
703 		goto AssignLid;
704 
705 	lmc_mask = ~(num_lids - 1);
706 
707 	/* if the port matches the guid2lid */
708 	if (!osm_db_guid2lid_get(p_mgr->p_g2l, guid, &min_lid, &max_lid)) {
709 		*p_min_lid = min_lid;
710 		*p_max_lid = min_lid + num_lids - 1;
711 		if (min_lid == cl_ntoh16(osm_port_get_base_lid(p_port)))
712 			OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG, "0x%016" PRIx64
713 				" matches its known lid:%u\n", guid, min_lid);
714 		else {
715 			OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
716 				"0x%016" PRIx64 " with lid:%u "
717 				"does not match its known lid:%u\n",
718 				guid, cl_ntoh16(osm_port_get_base_lid(p_port)),
719 				min_lid);
720 			lid_mgr_cleanup_discovered_port_lid_range(p_mgr,
721 								  p_port);
722 			/* we still need to send the setting to the target port */
723 			lid_changed = 1;
724 		}
725 		goto NewLidSet;
726 	} else
727 		OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
728 			"0x%016" PRIx64 " has no persistent lid assigned\n",
729 			guid);
730 
731 	/* if the port info carries a lid it must be lmc aligned and not mapped
732 	   by the persistent storage  */
733 	min_lid = cl_ntoh16(osm_port_get_base_lid(p_port));
734 
735 	/* we want to ignore the discovered lid if we are also on first sweep of
736 	   reassign lids flow */
737 	if (min_lid) {
738 		/* make sure lid is valid */
739 		if ((min_lid & lmc_mask) == min_lid) {
740 			/* is it free */
741 			if (lid_mgr_is_range_not_persistent
742 			    (p_mgr, min_lid, num_lids)) {
743 				*p_min_lid = min_lid;
744 				*p_max_lid = min_lid + num_lids - 1;
745 				OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
746 					"0x%016" PRIx64
747 					" lid range:[%u-%u] is free\n",
748 					guid, *p_min_lid, *p_max_lid);
749 				goto NewLidSet;
750 			} else
751 				OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
752 					"0x%016" PRIx64 " existing lid "
753 					"range:[%u:%u] is not free\n",
754 					guid, min_lid, min_lid + num_lids - 1);
755 		} else
756 			OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
757 				"0x%016" PRIx64 " existing lid range:"
758 				"[%u:%u] is not lmc aligned\n",
759 				guid, min_lid, min_lid + num_lids - 1);
760 	}
761 
762 AssignLid:
763 	/* first cleanup the existing discovered lid range */
764 	lid_mgr_cleanup_discovered_port_lid_range(p_mgr, p_port);
765 
766 	/* find an empty space */
767 	lid_mgr_find_free_lid_range(p_mgr, num_lids, p_min_lid, p_max_lid);
768 	OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
769 		"0x%016" PRIx64 " assigned a new lid range:[%u-%u]\n",
770 		guid, *p_min_lid, *p_max_lid);
771 	lid_changed = 1;
772 
773 NewLidSet:
774 	/* update the guid2lid db and used_lids */
775 	osm_db_guid2lid_set(p_mgr->p_g2l, guid, *p_min_lid, *p_max_lid);
776 	for (lid = *p_min_lid; lid <= *p_max_lid; lid++)
777 		p_mgr->used_lids[lid] = 1;
778 
779 	/* make sure the assigned lids are marked in port_lid_tbl */
780 	for (lid = *p_min_lid; lid <= *p_max_lid; lid++)
781 		cl_ptr_vector_set(&p_mgr->p_subn->port_lid_tbl, lid, p_port);
782 
783 	OSM_LOG_EXIT(p_mgr->p_log);
784 	return lid_changed;
785 }
786 
787 /**********************************************************************
788  Set to INIT the remote port of the given physical port
789  **********************************************************************/
lid_mgr_set_remote_pi_state_to_init(IN osm_lid_mgr_t * p_mgr,IN osm_physp_t * p_physp)790 static void lid_mgr_set_remote_pi_state_to_init(IN osm_lid_mgr_t * p_mgr,
791 						IN osm_physp_t * p_physp)
792 {
793 	osm_physp_t *p_rem_physp = osm_physp_get_remote(p_physp);
794 
795 	if (p_rem_physp == NULL)
796 		return;
797 
798 	/* but in some rare cases the remote side might be non responsive */
799 	ib_port_info_set_port_state(&p_rem_physp->port_info, IB_LINK_INIT);
800 }
801 
lid_mgr_set_physp_pi(IN osm_lid_mgr_t * p_mgr,IN osm_port_t * p_port,IN osm_physp_t * p_physp,IN ib_net16_t lid)802 static int lid_mgr_set_physp_pi(IN osm_lid_mgr_t * p_mgr,
803 				IN osm_port_t * p_port,
804 				IN osm_physp_t * p_physp, IN ib_net16_t lid)
805 {
806 	uint8_t payload[IB_SMP_DATA_SIZE];
807 	ib_port_info_t *p_pi = (ib_port_info_t *) payload;
808 	const ib_port_info_t *p_old_pi;
809 	osm_madw_context_t context;
810 	osm_node_t *p_node;
811 	ib_api_status_t status;
812 	uint8_t mtu;
813 	uint8_t op_vls;
814 	uint8_t port_num;
815 	boolean_t send_set = FALSE;
816 	boolean_t send_client_rereg = FALSE;
817 	boolean_t update_mkey = FALSE;
818 	int ret = 0;
819 
820 	OSM_LOG_ENTER(p_mgr->p_log);
821 
822 	/*
823 	   Don't bother doing anything if this Physical Port is not valid.
824 	   This allows simplified code in the caller.
825 	 */
826 	if (!p_physp)
827 		goto Exit;
828 
829 	port_num = osm_physp_get_port_num(p_physp);
830 	p_node = osm_physp_get_node_ptr(p_physp);
831 
832 	if (osm_node_get_type(p_node) == IB_NODE_TYPE_SWITCH && port_num != 0) {
833 		/*
834 		   Switch ports that are not numbered 0 should not be set
835 		   with the following attributes as they are set later
836 		   (during NO_CHANGE state in link mgr).
837 		 */
838 		OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
839 			"Skipping switch port %u, GUID 0x%016" PRIx64 "\n",
840 			port_num, cl_ntoh64(osm_physp_get_port_guid(p_physp)));
841 		goto Exit;
842 	}
843 
844 	p_old_pi = &p_physp->port_info;
845 
846 	/*
847 	   First, copy existing parameters from the PortInfo attribute we
848 	   already have for this node.
849 
850 	   Second, update with default values that we know must be set for
851 	   every Physical Port and the LID and set the neighbor MTU field
852 	   appropriately.
853 
854 	   Third, send the SMP to this physical port.
855 	 */
856 
857 	memcpy(payload, p_old_pi, sizeof(ib_port_info_t));
858 
859 	/*
860 	   Should never write back a value that is bigger then 3 in
861 	   the PortPhysicalState field, so cannot simply copy!
862 
863 	   Actually we want to write there:
864 	   port physical state - no change
865 	   link down default state = polling
866 	   port state - no change
867 	 */
868 	p_pi->state_info2 = 0x02;
869 	ib_port_info_set_port_state(p_pi, IB_LINK_NO_CHANGE);
870 
871 	if (ib_port_info_get_link_down_def_state(p_pi) !=
872 	    ib_port_info_get_link_down_def_state(p_old_pi))
873 		send_set = TRUE;
874 
875 	/* didn't get PortInfo before */
876 	if (!ib_port_info_get_port_state(p_old_pi))
877 		send_set = TRUE;
878 
879 	p_pi->m_key = p_mgr->p_subn->opt.m_key;
880 	if (memcmp(&p_pi->m_key, &p_old_pi->m_key, sizeof(p_pi->m_key))) {
881 		update_mkey = TRUE;
882 		send_set = TRUE;
883 	}
884 
885 	p_pi->subnet_prefix = p_mgr->p_subn->opt.subnet_prefix;
886 	if (memcmp(&p_pi->subnet_prefix, &p_old_pi->subnet_prefix,
887 		   sizeof(p_pi->subnet_prefix)))
888 		send_set = TRUE;
889 
890 	p_port->lid = lid;
891 	p_pi->base_lid = lid;
892 	if (memcmp(&p_pi->base_lid, &p_old_pi->base_lid,
893 		   sizeof(p_pi->base_lid))) {
894 		/*
895 		 * Reset stored base_lid.
896 		 * On successful send, we'll update it when we'll get a reply.
897 		 */
898 		osm_physp_set_base_lid(p_physp, 0);
899 		send_set = TRUE;
900 		p_mgr->dirty = TRUE;
901 	}
902 
903 	/*
904 	   We are updating the ports with our local sm_base_lid
905 	   if for some reason currently received SM LID is different from our SM LID,
906 	   need to send client reregister to this port
907 	*/
908 	p_pi->master_sm_base_lid = p_mgr->p_subn->sm_base_lid;
909 	if (memcmp(&p_pi->master_sm_base_lid, &p_old_pi->master_sm_base_lid,
910 		   sizeof(p_pi->master_sm_base_lid))) {
911 		send_client_rereg = TRUE;
912 		send_set = TRUE;
913 	}
914 
915 	p_pi->m_key_lease_period = p_mgr->p_subn->opt.m_key_lease_period;
916 	if (memcmp(&p_pi->m_key_lease_period, &p_old_pi->m_key_lease_period,
917 		   sizeof(p_pi->m_key_lease_period)))
918 		send_set = TRUE;
919 
920 	p_pi->mkey_lmc = 0;
921 	ib_port_info_set_mpb(p_pi, p_mgr->p_subn->opt.m_key_protect_bits);
922 	if (ib_port_info_get_mpb(p_pi) != ib_port_info_get_mpb(p_old_pi))
923 		send_set = TRUE;
924 
925 	/*
926 	   we want to set the timeout for both the switch port 0
927 	   and the CA ports
928 	 */
929 	ib_port_info_set_timeout(p_pi, p_mgr->p_subn->opt.subnet_timeout);
930 	if (ib_port_info_get_timeout(p_pi) !=
931 	    ib_port_info_get_timeout(p_old_pi))
932 		send_set = TRUE;
933 
934 	if (port_num != 0) {
935 		/*
936 		   CAs don't have a port 0, and for switch port 0,
937 		   the state bits are ignored.
938 		   This is not the switch management port
939 		 */
940 		p_pi->link_width_enabled = p_old_pi->link_width_supported;
941 		if (p_pi->link_width_enabled != p_old_pi->link_width_enabled)
942 			send_set = TRUE;
943 
944 		/* p_pi->mkey_lmc is initialized earlier */
945 		ib_port_info_set_lmc(p_pi, p_mgr->p_subn->opt.lmc);
946 		if (ib_port_info_get_lmc(p_pi) !=
947 		    ib_port_info_get_lmc(p_old_pi))
948 			send_set = TRUE;
949 
950 		/* calc new op_vls and mtu */
951 		op_vls = osm_physp_calc_link_op_vls(p_mgr->p_log, p_mgr->p_subn,
952 					      p_physp,
953 					      ib_port_info_get_op_vls(p_old_pi));
954 		mtu = osm_physp_calc_link_mtu(p_mgr->p_log, p_physp,
955 					      ib_port_info_get_neighbor_mtu(p_old_pi));
956 
957 		ib_port_info_set_neighbor_mtu(p_pi, mtu);
958 
959 		if (ib_port_info_get_neighbor_mtu(p_pi) !=
960 		    ib_port_info_get_neighbor_mtu(p_old_pi))
961 			send_set = TRUE;
962 
963 		ib_port_info_set_op_vls(p_pi, op_vls);
964 		if (ib_port_info_get_op_vls(p_pi) !=
965 		    ib_port_info_get_op_vls(p_old_pi))
966 			send_set = TRUE;
967 
968 		/*
969 		   Several timeout mechanisms:
970 		 */
971 		ib_port_info_set_phy_and_overrun_err_thd(p_pi,
972 							 p_mgr->p_subn->opt.
973 							 local_phy_errors_threshold,
974 							 p_mgr->p_subn->opt.
975 							 overrun_errors_threshold);
976 
977 		if (p_pi->error_threshold != p_old_pi->error_threshold)
978 			send_set = TRUE;
979 
980 		/*
981 		   To reset the port state machine we can send
982 		   PortInfo.State = DOWN. (see: 7.2.7 p171 lines:10-19)
983 		 */
984 		if (mtu != ib_port_info_get_neighbor_mtu(p_old_pi) ||
985 		    op_vls != ib_port_info_get_op_vls(p_old_pi)) {
986 			OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
987 				"Sending Link Down to GUID 0x%016"
988 				PRIx64 " port %d due to op_vls or "
989 				"mtu change. MTU:%u,%u VL_CAP:%u,%u\n",
990 				cl_ntoh64(osm_physp_get_port_guid(p_physp)),
991 				port_num, mtu,
992 				ib_port_info_get_neighbor_mtu(p_old_pi),
993 				op_vls, ib_port_info_get_op_vls(p_old_pi));
994 
995 			/*
996 			   we need to make sure the internal DB will follow the
997 			   fact that the remote port is also going through
998 			   "down" state into "init"...
999 			 */
1000 			lid_mgr_set_remote_pi_state_to_init(p_mgr, p_physp);
1001 
1002 			ib_port_info_set_port_state(p_pi, IB_LINK_DOWN);
1003 			if (ib_port_info_get_port_state(p_pi) !=
1004 			    ib_port_info_get_port_state(p_old_pi))
1005 				send_set = TRUE;
1006 		}
1007 	} else if (ib_switch_info_is_enhanced_port0(&p_node->sw->switch_info)) {
1008 		/*
1009 		 * Configure Enh. SP0:
1010 		 * Set MTU according to the mtu_cap.
1011 		 * Set LMC if lmc_esp0 is defined.
1012 		 */
1013 		ib_port_info_set_neighbor_mtu(p_pi,
1014 					      ib_port_info_get_mtu_cap
1015 					      (p_old_pi));
1016 		if (ib_port_info_get_neighbor_mtu(p_pi) !=
1017 		    ib_port_info_get_neighbor_mtu(p_old_pi))
1018 			send_set = TRUE;
1019 
1020 		OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
1021 			"Updating neighbor_mtu on switch GUID 0x%016" PRIx64
1022 			" port 0 to:%u\n",
1023 			cl_ntoh64(osm_physp_get_port_guid(p_physp)),
1024 			ib_port_info_get_neighbor_mtu(p_pi));
1025 
1026 		/* Configure LMC on enhanced SP0 */
1027 		if (p_mgr->p_subn->opt.lmc_esp0) {
1028 			/* p_pi->mkey_lmc is initialized earlier */
1029 			ib_port_info_set_lmc(p_pi, p_mgr->p_subn->opt.lmc);
1030 			if (ib_port_info_get_lmc(p_pi) !=
1031 			    ib_port_info_get_lmc(p_old_pi))
1032 				send_set = TRUE;
1033 		}
1034 	}
1035 
1036 	context.pi_context.node_guid = osm_node_get_node_guid(p_node);
1037 	context.pi_context.port_guid = osm_physp_get_port_guid(p_physp);
1038 	context.pi_context.set_method = TRUE;
1039 	context.pi_context.light_sweep = FALSE;
1040 	context.pi_context.active_transition = FALSE;
1041 
1042 	/*
1043 	  For ports supporting the ClientReregistration Vol1 (v1.2) p811 14.4.11:
1044 	  need to set the cli_rereg bit when current SM LID at the Host
1045 	  is different from our SM LID,
1046 	  also if we are in first_time_master_sweep,
1047 	  also if this port was just now discovered, then we should also set
1048 	  the cli_rereg bit (we know that the port was just discovered
1049 	  if its is_new field is set).
1050 	*/
1051 	if  ((send_client_rereg ||
1052 	    p_mgr->p_subn->first_time_master_sweep == TRUE || p_port->is_new)
1053 	    && !p_mgr->p_subn->opt.no_clients_rereg
1054 	    && (p_old_pi->capability_mask & IB_PORT_CAP_HAS_CLIENT_REREG)) {
1055 		OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
1056 			"Setting client rereg on %s, port %d\n",
1057 			p_port->p_node->print_desc, p_port->p_physp->port_num);
1058 		ib_port_info_set_client_rereg(p_pi, 1);
1059 		context.pi_context.client_rereg = TRUE;
1060 		send_set = TRUE;
1061 	} else {
1062 		ib_port_info_set_client_rereg(p_pi, 0);
1063 		context.pi_context.client_rereg = FALSE;
1064 	}
1065 
1066 	/* We need to send the PortInfo Set request with the new sm_lid
1067 	   in the following cases:
1068 	   1. There is a change in the values (send_set == TRUE)
1069 	   2. first_time_master_sweep flag on the subnet is TRUE. This means the
1070 	   SM just became master, and it then needs to send a PortInfo Set to
1071 	   every port.
1072 	 */
1073 	if (p_mgr->p_subn->first_time_master_sweep == TRUE)
1074 		send_set = TRUE;
1075 
1076 	if (!send_set)
1077 		goto Exit;
1078 
1079 	status = osm_req_set(p_mgr->sm, osm_physp_get_dr_path_ptr(p_physp),
1080 			     payload, sizeof(payload), IB_MAD_ATTR_PORT_INFO,
1081 			     cl_hton32(osm_physp_get_port_num(p_physp)),
1082 			     FALSE, ib_port_info_get_m_key(&p_physp->port_info),
1083 			     CL_DISP_MSGID_NONE, &context);
1084 	if (status != IB_SUCCESS)
1085 		ret = -1;
1086 	/* If we sent a new mkey above, update our guid2mkey map
1087 	   now, on the assumption that the SubnSet succeeds
1088 	*/
1089 	if (update_mkey)
1090 		osm_db_guid2mkey_set(p_mgr->p_subn->p_g2m,
1091 				     cl_ntoh64(p_physp->port_guid),
1092 				     cl_ntoh64(p_pi->m_key));
1093 
1094 Exit:
1095 	OSM_LOG_EXIT(p_mgr->p_log);
1096 	return ret;
1097 }
1098 
1099 /**********************************************************************
1100  Processes our own node
1101  Lock must already be held.
1102 **********************************************************************/
lid_mgr_process_our_sm_node(IN osm_lid_mgr_t * p_mgr)1103 static int lid_mgr_process_our_sm_node(IN osm_lid_mgr_t * p_mgr)
1104 {
1105 	osm_port_t *p_port;
1106 	uint16_t min_lid_ho;
1107 	uint16_t max_lid_ho;
1108 	int ret;
1109 
1110 	OSM_LOG_ENTER(p_mgr->p_log);
1111 
1112 	/*
1113 	   Acquire our own port object.
1114 	 */
1115 	p_port = osm_get_port_by_guid(p_mgr->p_subn,
1116 				      p_mgr->p_subn->sm_port_guid);
1117 	if (!p_port) {
1118 		OSM_LOG(p_mgr->p_log, OSM_LOG_ERROR, "ERR 0308: "
1119 			"Can't acquire SM's port object, GUID 0x%016" PRIx64
1120 			"\n", cl_ntoh64(p_mgr->p_subn->sm_port_guid));
1121 		ret = -1;
1122 		goto Exit;
1123 	}
1124 
1125 	/*
1126 	   Determine the LID this SM will use for its own port.
1127 	   Be careful.  With an LMC > 0, the bottom of the LID range becomes
1128 	   unusable, since port hardware will mask off least significant bits,
1129 	   leaving a LID of 0 (invalid).  Therefore, make sure that we always
1130 	   configure the SM with a LID that has non-zero bits, even after
1131 	   LMC masking by hardware.
1132 	 */
1133 	lid_mgr_get_port_lid(p_mgr, p_port, &min_lid_ho, &max_lid_ho);
1134 	OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
1135 		"Current base LID is %u\n", min_lid_ho);
1136 	/*
1137 	   Update subnet object.
1138 	 */
1139 	p_mgr->p_subn->master_sm_base_lid = cl_hton16(min_lid_ho);
1140 	p_mgr->p_subn->sm_base_lid = cl_hton16(min_lid_ho);
1141 
1142 	OSM_LOG(p_mgr->p_log, OSM_LOG_VERBOSE,
1143 		"Assigning SM's port 0x%016" PRIx64
1144 		"\n\t\t\t\tto LID range [%u,%u]\n",
1145 		cl_ntoh64(osm_port_get_guid(p_port)), min_lid_ho, max_lid_ho);
1146 
1147 	/*
1148 	   Set the PortInfo the Physical Port associated with this Port.
1149 	 */
1150 	ret = lid_mgr_set_physp_pi(p_mgr, p_port, p_port->p_physp,
1151 				   cl_hton16(min_lid_ho));
1152 
1153 Exit:
1154 	OSM_LOG_EXIT(p_mgr->p_log);
1155 	return ret;
1156 }
1157 
osm_lid_mgr_process_sm(IN osm_lid_mgr_t * p_mgr)1158 int osm_lid_mgr_process_sm(IN osm_lid_mgr_t * p_mgr)
1159 {
1160 	int ret;
1161 
1162 	OSM_LOG_ENTER(p_mgr->p_log);
1163 
1164 	CL_ASSERT(p_mgr->p_subn->sm_port_guid);
1165 
1166 	CL_PLOCK_EXCL_ACQUIRE(p_mgr->p_lock);
1167 
1168 	/* initialize the port_lid_tbl and empty ranges list following the
1169 	   persistent db */
1170 	lid_mgr_init_sweep(p_mgr);
1171 
1172 	ret = lid_mgr_process_our_sm_node(p_mgr);
1173 
1174 	CL_PLOCK_RELEASE(p_mgr->p_lock);
1175 
1176 	OSM_LOG_EXIT(p_mgr->p_log);
1177 	return ret;
1178 }
1179 
1180 /**********************************************************************
1181  1 go through all ports in the subnet.
1182  1.1 call lid_mgr_get_port_lid
1183  1.2 if a change is required send the port info
1184  2 if any change send the signal PENDING...
1185 **********************************************************************/
osm_lid_mgr_process_subnet(IN osm_lid_mgr_t * p_mgr)1186 int osm_lid_mgr_process_subnet(IN osm_lid_mgr_t * p_mgr)
1187 {
1188 	cl_qmap_t *p_port_guid_tbl;
1189 	osm_port_t *p_port;
1190 	ib_net64_t port_guid;
1191 	int lid_changed, ret = 0;
1192 	uint16_t min_lid_ho, max_lid_ho;
1193 
1194 	CL_ASSERT(p_mgr);
1195 
1196 	OSM_LOG_ENTER(p_mgr->p_log);
1197 
1198 	CL_PLOCK_EXCL_ACQUIRE(p_mgr->p_lock);
1199 
1200 	CL_ASSERT(p_mgr->p_subn->sm_port_guid);
1201 
1202 	p_port_guid_tbl = &p_mgr->p_subn->port_guid_tbl;
1203 
1204 	for (p_port = (osm_port_t *) cl_qmap_head(p_port_guid_tbl);
1205 	     p_port != (osm_port_t *) cl_qmap_end(p_port_guid_tbl);
1206 	     p_port = (osm_port_t *) cl_qmap_next(&p_port->map_item)) {
1207 		port_guid = osm_port_get_guid(p_port);
1208 
1209 		/*
1210 		   Our own port is a special case in that we want to
1211 		   assign a LID to ourselves first, since we have to
1212 		   advertise that LID value to the other ports.
1213 
1214 		   For that reason, our node is treated separately and
1215 		   we will not add it to any of these lists.
1216 		 */
1217 		if (port_guid == p_mgr->p_subn->sm_port_guid) {
1218 			OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
1219 				"Skipping our own port 0x%016" PRIx64 "\n",
1220 				cl_ntoh64(port_guid));
1221 			continue;
1222 		}
1223 
1224 		/*
1225 		   get the port lid range - we need to send it on first active
1226 		   sweep or if there was a change (the result of
1227 		   lid_mgr_get_port_lid)
1228 		 */
1229 		lid_changed = lid_mgr_get_port_lid(p_mgr, p_port,
1230 						   &min_lid_ho, &max_lid_ho);
1231 
1232 		/* we can call the function to update the port info as it known
1233 		   to look for any field change and will only send an updated
1234 		   if required */
1235 		OSM_LOG(p_mgr->p_log, OSM_LOG_VERBOSE,
1236 			"Assigned port 0x%016" PRIx64 ", %s LID [%u,%u]\n",
1237 			cl_ntoh64(port_guid), lid_changed ? "new" : "",
1238 			min_lid_ho, max_lid_ho);
1239 
1240 		/* the proc returns the fact it sent a set port info */
1241 		if (lid_mgr_set_physp_pi(p_mgr, p_port, p_port->p_physp,
1242 					 cl_hton16(min_lid_ho)))
1243 			ret = -1;
1244 	}			/* all ports */
1245 
1246 	/* store the guid to lid table in persistent db */
1247 	osm_db_store(p_mgr->p_g2l, p_mgr->p_subn->opt.fsync_high_avail_files);
1248 
1249 	CL_PLOCK_RELEASE(p_mgr->p_lock);
1250 
1251 	OSM_LOG_EXIT(p_mgr->p_log);
1252 	return ret;
1253 }
1254