Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.31 285 lines 6.4 kB view raw
1/* 2 * kvm trace 3 * 4 * It is designed to allow debugging traces of kvm to be generated 5 * on UP / SMP machines. Each trace entry can be timestamped so that 6 * it's possible to reconstruct a chronological record of trace events. 7 * The implementation refers to blktrace kernel support. 8 * 9 * Copyright (c) 2008 Intel Corporation 10 * Copyright (C) 2006 Jens Axboe <axboe@kernel.dk> 11 * 12 * Authors: Feng(Eric) Liu, eric.e.liu@intel.com 13 * 14 * Date: Feb 2008 15 */ 16 17#include <linux/module.h> 18#include <linux/relay.h> 19#include <linux/debugfs.h> 20#include <linux/ktime.h> 21 22#include <linux/kvm_host.h> 23 24#define KVM_TRACE_STATE_RUNNING (1 << 0) 25#define KVM_TRACE_STATE_PAUSE (1 << 1) 26#define KVM_TRACE_STATE_CLEARUP (1 << 2) 27 28struct kvm_trace { 29 int trace_state; 30 struct rchan *rchan; 31 struct dentry *lost_file; 32 atomic_t lost_records; 33}; 34static struct kvm_trace *kvm_trace; 35 36struct kvm_trace_probe { 37 const char *name; 38 const char *format; 39 u32 timestamp_in; 40 marker_probe_func *probe_func; 41}; 42 43static inline int calc_rec_size(int timestamp, int extra) 44{ 45 int rec_size = KVM_TRC_HEAD_SIZE; 46 47 rec_size += extra; 48 return timestamp ? rec_size += KVM_TRC_CYCLE_SIZE : rec_size; 49} 50 51static void kvm_add_trace(void *probe_private, void *call_data, 52 const char *format, va_list *args) 53{ 54 struct kvm_trace_probe *p = probe_private; 55 struct kvm_trace *kt = kvm_trace; 56 struct kvm_trace_rec rec; 57 struct kvm_vcpu *vcpu; 58 int i, size; 59 u32 extra; 60 61 if (unlikely(kt->trace_state != KVM_TRACE_STATE_RUNNING)) 62 return; 63 64 rec.rec_val = TRACE_REC_EVENT_ID(va_arg(*args, u32)); 65 vcpu = va_arg(*args, struct kvm_vcpu *); 66 rec.pid = current->tgid; 67 rec.vcpu_id = vcpu->vcpu_id; 68 69 extra = va_arg(*args, u32); 70 WARN_ON(!(extra <= KVM_TRC_EXTRA_MAX)); 71 extra = min_t(u32, extra, KVM_TRC_EXTRA_MAX); 72 73 rec.rec_val |= TRACE_REC_TCS(p->timestamp_in) 74 | TRACE_REC_NUM_DATA_ARGS(extra); 75 76 if (p->timestamp_in) { 77 rec.u.timestamp.timestamp = ktime_to_ns(ktime_get()); 78 79 for (i = 0; i < extra; i++) 80 rec.u.timestamp.extra_u32[i] = va_arg(*args, u32); 81 } else { 82 for (i = 0; i < extra; i++) 83 rec.u.notimestamp.extra_u32[i] = va_arg(*args, u32); 84 } 85 86 size = calc_rec_size(p->timestamp_in, extra * sizeof(u32)); 87 relay_write(kt->rchan, &rec, size); 88} 89 90static struct kvm_trace_probe kvm_trace_probes[] = { 91 { "kvm_trace_entryexit", "%u %p %u %u %u %u %u %u", 1, kvm_add_trace }, 92 { "kvm_trace_handler", "%u %p %u %u %u %u %u %u", 0, kvm_add_trace }, 93}; 94 95static int lost_records_get(void *data, u64 *val) 96{ 97 struct kvm_trace *kt = data; 98 99 *val = atomic_read(&kt->lost_records); 100 return 0; 101} 102 103DEFINE_SIMPLE_ATTRIBUTE(kvm_trace_lost_ops, lost_records_get, NULL, "%llu\n"); 104 105/* 106 * The relay channel is used in "no-overwrite" mode, it keeps trace of how 107 * many times we encountered a full subbuffer, to tell user space app the 108 * lost records there were. 109 */ 110static int kvm_subbuf_start_callback(struct rchan_buf *buf, void *subbuf, 111 void *prev_subbuf, size_t prev_padding) 112{ 113 struct kvm_trace *kt; 114 115 if (!relay_buf_full(buf)) { 116 if (!prev_subbuf) { 117 /* 118 * executed only once when the channel is opened 119 * save metadata as first record 120 */ 121 subbuf_start_reserve(buf, sizeof(u32)); 122 *(u32 *)subbuf = 0x12345678; 123 } 124 125 return 1; 126 } 127 128 kt = buf->chan->private_data; 129 atomic_inc(&kt->lost_records); 130 131 return 0; 132} 133 134static struct dentry *kvm_create_buf_file_callack(const char *filename, 135 struct dentry *parent, 136 int mode, 137 struct rchan_buf *buf, 138 int *is_global) 139{ 140 return debugfs_create_file(filename, mode, parent, buf, 141 &relay_file_operations); 142} 143 144static int kvm_remove_buf_file_callback(struct dentry *dentry) 145{ 146 debugfs_remove(dentry); 147 return 0; 148} 149 150static struct rchan_callbacks kvm_relay_callbacks = { 151 .subbuf_start = kvm_subbuf_start_callback, 152 .create_buf_file = kvm_create_buf_file_callack, 153 .remove_buf_file = kvm_remove_buf_file_callback, 154}; 155 156static int do_kvm_trace_enable(struct kvm_user_trace_setup *kuts) 157{ 158 struct kvm_trace *kt; 159 int i, r = -ENOMEM; 160 161 if (!kuts->buf_size || !kuts->buf_nr) 162 return -EINVAL; 163 164 kt = kzalloc(sizeof(*kt), GFP_KERNEL); 165 if (!kt) 166 goto err; 167 168 r = -EIO; 169 atomic_set(&kt->lost_records, 0); 170 kt->lost_file = debugfs_create_file("lost_records", 0444, kvm_debugfs_dir, 171 kt, &kvm_trace_lost_ops); 172 if (!kt->lost_file) 173 goto err; 174 175 kt->rchan = relay_open("trace", kvm_debugfs_dir, kuts->buf_size, 176 kuts->buf_nr, &kvm_relay_callbacks, kt); 177 if (!kt->rchan) 178 goto err; 179 180 kvm_trace = kt; 181 182 for (i = 0; i < ARRAY_SIZE(kvm_trace_probes); i++) { 183 struct kvm_trace_probe *p = &kvm_trace_probes[i]; 184 185 r = marker_probe_register(p->name, p->format, p->probe_func, p); 186 if (r) 187 printk(KERN_INFO "Unable to register probe %s\n", 188 p->name); 189 } 190 191 kvm_trace->trace_state = KVM_TRACE_STATE_RUNNING; 192 193 return 0; 194err: 195 if (kt) { 196 if (kt->lost_file) 197 debugfs_remove(kt->lost_file); 198 if (kt->rchan) 199 relay_close(kt->rchan); 200 kfree(kt); 201 } 202 return r; 203} 204 205static int kvm_trace_enable(char __user *arg) 206{ 207 struct kvm_user_trace_setup kuts; 208 int ret; 209 210 ret = copy_from_user(&kuts, arg, sizeof(kuts)); 211 if (ret) 212 return -EFAULT; 213 214 ret = do_kvm_trace_enable(&kuts); 215 if (ret) 216 return ret; 217 218 return 0; 219} 220 221static int kvm_trace_pause(void) 222{ 223 struct kvm_trace *kt = kvm_trace; 224 int r = -EINVAL; 225 226 if (kt == NULL) 227 return r; 228 229 if (kt->trace_state == KVM_TRACE_STATE_RUNNING) { 230 kt->trace_state = KVM_TRACE_STATE_PAUSE; 231 relay_flush(kt->rchan); 232 r = 0; 233 } 234 235 return r; 236} 237 238void kvm_trace_cleanup(void) 239{ 240 struct kvm_trace *kt = kvm_trace; 241 int i; 242 243 if (kt == NULL) 244 return; 245 246 if (kt->trace_state == KVM_TRACE_STATE_RUNNING || 247 kt->trace_state == KVM_TRACE_STATE_PAUSE) { 248 249 kt->trace_state = KVM_TRACE_STATE_CLEARUP; 250 251 for (i = 0; i < ARRAY_SIZE(kvm_trace_probes); i++) { 252 struct kvm_trace_probe *p = &kvm_trace_probes[i]; 253 marker_probe_unregister(p->name, p->probe_func, p); 254 } 255 marker_synchronize_unregister(); 256 257 relay_close(kt->rchan); 258 debugfs_remove(kt->lost_file); 259 kfree(kt); 260 } 261} 262 263int kvm_trace_ioctl(unsigned int ioctl, unsigned long arg) 264{ 265 void __user *argp = (void __user *)arg; 266 long r = -EINVAL; 267 268 if (!capable(CAP_SYS_ADMIN)) 269 return -EPERM; 270 271 switch (ioctl) { 272 case KVM_TRACE_ENABLE: 273 r = kvm_trace_enable(argp); 274 break; 275 case KVM_TRACE_PAUSE: 276 r = kvm_trace_pause(); 277 break; 278 case KVM_TRACE_DISABLE: 279 r = 0; 280 kvm_trace_cleanup(); 281 break; 282 } 283 284 return r; 285}