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

drm/mm: Add a search-by-address variant to only inspect a single hole

Searching for an available hole by address is slow, as there no
guarantee that a hole will be available and so we must walk over all
nodes in the rbtree before we determine the search was futile. In many
cases, the caller doesn't strictly care for the highest available hole
and was just opportunistically laying out the address space in a
preferred order. In such cases, the caller can accept any address and
would rather do so then do a slow walk.

To be able to mix search strategies, the caller wants to tell the drm_mm
how long to spend on the search. Without a good guide for what should be
the best split, start with a request to try once at most. That is return
the top-most (or lowest) hole if it fulfils the alignment and size
requirements.

v2: Documentation, by why of example (selftests) and kerneldoc.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Reviewed-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180521082131.13744-2-chris@chris-wilson.co.uk

+112 -2
+7 -2
drivers/gpu/drm/drm_mm.c
··· 480 480 { 481 481 struct drm_mm_node *hole; 482 482 u64 remainder_mask; 483 + bool once; 483 484 484 485 DRM_MM_BUG_ON(range_start >= range_end); 485 486 ··· 493 492 if (alignment <= 1) 494 493 alignment = 0; 495 494 495 + once = mode & DRM_MM_INSERT_ONCE; 496 + mode &= ~DRM_MM_INSERT_ONCE; 497 + 496 498 remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0; 497 - for (hole = first_hole(mm, range_start, range_end, size, mode); hole; 498 - hole = next_hole(mm, hole, mode)) { 499 + for (hole = first_hole(mm, range_start, range_end, size, mode); 500 + hole; 501 + hole = once ? NULL : next_hole(mm, hole, mode)) { 499 502 u64 hole_start = __drm_mm_hole_node_start(hole); 500 503 u64 hole_end = hole_start + hole->hole_size; 501 504 u64 adj_start, adj_end;
+2
drivers/gpu/drm/selftests/drm_mm_selftests.h
··· 19 19 selftest(evict, igt_evict) 20 20 selftest(evict_range, igt_evict_range) 21 21 selftest(bottomup, igt_bottomup) 22 + selftest(lowest, igt_lowest) 22 23 selftest(topdown, igt_topdown) 24 + selftest(highest, igt_highest) 23 25 selftest(color, igt_color) 24 26 selftest(color_evict, igt_color_evict) 25 27 selftest(color_evict_range, igt_color_evict_range)
+71
drivers/gpu/drm/selftests/test-drm_mm.c
··· 1825 1825 return ret; 1826 1826 } 1827 1827 1828 + static int __igt_once(unsigned int mode) 1829 + { 1830 + struct drm_mm mm; 1831 + struct drm_mm_node rsvd_lo, rsvd_hi, node; 1832 + int err; 1833 + 1834 + drm_mm_init(&mm, 0, 7); 1835 + 1836 + memset(&rsvd_lo, 0, sizeof(rsvd_lo)); 1837 + rsvd_lo.start = 1; 1838 + rsvd_lo.size = 1; 1839 + err = drm_mm_reserve_node(&mm, &rsvd_lo); 1840 + if (err) { 1841 + pr_err("Could not reserve low node\n"); 1842 + goto err; 1843 + } 1844 + 1845 + memset(&rsvd_hi, 0, sizeof(rsvd_hi)); 1846 + rsvd_hi.start = 5; 1847 + rsvd_hi.size = 1; 1848 + err = drm_mm_reserve_node(&mm, &rsvd_hi); 1849 + if (err) { 1850 + pr_err("Could not reserve low node\n"); 1851 + goto err_lo; 1852 + } 1853 + 1854 + if (!drm_mm_hole_follows(&rsvd_lo) || !drm_mm_hole_follows(&rsvd_hi)) { 1855 + pr_err("Expected a hole after lo and high nodes!\n"); 1856 + err = -EINVAL; 1857 + goto err_hi; 1858 + } 1859 + 1860 + memset(&node, 0, sizeof(node)); 1861 + err = drm_mm_insert_node_generic(&mm, &node, 1862 + 2, 0, 0, 1863 + mode | DRM_MM_INSERT_ONCE); 1864 + if (!err) { 1865 + pr_err("Unexpectedly inserted the node into the wrong hole: node.start=%llx\n", 1866 + node.start); 1867 + err = -EINVAL; 1868 + goto err_node; 1869 + } 1870 + 1871 + err = drm_mm_insert_node_generic(&mm, &node, 2, 0, 0, mode); 1872 + if (err) { 1873 + pr_err("Could not insert the node into the available hole!\n"); 1874 + err = -EINVAL; 1875 + goto err_hi; 1876 + } 1877 + 1878 + err_node: 1879 + drm_mm_remove_node(&node); 1880 + err_hi: 1881 + drm_mm_remove_node(&rsvd_hi); 1882 + err_lo: 1883 + drm_mm_remove_node(&rsvd_lo); 1884 + err: 1885 + drm_mm_takedown(&mm); 1886 + return err; 1887 + } 1888 + 1889 + static int igt_lowest(void *ignored) 1890 + { 1891 + return __igt_once(DRM_MM_INSERT_LOW); 1892 + } 1893 + 1894 + static int igt_highest(void *ignored) 1895 + { 1896 + return __igt_once(DRM_MM_INSERT_HIGH); 1897 + } 1898 + 1828 1899 static void separate_adjacent_colors(const struct drm_mm_node *node, 1829 1900 unsigned long color, 1830 1901 u64 *start,
+32
include/drm/drm_mm.h
··· 109 109 * Allocates the node from the bottom of the found hole. 110 110 */ 111 111 DRM_MM_INSERT_EVICT, 112 + 113 + /** 114 + * @DRM_MM_INSERT_ONCE: 115 + * 116 + * Only check the first hole for suitablity and report -ENOSPC 117 + * immediately otherwise, rather than check every hole until a 118 + * suitable one is found. Can only be used in conjunction with another 119 + * search method such as DRM_MM_INSERT_HIGH or DRM_MM_INSERT_LOW. 120 + */ 121 + DRM_MM_INSERT_ONCE = BIT(31), 122 + 123 + /** 124 + * @DRM_MM_INSERT_HIGHEST: 125 + * 126 + * Only check the highest hole (the hole with the largest address) and 127 + * insert the node at the top of the hole or report -ENOSPC if 128 + * unsuitable. 129 + * 130 + * Does not search all holes. 131 + */ 132 + DRM_MM_INSERT_HIGHEST = DRM_MM_INSERT_HIGH | DRM_MM_INSERT_ONCE, 133 + 134 + /** 135 + * @DRM_MM_INSERT_LOWEST: 136 + * 137 + * Only check the lowest hole (the hole with the smallest address) and 138 + * insert the node at the bottom of the hole or report -ENOSPC if 139 + * unsuitable. 140 + * 141 + * Does not search all holes. 142 + */ 143 + DRM_MM_INSERT_LOWEST = DRM_MM_INSERT_LOW | DRM_MM_INSERT_ONCE, 112 144 }; 113 145 114 146 /**