|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v12 2/3] tools/tests: introduce unit tests for domain ID allocator
On Wed Jul 30, 2025 at 5:34 AM CEST, dmkhn wrote:
> From: Denis Mukhin <dmukhin@xxxxxxxx>
>
> Introduce some basic infrastructure for doing domain ID allocation unit tests,
> and add a few tests that ensure correctness of the domain ID allocator.
>
> Signed-off-by: Denis Mukhin <dmukhin@xxxxxxxx>
> ---
> Changes since v11:
> - simplified test, dropped dom0less variant
> ---
> tools/tests/Makefile | 2 +-
> tools/tests/domid/.gitignore | 2 +
> tools/tests/domid/Makefile | 51 ++++++++++
> tools/tests/domid/include/xen/domain.h | 135 +++++++++++++++++++++++++
> tools/tests/domid/test-domid.c | 78 ++++++++++++++
> 5 files changed, 267 insertions(+), 1 deletion(-)
> create mode 100644 tools/tests/domid/.gitignore
> create mode 100644 tools/tests/domid/Makefile
> create mode 100644 tools/tests/domid/include/xen/domain.h
> create mode 100644 tools/tests/domid/test-domid.c
>
> diff --git a/tools/tests/Makefile b/tools/tests/Makefile
> index 36928676a666..ff1666425436 100644
> --- a/tools/tests/Makefile
> +++ b/tools/tests/Makefile
> @@ -1,7 +1,7 @@
> XEN_ROOT = $(CURDIR)/../..
> include $(XEN_ROOT)/tools/Rules.mk
>
> -SUBDIRS-y :=
> +SUBDIRS-y := domid
> SUBDIRS-y += resource
> SUBDIRS-$(CONFIG_X86) += cpu-policy
> SUBDIRS-$(CONFIG_X86) += tsx
> diff --git a/tools/tests/domid/.gitignore b/tools/tests/domid/.gitignore
> new file mode 100644
> index 000000000000..91ac43232518
> --- /dev/null
> +++ b/tools/tests/domid/.gitignore
> @@ -0,0 +1,2 @@
> +*.o
> +test-domid-*
> diff --git a/tools/tests/domid/Makefile b/tools/tests/domid/Makefile
> new file mode 100644
> index 000000000000..9a817bb70c36
> --- /dev/null
> +++ b/tools/tests/domid/Makefile
> @@ -0,0 +1,51 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# Unit tests for domain ID allocator.
> +#
> +# Copyright 2025 Ford Motor Company
> +
> +XEN_ROOT=$(CURDIR)/../../..
> +include $(XEN_ROOT)/tools/Rules.mk
> +
> +TESTS := test-domid
> +
> +vpath domid.c $(XEN_ROOT)/xen/common/
> +
> +.PHONY: all
> +all: $(TESTS)
> +
> +.PHONY: run
> +run: $(TESTS)
> + $(foreach t,$(TESTS),./$(t);)
> +
> +.PHONY: clean
> +clean:
> + $(RM) -- *.o $(TESTS) $(DEPS_RM)
> +
> +.PHONY: distclean
> +distclean: clean
> + $(RM) -- *~
> +
> +.PHONY: install
> +install: all
> + $(INSTALL_DIR) $(DESTDIR)$(LIBEXEC)/tests
> + $(INSTALL_PROG) test-domid $(DESTDIR)$(LIBEXEC)/tests
> +
> +.PHONY: uninstall
> +uninstall:
> + $(RM) -- $(DESTDIR)$(LIBEXEC)/tests/test-domid
> +
> +CFLAGS += -D__XEN_TOOLS__
> +CFLAGS += $(APPEND_CFLAGS)
> +CFLAGS += $(CFLAGS_xeninclude)
> +CFLAGS += -I./include/
> +
> +LDFLAGS += $(APPEND_LDFLAGS)
> +
> +%.o: %.c
> + $(CC) $(CFLAGS) -c $^ -o $@ $(LDFLAGS)
> +
> +test-domid: domid.o test-domid.o
> + $(CC) $^ -o $@ $(LDFLAGS)
> +
> +-include $(DEPS_INCLUDE)
I think the Makefile is off. If I try to recompile after modifying the header it
doesn't compile anymore.
> diff --git a/tools/tests/domid/include/xen/domain.h
> b/tools/tests/domid/include/xen/domain.h
> new file mode 100644
> index 000000000000..396c3bab9d26
> --- /dev/null
> +++ b/tools/tests/domid/include/xen/domain.h
I wish this could be the real one. Alas, I've been in that boat and I'm aware
you can't. Just came here to whine online.
> @@ -0,0 +1,135 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Unit test harness for domain ID allocator.
> + *
> + * Copyright 2025 Ford Motor Company
> + */
> +
> +#ifndef _TEST_HARNESS_
> +#define _TEST_HARNESS_
> +
> +#include <assert.h>
> +#include <errno.h> <======= not needed
> +#include <stdbool.h>
> +#include <stddef.h> <======= not needed
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h> <======= not needed
> +#include <string.h> <======= not needed
Bunch un headers are not required.
> +
> +#include <xen-tools/common-macros.h>
> +
> +#define BUG_ON(x) assert(!(x))
> +#define ASSERT(x) assert(x)
> +
> +#define __xen_mk_uint(x) x ## U
> +#define xen_mk_uint(x) __xen_mk_uint(x)
Do you need these, if...
> +
> +#define DOMID_FIRST_RESERVED xen_mk_uint(10)
> +#define DOMID_INVALID xen_mk_uint(11)
... these were just 10 and 11 without being wrapped?
> +
> +#define DEFINE_SPINLOCK(x) unsigned long *(x)
> +#define spin_lock(x) ((*(x))++)
> +#define spin_unlock(x) ((*(x))--)
> +
> +#define BITS_PER_LONG sizeof(unsigned long)
> +#define BITS_PER_WORD (8U * BITS_PER_LONG)
> +#define BITS_TO_LONGS(bits) \
> + (((bits) + BITS_PER_LONG - 1) / BITS_PER_LONG)
> +#define DECLARE_BITMAP(name, bits) \
> + unsigned long name[BITS_TO_LONGS(bits)]
> +
> +static inline int __test_and_set_bit(unsigned int nr, unsigned long *addr)
> +{
> + unsigned long mask = 1UL << (nr % BITS_PER_WORD);
> + unsigned long *p = addr + (nr / BITS_PER_WORD);
> + int old = (*p & mask) != 0;
> +
> + *p |= mask;
> +
> + return old;
> +}
> +
> +static inline int __test_and_clear_bit(unsigned int nr, unsigned long *addr)
> +{
> + unsigned long mask = 1UL << (nr % BITS_PER_WORD);
> + unsigned long *p = addr + (nr / BITS_PER_WORD);
> + int old = (*p & mask) != 0;
> +
> + *p &= ~mask;
> +
> + return old;
> +}
> +
> +static inline void __set_bit(unsigned int nr, volatile unsigned long *addr)
> +{
> + unsigned long mask = 1UL << (nr % BITS_PER_WORD);
> + unsigned long *p = (unsigned long *)addr + (nr / BITS_PER_WORD);
> +
> + *p |= mask;
> +}
> +
> +static inline void __clear_bit(unsigned int nr, volatile unsigned long *addr)
> +{
> + unsigned long mask = 1UL << (nr % BITS_PER_WORD);
> + unsigned long *p = (unsigned long *)addr + (nr / BITS_PER_WORD);
> +
> + *p &= ~mask;
> +}
> +
> +static inline unsigned long find_next_zero_bit(const unsigned long *addr,
> + unsigned long size,
> + unsigned long offset)
> +{
> + unsigned long idx = offset / BITS_PER_WORD;
> + unsigned long bit = offset % BITS_PER_WORD;
> +
> + if (offset >= size)
> + return size;
> +
> + while (offset < size)
> + {
> + unsigned long val = addr[idx] | (~0UL >> (BITS_PER_WORD - bit));
> +
> + if (~val)
> + {
> + unsigned long pos = __builtin_ffsl(~val);
> +
> + if (pos > 0)
> + {
> + unsigned long rc = idx * BITS_PER_WORD + (pos - 1);
> +
> + if (rc < size)
> + return rc;
> + }
> + }
> +
> + offset = (idx + 1) * BITS_PER_WORD;
> + idx++;
> + bit = 0;
> + }
> +
> + return size;
> +}
> +
> +#define printk printf
> +
> +#define cf_check
No longer needed
> +
> +typedef bool spinlock_t;
> +typedef uint16_t domid_t;
> +
> +/* See include/xen/domain.h */
> +extern domid_t domid_alloc(domid_t domid);
> +extern void domid_free(domid_t domid);
> +
> +#endif /* _TEST_HARNESS_ */
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/tools/tests/domid/test-domid.c b/tools/tests/domid/test-domid.c
> new file mode 100644
> index 000000000000..ed041bb56d89
> --- /dev/null
> +++ b/tools/tests/domid/test-domid.c
> @@ -0,0 +1,78 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Unit tests for domain ID allocator.
> + *
> + * Copyright 2025 Ford Motor Company
> + */
> +
> +/* Local test include replicating hypervisor includes. */
> +#include <xen/domain.h>
> +
> +int main(int argc, char **argv)
> +{
> + domid_t expected, allocated;
> +
> + printk("%s DOMID_FIRST_RESERVED=%u DOMID_INVALID=%u\n",
> + argv[0], DOMID_FIRST_RESERVED, DOMID_INVALID);
nit; IMO, argv[0] is inconsequential.
also, printf seeing how this is a unit test. I know it's #define'd, but it's
just easier to eyeball you're in userspace when you don't have printks around.
> + /* Test ID#0 cannot be allocated twice. */
> + allocated = domid_alloc(0);
> + printk("TEST 1: expected %u allocated %u\n", 0, allocated);
> + ASSERT(allocated == 0);
> + allocated = domid_alloc(0);
> + printk("TEST 1: expected %u allocated %u\n", DOMID_INVALID, allocated);
> + ASSERT(allocated == DOMID_INVALID);
> +
> + /* Ensure ID is not allocated. */
> + domid_free(0);
> +
> + /*
> + * Test that that two consecutive calls of domid_alloc(DOMID_INVALID)
> + * will never return the same ID.
> + * NB: ID#0 is reserved and shall not be allocated by
> + * domid_alloc(DOMID_INVALID).
> + */
> + for ( expected = 0; expected < DOMID_FIRST_RESERVED - 1; expected++ )
If this starts on 1...
> + {
> + allocated = domid_alloc(DOMID_INVALID);
> + printk("TEST 2: expected %u allocated %u\n", expected + 1,
> allocated);
> + ASSERT(allocated == expected + 1);
... these two doen't have to have it. Which is a bit easier to read and
unserstand.
"expect" should match the expectation, IMO.
> + }
> + for ( expected = 0; expected < DOMID_FIRST_RESERVED; expected++ )
> + {
> + allocated = domid_alloc(DOMID_INVALID);
> + printk("TEST 2: expected %u allocated %u\n", DOMID_INVALID,
> allocated);
> + ASSERT(allocated == DOMID_INVALID);
> + }
> +
> + /* Re-allocate first ID from [1..DOMID_FIRST_RESERVED - 1]. */
> + expected = 1;
> + domid_free(1);
> + allocated = domid_alloc(DOMID_INVALID);
> + printk("TEST 3: expected %u allocated %u\n", expected, allocated);
> + ASSERT(allocated == expected);
> +
> + /* Re-allocate last ID from [1..DOMID_FIRST_RESERVED - 1]. */
> + expected = DOMID_FIRST_RESERVED - 1;
> + domid_free(DOMID_FIRST_RESERVED - 1);
> + allocated = domid_alloc(DOMID_INVALID);
> + printk("TEST 4: expected %u allocated %u\n", expected, allocated);
> + ASSERT(allocated == expected);
> +
> + /* Allocate an invalid ID. */
> + expected = DOMID_INVALID;
> + allocated = domid_alloc(DOMID_FIRST_RESERVED);
> + printk("TEST 5: expected %u allocated %u\n", expected, allocated);
> + ASSERT(allocated == expected);
> +
> + return 0;
> +}
As far as the test cases go, they seem pretty extensive. I like it.
Cheers,
Alejandro
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |