traffic-control: (merges !362) Add FqCobalt queue disc

FqCobalt includes COBALT enhancements to CoDel (Blue enhancement) and
set-associative hash, as used in the Linux cake queue disc.  An L4S mode is
also included.
This commit is contained in:
Bhaskar Kataria
2020-07-23 00:13:32 +05:30
committed by Tom Henderson
parent fe26cf8b46
commit 3d7a42d42e
12 changed files with 2326 additions and 37 deletions

View File

@@ -54,7 +54,7 @@ us a note on ns-developers mailing list.</p>
<h1>Changes from ns-3.33 to ns-3.34</h1>
<h2>New API:</h2>
<ul>
<li></li>
<li>Added <b>FqCobalt</b> queue disc with L4S features and set associative hash.</li>
</ul>
<h2>Changes to existing API:</h2>
<ul>

View File

@@ -31,6 +31,7 @@ New user-visible features
- (wifi) Holland PHY configuration has been removed from the model
- (wifi) HT Greenfield (HT_GF) preamble support has been removed from the model
- (wifi) Some wifi/src/model files were moved to the relevant subfolders (non-ht, ht, vht, he, and rate-control)
- (traffic-control) Added FqCobalt queue disc with L4S features and set associative hash.
Bugs fixed
----------

File diff suppressed because it is too large Load Diff

View File

@@ -31,6 +31,7 @@ def build(bld):
test_test.source = [
'csma-system-test-suite.cc',
'ns3tc/fq-codel-queue-disc-test-suite.cc',
'ns3tc/fq-cobalt-queue-disc-test-suite.cc',
'ns3tc/pfifo-fast-queue-disc-test-suite.cc',
'ns3tcp/ns3tcp-cwnd-test-suite.cc',
'ns3tcp/ns3tcp-interop-test-suite.cc',

View File

@@ -37,10 +37,14 @@ Linux model of COBALT is provided in ([Cobalt19]_).
`m_stats.qLimDrop`.
* ``CobaltQueueDisc::ShouldDrop ()``: This routine is
``CobaltQueueDisc::DoDequeue()``'s helper routine that determines whether a
packet should be dropped or not based on its sojourn time. If the sojourn
time goes above `m_target` and remains above continuously for at least
`m_interval`, the routine returns ``true`` indicating that it is OK
to drop the packet. ``Otherwise, it returns ``false``. This routine
packet should be dropped or not based on its sojourn time. If L4S mode is
enabled then if the packet is ECT1 is checked and if delay is greater than
CE threshold then the packet is marked and returns ``false``.
If the sojourn time goes above `m_target` and remains above continuously
for at least `m_interval`, the routine returns ``true`` indicating that it
is OK to drop the packet. ``Otherwise, it returns ``false``. If L4S mode
is turned off and CE threshold marking is enabled, then if the delay is
greater than CE threshold, packet is marked. This routine
decides if a packet should be dropped based on the dropping state of
CoDel and drop probability of BLUE. The idea is to have both algorithms
running in parallel and their effectiveness is decided by their respective
@@ -53,10 +57,6 @@ Linux model of COBALT is provided in ([Cobalt19]_).
Otherwise Cobalt will take the next packet from the queue and calculate
its drop state by running CoDel and BLUE in parallel till there are none
left to drop.
* class :cpp:class:`CobaltTimestampTag`: This class implements the timestamp
tagging for a packet. This tag is used to compute the packet's sojourn time
(the difference between the time the packet is dequeued and the time it is
pushed into the queue).
@@ -94,6 +94,8 @@ The key attributes that the CobaltQueue Disc class holds include the following:
* ``Pdrop:`` Value of drop probability.
* ``Increment:`` Increment value of drop probability. Default value is 1./256 .
* ``Decrement:`` Decrement value of drop probability. Default value is 1./4096 .
* ``CeThreshold:`` The CoDel CE threshold for marking packets.
* ``UseL4s:`` True to use L4S (only ECT1 packets are marked at CE threshold).
* ``Count:`` Cobalt count.
* ``DropState:`` Dropping state of Cobalt. Default value is false.
* ``Sojourn:`` Per packet time spent in the queue.
@@ -120,7 +122,8 @@ The suite includes 2 test cases:
* Test 1: Simple enqueue/dequeue with no drops.
* Test 2: Change of BLUE's drop probability upon queue full
(Activation of Blue).
* Test 3: Test for ECN marking of packets
* Test 3: This test verfies ECN marking.
* Test 4: CE threshold marking test.
The test suite can be run using the following commands:

View File

@@ -0,0 +1,76 @@
.. include:: replace.txt
.. highlight:: cpp
.. highlight:: bash
FqCobalt queue disc
------------------
This chapter describes the FqCobalt ([Hoe16]_) queue disc implementation in |ns3|.
The FlowQueue-Cobalt (FQ-Cobalt) algorithm is similar to FlowQueue-CoDel (FQ-CoDel) algorithm available in ns-3-dev/src/traffic-control/doc/fq-codel.rst.
The documentation for Cobalt is available in ns-3-dev/src/traffic-control/doc/cobalt.rst.
FqCobalt is one of the crucial piece needed to complete to CAKE module `<https://ieeexplore.ieee.org/document/8475045>`
Model Description
*****************
The source code for the FqCobalt queue disc is located in the directory
``src/traffic-control/model`` and consists of 2 files `fq-cobalt-queue-disc.h`
and `fq-cobalt-queue-disc.cc` defining a FqCobaltQueueDisc class and a helper
FqCobaltFlow class. The code was ported to |ns3| based on Linux kernel code
implemented by Jonathan Morton.
The Model Description is similar to the FqCoDel documentation mentioned above.
References
==========
.. [CAK16] https://github.com/torvalds/linux/blob/master/net/sched/sch_cake.c , Implementation of CAKE in Linux
Attributes
==========
The key attributes that the FqCobaltQueue class holds include the following:
Most of the key attributes are similar to the FqCoDel implementation mentioned above.
Some differences are:
Absence of ``MinBytes`` parameter.
Some extra parameters are
* ``Pdrop:`` Value of drop probability.
* ``Increment:`` Increment value of drop probability. Default value is 1./256 .
* ``Decrement:`` Decrement value of drop probability. Default value is 1./4096 .
* ``BlueThreshold:`` The Threshold after which Blue is enabled. Default value is 400ms.
Note that if the user wants to disable Blue Enhancement then the user can set
it to a large value for example Time::Max().
Examples
========
A typical usage pattern is to create a traffic control helper and to configure type
and attributes of queue disc and filters from the helper. For example, FqCobalt
can be configured as follows:
.. sourcecode:: cpp
TrafficControlHelper tch;
tch.SetRootQueueDisc ("ns3::FqCobaltQueueDisc", "DropBatchSize", UintegerValue (1)
"Perturbation", UintegerValue (256));
QueueDiscContainer qdiscs = tch.Install (devices);
Validation
**********
The FqCobalt model is tested using :cpp:class:`FqCobaltQueueDiscTestSuite` class defined in `src/test/ns3tc/codel-queue-test-suite.cc`.
The tests are similar to the ones for FqCoDel queue disc mentioned in first section of this document.
The test suite can be run using the following commands::
$ ./waf configure --enable-examples --enable-tests
$ ./waf build
$ ./test.py -s fq-cobalt-queue-disc
or::
$ NS_LOG="FqCobaltQueueDisc" ./waf --run "test-runner --suite=fq-cobalt-queue-disc"

View File

@@ -84,6 +84,21 @@ TypeId CobaltQueueDisc::GetTypeId (void)
DoubleValue (1. / 4096),
MakeDoubleAccessor (&CobaltQueueDisc::m_decrement),
MakeDoubleChecker<double> ())
.AddAttribute ("CeThreshold",
"The CoDel CE threshold for marking packets",
TimeValue (Time::Max ()),
MakeTimeAccessor (&CobaltQueueDisc::m_ceThreshold),
MakeTimeChecker ())
.AddAttribute ("UseL4s",
"True to use L4S (only ECT1 packets are marked at CE threshold)",
BooleanValue (false),
MakeBooleanAccessor (&CobaltQueueDisc::m_useL4s),
MakeBooleanChecker ())
.AddAttribute ("BlueThreshold",
"The Threshold after which Blue is enabled",
TimeValue (MilliSeconds (400)),
MakeTimeAccessor (&CobaltQueueDisc::m_blueThreshold),
MakeTimeChecker ())
.AddTraceSource ("Count",
"Cobalt count",
MakeTraceSourceAccessor (&CobaltQueueDisc::m_count),
@@ -436,6 +451,31 @@ bool CobaltQueueDisc::CobaltShouldDrop (Ptr<QueueDiscItem> item, int64_t now)
int64_t schedule = now - m_dropNext;
bool over_target = CoDelTimeAfter (sojournTime, Time2CoDel (m_target));
bool next_due = m_count && schedule >= 0;
bool isMarked = false;
// If L4S mode is enabled then check if the packet is ECT1 or CE and
// if sojourn time is greater than CE threshold then the packet is marked.
// If packet is marked succesfully then the CoDel steps can be skipped.
if (item && m_useL4s)
{
uint8_t tosByte = 0;
if (item->GetUint8Value (QueueItem::IP_DSFIELD, tosByte) && (((tosByte & 0x3) == 1) || (tosByte & 0x3) == 3))
{
if ((tosByte & 0x3) == 1)
{
NS_LOG_DEBUG ("ECT1 packet " << static_cast<uint16_t> (tosByte & 0x3));
}
else
{
NS_LOG_DEBUG ("CE packet " << static_cast<uint16_t> (tosByte & 0x3));
}
if (CoDelTimeAfter (sojournTime, Time2CoDel (m_ceThreshold)) && Mark (item, CE_THRESHOLD_EXCEEDED_MARK))
{
NS_LOG_LOGIC ("Marking due to CeThreshold " << m_ceThreshold.GetSeconds ());
}
return false;
}
}
if (over_target)
{
@@ -458,7 +498,8 @@ bool CobaltQueueDisc::CobaltShouldDrop (Ptr<QueueDiscItem> item, int64_t now)
{
/* Check for marking possibility only if BLUE decides NOT to drop. */
/* Check if router and packet, both have ECN enabled. Only if this is true, mark the packet. */
drop = !(m_useEcn && Mark (item, FORCED_MARK));
isMarked = (m_useEcn && Mark (item, FORCED_MARK));
drop = !isMarked;
m_count = max (m_count, m_count + 1);
@@ -477,6 +518,22 @@ bool CobaltQueueDisc::CobaltShouldDrop (Ptr<QueueDiscItem> item, int64_t now)
next_due = m_count && schedule >= 0;
}
}
// If CE threshold is enabled then isMarked flag is used to determine whether
// packet is marked and if the packet is marked then a second attempt at marking should be suppressed.
// If UseL4S attribute is enabled then ECT0 packets should not be marked.
if (!isMarked && !m_useL4s && m_useEcn && CoDelTimeAfter (sojournTime, Time2CoDel (m_ceThreshold)) && Mark (item, CE_THRESHOLD_EXCEEDED_MARK))
{
NS_LOG_LOGIC ("Marking due to CeThreshold " << m_ceThreshold.GetSeconds ());
}
// Enable Blue Enhancement if sojourn time is greater than blueThreshold and its been m_target time until the last time blue was updated
if (CoDelTimeAfter (sojournTime, Time2CoDel (m_blueThreshold)) && CoDelTimeAfter ((now - m_lastUpdateTimeBlue), Time2CoDel (m_target)))
{
m_Pdrop = min (m_Pdrop + m_increment, (double)1.0);
m_lastUpdateTimeBlue = now;
}
/* Simple BLUE implementation. Lack of ECN is deliberate. */
if (m_Pdrop)
{

View File

@@ -103,6 +103,7 @@ public:
static constexpr const char* TARGET_EXCEEDED_DROP = "Target exceeded drop"; //!< Sojourn time above target
static constexpr const char* OVERLIMIT_DROP = "Overlimit drop"; //!< Overlimit dropped packet
static constexpr const char* FORCED_MARK = "forcedMark"; //!< forced marks by Codel on ECN-enabled
static constexpr const char* CE_THRESHOLD_EXCEEDED_MARK = "CE threshold exceeded mark"; //!< Sojourn time above CE threshold
/**
* \brief Get the drop probability of Blue
@@ -227,6 +228,9 @@ private:
Time m_interval; //!< 100 ms sliding minimum time window width
Time m_target; //!< 5 ms target queue delay
bool m_useEcn; //!< True if ECN is used (packets are marked instead of being dropped)
Time m_ceThreshold; //!< Threshold above which to CE mark
bool m_useL4s; //!< True if L4S is used (ECT1 packets are marked at CE threshold)
Time m_blueThreshold; //!< Threshold to enable blue enhancement
// Blue parameters
// Maintained by Cobalt

View File

@@ -0,0 +1,523 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2016 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
*
* Authors: Pasquale Imputato <p.imputato@gmail.com>
* Stefano Avallone <stefano.avallone@unina.it>
*/
#include "ns3/log.h"
#include "ns3/string.h"
#include "ns3/queue.h"
#include "fq-cobalt-queue-disc.h"
#include "cobalt-queue-disc.h"
#include "ns3/net-device-queue-interface.h"
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("FqCobaltQueueDisc");
NS_OBJECT_ENSURE_REGISTERED (FqCobaltFlow);
TypeId FqCobaltFlow::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::FqCobaltFlow")
.SetParent<QueueDiscClass> ()
.SetGroupName ("TrafficControl")
.AddConstructor<FqCobaltFlow> ()
;
return tid;
}
FqCobaltFlow::FqCobaltFlow ()
: m_deficit (0),
m_status (INACTIVE),
m_index (0)
{
NS_LOG_FUNCTION (this);
}
FqCobaltFlow::~FqCobaltFlow ()
{
NS_LOG_FUNCTION (this);
}
void
FqCobaltFlow::SetDeficit (uint32_t deficit)
{
NS_LOG_FUNCTION (this << deficit);
m_deficit = deficit;
}
int32_t
FqCobaltFlow::GetDeficit (void) const
{
NS_LOG_FUNCTION (this);
return m_deficit;
}
void
FqCobaltFlow::IncreaseDeficit (int32_t deficit)
{
NS_LOG_FUNCTION (this << deficit);
m_deficit += deficit;
}
void
FqCobaltFlow::SetStatus (FlowStatus status)
{
NS_LOG_FUNCTION (this);
m_status = status;
}
FqCobaltFlow::FlowStatus
FqCobaltFlow::GetStatus (void) const
{
NS_LOG_FUNCTION (this);
return m_status;
}
void
FqCobaltFlow::SetIndex (uint32_t index)
{
NS_LOG_FUNCTION (this);
m_index = index;
}
uint32_t
FqCobaltFlow::GetIndex (void) const
{
return m_index;
}
NS_OBJECT_ENSURE_REGISTERED (FqCobaltQueueDisc);
TypeId FqCobaltQueueDisc::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::FqCobaltQueueDisc")
.SetParent<QueueDisc> ()
.SetGroupName ("TrafficControl")
.AddConstructor<FqCobaltQueueDisc> ()
.AddAttribute ("UseEcn",
"True to use ECN (packets are marked instead of being dropped)",
BooleanValue (true),
MakeBooleanAccessor (&FqCobaltQueueDisc::m_useEcn),
MakeBooleanChecker ())
.AddAttribute ("Interval",
"The CoDel algorithm interval for each FqCobalt queue",
StringValue ("100ms"),
MakeStringAccessor (&FqCobaltQueueDisc::m_interval),
MakeStringChecker ())
.AddAttribute ("Target",
"The CoDel algorithm target queue delay for each FqCobalt queue",
StringValue ("5ms"),
MakeStringAccessor (&FqCobaltQueueDisc::m_target),
MakeStringChecker ())
.AddAttribute ("MaxSize",
"The maximum number of packets accepted by this queue disc",
QueueSizeValue (QueueSize ("10240p")),
MakeQueueSizeAccessor (&QueueDisc::SetMaxSize,
&QueueDisc::GetMaxSize),
MakeQueueSizeChecker ())
.AddAttribute ("Flows",
"The number of queues into which the incoming packets are classified",
UintegerValue (1024),
MakeUintegerAccessor (&FqCobaltQueueDisc::m_flows),
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("DropBatchSize",
"The maximum number of packets dropped from the fat flow",
UintegerValue (64),
MakeUintegerAccessor (&FqCobaltQueueDisc::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 (&FqCobaltQueueDisc::m_perturbation),
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("CeThreshold",
"The FqCobalt CE threshold for marking packets",
TimeValue (Time::Max ()),
MakeTimeAccessor (&FqCobaltQueueDisc::m_ceThreshold),
MakeTimeChecker ())
.AddAttribute ("EnableSetAssociativeHash",
"Enable/Disable Set Associative Hash",
BooleanValue (false),
MakeBooleanAccessor (&FqCobaltQueueDisc::m_enableSetAssociativeHash),
MakeBooleanChecker ())
.AddAttribute ("SetWays",
"The size of a set of queues (used by set associative hash)",
UintegerValue (8),
MakeUintegerAccessor (&FqCobaltQueueDisc::m_setWays),
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("UseL4s",
"True to use L4S (only ECT1 packets are marked at CE threshold)",
BooleanValue (false),
MakeBooleanAccessor (&FqCobaltQueueDisc::m_useL4s),
MakeBooleanChecker ())
.AddAttribute ("Pdrop",
"Marking Probability",
DoubleValue (0),
MakeDoubleAccessor (&FqCobaltQueueDisc::m_Pdrop),
MakeDoubleChecker<double> ())
.AddAttribute ("Increment",
"Pdrop increment value",
DoubleValue (1. / 256),
MakeDoubleAccessor (&FqCobaltQueueDisc::m_increment),
MakeDoubleChecker<double> ())
.AddAttribute ("Decrement",
"Pdrop decrement Value",
DoubleValue (1. / 4096),
MakeDoubleAccessor (&FqCobaltQueueDisc::m_decrement),
MakeDoubleChecker<double> ())
.AddAttribute ("BlueThreshold",
"The Threshold after which Blue is enabled",
TimeValue (MilliSeconds (400)),
MakeTimeAccessor (&FqCobaltQueueDisc::m_blueThreshold),
MakeTimeChecker ())
;
return tid;
}
FqCobaltQueueDisc::FqCobaltQueueDisc ()
: QueueDisc (QueueDiscSizePolicy::MULTIPLE_QUEUES, QueueSizeUnit::PACKETS),
m_quantum (0)
{
NS_LOG_FUNCTION (this);
}
FqCobaltQueueDisc::~FqCobaltQueueDisc ()
{
NS_LOG_FUNCTION (this);
}
void
FqCobaltQueueDisc::SetQuantum (uint32_t quantum)
{
NS_LOG_FUNCTION (this << quantum);
m_quantum = quantum;
}
uint32_t
FqCobaltQueueDisc::GetQuantum (void) const
{
return m_quantum;
}
uint32_t
FqCobaltQueueDisc::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<FqCobaltFlow> (GetQueueDiscClass (it->second))->GetStatus () == FqCobaltFlow::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
FqCobaltQueueDisc::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<FqCobaltFlow> flow;
if (m_flowsIndices.find (h) == m_flowsIndices.end ())
{
NS_LOG_DEBUG ("Creating a new flow queue with index " << h);
flow = m_flowFactory.Create<FqCobaltFlow> ();
Ptr<QueueDisc> qd = m_queueDiscFactory.Create<QueueDisc> ();
// If Cobalt, Set values of CobaltQueueDisc to match this QueueDisc
Ptr<CobaltQueueDisc> cobalt = qd->GetObject<CobaltQueueDisc> ();
if (cobalt)
{
cobalt->SetAttribute ("UseEcn", BooleanValue (m_useEcn));
cobalt->SetAttribute ("CeThreshold", TimeValue (m_ceThreshold));
cobalt->SetAttribute ("UseL4s", BooleanValue (m_useL4s));
cobalt->SetAttribute ("BlueThreshold", TimeValue (m_blueThreshold));
}
qd->Initialize ();
flow->SetQueueDisc (qd);
flow->SetIndex (h);
AddQueueDiscClass (flow);
m_flowsIndices[h] = GetNQueueDiscClasses () - 1;
}
else
{
flow = StaticCast<FqCobaltFlow> (GetQueueDiscClass (m_flowsIndices[h]));
}
if (flow->GetStatus () == FqCobaltFlow::INACTIVE)
{
flow->SetStatus (FqCobaltFlow::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 FqCobaltDrop ()");
FqCobaltDrop ();
}
return true;
}
Ptr<QueueDiscItem>
FqCobaltQueueDisc::DoDequeue (void)
{
NS_LOG_FUNCTION (this);
Ptr<FqCobaltFlow> 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 (FqCobaltFlow::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 (FqCobaltFlow::OLD_FLOW);
m_oldFlows.push_back (flow);
m_newFlows.pop_front ();
}
else
{
flow->SetStatus (FqCobaltFlow::INACTIVE);
m_oldFlows.pop_front ();
}
}
else
{
NS_LOG_DEBUG ("Dequeued packet " << item->GetPacket ());
}
} while (item == 0);
flow->IncreaseDeficit (item->GetSize () * -1);
return item;
}
bool
FqCobaltQueueDisc::CheckConfig (void)
{
NS_LOG_FUNCTION (this);
if (GetNQueueDiscClasses () > 0)
{
NS_LOG_ERROR ("FqCobaltQueueDisc cannot have classes");
return false;
}
if (GetNInternalQueues () > 0)
{
NS_LOG_ERROR ("FqCobaltQueueDisc 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
FqCobaltQueueDisc::InitializeParams (void)
{
NS_LOG_FUNCTION (this);
m_flowFactory.SetTypeId ("ns3::FqCobaltFlow");
m_queueDiscFactory.SetTypeId ("ns3::CobaltQueueDisc");
m_queueDiscFactory.Set ("MaxSize", QueueSizeValue (GetMaxSize ()));
m_queueDiscFactory.Set ("Interval", StringValue (m_interval));
m_queueDiscFactory.Set ("Target", StringValue (m_target));
m_queueDiscFactory.Set ("Pdrop", DoubleValue (m_Pdrop));
m_queueDiscFactory.Set ("Increment", DoubleValue (m_increment));
m_queueDiscFactory.Set ("Decrement", DoubleValue (m_decrement));
}
uint32_t
FqCobaltQueueDisc::FqCobaltDrop (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

View File

@@ -0,0 +1,197 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2020 NITK Surathkal
*
* 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: Bhaskar Kataria <bhaskar.k7920@gmail.com>
* Tom Henderson <tomhend@u.washington.edu>
* Mohit P. Tahiliani <tahiliani@nitk.edu.in>
* Vivek Jain <jain.vivek.anand@gmail.com>
* Ankit Deepak <adadeepak8@gmail.com>
*/
#ifndef FQ_COBALT_QUEUE_DISC
#define FQ_COBALT_QUEUE_DISC
#include "ns3/queue-disc.h"
#include "ns3/object-factory.h"
#include <list>
#include <map>
namespace ns3 {
/**
* \ingroup traffic-control
*
* \brief A flow queue used by the FqCobalt queue disc
*/
class FqCobaltFlow : public QueueDiscClass {
public:
/**
* \brief Get the type ID.
* \return the object TypeId
*/
static TypeId GetTypeId (void);
/**
* \brief FqCobaltFlow constructor
*/
FqCobaltFlow ();
virtual ~FqCobaltFlow ();
/**
* \enum FlowStatus
* \brief Used to determine the status of this flow queue
*/
enum FlowStatus
{
INACTIVE,
NEW_FLOW,
OLD_FLOW
};
/**
* \brief Set the deficit for this flow
* \param deficit the deficit for this flow
*/
void SetDeficit (uint32_t deficit);
/**
* \brief Get the deficit for this flow
* \return the deficit for this flow
*/
int32_t GetDeficit (void) const;
/**
* \brief Increase the deficit for this flow
* \param deficit the amount by which the deficit is to be increased
*/
void IncreaseDeficit (int32_t deficit);
/**
* \brief Set the status for this flow
* \param status the status for this flow
*/
void SetStatus (FlowStatus status);
/**
* \brief Get the status of this flow
* \return the status of this flow
*/
FlowStatus GetStatus (void) const;
/**
* \brief Set the index for this flow
* \param index the index for this flow
*/
void SetIndex (uint32_t index);
/**
* \brief Get the index of this flow
* \return the index of this flow
*/
uint32_t GetIndex (void) const;
private:
int32_t m_deficit; //!< the deficit for this flow
FlowStatus m_status; //!< the status of this flow
uint32_t m_index; //!< the index for this flow
};
/**
* \ingroup traffic-control
*
* \brief A FqCobalt packet queue disc
*/
class FqCobaltQueueDisc : public QueueDisc {
public:
/**
* \brief Get the type ID.
* \return the object TypeId
*/
static TypeId GetTypeId (void);
/**
* \brief FqCobaltQueueDisc constructor
*/
FqCobaltQueueDisc ();
virtual ~FqCobaltQueueDisc ();
/**
* \brief Set the quantum value.
*
* \param quantum The number of bytes each queue gets to dequeue on each round of the scheduling algorithm
*/
void SetQuantum (uint32_t quantum);
/**
* \brief Get the quantum value.
*
* \returns The number of bytes each queue gets to dequeue on each round of the scheduling algorithm
*/
uint32_t GetQuantum (void) const;
// Reasons for dropping packets
static constexpr const char* UNCLASSIFIED_DROP = "Unclassified drop"; //!< No packet filter able to classify packet
static constexpr const char* OVERLIMIT_DROP = "Overlimit drop"; //!< Overlimit dropped packets
private:
virtual bool DoEnqueue (Ptr<QueueDiscItem> item);
virtual Ptr<QueueDiscItem> DoDequeue (void);
virtual bool CheckConfig (void);
virtual void InitializeParams (void);
/**
* \brief Drop a packet from the head of the queue with the largest current byte count
* \return the index of the queue with the largest current byte count
*/
uint32_t FqCobaltDrop (void);
/**
* Compute the index of the queue for the flow having the given flowHash,
* according to the set associative hash approach.
*
* \param flowHash the hash of the flow 5-tuple
* \return the index of the queue for the given flow
*/
uint32_t SetAssociativeHash (uint32_t flowHash);
std::string m_interval; //!< CoDel interval attribute
std::string m_target; //!< CoDel target attribute
uint32_t m_quantum; //!< Deficit assigned to flows at each round
uint32_t m_flows; //!< Number of flow queues
uint32_t m_setWays; //!< size of a set of queues (used by set associative hash)
uint32_t m_dropBatchSize; //!< Max number of packets dropped from the fat flow
uint32_t m_perturbation; //!< hash perturbation value
bool m_useEcn; //!< True if ECN is used (packets are marked instead of being dropped)
Time m_ceThreshold; //!< Threshold above which to CE mark
bool m_enableSetAssociativeHash; //!< whether to enable set associative hash
bool m_useL4s; //!< True if L4S is used (ECT1 packets are marked at CE threshold)
double m_increment; //!< increment value for marking probability
double m_decrement; //!< decrement value for marking probability
double m_Pdrop; //!< Drop Probability
Time m_blueThreshold; //!< Threshold to enable blue enhancement
std::list<Ptr<FqCobaltFlow> > m_newFlows; //!< The list of new flows
std::list<Ptr<FqCobaltFlow> > m_oldFlows; //!< The list of old flows
std::map<uint32_t, uint32_t> m_flowsIndices; //!< Map with the index of class for each flow
std::map<uint32_t, uint32_t> m_tags; //!< Tags used by set associative hash
ObjectFactory m_flowFactory; //!< Factory to create a new flow
ObjectFactory m_queueDiscFactory; //!< Factory to create a new queue
};
} // namespace ns3
#endif /* FQ_COBALT_QUEUE_DISC */

View File

@@ -144,6 +144,8 @@ CobaltQueueDiscBasicEnqueueDequeue::DoRun (void)
"Verify that we can actually set the attribute Interval");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Target", StringValue ("4ms")), true,
"Verify that we can actually set the attribute Target");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("BlueThreshold", TimeValue (Time::Max ())), true,
"Disable Blue enhancement");
if (m_mode == QueueSizeUnit::BYTES)
{
@@ -272,7 +274,8 @@ CobaltQueueDiscDropTest::RunDropTest (QueueSizeUnit mode)
queue = CreateObject<CobaltQueueDisc> ();
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MaxSize", QueueSizeValue (QueueSize (mode, modeSize * 100))),
true, "Verify that we can actually set the attribute MaxSize");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("BlueThreshold", TimeValue (Time::Max ())), true,
"Disable Blue enhancement");
queue->Initialize ();
if (mode == QueueSizeUnit::BYTES)
@@ -411,7 +414,8 @@ CobaltQueueDiscMarkTest::DoRun (void)
true, "Verify that we can actually set the attribute MaxSize");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("UseEcn", BooleanValue (false)),
true, "Verify that we can actually set the attribute UseEcn");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("BlueThreshold", TimeValue (Time::Max ())), true,
"Disable Blue enhancement");
queue->Initialize ();
// Not-ECT traffic to induce packet drop
@@ -436,7 +440,8 @@ CobaltQueueDiscMarkTest::DoRun (void)
true, "Verify that we can actually set the attribute MaxSize");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("UseEcn", BooleanValue (true)),
true, "Verify that we can actually set the attribute UseEcn");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("BlueThreshold", TimeValue (Time::Max ())), true,
"Disable Blue enhancement");
queue->Initialize ();
// ECN capable traffic
@@ -466,7 +471,8 @@ CobaltQueueDiscMarkTest::DoRun (void)
true, "Verify that we can actually set the attribute MaxSize");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("UseEcn", BooleanValue (true)),
true, "Verify that we can actually set the attribute UseEcn");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("BlueThreshold", TimeValue (Time::Max ())), true,
"Disable Blue enhancement");
queue->Initialize ();
// First 3 packets in the queue are ecnCapable
@@ -529,7 +535,7 @@ CobaltQueueDiscMarkTest::Dequeue (Ptr<CobaltQueueDisc> queue, uint32_t modeSize,
nPacketsBeforeFirstDrop = initialQSize;
}
}
if (testCase == 2)
if (testCase == 2)
{
if (initialMarkCount == 0 && currentTime > queue->GetTarget ())
{
@@ -540,8 +546,8 @@ CobaltQueueDiscMarkTest::Dequeue (Ptr<CobaltQueueDisc> queue, uint32_t modeSize,
NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "There should be 1 packet dequeued.");
NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops");
NS_TEST_ASSERT_MSG_EQ (currentMarkCount, 0, "We are not in dropping state."
"Sojourn time has just gone above target from below."
"Hence, there should be no marked packets");
"Sojourn time has just gone above target from below."
"Hence, there should be no marked packets");
}
else if (currentTime >= queue->GetInterval ())
{
@@ -549,9 +555,9 @@ CobaltQueueDiscMarkTest::Dequeue (Ptr<CobaltQueueDisc> queue, uint32_t modeSize,
currentDropCount = queue->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP);
currentMarkCount = queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK);
NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize,
"Sojourn time has been above target for at least interval."
"We enter the dropping state and perform initial packet marking"
"So there should be only 1 more packet dequeued.");
"Sojourn time has been above target for at least interval."
"We enter the dropping state and perform initial packet marking"
"So there should be only 1 more packet dequeued.");
NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops");
NS_TEST_EXPECT_MSG_EQ (currentMarkCount, 1, "There should be 1 marked packet");
}
@@ -563,23 +569,23 @@ CobaltQueueDiscMarkTest::Dequeue (Ptr<CobaltQueueDisc> queue, uint32_t modeSize,
currentDropCount = queue->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP);
currentMarkCount = queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK);
NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "We are in dropping state."
"Sojourn is still above target."
"There should be only 1 more packet dequeued");
"Sojourn is still above target."
"There should be only 1 more packet dequeued");
NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops");
NS_TEST_EXPECT_MSG_EQ (currentMarkCount, 2, "There should be 2 marked packet as."
"current dropnext is equal to current time.");
"current dropnext is equal to current time.");
}
else if (currentTime.GetNanoSeconds () > initialDropNext)
{
currentDropCount = queue->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP);
currentMarkCount = queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK);
NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "We are in dropping state."
"It's time for packet to be marked"
"So there should be only 1 more packet dequeued");
"It's time for packet to be marked"
"So there should be only 1 more packet dequeued");
NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops");
NS_TEST_EXPECT_MSG_EQ (currentMarkCount, 3, "There should be 3 marked packet");
NS_TEST_EXPECT_MSG_EQ (nPacketsBeforeFirstDrop, nPacketsBeforeFirstMark, "Number of packets in the queue before drop should be equal"
"to number of packets in the queue before first mark as the behavior until packet N should be the same.");
"to number of packets in the queue before first mark as the behavior until packet N should be the same.");
}
}
}
@@ -594,17 +600,17 @@ CobaltQueueDiscMarkTest::Dequeue (Ptr<CobaltQueueDisc> queue, uint32_t modeSize,
NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "There should be 1 packet dequeued.");
NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops");
NS_TEST_ASSERT_MSG_EQ (currentMarkCount, 0, "We are not in dropping state."
"Sojourn time has just gone above target from below."
"Hence, there should be no marked packets");
"Sojourn time has just gone above target from below."
"Hence, there should be no marked packets");
}
else if (currentTime >= queue->GetInterval ())
{
currentDropCount = queue->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP);
currentMarkCount = queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK);
NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize,
"Sojourn time has been above target for at least interval."
"We enter the dropping state and perform initial packet marking"
"So there should be only 1 more packet dequeued.");
"Sojourn time has been above target for at least interval."
"We enter the dropping state and perform initial packet marking"
"So there should be only 1 more packet dequeued.");
NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops");
NS_TEST_EXPECT_MSG_EQ (currentMarkCount, 1, "There should be 1 marked packet");
}
@@ -616,19 +622,19 @@ CobaltQueueDiscMarkTest::Dequeue (Ptr<CobaltQueueDisc> queue, uint32_t modeSize,
currentDropCount = queue->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP);
currentMarkCount = queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK);
NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "We are in dropping state."
"Sojourn is still above target."
"So there should be only 1 more packet dequeued");
"Sojourn is still above target."
"So there should be only 1 more packet dequeued");
NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops");
NS_TEST_EXPECT_MSG_EQ (currentMarkCount, 2, "There should be 2 marked packet"
"as dropnext is equal to current time");
"as dropnext is equal to current time");
}
else if (currentTime.GetNanoSeconds () > initialDropNext)
{
currentDropCount = queue->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP);
currentMarkCount = queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK);
NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - (m_dropNextCount + 1) * modeSize, "We are in dropping state."
"It's time for packet to be dropped as packets are not ecnCapable"
"The number of packets dequeued equals to the number of times m_dropNext is updated plus initial dequeue");
"It's time for packet to be dropped as packets are not ecnCapable"
"The number of packets dequeued equals to the number of times m_dropNext is updated plus initial dequeue");
NS_TEST_EXPECT_MSG_EQ (currentDropCount, m_dropNextCount, "The number of drops equals to the number of times m_dropNext is updated");
NS_TEST_EXPECT_MSG_EQ (currentMarkCount, 2, "There should still be only 2 marked packet");
}
@@ -637,6 +643,296 @@ CobaltQueueDiscMarkTest::Dequeue (Ptr<CobaltQueueDisc> queue, uint32_t modeSize,
}
}
/**
* \ingroup traffic-control-test
* \ingroup tests
*
* \brief Test 4: Cobalt Queue Disc CE Threshold marking Test Item
*/
class CobaltQueueDiscCeThresholdTest : public TestCase
{
public:
CobaltQueueDiscCeThresholdTest (QueueSizeUnit mode);
virtual ~CobaltQueueDiscCeThresholdTest ();
private:
virtual void DoRun (void);
/**
* \brief Enqueue function
* \param queue the queue disc
* \param size the size
* \param nPkt the number of packets
*/
void Enqueue (Ptr<CobaltQueueDisc> queue, uint32_t size, uint32_t nPkt);
/**
* \brief Enqueue with delay function
* \param queue the queue disc
* \param size the size
* \param nPkt the number of packets
* \param delay the delay between the enqueueing of the packets
*/
void EnqueueWithDelay (Ptr<CobaltQueueDisc> queue, uint32_t size, uint32_t nPkt, Time delay);
/**
* \brief Dequeue function
* \param queue the queue disc
* \param modeSize the mode size
*/
void Dequeue (Ptr<CobaltQueueDisc> queue, uint32_t modeSize);
/**
* \brief Dequeue with delay function
* \param queue the queue disc
* \param modeSize the mode size
* \param nPkt the number of packets
* \param delay the delay between the enqueueing of the packets
*/
void DequeueWithDelay (Ptr<CobaltQueueDisc> queue, uint32_t modeSize, uint32_t nPkt, Time delay);
QueueSizeUnit m_mode; ///< mode
};
CobaltQueueDiscCeThresholdTest::CobaltQueueDiscCeThresholdTest (QueueSizeUnit mode)
: TestCase ("Test CE Threshold marking")
{
m_mode = mode;
}
CobaltQueueDiscCeThresholdTest::~CobaltQueueDiscCeThresholdTest ()
{
}
void
CobaltQueueDiscCeThresholdTest::Enqueue (Ptr<CobaltQueueDisc> queue, uint32_t size, uint32_t nPkt)
{
Address dest;
for (uint32_t i = 0; i < nPkt; i++)
{
queue->Enqueue (Create<CobaltQueueDiscTestItem> (Create<Packet> (size), dest, 0, true));
}
}
void
CobaltQueueDiscCeThresholdTest::EnqueueWithDelay (Ptr<CobaltQueueDisc> queue, uint32_t size, uint32_t nPkt, Time delay)
{
for (uint32_t i = 0; i < nPkt; i++)
{
Simulator::Schedule (Time (Seconds ((i + 1) * delay.GetSeconds ())), &CobaltQueueDiscCeThresholdTest::Enqueue, this, queue, size, 1);
}
}
void
CobaltQueueDiscCeThresholdTest::Dequeue (Ptr<CobaltQueueDisc> queue, uint32_t modeSize)
{
Ptr<QueueDiscItem> item = queue->Dequeue ();
if (Simulator::Now () > MilliSeconds (11) && Simulator::Now () < MilliSeconds (28))
{
NS_TEST_EXPECT_MSG_EQ (queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::CE_THRESHOLD_EXCEEDED_MARK), 1, "There should be only 1 packet"
"mark, the delay between the enqueueing of the packets decreased after the"
"1st mark (packet enqueued at 11ms) and increased for the packet enqueued after 20.6ms."
"Queue delay remains below or equal to 1ms for the packet enqueued before 28ms");
}
if (Simulator::Now () > MilliSeconds (31) )
{
NS_TEST_EXPECT_MSG_EQ (queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::CE_THRESHOLD_EXCEEDED_MARK), 3, "There should be 3 packet"
"marks, the delay between the enqueueing of the packets decreased after 1st mark"
"(packet enqueued at 11ms) and increased for the packet enqueued after 20.6ms."
"Queue delay remains below 1ms for the packets enqueued before 28ms and increases"
"for the packets enqueued after 28ms.");
}
}
void
CobaltQueueDiscCeThresholdTest::DequeueWithDelay (Ptr<CobaltQueueDisc> queue, uint32_t modeSize, uint32_t nPkt, Time delay)
{
for (uint32_t i = 0; i < nPkt; i++)
{
Simulator::Schedule (Time (Seconds ((i + 1) * delay.GetSeconds ())), &CobaltQueueDiscCeThresholdTest::Dequeue, this, queue, modeSize);
}
}
void
CobaltQueueDiscCeThresholdTest::DoRun (void)
{
Ptr<CobaltQueueDisc> queue = CreateObject<CobaltQueueDisc> ();
uint32_t pktSize = 1000;
uint32_t modeSize = 0;
if (m_mode == QueueSizeUnit::BYTES)
{
modeSize = pktSize;
}
else if (m_mode == QueueSizeUnit::PACKETS)
{
modeSize = 1;
}
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("UseEcn", BooleanValue (true)),
true, "Verify that we can actually set the attribute UseEcn");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("CeThreshold", TimeValue (MilliSeconds (1))),
true, "Verify that we can actually set the attribute UseEcn");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("BlueThreshold", TimeValue (Time::Max ())), true,
"Disable Blue enhancement");
queue->Initialize ();
// Enqueue 11 packets every 1ms
EnqueueWithDelay (queue, pktSize, 11, MilliSeconds (1));
// With every dequeue, queue delay increases by 0.1ms as packet enqueues every 1ms while dequeues at 1.1ms
// so at 11th dequeue, the dequeued packet should be marked.
Time dequeueInterval = MicroSeconds (1100);
DequeueWithDelay (queue, modeSize, 11, dequeueInterval);
// First mark occured for the packet enqueued at 11ms, ideally TCP would decrease sending rate
// which can be simulated by increasing interval between subsequent enqueues, so packets are now enqueued with a delay 1.2ms.
Time waitUntilFirstMark = MilliSeconds(11);
Simulator::Schedule (waitUntilFirstMark, &CobaltQueueDiscCeThresholdTest::EnqueueWithDelay, this, queue, pktSize, 9, MicroSeconds (1200));
// Keep dequeueing with the same delay
Simulator::Schedule (waitUntilFirstMark, &CobaltQueueDiscCeThresholdTest::DequeueWithDelay, this, queue, modeSize, 9, dequeueInterval);
// Queue delay becomes 0.2ms for the packet enqueued at 20.6ms, time to decrease interval between subsequent enqueues,
// as ideally TCP would again start increasing sending rate
Time waitUntilDecreasingEnqueueDelay = waitUntilFirstMark + MilliSeconds(9);
Simulator::Schedule (waitUntilDecreasingEnqueueDelay, &CobaltQueueDiscCeThresholdTest::EnqueueWithDelay, this, queue, pktSize, 10, MilliSeconds (1));
// Keep dequeueing with the same delay
Simulator::Schedule (waitUntilFirstMark, &CobaltQueueDiscCeThresholdTest::DequeueWithDelay, this, queue, modeSize, 10, dequeueInterval);
Simulator::Run ();
Simulator::Destroy ();
}
/**
* \ingroup traffic-control-test
* \ingroup tests
*
* \brief Test 5: Cobalt Queue Disc Enhanced Blue Test Item
* This test checks that the Blue Enhancement is working correctly. This test checks that Pdrop should increase as expected with
* a certain number of dropped packets which is fixed by assigning a stream to the uniform random variable.
* This test case is divided into 2 sub test cases
* 1) With Blue Enhancement enabled, the test checks that Pdrop should increase as expected with
* a certain number of dropped packets which is fixed by assigning a stream to the uniform random variable.
* 2) Without Blue Enhancement, Pdrop should remain zero and there should not be any dropped packets as ECN is enabled.
*/
class CobaltQueueDiscEnhancedBlueTest : public TestCase
{
public:
CobaltQueueDiscEnhancedBlueTest (QueueSizeUnit mode);
virtual ~CobaltQueueDiscEnhancedBlueTest ();
private:
virtual void DoRun (void);
/**
* Enqueue function
* \param queue the queue disc
* \param size the size
* \param nPkt the number of packets
*/
void Enqueue (Ptr<CobaltQueueDisc> queue, uint32_t size, uint32_t nPkt);
/**
* \brief Dequeue function
* \param queue the queue disc
*/
void Dequeue (Ptr<CobaltQueueDisc> queue);
/**
* \brief Dequeue with delay function
* \param queue the queue disc
* \param nPkt the number of packets
* \param delay the delay between the enqueueing of the packets
*/
void DequeueWithDelay (Ptr<CobaltQueueDisc> queue, uint32_t nPkt, Time delay);
QueueSizeUnit m_mode; ///< mode
};
CobaltQueueDiscEnhancedBlueTest::CobaltQueueDiscEnhancedBlueTest (QueueSizeUnit mode)
: TestCase ("Enhanced Blue tests verification for both packets and bytes mode")
{
m_mode = mode;
}
CobaltQueueDiscEnhancedBlueTest::~CobaltQueueDiscEnhancedBlueTest ()
{
}
void
CobaltQueueDiscEnhancedBlueTest::DoRun (void)
{
uint32_t pktSize = 1500;
uint32_t modeSize = 0;
Ptr<CobaltQueueDisc> queue = CreateObject<CobaltQueueDisc> ();
if (m_mode == QueueSizeUnit::BYTES)
{
modeSize = pktSize;
}
else if (m_mode == QueueSizeUnit::PACKETS)
{
modeSize = 1;
}
queue->Initialize ();
queue->AssignStreams (1);
Enqueue (queue, modeSize, 200);
DequeueWithDelay (queue, 100, MilliSeconds (10));
Simulator::Stop (Seconds (8.0));
Simulator::Run ();
QueueDisc::Stats st = queue->GetStats ();
// The Pdrop value should increase, from it's default value of zero
NS_TEST_EXPECT_MSG_EQ (queue->GetPdrop (), 0.234375, "Pdrop should be increased by 1/256 for every packet whose sojourn time is above 400ms."
" From the 41st dequeue until the last one, sojourn time is above 400ms, so 60 packets have sojourn time above 400ms"
"hence Pdrop should be increased 60*(1/256) which is 0.234375");
NS_TEST_EXPECT_MSG_EQ (st.GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), 49, "There should a fixed number of drops (49 here)");
Simulator::Destroy ();
queue = CreateObject<CobaltQueueDisc> ();
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("UseEcn", BooleanValue (true)),
true, "Verify that we can actually set the attribute UseEcn");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("BlueThreshold", TimeValue (Time::Max ())), true,
"Disable Blue enhancement");
queue->Initialize ();
Enqueue (queue, modeSize, 200);
DequeueWithDelay (queue, 100, MilliSeconds (10));
Simulator::Stop (Seconds (8.0));
Simulator::Run ();
st = queue->GetStats ();
NS_TEST_EXPECT_MSG_EQ (queue->GetPdrop (), 0, "Pdrop should be zero");
NS_TEST_EXPECT_MSG_EQ (st.GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), 0, "There should not any dropped packets");
Simulator::Destroy ();
}
void
CobaltQueueDiscEnhancedBlueTest::Enqueue (Ptr<CobaltQueueDisc> queue, uint32_t size, uint32_t nPkt)
{
Address dest;
for (uint32_t i = 0; i < nPkt; i++)
{
queue->Enqueue (Create<CobaltQueueDiscTestItem> (Create<Packet> (size), dest, 0, true));
}
}
void
CobaltQueueDiscEnhancedBlueTest::Dequeue (Ptr<CobaltQueueDisc> queue)
{
Ptr<QueueDiscItem> item = queue->Dequeue ();
}
void
CobaltQueueDiscEnhancedBlueTest::DequeueWithDelay (Ptr<CobaltQueueDisc> queue, uint32_t nPkt, Time delay)
{
for (uint32_t i = 0; i < nPkt; i++)
{
Simulator::Schedule (Time (Seconds ((i + 1) * delay.GetSeconds ())), &CobaltQueueDiscEnhancedBlueTest::Dequeue, this, queue);
}
}
static class CobaltQueueDiscTestSuite : public TestSuite
{
public:
@@ -651,5 +947,11 @@ public:
// Test 3: Mark test
AddTestCase (new CobaltQueueDiscMarkTest (PACKETS), TestCase::QUICK);
AddTestCase (new CobaltQueueDiscMarkTest (BYTES), TestCase::QUICK);
// Test 4: CE threshold marking test
AddTestCase (new CobaltQueueDiscCeThresholdTest (PACKETS), TestCase::QUICK);
AddTestCase (new CobaltQueueDiscCeThresholdTest (BYTES), TestCase::QUICK);
// Test 4: Blue enhancement test
AddTestCase (new CobaltQueueDiscEnhancedBlueTest (PACKETS), TestCase::QUICK);
AddTestCase (new CobaltQueueDiscEnhancedBlueTest (BYTES), TestCase::QUICK);
}
} g_cobaltQueueTestSuite; ///< the test suite

View File

@@ -22,6 +22,7 @@ def build(bld):
'model/mq-queue-disc.cc',
'model/tbf-queue-disc.cc',
'model/cobalt-queue-disc.cc',
'model/fq-cobalt-queue-disc.cc',
'helper/traffic-control-helper.cc',
'helper/queue-disc-container.cc'
]
@@ -62,6 +63,7 @@ def build(bld):
'model/mq-queue-disc.h',
'model/tbf-queue-disc.h',
'model/cobalt-queue-disc.h',
'model/fq-cobalt-queue-disc.h',
'helper/traffic-control-helper.h',
'helper/queue-disc-container.h'
]