drm/mm: Break long searches in fragmented address spaces
diff mbox series

Message ID 20200331132724.5700-1-chris@chris-wilson.co.uk
State New
Headers show
Series
  • drm/mm: Break long searches in fragmented address spaces
Related show

Commit Message

Chris Wilson March 31, 2020, 1:27 p.m. UTC
We try hard to select a suitable hole in the drm_mm first time. But if
that is unsuccessful, we then have to look at neighbouring nodes, and
this requires traversing the rbtree. Walking the rbtree can be slow
(much slower than a linear list for deep trees), and if the drm_mm has
been purposefully fragmented our search can be trapped for a long, long
time. For non-preemptible kernels, we need to break up long CPU bound
sections by manually checking for cond_resched(); similarly we should
also bail out if we have been told to terminate. (In an ideal world, we
would break for any signal, but we need to trade off having to perform
the search again after ERESTARTSYS, which again may form a trap of
making no forward progress.)

Reported-by: Zbigniew Kempczyński <zbigniew.kempczynski@intel.com>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Zbigniew Kempczyński <zbigniew.kempczynski@intel.com>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
---
 drivers/gpu/drm/drm_mm.c                      | 23 ++++++++++++-------
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    |  3 ++-
 drivers/gpu/drm/i915/gem/i915_gem_stolen.c    |  4 +++-
 drivers/gpu/drm/i915/i915_gem.c               |  3 ++-
 drivers/gpu/drm/i915/i915_gem_gtt.c           | 10 +++++---
 include/drm/drm_mm.h                          | 12 ++++++++++
 6 files changed, 41 insertions(+), 14 deletions(-)

Patch
diff mbox series

diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
index 8981abe8b7c9..794d9d4c2d36 100644
--- a/drivers/gpu/drm/drm_mm.c
+++ b/drivers/gpu/drm/drm_mm.c
@@ -45,6 +45,7 @@ 
 #include <linux/export.h>
 #include <linux/interval_tree_generic.h>
 #include <linux/seq_file.h>
+#include <linux/sched/signal.h>
 #include <linux/slab.h>
 #include <linux/stacktrace.h>
 
@@ -343,7 +344,7 @@  first_hole(struct drm_mm *mm,
 	   u64 start, u64 end, u64 size,
 	   enum drm_mm_insert_mode mode)
 {
-	switch (mode) {
+	switch (mode & DRM_MM_INSERT_MODE) {
 	default:
 	case DRM_MM_INSERT_BEST:
 		return best_hole(mm, size);
@@ -366,7 +367,17 @@  next_hole(struct drm_mm *mm,
 	  struct drm_mm_node *node,
 	  enum drm_mm_insert_mode mode)
 {
-	switch (mode) {
+	if (mode & DRM_MM_INSERT_ONCE)
+		return NULL; /* check only the first hit */
+
+	/* Searching is slow; check if we ran out of time/patience */
+	if (mode & DRM_MM_INSERT_INTERRUPTIBLE) {
+		cond_resched();
+		if (fatal_signal_pending(current))
+			return NULL;
+	}
+
+	switch (mode & DRM_MM_INSERT_MODE) {
 	default:
 	case DRM_MM_INSERT_BEST:
 		return rb_hole_size_to_node(rb_prev(&node->rb_hole_size));
@@ -470,7 +481,6 @@  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);
 
@@ -483,13 +493,10 @@  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 = once ? NULL : next_hole(mm, hole, mode)) {
+	     hole = 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;
@@ -557,7 +564,7 @@  int drm_mm_insert_node_in_range(struct drm_mm * const mm,
 		return 0;
 	}
 
-	return -ENOSPC;
+	return signal_pending(current) ? -ERESTARTSYS : -ENOSPC;
 }
 EXPORT_SYMBOL(drm_mm_insert_node_in_range);
 
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index cda35e6dfc44..21bdfa91b603 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -1080,7 +1080,8 @@  static void *reloc_iomap(struct drm_i915_gem_object *obj,
 				(&ggtt->vm.mm, &cache->node,
 				 PAGE_SIZE, 0, I915_COLOR_UNEVICTABLE,
 				 0, ggtt->mappable_end,
-				 DRM_MM_INSERT_LOW);
+				 DRM_MM_INSERT_LOW |
+				 DRM_MM_INSERT_INTERRUPTIBLE);
 			mutex_unlock(&ggtt->vm.mutex);
 			if (err) /* no inactive aperture space, use cpu reloc */
 				return NULL;
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
index 5557dfa83a7b..04403f8a7d3a 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
@@ -43,7 +43,9 @@  int i915_gem_stolen_insert_node_in_range(struct drm_i915_private *i915,
 	mutex_lock(&i915->mm.stolen_lock);
 	ret = drm_mm_insert_node_in_range(&i915->mm.stolen, node,
 					  size, alignment, 0,
-					  start, end, DRM_MM_INSERT_BEST);
+					  start, end,
+					  DRM_MM_INSERT_BEST |
+					  DRM_MM_INSERT_INTERRUPTIBLE);
 	mutex_unlock(&i915->mm.stolen_lock);
 
 	return ret;
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 762b50b08d73..9099c311f984 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -69,7 +69,8 @@  insert_mappable_node(struct i915_ggtt *ggtt, struct drm_mm_node *node, u32 size)
 	err = drm_mm_insert_node_in_range(&ggtt->vm.mm, node,
 					  size, 0, I915_COLOR_UNEVICTABLE,
 					  0, ggtt->mappable_end,
-					  DRM_MM_INSERT_LOW);
+					  DRM_MM_INSERT_LOW |
+					  DRM_MM_INSERT_INTERRUPTIBLE);
 
 	mutex_unlock(&ggtt->vm.mutex);
 
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index cb43381b0d37..899b2af13840 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -232,7 +232,8 @@  int i915_gem_gtt_insert(struct i915_address_space *vm,
 
 	err = drm_mm_insert_node_in_range(&vm->mm, node,
 					  size, alignment, color,
-					  start, end, mode);
+					  start, end,
+					  mode | DRM_MM_INSERT_INTERRUPTIBLE);
 	if (err != -ENOSPC)
 		return err;
 
@@ -240,7 +241,8 @@  int i915_gem_gtt_insert(struct i915_address_space *vm,
 		err = drm_mm_insert_node_in_range(&vm->mm, node,
 						  size, alignment, color,
 						  start, end,
-						  DRM_MM_INSERT_BEST);
+						  DRM_MM_INSERT_BEST |
+						  DRM_MM_INSERT_INTERRUPTIBLE);
 		if (err != -ENOSPC)
 			return err;
 	}
@@ -288,7 +290,9 @@  int i915_gem_gtt_insert(struct i915_address_space *vm,
 
 	return drm_mm_insert_node_in_range(&vm->mm, node,
 					   size, alignment, color,
-					   start, end, DRM_MM_INSERT_EVICT);
+					   start, end,
+					   DRM_MM_INSERT_EVICT |
+					   DRM_MM_INSERT_INTERRUPTIBLE);
 }
 
 #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h
index ee8b0e80ca90..74c9fd61be3a 100644
--- a/include/drm/drm_mm.h
+++ b/include/drm/drm_mm.h
@@ -141,8 +141,20 @@  enum drm_mm_insert_mode {
 	 * Does not search all holes.
 	 */
 	DRM_MM_INSERT_LOWEST  = DRM_MM_INSERT_LOW | DRM_MM_INSERT_ONCE,
+
+	/**
+	 * @DRM_MM_INSERT_INTERRUPTIBLE:
+	 *
+	 * Check for pending signals and allow rescheduling admist the
+	 * search. In some heavily fragmented cases, searching for an available
+	 * node of just the right size can take a long time, in which case it
+	 * is better to let something else run during our fruitless search.
+	 */
+	DRM_MM_INSERT_INTERRUPTIBLE = BIT(30),
 };
 
+#define DRM_MM_INSERT_MODE GENMASK(29, 0) /* all but the special bits */
+
 /**
  * struct drm_mm_node - allocated block in the DRM allocator
  *