Den 11.09.2016 20:47, skrev Noralf Trønnes:
This adds support for outputting kernel messages on panic(). A circular buffer is used to collect kernel messages. On panic() the notifier function loops over each DRM device and it's crtc's to find suitable framebuffers. On the next console->write(), the messages in the circular buffer are rendered on each of the recorded framebuffers.
Only atomic drivers are supported.
Signed-off-by: Noralf Trønnes noralf@tronnes.org
[...]
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
[...]
+static bool drm_panic_add_fb(struct drm_crtc *crtc, struct drm_plane *plane) +{
- unsigned int width = drm_rect_width(&plane->state->src) >> 16;
- unsigned int height = drm_rect_height(&plane->state->src) >> 16;
I just tried this on vc4 (which uses fb_cma_helper) and width/height is zero. Additionally plane->state->visible is false. Maybe something the driver should set?
If I work around that (use fb->width/height), then I get panic output on vc4 also in addition to simpledrm.
Noralf.
- struct drm_framebuffer *fb = plane->fb;
- const struct font_desc *font;
- struct drm_panic_fb *pfb;
- void *vmap;
- if (!width || !height || !fb || !fb->funcs || !fb->funcs->panic_vmap)
return false;
- /* only 8-bit wide fonts are supported */
- font = get_default_font(width, height, BIT(7), -1);
- if (!font) {
drm_panic_log("Couldn't get font\n");
return false;
- }
- vmap = fb->funcs->panic_vmap(fb);
- if (!vmap) {
drm_panic_log("panic_vmap() returned NULL\n");
return false;
- }
- pfb = &drm_panic_fbs[drm_panic_fbs_num++];
- pfb->plane = plane;
- pfb->crtc = crtc;
- pfb->fb = fb;
- pfb->width = width;
- pfb->height = height;
- pfb->vmap = vmap;
- pfb->font = font;
- pfb->cols = pfb->width / font->width;
- pfb->rows = pfb->height / font->height;
- drm_panic_log(" [FB:%d] %ux%u->%ux%u, %s, format=0x%08x\n",
fb->base.id, pfb->width, pfb->height, pfb->cols,
pfb->rows, font->name, fb->pixel_format);
- return true;
+}
+static void drm_panic_add(struct drm_device *drm) +{
- struct drm_plane *plane;
- struct drm_crtc *crtc;
- if (!drm || !drm->driver ||
!(drm->driver->driver_features & DRIVER_ATOMIC))
return;
- drm_panic_log("%s on minor %d\n", drm->driver->name,
drm->primary ? drm->primary->index : -1);
- drm_for_each_crtc(crtc, drm) {
drm_panic_log(" %s\n", crtc->name);
if (drm_panic_fbs_num >= DRM_PANIC_MAX_FBS)
return;
if (!ww_mutex_trylock(&crtc->mutex.mutex))
continue;
if (!crtc->enabled || !crtc->primary)
goto crtc_unlock;
if (!crtc->state || !crtc->state->active)
goto crtc_unlock;
plane = crtc->primary;
if (!ww_mutex_trylock(&plane->mutex.mutex))
goto crtc_unlock;
if (!plane->state || !plane->state->visible)
goto plane_unlock;
if (drm_panic_add_fb(crtc, plane))
continue;
+plane_unlock:
ww_mutex_unlock(&plane->mutex.mutex);
+crtc_unlock:
ww_mutex_unlock(&crtc->mutex.mutex);
- }
+}
+static int drm_panic_class_iter(struct device *dev, void *data) +{
- struct drm_minor *minor;
- minor = dev_get_drvdata(dev);
- if (minor && minor->type == DRM_MINOR_PRIMARY)
drm_panic_add(minor->dev);
- return 0;
+}
+/*
- The panic() function makes sure that only one CPU is allowed to run it's
- code, but a new panic can be triggered during it's processing.
- Prior to calling the panic handlers, panic() calls smp_send_stop(). If
- that went well, there's only one CPU running, but this is no guarantee.
- */
+static int drm_panic_handler(struct notifier_block *this, unsigned long ev,
void *ptr)
+{
- drm_panic_log("%s\n", __func__);
- /*
* TODO
* Maybe we need better protection here against reentrance in case
* panic_vmap() triggered a new panic.
*/
- /* Nested panic */
- if (drm_panic_fbs_num)
return NOTIFY_DONE;
- class_for_each_device(drm_class, NULL, NULL, drm_panic_class_iter);
- if (drm_panic_fbs_num)
drm_panic_active = true;
- return NOTIFY_DONE;
+}
+static struct notifier_block drm_panic_block = {
- .notifier_call = drm_panic_handler,
+};
+static void drm_panic_test(void) +{
- /* simulate calling panic_notifier_list */
- drm_panic_handler(NULL, 0, NULL);
+}
+static void drm_panic_test_cleanup(void) +{
- struct drm_panic_fb *pfb;
- unsigned int i;
- drm_panic_active = false;
- for (i = 0; i < drm_panic_fbs_num; i++) {
pfb = &drm_panic_fbs[i];
if (pfb->fb->funcs->panic_vunmap)
pfb->fb->funcs->panic_vunmap(pfb->fb, pfb->vmap);
ww_mutex_unlock(&pfb->plane->mutex.mutex);
ww_mutex_unlock(&pfb->crtc->mutex.mutex);
- }
- drm_panic_fbs_num = 0;
- memset(drm_panic_fbs, 0, DRM_PANIC_MAX_FBS * sizeof(*drm_panic_fbs));
+}
+/*
- Partial replication of panic() for testing purposes. Some symbols are
- only available when builtin (they're not exported).
- */
+static void drm_panic_fake_panic(unsigned int level) +{ +#ifndef MODULE
- int old_loglevel = console_loglevel;
- if (level > 1)
local_irq_disable();
- console_verbose();
- if (level > 2)
bust_spinlocks(1);
- pr_emerg("Kernel panic - not syncing: FAKING=%u, oops_in_progress=%d\n",
level, oops_in_progress);
- dump_stack();
- drm_panic_test();
- if (level > 2)
bust_spinlocks(0);
- console_flush_on_panic();
- pr_emerg("---[ end Kernel panic - not syncing: FAKING\n");
- if (level > 1)
local_irq_enable();
- console_loglevel = old_loglevel;
+#else /* MODULE */
- if (level > 1)
local_irq_disable();
- pr_emerg("Kernel panic - not syncing: FAKING=%u\n", level);
- dump_stack();
- drm_panic_test();
- pr_emerg("---[ end Kernel panic - not syncing: FAKING\n");
- if (level > 1)
local_irq_enable();
+#endif /* MODULE */
- drm_panic_test_cleanup();
+}
+static void drm_panic_clear_kmsgs(void) +{
- console_lock();
- memset(drm_panic_kmsgs, 0, DRM_PANIC_MAX_KMSGS);
- drm_panic_kmsgs_pos = 0;
- console_unlock();
+}
+/*
- Fake/simulate panic() at different levels:
- 1: only trigger panic handling internally
- 2: add local_irq_disable()
- 3: add bust_spinlocks();
- Test rendering code:
- 100: clear kmsgs buffer
- 101: call panic handler for testing
- 102: cleanup after testing
- The real deal:
- 200: don't fake it, do call panic()
- */
+static ssize_t drm_panic_file_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;
- if (val && val < 4)
drm_panic_fake_panic(val);
- else if (val == 100)
drm_panic_clear_kmsgs();
- else if (val == 101)
drm_panic_test();
- else if (val == 102)
drm_panic_test_cleanup();
- else if (val == 200)
panic("TESTING");
- else
return -EINVAL;
- return count;
+}
+static const struct file_operations drm_panic_panic_ops = {
- .write = drm_panic_file_panic_write,
- .open = simple_open,
- .llseek = default_llseek,
+};
+static int drm_panic_log_show(struct seq_file *m, void *v) +{
- size_t pos = log_pos;
- if (log_buf[0] == '\0')
return 0;
- if (!pos) {
seq_write(m, log_buf, DRM_PANIC_LOG_SIZE);
- } else if (log_buf[pos] == '\0') {
seq_write(m, log_buf, pos);
- } else {
seq_write(m, log_buf + pos, DRM_PANIC_LOG_SIZE - pos);
seq_write(m, log_buf, pos);
- }
- 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,
+};
+static struct dentry *drm_panic_d_panic; +static struct dentry *drm_panic_d_log;
+void __init drm_panic_init(struct dentry *debugfs_root) +{
- 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_d_panic = debugfs_create_file("panic-test", 0200,
debugfs_root, NULL,
&drm_panic_panic_ops);
- drm_panic_d_log = debugfs_create_file("panic-log", 0444, debugfs_root,
NULL, &drm_panic_log_ops);
- if (!IS_ERR_OR_NULL(drm_panic_d_log))
log_buf = kzalloc(DRM_PANIC_LOG_SIZE, GFP_KERNEL);
- register_console(&drm_panic_console);
- atomic_notifier_chain_register(&panic_notifier_list, &drm_panic_block);
+}
+void __exit drm_panic_exit(struct dentry *debugfs_root) +{
- if (!drm_panic_kmsgs)
return;
- debugfs_remove(drm_panic_d_log);
- debugfs_remove(drm_panic_d_panic);
- kfree(log_buf);
- log_buf = NULL;
- atomic_notifier_chain_unregister(&panic_notifier_list,
&drm_panic_block);
- unregister_console(&drm_panic_console);
- kfree(drm_panic_kmsgs);
+} diff --git a/include/drm/drm_framebuffer.h b/include/drm/drm_framebuffer.h index 50deb40..33f4022 100644 --- a/include/drm/drm_framebuffer.h +++ b/include/drm/drm_framebuffer.h @@ -90,6 +90,44 @@ struct drm_framebuffer_funcs { struct drm_file *file_priv, unsigned flags, unsigned color, struct drm_clip_rect *clips, unsigned num_clips);
/**
* @panic_vmap:
*
* Optional callback for panic handling.
*
* For vmapping the selected framebuffer in a panic context. Must
* be super careful about locking (only trylocking allowed).
*
* RETURNS:
*
* NULL if it didn't work out, otherwise 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);
/**
* @panic_vunmap:
*
* Optional callback for cleaning up after panic testing.
*
* Crtc and plane locks are released after this callback has run.
* vmap is the cookie returned by @panic_vmap.
*/
void (*panic_vunmap)(struct drm_framebuffer *fb, void *vmap);
/**
* @panic_draw_xy:
*
* Optional callback for drawing pixels during panic.
*
* For drawing pixels onto a framebuffer prepared with @panic_vmap.
* vmap is the cookie returned by @panic_vmap.
* If it's not set, drm_framebuffer_panic_draw_xy() is used.
*/
void (*panic_draw_xy)(struct drm_framebuffer *fb, void *vmap,
int x, int y, bool foreground);
};
/**
@@ -214,6 +252,8 @@ struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, void drm_framebuffer_remove(struct drm_framebuffer *fb); void drm_framebuffer_cleanup(struct drm_framebuffer *fb); void drm_framebuffer_unregister_private(struct drm_framebuffer *fb); +void drm_framebuffer_panic_draw_xy(struct drm_framebuffer *fb, void *vmap,
int x, int y, bool foreground);
/**
- drm_framebuffer_reference - incr the fb refcnt