From 8d6320cc4d5cd01e2e7fd01dd635e360cf0a1699 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 29 Dec 2019 21:19:26 -0700 Subject: [PATCH] dm: devres: Add tests The devres functionality has very few users in U-Boot, but it still should have tests. Add a few basic tests of the main functions. Signed-off-by: Simon Glass --- drivers/core/devres.c | 13 +++ include/dm/devres.h | 20 +++++ test/dm/Makefile | 1 + test/dm/devres.c | 178 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 212 insertions(+) create mode 100644 test/dm/devres.c diff --git a/drivers/core/devres.c b/drivers/core/devres.c index 9c04499c6d..36a8b1eb5f 100644 --- a/drivers/core/devres.c +++ b/drivers/core/devres.c @@ -223,6 +223,19 @@ void dm_dump_devres(void) if (root) dump_resources(root, 0); } + +void devres_get_stats(const struct udevice *dev, struct devres_stats *stats) +{ + struct devres *dr; + + stats->allocs = 0; + stats->total_size = 0; + list_for_each_entry(dr, &dev->devres_head, entry) { + stats->allocs++; + stats->total_size += dr->size; + } +} + #endif /* diff --git a/include/dm/devres.h b/include/dm/devres.h index 32fbf38054..9c69196054 100644 --- a/include/dm/devres.h +++ b/include/dm/devres.h @@ -15,6 +15,17 @@ typedef void (*dr_release_t)(struct udevice *dev, void *res); typedef int (*dr_match_t)(struct udevice *dev, void *res, void *match_data); +/** + * struct devres_stats - Information about devres allocations for a device + * + * @allocs: Number of allocations + * @total_size: Total size of allocations in bytes + */ +struct devres_stats { + int allocs; + int total_size; +}; + #ifdef CONFIG_DEVRES #ifdef CONFIG_DEBUG_DEVRES @@ -189,6 +200,9 @@ static inline void *devm_kcalloc(struct udevice *dev, */ void devm_kfree(struct udevice *dev, void *ptr); +/* Get basic stats on allocations */ +void devres_get_stats(const struct udevice *dev, struct devres_stats *stats); + #else /* ! CONFIG_DEVRES */ static inline void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp) @@ -265,5 +279,11 @@ static inline void devm_kfree(struct udevice *dev, void *ptr) { kfree(ptr); } + +static inline void devres_get_stats(const struct udevice *dev, + struct devres_stats *stats) +{ +} + #endif /* DEVRES */ #endif /* _DM_DEVRES_H */ diff --git a/test/dm/Makefile b/test/dm/Makefile index a268783169..85cc0f7fb8 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_BLK) += blk.o obj-$(CONFIG_BOARD) += board.o obj-$(CONFIG_DM_BOOTCOUNT) += bootcount.o obj-$(CONFIG_CLK) += clk.o clk_ccf.o +obj-$(CONFIG_DEVRES) += devres.o obj-$(CONFIG_VIDEO_MIPI_DSI) += dsi_host.o obj-$(CONFIG_DM_ETH) += eth.o obj-$(CONFIG_FIRMWARE) += firmware.o diff --git a/test/dm/devres.c b/test/dm/devres.c new file mode 100644 index 0000000000..c351844db9 --- /dev/null +++ b/test/dm/devres.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for the devres ( + * + * Copyright 2019 Google LLC + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Test that devm_kmalloc() allocates memory, free when device is removed */ +static int dm_test_devres_alloc(struct unit_test_state *uts) +{ + ulong mem_start, mem_dev, mem_kmalloc; + struct udevice *dev; + void *ptr; + + mem_start = ut_check_delta(0); + ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev)); + mem_dev = ut_check_delta(mem_start); + ut_assert(mem_dev > 0); + + /* This should increase allocated memory */ + ptr = devm_kmalloc(dev, TEST_DEVRES_SIZE, 0); + ut_assert(ptr != NULL); + mem_kmalloc = ut_check_delta(mem_dev); + ut_assert(mem_kmalloc > 0); + + /* Check that ptr is freed */ + device_remove(dev, DM_REMOVE_NORMAL); + ut_asserteq(0, ut_check_delta(mem_start)); + + return 0; +} +DM_TEST(dm_test_devres_alloc, DM_TESTF_SCAN_PDATA); + +/* Test devm_kfree() can be used to free memory too */ +static int dm_test_devres_free(struct unit_test_state *uts) +{ + ulong mem_start, mem_dev, mem_kmalloc; + struct udevice *dev; + void *ptr; + + mem_start = ut_check_delta(0); + ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev)); + mem_dev = ut_check_delta(mem_start); + ut_assert(mem_dev > 0); + + ptr = devm_kmalloc(dev, TEST_DEVRES_SIZE, 0); + ut_assert(ptr != NULL); + mem_kmalloc = ut_check_delta(mem_dev); + ut_assert(mem_kmalloc > 0); + + /* Free the ptr and check that memory usage goes down */ + devm_kfree(dev, ptr); + ut_assert(ut_check_delta(mem_kmalloc) < 0); + + device_remove(dev, DM_REMOVE_NORMAL); + ut_asserteq(0, ut_check_delta(mem_start)); + + return 0; +} +DM_TEST(dm_test_devres_free, DM_TESTF_SCAN_PDATA); + + +/* Test that kzalloc() returns memory that is zeroed */ +static int dm_test_devres_kzalloc(struct unit_test_state *uts) +{ + struct udevice *dev; + u8 *ptr, val; + int i; + + ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev)); + + ptr = devm_kzalloc(dev, TEST_DEVRES_SIZE, 0); + ut_assert(ptr != NULL); + for (val = 0, i = 0; i < TEST_DEVRES_SIZE; i++) + val |= *ptr; + ut_asserteq(0, val); + + return 0; +} +DM_TEST(dm_test_devres_kzalloc, DM_TESTF_SCAN_PDATA); + +/* Test that devm_kmalloc_array() allocates an array that can be set */ +static int dm_test_devres_kmalloc_array(struct unit_test_state *uts) +{ + ulong mem_start, mem_dev; + struct udevice *dev; + u8 *ptr; + + mem_start = ut_check_delta(0); + ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev)); + mem_dev = ut_check_delta(mem_start); + + ptr = devm_kmalloc_array(dev, TEST_DEVRES_COUNT, TEST_DEVRES_SIZE, 0); + ut_assert(ptr != NULL); + memset(ptr, '\xff', TEST_DEVRES_TOTAL); + ut_assert(ut_check_delta(mem_dev) > 0); + + device_remove(dev, DM_REMOVE_NORMAL); + ut_asserteq(0, ut_check_delta(mem_start)); + + return 0; +} +DM_TEST(dm_test_devres_kmalloc_array, DM_TESTF_SCAN_PDATA); + +/* Test that devm_kcalloc() allocates a zeroed array */ +static int dm_test_devres_kcalloc(struct unit_test_state *uts) +{ + ulong mem_start, mem_dev; + struct udevice *dev; + u8 *ptr, val; + int i; + + mem_start = ut_check_delta(0); + ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev)); + mem_dev = ut_check_delta(mem_start); + ut_assert(mem_dev > 0); + + /* This should increase allocated memory */ + ptr = devm_kcalloc(dev, TEST_DEVRES_SIZE, TEST_DEVRES_COUNT, 0); + ut_assert(ptr != NULL); + ut_assert(ut_check_delta(mem_dev) > 0); + for (val = 0, i = 0; i < TEST_DEVRES_TOTAL; i++) + val |= *ptr; + ut_asserteq(0, val); + + /* Check that ptr is freed */ + device_remove(dev, DM_REMOVE_NORMAL); + ut_asserteq(0, ut_check_delta(mem_start)); + + return 0; +} +DM_TEST(dm_test_devres_kcalloc, DM_TESTF_SCAN_PDATA); + +static int dm_test_devres_phase(struct unit_test_state *uts) +{ + struct devres_stats stats; + struct udevice *dev; + + /* + * The device is bound already, so find it and check that it has the + * allocation created in the bind() method. + */ + ut_assertok(uclass_find_first_device(UCLASS_TEST_DEVRES, &dev)); + devres_get_stats(dev, &stats); + ut_asserteq(1, stats.allocs); + ut_asserteq(TEST_DEVRES_SIZE, stats.total_size); + + /* Probing the device should add one allocation */ + ut_assertok(uclass_first_device(UCLASS_TEST_DEVRES, &dev)); + ut_assert(dev != NULL); + devres_get_stats(dev, &stats); + ut_asserteq(2, stats.allocs); + ut_asserteq(TEST_DEVRES_SIZE + TEST_DEVRES_SIZE2, stats.total_size); + + /* Removing the device should drop one allocation */ + device_remove(dev, DM_REMOVE_NORMAL); + devres_get_stats(dev, &stats); + ut_asserteq(1, stats.allocs); + ut_asserteq(TEST_DEVRES_SIZE, stats.total_size); + + /* Unbinding removes the other. Note this access a freed pointer */ + device_unbind(dev); + devres_get_stats(dev, &stats); + ut_asserteq(0, stats.allocs); + ut_asserteq(0, stats.total_size); + + return 0; +} +DM_TEST(dm_test_devres_phase, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- 2.25.1