summaryrefslogtreecommitdiffstats
path: root/drivers/of/overlay.c
diff options
context:
space:
mode:
authorFrank Rowand <frank.rowand@sony.com>2017-10-17 16:36:29 -0700
committerRob Herring <robh@kernel.org>2017-10-17 20:47:27 -0500
commitf948d6d8b792bb90041edc12eac35faf83030994 (patch)
treef98bad2bb9e0701c3fc198e72575216e442006b5 /drivers/of/overlay.c
parent6d0f5470dbdeb7f9b1e20fc9409bf07fab1b5ac5 (diff)
downloadlinux-f948d6d8b792bb90041edc12eac35faf83030994.tar.bz2
of: overlay: avoid race condition between applying multiple overlays
The process of applying an overlay consists of: - unflatten an overlay FDT (flattened device tree) into an EDT (expanded device tree) - fixup the phandle values in the overlay EDT to fit in a range above the phandle values in the live device tree - create the overlay changeset to reflect the contents of the overlay EDT - apply the overlay changeset, to modify the live device tree, potentially changing the maximum phandle value in the live device tree There is currently no protection against two overlay applies concurrently determining what range of phandle values are in use in the live device tree, and subsequently changing that range. Add a mutex to prevent multiple overlay applies from occurring simultaneously. Move of_resolve_phandles() into of_overlay_apply() so that it does not have to be duplicated by each caller of of_overlay_apply(). The test in of_resolve_phandles() that the overlay tree is detached is temporarily disabled so that old style overlay unittests do not fail. Signed-off-by: Frank Rowand <frank.rowand@sony.com> Signed-off-by: Rob Herring <robh@kernel.org>
Diffstat (limited to 'drivers/of/overlay.c')
-rw-r--r--drivers/of/overlay.c32
1 files changed, 32 insertions, 0 deletions
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 791753321ed2..d164f86e5541 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -71,6 +71,28 @@ static int build_changeset_next_level(struct overlay_changeset *ovcs,
const struct device_node *overlay_node,
bool is_symbols_node);
+/*
+ * of_resolve_phandles() finds the largest phandle in the live tree.
+ * of_overlay_apply() may add a larger phandle to the live tree.
+ * Do not allow race between two overlays being applied simultaneously:
+ * mutex_lock(&of_overlay_phandle_mutex)
+ * of_resolve_phandles()
+ * of_overlay_apply()
+ * mutex_unlock(&of_overlay_phandle_mutex)
+ */
+static DEFINE_MUTEX(of_overlay_phandle_mutex);
+
+void of_overlay_mutex_lock(void)
+{
+ mutex_lock(&of_overlay_phandle_mutex);
+}
+
+void of_overlay_mutex_unlock(void)
+{
+ mutex_unlock(&of_overlay_phandle_mutex);
+}
+
+
static LIST_HEAD(ovcs_list);
static DEFINE_IDR(ovcs_idr);
@@ -624,6 +646,12 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
goto out;
}
+ of_overlay_mutex_lock();
+
+ ret = of_resolve_phandles(tree);
+ if (ret)
+ goto err_overlay_unlock;
+
mutex_lock(&of_mutex);
ret = init_overlay_changeset(ovcs, tree);
@@ -669,9 +697,13 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
}
mutex_unlock(&of_mutex);
+ of_overlay_mutex_unlock();
goto out;
+err_overlay_unlock:
+ of_overlay_mutex_unlock();
+
err_free_overlay_changeset:
free_overlay_changeset(ovcs);