From a9f774e59847ae5049e31b5a5a4a90477d67a5db Mon Sep 17 00:00:00 2001 From: Tom Henderson Date: Wed, 13 May 2020 14:05:06 +0530 Subject: [PATCH 1/3] traffic-control: Add ECN marking to CoDel queue disc --- src/traffic-control/model/codel-queue-disc.cc | 56 ++++++++++++++----- src/traffic-control/model/codel-queue-disc.h | 5 ++ 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/src/traffic-control/model/codel-queue-disc.cc b/src/traffic-control/model/codel-queue-disc.cc index 089cfa4bb8..c8f93a0059 100644 --- a/src/traffic-control/model/codel-queue-disc.cc +++ b/src/traffic-control/model/codel-queue-disc.cc @@ -73,6 +73,11 @@ TypeId CoDelQueueDisc::GetTypeId (void) .SetParent () .SetGroupName ("TrafficControl") .AddConstructor () + .AddAttribute ("UseEcn", + "True to use ECN (packets are marked instead of being dropped)", + BooleanValue (false), + MakeBooleanAccessor (&CoDelQueueDisc::m_useEcn), + MakeBooleanChecker ()) .AddAttribute ("MaxSize", "The maximum number of packets/bytes accepted by this queue disc.", QueueSizeValue (QueueSize (QueueSizeUnit::BYTES, 1500 * DEFAULT_CODEL_LIMIT)), @@ -94,6 +99,11 @@ TypeId CoDelQueueDisc::GetTypeId (void) StringValue ("5ms"), MakeTimeAccessor (&CoDelQueueDisc::m_target), MakeTimeChecker ()) + .AddAttribute ("CeThreshold", + "The CoDel CE threshold for marking packets", + TimeValue (Time::Max ()), + MakeTimeAccessor (&CoDelQueueDisc::m_ceThreshold), + MakeTimeChecker ()) .AddTraceSource ("Count", "CoDel count", MakeTraceSourceAccessor (&CoDelQueueDisc::m_count), @@ -258,17 +268,25 @@ CoDelQueueDisc::DoDequeue (void) m_state2++; while (m_dropping && CoDelTimeAfterEq (now, m_dropNext)) { + ++m_count; + m_recInvSqrt = NewtonStep (m_recInvSqrt, m_count); // It's time for the next drop. Drop the current packet and // dequeue the next. The dequeue might take us out of dropping // state. If not, schedule the next drop. // A large amount of packets in queue might result in drop // rates so high that the next drop should happen now, // hence the while loop. + if (m_useEcn && Mark (item, TARGET_EXCEEDED_MARK)) + { + NS_LOG_LOGIC ("Sojourn time is still above target and it's time for next drop or mark; marking " << item); + NS_LOG_LOGIC ("Running ControlLaw for input m_dropNext: " << (double)m_dropNext / 1000000); + m_dropNext = ControlLaw (now, Time2CoDel (m_interval), m_recInvSqrt); + NS_LOG_LOGIC ("Scheduled next drop at " << (double) m_dropNext / 1000000); + goto end; + } NS_LOG_LOGIC ("Sojourn time is still above target and it's time for next drop; dropping " << item); DropAfterDequeue (item, TARGET_EXCEEDED_DROP); - ++m_count; - m_recInvSqrt = NewtonStep (m_recInvSqrt, m_count); item = GetInternalQueue (0)->Dequeue (); if (item) @@ -301,20 +319,24 @@ CoDelQueueDisc::DoDequeue (void) NS_LOG_LOGIC ("Not in dropping state; decide if we have to enter the state and drop the first packet"); if (okToDrop) { - // Drop the first packet and enter dropping state unless the queue is empty - NS_LOG_LOGIC ("Sojourn time goes above target, dropping the first packet " << item << " and entering the dropping state"); - DropAfterDequeue (item, TARGET_EXCEEDED_DROP); - - item = GetInternalQueue (0)->Dequeue (); - - if (item) + if (m_useEcn && Mark (item, TARGET_EXCEEDED_MARK)) { - NS_LOG_LOGIC ("Popped " << item); - NS_LOG_LOGIC ("Number packets remaining " << GetInternalQueue (0)->GetNPackets ()); - NS_LOG_LOGIC ("Number bytes remaining " << GetInternalQueue (0)->GetNBytes ()); + NS_LOG_LOGIC ("Sojourn time goes above target, marking the first packet " << item << " and entering the dropping state"); + } + else + { + // Drop the first packet and enter dropping state unless the queue is empty + NS_LOG_LOGIC ("Sojourn time goes above target, dropping the first packet " << item << " and entering the dropping state"); + DropAfterDequeue (item, TARGET_EXCEEDED_DROP); + item = GetInternalQueue (0)->Dequeue (); + if (item) + { + NS_LOG_LOGIC ("Popped " << item); + NS_LOG_LOGIC ("Number packets remaining " << GetInternalQueue (0)->GetNPackets ()); + NS_LOG_LOGIC ("Number bytes remaining " << GetInternalQueue (0)->GetNBytes ()); + } + OkToDrop (item, now); } - - OkToDrop (item, now); m_dropping = true; ++m_state3; /* @@ -339,6 +361,12 @@ CoDelQueueDisc::DoDequeue (void) NS_LOG_LOGIC ("Scheduled next drop at " << (double)m_dropNext / 1000000 << " now " << (double)now / 1000000); } } + end: + uint32_t ldelay = Time2CoDel (Simulator::Now () - item->GetTimeStamp ()); + if (item && m_useEcn && CoDelTimeAfter (ldelay, Time2CoDel (m_ceThreshold)) && Mark (item, CE_THRESHOLD_EXCEEDED_MARK)) + { + NS_LOG_LOGIC ("Marking due to CeThreshold " << m_ceThreshold.GetSeconds ()); + } ++m_states; return item; } diff --git a/src/traffic-control/model/codel-queue-disc.h b/src/traffic-control/model/codel-queue-disc.h index c6d24eecf5..1f1edc5091 100644 --- a/src/traffic-control/model/codel-queue-disc.h +++ b/src/traffic-control/model/codel-queue-disc.h @@ -101,6 +101,9 @@ public: // Reasons for dropping packets static constexpr const char* TARGET_EXCEEDED_DROP = "Target exceeded drop"; //!< Sojourn time above target static constexpr const char* OVERLIMIT_DROP = "Overlimit drop"; //!< Overlimit dropped packet + // Reasons for marking packets + static constexpr const char* TARGET_EXCEEDED_MARK = "Target exceeded mark"; //!< Sojourn time above target + static constexpr const char* CE_THRESHOLD_EXCEEDED_MARK = "CE threshold exceeded mark"; //!< Sojourn time above CE threshold private: friend class::CoDelQueueDiscNewtonStepTest; // Test code @@ -198,9 +201,11 @@ private: virtual void InitializeParams (void); + bool m_useEcn; //!< True if ECN is used (packets are marked instead of being dropped) uint32_t m_minBytes; //!< Minimum bytes in queue to allow a packet drop Time m_interval; //!< 100 ms sliding minimum time window width Time m_target; //!< 5 ms target queue delay + Time m_ceThreshold; //!< Threshold above which to CE mark TracedValue m_count; //!< Number of packets dropped since entering drop state TracedValue m_lastCount; //!< Last number of packets dropped since entering drop state TracedValue m_dropping; //!< True if in dropping state -- GitLab From 366f46b227c22c49d2a347b7f2998c194d5b41b4 Mon Sep 17 00:00:00 2001 From: BHASKAR K Date: Mon, 18 May 2020 12:43:33 +0530 Subject: [PATCH 2/3] traffic-control: Added ECN tests for CoDel queue disc. --- src/traffic-control/doc/codel.rst | 13 +- src/traffic-control/model/codel-queue-disc.cc | 2 +- .../test/codel-queue-disc-test-suite.cc | 448 +++++++++++++++++- 3 files changed, 443 insertions(+), 20 deletions(-) diff --git a/src/traffic-control/doc/codel.rst b/src/traffic-control/doc/codel.rst index 6ae1d34366..1848b64df8 100644 --- a/src/traffic-control/doc/codel.rst +++ b/src/traffic-control/doc/codel.rst @@ -32,18 +32,18 @@ Andrew McGregor based on Linux kernel code implemented by Dave Täht and Eric Du * ``CoDelQueueDisc::ShouldDrop ()``: This routine is ``CoDelQueueDisc::DoDequeue()``'s helper routine that determines whether a packet should be dropped or not based on its sojourn time. If the sojourn time goes above `m_target` and remains above continuously for at least `m_interval`, the routine returns ``true`` indicating that it is OK to drop the packet. Otherwise, it returns ``false``. - * ``CoDelQueueDisc::DoDequeue ()``: This routine performs the actual packet drop based on ``CoDelQueueDisc::ShouldDrop ()``'s return value and schedules the next drop. -* class :cpp:class:`CoDelTimestampTag`: This class implements the timestamp tagging for a packet. This tag is used to compute the packet's sojourn time (the difference between the time the packet is dequeued and the time it is pushed into the queue). + * ``CoDelQueueDisc::DoDequeue ()``: This routine performs the actual packet drop based on ``CoDelQueueDisc::ShouldDrop ()``'s return value and schedules the next drop/mark. +* class :cpp:class:`CoDelTimestampTag`: This class implements the timestamp tagging for a packet. This tag is used to compute the packet's sojourn time (the difference between the time the packet is dequeued and the time it is pushed into the queue). There are 2 branches to ``CoDelQueueDisc::DoDequeue ()``: -1. If the queue is currently in the dropping state, which means the sojourn time has remained above `m_target` for more than `m_interval`, the routine determines if it's OK to leave the dropping state or it's time for the next drop. When ``CoDelQueueDisc::ShouldDrop ()`` returns ``false``, the queue can move out of the dropping state (set `m_dropping` to ``false``). Otherwise, the queue continuously drops packets and updates the time for next drop (`m_dropNext`) until one of the following conditions is met: +1. If the queue is currently in the dropping state, which means the sojourn time has remained above `m_target` for more than `m_interval`, the routine determines if it's OK to leave the dropping state or it's time for the next drop/mark. When ``CoDelQueueDisc::ShouldDrop ()`` returns ``false``, the queue can move out of the dropping state (set `m_dropping` to ``false``). Otherwise, the queue continuously drops/marks packets and updates the time for next drop (`m_dropNext`) until one of the following conditions is met: 1. The queue is empty, upon which the queue leaves the dropping state and exits ``CoDelQueueDisc::ShouldDrop ()`` routine; 2. ``CoDelQueueDisc::ShouldDrop ()`` returns ``false`` (meaning the sojourn time goes below `m_target`) upon which the queue leaves the dropping state; - 3. It is not yet time for next drop (`m_dropNext` is less than current time) upon which the queue waits for the next packet dequeue to check the condition again. + 3. It is not yet time for next drop/mark (`m_dropNext` is less than current time) upon which the queue waits for the next packet dequeue to check the condition again. -2. If the queue is not in the dropping state, the routine enters the dropping state and drop the first packet if ``CoDelQueueDisc::ShouldDrop ()`` returns ``true`` (meaning the sojourn time has gone above `m_target` for at least `m_interval` for the first time or it has gone above again after the queue leaves the dropping state). +2. If the queue is not in the dropping state, the routine enters the dropping state and drop/mark the first packet if ``CoDelQueueDisc::ShouldDrop ()`` returns ``true`` (meaning the sojourn time has gone above `m_target` for at least `m_interval` for the first time or it has gone above again after the queue leaves the dropping state). The CoDel queue disc does not require packet filters, does not admit child queue discs and uses a single internal queue. If not provided by @@ -71,6 +71,8 @@ The key attributes that the CoDelQueue class holds include the following: * ``MinBytes:`` The CoDel algorithm minbytes parameter. The default value is 1500 bytes. * ``Interval:`` The sliding-minimum window. The default value is 100 ms. * ``Target:`` The CoDel algorithm target queue delay. The default value is 5 ms. +* ``UseEcn:`` True to use ECN (packets are marked instead of being dropped). The default value is false. +* ``CeThreshold:`` The CoDel CE threshold for marking packets. Disabled by default. Examples ======== @@ -129,6 +131,7 @@ The CoDel model is tested using :cpp:class:`CoDelQueueDiscTestSuite` class defin * Test 3: The third test checks the NewtonStep() arithmetic against explicit port of Linux implementation * Test 4: The fourth test checks the ControlLaw() against explicit port of Linux implementation * Test 5: The fifth test checks the enqueue/dequeue with drops according to CoDel algorithm +* Test 6: The sixth test checks the enqueue/dequeue with marks according to CoDel algorithm The test suite can be run using the following commands: diff --git a/src/traffic-control/model/codel-queue-disc.cc b/src/traffic-control/model/codel-queue-disc.cc index c8f93a0059..0b783743a1 100644 --- a/src/traffic-control/model/codel-queue-disc.cc +++ b/src/traffic-control/model/codel-queue-disc.cc @@ -392,7 +392,7 @@ CoDelQueueDisc::GetDropNext (void) bool CoDelQueueDisc::CoDelTimeAfter (uint32_t a, uint32_t b) { - return ((int)(a) - (int)(b) > 0); + return ((int)(a) - (int)(b) > 0); } bool diff --git a/src/traffic-control/test/codel-queue-disc-test-suite.cc b/src/traffic-control/test/codel-queue-disc-test-suite.cc index ecf4e0890e..ac7e8eb9dc 100644 --- a/src/traffic-control/test/codel-queue-disc-test-suite.cc +++ b/src/traffic-control/test/codel-queue-disc-test-suite.cc @@ -67,8 +67,9 @@ public: * * \param p packet * \param addr address + * \param ecnCapable ECN capable */ - CodelQueueDiscTestItem (Ptr p, const Address & addr); + CodelQueueDiscTestItem (Ptr p, const Address & addr, bool ecnCapable); virtual ~CodelQueueDiscTestItem (); virtual void AddHeader (void); virtual bool Mark(void); @@ -86,10 +87,12 @@ private: * Disable default implementation to avoid misuse */ CodelQueueDiscTestItem &operator = (const CodelQueueDiscTestItem &); + bool m_ecnCapablePacket; ///< ECN capable packet? }; -CodelQueueDiscTestItem::CodelQueueDiscTestItem (Ptr p, const Address & addr) - : QueueDiscItem (p, addr, 0) +CodelQueueDiscTestItem::CodelQueueDiscTestItem (Ptr p, const Address & addr, bool ecnCapable) + : QueueDiscItem (p, addr, ecnCapable), + m_ecnCapablePacket (ecnCapable) { } @@ -105,6 +108,10 @@ CodelQueueDiscTestItem::AddHeader (void) bool CodelQueueDiscTestItem::Mark (void) { + if (m_ecnCapablePacket) + { + return true; + } return false; } @@ -173,17 +180,17 @@ CoDelQueueDiscBasicEnqueueDequeue::DoRun (void) p6 = Create (pktSize); NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize().GetValue (), 0 * modeSize, "There should be no packets in queue"); - queue->Enqueue (Create (p1, dest)); + queue->Enqueue (Create (p1, dest, false)); NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize().GetValue (), 1 * modeSize, "There should be one packet in queue"); - queue->Enqueue (Create (p2, dest)); + queue->Enqueue (Create (p2, dest, false)); NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize().GetValue (), 2 * modeSize, "There should be two packets in queue"); - queue->Enqueue (Create (p3, dest)); + queue->Enqueue (Create (p3, dest, false)); NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize().GetValue (), 3 * modeSize, "There should be three packets in queue"); - queue->Enqueue (Create (p4, dest)); + queue->Enqueue (Create (p4, dest, false)); NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize().GetValue (), 4 * modeSize, "There should be four packets in queue"); - queue->Enqueue (Create (p5, dest)); + queue->Enqueue (Create (p5, dest, false)); NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize().GetValue (), 5 * modeSize, "There should be five packets in queue"); - queue->Enqueue (Create (p6, dest)); + queue->Enqueue (Create (p6, dest, false)); NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize().GetValue (), 6 * modeSize, "There should be six packets in queue"); NS_TEST_EXPECT_MSG_EQ (queue->GetStats ().GetNDroppedPackets (CoDelQueueDisc::OVERLIMIT_DROP), @@ -293,9 +300,9 @@ CoDelQueueDiscBasicOverflow::DoRun (void) queue->Initialize (); Enqueue (queue, pktSize, 500); - queue->Enqueue (Create (p1, dest)); - queue->Enqueue (Create (p2, dest)); - queue->Enqueue (Create (p3, dest)); + queue->Enqueue (Create (p1, dest, false)); + queue->Enqueue (Create (p2, dest, false)); + queue->Enqueue (Create (p3, dest, false)); NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize().GetValue (), 500 * modeSize, "There should be 500 packets in queue"); NS_TEST_EXPECT_MSG_EQ (queue->GetStats ().GetNDroppedPackets (CoDelQueueDisc::OVERLIMIT_DROP), @@ -308,7 +315,7 @@ CoDelQueueDiscBasicOverflow::Enqueue (Ptr queue, uint32_t size, Address dest; for (uint32_t i = 0; i < nPkt; i++) { - queue->Enqueue (Create (Create (size), dest)); + queue->Enqueue (Create (Create (size), dest, false)); } } @@ -511,7 +518,7 @@ CoDelQueueDiscBasicDrop::Enqueue (Ptr queue, uint32_t size, uint Address dest; for (uint32_t i = 0; i < nPkt; i++) { - queue->Enqueue (Create (Create (size), dest)); + queue->Enqueue (Create (Create (size), dest, false)); } } @@ -576,6 +583,416 @@ CoDelQueueDiscBasicDrop::Dequeue (Ptr queue, uint32_t modeSize) } } +/** + * \ingroup traffic-control-test + * \ingroup tests + * + * \brief Test 6: enqueue/dequeue with marks according to CoDel algorithm + */ +class CoDelQueueDiscBasicMark : public TestCase +{ +public: + /** + * Constructor + * + * \param mode the mode + */ + CoDelQueueDiscBasicMark (QueueSizeUnit mode); + virtual void DoRun (void); + +private: + /** + * Enqueue function + * \param queue the queue disc + * \param size the size + * \param nPkt the number of packets + * \param ecnCapable ECN capable traffic + */ + void Enqueue (Ptr queue, uint32_t size, uint32_t nPkt, bool ecnCapable); + /** Dequeue function + * \param queue the queue disc + * \param modeSize the mode size + * \param testCase the test case number + */ + void Dequeue (Ptr queue, uint32_t modeSize, uint32_t testCase); + /** + * Drop next tracer function + * \param oldVal the old value + * \param newVal the new value + */ + void DropNextTracer (uint32_t oldVal, uint32_t newVal); + QueueSizeUnit m_mode; ///< mode + uint32_t m_dropNextCount; ///< count the number of times m_dropNext is recalculated + uint32_t nPacketsBeforeFirstDrop; ///< Number of packets in the queue before first drop + uint32_t nPacketsBeforeFirstMark; ///< Number of packets in the queue before first mark +}; + +CoDelQueueDiscBasicMark::CoDelQueueDiscBasicMark (QueueSizeUnit mode) + : TestCase ("Basic mark operations") +{ + m_mode = mode; + m_dropNextCount = 0; +} + +void +CoDelQueueDiscBasicMark::DropNextTracer (uint32_t oldVal, uint32_t newVal) +{ + NS_UNUSED(oldVal); + NS_UNUSED(newVal); + m_dropNextCount++; +} + +void +CoDelQueueDiscBasicMark::DoRun (void) +{ + // Test is divided into 4 sub test cases: + // 1) Packets are not ECN capable. + // 2) Packets are ECN capable but marking due to exceeding CE threshold disabled + // 3) Some packets are ECN capable, with CE threshold set to 2ms. + // 4) Packets are ECN capable and CE threshold set to 2ms + + // Test case 1 + Ptr queue = CreateObject (); + uint32_t pktSize = 1000; + uint32_t modeSize = 0; + nPacketsBeforeFirstDrop = 0; + nPacketsBeforeFirstMark = 0; + + if (m_mode == QueueSizeUnit::BYTES) + { + modeSize = pktSize; + } + else if (m_mode == QueueSizeUnit::PACKETS) + { + modeSize = 1; + } + + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MaxSize", QueueSizeValue (QueueSize (m_mode, modeSize * 500))), + true, "Verify that we can actually set the attribute MaxSize"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("UseEcn", BooleanValue (true)), + true, "Verify that we can actually set the attribute UseEcn"); + + queue->Initialize (); + + // Not-ECT traffic to induce packet drop + Enqueue (queue, pktSize, 20, false); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 20 * modeSize, "There should be 20 packets in queue."); + + // Although the first dequeue occurs with a sojourn time above target + // there should not be any dropped packets in this interval + Time waitUntilFirstDequeue = 2 * queue->GetTarget (); + Simulator::Schedule (waitUntilFirstDequeue, &CoDelQueueDiscBasicMark::Dequeue, this, queue, modeSize, 1); + + // This dequeue should cause a packet to be dropped + Time waitUntilSecondDequeue = waitUntilFirstDequeue + 2 * queue->GetInterval (); + Simulator::Schedule (waitUntilSecondDequeue, &CoDelQueueDiscBasicMark::Dequeue, this, queue, modeSize, 1); + + Simulator::Run (); + Simulator::Destroy (); + + // Test case 2, queue with ECN capable traffic for marking of packets instead of dropping + queue = CreateObject (); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MaxSize", QueueSizeValue (QueueSize (m_mode, modeSize * 500))), + true, "Verify that we can actually set the attribute MaxSize"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("UseEcn", BooleanValue (true)), + true, "Verify that we can actually set the attribute UseEcn"); + + queue->Initialize (); + + // ECN capable traffic to induce packets to be marked + Enqueue (queue, pktSize, 20, true); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 20 * modeSize, "There should be 20 packets in queue."); + + // Although the first dequeue occurs with a sojourn time above target + // there should not be any target exceeded marked packets in this interval + Simulator::Schedule (waitUntilFirstDequeue, &CoDelQueueDiscBasicMark::Dequeue, this, queue, modeSize, 2); + + // This dequeue should cause a packet to be marked + Simulator::Schedule (waitUntilSecondDequeue, &CoDelQueueDiscBasicMark::Dequeue, this, queue, modeSize, 2); + + // Although we are in dropping state, it's not time for next packet to be target exceeded marked + // the dequeue should not cause a packet to be target exceeded marked + Simulator::Schedule (waitUntilSecondDequeue, &CoDelQueueDiscBasicMark::Dequeue, this, queue, modeSize, 2); + + // In dropping time and it's time for next packet to be target exceeded marked + // the dequeue should cause additional packet to be target exceeded marked + Simulator::Schedule (waitUntilSecondDequeue * 2, &CoDelQueueDiscBasicMark::Dequeue, this, queue, modeSize, 2); + + Simulator::Run (); + Simulator::Destroy (); + + // Test case 3, some packets are ECN capable, with CE threshold set to 2ms + queue = CreateObject (); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MaxSize", QueueSizeValue (QueueSize (m_mode, modeSize * 500))), + true, "Verify that we can actually set the attribute MaxSize"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("UseEcn", BooleanValue (true)), + true, "Verify that we can actually set the attribute UseEcn"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("CeThreshold", TimeValue (MilliSeconds (2))), + true, "Verify that we can actually set the attribute CeThreshold"); + + queue->Initialize (); + + // First 3 packets in the queue are ecnCapable + Enqueue (queue, pktSize, 3, true); + // Rest of the packet are not ecnCapable + Enqueue (queue, pktSize, 17, false); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 20 * modeSize, "There should be 20 packets in queue."); + + // Although the first dequeue occurs with a sojourn time above target + // there should not be any target exceeded marked packets in this interval + Simulator::Schedule (waitUntilFirstDequeue, &CoDelQueueDiscBasicMark::Dequeue, this, queue, modeSize, 3); + + // This dequeue should cause a packet to be marked + Simulator::Schedule (waitUntilSecondDequeue, &CoDelQueueDiscBasicMark::Dequeue, this, queue, modeSize, 3); + + // Although we are in dropping state, it's not time for next packet to be target exceeded marked + // the dequeue should not cause a packet to be target exceeded marked + Simulator::Schedule (waitUntilSecondDequeue, &CoDelQueueDiscBasicMark::Dequeue, this, queue, modeSize, 3); + + // In dropping time and it's time for next packet to be dropped as packets are not ECN capable + // the dequeue should cause packet to be dropped + Simulator::Schedule (waitUntilSecondDequeue * 2, &CoDelQueueDiscBasicMark::Dequeue, this, queue, modeSize, 3); + + Simulator::Run (); + Simulator::Destroy (); + + // Test case 4, queue with ECN capable traffic and CeThreshold set for marking of packets instead of dropping + queue = CreateObject (); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MaxSize", QueueSizeValue (QueueSize (m_mode, modeSize * 500))), + true, "Verify that we can actually set the attribute MaxSize"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("UseEcn", BooleanValue (true)), + true, "Verify that we can actually set the attribute UseEcn"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("CeThreshold", TimeValue (MilliSeconds (2))), + true, "Verify that we can actually set the attribute CeThreshold"); + + queue->Initialize (); + + // ECN capable traffic to induce packets to be marked + Enqueue (queue, pktSize, 20, true); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 20 * modeSize, "There should be 20 packets in queue."); + + // The first dequeue occurs with a sojourn time below CE threshold + // there should not any be CE threshold exceeded marked packets + Simulator::Schedule (MilliSeconds (1), &CoDelQueueDiscBasicMark::Dequeue, this, queue, modeSize, 4); + + // Sojourn time above CE threshold so this dequeue should cause a packet to be CE thershold exceeded marked + Simulator::Schedule (MilliSeconds (3), &CoDelQueueDiscBasicMark::Dequeue, this, queue, modeSize, 4); + + // the dequeue should cause a packet to be CE threshold exceeded marked + Simulator::Schedule (waitUntilFirstDequeue, &CoDelQueueDiscBasicMark::Dequeue, this, queue, modeSize, 4); + + // In dropping time and it's time for next packet to be dropped but because of using ECN, packet should be marked + Simulator::Schedule (waitUntilSecondDequeue, &CoDelQueueDiscBasicMark::Dequeue, this, queue, modeSize, 4); + + Simulator::Run (); + Simulator::Destroy (); +} + +void +CoDelQueueDiscBasicMark::Enqueue (Ptr queue, uint32_t size, uint32_t nPkt, bool ecnCapable) +{ + Address dest; + for (uint32_t i = 0; i < nPkt; i++) + { + queue->Enqueue (Create (Create (size), dest, ecnCapable)); + } +} + +void +CoDelQueueDiscBasicMark::Dequeue (Ptr queue, uint32_t modeSize, uint32_t testCase) +{ + uint32_t initialTargetMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::TARGET_EXCEEDED_MARK); + uint32_t initialCeThreshMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::CE_THRESHOLD_EXCEEDED_MARK); + uint32_t initialQSize = queue->GetCurrentSize ().GetValue (); + uint32_t initialDropNext = queue->GetDropNext (); + Time currentTime = Simulator::Now (); + uint32_t currentDropCount = 0; + uint32_t currentTargetMarkCount = 0; + uint32_t currentCeThreshMarkCount = 0; + + if (initialTargetMarkCount > 0 && currentTime.GetMicroSeconds () >= initialDropNext && testCase == 3) + { + queue->TraceConnectWithoutContext ("DropNext", MakeCallback (&CoDelQueueDiscBasicMark::DropNextTracer, this)); + } + + if (initialQSize != 0) + { + Ptr item = queue->Dequeue (); + if (testCase == 1) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CoDelQueueDisc::TARGET_EXCEEDED_DROP); + if (currentDropCount == 1) + { + nPacketsBeforeFirstDrop = initialQSize; + } + } + else if (testCase == 2) + { + if (initialTargetMarkCount == 0 && currentTime > queue->GetTarget ()) + { + if (currentTime < queue->GetInterval ()) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CoDelQueueDisc::TARGET_EXCEEDED_DROP); + currentTargetMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::TARGET_EXCEEDED_MARK); + currentCeThreshMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::CE_THRESHOLD_EXCEEDED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "There should be 1 packet dequeued."); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_ASSERT_MSG_EQ (currentTargetMarkCount, 0, "We are not in dropping state." + "Sojourn time has just gone above target from below." + "Hence, there should be no target exceeded marked packets"); + NS_TEST_ASSERT_MSG_EQ (currentCeThreshMarkCount, 0, "Marking due to CE threshold is disabled" + "Hence, there should not be any CE threshold exceeded marked packet"); + } + else if (currentTime >= queue->GetInterval ()) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CoDelQueueDisc::TARGET_EXCEEDED_DROP); + currentTargetMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::TARGET_EXCEEDED_MARK); + nPacketsBeforeFirstMark = initialQSize; + currentCeThreshMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::CE_THRESHOLD_EXCEEDED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "Sojourn time has been above target for at least interval." + "We enter the dropping state and perform initial packet marking" + "So there should be only 1 more packet dequeued."); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_EXPECT_MSG_EQ (currentTargetMarkCount, 1, "There should be 1 target exceeded marked packet"); + NS_TEST_ASSERT_MSG_EQ (currentCeThreshMarkCount, 0, "There should not be any CE threshold exceeded marked packet"); + } + } + else if (initialTargetMarkCount > 0) + { // In dropping state + if (currentTime.GetMicroSeconds () < initialDropNext) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CoDelQueueDisc::TARGET_EXCEEDED_DROP); + currentTargetMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::TARGET_EXCEEDED_MARK); + currentCeThreshMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::CE_THRESHOLD_EXCEEDED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "We are in dropping state." + "Sojourn is still above target." + "However, it's not time for next target exceeded mark." + "So there should be only 1 more packet dequeued"); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_EXPECT_MSG_EQ (currentTargetMarkCount, 1, "There should still be only 1 target exceeded marked packet from the last dequeue"); + NS_TEST_ASSERT_MSG_EQ (currentCeThreshMarkCount, 0, "There should not be any CE threshold exceeded marked packet"); + } + else if (currentTime.GetMicroSeconds () >= initialDropNext) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CoDelQueueDisc::TARGET_EXCEEDED_DROP); + currentTargetMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::TARGET_EXCEEDED_MARK); + currentCeThreshMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::CE_THRESHOLD_EXCEEDED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "We are in dropping state." + "It's time for packet to be marked" + "So there should be only 1 more packet dequeued"); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_EXPECT_MSG_EQ (currentTargetMarkCount, 2, "There should 2 target exceeded marked packet"); + NS_TEST_EXPECT_MSG_EQ (nPacketsBeforeFirstDrop, nPacketsBeforeFirstMark, "Number of packets in the queue before drop should be equal" + "to number of packets in the queue before first mark as the behavior untill packet N should be the same."); + NS_TEST_ASSERT_MSG_EQ (currentCeThreshMarkCount, 0, "There should not be any CE threshold exceeded marked packet"); + } + } + } + else if (testCase == 3) + { + if (initialTargetMarkCount == 0 && currentTime > queue->GetTarget ()) + { + if (currentTime < queue->GetInterval ()) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CoDelQueueDisc::TARGET_EXCEEDED_DROP); + currentTargetMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::TARGET_EXCEEDED_MARK); + currentCeThreshMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::CE_THRESHOLD_EXCEEDED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "There should be 1 packet dequeued."); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_ASSERT_MSG_EQ (currentTargetMarkCount, 0, "We are not in dropping state." + "Sojourn time has just gone above target from below." + "Hence, there should be no target exceeded marked packets"); + NS_TEST_ASSERT_MSG_EQ (currentCeThreshMarkCount, 1, "Sojourn time has gone above CE threshold." + "Hence, there should be 1 CE threshold exceeded marked packet"); + } + else if (currentTime >= queue->GetInterval ()) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CoDelQueueDisc::TARGET_EXCEEDED_DROP); + currentTargetMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::TARGET_EXCEEDED_MARK); + currentCeThreshMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::CE_THRESHOLD_EXCEEDED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "Sojourn time has been above target for at least interval." + "We enter the dropping state and perform initial packet marking" + "So there should be only 1 more packet dequeued."); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_EXPECT_MSG_EQ (currentTargetMarkCount, 1, "There should be 1 target exceeded marked packet"); + NS_TEST_EXPECT_MSG_EQ (currentCeThreshMarkCount, 2, "There should be 2 CE threshold exceeded marked packets"); + } + } + else if (initialTargetMarkCount > 0) + { // In dropping state + if (currentTime.GetMicroSeconds () < initialDropNext) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CoDelQueueDisc::TARGET_EXCEEDED_DROP); + currentTargetMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::TARGET_EXCEEDED_MARK); + currentCeThreshMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::CE_THRESHOLD_EXCEEDED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "We are in dropping state." + "Sojourn is still above target." + "However, it's not time for next target exceeded mark." + "So there should be only 1 more packet dequeued"); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_EXPECT_MSG_EQ (currentTargetMarkCount, 1, "There should still be only 1 target exceeded marked packet from the last dequeue"); + NS_TEST_EXPECT_MSG_EQ (currentCeThreshMarkCount, 3, "There should be 3 CE threshold exceeded marked packets"); + } + else if (currentTime.GetMicroSeconds () >= initialDropNext) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CoDelQueueDisc::TARGET_EXCEEDED_DROP); + currentTargetMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::TARGET_EXCEEDED_MARK); + currentCeThreshMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::CE_THRESHOLD_EXCEEDED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - (m_dropNextCount + 1) * modeSize, "We are in dropping state." + "It's time for packet to be dropped as packets are not ecnCapable" + "The number of packets dequeued equals to the number of times m_dropNext is updated plus initial dequeue"); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, m_dropNextCount, "The number of drops equals to the number of times m_dropNext is updated"); + NS_TEST_EXPECT_MSG_EQ (currentTargetMarkCount, 1, "There should still be only 1 target exceeded marked packet"); + NS_TEST_EXPECT_MSG_EQ (currentCeThreshMarkCount, 3, "There should still be 3 CE threshold exceeded marked packet as packets are not ecnCapable"); + } + } + } + else if (testCase == 4) + { + if (currentTime < queue->GetTarget ()) + { + if (initialCeThreshMarkCount == 0 && currentTime < MilliSeconds (2)) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CoDelQueueDisc::TARGET_EXCEEDED_DROP); + currentCeThreshMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::CE_THRESHOLD_EXCEEDED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "There should be 1 packet dequeued."); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_ASSERT_MSG_EQ (currentCeThreshMarkCount, 0, "Sojourn time has not gone above CE threshold." + "Hence, there should not be any CE threshold exceeded marked packet"); + } + else + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CoDelQueueDisc::TARGET_EXCEEDED_DROP); + currentCeThreshMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::CE_THRESHOLD_EXCEEDED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "There should be only 1 more packet dequeued."); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_EXPECT_MSG_EQ (currentCeThreshMarkCount, 1, "Sojourn time has gone above CE threshold." + "There should be 1 CE threshold exceeded marked packet"); + } + } + else if (initialCeThreshMarkCount > 0 && currentTime.GetMicroSeconds () < queue->GetInterval ()) + { + if (initialCeThreshMarkCount < 2) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CoDelQueueDisc::TARGET_EXCEEDED_DROP); + currentCeThreshMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::CE_THRESHOLD_EXCEEDED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "There should be only 1 more packet dequeued."); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_EXPECT_MSG_EQ (currentCeThreshMarkCount, 2, "There should be 2 CE threshold exceeded marked packets"); + } + else + { // In dropping state + currentDropCount = queue->GetStats ().GetNDroppedPackets (CoDelQueueDisc::TARGET_EXCEEDED_DROP); + currentCeThreshMarkCount = queue->GetStats ().GetNMarkedPackets (CoDelQueueDisc::CE_THRESHOLD_EXCEEDED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "There should be only 1 more packet dequeued."); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_EXPECT_MSG_EQ (currentCeThreshMarkCount, 3, "There should be 3 CE threshold exceeded marked packet"); + } + } + } + } +} + /** * \ingroup traffic-control-test * \ingroup tests @@ -601,5 +1018,8 @@ public: // Test 5: enqueue/dequeue with drops according to CoDel algorithm AddTestCase (new CoDelQueueDiscBasicDrop (QueueSizeUnit::PACKETS), TestCase::QUICK); AddTestCase (new CoDelQueueDiscBasicDrop (QueueSizeUnit::BYTES), TestCase::QUICK); + // Test 6: enqueue/dequeue with marks according to CoDel algorithm + AddTestCase (new CoDelQueueDiscBasicMark (QueueSizeUnit::PACKETS), TestCase::QUICK); + AddTestCase (new CoDelQueueDiscBasicMark (QueueSizeUnit::BYTES), TestCase::QUICK); } } g_coDelQueueTestSuite; ///< the test suite -- GitLab From 2e1959efc24e800a8264c5148515cd451106146d Mon Sep 17 00:00:00 2001 From: Tom Henderson Date: Fri, 29 May 2020 19:18:28 +0530 Subject: [PATCH 3/3] Update based on Bhaskar Kataria's comments --- src/traffic-control/model/codel-queue-disc.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/traffic-control/model/codel-queue-disc.cc b/src/traffic-control/model/codel-queue-disc.cc index 0b783743a1..0db4c4c136 100644 --- a/src/traffic-control/model/codel-queue-disc.cc +++ b/src/traffic-control/model/codel-queue-disc.cc @@ -392,25 +392,25 @@ CoDelQueueDisc::GetDropNext (void) bool CoDelQueueDisc::CoDelTimeAfter (uint32_t a, uint32_t b) { - return ((int)(a) - (int)(b) > 0); + return ((int64_t)(a) - (int64_t)(b) > 0); } bool CoDelQueueDisc::CoDelTimeAfterEq (uint32_t a, uint32_t b) { - return ((int)(a) - (int)(b) >= 0); + return ((int64_t)(a) - (int64_t)(b) >= 0); } bool CoDelQueueDisc::CoDelTimeBefore (uint32_t a, uint32_t b) { - return ((int)(a) - (int)(b) < 0); + return ((int64_t)(a) - (int64_t)(b) < 0); } bool CoDelQueueDisc::CoDelTimeBeforeEq (uint32_t a, uint32_t b) { - return ((int)(a) - (int)(b) <= 0); + return ((int64_t)(a) - (int64_t)(b) <= 0); } uint32_t -- GitLab