Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iommu/dma: Explicitly sort PCI DMA windows

Originally, creating the dma_ranges resource list in pre-sorted fashion
was the simplest and most efficient way to enforce the order required by
iova_reserve_pci_windows(). However since then at least one PCI host
driver is now re-sorting the list for its own probe-time processing,
which doesn't seem entirely unreasonable, so that basic assumption no
longer holds. Make iommu-dma robust and get the sort order it needs by
explicitly sorting, which means we can also save the effort at creation
time and just build the list in whatever natural order the DT had.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Link: https://lore.kernel.org/r/35661036a7e4160850895f9b37f35408b6a29f2f.1652091160.git.robin.murphy@arm.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>

authored by

Robin Murphy and committed by
Joerg Roedel
b8397a8f a3884774

+13 -8
+12 -1
drivers/iommu/dma-iommu.c
··· 20 20 #include <linux/iommu.h> 21 21 #include <linux/iova.h> 22 22 #include <linux/irq.h> 23 + #include <linux/list_sort.h> 23 24 #include <linux/mm.h> 24 25 #include <linux/mutex.h> 25 26 #include <linux/pci.h> ··· 415 414 return 0; 416 415 } 417 416 417 + static int iommu_dma_ranges_sort(void *priv, const struct list_head *a, 418 + const struct list_head *b) 419 + { 420 + struct resource_entry *res_a = list_entry(a, typeof(*res_a), node); 421 + struct resource_entry *res_b = list_entry(b, typeof(*res_b), node); 422 + 423 + return res_a->res->start > res_b->res->start; 424 + } 425 + 418 426 static int iova_reserve_pci_windows(struct pci_dev *dev, 419 427 struct iova_domain *iovad) 420 428 { ··· 442 432 } 443 433 444 434 /* Get reserved DMA windows from host bridge */ 435 + list_sort(NULL, &bridge->dma_ranges, iommu_dma_ranges_sort); 445 436 resource_list_for_each_entry(window, &bridge->dma_ranges) { 446 437 end = window->res->start - window->offset; 447 438 resv_iova: ··· 451 440 hi = iova_pfn(iovad, end); 452 441 reserve_iova(iovad, lo, hi); 453 442 } else if (end < start) { 454 - /* dma_ranges list should be sorted */ 443 + /* DMA ranges should be non-overlapping */ 455 444 dev_err(&dev->dev, 456 445 "Failed to reserve IOVA [%pa-%pa]\n", 457 446 &start, &end);
+1 -7
drivers/pci/of.c
··· 369 369 370 370 dev_dbg(dev, "Parsing dma-ranges property...\n"); 371 371 for_each_of_pci_range(&parser, &range) { 372 - struct resource_entry *entry; 373 372 /* 374 373 * If we failed translation or got a zero-sized region 375 374 * then skip this range ··· 392 393 goto failed; 393 394 } 394 395 395 - /* Keep the resource list sorted */ 396 - resource_list_for_each_entry(entry, ib_resources) 397 - if (entry->res->start > res->start) 398 - break; 399 - 400 - pci_add_resource_offset(&entry->node, res, 396 + pci_add_resource_offset(ib_resources, res, 401 397 res->start - range.pci_addr); 402 398 } 403 399