1067 lines
31 KiB
C++
1067 lines
31 KiB
C++
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
/*
|
|
* Copyright (c) 2007, 2014 University of Washington
|
|
* 2015 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
|
|
*/
|
|
|
|
#include "ns3/log.h"
|
|
#include "ns3/abort.h"
|
|
#include "ns3/uinteger.h"
|
|
#include "ns3/pointer.h"
|
|
#include "ns3/object-vector.h"
|
|
#include "ns3/packet.h"
|
|
#include "ns3/socket.h"
|
|
#include "ns3/unused.h"
|
|
#include "ns3/simulator.h"
|
|
#include "queue-disc.h"
|
|
#include <ns3/drop-tail-queue.h>
|
|
#include "ns3/net-device-queue-interface.h"
|
|
|
|
namespace ns3 {
|
|
|
|
NS_OBJECT_TEMPLATE_CLASS_DEFINE (Queue,QueueDiscItem);
|
|
NS_OBJECT_TEMPLATE_CLASS_DEFINE (DropTailQueue,QueueDiscItem);
|
|
|
|
NS_LOG_COMPONENT_DEFINE ("QueueDisc");
|
|
|
|
|
|
NS_OBJECT_ENSURE_REGISTERED (QueueDiscClass);
|
|
|
|
TypeId QueueDiscClass::GetTypeId (void)
|
|
{
|
|
static TypeId tid = TypeId ("ns3::QueueDiscClass")
|
|
.SetParent<Object> ()
|
|
.SetGroupName ("TrafficControl")
|
|
.AddConstructor<QueueDiscClass> ()
|
|
.AddAttribute ("QueueDisc", "The queue disc attached to the class",
|
|
PointerValue (),
|
|
MakePointerAccessor (&QueueDiscClass::m_queueDisc),
|
|
MakePointerChecker<QueueDisc> ())
|
|
;
|
|
return tid;
|
|
}
|
|
|
|
QueueDiscClass::QueueDiscClass ()
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
}
|
|
|
|
QueueDiscClass::~QueueDiscClass ()
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
}
|
|
|
|
void
|
|
QueueDiscClass::DoDispose (void)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
m_queueDisc = 0;
|
|
Object::DoDispose ();
|
|
}
|
|
|
|
Ptr<QueueDisc>
|
|
QueueDiscClass::GetQueueDisc (void) const
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
return m_queueDisc;
|
|
}
|
|
|
|
void
|
|
QueueDiscClass::SetQueueDisc (Ptr<QueueDisc> qd)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
NS_ABORT_MSG_IF (m_queueDisc, "Cannot set the queue disc on a class already having an attached queue disc");
|
|
m_queueDisc = qd;
|
|
}
|
|
|
|
QueueDisc::Stats::Stats ()
|
|
: nTotalReceivedPackets (0),
|
|
nTotalReceivedBytes (0),
|
|
nTotalSentPackets (0),
|
|
nTotalSentBytes (0),
|
|
nTotalEnqueuedPackets (0),
|
|
nTotalEnqueuedBytes (0),
|
|
nTotalDequeuedPackets (0),
|
|
nTotalDequeuedBytes (0),
|
|
nTotalDroppedPackets (0),
|
|
nTotalDroppedPacketsBeforeEnqueue (0),
|
|
nTotalDroppedPacketsAfterDequeue (0),
|
|
nTotalDroppedBytes (0),
|
|
nTotalDroppedBytesBeforeEnqueue (0),
|
|
nTotalDroppedBytesAfterDequeue (0),
|
|
nTotalRequeuedPackets (0),
|
|
nTotalRequeuedBytes (0),
|
|
nTotalMarkedPackets (0),
|
|
nTotalMarkedBytes (0)
|
|
{
|
|
}
|
|
|
|
uint32_t
|
|
QueueDisc::Stats::GetNDroppedPackets (std::string reason) const
|
|
{
|
|
uint32_t count = 0;
|
|
auto it = nDroppedPacketsBeforeEnqueue.find (reason);
|
|
|
|
if (it != nDroppedPacketsBeforeEnqueue.end ())
|
|
{
|
|
count += it->second;
|
|
}
|
|
|
|
it = nDroppedPacketsAfterDequeue.find (reason);
|
|
|
|
if (it != nDroppedPacketsAfterDequeue.end ())
|
|
{
|
|
count += it->second;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
uint64_t
|
|
QueueDisc::Stats::GetNDroppedBytes (std::string reason) const
|
|
{
|
|
uint64_t count = 0;
|
|
auto it = nDroppedBytesBeforeEnqueue.find (reason);
|
|
|
|
if (it != nDroppedBytesBeforeEnqueue.end ())
|
|
{
|
|
count += it->second;
|
|
}
|
|
|
|
it = nDroppedBytesAfterDequeue.find (reason);
|
|
|
|
if (it != nDroppedBytesAfterDequeue.end ())
|
|
{
|
|
count += it->second;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
uint32_t
|
|
QueueDisc::Stats::GetNMarkedPackets (std::string reason) const
|
|
{
|
|
auto it = nMarkedPackets.find (reason);
|
|
|
|
if (it != nMarkedPackets.end ())
|
|
{
|
|
return it->second;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint64_t
|
|
QueueDisc::Stats::GetNMarkedBytes (std::string reason) const
|
|
{
|
|
auto it = nMarkedBytes.find (reason);
|
|
|
|
if (it != nMarkedBytes.end ())
|
|
{
|
|
return it->second;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
QueueDisc::Stats::Print (std::ostream &os) const
|
|
{
|
|
std::map<std::string, uint32_t>::const_iterator itp;
|
|
std::map<std::string, uint64_t>::const_iterator itb;
|
|
|
|
os << std::endl << "Packets/Bytes received: "
|
|
<< nTotalReceivedPackets << " / "
|
|
<< nTotalReceivedBytes
|
|
<< std::endl << "Packets/Bytes enqueued: "
|
|
<< nTotalEnqueuedPackets << " / "
|
|
<< nTotalEnqueuedBytes
|
|
<< std::endl << "Packets/Bytes dequeued: "
|
|
<< nTotalDequeuedPackets << " / "
|
|
<< nTotalDequeuedBytes
|
|
<< std::endl << "Packets/Bytes requeued: "
|
|
<< nTotalRequeuedPackets << " / "
|
|
<< nTotalRequeuedBytes
|
|
<< std::endl << "Packets/Bytes dropped: "
|
|
<< nTotalDroppedPackets << " / "
|
|
<< nTotalDroppedBytes
|
|
<< std::endl << "Packets/Bytes dropped before enqueue: "
|
|
<< nTotalDroppedPacketsBeforeEnqueue << " / "
|
|
<< nTotalDroppedBytesBeforeEnqueue;
|
|
|
|
itp = nDroppedPacketsBeforeEnqueue.begin ();
|
|
itb = nDroppedBytesBeforeEnqueue.begin ();
|
|
|
|
while (itp != nDroppedPacketsBeforeEnqueue.end () &&
|
|
itb != nDroppedBytesBeforeEnqueue.end ())
|
|
{
|
|
NS_ASSERT (itp->first.compare (itb->first) == 0);
|
|
os << std::endl << " " << itp->first << ": "
|
|
<< itp->second << " / " << itb->second;
|
|
itp++;
|
|
itb++;
|
|
}
|
|
|
|
os << std::endl << "Packets/Bytes dropped after dequeue: "
|
|
<< nTotalDroppedPacketsAfterDequeue << " / "
|
|
<< nTotalDroppedBytesAfterDequeue;
|
|
|
|
itp = nDroppedPacketsAfterDequeue.begin ();
|
|
itb = nDroppedBytesAfterDequeue.begin ();
|
|
|
|
while (itp != nDroppedPacketsAfterDequeue.end () &&
|
|
itb != nDroppedBytesAfterDequeue.end ())
|
|
{
|
|
NS_ASSERT (itp->first.compare (itb->first) == 0);
|
|
os << std::endl << " " << itp->first << ": "
|
|
<< itp->second << " / " << itb->second;
|
|
itp++;
|
|
itb++;
|
|
}
|
|
|
|
os << std::endl << "Packets/Bytes sent: "
|
|
<< nTotalSentPackets << " / "
|
|
<< nTotalSentBytes
|
|
<< std::endl << "Packets/Bytes marked: "
|
|
<< nTotalMarkedPackets << " / "
|
|
<< nTotalMarkedBytes;
|
|
|
|
itp = nMarkedPackets.begin ();
|
|
itb = nMarkedBytes.begin ();
|
|
|
|
while (itp != nMarkedPackets.end () &&
|
|
itb != nMarkedBytes.end ())
|
|
{
|
|
NS_ASSERT (itp->first.compare (itb->first) == 0);
|
|
os << std::endl << " " << itp->first << ": "
|
|
<< itp->second << " / " << itb->second;
|
|
itp++;
|
|
itb++;
|
|
}
|
|
|
|
os << std::endl;
|
|
}
|
|
|
|
std::ostream & operator << (std::ostream &os, const QueueDisc::Stats &stats)
|
|
{
|
|
stats.Print (os);
|
|
return os;
|
|
}
|
|
|
|
NS_OBJECT_ENSURE_REGISTERED (QueueDisc);
|
|
|
|
TypeId QueueDisc::GetTypeId (void)
|
|
{
|
|
static TypeId tid = TypeId ("ns3::QueueDisc")
|
|
.SetParent<Object> ()
|
|
.SetGroupName ("TrafficControl")
|
|
.AddAttribute ("Quota", "The maximum number of packets dequeued in a qdisc run",
|
|
UintegerValue (DEFAULT_QUOTA),
|
|
MakeUintegerAccessor (&QueueDisc::SetQuota,
|
|
&QueueDisc::GetQuota),
|
|
MakeUintegerChecker<uint32_t> ())
|
|
.AddAttribute ("InternalQueueList", "The list of internal queues.",
|
|
ObjectVectorValue (),
|
|
MakeObjectVectorAccessor (&QueueDisc::m_queues),
|
|
MakeObjectVectorChecker<InternalQueue> ())
|
|
.AddAttribute ("PacketFilterList", "The list of packet filters.",
|
|
ObjectVectorValue (),
|
|
MakeObjectVectorAccessor (&QueueDisc::m_filters),
|
|
MakeObjectVectorChecker<PacketFilter> ())
|
|
.AddAttribute ("QueueDiscClassList", "The list of queue disc classes.",
|
|
ObjectVectorValue (),
|
|
MakeObjectVectorAccessor (&QueueDisc::m_classes),
|
|
MakeObjectVectorChecker<QueueDiscClass> ())
|
|
.AddTraceSource ("Enqueue", "Enqueue a packet in the queue disc",
|
|
MakeTraceSourceAccessor (&QueueDisc::m_traceEnqueue),
|
|
"ns3::QueueDiscItem::TracedCallback")
|
|
.AddTraceSource ("Dequeue", "Dequeue a packet from the queue disc",
|
|
MakeTraceSourceAccessor (&QueueDisc::m_traceDequeue),
|
|
"ns3::QueueDiscItem::TracedCallback")
|
|
.AddTraceSource ("Requeue", "Requeue a packet in the queue disc",
|
|
MakeTraceSourceAccessor (&QueueDisc::m_traceRequeue),
|
|
"ns3::QueueDiscItem::TracedCallback")
|
|
.AddTraceSource ("Drop", "Drop a packet stored in the queue disc",
|
|
MakeTraceSourceAccessor (&QueueDisc::m_traceDrop),
|
|
"ns3::QueueDiscItem::TracedCallback")
|
|
.AddTraceSource ("DropBeforeEnqueue", "Drop a packet before enqueue",
|
|
MakeTraceSourceAccessor (&QueueDisc::m_traceDropBeforeEnqueue),
|
|
"ns3::QueueDiscItem::TracedCallback")
|
|
.AddTraceSource ("DropAfterDequeue", "Drop a packet after dequeue",
|
|
MakeTraceSourceAccessor (&QueueDisc::m_traceDropAfterDequeue),
|
|
"ns3::QueueDiscItem::TracedCallback")
|
|
.AddTraceSource ("Mark", "Mark a packet stored in the queue disc",
|
|
MakeTraceSourceAccessor (&QueueDisc::m_traceMark),
|
|
"ns3::QueueDiscItem::TracedCallback")
|
|
.AddTraceSource ("PacketsInQueue",
|
|
"Number of packets currently stored in the queue disc",
|
|
MakeTraceSourceAccessor (&QueueDisc::m_nPackets),
|
|
"ns3::TracedValueCallback::Uint32")
|
|
.AddTraceSource ("BytesInQueue",
|
|
"Number of bytes currently stored in the queue disc",
|
|
MakeTraceSourceAccessor (&QueueDisc::m_nBytes),
|
|
"ns3::TracedValueCallback::Uint32")
|
|
.AddTraceSource ("SojournTime",
|
|
"Sojourn time of the last packet dequeued from the queue disc",
|
|
MakeTraceSourceAccessor (&QueueDisc::m_sojourn),
|
|
"ns3::TracedValueCallback::Time")
|
|
;
|
|
return tid;
|
|
}
|
|
|
|
QueueDisc::QueueDisc (QueueDiscSizePolicy policy)
|
|
: m_nPackets (0),
|
|
m_nBytes (0),
|
|
m_sojourn (0),
|
|
m_maxSize (QueueSize ("1p")), // to avoid that setting the mode at construction time is ignored
|
|
m_running (false),
|
|
m_sizePolicy (policy),
|
|
m_prohibitChangeMode (false)
|
|
{
|
|
NS_LOG_FUNCTION (this << (uint16_t)policy);
|
|
|
|
// These lambdas call the DropBeforeEnqueue or DropAfterDequeue methods of this
|
|
// QueueDisc object. Given that a callback to the operator() of these lambdas
|
|
// is connected to the DropBeforeEnqueue and DropAfterDequeue traces of the
|
|
// internal queues, the INTERNAL_QUEUE_DROP constant is passed as the reason
|
|
// why the packet is dropped.
|
|
m_internalQueueDbeFunctor = [this] (Ptr<const QueueDiscItem> item)
|
|
{
|
|
return DropBeforeEnqueue (item, INTERNAL_QUEUE_DROP);
|
|
};
|
|
m_internalQueueDadFunctor = [this] (Ptr<const QueueDiscItem> item)
|
|
{
|
|
return DropAfterDequeue (item, INTERNAL_QUEUE_DROP);
|
|
};
|
|
|
|
// These lambdas call the DropBeforeEnqueue or DropAfterDequeue methods of this
|
|
// QueueDisc object. Given that a callback to the operator() of these lambdas
|
|
// is connected to the DropBeforeEnqueue and DropAfterDequeue traces of the
|
|
// child queue discs, the concatenation of the CHILD_QUEUE_DISC_DROP constant
|
|
// and the second argument provided by such traces is passed as the reason why
|
|
// the packet is dropped.
|
|
m_childQueueDiscDbeFunctor = [this] (Ptr<const QueueDiscItem> item, const char* r)
|
|
{
|
|
return DropBeforeEnqueue (item,
|
|
m_childQueueDiscDropMsg.assign (CHILD_QUEUE_DISC_DROP).append (r).data ());
|
|
};
|
|
m_childQueueDiscDadFunctor = [this] (Ptr<const QueueDiscItem> item, const char* r)
|
|
{
|
|
return DropAfterDequeue (item,
|
|
m_childQueueDiscDropMsg.assign (CHILD_QUEUE_DISC_DROP).append (r).data ());
|
|
};
|
|
}
|
|
|
|
QueueDisc::QueueDisc (QueueDiscSizePolicy policy, QueueSizeUnit unit)
|
|
: QueueDisc (policy)
|
|
{
|
|
m_maxSize = QueueSize (unit, 0);
|
|
m_prohibitChangeMode = true;
|
|
}
|
|
|
|
QueueDisc::~QueueDisc ()
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
}
|
|
|
|
void
|
|
QueueDisc::DoDispose (void)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
m_queues.clear ();
|
|
m_filters.clear ();
|
|
m_classes.clear ();
|
|
m_device = 0;
|
|
m_devQueueIface = 0;
|
|
m_requeued = 0;
|
|
Object::DoDispose ();
|
|
}
|
|
|
|
void
|
|
QueueDisc::DoInitialize (void)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
// When adding a new interface, the traffic control aggregates
|
|
// a NetDeviceQueueInterface object to the netdevice
|
|
if (m_device)
|
|
{
|
|
m_devQueueIface = m_device->GetObject<NetDeviceQueueInterface> ();
|
|
}
|
|
|
|
// Check the configuration and initialize the parameters of this queue disc
|
|
bool ok = CheckConfig ();
|
|
NS_ASSERT_MSG (ok, "The queue disc configuration is not correct");
|
|
NS_UNUSED (ok); // suppress compiler warning
|
|
InitializeParams ();
|
|
|
|
// Check the configuration and initialize the parameters of the child queue discs
|
|
for (std::vector<Ptr<QueueDiscClass> >::iterator cl = m_classes.begin ();
|
|
cl != m_classes.end (); cl++)
|
|
{
|
|
(*cl)->GetQueueDisc ()->Initialize ();
|
|
}
|
|
|
|
Object::DoInitialize ();
|
|
}
|
|
|
|
const QueueDisc::Stats&
|
|
QueueDisc::GetStats (void)
|
|
{
|
|
NS_ASSERT (m_stats.nTotalDroppedPackets == m_stats.nTotalDroppedPacketsBeforeEnqueue
|
|
+ m_stats.nTotalDroppedPacketsAfterDequeue);
|
|
NS_ASSERT (m_stats.nTotalDroppedBytes == m_stats.nTotalDroppedBytesBeforeEnqueue
|
|
+ m_stats.nTotalDroppedBytesAfterDequeue);
|
|
|
|
// the total number of sent packets is only updated here to avoid to increase it
|
|
// after a dequeue and then having to decrease it if the packet is dropped after
|
|
// dequeue or requeued
|
|
m_stats.nTotalSentPackets = m_stats.nTotalDequeuedPackets - (m_requeued ? 1 : 0)
|
|
- m_stats.nTotalDroppedPacketsAfterDequeue;
|
|
m_stats.nTotalSentBytes = m_stats.nTotalDequeuedBytes - (m_requeued ? m_requeued->GetSize () : 0)
|
|
- m_stats.nTotalDroppedBytesAfterDequeue;
|
|
|
|
return m_stats;
|
|
}
|
|
|
|
uint32_t
|
|
QueueDisc::GetNPackets () const
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
return m_nPackets;
|
|
}
|
|
|
|
uint32_t
|
|
QueueDisc::GetNBytes (void) const
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
return m_nBytes;
|
|
}
|
|
|
|
QueueSize
|
|
QueueDisc::GetMaxSize (void) const
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
|
|
switch (m_sizePolicy)
|
|
{
|
|
case QueueDiscSizePolicy::NO_LIMITS:
|
|
NS_FATAL_ERROR ("The size of this queue disc is not limited");
|
|
|
|
case QueueDiscSizePolicy::SINGLE_INTERNAL_QUEUE:
|
|
if (GetNInternalQueues ())
|
|
{
|
|
return GetInternalQueue (0)->GetMaxSize ();
|
|
}
|
|
|
|
case QueueDiscSizePolicy::SINGLE_CHILD_QUEUE_DISC:
|
|
if (GetNQueueDiscClasses ())
|
|
{
|
|
return GetQueueDiscClass (0)->GetQueueDisc ()->GetMaxSize ();
|
|
}
|
|
|
|
case QueueDiscSizePolicy::MULTIPLE_QUEUES:
|
|
default:
|
|
return m_maxSize;
|
|
}
|
|
}
|
|
|
|
bool
|
|
QueueDisc::SetMaxSize (QueueSize size)
|
|
{
|
|
NS_LOG_FUNCTION (this << size);
|
|
|
|
// do nothing if the limit is null
|
|
if (!size.GetValue ())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (m_prohibitChangeMode && size.GetUnit () != m_maxSize.GetUnit ())
|
|
{
|
|
NS_LOG_DEBUG ("Changing the mode of this queue disc is prohibited");
|
|
return false;
|
|
}
|
|
|
|
switch (m_sizePolicy)
|
|
{
|
|
case QueueDiscSizePolicy::NO_LIMITS:
|
|
NS_FATAL_ERROR ("The size of this queue disc is not limited");
|
|
|
|
case QueueDiscSizePolicy::SINGLE_INTERNAL_QUEUE:
|
|
if (GetNInternalQueues ())
|
|
{
|
|
GetInternalQueue (0)->SetMaxSize (size);
|
|
}
|
|
|
|
case QueueDiscSizePolicy::SINGLE_CHILD_QUEUE_DISC:
|
|
if (GetNQueueDiscClasses ())
|
|
{
|
|
GetQueueDiscClass (0)->GetQueueDisc ()->SetMaxSize (size);
|
|
}
|
|
|
|
case QueueDiscSizePolicy::MULTIPLE_QUEUES:
|
|
default:
|
|
m_maxSize = size;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
QueueSize
|
|
QueueDisc::GetCurrentSize (void)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
|
|
if (GetMaxSize ().GetUnit () == QueueSizeUnit::PACKETS)
|
|
{
|
|
return QueueSize (QueueSizeUnit::PACKETS, m_nPackets);
|
|
}
|
|
if (GetMaxSize ().GetUnit () == QueueSizeUnit::BYTES)
|
|
{
|
|
return QueueSize (QueueSizeUnit::BYTES, m_nBytes);
|
|
}
|
|
NS_ABORT_MSG ("Unknown queue size unit");
|
|
}
|
|
|
|
void
|
|
QueueDisc::SetNetDevice (Ptr<NetDevice> device)
|
|
{
|
|
NS_LOG_FUNCTION (this << device);
|
|
m_device = device;
|
|
}
|
|
|
|
Ptr<NetDevice>
|
|
QueueDisc::GetNetDevice (void) const
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
return m_device;
|
|
}
|
|
|
|
void
|
|
QueueDisc::SetQuota (const uint32_t quota)
|
|
{
|
|
NS_LOG_FUNCTION (this << quota);
|
|
m_quota = quota;
|
|
}
|
|
|
|
uint32_t
|
|
QueueDisc::GetQuota (void) const
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
return m_quota;
|
|
}
|
|
|
|
void
|
|
QueueDisc::AddInternalQueue (Ptr<InternalQueue> queue)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
|
|
// set various callbacks on the internal queue, so that the queue disc is
|
|
// notified of packets enqueued, dequeued or dropped by the internal queue
|
|
queue->TraceConnectWithoutContext ("Enqueue",
|
|
MakeCallback (&QueueDisc::PacketEnqueued, this));
|
|
queue->TraceConnectWithoutContext ("Dequeue",
|
|
MakeCallback (&QueueDisc::PacketDequeued, this));
|
|
queue->TraceConnectWithoutContext ("DropBeforeEnqueue",
|
|
MakeCallback (&InternalQueueDropFunctor::operator(),
|
|
&m_internalQueueDbeFunctor));
|
|
queue->TraceConnectWithoutContext ("DropAfterDequeue",
|
|
MakeCallback (&InternalQueueDropFunctor::operator(),
|
|
&m_internalQueueDadFunctor));
|
|
m_queues.push_back (queue);
|
|
}
|
|
|
|
Ptr<QueueDisc::InternalQueue>
|
|
QueueDisc::GetInternalQueue (uint32_t i) const
|
|
{
|
|
NS_ASSERT (i < m_queues.size ());
|
|
return m_queues[i];
|
|
}
|
|
|
|
uint32_t
|
|
QueueDisc::GetNInternalQueues (void) const
|
|
{
|
|
return static_cast<uint32_t>(m_queues.size ());
|
|
}
|
|
|
|
void
|
|
QueueDisc::AddPacketFilter (Ptr<PacketFilter> filter)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
m_filters.push_back (filter);
|
|
}
|
|
|
|
Ptr<PacketFilter>
|
|
QueueDisc::GetPacketFilter (uint32_t i) const
|
|
{
|
|
NS_ASSERT (i < m_filters.size ());
|
|
return m_filters[i];
|
|
}
|
|
|
|
uint32_t
|
|
QueueDisc::GetNPacketFilters (void) const
|
|
{
|
|
return static_cast<uint32_t>(m_filters.size ());
|
|
}
|
|
|
|
void
|
|
QueueDisc::AddQueueDiscClass (Ptr<QueueDiscClass> qdClass)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
NS_ABORT_MSG_IF (qdClass->GetQueueDisc () == 0, "Cannot add a class with no attached queue disc");
|
|
// the child queue disc cannot be one with wake mode equal to WAKE_CHILD because
|
|
// such queue discs do not implement the enqueue/dequeue methods
|
|
NS_ABORT_MSG_IF (qdClass->GetQueueDisc ()->GetWakeMode () == WAKE_CHILD,
|
|
"A queue disc with WAKE_CHILD as wake mode can only be a root queue disc");
|
|
|
|
// set the parent callbacks on the child queue disc, so that it can notify
|
|
// the parent queue disc of packets enqueued, dequeued or dropped
|
|
qdClass->GetQueueDisc ()->TraceConnectWithoutContext ("Enqueue",
|
|
MakeCallback (&QueueDisc::PacketEnqueued, this));
|
|
qdClass->GetQueueDisc ()->TraceConnectWithoutContext ("Dequeue",
|
|
MakeCallback (&QueueDisc::PacketDequeued, this));
|
|
qdClass->GetQueueDisc ()->TraceConnectWithoutContext ("DropBeforeEnqueue",
|
|
MakeCallback (&ChildQueueDiscDropFunctor::operator(),
|
|
&m_childQueueDiscDbeFunctor));
|
|
qdClass->GetQueueDisc ()->TraceConnectWithoutContext ("DropAfterDequeue",
|
|
MakeCallback (&ChildQueueDiscDropFunctor::operator(),
|
|
&m_childQueueDiscDadFunctor));
|
|
m_classes.push_back (qdClass);
|
|
}
|
|
|
|
Ptr<QueueDiscClass>
|
|
QueueDisc::GetQueueDiscClass (uint32_t i) const
|
|
{
|
|
NS_ASSERT (i < m_classes.size ());
|
|
return m_classes[i];
|
|
}
|
|
|
|
uint32_t
|
|
QueueDisc::GetNQueueDiscClasses (void) const
|
|
{
|
|
return static_cast<uint32_t>(m_classes.size ());
|
|
}
|
|
|
|
int32_t
|
|
QueueDisc::Classify (Ptr<QueueDiscItem> item)
|
|
{
|
|
NS_LOG_FUNCTION (this << item);
|
|
|
|
int32_t ret = PacketFilter::PF_NO_MATCH;
|
|
for (std::vector<Ptr<PacketFilter> >::iterator f = m_filters.begin ();
|
|
f != m_filters.end () && ret == PacketFilter::PF_NO_MATCH; f++)
|
|
{
|
|
ret = (*f)->Classify (item);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QueueDisc::WakeMode
|
|
QueueDisc::GetWakeMode (void) const
|
|
{
|
|
return WAKE_ROOT;
|
|
}
|
|
|
|
void
|
|
QueueDisc::PacketEnqueued (Ptr<const QueueDiscItem> item)
|
|
{
|
|
m_nPackets++;
|
|
m_nBytes += item->GetSize ();
|
|
m_stats.nTotalEnqueuedPackets++;
|
|
m_stats.nTotalEnqueuedBytes += item->GetSize ();
|
|
|
|
NS_LOG_LOGIC ("m_traceEnqueue (p)");
|
|
m_traceEnqueue (item);
|
|
}
|
|
|
|
void
|
|
QueueDisc::PacketDequeued (Ptr<const QueueDiscItem> item)
|
|
{
|
|
m_nPackets--;
|
|
m_nBytes -= item->GetSize ();
|
|
m_stats.nTotalDequeuedPackets++;
|
|
m_stats.nTotalDequeuedBytes += item->GetSize ();
|
|
|
|
m_sojourn = Simulator::Now () - item->GetTimeStamp ();
|
|
|
|
NS_LOG_LOGIC ("m_traceDequeue (p)");
|
|
m_traceDequeue (item);
|
|
}
|
|
|
|
void
|
|
QueueDisc::DropBeforeEnqueue (Ptr<const QueueDiscItem> item, const char* reason)
|
|
{
|
|
NS_LOG_FUNCTION (this << item << reason);
|
|
|
|
m_stats.nTotalDroppedPackets++;
|
|
m_stats.nTotalDroppedBytes += item->GetSize ();
|
|
m_stats.nTotalDroppedPacketsBeforeEnqueue++;
|
|
m_stats.nTotalDroppedBytesBeforeEnqueue += item->GetSize ();
|
|
|
|
// update the number of packets dropped for the given reason
|
|
std::map<std::string, uint32_t>::iterator itp = m_stats.nDroppedPacketsBeforeEnqueue.find (reason);
|
|
if (itp != m_stats.nDroppedPacketsBeforeEnqueue.end ())
|
|
{
|
|
itp->second++;
|
|
}
|
|
else
|
|
{
|
|
m_stats.nDroppedPacketsBeforeEnqueue[reason] = 1;
|
|
}
|
|
// update the amount of bytes dropped for the given reason
|
|
std::map<std::string, uint64_t>::iterator itb = m_stats.nDroppedBytesBeforeEnqueue.find (reason);
|
|
if (itb != m_stats.nDroppedBytesBeforeEnqueue.end ())
|
|
{
|
|
itb->second += item->GetSize ();
|
|
}
|
|
else
|
|
{
|
|
m_stats.nDroppedBytesBeforeEnqueue[reason] = item->GetSize ();
|
|
}
|
|
|
|
NS_LOG_DEBUG ("Total packets/bytes dropped before enqueue: "
|
|
<< m_stats.nTotalDroppedPacketsBeforeEnqueue << " / "
|
|
<< m_stats.nTotalDroppedBytesBeforeEnqueue);
|
|
NS_LOG_LOGIC ("m_traceDropBeforeEnqueue (p)");
|
|
m_traceDrop (item);
|
|
m_traceDropBeforeEnqueue (item, reason);
|
|
}
|
|
|
|
void
|
|
QueueDisc::DropAfterDequeue (Ptr<const QueueDiscItem> item, const char* reason)
|
|
{
|
|
NS_LOG_FUNCTION (this << item << reason);
|
|
|
|
m_stats.nTotalDroppedPackets++;
|
|
m_stats.nTotalDroppedBytes += item->GetSize ();
|
|
m_stats.nTotalDroppedPacketsAfterDequeue++;
|
|
m_stats.nTotalDroppedBytesAfterDequeue += item->GetSize ();
|
|
|
|
// update the number of packets dropped for the given reason
|
|
std::map<std::string, uint32_t>::iterator itp = m_stats.nDroppedPacketsAfterDequeue.find (reason);
|
|
if (itp != m_stats.nDroppedPacketsAfterDequeue.end ())
|
|
{
|
|
itp->second++;
|
|
}
|
|
else
|
|
{
|
|
m_stats.nDroppedPacketsAfterDequeue[reason] = 1;
|
|
}
|
|
// update the amount of bytes dropped for the given reason
|
|
std::map<std::string, uint64_t>::iterator itb = m_stats.nDroppedBytesAfterDequeue.find (reason);
|
|
if (itb != m_stats.nDroppedBytesAfterDequeue.end ())
|
|
{
|
|
itb->second += item->GetSize ();
|
|
}
|
|
else
|
|
{
|
|
m_stats.nDroppedBytesAfterDequeue[reason] = item->GetSize ();
|
|
}
|
|
|
|
NS_LOG_DEBUG ("Total packets/bytes dropped after dequeue: "
|
|
<< m_stats.nTotalDroppedPacketsAfterDequeue << " / "
|
|
<< m_stats.nTotalDroppedBytesAfterDequeue);
|
|
NS_LOG_LOGIC ("m_traceDropAfterDequeue (p)");
|
|
m_traceDrop (item);
|
|
m_traceDropAfterDequeue (item, reason);
|
|
}
|
|
|
|
bool
|
|
QueueDisc::Mark (Ptr<QueueDiscItem> item, const char* reason)
|
|
{
|
|
NS_LOG_FUNCTION (this << item << reason);
|
|
|
|
bool retval = item->Mark ();
|
|
|
|
if (!retval)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_stats.nTotalMarkedPackets++;
|
|
m_stats.nTotalMarkedBytes += item->GetSize ();
|
|
|
|
// update the number of packets marked for the given reason
|
|
std::map<std::string, uint32_t>::iterator itp = m_stats.nMarkedPackets.find (reason);
|
|
if (itp != m_stats.nMarkedPackets.end ())
|
|
{
|
|
itp->second++;
|
|
}
|
|
else
|
|
{
|
|
m_stats.nMarkedPackets[reason] = 1;
|
|
}
|
|
// update the amount of bytes marked for the given reason
|
|
std::map<std::string, uint64_t>::iterator itb = m_stats.nMarkedBytes.find (reason);
|
|
if (itb != m_stats.nMarkedBytes.end ())
|
|
{
|
|
itb->second += item->GetSize ();
|
|
}
|
|
else
|
|
{
|
|
m_stats.nMarkedBytes[reason] = item->GetSize ();
|
|
}
|
|
|
|
NS_LOG_DEBUG ("Total packets/bytes marked: "
|
|
<< m_stats.nTotalMarkedPackets << " / "
|
|
<< m_stats.nTotalMarkedBytes);
|
|
m_traceMark (item, reason);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
QueueDisc::Enqueue (Ptr<QueueDiscItem> item)
|
|
{
|
|
NS_LOG_FUNCTION (this << item);
|
|
|
|
m_stats.nTotalReceivedPackets++;
|
|
m_stats.nTotalReceivedBytes += item->GetSize ();
|
|
|
|
bool retval = DoEnqueue (item);
|
|
|
|
if (retval)
|
|
{
|
|
item->SetTimeStamp (Simulator::Now ());
|
|
}
|
|
|
|
// DoEnqueue may return false because:
|
|
// 1) the internal queue is full
|
|
// -> the DropBeforeEnqueue method of this queue disc is automatically called
|
|
// because QueueDisc::AddInternalQueue sets the trace callback
|
|
// 2) the child queue disc dropped the packet
|
|
// -> the DropBeforeEnqueue method of this queue disc is automatically called
|
|
// because QueueDisc::AddQueueDiscClass sets the trace callback
|
|
// 3) it dropped the packet
|
|
// -> DoEnqueue has to explicitly call DropBeforeEnqueue
|
|
// Thus, we do not have to call DropBeforeEnqueue here.
|
|
|
|
// check that the received packet was either enqueued or dropped
|
|
NS_ASSERT (m_stats.nTotalReceivedPackets == m_stats.nTotalDroppedPacketsBeforeEnqueue +
|
|
m_stats.nTotalEnqueuedPackets);
|
|
NS_ASSERT (m_stats.nTotalReceivedBytes == m_stats.nTotalDroppedBytesBeforeEnqueue +
|
|
m_stats.nTotalEnqueuedBytes);
|
|
|
|
return retval;
|
|
}
|
|
|
|
Ptr<QueueDiscItem>
|
|
QueueDisc::Dequeue (void)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
|
|
Ptr<QueueDiscItem> item = DoDequeue ();
|
|
|
|
NS_ASSERT (m_nPackets == m_stats.nTotalEnqueuedPackets - m_stats.nTotalDequeuedPackets);
|
|
NS_ASSERT (m_nBytes == m_stats.nTotalEnqueuedBytes - m_stats.nTotalDequeuedBytes);
|
|
|
|
return item;
|
|
}
|
|
|
|
Ptr<const QueueDiscItem>
|
|
QueueDisc::Peek (void)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
return DoPeek ();
|
|
}
|
|
|
|
Ptr<const QueueDiscItem>
|
|
QueueDisc::PeekDequeued (void)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
|
|
if (!m_requeued)
|
|
{
|
|
m_requeued = Dequeue ();
|
|
}
|
|
return m_requeued;
|
|
}
|
|
|
|
Ptr<QueueDiscItem>
|
|
QueueDisc::DequeuePeeked (void)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
|
|
Ptr<QueueDiscItem> item = m_requeued;
|
|
|
|
if (item)
|
|
{
|
|
m_requeued = 0;
|
|
}
|
|
else
|
|
{
|
|
item = Dequeue ();
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
void
|
|
QueueDisc::Run (void)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
|
|
if (RunBegin ())
|
|
{
|
|
uint32_t quota = m_quota;
|
|
while (Restart ())
|
|
{
|
|
quota -= 1;
|
|
if (quota <= 0)
|
|
{
|
|
/// \todo netif_schedule (q);
|
|
break;
|
|
}
|
|
}
|
|
RunEnd ();
|
|
}
|
|
}
|
|
|
|
bool
|
|
QueueDisc::RunBegin (void)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
if (m_running)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_running = true;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
QueueDisc::RunEnd (void)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
m_running = false;
|
|
}
|
|
|
|
bool
|
|
QueueDisc::Restart (void)
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
Ptr<QueueDiscItem> item = DequeuePacket();
|
|
if (item == 0)
|
|
{
|
|
NS_LOG_LOGIC ("No packet to send");
|
|
return false;
|
|
}
|
|
|
|
return Transmit (item);
|
|
}
|
|
|
|
Ptr<QueueDiscItem>
|
|
QueueDisc::DequeuePacket ()
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
NS_ASSERT (m_devQueueIface);
|
|
Ptr<QueueDiscItem> item;
|
|
|
|
// First check if there is a requeued packet
|
|
if (m_requeued != 0)
|
|
{
|
|
// If the queue where the requeued packet is destined to is not stopped, return
|
|
// the requeued packet; otherwise, return an empty packet.
|
|
// If the device does not support flow control, the device queue is never stopped
|
|
if (!m_devQueueIface->GetTxQueue (m_requeued->GetTxQueueIndex ())->IsStopped ())
|
|
{
|
|
item = m_requeued;
|
|
m_requeued = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If the device is multi-queue (actually, Linux checks if the queue disc has
|
|
// multiple queues), ask the queue disc to dequeue a packet (a multi-queue aware
|
|
// queue disc should try not to dequeue a packet destined to a stopped queue).
|
|
// Otherwise, ask the queue disc to dequeue a packet only if the (unique) queue
|
|
// is not stopped.
|
|
if (m_devQueueIface->GetNTxQueues ()>1 || !m_devQueueIface->GetTxQueue (0)->IsStopped ())
|
|
{
|
|
item = Dequeue ();
|
|
// If the item is not null, add the header to the packet.
|
|
if (item != 0)
|
|
{
|
|
item->AddHeader ();
|
|
}
|
|
// Here, Linux tries bulk dequeues
|
|
}
|
|
}
|
|
return item;
|
|
}
|
|
|
|
void
|
|
QueueDisc::Requeue (Ptr<QueueDiscItem> item)
|
|
{
|
|
NS_LOG_FUNCTION (this << item);
|
|
m_requeued = item;
|
|
/// \todo netif_schedule (q);
|
|
|
|
m_stats.nTotalRequeuedPackets++;
|
|
m_stats.nTotalRequeuedBytes += item->GetSize ();
|
|
|
|
NS_LOG_LOGIC ("m_traceRequeue (p)");
|
|
m_traceRequeue (item);
|
|
}
|
|
|
|
bool
|
|
QueueDisc::Transmit (Ptr<QueueDiscItem> item)
|
|
{
|
|
NS_LOG_FUNCTION (this << item);
|
|
NS_ASSERT (m_devQueueIface);
|
|
|
|
// if the device queue is stopped, requeue the packet and return false.
|
|
// Note that if the underlying device is tc-unaware, packets are never
|
|
// requeued because the queues of tc-unaware devices are never stopped
|
|
if (m_devQueueIface->GetTxQueue (item->GetTxQueueIndex ())->IsStopped ())
|
|
{
|
|
Requeue (item);
|
|
return false;
|
|
}
|
|
|
|
// a single queue device makes no use of the priority tag
|
|
if (m_devQueueIface->GetNTxQueues () == 1)
|
|
{
|
|
SocketPriorityTag priorityTag;
|
|
item->GetPacket ()->RemovePacketTag (priorityTag);
|
|
}
|
|
m_device->Send (item->GetPacket (), item->GetAddress (), item->GetProtocol ());
|
|
|
|
// the behavior here slightly diverges from Linux. In Linux, it is advised that
|
|
// the function called when a packet needs to be transmitted (ndo_start_xmit)
|
|
// should always return NETDEV_TX_OK, which means that the packet is consumed by
|
|
// the device driver and thus is not requeued. However, the ndo_start_xmit function
|
|
// of the device driver is allowed to return NETDEV_TX_BUSY (and hence the packet
|
|
// is requeued) when there is no room for the received packet in the device queue,
|
|
// despite the queue is not stopped. This case is considered as a corner case or
|
|
// an hard error, and should be avoided.
|
|
// Here, we do not handle such corner case and always assume that the packet is
|
|
// consumed by the netdevice. Thus, we ignore the value returned by Send and a
|
|
// packet sent to a netdevice is never requeued. The reason is that the semantics
|
|
// of the value returned by NetDevice::Send does not match that of the value
|
|
// returned by ndo_start_xmit.
|
|
|
|
// if the queue disc is empty or the device queue is now stopped, return false so
|
|
// that the Run method does not attempt to dequeue other packets and exits
|
|
if (GetNPackets () == 0 || m_devQueueIface->GetTxQueue (item->GetTxQueueIndex ())->IsStopped ())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace ns3
|