162306a36Sopenharmony_ci#ifndef __DRM_VMA_MANAGER_H__ 262306a36Sopenharmony_ci#define __DRM_VMA_MANAGER_H__ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci/* 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 <drm/drm_mm.h> 2762306a36Sopenharmony_ci#include <linux/mm.h> 2862306a36Sopenharmony_ci#include <linux/rbtree.h> 2962306a36Sopenharmony_ci#include <linux/spinlock.h> 3062306a36Sopenharmony_ci#include <linux/types.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* We make up offsets for buffer objects so we can recognize them at 3362306a36Sopenharmony_ci * mmap time. pgoff in mmap is an unsigned long, so we need to make sure 3462306a36Sopenharmony_ci * that the faked up offset will fit 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci#if BITS_PER_LONG == 64 3762306a36Sopenharmony_ci#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1) 3862306a36Sopenharmony_ci#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 256) 3962306a36Sopenharmony_ci#else 4062306a36Sopenharmony_ci#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFUL >> PAGE_SHIFT) + 1) 4162306a36Sopenharmony_ci#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFUL >> PAGE_SHIFT) * 16) 4262306a36Sopenharmony_ci#endif 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct drm_file; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistruct drm_vma_offset_file { 4762306a36Sopenharmony_ci struct rb_node vm_rb; 4862306a36Sopenharmony_ci struct drm_file *vm_tag; 4962306a36Sopenharmony_ci unsigned long vm_count; 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct drm_vma_offset_node { 5362306a36Sopenharmony_ci rwlock_t vm_lock; 5462306a36Sopenharmony_ci struct drm_mm_node vm_node; 5562306a36Sopenharmony_ci struct rb_root vm_files; 5662306a36Sopenharmony_ci void *driver_private; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct drm_vma_offset_manager { 6062306a36Sopenharmony_ci rwlock_t vm_lock; 6162306a36Sopenharmony_ci struct drm_mm vm_addr_space_mm; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_civoid drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr, 6562306a36Sopenharmony_ci unsigned long page_offset, unsigned long size); 6662306a36Sopenharmony_civoid drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr, 6962306a36Sopenharmony_ci unsigned long start, 7062306a36Sopenharmony_ci unsigned long pages); 7162306a36Sopenharmony_ciint drm_vma_offset_add(struct drm_vma_offset_manager *mgr, 7262306a36Sopenharmony_ci struct drm_vma_offset_node *node, unsigned long pages); 7362306a36Sopenharmony_civoid drm_vma_offset_remove(struct drm_vma_offset_manager *mgr, 7462306a36Sopenharmony_ci struct drm_vma_offset_node *node); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ciint drm_vma_node_allow(struct drm_vma_offset_node *node, struct drm_file *tag); 7762306a36Sopenharmony_ciint drm_vma_node_allow_once(struct drm_vma_offset_node *node, struct drm_file *tag); 7862306a36Sopenharmony_civoid drm_vma_node_revoke(struct drm_vma_offset_node *node, 7962306a36Sopenharmony_ci struct drm_file *tag); 8062306a36Sopenharmony_cibool drm_vma_node_is_allowed(struct drm_vma_offset_node *node, 8162306a36Sopenharmony_ci struct drm_file *tag); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/** 8462306a36Sopenharmony_ci * drm_vma_offset_exact_lookup_locked() - Look up node by exact address 8562306a36Sopenharmony_ci * @mgr: Manager object 8662306a36Sopenharmony_ci * @start: Start address (page-based, not byte-based) 8762306a36Sopenharmony_ci * @pages: Size of object (page-based) 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * Same as drm_vma_offset_lookup_locked() but does not allow any offset into the node. 9062306a36Sopenharmony_ci * It only returns the exact object with the given start address. 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * RETURNS: 9362306a36Sopenharmony_ci * Node at exact start address @start. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_cistatic inline struct drm_vma_offset_node * 9662306a36Sopenharmony_cidrm_vma_offset_exact_lookup_locked(struct drm_vma_offset_manager *mgr, 9762306a36Sopenharmony_ci unsigned long start, 9862306a36Sopenharmony_ci unsigned long pages) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct drm_vma_offset_node *node; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci node = drm_vma_offset_lookup_locked(mgr, start, pages); 10362306a36Sopenharmony_ci return (node && node->vm_node.start == start) ? node : NULL; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/** 10762306a36Sopenharmony_ci * drm_vma_offset_lock_lookup() - Lock lookup for extended private use 10862306a36Sopenharmony_ci * @mgr: Manager object 10962306a36Sopenharmony_ci * 11062306a36Sopenharmony_ci * Lock VMA manager for extended lookups. Only locked VMA function calls 11162306a36Sopenharmony_ci * are allowed while holding this lock. All other contexts are blocked from VMA 11262306a36Sopenharmony_ci * until the lock is released via drm_vma_offset_unlock_lookup(). 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * Use this if you need to take a reference to the objects returned by 11562306a36Sopenharmony_ci * drm_vma_offset_lookup_locked() before releasing this lock again. 11662306a36Sopenharmony_ci * 11762306a36Sopenharmony_ci * This lock must not be used for anything else than extended lookups. You must 11862306a36Sopenharmony_ci * not call any other VMA helpers while holding this lock. 11962306a36Sopenharmony_ci * 12062306a36Sopenharmony_ci * Note: You're in atomic-context while holding this lock! 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_cistatic inline void drm_vma_offset_lock_lookup(struct drm_vma_offset_manager *mgr) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci read_lock(&mgr->vm_lock); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/** 12862306a36Sopenharmony_ci * drm_vma_offset_unlock_lookup() - Unlock lookup for extended private use 12962306a36Sopenharmony_ci * @mgr: Manager object 13062306a36Sopenharmony_ci * 13162306a36Sopenharmony_ci * Release lookup-lock. See drm_vma_offset_lock_lookup() for more information. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_cistatic inline void drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager *mgr) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci read_unlock(&mgr->vm_lock); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/** 13962306a36Sopenharmony_ci * drm_vma_node_reset() - Initialize or reset node object 14062306a36Sopenharmony_ci * @node: Node to initialize or reset 14162306a36Sopenharmony_ci * 14262306a36Sopenharmony_ci * Reset a node to its initial state. This must be called before using it with 14362306a36Sopenharmony_ci * any VMA offset manager. 14462306a36Sopenharmony_ci * 14562306a36Sopenharmony_ci * This must not be called on an already allocated node, or you will leak 14662306a36Sopenharmony_ci * memory. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_cistatic inline void drm_vma_node_reset(struct drm_vma_offset_node *node) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci memset(node, 0, sizeof(*node)); 15162306a36Sopenharmony_ci node->vm_files = RB_ROOT; 15262306a36Sopenharmony_ci rwlock_init(&node->vm_lock); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/** 15662306a36Sopenharmony_ci * drm_vma_node_start() - Return start address for page-based addressing 15762306a36Sopenharmony_ci * @node: Node to inspect 15862306a36Sopenharmony_ci * 15962306a36Sopenharmony_ci * Return the start address of the given node. This can be used as offset into 16062306a36Sopenharmony_ci * the linear VM space that is provided by the VMA offset manager. Note that 16162306a36Sopenharmony_ci * this can only be used for page-based addressing. If you need a proper offset 16262306a36Sopenharmony_ci * for user-space mappings, you must apply "<< PAGE_SHIFT" or use the 16362306a36Sopenharmony_ci * drm_vma_node_offset_addr() helper instead. 16462306a36Sopenharmony_ci * 16562306a36Sopenharmony_ci * RETURNS: 16662306a36Sopenharmony_ci * Start address of @node for page-based addressing. 0 if the node does not 16762306a36Sopenharmony_ci * have an offset allocated. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_cistatic inline unsigned long drm_vma_node_start(const struct drm_vma_offset_node *node) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci return node->vm_node.start; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/** 17562306a36Sopenharmony_ci * drm_vma_node_size() - Return size (page-based) 17662306a36Sopenharmony_ci * @node: Node to inspect 17762306a36Sopenharmony_ci * 17862306a36Sopenharmony_ci * Return the size as number of pages for the given node. This is the same size 17962306a36Sopenharmony_ci * that was passed to drm_vma_offset_add(). If no offset is allocated for the 18062306a36Sopenharmony_ci * node, this is 0. 18162306a36Sopenharmony_ci * 18262306a36Sopenharmony_ci * RETURNS: 18362306a36Sopenharmony_ci * Size of @node as number of pages. 0 if the node does not have an offset 18462306a36Sopenharmony_ci * allocated. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_cistatic inline unsigned long drm_vma_node_size(struct drm_vma_offset_node *node) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci return node->vm_node.size; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/** 19262306a36Sopenharmony_ci * drm_vma_node_offset_addr() - Return sanitized offset for user-space mmaps 19362306a36Sopenharmony_ci * @node: Linked offset node 19462306a36Sopenharmony_ci * 19562306a36Sopenharmony_ci * Same as drm_vma_node_start() but returns the address as a valid offset that 19662306a36Sopenharmony_ci * can be used for user-space mappings during mmap(). 19762306a36Sopenharmony_ci * This must not be called on unlinked nodes. 19862306a36Sopenharmony_ci * 19962306a36Sopenharmony_ci * RETURNS: 20062306a36Sopenharmony_ci * Offset of @node for byte-based addressing. 0 if the node does not have an 20162306a36Sopenharmony_ci * object allocated. 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_cistatic inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci return ((__u64)node->vm_node.start) << PAGE_SHIFT; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/** 20962306a36Sopenharmony_ci * drm_vma_node_unmap() - Unmap offset node 21062306a36Sopenharmony_ci * @node: Offset node 21162306a36Sopenharmony_ci * @file_mapping: Address space to unmap @node from 21262306a36Sopenharmony_ci * 21362306a36Sopenharmony_ci * Unmap all userspace mappings for a given offset node. The mappings must be 21462306a36Sopenharmony_ci * associated with the @file_mapping address-space. If no offset exists 21562306a36Sopenharmony_ci * nothing is done. 21662306a36Sopenharmony_ci * 21762306a36Sopenharmony_ci * This call is unlocked. The caller must guarantee that drm_vma_offset_remove() 21862306a36Sopenharmony_ci * is not called on this node concurrently. 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_cistatic inline void drm_vma_node_unmap(struct drm_vma_offset_node *node, 22162306a36Sopenharmony_ci struct address_space *file_mapping) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci if (drm_mm_node_allocated(&node->vm_node)) 22462306a36Sopenharmony_ci unmap_mapping_range(file_mapping, 22562306a36Sopenharmony_ci drm_vma_node_offset_addr(node), 22662306a36Sopenharmony_ci drm_vma_node_size(node) << PAGE_SHIFT, 1); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/** 23062306a36Sopenharmony_ci * drm_vma_node_verify_access() - Access verification helper for TTM 23162306a36Sopenharmony_ci * @node: Offset node 23262306a36Sopenharmony_ci * @tag: Tag of file to check 23362306a36Sopenharmony_ci * 23462306a36Sopenharmony_ci * This checks whether @tag is granted access to @node. It is the same as 23562306a36Sopenharmony_ci * drm_vma_node_is_allowed() but suitable as drop-in helper for TTM 23662306a36Sopenharmony_ci * verify_access() callbacks. 23762306a36Sopenharmony_ci * 23862306a36Sopenharmony_ci * RETURNS: 23962306a36Sopenharmony_ci * 0 if access is granted, -EACCES otherwise. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_cistatic inline int drm_vma_node_verify_access(struct drm_vma_offset_node *node, 24262306a36Sopenharmony_ci struct drm_file *tag) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci return drm_vma_node_is_allowed(node, tag) ? 0 : -EACCES; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci#endif /* __DRM_VMA_MANAGER_H__ */ 248