| From 448c41bbb36c0c636f6dd1bd3803f07a43051338 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Tue, 30 Apr 2024 20:38:57 -0700 |
| Subject: soc: qcom: pmic_glink: Make client-lock non-sleeping |
| |
| From: Bjorn Andersson <quic_bjorande@quicinc.com> |
| |
| [ Upstream commit 9329933699b32d467a99befa20415c4b2172389a ] |
| |
| The recently introduced commit '635ce0db8956 ("soc: qcom: pmic_glink: |
| don't traverse clients list without a lock")' ensured that the clients |
| list is not modified while traversed. |
| |
| But the callback is made from the GLINK IRQ handler and as such this |
| mutual exclusion can not be provided by a (sleepable) mutex. |
| |
| Replace the mutex with a spinlock. |
| |
| Fixes: 635ce0db8956 ("soc: qcom: pmic_glink: don't traverse clients list without a lock") |
| Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com> |
| Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> |
| Link: https://lore.kernel.org/r/20240430-pmic-glink-sleep-while-atomic-v1-1-88fb493e8545@quicinc.com |
| Signed-off-by: Bjorn Andersson <andersson@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/soc/qcom/pmic_glink.c | 25 +++++++++++++++---------- |
| 1 file changed, 15 insertions(+), 10 deletions(-) |
| |
| diff --git a/drivers/soc/qcom/pmic_glink.c b/drivers/soc/qcom/pmic_glink.c |
| index e85a12ec2aab1..823fd108fa039 100644 |
| --- a/drivers/soc/qcom/pmic_glink.c |
| +++ b/drivers/soc/qcom/pmic_glink.c |
| @@ -11,6 +11,7 @@ |
| #include <linux/slab.h> |
| #include <linux/soc/qcom/pdr.h> |
| #include <linux/soc/qcom/pmic_glink.h> |
| +#include <linux/spinlock.h> |
| |
| enum { |
| PMIC_GLINK_CLIENT_BATT = 0, |
| @@ -36,7 +37,7 @@ struct pmic_glink { |
| unsigned int pdr_state; |
| |
| /* serializing clients list updates */ |
| - struct mutex client_lock; |
| + spinlock_t client_lock; |
| struct list_head clients; |
| }; |
| |
| @@ -58,10 +59,11 @@ static void _devm_pmic_glink_release_client(struct device *dev, void *res) |
| { |
| struct pmic_glink_client *client = (struct pmic_glink_client *)res; |
| struct pmic_glink *pg = client->pg; |
| + unsigned long flags; |
| |
| - mutex_lock(&pg->client_lock); |
| + spin_lock_irqsave(&pg->client_lock, flags); |
| list_del(&client->node); |
| - mutex_unlock(&pg->client_lock); |
| + spin_unlock_irqrestore(&pg->client_lock, flags); |
| } |
| |
| struct pmic_glink_client *devm_pmic_glink_register_client(struct device *dev, |
| @@ -72,6 +74,7 @@ struct pmic_glink_client *devm_pmic_glink_register_client(struct device *dev, |
| { |
| struct pmic_glink_client *client; |
| struct pmic_glink *pg = dev_get_drvdata(dev->parent); |
| + unsigned long flags; |
| |
| client = devres_alloc(_devm_pmic_glink_release_client, sizeof(*client), GFP_KERNEL); |
| if (!client) |
| @@ -84,12 +87,12 @@ struct pmic_glink_client *devm_pmic_glink_register_client(struct device *dev, |
| client->priv = priv; |
| |
| mutex_lock(&pg->state_lock); |
| - mutex_lock(&pg->client_lock); |
| + spin_lock_irqsave(&pg->client_lock, flags); |
| |
| list_add(&client->node, &pg->clients); |
| client->pdr_notify(client->priv, pg->client_state); |
| |
| - mutex_unlock(&pg->client_lock); |
| + spin_unlock_irqrestore(&pg->client_lock, flags); |
| mutex_unlock(&pg->state_lock); |
| |
| devres_add(dev, client); |
| @@ -112,6 +115,7 @@ static int pmic_glink_rpmsg_callback(struct rpmsg_device *rpdev, void *data, |
| struct pmic_glink_client *client; |
| struct pmic_glink_hdr *hdr; |
| struct pmic_glink *pg = dev_get_drvdata(&rpdev->dev); |
| + unsigned long flags; |
| |
| if (len < sizeof(*hdr)) { |
| dev_warn(pg->dev, "ignoring truncated message\n"); |
| @@ -120,12 +124,12 @@ static int pmic_glink_rpmsg_callback(struct rpmsg_device *rpdev, void *data, |
| |
| hdr = data; |
| |
| - mutex_lock(&pg->client_lock); |
| + spin_lock_irqsave(&pg->client_lock, flags); |
| list_for_each_entry(client, &pg->clients, node) { |
| if (client->id == le32_to_cpu(hdr->owner)) |
| client->cb(data, len, client->priv); |
| } |
| - mutex_unlock(&pg->client_lock); |
| + spin_unlock_irqrestore(&pg->client_lock, flags); |
| |
| return 0; |
| } |
| @@ -165,6 +169,7 @@ static void pmic_glink_state_notify_clients(struct pmic_glink *pg) |
| { |
| struct pmic_glink_client *client; |
| unsigned int new_state = pg->client_state; |
| + unsigned long flags; |
| |
| if (pg->client_state != SERVREG_SERVICE_STATE_UP) { |
| if (pg->pdr_state == SERVREG_SERVICE_STATE_UP && pg->ept) |
| @@ -175,10 +180,10 @@ static void pmic_glink_state_notify_clients(struct pmic_glink *pg) |
| } |
| |
| if (new_state != pg->client_state) { |
| - mutex_lock(&pg->client_lock); |
| + spin_lock_irqsave(&pg->client_lock, flags); |
| list_for_each_entry(client, &pg->clients, node) |
| client->pdr_notify(client->priv, new_state); |
| - mutex_unlock(&pg->client_lock); |
| + spin_unlock_irqrestore(&pg->client_lock, flags); |
| pg->client_state = new_state; |
| } |
| } |
| @@ -265,7 +270,7 @@ static int pmic_glink_probe(struct platform_device *pdev) |
| pg->dev = &pdev->dev; |
| |
| INIT_LIST_HEAD(&pg->clients); |
| - mutex_init(&pg->client_lock); |
| + spin_lock_init(&pg->client_lock); |
| mutex_init(&pg->state_lock); |
| |
| match_data = (unsigned long *)of_device_get_match_data(&pdev->dev); |
| -- |
| 2.43.0 |
| |