13#include "fastmm/core/config_macros.hpp"
16#include "fastmm/net/udp_frame.hpp"
17#include "fastmm/net/xdp_program.hpp"
19#include <sys/socket.h>
52struct XdpSubscription {
68 std::vector<XdpInterfaceQueues>
queues;
133 int create(std::span<const xdp::Key> keys, std::uint32_t xsk_slots, std::string& err);
137 int attach(
unsigned ifindex,
bool generic)
noexcept;
145 int test_run(std::span<const std::
byte> frame, std::uint32_t& action) noexcept;
147 [[nodiscard]]
int prog_fd() const noexcept {
return prog_fd_; }
148 [[nodiscard]]
int link_fd() const noexcept {
return link_fd_; }
153 std::swap(subs_fd_, o.subs_fd_);
154 std::swap(xsks_fd_, o.xsks_fd_);
155 std::swap(fallback_fd_, o.fallback_fd_);
156 std::swap(prog_fd_, o.prog_fd_);
157 std::swap(link_fd_, o.link_fd_);
158 percpu_.swap(o.percpu_);
163 int fallback_fd_ = -1;
166 std::vector<std::uint64_t> percpu_;
173 std::atomic<std::uint32_t>* producer =
nullptr;
174 std::atomic<std::uint32_t>* consumer =
nullptr;
175 std::atomic<std::uint32_t>* flags =
nullptr;
176 void* entries =
nullptr;
177 std::uint32_t mask = 0;
178 std::uint32_t size = 0;
179 std::uint32_t cached_prod = 0;
180 std::uint32_t cached_cons = 0;
183 std::uint32_t peek(std::uint32_t
max, std::uint32_t& idx)
noexcept {
184 std::uint32_t avail = cached_prod - cached_cons;
186 cached_prod = producer->load(std::memory_order_acquire);
187 avail = cached_prod - cached_cons;
189 const std::uint32_t n = avail <
max ? avail :
max;
194 void release() noexcept { consumer->store(cached_cons, std::memory_order_release); }
197 std::uint32_t reserve(std::uint32_t n, std::uint32_t& idx)
noexcept {
198 if (cached_cons - cached_prod < n) {
199 cached_cons = consumer->load(std::memory_order_acquire) + size;
200 if (cached_cons - cached_prod < n)
return 0;
206 void submit() noexcept { producer->store(cached_prod, std::memory_order_release); }
207 [[nodiscard]]
bool needs_wakeup() const noexcept {
208 return (flags->load(std::memory_order_relaxed) & 1U) != 0;
215 std::uint32_t options;
219 std::uint32_t dst_ip;
220 std::uint16_t dst_port;
226 unsigned ifindex = 0;
227 std::uint32_t queue = 0;
228 std::uint32_t routes_begin = 0;
229 std::uint32_t routes_end = 0;
230 std::byte* umem =
nullptr;
231 std::size_t umem_len = 0;
232 void* fill_map =
nullptr;
233 std::size_t fill_map_len = 0;
234 void* rx_map =
nullptr;
235 std::size_t rx_map_len = 0;
242class XdpDatagramSource {
255 void close() noexcept;
256 [[nodiscard]] const std::
string&
error() const noexcept {
return error_; }
258 [[nodiscard]] std::span<const std::string>
warnings() const noexcept {
return warnings_; }
262 std::size_t
poll(H&& handler)
noexcept {
263 std::size_t total = 0;
264 for (
auto& s : sockets_) total += poll_socket(s, handler);
269 [[nodiscard]] std::span<const int>
fds() const noexcept {
return fds_; }
273 [[nodiscard]] const
XdpStats&
stats() const noexcept {
return stats_; }
274 [[nodiscard]] std::span<const XdpSocketStats>
socket_stats() const noexcept {
275 return socket_stats_;
277 [[nodiscard]] std::span<const XdpInterfaceStatus>
interfaces() const noexcept {
281 [[nodiscard]]
XdpFilter&
filter(std::size_t i)
noexcept {
return filters_[i]; }
285 std::size_t poll_socket(detail::XskSocket& s, H& handler)
noexcept {
286 std::uint32_t rx_idx = 0;
287 const std::uint32_t n = s.rx.peek(batch_, rx_idx);
289 if (busy_poll_ || s.fill.needs_wakeup()) wake(s);
295 std::uint32_t fill_idx = 0;
296 const bool can_fill = s.fill.reserve(n, fill_idx) == n;
297 const auto* descs =
static_cast<const detail::XdpDescView*
>(s.rx.entries);
298 auto* fill_addrs =
static_cast<std::uint64_t*
>(s.fill.entries);
299 std::size_t delivered = 0;
300 for (std::uint32_t i = 0; i < n; ++i) {
301 const detail::XdpDescView d = descs[(rx_idx + i) & s.rx.mask];
302 if (can_fill) fill_addrs[(fill_idx + i) & s.fill.mask] = d.addr & frame_mask_;
303 if (FASTMM_UNLIKELY(d.addr > s.umem_len || d.len > s.umem_len - d.addr)) {
307 const std::span<const std::byte> frame(s.umem + d.addr, d.len);
308 const UdpFrame f = parse_udp_frame(frame, verify_checksums_);
309 if (FASTMM_UNLIKELY(f.status != FrameStatus::Ok)) {
313 const detail::XdpRoute* route =
nullptr;
314 for (std::uint32_t r = s.routes_begin; r < s.routes_end; ++r) {
315 if (routes_[r].dst_ip == f.dst_ip && routes_[r].dst_port == f.dst_port) {
320 if (FASTMM_UNLIKELY(route ==
nullptr)) {
324 meta.src_ip = f.src_ip;
325 meta.dst_ip = f.dst_ip;
326 meta.dst_port = f.dst_port;
327 meta.line = route->line;
328 handler(f.payload,
static_cast<const RxMeta&
>(meta));
330 stats_.
bytes += f.payload.size();
342 static void wake(
const detail::XskSocket& s)
noexcept {
343 ::recvfrom(s.fd,
nullptr, 0, MSG_DONTWAIT,
nullptr,
nullptr);
346 int fail(
int err, std::string msg);
347 int open_interface(std::size_t iface,
348 std::span<const std::uint32_t> listed,
349 std::uint32_t routes_begin,
350 std::uint32_t routes_end,
351 const XdpConfig& cfg);
352 int create_socket(detail::XskSocket& s,
bool zerocopy,
const XdpConfig& cfg, std::string& why);
353 static void close_socket(detail::XskSocket& s)
noexcept;
355 std::vector<detail::XskSocket> sockets_;
356 std::vector<detail::XdpRoute> routes_;
357 std::vector<int> fds_;
358 std::vector<XdpFilter> filters_;
359 std::vector<int> join_fds_;
360 std::vector<XdpInterfaceStatus> interfaces_;
361 std::vector<XdpSocketStats> socket_stats_;
362 std::vector<std::string> warnings_;
365 std::uint64_t frame_mask_ = 0;
366 std::uint32_t batch_ = 64;
367 bool busy_poll_ =
false;
368 bool verify_checksums_ =
false;
371static_assert(DatagramSource<XdpDatagramSource>);
Definition xdp_datagram_source.hpp:244
std::span< const XdpSocketStats > socket_stats() const noexcept
Definition xdp_datagram_source.hpp:276
XdpDatagramSource()=default
XdpFilter & filter(std::size_t i) noexcept
The filter of interfaces()[i] (tests run BPF_PROG_TEST_RUN on the attached program).
Definition xdp_datagram_source.hpp:283
std::span< const int > fds() const noexcept
XDP socket fds, readable when their RX ring has descriptors (adaptive mode waits on them).
Definition xdp_datagram_source.hpp:271
std::size_t poll(H &&handler) noexcept
Delivers up to batch datagrams per socket; returns the number delivered.
Definition xdp_datagram_source.hpp:264
std::span< const std::string > warnings() const noexcept
Busy-poll options the kernel refused at open (the sockets poll from user space without them).
Definition xdp_datagram_source.hpp:260
XdpDatagramSource(const XdpDatagramSource &)=delete
XdpDatagramSource & operator=(const XdpDatagramSource &)=delete
int refresh_stats() noexcept
Reads XDP_STATISTICS and the fallback counters (system calls; not for the hot loop).
std::span< const XdpInterfaceStatus > interfaces() const noexcept
Definition xdp_datagram_source.hpp:279
int open(const XdpConfig &cfg)
Returns 0 or -errno; error() explains.
~XdpDatagramSource()
Definition xdp_datagram_source.hpp:247
const std::string & error() const noexcept
Definition xdp_datagram_source.hpp:258
const XdpStats & stats() const noexcept
Definition xdp_datagram_source.hpp:275
The BPF objects of one interface: subscription hash map, XSKMAP, per-CPU fallback counter and the pro...
Definition xdp_datagram_source.hpp:117
int set_socket(std::uint32_t queue, int xsk_fd) noexcept
xsks[queue] = xsk_fd. The socket must be bound.
XdpFilter & operator=(const XdpFilter &)=delete
int create(std::span< const xdp::Key > keys, std::uint32_t xsk_slots, std::string &err)
Creates the maps (XSKMAP with xsk_slots entries), inserts keys and loads the program.
XdpFilter(const XdpFilter &)=delete
int fallback_count(std::uint64_t &out) noexcept
Sum of the per-CPU fallback counters.
int test_run(std::span< const std::byte > frame, std::uint32_t &action) noexcept
BPF_PROG_TEST_RUN on frame (at least 14 bytes).
int prog_fd() const noexcept
Definition xdp_datagram_source.hpp:149
int link_fd() const noexcept
Definition xdp_datagram_source.hpp:150
int attach(unsigned ifindex, bool generic) noexcept
BPF_LINK_CREATE (attach type BPF_XDP) on ifindex with XDP_FLAGS_DRV_MODE or XDP_FLAGS_SKB_MODE.
~XdpFilter()
Definition xdp_datagram_source.hpp:120
Datagram receive interface for multicast market data (ADR-0015, section 1).
Definition reactor.hpp:37
XdpMode
Attach and bind mode.
Definition xdp_datagram_source.hpp:38
std::string_view to_string(ReactorBackend backend) noexcept
"epoll" / "io_uring", the spelling of [engine] net_backend.
std::string xdp_capability_error()
"" when the process has CAP_NET_ADMIN, CAP_NET_RAW, CAP_BPF (or CAP_SYS_ADMIN) and CAP_IPC_LOCK in it...
std::string xdp_kernel_error()
"" on Linux 5.11 or later; otherwise the reason.
constexpr Fixed< T > max(Fixed< T > a, Fixed< T > b) noexcept
Definition fixed_point.hpp:418
Timestamp wall_now() noexcept
CLOCK_REALTIME via vDSO (~20 ns). Implemented in src/core/time.cpp.
FASTMM_FORCE_INLINE Cycles rdtscp() noexcept
Serialising read (rdtscp) is what latency hops want: it waits for prior loads to retire.
Definition time.hpp:66
std::int64_t ns
Definition time.hpp:46
Definition xdp_datagram_source.hpp:66
int busy_poll_budget
Definition xdp_datagram_source.hpp:79
bool verify_udp_checksum
IPv4 header and UDP checksums in user space (a zero UDP checksum is accepted).
Definition xdp_datagram_source.hpp:81
std::uint32_t frame_size
2048 or 4096 (at most the page size)
Definition xdp_datagram_source.hpp:72
int busy_poll_usec
Definition xdp_datagram_source.hpp:78
std::vector< XdpSubscription > subscriptions
line = index, at most 256
Definition xdp_datagram_source.hpp:67
bool busy_poll
SO_PREFER_BUSY_POLL, SO_BUSY_POLL and SO_BUSY_POLL_BUDGET on every socket, and a recvfrom on every po...
Definition xdp_datagram_source.hpp:77
std::uint32_t frame_count
UMEM frames per socket, power of two.
Definition xdp_datagram_source.hpp:71
std::uint32_t batch
RX descriptors per socket per poll.
Definition xdp_datagram_source.hpp:73
XdpMode mode
Definition xdp_datagram_source.hpp:74
std::vector< XdpInterfaceQueues > queues
RX queues per interface.
Definition xdp_datagram_source.hpp:70
Definition xdp_datagram_source.hpp:61
std::vector< std::uint32_t > queues
Definition xdp_datagram_source.hpp:63
std::string interface
Definition xdp_datagram_source.hpp:62
Definition xdp_datagram_source.hpp:102
XdpMode mode
the mode open() settled on
Definition xdp_datagram_source.hpp:105
unsigned ifindex
Definition xdp_datagram_source.hpp:104
std::string name
Definition xdp_datagram_source.hpp:103
std::uint64_t fallback_packets
subscribed, but no socket on the RX queue: XDP_PASS
Definition xdp_datagram_source.hpp:106
XDP_STATISTICS of one socket, as of the last refresh_stats().
Definition xdp_datagram_source.hpp:93
std::uint64_t rx_invalid_descs
Definition xdp_datagram_source.hpp:97
std::uint64_t rx_fill_ring_empty_descs
Definition xdp_datagram_source.hpp:99
std::uint64_t rx_ring_full
Definition xdp_datagram_source.hpp:98
unsigned ifindex
Definition xdp_datagram_source.hpp:94
std::uint32_t queue
Definition xdp_datagram_source.hpp:95
std::uint64_t rx_dropped
Definition xdp_datagram_source.hpp:96
Definition xdp_datagram_source.hpp:84
std::uint64_t unmatched
valid, but no subscription on that interface
Definition xdp_datagram_source.hpp:88
std::uint64_t fill_short
descriptors not returned because the fill ring was full
Definition xdp_datagram_source.hpp:89
std::uint64_t datagrams
delivered to the handler
Definition xdp_datagram_source.hpp:85
std::uint64_t bytes
payload bytes delivered
Definition xdp_datagram_source.hpp:86
std::uint64_t bad_frames
failed the user-space checks (udp_frame.hpp)
Definition xdp_datagram_source.hpp:87
std::string interface
netdev name
Definition xdp_datagram_source.hpp:55
std::uint32_t group
IPv4 multicast group or local unicast address, network byte order.
Definition xdp_datagram_source.hpp:56
std::uint32_t source
network byte order; 0 = any. Joins only: the XDP map ignores it.
Definition xdp_datagram_source.hpp:58
std::uint16_t port
host byte order
Definition xdp_datagram_source.hpp:57