diff mbox

[PART2,v3,06/11] iommu/amd: Adding GALOG interrupt handler

Message ID 1468231899-6987-7-git-send-email-suravee.suthikulpanit@amd.com
State New
Headers show

Commit Message

Suthikulpanit, Suravee July 11, 2016, 10:11 a.m. UTC
From: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>


This patch adds AMD IOMMU guest virtual APIC log (GALOG) handler.
When IOMMU hardware receives an interrupt targeting a blocking vcpu,
it creates an entry in the GALOG, and generates an interrupt to notify
the AMD IOMMU driver.

At this point, the driver processes the log entry, and notify the SVM
driver via the registered iommu_ga_log_notifier function.

Signed-off-by: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>

---
 drivers/iommu/amd_iommu.c       | 100 ++++++++++++++++++++++++++++++++++++++--
 drivers/iommu/amd_iommu_init.c  |   5 ++
 drivers/iommu/amd_iommu_types.h |  14 ++++++
 include/linux/amd-iommu.h       |  20 ++++++--
 4 files changed, 133 insertions(+), 6 deletions(-)

-- 
1.9.1

Comments

Suthikulpanit, Suravee July 13, 2016, 12:49 p.m. UTC | #1
Hi Radim,

On 07/12/2016 01:47 AM, Radim Krčmář wrote:
> 2016-07-11 05:11-0500, Suravee Suthikulpanit:

>> From: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>

>>

>> This patch adds AMD IOMMU guest virtual APIC log (GALOG) handler.

>> When IOMMU hardware receives an interrupt targeting a blocking vcpu,

>> it creates an entry in the GALOG, and generates an interrupt to notify

>> the AMD IOMMU driver.

>>

>> At this point, the driver processes the log entry, and notify the SVM

>> driver via the registered iommu_ga_log_notifier function.

>>

>> Signed-off-by: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>

>> ---

>> diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c

>> @@ -741,14 +741,101 @@ static void iommu_poll_ppr_log(struct amd_iommu *iommu)

>> +static void iommu_handle_ga_guest_nr_entry(struct amd_iommu *iommu,

>> +					   u16 devid, u32 ga_tag)

>> +{

>> +	struct amd_ir_data *ir_data;

>> +	unsigned long flags;

>> +	int vec = 0;

>> +

>> +	if (!iommu_ga_log_notifier)

>> +		return;

>> +

>> +	spin_lock_irqsave(&iommu->ga_hash_lock, flags);

>> +	hash_for_each_possible(iommu->ga_hash, ir_data, hnode, ga_tag) {

>> +		struct irte_ga *entry = (struct irte_ga *)ir_data->entry;

>

>    if (ga_tag != entry->fields_vapic.ga_tag)

>    	continue;

>

> Multiple different ga_tags can map to the same bucket, so we need to

> explicitly select the correct entry.

>


In the upcoming V4 changes, we should not need this look up anymore.

>> +

>> +		vec = entry->hi.fields.vector;

>> +		break;

>> +	}

>> +	spin_unlock_irqrestore(&iommu->ga_hash_lock, flags);

>> +

>> +	if (vec) {

>> +		pr_debug("AMD-Vi: %s: devid=%#x, ga_tag=%#x\n",

>> +			 __func__, devid, ga_tag);

>> +

>> +		if (iommu_ga_log_notifier(GATAG_TO_AVICTAG(ga_tag),

>> +					  GATAG_TO_VCPUID(ga_tag), vec) != 0)



The necessary look up (i.e. to get vm and vcpu information should be 
done in the calling notifier here. I'll update this in V4, which getting 
ready to send out soon.

Thanks,
Suravee
diff mbox

Patch

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index abfb2b7..939ebb8 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -741,14 +741,101 @@  static void iommu_poll_ppr_log(struct amd_iommu *iommu)
 	}
 }
 
+#ifdef CONFIG_IRQ_REMAP
+static int (*iommu_ga_log_notifier)(int, int, int);
+
+int amd_iommu_register_ga_log_notifier(int (*notifier)(int, int, int))
+{
+	iommu_ga_log_notifier = notifier;
+
+	return 0;
+}
+EXPORT_SYMBOL(amd_iommu_register_ga_log_notifier);
+
+static void iommu_handle_ga_guest_nr_entry(struct amd_iommu *iommu,
+					   u16 devid, u32 ga_tag)
+{
+	struct amd_ir_data *ir_data;
+	unsigned long flags;
+	int vec = 0;
+
+	if (!iommu_ga_log_notifier)
+		return;
+
+	spin_lock_irqsave(&iommu->ga_hash_lock, flags);
+	hash_for_each_possible(iommu->ga_hash, ir_data, hnode, ga_tag) {
+		struct irte_ga *entry = (struct irte_ga *)ir_data->entry;
+
+		vec = entry->hi.fields.vector;
+		break;
+	}
+	spin_unlock_irqrestore(&iommu->ga_hash_lock, flags);
+
+	if (vec) {
+		pr_debug("AMD-Vi: %s: devid=%#x, ga_tag=%#x\n",
+			 __func__, devid, ga_tag);
+
+		if (iommu_ga_log_notifier(GATAG_TO_AVICTAG(ga_tag),
+					  GATAG_TO_VCPUID(ga_tag), vec) != 0)
+			pr_err("AMD-Vi: GA log notifier failed.\n");
+	}
+}
+
+static void iommu_poll_ga_log(struct amd_iommu *iommu)
+{
+	u32 head, tail, cnt = 0;
+
+	if (iommu->ga_log == NULL)
+		return;
+
+	head = readl(iommu->mmio_base + MMIO_GA_HEAD_OFFSET);
+	tail = readl(iommu->mmio_base + MMIO_GA_TAIL_OFFSET);
+
+	while (head != tail) {
+		volatile u64 *raw;
+		u64 entry;
+
+		raw = (u64 *)(iommu->ga_log + head);
+		cnt++;
+
+		/* Avoid memcpy function-call overhead */
+		entry = *raw;
+
+		/* Update head pointer of hardware ring-buffer */
+		head = (head + GA_ENTRY_SIZE) % GA_LOG_SIZE;
+		writel(head, iommu->mmio_base + MMIO_GA_HEAD_OFFSET);
+
+		/* Handle GA entry */
+		switch (GA_REQ_TYPE(entry)) {
+		case GA_GUEST_NR:
+			iommu_handle_ga_guest_nr_entry(iommu,
+							GA_DEVID(entry),
+							GA_TAG(entry));
+			break;
+		default:
+			break;
+		}
+
+		/* Refresh ring-buffer information */
+		head = readl(iommu->mmio_base + MMIO_GA_HEAD_OFFSET);
+		tail = readl(iommu->mmio_base + MMIO_GA_TAIL_OFFSET);
+	}
+}
+#endif /* CONFIG_IRQ_REMAP */
+
+#define AMD_IOMMU_INT_MASK	\
+	(MMIO_STATUS_EVT_INT_MASK | \
+	 MMIO_STATUS_PPR_INT_MASK | \
+	 MMIO_STATUS_GALOG_INT_MASK)
+
 irqreturn_t amd_iommu_int_thread(int irq, void *data)
 {
 	struct amd_iommu *iommu = (struct amd_iommu *) data;
 	u32 status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET);
 
-	while (status & (MMIO_STATUS_EVT_INT_MASK | MMIO_STATUS_PPR_INT_MASK)) {
-		/* Enable EVT and PPR interrupts again */
-		writel((MMIO_STATUS_EVT_INT_MASK | MMIO_STATUS_PPR_INT_MASK),
+	while (status & AMD_IOMMU_INT_MASK) {
+		/* Enable EVT and PPR and GA interrupts again */
+		writel(AMD_IOMMU_INT_MASK,
 			iommu->mmio_base + MMIO_STATUS_OFFSET);
 
 		if (status & MMIO_STATUS_EVT_INT_MASK) {
@@ -761,6 +848,13 @@  irqreturn_t amd_iommu_int_thread(int irq, void *data)
 			iommu_poll_ppr_log(iommu);
 		}
 
+#ifdef CONFIG_IRQ_REMAP
+		if (status & MMIO_STATUS_GALOG_INT_MASK) {
+			pr_devel("AMD-Vi: Processing IOMMU GA Log\n");
+			iommu_poll_ga_log(iommu);
+		}
+#endif
+
 		/*
 		 * Hardware bug: ERBT1312
 		 * When re-enabling interrupt (by writing 1
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index 44483a7..d103c3f 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -765,6 +765,11 @@  static int iommu_init_ga(struct amd_iommu *iommu)
 	    !iommu_feature(iommu, FEATURE_GAM_VAPIC))
 		amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY_GA;
 
+	if (AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir)) {
+		hash_init(iommu->ga_hash);
+		spin_lock_init(&iommu->ga_hash_lock);
+	}
+
 	ret = iommu_init_ga_log(iommu);
 #endif /* CONFIG_IRQ_REMAP */
 
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index d72ab15..926ef6e 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -27,6 +27,7 @@ 
 #include <linux/spinlock.h>
 #include <linux/pci.h>
 #include <linux/irqreturn.h>
+#include <linux/hashtable.h>
 
 /*
  * Maximum number of IOMMUs supported
@@ -120,6 +121,14 @@ 
 #define MMIO_STATUS_GALOG_OVERFLOW_MASK	(1 << 9)
 #define MMIO_STATUS_GALOG_INT_MASK	(1 << 10)
 
+#define AMD_IOMMU_GA_HASH_BITS	16
+#define AMD_IOMMU_GA_HASH_MASK	((1U << AMD_IOMMU_GA_HASH_BITS) - 1)
+#define AMD_IOMMU_GATAG(x, y)	\
+	((((x & 0xFF) << 8) | (y & 0xFF)) & AMD_IOMMU_GA_HASH_MASK)
+
+#define GATAG_TO_AVICTAG(x)	((x >> 8) & 0xFF)
+#define GATAG_TO_VCPUID(x)	(x & 0xFF)
+
 /* event logging constants */
 #define EVENT_ENTRY_SIZE	0x10
 #define EVENT_TYPE_SHIFT	28
@@ -565,6 +574,10 @@  struct amd_iommu {
 	struct irq_domain *msi_domain;
 
 	struct amd_irte_ops *irte_ops;
+
+	/* Hash table for ga_tag to irte_ga entry */
+	DECLARE_HASHTABLE(ga_hash, AMD_IOMMU_GA_HASH_BITS);
+	spinlock_t ga_hash_lock;
 #endif
 };
 
@@ -819,6 +832,7 @@  struct amd_irte_ops {
 };
 
 struct amd_ir_data {
+	struct hlist_node hnode;
 	struct irq_2_irte irq_2_irte;
 	void *entry;
 	union {
diff --git a/include/linux/amd-iommu.h b/include/linux/amd-iommu.h
index 2b08e79..8f7a469 100644
--- a/include/linux/amd-iommu.h
+++ b/include/linux/amd-iommu.h
@@ -168,11 +168,25 @@  typedef void (*amd_iommu_invalidate_ctx)(struct pci_dev *pdev, int pasid);
 
 extern int amd_iommu_set_invalidate_ctx_cb(struct pci_dev *pdev,
 					   amd_iommu_invalidate_ctx cb);
-
-#else
+#else /* CONFIG_AMD_IOMMU */
 
 static inline int amd_iommu_detect(void) { return -ENODEV; }
 
-#endif
+#endif /* CONFIG_AMD_IOMMU */
+
+#if defined(CONFIG_AMD_IOMMU) && defined(CONFIG_IRQ_REMAP)
+
+/* IOMMU AVIC Function */
+extern int amd_iommu_register_ga_log_notifier(int (*notifier)(int, int, int));
+
+#else /* defined(CONFIG_AMD_IOMMU) && defined(CONFIG_IRQ_REMAP) */
+
+static inline int
+amd_iommu_register_ga_log_notifier(int (*notifier)(int, int, int))
+{
+	return 0;
+}
+
+#endif /* defined(CONFIG_AMD_IOMMU) && defined(CONFIG_IRQ_REMAP) */
 
 #endif /* _ASM_X86_AMD_IOMMU_H */