FastMM documentation
FastMM C++ API 0.6.0
Public headers, tier 1 and tier 2
Loading...
Searching...
No Matches
dpdk_datagram_source.hpp
Go to the documentation of this file.
1#pragma once
2// DPDK datagram source (rx_backend = "dpdk"): one ethdev port, one RX and one TX queue polled from
3// the venue's network thread. Frames are parsed with parse_udp_frame (Ethernet, at most one 802.1Q
4// tag, IPv4, UDP), matched against the subscriptions and handed to the handler; every mbuf of the
5// burst is freed (or passed on) before poll() returns, so payload spans are valid only inside the
6// handler call.
7//
8// Frames that are not a subscribed datagram:
9// - with an exception port (a net_tap vdev), everything else goes to the kernel through it, and
10// what the kernel sends there goes out of the port: the kernel keeps an interface with the
11// port's MAC and exception_ip (ARP, ICMP, IGMP joins, GLIMPSE, re-requests, kernel TCP). The
12// tap is read every exception_interval_ns (a system call per read).
13// - without one, ARP requests for a unicast subscription address are answered here; the rest
14// is dropped
15//
16// Multicast: a kernel UDP socket per subscription joins the group on `interface` so the IGMP
17// report goes out (the exception interface when the port is bound to vfio-pci; the port's own
18// netdev for af_packet vdevs). A unicast subscription (a local address) needs no join.
19//
20// Built only with -DFASTMM_WITH_DPDK=ON (FASTMM_HAS_DPDK=1); the header compiles without DPDK so
21// the venue can name the type. No DPDK header is included here.
22//
23// EAL: initialised once per process by the first open() with DpdkConfig::eal_args; a later open()
24// must pass the same arguments. The calling thread's CPU affinity is restored after rte_eal_init.
25// Unprivileged use (tests, bench over veth): "--no-huge --no-pci --in-memory --no-telemetry
26// --vdev=net_af_packet0,iface=<netdev>" inside a user + network namespace (CAP_NET_RAW there).
27#include "fastmm/core/config_macros.hpp"
28#include "fastmm/core/time.hpp"
30#include "fastmm/net/udp_frame.hpp"
31
32#include <array>
33#include <cstddef>
34#include <cstdint>
35#include <span>
36#include <string>
37#include <vector>
38
39namespace fastmm::net {
40
41struct DpdkSubscription {
42 std::string interface; // kernel netdev for the IGMP join; "" = no join
43 std::uint32_t group = 0; // IPv4 multicast group or local unicast address, network byte order
44 std::uint16_t port = 0; // host byte order
45 std::uint32_t source = 0; // network byte order; 0 = any (joins only)
46};
48struct DpdkConfig {
49 std::vector<std::string> eal_args; // argv[1..] of rte_eal_init
50 std::string port; // ethdev name ("net_af_packet0", "0000:00:06.0"); "" = first
51 std::vector<DpdkSubscription> subscriptions; // line = index, at most 256
52 std::uint16_t rx_descriptors = 1024;
53 std::uint16_t tx_descriptors = 512;
54 std::uint32_t mbufs = 8191;
55 std::uint32_t batch = 32; // frames per rx burst, at most 256
56 bool verify_udp_checksum = false;
57 // Kernel exception path: ethdev name of a net_tap vdev ("" = none). Its netdev gets the port's
58 // MAC and exception_ip ("a.b.c.d/len", "" = leave it unconfigured) and is brought up.
59 std::string exception_port;
60 std::string exception_ip;
61 std::int64_t exception_interval_ns = 20'000; // 0 = read the tap on every poll
62};
64struct DpdkStats {
65 std::uint64_t datagrams = 0;
66 std::uint64_t bytes = 0;
67 std::uint64_t bad_frames = 0; // truncated, bad IPv4 header, lengths or checksums, chained mbufs
68 std::uint64_t other = 0; // not IPv4 UDP (ARP, TCP, IGMP, fragments): not for this source
69 std::uint64_t unmatched = 0; // UDP, but no subscription
70 std::uint64_t to_kernel = 0; // frames passed to the exception port
71 std::uint64_t from_kernel = 0; // frames the kernel sent through the exception port
72 std::uint64_t arp_replies = 0; // answered here (no exception port)
73 std::uint64_t tx_drops = 0; // ARP replies not sent: no mbuf, or the TX queue was full
74 // rte_eth_stats_get, as of the last refresh_stats()
75 std::uint64_t ipackets = 0;
76 std::uint64_t imissed = 0;
77 std::uint64_t ierrors = 0;
78 std::uint64_t rx_nombuf = 0;
79};
81// True when this build has the DPDK backend.
82[[nodiscard]] bool dpdk_available() noexcept;
83
85 public:
86 DpdkDatagramSource() = default;
87 ~DpdkDatagramSource() { close(); }
89 DpdkDatagramSource& operator=(const DpdkDatagramSource&) = delete;
93 // Returns 0 or -errno; error() explains. -ENOTSUP without DPDK in the build.
94 int open(const DpdkConfig& cfg);
95 void close() noexcept;
96 [[nodiscard]] const std::string& error() const noexcept { return error_; }
97 [[nodiscard]] const std::string& port_name() const noexcept { return port_name_; }
98 // The exception port's netdev ("" without one).
99 [[nodiscard]] const std::string& exception_interface() const noexcept { return exc_ifname_; }
100
101 template <class H>
102 std::size_t poll(H&& handler) noexcept {
103 const std::uint32_t n = rx_burst();
104 if (n == 0) {
105 if (FASTMM_UNLIKELY(exc_ != kNoPort)) service_exception();
106 return 0;
107 }
108 RxMeta meta{};
109 meta.t0_cycles = rdtscp();
110 meta.t0_wall_ns = wall_now().ns;
111 std::size_t delivered = 0;
112 for (std::uint32_t i = 0; i < n; ++i) {
113 const UdpFrame f = parse_udp_frame(frames_[i], verify_checksums_);
114 if (FASTMM_UNLIKELY(f.status != FrameStatus::Ok)) {
115 const bool other = f.status == FrameStatus::NotIpv4 || f.status == FrameStatus::NotUdp ||
116 f.status == FrameStatus::Fragment;
117 ++(other ? stats_.other : stats_.bad_frames);
118 if (other) divert(i);
119 continue;
120 }
121 const Route* route = nullptr;
122 for (const Route& r : routes_) {
123 if (r.dst_ip == f.dst_ip && r.dst_port == f.dst_port) {
124 route = &r;
125 break;
126 }
127 }
128 if (route == nullptr) {
129 ++stats_.unmatched;
130 divert(i);
131 continue;
132 }
133 meta.src_ip = f.src_ip;
134 meta.dst_ip = f.dst_ip;
135 meta.dst_port = f.dst_port;
136 meta.line = route->line;
137 handler(f.payload, static_cast<const RxMeta&>(meta));
138 ++delivered;
139 stats_.bytes += f.payload.size();
140 }
141 free_burst(n);
142 stats_.datagrams += delivered;
143 if (FASTMM_UNLIKELY(exc_ != kNoPort)) service_exception();
144 return delivered;
145 }
146
147 // rte_eth_stats_get (not for the hot loop).
148 int refresh_stats() noexcept;
149 [[nodiscard]] const DpdkStats& stats() const noexcept { return stats_; }
151 private:
152 static constexpr std::uint16_t kNoPort = 0xFFFF;
153
154 struct Route {
155 std::uint32_t dst_ip;
156 std::uint16_t dst_port;
157 std::uint8_t line;
158 };
159 std::uint32_t rx_burst() noexcept; // fills frames_ (empty span: chained mbuf)
160 void free_burst(std::uint32_t n) noexcept;
161 // Frame i was not delivered: the exception port or an ARP answer (out of line).
162 void divert(std::uint32_t i) noexcept;
163 // Passes the diverted frames to the kernel and, when due, the kernel's frames to the port.
164 void service_exception() noexcept;
165 bool send_frame(std::span<const std::byte> frame) noexcept;
166 int open_exception(const DpdkConfig& cfg, unsigned sock);
167 int fail(int err, std::string msg);
168
169 std::vector<std::span<const std::byte>> frames_;
170 std::vector<void*> mbufs_; // rte_mbuf*
171 std::vector<void*> to_exc_; // mbufs for the exception port
172 std::vector<void*> exc_rx_; // burst read from the exception port
173 std::vector<Route> routes_;
174 std::vector<std::uint32_t> unicast_; // unicast subscription addresses (ARP answers)
175 std::vector<int> join_fds_;
176 void* pool_ = nullptr; // rte_mempool*
177 std::string error_;
178 std::string port_name_;
179 std::string exc_ifname_;
180 DpdkStats stats_{};
181 std::array<std::uint8_t, 6> mac_{};
182 std::uint32_t to_exc_n_ = 0;
183 std::uint64_t exc_interval_cycles_ = 0;
184 std::uint64_t exc_next_ = 0;
185 std::uint16_t port_ = 0;
186 std::uint16_t exc_ = kNoPort;
187 std::uint16_t batch_ = 32;
188 bool started_ = false;
189 bool exc_started_ = false;
190 bool verify_checksums_ = false;
191};
192
193static_assert(DatagramSource<DpdkDatagramSource>);
194
195} // namespace fastmm::net
Definition dpdk_datagram_source.hpp:86
DpdkDatagramSource & operator=(const DpdkDatagramSource &)=delete
DpdkDatagramSource(const DpdkDatagramSource &)=delete
Datagram receive interface for multicast market data (ADR-0015, section 1).
Definition reactor.hpp:37
bool dpdk_available() noexcept
True when this build has the DPDK backend.
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
constexpr Unexpected< std::decay_t< E > > fail(E &&e) noexcept
Definition result.hpp:25
std::int64_t ns
Definition time.hpp:46
Definition dpdk_datagram_source.hpp:50
std::string exception_ip
Definition dpdk_datagram_source.hpp:62
std::uint32_t batch
frames per rx burst, at most 256
Definition dpdk_datagram_source.hpp:57
std::string port
ethdev name ("net_af_packet0", "0000:00:06.0"); "" = first
Definition dpdk_datagram_source.hpp:52
std::vector< DpdkSubscription > subscriptions
line = index, at most 256
Definition dpdk_datagram_source.hpp:53
bool verify_udp_checksum
Definition dpdk_datagram_source.hpp:58
std::uint32_t mbufs
Definition dpdk_datagram_source.hpp:56
std::vector< std::string > eal_args
argv[1..] of rte_eal_init
Definition dpdk_datagram_source.hpp:51
std::int64_t exception_interval_ns
Definition dpdk_datagram_source.hpp:63
std::uint16_t tx_descriptors
Definition dpdk_datagram_source.hpp:55
std::uint16_t rx_descriptors
Definition dpdk_datagram_source.hpp:54
std::string exception_port
Kernel exception path: ethdev name of a net_tap vdev ("" = none).
Definition dpdk_datagram_source.hpp:61
Definition dpdk_datagram_source.hpp:66
std::uint64_t to_kernel
frames passed to the exception port
Definition dpdk_datagram_source.hpp:72
std::uint64_t bad_frames
truncated, bad IPv4 header, lengths or checksums, chained mbufs
Definition dpdk_datagram_source.hpp:69
std::uint64_t datagrams
Definition dpdk_datagram_source.hpp:67
std::uint64_t other
not IPv4 UDP (ARP, TCP, IGMP, fragments): not for this source
Definition dpdk_datagram_source.hpp:70
std::uint64_t from_kernel
frames the kernel sent through the exception port
Definition dpdk_datagram_source.hpp:73
std::uint64_t arp_replies
answered here (no exception port)
Definition dpdk_datagram_source.hpp:74
std::uint64_t ipackets
rte_eth_stats_get, as of the last refresh_stats()
Definition dpdk_datagram_source.hpp:77
std::uint64_t ierrors
Definition dpdk_datagram_source.hpp:79
std::uint64_t rx_nombuf
Definition dpdk_datagram_source.hpp:80
std::uint64_t imissed
Definition dpdk_datagram_source.hpp:78
std::uint64_t tx_drops
ARP replies not sent: no mbuf, or the TX queue was full.
Definition dpdk_datagram_source.hpp:75
std::uint64_t unmatched
UDP, but no subscription.
Definition dpdk_datagram_source.hpp:71
std::uint64_t bytes
Definition dpdk_datagram_source.hpp:68
std::uint32_t group
IPv4 multicast group or local unicast address, network byte order.
Definition dpdk_datagram_source.hpp:45
std::uint32_t source
network byte order; 0 = any (joins only)
Definition dpdk_datagram_source.hpp:47
std::uint16_t port
host byte order
Definition dpdk_datagram_source.hpp:46
std::string interface
kernel netdev for the IGMP join; "" = no join
Definition dpdk_datagram_source.hpp:44
Definition datagram_source.hpp:22
Cycles t0_cycles
rdtscp after the batch left the kernel or the RX ring
Definition datagram_source.hpp:23
Time primitives.