|
16 | 16 |
|
17 | 17 | #include <linux/kvm.h>
|
18 | 18 | #include <linux/kvm_host.h>
|
| 19 | +#include <linux/list_sort.h> |
19 | 20 |
|
20 | 21 | #include "vgic.h"
|
21 | 22 |
|
@@ -102,6 +103,62 @@ static struct kvm_vcpu *vgic_target_oracle(struct vgic_irq *irq)
|
102 | 103 | return NULL;
|
103 | 104 | }
|
104 | 105 |
|
| 106 | +/* |
| 107 | + * The order of items in the ap_lists defines how we'll pack things in LRs as |
| 108 | + * well, the first items in the list being the first things populated in the |
| 109 | + * LRs. |
| 110 | + * |
| 111 | + * A hard rule is that active interrupts can never be pushed out of the LRs |
| 112 | + * (and therefore take priority) since we cannot reliably trap on deactivation |
| 113 | + * of IRQs and therefore they have to be present in the LRs. |
| 114 | + * |
| 115 | + * Otherwise things should be sorted by the priority field and the GIC |
| 116 | + * hardware support will take care of preemption of priority groups etc. |
| 117 | + * |
| 118 | + * Return negative if "a" sorts before "b", 0 to preserve order, and positive |
| 119 | + * to sort "b" before "a". |
| 120 | + */ |
| 121 | +static int vgic_irq_cmp(void *priv, struct list_head *a, struct list_head *b) |
| 122 | +{ |
| 123 | + struct vgic_irq *irqa = container_of(a, struct vgic_irq, ap_list); |
| 124 | + struct vgic_irq *irqb = container_of(b, struct vgic_irq, ap_list); |
| 125 | + bool penda, pendb; |
| 126 | + int ret; |
| 127 | + |
| 128 | + spin_lock(&irqa->irq_lock); |
| 129 | + spin_lock_nested(&irqb->irq_lock, SINGLE_DEPTH_NESTING); |
| 130 | + |
| 131 | + if (irqa->active || irqb->active) { |
| 132 | + ret = (int)irqb->active - (int)irqa->active; |
| 133 | + goto out; |
| 134 | + } |
| 135 | + |
| 136 | + penda = irqa->enabled && irqa->pending; |
| 137 | + pendb = irqb->enabled && irqb->pending; |
| 138 | + |
| 139 | + if (!penda || !pendb) { |
| 140 | + ret = (int)pendb - (int)penda; |
| 141 | + goto out; |
| 142 | + } |
| 143 | + |
| 144 | + /* Both pending and enabled, sort by priority */ |
| 145 | + ret = irqa->priority - irqb->priority; |
| 146 | +out: |
| 147 | + spin_unlock(&irqb->irq_lock); |
| 148 | + spin_unlock(&irqa->irq_lock); |
| 149 | + return ret; |
| 150 | +} |
| 151 | + |
| 152 | +/* Must be called with the ap_list_lock held */ |
| 153 | +static void vgic_sort_ap_list(struct kvm_vcpu *vcpu) |
| 154 | +{ |
| 155 | + struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; |
| 156 | + |
| 157 | + DEBUG_SPINLOCK_BUG_ON(!spin_is_locked(&vgic_cpu->ap_list_lock)); |
| 158 | + |
| 159 | + list_sort(NULL, &vgic_cpu->ap_list_head, vgic_irq_cmp); |
| 160 | +} |
| 161 | + |
105 | 162 | /*
|
106 | 163 | * Only valid injection if changing level for level-triggered IRQs or for a
|
107 | 164 | * rising edge.
|
|
0 commit comments