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; |
