wifi: AP continues TXOP if missing response from EMLSR client

This commit is contained in:
Stefano Avallone
2023-06-20 16:46:42 +02:00
committed by Stefano Avallone
parent 463d73aeda
commit 1b18f743e1
3 changed files with 173 additions and 125 deletions

View File

@@ -66,7 +66,6 @@ void
EhtFrameExchangeManager::DoDispose()
{
NS_LOG_FUNCTION(this);
m_responseFromEmlsrClients.Cancel();
m_ongoingTxopEnd.Cancel();
HeFrameExchangeManager::DoDispose();
}
@@ -77,15 +76,6 @@ EhtFrameExchangeManager::RxStartIndication(WifiTxVector txVector, Time psduDurat
NS_LOG_FUNCTION(this << txVector << psduDuration.As(Time::MS));
HeFrameExchangeManager::RxStartIndication(txVector, psduDuration);
if (m_txTimer.IsRunning() && m_responseFromEmlsrClients.IsRunning())
{
m_responseFromEmlsrClients.Cancel();
m_responseFromEmlsrClients =
Simulator::Schedule(m_txTimer.GetDelayLeft(),
&EhtFrameExchangeManager::HandleMissingResponses,
this);
}
UpdateTxopEndOnRxStartIndication(psduDuration);
}
@@ -191,26 +181,17 @@ EhtFrameExchangeManager::ForwardPsduDown(Ptr<const WifiPsdu> psdu, WifiTxVector&
{
auto aid = GetWifiRemoteStationManager()->GetAssociationId(*clientIt);
if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt))
if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) &&
GetEmlsrSwitchToListening(psdu, aid, *clientIt))
{
if (GetEmlsrSwitchToListening(psdu, aid, *clientIt))
{
EmlsrSwitchToListening(*clientIt, txDuration);
// this client is no longer involved in the current TXOP
clientIt = m_protectedStas.erase(clientIt);
continue;
}
if (!m_responseFromEmlsrClients.IsRunning() && m_txTimer.IsRunning() &&
m_txTimer.GetStasExpectedToRespond().count(*clientIt) == 1)
{
// we expect a response from this EMLSR client
m_responseFromEmlsrClients =
Simulator::Schedule(m_txTimer.GetDelayLeft(),
&EhtFrameExchangeManager::HandleMissingResponses,
this);
}
EmlsrSwitchToListening(*clientIt, txDuration);
// this client is no longer involved in the current TXOP
clientIt = m_protectedStas.erase(clientIt);
}
else
{
clientIt++;
}
clientIt++;
}
}
}
@@ -233,28 +214,19 @@ EhtFrameExchangeManager::ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVect
{
auto aid = GetWifiRemoteStationManager()->GetAssociationId(*clientIt);
if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt))
if (auto psduMapIt = psduMap.find(aid);
GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) &&
(psduMapIt == psduMap.cend() ||
GetEmlsrSwitchToListening(psduMapIt->second, aid, *clientIt)))
{
if (auto psduMapIt = psduMap.find(aid);
psduMapIt == psduMap.cend() ||
GetEmlsrSwitchToListening(psduMapIt->second, aid, *clientIt))
{
EmlsrSwitchToListening(*clientIt, txDuration);
// this client is no longer involved in the current TXOP
clientIt = m_protectedStas.erase(clientIt);
continue;
}
if (!m_responseFromEmlsrClients.IsRunning() && m_txTimer.IsRunning() &&
m_txTimer.GetStasExpectedToRespond().count(*clientIt) == 1)
{
// we expect a response from this EMLSR client
m_responseFromEmlsrClients =
Simulator::Schedule(m_txTimer.GetDelayLeft(),
&EhtFrameExchangeManager::HandleMissingResponses,
this);
}
EmlsrSwitchToListening(*clientIt, txDuration);
// this client is no longer involved in the current TXOP
clientIt = m_protectedStas.erase(clientIt);
}
else
{
clientIt++;
}
clientIt++;
}
}
}
@@ -505,6 +477,34 @@ EhtFrameExchangeManager::GetEmlsrSwitchToListening(Ptr<const WifiPsdu> psdu,
return true;
}
void
EhtFrameExchangeManager::TransmissionFailed()
{
NS_LOG_FUNCTION(this);
for (const auto& address : m_txTimer.GetStasExpectedToRespond())
{
if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
{
// This EMLSR client did not respond to a frame sent by the AP. Specs say:
// The AP affiliated with the AP MLD should transmit before the TXNAV timer expires
// another initial Control frame addressed to the non-AP STA affiliated with the
// non-AP MLD if the AP intends to continue the frame exchanges with the STA and did
// not receive the response frame from this STA for the most recently transmitted
// frame that requires an immediate response after a SIFS
// (Sec. 35.3.17 of 802.11be D3.1)
// We let the AP continue the TXOP. TransmissionSucceeded() removes this client from
// protected stations, hence next transmission to this client in this TXOP will be
// protected by ICF
NS_LOG_DEBUG("EMLSR client " << address << " did not respond, continue TXOP");
TransmissionSucceeded();
return;
}
}
HeFrameExchangeManager::TransmissionFailed();
}
void
EhtFrameExchangeManager::NotifyChannelReleased(Ptr<Txop> txop)
{
@@ -644,6 +644,7 @@ EhtFrameExchangeManager::ReceiveMpdu(Ptr<const WifiMpdu> mpdu,
m_linkId);
// we just got involved in a DL TXOP. Check if we are still involved in the TXOP in a
// SIFS (we are expected to reply by sending a CTS frame)
m_ongoingTxopEnd.Cancel();
NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + m_phy->GetSifs()).As(Time::S));
m_ongoingTxopEnd = Simulator::Schedule(m_phy->GetSifs() + NanoSeconds(1),
&EhtFrameExchangeManager::TxopEnd,
@@ -654,25 +655,6 @@ EhtFrameExchangeManager::ReceiveMpdu(Ptr<const WifiMpdu> mpdu,
HeFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
}
void
EhtFrameExchangeManager::HandleMissingResponses()
{
NS_LOG_FUNCTION(this);
// The non-AP STA affiliated with the non-AP MLD that received the initial Control frame
// does not respond to the most recently received frame from the AP affiliated with the
// AP MLD that requires immediate response after a SIFS. (Sec. 35.3.17 of 802.11be D3.1)
for (const auto& address : m_txTimer.GetStasExpectedToRespond())
{
NS_LOG_DEBUG(address << " did not respond");
if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
{
m_protectedStas.erase(address);
EmlsrSwitchToListening(address, Seconds(0));
}
}
}
void
EhtFrameExchangeManager::TxopEnd()
{

View File

@@ -93,6 +93,7 @@ class EhtFrameExchangeManager : public HeFrameExchangeManager
void ForwardPsduDown(Ptr<const WifiPsdu> psdu, WifiTxVector& txVector) override;
void ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector& txVector) override;
void SendMuRts(const WifiTxParameters& txParams) override;
void TransmissionFailed() override;
void NotifyChannelReleased(Ptr<Txop> txop) override;
void PostProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) override;
void ReceiveMpdu(Ptr<const WifiMpdu> mpdu,
@@ -113,11 +114,6 @@ class EhtFrameExchangeManager : public HeFrameExchangeManager
void EmlsrSwitchToListening(const Mac48Address& address, const Time& delay);
private:
/**
* Handle missing responses from EMLSR clients that were expected to send a response.
*/
void HandleMissingResponses();
/**
* Update the TXOP end timer when starting a frame transmission.
*
@@ -142,8 +138,6 @@ class EhtFrameExchangeManager : public HeFrameExchangeManager
*/
void TxopEnd();
EventId m_responseFromEmlsrClients; ///< timer used by an AP MLD when expecting a response from
///< an EMLSR client
EventId m_ongoingTxopEnd; //!< event indicating the possible end of the current TXOP (of which
//!< we are not the holder)
};

View File

@@ -1701,15 +1701,15 @@ EmlsrDlTxopTest::CheckResults()
* │CTS│ │BA│ │BA│
* ├───┤ ├──┤ └──┘
* │CTS│ │BA│
* └───┘ └──┘ A switches to listening
* after timeout + transition delay
* │
* ┌───┐ ┌─────┐ │ ┌───┐
* │MU │ │QoS x│ │ │MU │ ┌───┐
* [link 1] │RTS│ │ to A│ │ │RTS│ │BAR│
* ───────────────────────────────────┴───┴┬───┬┴─────┴┬──┬───────┴───┴┬───┬┴───┴┬──┬─
* │CTS│ │BA│ │CTS│ │BA│
* └───┘ └──x └───┘ └──┘
* └───┘ └──┘ AP continues the TXOP despite A switches to listening
* the failure, but sends an ICF after transition delay
*
* ┌───┐ ┌─────┐ │┌───┐ ┌───┐
* │MU │ │QoS x│ ││MU │ ┌───┐ │CF-│
* [link 1] │RTS│ │ to A│ ││RTS│ │BAR│ │End│
* ──────────────────────────────────────────┴───┴┬───┬┴─────┴┬──┬───┴┬───┬┴───┴┬──┬┴───┴
* │CTS│ │BA│ │CTS│ │BA│
* └───┘ └──x └───┘ └──┘
*/
if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
{
@@ -1829,11 +1829,16 @@ EmlsrDlTxopTest::CheckResults()
phy->GetPhyBand());
auto timeout = phy->GetSifs() + phy->GetSlot() + MicroSeconds(20);
// the fourth frame exchange starts a SIFS after the previous one
// the fourth frame exchange starts a SIFS after the previous one because the AP can
// continue the TXOP despite it does not receive the BlockAck (the AP received the
// PHY-RXSTART.indication and the frame exchange involves an EMLSR client)
NS_TEST_EXPECT_MSG_GT_OR_EQ(fourthExchangeIt->front()->startTx,
bAckRespTxEnd + timeout +
m_transitionDelay.at(thirdExchangeStaId),
"Transmission started before transition delay");
bAckRespTxEnd + phy->GetSifs(),
"Transmission started less than a SIFS after BlockAck");
NS_TEST_EXPECT_MSG_LT(fourthExchangeIt->front()->startTx,
bAckRespTxEnd + phy->GetSifs() +
MicroSeconds(1) /* propagation delay upper bound */,
"Transmission started too much time after BlockAck");
auto bAckReqIt = std::next(fourthExchangeIt->front(), 2);
NS_TEST_EXPECT_MSG_EQ(bAckReqIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAckReq(),
@@ -2445,6 +2450,10 @@ EmlsrDlTxopTest::CheckBlockAck(const WifiConstPsduMap& psduMap,
auto apPhy = m_apMac->GetWifiPhy(linkId);
auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, apPhy->GetPhyBand());
auto cfEndTxDuration = WifiPhy::CalculateTxDuration(
Create<WifiPsdu>(Create<Packet>(), WifiMacHeader(WIFI_MAC_CTL_END)),
m_apMac->GetWifiRemoteStationManager(linkId)->GetRtsTxVector(Mac48Address::GetBroadcast()),
apPhy->GetPhyBand());
m_countBlockAck++;
@@ -2454,10 +2463,10 @@ EmlsrDlTxopTest::CheckBlockAck(const WifiConstPsduMap& psduMap,
// the PPDU carrying this BlockAck is corrupted, hence the AP MLD MAC receives the
// PHY-RXSTART indication but it does not receive any frame from the PHY. Therefore,
// at the end of the PPDU transmission, the AP MLD realizes that the EMLSR client has
// not responded and assumes that the EMLSR client has started the transition to the
// listening mode (such transition lasting the transition delay)
// not responded and makes an attempt at continuing the TXOP
// at the end of the PPDU, this link only is not blocked on the EMLSR client
// at the end of the PPDU, this link only is not blocked on both the EMLSR client and
// the AP MLD
Simulator::Schedule(txDuration, [=]() {
for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
{
@@ -2474,50 +2483,30 @@ EmlsrDlTxopTest::CheckBlockAck(const WifiConstPsduMap& psduMap,
WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
id != linkId,
"Checking links of EMLSR client " + std::to_string(clientId) +
" on the AP MLD before the end of the PPDU plus timeout");
" on the AP MLD at the end of fourth BlockAck");
}
});
// immediately after the end of the PPDU, all links are blocked for the EMLSR client
Simulator::Schedule(txDuration + MicroSeconds(1), [=]() {
for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
{
CheckBlockedLink(
m_apMac,
*addr,
id,
WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
true,
"Checking links of EMLSR client " + std::to_string(clientId) +
" are all blocked on the AP MLD after the end of the PPDU plus timeout");
}
});
// immediately before the transition delay, all links are still blocked for the EMLSR client
Simulator::Schedule(txDuration + m_transitionDelay.at(clientId), [=]() {
// a SIFS after the end of the PPDU, still this link only is not blocked on both the
// EMLSR client and the AP MLD
Simulator::Schedule(txDuration + apPhy->GetSifs(), [=]() {
for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
{
CheckBlockedLink(m_staMacs[clientId],
m_apMac->GetAddress(),
id,
WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
id != linkId,
"Checking links on EMLSR client " + std::to_string(clientId) +
" a SIFS after the end of fourth BlockAck");
CheckBlockedLink(m_apMac,
*addr,
id,
WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
true,
WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
id != linkId,
"Checking links of EMLSR client " + std::to_string(clientId) +
" are all blocked on the AP MLD before the transition delay");
" a SIFS after the end of fourth BlockAck");
}
});
// immediately after the transition delay, all links are unblocked for the EMLSR client
Simulator::Schedule(txDuration + m_transitionDelay.at(clientId) + MicroSeconds(1), [=]() {
for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
{
CheckBlockedLink(m_apMac,
*addr,
id,
WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
false,
"Checking links of EMLSR client " + std::to_string(clientId) +
" are all unblocked on the AP MLD after the transition delay");
}
});
// corrupt this BlockAck so that the AP MLD sends a BlockAckReq later on
{
auto uid = psduMap.cbegin()->second->GetPacket()->GetUid();
@@ -2525,7 +2514,8 @@ EmlsrDlTxopTest::CheckBlockAck(const WifiConstPsduMap& psduMap,
}
break;
case 5:
// at the end of the PPDU, this link only is not blocked on the EMLSR client
// at the end of the PPDU, this link only is not blocked on both the EMLSR client and
// the AP MLD
Simulator::Schedule(txDuration, [=]() {
for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
{
@@ -2536,8 +2526,90 @@ EmlsrDlTxopTest::CheckBlockAck(const WifiConstPsduMap& psduMap,
id != linkId,
"Checking links on EMLSR client " + std::to_string(clientId) +
" at the end of fifth BlockAck");
CheckBlockedLink(m_apMac,
*addr,
id,
WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
id != linkId,
"Checking links of EMLSR client " + std::to_string(clientId) +
" on the AP MLD at the end of fifth BlockAck");
}
});
// before the end of the CF-End frame, still this link only is not blocked on both the
// EMLSR client and the AP MLD
Simulator::Schedule(
txDuration + apPhy->GetSifs() + cfEndTxDuration - MicroSeconds(1),
[=]() {
for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
{
CheckBlockedLink(m_staMacs[clientId],
m_apMac->GetAddress(),
id,
WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
id != linkId,
"Checking links on EMLSR client " + std::to_string(clientId) +
" before the end of CF-End frame");
CheckBlockedLink(m_apMac,
*addr,
id,
WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
id != linkId,
"Checking links of EMLSR client " + std::to_string(clientId) +
" on the AP MLD before the end of CF-End frame");
}
});
// after the end of the CF-End frame, all links for the EMLSR client are blocked on the
// AP MLD
Simulator::Schedule(
txDuration + apPhy->GetSifs() + cfEndTxDuration + MicroSeconds(1),
[=]() {
for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
{
CheckBlockedLink(
m_apMac,
*addr,
id,
WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
true,
"Checking links of EMLSR client " + std::to_string(clientId) +
" are all blocked on the AP MLD right after the end of CF-End");
}
});
// before the end of the transition delay, all links for the EMLSR client are still
// blocked on the AP MLD
Simulator::Schedule(
txDuration + apPhy->GetSifs() + cfEndTxDuration + m_transitionDelay.at(clientId) -
MicroSeconds(1),
[=]() {
for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
{
CheckBlockedLink(
m_apMac,
*addr,
id,
WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
true,
"Checking links of EMLSR client " + std::to_string(clientId) +
" are all blocked on the AP MLD before the end of transition delay");
}
});
// immediately after the transition delay, all links for the EMLSR client are unblocked
Simulator::Schedule(
txDuration + apPhy->GetSifs() + cfEndTxDuration + m_transitionDelay.at(clientId) +
MicroSeconds(1),
[=]() {
for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
{
CheckBlockedLink(
m_apMac,
*addr,
id,
WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
false,
"Checking links of EMLSR client " + std::to_string(clientId) +
" are all unblocked on the AP MLD after the transition delay");
}
});
break;
}
}