diff --git a/tools/tests/Makefile b/tools/tests/Makefile index e566bd1699..6477a4386d 100644 --- a/tools/tests/Makefile +++ b/tools/tests/Makefile @@ -3,6 +3,7 @@ include $(XEN_ROOT)/tools/Rules.mk SUBDIRS-y := SUBDIRS-y += domid +SUBDIRS-y += mem-claim SUBDIRS-y += paging-mempool SUBDIRS-y += pdx SUBDIRS-y += rangeset diff --git a/tools/tests/mem-claim/.gitignore b/tools/tests/mem-claim/.gitignore new file mode 100644 index 0000000000..cfcee00b81 --- /dev/null +++ b/tools/tests/mem-claim/.gitignore @@ -0,0 +1 @@ +test-mem-claim diff --git a/tools/tests/mem-claim/Makefile b/tools/tests/mem-claim/Makefile new file mode 100644 index 0000000000..76ba3e3c8b --- /dev/null +++ b/tools/tests/mem-claim/Makefile @@ -0,0 +1,38 @@ +XEN_ROOT = $(CURDIR)/../../.. +include $(XEN_ROOT)/tools/Rules.mk + +TARGET := test-mem-claim + +.PHONY: all +all: $(TARGET) + +.PHONY: clean +clean: + $(RM) -- *.o $(TARGET) $(DEPS_RM) + +.PHONY: distclean +distclean: clean + $(RM) -- *~ + +.PHONY: install +install: all + $(INSTALL_DIR) $(DESTDIR)$(LIBEXEC)/tests + $(INSTALL_PROG) $(TARGET) $(DESTDIR)$(LIBEXEC)/tests + +.PHONY: uninstall +uninstall: + $(RM) -- $(DESTDIR)$(LIBEXEC)/tests/$(TARGET) + +CFLAGS += $(CFLAGS_xeninclude) +CFLAGS += $(CFLAGS_libxenctrl) +CFLAGS += $(APPEND_CFLAGS) + +LDFLAGS += $(LDLIBS_libxenctrl) +LDFLAGS += $(APPEND_LDFLAGS) + +%.o: Makefile + +$(TARGET): test-mem-claim.o + $(CC) -o $@ $< $(LDFLAGS) + +-include $(DEPS_INCLUDE) diff --git a/tools/tests/mem-claim/test-mem-claim.c b/tools/tests/mem-claim/test-mem-claim.c new file mode 100644 index 0000000000..ad038e45d1 --- /dev/null +++ b/tools/tests/mem-claim/test-mem-claim.c @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static unsigned int nr_failures; +#define fail(fmt, ...) \ +({ \ + nr_failures++; \ + (void)printf(fmt, ##__VA_ARGS__); \ +}) + +#define MB_PAGES(x) (MB(x) / XC_PAGE_SIZE) + +static xc_interface *xch; +static uint32_t domid = DOMID_INVALID; + +static xc_physinfo_t physinfo; + +static struct xen_domctl_createdomain create = { + .flags = XEN_DOMCTL_CDF_hvm | XEN_DOMCTL_CDF_hap, + .max_vcpus = 1, + .max_grant_frames = 1, + .grant_opts = XEN_DOMCTL_GRANT_version(1), + + .arch = { +#if defined(__x86_64__) || defined(__i386__) + .emulation_flags = XEN_X86_EMU_LAPIC, +#endif + }, +}; + +static void run_tests(void) +{ + int rc; + + /* + * Check that the system is quiescent. Outstanding claims is a global + * field. + */ + rc = xc_physinfo(xch, &physinfo); + if ( rc ) + return fail("Failed to obtain physinfo: %d - %s\n", + errno, strerror(errno)); + + printf("Free pages: %"PRIu64", Oustanding claims: %"PRIu64"\n", + physinfo.free_pages, physinfo.outstanding_pages); + + if ( physinfo.outstanding_pages ) + return fail(" Test needs running on a quiescent system\n"); + + /* + * We want any arbitrary domain. Start with HVM/HAP, falling back to + * HVM/Shadow and then to PV. The dom0 running this test case is one of + * these modes. + */ +#if defined(__x86_64__) || defined(__i386__) + if ( !(physinfo.capabilities & XEN_SYSCTL_PHYSCAP_hap) ) + create.flags &= ~XEN_DOMCTL_CDF_hap; + + if ( !(physinfo.capabilities & (XEN_SYSCTL_PHYSCAP_hap|XEN_SYSCTL_PHYSCAP_shadow)) || + !(physinfo.capabilities & XEN_SYSCTL_PHYSCAP_hvm) ) + { + create.flags &= ~XEN_DOMCTL_CDF_hvm; + create.arch.emulation_flags = 0; + } +#endif + + rc = xc_domain_create(xch, &domid, &create); + if ( rc ) + return fail(" Domain create failure: %d - %s\n", + errno, strerror(errno)); + + rc = xc_domain_setmaxmem(xch, domid, -1); + if ( rc ) + return fail(" Failed to set maxmem: %d - %s\n", + errno, strerror(errno)); + + printf(" Created d%u\n", domid); + + /* + * Creating a domain shouldn't change the claim. Check it's still 0. + */ + rc = xc_physinfo(xch, &physinfo); + if ( rc ) + return fail(" Failed to obtain physinfo: %d - %s\n", + errno, strerror(errno)); + + if ( physinfo.outstanding_pages ) + 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)); + if ( rc ) + return fail(" Failed to claim 4M of RAM: %d - %s\n", + errno, strerror(errno)); + + rc = xc_physinfo(xch, &physinfo); + if ( rc ) + 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); + + /* + * Allocate 2M of RAM to the domain. This should be deducted from global + * claim. + */ + xen_pfn_t ram[] = { 0 }; + rc = xc_domain_populate_physmap_exact( + xch, domid, ARRAY_SIZE(ram), 9 /* Order 2M */, 0, ram); + if ( rc ) + return fail(" Failed to populate physmap domain: %d - %s\n", + errno, strerror(errno)); + + rc = xc_physinfo(xch, &physinfo); + if ( rc ) + 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); + + /* + * Destroying the domain should release the outstanding 2M claim. + */ + rc = xc_domain_destroy(xch, domid); + + /* Cancel the cleanup path, even in the case of an error. */ + domid = DOMID_INVALID; + + if ( rc ) + return fail(" Failed to destroy domain: %d - %s\n", + errno, strerror(errno)); + + rc = xc_physinfo(xch, &physinfo); + if ( rc ) + return fail(" Failed to obtain physinfo: %d - %s\n", + errno, strerror(errno)); + + if ( physinfo.outstanding_pages ) + return fail(" Expected no outstanding claim, got %"PRIu64" pages\n", + physinfo.outstanding_pages); +} + +int main(int argc, char **argv) +{ + int rc; + + printf("Memory claims tests\n"); + + xch = xc_interface_open(NULL, NULL, 0); + + if ( !xch ) + err(1, "xc_interface_open"); + + run_tests(); + + if ( domid != DOMID_INVALID ) + { + rc = xc_domain_destroy(xch, domid); + if ( rc ) + fail(" Failed to destroy domain: %d - %s\n", + errno, strerror(errno)); + } + + return !!nr_failures; +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */