summaryrefslogtreecommitdiff
path: root/drivers/soc
diff options
context:
space:
mode:
authorHongyan Xu <getshell@seu.edu.cn>2026-08-06 14:05:40 +0800
committerBjorn Andersson <andersson@kernel.org>2026-10-05 09:10:43 +0200
commitbb5c846a74ca119679fdc3241888a3de179d02c2 (patch)
treee907894a9d06c5bfce5358b57b2e09ad10815033 /drivers/soc
parentf3c95a76d2ee9c17034e9d96917b37d194dd9f91 (diff)
soc: qcom: apr: clean up failed service registrations
apr_add_device() publishes a service in svcs_idr before parsing the optional protection domain and registering the device. If device registration fails, put_device() frees the apr_device while its service remains in the IDR. A received packet can then dereference that stale entry. The property error path also leaves the service and allocation behind. Split device_register() into device_initialize() and device_add() so every pre-registration error can safely drop the device reference. Remove the service from the IDR on all failures after publication, then drain the RX workqueue before dropping the device reference. This prevents an in-flight lookup from outliving the device. Fixes: 6adba21eb434 ("soc: qcom: Add APR bus driver") Signed-off-by: Hongyan Xu <getshell@seu.edu.cn> Link: https://lore.kernel.org/r/20260806060541.820-1-getshell@seu.edu.cn Signed-off-by: Bjorn Andersson <andersson@kernel.org>
Diffstat (limited to 'drivers/soc')
-rw-r--r--drivers/soc/qcom/apr.c44
1 files changed, 28 insertions, 16 deletions
diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c
index 2073edb416a4..2d5645ff9919 100644
--- a/drivers/soc/qcom/apr.c
+++ b/drivers/soc/qcom/apr.c
@@ -411,31 +411,35 @@ static int apr_add_device(struct device *dev, struct device_node *np,
if (np)
snprintf(adev->name, APR_NAME_SIZE, "%pOFn", np);
+ adev->dev.bus = &aprbus;
+ adev->dev.parent = dev;
+ adev->dev.of_node = np;
+ adev->dev.release = apr_dev_release;
+ adev->dev.driver = NULL;
+ device_initialize(&adev->dev);
+
switch (apr->type) {
case PR_TYPE_APR:
- dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name,
- domain_id, svc_id);
+ ret = dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name,
+ domain_id, svc_id);
break;
case PR_TYPE_GPR:
- dev_set_name(&adev->dev, "gprsvc:%s:%x:%x", adev->name,
- domain_id, svc_id);
+ ret = dev_set_name(&adev->dev, "gprsvc:%s:%x:%x", adev->name,
+ domain_id, svc_id);
break;
default:
+ ret = -EINVAL;
break;
}
-
- adev->dev.bus = &aprbus;
- adev->dev.parent = dev;
- adev->dev.of_node = np;
- adev->dev.release = apr_dev_release;
- adev->dev.driver = NULL;
+ if (ret)
+ goto out_put_device;
spin_lock(&apr->svcs_lock);
ret = idr_alloc(&apr->svcs_idr, svc, svc_id, svc_id + 1, GFP_ATOMIC);
spin_unlock(&apr->svcs_lock);
if (ret < 0) {
dev_err(dev, "idr_alloc failed: %d\n", ret);
- goto out;
+ goto out_put_device;
}
/* Protection domain is optional, it does not exist on older platforms */
@@ -443,18 +447,26 @@ static int apr_add_device(struct device *dev, struct device_node *np,
1, &adev->service_path);
if (ret < 0 && ret != -EINVAL) {
dev_err(dev, "Failed to read second value of qcom,protection-domain\n");
- goto out;
+ goto out_remove_idr;
}
dev_info(dev, "Adding APR/GPR dev: %s\n", dev_name(&adev->dev));
- ret = device_register(&adev->dev);
+ ret = device_add(&adev->dev);
if (ret) {
- dev_err(dev, "device_register failed: %d\n", ret);
- put_device(&adev->dev);
+ dev_err(dev, "device_add failed: %d\n", ret);
+ goto out_remove_idr;
}
-out:
+ return 0;
+
+out_remove_idr:
+ spin_lock(&apr->svcs_lock);
+ idr_remove(&apr->svcs_idr, svc_id);
+ spin_unlock(&apr->svcs_lock);
+ flush_workqueue(apr->rxwq);
+out_put_device:
+ put_device(&adev->dev);
return ret;
}