Path: blob/master/tools/testing/selftests/kvm/s390/cmma_test.c
38237 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Test for s390x CMMA migration3*4* Copyright IBM Corp. 20235*6* Authors:7* Nico Boehr <[email protected]>8*/9#include <fcntl.h>10#include <stdio.h>11#include <stdlib.h>12#include <string.h>13#include <sys/ioctl.h>1415#include "test_util.h"16#include "kvm_util.h"17#include "kselftest.h"18#include "ucall_common.h"19#include "processor.h"2021#define MAIN_PAGE_COUNT 5122223#define TEST_DATA_PAGE_COUNT 51224#define TEST_DATA_MEMSLOT 125#define TEST_DATA_START_GFN PAGE_SIZE2627#define TEST_DATA_TWO_PAGE_COUNT 25628#define TEST_DATA_TWO_MEMSLOT 229#define TEST_DATA_TWO_START_GFN (2 * PAGE_SIZE)3031static char cmma_value_buf[MAIN_PAGE_COUNT + TEST_DATA_PAGE_COUNT];3233/**34* Dirty CMMA attributes of exactly one page in the TEST_DATA memslot,35* so use_cmma goes on and the CMMA related ioctls do something.36*/37static void guest_do_one_essa(void)38{39asm volatile(40/* load TEST_DATA_START_GFN into r1 */41" llilf 1,%[start_gfn]\n"42/* calculate the address from the gfn */43" sllg 1,1,12(0)\n"44/* set the first page in TEST_DATA memslot to STABLE */45" .insn rrf,0xb9ab0000,2,1,1,0\n"46/* hypercall */47" diag 0,0,0x501\n"48"0: j 0b"49:50: [start_gfn] "L"(TEST_DATA_START_GFN)51: "r1", "r2", "memory", "cc"52);53}5455/**56* Touch CMMA attributes of all pages in TEST_DATA memslot. Set them to stable57* state.58*/59static void guest_dirty_test_data(void)60{61asm volatile(62/* r1 = TEST_DATA_START_GFN */63" xgr 1,1\n"64" llilf 1,%[start_gfn]\n"65/* r5 = TEST_DATA_PAGE_COUNT */66" lghi 5,%[page_count]\n"67/* r5 += r1 */68"2: agfr 5,1\n"69/* r2 = r1 << PAGE_SHIFT */70"1: sllg 2,1,12(0)\n"71/* essa(r4, r2, SET_STABLE) */72" .insn rrf,0xb9ab0000,4,2,1,0\n"73/* i++ */74" agfi 1,1\n"75/* if r1 < r5 goto 1 */76" cgrjl 1,5,1b\n"77/* hypercall */78" diag 0,0,0x501\n"79"0: j 0b"80:81: [start_gfn] "L"(TEST_DATA_START_GFN),82[page_count] "L"(TEST_DATA_PAGE_COUNT)83:84/* the counter in our loop over the pages */85"r1",86/* the calculated page physical address */87"r2",88/* ESSA output register */89"r4",90/* last page */91"r5",92"cc", "memory"93);94}9596static void create_main_memslot(struct kvm_vm *vm)97{98int i;99100vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, MAIN_PAGE_COUNT, 0);101/* set the array of memslots to zero like __vm_create does */102for (i = 0; i < NR_MEM_REGIONS; i++)103vm->memslots[i] = 0;104}105106static void create_test_memslot(struct kvm_vm *vm)107{108vm_userspace_mem_region_add(vm,109VM_MEM_SRC_ANONYMOUS,110TEST_DATA_START_GFN << vm->page_shift,111TEST_DATA_MEMSLOT,112TEST_DATA_PAGE_COUNT,1130114);115vm->memslots[MEM_REGION_TEST_DATA] = TEST_DATA_MEMSLOT;116}117118static void create_memslots(struct kvm_vm *vm)119{120/*121* Our VM has the following memory layout:122* +------+---------------------------+123* | GFN | Memslot |124* +------+---------------------------+125* | 0 | |126* | ... | MAIN (Code, Stack, ...) |127* | 511 | |128* +------+---------------------------+129* | 4096 | |130* | ... | TEST_DATA |131* | 4607 | |132* +------+---------------------------+133*/134create_main_memslot(vm);135create_test_memslot(vm);136}137138static void finish_vm_setup(struct kvm_vm *vm)139{140struct userspace_mem_region *slot0;141142kvm_vm_elf_load(vm, program_invocation_name);143144slot0 = memslot2region(vm, 0);145ucall_init(vm, slot0->region.guest_phys_addr + slot0->region.memory_size);146147kvm_arch_vm_post_create(vm, 0);148}149150static struct kvm_vm *create_vm_two_memslots(void)151{152struct kvm_vm *vm;153154vm = vm_create_barebones();155156create_memslots(vm);157158finish_vm_setup(vm);159160return vm;161}162163static void enable_cmma(struct kvm_vm *vm)164{165int r;166167r = __kvm_device_attr_set(vm->fd, KVM_S390_VM_MEM_CTRL, KVM_S390_VM_MEM_ENABLE_CMMA, NULL);168TEST_ASSERT(!r, "enabling cmma failed r=%d errno=%d", r, errno);169}170171static void enable_dirty_tracking(struct kvm_vm *vm)172{173vm_mem_region_set_flags(vm, 0, KVM_MEM_LOG_DIRTY_PAGES);174vm_mem_region_set_flags(vm, TEST_DATA_MEMSLOT, KVM_MEM_LOG_DIRTY_PAGES);175}176177static int __enable_migration_mode(struct kvm_vm *vm)178{179return __kvm_device_attr_set(vm->fd,180KVM_S390_VM_MIGRATION,181KVM_S390_VM_MIGRATION_START,182NULL183);184}185186static void enable_migration_mode(struct kvm_vm *vm)187{188int r = __enable_migration_mode(vm);189190TEST_ASSERT(!r, "enabling migration mode failed r=%d errno=%d", r, errno);191}192193static bool is_migration_mode_on(struct kvm_vm *vm)194{195u64 out;196int r;197198r = __kvm_device_attr_get(vm->fd,199KVM_S390_VM_MIGRATION,200KVM_S390_VM_MIGRATION_STATUS,201&out202);203TEST_ASSERT(!r, "getting migration mode status failed r=%d errno=%d", r, errno);204return out;205}206207static int vm_get_cmma_bits(struct kvm_vm *vm, u64 flags, int *errno_out)208{209struct kvm_s390_cmma_log args;210int rc;211212errno = 0;213214args = (struct kvm_s390_cmma_log){215.start_gfn = 0,216.count = sizeof(cmma_value_buf),217.flags = flags,218.values = (__u64)&cmma_value_buf[0]219};220rc = __vm_ioctl(vm, KVM_S390_GET_CMMA_BITS, &args);221222*errno_out = errno;223return rc;224}225226static void test_get_cmma_basic(void)227{228struct kvm_vm *vm = create_vm_two_memslots();229struct kvm_vcpu *vcpu;230int rc, errno_out;231232/* GET_CMMA_BITS without CMMA enabled should fail */233rc = vm_get_cmma_bits(vm, 0, &errno_out);234TEST_ASSERT_EQ(rc, -1);235TEST_ASSERT_EQ(errno_out, ENXIO);236237enable_cmma(vm);238vcpu = vm_vcpu_add(vm, 1, guest_do_one_essa);239240vcpu_run(vcpu);241242/* GET_CMMA_BITS without migration mode and without peeking should fail */243rc = vm_get_cmma_bits(vm, 0, &errno_out);244TEST_ASSERT_EQ(rc, -1);245TEST_ASSERT_EQ(errno_out, EINVAL);246247/* GET_CMMA_BITS without migration mode and with peeking should work */248rc = vm_get_cmma_bits(vm, KVM_S390_CMMA_PEEK, &errno_out);249TEST_ASSERT_EQ(rc, 0);250TEST_ASSERT_EQ(errno_out, 0);251252enable_dirty_tracking(vm);253enable_migration_mode(vm);254255/* GET_CMMA_BITS with invalid flags */256rc = vm_get_cmma_bits(vm, 0xfeedc0fe, &errno_out);257TEST_ASSERT_EQ(rc, -1);258TEST_ASSERT_EQ(errno_out, EINVAL);259260kvm_vm_free(vm);261}262263static void assert_exit_was_hypercall(struct kvm_vcpu *vcpu)264{265TEST_ASSERT_EQ(vcpu->run->exit_reason, 13);266TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, 4);267TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x8300);268TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipb, 0x5010000);269}270271static void test_migration_mode(void)272{273struct kvm_vm *vm = vm_create_barebones();274struct kvm_vcpu *vcpu;275u64 orig_psw;276int rc;277278/* enabling migration mode on a VM without memory should fail */279rc = __enable_migration_mode(vm);280TEST_ASSERT_EQ(rc, -1);281TEST_ASSERT_EQ(errno, EINVAL);282TEST_ASSERT(!is_migration_mode_on(vm), "migration mode should still be off");283errno = 0;284285create_memslots(vm);286finish_vm_setup(vm);287288enable_cmma(vm);289vcpu = vm_vcpu_add(vm, 1, guest_do_one_essa);290orig_psw = vcpu->run->psw_addr;291292/*293* Execute one essa instruction in the guest. Otherwise the guest will294* not have use_cmm enabled and GET_CMMA_BITS will return no pages.295*/296vcpu_run(vcpu);297assert_exit_was_hypercall(vcpu);298299/* migration mode when memslots have dirty tracking off should fail */300rc = __enable_migration_mode(vm);301TEST_ASSERT_EQ(rc, -1);302TEST_ASSERT_EQ(errno, EINVAL);303TEST_ASSERT(!is_migration_mode_on(vm), "migration mode should still be off");304errno = 0;305306/* enable dirty tracking */307enable_dirty_tracking(vm);308309/* enabling migration mode should work now */310rc = __enable_migration_mode(vm);311TEST_ASSERT_EQ(rc, 0);312TEST_ASSERT(is_migration_mode_on(vm), "migration mode should be on");313errno = 0;314315/* execute another ESSA instruction to see this goes fine */316vcpu->run->psw_addr = orig_psw;317vcpu_run(vcpu);318assert_exit_was_hypercall(vcpu);319320/*321* With migration mode on, create a new memslot with dirty tracking off.322* This should turn off migration mode.323*/324TEST_ASSERT(is_migration_mode_on(vm), "migration mode should be on");325vm_userspace_mem_region_add(vm,326VM_MEM_SRC_ANONYMOUS,327TEST_DATA_TWO_START_GFN << vm->page_shift,328TEST_DATA_TWO_MEMSLOT,329TEST_DATA_TWO_PAGE_COUNT,3300331);332TEST_ASSERT(!is_migration_mode_on(vm),333"creating memslot without dirty tracking turns off migration mode"334);335336/* ESSA instructions should still execute fine */337vcpu->run->psw_addr = orig_psw;338vcpu_run(vcpu);339assert_exit_was_hypercall(vcpu);340341/*342* Turn on dirty tracking on the new memslot.343* It should be possible to turn migration mode back on again.344*/345vm_mem_region_set_flags(vm, TEST_DATA_TWO_MEMSLOT, KVM_MEM_LOG_DIRTY_PAGES);346rc = __enable_migration_mode(vm);347TEST_ASSERT_EQ(rc, 0);348TEST_ASSERT(is_migration_mode_on(vm), "migration mode should be on");349errno = 0;350351/*352* Turn off dirty tracking again, this time with just a flag change.353* Again, migration mode should turn off.354*/355TEST_ASSERT(is_migration_mode_on(vm), "migration mode should be on");356vm_mem_region_set_flags(vm, TEST_DATA_TWO_MEMSLOT, 0);357TEST_ASSERT(!is_migration_mode_on(vm),358"disabling dirty tracking should turn off migration mode"359);360361/* ESSA instructions should still execute fine */362vcpu->run->psw_addr = orig_psw;363vcpu_run(vcpu);364assert_exit_was_hypercall(vcpu);365366kvm_vm_free(vm);367}368369/**370* Given a VM with the MAIN and TEST_DATA memslot, assert that both slots have371* CMMA attributes of all pages in both memslots and nothing more dirty.372* This has the useful side effect of ensuring nothing is CMMA dirty after this373* function.374*/375static void assert_all_slots_cmma_dirty(struct kvm_vm *vm)376{377struct kvm_s390_cmma_log args;378379/*380* First iteration - everything should be dirty.381* Start at the main memslot...382*/383args = (struct kvm_s390_cmma_log){384.start_gfn = 0,385.count = sizeof(cmma_value_buf),386.flags = 0,387.values = (__u64)&cmma_value_buf[0]388};389memset(cmma_value_buf, 0xff, sizeof(cmma_value_buf));390vm_ioctl(vm, KVM_S390_GET_CMMA_BITS, &args);391TEST_ASSERT_EQ(args.count, MAIN_PAGE_COUNT);392TEST_ASSERT_EQ(args.remaining, TEST_DATA_PAGE_COUNT);393TEST_ASSERT_EQ(args.start_gfn, 0);394395/* ...and then - after a hole - the TEST_DATA memslot should follow */396args = (struct kvm_s390_cmma_log){397.start_gfn = MAIN_PAGE_COUNT,398.count = sizeof(cmma_value_buf),399.flags = 0,400.values = (__u64)&cmma_value_buf[0]401};402memset(cmma_value_buf, 0xff, sizeof(cmma_value_buf));403vm_ioctl(vm, KVM_S390_GET_CMMA_BITS, &args);404TEST_ASSERT_EQ(args.count, TEST_DATA_PAGE_COUNT);405TEST_ASSERT_EQ(args.start_gfn, TEST_DATA_START_GFN);406TEST_ASSERT_EQ(args.remaining, 0);407408/* ...and nothing else should be there */409args = (struct kvm_s390_cmma_log){410.start_gfn = TEST_DATA_START_GFN + TEST_DATA_PAGE_COUNT,411.count = sizeof(cmma_value_buf),412.flags = 0,413.values = (__u64)&cmma_value_buf[0]414};415memset(cmma_value_buf, 0xff, sizeof(cmma_value_buf));416vm_ioctl(vm, KVM_S390_GET_CMMA_BITS, &args);417TEST_ASSERT_EQ(args.count, 0);418TEST_ASSERT_EQ(args.start_gfn, 0);419TEST_ASSERT_EQ(args.remaining, 0);420}421422/**423* Given a VM, assert no pages are CMMA dirty.424*/425static void assert_no_pages_cmma_dirty(struct kvm_vm *vm)426{427struct kvm_s390_cmma_log args;428429/* If we start from GFN 0 again, nothing should be dirty. */430args = (struct kvm_s390_cmma_log){431.start_gfn = 0,432.count = sizeof(cmma_value_buf),433.flags = 0,434.values = (__u64)&cmma_value_buf[0]435};436memset(cmma_value_buf, 0xff, sizeof(cmma_value_buf));437vm_ioctl(vm, KVM_S390_GET_CMMA_BITS, &args);438if (args.count || args.remaining || args.start_gfn)439TEST_FAIL("pages are still dirty start_gfn=0x%llx count=%u remaining=%llu",440args.start_gfn,441args.count,442args.remaining443);444}445446static void test_get_initial_dirty(void)447{448struct kvm_vm *vm = create_vm_two_memslots();449struct kvm_vcpu *vcpu;450451enable_cmma(vm);452vcpu = vm_vcpu_add(vm, 1, guest_do_one_essa);453454/*455* Execute one essa instruction in the guest. Otherwise the guest will456* not have use_cmm enabled and GET_CMMA_BITS will return no pages.457*/458vcpu_run(vcpu);459assert_exit_was_hypercall(vcpu);460461enable_dirty_tracking(vm);462enable_migration_mode(vm);463464assert_all_slots_cmma_dirty(vm);465466/* Start from the beginning again and make sure nothing else is dirty */467assert_no_pages_cmma_dirty(vm);468469kvm_vm_free(vm);470}471472static void query_cmma_range(struct kvm_vm *vm,473u64 start_gfn, u64 gfn_count,474struct kvm_s390_cmma_log *res_out)475{476*res_out = (struct kvm_s390_cmma_log){477.start_gfn = start_gfn,478.count = gfn_count,479.flags = 0,480.values = (__u64)&cmma_value_buf[0]481};482memset(cmma_value_buf, 0xff, sizeof(cmma_value_buf));483vm_ioctl(vm, KVM_S390_GET_CMMA_BITS, res_out);484}485486/**487* Assert the given cmma_log struct that was executed by query_cmma_range()488* indicates the first dirty gfn is at first_dirty_gfn and contains exactly489* dirty_gfn_count CMMA values.490*/491static void assert_cmma_dirty(u64 first_dirty_gfn,492u64 dirty_gfn_count,493const struct kvm_s390_cmma_log *res)494{495TEST_ASSERT_EQ(res->start_gfn, first_dirty_gfn);496TEST_ASSERT_EQ(res->count, dirty_gfn_count);497for (size_t i = 0; i < dirty_gfn_count; i++)498TEST_ASSERT_EQ(cmma_value_buf[0], 0x0); /* stable state */499TEST_ASSERT_EQ(cmma_value_buf[dirty_gfn_count], 0xff); /* not touched */500}501502static void test_get_skip_holes(void)503{504size_t gfn_offset;505struct kvm_vm *vm = create_vm_two_memslots();506struct kvm_s390_cmma_log log;507struct kvm_vcpu *vcpu;508u64 orig_psw;509510enable_cmma(vm);511vcpu = vm_vcpu_add(vm, 1, guest_dirty_test_data);512513orig_psw = vcpu->run->psw_addr;514515/*516* Execute some essa instructions in the guest. Otherwise the guest will517* not have use_cmm enabled and GET_CMMA_BITS will return no pages.518*/519vcpu_run(vcpu);520assert_exit_was_hypercall(vcpu);521522enable_dirty_tracking(vm);523enable_migration_mode(vm);524525/* un-dirty all pages */526assert_all_slots_cmma_dirty(vm);527528/* Then, dirty just the TEST_DATA memslot */529vcpu->run->psw_addr = orig_psw;530vcpu_run(vcpu);531532gfn_offset = TEST_DATA_START_GFN;533/**534* Query CMMA attributes of one page, starting at page 0. Since the535* main memslot was not touched by the VM, this should yield the first536* page of the TEST_DATA memslot.537* The dirty bitmap should now look like this:538* 0: not dirty539* [0x1, 0x200): dirty540*/541query_cmma_range(vm, 0, 1, &log);542assert_cmma_dirty(gfn_offset, 1, &log);543gfn_offset++;544545/**546* Query CMMA attributes of 32 (0x20) pages past the end of the TEST_DATA547* memslot. This should wrap back to the beginning of the TEST_DATA548* memslot, page 1.549* The dirty bitmap should now look like this:550* [0, 0x21): not dirty551* [0x21, 0x200): dirty552*/553query_cmma_range(vm, TEST_DATA_START_GFN + TEST_DATA_PAGE_COUNT, 0x20, &log);554assert_cmma_dirty(gfn_offset, 0x20, &log);555gfn_offset += 0x20;556557/* Skip 32 pages */558gfn_offset += 0x20;559560/**561* After skipping 32 pages, query the next 32 (0x20) pages.562* The dirty bitmap should now look like this:563* [0, 0x21): not dirty564* [0x21, 0x41): dirty565* [0x41, 0x61): not dirty566* [0x61, 0x200): dirty567*/568query_cmma_range(vm, gfn_offset, 0x20, &log);569assert_cmma_dirty(gfn_offset, 0x20, &log);570gfn_offset += 0x20;571572/**573* Query 1 page from the beginning of the TEST_DATA memslot. This should574* yield page 0x21.575* The dirty bitmap should now look like this:576* [0, 0x22): not dirty577* [0x22, 0x41): dirty578* [0x41, 0x61): not dirty579* [0x61, 0x200): dirty580*/581query_cmma_range(vm, TEST_DATA_START_GFN, 1, &log);582assert_cmma_dirty(TEST_DATA_START_GFN + 0x21, 1, &log);583gfn_offset++;584585/**586* Query 15 (0xF) pages from page 0x23 in TEST_DATA memslot.587* This should yield pages [0x23, 0x33).588* The dirty bitmap should now look like this:589* [0, 0x22): not dirty590* 0x22: dirty591* [0x23, 0x33): not dirty592* [0x33, 0x41): dirty593* [0x41, 0x61): not dirty594* [0x61, 0x200): dirty595*/596gfn_offset = TEST_DATA_START_GFN + 0x23;597query_cmma_range(vm, gfn_offset, 15, &log);598assert_cmma_dirty(gfn_offset, 15, &log);599600/**601* Query 17 (0x11) pages from page 0x22 in TEST_DATA memslot.602* This should yield page [0x22, 0x33)603* The dirty bitmap should now look like this:604* [0, 0x33): not dirty605* [0x33, 0x41): dirty606* [0x41, 0x61): not dirty607* [0x61, 0x200): dirty608*/609gfn_offset = TEST_DATA_START_GFN + 0x22;610query_cmma_range(vm, gfn_offset, 17, &log);611assert_cmma_dirty(gfn_offset, 17, &log);612613/**614* Query 25 (0x19) pages from page 0x40 in TEST_DATA memslot.615* This should yield page 0x40 and nothing more, since there are more616* than 16 non-dirty pages after page 0x40.617* The dirty bitmap should now look like this:618* [0, 0x33): not dirty619* [0x33, 0x40): dirty620* [0x40, 0x61): not dirty621* [0x61, 0x200): dirty622*/623gfn_offset = TEST_DATA_START_GFN + 0x40;624query_cmma_range(vm, gfn_offset, 25, &log);625assert_cmma_dirty(gfn_offset, 1, &log);626627/**628* Query pages [0x33, 0x40).629* The dirty bitmap should now look like this:630* [0, 0x61): not dirty631* [0x61, 0x200): dirty632*/633gfn_offset = TEST_DATA_START_GFN + 0x33;634query_cmma_range(vm, gfn_offset, 0x40 - 0x33, &log);635assert_cmma_dirty(gfn_offset, 0x40 - 0x33, &log);636637/**638* Query the remaining pages [0x61, 0x200).639*/640gfn_offset = TEST_DATA_START_GFN;641query_cmma_range(vm, gfn_offset, TEST_DATA_PAGE_COUNT - 0x61, &log);642assert_cmma_dirty(TEST_DATA_START_GFN + 0x61, TEST_DATA_PAGE_COUNT - 0x61, &log);643644assert_no_pages_cmma_dirty(vm);645}646647struct testdef {648const char *name;649void (*test)(void);650} testlist[] = {651{ "migration mode and dirty tracking", test_migration_mode },652{ "GET_CMMA_BITS: basic calls", test_get_cmma_basic },653{ "GET_CMMA_BITS: all pages are dirty initially", test_get_initial_dirty },654{ "GET_CMMA_BITS: holes are skipped", test_get_skip_holes },655};656657/**658* The kernel may support CMMA, but the machine may not (i.e. if running as659* guest-3).660*661* In this case, the CMMA capabilities are all there, but the CMMA-related662* ioctls fail. To find out whether the machine supports CMMA, create a663* temporary VM and then query the CMMA feature of the VM.664*/665static int machine_has_cmma(void)666{667struct kvm_vm *vm = vm_create_barebones();668int r;669670r = !__kvm_has_device_attr(vm->fd, KVM_S390_VM_MEM_CTRL, KVM_S390_VM_MEM_ENABLE_CMMA);671kvm_vm_free(vm);672673return r;674}675676int main(int argc, char *argv[])677{678int idx;679680TEST_REQUIRE(kvm_has_cap(KVM_CAP_SYNC_REGS));681TEST_REQUIRE(kvm_has_cap(KVM_CAP_S390_CMMA_MIGRATION));682TEST_REQUIRE(machine_has_cmma());683684ksft_print_header();685686ksft_set_plan(ARRAY_SIZE(testlist));687688for (idx = 0; idx < ARRAY_SIZE(testlist); idx++) {689testlist[idx].test();690ksft_test_result_pass("%s\n", testlist[idx].name);691}692693ksft_finished(); /* Print results and exit() accordingly */694}695696697