diff options
| author | Frank Rowand <frank.rowand@sony.com> | 2018-12-18 14:40:03 -0500 |
|---|---|---|
| committer | Rob Herring <robh@kernel.org> | 2018-12-21 13:42:36 -0500 |
| commit | 5801169a2ed20003f771acecf3ac00574cf10a38 (patch) | |
| tree | 56bcdc99f2d669033eea3c4308e52419a44ddc7e /drivers/of | |
| parent | b8a9ac1a5b99a2fcbed19fd29d2d59270c281a31 (diff) | |
of: __of_detach_node() - remove node from phandle cache
Non-overlay dynamic devicetree node removal may leave the node in
the phandle cache. Subsequent calls to of_find_node_by_phandle()
will incorrectly find the stale entry. Remove the node from the
cache.
Add paranoia checks in of_find_node_by_phandle() as a second level
of defense (do not return cached node if detached, do not add node
to cache if detached).
Fixes: 0b3ce78e90fc ("of: cache phandle nodes to reduce cost of of_find_node_by_phandle()")
Reported-by: Michael Bringmann <mwb@linux.vnet.ibm.com>
Cc: stable@vger.kernel.org # v4.17+
Signed-off-by: Frank Rowand <frank.rowand@sony.com>
Signed-off-by: Rob Herring <robh@kernel.org>
Diffstat (limited to 'drivers/of')
| -rw-r--r-- | drivers/of/base.c | 31 | ||||
| -rw-r--r-- | drivers/of/dynamic.c | 3 | ||||
| -rw-r--r-- | drivers/of/of_private.h | 4 |
3 files changed, 37 insertions, 1 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c index 4da1c688995b..5226e898476e 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c | |||
| @@ -169,6 +169,28 @@ int of_free_phandle_cache(void) | |||
| 169 | late_initcall_sync(of_free_phandle_cache); | 169 | late_initcall_sync(of_free_phandle_cache); |
| 170 | #endif | 170 | #endif |
| 171 | 171 | ||
| 172 | /* | ||
| 173 | * Caller must hold devtree_lock. | ||
| 174 | */ | ||
| 175 | void __of_free_phandle_cache_entry(phandle handle) | ||
| 176 | { | ||
| 177 | phandle masked_handle; | ||
| 178 | struct device_node *np; | ||
| 179 | |||
| 180 | if (!handle) | ||
| 181 | return; | ||
| 182 | |||
| 183 | masked_handle = handle & phandle_cache_mask; | ||
| 184 | |||
| 185 | if (phandle_cache) { | ||
| 186 | np = phandle_cache[masked_handle]; | ||
| 187 | if (np && handle == np->phandle) { | ||
| 188 | of_node_put(np); | ||
| 189 | phandle_cache[masked_handle] = NULL; | ||
| 190 | } | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 172 | void of_populate_phandle_cache(void) | 194 | void of_populate_phandle_cache(void) |
| 173 | { | 195 | { |
| 174 | unsigned long flags; | 196 | unsigned long flags; |
| @@ -1214,11 +1236,18 @@ struct device_node *of_find_node_by_phandle(phandle handle) | |||
| 1214 | if (phandle_cache[masked_handle] && | 1236 | if (phandle_cache[masked_handle] && |
| 1215 | handle == phandle_cache[masked_handle]->phandle) | 1237 | handle == phandle_cache[masked_handle]->phandle) |
| 1216 | np = phandle_cache[masked_handle]; | 1238 | np = phandle_cache[masked_handle]; |
| 1239 | if (np && of_node_check_flag(np, OF_DETACHED)) { | ||
| 1240 | WARN_ON(1); /* did not uncache np on node removal */ | ||
| 1241 | of_node_put(np); | ||
| 1242 | phandle_cache[masked_handle] = NULL; | ||
| 1243 | np = NULL; | ||
| 1244 | } | ||
| 1217 | } | 1245 | } |
| 1218 | 1246 | ||
| 1219 | if (!np) { | 1247 | if (!np) { |
| 1220 | for_each_of_allnodes(np) | 1248 | for_each_of_allnodes(np) |
| 1221 | if (np->phandle == handle) { | 1249 | if (np->phandle == handle && |
| 1250 | !of_node_check_flag(np, OF_DETACHED)) { | ||
| 1222 | if (phandle_cache) { | 1251 | if (phandle_cache) { |
| 1223 | /* will put when removed from cache */ | 1252 | /* will put when removed from cache */ |
| 1224 | of_node_get(np); | 1253 | of_node_get(np); |
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index b4e5b90cb314..a09c1c3cf831 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c | |||
| @@ -277,6 +277,9 @@ void __of_detach_node(struct device_node *np) | |||
| 277 | } | 277 | } |
| 278 | 278 | ||
| 279 | of_node_set_flag(np, OF_DETACHED); | 279 | of_node_set_flag(np, OF_DETACHED); |
| 280 | |||
| 281 | /* race with of_find_node_by_phandle() prevented by devtree_lock */ | ||
| 282 | __of_free_phandle_cache_entry(np->phandle); | ||
| 280 | } | 283 | } |
| 281 | 284 | ||
| 282 | /** | 285 | /** |
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 5d1567025358..24786818e32e 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h | |||
| @@ -84,6 +84,10 @@ static inline void __of_detach_node_sysfs(struct device_node *np) {} | |||
| 84 | int of_resolve_phandles(struct device_node *tree); | 84 | int of_resolve_phandles(struct device_node *tree); |
| 85 | #endif | 85 | #endif |
| 86 | 86 | ||
| 87 | #if defined(CONFIG_OF_DYNAMIC) | ||
| 88 | void __of_free_phandle_cache_entry(phandle handle); | ||
| 89 | #endif | ||
| 90 | |||
| 87 | #if defined(CONFIG_OF_OVERLAY) | 91 | #if defined(CONFIG_OF_OVERLAY) |
| 88 | void of_overlay_mutex_lock(void); | 92 | void of_overlay_mutex_lock(void); |
| 89 | void of_overlay_mutex_unlock(void); | 93 | void of_overlay_mutex_unlock(void); |
