Skip to content

Commit

Permalink
dpdk: rework hugepage hints to use per-numa information
Browse files Browse the repository at this point in the history
Previous integration of hugepage analysis only fetched data
from /proc/meminfo. However this proved to be often
deceiving mainly for providing only global information and
not taking into account different hugepage sizes (e.g. 1GB
hugepages) and different NUMA nodes.

Ticket: #6419
  • Loading branch information
Lukas Sismis authored and lukashino committed Nov 27, 2023
1 parent d005fff commit 45db6a9
Show file tree
Hide file tree
Showing 7 changed files with 471 additions and 101 deletions.
51 changes: 51 additions & 0 deletions doc/userguide/capture-hardware/dpdk.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,57 @@ learn more about the basic setup for DPDK.
The following sections contain examples of how to set up DPDK and Suricata for
more obscure use-cases.

Hugepage analysis
-----------------

Suricata can analyse utilized hugepages on the system. This can be particularly
beneficial when there's a potential overallocation of hugepages.
The hugepage analysis is designed to examine the hugepages in use and
provide recommendations on an adequate number of hugepages. This then ensures
Suricata operates optimally while leaving sufficient memory for other
applications on the system. The analysis works by comparing snapshots of the
hugepages before and after Suricata is initialized. After the initialization,
no more hugepages are allocated by Suricata.
The hugepage analysis can be seen in the Perf log level and is printed out
during the Suricata start. It is only printed when Suricata detects some
disrepancies in the system related to hugepage allocation.

It's recommended to perform this analysis from a "clean" state -
that is a state when all your hugepages are free. It is especially recommended
when no other hugepage-dependent applications are running on your system.
This can be checked in one of two ways:

.. code-block::
# global check
cat /proc/meminfo
HugePages_Total: 1024
HugePages_Free: 1024
# per-numa check depends on NUMA node ID, hugepage size,
# and nr_hugepages/free_hugepages - e.g.:
cat /sys/devices/system/node/node0/hugepages/hugepages-2048kB/free_hugepages
After the termination of Suricata and other hugepage-related applications,
if the count of free hugepages is not equal with the total number of hugepages,
it indicates some hugepages were not freed completely.
This can be fixed by removing DPDK-related files from the hugepage-mounted
directory (filesystem).
It's important to exercise caution while removing hugepages, especially when
other hugepage-dependent applications are in operation, as this action will
disrupt their memory functionality.
Removing the DPDK files from the hugepage directory can often be done as:

.. code-block:: bash
sudo rm -rf /dev/hugepages/rtemap_*
# To check where hugepages are mounted:
dpdk-hugepages.py -s
# or
mount | grep huge
Bond interface
--------------

Expand Down
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ noinst_HEADERS = \
util-hash-string.h \
util-host-info.h \
util-host-os-info.h \
util-hugepages.h \
util-hyperscan.h \
util-ioctl.h \
util-ip.h \
Expand Down Expand Up @@ -1152,6 +1153,7 @@ libsuricata_c_a_SOURCES = \
util-hash-string.c \
util-host-info.c \
util-host-os-info.c \
util-hugepages.c \
util-hyperscan.c \
util-ioctl.c \
util-ip.c \
Expand Down
8 changes: 7 additions & 1 deletion src/suricata.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
#include "util-ebpf.h"
#include "util-exception-policy.h"
#include "util-host-os-info.h"
#include "util-hugepages.h"
#include "util-ioctl.h"
#include "util-landlock.h"
#include "util-luajit.h"
Expand Down Expand Up @@ -2968,6 +2969,7 @@ int SuricataMain(int argc, char **argv)
goto out;
}

SystemHugepageSnapshot *prerun_snap = SystemHugepageSnapshotCreate();
SCSetStartTime(&suricata);
RunModeDispatch(suricata.run_mode, suricata.runmode_custom_mode,
suricata.capture_plugin_name, suricata.capture_plugin_args);
Expand Down Expand Up @@ -3026,7 +3028,11 @@ int SuricataMain(int argc, char **argv)

PostRunStartedDetectSetup(&suricata);

DPDKEvaluateHugepages();
SystemHugepageSnapshot *postrun_snap = SystemHugepageSnapshotCreate();
if (run_mode == RUNMODE_DPDK) // only DPDK uses hpages at the moment
SystemHugepageEvaluateHugepages(prerun_snap, postrun_snap);
SystemHugepageSnapshotDestroy(prerun_snap);
SystemHugepageSnapshotDestroy(postrun_snap);

SCPledge();
SuricataMainLoop(&suricata);
Expand Down
99 changes: 0 additions & 99 deletions src/util-dpdk.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,106 +66,7 @@ void DPDKFreeDevice(LiveDevice *ldev)
#endif
}

static FILE *HugepagesMeminfoOpen(void)
{
FILE *fp = fopen("/proc/meminfo", "r");
if (fp == NULL) {
SCLogInfo("Can't analyze hugepage usage: failed to open /proc/meminfo");
}
return fp;
}

static void HugepagesMeminfoClose(FILE *fp)
{
if (fp) {
fclose(fp);
}
}

/**
* Parsing values of meminfo
*
* \param fp Opened file pointer for reading of file /proc/meminfo at beginning
* \param keyword Entry to look for e.g. "HugePages_Free:"
* \return n Value of the entry
* \return -1 On error
*
*/
static int32_t MemInfoParseValue(FILE *fp, const char *keyword)
{
char path[256], value_str[64];
int32_t value = -1;

while (fscanf(fp, "%255s", path) != EOF) {
if (strcmp(path, keyword) == 0) {
if (fscanf(fp, "%63s", value_str) == EOF) {
SCLogDebug("%s: not followed by any number", keyword);
break;
}

if (StringParseInt32(&value, 10, 23, value_str) < 0) {
SCLogDebug("Failed to convert %s from /proc/meminfo", keyword);
value = -1;
}
break;
}
}
return value;
}

static void MemInfoEvaluateHugepages(FILE *fp)
{
int32_t free_hugepages = MemInfoParseValue(fp, "HugePages_Free:");
if (free_hugepages < 0) {
SCLogInfo("HugePages_Free information not found in /proc/meminfo");
return;
}

rewind(fp);

int32_t total_hugepages = MemInfoParseValue(fp, "HugePages_Total:");
if (total_hugepages < 0) {
SCLogInfo("HugePages_Total information not found in /proc/meminfo");
return;
} else if (total_hugepages == 0) {
SCLogInfo("HugePages_Total equals to zero");
return;
}

float free_hugepages_ratio = (float)free_hugepages / (float)total_hugepages;
if (free_hugepages_ratio > 0.5) {
SCLogInfo("%" PRIu32 " of %" PRIu32
" of hugepages are free - number of hugepages can be lowered to e.g. %.0lf",
free_hugepages, total_hugepages, ceil((total_hugepages - free_hugepages) * 1.15));
}
}

static void MemInfoWith(void (*callback)(FILE *))
{
FILE *fp = HugepagesMeminfoOpen();
if (fp) {
callback(fp);
HugepagesMeminfoClose(fp);
}
}

void DPDKEvaluateHugepages(void)
{
if (run_mode != RUNMODE_DPDK)
return;

#ifdef HAVE_DPDK
if (rte_eal_has_hugepages() == 0) { // hugepages disabled
SCLogPerf("Hugepages not enabled - enabling hugepages can improve performance");
return;
}
#endif

MemInfoWith(MemInfoEvaluateHugepages);
}

#ifdef HAVE_DPDK

/**
* Retrieves name of the port from port id
* Not thread-safe
Expand Down
1 change: 0 additions & 1 deletion src/util-dpdk.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ void DPDKCleanupEAL(void);

void DPDKCloseDevice(LiveDevice *ldev);
void DPDKFreeDevice(LiveDevice *ldev);
void DPDKEvaluateHugepages(void);

#ifdef HAVE_DPDK
const char *DPDKGetPortNameByPortID(uint16_t pid);
Expand Down
Loading

0 comments on commit 45db6a9

Please sign in to comment.