>From 1fd1215cc56a52109d6df1a2302b391511d3d91b Mon Sep 17 00:00:00 2001
From: Feng(Eric) Liu <[EMAIL PROTECTED]>
Date: Sat, 15 Mar 2008 08:09:24 -0400
Subject: [PATCH] KVM: Porting xentrace to kvm. The trace data is
outputted
into the trace buffer managed as ring buffer for per cpu,
and the userspace tool can get the mapped virtual address
of the buffer to access it.

Signed-off-by: Feng(Eric) Liu <[EMAIL PROTECTED]>
---
 virt/kvm/kvm_trace.c |  353
++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 353 insertions(+), 0 deletions(-)
 create mode 100644 virt/kvm/kvm_trace.c

diff --git a/virt/kvm/kvm_trace.c b/virt/kvm/kvm_trace.c
new file mode 100644
index 0000000..ebb132c
--- /dev/null
+++ b/virt/kvm/kvm_trace.c
@@ -0,0 +1,353 @@
+/*
+ * kvm Trace Buffer
+ *
+ * it is designed to allow debugging traces of kvm to be generated
+ * on UP / SMP machines.  Each trace entry is timestamped so that it's
+ * possible to reconstruct a chronological record of trace events.
+ * The code is based on Xen.
+ *
+ * Copyright (C) 2004 by Intel Research Cambridge
+ *
+ * Authors: Mark Williamson, [EMAIL PROTECTED]
+ *          Rob Gardner, [EMAIL PROTECTED]
+ *          Eric Liu, [EMAIL PROTECTED]
+ *
+ * Date:    Feb 2008
+ *
+ */
+
+#include <linux/mman.h>
+#include <linux/pagemap.h>
+#include <linux/module.h>
+#include <asm/percpu.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <linux/kvm_host.h>
+
+#define KVM_LOST_REC_SIZE    8
+#define KVM_TRC_EVENT_SIZE   4
+#define KVM_TRC_TSC_SIZE     8
+
+int kvm_trace_enable_flag __read_mostly;
+EXPORT_SYMBOL_GPL(kvm_trace_enable_flag);
+
+static struct task_struct *kvm_trace_cons_task;
+static struct kvm_trace_info kvm_cur_tinfo;
+static unsigned long kvm_tbuf_size;
+static unsigned long kvm_pcpu_tdata_size;
+static unsigned long kvm_trace_rawbuf;
+
+static DEFINE_PER_CPU(struct kvm_trace_buf *, t_bufs);
+static DEFINE_PER_CPU(unsigned long, lost_records);
+
+/* High water mark for trace buffers; */
+static int t_buf_highwater;
+
+int kvm_enable_trace_buf(struct kvm_trace_info *t_info)
+{
+       int           i;
+       unsigned long addr;
+       unsigned long size;
+       struct        kvm_trace_buf *buf;
+       unsigned long pcpu_size;
+
+       t_info->ncpus = num_online_cpus();
+
+       /*
+        * Trace buffer cannot be resized, if it has
+        * already been set in this module cycle.
+        */
+       if (kvm_cur_tinfo.pcpu_pages) {
+               if (t_info->ncpus > kvm_cur_tinfo.ncpus)
+                       t_info->ncpus = kvm_cur_tinfo.ncpus;
+               t_info->pcpu_pages = kvm_cur_tinfo.pcpu_pages;
+               return 0;
+       }
+
+       pcpu_size = t_info->pcpu_pages * PAGE_SIZE;
+       if (pcpu_size == 0)
+               return -EINVAL;
+
+       size = kvm_tbuf_size = t_info->ncpus * pcpu_size;
+       kvm_trace_rawbuf = __get_free_pages(GFP_KERNEL | __GFP_ZERO,
+                                           get_order(size));
+
+       if (!kvm_trace_rawbuf)
+               return -ENOMEM;
+
+       addr = kvm_trace_rawbuf;
+       while ((long)size > 0) {
+               SetPageReserved(virt_to_page((void *)addr));
+               addr += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+
+       for_each_online_cpu(i) {
+               buf = per_cpu(t_bufs, i) =
+               (struct kvm_trace_buf *)(kvm_trace_rawbuf + i *
pcpu_size);
+               buf->cons = buf->prod = 0;
+       }
+
+       kvm_cur_tinfo.pcpu_pages = t_info->pcpu_pages;
+       kvm_cur_tinfo.ncpus = t_info->ncpus;
+       kvm_pcpu_tdata_size = pcpu_size - sizeof(struct kvm_trace_buf);
+       t_buf_highwater = kvm_pcpu_tdata_size >> 1; /* 50% high water */
+
+       return 0;
+}
+
+unsigned long kvm_mmap_trace_buf(void)
+{
+       unsigned long vaddr;
+
+       down_write(&current->mm->mmap_sem);
+       vaddr = do_mmap(NULL, 0, kvm_tbuf_size, PROT_READ | PROT_WRITE,
+                       MAP_SHARED | MAP_ANONYMOUS, 0);
+       if (vaddr) {
+               unsigned long page;
+               struct vm_area_struct *vma = find_vma(current->mm,
vaddr);
+
+               if (!vma) {
+                       up_write(&current->mm->mmap_sem);
+                       return 0;
+               }
+
+               page = virt_to_phys((void *)kvm_trace_rawbuf);
+               if (remap_pfn_range(vma, vaddr, page >> PAGE_SHIFT,
+                                   kvm_tbuf_size, PAGE_SHARED))
+                       vaddr = 0;
+       }
+       up_write(&current->mm->mmap_sem);
+
+       return vaddr;
+}
+
+static void free_kvmtrace_buf(void)
+{
+       unsigned long addr = kvm_trace_rawbuf;
+       unsigned long size = kvm_tbuf_size;
+
+       if (!addr)
+               return;
+
+       while ((long)size > 0) {
+               ClearPageReserved(virt_to_page((void *)addr));
+               free_page(addr);
+               addr += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+}
+
+int kvm_unmmap_trace_buf(void)
+{
+       int ret;
+
+       down_write(&current->mm->mmap_sem);
+       ret = do_munmap(current->mm, kvm_cur_tinfo.userspace_addr,
+                       kvm_tbuf_size);
+       up_write(&current->mm->mmap_sem);
+       return ret;
+}
+
+static inline int calc_rec_size(int cycles, int extra)
+{
+       int rec_size = KVM_TRC_EVENT_SIZE;
+
+       rec_size += extra;
+       return cycles ? rec_size += KVM_TRC_TSC_SIZE : rec_size;
+}
+
+static inline int calc_bytes_to_wrap(struct kvm_trace_buf *buf)
+{
+       return kvm_pcpu_tdata_size - (buf->prod % kvm_pcpu_tdata_size);
+}
+
+static inline unsigned calc_bytes_avail(struct kvm_trace_buf *buf)
+{
+       return kvm_pcpu_tdata_size - (buf->prod - buf->cons);
+}
+
+static inline int __insert_record(struct kvm_trace_buf *buf,
+                                 unsigned long event, int extra, int
cycles,
+                                 int rec_size, unsigned char
*extra_data)
+{
+       struct kvm_trace_rec *rec;
+       unsigned char *dst;
+       unsigned char *t_data = __get_cpu_var(t_bufs)->data;
+       unsigned prod = buf->prod % kvm_pcpu_tdata_size;
+       unsigned long extra_word = extra/sizeof(u32);
+       int local_rec_size = calc_rec_size(cycles, extra);
+
+       BUG_ON(local_rec_size != rec_size);
+
+       if (calc_bytes_avail(buf) < rec_size)
+               return 0;
+
+       rmb();
+       rec = (struct kvm_trace_rec *)(t_data + prod);
+       rec->event = event;
+       rec->extra_u32 = extra_word;
+       dst = (unsigned char *)rec->u.nocycles.extra_u32;
+       rec->cycles_in = cycles;
+
+       if (rec->cycles_in != 0) {
+               u64 tsc = 0;
+               rdtscll(tsc);
+               rec->u.cycles.cycles_lo = (uint32_t)tsc;
+               rec->u.cycles.cycles_hi = (uint32_t)(tsc >> 32);
+               dst = (unsigned char *)rec->u.cycles.extra_u32;
+       }
+
+       if (extra_data && extra)
+               memcpy(dst, extra_data, extra);
+
+       wmb();
+       buf->prod += rec_size;
+       return rec_size;
+}
+
+static inline int insert_wrap_record(struct kvm_trace_buf *buf, int
size)
+{
+       int space_left = calc_bytes_to_wrap(buf);
+       unsigned long extra_space = space_left - sizeof(u32);
+       int cycles = 0;
+
+       BUG_ON(space_left > size);
+
+       if ((extra_space/sizeof(u32)) > KVM_TRC_EXTRA_MAX) {
+               cycles = 1;
+               extra_space -= sizeof(u64);
+               WARN_ON(!((extra_space/sizeof(u32)) <=
KVM_TRC_EXTRA_MAX));
+       }
+
+       return __insert_record(buf, KVM_TRC_WRAP_BUF, extra_space,
cycles,
+                              space_left, NULL);
+}
+
+static inline int insert_lost_records(struct kvm_trace_buf *buf)
+{
+       struct {
+               u32 lost_records;
+       } ed;
+
+       ed.lost_records = __get_cpu_var(lost_records);
+       __get_cpu_var(lost_records) = 0;
+
+       return __insert_record(buf, KVM_TRC_LOST_REC, sizeof(ed), 0,
+                              KVM_LOST_REC_SIZE, (unsigned char *)&ed);
+}
+
+void kvm_trace_var(u32 event, int cycles, int extra, unsigned char
*extra_data)
+{
+       struct kvm_trace_buf *buf;
+       unsigned long flags, bytes_to_tail, bytes_to_wrap;
+       int rec_size, total_size = 0;
+       int extra_word;
+       int started_below_highwater;
+
+       extra_word = extra / sizeof(u32);
+       if ((extra % sizeof(u32)) != 0)
+               extra_word++;
+
+       WARN_ON(!(extra_word <= KVM_TRC_EXTRA_MAX));
+       extra_word = min_t(int, extra_word, KVM_TRC_EXTRA_MAX);
+
+       extra = extra_word * sizeof(u32);
+
+       rmb();
+       buf = __get_cpu_var(t_bufs);
+       if (buf == NULL)
+               return;
+       local_irq_save(flags);
+       started_below_highwater = ((buf->prod - buf->cons) <
t_buf_highwater);
+       rec_size = calc_rec_size(cycles, extra);
+       bytes_to_tail = calc_bytes_avail(buf);
+       bytes_to_wrap = calc_bytes_to_wrap(buf);
+
+       if (__get_cpu_var(lost_records)) {
+               if (KVM_LOST_REC_SIZE > bytes_to_wrap) {
+                       total_size += bytes_to_wrap;
+                       bytes_to_wrap = kvm_pcpu_tdata_size;
+               }
+               total_size += KVM_LOST_REC_SIZE;
+               bytes_to_wrap -= KVM_LOST_REC_SIZE;
+
+               if (bytes_to_wrap == 0)
+                       bytes_to_wrap = kvm_pcpu_tdata_size;
+       }
+
+       if (rec_size > bytes_to_wrap)
+               total_size += bytes_to_wrap;
+
+       total_size += rec_size;
+
+       if (total_size > bytes_to_tail) {
+               __get_cpu_var(lost_records)++;
+               local_irq_restore(flags);
+               return;
+       }
+
+       bytes_to_wrap = calc_bytes_to_wrap(buf);
+
+       if (__get_cpu_var(lost_records)) {
+               if (KVM_LOST_REC_SIZE > bytes_to_wrap) {
+                       insert_wrap_record(buf, KVM_LOST_REC_SIZE);
+                       bytes_to_wrap = kvm_pcpu_tdata_size;
+               }
+               insert_lost_records(buf);
+               bytes_to_wrap -= KVM_LOST_REC_SIZE;
+
+               if (bytes_to_wrap == 0)
+                       bytes_to_wrap = kvm_pcpu_tdata_size;
+       }
+
+       if (rec_size > bytes_to_wrap)
+               insert_wrap_record(buf, rec_size);
+
+       __insert_record(buf, event, extra, cycles, rec_size,
extra_data);
+
+       local_irq_restore(flags);
+
+       if (started_below_highwater &&
+          (buf->prod - buf->cons) >= t_buf_highwater)
+               send_sig(SIGALRM, kvm_trace_cons_task, 1);
+}
+EXPORT_SYMBOL_GPL(kvm_trace_var);
+
+int kvm_dev_ioctl_enable_trace(struct kvm_trace_info *t_info)
+{
+       int r;
+       unsigned long mapped_vaddr;
+
+       r = kvm_enable_trace_buf(t_info);
+       if (r < 0)
+               return r;
+
+       mapped_vaddr = kvm_mmap_trace_buf();
+       if (mapped_vaddr) {
+               kvm_trace_cons_task = current;
+               kvm_trace_enable_flag = 1;
+               t_info->userspace_addr = mapped_vaddr;
+               kvm_cur_tinfo.userspace_addr = mapped_vaddr;
+               return 0;
+       }
+       return -EFAULT;
+} 
+
+int kvm_dev_ioctl_disable_trace(void)
+{
+       int r = 0;
+
+       if (kvm_trace_enable_flag)
+               r = kvm_unmmap_trace_buf();
+       kvm_trace_cons_task = NULL;
+       kvm_trace_enable_flag = 0;
+       return r;
+}
+
+void kvm_trace_exit(void)
+{
+       if (kvm_cur_tinfo.pcpu_pages)
+               free_kvmtrace_buf();
+}
-- 
1.5.1

--Eric (Liu, Feng)

Attachment: 0001-KVM-Porting-xentrace-to-kvm.-The-trace-data-is-outp.patch
Description: 0001-KVM-Porting-xentrace-to-kvm.-The-trace-data-is-outp.patch

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
kvm-devel mailing list
kvm-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/kvm-devel

Reply via email to