}
/**
+ * kfilnd_peer_del() - Mark a peer for deletion
+ * @kp: Peer to be deleted
+ */
+static void kfilnd_peer_del(struct kfilnd_peer *kp)
+{
+ if (atomic_cmpxchg(&kp->kp_remove_peer, 0, 1) == 0) {
+ struct lnet_nid peer_nid;
+
+ lnet_nid4_to_nid(kp->kp_nid, &peer_nid);
+ CDEBUG(D_NET, "%s(%p):0x%llx marked for removal from peer cache\n",
+ libcfs_nidstr(&peer_nid), kp, kp->kp_addr);
+
+ lnet_notify(kp->kp_dev->kfd_ni, &peer_nid, false, false,
+ kp->kp_last_alive);
+ }
+}
+
+/**
* kfilnd_peer_purge_old_peer() - Delete the specified peer from the cache
* if we haven't heard from it within 5x LND timeouts.
* @kp: The peer to be checked or purged
/**
* kfilnd_peer_tn_failed() - A transaction with this peer has failed. Mark the
- * peer as either stale or down depending on the provided error value.
- * @kp: The peer to be marked down or stale
+ * peer as either stale or down depending on the provided error value. If
+ * @delete is true we also delete the peer from the cache.
+ * @kp: The peer to be marked down, stale, or deleted.
* @error: An errno indicating why the transaction failed.
+ * @delete: Whether to delete the peer
* Note: We currently only consider EHOSTUNREACH which corresponds to
* C_RC_UNDELIVERABLE, and ENOTCONN which corresponds to C_RC_VNI_NOT_FOUND.
*/
-void kfilnd_peer_tn_failed(struct kfilnd_peer *kp, int error)
+void kfilnd_peer_tn_failed(struct kfilnd_peer *kp, int error, bool delete)
{
if (error == -EHOSTUNREACH || error == -ENOTCONN)
kfilnd_peer_down(kp);
else
kfilnd_peer_stale(kp);
-}
-
-/**
- * kfilnd_peer_del() - Mark a peer for deletion
- * @kp: Peer to be deleted
- */
-void kfilnd_peer_del(struct kfilnd_peer *kp)
-{
- if (atomic_cmpxchg(&kp->kp_remove_peer, 0, 1) == 0) {
- struct lnet_nid peer_nid;
- lnet_nid4_to_nid(kp->kp_nid, &peer_nid);
- CDEBUG(D_NET, "%s(%p):0x%llx marked for removal from peer cache\n",
- libcfs_nidstr(&peer_nid), kp, kp->kp_addr);
-
- lnet_notify(kp->kp_dev->kfd_ni, &peer_nid, false, false,
- kp->kp_last_alive);
- }
+ if (delete)
+ kfilnd_peer_del(kp);
}
/**
hstatus = LNET_MSG_STATUS_REMOTE_ERROR;
kfilnd_tn_status_update(tn, status, hstatus);
- kfilnd_peer_tn_failed(tn->tn_kp, status);
+ /* RKEY is not involved in immediate sends, so no need to
+ * delete peer
+ */
+ kfilnd_peer_tn_failed(tn->tn_kp, status, false);
if (tn->msg_type == KFILND_MSG_HELLO_REQ)
kfilnd_peer_clear_hello_pending(tn->tn_kp);
break;
CFS_FAIL_CHECK(CFS_KFI_FAIL_WAIT_SEND_COMP3)) {
hstatus = LNET_MSG_STATUS_REMOTE_ERROR;
kfilnd_tn_status_update(tn, -EIO, hstatus);
- kfilnd_peer_tn_failed(tn->tn_kp, -EIO);
+ /* Don't delete peer on debug/test path */
+ kfilnd_peer_tn_failed(tn->tn_kp, -EIO, false);
kfilnd_tn_state_change(tn, TN_STATE_FAIL);
break;
}
hstatus = LNET_MSG_STATUS_REMOTE_ERROR;
kfilnd_tn_status_update(tn, status, hstatus);
- kfilnd_peer_tn_failed(tn->tn_kp, status);
+ /* The bulk request message failed, however, there is an edge
+ * case where the last request packet of a message is received
+ * at the target successfully, but the corresponding response
+ * packet is repeatedly dropped. This results in the target
+ * generating a success completion event but the initiator
+ * generating an error completion event. Due to this, we have to
+ * delete the peer here to protect the RKEY.
+ */
+ kfilnd_peer_tn_failed(tn->tn_kp, status, true);
/* Need to cancel the tagged receive to prevent resources from
* being leaked.
case TN_EVENT_TAG_RX_FAIL:
kfilnd_tn_status_update(tn, status,
LNET_MSG_STATUS_LOCAL_ERROR);
+ /* The target may hold a reference to the RKEY, so we need to
+ * delete the peer to protect it
+ */
+ kfilnd_peer_tn_failed(tn->tn_kp, status, true);
kfilnd_tn_state_change(tn, TN_STATE_FAIL);
break;
case TN_EVENT_TX_FAIL:
kfilnd_tn_status_update(tn, status,
LNET_MSG_STATUS_NETWORK_TIMEOUT);
- kfilnd_peer_tn_failed(tn->tn_kp, status);
+ /* The bulk request message was never queued so we do not need
+ * to delete the peer
+ */
+ kfilnd_peer_tn_failed(tn->tn_kp, status, false);
break;
default:
KFILND_TN_ERROR(tn, "Invalid %s event", tn_event_to_str(event));
hstatus = LNET_MSG_STATUS_REMOTE_ERROR;
kfilnd_tn_status_update(tn, status, hstatus);
- kfilnd_peer_tn_failed(tn->tn_kp, status);
+ /* This event occurrs at the target of a bulk LNetPut/Get.
+ * Since the target did not generate the RKEY, we needn't
+ * delete the peer.
+ */
+ kfilnd_peer_tn_failed(tn->tn_kp, status, false);
break;
default:
hstatus = LNET_MSG_STATUS_REMOTE_ERROR;
kfilnd_tn_status_update(tn, status, hstatus);
- kfilnd_peer_tn_failed(tn->tn_kp, status);
+ /* This event occurrs at the target of a bulk LNetPut/Get.
+ * Since the target did not generate the RKEY, we needn't
+ * delete the peer.
+ */
+ kfilnd_peer_tn_failed(tn->tn_kp, status, false);
break;
case TN_EVENT_TAG_TX_OK:
switch (event) {
case TN_EVENT_TX_FAIL:
- kfilnd_peer_tn_failed(tn->tn_kp, status);
+ /* Prior TN states will have deleted the peer if necessary */
+ kfilnd_peer_tn_failed(tn->tn_kp, status, false);
break;
case TN_EVENT_TX_OK:
case TN_EVENT_TAG_RX_CANCEL:
kfilnd_tn_status_update(tn, -ETIMEDOUT,
LNET_MSG_STATUS_NETWORK_TIMEOUT);
- kfilnd_peer_tn_failed(tn->tn_kp, -ETIMEDOUT);
+ /* We've cancelled locally, but the target may still have a ref
+ * on the RKEY. Delete the peer to protect it.
+ */
+ kfilnd_peer_tn_failed(tn->tn_kp, -ETIMEDOUT, true);
break;
case TN_EVENT_TAG_RX_FAIL:
kfilnd_tn_status_update(tn, status,
LNET_MSG_STATUS_LOCAL_ERROR);
+ /* The initiator of a bulk LNetPut/Get eagerly sends the bulk
+ * request message to the target without ensuring the tagged
+ * receive buffer is posted. Thus, the target could be issuing
+ * kfi_write/read operations using the tagged receive buffer
+ * RKEY, and we need to delete this peer to protect the it.
+ */
+ kfilnd_peer_tn_failed(tn->tn_kp, status, true);
break;
case TN_EVENT_TAG_RX_OK: