simulation/src/traffic-control/model/fq-pie-queue-disc.cc (463 lines of code) (raw):

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* * Copyright (c) 2016 Universita' degli Studi di Napoli Federico II * Copyright (c) 2018 NITK Surathkal (modified for FQ-PIE) * * 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: Pasquale Imputato <p.imputato@gmail.com> * Stefano Avallone <stefano.avallone@unina.it> * Modified for FQ-PIE by: Sumukha PK <sumukhapk46@gmail.com> * Prajval M <26prajval98@gmail.com> * Ishaan R D <ishaanrd6@gmail.com> * Mohit P. Tahiliani <tahiliani@nitk.edu.in> */ #include "ns3/log.h" #include "ns3/string.h" #include "ns3/queue.h" #include "fq-pie-queue-disc.h" #include "pie-queue-disc.h" #include "ns3/net-device-queue-interface.h" namespace ns3 { NS_LOG_COMPONENT_DEFINE ("FqPieQueueDisc"); NS_OBJECT_ENSURE_REGISTERED (FqPieFlow); TypeId FqPieFlow::GetTypeId (void) { static TypeId tid = TypeId ("ns3::FqPieFlow") .SetParent<QueueDiscClass> () .SetGroupName ("TrafficControl") .AddConstructor<FqPieFlow> () ; return tid; } FqPieFlow::FqPieFlow () : m_deficit (0), m_status (INACTIVE), m_index (0) { NS_LOG_FUNCTION (this); } FqPieFlow::~FqPieFlow () { NS_LOG_FUNCTION (this); } void FqPieFlow::SetDeficit (uint32_t deficit) { NS_LOG_FUNCTION (this << deficit); m_deficit = deficit; } int32_t FqPieFlow::GetDeficit (void) const { NS_LOG_FUNCTION (this); return m_deficit; } void FqPieFlow::IncreaseDeficit (int32_t deficit) { NS_LOG_FUNCTION (this << deficit); m_deficit += deficit; } void FqPieFlow::SetStatus (FlowStatus status) { NS_LOG_FUNCTION (this); m_status = status; } FqPieFlow::FlowStatus FqPieFlow::GetStatus (void) const { NS_LOG_FUNCTION (this); return m_status; } void FqPieFlow::SetIndex (uint32_t index) { NS_LOG_FUNCTION (this); m_index = index; } uint32_t FqPieFlow::GetIndex (void) const { return m_index; } NS_OBJECT_ENSURE_REGISTERED (FqPieQueueDisc); TypeId FqPieQueueDisc::GetTypeId (void) { static TypeId tid = TypeId ("ns3::FqPieQueueDisc") .SetParent<QueueDisc> () .SetGroupName ("TrafficControl") .AddConstructor<FqPieQueueDisc> () .AddAttribute ("UseEcn", "True to use ECN (packets are marked instead of being dropped)", BooleanValue (true), MakeBooleanAccessor (&FqPieQueueDisc::m_useEcn), MakeBooleanChecker ()) .AddAttribute ("MarkEcnThreshold", "ECN marking threshold (RFC 8033 suggests 0.1 (i.e., 10%) default)", DoubleValue (0.1), MakeDoubleAccessor (&FqPieQueueDisc::m_markEcnTh), MakeDoubleChecker<double> (0, 1)) .AddAttribute ("CeThreshold", "The FqPie CE threshold for marking packets", TimeValue (Time::Max ()), MakeTimeAccessor (&FqPieQueueDisc::m_ceThreshold), MakeTimeChecker ()) .AddAttribute ("UseL4s", "True to use L4S (only ECT1 packets are marked at CE threshold)", BooleanValue (false), MakeBooleanAccessor (&FqPieQueueDisc::m_useL4s), MakeBooleanChecker ()) .AddAttribute ("MeanPktSize", "Average of packet size", UintegerValue (1000), MakeUintegerAccessor (&FqPieQueueDisc::m_meanPktSize), MakeUintegerChecker<uint32_t> ()) .AddAttribute ("A", "Value of alpha", DoubleValue (0.125), MakeDoubleAccessor (&FqPieQueueDisc::m_a), MakeDoubleChecker<double> ()) .AddAttribute ("B", "Value of beta", DoubleValue (1.25), MakeDoubleAccessor (&FqPieQueueDisc::m_b), MakeDoubleChecker<double> ()) .AddAttribute ("Tupdate", "Time period to calculate drop probability", TimeValue (Seconds (0.015)), MakeTimeAccessor (&FqPieQueueDisc::m_tUpdate), MakeTimeChecker ()) .AddAttribute ("Supdate", "Start time of the update timer", TimeValue (Seconds (0)), MakeTimeAccessor (&FqPieQueueDisc::m_sUpdate), MakeTimeChecker ()) .AddAttribute ("MaxSize", "The maximum number of packets accepted by this queue disc", QueueSizeValue (QueueSize ("10240p")), MakeQueueSizeAccessor (&QueueDisc::SetMaxSize, &QueueDisc::GetMaxSize), MakeQueueSizeChecker ()) .AddAttribute ("DequeueThreshold", "Minimum queue size in bytes before dequeue rate is measured", UintegerValue (16384), MakeUintegerAccessor (&FqPieQueueDisc::m_dqThreshold), MakeUintegerChecker<uint32_t> ()) .AddAttribute ("QueueDelayReference", "Desired queue delay", TimeValue (Seconds (0.015)), MakeTimeAccessor (&FqPieQueueDisc::m_qDelayRef), MakeTimeChecker ()) .AddAttribute ("MaxBurstAllowance", "Current max burst allowance before random drop", TimeValue (Seconds (0.15)), MakeTimeAccessor (&FqPieQueueDisc::m_maxBurst), MakeTimeChecker ()) .AddAttribute ("UseDequeueRateEstimator", "Enable/Disable usage of Dequeue Rate Estimator", BooleanValue (false), MakeBooleanAccessor (&FqPieQueueDisc::m_useDqRateEstimator), MakeBooleanChecker ()) .AddAttribute ("UseCapDropAdjustment", "Enable/Disable Cap Drop Adjustment feature mentioned in RFC 8033", BooleanValue (true), MakeBooleanAccessor (&FqPieQueueDisc::m_isCapDropAdjustment), MakeBooleanChecker ()) .AddAttribute ("UseDerandomization", "Enable/Disable Derandomization feature mentioned in RFC 8033", BooleanValue (false), MakeBooleanAccessor (&FqPieQueueDisc::m_useDerandomization), MakeBooleanChecker ()) .AddAttribute ("Flows", "The number of queues into which the incoming packets are classified", UintegerValue (1024), MakeUintegerAccessor (&FqPieQueueDisc::m_flows), MakeUintegerChecker<uint32_t> ()) .AddAttribute ("DropBatchSize", "The maximum number of packets dropped from the fat flow", UintegerValue (64), MakeUintegerAccessor (&FqPieQueueDisc::m_dropBatchSize), MakeUintegerChecker<uint32_t> ()) .AddAttribute ("Perturbation", "The salt used as an additional input to the hash function used to classify packets", UintegerValue (0), MakeUintegerAccessor (&FqPieQueueDisc::m_perturbation), MakeUintegerChecker<uint32_t> ()) .AddAttribute ("EnableSetAssociativeHash", "Enable/Disable Set Associative Hash", BooleanValue (false), MakeBooleanAccessor (&FqPieQueueDisc::m_enableSetAssociativeHash), MakeBooleanChecker ()) .AddAttribute ("SetWays", "The size of a set of queues (used by set associative hash)", UintegerValue (8), MakeUintegerAccessor (&FqPieQueueDisc::m_setWays), MakeUintegerChecker<uint32_t> ()) ; return tid; } FqPieQueueDisc::FqPieQueueDisc () : QueueDisc (QueueDiscSizePolicy::MULTIPLE_QUEUES, QueueSizeUnit::PACKETS), m_quantum (0) { NS_LOG_FUNCTION (this); } FqPieQueueDisc::~FqPieQueueDisc () { NS_LOG_FUNCTION (this); } void FqPieQueueDisc::SetQuantum (uint32_t quantum) { NS_LOG_FUNCTION (this << quantum); m_quantum = quantum; } uint32_t FqPieQueueDisc::GetQuantum (void) const { return m_quantum; } uint32_t FqPieQueueDisc::SetAssociativeHash (uint32_t flowHash) { NS_LOG_FUNCTION (this << flowHash); uint32_t h = (flowHash % m_flows); uint32_t innerHash = h % m_setWays; uint32_t outerHash = h - innerHash; for (uint32_t i = outerHash; i < outerHash + m_setWays; i++) { auto it = m_flowsIndices.find (i); if (it == m_flowsIndices.end () || (m_tags.find (i) != m_tags.end () && m_tags[i] == flowHash) || StaticCast<FqPieFlow> (GetQueueDiscClass (it->second))->GetStatus () == FqPieFlow::INACTIVE) { // this queue has not been created yet or is associated with this flow // or is inactive, hence we can use it m_tags[i] = flowHash; return i; } } // all the queues of the set are used. Use the first queue of the set m_tags[outerHash] = flowHash; return outerHash; } bool FqPieQueueDisc::DoEnqueue (Ptr<QueueDiscItem> item) { NS_LOG_FUNCTION (this << item); uint32_t flowHash, h; if (GetNPacketFilters () == 0) { flowHash = item->Hash (m_perturbation); } else { int32_t ret = Classify (item); if (ret != PacketFilter::PF_NO_MATCH) { flowHash = static_cast<uint32_t> (ret); } else { NS_LOG_ERROR ("No filter has been able to classify this packet, drop it."); DropBeforeEnqueue (item, UNCLASSIFIED_DROP); return false; } } if (m_enableSetAssociativeHash) { h = SetAssociativeHash (flowHash); } else { h = flowHash % m_flows; } Ptr<FqPieFlow> flow; if (m_flowsIndices.find (h) == m_flowsIndices.end ()) { NS_LOG_DEBUG ("Creating a new flow queue with index " << h); flow = m_flowFactory.Create<FqPieFlow> (); Ptr<QueueDisc> qd = m_queueDiscFactory.Create<QueueDisc> (); // If Pie, Set values of PieQueueDisc to match this QueueDisc Ptr<PieQueueDisc> pie = qd->GetObject<PieQueueDisc> (); if (pie) { pie->SetAttribute ("UseEcn", BooleanValue (m_useEcn)); pie->SetAttribute ("CeThreshold", TimeValue (m_ceThreshold)); pie->SetAttribute ("UseL4s", BooleanValue (m_useL4s)); } qd->Initialize (); flow->SetQueueDisc (qd); flow->SetIndex (h); AddQueueDiscClass (flow); m_flowsIndices[h] = GetNQueueDiscClasses () - 1; } else { flow = StaticCast<FqPieFlow> (GetQueueDiscClass (m_flowsIndices[h])); } if (flow->GetStatus () == FqPieFlow::INACTIVE) { flow->SetStatus (FqPieFlow::NEW_FLOW); flow->SetDeficit (m_quantum); m_newFlows.push_back (flow); } flow->GetQueueDisc ()->Enqueue (item); NS_LOG_DEBUG ("Packet enqueued into flow " << h << "; flow index " << m_flowsIndices[h]); if (GetCurrentSize () > GetMaxSize ()) { NS_LOG_DEBUG ("Overload; enter FqPieDrop ()"); FqPieDrop (); } return true; } Ptr<QueueDiscItem> FqPieQueueDisc::DoDequeue (void) { NS_LOG_FUNCTION (this); Ptr<FqPieFlow> flow; Ptr<QueueDiscItem> item; do { bool found = false; while (!found && !m_newFlows.empty ()) { flow = m_newFlows.front (); if (flow->GetDeficit () <= 0) { NS_LOG_DEBUG ("Increase deficit for new flow index " << flow->GetIndex ()); flow->IncreaseDeficit (m_quantum); flow->SetStatus (FqPieFlow::OLD_FLOW); m_oldFlows.push_back (flow); m_newFlows.pop_front (); } else { NS_LOG_DEBUG ("Found a new flow " << flow->GetIndex () << " with positive deficit"); found = true; } } while (!found && !m_oldFlows.empty ()) { flow = m_oldFlows.front (); if (flow->GetDeficit () <= 0) { NS_LOG_DEBUG ("Increase deficit for old flow index " << flow->GetIndex ()); flow->IncreaseDeficit (m_quantum); m_oldFlows.push_back (flow); m_oldFlows.pop_front (); } else { NS_LOG_DEBUG ("Found an old flow " << flow->GetIndex () << " with positive deficit"); found = true; } } if (!found) { NS_LOG_DEBUG ("No flow found to dequeue a packet"); return 0; } item = flow->GetQueueDisc ()->Dequeue (); if (!item) { NS_LOG_DEBUG ("Could not get a packet from the selected flow queue"); if (!m_newFlows.empty ()) { flow->SetStatus (FqPieFlow::OLD_FLOW); m_oldFlows.push_back (flow); m_newFlows.pop_front (); } else { flow->SetStatus (FqPieFlow::INACTIVE); m_oldFlows.pop_front (); } } else { NS_LOG_DEBUG ("Dequeued packet " << item->GetPacket ()); } } while (item == 0); flow->IncreaseDeficit (item->GetSize () * -1); return item; } bool FqPieQueueDisc::CheckConfig (void) { NS_LOG_FUNCTION (this); if (GetNQueueDiscClasses () > 0) { NS_LOG_ERROR ("FqPieQueueDisc cannot have classes"); return false; } if (GetNInternalQueues () > 0) { NS_LOG_ERROR ("FqPieQueueDisc cannot have internal queues"); return false; } // we are at initialization time. If the user has not set a quantum value, // set the quantum to the MTU of the device (if any) if (!m_quantum) { Ptr<NetDeviceQueueInterface> ndqi = GetNetDeviceQueueInterface (); Ptr<NetDevice> dev; // if the NetDeviceQueueInterface object is aggregated to a // NetDevice, get the MTU of such NetDevice if (ndqi && (dev = ndqi->GetObject<NetDevice> ())) { m_quantum = dev->GetMtu (); NS_LOG_DEBUG ("Setting the quantum to the MTU of the device: " << m_quantum); } if (!m_quantum) { NS_LOG_ERROR ("The quantum parameter cannot be null"); return false; } } if (m_enableSetAssociativeHash && (m_flows % m_setWays != 0)) { NS_LOG_ERROR ("The number of queues must be an integer multiple of the size " "of the set of queues used by set associative hash"); return false; } // If UseL4S attribute is enabled then CE threshold must be set. if (m_useL4s) { NS_ABORT_MSG_IF (m_ceThreshold == Time::Max (), "CE threshold not set"); if (m_useEcn == false) { NS_LOG_WARN ("Enabling ECN as L4S mode is enabled"); } } return true; } void FqPieQueueDisc::InitializeParams (void) { NS_LOG_FUNCTION (this); m_flowFactory.SetTypeId ("ns3::FqPieFlow"); m_queueDiscFactory.SetTypeId ("ns3::PieQueueDisc"); m_queueDiscFactory.Set ("MaxSize", QueueSizeValue (GetMaxSize ())); m_queueDiscFactory.Set ("MeanPktSize", UintegerValue(m_meanPktSize)); m_queueDiscFactory.Set ("A", DoubleValue (m_a)); m_queueDiscFactory.Set ("B", DoubleValue (m_b)); m_queueDiscFactory.Set ("Tupdate", TimeValue (m_tUpdate)); m_queueDiscFactory.Set ("Supdate", TimeValue (m_sUpdate)); m_queueDiscFactory.Set ("DequeueThreshold", UintegerValue (m_dqThreshold)); m_queueDiscFactory.Set ("QueueDelayReference", TimeValue (m_qDelayRef)); m_queueDiscFactory.Set ("MaxBurstAllowance", TimeValue (m_maxBurst)); m_queueDiscFactory.Set ("UseDequeueRateEstimator", BooleanValue (m_useDqRateEstimator)); m_queueDiscFactory.Set ("UseCapDropAdjustment", BooleanValue (m_isCapDropAdjustment)); m_queueDiscFactory.Set ("UseDerandomization", BooleanValue (m_useDerandomization)); } uint32_t FqPieQueueDisc::FqPieDrop (void) { NS_LOG_FUNCTION (this); uint32_t maxBacklog = 0, index = 0; Ptr<QueueDisc> qd; /* Queue is full! Find the fat flow and drop packet(s) from it */ for (uint32_t i = 0; i < GetNQueueDiscClasses (); i++) { qd = GetQueueDiscClass (i)->GetQueueDisc (); uint32_t bytes = qd->GetNBytes (); if (bytes > maxBacklog) { maxBacklog = bytes; index = i; } } /* Our goal is to drop half of this fat flow backlog */ uint32_t len = 0, count = 0, threshold = maxBacklog >> 1; qd = GetQueueDiscClass (index)->GetQueueDisc (); Ptr<QueueDiscItem> item; do { NS_LOG_DEBUG ("Drop packet (overflow); count: " << count << " len: " << len << " threshold: " << threshold); item = qd->GetInternalQueue (0)->Dequeue (); DropAfterDequeue (item, OVERLIMIT_DROP); len += item->GetSize (); } while (++count < m_dropBatchSize && len < threshold); return index; } } // namespace ns3