18c2ecf20Sopenharmony_ci#ifndef __DRM_VMA_MANAGER_H__ 28c2ecf20Sopenharmony_ci#define __DRM_VMA_MANAGER_H__ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* 58c2ecf20Sopenharmony_ci * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 88c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 98c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 108c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 118c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 128c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 158c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 188c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 198c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 208c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 218c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 228c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 238c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <drm/drm_mm.h> 278c2ecf20Sopenharmony_ci#include <linux/mm.h> 288c2ecf20Sopenharmony_ci#include <linux/rbtree.h> 298c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 308c2ecf20Sopenharmony_ci#include <linux/types.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* We make up offsets for buffer objects so we can recognize them at 338c2ecf20Sopenharmony_ci * mmap time. pgoff in mmap is an unsigned long, so we need to make sure 348c2ecf20Sopenharmony_ci * that the faked up offset will fit 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci#if BITS_PER_LONG == 64 378c2ecf20Sopenharmony_ci#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1) 388c2ecf20Sopenharmony_ci#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 256) 398c2ecf20Sopenharmony_ci#else 408c2ecf20Sopenharmony_ci#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFUL >> PAGE_SHIFT) + 1) 418c2ecf20Sopenharmony_ci#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFUL >> PAGE_SHIFT) * 16) 428c2ecf20Sopenharmony_ci#endif 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct drm_file; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct drm_vma_offset_file { 478c2ecf20Sopenharmony_ci struct rb_node vm_rb; 488c2ecf20Sopenharmony_ci struct drm_file *vm_tag; 498c2ecf20Sopenharmony_ci unsigned long vm_count; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistruct drm_vma_offset_node { 538c2ecf20Sopenharmony_ci rwlock_t vm_lock; 548c2ecf20Sopenharmony_ci struct drm_mm_node vm_node; 558c2ecf20Sopenharmony_ci struct rb_root vm_files; 568c2ecf20Sopenharmony_ci bool readonly:1; 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct drm_vma_offset_manager { 608c2ecf20Sopenharmony_ci rwlock_t vm_lock; 618c2ecf20Sopenharmony_ci struct drm_mm vm_addr_space_mm; 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_civoid drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr, 658c2ecf20Sopenharmony_ci unsigned long page_offset, unsigned long size); 668c2ecf20Sopenharmony_civoid drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistruct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr, 698c2ecf20Sopenharmony_ci unsigned long start, 708c2ecf20Sopenharmony_ci unsigned long pages); 718c2ecf20Sopenharmony_ciint drm_vma_offset_add(struct drm_vma_offset_manager *mgr, 728c2ecf20Sopenharmony_ci struct drm_vma_offset_node *node, unsigned long pages); 738c2ecf20Sopenharmony_civoid drm_vma_offset_remove(struct drm_vma_offset_manager *mgr, 748c2ecf20Sopenharmony_ci struct drm_vma_offset_node *node); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ciint drm_vma_node_allow(struct drm_vma_offset_node *node, struct drm_file *tag); 778c2ecf20Sopenharmony_civoid drm_vma_node_revoke(struct drm_vma_offset_node *node, 788c2ecf20Sopenharmony_ci struct drm_file *tag); 798c2ecf20Sopenharmony_cibool drm_vma_node_is_allowed(struct drm_vma_offset_node *node, 808c2ecf20Sopenharmony_ci struct drm_file *tag); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/** 838c2ecf20Sopenharmony_ci * drm_vma_offset_exact_lookup_locked() - Look up node by exact address 848c2ecf20Sopenharmony_ci * @mgr: Manager object 858c2ecf20Sopenharmony_ci * @start: Start address (page-based, not byte-based) 868c2ecf20Sopenharmony_ci * @pages: Size of object (page-based) 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * Same as drm_vma_offset_lookup_locked() but does not allow any offset into the node. 898c2ecf20Sopenharmony_ci * It only returns the exact object with the given start address. 908c2ecf20Sopenharmony_ci * 918c2ecf20Sopenharmony_ci * RETURNS: 928c2ecf20Sopenharmony_ci * Node at exact start address @start. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_cistatic inline struct drm_vma_offset_node * 958c2ecf20Sopenharmony_cidrm_vma_offset_exact_lookup_locked(struct drm_vma_offset_manager *mgr, 968c2ecf20Sopenharmony_ci unsigned long start, 978c2ecf20Sopenharmony_ci unsigned long pages) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct drm_vma_offset_node *node; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci node = drm_vma_offset_lookup_locked(mgr, start, pages); 1028c2ecf20Sopenharmony_ci return (node && node->vm_node.start == start) ? node : NULL; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/** 1068c2ecf20Sopenharmony_ci * drm_vma_offset_lock_lookup() - Lock lookup for extended private use 1078c2ecf20Sopenharmony_ci * @mgr: Manager object 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * Lock VMA manager for extended lookups. Only locked VMA function calls 1108c2ecf20Sopenharmony_ci * are allowed while holding this lock. All other contexts are blocked from VMA 1118c2ecf20Sopenharmony_ci * until the lock is released via drm_vma_offset_unlock_lookup(). 1128c2ecf20Sopenharmony_ci * 1138c2ecf20Sopenharmony_ci * Use this if you need to take a reference to the objects returned by 1148c2ecf20Sopenharmony_ci * drm_vma_offset_lookup_locked() before releasing this lock again. 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * This lock must not be used for anything else than extended lookups. You must 1178c2ecf20Sopenharmony_ci * not call any other VMA helpers while holding this lock. 1188c2ecf20Sopenharmony_ci * 1198c2ecf20Sopenharmony_ci * Note: You're in atomic-context while holding this lock! 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_cistatic inline void drm_vma_offset_lock_lookup(struct drm_vma_offset_manager *mgr) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci read_lock(&mgr->vm_lock); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/** 1278c2ecf20Sopenharmony_ci * drm_vma_offset_unlock_lookup() - Unlock lookup for extended private use 1288c2ecf20Sopenharmony_ci * @mgr: Manager object 1298c2ecf20Sopenharmony_ci * 1308c2ecf20Sopenharmony_ci * Release lookup-lock. See drm_vma_offset_lock_lookup() for more information. 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_cistatic inline void drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager *mgr) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci read_unlock(&mgr->vm_lock); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/** 1388c2ecf20Sopenharmony_ci * drm_vma_node_reset() - Initialize or reset node object 1398c2ecf20Sopenharmony_ci * @node: Node to initialize or reset 1408c2ecf20Sopenharmony_ci * 1418c2ecf20Sopenharmony_ci * Reset a node to its initial state. This must be called before using it with 1428c2ecf20Sopenharmony_ci * any VMA offset manager. 1438c2ecf20Sopenharmony_ci * 1448c2ecf20Sopenharmony_ci * This must not be called on an already allocated node, or you will leak 1458c2ecf20Sopenharmony_ci * memory. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_cistatic inline void drm_vma_node_reset(struct drm_vma_offset_node *node) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci memset(node, 0, sizeof(*node)); 1508c2ecf20Sopenharmony_ci node->vm_files = RB_ROOT; 1518c2ecf20Sopenharmony_ci rwlock_init(&node->vm_lock); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/** 1558c2ecf20Sopenharmony_ci * drm_vma_node_start() - Return start address for page-based addressing 1568c2ecf20Sopenharmony_ci * @node: Node to inspect 1578c2ecf20Sopenharmony_ci * 1588c2ecf20Sopenharmony_ci * Return the start address of the given node. This can be used as offset into 1598c2ecf20Sopenharmony_ci * the linear VM space that is provided by the VMA offset manager. Note that 1608c2ecf20Sopenharmony_ci * this can only be used for page-based addressing. If you need a proper offset 1618c2ecf20Sopenharmony_ci * for user-space mappings, you must apply "<< PAGE_SHIFT" or use the 1628c2ecf20Sopenharmony_ci * drm_vma_node_offset_addr() helper instead. 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * RETURNS: 1658c2ecf20Sopenharmony_ci * Start address of @node for page-based addressing. 0 if the node does not 1668c2ecf20Sopenharmony_ci * have an offset allocated. 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_cistatic inline unsigned long drm_vma_node_start(const struct drm_vma_offset_node *node) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci return node->vm_node.start; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/** 1748c2ecf20Sopenharmony_ci * drm_vma_node_size() - Return size (page-based) 1758c2ecf20Sopenharmony_ci * @node: Node to inspect 1768c2ecf20Sopenharmony_ci * 1778c2ecf20Sopenharmony_ci * Return the size as number of pages for the given node. This is the same size 1788c2ecf20Sopenharmony_ci * that was passed to drm_vma_offset_add(). If no offset is allocated for the 1798c2ecf20Sopenharmony_ci * node, this is 0. 1808c2ecf20Sopenharmony_ci * 1818c2ecf20Sopenharmony_ci * RETURNS: 1828c2ecf20Sopenharmony_ci * Size of @node as number of pages. 0 if the node does not have an offset 1838c2ecf20Sopenharmony_ci * allocated. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_cistatic inline unsigned long drm_vma_node_size(struct drm_vma_offset_node *node) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci return node->vm_node.size; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/** 1918c2ecf20Sopenharmony_ci * drm_vma_node_offset_addr() - Return sanitized offset for user-space mmaps 1928c2ecf20Sopenharmony_ci * @node: Linked offset node 1938c2ecf20Sopenharmony_ci * 1948c2ecf20Sopenharmony_ci * Same as drm_vma_node_start() but returns the address as a valid offset that 1958c2ecf20Sopenharmony_ci * can be used for user-space mappings during mmap(). 1968c2ecf20Sopenharmony_ci * This must not be called on unlinked nodes. 1978c2ecf20Sopenharmony_ci * 1988c2ecf20Sopenharmony_ci * RETURNS: 1998c2ecf20Sopenharmony_ci * Offset of @node for byte-based addressing. 0 if the node does not have an 2008c2ecf20Sopenharmony_ci * object allocated. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_cistatic inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci return ((__u64)node->vm_node.start) << PAGE_SHIFT; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/** 2088c2ecf20Sopenharmony_ci * drm_vma_node_unmap() - Unmap offset node 2098c2ecf20Sopenharmony_ci * @node: Offset node 2108c2ecf20Sopenharmony_ci * @file_mapping: Address space to unmap @node from 2118c2ecf20Sopenharmony_ci * 2128c2ecf20Sopenharmony_ci * Unmap all userspace mappings for a given offset node. The mappings must be 2138c2ecf20Sopenharmony_ci * associated with the @file_mapping address-space. If no offset exists 2148c2ecf20Sopenharmony_ci * nothing is done. 2158c2ecf20Sopenharmony_ci * 2168c2ecf20Sopenharmony_ci * This call is unlocked. The caller must guarantee that drm_vma_offset_remove() 2178c2ecf20Sopenharmony_ci * is not called on this node concurrently. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_cistatic inline void drm_vma_node_unmap(struct drm_vma_offset_node *node, 2208c2ecf20Sopenharmony_ci struct address_space *file_mapping) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci if (drm_mm_node_allocated(&node->vm_node)) 2238c2ecf20Sopenharmony_ci unmap_mapping_range(file_mapping, 2248c2ecf20Sopenharmony_ci drm_vma_node_offset_addr(node), 2258c2ecf20Sopenharmony_ci drm_vma_node_size(node) << PAGE_SHIFT, 1); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/** 2298c2ecf20Sopenharmony_ci * drm_vma_node_verify_access() - Access verification helper for TTM 2308c2ecf20Sopenharmony_ci * @node: Offset node 2318c2ecf20Sopenharmony_ci * @tag: Tag of file to check 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * This checks whether @tag is granted access to @node. It is the same as 2348c2ecf20Sopenharmony_ci * drm_vma_node_is_allowed() but suitable as drop-in helper for TTM 2358c2ecf20Sopenharmony_ci * verify_access() callbacks. 2368c2ecf20Sopenharmony_ci * 2378c2ecf20Sopenharmony_ci * RETURNS: 2388c2ecf20Sopenharmony_ci * 0 if access is granted, -EACCES otherwise. 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_cistatic inline int drm_vma_node_verify_access(struct drm_vma_offset_node *node, 2418c2ecf20Sopenharmony_ci struct drm_file *tag) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci return drm_vma_node_is_allowed(node, tag) ? 0 : -EACCES; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci#endif /* __DRM_VMA_MANAGER_H__ */ 247