summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2018-05-21 10:21:29 +0200
committerChris Wilson <chris@chris-wilson.co.uk>2018-05-24 16:04:30 +0200
commit83bc4ec37210b17bd611a58968b2ce0e9cc7f251 (patch)
tree16ec4cb4c765fd6a76ef10c9b63522d2b03780bc
parentdrm/mm: Reject over-sized allocation requests early (diff)
downloadlinux-83bc4ec37210b17bd611a58968b2ce0e9cc7f251.tar.xz
linux-83bc4ec37210b17bd611a58968b2ce0e9cc7f251.zip
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
-rw-r--r--drivers/gpu/drm/drm_mm.c9
-rw-r--r--drivers/gpu/drm/selftests/drm_mm_selftests.h2
-rw-r--r--drivers/gpu/drm/selftests/test-drm_mm.c71
-rw-r--r--include/drm/drm_mm.h32
4 files changed, 112 insertions, 2 deletions
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
index 7b4ad05fe1c0..3cc5fbd78ee2 100644
--- a/drivers/gpu/drm/drm_mm.c
+++ b/drivers/gpu/drm/drm_mm.c
@@ -480,6 +480,7 @@ int drm_mm_insert_node_in_range(struct drm_mm * const mm,
{
struct drm_mm_node *hole;
u64 remainder_mask;
+ bool once;
DRM_MM_BUG_ON(range_start >= range_end);
@@ -492,9 +493,13 @@ int drm_mm_insert_node_in_range(struct drm_mm * const mm,
if (alignment <= 1)
alignment = 0;
+ once = mode & DRM_MM_INSERT_ONCE;
+ mode &= ~DRM_MM_INSERT_ONCE;
+
remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0;
- for (hole = first_hole(mm, range_start, range_end, size, mode); hole;
- hole = next_hole(mm, hole, mode)) {
+ for (hole = first_hole(mm, range_start, range_end, size, mode);
+ hole;
+ hole = once ? NULL : next_hole(mm, hole, mode)) {
u64 hole_start = __drm_mm_hole_node_start(hole);
u64 hole_end = hole_start + hole->hole_size;
u64 adj_start, adj_end;
diff --git a/drivers/gpu/drm/selftests/drm_mm_selftests.h b/drivers/gpu/drm/selftests/drm_mm_selftests.h
index 54acc117550c..6b943ea1c57d 100644
--- a/drivers/gpu/drm/selftests/drm_mm_selftests.h
+++ b/drivers/gpu/drm/selftests/drm_mm_selftests.h
@@ -19,7 +19,9 @@ selftest(align64, igt_align64)
selftest(evict, igt_evict)
selftest(evict_range, igt_evict_range)
selftest(bottomup, igt_bottomup)
+selftest(lowest, igt_lowest)
selftest(topdown, igt_topdown)
+selftest(highest, igt_highest)
selftest(color, igt_color)
selftest(color_evict, igt_color_evict)
selftest(color_evict_range, igt_color_evict_range)
diff --git a/drivers/gpu/drm/selftests/test-drm_mm.c b/drivers/gpu/drm/selftests/test-drm_mm.c
index 7cc935d7b7aa..f69d8da222b0 100644
--- a/drivers/gpu/drm/selftests/test-drm_mm.c
+++ b/drivers/gpu/drm/selftests/test-drm_mm.c
@@ -1825,6 +1825,77 @@ err:
return ret;
}
+static int __igt_once(unsigned int mode)
+{
+ struct drm_mm mm;
+ struct drm_mm_node rsvd_lo, rsvd_hi, node;
+ int err;
+
+ drm_mm_init(&mm, 0, 7);
+
+ memset(&rsvd_lo, 0, sizeof(rsvd_lo));
+ rsvd_lo.start = 1;
+ rsvd_lo.size = 1;
+ err = drm_mm_reserve_node(&mm, &rsvd_lo);
+ if (err) {
+ pr_err("Could not reserve low node\n");
+ goto err;
+ }
+
+ memset(&rsvd_hi, 0, sizeof(rsvd_hi));
+ rsvd_hi.start = 5;
+ rsvd_hi.size = 1;
+ err = drm_mm_reserve_node(&mm, &rsvd_hi);
+ if (err) {
+ pr_err("Could not reserve low node\n");
+ goto err_lo;
+ }
+
+ if (!drm_mm_hole_follows(&rsvd_lo) || !drm_mm_hole_follows(&rsvd_hi)) {
+ pr_err("Expected a hole after lo and high nodes!\n");
+ err = -EINVAL;
+ goto err_hi;
+ }
+
+ memset(&node, 0, sizeof(node));
+ err = drm_mm_insert_node_generic(&mm, &node,
+ 2, 0, 0,
+ mode | DRM_MM_INSERT_ONCE);
+ if (!err) {
+ pr_err("Unexpectedly inserted the node into the wrong hole: node.start=%llx\n",
+ node.start);
+ err = -EINVAL;
+ goto err_node;
+ }
+
+ err = drm_mm_insert_node_generic(&mm, &node, 2, 0, 0, mode);
+ if (err) {
+ pr_err("Could not insert the node into the available hole!\n");
+ err = -EINVAL;
+ goto err_hi;
+ }
+
+err_node:
+ drm_mm_remove_node(&node);
+err_hi:
+ drm_mm_remove_node(&rsvd_hi);
+err_lo:
+ drm_mm_remove_node(&rsvd_lo);
+err:
+ drm_mm_takedown(&mm);
+ return err;
+}
+
+static int igt_lowest(void *ignored)
+{
+ return __igt_once(DRM_MM_INSERT_LOW);
+}
+
+static int igt_highest(void *ignored)
+{
+ return __igt_once(DRM_MM_INSERT_HIGH);
+}
+
static void separate_adjacent_colors(const struct drm_mm_node *node,
unsigned long color,
u64 *start,
diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h
index e3aa3bfd4860..2c3bbb43c7d1 100644
--- a/include/drm/drm_mm.h
+++ b/include/drm/drm_mm.h
@@ -109,6 +109,38 @@ enum drm_mm_insert_mode {
* Allocates the node from the bottom of the found hole.
*/
DRM_MM_INSERT_EVICT,
+
+ /**
+ * @DRM_MM_INSERT_ONCE:
+ *
+ * Only check the first hole for suitablity and report -ENOSPC
+ * immediately otherwise, rather than check every hole until a
+ * suitable one is found. Can only be used in conjunction with another
+ * search method such as DRM_MM_INSERT_HIGH or DRM_MM_INSERT_LOW.
+ */
+ DRM_MM_INSERT_ONCE = BIT(31),
+
+ /**
+ * @DRM_MM_INSERT_HIGHEST:
+ *
+ * Only check the highest hole (the hole with the largest address) and
+ * insert the node at the top of the hole or report -ENOSPC if
+ * unsuitable.
+ *
+ * Does not search all holes.
+ */
+ DRM_MM_INSERT_HIGHEST = DRM_MM_INSERT_HIGH | DRM_MM_INSERT_ONCE,
+
+ /**
+ * @DRM_MM_INSERT_LOWEST:
+ *
+ * Only check the lowest hole (the hole with the smallest address) and
+ * insert the node at the bottom of the hole or report -ENOSPC if
+ * unsuitable.
+ *
+ * Does not search all holes.
+ */
+ DRM_MM_INSERT_LOWEST = DRM_MM_INSERT_LOW | DRM_MM_INSERT_ONCE,
};
/**