wifi: AP continues TXOP if missing response from EMLSR client
This commit is contained in:
committed by
Stefano Avallone
parent
463d73aeda
commit
1b18f743e1
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user