Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions Pcap++/header/XdpDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
/// @
namespace pcpp
{
#define XDP_MAX_RXTX_QUEUES 16
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This const isn't used anywhere, should we remove it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove. I think at first I was simply setting a hard limit before I created a function that can detect it. If the function cannot detect it, it returns 0 which may not be desirable. Maybe it should return 1 if nothing else. That will be the identical behavior in the release version.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made the default number 1. So a qid of zero, the default, and as things worked before, will pass the test.


/// @class XdpDevice
/// A class wrapping the main functionality of using AF_XDP (XSK) sockets
/// which are optimized for high performance packet processing.
Expand Down Expand Up @@ -76,6 +78,14 @@ namespace pcpp
/// The max number of packets to be received or sent in one batch
uint16_t rxTxBatchSize;

/// This parameter specifies the amount of space designated for prepending data to the packet in the frame.
/// NOTE: the headroom size should be less than the frame size
uint16_t frameHeadroomSize;

/// The queue identifier for the underlying socket. This value should be less than the number
/// of hardware queues supported by the device
uint16_t queueId;

/// A c'tor for this struct. Each parameter has a default value described below.
/// @param[in] attachMode AF_XDP operation mode. The default value is auto mode
/// @param[in] umemNumFrames Number of UMEM frames to allocate. The default value is 4096
Expand All @@ -87,19 +97,24 @@ namespace pcpp
/// @param[in] txSize The size of the TX ring used by the AF_XDP socket. The default value is 2048
/// @param[in] rxTxBatchSize The max number of packets to be received or sent in one batch. The default
/// value is 64
/// @param[in] frameHeadroomSize Space for prepending to packets. The default value is 0
/// @param[in] queueId The hardware queue id of the underlying socket. The default value is 0
explicit XdpDeviceConfiguration(AttachMode attachMode = AutoMode, uint16_t umemNumFrames = 0,
uint16_t umemFrameSize = 0, uint32_t fillRingSize = 0,
uint32_t completionRingSize = 0, uint32_t rxSize = 0, uint32_t txSize = 0,
uint16_t rxTxBatchSize = 0)
uint16_t rxTxBatchSize = 0, uint16_t frameHeadroomSize = 0,
uint32_t queueId = 0)
{
this->attachMode = attachMode;
this->umemNumFrames = umemNumFrames;
this->umemFrameSize = umemFrameSize;
this->frameHeadroomSize = frameHeadroomSize;
this->fillRingSize = fillRingSize;
this->completionRingSize = completionRingSize;
this->rxSize = rxSize;
this->txSize = txSize;
this->rxTxBatchSize = rxTxBatchSize;
this->queueId = queueId;
}
};

Expand Down Expand Up @@ -238,11 +253,28 @@ namespace pcpp
/// @return Current device statistics
XdpDeviceStats getStatistics();

/// @return Return queue identifier for underlying socket
uint32_t getQueueId() const
{
if (m_Config)
{
return m_Config->queueId;
}

return 0;
}

/// Get number of RX or TX hardware queues for device
/// @param[in] interfaceName The interface name to use to detect hardware queues
/// @param[in] tx If true, return TX queues, otherwise RX. Default is false
/// @return The number of hardware queues associated with the device.
static uint32_t numQueues(const std::string& interfaceName, bool tx = false);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a public method? Shouldn't it be private?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be useful for configuring the user space, such as limiting the number of threads and resources.


private:
class XdpUmem
{
public:
explicit XdpUmem(uint16_t numFrames, uint16_t frameSize, uint32_t fillRingSize,
explicit XdpUmem(uint16_t numFrames, uint16_t frameSize, uint16_t frameHeadroomSize, uint32_t fillRingSize,
uint32_t completionRingSize);

virtual ~XdpUmem();
Expand Down
72 changes: 63 additions & 9 deletions Pcap++/src/XdpDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
#include <net/if.h>
#include <sys/mman.h>
#include <unistd.h>
#include <dirent.h>
#include <vector>
#include <functional>
#include <algorithm>
#include <regex>
#include <poll.h>

namespace pcpp
Expand All @@ -36,10 +38,11 @@ namespace pcpp
#define DEFAULT_FILL_RING_SIZE (XSK_RING_PROD__DEFAULT_NUM_DESCS * 2)
#define DEFAULT_COMPLETION_RING_SIZE XSK_RING_PROD__DEFAULT_NUM_DESCS
#define DEFAULT_BATCH_SIZE 64
#define DEFAULT_FRAME_HEADROOM_SIZE XSK_UMEM__DEFAULT_FRAME_HEADROOM
#define IS_POWER_OF_TWO(num) (num && ((num & (num - 1)) == 0))

XdpDevice::XdpUmem::XdpUmem(uint16_t numFrames, uint16_t frameSize, uint32_t fillRingSize,
uint32_t completionRingSize)
XdpDevice::XdpUmem::XdpUmem(uint16_t numFrames, uint16_t frameSize, uint16_t frameHeadroomSize,
uint32_t fillRingSize, uint32_t completionRingSize)
{
size_t bufferSize = numFrames * frameSize;

Expand All @@ -51,7 +54,7 @@ namespace pcpp
struct xsk_umem_config cfg = { .fill_size = fillRingSize,
.comp_size = completionRingSize,
.frame_size = frameSize,
.frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM,
.frame_headroom = frameHeadroomSize,
.flags = 0 };

struct xsk_umem_info* umem = new xsk_umem_info;
Expand Down Expand Up @@ -403,8 +406,8 @@ namespace pcpp
auto umemInfo = static_cast<xsk_umem_info*>(m_Umem->getInfo());

struct xsk_socket_config xskConfig;
xskConfig.rx_size = m_Config->txSize;
xskConfig.tx_size = m_Config->rxSize;
xskConfig.rx_size = m_Config->rxSize;
xskConfig.tx_size = m_Config->txSize;
Comment on lines +410 to +411
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was already fixed in #2030

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will that be a problem if I leave it like this? A merge issue?

xskConfig.libbpf_flags = 0;
xskConfig.xdp_flags = 0;
xskConfig.bind_flags = 0;
Expand All @@ -419,8 +422,8 @@ namespace pcpp
xskConfig.xdp_flags = XDP_FLAGS_DRV_MODE;
}

int ret = xsk_socket__create(&socketInfo->xsk, m_InterfaceName.c_str(), 0, umemInfo->umem, &socketInfo->rx,
&socketInfo->tx, &xskConfig);
int ret = xsk_socket__create(&socketInfo->xsk, m_InterfaceName.c_str(), m_Config->queueId, umemInfo->umem,
&socketInfo->rx, &socketInfo->tx, &xskConfig);
if (ret)
{
PCPP_LOG_ERROR("xsk_socket__create returned an error: " << ret);
Expand All @@ -429,26 +432,29 @@ namespace pcpp
}

m_SocketInfo = socketInfo;

return true;
}

bool XdpDevice::initUmem()
{
m_Umem = new XdpUmem(m_Config->umemNumFrames, m_Config->umemFrameSize, m_Config->fillRingSize,
m_Config->completionRingSize);
m_Umem = new XdpUmem(m_Config->umemNumFrames, m_Config->umemFrameSize, m_Config->frameHeadroomSize,
m_Config->fillRingSize, m_Config->completionRingSize);
return true;
}

bool XdpDevice::populateConfigDefaults(XdpDeviceConfiguration& config) const
{
uint16_t numFrames = config.umemNumFrames ? config.umemNumFrames : DEFAULT_UMEM_NUM_FRAMES;
uint16_t frameSize = config.umemFrameSize ? config.umemFrameSize : getpagesize();
uint16_t frameHeadroomSize = config.frameHeadroomSize ? config.frameHeadroomSize : DEFAULT_FRAME_HEADROOM_SIZE;
uint32_t fillRingSize = config.fillRingSize ? config.fillRingSize : DEFAULT_FILL_RING_SIZE;
uint32_t completionRingSize =
config.completionRingSize ? config.completionRingSize : DEFAULT_COMPLETION_RING_SIZE;
uint32_t rxSize = config.rxSize ? config.rxSize : XSK_RING_CONS__DEFAULT_NUM_DESCS;
uint32_t txSize = config.txSize ? config.txSize : XSK_RING_PROD__DEFAULT_NUM_DESCS;
uint32_t batchSize = config.rxTxBatchSize ? config.rxTxBatchSize : DEFAULT_BATCH_SIZE;
uint32_t qId = config.queueId; // default is zero

if (frameSize != getpagesize())
{
Expand All @@ -463,6 +469,12 @@ namespace pcpp
return false;
}

if (frameHeadroomSize > frameSize)
{
PCPP_LOG_ERROR("Frame headroom size must be less than the frame size");
return false;
}

if (fillRingSize > numFrames)
{
PCPP_LOG_ERROR("Fill ring size (" << fillRingSize
Expand Down Expand Up @@ -499,13 +511,23 @@ namespace pcpp
return false;
}

unsigned int nhwqueues = numQueues(m_InterfaceName);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: let's rename to numOfHardwareQueues?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.

if (qId >= nhwqueues)
{
PCPP_LOG_ERROR("Queue Id (" << qId << ") must be less than the number hardware queues (" << nhwqueues
<< ") of device");
return false;
}

config.umemNumFrames = numFrames;
config.umemFrameSize = frameSize;
config.frameHeadroomSize = frameHeadroomSize;
config.fillRingSize = fillRingSize;
config.completionRingSize = completionRingSize;
config.rxSize = rxSize;
config.txSize = txSize;
config.rxTxBatchSize = batchSize;
config.queueId = qId;

return true;
}
Expand Down Expand Up @@ -636,4 +658,36 @@ namespace pcpp
return m_Stats;
}

uint32_t XdpDevice::numQueues(const std::string& iface, bool tx)
{
// returns number of hardware queues associated with the device
uint32_t rxtxqueues = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we rename this to numQueues?

std::string prefix = tx ? "tx-" : "rx-";
std::string path = "/sys/class/net/" + iface + "/queues/";
DIR* dir = opendir(path.c_str());

if (dir)
{
std::regex rxtx_regex("^" + prefix + "[0-9]+$");

struct dirent* entry;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Curious why the struct dirent*? Can't we just do dirent*?

while ((entry = readdir(dir)) != nullptr)
{
if (std::regex_match(entry->d_name, rxtx_regex))
{
rxtxqueues++;
}
}

closedir(dir);
}

else
{
PCPP_LOG_ERROR("Error getting number of hardware queues from " << iface);
}

return rxtxqueues;
}

} // namespace pcpp