162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright © 2015 Broadcom
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci/**
762306a36Sopenharmony_ci * DOC: VC4 GEM BO management support
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * The VC4 GPU architecture (both scanout and rendering) has direct
1062306a36Sopenharmony_ci * access to system memory with no MMU in between.  To support it, we
1162306a36Sopenharmony_ci * use the GEM DMA helper functions to allocate contiguous ranges of
1262306a36Sopenharmony_ci * physical memory for our BOs.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * Since the DMA allocator is very slow, we keep a cache of recently
1562306a36Sopenharmony_ci * freed BOs around so that the kernel's allocation of objects for 3D
1662306a36Sopenharmony_ci * rendering can return quickly.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/dma-buf.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "vc4_drv.h"
2462306a36Sopenharmony_ci#include "uapi/drm/vc4_drm.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic const struct drm_gem_object_funcs vc4_gem_object_funcs;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic const char * const bo_type_names[] = {
2962306a36Sopenharmony_ci	"kernel",
3062306a36Sopenharmony_ci	"V3D",
3162306a36Sopenharmony_ci	"V3D shader",
3262306a36Sopenharmony_ci	"dumb",
3362306a36Sopenharmony_ci	"binner",
3462306a36Sopenharmony_ci	"RCL",
3562306a36Sopenharmony_ci	"BCL",
3662306a36Sopenharmony_ci	"kernel BO cache",
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic bool is_user_label(int label)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	return label >= VC4_BO_TYPE_COUNT;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic void vc4_bo_stats_print(struct drm_printer *p, struct vc4_dev *vc4)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	int i;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	for (i = 0; i < vc4->num_labels; i++) {
4962306a36Sopenharmony_ci		if (!vc4->bo_labels[i].num_allocated)
5062306a36Sopenharmony_ci			continue;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci		drm_printf(p, "%30s: %6dkb BOs (%d)\n",
5362306a36Sopenharmony_ci			   vc4->bo_labels[i].name,
5462306a36Sopenharmony_ci			   vc4->bo_labels[i].size_allocated / 1024,
5562306a36Sopenharmony_ci			   vc4->bo_labels[i].num_allocated);
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	mutex_lock(&vc4->purgeable.lock);
5962306a36Sopenharmony_ci	if (vc4->purgeable.num)
6062306a36Sopenharmony_ci		drm_printf(p, "%30s: %6zdkb BOs (%d)\n", "userspace BO cache",
6162306a36Sopenharmony_ci			   vc4->purgeable.size / 1024, vc4->purgeable.num);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	if (vc4->purgeable.purged_num)
6462306a36Sopenharmony_ci		drm_printf(p, "%30s: %6zdkb BOs (%d)\n", "total purged BO",
6562306a36Sopenharmony_ci			   vc4->purgeable.purged_size / 1024,
6662306a36Sopenharmony_ci			   vc4->purgeable.purged_num);
6762306a36Sopenharmony_ci	mutex_unlock(&vc4->purgeable.lock);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic int vc4_bo_stats_debugfs(struct seq_file *m, void *unused)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct drm_debugfs_entry *entry = m->private;
7362306a36Sopenharmony_ci	struct drm_device *dev = entry->dev;
7462306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
7562306a36Sopenharmony_ci	struct drm_printer p = drm_seq_file_printer(m);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	vc4_bo_stats_print(&p, vc4);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return 0;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/* Takes ownership of *name and returns the appropriate slot for it in
8362306a36Sopenharmony_ci * the bo_labels[] array, extending it as necessary.
8462306a36Sopenharmony_ci *
8562306a36Sopenharmony_ci * This is inefficient and could use a hash table instead of walking
8662306a36Sopenharmony_ci * an array and strcmp()ing.  However, the assumption is that user
8762306a36Sopenharmony_ci * labeling will be infrequent (scanout buffers and other long-lived
8862306a36Sopenharmony_ci * objects, or debug driver builds), so we can live with it for now.
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_cistatic int vc4_get_user_label(struct vc4_dev *vc4, const char *name)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	int i;
9362306a36Sopenharmony_ci	int free_slot = -1;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	for (i = 0; i < vc4->num_labels; i++) {
9662306a36Sopenharmony_ci		if (!vc4->bo_labels[i].name) {
9762306a36Sopenharmony_ci			free_slot = i;
9862306a36Sopenharmony_ci		} else if (strcmp(vc4->bo_labels[i].name, name) == 0) {
9962306a36Sopenharmony_ci			kfree(name);
10062306a36Sopenharmony_ci			return i;
10162306a36Sopenharmony_ci		}
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (free_slot != -1) {
10562306a36Sopenharmony_ci		WARN_ON(vc4->bo_labels[free_slot].num_allocated != 0);
10662306a36Sopenharmony_ci		vc4->bo_labels[free_slot].name = name;
10762306a36Sopenharmony_ci		return free_slot;
10862306a36Sopenharmony_ci	} else {
10962306a36Sopenharmony_ci		u32 new_label_count = vc4->num_labels + 1;
11062306a36Sopenharmony_ci		struct vc4_label *new_labels =
11162306a36Sopenharmony_ci			krealloc(vc4->bo_labels,
11262306a36Sopenharmony_ci				 new_label_count * sizeof(*new_labels),
11362306a36Sopenharmony_ci				 GFP_KERNEL);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		if (!new_labels) {
11662306a36Sopenharmony_ci			kfree(name);
11762306a36Sopenharmony_ci			return -1;
11862306a36Sopenharmony_ci		}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		free_slot = vc4->num_labels;
12162306a36Sopenharmony_ci		vc4->bo_labels = new_labels;
12262306a36Sopenharmony_ci		vc4->num_labels = new_label_count;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		vc4->bo_labels[free_slot].name = name;
12562306a36Sopenharmony_ci		vc4->bo_labels[free_slot].num_allocated = 0;
12662306a36Sopenharmony_ci		vc4->bo_labels[free_slot].size_allocated = 0;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		return free_slot;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic void vc4_bo_set_label(struct drm_gem_object *gem_obj, int label)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct vc4_bo *bo = to_vc4_bo(gem_obj);
13562306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(gem_obj->dev);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	lockdep_assert_held(&vc4->bo_lock);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (label != -1) {
14062306a36Sopenharmony_ci		vc4->bo_labels[label].num_allocated++;
14162306a36Sopenharmony_ci		vc4->bo_labels[label].size_allocated += gem_obj->size;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	vc4->bo_labels[bo->label].num_allocated--;
14562306a36Sopenharmony_ci	vc4->bo_labels[bo->label].size_allocated -= gem_obj->size;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (vc4->bo_labels[bo->label].num_allocated == 0 &&
14862306a36Sopenharmony_ci	    is_user_label(bo->label)) {
14962306a36Sopenharmony_ci		/* Free user BO label slots on last unreference.
15062306a36Sopenharmony_ci		 * Slots are just where we track the stats for a given
15162306a36Sopenharmony_ci		 * name, and once a name is unused we can reuse that
15262306a36Sopenharmony_ci		 * slot.
15362306a36Sopenharmony_ci		 */
15462306a36Sopenharmony_ci		kfree(vc4->bo_labels[bo->label].name);
15562306a36Sopenharmony_ci		vc4->bo_labels[bo->label].name = NULL;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	bo->label = label;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic uint32_t bo_page_index(size_t size)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	return (size / PAGE_SIZE) - 1;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic void vc4_bo_destroy(struct vc4_bo *bo)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct drm_gem_object *obj = &bo->base.base;
16962306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(obj->dev);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	lockdep_assert_held(&vc4->bo_lock);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	vc4_bo_set_label(obj, -1);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (bo->validated_shader) {
17662306a36Sopenharmony_ci		kfree(bo->validated_shader->uniform_addr_offsets);
17762306a36Sopenharmony_ci		kfree(bo->validated_shader->texture_samples);
17862306a36Sopenharmony_ci		kfree(bo->validated_shader);
17962306a36Sopenharmony_ci		bo->validated_shader = NULL;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	mutex_destroy(&bo->madv_lock);
18362306a36Sopenharmony_ci	drm_gem_dma_free(&bo->base);
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic void vc4_bo_remove_from_cache(struct vc4_bo *bo)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	lockdep_assert_held(&vc4->bo_lock);
19162306a36Sopenharmony_ci	list_del(&bo->unref_head);
19262306a36Sopenharmony_ci	list_del(&bo->size_head);
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic struct list_head *vc4_get_cache_list_for_size(struct drm_device *dev,
19662306a36Sopenharmony_ci						     size_t size)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
19962306a36Sopenharmony_ci	uint32_t page_index = bo_page_index(size);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (vc4->bo_cache.size_list_size <= page_index) {
20262306a36Sopenharmony_ci		uint32_t new_size = max(vc4->bo_cache.size_list_size * 2,
20362306a36Sopenharmony_ci					page_index + 1);
20462306a36Sopenharmony_ci		struct list_head *new_list;
20562306a36Sopenharmony_ci		uint32_t i;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		new_list = kmalloc_array(new_size, sizeof(struct list_head),
20862306a36Sopenharmony_ci					 GFP_KERNEL);
20962306a36Sopenharmony_ci		if (!new_list)
21062306a36Sopenharmony_ci			return NULL;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		/* Rebase the old cached BO lists to their new list
21362306a36Sopenharmony_ci		 * head locations.
21462306a36Sopenharmony_ci		 */
21562306a36Sopenharmony_ci		for (i = 0; i < vc4->bo_cache.size_list_size; i++) {
21662306a36Sopenharmony_ci			struct list_head *old_list =
21762306a36Sopenharmony_ci				&vc4->bo_cache.size_list[i];
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci			if (list_empty(old_list))
22062306a36Sopenharmony_ci				INIT_LIST_HEAD(&new_list[i]);
22162306a36Sopenharmony_ci			else
22262306a36Sopenharmony_ci				list_replace(old_list, &new_list[i]);
22362306a36Sopenharmony_ci		}
22462306a36Sopenharmony_ci		/* And initialize the brand new BO list heads. */
22562306a36Sopenharmony_ci		for (i = vc4->bo_cache.size_list_size; i < new_size; i++)
22662306a36Sopenharmony_ci			INIT_LIST_HEAD(&new_list[i]);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		kfree(vc4->bo_cache.size_list);
22962306a36Sopenharmony_ci		vc4->bo_cache.size_list = new_list;
23062306a36Sopenharmony_ci		vc4->bo_cache.size_list_size = new_size;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	return &vc4->bo_cache.size_list[page_index];
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic void vc4_bo_cache_purge(struct drm_device *dev)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	mutex_lock(&vc4->bo_lock);
24162306a36Sopenharmony_ci	while (!list_empty(&vc4->bo_cache.time_list)) {
24262306a36Sopenharmony_ci		struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list,
24362306a36Sopenharmony_ci						    struct vc4_bo, unref_head);
24462306a36Sopenharmony_ci		vc4_bo_remove_from_cache(bo);
24562306a36Sopenharmony_ci		vc4_bo_destroy(bo);
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci	mutex_unlock(&vc4->bo_lock);
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_civoid vc4_bo_add_to_purgeable_pool(struct vc4_bo *bo)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
25562306a36Sopenharmony_ci		return;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	mutex_lock(&vc4->purgeable.lock);
25862306a36Sopenharmony_ci	list_add_tail(&bo->size_head, &vc4->purgeable.list);
25962306a36Sopenharmony_ci	vc4->purgeable.num++;
26062306a36Sopenharmony_ci	vc4->purgeable.size += bo->base.base.size;
26162306a36Sopenharmony_ci	mutex_unlock(&vc4->purgeable.lock);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic void vc4_bo_remove_from_purgeable_pool_locked(struct vc4_bo *bo)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
26962306a36Sopenharmony_ci		return;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/* list_del_init() is used here because the caller might release
27262306a36Sopenharmony_ci	 * the purgeable lock in order to acquire the madv one and update the
27362306a36Sopenharmony_ci	 * madv status.
27462306a36Sopenharmony_ci	 * During this short period of time a user might decide to mark
27562306a36Sopenharmony_ci	 * the BO as unpurgeable, and if bo->madv is set to
27662306a36Sopenharmony_ci	 * VC4_MADV_DONTNEED it will try to remove the BO from the
27762306a36Sopenharmony_ci	 * purgeable list which will fail if the ->next/prev fields
27862306a36Sopenharmony_ci	 * are set to LIST_POISON1/LIST_POISON2 (which is what
27962306a36Sopenharmony_ci	 * list_del() does).
28062306a36Sopenharmony_ci	 * Re-initializing the list element guarantees that list_del()
28162306a36Sopenharmony_ci	 * will work correctly even if it's a NOP.
28262306a36Sopenharmony_ci	 */
28362306a36Sopenharmony_ci	list_del_init(&bo->size_head);
28462306a36Sopenharmony_ci	vc4->purgeable.num--;
28562306a36Sopenharmony_ci	vc4->purgeable.size -= bo->base.base.size;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_civoid vc4_bo_remove_from_purgeable_pool(struct vc4_bo *bo)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	mutex_lock(&vc4->purgeable.lock);
29362306a36Sopenharmony_ci	vc4_bo_remove_from_purgeable_pool_locked(bo);
29462306a36Sopenharmony_ci	mutex_unlock(&vc4->purgeable.lock);
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic void vc4_bo_purge(struct drm_gem_object *obj)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct vc4_bo *bo = to_vc4_bo(obj);
30062306a36Sopenharmony_ci	struct drm_device *dev = obj->dev;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	WARN_ON(!mutex_is_locked(&bo->madv_lock));
30362306a36Sopenharmony_ci	WARN_ON(bo->madv != VC4_MADV_DONTNEED);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	dma_free_wc(dev->dev, obj->size, bo->base.vaddr, bo->base.dma_addr);
30862306a36Sopenharmony_ci	bo->base.vaddr = NULL;
30962306a36Sopenharmony_ci	bo->madv = __VC4_MADV_PURGED;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic void vc4_bo_userspace_cache_purge(struct drm_device *dev)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	mutex_lock(&vc4->purgeable.lock);
31762306a36Sopenharmony_ci	while (!list_empty(&vc4->purgeable.list)) {
31862306a36Sopenharmony_ci		struct vc4_bo *bo = list_first_entry(&vc4->purgeable.list,
31962306a36Sopenharmony_ci						     struct vc4_bo, size_head);
32062306a36Sopenharmony_ci		struct drm_gem_object *obj = &bo->base.base;
32162306a36Sopenharmony_ci		size_t purged_size = 0;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		vc4_bo_remove_from_purgeable_pool_locked(bo);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		/* Release the purgeable lock while we're purging the BO so
32662306a36Sopenharmony_ci		 * that other people can continue inserting things in the
32762306a36Sopenharmony_ci		 * purgeable pool without having to wait for all BOs to be
32862306a36Sopenharmony_ci		 * purged.
32962306a36Sopenharmony_ci		 */
33062306a36Sopenharmony_ci		mutex_unlock(&vc4->purgeable.lock);
33162306a36Sopenharmony_ci		mutex_lock(&bo->madv_lock);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci		/* Since we released the purgeable pool lock before acquiring
33462306a36Sopenharmony_ci		 * the BO madv one, the user may have marked the BO as WILLNEED
33562306a36Sopenharmony_ci		 * and re-used it in the meantime.
33662306a36Sopenharmony_ci		 * Before purging the BO we need to make sure
33762306a36Sopenharmony_ci		 * - it is still marked as DONTNEED
33862306a36Sopenharmony_ci		 * - it has not been re-inserted in the purgeable list
33962306a36Sopenharmony_ci		 * - it is not used by HW blocks
34062306a36Sopenharmony_ci		 * If one of these conditions is not met, just skip the entry.
34162306a36Sopenharmony_ci		 */
34262306a36Sopenharmony_ci		if (bo->madv == VC4_MADV_DONTNEED &&
34362306a36Sopenharmony_ci		    list_empty(&bo->size_head) &&
34462306a36Sopenharmony_ci		    !refcount_read(&bo->usecnt)) {
34562306a36Sopenharmony_ci			purged_size = bo->base.base.size;
34662306a36Sopenharmony_ci			vc4_bo_purge(obj);
34762306a36Sopenharmony_ci		}
34862306a36Sopenharmony_ci		mutex_unlock(&bo->madv_lock);
34962306a36Sopenharmony_ci		mutex_lock(&vc4->purgeable.lock);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci		if (purged_size) {
35262306a36Sopenharmony_ci			vc4->purgeable.purged_size += purged_size;
35362306a36Sopenharmony_ci			vc4->purgeable.purged_num++;
35462306a36Sopenharmony_ci		}
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci	mutex_unlock(&vc4->purgeable.lock);
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic struct vc4_bo *vc4_bo_get_from_cache(struct drm_device *dev,
36062306a36Sopenharmony_ci					    uint32_t size,
36162306a36Sopenharmony_ci					    enum vc4_kernel_bo_type type)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
36462306a36Sopenharmony_ci	uint32_t page_index = bo_page_index(size);
36562306a36Sopenharmony_ci	struct vc4_bo *bo = NULL;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	mutex_lock(&vc4->bo_lock);
36862306a36Sopenharmony_ci	if (page_index >= vc4->bo_cache.size_list_size)
36962306a36Sopenharmony_ci		goto out;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (list_empty(&vc4->bo_cache.size_list[page_index]))
37262306a36Sopenharmony_ci		goto out;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	bo = list_first_entry(&vc4->bo_cache.size_list[page_index],
37562306a36Sopenharmony_ci			      struct vc4_bo, size_head);
37662306a36Sopenharmony_ci	vc4_bo_remove_from_cache(bo);
37762306a36Sopenharmony_ci	kref_init(&bo->base.base.refcount);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ciout:
38062306a36Sopenharmony_ci	if (bo)
38162306a36Sopenharmony_ci		vc4_bo_set_label(&bo->base.base, type);
38262306a36Sopenharmony_ci	mutex_unlock(&vc4->bo_lock);
38362306a36Sopenharmony_ci	return bo;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci/**
38762306a36Sopenharmony_ci * vc4_create_object - Implementation of driver->gem_create_object.
38862306a36Sopenharmony_ci * @dev: DRM device
38962306a36Sopenharmony_ci * @size: Size in bytes of the memory the object will reference
39062306a36Sopenharmony_ci *
39162306a36Sopenharmony_ci * This lets the DMA helpers allocate object structs for us, and keep
39262306a36Sopenharmony_ci * our BO stats correct.
39362306a36Sopenharmony_ci */
39462306a36Sopenharmony_cistruct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
39762306a36Sopenharmony_ci	struct vc4_bo *bo;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
40062306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	bo = kzalloc(sizeof(*bo), GFP_KERNEL);
40362306a36Sopenharmony_ci	if (!bo)
40462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	bo->madv = VC4_MADV_WILLNEED;
40762306a36Sopenharmony_ci	refcount_set(&bo->usecnt, 0);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	mutex_init(&bo->madv_lock);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	mutex_lock(&vc4->bo_lock);
41262306a36Sopenharmony_ci	bo->label = VC4_BO_TYPE_KERNEL;
41362306a36Sopenharmony_ci	vc4->bo_labels[VC4_BO_TYPE_KERNEL].num_allocated++;
41462306a36Sopenharmony_ci	vc4->bo_labels[VC4_BO_TYPE_KERNEL].size_allocated += size;
41562306a36Sopenharmony_ci	mutex_unlock(&vc4->bo_lock);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	bo->base.base.funcs = &vc4_gem_object_funcs;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	return &bo->base.base;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistruct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size,
42362306a36Sopenharmony_ci			     bool allow_unzeroed, enum vc4_kernel_bo_type type)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	size_t size = roundup(unaligned_size, PAGE_SIZE);
42662306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
42762306a36Sopenharmony_ci	struct drm_gem_dma_object *dma_obj;
42862306a36Sopenharmony_ci	struct vc4_bo *bo;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
43162306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (size == 0)
43462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	/* First, try to get a vc4_bo from the kernel BO cache. */
43762306a36Sopenharmony_ci	bo = vc4_bo_get_from_cache(dev, size, type);
43862306a36Sopenharmony_ci	if (bo) {
43962306a36Sopenharmony_ci		if (!allow_unzeroed)
44062306a36Sopenharmony_ci			memset(bo->base.vaddr, 0, bo->base.base.size);
44162306a36Sopenharmony_ci		return bo;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	dma_obj = drm_gem_dma_create(dev, size);
44562306a36Sopenharmony_ci	if (IS_ERR(dma_obj)) {
44662306a36Sopenharmony_ci		/*
44762306a36Sopenharmony_ci		 * If we've run out of DMA memory, kill the cache of
44862306a36Sopenharmony_ci		 * DMA allocations we've got laying around and try again.
44962306a36Sopenharmony_ci		 */
45062306a36Sopenharmony_ci		vc4_bo_cache_purge(dev);
45162306a36Sopenharmony_ci		dma_obj = drm_gem_dma_create(dev, size);
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (IS_ERR(dma_obj)) {
45562306a36Sopenharmony_ci		/*
45662306a36Sopenharmony_ci		 * Still not enough DMA memory, purge the userspace BO
45762306a36Sopenharmony_ci		 * cache and retry.
45862306a36Sopenharmony_ci		 * This is sub-optimal since we purge the whole userspace
45962306a36Sopenharmony_ci		 * BO cache which forces user that want to re-use the BO to
46062306a36Sopenharmony_ci		 * restore its initial content.
46162306a36Sopenharmony_ci		 * Ideally, we should purge entries one by one and retry
46262306a36Sopenharmony_ci		 * after each to see if DMA allocation succeeds. Or even
46362306a36Sopenharmony_ci		 * better, try to find an entry with at least the same
46462306a36Sopenharmony_ci		 * size.
46562306a36Sopenharmony_ci		 */
46662306a36Sopenharmony_ci		vc4_bo_userspace_cache_purge(dev);
46762306a36Sopenharmony_ci		dma_obj = drm_gem_dma_create(dev, size);
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (IS_ERR(dma_obj)) {
47162306a36Sopenharmony_ci		struct drm_printer p = drm_info_printer(vc4->base.dev);
47262306a36Sopenharmony_ci		DRM_ERROR("Failed to allocate from GEM DMA helper:\n");
47362306a36Sopenharmony_ci		vc4_bo_stats_print(&p, vc4);
47462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci	bo = to_vc4_bo(&dma_obj->base);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	/* By default, BOs do not support the MADV ioctl. This will be enabled
47962306a36Sopenharmony_ci	 * only on BOs that are exposed to userspace (V3D, V3D_SHADER and DUMB
48062306a36Sopenharmony_ci	 * BOs).
48162306a36Sopenharmony_ci	 */
48262306a36Sopenharmony_ci	bo->madv = __VC4_MADV_NOTSUPP;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	mutex_lock(&vc4->bo_lock);
48562306a36Sopenharmony_ci	vc4_bo_set_label(&dma_obj->base, type);
48662306a36Sopenharmony_ci	mutex_unlock(&vc4->bo_lock);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	return bo;
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ciint vc4_bo_dumb_create(struct drm_file *file_priv,
49262306a36Sopenharmony_ci		       struct drm_device *dev,
49362306a36Sopenharmony_ci		       struct drm_mode_create_dumb *args)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
49662306a36Sopenharmony_ci	struct vc4_bo *bo = NULL;
49762306a36Sopenharmony_ci	int ret;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
50062306a36Sopenharmony_ci		return -ENODEV;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	ret = vc4_dumb_fixup_args(args);
50362306a36Sopenharmony_ci	if (ret)
50462306a36Sopenharmony_ci		return ret;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	bo = vc4_bo_create(dev, args->size, false, VC4_BO_TYPE_DUMB);
50762306a36Sopenharmony_ci	if (IS_ERR(bo))
50862306a36Sopenharmony_ci		return PTR_ERR(bo);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	bo->madv = VC4_MADV_WILLNEED;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle);
51362306a36Sopenharmony_ci	drm_gem_object_put(&bo->base.base);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	return ret;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic void vc4_bo_cache_free_old(struct drm_device *dev)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
52162306a36Sopenharmony_ci	unsigned long expire_time = jiffies - msecs_to_jiffies(1000);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	lockdep_assert_held(&vc4->bo_lock);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	while (!list_empty(&vc4->bo_cache.time_list)) {
52662306a36Sopenharmony_ci		struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list,
52762306a36Sopenharmony_ci						    struct vc4_bo, unref_head);
52862306a36Sopenharmony_ci		if (time_before(expire_time, bo->free_time)) {
52962306a36Sopenharmony_ci			mod_timer(&vc4->bo_cache.time_timer,
53062306a36Sopenharmony_ci				  round_jiffies_up(jiffies +
53162306a36Sopenharmony_ci						   msecs_to_jiffies(1000)));
53262306a36Sopenharmony_ci			return;
53362306a36Sopenharmony_ci		}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci		vc4_bo_remove_from_cache(bo);
53662306a36Sopenharmony_ci		vc4_bo_destroy(bo);
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci/* Called on the last userspace/kernel unreference of the BO.  Returns
54162306a36Sopenharmony_ci * it to the BO cache if possible, otherwise frees it.
54262306a36Sopenharmony_ci */
54362306a36Sopenharmony_cistatic void vc4_free_object(struct drm_gem_object *gem_bo)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct drm_device *dev = gem_bo->dev;
54662306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
54762306a36Sopenharmony_ci	struct vc4_bo *bo = to_vc4_bo(gem_bo);
54862306a36Sopenharmony_ci	struct list_head *cache_list;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	/* Remove the BO from the purgeable list. */
55162306a36Sopenharmony_ci	mutex_lock(&bo->madv_lock);
55262306a36Sopenharmony_ci	if (bo->madv == VC4_MADV_DONTNEED && !refcount_read(&bo->usecnt))
55362306a36Sopenharmony_ci		vc4_bo_remove_from_purgeable_pool(bo);
55462306a36Sopenharmony_ci	mutex_unlock(&bo->madv_lock);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	mutex_lock(&vc4->bo_lock);
55762306a36Sopenharmony_ci	/* If the object references someone else's memory, we can't cache it.
55862306a36Sopenharmony_ci	 */
55962306a36Sopenharmony_ci	if (gem_bo->import_attach) {
56062306a36Sopenharmony_ci		vc4_bo_destroy(bo);
56162306a36Sopenharmony_ci		goto out;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* Don't cache if it was publicly named. */
56562306a36Sopenharmony_ci	if (gem_bo->name) {
56662306a36Sopenharmony_ci		vc4_bo_destroy(bo);
56762306a36Sopenharmony_ci		goto out;
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	/* If this object was partially constructed but DMA allocation
57162306a36Sopenharmony_ci	 * had failed, just free it. Can also happen when the BO has been
57262306a36Sopenharmony_ci	 * purged.
57362306a36Sopenharmony_ci	 */
57462306a36Sopenharmony_ci	if (!bo->base.vaddr) {
57562306a36Sopenharmony_ci		vc4_bo_destroy(bo);
57662306a36Sopenharmony_ci		goto out;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	cache_list = vc4_get_cache_list_for_size(dev, gem_bo->size);
58062306a36Sopenharmony_ci	if (!cache_list) {
58162306a36Sopenharmony_ci		vc4_bo_destroy(bo);
58262306a36Sopenharmony_ci		goto out;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	if (bo->validated_shader) {
58662306a36Sopenharmony_ci		kfree(bo->validated_shader->uniform_addr_offsets);
58762306a36Sopenharmony_ci		kfree(bo->validated_shader->texture_samples);
58862306a36Sopenharmony_ci		kfree(bo->validated_shader);
58962306a36Sopenharmony_ci		bo->validated_shader = NULL;
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	/* Reset madv and usecnt before adding the BO to the cache. */
59362306a36Sopenharmony_ci	bo->madv = __VC4_MADV_NOTSUPP;
59462306a36Sopenharmony_ci	refcount_set(&bo->usecnt, 0);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	bo->t_format = false;
59762306a36Sopenharmony_ci	bo->free_time = jiffies;
59862306a36Sopenharmony_ci	list_add(&bo->size_head, cache_list);
59962306a36Sopenharmony_ci	list_add(&bo->unref_head, &vc4->bo_cache.time_list);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	vc4_bo_set_label(&bo->base.base, VC4_BO_TYPE_KERNEL_CACHE);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	vc4_bo_cache_free_old(dev);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ciout:
60662306a36Sopenharmony_ci	mutex_unlock(&vc4->bo_lock);
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic void vc4_bo_cache_time_work(struct work_struct *work)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	struct vc4_dev *vc4 =
61262306a36Sopenharmony_ci		container_of(work, struct vc4_dev, bo_cache.time_work);
61362306a36Sopenharmony_ci	struct drm_device *dev = &vc4->base;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	mutex_lock(&vc4->bo_lock);
61662306a36Sopenharmony_ci	vc4_bo_cache_free_old(dev);
61762306a36Sopenharmony_ci	mutex_unlock(&vc4->bo_lock);
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ciint vc4_bo_inc_usecnt(struct vc4_bo *bo)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
62362306a36Sopenharmony_ci	int ret;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
62662306a36Sopenharmony_ci		return -ENODEV;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	/* Fast path: if the BO is already retained by someone, no need to
62962306a36Sopenharmony_ci	 * check the madv status.
63062306a36Sopenharmony_ci	 */
63162306a36Sopenharmony_ci	if (refcount_inc_not_zero(&bo->usecnt))
63262306a36Sopenharmony_ci		return 0;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	mutex_lock(&bo->madv_lock);
63562306a36Sopenharmony_ci	switch (bo->madv) {
63662306a36Sopenharmony_ci	case VC4_MADV_WILLNEED:
63762306a36Sopenharmony_ci		if (!refcount_inc_not_zero(&bo->usecnt))
63862306a36Sopenharmony_ci			refcount_set(&bo->usecnt, 1);
63962306a36Sopenharmony_ci		ret = 0;
64062306a36Sopenharmony_ci		break;
64162306a36Sopenharmony_ci	case VC4_MADV_DONTNEED:
64262306a36Sopenharmony_ci		/* We shouldn't use a BO marked as purgeable if at least
64362306a36Sopenharmony_ci		 * someone else retained its content by incrementing usecnt.
64462306a36Sopenharmony_ci		 * Luckily the BO hasn't been purged yet, but something wrong
64562306a36Sopenharmony_ci		 * is happening here. Just throw an error instead of
64662306a36Sopenharmony_ci		 * authorizing this use case.
64762306a36Sopenharmony_ci		 */
64862306a36Sopenharmony_ci	case __VC4_MADV_PURGED:
64962306a36Sopenharmony_ci		/* We can't use a purged BO. */
65062306a36Sopenharmony_ci	default:
65162306a36Sopenharmony_ci		/* Invalid madv value. */
65262306a36Sopenharmony_ci		ret = -EINVAL;
65362306a36Sopenharmony_ci		break;
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci	mutex_unlock(&bo->madv_lock);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	return ret;
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_civoid vc4_bo_dec_usecnt(struct vc4_bo *bo)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
66562306a36Sopenharmony_ci		return;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	/* Fast path: if the BO is still retained by someone, no need to test
66862306a36Sopenharmony_ci	 * the madv value.
66962306a36Sopenharmony_ci	 */
67062306a36Sopenharmony_ci	if (refcount_dec_not_one(&bo->usecnt))
67162306a36Sopenharmony_ci		return;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	mutex_lock(&bo->madv_lock);
67462306a36Sopenharmony_ci	if (refcount_dec_and_test(&bo->usecnt) &&
67562306a36Sopenharmony_ci	    bo->madv == VC4_MADV_DONTNEED)
67662306a36Sopenharmony_ci		vc4_bo_add_to_purgeable_pool(bo);
67762306a36Sopenharmony_ci	mutex_unlock(&bo->madv_lock);
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_cistatic void vc4_bo_cache_time_timer(struct timer_list *t)
68162306a36Sopenharmony_ci{
68262306a36Sopenharmony_ci	struct vc4_dev *vc4 = from_timer(vc4, t, bo_cache.time_timer);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	schedule_work(&vc4->bo_cache.time_work);
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_cistatic struct dma_buf *vc4_prime_export(struct drm_gem_object *obj, int flags)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	struct vc4_bo *bo = to_vc4_bo(obj);
69062306a36Sopenharmony_ci	struct dma_buf *dmabuf;
69162306a36Sopenharmony_ci	int ret;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (bo->validated_shader) {
69462306a36Sopenharmony_ci		DRM_DEBUG("Attempting to export shader BO\n");
69562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	/* Note: as soon as the BO is exported it becomes unpurgeable, because
69962306a36Sopenharmony_ci	 * noone ever decrements the usecnt even if the reference held by the
70062306a36Sopenharmony_ci	 * exported BO is released. This shouldn't be a problem since we don't
70162306a36Sopenharmony_ci	 * expect exported BOs to be marked as purgeable.
70262306a36Sopenharmony_ci	 */
70362306a36Sopenharmony_ci	ret = vc4_bo_inc_usecnt(bo);
70462306a36Sopenharmony_ci	if (ret) {
70562306a36Sopenharmony_ci		DRM_ERROR("Failed to increment BO usecnt\n");
70662306a36Sopenharmony_ci		return ERR_PTR(ret);
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	dmabuf = drm_gem_prime_export(obj, flags);
71062306a36Sopenharmony_ci	if (IS_ERR(dmabuf))
71162306a36Sopenharmony_ci		vc4_bo_dec_usecnt(bo);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	return dmabuf;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cistatic vm_fault_t vc4_fault(struct vm_fault *vmf)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	struct vm_area_struct *vma = vmf->vma;
71962306a36Sopenharmony_ci	struct drm_gem_object *obj = vma->vm_private_data;
72062306a36Sopenharmony_ci	struct vc4_bo *bo = to_vc4_bo(obj);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	/* The only reason we would end up here is when user-space accesses
72362306a36Sopenharmony_ci	 * BO's memory after it's been purged.
72462306a36Sopenharmony_ci	 */
72562306a36Sopenharmony_ci	mutex_lock(&bo->madv_lock);
72662306a36Sopenharmony_ci	WARN_ON(bo->madv != __VC4_MADV_PURGED);
72762306a36Sopenharmony_ci	mutex_unlock(&bo->madv_lock);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	return VM_FAULT_SIGBUS;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic int vc4_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	struct vc4_bo *bo = to_vc4_bo(obj);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) {
73762306a36Sopenharmony_ci		DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n");
73862306a36Sopenharmony_ci		return -EINVAL;
73962306a36Sopenharmony_ci	}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	if (bo->madv != VC4_MADV_WILLNEED) {
74262306a36Sopenharmony_ci		DRM_DEBUG("mmapping of %s BO not allowed\n",
74362306a36Sopenharmony_ci			  bo->madv == VC4_MADV_DONTNEED ?
74462306a36Sopenharmony_ci			  "purgeable" : "purged");
74562306a36Sopenharmony_ci		return -EINVAL;
74662306a36Sopenharmony_ci	}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	return drm_gem_dma_mmap(&bo->base, vma);
74962306a36Sopenharmony_ci}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_cistatic const struct vm_operations_struct vc4_vm_ops = {
75262306a36Sopenharmony_ci	.fault = vc4_fault,
75362306a36Sopenharmony_ci	.open = drm_gem_vm_open,
75462306a36Sopenharmony_ci	.close = drm_gem_vm_close,
75562306a36Sopenharmony_ci};
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic const struct drm_gem_object_funcs vc4_gem_object_funcs = {
75862306a36Sopenharmony_ci	.free = vc4_free_object,
75962306a36Sopenharmony_ci	.export = vc4_prime_export,
76062306a36Sopenharmony_ci	.get_sg_table = drm_gem_dma_object_get_sg_table,
76162306a36Sopenharmony_ci	.vmap = drm_gem_dma_object_vmap,
76262306a36Sopenharmony_ci	.mmap = vc4_gem_object_mmap,
76362306a36Sopenharmony_ci	.vm_ops = &vc4_vm_ops,
76462306a36Sopenharmony_ci};
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_cistatic int vc4_grab_bin_bo(struct vc4_dev *vc4, struct vc4_file *vc4file)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	if (!vc4->v3d)
76962306a36Sopenharmony_ci		return -ENODEV;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	if (vc4file->bin_bo_used)
77262306a36Sopenharmony_ci		return 0;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	return vc4_v3d_bin_bo_get(vc4, &vc4file->bin_bo_used);
77562306a36Sopenharmony_ci}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ciint vc4_create_bo_ioctl(struct drm_device *dev, void *data,
77862306a36Sopenharmony_ci			struct drm_file *file_priv)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	struct drm_vc4_create_bo *args = data;
78162306a36Sopenharmony_ci	struct vc4_file *vc4file = file_priv->driver_priv;
78262306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
78362306a36Sopenharmony_ci	struct vc4_bo *bo = NULL;
78462306a36Sopenharmony_ci	int ret;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
78762306a36Sopenharmony_ci		return -ENODEV;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	ret = vc4_grab_bin_bo(vc4, vc4file);
79062306a36Sopenharmony_ci	if (ret)
79162306a36Sopenharmony_ci		return ret;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	/*
79462306a36Sopenharmony_ci	 * We can't allocate from the BO cache, because the BOs don't
79562306a36Sopenharmony_ci	 * get zeroed, and that might leak data between users.
79662306a36Sopenharmony_ci	 */
79762306a36Sopenharmony_ci	bo = vc4_bo_create(dev, args->size, false, VC4_BO_TYPE_V3D);
79862306a36Sopenharmony_ci	if (IS_ERR(bo))
79962306a36Sopenharmony_ci		return PTR_ERR(bo);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	bo->madv = VC4_MADV_WILLNEED;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle);
80462306a36Sopenharmony_ci	drm_gem_object_put(&bo->base.base);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	return ret;
80762306a36Sopenharmony_ci}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ciint vc4_mmap_bo_ioctl(struct drm_device *dev, void *data,
81062306a36Sopenharmony_ci		      struct drm_file *file_priv)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
81362306a36Sopenharmony_ci	struct drm_vc4_mmap_bo *args = data;
81462306a36Sopenharmony_ci	struct drm_gem_object *gem_obj;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
81762306a36Sopenharmony_ci		return -ENODEV;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	gem_obj = drm_gem_object_lookup(file_priv, args->handle);
82062306a36Sopenharmony_ci	if (!gem_obj) {
82162306a36Sopenharmony_ci		DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
82262306a36Sopenharmony_ci		return -EINVAL;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	/* The mmap offset was set up at BO allocation time. */
82662306a36Sopenharmony_ci	args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node);
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	drm_gem_object_put(gem_obj);
82962306a36Sopenharmony_ci	return 0;
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ciint
83362306a36Sopenharmony_civc4_create_shader_bo_ioctl(struct drm_device *dev, void *data,
83462306a36Sopenharmony_ci			   struct drm_file *file_priv)
83562306a36Sopenharmony_ci{
83662306a36Sopenharmony_ci	struct drm_vc4_create_shader_bo *args = data;
83762306a36Sopenharmony_ci	struct vc4_file *vc4file = file_priv->driver_priv;
83862306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
83962306a36Sopenharmony_ci	struct vc4_bo *bo = NULL;
84062306a36Sopenharmony_ci	int ret;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
84362306a36Sopenharmony_ci		return -ENODEV;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	if (args->size == 0)
84662306a36Sopenharmony_ci		return -EINVAL;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	if (args->size % sizeof(u64) != 0)
84962306a36Sopenharmony_ci		return -EINVAL;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	if (args->flags != 0) {
85262306a36Sopenharmony_ci		DRM_INFO("Unknown flags set: 0x%08x\n", args->flags);
85362306a36Sopenharmony_ci		return -EINVAL;
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	if (args->pad != 0) {
85762306a36Sopenharmony_ci		DRM_INFO("Pad set: 0x%08x\n", args->pad);
85862306a36Sopenharmony_ci		return -EINVAL;
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	ret = vc4_grab_bin_bo(vc4, vc4file);
86262306a36Sopenharmony_ci	if (ret)
86362306a36Sopenharmony_ci		return ret;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	bo = vc4_bo_create(dev, args->size, true, VC4_BO_TYPE_V3D_SHADER);
86662306a36Sopenharmony_ci	if (IS_ERR(bo))
86762306a36Sopenharmony_ci		return PTR_ERR(bo);
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	bo->madv = VC4_MADV_WILLNEED;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	if (copy_from_user(bo->base.vaddr,
87262306a36Sopenharmony_ci			     (void __user *)(uintptr_t)args->data,
87362306a36Sopenharmony_ci			     args->size)) {
87462306a36Sopenharmony_ci		ret = -EFAULT;
87562306a36Sopenharmony_ci		goto fail;
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci	/* Clear the rest of the memory from allocating from the BO
87862306a36Sopenharmony_ci	 * cache.
87962306a36Sopenharmony_ci	 */
88062306a36Sopenharmony_ci	memset(bo->base.vaddr + args->size, 0,
88162306a36Sopenharmony_ci	       bo->base.base.size - args->size);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	bo->validated_shader = vc4_validate_shader(&bo->base);
88462306a36Sopenharmony_ci	if (!bo->validated_shader) {
88562306a36Sopenharmony_ci		ret = -EINVAL;
88662306a36Sopenharmony_ci		goto fail;
88762306a36Sopenharmony_ci	}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	/* We have to create the handle after validation, to avoid
89062306a36Sopenharmony_ci	 * races for users to do doing things like mmap the shader BO.
89162306a36Sopenharmony_ci	 */
89262306a36Sopenharmony_ci	ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_cifail:
89562306a36Sopenharmony_ci	drm_gem_object_put(&bo->base.base);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	return ret;
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci/**
90162306a36Sopenharmony_ci * vc4_set_tiling_ioctl() - Sets the tiling modifier for a BO.
90262306a36Sopenharmony_ci * @dev: DRM device
90362306a36Sopenharmony_ci * @data: ioctl argument
90462306a36Sopenharmony_ci * @file_priv: DRM file for this fd
90562306a36Sopenharmony_ci *
90662306a36Sopenharmony_ci * The tiling state of the BO decides the default modifier of an fb if
90762306a36Sopenharmony_ci * no specific modifier was set by userspace, and the return value of
90862306a36Sopenharmony_ci * vc4_get_tiling_ioctl() (so that userspace can treat a BO it
90962306a36Sopenharmony_ci * received from dmabuf as the same tiling format as the producer
91062306a36Sopenharmony_ci * used).
91162306a36Sopenharmony_ci */
91262306a36Sopenharmony_ciint vc4_set_tiling_ioctl(struct drm_device *dev, void *data,
91362306a36Sopenharmony_ci			 struct drm_file *file_priv)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
91662306a36Sopenharmony_ci	struct drm_vc4_set_tiling *args = data;
91762306a36Sopenharmony_ci	struct drm_gem_object *gem_obj;
91862306a36Sopenharmony_ci	struct vc4_bo *bo;
91962306a36Sopenharmony_ci	bool t_format;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
92262306a36Sopenharmony_ci		return -ENODEV;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	if (args->flags != 0)
92562306a36Sopenharmony_ci		return -EINVAL;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	switch (args->modifier) {
92862306a36Sopenharmony_ci	case DRM_FORMAT_MOD_NONE:
92962306a36Sopenharmony_ci		t_format = false;
93062306a36Sopenharmony_ci		break;
93162306a36Sopenharmony_ci	case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED:
93262306a36Sopenharmony_ci		t_format = true;
93362306a36Sopenharmony_ci		break;
93462306a36Sopenharmony_ci	default:
93562306a36Sopenharmony_ci		return -EINVAL;
93662306a36Sopenharmony_ci	}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	gem_obj = drm_gem_object_lookup(file_priv, args->handle);
93962306a36Sopenharmony_ci	if (!gem_obj) {
94062306a36Sopenharmony_ci		DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
94162306a36Sopenharmony_ci		return -ENOENT;
94262306a36Sopenharmony_ci	}
94362306a36Sopenharmony_ci	bo = to_vc4_bo(gem_obj);
94462306a36Sopenharmony_ci	bo->t_format = t_format;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	drm_gem_object_put(gem_obj);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	return 0;
94962306a36Sopenharmony_ci}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci/**
95262306a36Sopenharmony_ci * vc4_get_tiling_ioctl() - Gets the tiling modifier for a BO.
95362306a36Sopenharmony_ci * @dev: DRM device
95462306a36Sopenharmony_ci * @data: ioctl argument
95562306a36Sopenharmony_ci * @file_priv: DRM file for this fd
95662306a36Sopenharmony_ci *
95762306a36Sopenharmony_ci * Returns the tiling modifier for a BO as set by vc4_set_tiling_ioctl().
95862306a36Sopenharmony_ci */
95962306a36Sopenharmony_ciint vc4_get_tiling_ioctl(struct drm_device *dev, void *data,
96062306a36Sopenharmony_ci			 struct drm_file *file_priv)
96162306a36Sopenharmony_ci{
96262306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
96362306a36Sopenharmony_ci	struct drm_vc4_get_tiling *args = data;
96462306a36Sopenharmony_ci	struct drm_gem_object *gem_obj;
96562306a36Sopenharmony_ci	struct vc4_bo *bo;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
96862306a36Sopenharmony_ci		return -ENODEV;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	if (args->flags != 0 || args->modifier != 0)
97162306a36Sopenharmony_ci		return -EINVAL;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	gem_obj = drm_gem_object_lookup(file_priv, args->handle);
97462306a36Sopenharmony_ci	if (!gem_obj) {
97562306a36Sopenharmony_ci		DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
97662306a36Sopenharmony_ci		return -ENOENT;
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci	bo = to_vc4_bo(gem_obj);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	if (bo->t_format)
98162306a36Sopenharmony_ci		args->modifier = DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED;
98262306a36Sopenharmony_ci	else
98362306a36Sopenharmony_ci		args->modifier = DRM_FORMAT_MOD_NONE;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	drm_gem_object_put(gem_obj);
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	return 0;
98862306a36Sopenharmony_ci}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ciint vc4_bo_debugfs_init(struct drm_minor *minor)
99162306a36Sopenharmony_ci{
99262306a36Sopenharmony_ci	struct drm_device *drm = minor->dev;
99362306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(drm);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	if (!vc4->v3d)
99662306a36Sopenharmony_ci		return -ENODEV;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	drm_debugfs_add_file(drm, "bo_stats", vc4_bo_stats_debugfs, NULL);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	return 0;
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic void vc4_bo_cache_destroy(struct drm_device *dev, void *unused);
100462306a36Sopenharmony_ciint vc4_bo_cache_init(struct drm_device *dev)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
100762306a36Sopenharmony_ci	int ret;
100862306a36Sopenharmony_ci	int i;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
101162306a36Sopenharmony_ci		return -ENODEV;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	/* Create the initial set of BO labels that the kernel will
101462306a36Sopenharmony_ci	 * use.  This lets us avoid a bunch of string reallocation in
101562306a36Sopenharmony_ci	 * the kernel's draw and BO allocation paths.
101662306a36Sopenharmony_ci	 */
101762306a36Sopenharmony_ci	vc4->bo_labels = kcalloc(VC4_BO_TYPE_COUNT, sizeof(*vc4->bo_labels),
101862306a36Sopenharmony_ci				 GFP_KERNEL);
101962306a36Sopenharmony_ci	if (!vc4->bo_labels)
102062306a36Sopenharmony_ci		return -ENOMEM;
102162306a36Sopenharmony_ci	vc4->num_labels = VC4_BO_TYPE_COUNT;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(bo_type_names) != VC4_BO_TYPE_COUNT);
102462306a36Sopenharmony_ci	for (i = 0; i < VC4_BO_TYPE_COUNT; i++)
102562306a36Sopenharmony_ci		vc4->bo_labels[i].name = bo_type_names[i];
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	ret = drmm_mutex_init(dev, &vc4->bo_lock);
102862306a36Sopenharmony_ci	if (ret) {
102962306a36Sopenharmony_ci		kfree(vc4->bo_labels);
103062306a36Sopenharmony_ci		return ret;
103162306a36Sopenharmony_ci	}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	INIT_LIST_HEAD(&vc4->bo_cache.time_list);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	INIT_WORK(&vc4->bo_cache.time_work, vc4_bo_cache_time_work);
103662306a36Sopenharmony_ci	timer_setup(&vc4->bo_cache.time_timer, vc4_bo_cache_time_timer, 0);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	return drmm_add_action_or_reset(dev, vc4_bo_cache_destroy, NULL);
103962306a36Sopenharmony_ci}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_cistatic void vc4_bo_cache_destroy(struct drm_device *dev, void *unused)
104262306a36Sopenharmony_ci{
104362306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
104462306a36Sopenharmony_ci	int i;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	del_timer(&vc4->bo_cache.time_timer);
104762306a36Sopenharmony_ci	cancel_work_sync(&vc4->bo_cache.time_work);
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	vc4_bo_cache_purge(dev);
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	for (i = 0; i < vc4->num_labels; i++) {
105262306a36Sopenharmony_ci		if (vc4->bo_labels[i].num_allocated) {
105362306a36Sopenharmony_ci			DRM_ERROR("Destroying BO cache with %d %s "
105462306a36Sopenharmony_ci				  "BOs still allocated\n",
105562306a36Sopenharmony_ci				  vc4->bo_labels[i].num_allocated,
105662306a36Sopenharmony_ci				  vc4->bo_labels[i].name);
105762306a36Sopenharmony_ci		}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci		if (is_user_label(i))
106062306a36Sopenharmony_ci			kfree(vc4->bo_labels[i].name);
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci	kfree(vc4->bo_labels);
106362306a36Sopenharmony_ci}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ciint vc4_label_bo_ioctl(struct drm_device *dev, void *data,
106662306a36Sopenharmony_ci		       struct drm_file *file_priv)
106762306a36Sopenharmony_ci{
106862306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
106962306a36Sopenharmony_ci	struct drm_vc4_label_bo *args = data;
107062306a36Sopenharmony_ci	char *name;
107162306a36Sopenharmony_ci	struct drm_gem_object *gem_obj;
107262306a36Sopenharmony_ci	int ret = 0, label;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
107562306a36Sopenharmony_ci		return -ENODEV;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	if (!args->len)
107862306a36Sopenharmony_ci		return -EINVAL;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	name = strndup_user(u64_to_user_ptr(args->name), args->len + 1);
108162306a36Sopenharmony_ci	if (IS_ERR(name))
108262306a36Sopenharmony_ci		return PTR_ERR(name);
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	gem_obj = drm_gem_object_lookup(file_priv, args->handle);
108562306a36Sopenharmony_ci	if (!gem_obj) {
108662306a36Sopenharmony_ci		DRM_ERROR("Failed to look up GEM BO %d\n", args->handle);
108762306a36Sopenharmony_ci		kfree(name);
108862306a36Sopenharmony_ci		return -ENOENT;
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	mutex_lock(&vc4->bo_lock);
109262306a36Sopenharmony_ci	label = vc4_get_user_label(vc4, name);
109362306a36Sopenharmony_ci	if (label != -1)
109462306a36Sopenharmony_ci		vc4_bo_set_label(gem_obj, label);
109562306a36Sopenharmony_ci	else
109662306a36Sopenharmony_ci		ret = -ENOMEM;
109762306a36Sopenharmony_ci	mutex_unlock(&vc4->bo_lock);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	drm_gem_object_put(gem_obj);
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	return ret;
110262306a36Sopenharmony_ci}
1103