Pull cxl updates from Dan Williams: "Compute Express Link (CXL) updates for 6.0: - Introduce a 'struct cxl_region' object with support for provisioning and assembling persistent memory regions. - Introduce alloc_free_mem_region() to accompany the existing request_free_mem_region() as a method to allocate physical memory capacity out of an existing resource. - Export insert_resource_expand_to_fit() for the CXL subsystem to late-publish CXL platform windows in iomem_resource. - Add a polled mode PCI DOE (Data Object Exchange) driver service and use it in cxl_pci to retrieve the CDAT (Coherent Device Attribute Table)" * tag 'cxl-for-6.0' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl: (74 commits) cxl/hdm: Fix skip allocations vs multiple pmem allocations cxl/region: Disallow region granularity != window granularity cxl/region: Fix x1 interleave to greater than x1 interleave routing cxl/region: Move HPA setup to cxl_region_attach() cxl/region: Fix decoder interleave programming Documentation: cxl: remove dangling kernel-doc reference cxl/region: describe targets and nr_targets members of cxl_region_params cxl/regions: add padding for cxl_rr_ep_add nested lists cxl/region: Fix IS_ERR() vs NULL check cxl/region: Fix region reference target accounting cxl/region: Fix region commit uninitialized variable warning cxl/region: Fix port setup uninitialized variable warnings cxl/region: Stop initializing interleave granularity cxl/hdm: Fix DPA reservation vs cxl_endpoint_decoder lifetime cxl/acpi: Minimize granularity for x1 interleaves cxl/region: Delete 'region' attribute from root decoders cxl/acpi: Autoload driver for 'cxl_acpi' test devices cxl/region: decrement ->nr_targets on error in cxl_region_attach() cxl/region: prevent underflow in ways_to_cxl() cxl/region: uninitialized variable in alloc_hpa() ...
Showing the changes to kernel (1 of 39 files). Show all
Unified · Split
| @@ -489,8 +489,9 @@ int __weak page_is_ram(unsigned long pfn) · 488 unmodified lines | |||
| 489 | } | 489 | } |
| 490 | EXPORT_SYMBOL_GPL(page_is_ram); | 490 | EXPORT_SYMBOL_GPL(page_is_ram); |
| 491 | 491 | ||
| 492 | static int __region_intersects(resource_size_t start, size_t size, | 492 | static int __region_intersects(struct resource *parent, resource_size_t start, |
| 493 | unsigned long flags, unsigned long desc) | 493 | size_t size, unsigned long flags, |
| 494 | unsigned long desc) | ||
| 494 | { | 495 | { |
| 495 | struct resource res; | 496 | struct resource res; |
| 496 | int type = 0; int other = 0; | 497 | int type = 0; int other = 0; |
| @@ -499,7 +500,7 @@ static int __region_intersects(resource_size_t start, size_t size, · 2 unmodified lines | |||
| 499 | res.start = start; | 500 | res.start = start; |
| 500 | res.end = start + size - 1; | 501 | res.end = start + size - 1; |
| 501 | 502 | ||
| 502 | for (p = iomem_resource.child; p ; p = p->sibling) { | 503 | for (p = parent->child; p ; p = p->sibling) { |
| 503 | bool is_type = (((p->flags & flags) == flags) && | 504 | bool is_type = (((p->flags & flags) == flags) && |
| 504 | ((desc == IORES_DESC_NONE) || | 505 | ((desc == IORES_DESC_NONE) || |
| 505 | (desc == p->desc))); | 506 | (desc == p->desc))); |
| @@ -543,7 +544,7 @@ int region_intersects(resource_size_t start, size_t size, unsigned long flags, · 37 unmodified lines | |||
| 543 | int ret; | 544 | int ret; |
| 544 | 545 | ||
| 545 | read_lock(&resource_lock); | 546 | read_lock(&resource_lock); |
| 546 | ret = __region_intersects(start, size, flags, desc); | 547 | ret = __region_intersects(&iomem_resource, start, size, flags, desc); |
| 547 | read_unlock(&resource_lock); | 548 | read_unlock(&resource_lock); |
| 548 | 549 | ||
| 549 | return ret; | 550 | return ret; |
| @@ -891,6 +892,13 @@ void insert_resource_expand_to_fit(struct resource *root, struct resource *new) · 341 unmodified lines | |||
| 891 | } | 892 | } |
| 892 | write_unlock(&resource_lock); | 893 | write_unlock(&resource_lock); |
| 893 | } | 894 | } |
| 895 | /* | ||
| 896 | * Not for general consumption, only early boot memory map parsing, PCI | ||
| 897 | * resource discovery, and late discovery of CXL resources are expected | ||
| 898 | * to use this interface. The former are built-in and only the latter, | ||
| 899 | * CXL, is a module. | ||
| 900 | */ | ||
| 901 | EXPORT_SYMBOL_NS_GPL(insert_resource_expand_to_fit, CXL); | ||
| 894 | 902 | ||
| 895 | /** | 903 | /** |
| 896 | * remove_resource - Remove a resource in the resource tree | 904 | * remove_resource - Remove a resource in the resource tree |
| @@ -1773,62 +1781,139 @@ void resource_list_free(struct list_head *head) · 876 unmodified lines | |||
| 1773 | } | 1781 | } |
| 1774 | EXPORT_SYMBOL(resource_list_free); | 1782 | EXPORT_SYMBOL(resource_list_free); |
| 1775 | 1783 | ||
| 1776 | #ifdef CONFIG_DEVICE_PRIVATE | 1784 | #ifdef CONFIG_GET_FREE_REGION |
| 1777 | static struct resource *__request_free_mem_region(struct device *dev, | 1785 | #define GFR_DESCENDING (1UL << 0) |
| 1778 | struct resource *base, unsigned long size, const char *name) | 1786 | #define GFR_REQUEST_REGION (1UL << 1) |
| 1787 | #define GFR_DEFAULT_ALIGN (1UL << PA_SECTION_SHIFT) | ||
| 1788 | |||
| 1789 | static resource_size_t gfr_start(struct resource *base, resource_size_t size, | ||
| 1790 | resource_size_t align, unsigned long flags) | ||
| 1791 | { | ||
| 1792 | if (flags & GFR_DESCENDING) { | ||
| 1793 | resource_size_t end; | ||
| 1794 | |||
| 1795 | end = min_t(resource_size_t, base->end, | ||
| 1796 | (1ULL << MAX_PHYSMEM_BITS) - 1); | ||
| 1797 | return end - size + 1; | ||
| 1798 | } | ||
| 1799 | |||
| 1800 | return ALIGN(base->start, align); | ||
| 1801 | } | ||
| 1802 | |||
| 1803 | static bool gfr_continue(struct resource *base, resource_size_t addr, | ||
| 1804 | resource_size_t size, unsigned long flags) | ||
| 1805 | { | ||
| 1806 | if (flags & GFR_DESCENDING) | ||
| 1807 | return addr > size && addr >= base->start; | ||
| 1808 | /* | ||
| 1809 | * In the ascend case be careful that the last increment by | ||
| 1810 | * @size did not wrap 0. | ||
| 1811 | */ | ||
| 1812 | return addr > addr - size && | ||
| 1813 | addr <= min_t(resource_size_t, base->end, | ||
| 1814 | (1ULL << MAX_PHYSMEM_BITS) - 1); | ||
| 1815 | } | ||
| 1816 | |||
| 1817 | static resource_size_t gfr_next(resource_size_t addr, resource_size_t size, | ||
| 1818 | unsigned long flags) | ||
| 1779 | { | 1819 | { |
| 1780 | resource_size_t end, addr; | 1820 | if (flags & GFR_DESCENDING) |
| 1821 | return addr - size; | ||
| 1822 | return addr + size; | ||
| 1823 | } | ||
| 1824 | |||
| 1825 | static void remove_free_mem_region(void *_res) | ||
| 1826 | { | ||
| 1827 | struct resource *res = _res; | ||
| 1828 | |||
| 1829 | if (res->parent) | ||
| 1830 | remove_resource(res); | ||
| 1831 | free_resource(res); | ||
| 1832 | } | ||
| 1833 | |||
| 1834 | static struct resource * | ||
| 1835 | get_free_mem_region(struct device *dev, struct resource *base, | ||
| 1836 | resource_size_t size, const unsigned long align, | ||
| 1837 | const char *name, const unsigned long desc, | ||
| 1838 | const unsigned long flags) | ||
| 1839 | { | ||
| 1840 | resource_size_t addr; | ||
| 1781 | struct resource *res; | 1841 | struct resource *res; |
| 1782 | struct region_devres *dr = NULL; | 1842 | struct region_devres *dr = NULL; |
| 1783 | 1843 | ||
| 1784 | size = ALIGN(size, 1UL << PA_SECTION_SHIFT); | 1844 | size = ALIGN(size, align); |
| 1785 | end = min_t(unsigned long, base->end, (1UL << MAX_PHYSMEM_BITS) - 1); | ||
| 1786 | addr = end - size + 1UL; | ||
| 1787 | 1845 | ||
| 1788 | res = alloc_resource(GFP_KERNEL); | 1846 | res = alloc_resource(GFP_KERNEL); |
| 1789 | if (!res) | 1847 | if (!res) |
| 1790 | return ERR_PTR(-ENOMEM); | 1848 | return ERR_PTR(-ENOMEM); |
| 1791 | 1849 | ||
| 1792 | if (dev) { | 1850 | if (dev && (flags & GFR_REQUEST_REGION)) { |
| 1793 | dr = devres_alloc(devm_region_release, | 1851 | dr = devres_alloc(devm_region_release, |
| 1794 | sizeof(struct region_devres), GFP_KERNEL); | 1852 | sizeof(struct region_devres), GFP_KERNEL); |
| 1795 | if (!dr) { | 1853 | if (!dr) { |
| 1796 | free_resource(res); | 1854 | free_resource(res); |
| 1797 | return ERR_PTR(-ENOMEM); | 1855 | return ERR_PTR(-ENOMEM); |
| 1798 | } | 1856 | } |
| 1857 | } else if (dev) { | ||
| 1858 | if (devm_add_action_or_reset(dev, remove_free_mem_region, res)) | ||
| 1859 | return ERR_PTR(-ENOMEM); | ||
| 1799 | } | 1860 | } |
| 1800 | 1861 | ||
| 1801 | write_lock(&resource_lock); | 1862 | write_lock(&resource_lock); |
| 1802 | for (; addr > size && addr >= base->start; addr -= size) { | 1863 | for (addr = gfr_start(base, size, align, flags); |
| 1803 | if (__region_intersects(addr, size, 0, IORES_DESC_NONE) != | 1864 | gfr_continue(base, addr, size, flags); |
| 1804 | REGION_DISJOINT) | 1865 | addr = gfr_next(addr, size, flags)) { |
| 1866 | if (__region_intersects(base, addr, size, 0, IORES_DESC_NONE) != | ||
| 1867 | REGION_DISJOINT) | ||
| 1805 | continue; | 1868 | continue; |
| 1806 | 1869 | ||
| 1807 | if (__request_region_locked(res, &iomem_resource, addr, size, | 1870 | if (flags & GFR_REQUEST_REGION) { |
| 1808 | name, 0)) | 1871 | if (__request_region_locked(res, &iomem_resource, addr, |
| 1809 | break; | 1872 | size, name, 0)) |
| 1873 | break; | ||
| 1810 | 1874 | ||
| 1811 | if (dev) { | 1875 | if (dev) { |
| 1812 | dr->parent = &iomem_resource; | 1876 | dr->parent = &iomem_resource; |
| 1813 | dr->start = addr; | 1877 | dr->start = addr; |
| 1814 | dr->n = size; | 1878 | dr->n = size; |
| 1815 | devres_add(dev, dr); | 1879 | devres_add(dev, dr); |
| 1816 | } | 1880 | } |
| 1817 | 1881 | ||
| 1818 | res->desc = IORES_DESC_DEVICE_PRIVATE_MEMORY; | 1882 | res->desc = desc; |
| 1819 | write_unlock(&resource_lock); | 1883 | write_unlock(&resource_lock); |
| 1884 | |||
| 1885 | |||
| 1886 | /* | ||
| 1887 | * A driver is claiming this region so revoke any | ||
| 1888 | * mappings. | ||
| 1889 | */ | ||
| 1890 | revoke_iomem(res); | ||
| 1891 | } else { | ||
| 1892 | res->start = addr; | ||
| 1893 | res->end = addr + size - 1; | ||
| 1894 | res->name = name; | ||
| 1895 | res->desc = desc; | ||
| 1896 | res->flags = IORESOURCE_MEM; | ||
| 1897 | |||
| 1898 | /* | ||
| 1899 | * Only succeed if the resource hosts an exclusive | ||
| 1900 | * range after the insert | ||
| 1901 | */ | ||
| 1902 | if (__insert_resource(base, res) || res->child) | ||
| 1903 | break; | ||
| 1904 | |||
| 1905 | write_unlock(&resource_lock); | ||
| 1906 | } | ||
| 1820 | 1907 | ||
| 1821 | /* | ||
| 1822 | * A driver is claiming this region so revoke any mappings. | ||
| 1823 | */ | ||
| 1824 | revoke_iomem(res); | ||
| 1825 | return res; | 1908 | return res; |
| 1826 | } | 1909 | } |
| 1827 | write_unlock(&resource_lock); | 1910 | write_unlock(&resource_lock); |
| 1828 | 1911 | ||
| 1829 | free_resource(res); | 1912 | if (flags & GFR_REQUEST_REGION) { |
| 1830 | if (dr) | 1913 | free_resource(res); |
| 1831 | devres_free(dr); | 1914 | devres_free(dr); |
| 1915 | } else if (dev) | ||
| 1916 | devm_release_action(dev, remove_free_mem_region, res); | ||
| 1832 | 1917 | ||
| 1833 | return ERR_PTR(-ERANGE); | 1918 | return ERR_PTR(-ERANGE); |
| 1834 | } | 1919 | } |
| @@ -1847,18 +1932,48 @@ static struct resource *__request_free_mem_region(struct device *dev, · 12 unmodified lines | |||
| 1847 | struct resource *devm_request_free_mem_region(struct device *dev, | 1932 | struct resource *devm_request_free_mem_region(struct device *dev, |
| 1848 | struct resource *base, unsigned long size) | 1933 | struct resource *base, unsigned long size) |
| 1849 | { | 1934 | { |
| 1850 | return __request_free_mem_region(dev, base, size, dev_name(dev)); | 1935 | unsigned long flags = GFR_DESCENDING | GFR_REQUEST_REGION; |
| 1936 | |||
| 1937 | return get_free_mem_region(dev, base, size, GFR_DEFAULT_ALIGN, | ||
| 1938 | dev_name(dev), | ||
| 1939 | IORES_DESC_DEVICE_PRIVATE_MEMORY, flags); | ||
| 1851 | } | 1940 | } |
| 1852 | EXPORT_SYMBOL_GPL(devm_request_free_mem_region); | 1941 | EXPORT_SYMBOL_GPL(devm_request_free_mem_region); |
| 1853 | 1942 | ||
| 1854 | struct resource *request_free_mem_region(struct resource *base, | 1943 | struct resource *request_free_mem_region(struct resource *base, |
| 1855 | unsigned long size, const char *name) | 1944 | unsigned long size, const char *name) |
| 1856 | { | 1945 | { |
| 1857 | return __request_free_mem_region(NULL, base, size, name); | 1946 | unsigned long flags = GFR_DESCENDING | GFR_REQUEST_REGION; |
| 1947 | |||
| 1948 | return get_free_mem_region(NULL, base, size, GFR_DEFAULT_ALIGN, name, | ||
| 1949 | IORES_DESC_DEVICE_PRIVATE_MEMORY, flags); | ||
| 1858 | } | 1950 | } |
| 1859 | EXPORT_SYMBOL_GPL(request_free_mem_region); | 1951 | EXPORT_SYMBOL_GPL(request_free_mem_region); |
| 1860 | 1952 | ||
| 1861 | #endif /* CONFIG_DEVICE_PRIVATE */ | 1953 | /** |
| 1954 | * alloc_free_mem_region - find a free region relative to @base | ||
| 1955 | * @base: resource that will parent the new resource | ||
| 1956 | * @size: size in bytes of memory to allocate from @base | ||
| 1957 | * @align: alignment requirements for the allocation | ||
| 1958 | * @name: resource name | ||
| 1959 | * | ||
| 1960 | * Buses like CXL, that can dynamically instantiate new memory regions, | ||
| 1961 | * need a method to allocate physical address space for those regions. | ||
| 1962 | * Allocate and insert a new resource to cover a free, unclaimed by a | ||
| 1963 | * descendant of @base, range in the span of @base. | ||
| 1964 | */ | ||
| 1965 | struct resource *alloc_free_mem_region(struct resource *base, | ||
| 1966 | unsigned long size, unsigned long align, | ||
| 1967 | const char *name) | ||
| 1968 | { | ||
| 1969 | /* Default of ascending direction and insert resource */ | ||
| 1970 | unsigned long flags = 0; | ||
| 1971 | |||
| 1972 | return get_free_mem_region(NULL, base, size, align, name, | ||
| 1973 | IORES_DESC_NONE, flags); | ||
| 1974 | } | ||
| 1975 | EXPORT_SYMBOL_NS_GPL(alloc_free_mem_region, CXL); | ||
| 1976 | #endif /* CONFIG_GET_FREE_REGION */ | ||
| 1862 | 1977 | ||
| 1863 | static int __init strict_iomem(char *str) | 1978 | static int __init strict_iomem(char *str) |
| 1864 | { | 1979 | { |