|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v4 09/10] tools/tests: Update the claims test to test claim_memory hypercall
Extend the existing mem-claim test to verify both the legacy
XENMEM_claim_pages and the new XEN_DOMCTL_claim_memory hypercalls.
It tests both host-wide claims (NUMA_NO_NODE) and node-specific
claims (assuming at least a single NUMA node, node 0 is provided)
to ensure the new infrastructure works correctly.
It also checks the protection of host- and node-claims against
allocations without sufficient, specific claims.
Signed-off-by: Bernhard Kaindl <bernhard.kaindl@xxxxxxxxxx>
---
tools/tests/mem-claim/test-mem-claim.c | 277 +++++++++++++++++++++++--
1 file changed, 254 insertions(+), 23 deletions(-)
diff --git a/tools/tests/mem-claim/test-mem-claim.c
b/tools/tests/mem-claim/test-mem-claim.c
index ad038e45d188..a98d3e43ff54 100644
--- a/tools/tests/mem-claim/test-mem-claim.c
+++ b/tools/tests/mem-claim/test-mem-claim.c
@@ -2,6 +2,7 @@
#include <err.h>
#include <errno.h>
#include <inttypes.h>
+#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
@@ -20,10 +21,13 @@ static unsigned int nr_failures;
#define MB_PAGES(x) (MB(x) / XC_PAGE_SIZE)
+#define CLAIM_TEST_ORDER 9 /* 2M */
+
static xc_interface *xch;
static uint32_t domid = DOMID_INVALID;
static xc_physinfo_t physinfo;
+static unsigned int claim_test_node;
static struct xen_domctl_createdomain create = {
.flags = XEN_DOMCTL_CDF_hvm | XEN_DOMCTL_CDF_hap,
@@ -38,10 +42,138 @@ static struct xen_domctl_createdomain create = {
},
};
-static void run_tests(void)
+typedef int (*claim_fn_t)(xc_interface *xch, uint32_t domid,
+ unsigned long pages);
+
+/* Wrapper function to test claiming memory using xc_domain_claim_pages. */
+static int wrap_claim_pages(xc_interface *xch,
+ uint32_t domid,
+ unsigned long pages)
+{
+ return xc_domain_claim_pages(xch, domid, pages);
+}
+
+/* Wrapper function to test claiming memory using xc_domain_claim_memory. */
+static int wrap_claim_memory(xc_interface *xch,
+ uint32_t domid,
+ unsigned long pages)
+{
+ memory_claim_t claim[] = {
+ XEN_NODE_CLAIM_INIT(pages, XEN_DOMCTL_CLAIM_MEMORY_NO_NODE)
+ };
+
+ return xc_domain_claim_memory(xch, domid, 1, claim);
+}
+
+/* Wrapper to test claiming memory using xc_domain_claim_memory on a NUMA node
*/
+static int wrap_claim_memory_node(xc_interface *xch,
+ uint32_t domid,
+ unsigned long pages)
{
int rc;
+ memory_claim_t claims[UINT8_MAX + 1] = {}; /* + 1 to test overflow check */
+
+ /* claim with a node that is not present */
+ claims[0] = (memory_claim_t)XEN_NODE_CLAIM_INIT(pages, physinfo.nr_nodes);
+ /* Check the return value of claiming memory on an invalid node */
+ rc = xc_domain_claim_memory(xch, domid, 1, claims);
+ if ( rc != -1 || errno != ENOENT )
+ {
+ fail("Expected claim failure on invalid node to fail with ENOENT\n");
+ return rc;
+ }
+ /*
+ * Check the return value of claiming on two nodes (not yet implemented)
+ * and that the valid claim is rejected when nr_claims > 1. We expect that
+ * the API will reject the call due exceeding nr_claims before it checks
+ * the validity of the node(s), so we expect EINVAL rather than ENOENT.
+ */
+ rc = xc_domain_claim_memory(xch, domid, 2, claims);
+ if ( rc != -1 || errno != EINVAL )
+ {
+ fail("Expected nr_claims == 2 to fail with EINVAL (for now)\n");
+ return rc;
+
+ }
+ /* Likewise check with nr_claims > MAX_UINT8 to test overflow */
+ rc = xc_domain_claim_memory(xch, domid, UINT8_MAX + 1, claims);
+ if ( rc != -1 || errno != EINVAL )
+ {
+ fail("Expected nr_claims = UINT8_MAX + 1 to fail with EINVAL\n");
+ return rc;
+ }
+ /* Likewise check with a node of MAX_UINT8 + 1 to test overflow */
+ claims[0].node = UINT8_MAX + 1;
+ rc = xc_domain_claim_memory(xch, domid, 1, claims);
+ if ( rc != -1 || errno != ENOENT )
+ {
+ fail("Expected node == UINT8_MAX + 1 to fail with ENOENT\n");
+ return rc;
+ }
+ /* Test with pages exceeding INT32_MAX to check overflow */
+ claims[0] = (memory_claim_t)XEN_NODE_CLAIM_INIT((unsigned)INT32_MAX + 1,
0);
+ rc = xc_domain_claim_memory(xch, domid, 1, claims);
+ if ( rc != -1 || errno != ENOMEM )
+ {
+ fail("Expected ENOMEM with pages > INT32_MAX\n");
+ return rc;
+ }
+ /* Test with pad not set to zero */
+ claims[0] = (memory_claim_t)XEN_NODE_CLAIM_INIT(pages, claim_test_node);
+ claims[0].pad = 1;
+ rc = xc_domain_claim_memory(xch, domid, 1, claims);
+ if ( rc != -1 || errno != EINVAL )
+ {
+ fail("Expected EINVAL with pad not set to zero\n");
+ return rc;
+ }
+
+ /* Pass a valid claim for the selected node and continue the test */
+ claims[0] = (memory_claim_t)XEN_NODE_CLAIM_INIT(pages, claim_test_node);
+ return xc_domain_claim_memory(xch, domid, 1, claims);
+}
+
+static int get_node_free_pages(unsigned int node, unsigned long *free_pages)
+{
+ int rc;
+ unsigned int num_nodes = 0;
+ xc_meminfo_t *meminfo;
+
+ rc = xc_numainfo(xch, &num_nodes, NULL, NULL);
+ if ( rc )
+ return rc;
+
+ if ( node >= num_nodes )
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ meminfo = calloc(num_nodes, sizeof(*meminfo));
+ if ( !meminfo )
+ return -1;
+
+ rc = xc_numainfo(xch, &num_nodes, meminfo, NULL);
+ if ( rc )
+ goto out;
+
+ *free_pages = meminfo[node].memfree / XC_PAGE_SIZE;
+
+ out:
+ free(meminfo);
+ return rc;
+}
+
+static void run_test(claim_fn_t claim_call_wrapper, const char *claim_name,
+ bool host_wide_claim)
+{
+ int rc;
+ uint64_t free_heap_bytes;
+ unsigned long free_pages, claim_pages;
+ const unsigned long request_pages = 1UL << CLAIM_TEST_ORDER;
+
+ printf(" Testing %s\n", claim_name);
/*
* Check that the system is quiescent. Outstanding claims is a global
* field.
@@ -51,7 +183,7 @@ static void run_tests(void)
return fail("Failed to obtain physinfo: %d - %s\n",
errno, strerror(errno));
- printf("Free pages: %"PRIu64", Oustanding claims: %"PRIu64"\n",
+ printf("Free pages: %"PRIu64", Outstanding claims: %"PRIu64"\n",
physinfo.free_pages, physinfo.outstanding_pages);
if ( physinfo.outstanding_pages )
@@ -98,13 +230,30 @@ static void run_tests(void)
return fail(" Unexpected outstanding claim of %"PRIu64" pages\n",
physinfo.outstanding_pages);
- /*
- * Set a claim for 4M. This should be the only claim in the system, and
- * show up globally.
- */
- rc = xc_domain_claim_pages(xch, domid, MB_PAGES(4));
+ rc = xc_availheap(xch, 0, 0, host_wide_claim ? -1 : (int)claim_test_node,
+ &free_heap_bytes);
if ( rc )
- return fail(" Failed to claim 4M of RAM: %d - %s\n",
+ return fail(" Failed to query available heap: %d - %s\n",
+ errno, strerror(errno));
+
+ free_pages = free_heap_bytes / XC_PAGE_SIZE;
+ if ( !host_wide_claim )
+ {
+ rc = get_node_free_pages(claim_test_node, &free_pages);
+ if ( rc )
+ return fail(" Failed to query free pages on node %u: %d - %s\n",
+ claim_test_node, errno, strerror(errno));
+ }
+
+ if ( free_pages <= request_pages + 1 )
+ return fail(" Not enough free pages (%lu) to test %s claim
enforcement\n",
+ free_pages, host_wide_claim ? "host-wide" : "node");
+
+ claim_pages = free_pages - request_pages + 1;
+
+ rc = claim_call_wrapper(xch, domid, claim_pages);
+ if ( rc )
+ return fail(" Failed to claim calculated RAM amount: %d - %s\n",
errno, strerror(errno));
rc = xc_physinfo(xch, &physinfo);
@@ -112,17 +261,51 @@ static void run_tests(void)
return fail(" Failed to obtain physinfo: %d - %s\n",
errno, strerror(errno));
- if ( physinfo.outstanding_pages != MB_PAGES(4) )
- return fail(" Expected claim to be 4M, got %"PRIu64" pages\n",
- physinfo.outstanding_pages);
+ if ( physinfo.outstanding_pages != claim_pages )
+ return fail(" Expected claim to be %lu pages, got %"PRIu64" pages\n",
+ claim_pages, physinfo.outstanding_pages);
+
+ {
+ uint32_t other_domid = DOMID_INVALID;
+ xen_pfn_t other_ram[] = { 0 };
+ unsigned int memflags = host_wide_claim ? 0 :
XENMEMF_exact_node(claim_test_node);
+
+ rc = xc_domain_create(xch, &other_domid, &create);
+ if ( rc )
+ return fail(" Second domain create failure: %d - %s\n",
+ errno, strerror(errno));
+
+ rc = xc_domain_setmaxmem(xch, other_domid, -1);
+ if ( rc )
+ {
+ fail(" Failed to set maxmem for second domain: %d - %s\n",
+ errno, strerror(errno));
+ goto destroy_other;
+ }
+
+ rc = xc_domain_populate_physmap_exact(
+ xch, other_domid, ARRAY_SIZE(other_ram), CLAIM_TEST_ORDER,
+ memflags, other_ram);
+ if ( rc == 0 )
+ fail(" Expected %s claim to block second-domain allocation\n",
+ host_wide_claim ? "host-wide" : "node");
+
+ destroy_other:
+ rc = xc_domain_destroy(xch, other_domid);
+ if ( rc )
+ return fail(" Failed to destroy second domain: %d - %s\n",
+ errno, strerror(errno));
+ }
/*
- * Allocate 2M of RAM to the domain. This should be deducted from global
- * claim.
+ * Allocate one CLAIM_TEST_ORDER chunk to the domain. This should reduce
+ * the outstanding claim by request_pages. For node claims, request memory
+ * from the claimed node.
*/
xen_pfn_t ram[] = { 0 };
rc = xc_domain_populate_physmap_exact(
- xch, domid, ARRAY_SIZE(ram), 9 /* Order 2M */, 0, ram);
+ xch, domid, ARRAY_SIZE(ram), CLAIM_TEST_ORDER,
+ host_wide_claim ? 0 : XENMEMF_node(claim_test_node), ram);
if ( rc )
return fail(" Failed to populate physmap domain: %d - %s\n",
errno, strerror(errno));
@@ -132,9 +315,9 @@ static void run_tests(void)
return fail(" Failed to obtain physinfo: %d - %s\n",
errno, strerror(errno));
- if ( physinfo.outstanding_pages != MB_PAGES(2) )
- return fail(" Expected claim to be 2M, got %"PRIu64" pages\n",
- physinfo.outstanding_pages);
+ if ( physinfo.outstanding_pages != claim_pages - request_pages )
+ return fail(" Expected claim to be %lu pages, got %"PRIu64" pages\n",
+ claim_pages - request_pages, physinfo.outstanding_pages);
/*
* Destroying the domain should release the outstanding 2M claim.
@@ -161,6 +344,8 @@ static void run_tests(void)
int main(int argc, char **argv)
{
int rc;
+ unsigned int num_nodes = 0;
+ xc_meminfo_t *meminfo = NULL;
printf("Memory claims tests\n");
@@ -169,14 +354,60 @@ int main(int argc, char **argv)
if ( !xch )
err(1, "xc_interface_open");
- run_tests();
+ rc = xc_numainfo(xch, &num_nodes, NULL, NULL);
+ if ( rc || !num_nodes )
+ err(1, "xc_numainfo");
+
+ meminfo = calloc(num_nodes, sizeof(*meminfo));
+ if ( !meminfo )
+ err(1, "calloc");
- if ( domid != DOMID_INVALID )
+ rc = xc_numainfo(xch, &num_nodes, meminfo, NULL);
+ if ( rc )
+ err(1, "xc_numainfo");
+
+ claim_test_node = 0;
+ for ( unsigned int i = 1; i < num_nodes; i++ )
{
- rc = xc_domain_destroy(xch, domid);
- if ( rc )
- fail(" Failed to destroy domain: %d - %s\n",
- errno, strerror(errno));
+ if ( meminfo[i].memfree > meminfo[claim_test_node].memfree )
+ claim_test_node = i;
+ }
+
+ free(meminfo);
+
+ struct {
+ claim_fn_t fn;
+ const char *name;
+ bool host_wide;
+ } tests[] = {
+ {
+ .fn = wrap_claim_pages,
+ .name = "xc_domain_claim_pages",
+ .host_wide = true,
+ },
+ {
+ .fn = wrap_claim_memory,
+ .name = "xc_domain_claim_memory",
+ .host_wide = true,
+ },
+ {
+ .fn = wrap_claim_memory_node,
+ .name = "xc_domain_claim_memory_node",
+ .host_wide = false,
+ },
+ };
+ size_t num_tests = sizeof(tests) / sizeof(tests[0]);
+ for ( size_t i = 0; i < num_tests; i++ )
+ {
+ run_test(tests[i].fn, tests[i].name, tests[i].host_wide);
+ if ( domid != DOMID_INVALID )
+ {
+ rc = xc_domain_destroy(xch, domid);
+ if ( rc )
+ fail(" Failed to destroy domain: %d - %s\n",
+ errno, strerror(errno));
+ domid = DOMID_INVALID;
+ }
}
return !!nr_failures;
--
2.39.5
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |