162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
462306a36Sopenharmony_ci * Copyright (c) 2012 David Airlie <airlied@linux.ie>
562306a36Sopenharmony_ci * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
862306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
962306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
1062306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1162306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
1262306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
1562306a36Sopenharmony_ci * all copies or substantial portions of the Software.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1862306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1962306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2062306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
2162306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2262306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2362306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <linux/mm.h>
2762306a36Sopenharmony_ci#include <linux/module.h>
2862306a36Sopenharmony_ci#include <linux/rbtree.h>
2962306a36Sopenharmony_ci#include <linux/slab.h>
3062306a36Sopenharmony_ci#include <linux/spinlock.h>
3162306a36Sopenharmony_ci#include <linux/types.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <drm/drm_mm.h>
3462306a36Sopenharmony_ci#include <drm/drm_vma_manager.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/**
3762306a36Sopenharmony_ci * DOC: vma offset manager
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci * The vma-manager is responsible to map arbitrary driver-dependent memory
4062306a36Sopenharmony_ci * regions into the linear user address-space. It provides offsets to the
4162306a36Sopenharmony_ci * caller which can then be used on the address_space of the drm-device. It
4262306a36Sopenharmony_ci * takes care to not overlap regions, size them appropriately and to not
4362306a36Sopenharmony_ci * confuse mm-core by inconsistent fake vm_pgoff fields.
4462306a36Sopenharmony_ci * Drivers shouldn't use this for object placement in VMEM. This manager should
4562306a36Sopenharmony_ci * only be used to manage mappings into linear user-space VMs.
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci * We use drm_mm as backend to manage object allocations. But it is highly
4862306a36Sopenharmony_ci * optimized for alloc/free calls, not lookups. Hence, we use an rb-tree to
4962306a36Sopenharmony_ci * speed up offset lookups.
5062306a36Sopenharmony_ci *
5162306a36Sopenharmony_ci * You must not use multiple offset managers on a single address_space.
5262306a36Sopenharmony_ci * Otherwise, mm-core will be unable to tear down memory mappings as the VM will
5362306a36Sopenharmony_ci * no longer be linear.
5462306a36Sopenharmony_ci *
5562306a36Sopenharmony_ci * This offset manager works on page-based addresses. That is, every argument
5662306a36Sopenharmony_ci * and return code (with the exception of drm_vma_node_offset_addr()) is given
5762306a36Sopenharmony_ci * in number of pages, not number of bytes. That means, object sizes and offsets
5862306a36Sopenharmony_ci * must always be page-aligned (as usual).
5962306a36Sopenharmony_ci * If you want to get a valid byte-based user-space address for a given offset,
6062306a36Sopenharmony_ci * please see drm_vma_node_offset_addr().
6162306a36Sopenharmony_ci *
6262306a36Sopenharmony_ci * Additionally to offset management, the vma offset manager also handles access
6362306a36Sopenharmony_ci * management. For every open-file context that is allowed to access a given
6462306a36Sopenharmony_ci * node, you must call drm_vma_node_allow(). Otherwise, an mmap() call on this
6562306a36Sopenharmony_ci * open-file with the offset of the node will fail with -EACCES. To revoke
6662306a36Sopenharmony_ci * access again, use drm_vma_node_revoke(). However, the caller is responsible
6762306a36Sopenharmony_ci * for destroying already existing mappings, if required.
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/**
7162306a36Sopenharmony_ci * drm_vma_offset_manager_init - Initialize new offset-manager
7262306a36Sopenharmony_ci * @mgr: Manager object
7362306a36Sopenharmony_ci * @page_offset: Offset of available memory area (page-based)
7462306a36Sopenharmony_ci * @size: Size of available address space range (page-based)
7562306a36Sopenharmony_ci *
7662306a36Sopenharmony_ci * Initialize a new offset-manager. The offset and area size available for the
7762306a36Sopenharmony_ci * manager are given as @page_offset and @size. Both are interpreted as
7862306a36Sopenharmony_ci * page-numbers, not bytes.
7962306a36Sopenharmony_ci *
8062306a36Sopenharmony_ci * Adding/removing nodes from the manager is locked internally and protected
8162306a36Sopenharmony_ci * against concurrent access. However, node allocation and destruction is left
8262306a36Sopenharmony_ci * for the caller. While calling into the vma-manager, a given node must
8362306a36Sopenharmony_ci * always be guaranteed to be referenced.
8462306a36Sopenharmony_ci */
8562306a36Sopenharmony_civoid drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
8662306a36Sopenharmony_ci				 unsigned long page_offset, unsigned long size)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	rwlock_init(&mgr->vm_lock);
8962306a36Sopenharmony_ci	drm_mm_init(&mgr->vm_addr_space_mm, page_offset, size);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_vma_offset_manager_init);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/**
9462306a36Sopenharmony_ci * drm_vma_offset_manager_destroy() - Destroy offset manager
9562306a36Sopenharmony_ci * @mgr: Manager object
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci * Destroy an object manager which was previously created via
9862306a36Sopenharmony_ci * drm_vma_offset_manager_init(). The caller must remove all allocated nodes
9962306a36Sopenharmony_ci * before destroying the manager. Otherwise, drm_mm will refuse to free the
10062306a36Sopenharmony_ci * requested resources.
10162306a36Sopenharmony_ci *
10262306a36Sopenharmony_ci * The manager must not be accessed after this function is called.
10362306a36Sopenharmony_ci */
10462306a36Sopenharmony_civoid drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	drm_mm_takedown(&mgr->vm_addr_space_mm);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_vma_offset_manager_destroy);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci/**
11162306a36Sopenharmony_ci * drm_vma_offset_lookup_locked() - Find node in offset space
11262306a36Sopenharmony_ci * @mgr: Manager object
11362306a36Sopenharmony_ci * @start: Start address for object (page-based)
11462306a36Sopenharmony_ci * @pages: Size of object (page-based)
11562306a36Sopenharmony_ci *
11662306a36Sopenharmony_ci * Find a node given a start address and object size. This returns the _best_
11762306a36Sopenharmony_ci * match for the given node. That is, @start may point somewhere into a valid
11862306a36Sopenharmony_ci * region and the given node will be returned, as long as the node spans the
11962306a36Sopenharmony_ci * whole requested area (given the size in number of pages as @pages).
12062306a36Sopenharmony_ci *
12162306a36Sopenharmony_ci * Note that before lookup the vma offset manager lookup lock must be acquired
12262306a36Sopenharmony_ci * with drm_vma_offset_lock_lookup(). See there for an example. This can then be
12362306a36Sopenharmony_ci * used to implement weakly referenced lookups using kref_get_unless_zero().
12462306a36Sopenharmony_ci *
12562306a36Sopenharmony_ci * Example:
12662306a36Sopenharmony_ci *
12762306a36Sopenharmony_ci * ::
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci *     drm_vma_offset_lock_lookup(mgr);
13062306a36Sopenharmony_ci *     node = drm_vma_offset_lookup_locked(mgr);
13162306a36Sopenharmony_ci *     if (node)
13262306a36Sopenharmony_ci *         kref_get_unless_zero(container_of(node, sth, entr));
13362306a36Sopenharmony_ci *     drm_vma_offset_unlock_lookup(mgr);
13462306a36Sopenharmony_ci *
13562306a36Sopenharmony_ci * RETURNS:
13662306a36Sopenharmony_ci * Returns NULL if no suitable node can be found. Otherwise, the best match
13762306a36Sopenharmony_ci * is returned. It's the caller's responsibility to make sure the node doesn't
13862306a36Sopenharmony_ci * get destroyed before the caller can access it.
13962306a36Sopenharmony_ci */
14062306a36Sopenharmony_cistruct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr,
14162306a36Sopenharmony_ci							 unsigned long start,
14262306a36Sopenharmony_ci							 unsigned long pages)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct drm_mm_node *node, *best;
14562306a36Sopenharmony_ci	struct rb_node *iter;
14662306a36Sopenharmony_ci	unsigned long offset;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	iter = mgr->vm_addr_space_mm.interval_tree.rb_root.rb_node;
14962306a36Sopenharmony_ci	best = NULL;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	while (likely(iter)) {
15262306a36Sopenharmony_ci		node = rb_entry(iter, struct drm_mm_node, rb);
15362306a36Sopenharmony_ci		offset = node->start;
15462306a36Sopenharmony_ci		if (start >= offset) {
15562306a36Sopenharmony_ci			iter = iter->rb_right;
15662306a36Sopenharmony_ci			best = node;
15762306a36Sopenharmony_ci			if (start == offset)
15862306a36Sopenharmony_ci				break;
15962306a36Sopenharmony_ci		} else {
16062306a36Sopenharmony_ci			iter = iter->rb_left;
16162306a36Sopenharmony_ci		}
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* verify that the node spans the requested area */
16562306a36Sopenharmony_ci	if (best) {
16662306a36Sopenharmony_ci		offset = best->start + best->size;
16762306a36Sopenharmony_ci		if (offset < start + pages)
16862306a36Sopenharmony_ci			best = NULL;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (!best)
17262306a36Sopenharmony_ci		return NULL;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return container_of(best, struct drm_vma_offset_node, vm_node);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_vma_offset_lookup_locked);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/**
17962306a36Sopenharmony_ci * drm_vma_offset_add() - Add offset node to manager
18062306a36Sopenharmony_ci * @mgr: Manager object
18162306a36Sopenharmony_ci * @node: Node to be added
18262306a36Sopenharmony_ci * @pages: Allocation size visible to user-space (in number of pages)
18362306a36Sopenharmony_ci *
18462306a36Sopenharmony_ci * Add a node to the offset-manager. If the node was already added, this does
18562306a36Sopenharmony_ci * nothing and return 0. @pages is the size of the object given in number of
18662306a36Sopenharmony_ci * pages.
18762306a36Sopenharmony_ci * After this call succeeds, you can access the offset of the node until it
18862306a36Sopenharmony_ci * is removed again.
18962306a36Sopenharmony_ci *
19062306a36Sopenharmony_ci * If this call fails, it is safe to retry the operation or call
19162306a36Sopenharmony_ci * drm_vma_offset_remove(), anyway. However, no cleanup is required in that
19262306a36Sopenharmony_ci * case.
19362306a36Sopenharmony_ci *
19462306a36Sopenharmony_ci * @pages is not required to be the same size as the underlying memory object
19562306a36Sopenharmony_ci * that you want to map. It only limits the size that user-space can map into
19662306a36Sopenharmony_ci * their address space.
19762306a36Sopenharmony_ci *
19862306a36Sopenharmony_ci * RETURNS:
19962306a36Sopenharmony_ci * 0 on success, negative error code on failure.
20062306a36Sopenharmony_ci */
20162306a36Sopenharmony_ciint drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
20262306a36Sopenharmony_ci		       struct drm_vma_offset_node *node, unsigned long pages)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	int ret = 0;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	write_lock(&mgr->vm_lock);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (!drm_mm_node_allocated(&node->vm_node))
20962306a36Sopenharmony_ci		ret = drm_mm_insert_node(&mgr->vm_addr_space_mm,
21062306a36Sopenharmony_ci					 &node->vm_node, pages);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	write_unlock(&mgr->vm_lock);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return ret;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_vma_offset_add);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci/**
21962306a36Sopenharmony_ci * drm_vma_offset_remove() - Remove offset node from manager
22062306a36Sopenharmony_ci * @mgr: Manager object
22162306a36Sopenharmony_ci * @node: Node to be removed
22262306a36Sopenharmony_ci *
22362306a36Sopenharmony_ci * Remove a node from the offset manager. If the node wasn't added before, this
22462306a36Sopenharmony_ci * does nothing. After this call returns, the offset and size will be 0 until a
22562306a36Sopenharmony_ci * new offset is allocated via drm_vma_offset_add() again. Helper functions like
22662306a36Sopenharmony_ci * drm_vma_node_start() and drm_vma_node_offset_addr() will return 0 if no
22762306a36Sopenharmony_ci * offset is allocated.
22862306a36Sopenharmony_ci */
22962306a36Sopenharmony_civoid drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
23062306a36Sopenharmony_ci			   struct drm_vma_offset_node *node)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	write_lock(&mgr->vm_lock);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (drm_mm_node_allocated(&node->vm_node)) {
23562306a36Sopenharmony_ci		drm_mm_remove_node(&node->vm_node);
23662306a36Sopenharmony_ci		memset(&node->vm_node, 0, sizeof(node->vm_node));
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	write_unlock(&mgr->vm_lock);
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_vma_offset_remove);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic int vma_node_allow(struct drm_vma_offset_node *node,
24462306a36Sopenharmony_ci			  struct drm_file *tag, bool ref_counted)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct rb_node **iter;
24762306a36Sopenharmony_ci	struct rb_node *parent = NULL;
24862306a36Sopenharmony_ci	struct drm_vma_offset_file *new, *entry;
24962306a36Sopenharmony_ci	int ret = 0;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	/* Preallocate entry to avoid atomic allocations below. It is quite
25262306a36Sopenharmony_ci	 * unlikely that an open-file is added twice to a single node so we
25362306a36Sopenharmony_ci	 * don't optimize for this case. OOM is checked below only if the entry
25462306a36Sopenharmony_ci	 * is actually used. */
25562306a36Sopenharmony_ci	new = kmalloc(sizeof(*entry), GFP_KERNEL);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	write_lock(&node->vm_lock);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	iter = &node->vm_files.rb_node;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	while (likely(*iter)) {
26262306a36Sopenharmony_ci		parent = *iter;
26362306a36Sopenharmony_ci		entry = rb_entry(*iter, struct drm_vma_offset_file, vm_rb);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci		if (tag == entry->vm_tag) {
26662306a36Sopenharmony_ci			if (ref_counted)
26762306a36Sopenharmony_ci				entry->vm_count++;
26862306a36Sopenharmony_ci			goto unlock;
26962306a36Sopenharmony_ci		} else if (tag > entry->vm_tag) {
27062306a36Sopenharmony_ci			iter = &(*iter)->rb_right;
27162306a36Sopenharmony_ci		} else {
27262306a36Sopenharmony_ci			iter = &(*iter)->rb_left;
27362306a36Sopenharmony_ci		}
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (!new) {
27762306a36Sopenharmony_ci		ret = -ENOMEM;
27862306a36Sopenharmony_ci		goto unlock;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	new->vm_tag = tag;
28262306a36Sopenharmony_ci	new->vm_count = 1;
28362306a36Sopenharmony_ci	rb_link_node(&new->vm_rb, parent, iter);
28462306a36Sopenharmony_ci	rb_insert_color(&new->vm_rb, &node->vm_files);
28562306a36Sopenharmony_ci	new = NULL;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ciunlock:
28862306a36Sopenharmony_ci	write_unlock(&node->vm_lock);
28962306a36Sopenharmony_ci	kfree(new);
29062306a36Sopenharmony_ci	return ret;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci/**
29462306a36Sopenharmony_ci * drm_vma_node_allow - Add open-file to list of allowed users
29562306a36Sopenharmony_ci * @node: Node to modify
29662306a36Sopenharmony_ci * @tag: Tag of file to remove
29762306a36Sopenharmony_ci *
29862306a36Sopenharmony_ci * Add @tag to the list of allowed open-files for this node. If @tag is
29962306a36Sopenharmony_ci * already on this list, the ref-count is incremented.
30062306a36Sopenharmony_ci *
30162306a36Sopenharmony_ci * The list of allowed-users is preserved across drm_vma_offset_add() and
30262306a36Sopenharmony_ci * drm_vma_offset_remove() calls. You may even call it if the node is currently
30362306a36Sopenharmony_ci * not added to any offset-manager.
30462306a36Sopenharmony_ci *
30562306a36Sopenharmony_ci * You must remove all open-files the same number of times as you added them
30662306a36Sopenharmony_ci * before destroying the node. Otherwise, you will leak memory.
30762306a36Sopenharmony_ci *
30862306a36Sopenharmony_ci * This is locked against concurrent access internally.
30962306a36Sopenharmony_ci *
31062306a36Sopenharmony_ci * RETURNS:
31162306a36Sopenharmony_ci * 0 on success, negative error code on internal failure (out-of-mem)
31262306a36Sopenharmony_ci */
31362306a36Sopenharmony_ciint drm_vma_node_allow(struct drm_vma_offset_node *node, struct drm_file *tag)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	return vma_node_allow(node, tag, true);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_vma_node_allow);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci/**
32062306a36Sopenharmony_ci * drm_vma_node_allow_once - Add open-file to list of allowed users
32162306a36Sopenharmony_ci * @node: Node to modify
32262306a36Sopenharmony_ci * @tag: Tag of file to remove
32362306a36Sopenharmony_ci *
32462306a36Sopenharmony_ci * Add @tag to the list of allowed open-files for this node.
32562306a36Sopenharmony_ci *
32662306a36Sopenharmony_ci * The list of allowed-users is preserved across drm_vma_offset_add() and
32762306a36Sopenharmony_ci * drm_vma_offset_remove() calls. You may even call it if the node is currently
32862306a36Sopenharmony_ci * not added to any offset-manager.
32962306a36Sopenharmony_ci *
33062306a36Sopenharmony_ci * This is not ref-counted unlike drm_vma_node_allow() hence drm_vma_node_revoke()
33162306a36Sopenharmony_ci * should only be called once after this.
33262306a36Sopenharmony_ci *
33362306a36Sopenharmony_ci * This is locked against concurrent access internally.
33462306a36Sopenharmony_ci *
33562306a36Sopenharmony_ci * RETURNS:
33662306a36Sopenharmony_ci * 0 on success, negative error code on internal failure (out-of-mem)
33762306a36Sopenharmony_ci */
33862306a36Sopenharmony_ciint drm_vma_node_allow_once(struct drm_vma_offset_node *node, struct drm_file *tag)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	return vma_node_allow(node, tag, false);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_vma_node_allow_once);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci/**
34562306a36Sopenharmony_ci * drm_vma_node_revoke - Remove open-file from list of allowed users
34662306a36Sopenharmony_ci * @node: Node to modify
34762306a36Sopenharmony_ci * @tag: Tag of file to remove
34862306a36Sopenharmony_ci *
34962306a36Sopenharmony_ci * Decrement the ref-count of @tag in the list of allowed open-files on @node.
35062306a36Sopenharmony_ci * If the ref-count drops to zero, remove @tag from the list. You must call
35162306a36Sopenharmony_ci * this once for every drm_vma_node_allow() on @tag.
35262306a36Sopenharmony_ci *
35362306a36Sopenharmony_ci * This is locked against concurrent access internally.
35462306a36Sopenharmony_ci *
35562306a36Sopenharmony_ci * If @tag is not on the list, nothing is done.
35662306a36Sopenharmony_ci */
35762306a36Sopenharmony_civoid drm_vma_node_revoke(struct drm_vma_offset_node *node,
35862306a36Sopenharmony_ci			 struct drm_file *tag)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	struct drm_vma_offset_file *entry;
36162306a36Sopenharmony_ci	struct rb_node *iter;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	write_lock(&node->vm_lock);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	iter = node->vm_files.rb_node;
36662306a36Sopenharmony_ci	while (likely(iter)) {
36762306a36Sopenharmony_ci		entry = rb_entry(iter, struct drm_vma_offset_file, vm_rb);
36862306a36Sopenharmony_ci		if (tag == entry->vm_tag) {
36962306a36Sopenharmony_ci			if (!--entry->vm_count) {
37062306a36Sopenharmony_ci				rb_erase(&entry->vm_rb, &node->vm_files);
37162306a36Sopenharmony_ci				kfree(entry);
37262306a36Sopenharmony_ci			}
37362306a36Sopenharmony_ci			break;
37462306a36Sopenharmony_ci		} else if (tag > entry->vm_tag) {
37562306a36Sopenharmony_ci			iter = iter->rb_right;
37662306a36Sopenharmony_ci		} else {
37762306a36Sopenharmony_ci			iter = iter->rb_left;
37862306a36Sopenharmony_ci		}
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	write_unlock(&node->vm_lock);
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_vma_node_revoke);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci/**
38662306a36Sopenharmony_ci * drm_vma_node_is_allowed - Check whether an open-file is granted access
38762306a36Sopenharmony_ci * @node: Node to check
38862306a36Sopenharmony_ci * @tag: Tag of file to remove
38962306a36Sopenharmony_ci *
39062306a36Sopenharmony_ci * Search the list in @node whether @tag is currently on the list of allowed
39162306a36Sopenharmony_ci * open-files (see drm_vma_node_allow()).
39262306a36Sopenharmony_ci *
39362306a36Sopenharmony_ci * This is locked against concurrent access internally.
39462306a36Sopenharmony_ci *
39562306a36Sopenharmony_ci * RETURNS:
39662306a36Sopenharmony_ci * true if @filp is on the list
39762306a36Sopenharmony_ci */
39862306a36Sopenharmony_cibool drm_vma_node_is_allowed(struct drm_vma_offset_node *node,
39962306a36Sopenharmony_ci			     struct drm_file *tag)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct drm_vma_offset_file *entry;
40262306a36Sopenharmony_ci	struct rb_node *iter;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	read_lock(&node->vm_lock);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	iter = node->vm_files.rb_node;
40762306a36Sopenharmony_ci	while (likely(iter)) {
40862306a36Sopenharmony_ci		entry = rb_entry(iter, struct drm_vma_offset_file, vm_rb);
40962306a36Sopenharmony_ci		if (tag == entry->vm_tag)
41062306a36Sopenharmony_ci			break;
41162306a36Sopenharmony_ci		else if (tag > entry->vm_tag)
41262306a36Sopenharmony_ci			iter = iter->rb_right;
41362306a36Sopenharmony_ci		else
41462306a36Sopenharmony_ci			iter = iter->rb_left;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	read_unlock(&node->vm_lock);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	return iter;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_vma_node_is_allowed);
422