[PATCH 4/4 RFC] fsl/msi: Add interface to reserve/free msi bank

Bharat Bhushan Bharat.Bhushan at freescale.com
Tue Mar 3 16:17:46 AEDT 2015


This patch allows a context (different from kernel context)
to reserve a MSI bank for itself. And then the devices in the
context will share the MSI bank.

VFIO meta driver is one of typical user of these APIs. It will
reserve a MSI bank for MSI interrupt support of direct assignment
PCI devices to a Guest. Patches for same will follow this patch.

Signed-off-by: Bharat Bhushan <Bharat.Bhushan at freescale.com>
---
 arch/powerpc/include/asm/device.h  |   2 +
 arch/powerpc/include/asm/fsl_msi.h |  26 ++++++
 arch/powerpc/sysdev/fsl_msi.c      | 169 +++++++++++++++++++++++++++++++------
 arch/powerpc/sysdev/fsl_msi.h      |   1 +
 4 files changed, 173 insertions(+), 25 deletions(-)
 create mode 100644 arch/powerpc/include/asm/fsl_msi.h

diff --git a/arch/powerpc/include/asm/device.h b/arch/powerpc/include/asm/device.h
index 38faede..1c2bfd7 100644
--- a/arch/powerpc/include/asm/device.h
+++ b/arch/powerpc/include/asm/device.h
@@ -40,6 +40,8 @@ struct dev_archdata {
 #ifdef CONFIG_FAIL_IOMMU
 	int fail_iommu;
 #endif
+
+	void *context;
 };
 
 struct pdev_archdata {
diff --git a/arch/powerpc/include/asm/fsl_msi.h b/arch/powerpc/include/asm/fsl_msi.h
new file mode 100644
index 0000000..e9041c2
--- /dev/null
+++ b/arch/powerpc/include/asm/fsl_msi.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Bharat Bhushan <bharat.bhushan at freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ */
+
+#ifndef _POWERPC_FSL_MSI_H
+#define _POWERPC_FSL_MSI_H
+
+extern int fsl_msi_set_msi_bank_region(struct iommu_domain *domain,
+				       void *context, int win,
+				       dma_addr_t iova, int prot);
+extern int fsl_msi_clear_msi_bank_region(struct iommu_domain *domain,
+					 struct iommu_group *iommu_group,
+					 int win, dma_addr_t iova);
+extern struct fsl_msi *fsl_msi_reserve_msi_bank(void *context);
+extern int fsl_msi_unreserve_msi_bank(void *context);
+extern int fsl_msi_set_msi_bank_in_dev(struct device *dev, void *data);
+
+#endif /* _POWERPC_FSL_MSI_H */
diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c
index 027aeeb..75cd196 100644
--- a/arch/powerpc/sysdev/fsl_msi.c
+++ b/arch/powerpc/sysdev/fsl_msi.c
@@ -25,6 +25,7 @@
 #include <asm/ppc-pci.h>
 #include <asm/mpic.h>
 #include <asm/fsl_hcalls.h>
+#include <linux/iommu.h>
 
 #include "fsl_msi.h"
 #include "fsl_pci.h"
@@ -172,22 +173,6 @@ static struct fsl_msi *fsl_msi_allocate_msi_bank(void *context)
 	return NULL;
 }
 
-/* FIXME: Assumption that host kernel will allocate only one MSI bank */
- __attribute__ ((unused)) static int fsl_msi_free_msi_bank(void *context)
-{
-	struct fsl_msi *msi_data;
-
-	list_for_each_entry(msi_data, &msi_head, list) {
-		if ((msi_data->reserved == MSI_RESERVED) &&
-		     (msi_data->context == context)) {
-			msi_data->reserved = MSI_FREE;
-			msi_data->context = NULL;
-			return 0;
-		}
-	}
-	return -ENODEV;
-}
-
 /*  This API returns the allocated MSI bank of "context"
  *  to which "pdev" device belongs.
  *  All kernel owned devices have NULL context. All devices
@@ -200,6 +185,12 @@ static struct fsl_msi *fsl_msi_get_reserved_msi_bank(struct pci_dev *pdev)
 {
 	struct fsl_msi *msi_data = NULL;
 	void *context = NULL;
+	struct device *dev = &pdev->dev;
+
+	/* Device assigned to userspace if there is valid context */
+	if (dev->archdata.context) {
+		context = dev->archdata.context;
+	}
 
 	list_for_each_entry(msi_data, &msi_head, list) {
 		if ((msi_data->reserved == MSI_RESERVED) &&
@@ -208,13 +199,133 @@ static struct fsl_msi *fsl_msi_get_reserved_msi_bank(struct pci_dev *pdev)
 	}
 
 	/* If no MSI bank allocated for kernel owned device, allocate one */
-	msi_data = fsl_msi_allocate_msi_bank(NULL);
-	if (msi_data)
-		return msi_data;
+	if (!context) {
+		msi_data = fsl_msi_allocate_msi_bank(NULL);
+		if (msi_data)
+			return msi_data;
+	}
 
 	return NULL;
 }
 
+/* API to set "context" to which the device belongs */
+int fsl_msi_set_msi_bank_in_dev(struct device *dev, void *data)
+{
+	dev->archdata.context = data;
+	return 0;
+}
+
+/*  This API Allows a MSI bank to be reserved for a "context".
+ *  All devices in same "context" will share the allocated
+ *  MSI bank.
+ *  Typically this function will be called from meta
+ *  driver like VFIO with a valid "context".
+ */
+struct fsl_msi *fsl_msi_reserve_msi_bank(void *context)
+{
+	struct fsl_msi *msi_data;
+
+	if (!context)
+		return NULL;
+
+	/* Check if msi-bank already allocated for the context */
+	list_for_each_entry(msi_data, &msi_head, list) {
+		if (msi_data->reserved == MSI_FREE)
+			continue;
+
+		if (context == msi_data->context)
+			return msi_data;
+	}
+
+	msi_data = fsl_msi_allocate_msi_bank(context);
+	return msi_data;
+}
+
+/* Free reserved MSI bank for a given valid context */
+int fsl_msi_unreserve_msi_bank(void *context)
+{
+	struct fsl_msi *msi_data;
+
+	if (!context)
+		return -EINVAL;
+
+	list_for_each_entry(msi_data, &msi_head, list) {
+		if ((context == msi_data->context) &&
+		    (msi_data->reserved == MSI_RESERVED)) {
+			msi_data->reserved = MSI_FREE;
+			msi_data->context = NULL;
+			return 0;
+		}
+	}
+	return -ENODEV;
+}
+
+static int is_msi_bank_reserved(struct fsl_msi *msi)
+{
+	return msi->reserved != MSI_FREE;
+}
+
+/*
+ * This function configures PAMU window for MSI page with
+ * given iova. Also same iova will be used as "msi-address"
+ * when configuring msi-message in the devices using this
+ * msi bank.
+ */
+int fsl_msi_set_msi_bank_region(struct iommu_domain *domain,
+				void *context , int win,
+				dma_addr_t iova, int prot)
+{
+	struct fsl_msi *msi_data;
+	dma_addr_t addr;
+	u64 size;
+	int ret;
+
+	if (!context)
+		return -EINVAL;
+
+	list_for_each_entry(msi_data, &msi_head, list) {
+		if (msi_data->reserved == MSI_FREE)
+			continue;
+
+		if (context != msi_data->context)
+			continue;
+
+		size = PAGE_SIZE;
+		addr = msi_data->msiir & ~(size - 1);
+		ret = iommu_domain_window_enable(domain, win, addr, size, prot);
+		if (ret) {
+			pr_err("%s Error: unable to map msi region\n", __func__);
+			return ret;
+		}
+		msi_data->iova = iova | (msi_data->msiir & (size - 1));
+		return 0;
+	}
+
+	return -ENODEV;
+}
+
+/* This allows to undo what is done in fsl_msi_set_msi_bank_region() */
+int fsl_msi_clear_msi_bank_region(struct iommu_domain *domain, void *context,
+				  int win)
+{
+	struct fsl_msi *msi_data;
+
+	if (!context)
+		return -EINVAL;
+
+	list_for_each_entry(msi_data, &msi_head, list) {
+		if (msi_data->reserved == MSI_FREE)
+			continue;
+
+		if (context == msi_data->context) {
+			iommu_domain_window_disable(domain, win);
+			msi_data->iova = 0;
+			return 0;
+		}
+	}
+	return -ENODEV;
+}
+
 static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq,
 				struct msi_msg *msg,
 				struct fsl_msi *fsl_msi_data)
@@ -225,12 +336,17 @@ static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq,
 	int len;
 	const __be64 *reg;
 
-	/* If the msi-address-64 property exists, then use it */
-	reg = of_get_property(hose->dn, "msi-address-64", &len);
-	if (reg && (len == sizeof(u64)))
-		address = be64_to_cpup(reg);
-	else
-		address = msi_data->msiir;
+	if (pdev->dev.archdata.context) {
+		address = msi_data->iova;
+	} else {
+		/* If the msi-address-64 property exists, then use it */
+		reg = of_get_property(hose->dn, "msi-address-64", &len);
+		if (reg && (len == sizeof(u64)))
+			address = be64_to_cpup(reg);
+		else
+			address = fsl_pci_immrbar_base(hose) +
+					(msi_data->msiir & 0xfffff);
+	}
 
 	msg->address_lo = lower_32_bits(address);
 	msg->address_hi = upper_32_bits(address);
@@ -401,6 +517,9 @@ static int fsl_of_msi_remove(struct platform_device *ofdev)
 	struct fsl_msi *msi = platform_get_drvdata(ofdev);
 	int virq, i;
 
+	if (is_msi_bank_reserved(msi))
+		return -EBUSY;
+
 	if (msi->list.prev != NULL)
 		list_del(&msi->list);
 	for (i = 0; i < NR_MSI_REG_MAX; i++) {
diff --git a/arch/powerpc/sysdev/fsl_msi.h b/arch/powerpc/sysdev/fsl_msi.h
index c69702b..7dc6f35 100644
--- a/arch/powerpc/sysdev/fsl_msi.h
+++ b/arch/powerpc/sysdev/fsl_msi.h
@@ -50,6 +50,7 @@ struct fsl_msi {
 #define MSI_RESERVED		1
         int reserved;
         void *context;
+	dma_addr_t iova;
 };
 
 #endif /* _POWERPC_SYSDEV_FSL_MSI_H */
-- 
1.9.3



More information about the Linuxppc-dev mailing list