]> git.baikalelectronics.ru Git - kernel.git/commitdiff
PCI: endpoint: Fix for concurrent memory allocation in OB address region
authorKishon Vijay Abraham I <kishon@ti.com>
Mon, 24 Feb 2020 09:53:36 +0000 (15:23 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 17 Apr 2020 08:50:11 +0000 (10:50 +0200)
commit 6ffaf3fb6d5204d23f22f6e57333dc2760fb514f upstream.

pci-epc-mem uses a bitmap to manage the Endpoint outbound (OB) address
region. This address region will be shared by multiple endpoint
functions (in the case of multi function endpoint) and it has to be
protected from concurrent access to avoid updating an inconsistent state.

Use a mutex to protect bitmap updates to prevent the memory
allocation API from returning incorrect addresses.

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: stable@vger.kernel.org # v4.14+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/pci/endpoint/pci-epc-mem.c
include/linux/pci-epc.h

index 2bf8bd1f0563eaf4740874ec1e3f0c836adc5687..0471643cf536d1a383c0582b2d84de07ab5949e7 100644 (file)
@@ -79,6 +79,7 @@ int __pci_epc_mem_init(struct pci_epc *epc, phys_addr_t phys_base, size_t size,
        mem->page_size = page_size;
        mem->pages = pages;
        mem->size = size;
+       mutex_init(&mem->lock);
 
        epc->mem = mem;
 
@@ -122,7 +123,7 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
                                     phys_addr_t *phys_addr, size_t size)
 {
        int pageno;
-       void __iomem *virt_addr;
+       void __iomem *virt_addr = NULL;
        struct pci_epc_mem *mem = epc->mem;
        unsigned int page_shift = ilog2(mem->page_size);
        int order;
@@ -130,15 +131,18 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
        size = ALIGN(size, mem->page_size);
        order = pci_epc_mem_get_order(mem, size);
 
+       mutex_lock(&mem->lock);
        pageno = bitmap_find_free_region(mem->bitmap, mem->pages, order);
        if (pageno < 0)
-               return NULL;
+               goto ret;
 
        *phys_addr = mem->phys_base + (pageno << page_shift);
        virt_addr = ioremap(*phys_addr, size);
        if (!virt_addr)
                bitmap_release_region(mem->bitmap, pageno, order);
 
+ret:
+       mutex_unlock(&mem->lock);
        return virt_addr;
 }
 EXPORT_SYMBOL_GPL(pci_epc_mem_alloc_addr);
@@ -164,7 +168,9 @@ void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr,
        pageno = (phys_addr - mem->phys_base) >> page_shift;
        size = ALIGN(size, mem->page_size);
        order = pci_epc_mem_get_order(mem, size);
+       mutex_lock(&mem->lock);
        bitmap_release_region(mem->bitmap, pageno, order);
+       mutex_unlock(&mem->lock);
 }
 EXPORT_SYMBOL_GPL(pci_epc_mem_free_addr);
 
index f641badc2c6157c3b1f670b4690b0514d9871352..0c12d69dde9299cd597d013336a21909af8a4ae4 100644 (file)
@@ -71,6 +71,7 @@ struct pci_epc_ops {
  * @bitmap: bitmap to manage the PCI address space
  * @pages: number of bits representing the address region
  * @page_size: size of each page
+ * @lock: mutex to protect bitmap
  */
 struct pci_epc_mem {
        phys_addr_t     phys_base;
@@ -78,6 +79,8 @@ struct pci_epc_mem {
        unsigned long   *bitmap;
        size_t          page_size;
        int             pages;
+       /* mutex to protect against concurrent access for memory allocation*/
+       struct mutex    lock;
 };
 
 /**