[PATCH] drm/panic: Add support to scanout buffer as array of pages

Thomas Zimmermann tzimmermann at suse.de
Mon Mar 17 13:47:50 UTC 2025


Hi

Am 13.03.25 um 10:42 schrieb Jocelyn Falempe:
> Some drivers like virtio-gpu, don't map the scanout buffer in the
> kernel. Calling vmap() in a panic handler is not safe, and writing an
> atomic_vmap() API is more complex than expected [1].
> So instead, pass the array of pages of the scanout buffer to the
> panic handler, and map only one page at a time to draw the pixels.
> This is obviously slow, but acceptable for a panic handler.

It seems this could still be optimized by writing multiple pixels at 
once if they are located on the same pages. Although it's not really 
necessary.

>
> [1] https://lore.kernel.org/dri-devel/20250305152555.318159-1-ryasuoka@redhat.com/
>
> Signed-off-by: Jocelyn Falempe <jfalempe at redhat.com>

Acked-by: Thomas Zimmermann <tzimmermann at suse.de>

Best regards
Thomas

> ---
>   drivers/gpu/drm/drm_panic.c | 139 ++++++++++++++++++++++++++++++++++--
>   include/drm/drm_panic.h     |  10 ++-
>   2 files changed, 142 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
> index ab42a2b1567d..e10ab8fe847c 100644
> --- a/drivers/gpu/drm/drm_panic.c
> +++ b/drivers/gpu/drm/drm_panic.c
> @@ -7,6 +7,7 @@
>    */
>   
>   #include <linux/font.h>
> +#include <linux/highmem.h>
>   #include <linux/init.h>
>   #include <linux/iosys-map.h>
>   #include <linux/kdebug.h>
> @@ -154,6 +155,87 @@ static void drm_panic_blit_pixel(struct drm_scanout_buffer *sb, struct drm_rect
>   				sb->set_pixel(sb, clip->x1 + x, clip->y1 + y, fg_color);
>   }
>   
> +static void drm_panic_write_pixel16(void *vaddr, unsigned int offset, u16 color)
> +{
> +	u16 *p = vaddr + offset;
> +
> +	*p = color;
> +}
> +
> +static void drm_panic_write_pixel24(void *vaddr, unsigned int offset, u32 color)
> +{
> +	u8 *p = vaddr + offset;
> +
> +	*p++ = color & 0xff;
> +	color >>= 8;
> +	*p++ = color & 0xff;
> +	color >>= 8;
> +	*p = color & 0xff;
> +}
> +
> +static void drm_panic_write_pixel32(void *vaddr, unsigned int offset, u32 color)
> +{
> +	u32 *p = vaddr + offset;
> +
> +	*p = color;
> +}
> +
> +static void drm_panic_write_pixel(void *vaddr, unsigned int offset, u32 color, unsigned int cpp)
> +{
> +	switch (cpp) {
> +	case 2:
> +		drm_panic_write_pixel16(vaddr, offset, color);
> +		break;
> +	case 3:
> +		drm_panic_write_pixel24(vaddr, offset, color);
> +		break;
> +	case 4:
> +		drm_panic_write_pixel32(vaddr, offset, color);
> +		break;
> +	default:
> +		DRM_WARN_ONCE("Can't blit with pixel width %d\n", cpp);
> +	}
> +}
> +
> +/*
> + * The scanout buffer pages are not mapped, so for each pixel,
> + * use kmap_local_page() to map the page, and write the pixel.
> + * Tries to keep the map from the previous pixel, to avoid too much map/unmap.
> + */
> +static void drm_panic_blit_page(struct page **pages, unsigned int dpitch,
> +				unsigned int cpp, const u8 *sbuf8,
> +				unsigned int spitch, struct drm_rect *clip,
> +				unsigned int scale, u32 fg32)
> +{
> +	unsigned int y, x;
> +	unsigned int page = ~0;
> +	unsigned int height = drm_rect_height(clip);
> +	unsigned int width = drm_rect_width(clip);
> +	void *vaddr = NULL;
> +
> +	for (y = 0; y < height; y++) {
> +		for (x = 0; x < width; x++) {
> +			if (drm_draw_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) {
> +				unsigned int new_page;
> +				unsigned int offset;
> +
> +				offset = (y + clip->y1) * dpitch + (x + clip->x1) * cpp;
> +				new_page = offset >> PAGE_SHIFT;
> +				offset = offset % PAGE_SIZE;
> +				if (new_page != page) {
> +					if (vaddr)
> +						kunmap_local(vaddr);
> +					page = new_page;
> +					vaddr = kmap_local_page(pages[page]);
> +				}
> +				drm_panic_write_pixel(vaddr, offset, fg32, cpp);
> +			}
> +		}
> +	}
> +	if (vaddr)
> +		kunmap_local(vaddr);
> +}
> +
>   /*
>    * drm_panic_blit - convert a monochrome image to a linear framebuffer
>    * @sb: destination scanout buffer
> @@ -177,6 +259,10 @@ static void drm_panic_blit(struct drm_scanout_buffer *sb, struct drm_rect *clip,
>   	if (sb->set_pixel)
>   		return drm_panic_blit_pixel(sb, clip, sbuf8, spitch, scale, fg_color);
>   
> +	if (sb->pages)
> +		return drm_panic_blit_page(sb->pages, sb->pitch[0], sb->format->cpp[0],
> +					   sbuf8, spitch, clip, scale, fg_color);
> +
>   	map = sb->map[0];
>   	iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]);
>   
> @@ -209,6 +295,35 @@ static void drm_panic_fill_pixel(struct drm_scanout_buffer *sb,
>   			sb->set_pixel(sb, clip->x1 + x, clip->y1 + y, color);
>   }
>   
> +static void drm_panic_fill_page(struct page **pages, unsigned int dpitch,
> +				unsigned int cpp, struct drm_rect *clip,
> +				u32 color)
> +{
> +	unsigned int y, x;
> +	unsigned int page = ~0;
> +	void *vaddr = NULL;
> +
> +	for (y = clip->y1; y < clip->y2; y++) {
> +		for (x = clip->x1; x < clip->x2; x++) {
> +			unsigned int new_page;
> +			unsigned int offset;
> +
> +			offset = y * dpitch + x * cpp;
> +			new_page = offset >> PAGE_SHIFT;
> +			offset = offset % PAGE_SIZE;
> +			if (new_page != page) {
> +				if (vaddr)
> +					kunmap_local(vaddr);
> +				page = new_page;
> +				vaddr = kmap_local_page(pages[page]);
> +			}
> +			drm_panic_write_pixel(vaddr, offset, color, cpp);
> +		}
> +	}
> +	if (vaddr)
> +		kunmap_local(vaddr);
> +}
> +
>   /*
>    * drm_panic_fill - Fill a rectangle with a color
>    * @sb: destination scanout buffer
> @@ -225,6 +340,10 @@ static void drm_panic_fill(struct drm_scanout_buffer *sb, struct drm_rect *clip,
>   	if (sb->set_pixel)
>   		return drm_panic_fill_pixel(sb, clip, color);
>   
> +	if (sb->pages)
> +		return drm_panic_fill_page(sb->pages, sb->pitch[0], sb->format->cpp[0],
> +					   clip, color);
> +
>   	map = sb->map[0];
>   	iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]);
>   
> @@ -714,16 +833,24 @@ static void draw_panic_plane(struct drm_plane *plane, const char *description)
>   	if (!drm_panic_trylock(plane->dev, flags))
>   		return;
>   
> +	ret = plane->helper_private->get_scanout_buffer(plane, &sb);
> +
> +	if (ret || !drm_panic_is_format_supported(sb.format))
> +		goto unlock;
> +
> +	/* One of these should be set, or it can't draw pixels */
> +	if (!sb.set_pixel && !sb.pages && iosys_map_is_null(&sb.map[0]))
> +		goto unlock;
> +
>   	drm_panic_set_description(description);
>   
> -	ret = plane->helper_private->get_scanout_buffer(plane, &sb);
> +	draw_panic_dispatch(&sb);
> +	if (plane->helper_private->panic_flush)
> +		plane->helper_private->panic_flush(plane);
>   
> -	if (!ret && drm_panic_is_format_supported(sb.format)) {
> -		draw_panic_dispatch(&sb);
> -		if (plane->helper_private->panic_flush)
> -			plane->helper_private->panic_flush(plane);
> -	}
>   	drm_panic_clear_description();
> +
> +unlock:
>   	drm_panic_unlock(plane->dev, flags);
>   }
>   
> diff --git a/include/drm/drm_panic.h b/include/drm/drm_panic.h
> index f4e1fa9ae607..8b91a13347b9 100644
> --- a/include/drm/drm_panic.h
> +++ b/include/drm/drm_panic.h
> @@ -39,6 +39,14 @@ struct drm_scanout_buffer {
>   	 */
>   	struct iosys_map map[DRM_FORMAT_MAX_PLANES];
>   
> +	/**
> +	 * @pages: Optional, if the scanout buffer is not mapped, set this field
> +	 * to the array of pages of the scanout buffer. The panic code will use
> +	 * kmap_local_page() to map one page at a time to write all the pixels.
> +	 * The scanout buffer should be in linear format.
> +	 */
> +	struct page **pages;
> +
>   	/**
>   	 * @width: Width of the scanout buffer, in pixels.
>   	 */
> @@ -57,7 +65,7 @@ struct drm_scanout_buffer {
>   	/**
>   	 * @set_pixel: Optional function, to set a pixel color on the
>   	 * framebuffer. It allows to handle special tiling format inside the
> -	 * driver.
> +	 * driver. It takes precedence over the @map and @pages fields.
>   	 */
>   	void (*set_pixel)(struct drm_scanout_buffer *sb, unsigned int x,
>   			  unsigned int y, u32 color);
>
> base-commit: 9e75b6ef407fee5d4ed8021cd7ddd9d6a8f7b0e8

-- 
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstrasse 146, 90461 Nuernberg, Germany
GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman
HRB 36809 (AG Nuernberg)



More information about the dri-devel mailing list