diff mbox series

drivers/firmware: arch64: Add SMC call testing module

Message ID 1554821161-4623-1-git-send-email-marek.bykowski@gmail.com (mailing list archive)
State New, archived
Headers show
Series drivers/firmware: arch64: Add SMC call testing module | expand

Commit Message

Marek Bykowski April 9, 2019, 2:46 p.m. UTC
From: Marek Bykowski <marek.bykowski@gmail.com>

The module measures the round-trip latency for both PSCI_VERSION and
SMCCC_ARCH_WORKAROUND_1 conforming to SMC Calling Convention v1.0 and v1.1
respectively for each of the CPUs. Knowing these latencies is becoming
critical in quantifying the performance impact resulting in from execution
of the mitigation to CVE-2017-5715. Generally the module application can be
extended into measuring an arbitrary SMC call duration.

The sample results for a SoC based on ARM Cortex-A57 MPCore Processor
running 16 CPUs is as follows:

SMC_VARIANT(0): smc_call_v10 [smc_time]
CPU0  CPU1  CPU2  CPU3  CPU4  CPU5  CPU6  CPU7  CPU8  CPU9  CPU10 CPU11 CPU12 CPU13 CPU14 CPU15
4812  5140  5039  5199  5265  5242  4571  4715  4879  4891  4886  4910  4016  4215  4015  4051
SMC_VARIANT(1): smc_call_v11 [smc_time]
CPU0  CPU1  CPU2  CPU3  CPU4  CPU5  CPU6  CPU7  CPU8  CPU9  CPU10 CPU11 CPU12 CPU13 CPU14 CPU15
476   481   489   489   496   496   496   496   496   496   504   496   480   481   469   477

Signed-off-by: Marek Bykowski <marek.bykowski@gmail.com>
---
 drivers/firmware/Kconfig       |   9 ++
 drivers/firmware/Makefile      |   1 +
 drivers/firmware/smc_latency.c | 220 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 230 insertions(+)
 create mode 100644 drivers/firmware/smc_latency.c

Comments

Mark Rutland April 9, 2019, 4:06 p.m. UTC | #1
Hi Marek,

On Tue, Apr 09, 2019 at 04:46:01PM +0200, marek.bykowski@gmail.com wrote:
> From: Marek Bykowski <marek.bykowski@gmail.com>
> 
> The module measures the round-trip latency for both PSCI_VERSION and
> SMCCC_ARCH_WORKAROUND_1 conforming to SMC Calling Convention v1.0 and v1.1
> respectively for each of the CPUs. Knowing these latencies is becoming
> critical in quantifying the performance impact resulting in from execution
> of the mitigation to CVE-2017-5715. Generally the module application can be
> extended into measuring an arbitrary SMC call duration.

While this is easy to quantify, the overhead in practice will depend on
your specific application, how it is scheduled, etc. I'd generally hope
that people would benchmark the typical workload that they care about.

I can see that this is useful for firmware developers, and it looks like
the trusted firmware test suite already has performance tests for
exactly these two cases:

https://git.trustedfirmware.org/TF-A/tf-a-tests.git/tree/tftf/tests/performance_tests/smc_latencies.c

... given that, I'm not sure if we really need this in Linux.

The code in this patch is missing necessary checks (e.g. that SMCCC is
present and uses SMC), and as such isn't safe on all platforms. Given it
duplicates functionality we have elsewhere in the arm64 errata handling
framework, I'd rather the benchmarking lived there if we actually need
it.

I'm not going to review the code below in detail, but I note that there
are unusued structure definitions, includes that don't exist in
mainline, and vestigal function names from another module. So as-is, I
don't think this is suitable for mainline.

Thanks,
Mark.

> The sample results for a SoC based on ARM Cortex-A57 MPCore Processor
> running 16 CPUs is as follows:
> 
> SMC_VARIANT(0): smc_call_v10 [smc_time]
> CPU0  CPU1  CPU2  CPU3  CPU4  CPU5  CPU6  CPU7  CPU8  CPU9  CPU10 CPU11 CPU12 CPU13 CPU14 CPU15
> 4812  5140  5039  5199  5265  5242  4571  4715  4879  4891  4886  4910  4016  4215  4015  4051
> SMC_VARIANT(1): smc_call_v11 [smc_time]
> CPU0  CPU1  CPU2  CPU3  CPU4  CPU5  CPU6  CPU7  CPU8  CPU9  CPU10 CPU11 CPU12 CPU13 CPU14 CPU15
> 476   481   489   489   496   496   496   496   496   496   504   496   480   481   469   477
> 
> Signed-off-by: Marek Bykowski <marek.bykowski@gmail.com>
> ---
>  drivers/firmware/Kconfig       |   9 ++
>  drivers/firmware/Makefile      |   1 +
>  drivers/firmware/smc_latency.c | 220 +++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 230 insertions(+)
>  create mode 100644 drivers/firmware/smc_latency.c
> 
> diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
> index bca172d42c74..30777ac0ce1b 100644
> --- a/drivers/firmware/Kconfig
> +++ b/drivers/firmware/Kconfig
> @@ -195,6 +195,15 @@ config QCOM_SCM
>  	depends on ARM || ARM64
>  	select RESET_CONTROLLER
>  
> +config SMC_LATENCY
> +	tristate "Measure round-trip latency of the SMC calls"
> +	depends on HAVE_ARM_SMCCC
> +	default m
> +	help
> +	  Say Y or M here to enable the measurement of the round-trip latency
> +	  for both PSCI_VERSION and SMCCC_ARCH_WORKAROUND_1 conforming to SMC
> +	  Calling Convention v1.0 and v1.1 respectively for each of the CPUs.
> +
>  config QCOM_SCM_32
>  	def_bool y
>  	depends on QCOM_SCM && ARM
> diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
> index 898ac41fa8b3..382bf01470f7 100644
> --- a/drivers/firmware/Makefile
> +++ b/drivers/firmware/Makefile
> @@ -19,6 +19,7 @@ obj-$(CONFIG_FW_CFG_SYSFS)	+= qemu_fw_cfg.o
>  obj-$(CONFIG_QCOM_SCM)		+= qcom_scm.o
>  obj-$(CONFIG_QCOM_SCM_64)	+= qcom_scm-64.o
>  obj-$(CONFIG_QCOM_SCM_32)	+= qcom_scm-32.o
> +obj-$(CONFIG_SMC_LATENCY)	+= smc_latency.o
>  CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a
>  
>  obj-y				+= broadcom/
> diff --git a/drivers/firmware/smc_latency.c b/drivers/firmware/smc_latency.c
> new file mode 100644
> index 000000000000..fc3697697ed6
> --- /dev/null
> +++ b/drivers/firmware/smc_latency.c
> @@ -0,0 +1,220 @@
> +/*
> + *
> + * SMC testing module
> + *
> + * Copyright (C) 2019 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/proc_fs.h>
> +#include <linux/slab.h>
> +#include <linux/linkage.h>
> +#include <linux/axxia-oem.h>
> +#include <linux/uaccess.h>
> +#include <linux/arm-smccc.h>
> +#include <asm/smp_plat.h>
> +#include <linux/seq_file.h>
> +#include <uapi/linux/psci.h>
> +
> +#define SMC_DEBUG 0
> +#if SMC_DEBUG
> +#define smc_dbg(...) pr_info(__VA_ARGS__)
> +#define seq_printf(...)
> +#define seq_puts(...)
> +#else
> +#define smc_dbg(...)
> +#define seq_printf(arg0, ...) seq_printf(arg0, __VA_ARGS__)
> +#define seq_puts(arg0, ...) seq_puts(arg0, __VA_ARGS__)
> +#endif
> +
> +struct _pt_regs {
> +	unsigned long elr;
> +	unsigned long regs[31];
> +};
> +
> +typedef void (*smc_cb_t)(void *);
> +
> +static bool all_cpus;
> +module_param(all_cpus, bool, 0644);
> +MODULE_PARM_DESC(all_cpus, "Run smc call on executing or all cpus (default: all");
> +
> +void smc_call_v10(void *arg);
> +void smc_call_v11(void *arg);
> +
> +smc_cb_t smc_call[] = {
> +	smc_call_v10,
> +	smc_call_v11
> +};
> +
> +#define SMC_VARIANTS ARRAY_SIZE(smc_call)
> +#define SMC_VARIANT(n) n
> +#define for_each_smc(smc) \
> +	for ((smc) = 0; (smc) < SMC_VARIANTS; (smc)++)
> +
> +unsigned long __percpu *time[SMC_VARIANTS];
> +
> +/* SMCCC 1.0 */
> +void smc_call_v10(void *arg)
> +{
> +	struct pt_regs _regs = {0}, *regs = &_regs;
> +	int smc = *(unsigned int *)arg;
> +	struct arm_smccc_res res;
> +	ktime_t start, end;
> +
> +	regs->regs[0] = PSCI_0_2_FN_PSCI_VERSION;
> +
> +	start = ktime_get();
> +	arm_smccc_smc(regs->regs[0], regs->regs[1], regs->regs[2],
> +				  regs->regs[3], 0, 0, 0, 0, &res);
> +	end = ktime_get();
> +
> +	this_cpu_write(*time[smc], ktime_to_ns(ktime_sub(end, start)));
> +	smc_dbg("cpu%d %lu ns (ret %#lx)\n",
> +				smc,
> +				this_cpu_read(*time[smc]),
> +				res.a0);
> +}
> +
> +/*  SMCCC 1.1 */
> +void smc_call_v11(void *arg)
> +{
> +	int smc = *(unsigned int *)arg;
> +	ktime_t start, end;
> +
> +	start = ktime_get();
> +	arm_smccc_1_1_smc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL);
> +	end = ktime_get();
> +
> +	this_cpu_write(*time[smc], ktime_to_ns(ktime_sub(end, start)));
> +	smc_dbg("cpu%d %lu ns (ret %s)\n",
> +				smc,
> +				this_cpu_read(*time[smc]),
> +				"RET0 in smc call");
> +}
> +
> +void
> +zeroise_times(void)
> +{
> +	int cpu, smc;
> +
> +	for_each_smc(smc) {
> +		for_each_online_cpu(cpu) {
> +			*per_cpu_ptr(time[smc], cpu) = 0;
> +		}
> +	}
> +}
> +
> +static void run_test(void)
> +{
> +	int smc;
> +
> +	/* init the smc call regs before the next call round */
> +	zeroise_times();
> +
> +	switch (all_cpus) {
> +	case true:
> +		for_each_smc(smc) {
> +			int cpu;
> +
> +			for_each_online_cpu(cpu) {
> +				int ret;
> +
> +				ret = smp_call_function_single(cpu, smc_call[smc],
> +						 (void *)&smc, 1);
> +				if (ret)
> +					WARN_ON(ret);
> +			}
> +		}
> +		break;
> +	case false:
> +		for_each_smc(smc)
> +			smc_call[smc]((void *)&smc);
> +		break;
> +	}
> +}
> +
> +
> +static int proc_smc_show(struct seq_file *m, void *v)
> +{
> +	int smc;
> +
> +	run_test();
> +
> +	for_each_smc(smc) {
> +		int cpu;
> +
> +		seq_printf(m, "SMC_VARIANT(%u): %pf\n", smc, smc_call[smc]);
> +		for_each_online_cpu(cpu)
> +			seq_printf(m, "CPU%-2d ", cpu);
> +		seq_puts(m, "\n");
> +		for_each_online_cpu(cpu)
> +			seq_printf(m, "%-5lu ",
> +				*per_cpu_ptr(time[SMC_VARIANT(smc)], cpu));
> +		seq_puts(m, "\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static int proc_smc_open(struct inode *inode, struct file *file);
> +static const struct file_operations proc_smc_operations = {
> +	.open		= proc_smc_open,
> +	.read		= seq_read,
> +	.llseek		= seq_lseek,
> +	.release	= single_release,
> +};
> +
> +static int proc_smc_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, proc_smc_show, NULL);
> +}
> +
> +static struct proc_dir_entry *procent;
> +static int __init proc_dma_init(void)
> +{
> +	procent = proc_create("axxia_smccall", 0444, NULL,
> +					&proc_smc_operations);
> +	return 0;
> +}
> +
> +
> +static int __init smctest_init(void)
> +{
> +	unsigned int smc;
> +
> +	for_each_smc(smc)
> +		time[smc] = alloc_percpu(unsigned long);
> +
> +	zeroise_times();
> +	proc_dma_init();
> +
> +	return 0;
> +}
> +late_initcall(smctest_init);
> +
> +static void __exit dmatest_exit(void)
> +{
> +	unsigned int smc;
> +
> +	for_each_smc(smc)
> +		if (time[smc]) {
> +			free_percpu(time[smc]);
> +			time[smc] = NULL;
> +		}
> +
> +	if (procent) {
> +		proc_remove(procent);
> +		procent = NULL;
> +	}
> +}
> +
> +module_exit(dmatest_exit);
> +
> +MODULE_AUTHOR("Marek Bykowski");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.16.2
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Marek Bykowski April 10, 2019, 8:07 a.m. UTC | #2
[Apology I didn't respond in the plain text so resending to the mailing list]

Hi Mark,

Thanks for looking through and the posting.

Honestly I didn't know a test framework for ATF as this exists even
though I'm actually part time working on Uboot/Secure Monitor around
the Axxia ARM boards. Actually the patch of myslef is a result of
getting the SMCCC upgraded from 1.0 to 1.1 around the Axxia's TF and
testing it from within the kernel.

For the SMC test in tf-a-tests
https://git.trustedfirmware.org/TF-A/tf-a-tests.git/tree/tftf/tests/performance_tests/smc_latencies.c
as far as I understood the test module built replaces BL33 at EL2
(non-secure state only) to test. First, integration of that framework
is not easy for I guess many platforms, eg. for ours wouldn't be, for
the reason we have a mixture of bootloaders off various places; ROM
bootloader is ours, Secure Monitor is taken off ATF, the BL3 and BL33
is SPL Uboot and Uboot respectively. Secondly I don't think it tests
the SMC latencies for all the CPUs but only for the boot CPU/CPU0. For
us testing the (upgraded) SMC calls on all the CPUs was important as
it turned out the MMU wasn't enabled for the secondary CPUs at EL3 and
a go at invalidating the BTB by turning the MMU off and on (aka
execution of the mitigation to CVE-2017-5715) led to board hang (less
important now the reason but for the reason the Dcache was on by reset
in SCTLR_EL3 when turning the MMU on for the code sitting in (static)
memory non-cachable).

If you reckon the test module I patched could be acceptable if it is
cleaned for the mainstream, the aforementioned missing checks if SMCCC
is supported, duplicated/unused structs, includes not existing in the
mainline, etc. I could work on it. I already know our Customers are
interested in this module that is why I thought I'd get the ARM
community to consider if it could be pushed to the mainline.

Thanks,
Marek



On Tue, 9 Apr 2019 at 18:07, Mark Rutland <mark.rutland@arm.com> wrote:
>
> Hi Marek,
>
> On Tue, Apr 09, 2019 at 04:46:01PM +0200, marek.bykowski@gmail.com wrote:
> > From: Marek Bykowski <marek.bykowski@gmail.com>
> >
> > The module measures the round-trip latency for both PSCI_VERSION and
> > SMCCC_ARCH_WORKAROUND_1 conforming to SMC Calling Convention v1.0 and v1.1
> > respectively for each of the CPUs. Knowing these latencies is becoming
> > critical in quantifying the performance impact resulting in from execution
> > of the mitigation to CVE-2017-5715. Generally the module application can be
> > extended into measuring an arbitrary SMC call duration.
>
> While this is easy to quantify, the overhead in practice will depend on
> your specific application, how it is scheduled, etc. I'd generally hope
> that people would benchmark the typical workload that they care about.
>
> I can see that this is useful for firmware developers, and it looks like
> the trusted firmware test suite already has performance tests for
> exactly these two cases:
>
> https://git.trustedfirmware.org/TF-A/tf-a-tests.git/tree/tftf/tests/performance_tests/smc_latencies.c
>
> ... given that, I'm not sure if we really need this in Linux.
>
> The code in this patch is missing necessary checks (e.g. that SMCCC is
> present and uses SMC), and as such isn't safe on all platforms. Given it
> duplicates functionality we have elsewhere in the arm64 errata handling
> framework, I'd rather the benchmarking lived there if we actually need
> it.
>
> I'm not going to review the code below in detail, but I note that there
> are unusued structure definitions, includes that don't exist in
> mainline, and vestigal function names from another module. So as-is, I
> don't think this is suitable for mainline.
>
> Thanks,
> Mark.
>
> > The sample results for a SoC based on ARM Cortex-A57 MPCore Processor
> > running 16 CPUs is as follows:
> >
> > SMC_VARIANT(0): smc_call_v10 [smc_time]
> > CPU0  CPU1  CPU2  CPU3  CPU4  CPU5  CPU6  CPU7  CPU8  CPU9  CPU10 CPU11 CPU12 CPU13 CPU14 CPU15
> > 4812  5140  5039  5199  5265  5242  4571  4715  4879  4891  4886  4910  4016  4215  4015  4051
> > SMC_VARIANT(1): smc_call_v11 [smc_time]
> > CPU0  CPU1  CPU2  CPU3  CPU4  CPU5  CPU6  CPU7  CPU8  CPU9  CPU10 CPU11 CPU12 CPU13 CPU14 CPU15
> > 476   481   489   489   496   496   496   496   496   496   504   496   480   481   469   477
> >
> > Signed-off-by: Marek Bykowski <marek.bykowski@gmail.com>
> > ---
> >  drivers/firmware/Kconfig       |   9 ++
> >  drivers/firmware/Makefile      |   1 +
> >  drivers/firmware/smc_latency.c | 220 +++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 230 insertions(+)
> >  create mode 100644 drivers/firmware/smc_latency.c
> >
> > diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
> > index bca172d42c74..30777ac0ce1b 100644
> > --- a/drivers/firmware/Kconfig
> > +++ b/drivers/firmware/Kconfig
> > @@ -195,6 +195,15 @@ config QCOM_SCM
> >       depends on ARM || ARM64
> >       select RESET_CONTROLLER
> >
> > +config SMC_LATENCY
> > +     tristate "Measure round-trip latency of the SMC calls"
> > +     depends on HAVE_ARM_SMCCC
> > +     default m
> > +     help
> > +       Say Y or M here to enable the measurement of the round-trip latency
> > +       for both PSCI_VERSION and SMCCC_ARCH_WORKAROUND_1 conforming to SMC
> > +       Calling Convention v1.0 and v1.1 respectively for each of the CPUs.
> > +
> >  config QCOM_SCM_32
> >       def_bool y
> >       depends on QCOM_SCM && ARM
> > diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
> > index 898ac41fa8b3..382bf01470f7 100644
> > --- a/drivers/firmware/Makefile
> > +++ b/drivers/firmware/Makefile
> > @@ -19,6 +19,7 @@ obj-$(CONFIG_FW_CFG_SYSFS)  += qemu_fw_cfg.o
> >  obj-$(CONFIG_QCOM_SCM)               += qcom_scm.o
> >  obj-$(CONFIG_QCOM_SCM_64)    += qcom_scm-64.o
> >  obj-$(CONFIG_QCOM_SCM_32)    += qcom_scm-32.o
> > +obj-$(CONFIG_SMC_LATENCY)    += smc_latency.o
> >  CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a
> >
> >  obj-y                                += broadcom/
> > diff --git a/drivers/firmware/smc_latency.c b/drivers/firmware/smc_latency.c
> > new file mode 100644
> > index 000000000000..fc3697697ed6
> > --- /dev/null
> > +++ b/drivers/firmware/smc_latency.c
> > @@ -0,0 +1,220 @@
> > +/*
> > + *
> > + * SMC testing module
> > + *
> > + * Copyright (C) 2019 Intel Corporation
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + */
> > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> > +
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/proc_fs.h>
> > +#include <linux/slab.h>
> > +#include <linux/linkage.h>
> > +#include <linux/axxia-oem.h>
> > +#include <linux/uaccess.h>
> > +#include <linux/arm-smccc.h>
> > +#include <asm/smp_plat.h>
> > +#include <linux/seq_file.h>
> > +#include <uapi/linux/psci.h>
> > +
> > +#define SMC_DEBUG 0
> > +#if SMC_DEBUG
> > +#define smc_dbg(...) pr_info(__VA_ARGS__)
> > +#define seq_printf(...)
> > +#define seq_puts(...)
> > +#else
> > +#define smc_dbg(...)
> > +#define seq_printf(arg0, ...) seq_printf(arg0, __VA_ARGS__)
> > +#define seq_puts(arg0, ...) seq_puts(arg0, __VA_ARGS__)
> > +#endif
> > +
> > +struct _pt_regs {
> > +     unsigned long elr;
> > +     unsigned long regs[31];
> > +};
> > +
> > +typedef void (*smc_cb_t)(void *);
> > +
> > +static bool all_cpus;
> > +module_param(all_cpus, bool, 0644);
> > +MODULE_PARM_DESC(all_cpus, "Run smc call on executing or all cpus (default: all");
> > +
> > +void smc_call_v10(void *arg);
> > +void smc_call_v11(void *arg);
> > +
> > +smc_cb_t smc_call[] = {
> > +     smc_call_v10,
> > +     smc_call_v11
> > +};
> > +
> > +#define SMC_VARIANTS ARRAY_SIZE(smc_call)
> > +#define SMC_VARIANT(n) n
> > +#define for_each_smc(smc) \
> > +     for ((smc) = 0; (smc) < SMC_VARIANTS; (smc)++)
> > +
> > +unsigned long __percpu *time[SMC_VARIANTS];
> > +
> > +/* SMCCC 1.0 */
> > +void smc_call_v10(void *arg)
> > +{
> > +     struct pt_regs _regs = {0}, *regs = &_regs;
> > +     int smc = *(unsigned int *)arg;
> > +     struct arm_smccc_res res;
> > +     ktime_t start, end;
> > +
> > +     regs->regs[0] = PSCI_0_2_FN_PSCI_VERSION;
> > +
> > +     start = ktime_get();
> > +     arm_smccc_smc(regs->regs[0], regs->regs[1], regs->regs[2],
> > +                               regs->regs[3], 0, 0, 0, 0, &res);
> > +     end = ktime_get();
> > +
> > +     this_cpu_write(*time[smc], ktime_to_ns(ktime_sub(end, start)));
> > +     smc_dbg("cpu%d %lu ns (ret %#lx)\n",
> > +                             smc,
> > +                             this_cpu_read(*time[smc]),
> > +                             res.a0);
> > +}
> > +
> > +/*  SMCCC 1.1 */
> > +void smc_call_v11(void *arg)
> > +{
> > +     int smc = *(unsigned int *)arg;
> > +     ktime_t start, end;
> > +
> > +     start = ktime_get();
> > +     arm_smccc_1_1_smc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL);
> > +     end = ktime_get();
> > +
> > +     this_cpu_write(*time[smc], ktime_to_ns(ktime_sub(end, start)));
> > +     smc_dbg("cpu%d %lu ns (ret %s)\n",
> > +                             smc,
> > +                             this_cpu_read(*time[smc]),
> > +                             "RET0 in smc call");
> > +}
> > +
> > +void
> > +zeroise_times(void)
> > +{
> > +     int cpu, smc;
> > +
> > +     for_each_smc(smc) {
> > +             for_each_online_cpu(cpu) {
> > +                     *per_cpu_ptr(time[smc], cpu) = 0;
> > +             }
> > +     }
> > +}
> > +
> > +static void run_test(void)
> > +{
> > +     int smc;
> > +
> > +     /* init the smc call regs before the next call round */
> > +     zeroise_times();
> > +
> > +     switch (all_cpus) {
> > +     case true:
> > +             for_each_smc(smc) {
> > +                     int cpu;
> > +
> > +                     for_each_online_cpu(cpu) {
> > +                             int ret;
> > +
> > +                             ret = smp_call_function_single(cpu, smc_call[smc],
> > +                                              (void *)&smc, 1);
> > +                             if (ret)
> > +                                     WARN_ON(ret);
> > +                     }
> > +             }
> > +             break;
> > +     case false:
> > +             for_each_smc(smc)
> > +                     smc_call[smc]((void *)&smc);
> > +             break;
> > +     }
> > +}
> > +
> > +
> > +static int proc_smc_show(struct seq_file *m, void *v)
> > +{
> > +     int smc;
> > +
> > +     run_test();
> > +
> > +     for_each_smc(smc) {
> > +             int cpu;
> > +
> > +             seq_printf(m, "SMC_VARIANT(%u): %pf\n", smc, smc_call[smc]);
> > +             for_each_online_cpu(cpu)
> > +                     seq_printf(m, "CPU%-2d ", cpu);
> > +             seq_puts(m, "\n");
> > +             for_each_online_cpu(cpu)
> > +                     seq_printf(m, "%-5lu ",
> > +                             *per_cpu_ptr(time[SMC_VARIANT(smc)], cpu));
> > +             seq_puts(m, "\n");
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int proc_smc_open(struct inode *inode, struct file *file);
> > +static const struct file_operations proc_smc_operations = {
> > +     .open           = proc_smc_open,
> > +     .read           = seq_read,
> > +     .llseek         = seq_lseek,
> > +     .release        = single_release,
> > +};
> > +
> > +static int proc_smc_open(struct inode *inode, struct file *file)
> > +{
> > +     return single_open(file, proc_smc_show, NULL);
> > +}
> > +
> > +static struct proc_dir_entry *procent;
> > +static int __init proc_dma_init(void)
> > +{
> > +     procent = proc_create("axxia_smccall", 0444, NULL,
> > +                                     &proc_smc_operations);
> > +     return 0;
> > +}
> > +
> > +
> > +static int __init smctest_init(void)
> > +{
> > +     unsigned int smc;
> > +
> > +     for_each_smc(smc)
> > +             time[smc] = alloc_percpu(unsigned long);
> > +
> > +     zeroise_times();
> > +     proc_dma_init();
> > +
> > +     return 0;
> > +}
> > +late_initcall(smctest_init);
> > +
> > +static void __exit dmatest_exit(void)
> > +{
> > +     unsigned int smc;
> > +
> > +     for_each_smc(smc)
> > +             if (time[smc]) {
> > +                     free_percpu(time[smc]);
> > +                     time[smc] = NULL;
> > +             }
> > +
> > +     if (procent) {
> > +             proc_remove(procent);
> > +             procent = NULL;
> > +     }
> > +}
> > +
> > +module_exit(dmatest_exit);
> > +
> > +MODULE_AUTHOR("Marek Bykowski");
> > +MODULE_LICENSE("GPL v2");
> > --
> > 2.16.2
> >
> >
> > _______________________________________________
> > linux-arm-kernel mailing list
> > linux-arm-kernel@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel



--
Slán,
Marek
diff mbox series

Patch

diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index bca172d42c74..30777ac0ce1b 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -195,6 +195,15 @@  config QCOM_SCM
 	depends on ARM || ARM64
 	select RESET_CONTROLLER
 
+config SMC_LATENCY
+	tristate "Measure round-trip latency of the SMC calls"
+	depends on HAVE_ARM_SMCCC
+	default m
+	help
+	  Say Y or M here to enable the measurement of the round-trip latency
+	  for both PSCI_VERSION and SMCCC_ARCH_WORKAROUND_1 conforming to SMC
+	  Calling Convention v1.0 and v1.1 respectively for each of the CPUs.
+
 config QCOM_SCM_32
 	def_bool y
 	depends on QCOM_SCM && ARM
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 898ac41fa8b3..382bf01470f7 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -19,6 +19,7 @@  obj-$(CONFIG_FW_CFG_SYSFS)	+= qemu_fw_cfg.o
 obj-$(CONFIG_QCOM_SCM)		+= qcom_scm.o
 obj-$(CONFIG_QCOM_SCM_64)	+= qcom_scm-64.o
 obj-$(CONFIG_QCOM_SCM_32)	+= qcom_scm-32.o
+obj-$(CONFIG_SMC_LATENCY)	+= smc_latency.o
 CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a
 
 obj-y				+= broadcom/
diff --git a/drivers/firmware/smc_latency.c b/drivers/firmware/smc_latency.c
new file mode 100644
index 000000000000..fc3697697ed6
--- /dev/null
+++ b/drivers/firmware/smc_latency.c
@@ -0,0 +1,220 @@ 
+/*
+ *
+ * SMC testing module
+ *
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/linkage.h>
+#include <linux/axxia-oem.h>
+#include <linux/uaccess.h>
+#include <linux/arm-smccc.h>
+#include <asm/smp_plat.h>
+#include <linux/seq_file.h>
+#include <uapi/linux/psci.h>
+
+#define SMC_DEBUG 0
+#if SMC_DEBUG
+#define smc_dbg(...) pr_info(__VA_ARGS__)
+#define seq_printf(...)
+#define seq_puts(...)
+#else
+#define smc_dbg(...)
+#define seq_printf(arg0, ...) seq_printf(arg0, __VA_ARGS__)
+#define seq_puts(arg0, ...) seq_puts(arg0, __VA_ARGS__)
+#endif
+
+struct _pt_regs {
+	unsigned long elr;
+	unsigned long regs[31];
+};
+
+typedef void (*smc_cb_t)(void *);
+
+static bool all_cpus;
+module_param(all_cpus, bool, 0644);
+MODULE_PARM_DESC(all_cpus, "Run smc call on executing or all cpus (default: all");
+
+void smc_call_v10(void *arg);
+void smc_call_v11(void *arg);
+
+smc_cb_t smc_call[] = {
+	smc_call_v10,
+	smc_call_v11
+};
+
+#define SMC_VARIANTS ARRAY_SIZE(smc_call)
+#define SMC_VARIANT(n) n
+#define for_each_smc(smc) \
+	for ((smc) = 0; (smc) < SMC_VARIANTS; (smc)++)
+
+unsigned long __percpu *time[SMC_VARIANTS];
+
+/* SMCCC 1.0 */
+void smc_call_v10(void *arg)
+{
+	struct pt_regs _regs = {0}, *regs = &_regs;
+	int smc = *(unsigned int *)arg;
+	struct arm_smccc_res res;
+	ktime_t start, end;
+
+	regs->regs[0] = PSCI_0_2_FN_PSCI_VERSION;
+
+	start = ktime_get();
+	arm_smccc_smc(regs->regs[0], regs->regs[1], regs->regs[2],
+				  regs->regs[3], 0, 0, 0, 0, &res);
+	end = ktime_get();
+
+	this_cpu_write(*time[smc], ktime_to_ns(ktime_sub(end, start)));
+	smc_dbg("cpu%d %lu ns (ret %#lx)\n",
+				smc,
+				this_cpu_read(*time[smc]),
+				res.a0);
+}
+
+/*  SMCCC 1.1 */
+void smc_call_v11(void *arg)
+{
+	int smc = *(unsigned int *)arg;
+	ktime_t start, end;
+
+	start = ktime_get();
+	arm_smccc_1_1_smc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL);
+	end = ktime_get();
+
+	this_cpu_write(*time[smc], ktime_to_ns(ktime_sub(end, start)));
+	smc_dbg("cpu%d %lu ns (ret %s)\n",
+				smc,
+				this_cpu_read(*time[smc]),
+				"RET0 in smc call");
+}
+
+void
+zeroise_times(void)
+{
+	int cpu, smc;
+
+	for_each_smc(smc) {
+		for_each_online_cpu(cpu) {
+			*per_cpu_ptr(time[smc], cpu) = 0;
+		}
+	}
+}
+
+static void run_test(void)
+{
+	int smc;
+
+	/* init the smc call regs before the next call round */
+	zeroise_times();
+
+	switch (all_cpus) {
+	case true:
+		for_each_smc(smc) {
+			int cpu;
+
+			for_each_online_cpu(cpu) {
+				int ret;
+
+				ret = smp_call_function_single(cpu, smc_call[smc],
+						 (void *)&smc, 1);
+				if (ret)
+					WARN_ON(ret);
+			}
+		}
+		break;
+	case false:
+		for_each_smc(smc)
+			smc_call[smc]((void *)&smc);
+		break;
+	}
+}
+
+
+static int proc_smc_show(struct seq_file *m, void *v)
+{
+	int smc;
+
+	run_test();
+
+	for_each_smc(smc) {
+		int cpu;
+
+		seq_printf(m, "SMC_VARIANT(%u): %pf\n", smc, smc_call[smc]);
+		for_each_online_cpu(cpu)
+			seq_printf(m, "CPU%-2d ", cpu);
+		seq_puts(m, "\n");
+		for_each_online_cpu(cpu)
+			seq_printf(m, "%-5lu ",
+				*per_cpu_ptr(time[SMC_VARIANT(smc)], cpu));
+		seq_puts(m, "\n");
+	}
+
+	return 0;
+}
+
+static int proc_smc_open(struct inode *inode, struct file *file);
+static const struct file_operations proc_smc_operations = {
+	.open		= proc_smc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int proc_smc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, proc_smc_show, NULL);
+}
+
+static struct proc_dir_entry *procent;
+static int __init proc_dma_init(void)
+{
+	procent = proc_create("axxia_smccall", 0444, NULL,
+					&proc_smc_operations);
+	return 0;
+}
+
+
+static int __init smctest_init(void)
+{
+	unsigned int smc;
+
+	for_each_smc(smc)
+		time[smc] = alloc_percpu(unsigned long);
+
+	zeroise_times();
+	proc_dma_init();
+
+	return 0;
+}
+late_initcall(smctest_init);
+
+static void __exit dmatest_exit(void)
+{
+	unsigned int smc;
+
+	for_each_smc(smc)
+		if (time[smc]) {
+			free_percpu(time[smc]);
+			time[smc] = NULL;
+		}
+
+	if (procent) {
+		proc_remove(procent);
+		procent = NULL;
+	}
+}
+
+module_exit(dmatest_exit);
+
+MODULE_AUTHOR("Marek Bykowski");
+MODULE_LICENSE("GPL v2");