19edbd4a0SFrançois Tigeot /*
29edbd4a0SFrançois Tigeot * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
39edbd4a0SFrançois Tigeot * Copyright (c) 2012 David Airlie <airlied@linux.ie>
49edbd4a0SFrançois Tigeot * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com>
59edbd4a0SFrançois Tigeot *
69edbd4a0SFrançois Tigeot * Permission is hereby granted, free of charge, to any person obtaining a
79edbd4a0SFrançois Tigeot * copy of this software and associated documentation files (the "Software"),
89edbd4a0SFrançois Tigeot * to deal in the Software without restriction, including without limitation
99edbd4a0SFrançois Tigeot * the rights to use, copy, modify, merge, publish, distribute, sublicense,
109edbd4a0SFrançois Tigeot * and/or sell copies of the Software, and to permit persons to whom the
119edbd4a0SFrançois Tigeot * Software is furnished to do so, subject to the following conditions:
129edbd4a0SFrançois Tigeot *
139edbd4a0SFrançois Tigeot * The above copyright notice and this permission notice shall be included in
149edbd4a0SFrançois Tigeot * all copies or substantial portions of the Software.
159edbd4a0SFrançois Tigeot *
169edbd4a0SFrançois Tigeot * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
179edbd4a0SFrançois Tigeot * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
189edbd4a0SFrançois Tigeot * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
199edbd4a0SFrançois Tigeot * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
209edbd4a0SFrançois Tigeot * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
219edbd4a0SFrançois Tigeot * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
229edbd4a0SFrançois Tigeot * OTHER DEALINGS IN THE SOFTWARE.
239edbd4a0SFrançois Tigeot */
249edbd4a0SFrançois Tigeot
259edbd4a0SFrançois Tigeot #include <drm/drmP.h>
269edbd4a0SFrançois Tigeot #include <drm/drm_mm.h>
279edbd4a0SFrançois Tigeot #include <drm/drm_vma_manager.h>
289edbd4a0SFrançois Tigeot #include <linux/mm.h>
299edbd4a0SFrançois Tigeot #include <linux/module.h>
309edbd4a0SFrançois Tigeot #include <linux/rbtree.h>
319edbd4a0SFrançois Tigeot #include <linux/slab.h>
329edbd4a0SFrançois Tigeot #include <linux/spinlock.h>
339edbd4a0SFrançois Tigeot #include <linux/types.h>
349edbd4a0SFrançois Tigeot
359edbd4a0SFrançois Tigeot /**
369edbd4a0SFrançois Tigeot * DOC: vma offset manager
379edbd4a0SFrançois Tigeot *
389edbd4a0SFrançois Tigeot * The vma-manager is responsible to map arbitrary driver-dependent memory
399edbd4a0SFrançois Tigeot * regions into the linear user address-space. It provides offsets to the
409edbd4a0SFrançois Tigeot * caller which can then be used on the address_space of the drm-device. It
419edbd4a0SFrançois Tigeot * takes care to not overlap regions, size them appropriately and to not
429edbd4a0SFrançois Tigeot * confuse mm-core by inconsistent fake vm_pgoff fields.
439edbd4a0SFrançois Tigeot * Drivers shouldn't use this for object placement in VMEM. This manager should
449edbd4a0SFrançois Tigeot * only be used to manage mappings into linear user-space VMs.
459edbd4a0SFrançois Tigeot *
469edbd4a0SFrançois Tigeot * We use drm_mm as backend to manage object allocations. But it is highly
479edbd4a0SFrançois Tigeot * optimized for alloc/free calls, not lookups. Hence, we use an rb-tree to
489edbd4a0SFrançois Tigeot * speed up offset lookups.
499edbd4a0SFrançois Tigeot *
509edbd4a0SFrançois Tigeot * You must not use multiple offset managers on a single address_space.
519edbd4a0SFrançois Tigeot * Otherwise, mm-core will be unable to tear down memory mappings as the VM will
520e1ba51bSFrançois Tigeot * no longer be linear.
539edbd4a0SFrançois Tigeot *
549edbd4a0SFrançois Tigeot * This offset manager works on page-based addresses. That is, every argument
559edbd4a0SFrançois Tigeot * and return code (with the exception of drm_vma_node_offset_addr()) is given
569edbd4a0SFrançois Tigeot * in number of pages, not number of bytes. That means, object sizes and offsets
579edbd4a0SFrançois Tigeot * must always be page-aligned (as usual).
589edbd4a0SFrançois Tigeot * If you want to get a valid byte-based user-space address for a given offset,
599edbd4a0SFrançois Tigeot * please see drm_vma_node_offset_addr().
600e1ba51bSFrançois Tigeot *
610e1ba51bSFrançois Tigeot * Additionally to offset management, the vma offset manager also handles access
620e1ba51bSFrançois Tigeot * management. For every open-file context that is allowed to access a given
630e1ba51bSFrançois Tigeot * node, you must call drm_vma_node_allow(). Otherwise, an mmap() call on this
640e1ba51bSFrançois Tigeot * open-file with the offset of the node will fail with -EACCES. To revoke
650e1ba51bSFrançois Tigeot * access again, use drm_vma_node_revoke(). However, the caller is responsible
660e1ba51bSFrançois Tigeot * for destroying already existing mappings, if required.
679edbd4a0SFrançois Tigeot */
689edbd4a0SFrançois Tigeot
699edbd4a0SFrançois Tigeot /**
709edbd4a0SFrançois Tigeot * drm_vma_offset_manager_init - Initialize new offset-manager
719edbd4a0SFrançois Tigeot * @mgr: Manager object
729edbd4a0SFrançois Tigeot * @page_offset: Offset of available memory area (page-based)
739edbd4a0SFrançois Tigeot * @size: Size of available address space range (page-based)
749edbd4a0SFrançois Tigeot *
759edbd4a0SFrançois Tigeot * Initialize a new offset-manager. The offset and area size available for the
769edbd4a0SFrançois Tigeot * manager are given as @page_offset and @size. Both are interpreted as
779edbd4a0SFrançois Tigeot * page-numbers, not bytes.
789edbd4a0SFrançois Tigeot *
799edbd4a0SFrançois Tigeot * Adding/removing nodes from the manager is locked internally and protected
809edbd4a0SFrançois Tigeot * against concurrent access. However, node allocation and destruction is left
819edbd4a0SFrançois Tigeot * for the caller. While calling into the vma-manager, a given node must
829edbd4a0SFrançois Tigeot * always be guaranteed to be referenced.
839edbd4a0SFrançois Tigeot */
drm_vma_offset_manager_init(struct drm_vma_offset_manager * mgr,unsigned long page_offset,unsigned long size)849edbd4a0SFrançois Tigeot void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
859edbd4a0SFrançois Tigeot unsigned long page_offset, unsigned long size)
869edbd4a0SFrançois Tigeot {
879edbd4a0SFrançois Tigeot lockinit(&mgr->vm_lock, "drmvml", 0, LK_CANRECURSE);
88a8601baeSFrançois Tigeot mgr->vm_addr_space_rb = LINUX_RB_ROOT;
899edbd4a0SFrançois Tigeot drm_mm_init(&mgr->vm_addr_space_mm, page_offset, size);
909edbd4a0SFrançois Tigeot }
919edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_vma_offset_manager_init);
929edbd4a0SFrançois Tigeot
939edbd4a0SFrançois Tigeot /**
949edbd4a0SFrançois Tigeot * drm_vma_offset_manager_destroy() - Destroy offset manager
959edbd4a0SFrançois Tigeot * @mgr: Manager object
969edbd4a0SFrançois Tigeot *
979edbd4a0SFrançois Tigeot * Destroy an object manager which was previously created via
989edbd4a0SFrançois Tigeot * drm_vma_offset_manager_init(). The caller must remove all allocated nodes
999edbd4a0SFrançois Tigeot * before destroying the manager. Otherwise, drm_mm will refuse to free the
1009edbd4a0SFrançois Tigeot * requested resources.
1019edbd4a0SFrançois Tigeot *
1029edbd4a0SFrançois Tigeot * The manager must not be accessed after this function is called.
1039edbd4a0SFrançois Tigeot */
drm_vma_offset_manager_destroy(struct drm_vma_offset_manager * mgr)1049edbd4a0SFrançois Tigeot void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr)
1059edbd4a0SFrançois Tigeot {
1069edbd4a0SFrançois Tigeot /* take the lock to protect against buggy drivers */
1079edbd4a0SFrançois Tigeot lockmgr(&mgr->vm_lock, LK_EXCLUSIVE);
1089edbd4a0SFrançois Tigeot drm_mm_takedown(&mgr->vm_addr_space_mm);
1099edbd4a0SFrançois Tigeot lockmgr(&mgr->vm_lock, LK_RELEASE);
1109edbd4a0SFrançois Tigeot }
1119edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_vma_offset_manager_destroy);
1129edbd4a0SFrançois Tigeot
1139edbd4a0SFrançois Tigeot /**
114352ff8bdSFrançois Tigeot * drm_vma_offset_lookup_locked() - Find node in offset space
1159edbd4a0SFrançois Tigeot * @mgr: Manager object
1169edbd4a0SFrançois Tigeot * @start: Start address for object (page-based)
1179edbd4a0SFrançois Tigeot * @pages: Size of object (page-based)
1189edbd4a0SFrançois Tigeot *
1199edbd4a0SFrançois Tigeot * Find a node given a start address and object size. This returns the _best_
1209edbd4a0SFrançois Tigeot * match for the given node. That is, @start may point somewhere into a valid
1219edbd4a0SFrançois Tigeot * region and the given node will be returned, as long as the node spans the
1229edbd4a0SFrançois Tigeot * whole requested area (given the size in number of pages as @pages).
1239edbd4a0SFrançois Tigeot *
1240e1ba51bSFrançois Tigeot * Note that before lookup the vma offset manager lookup lock must be acquired
1250e1ba51bSFrançois Tigeot * with drm_vma_offset_lock_lookup(). See there for an example. This can then be
1260e1ba51bSFrançois Tigeot * used to implement weakly referenced lookups using kref_get_unless_zero().
1270e1ba51bSFrançois Tigeot *
1280e1ba51bSFrançois Tigeot * Example:
1291dedbd3bSFrançois Tigeot *
1301dedbd3bSFrançois Tigeot * ::
1311dedbd3bSFrançois Tigeot *
1320e1ba51bSFrançois Tigeot * drm_vma_offset_lock_lookup(mgr);
1330e1ba51bSFrançois Tigeot * node = drm_vma_offset_lookup_locked(mgr);
1340e1ba51bSFrançois Tigeot * if (node)
1350e1ba51bSFrançois Tigeot * kref_get_unless_zero(container_of(node, sth, entr));
1360e1ba51bSFrançois Tigeot * drm_vma_offset_unlock_lookup(mgr);
1370e1ba51bSFrançois Tigeot *
1389edbd4a0SFrançois Tigeot * RETURNS:
1399edbd4a0SFrançois Tigeot * Returns NULL if no suitable node can be found. Otherwise, the best match
1409edbd4a0SFrançois Tigeot * is returned. It's the caller's responsibility to make sure the node doesn't
1419edbd4a0SFrançois Tigeot * get destroyed before the caller can access it.
1429edbd4a0SFrançois Tigeot */
drm_vma_offset_lookup_locked(struct drm_vma_offset_manager * mgr,unsigned long start,unsigned long pages)1439edbd4a0SFrançois Tigeot struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr,
1449edbd4a0SFrançois Tigeot unsigned long start,
1459edbd4a0SFrançois Tigeot unsigned long pages)
1469edbd4a0SFrançois Tigeot {
1479edbd4a0SFrançois Tigeot struct drm_vma_offset_node *node, *best;
1489edbd4a0SFrançois Tigeot struct rb_node *iter;
1499edbd4a0SFrançois Tigeot unsigned long offset;
1509edbd4a0SFrançois Tigeot
1519edbd4a0SFrançois Tigeot iter = mgr->vm_addr_space_rb.rb_node;
1529edbd4a0SFrançois Tigeot best = NULL;
1539edbd4a0SFrançois Tigeot
1549edbd4a0SFrançois Tigeot while (likely(iter)) {
1559edbd4a0SFrançois Tigeot node = rb_entry(iter, struct drm_vma_offset_node, vm_rb);
1569edbd4a0SFrançois Tigeot offset = node->vm_node.start;
1579edbd4a0SFrançois Tigeot if (start >= offset) {
1589edbd4a0SFrançois Tigeot iter = iter->rb_right;
1599edbd4a0SFrançois Tigeot best = node;
1609edbd4a0SFrançois Tigeot if (start == offset)
1619edbd4a0SFrançois Tigeot break;
1629edbd4a0SFrançois Tigeot } else {
1639edbd4a0SFrançois Tigeot iter = iter->rb_left;
1649edbd4a0SFrançois Tigeot }
1659edbd4a0SFrançois Tigeot }
1669edbd4a0SFrançois Tigeot
1679edbd4a0SFrançois Tigeot /* verify that the node spans the requested area */
1689edbd4a0SFrançois Tigeot if (best) {
1699edbd4a0SFrançois Tigeot offset = best->vm_node.start + best->vm_node.size;
1709edbd4a0SFrançois Tigeot if (offset < start + pages)
1719edbd4a0SFrançois Tigeot best = NULL;
1729edbd4a0SFrançois Tigeot }
1739edbd4a0SFrançois Tigeot
1749edbd4a0SFrançois Tigeot return best;
1759edbd4a0SFrançois Tigeot }
1769edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_vma_offset_lookup_locked);
1779edbd4a0SFrançois Tigeot
1789edbd4a0SFrançois Tigeot /* internal helper to link @node into the rb-tree */
_drm_vma_offset_add_rb(struct drm_vma_offset_manager * mgr,struct drm_vma_offset_node * node)1799edbd4a0SFrançois Tigeot static void _drm_vma_offset_add_rb(struct drm_vma_offset_manager *mgr,
1809edbd4a0SFrançois Tigeot struct drm_vma_offset_node *node)
1819edbd4a0SFrançois Tigeot {
1829edbd4a0SFrançois Tigeot struct rb_node **iter = &mgr->vm_addr_space_rb.rb_node;
1839edbd4a0SFrançois Tigeot struct rb_node *parent = NULL;
1849edbd4a0SFrançois Tigeot struct drm_vma_offset_node *iter_node;
1859edbd4a0SFrançois Tigeot
1869edbd4a0SFrançois Tigeot while (likely(*iter)) {
1879edbd4a0SFrançois Tigeot parent = *iter;
1889edbd4a0SFrançois Tigeot iter_node = rb_entry(*iter, struct drm_vma_offset_node, vm_rb);
1899edbd4a0SFrançois Tigeot
1909edbd4a0SFrançois Tigeot if (node->vm_node.start < iter_node->vm_node.start)
1919edbd4a0SFrançois Tigeot iter = &(*iter)->rb_left;
1929edbd4a0SFrançois Tigeot else if (node->vm_node.start > iter_node->vm_node.start)
1939edbd4a0SFrançois Tigeot iter = &(*iter)->rb_right;
1949edbd4a0SFrançois Tigeot else
1959edbd4a0SFrançois Tigeot BUG();
1969edbd4a0SFrançois Tigeot }
1979edbd4a0SFrançois Tigeot
1989edbd4a0SFrançois Tigeot rb_link_node(&node->vm_rb, parent, iter);
1999edbd4a0SFrançois Tigeot rb_insert_color(&node->vm_rb, &mgr->vm_addr_space_rb);
2009edbd4a0SFrançois Tigeot }
2019edbd4a0SFrançois Tigeot
2029edbd4a0SFrançois Tigeot /**
2039edbd4a0SFrançois Tigeot * drm_vma_offset_add() - Add offset node to manager
2049edbd4a0SFrançois Tigeot * @mgr: Manager object
2059edbd4a0SFrançois Tigeot * @node: Node to be added
2069edbd4a0SFrançois Tigeot * @pages: Allocation size visible to user-space (in number of pages)
2079edbd4a0SFrançois Tigeot *
2089edbd4a0SFrançois Tigeot * Add a node to the offset-manager. If the node was already added, this does
2099edbd4a0SFrançois Tigeot * nothing and return 0. @pages is the size of the object given in number of
2109edbd4a0SFrançois Tigeot * pages.
2119edbd4a0SFrançois Tigeot * After this call succeeds, you can access the offset of the node until it
2129edbd4a0SFrançois Tigeot * is removed again.
2139edbd4a0SFrançois Tigeot *
2149edbd4a0SFrançois Tigeot * If this call fails, it is safe to retry the operation or call
2159edbd4a0SFrançois Tigeot * drm_vma_offset_remove(), anyway. However, no cleanup is required in that
2169edbd4a0SFrançois Tigeot * case.
2179edbd4a0SFrançois Tigeot *
2189edbd4a0SFrançois Tigeot * @pages is not required to be the same size as the underlying memory object
2199edbd4a0SFrançois Tigeot * that you want to map. It only limits the size that user-space can map into
2209edbd4a0SFrançois Tigeot * their address space.
2219edbd4a0SFrançois Tigeot *
2229edbd4a0SFrançois Tigeot * RETURNS:
2239edbd4a0SFrançois Tigeot * 0 on success, negative error code on failure.
2249edbd4a0SFrançois Tigeot */
drm_vma_offset_add(struct drm_vma_offset_manager * mgr,struct drm_vma_offset_node * node,unsigned long pages)2259edbd4a0SFrançois Tigeot int drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
2269edbd4a0SFrançois Tigeot struct drm_vma_offset_node *node, unsigned long pages)
2279edbd4a0SFrançois Tigeot {
2289edbd4a0SFrançois Tigeot int ret;
2299edbd4a0SFrançois Tigeot
2309edbd4a0SFrançois Tigeot lockmgr(&mgr->vm_lock, LK_EXCLUSIVE);
2319edbd4a0SFrançois Tigeot
2329edbd4a0SFrançois Tigeot if (drm_mm_node_allocated(&node->vm_node)) {
2339edbd4a0SFrançois Tigeot ret = 0;
2349edbd4a0SFrançois Tigeot goto out_unlock;
2359edbd4a0SFrançois Tigeot }
2369edbd4a0SFrançois Tigeot
237*a85cb24fSFrançois Tigeot ret = drm_mm_insert_node(&mgr->vm_addr_space_mm, &node->vm_node, pages);
2389edbd4a0SFrançois Tigeot if (ret)
2399edbd4a0SFrançois Tigeot goto out_unlock;
2409edbd4a0SFrançois Tigeot
2419edbd4a0SFrançois Tigeot _drm_vma_offset_add_rb(mgr, node);
2429edbd4a0SFrançois Tigeot
2439edbd4a0SFrançois Tigeot out_unlock:
2449edbd4a0SFrançois Tigeot lockmgr(&mgr->vm_lock, LK_RELEASE);
2459edbd4a0SFrançois Tigeot return ret;
2469edbd4a0SFrançois Tigeot }
2479edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_vma_offset_add);
2489edbd4a0SFrançois Tigeot
2499edbd4a0SFrançois Tigeot /**
2509edbd4a0SFrançois Tigeot * drm_vma_offset_remove() - Remove offset node from manager
2519edbd4a0SFrançois Tigeot * @mgr: Manager object
2529edbd4a0SFrançois Tigeot * @node: Node to be removed
2539edbd4a0SFrançois Tigeot *
2549edbd4a0SFrançois Tigeot * Remove a node from the offset manager. If the node wasn't added before, this
2559edbd4a0SFrançois Tigeot * does nothing. After this call returns, the offset and size will be 0 until a
2569edbd4a0SFrançois Tigeot * new offset is allocated via drm_vma_offset_add() again. Helper functions like
2579edbd4a0SFrançois Tigeot * drm_vma_node_start() and drm_vma_node_offset_addr() will return 0 if no
2589edbd4a0SFrançois Tigeot * offset is allocated.
2599edbd4a0SFrançois Tigeot */
drm_vma_offset_remove(struct drm_vma_offset_manager * mgr,struct drm_vma_offset_node * node)2609edbd4a0SFrançois Tigeot void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
2619edbd4a0SFrançois Tigeot struct drm_vma_offset_node *node)
2629edbd4a0SFrançois Tigeot {
2639edbd4a0SFrançois Tigeot lockmgr(&mgr->vm_lock, LK_EXCLUSIVE);
2649edbd4a0SFrançois Tigeot
2659edbd4a0SFrançois Tigeot if (drm_mm_node_allocated(&node->vm_node)) {
2669edbd4a0SFrançois Tigeot rb_erase(&node->vm_rb, &mgr->vm_addr_space_rb);
2679edbd4a0SFrançois Tigeot drm_mm_remove_node(&node->vm_node);
2689edbd4a0SFrançois Tigeot memset(&node->vm_node, 0, sizeof(node->vm_node));
2699edbd4a0SFrançois Tigeot }
2709edbd4a0SFrançois Tigeot
2719edbd4a0SFrançois Tigeot lockmgr(&mgr->vm_lock, LK_RELEASE);
2729edbd4a0SFrançois Tigeot }
2739edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_vma_offset_remove);
2740e1ba51bSFrançois Tigeot
2750e1ba51bSFrançois Tigeot /**
2760e1ba51bSFrançois Tigeot * drm_vma_node_allow - Add open-file to list of allowed users
2770e1ba51bSFrançois Tigeot * @node: Node to modify
2781dedbd3bSFrançois Tigeot * @tag: Tag of file to remove
2790e1ba51bSFrançois Tigeot *
2801dedbd3bSFrançois Tigeot * Add @tag to the list of allowed open-files for this node. If @tag is
2810e1ba51bSFrançois Tigeot * already on this list, the ref-count is incremented.
2820e1ba51bSFrançois Tigeot *
2830e1ba51bSFrançois Tigeot * The list of allowed-users is preserved across drm_vma_offset_add() and
2840e1ba51bSFrançois Tigeot * drm_vma_offset_remove() calls. You may even call it if the node is currently
2850e1ba51bSFrançois Tigeot * not added to any offset-manager.
2860e1ba51bSFrançois Tigeot *
2870e1ba51bSFrançois Tigeot * You must remove all open-files the same number of times as you added them
2880e1ba51bSFrançois Tigeot * before destroying the node. Otherwise, you will leak memory.
2890e1ba51bSFrançois Tigeot *
2900e1ba51bSFrançois Tigeot * This is locked against concurrent access internally.
2910e1ba51bSFrançois Tigeot *
2920e1ba51bSFrançois Tigeot * RETURNS:
2930e1ba51bSFrançois Tigeot * 0 on success, negative error code on internal failure (out-of-mem)
2940e1ba51bSFrançois Tigeot */
drm_vma_node_allow(struct drm_vma_offset_node * node,struct drm_file * tag)2951dedbd3bSFrançois Tigeot int drm_vma_node_allow(struct drm_vma_offset_node *node, struct drm_file *tag)
2960e1ba51bSFrançois Tigeot {
2970e1ba51bSFrançois Tigeot struct rb_node **iter;
2980e1ba51bSFrançois Tigeot struct rb_node *parent = NULL;
2990e1ba51bSFrançois Tigeot struct drm_vma_offset_file *new, *entry;
3000e1ba51bSFrançois Tigeot int ret = 0;
3010e1ba51bSFrançois Tigeot
3020e1ba51bSFrançois Tigeot /* Preallocate entry to avoid atomic allocations below. It is quite
3030e1ba51bSFrançois Tigeot * unlikely that an open-file is added twice to a single node so we
3040e1ba51bSFrançois Tigeot * don't optimize for this case. OOM is checked below only if the entry
3050e1ba51bSFrançois Tigeot * is actually used. */
3060e1ba51bSFrançois Tigeot new = kmalloc(sizeof(*entry), M_DRM, M_WAITOK);
3070e1ba51bSFrançois Tigeot
3080e1ba51bSFrançois Tigeot lockmgr(&node->vm_lock, LK_EXCLUSIVE);
3090e1ba51bSFrançois Tigeot
3100e1ba51bSFrançois Tigeot iter = &node->vm_files.rb_node;
3110e1ba51bSFrançois Tigeot
3120e1ba51bSFrançois Tigeot while (likely(*iter)) {
3130e1ba51bSFrançois Tigeot parent = *iter;
3140e1ba51bSFrançois Tigeot entry = rb_entry(*iter, struct drm_vma_offset_file, vm_rb);
3150e1ba51bSFrançois Tigeot
3161dedbd3bSFrançois Tigeot if (tag == entry->vm_tag) {
3170e1ba51bSFrançois Tigeot entry->vm_count++;
3180e1ba51bSFrançois Tigeot goto unlock;
3191dedbd3bSFrançois Tigeot } else if (tag > entry->vm_tag) {
3200e1ba51bSFrançois Tigeot iter = &(*iter)->rb_right;
3210e1ba51bSFrançois Tigeot } else {
3220e1ba51bSFrançois Tigeot iter = &(*iter)->rb_left;
3230e1ba51bSFrançois Tigeot }
3240e1ba51bSFrançois Tigeot }
3250e1ba51bSFrançois Tigeot
3260e1ba51bSFrançois Tigeot if (!new) {
3270e1ba51bSFrançois Tigeot ret = -ENOMEM;
3280e1ba51bSFrançois Tigeot goto unlock;
3290e1ba51bSFrançois Tigeot }
3300e1ba51bSFrançois Tigeot
3311dedbd3bSFrançois Tigeot new->vm_tag = tag;
3320e1ba51bSFrançois Tigeot new->vm_count = 1;
3330e1ba51bSFrançois Tigeot rb_link_node(&new->vm_rb, parent, iter);
3340e1ba51bSFrançois Tigeot rb_insert_color(&new->vm_rb, &node->vm_files);
3350e1ba51bSFrançois Tigeot new = NULL;
3360e1ba51bSFrançois Tigeot
3370e1ba51bSFrançois Tigeot unlock:
3380e1ba51bSFrançois Tigeot lockmgr(&node->vm_lock, LK_RELEASE);
3390e1ba51bSFrançois Tigeot kfree(new);
3400e1ba51bSFrançois Tigeot return ret;
3410e1ba51bSFrançois Tigeot }
3420e1ba51bSFrançois Tigeot EXPORT_SYMBOL(drm_vma_node_allow);
3430e1ba51bSFrançois Tigeot
3440e1ba51bSFrançois Tigeot /**
3450e1ba51bSFrançois Tigeot * drm_vma_node_revoke - Remove open-file from list of allowed users
3460e1ba51bSFrançois Tigeot * @node: Node to modify
3471dedbd3bSFrançois Tigeot * @tag: Tag of file to remove
3480e1ba51bSFrançois Tigeot *
3491dedbd3bSFrançois Tigeot * Decrement the ref-count of @tag in the list of allowed open-files on @node.
3501dedbd3bSFrançois Tigeot * If the ref-count drops to zero, remove @tag from the list. You must call
3511dedbd3bSFrançois Tigeot * this once for every drm_vma_node_allow() on @tag.
3520e1ba51bSFrançois Tigeot *
3530e1ba51bSFrançois Tigeot * This is locked against concurrent access internally.
3540e1ba51bSFrançois Tigeot *
3551dedbd3bSFrançois Tigeot * If @tag is not on the list, nothing is done.
3560e1ba51bSFrançois Tigeot */
drm_vma_node_revoke(struct drm_vma_offset_node * node,struct drm_file * tag)3571dedbd3bSFrançois Tigeot void drm_vma_node_revoke(struct drm_vma_offset_node *node,
3581dedbd3bSFrançois Tigeot struct drm_file *tag)
3590e1ba51bSFrançois Tigeot {
3600e1ba51bSFrançois Tigeot struct drm_vma_offset_file *entry;
3610e1ba51bSFrançois Tigeot struct rb_node *iter;
3620e1ba51bSFrançois Tigeot
3630e1ba51bSFrançois Tigeot lockmgr(&node->vm_lock, LK_EXCLUSIVE);
3640e1ba51bSFrançois Tigeot
3650e1ba51bSFrançois Tigeot iter = node->vm_files.rb_node;
3660e1ba51bSFrançois Tigeot while (likely(iter)) {
3670e1ba51bSFrançois Tigeot entry = rb_entry(iter, struct drm_vma_offset_file, vm_rb);
3681dedbd3bSFrançois Tigeot if (tag == entry->vm_tag) {
3690e1ba51bSFrançois Tigeot if (!--entry->vm_count) {
3700e1ba51bSFrançois Tigeot rb_erase(&entry->vm_rb, &node->vm_files);
3710e1ba51bSFrançois Tigeot kfree(entry);
3720e1ba51bSFrançois Tigeot }
3730e1ba51bSFrançois Tigeot break;
3741dedbd3bSFrançois Tigeot } else if (tag > entry->vm_tag) {
3750e1ba51bSFrançois Tigeot iter = iter->rb_right;
3760e1ba51bSFrançois Tigeot } else {
3770e1ba51bSFrançois Tigeot iter = iter->rb_left;
3780e1ba51bSFrançois Tigeot }
3790e1ba51bSFrançois Tigeot }
3800e1ba51bSFrançois Tigeot
3810e1ba51bSFrançois Tigeot lockmgr(&node->vm_lock, LK_RELEASE);
3820e1ba51bSFrançois Tigeot }
3830e1ba51bSFrançois Tigeot EXPORT_SYMBOL(drm_vma_node_revoke);
3840e1ba51bSFrançois Tigeot
3850e1ba51bSFrançois Tigeot /**
3860e1ba51bSFrançois Tigeot * drm_vma_node_is_allowed - Check whether an open-file is granted access
3870e1ba51bSFrançois Tigeot * @node: Node to check
3881dedbd3bSFrançois Tigeot * @tag: Tag of file to remove
3890e1ba51bSFrançois Tigeot *
3901dedbd3bSFrançois Tigeot * Search the list in @node whether @tag is currently on the list of allowed
3910e1ba51bSFrançois Tigeot * open-files (see drm_vma_node_allow()).
3920e1ba51bSFrançois Tigeot *
3930e1ba51bSFrançois Tigeot * This is locked against concurrent access internally.
3940e1ba51bSFrançois Tigeot *
3950e1ba51bSFrançois Tigeot * RETURNS:
3960e1ba51bSFrançois Tigeot * true iff @filp is on the list
3970e1ba51bSFrançois Tigeot */
drm_vma_node_is_allowed(struct drm_vma_offset_node * node,struct drm_file * tag)3980e1ba51bSFrançois Tigeot bool drm_vma_node_is_allowed(struct drm_vma_offset_node *node,
3991dedbd3bSFrançois Tigeot struct drm_file *tag)
4000e1ba51bSFrançois Tigeot {
4010e1ba51bSFrançois Tigeot struct drm_vma_offset_file *entry;
4020e1ba51bSFrançois Tigeot struct rb_node *iter;
4030e1ba51bSFrançois Tigeot
4040e1ba51bSFrançois Tigeot lockmgr(&node->vm_lock, LK_EXCLUSIVE);
4050e1ba51bSFrançois Tigeot
4060e1ba51bSFrançois Tigeot iter = node->vm_files.rb_node;
4070e1ba51bSFrançois Tigeot while (likely(iter)) {
4080e1ba51bSFrançois Tigeot entry = rb_entry(iter, struct drm_vma_offset_file, vm_rb);
4091dedbd3bSFrançois Tigeot if (tag == entry->vm_tag)
4100e1ba51bSFrançois Tigeot break;
4111dedbd3bSFrançois Tigeot else if (tag > entry->vm_tag)
4120e1ba51bSFrançois Tigeot iter = iter->rb_right;
4130e1ba51bSFrançois Tigeot else
4140e1ba51bSFrançois Tigeot iter = iter->rb_left;
4150e1ba51bSFrançois Tigeot }
4160e1ba51bSFrançois Tigeot
4170e1ba51bSFrançois Tigeot lockmgr(&node->vm_lock, LK_RELEASE);
4180e1ba51bSFrançois Tigeot
4190e1ba51bSFrançois Tigeot return iter;
4200e1ba51bSFrançois Tigeot }
4210e1ba51bSFrançois Tigeot EXPORT_SYMBOL(drm_vma_node_is_allowed);
422