of: __of_detach_node() - remove node from phandle cache
commit 5801169a2ed20003f771acecf3ac00574cf10a38 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: 0b3ce78e90
("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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
1cb5a0333a
commit
8dbea64423
3 changed files with 37 additions and 1 deletions
|
@ -161,6 +161,28 @@ int of_free_phandle_cache(void)
|
|||
late_initcall_sync(of_free_phandle_cache);
|
||||
#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)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
@ -1169,11 +1191,18 @@ struct device_node *of_find_node_by_phandle(phandle handle)
|
|||
if (phandle_cache[masked_handle] &&
|
||||
handle == phandle_cache[masked_handle]->phandle)
|
||||
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) {
|
||||
for_each_of_allnodes(np)
|
||||
if (np->phandle == handle) {
|
||||
if (np->phandle == handle &&
|
||||
!of_node_check_flag(np, OF_DETACHED)) {
|
||||
if (phandle_cache) {
|
||||
/* will put when removed from cache */
|
||||
of_node_get(np);
|
||||
|
|
|
@ -268,6 +268,9 @@ void __of_detach_node(struct device_node *np)
|
|||
}
|
||||
|
||||
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) {}
|
|||
int of_resolve_phandles(struct device_node *tree);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OF_DYNAMIC)
|
||||
void __of_free_phandle_cache_entry(phandle handle);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OF_OVERLAY)
|
||||
void of_overlay_mutex_lock(void);
|
||||
void of_overlay_mutex_unlock(void);
|
||||
|
|
Loading…
Reference in a new issue