Implementation of the osm unicast routing cache.

Signed-off-by: Yevgeny Kliteynik <[EMAIL PROTECTED]>
---
 opensm/include/opensm/osm_ucast_cache.h |  439 ++++++++++++
 opensm/opensm/osm_ucast_cache.c         | 1176 +++++++++++++++++++++++++++++++
 2 files changed, 1615 insertions(+), 0 deletions(-)
 create mode 100644 opensm/include/opensm/osm_ucast_cache.h
 create mode 100644 opensm/opensm/osm_ucast_cache.c

diff --git a/opensm/include/opensm/osm_ucast_cache.h 
b/opensm/include/opensm/osm_ucast_cache.h
new file mode 100644
index 0000000..2dc1c4e
--- /dev/null
+++ b/opensm/include/opensm/osm_ucast_cache.h
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2008      Mellanox Technologies LTD. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+/*
+ * Abstract:
+ *     Header file that describes Unicast Cache functions.
+ *
+ * Environment:
+ *     Linux User Mode
+ *
+ * $Revision: 1.4 $
+ */
+
+#ifndef _OSM_UCAST_CACHE_H_
+#define _OSM_UCAST_CACHE_H_
+
+#include <iba/ib_types.h>
+#include <complib/cl_qmap.h>
+#include <opensm/osm_switch.h>
+
+#ifdef __cplusplus
+#  define BEGIN_C_DECLS extern "C" {
+#  define END_C_DECLS   }
+#else                          /* !__cplusplus */
+#  define BEGIN_C_DECLS
+#  define END_C_DECLS
+#endif                         /* __cplusplus */
+
+BEGIN_C_DECLS
+
+struct osm_ucast_mgr;
+
+/****h* OpenSM/Unicast Manager/Unicast Cache
+* NAME
+*      Unicast Cache
+*
+* DESCRIPTION
+*      The Unicast Cache object encapsulates the information
+*      needed to cache and write unicast routing of the subnet.
+*
+*      The Unicast Cache object is NOT thread safe.
+*
+*      This object should be treated as opaque and should be
+*      manipulated only through the provided functions.
+*
+* AUTHOR
+*      Yevgeny Kliteynik, Mellanox
+*
+*********/
+
+/****s* OpenSM: Unicast Cache/osm_ucast_cache_t
+* NAME
+*      osm_ucast_cache_t
+*
+* DESCRIPTION
+*      Unicast Cache structure.
+*
+*      This object should be treated as opaque and should
+*      be manipulated only through the provided functions.
+*
+* SYNOPSIS
+*/
+typedef struct _osm_ucast_cache {
+       cl_qmap_t sw_tbl;
+       boolean_t valid;
+       struct osm_ucast_mgr * p_ucast_mgr;
+} osm_ucast_cache_t;
+/*
+* FIELDS
+*      sw_tbl
+*              Cached switches table.
+*
+*      valid
+*              TRUE if the cache is valid.
+*
+*      p_ucast_mgr
+*              Pointer to the Unicast Manager for this subnet.
+*
+* SEE ALSO
+*      Unicast Manager object
+*********/
+
+/****f* OpenSM: Unicast Cache/osm_ucast_cache_construct
+* NAME
+*      osm_ucast_cache_construct
+*
+* DESCRIPTION
+*      This function constructs a Unicast Cache object.
+*
+* SYNOPSIS
+*/
+osm_ucast_cache_t *
+osm_ucast_cache_construct(struct osm_ucast_mgr * const p_mgr);
+/*
+* PARAMETERS
+*      p_mgr
+*              [in] Pointer to a Unicast Manager object.
+*
+* RETURN VALUE
+*      This function return the created Ucast Cache object on success,
+*      or NULL on any error.
+*
+* NOTES
+*      Allows osm_ucast_cache_destroy
+*
+*      Calling osm_ucast_mgr_construct is a prerequisite to
+*      calling any other method.
+*
+* SEE ALSO
+*      Unicast Cache object, osm_ucast_cache_destroy
+*********/
+
+/****f* OpenSM: Unicast Cache/osm_ucast_cache_destroy
+* NAME
+*      osm_ucast_cache_destroy
+*
+* DESCRIPTION
+*      The osm_ucast_cache_destroy function destroys the object,
+*      releasing all resources.
+*
+* SYNOPSIS
+*/
+void
+osm_ucast_cache_destroy(osm_ucast_cache_t * p_cache);
+/*
+* PARAMETERS
+*      p_cache
+*              [in] Pointer to the object to destroy.
+*
+* RETURN VALUE
+*      This function does not return any value.
+*
+* NOTES
+*      Performs any necessary cleanup of the specified
+*      Unicast Cache object.
+*      Further operations should not be attempted on the
+*      destroyed object.
+*      This function should only be called after a call to
+*      osm_ucast_cache_construct.
+*
+* SEE ALSO
+*      Unicast Cache object, osm_ucast_cache_construct
+*********/
+
+/****f* OpenSM: Unicast Cache/osm_ucast_cache_invalidate
+* NAME
+*      osm_ucast_cache_invalidate
+*
+* DESCRIPTION
+*      The osm_ucast_cache_invalidate function purges the
+*      unicast cache and marks the cache as invalid.
+*
+* SYNOPSIS
+*/
+void
+osm_ucast_cache_invalidate(osm_ucast_cache_t * p_cache);
+/*
+* PARAMETERS
+*      p_cache
+*              [in] Pointer to the object to invalidate.
+*
+* RETURN VALUE
+*      This function does not return any value.
+*
+* NOTES
+*
+* SEE ALSO
+*      Unicast Cache object
+*********/
+
+/****f* OpenSM: Unicast Cache/osm_ucast_cache_validate
+* NAME
+*      osm_ucast_cache_validate
+*
+* DESCRIPTION
+*      The osm_ucast_cache_validate function checks
+*      whether or not the cached routing can be applied
+*      to the current subnet switches.
+*
+* SYNOPSIS
+*/
+void
+osm_ucast_cache_validate(osm_ucast_cache_t * p_cache);
+/*
+* PARAMETERS
+*      p_cache
+*              [in] Pointer to the object to check.
+*
+* RETURN VALUE
+*      This function does not return any value.
+*
+* NOTES
+*      This function checks the current subnet and the
+*      cached links, and decides whether or not there
+*      is a need to re-run unicast routing engine.
+*      If the cached routing can't be applied to the
+*      current subnet switches as is, cache is invalidated.
+*
+* SEE ALSO
+*      Unicast Cache object
+*********/
+
+/****f* OpenSM: Unicast Cache/osm_ucast_cache_mark_valid
+* NAME
+*      osm_ucast_cache_mark_valid
+*
+* DESCRIPTION
+*      The osm_ucast_cache_mark_valid function marks
+*      the cache as valid.
+*
+* SYNOPSIS
+*/
+void
+osm_ucast_cache_mark_valid(osm_ucast_cache_t * p_cache);
+/*
+* PARAMETERS
+*      p_cache
+*              [in] Pointer to the object to mark.
+*
+* RETURN VALUE
+*      This function does not return any value.
+*
+* NOTES
+*
+* SEE ALSO
+*      Unicast Cache object
+*********/
+
+/****f* OpenSM: Unicast Cache/osm_ucast_cache_is_valid
+* NAME
+*      osm_ucast_cache_is_valid
+*
+* DESCRIPTION
+*      Check whether the unicast cache is valid.
+*
+* SYNOPSIS
+*/
+boolean_t
+osm_ucast_cache_is_valid(osm_ucast_cache_t * p_cache);
+/*
+* PARAMETERS
+*      p_cache
+*              [in] Pointer to the object to check.
+*
+* RETURN VALUE
+*      TRUE if the cache is valid, FALSE otherwise.
+*
+* NOTES
+*
+* SEE ALSO
+*      Unicast Cache object
+*********/
+
+/****f* OpenSM: Unicast Cache/osm_ucast_cache_check_new_link
+* NAME
+*      osm_ucast_cache_check_new_link
+*
+* DESCRIPTION
+*      The osm_ucast_cache_check_new_link checks whether
+*      the newly discovered link still allows us to use
+*      cached unicast routing.
+*
+* SYNOPSIS
+*/
+void
+osm_ucast_cache_check_new_link(osm_ucast_cache_t * p_cache,
+                              osm_node_t * p_node_1,
+                              uint8_t port_num_1,
+                              osm_node_t * p_node_2,
+                              uint8_t port_num_2);
+/*
+* PARAMETERS
+*      p_cache
+*              [in] Pointer to the cache object.
+*
+*      p_node_1
+*              [in] Pointer to the first node of the link.
+*
+*      port_num_1
+*              [in] Port number on the first node of the link.
+*
+*      p_node_2
+*              [in] Pointer to the second node of the link.
+*
+*      port_num_2
+*              [in] Port number on the second node of the link.
+*
+* RETURN VALUE
+*      This function does not return any value.
+*
+* NOTES
+*      The function checks whether the link was previously
+*      cached/dropped or is this a completely new link.
+*      If it decides that the new link makes cached routing
+*      invalid, the cache is purged and marked as invalid.
+*
+* SEE ALSO
+*      Unicast Cache object
+*********/
+
+/****f* OpenSM: Unicast Cache/osm_ucast_cache_add_link
+* NAME
+*      osm_ucast_cache_add_link
+*
+* DESCRIPTION
+*      The osm_ucast_cache_add_link adds link to the cache.
+*
+* SYNOPSIS
+*/
+void
+osm_ucast_cache_add_link(osm_ucast_cache_t * p_cache,
+                        osm_node_t * p_node_1,
+                        uint8_t port_num_1,
+                        osm_node_t * p_node_2,
+                        uint8_t port_num_2);
+/*
+* PARAMETERS
+*      p_cache
+*              [in] Pointer to the cache object.
+*
+*      p_node_1
+*              [in] Pointer to the first node of the link.
+*
+*      port_num_1
+*              [in] Port number on the first node of the link.
+*
+*      p_node_2
+*              [in] Pointer to the second node of the link.
+*
+*      port_num_2
+*              [in] Port number on the second node of the link.
+*
+* RETURN VALUE
+*      This function does not return any value.
+*
+* NOTES
+*      Since the cache operates with ports and not links,
+*      the function adds two port entries (both sides of the
+*      link) to the cache.
+*      If it decides that the dropped link makes cached routing
+*      invalid, the cache is purged and marked as invalid.
+*
+* SEE ALSO
+*      Unicast Cache object
+*********/
+
+/****f* OpenSM: Unicast Cache/osm_ucast_cache_add_node
+* NAME
+*      osm_ucast_cache_add_node
+*
+* DESCRIPTION
+*      The osm_ucast_cache_add_node adds node and all
+*      its links to the cache.
+*
+* SYNOPSIS
+*/
+void
+osm_ucast_cache_add_node(osm_ucast_cache_t * p_cache,
+                        osm_node_t * p_node);
+/*
+* PARAMETERS
+*      p_cache
+*              [in] Pointer to the cache object.
+*
+*      p_node
+*              [in] Pointer to the node object that should be cached.
+*
+* RETURN VALUE
+*      This function does not return any value.
+*
+* NOTES
+*      If the function decides that the dropped node makes cached
+*      routing invalid, the cache is purged and marked as invalid.
+*
+* SEE ALSO
+*      Unicast Cache object
+*********/
+
+/****f* OpenSM: Unicast Cache/osm_ucast_cache_apply
+* NAME
+*      osm_ucast_cache_apply
+*
+* DESCRIPTION
+*      The osm_ucast_cache_apply function writes the
+*      cached unicast routing on the subnet switches.
+*
+* SYNOPSIS
+*/
+void
+osm_ucast_cache_apply(osm_ucast_cache_t * p_cache);
+/*
+* PARAMETERS
+*      p_cache
+*              [in] Pointer to the cache object to be used.
+*
+* RETURN VALUE
+*      This function does not return any value.
+*
+* NOTES
+*      Iterates through all the subnet switches and writes
+*      the LFTs that were calculated during the last routing
+*       engine execution to the switches.
+*
+* SEE ALSO
+*      Unicast Cache object
+*********/
+
+END_C_DECLS
+#endif                         /* _OSM_UCAST_CACHE_H_ */
diff --git a/opensm/opensm/osm_ucast_cache.c b/opensm/opensm/osm_ucast_cache.c
new file mode 100644
index 0000000..2c2154a
--- /dev/null
+++ b/opensm/opensm/osm_ucast_cache.c
@@ -0,0 +1,1176 @@
+/*
+ * Copyright (c) 2008      Mellanox Technologies LTD. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+/*
+ * Abstract:
+ *    Implementation of OpenSM Cached Unicast Routing
+ *
+ * Environment:
+ *    Linux User Mode
+ *
+ */
+
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <iba/ib_types.h>
+#include <complib/cl_qmap.h>
+#include <complib/cl_pool.h>
+#include <complib/cl_debug.h>
+#include <opensm/osm_opensm.h>
+#include <opensm/osm_ucast_mgr.h>
+#include <opensm/osm_ucast_cache.h>
+#include <opensm/osm_switch.h>
+#include <opensm/osm_node.h>
+#include <opensm/osm_port.h>
+
+typedef struct _cache_port {
+       boolean_t is_leaf;
+       uint16_t remote_lid_ho;
+} cache_port_t;
+
+typedef struct _cache_switch {
+       cl_map_item_t map_item;
+       boolean_t dropped;
+       uint16_t max_lid_ho;
+       uint8_t num_ports;
+       cache_port_t * ports;
+       uint16_t num_hops;
+       uint8_t ** hops;
+       uint8_t * lft;
+} cache_switch_t;
+
+/**********************************************************************
+ **********************************************************************/
+
+static uint16_t
+__cache_sw_get_base_lid_ho(cache_switch_t * p_sw)
+{
+       return p_sw->ports[0].remote_lid_ho;
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+static boolean_t
+__cache_sw_is_leaf(cache_switch_t * p_sw)
+{
+       return p_sw->ports[0].is_leaf;
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+static void
+__cache_sw_set_leaf(cache_switch_t * p_sw)
+{
+       p_sw->ports[0].is_leaf = TRUE;
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+static cache_switch_t *
+__cache_sw_new(uint16_t lid_ho)
+{
+       cache_switch_t * p_cache_sw =
+               (cache_switch_t *)malloc(sizeof(cache_switch_t));
+       if (!p_cache_sw)
+               return NULL;
+
+       memset(p_cache_sw, 0, sizeof(cache_switch_t));
+
+       p_cache_sw->ports = (cache_port_t *)malloc(sizeof(cache_port_t));
+       if (!p_cache_sw->ports) {
+               free(p_cache_sw);
+               return NULL;
+       }
+
+       /* port[0] fields represent this switch details - lid and type */
+       p_cache_sw->ports[0].remote_lid_ho = lid_ho;
+       p_cache_sw->ports[0].is_leaf = FALSE;
+
+       return p_cache_sw;
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+static void
+__cache_sw_destroy(cache_switch_t * p_sw)
+{
+       if (!p_sw)
+               return;
+
+       if (p_sw->lft)
+               free(p_sw->lft);
+       if (p_sw->hops)
+               free(p_sw->hops);
+       if (p_sw->ports)
+               free(p_sw->ports);
+       free(p_sw);
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+static cache_switch_t *
+__cache_get_sw(osm_ucast_cache_t * p_cache, uint16_t lid_ho)
+{
+       cache_switch_t * p_cache_sw =
+               (cache_switch_t *)cl_qmap_get(&p_cache->sw_tbl, lid_ho);
+       if (p_cache_sw == (cache_switch_t *)cl_qmap_end(&p_cache->sw_tbl))
+               p_cache_sw = NULL;
+
+       return p_cache_sw;
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+static cache_switch_t *
+__cache_get_or_add_sw(osm_ucast_cache_t * p_cache, uint16_t lid_ho)
+{
+       cache_switch_t * p_cache_sw = __cache_get_sw(p_cache, lid_ho);
+        if (!p_cache_sw) {
+               p_cache_sw = __cache_sw_new(lid_ho);
+               if (p_cache_sw)
+                       cl_qmap_insert(&p_cache->sw_tbl, lid_ho,
+                                      &p_cache_sw->map_item);
+       }
+       return p_cache_sw;
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+static void
+__cache_add_port(osm_ucast_cache_t * p_cache,
+                uint16_t lid_ho,
+                uint8_t port_num,
+                uint16_t remote_lid_ho,
+                boolean_t is_ca)
+{
+       cache_switch_t * p_cache_sw;
+
+       OSM_LOG_ENTER(p_cache->p_ucast_mgr->p_log);
+
+       if (!lid_ho || !remote_lid_ho || !port_num)
+               goto Exit;
+
+       OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_VERBOSE,
+               "Caching switch port: lid %u [port %u] -> lid %u (%s)\n",
+               lid_ho, port_num, remote_lid_ho,
+               (is_ca)? "CA/RTR" : "SW");
+
+       p_cache_sw = __cache_get_or_add_sw(p_cache, lid_ho);
+       if (!p_cache_sw) {
+               OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_ERROR,
+                       "ERR AD01: Out of memory - cache is invalid\n");
+               osm_ucast_cache_invalidate(p_cache);
+               goto Exit;
+       }
+
+       if (port_num >= p_cache_sw->num_ports) {
+               cache_port_t * ports = (cache_port_t *)
+                       malloc(sizeof(cache_port_t)*(port_num+1));
+               memset(ports, 0, sizeof(cache_port_t)*(port_num+1));
+
+               if (p_cache_sw->ports) {
+                       memcpy(ports, p_cache_sw->ports,
+                              sizeof(cache_port_t)*(p_cache_sw->num_ports+1));
+                       free(p_cache_sw->ports);
+               }
+
+               p_cache_sw->ports = ports;
+               p_cache_sw->num_ports = port_num + 1;
+       }
+
+       if (is_ca)
+               __cache_sw_set_leaf(p_cache_sw);
+
+       if (p_cache_sw->ports[port_num].remote_lid_ho == 0) {
+               /* cache this link only if it hasn't been already cached */
+               p_cache_sw->ports[port_num].remote_lid_ho = remote_lid_ho;
+               p_cache_sw->ports[port_num].is_leaf = is_ca;
+       }
+Exit:
+       OSM_LOG_EXIT(p_cache->p_ucast_mgr->p_log);
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+static void
+__cache_cleanup_switches(osm_ucast_cache_t * p_cache)
+{
+       cache_switch_t * p_sw;
+       cache_switch_t * p_next_sw;
+       unsigned port_num;
+       boolean_t found_port;
+
+       CL_ASSERT(p_cache);
+       if (!p_cache->valid)
+               return;
+
+       p_next_sw = (cache_switch_t *) cl_qmap_head(&p_cache->sw_tbl);
+       while (p_next_sw != (cache_switch_t *) cl_qmap_end(&p_cache->sw_tbl)) {
+               p_sw = p_next_sw;
+               p_next_sw = (cache_switch_t *) cl_qmap_next(&p_sw->map_item);
+
+               found_port = FALSE;
+               for (port_num = 1; port_num < p_sw->num_ports; port_num++)
+                       if (p_sw->ports[port_num].remote_lid_ho)
+                               found_port = TRUE;
+
+               if (!found_port) {
+                       cl_qmap_remove_item(&p_cache->sw_tbl, &p_sw->map_item);
+                       __cache_sw_destroy(p_sw);
+               }
+       }
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+static void
+__cache_check_link_change(osm_ucast_cache_t * p_cache,
+                         osm_physp_t * p_physp_1,
+                         osm_physp_t * p_physp_2)
+{
+       OSM_LOG_ENTER(p_cache->p_ucast_mgr->p_log);
+       CL_ASSERT(p_physp_1 && p_physp_2);
+
+       if (!p_cache->valid)
+               goto Exit;
+
+       if (!p_physp_1->p_remote_physp && !p_physp_2->p_remote_physp)
+               /* both ports were down - new link */
+               goto Exit;
+
+       /* unicast cache cannot tolerate any link location change */
+
+       if ((p_physp_1->p_remote_physp &&
+            p_physp_1->p_remote_physp->p_remote_physp) ||
+           (p_physp_2->p_remote_physp &&
+            p_physp_2->p_remote_physp->p_remote_physp)) {
+               OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_INFO,
+                       "Link location change discovered - cache is invalid\n");
+               osm_ucast_cache_invalidate(p_cache);
+               goto Exit;
+       }
+Exit:
+       OSM_LOG_EXIT(p_cache->p_ucast_mgr->p_log);
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+static void
+__cache_remove_port(osm_ucast_cache_t * p_cache,
+                   uint16_t lid_ho,
+                   uint8_t port_num,
+                   uint16_t remote_lid_ho,
+                   boolean_t is_ca)
+{
+       cache_switch_t * p_cache_sw;
+
+       OSM_LOG_ENTER(p_cache->p_ucast_mgr->p_log);
+
+       if (!p_cache->valid)
+               goto Exit;
+
+       p_cache_sw = __cache_get_sw(p_cache, lid_ho);
+       if (!p_cache_sw) {
+               OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_INFO,
+                       "Found uncached switch/link (lid %u, port %u) - "
+                       "cache is invalid\n", lid_ho, port_num);
+               osm_ucast_cache_invalidate(p_cache);
+               goto Exit;
+       }
+
+       if (port_num >= p_cache_sw->num_ports ||
+           !p_cache_sw->ports[port_num].remote_lid_ho) {
+               OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_INFO,
+                       "Found uncached switch link (lid %u, port %u) - "
+                       "cache is invalid\n", lid_ho, port_num);
+               osm_ucast_cache_invalidate(p_cache);
+               goto Exit;
+       }
+
+       if (p_cache_sw->ports[port_num].remote_lid_ho != remote_lid_ho) {
+               OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_INFO,
+                       "Remote lid change on switch lid %u, port %u"
+                       "(was %u, now %u) - cache is invalid\n",
+                       lid_ho, port_num,
+                       p_cache_sw->ports[port_num].remote_lid_ho,
+                       remote_lid_ho);
+               osm_ucast_cache_invalidate(p_cache);
+               goto Exit;
+       }
+
+       if ((p_cache_sw->ports[port_num].is_leaf && !is_ca) ||
+           (!p_cache_sw->ports[port_num].is_leaf && is_ca)) {
+               OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_INFO,
+                       "Remote node type change on switch lid %u, port %u - "
+                       "cache is invalid\n",
+                       lid_ho, port_num);
+               osm_ucast_cache_invalidate(p_cache);
+               goto Exit;
+       }
+
+       OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_VERBOSE,
+               "New link from lid %u, port %u to lid %u - "
+               "found in cache\n",
+               lid_ho, port_num, remote_lid_ho);
+
+       /* the new link was cached - clean it from the cache */
+
+       p_cache_sw->ports[port_num].remote_lid_ho = 0;
+       p_cache_sw->ports[port_num].is_leaf = FALSE;
+Exit:
+       OSM_LOG_EXIT(p_cache->p_ucast_mgr->p_log);
+} /* __cache_remove_port() */
+
+/**********************************************************************
+ **********************************************************************/
+
+static void
+__cache_restore_ucast_info(osm_ucast_cache_t * p_cache,
+                          cache_switch_t * p_cache_sw,
+                          osm_switch_t * p_sw)
+{
+       if (!p_cache->valid)
+               return;
+
+       /* when seting unicast info, the cached port
+          should have all the required info */
+       CL_ASSERT(p_cache_sw->max_lid_ho && p_cache_sw->lft &&
+                 p_cache_sw->num_hops && p_cache_sw->hops);
+
+       p_sw->max_lid_ho = p_cache_sw->max_lid_ho;
+
+       if (p_sw->lft_buf)
+               free(p_sw->lft_buf);
+       p_sw->lft_buf = p_cache_sw->lft;
+       p_cache_sw->lft = NULL;
+
+       p_sw->num_hops = p_cache_sw->num_hops;
+       p_cache_sw->num_hops = 0;
+       if (p_sw->hops)
+               free(p_sw->hops);
+       p_sw->hops = p_cache_sw->hops;
+       p_cache_sw->hops = NULL;
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+static void
+__ucast_cache_dump(osm_ucast_cache_t * p_cache)
+{
+       cache_switch_t * p_sw;
+       unsigned i;
+
+       OSM_LOG_ENTER(p_cache->p_ucast_mgr->p_log);
+
+       if (!osm_log_is_active(p_cache->p_ucast_mgr->p_log, OSM_LOG_VERBOSE))
+               goto Exit;
+
+       OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_VERBOSE,
+               "Dumping missing nodes/links as logged by unicast cache:\n");
+       for (p_sw = (cache_switch_t *) cl_qmap_head(&p_cache->sw_tbl);
+            p_sw != (cache_switch_t *) cl_qmap_end(&p_cache->sw_tbl);
+            p_sw = (cache_switch_t *) cl_qmap_next(&p_sw->map_item)) {
+
+               OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_VERBOSE,
+                       "\t Switch lid %u %s%s\n",
+                       __cache_sw_get_base_lid_ho(p_sw),
+                       (__cache_sw_is_leaf(p_sw))? "[leaf switch] " : "",
+                       (p_sw->dropped)? "[whole switch missing]" : "");
+
+               for (i = 1; i < p_sw->num_ports; i++)
+                       if (p_sw->ports[i].remote_lid_ho > 0)
+                               OSM_LOG(p_cache->p_ucast_mgr->p_log,
+                                       OSM_LOG_VERBOSE,
+                                       "\t     - port %u -> lid %u %s\n",
+                                       i, p_sw->ports[i].remote_lid_ho,
+                                       (p_sw->ports[i].is_leaf) ?
+                                       "[remote node is leaf]" : "");
+       }
+Exit:
+       OSM_LOG_EXIT(p_cache->p_ucast_mgr->p_log);
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+osm_ucast_cache_t *
+osm_ucast_cache_construct(osm_ucast_mgr_t * const p_mgr)
+{
+       osm_ucast_cache_t * p_cache = (osm_ucast_cache_t *)
+               malloc(sizeof(osm_ucast_cache_t));
+       if (!p_cache)
+               return NULL;
+
+       memset(p_cache, 0, sizeof(osm_ucast_cache_t));
+       cl_qmap_init(&p_cache->sw_tbl);
+       p_cache->p_ucast_mgr = p_mgr;
+       return p_cache;
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+void
+osm_ucast_cache_destroy(osm_ucast_cache_t * p_cache)
+{
+       if (!p_cache)
+               return;
+       osm_ucast_cache_invalidate(p_cache);
+       free(p_cache);
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+void
+osm_ucast_cache_mark_valid(osm_ucast_cache_t * p_cache)
+{
+       CL_ASSERT(p_cache && p_cache->p_ucast_mgr);
+       p_cache->valid = TRUE;
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+boolean_t
+osm_ucast_cache_is_valid(osm_ucast_cache_t * p_cache)
+{
+       CL_ASSERT(p_cache && p_cache->p_ucast_mgr);
+       return p_cache->valid;
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+void
+osm_ucast_cache_invalidate(osm_ucast_cache_t * p_cache)
+{
+       cache_switch_t * p_sw;
+       cache_switch_t * p_next_sw;
+
+       CL_ASSERT(p_cache);
+
+       OSM_LOG_ENTER(p_cache->p_ucast_mgr->p_log);
+       OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_VERBOSE,
+               "Invalidating unicast cache\n");
+
+       if (!p_cache->valid)
+               goto Exit;
+
+       p_cache->valid = FALSE;
+
+       p_next_sw = (cache_switch_t *) cl_qmap_head(&p_cache->sw_tbl);
+       while (p_next_sw != (cache_switch_t *) cl_qmap_end(&p_cache->sw_tbl)) {
+               p_sw = p_next_sw;
+               p_next_sw = (cache_switch_t *) cl_qmap_next(&p_sw->map_item);
+               __cache_sw_destroy(p_sw);
+       }
+       cl_qmap_remove_all(&p_cache->sw_tbl);
+Exit:
+       OSM_LOG_EXIT(p_cache->p_ucast_mgr->p_log);
+}
+
+/**********************************************************************
+ **********************************************************************/
+
+void
+osm_ucast_cache_validate(osm_ucast_cache_t * p_cache)
+{
+       cache_switch_t * p_cache_sw;
+       cache_switch_t * p_remote_cache_sw;
+       unsigned         port_num;
+       unsigned         max_ports;
+       uint8_t          remote_node_type;
+       uint16_t         lid_ho;
+       uint16_t         remote_lid_ho;
+       osm_switch_t   * p_sw;
+       osm_switch_t   * p_remote_sw;
+       osm_node_t     * p_node;
+       osm_physp_t    * p_physp;
+       osm_physp_t    * p_remote_physp;
+       osm_port_t     * p_remote_port;
+       cl_qmap_t      * p_node_guid_tbl;
+
+       OSM_LOG_ENTER(p_cache->p_ucast_mgr->p_log);
+       if (!p_cache->valid)
+               goto Exit;
+
+       /*
+        * Scan all the physical switch ports in the subnet.
+        * If the port need_update flag is on, check whether
+        * it's just some node/port reset or a cached topology
+        * change. Otherwise the cache is invalid.
+        */
+       p_node_guid_tbl = &p_cache->p_ucast_mgr->p_subn->node_guid_tbl;
+       for (p_node = (osm_node_t *) cl_qmap_head(p_node_guid_tbl);
+            p_node != (osm_node_t *) cl_qmap_end(p_node_guid_tbl);
+            p_node = (osm_node_t *) cl_qmap_next(&p_node->map_item)) {
+
+               if (osm_node_get_type(p_node) != IB_NODE_TYPE_SWITCH)
+                       continue;
+
+               lid_ho = cl_ntoh16(osm_node_get_base_lid(p_node,0));
+               p_cache_sw = __cache_get_sw(p_cache, lid_ho);
+
+               p_sw = p_node->sw;
+               max_ports = osm_node_get_num_physp(p_node);
+
+               /* skip port 0 */
+               for (port_num = 1; port_num < max_ports; port_num++) {
+
+                       p_physp = osm_node_get_physp_ptr(p_node, port_num);
+
+                       if (!p_physp || !p_physp->p_remote_physp ||
+                           !osm_physp_link_exists(p_physp, 
p_physp->p_remote_physp))
+                               /* no valid link */
+                               continue;
+
+                       /*
+                        * While scanning all the physical ports in the subnet,
+                        * mark corresponding leaf switches in the cache.
+                        */
+                       if (p_cache_sw &&
+                           !p_cache_sw->dropped &&
+                           !__cache_sw_is_leaf(p_cache_sw) &&
+                           p_physp->p_remote_physp->p_node &&
+                           osm_node_get_type(
+                                   p_physp->p_remote_physp->p_node) !=
+                                       IB_NODE_TYPE_SWITCH)
+                               __cache_sw_set_leaf(p_cache_sw);
+
+                       if (!p_physp->need_update)
+                               continue;
+
+                       OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_VERBOSE,
+                               "Checking switch lid %u, port %u\n",
+                               lid_ho, port_num);
+
+                       p_remote_physp = osm_physp_get_remote(p_physp);
+                       remote_node_type = 
osm_node_get_type(p_remote_physp->p_node);
+
+                       if (remote_node_type == IB_NODE_TYPE_SWITCH)
+                               remote_lid_ho = cl_ntoh16(osm_node_get_base_lid(
+                                       p_remote_physp->p_node, 0));
+                       else
+                               remote_lid_ho = cl_ntoh16(osm_node_get_base_lid(
+                                       p_remote_physp->p_node,
+                                       
osm_physp_get_port_num(p_remote_physp)));
+
+                       if (!p_cache_sw ||
+                           port_num >= p_cache_sw->num_ports ||
+                           !p_cache_sw->ports[port_num].remote_lid_ho) {
+                               /*
+                                * There is some uncached change on the port.
+                                * In general, the reasons might be as follows:
+                                *  - switch reset
+                                *  - port reset (or port down/up)
+                                *  - quick connection location change
+                                *  - new link (or new switch)
+                                *
+                                * First two reasons allow cache usage, while
+                                * the last two reasons should invalidate cache.
+                                *
+                                * In case of quick connection location change,
+                                * cache would have been invalidated by
+                                * osm_ucast_cache_check_new_link() function.
+                                *
+                                * In case of new link between two known nodes,
+                                * cache also would have been invalidated by
+                                * osm_ucast_cache_check_new_link() function.
+                                *
+                                * Another reason is cached link between two
+                                * known switches went back. In this case the
+                                * osm_ucast_cache_check_new_link() function 
would
+                                * clear both sides of the link from the cache
+                                * during the discovery process, so effectively
+                                * this would be equivalent to port reset.
+                                *
+                                * So three possible reasons remain:
+                                *  - switch reset
+                                *  - port reset (or port down/up)
+                                *  - link of a new switch
+                                *
+                                * To validate cache, we need to check only the
+                                * third reason - link of a new node/switch:
+                                *  - If this is the local switch that is new,
+                                *    then it should have (p_sw->need_update == 
2).
+                                *  - If the remote node is switch and it's new,
+                                *    then it also should have
+                                *    (p_sw->need_update == 2).
+                                *  - If the remote node is CA/RTR and it's new,
+                                *    then its port should have is_new flag on.
+                                */
+                               if (p_sw->need_update == 2) {
+                                       OSM_LOG(p_cache->p_ucast_mgr->p_log, 
OSM_LOG_INFO,
+                                               "New switch found (lid %u) - "
+                                               "cache is invalid\n",
+                                               lid_ho);
+                                       osm_ucast_cache_invalidate(p_cache);
+                                       goto Exit;
+                               }
+
+                               if (remote_node_type == IB_NODE_TYPE_SWITCH) {
+
+                                       p_remote_sw = 
p_remote_physp->p_node->sw;
+                                        if (p_remote_sw->need_update == 2) {
+                                               /* this could also be case of
+                                                  switch coming back with an
+                                                  additional link that it
+                                                  didn't have before */
+                                               
OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_INFO,
+                                                       "New switch/link found 
(lid %u) - "
+                                                       "cache is invalid\n",
+                                                      remote_lid_ho);
+                                               
osm_ucast_cache_invalidate(p_cache);
+                                               goto Exit;
+                                       }
+                               }
+                               else {
+                                       /*
+                                        * Remote node is CA/RTR.
+                                        * Get p_port of the remote node and
+                                        * check its p_port->is_new flag.
+                                        */
+                                       p_remote_port = osm_get_port_by_guid(
+                                               p_cache->p_ucast_mgr->p_subn,
+                                               
osm_physp_get_port_guid(p_remote_physp));
+                                       if (p_remote_port->is_new) {
+                                               
OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_INFO,
+                                                       "New CA/RTR found (lid 
%u) - "
+                                                       "cache is invalid\n",
+                                                       remote_lid_ho);
+                                               
osm_ucast_cache_invalidate(p_cache);
+                                               goto Exit;
+                                       }
+                               }
+                       }
+                       else {
+                               /*
+                                * The change on the port is cached.
+                                * In general, the reasons might be as follows:
+                                *  - link between two known nodes went back
+                                *  - one or more nodes went back, causing all
+                                *    the links to reappear
+                                *
+                                * If it was link that went back, then this case
+                                * would have been taken care of during the
+                                * discovery by 
osm_ucast_cache_check_new_link(),
+                                * so it's some node that went back.
+                                */
+                               if ((p_cache_sw->ports[port_num].is_leaf &&
+                                    remote_node_type == IB_NODE_TYPE_SWITCH) ||
+                                   (!p_cache_sw->ports[port_num].is_leaf &&
+                                    remote_node_type != IB_NODE_TYPE_SWITCH)) {
+                                       OSM_LOG(p_cache->p_ucast_mgr->p_log, 
OSM_LOG_INFO,
+                                               "Remote node type change on 
switch lid %u, port %u - "
+                                               "cache is invalid\n",
+                                               lid_ho, port_num);
+                                       osm_ucast_cache_invalidate(p_cache);
+                                       goto Exit;
+                               }
+
+                               if (p_cache_sw->ports[port_num].remote_lid_ho !=
+                                   remote_lid_ho) {
+                                       OSM_LOG(p_cache->p_ucast_mgr->p_log, 
OSM_LOG_INFO,
+                                               "Remote lid change on switch 
lid %u, port %u"
+                                               "(was %u, now %u) - cache is 
invalid\n",
+                                               lid_ho, port_num,
+                                               
p_cache_sw->ports[port_num].remote_lid_ho,
+                                               remote_lid_ho);
+                                       osm_ucast_cache_invalidate(p_cache);
+                                       goto Exit;
+                               }
+
+                               /*
+                                * We don't care who is the node that has
+                                * reappeared in the subnet (local or remote).
+                                * What's important that the cached link matches
+                                * the real fabrics link.
+                                * Just clean it from cache.
+                                */
+
+                               p_cache_sw->ports[port_num].remote_lid_ho = 0;
+                               p_cache_sw->ports[port_num].is_leaf = FALSE;
+                               if (p_cache_sw->dropped) {
+                                       __cache_restore_ucast_info(
+                                               p_cache, p_cache_sw, p_sw);
+                                       p_cache_sw->dropped = FALSE;
+                               }
+
+                               OSM_LOG(p_cache->p_ucast_mgr->p_log, 
OSM_LOG_VERBOSE,
+                                       "Restored link from cache: lid %u, port 
%u to lid %u\n",
+                                       lid_ho, port_num, remote_lid_ho);
+                       }
+               }
+       }
+
+       /* Remove all the cached switches that
+          have all their ports restored */
+       __cache_cleanup_switches(p_cache);
+
+       /*
+        * Done scanning all the physical switch ports in the subnet.
+        * Now we need to check the other side:
+        * Scan all the cached switches and their ports:
+        *  - If the cached switch is missing in the subnet
+        *    (dropped flag is on), check that it's a leaf switch.
+        *    If it's not a leaf, the cache is invalid, because
+        *    cache can tolerate only leaf switch removal.
+        *  - If the cached switch exists in fabric, check all
+        *    its cached ports. These cached ports represent
+        *    missing link in the fabric.
+        *    The missing links that can be tolerated are:
+        *      + link to missing CA/RTR
+        *      + link to missing leaf switch
+        */
+       for (p_cache_sw = (cache_switch_t *) cl_qmap_head(&p_cache->sw_tbl);
+            p_cache_sw != (cache_switch_t *) cl_qmap_end(&p_cache->sw_tbl);
+            p_cache_sw = (cache_switch_t *) 
cl_qmap_next(&p_cache_sw->map_item)) {
+
+               if (p_cache_sw->dropped) {
+                       if (!__cache_sw_is_leaf(p_cache_sw)){
+                               OSM_LOG(p_cache->p_ucast_mgr->p_log, 
OSM_LOG_INFO,
+                                       "Missing non-leaf switch (lid %u) - "
+                                       "cache is invalid\n",
+                                       __cache_sw_get_base_lid_ho(p_cache_sw));
+                               osm_ucast_cache_invalidate(p_cache);
+                               goto Exit;
+                       }
+
+                       OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_VERBOSE,
+                               "Missing leaf switch (lid %u) - "
+                               "continuing validation\n",
+                               __cache_sw_get_base_lid_ho(p_cache_sw));
+                       continue;
+               }
+
+               for (port_num = 1; port_num < p_cache_sw->num_ports; 
port_num++) {
+                       if (!p_cache_sw->ports[port_num].remote_lid_ho)
+                               continue;
+
+                       if (p_cache_sw->ports[port_num].is_leaf){
+                               CL_ASSERT(__cache_sw_is_leaf(p_cache_sw));
+                               OSM_LOG(p_cache->p_ucast_mgr->p_log, 
OSM_LOG_VERBOSE,
+                                       "Switch lid %u, port %u: missing link 
to CA/RTR - "
+                                       "continuing validation\n",
+                                       __cache_sw_get_base_lid_ho(p_cache_sw), 
port_num);
+                               continue;
+                       }
+
+                       p_remote_cache_sw = __cache_get_sw(p_cache,
+                               p_cache_sw->ports[port_num].remote_lid_ho);
+
+                       if (!p_remote_cache_sw || !p_remote_cache_sw->dropped) {
+                               OSM_LOG(p_cache->p_ucast_mgr->p_log, 
OSM_LOG_INFO,
+                                       "Switch lid %u, port %u: missing link 
to existing switch - "
+                                       "cache is invalid\n",
+                                       __cache_sw_get_base_lid_ho(p_cache_sw), 
port_num);
+                               osm_ucast_cache_invalidate(p_cache);
+                               goto Exit;
+                       }
+
+                       if (!__cache_sw_is_leaf(p_remote_cache_sw)) {
+                               OSM_LOG(p_cache->p_ucast_mgr->p_log, 
OSM_LOG_INFO,
+                                       "Switch lid %u, port %u: missing link 
to non-leaf switch - "
+                                       "cache is invalid\n",
+                                       __cache_sw_get_base_lid_ho(p_cache_sw), 
port_num);
+                               osm_ucast_cache_invalidate(p_cache);
+                               goto Exit;
+                       }
+
+                       /*
+                        * At this point we know that the missing link is to
+                        * a leaf switch. However, one case deserves a special
+                        * treatment. If there was a link between two leaf
+                        * switches, then missing leaf switch might break
+                        * routing. It is possible that there are routes
+                        * that use leaf switches to get from switch to switch
+                        * and not just to get to the CAs behind the leaf 
switch.
+                        */
+                       if (__cache_sw_is_leaf(p_cache_sw) &&
+                           __cache_sw_is_leaf(p_remote_cache_sw)) {
+                               OSM_LOG(p_cache->p_ucast_mgr->p_log, 
OSM_LOG_INFO,
+                                       "Switch lid %u, port %u: missing 
leaf-2-leaf link - "
+                                       "cache is invalid\n",
+                                       __cache_sw_get_base_lid_ho(p_cache_sw), 
port_num);
+                               osm_ucast_cache_invalidate(p_cache);
+                               goto Exit;
+                       }
+
+                       OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_VERBOSE,
+                               "Switch lid %u, port %u: missing remote leaf 
switch - "
+                               "continuing validation\n",
+                               __cache_sw_get_base_lid_ho(p_cache_sw), 
port_num);
+               }
+       }
+
+       OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_VERBOSE,
+               "Unicast cache is valid\n");
+       __ucast_cache_dump(p_cache);
+Exit:
+       OSM_LOG_EXIT(p_cache->p_ucast_mgr->p_log);
+} /* osm_ucast_cache_validate() */
+
+/**********************************************************************
+ **********************************************************************/
+
+void
+osm_ucast_cache_check_new_link(osm_ucast_cache_t * p_cache,
+                              osm_node_t * p_node_1,
+                              uint8_t port_num_1,
+                              osm_node_t * p_node_2,
+                              uint8_t port_num_2)
+{
+       uint16_t lid_ho_1;
+       uint16_t lid_ho_2;
+
+       OSM_LOG_ENTER(p_cache->p_ucast_mgr->p_log);
+
+       if (!p_cache->valid)
+               goto Exit;
+
+       __cache_check_link_change(p_cache,
+                                 osm_node_get_physp_ptr(p_node_1, port_num_1),
+                                 osm_node_get_physp_ptr(p_node_2, port_num_2));
+
+       if (!p_cache->valid)
+               goto Exit;
+
+       if (osm_node_get_type(p_node_1) != IB_NODE_TYPE_SWITCH &&
+           osm_node_get_type(p_node_2) != IB_NODE_TYPE_SWITCH) {
+               OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_INFO,
+                       "Found CA/RTR-2-CA/RTR link - cache is invalid\n");
+               osm_ucast_cache_invalidate(p_cache);
+               goto Exit;
+       }
+
+       /* for code simplicity, we want the first node to be switch */
+       if (osm_node_get_type(p_node_1) != IB_NODE_TYPE_SWITCH) {
+               osm_node_t * tmp_node = p_node_1;
+               uint8_t tmp_port_num = port_num_1;
+               p_node_1 = p_node_2;
+               port_num_1 = port_num_2;
+               p_node_2 = tmp_node;
+               port_num_2 = tmp_port_num;
+       }
+
+       lid_ho_1 = cl_ntoh16(osm_node_get_base_lid(p_node_1, 0));
+
+       if (osm_node_get_type(p_node_2) == IB_NODE_TYPE_SWITCH)
+               lid_ho_2 = cl_ntoh16(
+                       osm_node_get_base_lid(p_node_2, 0));
+       else
+               lid_ho_2 = cl_ntoh16(
+                       osm_node_get_base_lid(p_node_2, port_num_2));
+
+       if (!lid_ho_1 || !lid_ho_2) {
+               /*
+                * No lid assigned, which means that one of the nodes is new.
+                * Need to wait for lid manager to process this node.
+                * The switches and their links will be checked later when
+                * the whole cache validity will be verified.
+                */
+               OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_VERBOSE,
+                       "Link port %u <-> %u reveals new node - cache will "
+                       "be validated later\n",
+                       port_num_1, port_num_2);
+               goto Exit;
+       }
+
+       __cache_remove_port(p_cache, lid_ho_1, port_num_1, lid_ho_2,
+                   (osm_node_get_type(p_node_2) != IB_NODE_TYPE_SWITCH));
+
+       /* if node_2 is a switch, the link should be cleaned from its cache */
+
+       if (osm_node_get_type(p_node_2) == IB_NODE_TYPE_SWITCH)
+               __cache_remove_port(p_cache, lid_ho_2,
+                                   port_num_2, lid_ho_1, FALSE);
+
+Exit:
+       OSM_LOG_EXIT(p_cache->p_ucast_mgr->p_log);
+} /* osm_ucast_cache_check_new_link() */
+
+/**********************************************************************
+ **********************************************************************/
+
+void
+osm_ucast_cache_add_link(osm_ucast_cache_t * p_cache,
+                        osm_node_t * p_node_1,
+                        uint8_t port_num_1,
+                        osm_node_t * p_node_2,
+                        uint8_t port_num_2)
+{
+       uint16_t lid_ho_1;
+       uint16_t lid_ho_2;
+
+       OSM_LOG_ENTER(p_cache->p_ucast_mgr->p_log);
+
+       if (!p_cache->valid)
+               goto Exit;
+
+       if (osm_node_get_type(p_node_1) != IB_NODE_TYPE_SWITCH &&
+           osm_node_get_type(p_node_2) != IB_NODE_TYPE_SWITCH) {
+               OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_INFO,
+                       "Dropping CA-2-CA link - cache invalid\n");
+               osm_ucast_cache_invalidate(p_cache);
+               goto Exit;
+       }
+
+       if (((osm_node_get_type(p_node_1) == IB_NODE_TYPE_SWITCH) &&
+            (!osm_node_get_physp_ptr(p_node_1, 0) ||
+             !osm_physp_is_valid(osm_node_get_physp_ptr(p_node_1, 0)))) ||
+           ((osm_node_get_type(p_node_2) == IB_NODE_TYPE_SWITCH) &&
+            (!osm_node_get_physp_ptr(p_node_2, 0) ||
+             !osm_physp_is_valid(osm_node_get_physp_ptr(p_node_2, 0))))) {
+               /* we're caching a link when one of the nodes
+                  has already been dropped and cached */
+               OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_VERBOSE,
+                       "Port %u <-> port %u: port0 on one of the nodes"
+                       "has already been dropped and cached\n",
+                       port_num_1, port_num_2);
+               goto Exit;
+       }
+
+       /* One of the nodes is switch. Just for code
+          simplicity, make sure that it's the first node. */
+
+       if (osm_node_get_type(p_node_1) != IB_NODE_TYPE_SWITCH) {
+               osm_node_t * tmp_node = p_node_1;
+               uint8_t tmp_port_num = port_num_1;
+               p_node_1 = p_node_2;
+               port_num_1 = port_num_2;
+               p_node_2 = tmp_node;
+               port_num_2 = tmp_port_num;
+       }
+
+       if (!p_node_1->sw) {
+               /* something is wrong - we'd better not use cache */
+               osm_ucast_cache_invalidate(p_cache);
+               goto Exit;
+       }
+
+       lid_ho_1 = cl_ntoh16(osm_node_get_base_lid(p_node_1,0));
+
+       if (osm_node_get_type(p_node_2) == IB_NODE_TYPE_SWITCH) {
+
+               if (!p_node_2->sw) {
+                       /* something is wrong - we'd better not use cache */
+                       osm_ucast_cache_invalidate(p_cache);
+                       goto Exit;
+               }
+
+               lid_ho_2 = cl_ntoh16(osm_node_get_base_lid(p_node_2,0));
+
+               /* lost switch-2-switch link - cache both sides */
+               __cache_add_port(p_cache, lid_ho_1, port_num_1,
+                                lid_ho_2, FALSE);
+               __cache_add_port(p_cache, lid_ho_2, port_num_2,
+                                lid_ho_1, FALSE);
+       }
+       else {
+               lid_ho_2 = cl_ntoh16(
+                       osm_node_get_base_lid(p_node_2, port_num_2));
+
+               /* lost link to CA/RTR - cache only switch side */
+               __cache_add_port(p_cache, lid_ho_1, port_num_1,
+                                lid_ho_2, TRUE);
+       }
+
+Exit:
+       OSM_LOG_EXIT(p_cache->p_ucast_mgr->p_log);
+} /* osm_ucast_cache_add_link() */
+
+/**********************************************************************
+ **********************************************************************/
+
+void
+osm_ucast_cache_add_node(osm_ucast_cache_t * p_cache,
+                        osm_node_t * p_node)
+{
+       uint16_t lid_ho;
+       uint8_t max_ports;
+       uint8_t port_num;
+       osm_physp_t * p_physp;
+       osm_node_t * p_remote_node;
+       cache_switch_t * p_cache_sw;
+
+       OSM_LOG_ENTER(p_cache->p_ucast_mgr->p_log);
+
+       if (!p_cache->valid)
+               goto Exit;
+
+       if (osm_node_get_type(p_node) == IB_NODE_TYPE_SWITCH) {
+
+               lid_ho = cl_ntoh16(osm_node_get_base_lid(p_node,0));
+
+               OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_VERBOSE,
+                       "Caching dropped switch lid %u\n", lid_ho);
+
+               if (!p_node->sw) {
+                       /* something is wrong - forget about cache */
+                       OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_ERROR,
+                               "ERR AD02: no switch info for node lid %u -"
+                               " clearing cache\n", lid_ho);
+                       osm_ucast_cache_invalidate(p_cache);
+                       goto Exit;
+               }
+
+               /* unlink (add to cache) all the ports of this switch */
+               max_ports = osm_node_get_num_physp(p_node);
+               for (port_num = 1; port_num < max_ports; port_num++) {
+
+                       p_physp = osm_node_get_physp_ptr(p_node, port_num);
+                       if (!p_physp || !p_physp->p_node ||
+                           !p_physp->p_remote_physp ||
+                           !p_physp->p_remote_physp->p_node)
+                               continue;
+
+                       osm_ucast_cache_add_link(p_cache, p_node, port_num,
+                                                
p_physp->p_remote_physp->p_node,
+                                                
p_physp->p_remote_physp->port_num);
+               }
+
+               /*
+                * All the ports have been dropped (cached).
+                * If one of the ports was connected to CA/RTR,
+                * then the cached switch would be marked as leaf.
+                * If it isn't, then the dropped switch isn't a leaf,
+                * and cache can't handle it.
+                */
+
+               p_cache_sw = __cache_get_sw(p_cache, lid_ho);
+               CL_ASSERT(p_cache_sw);
+
+               if (!__cache_sw_is_leaf(p_cache_sw)) {
+                       OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_INFO,
+                               "Dropped non-leaf switch (lid %u) - "
+                               "cache is invalid\n", lid_ho);
+                       osm_ucast_cache_invalidate(p_cache);
+                       goto Exit;
+               }
+
+               p_cache_sw->dropped = TRUE;
+
+               if (!p_node->sw->num_hops || !p_node->sw->hops) {
+                       OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_INFO,
+                               "No LID matrices for switch lid %u - "
+                               "cache is invalid\n", lid_ho);
+                       osm_ucast_cache_invalidate(p_cache);
+                       goto Exit;
+               }
+
+               /* lid matrices */
+
+               p_cache_sw->num_hops = p_node->sw->num_hops;
+               p_node->sw->num_hops = 0;
+               p_cache_sw->hops = p_node->sw->hops;
+               p_node->sw->hops = NULL;
+
+               /* linear forwarding table */
+
+               p_cache_sw->lft = p_node->sw->lft_buf;
+               p_node->sw->lft_buf = NULL;
+               p_cache_sw->max_lid_ho = p_node->sw->max_lid_ho;
+       }
+       else {
+               /* dropping CA/RTR: add to cache all the ports of this switch */
+               max_ports = osm_node_get_num_physp(p_node);
+               for (port_num = 0; port_num < max_ports; port_num++) {
+
+                       p_physp = osm_node_get_physp_ptr(p_node, port_num);
+                       if (!p_physp || !p_physp->p_node ||
+                           !p_physp->p_remote_physp ||
+                           !p_physp->p_remote_physp->p_node)
+                               continue;
+
+                       p_remote_node = p_physp->p_remote_physp->p_node;
+                       if (osm_node_get_type(p_remote_node) !=
+                           IB_NODE_TYPE_SWITCH) {
+                               /* CA/RTR to CA/RTR connection */
+                               OSM_LOG(p_cache->p_ucast_mgr->p_log, 
OSM_LOG_INFO,
+                                       "Dropping CA/RTR to CA/RTR connection - 
"
+                                       "cache is invalid\n");
+                               osm_ucast_cache_invalidate(p_cache);
+                               goto Exit;
+                       }
+
+                       osm_ucast_cache_add_link(p_cache, p_remote_node,
+                                                
p_physp->p_remote_physp->port_num,
+                                                p_node, port_num);
+               }
+       }
+Exit:
+       OSM_LOG_EXIT(p_cache->p_ucast_mgr->p_log);
+} /* osm_ucast_cache_add_node() */
+
+/**********************************************************************
+ **********************************************************************/
+
+void
+osm_ucast_cache_apply(osm_ucast_cache_t * p_cache)
+{
+       osm_subn_t * p_subn = p_cache->p_ucast_mgr->p_subn;
+       osm_switch_t *p_sw;
+
+       OSM_LOG_ENTER(p_cache->p_ucast_mgr->p_log);
+       OSM_LOG(p_cache->p_ucast_mgr->p_log, OSM_LOG_INFO,
+               "Applying unicast cache\n");
+       CL_ASSERT(p_cache && p_cache->p_ucast_mgr && p_cache->valid);
+
+       for (p_sw = (osm_switch_t *) cl_qmap_head(&p_subn->sw_guid_tbl);
+            p_sw != (osm_switch_t *) cl_qmap_end(&p_subn->sw_guid_tbl);
+            p_sw = (osm_switch_t *) cl_qmap_next(&p_sw->map_item))
+               osm_ucast_mgr_set_fwd_table(p_cache->p_ucast_mgr, p_sw);
+
+       OSM_LOG_EXIT(p_cache->p_ucast_mgr->p_log);
+}
+
+/**********************************************************************
+ **********************************************************************/
-- 
1.5.1.4


_______________________________________________
general mailing list
[email protected]
http://lists.openfabrics.org/cgi-bin/mailman/listinfo/general

To unsubscribe, please visit http://openib.org/mailman/listinfo/openib-general

Reply via email to