/* -*- 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 "queue-disc.h" #include #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 () .SetGroupName ("TrafficControl") .AddConstructor () .AddAttribute ("QueueDisc", "The queue disc attached to the class", PointerValue (), MakePointerAccessor (&QueueDiscClass::m_queueDisc), MakePointerChecker ()) ; 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 QueueDiscClass::GetQueueDisc (void) const { NS_LOG_FUNCTION (this); return m_queueDisc; } void QueueDiscClass::SetQueueDisc (Ptr 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) { } void QueueDisc::Stats::Print (std::ostream &os) const { 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 << std::endl << "Packets/Bytes dropped after dequeue: " << nTotalDroppedPacketsAfterDequeue << " / " << nTotalDroppedBytesAfterDequeue << std::endl << "Packets/Bytes sent: " << nTotalSentPackets << " / " << nTotalSentBytes << 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 () .SetGroupName ("TrafficControl") .AddAttribute ("Quota", "The maximum number of packets dequeued in a qdisc run", UintegerValue (DEFAULT_QUOTA), MakeUintegerAccessor (&QueueDisc::SetQuota, &QueueDisc::GetQuota), MakeUintegerChecker ()) .AddAttribute ("InternalQueueList", "The list of internal queues.", ObjectVectorValue (), MakeObjectVectorAccessor (&QueueDisc::m_queues), MakeObjectVectorChecker ()) .AddAttribute ("PacketFilterList", "The list of packet filters.", ObjectVectorValue (), MakeObjectVectorAccessor (&QueueDisc::m_filters), MakeObjectVectorChecker ()) .AddAttribute ("QueueDiscClassList", "The list of queue disc classes.", ObjectVectorValue (), MakeObjectVectorAccessor (&QueueDisc::m_classes), MakeObjectVectorChecker ()) .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 ("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") ; return tid; } QueueDisc::QueueDisc () : m_nPackets (0), m_nBytes (0), m_running (false) { NS_LOG_FUNCTION (this); } 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 (); } // 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 >::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; } void QueueDisc::SetNetDevice (Ptr device) { NS_LOG_FUNCTION (this << device); m_device = device; } Ptr 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 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 (&QueueDisc::DropBeforeEnqueue, this)); queue->TraceConnectWithoutContext ("DropAfterDequeue", MakeCallback (&QueueDisc::DropAfterDequeue, this)); m_queues.push_back (queue); } Ptr QueueDisc::GetInternalQueue (uint32_t i) const { NS_ASSERT (i < m_queues.size ()); return m_queues[i]; } uint32_t QueueDisc::GetNInternalQueues (void) const { return m_queues.size (); } void QueueDisc::AddPacketFilter (Ptr filter) { NS_LOG_FUNCTION (this); m_filters.push_back (filter); } Ptr QueueDisc::GetPacketFilter (uint32_t i) const { NS_ASSERT (i < m_filters.size ()); return m_filters[i]; } uint32_t QueueDisc::GetNPacketFilters (void) const { return m_filters.size (); } void QueueDisc::AddQueueDiscClass (Ptr 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 (&QueueDisc::DropBeforeEnqueue, this)); qdClass->GetQueueDisc ()->TraceConnectWithoutContext ("DropAfterDequeue", MakeCallback (&QueueDisc::DropAfterDequeue, this)); m_classes.push_back (qdClass); } Ptr QueueDisc::GetQueueDiscClass (uint32_t i) const { NS_ASSERT (i < m_classes.size ()); return m_classes[i]; } uint32_t QueueDisc::GetNQueueDiscClasses (void) const { return m_classes.size (); } int32_t QueueDisc::Classify (Ptr item) { NS_LOG_FUNCTION (this << item); int32_t ret = PacketFilter::PF_NO_MATCH; for (std::vector >::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 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 item) { m_nPackets--; m_nBytes -= item->GetSize (); m_stats.nTotalDequeuedPackets++; m_stats.nTotalDequeuedBytes += item->GetSize (); NS_LOG_LOGIC ("m_traceDequeue (p)"); m_traceDequeue (item); } void QueueDisc::DropBeforeEnqueue (Ptr item) { NS_LOG_FUNCTION (this << item); m_stats.nTotalDroppedPackets++; m_stats.nTotalDroppedBytes += item->GetSize (); m_stats.nTotalDroppedPacketsBeforeEnqueue++; m_stats.nTotalDroppedBytesBeforeEnqueue += item->GetSize (); NS_LOG_LOGIC ("m_traceDropBeforeEnqueue (p)"); m_traceDrop (item); m_traceDropBeforeEnqueue (item); } void QueueDisc::DropAfterDequeue (Ptr item) { NS_LOG_FUNCTION (this << item); m_stats.nTotalDroppedPackets++; m_stats.nTotalDroppedBytes += item->GetSize (); m_stats.nTotalDroppedPacketsAfterDequeue++; m_stats.nTotalDroppedBytesAfterDequeue += item->GetSize (); NS_LOG_LOGIC ("m_traceDropAfterDequeue (p)"); m_traceDrop (item); m_traceDropAfterDequeue (item); } bool QueueDisc::Enqueue (Ptr item) { NS_LOG_FUNCTION (this << item); m_stats.nTotalReceivedPackets++; m_stats.nTotalReceivedBytes += item->GetSize (); bool retval = DoEnqueue (item); // 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 QueueDisc::Dequeue (void) { NS_LOG_FUNCTION (this); Ptr 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 QueueDisc::Peek (void) const { NS_LOG_FUNCTION (this); return DoPeek (); } 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 item = DequeuePacket(); if (item == 0) { NS_LOG_LOGIC ("No packet to send"); return false; } return Transmit (item); } Ptr QueueDisc::DequeuePacket () { NS_LOG_FUNCTION (this); NS_ASSERT (m_devQueueIface); Ptr 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 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 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