Commit 8dbea644 authored by Frank Rowand's avatar Frank Rowand Committed by Greg Kroah-Hartman

of: __of_detach_node() - remove node from phandle cache

commit 5801169a upstream.

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: 0b3ce78e ("of: cache phandle nodes to reduce cost of of_find_node_by_phandle()")
Reported-by: default avatarMichael Bringmann <mwb@linux.vnet.ibm.com>
Cc: stable@vger.kernel.org # v4.17+
Signed-off-by: default avatarFrank Rowand <frank.rowand@sony.com>
Signed-off-by: default avatarRob Herring <robh@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 1cb5a033
...@@ -161,6 +161,28 @@ int of_free_phandle_cache(void) ...@@ -161,6 +161,28 @@ int of_free_phandle_cache(void)
late_initcall_sync(of_free_phandle_cache); late_initcall_sync(of_free_phandle_cache);
#endif #endif
/*
* Caller must hold devtree_lock.
*/
void __of_free_phandle_cache_entry(phandle handle)
{
phandle masked_handle;
struct device_node *np;
if (!handle)
return;
masked_handle = handle & phandle_cache_mask;
if (phandle_cache) {
np = phandle_cache[masked_handle];
if (np && handle == np->phandle) {
of_node_put(np);
phandle_cache[masked_handle] = NULL;
}
}
}
void of_populate_phandle_cache(void) void of_populate_phandle_cache(void)
{ {
unsigned long flags; unsigned long flags;
...@@ -1169,11 +1191,18 @@ struct device_node *of_find_node_by_phandle(phandle handle) ...@@ -1169,11 +1191,18 @@ struct device_node *of_find_node_by_phandle(phandle handle)
if (phandle_cache[masked_handle] && if (phandle_cache[masked_handle] &&
handle == phandle_cache[masked_handle]->phandle) handle == phandle_cache[masked_handle]->phandle)
np = phandle_cache[masked_handle]; np = phandle_cache[masked_handle];
if (np && of_node_check_flag(np, OF_DETACHED)) {
WARN_ON(1); /* did not uncache np on node removal */
of_node_put(np);
phandle_cache[masked_handle] = NULL;
np = NULL;
}
} }
if (!np) { if (!np) {
for_each_of_allnodes(np) for_each_of_allnodes(np)
if (np->phandle == handle) { if (np->phandle == handle &&
!of_node_check_flag(np, OF_DETACHED)) {
if (phandle_cache) { if (phandle_cache) {
/* will put when removed from cache */ /* will put when removed from cache */
of_node_get(np); of_node_get(np);
......
...@@ -268,6 +268,9 @@ void __of_detach_node(struct device_node *np) ...@@ -268,6 +268,9 @@ void __of_detach_node(struct device_node *np)
} }
of_node_set_flag(np, OF_DETACHED); of_node_set_flag(np, OF_DETACHED);
/* race with of_find_node_by_phandle() prevented by devtree_lock */
__of_free_phandle_cache_entry(np->phandle);
} }
/** /**
......
...@@ -76,6 +76,10 @@ static inline void __of_detach_node_sysfs(struct device_node *np) {} ...@@ -76,6 +76,10 @@ static inline void __of_detach_node_sysfs(struct device_node *np) {}
int of_resolve_phandles(struct device_node *tree); int of_resolve_phandles(struct device_node *tree);
#endif #endif
#if defined(CONFIG_OF_DYNAMIC)
void __of_free_phandle_cache_entry(phandle handle);
#endif
#if defined(CONFIG_OF_OVERLAY) #if defined(CONFIG_OF_OVERLAY)
void of_overlay_mutex_lock(void); void of_overlay_mutex_lock(void);
void of_overlay_mutex_unlock(void); void of_overlay_mutex_unlock(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment