of: overlay: unittest: add tests for overlay notifiers

Add tests for overlay apply and remove notifiers.  Trigger errors
for each of the notifier actions.

These tests will reveal a memory leak problem when a notifier returns
an error for action OF_OVERLAY_POST_APPLY.  The pr_err() message is:

   OF: ERROR: memory leak, expected refcount 1 instead of 3,
   of_node_get()/of_node_put() unbalanced - destroy cset entry: attach
   overlay node /testcase-data/overlay-node/test-bus/test-unittest17

Signed-off-by: Frank Rowand <frank.rowand@sony.com>
Signed-off-by: Rob Herring <robh@kernel.org>
Link: https://lore.kernel.org/r/20220502181742.1402826-3-frowand.list@gmail.com
This commit is contained in:
Frank Rowand 2022-05-02 13:17:41 -05:00 committed by Rob Herring
parent 1ac17586c9
commit 992b0dc5c3
7 changed files with 280 additions and 0 deletions

View File

@ -17,6 +17,11 @@ obj-$(CONFIG_OF_OVERLAY) += overlay.dtb.o \
overlay_12.dtb.o \
overlay_13.dtb.o \
overlay_15.dtb.o \
overlay_16.dtb.o \
overlay_17.dtb.o \
overlay_18.dtb.o \
overlay_19.dtb.o \
overlay_20.dtb.o \
overlay_bad_add_dup_node.dtb.o \
overlay_bad_add_dup_prop.dtb.o \
overlay_bad_phandle.dtb.o \
@ -75,6 +80,11 @@ apply_static_overlay_1 := overlay_0.dtbo \
overlay_12.dtbo \
overlay_13.dtbo \
overlay_15.dtbo \
overlay_16.dtbo \
overlay_17.dtbo \
overlay_18.dtbo \
overlay_19.dtbo \
overlay_20.dtbo \
overlay_gpio_01.dtbo \
overlay_gpio_02a.dtbo \
overlay_gpio_02b.dtbo \

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_16 - notify test */
&unittest_test_bus {
#address-cells = <1>;
#size-cells = <0>;
test-unittest16 {
compatible = "unittest";
reg = <16>;
};
};

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_17 - notify test */
&unittest_test_bus {
#address-cells = <1>;
#size-cells = <0>;
test-unittest17 {
compatible = "unittest";
reg = <17>;
};
};

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_18 - notify test */
&unittest_test_bus {
#address-cells = <1>;
#size-cells = <0>;
test-unittest18 {
compatible = "unittest";
reg = <18>;
};
};

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_19 - notify test */
&unittest_test_bus {
#address-cells = <1>;
#size-cells = <0>;
test-unittest19 {
compatible = "unittest";
reg = <19>;
};
};

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_20 - notify test */
&unittest_test_bus {
#address-cells = <1>;
#size-cells = <0>;
test-unittest20 {
compatible = "unittest";
reg = <20>;
};
};

View File

@ -2741,6 +2741,189 @@ static inline void of_unittest_overlay_i2c_15(void) { }
#endif
static int of_notify(struct notifier_block *nb, unsigned long action,
void *arg)
{
struct of_overlay_notify_data *nd = arg;
struct device_node *found;
int ret;
/*
* For overlay_16 .. overlay_19, check that returning an error
* works for each of the actions by setting an arbitrary return
* error number that matches the test number. e.g. for unittest16,
* ret = -EBUSY which is -16.
*
* OVERLAY_INFO() for the overlays is declared to expect the same
* error number, so overlay_data_apply() will return no error.
*
* overlay_20 will return NOTIFY_DONE
*/
ret = 0;
of_node_get(nd->overlay);
switch (action) {
case OF_OVERLAY_PRE_APPLY:
found = of_find_node_by_name(nd->overlay, "test-unittest16");
if (found) {
of_node_put(found);
ret = -EBUSY;
}
break;
case OF_OVERLAY_POST_APPLY:
found = of_find_node_by_name(nd->overlay, "test-unittest17");
if (found) {
of_node_put(found);
ret = -EEXIST;
}
break;
case OF_OVERLAY_PRE_REMOVE:
found = of_find_node_by_name(nd->overlay, "test-unittest18");
if (found) {
of_node_put(found);
ret = -EXDEV;
}
break;
case OF_OVERLAY_POST_REMOVE:
found = of_find_node_by_name(nd->overlay, "test-unittest19");
if (found) {
of_node_put(found);
ret = -ENODEV;
}
break;
default: /* should not happen */
of_node_put(nd->overlay);
ret = -EINVAL;
break;
}
if (ret)
return notifier_from_errno(ret);
return NOTIFY_DONE;
}
static struct notifier_block of_nb = {
.notifier_call = of_notify,
};
static void __init of_unittest_overlay_notify(void)
{
int ovcs_id;
int ret;
ret = of_overlay_notifier_register(&of_nb);
unittest(!ret,
"of_overlay_notifier_register() failed, ret = %d\n", ret);
if (ret)
return;
/*
* The overlays are applied by overlay_data_apply()
* instead of of_unittest_apply_overlay() so that they
* will not be tracked. Thus they will not be removed
* by of_unittest_remove_tracked_overlays().
*
* Applying overlays 16 - 19 will each trigger an error for a
* different action in of_notify().
*
* Applying overlay 20 will not trigger any error in of_notify().
*/
/* --- overlay 16 --- */
EXPECT_BEGIN(KERN_INFO, "OF: overlay: overlay changeset pre-apply notifier error -16, target: /testcase-data/overlay-node/test-bus");
unittest(overlay_data_apply("overlay_16", &ovcs_id),
"test OF_OVERLAY_PRE_APPLY notify injected error\n");
EXPECT_END(KERN_INFO, "OF: overlay: overlay changeset pre-apply notifier error -16, target: /testcase-data/overlay-node/test-bus");
unittest(!ovcs_id, "ovcs_id created for overlay_16\n");
/* --- overlay 17 --- */
EXPECT_BEGIN(KERN_INFO, "OF: overlay: overlay changeset post-apply notifier error -17, target: /testcase-data/overlay-node/test-bus");
unittest(overlay_data_apply("overlay_17", &ovcs_id),
"test OF_OVERLAY_POST_APPLY notify injected error\n");
EXPECT_END(KERN_INFO, "OF: overlay: overlay changeset post-apply notifier error -17, target: /testcase-data/overlay-node/test-bus");
unittest(!ovcs_id, "ovcs_id created for overlay_17\n");
/* --- overlay 18 --- */
unittest(overlay_data_apply("overlay_18", &ovcs_id),
"OF_OVERLAY_PRE_REMOVE notify injected error\n");
unittest(ovcs_id, "ovcs_id not created for overlay_18\n");
if (ovcs_id) {
EXPECT_BEGIN(KERN_INFO, "OF: overlay: overlay changeset pre-remove notifier error -18, target: /testcase-data/overlay-node/test-bus");
ret = of_overlay_remove(&ovcs_id);
EXPECT_END(KERN_INFO, "OF: overlay: overlay changeset pre-remove notifier error -18, target: /testcase-data/overlay-node/test-bus");
if (ret == -EXDEV) {
/*
* change set ovcs_id should still exist
*/
unittest(1, "overlay_18 of_overlay_remove() injected error for OF_OVERLAY_PRE_REMOVE\n");
} else {
unittest(0, "overlay_18 of_overlay_remove() injected error for OF_OVERLAY_PRE_REMOVE not returned\n");
}
} else {
unittest(1, "ovcs_id not created for overlay_18\n");
}
unittest(ovcs_id, "ovcs_id removed for overlay_18\n");
/* --- overlay 19 --- */
unittest(overlay_data_apply("overlay_19", &ovcs_id),
"OF_OVERLAY_POST_REMOVE notify injected error\n");
unittest(ovcs_id, "ovcs_id not created for overlay_19\n");
if (ovcs_id) {
EXPECT_BEGIN(KERN_INFO, "OF: overlay: overlay changeset post-remove notifier error -19, target: /testcase-data/overlay-node/test-bus");
ret = of_overlay_remove(&ovcs_id);
EXPECT_END(KERN_INFO, "OF: overlay: overlay changeset post-remove notifier error -19, target: /testcase-data/overlay-node/test-bus");
if (ret == -ENODEV)
unittest(1, "overlay_19 of_overlay_remove() injected error for OF_OVERLAY_POST_REMOVE\n");
else
unittest(0, "overlay_19 of_overlay_remove() injected error for OF_OVERLAY_POST_REMOVE not returned\n");
} else {
unittest(1, "ovcs_id removed for overlay_19\n");
}
unittest(!ovcs_id, "changeset ovcs_id = %d not removed for overlay_19\n",
ovcs_id);
/* --- overlay 20 --- */
unittest(overlay_data_apply("overlay_20", &ovcs_id),
"overlay notify no injected error\n");
if (ovcs_id) {
ret = of_overlay_remove(&ovcs_id);
if (ret)
unittest(1, "overlay_20 failed to be destroyed, ret = %d\n",
ret);
} else {
unittest(1, "ovcs_id not created for overlay_20\n");
}
unittest(!of_overlay_notifier_unregister(&of_nb),
"of_overlay_notifier_unregister() failed, ret = %d\n", ret);
}
static void __init of_unittest_overlay(void)
{
struct device_node *bus_np = NULL;
@ -2804,6 +2987,8 @@ static void __init of_unittest_overlay(void)
of_unittest_remove_tracked_overlays();
of_unittest_overlay_notify();
out:
of_node_put(bus_np);
}
@ -2855,6 +3040,11 @@ OVERLAY_INFO_EXTERN(overlay_11);
OVERLAY_INFO_EXTERN(overlay_12);
OVERLAY_INFO_EXTERN(overlay_13);
OVERLAY_INFO_EXTERN(overlay_15);
OVERLAY_INFO_EXTERN(overlay_16);
OVERLAY_INFO_EXTERN(overlay_17);
OVERLAY_INFO_EXTERN(overlay_18);
OVERLAY_INFO_EXTERN(overlay_19);
OVERLAY_INFO_EXTERN(overlay_20);
OVERLAY_INFO_EXTERN(overlay_gpio_01);
OVERLAY_INFO_EXTERN(overlay_gpio_02a);
OVERLAY_INFO_EXTERN(overlay_gpio_02b);
@ -2885,6 +3075,11 @@ static struct overlay_info overlays[] = {
OVERLAY_INFO(overlay_12, 0),
OVERLAY_INFO(overlay_13, 0),
OVERLAY_INFO(overlay_15, 0),
OVERLAY_INFO(overlay_16, -EBUSY),
OVERLAY_INFO(overlay_17, -EEXIST),
OVERLAY_INFO(overlay_18, 0),
OVERLAY_INFO(overlay_19, 0),
OVERLAY_INFO(overlay_20, 0),
OVERLAY_INFO(overlay_gpio_01, 0),
OVERLAY_INFO(overlay_gpio_02a, 0),
OVERLAY_INFO(overlay_gpio_02b, 0),