The aim of the API is to get information on number of free pages
on the system. The API behaves similar to the
virNodeGetCellsFreeMemory(). User passes starting NUMA cell, the
count of nodes that he's interested in, pages sizes (yes,
multiple sizes can be queried at once) and the counts are
returned in an array.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
daemon/remote.c | 52 ++++++++++++++++++++++++
include/libvirt/libvirt.h.in | 7 ++++
src/driver.h | 10 +++++
src/libvirt.c | 95 ++++++++++++++++++++++++++++++++++++++++++++
src/libvirt_public.syms | 4 ++
src/remote/remote_driver.c | 50 +++++++++++++++++++++++
src/remote/remote_protocol.x | 20 +++++++++-
src/remote_protocol-structs | 16 ++++++++
8 files changed, 253 insertions(+), 1 deletion(-)
diff --git a/daemon/remote.c b/daemon/remote.c
index 34c96c9..11ae758 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -6115,6 +6115,58 @@ remoteDispatchDomainGetTime(virNetServerPtr server
ATTRIBUTE_UNUSED,
return rv;
}
+
+static int
+remoteDispatchNodeGetFreePages(virNetServerPtr server ATTRIBUTE_UNUSED,
+ virNetServerClientPtr client,
+ virNetMessagePtr msg ATTRIBUTE_UNUSED,
+ virNetMessageErrorPtr rerr,
+ remote_node_get_free_pages_args *args,
+ remote_node_get_free_pages_ret *ret)
+{
+ int rv = -1;
+ int len;
+ struct daemonClientPrivate *priv =
+ virNetServerClientGetPrivateData(client);
+
+ if (!priv->conn) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not
open"));
+ goto cleanup;
+ }
+
+ if (args->pages.pages_len * args->cellCount > REMOTE_NODE_MAX_CELLS) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("the result won't fit into
REMOTE_NODE_MAX_CELLS"));
+ goto cleanup;
+ }
+
+ /* Allocate return buffer. */
+ if (VIR_ALLOC_N(ret->counts.counts_val,
+ args->pages.pages_len * args->cellCount) < 0)
+ goto cleanup;
+
+ if ((len = virNodeGetFreePages(priv->conn,
+ args->pages.pages_len,
+ args->pages.pages_val,
+ args->startCell,
+ args->cellCount,
+ (unsigned long long *) ret->counts.counts_val,
+ args->flags)) <= 0)
+ goto cleanup;
+
+ ret->counts.counts_len = len;
+ rv = 0;
+
+ cleanup:
+ if (rv < 0) {
+ virNetMessageSaveError(rerr);
+ VIR_FREE(ret->counts.counts_val);
+ }
+ return rv;
+
+}
+
+
/*----- Helpers. -----*/
/* get_nonnull_domain and get_nonnull_network turn an on-wire
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 127de11..b939848 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -5307,6 +5307,13 @@ int virDomainSetTime(virDomainPtr dom,
unsigned int nseconds,
unsigned int flags);
+int virNodeGetFreePages(virConnectPtr conn,
+ unsigned int npages,
+ unsigned int *pages,
+ int startcell,
+ unsigned int cellcount,
+ unsigned long long *counts,
+ unsigned int flags);
/**
* virSchedParameterType:
*
diff --git a/src/driver.h b/src/driver.h
index 5ac89d6..a0b258a 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -1173,6 +1173,15 @@ typedef int
unsigned int nmountpoints,
unsigned int flags);
+typedef int
+(*virDrvNodeGetFreePages)(virConnectPtr conn,
+ unsigned int npages,
+ unsigned int *pages,
+ int startCell,
+ unsigned int cellCount,
+ unsigned long long *counts,
+ unsigned int flags);
+
typedef struct _virDriver virDriver;
typedef virDriver *virDriverPtr;
@@ -1391,6 +1400,7 @@ struct _virDriver {
virDrvDomainFSThaw domainFSThaw;
virDrvDomainGetTime domainGetTime;
virDrvDomainSetTime domainSetTime;
+ virDrvNodeGetFreePages nodeGetFreePages;
};
diff --git a/src/libvirt.c b/src/libvirt.c
index 6c4a124..83b7437 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -20910,3 +20910,98 @@ virDomainSetTime(virDomainPtr dom,
virDispatchError(dom->conn);
return -1;
}
+
+
+/**
+ * virNodeGetFreePages:
+ * @conn: pointer to the hypervisor connection
+ * @npages: number of items in the @pages array
+ * @pages: page sizes to query
+ * @startCell: index of first cell to return free pages info on.
+ * @cellCount: maximum number of cells for which free pages
+ * information can be returned.
+ * @counts: returned counts of free pages
+ * @flags: extra flags; not used yet, so callers should always pass 0
+ *
+ * This calls queries the host system on free pages of
+ * specified size. Ont the input, @pages is expected to be
+ * filled with pages that caller is interested in (the size
+ * unit is kibibytes, so e.g. pass 2048 for 2MB), then @startcell
+ * refers to the first NUMA node that info should be collected
+ * from, and @cellcount tells how many consecutive nodes should
+ * be queried. On the function output, @counts is filled with
+ * desired information, where items are grouped by NUMA node.
+ * So from @counts[0] till @counts[@npages - 1] you'll find count
+ * for the first node (@startcell), then from @counts[@npages]
+ * till @count[2 * @npages - 1] you'll find info for the
+ * (@startcell + 1) node, and so on. It's callers responsibility
+ * to allocate the @counts array.
+ *
+ * Example how to use this API:
+ *
+ * unsigned int pages[] = { 4, 2048, 1048576}
+ * unsigned int npages = ARRAY_CARDINALITY(pages);
+ * int startcell = 0;
+ * unsigned int cellcount = 2;
+ *
+ * unsigned long long counts = malloc(sizeof(long long) * npages * cellcount);
+ *
+ * virNodeGetFreePages(conn, pages, npages,
+ * startcell, cellcount, counts, 0);
+ *
+ * for (i = 0 ; i < cellcount ; i++) {
+ * fprintf(stdout, "Cell %d\n", startcell + i);
+ * for (j = 0 ; j < npages ; j++) {
+ * fprintf(stdout, " Page size=%d count=%d bytes=%llu\n",
+ * pages[j], counts[(i * npages) + j],
+ * pages[j] * counts[(i * npages) + j]);
+ * }
+ * }
+ *
+ * This little code snippet will produce something like this:
+ * Cell 0
+ * Page size=4096 count=300 bytes=1228800
+ * Page size=2097152 count=0 bytes=0
+ * Page size=1073741824 count=1 bytes=1073741824
+ * Cell 1
+ * Page size=4096 count=0 bytes=0
+ * Page size=2097152 count=20 bytes=41943040
+ * Page size=1073741824 count=0 bytes=0
+ *
+ * Returns: the number of entries filled in @counts or -1 in case of error.
+ */
+int
+virNodeGetFreePages(virConnectPtr conn,
+ unsigned int npages,
+ unsigned int *pages,
+ int startCell,
+ unsigned int cellCount,
+ unsigned long long *counts,
+ unsigned int flags)
+{
+ VIR_DEBUG("conn=%p, npages=%u, pages=%p, startCell=%u, "
+ "cellCount=%u, counts=%p, flags=%x",
+ conn, npages, pages, startCell, cellCount, counts, flags);
+
+ virResetLastError();
+
+ virCheckConnectReturn(conn, -1);
+ virCheckNonZeroArgGoto(npages, error);
+ virCheckNonNullArgGoto(pages, error);
+ virCheckNonZeroArgGoto(cellCount, error);
+ virCheckNonNullArgGoto(counts, error);
+
+ if (conn->driver->nodeGetFreePages) {
+ int ret;
+ ret = conn->driver->nodeGetFreePages(conn, npages, pages, startCell,
+ cellCount, counts, flags);
+ if (ret < 0)
+ goto error;
+ return ret;
+ }
+
+ virReportUnsupportedError();
+ error:
+ virDispatchError(conn);
+ return -1;
+}
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
index cce6bdf..40d2c1a 100644
--- a/src/libvirt_public.syms
+++ b/src/libvirt_public.syms
@@ -658,5 +658,9 @@ LIBVIRT_1.2.5 {
virDomainSetTime;
} LIBVIRT_1.2.3;
+LIBVIRT_1.2.6 {
+ global:
+ virNodeGetFreePages;
+} LIBVIRT_1.2.5;
# .... define new API here using predicted next version number ....
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 85fe597..563fac0 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -7469,6 +7469,55 @@ remoteDomainGetTime(virDomainPtr dom,
}
+static int
+remoteNodeGetFreePages(virConnectPtr conn,
+ unsigned int npages,
+ unsigned int *pages,
+ int startCell,
+ unsigned int cellCount,
+ unsigned long long *counts,
+ unsigned int flags)
+{
+ int rv = -1;
+ remote_node_get_free_pages_args args;
+ remote_node_get_free_pages_ret ret;
+ struct private_data *priv = conn->privateData;
+
+ remoteDriverLock(priv);
+
+ if (npages * cellCount > REMOTE_NODE_MAX_CELLS) {
+ virReportError(VIR_ERR_RPC,
+ _("too many NUMA cells: %d > %d"),
+ npages * cellCount, REMOTE_NODE_MAX_CELLS);
+ goto done;
+ }
+
+ if (VIR_ALLOC_N(args.pages.pages_val, npages) < 0)
+ goto done;
+ memcpy(args.pages.pages_val, pages, npages * sizeof(*pages));
+ args.pages.pages_len = npages;
+ args.startCell = startCell;
+ args.cellCount = cellCount;
+ args.flags = flags;
+
+ memset(&ret, 0, sizeof(ret));
+ if (call(conn, priv, 0, REMOTE_PROC_NODE_GET_FREE_PAGES,
+ (xdrproc_t) xdr_remote_node_get_free_pages_args, (char *)&args,
+ (xdrproc_t) xdr_remote_node_get_free_pages_ret, (char *)&ret) == -1)
+ goto done;
+
+ memcpy(counts, ret.counts.counts_val, ret.counts.counts_len * sizeof(*counts));
+
+ xdr_free((xdrproc_t) xdr_remote_node_get_free_pages_ret, (char *) &ret);
+
+ rv = ret.counts.counts_len;
+
+ done:
+ remoteDriverUnlock(priv);
+ return rv;
+}
+
+
/* get_nonnull_domain and get_nonnull_network turn an on-wire
* (name, uuid) pair into virDomainPtr or virNetworkPtr object.
* These can return NULL if underlying memory allocations fail,
@@ -7805,6 +7854,7 @@ static virDriver remote_driver = {
.domainFSThaw = remoteDomainFSThaw, /* 1.2.5 */
.domainGetTime = remoteDomainGetTime, /* 1.2.5 */
.domainSetTime = remoteDomainSetTime, /* 1.2.5 */
+ .nodeGetFreePages = remoteNodeGetFreePages, /* 1.2.6 */
};
static virNetworkDriver network_driver = {
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index 1f9d583..ec4f3e2 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -2999,6 +2999,17 @@ struct remote_domain_fsthaw_ret {
int filesystems;
};
+struct remote_node_get_free_pages_args {
+ unsigned int pages<REMOTE_NODE_MAX_CELLS>;
+ int startCell;
+ unsigned int cellCount;
+ unsigned int flags;
+};
+
+struct remote_node_get_free_pages_ret {
+ unsigned hyper counts<REMOTE_NODE_MAX_CELLS>;
+};
+
/*----- Protocol. -----*/
@@ -5338,5 +5349,12 @@ enum remote_procedure {
* @generate: both
* @acl: domain:set_time
*/
- REMOTE_PROC_DOMAIN_SET_TIME = 338
+ REMOTE_PROC_DOMAIN_SET_TIME = 338,
+
+ /**
+ * @generate: none
+ * @priority: high
+ * @acl: connect:read
+ */
+ REMOTE_PROC_NODE_GET_FREE_PAGES = 339
};
diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs
index 5b22049..6c51e75 100644
--- a/src/remote_protocol-structs
+++ b/src/remote_protocol-structs
@@ -2463,6 +2463,21 @@ struct remote_domain_fsthaw_args {
struct remote_domain_fsthaw_ret {
int filesystems;
};
+struct remote_node_get_free_pages_args {
+ struct {
+ u_int pages_len;
+ u_int * pages_val;
+ } pages;
+ int startCell;
+ u_int cellCount;
+ u_int flags;
+};
+struct remote_node_get_free_pages_ret {
+ struct {
+ u_int counts_len;
+ uint64_t * counts_val;
+ } counts;
+};
enum remote_procedure {
REMOTE_PROC_CONNECT_OPEN = 1,
REMOTE_PROC_CONNECT_CLOSE = 2,
@@ -2802,4 +2817,5 @@ enum remote_procedure {
REMOTE_PROC_DOMAIN_FSTHAW = 336,
REMOTE_PROC_DOMAIN_GET_TIME = 337,
REMOTE_PROC_DOMAIN_SET_TIME = 338,
+ REMOTE_PROC_NODE_GET_FREE_PAGES = 339,
};
--
1.8.5.5