diff --git a/examples/wireless/examples-to-run.py b/examples/wireless/examples-to-run.py index f7fdba6e8..ad7694f04 100755 --- a/examples/wireless/examples-to-run.py +++ b/examples/wireless/examples-to-run.py @@ -58,7 +58,7 @@ cpp_examples = [ ("wifi-eht-network --simulationTime=0.1 --frequency=5 --useRts=1 --minExpectedThroughput=6 --maxExpectedThroughput=547", "True", "True"), ("wifi-eht-network --simulationTime=0.1 --frequency=2.4 --useRts=0 --useExtendedBlockAck=1 --frequency2=5 --minExpectedThroughput=12 --maxExpectedThroughput=500", "True", "True"), ("wifi-eht-network --simulationTime=0.1 --frequency=2.4 --useRts=1 --minExpectedThroughput=6 --maxExpectedThroughput=212", "True", "True"), - ("wifi-eht-network --simulationTime=0.2 --udp=0 --downlink=1 --useRts=0 --nStations=4 --dlAckType=ACK-SU-FORMAT --enableUlOfdma=1 --enableBsrp=0 --mcs=4 --frequency2=6 --minExpectedThroughput=35 --maxExpectedThroughput=280", "True", "True"), + ("wifi-eht-network --simulationTime=0.22 --udp=0 --downlink=1 --useRts=0 --nStations=4 --dlAckType=ACK-SU-FORMAT --enableUlOfdma=1 --enableBsrp=0 --mcs=4 --frequency2=6 --minExpectedThroughput=35 --maxExpectedThroughput=280", "True", "True"), ("wifi-eht-network --simulationTime=0.25 --frequency=2.4 --udp=0 --downlink=1 --useRts=0 --nStations=5 --dlAckType=MU-BAR --enableUlOfdma=1 --enableBsrp=1 --mcs=5 --frequency2=5 --useExtendedBlockAck=1 --minExpectedThroughput=40 --maxExpectedThroughput=100", "True", "True"), ("wifi-eht-network --simulationTime=0.3 --udp=0 --downlink=1 --useRts=1 --nStations=5 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=0 --mcs=6 --muSchedAccessReqInterval=50ms --frequency2=2.4 --minExpectedThroughput=50 --maxExpectedThroughput=140", "True", "True"), ("wifi-eht-network --simulationTime=0.2 --udp=1 --downlink=0 --useRts=0 --nStations=5 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=1 --mcs=5 --muSchedAccessReqInterval=50ms --frequency2=6 --minExpectedThroughput=70 --maxExpectedThroughput=715", "True", "True"), diff --git a/src/wifi/model/block-ack-manager.cc b/src/wifi/model/block-ack-manager.cc index 75ed03966..ef3326701 100644 --- a/src/wifi/model/block-ack-manager.cc +++ b/src/wifi/model/block-ack-manager.cc @@ -669,7 +669,10 @@ BlockAckManager::ScheduleBar(Ptr bar) bar->GetPacket()->PeekHeader(reqHdr); uint8_t tid = reqHdr.GetTidInfo(); - WifiContainerQueueId queueId(WIFI_CTL_QUEUE, bar->GetHeader().GetAddr2(), std::nullopt); + WifiContainerQueueId queueId(WIFI_CTL_QUEUE, + WIFI_UNICAST, + bar->GetHeader().GetAddr1(), + std::nullopt); Ptr item = nullptr; // if a BAR for the given agreement is present, replace it with the new one diff --git a/src/wifi/model/ht/ht-frame-exchange-manager.cc b/src/wifi/model/ht/ht-frame-exchange-manager.cc index 1f254fbb6..6f39f1243 100644 --- a/src/wifi/model/ht/ht-frame-exchange-manager.cc +++ b/src/wifi/model/ht/ht-frame-exchange-manager.cc @@ -20,9 +20,11 @@ #include "ht-frame-exchange-manager.h" #include "ns3/abort.h" +#include "ns3/assert.h" #include "ns3/ctrl-headers.h" #include "ns3/log.h" #include "ns3/mgt-headers.h" +#include "ns3/qos-blocked-destinations.h" #include "ns3/recipient-block-ack-agreement.h" #include "ns3/snr-tag.h" #include "ns3/wifi-mac-queue.h" @@ -118,7 +120,7 @@ HtFrameExchangeManager::NeedSetupBlockAck(Mac48Address recipient, uint8_t tid) } else { - WifiContainerQueueId queueId{WIFI_QOSDATA_UNICAST_QUEUE, recipient, tid}; + WifiContainerQueueId queueId{WIFI_QOSDATA_QUEUE, WIFI_UNICAST, recipient, tid}; uint32_t packets = qosTxop->GetWifiMacQueue()->GetNPackets(queueId); establish = ((qosTxop->GetBlockAckThreshold() > 0 && packets >= qosTxop->GetBlockAckThreshold()) || @@ -417,110 +419,99 @@ HtFrameExchangeManager::GetBar(AcIndex ac, auto queue = m_mac->GetTxopQueue(ac); queue->WipeAllExpiredMpdus(); - // in case of MLD, we need to check both the queue of control frames that need to be sent - // on our link and the queue of control frames that can be sent on any link. We start with - // the former, but we check the latter even if a suitable frame is found in the former - // (because a BAR that can be sent on any link to a recipient is no longer needed after - // sending a BAR to that recipient on our link). - - std::list queueIds{{WIFI_CTL_QUEUE, m_self, std::nullopt}}; - if (m_self != m_mac->GetAddress()) - { - // add the MLD address - queueIds.emplace_back(WIFI_CTL_QUEUE, m_mac->GetAddress(), std::nullopt); - } + Ptr bar; + Ptr prevBar; Ptr selectedBar; - uint8_t selectedTid = 0; - for (const auto& queueId : queueIds) + // we could iterate over all the scheduler's queues and ignore those that do not contain + // control frames, but it's more efficient to peek frames until we get frames that are + // not control frames, given that control frames have the highest priority + while ((bar = queue->PeekFirstAvailable(m_linkId, nullptr, prevBar)) && bar && + bar->GetHeader().IsCtl()) { - Ptr bar; - Ptr prevBar; - - while ((bar = queue->PeekByQueueId(queueId, prevBar))) + if (bar->GetHeader().IsBlockAckReq()) { - if (bar->GetHeader().IsBlockAckReq()) - { - CtrlBAckRequestHeader reqHdr; - bar->GetPacket()->PeekHeader(reqHdr); - auto tid = reqHdr.GetTidInfo(); - Mac48Address recipient = bar->GetHeader().GetAddr1(); + CtrlBAckRequestHeader reqHdr; + bar->GetPacket()->PeekHeader(reqHdr); + auto tid = reqHdr.GetTidInfo(); + Mac48Address recipient = bar->GetHeader().GetAddr1(); + auto recipientMld = m_mac->GetMldAddress(recipient); - if (selectedBar) + // the scheduler should not return a BlockAckReq that cannot be sent on this link: + // either the TA address is the address of this link or it is the MLD address and + // the RA field is the MLD address of a device we can communicate with on this link + NS_ASSERT_MSG(bar->GetHeader().GetAddr2() == m_self || + (bar->GetHeader().GetAddr2() == m_mac->GetAddress() && recipientMld && + GetWifiRemoteStationManager()->GetAffiliatedStaAddress(recipient)), + "Cannot use link " << +m_linkId << " to send BAR: " << *bar); + + if (optAddress && + (GetWifiRemoteStationManager()->GetMldAddress(*optAddress).value_or(*optAddress) != + GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(recipient) || + optTid != tid)) + { + NS_LOG_DEBUG("BAR " << *bar + << " cannot be returned because it is not addressed" + " to the given station for the given TID"); + prevBar = bar; + continue; + } + + auto agreement = m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid); + if (!agreement) + { + NS_LOG_DEBUG("BA agreement with " << recipient << " for TID=" << +tid + << " was torn down"); + queue->Remove(bar); + continue; + } + // update BAR if the starting sequence number changed + if (auto seqNo = agreement->get().GetStartingSequence(); + reqHdr.GetStartingSequence() != seqNo) + { + reqHdr.SetStartingSequence(seqNo); + Ptr packet = Create(); + packet->AddHeader(reqHdr); + auto updatedBar = Create(packet, bar->GetHeader(), bar->GetTimestamp()); + queue->Replace(bar, updatedBar); + bar = updatedBar; + } + // bar is the BlockAckReq to send + selectedBar = bar; + + // if the selected BAR is intended to be sent on this specific link and the recipient + // is an MLD, remove the BAR (if any) for this BA agreement that can be sent on any + // link (because a BAR that can be sent on any link to a recipient is no longer + // needed after sending a BAR to that recipient on this link) + if (bar->GetHeader().GetAddr2() == m_self && recipientMld) + { + WifiContainerQueueId queueId{WIFI_CTL_QUEUE, + WIFI_UNICAST, + *recipientMld, + std::nullopt}; + Ptr otherBar; + while ((otherBar = queue->PeekByQueueId(queueId, otherBar))) { - NS_ASSERT_MSG(std::get(queueId) != m_self, - "We shall not keep iterating over the control frames that need" - "to be sent on a specific link after selecting a BAR to send"); - // if this BlockAckReq is addressed to the same recipient as the selected BAR, - // remove it from the queue and stop iterating over this queue; otherwise, - // move on to the next frame in the queue - if (GetWifiRemoteStationManager()->GetAffiliatedStaAddress(recipient) == - selectedBar->GetHeader().GetAddr1() && - selectedTid == tid) + if (otherBar->GetHeader().IsBlockAckReq()) { - queue->Remove(bar); - break; + CtrlBAckRequestHeader otherReqHdr; + otherBar->GetPacket()->PeekHeader(otherReqHdr); + if (otherReqHdr.GetTidInfo() == tid) + { + queue->Remove(otherBar); + break; + } } - prevBar = bar; - continue; } - - // if this BlockAckReq can be sent on any link, we have to check that this link - // has been setup with the recipient (this may happen, e.g., if we are an AP MLD - // and we have setup a subset of our links with a non-AP MLD) - if (bar->GetHeader().GetAddr2() != m_self && m_mac->GetMldAddress(recipient) && - !GetWifiRemoteStationManager()->GetAffiliatedStaAddress(recipient)) - { - prevBar = bar; - continue; - } - - if (optAddress && - (GetWifiRemoteStationManager() - ->GetMldAddress(*optAddress) - .value_or(*optAddress) != - GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or( - recipient) || - optTid != tid)) - { - NS_LOG_DEBUG("BAR " << *bar - << " cannot be returned because it is not addressed" - " to the given station for the given TID"); - prevBar = bar; - continue; - } - - auto agreement = m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid); - if (!agreement) - { - NS_LOG_DEBUG("BA agreement with " << recipient << " for TID=" << +tid - << " was torn down"); - queue->Remove(bar); - continue; - } - // update BAR if the starting sequence number changed - if (auto seqNo = agreement->get().GetStartingSequence(); - reqHdr.GetStartingSequence() != seqNo) - { - reqHdr.SetStartingSequence(seqNo); - Ptr packet = Create(); - packet->AddHeader(reqHdr); - auto updatedBar = Create(packet, bar->GetHeader()); - queue->Replace(bar, updatedBar); - bar = updatedBar; - } - // bar is the BlockAckReq to send - selectedBar = bar; - selectedTid = tid; - break; } - if (bar->GetHeader().IsTrigger() && !optAddress && !selectedBar) - { - return bar; - } - // not a BAR nor a Trigger Frame, continue - prevBar = bar; + break; } + if (bar->GetHeader().IsTrigger() && !optAddress && !selectedBar) + { + return bar; + } + // not a BAR nor a Trigger Frame, continue + prevBar = bar; } if (!selectedBar) diff --git a/src/wifi/model/qos-txop.cc b/src/wifi/model/qos-txop.cc index 6342216cb..5270e1c14 100644 --- a/src/wifi/model/qos-txop.cc +++ b/src/wifi/model/qos-txop.cc @@ -153,7 +153,7 @@ QosTxop::GetLink(uint8_t linkId) const uint8_t QosTxop::GetQosQueueSize(uint8_t tid, Mac48Address receiver) const { - WifiContainerQueueId queueId{WIFI_QOSDATA_UNICAST_QUEUE, receiver, tid}; + WifiContainerQueueId queueId{WIFI_QOSDATA_QUEUE, WIFI_UNICAST, receiver, tid}; uint32_t bufferSize = m_queue->GetNBytes(queueId); // A queue size value of 254 is used for all sizes greater than 64 768 octets. uint8_t queueSize = static_cast(std::ceil(std::min(bufferSize, 64769U) / 256.0)); diff --git a/src/wifi/model/wifi-default-ack-manager.cc b/src/wifi/model/wifi-default-ack-manager.cc index 308ef13cc..1d053bdb6 100644 --- a/src/wifi/model/wifi-default-ack-manager.cc +++ b/src/wifi/model/wifi-default-ack-manager.cc @@ -157,7 +157,7 @@ WifiDefaultAckManager::IsResponseNeeded(Ptr mpdu, if (m_baThreshold > 0 && GetMaxDistFromStartingSeq(mpdu, txParams) < m_baThreshold * edca->GetBaBufferSize(receiver, tid) && - (edca->GetWifiMacQueue()->GetNPackets({WIFI_QOSDATA_UNICAST_QUEUE, receiver, tid}) - + (edca->GetWifiMacQueue()->GetNPackets({WIFI_QOSDATA_QUEUE, WIFI_UNICAST, receiver, tid}) - edca->GetBaManager()->GetNBufferedPackets(receiver, tid) > 1) && !(edca->GetTxopLimit(m_linkId).IsStrictlyPositive() && diff --git a/src/wifi/model/wifi-mac-queue-container.cc b/src/wifi/model/wifi-mac-queue-container.cc index 31c4d0111..a50b8e654 100644 --- a/src/wifi/model/wifi-mac-queue-container.cc +++ b/src/wifi/model/wifi-mac-queue-container.cc @@ -79,23 +79,22 @@ WifiMacQueueContainer::GetQueueId(Ptr mpdu) { const WifiMacHeader& hdr = mpdu->GetHeader(); + auto addrType = hdr.GetAddr1().IsGroup() ? WIFI_BROADCAST : WIFI_UNICAST; + auto address = hdr.GetAddr1().IsGroup() ? hdr.GetAddr2() : hdr.GetAddr1(); + if (hdr.IsCtl()) { - return {WIFI_CTL_QUEUE, hdr.GetAddr2(), std::nullopt}; + return {WIFI_CTL_QUEUE, addrType, address, std::nullopt}; } if (hdr.IsMgt()) { - return {WIFI_MGT_QUEUE, hdr.GetAddr2(), std::nullopt}; + return {WIFI_MGT_QUEUE, addrType, address, std::nullopt}; } if (hdr.IsQosData()) { - if (hdr.GetAddr1().IsGroup()) - { - return {WIFI_QOSDATA_BROADCAST_QUEUE, hdr.GetAddr2(), hdr.GetQosTid()}; - } - return {WIFI_QOSDATA_UNICAST_QUEUE, hdr.GetAddr1(), hdr.GetQosTid()}; + return {WIFI_QOSDATA_QUEUE, addrType, address, hdr.GetQosTid()}; } - return {WIFI_DATA_QUEUE, hdr.GetAddr1(), std::nullopt}; + return {WIFI_DATA_QUEUE, addrType, address, std::nullopt}; } const WifiMacQueueContainer::ContainerQueue& @@ -207,7 +206,7 @@ WifiMacQueueContainer::GetAllExpiredMpdus() const std::size_t std::hash::operator()(ns3::WifiContainerQueueId queueId) const { - auto [type, address, tid] = queueId; + auto [type, addrType, address, tid] = queueId; const std::size_t size = tid.has_value() ? 8 : 7; uint8_t buffer[size]; diff --git a/src/wifi/model/wifi-mac-queue-container.h b/src/wifi/model/wifi-mac-queue-container.h index f5acf302d..229107df1 100644 --- a/src/wifi/model/wifi-mac-queue-container.h +++ b/src/wifi/model/wifi-mac-queue-container.h @@ -37,37 +37,46 @@ enum WifiContainerQueueType { WIFI_CTL_QUEUE = 0, WIFI_MGT_QUEUE = 1, - WIFI_QOSDATA_UNICAST_QUEUE = 2, - WIFI_QOSDATA_BROADCAST_QUEUE = 3, - WIFI_DATA_QUEUE = 4 + WIFI_QOSDATA_QUEUE = 2, + WIFI_DATA_QUEUE = 3 +}; + +/// enumeration of frame directions +enum WifiReceiverAddressType : uint8_t +{ + WIFI_UNICAST = 0, + WIFI_BROADCAST }; /** - * Tuple (queue type, Address, TID) identifying a container queue. + * Tuple (queue type, receiver address type, Address, TID) identifying a container queue. * * \note that Address has a different meaning depending on container queue type: - * - if container queue type is WIFI_CTL_QUEUE, Address is the Transmitter Address - * (TA) of the frames stored in the queue. We have distinct control queues - * depending on TA to distinguish among control frames that need to be sent - * over different links by 11be MLDs. MLD address as TA indicates that the frames - * can be sent on any link. TID is ignored. - * - if container queue type is WIFI_MGT_QUEUE, Address is the Transmitter Address - * (TA) of the frames stored in the queue. We have distinct management queues - * depending on TA to distinguish among management frames that need to be sent - * over different links by 11be MLDs. TID is ignored. - * - if container queue type is WIFI_QOSDATA_UNICAST_QUEUE, Address is the Receiver - * Address (RA) of the frames stored in the queue. - * - if container queue type is WIFI_QOSDATA_BROADCAST_QUEUE, Address is the - * Transmitter Address (TA) of the frames stored in the queue. We have distinct - * broadcast QoS queues depending on TA to distinguish among broadcast QoS Data - * frames that need to be sent over different links by 11be MLDs. - * - if container queue type is WIFI_DATA_QUEUE, Address is the Receiver Address - * (RA) of the frames stored in the queue. We do not need to consider the - * Transmitter Address (TA) because 11be stations are QoS stations and hence do - * not send non-QoS Data frames. TID is ignored. + * + * - for container queue types holding unicast frames, Address is the Receiver Address (RA) + * of the frames stored in the queue. For 11be MLDs, it is expected that: + * + the RA of unicast management frames are link addresses (indicating the link on which + * they must be sent) + * + the RA of unicast QoS data frames are MLD addresses (indicating that they can be sent + * on any link) + * + if the RA of a unicast control frame is a link address, that control frame can only be + * sent on the corresponding link; if the RA is an MLD address, that control frame can be + * sent on any link + * + * - for container queue types holding broadcast frames, Address is the Transmitter Address (TA) + * of the frames stored in the queue. For 11be MLDs, it is expected that: + * + the TA of broadcast management frames are link addresses (indicating the link on which + * they must be sent) + * + the TA of broadcast QoS data frames are MLD addresses (indicating that they can be sent + * on any link) + * + if the TA of a broadcast control frame is a link address, that control frame can only be + * sent on the corresponding link; if the TA is an MLD address, that control frame can be + * sent on any link + * + * The TID is only specified for container queue types holding QoS data frames. */ -using WifiContainerQueueId = - std::tuple>; +using WifiContainerQueueId = std:: + tuple>; } // namespace ns3 diff --git a/src/wifi/model/wifi-mac-queue.cc b/src/wifi/model/wifi-mac-queue.cc index 9442e10ea..978167474 100644 --- a/src/wifi/model/wifi-mac-queue.cc +++ b/src/wifi/model/wifi-mac-queue.cc @@ -303,7 +303,7 @@ WifiMacQueue::PeekByTidAndAddress(uint8_t tid, Mac48Address dest, PtrGetNext(m_ac, linkId); } - NS_ASSERT(!queueId || std::get<0>(*queueId) != WIFI_QOSDATA_UNICAST_QUEUE || - std::get<2>(*queueId)); + NS_ASSERT(!queueId || std::get<0>(*queueId) != WIFI_QOSDATA_QUEUE || std::get<3>(*queueId)); while (queueId.has_value() && blockedPackets && - std::get<0>(queueId.value()) == WIFI_QOSDATA_UNICAST_QUEUE && - blockedPackets->IsBlocked(std::get<1>(queueId.value()), *std::get<2>(queueId.value()))) + std::get<0>(queueId.value()) == WIFI_QOSDATA_QUEUE && + blockedPackets->IsBlocked(std::get<2>(queueId.value()), *std::get<3>(queueId.value()))) { queueId = m_scheduler->GetNext(m_ac, linkId, queueId.value()); - NS_ASSERT(!queueId || std::get<0>(*queueId) != WIFI_QOSDATA_UNICAST_QUEUE || - std::get<2>(*queueId)); + NS_ASSERT(!queueId || std::get<0>(*queueId) != WIFI_QOSDATA_QUEUE || std::get<3>(*queueId)); } if (!queueId.has_value()) diff --git a/src/wifi/test/wifi-mac-queue-test.cc b/src/wifi/test/wifi-mac-queue-test.cc index d4a7f9a7b..c49767506 100644 --- a/src/wifi/test/wifi-mac-queue-test.cc +++ b/src/wifi/test/wifi-mac-queue-test.cc @@ -225,8 +225,8 @@ WifiExtractExpiredMpdusTest::DoRun() Enqueue(rxAddr2, false, MilliSeconds(70)); Enqueue(rxAddr2, false, MilliSeconds(75)); - WifiContainerQueueId queueId1{WIFI_QOSDATA_UNICAST_QUEUE, rxAddr1, 0}; - WifiContainerQueueId queueId2{WIFI_QOSDATA_UNICAST_QUEUE, rxAddr2, 0}; + WifiContainerQueueId queueId1{WIFI_QOSDATA_QUEUE, WIFI_UNICAST, rxAddr1, 0}; + WifiContainerQueueId queueId2{WIFI_QOSDATA_QUEUE, WIFI_UNICAST, rxAddr2, 0}; Simulator::Schedule(MilliSeconds(25), [&]() { /**