1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3 * Copyright (c) 2007, 2014 University of Washington
4 * 2015 Universita' degli Studi di Napoli Federico II
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation;
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 #include "ns3/log.h"
21 #include "ns3/abort.h"
22 #include "ns3/uinteger.h"
23 #include "ns3/pointer.h"
24 #include "ns3/object-vector.h"
25 #include "ns3/packet.h"
26 #include "ns3/socket.h"
27 #include "ns3/unused.h"
28 #include "ns3/simulator.h"
29 #include "queue-disc.h"
30 #include "ns3/net-device-queue-interface.h"
31 #include "ns3/queue.h"
32
33 namespace ns3 {
34
35 NS_LOG_COMPONENT_DEFINE ("QueueDisc");
36
37
38 NS_OBJECT_ENSURE_REGISTERED (QueueDiscClass);
39
GetTypeId(void)40 TypeId QueueDiscClass::GetTypeId (void)
41 {
42 static TypeId tid = TypeId ("ns3::QueueDiscClass")
43 .SetParent<Object> ()
44 .SetGroupName ("TrafficControl")
45 .AddConstructor<QueueDiscClass> ()
46 .AddAttribute ("QueueDisc", "The queue disc attached to the class",
47 PointerValue (),
48 MakePointerAccessor (&QueueDiscClass::m_queueDisc),
49 MakePointerChecker<QueueDisc> ())
50 ;
51 return tid;
52 }
53
QueueDiscClass()54 QueueDiscClass::QueueDiscClass ()
55 {
56 NS_LOG_FUNCTION (this);
57 }
58
~QueueDiscClass()59 QueueDiscClass::~QueueDiscClass ()
60 {
61 NS_LOG_FUNCTION (this);
62 }
63
64 void
DoDispose(void)65 QueueDiscClass::DoDispose (void)
66 {
67 NS_LOG_FUNCTION (this);
68 m_queueDisc = 0;
69 Object::DoDispose ();
70 }
71
72 Ptr<QueueDisc>
GetQueueDisc(void) const73 QueueDiscClass::GetQueueDisc (void) const
74 {
75 NS_LOG_FUNCTION (this);
76 return m_queueDisc;
77 }
78
79 void
SetQueueDisc(Ptr<QueueDisc> qd)80 QueueDiscClass::SetQueueDisc (Ptr<QueueDisc> qd)
81 {
82 NS_LOG_FUNCTION (this);
83 NS_ABORT_MSG_IF (m_queueDisc, "Cannot set the queue disc on a class already having an attached queue disc");
84 m_queueDisc = qd;
85 }
86
Stats()87 QueueDisc::Stats::Stats ()
88 : nTotalReceivedPackets (0),
89 nTotalReceivedBytes (0),
90 nTotalSentPackets (0),
91 nTotalSentBytes (0),
92 nTotalEnqueuedPackets (0),
93 nTotalEnqueuedBytes (0),
94 nTotalDequeuedPackets (0),
95 nTotalDequeuedBytes (0),
96 nTotalDroppedPackets (0),
97 nTotalDroppedPacketsBeforeEnqueue (0),
98 nTotalDroppedPacketsAfterDequeue (0),
99 nTotalDroppedBytes (0),
100 nTotalDroppedBytesBeforeEnqueue (0),
101 nTotalDroppedBytesAfterDequeue (0),
102 nTotalRequeuedPackets (0),
103 nTotalRequeuedBytes (0),
104 nTotalMarkedPackets (0),
105 nTotalMarkedBytes (0)
106 {
107 }
108
109 uint32_t
GetNDroppedPackets(std::string reason) const110 QueueDisc::Stats::GetNDroppedPackets (std::string reason) const
111 {
112 uint32_t count = 0;
113 auto it = nDroppedPacketsBeforeEnqueue.find (reason);
114
115 if (it != nDroppedPacketsBeforeEnqueue.end ())
116 {
117 count += it->second;
118 }
119
120 it = nDroppedPacketsAfterDequeue.find (reason);
121
122 if (it != nDroppedPacketsAfterDequeue.end ())
123 {
124 count += it->second;
125 }
126
127 return count;
128 }
129
130 uint64_t
GetNDroppedBytes(std::string reason) const131 QueueDisc::Stats::GetNDroppedBytes (std::string reason) const
132 {
133 uint64_t count = 0;
134 auto it = nDroppedBytesBeforeEnqueue.find (reason);
135
136 if (it != nDroppedBytesBeforeEnqueue.end ())
137 {
138 count += it->second;
139 }
140
141 it = nDroppedBytesAfterDequeue.find (reason);
142
143 if (it != nDroppedBytesAfterDequeue.end ())
144 {
145 count += it->second;
146 }
147
148 return count;
149 }
150
151 uint32_t
GetNMarkedPackets(std::string reason) const152 QueueDisc::Stats::GetNMarkedPackets (std::string reason) const
153 {
154 auto it = nMarkedPackets.find (reason);
155
156 if (it != nMarkedPackets.end ())
157 {
158 return it->second;
159 }
160
161 return 0;
162 }
163
164 uint64_t
GetNMarkedBytes(std::string reason) const165 QueueDisc::Stats::GetNMarkedBytes (std::string reason) const
166 {
167 auto it = nMarkedBytes.find (reason);
168
169 if (it != nMarkedBytes.end ())
170 {
171 return it->second;
172 }
173
174 return 0;
175 }
176
177 void
Print(std::ostream & os) const178 QueueDisc::Stats::Print (std::ostream &os) const
179 {
180 std::map<std::string, uint32_t>::const_iterator itp;
181 std::map<std::string, uint64_t>::const_iterator itb;
182
183 os << std::endl << "Packets/Bytes received: "
184 << nTotalReceivedPackets << " / "
185 << nTotalReceivedBytes
186 << std::endl << "Packets/Bytes enqueued: "
187 << nTotalEnqueuedPackets << " / "
188 << nTotalEnqueuedBytes
189 << std::endl << "Packets/Bytes dequeued: "
190 << nTotalDequeuedPackets << " / "
191 << nTotalDequeuedBytes
192 << std::endl << "Packets/Bytes requeued: "
193 << nTotalRequeuedPackets << " / "
194 << nTotalRequeuedBytes
195 << std::endl << "Packets/Bytes dropped: "
196 << nTotalDroppedPackets << " / "
197 << nTotalDroppedBytes
198 << std::endl << "Packets/Bytes dropped before enqueue: "
199 << nTotalDroppedPacketsBeforeEnqueue << " / "
200 << nTotalDroppedBytesBeforeEnqueue;
201
202 itp = nDroppedPacketsBeforeEnqueue.begin ();
203 itb = nDroppedBytesBeforeEnqueue.begin ();
204
205 while (itp != nDroppedPacketsBeforeEnqueue.end () &&
206 itb != nDroppedBytesBeforeEnqueue.end ())
207 {
208 NS_ASSERT (itp->first.compare (itb->first) == 0);
209 os << std::endl << " " << itp->first << ": "
210 << itp->second << " / " << itb->second;
211 itp++;
212 itb++;
213 }
214
215 os << std::endl << "Packets/Bytes dropped after dequeue: "
216 << nTotalDroppedPacketsAfterDequeue << " / "
217 << nTotalDroppedBytesAfterDequeue;
218
219 itp = nDroppedPacketsAfterDequeue.begin ();
220 itb = nDroppedBytesAfterDequeue.begin ();
221
222 while (itp != nDroppedPacketsAfterDequeue.end () &&
223 itb != nDroppedBytesAfterDequeue.end ())
224 {
225 NS_ASSERT (itp->first.compare (itb->first) == 0);
226 os << std::endl << " " << itp->first << ": "
227 << itp->second << " / " << itb->second;
228 itp++;
229 itb++;
230 }
231
232 os << std::endl << "Packets/Bytes sent: "
233 << nTotalSentPackets << " / "
234 << nTotalSentBytes
235 << std::endl << "Packets/Bytes marked: "
236 << nTotalMarkedPackets << " / "
237 << nTotalMarkedBytes;
238
239 itp = nMarkedPackets.begin ();
240 itb = nMarkedBytes.begin ();
241
242 while (itp != nMarkedPackets.end () &&
243 itb != nMarkedBytes.end ())
244 {
245 NS_ASSERT (itp->first.compare (itb->first) == 0);
246 os << std::endl << " " << itp->first << ": "
247 << itp->second << " / " << itb->second;
248 itp++;
249 itb++;
250 }
251
252 os << std::endl;
253 }
254
operator <<(std::ostream & os,const QueueDisc::Stats & stats)255 std::ostream & operator << (std::ostream &os, const QueueDisc::Stats &stats)
256 {
257 stats.Print (os);
258 return os;
259 }
260
261 NS_OBJECT_ENSURE_REGISTERED (QueueDisc);
262
GetTypeId(void)263 TypeId QueueDisc::GetTypeId (void)
264 {
265 static TypeId tid = TypeId ("ns3::QueueDisc")
266 .SetParent<Object> ()
267 .SetGroupName ("TrafficControl")
268 .AddAttribute ("Quota", "The maximum number of packets dequeued in a qdisc run",
269 UintegerValue (DEFAULT_QUOTA),
270 MakeUintegerAccessor (&QueueDisc::SetQuota,
271 &QueueDisc::GetQuota),
272 MakeUintegerChecker<uint32_t> ())
273 .AddAttribute ("InternalQueueList", "The list of internal queues.",
274 ObjectVectorValue (),
275 MakeObjectVectorAccessor (&QueueDisc::m_queues),
276 MakeObjectVectorChecker<InternalQueue> ())
277 .AddAttribute ("PacketFilterList", "The list of packet filters.",
278 ObjectVectorValue (),
279 MakeObjectVectorAccessor (&QueueDisc::m_filters),
280 MakeObjectVectorChecker<PacketFilter> ())
281 .AddAttribute ("QueueDiscClassList", "The list of queue disc classes.",
282 ObjectVectorValue (),
283 MakeObjectVectorAccessor (&QueueDisc::m_classes),
284 MakeObjectVectorChecker<QueueDiscClass> ())
285 .AddTraceSource ("Enqueue", "Enqueue a packet in the queue disc",
286 MakeTraceSourceAccessor (&QueueDisc::m_traceEnqueue),
287 "ns3::QueueDiscItem::TracedCallback")
288 .AddTraceSource ("Dequeue", "Dequeue a packet from the queue disc",
289 MakeTraceSourceAccessor (&QueueDisc::m_traceDequeue),
290 "ns3::QueueDiscItem::TracedCallback")
291 .AddTraceSource ("Requeue", "Requeue a packet in the queue disc",
292 MakeTraceSourceAccessor (&QueueDisc::m_traceRequeue),
293 "ns3::QueueDiscItem::TracedCallback")
294 .AddTraceSource ("Drop", "Drop a packet stored in the queue disc",
295 MakeTraceSourceAccessor (&QueueDisc::m_traceDrop),
296 "ns3::QueueDiscItem::TracedCallback")
297 .AddTraceSource ("DropBeforeEnqueue", "Drop a packet before enqueue",
298 MakeTraceSourceAccessor (&QueueDisc::m_traceDropBeforeEnqueue),
299 "ns3::QueueDiscItem::TracedCallback")
300 .AddTraceSource ("DropAfterDequeue", "Drop a packet after dequeue",
301 MakeTraceSourceAccessor (&QueueDisc::m_traceDropAfterDequeue),
302 "ns3::QueueDiscItem::TracedCallback")
303 .AddTraceSource ("Mark", "Mark a packet stored in the queue disc",
304 MakeTraceSourceAccessor (&QueueDisc::m_traceMark),
305 "ns3::QueueDiscItem::TracedCallback")
306 .AddTraceSource ("PacketsInQueue",
307 "Number of packets currently stored in the queue disc",
308 MakeTraceSourceAccessor (&QueueDisc::m_nPackets),
309 "ns3::TracedValueCallback::Uint32")
310 .AddTraceSource ("BytesInQueue",
311 "Number of bytes currently stored in the queue disc",
312 MakeTraceSourceAccessor (&QueueDisc::m_nBytes),
313 "ns3::TracedValueCallback::Uint32")
314 .AddTraceSource ("SojournTime",
315 "Sojourn time of the last packet dequeued from the queue disc",
316 MakeTraceSourceAccessor (&QueueDisc::m_sojourn),
317 "ns3::Time::TracedCallback")
318 ;
319 return tid;
320 }
321
QueueDisc(QueueDiscSizePolicy policy)322 QueueDisc::QueueDisc (QueueDiscSizePolicy policy)
323 : m_nPackets (0),
324 m_nBytes (0),
325 m_maxSize (QueueSize ("1p")), // to avoid that setting the mode at construction time is ignored
326 m_running (false),
327 m_peeked (false),
328 m_sizePolicy (policy),
329 m_prohibitChangeMode (false)
330 {
331 NS_LOG_FUNCTION (this << (uint16_t)policy);
332
333 // These lambdas call the DropBeforeEnqueue or DropAfterDequeue methods of this
334 // QueueDisc object. Given that a callback to the operator() of these lambdas
335 // is connected to the DropBeforeEnqueue and DropAfterDequeue traces of the
336 // internal queues, the INTERNAL_QUEUE_DROP constant is passed as the reason
337 // why the packet is dropped.
338 m_internalQueueDbeFunctor = [this] (Ptr<const QueueDiscItem> item)
339 {
340 return DropBeforeEnqueue (item, INTERNAL_QUEUE_DROP);
341 };
342 m_internalQueueDadFunctor = [this] (Ptr<const QueueDiscItem> item)
343 {
344 return DropAfterDequeue (item, INTERNAL_QUEUE_DROP);
345 };
346
347 // These lambdas call the DropBeforeEnqueue or DropAfterDequeue methods of this
348 // QueueDisc object. Given that a callback to the operator() of these lambdas
349 // is connected to the DropBeforeEnqueue and DropAfterDequeue traces of the
350 // child queue discs, the concatenation of the CHILD_QUEUE_DISC_DROP constant
351 // and the second argument provided by such traces is passed as the reason why
352 // the packet is dropped.
353 m_childQueueDiscDbeFunctor = [this] (Ptr<const QueueDiscItem> item, const char* r)
354 {
355 return DropBeforeEnqueue (item,
356 m_childQueueDiscDropMsg.assign (CHILD_QUEUE_DISC_DROP).append (r).data ());
357 };
358 m_childQueueDiscDadFunctor = [this] (Ptr<const QueueDiscItem> item, const char* r)
359 {
360 return DropAfterDequeue (item,
361 m_childQueueDiscDropMsg.assign (CHILD_QUEUE_DISC_DROP).append (r).data ());
362 };
363 m_childQueueDiscMarkFunctor = [this] (Ptr<const QueueDiscItem> item, const char* r)
364 {
365 return Mark (const_cast<QueueDiscItem *> (PeekPointer (item)),
366 m_childQueueDiscMarkMsg.assign (CHILD_QUEUE_DISC_MARK).append (r).data ());
367 };
368 }
369
QueueDisc(QueueDiscSizePolicy policy,QueueSizeUnit unit)370 QueueDisc::QueueDisc (QueueDiscSizePolicy policy, QueueSizeUnit unit)
371 : QueueDisc (policy)
372 {
373 m_maxSize = QueueSize (unit, 0);
374 m_prohibitChangeMode = true;
375 }
376
~QueueDisc()377 QueueDisc::~QueueDisc ()
378 {
379 NS_LOG_FUNCTION (this);
380 }
381
382 void
DoDispose(void)383 QueueDisc::DoDispose (void)
384 {
385 NS_LOG_FUNCTION (this);
386 m_queues.clear ();
387 m_filters.clear ();
388 m_classes.clear ();
389 m_devQueueIface = 0;
390 m_send = nullptr;
391 m_requeued = 0;
392 m_internalQueueDbeFunctor = nullptr;
393 m_internalQueueDadFunctor = nullptr;
394 m_childQueueDiscDbeFunctor = nullptr;
395 m_childQueueDiscDadFunctor = nullptr;
396 Object::DoDispose ();
397 }
398
399 void
DoInitialize(void)400 QueueDisc::DoInitialize (void)
401 {
402 NS_LOG_FUNCTION (this);
403
404 // Check the configuration and initialize the parameters of this queue disc
405 bool ok = CheckConfig ();
406 NS_ASSERT_MSG (ok, "The queue disc configuration is not correct");
407 NS_UNUSED (ok); // suppress compiler warning
408 InitializeParams ();
409
410 // Check the configuration and initialize the parameters of the child queue discs
411 for (std::vector<Ptr<QueueDiscClass> >::iterator cl = m_classes.begin ();
412 cl != m_classes.end (); cl++)
413 {
414 (*cl)->GetQueueDisc ()->Initialize ();
415 }
416
417 Object::DoInitialize ();
418 }
419
420 const QueueDisc::Stats&
GetStats(void)421 QueueDisc::GetStats (void)
422 {
423 NS_ASSERT (m_stats.nTotalDroppedPackets == m_stats.nTotalDroppedPacketsBeforeEnqueue
424 + m_stats.nTotalDroppedPacketsAfterDequeue);
425 NS_ASSERT (m_stats.nTotalDroppedBytes == m_stats.nTotalDroppedBytesBeforeEnqueue
426 + m_stats.nTotalDroppedBytesAfterDequeue);
427
428 // the total number of sent packets is only updated here to avoid to increase it
429 // after a dequeue and then having to decrease it if the packet is dropped after
430 // dequeue or requeued
431 m_stats.nTotalSentPackets = m_stats.nTotalDequeuedPackets - (m_requeued ? 1 : 0)
432 - m_stats.nTotalDroppedPacketsAfterDequeue;
433 m_stats.nTotalSentBytes = m_stats.nTotalDequeuedBytes - (m_requeued ? m_requeued->GetSize () : 0)
434 - m_stats.nTotalDroppedBytesAfterDequeue;
435
436 return m_stats;
437 }
438
439 uint32_t
GetNPackets() const440 QueueDisc::GetNPackets () const
441 {
442 NS_LOG_FUNCTION (this);
443 return m_nPackets;
444 }
445
446 uint32_t
GetNBytes(void) const447 QueueDisc::GetNBytes (void) const
448 {
449 NS_LOG_FUNCTION (this);
450 return m_nBytes;
451 }
452
453 QueueSize
GetMaxSize(void) const454 QueueDisc::GetMaxSize (void) const
455 {
456 NS_LOG_FUNCTION (this);
457
458 switch (m_sizePolicy)
459 {
460 case QueueDiscSizePolicy::NO_LIMITS:
461 NS_FATAL_ERROR ("The size of this queue disc is not limited");
462
463 case QueueDiscSizePolicy::SINGLE_INTERNAL_QUEUE:
464 if (GetNInternalQueues ())
465 {
466 return GetInternalQueue (0)->GetMaxSize ();
467 }
468
469 case QueueDiscSizePolicy::SINGLE_CHILD_QUEUE_DISC:
470 if (GetNQueueDiscClasses ())
471 {
472 return GetQueueDiscClass (0)->GetQueueDisc ()->GetMaxSize ();
473 }
474
475 case QueueDiscSizePolicy::MULTIPLE_QUEUES:
476 default:
477 return m_maxSize;
478 }
479 }
480
481 bool
SetMaxSize(QueueSize size)482 QueueDisc::SetMaxSize (QueueSize size)
483 {
484 NS_LOG_FUNCTION (this << size);
485
486 // do nothing if the limit is null
487 if (!size.GetValue ())
488 {
489 return false;
490 }
491
492 if (m_prohibitChangeMode && size.GetUnit () != m_maxSize.GetUnit ())
493 {
494 NS_LOG_DEBUG ("Changing the mode of this queue disc is prohibited");
495 return false;
496 }
497
498 switch (m_sizePolicy)
499 {
500 case QueueDiscSizePolicy::NO_LIMITS:
501 NS_FATAL_ERROR ("The size of this queue disc is not limited");
502
503 case QueueDiscSizePolicy::SINGLE_INTERNAL_QUEUE:
504 if (GetNInternalQueues ())
505 {
506 GetInternalQueue (0)->SetMaxSize (size);
507 }
508
509 case QueueDiscSizePolicy::SINGLE_CHILD_QUEUE_DISC:
510 if (GetNQueueDiscClasses ())
511 {
512 GetQueueDiscClass (0)->GetQueueDisc ()->SetMaxSize (size);
513 }
514
515 case QueueDiscSizePolicy::MULTIPLE_QUEUES:
516 default:
517 m_maxSize = size;
518 }
519 return true;
520 }
521
522 QueueSize
GetCurrentSize(void)523 QueueDisc::GetCurrentSize (void)
524 {
525 NS_LOG_FUNCTION (this);
526
527 if (GetMaxSize ().GetUnit () == QueueSizeUnit::PACKETS)
528 {
529 return QueueSize (QueueSizeUnit::PACKETS, m_nPackets);
530 }
531 if (GetMaxSize ().GetUnit () == QueueSizeUnit::BYTES)
532 {
533 return QueueSize (QueueSizeUnit::BYTES, m_nBytes);
534 }
535 NS_ABORT_MSG ("Unknown queue size unit");
536 }
537
538 void
SetNetDeviceQueueInterface(Ptr<NetDeviceQueueInterface> ndqi)539 QueueDisc::SetNetDeviceQueueInterface (Ptr<NetDeviceQueueInterface> ndqi)
540 {
541 NS_LOG_FUNCTION (this << ndqi);
542 m_devQueueIface = ndqi;
543 }
544
545 Ptr<NetDeviceQueueInterface>
GetNetDeviceQueueInterface(void) const546 QueueDisc::GetNetDeviceQueueInterface (void) const
547 {
548 NS_LOG_FUNCTION (this);
549 return m_devQueueIface;
550 }
551
552 void
SetSendCallback(SendCallback func)553 QueueDisc::SetSendCallback (SendCallback func)
554 {
555 NS_LOG_FUNCTION (this);
556 m_send = func;
557 }
558
559 QueueDisc::SendCallback
GetSendCallback(void) const560 QueueDisc::GetSendCallback (void) const
561 {
562 NS_LOG_FUNCTION (this);
563 return m_send;
564 }
565
566 void
SetQuota(const uint32_t quota)567 QueueDisc::SetQuota (const uint32_t quota)
568 {
569 NS_LOG_FUNCTION (this << quota);
570 m_quota = quota;
571 }
572
573 uint32_t
GetQuota(void) const574 QueueDisc::GetQuota (void) const
575 {
576 NS_LOG_FUNCTION (this);
577 return m_quota;
578 }
579
580 void
AddInternalQueue(Ptr<InternalQueue> queue)581 QueueDisc::AddInternalQueue (Ptr<InternalQueue> queue)
582 {
583 NS_LOG_FUNCTION (this);
584
585 // set various callbacks on the internal queue, so that the queue disc is
586 // notified of packets enqueued, dequeued or dropped by the internal queue
587 queue->TraceConnectWithoutContext ("Enqueue",
588 MakeCallback (&QueueDisc::PacketEnqueued, this));
589 queue->TraceConnectWithoutContext ("Dequeue",
590 MakeCallback (&QueueDisc::PacketDequeued, this));
591 queue->TraceConnectWithoutContext ("DropBeforeEnqueue",
592 MakeCallback (&InternalQueueDropFunctor::operator(),
593 &m_internalQueueDbeFunctor));
594 queue->TraceConnectWithoutContext ("DropAfterDequeue",
595 MakeCallback (&InternalQueueDropFunctor::operator(),
596 &m_internalQueueDadFunctor));
597 m_queues.push_back (queue);
598 }
599
600 Ptr<QueueDisc::InternalQueue>
GetInternalQueue(std::size_t i) const601 QueueDisc::GetInternalQueue (std::size_t i) const
602 {
603 NS_ASSERT (i < m_queues.size ());
604 return m_queues[i];
605 }
606
607 std::size_t
GetNInternalQueues(void) const608 QueueDisc::GetNInternalQueues (void) const
609 {
610 return m_queues.size ();
611 }
612
613 void
AddPacketFilter(Ptr<PacketFilter> filter)614 QueueDisc::AddPacketFilter (Ptr<PacketFilter> filter)
615 {
616 NS_LOG_FUNCTION (this);
617 m_filters.push_back (filter);
618 }
619
620 Ptr<PacketFilter>
GetPacketFilter(std::size_t i) const621 QueueDisc::GetPacketFilter (std::size_t i) const
622 {
623 NS_ASSERT (i < m_filters.size ());
624 return m_filters[i];
625 }
626
627 std::size_t
GetNPacketFilters(void) const628 QueueDisc::GetNPacketFilters (void) const
629 {
630 return m_filters.size ();
631 }
632
633 void
AddQueueDiscClass(Ptr<QueueDiscClass> qdClass)634 QueueDisc::AddQueueDiscClass (Ptr<QueueDiscClass> qdClass)
635 {
636 NS_LOG_FUNCTION (this);
637 NS_ABORT_MSG_IF (qdClass->GetQueueDisc () == 0, "Cannot add a class with no attached queue disc");
638 // the child queue disc cannot be one with wake mode equal to WAKE_CHILD because
639 // such queue discs do not implement the enqueue/dequeue methods
640 NS_ABORT_MSG_IF (qdClass->GetQueueDisc ()->GetWakeMode () == WAKE_CHILD,
641 "A queue disc with WAKE_CHILD as wake mode can only be a root queue disc");
642
643 // set the parent callbacks on the child queue disc, so that it can notify
644 // the parent queue disc of packets enqueued, dequeued, dropped, or marked
645 qdClass->GetQueueDisc ()->TraceConnectWithoutContext ("Enqueue",
646 MakeCallback (&QueueDisc::PacketEnqueued, this));
647 qdClass->GetQueueDisc ()->TraceConnectWithoutContext ("Dequeue",
648 MakeCallback (&QueueDisc::PacketDequeued, this));
649 qdClass->GetQueueDisc ()->TraceConnectWithoutContext ("DropBeforeEnqueue",
650 MakeCallback (&ChildQueueDiscDropFunctor::operator(),
651 &m_childQueueDiscDbeFunctor));
652 qdClass->GetQueueDisc ()->TraceConnectWithoutContext ("DropAfterDequeue",
653 MakeCallback (&ChildQueueDiscDropFunctor::operator(),
654 &m_childQueueDiscDadFunctor));
655 qdClass->GetQueueDisc ()->TraceConnectWithoutContext ("Mark",
656 MakeCallback (&ChildQueueDiscMarkFunctor::operator(),
657 &m_childQueueDiscMarkFunctor));
658 m_classes.push_back (qdClass);
659 }
660
661 Ptr<QueueDiscClass>
GetQueueDiscClass(std::size_t i) const662 QueueDisc::GetQueueDiscClass (std::size_t i) const
663 {
664 NS_ASSERT (i < m_classes.size ());
665 return m_classes[i];
666 }
667
668 std::size_t
GetNQueueDiscClasses(void) const669 QueueDisc::GetNQueueDiscClasses (void) const
670 {
671 return m_classes.size ();
672 }
673
674 int32_t
Classify(Ptr<QueueDiscItem> item)675 QueueDisc::Classify (Ptr<QueueDiscItem> item)
676 {
677 NS_LOG_FUNCTION (this << item);
678
679 int32_t ret = PacketFilter::PF_NO_MATCH;
680 for (std::vector<Ptr<PacketFilter> >::iterator f = m_filters.begin ();
681 f != m_filters.end () && ret == PacketFilter::PF_NO_MATCH; f++)
682 {
683 ret = (*f)->Classify (item);
684 }
685 return ret;
686 }
687
688 QueueDisc::WakeMode
GetWakeMode(void) const689 QueueDisc::GetWakeMode (void) const
690 {
691 return WAKE_ROOT;
692 }
693
694 void
PacketEnqueued(Ptr<const QueueDiscItem> item)695 QueueDisc::PacketEnqueued (Ptr<const QueueDiscItem> item)
696 {
697 m_nPackets++;
698 m_nBytes += item->GetSize ();
699 m_stats.nTotalEnqueuedPackets++;
700 m_stats.nTotalEnqueuedBytes += item->GetSize ();
701
702 NS_LOG_LOGIC ("m_traceEnqueue (p)");
703 m_traceEnqueue (item);
704 }
705
706 void
PacketDequeued(Ptr<const QueueDiscItem> item)707 QueueDisc::PacketDequeued (Ptr<const QueueDiscItem> item)
708 {
709 // If the queue disc asked the internal queue or the child queue disc to
710 // dequeue a packet because a peek operation was requested, the packet is
711 // still held by the queue disc, hence we do not need to update statistics
712 // and fire the dequeue trace. This function will be explicitly called when
713 // the packet will be actually dequeued.
714 if (!m_peeked)
715 {
716 m_nPackets--;
717 m_nBytes -= item->GetSize ();
718 m_stats.nTotalDequeuedPackets++;
719 m_stats.nTotalDequeuedBytes += item->GetSize ();
720
721 m_sojourn (Simulator::Now () - item->GetTimeStamp ());
722
723 NS_LOG_LOGIC ("m_traceDequeue (p)");
724 m_traceDequeue (item);
725 }
726 }
727
728 void
DropBeforeEnqueue(Ptr<const QueueDiscItem> item,const char * reason)729 QueueDisc::DropBeforeEnqueue (Ptr<const QueueDiscItem> item, const char* reason)
730 {
731 NS_LOG_FUNCTION (this << item << reason);
732
733 m_stats.nTotalDroppedPackets++;
734 m_stats.nTotalDroppedBytes += item->GetSize ();
735 m_stats.nTotalDroppedPacketsBeforeEnqueue++;
736 m_stats.nTotalDroppedBytesBeforeEnqueue += item->GetSize ();
737
738 // update the number of packets dropped for the given reason
739 std::map<std::string, uint32_t>::iterator itp = m_stats.nDroppedPacketsBeforeEnqueue.find (reason);
740 if (itp != m_stats.nDroppedPacketsBeforeEnqueue.end ())
741 {
742 itp->second++;
743 }
744 else
745 {
746 m_stats.nDroppedPacketsBeforeEnqueue[reason] = 1;
747 }
748 // update the amount of bytes dropped for the given reason
749 std::map<std::string, uint64_t>::iterator itb = m_stats.nDroppedBytesBeforeEnqueue.find (reason);
750 if (itb != m_stats.nDroppedBytesBeforeEnqueue.end ())
751 {
752 itb->second += item->GetSize ();
753 }
754 else
755 {
756 m_stats.nDroppedBytesBeforeEnqueue[reason] = item->GetSize ();
757 }
758
759 NS_LOG_DEBUG ("Total packets/bytes dropped before enqueue: "
760 << m_stats.nTotalDroppedPacketsBeforeEnqueue << " / "
761 << m_stats.nTotalDroppedBytesBeforeEnqueue);
762 NS_LOG_LOGIC ("m_traceDropBeforeEnqueue (p)");
763 m_traceDrop (item);
764 m_traceDropBeforeEnqueue (item, reason);
765 }
766
767 void
DropAfterDequeue(Ptr<const QueueDiscItem> item,const char * reason)768 QueueDisc::DropAfterDequeue (Ptr<const QueueDiscItem> item, const char* reason)
769 {
770 NS_LOG_FUNCTION (this << item << reason);
771
772 m_stats.nTotalDroppedPackets++;
773 m_stats.nTotalDroppedBytes += item->GetSize ();
774 m_stats.nTotalDroppedPacketsAfterDequeue++;
775 m_stats.nTotalDroppedBytesAfterDequeue += item->GetSize ();
776
777 // update the number of packets dropped for the given reason
778 std::map<std::string, uint32_t>::iterator itp = m_stats.nDroppedPacketsAfterDequeue.find (reason);
779 if (itp != m_stats.nDroppedPacketsAfterDequeue.end ())
780 {
781 itp->second++;
782 }
783 else
784 {
785 m_stats.nDroppedPacketsAfterDequeue[reason] = 1;
786 }
787 // update the amount of bytes dropped for the given reason
788 std::map<std::string, uint64_t>::iterator itb = m_stats.nDroppedBytesAfterDequeue.find (reason);
789 if (itb != m_stats.nDroppedBytesAfterDequeue.end ())
790 {
791 itb->second += item->GetSize ();
792 }
793 else
794 {
795 m_stats.nDroppedBytesAfterDequeue[reason] = item->GetSize ();
796 }
797
798 // if in the context of a peek request a dequeued packet is dropped, we need
799 // to update the statistics and fire the dequeue trace before firing the drop
800 // after dequeue trace
801 if (m_peeked)
802 {
803 // temporarily set m_peeked to false, otherwise PacketDequeued does nothing
804 m_peeked = false;
805 PacketDequeued (item);
806 m_peeked = true;
807 }
808
809 NS_LOG_DEBUG ("Total packets/bytes dropped after dequeue: "
810 << m_stats.nTotalDroppedPacketsAfterDequeue << " / "
811 << m_stats.nTotalDroppedBytesAfterDequeue);
812 NS_LOG_LOGIC ("m_traceDropAfterDequeue (p)");
813 m_traceDrop (item);
814 m_traceDropAfterDequeue (item, reason);
815 }
816
817 bool
Mark(Ptr<QueueDiscItem> item,const char * reason)818 QueueDisc::Mark (Ptr<QueueDiscItem> item, const char* reason)
819 {
820 NS_LOG_FUNCTION (this << item << reason);
821
822 bool retval = item->Mark ();
823
824 if (!retval)
825 {
826 return false;
827 }
828
829 m_stats.nTotalMarkedPackets++;
830 m_stats.nTotalMarkedBytes += item->GetSize ();
831
832 // update the number of packets marked for the given reason
833 std::map<std::string, uint32_t>::iterator itp = m_stats.nMarkedPackets.find (reason);
834 if (itp != m_stats.nMarkedPackets.end ())
835 {
836 itp->second++;
837 }
838 else
839 {
840 m_stats.nMarkedPackets[reason] = 1;
841 }
842 // update the amount of bytes marked for the given reason
843 std::map<std::string, uint64_t>::iterator itb = m_stats.nMarkedBytes.find (reason);
844 if (itb != m_stats.nMarkedBytes.end ())
845 {
846 itb->second += item->GetSize ();
847 }
848 else
849 {
850 m_stats.nMarkedBytes[reason] = item->GetSize ();
851 }
852
853 NS_LOG_DEBUG ("Total packets/bytes marked: "
854 << m_stats.nTotalMarkedPackets << " / "
855 << m_stats.nTotalMarkedBytes);
856 m_traceMark (item, reason);
857 return true;
858 }
859
860 bool
Enqueue(Ptr<QueueDiscItem> item)861 QueueDisc::Enqueue (Ptr<QueueDiscItem> item)
862 {
863 NS_LOG_FUNCTION (this << item);
864
865 m_stats.nTotalReceivedPackets++;
866 m_stats.nTotalReceivedBytes += item->GetSize ();
867
868 bool retval = DoEnqueue (item);
869
870 if (retval)
871 {
872 item->SetTimeStamp (Simulator::Now ());
873 }
874
875 // DoEnqueue may return false because:
876 // 1) the internal queue is full
877 // -> the DropBeforeEnqueue method of this queue disc is automatically called
878 // because QueueDisc::AddInternalQueue sets the trace callback
879 // 2) the child queue disc dropped the packet
880 // -> the DropBeforeEnqueue method of this queue disc is automatically called
881 // because QueueDisc::AddQueueDiscClass sets the trace callback
882 // 3) it dropped the packet
883 // -> DoEnqueue has to explicitly call DropBeforeEnqueue
884 // Thus, we do not have to call DropBeforeEnqueue here.
885
886 // check that the received packet was either enqueued or dropped
887 NS_ASSERT (m_stats.nTotalReceivedPackets == m_stats.nTotalDroppedPacketsBeforeEnqueue +
888 m_stats.nTotalEnqueuedPackets);
889 NS_ASSERT (m_stats.nTotalReceivedBytes == m_stats.nTotalDroppedBytesBeforeEnqueue +
890 m_stats.nTotalEnqueuedBytes);
891
892 return retval;
893 }
894
895 Ptr<QueueDiscItem>
Dequeue(void)896 QueueDisc::Dequeue (void)
897 {
898 NS_LOG_FUNCTION (this);
899
900 // The QueueDisc::DoPeek method dequeues a packet and keeps it as a requeued
901 // packet. Thus, first check whether a peeked packet exists. Otherwise, call
902 // the private DoDequeue method.
903 Ptr<QueueDiscItem> item = m_requeued;
904
905 if (item)
906 {
907 m_requeued = 0;
908 if (m_peeked)
909 {
910 // If the packet was requeued because a peek operation was requested
911 // (which is the case here because DequeuePacket calls Dequeue only
912 // when m_requeued is null), we need to explicitly call PacketDequeued
913 // to update statistics about dequeued packets and fire the dequeue trace.
914 m_peeked = false;
915 PacketDequeued (item);
916 }
917 }
918 else
919 {
920 item = DoDequeue ();
921 }
922
923 NS_ASSERT (m_nPackets == m_stats.nTotalEnqueuedPackets - m_stats.nTotalDequeuedPackets);
924 NS_ASSERT (m_nBytes == m_stats.nTotalEnqueuedBytes - m_stats.nTotalDequeuedBytes);
925
926 return item;
927 }
928
929 Ptr<const QueueDiscItem>
Peek(void)930 QueueDisc::Peek (void)
931 {
932 NS_LOG_FUNCTION (this);
933 return DoPeek ();
934 }
935
936 Ptr<const QueueDiscItem>
DoPeek(void)937 QueueDisc::DoPeek (void)
938 {
939 NS_LOG_FUNCTION (this);
940
941 if (!m_requeued)
942 {
943 m_peeked = true;
944 m_requeued = Dequeue ();
945 // if no packet is returned, reset the m_peeked flag
946 if (!m_requeued)
947 {
948 m_peeked = false;
949 }
950 }
951 return m_requeued;
952 }
953
954 void
Run(void)955 QueueDisc::Run (void)
956 {
957 NS_LOG_FUNCTION (this);
958
959 if (RunBegin ())
960 {
961 uint32_t quota = m_quota;
962 while (Restart ())
963 {
964 quota -= 1;
965 if (quota <= 0)
966 {
967 /// \todo netif_schedule (q);
968 break;
969 }
970 }
971 RunEnd ();
972 }
973 }
974
975 bool
RunBegin(void)976 QueueDisc::RunBegin (void)
977 {
978 NS_LOG_FUNCTION (this);
979 if (m_running)
980 {
981 return false;
982 }
983
984 m_running = true;
985 return true;
986 }
987
988 void
RunEnd(void)989 QueueDisc::RunEnd (void)
990 {
991 NS_LOG_FUNCTION (this);
992 m_running = false;
993 }
994
995 bool
Restart(void)996 QueueDisc::Restart (void)
997 {
998 NS_LOG_FUNCTION (this);
999 Ptr<QueueDiscItem> item = DequeuePacket();
1000 if (item == 0)
1001 {
1002 NS_LOG_LOGIC ("No packet to send");
1003 return false;
1004 }
1005
1006 return Transmit (item);
1007 }
1008
1009 Ptr<QueueDiscItem>
DequeuePacket()1010 QueueDisc::DequeuePacket ()
1011 {
1012 NS_LOG_FUNCTION (this);
1013
1014 Ptr<QueueDiscItem> item;
1015
1016 // First check if there is a requeued packet
1017 if (m_requeued != 0)
1018 {
1019 // If the queue where the requeued packet is destined to is not stopped, return
1020 // the requeued packet; otherwise, return an empty packet.
1021 // If the device does not support flow control, the device queue is never stopped
1022 if (!m_devQueueIface || !m_devQueueIface->GetTxQueue (m_requeued->GetTxQueueIndex ())->IsStopped ())
1023 {
1024 item = m_requeued;
1025 m_requeued = 0;
1026 if (m_peeked)
1027 {
1028 // If the packet was requeued because a peek operation was requested
1029 // we need to explicitly call PacketDequeued to update statistics
1030 // about dequeued packets and fire the dequeue trace.
1031 m_peeked = false;
1032 PacketDequeued (item);
1033 }
1034 }
1035 }
1036 else
1037 {
1038 // If the device is multi-queue (actually, Linux checks if the queue disc has
1039 // multiple queues), ask the queue disc to dequeue a packet (a multi-queue aware
1040 // queue disc should try not to dequeue a packet destined to a stopped queue).
1041 // Otherwise, ask the queue disc to dequeue a packet only if the (unique) queue
1042 // is not stopped.
1043 if (!m_devQueueIface ||
1044 m_devQueueIface->GetNTxQueues ()>1 || !m_devQueueIface->GetTxQueue (0)->IsStopped ())
1045 {
1046 item = Dequeue ();
1047 // If the item is not null, add the header to the packet.
1048 if (item != 0)
1049 {
1050 item->AddHeader ();
1051 }
1052 // Here, Linux tries bulk dequeues
1053 }
1054 }
1055 return item;
1056 }
1057
1058 void
Requeue(Ptr<QueueDiscItem> item)1059 QueueDisc::Requeue (Ptr<QueueDiscItem> item)
1060 {
1061 NS_LOG_FUNCTION (this << item);
1062 m_requeued = item;
1063 /// \todo netif_schedule (q);
1064
1065 m_stats.nTotalRequeuedPackets++;
1066 m_stats.nTotalRequeuedBytes += item->GetSize ();
1067
1068 NS_LOG_LOGIC ("m_traceRequeue (p)");
1069 m_traceRequeue (item);
1070 }
1071
1072 bool
Transmit(Ptr<QueueDiscItem> item)1073 QueueDisc::Transmit (Ptr<QueueDiscItem> item)
1074 {
1075 NS_LOG_FUNCTION (this << item);
1076
1077 // if the device queue is stopped, requeue the packet and return false.
1078 // Note that if the underlying device is tc-unaware, packets are never
1079 // requeued because the queues of tc-unaware devices are never stopped
1080 if (m_devQueueIface && m_devQueueIface->GetTxQueue (item->GetTxQueueIndex ())->IsStopped ())
1081 {
1082 Requeue (item);
1083 return false;
1084 }
1085
1086 // a single queue device makes no use of the priority tag
1087 // a device that does not install a device queue interface likely makes no use of it as well
1088 if (!m_devQueueIface || m_devQueueIface->GetNTxQueues () == 1)
1089 {
1090 SocketPriorityTag priorityTag;
1091 item->GetPacket ()->RemovePacketTag (priorityTag);
1092 }
1093 NS_ASSERT_MSG (m_send, "Send callback not set");
1094 m_send (item);
1095
1096 // the behavior here slightly diverges from Linux. In Linux, it is advised that
1097 // the function called when a packet needs to be transmitted (ndo_start_xmit)
1098 // should always return NETDEV_TX_OK, which means that the packet is consumed by
1099 // the device driver and thus is not requeued. However, the ndo_start_xmit function
1100 // of the device driver is allowed to return NETDEV_TX_BUSY (and hence the packet
1101 // is requeued) when there is no room for the received packet in the device queue,
1102 // despite the queue is not stopped. This case is considered as a corner case or
1103 // an hard error, and should be avoided.
1104 // Here, we do not handle such corner case and always assume that the packet is
1105 // consumed by the netdevice. Thus, we ignore the value returned by Send and a
1106 // packet sent to a netdevice is never requeued. The reason is that the semantics
1107 // of the value returned by NetDevice::Send does not match that of the value
1108 // returned by ndo_start_xmit.
1109
1110 // if the queue disc is empty or the device queue is now stopped, return false so
1111 // that the Run method does not attempt to dequeue other packets and exits
1112 if (GetNPackets () == 0 ||
1113 (m_devQueueIface && m_devQueueIface->GetTxQueue (item->GetTxQueueIndex ())->IsStopped ()))
1114 {
1115 return false;
1116 }
1117
1118 return true;
1119 }
1120
1121 } // namespace ns3
1122