summaryrefslogtreecommitdiff
path: root/drivers/md/dm-vdo/logger.c
diff options
context:
space:
mode:
authorMatthew Sakai <msakai@redhat.com>2023-11-17 03:44:01 +0300
committerMike Snitzer <snitzer@kernel.org>2024-02-20 21:43:13 +0300
commit03d1089e1dc7f05afb2bfc76bffc71de17e4297e (patch)
treeef710d43041426d0d231556db3936bbab6ddfcf4 /drivers/md/dm-vdo/logger.c
parent46766d4888ffdfd2bce91a6879bd6285a92a4881 (diff)
downloadlinux-03d1089e1dc7f05afb2bfc76bffc71de17e4297e.tar.xz
dm vdo: add basic logging and support utilities
Add various support utilities for the vdo target and deduplication index, including logging utilities, string and time management, and index-specific error codes. Co-developed-by: J. corwin Coburn <corwin@hurlbutnet.net> Signed-off-by: J. corwin Coburn <corwin@hurlbutnet.net> Co-developed-by: Michael Sclafani <dm-devel@lists.linux.dev> Signed-off-by: Michael Sclafani <dm-devel@lists.linux.dev> Co-developed-by: Thomas Jaskiewicz <tom@jaskiewicz.us> Signed-off-by: Thomas Jaskiewicz <tom@jaskiewicz.us> Co-developed-by: Ken Raeburn <raeburn@redhat.com> Signed-off-by: Ken Raeburn <raeburn@redhat.com> Signed-off-by: Matthew Sakai <msakai@redhat.com> Signed-off-by: Mike Snitzer <snitzer@kernel.org>
Diffstat (limited to 'drivers/md/dm-vdo/logger.c')
-rw-r--r--drivers/md/dm-vdo/logger.c291
1 files changed, 291 insertions, 0 deletions
diff --git a/drivers/md/dm-vdo/logger.c b/drivers/md/dm-vdo/logger.c
new file mode 100644
index 000000000000..fe632f10b5d9
--- /dev/null
+++ b/drivers/md/dm-vdo/logger.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2023 Red Hat
+ */
+
+#include "logger.h"
+
+#include <linux/delay.h>
+#include <linux/hardirq.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/sched.h>
+
+#include "thread-device.h"
+#include "uds-threads.h"
+
+struct priority_name {
+ const char *name;
+ const int priority;
+};
+
+static const struct priority_name PRIORITIES[] = {
+ { "ALERT", UDS_LOG_ALERT },
+ { "CRITICAL", UDS_LOG_CRIT },
+ { "CRIT", UDS_LOG_CRIT },
+ { "DEBUG", UDS_LOG_DEBUG },
+ { "EMERGENCY", UDS_LOG_EMERG },
+ { "EMERG", UDS_LOG_EMERG },
+ { "ERROR", UDS_LOG_ERR },
+ { "ERR", UDS_LOG_ERR },
+ { "INFO", UDS_LOG_INFO },
+ { "NOTICE", UDS_LOG_NOTICE },
+ { "PANIC", UDS_LOG_EMERG },
+ { "WARN", UDS_LOG_WARNING },
+ { "WARNING", UDS_LOG_WARNING },
+ { NULL, -1 },
+};
+
+static const char *const PRIORITY_STRINGS[] = {
+ "EMERGENCY",
+ "ALERT",
+ "CRITICAL",
+ "ERROR",
+ "WARN",
+ "NOTICE",
+ "INFO",
+ "DEBUG",
+};
+
+static int log_level = UDS_LOG_INFO;
+
+int uds_get_log_level(void)
+{
+ return log_level;
+}
+
+void uds_set_log_level(int new_log_level)
+{
+ log_level = new_log_level;
+}
+
+int uds_log_string_to_priority(const char *string)
+{
+ int i;
+
+ for (i = 0; PRIORITIES[i].name != NULL; i++) {
+ if (strcasecmp(string, PRIORITIES[i].name) == 0)
+ return PRIORITIES[i].priority;
+ }
+
+ return UDS_LOG_INFO;
+}
+
+const char *uds_log_priority_to_string(int priority)
+{
+ if ((priority < 0) || (priority >= (int) ARRAY_SIZE(PRIORITY_STRINGS)))
+ return "unknown";
+
+ return PRIORITY_STRINGS[priority];
+}
+
+static const char *get_current_interrupt_type(void)
+{
+ if (in_nmi())
+ return "NMI";
+
+ if (in_irq())
+ return "HI";
+
+ if (in_softirq())
+ return "SI";
+
+ return "INTR";
+}
+
+/**
+ * emit_log_message_to_kernel() - Emit a log message to the kernel at the specified priority.
+ *
+ * @priority: The priority at which to log the message
+ * @fmt: The format string of the message
+ */
+static void emit_log_message_to_kernel(int priority, const char *fmt, ...)
+{
+ va_list args;
+ struct va_format vaf;
+
+ if (priority > uds_get_log_level())
+ return;
+
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ switch (priority) {
+ case UDS_LOG_EMERG:
+ case UDS_LOG_ALERT:
+ case UDS_LOG_CRIT:
+ printk(KERN_CRIT "%pV", &vaf);
+ break;
+ case UDS_LOG_ERR:
+ printk(KERN_ERR "%pV", &vaf);
+ break;
+ case UDS_LOG_WARNING:
+ printk(KERN_WARNING "%pV", &vaf);
+ break;
+ case UDS_LOG_NOTICE:
+ printk(KERN_NOTICE "%pV", &vaf);
+ break;
+ case UDS_LOG_INFO:
+ printk(KERN_INFO "%pV", &vaf);
+ break;
+ case UDS_LOG_DEBUG:
+ printk(KERN_DEBUG "%pV", &vaf);
+ break;
+ default:
+ printk(KERN_DEFAULT "%pV", &vaf);
+ break;
+ }
+
+ va_end(args);
+}
+
+/**
+ * emit_log_message() - Emit a log message to the kernel log in a format suited to the current
+ * thread context.
+ *
+ * Context info formats:
+ *
+ * interrupt: uds[NMI]: blah
+ * kvdo thread: kvdo12:foobarQ: blah
+ * thread w/device id: kvdo12:myprog: blah
+ * other thread: uds: myprog: blah
+ *
+ * Fields: module name, interrupt level, process name, device ID.
+ *
+ * @priority: the priority at which to log the message
+ * @module: The name of the module doing the logging
+ * @prefix: The prefix of the log message
+ * @vaf1: The first message format descriptor
+ * @vaf2: The second message format descriptor
+ */
+static void emit_log_message(int priority, const char *module, const char *prefix,
+ const struct va_format *vaf1, const struct va_format *vaf2)
+{
+ int device_instance;
+
+ /*
+ * In interrupt context, identify the interrupt type and module. Ignore the process/thread
+ * since it could be anything.
+ */
+ if (in_interrupt()) {
+ const char *type = get_current_interrupt_type();
+
+ emit_log_message_to_kernel(priority, "%s[%s]: %s%pV%pV\n", module, type,
+ prefix, vaf1, vaf2);
+ return;
+ }
+
+ /* Not at interrupt level; we have a process we can look at, and might have a device ID. */
+ device_instance = uds_get_thread_device_id();
+ if (device_instance >= 0) {
+ emit_log_message_to_kernel(priority, "%s%u:%s: %s%pV%pV\n", module,
+ device_instance, current->comm, prefix, vaf1,
+ vaf2);
+ return;
+ }
+
+ /*
+ * If it's a kernel thread and the module name is a prefix of its name, assume it is ours
+ * and only identify the thread.
+ */
+ if (((current->flags & PF_KTHREAD) != 0) &&
+ (strncmp(module, current->comm, strlen(module)) == 0)) {
+ emit_log_message_to_kernel(priority, "%s: %s%pV%pV\n", current->comm,
+ prefix, vaf1, vaf2);
+ return;
+ }
+
+ /* Identify the module and the process. */
+ emit_log_message_to_kernel(priority, "%s: %s: %s%pV%pV\n", module, current->comm,
+ prefix, vaf1, vaf2);
+}
+
+/*
+ * uds_log_embedded_message() - Log a message embedded within another message.
+ * @priority: the priority at which to log the message
+ * @module: the name of the module doing the logging
+ * @prefix: optional string prefix to message, may be NULL
+ * @fmt1: format of message first part (required)
+ * @args1: arguments for message first part (required)
+ * @fmt2: format of message second part
+ */
+void uds_log_embedded_message(int priority, const char *module, const char *prefix,
+ const char *fmt1, va_list args1, const char *fmt2, ...)
+{
+ va_list args1_copy;
+ va_list args2;
+ struct va_format vaf1, vaf2;
+
+ va_start(args2, fmt2);
+
+ if (module == NULL)
+ module = UDS_LOGGING_MODULE_NAME;
+
+ if (prefix == NULL)
+ prefix = "";
+
+ /*
+ * It is implementation dependent whether va_list is defined as an array type that decays
+ * to a pointer when passed as an argument. Copy args1 and args2 with va_copy so that vaf1
+ * and vaf2 get proper va_list pointers irrespective of how va_list is defined.
+ */
+ va_copy(args1_copy, args1);
+ vaf1.fmt = fmt1;
+ vaf1.va = &args1_copy;
+
+ vaf2.fmt = fmt2;
+ vaf2.va = &args2;
+
+ emit_log_message(priority, module, prefix, &vaf1, &vaf2);
+
+ va_end(args1_copy);
+ va_end(args2);
+}
+
+int uds_vlog_strerror(int priority, int errnum, const char *module, const char *format,
+ va_list args)
+{
+ char errbuf[UDS_MAX_ERROR_MESSAGE_SIZE];
+ const char *message = uds_string_error(errnum, errbuf, sizeof(errbuf));
+
+ uds_log_embedded_message(priority, module, NULL, format, args, ": %s (%d)",
+ message, errnum);
+ return errnum;
+}
+
+int __uds_log_strerror(int priority, int errnum, const char *module, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ uds_vlog_strerror(priority, errnum, module, format, args);
+ va_end(args);
+ return errnum;
+}
+
+void uds_log_backtrace(int priority)
+{
+ if (priority > uds_get_log_level())
+ return;
+
+ dump_stack();
+}
+
+void __uds_log_message(int priority, const char *module, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ uds_log_embedded_message(priority, module, NULL, format, args, "%s", "");
+ va_end(args);
+}
+
+/*
+ * Sleep or delay a few milliseconds in an attempt to allow the log buffers to be flushed lest they
+ * be overrun.
+ */
+void uds_pause_for_logger(void)
+{
+ fsleep(4000);
+}