MSIs for a given device are normally all allocated in one go.
Make sure the internal code can allocate them one at a time
if required.

Signed-off-by: Marc Zyngier <marc.zyng...@arm.com>
---
 drivers/base/platform-msi.c | 32 ++++++++++++++++++++------------
 1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c
index 5df4575..6148c78 100644
--- a/drivers/base/platform-msi.c
+++ b/drivers/base/platform-msi.c
@@ -25,6 +25,7 @@
 #include <linux/slab.h>
 
 #define DEV_ID_SHIFT   24
+#define MAX_DEV_MSIS   (1 << (32 - DEV_ID_SHIFT))
 
 /*
  * Internal data structure containing a (made up, but unique) devid
@@ -110,13 +111,16 @@ static void platform_msi_update_chip_ops(struct 
msi_domain_info *info)
                chip->irq_write_msi_msg = platform_msi_write_msg;
 }
 
-static void platform_msi_free_descs(struct device *dev)
+static void platform_msi_free_descs(struct device *dev, int base, int nvec)
 {
        struct msi_desc *desc, *tmp;
 
        list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
-               list_del(&desc->list);
-               free_msi_entry(desc);
+               if (desc->platform.msi_index >= base &&
+                   desc->platform.msi_index < (base + nvec)) {
+                       list_del(&desc->list);
+                       free_msi_entry(desc);
+               }
        }
 }
 
@@ -124,17 +128,22 @@ static int platform_msi_alloc_descs(struct device *dev, 
int nvec,
                                    struct platform_msi_priv_data *data)
 
 {
-       int i;
+       struct msi_desc *desc;
+       int i, base = 0;
 
-       for (i = 0; i < nvec; i++) {
-               struct msi_desc *desc;
+       if (!list_empty(dev_to_msi_list(dev))) {
+               desc = list_last_entry(dev_to_msi_list(dev),
+                                      struct msi_desc, list);
+               base = desc->platform.msi_index + 1;
+       }
 
+       for (i = 0; i < nvec; i++) {
                desc = alloc_msi_entry(dev);
                if (!desc)
                        break;
 
                desc->platform.msi_priv_data = data;
-               desc->platform.msi_index = i;
+               desc->platform.msi_index = base + i;
                desc->nvec_used = 1;
 
                list_add_tail(&desc->list, dev_to_msi_list(dev));
@@ -142,7 +151,7 @@ static int platform_msi_alloc_descs(struct device *dev, int 
nvec,
 
        if (i != nvec) {
                /* Clean up the mess */
-               platform_msi_free_descs(dev);
+               platform_msi_free_descs(dev, base, nvec);
 
                return -ENOMEM;
        }
@@ -201,8 +210,7 @@ int platform_msi_domain_alloc_irqs(struct device *dev, 
unsigned int nvec,
         * accordingly (which would impact the max number of MSI
         * capable devices).
         */
-       if (!dev->msi_domain || !write_msi_msg || !nvec ||
-           nvec > (1 << (32 - DEV_ID_SHIFT)))
+       if (!dev->msi_domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS)
                return -EINVAL;
 
        if (dev->msi_domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) {
@@ -238,7 +246,7 @@ int platform_msi_domain_alloc_irqs(struct device *dev, 
unsigned int nvec,
        return 0;
 
 out_free_desc:
-       platform_msi_free_descs(dev);
+       platform_msi_free_descs(dev, 0, nvec);
 out_free_id:
        ida_simple_remove(&platform_msi_devid_ida, priv_data->devid);
 out_free_data:
@@ -266,5 +274,5 @@ void platform_msi_domain_free_irqs(struct device *dev)
        }
 
        msi_domain_free_irqs(dev->msi_domain, dev);
-       platform_msi_free_descs(dev);
+       platform_msi_free_descs(dev, 0, MAX_DEV_MSIS);
 }
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to