usb: chipidea: core: fix possible concurrent when switch role
commit 451b15ed138ec15bffbebb58a00ebdd884c3e659 upstream.
The user may call role_store() when driver is handling
ci_handle_id_switch() which is triggerred by otg event or power lost
event. Unfortunately, the controller may go into chaos in this case.
Fix this by protecting it with mutex lock.
Fixes: a932a8041f
("usb: chipidea: core: add sysfs group")
cc: <stable@vger.kernel.org>
Acked-by: Peter Chen <peter.chen@kernel.org>
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
Link: https://lore.kernel.org/r/20230317061516.2451728-2-xu.yang_2@nxp.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
073ce98aa3
commit
09671cfc2b
@@ -204,6 +204,7 @@ struct hw_bank {
|
|||||||
* @in_lpm: if the core in low power mode
|
* @in_lpm: if the core in low power mode
|
||||||
* @wakeup_int: if wakeup interrupt occur
|
* @wakeup_int: if wakeup interrupt occur
|
||||||
* @rev: The revision number for controller
|
* @rev: The revision number for controller
|
||||||
|
* @mutex: protect code from concorrent running when doing role switch
|
||||||
*/
|
*/
|
||||||
struct ci_hdrc {
|
struct ci_hdrc {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
@@ -257,6 +258,7 @@ struct ci_hdrc {
|
|||||||
bool in_lpm;
|
bool in_lpm;
|
||||||
bool wakeup_int;
|
bool wakeup_int;
|
||||||
enum ci_revision rev;
|
enum ci_revision rev;
|
||||||
|
struct mutex mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci)
|
static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci)
|
||||||
|
@@ -969,8 +969,12 @@ static ssize_t role_store(struct device *dev,
|
|||||||
if (role == CI_ROLE_END)
|
if (role == CI_ROLE_END)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (role == ci->role)
|
mutex_lock(&ci->mutex);
|
||||||
|
|
||||||
|
if (role == ci->role) {
|
||||||
|
mutex_unlock(&ci->mutex);
|
||||||
return n;
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
pm_runtime_get_sync(dev);
|
pm_runtime_get_sync(dev);
|
||||||
disable_irq(ci->irq);
|
disable_irq(ci->irq);
|
||||||
@@ -980,6 +984,7 @@ static ssize_t role_store(struct device *dev,
|
|||||||
ci_handle_vbus_change(ci);
|
ci_handle_vbus_change(ci);
|
||||||
enable_irq(ci->irq);
|
enable_irq(ci->irq);
|
||||||
pm_runtime_put_sync(dev);
|
pm_runtime_put_sync(dev);
|
||||||
|
mutex_unlock(&ci->mutex);
|
||||||
|
|
||||||
return (ret == 0) ? n : ret;
|
return (ret == 0) ? n : ret;
|
||||||
}
|
}
|
||||||
@@ -1015,6 +1020,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
spin_lock_init(&ci->lock);
|
spin_lock_init(&ci->lock);
|
||||||
|
mutex_init(&ci->mutex);
|
||||||
ci->dev = dev;
|
ci->dev = dev;
|
||||||
ci->platdata = dev_get_platdata(dev);
|
ci->platdata = dev_get_platdata(dev);
|
||||||
ci->imx28_write_fix = !!(ci->platdata->flags &
|
ci->imx28_write_fix = !!(ci->platdata->flags &
|
||||||
|
@@ -166,8 +166,10 @@ static int hw_wait_vbus_lower_bsv(struct ci_hdrc *ci)
|
|||||||
|
|
||||||
static void ci_handle_id_switch(struct ci_hdrc *ci)
|
static void ci_handle_id_switch(struct ci_hdrc *ci)
|
||||||
{
|
{
|
||||||
enum ci_role role = ci_otg_role(ci);
|
enum ci_role role;
|
||||||
|
|
||||||
|
mutex_lock(&ci->mutex);
|
||||||
|
role = ci_otg_role(ci);
|
||||||
if (role != ci->role) {
|
if (role != ci->role) {
|
||||||
dev_dbg(ci->dev, "switching from %s to %s\n",
|
dev_dbg(ci->dev, "switching from %s to %s\n",
|
||||||
ci_role(ci)->name, ci->roles[role]->name);
|
ci_role(ci)->name, ci->roles[role]->name);
|
||||||
@@ -197,6 +199,7 @@ static void ci_handle_id_switch(struct ci_hdrc *ci)
|
|||||||
if (role == CI_ROLE_GADGET)
|
if (role == CI_ROLE_GADGET)
|
||||||
ci_handle_vbus_change(ci);
|
ci_handle_vbus_change(ci);
|
||||||
}
|
}
|
||||||
|
mutex_unlock(&ci->mutex);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* ci_otg_work - perform otg (vbus/id) event handle
|
* ci_otg_work - perform otg (vbus/id) event handle
|
||||||
|
Reference in New Issue
Block a user