Skip to content

Commit d68a2ce

Browse files
committed
soundwire: Use get_device()/put_device() to protect callbacks
Use get_device()/put_device() around calling driver callbacks instead of holding a mutex. This avoids mutex nesting and possible mutex inversion. Rename sdw_dev_lock to probe_remove_lock to make its purpose explicit. When the core SoundWire code calls a driver callback it must be sure that the target driver is not unloaded while the callback is running. The code was using the sdw_dev_lock mutex to block driver remove() while the callback is executing. This meant the callbacks were always called while holding that mutex, which could lead to mutex inversion. For example during enumeration: 1) Take sdw_dev_lock 2) Call update_status() 3) Take regmap lock But during BRA activity we could have: 1) Take regmap lock 2) Start BRA transaction 3) Take sdw_dev_lock 4) Call port_prep() and/or bus_config() callbacks A better way to prevent the target driver being unloaded is to increment its reference count by calling get_device(). This pattern is seen in other drivers that need to call callbacks in another device. The mutex is only needed around the call to get_device() to prevent the driver being removed while its reference count is being incremented. After that, the mutex can be released becase the driver cannot be removed until its reference count is decremented. Change-Id: I1f5cfd81d6204cf3aa92ddc2896af17307edd9c1 Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
1 parent 3a0f2ae commit d68a2ce

6 files changed

Lines changed: 44 additions & 34 deletions

File tree

drivers/soundwire/bus.c

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -962,17 +962,15 @@ static int sdw_slave_clk_stop_callback(struct sdw_slave *slave,
962962
{
963963
int ret = 0;
964964

965-
mutex_lock(&slave->sdw_dev_lock);
966-
967-
if (slave->probed) {
965+
if (sdw_slave_device_get(slave)) {
968966
struct device *dev = &slave->dev;
969967
struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
970968

971969
if (drv->ops && drv->ops->clk_stop)
972970
ret = drv->ops->clk_stop(slave, mode, type);
973-
}
974971

975-
mutex_unlock(&slave->sdw_dev_lock);
972+
sdw_slave_device_put(slave);
973+
}
976974

977975
return ret;
978976
}
@@ -1799,9 +1797,7 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
17991797
if (slave->prop.use_domain_irq && slave->irq)
18001798
handle_nested_irq(slave->irq);
18011799

1802-
mutex_lock(&slave->sdw_dev_lock);
1803-
1804-
if (slave->probed) {
1800+
if (sdw_slave_device_get(slave)) {
18051801
struct device *dev = &slave->dev;
18061802
struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
18071803

@@ -1813,9 +1809,9 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
18131809

18141810
drv->ops->interrupt_callback(slave, &slave_intr);
18151811
}
1816-
}
18171812

1818-
mutex_unlock(&slave->sdw_dev_lock);
1813+
sdw_slave_device_put(slave);
1814+
}
18191815
}
18201816

18211817
/* Ack interrupt */
@@ -1887,17 +1883,15 @@ static int sdw_update_slave_status(struct sdw_slave *slave,
18871883
{
18881884
int ret = 0;
18891885

1890-
mutex_lock(&slave->sdw_dev_lock);
1891-
1892-
if (slave->probed) {
1886+
if (sdw_slave_device_get(slave)) {
18931887
struct device *dev = &slave->dev;
18941888
struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
18951889

18961890
if (drv->ops && drv->ops->update_status)
18971891
ret = drv->ops->update_status(slave, status);
1898-
}
18991892

1900-
mutex_unlock(&slave->sdw_dev_lock);
1893+
sdw_slave_device_put(slave);
1894+
}
19011895

19021896
return ret;
19031897
}

drivers/soundwire/bus.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ static inline int sdw_acpi_find_slaves(struct sdw_bus *bus)
1818
}
1919
#endif
2020

21+
struct device *sdw_slave_device_get(struct sdw_slave *slave);
22+
void sdw_slave_device_put(struct sdw_slave *slave);
23+
2124
int sdw_of_find_slaves(struct sdw_bus *bus);
2225
void sdw_extract_slave_id(struct sdw_bus *bus,
2326
u64 addr, struct sdw_slave_id *id);

drivers/soundwire/bus_type.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ static int sdw_bus_probe(struct device *dev)
112112
return ret;
113113
}
114114

115-
mutex_lock(&slave->sdw_dev_lock);
115+
mutex_lock(&slave->probe_remove_lock);
116116

117117
/* device is probed so let's read the properties now */
118118
if (drv->ops && drv->ops->read_prop)
@@ -151,7 +151,7 @@ static int sdw_bus_probe(struct device *dev)
151151
dev_warn(dev, "failed to update status at probe: %d\n", ret);
152152
}
153153

154-
mutex_unlock(&slave->sdw_dev_lock);
154+
mutex_unlock(&slave->probe_remove_lock);
155155

156156
dev_dbg(dev, "probe complete\n");
157157

@@ -163,11 +163,11 @@ static void sdw_bus_remove(struct device *dev)
163163
struct sdw_slave *slave = dev_to_sdw_dev(dev);
164164
struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
165165

166-
mutex_lock(&slave->sdw_dev_lock);
166+
mutex_lock(&slave->probe_remove_lock);
167167

168168
slave->probed = false;
169169

170-
mutex_unlock(&slave->sdw_dev_lock);
170+
mutex_unlock(&slave->probe_remove_lock);
171171

172172
if (drv->remove)
173173
drv->remove(slave);

drivers/soundwire/slave.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,36 @@
22
// Copyright(c) 2015-17 Intel Corporation.
33

44
#include <linux/acpi.h>
5+
#include <linux/cleanup.h>
6+
#include <linux/mutex.h>
57
#include <linux/of.h>
68
#include <linux/soundwire/sdw.h>
79
#include <linux/soundwire/sdw_type.h>
810
#include <sound/sdca.h>
911
#include "bus.h"
1012
#include "sysfs_local.h"
1113

14+
struct device *sdw_slave_device_get(struct sdw_slave *slave)
15+
{
16+
guard(mutex)(&slave->probe_remove_lock);
17+
18+
if (!slave->probed)
19+
return NULL;
20+
21+
return get_device(&slave->dev);
22+
}
23+
24+
void sdw_slave_device_put(struct sdw_slave *slave)
25+
{
26+
put_device(&slave->dev);
27+
}
28+
1229
static void sdw_slave_release(struct device *dev)
1330
{
1431
struct sdw_slave *slave = dev_to_sdw_dev(dev);
1532

1633
of_node_put(slave->dev.of_node);
17-
mutex_destroy(&slave->sdw_dev_lock);
34+
mutex_destroy(&slave->probe_remove_lock);
1835
kfree(slave);
1936
}
2037

@@ -64,7 +81,7 @@ int sdw_slave_add(struct sdw_bus *bus,
6481
slave->dev_num = 0;
6582
slave->probed = false;
6683
slave->first_interrupt_done = false;
67-
mutex_init(&slave->sdw_dev_lock);
84+
mutex_init(&slave->probe_remove_lock);
6885

6986
for (i = 0; i < SDW_MAX_PORTS; i++)
7087
init_completion(&slave->port_ready[i]);

drivers/soundwire/stream.c

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -430,9 +430,7 @@ static int sdw_do_port_prep(struct sdw_slave_runtime *s_rt,
430430
int ret = 0;
431431
struct sdw_slave *slave = s_rt->slave;
432432

433-
mutex_lock(&slave->sdw_dev_lock);
434-
435-
if (slave->probed) {
433+
if (sdw_slave_device_get(slave)) {
436434
struct device *dev = &slave->dev;
437435
struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
438436

@@ -442,9 +440,9 @@ static int sdw_do_port_prep(struct sdw_slave_runtime *s_rt,
442440
dev_err(dev, "Slave Port Prep cmd %d failed: %d\n",
443441
cmd, ret);
444442
}
445-
}
446443

447-
mutex_unlock(&slave->sdw_dev_lock);
444+
sdw_slave_device_put(slave);
445+
}
448446

449447
return ret;
450448
}
@@ -642,9 +640,7 @@ static int sdw_notify_config(struct sdw_master_runtime *m_rt)
642640
list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
643641
slave = s_rt->slave;
644642

645-
mutex_lock(&slave->sdw_dev_lock);
646-
647-
if (slave->probed) {
643+
if (sdw_slave_device_get(slave)) {
648644
struct device *dev = &slave->dev;
649645
struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
650646

@@ -653,13 +649,13 @@ static int sdw_notify_config(struct sdw_master_runtime *m_rt)
653649
if (ret < 0) {
654650
dev_err(dev, "Notify Slave: %d failed\n",
655651
slave->dev_num);
656-
mutex_unlock(&slave->sdw_dev_lock);
652+
sdw_slave_device_put(slave);
657653
return ret;
658654
}
659655
}
660-
}
661656

662-
mutex_unlock(&slave->sdw_dev_lock);
657+
sdw_slave_device_put(slave);
658+
}
663659
}
664660

665661
return 0;

include/linux/soundwire/sdw.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,7 @@ struct sdw_slave_ops {
659659
* for a Slave happens for the first time after enumeration
660660
* @is_mockup_device: status flag used to squelch errors in the command/control
661661
* protocol for SoundWire mockup devices
662-
* @sdw_dev_lock: mutex used to protect callbacks/remove races
662+
* @probe_remove_lock: mutex used to protect callbacks/remove races
663663
* @sdca_data: structure containing all device data for SDCA helpers
664664
*/
665665
struct sdw_slave {
@@ -684,7 +684,7 @@ struct sdw_slave {
684684
u32 unattach_request;
685685
bool first_interrupt_done;
686686
bool is_mockup_device;
687-
struct mutex sdw_dev_lock; /* protect callbacks/remove races */
687+
struct mutex probe_remove_lock;
688688
struct sdca_device_data sdca_data;
689689
};
690690

0 commit comments

Comments
 (0)