[RFC 2/3] drm: Add panic handling

Daniel Vetter daniel at ffwll.ch
Wed Aug 10 09:18:59 UTC 2016


On Wed, Aug 10, 2016 at 11:15:29AM +0200, Daniel Vetter wrote:
> 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?

One more: This is quite a bit of work to get going, I estimate that it's
good enough to fill a full gsoc internship, so about 2-3 months of work,
but for someone unexperienced, unlike you ;-)
-Daniel

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

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


More information about the dri-devel mailing list