1/* 2 * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA 3 * Copyright (c) 2012 David Airlie <airlied@linux.ie> 4 * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25#include <drm/drmP.h> 26#include <drm/drm_mm.h> 27#include <drm/drm_vma_manager.h> 28#include <linux/mm.h> 29#include <linux/module.h> 30#include <linux/rbtree.h> 31#include <linux/slab.h> 32#include <linux/spinlock.h> 33#include <linux/types.h> 34 35/** 36 * DOC: vma offset manager 37 * 38 * The vma-manager is responsible to map arbitrary driver-dependent memory 39 * regions into the linear user address-space. It provides offsets to the 40 * caller which can then be used on the address_space of the drm-device. It 41 * takes care to not overlap regions, size them appropriately and to not 42 * confuse mm-core by inconsistent fake vm_pgoff fields. 43 * Drivers shouldn't use this for object placement in VMEM. This manager should 44 * only be used to manage mappings into linear user-space VMs. 45 * 46 * We use drm_mm as backend to manage object allocations. But it is highly 47 * optimized for alloc/free calls, not lookups. Hence, we use an rb-tree to 48 * speed up offset lookups. 49 * 50 * You must not use multiple offset managers on a single address_space. 51 * Otherwise, mm-core will be unable to tear down memory mappings as the VM will 52 * no longer be linear. 53 * 54 * This offset manager works on page-based addresses. That is, every argument 55 * and return code (with the exception of drm_vma_node_offset_addr()) is given 56 * in number of pages, not number of bytes. That means, object sizes and offsets 57 * must always be page-aligned (as usual). 58 * If you want to get a valid byte-based user-space address for a given offset, 59 * please see drm_vma_node_offset_addr(). 60 * 61 * Additionally to offset management, the vma offset manager also handles access 62 * management. For every open-file context that is allowed to access a given 63 * node, you must call drm_vma_node_allow(). Otherwise, an mmap() call on this 64 * open-file with the offset of the node will fail with -EACCES. To revoke 65 * access again, use drm_vma_node_revoke(). However, the caller is responsible 66 * for destroying already existing mappings, if required. 67 */ 68 69/** 70 * drm_vma_offset_manager_init - Initialize new offset-manager 71 * @mgr: Manager object 72 * @page_offset: Offset of available memory area (page-based) 73 * @size: Size of available address space range (page-based) 74 * 75 * Initialize a new offset-manager. The offset and area size available for the 76 * manager are given as @page_offset and @size. Both are interpreted as 77 * page-numbers, not bytes. 78 * 79 * Adding/removing nodes from the manager is locked internally and protected 80 * against concurrent access. However, node allocation and destruction is left 81 * for the caller. While calling into the vma-manager, a given node must 82 * always be guaranteed to be referenced. 83 */ 84void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr, 85 unsigned long page_offset, unsigned long size) 86{ 87 rwlock_init(&mgr->vm_lock); 88 drm_mm_init(&mgr->vm_addr_space_mm, page_offset, size); 89} 90EXPORT_SYMBOL(drm_vma_offset_manager_init); 91 92/** 93 * drm_vma_offset_manager_destroy() - Destroy offset manager 94 * @mgr: Manager object 95 * 96 * Destroy an object manager which was previously created via 97 * drm_vma_offset_manager_init(). The caller must remove all allocated nodes 98 * before destroying the manager. Otherwise, drm_mm will refuse to free the 99 * requested resources. 100 * 101 * The manager must not be accessed after this function is called. 102 */ 103void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr) 104{ 105 /* take the lock to protect against buggy drivers */ 106 write_lock(&mgr->vm_lock); 107 drm_mm_takedown(&mgr->vm_addr_space_mm); 108 write_unlock(&mgr->vm_lock); 109} 110EXPORT_SYMBOL(drm_vma_offset_manager_destroy); 111 112/** 113 * drm_vma_offset_lookup_locked() - Find node in offset space 114 * @mgr: Manager object 115 * @start: Start address for object (page-based) 116 * @pages: Size of object (page-based) 117 * 118 * Find a node given a start address and object size. This returns the _best_ 119 * match for the given node. That is, @start may point somewhere into a valid 120 * region and the given node will be returned, as long as the node spans the 121 * whole requested area (given the size in number of pages as @pages). 122 * 123 * Note that before lookup the vma offset manager lookup lock must be acquired 124 * with drm_vma_offset_lock_lookup(). See there for an example. This can then be 125 * used to implement weakly referenced lookups using kref_get_unless_zero(). 126 * 127 * Example: 128 * 129 * :: 130 * 131 * drm_vma_offset_lock_lookup(mgr); 132 * node = drm_vma_offset_lookup_locked(mgr); 133 * if (node) 134 * kref_get_unless_zero(container_of(node, sth, entr)); 135 * drm_vma_offset_unlock_lookup(mgr); 136 * 137 * RETURNS: 138 * Returns NULL if no suitable node can be found. Otherwise, the best match 139 * is returned. It's the caller's responsibility to make sure the node doesn't 140 * get destroyed before the caller can access it. 141 */ 142struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr, 143 unsigned long start, 144 unsigned long pages) 145{ 146 struct drm_mm_node *node, *best; 147 struct rb_node *iter; 148 unsigned long offset; 149 150 iter = mgr->vm_addr_space_mm.interval_tree.rb_node; 151 best = NULL; 152 153 while (likely(iter)) { 154 node = rb_entry(iter, struct drm_mm_node, rb); 155 offset = node->start; 156 if (start >= offset) { 157 iter = iter->rb_right; 158 best = node; 159 if (start == offset) 160 break; 161 } else { 162 iter = iter->rb_left; 163 } 164 } 165 166 /* verify that the node spans the requested area */ 167 if (best) { 168 offset = best->start + best->size; 169 if (offset < start + pages) 170 best = NULL; 171 } 172 173 if (!best) 174 return NULL; 175 176 return container_of(best, struct drm_vma_offset_node, vm_node); 177} 178EXPORT_SYMBOL(drm_vma_offset_lookup_locked); 179 180/** 181 * drm_vma_offset_add() - Add offset node to manager 182 * @mgr: Manager object 183 * @node: Node to be added 184 * @pages: Allocation size visible to user-space (in number of pages) 185 * 186 * Add a node to the offset-manager. If the node was already added, this does 187 * nothing and return 0. @pages is the size of the object given in number of 188 * pages. 189 * After this call succeeds, you can access the offset of the node until it 190 * is removed again. 191 * 192 * If this call fails, it is safe to retry the operation or call 193 * drm_vma_offset_remove(), anyway. However, no cleanup is required in that 194 * case. 195 * 196 * @pages is not required to be the same size as the underlying memory object 197 * that you want to map. It only limits the size that user-space can map into 198 * their address space. 199 * 200 * RETURNS: 201 * 0 on success, negative error code on failure. 202 */ 203int drm_vma_offset_add(struct drm_vma_offset_manager *mgr, 204 struct drm_vma_offset_node *node, unsigned long pages) 205{ 206 int ret; 207 208 write_lock(&mgr->vm_lock); 209 210 if (drm_mm_node_allocated(&node->vm_node)) { 211 ret = 0; 212 goto out_unlock; 213 } 214 215 ret = drm_mm_insert_node(&mgr->vm_addr_space_mm, &node->vm_node, pages); 216 if (ret) 217 goto out_unlock; 218 219out_unlock: 220 write_unlock(&mgr->vm_lock); 221 return ret; 222} 223EXPORT_SYMBOL(drm_vma_offset_add); 224 225/** 226 * drm_vma_offset_remove() - Remove offset node from manager 227 * @mgr: Manager object 228 * @node: Node to be removed 229 * 230 * Remove a node from the offset manager. If the node wasn't added before, this 231 * does nothing. After this call returns, the offset and size will be 0 until a 232 * new offset is allocated via drm_vma_offset_add() again. Helper functions like 233 * drm_vma_node_start() and drm_vma_node_offset_addr() will return 0 if no 234 * offset is allocated. 235 */ 236void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr, 237 struct drm_vma_offset_node *node) 238{ 239 write_lock(&mgr->vm_lock); 240 241 if (drm_mm_node_allocated(&node->vm_node)) { 242 drm_mm_remove_node(&node->vm_node); 243 memset(&node->vm_node, 0, sizeof(node->vm_node)); 244 } 245 246 write_unlock(&mgr->vm_lock); 247} 248EXPORT_SYMBOL(drm_vma_offset_remove); 249 250/** 251 * drm_vma_node_allow - Add open-file to list of allowed users 252 * @node: Node to modify 253 * @tag: Tag of file to remove 254 * 255 * Add @tag to the list of allowed open-files for this node. If @tag is 256 * already on this list, the ref-count is incremented. 257 * 258 * The list of allowed-users is preserved across drm_vma_offset_add() and 259 * drm_vma_offset_remove() calls. You may even call it if the node is currently 260 * not added to any offset-manager. 261 * 262 * You must remove all open-files the same number of times as you added them 263 * before destroying the node. Otherwise, you will leak memory. 264 * 265 * This is locked against concurrent access internally. 266 * 267 * RETURNS: 268 * 0 on success, negative error code on internal failure (out-of-mem) 269 */ 270int drm_vma_node_allow(struct drm_vma_offset_node *node, struct drm_file *tag) 271{ 272 struct rb_node **iter; 273 struct rb_node *parent = NULL; 274 struct drm_vma_offset_file *new, *entry; 275 int ret = 0; 276 277 /* Preallocate entry to avoid atomic allocations below. It is quite 278 * unlikely that an open-file is added twice to a single node so we 279 * don't optimize for this case. OOM is checked below only if the entry 280 * is actually used. */ 281 new = kmalloc(sizeof(*entry), GFP_KERNEL); 282 283 write_lock(&node->vm_lock); 284 285 iter = &node->vm_files.rb_node; 286 287 while (likely(*iter)) { 288 parent = *iter; 289 entry = rb_entry(*iter, struct drm_vma_offset_file, vm_rb); 290 291 if (tag == entry->vm_tag) { 292 entry->vm_count++; 293 goto unlock; 294 } else if (tag > entry->vm_tag) { 295 iter = &(*iter)->rb_right; 296 } else { 297 iter = &(*iter)->rb_left; 298 } 299 } 300 301 if (!new) { 302 ret = -ENOMEM; 303 goto unlock; 304 } 305 306 new->vm_tag = tag; 307 new->vm_count = 1; 308 rb_link_node(&new->vm_rb, parent, iter); 309 rb_insert_color(&new->vm_rb, &node->vm_files); 310 new = NULL; 311 312unlock: 313 write_unlock(&node->vm_lock); 314 kfree(new); 315 return ret; 316} 317EXPORT_SYMBOL(drm_vma_node_allow); 318 319/** 320 * drm_vma_node_revoke - Remove open-file from list of allowed users 321 * @node: Node to modify 322 * @tag: Tag of file to remove 323 * 324 * Decrement the ref-count of @tag in the list of allowed open-files on @node. 325 * If the ref-count drops to zero, remove @tag from the list. You must call 326 * this once for every drm_vma_node_allow() on @tag. 327 * 328 * This is locked against concurrent access internally. 329 * 330 * If @tag is not on the list, nothing is done. 331 */ 332void drm_vma_node_revoke(struct drm_vma_offset_node *node, 333 struct drm_file *tag) 334{ 335 struct drm_vma_offset_file *entry; 336 struct rb_node *iter; 337 338 write_lock(&node->vm_lock); 339 340 iter = node->vm_files.rb_node; 341 while (likely(iter)) { 342 entry = rb_entry(iter, struct drm_vma_offset_file, vm_rb); 343 if (tag == entry->vm_tag) { 344 if (!--entry->vm_count) { 345 rb_erase(&entry->vm_rb, &node->vm_files); 346 kfree(entry); 347 } 348 break; 349 } else if (tag > entry->vm_tag) { 350 iter = iter->rb_right; 351 } else { 352 iter = iter->rb_left; 353 } 354 } 355 356 write_unlock(&node->vm_lock); 357} 358EXPORT_SYMBOL(drm_vma_node_revoke); 359 360/** 361 * drm_vma_node_is_allowed - Check whether an open-file is granted access 362 * @node: Node to check 363 * @tag: Tag of file to remove 364 * 365 * Search the list in @node whether @tag is currently on the list of allowed 366 * open-files (see drm_vma_node_allow()). 367 * 368 * This is locked against concurrent access internally. 369 * 370 * RETURNS: 371 * true iff @filp is on the list 372 */ 373bool drm_vma_node_is_allowed(struct drm_vma_offset_node *node, 374 struct drm_file *tag) 375{ 376 struct drm_vma_offset_file *entry; 377 struct rb_node *iter; 378 379 read_lock(&node->vm_lock); 380 381 iter = node->vm_files.rb_node; 382 while (likely(iter)) { 383 entry = rb_entry(iter, struct drm_vma_offset_file, vm_rb); 384 if (tag == entry->vm_tag) 385 break; 386 else if (tag > entry->vm_tag) 387 iter = iter->rb_right; 388 else 389 iter = iter->rb_left; 390 } 391 392 read_unlock(&node->vm_lock); 393 394 return iter; 395} 396EXPORT_SYMBOL(drm_vma_node_is_allowed); 397