Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/selftests: add drm buddy alloc range testcase

- add a test to check the range allocation
- export get_buddy() function in drm_buddy.c
- export drm_prandom_u32_max_state() in lib/drm_random.c
- include helper functions
- include prime number header file

v2:
- add drm_get_buddy() function description (Matthew Auld)
- removed unnecessary test succeeded print

Signed-off-by: Arunpravin <Arunpravin.PaneerSelvam@amd.com>
Reviewed-by: Matthew Auld <matthew.auld@intel.com>
Acked-by: Christian König <christian.koenig@amd.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20220222174845.2175-3-Arunpravin.PaneerSelvam@amd.com
Signed-off-by: Christian König <christian.koenig@amd.com>

authored by

Arunpravin and committed by
Christian König
92937f17 cb8f00f2

+417 -5
+21 -4
drivers/gpu/drm/drm_buddy.c
··· 211 211 } 212 212 213 213 static struct drm_buddy_block * 214 - get_buddy(struct drm_buddy_block *block) 214 + __get_buddy(struct drm_buddy_block *block) 215 215 { 216 216 struct drm_buddy_block *parent; 217 217 ··· 225 225 return parent->left; 226 226 } 227 227 228 + /** 229 + * drm_get_buddy - get buddy address 230 + * 231 + * @block: DRM buddy block 232 + * 233 + * Returns the corresponding buddy block for @block, or NULL 234 + * if this is a root block and can't be merged further. 235 + * Requires some kind of locking to protect against 236 + * any concurrent allocate and free operations. 237 + */ 238 + struct drm_buddy_block * 239 + drm_get_buddy(struct drm_buddy_block *block) 240 + { 241 + return __get_buddy(block); 242 + } 243 + EXPORT_SYMBOL(drm_get_buddy); 244 + 228 245 static void __drm_buddy_free(struct drm_buddy *mm, 229 246 struct drm_buddy_block *block) 230 247 { ··· 250 233 while ((parent = block->parent)) { 251 234 struct drm_buddy_block *buddy; 252 235 253 - buddy = get_buddy(block); 236 + buddy = __get_buddy(block); 254 237 255 238 if (!drm_buddy_block_is_free(buddy)) 256 239 break; ··· 378 361 * bigger is better, so make sure we merge everything back before we 379 362 * free the allocated blocks. 380 363 */ 381 - buddy = get_buddy(block); 364 + buddy = __get_buddy(block); 382 365 if (buddy && 383 366 (drm_buddy_block_is_free(block) && 384 367 drm_buddy_block_is_free(buddy))) ··· 517 500 * bigger is better, so make sure we merge everything back before we 518 501 * free the allocated blocks. 519 502 */ 520 - buddy = get_buddy(block); 503 + buddy = __get_buddy(block); 521 504 if (buddy && 522 505 (drm_buddy_block_is_free(block) && 523 506 drm_buddy_block_is_free(buddy)))
+2 -1
drivers/gpu/drm/lib/drm_random.c
··· 7 7 8 8 #include "drm_random.h" 9 9 10 - static inline u32 drm_prandom_u32_max_state(u32 ep_ro, struct rnd_state *state) 10 + u32 drm_prandom_u32_max_state(u32 ep_ro, struct rnd_state *state) 11 11 { 12 12 return upper_32_bits((u64)prandom_u32_state(state) * ep_ro); 13 13 } 14 + EXPORT_SYMBOL(drm_prandom_u32_max_state); 14 15 15 16 void drm_random_reorder(unsigned int *order, unsigned int count, 16 17 struct rnd_state *state)
+2
drivers/gpu/drm/lib/drm_random.h
··· 22 22 void drm_random_reorder(unsigned int *order, 23 23 unsigned int count, 24 24 struct rnd_state *state); 25 + u32 drm_prandom_u32_max_state(u32 ep_ro, 26 + struct rnd_state *state); 25 27 26 28 #endif /* !__DRM_RANDOM_H__ */
+1
drivers/gpu/drm/selftests/drm_buddy_selftests.h
··· 8 8 */ 9 9 selftest(sanitycheck, igt_sanitycheck) /* keep first (selfcheck for igt) */ 10 10 selftest(buddy_alloc_limit, igt_buddy_alloc_limit) 11 + selftest(buddy_alloc_range, igt_buddy_alloc_range)
+388
drivers/gpu/drm/selftests/test-drm_buddy.c
··· 6 6 #define pr_fmt(fmt) "drm_buddy: " fmt 7 7 8 8 #include <linux/module.h> 9 + #include <linux/prime_numbers.h> 9 10 10 11 #include <drm/drm_buddy.h> 11 12 ··· 16 15 #include "drm_selftest.h" 17 16 18 17 static unsigned int random_seed; 18 + 19 + static inline const char *yesno(bool v) 20 + { 21 + return v ? "yes" : "no"; 22 + } 23 + 24 + static void __igt_dump_block(struct drm_buddy *mm, 25 + struct drm_buddy_block *block, 26 + bool buddy) 27 + { 28 + pr_err("block info: header=%llx, state=%u, order=%d, offset=%llx size=%llx root=%s buddy=%s\n", 29 + block->header, 30 + drm_buddy_block_state(block), 31 + drm_buddy_block_order(block), 32 + drm_buddy_block_offset(block), 33 + drm_buddy_block_size(mm, block), 34 + yesno(!block->parent), 35 + yesno(buddy)); 36 + } 37 + 38 + static void igt_dump_block(struct drm_buddy *mm, 39 + struct drm_buddy_block *block) 40 + { 41 + struct drm_buddy_block *buddy; 42 + 43 + __igt_dump_block(mm, block, false); 44 + 45 + buddy = drm_get_buddy(block); 46 + if (buddy) 47 + __igt_dump_block(mm, buddy, true); 48 + } 49 + 50 + static int igt_check_block(struct drm_buddy *mm, 51 + struct drm_buddy_block *block) 52 + { 53 + struct drm_buddy_block *buddy; 54 + unsigned int block_state; 55 + u64 block_size; 56 + u64 offset; 57 + int err = 0; 58 + 59 + block_state = drm_buddy_block_state(block); 60 + 61 + if (block_state != DRM_BUDDY_ALLOCATED && 62 + block_state != DRM_BUDDY_FREE && 63 + block_state != DRM_BUDDY_SPLIT) { 64 + pr_err("block state mismatch\n"); 65 + err = -EINVAL; 66 + } 67 + 68 + block_size = drm_buddy_block_size(mm, block); 69 + offset = drm_buddy_block_offset(block); 70 + 71 + if (block_size < mm->chunk_size) { 72 + pr_err("block size smaller than min size\n"); 73 + err = -EINVAL; 74 + } 75 + 76 + if (!is_power_of_2(block_size)) { 77 + pr_err("block size not power of two\n"); 78 + err = -EINVAL; 79 + } 80 + 81 + if (!IS_ALIGNED(block_size, mm->chunk_size)) { 82 + pr_err("block size not aligned to min size\n"); 83 + err = -EINVAL; 84 + } 85 + 86 + if (!IS_ALIGNED(offset, mm->chunk_size)) { 87 + pr_err("block offset not aligned to min size\n"); 88 + err = -EINVAL; 89 + } 90 + 91 + if (!IS_ALIGNED(offset, block_size)) { 92 + pr_err("block offset not aligned to block size\n"); 93 + err = -EINVAL; 94 + } 95 + 96 + buddy = drm_get_buddy(block); 97 + 98 + if (!buddy && block->parent) { 99 + pr_err("buddy has gone fishing\n"); 100 + err = -EINVAL; 101 + } 102 + 103 + if (buddy) { 104 + if (drm_buddy_block_offset(buddy) != (offset ^ block_size)) { 105 + pr_err("buddy has wrong offset\n"); 106 + err = -EINVAL; 107 + } 108 + 109 + if (drm_buddy_block_size(mm, buddy) != block_size) { 110 + pr_err("buddy size mismatch\n"); 111 + err = -EINVAL; 112 + } 113 + 114 + if (drm_buddy_block_state(buddy) == block_state && 115 + block_state == DRM_BUDDY_FREE) { 116 + pr_err("block and its buddy are free\n"); 117 + err = -EINVAL; 118 + } 119 + } 120 + 121 + return err; 122 + } 123 + 124 + static int igt_check_blocks(struct drm_buddy *mm, 125 + struct list_head *blocks, 126 + u64 expected_size, 127 + bool is_contiguous) 128 + { 129 + struct drm_buddy_block *block; 130 + struct drm_buddy_block *prev; 131 + u64 total; 132 + int err = 0; 133 + 134 + block = NULL; 135 + prev = NULL; 136 + total = 0; 137 + 138 + list_for_each_entry(block, blocks, link) { 139 + err = igt_check_block(mm, block); 140 + 141 + if (!drm_buddy_block_is_allocated(block)) { 142 + pr_err("block not allocated\n"), 143 + err = -EINVAL; 144 + } 145 + 146 + if (is_contiguous && prev) { 147 + u64 prev_block_size; 148 + u64 prev_offset; 149 + u64 offset; 150 + 151 + prev_offset = drm_buddy_block_offset(prev); 152 + prev_block_size = drm_buddy_block_size(mm, prev); 153 + offset = drm_buddy_block_offset(block); 154 + 155 + if (offset != (prev_offset + prev_block_size)) { 156 + pr_err("block offset mismatch\n"); 157 + err = -EINVAL; 158 + } 159 + } 160 + 161 + if (err) 162 + break; 163 + 164 + total += drm_buddy_block_size(mm, block); 165 + prev = block; 166 + } 167 + 168 + if (!err) { 169 + if (total != expected_size) { 170 + pr_err("size mismatch, expected=%llx, found=%llx\n", 171 + expected_size, total); 172 + err = -EINVAL; 173 + } 174 + return err; 175 + } 176 + 177 + if (prev) { 178 + pr_err("prev block, dump:\n"); 179 + igt_dump_block(mm, prev); 180 + } 181 + 182 + pr_err("bad block, dump:\n"); 183 + igt_dump_block(mm, block); 184 + 185 + return err; 186 + } 187 + 188 + static int igt_check_mm(struct drm_buddy *mm) 189 + { 190 + struct drm_buddy_block *root; 191 + struct drm_buddy_block *prev; 192 + unsigned int i; 193 + u64 total; 194 + int err = 0; 195 + 196 + if (!mm->n_roots) { 197 + pr_err("n_roots is zero\n"); 198 + return -EINVAL; 199 + } 200 + 201 + if (mm->n_roots != hweight64(mm->size)) { 202 + pr_err("n_roots mismatch, n_roots=%u, expected=%lu\n", 203 + mm->n_roots, hweight64(mm->size)); 204 + return -EINVAL; 205 + } 206 + 207 + root = NULL; 208 + prev = NULL; 209 + total = 0; 210 + 211 + for (i = 0; i < mm->n_roots; ++i) { 212 + struct drm_buddy_block *block; 213 + unsigned int order; 214 + 215 + root = mm->roots[i]; 216 + if (!root) { 217 + pr_err("root(%u) is NULL\n", i); 218 + err = -EINVAL; 219 + break; 220 + } 221 + 222 + err = igt_check_block(mm, root); 223 + 224 + if (!drm_buddy_block_is_free(root)) { 225 + pr_err("root not free\n"); 226 + err = -EINVAL; 227 + } 228 + 229 + order = drm_buddy_block_order(root); 230 + 231 + if (!i) { 232 + if (order != mm->max_order) { 233 + pr_err("max order root missing\n"); 234 + err = -EINVAL; 235 + } 236 + } 237 + 238 + if (prev) { 239 + u64 prev_block_size; 240 + u64 prev_offset; 241 + u64 offset; 242 + 243 + prev_offset = drm_buddy_block_offset(prev); 244 + prev_block_size = drm_buddy_block_size(mm, prev); 245 + offset = drm_buddy_block_offset(root); 246 + 247 + if (offset != (prev_offset + prev_block_size)) { 248 + pr_err("root offset mismatch\n"); 249 + err = -EINVAL; 250 + } 251 + } 252 + 253 + block = list_first_entry_or_null(&mm->free_list[order], 254 + struct drm_buddy_block, 255 + link); 256 + if (block != root) { 257 + pr_err("root mismatch at order=%u\n", order); 258 + err = -EINVAL; 259 + } 260 + 261 + if (err) 262 + break; 263 + 264 + prev = root; 265 + total += drm_buddy_block_size(mm, root); 266 + } 267 + 268 + if (!err) { 269 + if (total != mm->size) { 270 + pr_err("expected mm size=%llx, found=%llx\n", mm->size, 271 + total); 272 + err = -EINVAL; 273 + } 274 + return err; 275 + } 276 + 277 + if (prev) { 278 + pr_err("prev root(%u), dump:\n", i - 1); 279 + igt_dump_block(mm, prev); 280 + } 281 + 282 + if (root) { 283 + pr_err("bad root(%u), dump:\n", i); 284 + igt_dump_block(mm, root); 285 + } 286 + 287 + return err; 288 + } 289 + 290 + static void igt_mm_config(u64 *size, u64 *chunk_size) 291 + { 292 + DRM_RND_STATE(prng, random_seed); 293 + u32 s, ms; 294 + 295 + /* Nothing fancy, just try to get an interesting bit pattern */ 296 + 297 + prandom_seed_state(&prng, random_seed); 298 + 299 + /* Let size be a random number of pages up to 8 GB (2M pages) */ 300 + s = 1 + drm_prandom_u32_max_state((BIT(33 - 12)) - 1, &prng); 301 + /* Let the chunk size be a random power of 2 less than size */ 302 + ms = BIT(drm_prandom_u32_max_state(ilog2(s), &prng)); 303 + /* Round size down to the chunk size */ 304 + s &= -ms; 305 + 306 + /* Convert from pages to bytes */ 307 + *chunk_size = (u64)ms << 12; 308 + *size = (u64)s << 12; 309 + } 310 + 311 + static int igt_buddy_alloc_range(void *arg) 312 + { 313 + unsigned long flags = DRM_BUDDY_RANGE_ALLOCATION; 314 + u64 offset, size, rem, chunk_size, end; 315 + unsigned long page_num; 316 + struct drm_buddy mm; 317 + LIST_HEAD(blocks); 318 + int err; 319 + 320 + igt_mm_config(&size, &chunk_size); 321 + 322 + err = drm_buddy_init(&mm, size, chunk_size); 323 + if (err) { 324 + pr_err("buddy_init failed(%d)\n", err); 325 + return err; 326 + } 327 + 328 + err = igt_check_mm(&mm); 329 + if (err) { 330 + pr_err("pre-mm check failed, abort, abort, abort!\n"); 331 + goto err_fini; 332 + } 333 + 334 + rem = mm.size; 335 + offset = 0; 336 + 337 + for_each_prime_number_from(page_num, 1, ULONG_MAX - 1) { 338 + struct drm_buddy_block *block; 339 + LIST_HEAD(tmp); 340 + 341 + size = min(page_num * mm.chunk_size, rem); 342 + end = offset + size; 343 + 344 + err = drm_buddy_alloc_blocks(&mm, offset, end, size, mm.chunk_size, &tmp, flags); 345 + if (err) { 346 + if (err == -ENOMEM) { 347 + pr_info("alloc_range hit -ENOMEM with size=%llx\n", 348 + size); 349 + } else { 350 + pr_err("alloc_range with offset=%llx, size=%llx failed(%d)\n", 351 + offset, size, err); 352 + } 353 + 354 + break; 355 + } 356 + 357 + block = list_first_entry_or_null(&tmp, 358 + struct drm_buddy_block, 359 + link); 360 + if (!block) { 361 + pr_err("alloc_range has no blocks\n"); 362 + err = -EINVAL; 363 + break; 364 + } 365 + 366 + if (drm_buddy_block_offset(block) != offset) { 367 + pr_err("alloc_range start offset mismatch, found=%llx, expected=%llx\n", 368 + drm_buddy_block_offset(block), offset); 369 + err = -EINVAL; 370 + } 371 + 372 + if (!err) 373 + err = igt_check_blocks(&mm, &tmp, size, true); 374 + 375 + list_splice_tail(&tmp, &blocks); 376 + 377 + if (err) 378 + break; 379 + 380 + offset += size; 381 + 382 + rem -= size; 383 + if (!rem) 384 + break; 385 + 386 + cond_resched(); 387 + } 388 + 389 + if (err == -ENOMEM) 390 + err = 0; 391 + 392 + drm_buddy_free_list(&mm, &blocks); 393 + 394 + if (!err) { 395 + err = igt_check_mm(&mm); 396 + if (err) 397 + pr_err("post-mm check failed\n"); 398 + } 399 + 400 + err_fini: 401 + drm_buddy_fini(&mm); 402 + 403 + return err; 404 + } 19 405 20 406 static int igt_buddy_alloc_limit(void *arg) 21 407 {
+3
include/drm/drm_buddy.h
··· 134 134 135 135 void drm_buddy_fini(struct drm_buddy *mm); 136 136 137 + struct drm_buddy_block * 138 + drm_get_buddy(struct drm_buddy_block *block); 139 + 137 140 int drm_buddy_alloc_blocks(struct drm_buddy *mm, 138 141 u64 start, u64 end, u64 size, 139 142 u64 min_page_size,