Skip to content

Commit cc160ce

Browse files
Mani-Sadhasivamgregkh
authored andcommitted
soc: qcom: ice: Fix race between qcom_ice_probe() and of_qcom_ice_get()
commit d922113 upstream. The current platform driver design causes probe ordering races with consumers (UFS, eMMC) due to ICE's dependency on SCM firmware calls. If ICE probe fails (missing ICE SCM or DT registers), devm_of_qcom_ice_get() loops with -EPROBE_DEFER, leaving consumers non-functional even when ICE should be gracefully disabled. devm_of_qcom_ice_get() doesn't know if the ICE driver probe has failed due to above reasons or it is waiting for the SCM driver. Moreover, there is no devlink dependency between ICE and consumer drivers as 'qcom,ice' is not considered as a DT 'supplier'. So the consumer drivers have no idea of when the ICE driver is going to probe. To address these issues, store the error pointer in a global xarray with ice node phandle as a key during probe in addition to the valid ice pointer and synchronize both qcom_ice_probe() and of_qcom_ice_get() using a mutex. If the xarray entry is NULL, then it implies that the driver is not probed yet, so return -EPROBE_DEFER. If it has any error pointer, return that error pointer directly. Otherwise, add the devlink as usual and return the valid pointer to the consumer. Xarray is used instead of platform drvdata, since driver core frees the drvdata during probe failure. So it cannot be used to pass the error pointer to the consumers. Note that this change only fixes the standalone ICE DT node bindings and not the ones with 'ice' range embedded in the consumer nodes, where there is no issue. Fixes: 2afbf43 ("soc: qcom: Make the Qualcomm UFS/SDCC ICE a dedicated driver") Reported-by: Sumit Garg <sumit.garg@oss.qualcomm.com> Tested-by: Sumit Garg <sumit.garg@oss.qualcomm.com> # OP-TEE as TZ Acked-by: Sumit Garg <sumit.garg@oss.qualcomm.com> Cc: stable@vger.kernel.org # 6.4 Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com> Link: https://lore.kernel.org/r/20260518-qcom-ice-fix-v7-1-2a595382185b@oss.qualcomm.com Signed-off-by: Bjorn Andersson <andersson@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent dedc92b commit cc160ce

1 file changed

Lines changed: 31 additions & 7 deletions

File tree

drivers/soc/qcom/ice.c

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <linux/of.h>
1717
#include <linux/of_platform.h>
1818
#include <linux/platform_device.h>
19+
#include <linux/xarray.h>
1920

2021
#include <linux/firmware/qcom/qcom_scm.h>
2122

@@ -100,6 +101,9 @@ struct qcom_ice {
100101
bool hwkm_init_complete;
101102
};
102103

104+
static DEFINE_XARRAY(ice_handles);
105+
static DEFINE_MUTEX(ice_mutex);
106+
103107
static bool qcom_ice_check_supported(struct qcom_ice *ice)
104108
{
105109
u32 regval = qcom_ice_readl(ice, QCOM_ICE_REG_VERSION);
@@ -609,6 +613,8 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev)
609613
return qcom_ice_create(&pdev->dev, base);
610614
}
611615

616+
guard(mutex)(&ice_mutex);
617+
612618
/*
613619
* If the consumer node does not provider an 'ice' reg range
614620
* (legacy DT binding), then it must at least provide a phandle
@@ -625,12 +631,13 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev)
625631
return ERR_PTR(-ENODEV);
626632
}
627633

628-
ice = platform_get_drvdata(pdev);
629-
if (!ice) {
630-
dev_err(dev, "Cannot get ice instance from %s\n",
631-
dev_name(&pdev->dev));
634+
ice = xa_load(&ice_handles, pdev->dev.of_node->phandle);
635+
if (IS_ERR_OR_NULL(ice)) {
632636
platform_device_put(pdev);
633-
return ERR_PTR(-EPROBE_DEFER);
637+
if (!ice)
638+
return ERR_PTR(-EPROBE_DEFER);
639+
else
640+
return ice;
634641
}
635642

636643
link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER);
@@ -694,24 +701,40 @@ EXPORT_SYMBOL_GPL(devm_of_qcom_ice_get);
694701

695702
static int qcom_ice_probe(struct platform_device *pdev)
696703
{
704+
unsigned long phandle = pdev->dev.of_node->phandle;
697705
struct qcom_ice *engine;
698706
void __iomem *base;
699707

708+
guard(mutex)(&ice_mutex);
709+
700710
base = devm_platform_ioremap_resource(pdev, 0);
701711
if (IS_ERR(base)) {
702712
dev_warn(&pdev->dev, "ICE registers not found\n");
713+
/* Store the error pointer for devm_of_qcom_ice_get() */
714+
xa_store(&ice_handles, phandle, (__force void *)base, GFP_KERNEL);
703715
return PTR_ERR(base);
704716
}
705717

706718
engine = qcom_ice_create(&pdev->dev, base);
707-
if (IS_ERR(engine))
719+
if (IS_ERR(engine)) {
720+
/* Store the error pointer for devm_of_qcom_ice_get() */
721+
xa_store(&ice_handles, phandle, engine, GFP_KERNEL);
708722
return PTR_ERR(engine);
723+
}
709724

710-
platform_set_drvdata(pdev, engine);
725+
xa_store(&ice_handles, phandle, engine, GFP_KERNEL);
711726

712727
return 0;
713728
}
714729

730+
static void qcom_ice_remove(struct platform_device *pdev)
731+
{
732+
unsigned long phandle = pdev->dev.of_node->phandle;
733+
734+
guard(mutex)(&ice_mutex);
735+
xa_store(&ice_handles, phandle, NULL, GFP_KERNEL);
736+
}
737+
715738
static const struct of_device_id qcom_ice_of_match_table[] = {
716739
{ .compatible = "qcom,inline-crypto-engine" },
717740
{ },
@@ -720,6 +743,7 @@ MODULE_DEVICE_TABLE(of, qcom_ice_of_match_table);
720743

721744
static struct platform_driver qcom_ice_driver = {
722745
.probe = qcom_ice_probe,
746+
.remove = qcom_ice_remove,
723747
.driver = {
724748
.name = "qcom-ice",
725749
.of_match_table = qcom_ice_of_match_table,

0 commit comments

Comments
 (0)