Check that the host and a VE keep the CPU heuristic of the io_setup()
ring and a pseudosuper VE skips it, and that io_setup((nr - 2) / 2)
there gives back a ring of nr events for rings of 1 to 4 pages.

The rings differ only when the CPU heuristic makes a one-page ring
bigger, e.g. with 16 possible CPUs and 4K pages, else the test is
skipped. Before the fix the pseudosuper tests fail on 16 CPUs with a
ring of 255 events instead of 127.

https://virtuozzo.atlassian.net/browse/VSTOR-154129

Feature: ve: CRIU support
Signed-off-by: Pavel Tikhomirov <[email protected]>
---
 tools/testing/selftests/ve/.gitignore         |   1 +
 tools/testing/selftests/ve/Makefile           |   1 +
 tools/testing/selftests/ve/ve_aio_ring_test.c | 214 ++++++++++++++++++++++++++
 3 files changed, 216 insertions(+)

diff --git a/tools/testing/selftests/ve/.gitignore 
b/tools/testing/selftests/ve/.gitignore
index 3d1179bd2c5f4..2ff027048a6ae 100644
--- a/tools/testing/selftests/ve/.gitignore
+++ b/tools/testing/selftests/ve/.gitignore
@@ -4,3 +4,4 @@ ve_mount_accounting_test
 ve_cpuid_override_test
 ve_vtty_stress
 ve_numa_migrate_test
+ve_aio_ring_test
diff --git a/tools/testing/selftests/ve/Makefile 
b/tools/testing/selftests/ve/Makefile
index e96320a183844..124b4a918016c 100644
--- a/tools/testing/selftests/ve/Makefile
+++ b/tools/testing/selftests/ve/Makefile
@@ -7,6 +7,7 @@ TEST_GEN_PROGS += ve_perms_test
 TEST_GEN_PROGS += ve_mount_accounting_test
 TEST_GEN_PROGS += ve_vtty_stress
 TEST_GEN_PROGS += ve_numa_migrate_test
+TEST_GEN_PROGS += ve_aio_ring_test
 
 # CPUID faulting is x86-64 only. ARCH says "x86" for 32-bit too and lib.mk
 # picks the clang target by its emptiness, so decide by the compiler.
diff --git a/tools/testing/selftests/ve/ve_aio_ring_test.c 
b/tools/testing/selftests/ve/ve_aio_ring_test.c
new file mode 100644
index 0000000000000..c370941574224
--- /dev/null
+++ b/tools/testing/selftests/ve/ve_aio_ring_test.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ve_aio_ring selftest
+ *
+ * io_setup() makes the ring at least 4 events per possible CPU. CRIU
+ * restores a ring with io_setup() and needs the size it had on dump, so
+ * a VE in pseudosuper mode (restore) gets the ring of exactly the size
+ * asked for. The tests check that:
+ *
+ *   - the host keeps the CPU heuristic
+ *   - a VE keeps the CPU heuristic
+ *   - a VE in pseudosuper mode skips the CPU heuristic
+ *   - a VE in pseudosuper mode gets a ring of nr events from
+ *     io_setup((nr - 2) / 2), for rings of 1 to 4 pages
+ *
+ * The rings differ only when the CPU heuristic makes a small ring bigger,
+ * e.g. with 16 possible CPUs and 4K pages, else the test is skipped.
+ */
+#define _GNU_SOURCE
+#include <sched.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <linux/aio_abi.h>
+
+#include "../kselftest_harness.h"
+#include "ve_selftest.h"
+
+/* The ring header mapped by io_setup(), a copy of the one in fs/aio.c. */
+struct aio_ring {
+       unsigned int id;
+       unsigned int nr;        /* ring capacity in io_events */
+       unsigned int head;
+       unsigned int tail;
+       unsigned int magic;
+       unsigned int compat_features;
+       unsigned int incompat_features;
+       unsigned int header_length;
+       struct io_event io_events[];
+};
+
+#define PAGE_ALIGN(size, ps)   (((size) + ((ps) - 1)) & ~((ps) - 1))
+
+/* The biggest ring, in pages, the pseudosuper restore test covers. */
+#define RING_PAGES_MAX         4
+
+struct aio_ring_arg {
+       unsigned int nr_events; /* in: io_setup() nr_events */
+       unsigned int ring_nr;   /* out: ring capacity, struct aio_ring::nr */
+};
+
+FIXTURE(ve_aio_ring)
+{
+       int cgv2_fd;
+       int ctid;
+       struct namespace_ctx ns_ctx;
+       unsigned int cpus;
+       unsigned long page_size;
+       struct aio_ring_arg *arg;       /* shared with the child in the VE */
+};
+
+/* The ring capacity aio_setup_ring() makes for @nr_events. */
+static unsigned int calculate_ring_nr(FIXTURE_DATA(ve_aio_ring) * self,
+                                     unsigned int nr_events)
+{
+       unsigned long size;
+
+       size = sizeof(struct aio_ring) + (nr_events + 2) * sizeof(struct 
io_event);
+       size = PAGE_ALIGN(size, self->page_size);
+       return (size - sizeof(struct aio_ring)) / sizeof(struct io_event);
+}
+
+/* The ring capacity io_setup(@nr_events) makes. */
+static unsigned int ring_nr(FIXTURE_DATA(ve_aio_ring) * self,
+                           unsigned int nr_events)
+{
+       unsigned int cpu_events = self->cpus * 4;
+
+       if (nr_events < cpu_events)
+               nr_events = cpu_events;
+       return calculate_ring_nr(self, 2 * nr_events);
+}
+
+/* The ring capacity io_setup(@nr_events) makes without the CPU heuristic. */
+static unsigned int ring_nr_pseudosuper(FIXTURE_DATA(ve_aio_ring) * self,
+                                       unsigned int nr_events)
+{
+       return calculate_ring_nr(self, 2 * nr_events);
+}
+
+/* Create an AIO context of arg->nr_events, store its ring capacity. */
+static int aio_ring_nr(void *data)
+{
+       struct aio_ring_arg *arg = data;
+       aio_context_t ctx = 0;
+
+       if (syscall(__NR_io_setup, arg->nr_events, &ctx)) {
+               fprintf(stderr, "%s: io_setup(%u): %s\n", __func__,
+                       arg->nr_events, strerror(errno));
+               return -1;
+       }
+       arg->ring_nr = ((struct aio_ring *)ctx)->nr;
+
+       if (syscall(__NR_io_destroy, ctx)) {
+               fprintf(stderr, "%s: io_destroy: %s\n", __func__,
+                       strerror(errno));
+               return -1;
+       }
+       return 0;
+}
+
+/* The ring capacity io_setup(@nr_events) makes in the VE, or 0. */
+static unsigned int ve_ring_nr(FIXTURE_DATA(ve_aio_ring) * self,
+                              unsigned int nr_events)
+{
+       self->arg->nr_events = nr_events;
+       self->arg->ring_nr = 0;
+       if (run_in_ve(self->cgv2_fd, self->ctid, &self->ns_ctx, CLONE_NEWVE,
+                     aio_ring_nr, self->arg))
+               return 0;
+       return self->arg->ring_nr;
+}
+
+static int set_pseudosuper(FIXTURE_DATA(ve_aio_ring) * self, int value)
+{
+       char path[64];
+
+       snprintf(path, sizeof(path), "%d/ve.pseudosuper", self->ctid);
+       return write_u64_at(self->cgv2_fd, path, value);
+}
+
+FIXTURE_SETUP(ve_aio_ring)
+{
+       long cpus;
+
+       self->ctid = -1;
+       self->arg = MAP_FAILED;
+
+       self->page_size = sysconf(_SC_PAGESIZE);
+       cpus = sysconf(_SC_NPROCESSORS_CONF);
+       ASSERT_GT(cpus, 0);
+       self->cpus = cpus;
+       if (ring_nr(self, 1) == ring_nr_pseudosuper(self, 1))
+               SKIP(return, "%u possible CPUs are too few for the CPU 
heuristic to matter",
+                    self->cpus);
+
+       self->arg = mmap(NULL, sizeof(*self->arg), PROT_READ | PROT_WRITE,
+                        MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+       ASSERT_NE(self->arg, MAP_FAILED);
+
+       self->cgv2_fd = mount_cg2_fd();
+       ASSERT_GE(self->cgv2_fd, 0);
+
+       ASSERT_EQ(write_file_at(self->cgv2_fd, "cgroup.subtree_control",
+                               VE_CONTROLLERS), 0);
+
+       self->ctid = make_ve(self->cgv2_fd, CTID_MIN);
+       ASSERT_GE(self->ctid, 0);
+
+       ASSERT_EQ(prepare_namespaces(&self->ns_ctx, CLONE_NEWVE,
+                                    self->cgv2_fd, self->ctid), 0);
+}
+
+FIXTURE_TEARDOWN(ve_aio_ring)
+{
+       if (self->ctid >= 0) {
+               set_pseudosuper(self, 0);
+               close_namespace_fds(&self->ns_ctx);
+               destroy_ve(self->cgv2_fd, self->ctid);
+               close(self->cgv2_fd);
+       }
+       if (self->arg != MAP_FAILED)
+               munmap(self->arg, sizeof(*self->arg));
+}
+
+TEST_F(ve_aio_ring, host_keeps_cpu_heuristic)
+{
+       self->arg->nr_events = 1;
+       EXPECT_EQ(aio_ring_nr(self->arg), 0);
+       EXPECT_EQ(self->arg->ring_nr, ring_nr(self, 1));
+}
+
+TEST_F(ve_aio_ring, ve_keeps_cpu_heuristic)
+{
+       EXPECT_EQ(ve_ring_nr(self, 1), ring_nr(self, 1));
+}
+
+TEST_F(ve_aio_ring, pseudosuper_ve_skips_cpu_heuristic)
+{
+       EXPECT_EQ(set_pseudosuper(self, 1), 0);
+       EXPECT_EQ(ve_ring_nr(self, 1), ring_nr_pseudosuper(self, 1));
+}
+
+/*
+ * A ring of nr events is restored with io_setup((nr - 2) / 2), check
+ * it gives back a ring of nr events.
+ */
+TEST_F(ve_aio_ring, pseudosuper_ve_restores_ring_size)
+{
+       unsigned int pages, nr;
+
+       EXPECT_EQ(set_pseudosuper(self, 1), 0);
+       for (pages = 1; pages <= RING_PAGES_MAX; pages++) {
+               nr = (pages * self->page_size - sizeof(struct aio_ring)) /
+                       sizeof(struct io_event);
+               EXPECT_EQ(ve_ring_nr(self, (nr - 2) / 2), nr)
+                       TH_LOG("ring of %u pages", pages);
+       }
+}
+
+TEST_HARNESS_MAIN

-- 
2.56.0

_______________________________________________
Devel mailing list
[email protected]
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to