diff options
-rw-r--r-- | include/linux/perf_event.h | 6 | ||||
-rw-r--r-- | kernel/perf_event.c | 79 | ||||
-rw-r--r-- | kernel/sched.c | 2 |
3 files changed, 55 insertions, 32 deletions
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 165287fd2cc4..61b1e2d760fd 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
@@ -870,8 +870,8 @@ struct perf_cpu_context { | |||
870 | struct perf_event_context *task_ctx; | 870 | struct perf_event_context *task_ctx; |
871 | int active_oncpu; | 871 | int active_oncpu; |
872 | int exclusive; | 872 | int exclusive; |
873 | u64 timer_interval; | 873 | struct list_head rotation_list; |
874 | struct hrtimer timer; | 874 | int jiffies_interval; |
875 | }; | 875 | }; |
876 | 876 | ||
877 | struct perf_output_handle { | 877 | struct perf_output_handle { |
@@ -1065,6 +1065,7 @@ extern int perf_swevent_get_recursion_context(void); | |||
1065 | extern void perf_swevent_put_recursion_context(int rctx); | 1065 | extern void perf_swevent_put_recursion_context(int rctx); |
1066 | extern void perf_event_enable(struct perf_event *event); | 1066 | extern void perf_event_enable(struct perf_event *event); |
1067 | extern void perf_event_disable(struct perf_event *event); | 1067 | extern void perf_event_disable(struct perf_event *event); |
1068 | extern void perf_event_task_tick(void); | ||
1068 | #else | 1069 | #else |
1069 | static inline void | 1070 | static inline void |
1070 | perf_event_task_sched_in(struct task_struct *task) { } | 1071 | perf_event_task_sched_in(struct task_struct *task) { } |
@@ -1099,6 +1100,7 @@ static inline int perf_swevent_get_recursion_context(void) { return -1; } | |||
1099 | static inline void perf_swevent_put_recursion_context(int rctx) { } | 1100 | static inline void perf_swevent_put_recursion_context(int rctx) { } |
1100 | static inline void perf_event_enable(struct perf_event *event) { } | 1101 | static inline void perf_event_enable(struct perf_event *event) { } |
1101 | static inline void perf_event_disable(struct perf_event *event) { } | 1102 | static inline void perf_event_disable(struct perf_event *event) { } |
1103 | static inline void perf_event_task_tick(void) { } | ||
1102 | #endif | 1104 | #endif |
1103 | 1105 | ||
1104 | #define perf_output_put(handle, x) \ | 1106 | #define perf_output_put(handle, x) \ |
diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 27332e5f51a7..baae1367e945 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c | |||
@@ -77,23 +77,22 @@ void perf_pmu_enable(struct pmu *pmu) | |||
77 | pmu->pmu_enable(pmu); | 77 | pmu->pmu_enable(pmu); |
78 | } | 78 | } |
79 | 79 | ||
80 | static DEFINE_PER_CPU(struct list_head, rotation_list); | ||
81 | |||
82 | /* | ||
83 | * perf_pmu_rotate_start() and perf_rotate_context() are fully serialized | ||
84 | * because they're strictly cpu affine and rotate_start is called with IRQs | ||
85 | * disabled, while rotate_context is called from IRQ context. | ||
86 | */ | ||
80 | static void perf_pmu_rotate_start(struct pmu *pmu) | 87 | static void perf_pmu_rotate_start(struct pmu *pmu) |
81 | { | 88 | { |
82 | struct perf_cpu_context *cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); | 89 | struct perf_cpu_context *cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); |
90 | struct list_head *head = &__get_cpu_var(rotation_list); | ||
83 | 91 | ||
84 | if (hrtimer_active(&cpuctx->timer)) | 92 | WARN_ON(!irqs_disabled()); |
85 | return; | ||
86 | 93 | ||
87 | __hrtimer_start_range_ns(&cpuctx->timer, | 94 | if (list_empty(&cpuctx->rotation_list)) |
88 | ns_to_ktime(cpuctx->timer_interval), 0, | 95 | list_add(&cpuctx->rotation_list, head); |
89 | HRTIMER_MODE_REL_PINNED, 0); | ||
90 | } | ||
91 | |||
92 | static void perf_pmu_rotate_stop(struct pmu *pmu) | ||
93 | { | ||
94 | struct perf_cpu_context *cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); | ||
95 | |||
96 | hrtimer_cancel(&cpuctx->timer); | ||
97 | } | 96 | } |
98 | 97 | ||
99 | static void get_ctx(struct perf_event_context *ctx) | 98 | static void get_ctx(struct perf_event_context *ctx) |
@@ -1607,36 +1606,33 @@ static void rotate_ctx(struct perf_event_context *ctx) | |||
1607 | } | 1606 | } |
1608 | 1607 | ||
1609 | /* | 1608 | /* |
1610 | * Cannot race with ->pmu_rotate_start() because this is ran from hardirq | 1609 | * perf_pmu_rotate_start() and perf_rotate_context() are fully serialized |
1611 | * context, and ->pmu_rotate_start() is called with irqs disabled (both are | 1610 | * because they're strictly cpu affine and rotate_start is called with IRQs |
1612 | * cpu affine, so there are no SMP races). | 1611 | * disabled, while rotate_context is called from IRQ context. |
1613 | */ | 1612 | */ |
1614 | static enum hrtimer_restart perf_event_context_tick(struct hrtimer *timer) | 1613 | static void perf_rotate_context(struct perf_cpu_context *cpuctx) |
1615 | { | 1614 | { |
1616 | enum hrtimer_restart restart = HRTIMER_NORESTART; | 1615 | u64 interval = (u64)cpuctx->jiffies_interval * TICK_NSEC; |
1617 | struct perf_cpu_context *cpuctx; | ||
1618 | struct perf_event_context *ctx = NULL; | 1616 | struct perf_event_context *ctx = NULL; |
1619 | int rotate = 0; | 1617 | int rotate = 0, remove = 1; |
1620 | |||
1621 | cpuctx = container_of(timer, struct perf_cpu_context, timer); | ||
1622 | 1618 | ||
1623 | if (cpuctx->ctx.nr_events) { | 1619 | if (cpuctx->ctx.nr_events) { |
1624 | restart = HRTIMER_RESTART; | 1620 | remove = 0; |
1625 | if (cpuctx->ctx.nr_events != cpuctx->ctx.nr_active) | 1621 | if (cpuctx->ctx.nr_events != cpuctx->ctx.nr_active) |
1626 | rotate = 1; | 1622 | rotate = 1; |
1627 | } | 1623 | } |
1628 | 1624 | ||
1629 | ctx = cpuctx->task_ctx; | 1625 | ctx = cpuctx->task_ctx; |
1630 | if (ctx && ctx->nr_events) { | 1626 | if (ctx && ctx->nr_events) { |
1631 | restart = HRTIMER_RESTART; | 1627 | remove = 0; |
1632 | if (ctx->nr_events != ctx->nr_active) | 1628 | if (ctx->nr_events != ctx->nr_active) |
1633 | rotate = 1; | 1629 | rotate = 1; |
1634 | } | 1630 | } |
1635 | 1631 | ||
1636 | perf_pmu_disable(cpuctx->ctx.pmu); | 1632 | perf_pmu_disable(cpuctx->ctx.pmu); |
1637 | perf_ctx_adjust_freq(&cpuctx->ctx, cpuctx->timer_interval); | 1633 | perf_ctx_adjust_freq(&cpuctx->ctx, interval); |
1638 | if (ctx) | 1634 | if (ctx) |
1639 | perf_ctx_adjust_freq(ctx, cpuctx->timer_interval); | 1635 | perf_ctx_adjust_freq(ctx, interval); |
1640 | 1636 | ||
1641 | if (!rotate) | 1637 | if (!rotate) |
1642 | goto done; | 1638 | goto done; |
@@ -1654,10 +1650,24 @@ static enum hrtimer_restart perf_event_context_tick(struct hrtimer *timer) | |||
1654 | task_ctx_sched_in(ctx, EVENT_FLEXIBLE); | 1650 | task_ctx_sched_in(ctx, EVENT_FLEXIBLE); |
1655 | 1651 | ||
1656 | done: | 1652 | done: |
1653 | if (remove) | ||
1654 | list_del_init(&cpuctx->rotation_list); | ||
1655 | |||
1657 | perf_pmu_enable(cpuctx->ctx.pmu); | 1656 | perf_pmu_enable(cpuctx->ctx.pmu); |
1658 | hrtimer_forward_now(timer, ns_to_ktime(cpuctx->timer_interval)); | 1657 | } |
1658 | |||
1659 | void perf_event_task_tick(void) | ||
1660 | { | ||
1661 | struct list_head *head = &__get_cpu_var(rotation_list); | ||
1662 | struct perf_cpu_context *cpuctx, *tmp; | ||
1659 | 1663 | ||
1660 | return restart; | 1664 | WARN_ON(!irqs_disabled()); |
1665 | |||
1666 | list_for_each_entry_safe(cpuctx, tmp, head, rotation_list) { | ||
1667 | if (cpuctx->jiffies_interval == 1 || | ||
1668 | !(jiffies % cpuctx->jiffies_interval)) | ||
1669 | perf_rotate_context(cpuctx); | ||
1670 | } | ||
1661 | } | 1671 | } |
1662 | 1672 | ||
1663 | static int event_enable_on_exec(struct perf_event *event, | 1673 | static int event_enable_on_exec(struct perf_event *event, |
@@ -5186,9 +5196,8 @@ int perf_pmu_register(struct pmu *pmu) | |||
5186 | __perf_event_init_context(&cpuctx->ctx); | 5196 | __perf_event_init_context(&cpuctx->ctx); |
5187 | cpuctx->ctx.type = cpu_context; | 5197 | cpuctx->ctx.type = cpu_context; |
5188 | cpuctx->ctx.pmu = pmu; | 5198 | cpuctx->ctx.pmu = pmu; |
5189 | cpuctx->timer_interval = TICK_NSEC; | 5199 | cpuctx->jiffies_interval = 1; |
5190 | hrtimer_init(&cpuctx->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | 5200 | INIT_LIST_HEAD(&cpuctx->rotation_list); |
5191 | cpuctx->timer.function = perf_event_context_tick; | ||
5192 | } | 5201 | } |
5193 | 5202 | ||
5194 | got_cpu_context: | 5203 | got_cpu_context: |
@@ -6229,6 +6238,7 @@ static void __init perf_event_init_all_cpus(void) | |||
6229 | for_each_possible_cpu(cpu) { | 6238 | for_each_possible_cpu(cpu) { |
6230 | swhash = &per_cpu(swevent_htable, cpu); | 6239 | swhash = &per_cpu(swevent_htable, cpu); |
6231 | mutex_init(&swhash->hlist_mutex); | 6240 | mutex_init(&swhash->hlist_mutex); |
6241 | INIT_LIST_HEAD(&per_cpu(rotation_list, cpu)); | ||
6232 | } | 6242 | } |
6233 | } | 6243 | } |
6234 | 6244 | ||
@@ -6248,6 +6258,15 @@ static void __cpuinit perf_event_init_cpu(int cpu) | |||
6248 | } | 6258 | } |
6249 | 6259 | ||
6250 | #ifdef CONFIG_HOTPLUG_CPU | 6260 | #ifdef CONFIG_HOTPLUG_CPU |
6261 | static void perf_pmu_rotate_stop(struct pmu *pmu) | ||
6262 | { | ||
6263 | struct perf_cpu_context *cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); | ||
6264 | |||
6265 | WARN_ON(!irqs_disabled()); | ||
6266 | |||
6267 | list_del_init(&cpuctx->rotation_list); | ||
6268 | } | ||
6269 | |||
6251 | static void __perf_event_exit_context(void *__info) | 6270 | static void __perf_event_exit_context(void *__info) |
6252 | { | 6271 | { |
6253 | struct perf_event_context *ctx = __info; | 6272 | struct perf_event_context *ctx = __info; |
diff --git a/kernel/sched.c b/kernel/sched.c index 1c3ea7a55b7b..794819eab9ca 100644 --- a/kernel/sched.c +++ b/kernel/sched.c | |||
@@ -3584,6 +3584,8 @@ void scheduler_tick(void) | |||
3584 | curr->sched_class->task_tick(rq, curr, 0); | 3584 | curr->sched_class->task_tick(rq, curr, 0); |
3585 | raw_spin_unlock(&rq->lock); | 3585 | raw_spin_unlock(&rq->lock); |
3586 | 3586 | ||
3587 | perf_event_task_tick(); | ||
3588 | |||
3587 | #ifdef CONFIG_SMP | 3589 | #ifdef CONFIG_SMP |
3588 | rq->idle_at_tick = idle_cpu(cpu); | 3590 | rq->idle_at_tick = idle_cpu(cpu); |
3589 | trigger_load_balance(rq, cpu); | 3591 | trigger_load_balance(rq, cpu); |