of: overlay: check prevents multiple fragments touching same property
Add test case of two fragments updating the same property. After adding the test case, the system hangs at end of boot, after after slub stack dumps from kfree() in crypto modprobe code. Multiple overlay fragments adding, modifying, or deleting the same property is not supported. Add check to detect the attempt and fail the overlay apply. Before this patch, the first fragment error would terminate processing. Allow fragment checking to proceed and report all of the fragment errors before terminating the overlay apply. This is not a hot path, thus not a performance issue (the error is not transient and requires fixing the overlay before attempting to apply it again). After applying this patch, the devicetree unittest messages will include: OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/rpm_avail ... ### dt-test ### end of unittest - 212 passed, 0 failed The check to detect two fragments updating the same property is folded into the patch that created the test case to maintain bisectability. Tested-by: Alan Tull <atull@kernel.org> Signed-off-by: Frank Rowand <frank.rowand@sony.com>
This commit is contained in:
parent
c168263b5a
commit
2fe0e8769d
@ -508,54 +508,98 @@ static int build_changeset_symbols_node(struct overlay_changeset *ovcs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* check_changeset_dup_add_node() - changeset validation: duplicate add node
|
||||
* @ovcs: Overlay changeset
|
||||
*
|
||||
* Check changeset @ovcs->cset for multiple add node entries for the same
|
||||
* node.
|
||||
*
|
||||
* Returns 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if
|
||||
* invalid overlay in @ovcs->fragments[].
|
||||
*/
|
||||
static int check_changeset_dup_add_node(struct overlay_changeset *ovcs)
|
||||
static int find_dup_cset_node_entry(struct overlay_changeset *ovcs,
|
||||
struct of_changeset_entry *ce_1)
|
||||
{
|
||||
struct of_changeset_entry *ce_1, *ce_2;
|
||||
struct of_changeset_entry *ce_2;
|
||||
char *fn_1, *fn_2;
|
||||
int name_match;
|
||||
int node_path_match;
|
||||
|
||||
list_for_each_entry(ce_1, &ovcs->cset.entries, node) {
|
||||
if (ce_1->action != OF_RECONFIG_ATTACH_NODE &&
|
||||
ce_1->action != OF_RECONFIG_DETACH_NODE)
|
||||
return 0;
|
||||
|
||||
if (ce_1->action == OF_RECONFIG_ATTACH_NODE ||
|
||||
ce_1->action == OF_RECONFIG_DETACH_NODE) {
|
||||
ce_2 = ce_1;
|
||||
list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) {
|
||||
if ((ce_2->action != OF_RECONFIG_ATTACH_NODE &&
|
||||
ce_2->action != OF_RECONFIG_DETACH_NODE) ||
|
||||
of_node_cmp(ce_1->np->full_name, ce_2->np->full_name))
|
||||
continue;
|
||||
|
||||
ce_2 = ce_1;
|
||||
list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) {
|
||||
if (ce_2->action == OF_RECONFIG_ATTACH_NODE ||
|
||||
ce_2->action == OF_RECONFIG_DETACH_NODE) {
|
||||
/* inexpensive name compare */
|
||||
if (!of_node_cmp(ce_1->np->full_name,
|
||||
ce_2->np->full_name)) {
|
||||
/* expensive full path name compare */
|
||||
fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np);
|
||||
fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np);
|
||||
name_match = !strcmp(fn_1, fn_2);
|
||||
kfree(fn_1);
|
||||
kfree(fn_2);
|
||||
if (name_match) {
|
||||
pr_err("ERROR: multiple overlay fragments add and/or delete node %pOF\n",
|
||||
ce_1->np);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np);
|
||||
fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np);
|
||||
node_path_match = !strcmp(fn_1, fn_2);
|
||||
kfree(fn_1);
|
||||
kfree(fn_2);
|
||||
if (node_path_match) {
|
||||
pr_err("ERROR: multiple fragments add and/or delete node %pOF\n",
|
||||
ce_1->np);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_dup_cset_prop(struct overlay_changeset *ovcs,
|
||||
struct of_changeset_entry *ce_1)
|
||||
{
|
||||
struct of_changeset_entry *ce_2;
|
||||
char *fn_1, *fn_2;
|
||||
int node_path_match;
|
||||
|
||||
if (ce_1->action != OF_RECONFIG_ADD_PROPERTY &&
|
||||
ce_1->action != OF_RECONFIG_REMOVE_PROPERTY &&
|
||||
ce_1->action != OF_RECONFIG_UPDATE_PROPERTY)
|
||||
return 0;
|
||||
|
||||
ce_2 = ce_1;
|
||||
list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) {
|
||||
if ((ce_2->action != OF_RECONFIG_ADD_PROPERTY &&
|
||||
ce_2->action != OF_RECONFIG_REMOVE_PROPERTY &&
|
||||
ce_2->action != OF_RECONFIG_UPDATE_PROPERTY) ||
|
||||
of_node_cmp(ce_1->np->full_name, ce_2->np->full_name))
|
||||
continue;
|
||||
|
||||
fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np);
|
||||
fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np);
|
||||
node_path_match = !strcmp(fn_1, fn_2);
|
||||
kfree(fn_1);
|
||||
kfree(fn_2);
|
||||
if (node_path_match &&
|
||||
!of_prop_cmp(ce_1->prop->name, ce_2->prop->name)) {
|
||||
pr_err("ERROR: multiple fragments add, update, and/or delete property %pOF/%s\n",
|
||||
ce_1->np, ce_1->prop->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* changeset_dup_entry_check() - check for duplicate entries
|
||||
* @ovcs: Overlay changeset
|
||||
*
|
||||
* Check changeset @ovcs->cset for multiple {add or delete} node entries for
|
||||
* the same node or duplicate {add, delete, or update} properties entries
|
||||
* for the same property.
|
||||
*
|
||||
* Returns 0 on success, or -EINVAL if duplicate changeset entry found.
|
||||
*/
|
||||
static int changeset_dup_entry_check(struct overlay_changeset *ovcs)
|
||||
{
|
||||
struct of_changeset_entry *ce_1;
|
||||
int dup_entry = 0;
|
||||
|
||||
list_for_each_entry(ce_1, &ovcs->cset.entries, node) {
|
||||
dup_entry |= find_dup_cset_node_entry(ovcs, ce_1);
|
||||
dup_entry |= find_dup_cset_prop(ovcs, ce_1);
|
||||
}
|
||||
|
||||
return dup_entry ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* build_changeset() - populate overlay changeset in @ovcs from @ovcs->fragments
|
||||
* @ovcs: Overlay changeset
|
||||
@ -611,7 +655,7 @@ static int build_changeset(struct overlay_changeset *ovcs)
|
||||
}
|
||||
}
|
||||
|
||||
return check_changeset_dup_add_node(ovcs);
|
||||
return changeset_dup_entry_check(ovcs);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -18,6 +18,7 @@ obj-$(CONFIG_OF_OVERLAY) += overlay.dtb.o \
|
||||
overlay_13.dtb.o \
|
||||
overlay_15.dtb.o \
|
||||
overlay_bad_add_dup_node.dtb.o \
|
||||
overlay_bad_add_dup_prop.dtb.o \
|
||||
overlay_bad_phandle.dtb.o \
|
||||
overlay_bad_symbol.dtb.o \
|
||||
overlay_base.dtb.o
|
||||
|
24
drivers/of/unittest-data/overlay_bad_add_dup_prop.dts
Normal file
24
drivers/of/unittest-data/overlay_bad_add_dup_prop.dts
Normal file
@ -0,0 +1,24 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
/*
|
||||
* &electric_1/motor-1 and &spin_ctrl_1 are the same node:
|
||||
* /testcase-data-2/substation@100/motor-1
|
||||
*
|
||||
* Thus the property "rpm_avail" in each fragment will
|
||||
* result in an attempt to update the same property twice.
|
||||
* This will result in an error and the overlay apply
|
||||
* will fail.
|
||||
*/
|
||||
|
||||
&electric_1 {
|
||||
|
||||
motor-1 {
|
||||
rpm_avail = < 100 >;
|
||||
};
|
||||
};
|
||||
|
||||
&spin_ctrl_1 {
|
||||
rpm_avail = < 100 200 >;
|
||||
};
|
@ -30,6 +30,7 @@
|
||||
spin_ctrl_1: motor-1 {
|
||||
compatible = "ot,ferris-wheel-motor";
|
||||
spin = "clockwise";
|
||||
rpm_avail = < 50 >;
|
||||
};
|
||||
|
||||
spin_ctrl_2: motor-8 {
|
||||
|
@ -2162,6 +2162,7 @@ OVERLAY_INFO_EXTERN(overlay_12);
|
||||
OVERLAY_INFO_EXTERN(overlay_13);
|
||||
OVERLAY_INFO_EXTERN(overlay_15);
|
||||
OVERLAY_INFO_EXTERN(overlay_bad_add_dup_node);
|
||||
OVERLAY_INFO_EXTERN(overlay_bad_add_dup_prop);
|
||||
OVERLAY_INFO_EXTERN(overlay_bad_phandle);
|
||||
OVERLAY_INFO_EXTERN(overlay_bad_symbol);
|
||||
|
||||
@ -2185,6 +2186,7 @@ static struct overlay_info overlays[] = {
|
||||
OVERLAY_INFO(overlay_13, 0),
|
||||
OVERLAY_INFO(overlay_15, 0),
|
||||
OVERLAY_INFO(overlay_bad_add_dup_node, -EINVAL),
|
||||
OVERLAY_INFO(overlay_bad_add_dup_prop, -EINVAL),
|
||||
OVERLAY_INFO(overlay_bad_phandle, -EINVAL),
|
||||
OVERLAY_INFO(overlay_bad_symbol, -EINVAL),
|
||||
{}
|
||||
@ -2435,6 +2437,9 @@ static __init void of_unittest_overlay_high_level(void)
|
||||
unittest(overlay_data_apply("overlay_bad_add_dup_node", NULL),
|
||||
"Adding overlay 'overlay_bad_add_dup_node' failed\n");
|
||||
|
||||
unittest(overlay_data_apply("overlay_bad_add_dup_prop", NULL),
|
||||
"Adding overlay 'overlay_bad_add_dup_prop' failed\n");
|
||||
|
||||
unittest(overlay_data_apply("overlay_bad_phandle", NULL),
|
||||
"Adding overlay 'overlay_bad_phandle' failed\n");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user