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

PCI: Add pci_mmap_resource_range() and use it for ARM64

Starting to leave behind the legacy of the pci_mmap_page_range() interface
which takes "user-visible" BAR addresses. This takes just the resource and
offset.

For now, both APIs coexist and depending on the platform, one is
implemented as a wrapper around the other.

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>

authored by

David Woodhouse and committed by
Bjorn Helgaas
f7195824 f66e2258

+125 -20
+7 -3
Documentation/filesystems/sysfs-pci.txt
··· 113 113 -------------------------------------- 114 114 115 115 In order to support PCI resource mapping as described above, Linux platform 116 - code must define HAVE_PCI_MMAP and provide a pci_mmap_page_range function. 117 - Platforms are free to only support subsets of the mmap functionality, but 118 - useful return codes should be provided. 116 + code should ideally define ARCH_GENERIC_PCI_MMAP_RESOURCE and use the generic 117 + implementation of that functionality. To support the historical interface of 118 + mmap() through files in /proc/bus/pci, platforms may also set HAVE_PCI_MMAP. 119 + 120 + Alternatively, platforms which set HAVE_PCI_MMAP may provide their own 121 + implementation of pci_mmap_page_range() instead of defining 122 + ARCH_GENERIC_PCI_MMAP_RESOURCE. 119 123 120 124 Platforms which support write-combining maps of PCI resources must define 121 125 arch_can_pci_mmap_wc() which shall evaluate to non-zero at runtime when
+2
arch/arm64/include/asm/pci.h
··· 22 22 */ 23 23 #define PCI_DMA_BUS_IS_PHYS (0) 24 24 25 + #define ARCH_GENERIC_PCI_MMAP_RESOURCE 1 26 + 25 27 extern int isa_dma_bridge_buggy; 26 28 27 29 #ifdef CONFIG_PCI
+1 -1
drivers/pci/Makefile
··· 4 4 5 5 obj-y += access.o bus.o probe.o host-bridge.o remove.o pci.o \ 6 6 pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \ 7 - irq.o vpd.o setup-bus.o vc.o 7 + irq.o vpd.o setup-bus.o vc.o mmap.o 8 8 obj-$(CONFIG_PROC_FS) += proc.o 9 9 obj-$(CONFIG_SYSFS) += slot.o 10 10
+95
drivers/pci/mmap.c
··· 1 + /* 2 + * mmap.c — generic PCI resource mmap helper 3 + * 4 + * Copyright © 2017 Amazon.com, Inc. or its affiliates. 5 + * 6 + * Author: David Woodhouse <dwmw2@infradead.org> 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + */ 12 + 13 + #include <linux/kernel.h> 14 + #include <linux/mm.h> 15 + #include <linux/pci.h> 16 + 17 + #ifdef ARCH_GENERIC_PCI_MMAP_RESOURCE 18 + 19 + /* 20 + * Modern setup: generic pci_mmap_resource_range(), and implement the legacy 21 + * pci_mmap_page_range() (if needed) as a wrapper round it. 22 + */ 23 + 24 + #ifdef HAVE_PCI_MMAP 25 + int pci_mmap_page_range(struct pci_dev *pdev, int bar, 26 + struct vm_area_struct *vma, 27 + enum pci_mmap_state mmap_state, int write_combine) 28 + { 29 + resource_size_t start, end; 30 + 31 + pci_resource_to_user(pdev, bar, &pdev->resource[bar], &start, &end); 32 + 33 + /* Adjust vm_pgoff to be the offset within the resource */ 34 + vma->vm_pgoff -= start >> PAGE_SHIFT; 35 + return pci_mmap_resource_range(pdev, bar, vma, mmap_state, 36 + write_combine); 37 + } 38 + #endif 39 + 40 + static const struct vm_operations_struct pci_phys_vm_ops = { 41 + #ifdef CONFIG_HAVE_IOREMAP_PROT 42 + .access = generic_access_phys, 43 + #endif 44 + }; 45 + 46 + int pci_mmap_resource_range(struct pci_dev *pdev, int bar, 47 + struct vm_area_struct *vma, 48 + enum pci_mmap_state mmap_state, int write_combine) 49 + { 50 + unsigned long size; 51 + 52 + if (mmap_state == pci_mmap_io) 53 + return -EINVAL; 54 + 55 + size = ((pci_resource_len(pdev, bar) - 1) >> PAGE_SHIFT) + 1; 56 + if (vma->vm_pgoff + vma_pages(vma) > size) 57 + return -EINVAL; 58 + 59 + if (write_combine) 60 + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); 61 + else 62 + vma->vm_page_prot = pgprot_device(vma->vm_page_prot); 63 + 64 + vma->vm_pgoff += (pci_resource_start(pdev, bar) >> PAGE_SHIFT); 65 + vma->vm_ops = &pci_phys_vm_ops; 66 + 67 + return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, 68 + vma->vm_end - vma->vm_start, 69 + vma->vm_page_prot); 70 + } 71 + 72 + #elif defined(HAVE_PCI_MMAP) /* && !ARCH_GENERIC_PCI_MMAP_RESOURCE */ 73 + 74 + /* 75 + * Legacy setup: Impement pci_mmap_resource_range() as a wrapper around 76 + * the architecture's pci_mmap_page_range(), converting to "user visible" 77 + * addresses as necessary. 78 + */ 79 + 80 + int pci_mmap_resource_range(struct pci_dev *pdev, int bar, 81 + struct vm_area_struct *vma, 82 + enum pci_mmap_state mmap_state, int write_combine) 83 + { 84 + resource_size_t start, end; 85 + 86 + /* 87 + * pci_mmap_page_range() expects the same kind of entry as coming 88 + * from /proc/bus/pci/ which is a "user visible" value. If this is 89 + * different from the resource itself, arch will do necessary fixup. 90 + */ 91 + pci_resource_to_user(pdev, bar, &pdev->resource[bar], &start, &end); 92 + vma->vm_pgoff += start >> PAGE_SHIFT; 93 + return pci_mmap_page_range(pdev, bar, vma, mmap_state, write_combine); 94 + } 95 + #endif
+3 -10
drivers/pci/pci-sysfs.c
··· 980 980 } 981 981 #endif /* HAVE_PCI_LEGACY */ 982 982 983 - #ifdef HAVE_PCI_MMAP 983 + #if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) 984 984 985 985 int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma, 986 986 enum pci_mmap_api mmap_api) ··· 1019 1019 struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); 1020 1020 int bar = (unsigned long)attr->private; 1021 1021 enum pci_mmap_state mmap_type; 1022 - resource_size_t start, end; 1023 1022 struct resource *res = &pdev->resource[bar]; 1024 1023 1025 1024 if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start)) ··· 1032 1033 (u64)pci_resource_len(pdev, bar)); 1033 1034 return -EINVAL; 1034 1035 } 1035 - 1036 - /* pci_mmap_page_range() expects the same kind of entry as coming 1037 - * from /proc/bus/pci/ which is a "user visible" value. If this is 1038 - * different from the resource itself, arch will do necessary fixup. 1039 - */ 1040 - pci_resource_to_user(pdev, bar, res, &start, &end); 1041 - vma->vm_pgoff += start >> PAGE_SHIFT; 1042 1036 mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; 1043 - return pci_mmap_page_range(pdev, bar, vma, mmap_type, write_combine); 1037 + 1038 + return pci_mmap_resource_range(pdev, bar, vma, mmap_type, write_combine); 1044 1039 } 1045 1040 1046 1041 static int pci_mmap_resource_uc(struct file *filp, struct kobject *kobj,
+2 -2
drivers/pci/pci.h
··· 21 21 void pci_remove_firmware_label_files(struct pci_dev *pdev); 22 22 #endif 23 23 void pci_cleanup_rom(struct pci_dev *dev); 24 - #ifdef HAVE_PCI_MMAP 24 + 25 25 enum pci_mmap_api { 26 26 PCI_MMAP_SYSFS, /* mmap on /sys/bus/pci/devices/<BDF>/resource<N> */ 27 27 PCI_MMAP_PROCFS /* mmap on /proc/bus/pci/<BDF> */ 28 28 }; 29 29 int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai, 30 30 enum pci_mmap_api mmap_api); 31 - #endif 31 + 32 32 int pci_probe_reset_function(struct pci_dev *dev); 33 33 34 34 /**
+15 -4
include/linux/pci.h
··· 1626 1626 1627 1627 #include <asm/pci.h> 1628 1628 1629 - /* Map a range of PCI memory or I/O space for a device into user space. 1630 - * Architectures provide this function if they set HAVE_PCI_MMAP, and 1631 - * it accepts the 'write_combine' argument when arch_can_pci_mmap_wc() 1632 - * evaluates to nonzero. */ 1629 + /* These two functions provide almost identical functionality. Depennding 1630 + * on the architecture, one will be implemented as a wrapper around the 1631 + * other (in drivers/pci/mmap.c). 1632 + * 1633 + * pci_mmap_resource_range() maps a specific BAR, and vm->vm_pgoff 1634 + * is expected to be an offset within that region. 1635 + * 1636 + * pci_mmap_page_range() is the legacy architecture-specific interface, 1637 + * which accepts a "user visible" resource address converted by 1638 + * pci_resource_to_user(), as used in the legacy mmap() interface in 1639 + * /proc/bus/pci/. 1640 + */ 1641 + int pci_mmap_resource_range(struct pci_dev *dev, int bar, 1642 + struct vm_area_struct *vma, 1643 + enum pci_mmap_state mmap_state, int write_combine); 1633 1644 int pci_mmap_page_range(struct pci_dev *pdev, int bar, 1634 1645 struct vm_area_struct *vma, 1635 1646 enum pci_mmap_state mmap_state, int write_combine);