+/*
+ * Do a health check on the message:
+ * return -1 if we're not going to handle the error or
+ * if we've reached the maximum number of retries.
+ * success case will return -1 as well
+ * return 0 if it the message is requeued for send
+ */
+static int
+lnet_health_check(struct lnet_msg *msg)
+{
+ enum lnet_msg_hstatus hstatus = msg->msg_health_status;
+ struct lnet_peer_ni *lpni;
+ struct lnet_ni *ni;
+ bool lo = false;
+
+ /* if we're shutting down no point in handling health. */
+ if (the_lnet.ln_mt_state != LNET_MT_STATE_RUNNING)
+ return -1;
+
+ LASSERT(msg->msg_tx_committed || msg->msg_rx_committed);
+
+ /*
+ * if we're sending to the LOLND then the msg_txpeer will not be
+ * set. So no need to sanity check it.
+ */
+ if (msg->msg_tx_committed &&
+ LNET_NETTYP(LNET_NIDNET(msg->msg_txni->ni_nid)) != LOLND)
+ LASSERT(msg->msg_txpeer);
+ else if (msg->msg_tx_committed &&
+ LNET_NETTYP(LNET_NIDNET(msg->msg_txni->ni_nid)) == LOLND)
+ lo = true;
+
+ if (hstatus != LNET_MSG_STATUS_OK &&
+ ktime_compare(ktime_get(), msg->msg_deadline) >= 0)
+ return -1;
+
+ /*
+ * stats are only incremented for errors so avoid wasting time
+ * incrementing statistics if there is no error.
+ */
+ if (hstatus != LNET_MSG_STATUS_OK) {
+ lnet_net_lock(0);
+ lnet_incr_hstats(msg, hstatus);
+ lnet_net_unlock(0);
+ }
+
+ /*
+ * always prefer txni/txpeer if they message is committed for both
+ * directions.
+ */
+ if (msg->msg_tx_committed) {
+ ni = msg->msg_txni;
+ lpni = msg->msg_txpeer;
+ } else {
+ ni = msg->msg_rxni;
+ lpni = msg->msg_rxpeer;
+ }
+
+ if (!lo)
+ LASSERT(ni && lpni);
+ else
+ LASSERT(ni);
+
+ CDEBUG(D_NET, "health check: %s->%s: %s: %s\n",
+ libcfs_nid2str(ni->ni_nid),
+ (lo) ? "self" : libcfs_nid2str(lpni->lpni_nid),
+ lnet_msgtyp2str(msg->msg_type),
+ lnet_health_error2str(hstatus));
+
+ switch (hstatus) {
+ case LNET_MSG_STATUS_OK:
+ /*
+ * increment the local ni health weather we successfully
+ * received or sent a message on it.
+ */
+ lnet_inc_healthv(&ni->ni_healthv);
+ /*
+ * It's possible msg_txpeer is NULL in the LOLND
+ * case. Only increment the peer's health if we're
+ * receiving a message from it. It's the only sure way to
+ * know that a remote interface is up.
+ * If this interface is part of a router, then take that
+ * as indication that the router is fully healthy.
+ */
+ if (lpni && msg->msg_rx_committed) {
+ /*
+ * If we're receiving a message from the router or
+ * I'm a router, then set that lpni's health to
+ * maximum so we can commence communication
+ */
+ if (lnet_isrouter(lpni) || the_lnet.ln_routing)
+ lnet_set_healthv(&lpni->lpni_healthv,
+ LNET_MAX_HEALTH_VALUE);
+ else
+ lnet_inc_healthv(&lpni->lpni_healthv);
+ }
+
+ /* we can finalize this message */
+ return -1;
+ case LNET_MSG_STATUS_LOCAL_INTERRUPT:
+ case LNET_MSG_STATUS_LOCAL_DROPPED:
+ case LNET_MSG_STATUS_LOCAL_ABORTED:
+ case LNET_MSG_STATUS_LOCAL_NO_ROUTE:
+ case LNET_MSG_STATUS_LOCAL_TIMEOUT:
+ lnet_handle_local_failure(ni);
+ if (msg->msg_tx_committed)
+ /* add to the re-send queue */
+ return lnet_attempt_msg_resend(msg);
+ break;
+
+ /*
+ * These errors will not trigger a resend so simply
+ * finalize the message
+ */
+ case LNET_MSG_STATUS_LOCAL_ERROR:
+ lnet_handle_local_failure(ni);
+ return -1;
+
+ /*
+ * TODO: since the remote dropped the message we can
+ * attempt a resend safely.
+ */
+ case LNET_MSG_STATUS_REMOTE_DROPPED:
+ lnet_handle_remote_failure(lpni);
+ if (msg->msg_tx_committed)
+ return lnet_attempt_msg_resend(msg);
+ break;
+
+ case LNET_MSG_STATUS_REMOTE_ERROR:
+ case LNET_MSG_STATUS_REMOTE_TIMEOUT:
+ case LNET_MSG_STATUS_NETWORK_TIMEOUT:
+ lnet_handle_remote_failure(lpni);
+ return -1;
+ default:
+ LBUG();
+ }
+
+ /* no resend is needed */
+ return -1;
+}
+