1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright 2023 Red Hat 4 */ 5 6 #include "logger.h" 7 8 #include <asm/current.h> 9 #include <linux/delay.h> 10 #include <linux/hardirq.h> 11 #include <linux/module.h> 12 #include <linux/printk.h> 13 #include <linux/sched.h> 14 15 #include "errors.h" 16 #include "thread-device.h" 17 #include "thread-utils.h" 18 19 int vdo_log_level = VDO_LOG_DEFAULT; 20 21 int vdo_get_log_level(void) 22 { 23 int log_level_latch = READ_ONCE(vdo_log_level); 24 25 if (unlikely(log_level_latch > VDO_LOG_MAX)) { 26 log_level_latch = VDO_LOG_DEFAULT; 27 WRITE_ONCE(vdo_log_level, log_level_latch); 28 } 29 return log_level_latch; 30 } 31 32 static const char *get_current_interrupt_type(void) 33 { 34 if (in_nmi()) 35 return "NMI"; 36 37 if (in_irq()) 38 return "HI"; 39 40 if (in_softirq()) 41 return "SI"; 42 43 return "INTR"; 44 } 45 46 /** 47 * emit_log_message_to_kernel() - Emit a log message to the kernel at the specified priority. 48 * 49 * @priority: The priority at which to log the message 50 * @fmt: The format string of the message 51 */ 52 static void emit_log_message_to_kernel(int priority, const char *fmt, ...) 53 { 54 va_list args; 55 struct va_format vaf; 56 57 if (priority > vdo_get_log_level()) 58 return; 59 60 va_start(args, fmt); 61 vaf.fmt = fmt; 62 vaf.va = &args; 63 64 switch (priority) { 65 case VDO_LOG_EMERG: 66 case VDO_LOG_ALERT: 67 case VDO_LOG_CRIT: 68 pr_crit("%pV", &vaf); 69 break; 70 case VDO_LOG_ERR: 71 pr_err("%pV", &vaf); 72 break; 73 case VDO_LOG_WARNING: 74 pr_warn("%pV", &vaf); 75 break; 76 case VDO_LOG_NOTICE: 77 case VDO_LOG_INFO: 78 pr_info("%pV", &vaf); 79 break; 80 case VDO_LOG_DEBUG: 81 pr_debug("%pV", &vaf); 82 break; 83 default: 84 printk(KERN_DEFAULT "%pV", &vaf); 85 break; 86 } 87 88 va_end(args); 89 } 90 91 /** 92 * emit_log_message() - Emit a log message to the kernel log in a format suited to the current 93 * thread context. 94 * 95 * Context info formats: 96 * 97 * interrupt: uds[NMI]: blah 98 * kvdo thread: kvdo12:foobarQ: blah 99 * thread w/device id: kvdo12:myprog: blah 100 * other thread: uds: myprog: blah 101 * 102 * Fields: module name, interrupt level, process name, device ID. 103 * 104 * @priority: the priority at which to log the message 105 * @module: The name of the module doing the logging 106 * @prefix: The prefix of the log message 107 * @vaf1: The first message format descriptor 108 * @vaf2: The second message format descriptor 109 */ 110 static void emit_log_message(int priority, const char *module, const char *prefix, 111 const struct va_format *vaf1, const struct va_format *vaf2) 112 { 113 int device_instance; 114 115 /* 116 * In interrupt context, identify the interrupt type and module. Ignore the process/thread 117 * since it could be anything. 118 */ 119 if (in_interrupt()) { 120 const char *type = get_current_interrupt_type(); 121 122 emit_log_message_to_kernel(priority, "%s[%s]: %s%pV%pV\n", module, type, 123 prefix, vaf1, vaf2); 124 return; 125 } 126 127 /* Not at interrupt level; we have a process we can look at, and might have a device ID. */ 128 device_instance = vdo_get_thread_device_id(); 129 if (device_instance >= 0) { 130 emit_log_message_to_kernel(priority, "%s%u:%s: %s%pV%pV\n", module, 131 device_instance, current->comm, prefix, vaf1, 132 vaf2); 133 return; 134 } 135 136 /* 137 * If it's a kernel thread and the module name is a prefix of its name, assume it is ours 138 * and only identify the thread. 139 */ 140 if (((current->flags & PF_KTHREAD) != 0) && 141 (strncmp(module, current->comm, strlen(module)) == 0)) { 142 emit_log_message_to_kernel(priority, "%s: %s%pV%pV\n", current->comm, 143 prefix, vaf1, vaf2); 144 return; 145 } 146 147 /* Identify the module and the process. */ 148 emit_log_message_to_kernel(priority, "%s: %s: %s%pV%pV\n", module, current->comm, 149 prefix, vaf1, vaf2); 150 } 151 152 /* 153 * vdo_log_embedded_message() - Log a message embedded within another message. 154 * @priority: the priority at which to log the message 155 * @module: the name of the module doing the logging 156 * @prefix: optional string prefix to message, may be NULL 157 * @fmt1: format of message first part (required) 158 * @args1: arguments for message first part (required) 159 * @fmt2: format of message second part 160 */ 161 void vdo_log_embedded_message(int priority, const char *module, const char *prefix, 162 const char *fmt1, va_list args1, const char *fmt2, ...) 163 { 164 va_list args1_copy; 165 va_list args2; 166 struct va_format vaf1, vaf2; 167 168 va_start(args2, fmt2); 169 170 if (module == NULL) 171 module = VDO_LOGGING_MODULE_NAME; 172 173 if (prefix == NULL) 174 prefix = ""; 175 176 /* 177 * It is implementation dependent whether va_list is defined as an array type that decays 178 * to a pointer when passed as an argument. Copy args1 and args2 with va_copy so that vaf1 179 * and vaf2 get proper va_list pointers irrespective of how va_list is defined. 180 */ 181 va_copy(args1_copy, args1); 182 vaf1.fmt = fmt1; 183 vaf1.va = &args1_copy; 184 185 vaf2.fmt = fmt2; 186 vaf2.va = &args2; 187 188 emit_log_message(priority, module, prefix, &vaf1, &vaf2); 189 190 va_end(args1_copy); 191 va_end(args2); 192 } 193 194 int vdo_vlog_strerror(int priority, int errnum, const char *module, const char *format, 195 va_list args) 196 { 197 char errbuf[VDO_MAX_ERROR_MESSAGE_SIZE]; 198 const char *message = uds_string_error(errnum, errbuf, sizeof(errbuf)); 199 200 vdo_log_embedded_message(priority, module, NULL, format, args, ": %s (%d)", 201 message, errnum); 202 return errnum; 203 } 204 205 int __vdo_log_strerror(int priority, int errnum, const char *module, const char *format, ...) 206 { 207 va_list args; 208 209 va_start(args, format); 210 vdo_vlog_strerror(priority, errnum, module, format, args); 211 va_end(args); 212 return errnum; 213 } 214 215 void vdo_log_backtrace(int priority) 216 { 217 if (priority > vdo_get_log_level()) 218 return; 219 220 dump_stack(); 221 } 222 223 void __vdo_log_message(int priority, const char *module, const char *format, ...) 224 { 225 va_list args; 226 227 va_start(args, format); 228 vdo_log_embedded_message(priority, module, NULL, format, args, "%s", ""); 229 va_end(args); 230 } 231 232 /* 233 * Sleep or delay a few milliseconds in an attempt to allow the log buffers to be flushed lest they 234 * be overrun. 235 */ 236 void vdo_pause_for_logger(void) 237 { 238 fsleep(4000); 239 } 240