Message ID | PS2P216MB0642603E54D081F0DB349F5280930@PS2P216MB0642.KORP216.PROD.OUTLOOK.COM (mailing list archive) |
---|---|
State | New, archived |
Delegated to: | Bjorn Helgaas |
Headers | show |
Series | PCI: Consider alignment of hot-added bridges when distributing available resources | expand |
Hi, Please indicate in the $subject that this is second version of the patch as explained in: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#the-canonical-patch-format For example here the $subject should look like [PATCH v2] PCI: Consider alignment of hot-added bridges... You can use git format-patch --subject-prefix="PATCH v2" ... to generate such patch. On Sat, Feb 02, 2019 at 04:25:02PM +0000, Nicholas Johnson wrote: > New systems with Thunderbolt are starting to use native PCI enumeration. > Mika Westerberg's patch "PCI: Distribute available resources to hotplug > capable PCIe downstream ports" > (https://patchwork.kernel.org/patch/9972155/) adds code to expand > downstream PCI hotplug bridges to consume all remaining resource space > in the parent bridge. It is a crucial patch for supporting Thunderbolt > native enumeration on Linux. > > However, it does not consider bridge alignment in all cases, which rules > out hot-adding an external graphics processor at the end of a > Thunderbolt daisy chain. Hot-adding such a device will likely fail to > work with the existing code. It also might disrupt the operation of > existing devices or prevent the subsequent hot-plugging of lower aligned > devices if the kernel frees and reallocates upstream resources to > attempt assign the resources that failed to assign in the first pass. By > Intel's ruling, Thunderbolt external graphics processors are generally > meant to function only as the first and only device in the chain. > However, there is no technical reason that prevents it from being > possible if sufficient resources are available, and people are likely to > attempt such configurations with Thunderbolt devices if they own such > hardware. Hence, I argue that we should improve the user experience and > reduce the number of constraints placed on the user by always > considering resource alignment, which will make such configurations > possible. > > The other problem with the patch is that it is incompatible with > resources allocated by "pci=hpmemsize=nnM" due to a check which does not > allow for dev_res->add_size to be reduced. This check also makes a big > assumption that the hpmemsize is small but non-zero, and no action has > been taken to ensure that. In the current state, any bridge smaller than > hpmemsize will likely fail to size correctly, which will cause major > issues if the default were to change, or if the user also wants to > configure non-Thunderbolt hotplug bridges simultaneously. I argue that > if assumptions and limitations can be removed with no risks and adverse > effects, then it should be done. > > The former problem is solved by rewriting the > pci_bus_distribute_available_resources() function with more information > passed in the arguments, eliminating assumptions about the initial > bridge alignment. My patch makes no assumptions about the alignment of > hot-added bridges, allowing for any device to be hot-added, given > sufficient resources are available. > > The latter problem is solved by removing the check preventing the > shrinking of dev_res->add_size, which allows for the distribution of > resources if hpmemsize is non-zero. It can be made to work with zero > hpmemsize with two-line patches in pbus_size_mem() and pbus_size_io(), > or by modifying extend_bridge_window() to add a new entry to the > additional resource list if one does not exist. In theory, and by my > testing, the removal of this check does not affect the functionality of > the resource distribution with firmware-allocated resources. But it does > enable the same functionality when using pci=hpmemsize=nnM, which was > not possible prior. This may be one piece of the puzzle needed to > support Thunderbolt add-in cards that support native PCI enumeration, > without any platform dependencies. > > I have tested this proposed patch extensively. Using Linux-allocated > resources, it works perfectly. I have two Gigabyte GC-TITAN RIDGE > Thunderbolt 3 add-in cards in my desktop, and a Dell XPS 9370 with the > Dell XPS 9380 Thunderbolt NVM40 firmware flashed. My peripherals are > three HP ZBook Thunderbolt 3 docks, two external graphics enclosures > with AMD Fiji XT in both, a Promise SANLink3 N1 (AQC107S), and a Dell > Thunderbolt 3 NVMe SSD. All configurations of these devices worked well, > and I can no longer make it fail if I try to. My testing with > firmware-allocated resources is limited due to using computers with > Alpine Ridge BIOS support. However, I did get manage to test the patch > with firmware-allocated resources by enabling the Alpine Ridge BIOS > support and forcing pcie_ports=native, and the results were perfect. > > Mika Westerberg has agreed to test this on an official platform with > native enumeration firmware support to be sure that it works in this > situation. It is also appropriate that he reviews these changes as he > wrote the original code and any changes made to Thunderbolt-critical > code cannot be allowed to break any of the base requirements for > Thunderbolt. I would like to thank him for putting up with my emails and > trying to help where he can, and for the great work he has done on > Thunderbolt in Linux. > > I have more patches in the pipeline to further improve the Thunderbolt > experience on Linux on platforms without BIOS support. This is the most > technical but least user-facing one planned. The most exciting changes > are yet to come. > > Edits: These should be put below '---' in the patch as described in https://www.kernel.org/doc/html/latest/process/submitting-patches.html#the-canonical-patch-format > I have made code styling changes as suggested by Mika Westerberg. > > I have been testing Thunderbolt devices with my other host card which > happens to be in SL0 mode. This means that devices are discovered much > more quickly. I noticed that multiple devices can be enumerated > together, rather than each getting enumerated before the next appears. > It turns out that this can break the allocation, but I have absolutely > no idea why. I have modified the patch to solve this problem. Before, > extend_bridge_window() used add_size to change the resource size. Now it > simply changes the size of the actual resource, and clears the add_size > to zero if a list entry exists. That solves the issue, and proves that > the calculated resource sizes are not at fault (the algorithm is sound). > Hence, I recommend this version with the modification be applied. You should split that into a separate patch as it is different issue AFAICT. Furthermore I think it may be good idea to spend more time investigating and hopefully root causing the problem.
Hi Nicholas, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on pci/next] [also build test WARNING on v5.0-rc4 next-20190204] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Nicholas-Johnson/PCI-Consider-alignment-of-hot-added-bridges-when-distributing-available-resources/20190204-182638 base: https://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git next config: i386-randconfig-x007-201905 (attached as .config) compiler: gcc-8 (Debian 8.2.0-14) 8.2.0 reproduce: # save the attached .config to linux build tree make ARCH=i386 All warnings (new ones prefixed by >>): In file included from include/linux/printk.h:331, from include/linux/kernel.h:14, from drivers//pci/setup-bus.c:18: drivers//pci/setup-bus.c: In function 'extend_bridge_window': >> drivers//pci/setup-bus.c:1831:18: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 5 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=] pci_dbg(bridge, "bridge window %pR extended by 0x%016llx\n", res, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dynamic_debug.h:135:39: note: in definition of macro 'dynamic_dev_dbg' __dynamic_dev_dbg(&descriptor, dev, fmt, \ ^~~ include/linux/device.h:1473:23: note: in expansion of macro 'dev_fmt' dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__) ^~~~~~~ include/linux/pci.h:2362:36: note: in expansion of macro 'dev_dbg' #define pci_dbg(pdev, fmt, arg...) dev_dbg(&(pdev)->dev, fmt, ##arg) ^~~~~~~ drivers//pci/setup-bus.c:1831:2: note: in expansion of macro 'pci_dbg' pci_dbg(bridge, "bridge window %pR extended by 0x%016llx\n", res, ^~~~~~~ vim +1831 drivers//pci/setup-bus.c 1816 1817 static void extend_bridge_window(struct pci_dev *bridge, struct resource *res, 1818 struct list_head *add_list, resource_size_t available) 1819 { 1820 struct pci_dev_resource *dev_res; 1821 1822 if (res->parent) 1823 return; 1824 1825 /* 1826 * Hot-adding multiple Thunderbolt devices in SL0 might result in 1827 * multiple devices being enumerated together. This can break the 1828 * resource allocation if the resource sizes are specified with 1829 * add_size instead of simply changing the resource size. 1830 */ > 1831 pci_dbg(bridge, "bridge window %pR extended by 0x%016llx\n", res, 1832 available - resource_size(res)); 1833 res->end = res->start + available - 1; 1834 1835 /* 1836 * If a list entry exists, we need to remove any additional size 1837 * requested because that could interfere with the alignment and 1838 * sizing done when distributing resources, causing resources to 1839 * fail to allocate later on. 1840 */ 1841 dev_res = res_to_dev_res(add_list, res); 1842 if (!dev_res) 1843 return; 1844 1845 dev_res->add_size = 0; 1846 } 1847 --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi Nicholas, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on pci/next] [also build test WARNING on v5.0-rc4 next-20190204] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Nicholas-Johnson/PCI-Consider-alignment-of-hot-added-bridges-when-distributing-available-resources/20190204-182638 base: https://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git next config: i386-randconfig-s0-02041749 (attached as .config) compiler: gcc-6 (Debian 6.5.0-2) 6.5.0 20181026 reproduce: # save the attached .config to linux build tree make ARCH=i386 All warnings (new ones prefixed by >>): In file included from include/linux/pci.h:31:0, from drivers/pci/setup-bus.c:20: drivers/pci/setup-bus.c: In function 'extend_bridge_window': drivers/pci/setup-bus.c:1831:18: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 5 has type 'resource_size_t {aka unsigned int}' [-Wformat=] pci_dbg(bridge, "bridge window %pR extended by 0x%016llx\n", res, ^ include/linux/device.h:1380:22: note: in definition of macro 'dev_fmt' #define dev_fmt(fmt) fmt ^~~ >> include/linux/pci.h:2362:36: note: in expansion of macro 'dev_dbg' #define pci_dbg(pdev, fmt, arg...) dev_dbg(&(pdev)->dev, fmt, ##arg) ^~~~~~~ >> drivers/pci/setup-bus.c:1831:2: note: in expansion of macro 'pci_dbg' pci_dbg(bridge, "bridge window %pR extended by 0x%016llx\n", res, ^~~~~~~ Cyclomatic Complexity 5 include/linux/compiler.h:__read_once_size Cyclomatic Complexity 5 include/linux/compiler.h:__write_once_size Cyclomatic Complexity 1 include/linux/kasan-checks.h:kasan_check_read Cyclomatic Complexity 1 arch/x86/include/asm/bitops.h:__ffs Cyclomatic Complexity 1 arch/x86/include/asm/bitops.h:fls Cyclomatic Complexity 1 include/linux/log2.h:__ilog2_u32 Cyclomatic Complexity 1 include/linux/list.h:__list_del Cyclomatic Complexity 1 include/linux/list.h:list_empty Cyclomatic Complexity 1 include/asm-generic/getorder.h:__get_order Cyclomatic Complexity 1 arch/x86/include/asm/atomic.h:arch_atomic_read Cyclomatic Complexity 1 include/asm-generic/atomic-instrumented.h:atomic_read Cyclomatic Complexity 1 include/linux/ioport.h:resource_size Cyclomatic Complexity 1 include/linux/ioport.h:resource_type Cyclomatic Complexity 3 include/linux/slab.h:kmalloc_type Cyclomatic Complexity 56 include/linux/slab.h:kmalloc_index Cyclomatic Complexity 67 include/linux/slab.h:kmalloc_large Cyclomatic Complexity 7 include/linux/slab.h:kmalloc Cyclomatic Complexity 1 include/linux/slab.h:kzalloc Cyclomatic Complexity 1 include/linux/pci.h:pci_is_root_bus Cyclomatic Complexity 1 include/linux/pci.h:pci_is_bridge Cyclomatic Complexity 1 include/linux/pci.h:pci_is_enabled Cyclomatic Complexity 1 arch/x86/include/asm/pci.h:pci_domain_nr Cyclomatic Complexity 2 include/linux/acpi.h:acpi_device_handle Cyclomatic Complexity 1 drivers/pci/pci.h:pci_has_subordinate Cyclomatic Complexity 1 drivers/pci/setup-bus.c:reset_resource Cyclomatic Complexity 2 drivers/pci/setup-bus.c:pci_fail_res_type_mask Cyclomatic Complexity 1 drivers/pci/setup-bus.c:pci_realloc_enabled Cyclomatic Complexity 1 drivers/pci/setup-bus.c:pci_realloc_detect Cyclomatic Complexity 7 drivers/pci/setup-bus.c:pci_bridge_check_ranges Cyclomatic Complexity 4 drivers/pci/setup-bus.c:res_to_dev_res Cyclomatic Complexity 2 drivers/pci/setup-bus.c:get_res_add_size Cyclomatic Complexity 2 drivers/pci/setup-bus.c:get_res_add_align Cyclomatic Complexity 7 drivers/pci/setup-bus.c:calculate_iosize Cyclomatic Complexity 6 drivers/pci/setup-bus.c:calculate_mem_align Cyclomatic Complexity 7 drivers/pci/setup-bus.c:calculate_memsize Cyclomatic Complexity 17 drivers/pci/setup-bus.c:pci_need_to_release Cyclomatic Complexity 12 include/linux/ioport.h:resource_contains Cyclomatic Complexity 4 drivers/pci/setup-bus.c:pci_bus_get_depth Cyclomatic Complexity 5 drivers/pci/setup-bus.c:extend_bridge_window Cyclomatic Complexity 3 arch/x86/include/asm/div64.h:div_u64_rem Cyclomatic Complexity 1 include/linux/math64.h:div_u64 Cyclomatic Complexity 3 drivers/pci/setup-bus.c:pci_setup_bridge_mmio Cyclomatic Complexity 5 drivers/pci/setup-bus.c:pci_setup_bridge_mmio_pref Cyclomatic Complexity 5 drivers/pci/setup-bus.c:pci_setup_bridge_io Cyclomatic Complexity 7 drivers/pci/setup-bus.c:__pci_setup_bridge Cyclomatic Complexity 10 drivers/pci/setup-bus.c:pci_claim_device_resources Cyclomatic Complexity 4 drivers/pci/setup-bus.c:pci_bus_allocate_dev_resources Cyclomatic Complexity 2 include/linux/list.h:__list_add Cyclomatic Complexity 1 include/linux/list.h:list_add Cyclomatic Complexity 3 drivers/pci/setup-bus.c:add_to_list Cyclomatic Complexity 23 drivers/pci/setup-bus.c:pci_bus_size_cardbus Cyclomatic Complexity 1 include/linux/list.h:list_add_tail Cyclomatic Complexity 22 drivers/pci/setup-bus.c:find_free_bus_resource Cyclomatic Complexity 14 drivers/pci/setup-bus.c:pci_bus_dump_res Cyclomatic Complexity 4 drivers/pci/setup-bus.c:pci_bus_dump_resources Cyclomatic Complexity 2 include/linux/list.h:__list_del_entry Cyclomatic Complexity 1 include/linux/list.h:list_move_tail Cyclomatic Complexity 1 include/linux/list.h:list_del Cyclomatic Complexity 4 drivers/pci/setup-bus.c:remove_from_list Cyclomatic Complexity 2 drivers/pci/setup-bus.c:free_list Cyclomatic Complexity 15 drivers/pci/setup-bus.c:assign_requested_resources_sorted Cyclomatic Complexity 12 drivers/pci/setup-bus.c:reassign_resources_sorted Cyclomatic Complexity 8 drivers/pci/setup-bus.c:assign_fixed_resource_on_bus Cyclomatic Complexity 15 drivers/pci/setup-bus.c:pdev_assign_fixed_resources Cyclomatic Complexity 20 drivers/pci/setup-bus.c:pci_bridge_release_resources Cyclomatic Complexity 13 drivers/pci/setup-bus.c:pci_bus_release_bridge_resources Cyclomatic Complexity 9 drivers/pci/setup-bus.c:pci_setup_cardbus Cyclomatic Complexity 1 drivers/pci/setup-bus.c:pcibios_setup_bridge Cyclomatic Complexity 1 drivers/pci/setup-bus.c:pci_setup_bridge Cyclomatic Complexity 11 drivers/pci/setup-bus.c:pci_claim_bridge_resource Cyclomatic Complexity 10 drivers/pci/setup-bus.c:pci_claim_bridge_resources Cyclomatic Complexity 4 drivers/pci/setup-bus.c:pci_bus_allocate_resources Cyclomatic Complexity 1 drivers/pci/setup-bus.c:pcibios_window_alignment Cyclomatic Complexity 7 drivers/pci/setup-bus.c:window_alignment Cyclomatic Complexity 5 drivers/pci/setup-bus.c:pci_cardbus_resource_alignment Cyclomatic Complexity 5 drivers/pci/pci.h:pci_resource_alignment Cyclomatic Complexity 34 drivers/pci/setup-bus.c:pbus_size_io Cyclomatic Complexity 55 drivers/pci/setup-bus.c:pbus_size_mem Cyclomatic Complexity 18 drivers/pci/setup-bus.c:pdev_sort_resources Cyclomatic Complexity 7 drivers/pci/setup-bus.c:__dev_sort_resources Cyclomatic Complexity 26 drivers/pci/setup-bus.c:__assign_resources_sorted Cyclomatic Complexity 2 drivers/pci/setup-bus.c:pbus_assign_resources_sorted Cyclomatic Complexity 1 drivers/pci/setup-bus.c:pdev_assign_resources_sorted Cyclomatic Complexity 61 drivers/pci/setup-bus.c:pci_bus_distribute_available_resources Cyclomatic Complexity 3 drivers/pci/setup-bus.c:pci_bridge_distribute_available_resources Cyclomatic Complexity 22 drivers/pci/setup-bus.c:__pci_bus_size_bridges Cyclomatic Complexity 1 drivers/pci/setup-bus.c:pci_bus_size_bridges Cyclomatic Complexity 7 drivers/pci/setup-bus.c:__pci_bus_assign_resources Cyclomatic Complexity 5 drivers/pci/setup-bus.c:__pci_bridge_assign_resources Cyclomatic Complexity 1 drivers/pci/setup-bus.c:pci_bus_assign_resources Cyclomatic Complexity 1 drivers/pci/setup-bus.c:pci_bus_claim_resources Cyclomatic Complexity 3 drivers/pci/setup-bus.c:pci_realloc_get_opt Cyclomatic Complexity 19 drivers/pci/setup-bus.c:pci_assign_unassigned_root_bus_resources Cyclomatic Complexity 5 drivers/pci/setup-bus.c:pci_assign_unassigned_resources Cyclomatic Complexity 10 drivers/pci/setup-bus.c:pci_assign_unassigned_bridge_resources Cyclomatic Complexity 21 drivers/pci/setup-bus.c:pci_reassign_bridge_resources Cyclomatic Complexity 4 drivers/pci/setup-bus.c:pci_assign_unassigned_bus_resources -- In file included from include/linux/pci.h:31:0, from drivers//pci/setup-bus.c:20: drivers//pci/setup-bus.c: In function 'extend_bridge_window': drivers//pci/setup-bus.c:1831:18: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 5 has type 'resource_size_t {aka unsigned int}' [-Wformat=] pci_dbg(bridge, "bridge window %pR extended by 0x%016llx\n", res, ^ include/linux/device.h:1380:22: note: in definition of macro 'dev_fmt' #define dev_fmt(fmt) fmt ^~~ >> include/linux/pci.h:2362:36: note: in expansion of macro 'dev_dbg' #define pci_dbg(pdev, fmt, arg...) dev_dbg(&(pdev)->dev, fmt, ##arg) ^~~~~~~ drivers//pci/setup-bus.c:1831:2: note: in expansion of macro 'pci_dbg' pci_dbg(bridge, "bridge window %pR extended by 0x%016llx\n", res, ^~~~~~~ Cyclomatic Complexity 5 include/linux/compiler.h:__read_once_size Cyclomatic Complexity 5 include/linux/compiler.h:__write_once_size Cyclomatic Complexity 1 include/linux/kasan-checks.h:kasan_check_read Cyclomatic Complexity 1 arch/x86/include/asm/bitops.h:__ffs Cyclomatic Complexity 1 arch/x86/include/asm/bitops.h:fls Cyclomatic Complexity 1 include/linux/log2.h:__ilog2_u32 Cyclomatic Complexity 1 include/linux/list.h:__list_del Cyclomatic Complexity 1 include/linux/list.h:list_empty Cyclomatic Complexity 1 include/asm-generic/getorder.h:__get_order Cyclomatic Complexity 1 arch/x86/include/asm/atomic.h:arch_atomic_read Cyclomatic Complexity 1 include/asm-generic/atomic-instrumented.h:atomic_read Cyclomatic Complexity 1 include/linux/ioport.h:resource_size Cyclomatic Complexity 1 include/linux/ioport.h:resource_type Cyclomatic Complexity 3 include/linux/slab.h:kmalloc_type Cyclomatic Complexity 56 include/linux/slab.h:kmalloc_index Cyclomatic Complexity 67 include/linux/slab.h:kmalloc_large Cyclomatic Complexity 7 include/linux/slab.h:kmalloc Cyclomatic Complexity 1 include/linux/slab.h:kzalloc Cyclomatic Complexity 1 include/linux/pci.h:pci_is_root_bus Cyclomatic Complexity 1 include/linux/pci.h:pci_is_bridge Cyclomatic Complexity 1 include/linux/pci.h:pci_is_enabled Cyclomatic Complexity 1 arch/x86/include/asm/pci.h:pci_domain_nr Cyclomatic Complexity 2 include/linux/acpi.h:acpi_device_handle Cyclomatic Complexity 1 drivers//pci/pci.h:pci_has_subordinate Cyclomatic Complexity 1 drivers//pci/setup-bus.c:reset_resource Cyclomatic Complexity 2 drivers//pci/setup-bus.c:pci_fail_res_type_mask Cyclomatic Complexity 1 drivers//pci/setup-bus.c:pci_realloc_enabled Cyclomatic Complexity 1 drivers//pci/setup-bus.c:pci_realloc_detect Cyclomatic Complexity 7 drivers//pci/setup-bus.c:pci_bridge_check_ranges Cyclomatic Complexity 4 drivers//pci/setup-bus.c:res_to_dev_res Cyclomatic Complexity 2 drivers//pci/setup-bus.c:get_res_add_size Cyclomatic Complexity 2 drivers//pci/setup-bus.c:get_res_add_align Cyclomatic Complexity 7 drivers//pci/setup-bus.c:calculate_iosize Cyclomatic Complexity 6 drivers//pci/setup-bus.c:calculate_mem_align Cyclomatic Complexity 7 drivers//pci/setup-bus.c:calculate_memsize Cyclomatic Complexity 17 drivers//pci/setup-bus.c:pci_need_to_release Cyclomatic Complexity 12 include/linux/ioport.h:resource_contains Cyclomatic Complexity 4 drivers//pci/setup-bus.c:pci_bus_get_depth Cyclomatic Complexity 5 drivers//pci/setup-bus.c:extend_bridge_window Cyclomatic Complexity 3 arch/x86/include/asm/div64.h:div_u64_rem Cyclomatic Complexity 1 include/linux/math64.h:div_u64 Cyclomatic Complexity 3 drivers//pci/setup-bus.c:pci_setup_bridge_mmio Cyclomatic Complexity 5 drivers//pci/setup-bus.c:pci_setup_bridge_mmio_pref Cyclomatic Complexity 5 drivers//pci/setup-bus.c:pci_setup_bridge_io Cyclomatic Complexity 7 drivers//pci/setup-bus.c:__pci_setup_bridge Cyclomatic Complexity 10 drivers//pci/setup-bus.c:pci_claim_device_resources Cyclomatic Complexity 4 drivers//pci/setup-bus.c:pci_bus_allocate_dev_resources Cyclomatic Complexity 2 include/linux/list.h:__list_add Cyclomatic Complexity 1 include/linux/list.h:list_add Cyclomatic Complexity 3 drivers//pci/setup-bus.c:add_to_list Cyclomatic Complexity 23 drivers//pci/setup-bus.c:pci_bus_size_cardbus Cyclomatic Complexity 1 include/linux/list.h:list_add_tail Cyclomatic Complexity 22 drivers//pci/setup-bus.c:find_free_bus_resource Cyclomatic Complexity 14 drivers//pci/setup-bus.c:pci_bus_dump_res Cyclomatic Complexity 4 drivers//pci/setup-bus.c:pci_bus_dump_resources Cyclomatic Complexity 2 include/linux/list.h:__list_del_entry Cyclomatic Complexity 1 include/linux/list.h:list_move_tail Cyclomatic Complexity 1 include/linux/list.h:list_del Cyclomatic Complexity 4 drivers//pci/setup-bus.c:remove_from_list Cyclomatic Complexity 2 drivers//pci/setup-bus.c:free_list Cyclomatic Complexity 15 drivers//pci/setup-bus.c:assign_requested_resources_sorted Cyclomatic Complexity 12 drivers//pci/setup-bus.c:reassign_resources_sorted Cyclomatic Complexity 8 drivers//pci/setup-bus.c:assign_fixed_resource_on_bus Cyclomatic Complexity 15 drivers//pci/setup-bus.c:pdev_assign_fixed_resources Cyclomatic Complexity 20 drivers//pci/setup-bus.c:pci_bridge_release_resources Cyclomatic Complexity 13 drivers//pci/setup-bus.c:pci_bus_release_bridge_resources Cyclomatic Complexity 9 drivers//pci/setup-bus.c:pci_setup_cardbus Cyclomatic Complexity 1 drivers//pci/setup-bus.c:pcibios_setup_bridge Cyclomatic Complexity 1 drivers//pci/setup-bus.c:pci_setup_bridge Cyclomatic Complexity 11 drivers//pci/setup-bus.c:pci_claim_bridge_resource Cyclomatic Complexity 10 drivers//pci/setup-bus.c:pci_claim_bridge_resources Cyclomatic Complexity 4 drivers//pci/setup-bus.c:pci_bus_allocate_resources Cyclomatic Complexity 1 drivers//pci/setup-bus.c:pcibios_window_alignment Cyclomatic Complexity 7 drivers//pci/setup-bus.c:window_alignment Cyclomatic Complexity 5 drivers//pci/setup-bus.c:pci_cardbus_resource_alignment Cyclomatic Complexity 5 drivers//pci/pci.h:pci_resource_alignment Cyclomatic Complexity 34 drivers//pci/setup-bus.c:pbus_size_io Cyclomatic Complexity 55 drivers//pci/setup-bus.c:pbus_size_mem Cyclomatic Complexity 18 drivers//pci/setup-bus.c:pdev_sort_resources Cyclomatic Complexity 7 drivers//pci/setup-bus.c:__dev_sort_resources Cyclomatic Complexity 26 drivers//pci/setup-bus.c:__assign_resources_sorted Cyclomatic Complexity 2 drivers//pci/setup-bus.c:pbus_assign_resources_sorted Cyclomatic Complexity 1 drivers//pci/setup-bus.c:pdev_assign_resources_sorted Cyclomatic Complexity 61 drivers//pci/setup-bus.c:pci_bus_distribute_available_resources Cyclomatic Complexity 3 drivers//pci/setup-bus.c:pci_bridge_distribute_available_resources Cyclomatic Complexity 22 drivers//pci/setup-bus.c:__pci_bus_size_bridges Cyclomatic Complexity 1 drivers//pci/setup-bus.c:pci_bus_size_bridges Cyclomatic Complexity 7 drivers//pci/setup-bus.c:__pci_bus_assign_resources Cyclomatic Complexity 5 drivers//pci/setup-bus.c:__pci_bridge_assign_resources Cyclomatic Complexity 1 drivers//pci/setup-bus.c:pci_bus_assign_resources Cyclomatic Complexity 1 drivers//pci/setup-bus.c:pci_bus_claim_resources Cyclomatic Complexity 3 drivers//pci/setup-bus.c:pci_realloc_get_opt Cyclomatic Complexity 19 drivers//pci/setup-bus.c:pci_assign_unassigned_root_bus_resources Cyclomatic Complexity 5 drivers//pci/setup-bus.c:pci_assign_unassigned_resources Cyclomatic Complexity 10 drivers//pci/setup-bus.c:pci_assign_unassigned_bridge_resources vim +/pci_dbg +1831 drivers/pci/setup-bus.c 1816 1817 static void extend_bridge_window(struct pci_dev *bridge, struct resource *res, 1818 struct list_head *add_list, resource_size_t available) 1819 { 1820 struct pci_dev_resource *dev_res; 1821 1822 if (res->parent) 1823 return; 1824 1825 /* 1826 * Hot-adding multiple Thunderbolt devices in SL0 might result in 1827 * multiple devices being enumerated together. This can break the 1828 * resource allocation if the resource sizes are specified with 1829 * add_size instead of simply changing the resource size. 1830 */ > 1831 pci_dbg(bridge, "bridge window %pR extended by 0x%016llx\n", res, 1832 available - resource_size(res)); 1833 res->end = res->start + available - 1; 1834 1835 /* 1836 * If a list entry exists, we need to remove any additional size 1837 * requested because that could interfere with the alignment and 1838 * sizing done when distributing resources, causing resources to 1839 * fail to allocate later on. 1840 */ 1841 dev_res = res_to_dev_res(add_list, res); 1842 if (!dev_res) 1843 return; 1844 1845 dev_res->add_size = 0; 1846 } 1847 --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
On Sat, Feb 02, 2019 at 04:25:02PM +0000, Nicholas Johnson wrote: > New systems with Thunderbolt are starting to use native PCI enumeration. To clarify, I assume "native PCI enumeration" means platform firmware is not involved in hotplug, which means we would use pciehp (not acpiphp) for these Thunderbolt hotplug events. > Mika Westerberg's patch "PCI: Distribute available resources to hotplug > capable PCIe downstream ports" > (https://patchwork.kernel.org/patch/9972155/) adds code to expand > downstream PCI hotplug bridges to consume all remaining resource space > in the parent bridge. It is a crucial patch for supporting Thunderbolt > native enumeration on Linux. > > However, it does not consider bridge alignment in all cases, which rules > out hot-adding an external graphics processor at the end of a > Thunderbolt daisy chain. Hot-adding such a device will likely fail to > work with the existing code. It also might disrupt the operation of > existing devices Have you observed disruption of existing devices when hot-adding a new device? That's a much more serious problem than if the new device doesn't work. It will always be possible that we don't have enough resources for a hot-added device, but it should never break a previously working device. If you've seen breakage of an existing device, I'd like to start with the minimum patch that solves only *that*, with following patches that improve the likelihood that hot-added devices will work. It's always good to break up patches into the smallest logical units (as long as none introduces a regression, i.e., after each patch, the kernel must build and work as well as before). That minimum patch may be a candidate for backporting, while the new functionality (improving support for hot-added devices) may not be. > or prevent the subsequent hot-plugging of lower aligned > devices if the kernel frees and reallocates upstream resources to > attempt assign the resources that failed to assign in the first pass. By > Intel's ruling, Thunderbolt external graphics processors are generally > meant to function only as the first and only device in the chain. Do you have a reference for this? Some background on this would be really useful in case those restrictions have any bearing on Linux. > However, there is no technical reason that prevents it from being > possible if sufficient resources are available, and people are likely to > attempt such configurations with Thunderbolt devices if they own such > hardware. Hence, I argue that we should improve the user experience and > reduce the number of constraints placed on the user by always > considering resource alignment, which will make such configurations > possible. > > The other problem with the patch is that it is incompatible with > resources allocated by "pci=hpmemsize=nnM" due to a check which does not > allow for dev_res->add_size to be reduced. This check also makes a big > assumption that the hpmemsize is small but non-zero, and no action has > been taken to ensure that. In the current state, any bridge smaller than > hpmemsize will likely fail to size correctly, which will cause major > issues if the default were to change, or if the user also wants to > configure non-Thunderbolt hotplug bridges simultaneously. I argue that > if assumptions and limitations can be removed with no risks and adverse > effects, then it should be done. > > The former problem is solved by rewriting the > pci_bus_distribute_available_resources() function with more information > passed in the arguments, eliminating assumptions about the initial > bridge alignment. My patch makes no assumptions about the alignment of > hot-added bridges, allowing for any device to be hot-added, given > sufficient resources are available. > > The latter problem is solved by removing the check preventing the > shrinking of dev_res->add_size, which allows for the distribution of > resources if hpmemsize is non-zero. It can be made to work with zero > hpmemsize with two-line patches in pbus_size_mem() and pbus_size_io(), > or by modifying extend_bridge_window() to add a new entry to the > additional resource list if one does not exist. In theory, and by my > testing, the removal of this check does not affect the functionality of > the resource distribution with firmware-allocated resources. But it does > enable the same functionality when using pci=hpmemsize=nnM, which was > not possible prior. This may be one piece of the puzzle needed to > support Thunderbolt add-in cards that support native PCI enumeration, > without any platform dependencies. You mention two problems with (apparently) two solutions. If it's possible to separate these into two (or more) patches, please do that. > I have tested this proposed patch extensively. Using Linux-allocated > resources, it works perfectly. I have two Gigabyte GC-TITAN RIDGE > Thunderbolt 3 add-in cards in my desktop, and a Dell XPS 9370 with the > Dell XPS 9380 Thunderbolt NVM40 firmware flashed. My peripherals are > three HP ZBook Thunderbolt 3 docks, two external graphics enclosures > with AMD Fiji XT in both, a Promise SANLink3 N1 (AQC107S), and a Dell > Thunderbolt 3 NVMe SSD. All configurations of these devices worked well, > and I can no longer make it fail if I try to. My testing with > firmware-allocated resources is limited due to using computers with > Alpine Ridge BIOS support. However, I did get manage to test the patch > with firmware-allocated resources by enabling the Alpine Ridge BIOS > support and forcing pcie_ports=native, and the results were perfect. > > Mika Westerberg has agreed to test this on an official platform with > native enumeration firmware support to be sure that it works in this > situation. It is also appropriate that he reviews these changes as he > wrote the original code and any changes made to Thunderbolt-critical > code cannot be allowed to break any of the base requirements for > Thunderbolt. I would like to thank him for putting up with my emails and > trying to help where he can, and for the great work he has done on > Thunderbolt in Linux. > > I have more patches in the pipeline to further improve the Thunderbolt > experience on Linux on platforms without BIOS support. This is the most > technical but least user-facing one planned. The most exciting changes > are yet to come. Some of this text, especially the previous two paragraphs and the following text, would better fit in a [0/N] cover letter because it will not be useful after this work is merged, i.e., it won't be interesting to people reading the git changelogs. > Edits: > > I have made code styling changes as suggested by Mika Westerberg. > > I have been testing Thunderbolt devices with my other host card which > happens to be in SL0 mode. This means that devices are discovered much > more quickly. I noticed that multiple devices can be enumerated > together, rather than each getting enumerated before the next appears. > It turns out that this can break the allocation, but I have absolutely > no idea why. I have modified the patch to solve this problem. Before, > extend_bridge_window() used add_size to change the resource size. Now it > simply changes the size of the actual resource, and clears the add_size > to zero if a list entry exists. That solves the issue, and proves that > the calculated resource sizes are not at fault (the algorithm is sound). > Hence, I recommend this version with the modification be applied. > > I have removed Mika Westerberg's "Tested-By" line to allow him to give > his approval for this new change. > > Observation: the fact that a single Thunderbolt dock can consume 1/4 of > the total IO space (16-bit, 0xffff) is evidence that the depreciated IO > bars need to be dropped from the kernel at some point. I/O space is still used by some devices, so we can't drop it completely. We can certainly improve Linux behavior when it's exhausted though. There are outstanding bugs in that area. > The docks have > four bridges each, with 4096-byte alignment. The IO BARs do not do > anything, crash the system if not claimed and spam dmesg when not > assigned. > > Signed-off-by: Nicholas-Johnson-opensource <nicholas.johnson-opensource@outlook.com.au> This part ------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ should be your proper name. I assume your name is "Nicholas Johnson", not "Nicholas-Johnson-opensource"? > --- > drivers/pci/setup-bus.c | 188 +++++++++++++++++++++------------------- > 1 file changed, 99 insertions(+), 89 deletions(-) > > diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c > index ed960436df5e..09310b6fcdb3 100644 > --- a/drivers/pci/setup-bus.c > +++ b/drivers/pci/setup-bus.c > @@ -1859,27 +1859,34 @@ static void extend_bridge_window(struct pci_dev *bridge, struct resource *res, > if (res->parent) > return; > > - if (resource_size(res) >= available) > - return; > + /* > + * Hot-adding multiple Thunderbolt devices in SL0 might result in > + * multiple devices being enumerated together. This can break the > + * resource allocation if the resource sizes are specified with > + * add_size instead of simply changing the resource size. > + */ > + pci_dbg(bridge, "bridge window %pR extended by 0x%016llx\n", res, > + available - resource_size(res)); > + res->end = res->start + available - 1; > > + /* > + * If a list entry exists, we need to remove any additional size > + * requested because that could interfere with the alignment and > + * sizing done when distributing resources, causing resources to > + * fail to allocate later on. > + */ > dev_res = res_to_dev_res(add_list, res); > if (!dev_res) > return; > > - /* Is there room to extend the window? */ > - if (available - resource_size(res) <= dev_res->add_size) > - return; > - > - dev_res->add_size = available - resource_size(res); > - pci_dbg(bridge, "bridge window %pR extended by %pa\n", res, > - &dev_res->add_size); > + dev_res->add_size = 0; > } > > static void pci_bus_distribute_available_resources(struct pci_bus *bus, > - struct list_head *add_list, resource_size_t available_io, > - resource_size_t available_mmio, resource_size_t available_mmio_pref) > + struct list_head *add_list, struct resource io, > + struct resource mmio, struct resource mmio_pref) > { > - resource_size_t remaining_io, remaining_mmio, remaining_mmio_pref; > + resource_size_t io_per_hp, mmio_per_hp, mmio_pref_per_hp, align; > unsigned int normal_bridges = 0, hotplug_bridges = 0; > struct resource *io_res, *mmio_res, *mmio_pref_res; > struct pci_dev *dev, *bridge = bus->self; > @@ -1889,25 +1896,32 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, > mmio_pref_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2]; > > /* > - * Update additional resource list (add_list) to fill all the > - * extra resource space available for this port except the space > - * calculated in __pci_bus_size_bridges() which covers all the > - * devices currently connected to the port and below. > + * The alignment of this bridge is yet to be considered, hence it must > + * be done now before extending its bridge window. A single bridge > + * might not be able to occupy the whole parent region if the alignment > + * differs - for example, an external GPU at the end of a Thunderbolt > + * daisy chain. > */ > - extend_bridge_window(bridge, io_res, add_list, available_io); > - extend_bridge_window(bridge, mmio_res, add_list, available_mmio); > - extend_bridge_window(bridge, mmio_pref_res, add_list, > - available_mmio_pref); > + align = pci_resource_alignment(bridge, io_res); > + if (!io_res->parent && align) > + io.start = ALIGN(io.start, align); > + > + align = pci_resource_alignment(bridge, mmio_res); > + if (!mmio_res->parent && align) > + mmio.start = ALIGN(mmio.start, align); > + > + align = pci_resource_alignment(bridge, mmio_pref_res); > + if (!mmio_pref_res->parent && align) > + mmio_pref.start = ALIGN(mmio_pref.start, align); > > /* > - * Calculate the total amount of extra resource space we can > - * pass to bridges below this one. This is basically the > - * extra space reduced by the minimal required space for the > - * non-hotplug bridges. > + * Update the resources to fill as much remaining resource space in the > + * parent bridge as possible, while considering alignment. > */ > - remaining_io = available_io; > - remaining_mmio = available_mmio; > - remaining_mmio_pref = available_mmio_pref; > + extend_bridge_window(bridge, io_res, add_list, resource_size(&io)); > + extend_bridge_window(bridge, mmio_res, add_list, resource_size(&mmio)); > + extend_bridge_window(bridge, mmio_pref_res, add_list, > + resource_size(&mmio_pref)); > > /* > * Calculate how many hotplug bridges and normal bridges there > @@ -1921,80 +1935,79 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, > normal_bridges++; > } > > + /* > + * There is only one bridge on the bus so it gets all possible > + * resources which it can then distribute to the possible > + * hotplug bridges below. > + */ > + if (hotplug_bridges + normal_bridges == 1) { > + dev = list_first_entry(&bus->devices, struct pci_dev, bus_list); > + if (dev->subordinate) > + pci_bus_distribute_available_resources(dev->subordinate, > + add_list, io, mmio, mmio_pref); > + return; > + } > + > + /* > + * Reduce the available resource space by what the > + * bridge and devices below it occupy. > + */ > for_each_pci_bridge(dev, bus) { > - const struct resource *res; > + struct resource *res; > + resource_size_t used_size; > > if (dev->is_hotplug_bridge) > continue; > > - /* > - * Reduce the available resource space by what the > - * bridge and devices below it occupy. > - */ > res = &dev->resource[PCI_BRIDGE_RESOURCES + 0]; > - if (!res->parent && available_io > resource_size(res)) > - remaining_io -= resource_size(res); > + align = pci_resource_alignment(dev, res); > + align = align ? ALIGN(io.start, align) - io.start : 0; > + used_size = align + resource_size(res); > + if (!res->parent && used_size <= resource_size(&io)) > + io.start += used_size; > > res = &dev->resource[PCI_BRIDGE_RESOURCES + 1]; > - if (!res->parent && available_mmio > resource_size(res)) > - remaining_mmio -= resource_size(res); > + align = pci_resource_alignment(dev, res); > + align = align ? ALIGN(mmio.start, align) - mmio.start : 0; > + used_size = align + resource_size(res); > + if (!res->parent && used_size <= resource_size(&mmio)) > + mmio.start += used_size; > > res = &dev->resource[PCI_BRIDGE_RESOURCES + 2]; > - if (!res->parent && available_mmio_pref > resource_size(res)) > - remaining_mmio_pref -= resource_size(res); > + align = pci_resource_alignment(dev, res); > + align = align ? ALIGN(mmio_pref.start, align) - > + mmio_pref.start : 0; > + used_size = align + resource_size(res); > + if (!res->parent && used_size <= resource_size(&mmio_pref)) > + mmio_pref.start += used_size; > } > > - /* > - * There is only one bridge on the bus so it gets all available > - * resources which it can then distribute to the possible > - * hotplug bridges below. > - */ > - if (hotplug_bridges + normal_bridges == 1) { > - dev = list_first_entry(&bus->devices, struct pci_dev, bus_list); > - if (dev->subordinate) { > - pci_bus_distribute_available_resources(dev->subordinate, > - add_list, available_io, available_mmio, > - available_mmio_pref); > - } > + if (!hotplug_bridges) > return; > - } > > /* > - * Go over devices on this bus and distribute the remaining > - * resource space between hotplug bridges. > + * Distribute any remaining resources equally between > + * the hotplug-capable downstream ports. > */ > - for_each_pci_bridge(dev, bus) { > - resource_size_t align, io, mmio, mmio_pref; > - struct pci_bus *b; > + io_per_hp = div64_ul(resource_size(&io), hotplug_bridges); > + mmio_per_hp = div64_ul(resource_size(&mmio), hotplug_bridges); > + mmio_pref_per_hp = div64_ul(resource_size(&mmio_pref), > + hotplug_bridges); > > - b = dev->subordinate; > - if (!b || !dev->is_hotplug_bridge) > + for_each_pci_bridge(dev, bus) { > + if (!dev->subordinate || !dev->is_hotplug_bridge) > continue; > > - /* > - * Distribute available extra resources equally between > - * hotplug-capable downstream ports taking alignment into > - * account. > - * > - * Here hotplug_bridges is always != 0. > - */ > - align = pci_resource_alignment(bridge, io_res); > - io = div64_ul(available_io, hotplug_bridges); > - io = min(ALIGN(io, align), remaining_io); > - remaining_io -= io; > + io.end = io.start + io_per_hp - 1; > + mmio.end = mmio.start + mmio_per_hp - 1; > + mmio_pref.end = mmio_pref.start + mmio_pref_per_hp - 1; > > - align = pci_resource_alignment(bridge, mmio_res); > - mmio = div64_ul(available_mmio, hotplug_bridges); > - mmio = min(ALIGN(mmio, align), remaining_mmio); > - remaining_mmio -= mmio; > + pci_bus_distribute_available_resources(dev->subordinate, > + add_list, io, mmio, mmio_pref); > > - align = pci_resource_alignment(bridge, mmio_pref_res); > - mmio_pref = div64_ul(available_mmio_pref, hotplug_bridges); > - mmio_pref = min(ALIGN(mmio_pref, align), remaining_mmio_pref); > - remaining_mmio_pref -= mmio_pref; > - > - pci_bus_distribute_available_resources(b, add_list, io, mmio, > - mmio_pref); > + io.start = io.end + 1; > + mmio.start = mmio.end + 1; > + mmio_pref.start = mmio_pref.end + 1; > } > } > > @@ -2002,22 +2015,19 @@ static void > pci_bridge_distribute_available_resources(struct pci_dev *bridge, > struct list_head *add_list) > { > - resource_size_t available_io, available_mmio, available_mmio_pref; > - const struct resource *res; > + struct resource io_res, mmio_res, mmio_pref_res; > > if (!bridge->is_hotplug_bridge) > return; > > + io_res = bridge->resource[PCI_BRIDGE_RESOURCES + 0]; > + mmio_res = bridge->resource[PCI_BRIDGE_RESOURCES + 1]; > + mmio_pref_res = bridge->resource[PCI_BRIDGE_RESOURCES + 2]; > + > /* Take the initial extra resources from the hotplug port */ > - res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0]; > - available_io = resource_size(res); > - res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1]; > - available_mmio = resource_size(res); > - res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2]; > - available_mmio_pref = resource_size(res); > > pci_bus_distribute_available_resources(bridge->subordinate, > - add_list, available_io, available_mmio, available_mmio_pref); > + add_list, io_res, mmio_res, mmio_pref_res); > } > > void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) > -- > 2.19.1 >
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index ed960436df5e..09310b6fcdb3 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1859,27 +1859,34 @@ static void extend_bridge_window(struct pci_dev *bridge, struct resource *res, if (res->parent) return; - if (resource_size(res) >= available) - return; + /* + * Hot-adding multiple Thunderbolt devices in SL0 might result in + * multiple devices being enumerated together. This can break the + * resource allocation if the resource sizes are specified with + * add_size instead of simply changing the resource size. + */ + pci_dbg(bridge, "bridge window %pR extended by 0x%016llx\n", res, + available - resource_size(res)); + res->end = res->start + available - 1; + /* + * If a list entry exists, we need to remove any additional size + * requested because that could interfere with the alignment and + * sizing done when distributing resources, causing resources to + * fail to allocate later on. + */ dev_res = res_to_dev_res(add_list, res); if (!dev_res) return; - /* Is there room to extend the window? */ - if (available - resource_size(res) <= dev_res->add_size) - return; - - dev_res->add_size = available - resource_size(res); - pci_dbg(bridge, "bridge window %pR extended by %pa\n", res, - &dev_res->add_size); + dev_res->add_size = 0; } static void pci_bus_distribute_available_resources(struct pci_bus *bus, - struct list_head *add_list, resource_size_t available_io, - resource_size_t available_mmio, resource_size_t available_mmio_pref) + struct list_head *add_list, struct resource io, + struct resource mmio, struct resource mmio_pref) { - resource_size_t remaining_io, remaining_mmio, remaining_mmio_pref; + resource_size_t io_per_hp, mmio_per_hp, mmio_pref_per_hp, align; unsigned int normal_bridges = 0, hotplug_bridges = 0; struct resource *io_res, *mmio_res, *mmio_pref_res; struct pci_dev *dev, *bridge = bus->self; @@ -1889,25 +1896,32 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, mmio_pref_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2]; /* - * Update additional resource list (add_list) to fill all the - * extra resource space available for this port except the space - * calculated in __pci_bus_size_bridges() which covers all the - * devices currently connected to the port and below. + * The alignment of this bridge is yet to be considered, hence it must + * be done now before extending its bridge window. A single bridge + * might not be able to occupy the whole parent region if the alignment + * differs - for example, an external GPU at the end of a Thunderbolt + * daisy chain. */ - extend_bridge_window(bridge, io_res, add_list, available_io); - extend_bridge_window(bridge, mmio_res, add_list, available_mmio); - extend_bridge_window(bridge, mmio_pref_res, add_list, - available_mmio_pref); + align = pci_resource_alignment(bridge, io_res); + if (!io_res->parent && align) + io.start = ALIGN(io.start, align); + + align = pci_resource_alignment(bridge, mmio_res); + if (!mmio_res->parent && align) + mmio.start = ALIGN(mmio.start, align); + + align = pci_resource_alignment(bridge, mmio_pref_res); + if (!mmio_pref_res->parent && align) + mmio_pref.start = ALIGN(mmio_pref.start, align); /* - * Calculate the total amount of extra resource space we can - * pass to bridges below this one. This is basically the - * extra space reduced by the minimal required space for the - * non-hotplug bridges. + * Update the resources to fill as much remaining resource space in the + * parent bridge as possible, while considering alignment. */ - remaining_io = available_io; - remaining_mmio = available_mmio; - remaining_mmio_pref = available_mmio_pref; + extend_bridge_window(bridge, io_res, add_list, resource_size(&io)); + extend_bridge_window(bridge, mmio_res, add_list, resource_size(&mmio)); + extend_bridge_window(bridge, mmio_pref_res, add_list, + resource_size(&mmio_pref)); /* * Calculate how many hotplug bridges and normal bridges there @@ -1921,80 +1935,79 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, normal_bridges++; } + /* + * There is only one bridge on the bus so it gets all possible + * resources which it can then distribute to the possible + * hotplug bridges below. + */ + if (hotplug_bridges + normal_bridges == 1) { + dev = list_first_entry(&bus->devices, struct pci_dev, bus_list); + if (dev->subordinate) + pci_bus_distribute_available_resources(dev->subordinate, + add_list, io, mmio, mmio_pref); + return; + } + + /* + * Reduce the available resource space by what the + * bridge and devices below it occupy. + */ for_each_pci_bridge(dev, bus) { - const struct resource *res; + struct resource *res; + resource_size_t used_size; if (dev->is_hotplug_bridge) continue; - /* - * Reduce the available resource space by what the - * bridge and devices below it occupy. - */ res = &dev->resource[PCI_BRIDGE_RESOURCES + 0]; - if (!res->parent && available_io > resource_size(res)) - remaining_io -= resource_size(res); + align = pci_resource_alignment(dev, res); + align = align ? ALIGN(io.start, align) - io.start : 0; + used_size = align + resource_size(res); + if (!res->parent && used_size <= resource_size(&io)) + io.start += used_size; res = &dev->resource[PCI_BRIDGE_RESOURCES + 1]; - if (!res->parent && available_mmio > resource_size(res)) - remaining_mmio -= resource_size(res); + align = pci_resource_alignment(dev, res); + align = align ? ALIGN(mmio.start, align) - mmio.start : 0; + used_size = align + resource_size(res); + if (!res->parent && used_size <= resource_size(&mmio)) + mmio.start += used_size; res = &dev->resource[PCI_BRIDGE_RESOURCES + 2]; - if (!res->parent && available_mmio_pref > resource_size(res)) - remaining_mmio_pref -= resource_size(res); + align = pci_resource_alignment(dev, res); + align = align ? ALIGN(mmio_pref.start, align) - + mmio_pref.start : 0; + used_size = align + resource_size(res); + if (!res->parent && used_size <= resource_size(&mmio_pref)) + mmio_pref.start += used_size; } - /* - * There is only one bridge on the bus so it gets all available - * resources which it can then distribute to the possible - * hotplug bridges below. - */ - if (hotplug_bridges + normal_bridges == 1) { - dev = list_first_entry(&bus->devices, struct pci_dev, bus_list); - if (dev->subordinate) { - pci_bus_distribute_available_resources(dev->subordinate, - add_list, available_io, available_mmio, - available_mmio_pref); - } + if (!hotplug_bridges) return; - } /* - * Go over devices on this bus and distribute the remaining - * resource space between hotplug bridges. + * Distribute any remaining resources equally between + * the hotplug-capable downstream ports. */ - for_each_pci_bridge(dev, bus) { - resource_size_t align, io, mmio, mmio_pref; - struct pci_bus *b; + io_per_hp = div64_ul(resource_size(&io), hotplug_bridges); + mmio_per_hp = div64_ul(resource_size(&mmio), hotplug_bridges); + mmio_pref_per_hp = div64_ul(resource_size(&mmio_pref), + hotplug_bridges); - b = dev->subordinate; - if (!b || !dev->is_hotplug_bridge) + for_each_pci_bridge(dev, bus) { + if (!dev->subordinate || !dev->is_hotplug_bridge) continue; - /* - * Distribute available extra resources equally between - * hotplug-capable downstream ports taking alignment into - * account. - * - * Here hotplug_bridges is always != 0. - */ - align = pci_resource_alignment(bridge, io_res); - io = div64_ul(available_io, hotplug_bridges); - io = min(ALIGN(io, align), remaining_io); - remaining_io -= io; + io.end = io.start + io_per_hp - 1; + mmio.end = mmio.start + mmio_per_hp - 1; + mmio_pref.end = mmio_pref.start + mmio_pref_per_hp - 1; - align = pci_resource_alignment(bridge, mmio_res); - mmio = div64_ul(available_mmio, hotplug_bridges); - mmio = min(ALIGN(mmio, align), remaining_mmio); - remaining_mmio -= mmio; + pci_bus_distribute_available_resources(dev->subordinate, + add_list, io, mmio, mmio_pref); - align = pci_resource_alignment(bridge, mmio_pref_res); - mmio_pref = div64_ul(available_mmio_pref, hotplug_bridges); - mmio_pref = min(ALIGN(mmio_pref, align), remaining_mmio_pref); - remaining_mmio_pref -= mmio_pref; - - pci_bus_distribute_available_resources(b, add_list, io, mmio, - mmio_pref); + io.start = io.end + 1; + mmio.start = mmio.end + 1; + mmio_pref.start = mmio_pref.end + 1; } } @@ -2002,22 +2015,19 @@ static void pci_bridge_distribute_available_resources(struct pci_dev *bridge, struct list_head *add_list) { - resource_size_t available_io, available_mmio, available_mmio_pref; - const struct resource *res; + struct resource io_res, mmio_res, mmio_pref_res; if (!bridge->is_hotplug_bridge) return; + io_res = bridge->resource[PCI_BRIDGE_RESOURCES + 0]; + mmio_res = bridge->resource[PCI_BRIDGE_RESOURCES + 1]; + mmio_pref_res = bridge->resource[PCI_BRIDGE_RESOURCES + 2]; + /* Take the initial extra resources from the hotplug port */ - res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0]; - available_io = resource_size(res); - res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1]; - available_mmio = resource_size(res); - res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2]; - available_mmio_pref = resource_size(res); pci_bus_distribute_available_resources(bridge->subordinate, - add_list, available_io, available_mmio, available_mmio_pref); + add_list, io_res, mmio_res, mmio_pref_res); } void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
New systems with Thunderbolt are starting to use native PCI enumeration. Mika Westerberg's patch "PCI: Distribute available resources to hotplug capable PCIe downstream ports" (https://patchwork.kernel.org/patch/9972155/) adds code to expand downstream PCI hotplug bridges to consume all remaining resource space in the parent bridge. It is a crucial patch for supporting Thunderbolt native enumeration on Linux. However, it does not consider bridge alignment in all cases, which rules out hot-adding an external graphics processor at the end of a Thunderbolt daisy chain. Hot-adding such a device will likely fail to work with the existing code. It also might disrupt the operation of existing devices or prevent the subsequent hot-plugging of lower aligned devices if the kernel frees and reallocates upstream resources to attempt assign the resources that failed to assign in the first pass. By Intel's ruling, Thunderbolt external graphics processors are generally meant to function only as the first and only device in the chain. However, there is no technical reason that prevents it from being possible if sufficient resources are available, and people are likely to attempt such configurations with Thunderbolt devices if they own such hardware. Hence, I argue that we should improve the user experience and reduce the number of constraints placed on the user by always considering resource alignment, which will make such configurations possible. The other problem with the patch is that it is incompatible with resources allocated by "pci=hpmemsize=nnM" due to a check which does not allow for dev_res->add_size to be reduced. This check also makes a big assumption that the hpmemsize is small but non-zero, and no action has been taken to ensure that. In the current state, any bridge smaller than hpmemsize will likely fail to size correctly, which will cause major issues if the default were to change, or if the user also wants to configure non-Thunderbolt hotplug bridges simultaneously. I argue that if assumptions and limitations can be removed with no risks and adverse effects, then it should be done. The former problem is solved by rewriting the pci_bus_distribute_available_resources() function with more information passed in the arguments, eliminating assumptions about the initial bridge alignment. My patch makes no assumptions about the alignment of hot-added bridges, allowing for any device to be hot-added, given sufficient resources are available. The latter problem is solved by removing the check preventing the shrinking of dev_res->add_size, which allows for the distribution of resources if hpmemsize is non-zero. It can be made to work with zero hpmemsize with two-line patches in pbus_size_mem() and pbus_size_io(), or by modifying extend_bridge_window() to add a new entry to the additional resource list if one does not exist. In theory, and by my testing, the removal of this check does not affect the functionality of the resource distribution with firmware-allocated resources. But it does enable the same functionality when using pci=hpmemsize=nnM, which was not possible prior. This may be one piece of the puzzle needed to support Thunderbolt add-in cards that support native PCI enumeration, without any platform dependencies. I have tested this proposed patch extensively. Using Linux-allocated resources, it works perfectly. I have two Gigabyte GC-TITAN RIDGE Thunderbolt 3 add-in cards in my desktop, and a Dell XPS 9370 with the Dell XPS 9380 Thunderbolt NVM40 firmware flashed. My peripherals are three HP ZBook Thunderbolt 3 docks, two external graphics enclosures with AMD Fiji XT in both, a Promise SANLink3 N1 (AQC107S), and a Dell Thunderbolt 3 NVMe SSD. All configurations of these devices worked well, and I can no longer make it fail if I try to. My testing with firmware-allocated resources is limited due to using computers with Alpine Ridge BIOS support. However, I did get manage to test the patch with firmware-allocated resources by enabling the Alpine Ridge BIOS support and forcing pcie_ports=native, and the results were perfect. Mika Westerberg has agreed to test this on an official platform with native enumeration firmware support to be sure that it works in this situation. It is also appropriate that he reviews these changes as he wrote the original code and any changes made to Thunderbolt-critical code cannot be allowed to break any of the base requirements for Thunderbolt. I would like to thank him for putting up with my emails and trying to help where he can, and for the great work he has done on Thunderbolt in Linux. I have more patches in the pipeline to further improve the Thunderbolt experience on Linux on platforms without BIOS support. This is the most technical but least user-facing one planned. The most exciting changes are yet to come. Edits: I have made code styling changes as suggested by Mika Westerberg. I have been testing Thunderbolt devices with my other host card which happens to be in SL0 mode. This means that devices are discovered much more quickly. I noticed that multiple devices can be enumerated together, rather than each getting enumerated before the next appears. It turns out that this can break the allocation, but I have absolutely no idea why. I have modified the patch to solve this problem. Before, extend_bridge_window() used add_size to change the resource size. Now it simply changes the size of the actual resource, and clears the add_size to zero if a list entry exists. That solves the issue, and proves that the calculated resource sizes are not at fault (the algorithm is sound). Hence, I recommend this version with the modification be applied. I have removed Mika Westerberg's "Tested-By" line to allow him to give his approval for this new change. Observation: the fact that a single Thunderbolt dock can consume 1/4 of the total IO space (16-bit, 0xffff) is evidence that the depreciated IO bars need to be dropped from the kernel at some point. The docks have four bridges each, with 4096-byte alignment. The IO BARs do not do anything, crash the system if not claimed and spam dmesg when not assigned. Signed-off-by: Nicholas-Johnson-opensource <nicholas.johnson-opensource@outlook.com.au> --- drivers/pci/setup-bus.c | 188 +++++++++++++++++++++------------------- 1 file changed, 99 insertions(+), 89 deletions(-)