diff --git a/autotest/units/001_one_port/074_dynamic_allocation_acl/autotest.yaml b/autotest/units/001_one_port/074_dynamic_allocation_acl/autotest.yaml new file mode 100644 index 00000000..721c5d44 --- /dev/null +++ b/autotest/units/001_one_port/074_dynamic_allocation_acl/autotest.yaml @@ -0,0 +1,15 @@ +steps: +- cli_check: | + YANET_FORMAT_COLUMNS=group,maximum memory group + group maximum + ------------------------------ ---------- + acl.network.ht 1048576 + acl.transport.ht 67108864 + acl.total.ht 67108864 + acl.network.v4.source.lpm 71303168 + acl.network.v4.destination.lpm 71303168 + acl.network.v6.source.lpm 1073741824 + acl.network.v6.destination.ht 262144 + acl.network.v6.destination.lpm 1073741824 + acl.network 157286400 + acl 277872640 diff --git a/autotest/units/001_one_port/074_dynamic_allocation_acl/controlplane.conf b/autotest/units/001_one_port/074_dynamic_allocation_acl/controlplane.conf new file mode 100644 index 00000000..5980f5ff --- /dev/null +++ b/autotest/units/001_one_port/074_dynamic_allocation_acl/controlplane.conf @@ -0,0 +1,87 @@ +{ + "modules": { + "lp0.100": { + "type": "logicalPort", + "physicalPort": "kni0", + "vlanId": "100", + "macAddress": "00:00:00:11:11:11", + "nextModule": "acl0" + }, + "lp0.200": { + "type": "logicalPort", + "physicalPort": "kni0", + "vlanId": "200", + "macAddress": "00:00:00:22:22:22", + "nextModule": "acl0" + }, + "acl0": { + "type": "acl", + "nextModules": [ + "route0" + ] + }, + "route0": { + "type": "route", + "interfaces": { + "kni0.100": { + "ipAddresses": [ + "200.0.0.100/24" + ], + "nextModule": "lp0.100" + }, + "kni0.200": { + "ipAddresses": [ + "fe80::200/96" + ], + "nextModule": "lp0.200" + } + } + } + }, + "memory_groups": [ + { + "name": "acl", + "limit": "265M", + "memory_groups": [ + { + "name": "acl.network.ht", + "limit": "1048576" + }, + { + "name": "acl.transport.ht", + "limit": "64M" + }, + { + "name": "acl.total.ht", + "limit": "64M" + }, + { + "name": "acl.network", + "limit": "150M", + "memory_groups": [ + { + "name": "acl.network.v4.source.lpm", + "limit": "68M" + }, + { + "name": "acl.network.v4.destination.lpm", + "limit": "68M" + }, + { + "name": "acl.network.v6.source.lpm", + "limit": "1G" + }, + { + "name": "acl.network.v6.destination.ht", + "limit": "256K" + }, + { + "name": "acl.network.v6.destination.lpm", + "limit": "1G" + } + ] + } + ] + } + ] +} diff --git a/cli/helper.h b/cli/helper.h index ea82da61..c3a9c8bb 100644 --- a/cli/helper.h +++ b/cli/helper.h @@ -3,18 +3,31 @@ #include #include #include -#include #include #include #include #include -#include #include #include #include "converter.h" +template +static std::string to_percent(const type current, const type maximum) +{ + double percent = 0.0; + if (maximum) + { + percent = (double)current / (double)maximum; + percent *= (double)100; + } + + std::stringstream stream; + stream << std::fixed << std::setprecision(2) << percent; + return stream.str(); +} + std::vector split(const char* string, const char delimiter) { diff --git a/cli/main.cpp b/cli/main.cpp index 0ed1855d..414c2f1d 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -12,6 +12,7 @@ #include "helper.h" #include "latch.h" #include "limit.h" +#include "memory_manager.h" #include "nat46clat.h" #include "nat64stateful.h" #include "neighbor.h" @@ -91,6 +92,8 @@ std::vector ///< current + currents; + + for (const auto& [name, socket_id, current] : response_objects) + { + (void)socket_id; + + currents[name] = std::max(currents[name].value, + current); + } + + response_memory_group.for_each([&](const auto& memory_group, + const std::set& object_names) { + if (memory_group.name.empty()) + { + return; + } + + uint64_t group_total = 0; + for (const auto& object_name : object_names) + { + group_total += currents[object_name]; + } + + std::optional maximum; + std::optional percent; + if (memory_group.limit) + { + maximum = memory_group.limit; + percent = to_percent(group_total, memory_group.limit); + } + + table.insert(memory_group.name, + group_total, + maximum, + percent); + }); + + table.print(); +} + +} diff --git a/cli/telegraf.h b/cli/telegraf.h index 4e24f44e..1b53010f 100644 --- a/cli/telegraf.h +++ b/cli/telegraf.h @@ -102,6 +102,8 @@ void unsafe() const auto static_counters = dataplane.getCounters(vector_range(0, (tCounterId)common::globalBase::static_counter_type::size)); const auto neighbor_stats = dataplane.neighbor_stats(); + const auto memory_stats = dataplane.memory_manager_stats(); + const auto& [memory_groups, memory_objects] = memory_stats; const auto durations = controlplane.controlplane_durations(); @@ -371,6 +373,56 @@ void unsafe() {"hashtable_remove_error", neighbor_stats.hashtable_remove_error}, {"netlink_neighbor_update", neighbor_stats.netlink_neighbor_update}, {"resolve", neighbor_stats.resolve}}); + + /// memory + { + std::map ///< current + currents; + + uint64_t total = 0; + for (const auto& [name, socket_id, current] : memory_objects) + { + total += current; + + currents[name] = std::max(currents[name].value, + current); + + influxdb_format::print("memory", + {{"name", name}, + {"socket_id", socket_id}}, + {{"current", current}}); + } + + influxdb_format::print("memory", + {{"name", "total"}}, + {{"current", total}}); + + memory_groups.for_each([&](const auto& memory_group, + const std::set& object_names) { + if (memory_group.name.empty()) + { + return; + } + + uint64_t group_total = 0; + for (const auto& object_name : object_names) + { + group_total += currents[object_name]; + } + + uint64_t maximum = 0; + if (memory_group.limit) + { + maximum = memory_group.limit; + } + + influxdb_format::print("memory", + {{"group", memory_group.name}}, + {{"current", group_total}, + {"maximum", maximum}}); + }); + } } void dregress() diff --git a/common/memory_manager.h b/common/memory_manager.h index a9e0b5f2..04e994a8 100644 --- a/common/memory_manager.h +++ b/common/memory_manager.h @@ -38,6 +38,36 @@ inline uint64_t convert_string_to_bytes(std::string string) class memory_group { public: + memory_group() : + limit(0) + { + } + + template + std::set for_each(const callback_t& callback) const + { + std::set object_names; + + if (memory_groups.empty()) + { + object_names.emplace(name); + } + else + { + for (const auto& memory_group_next : memory_groups) + { + auto object_names_next = memory_group_next->for_each(callback); + for (const auto& object_name : object_names_next) + { + object_names.emplace(object_name); + } + } + } + + callback(*this, object_names); + return object_names; + } + void pop(common::stream_in_t& stream) { stream.pop(name); diff --git a/dataplane/bus.cpp b/dataplane/bus.cpp index 89f231de..51feffa8 100644 --- a/dataplane/bus.cpp +++ b/dataplane/bus.cpp @@ -382,6 +382,10 @@ void cBus::clientThread(int clientSocket) { response = dataPlane->memory_manager.memory_manager_update(std::get(std::get<1>(request))); } + else if (type == common::idp::requestType::memory_manager_stats) + { + response = dataPlane->memory_manager.memory_manager_stats(); + } else { stats.errors[(uint32_t)common::idp::errorType::busParse]++; diff --git a/dataplane/memory_manager.cpp b/dataplane/memory_manager.cpp index 1d039cea..053fc12c 100644 --- a/dataplane/memory_manager.cpp +++ b/dataplane/memory_manager.cpp @@ -44,7 +44,7 @@ inline std::string to_hex(const void* pointer) void memory_manager::report(nlohmann::json& json) { - std::lock_guard guard(pointers_mutex); + std::lock_guard guard(mutex); for (const auto& [pointer, memory_pointer] : pointers) { nlohmann::json json_object; @@ -58,10 +58,32 @@ void memory_manager::report(nlohmann::json& json) eResult memory_manager::memory_manager_update(const common::idp::memory_manager_update::request& request) { + std::lock_guard guard(mutex); root_memory_group = request; return eResult::success; } +common::idp::memory_manager_stats::response memory_manager::memory_manager_stats() +{ + common::idp::memory_manager_stats::response response; + auto& [response_memory_group, response_objects] = response; + + { + std::lock_guard guard(mutex); + + response_memory_group = root_memory_group; + for (const auto& [pointer, memory_pointer] : pointers) + { + (void)pointer; + response_objects.emplace_back(memory_pointer.name, + memory_pointer.socket_id, + memory_pointer.size); + } + } + + return response; +} + void* memory_manager::alloc(const char* name, const tSocketId socket_id, uint64_t size, @@ -78,27 +100,34 @@ void* memory_manager::alloc(const char* name, size += 2 * RTE_CACHE_LINE_SIZE; - YANET_LOG_INFO("yanet_alloc(name: '%s', socket: %u, size: %lu)\n", - name, - socket_id, - size); - - void* pointer = rte_malloc_socket(nullptr, - size, - RTE_CACHE_LINE_SIZE, - socket_id); - if (pointer == nullptr) + void* pointer = nullptr; { - YANET_LOG_ERROR("error allocation memory (name: '%s', socket: %u, size: %lu)\n", - name, - socket_id, - size); - debug(socket_id); - return nullptr; - } + std::lock_guard guard(mutex); + + YANET_LOG_INFO("yanet_alloc(name: '%s', socket: %u, size: %lu)\n", + name, + socket_id, + size); + + if (!check_memory_limit(name, size)) + { + return nullptr; + } + + pointer = rte_malloc_socket(nullptr, + size, + RTE_CACHE_LINE_SIZE, + socket_id); + if (pointer == nullptr) + { + YANET_LOG_ERROR("error allocation memory (name: '%s', socket: %u, size: %lu)\n", + name, + socket_id, + size); + debug(socket_id); + return nullptr; + } - { - std::lock_guard guard(pointers_mutex); pointers.try_emplace(pointer, name, socket_id, size, pointer, [destructor](void* pointer) { destructor(pointer); rte_free(pointer); @@ -110,7 +139,7 @@ void* memory_manager::alloc(const char* name, void memory_manager::destroy(void* pointer) { - std::lock_guard guard(pointers_mutex); + std::lock_guard guard(mutex); auto it = pointers.find(pointer); if (it == pointers.end()) @@ -136,6 +165,65 @@ void memory_manager::debug(tSocketId socket_id) } } +bool memory_manager::check_memory_limit(const std::string& name, + const uint64_t size) +{ + bool result = true; + std::map ///< current + currents; + + for (const auto& [pointer, memory_pointer] : pointers) + { + (void)pointer; + + uint64_t object_size = memory_pointer.size; + if (memory_pointer.name == name) + { + object_size = size; + } + + currents[memory_pointer.name] = std::max(currents[memory_pointer.name].value, + object_size); + } + + root_memory_group.for_each([&](const auto& memory_group, + const std::set& object_names) { + bool check = false; + uint64_t group_total = 0; + for (const auto& object_name : object_names) + { + group_total += currents[object_name]; + + if (object_name == name) + { + check = true; + } + } + + if (check && memory_group.limit) + { + if (group_total > memory_group.limit) + { + YANET_LOG_ERROR("memory limit for '%s': group '%s': %lu of %lu\n", + name.data(), + memory_group.name.data(), + group_total, + memory_group.limit); + for (const auto& object_name : object_names) + { + YANET_LOG_ERROR(" object '%s': %lu\n", + object_name.data(), + currents[object_name].value); + } + result = false; + } + } + }); + + return result; +} + void dataplane::memory_manager::limits(common::idp::limits::response& response) { (void)response; diff --git a/dataplane/memory_manager.h b/dataplane/memory_manager.h index 4032d22e..6745905a 100644 --- a/dataplane/memory_manager.h +++ b/dataplane/memory_manager.h @@ -35,6 +35,7 @@ class memory_manager void report(nlohmann::json& json); void limits(common::idp::limits::response& response); eResult memory_manager_update(const common::idp::memory_manager_update::request& request); + common::idp::memory_manager_stats::response memory_manager_stats(); void* alloc( const char* name, @@ -63,13 +64,14 @@ class memory_manager void destroy(void* pointer); void debug(tSocketId socket_id); + bool check_memory_limit(const std::string& name, const uint64_t size); protected: cDataPlane* dataplane; - common::memory_manager::memory_group root_memory_group; - std::mutex pointers_mutex; + std::mutex mutex; std::map pointers; + common::memory_manager::memory_group root_memory_group; }; }