// Copyright (2019-2023) Paul Scherrer Institute #include "LinuxSocketDevice.h" #include #include #include #include #include "../common/NetworkAddressConvert.h" LinuxSocketDevice::LinuxSocketDevice(uint32_t in_ipv4_addr, uint16_t in_udp_port, uint16_t data_stream, size_t in_frame_buffer_size_modules, int32_t in_rcv_buf_size, int16_t in_numa_node) : AcquisitionDevice(data_stream), udp_port(in_udp_port), numa_node(in_numa_node), rcv_buf_size(in_rcv_buf_size) { ipv4_addr = in_ipv4_addr; max_modules = 16; MapBuffersStandard(in_frame_buffer_size_modules, 1, numa_node); mac_addr = 0; FindMACAddress(); } void LinuxSocketDevice::MeasureThread(int fd) { jf_udp_payload jf{}; work_completion_queue.Put(Completion{ .type = Completion::Type::Start }); try { ProcessJFPacket process(work_completion_queue, work_request_queue, max_modules); while (!cancel) { auto count = recv(fd, &jf, sizeof(jf_udp_payload), 0); if (count == sizeof(jf_udp_payload)) { process.ProcessPacket(&jf); } else if ((count == -1) && (errno != EAGAIN) && (errno != EWOULDBLOCK)) throw JFJochException(JFJochExceptionCategory::UDPError, "Error in UDP receiving"); } } catch (const JFJochException &e) { if (logger) logger->ErrorException(e); } // End message should be sent always work_completion_queue.Put(Completion{ .type = Completion::Type::End }); close(fd); } void LinuxSocketDevice::Start(const DiffractionExperiment& experiment) { if (experiment.GetConversionOnFPGA()) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Conversion on CPU flag has to be enabled for Raw Ethernet device"); int fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) throw JFJochException(JFJochExceptionCategory::UDPError, "Cannot create UDP socket"); sockaddr_in server_addr{ .sin_family = AF_INET, .sin_port = htons(udp_port), .sin_addr = {.s_addr = ipv4_addr} }; timeval timeout{ .tv_sec = 0, .tv_usec = 10000 // 10 ms }; if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) throw JFJochException(JFJochExceptionCategory::UDPError, "Cannot set socket timeout"); if (rcv_buf_size > 0) { if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_buf_size, sizeof(rcv_buf_size)) != 0) throw JFJochException(JFJochExceptionCategory::UDPError, "Cannot set receive buffer size"); } if (bind(fd, (struct sockaddr *) &server_addr, sizeof(server_addr)) != 0) throw JFJochException(JFJochExceptionCategory::UDPError, "Cannot bind to UDP port"); cancel = false; measure = std::async(std::launch::async, &LinuxSocketDevice::MeasureThread, this, fd); } void LinuxSocketDevice::Cancel() { cancel = true; } void LinuxSocketDevice::Finalize() { if (measure.valid()) measure.get(); } int32_t LinuxSocketDevice::GetNUMANode() const { return numa_node; } uint16_t LinuxSocketDevice::GetUDPPort() const { return udp_port; } void LinuxSocketDevice::FindMACAddress() { std::string ifname; bool found = false; ifaddrs *ifaddr; if (getifaddrs(&ifaddr) == -1) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "getifaddrs error"); for (ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family == AF_INET) { if (((sockaddr_in *) ifa->ifa_addr)->sin_addr.s_addr == ipv4_addr) { ifname = std::string(ifa->ifa_name); found = true; } } } if (!found) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "IPv4 address not found"); found = false; for (ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { if ((std::string(ifa->ifa_name) == ifname) && ((ifa->ifa_addr->sa_family == AF_PACKET))) { memcpy(&mac_addr, ((sockaddr_ll *) ifa->ifa_addr)->sll_addr, sizeof(uint64_t)); found = true; } } if (!found) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "MAC address not found"); freeifaddrs(ifaddr); }