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