debuggers.hg

annotate xen/common/trace.c @ 22855:1d1eec7e1fb4

xl: Perform minimal validation of virtual disk file while parsing config file

This patch performs some very basic validation on the virtual disk
file passed through the config file. This validation ensures that we
don't go too far with the initialization like spawn qemu and more
while there could be some potentially fundamental issues.

[ Patch fixed up to work with PHYSTYPE_EMPTY 22808:6ec61438713a -iwj ]

Signed-off-by: Kamala Narasimhan <kamala.narasimhan@citrix.com>
Acked-by: Ian Jackson <ian.jackson@eu.citrix.com>
Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com>
Committed-by: Ian Jackson <ian.jackson@eu.citrix.com>
author Kamala Narasimhan <kamala.narasimhan@gmail.com>
date Tue Jan 25 18:09:49 2011 +0000 (2011-01-25)
parents f5f3cf4e001f
children
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 */