Files
unison/src/network/utils/net-device-queue-interface.h
2024-11-08 18:05:46 +00:00

391 lines
13 KiB
C++

/*
* Copyright (c) 2017 Universita' degli Studi di Napoli Federico II
*
* SPDX-License-Identifier: GPL-2.0-only
*
* Author: Stefano Avallone <stefano.avallone@.unina.it>
*/
#ifndef NET_DEVICE_QUEUE_INTERFACE_H
#define NET_DEVICE_QUEUE_INTERFACE_H
#include "ns3/callback.h"
#include "ns3/log.h"
#include "ns3/net-device.h"
#include "ns3/object-factory.h"
#include "ns3/object.h"
#include "ns3/ptr.h"
#include "ns3/simulator.h"
#include <functional>
#include <vector>
namespace ns3
{
class QueueLimits;
class NetDeviceQueueInterface;
class QueueItem;
/**
* @ingroup network
* @defgroup netdevice Network Device
*/
/**
* @ingroup netdevice
*
* @brief Network device transmission queue
*
* This class stores information about a single transmission queue
* of a network device that is exposed to queue discs. Such information
* includes the state of the transmission queue (whether it has been
* stopped or not) and data used by techniques such as Byte Queue Limits.
*
* This class roughly models the struct netdev_queue of Linux.
*/
class NetDeviceQueue : public Object
{
public:
/**
* @brief Get the type ID.
* @return the object TypeId
*/
static TypeId GetTypeId();
NetDeviceQueue();
~NetDeviceQueue() override;
/**
* Called by the device to start this device transmission queue.
* This is the analogous to the netif_tx_start_queue function of the Linux kernel.
*/
virtual void Start();
/**
* Called by the device to stop this device transmission queue.
* This is the analogous to the netif_tx_stop_queue function of the Linux kernel.
*/
virtual void Stop();
/**
* Called by the device to wake the queue disc associated with this
* device transmission queue. This is done by invoking the wake callback.
* This is the analogous to the netif_tx_wake_queue function of the Linux kernel.
*/
virtual void Wake();
/**
* @brief Get the status of the device transmission queue.
* @return true if the device transmission queue is stopped.
*
* Called by queue discs to enquire about the status of a given transmission queue.
* This is the analogous to the netif_xmit_stopped function of the Linux kernel.
*/
virtual bool IsStopped() const;
/**
* @brief Notify this NetDeviceQueue that the NetDeviceQueueInterface was
* aggregated to an object.
*
* @param ndqi the NetDeviceQueueInterface.
*
* This NetDeviceQueue stores a pointer to the NetDevice the NetDeviceQueueInterface
* was aggregated to.
*/
void NotifyAggregatedObject(Ptr<NetDeviceQueueInterface> ndqi);
/// Callback invoked by netdevices to wake upper layers
typedef Callback<void> WakeCallback;
/**
* @brief Set the wake callback
* @param cb the callback to set
*
* Called by the traffic control layer to set the wake callback. The wake callback
* is invoked by the device whenever it is needed to "wake" the upper layers (i.e.,
* solicitate the queue disc associated with this transmission queue (in case of
* multi-queue aware queue discs) or to the network device (otherwise) to send
* packets down to the device).
*/
virtual void SetWakeCallback(WakeCallback cb);
/**
* @brief Called by the netdevice to report the number of bytes queued to the device queue
* @param bytes number of bytes queued to the device queue
*/
virtual void NotifyQueuedBytes(uint32_t bytes);
/**
* @brief Called by the netdevice to report the number of bytes it is going to transmit
* @param bytes number of bytes the device is going to transmit
*/
virtual void NotifyTransmittedBytes(uint32_t bytes);
/**
* @brief Reset queue limits state
*/
void ResetQueueLimits();
/**
* @brief Set queue limits to this queue
* @param ql the queue limits associated to this queue
*/
void SetQueueLimits(Ptr<QueueLimits> ql);
/**
* @brief Get queue limits to this queue
* @return the queue limits associated to this queue
*/
Ptr<QueueLimits> GetQueueLimits();
/**
* @brief Perform the actions required by flow control and dynamic queue
* limits when a packet is enqueued in the queue of a netdevice
*
* @param queue the device queue
* @param item the enqueued packet
*
* This method must be connected to the "Enqueue" traced callback of a Queue
* object (through a bound callback) in order for a netdevice to support
* flow control and dynamic queue limits.
*/
template <typename QueueType>
void PacketEnqueued(QueueType* queue, Ptr<const typename QueueType::ItemType> item);
/**
* @brief Perform the actions required by flow control and dynamic queue
* limits when a packet is dequeued (or dropped after dequeue) from
* the queue of a netdevice
*
* @param queue the device queue
* @param item the dequeued packet
*
* This method must be connected to the "Dequeue" traced callback of a Queue
* object (through a bound callback) in order for
* a netdevice to support flow control and dynamic queue limits.
*/
template <typename QueueType>
void PacketDequeued(QueueType* queue, Ptr<const typename QueueType::ItemType> item);
/**
* @brief Perform the actions required by flow control and dynamic queue
* limits when a packet is dropped before being enqueued in the queue
* of a netdevice (which likely indicates that the queue is full)
*
* @param queue the device queue
* @param item the dropped packet
*
* This method must be connected to the "DropBeforeEnqueue" traced callback
* of a Queue object (through a bound callback) in order for a netdevice to
* support flow control and dynamic queue limits.
*/
template <typename QueueType>
void PacketDiscarded(QueueType* queue, Ptr<const typename QueueType::ItemType> item);
/**
* @brief Connect the traced callbacks of a queue to the methods providing support
* for flow control and dynamic queue limits. A queue can be any object providing:
* - "Enqueue", "Dequeue", "DropBeforeEnqueue" traces
* - an ItemType typedef for the type of stored items
* - GetCurrentSize and GetMaxSize methods
* @param queue the queue
*/
template <typename QueueType>
void ConnectQueueTraces(Ptr<QueueType> queue);
private:
bool m_stoppedByDevice; //!< True if the queue has been stopped by the device
bool m_stoppedByQueueLimits; //!< True if the queue has been stopped by a queue limits object
Ptr<QueueLimits> m_queueLimits; //!< Queue limits object
WakeCallback m_wakeCallback; //!< Wake callback
Ptr<NetDevice> m_device; //!< the netdevice aggregated to the NetDeviceQueueInterface
NS_LOG_TEMPLATE_DECLARE; //!< redefinition of the log component
};
/**
* @ingroup netdevice
*
* @brief Network device transmission queue interface
*
* This interface is used by the traffic control layer and by the aggregated
* device to access the transmission queues of the device. Additionally, through
* this interface, traffic control aware netdevices can:
* - set the number of transmission queues
* - set the method used (by upper layers) to determine the transmission queue
* in which the netdevice would enqueue a given packet
* NetDevice helpers create this interface and aggregate it to the device.
*/
class NetDeviceQueueInterface : public Object
{
public:
/**
* @brief Get the type ID.
* @return the object TypeId
*/
static TypeId GetTypeId();
/**
* @brief Constructor
*/
NetDeviceQueueInterface();
~NetDeviceQueueInterface() override;
/**
* @brief Get the i-th transmission queue of the device.
*
* @param i the index of the requested queue.
* @return the i-th transmission queue of the device.
*
* The index of the first transmission queue is zero.
*/
Ptr<NetDeviceQueue> GetTxQueue(std::size_t i) const;
/**
* @brief Get the number of device transmission queues.
* @return the number of device transmission queues.
*/
std::size_t GetNTxQueues() const;
/**
* @brief Set the type of device transmission queues to create.
* @param type type of device transmission queues to create.
*
* This method is called when the TxQueuesType attribute is set to create
* the corresponding type of device transmission queues. It cannot be
* called again afterwards.
*/
void SetTxQueuesType(TypeId type);
/**
* @brief Set the number of device transmission queues to create.
* @param numTxQueues number of device transmission queues to create.
*
* This method is called when the NTxQueues attribute is set to create
* the corresponding number of device transmission queues. It cannot be
* called again afterwards.
*/
void SetNTxQueues(std::size_t numTxQueues);
/// Callback invoked to determine the tx queue selected for a given packet
typedef std::function<std::size_t(Ptr<QueueItem>)> SelectQueueCallback;
/**
* @brief Set the select queue callback.
* @param cb the callback to set.
*
* This method is called to set the select queue callback, i.e., the
* method used to select a device transmission queue for a given packet.
*/
void SetSelectQueueCallback(SelectQueueCallback cb);
/**
* @brief Get the select queue callback.
* @return the select queue callback.
*
* Called by the traffic control layer to get the select queue callback set
* by a multi-queue device.
*/
SelectQueueCallback GetSelectQueueCallback() const;
protected:
/**
* @brief Dispose of the object
*/
void DoDispose() override;
/**
* @brief Notify that an object was aggregated
*/
void NotifyNewAggregate() override;
private:
ObjectFactory m_txQueues; //!< Device transmission queues TypeId
std::vector<Ptr<NetDeviceQueue>> m_txQueuesVector; //!< Device transmission queues
SelectQueueCallback m_selectQueueCallback; //!< Select queue callback
};
/**
* Implementation of the templates declared above.
*/
template <typename QueueType>
void
NetDeviceQueue::ConnectQueueTraces(Ptr<QueueType> queue)
{
NS_ASSERT(queue);
queue->TraceConnectWithoutContext(
"Enqueue",
MakeCallback(&NetDeviceQueue::PacketEnqueued<QueueType>, this).Bind(PeekPointer(queue)));
queue->TraceConnectWithoutContext(
"Dequeue",
MakeCallback(&NetDeviceQueue::PacketDequeued<QueueType>, this).Bind(PeekPointer(queue)));
queue->TraceConnectWithoutContext(
"DropBeforeEnqueue",
MakeCallback(&NetDeviceQueue::PacketDiscarded<QueueType>, this).Bind(PeekPointer(queue)));
}
template <typename QueueType>
void
NetDeviceQueue::PacketEnqueued(QueueType* queue, Ptr<const typename QueueType::ItemType> item)
{
NS_LOG_FUNCTION(this << queue << item);
// Inform BQL
NotifyQueuedBytes(item->GetSize());
NS_ASSERT_MSG(m_device, "Aggregated NetDevice not set");
// After enqueuing a packet, we need to check whether the queue is able to
// store another packet. If not, we stop the queue
if (queue->WouldOverflow(1, m_device->GetMtu()))
{
NS_LOG_DEBUG("The device queue is being stopped (" << queue->GetCurrentSize()
<< " inside)");
Stop();
}
}
template <typename QueueType>
void
NetDeviceQueue::PacketDequeued(QueueType* queue, Ptr<const typename QueueType::ItemType> item)
{
NS_LOG_FUNCTION(this << queue << item);
NS_ASSERT_MSG(m_device, "Aggregated NetDevice not set");
Simulator::ScheduleNow([=, this]() {
// Inform BQL
NotifyTransmittedBytes(item->GetSize());
// After dequeuing a packet, if there is room for another packet we
// call Wake () that ensures that the queue is not stopped and restarts
// the queue disc if the queue was stopped
if (!queue->WouldOverflow(1, m_device->GetMtu()))
{
Wake();
}
});
}
template <typename QueueType>
void
NetDeviceQueue::PacketDiscarded(QueueType* queue, Ptr<const typename QueueType::ItemType> item)
{
NS_LOG_FUNCTION(this << queue << item);
// This method is called when a packet is discarded before being enqueued in the
// device queue, likely because the queue is full. This should not happen if the
// device correctly stops the queue. Anyway, stop the tx queue, so that the upper
// layers do not send packets until there is room in the queue again.
NS_LOG_ERROR("BUG! No room in the device queue for the received packet! ("
<< queue->GetCurrentSize() << " inside)");
Stop();
}
} // namespace ns3
#endif /* NET_DEVICE_QUEUE_INTERFACE_H */