我尝试在我的网卡的单个 RX-Queue 上最大化吞吐量。我当前的设置是Shared Umem
通过在同一个 RX-Queue 上设置多个套接字来利用该功能,每个套接字都引用同一个 Umem。
然后我的内核 XDP 程序通过BPF_MAP_TYPE_XSKMAP
. 这一切工作正常,但在大约 600.000 pps 时,ksoftirqd/18
达到 100% 的 CPU 负载(我将我的用户空间应用程序移动到另一个核心,taskset -c 1
以减少核心 18 上的负载)。我的用户空间应用程序没有超过 14% 的 CPU 负载,所以不幸的是,我无法处理更多数据包的原因是因为大量的中断。
然后,我阅读了 xdp 绑定标志,该标志XDP_USE_NEED_WAKEUP
将 Umem 填充环发送到睡眠状态,从而减少了中断开销(据我正确理解,关于这个主题的信息并不多)。因为 Umem Fill-Ring 可能正在睡觉,所以必须定期检查:
if (xsk_ring_prod__needs_wakeup(&umem->fq)) {
const int ret = poll(fds, len, 10);
}
fds
包含struct pollfd
每个套接字的文件描述符。我不太确定在哪里添加XDP_USE_NEED_WAKEUP
标志,但这是我使用它的方式:
static struct xsk_socket_info *xsk_configure_socket(struct xsk_umem_info *umem, struct config *cfg,
const bool rx, const bool tx) {
struct xsk_socket_config xsk_socket_cfg;
struct xsk_socket_info *xsk;
struct xsk_ring_cons *rxr;
struct xsk_ring_prod *txr;
int ret;
xsk = calloc(1, sizeof(*xsk));
if (!xsk) {
fprintf(stderr, "xsk `calloc` failed: %s\n", strerror(errno));
exit(1);
}
xsk->umem = umem;
xsk_socket_cfg.rx_size = XSK_CONS_AMOUNT;
xsk_socket_cfg.tx_size = XSK_PROD_AMOUNT;
if (cfg->ip_addrs_len > 1) {
xsk_socket_cfg.libbpf_flags = XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD;
} else {
xsk_socket_cfg.libbpf_flags = 0;
}
xsk_socket_cfg.xdp_flags = cfg->xdp_flags;
xsk_socket_cfg.bind_flags = cfg->xsk_bind_flags | XDP_USE_NEED_WAKEUP;
rxr = rx ? &xsk->rx : NULL;
txr = tx ? &xsk->tx : NULL;
ret = xsk_socket__create(&xsk->xsk, cfg->ifname_buf, cfg->xsk_if_queue, umem->umem, rxr, txr, &xsk_socket_cfg);
if (ret) {
fprintf(stderr, "`xsk_socket__create` returned error: %s\n", strerror(errno));
exit(-ret);
}
return xsk;
}
我观察到它对负载的影响很小,ksoftirqd/18
并且我能够比以前多处理 50.000 pps(但这也可能是因为系统的一般负载发生了变化 - 我不确定:/)。但我也注意到,这XDP_USE_NEED_WAKEUP
不起作用,Shared Umem
因为 libbpf 有这个代码xsk.c
:
sxdp.sxdp_family = PF_XDP;
sxdp.sxdp_ifindex = xsk->ifindex;
sxdp.sxdp_queue_id = xsk->queue_id;
if (umem->refcount > 1) {
sxdp.sxdp_flags = XDP_SHARED_UMEM;
sxdp.sxdp_shared_umem_fd = umem->fd;
} else {
sxdp.sxdp_flags = xsk->config.bind_flags;
如您所见,bind_flags
仅当Umem
arefcount
为 1 时才使用(它不能小于该值,因为它在 的上方某处递增xsk_socket__create
)。但是因为对于每个创建的套接字,refcount
都会增加 - 这些bind_flags
仅用于第一个套接字(refcount
仍然是<= 1
)。
我不太明白为什么XDP_USE_NEED_WAKEUP
只能用于一个插座?事实上,我完全不明白如果这个标志实际上影响了 Umem,为什么它与套接字有关?
尽管如此,我正在寻找一种减少中断开销的方法——有什么想法可以实现吗?我需要至少 1.000.000 pps。