KVM: implement multiple address spaces
Only two ioctls have to be modified; the address space id is placed in the higher 16 bits of their slot id argument. As of this patch, no architecture defines more than one address space; x86 will be the first. Reviewed-by: Radim Krčmář <rkrcmar@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
8e73485c79
commit
f481b069e6
@ -254,6 +254,11 @@ since the last call to this ioctl. Bit 0 is the first page in the
|
|||||||
memory slot. Ensure the entire structure is cleared to avoid padding
|
memory slot. Ensure the entire structure is cleared to avoid padding
|
||||||
issues.
|
issues.
|
||||||
|
|
||||||
|
If KVM_CAP_MULTI_ADDRESS_SPACE is available, bits 16-31 specifies
|
||||||
|
the address space for which you want to return the dirty bitmap.
|
||||||
|
They must be less than the value that KVM_CHECK_EXTENSION returns for
|
||||||
|
the KVM_CAP_MULTI_ADDRESS_SPACE capability.
|
||||||
|
|
||||||
|
|
||||||
4.9 KVM_SET_MEMORY_ALIAS
|
4.9 KVM_SET_MEMORY_ALIAS
|
||||||
|
|
||||||
@ -924,6 +929,13 @@ slot. When changing an existing slot, it may be moved in the guest
|
|||||||
physical memory space, or its flags may be modified. It may not be
|
physical memory space, or its flags may be modified. It may not be
|
||||||
resized. Slots may not overlap in guest physical address space.
|
resized. Slots may not overlap in guest physical address space.
|
||||||
|
|
||||||
|
If KVM_CAP_MULTI_ADDRESS_SPACE is available, bits 16-31 of "slot"
|
||||||
|
specifies the address space which is being modified. They must be
|
||||||
|
less than the value that KVM_CHECK_EXTENSION returns for the
|
||||||
|
KVM_CAP_MULTI_ADDRESS_SPACE capability. Slots in separate address spaces
|
||||||
|
are unrelated; the restriction on overlapping slots only applies within
|
||||||
|
each address space.
|
||||||
|
|
||||||
Memory for the region is taken starting at the address denoted by the
|
Memory for the region is taken starting at the address denoted by the
|
||||||
field userspace_addr, which must point at user addressable memory for
|
field userspace_addr, which must point at user addressable memory for
|
||||||
the entire memory slot size. Any object may back this memory, including
|
the entire memory slot size. Any object may back this memory, including
|
||||||
|
@ -430,7 +430,7 @@ static inline void note_hpte_modification(struct kvm *kvm,
|
|||||||
*/
|
*/
|
||||||
static inline struct kvm_memslots *kvm_memslots_raw(struct kvm *kvm)
|
static inline struct kvm_memslots *kvm_memslots_raw(struct kvm *kvm)
|
||||||
{
|
{
|
||||||
return rcu_dereference_raw_notrace(kvm->memslots);
|
return rcu_dereference_raw_notrace(kvm->memslots[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void kvmppc_mmu_debugfs_init(struct kvm *kvm);
|
extern void kvmppc_mmu_debugfs_init(struct kvm *kvm);
|
||||||
|
@ -44,6 +44,10 @@
|
|||||||
/* Two fragments for cross MMIO pages. */
|
/* Two fragments for cross MMIO pages. */
|
||||||
#define KVM_MAX_MMIO_FRAGMENTS 2
|
#define KVM_MAX_MMIO_FRAGMENTS 2
|
||||||
|
|
||||||
|
#ifndef KVM_ADDRESS_SPACE_NUM
|
||||||
|
#define KVM_ADDRESS_SPACE_NUM 1
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For the normal pfn, the highest 12 bits should be zero,
|
* For the normal pfn, the highest 12 bits should be zero,
|
||||||
* so we can mask bit 62 ~ bit 52 to indicate the error pfn,
|
* so we can mask bit 62 ~ bit 52 to indicate the error pfn,
|
||||||
@ -331,6 +335,13 @@ struct kvm_kernel_irq_routing_entry {
|
|||||||
#define KVM_MEM_SLOTS_NUM (KVM_USER_MEM_SLOTS + KVM_PRIVATE_MEM_SLOTS)
|
#define KVM_MEM_SLOTS_NUM (KVM_USER_MEM_SLOTS + KVM_PRIVATE_MEM_SLOTS)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef __KVM_VCPU_MULTIPLE_ADDRESS_SPACE
|
||||||
|
static inline int kvm_arch_vcpu_memslots_id(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note:
|
* Note:
|
||||||
* memslots are not sorted by id anymore, please use id_to_memslot()
|
* memslots are not sorted by id anymore, please use id_to_memslot()
|
||||||
@ -349,7 +360,7 @@ struct kvm {
|
|||||||
spinlock_t mmu_lock;
|
spinlock_t mmu_lock;
|
||||||
struct mutex slots_lock;
|
struct mutex slots_lock;
|
||||||
struct mm_struct *mm; /* userspace tied to this vm */
|
struct mm_struct *mm; /* userspace tied to this vm */
|
||||||
struct kvm_memslots *memslots;
|
struct kvm_memslots *memslots[KVM_ADDRESS_SPACE_NUM];
|
||||||
struct srcu_struct srcu;
|
struct srcu_struct srcu;
|
||||||
struct srcu_struct irq_srcu;
|
struct srcu_struct irq_srcu;
|
||||||
#ifdef CONFIG_KVM_APIC_ARCHITECTURE
|
#ifdef CONFIG_KVM_APIC_ARCHITECTURE
|
||||||
@ -464,16 +475,23 @@ void kvm_exit(void);
|
|||||||
void kvm_get_kvm(struct kvm *kvm);
|
void kvm_get_kvm(struct kvm *kvm);
|
||||||
void kvm_put_kvm(struct kvm *kvm);
|
void kvm_put_kvm(struct kvm *kvm);
|
||||||
|
|
||||||
static inline struct kvm_memslots *kvm_memslots(struct kvm *kvm)
|
static inline struct kvm_memslots *__kvm_memslots(struct kvm *kvm, int as_id)
|
||||||
{
|
{
|
||||||
return rcu_dereference_check(kvm->memslots,
|
return rcu_dereference_check(kvm->memslots[as_id],
|
||||||
srcu_read_lock_held(&kvm->srcu)
|
srcu_read_lock_held(&kvm->srcu)
|
||||||
|| lockdep_is_held(&kvm->slots_lock));
|
|| lockdep_is_held(&kvm->slots_lock));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline struct kvm_memslots *kvm_memslots(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
return __kvm_memslots(kvm, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static inline struct kvm_memslots *kvm_vcpu_memslots(struct kvm_vcpu *vcpu)
|
static inline struct kvm_memslots *kvm_vcpu_memslots(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
return kvm_memslots(vcpu->kvm);
|
int as_id = kvm_arch_vcpu_memslots_id(vcpu);
|
||||||
|
|
||||||
|
return __kvm_memslots(vcpu->kvm, as_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct kvm_memory_slot *
|
static inline struct kvm_memory_slot *
|
||||||
|
@ -816,6 +816,7 @@ struct kvm_ppc_smmu_info {
|
|||||||
#define KVM_CAP_PPC_HWRNG 115
|
#define KVM_CAP_PPC_HWRNG 115
|
||||||
#define KVM_CAP_DISABLE_QUIRKS 116
|
#define KVM_CAP_DISABLE_QUIRKS 116
|
||||||
#define KVM_CAP_X86_SMM 117
|
#define KVM_CAP_X86_SMM 117
|
||||||
|
#define KVM_CAP_MULTI_ADDRESS_SPACE 118
|
||||||
|
|
||||||
#ifdef KVM_CAP_IRQ_ROUTING
|
#ifdef KVM_CAP_IRQ_ROUTING
|
||||||
|
|
||||||
|
@ -518,9 +518,11 @@ static struct kvm *kvm_create_vm(unsigned long type)
|
|||||||
BUILD_BUG_ON(KVM_MEM_SLOTS_NUM > SHRT_MAX);
|
BUILD_BUG_ON(KVM_MEM_SLOTS_NUM > SHRT_MAX);
|
||||||
|
|
||||||
r = -ENOMEM;
|
r = -ENOMEM;
|
||||||
kvm->memslots = kvm_alloc_memslots();
|
for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++) {
|
||||||
if (!kvm->memslots)
|
kvm->memslots[i] = kvm_alloc_memslots();
|
||||||
|
if (!kvm->memslots[i])
|
||||||
goto out_err_no_srcu;
|
goto out_err_no_srcu;
|
||||||
|
}
|
||||||
|
|
||||||
if (init_srcu_struct(&kvm->srcu))
|
if (init_srcu_struct(&kvm->srcu))
|
||||||
goto out_err_no_srcu;
|
goto out_err_no_srcu;
|
||||||
@ -562,7 +564,8 @@ out_err_no_srcu:
|
|||||||
out_err_no_disable:
|
out_err_no_disable:
|
||||||
for (i = 0; i < KVM_NR_BUSES; i++)
|
for (i = 0; i < KVM_NR_BUSES; i++)
|
||||||
kfree(kvm->buses[i]);
|
kfree(kvm->buses[i]);
|
||||||
kvm_free_memslots(kvm, kvm->memslots);
|
for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++)
|
||||||
|
kvm_free_memslots(kvm, kvm->memslots[i]);
|
||||||
kvm_arch_free_vm(kvm);
|
kvm_arch_free_vm(kvm);
|
||||||
return ERR_PTR(r);
|
return ERR_PTR(r);
|
||||||
}
|
}
|
||||||
@ -612,7 +615,8 @@ static void kvm_destroy_vm(struct kvm *kvm)
|
|||||||
#endif
|
#endif
|
||||||
kvm_arch_destroy_vm(kvm);
|
kvm_arch_destroy_vm(kvm);
|
||||||
kvm_destroy_devices(kvm);
|
kvm_destroy_devices(kvm);
|
||||||
kvm_free_memslots(kvm, kvm->memslots);
|
for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++)
|
||||||
|
kvm_free_memslots(kvm, kvm->memslots[i]);
|
||||||
cleanup_srcu_struct(&kvm->irq_srcu);
|
cleanup_srcu_struct(&kvm->irq_srcu);
|
||||||
cleanup_srcu_struct(&kvm->srcu);
|
cleanup_srcu_struct(&kvm->srcu);
|
||||||
kvm_arch_free_vm(kvm);
|
kvm_arch_free_vm(kvm);
|
||||||
@ -729,9 +733,9 @@ static int check_memory_region_flags(const struct kvm_userspace_memory_region *m
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct kvm_memslots *install_new_memslots(struct kvm *kvm,
|
static struct kvm_memslots *install_new_memslots(struct kvm *kvm,
|
||||||
struct kvm_memslots *slots)
|
int as_id, struct kvm_memslots *slots)
|
||||||
{
|
{
|
||||||
struct kvm_memslots *old_memslots = kvm_memslots(kvm);
|
struct kvm_memslots *old_memslots = __kvm_memslots(kvm, as_id);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the low bit in the generation, which disables SPTE caching
|
* Set the low bit in the generation, which disables SPTE caching
|
||||||
@ -740,7 +744,7 @@ static struct kvm_memslots *install_new_memslots(struct kvm *kvm,
|
|||||||
WARN_ON(old_memslots->generation & 1);
|
WARN_ON(old_memslots->generation & 1);
|
||||||
slots->generation = old_memslots->generation + 1;
|
slots->generation = old_memslots->generation + 1;
|
||||||
|
|
||||||
rcu_assign_pointer(kvm->memslots, slots);
|
rcu_assign_pointer(kvm->memslots[as_id], slots);
|
||||||
synchronize_srcu_expedited(&kvm->srcu);
|
synchronize_srcu_expedited(&kvm->srcu);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -772,6 +776,7 @@ int __kvm_set_memory_region(struct kvm *kvm,
|
|||||||
struct kvm_memory_slot *slot;
|
struct kvm_memory_slot *slot;
|
||||||
struct kvm_memory_slot old, new;
|
struct kvm_memory_slot old, new;
|
||||||
struct kvm_memslots *slots = NULL, *old_memslots;
|
struct kvm_memslots *slots = NULL, *old_memslots;
|
||||||
|
int as_id, id;
|
||||||
enum kvm_mr_change change;
|
enum kvm_mr_change change;
|
||||||
|
|
||||||
r = check_memory_region_flags(mem);
|
r = check_memory_region_flags(mem);
|
||||||
@ -779,24 +784,27 @@ int __kvm_set_memory_region(struct kvm *kvm,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
r = -EINVAL;
|
r = -EINVAL;
|
||||||
|
as_id = mem->slot >> 16;
|
||||||
|
id = (u16)mem->slot;
|
||||||
|
|
||||||
/* General sanity checks */
|
/* General sanity checks */
|
||||||
if (mem->memory_size & (PAGE_SIZE - 1))
|
if (mem->memory_size & (PAGE_SIZE - 1))
|
||||||
goto out;
|
goto out;
|
||||||
if (mem->guest_phys_addr & (PAGE_SIZE - 1))
|
if (mem->guest_phys_addr & (PAGE_SIZE - 1))
|
||||||
goto out;
|
goto out;
|
||||||
/* We can read the guest memory with __xxx_user() later on. */
|
/* We can read the guest memory with __xxx_user() later on. */
|
||||||
if ((mem->slot < KVM_USER_MEM_SLOTS) &&
|
if ((id < KVM_USER_MEM_SLOTS) &&
|
||||||
((mem->userspace_addr & (PAGE_SIZE - 1)) ||
|
((mem->userspace_addr & (PAGE_SIZE - 1)) ||
|
||||||
!access_ok(VERIFY_WRITE,
|
!access_ok(VERIFY_WRITE,
|
||||||
(void __user *)(unsigned long)mem->userspace_addr,
|
(void __user *)(unsigned long)mem->userspace_addr,
|
||||||
mem->memory_size)))
|
mem->memory_size)))
|
||||||
goto out;
|
goto out;
|
||||||
if (mem->slot >= KVM_MEM_SLOTS_NUM)
|
if (as_id >= KVM_ADDRESS_SPACE_NUM || id >= KVM_MEM_SLOTS_NUM)
|
||||||
goto out;
|
goto out;
|
||||||
if (mem->guest_phys_addr + mem->memory_size < mem->guest_phys_addr)
|
if (mem->guest_phys_addr + mem->memory_size < mem->guest_phys_addr)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
slot = id_to_memslot(kvm_memslots(kvm), mem->slot);
|
slot = id_to_memslot(__kvm_memslots(kvm, as_id), id);
|
||||||
base_gfn = mem->guest_phys_addr >> PAGE_SHIFT;
|
base_gfn = mem->guest_phys_addr >> PAGE_SHIFT;
|
||||||
npages = mem->memory_size >> PAGE_SHIFT;
|
npages = mem->memory_size >> PAGE_SHIFT;
|
||||||
|
|
||||||
@ -805,7 +813,7 @@ int __kvm_set_memory_region(struct kvm *kvm,
|
|||||||
|
|
||||||
new = old = *slot;
|
new = old = *slot;
|
||||||
|
|
||||||
new.id = mem->slot;
|
new.id = id;
|
||||||
new.base_gfn = base_gfn;
|
new.base_gfn = base_gfn;
|
||||||
new.npages = npages;
|
new.npages = npages;
|
||||||
new.flags = mem->flags;
|
new.flags = mem->flags;
|
||||||
@ -840,9 +848,9 @@ int __kvm_set_memory_region(struct kvm *kvm,
|
|||||||
if ((change == KVM_MR_CREATE) || (change == KVM_MR_MOVE)) {
|
if ((change == KVM_MR_CREATE) || (change == KVM_MR_MOVE)) {
|
||||||
/* Check for overlaps */
|
/* Check for overlaps */
|
||||||
r = -EEXIST;
|
r = -EEXIST;
|
||||||
kvm_for_each_memslot(slot, kvm_memslots(kvm)) {
|
kvm_for_each_memslot(slot, __kvm_memslots(kvm, as_id)) {
|
||||||
if ((slot->id >= KVM_USER_MEM_SLOTS) ||
|
if ((slot->id >= KVM_USER_MEM_SLOTS) ||
|
||||||
(slot->id == mem->slot))
|
(slot->id == id))
|
||||||
continue;
|
continue;
|
||||||
if (!((base_gfn + npages <= slot->base_gfn) ||
|
if (!((base_gfn + npages <= slot->base_gfn) ||
|
||||||
(base_gfn >= slot->base_gfn + slot->npages)))
|
(base_gfn >= slot->base_gfn + slot->npages)))
|
||||||
@ -871,13 +879,13 @@ int __kvm_set_memory_region(struct kvm *kvm,
|
|||||||
slots = kvm_kvzalloc(sizeof(struct kvm_memslots));
|
slots = kvm_kvzalloc(sizeof(struct kvm_memslots));
|
||||||
if (!slots)
|
if (!slots)
|
||||||
goto out_free;
|
goto out_free;
|
||||||
memcpy(slots, kvm_memslots(kvm), sizeof(struct kvm_memslots));
|
memcpy(slots, __kvm_memslots(kvm, as_id), sizeof(struct kvm_memslots));
|
||||||
|
|
||||||
if ((change == KVM_MR_DELETE) || (change == KVM_MR_MOVE)) {
|
if ((change == KVM_MR_DELETE) || (change == KVM_MR_MOVE)) {
|
||||||
slot = id_to_memslot(slots, mem->slot);
|
slot = id_to_memslot(slots, id);
|
||||||
slot->flags |= KVM_MEMSLOT_INVALID;
|
slot->flags |= KVM_MEMSLOT_INVALID;
|
||||||
|
|
||||||
old_memslots = install_new_memslots(kvm, slots);
|
old_memslots = install_new_memslots(kvm, as_id, slots);
|
||||||
|
|
||||||
/* slot was deleted or moved, clear iommu mapping */
|
/* slot was deleted or moved, clear iommu mapping */
|
||||||
kvm_iommu_unmap_pages(kvm, &old);
|
kvm_iommu_unmap_pages(kvm, &old);
|
||||||
@ -909,7 +917,7 @@ int __kvm_set_memory_region(struct kvm *kvm,
|
|||||||
}
|
}
|
||||||
|
|
||||||
update_memslots(slots, &new);
|
update_memslots(slots, &new);
|
||||||
old_memslots = install_new_memslots(kvm, slots);
|
old_memslots = install_new_memslots(kvm, as_id, slots);
|
||||||
|
|
||||||
kvm_arch_commit_memory_region(kvm, mem, &old, &new, change);
|
kvm_arch_commit_memory_region(kvm, mem, &old, &new, change);
|
||||||
|
|
||||||
@ -956,7 +964,7 @@ EXPORT_SYMBOL_GPL(kvm_set_memory_region);
|
|||||||
static int kvm_vm_ioctl_set_memory_region(struct kvm *kvm,
|
static int kvm_vm_ioctl_set_memory_region(struct kvm *kvm,
|
||||||
struct kvm_userspace_memory_region *mem)
|
struct kvm_userspace_memory_region *mem)
|
||||||
{
|
{
|
||||||
if (mem->slot >= KVM_USER_MEM_SLOTS)
|
if ((u16)mem->slot >= KVM_USER_MEM_SLOTS)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return kvm_set_memory_region(kvm, mem);
|
return kvm_set_memory_region(kvm, mem);
|
||||||
@ -967,16 +975,18 @@ int kvm_get_dirty_log(struct kvm *kvm,
|
|||||||
{
|
{
|
||||||
struct kvm_memslots *slots;
|
struct kvm_memslots *slots;
|
||||||
struct kvm_memory_slot *memslot;
|
struct kvm_memory_slot *memslot;
|
||||||
int r, i;
|
int r, i, as_id, id;
|
||||||
unsigned long n;
|
unsigned long n;
|
||||||
unsigned long any = 0;
|
unsigned long any = 0;
|
||||||
|
|
||||||
r = -EINVAL;
|
r = -EINVAL;
|
||||||
if (log->slot >= KVM_USER_MEM_SLOTS)
|
as_id = log->slot >> 16;
|
||||||
|
id = (u16)log->slot;
|
||||||
|
if (as_id >= KVM_ADDRESS_SPACE_NUM || id >= KVM_USER_MEM_SLOTS)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
slots = kvm_memslots(kvm);
|
slots = __kvm_memslots(kvm, as_id);
|
||||||
memslot = id_to_memslot(slots, log->slot);
|
memslot = id_to_memslot(slots, id);
|
||||||
r = -ENOENT;
|
r = -ENOENT;
|
||||||
if (!memslot->dirty_bitmap)
|
if (!memslot->dirty_bitmap)
|
||||||
goto out;
|
goto out;
|
||||||
@ -1027,17 +1037,19 @@ int kvm_get_dirty_log_protect(struct kvm *kvm,
|
|||||||
{
|
{
|
||||||
struct kvm_memslots *slots;
|
struct kvm_memslots *slots;
|
||||||
struct kvm_memory_slot *memslot;
|
struct kvm_memory_slot *memslot;
|
||||||
int r, i;
|
int r, i, as_id, id;
|
||||||
unsigned long n;
|
unsigned long n;
|
||||||
unsigned long *dirty_bitmap;
|
unsigned long *dirty_bitmap;
|
||||||
unsigned long *dirty_bitmap_buffer;
|
unsigned long *dirty_bitmap_buffer;
|
||||||
|
|
||||||
r = -EINVAL;
|
r = -EINVAL;
|
||||||
if (log->slot >= KVM_USER_MEM_SLOTS)
|
as_id = log->slot >> 16;
|
||||||
|
id = (u16)log->slot;
|
||||||
|
if (as_id >= KVM_ADDRESS_SPACE_NUM || id >= KVM_USER_MEM_SLOTS)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
slots = kvm_memslots(kvm);
|
slots = __kvm_memslots(kvm, as_id);
|
||||||
memslot = id_to_memslot(slots, log->slot);
|
memslot = id_to_memslot(slots, id);
|
||||||
|
|
||||||
dirty_bitmap = memslot->dirty_bitmap;
|
dirty_bitmap = memslot->dirty_bitmap;
|
||||||
r = -ENOENT;
|
r = -ENOENT;
|
||||||
@ -2619,6 +2631,10 @@ static long kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg)
|
|||||||
#ifdef CONFIG_HAVE_KVM_IRQ_ROUTING
|
#ifdef CONFIG_HAVE_KVM_IRQ_ROUTING
|
||||||
case KVM_CAP_IRQ_ROUTING:
|
case KVM_CAP_IRQ_ROUTING:
|
||||||
return KVM_MAX_IRQ_ROUTES;
|
return KVM_MAX_IRQ_ROUTES;
|
||||||
|
#endif
|
||||||
|
#if KVM_ADDRESS_SPACE_NUM > 1
|
||||||
|
case KVM_CAP_MULTI_ADDRESS_SPACE:
|
||||||
|
return KVM_ADDRESS_SPACE_NUM;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user