1// SPDX-License-Identifier: GPL-2.0 AND MIT
2/*
3 * Copyright �� 2023 Intel Corporation
4 */
5#include <linux/shmem_fs.h>
6#include <drm/ttm/ttm_tt.h>
7
8#include "ttm_kunit_helpers.h"
9
10#define BO_SIZE		SZ_4K
11
12struct ttm_tt_test_case {
13	const char *description;
14	uint32_t size;
15	uint32_t extra_pages_num;
16};
17
18static int ttm_tt_test_init(struct kunit *test)
19{
20	struct ttm_test_devices *priv;
21
22	priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
23	KUNIT_ASSERT_NOT_NULL(test, priv);
24
25	priv = ttm_test_devices_all(test);
26	test->priv = priv;
27
28	return 0;
29}
30
31static const struct ttm_tt_test_case ttm_tt_init_basic_cases[] = {
32	{
33		.description = "Page-aligned size",
34		.size = SZ_4K,
35	},
36	{
37		.description = "Extra pages requested",
38		.size = SZ_4K,
39		.extra_pages_num = 1,
40	},
41};
42
43static void ttm_tt_init_case_desc(const struct ttm_tt_test_case *t,
44				  char *desc)
45{
46	strscpy(desc, t->description, KUNIT_PARAM_DESC_SIZE);
47}
48
49KUNIT_ARRAY_PARAM(ttm_tt_init_basic, ttm_tt_init_basic_cases,
50		  ttm_tt_init_case_desc);
51
52static void ttm_tt_init_basic(struct kunit *test)
53{
54	const struct ttm_tt_test_case *params = test->param_value;
55	struct ttm_buffer_object *bo;
56	struct ttm_tt *tt;
57	uint32_t page_flags = TTM_TT_FLAG_ZERO_ALLOC;
58	enum ttm_caching caching = ttm_cached;
59	uint32_t extra_pages = params->extra_pages_num;
60	int num_pages = params->size >> PAGE_SHIFT;
61	int err;
62
63	tt = kunit_kzalloc(test, sizeof(*tt), GFP_KERNEL);
64	KUNIT_ASSERT_NOT_NULL(test, tt);
65
66	bo = ttm_bo_kunit_init(test, test->priv, params->size);
67
68	err = ttm_tt_init(tt, bo, page_flags, caching, extra_pages);
69	KUNIT_ASSERT_EQ(test, err, 0);
70
71	KUNIT_ASSERT_EQ(test, tt->num_pages, num_pages + extra_pages);
72
73	KUNIT_ASSERT_EQ(test, tt->page_flags, page_flags);
74	KUNIT_ASSERT_EQ(test, tt->caching, caching);
75
76	KUNIT_ASSERT_NULL(test, tt->dma_address);
77	KUNIT_ASSERT_NULL(test, tt->swap_storage);
78}
79
80static void ttm_tt_init_misaligned(struct kunit *test)
81{
82	struct ttm_buffer_object *bo;
83	struct ttm_tt *tt;
84	enum ttm_caching caching = ttm_cached;
85	uint32_t size = SZ_8K;
86	int num_pages = (size + SZ_4K) >> PAGE_SHIFT;
87	int err;
88
89	tt = kunit_kzalloc(test, sizeof(*tt), GFP_KERNEL);
90	KUNIT_ASSERT_NOT_NULL(test, tt);
91
92	bo = ttm_bo_kunit_init(test, test->priv, size);
93
94	/* Make the object size misaligned */
95	bo->base.size += 1;
96
97	err = ttm_tt_init(tt, bo, 0, caching, 0);
98	KUNIT_ASSERT_EQ(test, err, 0);
99
100	KUNIT_ASSERT_EQ(test, tt->num_pages, num_pages);
101}
102
103static void ttm_tt_fini_basic(struct kunit *test)
104{
105	struct ttm_buffer_object *bo;
106	struct ttm_tt *tt;
107	enum ttm_caching caching = ttm_cached;
108	int err;
109
110	tt = kunit_kzalloc(test, sizeof(*tt), GFP_KERNEL);
111	KUNIT_ASSERT_NOT_NULL(test, tt);
112
113	bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
114
115	err = ttm_tt_init(tt, bo, 0, caching, 0);
116	KUNIT_ASSERT_EQ(test, err, 0);
117	KUNIT_ASSERT_NOT_NULL(test, tt->pages);
118
119	ttm_tt_fini(tt);
120	KUNIT_ASSERT_NULL(test, tt->pages);
121}
122
123static void ttm_tt_fini_sg(struct kunit *test)
124{
125	struct ttm_buffer_object *bo;
126	struct ttm_tt *tt;
127	enum ttm_caching caching = ttm_cached;
128	int err;
129
130	tt = kunit_kzalloc(test, sizeof(*tt), GFP_KERNEL);
131	KUNIT_ASSERT_NOT_NULL(test, tt);
132
133	bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
134
135	err = ttm_sg_tt_init(tt, bo, 0, caching);
136	KUNIT_ASSERT_EQ(test, err, 0);
137	KUNIT_ASSERT_NOT_NULL(test, tt->dma_address);
138
139	ttm_tt_fini(tt);
140	KUNIT_ASSERT_NULL(test, tt->dma_address);
141}
142
143static void ttm_tt_fini_shmem(struct kunit *test)
144{
145	struct ttm_buffer_object *bo;
146	struct ttm_tt *tt;
147	struct file *shmem;
148	enum ttm_caching caching = ttm_cached;
149	int err;
150
151	tt = kunit_kzalloc(test, sizeof(*tt), GFP_KERNEL);
152	KUNIT_ASSERT_NOT_NULL(test, tt);
153
154	bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
155
156	err = ttm_tt_init(tt, bo, 0, caching, 0);
157	KUNIT_ASSERT_EQ(test, err, 0);
158
159	shmem = shmem_file_setup("ttm swap", BO_SIZE, 0);
160	tt->swap_storage = shmem;
161
162	ttm_tt_fini(tt);
163	KUNIT_ASSERT_NULL(test, tt->swap_storage);
164}
165
166static void ttm_tt_create_basic(struct kunit *test)
167{
168	struct ttm_buffer_object *bo;
169	int err;
170
171	bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
172	bo->type = ttm_bo_type_device;
173
174	dma_resv_lock(bo->base.resv, NULL);
175	err = ttm_tt_create(bo, false);
176	dma_resv_unlock(bo->base.resv);
177
178	KUNIT_EXPECT_EQ(test, err, 0);
179	KUNIT_EXPECT_NOT_NULL(test, bo->ttm);
180
181	/* Free manually, as it was allocated outside of KUnit */
182	kfree(bo->ttm);
183}
184
185static void ttm_tt_create_invalid_bo_type(struct kunit *test)
186{
187	struct ttm_buffer_object *bo;
188	int err;
189
190	bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
191	bo->type = ttm_bo_type_sg + 1;
192
193	dma_resv_lock(bo->base.resv, NULL);
194	err = ttm_tt_create(bo, false);
195	dma_resv_unlock(bo->base.resv);
196
197	KUNIT_EXPECT_EQ(test, err, -EINVAL);
198	KUNIT_EXPECT_NULL(test, bo->ttm);
199}
200
201static void ttm_tt_create_ttm_exists(struct kunit *test)
202{
203	struct ttm_buffer_object *bo;
204	struct ttm_tt *tt;
205	enum ttm_caching caching = ttm_cached;
206	int err;
207
208	tt = kunit_kzalloc(test, sizeof(*tt), GFP_KERNEL);
209	KUNIT_ASSERT_NOT_NULL(test, tt);
210
211	bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
212
213	err = ttm_tt_init(tt, bo, 0, caching, 0);
214	KUNIT_ASSERT_EQ(test, err, 0);
215	bo->ttm = tt;
216
217	dma_resv_lock(bo->base.resv, NULL);
218	err = ttm_tt_create(bo, false);
219	dma_resv_unlock(bo->base.resv);
220
221	/* Expect to keep the previous TTM */
222	KUNIT_ASSERT_EQ(test, err, 0);
223	KUNIT_ASSERT_PTR_EQ(test, tt, bo->ttm);
224}
225
226static struct ttm_tt *ttm_tt_null_create(struct ttm_buffer_object *bo,
227					 uint32_t page_flags)
228{
229	return NULL;
230}
231
232static struct ttm_device_funcs ttm_dev_empty_funcs = {
233	.ttm_tt_create = ttm_tt_null_create,
234};
235
236static void ttm_tt_create_failed(struct kunit *test)
237{
238	const struct ttm_test_devices *devs = test->priv;
239	struct ttm_buffer_object *bo;
240	int err;
241
242	bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
243
244	/* Update ttm_device_funcs so we don't alloc ttm_tt */
245	devs->ttm_dev->funcs = &ttm_dev_empty_funcs;
246
247	dma_resv_lock(bo->base.resv, NULL);
248	err = ttm_tt_create(bo, false);
249	dma_resv_unlock(bo->base.resv);
250
251	KUNIT_ASSERT_EQ(test, err, -ENOMEM);
252}
253
254static void ttm_tt_destroy_basic(struct kunit *test)
255{
256	const struct ttm_test_devices *devs = test->priv;
257	struct ttm_buffer_object *bo;
258	int err;
259
260	bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE);
261
262	dma_resv_lock(bo->base.resv, NULL);
263	err = ttm_tt_create(bo, false);
264	dma_resv_unlock(bo->base.resv);
265
266	KUNIT_ASSERT_EQ(test, err, 0);
267	KUNIT_ASSERT_NOT_NULL(test, bo->ttm);
268
269	ttm_tt_destroy(devs->ttm_dev, bo->ttm);
270}
271
272static struct kunit_case ttm_tt_test_cases[] = {
273	KUNIT_CASE_PARAM(ttm_tt_init_basic, ttm_tt_init_basic_gen_params),
274	KUNIT_CASE(ttm_tt_init_misaligned),
275	KUNIT_CASE(ttm_tt_fini_basic),
276	KUNIT_CASE(ttm_tt_fini_sg),
277	KUNIT_CASE(ttm_tt_fini_shmem),
278	KUNIT_CASE(ttm_tt_create_basic),
279	KUNIT_CASE(ttm_tt_create_invalid_bo_type),
280	KUNIT_CASE(ttm_tt_create_ttm_exists),
281	KUNIT_CASE(ttm_tt_create_failed),
282	KUNIT_CASE(ttm_tt_destroy_basic),
283	{}
284};
285
286static struct kunit_suite ttm_tt_test_suite = {
287	.name = "ttm_tt",
288	.init = ttm_tt_test_init,
289	.exit = ttm_test_devices_fini,
290	.test_cases = ttm_tt_test_cases,
291};
292
293kunit_test_suites(&ttm_tt_test_suite);
294
295MODULE_LICENSE("GPL");
296