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 | 490 | EXPORT_SYMBOL_GPL(page_is_ram); |
| 491 | 491 | |
| 492 | −static int __region_intersects(resource_size_t start, size_t size, | |
| 493 | − unsigned long flags, unsigned long desc) | |
| 492 | +static int __region_intersects(struct resource *parent, resource_size_t start, | |
| 493 | + size_t size, unsigned long flags, | |
| 494 | + unsigned long desc) | |
| 494 | 495 | { |
| 495 | 496 | struct resource res; |
| 496 | 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 | 500 | res.start = start; |
| 500 | 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 | 504 | bool is_type = (((p->flags & flags) == flags) && |
| 504 | 505 | ((desc == IORES_DESC_NONE) || |
| 505 | 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 | 544 | int ret; |
| 544 | 545 | |
| 545 | 546 | read_lock(&resource_lock); |
| 546 | − ret = __region_intersects(start, size, flags, desc); | |
| 547 | + ret = __region_intersects(&iomem_resource, start, size, flags, desc); | |
| 547 | 548 | read_unlock(&resource_lock); |
| 548 | 549 | |
| 549 | 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 | 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 | 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 | 1782 | EXPORT_SYMBOL(resource_list_free); |
| 1775 | 1783 | |
| 1776 | −#ifdef CONFIG_DEVICE_PRIVATE | |
| 1777 | −static struct resource *__request_free_mem_region(struct device *dev, | |
| 1778 | − struct resource *base, unsigned long size, const char *name) | |
| 1784 | +#ifdef CONFIG_GET_FREE_REGION | |
| 1785 | +#define GFR_DESCENDING (1UL << 0) | |
| 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 | 1841 | struct resource *res; |
| 1782 | 1842 | struct region_devres *dr = NULL; |
| 1783 | 1843 | |
| 1784 | − size = ALIGN(size, 1UL << PA_SECTION_SHIFT); | |
| 1785 | − end = min_t(unsigned long, base->end, (1UL << MAX_PHYSMEM_BITS) - 1); | |
| 1786 | − addr = end - size + 1UL; | |
| 1844 | + size = ALIGN(size, align); | |
| 1787 | 1845 | |
| 1788 | 1846 | res = alloc_resource(GFP_KERNEL); |
| 1789 | 1847 | if (!res) |
| 1790 | 1848 | return ERR_PTR(-ENOMEM); |
| 1791 | 1849 | |
| 1792 | − if (dev) { | |
| 1850 | + if (dev && (flags & GFR_REQUEST_REGION)) { | |
| 1793 | 1851 | dr = devres_alloc(devm_region_release, |
| 1794 | 1852 | sizeof(struct region_devres), GFP_KERNEL); |
| 1795 | 1853 | if (!dr) { |
| 1796 | 1854 | free_resource(res); |
| 1797 | 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 | 1862 | write_lock(&resource_lock); |
| 1802 | − for (; addr > size && addr >= base->start; addr -= size) { | |
| 1803 | − if (__region_intersects(addr, size, 0, IORES_DESC_NONE) != | |
| 1804 | − REGION_DISJOINT) | |
| 1863 | + for (addr = gfr_start(base, size, align, flags); | |
| 1864 | + gfr_continue(base, addr, size, flags); | |
| 1865 | + addr = gfr_next(addr, size, flags)) { | |
| 1866 | + if (__region_intersects(base, addr, size, 0, IORES_DESC_NONE) != | |
| 1867 | + REGION_DISJOINT) | |
| 1805 | 1868 | continue; |
| 1806 | 1869 | |
| 1807 | − if (__request_region_locked(res, &iomem_resource, addr, size, | |
| 1808 | − name, 0)) | |
| 1809 | − break; | |
| 1870 | + if (flags & GFR_REQUEST_REGION) { | |
| 1871 | + if (__request_region_locked(res, &iomem_resource, addr, | |
| 1872 | + size, name, 0)) | |
| 1873 | + break; | |
| 1810 | 1874 | |
| 1811 | − if (dev) { | |
| 1812 | − dr->parent = &iomem_resource; | |
| 1813 | − dr->start = addr; | |
| 1814 | − dr->n = size; | |
| 1815 | − devres_add(dev, dr); | |
| 1816 | − } | |
| 1875 | + if (dev) { | |
| 1876 | + dr->parent = &iomem_resource; | |
| 1877 | + dr->start = addr; | |
| 1878 | + dr->n = size; | |
| 1879 | + devres_add(dev, dr); | |
| 1880 | + } | |
| 1817 | 1881 | |
| 1818 | − res->desc = IORES_DESC_DEVICE_PRIVATE_MEMORY; | |
| 1819 | − write_unlock(&resource_lock); | |
| 1882 | + res->desc = desc; | |
| 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 | 1908 | return res; |
| 1826 | 1909 | } |
| 1827 | 1910 | write_unlock(&resource_lock); |
| 1828 | 1911 | |
| 1829 | − free_resource(res); | |
| 1830 | − if (dr) | |
| 1912 | + if (flags & GFR_REQUEST_REGION) { | |
| 1913 | + free_resource(res); | |
| 1831 | 1914 | devres_free(dr); |
| 1915 | + } else if (dev) | |
| 1916 | + devm_release_action(dev, remove_free_mem_region, res); | |
| 1832 | 1917 | |
| 1833 | 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 | 1932 | struct resource *devm_request_free_mem_region(struct device *dev, |
| 1848 | 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 | 1941 | EXPORT_SYMBOL_GPL(devm_request_free_mem_region); |
| 1853 | 1942 | |
| 1854 | 1943 | struct resource *request_free_mem_region(struct resource *base, |
| 1855 | 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 | 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 | 1978 | static int __init strict_iomem(char *str) |
| 1864 | 1979 | { |