From 4e70063f8c536a95fb1c2d6f5ce58ed30b6841d8 Mon Sep 17 00:00:00 2001 From: Stefano Avallone Date: Tue, 15 Feb 2022 23:32:03 +0100 Subject: [PATCH] wifi: Rework wifi MAC queues Also, add a First Come First Serve wifi MAC queue scheduler --- src/mesh/helper/mesh-helper.cc | 18 +- src/mesh/model/mesh-wifi-interface-mac.cc | 3 + src/wifi/CMakeLists.txt | 2 + src/wifi/helper/wifi-mac-helper.cc | 5 + src/wifi/helper/wifi-mac-helper.h | 19 + src/wifi/model/block-ack-manager.cc | 66 +- src/wifi/model/fcfs-wifi-queue-scheduler.cc | 157 +++++ src/wifi/model/fcfs-wifi-queue-scheduler.h | 66 ++ src/wifi/model/frame-exchange-manager.cc | 4 +- .../model/ht/ht-frame-exchange-manager.cc | 17 +- src/wifi/model/msdu-aggregator.cc | 9 +- src/wifi/model/qos-txop.cc | 25 +- src/wifi/model/qos-txop.h | 8 - src/wifi/model/txop.cc | 1 + src/wifi/model/wifi-default-ack-manager.cc | 2 +- src/wifi/model/wifi-mac-queue-item.cc | 19 +- src/wifi/model/wifi-mac-queue-item.h | 16 +- .../model/wifi-mac-queue-scheduler-impl.h | 17 + src/wifi/model/wifi-mac-queue.cc | 638 ++++++++---------- src/wifi/model/wifi-mac-queue.h | 290 +++----- src/wifi/model/wifi-mac.cc | 19 + src/wifi/model/wifi-mac.h | 15 + src/wifi/test/power-rate-adaptation-test.cc | 2 + src/wifi/test/wifi-aggregation-test.cc | 4 + src/wifi/test/wifi-mac-ofdma-test.cc | 21 +- src/wifi/test/wifi-mac-queue-test.cc | 44 +- src/wifi/test/wifi-test.cc | 6 + 27 files changed, 805 insertions(+), 688 deletions(-) create mode 100644 src/wifi/model/fcfs-wifi-queue-scheduler.cc create mode 100644 src/wifi/model/fcfs-wifi-queue-scheduler.h diff --git a/src/mesh/helper/mesh-helper.cc b/src/mesh/helper/mesh-helper.cc index e64df564a..344d81bab 100644 --- a/src/mesh/helper/mesh-helper.cc +++ b/src/mesh/helper/mesh-helper.cc @@ -20,16 +20,17 @@ */ #include "mesh-helper.h" -#include "ns3/simulator.h" -#include "ns3/pointer.h" -#include "ns3/mesh-point-device.h" -#include "ns3/wifi-net-device.h" -#include "ns3/minstrel-wifi-manager.h" -#include "ns3/mesh-wifi-interface-mac.h" -#include "ns3/wifi-helper.h" +#include "ns3/fcfs-wifi-queue-scheduler.h" #include "ns3/frame-exchange-manager.h" -#include "ns3/wifi-default-protection-manager.h" +#include "ns3/mesh-point-device.h" +#include "ns3/mesh-wifi-interface-mac.h" +#include "ns3/minstrel-wifi-manager.h" +#include "ns3/pointer.h" +#include "ns3/simulator.h" #include "ns3/wifi-default-ack-manager.h" +#include "ns3/wifi-default-protection-manager.h" +#include "ns3/wifi-helper.h" +#include "ns3/wifi-net-device.h" namespace ns3 { @@ -127,6 +128,7 @@ MeshHelper::CreateInterface (const WifiPhyHelper &phyHelper, Ptr node, uin device->SetRemoteStationManager (manager); mac->SetAddress (Mac48Address::Allocate ()); device->SetMac (mac); + mac->SetMacQueueScheduler (CreateObject ()); mac->ConfigureStandard (m_standard); Ptr fem = mac->GetFrameExchangeManager (); if (fem) diff --git a/src/mesh/model/mesh-wifi-interface-mac.cc b/src/mesh/model/mesh-wifi-interface-mac.cc index e601417c1..272e97a7e 100644 --- a/src/mesh/model/mesh-wifi-interface-mac.cc +++ b/src/mesh/model/mesh-wifi-interface-mac.cc @@ -35,6 +35,8 @@ #include "ns3/channel-access-manager.h" #include "ns3/mac-tx-middle.h" #include "ns3/qos-txop.h" +#include "ns3/wifi-mac-queue-scheduler.h" +#include "ns3/wifi-mac-queue.h" namespace ns3 { @@ -566,6 +568,7 @@ MeshWifiInterfaceMac::ConfigureContentionWindow (uint32_t cwMin, uint32_t cwMax) m_txop->SetMinCw (0); m_txop->SetMaxCw (0); m_txop->SetAifsn (1); + m_scheduler->SetWifiMac (this); } } // namespace ns3 diff --git a/src/wifi/CMakeLists.txt b/src/wifi/CMakeLists.txt index fbc44e47c..4278b88e8 100644 --- a/src/wifi/CMakeLists.txt +++ b/src/wifi/CMakeLists.txt @@ -32,6 +32,7 @@ set(source_files model/eht/multi-link-element.cc model/error-rate-model.cc model/extended-capabilities.cc + model/fcfs-wifi-queue-scheduler.cc model/frame-capture-model.cc model/frame-exchange-manager.cc model/he/constant-obss-pd-algorithm.cc @@ -177,6 +178,7 @@ set(header_files model/eht/multi-link-element.h model/error-rate-model.h model/extended-capabilities.h + model/fcfs-wifi-queue-scheduler.h model/frame-capture-model.h model/frame-exchange-manager.h model/he/constant-obss-pd-algorithm.h diff --git a/src/wifi/helper/wifi-mac-helper.cc b/src/wifi/helper/wifi-mac-helper.cc index 9eccfa138..577ec35d7 100644 --- a/src/wifi/helper/wifi-mac-helper.cc +++ b/src/wifi/helper/wifi-mac-helper.cc @@ -20,6 +20,7 @@ #include "ns3/wifi-net-device.h" #include "wifi-mac-helper.h" +#include "ns3/wifi-mac-queue-scheduler.h" #include "ns3/frame-exchange-manager.h" #include "ns3/wifi-protection-manager.h" #include "ns3/wifi-ack-manager.h" @@ -35,6 +36,7 @@ WifiMacHelper::WifiMacHelper () SetType ("ns3::AdhocWifiMac"); m_assocManager.SetTypeId ("ns3::WifiDefaultAssocManager"); + m_queueScheduler.SetTypeId ("ns3::FcfsWifiQueueScheduler"); m_protectionManager.SetTypeId ("ns3::WifiDefaultProtectionManager"); m_ackManager.SetTypeId ("ns3::WifiDefaultAckManager"); } @@ -61,6 +63,9 @@ WifiMacHelper::Create (Ptr device, WifiStandard standard) const device->SetMac (mac); mac->ConfigureStandard (standard); + Ptr queueScheduler = m_queueScheduler.Create (); + mac->SetMacQueueScheduler (queueScheduler); + // WaveNetDevice stores PHY entities in a different member than WifiNetDevice, hence // GetNPhys() would return 0. We have to attach a protection manager and an ack manager // to the unique instance of frame exchange manager anyway diff --git a/src/wifi/helper/wifi-mac-helper.h b/src/wifi/helper/wifi-mac-helper.h index dbfd396f1..1dc2eb752 100644 --- a/src/wifi/helper/wifi-mac-helper.h +++ b/src/wifi/helper/wifi-mac-helper.h @@ -78,6 +78,16 @@ public: template void SetAssocManager (std::string type, Args&&... args); + /** + * Helper function used to set the MAC queue scheduler. + * + * \tparam Args \deduced Template type parameter pack for the sequence of name-value pairs. + * \param type the type of MAC queue scheduler + * \param args A sequence of name-value pairs of the attributes to set. + */ + template + void SetMacQueueScheduler (std::string type, Args&&... args); + /** * Helper function used to set the Protection Manager. * @@ -122,6 +132,7 @@ public: protected: ObjectFactory m_mac; ///< MAC object factory ObjectFactory m_assocManager; ///< Association Manager + ObjectFactory m_queueScheduler; ///< MAC queue scheduler ObjectFactory m_protectionManager; ///< Factory to create a protection manager ObjectFactory m_ackManager; ///< Factory to create an acknowledgment manager ObjectFactory m_muScheduler; ///< Multi-user Scheduler object factory @@ -152,6 +163,14 @@ WifiMacHelper::SetAssocManager (std::string type, Args&&... args) m_assocManager.Set (args...); } +template +void +WifiMacHelper::SetMacQueueScheduler (std::string type, Args&&... args) +{ + m_queueScheduler.SetTypeId (type); + m_queueScheduler.Set (args...); +} + template void WifiMacHelper::SetProtectionManager (std::string type, Args&&... args) diff --git a/src/wifi/model/block-ack-manager.cc b/src/wifi/model/block-ack-manager.cc index dce9955ff..b3d56b401 100644 --- a/src/wifi/model/block-ack-manager.cc +++ b/src/wifi/model/block-ack-manager.cc @@ -266,9 +266,9 @@ BlockAckManager::GetBar (bool remove, uint8_t tid, Mac48Address address) { Time now = Simulator::Now (); Ptr bar; - // remove all expired MPDUs from the head of the MAC queue, so that + // remove all expired MPDUs from the MAC queue, so that // BlockAckRequest frames (if needed) are scheduled - m_queue->IsEmpty (); + m_queue->WipeAllExpiredMpdus (); auto nextBar = m_bars.begin (); @@ -300,33 +300,6 @@ BlockAckManager::GetBar (bool remove, uint8_t tid, Mac48Address address) nextBar++; continue; } - // remove expired outstanding MPDUs and update the starting sequence number - for (auto mpduIt = it->second.second.begin (); mpduIt != it->second.second.end (); ) - { - if (!(*mpduIt)->IsQueued ()) - { - // the MPDU is no longer in the EDCA queue - mpduIt = it->second.second.erase (mpduIt); - continue; - } - - if ((*mpduIt)->GetTimeStamp () + m_queue->GetMaxDelay () <= now) - { - // MPDU expired - it->second.first.NotifyDiscardedMpdu (*mpduIt); - // Remove from the EDCA queue and fire the Expired trace source, but the - // consequent call to NotifyDiscardedMpdu does nothing (in particular, - // does not schedule a BAR) because we have advanced the transmit window - // and hence this MPDU became an old packet - m_queue->TtlExceeded (*mpduIt, now); - mpduIt = it->second.second.erase (mpduIt); - } - else - { - // MPDUs are typically in increasing order of remaining lifetime - break; - } - } // update BAR if the starting sequence number changed CtrlBAckRequestHeader reqHdr; nextBar->bar->GetPacket ()->PeekHeader (reqHdr); @@ -385,7 +358,6 @@ BlockAckManager::HandleInFlightMpdu (PacketQueueI mpduIt, MpduStatus status, if (status == ACKNOWLEDGED) { // the MPDU has to be dequeued from the EDCA queue - m_queue->DequeueIfQueued (*mpduIt); return it->second.second.erase (mpduIt); } @@ -401,7 +373,7 @@ BlockAckManager::HandleInFlightMpdu (PacketQueueI mpduIt, MpduStatus status, { m_droppedOldMpduCallback (*mpduIt); } - m_queue->Remove (*mpduIt, false); + m_queue->Remove (*mpduIt); return it->second.second.erase (mpduIt); } @@ -453,6 +425,7 @@ BlockAckManager::NotifyGotAck (Ptr mpdu) { if ((*queueIt)->GetHeader ().GetSequenceNumber () == mpdu->GetHeader ().GetSequenceNumber ()) { + m_queue->DequeueIfQueued ({*queueIt}); HandleInFlightMpdu (queueIt, ACKNOWLEDGED, it, Simulator::Now ()); break; } @@ -522,10 +495,12 @@ BlockAckManager::NotifyGotBlockAck (const CtrlBAckResponseHeader& blockAck, Mac4 NS_ASSERT (blockAck.IsCompressed () || blockAck.IsExtendedCompressed () || blockAck.IsMultiSta ()); Time now = Simulator::Now (); + std::list> acked; for (auto queueIt = it->second.second.begin (); queueIt != it->second.second.end (); ) { uint16_t currentSeq = (*queueIt)->GetHeader ().GetSequenceNumber (); + NS_LOG_DEBUG ("Current seq=" << currentSeq); if (blockAck.IsPacketReceived (currentSeq, index)) { it->second.first.NotifyAckedMpdu (*queueIt); @@ -534,18 +509,28 @@ BlockAckManager::NotifyGotBlockAck (const CtrlBAckResponseHeader& blockAck, Mac4 { m_txOkCallback (*queueIt); } + acked.push_back (*queueIt); queueIt = HandleInFlightMpdu (queueIt, ACKNOWLEDGED, it, now); } else { - nFailedMpdus++; - if (!m_txFailedCallback.IsNull ()) - { - m_txFailedCallback (*queueIt); - } - queueIt = HandleInFlightMpdu (queueIt, TO_RETRANSMIT, it, now); + ++queueIt; } } + + // Dequeue all acknowledged MPDUs at once + m_queue->DequeueIfQueued (acked); + + // Remaining outstanding MPDUs have not been acknowledged + for (auto queueIt = it->second.second.begin (); queueIt != it->second.second.end (); ) + { + nFailedMpdus++; + if (!m_txFailedCallback.IsNull ()) + { + m_txFailedCallback (*queueIt); + } + queueIt = HandleInFlightMpdu (queueIt, TO_RETRANSMIT, it, now); + } } return {nSuccessfulMpdus, nFailedMpdus}; } @@ -611,7 +596,7 @@ BlockAckManager::NotifyDiscardedMpdu (Ptr mpdu) if (it->second.first.GetDistance ((*mpduIt)->GetHeader ().GetSequenceNumber ()) >= SEQNO_SPACE_HALF_SIZE) { NS_LOG_DEBUG ("Dropping old MPDU: " << **mpduIt); - m_queue->DequeueIfQueued (*mpduIt); + m_queue->DequeueIfQueued ({*mpduIt}); if (!m_droppedOldMpduCallback.IsNull ()) { m_droppedOldMpduCallback (*mpduIt); @@ -780,8 +765,9 @@ BlockAckManager::SwitchToBlockAckIfNeeded (Mac48Address recipient, uint8_t tid, NS_ASSERT (!ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::PENDING)); if (!ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::REJECTED) && ExistsAgreement (recipient, tid)) { - uint32_t packets = m_queue->GetNPacketsByTidAndAddress (tid, recipient) + - GetNBufferedPackets (recipient, tid); + WifiContainerQueueId queueId {WIFI_QOSDATA_UNICAST_QUEUE, recipient, tid}; + uint32_t packets = m_queue->GetNPackets (queueId) + + GetNBufferedPackets (recipient, tid); if (packets >= m_blockAckThreshold) { NotifyAgreementEstablished (recipient, tid, startingSeq); diff --git a/src/wifi/model/fcfs-wifi-queue-scheduler.cc b/src/wifi/model/fcfs-wifi-queue-scheduler.cc new file mode 100644 index 000000000..7c3dc3cb8 --- /dev/null +++ b/src/wifi/model/fcfs-wifi-queue-scheduler.cc @@ -0,0 +1,157 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2022 Universita' degli Studi di Napoli Federico II + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Stefano Avallone + */ + +#include "ns3/log.h" +#include "ns3/enum.h" +#include "fcfs-wifi-queue-scheduler.h" +#include "wifi-mac-queue.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("FcfsWifiQueueScheduler"); + +NS_OBJECT_ENSURE_REGISTERED (FcfsWifiQueueScheduler); + +TypeId +FcfsWifiQueueScheduler::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::FcfsWifiQueueScheduler") + .SetParent> () + .SetGroupName ("Wifi") + .AddConstructor () + .AddAttribute ("DropPolicy", + "Upon enqueue with full queue, drop oldest (DropOldest) or newest (DropNewest) packet", + EnumValue (DROP_NEWEST), + MakeEnumAccessor (&FcfsWifiQueueScheduler::m_dropPolicy), + MakeEnumChecker (FcfsWifiQueueScheduler::DROP_OLDEST, "DropOldest", + FcfsWifiQueueScheduler::DROP_NEWEST, "DropNewest")) + ; + return tid; +} + +FcfsWifiQueueScheduler::FcfsWifiQueueScheduler () + : NS_LOG_TEMPLATE_DEFINE ("FcfsWifiQueueScheduler") +{ +} + +Ptr +FcfsWifiQueueScheduler::HasToDropBeforeEnqueuePriv (AcIndex ac, Ptr mpdu) +{ + auto queue = GetWifiMacQueue (ac); + if (queue->QueueBase::GetNPackets () < queue->GetMaxSize ().GetValue ()) + { + // the queue is not full, do not drop anything + return nullptr; + } + + // Management frames should be prioritized + if (m_dropPolicy == DROP_OLDEST || mpdu->GetHeader ().IsMgt ()) + { + auto sortedQueuesIt = GetSortedQueues (ac).begin (); + + while (sortedQueuesIt != GetSortedQueues (ac).end () + && std::get (sortedQueuesIt->second.get ().first) == WIFI_MGT_QUEUE) + { + sortedQueuesIt++; + } + + if (sortedQueuesIt != GetSortedQueues (ac).end ()) + { + return queue->PeekByQueueId (sortedQueuesIt->second.get ().first); + } + } + return mpdu; +} + +void +FcfsWifiQueueScheduler::DoNotifyEnqueue (AcIndex ac, Ptr mpdu) +{ + NS_LOG_FUNCTION (this << +ac << *mpdu); + + auto queueId = WifiMacQueueContainer::GetQueueId (mpdu); + + if (GetWifiMacQueue (ac)->GetNPackets (queueId) > 1) + { + // Enqueue takes place at the tail, while the priority is determined by the + // head of the queue. Therefore, if the queue was not empty before inserting + // this MPDU, priority does not change + return; + } + + auto priority = (mpdu->GetHeader ().IsMgt () ? Seconds(0) // Highest priority for management frames + : mpdu->GetExpiryTime ()); + SetPriority (ac, queueId, priority); +} + +void +FcfsWifiQueueScheduler::DoNotifyDequeue (AcIndex ac, const std::list>& mpdus) +{ + NS_LOG_FUNCTION (this << +ac); + + std::set queueIds; + + for (const auto& mpdu : mpdus) + { + queueIds.insert (WifiMacQueueContainer::GetQueueId (mpdu)); + } + + for (const auto& queueId : queueIds) + { + if (std::get (queueId) == WIFI_MGT_QUEUE) + { + // the priority of management queues does not change + continue; + } + + if (auto item = GetWifiMacQueue (ac)->PeekByQueueId (queueId); item != nullptr) + { + SetPriority (ac, queueId, item->GetExpiryTime ()); + } + } +} + +void +FcfsWifiQueueScheduler::DoNotifyRemove (AcIndex ac, const std::list>& mpdus) +{ + NS_LOG_FUNCTION (this << +ac); + + std::set queueIds; + + for (const auto& mpdu : mpdus) + { + queueIds.insert (WifiMacQueueContainer::GetQueueId (mpdu)); + } + + for (const auto& queueId : queueIds) + { + if (std::get<0> (queueId) == WIFI_MGT_QUEUE) + { + // the priority of management queues does not change + continue; + } + + if (auto item = GetWifiMacQueue (ac)->PeekByQueueId (queueId); item != nullptr) + { + SetPriority (ac, queueId, item->GetExpiryTime ()); + } + } +} + +} //namespace ns3 diff --git a/src/wifi/model/fcfs-wifi-queue-scheduler.h b/src/wifi/model/fcfs-wifi-queue-scheduler.h new file mode 100644 index 000000000..34b43ceb4 --- /dev/null +++ b/src/wifi/model/fcfs-wifi-queue-scheduler.h @@ -0,0 +1,66 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2022 Universita' degli Studi di Napoli Federico II + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Stefano Avallone + */ + +#ifndef FCFS_WIFI_QUEUE_SCHEDULER_H +#define FCFS_WIFI_QUEUE_SCHEDULER_H + +#include "ns3/nstime.h" +#include "wifi-mac-queue-scheduler-impl.h" + +namespace ns3 { + +class WifiMacQueueItem; + +/** + * \ingroup wifi + * + * WifiMacQueueScheduler ... + */ +class FcfsWifiQueueScheduler : public WifiMacQueueSchedulerImpl