diff mbox

[4/4] arm: vgic device control api support

Message ID 1377286862-5879-5-git-send-email-christoffer.dall@linaro.org
State New
Headers show

Commit Message

Christoffer Dall Aug. 23, 2013, 7:41 p.m. UTC
Support creating the ARM vgic device through the device control API and
setting the base address for the distributor and cpu interfaces in KVM
VMs using this API.

Because the older KVM_CREATE_IRQCHIP interface needs the irq chip to be
created prior to creating the VCPUs, we first test if if can use the
device control API in kvm_arch_irqchip_create (using the test flag from
the device control API).  If we cannot, it means we have to fall back to
KVM_CREATE_IRQCHIP and use the older ioctl at this point in time.  If
however, we can use the device control API, we don't do anything and
wait until the arm_gic_kvm driver initializes and let that use the
device control API.

Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
---
 hw/intc/arm_gic_kvm.c |   23 +++++++++++++++++++++--
 target-arm/kvm.c      |   49 ++++++++++++++++++++++++++++++++++++++++++-------
 target-arm/kvm_arm.h  |   18 ++++++++++++------
 3 files changed, 75 insertions(+), 15 deletions(-)

Comments

Peter Maydell Sept. 6, 2013, 1:34 p.m. UTC | #1
On 23 August 2013 20:41, Christoffer Dall <christoffer.dall@linaro.org> wrote:
> Support creating the ARM vgic device through the device control API and
> setting the base address for the distributor and cpu interfaces in KVM
> VMs using this API.
>
> Because the older KVM_CREATE_IRQCHIP interface needs the irq chip to be
> created prior to creating the VCPUs, we first test if if can use the

"if we"

> device control API in kvm_arch_irqchip_create (using the test flag from
> the device control API).  If we cannot, it means we have to fall back to
> KVM_CREATE_IRQCHIP and use the older ioctl at this point in time.  If
> however, we can use the device control API, we don't do anything and
> wait until the arm_gic_kvm driver initializes and let that use the
> device control API.
>
> Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
> ---
>  hw/intc/arm_gic_kvm.c |   23 +++++++++++++++++++++--
>  target-arm/kvm.c      |   49 ++++++++++++++++++++++++++++++++++++++++++-------
>  target-arm/kvm_arm.h  |   18 ++++++++++++------
>  3 files changed, 75 insertions(+), 15 deletions(-)
>
> diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c
> index f713975..9f0a852 100644
> --- a/hw/intc/arm_gic_kvm.c
> +++ b/hw/intc/arm_gic_kvm.c
> @@ -35,6 +35,7 @@ typedef struct KVMARMGICClass {
>      ARMGICCommonClass parent_class;
>      DeviceRealize parent_realize;
>      void (*parent_reset)(DeviceState *dev);
> +    int dev_fd;
>  } KVMARMGICClass;

This looks wrong -- surely we should have a dev_fd per
instance of KVMARMGIC, not just one in the class struct?

>  static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
> @@ -97,6 +98,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
>      GICState *s = KVM_ARM_GIC(dev);
>      SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
>      KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
> +    int ret;
>
>      kgc->parent_realize(dev, errp);
>      if (error_is_set(errp)) {
> @@ -119,13 +121,27 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
>      for (i = 0; i < s->num_cpu; i++) {
>          sysbus_init_irq(sbd, &s->parent_irq[i]);
>      }
> +
> +    /* Try to create the device via the device control API */
> +    kgc->dev_fd = -1;
> +    ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V2, false);
> +    if (ret >= 0) {
> +        kgc->dev_fd = ret;
> +    } else if (ret != -ENODEV) {
> +        fprintf(stderr, "Error creating in-kernel VGIC: %d\n", ret);
> +        abort();

We shouldn't abort here, we can just report our failure
back to the caller via the Error** it passed us:

    error_setg_errno(errp, -ret, "error creating in-kernel VGIC");
    return;

(there's also an error_setg() if there's no errno involved;
both versions use a printf-style format string and can take
extra args accordingly.)

> +    }
> +
>      /* Distributor */
>      memory_region_init_reservation(&s->iomem, OBJECT(s),
>                                     "kvm-gic_dist", 0x1000);
>      sysbus_init_mmio(sbd, &s->iomem);
>      kvm_arm_register_device(&s->iomem,
>                              (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
> -                            | KVM_VGIC_V2_ADDR_TYPE_DIST);
> +                            | KVM_VGIC_V2_ADDR_TYPE_DIST,
> +                            KVM_DEV_ARM_VGIC_GRP_ADDR,
> +                            KVM_VGIC_V2_ADDR_TYPE_DIST,
> +                            kgc->dev_fd);
>      /* CPU interface for current core. Unlike arm_gic, we don't
>       * provide the "interface for core #N" memory regions, because
>       * cores with a VGIC don't have those.
> @@ -135,7 +151,10 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
>      sysbus_init_mmio(sbd, &s->cpuiomem[0]);
>      kvm_arm_register_device(&s->cpuiomem[0],
>                              (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
> -                            | KVM_VGIC_V2_ADDR_TYPE_CPU);
> +                            | KVM_VGIC_V2_ADDR_TYPE_CPU,
> +                            KVM_DEV_ARM_VGIC_GRP_ADDR,
> +                            KVM_VGIC_V2_ADDR_TYPE_CPU,
> +                            kgc->dev_fd);
>  }
>
>  static void kvm_arm_gic_class_init(ObjectClass *klass, void *data)
> diff --git a/target-arm/kvm.c b/target-arm/kvm.c
> index 2484d90..aed3d86 100644
> --- a/target-arm/kvm.c
> +++ b/target-arm/kvm.c
> @@ -184,8 +184,10 @@ out:
>   */
>  typedef struct KVMDevice {
>      struct kvm_arm_device_addr kda;
> +    struct kvm_device_attr kdattr;
>      MemoryRegion *mr;
>      QSLIST_ENTRY(KVMDevice) entries;
> +    int dev_fd;
>  } KVMDevice;
>
>  static QSLIST_HEAD(kvm_devices_head, KVMDevice) kvm_devices_head;
> @@ -219,6 +221,28 @@ static MemoryListener devlistener = {
>      .region_del = kvm_arm_devlistener_del,
>  };
>
> +static void kvm_arm_set_device_addr(KVMDevice *kd)
> +{
> +    struct kvm_device_attr *attr = &kd->kdattr;
> +    int ret;
> +
> +    /* If the device control API is available and we have a device fd on the
> +     * KVMDevice struct, let's use the newer API */

putting the closing */ on a line of its own fits the style
in the rest of this file (and looks nicer imho ;-))


> +    if (kd->dev_fd >= 0) {
> +        uint64_t addr = kd->kda.addr;
> +        attr->addr = (uint64_t)(long)&addr;

why (uint64_t)(long)? Other places (like the get/put register
code) where we need to fill in an address into a uint64_t
field in a kernel struct we do with a simple
   attr->addr = (uintptr_t)&addr;

> +        ret = kvm_device_ioctl(kd->dev_fd, KVM_SET_DEVICE_ATTR, attr);
> +    } else {
> +        ret = kvm_vm_ioctl(kvm_state, KVM_ARM_SET_DEVICE_ADDR, &kd->kda);
> +    }
> +
> +    if (ret < 0) {
> +            fprintf(stderr, "Failed to set device address: %s\n",
> +                    strerror(errno));

Your error code is in -ret here, not in errno.

> +            abort();
> +    }
> +}

Otherwise looks OK.

thanks
-- PMM
Christoffer Dall Sept. 13, 2013, 5:15 a.m. UTC | #2
On Fri, Sep 06, 2013 at 02:34:40PM +0100, Peter Maydell wrote:
> On 23 August 2013 20:41, Christoffer Dall <christoffer.dall@linaro.org> wrote:
> > Support creating the ARM vgic device through the device control API and
> > setting the base address for the distributor and cpu interfaces in KVM
> > VMs using this API.
> >
> > Because the older KVM_CREATE_IRQCHIP interface needs the irq chip to be
> > created prior to creating the VCPUs, we first test if if can use the
> 
> "if we"
> 
Peter,

Thanks for your review comments and pointers on how to fix.  Much
appreciated.  I've addressed your comments and will get a new series out
asap.

-Christoffer
diff mbox

Patch

diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c
index f713975..9f0a852 100644
--- a/hw/intc/arm_gic_kvm.c
+++ b/hw/intc/arm_gic_kvm.c
@@ -35,6 +35,7 @@  typedef struct KVMARMGICClass {
     ARMGICCommonClass parent_class;
     DeviceRealize parent_realize;
     void (*parent_reset)(DeviceState *dev);
+    int dev_fd;
 } KVMARMGICClass;
 
 static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
@@ -97,6 +98,7 @@  static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
     GICState *s = KVM_ARM_GIC(dev);
     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
     KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
+    int ret;
 
     kgc->parent_realize(dev, errp);
     if (error_is_set(errp)) {
@@ -119,13 +121,27 @@  static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
     for (i = 0; i < s->num_cpu; i++) {
         sysbus_init_irq(sbd, &s->parent_irq[i]);
     }
+
+    /* Try to create the device via the device control API */
+    kgc->dev_fd = -1;
+    ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V2, false);
+    if (ret >= 0) {
+        kgc->dev_fd = ret;
+    } else if (ret != -ENODEV) {
+        fprintf(stderr, "Error creating in-kernel VGIC: %d\n", ret);
+        abort();
+    }
+
     /* Distributor */
     memory_region_init_reservation(&s->iomem, OBJECT(s),
                                    "kvm-gic_dist", 0x1000);
     sysbus_init_mmio(sbd, &s->iomem);
     kvm_arm_register_device(&s->iomem,
                             (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
-                            | KVM_VGIC_V2_ADDR_TYPE_DIST);
+                            | KVM_VGIC_V2_ADDR_TYPE_DIST,
+                            KVM_DEV_ARM_VGIC_GRP_ADDR,
+                            KVM_VGIC_V2_ADDR_TYPE_DIST,
+                            kgc->dev_fd);
     /* CPU interface for current core. Unlike arm_gic, we don't
      * provide the "interface for core #N" memory regions, because
      * cores with a VGIC don't have those.
@@ -135,7 +151,10 @@  static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
     sysbus_init_mmio(sbd, &s->cpuiomem[0]);
     kvm_arm_register_device(&s->cpuiomem[0],
                             (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
-                            | KVM_VGIC_V2_ADDR_TYPE_CPU);
+                            | KVM_VGIC_V2_ADDR_TYPE_CPU,
+                            KVM_DEV_ARM_VGIC_GRP_ADDR,
+                            KVM_VGIC_V2_ADDR_TYPE_CPU,
+                            kgc->dev_fd);
 }
 
 static void kvm_arm_gic_class_init(ObjectClass *klass, void *data)
diff --git a/target-arm/kvm.c b/target-arm/kvm.c
index 2484d90..aed3d86 100644
--- a/target-arm/kvm.c
+++ b/target-arm/kvm.c
@@ -184,8 +184,10 @@  out:
  */
 typedef struct KVMDevice {
     struct kvm_arm_device_addr kda;
+    struct kvm_device_attr kdattr;
     MemoryRegion *mr;
     QSLIST_ENTRY(KVMDevice) entries;
+    int dev_fd;
 } KVMDevice;
 
 static QSLIST_HEAD(kvm_devices_head, KVMDevice) kvm_devices_head;
@@ -219,6 +221,28 @@  static MemoryListener devlistener = {
     .region_del = kvm_arm_devlistener_del,
 };
 
+static void kvm_arm_set_device_addr(KVMDevice *kd)
+{
+    struct kvm_device_attr *attr = &kd->kdattr;
+    int ret;
+
+    /* If the device control API is available and we have a device fd on the
+     * KVMDevice struct, let's use the newer API */
+    if (kd->dev_fd >= 0) {
+        uint64_t addr = kd->kda.addr;
+        attr->addr = (uint64_t)(long)&addr;
+        ret = kvm_device_ioctl(kd->dev_fd, KVM_SET_DEVICE_ATTR, attr);
+    } else {
+        ret = kvm_vm_ioctl(kvm_state, KVM_ARM_SET_DEVICE_ADDR, &kd->kda);
+    }
+
+    if (ret < 0) {
+            fprintf(stderr, "Failed to set device address: %s\n",
+                    strerror(errno));
+            abort();
+    }
+}
+
 static void kvm_arm_machine_init_done(Notifier *notifier, void *data)
 {
     KVMDevice *kd, *tkd;
@@ -226,12 +250,7 @@  static void kvm_arm_machine_init_done(Notifier *notifier, void *data)
     memory_listener_unregister(&devlistener);
     QSLIST_FOREACH_SAFE(kd, &kvm_devices_head, entries, tkd) {
         if (kd->kda.addr != -1) {
-            if (kvm_vm_ioctl(kvm_state, KVM_ARM_SET_DEVICE_ADDR,
-                             &kd->kda) < 0) {
-                fprintf(stderr, "KVM_ARM_SET_DEVICE_ADDRESS failed: %s\n",
-                        strerror(errno));
-                abort();
-            }
+            kvm_arm_set_device_addr(kd);
         }
         memory_region_unref(kd->mr);
         g_free(kd);
@@ -242,7 +261,8 @@  static Notifier notify = {
     .notify = kvm_arm_machine_init_done,
 };
 
-void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid)
+void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group,
+                             uint64_t attr, int dev_fd)
 {
     KVMDevice *kd;
 
@@ -258,6 +278,10 @@  void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid)
     kd->mr = mr;
     kd->kda.id = devid;
     kd->kda.addr = -1;
+    kd->kdattr.flags = 0;
+    kd->kdattr.group = group;
+    kd->kdattr.attr = attr;
+    kd->dev_fd = dev_fd;
     QSLIST_INSERT_HEAD(&kvm_devices_head, kd, entries);
     memory_region_ref(kd->mr);
 }
@@ -650,5 +674,16 @@  void kvm_arch_init_irq_routing(KVMState *s)
 
 int kvm_arch_irqchip_create(KVMState *s)
 {
+    int ret;
+
+    /* If we can create the VGIC using the newer device control API, we
+     * let the device do this when it initializes itself, otherwise we
+     * fall back to the old API */
+
+    ret = kvm_create_device(s, KVM_DEV_TYPE_ARM_VGIC_V2, true);
+    if (ret == 0) {
+        return 1;
+    }
+
     return 0;
 }
diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h
index 5d14887..ea1805a 100644
--- a/target-arm/kvm_arm.h
+++ b/target-arm/kvm_arm.h
@@ -18,16 +18,22 @@ 
  * kvm_arm_register_device:
  * @mr: memory region for this device
  * @devid: the KVM device ID
+ * @type: device control API device type
+ * @group: device control API group for setting addresses
+ * @attr: device control API address type
+ * @dev_fd: device control device file descriptor (or -1 if not supported)
  *
  * Remember the memory region @mr, and when it is mapped by the
  * machine model, tell the kernel that base address using the
- * KVM_SET_DEVICE_ADDRESS ioctl. @devid should be the ID of
- * the device as defined by KVM_SET_DEVICE_ADDRESS.
- * The machine model may map and unmap the device multiple times;
- * the kernel will only be told the final address at the point
- * where machine init is complete.
+ * KVM_ARM_SET_DEVICE_ADDRESS ioctl or the newer device control API.  @devid
+ * should be the ID of the device as defined by KVM_ARM_SET_DEVICE_ADDRESS or
+ * the arm-vgic device in the device control API.
+ * The machine model may map
+ * and unmap the device multiple times; the kernel will only be told the final
+ * address at the point where machine init is complete.
  */
-void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid);
+void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group,
+                             uint64_t attr, int dev_fd);
 
 /**
  * write_list_to_kvmstate: