1219820Sjeff/*
2219820Sjeff * Copyright (c) 2008      Mellanox Technologies LTD. All rights reserved.
3219820Sjeff *
4219820Sjeff * This software is available to you under a choice of one of two
5219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
6219820Sjeff * General Public License (GPL) Version 2, available from the file
7219820Sjeff * COPYING in the main directory of this source tree, or the
8219820Sjeff * OpenIB.org BSD license below:
9219820Sjeff *
10219820Sjeff *     Redistribution and use in source and binary forms, with or
11219820Sjeff *     without modification, are permitted provided that the following
12219820Sjeff *     conditions are met:
13219820Sjeff *
14219820Sjeff *      - Redistributions of source code must retain the above
15219820Sjeff *        copyright notice, this list of conditions and the following
16219820Sjeff *        disclaimer.
17219820Sjeff *
18219820Sjeff *      - Redistributions in binary form must reproduce the above
19219820Sjeff *        copyright notice, this list of conditions and the following
20219820Sjeff *        disclaimer in the documentation and/or other materials
21219820Sjeff *        provided with the distribution.
22219820Sjeff *
23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30219820Sjeff * SOFTWARE.
31219820Sjeff *
32219820Sjeff */
33219820Sjeff
34219820Sjeff/*
35219820Sjeff * Abstract:
36219820Sjeff *    Implementation of OpenSM Cached Unicast Routing
37219820Sjeff *
38219820Sjeff * Environment:
39219820Sjeff *    Linux User Mode
40219820Sjeff *
41219820Sjeff */
42219820Sjeff
43219820Sjeff#if HAVE_CONFIG_H
44219820Sjeff#  include <config.h>
45219820Sjeff#endif
46219820Sjeff
47219820Sjeff#include <stdlib.h>
48219820Sjeff#include <string.h>
49219820Sjeff#include <ctype.h>
50219820Sjeff#include <errno.h>
51219820Sjeff#include <iba/ib_types.h>
52219820Sjeff#include <complib/cl_qmap.h>
53219820Sjeff#include <complib/cl_pool.h>
54219820Sjeff#include <complib/cl_debug.h>
55219820Sjeff#include <opensm/osm_opensm.h>
56219820Sjeff#include <opensm/osm_ucast_mgr.h>
57219820Sjeff#include <opensm/osm_ucast_cache.h>
58219820Sjeff#include <opensm/osm_switch.h>
59219820Sjeff#include <opensm/osm_node.h>
60219820Sjeff#include <opensm/osm_port.h>
61219820Sjeff
62219820Sjeff#define CACHE_SW_PORTS 36
63219820Sjeff
64219820Sjefftypedef struct cache_port {
65219820Sjeff	boolean_t is_leaf;
66219820Sjeff	uint16_t remote_lid_ho;
67219820Sjeff} cache_port_t;
68219820Sjeff
69219820Sjefftypedef struct cache_switch {
70219820Sjeff	cl_map_item_t map_item;
71219820Sjeff	boolean_t dropped;
72219820Sjeff	uint16_t max_lid_ho;
73219820Sjeff	uint16_t num_hops;
74219820Sjeff	uint8_t **hops;
75219820Sjeff	uint8_t *lft;
76219820Sjeff	uint8_t num_ports;
77219820Sjeff	cache_port_t ports[0];
78219820Sjeff} cache_switch_t;
79219820Sjeff
80219820Sjeff/**********************************************************************
81219820Sjeff **********************************************************************/
82219820Sjeff
83219820Sjeffstatic uint16_t __cache_sw_get_base_lid_ho(cache_switch_t * p_sw)
84219820Sjeff{
85219820Sjeff	return p_sw->ports[0].remote_lid_ho;
86219820Sjeff}
87219820Sjeff
88219820Sjeff/**********************************************************************
89219820Sjeff **********************************************************************/
90219820Sjeff
91219820Sjeffstatic boolean_t __cache_sw_is_leaf(cache_switch_t * p_sw)
92219820Sjeff{
93219820Sjeff	return p_sw->ports[0].is_leaf;
94219820Sjeff}
95219820Sjeff
96219820Sjeff/**********************************************************************
97219820Sjeff **********************************************************************/
98219820Sjeff
99219820Sjeffstatic void __cache_sw_set_leaf(cache_switch_t * p_sw)
100219820Sjeff{
101219820Sjeff	p_sw->ports[0].is_leaf = TRUE;
102219820Sjeff}
103219820Sjeff
104219820Sjeff/**********************************************************************
105219820Sjeff **********************************************************************/
106219820Sjeff
107219820Sjeffstatic cache_switch_t *__cache_sw_new(uint16_t lid_ho, unsigned num_ports)
108219820Sjeff{
109219820Sjeff	cache_switch_t *p_cache_sw = malloc(sizeof(cache_switch_t) +
110219820Sjeff					    num_ports * sizeof(cache_port_t));
111219820Sjeff	if (!p_cache_sw)
112219820Sjeff		return NULL;
113219820Sjeff
114219820Sjeff	memset(p_cache_sw, 0,
115219820Sjeff	       sizeof(*p_cache_sw) + num_ports * sizeof(cache_port_t));
116219820Sjeff
117219820Sjeff	p_cache_sw->num_ports = num_ports;
118219820Sjeff
119219820Sjeff	/* port[0] fields represent this switch details - lid and type */
120219820Sjeff	p_cache_sw->ports[0].remote_lid_ho = lid_ho;
121219820Sjeff	p_cache_sw->ports[0].is_leaf = FALSE;
122219820Sjeff
123219820Sjeff	return p_cache_sw;
124219820Sjeff}
125219820Sjeff
126219820Sjeff/**********************************************************************
127219820Sjeff **********************************************************************/
128219820Sjeff
129219820Sjeffstatic void __cache_sw_destroy(cache_switch_t * p_sw)
130219820Sjeff{
131219820Sjeff	if (!p_sw)
132219820Sjeff		return;
133219820Sjeff
134219820Sjeff	if (p_sw->lft)
135219820Sjeff		free(p_sw->lft);
136219820Sjeff	if (p_sw->hops)
137219820Sjeff		free(p_sw->hops);
138219820Sjeff	free(p_sw);
139219820Sjeff}
140219820Sjeff
141219820Sjeff/**********************************************************************
142219820Sjeff **********************************************************************/
143219820Sjeff
144219820Sjeffstatic cache_switch_t *__cache_get_sw(osm_ucast_mgr_t * p_mgr, uint16_t lid_ho)
145219820Sjeff{
146219820Sjeff	cache_switch_t *p_cache_sw = (cache_switch_t *)
147219820Sjeff	    cl_qmap_get(&p_mgr->cache_sw_tbl, lid_ho);
148219820Sjeff	if (p_cache_sw == (cache_switch_t *)
149219820Sjeff	    cl_qmap_end(&p_mgr->cache_sw_tbl))
150219820Sjeff		p_cache_sw = NULL;
151219820Sjeff
152219820Sjeff	return p_cache_sw;
153219820Sjeff}
154219820Sjeff
155219820Sjeff/**********************************************************************
156219820Sjeff **********************************************************************/
157219820Sjeffstatic void __cache_add_sw_link(osm_ucast_mgr_t * p_mgr, osm_physp_t *p,
158219820Sjeff				uint16_t remote_lid_ho, boolean_t is_ca)
159219820Sjeff{
160219820Sjeff	cache_switch_t *p_cache_sw;
161219820Sjeff	uint16_t lid_ho = cl_ntoh16(osm_node_get_base_lid(p->p_node, 0));
162219820Sjeff
163219820Sjeff	OSM_LOG_ENTER(p_mgr->p_log);
164219820Sjeff
165219820Sjeff	if (!lid_ho || !remote_lid_ho || !p->port_num)
166219820Sjeff		goto Exit;
167219820Sjeff
168219820Sjeff	OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
169219820Sjeff		"Caching switch port: lid %u [port %u] -> lid %u (%s)\n",
170219820Sjeff		lid_ho, p->port_num, remote_lid_ho, (is_ca) ? "CA/RTR" : "SW");
171219820Sjeff
172219820Sjeff	p_cache_sw = __cache_get_sw(p_mgr, lid_ho);
173219820Sjeff	if (!p_cache_sw) {
174219820Sjeff		p_cache_sw = __cache_sw_new(lid_ho, p->p_node->sw->num_ports);
175219820Sjeff		if (!p_cache_sw) {
176219820Sjeff			OSM_LOG(p_mgr->p_log, OSM_LOG_ERROR,
177219820Sjeff				"ERR AD01: Out of memory - cache is invalid\n");
178219820Sjeff			osm_ucast_cache_invalidate(p_mgr);
179219820Sjeff			goto Exit;
180219820Sjeff		}
181219820Sjeff		cl_qmap_insert(&p_mgr->cache_sw_tbl, lid_ho,
182219820Sjeff			       &p_cache_sw->map_item);
183219820Sjeff	}
184219820Sjeff
185219820Sjeff	if (p->port_num >= p_cache_sw->num_ports) {
186219820Sjeff		OSM_LOG(p_mgr->p_log, OSM_LOG_ERROR,
187219820Sjeff			"ERR AD02: Wrong switch? - cache is invalid\n");
188219820Sjeff		osm_ucast_cache_invalidate(p_mgr);
189219820Sjeff		goto Exit;
190219820Sjeff	}
191219820Sjeff
192219820Sjeff	if (is_ca)
193219820Sjeff		__cache_sw_set_leaf(p_cache_sw);
194219820Sjeff
195219820Sjeff	if (p_cache_sw->ports[p->port_num].remote_lid_ho == 0) {
196219820Sjeff		/* cache this link only if it hasn't been already cached */
197219820Sjeff		p_cache_sw->ports[p->port_num].remote_lid_ho = remote_lid_ho;
198219820Sjeff		p_cache_sw->ports[p->port_num].is_leaf = is_ca;
199219820Sjeff	}
200219820SjeffExit:
201219820Sjeff	OSM_LOG_EXIT(p_mgr->p_log);
202219820Sjeff}
203219820Sjeff
204219820Sjeff/**********************************************************************
205219820Sjeff **********************************************************************/
206219820Sjeff
207219820Sjeffstatic void __cache_cleanup_switches(osm_ucast_mgr_t * p_mgr)
208219820Sjeff{
209219820Sjeff	cache_switch_t *p_sw;
210219820Sjeff	cache_switch_t *p_next_sw;
211219820Sjeff	unsigned port_num;
212219820Sjeff	boolean_t found_port;
213219820Sjeff
214219820Sjeff	if (!p_mgr->cache_valid)
215219820Sjeff		return;
216219820Sjeff
217219820Sjeff	p_next_sw = (cache_switch_t *) cl_qmap_head(&p_mgr->cache_sw_tbl);
218219820Sjeff	while (p_next_sw !=
219219820Sjeff	       (cache_switch_t *) cl_qmap_end(&p_mgr->cache_sw_tbl)) {
220219820Sjeff		p_sw = p_next_sw;
221219820Sjeff		p_next_sw = (cache_switch_t *) cl_qmap_next(&p_sw->map_item);
222219820Sjeff
223219820Sjeff		found_port = FALSE;
224219820Sjeff		for (port_num = 1; port_num < p_sw->num_ports; port_num++)
225219820Sjeff			if (p_sw->ports[port_num].remote_lid_ho)
226219820Sjeff				found_port = TRUE;
227219820Sjeff
228219820Sjeff		if (!found_port) {
229219820Sjeff			cl_qmap_remove_item(&p_mgr->cache_sw_tbl,
230219820Sjeff					    &p_sw->map_item);
231219820Sjeff			__cache_sw_destroy(p_sw);
232219820Sjeff		}
233219820Sjeff	}
234219820Sjeff}
235219820Sjeff
236219820Sjeff/**********************************************************************
237219820Sjeff **********************************************************************/
238219820Sjeff
239219820Sjeffstatic void
240219820Sjeff__cache_check_link_change(osm_ucast_mgr_t * p_mgr,
241219820Sjeff			  osm_physp_t * p_physp_1, osm_physp_t * p_physp_2)
242219820Sjeff{
243219820Sjeff	OSM_LOG_ENTER(p_mgr->p_log);
244219820Sjeff	CL_ASSERT(p_physp_1 && p_physp_2);
245219820Sjeff
246219820Sjeff	if (!p_mgr->cache_valid)
247219820Sjeff		goto Exit;
248219820Sjeff
249219820Sjeff	if (!p_physp_1->p_remote_physp && !p_physp_2->p_remote_physp)
250219820Sjeff		/* both ports were down - new link */
251219820Sjeff		goto Exit;
252219820Sjeff
253219820Sjeff	/* unicast cache cannot tolerate any link location change */
254219820Sjeff
255219820Sjeff	if ((p_physp_1->p_remote_physp &&
256219820Sjeff	     p_physp_1->p_remote_physp->p_remote_physp) ||
257219820Sjeff	    (p_physp_2->p_remote_physp &&
258219820Sjeff	     p_physp_2->p_remote_physp->p_remote_physp)) {
259219820Sjeff		OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
260219820Sjeff			"Link location change discovered - cache is invalid\n");
261219820Sjeff		osm_ucast_cache_invalidate(p_mgr);
262219820Sjeff		goto Exit;
263219820Sjeff	}
264219820SjeffExit:
265219820Sjeff	OSM_LOG_EXIT(p_mgr->p_log);
266219820Sjeff}
267219820Sjeff
268219820Sjeff/**********************************************************************
269219820Sjeff **********************************************************************/
270219820Sjeff
271219820Sjeffstatic void __cache_remove_port(osm_ucast_mgr_t * p_mgr, uint16_t lid_ho,
272219820Sjeff				uint8_t port_num, uint16_t remote_lid_ho,
273219820Sjeff				boolean_t is_ca)
274219820Sjeff{
275219820Sjeff	cache_switch_t *p_cache_sw;
276219820Sjeff
277219820Sjeff	OSM_LOG_ENTER(p_mgr->p_log);
278219820Sjeff
279219820Sjeff	if (!p_mgr->cache_valid)
280219820Sjeff		goto Exit;
281219820Sjeff
282219820Sjeff	p_cache_sw = __cache_get_sw(p_mgr, lid_ho);
283219820Sjeff	if (!p_cache_sw) {
284219820Sjeff		OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
285219820Sjeff			"Found uncached switch/link (lid %u, port %u) - "
286219820Sjeff			"cache is invalid\n", lid_ho, port_num);
287219820Sjeff		osm_ucast_cache_invalidate(p_mgr);
288219820Sjeff		goto Exit;
289219820Sjeff	}
290219820Sjeff
291219820Sjeff	if (port_num >= p_cache_sw->num_ports ||
292219820Sjeff	    !p_cache_sw->ports[port_num].remote_lid_ho) {
293219820Sjeff		OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
294219820Sjeff			"Found uncached switch link (lid %u, port %u) - "
295219820Sjeff			"cache is invalid\n", lid_ho, port_num);
296219820Sjeff		osm_ucast_cache_invalidate(p_mgr);
297219820Sjeff		goto Exit;
298219820Sjeff	}
299219820Sjeff
300219820Sjeff	if (p_cache_sw->ports[port_num].remote_lid_ho != remote_lid_ho) {
301219820Sjeff		OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
302219820Sjeff			"Remote lid change on switch lid %u, port %u "
303219820Sjeff			"(was %u, now %u) - cache is invalid\n",
304219820Sjeff			lid_ho, port_num,
305219820Sjeff			p_cache_sw->ports[port_num].remote_lid_ho,
306219820Sjeff			remote_lid_ho);
307219820Sjeff		osm_ucast_cache_invalidate(p_mgr);
308219820Sjeff		goto Exit;
309219820Sjeff	}
310219820Sjeff
311219820Sjeff	if ((p_cache_sw->ports[port_num].is_leaf && !is_ca) ||
312219820Sjeff	    (!p_cache_sw->ports[port_num].is_leaf && is_ca)) {
313219820Sjeff		OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
314219820Sjeff			"Remote node type change on switch lid %u, port %u - "
315219820Sjeff			"cache is invalid\n", lid_ho, port_num);
316219820Sjeff		osm_ucast_cache_invalidate(p_mgr);
317219820Sjeff		goto Exit;
318219820Sjeff	}
319219820Sjeff
320219820Sjeff	OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
321219820Sjeff		"New link from lid %u, port %u to lid %u - "
322219820Sjeff		"found in cache\n", lid_ho, port_num, remote_lid_ho);
323219820Sjeff
324219820Sjeff	/* the new link was cached - clean it from the cache */
325219820Sjeff
326219820Sjeff	p_cache_sw->ports[port_num].remote_lid_ho = 0;
327219820Sjeff	p_cache_sw->ports[port_num].is_leaf = FALSE;
328219820SjeffExit:
329219820Sjeff	OSM_LOG_EXIT(p_mgr->p_log);
330219820Sjeff}				/* __cache_remove_port() */
331219820Sjeff
332219820Sjeff/**********************************************************************
333219820Sjeff **********************************************************************/
334219820Sjeff
335219820Sjeffstatic void
336219820Sjeff__cache_restore_ucast_info(osm_ucast_mgr_t * p_mgr,
337219820Sjeff			   cache_switch_t * p_cache_sw, osm_switch_t * p_sw)
338219820Sjeff{
339219820Sjeff	if (!p_mgr->cache_valid)
340219820Sjeff		return;
341219820Sjeff
342219820Sjeff	/* when seting unicast info, the cached port
343219820Sjeff	   should have all the required info */
344219820Sjeff	CL_ASSERT(p_cache_sw->max_lid_ho && p_cache_sw->lft &&
345219820Sjeff		  p_cache_sw->num_hops && p_cache_sw->hops);
346219820Sjeff
347219820Sjeff	p_sw->max_lid_ho = p_cache_sw->max_lid_ho;
348219820Sjeff
349219820Sjeff	if (p_sw->new_lft)
350219820Sjeff		free(p_sw->new_lft);
351219820Sjeff	p_sw->new_lft = p_cache_sw->lft;
352219820Sjeff	p_cache_sw->lft = NULL;
353219820Sjeff
354219820Sjeff	p_sw->num_hops = p_cache_sw->num_hops;
355219820Sjeff	p_cache_sw->num_hops = 0;
356219820Sjeff	if (p_sw->hops)
357219820Sjeff		free(p_sw->hops);
358219820Sjeff	p_sw->hops = p_cache_sw->hops;
359219820Sjeff	p_cache_sw->hops = NULL;
360219820Sjeff}
361219820Sjeff
362219820Sjeff/**********************************************************************
363219820Sjeff **********************************************************************/
364219820Sjeff
365219820Sjeffstatic void __ucast_cache_dump(osm_ucast_mgr_t * p_mgr)
366219820Sjeff{
367219820Sjeff	cache_switch_t *p_sw;
368219820Sjeff	unsigned i;
369219820Sjeff
370219820Sjeff	OSM_LOG_ENTER(p_mgr->p_log);
371219820Sjeff
372219820Sjeff	if (!osm_log_is_active(p_mgr->p_log, OSM_LOG_DEBUG))
373219820Sjeff		goto Exit;
374219820Sjeff
375219820Sjeff	OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
376219820Sjeff		"Dumping missing nodes/links as logged by unicast cache:\n");
377219820Sjeff	for (p_sw = (cache_switch_t *) cl_qmap_head(&p_mgr->cache_sw_tbl);
378219820Sjeff	     p_sw != (cache_switch_t *) cl_qmap_end(&p_mgr->cache_sw_tbl);
379219820Sjeff	     p_sw = (cache_switch_t *) cl_qmap_next(&p_sw->map_item)) {
380219820Sjeff
381219820Sjeff		OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
382219820Sjeff			"\t Switch lid %u %s%s\n",
383219820Sjeff			__cache_sw_get_base_lid_ho(p_sw),
384219820Sjeff			(__cache_sw_is_leaf(p_sw)) ? "[leaf switch] " : "",
385219820Sjeff			(p_sw->dropped) ? "[whole switch missing]" : "");
386219820Sjeff
387219820Sjeff		for (i = 1; i < p_sw->num_ports; i++)
388219820Sjeff			if (p_sw->ports[i].remote_lid_ho > 0)
389219820Sjeff				OSM_LOG(p_mgr->p_log,
390219820Sjeff					OSM_LOG_DEBUG,
391219820Sjeff					"\t     - port %u -> lid %u %s\n",
392219820Sjeff					i, p_sw->ports[i].remote_lid_ho,
393219820Sjeff					(p_sw->ports[i].is_leaf) ?
394219820Sjeff					"[remote node is leaf]" : "");
395219820Sjeff	}
396219820SjeffExit:
397219820Sjeff	OSM_LOG_EXIT(p_mgr->p_log);
398219820Sjeff}
399219820Sjeff
400219820Sjeff/**********************************************************************
401219820Sjeff **********************************************************************/
402219820Sjeff
403219820Sjeffvoid osm_ucast_cache_invalidate(osm_ucast_mgr_t * p_mgr)
404219820Sjeff{
405219820Sjeff	cache_switch_t *p_sw;
406219820Sjeff	cache_switch_t *p_next_sw;
407219820Sjeff
408219820Sjeff	OSM_LOG_ENTER(p_mgr->p_log);
409219820Sjeff	OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG, "Invalidating unicast cache\n");
410219820Sjeff
411219820Sjeff	if (!p_mgr->cache_valid)
412219820Sjeff		goto Exit;
413219820Sjeff
414219820Sjeff	p_mgr->cache_valid = FALSE;
415219820Sjeff
416219820Sjeff	p_next_sw = (cache_switch_t *) cl_qmap_head(&p_mgr->cache_sw_tbl);
417219820Sjeff	while (p_next_sw !=
418219820Sjeff	       (cache_switch_t *) cl_qmap_end(&p_mgr->cache_sw_tbl)) {
419219820Sjeff		p_sw = p_next_sw;
420219820Sjeff		p_next_sw = (cache_switch_t *) cl_qmap_next(&p_sw->map_item);
421219820Sjeff		__cache_sw_destroy(p_sw);
422219820Sjeff	}
423219820Sjeff	cl_qmap_remove_all(&p_mgr->cache_sw_tbl);
424219820SjeffExit:
425219820Sjeff	OSM_LOG_EXIT(p_mgr->p_log);
426219820Sjeff}
427219820Sjeff
428219820Sjeff/**********************************************************************
429219820Sjeff **********************************************************************/
430219820Sjeff
431219820Sjeffstatic void ucast_cache_validate(osm_ucast_mgr_t * p_mgr)
432219820Sjeff{
433219820Sjeff	cache_switch_t *p_cache_sw;
434219820Sjeff	cache_switch_t *p_remote_cache_sw;
435219820Sjeff	unsigned port_num;
436219820Sjeff	unsigned max_ports;
437219820Sjeff	uint8_t remote_node_type;
438219820Sjeff	uint16_t lid_ho;
439219820Sjeff	uint16_t remote_lid_ho;
440219820Sjeff	osm_switch_t *p_sw;
441219820Sjeff	osm_switch_t *p_remote_sw;
442219820Sjeff	osm_node_t *p_node;
443219820Sjeff	osm_physp_t *p_physp;
444219820Sjeff	osm_physp_t *p_remote_physp;
445219820Sjeff	osm_port_t *p_remote_port;
446219820Sjeff	cl_qmap_t *p_sw_tbl;
447219820Sjeff
448219820Sjeff	OSM_LOG_ENTER(p_mgr->p_log);
449219820Sjeff	if (!p_mgr->cache_valid)
450219820Sjeff		goto Exit;
451219820Sjeff
452219820Sjeff	/* If there are no switches in the subnet, we are done */
453219820Sjeff	p_sw_tbl = &p_mgr->p_subn->sw_guid_tbl;
454219820Sjeff	if (cl_qmap_count(p_sw_tbl) == 0) {
455219820Sjeff		osm_ucast_cache_invalidate(p_mgr);
456219820Sjeff		goto Exit;
457219820Sjeff	}
458219820Sjeff
459219820Sjeff	/*
460219820Sjeff	 * Scan all the physical switch ports in the subnet.
461219820Sjeff	 * If the port need_update flag is on, check whether
462219820Sjeff	 * it's just some node/port reset or a cached topology
463219820Sjeff	 * change. Otherwise the cache is invalid.
464219820Sjeff	 */
465219820Sjeff	for (p_sw = (osm_switch_t *) cl_qmap_head(p_sw_tbl);
466219820Sjeff	     p_sw != (osm_switch_t *) cl_qmap_end(p_sw_tbl);
467219820Sjeff	     p_sw = (osm_switch_t *) cl_qmap_next(&p_sw->map_item)) {
468219820Sjeff
469219820Sjeff		p_node = p_sw->p_node;
470219820Sjeff
471219820Sjeff		lid_ho = cl_ntoh16(osm_node_get_base_lid(p_node, 0));
472219820Sjeff		p_cache_sw = __cache_get_sw(p_mgr, lid_ho);
473219820Sjeff
474219820Sjeff		max_ports = osm_node_get_num_physp(p_node);
475219820Sjeff
476219820Sjeff		/* skip port 0 */
477219820Sjeff		for (port_num = 1; port_num < max_ports; port_num++) {
478219820Sjeff
479219820Sjeff			p_physp = osm_node_get_physp_ptr(p_node, port_num);
480219820Sjeff
481219820Sjeff			if (!p_physp || !p_physp->p_remote_physp ||
482219820Sjeff			    !osm_physp_link_exists(p_physp,
483219820Sjeff						   p_physp->p_remote_physp))
484219820Sjeff				/* no valid link */
485219820Sjeff				continue;
486219820Sjeff
487219820Sjeff			/*
488219820Sjeff			 * While scanning all the physical ports in the subnet,
489219820Sjeff			 * mark corresponding leaf switches in the cache.
490219820Sjeff			 */
491219820Sjeff			if (p_cache_sw &&
492219820Sjeff			    !p_cache_sw->dropped &&
493219820Sjeff			    !__cache_sw_is_leaf(p_cache_sw) &&
494219820Sjeff			    p_physp->p_remote_physp->p_node &&
495219820Sjeff			    osm_node_get_type(p_physp->p_remote_physp->
496219820Sjeff					      p_node) != IB_NODE_TYPE_SWITCH)
497219820Sjeff				__cache_sw_set_leaf(p_cache_sw);
498219820Sjeff
499219820Sjeff			if (!p_physp->need_update)
500219820Sjeff				continue;
501219820Sjeff
502219820Sjeff			OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
503219820Sjeff				"Checking switch lid %u, port %u\n",
504219820Sjeff				lid_ho, port_num);
505219820Sjeff
506219820Sjeff			p_remote_physp = osm_physp_get_remote(p_physp);
507219820Sjeff			remote_node_type =
508219820Sjeff			    osm_node_get_type(p_remote_physp->p_node);
509219820Sjeff
510219820Sjeff			if (remote_node_type == IB_NODE_TYPE_SWITCH)
511219820Sjeff				remote_lid_ho =
512219820Sjeff				    cl_ntoh16(osm_node_get_base_lid
513219820Sjeff					      (p_remote_physp->p_node, 0));
514219820Sjeff			else
515219820Sjeff				remote_lid_ho =
516219820Sjeff				    cl_ntoh16(osm_node_get_base_lid
517219820Sjeff					      (p_remote_physp->p_node,
518219820Sjeff					       osm_physp_get_port_num
519219820Sjeff					       (p_remote_physp)));
520219820Sjeff
521219820Sjeff			if (!p_cache_sw ||
522219820Sjeff			    port_num >= p_cache_sw->num_ports ||
523219820Sjeff			    !p_cache_sw->ports[port_num].remote_lid_ho) {
524219820Sjeff				/*
525219820Sjeff				 * There is some uncached change on the port.
526219820Sjeff				 * In general, the reasons might be as follows:
527219820Sjeff				 *  - switch reset
528219820Sjeff				 *  - port reset (or port down/up)
529219820Sjeff				 *  - quick connection location change
530219820Sjeff				 *  - new link (or new switch)
531219820Sjeff				 *
532219820Sjeff				 * First two reasons allow cache usage, while
533219820Sjeff				 * the last two reasons should invalidate cache.
534219820Sjeff				 *
535219820Sjeff				 * In case of quick connection location change,
536219820Sjeff				 * cache would have been invalidated by
537219820Sjeff				 * osm_ucast_cache_check_new_link() function.
538219820Sjeff				 *
539219820Sjeff				 * In case of new link between two known nodes,
540219820Sjeff				 * cache also would have been invalidated by
541219820Sjeff				 * osm_ucast_cache_check_new_link() function.
542219820Sjeff				 *
543219820Sjeff				 * Another reason is cached link between two
544219820Sjeff				 * known switches went back. In this case the
545219820Sjeff				 * osm_ucast_cache_check_new_link() function would
546219820Sjeff				 * clear both sides of the link from the cache
547219820Sjeff				 * during the discovery process, so effectively
548219820Sjeff				 * this would be equivalent to port reset.
549219820Sjeff				 *
550219820Sjeff				 * So three possible reasons remain:
551219820Sjeff				 *  - switch reset
552219820Sjeff				 *  - port reset (or port down/up)
553219820Sjeff				 *  - link of a new switch
554219820Sjeff				 *
555219820Sjeff				 * To validate cache, we need to check only the
556219820Sjeff				 * third reason - link of a new node/switch:
557219820Sjeff				 *  - If this is the local switch that is new,
558219820Sjeff				 *    then it should have (p_sw->need_update == 2).
559219820Sjeff				 *  - If the remote node is switch and it's new,
560219820Sjeff				 *    then it also should have
561219820Sjeff				 *    (p_sw->need_update == 2).
562219820Sjeff				 *  - If the remote node is CA/RTR and it's new,
563219820Sjeff				 *    then its port should have is_new flag on.
564219820Sjeff				 */
565219820Sjeff				if (p_sw->need_update == 2) {
566219820Sjeff					OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
567219820Sjeff						"New switch found (lid %u) - "
568219820Sjeff						"cache is invalid\n", lid_ho);
569219820Sjeff					osm_ucast_cache_invalidate(p_mgr);
570219820Sjeff					goto Exit;
571219820Sjeff				}
572219820Sjeff
573219820Sjeff				if (remote_node_type == IB_NODE_TYPE_SWITCH) {
574219820Sjeff
575219820Sjeff					p_remote_sw =
576219820Sjeff					    p_remote_physp->p_node->sw;
577219820Sjeff					if (p_remote_sw->need_update == 2) {
578219820Sjeff						/* this could also be case of
579219820Sjeff						   switch coming back with an
580219820Sjeff						   additional link that it
581219820Sjeff						   didn't have before */
582219820Sjeff						OSM_LOG(p_mgr->p_log,
583219820Sjeff							OSM_LOG_INFO,
584219820Sjeff							"New switch/link found (lid %u) - "
585219820Sjeff							"cache is invalid\n",
586219820Sjeff							remote_lid_ho);
587219820Sjeff						osm_ucast_cache_invalidate
588219820Sjeff						    (p_mgr);
589219820Sjeff						goto Exit;
590219820Sjeff					}
591219820Sjeff				} else {
592219820Sjeff					/*
593219820Sjeff					 * Remote node is CA/RTR.
594219820Sjeff					 * Get p_port of the remote node and
595219820Sjeff					 * check its p_port->is_new flag.
596219820Sjeff					 */
597219820Sjeff					p_remote_port =
598219820Sjeff					    osm_get_port_by_guid(p_mgr->p_subn,
599219820Sjeff								 osm_physp_get_port_guid
600219820Sjeff								 (p_remote_physp));
601219820Sjeff					if (p_remote_port->is_new) {
602219820Sjeff						OSM_LOG(p_mgr->p_log,
603219820Sjeff							OSM_LOG_INFO,
604219820Sjeff							"New CA/RTR found (lid %u) - "
605219820Sjeff							"cache is invalid\n",
606219820Sjeff							remote_lid_ho);
607219820Sjeff						osm_ucast_cache_invalidate
608219820Sjeff						    (p_mgr);
609219820Sjeff						goto Exit;
610219820Sjeff					}
611219820Sjeff				}
612219820Sjeff			} else {
613219820Sjeff				/*
614219820Sjeff				 * The change on the port is cached.
615219820Sjeff				 * In general, the reasons might be as follows:
616219820Sjeff				 *  - link between two known nodes went back
617219820Sjeff				 *  - one or more nodes went back, causing all
618219820Sjeff				 *    the links to reappear
619219820Sjeff				 *
620219820Sjeff				 * If it was link that went back, then this case
621219820Sjeff				 * would have been taken care of during the
622219820Sjeff				 * discovery by osm_ucast_cache_check_new_link(),
623219820Sjeff				 * so it's some node that went back.
624219820Sjeff				 */
625219820Sjeff				if ((p_cache_sw->ports[port_num].is_leaf &&
626219820Sjeff				     remote_node_type == IB_NODE_TYPE_SWITCH) ||
627219820Sjeff				    (!p_cache_sw->ports[port_num].is_leaf &&
628219820Sjeff				     remote_node_type != IB_NODE_TYPE_SWITCH)) {
629219820Sjeff					OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
630219820Sjeff						"Remote node type change on switch lid %u, port %u - "
631219820Sjeff						"cache is invalid\n",
632219820Sjeff						lid_ho, port_num);
633219820Sjeff					osm_ucast_cache_invalidate(p_mgr);
634219820Sjeff					goto Exit;
635219820Sjeff				}
636219820Sjeff
637219820Sjeff				if (p_cache_sw->ports[port_num].remote_lid_ho !=
638219820Sjeff				    remote_lid_ho) {
639219820Sjeff					OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
640219820Sjeff						"Remote lid change on switch lid %u, port %u"
641219820Sjeff						"(was %u, now %u) - cache is invalid\n",
642219820Sjeff						lid_ho, port_num,
643219820Sjeff						p_cache_sw->ports[port_num].
644219820Sjeff						remote_lid_ho, remote_lid_ho);
645219820Sjeff					osm_ucast_cache_invalidate(p_mgr);
646219820Sjeff					goto Exit;
647219820Sjeff				}
648219820Sjeff
649219820Sjeff				/*
650219820Sjeff				 * We don't care who is the node that has
651219820Sjeff				 * reappeared in the subnet (local or remote).
652219820Sjeff				 * What's important that the cached link matches
653219820Sjeff				 * the real fabrics link.
654219820Sjeff				 * Just clean it from cache.
655219820Sjeff				 */
656219820Sjeff
657219820Sjeff				p_cache_sw->ports[port_num].remote_lid_ho = 0;
658219820Sjeff				p_cache_sw->ports[port_num].is_leaf = FALSE;
659219820Sjeff				if (p_cache_sw->dropped) {
660219820Sjeff					__cache_restore_ucast_info(p_mgr,
661219820Sjeff								   p_cache_sw,
662219820Sjeff								   p_sw);
663219820Sjeff					p_cache_sw->dropped = FALSE;
664219820Sjeff				}
665219820Sjeff
666219820Sjeff				OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
667219820Sjeff					"Restored link from cache: lid %u, port %u to lid %u\n",
668219820Sjeff					lid_ho, port_num, remote_lid_ho);
669219820Sjeff			}
670219820Sjeff		}
671219820Sjeff	}
672219820Sjeff
673219820Sjeff	/* Remove all the cached switches that
674219820Sjeff	   have all their ports restored */
675219820Sjeff	__cache_cleanup_switches(p_mgr);
676219820Sjeff
677219820Sjeff	/*
678219820Sjeff	 * Done scanning all the physical switch ports in the subnet.
679219820Sjeff	 * Now we need to check the other side:
680219820Sjeff	 * Scan all the cached switches and their ports:
681219820Sjeff	 *  - If the cached switch is missing in the subnet
682219820Sjeff	 *    (dropped flag is on), check that it's a leaf switch.
683219820Sjeff	 *    If it's not a leaf, the cache is invalid, because
684219820Sjeff	 *    cache can tolerate only leaf switch removal.
685219820Sjeff	 *  - If the cached switch exists in fabric, check all
686219820Sjeff	 *    its cached ports. These cached ports represent
687219820Sjeff	 *    missing link in the fabric.
688219820Sjeff	 *    The missing links that can be tolerated are:
689219820Sjeff	 *      + link to missing CA/RTR
690219820Sjeff	 *      + link to missing leaf switch
691219820Sjeff	 */
692219820Sjeff	for (p_cache_sw = (cache_switch_t *) cl_qmap_head(&p_mgr->cache_sw_tbl);
693219820Sjeff	     p_cache_sw != (cache_switch_t *) cl_qmap_end(&p_mgr->cache_sw_tbl);
694219820Sjeff	     p_cache_sw =
695219820Sjeff	     (cache_switch_t *) cl_qmap_next(&p_cache_sw->map_item)) {
696219820Sjeff
697219820Sjeff		if (p_cache_sw->dropped) {
698219820Sjeff			if (!__cache_sw_is_leaf(p_cache_sw)) {
699219820Sjeff				OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
700219820Sjeff					"Missing non-leaf switch (lid %u) - "
701219820Sjeff					"cache is invalid\n",
702219820Sjeff					__cache_sw_get_base_lid_ho(p_cache_sw));
703219820Sjeff				osm_ucast_cache_invalidate(p_mgr);
704219820Sjeff				goto Exit;
705219820Sjeff			}
706219820Sjeff
707219820Sjeff			OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
708219820Sjeff				"Missing leaf switch (lid %u) - "
709219820Sjeff				"continuing validation\n",
710219820Sjeff				__cache_sw_get_base_lid_ho(p_cache_sw));
711219820Sjeff			continue;
712219820Sjeff		}
713219820Sjeff
714219820Sjeff		for (port_num = 1; port_num < p_cache_sw->num_ports; port_num++) {
715219820Sjeff			if (!p_cache_sw->ports[port_num].remote_lid_ho)
716219820Sjeff				continue;
717219820Sjeff
718219820Sjeff			if (p_cache_sw->ports[port_num].is_leaf) {
719219820Sjeff				CL_ASSERT(__cache_sw_is_leaf(p_cache_sw));
720219820Sjeff				OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
721219820Sjeff					"Switch lid %u, port %u: missing link to CA/RTR - "
722219820Sjeff					"continuing validation\n",
723219820Sjeff					__cache_sw_get_base_lid_ho(p_cache_sw),
724219820Sjeff					port_num);
725219820Sjeff				continue;
726219820Sjeff			}
727219820Sjeff
728219820Sjeff			p_remote_cache_sw = __cache_get_sw(p_mgr,
729219820Sjeff							   p_cache_sw->
730219820Sjeff							   ports[port_num].
731219820Sjeff							   remote_lid_ho);
732219820Sjeff
733219820Sjeff			if (!p_remote_cache_sw || !p_remote_cache_sw->dropped) {
734219820Sjeff				OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
735219820Sjeff					"Switch lid %u, port %u: missing link to existing switch - "
736219820Sjeff					"cache is invalid\n",
737219820Sjeff					__cache_sw_get_base_lid_ho(p_cache_sw),
738219820Sjeff					port_num);
739219820Sjeff				osm_ucast_cache_invalidate(p_mgr);
740219820Sjeff				goto Exit;
741219820Sjeff			}
742219820Sjeff
743219820Sjeff			if (!__cache_sw_is_leaf(p_remote_cache_sw)) {
744219820Sjeff				OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
745219820Sjeff					"Switch lid %u, port %u: missing link to non-leaf switch - "
746219820Sjeff					"cache is invalid\n",
747219820Sjeff					__cache_sw_get_base_lid_ho(p_cache_sw),
748219820Sjeff					port_num);
749219820Sjeff				osm_ucast_cache_invalidate(p_mgr);
750219820Sjeff				goto Exit;
751219820Sjeff			}
752219820Sjeff
753219820Sjeff			/*
754219820Sjeff			 * At this point we know that the missing link is to
755219820Sjeff			 * a leaf switch. However, one case deserves a special
756219820Sjeff			 * treatment. If there was a link between two leaf
757219820Sjeff			 * switches, then missing leaf switch might break
758219820Sjeff			 * routing. It is possible that there are routes
759219820Sjeff			 * that use leaf switches to get from switch to switch
760219820Sjeff			 * and not just to get to the CAs behind the leaf switch.
761219820Sjeff			 */
762219820Sjeff			if (__cache_sw_is_leaf(p_cache_sw) &&
763219820Sjeff			    __cache_sw_is_leaf(p_remote_cache_sw)) {
764219820Sjeff				OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
765219820Sjeff					"Switch lid %u, port %u: missing leaf-2-leaf link - "
766219820Sjeff					"cache is invalid\n",
767219820Sjeff					__cache_sw_get_base_lid_ho(p_cache_sw),
768219820Sjeff					port_num);
769219820Sjeff				osm_ucast_cache_invalidate(p_mgr);
770219820Sjeff				goto Exit;
771219820Sjeff			}
772219820Sjeff
773219820Sjeff			OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
774219820Sjeff				"Switch lid %u, port %u: missing remote leaf switch - "
775219820Sjeff				"continuing validation\n",
776219820Sjeff				__cache_sw_get_base_lid_ho(p_cache_sw),
777219820Sjeff				port_num);
778219820Sjeff		}
779219820Sjeff	}
780219820Sjeff
781219820Sjeff	OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG, "Unicast cache is valid\n");
782219820Sjeff	__ucast_cache_dump(p_mgr);
783219820SjeffExit:
784219820Sjeff	OSM_LOG_EXIT(p_mgr->p_log);
785219820Sjeff}				/* osm_ucast_cache_validate() */
786219820Sjeff
787219820Sjeff/**********************************************************************
788219820Sjeff **********************************************************************/
789219820Sjeff
790219820Sjeffvoid osm_ucast_cache_check_new_link(osm_ucast_mgr_t * p_mgr,
791219820Sjeff				    osm_node_t * p_node_1, uint8_t port_num_1,
792219820Sjeff				    osm_node_t * p_node_2, uint8_t port_num_2)
793219820Sjeff{
794219820Sjeff	uint16_t lid_ho_1;
795219820Sjeff	uint16_t lid_ho_2;
796219820Sjeff
797219820Sjeff	OSM_LOG_ENTER(p_mgr->p_log);
798219820Sjeff
799219820Sjeff	if (!p_mgr->cache_valid)
800219820Sjeff		goto Exit;
801219820Sjeff
802219820Sjeff	__cache_check_link_change(p_mgr,
803219820Sjeff				  osm_node_get_physp_ptr(p_node_1, port_num_1),
804219820Sjeff				  osm_node_get_physp_ptr(p_node_2, port_num_2));
805219820Sjeff
806219820Sjeff	if (!p_mgr->cache_valid)
807219820Sjeff		goto Exit;
808219820Sjeff
809219820Sjeff	if (osm_node_get_type(p_node_1) != IB_NODE_TYPE_SWITCH &&
810219820Sjeff	    osm_node_get_type(p_node_2) != IB_NODE_TYPE_SWITCH) {
811219820Sjeff		OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
812219820Sjeff			"Found CA/RTR-2-CA/RTR link - cache is invalid\n");
813219820Sjeff		osm_ucast_cache_invalidate(p_mgr);
814219820Sjeff		goto Exit;
815219820Sjeff	}
816219820Sjeff
817219820Sjeff	/* for code simplicity, we want the first node to be switch */
818219820Sjeff	if (osm_node_get_type(p_node_1) != IB_NODE_TYPE_SWITCH) {
819219820Sjeff		osm_node_t *tmp_node = p_node_1;
820219820Sjeff		uint8_t tmp_port_num = port_num_1;
821219820Sjeff		p_node_1 = p_node_2;
822219820Sjeff		port_num_1 = port_num_2;
823219820Sjeff		p_node_2 = tmp_node;
824219820Sjeff		port_num_2 = tmp_port_num;
825219820Sjeff	}
826219820Sjeff
827219820Sjeff	lid_ho_1 = cl_ntoh16(osm_node_get_base_lid(p_node_1, 0));
828219820Sjeff
829219820Sjeff	if (osm_node_get_type(p_node_2) == IB_NODE_TYPE_SWITCH)
830219820Sjeff		lid_ho_2 = cl_ntoh16(osm_node_get_base_lid(p_node_2, 0));
831219820Sjeff	else
832219820Sjeff		lid_ho_2 =
833219820Sjeff		    cl_ntoh16(osm_node_get_base_lid(p_node_2, port_num_2));
834219820Sjeff
835219820Sjeff	if (!lid_ho_1 || !lid_ho_2) {
836219820Sjeff		/*
837219820Sjeff		 * No lid assigned, which means that one of the nodes is new.
838219820Sjeff		 * Need to wait for lid manager to process this node.
839219820Sjeff		 * The switches and their links will be checked later when
840219820Sjeff		 * the whole cache validity will be verified.
841219820Sjeff		 */
842219820Sjeff		OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
843219820Sjeff			"Link port %u <-> %u reveals new node - cache will "
844219820Sjeff			"be validated later\n", port_num_1, port_num_2);
845219820Sjeff		goto Exit;
846219820Sjeff	}
847219820Sjeff
848219820Sjeff	__cache_remove_port(p_mgr, lid_ho_1, port_num_1, lid_ho_2,
849219820Sjeff			    (osm_node_get_type(p_node_2) !=
850219820Sjeff			     IB_NODE_TYPE_SWITCH));
851219820Sjeff
852219820Sjeff	/* if node_2 is a switch, the link should be cleaned from its cache */
853219820Sjeff
854219820Sjeff	if (osm_node_get_type(p_node_2) == IB_NODE_TYPE_SWITCH)
855219820Sjeff		__cache_remove_port(p_mgr, lid_ho_2,
856219820Sjeff				    port_num_2, lid_ho_1, FALSE);
857219820Sjeff
858219820SjeffExit:
859219820Sjeff	OSM_LOG_EXIT(p_mgr->p_log);
860219820Sjeff}				/* osm_ucast_cache_check_new_link() */
861219820Sjeff
862219820Sjeff/**********************************************************************
863219820Sjeff **********************************************************************/
864219820Sjeff
865219820Sjeffvoid osm_ucast_cache_add_link(osm_ucast_mgr_t * p_mgr,
866219820Sjeff			      osm_physp_t * p_physp1, osm_physp_t * p_physp2)
867219820Sjeff{
868219820Sjeff	osm_node_t *p_node_1 = p_physp1->p_node, *p_node_2 = p_physp2->p_node;
869219820Sjeff	uint16_t lid_ho_1, lid_ho_2;
870219820Sjeff
871219820Sjeff	OSM_LOG_ENTER(p_mgr->p_log);
872219820Sjeff
873219820Sjeff	if (!p_mgr->cache_valid)
874219820Sjeff		goto Exit;
875219820Sjeff
876219820Sjeff	if (osm_node_get_type(p_node_1) != IB_NODE_TYPE_SWITCH &&
877219820Sjeff	    osm_node_get_type(p_node_2) != IB_NODE_TYPE_SWITCH) {
878219820Sjeff		OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
879219820Sjeff			"Dropping CA-2-CA link - cache invalid\n");
880219820Sjeff		osm_ucast_cache_invalidate(p_mgr);
881219820Sjeff		goto Exit;
882219820Sjeff	}
883219820Sjeff
884219820Sjeff	if ((osm_node_get_type(p_node_1) == IB_NODE_TYPE_SWITCH &&
885219820Sjeff	     !osm_node_get_physp_ptr(p_node_1, 0)) ||
886219820Sjeff	    (osm_node_get_type(p_node_2) == IB_NODE_TYPE_SWITCH &&
887219820Sjeff	     !osm_node_get_physp_ptr(p_node_2, 0))) {
888219820Sjeff		/* we're caching a link when one of the nodes
889219820Sjeff		   has already been dropped and cached */
890219820Sjeff		OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
891219820Sjeff			"Port %u <-> port %u: port0 on one of the nodes "
892219820Sjeff			"has already been dropped and cached\n",
893219820Sjeff			p_physp1->port_num, p_physp2->port_num);
894219820Sjeff		goto Exit;
895219820Sjeff	}
896219820Sjeff
897219820Sjeff	/* One of the nodes is switch. Just for code
898219820Sjeff	   simplicity, make sure that it's the first node. */
899219820Sjeff
900219820Sjeff	if (osm_node_get_type(p_node_1) != IB_NODE_TYPE_SWITCH) {
901219820Sjeff		osm_physp_t *tmp = p_physp1;
902219820Sjeff		p_physp1 = p_physp2;
903219820Sjeff		p_physp2 = tmp;
904219820Sjeff		p_node_1 = p_physp1->p_node;
905219820Sjeff		p_node_2 = p_physp2->p_node;
906219820Sjeff	}
907219820Sjeff
908219820Sjeff	if (!p_node_1->sw) {
909219820Sjeff		/* something is wrong - we'd better not use cache */
910219820Sjeff		osm_ucast_cache_invalidate(p_mgr);
911219820Sjeff		goto Exit;
912219820Sjeff	}
913219820Sjeff
914219820Sjeff	lid_ho_1 = cl_ntoh16(osm_node_get_base_lid(p_node_1, 0));
915219820Sjeff
916219820Sjeff	if (osm_node_get_type(p_node_2) == IB_NODE_TYPE_SWITCH) {
917219820Sjeff
918219820Sjeff		if (!p_node_2->sw) {
919219820Sjeff			/* something is wrong - we'd better not use cache */
920219820Sjeff			osm_ucast_cache_invalidate(p_mgr);
921219820Sjeff			goto Exit;
922219820Sjeff		}
923219820Sjeff
924219820Sjeff		lid_ho_2 = cl_ntoh16(osm_node_get_base_lid(p_node_2, 0));
925219820Sjeff
926219820Sjeff		/* lost switch-2-switch link - cache both sides */
927219820Sjeff		__cache_add_sw_link(p_mgr, p_physp1, lid_ho_2, FALSE);
928219820Sjeff		__cache_add_sw_link(p_mgr, p_physp2, lid_ho_1, FALSE);
929219820Sjeff	} else {
930219820Sjeff		lid_ho_2 = cl_ntoh16(osm_physp_get_base_lid(p_physp2));
931219820Sjeff
932219820Sjeff		/* lost link to CA/RTR - cache only switch side */
933219820Sjeff		__cache_add_sw_link(p_mgr, p_physp1, lid_ho_2, TRUE);
934219820Sjeff	}
935219820Sjeff
936219820SjeffExit:
937219820Sjeff	OSM_LOG_EXIT(p_mgr->p_log);
938219820Sjeff}				/* osm_ucast_cache_add_link() */
939219820Sjeff
940219820Sjeff/**********************************************************************
941219820Sjeff **********************************************************************/
942219820Sjeff
943219820Sjeffvoid osm_ucast_cache_add_node(osm_ucast_mgr_t * p_mgr, osm_node_t * p_node)
944219820Sjeff{
945219820Sjeff	uint16_t lid_ho;
946219820Sjeff	uint8_t max_ports;
947219820Sjeff	uint8_t port_num;
948219820Sjeff	osm_physp_t *p_physp;
949219820Sjeff	cache_switch_t *p_cache_sw;
950219820Sjeff
951219820Sjeff	OSM_LOG_ENTER(p_mgr->p_log);
952219820Sjeff
953219820Sjeff	if (!p_mgr->cache_valid)
954219820Sjeff		goto Exit;
955219820Sjeff
956219820Sjeff	if (osm_node_get_type(p_node) == IB_NODE_TYPE_SWITCH) {
957219820Sjeff
958219820Sjeff		lid_ho = cl_ntoh16(osm_node_get_base_lid(p_node, 0));
959219820Sjeff
960219820Sjeff		OSM_LOG(p_mgr->p_log, OSM_LOG_DEBUG,
961219820Sjeff			"Caching dropped switch lid %u\n", lid_ho);
962219820Sjeff
963219820Sjeff		if (!p_node->sw) {
964219820Sjeff			/* something is wrong - forget about cache */
965219820Sjeff			OSM_LOG(p_mgr->p_log, OSM_LOG_ERROR,
966219820Sjeff				"ERR AD03: no switch info for node lid %u - "
967219820Sjeff				"clearing cache\n", lid_ho);
968219820Sjeff			osm_ucast_cache_invalidate(p_mgr);
969219820Sjeff			goto Exit;
970219820Sjeff		}
971219820Sjeff
972219820Sjeff		/* unlink (add to cache) all the ports of this switch */
973219820Sjeff		max_ports = osm_node_get_num_physp(p_node);
974219820Sjeff		for (port_num = 1; port_num < max_ports; port_num++) {
975219820Sjeff
976219820Sjeff			p_physp = osm_node_get_physp_ptr(p_node, port_num);
977219820Sjeff			if (!p_physp || !p_physp->p_remote_physp)
978219820Sjeff				continue;
979219820Sjeff
980219820Sjeff			osm_ucast_cache_add_link(p_mgr, p_physp,
981219820Sjeff						 p_physp->p_remote_physp);
982219820Sjeff		}
983219820Sjeff
984219820Sjeff		/*
985219820Sjeff		 * All the ports have been dropped (cached).
986219820Sjeff		 * If one of the ports was connected to CA/RTR,
987219820Sjeff		 * then the cached switch would be marked as leaf.
988219820Sjeff		 * If it isn't, then the dropped switch isn't a leaf,
989219820Sjeff		 * and cache can't handle it.
990219820Sjeff		 */
991219820Sjeff
992219820Sjeff		p_cache_sw = __cache_get_sw(p_mgr, lid_ho);
993219820Sjeff		CL_ASSERT(p_cache_sw);
994219820Sjeff
995219820Sjeff		if (!__cache_sw_is_leaf(p_cache_sw)) {
996219820Sjeff			OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
997219820Sjeff				"Dropped non-leaf switch (lid %u) - "
998219820Sjeff				"cache is invalid\n", lid_ho);
999219820Sjeff			osm_ucast_cache_invalidate(p_mgr);
1000219820Sjeff			goto Exit;
1001219820Sjeff		}
1002219820Sjeff
1003219820Sjeff		p_cache_sw->dropped = TRUE;
1004219820Sjeff
1005219820Sjeff		if (!p_node->sw->num_hops || !p_node->sw->hops) {
1006219820Sjeff			OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
1007219820Sjeff				"No LID matrices for switch lid %u - "
1008219820Sjeff				"cache is invalid\n", lid_ho);
1009219820Sjeff			osm_ucast_cache_invalidate(p_mgr);
1010219820Sjeff			goto Exit;
1011219820Sjeff		}
1012219820Sjeff
1013219820Sjeff		/* lid matrices */
1014219820Sjeff
1015219820Sjeff		p_cache_sw->num_hops = p_node->sw->num_hops;
1016219820Sjeff		p_node->sw->num_hops = 0;
1017219820Sjeff		p_cache_sw->hops = p_node->sw->hops;
1018219820Sjeff		p_node->sw->hops = NULL;
1019219820Sjeff
1020219820Sjeff		/* linear forwarding table */
1021219820Sjeff
1022219820Sjeff		if (p_node->sw->new_lft) {
1023219820Sjeff			/* LFT buffer exists - we use it, because
1024219820Sjeff			   it is more updated than the switch's LFT */
1025219820Sjeff			p_cache_sw->lft = p_node->sw->new_lft;
1026219820Sjeff			p_node->sw->new_lft = NULL;
1027219820Sjeff		} else {
1028219820Sjeff			/* no LFT buffer, so we use the switch's LFT */
1029219820Sjeff			p_cache_sw->lft = p_node->sw->lft;
1030219820Sjeff			p_node->sw->lft = NULL;
1031219820Sjeff		}
1032219820Sjeff		p_cache_sw->max_lid_ho = p_node->sw->max_lid_ho;
1033219820Sjeff	} else {
1034219820Sjeff		/* dropping CA/RTR: add to cache all the ports of this node */
1035219820Sjeff		max_ports = osm_node_get_num_physp(p_node);
1036219820Sjeff		for (port_num = 1; port_num < max_ports; port_num++) {
1037219820Sjeff
1038219820Sjeff			p_physp = osm_node_get_physp_ptr(p_node, port_num);
1039219820Sjeff			if (!p_physp || !p_physp->p_remote_physp)
1040219820Sjeff				continue;
1041219820Sjeff
1042219820Sjeff			CL_ASSERT(osm_node_get_type
1043219820Sjeff				  (p_physp->p_remote_physp->p_node) ==
1044219820Sjeff				  IB_NODE_TYPE_SWITCH);
1045219820Sjeff
1046219820Sjeff			osm_ucast_cache_add_link(p_mgr,
1047219820Sjeff						 p_physp->p_remote_physp,
1048219820Sjeff						 p_physp);
1049219820Sjeff		}
1050219820Sjeff	}
1051219820SjeffExit:
1052219820Sjeff	OSM_LOG_EXIT(p_mgr->p_log);
1053219820Sjeff}				/* osm_ucast_cache_add_node() */
1054219820Sjeff
1055219820Sjeff/**********************************************************************
1056219820Sjeff **********************************************************************/
1057219820Sjeff
1058219820Sjeffint osm_ucast_cache_process(osm_ucast_mgr_t * p_mgr)
1059219820Sjeff{
1060219820Sjeff	cl_qmap_t *tbl = &p_mgr->p_subn->sw_guid_tbl;
1061219820Sjeff	cl_map_item_t *item;
1062219820Sjeff	osm_switch_t *p_sw;
1063219820Sjeff
1064219820Sjeff	if (!p_mgr->p_subn->opt.use_ucast_cache)
1065219820Sjeff		return 1;
1066219820Sjeff
1067219820Sjeff	ucast_cache_validate(p_mgr);
1068219820Sjeff	if (!p_mgr->cache_valid)
1069219820Sjeff		return 1;
1070219820Sjeff
1071219820Sjeff	OSM_LOG(p_mgr->p_log, OSM_LOG_INFO,
1072219820Sjeff		"Configuring switch tables using cached routing\n");
1073219820Sjeff
1074219820Sjeff	for (item = cl_qmap_head(tbl); item != cl_qmap_end(tbl);
1075219820Sjeff	     item = cl_qmap_next(item)) {
1076219820Sjeff		p_sw = (osm_switch_t *) item;
1077219820Sjeff
1078219820Sjeff		if (p_sw->need_update && !p_sw->new_lft) {
1079219820Sjeff			/* no new routing was recently calculated for this
1080219820Sjeff			   switch, but the LFT needs to be updated anyway */
1081219820Sjeff			p_sw->new_lft = p_sw->lft;
1082219820Sjeff			p_sw->lft = malloc(IB_LID_UCAST_END_HO + 1);
1083219820Sjeff			if (!p_sw->lft)
1084219820Sjeff				return IB_INSUFFICIENT_MEMORY;
1085219820Sjeff			memset(p_sw->lft, OSM_NO_PATH, IB_LID_UCAST_END_HO + 1);
1086219820Sjeff		}
1087219820Sjeff
1088219820Sjeff		osm_ucast_mgr_set_fwd_table(p_mgr, p_sw);
1089219820Sjeff	}
1090219820Sjeff
1091219820Sjeff	return 0;
1092219820Sjeff}
1093219820Sjeff
1094219820Sjeff/**********************************************************************
1095219820Sjeff **********************************************************************/
1096