// Copyright (2019-2022) Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-or-later #include "IBReceiver.h" #include #ifdef JFJOCH_USE_NUMA #include #endif #ifdef JFJOCH_USE_NUMA_H #include #endif #include "../common/JFJochException.h" #include "RawJFUDPPacket.h" #define BUFFER_SIZE 16384 #define BUFFER_COUNT 4096 IBReceiverBuffer::IBReceiverBuffer(uint8_t numa_node) { buffer = (uint8_t *) mmap(nullptr, BUFFER_SIZE * BUFFER_COUNT, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (buffer == nullptr) throw JFJochException(JFJochExceptionCategory::MemAllocFailed, "frame_buffer"); #ifdef JFJOCH_USE_NUMA if (numa_node >= 0) { unsigned long nodemask = 1L << numa_node;; if (numa_node > sizeof(nodemask)*8) { Unmap(); throw JFJochException(JFJochExceptionCategory::MemAllocFailed, "Mask too small for NUMA node"); } if (mbind(buffer, BUFFER_SIZE * BUFFER_COUNT, MPOL_BIND, &nodemask, sizeof(nodemask)*8, MPOL_MF_STRICT) == -1) { Unmap(); throw JFJochException(JFJochExceptionCategory::MemAllocFailed, "Cannot apply NUMA policy"); } } #endif memset(buffer, 0, BUFFER_SIZE * BUFFER_COUNT); } void IBReceiverBuffer::Unmap() { munmap(buffer, BUFFER_SIZE * BUFFER_COUNT); } IBReceiverBuffer::~IBReceiverBuffer() { mr.reset(); Unmap(); } void IBReceiverBuffer::Register(IBProtectionDomain &pd) { mr = std::make_unique(pd, buffer, BUFFER_SIZE * BUFFER_COUNT); } IBMemoryRegion *IBReceiverBuffer::GetMemoryRegion() { return mr.get(); } uint8_t *IBReceiverBuffer::GetLocation(uint64_t location) { if (location < BUFFER_COUNT) return buffer + BUFFER_SIZE * location; else throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, "Location out of bounds"); } IBReceiver::IBReceiver(IBContext &context, ProcessJFPacket &process, uint64_t mac_addr, uint32_t ipv4, uint32_t nthreads, uint8_t numa_node) : pd(context), cq(context, BUFFER_COUNT), qp(pd, cq, 16, BUFFER_COUNT), buffer(numa_node) { buffer.Register(pd); qp.FlowSteering(mac_addr, ipv4); for (int i = 0; i < BUFFER_COUNT - 1; i++) qp.PostReceiveWR(*buffer.GetMemoryRegion(), i, buffer.GetLocation(i), BUFFER_SIZE); for (int i = 0; i < nthreads; i++) futures.emplace_back(std::async(std::launch::async, &IBReceiver::Run, this, &process, numa_node)); futures.emplace_back(std::async(std::launch::async, &IBReceiver::Arp, this, mac_addr, ipv4)); } void IBReceiver::Run(ProcessJFPacket *process, uint8_t numa_node) { #ifdef JFJOCH_USE_NUMA_H if (numa_available() != -1) numa_run_on_node(numa_node); #endif while (!cancel) { int64_t i; size_t size; if (cq.Poll(i, size) > 0) { if (size == sizeof(RawJFUDPacket)) { auto ptr = (RawJFUDPacket *) buffer.GetLocation(i); process->ProcessPacket(&ptr->jf, ptr->ipv4_header_sour_ip); qp.PostReceiveWR(*buffer.GetMemoryRegion(), i, buffer.GetLocation(i), BUFFER_SIZE); } } else std::this_thread::sleep_for(std::chrono::microseconds(10)); } } // ARP packet - from if_arp.h #pragma pack(push) #pragma pack(2) struct RAW_ARP_Packet { unsigned char dest_mac[6]; unsigned char sour_mac[6]; uint16_t ether_type; unsigned short int ar_hrd; /* Format of hardware address. */ unsigned short int ar_pro; /* Format of protocol address. */ unsigned char ar_hln; /* Length of hardware address. */ unsigned char ar_pln; /* Length of protocol address. */ unsigned short int ar_op; /* ARP opcode (command). */ unsigned char __ar_sha[6]; /* Sender hardware address. */ unsigned __ar_sip; /* Sender IP address. */ unsigned char __ar_tha[6]; /* Target hardware address. */ unsigned __ar_tip; /* Target IP address. */ }; #pragma pack(pop) void IBReceiver::Arp(uint64_t mac_addr, uint32_t ipv4_addr) { uint8_t src_mac[6]; for (int i = 0; i < 6; i++) src_mac[i] = (mac_addr & (0xFF << (i * 8))) >> (i * 8); RAW_ARP_Packet arp_packet{ .dest_mac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, .sour_mac = {src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5]}, .ether_type = 0x0608, // ether type for ARP .ar_hrd = 0x0001, // MAC addr .ar_pro = 0x0800, // IPv4 .ar_hln = 0x6, .ar_pln = 0x4, .ar_op = 0x0100, // request .__ar_sha = {src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5]}, .__ar_sip = ipv4_addr, .__ar_tha = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, .__ar_tip = ipv4_addr }; while (!cancel) { qp.PostInlineSendWR(&arp_packet, sizeof(RAW_ARP_Packet)); std::this_thread::sleep_for(std::chrono::milliseconds(200)); } } IBReceiver::~IBReceiver() { cancel = true; for (auto &iter: futures) { if (iter.valid()) iter.get(); } }