mirror of
https://github.com/torvalds/linux.git
synced 2025-11-01 00:58:39 +02:00
drm/panic: Add support to scanout buffer as array of pages
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. [1] https://lore.kernel.org/dri-devel/20250305152555.318159-1-ryasuoka@redhat.com/ Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Acked-by: Simona Vetter <simona@ffwll.ch> Signed-off-by: Jocelyn Falempe <jfalempe@redhat.com> Link: https://lore.kernel.org/r/20250407140138.162383-3-jfalempe@redhat.com
This commit is contained in:
parent
8702048bb8
commit
c9ff280879
2 changed files with 148 additions and 8 deletions
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/font.h>
|
#include <linux/font.h>
|
||||||
|
#include <linux/highmem.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/iosys-map.h>
|
#include <linux/iosys-map.h>
|
||||||
#include <linux/kdebug.h>
|
#include <linux/kdebug.h>
|
||||||
|
|
@ -154,6 +155,90 @@ 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);
|
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:
|
||||||
|
pr_debug_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_try_from_panic() to map the page, and write the pixel.
|
||||||
|
* Try 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 (!pages[new_page])
|
||||||
|
continue;
|
||||||
|
if (vaddr)
|
||||||
|
kunmap_local(vaddr);
|
||||||
|
page = new_page;
|
||||||
|
vaddr = kmap_local_page_try_from_panic(pages[page]);
|
||||||
|
}
|
||||||
|
if (vaddr)
|
||||||
|
drm_panic_write_pixel(vaddr, offset, fg32, cpp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vaddr)
|
||||||
|
kunmap_local(vaddr);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* drm_panic_blit - convert a monochrome image to a linear framebuffer
|
* drm_panic_blit - convert a monochrome image to a linear framebuffer
|
||||||
* @sb: destination scanout buffer
|
* @sb: destination scanout buffer
|
||||||
|
|
@ -177,6 +262,10 @@ static void drm_panic_blit(struct drm_scanout_buffer *sb, struct drm_rect *clip,
|
||||||
if (sb->set_pixel)
|
if (sb->set_pixel)
|
||||||
return drm_panic_blit_pixel(sb, clip, sbuf8, spitch, scale, fg_color);
|
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];
|
map = sb->map[0];
|
||||||
iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]);
|
iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]);
|
||||||
|
|
||||||
|
|
@ -209,6 +298,35 @@ static void drm_panic_fill_pixel(struct drm_scanout_buffer *sb,
|
||||||
sb->set_pixel(sb, clip->x1 + x, clip->y1 + y, color);
|
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_try_from_panic(pages[page]);
|
||||||
|
}
|
||||||
|
drm_panic_write_pixel(vaddr, offset, color, cpp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vaddr)
|
||||||
|
kunmap_local(vaddr);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* drm_panic_fill - Fill a rectangle with a color
|
* drm_panic_fill - Fill a rectangle with a color
|
||||||
* @sb: destination scanout buffer
|
* @sb: destination scanout buffer
|
||||||
|
|
@ -225,6 +343,10 @@ static void drm_panic_fill(struct drm_scanout_buffer *sb, struct drm_rect *clip,
|
||||||
if (sb->set_pixel)
|
if (sb->set_pixel)
|
||||||
return drm_panic_fill_pixel(sb, clip, color);
|
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];
|
map = sb->map[0];
|
||||||
iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]);
|
iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]);
|
||||||
|
|
||||||
|
|
@ -709,16 +831,24 @@ static void draw_panic_plane(struct drm_plane *plane, const char *description)
|
||||||
if (!drm_panic_trylock(plane->dev, flags))
|
if (!drm_panic_trylock(plane->dev, flags))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
drm_panic_set_description(description);
|
|
||||||
|
|
||||||
ret = plane->helper_private->get_scanout_buffer(plane, &sb);
|
ret = plane->helper_private->get_scanout_buffer(plane, &sb);
|
||||||
|
|
||||||
if (!ret && drm_panic_is_format_supported(sb.format)) {
|
if (ret || !drm_panic_is_format_supported(sb.format))
|
||||||
draw_panic_dispatch(&sb);
|
goto unlock;
|
||||||
if (plane->helper_private->panic_flush)
|
|
||||||
plane->helper_private->panic_flush(plane);
|
/* 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);
|
||||||
|
|
||||||
|
draw_panic_dispatch(&sb);
|
||||||
|
if (plane->helper_private->panic_flush)
|
||||||
|
plane->helper_private->panic_flush(plane);
|
||||||
|
|
||||||
drm_panic_clear_description();
|
drm_panic_clear_description();
|
||||||
|
|
||||||
|
unlock:
|
||||||
drm_panic_unlock(plane->dev, flags);
|
drm_panic_unlock(plane->dev, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,16 @@ struct drm_scanout_buffer {
|
||||||
*/
|
*/
|
||||||
struct iosys_map map[DRM_FORMAT_MAX_PLANES];
|
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_try_from_panic() to map one page at a time to write
|
||||||
|
* all the pixels. This array shouldn't be allocated from the
|
||||||
|
* get_scanoutbuffer() callback.
|
||||||
|
* The scanout buffer should be in linear format.
|
||||||
|
*/
|
||||||
|
struct page **pages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @width: Width of the scanout buffer, in pixels.
|
* @width: Width of the scanout buffer, in pixels.
|
||||||
*/
|
*/
|
||||||
|
|
@ -57,7 +67,7 @@ struct drm_scanout_buffer {
|
||||||
/**
|
/**
|
||||||
* @set_pixel: Optional function, to set a pixel color on the
|
* @set_pixel: Optional function, to set a pixel color on the
|
||||||
* framebuffer. It allows to handle special tiling format inside 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,
|
void (*set_pixel)(struct drm_scanout_buffer *sb, unsigned int x,
|
||||||
unsigned int y, u32 color);
|
unsigned int y, u32 color);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue