1 // SPDX-License-Identifier: GPL-2.0-only OR MIT
2 /* Copyright (c) 2023 Imagination Technologies Ltd. */
3
4 #include "pvr_free_list.h"
5 #include "pvr_gem.h"
6 #include "pvr_hwrt.h"
7 #include "pvr_rogue_fwif.h"
8 #include "pvr_vm.h"
9
10 #include <drm/drm_gem.h>
11 #include <drm/drm_print.h>
12 #include <linux/slab.h>
13 #include <linux/xarray.h>
14 #include <uapi/drm/pvr_drm.h>
15
16 #define FREE_LIST_ENTRY_SIZE sizeof(u32)
17
18 #define FREE_LIST_ALIGNMENT \
19 ((ROGUE_BIF_PM_FREELIST_BASE_ADDR_ALIGNSIZE / FREE_LIST_ENTRY_SIZE) - 1)
20
21 #define FREE_LIST_MIN_PAGES 50
22 #define FREE_LIST_MIN_PAGES_BRN66011 40
23 #define FREE_LIST_MIN_PAGES_ROGUEXE 25
24
25 /**
26 * pvr_get_free_list_min_pages() - Get minimum free list size for this device
27 * @pvr_dev: Device pointer.
28 *
29 * Returns:
30 * * Minimum free list size, in PM physical pages.
31 */
32 u32
pvr_get_free_list_min_pages(struct pvr_device * pvr_dev)33 pvr_get_free_list_min_pages(struct pvr_device *pvr_dev)
34 {
35 u32 value;
36
37 if (PVR_HAS_FEATURE(pvr_dev, roguexe)) {
38 if (PVR_HAS_QUIRK(pvr_dev, 66011))
39 value = FREE_LIST_MIN_PAGES_BRN66011;
40 else
41 value = FREE_LIST_MIN_PAGES_ROGUEXE;
42 } else {
43 value = FREE_LIST_MIN_PAGES;
44 }
45
46 return value;
47 }
48
49 static int
free_list_create_kernel_structure(struct pvr_file * pvr_file,struct drm_pvr_ioctl_create_free_list_args * args,struct pvr_free_list * free_list)50 free_list_create_kernel_structure(struct pvr_file *pvr_file,
51 struct drm_pvr_ioctl_create_free_list_args *args,
52 struct pvr_free_list *free_list)
53 {
54 struct pvr_gem_object *free_list_obj;
55 struct pvr_vm_context *vm_ctx;
56 u64 free_list_size;
57 int err;
58
59 if (args->grow_threshold > 100 ||
60 args->initial_num_pages > args->max_num_pages ||
61 args->grow_num_pages > args->max_num_pages ||
62 args->max_num_pages == 0 ||
63 (args->initial_num_pages < args->max_num_pages && !args->grow_num_pages) ||
64 (args->initial_num_pages == args->max_num_pages && args->grow_num_pages))
65 return -EINVAL;
66
67 if ((args->initial_num_pages & FREE_LIST_ALIGNMENT) ||
68 (args->max_num_pages & FREE_LIST_ALIGNMENT) ||
69 (args->grow_num_pages & FREE_LIST_ALIGNMENT))
70 return -EINVAL;
71
72 vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle);
73 if (!vm_ctx)
74 return -EINVAL;
75
76 free_list_obj = pvr_vm_find_gem_object(vm_ctx, args->free_list_gpu_addr,
77 NULL, &free_list_size);
78 if (!free_list_obj) {
79 err = -EINVAL;
80 goto err_put_vm_context;
81 }
82
83 if ((free_list_obj->flags & DRM_PVR_BO_ALLOW_CPU_USERSPACE_ACCESS) ||
84 !(free_list_obj->flags & DRM_PVR_BO_PM_FW_PROTECT) ||
85 free_list_size < (args->max_num_pages * FREE_LIST_ENTRY_SIZE)) {
86 err = -EINVAL;
87 goto err_put_free_list_obj;
88 }
89
90 free_list->pvr_dev = pvr_file->pvr_dev;
91 free_list->current_pages = 0;
92 free_list->max_pages = args->max_num_pages;
93 free_list->grow_pages = args->grow_num_pages;
94 free_list->grow_threshold = args->grow_threshold;
95 free_list->obj = free_list_obj;
96 free_list->free_list_gpu_addr = args->free_list_gpu_addr;
97 free_list->initial_num_pages = args->initial_num_pages;
98
99 pvr_vm_context_put(vm_ctx);
100
101 return 0;
102
103 err_put_free_list_obj:
104 pvr_gem_object_put(free_list_obj);
105
106 err_put_vm_context:
107 pvr_vm_context_put(vm_ctx);
108
109 return err;
110 }
111
112 static void
free_list_destroy_kernel_structure(struct pvr_free_list * free_list)113 free_list_destroy_kernel_structure(struct pvr_free_list *free_list)
114 {
115 WARN_ON(!list_empty(&free_list->hwrt_list));
116
117 pvr_gem_object_put(free_list->obj);
118 }
119
120 /**
121 * calculate_free_list_ready_pages_locked() - Function to work out the number of free
122 * list pages to reserve for growing within
123 * the FW without having to wait for the
124 * host to progress a grow request
125 * @free_list: Pointer to free list.
126 * @pages: Total pages currently in free list.
127 *
128 * If the threshold or grow size means less than the alignment size (4 pages on
129 * Rogue), then the feature is not used.
130 *
131 * Caller must hold &free_list->lock.
132 *
133 * Return: number of pages to reserve.
134 */
135 static u32
calculate_free_list_ready_pages_locked(struct pvr_free_list * free_list,u32 pages)136 calculate_free_list_ready_pages_locked(struct pvr_free_list *free_list, u32 pages)
137 {
138 u32 ready_pages;
139
140 lockdep_assert_held(&free_list->lock);
141
142 ready_pages = ((pages * free_list->grow_threshold) / 100);
143
144 /* The number of pages must be less than the grow size. */
145 ready_pages = min(ready_pages, free_list->grow_pages);
146
147 /*
148 * The number of pages must be a multiple of the free list align size.
149 */
150 ready_pages &= ~FREE_LIST_ALIGNMENT;
151
152 return ready_pages;
153 }
154
155 static u32
calculate_free_list_ready_pages(struct pvr_free_list * free_list,u32 pages)156 calculate_free_list_ready_pages(struct pvr_free_list *free_list, u32 pages)
157 {
158 u32 ret;
159
160 mutex_lock(&free_list->lock);
161
162 ret = calculate_free_list_ready_pages_locked(free_list, pages);
163
164 mutex_unlock(&free_list->lock);
165
166 return ret;
167 }
168
169 static void
free_list_fw_init(void * cpu_ptr,void * priv)170 free_list_fw_init(void *cpu_ptr, void *priv)
171 {
172 struct rogue_fwif_freelist *fw_data = cpu_ptr;
173 struct pvr_free_list *free_list = priv;
174 u32 ready_pages;
175
176 /* Fill out FW structure */
177 ready_pages = calculate_free_list_ready_pages(free_list,
178 free_list->initial_num_pages);
179
180 fw_data->max_pages = free_list->max_pages;
181 fw_data->current_pages = free_list->initial_num_pages - ready_pages;
182 fw_data->grow_pages = free_list->grow_pages;
183 fw_data->ready_pages = ready_pages;
184 fw_data->freelist_id = free_list->fw_id;
185 fw_data->grow_pending = false;
186 fw_data->current_stack_top = fw_data->current_pages - 1;
187 fw_data->freelist_dev_addr = free_list->free_list_gpu_addr;
188 fw_data->current_dev_addr = (fw_data->freelist_dev_addr +
189 ((fw_data->max_pages - fw_data->current_pages) *
190 FREE_LIST_ENTRY_SIZE)) &
191 ~((u64)ROGUE_BIF_PM_FREELIST_BASE_ADDR_ALIGNSIZE - 1);
192 }
193
194 static int
free_list_create_fw_structure(struct pvr_file * pvr_file,struct drm_pvr_ioctl_create_free_list_args * args,struct pvr_free_list * free_list)195 free_list_create_fw_structure(struct pvr_file *pvr_file,
196 struct drm_pvr_ioctl_create_free_list_args *args,
197 struct pvr_free_list *free_list)
198 {
199 struct pvr_device *pvr_dev = pvr_file->pvr_dev;
200
201 /*
202 * Create and map the FW structure so we can initialise it. This is not
203 * accessed on the CPU side post-initialisation so the mapping lifetime
204 * is only for this function.
205 */
206 free_list->fw_data = pvr_fw_object_create_and_map(pvr_dev, sizeof(*free_list->fw_data),
207 PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
208 free_list_fw_init, free_list,
209 &free_list->fw_obj);
210 if (IS_ERR(free_list->fw_data))
211 return PTR_ERR(free_list->fw_data);
212
213 return 0;
214 }
215
216 static void
free_list_destroy_fw_structure(struct pvr_free_list * free_list)217 free_list_destroy_fw_structure(struct pvr_free_list *free_list)
218 {
219 pvr_fw_object_unmap_and_destroy(free_list->fw_obj);
220 }
221
222 static int
pvr_free_list_insert_pages_locked(struct pvr_free_list * free_list,struct sg_table * sgt,u32 offset,u32 num_pages)223 pvr_free_list_insert_pages_locked(struct pvr_free_list *free_list,
224 struct sg_table *sgt, u32 offset, u32 num_pages)
225 {
226 struct sg_dma_page_iter dma_iter;
227 u32 *page_list;
228
229 lockdep_assert_held(&free_list->lock);
230
231 page_list = pvr_gem_object_vmap(free_list->obj);
232 if (IS_ERR(page_list))
233 return PTR_ERR(page_list);
234
235 offset /= FREE_LIST_ENTRY_SIZE;
236 /* clang-format off */
237 for_each_sgtable_dma_page(sgt, &dma_iter, 0) {
238 dma_addr_t dma_addr = sg_page_iter_dma_address(&dma_iter);
239 u64 dma_pfn = dma_addr >>
240 ROGUE_BIF_PM_PHYSICAL_PAGE_ALIGNSHIFT;
241
242 BUILD_BUG_ON(ROGUE_BIF_PM_PHYSICAL_PAGE_SIZE > PAGE_SIZE);
243
244 for (u32 dma_addr_offset = 0; dma_addr_offset < PAGE_SIZE;
245 dma_addr_offset += ROGUE_BIF_PM_PHYSICAL_PAGE_SIZE) {
246 WARN_ON_ONCE(dma_pfn >> 32);
247
248 page_list[offset++] = (u32)dma_pfn;
249 dma_pfn++;
250
251 num_pages--;
252 if (!num_pages)
253 break;
254 }
255
256 if (!num_pages)
257 break;
258 }
259 /* clang-format on */
260
261 /* Make sure our free_list update is flushed. */
262 wmb();
263
264 pvr_gem_object_vunmap(free_list->obj);
265
266 return 0;
267 }
268
269 static int
pvr_free_list_insert_node_locked(struct pvr_free_list_node * free_list_node)270 pvr_free_list_insert_node_locked(struct pvr_free_list_node *free_list_node)
271 {
272 struct pvr_free_list *free_list = free_list_node->free_list;
273 struct sg_table *sgt;
274 u32 start_page;
275 u32 offset;
276 int err;
277
278 lockdep_assert_held(&free_list->lock);
279
280 start_page = free_list->max_pages - free_list->current_pages -
281 free_list_node->num_pages;
282 offset = (start_page * FREE_LIST_ENTRY_SIZE) &
283 ~((u64)ROGUE_BIF_PM_FREELIST_BASE_ADDR_ALIGNSIZE - 1);
284
285 sgt = drm_gem_shmem_get_pages_sgt(&free_list_node->mem_obj->base);
286 if (WARN_ON(IS_ERR(sgt)))
287 return PTR_ERR(sgt);
288
289 err = pvr_free_list_insert_pages_locked(free_list, sgt,
290 offset, free_list_node->num_pages);
291 if (!err)
292 free_list->current_pages += free_list_node->num_pages;
293
294 return err;
295 }
296
297 static int
pvr_free_list_grow(struct pvr_free_list * free_list,u32 num_pages)298 pvr_free_list_grow(struct pvr_free_list *free_list, u32 num_pages)
299 {
300 struct pvr_device *pvr_dev = free_list->pvr_dev;
301 struct pvr_free_list_node *free_list_node;
302 int err;
303
304 mutex_lock(&free_list->lock);
305
306 if (num_pages & FREE_LIST_ALIGNMENT) {
307 err = -EINVAL;
308 goto err_unlock;
309 }
310
311 free_list_node = kzalloc_obj(*free_list_node);
312 if (!free_list_node) {
313 err = -ENOMEM;
314 goto err_unlock;
315 }
316
317 free_list_node->num_pages = num_pages;
318 free_list_node->free_list = free_list;
319
320 free_list_node->mem_obj = pvr_gem_object_create(pvr_dev,
321 num_pages <<
322 ROGUE_BIF_PM_PHYSICAL_PAGE_ALIGNSHIFT,
323 PVR_BO_FW_FLAGS_DEVICE_CACHED);
324 if (IS_ERR(free_list_node->mem_obj)) {
325 err = PTR_ERR(free_list_node->mem_obj);
326 goto err_free;
327 }
328
329 err = pvr_free_list_insert_node_locked(free_list_node);
330 if (err)
331 goto err_destroy_gem_object;
332
333 list_add_tail(&free_list_node->node, &free_list->mem_block_list);
334
335 /*
336 * Reserve a number ready pages to allow the FW to process OOM quickly
337 * and asynchronously request a grow.
338 */
339 free_list->ready_pages =
340 calculate_free_list_ready_pages_locked(free_list,
341 free_list->current_pages);
342 free_list->current_pages -= free_list->ready_pages;
343
344 mutex_unlock(&free_list->lock);
345
346 return 0;
347
348 err_destroy_gem_object:
349 pvr_gem_object_put(free_list_node->mem_obj);
350
351 err_free:
352 kfree(free_list_node);
353
354 err_unlock:
355 mutex_unlock(&free_list->lock);
356
357 return err;
358 }
359
pvr_free_list_process_grow_req(struct pvr_device * pvr_dev,struct rogue_fwif_fwccb_cmd_freelist_gs_data * req)360 void pvr_free_list_process_grow_req(struct pvr_device *pvr_dev,
361 struct rogue_fwif_fwccb_cmd_freelist_gs_data *req)
362 {
363 struct pvr_free_list *free_list = pvr_free_list_lookup_id(pvr_dev, req->freelist_id);
364 struct rogue_fwif_kccb_cmd resp_cmd = {
365 .cmd_type = ROGUE_FWIF_KCCB_CMD_FREELIST_GROW_UPDATE,
366 };
367 struct rogue_fwif_freelist_gs_data *resp = &resp_cmd.cmd_data.free_list_gs_data;
368 u32 grow_pages = 0;
369
370 /* If we don't have a freelist registered for this ID, we can't do much. */
371 if (WARN_ON(!free_list))
372 return;
373
374 /* Since the FW made the request, it has already consumed the ready pages,
375 * update the host struct.
376 */
377 free_list->current_pages += free_list->ready_pages;
378 free_list->ready_pages = 0;
379
380 /* If the grow succeeds, update the grow_pages argument. */
381 if (!pvr_free_list_grow(free_list, free_list->grow_pages))
382 grow_pages = free_list->grow_pages;
383
384 /* Now prepare the response and send it back to the FW. */
385 pvr_fw_object_get_fw_addr(free_list->fw_obj, &resp->freelist_fw_addr);
386 resp->delta_pages = grow_pages;
387 resp->new_pages = free_list->current_pages + free_list->ready_pages;
388 resp->ready_pages = free_list->ready_pages;
389 pvr_free_list_put(free_list);
390
391 WARN_ON(pvr_kccb_send_cmd(pvr_dev, &resp_cmd, NULL));
392 }
393
394 static void
pvr_free_list_free_node(struct pvr_free_list_node * free_list_node)395 pvr_free_list_free_node(struct pvr_free_list_node *free_list_node)
396 {
397 pvr_gem_object_put(free_list_node->mem_obj);
398
399 kfree(free_list_node);
400 }
401
402 /**
403 * pvr_free_list_create() - Create a new free list and return an object pointer
404 * @pvr_file: Pointer to pvr_file structure.
405 * @args: Creation arguments from userspace.
406 *
407 * Return:
408 * * Pointer to new free_list, or
409 * * ERR_PTR(-%ENOMEM) on out of memory.
410 */
411 struct pvr_free_list *
pvr_free_list_create(struct pvr_file * pvr_file,struct drm_pvr_ioctl_create_free_list_args * args)412 pvr_free_list_create(struct pvr_file *pvr_file,
413 struct drm_pvr_ioctl_create_free_list_args *args)
414 {
415 struct pvr_free_list *free_list;
416 int err;
417
418 /* Create and fill out the kernel structure */
419 free_list = kzalloc_obj(*free_list);
420
421 if (!free_list)
422 return ERR_PTR(-ENOMEM);
423
424 kref_init(&free_list->ref_count);
425 INIT_LIST_HEAD(&free_list->mem_block_list);
426 INIT_LIST_HEAD(&free_list->hwrt_list);
427 mutex_init(&free_list->lock);
428
429 err = free_list_create_kernel_structure(pvr_file, args, free_list);
430 if (err < 0)
431 goto err_free;
432
433 /* Allocate global object ID for firmware. */
434 err = xa_alloc(&pvr_file->pvr_dev->free_list_ids,
435 &free_list->fw_id,
436 free_list,
437 xa_limit_32b,
438 GFP_KERNEL);
439 if (err)
440 goto err_destroy_kernel_structure;
441
442 err = free_list_create_fw_structure(pvr_file, args, free_list);
443 if (err < 0)
444 goto err_free_fw_id;
445
446 err = pvr_free_list_grow(free_list, args->initial_num_pages);
447 if (err < 0)
448 goto err_fw_struct_cleanup;
449
450 return free_list;
451
452 err_fw_struct_cleanup:
453 WARN_ON(pvr_fw_structure_cleanup(free_list->pvr_dev,
454 ROGUE_FWIF_CLEANUP_FREELIST,
455 free_list->fw_obj, 0));
456
457 err_free_fw_id:
458 xa_erase(&free_list->pvr_dev->free_list_ids, free_list->fw_id);
459
460 err_destroy_kernel_structure:
461 free_list_destroy_kernel_structure(free_list);
462
463 err_free:
464 mutex_destroy(&free_list->lock);
465 kfree(free_list);
466
467 return ERR_PTR(err);
468 }
469
470 static void
pvr_free_list_release(struct kref * ref_count)471 pvr_free_list_release(struct kref *ref_count)
472 {
473 struct pvr_free_list *free_list =
474 container_of(ref_count, struct pvr_free_list, ref_count);
475 struct list_head *pos, *n;
476 int err;
477
478 xa_erase(&free_list->pvr_dev->free_list_ids, free_list->fw_id);
479
480 err = pvr_fw_structure_cleanup(free_list->pvr_dev,
481 ROGUE_FWIF_CLEANUP_FREELIST,
482 free_list->fw_obj, 0);
483 if (err == -EBUSY) {
484 /* Flush the FWCCB to process any HWR or freelist reconstruction
485 * request that might keep the freelist busy, and try again.
486 */
487 pvr_fwccb_process(free_list->pvr_dev);
488 err = pvr_fw_structure_cleanup(free_list->pvr_dev,
489 ROGUE_FWIF_CLEANUP_FREELIST,
490 free_list->fw_obj, 0);
491 }
492
493 WARN_ON(err);
494
495 /* clang-format off */
496 list_for_each_safe(pos, n, &free_list->mem_block_list) {
497 struct pvr_free_list_node *free_list_node =
498 container_of(pos, struct pvr_free_list_node, node);
499
500 list_del(pos);
501 pvr_free_list_free_node(free_list_node);
502 }
503 /* clang-format on */
504
505 free_list_destroy_kernel_structure(free_list);
506 free_list_destroy_fw_structure(free_list);
507 mutex_destroy(&free_list->lock);
508 kfree(free_list);
509 }
510
511 /**
512 * pvr_destroy_free_lists_for_file: Destroy any free lists associated with the
513 * given file.
514 * @pvr_file: Pointer to pvr_file structure.
515 *
516 * Removes all free lists associated with @pvr_file from the device free_list
517 * list and drops initial references. Free lists will then be destroyed once
518 * all outstanding references are dropped.
519 */
pvr_destroy_free_lists_for_file(struct pvr_file * pvr_file)520 void pvr_destroy_free_lists_for_file(struct pvr_file *pvr_file)
521 {
522 struct pvr_free_list *free_list;
523 unsigned long handle;
524
525 xa_for_each(&pvr_file->free_list_handles, handle, free_list) {
526 (void)free_list;
527 pvr_free_list_put(xa_erase(&pvr_file->free_list_handles, handle));
528 }
529 }
530
531 /**
532 * pvr_free_list_put() - Release reference on free list
533 * @free_list: Pointer to list to release reference on
534 */
535 void
pvr_free_list_put(struct pvr_free_list * free_list)536 pvr_free_list_put(struct pvr_free_list *free_list)
537 {
538 if (free_list)
539 kref_put(&free_list->ref_count, pvr_free_list_release);
540 }
541
pvr_free_list_add_hwrt(struct pvr_free_list * free_list,struct pvr_hwrt_data * hwrt_data)542 void pvr_free_list_add_hwrt(struct pvr_free_list *free_list, struct pvr_hwrt_data *hwrt_data)
543 {
544 mutex_lock(&free_list->lock);
545
546 list_add_tail(&hwrt_data->freelist_node, &free_list->hwrt_list);
547
548 mutex_unlock(&free_list->lock);
549 }
550
pvr_free_list_remove_hwrt(struct pvr_free_list * free_list,struct pvr_hwrt_data * hwrt_data)551 void pvr_free_list_remove_hwrt(struct pvr_free_list *free_list, struct pvr_hwrt_data *hwrt_data)
552 {
553 mutex_lock(&free_list->lock);
554
555 list_del(&hwrt_data->freelist_node);
556
557 mutex_unlock(&free_list->lock);
558 }
559
560 static void
pvr_free_list_reconstruct(struct pvr_device * pvr_dev,u32 freelist_id)561 pvr_free_list_reconstruct(struct pvr_device *pvr_dev, u32 freelist_id)
562 {
563 struct pvr_free_list *free_list = pvr_free_list_lookup_id(pvr_dev, freelist_id);
564 struct pvr_free_list_node *free_list_node;
565 struct rogue_fwif_freelist *fw_data;
566 struct pvr_hwrt_data *hwrt_data;
567
568 if (!free_list)
569 return;
570
571 mutex_lock(&free_list->lock);
572
573 /* Rebuild the free list based on the memory block list. */
574 free_list->current_pages = 0;
575
576 list_for_each_entry(free_list_node, &free_list->mem_block_list, node)
577 WARN_ON(pvr_free_list_insert_node_locked(free_list_node));
578
579 /*
580 * Remove the ready pages, which are reserved to allow the FW to process OOM quickly and
581 * asynchronously request a grow.
582 */
583 free_list->current_pages -= free_list->ready_pages;
584
585 fw_data = free_list->fw_data;
586 fw_data->current_stack_top = fw_data->current_pages - 1;
587 fw_data->allocated_page_count = 0;
588 fw_data->allocated_mmu_page_count = 0;
589
590 /* Reset the state of any associated HWRTs. */
591 list_for_each_entry(hwrt_data, &free_list->hwrt_list, freelist_node) {
592 struct rogue_fwif_hwrtdata *hwrt_fw_data = pvr_fw_object_vmap(hwrt_data->fw_obj);
593
594 if (!WARN_ON(IS_ERR(hwrt_fw_data))) {
595 hwrt_fw_data->state = ROGUE_FWIF_RTDATA_STATE_HWR;
596 hwrt_fw_data->hwrt_data_flags &= ~HWRTDATA_HAS_LAST_GEOM;
597 }
598
599 pvr_fw_object_vunmap(hwrt_data->fw_obj);
600 }
601
602 mutex_unlock(&free_list->lock);
603
604 pvr_free_list_put(free_list);
605 }
606
607 void
pvr_free_list_process_reconstruct_req(struct pvr_device * pvr_dev,struct rogue_fwif_fwccb_cmd_freelists_reconstruction_data * req)608 pvr_free_list_process_reconstruct_req(struct pvr_device *pvr_dev,
609 struct rogue_fwif_fwccb_cmd_freelists_reconstruction_data *req)
610 {
611 struct rogue_fwif_kccb_cmd resp_cmd = {
612 .cmd_type = ROGUE_FWIF_KCCB_CMD_FREELISTS_RECONSTRUCTION_UPDATE,
613 };
614 struct rogue_fwif_freelists_reconstruction_data *resp =
615 &resp_cmd.cmd_data.free_lists_reconstruction_data;
616 u32 count = min_t(u32, req->freelist_count,
617 ARRAY_SIZE(req->freelist_ids));
618
619 if (count != req->freelist_count) {
620 drm_warn_once(from_pvr_device(pvr_dev),
621 "Requested reconstruction of %u freelists, limiting to %u\n",
622 req->freelist_count, count);
623 }
624
625 for (u32 i = 0; i < count; i++)
626 pvr_free_list_reconstruct(pvr_dev, req->freelist_ids[i]);
627
628 resp->freelist_count = count;
629 memcpy(resp->freelist_ids, req->freelist_ids,
630 count * sizeof(resp->freelist_ids[0]));
631
632 WARN_ON(pvr_kccb_send_cmd(pvr_dev, &resp_cmd, NULL));
633 }
634