diff options
author | Frank Rowand <frank.rowand@sony.com> | 2018-10-04 23:24:17 -0400 |
---|---|---|
committer | Frank Rowand <frank.rowand@sony.com> | 2018-11-09 01:10:35 -0500 |
commit | 144552c786925314c1e7cb8f91a71dae1aca8798 (patch) | |
tree | 529691048e02c625ece39d41db6304f6f16db9a9 | |
parent | 651022382c7f8da46cb4872a545ee1da6d097d2a (diff) |
of: overlay: add tests to validate kfrees from overlay removal
Add checks:
- attempted kfree due to refcount reaching zero before overlay
is removed
- properties linked to an overlay node when the node is removed
- node refcount > one during node removal in a changeset destroy,
if the node was created by the changeset
After applying this patch, several validation warnings will be
reported from the devicetree unittest during boot due to
pre-existing devicetree bugs. The warnings will be similar to:
OF: ERROR: of_node_release(), unexpected properties in /testcase-data/overlay-node/test-bus/test-unittest11
OF: ERROR: memory leak, expected refcount 1 instead of 2, of_node_get()/of_node_put() unbalanced - destroy cset entry: attach overlay node /testcase-data-2/substation@100/
hvac-medium-2
Tested-by: Alan Tull <atull@kernel.org>
Signed-off-by: Frank Rowand <frank.rowand@sony.com>
-rw-r--r-- | drivers/of/dynamic.c | 29 | ||||
-rw-r--r-- | drivers/of/overlay.c | 1 | ||||
-rw-r--r-- | include/linux/of.h | 15 |
3 files changed, 40 insertions, 5 deletions
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index f4f8ed9b5454..12c3f9a15e94 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c | |||
@@ -330,6 +330,25 @@ void of_node_release(struct kobject *kobj) | |||
330 | if (!of_node_check_flag(node, OF_DYNAMIC)) | 330 | if (!of_node_check_flag(node, OF_DYNAMIC)) |
331 | return; | 331 | return; |
332 | 332 | ||
333 | if (of_node_check_flag(node, OF_OVERLAY)) { | ||
334 | |||
335 | if (!of_node_check_flag(node, OF_OVERLAY_FREE_CSET)) { | ||
336 | /* premature refcount of zero, do not free memory */ | ||
337 | pr_err("ERROR: memory leak before free overlay changeset, %pOF\n", | ||
338 | node); | ||
339 | return; | ||
340 | } | ||
341 | |||
342 | /* | ||
343 | * If node->properties non-empty then properties were added | ||
344 | * to this node either by different overlay that has not | ||
345 | * yet been removed, or by a non-overlay mechanism. | ||
346 | */ | ||
347 | if (node->properties) | ||
348 | pr_err("ERROR: %s(), unexpected properties in %pOF\n", | ||
349 | __func__, node); | ||
350 | } | ||
351 | |||
333 | property_list_free(node->properties); | 352 | property_list_free(node->properties); |
334 | property_list_free(node->deadprops); | 353 | property_list_free(node->deadprops); |
335 | 354 | ||
@@ -434,6 +453,16 @@ struct device_node *__of_node_dup(const struct device_node *np, | |||
434 | 453 | ||
435 | static void __of_changeset_entry_destroy(struct of_changeset_entry *ce) | 454 | static void __of_changeset_entry_destroy(struct of_changeset_entry *ce) |
436 | { | 455 | { |
456 | if (ce->action == OF_RECONFIG_ATTACH_NODE && | ||
457 | of_node_check_flag(ce->np, OF_OVERLAY)) { | ||
458 | if (kref_read(&ce->np->kobj.kref) > 1) { | ||
459 | pr_err("ERROR: memory leak, expected refcount 1 instead of %d, of_node_get()/of_node_put() unbalanced - destroy cset entry: attach overlay node %pOF\n", | ||
460 | kref_read(&ce->np->kobj.kref), ce->np); | ||
461 | } else { | ||
462 | of_node_set_flag(ce->np, OF_OVERLAY_FREE_CSET); | ||
463 | } | ||
464 | } | ||
465 | |||
437 | of_node_put(ce->np); | 466 | of_node_put(ce->np); |
438 | list_del(&ce->node); | 467 | list_del(&ce->node); |
439 | kfree(ce); | 468 | kfree(ce); |
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 42b1f73ac5f6..f5fc8859a7ee 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c | |||
@@ -373,6 +373,7 @@ static int add_changeset_node(struct overlay_changeset *ovcs, | |||
373 | return -ENOMEM; | 373 | return -ENOMEM; |
374 | 374 | ||
375 | tchild->parent = target_node; | 375 | tchild->parent = target_node; |
376 | of_node_set_flag(tchild, OF_OVERLAY); | ||
376 | 377 | ||
377 | ret = of_changeset_attach_node(&ovcs->cset, tchild); | 378 | ret = of_changeset_attach_node(&ovcs->cset, tchild); |
378 | if (ret) | 379 | if (ret) |
diff --git a/include/linux/of.h b/include/linux/of.h index a5aee3c438ad..664cd5573ae2 100644 --- a/include/linux/of.h +++ b/include/linux/of.h | |||
@@ -138,11 +138,16 @@ extern struct device_node *of_aliases; | |||
138 | extern struct device_node *of_stdout; | 138 | extern struct device_node *of_stdout; |
139 | extern raw_spinlock_t devtree_lock; | 139 | extern raw_spinlock_t devtree_lock; |
140 | 140 | ||
141 | /* flag descriptions (need to be visible even when !CONFIG_OF) */ | 141 | /* |
142 | #define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */ | 142 | * struct device_node flag descriptions |
143 | #define OF_DETACHED 2 /* node has been detached from the device tree */ | 143 | * (need to be visible even when !CONFIG_OF) |
144 | #define OF_POPULATED 3 /* device already created for the node */ | 144 | */ |
145 | #define OF_POPULATED_BUS 4 /* of_platform_populate recursed to children of this node */ | 145 | #define OF_DYNAMIC 1 /* (and properties) allocated via kmalloc */ |
146 | #define OF_DETACHED 2 /* detached from the device tree */ | ||
147 | #define OF_POPULATED 3 /* device already created */ | ||
148 | #define OF_POPULATED_BUS 4 /* platform bus created for children */ | ||
149 | #define OF_OVERLAY 5 /* allocated for an overlay */ | ||
150 | #define OF_OVERLAY_FREE_CSET 6 /* in overlay cset being freed */ | ||
146 | 151 | ||
147 | #define OF_BAD_ADDR ((u64)-1) | 152 | #define OF_BAD_ADDR ((u64)-1) |
148 | 153 | ||