diff options
author | Ulf Hansson <ulf.hansson@linaro.org> | 2015-01-27 15:13:42 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-02-03 16:56:53 -0500 |
commit | c0356db7d1b66840882744cbd9d9c5960b2d88c7 (patch) | |
tree | 98f3e89374400691135194fa4cbfdc065b96e5ed /drivers/base | |
parent | e07b45cb98839aa972c099ffd5fc5eb2ff1b2e26 (diff) |
PM / Domains: Eliminate the mutex for the generic_pm_domain_data
While adding devices to their PM domains, dev_pm_qos_add_notifier() was
invoked while allocating the generic_pm_domain_data for the device.
Since the generic_pm_domain_data's device pointer will be assigned
after allocation, the ->genpd_dev_pm_qos_notifier() callback could be
called prior having a valid pointer to the device. Similar scenario
existed while removing a device from a genpd.
To cope with these scenarios a mutex was used to protect the pointer to
the device.
By re-order the sequence for when dev_pm_qos_add|remove_notifier() are
invoked, we make sure the ->genpd_dev_pm_qos_notifier() callback are
always called with a valid device pointer available.
In this way, we eliminate the need for protecting the pointer and thus
we can remove the mutex from the struct generic_pm_domain_data.
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/power/domain.c | 37 |
1 files changed, 14 insertions, 23 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 88198ba919d9..1f026c18bc5c 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c | |||
@@ -344,14 +344,7 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, | |||
344 | struct device *dev; | 344 | struct device *dev; |
345 | 345 | ||
346 | gpd_data = container_of(nb, struct generic_pm_domain_data, nb); | 346 | gpd_data = container_of(nb, struct generic_pm_domain_data, nb); |
347 | |||
348 | mutex_lock(&gpd_data->lock); | ||
349 | dev = gpd_data->base.dev; | 347 | dev = gpd_data->base.dev; |
350 | if (!dev) { | ||
351 | mutex_unlock(&gpd_data->lock); | ||
352 | return NOTIFY_DONE; | ||
353 | } | ||
354 | mutex_unlock(&gpd_data->lock); | ||
355 | 348 | ||
356 | for (;;) { | 349 | for (;;) { |
357 | struct generic_pm_domain *genpd; | 350 | struct generic_pm_domain *genpd; |
@@ -1392,16 +1385,12 @@ static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev) | |||
1392 | if (!gpd_data) | 1385 | if (!gpd_data) |
1393 | return NULL; | 1386 | return NULL; |
1394 | 1387 | ||
1395 | mutex_init(&gpd_data->lock); | ||
1396 | gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; | ||
1397 | dev_pm_qos_add_notifier(dev, &gpd_data->nb); | ||
1398 | return gpd_data; | 1388 | return gpd_data; |
1399 | } | 1389 | } |
1400 | 1390 | ||
1401 | static void genpd_free_dev_data(struct device *dev, | 1391 | static void genpd_free_dev_data(struct device *dev, |
1402 | struct generic_pm_domain_data *gpd_data) | 1392 | struct generic_pm_domain_data *gpd_data) |
1403 | { | 1393 | { |
1404 | dev_pm_qos_remove_notifier(dev, &gpd_data->nb); | ||
1405 | kfree(gpd_data); | 1394 | kfree(gpd_data); |
1406 | } | 1395 | } |
1407 | 1396 | ||
@@ -1414,7 +1403,7 @@ static void genpd_free_dev_data(struct device *dev, | |||
1414 | int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, | 1403 | int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, |
1415 | struct gpd_timing_data *td) | 1404 | struct gpd_timing_data *td) |
1416 | { | 1405 | { |
1417 | struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL; | 1406 | struct generic_pm_domain_data *gpd_data; |
1418 | int ret = 0; | 1407 | int ret = 0; |
1419 | 1408 | ||
1420 | dev_dbg(dev, "%s()\n", __func__); | 1409 | dev_dbg(dev, "%s()\n", __func__); |
@@ -1422,8 +1411,8 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, | |||
1422 | if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) | 1411 | if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) |
1423 | return -EINVAL; | 1412 | return -EINVAL; |
1424 | 1413 | ||
1425 | gpd_data_new = genpd_alloc_dev_data(dev); | 1414 | gpd_data = genpd_alloc_dev_data(dev); |
1426 | if (!gpd_data_new) | 1415 | if (!gpd_data) |
1427 | return -ENOMEM; | 1416 | return -ENOMEM; |
1428 | 1417 | ||
1429 | genpd_acquire_lock(genpd); | 1418 | genpd_acquire_lock(genpd); |
@@ -1445,7 +1434,6 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, | |||
1445 | goto out; | 1434 | goto out; |
1446 | } | 1435 | } |
1447 | 1436 | ||
1448 | gpd_data = gpd_data_new; | ||
1449 | dev->power.subsys_data->domain_data = &gpd_data->base; | 1437 | dev->power.subsys_data->domain_data = &gpd_data->base; |
1450 | 1438 | ||
1451 | if (td) | 1439 | if (td) |
@@ -1461,19 +1449,20 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, | |||
1461 | genpd->device_count++; | 1449 | genpd->device_count++; |
1462 | genpd->max_off_time_changed = true; | 1450 | genpd->max_off_time_changed = true; |
1463 | 1451 | ||
1464 | mutex_lock(&gpd_data->lock); | ||
1465 | gpd_data->base.dev = dev; | 1452 | gpd_data->base.dev = dev; |
1466 | list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); | 1453 | list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); |
1467 | gpd_data->need_restore = -1; | 1454 | gpd_data->need_restore = -1; |
1468 | gpd_data->td.constraint_changed = true; | 1455 | gpd_data->td.constraint_changed = true; |
1469 | gpd_data->td.effective_constraint_ns = -1; | 1456 | gpd_data->td.effective_constraint_ns = -1; |
1470 | mutex_unlock(&gpd_data->lock); | 1457 | gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; |
1471 | 1458 | ||
1472 | out: | 1459 | out: |
1473 | genpd_release_lock(genpd); | 1460 | genpd_release_lock(genpd); |
1474 | 1461 | ||
1475 | if (gpd_data != gpd_data_new) | 1462 | if (ret) |
1476 | genpd_free_dev_data(dev, gpd_data_new); | 1463 | genpd_free_dev_data(dev, gpd_data); |
1464 | else | ||
1465 | dev_pm_qos_add_notifier(dev, &gpd_data->nb); | ||
1477 | 1466 | ||
1478 | return ret; | 1467 | return ret; |
1479 | } | 1468 | } |
@@ -1509,6 +1498,11 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, | |||
1509 | || pd_to_genpd(dev->pm_domain) != genpd) | 1498 | || pd_to_genpd(dev->pm_domain) != genpd) |
1510 | return -EINVAL; | 1499 | return -EINVAL; |
1511 | 1500 | ||
1501 | /* The above validation also means we have existing domain_data. */ | ||
1502 | pdd = dev->power.subsys_data->domain_data; | ||
1503 | gpd_data = to_gpd_data(pdd); | ||
1504 | dev_pm_qos_remove_notifier(dev, &gpd_data->nb); | ||
1505 | |||
1512 | genpd_acquire_lock(genpd); | 1506 | genpd_acquire_lock(genpd); |
1513 | 1507 | ||
1514 | if (genpd->prepared_count > 0) { | 1508 | if (genpd->prepared_count > 0) { |
@@ -1525,16 +1519,12 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, | |||
1525 | spin_lock_irq(&dev->power.lock); | 1519 | spin_lock_irq(&dev->power.lock); |
1526 | 1520 | ||
1527 | dev->pm_domain = NULL; | 1521 | dev->pm_domain = NULL; |
1528 | pdd = dev->power.subsys_data->domain_data; | ||
1529 | list_del_init(&pdd->list_node); | 1522 | list_del_init(&pdd->list_node); |
1530 | gpd_data = to_gpd_data(pdd); | ||
1531 | dev->power.subsys_data->domain_data = NULL; | 1523 | dev->power.subsys_data->domain_data = NULL; |
1532 | 1524 | ||
1533 | spin_unlock_irq(&dev->power.lock); | 1525 | spin_unlock_irq(&dev->power.lock); |
1534 | 1526 | ||
1535 | mutex_lock(&gpd_data->lock); | ||
1536 | pdd->dev = NULL; | 1527 | pdd->dev = NULL; |
1537 | mutex_unlock(&gpd_data->lock); | ||
1538 | 1528 | ||
1539 | genpd_release_lock(genpd); | 1529 | genpd_release_lock(genpd); |
1540 | 1530 | ||
@@ -1545,6 +1535,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, | |||
1545 | 1535 | ||
1546 | out: | 1536 | out: |
1547 | genpd_release_lock(genpd); | 1537 | genpd_release_lock(genpd); |
1538 | dev_pm_qos_add_notifier(dev, &gpd_data->nb); | ||
1548 | 1539 | ||
1549 | return ret; | 1540 | return ret; |
1550 | } | 1541 | } |