+ struct list_head *route_entry;
+ struct lnet_remotenet *rnet;
+ struct lnet_remotenet *rnet2;
+ struct lnet_route *route;
+ struct lnet_peer_ni *lpni;
+ struct lnet_peer *gw;
+ int add_route;
+ int rc;
+
+ CDEBUG(D_NET, "Add route: remote net %s hops %d priority %u gw %s\n",
+ libcfs_net2str(net), hops, priority, libcfs_nid2str(gateway));
+
+ if (gateway == LNET_NID_ANY ||
+ gateway == LNET_NID_LO_0 ||
+ net == LNET_NET_ANY ||
+ LNET_NETTYP(net) == LOLND ||
+ LNET_NIDNET(gateway) == net ||
+ (hops != LNET_UNDEFINED_HOPS && (hops < 1 || hops > 255)))
+ return -EINVAL;
+
+ /* it's a local network */
+ if (lnet_islocalnet(net))
+ return -EEXIST;
+
+ if (!lnet_islocalnet(LNET_NIDNET(gateway))) {
+ CERROR("Cannot add route with gateway %s. There is no local interface configured on LNet %s\n",
+ libcfs_nid2str(gateway),
+ libcfs_net2str(LNET_NIDNET(gateway)));
+ return -EHOSTUNREACH;
+ }
+
+ /* Assume net, route, all new */
+ LIBCFS_ALLOC(route, sizeof(*route));
+ LIBCFS_ALLOC(rnet, sizeof(*rnet));
+ if (route == NULL || rnet == NULL) {
+ CERROR("Out of memory creating route %s %d %s\n",
+ libcfs_net2str(net), hops, libcfs_nid2str(gateway));
+ if (route != NULL)
+ LIBCFS_FREE(route, sizeof(*route));
+ if (rnet != NULL)
+ LIBCFS_FREE(rnet, sizeof(*rnet));
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&rnet->lrn_routes);
+ rnet->lrn_net = net;
+ /* store the local and remote net that the route represents */
+ route->lr_lnet = LNET_NIDNET(gateway);
+ route->lr_net = net;
+ route->lr_nid = gateway;
+ route->lr_priority = priority;
+ route->lr_hops = hops;
+ if (lnet_peers_start_down())
+ atomic_set(&route->lr_alive, 0);
+ else
+ atomic_set(&route->lr_alive, 1);
+
+ lnet_net_lock(LNET_LOCK_EX);
+
+ /*
+ * lnet_nid2peerni_ex() grabs a ref on the lpni. We will need to
+ * lose that once we're done
+ */
+ lpni = lnet_nid2peerni_ex(gateway, LNET_LOCK_EX);
+ if (IS_ERR(lpni)) {
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ LIBCFS_FREE(route, sizeof(*route));
+ LIBCFS_FREE(rnet, sizeof(*rnet));
+
+ rc = PTR_ERR(lpni);
+ CERROR("Error %d creating route %s %d %s\n", rc,
+ libcfs_net2str(net), hops,
+ libcfs_nid2str(gateway));
+ return rc;
+ }
+
+ LASSERT(lpni->lpni_peer_net && lpni->lpni_peer_net->lpn_peer);
+ gw = lpni->lpni_peer_net->lpn_peer;
+
+ route->lr_gateway = gw;
+
+ rnet2 = lnet_find_rnet_locked(net);
+ if (rnet2 == NULL) {
+ /* new network */
+ list_add_tail(&rnet->lrn_list, lnet_net2rnethash(net));
+ rnet2 = rnet;
+ }
+
+ /* Search for a duplicate route (it's a NOOP if it is) */
+ add_route = 1;
+ list_for_each(route_entry, &rnet2->lrn_routes) {
+ struct lnet_route *route2;
+
+ route2 = list_entry(route_entry, struct lnet_route, lr_list);
+ if (route2->lr_gateway == route->lr_gateway) {
+ add_route = 0;
+ break;
+ }
+
+ /* our lookups must be true */
+ LASSERT(route2->lr_gateway->lp_primary_nid != gateway);
+ }
+
+ /*
+ * It is possible to add multiple routes through the same peer,
+ * but it'll be using a different NID of that peer. When the
+ * gateway is discovered, discovery will consolidate the different
+ * peers into one peer. In this case the discovery code will have
+ * to move the routes from the peer that's being deleted to the
+ * consolidated peer lp_routes list
+ */
+ if (add_route) {
+ gw->lp_health_sensitivity = sensitivity;
+ lnet_add_route_to_rnet(rnet2, route);
+ if (lnet_peer_discovery_disabled)
+ CWARN("Consider turning discovery on to enable full "
+ "Multi-Rail routing functionality\n");
+ }
+
+ /*
+ * get rid of the reference on the lpni.
+ */
+ lnet_peer_ni_decref_locked(lpni);
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ rc = 0;
+
+ if (!add_route) {
+ rc = -EEXIST;
+ LIBCFS_FREE(route, sizeof(*route));
+ }
+
+ if (rnet != rnet2)
+ LIBCFS_FREE(rnet, sizeof(*rnet));
+
+ /* kick start the monitor thread to handle the added route */
+ complete(&the_lnet.ln_mt_wait_complete);
+
+ return rc;