Line data Source code
1 : /* $OpenBSD: drm_vma_manager.h,v 1.2 2016/04/05 20:50:44 kettenis Exp $ */
2 : #ifndef __DRM_VMA_MANAGER_H__
3 : #define __DRM_VMA_MANAGER_H__
4 :
5 : /*
6 : * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com>
7 : *
8 : * Permission is hereby granted, free of charge, to any person obtaining a
9 : * copy of this software and associated documentation files (the "Software"),
10 : * to deal in the Software without restriction, including without limitation
11 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 : * and/or sell copies of the Software, and to permit persons to whom the
13 : * Software is furnished to do so, subject to the following conditions:
14 : *
15 : * The above copyright notice and this permission notice shall be included in
16 : * all copies or substantial portions of the Software.
17 : *
18 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 : * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
22 : * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23 : * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 : * OTHER DEALINGS IN THE SOFTWARE.
25 : */
26 :
27 : #include "drm_mm.h"
28 : #include "drm_linux_rbtree.h"
29 :
30 : struct drm_vma_offset_file {
31 : struct rb_node vm_rb;
32 : struct file *vm_filp;
33 : unsigned long vm_count;
34 : };
35 :
36 : struct drm_vma_offset_node {
37 : rwlock_t vm_lock;
38 : struct drm_mm_node vm_node;
39 : struct rb_node vm_rb;
40 : struct rb_root vm_files;
41 : };
42 :
43 : struct drm_vma_offset_manager {
44 : rwlock_t vm_lock;
45 : struct rb_root vm_addr_space_rb;
46 : struct drm_mm vm_addr_space_mm;
47 : };
48 :
49 : void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
50 : unsigned long page_offset, unsigned long size);
51 : void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr);
52 :
53 : struct drm_vma_offset_node *drm_vma_offset_lookup(struct drm_vma_offset_manager *mgr,
54 : unsigned long start,
55 : unsigned long pages);
56 : struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr,
57 : unsigned long start,
58 : unsigned long pages);
59 : int drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
60 : struct drm_vma_offset_node *node, unsigned long pages);
61 : void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
62 : struct drm_vma_offset_node *node);
63 :
64 : int drm_vma_node_allow(struct drm_vma_offset_node *node, struct file *filp);
65 : void drm_vma_node_revoke(struct drm_vma_offset_node *node, struct file *filp);
66 : bool drm_vma_node_is_allowed(struct drm_vma_offset_node *node,
67 : struct file *filp);
68 :
69 : /**
70 : * drm_vma_offset_exact_lookup() - Look up node by exact address
71 : * @mgr: Manager object
72 : * @start: Start address (page-based, not byte-based)
73 : * @pages: Size of object (page-based)
74 : *
75 : * Same as drm_vma_offset_lookup() but does not allow any offset into the node.
76 : * It only returns the exact object with the given start address.
77 : *
78 : * RETURNS:
79 : * Node at exact start address @start.
80 : */
81 : static inline struct drm_vma_offset_node *
82 0 : drm_vma_offset_exact_lookup(struct drm_vma_offset_manager *mgr,
83 : unsigned long start,
84 : unsigned long pages)
85 : {
86 : struct drm_vma_offset_node *node;
87 :
88 0 : node = drm_vma_offset_lookup(mgr, start, pages);
89 0 : return (node && node->vm_node.start == start) ? node : NULL;
90 : }
91 :
92 : /**
93 : * drm_vma_offset_lock_lookup() - Lock lookup for extended private use
94 : * @mgr: Manager object
95 : *
96 : * Lock VMA manager for extended lookups. Only *_locked() VMA function calls
97 : * are allowed while holding this lock. All other contexts are blocked from VMA
98 : * until the lock is released via drm_vma_offset_unlock_lookup().
99 : *
100 : * Use this if you need to take a reference to the objects returned by
101 : * drm_vma_offset_lookup_locked() before releasing this lock again.
102 : *
103 : * This lock must not be used for anything else than extended lookups. You must
104 : * not call any other VMA helpers while holding this lock.
105 : *
106 : * Note: You're in atomic-context while holding this lock!
107 : *
108 : * Example:
109 : * drm_vma_offset_lock_lookup(mgr);
110 : * node = drm_vma_offset_lookup_locked(mgr);
111 : * if (node)
112 : * kref_get_unless_zero(container_of(node, sth, entr));
113 : * drm_vma_offset_unlock_lookup(mgr);
114 : */
115 0 : static inline void drm_vma_offset_lock_lookup(struct drm_vma_offset_manager *mgr)
116 : {
117 0 : read_lock(&mgr->vm_lock);
118 0 : }
119 :
120 : /**
121 : * drm_vma_offset_unlock_lookup() - Unlock lookup for extended private use
122 : * @mgr: Manager object
123 : *
124 : * Release lookup-lock. See drm_vma_offset_lock_lookup() for more information.
125 : */
126 0 : static inline void drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager *mgr)
127 : {
128 0 : read_unlock(&mgr->vm_lock);
129 0 : }
130 :
131 : /**
132 : * drm_vma_node_reset() - Initialize or reset node object
133 : * @node: Node to initialize or reset
134 : *
135 : * Reset a node to its initial state. This must be called before using it with
136 : * any VMA offset manager.
137 : *
138 : * This must not be called on an already allocated node, or you will leak
139 : * memory.
140 : */
141 0 : static inline void drm_vma_node_reset(struct drm_vma_offset_node *node)
142 : {
143 0 : memset(node, 0, sizeof(*node));
144 0 : node->vm_files = RB_ROOT;
145 0 : rw_init(&node->vm_lock, "drmvma");
146 0 : }
147 :
148 : /**
149 : * drm_vma_node_start() - Return start address for page-based addressing
150 : * @node: Node to inspect
151 : *
152 : * Return the start address of the given node. This can be used as offset into
153 : * the linear VM space that is provided by the VMA offset manager. Note that
154 : * this can only be used for page-based addressing. If you need a proper offset
155 : * for user-space mappings, you must apply "<< PAGE_SHIFT" or use the
156 : * drm_vma_node_offset_addr() helper instead.
157 : *
158 : * RETURNS:
159 : * Start address of @node for page-based addressing. 0 if the node does not
160 : * have an offset allocated.
161 : */
162 0 : static inline unsigned long drm_vma_node_start(struct drm_vma_offset_node *node)
163 : {
164 0 : return node->vm_node.start;
165 : }
166 :
167 : /**
168 : * drm_vma_node_size() - Return size (page-based)
169 : * @node: Node to inspect
170 : *
171 : * Return the size as number of pages for the given node. This is the same size
172 : * that was passed to drm_vma_offset_add(). If no offset is allocated for the
173 : * node, this is 0.
174 : *
175 : * RETURNS:
176 : * Size of @node as number of pages. 0 if the node does not have an offset
177 : * allocated.
178 : */
179 : static inline unsigned long drm_vma_node_size(struct drm_vma_offset_node *node)
180 : {
181 : return node->vm_node.size;
182 : }
183 :
184 : /**
185 : * drm_vma_node_has_offset() - Check whether node is added to offset manager
186 : * @node: Node to be checked
187 : *
188 : * RETURNS:
189 : * true iff the node was previously allocated an offset and added to
190 : * an vma offset manager.
191 : */
192 0 : static inline bool drm_vma_node_has_offset(struct drm_vma_offset_node *node)
193 : {
194 0 : return drm_mm_node_allocated(&node->vm_node);
195 : }
196 :
197 : /**
198 : * drm_vma_node_offset_addr() - Return sanitized offset for user-space mmaps
199 : * @node: Linked offset node
200 : *
201 : * Same as drm_vma_node_start() but returns the address as a valid offset that
202 : * can be used for user-space mappings during mmap().
203 : * This must not be called on unlinked nodes.
204 : *
205 : * RETURNS:
206 : * Offset of @node for byte-based addressing. 0 if the node does not have an
207 : * object allocated.
208 : */
209 0 : static inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node)
210 : {
211 0 : return ((__u64)node->vm_node.start) << PAGE_SHIFT;
212 : }
213 :
214 : /**
215 : * drm_vma_node_unmap() - Unmap offset node
216 : * @node: Offset node
217 : * @file_mapping: Address space to unmap @node from
218 : *
219 : * Unmap all userspace mappings for a given offset node. The mappings must be
220 : * associated with the @file_mapping address-space. If no offset exists or
221 : * the address-space is invalid, nothing is done.
222 : *
223 : * This call is unlocked. The caller must guarantee that drm_vma_offset_remove()
224 : * is not called on this node concurrently.
225 : */
226 : static inline void drm_vma_node_unmap(struct drm_vma_offset_node *node,
227 : struct address_space *file_mapping)
228 : {
229 : if (file_mapping && drm_vma_node_has_offset(node))
230 : unmap_mapping_range(file_mapping,
231 : drm_vma_node_offset_addr(node),
232 : drm_vma_node_size(node) << PAGE_SHIFT, 1);
233 : }
234 :
235 : /**
236 : * drm_vma_node_verify_access() - Access verification helper for TTM
237 : * @node: Offset node
238 : * @filp: Open-file
239 : *
240 : * This checks whether @filp is granted access to @node. It is the same as
241 : * drm_vma_node_is_allowed() but suitable as drop-in helper for TTM
242 : * verify_access() callbacks.
243 : *
244 : * RETURNS:
245 : * 0 if access is granted, -EACCES otherwise.
246 : */
247 0 : static inline int drm_vma_node_verify_access(struct drm_vma_offset_node *node,
248 : struct file *filp)
249 : {
250 0 : return drm_vma_node_is_allowed(node, filp) ? 0 : -EACCES;
251 : }
252 :
253 : #endif /* __DRM_VMA_MANAGER_H__ */
|