perf_event: fix pmu deadlock issue [1/1]

PD#SWPL-3088

Problem:
smp_call_function_single() may cause deadlock.

Solution:
modify

Verify:
u200 w400

Change-Id: I86e9f67ed292245c5fe649e6750a6a406261552f
Signed-off-by: Hanjie Lin <hanjie.lin@amlogic.com>
This commit is contained in:
Hanjie Lin
2019-07-26 20:35:53 +08:00
committed by Luke Go
parent 44de0a9996
commit f05e89c411
6 changed files with 94 additions and 155 deletions

View File

@@ -242,15 +242,17 @@
};
arm_pmu {
compatible = "arm,cortex-a15-pmu";
/* clusterb-enabled; */
interrupts = <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>;
reg = <0xc8834680 0x4>;
cpumasks = <0xf>;
compatible = "arm,cortex-a7-pmu";
clusterb-enabled;
interrupts = <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>;
reg = <0xc8834680 0x4>,
<0xc8834740 0x4>;
cpumasks = <0xf 0xf0>;
/* default 10ms */
relax-timer-ns = <10000000>;
/* default 10000us */
max-wait-cnt = <10000>;
/* default 100000us */
max-wait-cnt = <100000>;
};
gic: interrupt-controller@2c001000 {

View File

@@ -951,13 +951,6 @@ static void armv7pmu_disable_event(struct perf_event *event)
#ifdef CONFIG_AMLOGIC_MODIFY
#include <linux/perf/arm_pmu.h>
static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev);
void amlpmu_handle_irq_ipi(void *arg)
{
armv7pmu_handle_irq(-1, amlpmu_ctx.pmu);
}
#endif
static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev)
@@ -976,9 +969,11 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev)
#ifdef CONFIG_AMLOGIC_MODIFY
/* amlpmu have routed the interrupt successfully, return IRQ_HANDLED */
if (amlpmu_handle_irq(cpu_pmu,
irq_num,
armv7_pmnc_has_overflowed(pmnc)))
amlpmu_handle_irq(cpu_pmu,
irq_num,
armv7_pmnc_has_overflowed(pmnc));
if (!armv7_pmnc_has_overflowed(pmnc))
return IRQ_HANDLED;
#else
/*

View File

@@ -228,6 +228,7 @@
<GIC_PPI 11 0xff08>,
<GIC_PPI 10 0xff08>;
};
timer_bc {
compatible = "arm, meson-bc-timer";
reg= <0x0 0xc1109990 0x0 0x4 0x0 0xc1109994 0x0 0x4>;
@@ -243,14 +244,16 @@
arm_pmu {
compatible = "arm,armv8-pmuv3";
/* clusterb-enabled; */
interrupts = <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>;
reg = <0x0 0xc8834680 0x0 0x4>;
cpumasks = <0xf>;
clusterb-enabled;
interrupts = <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>;
reg = <0x0 0xc8834680 0x0 0x4>,
<0x0 0xc8834740 0x0 0x4>;
cpumasks = <0xf 0xf0>;
/* default 10ms */
relax-timer-ns = <10000000>;
/* default 10000us */
max-wait-cnt = <10000>;
/* default 100000us */
max-wait-cnt = <100000>;
};
gic: interrupt-controller@2c001000 {

View File

@@ -751,13 +751,6 @@ static void armv8pmu_disable_event(struct perf_event *event)
#ifdef CONFIG_AMLOGIC_MODIFY
#include <linux/perf/arm_pmu.h>
static irqreturn_t armv8pmu_handle_irq(int irq_num, void *dev);
void amlpmu_handle_irq_ipi(void *arg)
{
armv8pmu_handle_irq(-1, amlpmu_ctx.pmu);
}
#endif
static irqreturn_t armv8pmu_handle_irq(int irq_num, void *dev)
@@ -776,9 +769,11 @@ static irqreturn_t armv8pmu_handle_irq(int irq_num, void *dev)
#ifdef CONFIG_AMLOGIC_MODIFY
/* amlpmu have routed the interrupt already, so return IRQ_HANDLED */
if (amlpmu_handle_irq(cpu_pmu,
irq_num,
armv8pmu_has_overflowed(pmovsr)))
amlpmu_handle_irq(cpu_pmu,
irq_num,
armv8pmu_has_overflowed(pmovsr));
if (!armv8pmu_has_overflowed(pmovsr))
return IRQ_HANDLED;
#else
/*

View File

@@ -1124,21 +1124,13 @@ static void amlpmu_fix_setup_affinity(int irq)
* on pmu interrupt generated cpu, @irq_num is valid
* on other cpus(called by AML_PMU_IPI), @irq_num is -1
*/
static int amlpmu_irq_fix(int irq_num)
static void amlpmu_irq_fix(int irq_num)
{
int i;
int cpu;
int cur_cpu;
int pmuirq_val;
int cluster_index = 0;
int fix_success = 0;
struct amlpmu_cpuinfo *ci;
struct amlpmu_context *ctx = &amlpmu_ctx;
struct call_single_data csd_stack;
int max_wait_cnt = ctx->max_wait_cnt;
csd_stack.func = amlpmu_handle_irq_ipi;
csd_stack.info = NULL;
cur_cpu = smp_processor_id();
@@ -1148,7 +1140,7 @@ static int amlpmu_irq_fix(int irq_num)
cluster_index = 1;
else {
pr_err("amlpmu_irq_fix() bad irq = %d\n", irq_num);
return fix_success;
return;
}
if (!cpumask_test_cpu(cur_cpu, &ctx->cpumasks[cluster_index])) {
@@ -1165,51 +1157,68 @@ static int amlpmu_irq_fix(int irq_num)
readl(ctx->regs[cluster_index]),
ctx->first_cpus[cluster_index],
*cpumask_bits(&ctx->cpumasks[cluster_index]));
/*
* if pmuirq_val is zero means we can't get irq cpu info
* from the register(eg: gxm clusterb), so we have to select another
* cpu(next cpu) in cluster to try to handle this irq.
*/
if (!pmuirq_val) {
int next_cpu = -1;
for_each_cpu_and(cpu,
&ctx->cpumasks[cluster_index],
cpu_possible_mask) {
if (pmuirq_val & (1<<cpu)) {
if (cpu == cur_cpu) {
pr_info("ownercpu %d in pmuirq = 0x%x\n",
cur_cpu, pmuirq_val);
continue;
for_each_cpu_and(cpu,
&ctx->cpumasks[cluster_index],
cpu_online_mask) {
if (cpu > cur_cpu) {
next_cpu = cpu;
break;
}
pr_debug("fix pmu irq cpu %d, pmuirq = 0x%x\n",
cpu,
pmuirq_val);
ci = per_cpu_ptr(ctx->cpuinfo, cpu);
WRITE_ONCE(ci->fix_done, 0);
WRITE_ONCE(ci->fix_overflowed, 0);
csd_stack.flags = 0;
smp_call_function_single_async(cpu, &csd_stack);
for (i = 0; i < max_wait_cnt; i++) {
if (READ_ONCE(ci->fix_done))
break;
udelay(1);
}
if (i == ctx->max_wait_cnt) {
pr_err("wait for cpu %d done timeout\n",
cpu);
//amlpmu_relax_timer_start(cpu);
}
if (READ_ONCE(ci->fix_overflowed))
fix_success++;
}
if (next_cpu == -1) {
for_each_cpu_and(cpu,
&ctx->cpumasks[cluster_index],
cpu_online_mask) {
if (cpu < cur_cpu) {
next_cpu = cpu;
break;
}
}
}
if (next_cpu != -1) {
if (irq_set_affinity(irq_num, cpumask_of(cpu)))
pr_err("irq_set_affin failed, irq=%d cpu=%d\n",
irq_num,
cpu);
} else
pr_err("can't find nextcpu\n");
return;
}
return fix_success;
/* fix irq from register info */
for_each_cpu_and(cpu,
&ctx->cpumasks[cluster_index],
cpu_online_mask) {
if (!(pmuirq_val & (1<<cpu)))
continue;
if (cpu == cur_cpu)
continue;
pr_debug("fix pmu irq cpu=%d, pmuirq=0x%x\n", cpu, pmuirq_val);
if (irq_set_affinity(irq_num, cpumask_of(cpu)))
pr_err("irq_set_affinity() failed, irq=%d cpu=%d\n",
irq_num,
cpu);
return;
}
}
static void amlpmu_update_stats(int irq_num,
int has_overflowed,
int fix_success)
int has_overflowed)
{
int freq;
int i;
@@ -1220,21 +1229,6 @@ static void amlpmu_update_stats(int irq_num,
ci = this_cpu_ptr(ctx->cpuinfo);
if (!has_overflowed && !fix_success) {
pr_debug("empty_irq_cnt: %lu\n", ci->empty_irq_cnt);
ci->empty_irq_cnt++;
ci->empty_irq_time = time;
}
if (fix_success) {
/* send IPI success */
pr_debug("fix_irq_cnt: %lu, fix_success = %d\n",
ci->fix_irq_cnt,
fix_success);
ci->fix_irq_cnt++;
ci->fix_irq_time = time;
}
if (has_overflowed) {
ci->valid_irq_cnt++;
ci->valid_irq_time = time;
@@ -1273,32 +1267,6 @@ static void amlpmu_update_stats(int irq_num,
}
if (time_after(ci->valid_irq_time, ci->last_valid_irq_time + 2*HZ)) {
freq = ci->empty_irq_cnt - ci->last_empty_irq_cnt;
freq *= HZ;
freq /= (ci->empty_irq_time - ci->last_empty_irq_time);
pr_info("######## empty_irq_cnt: %lu - %lu = %lu, freq = %d\n",
ci->empty_irq_cnt,
ci->last_empty_irq_cnt,
ci->empty_irq_cnt - ci->last_empty_irq_cnt,
freq);
ci->last_empty_irq_cnt = ci->empty_irq_cnt;
ci->last_empty_irq_time = ci->empty_irq_time;
freq = ci->fix_irq_cnt - ci->last_fix_irq_cnt;
freq *= HZ;
freq /= (ci->fix_irq_time - ci->last_fix_irq_time);
pr_info("######## fix_irq_cnt: %lu - %lu = %lu, freq = %d\n",
ci->fix_irq_cnt,
ci->last_fix_irq_cnt,
ci->fix_irq_cnt - ci->last_fix_irq_cnt,
freq);
ci->last_fix_irq_cnt = ci->fix_irq_cnt;
ci->last_fix_irq_time = ci->fix_irq_time;
freq = ci->valid_irq_cnt - ci->last_valid_irq_cnt;
freq *= HZ;
freq /= (ci->valid_irq_time - ci->last_valid_irq_time);
@@ -1313,10 +1281,9 @@ static void amlpmu_update_stats(int irq_num,
}
}
int amlpmu_handle_irq(struct arm_pmu *cpu_pmu, int irq_num, int has_overflowed)
void amlpmu_handle_irq(struct arm_pmu *cpu_pmu, int irq_num, int has_overflowed)
{
int cpu;
int fix_success = 0;
struct amlpmu_cpuinfo *ci;
struct amlpmu_context *ctx = &amlpmu_ctx;
@@ -1331,43 +1298,20 @@ int amlpmu_handle_irq(struct arm_pmu *cpu_pmu, int irq_num, int has_overflowed)
* if current cpu is not overflowed, it's possible some other
* cpus caused the pmu interrupt.
* so if current cpu is interrupt generated cpu(irq_num != -1),
* call aml_pmu_fix() try to send IPI to other cpus and waiting
* for fix_done.
* call aml_pmu_fix() try to fix it.
*/
if (!has_overflowed && irq_num != -1)
fix_success = amlpmu_irq_fix(irq_num);
if (!has_overflowed)
amlpmu_irq_fix(irq_num);
/*
* valid_irq, fix_irq and empty_irq status
* valid_irq status
* avg_delta time account to predict next interrupt time
*/
amlpmu_update_stats(irq_num, has_overflowed, fix_success);
amlpmu_update_stats(irq_num, has_overflowed);
/*
* armv*pmu_getreset_flags() will clear interrupt. If current
* interrupt is IPI fix(irq_num = -1), interrupt generated cpu
* now is waiting for ci->fix_done=1(clear interrupt).
* we must set ci->fix_done to 1 after amlpmu_stat_account(),
* because interrupt generated cpu need this predict time info
* to setup interrupt affinity.
*/
if (irq_num == -1) {
WRITE_ONCE(ci->fix_overflowed, has_overflowed);
/* fix_overflowed must before fix_done */
mb();
WRITE_ONCE(ci->fix_done, 1);
}
/* only interrupt generated cpu need setup affinity */
if (irq_num != -1)
if (has_overflowed)
amlpmu_fix_setup_affinity(irq_num);
/*
* when a pmu interrupt generated, if current cpu is not
* overflowed and some other cpus succeed in handling the
* interrupt by IPIs return true.
*/
return !has_overflowed && fix_success;
}
static int amlpmu_init(struct platform_device *pdev, struct arm_pmu *pmu)

View File

@@ -249,10 +249,10 @@ struct amlpmu_context {
extern struct amlpmu_context amlpmu_ctx;
int amlpmu_handle_irq(struct arm_pmu *cpu_pmu, int irq_num, int has_overflowed);
void amlpmu_handle_irq(struct arm_pmu *cpu_pmu,
int irq_num,
int has_overflowed);
/* defined int arch/arm(64)/kernel/perf_event(_v7).c */
void amlpmu_handle_irq_ipi(void *arg);
#endif
#endif /* CONFIG_ARM_PMU */