drm: drm_printer: Add printer for devcoredump
authorJordan Crouse <jcrouse@codeaurora.org>
Tue, 24 Jul 2018 16:33:20 +0000 (10:33 -0600)
committerRob Clark <robdclark@gmail.com>
Mon, 30 Jul 2018 12:49:31 +0000 (08:49 -0400)
Add a drm printer suitable for use with the read callback for
devcoredump or other suitable buffer based output format that
isn't otherwise covered by seq_file.

v2: Add improved documentation per Daniel Vetter

Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
drivers/gpu/drm/drm_print.c
include/drm/drm_print.h

index b25f98f33f6cb55e311b237b3c31d26f554edbc3..03d1f98e5ac7cd79730ceb5e0d901ac034c5b5f3 100644 (file)
 #include <drm/drmP.h>
 #include <drm/drm_print.h>
 
+void __drm_printfn_coredump(struct drm_printer *p, struct va_format *vaf)
+{
+       struct drm_print_iterator *iterator = p->arg;
+       ssize_t len;
+
+       if (!iterator->remain)
+               return;
+
+       /* Figure out how big the string will be */
+       len = snprintf(NULL, 0, "%pV", vaf);
+
+       if (iterator->offset < iterator->start) {
+               char *buf;
+               ssize_t copy;
+
+               if (iterator->offset + len <= iterator->start) {
+                       iterator->offset += len;
+                       return;
+               }
+
+               /* Print the string into a temporary buffer */
+               buf = kmalloc(len + 1,
+                       GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
+               if (!buf)
+                       return;
+
+               snprintf(buf, len + 1, "%pV", vaf);
+
+               copy = len - (iterator->start - iterator->offset);
+
+               if (copy > iterator->remain)
+                       copy = iterator->remain;
+
+               /* Copy out the bit of the string that we need */
+               memcpy(iterator->data,
+                       buf + (iterator->start - iterator->offset), copy);
+
+               iterator->offset = iterator->start + copy;
+               iterator->remain -= copy;
+
+               kfree(buf);
+       } else {
+               char *buf;
+               ssize_t pos = iterator->offset - iterator->start;
+
+               if (len < iterator->remain) {
+                       snprintf(((char *) iterator->data) + pos,
+                               iterator->remain, "%pV", vaf);
+
+                       iterator->offset += len;
+                       iterator->remain -= len;
+
+                       return;
+               }
+
+               /* Print the string into a temporary buffer */
+               buf = kmalloc(len + 1,
+                       GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
+               if (!buf)
+                       return;
+
+               snprintf(buf, len + 1, "%pV", vaf);
+
+               /* Copy out the remaining bits */
+               memcpy(iterator->data + pos, buf, iterator->remain);
+
+               iterator->offset += iterator->remain;
+               iterator->remain = 0;
+
+               kfree(buf);
+       }
+}
+EXPORT_SYMBOL(__drm_printfn_coredump);
+
 void __drm_printfn_seq_file(struct drm_printer *p, struct va_format *vaf)
 {
        seq_printf(p->arg, "%pV", vaf);
index 767c90b654c536601a06ee01b60e26a2d13e042a..e7570f93a21f8fba0fd8a450d3ed3c9495d9662b 100644 (file)
@@ -73,6 +73,7 @@ struct drm_printer {
        const char *prefix;
 };
 
+void __drm_printfn_coredump(struct drm_printer *p, struct va_format *vaf);
 void __drm_printfn_seq_file(struct drm_printer *p, struct va_format *vaf);
 void __drm_printfn_info(struct drm_printer *p, struct va_format *vaf);
 void __drm_printfn_debug(struct drm_printer *p, struct va_format *vaf);
@@ -104,6 +105,70 @@ drm_vprintf(struct drm_printer *p, const char *fmt, va_list *va)
 #define drm_printf_indent(printer, indent, fmt, ...) \
        drm_printf((printer), "%.*s" fmt, (indent), "\t\t\t\t\tX", ##__VA_ARGS__)
 
+/**
+ * struct drm_print_iterator - local struct used with drm_printer_coredump
+ * @data: Pointer to the devcoredump output buffer
+ * @start: The offset within the buffer to start writing
+ * @remain: The number of bytes to write for this iteration
+ */
+struct drm_print_iterator {
+       void *data;
+       ssize_t start;
+       ssize_t remain;
+       /* private: */
+       ssize_t offset;
+};
+
+/**
+ * drm_coredump_printer - construct a &drm_printer that can output to a buffer
+ * from the read function for devcoredump
+ * @iter: A pointer to a struct drm_print_iterator for the read instance
+ *
+ * This wrapper extends drm_printf() to work with a dev_coredumpm() callback
+ * function. The passed in drm_print_iterator struct contains the buffer
+ * pointer, size and offset as passed in from devcoredump.
+ *
+ * For example::
+ *
+ *     void coredump_read(char *buffer, loff_t offset, size_t count,
+ *             void *data, size_t datalen)
+ *     {
+ *             struct drm_print_iterator iter;
+ *             struct drm_printer p;
+ *
+ *             iter.data = buffer;
+ *             iter.start = offset;
+ *             iter.remain = count;
+ *
+ *             p = drm_coredump_printer(&iter);
+ *
+ *             drm_printf(p, "foo=%d\n", foo);
+ *     }
+ *
+ *     void makecoredump(...)
+ *     {
+ *             ...
+ *             dev_coredumpm(dev, THIS_MODULE, data, 0, GFP_KERNEL,
+ *                     coredump_read, ...)
+ *     }
+ *
+ * RETURNS:
+ * The &drm_printer object
+ */
+static inline struct drm_printer
+drm_coredump_printer(struct drm_print_iterator *iter)
+{
+       struct drm_printer p = {
+               .printfn = __drm_printfn_coredump,
+               .arg = iter,
+       };
+
+       /* Set the internal offset of the iterator to zero */
+       iter->offset = 0;
+
+       return p;
+}
+
 /**
  * drm_seq_file_printer - construct a &drm_printer that outputs to &seq_file
  * @f:  the &struct seq_file to output to