From ca5fbe955517feeb7790a68a36ddd4a369fdf445 Mon Sep 17 00:00:00 2001
From: Stefano Avallone
Date: Thu, 7 Jun 2018 23:55:24 +0200
Subject: [PATCH] traffic-control: Add Prio queue disc
---
CHANGES.html | 1 +
doc/models/Makefile | 1 +
doc/models/source/traffic-control.rst | 1 +
.../traffic-control/queue-discs-benchmark.cc | 12 +-
src/traffic-control/doc/prio.rst | 76 ++++
src/traffic-control/model/prio-queue-disc.cc | 224 ++++++++++++
src/traffic-control/model/prio-queue-disc.h | 113 ++++++
.../test/prio-queue-disc-test-suite.cc | 324 ++++++++++++++++++
src/traffic-control/wscript | 3 +
9 files changed, 753 insertions(+), 2 deletions(-)
create mode 100644 src/traffic-control/doc/prio.rst
create mode 100644 src/traffic-control/model/prio-queue-disc.cc
create mode 100644 src/traffic-control/model/prio-queue-disc.h
create mode 100644 src/traffic-control/test/prio-queue-disc-test-suite.cc
diff --git a/CHANGES.html b/CHANGES.html
index 0758fd006..615786106 100644
--- a/CHANGES.html
+++ b/CHANGES.html
@@ -61,6 +61,7 @@ us a note on ns-developers mailing list.
nodes to be placed outside of buildings defined in the scenario.
The Hash() method has been added to the QueueDiscItem class to compute the
hash of various fields of the packet header (depending on the packet type).
+ Added a priority queue disc (PrioQueueDisc).
Changes to existing API:
diff --git a/doc/models/Makefile b/doc/models/Makefile
index 85a1a3d6f..e153b6d24 100644
--- a/doc/models/Makefile
+++ b/doc/models/Makefile
@@ -82,6 +82,7 @@ SOURCES = \
$(SRC)/traffic-control/doc/queue-discs.rst \
$(SRC)/traffic-control/doc/pfifo-fast.rst \
$(SRC)/traffic-control/doc/fifo.rst \
+ $(SRC)/traffic-control/doc/prio.rst \
$(SRC)/traffic-control/doc/tbf.rst \
$(SRC)/traffic-control/doc/red.rst \
$(SRC)/traffic-control/doc/codel.rst \
diff --git a/doc/models/source/traffic-control.rst b/doc/models/source/traffic-control.rst
index e57c10c1d..a630480f0 100644
--- a/doc/models/source/traffic-control.rst
+++ b/doc/models/source/traffic-control.rst
@@ -7,6 +7,7 @@ Traffic Control Layer
queue-discs
fifo
pfifo-fast
+ prio
tbf
red
codel
diff --git a/examples/traffic-control/queue-discs-benchmark.cc b/examples/traffic-control/queue-discs-benchmark.cc
index 42e1ba932..3f5c6db7a 100644
--- a/examples/traffic-control/queue-discs-benchmark.cc
+++ b/examples/traffic-control/queue-discs-benchmark.cc
@@ -99,7 +99,7 @@ int main (int argc, char *argv[])
std::string delay = "5ms";
std::string queueDiscType = "PfifoFast";
uint32_t queueDiscSize = 1000;
- uint32_t netdevicesQueueSize = 100;
+ uint32_t netdevicesQueueSize = 50;
bool bql = false;
std::string flowsDatarate = "20Mbps";
@@ -112,7 +112,7 @@ int main (int argc, char *argv[])
CommandLine cmd;
cmd.AddValue ("bandwidth", "Bottleneck bandwidth", bandwidth);
cmd.AddValue ("delay", "Bottleneck delay", delay);
- cmd.AddValue ("queueDiscType", "Bottleneck queue disc type in {PfifoFast, ARED, CoDel, FqCoDel, PIE}", queueDiscType);
+ cmd.AddValue ("queueDiscType", "Bottleneck queue disc type in {PfifoFast, ARED, CoDel, FqCoDel, PIE, prio}", queueDiscType);
cmd.AddValue ("queueDiscSize", "Bottleneck queue disc size in packets", queueDiscSize);
cmd.AddValue ("netdevicesQueueSize", "Bottleneck netdevices queue size in packets", netdevicesQueueSize);
cmd.AddValue ("bql", "Enable byte queue limits on bottleneck netdevices", bql);
@@ -180,6 +180,14 @@ int main (int argc, char *argv[])
Config::SetDefault ("ns3::PieQueueDisc::MaxSize",
QueueSizeValue (QueueSize (QueueSizeUnit::PACKETS, queueDiscSize)));
}
+ else if (queueDiscType.compare ("prio") == 0)
+ {
+ uint16_t handle = tchBottleneck.SetRootQueueDisc ("ns3::PrioQueueDisc", "Priomap",
+ StringValue ("0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1"));
+ TrafficControlHelper::ClassIdList cid = tchBottleneck.AddQueueDiscClasses (handle, 2, "ns3::QueueDiscClass");
+ tchBottleneck.AddChildQueueDisc (handle, cid[0], "ns3::FifoQueueDisc");
+ tchBottleneck.AddChildQueueDisc (handle, cid[1], "ns3::RedQueueDisc");
+ }
else
{
NS_ABORT_MSG ("--queueDiscType not valid");
diff --git a/src/traffic-control/doc/prio.rst b/src/traffic-control/doc/prio.rst
new file mode 100644
index 000000000..856159ddd
--- /dev/null
+++ b/src/traffic-control/doc/prio.rst
@@ -0,0 +1,76 @@
+.. include:: replace.txt
+.. highlight:: cpp
+
+Prio queue disc
+---------------------
+
+Model Description
+*****************
+
+PrioQueueDisc implements a strict priority policy, where packets are dequeued from
+a band only if higher priority bands are all empty. PrioQueueDisc is a classful
+queue disc and can have an arbitrary number of bands, each of which is handled by a
+queue disc of any kind. The capacity of PrioQueueDisc is not limited; packets can
+only be dropped by child queue discs (which may have a limited capacity).
+If no packet filter is installed or able to classify a packet, then the
+packet is enqueued into a priority band based on its priority (modulo 16), which
+is used as an index into an array called priomap. Users can read :ref:`Socket-options`
+for details on how to set the packet priority. If a packet is classified
+by an installed packet filter and the returned value ``i`` is non-negative and less than the
+number of priority bands, then the packet is enqueued into the ``i``-th priority band.
+Otherwise, the packet is enqueued into the priority band specified by the first element
+of the priomap array.
+
+If no queue disc class is added by the user before the queue disc is initialized,
+three child queue discs of type FifoQueueDisc are automatically added. It has to
+be noted that PrioQueueDisc needs at least two child queue discs.
+
+
+Attributes
+==========
+
+The PrioQueueDisc class holds the following attribute:
+
+* ``Priomap:`` The priority to band mapping. The default value is the same mapping as the (fixed) one used by PfifoFastQueueDisc.
+
+Examples
+========
+
+An example of how to configure PrioQueueDisc with custom child queue discs and priomap
+is provided by `queue-discs-benchmark.cc` located in ``examples/traffic-control``:
+
+.. sourcecode:: cpp
+
+ TrafficControlHelper tch;
+ uint16_t handle = tch.SetRootQueueDisc ("ns3::PrioQueueDisc", "Priomap", StringValue ("0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1"));
+ TrafficControlHelper::ClassIdList cid = tch.AddQueueDiscClasses (handle, 2, "ns3::QueueDiscClass");
+ tch.AddChildQueueDisc (handle, cid[0], "ns3::FifoQueueDisc");
+ tch.AddChildQueueDisc (handle, cid[1], "ns3::RedQueueDisc");
+
+The code above adds two classes (bands) to a PrioQueueDisc. The highest priority one
+is a FifoQueueDisc, the other one is a RedQueueDisc. The attribute Priomap is set to
+an array containing only 0 and 1 (since PrioQueueDisc only has two bands).
+
+
+Validation
+**********
+
+PrioQueueDisc is tested using :cpp:class:`PrioQueueDiscTestSuite` class defined
+in ``src/traffic-control/test/prio-queue-disc-test-suite.cc``. The test aims to
+check that: i) packets are enqueued in the correct band based on their priority and
+the priomap or according to the value returned by the installed packet filter;
+ii) packets are dequeued in the correct order.
+
+The test suite can be run using the following commands:
+
+::
+
+ $ ./waf configure --enable-examples --enable-tests
+ $ ./waf build
+ $ ./test.py -s prio-queue-disc
+
+or
+
+::
+
+ $ NS_LOG="PrioQueueDisc" ./waf --run "test-runner --suite=prio-queue-disc"
diff --git a/src/traffic-control/model/prio-queue-disc.cc b/src/traffic-control/model/prio-queue-disc.cc
new file mode 100644
index 000000000..6ace6e5ef
--- /dev/null
+++ b/src/traffic-control/model/prio-queue-disc.cc
@@ -0,0 +1,224 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2017 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: Stefano Avallone
+ */
+
+#include "ns3/log.h"
+#include "ns3/pointer.h"
+#include "ns3/object-factory.h"
+#include "ns3/socket.h"
+#include "prio-queue-disc.h"
+#include
+#include
+
+namespace ns3 {
+
+NS_LOG_COMPONENT_DEFINE ("PrioQueueDisc");
+
+NS_OBJECT_ENSURE_REGISTERED (PrioQueueDisc);
+
+ATTRIBUTE_HELPER_CPP (Priomap);
+
+std::ostream &
+operator << (std::ostream &os, const Priomap &priomap)
+{
+ std::copy (priomap.begin (), priomap.end ()-1, std::ostream_iterator(os, " "));
+ os << priomap.back ();
+ return os;
+}
+
+std::istream &operator >> (std::istream &is, Priomap &priomap)
+{
+ for (int i = 0; i < 16; i++)
+ {
+ if (!(is >> priomap[i]))
+ {
+ NS_FATAL_ERROR ("Incomplete priomap specification (" << i << " values provided, 16 required)");
+ }
+ }
+ return is;
+}
+
+TypeId PrioQueueDisc::GetTypeId (void)
+{
+ static TypeId tid = TypeId ("ns3::PrioQueueDisc")
+ .SetParent ()
+ .SetGroupName ("TrafficControl")
+ .AddConstructor ()
+ .AddAttribute ("Priomap", "The priority to band mapping.",
+ PriomapValue (Priomap{{1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}}),
+ MakePriomapAccessor (&PrioQueueDisc::m_prio2band),
+ MakePriomapChecker ())
+ ;
+ return tid;
+}
+
+PrioQueueDisc::PrioQueueDisc ()
+ : QueueDisc (QueueDiscSizePolicy::NO_LIMITS)
+{
+ NS_LOG_FUNCTION (this);
+}
+
+PrioQueueDisc::~PrioQueueDisc ()
+{
+ NS_LOG_FUNCTION (this);
+}
+
+void
+PrioQueueDisc::SetBandForPriority (uint8_t prio, uint16_t band)
+{
+ NS_LOG_FUNCTION (this << prio << band);
+
+ NS_ASSERT_MSG (prio < 16, "Priority must be a value between 0 and 15");
+
+ m_prio2band[prio] = band;
+}
+
+uint16_t
+PrioQueueDisc::GetBandForPriority (uint8_t prio) const
+{
+ NS_LOG_FUNCTION (this << prio);
+
+ NS_ASSERT_MSG (prio < 16, "Priority must be a value between 0 and 15");
+
+ return m_prio2band[prio];
+}
+
+bool
+PrioQueueDisc::DoEnqueue (Ptr item)
+{
+ NS_LOG_FUNCTION (this << item);
+
+ uint32_t band = m_prio2band[0];
+
+ int32_t ret = Classify (item);
+
+ if (ret == PacketFilter::PF_NO_MATCH)
+ {
+ NS_LOG_DEBUG ("No filter has been able to classify this packet, using priomap.");
+
+ SocketPriorityTag priorityTag;
+ if (item->GetPacket ()->PeekPacketTag (priorityTag))
+ {
+ band = m_prio2band[priorityTag.GetPriority () & 0x0f];
+ }
+ }
+ else
+ {
+ NS_LOG_DEBUG ("Packet filters returned " << ret);
+
+ if (ret >= 0 && static_cast(ret) < GetNQueueDiscClasses ())
+ {
+ band = ret;
+ }
+ }
+
+ NS_ASSERT_MSG (band < GetNQueueDiscClasses (), "Selected band out of range");
+ bool retval = GetQueueDiscClass (band)->GetQueueDisc ()->Enqueue (item);
+
+ // If Queue::Enqueue fails, QueueDisc::Drop is called by the child queue disc
+ // because QueueDisc::AddQueueDiscClass sets the drop callback
+
+ NS_LOG_LOGIC ("Number packets band " << band << ": " << GetQueueDiscClass (band)->GetQueueDisc ()->GetNPackets ());
+
+ return retval;
+}
+
+Ptr
+PrioQueueDisc::DoDequeue (void)
+{
+ NS_LOG_FUNCTION (this);
+
+ Ptr item;
+
+ for (uint32_t i = 0; i < GetNQueueDiscClasses (); i++)
+ {
+ if ((item = GetQueueDiscClass (i)->GetQueueDisc ()->Dequeue ()) != 0)
+ {
+ NS_LOG_LOGIC ("Popped from band " << i << ": " << item);
+ NS_LOG_LOGIC ("Number packets band " << i << ": " << GetQueueDiscClass (i)->GetQueueDisc ()->GetNPackets ());
+ return item;
+ }
+ }
+
+ NS_LOG_LOGIC ("Queue empty");
+ return item;
+}
+
+Ptr
+PrioQueueDisc::DoPeek (void)
+{
+ NS_LOG_FUNCTION (this);
+
+ Ptr item;
+
+ for (uint32_t i = 0; i < GetNQueueDiscClasses (); i++)
+ {
+ if ((item = GetQueueDiscClass (i)->GetQueueDisc ()->Peek ()) != 0)
+ {
+ NS_LOG_LOGIC ("Peeked from band " << i << ": " << item);
+ NS_LOG_LOGIC ("Number packets band " << i << ": " << GetQueueDiscClass (i)->GetQueueDisc ()->GetNPackets ());
+ return item;
+ }
+ }
+
+ NS_LOG_LOGIC ("Queue empty");
+ return item;
+}
+
+bool
+PrioQueueDisc::CheckConfig (void)
+{
+ NS_LOG_FUNCTION (this);
+ if (GetNInternalQueues () > 0)
+ {
+ NS_LOG_ERROR ("PrioQueueDisc cannot have internal queues");
+ return false;
+ }
+
+ if (GetNQueueDiscClasses () == 0)
+ {
+ // create 3 fifo queue discs
+ ObjectFactory factory;
+ factory.SetTypeId ("ns3::FifoQueueDisc");
+ for (uint8_t i = 0; i < 2; i++)
+ {
+ Ptr qd = factory.Create ();
+ qd->Initialize ();
+ Ptr c = CreateObject ();
+ c->SetQueueDisc (qd);
+ AddQueueDiscClass (c);
+ }
+ }
+
+ if (GetNQueueDiscClasses () < 2)
+ {
+ NS_LOG_ERROR ("PrioQueueDisc needs at least 2 classes");
+ return false;
+ }
+
+ return true;
+}
+
+void
+PrioQueueDisc::InitializeParams (void)
+{
+ NS_LOG_FUNCTION (this);
+}
+
+} // namespace ns3
diff --git a/src/traffic-control/model/prio-queue-disc.h b/src/traffic-control/model/prio-queue-disc.h
new file mode 100644
index 000000000..dd1abbe84
--- /dev/null
+++ b/src/traffic-control/model/prio-queue-disc.h
@@ -0,0 +1,113 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2017 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: Stefano Avallone
+ */
+
+#ifndef PRIO_QUEUE_DISC_H
+#define PRIO_QUEUE_DISC_H
+
+#include "ns3/queue-disc.h"
+#include
+
+namespace ns3 {
+
+typedef std::array Priomap;
+
+/**
+ * \ingroup traffic-control
+ *
+ * The Prio qdisc is a simple classful queueing discipline that contains an
+ * arbitrary number of classes of differing priority. The classes are dequeued
+ * in numerical descending order of priority. By default, three Fifo queue
+ * discs are created, unless the user provides (at least two) child queue
+ * discs.
+ *
+ * If no packet filter is installed or able to classify a packet, then the
+ * packet is assigned a priority band based on its priority (modulo 16), which
+ * is used as an index into an array called priomap. If a packet is classified
+ * by a packet filter and the returned value is non-negative and less than the
+ * number of priority bands, then the packet is assigned the priority band
+ * corresponding to the value returned by the packet filter. Otherwise, the
+ * packet is assigned the priority band specified by the first element of the
+ * priomap array.
+ */
+class PrioQueueDisc : public QueueDisc {
+public:
+ /**
+ * \brief Get the type ID.
+ * \return the object TypeId
+ */
+ static TypeId GetTypeId (void);
+ /**
+ * \brief PrioQueueDisc constructor
+ */
+ PrioQueueDisc ();
+
+ virtual ~PrioQueueDisc();
+
+ /**
+ * Set the band (class) assigned to packets with specified priority.
+ *
+ * \param prio the priority of packets (a value between 0 and 15).
+ * \param band the band assigned to packets.
+ */
+ void SetBandForPriority (uint8_t prio, uint16_t band);
+
+ /**
+ * Get the band (class) assigned to packets with specified priority.
+ *
+ * \param prio the priority of packets (a value between 0 and 15).
+ * \returns the band assigned to packets.
+ */
+ uint16_t GetBandForPriority (uint8_t prio) const;
+
+private:
+ virtual bool DoEnqueue (Ptr item);
+ virtual Ptr DoDequeue (void);
+ virtual Ptr DoPeek (void);
+ virtual bool CheckConfig (void);
+ virtual void InitializeParams (void);
+
+ Priomap m_prio2band; //!< Priority to band mapping
+};
+
+/**
+ * Serialize the priomap to the given ostream
+ *
+ * \param os
+ * \param priomap
+ *
+ * \return std::ostream
+ */
+std::ostream &operator << (std::ostream &os, const Priomap &priomap);
+
+/**
+ * Serialize from the given istream to this priomap.
+ *
+ * \param is
+ * \param priomap
+ *
+ * \return std::istream
+ */
+std::istream &operator >> (std::istream &is, Priomap &priomap);
+
+ATTRIBUTE_HELPER_HEADER (Priomap);
+
+} // namespace ns3
+
+#endif /* PRIO_QUEUE_DISC_H */
diff --git a/src/traffic-control/test/prio-queue-disc-test-suite.cc b/src/traffic-control/test/prio-queue-disc-test-suite.cc
new file mode 100644
index 000000000..9a2a6fc42
--- /dev/null
+++ b/src/traffic-control/test/prio-queue-disc-test-suite.cc
@@ -0,0 +1,324 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2017 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: Stefano Avallone
+ *
+ */
+
+#include "ns3/test.h"
+#include "ns3/prio-queue-disc.h"
+#include "ns3/fifo-queue-disc.h"
+#include "ns3/packet-filter.h"
+#include "ns3/packet.h"
+#include "ns3/socket.h"
+#include "ns3/string.h"
+#include "ns3/log.h"
+#include "ns3/simulator.h"
+#include
+#include
+
+using namespace ns3;
+
+/**
+ * \ingroup traffic-control-test
+ * \ingroup tests
+ *
+ * \brief Prio Queue Disc Test Item
+ */
+class PrioQueueDiscTestItem : public QueueDiscItem
+{
+public:
+ /**
+ * Constructor
+ *
+ * \param p the packet
+ * \param addr the address
+ * \param priority the packet priority
+ */
+ PrioQueueDiscTestItem (Ptr p, const Address & addr, uint8_t priority);
+ virtual ~PrioQueueDiscTestItem ();
+ virtual void AddHeader (void);
+ virtual bool Mark (void);
+};
+
+PrioQueueDiscTestItem::PrioQueueDiscTestItem (Ptr p, const Address & addr, uint8_t priority)
+ : QueueDiscItem (p, addr, 0)
+{
+ SocketPriorityTag priorityTag;
+ priorityTag.SetPriority (priority);
+ p->ReplacePacketTag (priorityTag);
+}
+
+PrioQueueDiscTestItem::~PrioQueueDiscTestItem ()
+{
+}
+
+void
+PrioQueueDiscTestItem::AddHeader (void)
+{
+}
+
+bool
+PrioQueueDiscTestItem::Mark (void)
+{
+ return false;
+}
+
+
+/**
+ * \ingroup traffic-control-test
+ * \ingroup tests
+ *
+ * \brief Prio Queue Disc Test Packet Filter
+ */
+class PrioQueueDiscTestFilter : public PacketFilter
+{
+public:
+ /**
+ * Constructor
+ *
+ * \param cls whether this filter is able to classify a PrioQueueDiscTestItem
+ */
+ PrioQueueDiscTestFilter (bool cls);
+ virtual ~PrioQueueDiscTestFilter ();
+ /**
+ * \brief Set the value returned by DoClassify
+ *
+ * \param ret the value that DoClassify returns
+ */
+ void SetReturnValue (int32_t ret);
+
+private:
+ virtual bool CheckProtocol (Ptr item) const;
+ virtual int32_t DoClassify (Ptr item) const;
+
+ bool m_cls; //!< whether this filter is able to classify a PrioQueueDiscTestItem
+ int32_t m_ret; //!< the value that DoClassify returns if m_cls is true
+};
+
+PrioQueueDiscTestFilter::PrioQueueDiscTestFilter (bool cls)
+ : m_cls (cls),
+ m_ret (0)
+{
+}
+
+PrioQueueDiscTestFilter::~PrioQueueDiscTestFilter ()
+{
+}
+
+void
+PrioQueueDiscTestFilter::SetReturnValue (int32_t ret)
+{
+ m_ret = ret;
+}
+
+bool
+PrioQueueDiscTestFilter::CheckProtocol (Ptr item) const
+{
+ return m_cls;
+}
+
+int32_t
+PrioQueueDiscTestFilter::DoClassify (Ptr item) const
+{
+ return m_ret;
+}
+
+
+/**
+ * \ingroup traffic-control-test
+ * \ingroup tests
+ *
+ * \brief Prio Queue Disc Test Case
+ */
+class PrioQueueDiscTestCase : public TestCase
+{
+public:
+ PrioQueueDiscTestCase ();
+ virtual void DoRun (void);
+};
+
+PrioQueueDiscTestCase::PrioQueueDiscTestCase ()
+ : TestCase ("Sanity check on the prio queue disc implementation")
+{
+}
+
+void
+PrioQueueDiscTestCase::DoRun (void)
+{
+ Ptr qdisc;
+ Ptr item;
+ std::string priomap ("0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3");
+ Address dest;
+ std::array,4> uids;
+
+ /*
+ * Test 1: set priomap
+ */
+ qdisc = CreateObject ();
+
+ // add 4 child fifo queue discs
+ for (uint8_t i = 0; i < 4; i++)
+ {
+ Ptr child = CreateObject ();
+ child->Initialize ();
+ Ptr c = CreateObject ();
+ c->SetQueueDisc (child);
+ qdisc->AddQueueDiscClass (c);
+ }
+ qdisc->Initialize ();
+
+ NS_TEST_EXPECT_MSG_EQ (qdisc->GetNQueueDiscClasses (), 4, "Verify that the queue disc has 4 child queue discs");
+
+ NS_TEST_EXPECT_MSG_EQ (qdisc->SetAttributeFailSafe ("Priomap", StringValue (priomap)),
+ true, "Verify that we can actually set the attribute Priomap");
+
+ StringValue sv;
+ NS_TEST_EXPECT_MSG_EQ (qdisc->GetAttributeFailSafe ("Priomap", sv),
+ true, "Verify that we can actually get the attribute Priomap");
+
+ NS_TEST_EXPECT_MSG_EQ (sv.Get (), priomap, "Verify that the priomap has been correctly set");
+
+ /*
+ * Test 2: classify packets based on priomap because no packet filter is installed
+ */
+
+ // create packets with priorities from 0 to 3
+ for (uint16_t i = 0; i < 4; i++)
+ {
+ NS_TEST_EXPECT_MSG_EQ (qdisc->GetQueueDiscClass (i)->GetQueueDisc ()->GetNPackets (),
+ 0, "There should be no packets in the child queue disc " << i);
+
+ item = Create (Create (100), dest, i);
+ qdisc->Enqueue (item);
+ // packet is assigned band i
+ uids[i].push (item->GetPacket ()->GetUid ());
+
+ NS_TEST_EXPECT_MSG_EQ (qdisc->GetQueueDiscClass (i)->GetQueueDisc ()->GetNPackets (),
+ 1, "There should be one packet in the child queue disc " << i);
+ }
+
+ /*
+ * Test 3: classify packets based on priomap because no packet filter able
+ * to classify packets is installed
+ */
+
+ Ptr pf1 = CreateObject (false);
+ qdisc->AddPacketFilter (pf1);
+
+ // create packets with priorities from 4 to 7
+ for (uint16_t i = 0; i < 4; i++)
+ {
+ NS_TEST_EXPECT_MSG_EQ (qdisc->GetQueueDiscClass (i)->GetQueueDisc ()->GetNPackets (),
+ 1, "There should be one packet in the child queue disc " << i);
+
+ item = Create (Create (100), dest, i+4);
+ qdisc->Enqueue (item);
+ // packet is assigned band i
+ uids[i].push (item->GetPacket ()->GetUid ());
+
+ NS_TEST_EXPECT_MSG_EQ (qdisc->GetQueueDiscClass (i)->GetQueueDisc ()->GetNPackets (),
+ 2, "There should be two packets in the child queue disc " << i);
+ }
+
+ /*
+ * Test 4: classify packets based on the value returned by the installed packet filter
+ */
+
+ Ptr pf2 = CreateObject (true);
+ qdisc->AddPacketFilter (pf2);
+
+ // create packets with priority 0 (which is neglected by the prio queue disc)
+ for (uint16_t i = 0; i < 4; i++)
+ {
+ pf2->SetReturnValue (i);
+ NS_TEST_EXPECT_MSG_EQ (qdisc->GetQueueDiscClass (i)->GetQueueDisc ()->GetNPackets (),
+ 2, "There should be two packets in the child queue disc " << i);
+
+ item = Create (Create (100), dest, 0);
+ qdisc->Enqueue (item);
+ // packet is assigned band i
+ uids[i].push (item->GetPacket ()->GetUid ());
+
+ NS_TEST_EXPECT_MSG_EQ (qdisc->GetQueueDiscClass (i)->GetQueueDisc ()->GetNPackets (),
+ 3, "There should be three packets in the child queue disc " << i);
+ }
+
+ /*
+ * Test 5: classify packets into the band specified by the first element of the
+ * priomap array because the value returned by the installed packet filter is
+ * not less than the number of bands
+ */
+
+ // create packets with priority 1 (which is neglected by the prio queue disc)
+ for (uint16_t i = 0; i < 4; i++)
+ {
+ pf2->SetReturnValue (4+i);
+ NS_TEST_EXPECT_MSG_EQ (qdisc->GetBandForPriority (0), 0, "The band for priority 0 must be band 0");
+ NS_TEST_EXPECT_MSG_EQ (qdisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), i+3u,
+ "There should be " << i+3 << " packets in the child queue disc "
+ << qdisc->GetBandForPriority (0));
+
+ item = Create (Create (100), dest, 1);
+ qdisc->Enqueue (item);
+ // packet is assigned band 0
+ uids[0].push (item->GetPacket ()->GetUid ());
+
+ NS_TEST_EXPECT_MSG_EQ (qdisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), i+4u,
+ "There should be " << i+4 << " packets in the child queue disc "
+ << qdisc->GetBandForPriority (0));
+ }
+
+ /*
+ * Test 6: dequeue packets starting from the highest priority band (band 0)
+ */
+
+ while ((item = qdisc->Dequeue ()))
+ {
+ for (uint16_t i = 0; i < 4; i++)
+ {
+ if (uids[i].empty ())
+ {
+ NS_TEST_EXPECT_MSG_EQ (qdisc->GetQueueDiscClass (i)->GetQueueDisc ()->GetNPackets (),
+ 0, "Band " << i << " should be empty");
+ continue;
+ }
+ NS_TEST_EXPECT_MSG_EQ (uids[i].front (), item->GetPacket ()->GetUid (),
+ "The dequeued packet is not the one we expected");
+ uids[i].pop ();
+ break;
+ }
+ }
+
+ Simulator::Destroy ();
+}
+
+/**
+ * \ingroup traffic-control-test
+ * \ingroup tests
+ *
+ * \brief Prio Queue Disc Test Suite
+ */
+static class PrioQueueDiscTestSuite : public TestSuite
+{
+public:
+ PrioQueueDiscTestSuite ()
+ : TestSuite ("prio-queue-disc", UNIT)
+ {
+ AddTestCase (new PrioQueueDiscTestCase (), TestCase::QUICK);
+ }
+} g_prioQueueTestSuite; ///< the test suite
diff --git a/src/traffic-control/wscript b/src/traffic-control/wscript
index 5f84e9064..48fcc25c5 100644
--- a/src/traffic-control/wscript
+++ b/src/traffic-control/wscript
@@ -18,6 +18,7 @@ def build(bld):
'model/codel-queue-disc.cc',
'model/fq-codel-queue-disc.cc',
'model/pie-queue-disc.cc',
+ 'model/prio-queue-disc.cc',
'model/mq-queue-disc.cc',
'model/tbf-queue-disc.cc',
'helper/traffic-control-helper.cc',
@@ -31,6 +32,7 @@ def build(bld):
'test/adaptive-red-queue-disc-test-suite.cc',
'test/pie-queue-disc-test-suite.cc',
'test/fifo-queue-disc-test-suite.cc',
+ 'test/prio-queue-disc-test-suite.cc',
'test/tbf-queue-disc-test-suite.cc',
'test/tc-flow-control-test-suite.cc'
]
@@ -47,6 +49,7 @@ def build(bld):
'model/codel-queue-disc.h',
'model/fq-codel-queue-disc.h',
'model/pie-queue-disc.h',
+ 'model/prio-queue-disc.h',
'model/mq-queue-disc.h',
'model/tbf-queue-disc.h',
'helper/traffic-control-helper.h',