FastMM documentation
FastMM C++ API 0.6.0
Public headers, tier 1 and tier 2
Loading...
Searching...
No Matches
kernel_datagram_source.hpp
Go to the documentation of this file.
1#pragma once
2// `kernel` DatagramSource (ADR-0015, section 2): one UDP socket per subscription, bound to the
3// group and port, SO_REUSEADDR, IP_MULTICAST_ALL off, joined any-source or source-specific (a
4// unicast "group" is a local address: bound, not joined);
5// recvmmsg in fixed batches; SO_TIMESTAMPING for kernel and NIC receive times. Runs unmodified
6// under Onload.
7//
8// open() allocates every buffer (headers, iovecs, addresses, control messages, payload slots:
9// lines x batch x max_datagram bytes); poll() does not allocate. poll() makes one recvmmsg call
10// per line and returns the datagrams delivered; with an edge-triggered reactor, call it until it
11// returns 0 (every socket reported EAGAIN).
12//
13// NIC timestamps need SIOCSHWTSTAMP on the interface first (enable_hw_timestamps(), CAP_NET_ADMIN);
14// SOF_TIMESTAMPING_RX_HARDWARE alone does nothing.
17
18#include <netinet/in.h>
19#include <sys/socket.h>
20#include <sys/uio.h>
21
22#include <cstddef>
23#include <cstdint>
24#include <span>
25#include <string>
26#include <string_view>
27#include <type_traits>
28#include <vector>
29
30namespace fastmm::net {
31
32struct DatagramSubscription {
33 std::string interface; // name ("eth1") or IPv4 address; "" = routing table
34 std::string group; // IPv4 multicast group, or a local unicast address
35 std::uint16_t port = 0;
36 std::string source; // "" = any source; otherwise a source-specific join (multicast only)
37};
39struct KernelSourceConfig {
40 std::vector<DatagramSubscription> subscriptions; // line = index, at most 256
41 int rcvbuf_bytes = 0; // 0 keeps the system default
42 std::uint32_t batch = 32; // datagrams per recvmmsg, 1..1024
43 std::uint32_t max_datagram = 2048; // larger datagrams are dropped (truncated)
44 bool timestamps = true; // SO_TIMESTAMPING
45 // SO_BUSY_POLL / SO_PREFER_BUSY_POLL / SO_BUSY_POLL_BUDGET on every socket. Failures do not
46 // fail open(); they are reported in KernelSourceOpen.
47 bool busy_poll = false;
48 int busy_poll_usec = 50;
49 int busy_poll_budget = 0; // 0 keeps the kernel default (8)
50};
52// Outcome of open(). `err` is 0 or -errno of the step that failed, for subscription `line`.
53struct KernelSourceOpen {
54 int err = 0;
55 std::size_t line = 0;
56 std::string_view step; // "config", "interface", "group", "source", "socket", "bind", ...
57 // Busy-poll options: 0 when applied to every socket, else the first -errno (-EPERM without
58 // CAP_NET_ADMIN). Only set when busy_poll was requested.
59 int busy_poll = 0;
60 int prefer_busy_poll = 0;
62 int rcvbuf = 0; // SO_RCVBUF granted on line 0, as getsockopt reports it
64 [[nodiscard]] bool ok() const noexcept { return err == 0; }
65 [[nodiscard]] bool busy_poll_applied() const noexcept {
66 return busy_poll == 0 && prefer_busy_poll == 0 && busy_poll_budget == 0;
67 }
68};
69
70class KernelDatagramSource {
71 public:
73 ~KernelDatagramSource() = default;
77 KernelDatagramSource& operator=(KernelDatagramSource&&) noexcept = default;
79 // Closes any previous sockets, then opens one per subscription. On failure nothing stays open.
81 void close() noexcept;
83 // handler(std::span<const std::byte> payload, const RxMeta& meta) noexcept, once per datagram.
84 template <class H>
85 // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward): called per datagram, never moved
86 std::size_t poll(H&& handler) noexcept {
87 static_assert(std::is_nothrow_invocable_v<H&, std::span<const std::byte>, const RxMeta&>,
88 "fastmm: DatagramSource handler must be void(std::span<const std::byte>, "
89 "const RxMeta&) noexcept");
90 std::size_t delivered = 0;
91 for (std::size_t line = 0; line < lines_.size(); ++line) {
92 const std::size_t n = receive(line);
93 const std::size_t base = line * batch_;
94 for (std::size_t i = base; i < base + n; ++i) {
95 if (lens_[i] == kDropped) continue;
96 handler(std::span<const std::byte>(payload_.data() + i * max_datagram_, lens_[i]),
97 metas_[i]);
98 ++delivered;
99 }
100 }
101 return delivered;
102 }
103
104 [[nodiscard]] const DatagramSourceStats& stats() const noexcept { return stats_; }
105 [[nodiscard]] std::size_t line_count() const noexcept { return lines_.size(); }
106 [[nodiscard]] int fd(std::size_t line) const noexcept {
107 return line < lines_.size() ? lines_[line].sock.fd() : -1;
108 }
109 [[nodiscard]] bool is_open() const noexcept { return !lines_.empty(); }
111 private:
112 static constexpr std::uint32_t kDropped = 0xFFFF'FFFFU;
113
114 struct Line {
115 UdpSocket sock;
116 std::uint32_t group_be = 0;
117 std::uint16_t port = 0;
118 };
119
120 // recvmmsg on `line`: fills lens_/metas_ for the received slots, updates stats_, resets the
121 // headers for the next call. Returns the number of slots used (dropped ones included).
122 std::size_t receive(std::size_t line) noexcept;
123
124 std::vector<Line> lines_;
125 std::size_t batch_ = 0;
126 std::size_t max_datagram_ = 0;
127 std::size_t cmsg_space_ = 0;
128 std::vector<mmsghdr> msgs_; // lines x batch; each points at its slot's buffers below
129 std::vector<iovec> iovs_;
130 std::vector<sockaddr_in> names_;
131 std::vector<std::byte> cmsgs_;
132 std::vector<std::byte> payload_;
133 std::vector<std::uint32_t> lens_;
134 std::vector<RxMeta> metas_;
135 DatagramSourceStats stats_;
136};
137
138static_assert(DatagramSource<KernelDatagramSource>);
139
140} // namespace fastmm::net
Definition kernel_datagram_source.hpp:72
bool is_open() const noexcept
Definition kernel_datagram_source.hpp:110
std::size_t poll(H &&handler) noexcept
handler(std::span<const std::byte> payload, const RxMeta& meta) noexcept, once per datagram.
Definition kernel_datagram_source.hpp:87
const DatagramSourceStats & stats() const noexcept
Definition kernel_datagram_source.hpp:105
KernelDatagramSource(const KernelDatagramSource &)=delete
int fd(std::size_t line) const noexcept
Definition kernel_datagram_source.hpp:107
KernelSourceOpen open(const KernelSourceConfig &cfg)
Closes any previous sockets, then opens one per subscription. On failure nothing stays open.
KernelDatagramSource & operator=(const KernelDatagramSource &)=delete
std::size_t line_count() const noexcept
Definition kernel_datagram_source.hpp:106
Definition udp_socket.hpp:44
Datagram receive interface for multicast market data (ADR-0015, section 1).
Definition reactor.hpp:37
Definition datagram_source.hpp:33
std::uint16_t port
Definition kernel_datagram_source.hpp:37
std::string interface
name ("eth1") or IPv4 address; "" = routing table
Definition kernel_datagram_source.hpp:35
std::string group
IPv4 multicast group, or a local unicast address.
Definition kernel_datagram_source.hpp:36
std::string source
"" = any source; otherwise a source-specific join (multicast only)
Definition kernel_datagram_source.hpp:38
Definition kernel_datagram_source.hpp:41
int busy_poll_usec
Definition kernel_datagram_source.hpp:50
std::uint32_t batch
datagrams per recvmmsg, 1..1024
Definition kernel_datagram_source.hpp:44
std::vector< DatagramSubscription > subscriptions
line = index, at most 256
Definition kernel_datagram_source.hpp:42
bool timestamps
SO_TIMESTAMPING.
Definition kernel_datagram_source.hpp:46
int rcvbuf_bytes
0 keeps the system default
Definition kernel_datagram_source.hpp:43
bool busy_poll
SO_BUSY_POLL / SO_PREFER_BUSY_POLL / SO_BUSY_POLL_BUDGET on every socket.
Definition kernel_datagram_source.hpp:49
int busy_poll_budget
0 keeps the kernel default (8)
Definition kernel_datagram_source.hpp:51
std::uint32_t max_datagram
larger datagrams are dropped (truncated)
Definition kernel_datagram_source.hpp:45
Outcome of open(). err is 0 or -errno of the step that failed, for subscription line.
Definition kernel_datagram_source.hpp:55
int busy_poll
Busy-poll options: 0 when applied to every socket, else the first -errno (-EPERM without CAP_NET_ADMI...
Definition kernel_datagram_source.hpp:61
int busy_poll_budget
Definition kernel_datagram_source.hpp:63
std::size_t line
Definition kernel_datagram_source.hpp:57
bool ok() const noexcept
Definition kernel_datagram_source.hpp:66
bool busy_poll_applied() const noexcept
Definition kernel_datagram_source.hpp:67
int prefer_busy_poll
Definition kernel_datagram_source.hpp:62
int err
Definition kernel_datagram_source.hpp:56
int rcvbuf
SO_RCVBUF granted on line 0, as getsockopt reports it.
Definition kernel_datagram_source.hpp:64
std::string_view step
"config", "interface", "group", "source", "socket", "bind", ...
Definition kernel_datagram_source.hpp:58
Definition datagram_source.hpp:22
Non-blocking IPv4 UDP socket with RAII fd ownership: multicast membership, batch receive (recvmmsg) a...