[RFC 2/3] drm: Add panic handling
Daniel Vetter
daniel at ffwll.ch
Wed Aug 10 09:15:29 UTC 2016
On Tue, Aug 09, 2016 at 07:45:41PM +0200, Noralf Trønnes wrote:
> This adds support for outputting kernel messages on panic().
> The drivers that supports it, provides a framebuffer that the
> messages can be rendered on.
>
> Signed-off-by: Noralf Trønnes <noralf at tronnes.org>
Thinking about how we should implement this in a full-blown driver, I
think we will need a bit more than this. Here's the additional
requirements I've come up that a driver more complex than sdrm would need
for reliable panic handling:
- Multiple outputs, with multiple framebuffer (or one framebuffer and
multiple offsets within). And since one display might be dead we should
try really hard to show the oops on all of them. Consequence: I think we
need to also loop over all drm_crtc in a drm_device, to be able to
support them all.
- With desktop gpus you pretty much always end up with a tiled
framebuffer. And from an oops context it's going to be impossible to
detile that quickly. I think for this we need a helper to draw x/y
pixels (it's going to be dead slow, but meh), with a default
implementation which assumes linear layout. Drivers could then frob x/y
coordinates first in their own logic and call into that function.
- There's a good chance you're showing a video on a yuv buffer
full-screen. If we don't bother too much with what it looks like, but
only care about a foreground/background color then it's also easy to
implement a draw_xy_pixel for these framebuffers. That means though that
you can't clear things with memset, but that you need to call the
interface func for each pixel. Yes this is going to be dead slow, but
who cares as long as the oops eventually shows up ;-)
- The framebuffer size doesn't necessarily match the size of the visible
part. Probably best if we dig this out from the plane_state directly
(less fragile code in drivers). Relying on drm_plane_state means this
will only work on atomic drivers (in legacy drivers this information is
hidden in driver-private corners), but I think that's totally ok.
- We need locking. One of the biggest problems with the old oops handling
was that it was very good at trampling over driver state, causing more
(unrelated) oopses in kms code and making sure the original oops was no
longer visible. I think the shared code must take care of all the
locking needs to avoid fragile code in drivers. ww_mutex_trylock on the
drm_crtc and drm_plane should be enough (we need both for drivers where
planes can be reassigned between drivers).
- Multiple planes which might occlude the primary plane: I think we should
just bother with the primary plane, and maybe give drivers a
panic_commit hook where they could try to disable any additional planes.
But that's not something we need in the first version here at all.
- I think some helpers for creating the vmap would be nice. Should be
simple for cma-backed framebuffers, and also simple if you use a normal
shmem backed gem buffer. For cma probably best if drivers don't even
need to bother with this.
- For driver convenience I think we could check a few things about
visibility of each plane (e.g. crtc_state->active), and skip if there's
no chance it can be seen. Doing less means less chances to blow up, and
higher chances that the one screen which is on actually ends up with the
oops on it.
- Minimal cleanup. I think we can just leak the vmapping, no harm in that.
But the kms locks need to be dropped again. Dropping them again might
increase the odds that the system limps along long enough for logs to
hit the disks.
Taking this all together, I think the driver interface should be
restructured a bit, and that most of the handling should be on the
drm_framebuffer directly:
struct drm_framebuffer_funcs {
/* For vmapping the selected framebuffer in a panic context. Must
* be super careful about locking (only trylocking allowed), can
* return NULL if it didn't work out. The return value is an
* opaque cookie which is passed to @panic_draw_xy, it can be
* anything: vmap area, structure with more details, just a few
* flags, ...
*/
void *(panic_vmap)(struct drm_framebuffer *fb);
/* For drawing pixels onto a framebuffer mapping with @panic_vmap.
* This is optional, the default implementation assumes that vmap
* points at a linear mapping of the framebuffer.
*/
void (panic_draw_xy)(struct drm_framebuffer *fb, void *vmap,
int x, int y, bool foreground);
};
Ofc comments need to be fleshed out some more, but the idea is that all
the fb selection and lookup is handled in shared code (and with proper
locking, but only for atomic drivers).
Thoughts?
-Daniel
> ---
> drivers/gpu/drm/Makefile | 2 +-
> drivers/gpu/drm/drm_drv.c | 3 +
> drivers/gpu/drm/drm_internal.h | 4 +
> drivers/gpu/drm/drm_panic.c | 606 +++++++++++++++++++++++++++++++++++++++++
> include/drm/drmP.h | 22 ++
> 5 files changed, 636 insertions(+), 1 deletion(-)
> create mode 100644 drivers/gpu/drm/drm_panic.c
>
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index eba32ad..ff04e41 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -12,7 +12,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \
> drm_info.o drm_debugfs.o drm_encoder_slave.o \
> drm_trace_points.o drm_global.o drm_prime.o \
> drm_rect.o drm_vma_manager.o drm_flip_work.o \
> - drm_modeset_lock.o drm_atomic.o drm_bridge.o
> + drm_modeset_lock.o drm_atomic.o drm_bridge.o drm_panic.o
>
> drm-$(CONFIG_COMPAT) += drm_ioc32.o
> drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> index 3b14366..457ee91 100644
> --- a/drivers/gpu/drm/drm_drv.c
> +++ b/drivers/gpu/drm/drm_drv.c
> @@ -861,6 +861,8 @@ static int __init drm_core_init(void)
> goto err_p3;
> }
>
> + drm_panic_init();
> +
> DRM_INFO("Initialized %s %d.%d.%d %s\n",
> CORE_NAME, CORE_MAJOR, CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE);
> return 0;
> @@ -876,6 +878,7 @@ err_p1:
>
> static void __exit drm_core_exit(void)
> {
> + drm_panic_exit();
> debugfs_remove(drm_debugfs_root);
> drm_sysfs_destroy();
>
> diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
> index b86dc9b..7463d9d 100644
> --- a/drivers/gpu/drm/drm_internal.h
> +++ b/drivers/gpu/drm/drm_internal.h
> @@ -90,6 +90,10 @@ int drm_gem_open_ioctl(struct drm_device *dev, void *data,
> void drm_gem_open(struct drm_device *dev, struct drm_file *file_private);
> void drm_gem_release(struct drm_device *dev, struct drm_file *file_private);
>
> +/* drm_panic.c */
> +void drm_panic_init(void);
> +void drm_panic_exit(void);
> +
> /* drm_debugfs.c */
> #if defined(CONFIG_DEBUG_FS)
> int drm_debugfs_init(struct drm_minor *minor, int minor_id,
> diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
> new file mode 100644
> index 0000000..e185c9d
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_panic.c
> @@ -0,0 +1,606 @@
> +/*
> + * Copyright 2016 Noralf Trønnes
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <asm/unaligned.h>
> +#include <drm/drmP.h>
> +#include <linux/console.h>
> +#include <linux/debugfs.h>
> +#include <linux/font.h>
> +#include <linux/kernel.h>
> +#include <linux/seq_file.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +
> +struct drm_panic_fb {
> + struct drm_framebuffer *fb;
> + void *vmem;
> + const struct font_desc *font;
> + unsigned int cols;
> + unsigned int rows;
> + unsigned int xpos;
> + unsigned int ypos;
> +};
> +
> +#define DRM_PANIC_MAX_FBS 64
> +static struct drm_panic_fb drm_panic_fbs[DRM_PANIC_MAX_FBS];
> +
> +#define DRM_PANIC_MAX_KMSGS SZ_4K
> +static char *drm_panic_kmsgs;
> +static size_t drm_panic_kmsgs_pos;
> +
> +static bool drm_panic_active;
> +
> +static void drm_panic_log(const char *fmt, ...);
> +
> +static inline void drm_panic_draw_pixel(u8 *dst, u32 pixel_format, bool val)
> +{
> + switch (pixel_format & ~DRM_FORMAT_BIG_ENDIAN) {
> +
> + case DRM_FORMAT_C8:
> + case DRM_FORMAT_RGB332:
> + case DRM_FORMAT_BGR233:
> + *dst = val ? 0xff : 0x00;
> + break;
> +
> + case DRM_FORMAT_XRGB4444:
> + case DRM_FORMAT_ARGB4444:
> + case DRM_FORMAT_XBGR4444:
> + case DRM_FORMAT_ABGR4444:
> + put_unaligned(val ? 0x0fff : 0x0000, (u16 *)dst);
> + break;
> +
> + case DRM_FORMAT_RGBX4444:
> + case DRM_FORMAT_RGBA4444:
> + case DRM_FORMAT_BGRX4444:
> + case DRM_FORMAT_BGRA4444:
> + put_unaligned(val ? 0xfff0 : 0x0000, (u16 *)dst);
> + break;
> +
> + case DRM_FORMAT_XRGB1555:
> + case DRM_FORMAT_ARGB1555:
> + case DRM_FORMAT_XBGR1555:
> + case DRM_FORMAT_ABGR1555:
> + put_unaligned(val ? 0x7fff : 0x0000, (u16 *)dst);
> + break;
> +
> + case DRM_FORMAT_RGBX5551:
> + case DRM_FORMAT_RGBA5551:
> + case DRM_FORMAT_BGRX5551:
> + case DRM_FORMAT_BGRA5551:
> + put_unaligned(val ? 0xfffe : 0x0000, (u16 *)dst);
> + break;
> +
> + case DRM_FORMAT_RGB565:
> + case DRM_FORMAT_BGR565:
> + put_unaligned(val ? 0xffff : 0x0000, (u16 *)dst);
> + break;
> +
> + case DRM_FORMAT_RGB888:
> + case DRM_FORMAT_BGR888:
> + dst[0] = val ? 0xff : 0x00;
> + dst[1] = val ? 0xff : 0x00;
> + dst[2] = val ? 0xff : 0x00;
> + break;
> +
> + case DRM_FORMAT_XRGB8888:
> + case DRM_FORMAT_ARGB8888:
> + case DRM_FORMAT_XBGR8888:
> + case DRM_FORMAT_ABGR8888:
> + put_unaligned(val ? 0x00ffffff : 0x00000000, (u32 *)dst);
> + break;
> +
> + case DRM_FORMAT_RGBX8888:
> + case DRM_FORMAT_RGBA8888:
> + case DRM_FORMAT_BGRX8888:
> + case DRM_FORMAT_BGRA8888:
> + put_unaligned(val ? 0xffffff00 : 0x00000000, (u32 *)dst);
> + break;
> +
> + case DRM_FORMAT_XRGB2101010:
> + case DRM_FORMAT_ARGB2101010:
> + case DRM_FORMAT_XBGR2101010:
> + case DRM_FORMAT_ABGR2101010:
> + put_unaligned(val ? 0x3fffffff : 0x00000000, (u32 *)dst);
> + break;
> +
> + case DRM_FORMAT_RGBX1010102:
> + case DRM_FORMAT_RGBA1010102:
> + case DRM_FORMAT_BGRX1010102:
> + case DRM_FORMAT_BGRA1010102:
> + put_unaligned(val ? 0xfffffffc : 0x00000000, (u32 *)dst);
> + break;
> + }
> +}
> +
> +static void drm_panic_render(struct drm_panic_fb *pfb,
> + const char *text, unsigned int len)
> +{
> + const struct font_desc *font = pfb->font;
> + unsigned int pix_depth, pix_bpp, cpp;
> + unsigned int col = pfb->xpos;
> + unsigned int row = pfb->ypos;
> + unsigned int i, h, w;
> + void *dst, *pos;
> + u8 fontline;
> +
> + if ((row + 1) * font->height > pfb->fb->height)
> + return;
> +
> + if ((col + len) * font->width > pfb->fb->width)
> + return;
> +
> + drm_fb_get_bpp_depth(pfb->fb->pixel_format, &pix_depth, &pix_bpp);
> + cpp = DIV_ROUND_UP(pix_bpp, 8);
> +
> + /* TODO: should fb->offsets[0] be added here? */
> + dst = pfb->vmem + (row * font->height * pfb->fb->pitches[0]) +
> + (col * font->width * cpp);
> +
> + for (h = 0; h < font->height; h++) {
> + pos = dst;
> +
> + for (i = 0; i < len; i++) {
> + fontline = *(u8 *)(font->data + text[i] * font->height + h);
> +
> + for (w = 0; w < font->width; w++) {
> + drm_panic_draw_pixel(pos, pfb->fb->pixel_format,
> + fontline & BIT(7 - w));
> + pos += cpp;
> + }
> + }
> +
> + dst += pfb->fb->pitches[0];
> + }
> +}
> +
> +static void drm_panic_scroll_up(struct drm_panic_fb *pfb)
> +{
> + void *src = pfb->vmem + (pfb->font->height * pfb->fb->pitches[0]);
> + size_t len = (pfb->fb->height - pfb->font->height) *
> + pfb->fb->pitches[0];
> +
> + drm_panic_log("%s\n", __func__);
> +
> + memmove(pfb->vmem, src, len);
> + memset(pfb->vmem + len, 0, pfb->font->height * pfb->fb->pitches[0]);
> +}
> +
> +static void drm_panic_clear_screen(struct drm_panic_fb *pfb)
> +{
> + memset(pfb->vmem, 0, pfb->fb->height * pfb->fb->pitches[0]);
> +}
> +
> +static void drm_panic_log_msg(char *pre, const char *str, unsigned int len)
> +{
> + char buf[512];
> +
> + if (len > 510)
> + len = 510;
> +
> + memcpy(buf, str, len);
> + buf[len] = '\n';
> + buf[len + 1] = '\0';
> +
> + drm_panic_log("%s%s", pre, buf);
> +}
> +
> +static void drm_panic_putcs_no_lf(struct drm_panic_fb *pfb,
> + const char *str, unsigned int len)
> +{
> + drm_panic_log("%s(len=%u) x=%u, y=%u\n", __func__, len,
> + pfb->xpos, pfb->ypos);
> +
> + if (len <= 0)
> + return;
> +
> + drm_panic_log_msg("", str, len);
> +
> + drm_panic_render(pfb, str, len);
> +
> +}
> +
> +static void drm_panic_putcs(struct drm_panic_fb *pfb,
> + const char *str, unsigned int num)
> +{
> + unsigned int slen;
> + int len = num;
> + char *lf;
> +
> + drm_panic_log("%s(num=%u)\n", __func__, num);
> +
> + while (len > 0) {
> +
> + if (pfb->ypos == pfb->rows) {
> + pfb->ypos--;
> + drm_panic_scroll_up(pfb);
> + }
> +
> + lf = strpbrk(str, "\n");
> + if (lf)
> + slen = lf - str;
> + else
> + slen = len;
> +
> + if (pfb->xpos + slen > pfb->cols)
> + slen = pfb->cols - pfb->xpos;
> +
> + drm_panic_putcs_no_lf(pfb, str, slen);
> +
> + len -= slen;
> + str += slen;
> + pfb->xpos += slen;
> +
> + if (lf) {
> + str++;
> + len--;
> + pfb->xpos = 0;
> + pfb->ypos++;
> + }
> + }
> +}
> +
> +static void drm_panic_write(const char *str, unsigned int num)
> +{
> + unsigned int i;
> +
> + if (!num)
> + return;
> +
> + drm_panic_log("%s(num=%u)\n", __func__, num);
> +
> + for (i = 0; i < DRM_PANIC_MAX_FBS; i++) {
> + if (!drm_panic_fbs[i].fb)
> + break;
> + drm_panic_putcs(&drm_panic_fbs[i], str, num);
> + }
> +}
> +
> +/* this one is serialized by console_lock() */
> +static void drm_panic_console_write(struct console *con,
> + const char *str, unsigned int num)
> +{
> + unsigned int i;
> +
> + drm_panic_log_msg("->", str, num);
> +
> + /* Buffer up messages to be replayed on panic */
> + if (!drm_panic_active) {
> + for (i = 0; i < num; i++) {
> + drm_panic_kmsgs[drm_panic_kmsgs_pos++] = *str++;
> + if (drm_panic_kmsgs_pos == DRM_PANIC_MAX_KMSGS)
> + drm_panic_kmsgs_pos = 0;
> + }
> + return;
> + }
> +
> + drm_panic_write(str, num);
> +}
> +
> +static struct console drm_panic_console = {
> + .name = "drmpanic",
> + .write = drm_panic_console_write,
> + .flags = CON_PRINTBUFFER | CON_ENABLED,
> + .index = 0,
> +};
> +
> +/*
> + * The panic() function makes sure that only one CPU is allowed to run it's
> + * code. So when this handler is called, we're alone. No racing with
> + * console.write() is possible.
> + */
> +static int drm_panic_handler(struct notifier_block *this, unsigned long ev,
> + void *ptr)
> +{
> + const struct font_desc *font;
> + struct drm_framebuffer *fb;
> + struct drm_panic_fb *pfb;
> + struct drm_minor *minor;
> + unsigned int fbs = 0;
> + void *vmem;
> + int i;
> +
> + drm_panic_log("%s\n", __func__);
> +
> + drm_panic_active = true;
> +
> + drm_minor_for_each(minor, DRM_MINOR_LEGACY, i) {
> + drm_panic_log("Found minor=%d\n", minor->index);
> + if (!minor->dev || !minor->dev->driver ||
> + !minor->dev->driver->panic) {
> + drm_panic_log("Skipping: No panic handler\n");
> + continue;
> + }
> +
> + fb = minor->dev->driver->panic(minor->dev, &vmem);
> + if (!fb) {
> + drm_panic_log("Skipping: Driver returned NULL\n");
> + continue;
> + }
> +
> + if (!fb || !vmem || fb->dev != minor->dev || !fb->pitches[0]) {
> + drm_panic_log("Skipping: Failed check\n");
> + continue;
> + }
> +
> + /* only 8-bit wide fonts are supported */
> + font = get_default_font(fb->width, fb->height, BIT(7), -1);
> + if (!font) {
> + drm_panic_log("Skipping: No font available\n");
> + continue;
> + }
> +
> + pfb = &drm_panic_fbs[fbs++];
> +
> + pfb->fb = fb;
> + pfb->vmem = vmem;
> + pfb->font = font;
> + pfb->cols = fb->width / font->width;
> + pfb->rows = fb->height / font->height;
> +
> + drm_panic_clear_screen(pfb);
> +
> + drm_panic_log(" %ux%u -> %ux%u, %s, %s\n", fb->width,
> + fb->height, pfb->cols, pfb->rows, font->name,
> + drm_get_format_name(fb->pixel_format));
> + }
> +
> + if (drm_panic_kmsgs[0]) {
> + /* safeguard in case we interrupted drm_panic_console_write */
> + if (drm_panic_kmsgs_pos >= DRM_PANIC_MAX_KMSGS)
> + drm_panic_kmsgs_pos = 0;
> +
> + drm_panic_write(&drm_panic_kmsgs[drm_panic_kmsgs_pos],
> + DRM_PANIC_MAX_KMSGS - drm_panic_kmsgs_pos);
> + drm_panic_write(drm_panic_kmsgs, drm_panic_kmsgs_pos);
> + }
> +
> + return NOTIFY_DONE;
> +}
> +
> +static struct notifier_block drm_panic_block = {
> + .notifier_call = drm_panic_handler,
> +};
> +
> +
> +
> +#ifdef CONFIG_DEBUG_FS
> +
> +/* Out of band logging is useful at least in the initial development phase */
> +#define DRM_PANIC_LOG_SIZE SZ_64K
> +#define DRM_PANIC_LOG_LINE 128
> +#define DRM_PANIC_LOG_ENTRIES (DRM_PANIC_LOG_SIZE / DRM_PANIC_LOG_LINE)
> +
> +static char *log_buf;
> +static size_t log_pos;
> +static struct dentry *drm_panic_logfs_root;
> +
> +static void drm_panic_log(const char *fmt, ...)
> +{
> + va_list args;
> + u32 rem_nsec;
> + char *text;
> + size_t len;
> + u64 sec;
> +
> + if (!log_buf || oops_in_progress)
> + return;
> +
> + va_start(args, fmt);
> +
> + if (log_pos >= DRM_PANIC_LOG_ENTRIES)
> + log_pos = 0;
> +
> + text = log_buf + (log_pos++ * DRM_PANIC_LOG_LINE);
> + if (log_pos == DRM_PANIC_LOG_ENTRIES)
> + log_pos = 0;
> +
> + sec = div_u64_rem(local_clock(), 1000000000, &rem_nsec);
> +
> + len = scnprintf(text, DRM_PANIC_LOG_LINE, "[%5llu.%06u] ", sec,
> + rem_nsec / 1000);
> +
> + vscnprintf(text + len, DRM_PANIC_LOG_LINE - len, fmt, args);
> +
> + /* Make sure to always have a newline in case of overflow */
> + if (text[DRM_PANIC_LOG_LINE - 2] != '\0')
> + text[DRM_PANIC_LOG_LINE - 2] = '\n';
> +
> + va_end(args);
> +}
> +
> +static int drm_panic_log_show(struct seq_file *m, void *v)
> +{
> + size_t pos = log_pos;
> + unsigned int i;
> + char *text;
> +
> + for (i = 0; i < DRM_PANIC_LOG_ENTRIES; i++) {
> + text = log_buf + (pos++ * DRM_PANIC_LOG_LINE);
> + if (pos == DRM_PANIC_LOG_ENTRIES)
> + pos = 0;
> + if (*text == '\0')
> + continue;
> + seq_puts(m, text);
> + }
> +
> + return 0;
> +}
> +
> +static int drm_panic_log_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, drm_panic_log_show, NULL);
> +}
> +
> +static const struct file_operations drm_panic_log_ops = {
> + .owner = THIS_MODULE,
> + .open = drm_panic_log_open,
> + .read = seq_read,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> +/*
> + * Fake/simulate panic() at different levels:
> + * 1: only trigger panic handling internally
> + * 2: add local_irq_disable()
> + * 3: add bust_spinlocks();
> + * 100: don't fake it, do call panic()
> + */
> +static int drm_text_fake_panic(unsigned int level)
> +{
> +#ifndef MODULE
> + int old_loglevel = console_loglevel;
> +#endif
> +
> + if (!level && level != 100 && level > 3)
> + return -EINVAL;
> +
> + if (level == 100)
> + panic("TESTING");
> +
> + if (level > 1)
> + local_irq_disable();
> +
> +#ifndef MODULE
> + console_verbose();
> +#endif
> + if (level > 2)
> + bust_spinlocks(1);
> +
> + pr_emerg("Kernel panic - not syncing: FAKING=%u, oops_in_progress=%d\n",
> + level, oops_in_progress);
> +
> +#ifdef CONFIG_DEBUG_BUGVERBOSE
> + dump_stack();
> +#endif
> + /* simulate calling panic_notifier_list */
> + drm_panic_handler(NULL, 0, NULL);
> +
> + if (level > 2)
> + bust_spinlocks(0);
> +
> +#ifndef MODULE
> + console_flush_on_panic();
> +#endif
> + pr_emerg("---[ end Kernel panic - not syncing: FAKING\n");
> +
> + if (level > 1)
> + local_irq_enable();
> +
> +#ifndef MODULE
> + console_loglevel = old_loglevel;
> +#endif
> +
> + return 0;
> +}
> +
> +static ssize_t drm_text_panic_write(struct file *file,
> + const char __user *user_buf,
> + size_t count, loff_t *ppos)
> +{
> + unsigned long long val;
> + ssize_t ret = 0;
> + char buf[24];
> + size_t size;
> +
> + size = min(sizeof(buf) - 1, count);
> + if (copy_from_user(buf, user_buf, size))
> + return -EFAULT;
> +
> + buf[size] = '\0';
> + ret = kstrtoull(buf, 0, &val);
> + if (ret)
> + return ret;
> +
> + ret = drm_text_fake_panic(val);
> +
> + return ret < 0 ? ret : count;
> +}
> +
> +static const struct file_operations drm_text_panic_ops = {
> + .write = drm_text_panic_write,
> + .open = simple_open,
> + .llseek = default_llseek,
> +};
> +
> +static int drm_panic_logfs_init(void)
> +{
> + drm_panic_logfs_root = debugfs_create_dir("drm-panic", NULL);
> + if (!drm_panic_logfs_root)
> + return -ENOMEM;
> +
> + if (!debugfs_create_file("log", S_IRUGO, drm_panic_logfs_root, NULL,
> + &drm_panic_log_ops))
> + goto err_remove;
> +
> + log_buf = kzalloc(DRM_PANIC_LOG_SIZE, GFP_KERNEL);
> + if (!log_buf)
> + goto err_remove;
> +
> + debugfs_create_file("panic", S_IWUSR, drm_panic_logfs_root, NULL,
> + &drm_text_panic_ops);
> +
> + return 0;
> +
> +err_remove:
> + debugfs_remove_recursive(drm_panic_logfs_root);
> +
> + return -ENOMEM;
> +}
> +
> +static void drm_panic_logfs_exit(void)
> +{
> + debugfs_remove_recursive(drm_panic_logfs_root);
> + kfree(log_buf);
> + log_buf = NULL;
> +}
> +
> +#else
> +
> +static int drm_panic_logfs_init(void)
> +{
> +}
> +
> +static void drm_panic_logfs_exit(void)
> +{
> +}
> +
> +static void drm_panic_log(const char *fmt, ...)
> +{
> +}
> +
> +#endif
> +
> +
> +void __init drm_panic_init(void)
> +{
> + drm_panic_kmsgs = kzalloc(DRM_PANIC_MAX_KMSGS, GFP_KERNEL);
> + if (!drm_panic_kmsgs) {
> + DRM_ERROR("Failed to setup panic handler\n");
> + return;
> + }
> +
> + drm_panic_logfs_init();
> +drm_panic_log("%s\n", __func__);
> + register_console(&drm_panic_console);
> + atomic_notifier_chain_register(&panic_notifier_list,
> + &drm_panic_block);
> +}
> +
> +void __exit drm_panic_exit(void)
> +{
> + if (!drm_panic_kmsgs)
> + return;
> +
> + drm_panic_logfs_exit();
> + atomic_notifier_chain_unregister(&panic_notifier_list,
> + &drm_panic_block);
> + unregister_console(&drm_panic_console);
> + kfree(drm_panic_kmsgs);
> +}
> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> index bc7006e..4e84654 100644
> --- a/include/drm/drmP.h
> +++ b/include/drm/drmP.h
> @@ -550,6 +550,28 @@ struct drm_driver {
> bool from_open);
> void (*master_drop)(struct drm_device *dev, struct drm_file *file_priv);
>
> + /**
> + * @panic:
> + *
> + * This function is called on panic() asking for a framebuffer to
> + * display the panic messages on. It also needs the virtual address
> + * of the backing buffer.
> + * This function is optional.
> + *
> + * NOTE:
> + *
> + * This function is called in an atomic notifier chain and it cannot
> + * sleep. Care must be taken so the machine is not killed even harder,
> + * preventing output from going out on serial/netconsole.
> + *
> + * RETURNS:
> + *
> + * Framebuffer that should be used to display the panic messages,
> + * alongside the virtual address of the backing buffer, or NULL if
> + * the driver is unable to provide this.
> + */
> + struct drm_framebuffer *(*panic)(struct drm_device *dev, void **vmem);
> +
> int (*debugfs_init)(struct drm_minor *minor);
> void (*debugfs_cleanup)(struct drm_minor *minor);
>
> --
> 2.8.2
>
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
More information about the dri-devel
mailing list