From patchwork Thu Apr 16 02:29:37 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2175 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776306669; bh=Pl3FzOyXw7h9Cc/e7ZjG7/1hF2/p14UfmBtTRQ11zEg=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=ImiIM5YeTOfHw+uiIpGiqfIEYOQgpBQ8nBDi38WUZW58X2fo4Hp1fsl2YchTH8+vL ugfHxJV9dj5U9ZwqoVKMqXUNBCdmCUNctrnYTOxUlS+LcZcejZUNHbG/o1lIZXZ49q J1NeDxvS1njDKeH2w5AW856XinyD5kl0T7D7mlTw= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 3F0DC6A4CD for ; Wed, 15 Apr 2026 20:31:09 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id wfeedIO7yYb4 for ; Wed, 15 Apr 2026 20:31:09 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776306666; bh=Pl3FzOyXw7h9Cc/e7ZjG7/1hF2/p14UfmBtTRQ11zEg=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=lIoomP2CK7+CmXPYfj7OdZuySC3m8qpbyH07Q+orMVyK5Xd+vHZmq8K/9S0C3TH1j /Y30VgwcfCUEu7HyCQYrXPimYoaCKNpmsDBLKyKViJ2ZJa57BB921Isr0huszM8tAI 9kuDXzdB0AwmgebOSz7j5fGSjz66wlLGdN5jGlfo= Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 31D826A4A7 for ; Wed, 15 Apr 2026 20:31:06 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776306664; bh=9+cwAPYcsvuRnTx40/vQMckFXmW9C99Hx3qyU3VKoMo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=I/m0kPwgc0svDADoYrh5n0CU9e9VA6riyejAgxqccIlMCmWqMn/mE3JXW7/gxDOBY dW8xgzmiUBzkBiaZxGPD6zd+S0NnRSIOcLVDxB6PpNvyW9kSL5jKEUWWZNJX7TUo9t qB4dpSBFM7wp2n9oeCozfkHXOTujAg9xb1vuHKy8= Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 964C46A4A7; Wed, 15 Apr 2026 20:31:04 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id XP2c2A0lLIau; Wed, 15 Apr 2026 20:31:04 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1776306659; bh=WALcUicsW5ZvNcNY5IyHsixEIAqbcpaEtuDImAF3Ng4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=GSvyQgxa5kdjtOO4WxckFJYYO7E+MpgKoMl/4oizg4b7lD2392olmFtnl3YZDNYWt 1B9roJStdGEKdkKiQ/kKKb5ozsOZ71OVcjiT/5ApTuync+DVt4IHwLbMqJXO8YOGml i5wUCgJuqCzFiIzo0B8BaDwA8dnH+tX3KLhCg2iQ= Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 47F0A6A4A0; Wed, 15 Apr 2026 20:30:59 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Wed, 15 Apr 2026 20:29:37 -0600 Message-ID: <20260416023021.626949-13-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260416023021.626949-1-sjg@u-boot.org> References: <20260416023021.626949-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: XBRLY2IX4PYJDS36YBITCYI2257YX5IR X-Message-ID-Hash: XBRLY2IX4PYJDS36YBITCYI2257YX5IR X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 12/33] test: Save and restore global DM state around UTF_DM tests List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass dm_test_pre_run() sets gd->dm_root to NULL and calls dm_init(), which resets the uclass list head via INIT_LIST_HEAD(). That abandons the global driver-model state built by sandbox at boot without freeing it, leaving every uclass and device orphaned in the heap. At the end of each ut_run_list(), dm_test_restore() rebuilds a fresh global state with another dm_init() + dm_scan_plat() + dm_extended_scan(), which the next test's dm_test_pre_run() also orphans. Each 'ut' command therefore leaks several hundred allocations, and a long pytest session eventually exhausts the mcheck registry. The documented intent (see comment in test_pre_run()) is that UTF_DM tests run against a scratch state and leave the global state untouched. Implement that: snapshot gd->dm_root, gd->uclass_root_s, and gd->uclass_root before wiping them in dm_test_pre_run(), and write the snapshot back at the end of dm_test_post_run() once the test's uclasses have been destroyed. The saved uclasses' sibling_node pointers still reference the same &gd->uclass_root_s address, so restoring the list head re-links the list without any fix-up. The uclass_root pointer itself must also be restored since tests like dm_test_uclass_before_ready zero it explicitly. dm_test_restore() no longer needs to rebuild anything and only resets the live-tree root. Signed-off-by: Simon Glass --- include/test/test.h | 10 ++++++++++ test/test-main.c | 40 +++++++++++++++++++++++++++++++--------- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/include/test/test.h b/include/test/test.h index 7d28948eb5f..b4b20f6a701 100644 --- a/include/test/test.h +++ b/include/test/test.h @@ -9,6 +9,7 @@ #include #include #include +#include #define UT_MAX_ARGS 8 #define UT_PRIV_SIZE 256 @@ -78,6 +79,12 @@ struct ut_arg { * @start: Store the starting mallinfo when doing leak test * @of_live: true to use livetree if available, false to use flattree * @of_root: Record of the livetree root node (used for setting up tests) + * @saved_dm_root: Global dm_root swapped out while a UTF_DM test runs + * @saved_uclass_root: Global uclass-root list head swapped out while a + * UTF_DM test runs, so the pre-test state can be restored afterwards + * @saved_uclass_root_ptr: Saved value of gd->uclass_root (the pointer to + * the uclass-root list head). Restored alongside the list head above + * in case a test zeros the pointer (e.g. dm_test_uclass_before_ready) * @root: Root device * @testdev: Test device * @force_fail_alloc: Force all memory allocs to fail @@ -115,6 +122,9 @@ struct unit_test_state { int worst_ms; struct mallinfo start; struct device_node *of_root; + struct udevice *saved_dm_root; + struct list_head saved_uclass_root; + struct list_head *saved_uclass_root_ptr; bool of_live; struct udevice *root; struct udevice *testdev; diff --git a/test/test-main.c b/test/test-main.c index 8bbe7d039bc..fc66b2af0d0 100644 --- a/test/test-main.c +++ b/test/test-main.c @@ -263,7 +263,20 @@ static int dm_test_pre_run(struct unit_test_state *uts) if (fdt_action() == FDTCHK_CHECKSUM) uts->fdt_chksum = crc8(0, gd->fdt_blob, fdt_totalsize(gd->fdt_blob)); + + /* + * Save the global driver-model state so it can be restored after the + * test. Snapshot the uclass list head and the pointer to it, and + * detach the existing list. The old uclasses are left intact in + * memory but are no longer reachable, so a fresh dm_init() can build + * up a private list for the test without disturbing them. + */ + uts->saved_dm_root = gd->dm_root; + uts->saved_uclass_root = gd->uclass_root_s; + uts->saved_uclass_root_ptr = gd->uclass_root; gd->dm_root = NULL; + INIT_LIST_HEAD(&gd->uclass_root_s); + malloc_disable_testing(); if (CONFIG_IS_ENABLED(UT_DM) && !CONFIG_IS_ENABLED(OF_PLATDATA)) memset(dm_testdrv_op_count, '\0', sizeof(dm_testdrv_op_count)); @@ -330,6 +343,19 @@ static int dm_test_post_run(struct unit_test_state *uts) } } + /* + * Restore the global driver-model state that was saved by + * dm_test_pre_run(). Writing the saved list_head back reconnects + * the saved list because the uclasses' sibling_node pointers still + * reference &gd->uclass_root_s at the same address. A test may have + * zeroed gd->uclass_root (e.g. dm_test_uclass_before_ready) so put + * the pointer back as well. + */ + gd->dm_root = uts->saved_dm_root; + gd->uclass_root_s = uts->saved_uclass_root; + gd->uclass_root = uts->saved_uclass_root_ptr; + uts->saved_dm_root = NULL; + return 0; } @@ -434,21 +460,17 @@ static bool ut_list_has_dm_tests(struct unit_test *tests, int count, /** * dm_test_restore() Put things back to normal so sandbox works as expected * + * dm_test_pre_run()/dm_test_post_run() save and restore the global driver + * model state across each UTF_DM test, so the global root is already back + * in place by the time we get here. Only restore the live-tree root, which + * per-test setup leaves pointing at the test's own tree. + * * @of_root: Value to set for of_root * Return: 0 if OK, -ve on error */ static int dm_test_restore(struct device_node *of_root) { - int ret; - gd_set_of_root(of_root); - gd->dm_root = NULL; - ret = dm_init(CONFIG_IS_ENABLED(OF_LIVE)); - if (ret) - return ret; - dm_scan_plat(false); - if (!CONFIG_IS_ENABLED(OF_PLATDATA)) - dm_extended_scan(false); return 0; }