summaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
authorJon Hunter <jonathanh@nvidia.com>2016-09-12 07:01:11 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-09-12 20:49:34 -0400
commit19efa5ff63dc5ed41ac85f5a8518aa77cece3776 (patch)
treeadf901b588e83fe71c9887b4498e46bd6f29acd3 /drivers/base
parent0159ec670763dde7c8518034eda8ab9ec267fd23 (diff)
PM / Domains: Prepare for adding support to remove PM domains
In order to remove PM domains safely from the list of PM domains, it is necessary to adding locking for the PM domain list around any places where devices or subdomains are added to a PM domain. There are places where a reference to a PM domain is obtained via calling of_genpd_get_from_provider() before adding the device or the subdomain. In these cases a lock for the PM domain list needs to be held around the call to of_genpd_get_from_provider() and the call to add the device/subdomain. To avoid deadlocks by multiple attempts to obtain the PM domain list lock, add functions genpd_add_device() and genpd_add_subdomain() which require the user to hold the PM domain list lock when calling. Signed-off-by: Jon Hunter <jonathanh@nvidia.com> Acked-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.c97
1 files changed, 73 insertions, 24 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index fc9f11c26eec..1bd8d412db06 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1060,14 +1060,8 @@ static void genpd_free_dev_data(struct device *dev,
1060 dev_pm_put_subsys_data(dev); 1060 dev_pm_put_subsys_data(dev);
1061} 1061}
1062 1062
1063/** 1063static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1064 * __pm_genpd_add_device - Add a device to an I/O PM domain. 1064 struct gpd_timing_data *td)
1065 * @genpd: PM domain to add the device to.
1066 * @dev: Device to be added.
1067 * @td: Set of PM QoS timing parameters to attach to the device.
1068 */
1069int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1070 struct gpd_timing_data *td)
1071{ 1065{
1072 struct generic_pm_domain_data *gpd_data; 1066 struct generic_pm_domain_data *gpd_data;
1073 int ret = 0; 1067 int ret = 0;
@@ -1107,6 +1101,24 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1107 1101
1108 return ret; 1102 return ret;
1109} 1103}
1104
1105/**
1106 * __pm_genpd_add_device - Add a device to an I/O PM domain.
1107 * @genpd: PM domain to add the device to.
1108 * @dev: Device to be added.
1109 * @td: Set of PM QoS timing parameters to attach to the device.
1110 */
1111int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1112 struct gpd_timing_data *td)
1113{
1114 int ret;
1115
1116 mutex_lock(&gpd_list_lock);
1117 ret = genpd_add_device(genpd, dev, td);
1118 mutex_unlock(&gpd_list_lock);
1119
1120 return ret;
1121}
1110EXPORT_SYMBOL_GPL(__pm_genpd_add_device); 1122EXPORT_SYMBOL_GPL(__pm_genpd_add_device);
1111 1123
1112/** 1124/**
@@ -1160,13 +1172,8 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
1160} 1172}
1161EXPORT_SYMBOL_GPL(pm_genpd_remove_device); 1173EXPORT_SYMBOL_GPL(pm_genpd_remove_device);
1162 1174
1163/** 1175static int genpd_add_subdomain(struct generic_pm_domain *genpd,
1164 * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1176 struct generic_pm_domain *subdomain)
1165 * @genpd: Master PM domain to add the subdomain to.
1166 * @subdomain: Subdomain to be added.
1167 */
1168int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
1169 struct generic_pm_domain *subdomain)
1170{ 1177{
1171 struct gpd_link *link, *itr; 1178 struct gpd_link *link, *itr;
1172 int ret = 0; 1179 int ret = 0;
@@ -1209,6 +1216,23 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
1209 kfree(link); 1216 kfree(link);
1210 return ret; 1217 return ret;
1211} 1218}
1219
1220/**
1221 * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
1222 * @genpd: Master PM domain to add the subdomain to.
1223 * @subdomain: Subdomain to be added.
1224 */
1225int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
1226 struct generic_pm_domain *subdomain)
1227{
1228 int ret;
1229
1230 mutex_lock(&gpd_list_lock);
1231 ret = genpd_add_subdomain(genpd, subdomain);
1232 mutex_unlock(&gpd_list_lock);
1233
1234 return ret;
1235}
1212EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain); 1236EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain);
1213 1237
1214/** 1238/**
@@ -1571,12 +1595,22 @@ static struct generic_pm_domain *genpd_get_from_provider(
1571int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev) 1595int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev)
1572{ 1596{
1573 struct generic_pm_domain *genpd; 1597 struct generic_pm_domain *genpd;
1598 int ret;
1599
1600 mutex_lock(&gpd_list_lock);
1574 1601
1575 genpd = genpd_get_from_provider(genpdspec); 1602 genpd = genpd_get_from_provider(genpdspec);
1576 if (IS_ERR(genpd)) 1603 if (IS_ERR(genpd)) {
1577 return PTR_ERR(genpd); 1604 ret = PTR_ERR(genpd);
1605 goto out;
1606 }
1607
1608 ret = genpd_add_device(genpd, dev, NULL);
1578 1609
1579 return pm_genpd_add_device(genpd, dev); 1610out:
1611 mutex_unlock(&gpd_list_lock);
1612
1613 return ret;
1580} 1614}
1581EXPORT_SYMBOL_GPL(of_genpd_add_device); 1615EXPORT_SYMBOL_GPL(of_genpd_add_device);
1582 1616
@@ -1593,16 +1627,28 @@ int of_genpd_add_subdomain(struct of_phandle_args *parent_spec,
1593 struct of_phandle_args *subdomain_spec) 1627 struct of_phandle_args *subdomain_spec)
1594{ 1628{
1595 struct generic_pm_domain *parent, *subdomain; 1629 struct generic_pm_domain *parent, *subdomain;
1630 int ret;
1631
1632 mutex_lock(&gpd_list_lock);
1596 1633
1597 parent = genpd_get_from_provider(parent_spec); 1634 parent = genpd_get_from_provider(parent_spec);
1598 if (IS_ERR(parent)) 1635 if (IS_ERR(parent)) {
1599 return PTR_ERR(parent); 1636 ret = PTR_ERR(parent);
1637 goto out;
1638 }
1600 1639
1601 subdomain = genpd_get_from_provider(subdomain_spec); 1640 subdomain = genpd_get_from_provider(subdomain_spec);
1602 if (IS_ERR(subdomain)) 1641 if (IS_ERR(subdomain)) {
1603 return PTR_ERR(subdomain); 1642 ret = PTR_ERR(subdomain);
1643 goto out;
1644 }
1645
1646 ret = genpd_add_subdomain(parent, subdomain);
1604 1647
1605 return pm_genpd_add_subdomain(parent, subdomain); 1648out:
1649 mutex_unlock(&gpd_list_lock);
1650
1651 return ret;
1606} 1652}
1607EXPORT_SYMBOL_GPL(of_genpd_add_subdomain); 1653EXPORT_SYMBOL_GPL(of_genpd_add_subdomain);
1608 1654
@@ -1701,9 +1747,11 @@ int genpd_dev_pm_attach(struct device *dev)
1701 return -ENOENT; 1747 return -ENOENT;
1702 } 1748 }
1703 1749
1750 mutex_lock(&gpd_list_lock);
1704 pd = genpd_get_from_provider(&pd_args); 1751 pd = genpd_get_from_provider(&pd_args);
1705 of_node_put(pd_args.np); 1752 of_node_put(pd_args.np);
1706 if (IS_ERR(pd)) { 1753 if (IS_ERR(pd)) {
1754 mutex_unlock(&gpd_list_lock);
1707 dev_dbg(dev, "%s() failed to find PM domain: %ld\n", 1755 dev_dbg(dev, "%s() failed to find PM domain: %ld\n",
1708 __func__, PTR_ERR(pd)); 1756 __func__, PTR_ERR(pd));
1709 return -EPROBE_DEFER; 1757 return -EPROBE_DEFER;
@@ -1712,13 +1760,14 @@ int genpd_dev_pm_attach(struct device *dev)
1712 dev_dbg(dev, "adding to PM domain %s\n", pd->name); 1760 dev_dbg(dev, "adding to PM domain %s\n", pd->name);
1713 1761
1714 for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 1762 for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) {
1715 ret = pm_genpd_add_device(pd, dev); 1763 ret = genpd_add_device(pd, dev, NULL);
1716 if (ret != -EAGAIN) 1764 if (ret != -EAGAIN)
1717 break; 1765 break;
1718 1766
1719 mdelay(i); 1767 mdelay(i);
1720 cond_resched(); 1768 cond_resched();
1721 } 1769 }
1770 mutex_unlock(&gpd_list_lock);
1722 1771
1723 if (ret < 0) { 1772 if (ret < 0) {
1724 dev_err(dev, "failed to add to PM domain %s: %d", 1773 dev_err(dev, "failed to add to PM domain %s: %d",