Thread (49 messages) 49 messages, 4 authors, 2016-07-12
STALE3623d

[PATCH v8 15/17] KVM: arm64: implement ITS command queue command handlers

From: Marc Zyngier <hidden>
Date: 2016-07-11 17:52:10
Also in: kvm, kvmarm

On 11/07/16 18:47, Andre Przywara wrote:
Hi,

On 11/07/16 18:17, Marc Zyngier wrote:
quoted
On 05/07/16 12:23, Andre Przywara wrote:
quoted
The connection between a device, an event ID, the LPI number and the
allocated CPU is stored in in-memory tables in a GICv3, but their
format is not specified by the spec. Instead software uses a command
queue in a ring buffer to let the ITS implementation use their own
format.
Implement handlers for the various ITS commands and let them store
the requested relation into our own data structures. Those data
structures are protected by the its_lock mutex.
Our internal ring buffer read and write pointers are protected by the
its_cmd mutex, so that at most one VCPU per ITS can handle commands at
any given time.
Error handling is very basic at the moment, as we don't have a good
way of communicating errors to the guest (usually a SError).
The INT command handler is missing at this point, as we gain the
capability of actually injecting MSIs into the guest only later on.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 virt/kvm/arm/vgic/vgic-its.c | 609 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 605 insertions(+), 4 deletions(-)
diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index 5de71bd..432daed 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -58,6 +58,43 @@ out_unlock:
 	return irq;
 }
 
+/*
+ * Creates a new (reference to a) struct vgic_irq for a given LPI.
+ * If this LPI is already mapped on another ITS, we increase its refcount
+ * and return a pointer to the existing structure.
+ * If this is a "new" LPI, we allocate and initialize a new struct vgic_irq.
+ * This function returns a pointer to the _unlocked_ structure.
+ */
+static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	struct vgic_irq *irq = vgic_its_get_lpi(kvm, intid);
So this thing doesn't return with any lock held...
quoted
+
+	/* In this case there is no put, since we keep the reference. */
+	if (irq)
+		return irq;
+
+	irq = kzalloc(sizeof(struct vgic_irq), GFP_KERNEL);
+
+	if (!irq)
+		return NULL;
+
+	INIT_LIST_HEAD(&irq->lpi_entry);
+	INIT_LIST_HEAD(&irq->ap_list);
+	spin_lock_init(&irq->irq_lock);
+
+	irq->config = VGIC_CONFIG_EDGE;
+	kref_init(&irq->refcount);
+	irq->intid = intid;
which means that two callers can allocate their own irq structure...
In practise this will never happen, because the only caller
(handle_mapi) takes the its_lock mutex. But I see that this is fragile
Given that the its_lock is per ITS, and that we're dealing with global
objects, this doesn't protect against anything. I can have two VCPUs
firing MAPIs on two ITSs, and hit that path with reasonable chances of
creating mayhem.
and not safe. I guess I can search the list again after having taken the
lock.
Please do.
quoted
quoted
+
+	spin_lock(&dist->lpi_list_lock);
+	list_add_tail(&irq->lpi_entry, &dist->lpi_list_head);
+	dist->lpi_list_count++;
+	spin_unlock(&dist->lpi_list_lock);
and insert it. Not too bad if they are different LPIs, but leading to
Armageddon if they are the same. You absolutely need to check for the
the presence of the interrupt in this list *while holding the lock*.
quoted
+
+	return irq;
+}
+
 struct its_device {
 	struct list_head dev_list;
 
....
quoted
quoted
+/*
+ * The INVALL command requests flushing of all IRQ data in this collection.
+ * Find the VCPU mapped to that collection, then iterate over the VM's list
+ * of mapped LPIs and update the configuration for each IRQ which targets
+ * the specified vcpu. The configuration will be read from the in-memory
+ * configuration table.
+ */
+static int vgic_its_cmd_handle_invall(struct kvm *kvm, struct vgic_its *its,
+				  u64 *its_cmd)
+{
+	u32 coll_id = its_cmd_get_collection(its_cmd);
+	struct its_collection *collection;
+	struct kvm_vcpu *vcpu;
+	struct vgic_irq *irq;
+	u32 *intids;
+	int irq_count, i;
+
+	mutex_lock(&its->its_lock);
+
+	collection = find_collection(its, coll_id);
+	if (!its_is_collection_mapped(collection))
+		return E_ITS_INVALL_UNMAPPED_COLLECTION;
+
+	vcpu = kvm_get_vcpu(kvm, collection->target_addr);
+
+	irq_count = vgic_its_copy_lpi_list(kvm, &intids);
+	if (irq_count < 0)
+		return irq_count;
+
+	for (i = 0; i < irq_count; i++) {
+		irq = vgic_get_irq(kvm, NULL, intids[i]);
+		if (!irq)
+			continue;
+		update_lpi_config_filtered(kvm, irq, vcpu);
+		vgic_put_irq_locked(kvm, irq);
Where is the lpi_list_lock taken?
Argh, good catch!
quoted
And why would we need it since we've
copied everything already? By the look of it, this vgic_put_irq_locked
should not exist at all, as the only other use case is quite dubious.
Possibly, I don't like it either. Let me check if I can kill that sucker.
Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help