[PATCH 07/17] kvm tools: pci: add MMIO interface to virtio-pci devices

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds an MMIO interface for each virtio-pci device, so that
they can be accessed without having to use an ioport. For each device, a
new memory BAR is added which corresponds to an area of MMIO space with
a shim trap handler. This handler simply translates the access into an
ioport access via kvm__emulate_io. Since guests can generate accesses
via either the ioport or MMIO regions, an ioeventfd is registered for
both.

Signed-off-by: Will Deacon <will.deacon@xxxxxxx>
---
 tools/kvm/include/kvm/virtio-pci.h |   4 +-
 tools/kvm/virtio/pci.c             | 111 +++++++++++++++++++++++++------------
 2 files changed, 78 insertions(+), 37 deletions(-)

diff --git a/tools/kvm/include/kvm/virtio-pci.h b/tools/kvm/include/kvm/virtio-pci.h
index 9b063924e5da..c795ce71fa88 100644
--- a/tools/kvm/include/kvm/virtio-pci.h
+++ b/tools/kvm/include/kvm/virtio-pci.h
@@ -22,8 +22,10 @@ struct virtio_pci {
 	struct pci_device_header pci_hdr;
 	struct device_header	dev_hdr;
 	void			*dev;
+	struct kvm		*kvm;
 
-	u16			base_addr;
+	u16			port_addr;
+	u32			mmio_addr;
 	u8			status;
 	u8			isr;
 	u32			features;
diff --git a/tools/kvm/virtio/pci.c b/tools/kvm/virtio/pci.c
index 77c933fd4ab2..e1b5be6b036e 100644
--- a/tools/kvm/virtio/pci.c
+++ b/tools/kvm/virtio/pci.c
@@ -24,7 +24,8 @@ static int virtio_pci__init_ioeventfd(struct kvm *kvm, struct virtio_device *vde
 {
 	struct ioevent ioevent;
 	struct virtio_pci *vpci = vdev->virtio;
-	int r;
+	int i, r, flags = IOEVENTFD_FLAG_PIO;
+	int fds[2];
 
 	vpci->ioeventfds[vq] = (struct virtio_pci_ioevent_param) {
 		.vdev		= vdev,
@@ -32,32 +33,44 @@ static int virtio_pci__init_ioeventfd(struct kvm *kvm, struct virtio_device *vde
 	};
 
 	ioevent = (struct ioevent) {
-		.io_addr	= vpci->base_addr + VIRTIO_PCI_QUEUE_NOTIFY,
-		.io_len		= sizeof(u16),
 		.fn		= virtio_pci__ioevent_callback,
 		.fn_ptr		= &vpci->ioeventfds[vq],
 		.datamatch	= vq,
 		.fn_kvm		= kvm,
-		.fd		= eventfd(0, 0),
 	};
 
-	if (vdev->use_vhost)
-		/*
-		 * Vhost will poll the eventfd in host kernel side,
-		 * no need to poll in userspace.
-		 */
-		r = ioeventfd__add_event(&ioevent, IOEVENTFD_FLAG_PIO);
-	else
-		/* Need to poll in userspace. */
-		r = ioeventfd__add_event(&ioevent, IOEVENTFD_FLAG_PIO |
-						   IOEVENTFD_FLAG_USER_POLL);
+	/*
+	 * Vhost will poll the eventfd in host kernel side, otherwise we
+	 * need to poll in userspace.
+	 */
+	if (!vdev->use_vhost)
+		flags |= IOEVENTFD_FLAG_USER_POLL;
+
+	/* ioport */
+	ioevent.io_addr	= vpci->port_addr + VIRTIO_PCI_QUEUE_NOTIFY;
+	ioevent.io_len	= sizeof(u16);
+	ioevent.fd	= fds[0] = eventfd(0, 0);
+	r = ioeventfd__add_event(&ioevent, flags);
 	if (r)
 		return r;
 
-	if (vdev->ops->notify_vq_eventfd)
-		vdev->ops->notify_vq_eventfd(kvm, vpci->dev, vq, ioevent.fd);
+	/* mmio */
+	ioevent.io_addr	= vpci->mmio_addr + VIRTIO_PCI_QUEUE_NOTIFY;
+	ioevent.io_len	= sizeof(u32);
+	ioevent.fd	= fds[1] = eventfd(0, 0);
+	r = ioeventfd__add_event(&ioevent, flags);
+	if (r)
+		goto free_ioport_evt;
 
+	if (vdev->ops->notify_vq_eventfd)
+		for (i = 0; i < 2; ++i)
+			vdev->ops->notify_vq_eventfd(kvm, vpci->dev, vq,
+						     fds[i]);
 	return 0;
+
+free_ioport_evt:
+	ioeventfd__del_event(vpci->port_addr + VIRTIO_PCI_QUEUE_NOTIFY, vq);
+	return r;
 }
 
 static inline bool virtio_pci__msix_enabled(struct virtio_pci *vpci)
@@ -105,7 +118,7 @@ static bool virtio_pci__io_in(struct ioport *ioport, struct kvm *kvm, u16 port,
 
 	vdev = ioport->priv;
 	vpci = vdev->virtio;
-	offset = port - vpci->base_addr;
+	offset = port - vpci->port_addr;
 
 	switch (offset) {
 	case VIRTIO_PCI_HOST_FEATURES:
@@ -188,7 +201,7 @@ static bool virtio_pci__io_out(struct ioport *ioport, struct kvm *kvm, u16 port,
 
 	vdev = ioport->priv;
 	vpci = vdev->virtio;
-	offset = port - vpci->base_addr;
+	offset = port - vpci->port_addr;
 
 	switch (offset) {
 	case VIRTIO_PCI_GUEST_FEATURES:
@@ -227,7 +240,8 @@ static struct ioport_operations virtio_pci__io_ops = {
 	.io_out	= virtio_pci__io_out,
 };
 
-static void virtio_pci__mmio_callback(u64 addr, u8 *data, u32 len, u8 is_write, void *ptr)
+static void virtio_pci__msix_mmio_callback(u64 addr, u8 *data, u32 len,
+					   u8 is_write, void *ptr)
 {
 	struct virtio_pci *vpci = ptr;
 	void *table;
@@ -307,6 +321,16 @@ int virtio_pci__signal_config(struct kvm *kvm, struct virtio_device *vdev)
 	return 0;
 }
 
+static void virtio_pci__io_mmio_callback(u64 addr, u8 *data, u32 len,
+					 u8 is_write, void *ptr)
+{
+	struct virtio_pci *vpci = ptr;
+	int direction = is_write ? KVM_EXIT_IO_OUT : KVM_EXIT_IO_IN;
+	u16 port = vpci->port_addr + (addr & (IOPORT_SIZE - 1));
+
+	kvm__emulate_io(vpci->kvm, port, data, direction, len, 1);
+}
+
 int virtio_pci__init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
 		     int device_id, int subsys_id, int class)
 {
@@ -314,19 +338,26 @@ int virtio_pci__init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
 	u8 pin, line;
 	int r;
 
+	vpci->kvm = kvm;
 	vpci->dev = dev;
-	vpci->msix_io_block = pci_get_io_space_block(PCI_IO_SIZE * 2);
 
 	r = ioport__register(kvm, IOPORT_EMPTY, &virtio_pci__io_ops, IOPORT_SIZE, vdev);
 	if (r < 0)
 		return r;
+	vpci->port_addr = (u16)r;
 
-	vpci->base_addr = (u16)r;
-	r = kvm__register_mmio(kvm, vpci->msix_io_block, PCI_IO_SIZE * 2, false,
-			       virtio_pci__mmio_callback, vpci);
+	vpci->mmio_addr = pci_get_io_space_block(IOPORT_SIZE);
+	r = kvm__register_mmio(kvm, vpci->mmio_addr, IOPORT_SIZE, false,
+			       virtio_pci__io_mmio_callback, vpci);
 	if (r < 0)
 		goto free_ioport;
 
+	vpci->msix_io_block = pci_get_io_space_block(PCI_IO_SIZE * 2);
+	r = kvm__register_mmio(kvm, vpci->msix_io_block, PCI_IO_SIZE * 2, false,
+			       virtio_pci__msix_mmio_callback, vpci);
+	if (r < 0)
+		goto free_mmio;
+
 	vpci->pci_hdr = (struct pci_device_header) {
 		.vendor_id		= cpu_to_le16(PCI_VENDOR_ID_REDHAT_QUMRANET),
 		.device_id		= cpu_to_le16(device_id),
@@ -337,14 +368,17 @@ int virtio_pci__init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
 		.class[2]		= (class >> 16) & 0xff,
 		.subsys_vendor_id	= cpu_to_le16(PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET),
 		.subsys_id		= cpu_to_le16(subsys_id),
-		.bar[0]			= cpu_to_le32(vpci->base_addr
+		.bar[0]			= cpu_to_le32(vpci->mmio_addr
+							| PCI_BASE_ADDRESS_SPACE_MEMORY),
+		.bar[1]			= cpu_to_le32(vpci->port_addr
 							| PCI_BASE_ADDRESS_SPACE_IO),
-		.bar[1]			= cpu_to_le32(vpci->msix_io_block
+		.bar[2]			= cpu_to_le32(vpci->msix_io_block
 							| PCI_BASE_ADDRESS_SPACE_MEMORY),
 		.status			= cpu_to_le16(PCI_STATUS_CAP_LIST),
 		.capabilities		= (void *)&vpci->pci_hdr.msix - (void *)&vpci->pci_hdr,
 		.bar_size[0]		= IOPORT_SIZE,
-		.bar_size[1]		= PCI_IO_SIZE * 2,
+		.bar_size[1]		= IOPORT_SIZE,
+		.bar_size[2]		= PCI_IO_SIZE * 2,
 	};
 
 	vpci->dev_hdr = (struct device_header) {
@@ -367,14 +401,14 @@ int virtio_pci__init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
 	 */
 	vpci->pci_hdr.msix.ctrl = cpu_to_le16(VIRTIO_PCI_MAX_VQ + VIRTIO_PCI_MAX_CONFIG - 1);
 
-	/* Both table and PBA are mapped to the same BAR (1) */
-	vpci->pci_hdr.msix.table_offset = cpu_to_le32(1);
-	vpci->pci_hdr.msix.pba_offset = cpu_to_le32(1 | PCI_IO_SIZE);
+	/* Both table and PBA are mapped to the same BAR (2) */
+	vpci->pci_hdr.msix.table_offset = cpu_to_le32(2);
+	vpci->pci_hdr.msix.pba_offset = cpu_to_le32(2 | PCI_IO_SIZE);
 	vpci->config_vector = 0;
 
 	r = irq__register_device(subsys_id, &pin, &line);
 	if (r < 0)
-		goto free_mmio;
+		goto free_msix_mmio;
 
 	if (kvm__supports_extension(kvm, KVM_CAP_SIGNAL_MSI))
 		vpci->features |= VIRTIO_PCI_F_SIGNAL_MSI;
@@ -383,14 +417,16 @@ int virtio_pci__init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
 	vpci->pci_hdr.irq_line	= line;
 	r = device__register(&vpci->dev_hdr);
 	if (r < 0)
-		goto free_ioport;
+		goto free_msix_mmio;
 
 	return 0;
 
-free_mmio:
+free_msix_mmio:
 	kvm__deregister_mmio(kvm, vpci->msix_io_block);
+free_mmio:
+	kvm__deregister_mmio(kvm, vpci->mmio_addr);
 free_ioport:
-	ioport__unregister(kvm, vpci->base_addr);
+	ioport__unregister(kvm, vpci->port_addr);
 	return r;
 }
 
@@ -399,11 +435,14 @@ int virtio_pci__exit(struct kvm *kvm, struct virtio_device *vdev)
 	struct virtio_pci *vpci = vdev->virtio;
 	int i;
 
+	kvm__deregister_mmio(kvm, vpci->mmio_addr);
 	kvm__deregister_mmio(kvm, vpci->msix_io_block);
-	ioport__unregister(kvm, vpci->base_addr);
+	ioport__unregister(kvm, vpci->port_addr);
 
-	for (i = 0; i < VIRTIO_PCI_MAX_VQ; i++)
-		ioeventfd__del_event(vpci->base_addr + VIRTIO_PCI_QUEUE_NOTIFY, i);
+	for (i = 0; i < VIRTIO_PCI_MAX_VQ; i++) {
+		ioeventfd__del_event(vpci->port_addr + VIRTIO_PCI_QUEUE_NOTIFY, i);
+		ioeventfd__del_event(vpci->mmio_addr + VIRTIO_PCI_QUEUE_NOTIFY, i);
+	}
 
 	return 0;
 }
-- 
1.8.2.2

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux