Add a guest_memfd flag to allow userspace to state that the underlying memory should be configured to be initialized as shared, and reject user page faults if the guest_memfd instance's memory isn't shared. Because KVM doesn't yet support in-place private<=>shared conversions, all guest_memfd memory effectively follows the initial state. Alternatively, KVM could deduce the initial state based on MMAP, which for all intents and purposes is what KVM currently does. However, implicitly deriving the default state based on MMAP will result in a messy ABI when support for in-place conversions is added. For x86 CoCo VMs, which don't yet support MMAP, memory is currently private by default (otherwise the memory would be unusable). If MMAP implies memory is shared by default, then the default state for CoCo VMs will vary based on MMAP, and from userspace's perspective, will change when in-place conversion support is added. I.e. to maintain guest<=>host ABI, userspace would need to immediately convert all memory from shared=>private, which is both ugly and inefficient. The inefficiency could be avoided by adding a flag to state that memory is _private_ by default, irrespective of MMAP, but that would lead to an equally messy and hard to document ABI. Bite the bullet and immediately add a flag to control the default state so that the effective behavior is explicit and straightforward.
Unified · Split
| @@ -6438,6 +6438,11 @@ specified via KVM_CREATE_GUEST_MEMFD. Currently defined flags: · 6437 unmodified lines | |||
| 6438 | ============================ ================================================ | 6438 | ============================ ================================================ |
| 6439 | GUEST_MEMFD_FLAG_MMAP Enable using mmap() on the guest_memfd file | 6439 | GUEST_MEMFD_FLAG_MMAP Enable using mmap() on the guest_memfd file |
| 6440 | descriptor. | 6440 | descriptor. |
| 6441 | GUEST_MEMFD_FLAG_INIT_SHARED Make all memory in the file shared during | ||
| 6442 | KVM_CREATE_GUEST_MEMFD (memory files created | ||
| 6443 | without INIT_SHARED will be marked private). | ||
| 6444 | Shared memory can be faulted into host userspace | ||
| 6445 | page tables. Private memory cannot. | ||
| 6441 | ============================ ================================================ | 6446 | ============================ ================================================ |
| 6442 | 6447 | ||
| 6443 | When the KVM MMU performs a PFN lookup to service a guest fault and the backing | 6448 | When the KVM MMU performs a PFN lookup to service a guest fault and the backing |
| @@ -1599,7 +1599,8 @@ struct kvm_memory_attributes { · 1598 unmodified lines | |||
| 1599 | #define KVM_MEMORY_ATTRIBUTE_PRIVATE (1ULL << 3) | 1599 | #define KVM_MEMORY_ATTRIBUTE_PRIVATE (1ULL << 3) |
| 1600 | 1600 | ||
| 1601 | #define KVM_CREATE_GUEST_MEMFD _IOWR(KVMIO, 0xd4, struct kvm_create_guest_memfd) | 1601 | #define KVM_CREATE_GUEST_MEMFD _IOWR(KVMIO, 0xd4, struct kvm_create_guest_memfd) |
| 1602 | #define GUEST_MEMFD_FLAG_MMAP (1ULL << 0) | 1602 | #define GUEST_MEMFD_FLAG_MMAP (1ULL << 0) |
| 1603 | #define GUEST_MEMFD_FLAG_INIT_SHARED (1ULL << 1) | ||
| 1603 | 1604 | ||
| 1604 | struct kvm_create_guest_memfd { | 1605 | struct kvm_create_guest_memfd { |
| 1605 | __u64 size; | 1606 | __u64 size; |
| @@ -239,8 +239,9 @@ static void test_create_guest_memfd_multiple(struct kvm_vm *vm) · 238 unmodified lines | |||
| 239 | close(fd1); | 239 | close(fd1); |
| 240 | } | 240 | } |
| 241 | 241 | ||
| 242 | static void test_guest_memfd_flags(struct kvm_vm *vm, uint64_t valid_flags) | 242 | static void test_guest_memfd_flags(struct kvm_vm *vm) |
| 243 | { | 243 | { |
| 244 | uint64_t valid_flags = vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS); | ||
| 244 | size_t page_size = getpagesize(); | 245 | size_t page_size = getpagesize(); |
| 245 | uint64_t flag; | 246 | uint64_t flag; |
| 246 | int fd; | 247 | int fd; |
| @@ -274,6 +275,10 @@ static void test_guest_memfd(unsigned long vm_type) · 27 unmodified lines | |||
| 274 | vm = vm_create_barebones_type(vm_type); | 275 | vm = vm_create_barebones_type(vm_type); |
| 275 | flags = vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS); | 276 | flags = vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS); |
| 276 | 277 | ||
| 278 | /* This test doesn't yet support testing mmap() on private memory. */ | ||
| 279 | if (!(flags & GUEST_MEMFD_FLAG_INIT_SHARED)) | ||
| 280 | flags &= ~GUEST_MEMFD_FLAG_MMAP; | ||
| 281 | |||
| 277 | test_create_guest_memfd_multiple(vm); | 282 | test_create_guest_memfd_multiple(vm); |
| 278 | test_create_guest_memfd_invalid_sizes(vm, flags, page_size); | 283 | test_create_guest_memfd_invalid_sizes(vm, flags, page_size); |
| 279 | 284 | ||
| @@ -292,7 +297,7 @@ static void test_guest_memfd(unsigned long vm_type) · 12 unmodified lines | |||
| 292 | test_fallocate(fd, page_size, total_size); | 297 | test_fallocate(fd, page_size, total_size); |
| 293 | test_invalid_punch_hole(fd, page_size, total_size); | 298 | test_invalid_punch_hole(fd, page_size, total_size); |
| 294 | 299 | ||
| 295 | test_guest_memfd_flags(vm, flags); | 300 | test_guest_memfd_flags(vm); |
| 296 | 301 | ||
| 297 | close(fd); | 302 | close(fd); |
| 298 | kvm_vm_free(vm); | 303 | kvm_vm_free(vm); |
| @@ -334,9 +339,13 @@ static void test_guest_memfd_guest(void) · 35 unmodified lines | |||
| 334 | TEST_ASSERT(vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS) & GUEST_MEMFD_FLAG_MMAP, | 339 | TEST_ASSERT(vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS) & GUEST_MEMFD_FLAG_MMAP, |
| 335 | "Default VM type should support MMAP, supported flags = 0x%x", | 340 | "Default VM type should support MMAP, supported flags = 0x%x", |
| 336 | vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS)); | 341 | vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS)); |
| 342 | TEST_ASSERT(vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS) & GUEST_MEMFD_FLAG_INIT_SHARED, | ||
| 343 | "Default VM type should support INIT_SHARED, supported flags = 0x%x", | ||
| 344 | vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS)); | ||
| 337 | 345 | ||
| 338 | size = vm->page_size; | 346 | size = vm->page_size; |
| 339 | fd = vm_create_guest_memfd(vm, size, GUEST_MEMFD_FLAG_MMAP); | 347 | fd = vm_create_guest_memfd(vm, size, GUEST_MEMFD_FLAG_MMAP | |
| 348 | GUEST_MEMFD_FLAG_INIT_SHARED); | ||
| 340 | vm_set_user_memory_region2(vm, slot, KVM_MEM_GUEST_MEMFD, gpa, size, NULL, fd, 0); | 349 | vm_set_user_memory_region2(vm, slot, KVM_MEM_GUEST_MEMFD, gpa, size, NULL, fd, 0); |
| 341 | 350 | ||
| 342 | mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | 351 | mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); |
| @@ -328,6 +328,9 @@ static vm_fault_t kvm_gmem_fault_user_mapping(struct vm_fault *vmf) · 327 unmodified lines | |||
| 328 | if (((loff_t)vmf->pgoff << PAGE_SHIFT) >= i_size_read(inode)) | 328 | if (((loff_t)vmf->pgoff << PAGE_SHIFT) >= i_size_read(inode)) |
| 329 | return VM_FAULT_SIGBUS; | 329 | return VM_FAULT_SIGBUS; |
| 330 | 330 | ||
| 331 | if (!((u64)inode->i_private & GUEST_MEMFD_FLAG_INIT_SHARED)) | ||
| 332 | return VM_FAULT_SIGBUS; | ||
| 333 | |||
| 331 | folio = kvm_gmem_get_folio(inode, vmf->pgoff); | 334 | folio = kvm_gmem_get_folio(inode, vmf->pgoff); |
| 332 | if (IS_ERR(folio)) { | 335 | if (IS_ERR(folio)) { |
| 333 | int err = PTR_ERR(folio); | 336 | int err = PTR_ERR(folio); |
| @@ -525,7 +528,8 @@ int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args) · 191 unmodified lines | |||
| 525 | u64 valid_flags = 0; | 528 | u64 valid_flags = 0; |
| 526 | 529 | ||
| 527 | if (kvm_arch_supports_gmem_mmap(kvm)) | 530 | if (kvm_arch_supports_gmem_mmap(kvm)) |
| 528 | valid_flags |= GUEST_MEMFD_FLAG_MMAP; | 531 | valid_flags |= GUEST_MEMFD_FLAG_MMAP | |
| 532 | GUEST_MEMFD_FLAG_INIT_SHARED; | ||
| 529 | 533 | ||
| 530 | if (flags & ~valid_flags) | 534 | if (flags & ~valid_flags) |
| 531 | return -EINVAL; | 535 | return -EINVAL; |
| @@ -4930,7 +4930,8 @@ static int kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg) · 4929 unmodified lines | |||
| 4930 | return 1; | 4930 | return 1; |
| 4931 | case KVM_CAP_GUEST_MEMFD_FLAGS: | 4931 | case KVM_CAP_GUEST_MEMFD_FLAGS: |
| 4932 | if (!kvm || kvm_arch_supports_gmem_mmap(kvm)) | 4932 | if (!kvm || kvm_arch_supports_gmem_mmap(kvm)) |
| 4933 | return GUEST_MEMFD_FLAG_MMAP; | 4933 | return GUEST_MEMFD_FLAG_MMAP | |
| 4934 | GUEST_MEMFD_FLAG_INIT_SHARED; | ||
| 4934 | 4935 | ||
| 4935 | return 0; | 4936 | return 0; |
| 4936 | #endif | 4937 | #endif |