Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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: 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
Loading
Loading
Loading
Loading
+30 −1
Original line number Diff line number Diff line
@@ -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);
+3 −0
Original line number Diff line number Diff line
@@ -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);
}

/**
+4 −0
Original line number Diff line number Diff line
@@ -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);