For future work we need two functions that fetches total number of
huge pages and number of free pages for given numa node and page size
(virNumaGetHugePageInfo()).
Then we need to learn which huge pages are supported on given node
(virNumaGetHugePages()).
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
src/libvirt_private.syms | 2 +
src/util/virnuma.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++
src/util/virnuma.h | 10 ++
3 files changed, 254 insertions(+)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index d1d6ff3..cb0d5b1 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1655,6 +1655,8 @@ virDomainNumatuneMemModeTypeFromString;
virDomainNumatuneMemModeTypeToString;
virNumaGetAutoPlacementAdvice;
virNumaGetDistances;
+virNumaGetHugePageInfo;
+virNumaGetHugePages;
virNumaGetMaxNode;
virNumaGetNodeMemory;
virNumaIsAvailable;
diff --git a/src/util/virnuma.c b/src/util/virnuma.c
index 1676208..6a586e9 100644
--- a/src/util/virnuma.c
+++ b/src/util/virnuma.c
@@ -34,12 +34,17 @@
#endif /* WITH_NUMACTL */
+#include <sys/types.h>
+#include <dirent.h>
+
#include "virnuma.h"
#include "vircommand.h"
#include "virerror.h"
#include "virlog.h"
#include "viralloc.h"
#include "virbitmap.h"
+#include "virstring.h"
+#include "virfile.h"
#define VIR_FROM_THIS VIR_FROM_NONE
@@ -472,3 +477,240 @@ virNumaGetDistances(int node ATTRIBUTE_UNUSED,
return 0;
}
#endif
+
+
+#define HUGEPAGES_NUMA_PREFIX "/sys/devices/system/node/"
+#define HUGEPAGES_SYSTEM_PREFIX "/sys/kernel/mm/hugepages/"
+#define HUGEPAGES_PREFIX "hugepages-"
+
+static int
+virNumaGetHugePageInfoPath(char **path,
+ int node,
+ unsigned int page_size,
+ const char *suffix)
+{
+
+ int ret = -1;
+
+ if (node == -1) {
+ /* We are aiming at overall system info */
+ if (page_size) {
+ /* And even on specific huge page size */
+ if (virAsprintf(path,
+ HUGEPAGES_SYSTEM_PREFIX HUGEPAGES_PREFIX
"%ukB/%s",
+ page_size, suffix ? suffix : "") < 0)
+ goto cleanup;
+ } else {
+ if (VIR_STRDUP(*path, HUGEPAGES_SYSTEM_PREFIX) < 0)
+ goto cleanup;
+ }
+
+ } else {
+ /* We are aiming on specific NUMA node */
+ if (page_size) {
+ /* And even on specific huge page size */
+ if (virAsprintf(path,
+ HUGEPAGES_NUMA_PREFIX "node%d/hugepages/"
+ HUGEPAGES_PREFIX "%ukB/%s",
+ node, page_size, suffix ? suffix : "") < 0)
+ goto cleanup;
+ } else {
+ if (virAsprintf(path,
+ HUGEPAGES_NUMA_PREFIX "node%d/hugepages/",
+ node) < 0)
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+ cleanup:
+ return ret;
+}
+
+
+/**
+ * virNumaGetHugePageInfo:
+ * @node: NUMA node id
+ * @page_size: which huge page are we interested in
+ * @page_avail: total number of huge pages in the pool
+ * @page_free: the number of free huge pages in the pool
+ *
+ * For given NUMA node and huge page size fetch information on
+ * total number of huge pages in the pool (both free and taken)
+ * and count for free huge pages in the pool.
+ *
+ * If you're interested in just one bit, pass NULL to the other one.
+ *
+ * As a special case, if @node == -1, overall info is fetched
+ * from the system.
+ *
+ * Returns 0 on success, -1 otherwise (with error reported).
+ */
+int
+virNumaGetHugePageInfo(int node,
+ unsigned int page_size,
+ unsigned int *page_avail,
+ unsigned int *page_free)
+{
+ int ret = -1;
+ char *path = NULL;
+ char *buf = NULL;
+ char *end;
+
+ if (page_avail) {
+ if (virNumaGetHugePageInfoPath(&path, node,
+ page_size, "nr_hugepages") < 0)
+ goto cleanup;
+
+ if (virFileReadAll(path, 1024, &buf) < 0) {
+ virReportSystemError(errno,
+ _("unable to read %s"),
+ path);
+ goto cleanup;
+ }
+
+ if (virStrToLong_ui(buf, &end, 10, page_avail) < 0 ||
+ *end != '\n') {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unable to parse: %s"),
+ buf);
+ goto cleanup;
+ }
+ VIR_FREE(buf);
+ VIR_FREE(path);
+ }
+
+ if (page_free) {
+ if (virNumaGetHugePageInfoPath(&path, node,
+ page_size, "free_hugepages") < 0)
+ goto cleanup;
+
+ if (virFileReadAll(path, 1024, &buf) < 0) {
+ virReportSystemError(errno,
+ _("unable to read %s"),
+ path);
+ goto cleanup;
+ }
+
+ if (virStrToLong_ui(buf, &end, 10, page_free) < 0 ||
+ *end != '\n') {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unable to parse: %s"),
+ buf);
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(buf);
+ VIR_FREE(path);
+ return ret;
+}
+
+
+/**
+ * virNumaGetHugePages:
+ * @node: NUMA node id
+ * @hugepages_size: list of huge pages supported on @node
+ * @hugepages_avail: list of the pool sizes on @node
+ * @hugepages_free: list of free huge pages on @node
+ * @nhugepages: the lists size
+ *
+ * For given NUMA node fetch info on huge pages. The size of huge
+ * pages (e.g. 4K, 2M, 1G) is stored into @hugepages_size, the
+ * size of the pool is then stored into @hugepages_avail and the
+ * number of free huge pages in the pool is stored into
+ * @hugepages_free.
+ *
+ * If you're interested only in some lists, pass NULL to the
+ * other ones.
+ *
+ * As a special case, if @node == -1, overall info is fetched
+ * from the system.
+ *
+ * Returns 0 on success, -1 otherwise.
+ */
+int
+virNumaGetHugePages(int node,
+ unsigned int **hugepages_size,
+ unsigned int **hugepages_avail,
+ unsigned int **hugepages_free,
+ size_t *nhugepages)
+{
+ int ret = -1;
+ char *path = NULL;
+ DIR *dir = NULL;
+ struct dirent *entry;
+ unsigned int *tmp_size = NULL, *tmp_avail = NULL, *tmp_free = NULL;
+ unsigned int ntmp = 0;
+
+ if (virNumaGetHugePageInfoPath(&path, node, 0, NULL) < 0)
+ goto cleanup;
+
+ if (!(dir = opendir(path))) {
+ virReportSystemError(errno,
+ _("unable to open path: %s"),
+ path);
+ goto cleanup;
+ }
+
+ while (virDirRead(dir, &entry, path) > 0) {
+ const char *page_name = entry->d_name;
+ unsigned int page_size, page_avail = 0, page_free = 0;
+ char *end;
+
+ /* Just to give you a hint, we're dealing with this:
+ * hugepages-2048kB/ or hugepages-1048576kB/ */
+ if (!STRPREFIX(entry->d_name, HUGEPAGES_PREFIX))
+ continue;
+
+ page_name += strlen(HUGEPAGES_PREFIX);
+
+ if (virStrToLong_ui(page_name, &end, 10, &page_size) < 0 ||
+ STRCASENEQ(end, "kB")) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unable to parse %s"),
+ entry->d_name);
+ goto cleanup;
+ }
+
+ /* Querying more detailed info makes sense only sometimes */
+ if ((hugepages_avail || hugepages_free) &&
+ virNumaGetHugePageInfo(node, page_size,
+ &page_avail, &page_free) < 0)
+ goto cleanup;
+
+ if (VIR_REALLOC_N(tmp_size, ntmp + 1) < 0 ||
+ VIR_REALLOC_N(tmp_avail, ntmp + 1) < 0 ||
+ VIR_REALLOC_N(tmp_free, ntmp + 1) < 0)
+ goto cleanup;
+
+ tmp_size[ntmp] = page_size;
+ tmp_avail[ntmp] = page_avail;
+ tmp_free[ntmp] = page_free;
+ ntmp++;
+ }
+
+ if (hugepages_size) {
+ *hugepages_size = tmp_size;
+ tmp_size = NULL;
+ }
+ if (hugepages_avail) {
+ *hugepages_avail = tmp_avail;
+ tmp_avail = NULL;
+ }
+ if (hugepages_free) {
+ *hugepages_free = tmp_free;
+ tmp_free = NULL;
+ }
+ *nhugepages = ntmp;
+ ret = 0;
+ cleanup:
+ VIR_FREE(tmp_free);
+ VIR_FREE(tmp_avail);
+ VIR_FREE(tmp_size);
+ closedir(dir);
+ VIR_FREE(path);
+ return ret;
+}
diff --git a/src/util/virnuma.h b/src/util/virnuma.h
index fe1e966..3461935 100644
--- a/src/util/virnuma.h
+++ b/src/util/virnuma.h
@@ -69,4 +69,14 @@ unsigned int virNumaGetMaxCPUs(void);
int virNumaGetNodeCPUs(int node, virBitmapPtr *cpus);
+int virNumaGetHugePageInfo(int node,
+ unsigned int page_size,
+ unsigned int *page_avail,
+ unsigned int *page_free);
+int virNumaGetHugePages(int node,
+ unsigned int **hugepages_size,
+ unsigned int **hugepages_avail,
+ unsigned int **hugepages_free,
+ size_t *nhugepages)
+ ATTRIBUTE_NONNULL(5);
#endif /* __VIR_NUMA_H__ */
--
1.8.5.5