diff options
author | Stephen Warren <swarren@nvidia.com> | 2013-02-11 16:15:32 -0500 |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2013-02-13 05:05:24 -0500 |
commit | c31a0c052205e3ec24efc3fe18ef70c3e913f2d4 (patch) | |
tree | 0c9a9bfea6fd494caefd714fb016a863fc253adf /drivers/of | |
parent | bfc4a58986ba3934bb256ef3567aeeab262aa959 (diff) |
of: fix recursive locking in of_get_next_available_child()
of_get_next_available_child() acquires devtree_lock, then calls
of_device_is_available() which calls of_get_property() which calls
of_find_property() which tries to re-acquire devtree_lock, thus causing
deadlock.
To avoid this, create a new __of_device_is_available() which calls
__of_get_property() instead, which calls __of_find_property(), which
does not take the lock,. Update of_get_next_available_child() to call
the new __of_device_is_available() since it already owns the lock.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'drivers/of')
-rw-r--r-- | drivers/of/base.c | 30 |
1 files changed, 25 insertions, 5 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c index e8d65aff8639..f7a87ce1b480 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c | |||
@@ -290,19 +290,19 @@ int of_machine_is_compatible(const char *compat) | |||
290 | EXPORT_SYMBOL(of_machine_is_compatible); | 290 | EXPORT_SYMBOL(of_machine_is_compatible); |
291 | 291 | ||
292 | /** | 292 | /** |
293 | * of_device_is_available - check if a device is available for use | 293 | * __of_device_is_available - check if a device is available for use |
294 | * | 294 | * |
295 | * @device: Node to check for availability | 295 | * @device: Node to check for availability, with locks already held |
296 | * | 296 | * |
297 | * Returns 1 if the status property is absent or set to "okay" or "ok", | 297 | * Returns 1 if the status property is absent or set to "okay" or "ok", |
298 | * 0 otherwise | 298 | * 0 otherwise |
299 | */ | 299 | */ |
300 | int of_device_is_available(const struct device_node *device) | 300 | static int __of_device_is_available(const struct device_node *device) |
301 | { | 301 | { |
302 | const char *status; | 302 | const char *status; |
303 | int statlen; | 303 | int statlen; |
304 | 304 | ||
305 | status = of_get_property(device, "status", &statlen); | 305 | status = __of_get_property(device, "status", &statlen); |
306 | if (status == NULL) | 306 | if (status == NULL) |
307 | return 1; | 307 | return 1; |
308 | 308 | ||
@@ -313,6 +313,26 @@ int of_device_is_available(const struct device_node *device) | |||
313 | 313 | ||
314 | return 0; | 314 | return 0; |
315 | } | 315 | } |
316 | |||
317 | /** | ||
318 | * of_device_is_available - check if a device is available for use | ||
319 | * | ||
320 | * @device: Node to check for availability | ||
321 | * | ||
322 | * Returns 1 if the status property is absent or set to "okay" or "ok", | ||
323 | * 0 otherwise | ||
324 | */ | ||
325 | int of_device_is_available(const struct device_node *device) | ||
326 | { | ||
327 | unsigned long flags; | ||
328 | int res; | ||
329 | |||
330 | raw_spin_lock_irqsave(&devtree_lock, flags); | ||
331 | res = __of_device_is_available(device); | ||
332 | raw_spin_unlock_irqrestore(&devtree_lock, flags); | ||
333 | return res; | ||
334 | |||
335 | } | ||
316 | EXPORT_SYMBOL(of_device_is_available); | 336 | EXPORT_SYMBOL(of_device_is_available); |
317 | 337 | ||
318 | /** | 338 | /** |
@@ -404,7 +424,7 @@ struct device_node *of_get_next_available_child(const struct device_node *node, | |||
404 | raw_spin_lock(&devtree_lock); | 424 | raw_spin_lock(&devtree_lock); |
405 | next = prev ? prev->sibling : node->child; | 425 | next = prev ? prev->sibling : node->child; |
406 | for (; next; next = next->sibling) { | 426 | for (; next; next = next->sibling) { |
407 | if (!of_device_is_available(next)) | 427 | if (!__of_device_is_available(next)) |
408 | continue; | 428 | continue; |
409 | if (of_node_get(next)) | 429 | if (of_node_get(next)) |
410 | break; | 430 | break; |