Re: [PATCH 03/23] interconnect: fix provider registration API

From: Konrad Dybcio
Date: Thu Feb 02 2023 - 21:49:18 EST




On 1.02.2023 11:15, Johan Hovold wrote:
> The current interconnect provider interface is inherently racy as
> providers are expected to be added before being fully initialised.
>
> Specifically, nodes are currently not added and the provider data is not
> initialised until after registering the provider which can cause racing
> DT lookups to fail.
>
> Add a new provider API which will be used to fix up the interconnect
> drivers.
>
> The old API is reimplemented using the new interface and will be removed
> once all drivers have been fixed.
>
> Fixes: 11f1ceca7031 ("interconnect: Add generic on-chip interconnect API")
> Fixes: 87e3031b6fbd ("interconnect: Allow endpoints translation via DT")
> Cc: stable@xxxxxxxxxxxxxxx # 5.1
> Signed-off-by: Johan Hovold <johan+linaro@xxxxxxxxxx>
> ---
Reviewed-by: Konrad Dybcio <konrad.dybcio@xxxxxxxxxx>

Konrad
> drivers/interconnect/core.c | 52 +++++++++++++++++++--------
> include/linux/interconnect-provider.h | 12 +++++++
> 2 files changed, 50 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
> index 43c5c8503ee8..93d27ff8eef6 100644
> --- a/drivers/interconnect/core.c
> +++ b/drivers/interconnect/core.c
> @@ -1030,44 +1030,68 @@ int icc_nodes_remove(struct icc_provider *provider)
> EXPORT_SYMBOL_GPL(icc_nodes_remove);
>
> /**
> - * icc_provider_add() - add a new interconnect provider
> - * @provider: the interconnect provider that will be added into topology
> + * icc_provider_init() - initialize a new interconnect provider
> + * @provider: the interconnect provider to initialize
> + *
> + * Must be called before adding nodes to the provider.
> + */
> +void icc_provider_init(struct icc_provider *provider)
> +{
> + WARN_ON(!provider->set);
> +
> + INIT_LIST_HEAD(&provider->nodes);
> +}
> +EXPORT_SYMBOL_GPL(icc_provider_init);
> +
> +/**
> + * icc_provider_register() - register a new interconnect provider
> + * @provider: the interconnect provider to register
> *
> * Return: 0 on success, or an error code otherwise
> */
> -int icc_provider_add(struct icc_provider *provider)
> +int icc_provider_register(struct icc_provider *provider)
> {
> - if (WARN_ON(!provider->set))
> - return -EINVAL;
> if (WARN_ON(!provider->xlate && !provider->xlate_extended))
> return -EINVAL;
>
> mutex_lock(&icc_lock);
> -
> - INIT_LIST_HEAD(&provider->nodes);
> list_add_tail(&provider->provider_list, &icc_providers);
> -
> mutex_unlock(&icc_lock);
>
> - dev_dbg(provider->dev, "interconnect provider added to topology\n");
> + dev_dbg(provider->dev, "interconnect provider registered\n");
>
> return 0;
> }
> -EXPORT_SYMBOL_GPL(icc_provider_add);
> +EXPORT_SYMBOL_GPL(icc_provider_register);
>
> /**
> - * icc_provider_del() - delete previously added interconnect provider
> - * @provider: the interconnect provider that will be removed from topology
> + * icc_provider_deregister() - deregister an interconnect provider
> + * @provider: the interconnect provider to deregister
> */
> -void icc_provider_del(struct icc_provider *provider)
> +void icc_provider_deregister(struct icc_provider *provider)
> {
> mutex_lock(&icc_lock);
> WARN_ON(provider->users);
> - WARN_ON(!list_empty(&provider->nodes));
>
> list_del(&provider->provider_list);
> mutex_unlock(&icc_lock);
> }
> +EXPORT_SYMBOL_GPL(icc_provider_deregister);
> +
> +int icc_provider_add(struct icc_provider *provider)
> +{
> + icc_provider_init(provider);
> +
> + return icc_provider_register(provider);
> +}
> +EXPORT_SYMBOL_GPL(icc_provider_add);
> +
> +void icc_provider_del(struct icc_provider *provider)
> +{
> + WARN_ON(!list_empty(&provider->nodes));
> +
> + icc_provider_deregister(provider);
> +}
> EXPORT_SYMBOL_GPL(icc_provider_del);
>
> static const struct of_device_id __maybe_unused ignore_list[] = {
> diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h
> index cd5c5a27557f..d12cd18aab3f 100644
> --- a/include/linux/interconnect-provider.h
> +++ b/include/linux/interconnect-provider.h
> @@ -122,6 +122,9 @@ int icc_link_destroy(struct icc_node *src, struct icc_node *dst);
> void icc_node_add(struct icc_node *node, struct icc_provider *provider);
> void icc_node_del(struct icc_node *node);
> int icc_nodes_remove(struct icc_provider *provider);
> +void icc_provider_init(struct icc_provider *provider);
> +int icc_provider_register(struct icc_provider *provider);
> +void icc_provider_deregister(struct icc_provider *provider);
> int icc_provider_add(struct icc_provider *provider);
> void icc_provider_del(struct icc_provider *provider);
> struct icc_node_data *of_icc_get_from_provider(struct of_phandle_args *spec);
> @@ -167,6 +170,15 @@ static inline int icc_nodes_remove(struct icc_provider *provider)
> return -ENOTSUPP;
> }
>
> +static inline void icc_provider_init(struct icc_provider *provider) { }
> +
> +static inline int icc_provider_register(struct icc_provider *provider)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline void icc_provider_deregister(struct icc_provider *provider) { }
> +
> static inline int icc_provider_add(struct icc_provider *provider)
> {
> return -ENOTSUPP;