diff --git a/dataplane/dataplane.cpp b/dataplane/dataplane.cpp index 1d5277b2..f934858d 100644 --- a/dataplane/dataplane.cpp +++ b/dataplane/dataplane.cpp @@ -39,6 +39,7 @@ #include "common/utils.h" #include "dataplane.h" #include "dataplane/sdpserver.h" +#include "dpdk.h" #include "dump_rings.h" #include "globalbase.h" #include "sock_dev.h" @@ -69,12 +70,23 @@ cDataPlane::~cDataPlane() if (mempool_log) { rte_mempool_free(mempool_log); + mempool_log = nullptr; } for (auto& [socket_id, rte_mempool] : socket_cplane_mempools) { GCC_BUG_UNUSED(socket_id); rte_mempool_free(rte_mempool); } + socket_cplane_mempools.clear(); + + // Explicitly run all object destructors and rte_free() calls while EAL is + // still valid, then shut EAL down cleanly. This prevents the implicit + // destruction of memory_manager (which happens after the destructor body) + // from calling rte_free() after EAL has already been torn down, which + // would corrupt glibc's internal linked lists and crash with + // "corrupted double-linked list". + memory_manager.cleanup(); + rte_eal_cleanup(); } eResult cDataPlane::init(const std::string& binaryPath, @@ -167,12 +179,18 @@ eResult cDataPlane::init(const std::string& binaryPath, for (auto socket : slow_sockets) { + const uint64_t cp_pool_count = CONFIG_YADECAP_MBUFS_COUNT + + config_values_.fragmentation.size + + config_values_.master_mempool_size + + 4 * CONFIG_YADECAP_PORTS_SIZE * CONFIG_YADECAP_MBUFS_BURST_SIZE + + 4 * ports.size() * config_values_.kernel_interface_queue_size; + YADECAP_LOG_INFO("rte_mempool_create(cp-%u): count=%lu, elem_size=%u, total_approx=%lu MB\n", + socket, + cp_pool_count, + CONFIG_YADECAP_MBUF_SIZE, + cp_pool_count * CONFIG_YADECAP_MBUF_SIZE / (1024 * 1024)); auto pool = rte_mempool_create(("cp-" + std::to_string(socket)).c_str(), - CONFIG_YADECAP_MBUFS_COUNT + - config_values_.fragmentation.size + - config_values_.master_mempool_size + - 4 * CONFIG_YADECAP_PORTS_SIZE * CONFIG_YADECAP_MBUFS_BURST_SIZE + - 4 * ports.size() * config_values_.kernel_interface_queue_size, + cp_pool_count, CONFIG_YADECAP_MBUF_SIZE, 0, sizeof(struct rte_pktmbuf_pool_private), @@ -185,6 +203,7 @@ eResult cDataPlane::init(const std::string& binaryPath, if (!pool) { YADECAP_LOG_ERROR("rte_mempool_create(): %s [%u]\n", rte_strerror(rte_errno), rte_errno); + memory_manager.debug(socket); return eResult::errorAllocatingMemory; } socket_cplane_mempools.emplace(socket, pool); @@ -707,11 +726,33 @@ void cDataPlane::StartInterfaces() { for (auto& [portid, handles] : kni_interface_handles) { + // Read the actual MAC after rte_eth_dev_start(): for some drivers + // the MAC only becomes valid (or may change) once the port is started. + auto actual_mac = dpdk::GetMacAddress(portid); + if (!actual_mac) + { + YANET_LOG_ERROR("Failed to get MAC for port belonging to %s", std::get<0>(ports.at(portid)).c_str()); + std::abort(); + } + if (!handles.Start()) { YANET_LOG_ERROR("Failed to start kni interfaces"); std::abort(); } + + // Sync the actual MAC to all KNI interfaces, because it may differ + // from what was used at vdev creation time (before the port start). + // MAC must be changed while the interface is DOWN, hence before SetUp(). + if (!handles.forward.SyncMac(*actual_mac) || + !handles.in_dump.SyncMac(*actual_mac) || + !handles.out_dump.SyncMac(*actual_mac) || + !handles.drop_dump.SyncMac(*actual_mac)) + { + YANET_LOG_ERROR("Failed to sync MAC on kni interfaces belonging to %s", std::get<0>(ports.at(portid)).c_str()); + std::abort(); + } + if (!handles.forward.SetUp()) { YANET_LOG_ERROR("Failed to set kni interface belonging to %s up", std::get<0>(ports.at(portid)).c_str()); diff --git a/dataplane/kernel_interface_handle.cpp b/dataplane/kernel_interface_handle.cpp index 34a10d89..1d2a583b 100644 --- a/dataplane/kernel_interface_handle.cpp +++ b/dataplane/kernel_interface_handle.cpp @@ -1,6 +1,8 @@ #include +#include #include #include +#include #include @@ -9,6 +11,42 @@ namespace dataplane { +bool KernelInterfaceHandle::SyncMac(const common::mac_address_t& addr) const noexcept +{ + // Skip zero/invalid MAC (e.g. sock_dev leaves it all-zeros). Setting a + // zero MAC via SIOCSIFHWADDR fails with EINVAL on most drivers. + if (addr.is_default()) + { + YANET_LOG_WARNING("SyncMac: skipping zero MAC for interface %s\n", name_.data()); + return true; + } + + // NOTE: MAC must be changed while the interface is DOWN. This method is + // therefore expected to be called before SetUp() (which raises IFF_UP). + int sock = ::socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + { + YANET_LOG_ERROR("SyncMac: failed to open socket for interface %s\n", name_.data()); + return false; + } + + struct ifreq request; + memset(&request, 0, sizeof(request)); + strncpy(request.ifr_name, name_.data(), IFNAMSIZ - 1); + request.ifr_hwaddr.sa_family = ARPHRD_ETHER; + memcpy(request.ifr_hwaddr.sa_data, addr.data(), RTE_ETHER_ADDR_LEN); + + if (ioctl(sock, SIOCSIFHWADDR, &request) < 0) + { + YANET_LOG_ERROR("SyncMac: failed to set MAC on interface %s\n", name_.data()); + ::close(sock); + return false; + } + + ::close(sock); + return true; +} + bool KernelInterfaceHandle::SetUp() const noexcept { int socket = ::socket(AF_INET, SOCK_DGRAM, 0); @@ -20,14 +58,16 @@ bool KernelInterfaceHandle::SetUp() const noexcept struct ifreq request; memset(&request, 0, sizeof request); - strncpy(request.ifr_name, name_.data(), IFNAMSIZ); + strncpy(request.ifr_name, name_.data(), IFNAMSIZ - 1); request.ifr_flags |= IFF_UP; if (auto res = ioctl(socket, SIOCSIFFLAGS, &request)) { YANET_LOG_ERROR("failed to set interface %s up, ioctl returned (%d)", name_.data(), res); + ::close(socket); return false; } + ::close(socket); return true; } diff --git a/dataplane/kernel_interface_handle.h b/dataplane/kernel_interface_handle.h index bad5ef26..987ac4c3 100644 --- a/dataplane/kernel_interface_handle.h +++ b/dataplane/kernel_interface_handle.h @@ -30,6 +30,7 @@ class KernelInterfaceHandle [[nodiscard]] const tPortId& Id() const noexcept { return kni_port_; } [[nodiscard]] bool Start() const noexcept; [[nodiscard]] bool SetUp() const noexcept; + [[nodiscard]] bool SyncMac(const common::mac_address_t& addr) const noexcept; bool SetupRxQueue(tQueueId queue, tSocketId socket, rte_mempool* mempool) noexcept; bool SetupTxQueue(tQueueId queue, tSocketId socket) noexcept; bool CloneMTU(const uint16_t) noexcept; diff --git a/dataplane/memory_manager.cpp b/dataplane/memory_manager.cpp index 27288a61..de27b9a6 100644 --- a/dataplane/memory_manager.cpp +++ b/dataplane/memory_manager.cpp @@ -145,6 +145,17 @@ void memory_manager::destroy(void* pointer) pointers.erase(it); } +void memory_manager::cleanup() +{ + std::lock_guard guard(mutex); + + // Explicitly destroy all tracked objects and free hugepage memory in a + // controlled order, before rte_eal_cleanup() is called. Clearing the map + // here means the default destructor of memory_manager will find it empty + // and will not attempt any rte_free() calls after EAL has been torn down. + pointers.clear(); +} + void memory_manager::debug(tSocketId socket_id) { rte_malloc_socket_stats stats; diff --git a/dataplane/memory_manager.h b/dataplane/memory_manager.h index ccbd1c39..2a54dc66 100644 --- a/dataplane/memory_manager.h +++ b/dataplane/memory_manager.h @@ -151,6 +151,7 @@ class memory_manager } void destroy(void* pointer); + void cleanup(); void debug(tSocketId socket_id); bool check_memory_limit(const std::string& name, const uint64_t size); Deleter deleter() { return Deleter{this}; } diff --git a/dataplane/worker.cpp b/dataplane/worker.cpp index 971667ff..52e7c53e 100644 --- a/dataplane/worker.cpp +++ b/dataplane/worker.cpp @@ -101,10 +101,6 @@ eResult cWorker::init(const tCoreId& coreId, const dataplane::base::permanently& basePermanently, const dataplane::base::generation& base) { - YADECAP_LOG_DEBUG("rte_mempool_create(coreId: %u, socketId: %u)\n", - coreId, - rte_lcore_to_socket_id(coreId)); - this->coreId = coreId; this->socketId = rte_lcore_to_socket_id(coreId); this->basePermanently = basePermanently; @@ -113,7 +109,12 @@ eResult cWorker::init(const tCoreId& coreId, unsigned int elements_count = MempoolSize(); - YADECAP_LOG_DEBUG("elements_count: %u\n", elements_count); + YADECAP_LOG_INFO("rte_mempool_create(fp%u, socketId: %u): count=%u, elem_size=%u, total_approx=%lu MB\n", + coreId, + socketId, + elements_count, + CONFIG_YADECAP_MBUF_SIZE, + (uint64_t)elements_count * CONFIG_YADECAP_MBUF_SIZE / (1024 * 1024)); /// init mempool mempool = rte_mempool_create(("fp" + std::to_string(coreId)).data(), @@ -130,6 +131,7 @@ eResult cWorker::init(const tCoreId& coreId, if (!mempool) { YADECAP_LOG_ERROR("rte_mempool_create(): %s [%u]\n", rte_strerror(rte_errno), rte_errno); + dataPlane->memory_manager.debug(socketId); return eResult::errorInitMempool; } diff --git a/dataplane/worker_gc.cpp b/dataplane/worker_gc.cpp index 66c20434..49396edf 100644 --- a/dataplane/worker_gc.cpp +++ b/dataplane/worker_gc.cpp @@ -52,8 +52,15 @@ eResult worker_gc_t::init(const tCoreId& core_id, this->bases[local_base_id] = base; this->bases[local_base_id ^ 1] = base; + const uint64_t wgc_pool_count = CONFIG_YADECAP_MBUFS_COUNT + 3 * CONFIG_YADECAP_PORTS_SIZE * CONFIG_YADECAP_MBUFS_BURST_SIZE; + YADECAP_LOG_INFO("rte_mempool_create(wgc%u, socketId: %u): count=%lu, elem_size=%u, total_approx=%lu MB\n", + core_id, + socket_id, + wgc_pool_count, + CONFIG_YADECAP_MBUF_SIZE, + wgc_pool_count * CONFIG_YADECAP_MBUF_SIZE / (1024 * 1024)); mempool = rte_mempool_create(("wgc" + std::to_string(core_id)).data(), - CONFIG_YADECAP_MBUFS_COUNT + 3 * CONFIG_YADECAP_PORTS_SIZE * CONFIG_YADECAP_MBUFS_BURST_SIZE, + wgc_pool_count, CONFIG_YADECAP_MBUF_SIZE, 0, sizeof(struct rte_pktmbuf_pool_private),