[PATCH v2] coresight: etm4x: add CPU hotplug support for probing
Suzuki K Poulose
suzuki.poulose at arm.com
Mon Jul 11 02:57:21 PDT 2022
Hi Tamas
Thanks for the respin. Looks good to me except for the tear
down path. Please find my comment inline.
On 05/07/2022 15:59, Tamas Zsoldos wrote:
> etm4x devices cannot be successfully probed when their CPU is offline.
> For example, when booting with maxcpus=n, ETM probing will fail on
> CPUs >n, and the probing won't be reattempted once the CPUs come
> online. This will leave those CPUs unable to make use of ETM.
>
> This change adds a mechanism to delay the probing if the corresponding
> CPU is offline, and to try it again when the CPU comes online.
>
> Signed-off-by: Tamas Zsoldos <tamas.zsoldos at arm.com>
> ---
>
> Changes from v1 (all suggested by Suzuki):
> 1. Expanded commit message.
> 2. The delayed probe now uses etm4_init_arg.
> 3. Reuses etm4_online_cpu() instead of installing own cpuhp callback.
> 4. Changed guard condition in etm4_remove_dev().
>
> ---
> .../coresight/coresight-etm4x-core.c | 153 +++++++++++++-----
> 1 file changed, 113 insertions(+), 40 deletions(-)
>
> diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
> index 87299e99dabb..c39d9055b22c 100644
> --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
> +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
> @@ -66,10 +66,13 @@ static enum cpuhp_state hp_online;
>
> struct etm4_init_arg {
> unsigned int pid;
> - struct etmv4_drvdata *drvdata;
> + struct device *dev;
> struct csdev_access *csa;
> };
>
> +static DEFINE_PER_CPU(struct etm4_init_arg *, delayed_probe);
> +static int etm4_probe_cpu(unsigned int cpu);
> +
> /*
> * Check if TRCSSPCICRn(i) is implemented for a given instance.
> *
> @@ -1071,7 +1074,7 @@ static void etm4_init_arch_data(void *info)
> struct csdev_access *csa;
> int i;
>
> - drvdata = init_arg->drvdata;
> + drvdata = dev_get_drvdata(init_arg->dev);
> csa = init_arg->csa;
>
> /*
> @@ -1514,7 +1517,7 @@ void etm4_config_trace_mode(struct etmv4_config *config)
> static int etm4_online_cpu(unsigned int cpu)
> {
> if (!etmdrvdata[cpu])
> - return 0;
> + return etm4_probe_cpu(cpu);
>
> if (etmdrvdata[cpu]->boot_enable && !etmdrvdata[cpu]->sticky_enable)
> coresight_enable(etmdrvdata[cpu]->csdev);
> @@ -1890,48 +1893,20 @@ static void etm4_pm_clear(void)
> }
> }
>
> -static int etm4_probe(struct device *dev, void __iomem *base, u32 etm_pid)
> +static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg)
> {
> int ret;
> struct coresight_platform_data *pdata = NULL;
> - struct etmv4_drvdata *drvdata;
> + struct device *dev = init_arg->dev;
> + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
> struct coresight_desc desc = { 0 };
> - struct etm4_init_arg init_arg = { 0 };
> u8 major, minor;
> char *type_name;
>
> - drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> if (!drvdata)
> - return -ENOMEM;
> -
> - dev_set_drvdata(dev, drvdata);
> -
> - if (pm_save_enable == PARAM_PM_SAVE_FIRMWARE)
> - pm_save_enable = coresight_loses_context_with_cpu(dev) ?
> - PARAM_PM_SAVE_SELF_HOSTED : PARAM_PM_SAVE_NEVER;
> -
> - if (pm_save_enable != PARAM_PM_SAVE_NEVER) {
> - drvdata->save_state = devm_kmalloc(dev,
> - sizeof(struct etmv4_save_state), GFP_KERNEL);
> - if (!drvdata->save_state)
> - return -ENOMEM;
> - }
> -
> - drvdata->base = base;
> -
> - spin_lock_init(&drvdata->spinlock);
> -
> - drvdata->cpu = coresight_get_cpu(dev);
> - if (drvdata->cpu < 0)
> - return drvdata->cpu;
> -
> - init_arg.drvdata = drvdata;
> - init_arg.csa = &desc.access;
> - init_arg.pid = etm_pid;
> + return -EINVAL;
>
> - if (smp_call_function_single(drvdata->cpu,
> - etm4_init_arch_data, &init_arg, 1))
> - dev_err(dev, "ETM arch init failed\n");
> + desc.access = *init_arg->csa;
>
> if (!drvdata->arch)
> return -EINVAL;
> @@ -2002,6 +1977,68 @@ static int etm4_probe(struct device *dev, void __iomem *base, u32 etm_pid)
> return 0;
> }
>
> +static int etm4_probe(struct device *dev, void __iomem *base, u32 etm_pid)
> +{
> + struct etmv4_drvdata *drvdata;
> + struct csdev_access access = { 0 };
> + struct etm4_init_arg init_arg = { 0 };
> + struct etm4_init_arg *delayed;
> +
> + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> + if (!drvdata)
> + return -ENOMEM;
> +
> + dev_set_drvdata(dev, drvdata);
> +
> + if (pm_save_enable == PARAM_PM_SAVE_FIRMWARE)
> + pm_save_enable = coresight_loses_context_with_cpu(dev) ?
> + PARAM_PM_SAVE_SELF_HOSTED : PARAM_PM_SAVE_NEVER;
> +
> + if (pm_save_enable != PARAM_PM_SAVE_NEVER) {
> + drvdata->save_state = devm_kmalloc(dev,
> + sizeof(struct etmv4_save_state), GFP_KERNEL);
> + if (!drvdata->save_state)
> + return -ENOMEM;
> + }
> +
> + drvdata->base = base;
> +
> + spin_lock_init(&drvdata->spinlock);
> +
> + drvdata->cpu = coresight_get_cpu(dev);
> + if (drvdata->cpu < 0)
> + return drvdata->cpu;
> +
> + init_arg.dev = dev;
> + init_arg.csa = &access;
> + init_arg.pid = etm_pid;
> +
> + /*
> + * Serialize against CPUHP callbacks to avoid race condition
> + * between the smp call and saving the delayed probe.
> + */
> + cpus_read_lock();
> + if (smp_call_function_single(drvdata->cpu,
> + etm4_init_arch_data, &init_arg, 1)) {
> + /* The CPU was offline, try again once it comes online. */
> + delayed = devm_kmalloc(dev, sizeof(*delayed), GFP_KERNEL);
> + if (!delayed) {
> + cpus_read_unlock();
> + return -ENOMEM;
> + }
> +
> + *delayed = init_arg;
> +
> + per_cpu(delayed_probe, drvdata->cpu) = delayed;
> +
> + cpus_read_unlock();
> + return 0;
> + }
> + cpus_read_unlock();
> +
> + return etm4_add_coresight_dev(&init_arg);
> +}
> +
> static int etm4_probe_amba(struct amba_device *adev, const struct amba_id *id)
> {
> void __iomem *base;
> @@ -2040,6 +2077,35 @@ static int etm4_probe_platform_dev(struct platform_device *pdev)
> return ret;
> }
>
> +static int etm4_probe_cpu(unsigned int cpu)
> +{
> + int ret;
> + struct etm4_init_arg init_arg;
> + struct csdev_access access = { 0 };
> + struct etm4_init_arg *iap = *this_cpu_ptr(&delayed_probe);
> +
> + if (!iap)
> + return 0;
> +
> + init_arg = *iap;
> + devm_kfree(init_arg.dev, iap);
> + *this_cpu_ptr(&delayed_probe) = NULL;
> +
> + ret = pm_runtime_resume_and_get(init_arg.dev);
> + if (ret < 0) {
> + dev_err(init_arg.dev, "Failed to get PM runtime!\n");
> + return 0;
> + }
> +
> + init_arg.csa = &access;
> + etm4_init_arch_data(&init_arg);
> +
> + etm4_add_coresight_dev(&init_arg);
> +
> + pm_runtime_put(init_arg.dev);
> + return 0;
> +}
> +
> static struct amba_cs_uci_id uci_id_etm4[] = {
> {
> /* ETMv4 UCI data */
> @@ -2054,16 +2120,20 @@ static void clear_etmdrvdata(void *info)
> int cpu = *(int *)info;
>
> etmdrvdata[cpu] = NULL;
> + per_cpu(delayed_probe, cpu) = NULL;
> }
>
> static int __exit etm4_remove_dev(struct etmv4_drvdata *drvdata)
> {
The change with this patch is that we may get called to remove a
device with non-NULL drvdata, but without a coresight_device active.
And, ...
> - etm_perf_symlink(drvdata->csdev, false);
> + bool had_delayed_probe;
> /*
> * Taking hotplug lock here to avoid racing between etm4_remove_dev()
> * and CPU hotplug call backs.
> */
> cpus_read_lock();
> +
> + had_delayed_probe = per_cpu(delayed_probe, drvdata->cpu);
> +
... this check may not be sufficient. This only tells us, if a delayed
probe is pending. But, the delayed probe fail to register a csdev and
leave us with drvdata->csdev == NULL and that could blow up below.
So, I think the right check must be to ensure that the csdev was
successfully registered, which can be done via
if (etmdrvdata[drvdata->cpu] == drvdata)
and don't bother about delayed probe here.
Otherwise looks good to me.
Suzuki
More information about the linux-arm-kernel
mailing list