1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2017 Universita' degli Studi di Napoli Federico II
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  * Author: Stefano Avallone <stefano.avallone@.unina.it>
19  */
20 #ifndef NET_DEVICE_QUEUE_INTERFACE_H
21 #define NET_DEVICE_QUEUE_INTERFACE_H
22 
23 #include <vector>
24 #include <functional>
25 #include "ns3/callback.h"
26 #include "ns3/object.h"
27 #include "ns3/ptr.h"
28 #include "ns3/log.h"
29 #include "ns3/net-device.h"
30 #include "ns3/object-factory.h"
31 
32 namespace ns3 {
33 
34 class QueueLimits;
35 class NetDeviceQueueInterface;
36 class QueueItem;
37 
38 
39 /**
40  * \ingroup network
41  * \defgroup netdevice Network Device
42  */
43 
44 /**
45  * \ingroup netdevice
46  *
47  * \brief Network device transmission queue
48  *
49  * This class stores information about a single transmission queue
50  * of a network device that is exposed to queue discs. Such information
51  * includes the state of the transmission queue (whether it has been
52  * stopped or not) and data used by techniques such as Byte Queue Limits.
53  *
54  * This class roughly models the struct netdev_queue of Linux.
55  */
56 class NetDeviceQueue : public Object
57 {
58 public:
59   /**
60    * \brief Get the type ID.
61    * \return the object TypeId
62    */
63   static TypeId GetTypeId (void);
64 
65   NetDeviceQueue ();
66   virtual ~NetDeviceQueue();
67 
68   /**
69    * Called by the device to start this device transmission queue.
70    * This is the analogous to the netif_tx_start_queue function of the Linux kernel.
71    */
72   virtual void Start (void);
73 
74   /**
75    * Called by the device to stop this device transmission queue.
76    * This is the analogous to the netif_tx_stop_queue function of the Linux kernel.
77    */
78   virtual void Stop (void);
79 
80   /**
81    * Called by the device to wake the queue disc associated with this
82    * device transmission queue. This is done by invoking the wake callback.
83    * This is the analogous to the netif_tx_wake_queue function of the Linux kernel.
84    */
85   virtual void Wake (void);
86 
87   /**
88    * \brief Get the status of the device transmission queue.
89    * \return true if the device transmission queue is stopped.
90    *
91    * Called by queue discs to enquire about the status of a given transmission queue.
92    * This is the analogous to the netif_xmit_stopped function of the Linux kernel.
93    */
94   virtual bool IsStopped (void) const;
95 
96   /**
97    * \brief Notify this NetDeviceQueue that the NetDeviceQueueInterface was
98    *        aggregated to an object.
99    *
100    * \param ndqi the NetDeviceQueueInterface.
101    *
102    * This NetDeviceQueue stores a pointer to the NetDevice the NetDeviceQueueInterface
103    * was aggregated to.
104    */
105   void NotifyAggregatedObject (Ptr<NetDeviceQueueInterface> ndqi);
106 
107   /// Callback invoked by netdevices to wake upper layers
108   typedef Callback< void > WakeCallback;
109 
110   /**
111    * \brief Set the wake callback
112    * \param cb the callback to set
113    *
114    * Called by the traffic control layer to set the wake callback. The wake callback
115    * is invoked by the device whenever it is needed to "wake" the upper layers (i.e.,
116    * solicitate the queue disc associated with this transmission queue (in case of
117    * multi-queue aware queue discs) or to the network device (otherwise) to send
118    * packets down to the device).
119    */
120   virtual void SetWakeCallback (WakeCallback cb);
121 
122   /**
123    * \brief Called by the netdevice to report the number of bytes queued to the device queue
124    * \param bytes number of bytes queued to the device queue
125    */
126   virtual void NotifyQueuedBytes (uint32_t bytes);
127 
128   /**
129    * \brief Called by the netdevice to report the number of bytes it is going to transmit
130    * \param bytes number of bytes the device is going to transmit
131    */
132   virtual void NotifyTransmittedBytes (uint32_t bytes);
133 
134   /**
135    * \brief Reset queue limits state
136    */
137   void ResetQueueLimits ();
138 
139   /**
140    * \brief Set queue limits to this queue
141    * \param ql the queue limits associated to this queue
142    */
143   void SetQueueLimits (Ptr<QueueLimits> ql);
144 
145   /**
146    * \brief Get queue limits to this queue
147    * \return the queue limits associated to this queue
148    */
149   Ptr<QueueLimits> GetQueueLimits ();
150 
151   /**
152    * \brief Perform the actions required by flow control and dynamic queue
153    *        limits when a packet is enqueued in the queue of a netdevice
154    *
155    * \param queue the device queue
156    * \param item the enqueued packet
157    *
158    * This method must be connected to the "Enqueue" traced callback of a Queue
159    * object (through a bound callback) in order for a netdevice to support
160    * flow control and dynamic queue limits.
161    */
162   template <typename QueueType>
163   void PacketEnqueued (QueueType* queue, Ptr<const typename QueueType::ItemType> item);
164 
165   /**
166    * \brief Perform the actions required by flow control and dynamic queue
167    *        limits when a packet is dequeued (or dropped after dequeue) from
168    *        the queue of a netdevice
169    *
170    * \param queue the device queue
171    * \param item the dequeued packet
172    *
173    * This method must be connected to the "Dequeue" traced callback of a Queue
174    * object (through a bound callback) in order for
175    * a netdevice to support flow control and dynamic queue limits.
176    */
177   template <typename QueueType>
178   void PacketDequeued (QueueType* queue, Ptr<const typename QueueType::ItemType> item);
179 
180   /**
181    * \brief Perform the actions required by flow control and dynamic queue
182    *        limits when a packet is dropped before being enqueued in the queue
183    *        of a netdevice (which likely indicates that the queue is full)
184    *
185    * \param queue the device queue
186    * \param item the dropped packet
187    *
188    * This method must be connected to the "DropBeforeEnqueue" traced callback
189    * of a Queue object (through a bound callback) in order for a netdevice to
190    * support flow control and dynamic queue limits.
191    */
192   template <typename QueueType>
193   void PacketDiscarded (QueueType* queue, Ptr<const typename QueueType::ItemType> item);
194 
195   /**
196    * \brief Connect the traced callbacks of a queue to the methods providing support
197    *        for flow control and dynamic queue limits. A queue can be any object providing:
198    *        - "Enqueue", "Dequeue", "DropBeforeEnqueue" traces
199    *        - an ItemType typedef for the type of stored items
200    *        - GetCurrentSize and GetMaxSize methods
201    * \param queue the queue
202    */
203   template <typename QueueType>
204   void ConnectQueueTraces (Ptr<QueueType> queue);
205 
206 private:
207   bool m_stoppedByDevice;         //!< True if the queue has been stopped by the device
208   bool m_stoppedByQueueLimits;    //!< True if the queue has been stopped by a queue limits object
209   Ptr<QueueLimits> m_queueLimits; //!< Queue limits object
210   WakeCallback m_wakeCallback;    //!< Wake callback
211   Ptr<NetDevice> m_device;        //!< the netdevice aggregated to the NetDeviceQueueInterface
212 
213   NS_LOG_TEMPLATE_DECLARE;        //!< redefinition of the log component
214 };
215 
216 
217 /**
218  * \ingroup netdevice
219  *
220  * \brief Network device transmission queue interface
221  *
222  * This interface is used by the traffic control layer and by the aggregated
223  * device to access the transmission queues of the device. Additionally, through
224  * this interface, traffic control aware netdevices can:
225  * - set the number of transmission queues
226  * - set the method used (by upper layers) to determine the transmission queue
227  *   in which the netdevice would enqueue a given packet
228  * NetDevice helpers create this interface and aggregate it to the device.
229  */
230 class NetDeviceQueueInterface : public Object
231 {
232 public:
233   /**
234    * \brief Get the type ID.
235    * \return the object TypeId
236    */
237   static TypeId GetTypeId (void);
238 
239   /**
240    * \brief Constructor
241    */
242   NetDeviceQueueInterface ();
243   virtual ~NetDeviceQueueInterface ();
244 
245   /**
246    * \brief Get the i-th transmission queue of the device.
247    *
248    * \param i the index of the requested queue.
249    * \return the i-th transmission queue of the device.
250    *
251    * The index of the first transmission queue is zero.
252    */
253   Ptr<NetDeviceQueue> GetTxQueue (std::size_t i) const;
254 
255   /**
256    * \brief Get the number of device transmission queues.
257    * \return the number of device transmission queues.
258    */
259   std::size_t GetNTxQueues (void) const;
260 
261   /**
262    * \brief Set the type of device transmission queues to create.
263    * \param type type of device transmission queues to create.
264    *
265    * This method is called when the TxQueuesType attribute is set to create
266    * the corresponding type of device transmission queues. It cannot be
267    * called again afterwards.
268    */
269   void SetTxQueuesType (TypeId type);
270 
271   /**
272    * \brief Set the number of device transmission queues to create.
273    * \param numTxQueues number of device transmission queues to create.
274    *
275    * This method is called when the NTxQueues attribute is set to create
276    * the corresponding number of device transmission queues. It cannot be
277    * called again afterwards.
278    */
279   void SetNTxQueues (std::size_t numTxQueues);
280 
281   /// Callback invoked to determine the tx queue selected for a given packet
282   typedef std::function<std::size_t (Ptr<QueueItem>)> SelectQueueCallback;
283 
284   /**
285    * \brief Set the select queue callback.
286    * \param cb the callback to set.
287    *
288    * This method is called to set the select queue callback, i.e., the
289    * method used to select a device transmission queue for a given packet.
290    */
291   void SetSelectQueueCallback (SelectQueueCallback cb);
292 
293   /**
294    * \brief Get the select queue callback.
295    * \return the select queue callback.
296    *
297    * Called by the traffic control layer to get the select queue callback set
298    * by a multi-queue device.
299    */
300   SelectQueueCallback GetSelectQueueCallback (void) const;
301 
302 protected:
303   /**
304    * \brief Dispose of the object
305    */
306   virtual void DoDispose (void);
307   /**
308    * \brief Notify that an object was aggregated
309    */
310   virtual void NotifyNewAggregate (void);
311 
312 private:
313   ObjectFactory m_txQueues;   //!< Device transmission queues TypeId
314   std::vector< Ptr<NetDeviceQueue> > m_txQueuesVector;   //!< Device transmission queues
315   SelectQueueCallback m_selectQueueCallback;   //!< Select queue callback
316 };
317 
318 
319 /**
320  * Implementation of the templates declared above.
321  */
322 
323 template <typename QueueType>
324 void
ConnectQueueTraces(Ptr<QueueType> queue)325 NetDeviceQueue::ConnectQueueTraces (Ptr<QueueType> queue)
326 {
327   NS_ASSERT (queue != 0);
328 
329   queue->TraceConnectWithoutContext ("Enqueue",
330                                      MakeCallback (&NetDeviceQueue::PacketEnqueued<QueueType>, this)
331                                      .Bind (PeekPointer (queue)));
332   queue->TraceConnectWithoutContext ("Dequeue",
333                                      MakeCallback (&NetDeviceQueue::PacketDequeued<QueueType>, this)
334                                      .Bind (PeekPointer (queue)));
335   queue->TraceConnectWithoutContext ("DropBeforeEnqueue",
336                                      MakeCallback (&NetDeviceQueue::PacketDiscarded<QueueType>, this)
337                                      .Bind (PeekPointer (queue)));
338 }
339 
340 template <typename QueueType>
341 void
PacketEnqueued(QueueType * queue,Ptr<const typename QueueType::ItemType> item)342 NetDeviceQueue::PacketEnqueued (QueueType* queue, Ptr<const typename QueueType::ItemType> item)
343 {
344   NS_LOG_FUNCTION (this << queue << item);
345 
346   // Inform BQL
347   NotifyQueuedBytes (item->GetSize ());
348 
349   NS_ASSERT_MSG (m_device, "Aggregated NetDevice not set");
350   Ptr<Packet> p = Create<Packet> (m_device->GetMtu ());
351 
352   // After enqueuing a packet, we need to check whether the queue is able to
353   // store another packet. If not, we stop the queue
354 
355   if (queue->GetCurrentSize () + p > queue->GetMaxSize ())
356     {
357       NS_LOG_DEBUG ("The device queue is being stopped (" << queue->GetCurrentSize ()
358                     << " inside)");
359       Stop ();
360     }
361 }
362 
363 template <typename QueueType>
364 void
PacketDequeued(QueueType * queue,Ptr<const typename QueueType::ItemType> item)365 NetDeviceQueue::PacketDequeued (QueueType* queue, Ptr<const typename QueueType::ItemType> item)
366 {
367   NS_LOG_FUNCTION (this << queue << item);
368 
369   // Inform BQL
370   NotifyTransmittedBytes (item->GetSize ());
371 
372   NS_ASSERT_MSG (m_device, "Aggregated NetDevice not set");
373   Ptr<Packet> p = Create<Packet> (m_device->GetMtu ());
374 
375   // After dequeuing a packet, if there is room for another packet we
376   // call Wake () that ensures that the queue is not stopped and restarts
377   // the queue disc if the queue was stopped
378 
379   if (queue->GetCurrentSize () + p <= queue->GetMaxSize ())
380     {
381       Wake ();
382     }
383 }
384 
385 template <typename QueueType>
386 void
PacketDiscarded(QueueType * queue,Ptr<const typename QueueType::ItemType> item)387 NetDeviceQueue::PacketDiscarded (QueueType* queue, Ptr<const typename QueueType::ItemType> item)
388 {
389   NS_LOG_FUNCTION (this << queue << item);
390 
391   // This method is called when a packet is discarded before being enqueued in the
392   // device queue, likely because the queue is full. This should not happen if the
393   // device correctly stops the queue. Anyway, stop the tx queue, so that the upper
394   // layers do not send packets until there is room in the queue again.
395 
396   NS_LOG_ERROR ("BUG! No room in the device queue for the received packet! ("
397                 << queue->GetCurrentSize () << " inside)");
398 
399   Stop ();
400 }
401 
402 } // namespace ns3
403 
404 #endif /* NET_DEVICE_QUEUE_INTERFACE_H */
405