OnixS C++ CME MDP Premium Market Data Handler 5.11.1
Users' manual and API documentation
Loading...
Searching...
No Matches
Solarflare Feed Engine

The Solarflare Feed Engine provides kernel-bypass multicast reception through Solarflare's ef_vi API for latency-sensitive applications on Linux with supported Solarflare network adapters.

The OnixS::CME::MDH::SolarflareFeedEngine class implements this receive path. Construct an explicit instance and bind it to the Handler as described in Using an explicit Feed Engine.

Note
The Solarflare ef_vi API is available only for Solarflare network cards on Linux. To simplify the development process, the SDK exposes the OnixS::CME::MDH::SolarflareFeedEngine and related classes for all platforms. However, the instance creation will fail at runtime on platforms where the corresponding functionality is not supported.

The Solarflare Feed Engine does not support VLAN-tagged network traffic. The ef_vi receive path discards frames tagged with the IEEE 802.1Q or IEEE 802.1ad TPID.

The Solarflare Feed Engine requires the Onload major version it was built with: Onload 9 for the RHEL and Ubuntu packages, and Onload 7 (or 6) for the CentOS 7 packages.

Configuring the Solarflare Feed Engine

The Solarflare ef_vi API uses the concept of virtual interfaces to receive network data. The corresponding configuration parameters are available via the OnixS::CME::MDH::SolarflareFeedEngineSettings class:

  • The OnixS::CME::MDH::SolarflareFeedEngineSettings::mode defines the behaviour of the Feed Engine. The Feed Engine will fail to operate on non-Solarflare network interfaces in the conventional mode as the use of the ef_vi services on non-Solarflare network interfaces is not supported by the manufacturer and thus leads to a failure. In contrast, the mixed mode combines ef_vi with the standard socket API, thus making possible multicast packets receiving on both Solarflare and non-Solarflare network interfaces. The mixed mode gives more flexibility for the users whose operating environments are configured in a particular way (for example, when Incremental data is available on a Solarflare network adapter and recovery data comes via a non-Solarflare network interface).
  • The OnixS::CME::MDH::SolarflareFeedEngineSettings::memoryPolicy parameter controls how memory for ef_vi multicast receive buffers is allocated. The default, Native, uses regular page-aligned memory. PreferHuge tries 2 MiB HugeTLB pages before falling back to native allocation, while RequireHuge fails if 2 MiB HugeTLB allocation is unavailable. HugeTLB availability depends on operating-system support and configuration. The setting does not affect socket-based multicast or TCP recovery buffers. See Using HugeTLB pages for setup and sizing guidance.
  • The OnixS::CME::MDH::SolarflareFeedEngineSettings::receiveRingSize parameter defines the number of buffers in the virtual interface receive ring. Buffers from the receive ring are filled with network packets (a single buffer is used for a single network packet). The feed engine checks which buffers are filled with incoming data and delivers those buffers to the handlers for further processing. The receive ring must be large enough to accommodate incoming data during market data bursts.
  • The OnixS::CME::MDH::SolarflareFeedEngineSettings::cacheSize parameter sets the size of the pool of buffers used to receive and manipulate incoming data. When the buffers are put into the receive ring and filled with incoming data, the feed engine delivers the buffers to the handlers for further processing. Thus, the buffers filled with the data are kept busy until the received data is processed by handlers. In case of rapid transmission, buffers may be allocated by the feed engine quicker than they are processed by the handlers and released back to the pool. Therefore, the pool must be large enough to avoid data loss caused by an empty pool and receive ring.
  • The OnixS::CME::MDH::SolarflareFeedEngineSettings::socketBufferSize parameter establishes the size of the socket buffer used to store incoming packets before they are retrieved by the Feed Engine. This parameter is used by the Feed Engine operating in the mixed mode when ordinary sockets are used as the way of multicast data receiving on non-Solarflare network interfaces.

Using HugeTLB pages

The Solarflare Feed Engine can back its ef_vi receive-buffer pools with explicit 2 MiB Linux HugeTLB pages. A 2 MiB page covers the same memory as 512 regular 4 KiB pages, so using HugeTLB pages can reduce CPU translation lookaside buffer (TLB) misses and page-table walks while packets are received and processed. This can improve latency and reduce latency variation, particularly when many links or large buffer pools are active. The benefit depends on the hardware and workload; measure it under representative market-data traffic.

HugeTLB allocation is opt-in. Select PreferHuge to use HugeTLB pages when available while retaining a native-memory fallback:

SolarflareFeedEngineSettings settings;
settings.memoryPolicy(SolarflareFeedEngineMemoryPolicy::PreferHuge);
Note
HugeTLB pages are distinct from transparent huge pages. Enabling transparent huge pages alone does not provide the pages requested by PreferHuge or RequireHuge.

HugeTLB allocation can fail with ENOMEM when the pool has insufficient free 2 MiB pages or with EINVAL when the kernel does not support the requested 2 MiB page size. PreferHuge falls back to native page-aligned memory after such a failure; RequireHuge causes multicast-link setup to fail.

Checking HugeTLB support and availability

Check that the kernel exposes a 2 MiB HugeTLB pool and inspect its current capacity:

test -d /sys/kernel/mm/hugepages/hugepages-2048kB && echo "2 MiB HugeTLB pages are supported"
grep -H . \
/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages \
/sys/kernel/mm/hugepages/hugepages-2048kB/free_hugepages \
/sys/kernel/mm/hugepages/hugepages-2048kB/resv_hugepages \
/sys/kernel/mm/hugepages/hugepages-2048kB/surplus_hugepages

nr_hugepages is the configured pool size. free_hugepages counts pages that are not yet allocated, including pages already committed to existing mappings; resv_hugepages counts those commitments. Therefore, free_hugepages - resv_hugepages is the uncommitted capacity that a new mapping can obtain from the existing pool. The Feed Engine needs enough uncommitted pages when each ef_vi multicast link is created. The following command also shows the system default HugeTLB page size and summary counters:

grep -E 'HugePages_(Total|Free|Rsvd|Surp)|Hugepagesize' /proc/meminfo

Reserving 2 MiB pages

Reserve pages at runtime by writing the required count to the 2 MiB pool. For example, to reserve 128 pages (256 MiB):

echo 128 | sudo tee /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

Repeat the availability check and confirm that nr_hugepages reached the requested value. Runtime allocation can fall short when physical memory is fragmented. If 2 MiB is the default HugeTLB page size reported by /proc/meminfo, the equivalent setting is:

sudo sysctl -w vm.nr_hugepages=128

To retain this setting after reboot, add vm.nr_hugepages = 128 to a file under /etc/sysctl.d/ and run sudo sysctl --system. If runtime allocation is unreliable, reserve the pages at boot with the kernel parameters hugepagesz=2M hugepages=128; boot-time reservation occurs before memory becomes fragmented. See the Linux HugeTLB documentation for kernel configuration, boot-loader, and NUMA-specific options.

Each ef_vi multicast link requires the following number of 2 MiB pages for its receive-buffer pool:

ceil(cacheSize * 2048 bytes / 2 MiB)

The HugeTLB mapping length is rounded up to whole 2 MiB pages; this does not increase cacheSize or the memory region registered with ef_vi. Because each receive buffer occupies 2048 bytes, use a cacheSize that is a multiple of 1024 to avoid unused space at the end of the final HugeTLB page.

Reserve at least the sum of this value for every ef_vi multicast link that the engine may allocate, plus capacity required by other HugeTLB users. A handler allocates links for all of its multicast feeds during Handler::start(), before any feed connects. A channel can require up to eight links: incremental A/B, MBP recovery A/B, MBO recovery A/B, and instrument-definition A/B. Include all handlers that can use the engine when sizing the pool, rather than counting only connected feeds.

Physical addressing mode

By default, ef_vi uses a finite buffer table on the network adapter to map registered packet memory. Each ef_vi multicast link created by the Solarflare Feed Engine has its own registered receive-buffer pool. A deployment with many links or large OnixS::CME::MDH::SolarflareFeedEngineSettings::cacheSize values can exhaust the available buffer table entries and cause multicast-link initialization to fail while registering the buffer pool.

Physical addressing mode requires permission from the sfc_char driver. For a non-root deployment, create a dedicated group for applications that may use physical mode and add the application user to it. For example:

sudo groupadd --system efvi-phys
sudo usermod --append --groups efvi-phys <application-user>
getent group efvi-phys

Use the numeric group ID reported by getent to create a modprobe configuration such as /etc/modprobe.d/sfc_char.conf:

options sfc_char phys_mode_gid=<gid>

Apply the new value at runtime. It takes effect for protection domains created afterwards:

echo <gid> | sudo tee /sys/module/sfc_char/parameters/phys_mode_gid

Alternatively, stop all applications using ef_vi or Onload and reload the Onload driver stack so that the modprobe configuration is re-read:

sudo onload_tool reload --onload-only

The user must start a new login session after being added to the group. Confirm that the group is listed by id before starting the application.

After configuring the driver permission, set EF_VI_PD_FLAGS to phys to select physical addressing mode. The setting must be present in the application's environment before the Solarflare Feed Engine creates its protection domains. It can be applied to a single invocation:

EF_VI_PD_FLAGS=phys ./application

Alternatively, export it before starting the application:

export EF_VI_PD_FLAGS=phys
./application

Without the matching phys_mode_gid permission, the driver rejects protection-domain allocation for a non-root process. The Feed Engine then fails to initialize in conventional mode; in mixed mode, affected links may fall back to ordinary sockets instead of using ef_vi.

Physical addressing mode lets the adapter access packet buffers by their physical addresses, avoiding the buffer-table address-translation limit. Use it when the default mode cannot register all receive-buffer memory required by the configured links. Leave the variable unset when the default buffer-table capacity is sufficient.

Physical addressing mode and HugeTLB pages are independent settings. EF_VI_PD_FLAGS=phys changes how the network adapter addresses registered memory and avoids NIC buffer-table limits. HugeTLB configuration changes how Linux backs that memory and can reduce CPU translation lookaside buffer (TLB) misses. Enabling either feature does not enable or require the other.

This setting affects only links backed by ef_vi; socket-based links in mixed mode and TCP recovery connections are unaffected. See the AMD ef_vi User Guide for further details about packet-buffer addressing modes and their security implications.

Mixed mode

The mixed mode for the Solarflare Feed Engine combines the ef_vi SDK with standard network sockets. This mode allows taking advantage of using the ultra-low latency ef_vi API on Solarflare network adapters while receiving some data from non-Solarflare network interfaces using the socket API.

The following code shows how to configure data receiving when Incremental data comes via the Solarflare network adapter (eth0) and recovery data is available on a non-Solarflare network interface (eth2).

Configuring consists of two steps.

The first step is to tell the Solarflare Feed Engine to work in the mixed mode:

SolarflareFeedEngineSettings feSettings;
feSettings.mode(SolarflareFeedEngineOperatingMode::Mixed);
SolarflareFeedEngine feedEngine(feSettings);

The second step is to bind Handler's feeds to proper network interfaces:

Handler handler;
FeedSettings& feedSettings = handler.settings().feeds();
// We need to update all interfaces at once instead of assigning
// values for each feed type (Incremenal, Snapshot, Instrument, etc).
feedSettings.feedANetworkInterfaces("eth2").feedBNetworkInterfaces("eth2");
// Now, updates the settings for Incremental feeds only.
feedSettings.incrementalFeeds().feedANetworkInterfaces("eth0").feedBNetworkInterfaces("eth0");