2753 lines · c
1// SPDX-License-Identifier: GPL-2.0-only OR MIT2/*3 * Copyright (c) 2022 Red Hat.4 *5 * Permission is hereby granted, free of charge, to any person obtaining a6 * copy of this software and associated documentation files (the "Software"),7 * to deal in the Software without restriction, including without limitation8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,9 * and/or sell copies of the Software, and to permit persons to whom the10 * Software is furnished to do so, subject to the following conditions:11 *12 * The above copyright notice and this permission notice shall be included in13 * all copies or substantial portions of the Software.14 *15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL18 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR21 * OTHER DEALINGS IN THE SOFTWARE.22 *23 * Authors:24 * Danilo Krummrich <dakr@redhat.com>25 *26 */27 28#include <drm/drm_gpuvm.h>29 30#include <linux/interval_tree_generic.h>31#include <linux/mm.h>32 33/**34 * DOC: Overview35 *36 * The DRM GPU VA Manager, represented by struct drm_gpuvm keeps track of a37 * GPU's virtual address (VA) space and manages the corresponding virtual38 * mappings represented by &drm_gpuva objects. It also keeps track of the39 * mapping's backing &drm_gem_object buffers.40 *41 * &drm_gem_object buffers maintain a list of &drm_gpuva objects representing42 * all existent GPU VA mappings using this &drm_gem_object as backing buffer.43 *44 * GPU VAs can be flagged as sparse, such that drivers may use GPU VAs to also45 * keep track of sparse PTEs in order to support Vulkan 'Sparse Resources'.46 *47 * The GPU VA manager internally uses a rb-tree to manage the48 * &drm_gpuva mappings within a GPU's virtual address space.49 *50 * The &drm_gpuvm structure contains a special &drm_gpuva representing the51 * portion of VA space reserved by the kernel. This node is initialized together52 * with the GPU VA manager instance and removed when the GPU VA manager is53 * destroyed.54 *55 * In a typical application drivers would embed struct drm_gpuvm and56 * struct drm_gpuva within their own driver specific structures, there won't be57 * any memory allocations of its own nor memory allocations of &drm_gpuva58 * entries.59 *60 * The data structures needed to store &drm_gpuvas within the &drm_gpuvm are61 * contained within struct drm_gpuva already. Hence, for inserting &drm_gpuva62 * entries from within dma-fence signalling critical sections it is enough to63 * pre-allocate the &drm_gpuva structures.64 *65 * &drm_gem_objects which are private to a single VM can share a common66 * &dma_resv in order to improve locking efficiency (e.g. with &drm_exec).67 * For this purpose drivers must pass a &drm_gem_object to drm_gpuvm_init(), in68 * the following called 'resv object', which serves as the container of the69 * GPUVM's shared &dma_resv. This resv object can be a driver specific70 * &drm_gem_object, such as the &drm_gem_object containing the root page table,71 * but it can also be a 'dummy' object, which can be allocated with72 * drm_gpuvm_resv_object_alloc().73 *74 * In order to connect a struct drm_gpuva its backing &drm_gem_object each75 * &drm_gem_object maintains a list of &drm_gpuvm_bo structures, and each76 * &drm_gpuvm_bo contains a list of &drm_gpuva structures.77 *78 * A &drm_gpuvm_bo is an abstraction that represents a combination of a79 * &drm_gpuvm and a &drm_gem_object. Every such combination should be unique.80 * This is ensured by the API through drm_gpuvm_bo_obtain() and81 * drm_gpuvm_bo_obtain_prealloc() which first look into the corresponding82 * &drm_gem_object list of &drm_gpuvm_bos for an existing instance of this83 * particular combination. If not existent a new instance is created and linked84 * to the &drm_gem_object.85 *86 * &drm_gpuvm_bo structures, since unique for a given &drm_gpuvm, are also used87 * as entry for the &drm_gpuvm's lists of external and evicted objects. Those88 * lists are maintained in order to accelerate locking of dma-resv locks and89 * validation of evicted objects bound in a &drm_gpuvm. For instance, all90 * &drm_gem_object's &dma_resv of a given &drm_gpuvm can be locked by calling91 * drm_gpuvm_exec_lock(). Once locked drivers can call drm_gpuvm_validate() in92 * order to validate all evicted &drm_gem_objects. It is also possible to lock93 * additional &drm_gem_objects by providing the corresponding parameters to94 * drm_gpuvm_exec_lock() as well as open code the &drm_exec loop while making95 * use of helper functions such as drm_gpuvm_prepare_range() or96 * drm_gpuvm_prepare_objects().97 *98 * Every bound &drm_gem_object is treated as external object when its &dma_resv99 * structure is different than the &drm_gpuvm's common &dma_resv structure.100 */101 102/**103 * DOC: Split and Merge104 *105 * Besides its capability to manage and represent a GPU VA space, the106 * GPU VA manager also provides functions to let the &drm_gpuvm calculate a107 * sequence of operations to satisfy a given map or unmap request.108 *109 * Therefore the DRM GPU VA manager provides an algorithm implementing splitting110 * and merging of existent GPU VA mappings with the ones that are requested to111 * be mapped or unmapped. This feature is required by the Vulkan API to112 * implement Vulkan 'Sparse Memory Bindings' - drivers UAPIs often refer to this113 * as VM BIND.114 *115 * Drivers can call drm_gpuvm_sm_map() to receive a sequence of callbacks116 * containing map, unmap and remap operations for a given newly requested117 * mapping. The sequence of callbacks represents the set of operations to118 * execute in order to integrate the new mapping cleanly into the current state119 * of the GPU VA space.120 *121 * Depending on how the new GPU VA mapping intersects with the existent mappings122 * of the GPU VA space the &drm_gpuvm_ops callbacks contain an arbitrary amount123 * of unmap operations, a maximum of two remap operations and a single map124 * operation. The caller might receive no callback at all if no operation is125 * required, e.g. if the requested mapping already exists in the exact same way.126 *127 * The single map operation represents the original map operation requested by128 * the caller.129 *130 * &drm_gpuva_op_unmap contains a 'keep' field, which indicates whether the131 * &drm_gpuva to unmap is physically contiguous with the original mapping132 * request. Optionally, if 'keep' is set, drivers may keep the actual page table133 * entries for this &drm_gpuva, adding the missing page table entries only and134 * update the &drm_gpuvm's view of things accordingly.135 *136 * Drivers may do the same optimization, namely delta page table updates, also137 * for remap operations. This is possible since &drm_gpuva_op_remap consists of138 * one unmap operation and one or two map operations, such that drivers can139 * derive the page table update delta accordingly.140 *141 * Note that there can't be more than two existent mappings to split up, one at142 * the beginning and one at the end of the new mapping, hence there is a143 * maximum of two remap operations.144 *145 * Analogous to drm_gpuvm_sm_map() drm_gpuvm_sm_unmap() uses &drm_gpuvm_ops to146 * call back into the driver in order to unmap a range of GPU VA space. The147 * logic behind this function is way simpler though: For all existent mappings148 * enclosed by the given range unmap operations are created. For mappings which149 * are only partically located within the given range, remap operations are150 * created such that those mappings are split up and re-mapped partically.151 *152 * As an alternative to drm_gpuvm_sm_map() and drm_gpuvm_sm_unmap(),153 * drm_gpuvm_sm_map_ops_create() and drm_gpuvm_sm_unmap_ops_create() can be used154 * to directly obtain an instance of struct drm_gpuva_ops containing a list of155 * &drm_gpuva_op, which can be iterated with drm_gpuva_for_each_op(). This list156 * contains the &drm_gpuva_ops analogous to the callbacks one would receive when157 * calling drm_gpuvm_sm_map() or drm_gpuvm_sm_unmap(). While this way requires158 * more memory (to allocate the &drm_gpuva_ops), it provides drivers a way to159 * iterate the &drm_gpuva_op multiple times, e.g. once in a context where memory160 * allocations are possible (e.g. to allocate GPU page tables) and once in the161 * dma-fence signalling critical path.162 *163 * To update the &drm_gpuvm's view of the GPU VA space drm_gpuva_insert() and164 * drm_gpuva_remove() may be used. These functions can safely be used from165 * &drm_gpuvm_ops callbacks originating from drm_gpuvm_sm_map() or166 * drm_gpuvm_sm_unmap(). However, it might be more convenient to use the167 * provided helper functions drm_gpuva_map(), drm_gpuva_remap() and168 * drm_gpuva_unmap() instead.169 *170 * The following diagram depicts the basic relationships of existent GPU VA171 * mappings, a newly requested mapping and the resulting mappings as implemented172 * by drm_gpuvm_sm_map() - it doesn't cover any arbitrary combinations of these.173 *174 * 1) Requested mapping is identical. Replace it, but indicate the backing PTEs175 * could be kept.176 *177 * ::178 *179 * 0 a 1180 * old: |-----------| (bo_offset=n)181 *182 * 0 a 1183 * req: |-----------| (bo_offset=n)184 *185 * 0 a 1186 * new: |-----------| (bo_offset=n)187 *188 *189 * 2) Requested mapping is identical, except for the BO offset, hence replace190 * the mapping.191 *192 * ::193 *194 * 0 a 1195 * old: |-----------| (bo_offset=n)196 *197 * 0 a 1198 * req: |-----------| (bo_offset=m)199 *200 * 0 a 1201 * new: |-----------| (bo_offset=m)202 *203 *204 * 3) Requested mapping is identical, except for the backing BO, hence replace205 * the mapping.206 *207 * ::208 *209 * 0 a 1210 * old: |-----------| (bo_offset=n)211 *212 * 0 b 1213 * req: |-----------| (bo_offset=n)214 *215 * 0 b 1216 * new: |-----------| (bo_offset=n)217 *218 *219 * 4) Existent mapping is a left aligned subset of the requested one, hence220 * replace the existent one.221 *222 * ::223 *224 * 0 a 1225 * old: |-----| (bo_offset=n)226 *227 * 0 a 2228 * req: |-----------| (bo_offset=n)229 *230 * 0 a 2231 * new: |-----------| (bo_offset=n)232 *233 * .. note::234 * We expect to see the same result for a request with a different BO235 * and/or non-contiguous BO offset.236 *237 *238 * 5) Requested mapping's range is a left aligned subset of the existent one,239 * but backed by a different BO. Hence, map the requested mapping and split240 * the existent one adjusting its BO offset.241 *242 * ::243 *244 * 0 a 2245 * old: |-----------| (bo_offset=n)246 *247 * 0 b 1248 * req: |-----| (bo_offset=n)249 *250 * 0 b 1 a' 2251 * new: |-----|-----| (b.bo_offset=n, a.bo_offset=n+1)252 *253 * .. note::254 * We expect to see the same result for a request with a different BO255 * and/or non-contiguous BO offset.256 *257 *258 * 6) Existent mapping is a superset of the requested mapping. Split it up, but259 * indicate that the backing PTEs could be kept.260 *261 * ::262 *263 * 0 a 2264 * old: |-----------| (bo_offset=n)265 *266 * 0 a 1267 * req: |-----| (bo_offset=n)268 *269 * 0 a 1 a' 2270 * new: |-----|-----| (a.bo_offset=n, a'.bo_offset=n+1)271 *272 *273 * 7) Requested mapping's range is a right aligned subset of the existent one,274 * but backed by a different BO. Hence, map the requested mapping and split275 * the existent one, without adjusting the BO offset.276 *277 * ::278 *279 * 0 a 2280 * old: |-----------| (bo_offset=n)281 *282 * 1 b 2283 * req: |-----| (bo_offset=m)284 *285 * 0 a 1 b 2286 * new: |-----|-----| (a.bo_offset=n,b.bo_offset=m)287 *288 *289 * 8) Existent mapping is a superset of the requested mapping. Split it up, but290 * indicate that the backing PTEs could be kept.291 *292 * ::293 *294 * 0 a 2295 * old: |-----------| (bo_offset=n)296 *297 * 1 a 2298 * req: |-----| (bo_offset=n+1)299 *300 * 0 a' 1 a 2301 * new: |-----|-----| (a'.bo_offset=n, a.bo_offset=n+1)302 *303 *304 * 9) Existent mapping is overlapped at the end by the requested mapping backed305 * by a different BO. Hence, map the requested mapping and split up the306 * existent one, without adjusting the BO offset.307 *308 * ::309 *310 * 0 a 2311 * old: |-----------| (bo_offset=n)312 *313 * 1 b 3314 * req: |-----------| (bo_offset=m)315 *316 * 0 a 1 b 3317 * new: |-----|-----------| (a.bo_offset=n,b.bo_offset=m)318 *319 *320 * 10) Existent mapping is overlapped by the requested mapping, both having the321 * same backing BO with a contiguous offset. Indicate the backing PTEs of322 * the old mapping could be kept.323 *324 * ::325 *326 * 0 a 2327 * old: |-----------| (bo_offset=n)328 *329 * 1 a 3330 * req: |-----------| (bo_offset=n+1)331 *332 * 0 a' 1 a 3333 * new: |-----|-----------| (a'.bo_offset=n, a.bo_offset=n+1)334 *335 *336 * 11) Requested mapping's range is a centered subset of the existent one337 * having a different backing BO. Hence, map the requested mapping and split338 * up the existent one in two mappings, adjusting the BO offset of the right339 * one accordingly.340 *341 * ::342 *343 * 0 a 3344 * old: |-----------------| (bo_offset=n)345 *346 * 1 b 2347 * req: |-----| (bo_offset=m)348 *349 * 0 a 1 b 2 a' 3350 * new: |-----|-----|-----| (a.bo_offset=n,b.bo_offset=m,a'.bo_offset=n+2)351 *352 *353 * 12) Requested mapping is a contiguous subset of the existent one. Split it354 * up, but indicate that the backing PTEs could be kept.355 *356 * ::357 *358 * 0 a 3359 * old: |-----------------| (bo_offset=n)360 *361 * 1 a 2362 * req: |-----| (bo_offset=n+1)363 *364 * 0 a' 1 a 2 a'' 3365 * old: |-----|-----|-----| (a'.bo_offset=n, a.bo_offset=n+1, a''.bo_offset=n+2)366 *367 *368 * 13) Existent mapping is a right aligned subset of the requested one, hence369 * replace the existent one.370 *371 * ::372 *373 * 1 a 2374 * old: |-----| (bo_offset=n+1)375 *376 * 0 a 2377 * req: |-----------| (bo_offset=n)378 *379 * 0 a 2380 * new: |-----------| (bo_offset=n)381 *382 * .. note::383 * We expect to see the same result for a request with a different bo384 * and/or non-contiguous bo_offset.385 *386 *387 * 14) Existent mapping is a centered subset of the requested one, hence388 * replace the existent one.389 *390 * ::391 *392 * 1 a 2393 * old: |-----| (bo_offset=n+1)394 *395 * 0 a 3396 * req: |----------------| (bo_offset=n)397 *398 * 0 a 3399 * new: |----------------| (bo_offset=n)400 *401 * .. note::402 * We expect to see the same result for a request with a different bo403 * and/or non-contiguous bo_offset.404 *405 *406 * 15) Existent mappings is overlapped at the beginning by the requested mapping407 * backed by a different BO. Hence, map the requested mapping and split up408 * the existent one, adjusting its BO offset accordingly.409 *410 * ::411 *412 * 1 a 3413 * old: |-----------| (bo_offset=n)414 *415 * 0 b 2416 * req: |-----------| (bo_offset=m)417 *418 * 0 b 2 a' 3419 * new: |-----------|-----| (b.bo_offset=m,a.bo_offset=n+2)420 */421 422/**423 * DOC: Locking424 *425 * In terms of managing &drm_gpuva entries DRM GPUVM does not take care of426 * locking itself, it is the drivers responsibility to take care about locking.427 * Drivers might want to protect the following operations: inserting, removing428 * and iterating &drm_gpuva objects as well as generating all kinds of429 * operations, such as split / merge or prefetch.430 *431 * DRM GPUVM also does not take care of the locking of the backing432 * &drm_gem_object buffers GPU VA lists and &drm_gpuvm_bo abstractions by433 * itself; drivers are responsible to enforce mutual exclusion using either the434 * GEMs dma_resv lock or alternatively a driver specific external lock. For the435 * latter see also drm_gem_gpuva_set_lock().436 *437 * However, DRM GPUVM contains lockdep checks to ensure callers of its API hold438 * the corresponding lock whenever the &drm_gem_objects GPU VA list is accessed439 * by functions such as drm_gpuva_link() or drm_gpuva_unlink(), but also440 * drm_gpuvm_bo_obtain() and drm_gpuvm_bo_put().441 *442 * The latter is required since on creation and destruction of a &drm_gpuvm_bo443 * the &drm_gpuvm_bo is attached / removed from the &drm_gem_objects gpuva list.444 * Subsequent calls to drm_gpuvm_bo_obtain() for the same &drm_gpuvm and445 * &drm_gem_object must be able to observe previous creations and destructions446 * of &drm_gpuvm_bos in order to keep instances unique.447 *448 * The &drm_gpuvm's lists for keeping track of external and evicted objects are449 * protected against concurrent insertion / removal and iteration internally.450 *451 * However, drivers still need ensure to protect concurrent calls to functions452 * iterating those lists, namely drm_gpuvm_prepare_objects() and453 * drm_gpuvm_validate().454 *455 * Alternatively, drivers can set the &DRM_GPUVM_RESV_PROTECTED flag to indicate456 * that the corresponding &dma_resv locks are held in order to protect the457 * lists. If &DRM_GPUVM_RESV_PROTECTED is set, internal locking is disabled and458 * the corresponding lockdep checks are enabled. This is an optimization for459 * drivers which are capable of taking the corresponding &dma_resv locks and460 * hence do not require internal locking.461 */462 463/**464 * DOC: Examples465 *466 * This section gives two examples on how to let the DRM GPUVA Manager generate467 * &drm_gpuva_op in order to satisfy a given map or unmap request and how to468 * make use of them.469 *470 * The below code is strictly limited to illustrate the generic usage pattern.471 * To maintain simplicitly, it doesn't make use of any abstractions for common472 * code, different (asyncronous) stages with fence signalling critical paths,473 * any other helpers or error handling in terms of freeing memory and dropping474 * previously taken locks.475 *476 * 1) Obtain a list of &drm_gpuva_op to create a new mapping::477 *478 * // Allocates a new &drm_gpuva.479 * struct drm_gpuva * driver_gpuva_alloc(void);480 *481 * // Typically drivers would embedd the &drm_gpuvm and &drm_gpuva482 * // structure in individual driver structures and lock the dma-resv with483 * // drm_exec or similar helpers.484 * int driver_mapping_create(struct drm_gpuvm *gpuvm,485 * u64 addr, u64 range,486 * struct drm_gem_object *obj, u64 offset)487 * {488 * struct drm_gpuva_ops *ops;489 * struct drm_gpuva_op *op490 * struct drm_gpuvm_bo *vm_bo;491 *492 * driver_lock_va_space();493 * ops = drm_gpuvm_sm_map_ops_create(gpuvm, addr, range,494 * obj, offset);495 * if (IS_ERR(ops))496 * return PTR_ERR(ops);497 *498 * vm_bo = drm_gpuvm_bo_obtain(gpuvm, obj);499 * if (IS_ERR(vm_bo))500 * return PTR_ERR(vm_bo);501 *502 * drm_gpuva_for_each_op(op, ops) {503 * struct drm_gpuva *va;504 *505 * switch (op->op) {506 * case DRM_GPUVA_OP_MAP:507 * va = driver_gpuva_alloc();508 * if (!va)509 * ; // unwind previous VA space updates,510 * // free memory and unlock511 *512 * driver_vm_map();513 * drm_gpuva_map(gpuvm, va, &op->map);514 * drm_gpuva_link(va, vm_bo);515 *516 * break;517 * case DRM_GPUVA_OP_REMAP: {518 * struct drm_gpuva *prev = NULL, *next = NULL;519 *520 * va = op->remap.unmap->va;521 *522 * if (op->remap.prev) {523 * prev = driver_gpuva_alloc();524 * if (!prev)525 * ; // unwind previous VA space526 * // updates, free memory and527 * // unlock528 * }529 *530 * if (op->remap.next) {531 * next = driver_gpuva_alloc();532 * if (!next)533 * ; // unwind previous VA space534 * // updates, free memory and535 * // unlock536 * }537 *538 * driver_vm_remap();539 * drm_gpuva_remap(prev, next, &op->remap);540 *541 * if (prev)542 * drm_gpuva_link(prev, va->vm_bo);543 * if (next)544 * drm_gpuva_link(next, va->vm_bo);545 * drm_gpuva_unlink(va);546 *547 * break;548 * }549 * case DRM_GPUVA_OP_UNMAP:550 * va = op->unmap->va;551 *552 * driver_vm_unmap();553 * drm_gpuva_unlink(va);554 * drm_gpuva_unmap(&op->unmap);555 *556 * break;557 * default:558 * break;559 * }560 * }561 * drm_gpuvm_bo_put(vm_bo);562 * driver_unlock_va_space();563 *564 * return 0;565 * }566 *567 * 2) Receive a callback for each &drm_gpuva_op to create a new mapping::568 *569 * struct driver_context {570 * struct drm_gpuvm *gpuvm;571 * struct drm_gpuvm_bo *vm_bo;572 * struct drm_gpuva *new_va;573 * struct drm_gpuva *prev_va;574 * struct drm_gpuva *next_va;575 * };576 *577 * // ops to pass to drm_gpuvm_init()578 * static const struct drm_gpuvm_ops driver_gpuvm_ops = {579 * .sm_step_map = driver_gpuva_map,580 * .sm_step_remap = driver_gpuva_remap,581 * .sm_step_unmap = driver_gpuva_unmap,582 * };583 *584 * // Typically drivers would embedd the &drm_gpuvm and &drm_gpuva585 * // structure in individual driver structures and lock the dma-resv with586 * // drm_exec or similar helpers.587 * int driver_mapping_create(struct drm_gpuvm *gpuvm,588 * u64 addr, u64 range,589 * struct drm_gem_object *obj, u64 offset)590 * {591 * struct driver_context ctx;592 * struct drm_gpuvm_bo *vm_bo;593 * struct drm_gpuva_ops *ops;594 * struct drm_gpuva_op *op;595 * int ret = 0;596 *597 * ctx.gpuvm = gpuvm;598 *599 * ctx.new_va = kzalloc(sizeof(*ctx.new_va), GFP_KERNEL);600 * ctx.prev_va = kzalloc(sizeof(*ctx.prev_va), GFP_KERNEL);601 * ctx.next_va = kzalloc(sizeof(*ctx.next_va), GFP_KERNEL);602 * ctx.vm_bo = drm_gpuvm_bo_create(gpuvm, obj);603 * if (!ctx.new_va || !ctx.prev_va || !ctx.next_va || !vm_bo) {604 * ret = -ENOMEM;605 * goto out;606 * }607 *608 * // Typically protected with a driver specific GEM gpuva lock609 * // used in the fence signaling path for drm_gpuva_link() and610 * // drm_gpuva_unlink(), hence pre-allocate.611 * ctx.vm_bo = drm_gpuvm_bo_obtain_prealloc(ctx.vm_bo);612 *613 * driver_lock_va_space();614 * ret = drm_gpuvm_sm_map(gpuvm, &ctx, addr, range, obj, offset);615 * driver_unlock_va_space();616 *617 * out:618 * drm_gpuvm_bo_put(ctx.vm_bo);619 * kfree(ctx.new_va);620 * kfree(ctx.prev_va);621 * kfree(ctx.next_va);622 * return ret;623 * }624 *625 * int driver_gpuva_map(struct drm_gpuva_op *op, void *__ctx)626 * {627 * struct driver_context *ctx = __ctx;628 *629 * drm_gpuva_map(ctx->vm, ctx->new_va, &op->map);630 *631 * drm_gpuva_link(ctx->new_va, ctx->vm_bo);632 *633 * // prevent the new GPUVA from being freed in634 * // driver_mapping_create()635 * ctx->new_va = NULL;636 *637 * return 0;638 * }639 *640 * int driver_gpuva_remap(struct drm_gpuva_op *op, void *__ctx)641 * {642 * struct driver_context *ctx = __ctx;643 * struct drm_gpuva *va = op->remap.unmap->va;644 *645 * drm_gpuva_remap(ctx->prev_va, ctx->next_va, &op->remap);646 *647 * if (op->remap.prev) {648 * drm_gpuva_link(ctx->prev_va, va->vm_bo);649 * ctx->prev_va = NULL;650 * }651 *652 * if (op->remap.next) {653 * drm_gpuva_link(ctx->next_va, va->vm_bo);654 * ctx->next_va = NULL;655 * }656 *657 * drm_gpuva_unlink(va);658 * kfree(va);659 *660 * return 0;661 * }662 *663 * int driver_gpuva_unmap(struct drm_gpuva_op *op, void *__ctx)664 * {665 * drm_gpuva_unlink(op->unmap.va);666 * drm_gpuva_unmap(&op->unmap);667 * kfree(op->unmap.va);668 *669 * return 0;670 * }671 */672 673/**674 * get_next_vm_bo_from_list() - get the next vm_bo element675 * @__gpuvm: the &drm_gpuvm676 * @__list_name: the name of the list we're iterating on677 * @__local_list: a pointer to the local list used to store already iterated items678 * @__prev_vm_bo: the previous element we got from get_next_vm_bo_from_list()679 *680 * This helper is here to provide lockless list iteration. Lockless as in, the681 * iterator releases the lock immediately after picking the first element from682 * the list, so list insertion deletion can happen concurrently.683 *684 * Elements popped from the original list are kept in a local list, so removal685 * and is_empty checks can still happen while we're iterating the list.686 */687#define get_next_vm_bo_from_list(__gpuvm, __list_name, __local_list, __prev_vm_bo) \688 ({ \689 struct drm_gpuvm_bo *__vm_bo = NULL; \690 \691 drm_gpuvm_bo_put(__prev_vm_bo); \692 \693 spin_lock(&(__gpuvm)->__list_name.lock); \694 if (!(__gpuvm)->__list_name.local_list) \695 (__gpuvm)->__list_name.local_list = __local_list; \696 else \697 drm_WARN_ON((__gpuvm)->drm, \698 (__gpuvm)->__list_name.local_list != __local_list); \699 \700 while (!list_empty(&(__gpuvm)->__list_name.list)) { \701 __vm_bo = list_first_entry(&(__gpuvm)->__list_name.list, \702 struct drm_gpuvm_bo, \703 list.entry.__list_name); \704 if (kref_get_unless_zero(&__vm_bo->kref)) { \705 list_move_tail(&(__vm_bo)->list.entry.__list_name, \706 __local_list); \707 break; \708 } else { \709 list_del_init(&(__vm_bo)->list.entry.__list_name); \710 __vm_bo = NULL; \711 } \712 } \713 spin_unlock(&(__gpuvm)->__list_name.lock); \714 \715 __vm_bo; \716 })717 718/**719 * for_each_vm_bo_in_list() - internal vm_bo list iterator720 * @__gpuvm: the &drm_gpuvm721 * @__list_name: the name of the list we're iterating on722 * @__local_list: a pointer to the local list used to store already iterated items723 * @__vm_bo: the struct drm_gpuvm_bo to assign in each iteration step724 *725 * This helper is here to provide lockless list iteration. Lockless as in, the726 * iterator releases the lock immediately after picking the first element from the727 * list, hence list insertion and deletion can happen concurrently.728 *729 * It is not allowed to re-assign the vm_bo pointer from inside this loop.730 *731 * Typical use:732 *733 * struct drm_gpuvm_bo *vm_bo;734 * LIST_HEAD(my_local_list);735 *736 * ret = 0;737 * for_each_vm_bo_in_list(gpuvm, <list_name>, &my_local_list, vm_bo) {738 * ret = do_something_with_vm_bo(..., vm_bo);739 * if (ret)740 * break;741 * }742 * // Drop ref in case we break out of the loop.743 * drm_gpuvm_bo_put(vm_bo);744 * restore_vm_bo_list(gpuvm, <list_name>, &my_local_list);745 *746 *747 * Only used for internal list iterations, not meant to be exposed to the outside748 * world.749 */750#define for_each_vm_bo_in_list(__gpuvm, __list_name, __local_list, __vm_bo) \751 for (__vm_bo = get_next_vm_bo_from_list(__gpuvm, __list_name, \752 __local_list, NULL); \753 __vm_bo; \754 __vm_bo = get_next_vm_bo_from_list(__gpuvm, __list_name, \755 __local_list, __vm_bo))756 757static void758__restore_vm_bo_list(struct drm_gpuvm *gpuvm, spinlock_t *lock,759 struct list_head *list, struct list_head **local_list)760{761 /* Merge back the two lists, moving local list elements to the762 * head to preserve previous ordering, in case it matters.763 */764 spin_lock(lock);765 if (*local_list) {766 list_splice(*local_list, list);767 *local_list = NULL;768 }769 spin_unlock(lock);770}771 772/**773 * restore_vm_bo_list() - move vm_bo elements back to their original list774 * @__gpuvm: the &drm_gpuvm775 * @__list_name: the name of the list we're iterating on776 *777 * When we're done iterating a vm_bo list, we should call restore_vm_bo_list()778 * to restore the original state and let new iterations take place.779 */780#define restore_vm_bo_list(__gpuvm, __list_name) \781 __restore_vm_bo_list((__gpuvm), &(__gpuvm)->__list_name.lock, \782 &(__gpuvm)->__list_name.list, \783 &(__gpuvm)->__list_name.local_list)784 785static void786cond_spin_lock(spinlock_t *lock, bool cond)787{788 if (cond)789 spin_lock(lock);790}791 792static void793cond_spin_unlock(spinlock_t *lock, bool cond)794{795 if (cond)796 spin_unlock(lock);797}798 799static void800__drm_gpuvm_bo_list_add(struct drm_gpuvm *gpuvm, spinlock_t *lock,801 struct list_head *entry, struct list_head *list)802{803 cond_spin_lock(lock, !!lock);804 if (list_empty(entry))805 list_add_tail(entry, list);806 cond_spin_unlock(lock, !!lock);807}808 809/**810 * drm_gpuvm_bo_list_add() - insert a vm_bo into the given list811 * @__vm_bo: the &drm_gpuvm_bo812 * @__list_name: the name of the list to insert into813 * @__lock: whether to lock with the internal spinlock814 *815 * Inserts the given @__vm_bo into the list specified by @__list_name.816 */817#define drm_gpuvm_bo_list_add(__vm_bo, __list_name, __lock) \818 __drm_gpuvm_bo_list_add((__vm_bo)->vm, \819 __lock ? &(__vm_bo)->vm->__list_name.lock : \820 NULL, \821 &(__vm_bo)->list.entry.__list_name, \822 &(__vm_bo)->vm->__list_name.list)823 824static void825__drm_gpuvm_bo_list_del(struct drm_gpuvm *gpuvm, spinlock_t *lock,826 struct list_head *entry, bool init)827{828 cond_spin_lock(lock, !!lock);829 if (init) {830 if (!list_empty(entry))831 list_del_init(entry);832 } else {833 list_del(entry);834 }835 cond_spin_unlock(lock, !!lock);836}837 838/**839 * drm_gpuvm_bo_list_del_init() - remove a vm_bo from the given list840 * @__vm_bo: the &drm_gpuvm_bo841 * @__list_name: the name of the list to insert into842 * @__lock: whether to lock with the internal spinlock843 *844 * Removes the given @__vm_bo from the list specified by @__list_name.845 */846#define drm_gpuvm_bo_list_del_init(__vm_bo, __list_name, __lock) \847 __drm_gpuvm_bo_list_del((__vm_bo)->vm, \848 __lock ? &(__vm_bo)->vm->__list_name.lock : \849 NULL, \850 &(__vm_bo)->list.entry.__list_name, \851 true)852 853/**854 * drm_gpuvm_bo_list_del() - remove a vm_bo from the given list855 * @__vm_bo: the &drm_gpuvm_bo856 * @__list_name: the name of the list to insert into857 * @__lock: whether to lock with the internal spinlock858 *859 * Removes the given @__vm_bo from the list specified by @__list_name.860 */861#define drm_gpuvm_bo_list_del(__vm_bo, __list_name, __lock) \862 __drm_gpuvm_bo_list_del((__vm_bo)->vm, \863 __lock ? &(__vm_bo)->vm->__list_name.lock : \864 NULL, \865 &(__vm_bo)->list.entry.__list_name, \866 false)867 868#define to_drm_gpuva(__node) container_of((__node), struct drm_gpuva, rb.node)869 870#define GPUVA_START(node) ((node)->va.addr)871#define GPUVA_LAST(node) ((node)->va.addr + (node)->va.range - 1)872 873/* We do not actually use drm_gpuva_it_next(), tell the compiler to not complain874 * about this.875 */876INTERVAL_TREE_DEFINE(struct drm_gpuva, rb.node, u64, rb.__subtree_last,877 GPUVA_START, GPUVA_LAST, static __maybe_unused,878 drm_gpuva_it)879 880static int __drm_gpuva_insert(struct drm_gpuvm *gpuvm,881 struct drm_gpuva *va);882static void __drm_gpuva_remove(struct drm_gpuva *va);883 884static bool885drm_gpuvm_check_overflow(u64 addr, u64 range)886{887 u64 end;888 889 return check_add_overflow(addr, range, &end);890}891 892static bool893drm_gpuvm_warn_check_overflow(struct drm_gpuvm *gpuvm, u64 addr, u64 range)894{895 return drm_WARN(gpuvm->drm, drm_gpuvm_check_overflow(addr, range),896 "GPUVA address limited to %zu bytes.\n", sizeof(addr));897}898 899static bool900drm_gpuvm_in_mm_range(struct drm_gpuvm *gpuvm, u64 addr, u64 range)901{902 u64 end = addr + range;903 u64 mm_start = gpuvm->mm_start;904 u64 mm_end = mm_start + gpuvm->mm_range;905 906 return addr >= mm_start && end <= mm_end;907}908 909static bool910drm_gpuvm_in_kernel_node(struct drm_gpuvm *gpuvm, u64 addr, u64 range)911{912 u64 end = addr + range;913 u64 kstart = gpuvm->kernel_alloc_node.va.addr;914 u64 krange = gpuvm->kernel_alloc_node.va.range;915 u64 kend = kstart + krange;916 917 return krange && addr < kend && kstart < end;918}919 920/**921 * drm_gpuvm_range_valid() - checks whether the given range is valid for the922 * given &drm_gpuvm923 * @gpuvm: the GPUVM to check the range for924 * @addr: the base address925 * @range: the range starting from the base address926 *927 * Checks whether the range is within the GPUVM's managed boundaries.928 *929 * Returns: true for a valid range, false otherwise930 */931bool932drm_gpuvm_range_valid(struct drm_gpuvm *gpuvm,933 u64 addr, u64 range)934{935 return !drm_gpuvm_check_overflow(addr, range) &&936 drm_gpuvm_in_mm_range(gpuvm, addr, range) &&937 !drm_gpuvm_in_kernel_node(gpuvm, addr, range);938}939EXPORT_SYMBOL_GPL(drm_gpuvm_range_valid);940 941static void942drm_gpuvm_gem_object_free(struct drm_gem_object *obj)943{944 drm_gem_object_release(obj);945 kfree(obj);946}947 948static const struct drm_gem_object_funcs drm_gpuvm_object_funcs = {949 .free = drm_gpuvm_gem_object_free,950};951 952/**953 * drm_gpuvm_resv_object_alloc() - allocate a dummy &drm_gem_object954 * @drm: the drivers &drm_device955 *956 * Allocates a dummy &drm_gem_object which can be passed to drm_gpuvm_init() in957 * order to serve as root GEM object providing the &drm_resv shared across958 * &drm_gem_objects local to a single GPUVM.959 *960 * Returns: the &drm_gem_object on success, NULL on failure961 */962struct drm_gem_object *963drm_gpuvm_resv_object_alloc(struct drm_device *drm)964{965 struct drm_gem_object *obj;966 967 obj = kzalloc(sizeof(*obj), GFP_KERNEL);968 if (!obj)969 return NULL;970 971 obj->funcs = &drm_gpuvm_object_funcs;972 drm_gem_private_object_init(drm, obj, 0);973 974 return obj;975}976EXPORT_SYMBOL_GPL(drm_gpuvm_resv_object_alloc);977 978/**979 * drm_gpuvm_init() - initialize a &drm_gpuvm980 * @gpuvm: pointer to the &drm_gpuvm to initialize981 * @name: the name of the GPU VA space982 * @flags: the &drm_gpuvm_flags for this GPUVM983 * @drm: the &drm_device this VM resides in984 * @r_obj: the resv &drm_gem_object providing the GPUVM's common &dma_resv985 * @start_offset: the start offset of the GPU VA space986 * @range: the size of the GPU VA space987 * @reserve_offset: the start of the kernel reserved GPU VA area988 * @reserve_range: the size of the kernel reserved GPU VA area989 * @ops: &drm_gpuvm_ops called on &drm_gpuvm_sm_map / &drm_gpuvm_sm_unmap990 *991 * The &drm_gpuvm must be initialized with this function before use.992 *993 * Note that @gpuvm must be cleared to 0 before calling this function. The given994 * &name is expected to be managed by the surrounding driver structures.995 */996void997drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,998 enum drm_gpuvm_flags flags,999 struct drm_device *drm,1000 struct drm_gem_object *r_obj,1001 u64 start_offset, u64 range,1002 u64 reserve_offset, u64 reserve_range,1003 const struct drm_gpuvm_ops *ops)1004{1005 gpuvm->rb.tree = RB_ROOT_CACHED;1006 INIT_LIST_HEAD(&gpuvm->rb.list);1007 1008 INIT_LIST_HEAD(&gpuvm->extobj.list);1009 spin_lock_init(&gpuvm->extobj.lock);1010 1011 INIT_LIST_HEAD(&gpuvm->evict.list);1012 spin_lock_init(&gpuvm->evict.lock);1013 1014 kref_init(&gpuvm->kref);1015 1016 gpuvm->name = name ? name : "unknown";1017 gpuvm->flags = flags;1018 gpuvm->ops = ops;1019 gpuvm->drm = drm;1020 gpuvm->r_obj = r_obj;1021 1022 drm_gem_object_get(r_obj);1023 1024 drm_gpuvm_warn_check_overflow(gpuvm, start_offset, range);1025 gpuvm->mm_start = start_offset;1026 gpuvm->mm_range = range;1027 1028 memset(&gpuvm->kernel_alloc_node, 0, sizeof(struct drm_gpuva));1029 if (reserve_range) {1030 gpuvm->kernel_alloc_node.va.addr = reserve_offset;1031 gpuvm->kernel_alloc_node.va.range = reserve_range;1032 1033 if (likely(!drm_gpuvm_warn_check_overflow(gpuvm, reserve_offset,1034 reserve_range)))1035 __drm_gpuva_insert(gpuvm, &gpuvm->kernel_alloc_node);1036 }1037}1038EXPORT_SYMBOL_GPL(drm_gpuvm_init);1039 1040static void1041drm_gpuvm_fini(struct drm_gpuvm *gpuvm)1042{1043 gpuvm->name = NULL;1044 1045 if (gpuvm->kernel_alloc_node.va.range)1046 __drm_gpuva_remove(&gpuvm->kernel_alloc_node);1047 1048 drm_WARN(gpuvm->drm, !RB_EMPTY_ROOT(&gpuvm->rb.tree.rb_root),1049 "GPUVA tree is not empty, potentially leaking memory.\n");1050 1051 drm_WARN(gpuvm->drm, !list_empty(&gpuvm->extobj.list),1052 "Extobj list should be empty.\n");1053 drm_WARN(gpuvm->drm, !list_empty(&gpuvm->evict.list),1054 "Evict list should be empty.\n");1055 1056 drm_gem_object_put(gpuvm->r_obj);1057}1058 1059static void1060drm_gpuvm_free(struct kref *kref)1061{1062 struct drm_gpuvm *gpuvm = container_of(kref, struct drm_gpuvm, kref);1063 1064 drm_gpuvm_fini(gpuvm);1065 1066 if (drm_WARN_ON(gpuvm->drm, !gpuvm->ops->vm_free))1067 return;1068 1069 gpuvm->ops->vm_free(gpuvm);1070}1071 1072/**1073 * drm_gpuvm_put() - drop a struct drm_gpuvm reference1074 * @gpuvm: the &drm_gpuvm to release the reference of1075 *1076 * This releases a reference to @gpuvm.1077 *1078 * This function may be called from atomic context.1079 */1080void1081drm_gpuvm_put(struct drm_gpuvm *gpuvm)1082{1083 if (gpuvm)1084 kref_put(&gpuvm->kref, drm_gpuvm_free);1085}1086EXPORT_SYMBOL_GPL(drm_gpuvm_put);1087 1088static int1089exec_prepare_obj(struct drm_exec *exec, struct drm_gem_object *obj,1090 unsigned int num_fences)1091{1092 return num_fences ? drm_exec_prepare_obj(exec, obj, num_fences) :1093 drm_exec_lock_obj(exec, obj);1094}1095 1096/**1097 * drm_gpuvm_prepare_vm() - prepare the GPUVMs common dma-resv1098 * @gpuvm: the &drm_gpuvm1099 * @exec: the &drm_exec context1100 * @num_fences: the amount of &dma_fences to reserve1101 *1102 * Calls drm_exec_prepare_obj() for the GPUVMs dummy &drm_gem_object; if1103 * @num_fences is zero drm_exec_lock_obj() is called instead.1104 *1105 * Using this function directly, it is the drivers responsibility to call1106 * drm_exec_init() and drm_exec_fini() accordingly.1107 *1108 * Returns: 0 on success, negative error code on failure.1109 */1110int1111drm_gpuvm_prepare_vm(struct drm_gpuvm *gpuvm,1112 struct drm_exec *exec,1113 unsigned int num_fences)1114{1115 return exec_prepare_obj(exec, gpuvm->r_obj, num_fences);1116}1117EXPORT_SYMBOL_GPL(drm_gpuvm_prepare_vm);1118 1119static int1120__drm_gpuvm_prepare_objects(struct drm_gpuvm *gpuvm,1121 struct drm_exec *exec,1122 unsigned int num_fences)1123{1124 struct drm_gpuvm_bo *vm_bo;1125 LIST_HEAD(extobjs);1126 int ret = 0;1127 1128 for_each_vm_bo_in_list(gpuvm, extobj, &extobjs, vm_bo) {1129 ret = exec_prepare_obj(exec, vm_bo->obj, num_fences);1130 if (ret)1131 break;1132 }1133 /* Drop ref in case we break out of the loop. */1134 drm_gpuvm_bo_put(vm_bo);1135 restore_vm_bo_list(gpuvm, extobj);1136 1137 return ret;1138}1139 1140static int1141drm_gpuvm_prepare_objects_locked(struct drm_gpuvm *gpuvm,1142 struct drm_exec *exec,1143 unsigned int num_fences)1144{1145 struct drm_gpuvm_bo *vm_bo;1146 int ret = 0;1147 1148 drm_gpuvm_resv_assert_held(gpuvm);1149 list_for_each_entry(vm_bo, &gpuvm->extobj.list, list.entry.extobj) {1150 ret = exec_prepare_obj(exec, vm_bo->obj, num_fences);1151 if (ret)1152 break;1153 1154 if (vm_bo->evicted)1155 drm_gpuvm_bo_list_add(vm_bo, evict, false);1156 }1157 1158 return ret;1159}1160 1161/**1162 * drm_gpuvm_prepare_objects() - prepare all assoiciated BOs1163 * @gpuvm: the &drm_gpuvm1164 * @exec: the &drm_exec locking context1165 * @num_fences: the amount of &dma_fences to reserve1166 *1167 * Calls drm_exec_prepare_obj() for all &drm_gem_objects the given1168 * &drm_gpuvm contains mappings of; if @num_fences is zero drm_exec_lock_obj()1169 * is called instead.1170 *1171 * Using this function directly, it is the drivers responsibility to call1172 * drm_exec_init() and drm_exec_fini() accordingly.1173 *1174 * Note: This function is safe against concurrent insertion and removal of1175 * external objects, however it is not safe against concurrent usage itself.1176 *1177 * Drivers need to make sure to protect this case with either an outer VM lock1178 * or by calling drm_gpuvm_prepare_vm() before this function within the1179 * drm_exec_until_all_locked() loop, such that the GPUVM's dma-resv lock ensures1180 * mutual exclusion.1181 *1182 * Returns: 0 on success, negative error code on failure.1183 */1184int1185drm_gpuvm_prepare_objects(struct drm_gpuvm *gpuvm,1186 struct drm_exec *exec,1187 unsigned int num_fences)1188{1189 if (drm_gpuvm_resv_protected(gpuvm))1190 return drm_gpuvm_prepare_objects_locked(gpuvm, exec,1191 num_fences);1192 else1193 return __drm_gpuvm_prepare_objects(gpuvm, exec, num_fences);1194}1195EXPORT_SYMBOL_GPL(drm_gpuvm_prepare_objects);1196 1197/**1198 * drm_gpuvm_prepare_range() - prepare all BOs mapped within a given range1199 * @gpuvm: the &drm_gpuvm1200 * @exec: the &drm_exec locking context1201 * @addr: the start address within the VA space1202 * @range: the range to iterate within the VA space1203 * @num_fences: the amount of &dma_fences to reserve1204 *1205 * Calls drm_exec_prepare_obj() for all &drm_gem_objects mapped between @addr1206 * and @addr + @range; if @num_fences is zero drm_exec_lock_obj() is called1207 * instead.1208 *1209 * Returns: 0 on success, negative error code on failure.1210 */1211int1212drm_gpuvm_prepare_range(struct drm_gpuvm *gpuvm, struct drm_exec *exec,1213 u64 addr, u64 range, unsigned int num_fences)1214{1215 struct drm_gpuva *va;1216 u64 end = addr + range;1217 int ret;1218 1219 drm_gpuvm_for_each_va_range(va, gpuvm, addr, end) {1220 struct drm_gem_object *obj = va->gem.obj;1221 1222 ret = exec_prepare_obj(exec, obj, num_fences);1223 if (ret)1224 return ret;1225 }1226 1227 return 0;1228}1229EXPORT_SYMBOL_GPL(drm_gpuvm_prepare_range);1230 1231/**1232 * drm_gpuvm_exec_lock() - lock all dma-resv of all assoiciated BOs1233 * @vm_exec: the &drm_gpuvm_exec wrapper1234 *1235 * Acquires all dma-resv locks of all &drm_gem_objects the given1236 * &drm_gpuvm contains mappings of.1237 *1238 * Addionally, when calling this function with struct drm_gpuvm_exec::extra1239 * being set the driver receives the given @fn callback to lock additional1240 * dma-resv in the context of the &drm_gpuvm_exec instance. Typically, drivers1241 * would call drm_exec_prepare_obj() from within this callback.1242 *1243 * Returns: 0 on success, negative error code on failure.1244 */1245int1246drm_gpuvm_exec_lock(struct drm_gpuvm_exec *vm_exec)1247{1248 struct drm_gpuvm *gpuvm = vm_exec->vm;1249 struct drm_exec *exec = &vm_exec->exec;1250 unsigned int num_fences = vm_exec->num_fences;1251 int ret;1252 1253 drm_exec_init(exec, vm_exec->flags, 0);1254 1255 drm_exec_until_all_locked(exec) {1256 ret = drm_gpuvm_prepare_vm(gpuvm, exec, num_fences);1257 drm_exec_retry_on_contention(exec);1258 if (ret)1259 goto err;1260 1261 ret = drm_gpuvm_prepare_objects(gpuvm, exec, num_fences);1262 drm_exec_retry_on_contention(exec);1263 if (ret)1264 goto err;1265 1266 if (vm_exec->extra.fn) {1267 ret = vm_exec->extra.fn(vm_exec);1268 drm_exec_retry_on_contention(exec);1269 if (ret)1270 goto err;1271 }1272 }1273 1274 return 0;1275 1276err:1277 drm_exec_fini(exec);1278 return ret;1279}1280EXPORT_SYMBOL_GPL(drm_gpuvm_exec_lock);1281 1282static int1283fn_lock_array(struct drm_gpuvm_exec *vm_exec)1284{1285 struct {1286 struct drm_gem_object **objs;1287 unsigned int num_objs;1288 } *args = vm_exec->extra.priv;1289 1290 return drm_exec_prepare_array(&vm_exec->exec, args->objs,1291 args->num_objs, vm_exec->num_fences);1292}1293 1294/**1295 * drm_gpuvm_exec_lock_array() - lock all dma-resv of all assoiciated BOs1296 * @vm_exec: the &drm_gpuvm_exec wrapper1297 * @objs: additional &drm_gem_objects to lock1298 * @num_objs: the number of additional &drm_gem_objects to lock1299 *1300 * Acquires all dma-resv locks of all &drm_gem_objects the given &drm_gpuvm1301 * contains mappings of, plus the ones given through @objs.1302 *1303 * Returns: 0 on success, negative error code on failure.1304 */1305int1306drm_gpuvm_exec_lock_array(struct drm_gpuvm_exec *vm_exec,1307 struct drm_gem_object **objs,1308 unsigned int num_objs)1309{1310 struct {1311 struct drm_gem_object **objs;1312 unsigned int num_objs;1313 } args;1314 1315 args.objs = objs;1316 args.num_objs = num_objs;1317 1318 vm_exec->extra.fn = fn_lock_array;1319 vm_exec->extra.priv = &args;1320 1321 return drm_gpuvm_exec_lock(vm_exec);1322}1323EXPORT_SYMBOL_GPL(drm_gpuvm_exec_lock_array);1324 1325/**1326 * drm_gpuvm_exec_lock_range() - prepare all BOs mapped within a given range1327 * @vm_exec: the &drm_gpuvm_exec wrapper1328 * @addr: the start address within the VA space1329 * @range: the range to iterate within the VA space1330 *1331 * Acquires all dma-resv locks of all &drm_gem_objects mapped between @addr and1332 * @addr + @range.1333 *1334 * Returns: 0 on success, negative error code on failure.1335 */1336int1337drm_gpuvm_exec_lock_range(struct drm_gpuvm_exec *vm_exec,1338 u64 addr, u64 range)1339{1340 struct drm_gpuvm *gpuvm = vm_exec->vm;1341 struct drm_exec *exec = &vm_exec->exec;1342 int ret;1343 1344 drm_exec_init(exec, vm_exec->flags, 0);1345 1346 drm_exec_until_all_locked(exec) {1347 ret = drm_gpuvm_prepare_range(gpuvm, exec, addr, range,1348 vm_exec->num_fences);1349 drm_exec_retry_on_contention(exec);1350 if (ret)1351 goto err;1352 }1353 1354 return ret;1355 1356err:1357 drm_exec_fini(exec);1358 return ret;1359}1360EXPORT_SYMBOL_GPL(drm_gpuvm_exec_lock_range);1361 1362static int1363__drm_gpuvm_validate(struct drm_gpuvm *gpuvm, struct drm_exec *exec)1364{1365 const struct drm_gpuvm_ops *ops = gpuvm->ops;1366 struct drm_gpuvm_bo *vm_bo;1367 LIST_HEAD(evict);1368 int ret = 0;1369 1370 for_each_vm_bo_in_list(gpuvm, evict, &evict, vm_bo) {1371 ret = ops->vm_bo_validate(vm_bo, exec);1372 if (ret)1373 break;1374 }1375 /* Drop ref in case we break out of the loop. */1376 drm_gpuvm_bo_put(vm_bo);1377 restore_vm_bo_list(gpuvm, evict);1378 1379 return ret;1380}1381 1382static int1383drm_gpuvm_validate_locked(struct drm_gpuvm *gpuvm, struct drm_exec *exec)1384{1385 const struct drm_gpuvm_ops *ops = gpuvm->ops;1386 struct drm_gpuvm_bo *vm_bo, *next;1387 int ret = 0;1388 1389 drm_gpuvm_resv_assert_held(gpuvm);1390 1391 list_for_each_entry_safe(vm_bo, next, &gpuvm->evict.list,1392 list.entry.evict) {1393 ret = ops->vm_bo_validate(vm_bo, exec);1394 if (ret)1395 break;1396 1397 dma_resv_assert_held(vm_bo->obj->resv);1398 if (!vm_bo->evicted)1399 drm_gpuvm_bo_list_del_init(vm_bo, evict, false);1400 }1401 1402 return ret;1403}1404 1405/**1406 * drm_gpuvm_validate() - validate all BOs marked as evicted1407 * @gpuvm: the &drm_gpuvm to validate evicted BOs1408 * @exec: the &drm_exec instance used for locking the GPUVM1409 *1410 * Calls the &drm_gpuvm_ops::vm_bo_validate callback for all evicted buffer1411 * objects being mapped in the given &drm_gpuvm.1412 *1413 * Returns: 0 on success, negative error code on failure.1414 */1415int1416drm_gpuvm_validate(struct drm_gpuvm *gpuvm, struct drm_exec *exec)1417{1418 const struct drm_gpuvm_ops *ops = gpuvm->ops;1419 1420 if (unlikely(!ops || !ops->vm_bo_validate))1421 return -EOPNOTSUPP;1422 1423 if (drm_gpuvm_resv_protected(gpuvm))1424 return drm_gpuvm_validate_locked(gpuvm, exec);1425 else1426 return __drm_gpuvm_validate(gpuvm, exec);1427}1428EXPORT_SYMBOL_GPL(drm_gpuvm_validate);1429 1430/**1431 * drm_gpuvm_resv_add_fence - add fence to private and all extobj1432 * dma-resv1433 * @gpuvm: the &drm_gpuvm to add a fence to1434 * @exec: the &drm_exec locking context1435 * @fence: fence to add1436 * @private_usage: private dma-resv usage1437 * @extobj_usage: extobj dma-resv usage1438 */1439void1440drm_gpuvm_resv_add_fence(struct drm_gpuvm *gpuvm,1441 struct drm_exec *exec,1442 struct dma_fence *fence,1443 enum dma_resv_usage private_usage,1444 enum dma_resv_usage extobj_usage)1445{1446 struct drm_gem_object *obj;1447 unsigned long index;1448 1449 drm_exec_for_each_locked_object(exec, index, obj) {1450 dma_resv_assert_held(obj->resv);1451 dma_resv_add_fence(obj->resv, fence,1452 drm_gpuvm_is_extobj(gpuvm, obj) ?1453 extobj_usage : private_usage);1454 }1455}1456EXPORT_SYMBOL_GPL(drm_gpuvm_resv_add_fence);1457 1458/**1459 * drm_gpuvm_bo_create() - create a new instance of struct drm_gpuvm_bo1460 * @gpuvm: The &drm_gpuvm the @obj is mapped in.1461 * @obj: The &drm_gem_object being mapped in the @gpuvm.1462 *1463 * If provided by the driver, this function uses the &drm_gpuvm_ops1464 * vm_bo_alloc() callback to allocate.1465 *1466 * Returns: a pointer to the &drm_gpuvm_bo on success, NULL on failure1467 */1468struct drm_gpuvm_bo *1469drm_gpuvm_bo_create(struct drm_gpuvm *gpuvm,1470 struct drm_gem_object *obj)1471{1472 const struct drm_gpuvm_ops *ops = gpuvm->ops;1473 struct drm_gpuvm_bo *vm_bo;1474 1475 if (ops && ops->vm_bo_alloc)1476 vm_bo = ops->vm_bo_alloc();1477 else1478 vm_bo = kzalloc(sizeof(*vm_bo), GFP_KERNEL);1479 1480 if (unlikely(!vm_bo))1481 return NULL;1482 1483 vm_bo->vm = drm_gpuvm_get(gpuvm);1484 vm_bo->obj = obj;1485 drm_gem_object_get(obj);1486 1487 kref_init(&vm_bo->kref);1488 INIT_LIST_HEAD(&vm_bo->list.gpuva);1489 INIT_LIST_HEAD(&vm_bo->list.entry.gem);1490 1491 INIT_LIST_HEAD(&vm_bo->list.entry.extobj);1492 INIT_LIST_HEAD(&vm_bo->list.entry.evict);1493 1494 return vm_bo;1495}1496EXPORT_SYMBOL_GPL(drm_gpuvm_bo_create);1497 1498static void1499drm_gpuvm_bo_destroy(struct kref *kref)1500{1501 struct drm_gpuvm_bo *vm_bo = container_of(kref, struct drm_gpuvm_bo,1502 kref);1503 struct drm_gpuvm *gpuvm = vm_bo->vm;1504 const struct drm_gpuvm_ops *ops = gpuvm->ops;1505 struct drm_gem_object *obj = vm_bo->obj;1506 bool lock = !drm_gpuvm_resv_protected(gpuvm);1507 1508 if (!lock)1509 drm_gpuvm_resv_assert_held(gpuvm);1510 1511 drm_gpuvm_bo_list_del(vm_bo, extobj, lock);1512 drm_gpuvm_bo_list_del(vm_bo, evict, lock);1513 1514 drm_gem_gpuva_assert_lock_held(obj);1515 list_del(&vm_bo->list.entry.gem);1516 1517 if (ops && ops->vm_bo_free)1518 ops->vm_bo_free(vm_bo);1519 else1520 kfree(vm_bo);1521 1522 drm_gpuvm_put(gpuvm);1523 drm_gem_object_put(obj);1524}1525 1526/**1527 * drm_gpuvm_bo_put() - drop a struct drm_gpuvm_bo reference1528 * @vm_bo: the &drm_gpuvm_bo to release the reference of1529 *1530 * This releases a reference to @vm_bo.1531 *1532 * If the reference count drops to zero, the &gpuvm_bo is destroyed, which1533 * includes removing it from the GEMs gpuva list. Hence, if a call to this1534 * function can potentially let the reference count drop to zero the caller must1535 * hold the dma-resv or driver specific GEM gpuva lock.1536 *1537 * This function may only be called from non-atomic context.1538 *1539 * Returns: true if vm_bo was destroyed, false otherwise.1540 */1541bool1542drm_gpuvm_bo_put(struct drm_gpuvm_bo *vm_bo)1543{1544 might_sleep();1545 1546 if (vm_bo)1547 return !!kref_put(&vm_bo->kref, drm_gpuvm_bo_destroy);1548 1549 return false;1550}1551EXPORT_SYMBOL_GPL(drm_gpuvm_bo_put);1552 1553static struct drm_gpuvm_bo *1554__drm_gpuvm_bo_find(struct drm_gpuvm *gpuvm,1555 struct drm_gem_object *obj)1556{1557 struct drm_gpuvm_bo *vm_bo;1558 1559 drm_gem_gpuva_assert_lock_held(obj);1560 drm_gem_for_each_gpuvm_bo(vm_bo, obj)1561 if (vm_bo->vm == gpuvm)1562 return vm_bo;1563 1564 return NULL;1565}1566 1567/**1568 * drm_gpuvm_bo_find() - find the &drm_gpuvm_bo for the given1569 * &drm_gpuvm and &drm_gem_object1570 * @gpuvm: The &drm_gpuvm the @obj is mapped in.1571 * @obj: The &drm_gem_object being mapped in the @gpuvm.1572 *1573 * Find the &drm_gpuvm_bo representing the combination of the given1574 * &drm_gpuvm and &drm_gem_object. If found, increases the reference1575 * count of the &drm_gpuvm_bo accordingly.1576 *1577 * Returns: a pointer to the &drm_gpuvm_bo on success, NULL on failure1578 */1579struct drm_gpuvm_bo *1580drm_gpuvm_bo_find(struct drm_gpuvm *gpuvm,1581 struct drm_gem_object *obj)1582{1583 struct drm_gpuvm_bo *vm_bo = __drm_gpuvm_bo_find(gpuvm, obj);1584 1585 return vm_bo ? drm_gpuvm_bo_get(vm_bo) : NULL;1586}1587EXPORT_SYMBOL_GPL(drm_gpuvm_bo_find);1588 1589/**1590 * drm_gpuvm_bo_obtain() - obtains and instance of the &drm_gpuvm_bo for the1591 * given &drm_gpuvm and &drm_gem_object1592 * @gpuvm: The &drm_gpuvm the @obj is mapped in.1593 * @obj: The &drm_gem_object being mapped in the @gpuvm.1594 *1595 * Find the &drm_gpuvm_bo representing the combination of the given1596 * &drm_gpuvm and &drm_gem_object. If found, increases the reference1597 * count of the &drm_gpuvm_bo accordingly. If not found, allocates a new1598 * &drm_gpuvm_bo.1599 *1600 * A new &drm_gpuvm_bo is added to the GEMs gpuva list.1601 *1602 * Returns: a pointer to the &drm_gpuvm_bo on success, an ERR_PTR on failure1603 */1604struct drm_gpuvm_bo *1605drm_gpuvm_bo_obtain(struct drm_gpuvm *gpuvm,1606 struct drm_gem_object *obj)1607{1608 struct drm_gpuvm_bo *vm_bo;1609 1610 vm_bo = drm_gpuvm_bo_find(gpuvm, obj);1611 if (vm_bo)1612 return vm_bo;1613 1614 vm_bo = drm_gpuvm_bo_create(gpuvm, obj);1615 if (!vm_bo)1616 return ERR_PTR(-ENOMEM);1617 1618 drm_gem_gpuva_assert_lock_held(obj);1619 list_add_tail(&vm_bo->list.entry.gem, &obj->gpuva.list);1620 1621 return vm_bo;1622}1623EXPORT_SYMBOL_GPL(drm_gpuvm_bo_obtain);1624 1625/**1626 * drm_gpuvm_bo_obtain_prealloc() - obtains and instance of the &drm_gpuvm_bo1627 * for the given &drm_gpuvm and &drm_gem_object1628 * @__vm_bo: A pre-allocated struct drm_gpuvm_bo.1629 *1630 * Find the &drm_gpuvm_bo representing the combination of the given1631 * &drm_gpuvm and &drm_gem_object. If found, increases the reference1632 * count of the found &drm_gpuvm_bo accordingly, while the @__vm_bo reference1633 * count is decreased. If not found @__vm_bo is returned without further1634 * increase of the reference count.1635 *1636 * A new &drm_gpuvm_bo is added to the GEMs gpuva list.1637 *1638 * Returns: a pointer to the found &drm_gpuvm_bo or @__vm_bo if no existing1639 * &drm_gpuvm_bo was found1640 */1641struct drm_gpuvm_bo *1642drm_gpuvm_bo_obtain_prealloc(struct drm_gpuvm_bo *__vm_bo)1643{1644 struct drm_gpuvm *gpuvm = __vm_bo->vm;1645 struct drm_gem_object *obj = __vm_bo->obj;1646 struct drm_gpuvm_bo *vm_bo;1647 1648 vm_bo = drm_gpuvm_bo_find(gpuvm, obj);1649 if (vm_bo) {1650 drm_gpuvm_bo_put(__vm_bo);1651 return vm_bo;1652 }1653 1654 drm_gem_gpuva_assert_lock_held(obj);1655 list_add_tail(&__vm_bo->list.entry.gem, &obj->gpuva.list);1656 1657 return __vm_bo;1658}1659EXPORT_SYMBOL_GPL(drm_gpuvm_bo_obtain_prealloc);1660 1661/**1662 * drm_gpuvm_bo_extobj_add() - adds the &drm_gpuvm_bo to its &drm_gpuvm's1663 * extobj list1664 * @vm_bo: The &drm_gpuvm_bo to add to its &drm_gpuvm's the extobj list.1665 *1666 * Adds the given @vm_bo to its &drm_gpuvm's extobj list if not on the list1667 * already and if the corresponding &drm_gem_object is an external object,1668 * actually.1669 */1670void1671drm_gpuvm_bo_extobj_add(struct drm_gpuvm_bo *vm_bo)1672{1673 struct drm_gpuvm *gpuvm = vm_bo->vm;1674 bool lock = !drm_gpuvm_resv_protected(gpuvm);1675 1676 if (!lock)1677 drm_gpuvm_resv_assert_held(gpuvm);1678 1679 if (drm_gpuvm_is_extobj(gpuvm, vm_bo->obj))1680 drm_gpuvm_bo_list_add(vm_bo, extobj, lock);1681}1682EXPORT_SYMBOL_GPL(drm_gpuvm_bo_extobj_add);1683 1684/**1685 * drm_gpuvm_bo_evict() - add / remove a &drm_gpuvm_bo to / from the &drm_gpuvms1686 * evicted list1687 * @vm_bo: the &drm_gpuvm_bo to add or remove1688 * @evict: indicates whether the object is evicted1689 *1690 * Adds a &drm_gpuvm_bo to or removes it from the &drm_gpuvms evicted list.1691 */1692void1693drm_gpuvm_bo_evict(struct drm_gpuvm_bo *vm_bo, bool evict)1694{1695 struct drm_gpuvm *gpuvm = vm_bo->vm;1696 struct drm_gem_object *obj = vm_bo->obj;1697 bool lock = !drm_gpuvm_resv_protected(gpuvm);1698 1699 dma_resv_assert_held(obj->resv);1700 vm_bo->evicted = evict;1701 1702 /* Can't add external objects to the evicted list directly if not using1703 * internal spinlocks, since in this case the evicted list is protected1704 * with the VM's common dma-resv lock.1705 */1706 if (drm_gpuvm_is_extobj(gpuvm, obj) && !lock)1707 return;1708 1709 if (evict)1710 drm_gpuvm_bo_list_add(vm_bo, evict, lock);1711 else1712 drm_gpuvm_bo_list_del_init(vm_bo, evict, lock);1713}1714EXPORT_SYMBOL_GPL(drm_gpuvm_bo_evict);1715 1716static int1717__drm_gpuva_insert(struct drm_gpuvm *gpuvm,1718 struct drm_gpuva *va)1719{1720 struct rb_node *node;1721 struct list_head *head;1722 1723 if (drm_gpuva_it_iter_first(&gpuvm->rb.tree,1724 GPUVA_START(va),1725 GPUVA_LAST(va)))1726 return -EEXIST;1727 1728 va->vm = gpuvm;1729 1730 drm_gpuva_it_insert(va, &gpuvm->rb.tree);1731 1732 node = rb_prev(&va->rb.node);1733 if (node)1734 head = &(to_drm_gpuva(node))->rb.entry;1735 else1736 head = &gpuvm->rb.list;1737 1738 list_add(&va->rb.entry, head);1739 1740 return 0;1741}1742 1743/**1744 * drm_gpuva_insert() - insert a &drm_gpuva1745 * @gpuvm: the &drm_gpuvm to insert the &drm_gpuva in1746 * @va: the &drm_gpuva to insert1747 *1748 * Insert a &drm_gpuva with a given address and range into a1749 * &drm_gpuvm.1750 *1751 * It is safe to use this function using the safe versions of iterating the GPU1752 * VA space, such as drm_gpuvm_for_each_va_safe() and1753 * drm_gpuvm_for_each_va_range_safe().1754 *1755 * Returns: 0 on success, negative error code on failure.1756 */1757int1758drm_gpuva_insert(struct drm_gpuvm *gpuvm,1759 struct drm_gpuva *va)1760{1761 u64 addr = va->va.addr;1762 u64 range = va->va.range;1763 int ret;1764 1765 if (unlikely(!drm_gpuvm_range_valid(gpuvm, addr, range)))1766 return -EINVAL;1767 1768 ret = __drm_gpuva_insert(gpuvm, va);1769 if (likely(!ret))1770 /* Take a reference of the GPUVM for the successfully inserted1771 * drm_gpuva. We can't take the reference in1772 * __drm_gpuva_insert() itself, since we don't want to increse1773 * the reference count for the GPUVM's kernel_alloc_node.1774 */1775 drm_gpuvm_get(gpuvm);1776 1777 return ret;1778}1779EXPORT_SYMBOL_GPL(drm_gpuva_insert);1780 1781static void1782__drm_gpuva_remove(struct drm_gpuva *va)1783{1784 drm_gpuva_it_remove(va, &va->vm->rb.tree);1785 list_del_init(&va->rb.entry);1786}1787 1788/**1789 * drm_gpuva_remove() - remove a &drm_gpuva1790 * @va: the &drm_gpuva to remove1791 *1792 * This removes the given &va from the underlaying tree.1793 *1794 * It is safe to use this function using the safe versions of iterating the GPU1795 * VA space, such as drm_gpuvm_for_each_va_safe() and1796 * drm_gpuvm_for_each_va_range_safe().1797 */1798void1799drm_gpuva_remove(struct drm_gpuva *va)1800{1801 struct drm_gpuvm *gpuvm = va->vm;1802 1803 if (unlikely(va == &gpuvm->kernel_alloc_node)) {1804 drm_WARN(gpuvm->drm, 1,1805 "Can't destroy kernel reserved node.\n");1806 return;1807 }1808 1809 __drm_gpuva_remove(va);1810 drm_gpuvm_put(va->vm);1811}1812EXPORT_SYMBOL_GPL(drm_gpuva_remove);1813 1814/**1815 * drm_gpuva_link() - link a &drm_gpuva1816 * @va: the &drm_gpuva to link1817 * @vm_bo: the &drm_gpuvm_bo to add the &drm_gpuva to1818 *1819 * This adds the given &va to the GPU VA list of the &drm_gpuvm_bo and the1820 * &drm_gpuvm_bo to the &drm_gem_object it is associated with.1821 *1822 * For every &drm_gpuva entry added to the &drm_gpuvm_bo an additional1823 * reference of the latter is taken.1824 *1825 * This function expects the caller to protect the GEM's GPUVA list against1826 * concurrent access using either the GEMs dma_resv lock or a driver specific1827 * lock set through drm_gem_gpuva_set_lock().1828 */1829void1830drm_gpuva_link(struct drm_gpuva *va, struct drm_gpuvm_bo *vm_bo)1831{1832 struct drm_gem_object *obj = va->gem.obj;1833 struct drm_gpuvm *gpuvm = va->vm;1834 1835 if (unlikely(!obj))1836 return;1837 1838 drm_WARN_ON(gpuvm->drm, obj != vm_bo->obj);1839 1840 va->vm_bo = drm_gpuvm_bo_get(vm_bo);1841 1842 drm_gem_gpuva_assert_lock_held(obj);1843 list_add_tail(&va->gem.entry, &vm_bo->list.gpuva);1844}1845EXPORT_SYMBOL_GPL(drm_gpuva_link);1846 1847/**1848 * drm_gpuva_unlink() - unlink a &drm_gpuva1849 * @va: the &drm_gpuva to unlink1850 *1851 * This removes the given &va from the GPU VA list of the &drm_gem_object it is1852 * associated with.1853 *1854 * This removes the given &va from the GPU VA list of the &drm_gpuvm_bo and1855 * the &drm_gpuvm_bo from the &drm_gem_object it is associated with in case1856 * this call unlinks the last &drm_gpuva from the &drm_gpuvm_bo.1857 *1858 * For every &drm_gpuva entry removed from the &drm_gpuvm_bo a reference of1859 * the latter is dropped.1860 *1861 * This function expects the caller to protect the GEM's GPUVA list against1862 * concurrent access using either the GEMs dma_resv lock or a driver specific1863 * lock set through drm_gem_gpuva_set_lock().1864 */1865void1866drm_gpuva_unlink(struct drm_gpuva *va)1867{1868 struct drm_gem_object *obj = va->gem.obj;1869 struct drm_gpuvm_bo *vm_bo = va->vm_bo;1870 1871 if (unlikely(!obj))1872 return;1873 1874 drm_gem_gpuva_assert_lock_held(obj);1875 list_del_init(&va->gem.entry);1876 1877 va->vm_bo = NULL;1878 drm_gpuvm_bo_put(vm_bo);1879}1880EXPORT_SYMBOL_GPL(drm_gpuva_unlink);1881 1882/**1883 * drm_gpuva_find_first() - find the first &drm_gpuva in the given range1884 * @gpuvm: the &drm_gpuvm to search in1885 * @addr: the &drm_gpuvas address1886 * @range: the &drm_gpuvas range1887 *1888 * Returns: the first &drm_gpuva within the given range1889 */1890struct drm_gpuva *1891drm_gpuva_find_first(struct drm_gpuvm *gpuvm,1892 u64 addr, u64 range)1893{1894 u64 last = addr + range - 1;1895 1896 return drm_gpuva_it_iter_first(&gpuvm->rb.tree, addr, last);1897}1898EXPORT_SYMBOL_GPL(drm_gpuva_find_first);1899 1900/**1901 * drm_gpuva_find() - find a &drm_gpuva1902 * @gpuvm: the &drm_gpuvm to search in1903 * @addr: the &drm_gpuvas address1904 * @range: the &drm_gpuvas range1905 *1906 * Returns: the &drm_gpuva at a given &addr and with a given &range1907 */1908struct drm_gpuva *1909drm_gpuva_find(struct drm_gpuvm *gpuvm,1910 u64 addr, u64 range)1911{1912 struct drm_gpuva *va;1913 1914 va = drm_gpuva_find_first(gpuvm, addr, range);1915 if (!va)1916 goto out;1917 1918 if (va->va.addr != addr ||1919 va->va.range != range)1920 goto out;1921 1922 return va;1923 1924out:1925 return NULL;1926}1927EXPORT_SYMBOL_GPL(drm_gpuva_find);1928 1929/**1930 * drm_gpuva_find_prev() - find the &drm_gpuva before the given address1931 * @gpuvm: the &drm_gpuvm to search in1932 * @start: the given GPU VA's start address1933 *1934 * Find the adjacent &drm_gpuva before the GPU VA with given &start address.1935 *1936 * Note that if there is any free space between the GPU VA mappings no mapping1937 * is returned.1938 *1939 * Returns: a pointer to the found &drm_gpuva or NULL if none was found1940 */1941struct drm_gpuva *1942drm_gpuva_find_prev(struct drm_gpuvm *gpuvm, u64 start)1943{1944 if (!drm_gpuvm_range_valid(gpuvm, start - 1, 1))1945 return NULL;1946 1947 return drm_gpuva_it_iter_first(&gpuvm->rb.tree, start - 1, start);1948}1949EXPORT_SYMBOL_GPL(drm_gpuva_find_prev);1950 1951/**1952 * drm_gpuva_find_next() - find the &drm_gpuva after the given address1953 * @gpuvm: the &drm_gpuvm to search in1954 * @end: the given GPU VA's end address1955 *1956 * Find the adjacent &drm_gpuva after the GPU VA with given &end address.1957 *1958 * Note that if there is any free space between the GPU VA mappings no mapping1959 * is returned.1960 *1961 * Returns: a pointer to the found &drm_gpuva or NULL if none was found1962 */1963struct drm_gpuva *1964drm_gpuva_find_next(struct drm_gpuvm *gpuvm, u64 end)1965{1966 if (!drm_gpuvm_range_valid(gpuvm, end, 1))1967 return NULL;1968 1969 return drm_gpuva_it_iter_first(&gpuvm->rb.tree, end, end + 1);1970}1971EXPORT_SYMBOL_GPL(drm_gpuva_find_next);1972 1973/**1974 * drm_gpuvm_interval_empty() - indicate whether a given interval of the VA space1975 * is empty1976 * @gpuvm: the &drm_gpuvm to check the range for1977 * @addr: the start address of the range1978 * @range: the range of the interval1979 *1980 * Returns: true if the interval is empty, false otherwise1981 */1982bool1983drm_gpuvm_interval_empty(struct drm_gpuvm *gpuvm, u64 addr, u64 range)1984{1985 return !drm_gpuva_find_first(gpuvm, addr, range);1986}1987EXPORT_SYMBOL_GPL(drm_gpuvm_interval_empty);1988 1989/**1990 * drm_gpuva_map() - helper to insert a &drm_gpuva according to a1991 * &drm_gpuva_op_map1992 * @gpuvm: the &drm_gpuvm1993 * @va: the &drm_gpuva to insert1994 * @op: the &drm_gpuva_op_map to initialize @va with1995 *1996 * Initializes the @va from the @op and inserts it into the given @gpuvm.1997 */1998void1999drm_gpuva_map(struct drm_gpuvm *gpuvm,2000 struct drm_gpuva *va,2001 struct drm_gpuva_op_map *op)2002{2003 drm_gpuva_init_from_op(va, op);2004 drm_gpuva_insert(gpuvm, va);2005}2006EXPORT_SYMBOL_GPL(drm_gpuva_map);2007 2008/**2009 * drm_gpuva_remap() - helper to remap a &drm_gpuva according to a2010 * &drm_gpuva_op_remap2011 * @prev: the &drm_gpuva to remap when keeping the start of a mapping2012 * @next: the &drm_gpuva to remap when keeping the end of a mapping2013 * @op: the &drm_gpuva_op_remap to initialize @prev and @next with2014 *2015 * Removes the currently mapped &drm_gpuva and remaps it using @prev and/or2016 * @next.2017 */2018void2019drm_gpuva_remap(struct drm_gpuva *prev,2020 struct drm_gpuva *next,2021 struct drm_gpuva_op_remap *op)2022{2023 struct drm_gpuva *va = op->unmap->va;2024 struct drm_gpuvm *gpuvm = va->vm;2025 2026 drm_gpuva_remove(va);2027 2028 if (op->prev) {2029 drm_gpuva_init_from_op(prev, op->prev);2030 drm_gpuva_insert(gpuvm, prev);2031 }2032 2033 if (op->next) {2034 drm_gpuva_init_from_op(next, op->next);2035 drm_gpuva_insert(gpuvm, next);2036 }2037}2038EXPORT_SYMBOL_GPL(drm_gpuva_remap);2039 2040/**2041 * drm_gpuva_unmap() - helper to remove a &drm_gpuva according to a2042 * &drm_gpuva_op_unmap2043 * @op: the &drm_gpuva_op_unmap specifying the &drm_gpuva to remove2044 *2045 * Removes the &drm_gpuva associated with the &drm_gpuva_op_unmap.2046 */2047void2048drm_gpuva_unmap(struct drm_gpuva_op_unmap *op)2049{2050 drm_gpuva_remove(op->va);2051}2052EXPORT_SYMBOL_GPL(drm_gpuva_unmap);2053 2054static int2055op_map_cb(const struct drm_gpuvm_ops *fn, void *priv,2056 u64 addr, u64 range,2057 struct drm_gem_object *obj, u64 offset)2058{2059 struct drm_gpuva_op op = {};2060 2061 op.op = DRM_GPUVA_OP_MAP;2062 op.map.va.addr = addr;2063 op.map.va.range = range;2064 op.map.gem.obj = obj;2065 op.map.gem.offset = offset;2066 2067 return fn->sm_step_map(&op, priv);2068}2069 2070static int2071op_remap_cb(const struct drm_gpuvm_ops *fn, void *priv,2072 struct drm_gpuva_op_map *prev,2073 struct drm_gpuva_op_map *next,2074 struct drm_gpuva_op_unmap *unmap)2075{2076 struct drm_gpuva_op op = {};2077 struct drm_gpuva_op_remap *r;2078 2079 op.op = DRM_GPUVA_OP_REMAP;2080 r = &op.remap;2081 r->prev = prev;2082 r->next = next;2083 r->unmap = unmap;2084 2085 return fn->sm_step_remap(&op, priv);2086}2087 2088static int2089op_unmap_cb(const struct drm_gpuvm_ops *fn, void *priv,2090 struct drm_gpuva *va, bool merge)2091{2092 struct drm_gpuva_op op = {};2093 2094 op.op = DRM_GPUVA_OP_UNMAP;2095 op.unmap.va = va;2096 op.unmap.keep = merge;2097 2098 return fn->sm_step_unmap(&op, priv);2099}2100 2101static int2102__drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,2103 const struct drm_gpuvm_ops *ops, void *priv,2104 u64 req_addr, u64 req_range,2105 struct drm_gem_object *req_obj, u64 req_offset)2106{2107 struct drm_gpuva *va, *next;2108 u64 req_end = req_addr + req_range;2109 int ret;2110 2111 if (unlikely(!drm_gpuvm_range_valid(gpuvm, req_addr, req_range)))2112 return -EINVAL;2113 2114 drm_gpuvm_for_each_va_range_safe(va, next, gpuvm, req_addr, req_end) {2115 struct drm_gem_object *obj = va->gem.obj;2116 u64 offset = va->gem.offset;2117 u64 addr = va->va.addr;2118 u64 range = va->va.range;2119 u64 end = addr + range;2120 bool merge = !!va->gem.obj;2121 2122 if (addr == req_addr) {2123 merge &= obj == req_obj &&2124 offset == req_offset;2125 2126 if (end == req_end) {2127 ret = op_unmap_cb(ops, priv, va, merge);2128 if (ret)2129 return ret;2130 break;2131 }2132 2133 if (end < req_end) {2134 ret = op_unmap_cb(ops, priv, va, merge);2135 if (ret)2136 return ret;2137 continue;2138 }2139 2140 if (end > req_end) {2141 struct drm_gpuva_op_map n = {2142 .va.addr = req_end,2143 .va.range = range - req_range,2144 .gem.obj = obj,2145 .gem.offset = offset + req_range,2146 };2147 struct drm_gpuva_op_unmap u = {2148 .va = va,2149 .keep = merge,2150 };2151 2152 ret = op_remap_cb(ops, priv, NULL, &n, &u);2153 if (ret)2154 return ret;2155 break;2156 }2157 } else if (addr < req_addr) {2158 u64 ls_range = req_addr - addr;2159 struct drm_gpuva_op_map p = {2160 .va.addr = addr,2161 .va.range = ls_range,2162 .gem.obj = obj,2163 .gem.offset = offset,2164 };2165 struct drm_gpuva_op_unmap u = { .va = va };2166 2167 merge &= obj == req_obj &&2168 offset + ls_range == req_offset;2169 u.keep = merge;2170 2171 if (end == req_end) {2172 ret = op_remap_cb(ops, priv, &p, NULL, &u);2173 if (ret)2174 return ret;2175 break;2176 }2177 2178 if (end < req_end) {2179 ret = op_remap_cb(ops, priv, &p, NULL, &u);2180 if (ret)2181 return ret;2182 continue;2183 }2184 2185 if (end > req_end) {2186 struct drm_gpuva_op_map n = {2187 .va.addr = req_end,2188 .va.range = end - req_end,2189 .gem.obj = obj,2190 .gem.offset = offset + ls_range +2191 req_range,2192 };2193 2194 ret = op_remap_cb(ops, priv, &p, &n, &u);2195 if (ret)2196 return ret;2197 break;2198 }2199 } else if (addr > req_addr) {2200 merge &= obj == req_obj &&2201 offset == req_offset +2202 (addr - req_addr);2203 2204 if (end == req_end) {2205 ret = op_unmap_cb(ops, priv, va, merge);2206 if (ret)2207 return ret;2208 break;2209 }2210 2211 if (end < req_end) {2212 ret = op_unmap_cb(ops, priv, va, merge);2213 if (ret)2214 return ret;2215 continue;2216 }2217 2218 if (end > req_end) {2219 struct drm_gpuva_op_map n = {2220 .va.addr = req_end,2221 .va.range = end - req_end,2222 .gem.obj = obj,2223 .gem.offset = offset + req_end - addr,2224 };2225 struct drm_gpuva_op_unmap u = {2226 .va = va,2227 .keep = merge,2228 };2229 2230 ret = op_remap_cb(ops, priv, NULL, &n, &u);2231 if (ret)2232 return ret;2233 break;2234 }2235 }2236 }2237 2238 return op_map_cb(ops, priv,2239 req_addr, req_range,2240 req_obj, req_offset);2241}2242 2243static int2244__drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm,2245 const struct drm_gpuvm_ops *ops, void *priv,2246 u64 req_addr, u64 req_range)2247{2248 struct drm_gpuva *va, *next;2249 u64 req_end = req_addr + req_range;2250 int ret;2251 2252 if (unlikely(!drm_gpuvm_range_valid(gpuvm, req_addr, req_range)))2253 return -EINVAL;2254 2255 drm_gpuvm_for_each_va_range_safe(va, next, gpuvm, req_addr, req_end) {2256 struct drm_gpuva_op_map prev = {}, next = {};2257 bool prev_split = false, next_split = false;2258 struct drm_gem_object *obj = va->gem.obj;2259 u64 offset = va->gem.offset;2260 u64 addr = va->va.addr;2261 u64 range = va->va.range;2262 u64 end = addr + range;2263 2264 if (addr < req_addr) {2265 prev.va.addr = addr;2266 prev.va.range = req_addr - addr;2267 prev.gem.obj = obj;2268 prev.gem.offset = offset;2269 2270 prev_split = true;2271 }2272 2273 if (end > req_end) {2274 next.va.addr = req_end;2275 next.va.range = end - req_end;2276 next.gem.obj = obj;2277 next.gem.offset = offset + (req_end - addr);2278 2279 next_split = true;2280 }2281 2282 if (prev_split || next_split) {2283 struct drm_gpuva_op_unmap unmap = { .va = va };2284 2285 ret = op_remap_cb(ops, priv,2286 prev_split ? &prev : NULL,2287 next_split ? &next : NULL,2288 &unmap);2289 if (ret)2290 return ret;2291 } else {2292 ret = op_unmap_cb(ops, priv, va, false);2293 if (ret)2294 return ret;2295 }2296 }2297 2298 return 0;2299}2300 2301/**2302 * drm_gpuvm_sm_map() - creates the &drm_gpuva_op split/merge steps2303 * @gpuvm: the &drm_gpuvm representing the GPU VA space2304 * @req_addr: the start address of the new mapping2305 * @req_range: the range of the new mapping2306 * @req_obj: the &drm_gem_object to map2307 * @req_offset: the offset within the &drm_gem_object2308 * @priv: pointer to a driver private data structure2309 *2310 * This function iterates the given range of the GPU VA space. It utilizes the2311 * &drm_gpuvm_ops to call back into the driver providing the split and merge2312 * steps.2313 *2314 * Drivers may use these callbacks to update the GPU VA space right away within2315 * the callback. In case the driver decides to copy and store the operations for2316 * later processing neither this function nor &drm_gpuvm_sm_unmap is allowed to2317 * be called before the &drm_gpuvm's view of the GPU VA space was2318 * updated with the previous set of operations. To update the2319 * &drm_gpuvm's view of the GPU VA space drm_gpuva_insert(),2320 * drm_gpuva_destroy_locked() and/or drm_gpuva_destroy_unlocked() should be2321 * used.2322 *2323 * A sequence of callbacks can contain map, unmap and remap operations, but2324 * the sequence of callbacks might also be empty if no operation is required,2325 * e.g. if the requested mapping already exists in the exact same way.2326 *2327 * There can be an arbitrary amount of unmap operations, a maximum of two remap2328 * operations and a single map operation. The latter one represents the original2329 * map operation requested by the caller.2330 *2331 * Returns: 0 on success or a negative error code2332 */2333int2334drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm, void *priv,2335 u64 req_addr, u64 req_range,2336 struct drm_gem_object *req_obj, u64 req_offset)2337{2338 const struct drm_gpuvm_ops *ops = gpuvm->ops;2339 2340 if (unlikely(!(ops && ops->sm_step_map &&2341 ops->sm_step_remap &&2342 ops->sm_step_unmap)))2343 return -EINVAL;2344 2345 return __drm_gpuvm_sm_map(gpuvm, ops, priv,2346 req_addr, req_range,2347 req_obj, req_offset);2348}2349EXPORT_SYMBOL_GPL(drm_gpuvm_sm_map);2350 2351/**2352 * drm_gpuvm_sm_unmap() - creates the &drm_gpuva_ops to split on unmap2353 * @gpuvm: the &drm_gpuvm representing the GPU VA space2354 * @priv: pointer to a driver private data structure2355 * @req_addr: the start address of the range to unmap2356 * @req_range: the range of the mappings to unmap2357 *2358 * This function iterates the given range of the GPU VA space. It utilizes the2359 * &drm_gpuvm_ops to call back into the driver providing the operations to2360 * unmap and, if required, split existent mappings.2361 *2362 * Drivers may use these callbacks to update the GPU VA space right away within2363 * the callback. In case the driver decides to copy and store the operations for2364 * later processing neither this function nor &drm_gpuvm_sm_map is allowed to be2365 * called before the &drm_gpuvm's view of the GPU VA space was updated2366 * with the previous set of operations. To update the &drm_gpuvm's view2367 * of the GPU VA space drm_gpuva_insert(), drm_gpuva_destroy_locked() and/or2368 * drm_gpuva_destroy_unlocked() should be used.2369 *2370 * A sequence of callbacks can contain unmap and remap operations, depending on2371 * whether there are actual overlapping mappings to split.2372 *2373 * There can be an arbitrary amount of unmap operations and a maximum of two2374 * remap operations.2375 *2376 * Returns: 0 on success or a negative error code2377 */2378int2379drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm, void *priv,2380 u64 req_addr, u64 req_range)2381{2382 const struct drm_gpuvm_ops *ops = gpuvm->ops;2383 2384 if (unlikely(!(ops && ops->sm_step_remap &&2385 ops->sm_step_unmap)))2386 return -EINVAL;2387 2388 return __drm_gpuvm_sm_unmap(gpuvm, ops, priv,2389 req_addr, req_range);2390}2391EXPORT_SYMBOL_GPL(drm_gpuvm_sm_unmap);2392 2393static struct drm_gpuva_op *2394gpuva_op_alloc(struct drm_gpuvm *gpuvm)2395{2396 const struct drm_gpuvm_ops *fn = gpuvm->ops;2397 struct drm_gpuva_op *op;2398 2399 if (fn && fn->op_alloc)2400 op = fn->op_alloc();2401 else2402 op = kzalloc(sizeof(*op), GFP_KERNEL);2403 2404 if (unlikely(!op))2405 return NULL;2406 2407 return op;2408}2409 2410static void2411gpuva_op_free(struct drm_gpuvm *gpuvm,2412 struct drm_gpuva_op *op)2413{2414 const struct drm_gpuvm_ops *fn = gpuvm->ops;2415 2416 if (fn && fn->op_free)2417 fn->op_free(op);2418 else2419 kfree(op);2420}2421 2422static int2423drm_gpuva_sm_step(struct drm_gpuva_op *__op,2424 void *priv)2425{2426 struct {2427 struct drm_gpuvm *vm;2428 struct drm_gpuva_ops *ops;2429 } *args = priv;2430 struct drm_gpuvm *gpuvm = args->vm;2431 struct drm_gpuva_ops *ops = args->ops;2432 struct drm_gpuva_op *op;2433 2434 op = gpuva_op_alloc(gpuvm);2435 if (unlikely(!op))2436 goto err;2437 2438 memcpy(op, __op, sizeof(*op));2439 2440 if (op->op == DRM_GPUVA_OP_REMAP) {2441 struct drm_gpuva_op_remap *__r = &__op->remap;2442 struct drm_gpuva_op_remap *r = &op->remap;2443 2444 r->unmap = kmemdup(__r->unmap, sizeof(*r->unmap),2445 GFP_KERNEL);2446 if (unlikely(!r->unmap))2447 goto err_free_op;2448 2449 if (__r->prev) {2450 r->prev = kmemdup(__r->prev, sizeof(*r->prev),2451 GFP_KERNEL);2452 if (unlikely(!r->prev))2453 goto err_free_unmap;2454 }2455 2456 if (__r->next) {2457 r->next = kmemdup(__r->next, sizeof(*r->next),2458 GFP_KERNEL);2459 if (unlikely(!r->next))2460 goto err_free_prev;2461 }2462 }2463 2464 list_add_tail(&op->entry, &ops->list);2465 2466 return 0;2467 2468err_free_unmap:2469 kfree(op->remap.unmap);2470err_free_prev:2471 kfree(op->remap.prev);2472err_free_op:2473 gpuva_op_free(gpuvm, op);2474err:2475 return -ENOMEM;2476}2477 2478static const struct drm_gpuvm_ops gpuvm_list_ops = {2479 .sm_step_map = drm_gpuva_sm_step,2480 .sm_step_remap = drm_gpuva_sm_step,2481 .sm_step_unmap = drm_gpuva_sm_step,2482};2483 2484/**2485 * drm_gpuvm_sm_map_ops_create() - creates the &drm_gpuva_ops to split and merge2486 * @gpuvm: the &drm_gpuvm representing the GPU VA space2487 * @req_addr: the start address of the new mapping2488 * @req_range: the range of the new mapping2489 * @req_obj: the &drm_gem_object to map2490 * @req_offset: the offset within the &drm_gem_object2491 *2492 * This function creates a list of operations to perform splitting and merging2493 * of existent mapping(s) with the newly requested one.2494 *2495 * The list can be iterated with &drm_gpuva_for_each_op and must be processed2496 * in the given order. It can contain map, unmap and remap operations, but it2497 * also can be empty if no operation is required, e.g. if the requested mapping2498 * already exists is the exact same way.2499 *2500 * There can be an arbitrary amount of unmap operations, a maximum of two remap2501 * operations and a single map operation. The latter one represents the original2502 * map operation requested by the caller.2503 *2504 * Note that before calling this function again with another mapping request it2505 * is necessary to update the &drm_gpuvm's view of the GPU VA space. The2506 * previously obtained operations must be either processed or abandoned. To2507 * update the &drm_gpuvm's view of the GPU VA space drm_gpuva_insert(),2508 * drm_gpuva_destroy_locked() and/or drm_gpuva_destroy_unlocked() should be2509 * used.2510 *2511 * After the caller finished processing the returned &drm_gpuva_ops, they must2512 * be freed with &drm_gpuva_ops_free.2513 *2514 * Returns: a pointer to the &drm_gpuva_ops on success, an ERR_PTR on failure2515 */2516struct drm_gpuva_ops *2517drm_gpuvm_sm_map_ops_create(struct drm_gpuvm *gpuvm,2518 u64 req_addr, u64 req_range,2519 struct drm_gem_object *req_obj, u64 req_offset)2520{2521 struct drm_gpuva_ops *ops;2522 struct {2523 struct drm_gpuvm *vm;2524 struct drm_gpuva_ops *ops;2525 } args;2526 int ret;2527 2528 ops = kzalloc(sizeof(*ops), GFP_KERNEL);2529 if (unlikely(!ops))2530 return ERR_PTR(-ENOMEM);2531 2532 INIT_LIST_HEAD(&ops->list);2533 2534 args.vm = gpuvm;2535 args.ops = ops;2536 2537 ret = __drm_gpuvm_sm_map(gpuvm, &gpuvm_list_ops, &args,2538 req_addr, req_range,2539 req_obj, req_offset);2540 if (ret)2541 goto err_free_ops;2542 2543 return ops;2544 2545err_free_ops:2546 drm_gpuva_ops_free(gpuvm, ops);2547 return ERR_PTR(ret);2548}2549EXPORT_SYMBOL_GPL(drm_gpuvm_sm_map_ops_create);2550 2551/**2552 * drm_gpuvm_sm_unmap_ops_create() - creates the &drm_gpuva_ops to split on2553 * unmap2554 * @gpuvm: the &drm_gpuvm representing the GPU VA space2555 * @req_addr: the start address of the range to unmap2556 * @req_range: the range of the mappings to unmap2557 *2558 * This function creates a list of operations to perform unmapping and, if2559 * required, splitting of the mappings overlapping the unmap range.2560 *2561 * The list can be iterated with &drm_gpuva_for_each_op and must be processed2562 * in the given order. It can contain unmap and remap operations, depending on2563 * whether there are actual overlapping mappings to split.2564 *2565 * There can be an arbitrary amount of unmap operations and a maximum of two2566 * remap operations.2567 *2568 * Note that before calling this function again with another range to unmap it2569 * is necessary to update the &drm_gpuvm's view of the GPU VA space. The2570 * previously obtained operations must be processed or abandoned. To update the2571 * &drm_gpuvm's view of the GPU VA space drm_gpuva_insert(),2572 * drm_gpuva_destroy_locked() and/or drm_gpuva_destroy_unlocked() should be2573 * used.2574 *2575 * After the caller finished processing the returned &drm_gpuva_ops, they must2576 * be freed with &drm_gpuva_ops_free.2577 *2578 * Returns: a pointer to the &drm_gpuva_ops on success, an ERR_PTR on failure2579 */2580struct drm_gpuva_ops *2581drm_gpuvm_sm_unmap_ops_create(struct drm_gpuvm *gpuvm,2582 u64 req_addr, u64 req_range)2583{2584 struct drm_gpuva_ops *ops;2585 struct {2586 struct drm_gpuvm *vm;2587 struct drm_gpuva_ops *ops;2588 } args;2589 int ret;2590 2591 ops = kzalloc(sizeof(*ops), GFP_KERNEL);2592 if (unlikely(!ops))2593 return ERR_PTR(-ENOMEM);2594 2595 INIT_LIST_HEAD(&ops->list);2596 2597 args.vm = gpuvm;2598 args.ops = ops;2599 2600 ret = __drm_gpuvm_sm_unmap(gpuvm, &gpuvm_list_ops, &args,2601 req_addr, req_range);2602 if (ret)2603 goto err_free_ops;2604 2605 return ops;2606 2607err_free_ops:2608 drm_gpuva_ops_free(gpuvm, ops);2609 return ERR_PTR(ret);2610}2611EXPORT_SYMBOL_GPL(drm_gpuvm_sm_unmap_ops_create);2612 2613/**2614 * drm_gpuvm_prefetch_ops_create() - creates the &drm_gpuva_ops to prefetch2615 * @gpuvm: the &drm_gpuvm representing the GPU VA space2616 * @addr: the start address of the range to prefetch2617 * @range: the range of the mappings to prefetch2618 *2619 * This function creates a list of operations to perform prefetching.2620 *2621 * The list can be iterated with &drm_gpuva_for_each_op and must be processed2622 * in the given order. It can contain prefetch operations.2623 *2624 * There can be an arbitrary amount of prefetch operations.2625 *2626 * After the caller finished processing the returned &drm_gpuva_ops, they must2627 * be freed with &drm_gpuva_ops_free.2628 *2629 * Returns: a pointer to the &drm_gpuva_ops on success, an ERR_PTR on failure2630 */2631struct drm_gpuva_ops *2632drm_gpuvm_prefetch_ops_create(struct drm_gpuvm *gpuvm,2633 u64 addr, u64 range)2634{2635 struct drm_gpuva_ops *ops;2636 struct drm_gpuva_op *op;2637 struct drm_gpuva *va;2638 u64 end = addr + range;2639 int ret;2640 2641 ops = kzalloc(sizeof(*ops), GFP_KERNEL);2642 if (!ops)2643 return ERR_PTR(-ENOMEM);2644 2645 INIT_LIST_HEAD(&ops->list);2646 2647 drm_gpuvm_for_each_va_range(va, gpuvm, addr, end) {2648 op = gpuva_op_alloc(gpuvm);2649 if (!op) {2650 ret = -ENOMEM;2651 goto err_free_ops;2652 }2653 2654 op->op = DRM_GPUVA_OP_PREFETCH;2655 op->prefetch.va = va;2656 list_add_tail(&op->entry, &ops->list);2657 }2658 2659 return ops;2660 2661err_free_ops:2662 drm_gpuva_ops_free(gpuvm, ops);2663 return ERR_PTR(ret);2664}2665EXPORT_SYMBOL_GPL(drm_gpuvm_prefetch_ops_create);2666 2667/**2668 * drm_gpuvm_bo_unmap_ops_create() - creates the &drm_gpuva_ops to unmap a GEM2669 * @vm_bo: the &drm_gpuvm_bo abstraction2670 *2671 * This function creates a list of operations to perform unmapping for every2672 * GPUVA attached to a GEM.2673 *2674 * The list can be iterated with &drm_gpuva_for_each_op and consists out of an2675 * arbitrary amount of unmap operations.2676 *2677 * After the caller finished processing the returned &drm_gpuva_ops, they must2678 * be freed with &drm_gpuva_ops_free.2679 *2680 * It is the callers responsibility to protect the GEMs GPUVA list against2681 * concurrent access using the GEMs dma_resv lock.2682 *2683 * Returns: a pointer to the &drm_gpuva_ops on success, an ERR_PTR on failure2684 */2685struct drm_gpuva_ops *2686drm_gpuvm_bo_unmap_ops_create(struct drm_gpuvm_bo *vm_bo)2687{2688 struct drm_gpuva_ops *ops;2689 struct drm_gpuva_op *op;2690 struct drm_gpuva *va;2691 int ret;2692 2693 drm_gem_gpuva_assert_lock_held(vm_bo->obj);2694 2695 ops = kzalloc(sizeof(*ops), GFP_KERNEL);2696 if (!ops)2697 return ERR_PTR(-ENOMEM);2698 2699 INIT_LIST_HEAD(&ops->list);2700 2701 drm_gpuvm_bo_for_each_va(va, vm_bo) {2702 op = gpuva_op_alloc(vm_bo->vm);2703 if (!op) {2704 ret = -ENOMEM;2705 goto err_free_ops;2706 }2707 2708 op->op = DRM_GPUVA_OP_UNMAP;2709 op->unmap.va = va;2710 list_add_tail(&op->entry, &ops->list);2711 }2712 2713 return ops;2714 2715err_free_ops:2716 drm_gpuva_ops_free(vm_bo->vm, ops);2717 return ERR_PTR(ret);2718}2719EXPORT_SYMBOL_GPL(drm_gpuvm_bo_unmap_ops_create);2720 2721/**2722 * drm_gpuva_ops_free() - free the given &drm_gpuva_ops2723 * @gpuvm: the &drm_gpuvm the ops were created for2724 * @ops: the &drm_gpuva_ops to free2725 *2726 * Frees the given &drm_gpuva_ops structure including all the ops associated2727 * with it.2728 */2729void2730drm_gpuva_ops_free(struct drm_gpuvm *gpuvm,2731 struct drm_gpuva_ops *ops)2732{2733 struct drm_gpuva_op *op, *next;2734 2735 drm_gpuva_for_each_op_safe(op, next, ops) {2736 list_del(&op->entry);2737 2738 if (op->op == DRM_GPUVA_OP_REMAP) {2739 kfree(op->remap.prev);2740 kfree(op->remap.next);2741 kfree(op->remap.unmap);2742 }2743 2744 gpuva_op_free(gpuvm, op);2745 }2746 2747 kfree(ops);2748}2749EXPORT_SYMBOL_GPL(drm_gpuva_ops_free);2750 2751MODULE_DESCRIPTION("DRM GPUVM");2752MODULE_LICENSE("GPL");2753