traffic-control: Add the mq queue disc

This commit is contained in:
Stefano Avallone
2017-05-28 10:21:32 +02:00
parent 4bbf7c5e1b
commit 6c0840352d
8 changed files with 346 additions and 27 deletions

View File

@@ -93,6 +93,8 @@ us a note on ns-developers mailing list.</p>
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.
</li>
<li>MqQueueDisc, a multi-queue aware queue disc modelled after the mq qdisc in Linux, has been introduced.
</li>
</ul>
<h2>Changes to existing API:</h2>
<ul>

View File

@@ -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 \

View File

@@ -10,3 +10,4 @@ Traffic Control Layer
codel
fq-codel
pie
mq

View File

@@ -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<const QueueDiscItem> item);
static void PacketEnqueuedInWifiMacQueue (uint8_t tos, uint8_t* count, Ptr<const WifiMacQueueItem> 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<const QueueDiscItem> item)
{
uint8_t val;
if (item->GetUint8Value (QueueItem::IP_DSFIELD, val) && val == tos)
{
(*count)++;
}
}
void
WifiAcMappingTest::PacketEnqueuedInWifiMacQueue (uint8_t tos, uint8_t* count, Ptr<const WifiMacQueueItem> item)
{
LlcSnapHeader llc;
Ptr<Packet> 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<WifiPhy> apPhy = DynamicCast<WifiNetDevice> (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<QueueDisc> root = ap.Get (0)->GetObject<TrafficControlLayer> ()->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<WifiMac> apMac = DynamicCast<WifiNetDevice> (apDev.Get (0))->GetMac ();
PointerValue ptr;
Ptr<WifiMacQueue> 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<EdcaTxopN> ()->GetQueue ();
queues[0]->SetAttribute ("MaxDelay", TimeValue (Seconds (10.0)));
ptr.Get<EdcaTxopN> ()->GetQueue ()->TraceConnectWithoutContext ("Enqueue",
MakeBoundCallback (&WifiAcMappingTest::PacketEnqueuedInWifiMacQueue, m_tos, m_WifiMacQueueCount));
apMac->GetAttribute ("BK_EdcaTxopN", ptr);
queues[1] = ptr.Get<EdcaTxopN> ()->GetQueue ();
queues[1]->SetAttribute ("MaxDelay", TimeValue (Seconds (10.0)));
ptr.Get<EdcaTxopN> ()->GetQueue ()->TraceConnectWithoutContext ("Enqueue",
MakeBoundCallback (&WifiAcMappingTest::PacketEnqueuedInWifiMacQueue, m_tos, m_WifiMacQueueCount+1));
apMac->GetAttribute ("VI_EdcaTxopN", ptr);
queues[2] = ptr.Get<EdcaTxopN> ()->GetQueue ();
queues[2]->SetAttribute ("MaxDelay", TimeValue (Seconds (10.0)));
ptr.Get<EdcaTxopN> ()->GetQueue ()->TraceConnectWithoutContext ("Enqueue",
MakeBoundCallback (&WifiAcMappingTest::PacketEnqueuedInWifiMacQueue, m_tos, m_WifiMacQueueCount+2));
apMac->GetAttribute ("VO_EdcaTxopN", ptr);
queues[3] = ptr.Get<EdcaTxopN> ()->GetQueue ();
queues[3]->SetAttribute ("MaxDelay", TimeValue (Seconds (10.0)));
ptr.Get<EdcaTxopN> ()->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<PacketSink> (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 ();
}

View File

@@ -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"

View File

@@ -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 <p.imputato@gmail.com>
* Stefano Avallone <stavallo@unina.it>
*/
#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<QueueDisc> ()
.SetGroupName ("TrafficControl")
.AddConstructor<MqQueueDisc> ()
;
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<QueueDiscItem> item)
{
NS_FATAL_ERROR ("MqQueueDisc: DoEnqueue should never be called");
}
Ptr<QueueDiscItem>
MqQueueDisc::DoDequeue (void)
{
NS_FATAL_ERROR ("MqQueueDisc: DoDequeue should never be called");
}
Ptr<const QueueDiscItem>
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

View File

@@ -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 <p.imputato@gmail.com>
* Stefano Avallone <stavallo@unina.it>
*/
#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<QueueDiscItem> item);
virtual Ptr<QueueDiscItem> DoDequeue (void);
virtual Ptr<const QueueDiscItem> DoPeek (void) const;
virtual bool CheckConfig (void);
virtual void InitializeParams (void);
};
} // namespace ns3
#endif /* MQ_QUEUE_DISC_H */

View File

@@ -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'
]