/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* * Copyright (c) 2005, 2009 INRIA * Copyright (c) 2009 MIRKO BANCHI * * 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 * * Authors: Mathieu Lacage * Mirko Banchi * Stefano Avallone */ #include "ns3/simulator.h" #include "wifi-mac-queue.h" #include "qos-blocked-destinations.h" #include namespace ns3 { NS_LOG_COMPONENT_DEFINE ("WifiMacQueue"); NS_OBJECT_ENSURE_REGISTERED (WifiMacQueue); NS_OBJECT_TEMPLATE_CLASS_DEFINE (Queue, WifiMacQueueItem); TypeId WifiMacQueue::GetTypeId (void) { static TypeId tid = TypeId ("ns3::WifiMacQueue") .SetParent > () .SetGroupName ("Wifi") .AddConstructor () .AddAttribute ("MaxSize", "The max queue size", QueueSizeValue (QueueSize ("500p")), MakeQueueSizeAccessor (&QueueBase::SetMaxSize, &QueueBase::GetMaxSize), MakeQueueSizeChecker ()) .AddAttribute ("MaxDelay", "If a packet stays longer than this delay in the queue, it is dropped.", TimeValue (MilliSeconds (500)), MakeTimeAccessor (&WifiMacQueue::SetMaxDelay), MakeTimeChecker ()) .AddAttribute ("DropPolicy", "Upon enqueue with full queue, drop oldest (DropOldest) or newest (DropNewest) packet", EnumValue (DROP_NEWEST), MakeEnumAccessor (&WifiMacQueue::m_dropPolicy), MakeEnumChecker (WifiMacQueue::DROP_OLDEST, "DropOldest", WifiMacQueue::DROP_NEWEST, "DropNewest")) .AddTraceSource ("Expired", "MPDU dropped because its lifetime expired.", MakeTraceSourceAccessor (&WifiMacQueue::m_traceExpired), "ns3::WifiMacQueueItem::TracedCallback") ; return tid; } WifiMacQueue::WifiMacQueue (AcIndex ac) : m_ac (ac), NS_LOG_TEMPLATE_DEFINE ("WifiMacQueue") { } WifiMacQueue::~WifiMacQueue () { NS_LOG_FUNCTION_NOARGS (); m_nQueuedPackets.clear (); m_nQueuedBytes.clear (); } static std::list> g_emptyWifiMacQueue; //!< empty Wi-Fi MAC queue const WifiMacQueue::ConstIterator WifiMacQueue::EMPTY = g_emptyWifiMacQueue.end (); void WifiMacQueue::SetMaxDelay (Time delay) { NS_LOG_FUNCTION (this << delay); m_maxDelay = delay; } Time WifiMacQueue::GetMaxDelay (void) const { return m_maxDelay; } bool WifiMacQueue::Enqueue (Ptr item) { NS_LOG_FUNCTION (this << *item); return Insert (end (), item); } bool WifiMacQueue::PushFront (Ptr item) { NS_LOG_FUNCTION (this << *item); return Insert (begin (), item); } bool WifiMacQueue::Insert (ConstIterator pos, Ptr item) { NS_LOG_FUNCTION (this << *item); NS_ASSERT_MSG (GetMaxSize ().GetUnit () == QueueSizeUnit::PACKETS, "WifiMacQueues must be in packet mode"); // insert the item if the queue is not full if (QueueBase::GetNPackets () < GetMaxSize ().GetValue ()) { return DoEnqueue (pos, item); } // the queue is full; scan the list in the attempt to remove stale packets ConstIterator it = begin (); const Time now = Simulator::Now (); while (it != end ()) { if (it == pos && TtlExceeded (it, now)) { return DoEnqueue (it, item); } if (TtlExceeded (it, now)) { return DoEnqueue (pos, item); } it++; } // the queue is still full, remove the oldest item if the policy is drop oldest if (m_dropPolicy == DROP_OLDEST) { NS_LOG_DEBUG ("Remove the oldest item in the queue"); if (pos == begin ()) { // Avoid invalidating pos DoRemove (begin ()); pos = begin (); } else { DoRemove (begin ()); } } return DoEnqueue (pos, item); } Ptr WifiMacQueue::Dequeue (void) { NS_LOG_FUNCTION (this); const Time now = Simulator::Now (); for (ConstIterator it = begin (); it != end (); ) { if (!TtlExceeded (it, now)) { return DoDequeue (it); } } NS_LOG_DEBUG ("The queue is empty"); return 0; } void WifiMacQueue::DequeueIfQueued (Ptr mpdu) { NS_LOG_FUNCTION (this << *mpdu); if (mpdu->IsQueued ()) { NS_ASSERT (mpdu->m_queueAc == m_ac); NS_ASSERT (*mpdu->m_queueIt == mpdu); DoDequeue (mpdu->m_queueIt); } } Ptr WifiMacQueue::Peek (void) const { NS_LOG_FUNCTION (this); const Time now = Simulator::Now (); for (auto it = begin (); it != end (); it++) { // skip packets that stayed in the queue for too long. They will be // actually removed from the queue by the next call to a non-const method if (now <= (*it)->GetTimeStamp () + m_maxDelay) { return DoPeek (it); } } NS_LOG_DEBUG ("The queue is empty"); return 0; } WifiMacQueue::ConstIterator WifiMacQueue::PeekByAddress (Mac48Address dest, ConstIterator pos) const { NS_LOG_FUNCTION (this << dest); ConstIterator it = (pos != EMPTY ? pos : begin ()); const Time now = Simulator::Now (); while (it != end ()) { // skip packets that stayed in the queue for too long. They will be // actually removed from the queue by the next call to a non-const method if (now <= (*it)->GetTimeStamp () + m_maxDelay) { if (((*it)->GetHeader ().IsData () || (*it)->GetHeader ().IsQosData ()) && (*it)->GetDestinationAddress () == dest) { return it; } } it++; } NS_LOG_DEBUG ("The queue is empty"); return end (); } WifiMacQueue::ConstIterator WifiMacQueue::PeekByTid (uint8_t tid, ConstIterator pos) const { NS_LOG_FUNCTION (this << +tid); ConstIterator it = (pos != EMPTY ? pos : begin ()); const Time now = Simulator::Now (); while (it != end ()) { // skip packets that stayed in the queue for too long. They will be // actually removed from the queue by the next call to a non-const method if (now <= (*it)->GetTimeStamp () + m_maxDelay) { if ((*it)->GetHeader ().IsQosData () && (*it)->GetHeader ().GetQosTid () == tid) { return it; } } it++; } NS_LOG_DEBUG ("The queue is empty"); return end (); } WifiMacQueue::ConstIterator WifiMacQueue::PeekByTidAndAddress (uint8_t tid, Mac48Address dest, ConstIterator pos) const { NS_LOG_FUNCTION (this << +tid << dest); ConstIterator it = (pos != EMPTY ? pos : begin ()); const Time now = Simulator::Now (); while (it != end ()) { // skip packets that stayed in the queue for too long. They will be // actually removed from the queue by the next call to a non-const method if (now <= (*it)->GetTimeStamp () + m_maxDelay) { if ((*it)->GetHeader ().IsQosData () && (*it)->GetDestinationAddress () == dest && (*it)->GetHeader ().GetQosTid () == tid) { return it; } } it++; } NS_LOG_DEBUG ("The queue is empty"); return end (); } WifiMacQueue::ConstIterator WifiMacQueue::PeekFirstAvailable (const Ptr blockedPackets, ConstIterator pos) const { NS_LOG_FUNCTION (this); ConstIterator it = (pos != EMPTY ? pos : begin ()); const Time now = Simulator::Now (); while (it != end ()) { // skip packets that stayed in the queue for too long. They will be // actually removed from the queue by the next call to a non-const method if (now <= (*it)->GetTimeStamp () + m_maxDelay) { if (!(*it)->GetHeader ().IsQosData () || !blockedPackets || !blockedPackets->IsBlocked ((*it)->GetHeader ().GetAddr1 (), (*it)->GetHeader ().GetQosTid ())) { return it; } } it++; } NS_LOG_DEBUG ("The queue is empty"); return end (); } Ptr WifiMacQueue::Remove (void) { NS_LOG_FUNCTION (this); const Time now = Simulator::Now (); for (ConstIterator it = begin (); it != end (); ) { if (!TtlExceeded (it, now)) { return DoRemove (it); } } NS_LOG_DEBUG ("The queue is empty"); return 0; } bool WifiMacQueue::Remove (Ptr packet) { NS_LOG_FUNCTION (this << packet); const Time now = Simulator::Now (); for (ConstIterator it = begin (); it != end (); ) { if (!TtlExceeded (it, now)) { if ((*it)->GetPacket () == packet) { DoRemove (it); return true; } it++; } } NS_LOG_DEBUG ("Packet " << packet << " not found in the queue"); return false; } WifiMacQueue::ConstIterator WifiMacQueue::Remove (ConstIterator pos, bool removeExpired) { NS_LOG_FUNCTION (this); if (!removeExpired) { ConstIterator curr = pos++; DoRemove (curr); return pos; } const Time now = Simulator::Now (); // remove stale items queued before the given position ConstIterator it = begin (); while (it != end ()) { if (it == pos) { ConstIterator curr = pos++; DoRemove (curr); return pos; } else if (!TtlExceeded (it, now)) { it++; } } NS_LOG_DEBUG ("Invalid iterator"); return end (); } uint32_t WifiMacQueue::GetNPacketsByAddress (Mac48Address dest) { NS_LOG_FUNCTION (this << dest); uint32_t nPackets = 0; const Time now = Simulator::Now (); for (ConstIterator it = begin (); it != end (); ) { if (!TtlExceeded (it, now)) { if ((*it)->GetHeader ().IsData () && (*it)->GetDestinationAddress () == dest) { nPackets++; } it++; } } NS_LOG_DEBUG ("returns " << nPackets); return nPackets; } uint32_t WifiMacQueue::GetNPacketsByTidAndAddress (uint8_t tid, Mac48Address dest) { NS_LOG_FUNCTION (this << dest); uint32_t nPackets = 0; const Time now = Simulator::Now (); for (ConstIterator it = begin (); it != end (); ) { if (!TtlExceeded (it, now)) { if ((*it)->GetHeader ().IsQosData () && (*it)->GetDestinationAddress () == dest && (*it)->GetHeader ().GetQosTid () == tid) { nPackets++; } it++; } } NS_LOG_DEBUG ("returns " << nPackets); return nPackets; } bool WifiMacQueue::IsEmpty (void) { NS_LOG_FUNCTION (this); const Time now = Simulator::Now (); for (ConstIterator it = begin (); it != end (); ) { if (!TtlExceeded (it, now)) { NS_LOG_DEBUG ("returns false"); return false; } } NS_LOG_DEBUG ("returns true"); return true; } uint32_t WifiMacQueue::GetNPackets (void) { NS_LOG_FUNCTION (this); const Time now = Simulator::Now (); // remove packets that stayed in the queue for too long for (ConstIterator it = begin (); it != end (); ) { if (!TtlExceeded (it, now)) { it++; } } return QueueBase::GetNPackets (); } uint32_t WifiMacQueue::GetNBytes (void) { NS_LOG_FUNCTION (this); const Time now = Simulator::Now (); // remove packets that stayed in the queue for too long for (ConstIterator it = begin (); it != end (); ) { if (!TtlExceeded (it, now)) { it++; } } return QueueBase::GetNBytes (); } uint32_t WifiMacQueue::GetNPackets (uint8_t tid, Mac48Address dest) const { WifiAddressTidPair addressTidPair (dest, tid); auto it = m_nQueuedPackets.find (addressTidPair); if (it == m_nQueuedPackets.end ()) { return 0; } return m_nQueuedPackets.at (addressTidPair); } uint32_t WifiMacQueue::GetNBytes (uint8_t tid, Mac48Address dest) const { WifiAddressTidPair addressTidPair (dest, tid); auto it = m_nQueuedBytes.find (addressTidPair); if (it == m_nQueuedBytes.end ()) { return 0; } return m_nQueuedBytes.at (addressTidPair); } bool WifiMacQueue::DoEnqueue (ConstIterator pos, Ptr item) { Iterator ret; if (Queue::DoEnqueue (pos, item, ret)) { // update statistics about queued packets if (item->GetHeader ().IsQosData ()) { WifiAddressTidPair addressTidPair (item->GetHeader ().GetAddr1 (), item->GetHeader ().GetQosTid ()); auto it = m_nQueuedPackets.find (addressTidPair); if (it == m_nQueuedPackets.end ()) { m_nQueuedPackets[addressTidPair] = 0; m_nQueuedBytes[addressTidPair] = 0; } m_nQueuedPackets[addressTidPair]++; m_nQueuedBytes[addressTidPair] += item->GetSize (); } // set item's information about its position in the queue item->m_queueAc = m_ac; item->m_queueIt = ret; return true; } return false; } Ptr WifiMacQueue::DoDequeue (ConstIterator pos) { NS_LOG_FUNCTION (this); if (TtlExceeded (pos, Simulator::Now ())) { NS_LOG_DEBUG ("Packet lifetime expired"); return nullptr; } Ptr item = Queue::DoDequeue (pos); if (item != 0 && item->GetHeader ().IsQosData ()) { WifiAddressTidPair addressTidPair (item->GetHeader ().GetAddr1 (), item->GetHeader ().GetQosTid ()); NS_ASSERT (m_nQueuedPackets.find (addressTidPair) != m_nQueuedPackets.end ()); NS_ASSERT (m_nQueuedPackets[addressTidPair] >= 1); NS_ASSERT (m_nQueuedBytes[addressTidPair] >= item->GetSize ()); m_nQueuedPackets[addressTidPair]--; m_nQueuedBytes[addressTidPair] -= item->GetSize (); } if (item != 0) { NS_ASSERT (item->IsQueued ()); item->m_queueAc = AC_UNDEF; } return item; } Ptr WifiMacQueue::DoRemove (ConstIterator pos) { Ptr item = Queue::DoRemove (pos); if (item != 0 && item->GetHeader ().IsQosData ()) { WifiAddressTidPair addressTidPair (item->GetHeader ().GetAddr1 (), item->GetHeader ().GetQosTid ()); NS_ASSERT (m_nQueuedPackets.find (addressTidPair) != m_nQueuedPackets.end ()); NS_ASSERT (m_nQueuedPackets[addressTidPair] >= 1); NS_ASSERT (m_nQueuedBytes[addressTidPair] >= item->GetSize ()); m_nQueuedPackets[addressTidPair]--; m_nQueuedBytes[addressTidPair] -= item->GetSize (); } if (item != 0) { NS_ASSERT (item->IsQueued ()); item->m_queueAc = AC_UNDEF; } return item; } } //namespace ns3