OLD | NEW |
(Empty) | |
| 1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
| 2 /* |
| 3 * Copyright (c) 2017 Trinity College Dublin |
| 4 * |
| 5 * This program is free software; you can redistribute it and/or modify |
| 6 * it under the terms of the GNU General Public License version 2 as |
| 7 * published by the Free Software Foundation; |
| 8 * |
| 9 * This program is distributed in the hope that it will be useful, |
| 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 * GNU General Public License for more details. |
| 13 * |
| 14 * You should have received a copy of the GNU General Public License |
| 15 * along with this program; if not, write to the Free Software |
| 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 17 * |
| 18 * Authors: Rohit P. Tahiliani <rohit.tahil@gmail.com> |
| 19 * |
| 20 */ |
| 21 |
| 22 #include "ns3/log.h" |
| 23 #include "ns3/enum.h" |
| 24 #include "ns3/uinteger.h" |
| 25 #include "ns3/double.h" |
| 26 #include "ns3/simulator.h" |
| 27 #include "ns3/abort.h" |
| 28 #include "pi-square-queue-disc.h" |
| 29 #include "ns3/drop-tail-queue.h" |
| 30 |
| 31 namespace ns3 { |
| 32 |
| 33 NS_LOG_COMPONENT_DEFINE ("PiSquareQueueDisc"); |
| 34 |
| 35 NS_OBJECT_ENSURE_REGISTERED (PiSquareQueueDisc); |
| 36 |
| 37 TypeId PiSquareQueueDisc::GetTypeId (void) |
| 38 { |
| 39 static TypeId tid = TypeId ("ns3::PiSquareQueueDisc") |
| 40 .SetParent<QueueDisc> () |
| 41 .SetGroupName ("TrafficControl") |
| 42 .AddConstructor<PiSquareQueueDisc> () |
| 43 .AddAttribute ("Mode", |
| 44 "Determines unit for QueueLimit", |
| 45 EnumValue (QUEUE_DISC_MODE_PACKETS), |
| 46 MakeEnumAccessor (&PiSquareQueueDisc::SetMode), |
| 47 MakeEnumChecker (QUEUE_DISC_MODE_BYTES, "QUEUE_DISC_MODE_BYTE
S", |
| 48 QUEUE_DISC_MODE_PACKETS, "QUEUE_DISC_MODE_PA
CKETS")) |
| 49 .AddAttribute ("MeanPktSize", |
| 50 "Average of packet size", |
| 51 UintegerValue (1000), |
| 52 MakeUintegerAccessor (&PiSquareQueueDisc::m_meanPktSize), |
| 53 MakeUintegerChecker<uint32_t> ()) |
| 54 .AddAttribute ("A", |
| 55 "Value of alpha", |
| 56 DoubleValue (0.125), |
| 57 MakeDoubleAccessor (&PiSquareQueueDisc::m_a), |
| 58 MakeDoubleChecker<double> ()) |
| 59 .AddAttribute ("B", |
| 60 "Value of beta", |
| 61 DoubleValue (1.25), |
| 62 MakeDoubleAccessor (&PiSquareQueueDisc::m_b), |
| 63 MakeDoubleChecker<double> ()) |
| 64 .AddAttribute ("Tupdate", |
| 65 "Time period to calculate drop probability", |
| 66 TimeValue (Seconds (0.03)), |
| 67 MakeTimeAccessor (&PiSquareQueueDisc::m_tUpdate), |
| 68 MakeTimeChecker ()) |
| 69 .AddAttribute ("Supdate", |
| 70 "Start time of the update timer", |
| 71 TimeValue (Seconds (0)), |
| 72 MakeTimeAccessor (&PiSquareQueueDisc::m_sUpdate), |
| 73 MakeTimeChecker ()) |
| 74 .AddAttribute ("QueueLimit", |
| 75 "Queue limit in bytes/packets", |
| 76 UintegerValue (25), |
| 77 MakeUintegerAccessor (&PiSquareQueueDisc::SetQueueLimit), |
| 78 MakeUintegerChecker<uint32_t> ()) |
| 79 .AddAttribute ("DequeueThreshold", |
| 80 "Minimum queue size in bytes before dequeue rate is measured"
, |
| 81 UintegerValue (10000), |
| 82 MakeUintegerAccessor (&PiSquareQueueDisc::m_dqThreshold), |
| 83 MakeUintegerChecker<uint32_t> ()) |
| 84 .AddAttribute ("QueueDelayReference", |
| 85 "Desired queue delay", |
| 86 TimeValue (Seconds (0.02)), |
| 87 MakeTimeAccessor (&PiSquareQueueDisc::m_qDelayRef), |
| 88 MakeTimeChecker ()) |
| 89 .AddAttribute ("CoupledAqm", |
| 90 "True to enable Coupled AQM Functionality", |
| 91 BooleanValue (false), |
| 92 MakeBooleanAccessor (&PiSquareQueueDisc::SetCoupledAqm), |
| 93 MakeBooleanChecker ()) |
| 94 .AddAttribute ("UseEcn", |
| 95 "True to enable ECN (packets are marked instead of being drop
ped)", |
| 96 BooleanValue (false), |
| 97 MakeBooleanAccessor (&PiSquareQueueDisc::m_useEcn), |
| 98 MakeBooleanChecker ()) |
| 99 ; |
| 100 |
| 101 return tid; |
| 102 } |
| 103 |
| 104 PiSquareQueueDisc::PiSquareQueueDisc () |
| 105 : QueueDisc () |
| 106 { |
| 107 NS_LOG_FUNCTION (this); |
| 108 m_uv = CreateObject<UniformRandomVariable> (); |
| 109 m_rtrsEvent = Simulator::Schedule (m_sUpdate, &PiSquareQueueDisc::CalculateP,
this); |
| 110 } |
| 111 |
| 112 PiSquareQueueDisc::~PiSquareQueueDisc () |
| 113 { |
| 114 NS_LOG_FUNCTION (this); |
| 115 } |
| 116 |
| 117 void |
| 118 PiSquareQueueDisc::DoDispose (void) |
| 119 { |
| 120 NS_LOG_FUNCTION (this); |
| 121 m_uv = 0; |
| 122 Simulator::Remove (m_rtrsEvent); |
| 123 QueueDisc::DoDispose (); |
| 124 } |
| 125 |
| 126 void |
| 127 PiSquareQueueDisc::SetMode (QueueDiscMode mode) |
| 128 { |
| 129 NS_LOG_FUNCTION (this << mode); |
| 130 m_mode = mode; |
| 131 } |
| 132 |
| 133 PiSquareQueueDisc::QueueDiscMode |
| 134 PiSquareQueueDisc::GetMode (void) |
| 135 { |
| 136 NS_LOG_FUNCTION (this); |
| 137 return m_mode; |
| 138 } |
| 139 |
| 140 void |
| 141 PiSquareQueueDisc::SetQueueLimit (uint32_t lim) |
| 142 { |
| 143 NS_LOG_FUNCTION (this << lim); |
| 144 m_queueLimit = lim; |
| 145 } |
| 146 |
| 147 void |
| 148 PiSquareQueueDisc::SetCoupledAqm (bool coupledAqm) |
| 149 { |
| 150 NS_LOG_FUNCTION (this); |
| 151 m_coupledAqm = coupledAqm; |
| 152 // For Coupled AQM functionality, ECN must be enabled |
| 153 if (coupledAqm) |
| 154 { |
| 155 m_useEcn = true; |
| 156 } |
| 157 } |
| 158 |
| 159 uint32_t |
| 160 PiSquareQueueDisc::GetQueueSize (void) |
| 161 { |
| 162 NS_LOG_FUNCTION (this); |
| 163 if (GetMode () == QUEUE_DISC_MODE_BYTES) |
| 164 { |
| 165 return GetInternalQueue (0)->GetNBytes (); |
| 166 } |
| 167 else if (GetMode () == QUEUE_DISC_MODE_PACKETS) |
| 168 { |
| 169 return GetInternalQueue (0)->GetNPackets (); |
| 170 } |
| 171 else |
| 172 { |
| 173 NS_ABORT_MSG ("Unknown PI Square mode."); |
| 174 } |
| 175 } |
| 176 |
| 177 PiSquareQueueDisc::Stats |
| 178 PiSquareQueueDisc::GetStats () |
| 179 { |
| 180 NS_LOG_FUNCTION (this); |
| 181 return m_stats; |
| 182 } |
| 183 |
| 184 Time |
| 185 PiSquareQueueDisc::GetQueueDelay (void) |
| 186 { |
| 187 NS_LOG_FUNCTION (this); |
| 188 return m_qDelay; |
| 189 } |
| 190 |
| 191 double |
| 192 PiSquareQueueDisc::GetDropProb (void) |
| 193 { |
| 194 NS_LOG_FUNCTION (this); |
| 195 return m_dropProb; |
| 196 } |
| 197 |
| 198 int64_t |
| 199 PiSquareQueueDisc::AssignStreams (int64_t stream) |
| 200 { |
| 201 NS_LOG_FUNCTION (this << stream); |
| 202 m_uv->SetStream (stream); |
| 203 return 1; |
| 204 } |
| 205 |
| 206 bool |
| 207 PiSquareQueueDisc::DoEnqueue (Ptr<QueueDiscItem> item) |
| 208 { |
| 209 NS_LOG_FUNCTION (this << item); |
| 210 |
| 211 uint32_t nQueued = GetQueueSize (); |
| 212 |
| 213 if ((GetMode () == QUEUE_DISC_MODE_PACKETS && nQueued >= m_queueLimit) |
| 214 || (GetMode () == QUEUE_DISC_MODE_BYTES && nQueued + item->GetSize () > m_
queueLimit)) |
| 215 { |
| 216 // Drops due to queue limit: reactive |
| 217 Drop (item); |
| 218 m_stats.forcedDrop++; |
| 219 return false; |
| 220 } |
| 221 else if (DropEarly (item, nQueued)) |
| 222 { |
| 223 // Early probability drop: proactive |
| 224 if (!m_useEcn || !item->Mark ()) |
| 225 { |
| 226 Drop (item); |
| 227 m_stats.unforcedDrop++; |
| 228 return false; |
| 229 } |
| 230 m_stats.unforcedMark++;· |
| 231 } |
| 232 |
| 233 // No drop |
| 234 bool retval = GetInternalQueue (0)->Enqueue (item); |
| 235 |
| 236 // If Queue::Enqueue fails, QueueDisc::Drop is called by the internal queue |
| 237 // because QueueDisc::AddInternalQueue sets the drop callback |
| 238 |
| 239 NS_LOG_LOGIC ("\t bytesInQueue " << GetInternalQueue (0)->GetNBytes ()); |
| 240 NS_LOG_LOGIC ("\t packetsInQueue " << GetInternalQueue (0)->GetNPackets ()); |
| 241 |
| 242 return retval; |
| 243 } |
| 244 |
| 245 void |
| 246 PiSquareQueueDisc::InitializeParams (void) |
| 247 { |
| 248 // Initially queue is empty so variables are initialize to zero except m_dqCou
nt |
| 249 m_inMeasurement = false; |
| 250 m_dqCount = -1; |
| 251 m_dropProb = 0; |
| 252 m_avgDqRate = 0.0; |
| 253 m_dqStart = 0; |
| 254 m_qDelayOld = Time (Seconds (0)); |
| 255 m_stats.forcedDrop = 0; |
| 256 m_stats.unforcedDrop = 0; |
| 257 } |
| 258 |
| 259 bool PiSquareQueueDisc::DropEarly (Ptr<QueueDiscItem> item, uint32_t qSize) |
| 260 { |
| 261 NS_LOG_FUNCTION (this << item << qSize); |
| 262 |
| 263 double p = m_dropProb; |
| 264 |
| 265 uint32_t packetSize = item->GetSize (); |
| 266 |
| 267 if (GetMode () == QUEUE_DISC_MODE_BYTES) |
| 268 { |
| 269 p = p * packetSize / m_meanPktSize; |
| 270 } |
| 271 bool earlyDrop = true; |
| 272 double u = m_uv->GetValue (); |
| 273 |
| 274 if (GetMode () == QUEUE_DISC_MODE_BYTES && qSize <= 2 * m_meanPktSize) |
| 275 { |
| 276 return false; |
| 277 } |
| 278 else if (GetMode () == QUEUE_DISC_MODE_PACKETS && qSize <= 2) |
| 279 { |
| 280 return false; |
| 281 } |
| 282 |
| 283 if (m_coupledAqm && item->IsL4S ()) |
| 284 { |
| 285 // Apply linear drop probability |
| 286 if (u > p) |
| 287 { |
| 288 earlyDrop = false; |
| 289 } |
| 290 } |
| 291 else· |
| 292 { |
| 293 // Apply the squared drop probability |
| 294 if (u > p * p) |
| 295 { |
| 296 earlyDrop = false; |
| 297 } |
| 298 } |
| 299 if (!earlyDrop) |
| 300 { |
| 301 return false; |
| 302 } |
| 303 |
| 304 return true; |
| 305 } |
| 306 |
| 307 void PiSquareQueueDisc::CalculateP () |
| 308 { |
| 309 NS_LOG_FUNCTION (this); |
| 310 Time qDelay; |
| 311 double p = 0.0; |
| 312 bool missingInitFlag = false; |
| 313 if (m_avgDqRate > 0) |
| 314 { |
| 315 qDelay = Time (Seconds (GetInternalQueue (0)->GetNBytes () / m_avgDqRate))
; |
| 316 } |
| 317 else |
| 318 { |
| 319 qDelay = Time (Seconds (0)); |
| 320 missingInitFlag = true; |
| 321 } |
| 322 |
| 323 m_qDelay = qDelay; |
| 324 |
| 325 // Calculate the drop probability |
| 326 p = m_a * (qDelay.GetSeconds () - m_qDelayRef.GetSeconds ()) + m_b * (qDelay.G
etSeconds () - m_qDelayOld.GetSeconds ()); |
| 327 p += m_dropProb; |
| 328 |
| 329 // For non-linear drop in prob |
| 330 |
| 331 if (qDelay.GetSeconds () == 0 && m_qDelayOld.GetSeconds () == 0) |
| 332 { |
| 333 p *= 0.98; |
| 334 } |
| 335 |
| 336 m_dropProb = (p > 0) ? p : 0; |
| 337 |
| 338 if ( (qDelay.GetSeconds () < 0.5 * m_qDelayRef.GetSeconds ()) && (m_qDelayOld.
GetSeconds () < (0.5 * m_qDelayRef.GetSeconds ())) && (m_dropProb == 0) && !miss
ingInitFlag ) |
| 339 { |
| 340 m_dqCount = -1; |
| 341 m_avgDqRate = 0.0; |
| 342 } |
| 343 |
| 344 m_qDelayOld = qDelay; |
| 345 m_rtrsEvent = Simulator::Schedule (m_tUpdate, &PiSquareQueueDisc::CalculateP,
this); |
| 346 } |
| 347 |
| 348 Ptr<QueueDiscItem> |
| 349 PiSquareQueueDisc::DoDequeue () |
| 350 { |
| 351 NS_LOG_FUNCTION (this); |
| 352 |
| 353 if (GetInternalQueue (0)->IsEmpty ()) |
| 354 { |
| 355 NS_LOG_LOGIC ("Queue empty"); |
| 356 return 0; |
| 357 } |
| 358 |
| 359 Ptr<QueueDiscItem> item = StaticCast<QueueDiscItem> (GetInternalQueue (0)->Deq
ueue ()); |
| 360 double now = Simulator::Now ().GetSeconds (); |
| 361 uint32_t pktSize = item->GetSize (); |
| 362 |
| 363 // if not in a measurement cycle and the queue has built up to dq_threshold, |
| 364 // start the measurement cycle |
| 365 |
| 366 if ( (GetInternalQueue (0)->GetNBytes () >= m_dqThreshold) && (!m_inMeasuremen
t) ) |
| 367 { |
| 368 m_dqStart = now; |
| 369 m_dqCount = 0; |
| 370 m_inMeasurement = true; |
| 371 } |
| 372 |
| 373 if (m_inMeasurement) |
| 374 { |
| 375 m_dqCount += pktSize; |
| 376 |
| 377 // done with a measurement cycle |
| 378 if (m_dqCount >= m_dqThreshold) |
| 379 { |
| 380 |
| 381 double tmp = now - m_dqStart; |
| 382 |
| 383 if (tmp > 0) |
| 384 { |
| 385 if (m_avgDqRate == 0) |
| 386 { |
| 387 m_avgDqRate = m_dqCount / tmp; |
| 388 } |
| 389 else |
| 390 { |
| 391 m_avgDqRate = (0.5 * m_avgDqRate) + (0.5 * (m_dqCount / tmp)); |
| 392 } |
| 393 } |
| 394 |
| 395 // restart a measurement cycle if there is enough data |
| 396 if (GetInternalQueue (0)->GetNBytes () > m_dqThreshold) |
| 397 { |
| 398 m_dqStart = now; |
| 399 m_dqCount = 0; |
| 400 m_inMeasurement = true; |
| 401 } |
| 402 else |
| 403 { |
| 404 m_dqCount = 0; |
| 405 m_inMeasurement = false; |
| 406 } |
| 407 } |
| 408 } |
| 409 |
| 410 return item; |
| 411 } |
| 412 |
| 413 Ptr<const QueueDiscItem> |
| 414 PiSquareQueueDisc::DoPeek () const |
| 415 { |
| 416 NS_LOG_FUNCTION (this); |
| 417 if (GetInternalQueue (0)->IsEmpty ()) |
| 418 { |
| 419 NS_LOG_LOGIC ("Queue empty"); |
| 420 return 0; |
| 421 } |
| 422 |
| 423 Ptr<const QueueDiscItem> item = StaticCast<const QueueDiscItem> (GetInternalQu
eue (0)->Peek ()); |
| 424 |
| 425 NS_LOG_LOGIC ("Number packets " << GetInternalQueue (0)->GetNPackets ()); |
| 426 NS_LOG_LOGIC ("Number bytes " << GetInternalQueue (0)->GetNBytes ()); |
| 427 |
| 428 return item; |
| 429 } |
| 430 |
| 431 bool |
| 432 PiSquareQueueDisc::CheckConfig (void) |
| 433 { |
| 434 NS_LOG_FUNCTION (this); |
| 435 if (GetNQueueDiscClasses () > 0) |
| 436 { |
| 437 NS_LOG_ERROR ("PiSquareQueueDisc cannot have classes"); |
| 438 return false; |
| 439 } |
| 440 |
| 441 if (GetNPacketFilters () > 0) |
| 442 { |
| 443 NS_LOG_ERROR ("PiSquareQueueDisc cannot have packet filters"); |
| 444 return false; |
| 445 } |
| 446 |
| 447 if (GetNInternalQueues () == 0) |
| 448 { |
| 449 // create a DropTail queue |
| 450 Ptr<InternalQueue> queue = CreateObjectWithAttributes<DropTailQueue<QueueD
iscItem> > ("Mode", EnumValue (m_mode)); |
| 451 if (m_mode == QUEUE_DISC_MODE_PACKETS) |
| 452 { |
| 453 queue->SetMaxPackets (m_queueLimit); |
| 454 } |
| 455 else |
| 456 { |
| 457 queue->SetMaxBytes (m_queueLimit); |
| 458 } |
| 459 AddInternalQueue (queue); |
| 460 } |
| 461 |
| 462 if (GetNInternalQueues () != 1) |
| 463 { |
| 464 NS_LOG_ERROR ("PiSquareQueueDisc needs 1 internal queue"); |
| 465 return false; |
| 466 } |
| 467 |
| 468 if ((GetInternalQueue (0)->GetMode () == QueueBase::QUEUE_MODE_PACKETS && m_mo
de == QUEUE_DISC_MODE_BYTES) || |
| 469 (GetInternalQueue (0)->GetMode () == QueueBase::QUEUE_MODE_BYTES && m_mode
== QUEUE_DISC_MODE_PACKETS)) |
| 470 { |
| 471 NS_LOG_ERROR ("The mode of the provided queue does not match the mode set
on the PiSquareQueueDisc"); |
| 472 return false; |
| 473 } |
| 474 |
| 475 if ((m_mode == QUEUE_DISC_MODE_PACKETS && GetInternalQueue (0)->GetMaxPackets
() < m_queueLimit) |
| 476 || (m_mode == QUEUE_DISC_MODE_BYTES && GetInternalQueue (0)->GetMaxBytes
() < m_queueLimit)) |
| 477 { |
| 478 NS_LOG_ERROR ("The size of the internal queue is less than the queue disc
limit"); |
| 479 return false; |
| 480 } |
| 481 |
| 482 return true; |
| 483 } |
| 484 |
| 485 } //namespace ns3 |
OLD | NEW |