[PATCH v2 3/4] KVM: add io services to xinterface

From: Gregory Haskins
Date: Fri Oct 02 2009 - 16:19:54 EST


We want to add a more efficient way to get PIO signals out of the guest,
so we add an "xioevent" interface. This allows a client to register
for notifications when a specific MMIO/PIO address is touched by
the guest. This is an alternative interface to ioeventfd, which is
performance limited by io-bus scaling and eventfd wait-queue based
notification mechanism. This also has the advantage of retaining
the full PIO data payload and passing it to the recipient.

Signed-off-by: Gregory Haskins <ghaskins@xxxxxxxxxx>
---

include/linux/kvm_xinterface.h | 47 ++++++++++++++++++
virt/kvm/xinterface.c | 106 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 153 insertions(+), 0 deletions(-)

diff --git a/include/linux/kvm_xinterface.h b/include/linux/kvm_xinterface.h
index 01f092b..684b6f8 100644
--- a/include/linux/kvm_xinterface.h
+++ b/include/linux/kvm_xinterface.h
@@ -12,6 +12,16 @@

struct kvm_xinterface;
struct kvm_xvmap;
+struct kvm_xioevent;
+
+enum {
+ kvm_xioevent_flag_nr_pio,
+ kvm_xioevent_flag_nr_max,
+};
+
+#define KVM_XIOEVENT_FLAG_PIO (1 << kvm_xioevent_flag_nr_pio)
+
+#define KVM_XIOEVENT_VALID_FLAG_MASK ((1 << kvm_xioevent_flag_nr_max) - 1)

struct kvm_xinterface_ops {
unsigned long (*copy_to)(struct kvm_xinterface *intf,
@@ -22,6 +32,10 @@ struct kvm_xinterface_ops {
struct kvm_xvmap* (*vmap)(struct kvm_xinterface *intf,
unsigned long gpa,
unsigned long len);
+ struct kvm_xioevent* (*ioevent)(struct kvm_xinterface *intf,
+ u64 addr,
+ unsigned long len,
+ unsigned long flags);
void (*release)(struct kvm_xinterface *);
};

@@ -109,6 +123,39 @@ kvm_xvmap_put(struct kvm_xvmap *vmap)
kref_put(&vmap->kref, _kvm_xvmap_release);
}

+struct kvm_xioevent_ops {
+ void (*deassign)(struct kvm_xioevent *ioevent);
+};
+
+struct kvm_xioevent {
+ const struct kvm_xioevent_ops *ops;
+ struct kvm_xinterface *intf;
+ void (*signal)(struct kvm_xioevent *ioevent, const void *val);
+ void *priv;
+};
+
+static inline void
+kvm_xioevent_init(struct kvm_xioevent *ioevent,
+ const struct kvm_xioevent_ops *ops,
+ struct kvm_xinterface *intf)
+{
+ memset(ioevent, 0, sizeof(vmap));
+ ioevent->ops = ops;
+ ioevent->intf = intf;
+
+ kvm_xinterface_get(intf);
+}
+
+static inline void
+kvm_xioevent_deassign(struct kvm_xioevent *ioevent)
+{
+ struct kvm_xinterface *intf = ioevent->intf;
+ rmb();
+
+ ioevent->ops->deassign(ioevent);
+ kvm_xinterface_put(intf);
+}
+
struct kvm_xinterface *kvm_xinterface_bind(int fd);

#endif /* __KVM_XINTERFACE_H */
diff --git a/virt/kvm/xinterface.c b/virt/kvm/xinterface.c
index 3b586c5..c356835 100644
--- a/virt/kvm/xinterface.c
+++ b/virt/kvm/xinterface.c
@@ -28,6 +28,8 @@
#include <linux/kvm_host.h>
#include <linux/kvm_xinterface.h>

+#include "iodev.h"
+
struct _xinterface {
struct kvm *kvm;
struct task_struct *task;
@@ -42,6 +44,14 @@ struct _xvmap {
struct kvm_xvmap vmap;
};

+struct _ioevent {
+ u64 addr;
+ int length;
+ struct kvm_io_bus *bus;
+ struct kvm_io_device dev;
+ struct kvm_xioevent ioevent;
+};
+
static struct _xinterface *
to_intf(struct kvm_xinterface *intf)
{
@@ -362,6 +372,101 @@ fail:
return ERR_PTR(ret);
}

+/* MMIO/PIO writes trigger an event if the addr/val match */
+static int
+ioevent_write(struct kvm_io_device *dev, gpa_t addr, int len, const void *val)
+{
+ struct _ioevent *p = container_of(dev, struct _ioevent, dev);
+ struct kvm_xioevent *ioevent = &p->ioevent;
+
+ if (!(addr == p->addr && len == p->length))
+ return -EOPNOTSUPP;
+
+ if (!ioevent->signal)
+ return 0;
+
+ ioevent->signal(ioevent, val);
+ return 0;
+}
+
+static const struct kvm_io_device_ops ioevent_device_ops = {
+ .write = ioevent_write,
+};
+
+static void
+ioevent_deassign(struct kvm_xioevent *ioevent)
+{
+ struct _ioevent *p = container_of(ioevent, struct _ioevent, ioevent);
+ struct _xinterface *_intf = to_intf(ioevent->intf);
+ struct kvm *kvm = _intf->kvm;
+
+ kvm_io_bus_unregister_dev(kvm, p->bus, &p->dev);
+ kfree(p);
+}
+
+static const struct kvm_xioevent_ops ioevent_intf_ops = {
+ .deassign = ioevent_deassign,
+};
+
+static struct kvm_xioevent*
+xinterface_ioevent(struct kvm_xinterface *intf,
+ u64 addr,
+ unsigned long len,
+ unsigned long flags)
+{
+ struct _xinterface *_intf = to_intf(intf);
+ struct kvm *kvm = _intf->kvm;
+ int pio = flags & KVM_XIOEVENT_FLAG_PIO;
+ struct kvm_io_bus *bus = pio ? &kvm->pio_bus : &kvm->mmio_bus;
+ struct _ioevent *p;
+ int ret;
+
+ /* must be natural-word sized */
+ switch (len) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ break;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* check for range overflow */
+ if (addr + len < addr)
+ return ERR_PTR(-EINVAL);
+
+ /* check for extra flags that we don't understand */
+ if (flags & ~KVM_XIOEVENT_VALID_FLAG_MASK)
+ return ERR_PTR(-EINVAL);
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ p->addr = addr;
+ p->length = len;
+ p->bus = bus;
+
+ kvm_iodevice_init(&p->dev, &ioevent_device_ops);
+
+ ret = kvm_io_bus_register_dev(kvm, bus, &p->dev);
+ if (ret < 0)
+ goto fail;
+
+ kvm_xioevent_init(&p->ioevent, &ioevent_intf_ops, intf);
+
+ return &p->ioevent;
+
+fail:
+ kfree(p);
+
+ return ERR_PTR(ret);
+
+}
+
static void
xinterface_release(struct kvm_xinterface *intf)
{
@@ -377,6 +482,7 @@ struct kvm_xinterface_ops _xinterface_ops = {
.copy_to = xinterface_copy_to,
.copy_from = xinterface_copy_from,
.vmap = xinterface_vmap,
+ .ioevent = xinterface_ioevent,
.release = xinterface_release,
};


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/