changes suggested in original code review, and aditional improvements made during 2014 GSoC project
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*
|
||||
* Codel, the COntrolled DELay Queueing discipline
|
||||
* Based on ns2 simulation code presented by Kathie Nichols
|
||||
*
|
||||
@@ -35,25 +35,15 @@ NS_LOG_COMPONENT_DEFINE ("CoDelQueue");
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
#define BITS_PER_LONG (8 * sizeof (unsigned long))
|
||||
|
||||
/* borrowed from the linux kernel */
|
||||
#define do_div(n,base) \
|
||||
({ \
|
||||
int __res; \
|
||||
__res = ((unsigned long)n) % (unsigned int)base; \
|
||||
n = ((unsigned long)n) / (unsigned int)base; \
|
||||
__res; \
|
||||
})
|
||||
|
||||
static inline uint32_t reciprocal_divide(uint32_t A, uint32_t R)
|
||||
static inline uint32_t ReciprocalDivide (uint32_t A, uint32_t R)
|
||||
{
|
||||
return (uint32_t)(((uint64_t)A * R) >> 32);
|
||||
return (uint32_t)(((uint64_t)A * R) >> 32);
|
||||
}
|
||||
|
||||
/* end kernel borrowings */
|
||||
|
||||
static codel_time_t codel_get_time(void)
|
||||
static uint32_t CoDelGetTime (void)
|
||||
{
|
||||
Time time = Simulator::Now ();
|
||||
uint64_t ns = time.GetNanoSeconds ();
|
||||
@@ -61,21 +51,6 @@ static codel_time_t codel_get_time(void)
|
||||
return ns >> CODEL_SHIFT;
|
||||
}
|
||||
|
||||
#define codel_time_after(a, b) ((int)(a) - (int)(b) > 0)
|
||||
#define codel_time_after_eq(a, b) ((int)(a) - (int)(b) >= 0)
|
||||
#define codel_time_before(a, b) ((int)(a) - (int)(b) < 0)
|
||||
#define codel_time_before_eq(a, b) ((int)(a) - (int)(b) <= 0)
|
||||
|
||||
#define NSEC_PER_MSEC 1000000
|
||||
#define NSEC_PER_USEC 1000
|
||||
#define MS2TIME(a) ((a * NSEC_PER_MSEC) >> CODEL_SHIFT)
|
||||
#define US2TIME(a) ((a * NSEC_PER_USEC) >> CODEL_SHIFT)
|
||||
#define NS2TIME(a) ((a) >> CODEL_SHIFT)
|
||||
#define TIME2CODEL(a) NS2TIME(a.GetNanoSeconds())
|
||||
|
||||
#define DEFAULT_CODEL_LIMIT 1000
|
||||
|
||||
|
||||
class CoDelTimestampTag : public Tag
|
||||
{
|
||||
public:
|
||||
@@ -101,7 +76,7 @@ CoDelTimestampTag::CoDelTimestampTag ()
|
||||
TypeId
|
||||
CoDelTimestampTag::GetTypeId (void)
|
||||
{
|
||||
static TypeId tid = TypeId ("anon::CoDelTimestampTag")
|
||||
static TypeId tid = TypeId ("ns3::CoDelTimestampTag")
|
||||
.SetParent<Tag> ()
|
||||
.AddConstructor<CoDelTimestampTag> ()
|
||||
.AddAttribute ("CreationTime",
|
||||
@@ -112,6 +87,7 @@ CoDelTimestampTag::GetTypeId (void)
|
||||
;
|
||||
return tid;
|
||||
}
|
||||
|
||||
TypeId
|
||||
CoDelTimestampTag::GetInstanceTypeId (void) const
|
||||
{
|
||||
@@ -146,141 +122,154 @@ CoDelTimestampTag::GetTxTime (void) const
|
||||
|
||||
NS_OBJECT_ENSURE_REGISTERED (CoDelQueue);
|
||||
|
||||
TypeId CoDelQueue::GetTypeId (void)
|
||||
TypeId CoDelQueue::GetTypeId (void)
|
||||
{
|
||||
static TypeId tid = TypeId ("ns3::CoDelQueue")
|
||||
.SetParent<Queue> ()
|
||||
.AddConstructor<CoDelQueue> ()
|
||||
.AddAttribute ("Mode",
|
||||
.AddAttribute ("Mode",
|
||||
"Whether to use Bytes (see MaxBytes) or Packets (see MaxPackets) as the maximum queue size metric.",
|
||||
EnumValue (BYTES),
|
||||
EnumValue (QUEUE_MODE_BYTES),
|
||||
MakeEnumAccessor (&CoDelQueue::SetMode),
|
||||
MakeEnumChecker (BYTES, "Bytes",
|
||||
PACKETS, "Packets"))
|
||||
.AddAttribute ("MaxPackets",
|
||||
MakeEnumChecker (QUEUE_MODE_BYTES, "QUEUE_MODE_BYTES",
|
||||
QUEUE_MODE_PACKETS, "QUEUE_MODE_PACKETS"))
|
||||
.AddAttribute ("MaxPackets",
|
||||
"The maximum number of packets accepted by this CoDelQueue.",
|
||||
UintegerValue (DEFAULT_CODEL_LIMIT),
|
||||
MakeUintegerAccessor (&CoDelQueue::m_maxPackets),
|
||||
MakeUintegerChecker<uint32_t> ())
|
||||
.AddAttribute ("MaxBytes",
|
||||
.AddAttribute ("MaxBytes",
|
||||
"The maximum number of bytes accepted by this CoDelQueue.",
|
||||
UintegerValue (1500*DEFAULT_CODEL_LIMIT),
|
||||
UintegerValue (1500 * DEFAULT_CODEL_LIMIT),
|
||||
MakeUintegerAccessor (&CoDelQueue::m_maxBytes),
|
||||
MakeUintegerChecker<uint32_t> ())
|
||||
.AddAttribute ("MinBytes",
|
||||
.AddAttribute ("MinBytes",
|
||||
"The CoDel algorithm minbytes parameter.",
|
||||
UintegerValue (1500),
|
||||
MakeUintegerAccessor (&CoDelQueue::m_minbytes),
|
||||
MakeUintegerAccessor (&CoDelQueue::m_minBytes),
|
||||
MakeUintegerChecker<uint32_t> ())
|
||||
.AddAttribute ("Interval",
|
||||
"The CoDel algorithm interval",
|
||||
StringValue ("100ms"),
|
||||
MakeTimeAccessor (&CoDelQueue::m_Interval),
|
||||
MakeTimeAccessor (&CoDelQueue::m_interval),
|
||||
MakeTimeChecker ())
|
||||
.AddAttribute ("Target",
|
||||
"The CoDel algorithm target queue delay",
|
||||
StringValue ("5ms"),
|
||||
MakeTimeAccessor (&CoDelQueue::m_Target),
|
||||
MakeTimeAccessor (&CoDelQueue::m_target),
|
||||
MakeTimeChecker ())
|
||||
.AddTraceSource("count",
|
||||
"CoDel count",
|
||||
MakeTraceSourceAccessor(&CoDelQueue::m_count))
|
||||
.AddTraceSource("drop_count",
|
||||
"CoDel drop count",
|
||||
MakeTraceSourceAccessor(&CoDelQueue::m_drop_count))
|
||||
.AddTraceSource("last_count",
|
||||
"CoDel lastcount",
|
||||
MakeTraceSourceAccessor(&CoDelQueue::m_lastcount))
|
||||
// .AddTraceSource("bytesInQueue",
|
||||
// "Number of bytes in the queue",
|
||||
// MakeTraceSourceAccessor(&CoDelQueue::m_bytesInQueue))
|
||||
.AddTraceSource ("Count",
|
||||
"CoDel count",
|
||||
MakeTraceSourceAccessor (&CoDelQueue::m_count))
|
||||
.AddTraceSource ("DropCount",
|
||||
"CoDel drop count",
|
||||
MakeTraceSourceAccessor (&CoDelQueue::m_dropCount))
|
||||
.AddTraceSource ("LastCount",
|
||||
"CoDel lastcount",
|
||||
MakeTraceSourceAccessor (&CoDelQueue::m_lastCount))
|
||||
.AddTraceSource ("DropState",
|
||||
"Dropping state",
|
||||
MakeTraceSourceAccessor (&CoDelQueue::m_dropping))
|
||||
.AddTraceSource ("BytesInQueue",
|
||||
"Number of bytes in the queue",
|
||||
MakeTraceSourceAccessor (&CoDelQueue::m_bytesInQueue))
|
||||
.AddTraceSource ("Sojourn",
|
||||
"Time in the queue",
|
||||
MakeTraceSourceAccessor (&CoDelQueue::m_sojourn))
|
||||
.AddTraceSource ("DropNext",
|
||||
"Time until next packet drop",
|
||||
MakeTraceSourceAccessor (&CoDelQueue::m_dropNext))
|
||||
;
|
||||
|
||||
return tid;
|
||||
}
|
||||
|
||||
CoDelQueue::CoDelQueue () :
|
||||
Queue (),
|
||||
m_packets (),
|
||||
m_maxBytes(),
|
||||
m_bytesInQueue(0),
|
||||
backlog(&m_bytesInQueue),
|
||||
m_count(0),
|
||||
m_drop_count(0),
|
||||
m_lastcount(0),
|
||||
m_dropping(false),
|
||||
m_rec_inv_sqrt(~0U >> REC_INV_SQRT_SHIFT),
|
||||
m_first_above_time(0),
|
||||
m_drop_next(0),
|
||||
m_state1(0),
|
||||
m_state2(0),
|
||||
m_state3(0),
|
||||
m_states(0),
|
||||
m_drop_overlimit(0)
|
||||
CoDelQueue::CoDelQueue ()
|
||||
: Queue (),
|
||||
m_packets (),
|
||||
m_maxBytes (),
|
||||
m_bytesInQueue (0),
|
||||
m_count (0),
|
||||
m_dropCount (0),
|
||||
m_lastCount (0),
|
||||
m_dropping (false),
|
||||
m_recInvSqrt (~0U >> REC_INV_SQRT_SHIFT),
|
||||
m_firstAboveTime (0),
|
||||
m_dropNext (0),
|
||||
m_state1 (0),
|
||||
m_state2 (0),
|
||||
m_state3 (0),
|
||||
m_states (0),
|
||||
m_dropOverLimit (0),
|
||||
m_sojourn (0)
|
||||
{
|
||||
NS_LOG_FUNCTION_NOARGS ();
|
||||
NS_LOG_FUNCTION (this);
|
||||
}
|
||||
|
||||
CoDelQueue::~CoDelQueue ()
|
||||
{
|
||||
NS_LOG_FUNCTION_NOARGS ();
|
||||
NS_LOG_FUNCTION (this);
|
||||
}
|
||||
|
||||
void
|
||||
CoDelQueue::NewtonStep(void)
|
||||
CoDelQueue::NewtonStep (void)
|
||||
{
|
||||
uint32_t invsqrt = ((uint32_t) m_rec_inv_sqrt) << REC_INV_SQRT_SHIFT;
|
||||
uint32_t invsqrt2 = ((uint64_t) invsqrt*invsqrt) >> 32;
|
||||
uint64_t val = (3ll<<32) - ((uint64_t) m_count * invsqrt2);
|
||||
NS_LOG_FUNCTION (this);
|
||||
uint32_t invsqrt = ((uint32_t) m_recInvSqrt) << REC_INV_SQRT_SHIFT;
|
||||
uint32_t invsqrt2 = ((uint64_t) invsqrt * invsqrt) >> 32;
|
||||
uint64_t val = (3ll << 32) - ((uint64_t) m_count * invsqrt2);
|
||||
|
||||
val >>= 2; /* avoid overflow */
|
||||
val = (val * invsqrt) >> (32-2+1);
|
||||
m_rec_inv_sqrt = val >> REC_INV_SQRT_SHIFT;
|
||||
val = (val * invsqrt) >> (32 - 2 + 1);
|
||||
m_recInvSqrt = val >> REC_INV_SQRT_SHIFT;
|
||||
}
|
||||
|
||||
codel_time_t
|
||||
CoDelQueue::ControlLaw(codel_time_t t)
|
||||
uint32_t
|
||||
CoDelQueue::ControlLaw (uint32_t t)
|
||||
{
|
||||
return t + reciprocal_divide(TIME2CODEL(m_Interval), m_rec_inv_sqrt << REC_INV_SQRT_SHIFT);
|
||||
NS_LOG_FUNCTION (this);
|
||||
return t + ReciprocalDivide (Time2CoDel (m_interval), m_recInvSqrt << REC_INV_SQRT_SHIFT);
|
||||
}
|
||||
|
||||
void
|
||||
CoDelQueue::SetMode (enum Mode mode)
|
||||
CoDelQueue::SetMode (CoDelQueue::QueueMode mode)
|
||||
{
|
||||
NS_LOG_FUNCTION (mode);
|
||||
m_mode = mode;
|
||||
}
|
||||
|
||||
CoDelQueue::Mode
|
||||
CoDelQueue::QueueMode
|
||||
CoDelQueue::GetMode (void)
|
||||
{
|
||||
NS_LOG_FUNCTION_NOARGS ();
|
||||
NS_LOG_FUNCTION (this);
|
||||
return m_mode;
|
||||
}
|
||||
|
||||
bool
|
||||
bool
|
||||
CoDelQueue::DoEnqueue (Ptr<Packet> p)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << p);
|
||||
|
||||
if (m_mode == PACKETS && (m_packets.size () >= m_maxPackets))
|
||||
if (m_mode == QUEUE_MODE_PACKETS && (m_packets.size () + 1 > m_maxPackets))
|
||||
{
|
||||
NS_LOG_LOGIC ("Queue full (at max packets) -- droppping pkt");
|
||||
Drop (p);
|
||||
++m_drop_overlimit;
|
||||
++m_dropOverLimit;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_mode == BYTES && (m_bytesInQueue + p->GetSize () >= m_maxBytes))
|
||||
if (m_mode == QUEUE_MODE_BYTES && (m_bytesInQueue + p->GetSize () > m_maxBytes))
|
||||
{
|
||||
NS_LOG_LOGIC ("Queue full (packet would exceed max bytes) -- droppping pkt");
|
||||
Drop (p);
|
||||
++m_drop_overlimit;
|
||||
++m_dropOverLimit;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Tag packet with current time for DoDequeue() to compute sojourn time
|
||||
CoDelTimestampTag tag;
|
||||
p->AddByteTag (tag);
|
||||
p->AddPacketTag (tag);
|
||||
|
||||
m_bytesInQueue += p->GetSize ();
|
||||
m_packets.push (p);
|
||||
|
||||
@@ -291,39 +280,45 @@ CoDelQueue::DoEnqueue (Ptr<Packet> p)
|
||||
}
|
||||
|
||||
bool
|
||||
CoDelQueue::ShouldDrop(Ptr<Packet> p, codel_time_t now)
|
||||
CoDelQueue::OkToDrop (Ptr<Packet> p, uint32_t now)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
CoDelTimestampTag tag;
|
||||
bool drop;
|
||||
bool okToDrop;
|
||||
p->FindFirstMatchingByteTag (tag);
|
||||
bool found = p->RemovePacketTag (tag);
|
||||
NS_ASSERT_MSG (found, "found a packet without an input timestamp tag");
|
||||
NS_UNUSED (found); //silence compiler warning
|
||||
Time delta = Simulator::Now () - tag.GetTxTime ();
|
||||
NS_LOG_INFO ("Sojourn time "<<delta.GetSeconds ());
|
||||
codel_time_t sojourn_time = TIME2CODEL(delta);
|
||||
|
||||
if (codel_time_before(sojourn_time, TIME2CODEL(m_Target)) ||
|
||||
*backlog < m_minbytes)
|
||||
NS_LOG_INFO ("Sojourn time " << delta.GetSeconds ());
|
||||
m_sojourn = delta;
|
||||
uint32_t sojournTime = Time2CoDel (delta);
|
||||
|
||||
if (CoDelTimeBefore (sojournTime, Time2CoDel (m_target))
|
||||
|| m_bytesInQueue < m_minBytes)
|
||||
{
|
||||
/* went below so we'll stay below for at least q->interval */
|
||||
m_first_above_time = 0;
|
||||
// went below so we'll stay below for at least q->interval
|
||||
NS_LOG_LOGIC ("Sojourn time is below target or number of bytes in queue is less than minBytes; packet should not be dropped");
|
||||
m_firstAboveTime = 0;
|
||||
return false;
|
||||
}
|
||||
drop = false;
|
||||
if (m_first_above_time == 0)
|
||||
okToDrop = false;
|
||||
if (m_firstAboveTime == 0)
|
||||
{
|
||||
/* just went above from below. If we stay above
|
||||
* for at least q->interval we'll say it's ok to drop
|
||||
*/
|
||||
m_first_above_time = now + TIME2CODEL(m_Interval);
|
||||
}
|
||||
else
|
||||
if (codel_time_after(now, m_first_above_time))
|
||||
{
|
||||
drop = true;
|
||||
++m_state1;
|
||||
}
|
||||
if (!drop)
|
||||
Drop (p);
|
||||
return drop;
|
||||
NS_LOG_LOGIC ("Sojourn time has just gone above target from below, need to stay above for at least q->interval before packet can be dropped. ");
|
||||
m_firstAboveTime = now + Time2CoDel (m_interval);
|
||||
}
|
||||
else
|
||||
if (CoDelTimeAfter (now, m_firstAboveTime))
|
||||
{
|
||||
NS_LOG_LOGIC ("Sojourn time has been above target for at least q->interval; it's OK to (possibly) drop packet.");
|
||||
okToDrop = true;
|
||||
++m_state1;
|
||||
}
|
||||
return okToDrop;
|
||||
}
|
||||
|
||||
Ptr<Packet>
|
||||
@@ -333,46 +328,51 @@ CoDelQueue::DoDequeue (void)
|
||||
|
||||
if (m_packets.empty ())
|
||||
{
|
||||
// Leave dropping state when queue is empty
|
||||
m_dropping = false;
|
||||
m_first_above_time = 0;
|
||||
m_firstAboveTime = 0;
|
||||
NS_LOG_LOGIC ("Queue empty");
|
||||
return 0;
|
||||
}
|
||||
codel_time_t now = codel_get_time();
|
||||
uint32_t now = CoDelGetTime ();
|
||||
Ptr<Packet> p = m_packets.front ();
|
||||
m_packets.pop ();
|
||||
m_bytesInQueue -= p->GetSize ();
|
||||
|
||||
NS_LOG_LOGIC ("Popped " << p);
|
||||
NS_LOG_LOGIC ("Number packets " << m_packets.size ());
|
||||
NS_LOG_LOGIC ("Number bytes " << m_bytesInQueue);
|
||||
NS_LOG_LOGIC ("Number packets remaining " << m_packets.size ());
|
||||
NS_LOG_LOGIC ("Number bytes remaining " << m_bytesInQueue);
|
||||
|
||||
// Determine if p should be dropped
|
||||
bool okToDrop = OkToDrop (p, now);
|
||||
|
||||
bool drop = ShouldDrop(p, now);
|
||||
if (m_dropping)
|
||||
{
|
||||
if (!drop)
|
||||
{ // In the dropping state (sojourn time has gone above target and hasn't come down yet)
|
||||
// Check if we can leave the dropping state or next drop should occur
|
||||
NS_LOG_LOGIC ("In dropping state, check if it's OK to leave or next drop should occur");
|
||||
if (!okToDrop)
|
||||
{
|
||||
/* sojourn time below target - leave dropping state */
|
||||
/* sojourn time fell below target - leave dropping state */
|
||||
NS_LOG_LOGIC ("Sojourn time goes below target, it's OK to leave dropping state.");
|
||||
m_dropping = false;
|
||||
}
|
||||
else
|
||||
if (codel_time_after_eq(now, m_drop_next))
|
||||
{
|
||||
m_state2++;
|
||||
/* 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 backlog might result in drop rates so high
|
||||
* that the next drop should happen now,
|
||||
* hence the while loop.
|
||||
*/
|
||||
while (m_dropping &&
|
||||
codel_time_after_eq(now, m_drop_next)) {
|
||||
Drop(p);
|
||||
++m_drop_count;
|
||||
if (CoDelTimeAfterEq (now, m_dropNext))
|
||||
{
|
||||
m_state2++;
|
||||
while (m_dropping && CoDelTimeAfterEq (now, m_dropNext))
|
||||
{
|
||||
// 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.
|
||||
NS_LOG_LOGIC ("Sojourn time is still above target and it's time for next drop; dropping " << p);
|
||||
Drop (p);
|
||||
++m_dropCount;
|
||||
++m_count;
|
||||
NewtonStep();
|
||||
NewtonStep ();
|
||||
if (m_packets.empty ())
|
||||
{
|
||||
m_dropping = false;
|
||||
@@ -385,58 +385,79 @@ CoDelQueue::DoDequeue (void)
|
||||
m_bytesInQueue -= p->GetSize ();
|
||||
|
||||
NS_LOG_LOGIC ("Popped " << p);
|
||||
NS_LOG_LOGIC ("Number packets " << m_packets.size ());
|
||||
NS_LOG_LOGIC ("Number bytes " << m_bytesInQueue);
|
||||
NS_LOG_LOGIC ("Number packets remaining " << m_packets.size ());
|
||||
NS_LOG_LOGIC ("Number bytes remaining " << m_bytesInQueue);
|
||||
|
||||
if (!ShouldDrop(p, now))
|
||||
if (!OkToDrop (p, now))
|
||||
{
|
||||
/* leave dropping state */
|
||||
NS_LOG_LOGIC ("Leaving dropping state");
|
||||
m_dropping = false;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
/* and schedule the next drop */
|
||||
m_drop_next = ControlLaw(m_drop_next);
|
||||
/* schedule the next drop */
|
||||
NS_LOG_LOGIC ("Running ControlLaw for input m_dropNext: " << (double)m_dropNext / 1000000);
|
||||
m_dropNext = ControlLaw (m_dropNext);
|
||||
NS_LOG_LOGIC ("Scheduled next drop at " << (double)m_dropNext / 1000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if (drop &&
|
||||
(codel_time_before(now - m_drop_next,
|
||||
TIME2CODEL(m_Interval)) ||
|
||||
codel_time_after_eq(now - m_first_above_time,
|
||||
TIME2CODEL(m_Interval))))
|
||||
{
|
||||
++m_drop_count;
|
||||
else
|
||||
{
|
||||
// Not in the dropping state
|
||||
// Decide if we have to enter the dropping state and drop the first packet
|
||||
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 " << p << " and entering the dropping state");
|
||||
++m_dropCount;
|
||||
Drop (p);
|
||||
if (m_packets.empty ())
|
||||
{
|
||||
m_dropping = false;
|
||||
okToDrop = false;
|
||||
NS_LOG_LOGIC ("Queue empty");
|
||||
++m_states;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = m_packets.front ();
|
||||
m_packets.pop ();
|
||||
m_bytesInQueue -= p->GetSize ();
|
||||
|
||||
NS_LOG_LOGIC ("Popped " << p);
|
||||
NS_LOG_LOGIC ("Number packets " << m_packets.size ());
|
||||
NS_LOG_LOGIC ("Number bytes " << m_bytesInQueue);
|
||||
NS_LOG_LOGIC ("Popped " << p);
|
||||
NS_LOG_LOGIC ("Number packets remaining " << m_packets.size ());
|
||||
NS_LOG_LOGIC ("Number bytes remaining " << m_bytesInQueue);
|
||||
|
||||
drop = ShouldDrop(p, now);
|
||||
m_dropping = true;
|
||||
++m_state3;
|
||||
/*
|
||||
* if min went above target close to when we last went below it
|
||||
* assume that the drop rate that controlled the queue on the
|
||||
* last cycle is a good starting point to control it now.
|
||||
*/
|
||||
int delta = m_count - m_lastcount;
|
||||
if (delta > 1 && codel_time_after(now - m_drop_next, TIME2CODEL(m_Interval)))
|
||||
{
|
||||
m_count = delta;
|
||||
NewtonStep();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_count = 1;
|
||||
m_rec_inv_sqrt = ~0U >> REC_INV_SQRT_SHIFT;
|
||||
}
|
||||
m_lastcount = m_count;
|
||||
m_drop_next = ControlLaw(now);
|
||||
p = NULL;
|
||||
}
|
||||
okToDrop = OkToDrop (p, now);
|
||||
m_dropping = true;
|
||||
}
|
||||
++m_state3;
|
||||
/*
|
||||
* if min went above target close to when we last went below it
|
||||
* assume that the drop rate that controlled the queue on the
|
||||
* last cycle is a good starting point to control it now.
|
||||
*/
|
||||
int delta = m_count - m_lastCount;
|
||||
if (delta > 1 && CoDelTimeBefore (now - m_dropNext, 16 * Time2CoDel (m_interval)))
|
||||
{
|
||||
m_count = delta;
|
||||
NewtonStep ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_count = 1;
|
||||
m_recInvSqrt = ~0U >> REC_INV_SQRT_SHIFT;
|
||||
}
|
||||
m_lastCount = m_count;
|
||||
NS_LOG_LOGIC ("Running ControlLaw for input now: " << (double)now);
|
||||
m_dropNext = ControlLaw (now);
|
||||
NS_LOG_LOGIC ("Scheduled next drop at " << (double)m_dropNext / 1000000 << " now " << (double)now / 1000000);
|
||||
}
|
||||
}
|
||||
++m_states;
|
||||
return p;
|
||||
}
|
||||
@@ -444,12 +465,12 @@ CoDelQueue::DoDequeue (void)
|
||||
uint32_t
|
||||
CoDelQueue::GetQueueSize (void)
|
||||
{
|
||||
NS_LOG_FUNCTION_NOARGS ();
|
||||
if (GetMode () == BYTES)
|
||||
NS_LOG_FUNCTION (this);
|
||||
if (GetMode () == QUEUE_MODE_BYTES)
|
||||
{
|
||||
return m_bytesInQueue;
|
||||
}
|
||||
else if (GetMode () == PACKETS)
|
||||
else if (GetMode () == QUEUE_MODE_PACKETS)
|
||||
{
|
||||
return m_packets.size ();
|
||||
}
|
||||
@@ -459,6 +480,36 @@ CoDelQueue::GetQueueSize (void)
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CoDelQueue::GetDropOverLimit (void)
|
||||
{
|
||||
return m_dropOverLimit;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CoDelQueue::GetDropCount (void)
|
||||
{
|
||||
return m_dropCount;
|
||||
}
|
||||
|
||||
Time
|
||||
CoDelQueue::GetTarget (void)
|
||||
{
|
||||
return m_target;
|
||||
}
|
||||
|
||||
Time
|
||||
CoDelQueue::GetInterval (void)
|
||||
{
|
||||
return m_interval;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CoDelQueue::GetDropNext (void)
|
||||
{
|
||||
return m_dropNext;
|
||||
}
|
||||
|
||||
Ptr<const Packet>
|
||||
CoDelQueue::DoPeek (void) const
|
||||
{
|
||||
@@ -478,5 +529,36 @@ CoDelQueue::DoPeek (void) const
|
||||
return p;
|
||||
}
|
||||
|
||||
bool
|
||||
CoDelQueue::CoDelTimeAfter (uint32_t a, uint32_t b)
|
||||
{
|
||||
return ((int)(a) - (int)(b) > 0);
|
||||
}
|
||||
|
||||
bool
|
||||
CoDelQueue::CoDelTimeAfterEq (uint32_t a, uint32_t b)
|
||||
{
|
||||
return ((int)(a) - (int)(b) >= 0);
|
||||
}
|
||||
|
||||
bool
|
||||
CoDelQueue::CoDelTimeBefore (uint32_t a, uint32_t b)
|
||||
{
|
||||
return ((int)(a) - (int)(b) < 0);
|
||||
}
|
||||
|
||||
bool
|
||||
CoDelQueue::CoDelTimeBeforeEq (uint32_t a, uint32_t b)
|
||||
{
|
||||
return ((int)(a) - (int)(b) <= 0);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CoDelQueue::Time2CoDel (Time t)
|
||||
{
|
||||
return (t.GetNanoSeconds () >> CODEL_SHIFT);
|
||||
}
|
||||
|
||||
|
||||
} // namespace ns3
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*
|
||||
* Codel, the COntrolled DELay Queueing discipline
|
||||
* Based on ns2 simulation code presented by Kathie Nichols
|
||||
*
|
||||
@@ -37,13 +37,15 @@
|
||||
#include "ns3/traced-value.h"
|
||||
#include "ns3/trace-source-accessor.h"
|
||||
|
||||
class CoDelQueueNewtonStepTest; // Forward declaration for unit test
|
||||
class CoDelQueueControlLawTest; // Forward declaration for unit test
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
typedef uint32_t codel_time_t;
|
||||
typedef uint16_t rec_inv_sqrt_t;
|
||||
static const int CODEL_SHIFT = 10;
|
||||
static const int DEFAULT_CODEL_LIMIT = 1000;
|
||||
|
||||
#define CODEL_SHIFT 10
|
||||
#define REC_INV_SQRT_BITS (8*sizeof(rec_inv_sqrt_t))
|
||||
#define REC_INV_SQRT_BITS (8 * sizeof(uint16_t))
|
||||
#define REC_INV_SQRT_SHIFT (32 - REC_INV_SQRT_BITS)
|
||||
|
||||
class TraceContainer;
|
||||
@@ -51,78 +53,164 @@ class TraceContainer;
|
||||
/**
|
||||
* \ingroup queue
|
||||
*
|
||||
* \brief A FIFO packet queue that drops tail-end packets on overflow
|
||||
* \brief A CoDel packet queue
|
||||
*/
|
||||
class CoDelQueue : public Queue {
|
||||
public:
|
||||
friend class Fq_CoDelQueue;
|
||||
|
||||
class CoDelQueue : public Queue
|
||||
{
|
||||
public:
|
||||
static TypeId GetTypeId (void);
|
||||
|
||||
/**
|
||||
* \brief CoDelQueue Constructor
|
||||
*
|
||||
* Creates a codel queue with a maximum size of 100 packets by default
|
||||
* Creates a CoDel queue
|
||||
*/
|
||||
CoDelQueue ();
|
||||
|
||||
virtual ~CoDelQueue();
|
||||
virtual ~CoDelQueue ();
|
||||
|
||||
/**
|
||||
* Enumeration of the modes supported in the class.
|
||||
*
|
||||
*/
|
||||
enum Mode {
|
||||
ILLEGAL, /**< Mode not set */
|
||||
PACKETS, /**< Use number of packets for maximum queue size */
|
||||
BYTES, /**< Use number of bytes for maximum queue size */
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the operating mode of this device.
|
||||
* \brief Set the operating mode of this device.
|
||||
*
|
||||
* \param mode The operating mode of this device.
|
||||
*
|
||||
*/
|
||||
void SetMode (CoDelQueue::Mode mode);
|
||||
void SetMode (CoDelQueue::QueueMode mode);
|
||||
|
||||
/**
|
||||
* Get the encapsulation mode of this device.
|
||||
* \brief Get the encapsulation mode of this device.
|
||||
*
|
||||
* \returns The encapsulation mode of this device.
|
||||
*/
|
||||
CoDelQueue::Mode GetMode (void);
|
||||
CoDelQueue::QueueMode GetMode (void);
|
||||
|
||||
/**
|
||||
* \brief Get the current value of the queue in bytes or packets.
|
||||
*
|
||||
* \returns The queue size in bytes or packets.
|
||||
*/
|
||||
uint32_t GetQueueSize (void);
|
||||
|
||||
private:
|
||||
virtual bool DoEnqueue (Ptr<Packet> p);
|
||||
virtual Ptr<Packet> DoDequeue (void);
|
||||
virtual Ptr<const Packet> DoPeek (void) const;
|
||||
void NewtonStep(void);
|
||||
codel_time_t ControlLaw(codel_time_t t);
|
||||
bool ShouldDrop(Ptr<Packet> p, codel_time_t now);
|
||||
/**
|
||||
* \brief Get the number of packets dropped when packets
|
||||
* arrive at a full queue and cannot be enqueued.
|
||||
*
|
||||
* \returns The number of dropped packets
|
||||
*/
|
||||
uint32_t GetDropOverLimit (void);
|
||||
|
||||
std::queue<Ptr<Packet> > m_packets;
|
||||
uint32_t m_maxPackets;
|
||||
uint32_t m_maxBytes;
|
||||
uint32_t m_bytesInQueue;
|
||||
uint32_t *backlog;
|
||||
uint32_t m_minbytes;
|
||||
Time m_Interval;
|
||||
Time m_Target;
|
||||
TracedValue<uint32_t> m_count;
|
||||
TracedValue<uint32_t> m_drop_count;
|
||||
TracedValue<uint32_t> m_lastcount;
|
||||
bool m_dropping;
|
||||
uint16_t m_rec_inv_sqrt;
|
||||
codel_time_t m_first_above_time;
|
||||
codel_time_t m_drop_next;
|
||||
uint32_t m_state1;
|
||||
uint32_t m_state2;
|
||||
uint32_t m_state3;
|
||||
uint32_t m_states;
|
||||
uint32_t m_drop_overlimit;
|
||||
Mode m_mode;
|
||||
/**
|
||||
* \brief Get the number of packets dropped according to CoDel algorithm
|
||||
*
|
||||
* \returns The number of dropped packets
|
||||
*/
|
||||
uint32_t GetDropCount (void);
|
||||
|
||||
/**
|
||||
* \brief Get the target queue delay
|
||||
*
|
||||
* \returns The target queue delay
|
||||
*/
|
||||
Time GetTarget (void);
|
||||
|
||||
/**
|
||||
* \brief Get the interval
|
||||
*
|
||||
* \returns The interval
|
||||
*/
|
||||
Time GetInterval (void);
|
||||
|
||||
/**
|
||||
* \brief Get the time for next packet drop while in the dropping state
|
||||
*
|
||||
* \returns The time for next packet drop
|
||||
*/
|
||||
uint32_t GetDropNext (void);
|
||||
|
||||
private:
|
||||
friend class::CoDelQueueNewtonStepTest; // Test code
|
||||
friend class::CoDelQueueControlLawTest; // Test code
|
||||
/**
|
||||
* \brief Add a packet to the queue
|
||||
*
|
||||
* \param p The packet to be added
|
||||
* \returns True if the packet can be added, False if the packet is dropped due to full queue
|
||||
*/
|
||||
virtual bool DoEnqueue (Ptr<Packet> p);
|
||||
|
||||
/**
|
||||
* \brief Remove a packet from queue based on the current state
|
||||
* If we are in dropping state, check if we could leave the dropping state
|
||||
* or if we should perform next drop
|
||||
* If we are not currently in dropping state, check if we need to enter the state
|
||||
* and drop the first packet
|
||||
*
|
||||
* \returns The packet that is examined
|
||||
*/
|
||||
virtual Ptr<Packet> DoDequeue (void);
|
||||
|
||||
virtual Ptr<const Packet> DoPeek (void) const;
|
||||
|
||||
/**
|
||||
* \brief Calculate the reciprocal square root of m_count by using Newton's method
|
||||
* http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Iterative_methods_for_reciprocal_square_roots
|
||||
* m_recInvSqrt (new) = (m_recInvSqrt (old) / 2) * (3 - m_count * m_recInvSqrt^2)
|
||||
*/
|
||||
void NewtonStep (void);
|
||||
|
||||
/**
|
||||
* \brief Determine the time for next drop
|
||||
* CoDel control law is t + m_interval/sqrt(m_count).
|
||||
* Here, we use m_recInvSqrt calculated by Newton's method in NewtonStep() to avoid
|
||||
* both sqrt() and divide operations
|
||||
*
|
||||
* \param t Current next drop time
|
||||
* \returns The new next drop time:
|
||||
*/
|
||||
uint32_t ControlLaw (uint32_t t);
|
||||
|
||||
/**
|
||||
* \brief Determine whether a packet is OK to be dropped. The packet
|
||||
* may not be actually dropped (depending on the drop state)
|
||||
*
|
||||
* \param p The packet that is considered
|
||||
* \param now The current time represented as 32-bit unsigned integer (us)
|
||||
* \returns True if it is OK to drop the packet (sojourn time above target for at least interval)
|
||||
*/
|
||||
bool OkToDrop (Ptr<Packet> p, uint32_t now);
|
||||
|
||||
bool CoDelTimeAfter (uint32_t a, uint32_t b);
|
||||
bool CoDelTimeAfterEq (uint32_t a, uint32_t b);
|
||||
bool CoDelTimeBefore (uint32_t a, uint32_t b);
|
||||
bool CoDelTimeBeforeEq (uint32_t a, uint32_t b);
|
||||
|
||||
/**
|
||||
* returned unsigned 32-bit integer representation of the input Time object
|
||||
* units are microseconds
|
||||
*/
|
||||
uint32_t Time2CoDel (Time t);
|
||||
|
||||
std::queue<Ptr<Packet> > m_packets; //!< The packet queue
|
||||
uint32_t m_maxPackets; //!< Max # of packets accepted by the queue
|
||||
uint32_t m_maxBytes; //!< Max # of bytes accepted by the queue
|
||||
TracedValue<uint32_t> m_bytesInQueue; //!< The total number of bytes in queue
|
||||
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
|
||||
TracedValue<uint32_t> m_count; //!< Number of packets dropped since entering drop state
|
||||
TracedValue<uint32_t> m_dropCount; //!< Number of dropped packets according CoDel algorithm
|
||||
TracedValue<uint32_t> m_lastCount; //<! Last number of packets dropped since entering drop state
|
||||
TracedValue<bool> m_dropping; //!< True if in dropping state
|
||||
uint16_t m_recInvSqrt; //!< Reciprocal inverse square root
|
||||
uint32_t m_firstAboveTime; //!< Time to declare sojourn time above target
|
||||
TracedValue<uint32_t> m_dropNext; //!< Time to drop next packet
|
||||
uint32_t m_state1; //!< Number of times packet sojourn goes above target for interval
|
||||
uint32_t m_state2; //!< Number of times we perform next drop while in dropping state
|
||||
uint32_t m_state3; //!< Number of times we enter drop state and drop the fist packet
|
||||
uint32_t m_states; //!< Total number of times we are in state 1, state 2, or state 3
|
||||
uint32_t m_dropOverLimit; //!< The number of packets dropped due to full queue
|
||||
QueueMode m_mode; //!< The operating mode (Bytes or packets)
|
||||
TracedValue<Time> m_sojourn; //!< Time in queue
|
||||
};
|
||||
|
||||
} // namespace ns3
|
||||
|
||||
Reference in New Issue
Block a user