+
+MODULE_PARM_DESC(debug, DEBUG_PARM_DESC("syslog"));
module_param_named(debug, __drm_debug_syslog, int, 0600);
+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;
@@ -166,6 +184,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)
{
pr_err("*ERROR* %s %pV", p->prefix, vaf);
@@ -246,6 +278,14 @@ void drm_dev_printk(const struct device *dev,
const char *level,
struct va_format vaf;
va_list args;
+ 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);
+
va_start(args, format);
vaf.fmt = format;
vaf.va = &args;
@@ -267,21 +307,30 @@ void drm_dev_dbg(const struct device *dev, enum
drm_debug_category category,
struct va_format vaf;
va_list args;
- if (!drm_debug_enabled(category))
- return;
+ if (drm_debug_syslog_enabled(category)) {
+ va_start(args, format);
+ vaf.fmt = format;
+ vaf.va = &args;
- va_start(args, format);
- vaf.fmt = format;
- vaf.va = &args;
+ if (dev)
+ dev_printk(KERN_DEBUG, dev, "[" DRM_NAME ":%ps] %pV",
+ __builtin_return_address(0), &vaf);
+ else
+ printk(KERN_DEBUG "[" DRM_NAME ":%ps] %pV",
+ __builtin_return_address(0), &vaf);
- if (dev)
- dev_printk(KERN_DEBUG, dev, "[" DRM_NAME ":%ps] %pV",
- __builtin_return_address(0), &vaf);
- else
- printk(KERN_DEBUG "[" DRM_NAME ":%ps] %pV",
- __builtin_return_address(0), &vaf);
+ va_end(args);
+ }
- 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);
@@ -290,17 +339,25 @@ void __drm_dbg(enum drm_debug_category category,
const char *format, ...)
struct va_format vaf;
va_list args;
- if (!drm_debug_enabled(category))
- return;
+ if (drm_debug_syslog_enabled(category)) {
+ va_start(args, format);
+ vaf.fmt = format;
+ vaf.va = &args;
- va_start(args, format);
- vaf.fmt = format;
- vaf.va = &args;
+ printk(KERN_DEBUG "[" DRM_NAME ":%ps] %pV",
+ __builtin_return_address(0), &vaf);
- printk(KERN_DEBUG "[" DRM_NAME ":%ps] %pV",
- __builtin_return_address(0), &vaf);
+ va_end(args);
+ }
- va_end(args);
+ if (drm_debug_trace_enabled(category)) {
+ va_start(args, format);
+ vaf.fmt = format;
+ vaf.va = &args;
+ drm_trace_printf("[" DRM_NAME ":%ps] %pV",
+ __builtin_return_address(0), &vaf);
+ va_end(args);
+ }
}
EXPORT_SYMBOL(__drm_dbg);
@@ -317,6 +374,13 @@ void __drm_err(const char *format, ...)
__builtin_return_address(0), &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);
@@ -347,3 +411,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
\ No newline at end of file
diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h
index af31beeb82a1..4609a2f4a425 100644
--- a/include/drm/drm_print.h
+++ b/include/drm/drm_print.h
@@ -36,12 +36,13 @@
/* Do *not* use outside of drm_print.[ch]! */
extern unsigned int __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::
*
@@ -86,6 +87,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);
@@ -219,7 +223,8 @@ static inline struct drm_printer
drm_info_printer(struct device *dev)
}
/**
- * drm_debug_printer - construct a &drm_printer that outputs to
pr_debug()
+ * drm_debug_printer - construct a &drm_printer that outputs to
pr_debug() and
+ * drm tracefs
* @prefix: debug output prefix
*
* RETURNS:
@@ -228,7 +233,7 @@ static inline struct drm_printer
drm_info_printer(struct device *dev)
static inline struct drm_printer drm_debug_printer(const char *prefix)
{
struct drm_printer p = {
- .printfn = __drm_printfn_debug_syslog,
+ .printfn = __drm_printfn_debug_syslog_and_trace,
.prefix = prefix
};
return p;
@@ -254,14 +259,14 @@ static inline struct drm_printer
drm_err_printer(const char *prefix)
* 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 parameters. 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
@@ -270,10 +275,14 @@ static inline struct drm_printer
drm_err_printer(const char *prefix)
* - 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 {
/**
@@ -325,14 +334,20 @@ 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);
+ return drm_debug_syslog_enabled(category) ||
+ drm_debug_trace_enabled(category);
}
/**
* 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
*
@@ -347,8 +362,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;
@@ -357,6 +377,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
*