revised RLC AM NACK code after https://groups.google.com/d/msg/ns-3-users/CEfmMX3IRBw/rrmJiBwBGrYJ
authorNicola Baldo <nbaldo@cttc.es>
Wed, 23 Oct 2013 15:21:13 +0200
changeset 11302 b81502a7dae8
parent 11301 25168b480c1c
child 11303 4d5d717de23e
revised RLC AM NACK code after https://groups.google.com/d/msg/ns-3-users/CEfmMX3IRBw/rrmJiBwBGrYJ
src/lte/model/lte-rlc-am-header.cc
src/lte/model/lte-rlc-am-header.h
src/lte/model/lte-rlc-am.cc
src/lte/test/lte-test-rlc-am-e2e.cc
--- a/src/lte/model/lte-rlc-am-header.cc	Tue Oct 22 13:32:39 2013 +0200
+++ b/src/lte/model/lte-rlc-am-header.cc	Wed Oct 23 15:21:13 2013 +0200
@@ -213,9 +213,28 @@
   m_ackSn = ackSn;
 }
 
+bool
+LteRlcAmHeader::OneMoreNackWouldFitIn (uint16_t bytes)
+{
+  NS_LOG_FUNCTION (this << bytes);
+  NS_ASSERT_MSG (m_dataControlBit == CONTROL_PDU && m_controlPduType == LteRlcAmHeader::STATUS_PDU,
+                 "method allowed only for STATUS PDUs");
+  if (m_nackSnList.size () % 2 == 0)
+    {
+      return (m_headerLength + 1 <= bytes);
+    }
+  else
+    {
+      return (m_headerLength + 2 <= bytes);
+    }
+}
+
 void
 LteRlcAmHeader::PushNack (int nack)
 {
+  NS_LOG_FUNCTION (this << nack);
+  NS_ASSERT_MSG (m_dataControlBit == CONTROL_PDU && m_controlPduType == LteRlcAmHeader::STATUS_PDU,
+                 "method allowed only for STATUS PDUs");
   m_nackSnList.push_back (nack);
 
   if (m_nackSnList.size () % 2 == 0)
@@ -228,10 +247,30 @@
     }
 }
 
+bool
+LteRlcAmHeader::IsNackPresent (SequenceNumber10 nack)
+{
+  NS_LOG_FUNCTION (this);
+  NS_ASSERT_MSG (m_dataControlBit == CONTROL_PDU && m_controlPduType == LteRlcAmHeader::STATUS_PDU,
+                 "method allowed only for STATUS PDUs");
+  for (std::list<int>::iterator nackIt = m_nackSnList.begin ();
+       nackIt != m_nackSnList.end ();
+       ++nackIt)
+    {
+      if ((*nackIt) == nack.GetValue ())
+        {
+          return true;
+        }
+    }
+  return false;  
+}
 
 int
 LteRlcAmHeader::PopNack (void)
 {
+  NS_LOG_FUNCTION (this);
+  NS_ASSERT_MSG (m_dataControlBit == CONTROL_PDU && m_controlPduType == LteRlcAmHeader::STATUS_PDU,
+                 "method allowed only for STATUS PDUs");
   if ( m_nackSnList.empty () )
     {
       return -1;
@@ -244,7 +283,6 @@
 }
 
 
-
 SequenceNumber10
 LteRlcAmHeader::GetAckSn (void) const
 {
--- a/src/lte/model/lte-rlc-am-header.h	Tue Oct 22 13:32:39 2013 +0200
+++ b/src/lte/model/lte-rlc-am-header.h	Wed Oct 23 15:21:13 2013 +0200
@@ -136,10 +136,41 @@
   virtual void Serialize (Buffer::Iterator start) const;
   virtual uint32_t Deserialize (Buffer::Iterator start);
 
-  // Brett - Methods to handle NACKs
+  /** 
+   * 
+   * 
+   * \param bytes max allowed CONTROL PDU size
+   * 
+   * \return true if one more NACK would fit in the CONTROL PDU; false otherwise
+   */
+  bool OneMoreNackWouldFitIn (uint16_t bytes);
+
+  /** 
+   * Add one more NACK to the CONTROL PDU
+   * 
+   * \param nack 
+   */
   void PushNack (int nack);
+
+  /** 
+   * 
+   * 
+   * \param nack SN of the NACK
+   * 
+   * \return true if the NACK is present in the STATUS PDU, false otherwise
+   */
+  bool IsNackPresent (SequenceNumber10 nack);
+
+
+  /** 
+   * Retrieve one NACK from the CONTROL PDU
+   * 
+   * 
+   * \return the SN  >= 0 of the next nack; returns -1 if no NACK is left
+   */
   int PopNack (void);
 
+
 private:
   uint16_t m_headerLength;
   uint8_t  m_dataControlBit;
--- a/src/lte/model/lte-rlc-am.cc	Tue Oct 22 13:32:39 2013 +0200
+++ b/src/lte/model/lte-rlc-am.cc	Wed Oct 23 15:21:13 2013 +0200
@@ -33,12 +33,6 @@
 
 NS_OBJECT_ENSURE_REGISTERED (LteRlcAm);
 
-// Brett
-// Used to keep track of when the RLC AM sliding window wraps back around. This
-// is required for knowning when a small ACK SN also ACKs a larger SN from the
-// before the window wrapped around.
-bool m_windowWrap = false;
-
 LteRlcAm::LteRlcAm ()
 {
   NS_LOG_FUNCTION (this);
@@ -199,44 +193,43 @@
       NS_LOG_LOGIC ("Sending STATUS PDU");
 
       Ptr<Packet> packet = Create<Packet> ();
-      NS_LOG_LOGIC ( "Brett -- About to make AM header for STATUS PDU" );
       LteRlcAmHeader rlcAmHeader;
       rlcAmHeader.SetControlPdu (LteRlcAmHeader::STATUS_PDU);
-//      rlcAmHeader.SetAckSn (m_vrR); // Brett, need to change this
-      rlcAmHeader.SetAckSn (m_vrH); // Brett, is this right?
-
-      //=======================================================================
-      // Brett
-      // Check the reception buffer and find which Sequence Numbers are missing
-      // based on what is being ACKed. All SNs missing from the reception
-      // buffer that come before the SN number that is being ACKed are going to
-      // be NACKed packets.
-       
-      NS_LOG_LOGIC ("Brett - Receiver Side - Check for SNs to NACK");
-      NS_LOG_LOGIC ("Brett - Receiver Side - Check SNs from " << m_vrR.GetValue() << " to " << m_vrH.GetValue());
-      for ( int i = m_vrR.GetValue(); i < m_vrH.GetValue(); i++ ) {
-        // NS_LOG_LOGIC ("Brett - Receiver Side - Checking SN " << i );
-        // Need a different check but similar idea
-      /* struct PduBuffer
+     
+      NS_LOG_LOGIC ("Check for SNs to NACK from " << m_vrR.GetValue() << " to " << m_vrH.GetValue());
+      SequenceNumber10 sn;
+      sn.SetModulusBase (m_vrR);
+      std::map<uint16_t, PduBuffer>::iterator pduIt;
+      for (sn = m_vrR; sn < m_vrH; sn++) 
         {
-          SequenceNumber10  m_seqNumber;
-          std::list < Ptr<Packet> >  m_byteSegments;
-
-          bool      m_pduComplete;
-          uint16_t  m_totalSize;
-          uint16_t  m_currSize;
-        };*/
+          if (!rlcAmHeader.OneMoreNackWouldFitIn (bytes))
+            {
+              NS_LOG_LOGIC ("Can't fit more NACKs in STATUS PDU");
+              break;
+            }          
+          pduIt = m_rxonBuffer.find (sn.GetValue ());
+          if (pduIt == m_rxonBuffer.end () || (!(pduIt->second.m_pduComplete)))
+            {
+              NS_LOG_LOGIC ("adding NACK_SN " << sn.GetValue ());
+              rlcAmHeader.PushNack (sn.GetValue ());              
+            }          
+        }
 
-        int test =  m_rxonBuffer[ i ].m_pduComplete;
-        //NS_LOG_LOGIC("Brett - Is PDU complete: " <<  m_rxonBuffer[ i ].m_pduComplete);
-        if ( test == 0 ) {
-//          NS_LOG_LOGIC("Brett - NACK " << i );
-//          rlcAmHeader.EnqueueNAckSn( SequenceNumber10( i ) );
-          rlcAmHeader.PushNack( SequenceNumber10( i ).GetValue() );
+      // 3GPP TS 36.322 section 6.2.2.1.4 ACK SN
+      // find the  SN of the next not received RLC Data PDU 
+      // which is not reported as missing in the STATUS PDU. 
+      do
+        {
+          sn++;
+          pduIt = m_rxonBuffer.find (sn.GetValue ());
         }
-      }
-      //=======================================================================
+      while (pduIt != m_rxonBuffer.end () && (pduIt->second.m_pduComplete) && (sn < m_vrH));      
 
+      if (sn != m_vrMs)
+        {
+          NS_LOG_WARN ("SN=" << sn << ", VR(MS)=" << m_vrMs);
+        }      
+      rlcAmHeader.SetAckSn (m_vrR); 
 
 
       NS_LOG_LOGIC ("RLC header: " << rlcAmHeader);
@@ -261,8 +254,9 @@
       NS_LOG_LOGIC ("Sending data from Retransmission Buffer");
 
       Ptr<Packet> packet = m_retxBuffer.at (m_vtA.GetValue ()).m_pdu->Copy ();
-
-      if ( packet->GetSize () <= bytes )
+      
+      if (( packet->GetSize () <= bytes )
+          || m_txOpportunityForRetxAlwaysBigEnough)
         {
           LteRlcAmHeader rlcAmHeader;
           packet->PeekHeader (rlcAmHeader);
@@ -299,7 +293,7 @@
 
       NS_LOG_LOGIC ("Sending data from Transmission Buffer");
     }
- /* else if ( m_txedBufferSize > 0 )
+  /* else if ( m_txedBufferSize > 0 )
     {
       NS_LOG_LOGIC ("Sending data from Transmitted Buffer");
 
@@ -308,10 +302,10 @@
 
       uint16_t vta = m_vtA.GetValue ();
       Ptr<Packet> packet = m_txedBuffer.at (vta)->Copy ();
-
-      if ( packet->GetSize () <= bytes )
+      
+      if (( packet->GetSize () <= bytes )
+          || m_txOpportunityForRetxAlwaysBigEnough)
         {
-         // Brett - Move to retransmission buffer
           NS_LOG_INFO ("Move SN = " << vta << " to retxBuffer");
           m_retxBuffer.at (vta).m_pdu = m_txedBuffer.at (vta)->Copy ();
           m_retxBuffer.at (vta).m_retxCount = 1;
@@ -341,8 +335,8 @@
           NS_LOG_LOGIC ("Waiting for bigger TxOpportunity");
           return;
         }
-    }
-*/  else
+    } */
+  else
     {
       NS_LOG_LOGIC ("No data pending");
       return;
@@ -1010,137 +1004,93 @@
       NS_LOG_INFO ("Control AM RLC PDU");
 
       SequenceNumber10 ackSn = rlcAmHeader.GetAckSn ();
-//       SequenceNumber10 seqNumber = m_vtA;
+      SequenceNumber10 sn;
 
       NS_LOG_INFO ("ackSn     = " << ackSn);
       NS_LOG_INFO ("VT(A)     = " << m_vtA);
       NS_LOG_INFO ("VT(S)     = " << m_vtS);
 
+      m_vtA.SetModulusBase (m_vtA);
+      m_vtS.SetModulusBase (m_vtA);
+      ackSn.SetModulusBase (m_vtA);
+      sn.SetModulusBase (m_vtA);
 
+      bool incrementVtA = true; 
+
+      for (sn = m_vtA; sn < ackSn && sn < m_vtS; sn++)
+        {
+          NS_LOG_LOGIC ("sn = " << sn);
 
-      // Check for the first NACK
-      uint16_t nackSN = rlcAmHeader.PopNack ();
-      // Flag to indicate if any NACKs exist in the header. This is used to
-      // determine when VT(A) should stop incrementing as it is the lower
-      // bound of the transmission window and should equal the highest ACKed
-      // SN in sequence (no NACKs previous).
-      bool foundNack = false;
-      // Used to iterate over the transmission window to apply the ACKs and
-      // NACKs from the control PDU.
-      uint16_t loopCount = m_vtA.GetValue ();
+          uint16_t seqNumberValue = sn.GetValue ();
 
-      // Brett - Check to see if the transmission window needs to wrap around.
-      // This window needs to wrap if the current VT(A) (last ACKed PDU) plus
-      // the window size is larger than 1023 and the SN just received is for
-      // a PDU that somwhere in the range of 0 to the top of the window. If
-      // this is the case then set the window wrap flag.
-      if ((m_vtA.GetValue() + m_windowSize > 1023) 
-          && 0 <= ackSn.GetValue() && ackSn.GetValue() < m_vtA.GetValue() + m_windowSize - 1023)
-        {
-          m_windowWrap = true;
-        } 
+          if (m_pollRetransmitTimer.IsRunning () 
+              && (seqNumberValue == m_pollSn.GetValue ()))
+            {
+              m_pollRetransmitTimer.Cancel ();
+            }
+
+          if (rlcAmHeader.IsNackPresent (sn))
+            {
+              NS_LOG_LOGIC ("sn " << sn << " is NACKed");
 
-      // Brett - There is a problem with the transmitting window wrapping back
-      // to the beginning. An ACK is for the next sequence number expected, but
-      // the largest sequence number that can be sent is 1023. We cannot send
-      // 1024 or anything higher than this to ACK an SN of 1023. To resolve I
-      // am adding the OR condition to this while loop. In the situation that
-      // the check above finds that it is time for the window to wrap then the
-      // window wrap flag allows this loop to execute. The loop will ACK all
-      // SNs from VT(A) to SN 1023 before resetting the window wrap flag to
-      // false and resetting the last ACKed SN to 0.
-      NS_LOG_INFO("Brett - Check - Look at transmit window from " << loopCount << " to " << m_vtS.GetValue ());
-      while (( loopCount < ackSn.GetValue() && loopCount < m_vtS.GetValue ())
-              || m_windowWrap)
-       {
-           NS_LOG_INFO("Brett - Current NACK is " << nackSN);
-//           NS_LOG_INFO ("seqNumber = " << seqNumber);
-//           NS_LOG_INFO ("m_txedBuffer( VT(A) ).size = " << m_txedBuffer.size ());
+              incrementVtA = false;
 
-          //uint16_t seqNumberValue = m_vtA.GetValue ();
-          uint16_t seqNumberValue = loopCount;
-          if ( nackSN != seqNumberValue )
-          {
+              if (m_txedBuffer.at (seqNumberValue))
+                {
+                  NS_LOG_INFO ("Move SN = " << seqNumberValue << " to retxBuffer");
+                  m_retxBuffer.at (seqNumberValue).m_pdu = m_txedBuffer.at (seqNumberValue)->Copy ();
+                  m_retxBuffer.at (seqNumberValue).m_retxCount = 0;
+                  m_retxBufferSize += m_retxBuffer.at (seqNumberValue).m_pdu->GetSize ();
 
-            if (m_txedBuffer.at (seqNumberValue))
-              {
-                NS_LOG_INFO ("ACKed SN = " << seqNumberValue << " from txedBuffer");
-//                 NS_LOG_INFO ("m_txedBuffer( " << m_vtA << " )->GetSize = " << m_txedBuffer.at (m_vtA.GetValue ())->GetSize ());
-                m_txedBufferSize -= m_txedBuffer.at (seqNumberValue)->GetSize ();
-                m_txedBuffer.at (seqNumberValue) = 0;
-              }
-
-            if (m_retxBuffer.at (seqNumberValue).m_pdu)
-              {
-                NS_LOG_INFO ("ACKed SN = " << seqNumberValue << " from retxBuffer");
-                m_retxBufferSize -= m_retxBuffer.at (seqNumberValue).m_pdu->GetSize ();
-                m_retxBuffer.at (seqNumberValue).m_pdu = 0;
-                m_retxBuffer.at (seqNumberValue).m_retxCount = 0;
-              }
-
-            if ( !foundNack )
-              {
-                m_vtA++;
-                NS_LOG_INFO ("Brett -- Updated VT(A) = " << m_vtA);
-                m_vtA.SetModulusBase (m_vtA);
-                m_vtS.SetModulusBase (m_vtA);
-                ackSn.SetModulusBase (m_vtA);
-              }
+                  m_txedBufferSize -= m_txedBuffer.at (seqNumberValue)->GetSize ();
+                  m_txedBuffer.at (seqNumberValue) = 0;
+                }
+              else if (m_retxBuffer.at (seqNumberValue).m_pdu)
+                {
+                  m_retxBuffer.at (seqNumberValue).m_retxCount++;
+                  NS_LOG_INFO ("Incr RETX_COUNT for SN = " << seqNumberValue);
+                  if (m_retxBuffer.at (seqNumberValue).m_retxCount >= m_maxRetxThreshold)
+                    {
+                      NS_LOG_INFO ("Max RETX_COUNT for SN = " << seqNumberValue);
+                    }
+                }
+              
             }
           else
             {
-              foundNack = true;
-              if (m_txedBuffer.at (nackSN))
-               {
-                 NS_LOG_INFO ("NACKed SN = " << seqNumberValue << " from txedBuffer");
-                 // Brett - Move to Retransmission Buffer
-                  NS_LOG_INFO ("Move SN = " << nackSN << " to retxBuffer");
-                  m_retxBuffer.at (nackSN).m_pdu = m_txedBuffer.at (nackSN)->Copy ();
-                  m_retxBuffer.at (nackSN).m_retxCount = 0;
-                  m_retxBufferSize += m_retxBuffer.at (nackSN).m_pdu->GetSize ();
+              NS_LOG_LOGIC ("sn " << sn << " is ACKed");
 
-                  m_txedBufferSize -= m_txedBuffer.at (nackSN)->GetSize ();
-                  m_txedBuffer.at (nackSN) = 0;
-               }
-              else if (m_retxBuffer.at (nackSN).m_pdu)
-               {
-                 NS_LOG_INFO ("NACKed SN = " << seqNumberValue << " from retxBuffer");
-                 m_retxBuffer.at (nackSN).m_retxCount++;
-                 NS_LOG_INFO ("Incr RETX_COUNT for SN = " << nackSN);
-                 if (m_retxBuffer.at (nackSN).m_retxCount >= m_maxRetxThreshold)
-                   {
-                     NS_LOG_INFO ("Max RETX_COUNT for SN = " << nackSN);
-                   }
-               }
+              if (m_txedBuffer.at (seqNumberValue))
+                {
+                  NS_LOG_INFO ("ACKed SN = " << seqNumberValue << " from txedBuffer");
+                  //               NS_LOG_INFO ("m_txedBuffer( " << m_vtA << " )->GetSize = " << m_txedBuffer.at (m_vtA.GetValue ())->GetSize ());
+                  m_txedBufferSize -= m_txedBuffer.at (seqNumberValue)->GetSize ();
+                  m_txedBuffer.at (seqNumberValue) = 0;
+                }
+
+              if (m_retxBuffer.at (seqNumberValue).m_pdu)
+                {
+                  NS_LOG_INFO ("ACKed SN = " << seqNumberValue << " from retxBuffer");
+                  m_retxBufferSize -= m_retxBuffer.at (seqNumberValue).m_pdu->GetSize ();
+                  m_retxBuffer.at (seqNumberValue).m_pdu = 0;
+                  m_retxBuffer.at (seqNumberValue).m_retxCount = 0;
+                }
 
             }
-            // Check to see if it is time to find the next NACK
-            if ( seqNumberValue == nackSN )
-              {
-                nackSN = rlcAmHeader.PopNack ();
-              }
-
 
-            // If m_vtA has an SN of 1023 and the window wrap flag is true then
-            // it is time to reset m_vtA to 0 an reset the window wrap flag.
-            if ( m_vtA.GetValue() == 1023 && m_windowWrap )
-              {
-                loopCount = 0;
-                m_vtA = 0;
-                m_windowWrap = false;
-             
-                // Wrap around with the transmission window.
-                //nackSN = rlcAmHeader.PopNack ();
-                //NS_LOG_INFO( "Brett - Tx window wrap, new NACK is " << nackSN );
-              }
-            else
-              {
-                loopCount++;               
-              }
-        }
+          if (incrementVtA)
+            {
+              NS_LOG_INFO ("New VT(A) = " << m_vtA);
+              m_vtA.SetModulusBase (m_vtA);
+              m_vtS.SetModulusBase (m_vtA);
+              ackSn.SetModulusBase (m_vtA);
+              sn.SetModulusBase (m_vtA);
+            }
+          
+        } // loop over SN : VT(A) <= SN < ACK SN
+      
+      return;
 
-      NS_LOG_INFO ("New VT(A) = " << m_vtA);
-      return;
     }
   else
     {
@@ -1669,17 +1619,16 @@
     }
 
   // Retransmission Queue HOL time
-  Time retxQueueHolDelay (0);
+  Time retxQueueHolDelay;
   RlcTag retxQueueHolTimeTag;
   if ( m_retxBufferSize > 0 )
     {
       m_retxBuffer.at (m_vtA.GetValue ()).m_pdu->PeekPacketTag (retxQueueHolTimeTag);
       retxQueueHolDelay = now - retxQueueHolTimeTag.GetSenderTimestamp ();
     }
-  else if ( m_txedBufferSize > 0 )
-    {
-      m_txedBuffer.at (m_vtA.GetValue ())->PeekPacketTag (retxQueueHolTimeTag);
-      retxQueueHolDelay = now - retxQueueHolTimeTag.GetSenderTimestamp ();
+  else 
+    {      
+      retxQueueHolDelay = Seconds (0);
     }
 
   LteMacSapProvider::ReportBufferStatusParameters r;
--- a/src/lte/test/lte-test-rlc-am-e2e.cc	Tue Oct 22 13:32:39 2013 +0200
+++ b/src/lte/test/lte-test-rlc-am-e2e.cc	Wed Oct 23 15:21:13 2013 +0200
@@ -55,6 +55,8 @@
 {
   // NS_LOG_INFO ("Creating LteRlcAmE2eTestSuite");
 
+  AddTestCase (new LteRlcAmE2eTestCase ("the one that fails", 1111, 0.10), TestCase::QUICK);
+  
   double losses[] = {0.0, 0.10, 0.25, 0.50, 0.75, 0.90, 0.95};
   uint32_t seeds[] = {1111, 2222, 3333, 4444, 5555, 6666, 7777, 8888, 9999, 10101};