| From 52ddaba3dfce9ce1971aa6b00faec28b368a85b3 Mon Sep 17 00:00:00 2001 |
| From: Kishon Vijay Abraham I <kishon@ti.com> |
| Date: Mon, 24 Feb 2020 15:23:36 +0530 |
| Subject: [PATCH] PCI: endpoint: Fix for concurrent memory allocation in OB |
| address region |
| |
| commit 04e046ca57ebed3943422dee10eec9e73aec081e 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: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/pci/endpoint/pci-epc-mem.c b/drivers/pci/endpoint/pci-epc-mem.c |
| index d2b174ce15de..abfac1109a13 100644 |
| --- a/drivers/pci/endpoint/pci-epc-mem.c |
| +++ b/drivers/pci/endpoint/pci-epc-mem.c |
| @@ -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 + ((phys_addr_t)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); |
| |
| diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h |
| index f641badc2c61..0c12d69dde92 100644 |
| --- a/include/linux/pci-epc.h |
| +++ b/include/linux/pci-epc.h |
| @@ -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; |
| }; |
| |
| /** |
| -- |
| 2.7.4 |
| |