Index: xen/Rules.mk =================================================================== --- xen.orig/Rules.mk +++ xen/Rules.mk @@ -8,6 +8,8 @@ perfc ?= n perfc_arrays?= n crash_debug ?= n +logdev ?= n + XEN_ROOT=$(BASEDIR)/.. include $(XEN_ROOT)/Config.mk @@ -57,6 +59,8 @@ CFLAGS-$(verbose) += -DVERBOSE CFLAGS-$(crash_debug) += -DCRASH_DEBUG CFLAGS-$(perfc) += -DPERF_COUNTERS CFLAGS-$(perfc_arrays) += -DPERF_ARRAYS +CFLAGS-$(logdev) += -DLOGDEV_PAGES=16 +CFLAGS-$(logdev) += -DCONFIG_LOGDEV ifneq ($(max_phys_cpus),) CFLAGS-y += -DMAX_PHYS_CPUS=$(max_phys_cpus) Index: xen/common/Makefile =================================================================== --- xen.orig/common/Makefile +++ xen/common/Makefile @@ -27,6 +27,7 @@ obj-y += xmalloc.o obj-$(perfc) += perfc.o obj-$(crash_debug) += gdbstub.o +obj-$(logdev) += logdev.o # Object file contains changeset and compiler information. version.o: $(BASEDIR)/include/xen/compile.h Index: xen/common/logdev.c =================================================================== --- /dev/null +++ xen/common/logdev.c @@ -0,0 +1,1117 @@ +/* + * logdev.c + * + * Copyright (C) 2006 Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License (not later!) + * + * 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 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static char *logdev_xen_version = "0.1.0"; + +/* + * Need an easier way to pass switches from the config files. + * + * Change to 1 to have the prints enabled on startup. + */ +#define LOGDEV_PRINT_ENABLED 0 + +static void *logdev_get_free_pages(int order) +{ + return xmalloc_bytes((1< 0 ? 1 : x < 0 ? -1 : 0; +} + +/* + * Xen needs to define this for all archs. + */ +#ifdef atomic_inc_return + +static atomic_t logdev_counter = ATOMIC_INIT(0); +#define logdev_counter_inc() atomic_inc_return(&logdev_counter); + +#else /* atomic_inc_return not defined */ + +static unsigned long logdev_counter; + +#ifdef __HAVE_ARCH_CMPXCHG + +static inline unsigned long logdev_counter_inc(void) +{ + unsigned long val; + unsigned long *p = &logdev_counter; + do { + val = *p; + } while(cmpxchg(p, val, val+1) != val); + return val; +} +#else +/* need to use spin locks to increment counter */ +static DEFINE_SPINLOCK(logdev_counter_spinlock) +/* Must have interrupts disabled */ +static inline unsigned long logdev_counter_inc(void) +{ + unsigned long val; + + spin_lock(&logdev_counter_spinlock); + val = logdev_counter++; + spin_unlock(&logdev_counter_spinlock); + return val; +} +#endif /* _HAVE_ARCH_CMPXCHG */ +#endif /* atomic_inc_return */ + + +/* + * Ring buffer structures + */ +struct logdev_entry { + unsigned int head; + unsigned int tail; + char *dat; +}; + +struct logdev_dev { + struct logdev_entry *entry; + int init; + int size; + int len; + int start; + int end; + int corrupted; + spinlock_t lock; +}; + +static DEFINE_PER_CPU(struct logdev_dev, logdev_dev); +#define get_logdev(cpu) &per_cpu(logdev_dev, cpu) + +#define LOGDEV_CPUS NR_CPUS + +#define LOGDEV_DEV_UNINITALIZED 0 +#define LOGDEV_DEV_RUNNING 1 +#define LOGDEV_DEV_SUSPENDED 2 + +#define dev_running(dev) ((dev)->init == LOGDEV_DEV_RUNNING) +#define dev_suspended(dev) ((dev)->init == LOGDEV_DEV_SUSPENDED) + +/* + * We don't support hotplug CPUS + */ +#define check_cpu(cpu) ({ \ + static int once = 1; \ + int x; \ + if (unlikely(x = (cpu >= LOGDEV_CPUS)) && once) { \ + once = 0; \ + printk("BUG %s:%d: cpu %d doesn't fit logdev cpus\n", \ + __FILE__, __LINE__, cpu); \ + } \ + x; \ + }) + +static int logdev_copy_from_dev(struct logdev_dev *dev, void *buf, + int size); +static int logdev_copy_to_dev(struct logdev_dev *dev, const void *dat, + int size); + +static int logdev_valid(int id) +{ + switch (id) { + case LOGDEV_CUSTOM: + case LOGDEV_PRINT: + case LOGDEV_PRINT_TIME: + case LOGDEV_PRINT_TIME_FUNC: + return 1; + } + return 0; +} + +/* + * The following is to register call back functions to print out + * a custom record. + */ + +static DEFINE_SPINLOCK(logdev_callbacks_lock); +static LIST_HEAD(logdev_callbacks); + +int logdev_register_callback(int custom_id, logdev_callback_func func) +{ + struct list_head *p; + struct logdev_callback *cb; + unsigned long flags; + int ret = 0; + + cb = xmalloc(struct logdev_callback); + if (!cb) { + ret = -ENOMEM; + goto out; + } + + spin_lock_irqsave(&logdev_callbacks_lock, flags); + list_for_each(p,&logdev_callbacks) { + struct logdev_callback *c = list_entry(p, struct logdev_callback, list); + if (c->id == custom_id) { + spin_unlock_irqrestore(&logdev_callbacks_lock,flags); + xfree(cb); + ret = -EBUSY; + goto out; + } + } + + cb->id = custom_id; + cb->func = func; + list_add(&cb->list, &logdev_callbacks); + spin_unlock_irqrestore(&logdev_callbacks_lock, flags); + + out: + return ret; +} + +int logdev_unregister_callback(int custom_id) +{ + struct list_head *p; + struct logdev_callback *cb; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&logdev_callbacks_lock, flags); + list_for_each(p,&logdev_callbacks) { + cb = list_entry(p, struct logdev_callback, list); + if (cb->id == custom_id) + break; + } + if (p == &logdev_callbacks) { + ret = -ENODEV; + spin_unlock_irqrestore(&logdev_callbacks_lock,flags); + goto out; + } + + list_del(&cb->list); + spin_unlock_irqrestore(&logdev_callbacks_lock, flags); + + xfree(cb); + + out: + return ret; +} + +/* + * We have a separate kernel buffer for each CPU. + * This buffer is used to copy snprintf data into the ring buffer. + */ +static char kern_buffer[LOGDEV_CPUS][PAGE_SIZE] __cacheline_aligned ; + +/* + * logdev_print acts like printk but it writes to the logdev device instead + * of a console. + */ +int logdev_vprint(const char *str, va_list va) +{ + struct logdev_dev *dev; + char *buffer; + int len=0; + struct logdev_header hdr; + struct logdev_print rs; + unsigned long flags; + int cpu; + + local_irq_save(flags); + + cpu = smp_processor_id(); + + if (check_cpu(cpu)) + goto out; + + dev = get_logdev(cpu); + if (!dev_running(dev)) + goto out; + + buffer = kern_buffer[cpu]; + len = vsnprintf(buffer, PAGE_SIZE, str, va); + + if (len >= PAGE_SIZE) { + buffer[PAGE_SIZE-1] = 0; + len = PAGE_SIZE; + } + + hdr.counter = logdev_counter_inc(); + hdr.id = LOGDEV_PRINT; + hdr.size = sizeof(hdr) + sizeof(rs) + len; + + spin_lock(&dev->lock); + logdev_copy_to_dev(dev, &hdr, sizeof(hdr)); + if (sizeof(rs)) + logdev_copy_to_dev(dev, &rs, sizeof(rs)); + logdev_copy_to_dev(dev, buffer, len); + spin_unlock(&dev->lock); + + out: + local_irq_restore(flags); + + return len; +} + +int logdev_print(const char *str, ...) +{ + va_list va; + struct logdev_dev *dev; + char *buffer; + int len=0; + struct logdev_header hdr; + struct logdev_print rs; + unsigned long flags; + int cpu; + + local_irq_save(flags); + + cpu = smp_processor_id(); + + if (check_cpu(cpu)) + goto out; + + dev = get_logdev(cpu); + if (!dev_running(dev)) + goto out; + + buffer = kern_buffer[cpu]; + + va_start(va,str); + len = vsnprintf(buffer, PAGE_SIZE, str, va); + va_end(va); + + if (len >= PAGE_SIZE) { + buffer[PAGE_SIZE-1] = 0; + len = PAGE_SIZE; + } + + hdr.counter = logdev_counter_inc(); + hdr.id = LOGDEV_PRINT; + hdr.size = sizeof(hdr) + sizeof(rs) + len; + + spin_lock(&dev->lock); + logdev_copy_to_dev(dev, &hdr, sizeof(hdr)); + if (sizeof(rs)) + logdev_copy_to_dev(dev, &rs, sizeof(rs)); + logdev_copy_to_dev(dev, buffer, len); + spin_unlock(&dev->lock); + + out: + local_irq_restore(flags); + + return len; +} + +/* + * logdev_print_time is the same as logdev_print but it attaches a timestamp to it. + * saves on doing it yourself. + */ +int logdev_print_time(const char *str, ...) +{ + struct logdev_dev *dev; + char *buffer; + va_list va; + int len = 0; + struct logdev_header hdr; + struct logdev_print_time rs; + unsigned long flags; + int cpu; + + local_irq_save(flags); + + cpu = smp_processor_id(); + + if (check_cpu(cpu)) + goto out; + + dev = get_logdev(cpu); + if (!dev_running(dev)) + goto out; + + buffer = kern_buffer[cpu]; + + va_start(va,str); + len = vsnprintf(buffer, PAGE_SIZE, str, va); + va_end(va); + + if (len >= PAGE_SIZE) { + buffer[PAGE_SIZE-1] = 0; + len = PAGE_SIZE; + } + + rs.t = NOW(); + + hdr.counter = logdev_counter_inc(); + hdr.id = LOGDEV_PRINT_TIME; + hdr.size = sizeof(hdr) + sizeof(rs) + len; + + spin_lock(&dev->lock); + logdev_copy_to_dev(dev, &hdr, sizeof(hdr)); + logdev_copy_to_dev(dev, &rs, sizeof(rs)); + logdev_copy_to_dev(dev, buffer, len); + spin_unlock(&dev->lock); + + out: + local_irq_restore(flags); + + return len; +} + +/* + * logdev_print_time_func quickly stores the time, function and line number. + * this is really only good for live runs since the function is just a pointer, + * so a user land process would need to have the System.map available. + */ +int logdev_print_time_func(const char *file, int line, const char *str, ...) +{ + struct logdev_dev *dev; + char *buffer; + va_list va; + int len = 0; + struct logdev_header hdr; + struct logdev_print_time_func rs; + unsigned long flags; + int cpu; + + local_irq_save(flags); + + cpu = smp_processor_id(); + + if (check_cpu(cpu)) + goto out; + + dev = get_logdev(cpu); + if (!dev_running(dev)) + goto out; + + buffer = kern_buffer[cpu]; + + va_start(va,str); + len = vsnprintf(buffer, PAGE_SIZE, str, va); + va_end(va); + + if (len >= PAGE_SIZE) { + buffer[PAGE_SIZE-1] = 0; + len = PAGE_SIZE; + } + + rs.t = NOW(); + rs.file = file; + rs.line = line; + + hdr.counter = logdev_counter_inc(); + hdr.id = LOGDEV_PRINT_TIME_FUNC; + hdr.size = sizeof(hdr) + sizeof(rs) + len; + + spin_lock(&dev->lock); + logdev_copy_to_dev(dev, &hdr, sizeof(hdr)); + logdev_copy_to_dev(dev, &rs, sizeof(rs)); + logdev_copy_to_dev(dev, buffer, len); + spin_unlock(&dev->lock); + + out: + local_irq_restore(flags); + + return len; +} + +/* + * If you feel like recording your own data, you can use logdev_record. + * just pass your own id, size and data. The size is the size of + * the data being passed and not the size actually being written to the device. + * That is already calculated. + * + * This record will be added as LOGDEV_CUSTOM and the given id will be the custom id. + * + * We use total_size so that we can avoid multiple copies to get the data + * into the buffer. + */ +int logdev_record(int id, int total_size, const void *data, int size, ...) +{ + struct logdev_dev *dev; + struct logdev_header hdr; + struct logdev_custom rs; + unsigned long flags; + int tsize = size; + int cpu; + int ret = 0; + va_list ap; + + if (!data) + return -EINVAL; + + local_irq_save(flags); + + cpu = smp_processor_id(); + + if (check_cpu(cpu)) + goto out; + + dev = get_logdev(cpu); + if (!dev_running(dev)) + goto out; + + hdr.counter = logdev_counter_inc(); + hdr.id = LOGDEV_CUSTOM; + hdr.size = sizeof(hdr) + sizeof(rs) + total_size; + + rs.id = id; + + spin_lock(&dev->lock); + logdev_copy_to_dev(dev, &hdr, sizeof(hdr)); + logdev_copy_to_dev(dev, &rs, sizeof(rs)); + va_start(ap, size); + do { + ret = logdev_copy_to_dev(dev, data, size); + if (tsize > total_size) + break; + data = va_arg(ap, void *); + if (data) { + size = va_arg(ap, int); + tsize += size; + if (tsize > total_size) + size -= tsize - total_size; + } + } while (data); + va_end(ap); + + /* + * If total_size didn't equal all sizes, then write padding. + */ + while (tsize < total_size) { + int cnt = total_size - tsize; + if (cnt > PAGE_SIZE) + cnt = PAGE_SIZE; + logdev_copy_to_dev(dev, kern_buffer[cpu], cnt); + tsize += cnt; + } + + spin_unlock(&dev->lock); + +out: + local_irq_restore(flags); + + return ret; +} + +/* + * Used to let other funcs know we are dumping. + */ +int in_logdump; + +static int get_next_cpus(struct logdev_header *hdr, + int *_this_cpu, int *_next_cpu) +{ + int cpu; + int f = 0; + struct logdev_dev *dev; + int this_cpu = -1; + int next_cpu = -1; + int last_cpu = 1; + + for_each_present_cpu(cpu) { + + if (cpu >= LOGDEV_CPUS) + break; + + dev = get_logdev(cpu); + if (!dev_suspended(dev)) + continue; + + /* Skip empty buffers */ + if (hdr[cpu].id == LOGDEV_HDR_DONE) + continue; + + /* + * If the header is corrupted, just pick it + * as if this buffer was the last. The corruption + * will break out of the loop in the flush. + */ + if (!logdev_valid(hdr[cpu].id)) { + this_cpu = cpu; + next_cpu = -1; + last_cpu = 1; + break; + } + + /* + * If this is the first cpu, then use it, otherwise, + * compare. + */ + if (!f || + compare_cnt(hdr[cpu].counter, + hdr[this_cpu].counter) < 0) { + if (!f) + f = 1; + else { + /* we already have the first count */ + next_cpu = this_cpu; + last_cpu = 0; + f = 2; + } + this_cpu = cpu; + } else if (f == 1 || + compare_cnt(hdr[cpu].counter, + hdr[next_cpu].counter) < 0) { + f = 2; + next_cpu = cpu; + last_cpu = 0; + } + } + + *_this_cpu = this_cpu; + *_next_cpu = next_cpu; + + return last_cpu; +} + +static int process_log(struct logdev_dev *dev, struct logdev_header *hdr, + int cpu) +{ + int i; + int r; + int count; + int corrupt = 0; + int line = 0; + int newline = 1; + unsigned long long t; + unsigned long usec_rem; + unsigned long secs; + const char *file = NULL; + + r = sizeof(hdr[0]); + + switch (hdr[cpu].id) { + + case LOGDEV_PRINT_TIME_FUNC: + { + struct logdev_print_time_func rs; + int cap = sizeof(rs) - sizeof(struct logdev_print_time); + + logdev_copy_from_dev(dev, &rs, cap); + file = rs.file; + line = rs.line; + + r += cap; + + /* fall through */ + } + + case LOGDEV_PRINT_TIME: + { + struct logdev_print_time rs; + + logdev_copy_from_dev(dev, &rs, sizeof(rs)); + + if (newline) { + t = rs.t; + usec_rem = do_div(t, 1000000000)/1000; + secs = (unsigned long)t; + + printk("[%5lu.%06lu] ", + secs, usec_rem); + } + + r += sizeof(rs); + + /* fall through */ + } + + case LOGDEV_PRINT: + if (newline) { + printk("cpu:%d ",cpu); + + if (hdr[cpu].id == LOGDEV_PRINT_TIME_FUNC) + printk("%s:%d ",file, line); + } + for (i=r; i < hdr[cpu].size; i += r) { + count = hdr[cpu].size - i; + if (count > PAGE_SIZE-1) + count = PAGE_SIZE-1; + r = logdev_copy_from_dev(dev, kern_buffer[cpu], count); + if (r < 0) + break; + kern_buffer[cpu][count] = 0; + printk("%s", kern_buffer[cpu]); + newline = (count) && + (kern_buffer[cpu][count - 1] != '\n') ? 0 : 1; + } + break; + + case LOGDEV_CUSTOM: + { + struct list_head *p; + struct logdev_custom custom; + int len; + + logdev_copy_from_dev(dev,&custom,sizeof(custom)); + + spin_lock(&logdev_callbacks_lock); + i = len = hdr[cpu].size - sizeof(hdr[0]) - sizeof(custom); + if (i > PAGE_SIZE) { + i = PAGE_SIZE; + /* show that we truncated */ + hdr[cpu].size -= len - i; + } + logdev_copy_from_dev(dev,kern_buffer[cpu],i); + + list_for_each(p, &logdev_callbacks) { + struct logdev_callback *cb = list_entry(p, struct logdev_callback, list); + if (cb->id == custom.id) { + cb->func(&hdr[cpu], &custom, cpu, kern_buffer[cpu]); + break; + } + } + + /* No record should be bigger than a page. Ignore all else */ + while (i < len) { + int count = len - i; + if (count > PAGE_SIZE) + count = PAGE_SIZE; + logdev_copy_from_dev(dev,kern_buffer[cpu],count); + i += count; + } + + /* check if we didn't find a call back */ + if (p == &logdev_callbacks) { + printk("skipping! LOGDEV_CUSTOM id %d\n",custom.id); + } + + spin_unlock(&logdev_callbacks_lock); + break; + } + default: + corrupt = 1; + if (!dev->corrupted) { + dev->corrupted = 1; + printk(">>>>> Unknown logdev header, cpu %d buffer may be " + "corrupted from this point on\n", cpu); + } else + printk("CPU %d >>> corrupted header <<<\n", cpu); + break; + } + + return corrupt; +} + +static int flush_buffer(struct logdev_dev *dev, struct logdev_header *hdr, + int cpu, int next_cpu, int last_cpu) +{ + int corrupt = 0; + int more_work = 1; + + while (last_cpu || compare_cnt(hdr[cpu].counter, + hdr[next_cpu].counter) <= 0) { + + BUG_ON(hdr[cpu].id == LOGDEV_HDR_DONE); + + corrupt = process_log(dev, hdr, cpu); + + /* Read the next header for this */ + if ((logdev_copy_from_dev(dev,&hdr[cpu],sizeof(hdr[0]))) + != sizeof(hdr[0])) { + hdr[cpu].id = LOGDEV_HDR_DONE; + if (last_cpu) + more_work = 0; + break; + } + /* if we have a corrupted header, then stop this buffer flush. */ + if (corrupt) + break; + } + + return more_work; +} + +void logdev_dump(void) +{ + int save_print; + struct logdev_dev *dev; + struct logdev_header hdr[LOGDEV_CPUS]; + static int started = 0; + extern int in_logdump; + unsigned long flags; + int more_work = 0; + int cpu; + + /* + * We don't care about race conditions with this started variable. + * It only exists to keep dumps a little cleaner. If two dumps get + * through at the same time, it doesn't hurt. + */ + if (started) + return; + + started = 1; + + /* Because of the started race, we also use in_logdump just for reference.*/ + in_logdump++; + + printk("*** Starting Logdev Dump ***\n"); + local_irq_save(flags); + + watchdog_disable(); + + /* + * This is for debugging, so we don't want to reintroduce more output. + */ + save_print = logdev_print_ison(); + logdev_print_off(); + + + /* + * Read all the available headers for each CPU. + */ + for_each_present_cpu(cpu) { + + if (cpu >= LOGDEV_CPUS) + break; + + dev = get_logdev(cpu); + if (!dev_running(dev)) + continue; + + if (dev->corrupted) { + printk("Warning buffer for CPU %d is corrupted\n", + cpu); + /* will be set when corrupted part is read. */ + dev->corrupted = 0; + } + + /* + * Long time to hold the spin locks, but hey it's just debugging. + */ + spin_lock(&dev->lock); + + /* + * Try to limit the amount added while reading + * this buffer, suspend the buffer. (redundant but also good for + * accounting) + */ + dev->init = LOGDEV_DEV_SUSPENDED; + + if ((logdev_copy_from_dev(dev, &hdr[cpu], sizeof(hdr[0]))) + == sizeof(hdr[0])) + /* record that we have a buffer to work with. */ + more_work = 1; + else + /* record that the buffer is empty */ + hdr[cpu].id = LOGDEV_HDR_DONE; + } + + while (more_work) { + int this_cpu; + int next_cpu; + int last_cpu; + + /* + * Find the cpu to work with that has the earliest counter, + * and also the cpu with the next counter. + */ + last_cpu = get_next_cpus(hdr, &this_cpu, &next_cpu); + + cpu = this_cpu; + dev = get_logdev(cpu); + + BUG_ON(this_cpu < 0); + BUG_ON(!last_cpu && next_cpu < 0); + BUG_ON(dev->init != LOGDEV_DEV_SUSPENDED); + BUG_ON(hdr[cpu].id == LOGDEV_HDR_DONE); + + /* + * Now print out all from this buffer until we reach + * the next cpu. If this is the last buffer to write + * then finish the buffer. + */ + more_work = flush_buffer(dev, hdr, cpu, next_cpu, last_cpu); + } + + printk( ">>>>> done <<<<<\n"); + + for_each_present_cpu(cpu) { + if (cpu >= LOGDEV_CPUS) + break; + dev = get_logdev(cpu); + if (dev_suspended(dev)) { + dev->init = LOGDEV_DEV_RUNNING; + /* buffers should be emptied */ + dev->corrupted = 0; + spin_unlock(&dev->lock); + } + + } + if (save_print) + logdev_print_on(); + + watchdog_enable(); + + local_irq_restore(flags); + started = 0; + in_logdump--; +} + + +#define ENTRY_SIZE(e) ((((e)->tail - (e)->head)) & (PAGE_SIZE-1)) +#define ENTRY_FREE(e) ((PAGE_SIZE-1) - ENTRY_SIZE(e)) +#define ENTRY_ADD(e,x) ((e) = ((e) + x) & (PAGE_SIZE-1)) +#define ENTRY_INC(e) ENTRY_ADD(e,1) +#define ENTRY_MAX (PAGE_SIZE-1) + +static int logdev_copy_from_dev(struct logdev_dev *dev, void *buf, int size) +{ + struct logdev_entry *entry = &dev->entry[dev->start]; + int ret = 0; + + if (size < 0) { + printk("logdev_copy_from_dev: size < 0 ???\n"); + return -1; + } + if (size > dev->size) + size = dev->size; + + while (size && dev->size) { + int copy = size; + int used; + + if (!ENTRY_SIZE(entry)) { + dev->start = (dev->start + 1) % dev->len; + entry = &dev->entry[dev->start]; + } + + if (copy > (used=ENTRY_SIZE(entry))) + copy = used; + if (entry->head+copy > PAGE_SIZE) + copy = PAGE_SIZE - entry->head; + memcpy(buf,entry->dat+entry->head,copy); + ENTRY_ADD(entry->head,copy); + buf += copy; + dev->size -= copy; + size -= copy; + ret += copy; + + } + + return ret; +} + +static void move_start_to_next_entry(struct logdev_dev *dev) +{ + struct logdev_entry *entry = &dev->entry[dev->start]; + int start = dev->start; + int size; + struct logdev_header hdr; + + if (dev->corrupted) { + /* Don't trust headers, just skip to the next entry */ + dev->size -= ENTRY_SIZE(entry); + entry->head = entry->tail = 0; + dev->start = (dev->start+1) % dev->len; + return; + } + + while (start == dev->start) { + logdev_copy_from_dev(dev,(char*)&hdr,sizeof(hdr)); + entry = &dev->entry[dev->start]; + + if (!logdev_valid(hdr.id)) { + dev->corrupted = 1; + if (start == dev->start) + move_start_to_next_entry(dev); + return; + } + + size = sizeof(hdr); + while (size < hdr.size) { + int count = hdr.size - size; + if (count > ENTRY_SIZE(entry)) + count = ENTRY_SIZE(entry); + dev->size -= count; + size += count; + ENTRY_ADD(entry->head,count); + if (ENTRY_SIZE(entry) == 0) { + dev->start = (dev->start + 1) % dev->len; + entry->head = entry->tail = 0; + entry = &dev->entry[dev->start]; + } + } + } +} + +static int logdev_copy_to_dev(struct logdev_dev *dev, const void *dat, + int size) +{ + struct logdev_entry *entry = &dev->entry[dev->end]; + const char *buf = dat; + int ret = 0; + + while (size) { + int copy; + int free; + if (ENTRY_SIZE(entry) == ENTRY_MAX) { + dev->end = (dev->end+1) % dev->len; + entry = &dev->entry[dev->end]; + /* if we wrapped, then clear out this entire + * buffer. + */ + if (dev->end == dev->start) { + move_start_to_next_entry(dev); + } + } + copy = size; + if (copy > (free=ENTRY_FREE(entry))) + copy = free; + if (entry->tail+copy > PAGE_SIZE) + copy = PAGE_SIZE - entry->tail; + memcpy(entry->dat+entry->tail,buf,copy); + ENTRY_ADD(entry->tail,copy); + buf += copy; + size -= copy; + ret += copy; + dev->size += copy; + + } + + return ret; + +} + + +void logdev_cleanup(void) +{ + int i; + int cpu; + struct logdev_dev *dev; + + for_each_present_cpu(cpu) { + + if (cpu >= LOGDEV_CPUS) + break; + + dev = get_logdev(cpu); + if (!dev->init) + continue; + + if (dev->entry) { + struct logdev_entry *entry; + for (i=0, entry=dev->entry; ilen; i++,entry++) + if (entry->dat) { + logdev_free_page((unsigned long)entry->dat); + entry->dat = NULL; + } + dev->len = 0; + xfree(dev->entry); + dev->entry = NULL; + } + } +} + +int logdev_init(void) +{ + int res = 0; + int cpu; + struct logdev_entry *entry; + static int init = 0; + + if (init) + return 0; + + init = 1; + + printk("Logdevice: copyright Steven Rostedt." + " (Version %s)\n", + logdev_xen_version); + + for_each_present_cpu(cpu) { + int i; + struct logdev_dev *dev; + + if (cpu >= LOGDEV_CPUS) { + printk(KERN_WARNING "More present cpus (%d) than NR_CPUS (%d)\n", + cpu,LOGDEV_CPUS); + break; + } + + printk("Initializing logdev for cpu: %d\n",cpu); + + dev = get_logdev(cpu); + dev->len = pages; + + res = -ENOMEM; + + dev->entry = xmalloc_array(struct logdev_entry, dev->len); + if (!dev->entry) + goto fail; + + memset(dev->entry,0,sizeof(*dev->entry)*dev->len); + + for (i=0,entry=dev->entry; ilen; i++,entry++) { + entry->dat = logdev_get_free_pages(0); + if (!entry->dat) + goto fail; + } + spin_lock_init(&dev->lock); + + dev->init = LOGDEV_DEV_RUNNING; + } + + res = 0; + +#if LOGDEV_PRINT_ENABLED + logdev_print_on(); +#endif + +out: + return res; + +fail: + logdev_cleanup(); + goto out; +} + +__initcall(logdev_init); Index: xen/include/xen/logdev.h =================================================================== --- /dev/null +++ xen/include/xen/logdev.h @@ -0,0 +1,168 @@ +/* + * Logdevice - A device used to record debuging information in the kernel. + * It uses a large memory ring buffer consisting of individual pages + * to keep down on hogging large sections. A user may then read the device + * to get debugging information out of it. Or if configured, this can + * be dumped to the network on a system crash. + * + * Copyright - 2006 - Steven Rostedt + */ +#ifndef _LOG_DEV_H +#define _LOG_DEV_H + +#include +#include +#include +#include + +/* Random numbers out of my head used for MAGIC */ +#define LOGDEV_CUSTOM 0x1afb +#define LOGDEV_PRINT 0x4adb +#define LOGDEV_PRINT_TIME 0x4adc +#define LOGDEV_PRINT_TIME_FUNC 0x4add +#define LOGDEV_HDR_DONE 0xbbbb /* internal use only */ + +struct logdev_print { + char str[0]; +}; + +struct logdev_print_time { + unsigned long long t; + char str[0]; +}; + +struct logdev_print_time_func { + const char *file; + int line; + /* need to be after line, since we use this with print_time. */ + unsigned long long t; + char str[0]; +}; + +struct logdev_custom { + int id; + char data[0]; +}; + +struct logdev_header { + long counter; + int id; + int size; +}; + +struct logdev_item { + struct logdev_header hdr; + union { + struct logdev_print print; + struct logdev_print_time print_time; + struct logdev_print_time_func print_time_func; + struct logdev_custom custom; + char data[0]; + } u; +}; + +#ifdef CONFIG_LOGDEV + +extern int in_logdump; + +/* + * Right now we only have one switch, but this may change + * in the future. + */ +extern unsigned long logdev_switches; +#define LOGDEV_SW_PRINT_ENABLED 1 + +#define LOGDEV_SW_ISSET(sw) (test_bit(sw, &logdev_switches)) +#define LOGDEV_SW_SET(sw) (set_bit(sw, &logdev_switches)) +#define LOGDEV_SW_CLEAR(sw) (clear_bit(sw, &logdev_switches)) + +#define logdev_print_ison() LOGDEV_SW_ISSET(LOGDEV_SW_PRINT_ENABLED) +#define logdev_print_on() LOGDEV_SW_SET(LOGDEV_SW_PRINT_ENABLED); +#define logdev_print_off() LOGDEV_SW_CLEAR(LOGDEV_SW_PRINT_ENABLED); + +typedef void (*logdev_callback_func)(struct logdev_header *hdr, + struct logdev_custom *custom, + int cpu, + void *rec); + +int logdev_print(const char *str, ...) + __attribute__ ((format (printf, 1, 2))); +int logdev_vprint(const char *str, va_list va); +int logdev_print_time(const char *str, ...) + __attribute__ ((format (printf, 1, 2))); +int logdev_print_time_func(const char *file, int line, const char *str, ...) + __attribute__ ((format (printf, 3, 4))); + +/* + * logdev_record is used for custom writes (saves of sprintf) + * use id and register a callback so that the logdump knows + * what to do when it sees this record. + * You can pass in multiple data structures, just end the function + * parameters with a NULL. + */ +int logdev_record(int id, int total_size, const void *data, int size, ...); +void logdev_dump(void); + +#define LOGDEV(x,y...) logdev_##x(y) +#define LOGPRINTS(func,x...) do { if (logdev_print_ison()) LOGDEV(func,x); } while(0) + +/* + * Using the UPPER case here ignores the logdev_print_enabled flag + */ +#define LOGPRINT(x...) LOGDEV(print,x) +#define LOGTPRINT(x...) LOGDEV(print_time,x) +#define LOGTFPRINT(x...) LOGDEV(print_time_func,__FUNCTION__,__LINE__,x) + +/* + * Using these functions, will only log if logdev_print_enabled flag is set. + */ + +#define lprint(x...) LOGPRINTS(print,x) +#define ltprint(x...) LOGPRINTS(print_time,x) +#define lfprint(x...) LOGPRINTS(print_time_func,__FUNCTION__,__LINE__,x) + +/* + * lfnprint is identical to lfprint except that it adds a new line at the end. + */ +#define _lnprint(func,x,y...) func( x "%s\n", y) +#define lnprint(x...) _lnprint(lprint,x,"") +#define ltnprint(x...) _lnprint(ltprint,x,"") +#define lfnprint(x...) _lnprint(lfprint,x,"") + +struct logdev_callback { + struct list_head list; + int id; + logdev_callback_func func; +}; + +int logdev_register_callback(int custom_id, logdev_callback_func func); +int logdev_unregister_callback(int custom_id); + +#else /* !LOGDEV */ +#define logdev_dump() do {} while(0) +#define logdev_record(id, size, data) do {} while(0) + +#define LOGPRINT(x...) do {} while(0) +#define LOGTPRINT(x...) do {} while(0) +#define LOGTFPRINT(x...) do {} while(0) + +#define logdev_print_ison() ( 0 ) +#define logdev_print_on() do {} while(0) +#define logdev_print_off() do {} while(0) + +#define logdev_print(x...) do {} while(0) + +#define lprint(x...) do {} while(0) +#define ltprint(x...) do {} while(0) +#define lfprint(x...) do {} while(0) + +#define lnprint(x...) do {} while(0) +#define ltnprint(x...) do {} while(0) +#define lfnprint(x...) do {} while(0) + +#define logdev_register_callback(i,f) do {} while(0) +#define logdev_unregister_callback(i) do {} while(0) +#define in_logdump 0 +#endif /* LOGDEV */ + +#endif Index: xen/common/keyhandler.c =================================================================== --- xen.orig/common/keyhandler.c +++ xen/common/keyhandler.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +88,22 @@ static void show_handlers(unsigned char key_table[i].desc); } +static void log_dump(unsigned char key) +{ + logdev_dump(); +} + +static void log_toggle(unsigned char key) +{ + if (logdev_print_ison()) { + printk("turning off logdev\n"); + logdev_print_off(); + } else { + printk("turning on logdev\n"); + logdev_print_on(); + } +} + static void __dump_execstate(void *unused) { dump_execution_state(); @@ -273,6 +290,11 @@ void initialize_keytable(void) register_keyhandler( 't', read_clocks, "display multi-cpu clock info"); + register_keyhandler( + 'l', log_toggle, "toggle logger"); + register_keyhandler( + 'L', log_dump, "dump the debug logger"); + #ifdef PERF_COUNTERS register_keyhandler( 'p', perfc_printall, "print performance counters"); Index: xen/drivers/char/console.c =================================================================== --- xen.orig/drivers/char/console.c +++ xen/drivers/char/console.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -621,6 +622,8 @@ void panic(const char *fmt, ...) void __bug(char *file, int line) { + logdev_print_off(); + logdev_dump(); console_start_sync(); debugtrace_dump(); printk("BUG at %s:%d\n", file, line);