[PATCH] ACPI: RISC-V: CPPC: Implement cpc_read_ffh_fb_ctrs()
"Yufan Dou" <[email protected]>
| Newsgroups | org.kernel.vger.linux-acpi,org.infradead.lists.linux-riscv,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
On RISC-V, each cpc_read_ffh() for a CSR-type FFH register sends a separate IPI to the target hart via smp_call_function_single(). cppc_get_perf_ctrs() therefore samples the delivered and reference counters in two separate IPIs, at two different instants. The skew between the two sampling points distorts the delivered/reference ratio and thus the frequency reported by cpufreq. Implement the cpc_read_ffh_fb_ctrs() hook to read both CSR counters back-to-back in a single IPI, which narrows the sampling window to a few instructions and halves the number of cross-CPU calls. This improves the accuracy of the reported frequency in the same way as the arm64 implementation of the hook, which reads both AMU counters in a single counters_read_on_cpu() call. SBI-type FFH registers still fall back to individual reads, since pairing them would not reduce the number of SBI calls on the target hart. Testing was performed while CPU1 was kept busy with: # stress-ng --cpu 1 --taskset 1 On a CPU with cpuinfo_max_freq of 3000000 kHz: Before: Maximum observed cpuinfo_cur_freq: 3201369 kHz Maximum observed error: +201369 kHz (+6.71%) After: Maximum observed cpuinfo_cur_freq: 3009646 kHz Maximum observed error: +9646 kHz (+0.32%) The maximum observed error is reduced by 95.2%. Signed-off-by: Yufan Dou <[email protected]> --- drivers/acpi/riscv/cppc.c | 47 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/acpi/riscv/cppc.c b/drivers/acpi/riscv/cppc.c index 42c1a9052470..2ca98fa69e9a 100644 --- a/drivers/acpi/riscv/cppc.c +++ b/drivers/acpi/riscv/cppc.c @@ -85,6 +85,20 @@ static void cppc_ffh_csr_write(void *write_data) data->ret.error = -EINVAL; } +struct sbi_cppc_fb_ctrs_data { + struct sbi_cppc_data first; + struct sbi_cppc_data second; +}; + +static void cppc_ffh_csr_read_fb_ctrs(void *read_data) +{ + struct sbi_cppc_fb_ctrs_data *data = read_data; + + cppc_ffh_csr_read(&data->first); + if (!data->first.ret.error) + cppc_ffh_csr_read(&data->second); +} + /* * Refer to drivers/acpi/cppc_acpi.c for the description of the functions * below. @@ -125,6 +139,39 @@ int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val) return -EINVAL; } +int cpc_read_ffh_fb_ctrs(int cpu, struct cpc_reg *reg1, u64 *val1, + struct cpc_reg *reg2, u64 *val2) +{ + struct sbi_cppc_fb_ctrs_data data; + int ret; + + if (WARN_ON_ONCE(irqs_disabled())) + return -EPERM; + + /* Only CSR counters can be paired within a single IPI. */ + if (FFH_CPPC_TYPE(reg1->address) != FFH_CPPC_CSR || + FFH_CPPC_TYPE(reg2->address) != FFH_CPPC_CSR) + return -EOPNOTSUPP; + + data.first.reg = FFH_CPPC_CSR_NUM(reg1->address); + data.second.reg = FFH_CPPC_CSR_NUM(reg2->address); + + ret = smp_call_function_single(cpu, cppc_ffh_csr_read_fb_ctrs, + &data, 1); + if (ret) + return ret; + + if (data.first.ret.error) + return data.first.ret.error; + if (data.second.ret.error) + return data.second.ret.error; + + *val1 = data.first.ret.value; + *val2 = data.second.ret.value; + + return 0; +} + int cpc_write_ffh(int cpu, struct cpc_reg *reg, u64 val) { struct sbi_cppc_data data; base-commit: 89a312991dc6e638a36adc43ccb91dbc25504c04 -- 2.34.1