netdev
[Top] [All Lists]

Re: [PATCH] [2/2] kernel error reporting (revised)

To: James Morris <jmorris@xxxxxxxxxxxxxxxx>, "David S. Miller" <davem@xxxxxxxxxx>, linux-kernel@xxxxxxxxxxxxxxx, netdev@xxxxxxxxxxx, akpm@xxxxxxxx, jgarzik@xxxxxxxxx, alan@xxxxxxxxxxxxxxxxxxx, rddunlap@xxxxxxxx, kuznet@xxxxxxxxxxxxx, jkenisto@xxxxxxxxxx
Subject: Re: [PATCH] [2/2] kernel error reporting (revised)
From: Jim Keniston <jkenisto@xxxxxxxxxx>
Date: Tue, 15 Jul 2003 10:45:46 -0700
References: <Mutt.LNX.4.44.0307131052420.2146-100000@xxxxxxxxxxxxxxxxxxxxxxxxxx> <3F143D0A.A052F0B6@xxxxxxxxxx>
Sender: netdev-bounce@xxxxxxxxxxx
This patch is described in the previous post.

Jim Keniston
IBM Linux Technology Center
diff -Naur linux.org/include/linux/evlog.h 
linux.evlog.patched/include/linux/evlog.h
--- linux.org/include/linux/evlog.h     Wed Dec 31 16:00:00 1969
+++ linux.evlog.patched/include/linux/evlog.h   Mon Jul 14 09:52:59 2003
@@ -0,0 +1,109 @@
+/*
+ * Linux Event Logging
+ * Copyright (c) International Business Machines Corp., 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  Please send e-mail to kenistoj@xxxxxxxxxxxxxxxxxxxxx if you have
+ *  questions or comments.
+ *
+ *  Project Website:  http://evlog.sourceforge.net/
+ */
+
+#ifndef _LINUX_EVLOG_H
+#define _LINUX_EVLOG_H
+
+#include <stdarg.h>
+#include <linux/types.h>
+#include <asm/types.h>
+
+/* Values for log_flags member */
+#define EVL_TRUNCATE           0x1
+#define EVL_KERNEL_EVENT       0x2
+#define EVL_INTERRUPT          0x10    /* Logged from interrupt context */
+#define EVL_PRINTK             0x20    /* Strip leading <n> when formatting */
+#define EVL_EVTYCRC            0x40    /* Daemon will set event type = CRC */
+                                       /* of format string. */
+
+/* Formats for optional portion of record. */
+#define EVL_NODATA    0
+#define EVL_BINARY    1
+#define EVL_STRING    2
+#define EVL_PRINTF    3
+
+/* Maximum length of variable portion of record */
+#define EVL_ENTRY_MAXLEN (8 * 1024)
+
+/* Facility (e.g., driver) names are truncated to 15+null. */
+#define FACILITY_MAXLEN 16
+
+/*
+ * struct kern_log_entry - kernel record header
+ * Each record sent to group KERROR_GROUP_EVLOG begins with this header.
+ */
+struct kern_log_entry {
+       __u16   log_kmagic;     /* always LOGREC_KMAGIC */
+       __u16   log_kversion;   /* which version of this struct? */
+       __u16   log_size;       /* # bytes in variable part of record */
+       __s8    log_format;     /* BINARY, STRING, PRINTF, NODATA */
+       __s8    log_severity;   /* DEBUG, INFO, NOTICE, WARN, etc. */
+       __s32   log_event_type; /* facility-specific event ID */
+       __u32   log_flags;      /* EVL_TRUNCATE, etc. */
+       __s32   log_processor;  /* CPU ID */
+       uid_t   log_uid;        /* event context... */
+       gid_t   log_gid;
+       pid_t   log_pid;
+       pid_t   log_pgrp;
+       char    log_facility[FACILITY_MAXLEN];  /* e.g., driver name */
+}; 
+
+#define LOGREC_KMAGIC  0x7af8
+#define LOGREC_KVERSION        3
+
+#ifdef __KERNEL__
+/*
+ * severities, AKA priorities
+ */
+#define LOG_EMERG   0   /* system is unusable */
+#define LOG_ALERT   1   /* action must be taken immediately */
+#define LOG_CRIT    2   /* critical conditions */
+#define LOG_ERR     3   /* error conditions */
+#define LOG_WARNING 4   /* warning conditions */
+#define LOG_NOTICE  5   /* normal but significant condition */
+#define LOG_INFO    6   /* informational */
+#define LOG_DEBUG   7   /* debug-level messages */
+
+#ifdef CONFIG_NET
+extern int evl_write(const char *facility, int event_type,
+       int severity, const void *buf, size_t len, int format);
+extern int evl_printf(const char *facility, int event_type, int sev,
+       const char *fmt, ...);
+extern int evl_vprintf(const char *facility, int event_type, int sev,
+       const char *fmt, va_list args);
+#else  /* ! CONFIG_NET */
+static inline int evl_write(const char *facility, int event_type,
+       int severity, const void *buf, size_t len, int format)
+       { return -ENOSYS; }
+static inline int evl_printf(const char *facility, int event_type, int sev,
+       const char *fmt, ...);
+       { return -ENOSYS; }
+static inline int evl_vprintf(const char *facility, int event_type, int sev,
+       const char *fmt, va_list args)
+       { return -ENOSYS; }
+#endif /* CONFIG_NET */
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_EVLOG_H */
diff -Naur linux.org/kernel/Makefile linux.evlog.patched/kernel/Makefile
--- linux.org/kernel/Makefile   Mon Jul 14 09:52:59 2003
+++ linux.evlog.patched/kernel/Makefile Mon Jul 14 09:52:59 2003
@@ -19,6 +19,7 @@
 obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
 obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o
 obj-$(CONFIG_COMPAT) += compat.o
+obj-$(CONFIG_NET) += evlog.o
 
 ifneq ($(CONFIG_IA64),y)
 # According to Alan Modra <alan@xxxxxxxxxxxxxxxx>, the -fno-omit-frame-pointer 
is
diff -Naur linux.org/kernel/evlog.c linux.evlog.patched/kernel/evlog.c
--- linux.org/kernel/evlog.c    Wed Dec 31 16:00:00 1969
+++ linux.evlog.patched/kernel/evlog.c  Mon Jul 14 09:52:59 2003
@@ -0,0 +1,542 @@
+/*
+ * Linux Event Logging
+ * Copyright (c) International Business Machines Corp., 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  Please send e-mail to kenistoj@xxxxxxxxxxxxxxxxxxxxx if you have
+ *  questions or comments.
+ *
+ *  Project Website:  http://evlog.sourceforge.net/
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kerror.h>
+#include <linux/stddef.h>
+#include <linux/uio.h>
+#include <linux/spinlock.h>
+#include <linux/smp.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ctype.h>
+#include <linux/evlog.h>
+
+static void report_dropped_event(const struct kern_log_entry *hdr,
+       const void *vardata);
+static void report_dropped_printf_event(const struct kern_log_entry *hdr,
+       const char *fmt, va_list args);
+
+/**
+ * mk_rec_header() - Populate evlog record header.
+ * @fac: facility name (e.g., "kern", driver name)
+ * @event_type: event type (event ID assigned by programmer; may also be
+ *     computed by recipient -- e.g., CRC of format string)
+ * @severity: severity level (e.g., LOG_INFO)
+ * @size: length, in bytes, of variable data
+ * @flags: event flags (e.g., EVL_TRUNCATE, EVL_EVTYCRC)
+ * @format: format of variable data (e.g., EVL_STRING)
+ */
+static void
+mk_rec_header(struct kern_log_entry *rec_hdr,
+               const char *facility,
+               int     event_type,
+               int     severity,
+               size_t  size,
+               uint    flags,
+               int     format)
+{
+       rec_hdr->log_kmagic             =  LOGREC_KMAGIC;
+       rec_hdr->log_kversion           =  LOGREC_KVERSION;
+       rec_hdr->log_size               =  (__u16) size;
+       rec_hdr->log_format             =  (__s8) format;
+       rec_hdr->log_event_type         =  (__s32) event_type;
+       rec_hdr->log_severity           =  (__s8) severity;
+       rec_hdr->log_uid                =  current->uid;
+       rec_hdr->log_gid                =  current->gid;
+       rec_hdr->log_pid                =  current->pid;
+       rec_hdr->log_pgrp               =  current->pgrp;
+       rec_hdr->log_flags              =  (__u32) flags;
+       rec_hdr->log_processor          =  (__s32) smp_processor_id();
+
+       strncpy(rec_hdr->log_facility, facility, FACILITY_MAXLEN);
+       rec_hdr->log_facility[FACILITY_MAXLEN-1] = '\0';
+}
+
+/**
+ * evl_sendh() - Log event, given a pre-constructed header.
+ * In case of sloppiness, clean it up rather than failing, since the caller
+ * is unlikely to handle failure.
+ * Returns 0 on success, or a negative error code otherwise.
+ */
+static int
+evl_sendh(struct kern_log_entry *hdr, const void *vardata)
+{
+       struct iovec iov[2] = {
+               { hdr, sizeof(struct kern_log_entry) },
+               { (void*) vardata, hdr->log_size }
+       };
+       int nsegs = 2;
+
+       if (hdr->log_severity < 0 || hdr->log_severity > LOG_DEBUG) {
+               hdr->log_severity = LOG_WARNING;
+       }
+       if (vardata == NULL || hdr->log_size == 0) {
+               vardata = NULL;
+               hdr->log_size = 0;
+               hdr->log_format = EVL_NODATA;
+               nsegs = 1;
+       }
+       hdr->log_flags |= EVL_KERNEL_EVENT;
+       if (in_interrupt()) {
+               hdr->log_flags |= EVL_INTERRUPT;
+       }
+       if (hdr->log_size > EVL_ENTRY_MAXLEN) {
+               iov[1].iov_len = hdr->log_size = EVL_ENTRY_MAXLEN;
+               hdr->log_flags |= EVL_TRUNCATE;
+       }
+
+       return kernel_error_event_iov(iov, nsegs, KERROR_GROUP_EVLOG);
+}
+
+/**
+ * evl_write() - write header + optional buffer to event handler
+ *
+ * @buf: optional variable-length data
+ * other args as per mk_rec_header()
+ */
+int
+evl_write(const char *fac, int event_type, int severity, const void *buf,
+       size_t size, int format)
+{
+       int ret;
+       struct kern_log_entry hdr;
+
+       mk_rec_header(&hdr, fac, event_type, severity, size, 0, format);
+       ret = evl_sendh(&hdr, buf);
+       if (ret == -ESRCH) {
+               report_dropped_event(&hdr, buf);
+       }
+       return ret;
+}
+
+/*
+ * A buffer to pack with data, one value at a time.  By convention, b_tail
+ * reflects the total amount you've attempted to add, and so may be past b_end.
+ */
+struct evl_recbuf {
+       char *b_buf;    /* start of buffer */
+       char *b_tail;   /* add next data here */
+       char *b_end;    /* b_buf + buffer size */
+};
+
+void
+evl_init_recbuf(struct evl_recbuf *b, char *buf, size_t size)
+{
+       b->b_buf = buf;
+       b->b_tail = buf;
+       b->b_end = buf + size;
+}
+
+/**
+ * evl_put() - Append data to buffer; handle overflow.
+ * @b - describes buffer; updated to reflect data appended
+ * @data - data to append
+ * @datasz - data length in bytes
+ */
+void
+evl_put(struct evl_recbuf *b, const void *data, size_t datasz)
+{
+       ptrdiff_t room = b->b_end - b->b_tail;
+       if (room > 0) {
+               (void) memcpy(b->b_tail, data, min(datasz, (size_t)room));
+       }
+       b->b_tail += datasz;
+}
+
+/**
+ * evl_puts() - Append string to buffer; handle overflow.
+ * Append a string to the buffer.  If null == 1, we include the terminating
+ * null.  If the string extends over the end of the buffer, terminate the
+ * buffer with a null.
+ *
+ * @b - describes buffer; updated to reflect data appended
+ * @s - null-terminated string
+ * @null - 1 if we append the terminating null, 0 otherwise
+ */
+void
+evl_puts(struct evl_recbuf *b, const char *s, int null)
+{
+       char *old_tail = b->b_tail;
+       evl_put(b, s, strlen(s) + null);
+       if (b->b_tail > b->b_end && old_tail < b->b_end) {
+               *(b->b_end - 1) = '\0';
+       }
+}
+
+static inline void
+skip_atoi(const char **s)
+{
+       while (isdigit(**s)) {
+               (*s)++;
+       }
+}
+
+/**
+ * parse_printf_fmt() - Parse printf/printk conversion spec.
+ * fmt points to the '%' in a printk conversion specification.  Advance
+ * fmt past any flags, width and/or precision specifiers, and qualifiers
+ * such as 'l' and 'L'.  Return a pointer to the conversion character.
+ * Stores the qualifier character (or -1, if there is none) at *pqualifier.
+ * *wp is set to flags indicating whether the width and/or precision are '*'.
+ * For example, given
+ *      %*.2lx
+ * *pqualifier is set to 'l', *wp is set to 0x1, and a pointer to the 'x'
+ * is returned.
+ *
+ * Note: This function is derived from vsnprintf() (see lib/vsprintf.c),
+ * and should be kept in sync with that function.
+ *
+ * @fmt - points to '%' in conversion spec
+ * @pqualifier - *pqualifier is set to conversion spec's qualifier, or -1.
+ * @wp - Bits in *wp are set if the width or/and precision are '*'.
+ */
+const char *
+parse_printf_fmt(const char *fmt, int *pqualifier, int *wp)
+{
+       int qualifier = -1;
+       *wp = 0;
+
+       /* process flags */
+       repeat:
+               ++fmt;          /* this also skips first '%' */
+               switch (*fmt) {
+                       case '-':
+                       case '+':
+                       case ' ':
+                       case '#':
+                       case '0':
+                               goto repeat;
+               }
+
+       /* get field width */
+       if (isdigit(*fmt))
+               skip_atoi(&fmt);
+       else if (*fmt == '*') {
+               ++fmt;
+               /* it's the next argument */
+               *wp |= 0x1;
+       }
+
+       /* get the precision */
+       if (*fmt == '.') {
+               ++fmt;
+               if (isdigit(*fmt))
+                       skip_atoi(&fmt);
+               else if (*fmt == '*') {
+                       ++fmt;
+                       /* it's the next argument */
+                       *wp |= 0x2;
+               }
+       }
+
+       /* get the conversion qualifier */
+       if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
+           *fmt == 'Z' || *fmt == 'z') {
+               qualifier = *fmt;
+               ++fmt;
+               if (qualifier == 'l' && *fmt == 'l') {
+                       qualifier = 'L';
+                       ++fmt;
+               }
+       }
+
+       *pqualifier = qualifier;
+       return fmt;
+}
+
+/**
+ * evl_pack_args() - Pack args into buffer, guided by format string.
+ * b describes a buffer.  fmt and args are as passed to vsnprintf().  Using
+ * fmt as a guide, copy the args into b's buffer.
+ *
+ * @b - describes buffer; updated to reflect data added
+ * @fmt - printf/printk-style format string
+ * @args - values to be packed into buffer
+ */
+void
+evl_pack_args(struct evl_recbuf *b, const char *fmt, va_list args)
+{
+#define COPYARG(type) \
+    do { type v=va_arg(args,type); evl_put(b,&v,sizeof(v)); } while(0)
+
+       const char *s;
+       int qualifier;
+
+       for (; *fmt ; ++fmt) {
+               int wp = 0x0;
+               if (*fmt != '%') {
+                       continue;
+               }
+
+               fmt = parse_printf_fmt(fmt, &qualifier, &wp);
+               if (wp & 0x1) {
+                       /* width is '*' (next arg) */
+                       COPYARG(int);
+               }
+               if (wp & 0x2) {
+                       /* ditto precision */
+                       COPYARG(int);
+               }
+
+               switch (*fmt) {
+                       case 'c':
+                               COPYARG(int);
+                               continue;
+
+                       case 's':
+                               s = va_arg(args, char *);
+                               evl_puts(b, s, 1);
+                               continue;
+
+                       case 'p':
+                               COPYARG(void*);
+                               continue;
+
+                       case 'n':
+                               /* Skip over the %n arg. */
+                               if (qualifier == 'l') {
+                                       (void) va_arg(args, long *);
+                               } else if (qualifier == 'Z' || qualifier == 
'z') {
+                                       (void) va_arg(args, size_t *);
+                               } else {
+                                       (void) va_arg(args, int *);
+                               }
+                               continue;
+
+                       case '%':
+                               continue;
+
+                               /* integer number formats - handle outside 
switch */
+                       case 'o':
+                       case 'X':
+                       case 'x':
+                       case 'd':
+                       case 'i':
+                       case 'u':
+                               break;
+
+                       default:
+                               /* Bogus conversion.  Pass thru unchanged. */
+                               if (*fmt == '\0')
+                                       --fmt;
+                               continue;
+               }
+               if (qualifier == 'L') {
+                       COPYARG(long long);
+               } else if (qualifier == 'l') {
+                       COPYARG(long);
+               } else if (qualifier == 'Z' || qualifier == 'z') {
+                       COPYARG(size_t);
+               } else if (qualifier == 'h') {
+                       COPYARG(int);
+               } else {
+                       COPYARG(int);
+               }
+       }
+}
+
+/*
+ * Scratch buffer for constructing event records.  This is static because
+ * (1) we want events to be logged even in low-memory situations; and
+ * (2) the buffer is too big to be an auto variable.
+ */
+static spinlock_t msgbuf_lock = SPIN_LOCK_UNLOCKED;
+static char msgbuf[EVL_ENTRY_MAXLEN];
+
+/**
+ * evl_send_printf() - Format and log a PRINTF-format message.
+ * Create and log a PRINTF-format event record whose contents are:
+ *     format string
+ *     int containing args size
+ *     args
+ * @hdr - pre-constructed record header
+ * @fmt - format string
+ * @args - arg list
+ */
+static int
+evl_send_printf(struct kern_log_entry *hdr, const char *fmt, va_list args)
+{
+       int ret;
+       struct evl_recbuf b;
+       int argsz = 0;
+       char *nl, *pargsz, *pargs;
+       unsigned long iflags;
+
+       spin_lock_irqsave(&msgbuf_lock, iflags);
+       evl_init_recbuf(&b, msgbuf, EVL_ENTRY_MAXLEN);
+       evl_puts(&b, fmt, 1);
+
+       /*
+        * If the format ends in a newline, remove it.  We remove the
+        * terminating newline to increase flexibility when formatting
+        * the record for viewing.
+        */
+       nl = b.b_tail - 2;
+       if (b.b_buf <= nl && nl < b.b_end && *nl == '\n') {
+               *nl = '\0';
+               b.b_tail--;
+       }
+
+       /* Remember where to store argsz; store 0 for now. */
+       pargsz = b.b_tail;
+       evl_put(&b, &argsz, sizeof(argsz));
+       pargs = b.b_tail;
+
+       evl_pack_args(&b, fmt, args);
+       if (pargs <= b.b_end) {
+               argsz = (int) (b.b_tail - pargs);
+               memcpy(pargsz, &argsz, sizeof(argsz));
+       }
+
+       hdr->log_size = b.b_tail - b.b_buf;
+       if (hdr->log_size > EVL_ENTRY_MAXLEN) {
+               hdr->log_size = EVL_ENTRY_MAXLEN;
+               hdr->log_flags |= EVL_TRUNCATE;
+       }
+       
+       ret = evl_sendh(hdr, b.b_buf);
+       spin_unlock_irqrestore(&msgbuf_lock, iflags);
+
+       if (ret == -ESRCH) {
+               report_dropped_printf_event(hdr, fmt, args);
+       }
+       return ret;
+}
+
+/**
+ * evl_vprintf() - Format and log a PRINTF-format record.
+ * @fmt - format string
+ * @args - arg list
+ * other args as per mk_rec_header().  If event_type == 0, set flag to
+ *     request that recipient set event type.
+ */
+int
+evl_vprintf(const char *facility, int event_type, int severity,
+       const char *fmt, va_list args)
+{
+       struct kern_log_entry hdr;
+       unsigned int flags = 0;
+       if (event_type == 0) {
+               flags |= EVL_EVTYCRC;
+       }
+       mk_rec_header(&hdr, facility, event_type, severity, 0, flags,
+               EVL_PRINTF);
+       
+       return evl_send_printf(&hdr, fmt, args);
+}
+
+/**
+ * evl_printf() - Format and log a PRINTF-format record.
+ * @fmt - format string
+ * other args as per mk_rec_header()
+ */
+int
+evl_printf(const char *facility, int event_type, int severity,
+       const char *fmt, ...)
+{
+       va_list args;
+       int ret;
+       va_start(args, fmt);
+       ret = evl_vprintf(facility, event_type, severity, fmt, args);
+       va_end(args);
+       return ret;
+}
+
+/*** Functions for handling of events logged when nobody was listening ***/
+static void
+report_dropped_hdr(const struct kern_log_entry *hdr)
+{
+       printk("<%d>evlog packet dropped: size=%u fmt=%d evty=%#x"
+               " fac=%s sev=%d uid=%u gid=%u pid=%d pgrp=%d"
+               " flags=%#x cpu=%d\n",
+               hdr->log_severity, hdr->log_size, hdr->log_format,
+               hdr->log_event_type, hdr->log_facility, hdr->log_severity,
+               hdr->log_uid, hdr->log_gid, hdr->log_pid, hdr->log_pgrp,
+               hdr->log_flags, hdr->log_processor);
+}
+
+static void
+report_dropped_printf_event(const struct kern_log_entry *hdr, const char *fmt,
+       va_list args)
+{
+       char msg[500];
+       if (hdr->log_flags & EVL_PRINTK) {
+               /* printk's already reporting this event. */
+               return;
+       }
+       report_dropped_hdr(hdr);
+       vsnprintf(msg, 500, fmt, args);
+       printk("<%d>%s\n", hdr->log_severity, msg);
+}
+
+static void
+hexdump(const void *data, size_t nbytes, int severity)
+{
+#define MAX_HEXDUMP_LEN 512    /* Keep this small: don't flood printk buffer */
+       size_t nb = min(nbytes, (size_t)MAX_HEXDUMP_LEN);
+       const unsigned char *dbase = (const unsigned char*) data;
+       const unsigned char *dp = dbase, *dend = dbase + nb;
+       char *lp, line[100];
+       int i;
+
+       while (dp < dend) {
+               lp = line;
+               lp += sprintf(lp, "%04X ", (unsigned) (dp - dbase));
+               for (i = 0; i < 16 && dp < dend; i++, dp++) {
+                       lp += sprintf(lp, "%02X ", *dp);
+               }
+               printk("<%d>%s\n", severity, line);
+       }
+}
+
+static void
+report_dropped_event(const struct kern_log_entry *hdr, const void *vardata)
+{
+       if (hdr->log_flags & EVL_PRINTK) {
+               /* printk's already reporting this event. */
+               return;
+       }
+       report_dropped_hdr(hdr);
+       switch (hdr->log_format) {
+       case EVL_STRING:
+               printk("<%d>%s\n", hdr->log_severity, (const char*) vardata);
+               break;
+       case EVL_PRINTF:
+               /* Should be handled by report_dropped_printf_event() */
+               /*FALLTHRU*/
+       case EVL_BINARY:
+               hexdump(vardata, hdr->log_size, hdr->log_severity);
+               break;
+       case EVL_NODATA:
+       default:
+               break;
+       }
+}
+
+EXPORT_SYMBOL(evl_write);
+EXPORT_SYMBOL(evl_printf);
+EXPORT_SYMBOL(evl_vprintf);
<Prev in Thread] Current Thread [Next in Thread>