+/* Must hold net_lock/EX */
+static inline void
+lnet_set_route_aliveness(struct lnet_route *route, bool alive)
+{
+ /* Log when there's a state change */
+ if (route->lr_alive != alive) {
+ CERROR("route to %s through %s has gone from %s to %s\n",
+ libcfs_net2str(route->lr_net),
+ libcfs_nid2str(route->lr_gateway->lp_primary_nid),
+ (route->lr_alive) ? "up" : "down",
+ alive ? "up" : "down");
+ route->lr_alive = alive;
+ }
+}
+
+/* Must hold net_lock/EX */
+void
+lnet_router_discovery_ping_reply(struct lnet_peer *lp)
+{
+ struct lnet_ping_buffer *pbuf = lp->lp_data;
+ struct lnet_peer_net *llpn;
+ struct lnet_route *route;
+ bool single_hop = false;
+ bool net_up = false;
+ unsigned lp_state;
+ __u32 net;
+ int i;
+
+
+ spin_lock(&lp->lp_lock);
+ lp_state = lp->lp_state;
+
+ /* only handle replies if discovery is disabled. */
+ if (!lnet_is_discovery_disabled_locked(lp)) {
+ spin_unlock(&lp->lp_lock);
+ return;
+ }
+
+ spin_unlock(&lp->lp_lock);
+
+ if (lp_state & LNET_PEER_PING_FAILED ||
+ pbuf->pb_info.pi_features & LNET_PING_FEAT_RTE_DISABLED) {
+ CDEBUG(D_NET, "Set routes down for gw %s because %s %d\n",
+ libcfs_nid2str(lp->lp_primary_nid),
+ lp_state & LNET_PEER_PING_FAILED ? "ping failed" :
+ "route feature is disabled", lp->lp_ping_error);
+ /* If the ping failed or the peer has routing disabled then
+ * mark the routes served by this peer down
+ */
+ list_for_each_entry(route, &lp->lp_routes, lr_gwlist)
+ lnet_set_route_aliveness(route, false);
+ return;
+ }
+
+ CDEBUG(D_NET, "Discovery is disabled. Processing reply for gw: %s:%d\n",
+ libcfs_nid2str(lp->lp_primary_nid), pbuf->pb_info.pi_nnis);
+
+ /*
+ * examine the ping response to determine if the routes on that
+ * gateway should be declared alive.
+ * The route is alive if:
+ * 1. local network to reach the route is alive and
+ * 2. route is single hop, avoid_async_router_failure is set and
+ * there exists at least one NI on the route's remote net
+ */
+ list_for_each_entry(route, &lp->lp_routes, lr_gwlist) {
+ llpn = lnet_peer_get_net_locked(lp, route->lr_lnet);
+ if (!llpn) {
+ lnet_set_route_aliveness(route, false);
+ continue;
+ }
+
+ if (!lnet_is_gateway_net_alive(llpn)) {
+ lnet_set_route_aliveness(route, false);
+ continue;
+ }
+
+ single_hop = net_up = false;
+ for (i = 1; i < pbuf->pb_info.pi_nnis; i++) {
+ net = LNET_NIDNET(pbuf->pb_info.pi_ni[i].ns_nid);
+
+ if (route->lr_net == net) {
+ single_hop = true;
+ if (pbuf->pb_info.pi_ni[i].ns_status ==
+ LNET_NI_STATUS_UP) {
+ net_up = true;
+ break;
+ }
+ }
+ }
+
+ route->lr_single_hop = single_hop;
+ if (avoid_asym_router_failure &&
+ (route->lr_hops == 1 ||
+ route->lr_hops == LNET_UNDEFINED_HOPS))
+ lnet_set_route_aliveness(route, net_up);
+ else
+ lnet_set_route_aliveness(route, true);
+
+ /*
+ * warn that the route is configured as single-hop but it
+ * really is multi-hop as far as we can tell.
+ */
+ lnet_check_route_inconsistency(route);
+ }