Skip to content

Commit 35a4332

Browse files
Bharat Bhushanwilldeacon
authored andcommitted
perf/marvell: cn10k DDR perfmon event overflow handling
CN10k DSS h/w perfmon does not support event overflow interrupt, so periodic timer is being used. Each event counter is 48bit, which in worst case scenario can increment at maximum 5.6 GT/s. At this rate it may take many hours to overflow these counters. Therefore polling period for overflow is set to 100 sec, which can be changed using sysfs parameter. Two fixed event counters starts counting from zero on overflow, so overflow condition is when new count less than previous count. While eight programmable event counters freezes at maximum value. Also individual counter cannot be restarted, so need to restart all eight counters. Signed-off-by: Bharat Bhushan <bbhushan2@marvell.com> Reviewed-by: Bhaskara Budiredla <bbudiredla@marvell.com> Link: https://lore.kernel.org/r/20220211045346.17894-4-bbhushan2@marvell.com Signed-off-by: Will Deacon <will@kernel.org>
1 parent 7cf83e2 commit 35a4332

1 file changed

Lines changed: 111 additions & 0 deletions

File tree

drivers/perf/marvell_cn10k_ddr_pmu.c

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/of_address.h>
1212
#include <linux/of_device.h>
1313
#include <linux/perf_event.h>
14+
#include <linux/hrtimer.h>
1415

1516
/* Performance Counters Operating Mode Control Registers */
1617
#define DDRC_PERF_CNT_OP_MODE_CTRL 0x8020
@@ -127,6 +128,7 @@ struct cn10k_ddr_pmu {
127128
struct device *dev;
128129
int active_events;
129130
struct perf_event *events[DDRC_PERF_NUM_COUNTERS];
131+
struct hrtimer hrtimer;
130132
};
131133

132134
#define to_cn10k_ddr_pmu(p) container_of(p, struct cn10k_ddr_pmu, pmu)
@@ -251,6 +253,18 @@ static const struct attribute_group *cn10k_attr_groups[] = {
251253
NULL,
252254
};
253255

256+
/* Default poll timeout is 100 sec, which is very sufficient for
257+
* 48 bit counter incremented max at 5.6 GT/s, which may take many
258+
* hours to overflow.
259+
*/
260+
static unsigned long cn10k_ddr_pmu_poll_period_sec = 100;
261+
module_param_named(poll_period_sec, cn10k_ddr_pmu_poll_period_sec, ulong, 0644);
262+
263+
static ktime_t cn10k_ddr_pmu_timer_period(void)
264+
{
265+
return ms_to_ktime((u64)cn10k_ddr_pmu_poll_period_sec * USEC_PER_SEC);
266+
}
267+
254268
static int ddr_perf_get_event_bitmap(int eventid, u64 *event_bitmap)
255269
{
256270
switch (eventid) {
@@ -433,6 +447,10 @@ static int cn10k_ddr_perf_event_add(struct perf_event *event, int flags)
433447
pmu->active_events++;
434448
hwc->idx = counter;
435449

450+
if (pmu->active_events == 1)
451+
hrtimer_start(&pmu->hrtimer, cn10k_ddr_pmu_timer_period(),
452+
HRTIMER_MODE_REL_PINNED);
453+
436454
if (counter < DDRC_PERF_NUM_GEN_COUNTERS) {
437455
/* Generic counters, configure event id */
438456
reg_offset = DDRC_PERF_CFG(counter);
@@ -484,6 +502,10 @@ static void cn10k_ddr_perf_event_del(struct perf_event *event, int flags)
484502
cn10k_ddr_perf_free_counter(pmu, counter);
485503
pmu->active_events--;
486504
hwc->idx = -1;
505+
506+
/* Cancel timer when no events to capture */
507+
if (pmu->active_events == 0)
508+
hrtimer_cancel(&pmu->hrtimer);
487509
}
488510

489511
static void cn10k_ddr_perf_pmu_enable(struct pmu *pmu)
@@ -502,6 +524,92 @@ static void cn10k_ddr_perf_pmu_disable(struct pmu *pmu)
502524
DDRC_PERF_CNT_END_OP_CTRL);
503525
}
504526

527+
static void cn10k_ddr_perf_event_update_all(struct cn10k_ddr_pmu *pmu)
528+
{
529+
struct hw_perf_event *hwc;
530+
int i;
531+
532+
for (i = 0; i < DDRC_PERF_NUM_GEN_COUNTERS; i++) {
533+
if (pmu->events[i] == NULL)
534+
continue;
535+
536+
cn10k_ddr_perf_event_update(pmu->events[i]);
537+
}
538+
539+
/* Reset previous count as h/w counter are reset */
540+
for (i = 0; i < DDRC_PERF_NUM_GEN_COUNTERS; i++) {
541+
if (pmu->events[i] == NULL)
542+
continue;
543+
544+
hwc = &pmu->events[i]->hw;
545+
local64_set(&hwc->prev_count, 0);
546+
}
547+
}
548+
549+
static irqreturn_t cn10k_ddr_pmu_overflow_handler(struct cn10k_ddr_pmu *pmu)
550+
{
551+
struct perf_event *event;
552+
struct hw_perf_event *hwc;
553+
u64 prev_count, new_count;
554+
u64 value;
555+
int i;
556+
557+
event = pmu->events[DDRC_PERF_READ_COUNTER_IDX];
558+
if (event) {
559+
hwc = &event->hw;
560+
prev_count = local64_read(&hwc->prev_count);
561+
new_count = cn10k_ddr_perf_read_counter(pmu, hwc->idx);
562+
563+
/* Overflow condition is when new count less than
564+
* previous count
565+
*/
566+
if (new_count < prev_count)
567+
cn10k_ddr_perf_event_update(event);
568+
}
569+
570+
event = pmu->events[DDRC_PERF_WRITE_COUNTER_IDX];
571+
if (event) {
572+
hwc = &event->hw;
573+
prev_count = local64_read(&hwc->prev_count);
574+
new_count = cn10k_ddr_perf_read_counter(pmu, hwc->idx);
575+
576+
/* Overflow condition is when new count less than
577+
* previous count
578+
*/
579+
if (new_count < prev_count)
580+
cn10k_ddr_perf_event_update(event);
581+
}
582+
583+
for (i = 0; i < DDRC_PERF_NUM_GEN_COUNTERS; i++) {
584+
if (pmu->events[i] == NULL)
585+
continue;
586+
587+
value = cn10k_ddr_perf_read_counter(pmu, i);
588+
if (value == DDRC_PERF_CNT_MAX_VALUE) {
589+
pr_info("Counter-(%d) reached max value\n", i);
590+
cn10k_ddr_perf_event_update_all(pmu);
591+
cn10k_ddr_perf_pmu_disable(&pmu->pmu);
592+
cn10k_ddr_perf_pmu_enable(&pmu->pmu);
593+
}
594+
}
595+
596+
return IRQ_HANDLED;
597+
}
598+
599+
static enum hrtimer_restart cn10k_ddr_pmu_timer_handler(struct hrtimer *hrtimer)
600+
{
601+
struct cn10k_ddr_pmu *pmu = container_of(hrtimer, struct cn10k_ddr_pmu,
602+
hrtimer);
603+
unsigned long flags;
604+
605+
local_irq_save(flags);
606+
cn10k_ddr_pmu_overflow_handler(pmu);
607+
local_irq_restore(flags);
608+
609+
hrtimer_forward_now(hrtimer, cn10k_ddr_pmu_timer_period());
610+
return HRTIMER_RESTART;
611+
}
612+
505613
static int cn10k_ddr_perf_probe(struct platform_device *pdev)
506614
{
507615
struct cn10k_ddr_pmu *ddr_pmu;
@@ -550,6 +658,9 @@ static int cn10k_ddr_perf_probe(struct platform_device *pdev)
550658
if (!name)
551659
return -ENOMEM;
552660

661+
hrtimer_init(&ddr_pmu->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
662+
ddr_pmu->hrtimer.function = cn10k_ddr_pmu_timer_handler;
663+
553664
ret = perf_pmu_register(&ddr_pmu->pmu, name, -1);
554665
if (ret)
555666
return ret;

0 commit comments

Comments
 (0)