+static struct lnet_peer_ni *
+lnet_select_peer_ni(struct lnet_ni *best_ni, lnet_nid_t dst_nid,
+ struct lnet_peer *peer,
+ struct lnet_peer_ni *best_lpni,
+ struct lnet_peer_net *peer_net)
+{
+ /*
+ * Look at the peer NIs for the destination peer that connect
+ * to the chosen net. If a peer_ni is preferred when using the
+ * best_ni to communicate, we use that one. If there is no
+ * preferred peer_ni, or there are multiple preferred peer_ni,
+ * the available transmit credits are used. If the transmit
+ * credits are equal, we round-robin over the peer_ni.
+ */
+ struct lnet_peer_ni *lpni = NULL;
+ int best_lpni_credits = (best_lpni) ? best_lpni->lpni_txcredits :
+ INT_MIN;
+ int best_lpni_healthv = (best_lpni) ?
+ atomic_read(&best_lpni->lpni_healthv) : 0;
+ bool best_lpni_is_preferred = false;
+ bool lpni_is_preferred;
+ int lpni_healthv;
+ __u32 lpni_sel_prio;
+ __u32 best_sel_prio = LNET_MAX_SELECTION_PRIORITY;
+
+ while ((lpni = lnet_get_next_peer_ni_locked(peer, peer_net, lpni))) {
+ /*
+ * if the best_ni we've chosen aleady has this lpni
+ * preferred, then let's use it
+ */
+ if (best_ni) {
+ /* FIXME need to handle large-addr nid */
+ lpni_is_preferred = lnet_peer_is_pref_nid_locked(
+ lpni, lnet_nid_to_nid4(&best_ni->ni_nid));
+ CDEBUG(D_NET, "%s lpni_is_preferred = %d\n",
+ libcfs_nidstr(&best_ni->ni_nid),
+ lpni_is_preferred);
+ } else {
+ lpni_is_preferred = false;
+ }
+
+ lpni_healthv = atomic_read(&lpni->lpni_healthv);
+ lpni_sel_prio = lpni->lpni_sel_priority;
+
+ if (best_lpni)
+ CDEBUG(D_NET, "n:[%s, %s] h:[%d, %d] p:[%d, %d] c:[%d, %d] s:[%d, %d]\n",
+ libcfs_nidstr(&lpni->lpni_nid),
+ libcfs_nidstr(&best_lpni->lpni_nid),
+ lpni_healthv, best_lpni_healthv,
+ lpni_sel_prio, best_sel_prio,
+ lpni->lpni_txcredits, best_lpni_credits,
+ lpni->lpni_seq, best_lpni->lpni_seq);
+ else
+ goto select_lpni;
+
+ /* pick the healthiest peer ni */
+ if (lpni_healthv < best_lpni_healthv)
+ continue;
+ else if (lpni_healthv > best_lpni_healthv) {
+ if (best_lpni_is_preferred)
+ best_lpni_is_preferred = false;
+ goto select_lpni;
+ }
+
+ if (lpni_sel_prio > best_sel_prio)
+ continue;
+ else if (lpni_sel_prio < best_sel_prio) {
+ if (best_lpni_is_preferred)
+ best_lpni_is_preferred = false;
+ goto select_lpni;
+ }
+
+ /* if this is a preferred peer use it */
+ if (!best_lpni_is_preferred && lpni_is_preferred) {
+ best_lpni_is_preferred = true;
+ goto select_lpni;
+ } else if (best_lpni_is_preferred && !lpni_is_preferred) {
+ /* this is not the preferred peer so let's ignore
+ * it.
+ */
+ continue;
+ }
+
+ if (lpni->lpni_txcredits < best_lpni_credits)
+ /* We already have a peer that has more credits
+ * available than this one. No need to consider
+ * this peer further.
+ */
+ continue;
+ else if (lpni->lpni_txcredits > best_lpni_credits)
+ goto select_lpni;
+
+ /* The best peer found so far and the current peer
+ * have the same number of available credits let's
+ * make sure to select between them using Round Robin
+ */
+ if (best_lpni && (best_lpni->lpni_seq <= lpni->lpni_seq))
+ continue;
+select_lpni:
+ best_lpni_is_preferred = lpni_is_preferred;
+ best_lpni_healthv = lpni_healthv;
+ best_sel_prio = lpni_sel_prio;
+ best_lpni = lpni;
+ best_lpni_credits = lpni->lpni_txcredits;
+ }
+
+ /* if we still can't find a peer ni then we can't reach it */
+ if (!best_lpni) {
+ __u32 net_id = (peer_net) ? peer_net->lpn_net_id :
+ LNET_NIDNET(dst_nid);
+ CDEBUG(D_NET, "no peer_ni found on peer net %s\n",
+ libcfs_net2str(net_id));
+ return NULL;
+ }
+
+ CDEBUG(D_NET, "sd_best_lpni = %s\n",
+ libcfs_nidstr(&best_lpni->lpni_nid));
+
+ return best_lpni;
+}
+
+/*
+ * Prerequisite: the best_ni should already be set in the sd
+ * Find the best lpni.
+ * If the net id is provided then restrict lpni selection on
+ * that particular net.
+ * Otherwise find any reachable lpni. When dealing with an MR
+ * gateway and it has multiple lpnis which we can use
+ * we want to select the best one from the list of reachable
+ * ones.
+ */
+static inline struct lnet_peer_ni *
+lnet_find_best_lpni(struct lnet_ni *lni, lnet_nid_t dst_nid,
+ struct lnet_peer *peer, __u32 net_id)
+{
+ struct lnet_peer_net *peer_net;
+
+ /* find the best_lpni on any local network */
+ if (net_id == LNET_NET_ANY) {
+ struct lnet_peer_ni *best_lpni = NULL;
+ struct lnet_peer_net *lpn;
+ list_for_each_entry(lpn, &peer->lp_peer_nets, lpn_peer_nets) {
+ /* no net specified find any reachable peer ni */
+ if (!lnet_islocalnet_locked(lpn->lpn_net_id))
+ continue;
+ best_lpni = lnet_select_peer_ni(lni, dst_nid, peer,
+ best_lpni, lpn);
+ }
+
+ return best_lpni;
+ }
+ /* restrict on the specified net */
+ peer_net = lnet_peer_get_net_locked(peer, net_id);
+ if (peer_net)
+ return lnet_select_peer_ni(lni, dst_nid, peer, NULL, peer_net);
+
+ return NULL;
+}
+