diff mbox series

drm/mm: Break long searches in fragmented address spaces

Message ID 20200207151720.2812125-1-chris@chris-wilson.co.uk (mailing list archive)
State New, archived
Headers show
Series drm/mm: Break long searches in fragmented address spaces | expand

Commit Message

Chris Wilson Feb. 7, 2020, 3:17 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 | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

Comments

Andi Shyti Feb. 11, 2020, 10:56 p.m. UTC | #1
Hi Chris,

On Fri, Feb 07, 2020 at 03:17:20PM +0000, Chris Wilson wrote:
> 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

checking for "fatal signals" you mean?

> 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>

nice!

Reviewed-by: Andi Shyti <andi.shyti@intel.com>

Thanks,
Andi

> ---
>  drivers/gpu/drm/drm_mm.c | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
> index 2a6e34663146..47d5de9ca0a8 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>
>  
> @@ -366,6 +367,11 @@ next_hole(struct drm_mm *mm,
>  	  struct drm_mm_node *node,
>  	  enum drm_mm_insert_mode mode)
>  {
> +	/* Searching is slow; check if we ran out of time/patience */
> +	cond_resched();
> +	if (fatal_signal_pending(current))
> +		return NULL;
> +
>  	switch (mode) {
>  	default:
>  	case DRM_MM_INSERT_BEST:
> @@ -557,7 +563,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);
>  
> -- 
> 2.25.0
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Chris Wilson Feb. 14, 2020, 12:34 a.m. UTC | #2
Quoting Andi Shyti (2020-02-11 22:56:44)
> Hi Chris,
> 
> On Fri, Feb 07, 2020 at 03:17:20PM +0000, Chris Wilson wrote:
> > 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
> 
> checking for "fatal signals" you mean?

We need to call schedule() ourselves for non-voluntary CONFIG_PREEMPT
kernels, and we need to escape from the kernel back to userspace to
handle a pending signal in this process.

Other applications, sysadmins tend to notice and complain about driver
hogging a CPU not letting anything else run; typically the user notices
that their application doesn't close on ^C.

(That is schedule() delays of a few ms will be noticed, but it takes a
couple of seconds before a user will become aware of a severe problem
with a stuck signal. :)

Now since responding to a signal itself may be expensive (we have to
restart the syscall and re-process all the state from before this
point), we make the trade-off here of only responding to a fatal-signal
(process exit), which should allow for faster system recovery under
pathological conditions.
-Chris
diff mbox series

Patch

diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
index 2a6e34663146..47d5de9ca0a8 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>
 
@@ -366,6 +367,11 @@  next_hole(struct drm_mm *mm,
 	  struct drm_mm_node *node,
 	  enum drm_mm_insert_mode mode)
 {
+	/* Searching is slow; check if we ran out of time/patience */
+	cond_resched();
+	if (fatal_signal_pending(current))
+		return NULL;
+
 	switch (mode) {
 	default:
 	case DRM_MM_INSERT_BEST:
@@ -557,7 +563,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);