@@ -424,6 +424,8 @@ DECLARE_STATS_COUNTER(complete_ppr);
DECLARE_STATS_COUNTER(invalidate_iotlb);
DECLARE_STATS_COUNTER(invalidate_iotlb_all);
DECLARE_STATS_COUNTER(pri_requests);
+DECLARE_STATS_COUNTER(galog_max);
+DECLARE_STATS_COUNTER(galog_total);
static struct dentry *stats_dir;
static struct dentry *de_fflush;
@@ -462,6 +464,8 @@ static void amd_iommu_stats_init(void)
amd_iommu_stats_add(&invalidate_iotlb);
amd_iommu_stats_add(&invalidate_iotlb_all);
amd_iommu_stats_add(&pri_requests);
+ amd_iommu_stats_add(&galog_max);
+ amd_iommu_stats_add(&galog_total);
}
#endif
@@ -655,14 +659,102 @@ static void iommu_poll_ppr_log(struct amd_iommu *iommu)
}
}
+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) {
+ vec = ir_data->irte_ga_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);
+ }
+
+ ADD_STATS_COUNTER(galog_total, cnt);
+
+ if (STATS_COUNTER(galog_max) < cnt)
+ SET_STATS_COUNTER(galog_max, cnt);
+}
+
+#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) {
@@ -675,6 +767,11 @@ irqreturn_t amd_iommu_int_thread(int irq, void *data)
iommu_poll_ppr_log(iommu);
}
+ if (status & MMIO_STATUS_GALOG_INT_MASK) {
+ pr_devel("AMD-Vi: Processing IOMMU GA Log\n");
+ iommu_poll_ga_log(iommu);
+ }
+
/*
* Hardware bug: ERBT1312
* When re-enabling interrupt (by writing 1
@@ -169,10 +169,20 @@ 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);
+/* IOMMU AVIC Function */
+extern int
+amd_iommu_register_ga_log_notifier(int (*notifier)(int, int, int));
+
#else
static inline int amd_iommu_detect(void) { return -ENODEV; }
+static inline int
+amd_iommu_register_ga_log_notifier(int (*notifier)(int, int, int))
+{
+ return 0;
+}
+
#endif
#endif /* _ASM_X86_AMD_IOMMU_H */