MSI-X mask bit support for KVM
v6 cover MSI-X mask bit support for KVM
This patchset didn't include two PCI related patches which would be checked in through PCI subsystem.
Would add the API document soon.
Change from v5:
Complete rewrote according to Avi's comments.
Sheng Yang (2):
KVM: Move struct kvm_io_device to kvm_host.h
KVM: Emulate MSI-X table and PBA in kernel
arch/x86/kvm/Makefile | 2 +- arch/x86/kvm/x86.c | 8 +- include/linux/kvm.h | 22 ++++ include/linux/kvm_host.h | 48 +++++++++ virt/kvm/assigned-dev.c | 30 ++++++ virt/kvm/iodev.h | 25 +----- virt/kvm/kvm_main.c | 38 +++++++- virt/kvm/msix_mmio.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++ virt/kvm/msix_mmio.h | 24 +++++ 9 files changed, 410 insertions(+), 31 deletions(-) create mode 100644 virt/kvm/msix_mmio.c create mode 100644 virt/kvm/msix_mmio.h
PATCH v6 1/2 KVM: Move struct kvm_io_device to kvm_host.h
Then it can be used by other struct in kvm_host.h
--- include/linux/kvm_host.h | 23 +++++++++++++++++++++++ virt/kvm/iodev.h | 25 +------------------------ 2 files changed, 24 insertions(+), 24 deletions(-)
include/linux/kvm_host.h +23 -0
@@ -98,6 +98,29 @@ int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, gfn_t gfn,int kvm_async_pf_wakeup_all (struct kvm_vcpu *vcpu);#endif +struct kvm_io_device ; + +/** +* kvm_io_device_ops are called under kvm slots_lock. +* read and write handlers return 0 if the transaction has been handled, +* or non-zero to have it passed to the next device. +**/ +struct kvm_io_device_ops { +int (*read)(struct kvm_io_device *this, +gpa_t addr, +int len, +void *val); +int (*write)(struct kvm_io_device *this, +gpa_t addr, +int len, +const void *val); +void (*destructor)(struct kvm_io_device *this); +}; + +struct kvm_io_device { +const struct kvm_io_device_ops *ops; +}; +struct kvm_vcpu {struct kvm *kvm;#ifdef CONFIG_PREEMPT_NOTIFIERS
virt/kvm/iodev.h +1 -24
@@ -17,32 +17,9 @@#define __KVM_IODEV_H__#include <linux/kvm_types.h> +#include <linux/kvm_host.h> #include <asm/errno.h> -struct kvm_io_device ; - -/** -* kvm_io_device_ops are called under kvm slots_lock. -* read and write handlers return 0 if the transaction has been handled, -* or non-zero to have it passed to the next device. -**/ -struct kvm_io_device_ops { -int (*read)(struct kvm_io_device *this, -gpa_t addr, -int len, -void *val); -int (*write)(struct kvm_io_device *this, -gpa_t addr, -int len, -const void *val); -void (*destructor)(struct kvm_io_device *this); -}; - - -struct kvm_io_device { -const struct kvm_io_device_ops *ops; -}; -static inline void kvm_iodevice_init (struct kvm_io_device *dev,const struct kvm_io_device_ops *ops) {
Signature · 1 line
1.7.0.1
RFC PATCH v6 2/2 KVM: Emulate MSI-X table and PBA in kernel
In reply to Sheng YangThen we can support mask bit operation of assigned devices now.
--- arch/x86/kvm/Makefile | 2 +- arch/x86/kvm/x86.c | 8 +- include/linux/kvm.h | 22 ++++ include/linux/kvm_host.h | 25 +++++ virt/kvm/assigned-dev.c | 30 ++++++ virt/kvm/kvm_main.c | 38 +++++++- virt/kvm/msix_mmio.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++ virt/kvm/msix_mmio.h | 24 +++++ 8 files changed, 386 insertions(+), 7 deletions(-) create mode 100644 virt/kvm/msix_mmio.c create mode 100644 virt/kvm/msix_mmio.h
arch/x86/kvm/Makefile +1 -1
@@ -7,7 +7,7 @@ CFLAGS_vmx.o := -I. kvm-y += $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o \ coalesced_mmio.o irq_comm.o eventfd.o \ - assigned-dev.o) + assigned-dev.o msix_mmio.o) kvm-$(CONFIG_IOMMU_API) += $(addprefix ../../../virt/kvm/, iommu.o) kvm-$(CONFIG_KVM_ASYNC_PF) += $(addprefix ../../../virt/kvm/, async_pf.o)
arch/x86/kvm/x86.c +6 -2
@@ -1965,6 +1965,7 @@ int kvm_dev_ioctl_check_extension(long ext)case KVM_CAP_X86_ROBUST_SINGLESTEP :case KVM_CAP_XSAVE :case KVM_CAP_ASYNC_PF : +case KVM_CAP_MSIX_MMIO : r =1 ;break ;case KVM_CAP_COALESCED_MMIO :
@@ -3802,6 +3803,7 @@ static int emulator_write_emulated_onepage(unsigned long addr, struct kvm_vcpu *vcpu) {gpa_t gpa; +int r; gpa =kvm_mmu_gva_to_gpa_write (vcpu, addr, exception);
@@ -3817,14 +3819,16 @@ static int emulator_write_emulated_onepage(unsigned long addr, mmio:trace_kvm_mmio (KVM_TRACE_MMIO_WRITE , bytes, gpa, *(u64 *)val); + r =vcpu_mmio_write (vcpu, gpa, bytes, val);/* * Is this MMIO handled locally? */ -if (!vcpu_mmio_write (vcpu, gpa, bytes, val)) +if (!r)return X86EMUL_CONTINUE ; vcpu->mmio_needed =1 ; - vcpu->run->exit_reason =KVM_EXIT_MMIO ; + vcpu->run->exit_reason = (r == -ENOTSYNC ) ? +KVM_EXIT_MSIX_ROUTING_UPDATE :KVM_EXIT_MMIO ; vcpu->run->mmio.phys_addr = vcpu->mmio_phys_addr = gpa; vcpu->run->mmio.len = vcpu->mmio_size = bytes; vcpu->run->mmio.is_write = vcpu->mmio_is_write =1 ;
include/linux/kvm.h +22 -0
@@ -161,6 +161,7 @@ struct kvm_pit_config {#define KVM_EXIT_NMI 16#define KVM_EXIT_INTERNAL_ERROR 17#define KVM_EXIT_OSI 18 +#define KVM_EXIT_MSIX_ROUTING_UPDATE 19/* For KVM_EXIT_INTERNAL_ERROR */ #define KVM_INTERNAL_ERROR_EMULATION 1
@@ -541,6 +542,7 @@ struct kvm_ppc_pvinfo {#define KVM_CAP_PPC_GET_PVINFO 57#define KVM_CAP_PPC_IRQ_LEVEL 58#define KVM_CAP_ASYNC_PF 59 +#define KVM_CAP_MSIX_MMIO 60#ifdef KVM_CAP_IRQ_ROUTING
@@ -672,6 +674,9 @@ struct kvm_clock_data {#define KVM_XEN_HVM_CONFIG _IOW(KVMIO, 0x7a, struct kvm_xen_hvm_config)#define KVM_SET_CLOCK _IOW(KVMIO, 0x7b, struct kvm_clock_data)#define KVM_GET_CLOCK _IOR(KVMIO, 0x7c, struct kvm_clock_data) +/* Available with KVM_CAP_MSIX_MMIO */ +#define KVM_REGISTER_MSIX_MMIO _IOW(KVMIO, 0x7d, struct kvm_msix_mmio_user) +#define KVM_UNREGISTER_MSIX_MMIO _IOW(KVMIO, 0x7e, struct kvm_msix_mmio_user)/* Available with KVM_CAP_PIT_STATE2 */ #define KVM_GET_PIT2 _IOR(KVMIO, 0x9f, struct kvm_pit_state2)#define KVM_SET_PIT2 _IOW(KVMIO, 0xa0, struct kvm_pit_state2)
@@ -795,4 +800,21 @@ struct kvm_assigned_msix_entry { __u16 padding[3]; }; +#define KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV (1 << 0) + +#define KVM_MSIX_MMIO_TYPE_BASE_TABLE (1 << 8) +#define KVM_MSIX_MMIO_TYPE_BASE_PBA (1 << 9) + +#define KVM_MSIX_MMIO_TYPE_DEV_MASK 0x00ff +#define KVM_MSIX_MMIO_TYPE_BASE_MASK 0xff00 +struct kvm_msix_mmio_user { +__u32 dev_id; +__u16 type; +__u16 max_entries_nr; +__u64 base_addr; +__u64 base_va; +__u64 flags; +__u64 reserved[4 ]; +}; +#endif /* __LINUX_KVM_H */
include/linux/kvm_host.h +25 -0
@@ -231,6 +231,27 @@ struct kvm_memslots { KVM_PRIVATE_MEM_SLOTS]; }; +#define KVM_MSIX_MMIO_MAX 32 + +struct kvm_msix_mmio { +u32 dev_id; +u16 type; +u16 max_entries_nr; +u64 flags; +gpa_t table_base_addr; +hva_t table_base_va; +gpa_t pba_base_addr; +hva_t pba_base_va; +}; + +struct kvm_msix_mmio_dev { +struct kvm *kvm; +struct kvm_io_device table_dev; +int mmio_nr; +struct kvm_msix_mmio mmio[KVM_MSIX_MMIO_MAX ]; +struct mutex lock; +}; +struct kvm {spinlock_t mmu_lock;raw_spinlock_t requests_lock;
@@ -279,6 +300,7 @@ struct kvm {long mmu_notifier_count;long tlbs_dirty;#endif +struct kvm_msix_mmio_dev msix_mmio_dev; };/* The guest did something we don't support. */
@@ -551,6 +573,9 @@ void kvm_unregister_irq_ack_notifier(struct kvm *kvm,int kvm_request_irq_source_id (struct kvm *kvm);void kvm_free_irq_source_id (struct kvm *kvm,int irq_source_id); +int kvm_assigned_device_update_msix_mask_bit (struct kvm *kvm, +int assigned_dev_id,int entry,u32 flag); +/* For vcpu->arch.iommu_flags */ #define KVM_IOMMU_CACHE_COHERENCY 0x1
virt/kvm/assigned-dev.c +30 -0
@@ -785,3 +785,33 @@ out:return r; } +int kvm_assigned_device_update_msix_mask_bit (struct kvm *kvm, +int assigned_dev_id,int entry,u32 flag) +{ +int r = -EFAULT ; +struct kvm_assigned_dev_kernel *adev; +int i; + +if (!irqchip_in_kernel (kvm)) +return r; + +mutex_lock (&kvm->lock); + adev =kvm_find_assigned_dev (&kvm->arch.assigned_dev_head, + assigned_dev_id); +if (!adev) + goto out; + +for (i =0 ; i < adev->entries_nr; i++) +if (adev->host_msix_entries[i].entry == entry) { +if (flag) +disable_irq_nosync ( + adev->host_msix_entries[i].vector); +else +enable_irq (adev->host_msix_entries[i].vector); + r =0 ; +break ; + } +out: +mutex_unlock (&kvm->lock); +return r; +}
virt/kvm/kvm_main.c +34 -4
@@ -56,6 +56,7 @@#include "coalesced_mmio.h" #include "async_pf.h" +#include "msix_mmio.h" #define CREATE_TRACE_POINTS #include <trace/events/kvm.h>
@@ -521,6 +522,7 @@ static void kvm_destroy_vm(struct kvm *kvm)#else kvm_arch_flush_shadow (kvm);#endif +kvm_unregister_msix_mmio_dev (kvm);kvm_arch_destroy_vm (kvm);kvm_free_physmem (kvm);cleanup_srcu_struct (&kvm->srcu);
@@ -1877,6 +1879,24 @@ static long kvm_vm_ioctl(struct file *filp,mutex_unlock (&kvm->lock);break ;#endif +case KVM_REGISTER_MSIX_MMIO : { +struct kvm_msix_mmio_user mmio_user; + + r = -EFAULT ; +if (copy_from_user (&mmio_user, argp,sizeof mmio_user)) + goto out; + r =kvm_vm_ioctl_register_msix_mmio (kvm, &mmio_user); +break ; + } +case KVM_UNREGISTER_MSIX_MMIO : { +struct kvm_msix_mmio_user mmio_user; + + r = -EFAULT ; +if (copy_from_user (&mmio_user, argp,sizeof mmio_user)) + goto out; + r =kvm_vm_ioctl_unregister_msix_mmio (kvm, &mmio_user); +break ; + }default : r =kvm_arch_vm_ioctl (filp, ioctl, arg);if (r == -ENOTTY )
@@ -1988,6 +2008,12 @@ static int kvm_dev_ioctl_create_vm(void) return r; }#endif + r =kvm_register_msix_mmio_dev (kvm); +if (r <0 ) { +kvm_put_kvm (kvm); +return r; + } + r =anon_inode_getfd ("kvm-vm" , &kvm_vm_fops, kvm,O_RDWR );if (r <0 )kvm_put_kvm (kvm);
@@ -2223,14 +2249,18 @@ static void kvm_io_bus_destroy(struct kvm_io_bus *bus)int kvm_io_bus_write (struct kvm *kvm,enum kvm_bus bus_idx,gpa_t addr,int len,const void *val) { -int i; +int i, r = -EOPNOTSUPP ;struct kvm_io_bus *bus; bus =srcu_dereference (kvm->buses[bus_idx], &kvm->srcu); -for (i =0 ; i < bus->dev_count; i++) -if (!kvm_iodevice_write (bus->devs[i], addr, len, val)) +for (i =0 ; i < bus->dev_count; i++) { + r =kvm_iodevice_write (bus->devs[i], addr, len, val); +if (r == -ENOTSYNC ) +break ; +else if (!r)return 0 ; -return -EOPNOTSUPP ; + } +return r; }/* kvm_io_bus_read - called under kvm->slots_lock */
virt/kvm/msix_mmio.c new file +244 -0
244 lines not on this page · raw message
virt/kvm/msix_mmio.h new file +24 -0
@@ -0,0 +1,24 @@ +#ifndef __KVM_MSIX_MMIO_H__ +#define __KVM_MSIX_MMIO_H__ +/* +* MSI-X MMIO emulation +* +* Copyright (c) 2010 Intel Corporation +* +* This work is licensed under the terms of the GNU GPL, version 2. See +* the COPYING file in the top-level directory. +* +* Author: +* Sheng Yang <[email protected]> +*/ + +#include <linux/pci.h> + +int kvm_register_msix_mmio_dev (struct kvm *kvm); +int kvm_unregister_msix_mmio_dev (struct kvm *kvm); +int kvm_vm_ioctl_register_msix_mmio (struct kvm *kvm, +struct kvm_msix_mmio_user *mmio); +int kvm_vm_ioctl_unregister_msix_mmio (struct kvm *kvm, +struct kvm_msix_mmio_user *mmio); + +#endif
Signature · 1 line
1.7.0.1
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 9 lines · Sheng Yang
Then we can support mask bit operation of assigned devices now.
arch/x86/kvm/x86.c +1
-0@@ -3817,14 +3819,16 @@ static int emulator_write_emulated_onepage(unsigned long addr, mmio:trace_kvm_mmio (KVM_TRACE_MMIO_WRITE , bytes, gpa, *(u64 *)val); + r =vcpu_mmio_write (vcpu, gpa, bytes, val);/*
arch/x86/kvm/x86.c static int emulator_write_emulated_onepage(unsigned long addr, · line 3831
Quoted 10 lines
arch/x86/kvm/x86.c +3
-2* Is this MMIO handled locally? */ -if (!vcpu_mmio_write (vcpu, gpa, bytes, val)) +if (!r)return X86EMUL_CONTINUE ; vcpu->mmio_needed =1 ; - vcpu->run->exit_reason =KVM_EXIT_MMIO ; + vcpu->run->exit_reason = (r == -ENOTSYNC ) ? +KVM_EXIT_MSIX_ROUTING_UPDATE :KVM_EXIT_MMIO ;
This isn't very pretty, exit_reason should be written in vcpu_mmio_write(). I guess we can refactor it later.
include/linux/kvm.h struct kvm_assigned_msix_entry { · line 809
Quoted 8 lines
include/linux/kvm.h +7
-0+#define KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV (1<< 0) + +#define KVM_MSIX_MMIO_TYPE_BASE_TABLE (1<< 8) +#define KVM_MSIX_MMIO_TYPE_BASE_PBA (1<< 9) + +#define KVM_MSIX_MMIO_TYPE_DEV_MASK 0x00ff +#define KVM_MSIX_MMIO_TYPE_BASE_MASK 0xff00
Any explanation of these?
Quoted 5 lines
include/linux/kvm.h +5
-0+struct kvm_msix_mmio_user { +__u32 dev_id; +__u16 type; +__u16 max_entries_nr; +__u64 base_addr;
include/linux/kvm.h struct kvm_assigned_msix_entry {
Quoted 10 lines
include/linux/kvm.h +8
-0+__u64 base_va; +__u64 flags; +__u64 reserved[4 ]; +}; + +int kvm_assigned_device_update_msix_mask_bit (struct kvm *kvm, +int assigned_dev_id,int entry,u32 flag) +{
Need a better name for 'flag' (and make it a bool).
Quoted 29 lines
virt/kvm/assigned-dev.c +27
-0+int r = -EFAULT ; +struct kvm_assigned_dev_kernel *adev; +int i; + +if (!irqchip_in_kernel (kvm)) +return r; + +mutex_lock (&kvm->lock); + adev =kvm_find_assigned_dev (&kvm->arch.assigned_dev_head, + assigned_dev_id); +if (!adev) + goto out; + +for (i =0 ; i< adev->entries_nr; i++) +if (adev->host_msix_entries[i].entry == entry) { +if (flag) +disable_irq_nosync ( + adev->host_msix_entries[i].vector); +else +enable_irq (adev->host_msix_entries[i].vector); + r =0 ; +break ; + } +out: +mutex_unlock (&kvm->lock); +return r; +}
virt/kvm/assigned-dev.c static int kvm_dev_ioctl_create_vm(void) · line 2015
Quoted 8 lines
virt/kvm/assigned-dev.c +5
-0@@ -1988,6 +2008,12 @@ static int kvm_dev_ioctl_create_vm(void) return r; }#endif + r =kvm_register_msix_mmio_dev (kvm); +if (r<0 ) { +kvm_put_kvm (kvm); +return r; + }
Shouldn't this be part of individual KVM_REGISTER_MSIX_MMIO calls?
Quoted 6 lines
virt/kvm/msix_mmio.c new file +6
-0+static int msix_table_mmio_read (struct kvm_io_device *this,gpa_t addr,int len, +void *val) +{ +struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); +struct kvm_msix_mmio *mmio;
virt/kvm/msix_mmio.c line 63
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+int idx, ret =0 , entry, offset, r; + +mutex_lock (&mmio_dev->lock); + idx =get_mmio_table_index (mmio_dev, addr, len); +if (idx<0 ) { + ret = -EOPNOTSUPP ; + goto out; + } +if ((addr&0x3 ) || (len !=4 && len !=8 )) + goto out;
What about (addr & 4) && (len == 8)? Is it supported? It may cross entry boundaries.
Quoted 32 lines
virt/kvm/msix_mmio.c new file +30
-0+ mmio =&mmio_dev->mmio[idx]; + + entry = (addr - mmio->table_base_addr) /PCI_MSIX_ENTRY_SIZE ; + offset = addr&0xf ; + r =copy_from_user (val, (void *)(mmio->table_base_va + + entry *PCI_MSIX_ENTRY_SIZE + offset), len); +if (r) + goto out; +out: +mutex_unlock (&mmio_dev->lock); +return ret; +} + +static int msix_table_mmio_write (struct kvm_io_device *this,gpa_t addr, +int len,const void *val) +{ +struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); +struct kvm_msix_mmio *mmio; +int idx, entry, offset, ret =0 , r =0 ; +gpa_t entry_base; +u32 old_ctrl, new_ctrl; + +mutex_lock (&mmio_dev->lock); + idx =get_mmio_table_index (mmio_dev, addr, len); +if (idx<0 ) { + ret = -EOPNOTSUPP ; + goto out; + } +if ((addr&0x3 ) || (len !=4 && len !=8 ))
virt/kvm/msix_mmio.c line 103
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ goto out; + mmio =&mmio_dev->mmio[idx]; + entry = (addr - mmio->table_base_addr) /PCI_MSIX_ENTRY_SIZE ; + entry_base = mmio->table_base_va + entry *PCI_MSIX_ENTRY_SIZE ; + offset = addr&0xF ; + +if (copy_from_user (&old_ctrl, + entry_base +PCI_MSIX_ENTRY_VECTOR_CTRL , +sizeof old_ctrl)) + goto out;
get_user() is easier.
Quoted 4 lines
virt/kvm/msix_mmio.c new file +4
-0+ +/* No allow writing to other fields when entry is unmasked */ +if (!(old_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT )&& + offset !=PCI_MSIX_ENTRY_VECTOR_CTRL )
virt/kvm/msix_mmio.c line 116
Quoted 10 lines
virt/kvm/msix_mmio.c new file +9
-0+ goto out; + +if (copy_to_user (entry_base + offset, val, len)) + goto out; + +if (copy_from_user (&new_ctrl, + entry_base +PCI_MSIX_ENTRY_VECTOR_CTRL , +sizeof new_ctrl)) + goto out;
put_user()
Quoted 8 lines
virt/kvm/msix_mmio.c new file +8
-0+ +if ((offset<PCI_MSIX_ENTRY_VECTOR_CTRL && len ==4 ) || + (offset<PCI_MSIX_ENTRY_DATA && len ==8 )) + ret = -ENOTSYNC ; +if (old_ctrl == new_ctrl) + goto out; +if (!(old_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT )&& + (new_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT ))
virt/kvm/msix_mmio.c line 134
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ r =update_msix_mask_bit (mmio_dev->kvm, mmio, entry,1 ); + else if ((old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + !(new_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)) + r =update_msix_mask_bit (mmio_dev->kvm, mmio, entry,0 ); +if (r || ret) + ret = -ENOTSYNC ; +out: +mutex_unlock (&mmio_dev->lock); +return ret; +}
blank line...
Quoted 25 lines
virt/kvm/msix_mmio.c new file +25
-0+static const struct kvm_io_device_ops msix_mmio_table_ops = { + .read = msix_table_mmio_read, + .write = msix_table_mmio_write, +}; + ++ +int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm, + struct kvm_msix_mmio_user *mmio_user) +{ + struct kvm_msix_mmio_dev *mmio_dev =&kvm->msix_mmio_dev; +struct kvm_msix_mmio *mmio =NULL ; +int r =0 , i; + +mutex_lock (&mmio_dev->lock); +for (i =0 ; i< mmio_dev->mmio_nr; i++) { +if (mmio_dev->mmio[i].dev_id == mmio_user->dev_id&& + (mmio_dev->mmio[i].type&KVM_MSIX_MMIO_TYPE_DEV_MASK ) == + (mmio_user->type&KVM_MSIX_MMIO_TYPE_DEV_MASK )) { + mmio =&mmio_dev->mmio[i]; +if (mmio->max_entries_nr != mmio_user->max_entries_nr) { + r = -EINVAL ; + goto out; + } +break ; + }
virt/kvm/msix_mmio.c line 193
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ } +if (!mmio) { +if (mmio_dev->mmio_nr ==KVM_MSIX_MMIO_MAX ) { + r = -ENOSPC ; + goto out; + } + mmio =&mmio_dev->mmio[mmio_dev->mmio_nr]; + mmio_dev->mmio_nr++; + } + mmio->max_entries_nr = mmio_user->max_entries_nr;
Sanity check to avoid overflow.
virt/kvm/msix_mmio.c line 195
Quoted 2 lines
virt/kvm/msix_mmio.c new file +2
-0+ mmio->dev_id = mmio_user->dev_id; + mmio->flags = mmio_user->flags;
Check for unsupported bits (all of them at present?)
virt/kvm/msix_mmio.c line 204
Quoted 9 lines
virt/kvm/msix_mmio.c new file +9
-0+if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_DEV_MASK ) == +KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV ) + mmio->type =KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV ; + +if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_BASE_MASK ) == +KVM_MSIX_MMIO_TYPE_BASE_TABLE ) { + mmio->type |=KVM_MSIX_MMIO_TYPE_BASE_TABLE ; + mmio->table_base_addr = mmio_user->base_addr; + mmio->table_base_va = mmio_user->base_va;
Check for va in kernel space.
virt/kvm/msix_mmio.c line 222
Quoted 12 lines
virt/kvm/msix_mmio.c new file +12
-0+ }else if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_BASE_MASK ) == +KVM_MSIX_MMIO_TYPE_BASE_PBA ) { + mmio->type |=KVM_MSIX_MMIO_TYPE_BASE_PBA ; + mmio->pba_base_addr = mmio_user->base_addr; + mmio->pba_base_va = mmio_user->base_va; + } +out: +mutex_unlock (&mmio_dev->lock); +return r; +} + +
In all, looks reasonable. I'd like to see documentation for it, and review from the pci people. Alex, mst?
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 22 lines · On Tuesday 28 December 2010 20:26:13 Avi Kivity wrote:
Quoted 21 lines · Sheng Yang
Then we can support mask bit operation of assigned devices now.
(quoted diff) +2
-1@@ -3817,14 +3819,16 @@ static int emulator_write_emulated_onepage(unsigned long addr, mmio: trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, *(u64 *)val); + r = vcpu_mmio_write(vcpu, gpa, bytes, val); /* * Is this MMIO handled locally? */ - if (!vcpu_mmio_write(vcpu, gpa, bytes, val)) + if (!r) return X86EMUL_CONTINUE;
Quoted 8 lines
arch/x86/kvm/x86.c static int emulator_write_emulated_onepage(unsigned long addr, · line 3831
Quoted 5 lines
arch/x86/kvm/x86.c +2
-1vcpu->mmio_needed =1 ; - vcpu->run->exit_reason =KVM_EXIT_MMIO ; + vcpu->run->exit_reason = (r == -ENOTSYNC ) ? +KVM_EXIT_MSIX_ROUTING_UPDATE :KVM_EXIT_MMIO ;This isn't very pretty,
exit_reasonshould be written invcpu_mmio_write(). I guess we can refactor it later.
Sure.
Quoted 10 lines
include/linux/kvm.h struct kvm_assigned_msix_entry { · line 809
Quoted 8 lines
include/linux/kvm.h +7
-0+#define KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV (1<< 0) + +#define KVM_MSIX_MMIO_TYPE_BASE_TABLE (1<< 8) +#define KVM_MSIX_MMIO_TYPE_BASE_PBA (1<< 9) + +#define KVM_MSIX_MMIO_TYPE_DEV_MASK 0x00ff +#define KVM_MSIX_MMIO_TYPE_BASE_MASK 0xff00Any explanation of these?
I chose to use assigned device id instead of one specific table id, because every device should got at most one MSI MMIO(the same should applied to vfio device as well), and if we use specific table ID, we need way to associate with the device anyway, to perform mask/unmask or other operation. So I think it's better to use device ID here directly.
And for the table and pba address, it's due to the mapping in userspace may know the guest MSI-X table address and PBA address at different time(due to different BAR, refer to the code in assigned_dev_iomem_map() of qemu). So I purposed this API to allow each of them can be passed to kernel space individually.
Quoted 55 lines
Quoted 6 lines
include/linux/kvm.h +5
-0+struct kvm_msix_mmio_user { +__u32 dev_id; +__u16 type; +__u16 max_entries_nr; +__u64 base_addr;include/linux/kvm.h struct kvm_assigned_msix_entry {
Quoted 10 lines
include/linux/kvm.h +8
-0+__u64 base_va; +__u64 flags; +__u64 reserved[4 ]; +}; + +int kvm_assigned_device_update_msix_mask_bit (struct kvm *kvm, +int assigned_dev_id,int entry,u32 flag) +{Need a better name for 'flag' (and make it a bool).
Quoted 36 lines
virt/kvm/assigned-dev.c +27
-0+int r = -EFAULT ; +struct kvm_assigned_dev_kernel *adev; +int i; + +if (!irqchip_in_kernel (kvm)) +return r; + +mutex_lock (&kvm->lock); + adev =kvm_find_assigned_dev (&kvm->arch.assigned_dev_head, + assigned_dev_id); +if (!adev) + goto out; + +for (i =0 ; i< adev->entries_nr; i++) +if (adev->host_msix_entries[i].entry == entry) { +if (flag) +disable_irq_nosync ( + adev->host_msix_entries[i].vector); +else +enable_irq (adev->host_msix_entries[i].vector); + r =0 ; +break ; + } +out: +mutex_unlock (&kvm->lock); +return r; +}@@ -1988,6 +2008,12 @@ static int kvm_dev_ioctl_create_vm(void) return r; }#endif
Quoted 7 lines
virt/kvm/kvm_main.c static int kvm_dev_ioctl_create_vm(void) · line 2015
Quoted 5 lines
virt/kvm/kvm_main.c +5
-0+ r =kvm_register_msix_mmio_dev (kvm); +if (r<0 ) { +kvm_put_kvm (kvm); +return r; + }Shouldn't this be part of individual
KVM_REGISTER_MSIX_MMIOcalls?
In fact this MMIO device is more like global one for the VM, not for every devices. It should handle all MMIO from all MSI-X enabled devices, so I put it in the VM init/destroy process.
Quoted 11 lines
Quoted 11 lines
(quoted diff) +1
-0+static int msix_table_mmio_read(struct kvm_io_device *this, gpa_t addr,int len, + void *val)
virt/kvm/msix_mmio.c new file +9
-0+{ +struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); +struct kvm_msix_mmio *mmio; +int idx, ret =0 , entry, offset, r; + +mutex_lock (&mmio_dev->lock); + idx =get_mmio_table_index (mmio_dev, addr, len); +if (idx<0 ) {
Quoted 8 lines
virt/kvm/msix_mmio.c line 63
Quoted 5 lines
virt/kvm/msix_mmio.c new file +5
-0+ ret = -EOPNOTSUPP ; + goto out; + } +if ((addr&0x3 ) || (len !=4 && len !=8 )) + goto out;What about (addr & 4) && (len == 8)? Is it supported? It may cross entry boundaries.
Should not supported. But I haven't found words on the PCI spec for it. So I didn't add this check.
Quoted 139 lines
Quoted 33 lines
virt/kvm/msix_mmio.c new file +30
-0+ mmio =&mmio_dev->mmio[idx]; + + entry = (addr - mmio->table_base_addr) /PCI_MSIX_ENTRY_SIZE ; + offset = addr&0xf ; + r =copy_from_user (val, (void *)(mmio->table_base_va + + entry *PCI_MSIX_ENTRY_SIZE + offset), len); +if (r) + goto out; +out: +mutex_unlock (&mmio_dev->lock); +return ret; +} + +static int msix_table_mmio_write (struct kvm_io_device *this,gpa_t addr, +int len,const void *val) +{ +struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); +struct kvm_msix_mmio *mmio; +int idx, entry, offset, ret =0 , r =0 ; +gpa_t entry_base; +u32 old_ctrl, new_ctrl; + +mutex_lock (&mmio_dev->lock); + idx =get_mmio_table_index (mmio_dev, addr, len); +if (idx<0 ) { + ret = -EOPNOTSUPP ; + goto out; + } +if ((addr&0x3 ) || (len !=4 && len !=8 ))virt/kvm/msix_mmio.c line 103
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ goto out; + mmio =&mmio_dev->mmio[idx]; + entry = (addr - mmio->table_base_addr) /PCI_MSIX_ENTRY_SIZE ; + entry_base = mmio->table_base_va + entry *PCI_MSIX_ENTRY_SIZE ; + offset = addr&0xF ; + +if (copy_from_user (&old_ctrl, + entry_base +PCI_MSIX_ENTRY_VECTOR_CTRL , +sizeof old_ctrl)) + goto out;
get_user()is easier.Quoted 4 lines
virt/kvm/msix_mmio.c new file +4
-0+ +/* No allow writing to other fields when entry is unmasked */ +if (!(old_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT )&& + offset !=PCI_MSIX_ENTRY_VECTOR_CTRL )virt/kvm/msix_mmio.c line 116
Quoted 10 lines
virt/kvm/msix_mmio.c new file +9
-0+ goto out; + +if (copy_to_user (entry_base + offset, val, len)) + goto out; + +if (copy_from_user (&new_ctrl, + entry_base +PCI_MSIX_ENTRY_VECTOR_CTRL , +sizeof new_ctrl)) + goto out;
put_user()Quoted 8 lines
virt/kvm/msix_mmio.c new file +8
-0+ +if ((offset<PCI_MSIX_ENTRY_VECTOR_CTRL && len ==4 ) || + (offset<PCI_MSIX_ENTRY_DATA && len ==8 )) + ret = -ENOTSYNC ; +if (old_ctrl == new_ctrl) + goto out; +if (!(old_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT )&& + (new_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT ))virt/kvm/msix_mmio.c line 134
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ r =update_msix_mask_bit (mmio_dev->kvm, mmio, entry,1 ); + else if ((old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + !(new_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)) + r =update_msix_mask_bit (mmio_dev->kvm, mmio, entry,0 ); +if (r || ret) + ret = -ENOTSYNC ; +out: +mutex_unlock (&mmio_dev->lock); +return ret; +}blank line...
Quoted 25 lines
virt/kvm/msix_mmio.c new file +25
-0+static const struct kvm_io_device_ops msix_mmio_table_ops = { + .read = msix_table_mmio_read, + .write = msix_table_mmio_write, +}; + ++ +int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm, + struct kvm_msix_mmio_user *mmio_user) +{ + struct kvm_msix_mmio_dev *mmio_dev =&kvm->msix_mmio_dev; +struct kvm_msix_mmio *mmio =NULL ; +int r =0 , i; + +mutex_lock (&mmio_dev->lock); +for (i =0 ; i< mmio_dev->mmio_nr; i++) { +if (mmio_dev->mmio[i].dev_id == mmio_user->dev_id&& + (mmio_dev->mmio[i].type&KVM_MSIX_MMIO_TYPE_DEV_MASK ) == + (mmio_user->type&KVM_MSIX_MMIO_TYPE_DEV_MASK )) { + mmio =&mmio_dev->mmio[i]; +if (mmio->max_entries_nr != mmio_user->max_entries_nr) { + r = -EINVAL ; + goto out; + } +break ; + }virt/kvm/msix_mmio.c line 193
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ } +if (!mmio) { +if (mmio_dev->mmio_nr ==KVM_MSIX_MMIO_MAX ) { + r = -ENOSPC ; + goto out; + } + mmio =&mmio_dev->mmio[mmio_dev->mmio_nr]; + mmio_dev->mmio_nr++; + } + mmio->max_entries_nr = mmio_user->max_entries_nr;Sanity check to avoid overflow.
virt/kvm/msix_mmio.c line 195
Quoted 2 lines
virt/kvm/msix_mmio.c new file +2
-0+ mmio->dev_id = mmio_user->dev_id; + mmio->flags = mmio_user->flags;Check for unsupported bits (all of them at present?)
virt/kvm/msix_mmio.c line 204
Quoted 9 lines
virt/kvm/msix_mmio.c new file +9
-0+if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_DEV_MASK ) == +KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV ) + mmio->type =KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV ; + +if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_BASE_MASK ) == +KVM_MSIX_MMIO_TYPE_BASE_TABLE ) { + mmio->type |=KVM_MSIX_MMIO_TYPE_BASE_TABLE ; + mmio->table_base_addr = mmio_user->base_addr; + mmio->table_base_va = mmio_user->base_va;Check for va in kernel space.
Quoted 15 lines
virt/kvm/msix_mmio.c line 222
Quoted 12 lines
virt/kvm/msix_mmio.c new file +12
-0+ }else if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_BASE_MASK ) == +KVM_MSIX_MMIO_TYPE_BASE_PBA ) { + mmio->type |=KVM_MSIX_MMIO_TYPE_BASE_PBA ; + mmio->pba_base_addr = mmio_user->base_addr; + mmio->pba_base_va = mmio_user->base_va; + } +out: +mutex_unlock (&mmio_dev->lock); +return r; +} + +In all, looks reasonable. I'd like to see documentation for it, and review from the pci people. Alex, mst?
Would add the API document soon.
--
regards
Yang, Sheng
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 44 lines · Sheng Yang
Quoted 22 lines · On Tuesday 28 December 2010 20:26:13 Avi Kivity wrote:
Quoted 21 lines · Sheng Yang
Then we can support mask bit operation of assigned devices now.
(quoted diff) +2
-1@@ -3817,14 +3819,16 @@ static int emulator_write_emulated_onepage(unsigned long addr, mmio: trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, *(u64 *)val); + r = vcpu_mmio_write(vcpu, gpa, bytes, val); /* * Is this MMIO handled locally? */ - if (!vcpu_mmio_write(vcpu, gpa, bytes, val)) + if (!r) return X86EMUL_CONTINUE;Quoted 8 lines
arch/x86/kvm/x86.c static int emulator_write_emulated_onepage(unsigned long addr, · line 3831
Quoted 5 lines
arch/x86/kvm/x86.c +2
-1vcpu->mmio_needed =1 ; - vcpu->run->exit_reason =KVM_EXIT_MMIO ; + vcpu->run->exit_reason = (r == -ENOTSYNC ) ? +KVM_EXIT_MSIX_ROUTING_UPDATE :KVM_EXIT_MMIO ;This isn't very pretty,
exit_reasonshould be written invcpu_mmio_write(). I guess we can refactor it later.Sure.
Quoted 10 lines
include/linux/kvm.h struct kvm_assigned_msix_entry { · line 809
Quoted 8 lines
include/linux/kvm.h +7
-0+#define KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV (1<< 0) + +#define KVM_MSIX_MMIO_TYPE_BASE_TABLE (1<< 8) +#define KVM_MSIX_MMIO_TYPE_BASE_PBA (1<< 9) + +#define KVM_MSIX_MMIO_TYPE_DEV_MASK 0x00ff +#define KVM_MSIX_MMIO_TYPE_BASE_MASK 0xff00Any explanation of these?
Quoted 5 lines
I chose to use assigned device id instead of one specific table id, because every device should got at most one MSI MMIO(the same should applied to vfio device as well), and if we use specific table ID, we need way to associate with the device anyway, to perform mask/unmask or other operation. So I think it's better to use device ID here directly.
Table id will be needed to make things work for emulated devices.
My idea was this: we have the device id in kvm_assigned_msix_entry already. Just put table id and entry number in kvm_irq_routing_entry (create a new gsi type for this).
The result will also work for irqfd because these are mapped to gsi.
Quoted 91 lines
And for the table and pba address, it's due to the mapping in userspace may know the guest MSI-X table address and PBA address at different
time(due to different BAR, refer to the code in assigned_dev_iomem_map() of qemu). So I purposed this API to allow each of them can be passed to kernel space individually.Quoted 55 lines
Quoted 6 lines
include/linux/kvm.h +5
-0+struct kvm_msix_mmio_user { +__u32 dev_id; +__u16 type; +__u16 max_entries_nr; +__u64 base_addr;include/linux/kvm.h struct kvm_assigned_msix_entry {
Quoted 10 lines
include/linux/kvm.h +8
-0+__u64 base_va; +__u64 flags; +__u64 reserved[4 ]; +}; + +int kvm_assigned_device_update_msix_mask_bit (struct kvm *kvm, +int assigned_dev_id,int entry,u32 flag) +{Need a better name for 'flag' (and make it a bool).
Quoted 36 lines
virt/kvm/assigned-dev.c +27
-0+int r = -EFAULT ; +struct kvm_assigned_dev_kernel *adev; +int i; + +if (!irqchip_in_kernel (kvm)) +return r; + +mutex_lock (&kvm->lock); + adev =kvm_find_assigned_dev (&kvm->arch.assigned_dev_head, + assigned_dev_id); +if (!adev) + goto out; + +for (i =0 ; i< adev->entries_nr; i++) +if (adev->host_msix_entries[i].entry == entry) { +if (flag) +disable_irq_nosync ( + adev->host_msix_entries[i].vector); +else +enable_irq (adev->host_msix_entries[i].vector); + r =0 ; +break ; + } +out: +mutex_unlock (&kvm->lock); +return r; +}@@ -1988,6 +2008,12 @@ static int kvm_dev_ioctl_create_vm(void) return r; }#endif Quoted 7 lines
virt/kvm/kvm_main.c static int kvm_dev_ioctl_create_vm(void) · line 2015
Quoted 5 lines
virt/kvm/kvm_main.c +5
-0+ r =kvm_register_msix_mmio_dev (kvm); +if (r<0 ) { +kvm_put_kvm (kvm); +return r; + }Shouldn't this be part of individual
KVM_REGISTER_MSIX_MMIOcalls?In fact this MMIO device is more like global one for the VM, not for every devices. It should handle all MMIO from all MSI-X enabled devices, so I put it in the VM init/destroy process.
Quoted 11 lines
Quoted 11 lines
(quoted diff) +1
-0+static int msix_table_mmio_read(struct kvm_io_device *this, gpa_t addr,int len, + void *val)
virt/kvm/msix_mmio.c new file +9
-0+{ +struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); +struct kvm_msix_mmio *mmio; +int idx, ret =0 , entry, offset, r; + +mutex_lock (&mmio_dev->lock); + idx =get_mmio_table_index (mmio_dev, addr, len); +if (idx<0 ) {Quoted 8 lines
virt/kvm/msix_mmio.c line 63
Quoted 5 lines
virt/kvm/msix_mmio.c new file +5
-0+ ret = -EOPNOTSUPP ; + goto out; + } +if ((addr&0x3 ) || (len !=4 && len !=8 )) + goto out;What about (addr & 4) && (len == 8)? Is it supported? It may cross entry boundaries.
Quoted 2 lines
Should not supported. But I haven't found words on the PCI spec for it. So I didn't add this check.
IMPLEMENTATION NOTE
MSI-X Memory Space Structures in Read/Write Memory
....
For all accesses to MSI-X Table and MSI-X PBA fields, software must use aligned full
DWORD or aligned full QWORD transactions; otherwise, the result is undefined.
Quoted 139 lines
Quoted 139 lines
Quoted 33 lines
virt/kvm/msix_mmio.c new file +30
-0+ mmio =&mmio_dev->mmio[idx]; + + entry = (addr - mmio->table_base_addr) /PCI_MSIX_ENTRY_SIZE ; + offset = addr&0xf ; + r =copy_from_user (val, (void *)(mmio->table_base_va + + entry *PCI_MSIX_ENTRY_SIZE + offset), len); +if (r) + goto out; +out: +mutex_unlock (&mmio_dev->lock); +return ret; +} + +static int msix_table_mmio_write (struct kvm_io_device *this,gpa_t addr, +int len,const void *val) +{ +struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); +struct kvm_msix_mmio *mmio; +int idx, entry, offset, ret =0 , r =0 ; +gpa_t entry_base; +u32 old_ctrl, new_ctrl; + +mutex_lock (&mmio_dev->lock); + idx =get_mmio_table_index (mmio_dev, addr, len); +if (idx<0 ) { + ret = -EOPNOTSUPP ; + goto out; + } +if ((addr&0x3 ) || (len !=4 && len !=8 ))virt/kvm/msix_mmio.c line 103
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ goto out; + mmio =&mmio_dev->mmio[idx]; + entry = (addr - mmio->table_base_addr) /PCI_MSIX_ENTRY_SIZE ; + entry_base = mmio->table_base_va + entry *PCI_MSIX_ENTRY_SIZE ; + offset = addr&0xF ; + +if (copy_from_user (&old_ctrl, + entry_base +PCI_MSIX_ENTRY_VECTOR_CTRL , +sizeof old_ctrl)) + goto out;
get_user()is easier.Quoted 4 lines
virt/kvm/msix_mmio.c new file +4
-0+ +/* No allow writing to other fields when entry is unmasked */ +if (!(old_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT )&& + offset !=PCI_MSIX_ENTRY_VECTOR_CTRL )virt/kvm/msix_mmio.c line 116
Quoted 10 lines
virt/kvm/msix_mmio.c new file +9
-0+ goto out; + +if (copy_to_user (entry_base + offset, val, len)) + goto out; + +if (copy_from_user (&new_ctrl, + entry_base +PCI_MSIX_ENTRY_VECTOR_CTRL , +sizeof new_ctrl)) + goto out;
put_user()Quoted 8 lines
virt/kvm/msix_mmio.c new file +8
-0+ +if ((offset<PCI_MSIX_ENTRY_VECTOR_CTRL && len ==4 ) || + (offset<PCI_MSIX_ENTRY_DATA && len ==8 )) + ret = -ENOTSYNC ; +if (old_ctrl == new_ctrl) + goto out; +if (!(old_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT )&& + (new_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT ))virt/kvm/msix_mmio.c line 134
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ r =update_msix_mask_bit (mmio_dev->kvm, mmio, entry,1 ); + else if ((old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + !(new_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)) + r =update_msix_mask_bit (mmio_dev->kvm, mmio, entry,0 ); +if (r || ret) + ret = -ENOTSYNC ; +out: +mutex_unlock (&mmio_dev->lock); +return ret; +}blank line...
Quoted 25 lines
virt/kvm/msix_mmio.c new file +25
-0+static const struct kvm_io_device_ops msix_mmio_table_ops = { + .read = msix_table_mmio_read, + .write = msix_table_mmio_write, +}; + ++ +int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm, + struct kvm_msix_mmio_user *mmio_user) +{ + struct kvm_msix_mmio_dev *mmio_dev =&kvm->msix_mmio_dev; +struct kvm_msix_mmio *mmio =NULL ; +int r =0 , i; + +mutex_lock (&mmio_dev->lock); +for (i =0 ; i< mmio_dev->mmio_nr; i++) { +if (mmio_dev->mmio[i].dev_id == mmio_user->dev_id&& + (mmio_dev->mmio[i].type&KVM_MSIX_MMIO_TYPE_DEV_MASK ) == + (mmio_user->type&KVM_MSIX_MMIO_TYPE_DEV_MASK )) { + mmio =&mmio_dev->mmio[i]; +if (mmio->max_entries_nr != mmio_user->max_entries_nr) { + r = -EINVAL ; + goto out; + } +break ; + }virt/kvm/msix_mmio.c line 193
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ } +if (!mmio) { +if (mmio_dev->mmio_nr ==KVM_MSIX_MMIO_MAX ) { + r = -ENOSPC ; + goto out; + } + mmio =&mmio_dev->mmio[mmio_dev->mmio_nr]; + mmio_dev->mmio_nr++; + } + mmio->max_entries_nr = mmio_user->max_entries_nr;Sanity check to avoid overflow.
virt/kvm/msix_mmio.c line 195
Quoted 2 lines
virt/kvm/msix_mmio.c new file +2
-0+ mmio->dev_id = mmio_user->dev_id; + mmio->flags = mmio_user->flags;Check for unsupported bits (all of them at present?)
virt/kvm/msix_mmio.c line 204
Quoted 9 lines
virt/kvm/msix_mmio.c new file +9
-0+if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_DEV_MASK ) == +KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV ) + mmio->type =KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV ; + +if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_BASE_MASK ) == +KVM_MSIX_MMIO_TYPE_BASE_TABLE ) { + mmio->type |=KVM_MSIX_MMIO_TYPE_BASE_TABLE ; + mmio->table_base_addr = mmio_user->base_addr; + mmio->table_base_va = mmio_user->base_va;Check for va in kernel space.
Quoted 15 lines
Quoted 7 lines
Quoted 7 lines
virt/kvm/msix_mmio.c new file +7
-0+ }else if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_BASE_MASK ) == +KVM_MSIX_MMIO_TYPE_BASE_PBA ) { + mmio->type |=KVM_MSIX_MMIO_TYPE_BASE_PBA ; + mmio->pba_base_addr = mmio_user->base_addr; + mmio->pba_base_va = mmio_user->base_va; + } +out:Quoted 8 lines
virt/kvm/msix_mmio.c line 222
Quoted 5 lines
virt/kvm/msix_mmio.c new file +5
-0+mutex_unlock (&mmio_dev->lock); +return r; +} + +In all, looks reasonable. I'd like to see documentation for it, and review from the pci people. Alex, mst?
Some general comments:
PBA isn't supported in this version, which is OK, but let's not add a capability until it is, and let's not try to guess what
the interface will look like. I think keeping PBA in userspace will be hard because it needs to be modified from interrupt context. Removing the PBA stub will make the interface simpler.
Quoted 5 lines
Would add the API document soon.
--
regards
Yang, Sheng
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 45 lines · On Wednesday 29 December 2010 16:31:35 Michael S. Tsirkin wrote:
Quoted 44 lines · Sheng Yang
Quoted 22 lines · On Tuesday 28 December 2010 20:26:13 Avi Kivity wrote:
Quoted 21 lines · Sheng Yang
Then we can support mask bit operation of assigned devices now.
(quoted diff) +2
-1@@ -3817,14 +3819,16 @@ static int emulator_write_emulated_onepage(unsigned long addr, mmio: trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, *(u64 *)val); + r = vcpu_mmio_write(vcpu, gpa, bytes, val); /* * Is this MMIO handled locally? */ - if (!vcpu_mmio_write(vcpu, gpa, bytes, val)) + if (!r) return X86EMUL_CONTINUE;Quoted 8 lines
arch/x86/kvm/x86.c static int emulator_write_emulated_onepage(unsigned long addr, · line 3831
Quoted 5 lines
arch/x86/kvm/x86.c +2
-1vcpu->mmio_needed =1 ; - vcpu->run->exit_reason =KVM_EXIT_MMIO ; + vcpu->run->exit_reason = (r == -ENOTSYNC ) ? +KVM_EXIT_MSIX_ROUTING_UPDATE :KVM_EXIT_MMIO ;This isn't very pretty,
exit_reasonshould be written invcpu_mmio_write(). I guess we can refactor it later.Sure.
Quoted 9 lines
include/linux/kvm.h struct kvm_assigned_msix_entry { · line 809
Quoted 7 lines
include/linux/kvm.h +7
-0+#define KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV (1<< 0) + +#define KVM_MSIX_MMIO_TYPE_BASE_TABLE (1<< 8) +#define KVM_MSIX_MMIO_TYPE_BASE_PBA (1<< 9) + +#define KVM_MSIX_MMIO_TYPE_DEV_MASK 0x00ff +#define KVM_MSIX_MMIO_TYPE_BASE_MASK 0xff00Any explanation of these?
Quoted 7 lines
Quoted 5 lines
I chose to use assigned device id instead of one specific table id, because every device should got at most one MSI MMIO(the same should applied to vfio device as well), and if we use specific table ID, we need way to associate with the device anyway, to perform mask/unmask or other operation. So I think it's better to use device ID here directly.
Table id will be needed to make things work for emulated devices.
I suppose even emulated device should got some kind of id(BDF)? I think that is enough for identification, which is already there, so we don't need to allocate another ID for the device - because one device would got at most one MSI-X MMIO, then use BDF or other device specific ID should be quite straightforward.
Quoted 104 lines
My idea was this: we have the device id in
kvm_assigned_msix_entryalready. Just put table id and entry number inkvm_irq_routing_entry(create a new gsi type for this).
The result will also work for irqfd because these are mapped to gsi.Quoted 92 lines
And for the table and pba address, it's due to the mapping in userspace may know the guest MSI-X table address and PBA address at different
time(due to different BAR, refer to the code in assigned_dev_iomem_map() of qemu). So I purposed this API to allow each of them can be passed to kernel space individually.Quoted 54 lines
Quoted 5 lines
include/linux/kvm.h +5
-0+struct kvm_msix_mmio_user { +__u32 dev_id; +__u16 type; +__u16 max_entries_nr; +__u64 base_addr;include/linux/kvm.h struct kvm_assigned_msix_entry {
Quoted 10 lines
include/linux/kvm.h +8
-0+__u64 base_va; +__u64 flags; +__u64 reserved[4 ]; +}; + +int kvm_assigned_device_update_msix_mask_bit (struct kvm *kvm, +int assigned_dev_id,int entry,u32 flag) +{Need a better name for 'flag' (and make it a bool).
Quoted 36 lines
virt/kvm/assigned-dev.c +27
-0+int r = -EFAULT ; +struct kvm_assigned_dev_kernel *adev; +int i; + +if (!irqchip_in_kernel (kvm)) +return r; + +mutex_lock (&kvm->lock); + adev =kvm_find_assigned_dev (&kvm->arch.assigned_dev_head, + assigned_dev_id); +if (!adev) + goto out; + +for (i =0 ; i< adev->entries_nr; i++) +if (adev->host_msix_entries[i].entry == entry) { +if (flag) +disable_irq_nosync ( + adev->host_msix_entries[i].vector); +else +enable_irq (adev->host_msix_entries[i].vector); + r =0 ; +break ; + } +out: +mutex_unlock (&kvm->lock); +return r; +}@@ -1988,6 +2008,12 @@ static int kvm_dev_ioctl_create_vm(void) return r; }#endif Quoted 7 lines
virt/kvm/kvm_main.c static int kvm_dev_ioctl_create_vm(void) · line 2015
Quoted 5 lines
virt/kvm/kvm_main.c +5
-0+ r =kvm_register_msix_mmio_dev (kvm); +if (r<0 ) { +kvm_put_kvm (kvm); +return r; + }Shouldn't this be part of individual
KVM_REGISTER_MSIX_MMIOcalls?In fact this MMIO device is more like global one for the VM, not for every devices. It should handle all MMIO from all MSI-X enabled devices, so I put it in the VM init/destroy process.
Quoted 11 lines
Quoted 11 lines
(quoted diff) +1
-0+static int msix_table_mmio_read(struct kvm_io_device *this, gpa_taddr, int len, + void *val)
virt/kvm/msix_mmio.c new file +9
-0+{ +struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); +struct kvm_msix_mmio *mmio; +int idx, ret =0 , entry, offset, r; + +mutex_lock (&mmio_dev->lock); + idx =get_mmio_table_index (mmio_dev, addr, len); +if (idx<0 ) {Quoted 8 lines
virt/kvm/msix_mmio.c line 63
Quoted 5 lines
virt/kvm/msix_mmio.c new file +5
-0+ ret = -EOPNOTSUPP ; + goto out; + } +if ((addr&0x3 ) || (len !=4 && len !=8 )) + goto out;What about (addr & 4) && (len == 8)? Is it supported? It may cross entry boundaries.
Quoted 2 lines
Should not supported. But I haven't found words on the PCI spec for it. So I didn't add this check.
IMPLEMENTATION NOTE
MSI-X Memory Space Structures in Read/Write Memory
Quoted 6 lines
....
For all accesses to MSI-X Table and MSI-X PBA fields, software must use aligned full
DWORD or aligned full QWORD transactions; otherwise, the result is undefined.
Yes, this one is enough, I would add the checking.
Quoted 155 lines
Quoted 139 lines
Quoted 139 lines
Quoted 33 lines
virt/kvm/msix_mmio.c new file +14
-0+ mmio =&mmio_dev->mmio[idx]; + + entry = (addr - mmio->table_base_addr) /PCI_MSIX_ENTRY_SIZE ; + offset = addr&0xf ; + r =copy_from_user (val, (void *)(mmio->table_base_va + + entry *PCI_MSIX_ENTRY_SIZE + offset), len); +if (r) + goto out; +out: +mutex_unlock (&mmio_dev->lock); +return ret; +} + +static int msix_table_mmio_write(struct kvm_io_device *this, gpa_taddr, + int len, const void *val)
virt/kvm/msix_mmio.c new file +15
-0+{ +struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); +struct kvm_msix_mmio *mmio; +int idx, entry, offset, ret =0 , r =0 ; +gpa_t entry_base; +u32 old_ctrl, new_ctrl; + +mutex_lock (&mmio_dev->lock); + idx =get_mmio_table_index (mmio_dev, addr, len); +if (idx<0 ) { + ret = -EOPNOTSUPP ; + goto out; + } +if ((addr&0x3 ) || (len !=4 && len !=8 ))virt/kvm/msix_mmio.c line 103
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ goto out; + mmio =&mmio_dev->mmio[idx]; + entry = (addr - mmio->table_base_addr) /PCI_MSIX_ENTRY_SIZE ; + entry_base = mmio->table_base_va + entry *PCI_MSIX_ENTRY_SIZE ; + offset = addr&0xF ; + +if (copy_from_user (&old_ctrl, + entry_base +PCI_MSIX_ENTRY_VECTOR_CTRL , +sizeof old_ctrl)) + goto out;
get_user()is easier.Quoted 4 lines
virt/kvm/msix_mmio.c new file +4
-0+ +/* No allow writing to other fields when entry is unmasked */ +if (!(old_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT )&& + offset !=PCI_MSIX_ENTRY_VECTOR_CTRL )virt/kvm/msix_mmio.c line 116
Quoted 10 lines
virt/kvm/msix_mmio.c new file +9
-0+ goto out; + +if (copy_to_user (entry_base + offset, val, len)) + goto out; + +if (copy_from_user (&new_ctrl, + entry_base +PCI_MSIX_ENTRY_VECTOR_CTRL , +sizeof new_ctrl)) + goto out;
put_user()Quoted 8 lines
virt/kvm/msix_mmio.c new file +8
-0+ +if ((offset<PCI_MSIX_ENTRY_VECTOR_CTRL && len ==4 ) || + (offset<PCI_MSIX_ENTRY_DATA && len ==8 )) + ret = -ENOTSYNC ; +if (old_ctrl == new_ctrl) + goto out; +if (!(old_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT )&& + (new_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT ))virt/kvm/msix_mmio.c line 134
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ r =update_msix_mask_bit (mmio_dev->kvm, mmio, entry,1 ); + else if ((old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + !(new_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)) + r =update_msix_mask_bit (mmio_dev->kvm, mmio, entry,0 ); +if (r || ret) + ret = -ENOTSYNC ; +out: +mutex_unlock (&mmio_dev->lock); +return ret; +}blank line...
Quoted 25 lines
virt/kvm/msix_mmio.c new file +25
-0+static const struct kvm_io_device_ops msix_mmio_table_ops = { + .read = msix_table_mmio_read, + .write = msix_table_mmio_write, +}; + ++ +int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm, + struct kvm_msix_mmio_user *mmio_user) +{ + struct kvm_msix_mmio_dev *mmio_dev =&kvm->msix_mmio_dev; +struct kvm_msix_mmio *mmio =NULL ; +int r =0 , i; + +mutex_lock (&mmio_dev->lock); +for (i =0 ; i< mmio_dev->mmio_nr; i++) { +if (mmio_dev->mmio[i].dev_id == mmio_user->dev_id&& + (mmio_dev->mmio[i].type&KVM_MSIX_MMIO_TYPE_DEV_MASK ) == + (mmio_user->type&KVM_MSIX_MMIO_TYPE_DEV_MASK )) { + mmio =&mmio_dev->mmio[i]; +if (mmio->max_entries_nr != mmio_user->max_entries_nr) { + r = -EINVAL ; + goto out; + } +break ; + }virt/kvm/msix_mmio.c line 193
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ } +if (!mmio) { +if (mmio_dev->mmio_nr ==KVM_MSIX_MMIO_MAX ) { + r = -ENOSPC ; + goto out; + } + mmio =&mmio_dev->mmio[mmio_dev->mmio_nr]; + mmio_dev->mmio_nr++; + } + mmio->max_entries_nr = mmio_user->max_entries_nr;Sanity check to avoid overflow.
virt/kvm/msix_mmio.c line 195
Quoted 2 lines
virt/kvm/msix_mmio.c new file +2
-0+ mmio->dev_id = mmio_user->dev_id; + mmio->flags = mmio_user->flags;Check for unsupported bits (all of them at present?)
virt/kvm/msix_mmio.c line 204
Quoted 9 lines
virt/kvm/msix_mmio.c new file +9
-0+if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_DEV_MASK ) == +KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV ) + mmio->type =KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV ; + +if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_BASE_MASK ) == +KVM_MSIX_MMIO_TYPE_BASE_TABLE ) { + mmio->type |=KVM_MSIX_MMIO_TYPE_BASE_TABLE ; + mmio->table_base_addr = mmio_user->base_addr; + mmio->table_base_va = mmio_user->base_va;Check for va in kernel space.
Quoted 15 lines
Quoted 7 lines
Quoted 7 lines
virt/kvm/msix_mmio.c new file +7
-0+ }else if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_BASE_MASK ) == +KVM_MSIX_MMIO_TYPE_BASE_PBA ) { + mmio->type |=KVM_MSIX_MMIO_TYPE_BASE_PBA ; + mmio->pba_base_addr = mmio_user->base_addr; + mmio->pba_base_va = mmio_user->base_va; + } +out:Quoted 8 lines
virt/kvm/msix_mmio.c line 222
Quoted 5 lines
virt/kvm/msix_mmio.c new file +5
-0+mutex_unlock (&mmio_dev->lock); +return r; +} + +In all, looks reasonable. I'd like to see documentation for it, and review from the pci people. Alex, mst?
Quoted 6 lines
Some general comments:
PBA isn't supported in this version, which is OK, but let's not add a capability until it is, and let's not try to guess what
the interface will look like. I think keeping PBA in userspace will be hard because it needs to be modified from interrupt context. Removing the PBA stub will make the interface simpler.
The API only get the PBA address now which should be fine. And we still have threaded irq and tasklet for accessing the userspace for interrupt handler...
--
regards
Yang, Sheng
Quoted 5 lines
Quoted 5 lines
Would add the API document soon.
--
regards
Yang, Sheng
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 52 lines · Sheng Yang
Quoted 51 lines · On Wednesday 29 December 2010 16:31:35 Michael S. Tsirkin wrote:
Quoted 49 lines · Sheng Yang
Quoted 22 lines · On Tuesday 28 December 2010 20:26:13 Avi Kivity wrote:
Quoted 21 lines · Sheng Yang
Then we can support mask bit operation of assigned devices now.
(quoted diff) +2
-1@@ -3817,14 +3819,16 @@ static int emulator_write_emulated_onepage(unsigned long addr, mmio: trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, *(u64 *)val); + r = vcpu_mmio_write(vcpu, gpa, bytes, val); /* * Is this MMIO handled locally? */ - if (!vcpu_mmio_write(vcpu, gpa, bytes, val)) + if (!r) return X86EMUL_CONTINUE;Quoted 8 lines
arch/x86/kvm/x86.c static int emulator_write_emulated_onepage(unsigned long addr, · line 3831
Quoted 5 lines
arch/x86/kvm/x86.c +2
-1vcpu->mmio_needed =1 ; - vcpu->run->exit_reason =KVM_EXIT_MMIO ; + vcpu->run->exit_reason = (r == -ENOTSYNC ) ? +KVM_EXIT_MSIX_ROUTING_UPDATE :KVM_EXIT_MMIO ;This isn't very pretty,
exit_reasonshould be written invcpu_mmio_write(). I guess we can refactor it later.Sure.
Quoted 9 lines
include/linux/kvm.h struct kvm_assigned_msix_entry { · line 809
Quoted 7 lines
include/linux/kvm.h +7
-0+#define KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV (1<< 0) + +#define KVM_MSIX_MMIO_TYPE_BASE_TABLE (1<< 8) +#define KVM_MSIX_MMIO_TYPE_BASE_PBA (1<< 9) + +#define KVM_MSIX_MMIO_TYPE_DEV_MASK 0x00ff +#define KVM_MSIX_MMIO_TYPE_BASE_MASK 0xff00Any explanation of these?
I chose to use assigned device id instead of one specific table id, because every device should got at most one MSI MMIO(the same should applied to vfio device as well), and if we use specific table ID, we need way to associate with the device anyway, to perform mask/unmask or other operation. So I think it's better to use device ID here directly.
Quoted 3 lines
Quoted 1 line
Table id will be needed to make things work for emulated devices.
I suppose even emulated device should got some kind of id(BDF)?
Not that I know. Look at how irqfd is defined for example, or how interrupts are sent through a gsi.
I would like to make the interface be able to support that.
Quoted 4 lines
I think that is
enough for identification, which is already there, so we don't need to allocate another ID for the device - because one device would got at most one MSI-X MMIO, then use BDF or other device specific ID should be quite straightforward.
So you propose allocating ids for emulated devices? OK. How will we map e.g. irqfds to these?
Quoted 274 lines
Quoted 104 lines
My idea was this: we have the device id in
kvm_assigned_msix_entryalready. Just put table id and entry number inkvm_irq_routing_entry(create a new gsi type for this).
The result will also work for irqfd because these are mapped to gsi.Quoted 92 lines
And for the table and pba address, it's due to the mapping in userspace may know the guest MSI-X table address and PBA address at different
time(due to different BAR, refer to the code in assigned_dev_iomem_map() of qemu). So I purposed this API to allow each of them can be passed to kernel space individually.Quoted 54 lines
Quoted 5 lines
include/linux/kvm.h +5
-0+struct kvm_msix_mmio_user { +__u32 dev_id; +__u16 type; +__u16 max_entries_nr; +__u64 base_addr;include/linux/kvm.h struct kvm_assigned_msix_entry {
Quoted 10 lines
include/linux/kvm.h +8
-0+__u64 base_va; +__u64 flags; +__u64 reserved[4 ]; +}; + +int kvm_assigned_device_update_msix_mask_bit (struct kvm *kvm, +int assigned_dev_id,int entry,u32 flag) +{Need a better name for 'flag' (and make it a bool).
Quoted 36 lines
virt/kvm/assigned-dev.c +27
-0+int r = -EFAULT ; +struct kvm_assigned_dev_kernel *adev; +int i; + +if (!irqchip_in_kernel (kvm)) +return r; + +mutex_lock (&kvm->lock); + adev =kvm_find_assigned_dev (&kvm->arch.assigned_dev_head, + assigned_dev_id); +if (!adev) + goto out; + +for (i =0 ; i< adev->entries_nr; i++) +if (adev->host_msix_entries[i].entry == entry) { +if (flag) +disable_irq_nosync ( + adev->host_msix_entries[i].vector); +else +enable_irq (adev->host_msix_entries[i].vector); + r =0 ; +break ; + } +out: +mutex_unlock (&kvm->lock); +return r; +}@@ -1988,6 +2008,12 @@ static int kvm_dev_ioctl_create_vm(void) return r; }#endif Quoted 7 lines
virt/kvm/kvm_main.c static int kvm_dev_ioctl_create_vm(void) · line 2015
Quoted 5 lines
virt/kvm/kvm_main.c +5
-0+ r =kvm_register_msix_mmio_dev (kvm); +if (r<0 ) { +kvm_put_kvm (kvm); +return r; + }Shouldn't this be part of individual
KVM_REGISTER_MSIX_MMIOcalls?In fact this MMIO device is more like global one for the VM, not for every devices. It should handle all MMIO from all MSI-X enabled devices, so I put it in the VM init/destroy process.
Quoted 11 lines
Quoted 11 lines
(quoted diff) +1
-0+static int msix_table_mmio_read(struct kvm_io_device *this, gpa_taddr, int len, + void *val)
virt/kvm/msix_mmio.c new file +9
-0+{ +struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); +struct kvm_msix_mmio *mmio; +int idx, ret =0 , entry, offset, r; + +mutex_lock (&mmio_dev->lock); + idx =get_mmio_table_index (mmio_dev, addr, len); +if (idx<0 ) {Quoted 8 lines
virt/kvm/msix_mmio.c line 63
Quoted 5 lines
virt/kvm/msix_mmio.c new file +5
-0+ ret = -EOPNOTSUPP ; + goto out; + } +if ((addr&0x3 ) || (len !=4 && len !=8 )) + goto out;What about (addr & 4) && (len == 8)? Is it supported? It may cross entry boundaries.
Quoted 2 lines
Should not supported. But I haven't found words on the PCI spec for it. So I didn't add this check.
IMPLEMENTATION NOTE
MSI-X Memory Space Structures in Read/Write MemoryQuoted 6 lines
....
For all accesses to MSI-X Table and MSI-X PBA fields, software must use aligned full
DWORD or aligned full QWORD transactions; otherwise, the result is undefined.Yes, this one is enough, I would add the checking.
Quoted 155 lines
Quoted 139 lines
Quoted 139 lines
Quoted 33 lines
virt/kvm/msix_mmio.c new file +14
-0+ mmio =&mmio_dev->mmio[idx]; + + entry = (addr - mmio->table_base_addr) /PCI_MSIX_ENTRY_SIZE ; + offset = addr&0xf ; + r =copy_from_user (val, (void *)(mmio->table_base_va + + entry *PCI_MSIX_ENTRY_SIZE + offset), len); +if (r) + goto out; +out: +mutex_unlock (&mmio_dev->lock); +return ret; +} + +static int msix_table_mmio_write(struct kvm_io_device *this, gpa_taddr, + int len, const void *val)
virt/kvm/msix_mmio.c new file +15
-0+{ +struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); +struct kvm_msix_mmio *mmio; +int idx, entry, offset, ret =0 , r =0 ; +gpa_t entry_base; +u32 old_ctrl, new_ctrl; + +mutex_lock (&mmio_dev->lock); + idx =get_mmio_table_index (mmio_dev, addr, len); +if (idx<0 ) { + ret = -EOPNOTSUPP ; + goto out; + } +if ((addr&0x3 ) || (len !=4 && len !=8 ))virt/kvm/msix_mmio.c line 103
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ goto out; + mmio =&mmio_dev->mmio[idx]; + entry = (addr - mmio->table_base_addr) /PCI_MSIX_ENTRY_SIZE ; + entry_base = mmio->table_base_va + entry *PCI_MSIX_ENTRY_SIZE ; + offset = addr&0xF ; + +if (copy_from_user (&old_ctrl, + entry_base +PCI_MSIX_ENTRY_VECTOR_CTRL , +sizeof old_ctrl)) + goto out;
get_user()is easier.Quoted 4 lines
virt/kvm/msix_mmio.c new file +4
-0+ +/* No allow writing to other fields when entry is unmasked */ +if (!(old_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT )&& + offset !=PCI_MSIX_ENTRY_VECTOR_CTRL )virt/kvm/msix_mmio.c line 116
Quoted 10 lines
virt/kvm/msix_mmio.c new file +9
-0+ goto out; + +if (copy_to_user (entry_base + offset, val, len)) + goto out; + +if (copy_from_user (&new_ctrl, + entry_base +PCI_MSIX_ENTRY_VECTOR_CTRL , +sizeof new_ctrl)) + goto out;
put_user()Quoted 8 lines
virt/kvm/msix_mmio.c new file +8
-0+ +if ((offset<PCI_MSIX_ENTRY_VECTOR_CTRL && len ==4 ) || + (offset<PCI_MSIX_ENTRY_DATA && len ==8 )) + ret = -ENOTSYNC ; +if (old_ctrl == new_ctrl) + goto out; +if (!(old_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT )&& + (new_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT ))virt/kvm/msix_mmio.c line 134
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ r =update_msix_mask_bit (mmio_dev->kvm, mmio, entry,1 ); + else if ((old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + !(new_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)) + r =update_msix_mask_bit (mmio_dev->kvm, mmio, entry,0 ); +if (r || ret) + ret = -ENOTSYNC ; +out: +mutex_unlock (&mmio_dev->lock); +return ret; +}blank line...
Quoted 25 lines
virt/kvm/msix_mmio.c new file +25
-0+static const struct kvm_io_device_ops msix_mmio_table_ops = { + .read = msix_table_mmio_read, + .write = msix_table_mmio_write, +}; + ++ +int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm, + struct kvm_msix_mmio_user *mmio_user) +{ + struct kvm_msix_mmio_dev *mmio_dev =&kvm->msix_mmio_dev; +struct kvm_msix_mmio *mmio =NULL ; +int r =0 , i; + +mutex_lock (&mmio_dev->lock); +for (i =0 ; i< mmio_dev->mmio_nr; i++) { +if (mmio_dev->mmio[i].dev_id == mmio_user->dev_id&& + (mmio_dev->mmio[i].type&KVM_MSIX_MMIO_TYPE_DEV_MASK ) == + (mmio_user->type&KVM_MSIX_MMIO_TYPE_DEV_MASK )) { + mmio =&mmio_dev->mmio[i]; +if (mmio->max_entries_nr != mmio_user->max_entries_nr) { + r = -EINVAL ; + goto out; + } +break ; + }virt/kvm/msix_mmio.c line 193
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ } +if (!mmio) { +if (mmio_dev->mmio_nr ==KVM_MSIX_MMIO_MAX ) { + r = -ENOSPC ; + goto out; + } + mmio =&mmio_dev->mmio[mmio_dev->mmio_nr]; + mmio_dev->mmio_nr++; + } + mmio->max_entries_nr = mmio_user->max_entries_nr;Sanity check to avoid overflow.
virt/kvm/msix_mmio.c line 195
Quoted 2 lines
virt/kvm/msix_mmio.c new file +2
-0+ mmio->dev_id = mmio_user->dev_id; + mmio->flags = mmio_user->flags;Check for unsupported bits (all of them at present?)
virt/kvm/msix_mmio.c line 204
Quoted 9 lines
virt/kvm/msix_mmio.c new file +9
-0+if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_DEV_MASK ) == +KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV ) + mmio->type =KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV ; + +if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_BASE_MASK ) == +KVM_MSIX_MMIO_TYPE_BASE_TABLE ) { + mmio->type |=KVM_MSIX_MMIO_TYPE_BASE_TABLE ; + mmio->table_base_addr = mmio_user->base_addr; + mmio->table_base_va = mmio_user->base_va;Check for va in kernel space.
Quoted 15 lines
Quoted 7 lines
Quoted 7 lines
virt/kvm/msix_mmio.c new file +7
-0+ }else if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_BASE_MASK ) == +KVM_MSIX_MMIO_TYPE_BASE_PBA ) { + mmio->type |=KVM_MSIX_MMIO_TYPE_BASE_PBA ; + mmio->pba_base_addr = mmio_user->base_addr; + mmio->pba_base_va = mmio_user->base_va; + } +out:Quoted 8 lines
virt/kvm/msix_mmio.c line 222
Quoted 5 lines
virt/kvm/msix_mmio.c new file +5
-0+mutex_unlock (&mmio_dev->lock); +return r; +} + +In all, looks reasonable. I'd like to see documentation for it, and review from the pci people. Alex, mst?
Quoted 6 lines
Some general comments:
PBA isn't supported in this version, which is OK, but let's not add a capability until it is, and let's not try to guess what
the interface will look like. I think keeping PBA in userspace will be hard because it needs to be modified from interrupt context. Removing the PBA stub will make the interface simpler.
Quoted 2 lines
The API only get the PBA address now which should be fine. And we still have threaded irq and tasklet for accessing the userspace for interrupt handler...
I don't think it's going to work: we are not
in the context of the right process. Further
I think we should keep the option of
reading the PBA status from the device or host kernel open. And generally having an interface
for functionality we don't implement is not a good idea:
you don't know whether you really can support the interface you promised.
Quoted 9 lines
--
regards
Yang, ShengQuoted 5 lines
Quoted 5 lines
Would add the API document soon.
--
regards
Yang, Sheng
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 62 lines · On Wednesday 29 December 2010 17:28:24 Michael S. Tsirkin wrote:
Quoted 53 lines · Sheng Yang
Quoted 52 lines · On Wednesday 29 December 2010 16:31:35 Michael S. Tsirkin wrote:
Quoted 50 lines · Sheng Yang
Quoted 22 lines · On Tuesday 28 December 2010 20:26:13 Avi Kivity wrote:
Quoted 21 lines · Sheng Yang
Then we can support mask bit operation of assigned devices now.
(quoted diff) +2
-1@@ -3817,14 +3819,16 @@ static int emulator_write_emulated_onepage(unsigned long addr, mmio: trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, *(u64 *)val); + r = vcpu_mmio_write(vcpu, gpa, bytes, val); /* * Is this MMIO handled locally? */ - if (!vcpu_mmio_write(vcpu, gpa, bytes, val)) + if (!r) return X86EMUL_CONTINUE;Quoted 8 lines
arch/x86/kvm/x86.c static int emulator_write_emulated_onepage(unsigned long addr, · line 3831
Quoted 5 lines
arch/x86/kvm/x86.c +2
-1vcpu->mmio_needed =1 ; - vcpu->run->exit_reason =KVM_EXIT_MMIO ; + vcpu->run->exit_reason = (r == -ENOTSYNC ) ? +KVM_EXIT_MSIX_ROUTING_UPDATE :KVM_EXIT_MMIO ;This isn't very pretty,
exit_reasonshould be written invcpu_mmio_write(). I guess we can refactor it later.Sure.
Quoted 9 lines
include/linux/kvm.h struct kvm_assigned_msix_entry { · line 809
Quoted 7 lines
include/linux/kvm.h +7
-0+#define KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV (1<< 0) + +#define KVM_MSIX_MMIO_TYPE_BASE_TABLE (1<< 8) +#define KVM_MSIX_MMIO_TYPE_BASE_PBA (1<< 9) + +#define KVM_MSIX_MMIO_TYPE_DEV_MASK 0x00ff +#define KVM_MSIX_MMIO_TYPE_BASE_MASK 0xff00Any explanation of these?
I chose to use assigned device id instead of one specific table id, because every device should got at most one MSI MMIO(the same should applied to vfio device as well), and if we use specific table ID, we need way to associate with the device anyway, to perform mask/unmask or other operation. So I think it's better to use device ID here directly.
Quoted 3 lines
Quoted 1 line
Table id will be needed to make things work for emulated devices.
I suppose even emulated device should got some kind of id(BDF)?
Not that I know. Look at how irqfd is defined for example, or how interrupts are sent through a gsi.
I would like to make the interface be able to support that.
Quoted 8 lines
Quoted 5 lines
I think that is
enough for identification, which is already there, so we don't need to allocate another ID for the device - because one device would got at most one MSI-X MMIO, then use BDF or other device specific ID should be quite straightforward.So you propose allocating ids for emulated devices? OK. How will we map e.g. irqfds to these?
I don't understand. I've checked virtio-pci.c which is using irqfd, and it's still a PCI device, and still have BDF, right?
Also, what we want is a way to identify the MSI-X MMIO. For assigned device, we use BDF, then we can easily identify the MMIO as well as the device. For others, even they don't have BDF(I don't think so, because MSI-X is a part of PCI, and every PCI device has BDF), what you need is an ID, no matter what it is and how it defined. QEmu can get the allocation done, and the type field in this API can still tell which kind of ID/devices they are, then determine how to deal with them.
Quoted 205 lines
Quoted 205 lines
Quoted 104 lines
My idea was this: we have the device id in
kvm_assigned_msix_entryalready. Just put table id and entry number inkvm_irq_routing_entry(create a new gsi type for this).
The result will also work for irqfd because these are mapped to gsi.Quoted 92 lines
And for the table and pba address, it's due to the mapping in userspace may know the guest MSI-X table address and PBA address at different
time(due to different BAR, refer to the code in assigned_dev_iomem_map() of qemu). So I purposed this API to allow each of them can be passed to kernel space individually.Quoted 54 lines
Quoted 5 lines
include/linux/kvm.h +5
-0+struct kvm_msix_mmio_user { +__u32 dev_id; +__u16 type; +__u16 max_entries_nr; +__u64 base_addr;include/linux/kvm.h struct kvm_assigned_msix_entry {
Quoted 10 lines
include/linux/kvm.h +8
-0+__u64 base_va; +__u64 flags; +__u64 reserved[4 ]; +}; + +int kvm_assigned_device_update_msix_mask_bit (struct kvm *kvm, +int assigned_dev_id,int entry,u32 flag) +{Need a better name for 'flag' (and make it a bool).
Quoted 36 lines
virt/kvm/assigned-dev.c +27
-0+int r = -EFAULT ; +struct kvm_assigned_dev_kernel *adev; +int i; + +if (!irqchip_in_kernel (kvm)) +return r; + +mutex_lock (&kvm->lock); + adev =kvm_find_assigned_dev (&kvm->arch.assigned_dev_head, + assigned_dev_id); +if (!adev) + goto out; + +for (i =0 ; i< adev->entries_nr; i++) +if (adev->host_msix_entries[i].entry == entry) { +if (flag) +disable_irq_nosync ( + adev->host_msix_entries[i].vector); +else +enable_irq (adev->host_msix_entries[i].vector); + r =0 ; +break ; + } +out: +mutex_unlock (&kvm->lock); +return r; +}@@ -1988,6 +2008,12 @@ static int kvm_dev_ioctl_create_vm(void) return r; }#endif Quoted 7 lines
virt/kvm/kvm_main.c static int kvm_dev_ioctl_create_vm(void) · line 2015
Quoted 5 lines
virt/kvm/kvm_main.c +5
-0+ r =kvm_register_msix_mmio_dev (kvm); +if (r<0 ) { +kvm_put_kvm (kvm); +return r; + }Shouldn't this be part of individual
KVM_REGISTER_MSIX_MMIOcalls?In fact this MMIO device is more like global one for the VM, not for every devices. It should handle all MMIO from all MSI-X enabled devices, so I put it in the VM init/destroy process.
Quoted 11 lines
Quoted 11 lines
(quoted diff) +1
-0+static int msix_table_mmio_read(struct kvm_io_device *this,
gpa_taddr, int len, + void *val)virt/kvm/msix_mmio.c new file +9
-0+{ +struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); +struct kvm_msix_mmio *mmio; +int idx, ret =0 , entry, offset, r; + +mutex_lock (&mmio_dev->lock); + idx =get_mmio_table_index (mmio_dev, addr, len); +if (idx<0 ) {Quoted 8 lines
virt/kvm/msix_mmio.c line 63
Quoted 5 lines
virt/kvm/msix_mmio.c new file +5
-0+ ret = -EOPNOTSUPP ; + goto out; + } +if ((addr&0x3 ) || (len !=4 && len !=8 )) + goto out;What about (addr & 4) && (len == 8)? Is it supported? It may cross entry boundaries.
Quoted 2 lines
Should not supported. But I haven't found words on the PCI spec for it. So I didn't add this check.
IMPLEMENTATION NOTE
MSI-X Memory Space Structures in Read/Write MemoryQuoted 6 lines
....
For all accesses to MSI-X Table and MSI-X PBA fields, software must use aligned full
DWORD or aligned full QWORD transactions; otherwise, the result is undefined.Yes, this one is enough, I would add the checking.
Quoted 92 lines
Quoted 92 lines
Quoted 92 lines
Quoted 32 lines
virt/kvm/msix_mmio.c new file +14
-0+ mmio =&mmio_dev->mmio[idx]; + + entry = (addr - mmio->table_base_addr) /PCI_MSIX_ENTRY_SIZE ; + offset = addr&0xf ; + r =copy_from_user (val, (void *)(mmio->table_base_va + + entry *PCI_MSIX_ENTRY_SIZE + offset), len); +if (r) + goto out; +out: +mutex_unlock (&mmio_dev->lock); +return ret; +} + +static int msix_table_mmio_write(struct kvm_io_device *this,
gpa_taddr, + int len, const void *val)virt/kvm/msix_mmio.c new file +15
-0+{ +struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); +struct kvm_msix_mmio *mmio; +int idx, entry, offset, ret =0 , r =0 ; +gpa_t entry_base; +u32 old_ctrl, new_ctrl; + +mutex_lock (&mmio_dev->lock); + idx =get_mmio_table_index (mmio_dev, addr, len); +if (idx<0 ) { + ret = -EOPNOTSUPP ; + goto out; + } +if ((addr&0x3 ) || (len !=4 && len !=8 ))virt/kvm/msix_mmio.c line 103
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ goto out; + mmio =&mmio_dev->mmio[idx]; + entry = (addr - mmio->table_base_addr) /PCI_MSIX_ENTRY_SIZE ; + entry_base = mmio->table_base_va + entry *PCI_MSIX_ENTRY_SIZE ; + offset = addr&0xF ; + +if (copy_from_user (&old_ctrl, + entry_base +PCI_MSIX_ENTRY_VECTOR_CTRL , +sizeof old_ctrl)) + goto out;
get_user()is easier.Quoted 4 lines
virt/kvm/msix_mmio.c new file +4
-0+ +/* No allow writing to other fields when entry is unmasked */ +if (!(old_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT )&& + offset !=PCI_MSIX_ENTRY_VECTOR_CTRL )virt/kvm/msix_mmio.c line 116
Quoted 10 lines
virt/kvm/msix_mmio.c new file +9
-0+ goto out; + +if (copy_to_user (entry_base + offset, val, len)) + goto out; + +if (copy_from_user (&new_ctrl, + entry_base +PCI_MSIX_ENTRY_VECTOR_CTRL , +sizeof new_ctrl)) + goto out;
put_user()Quoted 8 lines
virt/kvm/msix_mmio.c new file +8
-0+ +if ((offset<PCI_MSIX_ENTRY_VECTOR_CTRL && len ==4 ) || + (offset<PCI_MSIX_ENTRY_DATA && len ==8 )) + ret = -ENOTSYNC ; +if (old_ctrl == new_ctrl) + goto out; +if (!(old_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT )&& + (new_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT ))virt/kvm/msix_mmio.c line 134
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ r =update_msix_mask_bit (mmio_dev->kvm, mmio, entry,1 ); + else if ((old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + !(new_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)) + r =update_msix_mask_bit (mmio_dev->kvm, mmio, entry,0 ); +if (r || ret) + ret = -ENOTSYNC ; +out: +mutex_unlock (&mmio_dev->lock); +return ret; +}blank line...
Quoted 9 lines
virt/kvm/msix_mmio.c new file +9
-0+static const struct kvm_io_device_ops msix_mmio_table_ops = { + .read = msix_table_mmio_read, + .write = msix_table_mmio_write, +}; + ++ +int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm, + struct kvm_msix_mmio_user *mmio_user) +{
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
virt/kvm/msix_mmio.c
Quoted 8 lines
virt/kvm/msix_mmio.c new file +8
-0+struct kvm_msix_mmio_dev *mmio_dev =&kvm->msix_mmio_dev; +struct kvm_msix_mmio *mmio =NULL ; +int r =0 , i; + +mutex_lock (&mmio_dev->lock); +for (i =0 ; i< mmio_dev->mmio_nr; i++) { +if (mmio_dev->mmio[i].dev_id == mmio_user->dev_id&& + (mmio_dev->mmio[i].type& KVM_MSIX_MMIO_TYPE_DEV_MASK)
==
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Review
Quoted 3 lines
(quoted diff) +3
-0+ (mmio_user->type& KVM_MSIX_MMIO_TYPE_DEV_MASK)) { + mmio =&mmio_dev->mmio[i]; + if (mmio->max_entries_nr != mmio_user->max_entries_nr)
{
Quoted 62 lines
Quoted 58 lines
Quoted 51 lines
Quoted 35 lines
Quoted 35 lines
Quoted 5 lines
virt/kvm/msix_mmio.c new file +5
-0+ r = -EINVAL ; + goto out; + } +break ; + }virt/kvm/msix_mmio.c line 193
Quoted 10 lines
virt/kvm/msix_mmio.c new file +10
-0+ } +if (!mmio) { +if (mmio_dev->mmio_nr ==KVM_MSIX_MMIO_MAX ) { + r = -ENOSPC ; + goto out; + } + mmio =&mmio_dev->mmio[mmio_dev->mmio_nr]; + mmio_dev->mmio_nr++; + } + mmio->max_entries_nr = mmio_user->max_entries_nr;Sanity check to avoid overflow.
virt/kvm/msix_mmio.c line 195
Quoted 2 lines
virt/kvm/msix_mmio.c new file +2
-0+ mmio->dev_id = mmio_user->dev_id; + mmio->flags = mmio_user->flags;Check for unsupported bits (all of them at present?)
virt/kvm/msix_mmio.c line 204
Quoted 9 lines
virt/kvm/msix_mmio.c new file +9
-0+if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_DEV_MASK ) == +KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV ) + mmio->type =KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV ; + +if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_BASE_MASK ) == +KVM_MSIX_MMIO_TYPE_BASE_TABLE ) { + mmio->type |=KVM_MSIX_MMIO_TYPE_BASE_TABLE ; + mmio->table_base_addr = mmio_user->base_addr; + mmio->table_base_va = mmio_user->base_va;Check for va in kernel space.
Quoted 15 lines
Quoted 7 lines
Quoted 7 lines
virt/kvm/msix_mmio.c new file +7
-0+ }else if ((mmio_user->type&KVM_MSIX_MMIO_TYPE_BASE_MASK ) == +KVM_MSIX_MMIO_TYPE_BASE_PBA ) { + mmio->type |=KVM_MSIX_MMIO_TYPE_BASE_PBA ; + mmio->pba_base_addr = mmio_user->base_addr; + mmio->pba_base_va = mmio_user->base_va; + } +out:Quoted 8 lines
virt/kvm/msix_mmio.c line 222
Quoted 5 lines
virt/kvm/msix_mmio.c new file +5
-0+mutex_unlock (&mmio_dev->lock); +return r; +} + +In all, looks reasonable. I'd like to see documentation for it, and review from the pci people. Alex, mst?
Quoted 6 lines
Some general comments:
PBA isn't supported in this version, which is OK, but let's not add a capability until it is, and let's not try to guess what
the interface will look like. I think keeping PBA in userspace will be hard because it needs to be modified from interrupt context. Removing the PBA stub will make the interface simpler.Quoted 3 lines
The API only get the PBA address now which should be fine. And we still have threaded irq and tasklet for accessing the userspace for interrupt handler...
Quoted 7 lines
I don't think it's going to work: we are not
in the context of the right process. Further
I think we should keep the option of
reading the PBA status from the device or host kernel open. And generally having an interface
for functionality we don't implement is not a good idea:
you don't know whether you really can support the interface you promised.
Well, I don't know if we want to read PBA from device directly. To me it's not a good idea because the real device has nothing to do with the one we show to the guest. At least direct accessing the mask bits of real device would be very dangerous. Avi?
--
regards
Yang, Sheng
Quoted 10 lines
Quoted 10 lines
--
regards
Yang, ShengQuoted 5 lines
Quoted 5 lines
Would add the API document soon.
--
regards
Yang, Sheng
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 75 lines · Sheng Yang
Quoted 62 lines · On Wednesday 29 December 2010 17:28:24 Michael S. Tsirkin wrote:
Quoted 53 lines · Sheng Yang
Quoted 52 lines · On Wednesday 29 December 2010 16:31:35 Michael S. Tsirkin wrote:
Quoted 50 lines · Sheng Yang
Quoted 22 lines · On Tuesday 28 December 2010 20:26:13 Avi Kivity wrote:
Quoted 21 lines · Sheng Yang
Then we can support mask bit operation of assigned devices now.
(quoted diff) +2
-1@@ -3817,14 +3819,16 @@ static int emulator_write_emulated_onepage(unsigned long addr, mmio: trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, *(u64 *)val); + r = vcpu_mmio_write(vcpu, gpa, bytes, val); /* * Is this MMIO handled locally? */ - if (!vcpu_mmio_write(vcpu, gpa, bytes, val)) + if (!r) return X86EMUL_CONTINUE;Quoted 8 lines
Review static int
Quoted 5 lines
(quoted diff) +2
-1vcpu->mmio_needed = 1; - vcpu->run->exit_reason = KVM_EXIT_MMIO; + vcpu->run->exit_reason = (r == -ENOTSYNC) ? + KVM_EXIT_MSIX_ROUTING_UPDATE : KVM_EXIT_MMIO;This isn't very pretty,
exit_reasonshould be written invcpu_mmio_write(). I guess we can refactor it later.Sure.
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+#define KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV (1<< 0) + +#define KVM_MSIX_MMIO_TYPE_BASE_TABLE (1<< 8) +#define KVM_MSIX_MMIO_TYPE_BASE_PBA (1<< 9) + +#define KVM_MSIX_MMIO_TYPE_DEV_MASK 0x00ff +#define KVM_MSIX_MMIO_TYPE_BASE_MASK 0xff00Any explanation of these?
I chose to use assigned device id instead of one specific table id, because every device should got at most one MSI MMIO(the same should applied to vfio device as well), and if we use specific table ID, we need way to associate with the device anyway, to perform mask/unmask or other operation. So I think it's better to use device ID here directly.
Quoted 3 lines
Quoted 1 line
Table id will be needed to make things work for emulated devices.
I suppose even emulated device should got some kind of id(BDF)?
Not that I know. Look at how irqfd is defined for example, or how interrupts are sent through a gsi.
I would like to make the interface be able to support that.Quoted 8 lines
Quoted 5 lines
I think that is
enough for identification, which is already there, so we don't need to allocate another ID for the device - because one device would got at most one MSI-X MMIO, then use BDF or other device specific ID should be quite straightforward.So you propose allocating ids for emulated devices? OK. How will we map e.g. irqfds to these?
I don't understand. I've checked
virtio-pci.cwhich is using irqfd, and it's still a PCI device, and still have BDF, right?
Quoted 6 lines
Also, what we want is a way to identify the MSI-X MMIO. For assigned device, we use BDF, then we can easily identify the MMIO as well as the device. For others, even they don't have
BDF(I don't think so, because MSI-X is a part of PCI, and every PCI device has BDF), what you need is an ID, no matter what it is and how it defined. QEmu can get the allocation done, and the type field in this API can still tell which kind of ID/devices they are, then determine how to deal with them.
Yes, the PCI device can be identified with e.g. BFD
(won't work for multi-domain but then we can write an allocator maybe). But how will we inject these interrupts?
We can do this now with GSI ioctl or map GSI to irqfd
and inject with irqfd write.
Quoted 288 lines
Quoted 205 lines
Quoted 205 lines
Quoted 104 lines
My idea was this: we have the device id in
kvm_assigned_msix_entryalready. Just put table id and entry number inkvm_irq_routing_entry(create a new gsi type for this).
The result will also work for irqfd because these are mapped to gsi.Quoted 92 lines
And for the table and pba address, it's due to the mapping in userspace may know the guest MSI-X table address and PBA address at different
time(due to different BAR, refer to the code in assigned_dev_iomem_map() of qemu). So I purposed this API to allow each of them can be passed to kernel space individually.Quoted 54 lines
Quoted 5 lines
(quoted diff) +5
-0+struct kvm_msix_mmio_user { + __u32 dev_id; + __u16 type; + __u16 max_entries_nr; + __u64 base_addr;Review
Quoted 10 lines
(quoted diff) +8
-0+ __u64 base_va; + __u64 flags; + __u64 reserved[4]; +}; + +int kvm_assigned_device_update_msix_mask_bit(struct kvm *kvm, + int assigned_dev_id, int entry, u32 flag) +{Need a better name for 'flag' (and make it a bool).
Quoted 36 lines
(quoted diff) +27
-0+ int r = -EFAULT; + struct kvm_assigned_dev_kernel *adev; + int i; + + if (!irqchip_in_kernel(kvm)) + return r; + + mutex_lock(&kvm->lock); + adev = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head, + assigned_dev_id); + if (!adev) + goto out; + + for (i = 0; i< adev->entries_nr; i++) + if (adev->host_msix_entries[i].entry == entry) { + if (flag) + disable_irq_nosync( + adev->host_msix_entries[i].vector); + else + enable_irq(adev->host_msix_entries[i].vector); + r = 0; + break; + } +out: + mutex_unlock(&kvm->lock); + return r; +}@@ -1988,6 +2008,12 @@ static int kvm_dev_ioctl_create_vm(void) return r; } #endifQuoted 7 lines
Review static int kvm_dev_ioctl_create_vm(void) · line 2018
Quoted 5 lines
(quoted diff) +5
-0+ r = kvm_register_msix_mmio_dev(kvm); + if (r< 0) { + kvm_put_kvm(kvm); + return r; + }Shouldn't this be part of individual
KVM_REGISTER_MSIX_MMIOcalls?In fact this MMIO device is more like global one for the VM, not for every devices. It should handle all MMIO from all MSI-X enabled devices, so I put it in the VM init/destroy process.
Quoted 11 lines
Quoted 11 lines
(quoted diff) +1
-0+static int msix_table_mmio_read(struct kvm_io_device *this,
gpa_taddr, int len, + void *val)(quoted diff) +9
-0+{ + struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); + struct kvm_msix_mmio *mmio; + int idx, ret = 0, entry, offset, r; + + mutex_lock(&mmio_dev->lock); + idx = get_mmio_table_index(mmio_dev, addr, len); + if (idx< 0) {Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ ret = -EOPNOTSUPP; + goto out; + } + if ((addr& 0x3) || (len != 4&& len != 8)) + goto out;What about (addr & 4) && (len == 8)? Is it supported? It may cross entry boundaries.
Quoted 2 lines
Should not supported. But I haven't found words on the PCI spec for it. So I didn't add this check.
IMPLEMENTATION NOTE
MSI-X Memory Space Structures in Read/Write MemoryQuoted 6 lines
....
For all accesses to MSI-X Table and MSI-X PBA fields, software must use aligned full
DWORD or aligned full QWORD transactions; otherwise, the result is undefined.Yes, this one is enough, I would add the checking.
Quoted 92 lines
Quoted 92 lines
Quoted 92 lines
Quoted 32 lines
(quoted diff) +14
-0+ mmio =&mmio_dev->mmio[idx]; + + entry = (addr - mmio->table_base_addr) / PCI_MSIX_ENTRY_SIZE; + offset = addr& 0xf; + r = copy_from_user(val, (void *)(mmio->table_base_va + + entry * PCI_MSIX_ENTRY_SIZE + offset), len); + if (r) + goto out; +out: + mutex_unlock(&mmio_dev->lock); + return ret; +} + +static int msix_table_mmio_write(struct kvm_io_device *this,
gpa_taddr, + int len, const void *val)(quoted diff) +15
-0+{ + struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); + struct kvm_msix_mmio *mmio; + int idx, entry, offset, ret = 0, r = 0; + gpa_t entry_base; + u32 old_ctrl, new_ctrl; + + mutex_lock(&mmio_dev->lock); + idx = get_mmio_table_index(mmio_dev, addr, len); + if (idx< 0) { + ret = -EOPNOTSUPP; + goto out; + } + if ((addr& 0x3) || (len != 4&& len != 8))Review
Quoted 10 lines
(quoted diff) +10
-0+ goto out; + mmio =&mmio_dev->mmio[idx]; + entry = (addr - mmio->table_base_addr) / PCI_MSIX_ENTRY_SIZE; + entry_base = mmio->table_base_va + entry * PCI_MSIX_ENTRY_SIZE; + offset = addr& 0xF; + + if (copy_from_user(&old_ctrl, + entry_base + PCI_MSIX_ENTRY_VECTOR_CTRL, + sizeof old_ctrl)) + goto out;
get_user()is easier.Quoted 4 lines
(quoted diff) +4
-0+ + /* No allow writing to other fields when entry is unmasked */ + if (!(old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + offset != PCI_MSIX_ENTRY_VECTOR_CTRL)Review
Quoted 10 lines
(quoted diff) +9
-0+ goto out; + + if (copy_to_user(entry_base + offset, val, len)) + goto out; + + if (copy_from_user(&new_ctrl, + entry_base + PCI_MSIX_ENTRY_VECTOR_CTRL, + sizeof new_ctrl)) + goto out;
put_user()Quoted 8 lines
(quoted diff) +8
-0+ + if ((offset< PCI_MSIX_ENTRY_VECTOR_CTRL&& len == 4) || + (offset< PCI_MSIX_ENTRY_DATA&& len == 8)) + ret = -ENOTSYNC; + if (old_ctrl == new_ctrl) + goto out; + if (!(old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + (new_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT))Review
Quoted 10 lines
(quoted diff) +10
-0+ r = update_msix_mask_bit(mmio_dev->kvm, mmio, entry, 1); + else if ((old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + !(new_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)) + r = update_msix_mask_bit(mmio_dev->kvm, mmio, entry, 0); + if (r || ret) + ret = -ENOTSYNC; +out: + mutex_unlock(&mmio_dev->lock); + return ret; +}blank line...
Quoted 9 lines
(quoted diff) +9
-0+static const struct kvm_io_device_ops msix_mmio_table_ops = { + .read = msix_table_mmio_read, + .write = msix_table_mmio_write, +}; + ++ +int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm, + struct kvm_msix_mmio_user *mmio_user) +{Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Review
Quoted 8 lines
(quoted diff) +8
-0+ struct kvm_msix_mmio_dev *mmio_dev =&kvm->msix_mmio_dev; + struct kvm_msix_mmio *mmio = NULL; + int r = 0, i; + + mutex_lock(&mmio_dev->lock); + for (i = 0; i< mmio_dev->mmio_nr; i++) { + if (mmio_dev->mmio[i].dev_id == mmio_user->dev_id&& + (mmio_dev->mmio[i].type& KVM_MSIX_MMIO_TYPE_DEV_MASK)==
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Review
Quoted 3 lines
(quoted diff) +3
-0+ (mmio_user->type& KVM_MSIX_MMIO_TYPE_DEV_MASK)) { + mmio =&mmio_dev->mmio[i]; + if (mmio->max_entries_nr != mmio_user->max_entries_nr){
Quoted 62 lines
Quoted 58 lines
Quoted 51 lines
Quoted 35 lines
Quoted 35 lines
Quoted 5 lines
(quoted diff) +5
-0+ r = -EINVAL; + goto out; + } + break; + }Review
Quoted 10 lines
(quoted diff) +10
-0+ } + if (!mmio) { + if (mmio_dev->mmio_nr == KVM_MSIX_MMIO_MAX) { + r = -ENOSPC; + goto out; + } + mmio =&mmio_dev->mmio[mmio_dev->mmio_nr]; + mmio_dev->mmio_nr++; + } + mmio->max_entries_nr = mmio_user->max_entries_nr;Sanity check to avoid overflow.
Review
Quoted 2 lines
(quoted diff) +2
-0+ mmio->dev_id = mmio_user->dev_id; + mmio->flags = mmio_user->flags;Check for unsupported bits (all of them at present?)
Review
Quoted 9 lines
(quoted diff) +9
-0+ if ((mmio_user->type& KVM_MSIX_MMIO_TYPE_DEV_MASK) == + KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV) + mmio->type = KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV; + + if ((mmio_user->type& KVM_MSIX_MMIO_TYPE_BASE_MASK) == + KVM_MSIX_MMIO_TYPE_BASE_TABLE) { + mmio->type |= KVM_MSIX_MMIO_TYPE_BASE_TABLE; + mmio->table_base_addr = mmio_user->base_addr; + mmio->table_base_va = mmio_user->base_va;Check for va in kernel space.
Quoted 15 lines
Quoted 7 lines
Quoted 7 lines
(quoted diff) +7
-0+ } else if ((mmio_user->type& KVM_MSIX_MMIO_TYPE_BASE_MASK) == + KVM_MSIX_MMIO_TYPE_BASE_PBA) { + mmio->type |= KVM_MSIX_MMIO_TYPE_BASE_PBA; + mmio->pba_base_addr = mmio_user->base_addr; + mmio->pba_base_va = mmio_user->base_va; + } +out:Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ mutex_unlock(&mmio_dev->lock); + return r; +} + +In all, looks reasonable. I'd like to see documentation for it, and review from the pci people. Alex, mst?
Quoted 6 lines
Some general comments:
PBA isn't supported in this version, which is OK, but let's not add a capability until it is, and let's not try to guess what
the interface will look like. I think keeping PBA in userspace will be hard because it needs to be modified from interrupt context. Removing the PBA stub will make the interface simpler.Quoted 3 lines
The API only get the PBA address now which should be fine. And we still have threaded irq and tasklet for accessing the userspace for interrupt handler...
Quoted 7 lines
I don't think it's going to work: we are not
in the context of the right process. Further
I think we should keep the option of
reading the PBA status from the device or host kernel open. And generally having an interface
for functionality we don't implement is not a good idea:
you don't know whether you really can support the interface you promised.
Quoted 8 lines
Well, I don't know if we want to read PBA from device directly. To me it's not a good idea because the real device has nothing to do with the one we show to the guest. At least direct accessing the mask bits of real device would be very dangerous. Avi?
--
regards
Yang, Sheng
I am not really suggesting this. What I say is PBA is unimplemented let us not commit to an interface yet.
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
--
regards
Yang, ShengQuoted 5 lines
Quoted 5 lines
Would add the API document soon.
--
regards
Yang, Sheng
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 86 lines · On Thursday 30 December 2010 15:47:48 Michael S. Tsirkin wrote:
Quoted 77 lines · Sheng Yang
Quoted 64 lines · On Wednesday 29 December 2010 17:28:24 Michael S. Tsirkin wrote:
Quoted 55 lines · Sheng Yang
Quoted 54 lines · On Wednesday 29 December 2010 16:31:35 Michael S. Tsirkin wrote:
Quoted 52 lines · Sheng Yang
Quoted 24 lines · On Tuesday 28 December 2010 20:26:13 Avi Kivity wrote:
Quoted 23 lines · Sheng Yang
Then we can support mask bit operation of assigned devices now.
(quoted diff) +2
-1@@ -3817,14 +3819,16 @@ static int emulator_write_emulated_onepage(unsigned long addr, mmio: trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, *(u64 *)val); + r = vcpu_mmio_write(vcpu, gpa, bytes, val); /* * Is this MMIO handled locally? */ - if (!vcpu_mmio_write(vcpu, gpa, bytes, val)) + if (!r) return X86EMUL_CONTINUE;Quoted 8 lines
Review
Quoted 5 lines
vcpu->mmio_needed = 1;(quoted diff) +2
-1- vcpu->run->exit_reason = KVM_EXIT_MMIO; + vcpu->run->exit_reason = (r == -ENOTSYNC) ? + KVM_EXIT_MSIX_ROUTING_UPDATE : KVM_EXIT_MMIO;This isn't very pretty,
exit_reasonshould be written invcpu_mmio_write(). I guess we can refactor it later.Sure.
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+#define KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV (1<< 0) + +#define KVM_MSIX_MMIO_TYPE_BASE_TABLE (1<< 8) +#define KVM_MSIX_MMIO_TYPE_BASE_PBA (1<< 9) + +#define KVM_MSIX_MMIO_TYPE_DEV_MASK 0x00ff +#define KVM_MSIX_MMIO_TYPE_BASE_MASK 0xff00Any explanation of these?
I chose to use assigned device id instead of one specific table id, because every device should got at most one MSI MMIO(the same should applied to vfio device as well), and if we use specific table ID, we need way to associate with the device anyway, to perform mask/unmask or other operation. So I think it's better to use device ID here directly.
Quoted 3 lines
Quoted 1 line
Table id will be needed to make things work for emulated devices.
I suppose even emulated device should got some kind of id(BDF)?
Not that I know. Look at how irqfd is defined for example, or how interrupts are sent through a gsi.
I would like to make the interface be able to support that.Quoted 8 lines
Quoted 5 lines
I think that is
enough for identification, which is already there, so we don't need to allocate another ID for the device - because one device would got at most one MSI-X MMIO, then use BDF or other device specific ID should be quite straightforward.So you propose allocating ids for emulated devices? OK. How will we map e.g. irqfds to these?
I don't understand. I've checked
virtio-pci.cwhich is using irqfd, and it's still a PCI device, and still have BDF, right?Quoted 7 lines
Also, what we want is a way to identify the MSI-X MMIO. For assigned device, we use BDF, then we can easily identify the MMIO as well as the device. For others, even they don't have
BDF(I don't think so, because MSI-X is a part of PCI, and every PCI device has BDF), what you need is an ID, no matter what it is and how it defined. QEmu can get the allocation done, and the type field in this API can still tell which kind of ID/devices they are, then determine how to deal with them.
Quoted 5 lines
Yes, the PCI device can be identified with e.g. BFD
(won't work for multi-domain but then we can write an allocator maybe). But how will we inject these interrupts?
We can do this now with GSI ioctl or map GSI to irqfd
and inject with irqfd write.
I suppose it's not in the scope of this patch... But I think you can still do this, everything is the same as before. QEmu can read from table to get data/address pair, then program the routing table, etc.
--
regards
Yang, Sheng
Quoted 207 lines
Quoted 207 lines
Quoted 207 lines
Quoted 207 lines
Quoted 106 lines
My idea was this: we have the device id in
kvm_assigned_msix_entryalready. Just put table id and entry number inkvm_irq_routing_entry(create a new gsi type for this). The result will also work for irqfd because these are mapped to gsi.Quoted 94 lines
And for the table and pba address, it's due to the mapping in userspace may know the guest MSI-X table address and PBA address at different
time(due to different BAR, refer to the code in assigned_dev_iomem_map() of qemu). So I purposed this API to allow each of them can be passed to kernel space individually.Quoted 55 lines
Quoted 13 lines
(quoted diff) +11
-0+struct kvm_msix_mmio_user { + __u32 dev_id; + __u16 type; + __u16 max_entries_nr; + __u64 base_addr; + __u64 base_va; + __u64 flags; + __u64 reserved[4]; +}; + +int kvm_assigned_device_update_msix_mask_bit(struct kvmQuoted 2 lines
*kvm, + int
assigned_dev_id, int entry, u32 flag)
+{Need a better name for 'flag' (and make it a bool).
Quoted 37 lines
(quoted diff) +27
-0+ int r = -EFAULT; + struct kvm_assigned_dev_kernel *adev; + int i; + + if (!irqchip_in_kernel(kvm)) + return r; + + mutex_lock(&kvm->lock); + adev = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head, + assigned_dev_id); + if (!adev) + goto out; + + for (i = 0; i< adev->entries_nr; i++) + if (adev->host_msix_entries[i].entry == entry) { + if (flag) + disable_irq_nosync( + adev->host_msix_entries[i].vector); + else + enable_irq(adev->host_msix_entries[i].vector); + r = 0; + break; + } +out: + mutex_unlock(&kvm->lock); + return r; +}@@ -1988,6 +2008,12 @@ static int kvm_dev_ioctl_create_vm(void) return r; } #endifQuoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ r = kvm_register_msix_mmio_dev(kvm); + if (r< 0) { + kvm_put_kvm(kvm); + return r; + }Shouldn't this be part of individual
KVM_REGISTER_MSIX_MMIOcalls?In fact this MMIO device is more like global one for the VM, not for every devices. It should handle all MMIO from all MSI-X enabled devices, so I put it in the VM init/destroy process.
Quoted 11 lines
Quoted 11 lines
+static int msix_table_mmio_read(struct
kvm_io_device*this,gpa_taddr, int len, + void *val)(quoted diff) +9
-0+{ + struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); + struct kvm_msix_mmio *mmio; + int idx, ret = 0, entry, offset, r; + + mutex_lock(&mmio_dev->lock); + idx = get_mmio_table_index(mmio_dev, addr, len); + if (idx< 0) {Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ ret = -EOPNOTSUPP; + goto out; + } + if ((addr& 0x3) || (len != 4&& len != 8)) + goto out;What about (addr & 4) && (len == 8)? Is it supported? It may cross entry boundaries.
Quoted 2 lines
Should not supported. But I haven't found words on the PCI spec for it. So I didn't add this check.
IMPLEMENTATION NOTE
MSI-X Memory Space Structures in Read/Write MemoryQuoted 6 lines
....
For all accesses to MSI-X Table and MSI-X PBA fields, software must use aligned full
DWORD or aligned full QWORD transactions; otherwise, the result is undefined.Yes, this one is enough, I would add the checking.
Quoted 92 lines
Quoted 92 lines
Quoted 92 lines
Quoted 37 lines
(quoted diff) +3
-0+ mmio =&mmio_dev->mmio[idx]; + + entry = (addr - mmio->table_base_addr) /
PCI_MSIX_ENTRY_SIZE; + offset = addr&0xf;(quoted diff) +10
-0+ r = copy_from_user(val, (void *)(mmio->table_base_va + + entry * PCI_MSIX_ENTRY_SIZE + offset), len); + if (r) + goto out; +out: + mutex_unlock(&mmio_dev->lock); + return ret; +} + +static int msix_table_mmio_write(struct kvm_io_device *this,
gpa_taddr, + int len, const void *val)(quoted diff) +18
-0+{ + struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); + struct kvm_msix_mmio *mmio; + int idx, entry, offset, ret = 0, r = 0; + gpa_t entry_base; + u32 old_ctrl, new_ctrl; + + mutex_lock(&mmio_dev->lock); + idx = get_mmio_table_index(mmio_dev, addr, len); + if (idx< 0) { + ret = -EOPNOTSUPP; + goto out; + } + if ((addr& 0x3) || (len != 4&& len != 8)) + goto out; + mmio =&mmio_dev->mmio[idx]; + entry = (addr - mmio->table_base_addr) /
PCI_MSIX_ENTRY_SIZE; +entry_base=mmio->table_base_va+
entry *PCI_MSIX_ENTRY_SIZE; + offset = addr&0xF;Review
Quoted 5 lines
(quoted diff) +5
-0+ + if (copy_from_user(&old_ctrl, + entry_base + PCI_MSIX_ENTRY_VECTOR_CTRL, + sizeof old_ctrl)) + goto out;
get_user()is easier.Quoted 4 lines
(quoted diff) +2
-0+ + /* No allow writing to other fields when entry is unmasked*/ + if (!(old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&&
(quoted diff) +1
-0+ offset != PCI_MSIX_ENTRY_VECTOR_CTRL)Review
Quoted 10 lines
(quoted diff) +9
-0+ goto out; + + if (copy_to_user(entry_base + offset, val, len)) + goto out; + + if (copy_from_user(&new_ctrl, + entry_base + PCI_MSIX_ENTRY_VECTOR_CTRL, + sizeof new_ctrl)) + goto out;
put_user()Quoted 8 lines
(quoted diff) +8
-0+ + if ((offset< PCI_MSIX_ENTRY_VECTOR_CTRL&& len == 4) || + (offset< PCI_MSIX_ENTRY_DATA&& len == 8)) + ret = -ENOTSYNC; + if (old_ctrl == new_ctrl) + goto out; + if (!(old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + (new_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT))Review
Quoted 10 lines
(quoted diff) +10
-0+ r = update_msix_mask_bit(mmio_dev->kvm, mmio, entry, 1); + else if ((old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + !(new_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)) + r = update_msix_mask_bit(mmio_dev->kvm, mmio, entry, 0); + if (r || ret) + ret = -ENOTSYNC; +out: + mutex_unlock(&mmio_dev->lock); + return ret; +}blank line...
Quoted 9 lines
+static const struct
kvm_io_device_opsmsix_mmio_table_ops=
{ + .read =msix_table_mmio_read,(quoted diff) +7
-0+ .write = msix_table_mmio_write, +}; + ++ +int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm, + struct kvm_msix_mmio_user *mmio_user) +{
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Review
Quoted 8 lines
(quoted diff) +8
-0+ struct kvm_msix_mmio_dev *mmio_dev =&kvm->msix_mmio_dev; + struct kvm_msix_mmio *mmio = NULL; + int r = 0, i; + + mutex_lock(&mmio_dev->lock); + for (i = 0; i< mmio_dev->mmio_nr; i++) { + if (mmio_dev->mmio[i].dev_id == mmio_user->dev_id&& + (mmio_dev->mmio[i].type&
KVM_MSIX_MMIO_TYPE_DEV_MASK)
Quoted 102 lines
Quoted 6 lines
==
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Review
Quoted 3 lines
(quoted diff) +3
-0+ (mmio_user->type& KVM_MSIX_MMIO_TYPE_DEV_MASK)) { + mmio =&mmio_dev->mmio[i]; + if (mmio->max_entries_nr != mmio_user-
max_entries_nr)Quoted 74 lines
{
Quoted 62 lines
Quoted 58 lines
Quoted 51 lines
Quoted 35 lines
Quoted 35 lines
Quoted 5 lines
(quoted diff) +5
-0+ r = -EINVAL; + goto out; + } + break; + }Review
Quoted 10 lines
(quoted diff) +10
-0+ } + if (!mmio) { + if (mmio_dev->mmio_nr == KVM_MSIX_MMIO_MAX) { + r = -ENOSPC; + goto out; + } + mmio =&mmio_dev->mmio[mmio_dev->mmio_nr]; + mmio_dev->mmio_nr++; + } + mmio->max_entries_nr = mmio_user->max_entries_nr;Sanity check to avoid overflow.
Review
Quoted 2 lines
(quoted diff) +2
-0+ mmio->dev_id = mmio_user->dev_id; + mmio->flags = mmio_user->flags;Check for unsupported bits (all of them at present?)
Review
Quoted 9 lines
(quoted diff) +9
-0+ if ((mmio_user->type& KVM_MSIX_MMIO_TYPE_DEV_MASK) == + KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV) + mmio->type = KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV; + + if ((mmio_user->type& KVM_MSIX_MMIO_TYPE_BASE_MASK) == + KVM_MSIX_MMIO_TYPE_BASE_TABLE) { + mmio->type |= KVM_MSIX_MMIO_TYPE_BASE_TABLE; + mmio->table_base_addr = mmio_user->base_addr; + mmio->table_base_va = mmio_user->base_va;Check for va in kernel space.
Quoted 15 lines
Quoted 7 lines
Quoted 7 lines
(quoted diff) +1
-0+ } else if ((mmio_user->type& KVM_MSIX_MMIO_TYPE_BASE_MASK)== +
KVM_MSIX_MMIO_TYPE_BASE_PBA) {(quoted diff) +5
-0+ mmio->type |= KVM_MSIX_MMIO_TYPE_BASE_PBA; + mmio->pba_base_addr = mmio_user->base_addr; + mmio->pba_base_va = mmio_user->base_va; + } +out:Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ mutex_unlock(&mmio_dev->lock); + return r; +} + +In all, looks reasonable. I'd like to see documentation for it, and review from the pci people. Alex, mst?
Quoted 6 lines
Some general comments:
PBA isn't supported in this version, which is OK, but let's not add a capability until it is, and let's not try to guess what the interface will look like. I think keeping PBA in userspace will be hard because it needs to be modified from interrupt context. Removing the PBA stub will make the interface simpler.Quoted 3 lines
The API only get the PBA address now which should be fine. And we still have threaded irq and tasklet for accessing the userspace for interrupt handler...
Quoted 8 lines
I don't think it's going to work: we are not
in the context of the right process. Further
I think we should keep the option of
reading the PBA status from the device or host kernel open. And generally having an interface
for functionality we don't implement is not a good idea:
you don't know whether you really can support the interface you promised.Quoted 8 lines
Well, I don't know if we want to read PBA from device directly. To me it's not a good idea because the real device has nothing to do with the one we show to the guest. At least direct accessing the mask bits of real device would be very dangerous. Avi?
--
regards
Yang, ShengI am not really suggesting this. What I say is PBA is unimplemented let us not commit to an interface yet.
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
--
regards
Yang, ShengQuoted 5 lines
Quoted 5 lines
Would add the API document soon.
--
regards
Yang, Sheng
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 87 lines · Sheng Yang
Quoted 86 lines · On Thursday 30 December 2010 15:47:48 Michael S. Tsirkin wrote:
Quoted 84 lines · Sheng Yang
Quoted 64 lines · On Wednesday 29 December 2010 17:28:24 Michael S. Tsirkin wrote:
Quoted 55 lines · Sheng Yang
Quoted 54 lines · On Wednesday 29 December 2010 16:31:35 Michael S. Tsirkin wrote:
Quoted 52 lines · Sheng Yang
Quoted 24 lines · On Tuesday 28 December 2010 20:26:13 Avi Kivity wrote:
Quoted 23 lines · Sheng Yang
Then we can support mask bit operation of assigned devices now.
(quoted diff) +2
-1@@ -3817,14 +3819,16 @@ static int emulator_write_emulated_onepage(unsigned long addr, mmio: trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, *(u64 *)val); + r = vcpu_mmio_write(vcpu, gpa, bytes, val); /* * Is this MMIO handled locally? */ - if (!vcpu_mmio_write(vcpu, gpa, bytes, val)) + if (!r) return X86EMUL_CONTINUE;Quoted 8 lines
Review
Quoted 5 lines
vcpu->mmio_needed = 1;(quoted diff) +2
-1- vcpu->run->exit_reason = KVM_EXIT_MMIO; + vcpu->run->exit_reason = (r == -ENOTSYNC) ? + KVM_EXIT_MSIX_ROUTING_UPDATE : KVM_EXIT_MMIO;This isn't very pretty,
exit_reasonshould be written invcpu_mmio_write(). I guess we can refactor it later.Sure.
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+#define KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV (1<< 0) + +#define KVM_MSIX_MMIO_TYPE_BASE_TABLE (1<< 8) +#define KVM_MSIX_MMIO_TYPE_BASE_PBA (1<< 9) + +#define KVM_MSIX_MMIO_TYPE_DEV_MASK 0x00ff +#define KVM_MSIX_MMIO_TYPE_BASE_MASK 0xff00Any explanation of these?
I chose to use assigned device id instead of one specific table id, because every device should got at most one MSI MMIO(the same should applied to vfio device as well), and if we use specific table ID, we need way to associate with the device anyway, to perform mask/unmask or other operation. So I think it's better to use device ID here directly.
Quoted 3 lines
Quoted 1 line
Table id will be needed to make things work for emulated devices.
I suppose even emulated device should got some kind of id(BDF)?
Not that I know. Look at how irqfd is defined for example, or how interrupts are sent through a gsi.
I would like to make the interface be able to support that.Quoted 8 lines
Quoted 5 lines
I think that is
enough for identification, which is already there, so we don't need to allocate another ID for the device - because one device would got at most one MSI-X MMIO, then use BDF or other device specific ID should be quite straightforward.So you propose allocating ids for emulated devices? OK. How will we map e.g. irqfds to these?
I don't understand. I've checked
virtio-pci.cwhich is using irqfd, and it's still a PCI device, and still have BDF, right?Also, what we want is a way to identify the MSI-X MMIO. For assigned device, we use BDF, then we can easily identify the MMIO as well as the device. For others, even they don't have
BDF(I don't think so, because MSI-X is a part of PCI, and every PCI device has BDF), what you need is an ID, no matter what it is and how it defined. QEmu can get the allocation done, and the type field in this API can still tell which kind of ID/devices they are, then determine how to deal with them.
Quoted 7 lines
Quoted 5 lines
Yes, the PCI device can be identified with e.g. BFD
(won't work for multi-domain but then we can write an allocator maybe). But how will we inject these interrupts?
We can do this now with GSI ioctl or map GSI to irqfd
and inject with irqfd write.I suppose it's not in the scope of this patch...
This is why I suggested mapping GSI to msix.
Quoted 3 lines
But I think you can still do
this, everything is the same as before. QEmu can read from table to get data/address pair, then program the routing table, etc.
Yes, fine, but mask is the problem :)
When qemu/irqfd injects an interrupt and it's masked, guest should not be interrupted.
Quoted 322 lines
--
regards
Yang, ShengQuoted 207 lines
Quoted 207 lines
Quoted 207 lines
Quoted 207 lines
Quoted 106 lines
My idea was this: we have the device id in
kvm_assigned_msix_entryalready. Just put table id and entry number inkvm_irq_routing_entry(create a new gsi type for this). The result will also work for irqfd because these are mapped to gsi.Quoted 94 lines
And for the table and pba address, it's due to the mapping in userspace may know the guest MSI-X table address and PBA address at different
time(due to different BAR, refer to the code in assigned_dev_iomem_map() of qemu). So I purposed this API to allow each of them can be passed to kernel space individually.Quoted 55 lines
Quoted 13 lines
(quoted diff) +11
-0+struct kvm_msix_mmio_user { + __u32 dev_id; + __u16 type; + __u16 max_entries_nr; + __u64 base_addr; + __u64 base_va; + __u64 flags; + __u64 reserved[4]; +}; + +int kvm_assigned_device_update_msix_mask_bit(struct kvmQuoted 2 lines
*kvm, + int
assigned_dev_id, int entry, u32 flag)
+{Need a better name for 'flag' (and make it a bool).
Quoted 37 lines
(quoted diff) +27
-0+ int r = -EFAULT; + struct kvm_assigned_dev_kernel *adev; + int i; + + if (!irqchip_in_kernel(kvm)) + return r; + + mutex_lock(&kvm->lock); + adev = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head, + assigned_dev_id); + if (!adev) + goto out; + + for (i = 0; i< adev->entries_nr; i++) + if (adev->host_msix_entries[i].entry == entry) { + if (flag) + disable_irq_nosync( + adev->host_msix_entries[i].vector); + else + enable_irq(adev->host_msix_entries[i].vector); + r = 0; + break; + } +out: + mutex_unlock(&kvm->lock); + return r; +}@@ -1988,6 +2008,12 @@ static int kvm_dev_ioctl_create_vm(void) return r; } #endifQuoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ r = kvm_register_msix_mmio_dev(kvm); + if (r< 0) { + kvm_put_kvm(kvm); + return r; + }Shouldn't this be part of individual
KVM_REGISTER_MSIX_MMIOcalls?In fact this MMIO device is more like global one for the VM, not for every devices. It should handle all MMIO from all MSI-X enabled devices, so I put it in the VM init/destroy process.
Quoted 11 lines
Quoted 11 lines
+static int msix_table_mmio_read(struct
kvm_io_device*this,gpa_taddr, int len, + void *val)(quoted diff) +9
-0+{ + struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); + struct kvm_msix_mmio *mmio; + int idx, ret = 0, entry, offset, r; + + mutex_lock(&mmio_dev->lock); + idx = get_mmio_table_index(mmio_dev, addr, len); + if (idx< 0) {Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ ret = -EOPNOTSUPP; + goto out; + } + if ((addr& 0x3) || (len != 4&& len != 8)) + goto out;What about (addr & 4) && (len == 8)? Is it supported? It may cross entry boundaries.
Quoted 2 lines
Should not supported. But I haven't found words on the PCI spec for it. So I didn't add this check.
IMPLEMENTATION NOTE
MSI-X Memory Space Structures in Read/Write MemoryQuoted 6 lines
....
For all accesses to MSI-X Table and MSI-X PBA fields, software must use aligned full
DWORD or aligned full QWORD transactions; otherwise, the result is undefined.Yes, this one is enough, I would add the checking.
Quoted 92 lines
Quoted 92 lines
Quoted 92 lines
Quoted 37 lines
(quoted diff) +3
-0+ mmio =&mmio_dev->mmio[idx]; + + entry = (addr - mmio->table_base_addr) /
PCI_MSIX_ENTRY_SIZE; + offset = addr&0xf;(quoted diff) +10
-0+ r = copy_from_user(val, (void *)(mmio->table_base_va + + entry * PCI_MSIX_ENTRY_SIZE + offset), len); + if (r) + goto out; +out: + mutex_unlock(&mmio_dev->lock); + return ret; +} + +static int msix_table_mmio_write(struct kvm_io_device *this,
gpa_taddr, + int len, const void *val)(quoted diff) +18
-0+{ + struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev, table_dev); + struct kvm_msix_mmio *mmio; + int idx, entry, offset, ret = 0, r = 0; + gpa_t entry_base; + u32 old_ctrl, new_ctrl; + + mutex_lock(&mmio_dev->lock); + idx = get_mmio_table_index(mmio_dev, addr, len); + if (idx< 0) { + ret = -EOPNOTSUPP; + goto out; + } + if ((addr& 0x3) || (len != 4&& len != 8)) + goto out; + mmio =&mmio_dev->mmio[idx]; + entry = (addr - mmio->table_base_addr) /
PCI_MSIX_ENTRY_SIZE; +entry_base=mmio->table_base_va+
entry *PCI_MSIX_ENTRY_SIZE; + offset = addr&0xF;Review
Quoted 5 lines
(quoted diff) +5
-0+ + if (copy_from_user(&old_ctrl, + entry_base + PCI_MSIX_ENTRY_VECTOR_CTRL, + sizeof old_ctrl)) + goto out;
get_user()is easier.Quoted 4 lines
(quoted diff) +2
-0+ + /* No allow writing to other fields when entry is unmasked*/ + if (!(old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&&
(quoted diff) +1
-0+ offset != PCI_MSIX_ENTRY_VECTOR_CTRL)Review
Quoted 10 lines
(quoted diff) +9
-0+ goto out; + + if (copy_to_user(entry_base + offset, val, len)) + goto out; + + if (copy_from_user(&new_ctrl, + entry_base + PCI_MSIX_ENTRY_VECTOR_CTRL, + sizeof new_ctrl)) + goto out;
put_user()Quoted 8 lines
(quoted diff) +8
-0+ + if ((offset< PCI_MSIX_ENTRY_VECTOR_CTRL&& len == 4) || + (offset< PCI_MSIX_ENTRY_DATA&& len == 8)) + ret = -ENOTSYNC; + if (old_ctrl == new_ctrl) + goto out; + if (!(old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + (new_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT))Review
Quoted 10 lines
(quoted diff) +10
-0+ r = update_msix_mask_bit(mmio_dev->kvm, mmio, entry, 1); + else if ((old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + !(new_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)) + r = update_msix_mask_bit(mmio_dev->kvm, mmio, entry, 0); + if (r || ret) + ret = -ENOTSYNC; +out: + mutex_unlock(&mmio_dev->lock); + return ret; +}blank line...
Quoted 9 lines
+static const struct
kvm_io_device_opsmsix_mmio_table_ops=
{ + .read =msix_table_mmio_read,(quoted diff) +7
-0+ .write = msix_table_mmio_write, +}; + ++ +int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm, + struct kvm_msix_mmio_user *mmio_user) +{Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Review
Quoted 8 lines
(quoted diff) +8
-0+ struct kvm_msix_mmio_dev *mmio_dev =&kvm->msix_mmio_dev; + struct kvm_msix_mmio *mmio = NULL; + int r = 0, i; + + mutex_lock(&mmio_dev->lock); + for (i = 0; i< mmio_dev->mmio_nr; i++) { + if (mmio_dev->mmio[i].dev_id == mmio_user->dev_id&& + (mmio_dev->mmio[i].type&
KVM_MSIX_MMIO_TYPE_DEV_MASK)Quoted 102 lines
Quoted 6 lines
==
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Review
Quoted 3 lines
(quoted diff) +3
-0+ (mmio_user->type& KVM_MSIX_MMIO_TYPE_DEV_MASK)) { + mmio =&mmio_dev->mmio[i]; + if (mmio->max_entries_nr != mmio_user-
max_entries_nr)Quoted 74 lines
{
Quoted 62 lines
Quoted 58 lines
Quoted 51 lines
Quoted 35 lines
Quoted 35 lines
Quoted 5 lines
(quoted diff) +5
-0+ r = -EINVAL; + goto out; + } + break; + }Review
Quoted 10 lines
(quoted diff) +10
-0+ } + if (!mmio) { + if (mmio_dev->mmio_nr == KVM_MSIX_MMIO_MAX) { + r = -ENOSPC; + goto out; + } + mmio =&mmio_dev->mmio[mmio_dev->mmio_nr]; + mmio_dev->mmio_nr++; + } + mmio->max_entries_nr = mmio_user->max_entries_nr;Sanity check to avoid overflow.
Review
Quoted 2 lines
(quoted diff) +2
-0+ mmio->dev_id = mmio_user->dev_id; + mmio->flags = mmio_user->flags;Check for unsupported bits (all of them at present?)
Review
Quoted 9 lines
(quoted diff) +9
-0+ if ((mmio_user->type& KVM_MSIX_MMIO_TYPE_DEV_MASK) == + KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV) + mmio->type = KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV; + + if ((mmio_user->type& KVM_MSIX_MMIO_TYPE_BASE_MASK) == + KVM_MSIX_MMIO_TYPE_BASE_TABLE) { + mmio->type |= KVM_MSIX_MMIO_TYPE_BASE_TABLE; + mmio->table_base_addr = mmio_user->base_addr; + mmio->table_base_va = mmio_user->base_va;Check for va in kernel space.
Quoted 15 lines
Quoted 7 lines
Quoted 7 lines
(quoted diff) +1
-0+ } else if ((mmio_user->type& KVM_MSIX_MMIO_TYPE_BASE_MASK)== +
KVM_MSIX_MMIO_TYPE_BASE_PBA) {(quoted diff) +5
-0+ mmio->type |= KVM_MSIX_MMIO_TYPE_BASE_PBA; + mmio->pba_base_addr = mmio_user->base_addr; + mmio->pba_base_va = mmio_user->base_va; + } +out:Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ mutex_unlock(&mmio_dev->lock); + return r; +} + +In all, looks reasonable. I'd like to see documentation for it, and review from the pci people. Alex, mst?
Quoted 6 lines
Some general comments:
PBA isn't supported in this version, which is OK, but let's not add a capability until it is, and let's not try to guess what the interface will look like. I think keeping PBA in userspace will be hard because it needs to be modified from interrupt context. Removing the PBA stub will make the interface simpler.Quoted 3 lines
The API only get the PBA address now which should be fine. And we still have threaded irq and tasklet for accessing the userspace for interrupt handler...
Quoted 8 lines
I don't think it's going to work: we are not
in the context of the right process. Further
I think we should keep the option of
reading the PBA status from the device or host kernel open. And generally having an interface
for functionality we don't implement is not a good idea:
you don't know whether you really can support the interface you promised.Quoted 8 lines
Well, I don't know if we want to read PBA from device directly. To me it's not a good idea because the real device has nothing to do with the one we show to the guest. At least direct accessing the mask bits of real device would be very dangerous. Avi?
--
regards
Yang, ShengI am not really suggesting this. What I say is PBA is unimplemented let us not commit to an interface yet.
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
--
regards
Yang, ShengQuoted 5 lines
Quoted 5 lines
Would add the API document soon.
--
regards
Yang, Sheng
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 100 lines · On Thursday 30 December 2010 16:15:32 Michael S. Tsirkin wrote:
Quoted 89 lines · Sheng Yang
Quoted 88 lines · On Thursday 30 December 2010 15:47:48 Michael S. Tsirkin wrote:
Quoted 86 lines · Sheng Yang
Quoted 65 lines · On Wednesday 29 December 2010 17:28:24 Michael S. Tsirkin wrote:
Quoted 55 lines · Sheng Yang
Quoted 54 lines · On Wednesday 29 December 2010 16:31:35 Michael S. Tsirkin wrote:
Quoted 52 lines · Sheng Yang
Quoted 24 lines · On Tuesday 28 December 2010 20:26:13 Avi Kivity wrote:
Quoted 23 lines · Sheng Yang
Then we can support mask bit operation of assigned devices now.
(quoted diff) +2
-1@@ -3817,14 +3819,16 @@ static int emulator_write_emulated_onepage(unsigned long addr, mmio: trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, *(u64 *)val); + r = vcpu_mmio_write(vcpu, gpa, bytes, val); /* * Is this MMIO handled locally? */ - if (!vcpu_mmio_write(vcpu, gpa, bytes, val)) + if (!r) return X86EMUL_CONTINUE;Quoted 8 lines
Review
Quoted 5 lines
vcpu->mmio_needed = 1;(quoted diff) +2
-1- vcpu->run->exit_reason = KVM_EXIT_MMIO; + vcpu->run->exit_reason = (r == -ENOTSYNC) ? + KVM_EXIT_MSIX_ROUTING_UPDATE : KVM_EXIT_MMIO;This isn't very pretty,
exit_reasonshould be written invcpu_mmio_write(). I guess we can refactor it later.Sure.
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+#define KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV (1<< 0) + +#define KVM_MSIX_MMIO_TYPE_BASE_TABLE (1<< 8) +#define KVM_MSIX_MMIO_TYPE_BASE_PBA (1<< 9) + +#define KVM_MSIX_MMIO_TYPE_DEV_MASK 0x00ff +#define KVM_MSIX_MMIO_TYPE_BASE_MASK 0xff00Any explanation of these?
I chose to use assigned device id instead of one specific table id, because every device should got at most one MSI MMIO(the same should applied to vfio device as well), and if we use specific table ID, we need way to associate with the device anyway, to perform mask/unmask or other operation. So I think it's better to use device ID here directly.
Quoted 4 lines
Quoted 2 lines
Table id will be needed to make things work for emulated devices.
I suppose even emulated device should got some kind of id(BDF)?
Not that I know. Look at how irqfd is defined for example, or how interrupts are sent through a gsi.
I would like to make the interface be able to support that.Quoted 8 lines
Quoted 5 lines
I think that is
enough for identification, which is already there, so we don't need to allocate another ID for the device - because one device would got at most one MSI-X MMIO, then use BDF or other device specific ID should be quite straightforward.So you propose allocating ids for emulated devices? OK. How will we map e.g. irqfds to these?
I don't understand. I've checked
virtio-pci.cwhich is using irqfd, and it's still a PCI device, and still have BDF, right?Also, what we want is a way to identify the MSI-X MMIO. For assigned device, we use BDF, then we can easily identify the MMIO as well as the device. For others, even they don't have
BDF(I don't think so, because MSI-X is a part of PCI, and every PCI device has BDF), what you need is an ID, no matter what it is and how it defined. QEmu can get the allocation done, and the type field in this API can still tell which kind of ID/devices they are, then determine how to deal with them.Quoted 7 lines
Quoted 5 lines
Yes, the PCI device can be identified with e.g. BFD
(won't work for multi-domain but then we can write an allocator maybe). But how will we inject these interrupts?
We can do this now with GSI ioctl or map GSI to irqfd
and inject with irqfd write.I suppose it's not in the scope of this patch...
This is why I suggested mapping GSI to msix.
Quoted 7 lines
Quoted 3 lines
But I think you can still do
this, everything is the same as before. QEmu can read from table to get data/address pair, then program the routing table, etc.Yes, fine, but mask is the problem :)
When qemu/irqfd injects an interrupt and it's masked, guest should not be interrupted.
I think this should be done by other APIs(to map GSI with MSI-X entry). And of course you can introduce one GSI type which require one ID and one entry number to eject later, but it's not in the scope of this patch.
--
regards
Yang, Sheng
Quoted 31 lines
Quoted 31 lines
--
regards
Yang, ShengQuoted 26 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
My idea was this: we have the device id in
kvm_assigned_msix_entryalready. Just put table id and entry number inkvm_irq_routing_entry(create a new gsi type for this). The result will also work for irqfd because these are mapped to gsi.Quoted 19 lines
And for the table and pba address, it's due to the mapping in userspace may know the guest MSI-X table address and PBA address at different
time(due to different BAR, refer to the code in assigned_dev_iomem_map() of qemu). So I purposed this API to allow each of them can be passed to kernel space individually.Quoted 12 lines
Quoted 12 lines
(quoted diff) +10
-0+struct kvm_msix_mmio_user { + __u32 dev_id; + __u16 type; + __u16 max_entries_nr; + __u64 base_addr; + __u64 base_va; + __u64 flags; + __u64 reserved[4]; +}; +
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
+int kvm_assigned_device_update_msix_mask_bit(struct kvm
*kvm, + intassigned_dev_id, int entry, u32
flag)
Quoted 155 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 1 line
+{
Need a better name for 'flag' (and make it a bool).
Quoted 13 lines
(quoted diff) +9
-0+ int r = -EFAULT; + struct kvm_assigned_dev_kernel *adev; + int i; + + if (!irqchip_in_kernel(kvm)) + return r; + + mutex_lock(&kvm->lock); + adev =kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
(quoted diff) +3
-0+ assigned_dev_id); + if (!adev) + goto out;Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Review
Quoted 8 lines
(quoted diff) +8
-0+ + for (i = 0; i< adev->entries_nr; i++) + if (adev->host_msix_entries[i].entry == entry) { + if (flag) + disable_irq_nosync( + adev->host_msix_entries[i].vector); + else + enable_irq(adev-host_msix_entries[i].vector);
Quoted 129 lines
Quoted 129 lines
Quoted 129 lines
Quoted 129 lines
Quoted 129 lines
Quoted 57 lines
Quoted 51 lines
Quoted 17 lines
Quoted 17 lines
(quoted diff) +7
-0+ r = 0; + break; + } +out: + mutex_unlock(&kvm->lock); + return r; +}@@ -1988,6 +2008,12 @@ static int kvm_dev_ioctl_create_vm(void) return r; } #endifQuoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ r = kvm_register_msix_mmio_dev(kvm); + if (r< 0) { + kvm_put_kvm(kvm); + return r; + }Shouldn't this be part of individual
KVM_REGISTER_MSIX_MMIOcalls?In fact this MMIO device is more like global one for the VM, not for every devices. It should handle all MMIO from all MSI-X enabled devices, so I put it in the VM init/destroy process.
Quoted 11 lines
Quoted 11 lines
+static int msix_table_mmio_read(struct
kvm_io_device
*this,gpa_taddr, int len, + void *val)(quoted diff) +3
-0+{ + struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev,
table_dev); + structkvm_msix_mmio*mmio;(quoted diff) +5
-0+ int idx, ret = 0, entry, offset, r; + + mutex_lock(&mmio_dev->lock); + idx = get_mmio_table_index(mmio_dev, addr, len); + if (idx< 0) {Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ ret = -EOPNOTSUPP; + goto out; + } + if ((addr& 0x3) || (len != 4&& len != 8)) + goto out;What about (addr & 4) && (len == 8)? Is it supported? It may cross entry boundaries.
Quoted 2 lines
Should not supported. But I haven't found words on the PCI spec for it. So I didn't add this check.
IMPLEMENTATION NOTE
MSI-X Memory Space Structures in Read/Write MemoryQuoted 6 lines
....
For all accesses to MSI-X Table and MSI-X PBA fields, software must use aligned full
DWORD or aligned full QWORD transactions; otherwise, the result is undefined.Yes, this one is enough, I would add the checking.
Quoted 63 lines
Quoted 63 lines
Quoted 63 lines
Quoted 37 lines
(quoted diff) +3
-0+ mmio =&mmio_dev->mmio[idx]; + + entry = (addr - mmio->table_base_addr) /
PCI_MSIX_ENTRY_SIZE; + offset = addr&0xf;(quoted diff) +10
-0+ r = copy_from_user(val, (void *)(mmio->table_base_va + + entry * PCI_MSIX_ENTRY_SIZE + offset), len); + if (r) + goto out; +out: + mutex_unlock(&mmio_dev->lock); + return ret; +} + +static int msix_table_mmio_write(struct kvm_io_device*this,
gpa_taddr, + int len, const void *val)(quoted diff) +3
-0+{ + struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev,
table_dev); + structkvm_msix_mmio*mmio;(quoted diff) +14
-0+ int idx, entry, offset, ret = 0, r = 0; + gpa_t entry_base; + u32 old_ctrl, new_ctrl; + + mutex_lock(&mmio_dev->lock); + idx = get_mmio_table_index(mmio_dev, addr, len); + if (idx< 0) { + ret = -EOPNOTSUPP; + goto out; + } + if ((addr& 0x3) || (len != 4&& len != 8)) + goto out; + mmio =&mmio_dev->mmio[idx]; + entry = (addr - mmio->table_base_addr) /
PCI_MSIX_ENTRY_SIZE; +entry_base=mmio->table_base_va+
entry *PCI_MSIX_ENTRY_SIZE; + offset = addr&0xF;Review
Quoted 5 lines
(quoted diff) +5
-0+ + if (copy_from_user(&old_ctrl, + entry_base + PCI_MSIX_ENTRY_VECTOR_CTRL, + sizeof old_ctrl)) + goto out;
get_user()is easier.Quoted 5 lines
(quoted diff) +2
-0+ + /* No allow writing to other fields when entry isunmasked */ + if (!(old_ctrl&
PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + offset !=PCI_MSIX_ENTRY_VECTOR_CTRL)Review
Quoted 10 lines
(quoted diff) +9
-0+ goto out; + + if (copy_to_user(entry_base + offset, val, len)) + goto out; + + if (copy_from_user(&new_ctrl, + entry_base + PCI_MSIX_ENTRY_VECTOR_CTRL, + sizeof new_ctrl)) + goto out;
put_user()
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Review
Quoted 9 lines
(quoted diff) +2
-0+ + if ((offset< PCI_MSIX_ENTRY_VECTOR_CTRL&& len == 4)|| + (offset< PCI_MSIX_ENTRY_DATA&& len == 8))
(quoted diff) +6
-0+ ret = -ENOTSYNC; + if (old_ctrl == new_ctrl) + goto out; + if (!(old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + (new_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)) + r = update_msix_mask_bit(mmio_dev->kvm, mmio,
entry,
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Review
Quoted 4 lines
1); + else if ((old_ctrl&
PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + !(new_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT))(quoted diff) +1
-0+ r = update_msix_mask_bit(mmio_dev->kvm, mmio,
entry,
Quoted 31 lines
Quoted 31 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Review
Quoted 6 lines
0); + if (r || ret)
(quoted diff) +5
-0+ ret = -ENOTSYNC; +out: + mutex_unlock(&mmio_dev->lock); + return ret; +}blank line...
Quoted 11 lines
+static const struct
kvm_io_device_opsmsix_mmio_table_ops= { + .read =msix_table_mmio_read,(quoted diff) +8
-0+ .write = msix_table_mmio_write, +}; + ++ +int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm, + struct kvm_msix_mmio_user *mmio_user) +{ + struct kvm_msix_mmio_dev *mmio_devQuoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Review
Quoted 8 lines
=&kvm->msix_mmio_dev; + struct
kvm_msix_mmio*mmio =
NULL;(quoted diff) +6
-0+ int r = 0, i; + + mutex_lock(&mmio_dev->lock); + for (i = 0; i< mmio_dev->mmio_nr; i++) { + if (mmio_dev->mmio[i].dev_id == mmio_user->dev_id&& + (mmio_dev->mmio[i].type&
KVM_MSIX_MMIO_TYPE_DEV_MASK)
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
==
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Review
Quoted 1 line
(quoted diff) +1
-0+ (mmio_user->type&
KVM_MSIX_MMIO_TYPE_DEV_MASK)) {
Quoted 101 lines
Quoted 101 lines
Quoted 101 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ mmio =&mmio_dev->mmio[i]; + if (mmio->max_entries_nr != mmio_user-
max_entries_nr)Quoted 75 lines
{
Quoted 64 lines
Quoted 60 lines
Quoted 52 lines
Quoted 35 lines
Quoted 35 lines
Quoted 5 lines
(quoted diff) +5
-0+ r = -EINVAL; + goto out; + } + break; + }Review
Quoted 10 lines
(quoted diff) +10
-0+ } + if (!mmio) { + if (mmio_dev->mmio_nr == KVM_MSIX_MMIO_MAX) { + r = -ENOSPC; + goto out; + } + mmio =&mmio_dev->mmio[mmio_dev->mmio_nr]; + mmio_dev->mmio_nr++; + } + mmio->max_entries_nr = mmio_user->max_entries_nr;Sanity check to avoid overflow.
Review
Quoted 2 lines
(quoted diff) +2
-0+ mmio->dev_id = mmio_user->dev_id; + mmio->flags = mmio_user->flags;Check for unsupported bits (all of them at present?)
Review
Quoted 9 lines
(quoted diff) +9
-0+ if ((mmio_user->type& KVM_MSIX_MMIO_TYPE_DEV_MASK) == + KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV) + mmio->type = KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV; + + if ((mmio_user->type& KVM_MSIX_MMIO_TYPE_BASE_MASK) == + KVM_MSIX_MMIO_TYPE_BASE_TABLE) { + mmio->type |= KVM_MSIX_MMIO_TYPE_BASE_TABLE; + mmio->table_base_addr = mmio_user->base_addr; + mmio->table_base_va = mmio_user->base_va;Check for va in kernel space.
Quoted 16 lines
Quoted 8 lines
Quoted 8 lines
(quoted diff) +1
-0+ } else if ((mmio_user->type&
KVM_MSIX_MMIO_TYPE_BASE_MASK) ==(quoted diff) +6
-0+ KVM_MSIX_MMIO_TYPE_BASE_PBA) { + mmio->type |= KVM_MSIX_MMIO_TYPE_BASE_PBA; + mmio->pba_base_addr = mmio_user->base_addr; + mmio->pba_base_va = mmio_user->base_va; + } +out:Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ mutex_unlock(&mmio_dev->lock); + return r; +} + +In all, looks reasonable. I'd like to see documentation for it, and review from the pci people. Alex, mst?
Quoted 7 lines
Some general comments:
PBA isn't supported in this version, which is OK, but let's not add a capability until it is, and let's not try to guess what the interface will look like. I think keeping PBA in userspace will be hard because it needs to be modified from interrupt context. Removing the PBA stub will make the interface simpler.Quoted 3 lines
The API only get the PBA address now which should be fine. And we still have threaded irq and tasklet for accessing the userspace for interrupt handler...
Quoted 8 lines
I don't think it's going to work: we are not
in the context of the right process. Further
I think we should keep the option of
reading the PBA status from the device or host kernel open. And generally having an interface
for functionality we don't implement is not a good idea:
you don't know whether you really can support the interface you promised.Quoted 8 lines
Well, I don't know if we want to read PBA from device directly. To me it's not a good idea because the real device has nothing to do with the one we show to the guest. At least direct accessing the mask bits of real device would be very dangerous. Avi?
--
regards
Yang, ShengI am not really suggesting this. What I say is PBA is unimplemented let us not commit to an interface yet.
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
--
regards
Yang, ShengQuoted 5 lines
Quoted 5 lines
Would add the API document soon.
--
regards
Yang, Sheng
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 105 lines · Sheng Yang
Quoted 104 lines · On Thursday 30 December 2010 16:15:32 Michael S. Tsirkin wrote:
Quoted 89 lines · Sheng Yang
Quoted 88 lines · On Thursday 30 December 2010 15:47:48 Michael S. Tsirkin wrote:
Quoted 86 lines · Sheng Yang
Quoted 65 lines · On Wednesday 29 December 2010 17:28:24 Michael S. Tsirkin wrote:
Quoted 55 lines · Sheng Yang
Quoted 54 lines · On Wednesday 29 December 2010 16:31:35 Michael S. Tsirkin wrote:
Quoted 52 lines · Sheng Yang
Quoted 24 lines · On Tuesday 28 December 2010 20:26:13 Avi Kivity wrote:
Quoted 23 lines · Sheng Yang
Then we can support mask bit operation of assigned devices now.
(quoted diff) +2
-1@@ -3817,14 +3819,16 @@ static int emulator_write_emulated_onepage(unsigned long addr, mmio: trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, *(u64 *)val); + r = vcpu_mmio_write(vcpu, gpa, bytes, val); /* * Is this MMIO handled locally? */ - if (!vcpu_mmio_write(vcpu, gpa, bytes, val)) + if (!r) return X86EMUL_CONTINUE;Quoted 8 lines
Review
Quoted 5 lines
vcpu->mmio_needed = 1;(quoted diff) +2
-1- vcpu->run->exit_reason = KVM_EXIT_MMIO; + vcpu->run->exit_reason = (r == -ENOTSYNC) ? + KVM_EXIT_MSIX_ROUTING_UPDATE : KVM_EXIT_MMIO;This isn't very pretty,
exit_reasonshould be written invcpu_mmio_write(). I guess we can refactor it later.Sure.
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+#define KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV (1<< 0) + +#define KVM_MSIX_MMIO_TYPE_BASE_TABLE (1<< 8) +#define KVM_MSIX_MMIO_TYPE_BASE_PBA (1<< 9) + +#define KVM_MSIX_MMIO_TYPE_DEV_MASK 0x00ff +#define KVM_MSIX_MMIO_TYPE_BASE_MASK 0xff00Any explanation of these?
I chose to use assigned device id instead of one specific table id, because every device should got at most one MSI MMIO(the same should applied to vfio device as well), and if we use specific table ID, we need way to associate with the device anyway, to perform mask/unmask or other operation. So I think it's better to use device ID here directly.
Quoted 4 lines
Quoted 2 lines
Table id will be needed to make things work for emulated devices.
I suppose even emulated device should got some kind of id(BDF)?
Not that I know. Look at how irqfd is defined for example, or how interrupts are sent through a gsi.
I would like to make the interface be able to support that.Quoted 8 lines
Quoted 5 lines
I think that is
enough for identification, which is already there, so we don't need to allocate another ID for the device - because one device would got at most one MSI-X MMIO, then use BDF or other device specific ID should be quite straightforward.So you propose allocating ids for emulated devices? OK. How will we map e.g. irqfds to these?
I don't understand. I've checked
virtio-pci.cwhich is using irqfd, and it's still a PCI device, and still have BDF, right?Also, what we want is a way to identify the MSI-X MMIO. For assigned device, we use BDF, then we can easily identify the MMIO as well as the device. For others, even they don't have
BDF(I don't think so, because MSI-X is a part of PCI, and every PCI device has BDF), what you need is an ID, no matter what it is and how it defined. QEmu can get the allocation done, and the type field in this API can still tell which kind of ID/devices they are, then determine how to deal with them.Quoted 7 lines
Quoted 5 lines
Yes, the PCI device can be identified with e.g. BFD
(won't work for multi-domain but then we can write an allocator maybe). But how will we inject these interrupts?
We can do this now with GSI ioctl or map GSI to irqfd
and inject with irqfd write.I suppose it's not in the scope of this patch...
This is why I suggested mapping GSI to msix.
Quoted 3 lines
But I think you can still do
this, everything is the same as before. QEmu can read from table to get data/address pair, then program the routing table, etc.
Quoted 7 lines
Quoted 3 lines
Yes, fine, but mask is the problem :)
When qemu/irqfd injects an interrupt and it's masked, guest should not be interrupted.I think this should be done by other APIs(to map GSI with MSI-X entry). And of course you can introduce one GSI type which require one ID and one entry number to eject later, but it's not in the scope of this patch.
What you propose is adding an API to map GSI to a table ID + entry number? Like this?
1. map table ID to address+length
2. map GSI to table ID + entry #
But if we have such an API we don't need anything else as there's already an API to map assigned device interrupts to GSIs?
Quoted 344 lines
--
regards
Yang, ShengQuoted 31 lines
Quoted 31 lines
--
regards
Yang, ShengQuoted 26 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
My idea was this: we have the device id in
kvm_assigned_msix_entryalready. Just put table id and entry number inkvm_irq_routing_entry(create a new gsi type for this). The result will also work for irqfd because these are mapped to gsi.Quoted 19 lines
And for the table and pba address, it's due to the mapping in userspace may know the guest MSI-X table address and PBA address at different
time(due to different BAR, refer to the code in assigned_dev_iomem_map() of qemu). So I purposed this API to allow each of them can be passed to kernel space individually.Quoted 12 lines
Quoted 12 lines
(quoted diff) +10
-0+struct kvm_msix_mmio_user { + __u32 dev_id; + __u16 type; + __u16 max_entries_nr; + __u64 base_addr; + __u64 base_va; + __u64 flags; + __u64 reserved[4]; +}; +Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
+int kvm_assigned_device_update_msix_mask_bit(struct kvm
*kvm, + intassigned_dev_id, int entry, u32flag)
Quoted 155 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 1 line
+{
Need a better name for 'flag' (and make it a bool).
Quoted 13 lines
(quoted diff) +9
-0+ int r = -EFAULT; + struct kvm_assigned_dev_kernel *adev; + int i; + + if (!irqchip_in_kernel(kvm)) + return r; + + mutex_lock(&kvm->lock); + adev =kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
(quoted diff) +3
-0+ assigned_dev_id); + if (!adev) + goto out;Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Review
Quoted 8 lines
(quoted diff) +8
-0+ + for (i = 0; i< adev->entries_nr; i++) + if (adev->host_msix_entries[i].entry == entry) { + if (flag) + disable_irq_nosync( + adev->host_msix_entries[i].vector); + else + enable_irq(adev-host_msix_entries[i].vector);
Quoted 129 lines
Quoted 129 lines
Quoted 129 lines
Quoted 129 lines
Quoted 129 lines
Quoted 57 lines
Quoted 51 lines
Quoted 17 lines
Quoted 17 lines
(quoted diff) +7
-0+ r = 0; + break; + } +out: + mutex_unlock(&kvm->lock); + return r; +}@@ -1988,6 +2008,12 @@ static int kvm_dev_ioctl_create_vm(void) return r; } #endifQuoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ r = kvm_register_msix_mmio_dev(kvm); + if (r< 0) { + kvm_put_kvm(kvm); + return r; + }Shouldn't this be part of individual
KVM_REGISTER_MSIX_MMIOcalls?In fact this MMIO device is more like global one for the VM, not for every devices. It should handle all MMIO from all MSI-X enabled devices, so I put it in the VM init/destroy process.
Quoted 11 lines
Quoted 11 lines
+static int msix_table_mmio_read(struct
kvm_io_device
*this,gpa_taddr, int len, + void *val)(quoted diff) +3
-0+{ + struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev,
table_dev); + structkvm_msix_mmio*mmio;(quoted diff) +5
-0+ int idx, ret = 0, entry, offset, r; + + mutex_lock(&mmio_dev->lock); + idx = get_mmio_table_index(mmio_dev, addr, len); + if (idx< 0) {Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ ret = -EOPNOTSUPP; + goto out; + } + if ((addr& 0x3) || (len != 4&& len != 8)) + goto out;What about (addr & 4) && (len == 8)? Is it supported? It may cross entry boundaries.
Quoted 2 lines
Should not supported. But I haven't found words on the PCI spec for it. So I didn't add this check.
IMPLEMENTATION NOTE
MSI-X Memory Space Structures in Read/Write MemoryQuoted 6 lines
....
For all accesses to MSI-X Table and MSI-X PBA fields, software must use aligned full
DWORD or aligned full QWORD transactions; otherwise, the result is undefined.Yes, this one is enough, I would add the checking.
Quoted 63 lines
Quoted 63 lines
Quoted 63 lines
Quoted 37 lines
(quoted diff) +3
-0+ mmio =&mmio_dev->mmio[idx]; + + entry = (addr - mmio->table_base_addr) /
PCI_MSIX_ENTRY_SIZE; + offset = addr&0xf;(quoted diff) +10
-0+ r = copy_from_user(val, (void *)(mmio->table_base_va + + entry * PCI_MSIX_ENTRY_SIZE + offset), len); + if (r) + goto out; +out: + mutex_unlock(&mmio_dev->lock); + return ret; +} + +static int msix_table_mmio_write(struct kvm_io_device*this,
gpa_taddr, + int len, const void *val)(quoted diff) +3
-0+{ + struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev,
table_dev); + structkvm_msix_mmio*mmio;(quoted diff) +14
-0+ int idx, entry, offset, ret = 0, r = 0; + gpa_t entry_base; + u32 old_ctrl, new_ctrl; + + mutex_lock(&mmio_dev->lock); + idx = get_mmio_table_index(mmio_dev, addr, len); + if (idx< 0) { + ret = -EOPNOTSUPP; + goto out; + } + if ((addr& 0x3) || (len != 4&& len != 8)) + goto out; + mmio =&mmio_dev->mmio[idx]; + entry = (addr - mmio->table_base_addr) /
PCI_MSIX_ENTRY_SIZE; +entry_base=mmio->table_base_va+
entry *PCI_MSIX_ENTRY_SIZE; + offset = addr&0xF;Review
Quoted 5 lines
(quoted diff) +5
-0+ + if (copy_from_user(&old_ctrl, + entry_base + PCI_MSIX_ENTRY_VECTOR_CTRL, + sizeof old_ctrl)) + goto out;
get_user()is easier.Quoted 5 lines
(quoted diff) +2
-0+ + /* No allow writing to other fields when entry isunmasked */ + if (!(old_ctrl&
PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + offset !=PCI_MSIX_ENTRY_VECTOR_CTRL)Review
Quoted 10 lines
(quoted diff) +9
-0+ goto out; + + if (copy_to_user(entry_base + offset, val, len)) + goto out; + + if (copy_from_user(&new_ctrl, + entry_base + PCI_MSIX_ENTRY_VECTOR_CTRL, + sizeof new_ctrl)) + goto out;
put_user()Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Review
Quoted 9 lines
(quoted diff) +2
-0+ + if ((offset< PCI_MSIX_ENTRY_VECTOR_CTRL&& len == 4)|| + (offset< PCI_MSIX_ENTRY_DATA&& len == 8))
(quoted diff) +6
-0+ ret = -ENOTSYNC; + if (old_ctrl == new_ctrl) + goto out; + if (!(old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + (new_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)) + r = update_msix_mask_bit(mmio_dev->kvm, mmio,entry,
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Review
Quoted 4 lines
1); + else if ((old_ctrl&
PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + !(new_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT))(quoted diff) +1
-0+ r = update_msix_mask_bit(mmio_dev->kvm, mmio,entry,
Quoted 31 lines
Quoted 31 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Review
Quoted 6 lines
0); + if (r || ret)
(quoted diff) +5
-0+ ret = -ENOTSYNC; +out: + mutex_unlock(&mmio_dev->lock); + return ret; +}blank line...
Quoted 11 lines
+static const struct
kvm_io_device_opsmsix_mmio_table_ops= { + .read =msix_table_mmio_read,(quoted diff) +8
-0+ .write = msix_table_mmio_write, +}; + ++ +int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm, + struct kvm_msix_mmio_user *mmio_user) +{ + struct kvm_msix_mmio_dev *mmio_devQuoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Review
Quoted 8 lines
=&kvm->msix_mmio_dev; + struct
kvm_msix_mmio*mmio =
NULL;(quoted diff) +6
-0+ int r = 0, i; + + mutex_lock(&mmio_dev->lock); + for (i = 0; i< mmio_dev->mmio_nr; i++) { + if (mmio_dev->mmio[i].dev_id == mmio_user->dev_id&& + (mmio_dev->mmio[i].type&
KVM_MSIX_MMIO_TYPE_DEV_MASK)Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
==
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Review
Quoted 1 line
(quoted diff) +1
-0+ (mmio_user->type&
KVM_MSIX_MMIO_TYPE_DEV_MASK)) {Quoted 101 lines
Quoted 101 lines
Quoted 101 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ mmio =&mmio_dev->mmio[i]; + if (mmio->max_entries_nr != mmio_user-
max_entries_nr)Quoted 75 lines
{
Quoted 64 lines
Quoted 60 lines
Quoted 52 lines
Quoted 35 lines
Quoted 35 lines
Quoted 5 lines
(quoted diff) +5
-0+ r = -EINVAL; + goto out; + } + break; + }Review
Quoted 10 lines
(quoted diff) +10
-0+ } + if (!mmio) { + if (mmio_dev->mmio_nr == KVM_MSIX_MMIO_MAX) { + r = -ENOSPC; + goto out; + } + mmio =&mmio_dev->mmio[mmio_dev->mmio_nr]; + mmio_dev->mmio_nr++; + } + mmio->max_entries_nr = mmio_user->max_entries_nr;Sanity check to avoid overflow.
Review
Quoted 2 lines
(quoted diff) +2
-0+ mmio->dev_id = mmio_user->dev_id; + mmio->flags = mmio_user->flags;Check for unsupported bits (all of them at present?)
Review
Quoted 9 lines
(quoted diff) +9
-0+ if ((mmio_user->type& KVM_MSIX_MMIO_TYPE_DEV_MASK) == + KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV) + mmio->type = KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV; + + if ((mmio_user->type& KVM_MSIX_MMIO_TYPE_BASE_MASK) == + KVM_MSIX_MMIO_TYPE_BASE_TABLE) { + mmio->type |= KVM_MSIX_MMIO_TYPE_BASE_TABLE; + mmio->table_base_addr = mmio_user->base_addr; + mmio->table_base_va = mmio_user->base_va;Check for va in kernel space.
Quoted 16 lines
Quoted 8 lines
Quoted 8 lines
(quoted diff) +1
-0+ } else if ((mmio_user->type&
KVM_MSIX_MMIO_TYPE_BASE_MASK) ==(quoted diff) +6
-0+ KVM_MSIX_MMIO_TYPE_BASE_PBA) { + mmio->type |= KVM_MSIX_MMIO_TYPE_BASE_PBA; + mmio->pba_base_addr = mmio_user->base_addr; + mmio->pba_base_va = mmio_user->base_va; + } +out:Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ mutex_unlock(&mmio_dev->lock); + return r; +} + +In all, looks reasonable. I'd like to see documentation for it, and review from the pci people. Alex, mst?
Quoted 7 lines
Some general comments:
PBA isn't supported in this version, which is OK, but let's not add a capability until it is, and let's not try to guess what the interface will look like. I think keeping PBA in userspace will be hard because it needs to be modified from interrupt context. Removing the PBA stub will make the interface simpler.Quoted 3 lines
The API only get the PBA address now which should be fine. And we still have threaded irq and tasklet for accessing the userspace for interrupt handler...
Quoted 8 lines
I don't think it's going to work: we are not
in the context of the right process. Further
I think we should keep the option of
reading the PBA status from the device or host kernel open. And generally having an interface
for functionality we don't implement is not a good idea:
you don't know whether you really can support the interface you promised.Quoted 8 lines
Well, I don't know if we want to read PBA from device directly. To me it's not a good idea because the real device has nothing to do with the one we show to the guest. At least direct accessing the mask bits of real device would be very dangerous. Avi?
--
regards
Yang, ShengI am not really suggesting this. What I say is PBA is unimplemented let us not commit to an interface yet.
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
--
regards
Yang, ShengQuoted 5 lines
Quoted 5 lines
Would add the API document soon.
--
regards
Yang, Sheng
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 121 lines · On Thursday 30 December 2010 16:52:58 Michael S. Tsirkin wrote:
Quoted 108 lines · Sheng Yang
Quoted 107 lines · On Thursday 30 December 2010 16:15:32 Michael S. Tsirkin wrote:
Quoted 92 lines · Sheng Yang
Quoted 91 lines · On Thursday 30 December 2010 15:47:48 Michael S. Tsirkin wrote:
Quoted 89 lines · Sheng Yang
Quoted 68 lines · On Wednesday 29 December 2010 17:28:24 Michael S. Tsirkin wrote:
Quoted 57 lines · Sheng Yang
Quoted 56 lines · On Wednesday 29 December 2010 16:31:35 Michael S. Tsirkin wrote:
Quoted 54 lines · Sheng Yang
Quoted 26 lines · On Tuesday 28 December 2010 20:26:13 Avi Kivity wrote:
Quoted 25 lines · Sheng Yang
Then we can support mask bit operation of assigned devices now.
(quoted diff) +2
-1@@ -3817,14 +3819,16 @@ static int emulator_write_emulated_onepage(unsigned long addr, mmio: trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, *(u64 *)val); + r = vcpu_mmio_write(vcpu, gpa, bytes, val); /* * Is this MMIO handled locally? */ - if (!vcpu_mmio_write(vcpu, gpa, bytes, val)) + if (!r) return X86EMUL_CONTINUE; vcpu->mmio_needed = 1;Quoted 7 lines
Review
Quoted 3 lines
(quoted diff) +2
-1- vcpu->run->exit_reason = KVM_EXIT_MMIO; + vcpu->run->exit_reason = (r == -ENOTSYNC) ? + KVM_EXIT_MSIX_ROUTING_UPDATE : KVM_EXIT_MMIO;This isn't very pretty,
exit_reasonshould be written invcpu_mmio_write(). I guess we can refactor it later.Sure.
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+#define KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV (1<< 0) + +#define KVM_MSIX_MMIO_TYPE_BASE_TABLE (1<< 8) +#define KVM_MSIX_MMIO_TYPE_BASE_PBA (1<< 9) + +#define KVM_MSIX_MMIO_TYPE_DEV_MASK 0x00ff +#define KVM_MSIX_MMIO_TYPE_BASE_MASK 0xff00Any explanation of these?
I chose to use assigned device id instead of one specific table id, because every device should got at most one MSI MMIO(the same should applied to vfio device as well), and if we use specific table ID, we need way to associate with the device anyway, to perform mask/unmask or other operation. So I think it's better to use device ID here directly.
Quoted 5 lines
Quoted 2 lines
Table id will be needed to make things work for emulated devices.
I suppose even emulated device should got some kind of id(BDF)?
Not that I know. Look at how irqfd is defined for example, or how interrupts are sent through a gsi.
I would like to make the interface be able to support that.Quoted 8 lines
Quoted 5 lines
I think that is
enough for identification, which is already there, so we don't need to allocate another ID for the device - because one device would got at most one MSI-X MMIO, then use BDF or other device specific ID should be quite straightforward.So you propose allocating ids for emulated devices? OK. How will we map e.g. irqfds to these?
I don't understand. I've checked
virtio-pci.cwhich is using irqfd, and it's still a PCI device, and still have BDF, right?Also, what we want is a way to identify the MSI-X MMIO. For assigned device, we use BDF, then we can easily identify the MMIO as well as the device. For others, even they don't have
BDF(I don't think so, because MSI-X is a part of PCI, and every PCI device has BDF), what you need is an ID, no matter what it is and how it defined. QEmu can get the allocation done, and the type field in this API can still tell which kind of ID/devices they are, then determine how to deal with them.Quoted 7 lines
Quoted 5 lines
Yes, the PCI device can be identified with e.g. BFD (won't work for multi-domain but then we can write an allocator maybe). But how will we inject these interrupts?
We can do this now with GSI ioctl or map GSI to irqfd and inject with irqfd write.I suppose it's not in the scope of this patch...
This is why I suggested mapping GSI to msix.
Quoted 3 lines
But I think you can still do
this, everything is the same as before. QEmu can read from table to get data/address pair, then program the routing table, etc.Quoted 8 lines
Quoted 3 lines
Yes, fine, but mask is the problem :)
When qemu/irqfd injects an interrupt and it's masked, guest should not be interrupted.I think this should be done by other APIs(to map GSI with MSI-X entry). And of course you can introduce one GSI type which require one ID and one entry number to eject later, but it's not in the scope of this patch.
What you propose is adding an API to map GSI to a table ID + entry number? Like this?
Quoted 6 lines
1. map table ID to address+length
2. map GSI to table ID + entry #But if we have such an API we don't need anything else as there's already an API to map assigned device interrupts to GSIs?
Assigned device's API is still there, and it's also not touched by this patch. You can modify it for other devices of course.
For assigned devices, this API is enough. And it can also support other devices, emulate their MSI-X MMIO. I think that's enough for this patch.
--
regards
Yang, Sheng
Quoted 36 lines
Quoted 36 lines
--
regards
Yang, ShengQuoted 31 lines
Quoted 31 lines
--
regards
Yang, ShengQuoted 27 lines
Quoted 27 lines
Quoted 27 lines
Quoted 27 lines
Quoted 27 lines
My idea was this: we have the device id in
kvm_assigned_msix_entryalready. Just put table id and entry number inkvm_irq_routing_entry(create a new gsi type for this). The result will also work for irqfd because these are mapped to gsi.Quoted 20 lines
And for the table and pba address, it's due to the mapping in userspace may know the guest MSI-X table address and PBA address at different
time(due to different BAR, refer to the code in assigned_dev_iomem_map() of qemu). So I purposed this API to allow each of them can be passed to kernel space individually.Quoted 12 lines
Quoted 12 lines
(quoted diff) +10
-0+struct kvm_msix_mmio_user { + __u32 dev_id; + __u16 type; + __u16 max_entries_nr; + __u64 base_addr; + __u64 base_va; + __u64 flags; + __u64 reserved[4]; +}; +
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
+int kvm_assigned_device_update_msix_mask_bit(struct
kvm *kvm, + intassigned_dev_id, int
entry, u32
Quoted 57 lines
Quoted 57 lines
flag)
Quoted 54 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 1 line
+{
Need a better name for 'flag' (and make it a bool).
Quoted 13 lines
(quoted diff) +9
-0+ int r = -EFAULT; + struct kvm_assigned_dev_kernel *adev; + int i; + + if (!irqchip_in_kernel(kvm)) + return r; + + mutex_lock(&kvm->lock); + adev =kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
(quoted diff) +3
-0+ assigned_dev_id); + if (!adev) + goto out;Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Review
Quoted 8 lines
(quoted diff) +8
-0+ + for (i = 0; i< adev->entries_nr; i++) + if (adev->host_msix_entries[i].entry == entry) { + if (flag) + disable_irq_nosync( + adev->host_msix_entries[i].vector); + else + enable_irq(adev-host_msix_entries[i].vector);
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
Quoted 25 lines
Quoted 12 lines
(quoted diff) +7
-0+ r = 0; + break; + } +out: + mutex_unlock(&kvm->lock); + return r; +}@@ -1988,6 +2008,12 @@ static int kvm_dev_ioctl_create_vm(void) return r;Review static int
Quoted 10 lines
(quoted diff) +5
-0} #endif + r = kvm_register_msix_mmio_dev(kvm); + if (r< 0) { + kvm_put_kvm(kvm); + return r; + }Shouldn't this be part of individual
KVM_REGISTER_MSIX_MMIOcalls?
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
In fact this MMIO device is more like global one for the VM, not for every devices. It should handle all MMIO from all MSI-X enabled devices, so I put it in the VM init/destroy process.
Quoted 2 lines
Quoted 2 lines
+static int msix_table_mmio_read(struct
kvm_io_device
*this,gpa_taddr, int len, + void
*val)
Quoted 42 lines
Quoted 42 lines
Quoted 42 lines
Quoted 42 lines
Quoted 42 lines
Quoted 42 lines
Quoted 42 lines
Quoted 42 lines
Quoted 24 lines
Quoted 18 lines
Quoted 9 lines
Quoted 9 lines
(quoted diff) +3
-0+{ + struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev,
table_dev); + structkvm_msix_mmio*mmio;(quoted diff) +5
-0+ int idx, ret = 0, entry, offset, r; + + mutex_lock(&mmio_dev->lock); + idx = get_mmio_table_index(mmio_dev, addr, len); + if (idx< 0) {Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ ret = -EOPNOTSUPP; + goto out; + } + if ((addr& 0x3) || (len != 4&& len != 8)) + goto out;What about (addr & 4) && (len == 8)? Is it supported? It may cross entry boundaries.
Quoted 2 lines
Should not supported. But I haven't found words on the PCI spec for it. So I didn't add this check.
IMPLEMENTATION NOTE
MSI-X Memory Space Structures in Read/Write MemoryQuoted 6 lines
....
For all accesses to MSI-X Table and MSI-X PBA fields, software must use aligned full
DWORD or aligned full QWORD transactions; otherwise, the result is undefined.Yes, this one is enough, I would add the checking.
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
(quoted diff) +3
-0+ mmio =&mmio_dev->mmio[idx]; + + entry = (addr - mmio->table_base_addr) /
PCI_MSIX_ENTRY_SIZE; + offset = addr&0xf;(quoted diff) +1
-0+ r = copy_from_user(val, (void*)(mmio->table_base_va + + entry *
PCI_MSIX_ENTRY_SIZE+ offset), len);
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
(quoted diff) +8
-0+ if (r) + goto out; +out: + mutex_unlock(&mmio_dev->lock); + return ret; +} + +static int msix_table_mmio_write(struct
kvm_io_device*this,gpa_taddr, + int
len, const
Quoted 209 lines
Quoted 209 lines
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
Quoted 20 lines
void *val) +{
(quoted diff) +2
-0+ struct kvm_msix_mmio_dev *mmio_dev = + container_of(this, struct kvm_msix_mmio_dev,
table_dev); + structkvm_msix_mmio*mmio;(quoted diff) +14
-0+ int idx, entry, offset, ret = 0, r = 0; + gpa_t entry_base; + u32 old_ctrl, new_ctrl; + + mutex_lock(&mmio_dev->lock); + idx = get_mmio_table_index(mmio_dev, addr, len); + if (idx< 0) { + ret = -EOPNOTSUPP; + goto out; + } + if ((addr& 0x3) || (len != 4&& len != 8)) + goto out; + mmio =&mmio_dev->mmio[idx]; + entry = (addr - mmio->table_base_addr) /
PCI_MSIX_ENTRY_SIZE; +entry_base=mmio->table_base_va+ entry *PCI_MSIX_ENTRY_SIZE;Review
Quoted 5 lines
(quoted diff) +5
-0+ offset = addr& 0xF; + + if (copy_from_user(&old_ctrl, + entry_base + PCI_MSIX_ENTRY_VECTOR_CTRL, + sizeof old_ctrl)) + goto out;
get_user()is easier.Quoted 5 lines
(quoted diff) +2
-0+ + /* No allow writing to other fields when entry isunmasked */ + if (!(old_ctrl&
PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + offset !=PCI_MSIX_ENTRY_VECTOR_CTRL)Review
Quoted 10 lines
(quoted diff) +9
-0+ goto out; + + if (copy_to_user(entry_base + offset, val, len)) + goto out; + + if (copy_from_user(&new_ctrl, + entry_base + PCI_MSIX_ENTRY_VECTOR_CTRL, + sizeof new_ctrl)) + goto out;
put_user()Quoted 4 lines
(quoted diff) +2
-0+ + if ((offset< PCI_MSIX_ENTRY_VECTOR_CTRL&& len ==4)
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Review
Quoted 8 lines
|| + (offset< PCI_MSIX_ENTRY_DATA&& len == 8))
(quoted diff) +6
-0+ ret = -ENOTSYNC; + if (old_ctrl == new_ctrl) + goto out; + if (!(old_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + (new_ctrl& PCI_MSIX_ENTRY_CTRL_MASKBIT)) + r = update_msix_mask_bit(mmio_dev->kvm, mmio,entry,
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Review
Quoted 4 lines
1); + else if ((old_ctrl&
PCI_MSIX_ENTRY_CTRL_MASKBIT)&& + !(new_ctrl&PCI_MSIX_ENTRY_CTRL_MASKBIT))(quoted diff) +1
-0+ r = update_msix_mask_bit(mmio_dev->kvm, mmio,entry,
Quoted 31 lines
Quoted 31 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
Review
Quoted 6 lines
0); + if (r || ret)
(quoted diff) +5
-0+ ret = -ENOTSYNC; +out: + mutex_unlock(&mmio_dev->lock); + return ret; +}blank line...
Quoted 11 lines
+static const struct
kvm_io_device_opsmsix_mmio_table_ops= { + .read =msix_table_mmio_read,(quoted diff) +8
-0+ .write = msix_table_mmio_write, +}; + ++ +int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm, + struct kvm_msix_mmio_user *mmio_user) +{ + struct kvm_msix_mmio_dev *mmio_devQuoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
=&kvm->msix_mmio_dev; + struct
kvm_msix_mmio*mmio =
NULL;(quoted diff) +5
-0+ int r = 0, i; + + mutex_lock(&mmio_dev->lock); + for (i = 0; i< mmio_dev->mmio_nr; i++) { + if (mmio_dev->mmio[i].dev_id ==mmio_user->dev_id&& + (mmio_dev->mmio[i].type&
KVM_MSIX_MMIO_TYPE_DEV_MASK)Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
==
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Review
Quoted 1 line
(quoted diff) +1
-0+ (mmio_user->type&
KVM_MSIX_MMIO_TYPE_DEV_MASK)) {Quoted 104 lines
Quoted 104 lines
Quoted 104 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ mmio =&mmio_dev->mmio[i]; + if (mmio->max_entries_nr != mmio_user-
max_entries_nr)Quoted 83 lines
{
Quoted 66 lines
Quoted 62 lines
Quoted 54 lines
Quoted 45 lines
Quoted 45 lines
Quoted 5 lines
(quoted diff) +5
-0+ r = -EINVAL; + goto out; + } + break; + }Review
Quoted 10 lines
(quoted diff) +10
-0+ } + if (!mmio) { + if (mmio_dev->mmio_nr == KVM_MSIX_MMIO_MAX) { + r = -ENOSPC; + goto out; + } + mmio =&mmio_dev->mmio[mmio_dev->mmio_nr]; + mmio_dev->mmio_nr++; + } + mmio->max_entries_nr = mmio_user->max_entries_nr;Sanity check to avoid overflow.
Review
Quoted 2 lines
(quoted diff) +2
-0+ mmio->dev_id = mmio_user->dev_id; + mmio->flags = mmio_user->flags;Check for unsupported bits (all of them at present?)
Review
Quoted 10 lines
(quoted diff) +1
-0+ if ((mmio_user->type& KVM_MSIX_MMIO_TYPE_DEV_MASK)== +
KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV)(quoted diff) +3
-0+ mmio->type = KVM_MSIX_MMIO_TYPE_ASSIGNED_DEV; + + if ((mmio_user->type&
KVM_MSIX_MMIO_TYPE_BASE_MASK) ==(quoted diff) +4
-0+ KVM_MSIX_MMIO_TYPE_BASE_TABLE) { + mmio->type |= KVM_MSIX_MMIO_TYPE_BASE_TABLE; + mmio->table_base_addr = mmio_user->base_addr; + mmio->table_base_va = mmio_user->base_va;Check for va in kernel space.
Quoted 9 lines
(quoted diff) +1
-0+ } else if ((mmio_user->type&
KVM_MSIX_MMIO_TYPE_BASE_MASK) ==(quoted diff) +7
-0+ KVM_MSIX_MMIO_TYPE_BASE_PBA) { + mmio->type |= KVM_MSIX_MMIO_TYPE_BASE_PBA; + mmio->pba_base_addr = mmio_user->base_addr; + mmio->pba_base_va = mmio_user->base_va; + } +out: + mutex_unlock(&mmio_dev->lock);Quoted 8 lines
Quoted 8 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+ return r; +} + +In all, looks reasonable. I'd like to see documentation for it, and review from the pci people. Alex, mst?
Quoted 7 lines
Some general comments:
PBA isn't supported in this version, which is OK, but let's not add a capability until it is, and let's not try to guess what the interface will look like. I think keeping PBA in userspace will be hard because it needs to be modified from interrupt context. Removing the PBA stub will make the interface simpler.Quoted 3 lines
The API only get the PBA address now which should be fine. And we still have threaded irq and tasklet for accessing the userspace for interrupt handler...
Quoted 8 lines
I don't think it's going to work: we are not
in the context of the right process. Further
I think we should keep the option of
reading the PBA status from the device or host kernel open. And generally having an interface
for functionality we don't implement is not a good idea:
you don't know whether you really can support the interface you promised.Well, I don't know if we want to read PBA from device directly. To me it's not a good idea because the real device has nothing to do with the one we show to the guest. At least direct accessing the mask bits of real device would be very dangerous. Avi?
Quoted 3 lines
--
regards
Yang, ShengI am not really suggesting this. What I say is PBA is unimplemented let us not commit to an interface yet.
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
--
regards
Yang, ShengQuoted 5 lines
Quoted 5 lines
Would add the API document soon.
--
regards
Yang, Sheng
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
In reply to Michael S. TsirkinQuoted 2 lines · Michael S. Tsirkin
I am not really suggesting this. What I say is PBA is unimplemented let us not commit to an interface yet.
What happens to a guest that tries to use PBA? It's a mandatory part of MSI-X, no?
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 6 lines · Avi Kivity
Quoted 2 lines · Michael S. Tsirkin
I am not really suggesting this. What I say is PBA is unimplemented let us not commit to an interface yet.
What happens to a guest that tries to use PBA?
It's a mandatory part of MSI-X, no?
Yes. Unfortunately the pending bit is in fact a communication channel used for function specific purposes when mask bit is set, and 0 when unset. The spec even seems to *require* this use:
I refer to this:
For MSI and MSI-X, while a vector is masked, the function is prohibited from sending the associated message, and the function must set the associated Pending bit whenever the function would otherwise send the message. When software unmasks a vector whose associated Pending bit is set, the function must schedule sending the associated message, and clear the Pending bit as soon as the message has been sent. Note that clearing the MSI-X Function Mask bit may result in many messages needing to be sent.
If a masked vector has its Pending bit set, and the associated underlying interrupt events are somehow satisfied (usually by software though the exact manner is function-specific), the function must clear the Pending bit, to avoid sending a spurious interrupt message later when software unmasks the vector. However, if a subsequent interrupt event occurs while the vector is still masked, the function must again set the Pending bit.
Software is permitted to mask one or more vectors indefinitely, and service their associated interrupt events strictly based on polling their Pending bits. A function must set and clear its Pending bits as necessary to support this “pure polling” mode of operation.
For assigned devices, supporting this would require that the mask bits on the device are set if the mask bit in guest is set (otherwise pending bits are disabled).
Existing code does not support PBA in assigned devices, so at least it's not a regression there, and the virtio spec says nothing about this so we should be fine.
Quoted 2 lines
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 33 lines · Michael S. Tsirkin
Quoted 6 lines · Avi Kivity
Quoted 2 lines · Michael S. Tsirkin
I am not really suggesting this. What I say is PBA is unimplemented let us not commit to an interface yet.
What happens to a guest that tries to use PBA?
It's a mandatory part of MSI-X, no?Yes. Unfortunately the pending bit is in fact a communication channel used for function specific purposes when mask bit is set, and 0 when unset. The spec even seems to *require* this use:
I refer to this:
For MSI and MSI-X, while a vector is masked, the function is prohibited from sending the associated message, and the function must set the associated Pending bit whenever the function would otherwise send the message. When software unmasks a vector whose associated Pending bit is set, the function must schedule sending the associated message, and clear the Pending bit as soon as the message has been sent. Note that clearing the MSI-X Function Mask bit may result in many messages needing to be sent.
If a masked vector has its Pending bit set, and the associated underlying interrupt events are somehow satisfied (usually by software though the exact manner is function-specific), the function must clear the Pending bit, to avoid sending a spurious interrupt message later when software unmasks the vector. However, if a subsequent interrupt event occurs while the vector is still masked, the function must again set the Pending bit.
Quoted 8 lines
Software is permitted to mask one or more vectors indefinitely, and service their associated interrupt events strictly based on polling their Pending bits. A function must set and clear its Pending bits as necessary to support this “pure polling” mode of operation.
For assigned devices, supporting this would require that the mask bits on the device are set if the mask bit in guest is set (otherwise pending bits are disabled).
Can't this be done by setting the real mask bit when the guest reads the virtual pending bit, then reading the real pending bit?
Quoted 3 lines
Existing code does not support PBA in assigned devices, so at least it's not a regression there, and the virtio spec says nothing about this so we should be fine.
Why isn't it subject to the pci spec?
If an interrupt condition exits, the bit should be set.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 39 lines · Avi Kivity
Quoted 38 lines · Michael S. Tsirkin
Quoted 6 lines · Avi Kivity
Quoted 2 lines · Michael S. Tsirkin
I am not really suggesting this. What I say is PBA is unimplemented let us not commit to an interface yet.
What happens to a guest that tries to use PBA?
It's a mandatory part of MSI-X, no?Yes. Unfortunately the pending bit is in fact a communication channel used for function specific purposes when mask bit is set, and 0 when unset. The spec even seems to *require* this use:
I refer to this:
For MSI and MSI-X, while a vector is masked, the function is prohibited from sending the associated message, and the function must set the associated Pending bit whenever the function would otherwise send the message. When software unmasks a vector whose associated Pending bit is set, the function must schedule sending the associated message, and clear the Pending bit as soon as the message has been sent. Note that clearing the MSI-X Function Mask bit may result in many messages needing to be sent.
If a masked vector has its Pending bit set, and the associated underlying interrupt events are somehow satisfied (usually by software though the exact manner is function-specific), the function must clear the Pending bit, to avoid sending a spurious interrupt message later when software unmasks the vector. However, if a subsequent interrupt event occurs while the vector is still masked, the function must again set the Pending bit.
Software is permitted to mask one or more vectors indefinitely, and service their associated interrupt events strictly based on polling their Pending bits. A function must set and clear its Pending bits as necessary to support this “pure polling” mode of operation.
Quoted 6 lines
Quoted 3 lines
For assigned devices, supporting this would require that the mask bits on the device are set if the mask bit in guest is set (otherwise pending bits are disabled).
Can't this be done by setting the real mask bit when the guest reads the virtual pending bit, then reading the real pending bit?
Function specific is function-specific, but most likely not, by that time the pending bit in the device might be clear:
'clear the Pending bit as soon as the message has been sent'
Quoted 7 lines
Quoted 3 lines
Existing code does not support PBA in assigned devices, so at least it's not a regression there, and the virtio spec says nothing about this so we should be fine.
Why isn't it subject to the pci spec?
If an interrupt condition exits, the bit should be set.
I wish. But this is not what the spec says above. It says if vector is unmasked, bit must be cleared.
Quoted 2 lines
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 44 lines · Michael S. Tsirkin
Quoted 43 lines · Avi Kivity
Quoted 41 lines · Michael S. Tsirkin
Quoted 6 lines · Avi Kivity
Quoted 2 lines · Michael S. Tsirkin
I am not really suggesting this. What I say is PBA is unimplemented let us not commit to an interface yet.
What happens to a guest that tries to use PBA?
It's a mandatory part of MSI-X, no?Yes. Unfortunately the pending bit is in fact a communication channel used for function specific purposes when mask bit is set, and 0 when unset. The spec even seems to *require* this use:
I refer to this:
For MSI and MSI-X, while a vector is masked, the function is prohibited from sending the associated message, and the function must set the associated Pending bit whenever the function would otherwise send the message. When software unmasks a vector whose associated Pending bit is set, the function must schedule sending the associated message, and clear the Pending bit as soon as the message has been sent. Note that clearing the MSI-X Function Mask bit may result in many messages needing to be sent.
If a masked vector has its Pending bit set, and the associated underlying interrupt events are somehow satisfied (usually by software though the exact manner is function-specific), the function must clear the Pending bit, to avoid sending a spurious interrupt message later when software unmasks the vector. However, if a subsequent interrupt event occurs while the vector is still masked, the function must again set the Pending bit.
Software is permitted to mask one or more vectors indefinitely, and service their associated interrupt events strictly based on polling their Pending bits. A function must set and clear its Pending bits as necessary to support this “pure polling” mode of operation.
For assigned devices, supporting this would require that the mask bits on the device are set if the mask bit in guest is set (otherwise pending bits are disabled).
Quoted 6 lines
Quoted 2 lines
Can't this be done by setting the real mask bit when the guest reads the virtual pending bit, then reading the real pending bit?
Function specific is function-specific, but most likely not, by that time the pending bit in the device might be clear:
'clear the Pending bit as soon as the message has been sent'
But when we set the mask bit, it must change the pending bit back to the function-specific condition?
Quoted 10 lines
Quoted 7 lines
Quoted 3 lines
Existing code does not support PBA in assigned devices, so at least it's not a regression there, and the virtio spec says nothing about this so we should be fine.
Why isn't it subject to the pci spec?
If an interrupt condition exits, the bit should be set.
I wish. But this is not what the spec says above. It says if vector is unmasked, bit must be cleared.
If interrupt condition exists, and the vector is masked, the pending bit is set. Otherwise the pending bit is clear. Better?
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 48 lines · Avi Kivity
Quoted 47 lines · Michael S. Tsirkin
Quoted 45 lines · Avi Kivity
Quoted 33 lines · Michael S. Tsirkin
Quoted 6 lines · Avi Kivity
Quoted 2 lines · Michael S. Tsirkin
I am not really suggesting this. What I say is PBA is unimplemented let us not commit to an interface yet.
What happens to a guest that tries to use PBA?
It's a mandatory part of MSI-X, no?Yes. Unfortunately the pending bit is in fact a communication channel used for function specific purposes when mask bit is set, and 0 when unset. The spec even seems to *require* this use:
I refer to this:
For MSI and MSI-X, while a vector is masked, the function is prohibited from sending the associated message, and the function must set the associated Pending bit whenever the function would otherwise send the message. When software unmasks a vector whose associated Pending bit is set, the function must schedule sending the associated message, and clear the Pending bit as soon as the message has been sent. Note that clearing the MSI-X Function Mask bit may result in many messages needing to be sent.
If a masked vector has its Pending bit set, and the associated underlying interrupt events are somehow satisfied (usually by software though the exact manner is function-specific), the function must clear the Pending bit, to avoid sending a spurious interrupt message later when software unmasks the vector. However, if a subsequent interrupt event occurs while the vector is still masked, the function must again set the Pending bit.
Quoted 8 lines
Software is permitted to mask one or more vectors indefinitely, and service their associated interrupt events strictly based on polling their Pending bits. A function must set and clear its Pending bits as necessary to support this “pure polling” mode of operation.
For assigned devices, supporting this would require that the mask bits on the device are set if the mask bit in guest is set (otherwise pending bits are disabled).
Can't this be done by setting the real mask bit when the guest reads the virtual pending bit, then reading the real pending bit?
Quoted 6 lines
Quoted 3 lines
Function specific is function-specific, but most likely not, by that time the pending bit in the device might be clear:
'clear the Pending bit as soon as the message has been sent'But when we set the mask bit, it must change the pending bit back to the function-specific condition?
All it says is 'whenever the function would otherwise send a message'. So this is function-specific, generally functions only send a message once per event, they don't resend it assuming that it was queued and will eventually be handled.
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 3 lines
Existing code does not support PBA in assigned devices, so at least it's not a regression there, and the virtio spec says nothing about this so we should be fine.
Why isn't it subject to the pci spec?
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
If an interrupt condition exits, the bit should be set.
I wish. But this is not what the spec says above. It says if vector is unmasked, bit must be cleared.
If interrupt condition exists, and the vector is masked, the pending bit is set. Otherwise the pending bit is clear. Better?
'whenever the function would otherwise send a message' does not seem to match this description:
functions do not generally keep sending messages as long as condition is satisfied (if you think about this, the optimization of not masking immediately in hardware relies on this).
Quoted 2 lines
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
In reply to Michael S. TsirkinQuoted 33 lines · On Thursday 30 December 2010 18:32:56 Michael S. Tsirkin wrote:
Quoted 6 lines · Avi Kivity
Quoted 2 lines · Michael S. Tsirkin
I am not really suggesting this. What I say is PBA is unimplemented let us not commit to an interface yet.
What happens to a guest that tries to use PBA?
It's a mandatory part of MSI-X, no?Yes. Unfortunately the pending bit is in fact a communication channel used for function specific purposes when mask bit is set, and 0 when unset. The spec even seems to *require* this use:
I refer to this:
For MSI and MSI-X, while a vector is masked, the function is prohibited from sending the associated message, and the function must set the associated Pending bit whenever the function would otherwise send the message. When software unmasks a vector whose associated Pending bit is set, the function must schedule sending the associated message, and clear the Pending bit as soon as the message has been sent. Note that clearing the MSI-X Function Mask bit may result in many messages needing to be sent.
If a masked vector has its Pending bit set, and the associated underlying interrupt events are somehow satisfied (usually by software though the exact manner is function-specific), the function must clear the Pending bit, to avoid sending a spurious interrupt message later when software unmasks the vector. However, if a subsequent interrupt event occurs while the vector is still masked, the function must again set the Pending bit.
Quoted 8 lines
Software is permitted to mask one or more vectors indefinitely, and service their associated interrupt events strictly based on polling their Pending bits. A function must set and clear its Pending bits as necessary to support this “pure polling” mode of operation.
For assigned devices, supporting this would require that the mask bits on the device are set if the mask bit in guest is set (otherwise pending bits are disabled).
For assigned device, I think the result we should return is IRQ_PENDING bit of related IRQ. Seems it perfectly fits the meaning of pending bit definition here - set when masked, and if we didn't clean it, one interrupt would be retriggered after unmask. But it's a internal flag, and use it would lead to some core change(more need to be considered if we want to operate the flag bit outside core kernel part).
Quoted 4 lines
Existing code does not support PBA in assigned devices, so at least it's not a regression there, and the virtio spec says nothing about this so we should be fine.
I agree. At least it's not a regression. And in fact we haven't seen any device driver use this. I've checked Linux kernel code, found no one used PCI_MSIX_PBA or msix_pba_offset_reg().
I guess it's fine to get MSI-X mask part in first, then deal with PBA part if necessary - though we haven't seen any driver use it so far. It won't be worse with this patch anyway...
--
regards
Yang, Sheng
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 39 lines · Sheng Yang
Quoted 38 lines · On Thursday 30 December 2010 18:32:56 Michael S. Tsirkin wrote:
Quoted 6 lines · Avi Kivity
Quoted 2 lines · Michael S. Tsirkin
I am not really suggesting this. What I say is PBA is unimplemented let us not commit to an interface yet.
What happens to a guest that tries to use PBA?
It's a mandatory part of MSI-X, no?Yes. Unfortunately the pending bit is in fact a communication channel used for function specific purposes when mask bit is set, and 0 when unset. The spec even seems to *require* this use:
I refer to this:
For MSI and MSI-X, while a vector is masked, the function is prohibited from sending the associated message, and the function must set the associated Pending bit whenever the function would otherwise send the message. When software unmasks a vector whose associated Pending bit is set, the function must schedule sending the associated message, and clear the Pending bit as soon as the message has been sent. Note that clearing the MSI-X Function Mask bit may result in many messages needing to be sent.
If a masked vector has its Pending bit set, and the associated underlying interrupt events are somehow satisfied (usually by software though the exact manner is function-specific), the function must clear the Pending bit, to avoid sending a spurious interrupt message later when software unmasks the vector. However, if a subsequent interrupt event occurs while the vector is still masked, the function must again set the Pending bit.
Software is permitted to mask one or more vectors indefinitely, and service their associated interrupt events strictly based on polling their Pending bits. A function must set and clear its Pending bits as necessary to support this “pure polling” mode of operation.
Quoted 8 lines
Quoted 3 lines
For assigned devices, supporting this would require that the mask bits on the device are set if the mask bit in guest is set (otherwise pending bits are disabled).
For assigned device, I think the result we should return is
IRQ_PENDINGbit of related IRQ. Seems it perfectly fits the meaning of pending bit definition here - set when masked, and if we didn't clean it, one interrupt would be retriggered after unmask.
Well, it doesn't seem to fit this part of the definition
Quoted 8 lines
Quoted 8 lines
If a masked vector has its Pending bit set, and the associated underlying interrupt events are somehow satisfied (usually by software though the exact manner is function-specific), the function must clear the Pending bit, to avoid sending a spurious interrupt message later when software unmasks the vector. However, if a subsequent interrupt event occurs while the vector is still masked, the function must again set the Pending bit.
Quoted 4 lines
Quoted 4 lines
Software is permitted to mask one or more vectors indefinitely, and service their associated interrupt events strictly based on polling their Pending bits. A function must set and clear its Pending bits as necessary to support this “pure polling” mode of operation.
looking at IRQ_PENDING will make the pending bit *never* clear while the vector is masked.
Quoted 19 lines
But it's a internal flag, and use it would lead to some core change(more need to be considered if we want to operate the flag bit outside core kernel part).
Quoted 4 lines
Existing code does not support PBA in assigned devices, so at least it's not a regression there, and the virtio spec says nothing about this so we should be fine.
I agree. At least it's not a regression. And in fact we haven't seen any device driver use this. I've checked Linux kernel code, found no one used
PCI_MSIX_PBAormsix_pba_offset_reg().I guess it's fine to get MSI-X mask part in first, then deal with PBA part if necessary - though we haven't seen any driver use it so far. It won't be worse with this patch anyway...
--
regards
Yang, Sheng
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
In reply to Sheng YangQuoted 5 lines · Sheng Yang
Quoted 4 lines
Existing code does not support PBA in assigned devices, so at least it's not a regression there, and the virtio spec says nothing about this so we should be fine.
Quoted 7 lines
I agree. At least it's not a regression. And in fact we haven't seen any device driver use this. I've checked Linux kernel code, found no one used
PCI_MSIX_PBAormsix_pba_offset_reg().I guess it's fine to get MSI-X mask part in first, then deal with PBA part if necessary - though we haven't seen any driver use it so far. It won't be worse with this patch anyway...
In a way it is worse because before, the fix would belong in user space, which is easier to test and distribute. Now we have to fix it in the kernel.
However I recognize that drivers which rely on the pending bit are rare/nonexistent (likely on in preboot environments where interrupts are hard), so even if we do code it, it will likely be incorrect (certainly without a test).
So I'll accept the patch without PBA. Michael, what about supporting virtio? Can we base something on this patch?
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 18 lines · Avi Kivity
Quoted 5 lines · Sheng Yang
Quoted 4 lines
Existing code does not support PBA in assigned devices, so at least it's not a regression there, and the virtio spec says nothing about this so we should be fine.
Quoted 7 lines
I agree. At least it's not a regression. And in fact we haven't seen any device driver use this. I've checked Linux kernel code, found no one used
PCI_MSIX_PBAormsix_pba_offset_reg().I guess it's fine to get MSI-X mask part in first, then deal with PBA part if necessary - though we haven't seen any driver use it so far. It won't be worse with this patch anyway...
In a way it is worse because before, the fix would belong in user space, which is easier to test and distribute. Now we have to fix it in the kernel.
Quoted 7 lines
However I recognize that drivers which rely on the pending bit are rare/nonexistent (likely on in preboot environments where interrupts are hard), so even if we do code it, it will likely be incorrect (certainly without a test).
So I'll accept the patch without PBA. Michael, what about supporting virtio? Can we base something on this patch?
I don't see how userspace can send interrupts with this interface unfortunately. We also need irqfd support ...
Quoted 2 lines
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 18 lines · Michael S. Tsirkin
Quoted 18 lines
Quoted 8 lines
I agree. At least it's not a regression. And in fact we haven't seen any device driver use this. I've checked Linux kernel code, found no one used
PCI_MSIX_PBAormsix_pba_offset_reg().I guess it's fine to get MSI-X mask part in first, then deal with PBA part if necessary - though we haven't seen any driver use it so far. It won't be worse with this patch anyway...
In a way it is worse because before, the fix would belong in user space, which is easier to test and distribute. Now we have to fix it in the kernel.
However I recognize that drivers which rely on the pending bit are rare/nonexistent (likely on in preboot environments where interrupts are hard), so even if we do code it, it will likely be incorrect (certainly without a test).
Quoted 5 lines
Quoted 2 lines
So I'll accept the patch without PBA. Michael, what about supporting virtio? Can we base something on this patch?
I don't see how userspace can send interrupts with this interface unfortunately. We also need irqfd support ...
Sure we'll need additions to that interface.
What about vhost-net and vfio? I thought that they could emulate the mask bits:
- KVM_MMIOFD(vmfd, mmio_range, fd1, fd2) associates an mmio range with an fd
- writel(mmio_range) or readl(mmio_range) from the guest causes a command to be written to fd1
- for readl(), read from fd2 to see the result (works nicely for "pci read flushes posted writes")
this allows interesting stuff to be implemented in separate processes, threads, or kernel modules.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 19 lines · Avi Kivity
Quoted 18 lines · Michael S. Tsirkin
Quoted 18 lines
Quoted 8 lines
I agree. At least it's not a regression. And in fact we haven't seen any device driver use this. I've checked Linux kernel code, found no one used
PCI_MSIX_PBAormsix_pba_offset_reg().I guess it's fine to get MSI-X mask part in first, then deal with PBA part if necessary - though we haven't seen any driver use it so far. It won't be worse with this patch anyway...
In a way it is worse because before, the fix would belong in user space, which is easier to test and distribute. Now we have to fix it in the kernel.
However I recognize that drivers which rely on the pending bit are rare/nonexistent (likely on in preboot environments where interrupts are hard), so even if we do code it, it will likely be incorrect (certainly without a test).
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
So I'll accept the patch without PBA. Michael, what about supporting virtio? Can we base something on this patch?
I don't see how userspace can send interrupts with this interface unfortunately. We also need irqfd support ...
Sure we'll need additions to that interface.
What I suggested is
1. an ioctl to map phy address + size to table id
2. a new gsi type with a table id + entry number.
If we have that, assigned devices, virtio and vhost-net can work mostly as is, with just the mask bits accelerated.
Quoted 11 lines
What about vhost-net and vfio? I thought that they could emulate the mask bits:
-
KVM_MMIOFD(vmfd, mmio_range, fd1, fd2)associates an mmio range with an fd
-writel(mmio_range)orreadl(mmio_range)from the guest causes a command to be written to fd1
- forreadl(), read from fd2 to see the result (works nicely for "pci read flushes posted writes")this allows interesting stuff to be implemented in separate processes, threads, or kernel modules.
This could work. Some thought needs to be given to how we make sure that an appropriate type of file is passed in. Maybe using a netlink based connector for this a good idea?
OTOH if we have MSIX mask bit emulation in kvm anyway, using it makes sense ...
Quoted 2 lines
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 4 lines · Michael S. Tsirkin
Quoted 4 lines
Quoted 3 lines
I don't see how userspace can send interrupts with this interface unfortunately. We also need irqfd support ...
Quoted 9 lines
Quoted 1 line
Sure we'll need additions to that interface.
What I suggested is
1. an ioctl to map phy address + size to table id
2. a new gsi type with a table id + entry number.If we have that, assigned devices, virtio and vhost-net can work mostly as is, with just the mask bits accelerated.
Ok. Please adopt this patch and send a series that does this. I'd like to see the whole thing working.
Quoted 9 lines
Quoted 9 lines
What about vhost-net and vfio? I thought that they could emulate the mask bits:
-
KVM_MMIOFD(vmfd, mmio_range, fd1, fd2)associates an mmio range with an fd
-writel(mmio_range)orreadl(mmio_range)from the guest causes a command to be written to fd1
- forreadl(), read from fd2 to see the result (works nicely for "pci read flushes posted writes")
Quoted 6 lines
Quoted 2 lines
this allows interesting stuff to be implemented in separate processes, threads, or kernel modules.
This could work. Some thought needs to be given to how we make sure that an appropriate type of file is passed in. Maybe using a netlink based connector for this a good idea?
Why do we care what type of file is passed? We just write there, and whatever is on the other side needs to handle it.
Quoted 2 lines
OTOH if we have MSIX mask bit emulation in kvm anyway, using it makes sense ...
Yeah, except I'm not sure how the current proposal can interface with vfio. So we'll have two interfaces, until we vfio takes over completely.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 34 lines · Avi Kivity
Quoted 4 lines · Michael S. Tsirkin
Quoted 4 lines
Quoted 3 lines
I don't see how userspace can send interrupts with this interface unfortunately. We also need irqfd support ...
Quoted 9 lines
Quoted 1 line
Sure we'll need additions to that interface.
What I suggested is
1. an ioctl to map phy address + size to table id
2. a new gsi type with a table id + entry number.If we have that, assigned devices, virtio and vhost-net can work mostly as is, with just the mask bits accelerated.
Ok. Please adopt this patch and send a series that does this. I'd like to see the whole thing working.
Quoted 9 lines
Quoted 9 lines
What about vhost-net and vfio? I thought that they could emulate the mask bits:
-
KVM_MMIOFD(vmfd, mmio_range, fd1, fd2)associates an mmio range with an fd
-writel(mmio_range)orreadl(mmio_range)from the guest causes a command to be written to fd1
- forreadl(), read from fd2 to see the result (works nicely for "pci read flushes posted writes")Quoted 6 lines
Quoted 2 lines
this allows interesting stuff to be implemented in separate processes, threads, or kernel modules.
This could work. Some thought needs to be given to how we make sure that an appropriate type of file is passed in. Maybe using a netlink based connector for this a good idea?
Quoted 8 lines
Why do we care what type of file is passed? We just write there, and whatever is on the other side needs to handle it.
Quoted 2 lines
OTOH if we have MSIX mask bit emulation in kvm anyway, using it makes sense ...
Yeah, except I'm not sure how the current proposal can interface with vfio.
Me too.
Quoted 4 lines
So we'll have two interfaces, until we vfio takes over
completely.Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
In reply to Sheng YangQuoted 9 lines · Sheng Yang
Quoted 8 lines
I don't think it's going to work: we are not
in the context of the right process. Further
I think we should keep the option of
reading the PBA status from the device or host kernel open. And generally having an interface
for functionality we don't implement is not a good idea:
you don't know whether you really can support the interface you promised.
Quoted 3 lines
Well, I don't know if we want to read PBA from device directly. To me it's not a good idea because the real device has nothing to do with the one we show to the guest.
Right. vPBA.bit = pPBA.bit | recorded_pending_bit_from_lazy_masked_interrupt.
Quoted 2 lines
At least direct accessing the mask bits of real device would be very
dangerous. Avi?
Agree.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 2/2][RFC] KVM: Emulate MSI-X table and PBA in kernel
Quoted 10 lines · Avi Kivity
Quoted 9 lines · Sheng Yang
Quoted 8 lines
I don't think it's going to work: we are not
in the context of the right process. Further
I think we should keep the option of
reading the PBA status from the device or host kernel open. And generally having an interface
for functionality we don't implement is not a good idea:
you don't know whether you really can support the interface you promised.
Quoted 6 lines
Quoted 3 lines
Well, I don't know if we want to read PBA from device directly. To me it's not a good idea because the real device has nothing to do with the one we show to the guest.
Right. vPBA.bit = pPBA.bit |
recorded_pending_bit_from_lazy_masked_interrupt.
At some level, this is correct. However both are *not* in userspace memory so an interface that assumes that pending bits in userspace are correct at all times is not going to work.
There is also the spec requirement that the device clears the pending bit if the interrupt was not served but the condition that caused the event no longer applies, so that a driver can work just by polling pending bits. Personally I don't think this requirement works in real life - Seems more like a vague idea on behalf of spec authors - so hopefully guests do not use this.
Quoted 7 lines
Quoted 2 lines
At least direct accessing the mask bits of real device would be very
dangerous. Avi?Agree.
Signature · 1 line
error compiling committee.c: too many arguments to function
Quoted 4 lines · On Wednesday 22 December 2010 16:44:53 Sheng Yang wrote:
This patchset didn't include two PCI related patches which would be checked in through PCI subsystem.
Would add the API document soon.
Avi?
BTW, there is one compiling issue for the second patch, due to last minute clean up...
Would update it along with other comments.
--
regards
Yang, Sheng
Quoted 16 lines
Change from v5:
Complete rewrote according to Avi's comments.Sheng Yang (2):
KVM: Move structkvm_io_devicetokvm_host.h
KVM: Emulate MSI-X table and PBA in kernelarch/x86/kvm/Makefile | 2 +- arch/x86/kvm/x86.c | 8 +- include/linux/kvm.h | 22 ++++ include/linux/kvm_host.h | 48 +++++++++ virt/kvm/assigned-dev.c | 30 ++++++ virt/kvm/iodev.h | 25 +----- virt/kvm/kvm_main.c | 38 +++++++- virt/kvm/msix_mmio.c | 244
Quoted 1 line
++++++++++++++++++++++++++++++++++++++++++++++
virt/kvm/msix_mmio.h |
Quoted 4 lines
24 +++++
9 files changed, 410 insertions(+), 31 deletions(-) create mode 100644 virt/kvm/msix_mmio.c create mode 100644 virt/kvm/msix_mmio.h