19edbd4a0SFrançois Tigeot #ifndef __DRM_VMA_MANAGER_H__ 29edbd4a0SFrançois Tigeot #define __DRM_VMA_MANAGER_H__ 39edbd4a0SFrançois Tigeot 49edbd4a0SFrançois Tigeot /* 59edbd4a0SFrançois Tigeot * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com> 69edbd4a0SFrançois Tigeot * 79edbd4a0SFrançois Tigeot * Permission is hereby granted, free of charge, to any person obtaining a 89edbd4a0SFrançois Tigeot * copy of this software and associated documentation files (the "Software"), 99edbd4a0SFrançois Tigeot * to deal in the Software without restriction, including without limitation 109edbd4a0SFrançois Tigeot * the rights to use, copy, modify, merge, publish, distribute, sublicense, 119edbd4a0SFrançois Tigeot * and/or sell copies of the Software, and to permit persons to whom the 129edbd4a0SFrançois Tigeot * Software is furnished to do so, subject to the following conditions: 139edbd4a0SFrançois Tigeot * 149edbd4a0SFrançois Tigeot * The above copyright notice and this permission notice shall be included in 159edbd4a0SFrançois Tigeot * all copies or substantial portions of the Software. 169edbd4a0SFrançois Tigeot * 179edbd4a0SFrançois Tigeot * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 189edbd4a0SFrançois Tigeot * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 199edbd4a0SFrançois Tigeot * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 209edbd4a0SFrançois Tigeot * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 219edbd4a0SFrançois Tigeot * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 229edbd4a0SFrançois Tigeot * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 239edbd4a0SFrançois Tigeot * OTHER DEALINGS IN THE SOFTWARE. 249edbd4a0SFrançois Tigeot */ 259edbd4a0SFrançois Tigeot 269edbd4a0SFrançois Tigeot #include <drm/drm_mm.h> 279edbd4a0SFrançois Tigeot #include <linux/mm.h> 289edbd4a0SFrançois Tigeot #include <linux/module.h> 299edbd4a0SFrançois Tigeot #include <linux/rbtree.h> 309edbd4a0SFrançois Tigeot #include <linux/spinlock.h> 319edbd4a0SFrançois Tigeot #include <linux/types.h> 329edbd4a0SFrançois Tigeot 33ba55f2f5SFrançois Tigeot struct drm_vma_offset_file { 34ba55f2f5SFrançois Tigeot struct rb_node vm_rb; 35ba55f2f5SFrançois Tigeot struct file *vm_filp; 36ba55f2f5SFrançois Tigeot unsigned long vm_count; 37ba55f2f5SFrançois Tigeot }; 38ba55f2f5SFrançois Tigeot 399edbd4a0SFrançois Tigeot struct drm_vma_offset_node { 40ba55f2f5SFrançois Tigeot struct lock vm_lock; 419edbd4a0SFrançois Tigeot struct drm_mm_node vm_node; 429edbd4a0SFrançois Tigeot struct rb_node vm_rb; 43ba55f2f5SFrançois Tigeot struct rb_root vm_files; 449edbd4a0SFrançois Tigeot }; 459edbd4a0SFrançois Tigeot 469edbd4a0SFrançois Tigeot struct drm_vma_offset_manager { 479edbd4a0SFrançois Tigeot struct lock vm_lock; 489edbd4a0SFrançois Tigeot struct rb_root vm_addr_space_rb; 499edbd4a0SFrançois Tigeot struct drm_mm vm_addr_space_mm; 509edbd4a0SFrançois Tigeot }; 519edbd4a0SFrançois Tigeot 529edbd4a0SFrançois Tigeot void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr, 539edbd4a0SFrançois Tigeot unsigned long page_offset, unsigned long size); 549edbd4a0SFrançois Tigeot void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr); 559edbd4a0SFrançois Tigeot 569edbd4a0SFrançois Tigeot struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr, 579edbd4a0SFrançois Tigeot unsigned long start, 589edbd4a0SFrançois Tigeot unsigned long pages); 599edbd4a0SFrançois Tigeot int drm_vma_offset_add(struct drm_vma_offset_manager *mgr, 609edbd4a0SFrançois Tigeot struct drm_vma_offset_node *node, unsigned long pages); 619edbd4a0SFrançois Tigeot void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr, 629edbd4a0SFrançois Tigeot struct drm_vma_offset_node *node); 639edbd4a0SFrançois Tigeot 64ba55f2f5SFrançois Tigeot int drm_vma_node_allow(struct drm_vma_offset_node *node, struct file *filp); 65ba55f2f5SFrançois Tigeot void drm_vma_node_revoke(struct drm_vma_offset_node *node, struct file *filp); 66ba55f2f5SFrançois Tigeot bool drm_vma_node_is_allowed(struct drm_vma_offset_node *node, 67ba55f2f5SFrançois Tigeot struct file *filp); 68ba55f2f5SFrançois Tigeot 699edbd4a0SFrançois Tigeot /** 70*0e1ba51bSFrançois Tigeot * drm_vma_offset_exact_lookup_locked() - Look up node by exact address 719edbd4a0SFrançois Tigeot * @mgr: Manager object 729edbd4a0SFrançois Tigeot * @start: Start address (page-based, not byte-based) 739edbd4a0SFrançois Tigeot * @pages: Size of object (page-based) 749edbd4a0SFrançois Tigeot * 75*0e1ba51bSFrançois Tigeot * Same as drm_vma_offset_lookup_locked() but does not allow any offset into the node. 769edbd4a0SFrançois Tigeot * It only returns the exact object with the given start address. 779edbd4a0SFrançois Tigeot * 789edbd4a0SFrançois Tigeot * RETURNS: 799edbd4a0SFrançois Tigeot * Node at exact start address @start. 809edbd4a0SFrançois Tigeot */ 819edbd4a0SFrançois Tigeot static inline struct drm_vma_offset_node * 82*0e1ba51bSFrançois Tigeot drm_vma_offset_exact_lookup_locked(struct drm_vma_offset_manager *mgr, 839edbd4a0SFrançois Tigeot unsigned long start, 849edbd4a0SFrançois Tigeot unsigned long pages) 859edbd4a0SFrançois Tigeot { 869edbd4a0SFrançois Tigeot struct drm_vma_offset_node *node; 879edbd4a0SFrançois Tigeot 88*0e1ba51bSFrançois Tigeot node = drm_vma_offset_lookup_locked(mgr, start, pages); 899edbd4a0SFrançois Tigeot return (node && node->vm_node.start == start) ? node : NULL; 909edbd4a0SFrançois Tigeot } 919edbd4a0SFrançois Tigeot 929edbd4a0SFrançois Tigeot /** 939edbd4a0SFrançois Tigeot * drm_vma_offset_lock_lookup() - Lock lookup for extended private use 949edbd4a0SFrançois Tigeot * @mgr: Manager object 959edbd4a0SFrançois Tigeot * 96*0e1ba51bSFrançois Tigeot * Lock VMA manager for extended lookups. Only locked VMA function calls 979edbd4a0SFrançois Tigeot * are allowed while holding this lock. All other contexts are blocked from VMA 989edbd4a0SFrançois Tigeot * until the lock is released via drm_vma_offset_unlock_lookup(). 999edbd4a0SFrançois Tigeot * 1009edbd4a0SFrançois Tigeot * Use this if you need to take a reference to the objects returned by 1019edbd4a0SFrançois Tigeot * drm_vma_offset_lookup_locked() before releasing this lock again. 1029edbd4a0SFrançois Tigeot * 1039edbd4a0SFrançois Tigeot * This lock must not be used for anything else than extended lookups. You must 1049edbd4a0SFrançois Tigeot * not call any other VMA helpers while holding this lock. 1059edbd4a0SFrançois Tigeot * 1069edbd4a0SFrançois Tigeot * Note: You're in atomic-context while holding this lock! 1079edbd4a0SFrançois Tigeot */ 1089edbd4a0SFrançois Tigeot static inline void drm_vma_offset_lock_lookup(struct drm_vma_offset_manager *mgr) 1099edbd4a0SFrançois Tigeot { 1109edbd4a0SFrançois Tigeot lockmgr(&mgr->vm_lock, LK_EXCLUSIVE); 1119edbd4a0SFrançois Tigeot } 1129edbd4a0SFrançois Tigeot 1139edbd4a0SFrançois Tigeot /** 1149edbd4a0SFrançois Tigeot * drm_vma_offset_unlock_lookup() - Unlock lookup for extended private use 1159edbd4a0SFrançois Tigeot * @mgr: Manager object 1169edbd4a0SFrançois Tigeot * 1179edbd4a0SFrançois Tigeot * Release lookup-lock. See drm_vma_offset_lock_lookup() for more information. 1189edbd4a0SFrançois Tigeot */ 1199edbd4a0SFrançois Tigeot static inline void drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager *mgr) 1209edbd4a0SFrançois Tigeot { 1219edbd4a0SFrançois Tigeot lockmgr(&mgr->vm_lock, LK_RELEASE); 1229edbd4a0SFrançois Tigeot } 1239edbd4a0SFrançois Tigeot 1249edbd4a0SFrançois Tigeot /** 1259edbd4a0SFrançois Tigeot * drm_vma_node_reset() - Initialize or reset node object 1269edbd4a0SFrançois Tigeot * @node: Node to initialize or reset 1279edbd4a0SFrançois Tigeot * 128ba55f2f5SFrançois Tigeot * Reset a node to its initial state. This must be called before using it with 129ba55f2f5SFrançois Tigeot * any VMA offset manager. 1309edbd4a0SFrançois Tigeot * 1319edbd4a0SFrançois Tigeot * This must not be called on an already allocated node, or you will leak 1329edbd4a0SFrançois Tigeot * memory. 1339edbd4a0SFrançois Tigeot */ 1349edbd4a0SFrançois Tigeot static inline void drm_vma_node_reset(struct drm_vma_offset_node *node) 1359edbd4a0SFrançois Tigeot { 1369edbd4a0SFrançois Tigeot memset(node, 0, sizeof(*node)); 137ba55f2f5SFrançois Tigeot node->vm_files = RB_ROOT; 138ba55f2f5SFrançois Tigeot lockinit(&node->vm_lock, "vmlock", 0, LK_CANRECURSE); 1399edbd4a0SFrançois Tigeot } 1409edbd4a0SFrançois Tigeot 1419edbd4a0SFrançois Tigeot /** 1429edbd4a0SFrançois Tigeot * drm_vma_node_start() - Return start address for page-based addressing 1439edbd4a0SFrançois Tigeot * @node: Node to inspect 1449edbd4a0SFrançois Tigeot * 1459edbd4a0SFrançois Tigeot * Return the start address of the given node. This can be used as offset into 1469edbd4a0SFrançois Tigeot * the linear VM space that is provided by the VMA offset manager. Note that 1479edbd4a0SFrançois Tigeot * this can only be used for page-based addressing. If you need a proper offset 1489edbd4a0SFrançois Tigeot * for user-space mappings, you must apply "<< PAGE_SHIFT" or use the 1499edbd4a0SFrançois Tigeot * drm_vma_node_offset_addr() helper instead. 1509edbd4a0SFrançois Tigeot * 1519edbd4a0SFrançois Tigeot * RETURNS: 1529edbd4a0SFrançois Tigeot * Start address of @node for page-based addressing. 0 if the node does not 1539edbd4a0SFrançois Tigeot * have an offset allocated. 1549edbd4a0SFrançois Tigeot */ 1559edbd4a0SFrançois Tigeot static inline unsigned long drm_vma_node_start(struct drm_vma_offset_node *node) 1569edbd4a0SFrançois Tigeot { 1579edbd4a0SFrançois Tigeot return node->vm_node.start; 1589edbd4a0SFrançois Tigeot } 1599edbd4a0SFrançois Tigeot 1609edbd4a0SFrançois Tigeot /** 1619edbd4a0SFrançois Tigeot * drm_vma_node_size() - Return size (page-based) 1629edbd4a0SFrançois Tigeot * @node: Node to inspect 1639edbd4a0SFrançois Tigeot * 1649edbd4a0SFrançois Tigeot * Return the size as number of pages for the given node. This is the same size 1659edbd4a0SFrançois Tigeot * that was passed to drm_vma_offset_add(). If no offset is allocated for the 1669edbd4a0SFrançois Tigeot * node, this is 0. 1679edbd4a0SFrançois Tigeot * 1689edbd4a0SFrançois Tigeot * RETURNS: 1699edbd4a0SFrançois Tigeot * Size of @node as number of pages. 0 if the node does not have an offset 1709edbd4a0SFrançois Tigeot * allocated. 1719edbd4a0SFrançois Tigeot */ 1729edbd4a0SFrançois Tigeot static inline unsigned long drm_vma_node_size(struct drm_vma_offset_node *node) 1739edbd4a0SFrançois Tigeot { 1749edbd4a0SFrançois Tigeot return node->vm_node.size; 1759edbd4a0SFrançois Tigeot } 1769edbd4a0SFrançois Tigeot 1779edbd4a0SFrançois Tigeot /** 1789edbd4a0SFrançois Tigeot * drm_vma_node_has_offset() - Check whether node is added to offset manager 1799edbd4a0SFrançois Tigeot * @node: Node to be checked 1809edbd4a0SFrançois Tigeot * 1819edbd4a0SFrançois Tigeot * RETURNS: 1829edbd4a0SFrançois Tigeot * true iff the node was previously allocated an offset and added to 1839edbd4a0SFrançois Tigeot * an vma offset manager. 1849edbd4a0SFrançois Tigeot */ 1859edbd4a0SFrançois Tigeot static inline bool drm_vma_node_has_offset(struct drm_vma_offset_node *node) 1869edbd4a0SFrançois Tigeot { 1879edbd4a0SFrançois Tigeot return drm_mm_node_allocated(&node->vm_node); 1889edbd4a0SFrançois Tigeot } 1899edbd4a0SFrançois Tigeot 1909edbd4a0SFrançois Tigeot /** 1919edbd4a0SFrançois Tigeot * drm_vma_node_offset_addr() - Return sanitized offset for user-space mmaps 1929edbd4a0SFrançois Tigeot * @node: Linked offset node 1939edbd4a0SFrançois Tigeot * 1949edbd4a0SFrançois Tigeot * Same as drm_vma_node_start() but returns the address as a valid offset that 1959edbd4a0SFrançois Tigeot * can be used for user-space mappings during mmap(). 1969edbd4a0SFrançois Tigeot * This must not be called on unlinked nodes. 1979edbd4a0SFrançois Tigeot * 1989edbd4a0SFrançois Tigeot * RETURNS: 1999edbd4a0SFrançois Tigeot * Offset of @node for byte-based addressing. 0 if the node does not have an 2009edbd4a0SFrançois Tigeot * object allocated. 2019edbd4a0SFrançois Tigeot */ 2029edbd4a0SFrançois Tigeot static inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node) 2039edbd4a0SFrançois Tigeot { 2049edbd4a0SFrançois Tigeot return ((__u64)node->vm_node.start) << PAGE_SHIFT; 2059edbd4a0SFrançois Tigeot } 2069edbd4a0SFrançois Tigeot 2079edbd4a0SFrançois Tigeot #if 0 2089edbd4a0SFrançois Tigeot /** 2099edbd4a0SFrançois Tigeot * drm_vma_node_unmap() - Unmap offset node 2109edbd4a0SFrançois Tigeot * @node: Offset node 2119edbd4a0SFrançois Tigeot * @file_mapping: Address space to unmap @node from 2129edbd4a0SFrançois Tigeot * 2139edbd4a0SFrançois Tigeot * Unmap all userspace mappings for a given offset node. The mappings must be 214ba55f2f5SFrançois Tigeot * associated with the @file_mapping address-space. If no offset exists 215ba55f2f5SFrançois Tigeot * nothing is done. 2169edbd4a0SFrançois Tigeot * 2179edbd4a0SFrançois Tigeot * This call is unlocked. The caller must guarantee that drm_vma_offset_remove() 2189edbd4a0SFrançois Tigeot * is not called on this node concurrently. 2199edbd4a0SFrançois Tigeot */ 2209edbd4a0SFrançois Tigeot static inline void drm_vma_node_unmap(struct drm_vma_offset_node *node, 2219edbd4a0SFrançois Tigeot struct address_space *file_mapping) 2229edbd4a0SFrançois Tigeot { 223ba55f2f5SFrançois Tigeot if (drm_vma_node_has_offset(node)) 2249edbd4a0SFrançois Tigeot unmap_mapping_range(file_mapping, 2259edbd4a0SFrançois Tigeot drm_vma_node_offset_addr(node), 2269edbd4a0SFrançois Tigeot drm_vma_node_size(node) << PAGE_SHIFT, 1); 2279edbd4a0SFrançois Tigeot } 2289edbd4a0SFrançois Tigeot #endif 2299edbd4a0SFrançois Tigeot 230ba55f2f5SFrançois Tigeot /** 231ba55f2f5SFrançois Tigeot * drm_vma_node_verify_access() - Access verification helper for TTM 232ba55f2f5SFrançois Tigeot * @node: Offset node 233ba55f2f5SFrançois Tigeot * @filp: Open-file 234ba55f2f5SFrançois Tigeot * 235ba55f2f5SFrançois Tigeot * This checks whether @filp is granted access to @node. It is the same as 236ba55f2f5SFrançois Tigeot * drm_vma_node_is_allowed() but suitable as drop-in helper for TTM 237ba55f2f5SFrançois Tigeot * verify_access() callbacks. 238ba55f2f5SFrançois Tigeot * 239ba55f2f5SFrançois Tigeot * RETURNS: 240ba55f2f5SFrançois Tigeot * 0 if access is granted, -EACCES otherwise. 241ba55f2f5SFrançois Tigeot */ 242ba55f2f5SFrançois Tigeot static inline int drm_vma_node_verify_access(struct drm_vma_offset_node *node, 243ba55f2f5SFrançois Tigeot struct file *filp) 244ba55f2f5SFrançois Tigeot { 245ba55f2f5SFrançois Tigeot return drm_vma_node_is_allowed(node, filp) ? 0 : -EACCES; 246ba55f2f5SFrançois Tigeot } 247ba55f2f5SFrançois Tigeot 2489edbd4a0SFrançois Tigeot #endif /* __DRM_VMA_MANAGER_H__ */ 249