aboutsummaryrefslogtreecommitdiff
path: root/test/dm/devres.c
blob: 4f959d11da928cdd75a3a847509636745c7f1480 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// 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);