diff mbox series

[v2] drm/vmwgfx: Filter modes which exceed 3/4 of graphics memory.

Message ID 20240201051343.9936-1-ian.forbes@broadcom.com (mailing list archive)
State New, archived
Headers show
Series [v2] drm/vmwgfx: Filter modes which exceed 3/4 of graphics memory. | expand

Commit Message

Ian Forbes Feb. 1, 2024, 5:13 a.m. UTC
SVGA requires surfaces to fit within graphics memory (max_mob_pages) which
means that modes with a final buffer size that would exceed graphics memory
must be pruned otherwise creation will fail.

Additionally, device commands which use multiple graphics resources must
have all their resources fit within graphics memory for the duration of the
command. Thus we need a small carve out of 1/4 of graphics memory to ensure
commands likes surface copies to the primary framebuffer for cursor
composition or damage clips can fit within graphics memory.

This fixes an issue where VMs with low graphics memory (< 64MiB) configured
with high resolution mode boot to a black screen because surface creation
fails.

Signed-off-by: Ian Forbes <ian.forbes@broadcom.com>
---
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

Comments

Zack Rusin Feb. 2, 2024, 7:29 p.m. UTC | #1
On Fri, Feb 2, 2024 at 11:58 AM Ian Forbes <ian.forbes@broadcom.com> wrote:
>
> SVGA requires surfaces to fit within graphics memory (max_mob_pages) which
> means that modes with a final buffer size that would exceed graphics memory
> must be pruned otherwise creation will fail.

Sorry, I didn't notice this originally but that's not quite true. svga
doesn't require all mob memory to stay within max_mob_pages (which is
SVGA_REG_GBOBJECT_MEM_SIZE_KB). max_mob_pages is really max resident
memory or suggested-guest-memory-for-best-performance. we can grow
that memory (and we do). I think what's causing problems on systems
with low memory is that cursor mobs and the fb's need to be both
resident but can't. Now SVGA_REG_MAX_PRIMARY_MEM is the max memory in
which our topology needs to fit in (which is max_primary_mem on
vmwgfx) but afaict that's not the issue here and it's checked later in
vmw_kms_validate_mode_vram

> Additionally, device commands which use multiple graphics resources must
> have all their resources fit within graphics memory for the duration of the
> command. Thus we need a small carve out of 1/4 of graphics memory to ensure
> commands likes surface copies to the primary framebuffer for cursor
> composition or damage clips can fit within graphics memory.

Yes, we should probably rename max_mob_pages to max_resident_memory
instead to make this obvious.

> This fixes an issue where VMs with low graphics memory (< 64MiB) configured
> with high resolution mode boot to a black screen because surface creation
> fails.

Does this work if you disable gbobjects? Without gbobject's we won't
have screen targets and thus won't be offsetting by 1/4 so I wonder if
4mb vram with legacy display would work with 1280x800 resolution.

Also, you want to add a "V2" section to your change to describe what
changed in v2 vs v1 (and same for any subsequent change).

>
> Signed-off-by: Ian Forbes <ian.forbes@broadcom.com>
> ---
>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 22 ++++++++++++++--------
>  1 file changed, 14 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> index cd4925346ed4..84e1b765cda3 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> @@ -2858,12 +2858,17 @@ enum drm_mode_status vmw_connector_mode_valid(struct drm_connector *connector,
>         struct vmw_private *dev_priv = vmw_priv(dev);
>         u32 max_width = dev_priv->texture_max_width;
>         u32 max_height = dev_priv->texture_max_height;
> -       u32 assumed_cpp = 4;
> -
> -       if (dev_priv->assume_16bpp)
> -               assumed_cpp = 2;
> +       u32 assumed_cpp = dev_priv->assume_16bpp ? 2 : 4;
> +       u32 pitch = mode->hdisplay * assumed_cpp;
> +       u64 total = mode->vdisplay * pitch;
> +       bool using_stdu = dev_priv->active_display_unit == vmw_du_screen_target;
> +       u64 max_mem_for_st = dev_priv->max_mob_pages * PAGE_SIZE * 3 / 4;
> +       /* ^^^ Max memory for the mode fb when using Screen Target / MOBs.
> +        * We need a carveout (1/4) to account for other gfx resources that are
> +        * required in gfx mem for an fb update to complete with low gfx mem (<64MiB).
> +        */

Same wording issue as mentioned above and lets use normal comment
style (i.e. comments attach to the code below). max_mem_for_st should
probably be max_mem_for_mode or max_mem_for_mode_st.

> -       if (dev_priv->active_display_unit == vmw_du_screen_target) {
> +       if (using_stdu) {
>                 max_width  = min(dev_priv->stdu_max_width,  max_width);
>                 max_height = min(dev_priv->stdu_max_height, max_height);
>         }
> @@ -2874,9 +2879,10 @@ enum drm_mode_status vmw_connector_mode_valid(struct drm_connector *connector,
>         if (max_height < mode->vdisplay)
>                 return MODE_BAD_VVALUE;
>
> -       if (!vmw_kms_validate_mode_vram(dev_priv,
> -                                       mode->hdisplay * assumed_cpp,
> -                                       mode->vdisplay))
> +       if (using_stdu && (total > max_mem_for_st || total > dev_priv->max_mob_size))
> +               return MODE_MEM;
> +
> +       if (!vmw_kms_validate_mode_vram(dev_priv, pitch, mode->vdisplay))
>                 return MODE_MEM;

It might make sense to just reuse vmw_kms_validate_mode_vram , it does
what we're claiming to do here and even though it's called
vmw_kms_validate_mode_vram it does actually validate st primary
memory.

z
Ian Forbes Feb. 6, 2024, 9:30 p.m. UTC | #2
So the issue is that SVGA_3D_CMD_DX_PRED_COPY_REGION between 2
surfaces that are the size of the mode fails. Technically for this to
work the filter will have to be 1/2 of graphics mem. I was just lucky
that the next mode in the list was already less than 1/2. 3/4 is not
actually going to work. Also this only happens on X/Gnome and seems
more like an issue with the compositor. Wayland/Gnome displays the
desktop but it's unusable and glitches even with the 1/2 limit. I
don't think wayland even abides by the mode limits as I see it trying
to create surfaces larger than the mode. It might be using texture
limits instead.

On Fri, Feb 2, 2024 at 1:29 PM Zack Rusin <zack.rusin@broadcom.com> wrote:
>
> On Fri, Feb 2, 2024 at 11:58 AM Ian Forbes <ian.forbes@broadcom.com> wrote:
> >
> > SVGA requires surfaces to fit within graphics memory (max_mob_pages) which
> > means that modes with a final buffer size that would exceed graphics memory
> > must be pruned otherwise creation will fail.
>
> Sorry, I didn't notice this originally but that's not quite true. svga
> doesn't require all mob memory to stay within max_mob_pages (which is
> SVGA_REG_GBOBJECT_MEM_SIZE_KB). max_mob_pages is really max resident
> memory or suggested-guest-memory-for-best-performance. we can grow
> that memory (and we do). I think what's causing problems on systems
> with low memory is that cursor mobs and the fb's need to be both
> resident but can't. Now SVGA_REG_MAX_PRIMARY_MEM is the max memory in
> which our topology needs to fit in (which is max_primary_mem on
> vmwgfx) but afaict that's not the issue here and it's checked later in
> vmw_kms_validate_mode_vram
>
> > Additionally, device commands which use multiple graphics resources must
> > have all their resources fit within graphics memory for the duration of the
> > command. Thus we need a small carve out of 1/4 of graphics memory to ensure
> > commands likes surface copies to the primary framebuffer for cursor
> > composition or damage clips can fit within graphics memory.
>
> Yes, we should probably rename max_mob_pages to max_resident_memory
> instead to make this obvious.
>
> > This fixes an issue where VMs with low graphics memory (< 64MiB) configured
> > with high resolution mode boot to a black screen because surface creation
> > fails.
>
> Does this work if you disable gbobjects? Without gbobject's we won't
> have screen targets and thus won't be offsetting by 1/4 so I wonder if
> 4mb vram with legacy display would work with 1280x800 resolution.
>
> Also, you want to add a "V2" section to your change to describe what
> changed in v2 vs v1 (and same for any subsequent change).
>
> >
> > Signed-off-by: Ian Forbes <ian.forbes@broadcom.com>
> > ---
> >  drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 22 ++++++++++++++--------
> >  1 file changed, 14 insertions(+), 8 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> > index cd4925346ed4..84e1b765cda3 100644
> > --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> > +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> > @@ -2858,12 +2858,17 @@ enum drm_mode_status vmw_connector_mode_valid(struct drm_connector *connector,
> >         struct vmw_private *dev_priv = vmw_priv(dev);
> >         u32 max_width = dev_priv->texture_max_width;
> >         u32 max_height = dev_priv->texture_max_height;
> > -       u32 assumed_cpp = 4;
> > -
> > -       if (dev_priv->assume_16bpp)
> > -               assumed_cpp = 2;
> > +       u32 assumed_cpp = dev_priv->assume_16bpp ? 2 : 4;
> > +       u32 pitch = mode->hdisplay * assumed_cpp;
> > +       u64 total = mode->vdisplay * pitch;
> > +       bool using_stdu = dev_priv->active_display_unit == vmw_du_screen_target;
> > +       u64 max_mem_for_st = dev_priv->max_mob_pages * PAGE_SIZE * 3 / 4;
> > +       /* ^^^ Max memory for the mode fb when using Screen Target / MOBs.
> > +        * We need a carveout (1/4) to account for other gfx resources that are
> > +        * required in gfx mem for an fb update to complete with low gfx mem (<64MiB).
> > +        */
>
> Same wording issue as mentioned above and lets use normal comment
> style (i.e. comments attach to the code below). max_mem_for_st should
> probably be max_mem_for_mode or max_mem_for_mode_st.
>
> > -       if (dev_priv->active_display_unit == vmw_du_screen_target) {
> > +       if (using_stdu) {
> >                 max_width  = min(dev_priv->stdu_max_width,  max_width);
> >                 max_height = min(dev_priv->stdu_max_height, max_height);
> >         }
> > @@ -2874,9 +2879,10 @@ enum drm_mode_status vmw_connector_mode_valid(struct drm_connector *connector,
> >         if (max_height < mode->vdisplay)
> >                 return MODE_BAD_VVALUE;
> >
> > -       if (!vmw_kms_validate_mode_vram(dev_priv,
> > -                                       mode->hdisplay * assumed_cpp,
> > -                                       mode->vdisplay))
> > +       if (using_stdu && (total > max_mem_for_st || total > dev_priv->max_mob_size))
> > +               return MODE_MEM;
> > +
> > +       if (!vmw_kms_validate_mode_vram(dev_priv, pitch, mode->vdisplay))
> >                 return MODE_MEM;
>
> It might make sense to just reuse vmw_kms_validate_mode_vram , it does
> what we're claiming to do here and even though it's called
> vmw_kms_validate_mode_vram it does actually validate st primary
> memory.
>
> z
Zack Rusin Feb. 7, 2024, 6:03 p.m. UTC | #3
On Tue, Feb 6, 2024 at 4:30 PM Ian Forbes <ian.forbes@broadcom.com> wrote:
>
> So the issue is that SVGA_3D_CMD_DX_PRED_COPY_REGION between 2
> surfaces that are the size of the mode fails. Technically for this to
> work the filter will have to be 1/2 of graphics mem. I was just lucky
> that the next mode in the list was already less than 1/2. 3/4 is not
> actually going to work. Also this only happens on X/Gnome and seems
> more like an issue with the compositor. Wayland/Gnome displays the
> desktop but it's unusable and glitches even with the 1/2 limit. I
> don't think wayland even abides by the mode limits as I see it trying
> to create surfaces larger than the mode. It might be using texture
> limits instead.

So the SVGA_3D_CMD_DX_PRED_COPY_REGION is only available with dx
contexts/3d enabled/gb surfaces. With 3d or gb objects disabled we
should fall back to legacy display and that command shouldn't have
been used. Is that the case? Does it work with 3d/gb objects disabled?

There's a few bugs there:
- SVGA_3D_CMD_DX_PRED_COPY_REGION should only come from userspace, the
userspace should validate that the max amount of resident memory
hasn't been exceeded before issuing those copies
- vmwgfx should be a lot better about determining whether the amount
of resident memory required by the current command buffers hasn't been
exceeded
- In case of high memory pressure vmwgfx should explicitly disable 3d
support. There's no way to run 3d workloads with anything less than
64mb of ram especially given that we do not adjust our texture limits
and they will remain either 4k, 8k or more depending on what we're
running on.

But those are secondary to making resolution switch work correctly on
basic system, i.e.:
1) Disable 3D and gb objects
2) Check if in the kernel log vmwgx says that it's using "legacy display"
3) Check if the resolution switching works correctly
4) If not lets fix that first (fix #1)
5) Disable 3D and keep gb objects active
6) Check that the kernel log select "screen target display unit" and
have 3d disabled (i.e. no SVGA_3D_CMD_DX_PRED_COPY_REGION is coming
through)
7) If that doesn't work lets fix that next (fix #2)
8) Enabled 3d and gb objects (your current default)
9) Check if max_mob_pages (i.e. max_resident_memory) is smaller than
what we'd need to hold even a single a texture limits * 4bpp, print a
warning and disable 3d (this should bring us in line with what we
fixed in point #7) (fix #3)

So basically we want to make sure that on vmwgfx all three
configurations work: 1) 3d and gb objects disabled, 2) 3d disabled, gb
objects enabled, 3) 3d and gb object enabled.

z
diff mbox series

Patch

diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index cd4925346ed4..84e1b765cda3 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -2858,12 +2858,17 @@  enum drm_mode_status vmw_connector_mode_valid(struct drm_connector *connector,
 	struct vmw_private *dev_priv = vmw_priv(dev);
 	u32 max_width = dev_priv->texture_max_width;
 	u32 max_height = dev_priv->texture_max_height;
-	u32 assumed_cpp = 4;
-
-	if (dev_priv->assume_16bpp)
-		assumed_cpp = 2;
+	u32 assumed_cpp = dev_priv->assume_16bpp ? 2 : 4;
+	u32 pitch = mode->hdisplay * assumed_cpp;
+	u64 total = mode->vdisplay * pitch;
+	bool using_stdu = dev_priv->active_display_unit == vmw_du_screen_target;
+	u64 max_mem_for_st = dev_priv->max_mob_pages * PAGE_SIZE * 3 / 4;
+	/* ^^^ Max memory for the mode fb when using Screen Target / MOBs.
+	 * We need a carveout (1/4) to account for other gfx resources that are
+	 * required in gfx mem for an fb update to complete with low gfx mem (<64MiB).
+	 */
 
-	if (dev_priv->active_display_unit == vmw_du_screen_target) {
+	if (using_stdu) {
 		max_width  = min(dev_priv->stdu_max_width,  max_width);
 		max_height = min(dev_priv->stdu_max_height, max_height);
 	}
@@ -2874,9 +2879,10 @@  enum drm_mode_status vmw_connector_mode_valid(struct drm_connector *connector,
 	if (max_height < mode->vdisplay)
 		return MODE_BAD_VVALUE;
 
-	if (!vmw_kms_validate_mode_vram(dev_priv,
-					mode->hdisplay * assumed_cpp,
-					mode->vdisplay))
+	if (using_stdu && (total > max_mem_for_st || total > dev_priv->max_mob_size))
+		return MODE_MEM;
+
+	if (!vmw_kms_validate_mode_vram(dev_priv, pitch, mode->vdisplay))
 		return MODE_MEM;
 
 	return MODE_OK;