mirror of
https://github.com/torvalds/linux.git
synced 2026-07-31 03:27:03 +02:00
soc: qcom: pmic_glink: Fix a race condition in removing rpmsg device
When a pmic_glink client tries to perform a transaction through pmic_glink_write API during the time when rpmsg device is being removed, there is a possibility for client attempting to send a message after rpmsg device has been marked NULL. Add a read-write semaphore to ensure multiple glink clients can still perform transactions in parallel while ensuring rpmsg device removal cannot be done concurrently with transactions. Change-Id: Ibeeced29d338154cebb2b47729a652cfe148dd83 Signed-off-by: Jishnu Prakash <quic_jprakash@quicinc.com>
This commit is contained in:
parent
98b7580d6c
commit
d116d1e14c
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "PMIC_GLINK: %s: " fmt, __func__
|
||||
|
|
@ -30,6 +30,8 @@
|
|||
* @channel_name: Glink channel name used by rpmsg device
|
||||
* @client_idr: idr list for the clients
|
||||
* @client_lock: mutex lock when idr APIs are used on client_idr
|
||||
* @rpdev_sem: read-write semaphore to synchronize glink channel
|
||||
* availability and rpmsg transactions
|
||||
* @rx_lock: spinlock to be used when rx_list is modified
|
||||
* @rx_work: worker for handling rx messages
|
||||
* @init_work: worker to instantiate child devices under pdev
|
||||
|
|
@ -59,6 +61,7 @@ struct pmic_glink_dev {
|
|||
const char *channel_name;
|
||||
struct idr client_idr;
|
||||
struct mutex client_lock;
|
||||
struct rw_semaphore rpdev_sem;
|
||||
spinlock_t rx_lock;
|
||||
struct work_struct rx_work;
|
||||
struct work_struct init_work;
|
||||
|
|
@ -234,14 +237,18 @@ int pmic_glink_write(struct pmic_glink_client *client, void *data,
|
|||
if (!client || !client->pgdev || !client->name)
|
||||
return -ENODEV;
|
||||
|
||||
down_read(&client->pgdev->rpdev_sem);
|
||||
|
||||
if (!client->pgdev->rpdev || !atomic_read(&client->pgdev->state)) {
|
||||
pr_err("Error in sending data for client %s\n", client->name);
|
||||
up_read(&client->pgdev->rpdev_sem);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
mutex_lock(&client->lock);
|
||||
rc = rpmsg_send(client->pgdev->rpdev->ept, data, len);
|
||||
mutex_unlock(&client->lock);
|
||||
up_read(&client->pgdev->rpdev_sem);
|
||||
|
||||
if (rc < 0)
|
||||
pr_err("Failed to send data [%*ph] for client %s, rc=%d\n",
|
||||
|
|
@ -454,8 +461,10 @@ static void pmic_glink_rpmsg_remove(struct rpmsg_device *rpdev)
|
|||
return;
|
||||
}
|
||||
|
||||
down_write(&pgdev->rpdev_sem);
|
||||
atomic_set(&pgdev->state, 0);
|
||||
pgdev->rpdev = NULL;
|
||||
up_write(&pgdev->rpdev_sem);
|
||||
pr_debug("%s removed\n", rpdev->id.name);
|
||||
}
|
||||
|
||||
|
|
@ -469,9 +478,11 @@ static int pmic_glink_rpmsg_probe(struct rpmsg_device *rpdev)
|
|||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
down_write(&pgdev->rpdev_sem);
|
||||
dev_set_drvdata(&rpdev->dev, pgdev);
|
||||
pgdev->rpdev = rpdev;
|
||||
atomic_set(&pgdev->state, 1);
|
||||
up_write(&pgdev->rpdev_sem);
|
||||
schedule_work(&pgdev->init_work);
|
||||
pr_debug("%s probed\n", rpdev->id.name);
|
||||
|
||||
|
|
@ -619,6 +630,7 @@ static int pmic_glink_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
init_rwsem(&pgdev->rpdev_sem);
|
||||
INIT_WORK(&pgdev->rx_work, pmic_glink_rx_work);
|
||||
INIT_WORK(&pgdev->init_work, pmic_glink_init_work);
|
||||
INIT_LIST_HEAD(&pgdev->client_dev_list);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user