rev |
line source |
mwilli2@1151
|
1 /******************************************************************************
|
mwilli2@1151
|
2 * common/trace.c
|
mwilli2@1151
|
3 *
|
mwilli2@1151
|
4 * Xen Trace Buffer
|
mwilli2@1151
|
5 *
|
mwilli2@1151
|
6 * Copyright (C) 2004 by Intel Research Cambridge
|
mwilli2@1151
|
7 *
|
kaf24@7604
|
8 * Authors: Mark Williamson, mark.a.williamson@intel.com
|
kaf24@7604
|
9 * Rob Gardner, rob.gardner@hp.com
|
kaf24@7604
|
10 * Date: October 2005
|
mwilli2@1151
|
11 *
|
bren@4534
|
12 * Copyright (C) 2005 Bin Ren
|
bren@4534
|
13 *
|
mwilli2@1151
|
14 * The trace buffer code is designed to allow debugging traces of Xen to be
|
mwilli2@1151
|
15 * generated on UP / SMP machines. Each trace entry is timestamped so that
|
mwilli2@1151
|
16 * it's possible to reconstruct a chronological record of trace events.
|
kaf24@1154
|
17 */
|
mwilli2@1151
|
18
|
kaf24@1248
|
19 #include <xen/config.h>
|
mwilli2@1151
|
20 #include <asm/types.h>
|
mwilli2@1151
|
21 #include <asm/io.h>
|
kaf24@1248
|
22 #include <xen/lib.h>
|
kaf24@1248
|
23 #include <xen/sched.h>
|
kaf24@1248
|
24 #include <xen/smp.h>
|
kaf24@1248
|
25 #include <xen/trace.h>
|
kaf24@1248
|
26 #include <xen/errno.h>
|
kaf24@9625
|
27 #include <xen/event.h>
|
keir@21242
|
28 #include <xen/tasklet.h>
|
smh22@3405
|
29 #include <xen/init.h>
|
kaf24@11236
|
30 #include <xen/mm.h>
|
kfraser@10728
|
31 #include <xen/percpu.h>
|
keir@21435
|
32 #include <xen/cpu.h>
|
mwilli2@1151
|
33 #include <asm/atomic.h>
|
kfraser@11295
|
34 #include <public/sysctl.h>
|
mwilli2@1151
|
35
|
ack@13311
|
36 #ifdef CONFIG_COMPAT
|
ack@13311
|
37 #include <compat/trace.h>
|
ack@13311
|
38 #define xen_t_buf t_buf
|
ack@13311
|
39 CHECK_t_buf;
|
ack@13311
|
40 #undef xen_t_buf
|
ack@13311
|
41 #else
|
ack@13311
|
42 #define compat_t_rec t_rec
|
ack@13311
|
43 #endif
|
ack@13311
|
44
|
kaf24@3372
|
45 /* opt_tbuf_size: trace buffer size (in pages) */
|
kaf24@7604
|
46 static unsigned int opt_tbuf_size = 0;
|
kaf24@3372
|
47 integer_param("tbuf_size", opt_tbuf_size);
|
kaf24@3372
|
48
|
mwilli2@1151
|
49 /* Pointers to the meta-data objects for all system trace buffers */
|
keir@20873
|
50 static struct t_info *t_info;
|
keir@20873
|
51 #define T_INFO_PAGES 2 /* Size fixed at 2 pages for now. */
|
keir@21197
|
52 #define T_INFO_SIZE ((T_INFO_PAGES)*(PAGE_SIZE))
|
keir@19964
|
53 static DEFINE_PER_CPU_READ_MOSTLY(struct t_buf *, t_bufs);
|
keir@19964
|
54 static DEFINE_PER_CPU_READ_MOSTLY(unsigned char *, t_data);
|
keir@20873
|
55 static DEFINE_PER_CPU_READ_MOSTLY(spinlock_t, t_lock);
|
keir@21752
|
56 static u32 data_size;
|
keir@21749
|
57 static u32 t_info_first_offset __read_mostly;
|
mwilli2@1151
|
58
|
kaf24@9625
|
59 /* High water mark for trace buffers; */
|
kaf24@9625
|
60 /* Send virtual interrupt when buffer level reaches this point */
|
keir@21752
|
61 static u32 t_buf_highwater;
|
kaf24@9625
|
62
|
kfraser@10698
|
63 /* Number of records lost due to per-CPU trace buffer being full. */
|
kfraser@10698
|
64 static DEFINE_PER_CPU(unsigned long, lost_records);
|
keir@18476
|
65 static DEFINE_PER_CPU(unsigned long, lost_records_first_tsc);
|
kaf24@9625
|
66
|
kaf24@7604
|
67 /* a flag recording whether initialization has been done */
|
kaf24@7604
|
68 /* or more properly, if the tbuf subsystem is enabled right now */
|
kfraser@14174
|
69 int tb_init_done __read_mostly;
|
mwilli2@1151
|
70
|
bren@4534
|
71 /* which CPUs tracing is enabled on */
|
kfraser@11295
|
72 static cpumask_t tb_cpu_mask = CPU_MASK_ALL;
|
bren@4534
|
73
|
bren@4534
|
74 /* which tracing events are enabled */
|
kaf24@7606
|
75 static u32 tb_event_mask = TRC_ALL;
|
kaf24@7604
|
76
|
keir@21749
|
77 /* Return the number of elements _type necessary to store at least _x bytes of data
|
keir@21749
|
78 * i.e., sizeof(_type) * ans >= _x. */
|
keir@21749
|
79 #define fit_to_type(_type, _x) (((_x)+sizeof(_type)-1) / sizeof(_type))
|
keir@21749
|
80
|
keir@21749
|
81 static void calc_tinfo_first_offset(void)
|
keir@21749
|
82 {
|
keir@22613
|
83 int offset_in_bytes = offsetof(struct t_info, mfn_offset[NR_CPUS]);
|
keir@21749
|
84 t_info_first_offset = fit_to_type(uint32_t, offset_in_bytes);
|
keir@21749
|
85 }
|
keir@21749
|
86
|
kaf24@7604
|
87 /**
|
keir@21197
|
88 * check_tbuf_size - check to make sure that the proposed size will fit
|
keir@21750
|
89 * in the currently sized struct t_info and allows prod and cons to
|
keir@21750
|
90 * reach double the value without overflow.
|
keir@21197
|
91 */
|
keir@21750
|
92 static int check_tbuf_size(u32 pages)
|
keir@21197
|
93 {
|
keir@21750
|
94 struct t_buf dummy;
|
keir@21750
|
95 typeof(dummy.prod) size;
|
keir@21750
|
96
|
keir@21750
|
97 size = ((typeof(dummy.prod))pages) * PAGE_SIZE;
|
keir@21750
|
98
|
keir@21750
|
99 return (size / PAGE_SIZE != pages)
|
keir@21750
|
100 || (size + size < size)
|
keir@21750
|
101 || (num_online_cpus() * pages + t_info_first_offset > T_INFO_SIZE / sizeof(uint32_t));
|
keir@21197
|
102 }
|
keir@21197
|
103
|
keir@21197
|
104 /**
|
kaf24@7604
|
105 * alloc_trace_bufs - performs initialization of the per-cpu trace buffers.
|
kaf24@7604
|
106 *
|
kaf24@7604
|
107 * This function is called at start of day in order to initialize the per-cpu
|
kaf24@7604
|
108 * trace buffers. The trace buffers are then available for debugging use, via
|
kaf24@7604
|
109 * the %TRACE_xD macros exported in <xen/trace.h>.
|
kaf24@7604
|
110 *
|
kaf24@7604
|
111 * This function may also be called later when enabling trace buffers
|
kaf24@7604
|
112 * via the SET_SIZE hypercall.
|
kaf24@7604
|
113 */
|
kaf24@7606
|
114 static int alloc_trace_bufs(void)
|
kaf24@7604
|
115 {
|
keir@20873
|
116 int i, cpu, order;
|
kaf24@7604
|
117 unsigned long nr_pages;
|
keir@20873
|
118 /* Start after a fixed-size array of NR_CPUS */
|
keir@22613
|
119 uint32_t *t_info_mfn_list;
|
keir@22613
|
120 int offset;
|
kaf24@7604
|
121
|
kaf24@7604
|
122 if ( opt_tbuf_size == 0 )
|
kaf24@7604
|
123 return -EINVAL;
|
kaf24@7604
|
124
|
keir@22613
|
125 if ( check_tbuf_size(opt_tbuf_size) )
|
kaf24@1154
|
126 {
|
keir@22613
|
127 printk("Xen trace buffers: tb size %d too large. "
|
keir@22613
|
128 "Tracing disabled.\n",
|
keir@22613
|
129 opt_tbuf_size);
|
kaf24@7604
|
130 return -EINVAL;
|
mwilli2@1151
|
131 }
|
mwilli2@1182
|
132
|
keir@22613
|
133 /* t_info size is fixed for now. Currently this works great, so there
|
keir@22613
|
134 * seems to be no need to make it dynamic. */
|
keir@22613
|
135 t_info = alloc_xenheap_pages(get_order_from_pages(T_INFO_PAGES), 0);
|
keir@22613
|
136 if ( t_info == NULL )
|
keir@22613
|
137 {
|
keir@22613
|
138 printk("Xen trace buffers: t_info allocation failed! "
|
keir@22613
|
139 "Tracing disabled.\n");
|
keir@22613
|
140 return -ENOMEM;
|
keir@22613
|
141 }
|
keir@22613
|
142
|
keir@22613
|
143 for ( i = 0; i < T_INFO_PAGES; i++ )
|
keir@22613
|
144 share_xen_page_with_privileged_guests(
|
keir@22613
|
145 virt_to_page(t_info) + i, XENSHARE_readonly);
|
keir@22613
|
146
|
keir@22613
|
147 t_info_mfn_list = (uint32_t *)t_info;
|
keir@22613
|
148 offset = t_info_first_offset;
|
keir@22613
|
149
|
keir@20873
|
150 t_info->tbuf_size = opt_tbuf_size;
|
keir@21751
|
151 printk(XENLOG_INFO "tbuf_size %d\n", t_info->tbuf_size);
|
keir@20873
|
152
|
keir@20873
|
153 nr_pages = opt_tbuf_size;
|
keir@20873
|
154 order = get_order_from_pages(nr_pages);
|
keir@20873
|
155
|
keir@20873
|
156 /*
|
keir@20873
|
157 * First, allocate buffers for all of the cpus. If any
|
keir@20873
|
158 * fails, deallocate what you have so far and exit.
|
keir@20873
|
159 */
|
keir@20873
|
160 for_each_online_cpu(cpu)
|
keir@20873
|
161 {
|
keir@20873
|
162 int flags;
|
keir@20873
|
163 char *rawbuf;
|
keir@20873
|
164 struct t_buf *buf;
|
kaf24@9214
|
165
|
keir@21728
|
166 if ( (rawbuf = alloc_xenheap_pages(
|
keir@21728
|
167 order, MEMF_bits(32 + PAGE_SHIFT))) == NULL )
|
keir@20873
|
168 {
|
keir@20873
|
169 printk("Xen trace buffers: memory allocation failed\n");
|
keir@20873
|
170 opt_tbuf_size = 0;
|
keir@20873
|
171 goto out_dealloc;
|
keir@20873
|
172 }
|
keir@20873
|
173
|
keir@20873
|
174 spin_lock_irqsave(&per_cpu(t_lock, cpu), flags);
|
keir@20873
|
175
|
keir@21752
|
176 per_cpu(t_bufs, cpu) = buf = (struct t_buf *)rawbuf;
|
kaf24@7609
|
177 buf->cons = buf->prod = 0;
|
keir@20873
|
178 per_cpu(t_data, cpu) = (unsigned char *)(buf + 1);
|
keir@20873
|
179
|
keir@20873
|
180 spin_unlock_irqrestore(&per_cpu(t_lock, cpu), flags);
|
keir@20873
|
181
|
mwilli2@1151
|
182 }
|
kaf24@7609
|
183
|
keir@20873
|
184 /*
|
keir@20873
|
185 * Now share the pages to xentrace can map them, and write them in
|
keir@20873
|
186 * the global t_info structure.
|
keir@20873
|
187 */
|
keir@20873
|
188 for_each_online_cpu(cpu)
|
keir@20873
|
189 {
|
keir@20873
|
190 /* Share pages so that xentrace can map them. */
|
keir@20873
|
191 char *rawbuf;
|
keir@20873
|
192
|
keir@20873
|
193 if ( (rawbuf = (char *)per_cpu(t_bufs, cpu)) )
|
keir@20873
|
194 {
|
keir@20873
|
195 struct page_info *p = virt_to_page(rawbuf);
|
keir@20873
|
196 uint32_t mfn = virt_to_mfn(rawbuf);
|
keir@20873
|
197
|
keir@20873
|
198 for ( i = 0; i < nr_pages; i++ )
|
keir@20873
|
199 {
|
keir@20873
|
200 share_xen_page_with_privileged_guests(
|
keir@20873
|
201 p + i, XENSHARE_writable);
|
keir@20873
|
202
|
keir@20873
|
203 t_info_mfn_list[offset + i]=mfn + i;
|
keir@20873
|
204 }
|
keir@20873
|
205 /* Write list first, then write per-cpu offset. */
|
keir@20873
|
206 wmb();
|
keir@20873
|
207 t_info->mfn_offset[cpu]=offset;
|
keir@21751
|
208 printk(XENLOG_INFO "p%d mfn %"PRIx32" offset %d\n",
|
keir@20873
|
209 cpu, mfn, offset);
|
keir@20873
|
210 offset+=i;
|
keir@20873
|
211 }
|
keir@20873
|
212 }
|
keir@20873
|
213
|
keir@20873
|
214 data_size = (opt_tbuf_size * PAGE_SIZE - sizeof(struct t_buf));
|
gdunlap@15968
|
215 t_buf_highwater = data_size >> 1; /* 50% high water */
|
kaf24@9625
|
216
|
kaf24@7604
|
217 return 0;
|
keir@20873
|
218 out_dealloc:
|
keir@20873
|
219 for_each_online_cpu(cpu)
|
keir@20873
|
220 {
|
keir@20873
|
221 int flags;
|
keir@20873
|
222 char * rawbuf;
|
keir@20873
|
223
|
keir@20873
|
224 spin_lock_irqsave(&per_cpu(t_lock, cpu), flags);
|
keir@20873
|
225 if ( (rawbuf = (char *)per_cpu(t_bufs, cpu)) )
|
keir@20873
|
226 {
|
keir@21752
|
227 per_cpu(t_bufs, cpu) = NULL;
|
keir@20873
|
228 ASSERT(!(virt_to_page(rawbuf)->count_info & PGC_allocated));
|
keir@20873
|
229 free_xenheap_pages(rawbuf, order);
|
keir@20873
|
230 }
|
keir@20873
|
231 spin_unlock_irqrestore(&per_cpu(t_lock, cpu), flags);
|
keir@20873
|
232 }
|
keir@21197
|
233
|
keir@21197
|
234 return -ENOMEM;
|
kaf24@7604
|
235 }
|
mwilli2@1151
|
236
|
kaf24@7604
|
237
|
kaf24@7604
|
238 /**
|
kaf24@7604
|
239 * tb_set_size - handle the logic involved with dynamically
|
kaf24@7604
|
240 * allocating and deallocating tbufs
|
kaf24@7604
|
241 *
|
kaf24@7604
|
242 * This function is called when the SET_SIZE hypercall is done.
|
kaf24@7604
|
243 */
|
kaf24@7606
|
244 static int tb_set_size(int size)
|
kaf24@7604
|
245 {
|
kaf24@7606
|
246 /*
|
kaf24@7606
|
247 * Setting size is a one-shot operation. It can be done either at
|
kaf24@7606
|
248 * boot time or via control tools, but not by both. Once buffers
|
kaf24@7606
|
249 * are created they cannot be destroyed.
|
kaf24@7606
|
250 */
|
keir@21197
|
251 int ret = 0;
|
keir@21197
|
252
|
keir@22613
|
253 if ( opt_tbuf_size != 0 )
|
kaf24@7606
|
254 {
|
keir@21197
|
255 if ( size != opt_tbuf_size )
|
keir@21197
|
256 gdprintk(XENLOG_INFO, "tb_set_size from %d to %d not implemented\n",
|
keir@21197
|
257 opt_tbuf_size, size);
|
keir@21197
|
258 return -EINVAL;
|
keir@21197
|
259 }
|
keir@21197
|
260
|
keir@21197
|
261 if ( size <= 0 )
|
keir@21197
|
262 return -EINVAL;
|
keir@21197
|
263
|
keir@22613
|
264 opt_tbuf_size = size;
|
keir@22613
|
265
|
keir@22613
|
266 if ( (ret = alloc_trace_bufs()) != 0 )
|
keir@21197
|
267 {
|
keir@22613
|
268 opt_tbuf_size = 0;
|
keir@22613
|
269 return ret;
|
kaf24@7606
|
270 }
|
kaf24@7606
|
271
|
keir@22613
|
272 printk("Xen trace buffers: initialized\n");
|
keir@22613
|
273 return 0;
|
kaf24@7606
|
274 }
|
kaf24@7606
|
275
|
keir@18479
|
276 int trace_will_trace_event(u32 event)
|
keir@18479
|
277 {
|
keir@18479
|
278 if ( !tb_init_done )
|
keir@18479
|
279 return 0;
|
keir@18479
|
280
|
keir@18479
|
281 /*
|
keir@18479
|
282 * Copied from __trace_var()
|
keir@18479
|
283 */
|
keir@18479
|
284 if ( (tb_event_mask & event) == 0 )
|
keir@18479
|
285 return 0;
|
keir@18479
|
286
|
keir@18479
|
287 /* match class */
|
keir@18479
|
288 if ( ((tb_event_mask >> TRC_CLS_SHIFT) & (event >> TRC_CLS_SHIFT)) == 0 )
|
keir@18479
|
289 return 0;
|
keir@18479
|
290
|
keir@18479
|
291 /* then match subclass */
|
keir@18479
|
292 if ( (((tb_event_mask >> TRC_SUBCLS_SHIFT) & 0xf )
|
keir@18479
|
293 & ((event >> TRC_SUBCLS_SHIFT) & 0xf )) == 0 )
|
keir@18479
|
294 return 0;
|
keir@18479
|
295
|
keir@18479
|
296 if ( !cpu_isset(smp_processor_id(), tb_cpu_mask) )
|
keir@18479
|
297 return 0;
|
keir@18479
|
298
|
keir@18479
|
299 return 1;
|
keir@18479
|
300 }
|
kaf24@7606
|
301
|
keir@21435
|
302 static int cpu_callback(
|
keir@21435
|
303 struct notifier_block *nfb, unsigned long action, void *hcpu)
|
keir@21435
|
304 {
|
keir@21435
|
305 unsigned int cpu = (unsigned long)hcpu;
|
keir@21435
|
306
|
keir@21435
|
307 if ( action == CPU_UP_PREPARE )
|
keir@21435
|
308 spin_lock_init(&per_cpu(t_lock, cpu));
|
keir@21435
|
309
|
keir@21435
|
310 return NOTIFY_DONE;
|
keir@21435
|
311 }
|
keir@21435
|
312
|
keir@21435
|
313 static struct notifier_block cpu_nfb = {
|
keir@21435
|
314 .notifier_call = cpu_callback
|
keir@21435
|
315 };
|
keir@22613
|
316
|
kaf24@7606
|
317 /**
|
kaf24@7606
|
318 * init_trace_bufs - performs initialization of the per-cpu trace buffers.
|
kaf24@7606
|
319 *
|
kaf24@7606
|
320 * This function is called at start of day in order to initialize the per-cpu
|
kaf24@7606
|
321 * trace buffers. The trace buffers are then available for debugging use, via
|
kaf24@7606
|
322 * the %TRACE_xD macros exported in <xen/trace.h>.
|
kaf24@7606
|
323 */
|
keir@15081
|
324 void __init init_trace_bufs(void)
|
kaf24@7606
|
325 {
|
keir@20873
|
326 int i;
|
keir@21749
|
327
|
keir@21749
|
328 /* Calculate offset in u32 of first mfn */
|
keir@21749
|
329 calc_tinfo_first_offset();
|
keir@21749
|
330
|
keir@22613
|
331 /* Per-cpu t_lock initialisation. */
|
keir@21435
|
332 for_each_online_cpu ( i )
|
keir@20873
|
333 spin_lock_init(&per_cpu(t_lock, i));
|
keir@21435
|
334 register_cpu_notifier(&cpu_nfb);
|
keir@20873
|
335
|
kaf24@7606
|
336 if ( opt_tbuf_size == 0 )
|
kaf24@7606
|
337 {
|
kaf24@7606
|
338 printk("Xen trace buffers: disabled\n");
|
keir@22613
|
339 goto fail;
|
keir@21197
|
340 }
|
kaf24@7606
|
341
|
keir@22613
|
342 if ( alloc_trace_bufs() != 0 )
|
kaf24@7606
|
343 {
|
keir@22613
|
344 dprintk(XENLOG_INFO, "Xen trace buffers: "
|
keir@22613
|
345 "allocation size %d failed, disabling\n",
|
keir@22613
|
346 opt_tbuf_size);
|
keir@22613
|
347 goto fail;
|
kaf24@7604
|
348 }
|
keir@22613
|
349
|
keir@22613
|
350 printk("Xen trace buffers: initialised\n");
|
keir@22613
|
351 wmb(); /* above must be visible before tb_init_done flag set */
|
keir@22613
|
352 tb_init_done = 1;
|
keir@22613
|
353 return;
|
keir@22613
|
354
|
keir@22613
|
355 fail:
|
keir@22613
|
356 opt_tbuf_size = 0;
|
kaf24@7604
|
357 }
|
mwilli2@1151
|
358
|
mwilli2@1151
|
359 /**
|
kfraser@11295
|
360 * tb_control - sysctl operations on trace buffers.
|
kfraser@11295
|
361 * @tbc: a pointer to a xen_sysctl_tbuf_op_t to be filled out
|
mwilli2@1151
|
362 */
|
kfraser@11295
|
363 int tb_control(xen_sysctl_tbuf_op_t *tbc)
|
mwilli2@1151
|
364 {
|
kaf24@10288
|
365 static DEFINE_SPINLOCK(lock);
|
bren@4534
|
366 int rc = 0;
|
bren@4534
|
367
|
bren@4534
|
368 spin_lock(&lock);
|
bren@4534
|
369
|
kfraser@11295
|
370 switch ( tbc->cmd )
|
mwilli2@1163
|
371 {
|
kfraser@11295
|
372 case XEN_SYSCTL_TBUFOP_get_info:
|
kaf24@6319
|
373 tbc->evt_mask = tb_event_mask;
|
keir@20873
|
374 tbc->buffer_mfn = t_info ? virt_to_mfn(t_info) : 0;
|
keir@21379
|
375 tbc->size = T_INFO_PAGES * PAGE_SIZE;
|
bren@4534
|
376 break;
|
kfraser@11295
|
377 case XEN_SYSCTL_TBUFOP_set_cpu_mask:
|
keir@21396
|
378 rc = xenctl_cpumap_to_cpumask(&tb_cpu_mask, &tbc->cpu_mask);
|
bren@4534
|
379 break;
|
kfraser@11295
|
380 case XEN_SYSCTL_TBUFOP_set_evt_mask:
|
bren@4534
|
381 tb_event_mask = tbc->evt_mask;
|
bren@4534
|
382 break;
|
kfraser@11295
|
383 case XEN_SYSCTL_TBUFOP_set_size:
|
keir@20873
|
384 rc = tb_set_size(tbc->size);
|
kaf24@7604
|
385 break;
|
kfraser@11295
|
386 case XEN_SYSCTL_TBUFOP_enable:
|
kaf24@7606
|
387 /* Enable trace buffers. Check buffers are already allocated. */
|
kaf24@7606
|
388 if ( opt_tbuf_size == 0 )
|
kaf24@7604
|
389 rc = -EINVAL;
|
kaf24@7604
|
390 else
|
kaf24@7604
|
391 tb_init_done = 1;
|
kaf24@7604
|
392 break;
|
kfraser@11295
|
393 case XEN_SYSCTL_TBUFOP_disable:
|
keir@20925
|
394 {
|
kaf24@7606
|
395 /*
|
kaf24@7606
|
396 * Disable trace buffers. Just stops new records from being written,
|
kaf24@7606
|
397 * does not deallocate any memory.
|
kaf24@7606
|
398 */
|
keir@20925
|
399 int i;
|
keir@20925
|
400
|
kaf24@7604
|
401 tb_init_done = 0;
|
keir@20925
|
402 wmb();
|
keir@20925
|
403 /* Clear any lost-record info so we don't get phantom lost records next time we
|
keir@20925
|
404 * start tracing. Grab the lock to make sure we're not racing anyone. After this
|
keir@20925
|
405 * hypercall returns, no more records should be placed into the buffers. */
|
keir@20925
|
406 for_each_online_cpu(i)
|
keir@20925
|
407 {
|
keir@20925
|
408 int flags;
|
keir@20925
|
409 spin_lock_irqsave(&per_cpu(t_lock, i), flags);
|
keir@20925
|
410 per_cpu(lost_records, i)=0;
|
keir@20925
|
411 spin_unlock_irqrestore(&per_cpu(t_lock, i), flags);
|
keir@20925
|
412 }
|
keir@20925
|
413 }
|
kaf24@7604
|
414 break;
|
bren@4534
|
415 default:
|
bren@4534
|
416 rc = -EINVAL;
|
kaf24@7606
|
417 break;
|
mwilli2@1163
|
418 }
|
bren@4534
|
419
|
bren@4534
|
420 spin_unlock(&lock);
|
bren@4534
|
421
|
bren@4534
|
422 return rc;
|
mwilli2@1151
|
423 }
|
kaf24@3952
|
424
|
keir@21752
|
425 static inline unsigned int calc_rec_size(bool_t cycles, unsigned int extra)
|
gdunlap@15968
|
426 {
|
keir@21752
|
427 unsigned int rec_size = 4;
|
keir@21752
|
428
|
keir@15979
|
429 if ( cycles )
|
gdunlap@15968
|
430 rec_size += 8;
|
gdunlap@15968
|
431 rec_size += extra;
|
gdunlap@15968
|
432 return rec_size;
|
gdunlap@15968
|
433 }
|
gdunlap@15968
|
434
|
keir@21752
|
435 static inline bool_t bogus(u32 prod, u32 cons)
|
keir@17038
|
436 {
|
keir@21752
|
437 if ( unlikely(prod & 3) || unlikely(prod >= 2 * data_size) ||
|
keir@21752
|
438 unlikely(cons & 3) || unlikely(cons >= 2 * data_size) )
|
keir@21752
|
439 {
|
keir@21752
|
440 tb_init_done = 0;
|
keir@21752
|
441 printk(XENLOG_WARNING "trc#%u: bogus prod (%08x) and/or cons (%08x)\n",
|
keir@21752
|
442 smp_processor_id(), prod, cons);
|
keir@21752
|
443 return 1;
|
keir@21752
|
444 }
|
keir@21752
|
445 return 0;
|
keir@21752
|
446 }
|
keir@21752
|
447
|
keir@21752
|
448 static inline u32 calc_unconsumed_bytes(const struct t_buf *buf)
|
keir@21752
|
449 {
|
keir@21752
|
450 u32 prod = buf->prod, cons = buf->cons;
|
keir@21759
|
451 s32 x;
|
keir@21752
|
452
|
keir@21759
|
453 barrier(); /* must read buf->prod and buf->cons only once */
|
keir@21752
|
454 if ( bogus(prod, cons) )
|
keir@21752
|
455 return data_size;
|
keir@21752
|
456
|
keir@21759
|
457 x = prod - cons;
|
keir@17038
|
458 if ( x < 0 )
|
keir@17038
|
459 x += 2*data_size;
|
keir@17038
|
460
|
keir@17038
|
461 ASSERT(x >= 0);
|
keir@17038
|
462 ASSERT(x <= data_size);
|
keir@17038
|
463
|
keir@17038
|
464 return x;
|
keir@17038
|
465 }
|
keir@17038
|
466
|
keir@21752
|
467 static inline u32 calc_bytes_to_wrap(const struct t_buf *buf)
|
gdunlap@15968
|
468 {
|
keir@21759
|
469 u32 prod = buf->prod, cons = buf->cons;
|
keir@21759
|
470 s32 x;
|
keir@21752
|
471
|
keir@21759
|
472 barrier(); /* must read buf->prod and buf->cons only once */
|
keir@21759
|
473 if ( bogus(prod, cons) )
|
keir@21752
|
474 return 0;
|
keir@21752
|
475
|
keir@21759
|
476 x = data_size - prod;
|
keir@17038
|
477 if ( x <= 0 )
|
keir@17038
|
478 x += data_size;
|
keir@17038
|
479
|
keir@17038
|
480 ASSERT(x > 0);
|
keir@17038
|
481 ASSERT(x <= data_size);
|
keir@17038
|
482
|
keir@17038
|
483 return x;
|
gdunlap@15968
|
484 }
|
gdunlap@15968
|
485
|
keir@21752
|
486 static inline u32 calc_bytes_avail(const struct t_buf *buf)
|
keir@17038
|
487 {
|
keir@17038
|
488 return data_size - calc_unconsumed_bytes(buf);
|
keir@17038
|
489 }
|
keir@17038
|
490
|
keir@21759
|
491 static inline struct t_rec *next_record(const struct t_buf *buf,
|
keir@21759
|
492 uint32_t *next)
|
gdunlap@15968
|
493 {
|
keir@21759
|
494 u32 x = buf->prod, cons = buf->cons;
|
keir@21752
|
495
|
keir@21759
|
496 barrier(); /* must read buf->prod and buf->cons only once */
|
keir@21759
|
497 *next = x;
|
keir@21759
|
498 if ( !tb_init_done || bogus(x, cons) )
|
keir@21752
|
499 return NULL;
|
keir@21752
|
500
|
keir@17038
|
501 if ( x >= data_size )
|
keir@17038
|
502 x -= data_size;
|
keir@17038
|
503
|
keir@17038
|
504 ASSERT(x < data_size);
|
keir@17038
|
505
|
keir@17038
|
506 return (struct t_rec *)&this_cpu(t_data)[x];
|
gdunlap@15968
|
507 }
|
gdunlap@15968
|
508
|
keir@21752
|
509 static inline void __insert_record(struct t_buf *buf,
|
keir@21752
|
510 unsigned long event,
|
keir@21752
|
511 unsigned int extra,
|
keir@21752
|
512 bool_t cycles,
|
keir@21752
|
513 unsigned int rec_size,
|
keir@21752
|
514 const void *extra_data)
|
gdunlap@15968
|
515 {
|
gdunlap@15968
|
516 struct t_rec *rec;
|
keir@15979
|
517 unsigned char *dst;
|
keir@21752
|
518 unsigned int extra_word = extra / sizeof(u32);
|
keir@21752
|
519 unsigned int local_rec_size = calc_rec_size(cycles, extra);
|
keir@17038
|
520 uint32_t next;
|
gdunlap@15968
|
521
|
gdunlap@15968
|
522 BUG_ON(local_rec_size != rec_size);
|
keir@17038
|
523 BUG_ON(extra & 3);
|
gdunlap@15968
|
524
|
keir@21759
|
525 rec = next_record(buf, &next);
|
keir@21759
|
526 if ( !rec )
|
keir@21759
|
527 return;
|
gdunlap@15968
|
528 /* Double-check once more that we have enough space.
|
gdunlap@15968
|
529 * Don't bugcheck here, in case the userland tool is doing
|
gdunlap@15968
|
530 * something stupid. */
|
keir@21759
|
531 if ( (unsigned char *)rec + rec_size > this_cpu(t_data) + data_size )
|
gdunlap@15968
|
532 {
|
keir@21751
|
533 if ( printk_ratelimit() )
|
keir@21751
|
534 printk(XENLOG_WARNING
|
keir@21759
|
535 "%s: size=%08x prod=%08x cons=%08x rec=%u\n",
|
keir@21759
|
536 __func__, data_size, next, buf->cons, rec_size);
|
keir@21752
|
537 return;
|
gdunlap@15968
|
538 }
|
gdunlap@15968
|
539
|
keir@15979
|
540 rec->event = event;
|
keir@15979
|
541 rec->extra_u32 = extra_word;
|
keir@15979
|
542 dst = (unsigned char *)rec->u.nocycles.extra_u32;
|
keir@15979
|
543 if ( (rec->cycles_included = cycles) != 0 )
|
gdunlap@15968
|
544 {
|
gdunlap@15968
|
545 u64 tsc = (u64)get_cycles();
|
keir@15979
|
546 rec->u.cycles.cycles_lo = (uint32_t)tsc;
|
keir@15979
|
547 rec->u.cycles.cycles_hi = (uint32_t)(tsc >> 32);
|
keir@15979
|
548 dst = (unsigned char *)rec->u.cycles.extra_u32;
|
keir@15979
|
549 }
|
gdunlap@15968
|
550
|
keir@15979
|
551 if ( extra_data && extra )
|
gdunlap@15968
|
552 memcpy(dst, extra_data, extra);
|
gdunlap@15968
|
553
|
gdunlap@15968
|
554 wmb();
|
keir@17038
|
555
|
keir@21752
|
556 next += rec_size;
|
keir@17038
|
557 if ( next >= 2*data_size )
|
keir@17038
|
558 next -= 2*data_size;
|
keir@17038
|
559 ASSERT(next < 2*data_size);
|
keir@17038
|
560 buf->prod = next;
|
gdunlap@15968
|
561 }
|
gdunlap@15968
|
562
|
keir@21752
|
563 static inline void insert_wrap_record(struct t_buf *buf,
|
keir@21752
|
564 unsigned int size)
|
gdunlap@15968
|
565 {
|
keir@21752
|
566 u32 space_left = calc_bytes_to_wrap(buf);
|
keir@21752
|
567 unsigned int extra_space = space_left - sizeof(u32);
|
keir@21752
|
568 bool_t cycles = 0;
|
gdunlap@15968
|
569
|
gdunlap@15968
|
570 BUG_ON(space_left > size);
|
gdunlap@15968
|
571
|
gdunlap@15968
|
572 /* We may need to add cycles to take up enough space... */
|
keir@15979
|
573 if ( (extra_space/sizeof(u32)) > TRACE_EXTRA_MAX )
|
gdunlap@15968
|
574 {
|
gdunlap@15968
|
575 cycles = 1;
|
gdunlap@15968
|
576 extra_space -= sizeof(u64);
|
gdunlap@15968
|
577 ASSERT((extra_space/sizeof(u32)) <= TRACE_EXTRA_MAX);
|
gdunlap@15968
|
578 }
|
gdunlap@15968
|
579
|
keir@21752
|
580 __insert_record(buf, TRC_TRACE_WRAP_BUFFER, extra_space, cycles,
|
keir@21752
|
581 space_left, NULL);
|
gdunlap@15968
|
582 }
|
gdunlap@15968
|
583
|
keir@18476
|
584 #define LOST_REC_SIZE (4 + 8 + 16) /* header + tsc + sizeof(struct ed) */
|
gdunlap@15968
|
585
|
keir@21752
|
586 static inline void insert_lost_records(struct t_buf *buf)
|
gdunlap@15968
|
587 {
|
gdunlap@15968
|
588 struct {
|
gdunlap@15968
|
589 u32 lost_records;
|
keir@18476
|
590 u32 did:16, vid:16;
|
keir@18476
|
591 u64 first_tsc;
|
keir@18476
|
592 } __attribute__((packed)) ed;
|
gdunlap@15968
|
593
|
keir@18476
|
594 ed.vid = current->vcpu_id;
|
keir@18476
|
595 ed.did = current->domain->domain_id;
|
gdunlap@15968
|
596 ed.lost_records = this_cpu(lost_records);
|
keir@18476
|
597 ed.first_tsc = this_cpu(lost_records_first_tsc);
|
gdunlap@15968
|
598
|
gdunlap@15968
|
599 this_cpu(lost_records) = 0;
|
gdunlap@15968
|
600
|
keir@21752
|
601 __insert_record(buf, TRC_LOST_RECORDS, sizeof(ed), 1 /* cycles */,
|
keir@21752
|
602 LOST_REC_SIZE, &ed);
|
gdunlap@15968
|
603 }
|
gdunlap@15968
|
604
|
keir@17552
|
605 /*
|
keir@17552
|
606 * Notification is performed in qtasklet to avoid deadlocks with contexts
|
keir@17552
|
607 * which __trace_var() may be called from (e.g., scheduler critical regions).
|
keir@17552
|
608 */
|
keir@17552
|
609 static void trace_notify_dom0(unsigned long unused)
|
keir@17552
|
610 {
|
keir@17552
|
611 send_guest_global_virq(dom0, VIRQ_TBUF);
|
keir@17552
|
612 }
|
keir@17552
|
613 static DECLARE_TASKLET(trace_notify_dom0_tasklet, trace_notify_dom0, 0);
|
gdunlap@15968
|
614
|
kaf24@7606
|
615 /**
|
kaf24@7606
|
616 * trace - Enters a trace tuple into the trace buffer for the current CPU.
|
kaf24@7606
|
617 * @event: the event type being logged
|
kaf24@7606
|
618 * @d1...d5: the data items for the event being logged
|
kaf24@7606
|
619 *
|
kaf24@7606
|
620 * Logs a trace record into the appropriate buffer. Returns nonzero on
|
kaf24@7606
|
621 * failure, otherwise 0. Failure occurs only if the trace buffers are not yet
|
kaf24@7606
|
622 * initialised.
|
kaf24@7606
|
623 */
|
keir@21752
|
624 void __trace_var(u32 event, bool_t cycles, unsigned int extra,
|
keir@21752
|
625 const void *extra_data)
|
kaf24@7606
|
626 {
|
kaf24@7606
|
627 struct t_buf *buf;
|
keir@21752
|
628 unsigned long flags;
|
keir@21752
|
629 u32 bytes_to_tail, bytes_to_wrap;
|
keir@21752
|
630 unsigned int rec_size, total_size;
|
keir@21752
|
631 unsigned int extra_word;
|
keir@21752
|
632 bool_t started_below_highwater;
|
gdunlap@15968
|
633
|
keir@18479
|
634 if( !tb_init_done )
|
keir@18479
|
635 return;
|
gdunlap@15968
|
636
|
gdunlap@15968
|
637 /* Convert byte count into word count, rounding up */
|
gdunlap@15968
|
638 extra_word = (extra / sizeof(u32));
|
keir@16111
|
639 if ( (extra % sizeof(u32)) != 0 )
|
gdunlap@15968
|
640 extra_word++;
|
kfraser@10698
|
641
|
keir@16111
|
642 ASSERT(extra_word <= TRACE_EXTRA_MAX);
|
keir@16111
|
643 extra_word = min_t(int, extra_word, TRACE_EXTRA_MAX);
|
gdunlap@15968
|
644
|
gdunlap@15968
|
645 /* Round size up to nearest word */
|
gdunlap@15968
|
646 extra = extra_word * sizeof(u32);
|
kaf24@7606
|
647
|
kaf24@7606
|
648 if ( (tb_event_mask & event) == 0 )
|
kaf24@7606
|
649 return;
|
kaf24@7606
|
650
|
kaf24@7606
|
651 /* match class */
|
kaf24@7606
|
652 if ( ((tb_event_mask >> TRC_CLS_SHIFT) & (event >> TRC_CLS_SHIFT)) == 0 )
|
kaf24@7606
|
653 return;
|
kaf24@7606
|
654
|
kaf24@7606
|
655 /* then match subclass */
|
kaf24@7606
|
656 if ( (((tb_event_mask >> TRC_SUBCLS_SHIFT) & 0xf )
|
kaf24@7606
|
657 & ((event >> TRC_SUBCLS_SHIFT) & 0xf )) == 0 )
|
kaf24@7606
|
658 return;
|
kaf24@7606
|
659
|
kfraser@11295
|
660 if ( !cpu_isset(smp_processor_id(), tb_cpu_mask) )
|
kaf24@7606
|
661 return;
|
kaf24@7606
|
662
|
kaf24@7606
|
663 /* Read tb_init_done /before/ t_bufs. */
|
kaf24@7606
|
664 rmb();
|
kaf24@7606
|
665
|
keir@20873
|
666 spin_lock_irqsave(&this_cpu(t_lock), flags);
|
keir@20873
|
667
|
kaf24@11022
|
668 buf = this_cpu(t_bufs);
|
kaf24@7606
|
669
|
keir@20873
|
670 if ( unlikely(!buf) )
|
keir@21752
|
671 {
|
keir@21752
|
672 /* Make gcc happy */
|
keir@21752
|
673 started_below_highwater = 0;
|
keir@20873
|
674 goto unlock;
|
keir@21752
|
675 }
|
kaf24@7609
|
676
|
keir@17038
|
677 started_below_highwater = (calc_unconsumed_bytes(buf) < t_buf_highwater);
|
gdunlap@15968
|
678
|
gdunlap@15968
|
679 /* Calculate the record size */
|
gdunlap@15968
|
680 rec_size = calc_rec_size(cycles, extra);
|
gdunlap@15968
|
681
|
gdunlap@15968
|
682 /* How many bytes are available in the buffer? */
|
gdunlap@15968
|
683 bytes_to_tail = calc_bytes_avail(buf);
|
gdunlap@15968
|
684
|
gdunlap@15968
|
685 /* How many bytes until the next wrap-around? */
|
gdunlap@15968
|
686 bytes_to_wrap = calc_bytes_to_wrap(buf);
|
gdunlap@15968
|
687
|
gdunlap@15968
|
688 /*
|
gdunlap@15968
|
689 * Calculate expected total size to commit this record by
|
gdunlap@15968
|
690 * doing a dry-run.
|
gdunlap@15968
|
691 */
|
gdunlap@15968
|
692 total_size = 0;
|
gdunlap@15968
|
693
|
gdunlap@15968
|
694 /* First, check to see if we need to include a lost_record.
|
gdunlap@15968
|
695 */
|
keir@15979
|
696 if ( this_cpu(lost_records) )
|
gdunlap@15968
|
697 {
|
keir@15979
|
698 if ( LOST_REC_SIZE > bytes_to_wrap )
|
gdunlap@15968
|
699 {
|
gdunlap@15968
|
700 total_size += bytes_to_wrap;
|
gdunlap@15968
|
701 bytes_to_wrap = data_size;
|
gdunlap@15968
|
702 }
|
gdunlap@15968
|
703 total_size += LOST_REC_SIZE;
|
keir@17037
|
704 bytes_to_wrap -= LOST_REC_SIZE;
|
keir@17037
|
705
|
keir@17037
|
706 /* LOST_REC might line up perfectly with the buffer wrap */
|
keir@17037
|
707 if ( bytes_to_wrap == 0 )
|
keir@17037
|
708 bytes_to_wrap = data_size;
|
gdunlap@15968
|
709 }
|
gdunlap@15968
|
710
|
keir@15979
|
711 if ( rec_size > bytes_to_wrap )
|
gdunlap@15968
|
712 {
|
gdunlap@15968
|
713 total_size += bytes_to_wrap;
|
gdunlap@15968
|
714 }
|
gdunlap@15968
|
715 total_size += rec_size;
|
gdunlap@15968
|
716
|
gdunlap@15968
|
717 /* Do we have enough space for everything? */
|
keir@15979
|
718 if ( total_size > bytes_to_tail )
|
kaf24@7606
|
719 {
|
keir@18476
|
720 if ( ++this_cpu(lost_records) == 1 )
|
keir@18476
|
721 this_cpu(lost_records_first_tsc)=(u64)get_cycles();
|
keir@20873
|
722 started_below_highwater = 0;
|
keir@20873
|
723 goto unlock;
|
kaf24@7606
|
724 }
|
kaf24@7606
|
725
|
gdunlap@15968
|
726 /*
|
gdunlap@15968
|
727 * Now, actually write information
|
gdunlap@15968
|
728 */
|
gdunlap@15968
|
729 bytes_to_wrap = calc_bytes_to_wrap(buf);
|
gdunlap@15968
|
730
|
keir@15979
|
731 if ( this_cpu(lost_records) )
|
kfraser@10698
|
732 {
|
keir@15979
|
733 if ( LOST_REC_SIZE > bytes_to_wrap )
|
ack@13311
|
734 {
|
gdunlap@15968
|
735 insert_wrap_record(buf, LOST_REC_SIZE);
|
gdunlap@15968
|
736 bytes_to_wrap = data_size;
|
gdunlap@15968
|
737 }
|
gdunlap@15968
|
738 insert_lost_records(buf);
|
keir@17037
|
739 bytes_to_wrap -= LOST_REC_SIZE;
|
keir@17037
|
740
|
keir@17037
|
741 /* LOST_REC might line up perfectly with the buffer wrap */
|
keir@17037
|
742 if ( bytes_to_wrap == 0 )
|
keir@17037
|
743 bytes_to_wrap = data_size;
|
kfraser@10698
|
744 }
|
kfraser@10698
|
745
|
keir@15979
|
746 if ( rec_size > bytes_to_wrap )
|
gdunlap@15968
|
747 insert_wrap_record(buf, rec_size);
|
kaf24@7609
|
748
|
gdunlap@15968
|
749 /* Write the original record */
|
gdunlap@15968
|
750 __insert_record(buf, event, extra, cycles, rec_size, extra_data);
|
kaf24@7609
|
751
|
keir@20873
|
752 unlock:
|
keir@20873
|
753 spin_unlock_irqrestore(&this_cpu(t_lock), flags);
|
kaf24@9625
|
754
|
keir@15979
|
755 /* Notify trace buffer consumer that we've crossed the high water mark. */
|
keir@21752
|
756 if ( likely(buf!=NULL)
|
keir@21752
|
757 && started_below_highwater
|
keir@21752
|
758 && (calc_unconsumed_bytes(buf) >= t_buf_highwater) )
|
keir@17552
|
759 tasklet_schedule(&trace_notify_dom0_tasklet);
|
kaf24@7606
|
760 }
|
kaf24@7606
|
761
|
kaf24@3952
|
762 /*
|
kaf24@3952
|
763 * Local variables:
|
kaf24@3952
|
764 * mode: C
|
kaf24@3952
|
765 * c-set-style: "BSD"
|
kaf24@3952
|
766 * c-basic-offset: 4
|
kaf24@3952
|
767 * tab-width: 4
|
kaf24@3952
|
768 * indent-tabs-mode: nil
|
kaf24@4026
|
769 * End:
|
kaf24@3952
|
770 */
|