From patchwork Thu Jul 28 14:15:06 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Noralf_Tr=C3=B8nnes?= X-Patchwork-Id: 9251171 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id F0770607D8 for ; Thu, 28 Jul 2016 14:21:21 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E1B61212BE for ; Thu, 28 Jul 2016 14:21:21 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D655127813; Thu, 28 Jul 2016 14:21:21 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 46AB7212BE for ; Thu, 28 Jul 2016 14:21:21 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 1BB696E82E; Thu, 28 Jul 2016 14:21:17 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from smtp.domeneshop.no (smtp.domeneshop.no [IPv6:2a01:5b40:0:3005::1]) by gabe.freedesktop.org (Postfix) with ESMTPS id D4AAA6E82E for ; Thu, 28 Jul 2016 14:21:12 +0000 (UTC) Received: from 211.81-166-168.customer.lyse.net ([81.166.168.211]:56115 helo=localhost.localdomain) by smtp.domeneshop.no with esmtpsa (TLS1.1:DHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.80) (envelope-from ) id 1bSm6K-0007dR-1Y; Thu, 28 Jul 2016 16:15:52 +0200 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= To: dri-devel@lists.freedesktop.org Subject: [RFC 2/3] drm/text: Add panic and boot console support Date: Thu, 28 Jul 2016 16:15:06 +0200 Message-Id: <1469715307-32708-3-git-send-email-noralf@tronnes.org> X-Mailer: git-send-email 2.8.2 In-Reply-To: <1469715307-32708-1-git-send-email-noralf@tronnes.org> References: <1469715307-32708-1-git-send-email-noralf@tronnes.org> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP This adds support for panic and boot console. Signed-off-by: Noralf Trønnes --- drivers/gpu/drm/drm-text/Makefile | 2 +- drivers/gpu/drm/drm-text/drm-text-buffer.c | 4 +- drivers/gpu/drm/drm-text/drm-text-console.c | 205 ++++++++++++++++++++++++++++ drivers/gpu/drm/drm-text/drm-text-debugfs.c | 89 ++++++++++++ drivers/gpu/drm/drm-text/drm-text.h | 4 + 5 files changed, 302 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/drm-text/drm-text-console.c diff --git a/drivers/gpu/drm/drm-text/Makefile b/drivers/gpu/drm/drm-text/Makefile index 48f55bc..be042b0 100644 --- a/drivers/gpu/drm/drm-text/Makefile +++ b/drivers/gpu/drm/drm-text/Makefile @@ -1,4 +1,4 @@ -drm-text-y := drm-text-buffer.o +drm-text-y := drm-text-console.o drm-text-buffer.o drm-text-$(CONFIG_DEBUG_FS) += drm-text-debugfs.o obj-m += drm-text.o diff --git a/drivers/gpu/drm/drm-text/drm-text-buffer.c b/drivers/gpu/drm/drm-text/drm-text-buffer.c index 187dd4b..91beb48 100644 --- a/drivers/gpu/drm/drm-text/drm-text-buffer.c +++ b/drivers/gpu/drm/drm-text/drm-text-buffer.c @@ -308,7 +308,7 @@ static void drm_text_free(struct drm_text_buffer *text) static int __init drm_text_init(void) { - int ret = 0; + int ret; ret = drm_text_debugfs_init(); if (ret) @@ -316,6 +316,7 @@ static int __init drm_text_init(void) drm_text_scan_fbdev(); + ret = drm_text_console_init(); return ret; } @@ -325,6 +326,7 @@ static void __exit drm_text_exit(void) { unsigned int i; + drm_text_console_exit(); drm_text_debugfs_exit(); for (i = 0; i < MAX_DRM_TEXT_BUFFERS; i++) diff --git a/drivers/gpu/drm/drm-text/drm-text-console.c b/drivers/gpu/drm/drm-text/drm-text-console.c new file mode 100644 index 0000000..e7ef244 --- /dev/null +++ b/drivers/gpu/drm/drm-text/drm-text-console.c @@ -0,0 +1,205 @@ +#define DEBUG +/* + * 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 +#include + +#include "drm-text.h" + +static bool drm_text_console_panic; + +/** + * DOC: message/boot console + * + * The kernel message &console is enabled by using console=drm[n] where n is + * the primary minor number. Only one console is supported. + */ + +static void drm_text_console_write(struct console *con, const char *str, + unsigned int num) +{ + struct drm_text_buffer *text; + + drm_text_debug("%s(num=%u)\n", __func__, num); + if (drm_text_console_panic) + return; + + text = drm_text_get(con->index); + if (!text) + return; + + drm_text_write(text, str, num); + drm_text_flush(text, false); +} + +static int drm_text_console_setup(struct console *con, char *options) +{ + struct drm_text_buffer *text; + + drm_text_debug("%s[%u](%s) data=%p\n", __func__, con->index, options, con->data); + + text = drm_text_get(con->index); + if (text) + drm_text_enable(text); + + return 0; +} + +/* TODO: test as a boot console */ +static struct console drm_text_console = { + .name = "drm", + .write = drm_text_console_write, + .setup = drm_text_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +/** + * DOC: panic console + * + * The panic &console is always enabled and collects kernel messages in a + * buffer as they come in. + * When the kernel does panic(), a panic notifier enables all text buffers. + * On the next &console->write it replays the message buffer and starts + * writing to all text buffers. Flushing of the text buffer to the pixel + * buffer is done inline instead of using the worker. + */ + +#define DRM_TEXT_PANIC_KMSGS_MAX SZ_1K + +struct drm_text_panic_console { + char kmsgs[DRM_TEXT_PANIC_KMSGS_MAX]; + size_t kmsg_pos; +}; + +static void drm_text_panic_write(const char *str, unsigned int num) +{ + unsigned int i; + + drm_text_debug("%s(num=%u)\n", __func__, num); + + for (i = 0; i < MAX_DRM_TEXT_BUFFERS; i++) + if (drm_text_buffers[i]) + drm_text_write(drm_text_buffers[i], str, num); +} + +static void drm_text_panic_flush(void) +{ + unsigned int i; + + drm_text_debug("%s()\n", __func__); + + for (i = 0; i < MAX_DRM_TEXT_BUFFERS; i++) + if (drm_text_buffers[i]) + drm_text_flush(drm_text_buffers[i], true); +} + +static void drm_text_panic_console_write(struct console *con, const char *str, + unsigned int num) +{ + struct drm_text_panic_console *pcon = con->data; + unsigned int i; + + drm_text_debug("%s(num=%u)\n", __func__, num); + + if (!pcon) + return; + + /* Buffer up messages to be replayed on panic */ + if (!drm_text_console_panic) { + for (i = 0; i < num; i++) { + pcon->kmsgs[pcon->kmsg_pos++] = *str++; + if (pcon->kmsg_pos == DRM_TEXT_PANIC_KMSGS_MAX) + pcon->kmsg_pos = 0; + } + return; + } + + if (pcon->kmsgs[0]) { + /* replay messages, the first one might be partial */ + if (pcon->kmsgs[pcon->kmsg_pos]) { /* buffer wrap around */ + drm_text_panic_write(&pcon->kmsgs[pcon->kmsg_pos], + DRM_TEXT_PANIC_KMSGS_MAX - pcon->kmsg_pos); + drm_text_panic_write(pcon->kmsgs, pcon->kmsg_pos); + } else { + drm_text_panic_write(pcon->kmsgs, pcon->kmsg_pos); + } + pcon->kmsgs[0] = '\0'; + } + + drm_text_panic_write(str, num); + drm_text_panic_flush(); +} + +static struct console drm_text_panic_console = { + .name = "drmpanic", + .write = drm_text_panic_console_write, + .flags = CON_PRINTBUFFER | CON_ENABLED, + .index = 0, +}; + +static int drm_text_panic_console_setup(struct console *con) +{ + struct drm_text_panic_console *pcon; + + drm_text_debug("%s[%u]() data=%p\n", __func__, con->index, con->data); + + pcon = kzalloc(sizeof(*pcon), GFP_KERNEL); + if (!pcon) + return -ENOMEM; + + con->data = pcon; + + return 0; +} + +int drm_text_panic(struct notifier_block *this, unsigned long ev, void *ptr) +{ + unsigned int i; + + drm_text_console_panic = true; + for (i = 0; i < MAX_DRM_TEXT_BUFFERS; i++) + if (drm_text_buffers[i]) + drm_text_enable(drm_text_buffers[i]); + + return NOTIFY_DONE; +} + +static struct notifier_block drm_text_panic_block = { + .notifier_call = drm_text_panic, +}; + +int drm_text_console_init(void) +{ + drm_text_log("%s\n", __func__); + + register_console(&drm_text_panic_console); + drm_text_panic_console_setup(&drm_text_panic_console); + atomic_notifier_chain_register(&panic_notifier_list, + &drm_text_panic_block); + drm_text_debug("drm_text_panic_console: index=%d, flags=0x%x\n", + drm_text_panic_console.index, drm_text_panic_console.flags); + + register_console(&drm_text_console); + drm_text_debug("drm_text_console: index=%d, flags=0x%x\n", + drm_text_console.index, drm_text_console.flags); + + return 0; +} + +void drm_text_console_exit(void) +{ + atomic_notifier_chain_unregister(&panic_notifier_list, + &drm_text_panic_block); + unregister_console(&drm_text_panic_console); + kfree(drm_text_panic_console.data); + + unregister_console(&drm_text_console); +} diff --git a/drivers/gpu/drm/drm-text/drm-text-debugfs.c b/drivers/gpu/drm/drm-text/drm-text-debugfs.c index d01f995..542ccac 100644 --- a/drivers/gpu/drm/drm-text/drm-text-debugfs.c +++ b/drivers/gpu/drm/drm-text/drm-text-debugfs.c @@ -173,6 +173,93 @@ static const struct file_operations drm_text_flush_ops = { .llseek = default_llseek, }; +/* + * Fake/simulate panic() at different levels: + * 1: only trigger panic handling internally + * 2: local_irq_disable() + * 3: bust_spinlocks(); + * 100: don't fake it, do call panic() + */ +static int drm_text_fake_panic(unsigned int level) +{ +/* The console_* symbols are not exported */ +// int old_loglevel = console_loglevel; + + if (!level && level != 100 && level > 3) + return -EINVAL; + + if (level == 100) + panic("TESTING"); + + if (level > 1) + local_irq_disable(); + +// console_verbose(); + + if (level > 2) + bust_spinlocks(1); + + pr_emerg("Kernel panic - not syncing: TESTING\n"); + +#ifdef CONFIG_DEBUG_BUGVERBOSE + dump_stack(); +#endif + + /* simulate calling panic_notifier_list */ + drm_text_panic(NULL, 0 , NULL); + + if (level > 2) + bust_spinlocks(0); + +// console_flush_on_panic(); + + pr_emerg("---[ end Kernel panic - not syncing: TESTING\n"); + + if (level > 1) + local_irq_enable(); + +// console_loglevel = old_loglevel; + +#ifdef HACK_NEED_FLUSHING +{ + struct drm_text_buffer *text = drm_text_get(0); + + if (text && text->fb->funcs->dirty) + text->fb->funcs->dirty(text->fb, NULL, 0, 0, NULL, 0); +} +#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; + char buf[24]; + ssize_t ret = 0; + 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(1); + + 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, +}; + int drm_text_debugfs_init(void) { drm_text_debugfs_root = debugfs_create_dir("drm-text", NULL); @@ -189,6 +276,8 @@ int drm_text_debugfs_init(void) debugfs_create_file("flush", S_IWUSR, drm_text_debugfs_root, NULL, &drm_text_flush_ops); + debugfs_create_file("panic", S_IWUSR, drm_text_debugfs_root, NULL, + &drm_text_panic_ops); return 0; diff --git a/drivers/gpu/drm/drm-text/drm-text.h b/drivers/gpu/drm/drm-text/drm-text.h index 77e7429..43ba76c 100644 --- a/drivers/gpu/drm/drm-text/drm-text.h +++ b/drivers/gpu/drm/drm-text/drm-text.h @@ -39,6 +39,10 @@ int drm_text_enable(struct drm_text_buffer *text); int drm_text_disable(struct drm_text_buffer *text); struct drm_text_buffer *drm_text_get(unsigned int index); +int drm_text_console_init(void); +void drm_text_console_exit(void); +int drm_text_panic(struct notifier_block *this, unsigned long ev, void *ptr); + #ifdef DEBUG #define drm_text_debug(fmt, ...) \ drm_text_log(fmt, ##__VA_ARGS__)