| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Tests for the devres ( |
| * |
| * Copyright 2019 Google LLC |
| */ |
| |
| #include <common.h> |
| #include <errno.h> |
| #include <dm.h> |
| #include <log.h> |
| #include <malloc.h> |
| #include <dm/device-internal.h> |
| #include <dm/devres.h> |
| #include <dm/test.h> |
| #include <dm/uclass-internal.h> |
| #include <test/ut.h> |
| |
| /* 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, UT_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, UT_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, UT_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, UT_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, UT_TESTF_SCAN_PDATA); |
| |
| /* Test devres releases resources automatically as expected */ |
| 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)); |
| ut_assertnonnull(dev); |
| devres_get_stats(dev, &stats); |
| ut_asserteq(1, stats.allocs); |
| ut_asserteq(TEST_DEVRES_SIZE, stats.total_size); |
| |
| /* Getting plat should add one allocation */ |
| ut_assertok(device_of_to_plat(dev)); |
| devres_get_stats(dev, &stats); |
| ut_asserteq(2, stats.allocs); |
| ut_asserteq(TEST_DEVRES_SIZE + TEST_DEVRES_SIZE3, 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(3, stats.allocs); |
| ut_asserteq(TEST_DEVRES_SIZE + TEST_DEVRES_SIZE2 + TEST_DEVRES_SIZE3, |
| stats.total_size); |
| |
| /* Removing the device should drop both those allocations */ |
| 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, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); |