Skip to content

Commit 805f7aa

Browse files
robherringlag-linaro
authored andcommitted
mfd: syscon: Fix race in device_node_get_regmap()
It is possible for multiple, simultaneous callers calling device_node_get_regmap() with the same node to fail to find an entry in the syscon_list. There is a period of time while the first caller is calling of_syscon_register() that subsequent callers also fail to find an entry in the syscon_list and then call of_syscon_register() a second time. Fix this by keeping the lock held until after of_syscon_register() completes and adds the node to syscon_list. Convert the spinlock to a mutex as many of the functions called in of_syscon_register() such as kzalloc() and of_clk_get() may sleep. Fixes: bdb0066 ("mfd: syscon: Decouple syscon interface from platform devices") Signed-off-by: Rob Herring (Arm) <[email protected]> Reviewed-by: Krzysztof Kozlowski <[email protected]> Tested-by: Krzysztof Kozlowski <[email protected]> Tested-by: Will McVicker <[email protected]> Tested-by: Pankaj Dubey <[email protected]> Reviewed-by: Pankaj Dubey <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Lee Jones <[email protected]>
1 parent b55689c commit 805f7aa

File tree

1 file changed

+10
-9
lines changed

1 file changed

+10
-9
lines changed

drivers/mfd/syscon.c

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/io.h>
1616
#include <linux/init.h>
1717
#include <linux/list.h>
18+
#include <linux/mutex.h>
1819
#include <linux/of.h>
1920
#include <linux/of_address.h>
2021
#include <linux/of_platform.h>
@@ -27,7 +28,7 @@
2728

2829
static struct platform_driver syscon_driver;
2930

30-
static DEFINE_SPINLOCK(syscon_list_slock);
31+
static DEFINE_MUTEX(syscon_list_lock);
3132
static LIST_HEAD(syscon_list);
3233

3334
struct syscon {
@@ -54,6 +55,8 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_res)
5455
struct resource res;
5556
struct reset_control *reset;
5657

58+
WARN_ON(!mutex_is_locked(&syscon_list_lock));
59+
5760
struct syscon *syscon __free(kfree) = kzalloc(sizeof(*syscon), GFP_KERNEL);
5861
if (!syscon)
5962
return ERR_PTR(-ENOMEM);
@@ -146,9 +149,7 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_res)
146149
syscon->regmap = regmap;
147150
syscon->np = np;
148151

149-
spin_lock(&syscon_list_slock);
150152
list_add_tail(&syscon->list, &syscon_list);
151-
spin_unlock(&syscon_list_slock);
152153

153154
return_ptr(syscon);
154155

@@ -169,19 +170,19 @@ static struct regmap *device_node_get_regmap(struct device_node *np,
169170
{
170171
struct syscon *entry, *syscon = NULL;
171172

172-
spin_lock(&syscon_list_slock);
173+
mutex_lock(&syscon_list_lock);
173174

174175
list_for_each_entry(entry, &syscon_list, list)
175176
if (entry->np == np) {
176177
syscon = entry;
177178
break;
178179
}
179180

180-
spin_unlock(&syscon_list_slock);
181-
182181
if (!syscon)
183182
syscon = of_syscon_register(np, check_res);
184183

184+
mutex_unlock(&syscon_list_lock);
185+
185186
if (IS_ERR(syscon))
186187
return ERR_CAST(syscon);
187188

@@ -212,7 +213,7 @@ int of_syscon_register_regmap(struct device_node *np, struct regmap *regmap)
212213
return -ENOMEM;
213214

214215
/* check if syscon entry already exists */
215-
spin_lock(&syscon_list_slock);
216+
mutex_lock(&syscon_list_lock);
216217

217218
list_for_each_entry(entry, &syscon_list, list)
218219
if (entry->np == np) {
@@ -225,12 +226,12 @@ int of_syscon_register_regmap(struct device_node *np, struct regmap *regmap)
225226

226227
/* register the regmap in syscon list */
227228
list_add_tail(&syscon->list, &syscon_list);
228-
spin_unlock(&syscon_list_slock);
229+
mutex_unlock(&syscon_list_lock);
229230

230231
return 0;
231232

232233
err_unlock:
233-
spin_unlock(&syscon_list_slock);
234+
mutex_unlock(&syscon_list_lock);
234235
kfree(syscon);
235236
return ret;
236237
}

0 commit comments

Comments
 (0)