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@xxxxxxxxxxx> > > 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@xxxxxxxxxxxxxxxxxxxxx > > 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 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel