| From 0a188449ef16824e102a9ce91574e2614e8d885a Mon Sep 17 00:00:00 2001 |
| From: Sean Paul <seanpaul@chromium.org> |
| Date: Tue, 25 Aug 2020 16:59:29 -0400 |
| Subject: [PATCH] CHROMIUM: drm/print: Add tracefs support to the drm logging |
| helpers |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| This patch adds a new module parameter called drm.trace which accepts |
| the same mask as drm.debug. When a debug category is enabled, log |
| messages will be put in a new tracefs instance called drm for |
| consumption. |
| |
| Using the new tracefs instance will allow distros to enable drm logging |
| in production without impacting performance or spamming the system |
| logs. |
| |
| Cc: Daniel Vetter <daniel.vetter@ffwll.ch> |
| Cc: David Airlie <airlied@gmail.com> |
| Cc: Jani Nikula <jani.nikula@linux.intel.com> |
| Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com> |
| Cc: Pekka Paalanen <ppaalanen@gmail.com> |
| Cc: Rob Clark <robdclark@gmail.com> |
| Cc: Steven Rostedt <rostedt@goodmis.org> |
| Cc: Thomas Zimmermann <tzimmermann@suse.de> |
| Cc: Ville Syrjälä <ville.syrjala@linux.intel.com> |
| Cc: Chris Wilson <chris@chris-wilson.co.uk> |
| Cc: Steven Rostedt <rostedt@goodmis.org> |
| Reported-by: kernel test robot <lkp@intel.com> # warning reported in v6 |
| Acked-by: Pekka Paalanen <pekka.paalanen@collabora.com> |
| Signed-off-by: Sean Paul <seanpaul@chromium.org> |
| Link: https://patchwork.freedesktop.org/patch/msgid/20191010204823.195540-1-sean@poorly.run #v1 |
| Link: https://lists.freedesktop.org/archives/dri-devel/2019-November/243230.html #v2 |
| Link: https://patchwork.freedesktop.org/patch/msgid/20191212203301.142437-1-sean@poorly.run #v3 |
| Link: https://patchwork.freedesktop.org/patch/msgid/20200114172155.215463-1-sean@poorly.run #v4 |
| Link: https://patchwork.freedesktop.org/patch/msgid/20200608210505.48519-14-sean@poorly.run #v5 |
| Link: https://patchwork.freedesktop.org/patch/msgid/20200818210510.49730-15-sean@poorly.run #v6 |
| |
| Changes in v5: |
| -Re-write to use trace_array and the tracefs instance support |
| Changes in v6: |
| -Use the new trace_array_init_printk() to initialize global trace |
| buffers |
| Changes in v6.5: |
| -Fix kernel test robot warning |
| -Add a trace printf in __drm_err |
| (am from https://patchwork.freedesktop.org/patch/387808/) |
| (also found at https://lore.kernel.org/r/20200825205936.38823-1-sean@poorly.run) |
| |
| Downstream reason: |
| -Patchset needs more work to resolve dynamic debug conflicts and |
| adoption upstream |
| |
| BUG=b:193917467 |
| TEST=Tested on volteer/zork/trogdor. Build tested on others |
| |
| Change-Id: Ie38c808a7da07c0b45d6ba53d72232b7f7399d0f |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/3260471 |
| Reviewed-by: Drew Davenport <ddavenport@chromium.org> |
| Tested-by: Sean Paul <seanpaul@chromium.org> |
| Commit-Queue: Sean Paul <seanpaul@chromium.org> |
| --- |
| Documentation/gpu/drm-uapi.rst | 6 ++ |
| drivers/gpu/drm/drm_drv.c | 3 + |
| drivers/gpu/drm/drm_print.c | 184 ++++++++++++++++++++++++++++++--- |
| include/drm/drm_print.h | 69 +++++++++++-- |
| 4 files changed, 235 insertions(+), 27 deletions(-) |
| |
| diff --git a/Documentation/gpu/drm-uapi.rst b/Documentation/gpu/drm-uapi.rst |
| index 370d820be24819de57a1b4b2e3ab3ace1c256deb..e75b014a5ed8c35fb252d4f686de4afec2030127 100644 |
| --- a/Documentation/gpu/drm-uapi.rst |
| +++ b/Documentation/gpu/drm-uapi.rst |
| @@ -512,6 +512,12 @@ Debugfs Support |
| .. kernel-doc:: drivers/gpu/drm/drm_debugfs.c |
| :export: |
| |
| +DRM Tracing |
| +--------------- |
| + |
| +.. kernel-doc:: drivers/gpu/drm/drm_print.c |
| + :doc: DRM Tracing |
| + |
| Sysfs Support |
| ============= |
| |
| diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c |
| index 2deadd62f827b4cd0f4455c4051111487a7516f2..1aed8c2db0aa8c0b241aeb350e09b360a6b69b9c 100644 |
| --- a/drivers/gpu/drm/drm_drv.c |
| +++ b/drivers/gpu/drm/drm_drv.c |
| @@ -1076,12 +1076,15 @@ static void drm_core_exit(void) |
| drm_sysfs_destroy(); |
| idr_destroy(&drm_minors_idr); |
| drm_connector_ida_destroy(); |
| + drm_trace_cleanup(); |
| } |
| |
| static int __init drm_core_init(void) |
| { |
| int ret; |
| |
| + drm_trace_init(); |
| + |
| drm_connector_ida_init(); |
| idr_init(&drm_minors_idr); |
| drm_memcpy_init_early(); |
| diff --git a/drivers/gpu/drm/drm_print.c b/drivers/gpu/drm/drm_print.c |
| index 191a1d80c77e23b8b9c3ef5f8829bc2a9b57653a..11bff2e2e0bb2d2c7e8a8ef784b2d39db559dd36 100644 |
| --- a/drivers/gpu/drm/drm_print.c |
| +++ b/drivers/gpu/drm/drm_print.c |
| @@ -30,6 +30,7 @@ |
| #include <linux/seq_file.h> |
| #include <linux/slab.h> |
| #include <linux/stdarg.h> |
| +#include <linux/trace.h> |
| |
| #include <drm/drm.h> |
| #include <drm/drm_drv.h> |
| @@ -42,15 +43,25 @@ |
| unsigned long __drm_debug_syslog; |
| EXPORT_SYMBOL(__drm_debug_syslog); |
| |
| -MODULE_PARM_DESC(debug, "Enable debug output, where each bit enables a debug category.\n" |
| -"\t\tBit 0 (0x01) will enable CORE messages (drm core code)\n" |
| -"\t\tBit 1 (0x02) will enable DRIVER messages (drm controller code)\n" |
| -"\t\tBit 2 (0x04) will enable KMS messages (modesetting code)\n" |
| -"\t\tBit 3 (0x08) will enable PRIME messages (prime code)\n" |
| -"\t\tBit 4 (0x10) will enable ATOMIC messages (atomic code)\n" |
| -"\t\tBit 5 (0x20) will enable VBL messages (vblank code)\n" |
| -"\t\tBit 7 (0x80) will enable LEASE messages (leasing code)\n" |
| -"\t\tBit 8 (0x100) will enable DP messages (displayport code)"); |
| +/* |
| + * __drm_debug_trace: Enable debug output in drm tracing instance. |
| + * Bitmask of DRM_UT_x. See include/drm/drm_print.h for details. |
| + */ |
| +unsigned int __drm_debug_trace; |
| +EXPORT_SYMBOL(__drm_debug_trace); |
| + |
| +#define DEBUG_PARM_DESC(dst) \ |
| +"Enable debug output to " dst ", where each bit enables a debug category.\n" \ |
| +"\t\tBit 0 (0x01) will enable CORE messages (drm core code)\n" \ |
| +"\t\tBit 1 (0x02) will enable DRIVER messages (drm controller code)\n" \ |
| +"\t\tBit 2 (0x04) will enable KMS messages (modesetting code)\n" \ |
| +"\t\tBit 3 (0x08) will enable PRIME messages (prime code)\n" \ |
| +"\t\tBit 4 (0x10) will enable ATOMIC messages (atomic code)\n" \ |
| +"\t\tBit 5 (0x20) will enable VBL messages (vblank code)\n" \ |
| +"\t\tBit 7 (0x80) will enable LEASE messages (leasing code)\n" \ |
| +"\t\tBit 8 (0x100) will enable DP messages (displayport code)" |
| + |
| +MODULE_PARM_DESC(debug, DEBUG_PARM_DESC("syslog")); |
| |
| #if !defined(CONFIG_DRM_USE_DYNAMIC_DEBUG) |
| module_param_named(debug, __drm_debug_syslog, ulong, 0600); |
| @@ -76,6 +87,13 @@ static struct ddebug_class_param drm_debug_bitmap = { |
| module_param_cb(debug, ¶m_ops_dyndbg_classes, &drm_debug_bitmap, 0600); |
| #endif |
| |
| +MODULE_PARM_DESC(trace, DEBUG_PARM_DESC("tracefs")); |
| +module_param_named(trace, __drm_debug_trace, int, 0600); |
| + |
| +#ifdef CONFIG_TRACING |
| +struct trace_array *trace_arr; |
| +#endif |
| + |
| void __drm_puts_coredump(struct drm_printer *p, const char *str) |
| { |
| struct drm_print_iterator *iterator = p->arg; |
| @@ -221,6 +239,20 @@ void __drm_printfn_debug_syslog(struct drm_printer *p, struct va_format *vaf) |
| } |
| EXPORT_SYMBOL(__drm_printfn_debug_syslog); |
| |
| +void __drm_printfn_trace(struct drm_printer *p, struct va_format *vaf) |
| +{ |
| + drm_trace_printf("%s %pV", p->prefix, vaf); |
| +} |
| +EXPORT_SYMBOL(__drm_printfn_trace); |
| + |
| +void __drm_printfn_debug_syslog_and_trace(struct drm_printer *p, |
| + struct va_format *vaf) |
| +{ |
| + pr_debug("%s %pV", p->prefix, vaf); |
| + drm_trace_printf("%s %pV", p->prefix, vaf); |
| +} |
| +EXPORT_SYMBOL(__drm_printfn_debug_syslog_and_trace); |
| + |
| void __drm_printfn_err(struct drm_printer *p, struct va_format *vaf) |
| { |
| struct drm_device *drm = p->arg; |
| @@ -310,6 +342,10 @@ void drm_dev_printk(const struct device *dev, const char *level, |
| vaf.fmt = format; |
| vaf.va = &args; |
| |
| + drm_trace_printf("%s%s[" DRM_NAME ":%ps] %pV", |
| + dev ? dev_name(dev) : "",dev ? " " : "", |
| + __builtin_return_address(0), &vaf); |
| + |
| __drm_dev_vprintk(dev, level, __builtin_return_address(0), NULL, &vaf); |
| |
| va_end(args); |
| @@ -322,17 +358,25 @@ void __drm_dev_dbg(struct _ddebug *desc, const struct device *dev, |
| struct va_format vaf; |
| va_list args; |
| |
| - if (!__drm_debug_enabled(category)) |
| - return; |
| - |
| - /* we know we are printing for either syslog, tracefs, or both */ |
| - va_start(args, format); |
| - vaf.fmt = format; |
| - vaf.va = &args; |
| + if (drm_debug_syslog_enabled(category)) { |
| + va_start(args, format); |
| + vaf.fmt = format; |
| + vaf.va = &args; |
| |
| __drm_dev_vprintk(dev, KERN_DEBUG, __builtin_return_address(0), NULL, &vaf); |
| |
| va_end(args); |
| + } |
| + |
| + if (drm_debug_trace_enabled(category)) { |
| + va_start(args, format); |
| + vaf.fmt = format; |
| + vaf.va = &args; |
| + drm_trace_printf("%s%s[" DRM_NAME ":%ps] %pV", |
| + dev ? dev_name(dev) : "", dev ? " " : "", |
| + __builtin_return_address(0), &vaf); |
| + va_end(args); |
| + } |
| } |
| EXPORT_SYMBOL(__drm_dev_dbg); |
| |
| @@ -348,6 +392,13 @@ void __drm_err(const char *format, ...) |
| __drm_dev_vprintk(NULL, KERN_ERR, __builtin_return_address(0), "*ERROR*", &vaf); |
| |
| va_end(args); |
| + |
| + va_start(args, format); |
| + vaf.fmt = format; |
| + vaf.va = &args; |
| + drm_trace_printf("[" DRM_NAME ":%ps] *ERROR* %pV", |
| + __builtin_return_address(0), &vaf); |
| + va_end(args); |
| } |
| EXPORT_SYMBOL(__drm_err); |
| |
| @@ -378,3 +429,104 @@ void drm_print_regset32(struct drm_printer *p, struct debugfs_regset32 *regset) |
| } |
| } |
| EXPORT_SYMBOL(drm_print_regset32); |
| + |
| + |
| +/** |
| + * DOC: DRM Tracing |
| + * |
| + * *tl;dr* DRM tracing is a lightweight alternative to traditional DRM debug |
| + * logging. |
| + * |
| + * While DRM logging is quite convenient when reproducing a specific issue, it |
| + * doesn't help when something goes wrong unexpectedly. There are a couple |
| + * reasons why one does not want to enable DRM logging at all times: |
| + * |
| + * 1. We don't want to overwhelm syslog with drm spam, others have to use it too |
| + * 2. Console logging is slow |
| + * |
| + * DRM tracing aims to solve both these problems. |
| + * |
| + * To use DRM tracing, set the drm.trace module parameter (via cmdline or sysfs) |
| + * to a DRM debug category mask (this is a bitmask of &drm_debug_category |
| + * values): |
| + * :: |
| + * |
| + * eg: echo 0x106 > /sys/module/drm/parameters/trace |
| + * |
| + * Once active, all log messages in the specified categories will be written to |
| + * the DRM trace. Once at capacity, the trace will overwrite old messages with |
| + * new ones. At any point, one can read the trace file to extract the previous N |
| + * DRM messages: |
| + * :: |
| + * |
| + * eg: cat /sys/kernel/tracing/instances/drm/trace |
| + * |
| + * Considerations |
| + * ************** |
| + * The trace is subsystem wide, so if you have multiple devices active, they |
| + * will be adding logs to the same trace. |
| + * |
| + * The contents of the DRM Trace are **not** considered UABI. **DO NOT depend on |
| + * the values of these traces in your userspace.** These traces are intended for |
| + * entertainment purposes only. The contents of these logs carry no warranty, |
| + * expressed or implied. |
| + */ |
| + |
| + |
| +#ifdef CONFIG_TRACING |
| + |
| +/** |
| + * drm_trace_init - initializes the drm trace array |
| + * |
| + * This function fetches (or creates) the drm trace array. This should be called |
| + * once on drm subsystem creation and matched with drm_trace_cleanup(). |
| + */ |
| +void drm_trace_init(void) |
| +{ |
| + int ret; |
| + |
| + trace_arr = trace_array_get_by_name("drm"); |
| + if (!trace_arr) |
| + return; |
| + |
| + ret = trace_array_init_printk(trace_arr); |
| + if (ret) |
| + drm_trace_cleanup(); |
| +} |
| +EXPORT_SYMBOL(drm_trace_init); |
| + |
| +/** |
| + * drm_trace_printf - adds an entry to the drm tracefs instance |
| + * @format: printf format of the message to add to the trace |
| + * |
| + * This function adds a new entry in the drm tracefs instance |
| + */ |
| +void drm_trace_printf(const char *format, ...) |
| +{ |
| + struct va_format vaf; |
| + va_list args; |
| + |
| + va_start(args, format); |
| + vaf.fmt = format; |
| + vaf.va = &args; |
| + trace_array_printk(trace_arr, _THIS_IP_, "%pV", &vaf); |
| + va_end(args); |
| +} |
| + |
| +/** |
| + * drm_trace_cleanup - destroys the drm trace array |
| + * |
| + * This function destroys the drm trace array created with drm_trace_init. This |
| + * should be called once on drm subsystem close and matched with |
| + * drm_trace_init(). |
| + */ |
| +void drm_trace_cleanup(void) |
| +{ |
| + if (trace_arr) { |
| + trace_array_put(trace_arr); |
| + trace_array_destroy(trace_arr); |
| + trace_arr = NULL; |
| + } |
| +} |
| +EXPORT_SYMBOL(drm_trace_cleanup); |
| +#endif |
| diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h |
| index 84da88790a775f749955e4d9af0acbc51ac601f7..7d7f3452714144ffaca547036e028daef9c017d7 100644 |
| --- a/include/drm/drm_print.h |
| +++ b/include/drm/drm_print.h |
| @@ -39,12 +39,13 @@ struct seq_file; |
| |
| /* Do *not* use outside of drm_print.[ch]! */ |
| extern unsigned long __drm_debug_syslog; |
| +extern unsigned int __drm_debug_trace; |
| |
| /** |
| * DOC: print |
| * |
| * A simple wrapper for dev_printk(), seq_printf(), etc. Allows same |
| - * debug code to be used for both debugfs and printk logging. |
| + * debug code to be used for debugfs, printk and tracefs logging. |
| * |
| * For example:: |
| * |
| @@ -73,14 +74,14 @@ extern unsigned long __drm_debug_syslog; |
| * enum drm_debug_category - The DRM debug categories |
| * |
| * Each of the DRM debug logging macros use a specific category, and the logging |
| - * is filtered by the drm.debug module parameter. This enum specifies the values |
| - * for the interface. |
| + * is filtered by the drm.debug and drm.trace module parameter. This enum |
| + * specifies the values for the interface. |
| * |
| * Each DRM_DEBUG_<CATEGORY> macro logs to DRM_UT_<CATEGORY> category, except |
| * DRM_DEBUG() logs to DRM_UT_CORE. |
| * |
| - * Enabling verbose debug messages is done through the drm.debug parameter, each |
| - * category being enabled by a bit: |
| + * Enabling verbose debug messages is done through the drm.debug and drm.trace |
| + * parameters, each category being enabled by a bit: |
| * |
| * - drm.debug=0x1 will enable CORE messages |
| * - drm.debug=0x2 will enable DRIVER messages |
| @@ -89,10 +90,14 @@ extern unsigned long __drm_debug_syslog; |
| * - drm.debug=0x1ff will enable all messages |
| * |
| * An interesting feature is that it's possible to enable verbose logging at |
| - * run-time by echoing the debug value in its sysfs node:: |
| + * run-time by echoing the debug category value in its sysfs node:: |
| * |
| + * # For syslog logging: |
| * # echo 0xf > /sys/module/drm/parameters/debug |
| * |
| + * # For tracefs logging: |
| + * # echo 0xf > /sys/module/drm/parameters/trace |
| + * |
| */ |
| enum drm_debug_category { |
| /* These names must match those in DYNAMIC_DEBUG_CLASSBITS */ |
| @@ -145,9 +150,21 @@ static inline bool drm_debug_syslog_enabled(enum drm_debug_category category) |
| return unlikely(__drm_debug_syslog & category); |
| } |
| |
| +static inline bool drm_debug_trace_enabled(enum drm_debug_category category) |
| +{ |
| + return unlikely(__drm_debug_trace & category); |
| +} |
| + |
| +static inline bool drm_debug_enabled(enum drm_debug_category category) |
| +{ |
| + return drm_debug_syslog_enabled(category) || |
| + drm_debug_trace_enabled(category); |
| +} |
| + |
| static inline bool drm_debug_enabled_raw(enum drm_debug_category category) |
| { |
| - return drm_debug_syslog_enabled(category); |
| + return drm_debug_syslog_enabled(category) || |
| + drm_debug_trace_enabled(category); |
| } |
| |
| #define drm_debug_enabled_instrumented(category) \ |
| @@ -191,6 +208,9 @@ void __drm_printfn_seq_file(struct drm_printer *p, struct va_format *vaf); |
| void __drm_puts_seq_file(struct drm_printer *p, const char *str); |
| void __drm_printfn_info(struct drm_printer *p, struct va_format *vaf); |
| void __drm_printfn_debug_syslog(struct drm_printer *p, struct va_format *vaf); |
| +void __drm_printfn_trace(struct drm_printer *p, struct va_format *vaf); |
| +void __drm_printfn_debug_syslog_and_trace(struct drm_printer *p, |
| + struct va_format *vaf); |
| void __drm_printfn_err(struct drm_printer *p, struct va_format *vaf); |
| void __drm_printfn_noop(struct drm_printer *p, struct va_format *vaf); |
| |
| @@ -324,7 +344,8 @@ static inline struct drm_printer drm_info_printer(struct device *dev) |
| } |
| |
| /** |
| - * drm_dbg_printer - construct a &drm_printer for drm device specific output |
| + * drm_debug_printer - construct a &drm_printer that outputs to pr_debug() and |
| + * drm tracefs |
| * @drm: the &struct drm_device pointer, or NULL |
| * @category: the debug category to use |
| * @prefix: debug output prefix, or NULL for no prefix |
| @@ -337,7 +358,7 @@ static inline struct drm_printer drm_dbg_printer(struct drm_device *drm, |
| const char *prefix) |
| { |
| struct drm_printer p = { |
| - .printfn = __drm_printfn_debug_syslog, |
| + .printfn = __drm_printfn_debug_syslog_and_trace, |
| .arg = drm, |
| .origin = (const void *)_THIS_IP_, /* it's fine as we will be inlined */ |
| .prefix = prefix, |
| @@ -367,7 +388,7 @@ static inline struct drm_printer drm_err_printer(struct drm_device *drm, |
| |
| /** |
| * drm_debug_category_printer - construct a &drm_printer that outputs to |
| - * pr_debug() if enabled for the given category. |
| + * pr_debug() and/or the drm tracefs instance if enabled for the given category. |
| * @category: the DRM_UT_* message category this message belongs to |
| * @prefix: trace output prefix |
| * |
| @@ -382,8 +403,13 @@ drm_debug_category_printer(enum drm_debug_category category, |
| .prefix = prefix |
| }; |
| |
| - if (drm_debug_syslog_enabled(category)) { |
| + if (drm_debug_syslog_enabled(category) && |
| + drm_debug_trace_enabled(category)) { |
| + p.printfn = __drm_printfn_debug_syslog_and_trace; |
| + } else if (drm_debug_syslog_enabled(category)) { |
| p.printfn = __drm_printfn_debug_syslog; |
| + } else if (drm_debug_trace_enabled(category)) { |
| + p.printfn = __drm_printfn_trace; |
| } else { |
| WARN(1, "Debug category %d is inactive.", category); |
| p.printfn = __drm_printfn_noop; |
| @@ -392,6 +418,27 @@ drm_debug_category_printer(enum drm_debug_category category, |
| return p; |
| } |
| |
| + |
| +#ifdef CONFIG_TRACING |
| +void drm_trace_init(void); |
| +__printf(1, 2) |
| +void drm_trace_printf(const char *format, ...); |
| +void drm_trace_cleanup(void); |
| +#else |
| +static inline void drm_trace_init(void) |
| +{ |
| +} |
| + |
| +__printf(1, 2) |
| +static inline void drm_trace_printf(const char *format, ...) |
| +{ |
| +} |
| + |
| +static inline void drm_trace_cleanup(void) |
| +{ |
| +} |
| +#endif |
| + |
| /* |
| * struct device based logging |
| * |
| -- |
| 2.46.0.rc2.264.g509ed76dc8-goog |
| |