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