diff options
-rw-r--r-- | include/linux/perf_event.h | 6 | ||||
-rw-r--r-- | kernel/perf_event.c | 75 |
2 files changed, 80 insertions, 1 deletions
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index df9d964c15fc..fa151d49a2ee 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
@@ -744,6 +744,12 @@ extern int hw_perf_group_sched_in(struct perf_event *group_leader, | |||
744 | struct perf_cpu_context *cpuctx, | 744 | struct perf_cpu_context *cpuctx, |
745 | struct perf_event_context *ctx, int cpu); | 745 | struct perf_event_context *ctx, int cpu); |
746 | extern void perf_event_update_userpage(struct perf_event *event); | 746 | extern void perf_event_update_userpage(struct perf_event *event); |
747 | extern int perf_event_release_kernel(struct perf_event *event); | ||
748 | extern struct perf_event * | ||
749 | perf_event_create_kernel_counter(struct perf_event_attr *attr, | ||
750 | int cpu, | ||
751 | pid_t pid); | ||
752 | extern u64 perf_event_read_value(struct perf_event *event); | ||
747 | 753 | ||
748 | struct perf_sample_data { | 754 | struct perf_sample_data { |
749 | u64 type; | 755 | u64 type; |
diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 12b5ec39bf97..02d4ff041b01 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c | |||
@@ -1725,6 +1725,26 @@ static int perf_release(struct inode *inode, struct file *file) | |||
1725 | return 0; | 1725 | return 0; |
1726 | } | 1726 | } |
1727 | 1727 | ||
1728 | int perf_event_release_kernel(struct perf_event *event) | ||
1729 | { | ||
1730 | struct perf_event_context *ctx = event->ctx; | ||
1731 | |||
1732 | WARN_ON_ONCE(ctx->parent_ctx); | ||
1733 | mutex_lock(&ctx->mutex); | ||
1734 | perf_event_remove_from_context(event); | ||
1735 | mutex_unlock(&ctx->mutex); | ||
1736 | |||
1737 | mutex_lock(&event->owner->perf_event_mutex); | ||
1738 | list_del_init(&event->owner_entry); | ||
1739 | mutex_unlock(&event->owner->perf_event_mutex); | ||
1740 | put_task_struct(event->owner); | ||
1741 | |||
1742 | free_event(event); | ||
1743 | |||
1744 | return 0; | ||
1745 | } | ||
1746 | EXPORT_SYMBOL_GPL(perf_event_release_kernel); | ||
1747 | |||
1728 | static int perf_event_read_size(struct perf_event *event) | 1748 | static int perf_event_read_size(struct perf_event *event) |
1729 | { | 1749 | { |
1730 | int entry = sizeof(u64); /* value */ | 1750 | int entry = sizeof(u64); /* value */ |
@@ -1750,7 +1770,7 @@ static int perf_event_read_size(struct perf_event *event) | |||
1750 | return size; | 1770 | return size; |
1751 | } | 1771 | } |
1752 | 1772 | ||
1753 | static u64 perf_event_read_value(struct perf_event *event) | 1773 | u64 perf_event_read_value(struct perf_event *event) |
1754 | { | 1774 | { |
1755 | struct perf_event *child; | 1775 | struct perf_event *child; |
1756 | u64 total = 0; | 1776 | u64 total = 0; |
@@ -1761,6 +1781,7 @@ static u64 perf_event_read_value(struct perf_event *event) | |||
1761 | 1781 | ||
1762 | return total; | 1782 | return total; |
1763 | } | 1783 | } |
1784 | EXPORT_SYMBOL_GPL(perf_event_read_value); | ||
1764 | 1785 | ||
1765 | static int perf_event_read_entry(struct perf_event *event, | 1786 | static int perf_event_read_entry(struct perf_event *event, |
1766 | u64 read_format, char __user *buf) | 1787 | u64 read_format, char __user *buf) |
@@ -4638,6 +4659,58 @@ err_put_context: | |||
4638 | return err; | 4659 | return err; |
4639 | } | 4660 | } |
4640 | 4661 | ||
4662 | /** | ||
4663 | * perf_event_create_kernel_counter | ||
4664 | * | ||
4665 | * @attr: attributes of the counter to create | ||
4666 | * @cpu: cpu in which the counter is bound | ||
4667 | * @pid: task to profile | ||
4668 | */ | ||
4669 | struct perf_event * | ||
4670 | perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, | ||
4671 | pid_t pid) | ||
4672 | { | ||
4673 | struct perf_event *event; | ||
4674 | struct perf_event_context *ctx; | ||
4675 | int err; | ||
4676 | |||
4677 | /* | ||
4678 | * Get the target context (task or percpu): | ||
4679 | */ | ||
4680 | |||
4681 | ctx = find_get_context(pid, cpu); | ||
4682 | if (IS_ERR(ctx)) | ||
4683 | return NULL ; | ||
4684 | |||
4685 | event = perf_event_alloc(attr, cpu, ctx, NULL, | ||
4686 | NULL, GFP_KERNEL); | ||
4687 | err = PTR_ERR(event); | ||
4688 | if (IS_ERR(event)) | ||
4689 | goto err_put_context; | ||
4690 | |||
4691 | event->filp = NULL; | ||
4692 | WARN_ON_ONCE(ctx->parent_ctx); | ||
4693 | mutex_lock(&ctx->mutex); | ||
4694 | perf_install_in_context(ctx, event, cpu); | ||
4695 | ++ctx->generation; | ||
4696 | mutex_unlock(&ctx->mutex); | ||
4697 | |||
4698 | event->owner = current; | ||
4699 | get_task_struct(current); | ||
4700 | mutex_lock(¤t->perf_event_mutex); | ||
4701 | list_add_tail(&event->owner_entry, ¤t->perf_event_list); | ||
4702 | mutex_unlock(¤t->perf_event_mutex); | ||
4703 | |||
4704 | return event; | ||
4705 | |||
4706 | err_put_context: | ||
4707 | if (err < 0) | ||
4708 | put_ctx(ctx); | ||
4709 | |||
4710 | return NULL; | ||
4711 | } | ||
4712 | EXPORT_SYMBOL_GPL(perf_event_create_kernel_counter); | ||
4713 | |||
4641 | /* | 4714 | /* |
4642 | * inherit a event from parent task to child task: | 4715 | * inherit a event from parent task to child task: |
4643 | */ | 4716 | */ |