2015-07-06 13:25:15

by Eric Auger

[permalink] [raw]
Subject: [RFC v2 0/9] ARM IRQ forward control based on IRQ bypass manager

This series allows to set ARM IRQ forwarding between a VFIO platform
device physical IRQ and a guest virtual IRQ. the link now is coordinated
by the IRQ bypass manager. This kernel integration deprecates kvm-vfio
approach (https://lkml.org/lkml/2015/4/13/353).

Some rationale about that change can be found in IRQ bypass manager thread:
https://lkml.org/lkml/2015/6/29/268

The principle is the VFIO platform driver registers an IRQ bypass producer
struct on VFIO_IRQ_SET_ACTION_TRIGGER while KVM irqfd registers a consumer
struct on the irqfd assignment. This leads to a handshake based on the
eventfd context (used as token) match.

When either of the producer/consumer module disappears, this leads to
an unregistration and the link is disconnected.

The series transforms the vfio platform driver into an IRQ bypass manager
producer and implements the KVM IRQ bypass callbacks.

Dependencies:
1- [PATCH 00/10] arm/arm64: KVM: Active interrupt state switching for
shared devices (http://www.spinics.net/lists/kvm/msg117411.html)
2- RFC "ARM: Forwarding physical interrupts to a guest VM"
(http://lwn.net/Articles/603514/)
3- [RFC v2 0/6] IRQ bypass manager and irqfd consumer
including Alex's IRQ bypass manager sent by email
(https://lkml.org/lkml/2015/6/29/268)
http://www.spinics.net/lists/kernel/msg2026011.html
4- [RFC v2 0/4] chip/vgic adaptations for forwarded irq
http://lists.infradead.org/pipermail/linux-arm-kernel/2015-February/323183.html

All those pieces can be found at:
https://git.linaro.org/people/eric.auger/linux.git/shortlog/refs/heads/v4.2-rc1-bypass-fwd-v2.1

More backgroung on ARM IRQ forwarding in the text below and at
http://www.linux-kvm.org/images/a/a8/01x04-ARMdevice.pdf.

A forwarded IRQ is deactivated by the guest and not by the host.
When the guest deactivates the associated virtual IRQ, the interrupt
controller automatically completes the physical IRQ. Obviously
this requires some HW support in the interrupt controller. This is
the case for ARM GICv2.

The direct benefit is that, for a level sensitive IRQ, a VM exit
can be avoided on forwarded IRQ completion.

When the IRQ is forwarded, the VFIO platform driver does not need to
mask the physical IRQ anymore before signaling the eventfd. Indeed
genirq lowers the running priority, enabling other physical IRQ to hit
except that one.

Besides, the injection still is based on irqfd triggering. The only
impact on irqfd process is resamplefd is not called anymore on
virtual IRQ completion since deactivation is not trapped by KVM.

This was tested on Calxeda Midway, assigning the xgmac main IRQ

History:

v1 -> v2:
- irq bypass manager and irqfd consumer moved in a separate patch
- kvm_arm_[halt,resume]_guest moved in a separate patch
- remove VFIO external functions since we do not need them anymore
- apply container_of strategy advised by Paolo. Only active field
remains and discussions will tell whether we get rid of it.
- renamed kvm_arch functions

- kvm-vfio v6 -> RFC v1 based on IRQ bypass manager
see previous history in https://lkml.org/lkml/2015/4/13/353).

Best Regards

Eric


Eric Auger (9):
VFIO: platform: registration of a dummy IRQ bypass producer
VFIO: platform: test forwarded state when selecting IRQ handler
VFIO: platform: single handler using function pointer
VFIO: platform: add vfio_platform_set_automasked
VFIO: platform: add vfio_platform_is_active
irq: bypass: add active field on producer side
VFIO: platform: add irq bypass producer management
KVM: arm/arm64: vgic: forwarding control
KVM: arm/arm64: implement IRQ bypass consumer functions

arch/arm/kvm/Kconfig | 1 +
arch/arm/kvm/arm.c | 40 ++++++
arch/arm64/kvm/Kconfig | 1 +
drivers/vfio/platform/vfio_platform_irq.c | 120 +++++++++++++++-
drivers/vfio/platform/vfio_platform_private.h | 3 +
include/kvm/arm_vgic.h | 7 +
include/linux/irqbypass.h | 2 +
virt/kvm/arm/vgic.c | 195 ++++++++++++++++++++++++++
8 files changed, 362 insertions(+), 7 deletions(-)

--
1.9.1


2015-07-06 13:29:27

by Eric Auger

[permalink] [raw]
Subject: [RFC v2 1/9] VFIO: platform: registration of a dummy IRQ bypass producer

Register a dummy producer with void callbacks

Signed-off-by: Eric Auger <[email protected]>
---
drivers/vfio/platform/vfio_platform_irq.c | 31 +++++++++++++++++++++++++++
drivers/vfio/platform/vfio_platform_private.h | 2 ++
2 files changed, 33 insertions(+)

diff --git a/drivers/vfio/platform/vfio_platform_irq.c b/drivers/vfio/platform/vfio_platform_irq.c
index 88bba57..4b059bf 100644
--- a/drivers/vfio/platform/vfio_platform_irq.c
+++ b/drivers/vfio/platform/vfio_platform_irq.c
@@ -20,6 +20,7 @@
#include <linux/types.h>
#include <linux/vfio.h>
#include <linux/irq.h>
+#include <linux/irqbypass.h>

#include "vfio_platform_private.h"

@@ -177,6 +178,26 @@ static irqreturn_t vfio_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}

+static void vfio_platform_irq_bypass_stop(struct irq_bypass_producer *prod)
+{
+}
+
+static void vfio_platform_irq_bypass_resume(struct irq_bypass_producer *prod)
+{
+}
+
+static void vfio_platform_irq_bypass_add_consumer(
+ struct irq_bypass_producer *prod,
+ struct irq_bypass_consumer *cons)
+{
+}
+
+static void vfio_platform_irq_bypass_del_consumer(
+ struct irq_bypass_producer *prod,
+ struct irq_bypass_consumer *cons)
+{
+}
+
static int vfio_set_trigger(struct vfio_platform_device *vdev, int index,
int fd, irq_handler_t handler)
{
@@ -186,6 +207,7 @@ static int vfio_set_trigger(struct vfio_platform_device *vdev, int index,

if (irq->trigger) {
free_irq(irq->hwirq, irq);
+ irq_bypass_unregister_producer(&irq->producer);
kfree(irq->name);
eventfd_ctx_put(irq->trigger);
irq->trigger = NULL;
@@ -216,6 +238,15 @@ static int vfio_set_trigger(struct vfio_platform_device *vdev, int index,
return ret;
}

+ irq->producer.token = (void *)trigger;
+ irq->producer.irq = irq->hwirq;
+ irq->producer.add_consumer = vfio_platform_irq_bypass_add_consumer;
+ irq->producer.del_consumer = vfio_platform_irq_bypass_del_consumer;
+ irq->producer.stop = vfio_platform_irq_bypass_stop;
+ irq->producer.resume = vfio_platform_irq_bypass_resume;
+ ret = irq_bypass_register_producer(&irq->producer);
+ WARN_ON(ret);
+
if (!irq->masked)
enable_irq(irq->hwirq);

diff --git a/drivers/vfio/platform/vfio_platform_private.h b/drivers/vfio/platform/vfio_platform_private.h
index 1c9b3d5..1d2d4d6 100644
--- a/drivers/vfio/platform/vfio_platform_private.h
+++ b/drivers/vfio/platform/vfio_platform_private.h
@@ -17,6 +17,7 @@

#include <linux/types.h>
#include <linux/interrupt.h>
+#include <linux/irqbypass.h>

#define VFIO_PLATFORM_OFFSET_SHIFT 40
#define VFIO_PLATFORM_OFFSET_MASK (((u64)(1) << VFIO_PLATFORM_OFFSET_SHIFT) - 1)
@@ -37,6 +38,7 @@ struct vfio_platform_irq {
spinlock_t lock;
struct virqfd *unmask;
struct virqfd *mask;
+ struct irq_bypass_producer producer;
};

struct vfio_platform_region {
--
1.9.1

2015-07-06 13:25:19

by Eric Auger

[permalink] [raw]
Subject: [RFC v2 2/9] VFIO: platform: test forwarded state when selecting IRQ handler

In case the IRQ is forwarded, the VFIO platform IRQ handler does not
need to disable the IRQ anymore.

When setting the IRQ handler we now also test the forwarded state. In
case the IRQ is forwarded we select the vfio_irq_handler.

Signed-off-by: Eric Auger <[email protected]>

---

v3 -> v4:
- change title

v2 -> v3:
- forwarded state was tested in the handler. Now the forwarded state
is tested before setting the handler. This definitively limits
the dynamics of forwarded state changes but I don't think there is
a use case where we need to be able to change the state at any time.

Conflicts:
drivers/vfio/platform/vfio_platform_irq.c
---
drivers/vfio/platform/vfio_platform_irq.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/vfio/platform/vfio_platform_irq.c b/drivers/vfio/platform/vfio_platform_irq.c
index 4b059bf..e39f795 100644
--- a/drivers/vfio/platform/vfio_platform_irq.c
+++ b/drivers/vfio/platform/vfio_platform_irq.c
@@ -260,8 +260,13 @@ static int vfio_platform_set_irq_trigger(struct vfio_platform_device *vdev,
{
struct vfio_platform_irq *irq = &vdev->irqs[index];
irq_handler_t handler;
+ struct irq_data *d;
+ bool is_forwarded;

- if (vdev->irqs[index].flags & VFIO_IRQ_INFO_AUTOMASKED)
+ d = irq_get_irq_data(irq->hwirq);
+ is_forwarded = irqd_irq_forwarded(d);
+
+ if (vdev->irqs[index].flags & VFIO_IRQ_INFO_AUTOMASKED && !is_forwarded)
handler = vfio_automasked_irq_handler;
else
handler = vfio_irq_handler;
--
1.9.1

2015-07-06 13:28:19

by Eric Auger

[permalink] [raw]
Subject: [RFC v2 3/9] VFIO: platform: single handler using function pointer

A single handler now is registered whatever the use case: automasked
or not. A function pointer is set according to the wished behavior
and the handler calls this function.

The irq lock is taken/released in the root handler. eventfd_signal can
be called in regions not allowed to sleep.

Signed-off-by: Eric Auger <[email protected]>

---

v4: creation
---
drivers/vfio/platform/vfio_platform_irq.c | 21 +++++++++++++++------
drivers/vfio/platform/vfio_platform_private.h | 1 +
2 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/drivers/vfio/platform/vfio_platform_irq.c b/drivers/vfio/platform/vfio_platform_irq.c
index e39f795..7f8d27c 100644
--- a/drivers/vfio/platform/vfio_platform_irq.c
+++ b/drivers/vfio/platform/vfio_platform_irq.c
@@ -148,11 +148,8 @@ static int vfio_platform_set_irq_unmask(struct vfio_platform_device *vdev,
static irqreturn_t vfio_automasked_irq_handler(int irq, void *dev_id)
{
struct vfio_platform_irq *irq_ctx = dev_id;
- unsigned long flags;
int ret = IRQ_NONE;

- spin_lock_irqsave(&irq_ctx->lock, flags);
-
if (!irq_ctx->masked) {
ret = IRQ_HANDLED;

@@ -161,8 +158,6 @@ static irqreturn_t vfio_automasked_irq_handler(int irq, void *dev_id)
irq_ctx->masked = true;
}

- spin_unlock_irqrestore(&irq_ctx->lock, flags);
-
if (ret == IRQ_HANDLED)
eventfd_signal(irq_ctx->trigger, 1);

@@ -178,6 +173,19 @@ static irqreturn_t vfio_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}

+static irqreturn_t vfio_handler(int irq, void *dev_id)
+{
+ struct vfio_platform_irq *irq_ctx = dev_id;
+ unsigned long flags;
+ irqreturn_t ret;
+
+ spin_lock_irqsave(&irq_ctx->lock, flags);
+ ret = irq_ctx->handler(irq, dev_id);
+ spin_unlock_irqrestore(&irq_ctx->lock, flags);
+
+ return ret;
+}
+
static void vfio_platform_irq_bypass_stop(struct irq_bypass_producer *prod)
{
}
@@ -228,9 +236,10 @@ static int vfio_set_trigger(struct vfio_platform_device *vdev, int index,
}

irq->trigger = trigger;
+ irq->handler = handler;

irq_set_status_flags(irq->hwirq, IRQ_NOAUTOEN);
- ret = request_irq(irq->hwirq, handler, 0, irq->name, irq);
+ ret = request_irq(irq->hwirq, vfio_handler, 0, irq->name, irq);
if (ret) {
kfree(irq->name);
eventfd_ctx_put(trigger);
diff --git a/drivers/vfio/platform/vfio_platform_private.h b/drivers/vfio/platform/vfio_platform_private.h
index 1d2d4d6..9737981 100644
--- a/drivers/vfio/platform/vfio_platform_private.h
+++ b/drivers/vfio/platform/vfio_platform_private.h
@@ -39,6 +39,7 @@ struct vfio_platform_irq {
struct virqfd *unmask;
struct virqfd *mask;
struct irq_bypass_producer producer;
+ irqreturn_t (*handler)(int irq, void *dev_id);
};

struct vfio_platform_region {
--
1.9.1

2015-07-06 13:27:55

by Eric Auger

[permalink] [raw]
Subject: [RFC v2 4/9] VFIO: platform: add vfio_platform_set_automasked

This function makes possible to change the automasked mode.

Signed-off-by: Eric Auger <[email protected]>

---
---
drivers/vfio/platform/vfio_platform_irq.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)

diff --git a/drivers/vfio/platform/vfio_platform_irq.c b/drivers/vfio/platform/vfio_platform_irq.c
index 7f8d27c..aa540db 100644
--- a/drivers/vfio/platform/vfio_platform_irq.c
+++ b/drivers/vfio/platform/vfio_platform_irq.c
@@ -186,6 +186,23 @@ static irqreturn_t vfio_handler(int irq, void *dev_id)
return ret;
}

+static int vfio_platform_set_automasked(struct vfio_platform_irq *irq,
+ bool automasked)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&irq->lock, flags);
+ if (automasked) {
+ irq->flags |= VFIO_IRQ_INFO_AUTOMASKED;
+ irq->handler = vfio_automasked_irq_handler;
+ } else {
+ irq->flags &= ~VFIO_IRQ_INFO_AUTOMASKED;
+ irq->handler = vfio_irq_handler;
+ }
+ spin_unlock_irqrestore(&irq->lock, flags);
+ return 0;
+}
+
static void vfio_platform_irq_bypass_stop(struct irq_bypass_producer *prod)
{
}
--
1.9.1

2015-07-06 13:25:36

by Eric Auger

[permalink] [raw]
Subject: [RFC v2 5/9] VFIO: platform: add vfio_platform_is_active

This function returns whether the IRQ is active at irqchip level or
VFIO masked. If either is true, it is considered the IRQ is active.
Currently there is no way to differentiate userspace masked IRQ from
automasked IRQ. There might be false detection of activity. However
it is currently acceptable to have false detection.

Signed-off-by: Eric Auger <[email protected]>

---
---
drivers/vfio/platform/vfio_platform_irq.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)

diff --git a/drivers/vfio/platform/vfio_platform_irq.c b/drivers/vfio/platform/vfio_platform_irq.c
index aa540db..d00ed24 100644
--- a/drivers/vfio/platform/vfio_platform_irq.c
+++ b/drivers/vfio/platform/vfio_platform_irq.c
@@ -203,6 +203,23 @@ static int vfio_platform_set_automasked(struct vfio_platform_irq *irq,
return 0;
}

+static int vfio_platform_is_active(struct vfio_platform_irq *irq)
+{
+ unsigned long flags;
+ bool active, masked, outstanding;
+ int ret;
+
+ spin_lock_irqsave(&irq->lock, flags);
+
+ ret = irq_get_irqchip_state(irq->hwirq, IRQCHIP_STATE_ACTIVE, &active);
+ BUG_ON(ret);
+ masked = irq->masked;
+ outstanding = active || masked;
+
+ spin_unlock_irqrestore(&irq->lock, flags);
+ return outstanding;
+}
+
static void vfio_platform_irq_bypass_stop(struct irq_bypass_producer *prod)
{
}
--
1.9.1

2015-07-06 13:25:28

by Eric Auger

[permalink] [raw]
Subject: [RFC v2 6/9] irq: bypass: add active field on producer side

An active boolean is introduced on producer side: it reflects whether
the source is active (at interrupt controller level or at VFIO level
- automasked -).

This is a temporary hack for ARM IRQ forwarding with vfio platform.
I think the connect and disconnect should become callbacks too.
This would make possible to handle error in the process. Typically
active could be returned by producer->stop and analyzed before going
further. in that case the list_add only if connect returns 0.

Signed-off-by: Eric Auger <[email protected]>
---
include/linux/irqbypass.h | 2 ++
1 file changed, 2 insertions(+)

diff --git a/include/linux/irqbypass.h b/include/linux/irqbypass.h
index 8f62235..c89c3a8 100644
--- a/include/linux/irqbypass.h
+++ b/include/linux/irqbypass.h
@@ -9,6 +9,8 @@ struct irq_bypass_producer {
struct list_head node;
void *token;
int irq; /* linux irq */
+ /* is irq active at irqchip or VFIO masked? */
+ bool active;
void (*stop)(struct irq_bypass_producer *);
void (*resume)(struct irq_bypass_producer *);
void (*add_consumer)(struct irq_bypass_producer *,
--
1.9.1

2015-07-06 13:25:31

by Eric Auger

[permalink] [raw]
Subject: [RFC v2 7/9] VFIO: platform: add irq bypass producer management

This patch populates the IRQ bypass callacks:
- stop/resume producer simply consist in disabling/enabling the host irq
- add/del consumer: basically set the automasked flag to false/true
- update remains void

Signed-off-by: Eric Auger <[email protected]>

---

v1 -> v2:
- device handle caching in vfio_platform_device is introduced in a
separate patch
- use container_of
---
drivers/vfio/platform/vfio_platform_irq.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)

diff --git a/drivers/vfio/platform/vfio_platform_irq.c b/drivers/vfio/platform/vfio_platform_irq.c
index d00ed24..7dc17a6 100644
--- a/drivers/vfio/platform/vfio_platform_irq.c
+++ b/drivers/vfio/platform/vfio_platform_irq.c
@@ -222,22 +222,49 @@ static int vfio_platform_is_active(struct vfio_platform_irq *irq)

static void vfio_platform_irq_bypass_stop(struct irq_bypass_producer *prod)
{
+ disable_irq(prod->irq);
}

static void vfio_platform_irq_bypass_resume(struct irq_bypass_producer *prod)
{
+ enable_irq(prod->irq);
}

static void vfio_platform_irq_bypass_add_consumer(
struct irq_bypass_producer *prod,
struct irq_bypass_consumer *cons)
{
+ int ret;
+ struct vfio_platform_irq *irq =
+ container_of(prod, struct vfio_platform_irq, producer);
+
+ //TODO
+ /*
+ * if the IRQ is active at irqchip level or VFIO (auto)masked
+ * this means the host IRQ is already under injection in the
+ * guest and this not safe to change the forwarding state at
+ * that stage.
+ * It is not possible to differentiate user-space masking
+ * from auto-masking, leading to possible false detection of
+ * active state.
+ */
+ prod->active = vfio_platform_is_active(irq);
+
+ ret = vfio_platform_set_automasked(irq, false);
+ WARN_ON(ret);
}

static void vfio_platform_irq_bypass_del_consumer(
struct irq_bypass_producer *prod,
struct irq_bypass_consumer *cons)
{
+ struct vfio_platform_irq *irq =
+ container_of(prod, struct vfio_platform_irq, producer);
+
+ if (prod->active)
+ vfio_platform_mask(irq);
+
+ vfio_platform_set_automasked(irq, true);
}

static int vfio_set_trigger(struct vfio_platform_device *vdev, int index,
--
1.9.1

2015-07-06 13:26:50

by Eric Auger

[permalink] [raw]
Subject: [RFC v2 8/9] KVM: arm/arm64: vgic: forwarding control

Implements kvm_vgic_[set|unset]_forward.

Handle low-level VGIC programming: physical IRQ/guest IRQ mapping,
list register cleanup, VGIC state machine. Also interacts with
the irqchip.

Signed-off-by: Eric Auger <[email protected]>

---

bypass rfc:
- rename kvm_arch_{set|unset}_forward into
kvm_vgic_{set|unset}_forward. Remove __KVM_HAVE_ARCH_HALT_GUEST.
The function is bound to be called by ARM code only.

v4 -> v5:
- fix arm64 compilation issues, ie. also defines
__KVM_HAVE_ARCH_HALT_GUEST for arm64

v3 -> v4:
- code originally located in kvm_vfio_arm.c
- kvm_arch_vfio_{set|unset}_forward renamed into
kvm_arch_{set|unset}_forward
- split into 2 functions (set/unset) since unset does not fail anymore
- unset can be invoked at whatever time. Extra care is taken to handle
transition in VGIC state machine, LR cleanup, ...

v2 -> v3:
- renaming of kvm_arch_set_fwd_state into kvm_arch_vfio_set_forward
- takes a bool arg instead of kvm_fwd_irq_action enum
- removal of KVM_VFIO_IRQ_CLEANUP
- platform device check now happens here
- more precise errors returned
- irq_eoi handled externally to this patch (VGIC)
- correct enable_irq bug done twice
- reword the commit message
- correct check of platform_bus_type
- use raw_spin_lock_irqsave and check the validity of the handler
---
include/kvm/arm_vgic.h | 7 ++
virt/kvm/arm/vgic.c | 195 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 202 insertions(+)

diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 5d47d60..93b379f 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -353,6 +353,13 @@ int vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, struct irq_phys_map *map);
bool vgic_get_phys_irq_active(struct irq_phys_map *map);
void vgic_set_phys_irq_active(struct irq_phys_map *map, bool active);

+int kvm_vgic_set_forward(struct kvm *kvm,
+ unsigned int host_irq, unsigned int guest_irq);
+
+void kvm_vgic_unset_forward(struct kvm *kvm,
+ unsigned int host_irq, unsigned int guest_irq,
+ bool *active);
+
#define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel))
#define vgic_initialized(k) (!!((k)->arch.vgic.nr_cpus))
#define vgic_ready(k) ((k)->arch.vgic.ready)
diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c
index eef35d9..9efc839 100644
--- a/virt/kvm/arm/vgic.c
+++ b/virt/kvm/arm/vgic.c
@@ -2402,3 +2402,198 @@ int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
{
return 0;
}
+
+/**
+ * kvm_vgic_set_forward - Set IRQ forwarding
+ *
+ * @kvm: handle to the VM
+ * @host_irq: physical IRQ number
+ * @guest_irq: virtual IRQ number
+ *
+ * This function is supposed to be called only if the IRQ
+ * is not in progress: ie. not active at GIC level and not
+ * currently under injection in the KVM. The physical IRQ must
+ * also be disabled and the guest must have been exited and
+ * prevented from being re-entered.
+ */
+int kvm_vgic_set_forward(struct kvm *kvm,
+ unsigned int host_irq,
+ unsigned int guest_irq)
+{
+ struct irq_desc *desc = irq_to_desc(host_irq);
+ struct irq_phys_map *map = NULL;
+ struct irq_data *d;
+ unsigned long flags;
+ struct kvm_vcpu *vcpu = kvm_get_vcpu(kvm, 0);
+ int spi_id = guest_irq + VGIC_NR_PRIVATE_IRQS;
+ struct vgic_dist *dist = &kvm->arch.vgic;
+
+ kvm_debug("%s host_irq=%d guest_irq=%d\n",
+ __func__, host_irq, guest_irq);
+
+ if (!vcpu)
+ return 0;
+
+ spin_lock(&dist->lock);
+
+ raw_spin_lock_irqsave(&desc->lock, flags);
+ d = &desc->irq_data;
+ irqd_set_irq_forwarded(d);
+ /*
+ * next physical IRQ will be be handled as forwarded
+ * by the host (priority drop only)
+ */
+
+ raw_spin_unlock_irqrestore(&desc->lock, flags);
+
+ /*
+ * need to release the dist spin_lock here since
+ * vgic_map_phys_irq can sleep
+ */
+ spin_unlock(&dist->lock);
+ map = vgic_map_phys_irq(vcpu, spi_id, host_irq, false);
+ /*
+ * next guest_irq injection will be considered as
+ * forwarded and next flush will program LR
+ * without maintenance IRQ but with HW bit set
+ */
+ return !map;
+}
+
+/**
+ * kvm_vgic_unset_forward - Unset IRQ forwarding
+ *
+ * @kvm: handle to the VM
+ * @host_irq: physical IRQ number
+ * @guest_irq: virtual IRQ number
+ * @active: returns whether the physical IRQ is active
+ *
+ * This function must be called when the host_irq is disabled
+ * and guest has been exited and prevented from being re-entered.
+ *
+ */
+void kvm_vgic_unset_forward(struct kvm *kvm,
+ unsigned int host_irq,
+ unsigned int guest_irq,
+ bool *active)
+{
+ struct kvm_vcpu *vcpu = kvm_get_vcpu(kvm, 0);
+ struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+ struct vgic_dist *dist = &kvm->arch.vgic;
+ int ret, lr;
+ struct vgic_lr vlr;
+ struct irq_desc *desc = irq_to_desc(host_irq);
+ struct irq_data *d;
+ unsigned long flags;
+ int spi_id = guest_irq + VGIC_NR_PRIVATE_IRQS;
+ struct irq_chip *chip;
+ bool queued, needs_deactivate = true;
+ struct irq_phys_map *map;
+
+ kvm_debug("%s host_irq=%d guest_irq=%d\n",
+ __func__, host_irq, guest_irq);
+
+ spin_lock(&dist->lock);
+
+ irq_get_irqchip_state(host_irq, IRQCHIP_STATE_ACTIVE, active);
+
+ if (!vcpu)
+ goto out;
+
+ map = vgic_irq_map_search(vcpu, spi_id);
+ BUG_ON(!map);
+ ret = vgic_unmap_phys_irq(vcpu, map);
+ BUG_ON(ret);
+ /*
+ * subsequent update_irq_pending or flush will handle this
+ * irq as forwarded
+ */
+
+ if (likely(!(*active))) {
+ /*
+ * the IRQ was not active. It may happen the handle_fasteoi_irq
+ * is entered afterwards due to lazy disable_irq. If this
+ * happens the IRQ will be deactivated and never be injected.
+ * let's simply prepare the states for subsequent non forwarded
+ * injection
+ */
+ vgic_dist_irq_clear_level(vcpu, spi_id);
+ vgic_dist_irq_clear_pending(vcpu, spi_id);
+ vgic_irq_clear_queued(vcpu, spi_id);
+ needs_deactivate = false;
+ goto out;
+ }
+
+ /* is there any list register with valid state? */
+ lr = vgic_cpu->vgic_irq_lr_map[spi_id];
+ queued = false;
+ if (lr != LR_EMPTY) {
+ vlr = vgic_get_lr(vcpu, lr);
+ if (vlr.state & LR_STATE_MASK)
+ queued = true;
+ }
+
+ if (!queued) {
+ vgic_irq_clear_queued(vcpu, spi_id);
+ if (vgic_dist_irq_is_pending(vcpu, spi_id)) {
+ /*
+ * IRQ is injected but not yet queued. LR will be
+ * written with EOI_INT and process_maintenance will
+ * reset the states: queued, level(resampler). Pending
+ * will be reset on flush.
+ */
+ vgic_dist_irq_set_level(vcpu, spi_id);
+ } else {
+ /*
+ * We are before the injection (update_irq_pending).
+ * This is the most tricky window. Due to the usage of
+ * disable_irq in generic unforward part (mandated by
+ * resamplefd unmask VFIO integration), there is a risk
+ * the fasteoi handler returns without calling the VFIO
+ * handler and deactivates the physical IRQ (lazy
+ * disable). Hence we cannot know whether the IRQ will
+ * ever be injected. The only solution found is to do as
+ * if the IRQ were not active. The active parameter
+ * typically is used by the caller to know whether
+ * it needs to mask * the IRQ. If not duly masked,
+ * another physical IRQ may hit again while the previous
+ * virtual IRQ is in progress. Update_irq_pending
+ * validate_injection will prevent this injection.
+ */
+ vgic_dist_irq_clear_level(vcpu, spi_id);
+ *active = false;
+ }
+ goto out;
+ }
+
+ /*
+ * the virtual IRQ is queued and a valid LR exists, let's patch it for
+ * EOI maintenance
+ */
+ vlr.state |= LR_EOI_INT;
+ vgic_set_lr(vcpu, lr, vlr);
+ /*
+ * we expect a maintenance IRQ which will reset the
+ * queued, pending and level states
+ */
+ vgic_dist_irq_set_level(vcpu, spi_id);
+ vgic_dist_irq_set_pending(vcpu, spi_id);
+ vgic_irq_set_queued(vcpu, spi_id);
+
+out:
+
+ raw_spin_lock_irqsave(&desc->lock, flags);
+ d = irq_desc_get_irq_data(desc);
+ if (needs_deactivate) {
+ chip = irq_data_get_irq_chip(d);
+ chip->irq_eoi(d);
+ }
+ irqd_clr_irq_forwarded(d);
+ /* next occurrence will be deactivated by the host */
+ raw_spin_unlock_irqrestore(&desc->lock, flags);
+
+ *active = *active && vcpu;
+
+ spin_unlock(&dist->lock);
+}
+
--
1.9.1

2015-07-06 13:25:41

by Eric Auger

[permalink] [raw]
Subject: [RFC v2 9/9] KVM: arm/arm64: implement IRQ bypass consumer functions

Implement IRQ bypass callbacks for arm/arm64 IRQ forwarding:
- kvm_arch_irq_bypass_add_producer: perform VGIC/irqchip
settings for forwarding
- kvm_arch_irq_bypass_del_producer: same for inverse operation
- kvm_arch_irq_bypass_stop: halt guest execution
- kvm_arch_irq_bypass_resume: resume guest execution
- void kvm_arch_irq_bypass_update

and set CONFIG_HAVE_KVM_IRQ_BYPASS for arm/arm64.

Signed-off-by: Eric Auger <[email protected]>

---

v1 -> v2:
- struct kvm_kernel_irqfd is retrieved with container_of
- function names changed
---
arch/arm/kvm/Kconfig | 1 +
arch/arm/kvm/arm.c | 40 ++++++++++++++++++++++++++++++++++++++++
arch/arm64/kvm/Kconfig | 1 +
3 files changed, 42 insertions(+)

diff --git a/arch/arm/kvm/Kconfig b/arch/arm/kvm/Kconfig
index 7d38d25..19114a1 100644
--- a/arch/arm/kvm/Kconfig
+++ b/arch/arm/kvm/Kconfig
@@ -32,6 +32,7 @@ config KVM
select HAVE_KVM_EVENTFD
select HAVE_KVM_IRQFD
select IRQ_BYPASS_MANAGER
+ select HAVE_KVM_IRQ_BYPASS
depends on ARM_VIRT_EXT && ARM_LPAE && ARM_ARCH_TIMER
---help---
Support hosting virtualized guest machines.
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index 46d4ef6..db8f040 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -27,6 +27,8 @@
#include <linux/mman.h>
#include <linux/sched.h>
#include <linux/kvm.h>
+#include <linux/kvm_irqfd.h>
+#include <linux/irqbypass.h>
#include <trace/events/kvm.h>

#define CREATE_TRACE_POINTS
@@ -1149,6 +1151,44 @@ struct kvm_vcpu *kvm_mpidr_to_vcpu(struct kvm *kvm, unsigned long mpidr)
return NULL;
}

+void kvm_arch_irq_bypass_add_producer(struct irq_bypass_consumer *cons,
+ struct irq_bypass_producer *prod)
+{
+ struct kvm_kernel_irqfd *irqfd =
+ container_of(cons, struct kvm_kernel_irqfd, consumer);
+
+ kvm_vgic_set_forward(irqfd->kvm, prod->irq, irqfd->gsi);
+}
+void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons,
+ struct irq_bypass_producer *prod)
+{
+ struct kvm_kernel_irqfd *irqfd =
+ container_of(cons, struct kvm_kernel_irqfd, consumer);
+
+ kvm_vgic_unset_forward(irqfd->kvm, prod->irq, irqfd->gsi,
+ &prod->active);
+}
+
+void kvm_arch_irq_bypass_stop(struct irq_bypass_consumer *cons)
+{
+ struct kvm_kernel_irqfd *irqfd =
+ container_of(cons, struct kvm_kernel_irqfd, consumer);
+
+ kvm_arm_halt_guest(irqfd->kvm);
+}
+
+void kvm_arch_irq_bypass_resume(struct irq_bypass_consumer *cons)
+{
+ struct kvm_kernel_irqfd *irqfd =
+ container_of(cons, struct kvm_kernel_irqfd, consumer);
+
+ kvm_arm_resume_guest(irqfd->kvm);
+}
+
+void kvm_arch_irq_bypass_update(struct irq_bypass_consumer *cons)
+{
+}
+
/**
* Initialize Hyp-mode and memory mappings on all CPUs.
*/
diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig
index dfaca85..822551c 100644
--- a/arch/arm64/kvm/Kconfig
+++ b/arch/arm64/kvm/Kconfig
@@ -32,6 +32,7 @@ config KVM
select HAVE_KVM_EVENTFD
select HAVE_KVM_IRQFD
select IRQ_BYPASS_MANAGER
+ select HAVE_KVM_IRQ_BYPASS
---help---
Support hosting virtualized guest machines.

--
1.9.1