1 /*
2 * Copyright (C) 2005 Hewlett-Packard Co.
3 * written by Aravind Menon & Jose Renato Santos
4 * (email: xenoprof@groups.hp.com)
5 *
6 * arch generic xenoprof and IA64 support.
7 * dynamic map/unmap xenoprof buffer support.
8 * Copyright (c) 2006 Isaku Yamahata <yamahata at valinux co jp>
9 * VA Linux Systems Japan K.K.
10 */
11
12 #ifndef COMPAT
13 #include <xen/guest_access.h>
14 #include <xen/sched.h>
15 #include <xen/event.h>
16 #include <xen/xenoprof.h>
17 #include <public/xenoprof.h>
18 #include <xen/paging.h>
19 #include <xsm/xsm.h>
20 #include <xen/hypercall.h>
21
22 /* Override macros from asm/page.h to make them work with mfn_t */
23 #undef virt_to_mfn
24 #define virt_to_mfn(va) _mfn(__virt_to_mfn(va))
25
26 #define XENOPROF_DOMAIN_IGNORED 0
27 #define XENOPROF_DOMAIN_ACTIVE 1
28 #define XENOPROF_DOMAIN_PASSIVE 2
29
30 #define XENOPROF_IDLE 0
31 #define XENOPROF_INITIALIZED 1
32 #define XENOPROF_COUNTERS_RESERVED 2
33 #define XENOPROF_READY 3
34 #define XENOPROF_PROFILING 4
35
36 #ifndef CONFIG_COMPAT
37 #define XENOPROF_COMPAT(x) false
38 typedef struct xenoprof_buf xenoprof_buf_t;
39 #define xenoprof_buf(d, b, field) ACCESS_ONCE((b)->field)
40 #else
41 #include <compat/xenoprof.h>
42 #define XENOPROF_COMPAT(x) ((x)->is_compat)
43 typedef union {
44 struct xenoprof_buf native;
45 struct compat_oprof_buf compat;
46 } xenoprof_buf_t;
47 #define xenoprof_buf(d, b, field) ACCESS_ONCE(*(!(d)->xenoprof->is_compat \
48 ? &(b)->native.field \
49 : &(b)->compat.field))
50 #endif
51
52 /* Limit amount of pages used for shared buffer (per domain) */
53 #define MAX_OPROF_SHARED_PAGES 32
54
55 /* Lock protecting the following global state */
56 static DEFINE_SPINLOCK(xenoprof_lock);
57
58 static DEFINE_SPINLOCK(pmu_owner_lock);
59 int pmu_owner = 0;
60 int pmu_hvm_refcount = 0;
61
62 struct xenoprof_vcpu {
63 int event_size;
64 xenoprof_buf_t *buffer;
65 };
66
67 struct xenoprof {
68 char *rawbuf;
69 int npages;
70 int nbuf;
71 int bufsize;
72 int domain_type;
73 #ifdef CONFIG_COMPAT
74 bool is_compat;
75 #endif
76 struct xenoprof_vcpu *vcpu;
77 };
78
79 static struct domain *active_domains[MAX_OPROF_DOMAINS];
80 static int active_ready[MAX_OPROF_DOMAINS];
81 static unsigned int adomains;
82
83 static struct domain *passive_domains[MAX_OPROF_DOMAINS];
84 static unsigned int pdomains;
85
86 static unsigned int activated;
87 static struct domain *xenoprof_primary_profiler;
88 static int xenoprof_state = XENOPROF_IDLE;
89 static unsigned long backtrace_depth;
90
91 static u64 total_samples;
92 static u64 invalid_buffer_samples;
93 static u64 corrupted_buffer_samples;
94 static u64 lost_samples;
95 static u64 active_samples;
96 static u64 passive_samples;
97 static u64 idle_samples;
98 static u64 others_samples;
99
acquire_pmu_ownership(int pmu_ownership)100 int acquire_pmu_ownership(int pmu_ownership)
101 {
102 spin_lock(&pmu_owner_lock);
103 if ( pmu_owner == PMU_OWNER_NONE )
104 {
105 pmu_owner = pmu_ownership;
106 goto out;
107 }
108
109 if ( pmu_owner == pmu_ownership )
110 goto out;
111
112 spin_unlock(&pmu_owner_lock);
113 return 0;
114 out:
115 if ( pmu_owner == PMU_OWNER_HVM )
116 pmu_hvm_refcount++;
117 spin_unlock(&pmu_owner_lock);
118 return 1;
119 }
120
release_pmu_ownership(int pmu_ownership)121 void release_pmu_ownership(int pmu_ownership)
122 {
123 spin_lock(&pmu_owner_lock);
124 if ( pmu_ownership == PMU_OWNER_HVM )
125 pmu_hvm_refcount--;
126 if ( !pmu_hvm_refcount )
127 pmu_owner = PMU_OWNER_NONE;
128 spin_unlock(&pmu_owner_lock);
129 }
130
is_active(struct domain * d)131 int is_active(struct domain *d)
132 {
133 struct xenoprof *x = d->xenoprof;
134 return ((x != NULL) && (x->domain_type == XENOPROF_DOMAIN_ACTIVE));
135 }
136
is_passive(struct domain * d)137 int is_passive(struct domain *d)
138 {
139 struct xenoprof *x = d->xenoprof;
140 return ((x != NULL) && (x->domain_type == XENOPROF_DOMAIN_PASSIVE));
141 }
142
is_profiled(struct domain * d)143 static int is_profiled(struct domain *d)
144 {
145 return (is_active(d) || is_passive(d));
146 }
147
xenoprof_reset_stat(void)148 static void xenoprof_reset_stat(void)
149 {
150 total_samples = 0;
151 invalid_buffer_samples = 0;
152 corrupted_buffer_samples = 0;
153 lost_samples = 0;
154 active_samples = 0;
155 passive_samples = 0;
156 idle_samples = 0;
157 others_samples = 0;
158 }
159
xenoprof_reset_buf(struct domain * d)160 static void xenoprof_reset_buf(struct domain *d)
161 {
162 int j;
163 xenoprof_buf_t *buf;
164
165 if ( d->xenoprof == NULL )
166 {
167 printk("xenoprof_reset_buf: ERROR - Unexpected "
168 "Xenoprof NULL pointer \n");
169 return;
170 }
171
172 for ( j = 0; j < d->max_vcpus; j++ )
173 {
174 buf = d->xenoprof->vcpu[j].buffer;
175 if ( buf != NULL )
176 {
177 xenoprof_buf(d, buf, event_head) = 0;
178 xenoprof_buf(d, buf, event_tail) = 0;
179 }
180 }
181 }
182
183 static int
share_xenoprof_page_with_guest(struct domain * d,mfn_t mfn,int npages)184 share_xenoprof_page_with_guest(struct domain *d, mfn_t mfn, int npages)
185 {
186 int i;
187
188 /* Check if previous page owner has released the page. */
189 for ( i = 0; i < npages; i++ )
190 {
191 struct page_info *page = mfn_to_page(mfn_add(mfn, i));
192
193 if ( (page->count_info & (PGC_allocated|PGC_count_mask)) != 0 )
194 {
195 printk(XENLOG_G_INFO "dom%d mfn %#lx page->count_info %#lx\n",
196 d->domain_id, mfn_x(mfn_add(mfn, i)), page->count_info);
197 return -EBUSY;
198 }
199 page_set_owner(page, NULL);
200 }
201
202 for ( i = 0; i < npages; i++ )
203 share_xen_page_with_guest(mfn_to_page(mfn_add(mfn, i)), d, SHARE_rw);
204
205 return 0;
206 }
207
208 static void
unshare_xenoprof_page_with_guest(struct xenoprof * x)209 unshare_xenoprof_page_with_guest(struct xenoprof *x)
210 {
211 int i, npages = x->npages;
212 mfn_t mfn = virt_to_mfn(x->rawbuf);
213
214 for ( i = 0; i < npages; i++ )
215 {
216 struct page_info *page = mfn_to_page(mfn_add(mfn, i));
217
218 BUG_ON(page_get_owner(page) != current->domain);
219 put_page_alloc_ref(page);
220 }
221 }
222
223 static void
xenoprof_shared_gmfn_with_guest(struct domain * d,unsigned long maddr,unsigned long gmaddr,int npages)224 xenoprof_shared_gmfn_with_guest(
225 struct domain *d, unsigned long maddr, unsigned long gmaddr, int npages)
226 {
227 int i;
228
229 for ( i = 0; i < npages; i++, maddr += PAGE_SIZE, gmaddr += PAGE_SIZE )
230 {
231 BUG_ON(page_get_owner(maddr_to_page(maddr)) != d);
232 if ( i == 0 )
233 gdprintk(XENLOG_WARNING,
234 "xenoprof unsupported with autotranslated guests\n");
235
236 }
237 }
238
alloc_xenoprof_struct(struct domain * d,int max_samples,int is_passive)239 static int alloc_xenoprof_struct(
240 struct domain *d, int max_samples, int is_passive)
241 {
242 struct vcpu *v;
243 int nvcpu, npages, bufsize, max_bufsize;
244 unsigned max_max_samples;
245 int i;
246
247 nvcpu = 0;
248 for_each_vcpu ( d, v )
249 nvcpu++;
250
251 if ( !nvcpu )
252 return -EINVAL;
253
254 d->xenoprof = xzalloc(struct xenoprof);
255 if ( d->xenoprof == NULL )
256 {
257 printk("alloc_xenoprof_struct(): memory allocation failed\n");
258 return -ENOMEM;
259 }
260
261 d->xenoprof->vcpu = xzalloc_array(struct xenoprof_vcpu, d->max_vcpus);
262 if ( d->xenoprof->vcpu == NULL )
263 {
264 xfree(d->xenoprof);
265 d->xenoprof = NULL;
266 printk("alloc_xenoprof_struct(): vcpu array allocation failed\n");
267 return -ENOMEM;
268 }
269
270 bufsize = sizeof(struct xenoprof_buf);
271 i = sizeof(struct event_log);
272 #ifdef CONFIG_COMPAT
273 d->xenoprof->is_compat = is_pv_32bit_domain(is_passive ? hardware_domain : d);
274 if ( XENOPROF_COMPAT(d->xenoprof) )
275 {
276 bufsize = sizeof(struct compat_oprof_buf);
277 i = sizeof(struct compat_event_log);
278 }
279 #endif
280
281 /* reduce max_samples if necessary to limit pages allocated */
282 max_bufsize = (MAX_OPROF_SHARED_PAGES * PAGE_SIZE) / nvcpu;
283 max_max_samples = ( (max_bufsize - bufsize) / i ) + 1;
284 if ( (unsigned)max_samples > max_max_samples )
285 max_samples = max_max_samples;
286
287 bufsize += (max_samples - 1) * i;
288 npages = (nvcpu * bufsize - 1) / PAGE_SIZE + 1;
289
290 d->xenoprof->rawbuf = alloc_xenheap_pages(get_order_from_pages(npages), 0);
291 if ( d->xenoprof->rawbuf == NULL )
292 {
293 xfree(d->xenoprof->vcpu);
294 xfree(d->xenoprof);
295 d->xenoprof = NULL;
296 return -ENOMEM;
297 }
298
299 for ( i = 0; i < npages; ++i )
300 clear_page(d->xenoprof->rawbuf + i * PAGE_SIZE);
301
302 d->xenoprof->npages = npages;
303 d->xenoprof->nbuf = nvcpu;
304 d->xenoprof->bufsize = bufsize;
305 d->xenoprof->domain_type = XENOPROF_DOMAIN_IGNORED;
306
307 /* Update buffer pointers for active vcpus */
308 i = 0;
309 for_each_vcpu ( d, v )
310 {
311 xenoprof_buf_t *buf = (xenoprof_buf_t *)
312 &d->xenoprof->rawbuf[i * bufsize];
313
314 d->xenoprof->vcpu[v->vcpu_id].event_size = max_samples;
315 d->xenoprof->vcpu[v->vcpu_id].buffer = buf;
316 xenoprof_buf(d, buf, event_size) = max_samples;
317 xenoprof_buf(d, buf, vcpu_id) = v->vcpu_id;
318
319 i++;
320 /* in the unlikely case that the number of active vcpus changes */
321 if ( i >= nvcpu )
322 break;
323 }
324
325 return 0;
326 }
327
free_xenoprof_pages(struct domain * d)328 void free_xenoprof_pages(struct domain *d)
329 {
330 struct xenoprof *x;
331 int order;
332
333 x = d->xenoprof;
334 if ( x == NULL )
335 return;
336
337 if ( x->rawbuf != NULL )
338 {
339 order = get_order_from_pages(x->npages);
340 free_xenheap_pages(x->rawbuf, order);
341 }
342
343 xfree(x->vcpu);
344 xfree(x);
345 d->xenoprof = NULL;
346 }
347
active_index(struct domain * d)348 static int active_index(struct domain *d)
349 {
350 int i;
351
352 for ( i = 0; i < adomains; i++ )
353 if ( active_domains[i] == d )
354 return i;
355
356 return -1;
357 }
358
set_active(struct domain * d)359 static int set_active(struct domain *d)
360 {
361 int ind;
362 struct xenoprof *x;
363
364 ind = active_index(d);
365 if ( ind < 0 )
366 return -EPERM;
367
368 x = d->xenoprof;
369 if ( x == NULL )
370 return -EPERM;
371
372 x->domain_type = XENOPROF_DOMAIN_ACTIVE;
373 active_ready[ind] = 1;
374 activated++;
375
376 return 0;
377 }
378
reset_active(struct domain * d)379 static int reset_active(struct domain *d)
380 {
381 int ind;
382 struct xenoprof *x;
383
384 ind = active_index(d);
385 if ( ind < 0 )
386 return -EPERM;
387
388 x = d->xenoprof;
389 if ( x == NULL )
390 return -EPERM;
391
392 x->domain_type = XENOPROF_DOMAIN_IGNORED;
393 active_ready[ind] = 0;
394 active_domains[ind] = NULL;
395 activated--;
396 put_domain(d);
397
398 if ( activated <= 0 )
399 adomains = 0;
400
401 return 0;
402 }
403
reset_passive(struct domain * d)404 static void reset_passive(struct domain *d)
405 {
406 struct xenoprof *x;
407
408 if ( d == NULL )
409 return;
410
411 x = d->xenoprof;
412 if ( x == NULL )
413 return;
414
415 x->domain_type = XENOPROF_DOMAIN_IGNORED;
416 unshare_xenoprof_page_with_guest(x);
417 }
418
reset_active_list(void)419 static void reset_active_list(void)
420 {
421 int i;
422
423 for ( i = 0; i < adomains; i++ )
424 if ( active_ready[i] )
425 reset_active(active_domains[i]);
426
427 adomains = 0;
428 activated = 0;
429 }
430
reset_passive_list(void)431 static void reset_passive_list(void)
432 {
433 int i;
434
435 for ( i = 0; i < pdomains; i++ )
436 {
437 reset_passive(passive_domains[i]);
438 put_domain(passive_domains[i]);
439 passive_domains[i] = NULL;
440 }
441
442 pdomains = 0;
443 }
444
add_active_list(domid_t domid)445 static int add_active_list(domid_t domid)
446 {
447 struct domain *d;
448
449 if ( adomains >= MAX_OPROF_DOMAINS )
450 return -E2BIG;
451
452 d = get_domain_by_id(domid);
453 if ( d == NULL )
454 return -EINVAL;
455
456 active_domains[adomains] = d;
457 active_ready[adomains] = 0;
458 adomains++;
459
460 return 0;
461 }
462
add_passive_list(XEN_GUEST_HANDLE_PARAM (void)arg)463 static int add_passive_list(XEN_GUEST_HANDLE_PARAM(void) arg)
464 {
465 struct xenoprof_passive passive;
466 struct domain *d;
467 int ret = 0;
468
469 if ( pdomains >= MAX_OPROF_DOMAINS )
470 return -E2BIG;
471
472 if ( copy_from_guest(&passive, arg, 1) )
473 return -EFAULT;
474
475 d = get_domain_by_id(passive.domain_id);
476 if ( d == NULL )
477 return -EINVAL;
478
479 if ( d->xenoprof == NULL )
480 {
481 ret = alloc_xenoprof_struct(d, passive.max_samples, 1);
482 if ( ret < 0 )
483 {
484 put_domain(d);
485 return -ENOMEM;
486 }
487 }
488
489 ret = share_xenoprof_page_with_guest(
490 current->domain, virt_to_mfn(d->xenoprof->rawbuf),
491 d->xenoprof->npages);
492 if ( ret < 0 )
493 {
494 put_domain(d);
495 return ret;
496 }
497
498 d->xenoprof->domain_type = XENOPROF_DOMAIN_PASSIVE;
499 passive.nbuf = d->xenoprof->nbuf;
500 passive.bufsize = d->xenoprof->bufsize;
501 if ( !paging_mode_translate(current->domain) )
502 passive.buf_gmaddr = __pa(d->xenoprof->rawbuf);
503 else
504 xenoprof_shared_gmfn_with_guest(
505 current->domain, __pa(d->xenoprof->rawbuf),
506 passive.buf_gmaddr, d->xenoprof->npages);
507
508 if ( __copy_to_guest(arg, &passive, 1) )
509 {
510 put_domain(d);
511 return -EFAULT;
512 }
513
514 passive_domains[pdomains] = d;
515 pdomains++;
516
517 return ret;
518 }
519
520
521 /* Get space in the buffer */
xenoprof_buf_space(int head,int tail,int size)522 static int xenoprof_buf_space(int head, int tail, int size)
523 {
524 return ((tail > head) ? 0 : size) + tail - head - 1;
525 }
526
527 /* Check for space and add a sample. Return 1 if successful, 0 otherwise. */
xenoprof_add_sample(const struct domain * d,const struct xenoprof_vcpu * v,uint64_t eip,int mode,int event)528 static int xenoprof_add_sample(const struct domain *d,
529 const struct xenoprof_vcpu *v,
530 uint64_t eip, int mode, int event)
531 {
532 xenoprof_buf_t *buf = v->buffer;
533 int head, tail, size;
534
535 head = xenoprof_buf(d, buf, event_head);
536 tail = xenoprof_buf(d, buf, event_tail);
537 size = v->event_size;
538
539 /* make sure indexes in shared buffer are sane */
540 if ( (head < 0) || (head >= size) || (tail < 0) || (tail >= size) )
541 {
542 corrupted_buffer_samples++;
543 return 0;
544 }
545
546 if ( xenoprof_buf_space(head, tail, size) > 0 )
547 {
548 xenoprof_buf(d, buf, event_log[head].eip) = eip;
549 xenoprof_buf(d, buf, event_log[head].mode) = mode;
550 xenoprof_buf(d, buf, event_log[head].event) = event;
551 head++;
552 if ( head >= size )
553 head = 0;
554
555 xenoprof_buf(d, buf, event_head) = head;
556 }
557 else
558 {
559 xenoprof_buf(d, buf, lost_samples)++;
560 lost_samples++;
561 return 0;
562 }
563
564 return 1;
565 }
566
xenoprof_add_trace(struct vcpu * vcpu,uint64_t pc,int mode)567 int xenoprof_add_trace(struct vcpu *vcpu, uint64_t pc, int mode)
568 {
569 struct domain *d = vcpu->domain;
570
571 /* Do not accidentally write an escape code due to a broken frame. */
572 if ( pc == XENOPROF_ESCAPE_CODE )
573 {
574 invalid_buffer_samples++;
575 return 0;
576 }
577
578 return xenoprof_add_sample(d, &d->xenoprof->vcpu[vcpu->vcpu_id],
579 pc, mode, 0);
580 }
581
xenoprof_log_event(struct vcpu * vcpu,const struct cpu_user_regs * regs,uint64_t pc,int mode,int event)582 void xenoprof_log_event(struct vcpu *vcpu, const struct cpu_user_regs *regs,
583 uint64_t pc, int mode, int event)
584 {
585 struct domain *d = vcpu->domain;
586 struct xenoprof_vcpu *v;
587 xenoprof_buf_t *buf;
588
589 total_samples++;
590
591 /* Ignore samples of un-monitored domains. */
592 if ( !is_profiled(d) )
593 {
594 others_samples++;
595 return;
596 }
597
598 v = &d->xenoprof->vcpu[vcpu->vcpu_id];
599 if ( v->buffer == NULL )
600 {
601 invalid_buffer_samples++;
602 return;
603 }
604
605 buf = v->buffer;
606
607 /* Provide backtrace if requested. */
608 if ( backtrace_depth > 0 )
609 {
610 if ( xenoprof_buf_space(xenoprof_buf(d, buf, event_head),
611 xenoprof_buf(d, buf, event_tail),
612 v->event_size) < 2 )
613 {
614 xenoprof_buf(d, buf, lost_samples)++;
615 lost_samples++;
616 return;
617 }
618
619 /* xenoprof_add_sample() will increment lost_samples on failure */
620 if ( !xenoprof_add_sample(d, v, XENOPROF_ESCAPE_CODE, mode,
621 XENOPROF_TRACE_BEGIN) )
622 return;
623 }
624
625 if ( xenoprof_add_sample(d, v, pc, mode, event) )
626 {
627 if ( is_active(vcpu->domain) )
628 active_samples++;
629 else
630 passive_samples++;
631 if ( mode == 0 )
632 xenoprof_buf(d, buf, user_samples)++;
633 else if ( mode == 1 )
634 xenoprof_buf(d, buf, kernel_samples)++;
635 else
636 xenoprof_buf(d, buf, xen_samples)++;
637
638 }
639
640 if ( backtrace_depth > 0 )
641 xenoprof_backtrace(vcpu, regs, backtrace_depth, mode);
642 }
643
644
645
xenoprof_op_init(XEN_GUEST_HANDLE_PARAM (void)arg)646 static int xenoprof_op_init(XEN_GUEST_HANDLE_PARAM(void) arg)
647 {
648 struct domain *d = current->domain;
649 struct xenoprof_init xenoprof_init;
650 int ret;
651
652 if ( copy_from_guest(&xenoprof_init, arg, 1) )
653 return -EFAULT;
654
655 if ( (ret = xenoprof_arch_init(&xenoprof_init.num_events,
656 xenoprof_init.cpu_type)) )
657 return ret;
658
659 /* Only the hardware domain may become the primary profiler here because
660 * there is currently no cleanup of xenoprof_primary_profiler or associated
661 * profiling state when the primary profiling domain is shut down or
662 * crashes. Once a better cleanup method is present, it will be possible to
663 * allow another domain to be the primary profiler.
664 */
665 xenoprof_init.is_primary =
666 ((xenoprof_primary_profiler == d) ||
667 ((xenoprof_primary_profiler == NULL) && is_hardware_domain(d)));
668 if ( xenoprof_init.is_primary )
669 xenoprof_primary_profiler = current->domain;
670
671 return __copy_to_guest(arg, &xenoprof_init, 1) ? -EFAULT : 0;
672 }
673
674 #define ret_t long
675
676 #endif /* !COMPAT */
677
xenoprof_op_get_buffer(XEN_GUEST_HANDLE_PARAM (void)arg)678 static int xenoprof_op_get_buffer(XEN_GUEST_HANDLE_PARAM(void) arg)
679 {
680 struct xenoprof_get_buffer xenoprof_get_buffer;
681 struct domain *d = current->domain;
682 int ret;
683
684 if ( copy_from_guest(&xenoprof_get_buffer, arg, 1) )
685 return -EFAULT;
686
687 /*
688 * We allocate xenoprof struct and buffers only at first time
689 * get_buffer is called. Memory is then kept until domain is destroyed.
690 */
691 if ( d->xenoprof == NULL )
692 {
693 ret = alloc_xenoprof_struct(d, xenoprof_get_buffer.max_samples, 0);
694 if ( ret < 0 )
695 return ret;
696 }
697 else
698 d->xenoprof->domain_type = XENOPROF_DOMAIN_IGNORED;
699
700 ret = share_xenoprof_page_with_guest(
701 d, virt_to_mfn(d->xenoprof->rawbuf), d->xenoprof->npages);
702 if ( ret < 0 )
703 return ret;
704
705 xenoprof_reset_buf(d);
706
707 xenoprof_get_buffer.nbuf = d->xenoprof->nbuf;
708 xenoprof_get_buffer.bufsize = d->xenoprof->bufsize;
709 if ( !paging_mode_translate(d) )
710 xenoprof_get_buffer.buf_gmaddr = __pa(d->xenoprof->rawbuf);
711 else
712 xenoprof_shared_gmfn_with_guest(
713 d, __pa(d->xenoprof->rawbuf), xenoprof_get_buffer.buf_gmaddr,
714 d->xenoprof->npages);
715
716 return __copy_to_guest(arg, &xenoprof_get_buffer, 1) ? -EFAULT : 0;
717 }
718
719 #define NONPRIV_OP(op) ( (op == XENOPROF_init) \
720 || (op == XENOPROF_enable_virq) \
721 || (op == XENOPROF_disable_virq) \
722 || (op == XENOPROF_get_buffer))
723
do_xenoprof_op(int op,XEN_GUEST_HANDLE_PARAM (void)arg)724 ret_t do_xenoprof_op(int op, XEN_GUEST_HANDLE_PARAM(void) arg)
725 {
726 int ret = 0;
727
728 if ( (op < 0) || (op > XENOPROF_last_op) )
729 {
730 gdprintk(XENLOG_DEBUG, "invalid operation %d\n", op);
731 return -EINVAL;
732 }
733
734 if ( !NONPRIV_OP(op) && (current->domain != xenoprof_primary_profiler) )
735 {
736 gdprintk(XENLOG_DEBUG, "denied privileged operation %d\n", op);
737 return -EPERM;
738 }
739
740 ret = xsm_profile(XSM_HOOK, current->domain, op);
741 if ( ret )
742 return ret;
743
744 spin_lock(&xenoprof_lock);
745
746 switch ( op )
747 {
748 case XENOPROF_init:
749 ret = xenoprof_op_init(arg);
750 if ( (ret == 0) &&
751 (current->domain == xenoprof_primary_profiler) )
752 xenoprof_state = XENOPROF_INITIALIZED;
753 break;
754
755 case XENOPROF_get_buffer:
756 if ( !acquire_pmu_ownership(PMU_OWNER_XENOPROF) )
757 {
758 ret = -EBUSY;
759 break;
760 }
761 ret = xenoprof_op_get_buffer(arg);
762 break;
763
764 case XENOPROF_reset_active_list:
765 reset_active_list();
766 ret = 0;
767 break;
768
769 case XENOPROF_reset_passive_list:
770 reset_passive_list();
771 ret = 0;
772 break;
773
774 case XENOPROF_set_active:
775 {
776 domid_t domid;
777 if ( xenoprof_state != XENOPROF_INITIALIZED )
778 {
779 ret = -EPERM;
780 break;
781 }
782 if ( copy_from_guest(&domid, arg, 1) )
783 {
784 ret = -EFAULT;
785 break;
786 }
787 ret = add_active_list(domid);
788 break;
789 }
790
791 case XENOPROF_set_passive:
792 if ( xenoprof_state != XENOPROF_INITIALIZED )
793 {
794 ret = -EPERM;
795 break;
796 }
797 ret = add_passive_list(arg);
798 break;
799
800 case XENOPROF_reserve_counters:
801 if ( xenoprof_state != XENOPROF_INITIALIZED )
802 {
803 ret = -EPERM;
804 break;
805 }
806 ret = xenoprof_arch_reserve_counters();
807 if ( !ret )
808 xenoprof_state = XENOPROF_COUNTERS_RESERVED;
809 break;
810
811 case XENOPROF_counter:
812 if ( (xenoprof_state != XENOPROF_COUNTERS_RESERVED) ||
813 (adomains == 0) )
814 {
815 ret = -EPERM;
816 break;
817 }
818 ret = xenoprof_arch_counter(arg);
819 break;
820
821 case XENOPROF_setup_events:
822 if ( xenoprof_state != XENOPROF_COUNTERS_RESERVED )
823 {
824 ret = -EPERM;
825 break;
826 }
827 ret = xenoprof_arch_setup_events();
828 if ( !ret )
829 xenoprof_state = XENOPROF_READY;
830 break;
831
832 case XENOPROF_enable_virq:
833 {
834 int i;
835
836 if ( current->domain == xenoprof_primary_profiler )
837 {
838 if ( xenoprof_state != XENOPROF_READY )
839 {
840 ret = -EPERM;
841 break;
842 }
843 xenoprof_arch_enable_virq();
844 xenoprof_reset_stat();
845 for ( i = 0; i < pdomains; i++ )
846 xenoprof_reset_buf(passive_domains[i]);
847 }
848 xenoprof_reset_buf(current->domain);
849 ret = set_active(current->domain);
850 break;
851 }
852
853 case XENOPROF_start:
854 ret = -EPERM;
855 if ( (xenoprof_state == XENOPROF_READY) &&
856 (activated == adomains) )
857 ret = xenoprof_arch_start();
858 if ( ret == 0 )
859 xenoprof_state = XENOPROF_PROFILING;
860 break;
861
862 case XENOPROF_stop:
863 {
864 struct domain *d;
865 struct vcpu *v;
866 int i;
867
868 if ( xenoprof_state != XENOPROF_PROFILING )
869 {
870 ret = -EPERM;
871 break;
872 }
873 xenoprof_arch_stop();
874
875 /* Flush remaining samples. */
876 for ( i = 0; i < adomains; i++ )
877 {
878 if ( !active_ready[i] )
879 continue;
880 d = active_domains[i];
881 for_each_vcpu(d, v)
882 send_guest_vcpu_virq(v, VIRQ_XENOPROF);
883 }
884 xenoprof_state = XENOPROF_READY;
885 break;
886 }
887
888 case XENOPROF_disable_virq:
889 {
890 struct xenoprof *x;
891 if ( (xenoprof_state == XENOPROF_PROFILING) &&
892 (is_active(current->domain)) )
893 {
894 ret = -EPERM;
895 break;
896 }
897 if ( (ret = reset_active(current->domain)) != 0 )
898 break;
899 x = current->domain->xenoprof;
900 unshare_xenoprof_page_with_guest(x);
901 release_pmu_ownership(PMU_OWNER_XENOPROF);
902 break;
903 }
904
905 case XENOPROF_release_counters:
906 ret = -EPERM;
907 if ( (xenoprof_state == XENOPROF_COUNTERS_RESERVED) ||
908 (xenoprof_state == XENOPROF_READY) )
909 {
910 xenoprof_state = XENOPROF_INITIALIZED;
911 xenoprof_arch_release_counters();
912 xenoprof_arch_disable_virq();
913 reset_passive_list();
914 ret = 0;
915 }
916 break;
917
918 case XENOPROF_shutdown:
919 ret = -EPERM;
920 if ( xenoprof_state == XENOPROF_INITIALIZED )
921 {
922 activated = 0;
923 adomains=0;
924 xenoprof_primary_profiler = NULL;
925 backtrace_depth=0;
926 ret = 0;
927 }
928 break;
929
930 case XENOPROF_set_backtrace:
931 ret = 0;
932 if ( !xenoprof_backtrace_supported() )
933 ret = -EINVAL;
934 else if ( copy_from_guest(&backtrace_depth, arg, 1) )
935 ret = -EFAULT;
936 break;
937
938 case XENOPROF_ibs_counter:
939 if ( (xenoprof_state != XENOPROF_COUNTERS_RESERVED) ||
940 (adomains == 0) )
941 {
942 ret = -EPERM;
943 break;
944 }
945 ret = xenoprof_arch_ibs_counter(arg);
946 break;
947
948 case XENOPROF_get_ibs_caps:
949 ret = ibs_caps;
950 break;
951
952 default:
953 ret = -ENOSYS;
954 }
955
956 spin_unlock(&xenoprof_lock);
957
958 if ( ret < 0 )
959 gdprintk(XENLOG_DEBUG, "operation %d failed: %d\n", op, ret);
960
961 return ret;
962 }
963
964 #if defined(CONFIG_COMPAT) && !defined(COMPAT)
965 #undef ret_t
966 #include "compat/xenoprof.c"
967 #endif
968
969 /*
970 * Local variables:
971 * mode: C
972 * c-file-style: "BSD"
973 * c-basic-offset: 4
974 * tab-width: 4
975 * indent-tabs-mode: nil
976 * End:
977 */
978