bool ProbeTxControlProcessPacket()

in src/cdi/adapter_efa_probe_tx.c [194:339]


bool ProbeTxControlProcessPacket(ProbeEndpointState* probe_ptr, const CdiDecodedProbeHeader* probe_hdr_ptr,
                                 uint64_t* wait_timeout_ms_ptr)
{
    bool ret_new_state = false;
    EfaEndpointState* efa_endpoint_state_ptr = (EfaEndpointState*)probe_ptr->app_adapter_endpoint_handle->type_specific_ptr;
    CdiEndpointHandle cdi_endpoint_handle = probe_ptr->app_adapter_endpoint_handle->cdi_endpoint_handle;

    switch (probe_hdr_ptr->command) {
        case kProbeCommandReset:
            CDI_LOG_THREAD_COMPONENT(kLogDebug, kLogComponentProbe,
                                     "Probe Tx remote IP[%s:%d] got Reset command from Rx. Restarting EFA connection.",
                                     probe_hdr_ptr->senders_ip_str, probe_hdr_ptr->senders_control_dest_port);
            // Queue Endpoint Manager to reset the EFA connection and notify the application that we are disconnected.
            ProbeControlEfaConnectionQueueReset(probe_ptr, NULL);

            // Get latest GID from remote.
            memcpy(efa_endpoint_state_ptr->remote_ipv6_gid_array, probe_hdr_ptr->senders_gid_array,
                   sizeof(efa_endpoint_state_ptr->remote_ipv6_gid_array));

            if (NULL == probe_ptr->app_adapter_endpoint_handle->protocol_handle) {
                // Negotiated protocol version has not been set yet, so do so now.
                EndpointManagerProtocolVersionSet(cdi_endpoint_handle, &probe_hdr_ptr->senders_version);
            }

            probe_ptr->tx_probe_state.tx_state = kProbeStateResetting;
            *wait_timeout_ms_ptr = ENDPOINT_MANAGER_COMPLETION_TIMEOUT_MSEC;
            ret_new_state = true;
            break;
        case kProbeCommandAck:
            CdiOsCritSectionReserve(probe_ptr->ack_lock); // Lock access to the ack state data.
            const CdiDecodedProbeAck* packet_ack_ptr = &probe_hdr_ptr->ack_packet;

            // Check if we sent a command and are waiting for an ACK for it. If not, ignore the ACK.
            if (probe_ptr->ack_is_pending) {
                // We are waiting for an ACK. Check if the ACK contains the same command and probe packet number of
                // the command that was sent.

                // Ensure the sizes of these values are the same, so wrapping doesn't affect results when comparing
                // them.
                CDI_STATIC_ASSERT(sizeof(packet_ack_ptr->ack_control_packet_num) == sizeof(probe_ptr->ack_control_packet_num), \
                    "Control packet sizes must match.");

                if (packet_ack_ptr->ack_command == probe_ptr->ack_command &&
                    packet_ack_ptr->ack_control_packet_num == probe_ptr->ack_control_packet_num) {
                    // It matches, so we got the ACK for the command that was sent.
                    probe_ptr->ack_is_pending = false;

                    // Don't log the ping ACK commands (generates too many log messages).
                    if (kProbeCommandPing != packet_ack_ptr->ack_command) {
                        CDI_LOG_THREAD_COMPONENT(kLogDebug, kLogComponentProbe,
                                                 "Probe Tx remote IP[%s:%d] accepted ACK.",
                                                 probe_hdr_ptr->senders_ip_str,
                                                 probe_hdr_ptr->senders_control_dest_port);
                        CDI_LOG_THREAD(kLogInfo, "Received connection response");
                    }

                    if (kProbeCommandReset == packet_ack_ptr->ack_command) {
                        // Get latest GID from remote.
                        memcpy(efa_endpoint_state_ptr->remote_ipv6_gid_array, probe_hdr_ptr->senders_gid_array,
                               sizeof(efa_endpoint_state_ptr->remote_ipv6_gid_array));

                        char gid_name_str[MAX_IPV6_ADDRESS_STRING_LENGTH];
                        DeviceGidToString(efa_endpoint_state_ptr->remote_ipv6_gid_array,
                                          sizeof(efa_endpoint_state_ptr->remote_ipv6_gid_array), gid_name_str,
                                          sizeof(gid_name_str));
                        CDI_LOG_THREAD(kLogInfo, "Probe Tx remote IP[%s:%d] using remote EFA device GID[%s].",
                                       probe_hdr_ptr->senders_ip_str, probe_hdr_ptr->senders_control_dest_port,
                                       gid_name_str);

                        // Reset negotiated protocol version.
                        ProtocolVersionDestroy(probe_ptr->app_adapter_endpoint_handle->protocol_handle);
                        probe_ptr->app_adapter_endpoint_handle->protocol_handle = NULL;

                        // Check if we received a probe version in the ACK that only supports probe versions before
                        // 3. Probe version 3 and later support the kProbeStateSendProtocolVersion command.
                        if (probe_hdr_ptr->senders_version.probe_version_num < 3) {
                            // Remote is using probe version before 3. It does not support the version command. So,
                            // queue endpoint start and advance state to wait for it to complete.
                            EndpointManagerProtocolVersionSet(cdi_endpoint_handle, &probe_hdr_ptr->senders_version);
                            EndpointManagerQueueEndpointStart(probe_ptr->app_adapter_endpoint_handle->cdi_endpoint_handle);
                            probe_ptr->tx_probe_state.tx_state = kProbeStateWaitForStart;
                            *wait_timeout_ms_ptr = ENDPOINT_MANAGER_COMPLETION_TIMEOUT_MSEC;
                        } else {
                            // Remote supports probe later than version 2, so send it our protocol/probe version using a
                            // command that is only supported by probe versions later than 2.
                            probe_ptr->tx_probe_state.tx_state = kProbeStateSendProtocolVersion;
                            probe_ptr->tx_probe_state.send_command_retry_count = 0;
                            *wait_timeout_ms_ptr = 0; // Process immediately.
                        }
                        ret_new_state = true;
                    } else if (kProbeCommandProtocolVersion == packet_ack_ptr->ack_command) {
                        // Got an ACK for a protocol version command. Set protocol version.
                        EndpointManagerProtocolVersionSet(cdi_endpoint_handle, &probe_hdr_ptr->senders_version);
                        // Queue endpoint start and advance state to wait for it to complete.
                        EndpointManagerQueueEndpointStart(probe_ptr->app_adapter_endpoint_handle->cdi_endpoint_handle);
                        probe_ptr->tx_probe_state.tx_state = kProbeStateWaitForStart;
                        *wait_timeout_ms_ptr = ENDPOINT_MANAGER_COMPLETION_TIMEOUT_MSEC;
                        ret_new_state = true;
                    } else if (kProbeCommandPing == packet_ack_ptr->ack_command) {
                        // Got an ACK for a ping command. Drop back to the EFA connected state, which will repeat the
                        // ping process. Setup wait period for next ping based on ping frequency.
                        probe_ptr->tx_probe_state.tx_state = kProbeStateEfaConnected;
                        *wait_timeout_ms_ptr = SEND_PING_COMMAND_FREQUENCY_MSEC;
                        ret_new_state = true;
                    } else {
                        assert(false); // No other supported commands return an Ack.
                    }
                } else {
                    CDI_LOG_THREAD_COMPONENT(kLogDebug, kLogComponentProbe,
                        "Probe Tx remote IP[%s:%d] ignoring ACK. Got ACK for command[%s] packet_num[%d]. Expected "
                        "command[%s] packet_num[%d].", probe_hdr_ptr->senders_ip_str,
                        probe_hdr_ptr->senders_control_dest_port,
                        InternalUtilityKeyEnumToString(kKeyProbeCommand, packet_ack_ptr->ack_command),
                        packet_ack_ptr->ack_control_packet_num,
                        InternalUtilityKeyEnumToString(kKeyProbeCommand, probe_ptr->ack_command),
                        probe_ptr->ack_control_packet_num);
                }
            } else {
                CDI_LOG_THREAD_COMPONENT(kLogDebug, kLogComponentProbe,
                                         "Probe Tx remote IP[%s:%d]  ignoring unexpected ACK.",
                                         probe_hdr_ptr->senders_ip_str, probe_hdr_ptr->senders_control_dest_port);
            }
            CdiOsCritSectionRelease(probe_ptr->ack_lock); // Release access to the ack state data.
            break;
        case kProbeCommandConnected:
            if (kProbeStateEfaProbe != probe_ptr->tx_probe_state.tx_state) {
                // We are not expecting a connection command yet, so send a reset.
                probe_ptr->tx_probe_state.tx_state = kProbeStateSendReset;
                *wait_timeout_ms_ptr = 0; // Take effect immediately.
                ret_new_state = true;
            } else {
                // Got a connected command from receiver. Advance state to ensure probe ACKs have all been received.
                probe_ptr->tx_probe_state.tx_state = kProbeStateEfaTxProbeAcks;
                *wait_timeout_ms_ptr = 0; // Take effect immediately.
                ret_new_state = true;
            }
            break;

        // Should never get these commands.
        case kProbeCommandPing:
        default:
            assert(false);
    }

    return ret_new_state;
}