summaryrefslogtreecommitdiff
path: root/mali_kbase/csf/mali_kbase_csf_tiler_heap.c
blob: 85babf90bfde4b3dfcb8c6e50d59c509c492a2bf (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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
/*
 *
 * (C) COPYRIGHT 2019-2021 ARM Limited. All rights reserved.
 *
 * This program is free software and is provided to you under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation, and any use by you of this program is subject to the terms
 * of such GNU license.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you can access it online at
 * http://www.gnu.org/licenses/gpl-2.0.html.
 *
 */

#include <tl/mali_kbase_tracepoints.h>

#include "mali_kbase_csf_tiler_heap.h"
#include "mali_kbase_csf_tiler_heap_def.h"
#include "mali_kbase_csf_heap_context_alloc.h"

/**
 * encode_chunk_ptr - Encode the address and size of a chunk as an integer.
 *
 * @chunk_size: Size of a tiler heap chunk, in bytes.
 * @chunk_addr: GPU virtual address of the same tiler heap chunk.
 *
 * The size and address of the next chunk in a list are packed into a single
 * 64-bit value for storage in a chunk's header. This function returns that
 * value.
 *
 * Return: Next chunk pointer suitable for writing into a chunk header.
 */
static u64 encode_chunk_ptr(u32 const chunk_size, u64 const chunk_addr)
{
	u64 encoded_size, encoded_addr;

	WARN_ON(chunk_size & ~CHUNK_SIZE_MASK);
	WARN_ON(chunk_addr & ~CHUNK_ADDR_MASK);

	encoded_size =
		(u64)(chunk_size >> CHUNK_HDR_NEXT_SIZE_ENCODE_SHIFT) <<
		CHUNK_HDR_NEXT_SIZE_POS;

	encoded_addr =
		(chunk_addr >> CHUNK_HDR_NEXT_ADDR_ENCODE_SHIFT) <<
		CHUNK_HDR_NEXT_ADDR_POS;

	return (encoded_size & CHUNK_HDR_NEXT_SIZE_MASK) |
		(encoded_addr & CHUNK_HDR_NEXT_ADDR_MASK);
}

/**
 * get_last_chunk - Get the last chunk of a tiler heap
 *
 * @heap:  Pointer to the tiler heap.
 *
 * Return: The address of the most recently-linked chunk, or NULL if none.
 */
static struct kbase_csf_tiler_heap_chunk *get_last_chunk(
	struct kbase_csf_tiler_heap *const heap)
{
	if (list_empty(&heap->chunks_list))
		return NULL;

	return list_last_entry(&heap->chunks_list,
		struct kbase_csf_tiler_heap_chunk, link);
}

/**
 * link_chunk - Link a chunk into a tiler heap
 *
 * @heap:  Pointer to the tiler heap.
 * @chunk: Pointer to the heap chunk to be linked.
 *
 * Unless the @chunk is the first in the kernel's list of chunks belonging to
 * a given tiler heap, this function stores the size and address of the @chunk
 * in the header of the preceding chunk. This requires the GPU memory region
 * containing the header to be mapped temporarily, which can fail.
 *
 * Return: 0 if successful or a negative error code on failure.
 */
static int link_chunk(struct kbase_csf_tiler_heap *const heap,
	struct kbase_csf_tiler_heap_chunk *const chunk)
{
	struct kbase_csf_tiler_heap_chunk *const prev = get_last_chunk(heap);

	if (prev) {
		struct kbase_context *const kctx = heap->kctx;
		struct kbase_vmap_struct map;
		u64 *const prev_hdr = kbase_vmap_prot(kctx, prev->gpu_va,
			sizeof(*prev_hdr), KBASE_REG_CPU_WR, &map);

		if (unlikely(!prev_hdr)) {
			dev_err(kctx->kbdev->dev,
				"Failed to map tiler heap chunk 0x%llX\n",
				prev->gpu_va);
			return -ENOMEM;
		}

		*prev_hdr = encode_chunk_ptr(heap->chunk_size, chunk->gpu_va);
		kbase_vunmap(kctx, &map);

		dev_dbg(kctx->kbdev->dev,
			"Linked tiler heap chunks, 0x%llX -> 0x%llX\n",
			prev->gpu_va, chunk->gpu_va);
	}

	return 0;
}

/**
 * init_chunk - Initialize and link a tiler heap chunk
 *
 * @heap:  Pointer to the tiler heap.
 * @chunk: Pointer to the heap chunk to be initialized and linked.
 * @link_with_prev: Flag to indicate if the new chunk needs to be linked with
 *                  the previously allocated chunk.
 *
 * Zero-initialize a new chunk's header (including its pointer to the next
 * chunk, which doesn't exist yet) and then update the previous chunk's
 * header to link the new chunk into the chunk list.
 *
 * Return: 0 if successful or a negative error code on failure.
 */
static int init_chunk(struct kbase_csf_tiler_heap *const heap,
	struct kbase_csf_tiler_heap_chunk *const chunk, bool link_with_prev)
{
	struct kbase_vmap_struct map;
	struct u64 *chunk_hdr = NULL;
	struct kbase_context *const kctx = heap->kctx;

	if (unlikely(chunk->gpu_va & ~CHUNK_ADDR_MASK)) {
		dev_err(kctx->kbdev->dev,
			"Tiler heap chunk address is unusable\n");
		return -EINVAL;
	}

	chunk_hdr = kbase_vmap_prot(kctx,
		chunk->gpu_va, CHUNK_HDR_SIZE, KBASE_REG_CPU_WR, &map);

	if (unlikely(!chunk_hdr)) {
		dev_err(kctx->kbdev->dev,
			"Failed to map a tiler heap chunk header\n");
		return -ENOMEM;
	}

	memset(chunk_hdr, 0, CHUNK_HDR_SIZE);
	kbase_vunmap(kctx, &map);

	if (link_with_prev)
		return link_chunk(heap, chunk);
	else
		return 0;
}

/**
 * create_chunk - Create a tiler heap chunk
 *
 * @heap: Pointer to the tiler heap for which to allocate memory.
 * @link_with_prev: Flag to indicate if the chunk to be allocated needs to be
 *                  linked with the previously allocated chunk.
 *
 * This function allocates a chunk of memory for a tiler heap and adds it to
 * the end of the list of chunks associated with that heap. The size of the
 * chunk is not a parameter because it is configured per-heap not per-chunk.
 *
 * Return: 0 if successful or a negative error code on failure.
 */
static int create_chunk(struct kbase_csf_tiler_heap *const heap,
			bool link_with_prev)
{
	int err = 0;
	struct kbase_context *const kctx = heap->kctx;
	u64 nr_pages = PFN_UP(heap->chunk_size);
	u64 flags = BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_GPU_WR |
		BASE_MEM_PROT_CPU_WR | BASEP_MEM_NO_USER_FREE |
		BASE_MEM_COHERENT_LOCAL;
	struct kbase_csf_tiler_heap_chunk *chunk = NULL;

	/* Calls to this function are inherently synchronous, with respect to
	 * MMU operations.
	 */
	const enum kbase_caller_mmu_sync_info mmu_sync_info = CALLER_MMU_SYNC;

	flags |= kbase_mem_group_id_set(kctx->jit_group_id);

#if defined(CONFIG_MALI_DEBUG) || defined(CONFIG_MALI_VECTOR_DUMP)
	flags |= BASE_MEM_PROT_CPU_RD;
#endif

	chunk = kzalloc(sizeof(*chunk), GFP_KERNEL);
	if (unlikely(!chunk)) {
		dev_err(kctx->kbdev->dev,
			"No kernel memory for a new tiler heap chunk\n");
		return -ENOMEM;
	}

	/* Allocate GPU memory for the new chunk. */
	INIT_LIST_HEAD(&chunk->link);
	chunk->region =
		kbase_mem_alloc(kctx, nr_pages, nr_pages, 0, &flags, &chunk->gpu_va, mmu_sync_info);

	if (unlikely(!chunk->region)) {
		dev_err(kctx->kbdev->dev,
			"Failed to allocate a tiler heap chunk\n");
		err = -ENOMEM;
	} else {
		err = init_chunk(heap, chunk, link_with_prev);
		if (unlikely(err)) {
			kbase_gpu_vm_lock(kctx);
			chunk->region->flags &= ~KBASE_REG_NO_USER_FREE;
			kbase_mem_free_region(kctx, chunk->region);
			kbase_gpu_vm_unlock(kctx);
		}
	}

	if (unlikely(err)) {
		kfree(chunk);
	} else {
		list_add_tail(&chunk->link, &heap->chunks_list);
		heap->chunk_count++;

		dev_dbg(kctx->kbdev->dev, "Created tiler heap chunk 0x%llX\n",
			chunk->gpu_va);
	}

	return err;
}

/**
 * delete_chunk - Delete a tiler heap chunk
 *
 * @heap:  Pointer to the tiler heap for which @chunk was allocated.
 * @chunk: Pointer to a chunk to be deleted.
 *
 * This function frees a tiler heap chunk previously allocated by @create_chunk
 * and removes it from the list of chunks associated with the heap.
 *
 * WARNING: The deleted chunk is not unlinked from the list of chunks used by
 *          the GPU, therefore it is only safe to use this function when
 *          deleting a heap.
 */
static void delete_chunk(struct kbase_csf_tiler_heap *const heap,
	struct kbase_csf_tiler_heap_chunk *const chunk)
{
	struct kbase_context *const kctx = heap->kctx;

	kbase_gpu_vm_lock(kctx);
	chunk->region->flags &= ~KBASE_REG_NO_USER_FREE;
	kbase_mem_free_region(kctx, chunk->region);
	kbase_gpu_vm_unlock(kctx);
	list_del(&chunk->link);
	heap->chunk_count--;
	kfree(chunk);
}

/**
 * delete_all_chunks - Delete all chunks belonging to a tiler heap
 *
 * @heap: Pointer to a tiler heap.
 *
 * This function empties the list of chunks associated with a tiler heap by
 * freeing all chunks previously allocated by @create_chunk.
 */
static void delete_all_chunks(struct kbase_csf_tiler_heap *heap)
{
	struct list_head *entry = NULL, *tmp = NULL;

	list_for_each_safe(entry, tmp, &heap->chunks_list) {
		struct kbase_csf_tiler_heap_chunk *chunk = list_entry(
			entry, struct kbase_csf_tiler_heap_chunk, link);

		delete_chunk(heap, chunk);
	}
}

/**
 * create_initial_chunks - Create the initial list of chunks for a tiler heap
 *
 * @heap:    Pointer to the tiler heap for which to allocate memory.
 * @nchunks: Number of chunks to create.
 *
 * This function allocates a given number of chunks for a tiler heap and
 * adds them to the list of chunks associated with that heap.
 *
 * Return: 0 if successful or a negative error code on failure.
 */
static int create_initial_chunks(struct kbase_csf_tiler_heap *const heap,
	u32 const nchunks)
{
	int err = 0;
	u32 i;

	for (i = 0; (i < nchunks) && likely(!err); i++)
		err = create_chunk(heap, true);

	if (unlikely(err))
		delete_all_chunks(heap);

	return err;
}

/**
 * delete_heap - Delete a tiler heap
 *
 * @heap: Pointer to a tiler heap to be deleted.
 *
 * This function frees any chunks allocated for a tiler heap previously
 * initialized by @kbase_csf_tiler_heap_init and removes it from the list of
 * heaps associated with the kbase context. The heap context structure used by
 * the firmware is also freed.
 */
static void delete_heap(struct kbase_csf_tiler_heap *heap)
{
	struct kbase_context *const kctx = heap->kctx;

	dev_dbg(kctx->kbdev->dev, "Deleting tiler heap 0x%llX\n", heap->gpu_va);

	lockdep_assert_held(&kctx->csf.tiler_heaps.lock);

	delete_all_chunks(heap);

	/* We could optimize context destruction by not freeing leaked heap
	 * contexts but it doesn't seem worth the extra complexity.
	 */
	kbase_csf_heap_context_allocator_free(&kctx->csf.tiler_heaps.ctx_alloc,
		heap->gpu_va);

	list_del(&heap->link);

	WARN_ON(heap->chunk_count);
	KBASE_TLSTREAM_AUX_TILER_HEAP_STATS(kctx->kbdev, kctx->id,
		heap->heap_id, 0, 0, heap->max_chunks, heap->chunk_size, 0,
		heap->target_in_flight, 0);

	kfree(heap);
}

/**
 * find_tiler_heap - Find a tiler heap from the address of its heap context
 *
 * @kctx:        Pointer to the kbase context to search for a tiler heap.
 * @heap_gpu_va: GPU virtual address of a heap context structure.
 *
 * Each tiler heap managed by the kernel has an associated heap context
 * structure used by the firmware. This function finds a tiler heap object from
 * the GPU virtual address of its associated heap context. The heap context
 * should have been allocated by @kbase_csf_heap_context_allocator_alloc in the
 * same @kctx.
 *
 * Return: pointer to the tiler heap object, or NULL if not found.
 */
static struct kbase_csf_tiler_heap *find_tiler_heap(
	struct kbase_context *const kctx, u64 const heap_gpu_va)
{
	struct kbase_csf_tiler_heap *heap = NULL;

	lockdep_assert_held(&kctx->csf.tiler_heaps.lock);

	list_for_each_entry(heap, &kctx->csf.tiler_heaps.list, link) {
		if (heap_gpu_va == heap->gpu_va)
			return heap;
	}

	dev_dbg(kctx->kbdev->dev, "Tiler heap 0x%llX was not found\n",
		heap_gpu_va);

	return NULL;
}

int kbase_csf_tiler_heap_context_init(struct kbase_context *const kctx)
{
	int err = kbase_csf_heap_context_allocator_init(
		&kctx->csf.tiler_heaps.ctx_alloc, kctx);

	if (unlikely(err))
		return err;

	INIT_LIST_HEAD(&kctx->csf.tiler_heaps.list);
	mutex_init(&kctx->csf.tiler_heaps.lock);

	dev_dbg(kctx->kbdev->dev, "Initialized a context for tiler heaps\n");

	return 0;
}

void kbase_csf_tiler_heap_context_term(struct kbase_context *const kctx)
{
	struct list_head *entry = NULL, *tmp = NULL;

	dev_dbg(kctx->kbdev->dev, "Terminating a context for tiler heaps\n");

	mutex_lock(&kctx->csf.tiler_heaps.lock);

	list_for_each_safe(entry, tmp, &kctx->csf.tiler_heaps.list) {
		struct kbase_csf_tiler_heap *heap = list_entry(
			entry, struct kbase_csf_tiler_heap, link);
		delete_heap(heap);
	}

	mutex_unlock(&kctx->csf.tiler_heaps.lock);
	mutex_destroy(&kctx->csf.tiler_heaps.lock);

	kbase_csf_heap_context_allocator_term(&kctx->csf.tiler_heaps.ctx_alloc);
}

int kbase_csf_tiler_heap_init(struct kbase_context *const kctx,
	u32 const chunk_size, u32 const initial_chunks, u32 const max_chunks,
	u16 const target_in_flight, u64 *const heap_gpu_va,
	u64 *const first_chunk_va)
{
	int err = 0;
	struct kbase_csf_tiler_heap *heap = NULL;
	struct kbase_csf_heap_context_allocator *const ctx_alloc =
		&kctx->csf.tiler_heaps.ctx_alloc;

	dev_dbg(kctx->kbdev->dev,
		"Creating a tiler heap with %u chunks (limit: %u) of size %u\n",
		initial_chunks, max_chunks, chunk_size);

	if (!kbase_mem_allow_alloc(kctx))
		return -EINVAL;

	if (chunk_size == 0)
		return -EINVAL;

	if (chunk_size & ~CHUNK_SIZE_MASK)
		return -EINVAL;

	if (initial_chunks == 0)
		return -EINVAL;

	if (initial_chunks > max_chunks)
		return -EINVAL;

	if (target_in_flight == 0)
		return -EINVAL;

	heap = kzalloc(sizeof(*heap), GFP_KERNEL);
	if (unlikely(!heap)) {
		dev_err(kctx->kbdev->dev,
			"No kernel memory for a new tiler heap\n");
		return -ENOMEM;
	}

	heap->kctx = kctx;
	heap->chunk_size = chunk_size;
	heap->max_chunks = max_chunks;
	heap->target_in_flight = target_in_flight;
	INIT_LIST_HEAD(&heap->chunks_list);

	heap->gpu_va = kbase_csf_heap_context_allocator_alloc(ctx_alloc);

	if (unlikely(!heap->gpu_va)) {
		dev_dbg(kctx->kbdev->dev,
			"Failed to allocate a tiler heap context");
		err = -ENOMEM;
	} else {
		err = create_initial_chunks(heap, initial_chunks);
		if (unlikely(err))
			kbase_csf_heap_context_allocator_free(ctx_alloc, heap->gpu_va);
	}

	if (unlikely(err)) {
		kfree(heap);
	} else {
		struct kbase_csf_tiler_heap_chunk const *chunk = list_first_entry(
			&heap->chunks_list, struct kbase_csf_tiler_heap_chunk, link);

		*heap_gpu_va = heap->gpu_va;
		*first_chunk_va = chunk->gpu_va;

		mutex_lock(&kctx->csf.tiler_heaps.lock);
		kctx->csf.tiler_heaps.nr_of_heaps++;
		heap->heap_id = kctx->csf.tiler_heaps.nr_of_heaps;
		list_add(&heap->link, &kctx->csf.tiler_heaps.list);

		KBASE_TLSTREAM_AUX_TILER_HEAP_STATS(
			kctx->kbdev, kctx->id, heap->heap_id,
			PFN_UP(heap->chunk_size * heap->max_chunks),
			PFN_UP(heap->chunk_size * heap->chunk_count), heap->max_chunks,
			heap->chunk_size, heap->chunk_count, heap->target_in_flight, 0);

#if defined(CONFIG_MALI_VECTOR_DUMP)
		list_for_each_entry(chunk, &heap->chunks_list, link) {
			KBASE_TLSTREAM_JD_TILER_HEAP_CHUNK_ALLOC(
				kctx->kbdev, kctx->id, heap->heap_id, chunk->gpu_va);
		}
#endif

		dev_dbg(kctx->kbdev->dev, "Created tiler heap 0x%llX\n", heap->gpu_va);
		mutex_unlock(&kctx->csf.tiler_heaps.lock);
		kctx->running_total_tiler_heap_nr_chunks += heap->chunk_count;
		kctx->running_total_tiler_heap_memory +=
			heap->chunk_size * heap->chunk_count;
		if (kctx->running_total_tiler_heap_memory >
		    kctx->peak_total_tiler_heap_memory)
			kctx->peak_total_tiler_heap_memory =
				kctx->running_total_tiler_heap_memory;
	}
	return err;
}

int kbase_csf_tiler_heap_term(struct kbase_context *const kctx,
	u64 const heap_gpu_va)
{
	int err = 0;
	struct kbase_csf_tiler_heap *heap = NULL;
	u32 chunk_count = 0;
	u64 heap_size = 0;

	mutex_lock(&kctx->csf.tiler_heaps.lock);

	heap = find_tiler_heap(kctx, heap_gpu_va);
	if (likely(heap)) {
		chunk_count = heap->chunk_count;
		heap_size = heap->chunk_size * chunk_count;
		delete_heap(heap);
	} else
		err = -EINVAL;

	mutex_unlock(&kctx->csf.tiler_heaps.lock);
	if (likely(kctx->running_total_tiler_heap_memory >= heap_size))
		kctx->running_total_tiler_heap_memory -= heap_size;
	else
		dev_warn(kctx->kbdev->dev,
			 "Running total tiler heap memory lower than expected!");
	if (likely(kctx->running_total_tiler_heap_nr_chunks >= chunk_count))
		kctx->running_total_tiler_heap_nr_chunks -= chunk_count;
	else
		dev_warn(kctx->kbdev->dev,
			 "Running total tiler chunk count lower than expected!");
	return err;
}

/**
 * alloc_new_chunk - Allocate a new chunk for the tiler heap.
 *
 * @heap:               Pointer to the tiler heap.
 * @nr_in_flight:       Number of render passes that are in-flight, must not be zero.
 * @pending_frag_count: Number of render passes in-flight with completed vertex/tiler stage.
 *                      The minimum value is zero but it must be less or equal to
 *                      the total number of render passes in flight
 * @new_chunk_ptr:      Where to store the GPU virtual address & size of the new
 *                      chunk allocated for the heap.
 *
 * This function will allocate a new chunk for the chunked tiler heap depending
 * on the settings provided by userspace when the heap was created and the
 * heap's statistics (like number of render passes in-flight).
 *
 * Return: 0 if a new chunk was allocated otherwise an appropriate negative
 *         error code.
 */
static int alloc_new_chunk(struct kbase_csf_tiler_heap *heap,
		u32 nr_in_flight, u32 pending_frag_count, u64 *new_chunk_ptr)
{
	int err = -ENOMEM;

	lockdep_assert_held(&heap->kctx->csf.tiler_heaps.lock);

	if (WARN_ON(!nr_in_flight) ||
		WARN_ON(pending_frag_count > nr_in_flight))
		return -EINVAL;

	if (nr_in_flight <= heap->target_in_flight) {
		if (heap->chunk_count < heap->max_chunks) {
			/* Not exceeded the target number of render passes yet so be
			 * generous with memory.
			 */
			err = create_chunk(heap, false);

			if (likely(!err)) {
				struct kbase_csf_tiler_heap_chunk *new_chunk =
								get_last_chunk(heap);
				if (!WARN_ON(!new_chunk)) {
					*new_chunk_ptr =
						encode_chunk_ptr(heap->chunk_size,
								 new_chunk->gpu_va);
					return 0;
				}
			}
		} else if (pending_frag_count > 0) {
			err = -EBUSY;
		} else {
			err = -ENOMEM;
		}
	} else {
		/* Reached target number of render passes in flight.
		 * Wait for some of them to finish
		 */
		err = -EBUSY;
	}

	return err;
}

int kbase_csf_tiler_heap_alloc_new_chunk(struct kbase_context *kctx,
	u64 gpu_heap_va, u32 nr_in_flight, u32 pending_frag_count, u64 *new_chunk_ptr)
{
	struct kbase_csf_tiler_heap *heap;
	int err = -EINVAL;

	mutex_lock(&kctx->csf.tiler_heaps.lock);

	heap = find_tiler_heap(kctx, gpu_heap_va);

	if (likely(heap)) {
		err = alloc_new_chunk(heap, nr_in_flight, pending_frag_count,
			new_chunk_ptr);
		if (likely(!err)) {
			/* update total and peak tiler heap memory record */
			kctx->running_total_tiler_heap_nr_chunks++;
			kctx->running_total_tiler_heap_memory += heap->chunk_size;

			if (kctx->running_total_tiler_heap_memory >
			    kctx->peak_total_tiler_heap_memory)
				kctx->peak_total_tiler_heap_memory =
					kctx->running_total_tiler_heap_memory;
		}

		KBASE_TLSTREAM_AUX_TILER_HEAP_STATS(
			kctx->kbdev, kctx->id, heap->heap_id,
			PFN_UP(heap->chunk_size * heap->max_chunks),
			PFN_UP(heap->chunk_size * heap->chunk_count),
			heap->max_chunks, heap->chunk_size, heap->chunk_count,
			heap->target_in_flight, nr_in_flight);
	}

	mutex_unlock(&kctx->csf.tiler_heaps.lock);

	return err;
}