|
23 | 23 |
|
24 | 24 | #include "vgic.h"
|
25 | 25 |
|
| 26 | +/* |
| 27 | + * How KVM uses GICv4 (insert rude comments here): |
| 28 | + * |
| 29 | + * The vgic-v4 layer acts as a bridge between several entities: |
| 30 | + * - The GICv4 ITS representation offered by the ITS driver |
| 31 | + * - VFIO, which is in charge of the PCI endpoint |
| 32 | + * - The virtual ITS, which is the only thing the guest sees |
| 33 | + * |
| 34 | + * The configuration of VLPIs is triggered by a callback from VFIO, |
| 35 | + * instructing KVM that a PCI device has been configured to deliver |
| 36 | + * MSIs to a vITS. |
| 37 | + * |
| 38 | + * kvm_vgic_v4_set_forwarding() is thus called with the routing entry, |
| 39 | + * and this is used to find the corresponding vITS data structures |
| 40 | + * (ITS instance, device, event and irq) using a process that is |
| 41 | + * extremely similar to the injection of an MSI. |
| 42 | + * |
| 43 | + * At this stage, we can link the guest's view of an LPI (uniquely |
| 44 | + * identified by the routing entry) and the host irq, using the GICv4 |
| 45 | + * driver mapping operation. Should the mapping succeed, we've then |
| 46 | + * successfully upgraded the guest's LPI to a VLPI. We can then start |
| 47 | + * with updating GICv4's view of the property table and generating an |
| 48 | + * INValidation in order to kickstart the delivery of this VLPI to the |
| 49 | + * guest directly, without software intervention. Well, almost. |
| 50 | + * |
| 51 | + * When the PCI endpoint is deconfigured, this operation is reversed |
| 52 | + * with VFIO calling kvm_vgic_v4_unset_forwarding(). |
| 53 | + * |
| 54 | + * Once the VLPI has been mapped, it needs to follow any change the |
| 55 | + * guest performs on its LPI through the vITS. For that, a number of |
| 56 | + * command handlers have hooks to communicate these changes to the HW: |
| 57 | + * - Any invalidation triggers a call to its_prop_update_vlpi() |
| 58 | + * - The INT command results in a irq_set_irqchip_state(), which |
| 59 | + * generates an INT on the corresponding VLPI. |
| 60 | + * - The CLEAR command results in a irq_set_irqchip_state(), which |
| 61 | + * generates an CLEAR on the corresponding VLPI. |
| 62 | + * - DISCARD translates into an unmap, similar to a call to |
| 63 | + * kvm_vgic_v4_unset_forwarding(). |
| 64 | + * - MOVI is translated by an update of the existing mapping, changing |
| 65 | + * the target vcpu, resulting in a VMOVI being generated. |
| 66 | + * - MOVALL is translated by a string of mapping updates (similar to |
| 67 | + * the handling of MOVI). MOVALL is horrible. |
| 68 | + * |
| 69 | + * Note that a DISCARD/MAPTI sequence emitted from the guest without |
| 70 | + * reprogramming the PCI endpoint after MAPTI does not result in a |
| 71 | + * VLPI being mapped, as there is no callback from VFIO (the guest |
| 72 | + * will get the interrupt via the normal SW injection). Fixing this is |
| 73 | + * not trivial, and requires some horrible messing with the VFIO |
| 74 | + * internals. Not fun. Don't do that. |
| 75 | + * |
| 76 | + * Then there is the scheduling. Each time a vcpu is about to run on a |
| 77 | + * physical CPU, KVM must tell the corresponding redistributor about |
| 78 | + * it. And if we've migrated our vcpu from one CPU to another, we must |
| 79 | + * tell the ITS (so that the messages reach the right redistributor). |
| 80 | + * This is done in two steps: first issue a irq_set_affinity() on the |
| 81 | + * irq corresponding to the vcpu, then call its_schedule_vpe(). You |
| 82 | + * must be in a non-preemptible context. On exit, another call to |
| 83 | + * its_schedule_vpe() tells the redistributor that we're done with the |
| 84 | + * vcpu. |
| 85 | + * |
| 86 | + * Finally, the doorbell handling: Each vcpu is allocated an interrupt |
| 87 | + * which will fire each time a VLPI is made pending whilst the vcpu is |
| 88 | + * not running. Each time the vcpu gets blocked, the doorbell |
| 89 | + * interrupt gets enabled. When the vcpu is unblocked (for whatever |
| 90 | + * reason), the doorbell interrupt is disabled. |
| 91 | + */ |
| 92 | + |
26 | 93 | #define DB_IRQ_FLAGS (IRQ_NOAUTOEN | IRQ_DISABLE_UNLAZY | IRQ_NO_BALANCING)
|
27 | 94 |
|
28 | 95 | static irqreturn_t vgic_v4_doorbell_handler(int irq, void *info)
|
|
0 commit comments