📄 printk.c
字号:
/* * linux/kernel/printk.c * * Copyright (C) 1991, 1992 Linus Torvalds * * Modified to make sys_syslog() more flexible: added commands to * return the last 4k of kernel messages, regardless of whether * they've been read or not. Added option to suppress kernel printk's * to the console. Added hook for sending the console messages * elsewhere, in preparation for a serial line console (someday). * Ted Ts'o, 2/11/93. * Modified for sysctl support, 1/8/97, Chris Horn. * Fixed SMP synchronization, 08/08/99, Manfred Spraul * manfreds@colorfullife.com * Rewrote bits to get rid of console_lock * 01Mar01 Andrew Morton <andrewm@uow.edu.au> */#include <linux/kernel.h>#include <linux/mm.h>#include <linux/tty.h>#include <linux/tty_driver.h>#include <linux/smp_lock.h>#include <linux/console.h>#include <linux/init.h>#include <linux/module.h>#include <linux/interrupt.h> /* For in_interrupt() */#include <linux/config.h>#include <linux/delay.h>#include <asm/uaccess.h>#if defined(CONFIG_MULTIQUAD) || defined(CONFIG_IA64)#define LOG_BUF_LEN (65536)#elif defined(CONFIG_ARCH_S390)#define LOG_BUF_LEN (131072)#elif defined(CONFIG_SMP)#define LOG_BUF_LEN (32768)#else #define LOG_BUF_LEN (16384) /* This must be a power of two */#endif#define LOG_BUF_MASK (LOG_BUF_LEN-1)#ifndef arch_consoles_callable#define arch_consoles_callable() (1)#endif/* printk's without a loglevel use this.. */#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING *//* We show everything that is MORE important than this.. */#define MINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */DECLARE_WAIT_QUEUE_HEAD(log_wait);int console_printk[4] = { DEFAULT_CONSOLE_LOGLEVEL, /* console_loglevel */ DEFAULT_MESSAGE_LOGLEVEL, /* default_message_loglevel */ MINIMUM_CONSOLE_LOGLEVEL, /* minimum_console_loglevel */ DEFAULT_CONSOLE_LOGLEVEL, /* default_console_loglevel */};int oops_in_progress;/* * console_sem protects the console_drivers list, and also * provides serialisation for access to the entire console * driver system. */static DECLARE_MUTEX(console_sem);struct console *console_drivers;/* * logbuf_lock protects log_buf, log_start, log_end, con_start and logged_chars * It is also used in interesting ways to provide interlocking in * release_console_sem(). */static spinlock_t logbuf_lock = SPIN_LOCK_UNLOCKED;static char log_buf[LOG_BUF_LEN];#define LOG_BUF(idx) (log_buf[(idx) & LOG_BUF_MASK])/* * The indices into log_buf are not constrained to LOG_BUF_LEN - they * must be masked before subscripting */static unsigned long log_start; /* Index into log_buf: next char to be read by syslog() */static unsigned long con_start; /* Index into log_buf: next char to be sent to consoles */static unsigned long log_end; /* Index into log_buf: most-recently-written-char + 1 */static unsigned long logged_chars; /* Number of chars produced since last read+clear operation */struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];static int preferred_console = -1;/* Flag: console code may call schedule() */static int console_may_schedule;/* * Setup a list of consoles. Called from init/main.c */static int __init console_setup(char *str){ struct console_cmdline *c; char name[sizeof(c->name)]; char *s, *options; int i, idx; /* * Decode str into name, index, options. */ if (str[0] >= '0' && str[0] <= '9') { strcpy(name, "ttyS"); strncpy(name + 4, str, sizeof(name) - 5); } else strncpy(name, str, sizeof(name) - 1); name[sizeof(name) - 1] = 0; if ((options = strchr(str, ',')) != NULL) *(options++) = 0;#ifdef __sparc__ if (!strcmp(str, "ttya")) strcpy(name, "ttyS0"); if (!strcmp(str, "ttyb")) strcpy(name, "ttyS1");#endif for(s = name; *s; s++) if (*s >= '0' && *s <= '9') break; idx = simple_strtoul(s, NULL, 10); *s = 0; /* * See if this tty is not yet registered, and * if we have a slot free. */ for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++) if (strcmp(console_cmdline[i].name, name) == 0 && console_cmdline[i].index == idx) { preferred_console = i; return 1; } if (i == MAX_CMDLINECONSOLES) return 1; preferred_console = i; c = &console_cmdline[i]; memcpy(c->name, name, sizeof(c->name)); c->options = options; c->index = idx; return 1;}__setup("console=", console_setup);/* * Commands to do_syslog: * * 0 -- Close the log. Currently a NOP. * 1 -- Open the log. Currently a NOP. * 2 -- Read from the log. * 3 -- Read all messages remaining in the ring buffer. * 4 -- Read and clear all messages remaining in the ring buffer * 5 -- Clear ring buffer. * 6 -- Disable printk's to console * 7 -- Enable printk's to console * 8 -- Set level of messages printed to console * 9 -- Return number of unread characters in the log buffer */int do_syslog(int type, char * buf, int len){ unsigned long i, j, limit, count; int do_clear = 0; char c; int error = 0; switch (type) { case 0: /* Close log */ break; case 1: /* Open log */ break; case 2: /* Read from log */ error = -EINVAL; if (!buf || len < 0) goto out; error = 0; if (!len) goto out; error = verify_area(VERIFY_WRITE,buf,len); if (error) goto out; error = wait_event_interruptible(log_wait, (log_start - log_end)); if (error) goto out; i = 0; spin_lock_irq(&logbuf_lock); while ((log_start != log_end) && i < len) { c = LOG_BUF(log_start); log_start++; spin_unlock_irq(&logbuf_lock); __put_user(c,buf); buf++; i++; spin_lock_irq(&logbuf_lock); } spin_unlock_irq(&logbuf_lock); error = i; break; case 4: /* Read/clear last kernel messages */ do_clear = 1; /* FALL THRU */ case 3: /* Read last kernel messages */ error = -EINVAL; if (!buf || len < 0) goto out; error = 0; if (!len) goto out; error = verify_area(VERIFY_WRITE,buf,len); if (error) goto out; count = len; if (count > LOG_BUF_LEN) count = LOG_BUF_LEN; spin_lock_irq(&logbuf_lock); if (count > logged_chars) count = logged_chars; if (do_clear) logged_chars = 0; limit = log_end; /* * __put_user() could sleep, and while we sleep * printk() could overwrite the messages * we try to copy to user space. Therefore * the messages are copied in reverse. <manfreds> */ for(i=0;i < count;i++) { j = limit-1-i; if (j+LOG_BUF_LEN < log_end) break; c = LOG_BUF(j); spin_unlock_irq(&logbuf_lock); __put_user(c,&buf[count-1-i]); spin_lock_irq(&logbuf_lock); } spin_unlock_irq(&logbuf_lock); error = i; if(i != count) { int offset = count-error; /* buffer overflow during copy, correct user buffer. */ for(i=0;i<error;i++) { __get_user(c,&buf[i+offset]); __put_user(c,&buf[i]); } } break; case 5: /* Clear ring buffer */ spin_lock_irq(&logbuf_lock); logged_chars = 0; spin_unlock_irq(&logbuf_lock); break; case 6: /* Disable logging to console */ spin_lock_irq(&logbuf_lock); console_loglevel = minimum_console_loglevel; spin_unlock_irq(&logbuf_lock); break; case 7: /* Enable logging to console */ spin_lock_irq(&logbuf_lock); console_loglevel = default_console_loglevel; spin_unlock_irq(&logbuf_lock); break; case 8: /* Set level of messages printed to console */ error = -EINVAL; if (len < 1 || len > 8) goto out; if (len < minimum_console_loglevel) len = minimum_console_loglevel; spin_lock_irq(&logbuf_lock); console_loglevel = len; spin_unlock_irq(&logbuf_lock); error = 0; break; case 9: /* Number of chars in the log buffer */ spin_lock_irq(&logbuf_lock); error = log_end - log_start; spin_unlock_irq(&logbuf_lock); break; default: error = -EINVAL; break; }out: return error;}asmlinkage long sys_syslog(int type, char * buf, int len){ if ((type != 3) && !capable(CAP_SYS_ADMIN)) return -EPERM; return do_syslog(type, buf, len);}/* * Call the console drivers on a range of log_buf */static void __call_console_drivers(unsigned long start, unsigned long end){ struct console *con; for (con = console_drivers; con; con = con->next) { if ((con->flags & CON_ENABLED) && con->write) con->write(con, &LOG_BUF(start), end - start); }}/* * Write out chars from start to end - 1 inclusive */static void _call_console_drivers(unsigned long start, unsigned long end, int msg_log_level){ if (msg_log_level < console_loglevel && console_drivers && start != end) { if ((start & LOG_BUF_MASK) > (end & LOG_BUF_MASK)) { /* wrapped write */ __call_console_drivers(start & LOG_BUF_MASK, LOG_BUF_LEN); __call_console_drivers(0, end & LOG_BUF_MASK); } else { __call_console_drivers(start, end); } }}/* * Call the console drivers, asking them to write out * log_buf[start] to log_buf[end - 1]. * The console_sem must be held. */static void call_console_drivers(unsigned long start, unsigned long end){ unsigned long cur_index, start_print; static int msg_level = -1; if (((long)(start - end)) > 0) BUG(); cur_index = start; start_print = start; while (cur_index != end) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -