traffic-control: (merges !377) Add FqPIE and an L4S mode for PIE queue disc

This commit is contained in:
Bhaskar Kataria
2020-08-09 17:51:47 +05:30
committed by Tom Henderson
parent 31ad20931a
commit 85e3a7a917
10 changed files with 1749 additions and 5 deletions

View File

@@ -55,6 +55,7 @@ us a note on ns-developers mailing list.</p>
<h2>New API:</h2>
<ul>
<li>Added <b>FqCobalt</b> queue disc with L4S features and set associative hash.</li>
<li>Added <b>FqPIE</b> queue disc with <b>L4S</b> mode</li>
</ul>
<h2>Changes to existing API:</h2>
<ul>
@@ -127,9 +128,9 @@ in order to support multi-users (MU) transmissions.
<li>Added <b>Active/Inactive feature</b> to PIE queue disc</li>
<li>Added <b>netmap</b> and <b>DPDK</b> emulation device variants</li>
<li>Added capability to configure <b>STL pair and containers as attributes</b></li>
<li> Added <b>CartesianToGeographic</b> coordinate conversion capability
<li> Added <b>LollipopCounter</b>, a sequence number counter type
<li> Added <b>6 GHz band</b> support for Wi-Fi 802.11ax
<li> Added <b>CartesianToGeographic</b> coordinate conversion capability</li>
<li> Added <b>LollipopCounter</b>, a sequence number counter type</li>
<li> Added <b>6 GHz band</b> support for Wi-Fi 802.11ax</li>
</ul>
<h2>Changes to existing API:</h2>
<ul>

View File

@@ -32,6 +32,7 @@ New user-visible features
- (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.
- (traffic-control) Added FqPIE queue disc with L4S mode.
Bugs fixed
----------

View File

@@ -0,0 +1,834 @@
/* -*- 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>
*
*/
#include "ns3/test.h"
#include "ns3/simulator.h"
#include "ns3/fq-pie-queue-disc.h"
#include "ns3/pie-queue-disc.h"
#include "ns3/ipv4-header.h"
#include "ns3/ipv4-packet-filter.h"
#include "ns3/ipv4-queue-disc-item.h"
#include "ns3/ipv4-address.h"
#include "ns3/ipv6-header.h"
#include "ns3/ipv6-packet-filter.h"
#include "ns3/ipv6-queue-disc-item.h"
#include "ns3/tcp-header.h"
#include "ns3/udp-header.h"
#include "ns3/string.h"
#include "ns3/pointer.h"
using namespace ns3;
// Variable to assign g_hash to a new packet's flow
int32_t g_hash;
/**
* Simple test packet filter able to classify IPv4 packets
*
*/
class Ipv4FqPieTestPacketFilter : public Ipv4PacketFilter {
public:
/**
* \brief Get the type ID.
* \return the object TypeId
*/
static TypeId GetTypeId (void);
Ipv4FqPieTestPacketFilter ();
virtual ~Ipv4FqPieTestPacketFilter ();
private:
virtual int32_t DoClassify (Ptr<QueueDiscItem> item) const;
virtual bool CheckProtocol (Ptr<QueueDiscItem> item) const;
};
TypeId
Ipv4FqPieTestPacketFilter::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::Ipv4FqPieTestPacketFilter")
.SetParent<Ipv4PacketFilter> ()
.SetGroupName ("Internet")
.AddConstructor<Ipv4FqPieTestPacketFilter> ()
;
return tid;
}
Ipv4FqPieTestPacketFilter::Ipv4FqPieTestPacketFilter ()
{
}
Ipv4FqPieTestPacketFilter::~Ipv4FqPieTestPacketFilter ()
{
}
int32_t
Ipv4FqPieTestPacketFilter::DoClassify (Ptr<QueueDiscItem> item) const
{
return g_hash;
}
bool
Ipv4FqPieTestPacketFilter::CheckProtocol (Ptr<QueueDiscItem> item) const
{
return true;
}
/**
* This class tests packets for which there is no suitable filter
*/
class FqPieQueueDiscNoSuitableFilter : public TestCase
{
public:
FqPieQueueDiscNoSuitableFilter ();
virtual ~FqPieQueueDiscNoSuitableFilter ();
private:
virtual void DoRun (void);
};
FqPieQueueDiscNoSuitableFilter::FqPieQueueDiscNoSuitableFilter ()
: TestCase ("Test packets that are not classified by any filter")
{
}
FqPieQueueDiscNoSuitableFilter::~FqPieQueueDiscNoSuitableFilter ()
{
}
void
FqPieQueueDiscNoSuitableFilter::DoRun (void)
{
// Packets that cannot be classified by the available filters should be dropped
Ptr<FqPieQueueDisc> queueDisc = CreateObjectWithAttributes<FqPieQueueDisc> ("MaxSize", StringValue ("4p"));
Ptr<Ipv4FqPieTestPacketFilter> filter = CreateObject<Ipv4FqPieTestPacketFilter> ();
queueDisc->AddPacketFilter (filter);
g_hash = -1;
queueDisc->SetQuantum (1500);
queueDisc->Initialize ();
Ptr<Packet> p;
p = Create<Packet> ();
Ptr<Ipv6QueueDiscItem> item;
Ipv6Header ipv6Header;
Address dest;
item = Create<Ipv6QueueDiscItem> (p, dest, 0, ipv6Header);
queueDisc->Enqueue (item);
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetNQueueDiscClasses (), 0, "no flow queue should have been created");
p = Create<Packet> (reinterpret_cast<const uint8_t*> ("hello, world"), 12);
item = Create<Ipv6QueueDiscItem> (p, dest, 0, ipv6Header);
queueDisc->Enqueue (item);
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetNQueueDiscClasses (), 0, "no flow queue should have been created");
Simulator::Destroy ();
}
/**
* This class tests the IP flows separation and the packet limit
*/
class FqPieQueueDiscIPFlowsSeparationAndPacketLimit : public TestCase
{
public:
FqPieQueueDiscIPFlowsSeparationAndPacketLimit ();
virtual ~FqPieQueueDiscIPFlowsSeparationAndPacketLimit ();
private:
virtual void DoRun (void);
void AddPacket (Ptr<FqPieQueueDisc> queue, Ipv4Header hdr);
};
FqPieQueueDiscIPFlowsSeparationAndPacketLimit::FqPieQueueDiscIPFlowsSeparationAndPacketLimit ()
: TestCase ("Test IP flows separation and packet limit")
{
}
FqPieQueueDiscIPFlowsSeparationAndPacketLimit::~FqPieQueueDiscIPFlowsSeparationAndPacketLimit ()
{
}
void
FqPieQueueDiscIPFlowsSeparationAndPacketLimit::AddPacket (Ptr<FqPieQueueDisc> queue, Ipv4Header hdr)
{
Ptr<Packet> p = Create<Packet> (100);
Address dest;
Ptr<Ipv4QueueDiscItem> item = Create<Ipv4QueueDiscItem> (p, dest, 0, hdr);
queue->Enqueue (item);
}
void
FqPieQueueDiscIPFlowsSeparationAndPacketLimit::DoRun (void)
{
Ptr<FqPieQueueDisc> queueDisc = CreateObjectWithAttributes<FqPieQueueDisc> ("MaxSize", StringValue ("4p"));
queueDisc->SetQuantum (1500);
queueDisc->Initialize ();
Ipv4Header hdr;
hdr.SetPayloadSize (100);
hdr.SetSource (Ipv4Address ("10.10.1.1"));
hdr.SetDestination (Ipv4Address ("10.10.1.2"));
hdr.SetProtocol (7);
// Add three packets from the first flow
AddPacket (queueDisc, hdr);
AddPacket (queueDisc, hdr);
AddPacket (queueDisc, hdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 3, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the flow queue");
// Add two packets from the second flow
hdr.SetDestination (Ipv4Address ("10.10.1.7"));
// Add the first packet
AddPacket (queueDisc, hdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 4, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the flow queue");
// Add the second packet that causes two packets to be dropped from the fat flow (max backlog = 300, threshold = 150)
AddPacket (queueDisc, hdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 3, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 2, "unexpected number of packets in the flow queue");
Simulator::Destroy ();
}
/**
* This class tests the deficit per flow
*/
class FqPieQueueDiscDeficit : public TestCase
{
public:
FqPieQueueDiscDeficit ();
virtual ~FqPieQueueDiscDeficit ();
private:
virtual void DoRun (void);
void AddPacket (Ptr<FqPieQueueDisc> queue, Ipv4Header hdr);
};
FqPieQueueDiscDeficit::FqPieQueueDiscDeficit ()
: TestCase ("Test credits and flows status")
{
}
FqPieQueueDiscDeficit::~FqPieQueueDiscDeficit ()
{
}
void
FqPieQueueDiscDeficit::AddPacket (Ptr<FqPieQueueDisc> queue, Ipv4Header hdr)
{
Ptr<Packet> p = Create<Packet> (100);
Address dest;
Ptr<Ipv4QueueDiscItem> item = Create<Ipv4QueueDiscItem> (p, dest, 0, hdr);
queue->Enqueue (item);
}
void
FqPieQueueDiscDeficit::DoRun (void)
{
Ptr<FqPieQueueDisc> queueDisc = CreateObjectWithAttributes<FqPieQueueDisc> ();
queueDisc->SetQuantum (90);
queueDisc->Initialize ();
Ipv4Header hdr;
hdr.SetPayloadSize (100);
hdr.SetSource (Ipv4Address ("10.10.1.1"));
hdr.SetDestination (Ipv4Address ("10.10.1.2"));
hdr.SetProtocol (7);
// Add a packet from the first flow
AddPacket (queueDisc, hdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 1, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the first flow queue");
Ptr<FqPieFlow> flow1 = StaticCast<FqPieFlow> (queueDisc->GetQueueDiscClass (0));
NS_TEST_ASSERT_MSG_EQ (flow1->GetDeficit (), static_cast<int32_t> (queueDisc->GetQuantum ()), "the deficit of the first flow must equal the quantum");
NS_TEST_ASSERT_MSG_EQ (flow1->GetStatus (), FqPieFlow::NEW_FLOW, "the first flow must be in the list of new queues");
// Dequeue a packet
queueDisc->Dequeue ();
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 0, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 0, "unexpected number of packets in the first flow queue");
// the deficit for the first flow becomes 90 - (100+20) = -30
NS_TEST_ASSERT_MSG_EQ (flow1->GetDeficit (), -30, "unexpected deficit for the first flow");
// Add two packets from the first flow
AddPacket (queueDisc, hdr);
AddPacket (queueDisc, hdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 2, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 2, "unexpected number of packets in the first flow queue");
NS_TEST_ASSERT_MSG_EQ (flow1->GetStatus (), FqPieFlow::NEW_FLOW, "the first flow must still be in the list of new queues");
// Add two packets from the second flow
hdr.SetDestination (Ipv4Address ("10.10.1.10"));
AddPacket (queueDisc, hdr);
AddPacket (queueDisc, hdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 4, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 2, "unexpected number of packets in the first flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 2, "unexpected number of packets in the second flow queue");
Ptr<FqPieFlow> flow2 = StaticCast<FqPieFlow> (queueDisc->GetQueueDiscClass (1));
NS_TEST_ASSERT_MSG_EQ (flow2->GetDeficit (), static_cast<int32_t> (queueDisc->GetQuantum ()), "the deficit of the second flow must equal the quantum");
NS_TEST_ASSERT_MSG_EQ (flow2->GetStatus (), FqPieFlow::NEW_FLOW, "the second flow must be in the list of new queues");
// Dequeue a packet (from the second flow, as the first flow has a negative deficit)
queueDisc->Dequeue ();
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 3, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 2, "unexpected number of packets in the first flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the second flow queue");
// the first flow got a quantum of deficit (-30+90=60) and has been moved to the end of the list of old queues
NS_TEST_ASSERT_MSG_EQ (flow1->GetDeficit (), 60, "unexpected deficit for the first flow");
NS_TEST_ASSERT_MSG_EQ (flow1->GetStatus (), FqPieFlow::OLD_FLOW, "the first flow must be in the list of old queues");
// the second flow has a negative deficit (-30) and is still in the list of new queues
NS_TEST_ASSERT_MSG_EQ (flow2->GetDeficit (), -30, "unexpected deficit for the second flow");
NS_TEST_ASSERT_MSG_EQ (flow2->GetStatus (), FqPieFlow::NEW_FLOW, "the second flow must be in the list of new queues");
// Dequeue a packet (from the first flow, as the second flow has a negative deficit)
queueDisc->Dequeue ();
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 2, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the first flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the second flow queue");
// the first flow has a negative deficit (60-(100+20)= -60) and stays in the list of old queues
NS_TEST_ASSERT_MSG_EQ (flow1->GetDeficit (), -60, "unexpected deficit for the first flow");
NS_TEST_ASSERT_MSG_EQ (flow1->GetStatus (), FqPieFlow::OLD_FLOW, "the first flow must be in the list of old queues");
// the second flow got a quantum of deficit (-30+90=60) and has been moved to the end of the list of old queues
NS_TEST_ASSERT_MSG_EQ (flow2->GetDeficit (), 60, "unexpected deficit for the second flow");
NS_TEST_ASSERT_MSG_EQ (flow2->GetStatus (), FqPieFlow::OLD_FLOW, "the second flow must be in the list of new queues");
// Dequeue a packet (from the second flow, as the first flow has a negative deficit)
queueDisc->Dequeue ();
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 1, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the first flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 0, "unexpected number of packets in the second flow queue");
// the first flow got a quantum of deficit (-60+90=30) and has been moved to the end of the list of old queues
NS_TEST_ASSERT_MSG_EQ (flow1->GetDeficit (), 30, "unexpected deficit for the first flow");
NS_TEST_ASSERT_MSG_EQ (flow1->GetStatus (), FqPieFlow::OLD_FLOW, "the first flow must be in the list of old queues");
// the second flow has a negative deficit (60-(100+20)= -60)
NS_TEST_ASSERT_MSG_EQ (flow2->GetDeficit (), -60, "unexpected deficit for the second flow");
NS_TEST_ASSERT_MSG_EQ (flow2->GetStatus (), FqPieFlow::OLD_FLOW, "the second flow must be in the list of new queues");
// Dequeue a packet (from the first flow, as the second flow has a negative deficit)
queueDisc->Dequeue ();
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 0, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 0, "unexpected number of packets in the first flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 0, "unexpected number of packets in the second flow queue");
// the first flow has a negative deficit (30-(100+20)= -90)
NS_TEST_ASSERT_MSG_EQ (flow1->GetDeficit (), -90, "unexpected deficit for the first flow");
NS_TEST_ASSERT_MSG_EQ (flow1->GetStatus (), FqPieFlow::OLD_FLOW, "the first flow must be in the list of old queues");
// the second flow got a quantum of deficit (-60+90=30) and has been moved to the end of the list of old queues
NS_TEST_ASSERT_MSG_EQ (flow2->GetDeficit (), 30, "unexpected deficit for the second flow");
NS_TEST_ASSERT_MSG_EQ (flow2->GetStatus (), FqPieFlow::OLD_FLOW, "the second flow must be in the list of new queues");
// Dequeue a packet
queueDisc->Dequeue ();
// the first flow is at the head of the list of old queues but has a negative deficit, thus it gets a quantun
// of deficit (-90+90=0) and is moved to the end of the list of old queues. Then, the second flow (which has a
// positive deficit) is selected, but the second flow is empty and thus it is set to inactive. The first flow is
// reconsidered, but it has a null deficit, hence it gets another quantum of deficit (0+90=90). Then, the first
// flow is reconsidered again, now it has a positive deficit and hence it is selected. But, it is empty and
// therefore is set to inactive, too.
NS_TEST_ASSERT_MSG_EQ (flow1->GetDeficit (), 90, "unexpected deficit for the first flow");
NS_TEST_ASSERT_MSG_EQ (flow1->GetStatus (), FqPieFlow::INACTIVE, "the first flow must be inactive");
NS_TEST_ASSERT_MSG_EQ (flow2->GetDeficit (), 30, "unexpected deficit for the second flow");
NS_TEST_ASSERT_MSG_EQ (flow2->GetStatus (), FqPieFlow::INACTIVE, "the second flow must be inactive");
Simulator::Destroy ();
}
/**
* This class tests the TCP flows separation
*/
class FqPieQueueDiscTCPFlowsSeparation : public TestCase
{
public:
FqPieQueueDiscTCPFlowsSeparation ();
virtual ~FqPieQueueDiscTCPFlowsSeparation ();
private:
virtual void DoRun (void);
void AddPacket (Ptr<FqPieQueueDisc> queue, Ipv4Header ipHdr, TcpHeader tcpHdr);
};
FqPieQueueDiscTCPFlowsSeparation::FqPieQueueDiscTCPFlowsSeparation ()
: TestCase ("Test TCP flows separation")
{
}
FqPieQueueDiscTCPFlowsSeparation::~FqPieQueueDiscTCPFlowsSeparation ()
{
}
void
FqPieQueueDiscTCPFlowsSeparation::AddPacket (Ptr<FqPieQueueDisc> queue, Ipv4Header ipHdr, TcpHeader tcpHdr)
{
Ptr<Packet> p = Create<Packet> (100);
p->AddHeader (tcpHdr);
Address dest;
Ptr<Ipv4QueueDiscItem> item = Create<Ipv4QueueDiscItem> (p, dest, 0, ipHdr);
queue->Enqueue (item);
}
void
FqPieQueueDiscTCPFlowsSeparation::DoRun (void)
{
Ptr<FqPieQueueDisc> queueDisc = CreateObjectWithAttributes<FqPieQueueDisc> ("MaxSize", StringValue ("10p"));
queueDisc->SetQuantum (1500);
queueDisc->Initialize ();
Ipv4Header hdr;
hdr.SetPayloadSize (100);
hdr.SetSource (Ipv4Address ("10.10.1.1"));
hdr.SetDestination (Ipv4Address ("10.10.1.2"));
hdr.SetProtocol (6);
TcpHeader tcpHdr;
tcpHdr.SetSourcePort (7);
tcpHdr.SetDestinationPort (27);
// Add three packets from the first flow
AddPacket (queueDisc, hdr, tcpHdr);
AddPacket (queueDisc, hdr, tcpHdr);
AddPacket (queueDisc, hdr, tcpHdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 3, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the first flow queue");
// Add a packet from the second flow
tcpHdr.SetSourcePort (8);
AddPacket (queueDisc, hdr, tcpHdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 4, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the first flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the second flow queue");
// Add a packet from the third flow
tcpHdr.SetDestinationPort (28);
AddPacket (queueDisc, hdr, tcpHdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 5, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the first flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the second flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (2)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the third flow queue");
// Add two packets from the fourth flow
tcpHdr.SetSourcePort (7);
AddPacket (queueDisc, hdr, tcpHdr);
AddPacket (queueDisc, hdr, tcpHdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 7, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the first flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the second flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (2)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the third flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (3)->GetQueueDisc ()->GetNPackets (), 2, "unexpected number of packets in the third flow queue");
Simulator::Destroy ();
}
/**
* This class tests the UDP flows separation
*/
class FqPieQueueDiscUDPFlowsSeparation : public TestCase
{
public:
FqPieQueueDiscUDPFlowsSeparation ();
virtual ~FqPieQueueDiscUDPFlowsSeparation ();
private:
virtual void DoRun (void);
void AddPacket (Ptr<FqPieQueueDisc> queue, Ipv4Header ipHdr, UdpHeader udpHdr);
};
FqPieQueueDiscUDPFlowsSeparation::FqPieQueueDiscUDPFlowsSeparation ()
: TestCase ("Test UDP flows separation")
{
}
FqPieQueueDiscUDPFlowsSeparation::~FqPieQueueDiscUDPFlowsSeparation ()
{
}
void
FqPieQueueDiscUDPFlowsSeparation::AddPacket (Ptr<FqPieQueueDisc> queue, Ipv4Header ipHdr, UdpHeader udpHdr)
{
Ptr<Packet> p = Create<Packet> (100);
p->AddHeader (udpHdr);
Address dest;
Ptr<Ipv4QueueDiscItem> item = Create<Ipv4QueueDiscItem> (p, dest, 0, ipHdr);
queue->Enqueue (item);
}
void
FqPieQueueDiscUDPFlowsSeparation::DoRun (void)
{
Ptr<FqPieQueueDisc> queueDisc = CreateObjectWithAttributes<FqPieQueueDisc> ("MaxSize", StringValue ("10p"));
queueDisc->SetQuantum (1500);
queueDisc->Initialize ();
Ipv4Header hdr;
hdr.SetPayloadSize (100);
hdr.SetSource (Ipv4Address ("10.10.1.1"));
hdr.SetDestination (Ipv4Address ("10.10.1.2"));
hdr.SetProtocol (17);
UdpHeader udpHdr;
udpHdr.SetSourcePort (7);
udpHdr.SetDestinationPort (27);
// Add three packets from the first flow
AddPacket (queueDisc, hdr, udpHdr);
AddPacket (queueDisc, hdr, udpHdr);
AddPacket (queueDisc, hdr, udpHdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 3, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the first flow queue");
// Add a packet from the second flow
udpHdr.SetSourcePort (8);
AddPacket (queueDisc, hdr, udpHdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 4, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the first flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the second flow queue");
// Add a packet from the third flow
udpHdr.SetDestinationPort (28);
AddPacket (queueDisc, hdr, udpHdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 5, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the first flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the second flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (2)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the third flow queue");
// Add two packets from the fourth flow
udpHdr.SetSourcePort (7);
AddPacket (queueDisc, hdr, udpHdr);
AddPacket (queueDisc, hdr, udpHdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 7, "unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the first flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the second flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (2)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the third flow queue");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (3)->GetQueueDisc ()->GetNPackets (), 2, "unexpected number of packets in the third flow queue");
Simulator::Destroy ();
}
/*
* This class tests linear probing, collision response, and set
* creation capability of set associative hashing in FqPIE.
* We modified DoClassify () and CheckProtocol () so that we could control
* the hash returned for each packet. In the beginning, we use flow hashes
* ranging from 0 to 7. These must go into different queues in the same set.
* The set number for these is obtained using outerhash, which is 0.
* When a new packet arrives with flow hash 1024, outerhash = 0 is obtained
* and the first set is iteratively searched.
* The packet is eventually added to queue 0 since the tags of queues
* in the set do not match with the hash of the flow. The tag of queue 0 is
* updated as 1024. When a packet with hash 1025 arrives, outerhash = 0
* is obtained and the first set is iteratively searched.
* Since there is no match, it is added to queue 0 and the tag of queue 0 is
* updated to 1025.
*
* The variable outerhash stores the nearest multiple of 8 that is lesser than
* the hash. When a flow hash of 20 arrives, the value of outerhash
* is 16. Since m_flowIndices[16] wasnt previously allotted, a new flow
* is created, and the tag corresponding to this queue is set to 20.
*/
class FqPieQueueDiscSetLinearProbing : public TestCase
{
public:
FqPieQueueDiscSetLinearProbing ();
virtual ~FqPieQueueDiscSetLinearProbing ();
private:
virtual void DoRun (void);
void AddPacket (Ptr<FqPieQueueDisc> queue, Ipv4Header hdr);
};
FqPieQueueDiscSetLinearProbing::FqPieQueueDiscSetLinearProbing ()
: TestCase ("Test credits and flows status")
{
}
FqPieQueueDiscSetLinearProbing::~FqPieQueueDiscSetLinearProbing ()
{
}
void
FqPieQueueDiscSetLinearProbing::AddPacket (Ptr<FqPieQueueDisc> queue, Ipv4Header hdr)
{
Ptr<Packet> p = Create<Packet> (100);
Address dest;
Ptr<Ipv4QueueDiscItem> item = Create<Ipv4QueueDiscItem> (p, dest, 0, hdr);
queue->Enqueue (item);
}
void
FqPieQueueDiscSetLinearProbing::DoRun (void)
{
Ptr<FqPieQueueDisc> queueDisc = CreateObjectWithAttributes<FqPieQueueDisc> ("EnableSetAssociativeHash", BooleanValue (true));
queueDisc->SetQuantum (90);
queueDisc->Initialize ();
Ptr<Ipv4FqPieTestPacketFilter> filter = CreateObject<Ipv4FqPieTestPacketFilter> ();
queueDisc->AddPacketFilter (filter);
Ipv4Header hdr;
hdr.SetPayloadSize (100);
hdr.SetSource (Ipv4Address ("10.10.1.1"));
hdr.SetDestination (Ipv4Address ("10.10.1.2"));
hdr.SetProtocol (7);
g_hash = 0;
AddPacket (queueDisc, hdr);
g_hash = 1;
AddPacket (queueDisc, hdr);
AddPacket (queueDisc, hdr);
g_hash = 2;
AddPacket (queueDisc, hdr);
g_hash = 3;
AddPacket (queueDisc, hdr);
g_hash = 4;
AddPacket (queueDisc, hdr);
AddPacket (queueDisc, hdr);
g_hash = 5;
AddPacket (queueDisc, hdr);
g_hash = 6;
AddPacket (queueDisc, hdr);
g_hash = 7;
AddPacket (queueDisc, hdr);
g_hash = 1024;
AddPacket (queueDisc, hdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 11,
"unexpected number of packets in the queue disc");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 2,
"unexpected number of packets in the first flow queue of set one");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 2,
"unexpected number of packets in the second flow queue of set one");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (2)->GetQueueDisc ()->GetNPackets (), 1,
"unexpected number of packets in the third flow queue of set one");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (3)->GetQueueDisc ()->GetNPackets (), 1,
"unexpected number of packets in the fourth flow queue of set one");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (4)->GetQueueDisc ()->GetNPackets (), 2,
"unexpected number of packets in the fifth flow queue of set one");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (5)->GetQueueDisc ()->GetNPackets (), 1,
"unexpected number of packets in the sixth flow queue of set one");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (6)->GetQueueDisc ()->GetNPackets (), 1,
"unexpected number of packets in the seventh flow queue of set one");
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (7)->GetQueueDisc ()->GetNPackets (), 1,
"unexpected number of packets in the eighth flow queue of set one");
g_hash = 1025;
AddPacket (queueDisc, hdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3,
"unexpected number of packets in the first flow of set one");
g_hash = 10;
AddPacket (queueDisc, hdr);
NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (8)->GetQueueDisc ()->GetNPackets (), 1,
"unexpected number of packets in the first flow of set two");
Simulator::Destroy ();
}
/**
* This class tests L4S mode. This test is divided to sub test one without hash collisions and so ECT0 and ECT1 flows are
* classified into different flows.
* Sub Test 1
* 70 packets are enqueued into both the flows with the delay of 0.5ms between two enqueues, and dequeued with the delay of
* 1ms between two dequeues.
* Sub Test 2
* 140(70 ECT0 + 70 ECT1) packets are enqueued such that ECT1 packets are enqueued at 0.5ms, 1.5ms, 2.5ms and so on, and ECT0 packets are
* enqueued are enqueued at 1ms, 2ms, 3ms and so on
* Any future classifier options (e.g. SetAssociativehash) should be disabled to prevent a hash collision on this test case.
*/
class FqPieQueueDiscL4sMode : public TestCase
{
public:
FqPieQueueDiscL4sMode ();
virtual ~FqPieQueueDiscL4sMode ();
private:
virtual void DoRun (void);
void AddPacket (Ptr<FqPieQueueDisc> queue, Ipv4Header hdr, u_int32_t nPkt);
void AddPacketWithDelay (Ptr<FqPieQueueDisc> queue,Ipv4Header hdr, double delay, uint32_t nPkt);
void Dequeue (Ptr<FqPieQueueDisc> queue, uint32_t nPkt);
void DequeueWithDelay (Ptr<FqPieQueueDisc> queue, double delay, uint32_t nPkt);
};
FqPieQueueDiscL4sMode::FqPieQueueDiscL4sMode ()
: TestCase ("Test L4S mode")
{
}
FqPieQueueDiscL4sMode::~FqPieQueueDiscL4sMode ()
{
}
void
FqPieQueueDiscL4sMode::AddPacket (Ptr<FqPieQueueDisc> queue, Ipv4Header hdr, uint32_t nPkt)
{
Address dest;
Ptr<Packet> p = Create<Packet> (100);
for (uint32_t i = 0; i < nPkt; i++)
{
Ptr<Ipv4QueueDiscItem> item = Create<Ipv4QueueDiscItem> (p, dest, 0, hdr);
queue->Enqueue (item);
}
}
void
FqPieQueueDiscL4sMode::AddPacketWithDelay (Ptr<FqPieQueueDisc> queue,Ipv4Header hdr, double delay, uint32_t nPkt)
{
for (uint32_t i = 0; i < nPkt; i++)
{
Simulator::Schedule (Time (Seconds ((i + 1) * delay)), &FqPieQueueDiscL4sMode::AddPacket, this, queue, hdr, 1);
}
}
void
FqPieQueueDiscL4sMode::Dequeue (Ptr<FqPieQueueDisc> queue, uint32_t nPkt)
{
for (uint32_t i = 0; i < nPkt; i++)
{
Ptr<QueueDiscItem> item = queue->Dequeue ();
}
}
void
FqPieQueueDiscL4sMode::DequeueWithDelay (Ptr<FqPieQueueDisc> queue, double delay, uint32_t nPkt)
{
for (uint32_t i = 0; i < nPkt; i++)
{
Simulator::Schedule (Time (Seconds ((i + 1) * delay)), &FqPieQueueDiscL4sMode::Dequeue, this, queue, 1);
}
}
void
FqPieQueueDiscL4sMode::DoRun (void)
{
// Test is divided into 2 sub test cases:
// 1) Without hash collisions
// 2) With hash collisions
// Test case 1, Without hash collisions
Ptr<FqPieQueueDisc> queueDisc = CreateObjectWithAttributes<FqPieQueueDisc> ("MaxSize", StringValue ("10240p"), "UseEcn", BooleanValue (true),
"Perturbation", UintegerValue (0), "UseL4s", BooleanValue (true),
"CeThreshold", TimeValue (MilliSeconds (2)));
queueDisc->SetQuantum (1514);
queueDisc->Initialize ();
Ipv4Header hdr;
hdr.SetPayloadSize (100);
hdr.SetSource (Ipv4Address ("10.10.1.1"));
hdr.SetDestination (Ipv4Address ("10.10.1.2"));
hdr.SetProtocol (7);
hdr.SetEcn (Ipv4Header::ECN_ECT1);
// Add 70 ECT1 (ECN capable) packets from the first flow
// Set delay = 0.5ms
double delay = 0.0005;
Simulator::Schedule (Time (Seconds (0)), &FqPieQueueDiscL4sMode::AddPacketWithDelay, this, queueDisc, hdr, delay, 70);
// Add 70 ECT0 (ECN capable) packets from second flow
hdr.SetEcn (Ipv4Header::ECN_ECT0);
hdr.SetDestination (Ipv4Address ("10.10.1.10"));
Simulator::Schedule (Time (Seconds (0)), &FqPieQueueDiscL4sMode::AddPacketWithDelay, this, queueDisc, hdr, delay, 70);
//Dequeue 140 packets with delay 1ms
delay = 0.001;
DequeueWithDelay (queueDisc, delay, 140);
Simulator::Stop (Seconds (10.0));
Simulator::Run ();
Ptr<PieQueueDisc> q0 = queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetObject <PieQueueDisc> ();
Ptr<PieQueueDisc> q1 = queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetObject <PieQueueDisc> ();
NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNMarkedPackets (PieQueueDisc::CE_THRESHOLD_EXCEEDED_MARK), 66, "There should be 66 marked packets"
"4th packet is enqueued at 2ms and dequeued at 4ms hence the delay of 2ms which not greater than CE threshold"
"5th packet is enqueued at 2.5ms and dequeued at 5ms hence the delay of 2.5ms and subsequent packet also do have delay"
"greater than CE threshold so all the packets after 4th packet are marked");
NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNDroppedPackets (PieQueueDisc::UNFORCED_DROP), 0, "Queue delay is less than max burst allowance so"
"There should not be any dropped packets");
NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNMarkedPackets (PieQueueDisc::UNFORCED_MARK), 0, "There should not be any marked packets");
NS_TEST_EXPECT_MSG_EQ (q1->GetStats ().GetNMarkedPackets (PieQueueDisc::UNFORCED_MARK), 0, "There should not be marked packets.");
NS_TEST_EXPECT_MSG_EQ (q1->GetStats ().GetNDroppedPackets (PieQueueDisc::UNFORCED_DROP), 0, "There should not be any dropped packets");
Simulator::Destroy ();
// Test case 2, With hash collisions
queueDisc = CreateObjectWithAttributes<FqPieQueueDisc> ("MaxSize", StringValue ("10240p"), "UseEcn", BooleanValue (true),
"Perturbation", UintegerValue (0), "UseL4s", BooleanValue (true),
"CeThreshold", TimeValue (MilliSeconds (2)));
queueDisc->SetQuantum (1514);
queueDisc->Initialize ();
hdr.SetPayloadSize (100);
hdr.SetSource (Ipv4Address ("10.10.1.1"));
hdr.SetDestination (Ipv4Address ("10.10.1.2"));
hdr.SetProtocol (7);
hdr.SetEcn (Ipv4Header::ECN_ECT1);
// Add 70 ECT1 (ECN capable) packets from the first flow
// Set delay = 1ms
delay = 0.001;
Simulator::Schedule (Time (Seconds (0.0005)), &FqPieQueueDiscL4sMode::AddPacket, this, queueDisc, hdr, 1);
Simulator::Schedule (Time (Seconds (0.0005)), &FqPieQueueDiscL4sMode::AddPacketWithDelay, this, queueDisc, hdr, delay, 69);
// Add 70 ECT0 (ECN capable) packets from first flow
hdr.SetEcn (Ipv4Header::ECN_ECT0);
Simulator::Schedule (Time (Seconds (0)), &FqPieQueueDiscL4sMode::AddPacketWithDelay, this, queueDisc, hdr, delay, 70);
//Dequeue 140 packets with delay 1ms
DequeueWithDelay (queueDisc, delay, 140);
Simulator::Stop (Seconds (1.0));
Simulator::Run ();
q0 = queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetObject <PieQueueDisc> ();
q0 = queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetObject <PieQueueDisc> ();
NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNMarkedPackets (PieQueueDisc::CE_THRESHOLD_EXCEEDED_MARK), 68, "There should be 68 marked packets"
"2nd ECT1 packet is enqueued at 1.5ms and dequeued at 3ms hence the delay of 1.5ms which not greater than CE threshold"
"3rd packet is enqueued at 2.5ms and dequeued at 5ms hence the delay of 2.5ms and subsequent packet also do have delay"
"greater than CE threshold so all the packets after 2nd packet are marked");
NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNDroppedPackets (PieQueueDisc::UNFORCED_DROP), 0, "Queue delay is less than max burst allowance so"
"There should not be any dropped packets");
NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNMarkedPackets (PieQueueDisc::UNFORCED_MARK), 0, "There should not be any marked packets");
Simulator::Destroy ();
}
class FqPieQueueDiscTestSuite : public TestSuite
{
public:
FqPieQueueDiscTestSuite ();
};
FqPieQueueDiscTestSuite::FqPieQueueDiscTestSuite ()
: TestSuite ("fq-pie-queue-disc", UNIT)
{
AddTestCase (new FqPieQueueDiscNoSuitableFilter, TestCase::QUICK);
AddTestCase (new FqPieQueueDiscIPFlowsSeparationAndPacketLimit, TestCase::QUICK);
AddTestCase (new FqPieQueueDiscDeficit, TestCase::QUICK);
AddTestCase (new FqPieQueueDiscTCPFlowsSeparation, TestCase::QUICK);
AddTestCase (new FqPieQueueDiscUDPFlowsSeparation, TestCase::QUICK);
AddTestCase (new FqPieQueueDiscSetLinearProbing, TestCase::QUICK);
AddTestCase (new FqPieQueueDiscL4sMode, TestCase::QUICK);
}
static FqPieQueueDiscTestSuite FqPieQueueDiscTestSuite;

View File

@@ -32,6 +32,7 @@ def build(bld):
'csma-system-test-suite.cc',
'ns3tc/fq-codel-queue-disc-test-suite.cc',
'ns3tc/fq-cobalt-queue-disc-test-suite.cc',
'ns3tc/fq-pie-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

@@ -0,0 +1,83 @@
.. include:: replace.txt
.. highlight:: cpp
.. highlight:: bash
FqPIE queue disc
------------------
This chapter describes the FqPIE ([Hoe16]_) queue disc implementation in |ns3|.
The FlowQueue-PIE (FQ-PIE) algorithm is similar to the FlowQueue part of FlowQueue-CoDel (FQ-CoDel) algorithm available in ns-3-dev/src/traffic-control/doc/fq-codel.rst.
The documentation for PIE is available in ns-3-dev/src/traffic-control/doc/pie.rst.
Model Description
*****************
The source code for the FqPIE queue disc is located in the directory
``src/traffic-control/model`` and consists of 2 files `fq-pie-queue-disc.h`
and `fq-pie-queue-disc.cc` defining a FqPieQueueDisc class and a helper
FqPieFlow class. The code was ported to |ns3| based on Linux kernel code
implemented by Mohit P. Tahiliani.
The Model Description is similar to the FqCoDel documentation mentioned above.
References
==========
.. [CAK16] https://github.com/torvalds/linux/blob/master/net/sched/sch_fq_pie.c , Implementation of FqPIE in Linux
.. [PIE] https://ieeexplore.ieee.org/document/9000684
Attributes
==========
The key attributes that the FqPieQueue class holds include the following:
Most of the key attributes are similar to the FqCoDel implementation mentioned above.
Some differences are:
Absence of ``MinBytes``, ``Interval`` and ``Target`` parameter.
Some extra parameters are
* ``MarkEcnThreshold:`` ECN marking threshold (RFC 8033 suggests 0.1 (i.e., 10%) default).
* ``MeanPktSize:`` Average of packet size.
* ``A:`` Decrement value of drop probability. Default value is 1./4096 .
* ``B:`` The Threshold after which Blue is enabled. Default value is 400ms.
* ``Tupdate:`` Time period to calculate drop probability
* ``Supdate:`` Start time of the update timer
* ``DequeueThreshold:`` Minimum queue size in bytes before dequeue rate is measured
* ``QueueDelayReference:`` Desired queue delay
* ``MaxBurstAllowance:`` Current max burst allowance before random drop
* ``UseDequeueRateEstimator:`` Enable/Disable usage of Dequeue Rate Estimator
* ``UseCapDropAdjustment:`` Enable/Disable Cap Drop Adjustment feature mentioned in RFC 8033
* ``UseDerandomization:`` Enable/Disable Derandomization feature mentioned in RFC 8033
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, FqPIE
can be configured as follows:
.. sourcecode:: cpp
TrafficControlHelper tch;
tch.SetRootQueueDisc ("ns3::FqPieQueueDisc", "DropBatchSize", UintegerValue (1)
"Perturbation", UintegerValue (256));
QueueDiscContainer qdiscs = tch.Install (devices);
Validation
**********
The FqPie model is tested using :cpp:class:`FqPieQueueDiscTestSuite` class defined in `src/test/ns3tc/fq-pie-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-pie-queue-disc
or::
$ NS_LOG="FqPieQueueDisc" ./waf --run "test-runner --suite=fq-pie-queue-disc"

View File

@@ -0,0 +1,561 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2018 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: 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 (1000));
m_queueDiscFactory.Set ("A", DoubleValue (0.125));
m_queueDiscFactory.Set ("B", DoubleValue (1.25));
m_queueDiscFactory.Set ("Tupdate", TimeValue (Seconds (0.015)));
m_queueDiscFactory.Set ("Supdate", TimeValue (Seconds (0)));
m_queueDiscFactory.Set ("DequeueThreshold", UintegerValue (16384));
m_queueDiscFactory.Set ("QueueDelayReference", TimeValue (Seconds (0.015)));
m_queueDiscFactory.Set ("MaxBurstAllowance", TimeValue (Seconds (0.15)));
m_queueDiscFactory.Set ("UseDequeueRateEstimator", BooleanValue (false));
m_queueDiscFactory.Set ("UseCapDropAdjustment", BooleanValue (true));
m_queueDiscFactory.Set ("UseDerandomization", BooleanValue (false));
}
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

View File

@@ -0,0 +1,204 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2018 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: Sumukha PK <sumukhapk46@gmail.com>
* Prajval M <26prajval98@gmail.com>
* Ishaan R D <ishaanrd6@gmail.com>
* Mohit P. Tahiliani <tahiliani@nitk.edu.in>
*/
#ifndef FQ_PIE_QUEUE_DISC
#define FQ_PIE_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 FqPie queue disc
*/
class FqPieFlow : public QueueDiscClass {
public:
/**
* \brief Get the type ID.
* \return the object TypeId
*/
static TypeId GetTypeId (void);
/**
* \brief FqPieFlow constructor
*/
FqPieFlow ();
virtual ~FqPieFlow ();
/**
* \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 FqPie packet queue disc
*/
class FqPieQueueDisc : public QueueDisc {
public:
/**
* \brief Get the type ID.
* \return the object TypeId
*/
static TypeId GetTypeId (void);
/**
* \brief FqPieQueueDisc constructor
*/
FqPieQueueDisc ();
virtual ~FqPieQueueDisc ();
/**
* \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 FqPieDrop (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);
// PIE queue disc parameter
bool m_useEcn; //!< True if ECN is used (packets are marked instead of being dropped)
double m_markEcnTh; //!< ECN marking threshold (default 10% as suggested in RFC 8033)
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_sUpdate; //!< Start time of the update timer
Time m_tUpdate; //!< Time period after which CalculateP () is called
Time m_qDelayRef; //!< Desired queue delay
uint32_t m_meanPktSize; //!< Average packet size in bytes
Time m_maxBurst; //!< Maximum burst allowed before random early dropping kicks in
double m_a; //!< Parameter to pie controller
double m_b; //!< Parameter to pie controller
uint32_t m_dqThreshold; //!< Minimum queue size in bytes before dequeue rate is measured
bool m_useDqRateEstimator; //!< Enable/Disable usage of dequeue rate estimator for queue delay calculation
bool m_isCapDropAdjustment;//!< Enable/Disable Cap Drop Adjustment feature mentioned in RFC 8033
bool m_useDerandomization; //!< Enable Derandomization feature mentioned in RFC 8033
// Fq parameters
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_enableSetAssociativeHash; //!< whether to enable set associative hash
std::list<Ptr<FqPieFlow> > m_newFlows; //!< The list of new flows
std::list<Ptr<FqPieFlow> > 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_PIE_QUEUE_DISC */

View File

@@ -50,7 +50,7 @@ TypeId PieQueueDisc::GetTypeId (void)
"Average of packet size",
UintegerValue (1000),
MakeUintegerAccessor (&PieQueueDisc::m_meanPktSize),
MakeUintegerChecker<uint32_t> ())
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("A",
"Value of alpha",
DoubleValue (0.125),
@@ -122,6 +122,16 @@ TypeId PieQueueDisc::GetTypeId (void)
TimeValue (Time::Max ()),
MakeTimeAccessor (&PieQueueDisc::m_activeThreshold),
MakeTimeChecker ())
.AddAttribute ("CeThreshold",
"The FqPie CE threshold for marking packets",
TimeValue (Time::Max ()),
MakeTimeAccessor (&PieQueueDisc::m_ceThreshold),
MakeTimeChecker ())
.AddAttribute ("UseL4s",
"True to use L4S (only ECT1 packets are marked at CE threshold)",
BooleanValue (false),
MakeBooleanAccessor (&PieQueueDisc::m_useL4s),
MakeBooleanChecker ())
;
return tid;
@@ -169,6 +179,24 @@ PieQueueDisc::DoEnqueue (Ptr<QueueDiscItem> item)
NS_LOG_FUNCTION (this << item);
QueueSize nQueued = GetCurrentSize ();
// If L4S is enabled, then check if the packet is ECT1, and if it is then set isEct true
bool isEct1 = false;
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 ("Enqueueing ECT1 packet " << static_cast<uint16_t> (tosByte & 0x3));
}
else
{
NS_LOG_DEBUG ("Enqueueing CE packet " << static_cast<uint16_t> (tosByte & 0x3));
}
isEct1 = true;
}
}
if (nQueued + item > GetMaxSize ())
{
@@ -177,7 +205,9 @@ PieQueueDisc::DoEnqueue (Ptr<QueueDiscItem> item)
m_accuProb = 0;
return false;
}
else if ((m_activeThreshold == Time::Max () || m_active) && DropEarly (item, nQueued.GetValue ()))
// isEct1 will be true only if L4S enabled as well as the packet is ECT1.
// If L4S is enabled and packet is ECT1 then directly enqueue the packet.
else if ((m_activeThreshold == Time::Max () || m_active) && !isEct1 && DropEarly (item, nQueued.GetValue ()))
{
if (!m_useEcn || m_dropProb >= m_markEcnTh || !Mark (item, UNFORCED_MARK))
{
@@ -452,6 +482,29 @@ PieQueueDisc::DoDequeue ()
double now = Simulator::Now ().GetSeconds ();
uint32_t pktSize = item->GetSize ();
// If L4S is enabled and packet is ECT1, then check if delay is greater than CE threshold and if it is then mark the packet,
// and skip PIE steps, and return the item.
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 (Time (Seconds (now - item->GetTimeStamp ().GetSeconds ())) > m_ceThreshold && Mark (item, CE_THRESHOLD_EXCEEDED_MARK))
{
NS_LOG_LOGIC ("Marking due to CeThreshold " << m_ceThreshold.GetSeconds ());
}
return item;
}
}
// if not in a measurement cycle and the queue has built up to dq_threshold,
// start the measurement cycle
if (m_useDqRateEstimator)

View File

@@ -98,6 +98,7 @@ public:
static constexpr const char* UNFORCED_DROP = "Unforced drop"; //!< Early probability drops: proactive
static constexpr const char* FORCED_DROP = "Forced drop"; //!< Drops due to queue limit: reactive
static constexpr const char* UNFORCED_MARK = "Unforced mark"; //!< Early probability marks: proactive
static constexpr const char* CE_THRESHOLD_EXCEEDED_MARK = "CE threshold exceeded mark"; //!< Early probability marks: proactive
protected:
/**
@@ -131,6 +132,7 @@ private:
*/
void CalculateP ();
static const uint64_t DQCOUNT_INVALID = std::numeric_limits<uint64_t>::max(); //!< Invalid dqCount value
// ** Variables supplied by user
@@ -148,6 +150,8 @@ private:
bool m_useDerandomization; //!< Enable Derandomization feature mentioned in RFC 8033
double m_markEcnTh; //!< ECN marking threshold (default 10% as suggested in RFC 8033)
Time m_activeThreshold; //!< Threshold for activating PIE (disabled by default)
Time m_ceThreshold; //!< Threshold above which to CE mark
bool m_useL4s; //!< True if L4S is used (ECT1 packets are marked at CE threshold)
// ** Variables maintained by PIE
double m_dropProb; //!< Variable used in calculation of drop probability

View File

@@ -18,6 +18,7 @@ def build(bld):
'model/codel-queue-disc.cc',
'model/fq-codel-queue-disc.cc',
'model/pie-queue-disc.cc',
'model/fq-pie-queue-disc.cc',
'model/prio-queue-disc.cc',
'model/mq-queue-disc.cc',
'model/tbf-queue-disc.cc',
@@ -59,6 +60,7 @@ def build(bld):
'model/codel-queue-disc.h',
'model/fq-codel-queue-disc.h',
'model/pie-queue-disc.h',
'model/fq-pie-queue-disc.h',
'model/prio-queue-disc.h',
'model/mq-queue-disc.h',
'model/tbf-queue-disc.h',