From 6c0840352d206176ddbe3e4b60c44fd2cbf5bc98 Mon Sep 17 00:00:00 2001
From: Stefano Avallone
Date: Sun, 28 May 2017 10:21:32 +0200
Subject: [PATCH] traffic-control: Add the mq queue disc
---
CHANGES.html | 2 +
doc/models/Makefile | 1 +
doc/models/source/traffic-control.rst | 1 +
.../ns3wifi/wifi-ac-mapping-test-suite.cc | 117 ++++++++++++++----
src/traffic-control/doc/mq.rst | 83 +++++++++++++
src/traffic-control/model/mq-queue-disc.cc | 101 +++++++++++++++
src/traffic-control/model/mq-queue-disc.h | 66 ++++++++++
src/traffic-control/wscript | 2 +
8 files changed, 346 insertions(+), 27 deletions(-)
create mode 100644 src/traffic-control/doc/mq.rst
create mode 100644 src/traffic-control/model/mq-queue-disc.cc
create mode 100644 src/traffic-control/model/mq-queue-disc.h
diff --git a/CHANGES.html b/CHANGES.html
index d2e74c9a8..b2e1000d5 100644
--- a/CHANGES.html
+++ b/CHANGES.html
@@ -93,6 +93,8 @@ us a note on ns-developers mailing list.
which returns a vector of pairs (dscp,count), each of which indicates how many packets with the
associated dscp value have been classified for a given flow.
+MqQueueDisc, a multi-queue aware queue disc modelled after the mq qdisc in Linux, has been introduced.
+
Changes to existing API:
diff --git a/doc/models/Makefile b/doc/models/Makefile
index 405a08b82..066bdf15b 100644
--- a/doc/models/Makefile
+++ b/doc/models/Makefile
@@ -83,6 +83,7 @@ SOURCES = \
$(SRC)/traffic-control/doc/codel.rst \
$(SRC)/traffic-control/doc/fq-codel.rst \
$(SRC)/traffic-control/doc/pie.rst \
+ $(SRC)/traffic-control/doc/mq.rst \
$(SRC)/spectrum/doc/spectrum.rst \
$(SRC)/stats/doc/adaptor.rst \
$(SRC)/stats/doc/aggregator.rst \
diff --git a/doc/models/source/traffic-control.rst b/doc/models/source/traffic-control.rst
index fe5562513..1a6dddea0 100644
--- a/doc/models/source/traffic-control.rst
+++ b/doc/models/source/traffic-control.rst
@@ -10,3 +10,4 @@ Traffic Control Layer
codel
fq-codel
pie
+ mq
diff --git a/src/test/ns3wifi/wifi-ac-mapping-test-suite.cc b/src/test/ns3wifi/wifi-ac-mapping-test-suite.cc
index 990c914de..02e113be7 100644
--- a/src/test/ns3wifi/wifi-ac-mapping-test-suite.cc
+++ b/src/test/ns3wifi/wifi-ac-mapping-test-suite.cc
@@ -41,6 +41,9 @@
#include "ns3/ipv4-address-helper.h"
#include "ns3/packet-sink-helper.h"
#include "ns3/on-off-helper.h"
+#include "ns3/traffic-control-helper.h"
+#include "ns3/traffic-control-layer.h"
+#include "ns3/llc-snap-header.h"
using namespace ns3;
@@ -53,15 +56,54 @@ public:
virtual void DoRun (void);
private:
+ static void PacketEnqueuedInQueueDisc (uint8_t tos, uint8_t* count, Ptr item);
+ static void PacketEnqueuedInWifiMacQueue (uint8_t tos, uint8_t* count, Ptr item);
uint8_t m_tos;
uint8_t m_expectedQueue;
+ uint8_t m_QueueDiscCount[4];
+ uint8_t m_WifiMacQueueCount[4];
};
WifiAcMappingTest::WifiAcMappingTest (uint8_t tos, uint8_t expectedQueue)
- : TestCase ("User priority to Access Category mapping test"),
+ : TestCase ("User priority to Access Category mapping test. Checks that packets are"
+ "enqueued in the correct child queue disc of the mq root queue disc and"
+ "in the correct wifi MAC queue"),
m_tos (tos),
m_expectedQueue (expectedQueue)
{
+ for (uint8_t i = 0; i < 4; i++)
+ {
+ m_QueueDiscCount[i] = 0;
+ m_WifiMacQueueCount[i] = 0;
+ }
+}
+
+void
+WifiAcMappingTest::PacketEnqueuedInQueueDisc (uint8_t tos, uint8_t* count, Ptr item)
+{
+ uint8_t val;
+ if (item->GetUint8Value (QueueItem::IP_DSFIELD, val) && val == tos)
+ {
+ (*count)++;
+ }
+}
+
+void
+WifiAcMappingTest::PacketEnqueuedInWifiMacQueue (uint8_t tos, uint8_t* count, Ptr item)
+{
+ LlcSnapHeader llc;
+ Ptr packet = item->GetPacket ()->Copy ();
+ packet->RemoveHeader (llc);
+
+ if (llc.GetType () == Ipv4L3Protocol::PROT_NUMBER)
+ {
+ Ipv4Header iph;
+ packet->PeekHeader (iph);
+ if (iph.GetTos () == tos)
+ {
+ (*count)++;
+ }
+ }
}
void
@@ -111,6 +153,17 @@ WifiAcMappingTest::DoRun (void)
stack.Install (ap);
stack.Install (sta);
+ TrafficControlHelper tch;
+ uint16_t handle = tch.SetRootQueueDisc ("ns3::MqQueueDisc");
+ TrafficControlHelper::ClassIdList cls = tch.AddQueueDiscClasses (handle, 4, "ns3::QueueDiscClass");
+ TrafficControlHelper::HandleList hdl = tch.AddChildQueueDiscs (handle, cls, "ns3::FqCoDelQueueDisc");
+ for (auto h : hdl)
+ {
+ tch.AddPacketFilter (h, "ns3::FqCoDelIpv4PacketFilter");
+ }
+ tch.Install (apDev);
+ tch.Install (staDev);
+
Ipv4AddressHelper address;
address.SetBase ("192.168.0.0", "255.255.255.0");
Ipv4InterfaceContainer staNodeInterface, apNodeInterface;
@@ -123,7 +176,7 @@ WifiAcMappingTest::DoRun (void)
InetSocketAddress (Ipv4Address::GetAny (), udpPort));
ApplicationContainer sinkApp = packetSink.Install (sta.Get (0));
sinkApp.Start (Seconds (0));
- sinkApp.Stop (Seconds (3.0));
+ sinkApp.Stop (Seconds (4.0));
// The packet source is an on-off application on the AP device
InetSocketAddress dest (staNodeInterface.GetAddress (0), udpPort);
@@ -132,41 +185,49 @@ WifiAcMappingTest::DoRun (void)
onoff.SetConstantRate (DataRate ("5kbps"), 500);
ApplicationContainer sourceApp = onoff.Install (ap.Get (0));
sourceApp.Start (Seconds (1.0));
- sourceApp.Stop (Seconds (3.0));
+ sourceApp.Stop (Seconds (4.0));
// The first packet will be transmitted at time 1+(500*8)/5000 = 1.8s.
- // Let this packet be transmitted successfully, so that the AP can resolve
- // the IP address of the station and get its MAC address.
// The second packet will be transmitted at time 1.8+(500*8)/5000 = 2.6s.
- // Put the AP in sleep mode at time 2.0s, so that the second packet remains
- // in a wifi mac queue and we can discover the queue it has been sent to.
- Ptr apPhy = DynamicCast (apDev.Get (0))->GetPhy ();
- Simulator::ScheduleWithContext (ap.Get (0)->GetId (), Seconds (2.0),
- &WifiPhy::SetSleepMode, apPhy);
+ // The third packet will be transmitted at time 2.6+(500*8)/5000 = 3.4s.
- Simulator::Stop (Seconds (4.0));
+ Simulator::Stop (Seconds (5.0));
+
+ Ptr root = ap.Get (0)->GetObject ()->GetRootQueueDiscOnDevice (apDev.Get (0));
+ NS_TEST_ASSERT_MSG_EQ (root->GetNQueueDiscClasses (), 4, "The root queue disc should have 4 classes");
+ // Get the four child queue discs and connect their Enqueue trace to the PacketEnqueuedInQueueDisc
+ // method, which counts how many packets with the given ToS value have been enqueued
+ root->GetQueueDiscClass (0)->GetQueueDisc ()->TraceConnectWithoutContext ("Enqueue",
+ MakeBoundCallback (&WifiAcMappingTest::PacketEnqueuedInQueueDisc, m_tos, m_QueueDiscCount));
+
+ root->GetQueueDiscClass (1)->GetQueueDisc ()->TraceConnectWithoutContext ("Enqueue",
+ MakeBoundCallback (&WifiAcMappingTest::PacketEnqueuedInQueueDisc, m_tos, m_QueueDiscCount+1));
+
+ root->GetQueueDiscClass (2)->GetQueueDisc ()->TraceConnectWithoutContext ("Enqueue",
+ MakeBoundCallback (&WifiAcMappingTest::PacketEnqueuedInQueueDisc, m_tos, m_QueueDiscCount+2));
+
+ root->GetQueueDiscClass (3)->GetQueueDisc ()->TraceConnectWithoutContext ("Enqueue",
+ MakeBoundCallback (&WifiAcMappingTest::PacketEnqueuedInQueueDisc, m_tos, m_QueueDiscCount+3));
Ptr apMac = DynamicCast (apDev.Get (0))->GetMac ();
PointerValue ptr;
- Ptr queues[4];
- // Get the four wifi mac queues and set their MaxDelay attribute to a large
- // value, so that packets are not removed by the Cleanup method
+ // Get the four wifi mac queues and connect their Enqueue trace to the PacketEnqueuedInWifiMacQueue
+ // method, which counts how many packets with the given ToS value have been enqueued
apMac->GetAttribute ("BE_EdcaTxopN", ptr);
- queues[0] = ptr.Get ()->GetQueue ();
- queues[0]->SetAttribute ("MaxDelay", TimeValue (Seconds (10.0)));
+ ptr.Get ()->GetQueue ()->TraceConnectWithoutContext ("Enqueue",
+ MakeBoundCallback (&WifiAcMappingTest::PacketEnqueuedInWifiMacQueue, m_tos, m_WifiMacQueueCount));
apMac->GetAttribute ("BK_EdcaTxopN", ptr);
- queues[1] = ptr.Get ()->GetQueue ();
- queues[1]->SetAttribute ("MaxDelay", TimeValue (Seconds (10.0)));
+ ptr.Get ()->GetQueue ()->TraceConnectWithoutContext ("Enqueue",
+ MakeBoundCallback (&WifiAcMappingTest::PacketEnqueuedInWifiMacQueue, m_tos, m_WifiMacQueueCount+1));
apMac->GetAttribute ("VI_EdcaTxopN", ptr);
- queues[2] = ptr.Get ()->GetQueue ();
- queues[2]->SetAttribute ("MaxDelay", TimeValue (Seconds (10.0)));
+ ptr.Get ()->GetQueue ()->TraceConnectWithoutContext ("Enqueue",
+ MakeBoundCallback (&WifiAcMappingTest::PacketEnqueuedInWifiMacQueue, m_tos, m_WifiMacQueueCount+2));
apMac->GetAttribute ("VO_EdcaTxopN", ptr);
- queues[3] = ptr.Get ()->GetQueue ();
- queues[3]->SetAttribute ("MaxDelay", TimeValue (Seconds (10.0)));
-
+ ptr.Get ()->GetQueue ()->TraceConnectWithoutContext ("Enqueue",
+ MakeBoundCallback (&WifiAcMappingTest::PacketEnqueuedInWifiMacQueue, m_tos, m_WifiMacQueueCount+3));
Simulator::Run ();
@@ -174,19 +235,21 @@ WifiAcMappingTest::DoRun (void)
{
if (i == m_expectedQueue)
{
- NS_TEST_ASSERT_MSG_EQ (queues[i]->GetNPackets (), 1, "There is no packet in the expected queue " << i);
+ NS_TEST_ASSERT_MSG_GT_OR_EQ (m_QueueDiscCount[i], 1, "There is no packet in the expected queue disc " << i);
+ NS_TEST_ASSERT_MSG_GT_OR_EQ (m_WifiMacQueueCount[i], 1, "There is no packet in the expected Wifi MAC queue " << i);
}
else
{
- NS_TEST_ASSERT_MSG_EQ (queues[i]->GetNPackets (), 0, "Unexpectedly, there is a packet in queue " << i);
+ NS_TEST_ASSERT_MSG_EQ (m_QueueDiscCount[i], 0, "Unexpectedly, there is a packet in queue disc " << i);
+ NS_TEST_ASSERT_MSG_EQ (m_WifiMacQueueCount[i], 0, "Unexpectedly, there is a packet in Wifi MAC queue " << i);
}
}
uint32_t totalOctetsThrough =
DynamicCast (sinkApp.Get (0))->GetTotalRx ();
- // Check that the first packet has been received
- NS_TEST_ASSERT_MSG_EQ (totalOctetsThrough, 500, "A single packet should have been received");
+ // Check that the three packets have been received
+ NS_TEST_ASSERT_MSG_EQ (totalOctetsThrough, 1500, "Three packets should have been received");
Simulator::Destroy ();
}
diff --git a/src/traffic-control/doc/mq.rst b/src/traffic-control/doc/mq.rst
new file mode 100644
index 000000000..6b973a91d
--- /dev/null
+++ b/src/traffic-control/doc/mq.rst
@@ -0,0 +1,83 @@
+.. include:: replace.txt
+.. highlight:: cpp
+.. highlight:: bash
+
+Mq queue disc
+------------------
+
+This chapter describes the mq queue disc implementation in |ns3|.
+
+mq is a classful multiqueue dummy scheduler developed to best fit the multiqueue
+traffic control API in Linux. The mq scheduler presents device transmission queues as
+classes, allowing to attach different queue discs to them, which are grafted to the
+device transmission queues.
+
+Model Description
+*****************
+
+mq is a multi-queue aware queue disc, meaning that it has as many child queue discs as
+the number of device transmission queues. Each child queue disc maps to a distinct
+device transmission queue. Every packet is enqueued into the child queue disc which
+maps to the device transmission queue in which the device will enqueue
+the packet.
+
+In |ns3|, :cpp:class:`MqQueueDisc` has a wake mode of WAKE_CHILD, which means that the
+traffic control layer enqueues packets directly into one of the child queue discs
+(multi-queue devices can provide a callback to inform the traffic control layer of
+the device transmission queue that will be selected for a given packet). Therefore,
+``MqQueueDisc::DoEnqueue ()`` shall never be called (in fact, it raises a fatal error).
+Given that dequeuing packets is triggered by enqueuing a packet in the queue disc or
+by the device invoking the wake callback, it turns out that ``MqQueueDisc::DoDequeue ()``
+is never called as well (in fact, it raises a fatal error, too).
+
+The mq queue disc does not require packet filters, does not admit internal queues
+and must have as many child queue discs as the number of device transmission queues.
+
+Examples
+========
+
+A typical usage pattern is to create a traffic control helper used to add the required number of
+queue disc classes, attach child queue discs to the classes and (if needed) add packet filters to the
+child queue discs. The following code shows how to install an mq queue disc having FqCodel child queue
+discs:
+
+.. sourcecode:: cpp
+
+ TrafficControlHelper tch;
+ uint16_t handle = tch.SetRootQueueDisc ("ns3::MqQueueDisc");
+ TrafficControlHelper::ClassIdList cls = tch.AddQueueDiscClasses (handle, numTxQueues, "ns3::QueueDiscClass");
+ TrafficControlHelper::HandleList hdl = tch.AddChildQueueDiscs (handle, cls, "ns3::FqCoDelQueueDisc");
+ for (auto h : hdl)
+ {
+ tch.AddPacketFilter (h, "ns3::FqCoDelIpv4PacketFilter");
+ }
+ QueueDiscContainer qdiscs = tch.Install (devices);
+
+Note that the child queue discs attached to the classes do not necessarily have to be of the same type.
+
+Validation
+**********
+
+The mq model is tested using :cpp:class:`WifiAcMappingTestSuite` class defined in
+`src/test/wifi-ac-mapping-test-suite.cc`. The suite considers a node with a QoS-enabled
+wifi device (which has 4 transmission queues) and includes 4 test cases:
+
+* Test 1: EF-marked packets are enqueued in the queue disc which maps to the AC_VI queue
+* Test 2: AF11-marked packets are enqueued in the queue disc which maps to the AC_BK queue
+* Test 3: AF32-marked packets are enqueued in the queue disc which maps to the AC_BE queue
+* Test 4: CS7-marked packets are enqueued in the queue disc which maps to the AC_VO queue
+
+The test suite can be run using the following commands:
+
+::
+
+ $ ./waf configure --enable-examples --enable-tests
+ $ ./waf build
+ $ ./test.py -s ns3-wifi-ac-mapping
+
+or
+
+::
+
+ $ NS_LOG="WifiAcMappingTest" ./waf --run "test-runner --suite=ns3-wifi-ac-mapping"
+
diff --git a/src/traffic-control/model/mq-queue-disc.cc b/src/traffic-control/model/mq-queue-disc.cc
new file mode 100644
index 000000000..6c2e51c89
--- /dev/null
+++ b/src/traffic-control/model/mq-queue-disc.cc
@@ -0,0 +1,101 @@
+/* -*- 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
+ * Stefano Avallone
+ */
+
+#include "ns3/log.h"
+#include "mq-queue-disc.h"
+
+namespace ns3 {
+
+NS_LOG_COMPONENT_DEFINE ("MqQueueDisc");
+
+NS_OBJECT_ENSURE_REGISTERED (MqQueueDisc);
+
+TypeId MqQueueDisc::GetTypeId (void)
+{
+ static TypeId tid = TypeId ("ns3::MqQueueDisc")
+ .SetParent ()
+ .SetGroupName ("TrafficControl")
+ .AddConstructor ()
+ ;
+ return tid;
+}
+
+MqQueueDisc::MqQueueDisc ()
+{
+ NS_LOG_FUNCTION (this);
+}
+
+MqQueueDisc::~MqQueueDisc ()
+{
+ NS_LOG_FUNCTION (this);
+}
+
+MqQueueDisc::WakeMode
+MqQueueDisc::GetWakeMode (void) const
+{
+ return WAKE_CHILD;
+}
+
+bool
+MqQueueDisc::DoEnqueue (Ptr item)
+{
+ NS_FATAL_ERROR ("MqQueueDisc: DoEnqueue should never be called");
+}
+
+Ptr
+MqQueueDisc::DoDequeue (void)
+{
+ NS_FATAL_ERROR ("MqQueueDisc: DoDequeue should never be called");
+}
+
+Ptr
+MqQueueDisc::DoPeek (void) const
+{
+ NS_FATAL_ERROR ("MqQueueDisc: DoPeek should never be called");
+}
+
+bool
+MqQueueDisc::CheckConfig (void)
+{
+ NS_LOG_FUNCTION (this);
+
+ if (GetNPacketFilters () > 0)
+ {
+ NS_LOG_ERROR ("MqQueueDisc cannot have packet filters");
+ return false;
+ }
+
+ if (GetNInternalQueues () > 0)
+ {
+ NS_LOG_ERROR ("MqQueueDisc cannot have internal queues");
+ return false;
+ }
+
+ return true;
+}
+
+void
+MqQueueDisc::InitializeParams (void)
+{
+ NS_LOG_FUNCTION (this);
+}
+
+} // namespace ns3
diff --git a/src/traffic-control/model/mq-queue-disc.h b/src/traffic-control/model/mq-queue-disc.h
new file mode 100644
index 000000000..8bf23c477
--- /dev/null
+++ b/src/traffic-control/model/mq-queue-disc.h
@@ -0,0 +1,66 @@
+/* -*- 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
+ * Stefano Avallone
+ */
+
+#ifndef MQ_QUEUE_DISC_H
+#define MQ_QUEUE_DISC_H
+
+#include "ns3/queue-disc.h"
+
+namespace ns3 {
+
+/**
+ * \ingroup traffic-control
+ *
+ * mq is a classful multi-queue aware dummy scheduler. It has as many child
+ * queue discs as the number of device transmission queues. Packets are
+ * directly enqueued into and dequeued from child queue discs.
+ */
+class MqQueueDisc : public QueueDisc {
+public:
+ /**
+ * \brief Get the type ID.
+ * \return the object TypeId
+ */
+ static TypeId GetTypeId (void);
+ /**
+ * \brief MqQueueDisc constructor
+ */
+ MqQueueDisc ();
+
+ virtual ~MqQueueDisc();
+
+ /**
+ * \brief Return the wake mode adopted by this queue disc.
+ * \return the wake mode adopted by this queue disc.
+ */
+ WakeMode GetWakeMode (void) const;
+
+private:
+ virtual bool DoEnqueue (Ptr item);
+ virtual Ptr DoDequeue (void);
+ virtual Ptr DoPeek (void) const;
+ virtual bool CheckConfig (void);
+ virtual void InitializeParams (void);
+};
+
+} // namespace ns3
+
+#endif /* MQ_QUEUE_DISC_H */
diff --git a/src/traffic-control/wscript b/src/traffic-control/wscript
index 21ebed4ef..91dfeb7c6 100644
--- a/src/traffic-control/wscript
+++ b/src/traffic-control/wscript
@@ -17,6 +17,7 @@ def build(bld):
'model/codel-queue-disc.cc',
'model/fq-codel-queue-disc.cc',
'model/pie-queue-disc.cc',
+ 'model/mq-queue-disc.cc',
'helper/traffic-control-helper.cc',
'helper/queue-disc-container.cc'
]
@@ -41,6 +42,7 @@ def build(bld):
'model/codel-queue-disc.h',
'model/fq-codel-queue-disc.h',
'model/pie-queue-disc.h',
+ 'model/mq-queue-disc.h',
'helper/traffic-control-helper.h',
'helper/queue-disc-container.h'
]