On Tue, Nov 5, 2024 at 10:49 AM James Houghton <jthoughton@xxxxxxxxxx> wrote: > > This test now has two modes of operation: > 1. (default) To check how much vCPU performance was affected by access > tracking (previously existed, now supports MGLRU aging). > 2. (-p) To also benchmark how fast MGLRU can do aging while vCPUs are > faulting in memory. > > Mode (1) also serves as a way to verify that aging is working properly > for pages only accessed by KVM. It will fail if one does not have the > 0x8 lru_gen feature bit. > > To support MGLRU, the test creates a memory cgroup, moves itself into > it, then uses the lru_gen debugfs output to track memory in that cgroup. > The logic to parse the lru_gen debugfs output has been put into > selftests/kvm/lib/lru_gen_util.c. > > Co-developed-by: Axel Rasmussen <axelrasmussen@xxxxxxxxxx> > Signed-off-by: Axel Rasmussen <axelrasmussen@xxxxxxxxxx> > Signed-off-by: James Houghton <jthoughton@xxxxxxxxxx> > --- > tools/testing/selftests/kvm/Makefile | 1 + > .../selftests/kvm/access_tracking_perf_test.c | 366 ++++++++++++++-- > .../selftests/kvm/include/lru_gen_util.h | 55 +++ > .../testing/selftests/kvm/lib/lru_gen_util.c | 391 ++++++++++++++++++ > 4 files changed, 783 insertions(+), 30 deletions(-) > create mode 100644 tools/testing/selftests/kvm/include/lru_gen_util.h > create mode 100644 tools/testing/selftests/kvm/lib/lru_gen_util.c > > diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile > index f186888f0e00..542548e6e8ba 100644 > --- a/tools/testing/selftests/kvm/Makefile > +++ b/tools/testing/selftests/kvm/Makefile > @@ -22,6 +22,7 @@ LIBKVM += lib/elf.c > LIBKVM += lib/guest_modes.c > LIBKVM += lib/io.c > LIBKVM += lib/kvm_util.c > +LIBKVM += lib/lru_gen_util.c > LIBKVM += lib/memstress.c > LIBKVM += lib/guest_sprintf.c > LIBKVM += lib/rbtree.c > diff --git a/tools/testing/selftests/kvm/access_tracking_perf_test.c b/tools/testing/selftests/kvm/access_tracking_perf_test.c > index 3c7defd34f56..8d6c2ce4b98a 100644 > --- a/tools/testing/selftests/kvm/access_tracking_perf_test.c > +++ b/tools/testing/selftests/kvm/access_tracking_perf_test.c > @@ -38,6 +38,7 @@ > #include <inttypes.h> > #include <limits.h> > #include <pthread.h> > +#include <stdio.h> > #include <sys/mman.h> > #include <sys/types.h> > #include <sys/stat.h> > @@ -47,6 +48,19 @@ > #include "memstress.h" > #include "guest_modes.h" > #include "processor.h" > +#include "lru_gen_util.h" > + > +static const char *TEST_MEMCG_NAME = "access_tracking_perf_test"; > +static const int LRU_GEN_ENABLED = 0x1; > +static const int LRU_GEN_MM_WALK = 0x2; > +static const char *CGROUP_PROCS = "cgroup.procs"; > +/* > + * If using MGLRU, this test assumes a cgroup v2 or cgroup v1 memory hierarchy > + * is mounted at cgroup_root. > + * > + * Can be changed with -r. > + */ > +static const char *cgroup_root = "/sys/fs/cgroup"; > > /* Global variable used to synchronize all of the vCPU threads. */ > static int iteration; > @@ -62,6 +76,9 @@ static enum { > /* The iteration that was last completed by each vCPU. */ > static int vcpu_last_completed_iteration[KVM_MAX_VCPUS]; > > +/* The time at which the last iteration was completed */ > +static struct timespec vcpu_last_completed_time[KVM_MAX_VCPUS]; > + > /* Whether to overlap the regions of memory vCPUs access. */ > static bool overlap_memory_access; > > @@ -74,6 +91,12 @@ struct test_params { > > /* The number of vCPUs to create in the VM. */ > int nr_vcpus; > + > + /* Whether to use lru_gen aging instead of idle page tracking. */ > + bool lru_gen; > + > + /* Whether to test the performance of aging itself. */ > + bool benchmark_lru_gen; > }; > > static uint64_t pread_uint64(int fd, const char *filename, uint64_t index) > @@ -89,6 +112,50 @@ static uint64_t pread_uint64(int fd, const char *filename, uint64_t index) > > } > > +static void write_file_long(const char *path, long v) > +{ > + FILE *f; > + > + f = fopen(path, "w"); > + TEST_ASSERT(f, "fopen(%s) failed", path); > + TEST_ASSERT(fprintf(f, "%ld\n", v) > 0, > + "fprintf to %s failed", path); > + TEST_ASSERT(!fclose(f), "fclose(%s) failed", path); > +} > + > +static char *path_join(const char *parent, const char *child) > +{ > + char *out = NULL; > + > + return asprintf(&out, "%s/%s", parent, child) >= 0 ? out : NULL; > +} > + > +static char *memcg_path(const char *memcg) > +{ > + return path_join(cgroup_root, memcg); > +} > + > +static char *memcg_file_path(const char *memcg, const char *file) > +{ > + char *mp = memcg_path(memcg); > + char *fp; > + > + if (!mp) > + return NULL; > + fp = path_join(mp, file); > + free(mp); > + return fp; > +} > + > +static void move_to_memcg(const char *memcg, pid_t pid) > +{ > + char *procs = memcg_file_path(memcg, CGROUP_PROCS); > + > + TEST_ASSERT(procs, "Failed to construct cgroup.procs path"); > + write_file_long(procs, pid); > + free(procs); > +} > +