162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2022 Red Hat.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
662306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
762306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
862306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
962306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
1062306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
1362306a36Sopenharmony_ci * all copies or substantial portions of the Software.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1862306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
1962306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2062306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2162306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Authors:
2462306a36Sopenharmony_ci *     Danilo Krummrich <dakr@redhat.com>
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <drm/drm_gpuva_mgr.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <linux/interval_tree_generic.h>
3162306a36Sopenharmony_ci#include <linux/mm.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/**
3462306a36Sopenharmony_ci * DOC: Overview
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * The DRM GPU VA Manager, represented by struct drm_gpuva_manager keeps track
3762306a36Sopenharmony_ci * of a GPU's virtual address (VA) space and manages the corresponding virtual
3862306a36Sopenharmony_ci * mappings represented by &drm_gpuva objects. It also keeps track of the
3962306a36Sopenharmony_ci * mapping's backing &drm_gem_object buffers.
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci * &drm_gem_object buffers maintain a list of &drm_gpuva objects representing
4262306a36Sopenharmony_ci * all existent GPU VA mappings using this &drm_gem_object as backing buffer.
4362306a36Sopenharmony_ci *
4462306a36Sopenharmony_ci * GPU VAs can be flagged as sparse, such that drivers may use GPU VAs to also
4562306a36Sopenharmony_ci * keep track of sparse PTEs in order to support Vulkan 'Sparse Resources'.
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci * The GPU VA manager internally uses a rb-tree to manage the
4862306a36Sopenharmony_ci * &drm_gpuva mappings within a GPU's virtual address space.
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * The &drm_gpuva_manager contains a special &drm_gpuva representing the
5162306a36Sopenharmony_ci * portion of VA space reserved by the kernel. This node is initialized together
5262306a36Sopenharmony_ci * with the GPU VA manager instance and removed when the GPU VA manager is
5362306a36Sopenharmony_ci * destroyed.
5462306a36Sopenharmony_ci *
5562306a36Sopenharmony_ci * In a typical application drivers would embed struct drm_gpuva_manager and
5662306a36Sopenharmony_ci * struct drm_gpuva within their own driver specific structures, there won't be
5762306a36Sopenharmony_ci * any memory allocations of its own nor memory allocations of &drm_gpuva
5862306a36Sopenharmony_ci * entries.
5962306a36Sopenharmony_ci *
6062306a36Sopenharmony_ci * The data structures needed to store &drm_gpuvas within the &drm_gpuva_manager
6162306a36Sopenharmony_ci * are contained within struct drm_gpuva already. Hence, for inserting
6262306a36Sopenharmony_ci * &drm_gpuva entries from within dma-fence signalling critical sections it is
6362306a36Sopenharmony_ci * enough to pre-allocate the &drm_gpuva structures.
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/**
6762306a36Sopenharmony_ci * DOC: Split and Merge
6862306a36Sopenharmony_ci *
6962306a36Sopenharmony_ci * Besides its capability to manage and represent a GPU VA space, the
7062306a36Sopenharmony_ci * &drm_gpuva_manager also provides functions to let the &drm_gpuva_manager
7162306a36Sopenharmony_ci * calculate a sequence of operations to satisfy a given map or unmap request.
7262306a36Sopenharmony_ci *
7362306a36Sopenharmony_ci * Therefore the DRM GPU VA manager provides an algorithm implementing splitting
7462306a36Sopenharmony_ci * and merging of existent GPU VA mappings with the ones that are requested to
7562306a36Sopenharmony_ci * be mapped or unmapped. This feature is required by the Vulkan API to
7662306a36Sopenharmony_ci * implement Vulkan 'Sparse Memory Bindings' - drivers UAPIs often refer to this
7762306a36Sopenharmony_ci * as VM BIND.
7862306a36Sopenharmony_ci *
7962306a36Sopenharmony_ci * Drivers can call drm_gpuva_sm_map() to receive a sequence of callbacks
8062306a36Sopenharmony_ci * containing map, unmap and remap operations for a given newly requested
8162306a36Sopenharmony_ci * mapping. The sequence of callbacks represents the set of operations to
8262306a36Sopenharmony_ci * execute in order to integrate the new mapping cleanly into the current state
8362306a36Sopenharmony_ci * of the GPU VA space.
8462306a36Sopenharmony_ci *
8562306a36Sopenharmony_ci * Depending on how the new GPU VA mapping intersects with the existent mappings
8662306a36Sopenharmony_ci * of the GPU VA space the &drm_gpuva_fn_ops callbacks contain an arbitrary
8762306a36Sopenharmony_ci * amount of unmap operations, a maximum of two remap operations and a single
8862306a36Sopenharmony_ci * map operation. The caller might receive no callback at all if no operation is
8962306a36Sopenharmony_ci * required, e.g. if the requested mapping already exists in the exact same way.
9062306a36Sopenharmony_ci *
9162306a36Sopenharmony_ci * The single map operation represents the original map operation requested by
9262306a36Sopenharmony_ci * the caller.
9362306a36Sopenharmony_ci *
9462306a36Sopenharmony_ci * &drm_gpuva_op_unmap contains a 'keep' field, which indicates whether the
9562306a36Sopenharmony_ci * &drm_gpuva to unmap is physically contiguous with the original mapping
9662306a36Sopenharmony_ci * request. Optionally, if 'keep' is set, drivers may keep the actual page table
9762306a36Sopenharmony_ci * entries for this &drm_gpuva, adding the missing page table entries only and
9862306a36Sopenharmony_ci * update the &drm_gpuva_manager's view of things accordingly.
9962306a36Sopenharmony_ci *
10062306a36Sopenharmony_ci * Drivers may do the same optimization, namely delta page table updates, also
10162306a36Sopenharmony_ci * for remap operations. This is possible since &drm_gpuva_op_remap consists of
10262306a36Sopenharmony_ci * one unmap operation and one or two map operations, such that drivers can
10362306a36Sopenharmony_ci * derive the page table update delta accordingly.
10462306a36Sopenharmony_ci *
10562306a36Sopenharmony_ci * Note that there can't be more than two existent mappings to split up, one at
10662306a36Sopenharmony_ci * the beginning and one at the end of the new mapping, hence there is a
10762306a36Sopenharmony_ci * maximum of two remap operations.
10862306a36Sopenharmony_ci *
10962306a36Sopenharmony_ci * Analogous to drm_gpuva_sm_map() drm_gpuva_sm_unmap() uses &drm_gpuva_fn_ops
11062306a36Sopenharmony_ci * to call back into the driver in order to unmap a range of GPU VA space. The
11162306a36Sopenharmony_ci * logic behind this function is way simpler though: For all existent mappings
11262306a36Sopenharmony_ci * enclosed by the given range unmap operations are created. For mappings which
11362306a36Sopenharmony_ci * are only partically located within the given range, remap operations are
11462306a36Sopenharmony_ci * created such that those mappings are split up and re-mapped partically.
11562306a36Sopenharmony_ci *
11662306a36Sopenharmony_ci * As an alternative to drm_gpuva_sm_map() and drm_gpuva_sm_unmap(),
11762306a36Sopenharmony_ci * drm_gpuva_sm_map_ops_create() and drm_gpuva_sm_unmap_ops_create() can be used
11862306a36Sopenharmony_ci * to directly obtain an instance of struct drm_gpuva_ops containing a list of
11962306a36Sopenharmony_ci * &drm_gpuva_op, which can be iterated with drm_gpuva_for_each_op(). This list
12062306a36Sopenharmony_ci * contains the &drm_gpuva_ops analogous to the callbacks one would receive when
12162306a36Sopenharmony_ci * calling drm_gpuva_sm_map() or drm_gpuva_sm_unmap(). While this way requires
12262306a36Sopenharmony_ci * more memory (to allocate the &drm_gpuva_ops), it provides drivers a way to
12362306a36Sopenharmony_ci * iterate the &drm_gpuva_op multiple times, e.g. once in a context where memory
12462306a36Sopenharmony_ci * allocations are possible (e.g. to allocate GPU page tables) and once in the
12562306a36Sopenharmony_ci * dma-fence signalling critical path.
12662306a36Sopenharmony_ci *
12762306a36Sopenharmony_ci * To update the &drm_gpuva_manager's view of the GPU VA space
12862306a36Sopenharmony_ci * drm_gpuva_insert() and drm_gpuva_remove() may be used. These functions can
12962306a36Sopenharmony_ci * safely be used from &drm_gpuva_fn_ops callbacks originating from
13062306a36Sopenharmony_ci * drm_gpuva_sm_map() or drm_gpuva_sm_unmap(). However, it might be more
13162306a36Sopenharmony_ci * convenient to use the provided helper functions drm_gpuva_map(),
13262306a36Sopenharmony_ci * drm_gpuva_remap() and drm_gpuva_unmap() instead.
13362306a36Sopenharmony_ci *
13462306a36Sopenharmony_ci * The following diagram depicts the basic relationships of existent GPU VA
13562306a36Sopenharmony_ci * mappings, a newly requested mapping and the resulting mappings as implemented
13662306a36Sopenharmony_ci * by drm_gpuva_sm_map() - it doesn't cover any arbitrary combinations of these.
13762306a36Sopenharmony_ci *
13862306a36Sopenharmony_ci * 1) Requested mapping is identical. Replace it, but indicate the backing PTEs
13962306a36Sopenharmony_ci *    could be kept.
14062306a36Sopenharmony_ci *
14162306a36Sopenharmony_ci *    ::
14262306a36Sopenharmony_ci *
14362306a36Sopenharmony_ci *	     0     a     1
14462306a36Sopenharmony_ci *	old: |-----------| (bo_offset=n)
14562306a36Sopenharmony_ci *
14662306a36Sopenharmony_ci *	     0     a     1
14762306a36Sopenharmony_ci *	req: |-----------| (bo_offset=n)
14862306a36Sopenharmony_ci *
14962306a36Sopenharmony_ci *	     0     a     1
15062306a36Sopenharmony_ci *	new: |-----------| (bo_offset=n)
15162306a36Sopenharmony_ci *
15262306a36Sopenharmony_ci *
15362306a36Sopenharmony_ci * 2) Requested mapping is identical, except for the BO offset, hence replace
15462306a36Sopenharmony_ci *    the mapping.
15562306a36Sopenharmony_ci *
15662306a36Sopenharmony_ci *    ::
15762306a36Sopenharmony_ci *
15862306a36Sopenharmony_ci *	     0     a     1
15962306a36Sopenharmony_ci *	old: |-----------| (bo_offset=n)
16062306a36Sopenharmony_ci *
16162306a36Sopenharmony_ci *	     0     a     1
16262306a36Sopenharmony_ci *	req: |-----------| (bo_offset=m)
16362306a36Sopenharmony_ci *
16462306a36Sopenharmony_ci *	     0     a     1
16562306a36Sopenharmony_ci *	new: |-----------| (bo_offset=m)
16662306a36Sopenharmony_ci *
16762306a36Sopenharmony_ci *
16862306a36Sopenharmony_ci * 3) Requested mapping is identical, except for the backing BO, hence replace
16962306a36Sopenharmony_ci *    the mapping.
17062306a36Sopenharmony_ci *
17162306a36Sopenharmony_ci *    ::
17262306a36Sopenharmony_ci *
17362306a36Sopenharmony_ci *	     0     a     1
17462306a36Sopenharmony_ci *	old: |-----------| (bo_offset=n)
17562306a36Sopenharmony_ci *
17662306a36Sopenharmony_ci *	     0     b     1
17762306a36Sopenharmony_ci *	req: |-----------| (bo_offset=n)
17862306a36Sopenharmony_ci *
17962306a36Sopenharmony_ci *	     0     b     1
18062306a36Sopenharmony_ci *	new: |-----------| (bo_offset=n)
18162306a36Sopenharmony_ci *
18262306a36Sopenharmony_ci *
18362306a36Sopenharmony_ci * 4) Existent mapping is a left aligned subset of the requested one, hence
18462306a36Sopenharmony_ci *    replace the existent one.
18562306a36Sopenharmony_ci *
18662306a36Sopenharmony_ci *    ::
18762306a36Sopenharmony_ci *
18862306a36Sopenharmony_ci *	     0  a  1
18962306a36Sopenharmony_ci *	old: |-----|       (bo_offset=n)
19062306a36Sopenharmony_ci *
19162306a36Sopenharmony_ci *	     0     a     2
19262306a36Sopenharmony_ci *	req: |-----------| (bo_offset=n)
19362306a36Sopenharmony_ci *
19462306a36Sopenharmony_ci *	     0     a     2
19562306a36Sopenharmony_ci *	new: |-----------| (bo_offset=n)
19662306a36Sopenharmony_ci *
19762306a36Sopenharmony_ci *    .. note::
19862306a36Sopenharmony_ci *       We expect to see the same result for a request with a different BO
19962306a36Sopenharmony_ci *       and/or non-contiguous BO offset.
20062306a36Sopenharmony_ci *
20162306a36Sopenharmony_ci *
20262306a36Sopenharmony_ci * 5) Requested mapping's range is a left aligned subset of the existent one,
20362306a36Sopenharmony_ci *    but backed by a different BO. Hence, map the requested mapping and split
20462306a36Sopenharmony_ci *    the existent one adjusting its BO offset.
20562306a36Sopenharmony_ci *
20662306a36Sopenharmony_ci *    ::
20762306a36Sopenharmony_ci *
20862306a36Sopenharmony_ci *	     0     a     2
20962306a36Sopenharmony_ci *	old: |-----------| (bo_offset=n)
21062306a36Sopenharmony_ci *
21162306a36Sopenharmony_ci *	     0  b  1
21262306a36Sopenharmony_ci *	req: |-----|       (bo_offset=n)
21362306a36Sopenharmony_ci *
21462306a36Sopenharmony_ci *	     0  b  1  a' 2
21562306a36Sopenharmony_ci *	new: |-----|-----| (b.bo_offset=n, a.bo_offset=n+1)
21662306a36Sopenharmony_ci *
21762306a36Sopenharmony_ci *    .. note::
21862306a36Sopenharmony_ci *       We expect to see the same result for a request with a different BO
21962306a36Sopenharmony_ci *       and/or non-contiguous BO offset.
22062306a36Sopenharmony_ci *
22162306a36Sopenharmony_ci *
22262306a36Sopenharmony_ci * 6) Existent mapping is a superset of the requested mapping. Split it up, but
22362306a36Sopenharmony_ci *    indicate that the backing PTEs could be kept.
22462306a36Sopenharmony_ci *
22562306a36Sopenharmony_ci *    ::
22662306a36Sopenharmony_ci *
22762306a36Sopenharmony_ci *	     0     a     2
22862306a36Sopenharmony_ci *	old: |-----------| (bo_offset=n)
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci *	     0  a  1
23162306a36Sopenharmony_ci *	req: |-----|       (bo_offset=n)
23262306a36Sopenharmony_ci *
23362306a36Sopenharmony_ci *	     0  a  1  a' 2
23462306a36Sopenharmony_ci *	new: |-----|-----| (a.bo_offset=n, a'.bo_offset=n+1)
23562306a36Sopenharmony_ci *
23662306a36Sopenharmony_ci *
23762306a36Sopenharmony_ci * 7) Requested mapping's range is a right aligned subset of the existent one,
23862306a36Sopenharmony_ci *    but backed by a different BO. Hence, map the requested mapping and split
23962306a36Sopenharmony_ci *    the existent one, without adjusting the BO offset.
24062306a36Sopenharmony_ci *
24162306a36Sopenharmony_ci *    ::
24262306a36Sopenharmony_ci *
24362306a36Sopenharmony_ci *	     0     a     2
24462306a36Sopenharmony_ci *	old: |-----------| (bo_offset=n)
24562306a36Sopenharmony_ci *
24662306a36Sopenharmony_ci *	           1  b  2
24762306a36Sopenharmony_ci *	req:       |-----| (bo_offset=m)
24862306a36Sopenharmony_ci *
24962306a36Sopenharmony_ci *	     0  a  1  b  2
25062306a36Sopenharmony_ci *	new: |-----|-----| (a.bo_offset=n,b.bo_offset=m)
25162306a36Sopenharmony_ci *
25262306a36Sopenharmony_ci *
25362306a36Sopenharmony_ci * 8) Existent mapping is a superset of the requested mapping. Split it up, but
25462306a36Sopenharmony_ci *    indicate that the backing PTEs could be kept.
25562306a36Sopenharmony_ci *
25662306a36Sopenharmony_ci *    ::
25762306a36Sopenharmony_ci *
25862306a36Sopenharmony_ci *	      0     a     2
25962306a36Sopenharmony_ci *	old: |-----------| (bo_offset=n)
26062306a36Sopenharmony_ci *
26162306a36Sopenharmony_ci *	           1  a  2
26262306a36Sopenharmony_ci *	req:       |-----| (bo_offset=n+1)
26362306a36Sopenharmony_ci *
26462306a36Sopenharmony_ci *	     0  a' 1  a  2
26562306a36Sopenharmony_ci *	new: |-----|-----| (a'.bo_offset=n, a.bo_offset=n+1)
26662306a36Sopenharmony_ci *
26762306a36Sopenharmony_ci *
26862306a36Sopenharmony_ci * 9) Existent mapping is overlapped at the end by the requested mapping backed
26962306a36Sopenharmony_ci *    by a different BO. Hence, map the requested mapping and split up the
27062306a36Sopenharmony_ci *    existent one, without adjusting the BO offset.
27162306a36Sopenharmony_ci *
27262306a36Sopenharmony_ci *    ::
27362306a36Sopenharmony_ci *
27462306a36Sopenharmony_ci *	     0     a     2
27562306a36Sopenharmony_ci *	old: |-----------|       (bo_offset=n)
27662306a36Sopenharmony_ci *
27762306a36Sopenharmony_ci *	           1     b     3
27862306a36Sopenharmony_ci *	req:       |-----------| (bo_offset=m)
27962306a36Sopenharmony_ci *
28062306a36Sopenharmony_ci *	     0  a  1     b     3
28162306a36Sopenharmony_ci *	new: |-----|-----------| (a.bo_offset=n,b.bo_offset=m)
28262306a36Sopenharmony_ci *
28362306a36Sopenharmony_ci *
28462306a36Sopenharmony_ci * 10) Existent mapping is overlapped by the requested mapping, both having the
28562306a36Sopenharmony_ci *     same backing BO with a contiguous offset. Indicate the backing PTEs of
28662306a36Sopenharmony_ci *     the old mapping could be kept.
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci *     ::
28962306a36Sopenharmony_ci *
29062306a36Sopenharmony_ci *	      0     a     2
29162306a36Sopenharmony_ci *	 old: |-----------|       (bo_offset=n)
29262306a36Sopenharmony_ci *
29362306a36Sopenharmony_ci *	            1     a     3
29462306a36Sopenharmony_ci *	 req:       |-----------| (bo_offset=n+1)
29562306a36Sopenharmony_ci *
29662306a36Sopenharmony_ci *	      0  a' 1     a     3
29762306a36Sopenharmony_ci *	 new: |-----|-----------| (a'.bo_offset=n, a.bo_offset=n+1)
29862306a36Sopenharmony_ci *
29962306a36Sopenharmony_ci *
30062306a36Sopenharmony_ci * 11) Requested mapping's range is a centered subset of the existent one
30162306a36Sopenharmony_ci *     having a different backing BO. Hence, map the requested mapping and split
30262306a36Sopenharmony_ci *     up the existent one in two mappings, adjusting the BO offset of the right
30362306a36Sopenharmony_ci *     one accordingly.
30462306a36Sopenharmony_ci *
30562306a36Sopenharmony_ci *     ::
30662306a36Sopenharmony_ci *
30762306a36Sopenharmony_ci *	      0        a        3
30862306a36Sopenharmony_ci *	 old: |-----------------| (bo_offset=n)
30962306a36Sopenharmony_ci *
31062306a36Sopenharmony_ci *	            1  b  2
31162306a36Sopenharmony_ci *	 req:       |-----|       (bo_offset=m)
31262306a36Sopenharmony_ci *
31362306a36Sopenharmony_ci *	      0  a  1  b  2  a' 3
31462306a36Sopenharmony_ci *	 new: |-----|-----|-----| (a.bo_offset=n,b.bo_offset=m,a'.bo_offset=n+2)
31562306a36Sopenharmony_ci *
31662306a36Sopenharmony_ci *
31762306a36Sopenharmony_ci * 12) Requested mapping is a contiguous subset of the existent one. Split it
31862306a36Sopenharmony_ci *     up, but indicate that the backing PTEs could be kept.
31962306a36Sopenharmony_ci *
32062306a36Sopenharmony_ci *     ::
32162306a36Sopenharmony_ci *
32262306a36Sopenharmony_ci *	      0        a        3
32362306a36Sopenharmony_ci *	 old: |-----------------| (bo_offset=n)
32462306a36Sopenharmony_ci *
32562306a36Sopenharmony_ci *	            1  a  2
32662306a36Sopenharmony_ci *	 req:       |-----|       (bo_offset=n+1)
32762306a36Sopenharmony_ci *
32862306a36Sopenharmony_ci *	      0  a' 1  a  2 a'' 3
32962306a36Sopenharmony_ci *	 old: |-----|-----|-----| (a'.bo_offset=n, a.bo_offset=n+1, a''.bo_offset=n+2)
33062306a36Sopenharmony_ci *
33162306a36Sopenharmony_ci *
33262306a36Sopenharmony_ci * 13) Existent mapping is a right aligned subset of the requested one, hence
33362306a36Sopenharmony_ci *     replace the existent one.
33462306a36Sopenharmony_ci *
33562306a36Sopenharmony_ci *     ::
33662306a36Sopenharmony_ci *
33762306a36Sopenharmony_ci *	            1  a  2
33862306a36Sopenharmony_ci *	 old:       |-----| (bo_offset=n+1)
33962306a36Sopenharmony_ci *
34062306a36Sopenharmony_ci *	      0     a     2
34162306a36Sopenharmony_ci *	 req: |-----------| (bo_offset=n)
34262306a36Sopenharmony_ci *
34362306a36Sopenharmony_ci *	      0     a     2
34462306a36Sopenharmony_ci *	 new: |-----------| (bo_offset=n)
34562306a36Sopenharmony_ci *
34662306a36Sopenharmony_ci *     .. note::
34762306a36Sopenharmony_ci *        We expect to see the same result for a request with a different bo
34862306a36Sopenharmony_ci *        and/or non-contiguous bo_offset.
34962306a36Sopenharmony_ci *
35062306a36Sopenharmony_ci *
35162306a36Sopenharmony_ci * 14) Existent mapping is a centered subset of the requested one, hence
35262306a36Sopenharmony_ci *     replace the existent one.
35362306a36Sopenharmony_ci *
35462306a36Sopenharmony_ci *     ::
35562306a36Sopenharmony_ci *
35662306a36Sopenharmony_ci *	            1  a  2
35762306a36Sopenharmony_ci *	 old:       |-----| (bo_offset=n+1)
35862306a36Sopenharmony_ci *
35962306a36Sopenharmony_ci *	      0        a       3
36062306a36Sopenharmony_ci *	 req: |----------------| (bo_offset=n)
36162306a36Sopenharmony_ci *
36262306a36Sopenharmony_ci *	      0        a       3
36362306a36Sopenharmony_ci *	 new: |----------------| (bo_offset=n)
36462306a36Sopenharmony_ci *
36562306a36Sopenharmony_ci *     .. note::
36662306a36Sopenharmony_ci *        We expect to see the same result for a request with a different bo
36762306a36Sopenharmony_ci *        and/or non-contiguous bo_offset.
36862306a36Sopenharmony_ci *
36962306a36Sopenharmony_ci *
37062306a36Sopenharmony_ci * 15) Existent mappings is overlapped at the beginning by the requested mapping
37162306a36Sopenharmony_ci *     backed by a different BO. Hence, map the requested mapping and split up
37262306a36Sopenharmony_ci *     the existent one, adjusting its BO offset accordingly.
37362306a36Sopenharmony_ci *
37462306a36Sopenharmony_ci *     ::
37562306a36Sopenharmony_ci *
37662306a36Sopenharmony_ci *	            1     a     3
37762306a36Sopenharmony_ci *	 old:       |-----------| (bo_offset=n)
37862306a36Sopenharmony_ci *
37962306a36Sopenharmony_ci *	      0     b     2
38062306a36Sopenharmony_ci *	 req: |-----------|       (bo_offset=m)
38162306a36Sopenharmony_ci *
38262306a36Sopenharmony_ci *	      0     b     2  a' 3
38362306a36Sopenharmony_ci *	 new: |-----------|-----| (b.bo_offset=m,a.bo_offset=n+2)
38462306a36Sopenharmony_ci */
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci/**
38762306a36Sopenharmony_ci * DOC: Locking
38862306a36Sopenharmony_ci *
38962306a36Sopenharmony_ci * Generally, the GPU VA manager does not take care of locking itself, it is
39062306a36Sopenharmony_ci * the drivers responsibility to take care about locking. Drivers might want to
39162306a36Sopenharmony_ci * protect the following operations: inserting, removing and iterating
39262306a36Sopenharmony_ci * &drm_gpuva objects as well as generating all kinds of operations, such as
39362306a36Sopenharmony_ci * split / merge or prefetch.
39462306a36Sopenharmony_ci *
39562306a36Sopenharmony_ci * The GPU VA manager also does not take care of the locking of the backing
39662306a36Sopenharmony_ci * &drm_gem_object buffers GPU VA lists by itself; drivers are responsible to
39762306a36Sopenharmony_ci * enforce mutual exclusion using either the GEMs dma_resv lock or alternatively
39862306a36Sopenharmony_ci * a driver specific external lock. For the latter see also
39962306a36Sopenharmony_ci * drm_gem_gpuva_set_lock().
40062306a36Sopenharmony_ci *
40162306a36Sopenharmony_ci * However, the GPU VA manager contains lockdep checks to ensure callers of its
40262306a36Sopenharmony_ci * API hold the corresponding lock whenever the &drm_gem_objects GPU VA list is
40362306a36Sopenharmony_ci * accessed by functions such as drm_gpuva_link() or drm_gpuva_unlink().
40462306a36Sopenharmony_ci */
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci/**
40762306a36Sopenharmony_ci * DOC: Examples
40862306a36Sopenharmony_ci *
40962306a36Sopenharmony_ci * This section gives two examples on how to let the DRM GPUVA Manager generate
41062306a36Sopenharmony_ci * &drm_gpuva_op in order to satisfy a given map or unmap request and how to
41162306a36Sopenharmony_ci * make use of them.
41262306a36Sopenharmony_ci *
41362306a36Sopenharmony_ci * The below code is strictly limited to illustrate the generic usage pattern.
41462306a36Sopenharmony_ci * To maintain simplicitly, it doesn't make use of any abstractions for common
41562306a36Sopenharmony_ci * code, different (asyncronous) stages with fence signalling critical paths,
41662306a36Sopenharmony_ci * any other helpers or error handling in terms of freeing memory and dropping
41762306a36Sopenharmony_ci * previously taken locks.
41862306a36Sopenharmony_ci *
41962306a36Sopenharmony_ci * 1) Obtain a list of &drm_gpuva_op to create a new mapping::
42062306a36Sopenharmony_ci *
42162306a36Sopenharmony_ci *	// Allocates a new &drm_gpuva.
42262306a36Sopenharmony_ci *	struct drm_gpuva * driver_gpuva_alloc(void);
42362306a36Sopenharmony_ci *
42462306a36Sopenharmony_ci *	// Typically drivers would embedd the &drm_gpuva_manager and &drm_gpuva
42562306a36Sopenharmony_ci *	// structure in individual driver structures and lock the dma-resv with
42662306a36Sopenharmony_ci *	// drm_exec or similar helpers.
42762306a36Sopenharmony_ci *	int driver_mapping_create(struct drm_gpuva_manager *mgr,
42862306a36Sopenharmony_ci *				  u64 addr, u64 range,
42962306a36Sopenharmony_ci *				  struct drm_gem_object *obj, u64 offset)
43062306a36Sopenharmony_ci *	{
43162306a36Sopenharmony_ci *		struct drm_gpuva_ops *ops;
43262306a36Sopenharmony_ci *		struct drm_gpuva_op *op
43362306a36Sopenharmony_ci *
43462306a36Sopenharmony_ci *		driver_lock_va_space();
43562306a36Sopenharmony_ci *		ops = drm_gpuva_sm_map_ops_create(mgr, addr, range,
43662306a36Sopenharmony_ci *						  obj, offset);
43762306a36Sopenharmony_ci *		if (IS_ERR(ops))
43862306a36Sopenharmony_ci *			return PTR_ERR(ops);
43962306a36Sopenharmony_ci *
44062306a36Sopenharmony_ci *		drm_gpuva_for_each_op(op, ops) {
44162306a36Sopenharmony_ci *			struct drm_gpuva *va;
44262306a36Sopenharmony_ci *
44362306a36Sopenharmony_ci *			switch (op->op) {
44462306a36Sopenharmony_ci *			case DRM_GPUVA_OP_MAP:
44562306a36Sopenharmony_ci *				va = driver_gpuva_alloc();
44662306a36Sopenharmony_ci *				if (!va)
44762306a36Sopenharmony_ci *					; // unwind previous VA space updates,
44862306a36Sopenharmony_ci *					  // free memory and unlock
44962306a36Sopenharmony_ci *
45062306a36Sopenharmony_ci *				driver_vm_map();
45162306a36Sopenharmony_ci *				drm_gpuva_map(mgr, va, &op->map);
45262306a36Sopenharmony_ci *				drm_gpuva_link(va);
45362306a36Sopenharmony_ci *
45462306a36Sopenharmony_ci *				break;
45562306a36Sopenharmony_ci *			case DRM_GPUVA_OP_REMAP: {
45662306a36Sopenharmony_ci *				struct drm_gpuva *prev = NULL, *next = NULL;
45762306a36Sopenharmony_ci *
45862306a36Sopenharmony_ci *				va = op->remap.unmap->va;
45962306a36Sopenharmony_ci *
46062306a36Sopenharmony_ci *				if (op->remap.prev) {
46162306a36Sopenharmony_ci *					prev = driver_gpuva_alloc();
46262306a36Sopenharmony_ci *					if (!prev)
46362306a36Sopenharmony_ci *						; // unwind previous VA space
46462306a36Sopenharmony_ci *						  // updates, free memory and
46562306a36Sopenharmony_ci *						  // unlock
46662306a36Sopenharmony_ci *				}
46762306a36Sopenharmony_ci *
46862306a36Sopenharmony_ci *				if (op->remap.next) {
46962306a36Sopenharmony_ci *					next = driver_gpuva_alloc();
47062306a36Sopenharmony_ci *					if (!next)
47162306a36Sopenharmony_ci *						; // unwind previous VA space
47262306a36Sopenharmony_ci *						  // updates, free memory and
47362306a36Sopenharmony_ci *						  // unlock
47462306a36Sopenharmony_ci *				}
47562306a36Sopenharmony_ci *
47662306a36Sopenharmony_ci *				driver_vm_remap();
47762306a36Sopenharmony_ci *				drm_gpuva_remap(prev, next, &op->remap);
47862306a36Sopenharmony_ci *
47962306a36Sopenharmony_ci *				drm_gpuva_unlink(va);
48062306a36Sopenharmony_ci *				if (prev)
48162306a36Sopenharmony_ci *					drm_gpuva_link(prev);
48262306a36Sopenharmony_ci *				if (next)
48362306a36Sopenharmony_ci *					drm_gpuva_link(next);
48462306a36Sopenharmony_ci *
48562306a36Sopenharmony_ci *				break;
48662306a36Sopenharmony_ci *			}
48762306a36Sopenharmony_ci *			case DRM_GPUVA_OP_UNMAP:
48862306a36Sopenharmony_ci *				va = op->unmap->va;
48962306a36Sopenharmony_ci *
49062306a36Sopenharmony_ci *				driver_vm_unmap();
49162306a36Sopenharmony_ci *				drm_gpuva_unlink(va);
49262306a36Sopenharmony_ci *				drm_gpuva_unmap(&op->unmap);
49362306a36Sopenharmony_ci *
49462306a36Sopenharmony_ci *				break;
49562306a36Sopenharmony_ci *			default:
49662306a36Sopenharmony_ci *				break;
49762306a36Sopenharmony_ci *			}
49862306a36Sopenharmony_ci *		}
49962306a36Sopenharmony_ci *		driver_unlock_va_space();
50062306a36Sopenharmony_ci *
50162306a36Sopenharmony_ci *		return 0;
50262306a36Sopenharmony_ci *	}
50362306a36Sopenharmony_ci *
50462306a36Sopenharmony_ci * 2) Receive a callback for each &drm_gpuva_op to create a new mapping::
50562306a36Sopenharmony_ci *
50662306a36Sopenharmony_ci *	struct driver_context {
50762306a36Sopenharmony_ci *		struct drm_gpuva_manager *mgr;
50862306a36Sopenharmony_ci *		struct drm_gpuva *new_va;
50962306a36Sopenharmony_ci *		struct drm_gpuva *prev_va;
51062306a36Sopenharmony_ci *		struct drm_gpuva *next_va;
51162306a36Sopenharmony_ci *	};
51262306a36Sopenharmony_ci *
51362306a36Sopenharmony_ci *	// ops to pass to drm_gpuva_manager_init()
51462306a36Sopenharmony_ci *	static const struct drm_gpuva_fn_ops driver_gpuva_ops = {
51562306a36Sopenharmony_ci *		.sm_step_map = driver_gpuva_map,
51662306a36Sopenharmony_ci *		.sm_step_remap = driver_gpuva_remap,
51762306a36Sopenharmony_ci *		.sm_step_unmap = driver_gpuva_unmap,
51862306a36Sopenharmony_ci *	};
51962306a36Sopenharmony_ci *
52062306a36Sopenharmony_ci *	// Typically drivers would embedd the &drm_gpuva_manager and &drm_gpuva
52162306a36Sopenharmony_ci *	// structure in individual driver structures and lock the dma-resv with
52262306a36Sopenharmony_ci *	// drm_exec or similar helpers.
52362306a36Sopenharmony_ci *	int driver_mapping_create(struct drm_gpuva_manager *mgr,
52462306a36Sopenharmony_ci *				  u64 addr, u64 range,
52562306a36Sopenharmony_ci *				  struct drm_gem_object *obj, u64 offset)
52662306a36Sopenharmony_ci *	{
52762306a36Sopenharmony_ci *		struct driver_context ctx;
52862306a36Sopenharmony_ci *		struct drm_gpuva_ops *ops;
52962306a36Sopenharmony_ci *		struct drm_gpuva_op *op;
53062306a36Sopenharmony_ci *		int ret = 0;
53162306a36Sopenharmony_ci *
53262306a36Sopenharmony_ci *		ctx.mgr = mgr;
53362306a36Sopenharmony_ci *
53462306a36Sopenharmony_ci *		ctx.new_va = kzalloc(sizeof(*ctx.new_va), GFP_KERNEL);
53562306a36Sopenharmony_ci *		ctx.prev_va = kzalloc(sizeof(*ctx.prev_va), GFP_KERNEL);
53662306a36Sopenharmony_ci *		ctx.next_va = kzalloc(sizeof(*ctx.next_va), GFP_KERNEL);
53762306a36Sopenharmony_ci *		if (!ctx.new_va || !ctx.prev_va || !ctx.next_va) {
53862306a36Sopenharmony_ci *			ret = -ENOMEM;
53962306a36Sopenharmony_ci *			goto out;
54062306a36Sopenharmony_ci *		}
54162306a36Sopenharmony_ci *
54262306a36Sopenharmony_ci *		driver_lock_va_space();
54362306a36Sopenharmony_ci *		ret = drm_gpuva_sm_map(mgr, &ctx, addr, range, obj, offset);
54462306a36Sopenharmony_ci *		driver_unlock_va_space();
54562306a36Sopenharmony_ci *
54662306a36Sopenharmony_ci *	out:
54762306a36Sopenharmony_ci *		kfree(ctx.new_va);
54862306a36Sopenharmony_ci *		kfree(ctx.prev_va);
54962306a36Sopenharmony_ci *		kfree(ctx.next_va);
55062306a36Sopenharmony_ci *		return ret;
55162306a36Sopenharmony_ci *	}
55262306a36Sopenharmony_ci *
55362306a36Sopenharmony_ci *	int driver_gpuva_map(struct drm_gpuva_op *op, void *__ctx)
55462306a36Sopenharmony_ci *	{
55562306a36Sopenharmony_ci *		struct driver_context *ctx = __ctx;
55662306a36Sopenharmony_ci *
55762306a36Sopenharmony_ci *		drm_gpuva_map(ctx->mgr, ctx->new_va, &op->map);
55862306a36Sopenharmony_ci *
55962306a36Sopenharmony_ci *		drm_gpuva_link(ctx->new_va);
56062306a36Sopenharmony_ci *
56162306a36Sopenharmony_ci *		// prevent the new GPUVA from being freed in
56262306a36Sopenharmony_ci *		// driver_mapping_create()
56362306a36Sopenharmony_ci *		ctx->new_va = NULL;
56462306a36Sopenharmony_ci *
56562306a36Sopenharmony_ci *		return 0;
56662306a36Sopenharmony_ci *	}
56762306a36Sopenharmony_ci *
56862306a36Sopenharmony_ci *	int driver_gpuva_remap(struct drm_gpuva_op *op, void *__ctx)
56962306a36Sopenharmony_ci *	{
57062306a36Sopenharmony_ci *		struct driver_context *ctx = __ctx;
57162306a36Sopenharmony_ci *
57262306a36Sopenharmony_ci *		drm_gpuva_remap(ctx->prev_va, ctx->next_va, &op->remap);
57362306a36Sopenharmony_ci *
57462306a36Sopenharmony_ci *		drm_gpuva_unlink(op->remap.unmap->va);
57562306a36Sopenharmony_ci *		kfree(op->remap.unmap->va);
57662306a36Sopenharmony_ci *
57762306a36Sopenharmony_ci *		if (op->remap.prev) {
57862306a36Sopenharmony_ci *			drm_gpuva_link(ctx->prev_va);
57962306a36Sopenharmony_ci *			ctx->prev_va = NULL;
58062306a36Sopenharmony_ci *		}
58162306a36Sopenharmony_ci *
58262306a36Sopenharmony_ci *		if (op->remap.next) {
58362306a36Sopenharmony_ci *			drm_gpuva_link(ctx->next_va);
58462306a36Sopenharmony_ci *			ctx->next_va = NULL;
58562306a36Sopenharmony_ci *		}
58662306a36Sopenharmony_ci *
58762306a36Sopenharmony_ci *		return 0;
58862306a36Sopenharmony_ci *	}
58962306a36Sopenharmony_ci *
59062306a36Sopenharmony_ci *	int driver_gpuva_unmap(struct drm_gpuva_op *op, void *__ctx)
59162306a36Sopenharmony_ci *	{
59262306a36Sopenharmony_ci *		drm_gpuva_unlink(op->unmap.va);
59362306a36Sopenharmony_ci *		drm_gpuva_unmap(&op->unmap);
59462306a36Sopenharmony_ci *		kfree(op->unmap.va);
59562306a36Sopenharmony_ci *
59662306a36Sopenharmony_ci *		return 0;
59762306a36Sopenharmony_ci *	}
59862306a36Sopenharmony_ci */
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci#define to_drm_gpuva(__node)	container_of((__node), struct drm_gpuva, rb.node)
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci#define GPUVA_START(node) ((node)->va.addr)
60362306a36Sopenharmony_ci#define GPUVA_LAST(node) ((node)->va.addr + (node)->va.range - 1)
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci/* We do not actually use drm_gpuva_it_next(), tell the compiler to not complain
60662306a36Sopenharmony_ci * about this.
60762306a36Sopenharmony_ci */
60862306a36Sopenharmony_ciINTERVAL_TREE_DEFINE(struct drm_gpuva, rb.node, u64, rb.__subtree_last,
60962306a36Sopenharmony_ci		     GPUVA_START, GPUVA_LAST, static __maybe_unused,
61062306a36Sopenharmony_ci		     drm_gpuva_it)
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic int __drm_gpuva_insert(struct drm_gpuva_manager *mgr,
61362306a36Sopenharmony_ci			      struct drm_gpuva *va);
61462306a36Sopenharmony_cistatic void __drm_gpuva_remove(struct drm_gpuva *va);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_cistatic bool
61762306a36Sopenharmony_cidrm_gpuva_check_overflow(u64 addr, u64 range)
61862306a36Sopenharmony_ci{
61962306a36Sopenharmony_ci	u64 end;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	return WARN(check_add_overflow(addr, range, &end),
62262306a36Sopenharmony_ci		    "GPUVA address limited to %zu bytes.\n", sizeof(end));
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic bool
62662306a36Sopenharmony_cidrm_gpuva_in_mm_range(struct drm_gpuva_manager *mgr, u64 addr, u64 range)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	u64 end = addr + range;
62962306a36Sopenharmony_ci	u64 mm_start = mgr->mm_start;
63062306a36Sopenharmony_ci	u64 mm_end = mm_start + mgr->mm_range;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	return addr >= mm_start && end <= mm_end;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic bool
63662306a36Sopenharmony_cidrm_gpuva_in_kernel_node(struct drm_gpuva_manager *mgr, u64 addr, u64 range)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	u64 end = addr + range;
63962306a36Sopenharmony_ci	u64 kstart = mgr->kernel_alloc_node.va.addr;
64062306a36Sopenharmony_ci	u64 krange = mgr->kernel_alloc_node.va.range;
64162306a36Sopenharmony_ci	u64 kend = kstart + krange;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	return krange && addr < kend && kstart < end;
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic bool
64762306a36Sopenharmony_cidrm_gpuva_range_valid(struct drm_gpuva_manager *mgr,
64862306a36Sopenharmony_ci		      u64 addr, u64 range)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	return !drm_gpuva_check_overflow(addr, range) &&
65162306a36Sopenharmony_ci	       drm_gpuva_in_mm_range(mgr, addr, range) &&
65262306a36Sopenharmony_ci	       !drm_gpuva_in_kernel_node(mgr, addr, range);
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci/**
65662306a36Sopenharmony_ci * drm_gpuva_manager_init() - initialize a &drm_gpuva_manager
65762306a36Sopenharmony_ci * @mgr: pointer to the &drm_gpuva_manager to initialize
65862306a36Sopenharmony_ci * @name: the name of the GPU VA space
65962306a36Sopenharmony_ci * @start_offset: the start offset of the GPU VA space
66062306a36Sopenharmony_ci * @range: the size of the GPU VA space
66162306a36Sopenharmony_ci * @reserve_offset: the start of the kernel reserved GPU VA area
66262306a36Sopenharmony_ci * @reserve_range: the size of the kernel reserved GPU VA area
66362306a36Sopenharmony_ci * @ops: &drm_gpuva_fn_ops called on &drm_gpuva_sm_map / &drm_gpuva_sm_unmap
66462306a36Sopenharmony_ci *
66562306a36Sopenharmony_ci * The &drm_gpuva_manager must be initialized with this function before use.
66662306a36Sopenharmony_ci *
66762306a36Sopenharmony_ci * Note that @mgr must be cleared to 0 before calling this function. The given
66862306a36Sopenharmony_ci * &name is expected to be managed by the surrounding driver structures.
66962306a36Sopenharmony_ci */
67062306a36Sopenharmony_civoid
67162306a36Sopenharmony_cidrm_gpuva_manager_init(struct drm_gpuva_manager *mgr,
67262306a36Sopenharmony_ci		       const char *name,
67362306a36Sopenharmony_ci		       u64 start_offset, u64 range,
67462306a36Sopenharmony_ci		       u64 reserve_offset, u64 reserve_range,
67562306a36Sopenharmony_ci		       const struct drm_gpuva_fn_ops *ops)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	mgr->rb.tree = RB_ROOT_CACHED;
67862306a36Sopenharmony_ci	INIT_LIST_HEAD(&mgr->rb.list);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	drm_gpuva_check_overflow(start_offset, range);
68162306a36Sopenharmony_ci	mgr->mm_start = start_offset;
68262306a36Sopenharmony_ci	mgr->mm_range = range;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	mgr->name = name ? name : "unknown";
68562306a36Sopenharmony_ci	mgr->ops = ops;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	memset(&mgr->kernel_alloc_node, 0, sizeof(struct drm_gpuva));
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (reserve_range) {
69062306a36Sopenharmony_ci		mgr->kernel_alloc_node.va.addr = reserve_offset;
69162306a36Sopenharmony_ci		mgr->kernel_alloc_node.va.range = reserve_range;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci		if (likely(!drm_gpuva_check_overflow(reserve_offset,
69462306a36Sopenharmony_ci						     reserve_range)))
69562306a36Sopenharmony_ci			__drm_gpuva_insert(mgr, &mgr->kernel_alloc_node);
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_manager_init);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci/**
70162306a36Sopenharmony_ci * drm_gpuva_manager_destroy() - cleanup a &drm_gpuva_manager
70262306a36Sopenharmony_ci * @mgr: pointer to the &drm_gpuva_manager to clean up
70362306a36Sopenharmony_ci *
70462306a36Sopenharmony_ci * Note that it is a bug to call this function on a manager that still
70562306a36Sopenharmony_ci * holds GPU VA mappings.
70662306a36Sopenharmony_ci */
70762306a36Sopenharmony_civoid
70862306a36Sopenharmony_cidrm_gpuva_manager_destroy(struct drm_gpuva_manager *mgr)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	mgr->name = NULL;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	if (mgr->kernel_alloc_node.va.range)
71362306a36Sopenharmony_ci		__drm_gpuva_remove(&mgr->kernel_alloc_node);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	WARN(!RB_EMPTY_ROOT(&mgr->rb.tree.rb_root),
71662306a36Sopenharmony_ci	     "GPUVA tree is not empty, potentially leaking memory.");
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_manager_destroy);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cistatic int
72162306a36Sopenharmony_ci__drm_gpuva_insert(struct drm_gpuva_manager *mgr,
72262306a36Sopenharmony_ci		   struct drm_gpuva *va)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	struct rb_node *node;
72562306a36Sopenharmony_ci	struct list_head *head;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	if (drm_gpuva_it_iter_first(&mgr->rb.tree,
72862306a36Sopenharmony_ci				    GPUVA_START(va),
72962306a36Sopenharmony_ci				    GPUVA_LAST(va)))
73062306a36Sopenharmony_ci		return -EEXIST;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	va->mgr = mgr;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	drm_gpuva_it_insert(va, &mgr->rb.tree);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	node = rb_prev(&va->rb.node);
73762306a36Sopenharmony_ci	if (node)
73862306a36Sopenharmony_ci		head = &(to_drm_gpuva(node))->rb.entry;
73962306a36Sopenharmony_ci	else
74062306a36Sopenharmony_ci		head = &mgr->rb.list;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	list_add(&va->rb.entry, head);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	return 0;
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci/**
74862306a36Sopenharmony_ci * drm_gpuva_insert() - insert a &drm_gpuva
74962306a36Sopenharmony_ci * @mgr: the &drm_gpuva_manager to insert the &drm_gpuva in
75062306a36Sopenharmony_ci * @va: the &drm_gpuva to insert
75162306a36Sopenharmony_ci *
75262306a36Sopenharmony_ci * Insert a &drm_gpuva with a given address and range into a
75362306a36Sopenharmony_ci * &drm_gpuva_manager.
75462306a36Sopenharmony_ci *
75562306a36Sopenharmony_ci * It is safe to use this function using the safe versions of iterating the GPU
75662306a36Sopenharmony_ci * VA space, such as drm_gpuva_for_each_va_safe() and
75762306a36Sopenharmony_ci * drm_gpuva_for_each_va_range_safe().
75862306a36Sopenharmony_ci *
75962306a36Sopenharmony_ci * Returns: 0 on success, negative error code on failure.
76062306a36Sopenharmony_ci */
76162306a36Sopenharmony_ciint
76262306a36Sopenharmony_cidrm_gpuva_insert(struct drm_gpuva_manager *mgr,
76362306a36Sopenharmony_ci		 struct drm_gpuva *va)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	u64 addr = va->va.addr;
76662306a36Sopenharmony_ci	u64 range = va->va.range;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if (unlikely(!drm_gpuva_range_valid(mgr, addr, range)))
76962306a36Sopenharmony_ci		return -EINVAL;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	return __drm_gpuva_insert(mgr, va);
77262306a36Sopenharmony_ci}
77362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_insert);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_cistatic void
77662306a36Sopenharmony_ci__drm_gpuva_remove(struct drm_gpuva *va)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	drm_gpuva_it_remove(va, &va->mgr->rb.tree);
77962306a36Sopenharmony_ci	list_del_init(&va->rb.entry);
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci/**
78362306a36Sopenharmony_ci * drm_gpuva_remove() - remove a &drm_gpuva
78462306a36Sopenharmony_ci * @va: the &drm_gpuva to remove
78562306a36Sopenharmony_ci *
78662306a36Sopenharmony_ci * This removes the given &va from the underlaying tree.
78762306a36Sopenharmony_ci *
78862306a36Sopenharmony_ci * It is safe to use this function using the safe versions of iterating the GPU
78962306a36Sopenharmony_ci * VA space, such as drm_gpuva_for_each_va_safe() and
79062306a36Sopenharmony_ci * drm_gpuva_for_each_va_range_safe().
79162306a36Sopenharmony_ci */
79262306a36Sopenharmony_civoid
79362306a36Sopenharmony_cidrm_gpuva_remove(struct drm_gpuva *va)
79462306a36Sopenharmony_ci{
79562306a36Sopenharmony_ci	struct drm_gpuva_manager *mgr = va->mgr;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	if (unlikely(va == &mgr->kernel_alloc_node)) {
79862306a36Sopenharmony_ci		WARN(1, "Can't destroy kernel reserved node.\n");
79962306a36Sopenharmony_ci		return;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	__drm_gpuva_remove(va);
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_remove);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci/**
80762306a36Sopenharmony_ci * drm_gpuva_link() - link a &drm_gpuva
80862306a36Sopenharmony_ci * @va: the &drm_gpuva to link
80962306a36Sopenharmony_ci *
81062306a36Sopenharmony_ci * This adds the given &va to the GPU VA list of the &drm_gem_object it is
81162306a36Sopenharmony_ci * associated with.
81262306a36Sopenharmony_ci *
81362306a36Sopenharmony_ci * This function expects the caller to protect the GEM's GPUVA list against
81462306a36Sopenharmony_ci * concurrent access using the GEMs dma_resv lock.
81562306a36Sopenharmony_ci */
81662306a36Sopenharmony_civoid
81762306a36Sopenharmony_cidrm_gpuva_link(struct drm_gpuva *va)
81862306a36Sopenharmony_ci{
81962306a36Sopenharmony_ci	struct drm_gem_object *obj = va->gem.obj;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	if (unlikely(!obj))
82262306a36Sopenharmony_ci		return;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	drm_gem_gpuva_assert_lock_held(obj);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	list_add_tail(&va->gem.entry, &obj->gpuva.list);
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_link);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci/**
83162306a36Sopenharmony_ci * drm_gpuva_unlink() - unlink a &drm_gpuva
83262306a36Sopenharmony_ci * @va: the &drm_gpuva to unlink
83362306a36Sopenharmony_ci *
83462306a36Sopenharmony_ci * This removes the given &va from the GPU VA list of the &drm_gem_object it is
83562306a36Sopenharmony_ci * associated with.
83662306a36Sopenharmony_ci *
83762306a36Sopenharmony_ci * This function expects the caller to protect the GEM's GPUVA list against
83862306a36Sopenharmony_ci * concurrent access using the GEMs dma_resv lock.
83962306a36Sopenharmony_ci */
84062306a36Sopenharmony_civoid
84162306a36Sopenharmony_cidrm_gpuva_unlink(struct drm_gpuva *va)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	struct drm_gem_object *obj = va->gem.obj;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	if (unlikely(!obj))
84662306a36Sopenharmony_ci		return;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	drm_gem_gpuva_assert_lock_held(obj);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	list_del_init(&va->gem.entry);
85162306a36Sopenharmony_ci}
85262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_unlink);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci/**
85562306a36Sopenharmony_ci * drm_gpuva_find_first() - find the first &drm_gpuva in the given range
85662306a36Sopenharmony_ci * @mgr: the &drm_gpuva_manager to search in
85762306a36Sopenharmony_ci * @addr: the &drm_gpuvas address
85862306a36Sopenharmony_ci * @range: the &drm_gpuvas range
85962306a36Sopenharmony_ci *
86062306a36Sopenharmony_ci * Returns: the first &drm_gpuva within the given range
86162306a36Sopenharmony_ci */
86262306a36Sopenharmony_cistruct drm_gpuva *
86362306a36Sopenharmony_cidrm_gpuva_find_first(struct drm_gpuva_manager *mgr,
86462306a36Sopenharmony_ci		     u64 addr, u64 range)
86562306a36Sopenharmony_ci{
86662306a36Sopenharmony_ci	u64 last = addr + range - 1;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	return drm_gpuva_it_iter_first(&mgr->rb.tree, addr, last);
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_find_first);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci/**
87362306a36Sopenharmony_ci * drm_gpuva_find() - find a &drm_gpuva
87462306a36Sopenharmony_ci * @mgr: the &drm_gpuva_manager to search in
87562306a36Sopenharmony_ci * @addr: the &drm_gpuvas address
87662306a36Sopenharmony_ci * @range: the &drm_gpuvas range
87762306a36Sopenharmony_ci *
87862306a36Sopenharmony_ci * Returns: the &drm_gpuva at a given &addr and with a given &range
87962306a36Sopenharmony_ci */
88062306a36Sopenharmony_cistruct drm_gpuva *
88162306a36Sopenharmony_cidrm_gpuva_find(struct drm_gpuva_manager *mgr,
88262306a36Sopenharmony_ci	       u64 addr, u64 range)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	struct drm_gpuva *va;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	va = drm_gpuva_find_first(mgr, addr, range);
88762306a36Sopenharmony_ci	if (!va)
88862306a36Sopenharmony_ci		goto out;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	if (va->va.addr != addr ||
89162306a36Sopenharmony_ci	    va->va.range != range)
89262306a36Sopenharmony_ci		goto out;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	return va;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ciout:
89762306a36Sopenharmony_ci	return NULL;
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_find);
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci/**
90262306a36Sopenharmony_ci * drm_gpuva_find_prev() - find the &drm_gpuva before the given address
90362306a36Sopenharmony_ci * @mgr: the &drm_gpuva_manager to search in
90462306a36Sopenharmony_ci * @start: the given GPU VA's start address
90562306a36Sopenharmony_ci *
90662306a36Sopenharmony_ci * Find the adjacent &drm_gpuva before the GPU VA with given &start address.
90762306a36Sopenharmony_ci *
90862306a36Sopenharmony_ci * Note that if there is any free space between the GPU VA mappings no mapping
90962306a36Sopenharmony_ci * is returned.
91062306a36Sopenharmony_ci *
91162306a36Sopenharmony_ci * Returns: a pointer to the found &drm_gpuva or NULL if none was found
91262306a36Sopenharmony_ci */
91362306a36Sopenharmony_cistruct drm_gpuva *
91462306a36Sopenharmony_cidrm_gpuva_find_prev(struct drm_gpuva_manager *mgr, u64 start)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	if (!drm_gpuva_range_valid(mgr, start - 1, 1))
91762306a36Sopenharmony_ci		return NULL;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	return drm_gpuva_it_iter_first(&mgr->rb.tree, start - 1, start);
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_find_prev);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci/**
92462306a36Sopenharmony_ci * drm_gpuva_find_next() - find the &drm_gpuva after the given address
92562306a36Sopenharmony_ci * @mgr: the &drm_gpuva_manager to search in
92662306a36Sopenharmony_ci * @end: the given GPU VA's end address
92762306a36Sopenharmony_ci *
92862306a36Sopenharmony_ci * Find the adjacent &drm_gpuva after the GPU VA with given &end address.
92962306a36Sopenharmony_ci *
93062306a36Sopenharmony_ci * Note that if there is any free space between the GPU VA mappings no mapping
93162306a36Sopenharmony_ci * is returned.
93262306a36Sopenharmony_ci *
93362306a36Sopenharmony_ci * Returns: a pointer to the found &drm_gpuva or NULL if none was found
93462306a36Sopenharmony_ci */
93562306a36Sopenharmony_cistruct drm_gpuva *
93662306a36Sopenharmony_cidrm_gpuva_find_next(struct drm_gpuva_manager *mgr, u64 end)
93762306a36Sopenharmony_ci{
93862306a36Sopenharmony_ci	if (!drm_gpuva_range_valid(mgr, end, 1))
93962306a36Sopenharmony_ci		return NULL;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	return drm_gpuva_it_iter_first(&mgr->rb.tree, end, end + 1);
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_find_next);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci/**
94662306a36Sopenharmony_ci * drm_gpuva_interval_empty() - indicate whether a given interval of the VA space
94762306a36Sopenharmony_ci * is empty
94862306a36Sopenharmony_ci * @mgr: the &drm_gpuva_manager to check the range for
94962306a36Sopenharmony_ci * @addr: the start address of the range
95062306a36Sopenharmony_ci * @range: the range of the interval
95162306a36Sopenharmony_ci *
95262306a36Sopenharmony_ci * Returns: true if the interval is empty, false otherwise
95362306a36Sopenharmony_ci */
95462306a36Sopenharmony_cibool
95562306a36Sopenharmony_cidrm_gpuva_interval_empty(struct drm_gpuva_manager *mgr, u64 addr, u64 range)
95662306a36Sopenharmony_ci{
95762306a36Sopenharmony_ci	return !drm_gpuva_find_first(mgr, addr, range);
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_interval_empty);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci/**
96262306a36Sopenharmony_ci * drm_gpuva_map() - helper to insert a &drm_gpuva according to a
96362306a36Sopenharmony_ci * &drm_gpuva_op_map
96462306a36Sopenharmony_ci * @mgr: the &drm_gpuva_manager
96562306a36Sopenharmony_ci * @va: the &drm_gpuva to insert
96662306a36Sopenharmony_ci * @op: the &drm_gpuva_op_map to initialize @va with
96762306a36Sopenharmony_ci *
96862306a36Sopenharmony_ci * Initializes the @va from the @op and inserts it into the given @mgr.
96962306a36Sopenharmony_ci */
97062306a36Sopenharmony_civoid
97162306a36Sopenharmony_cidrm_gpuva_map(struct drm_gpuva_manager *mgr,
97262306a36Sopenharmony_ci	      struct drm_gpuva *va,
97362306a36Sopenharmony_ci	      struct drm_gpuva_op_map *op)
97462306a36Sopenharmony_ci{
97562306a36Sopenharmony_ci	drm_gpuva_init_from_op(va, op);
97662306a36Sopenharmony_ci	drm_gpuva_insert(mgr, va);
97762306a36Sopenharmony_ci}
97862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_map);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci/**
98162306a36Sopenharmony_ci * drm_gpuva_remap() - helper to remap a &drm_gpuva according to a
98262306a36Sopenharmony_ci * &drm_gpuva_op_remap
98362306a36Sopenharmony_ci * @prev: the &drm_gpuva to remap when keeping the start of a mapping
98462306a36Sopenharmony_ci * @next: the &drm_gpuva to remap when keeping the end of a mapping
98562306a36Sopenharmony_ci * @op: the &drm_gpuva_op_remap to initialize @prev and @next with
98662306a36Sopenharmony_ci *
98762306a36Sopenharmony_ci * Removes the currently mapped &drm_gpuva and remaps it using @prev and/or
98862306a36Sopenharmony_ci * @next.
98962306a36Sopenharmony_ci */
99062306a36Sopenharmony_civoid
99162306a36Sopenharmony_cidrm_gpuva_remap(struct drm_gpuva *prev,
99262306a36Sopenharmony_ci		struct drm_gpuva *next,
99362306a36Sopenharmony_ci		struct drm_gpuva_op_remap *op)
99462306a36Sopenharmony_ci{
99562306a36Sopenharmony_ci	struct drm_gpuva *curr = op->unmap->va;
99662306a36Sopenharmony_ci	struct drm_gpuva_manager *mgr = curr->mgr;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	drm_gpuva_remove(curr);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	if (op->prev) {
100162306a36Sopenharmony_ci		drm_gpuva_init_from_op(prev, op->prev);
100262306a36Sopenharmony_ci		drm_gpuva_insert(mgr, prev);
100362306a36Sopenharmony_ci	}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	if (op->next) {
100662306a36Sopenharmony_ci		drm_gpuva_init_from_op(next, op->next);
100762306a36Sopenharmony_ci		drm_gpuva_insert(mgr, next);
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_remap);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci/**
101362306a36Sopenharmony_ci * drm_gpuva_unmap() - helper to remove a &drm_gpuva according to a
101462306a36Sopenharmony_ci * &drm_gpuva_op_unmap
101562306a36Sopenharmony_ci * @op: the &drm_gpuva_op_unmap specifying the &drm_gpuva to remove
101662306a36Sopenharmony_ci *
101762306a36Sopenharmony_ci * Removes the &drm_gpuva associated with the &drm_gpuva_op_unmap.
101862306a36Sopenharmony_ci */
101962306a36Sopenharmony_civoid
102062306a36Sopenharmony_cidrm_gpuva_unmap(struct drm_gpuva_op_unmap *op)
102162306a36Sopenharmony_ci{
102262306a36Sopenharmony_ci	drm_gpuva_remove(op->va);
102362306a36Sopenharmony_ci}
102462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_unmap);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_cistatic int
102762306a36Sopenharmony_ciop_map_cb(const struct drm_gpuva_fn_ops *fn, void *priv,
102862306a36Sopenharmony_ci	  u64 addr, u64 range,
102962306a36Sopenharmony_ci	  struct drm_gem_object *obj, u64 offset)
103062306a36Sopenharmony_ci{
103162306a36Sopenharmony_ci	struct drm_gpuva_op op = {};
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	op.op = DRM_GPUVA_OP_MAP;
103462306a36Sopenharmony_ci	op.map.va.addr = addr;
103562306a36Sopenharmony_ci	op.map.va.range = range;
103662306a36Sopenharmony_ci	op.map.gem.obj = obj;
103762306a36Sopenharmony_ci	op.map.gem.offset = offset;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	return fn->sm_step_map(&op, priv);
104062306a36Sopenharmony_ci}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_cistatic int
104362306a36Sopenharmony_ciop_remap_cb(const struct drm_gpuva_fn_ops *fn, void *priv,
104462306a36Sopenharmony_ci	    struct drm_gpuva_op_map *prev,
104562306a36Sopenharmony_ci	    struct drm_gpuva_op_map *next,
104662306a36Sopenharmony_ci	    struct drm_gpuva_op_unmap *unmap)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	struct drm_gpuva_op op = {};
104962306a36Sopenharmony_ci	struct drm_gpuva_op_remap *r;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	op.op = DRM_GPUVA_OP_REMAP;
105262306a36Sopenharmony_ci	r = &op.remap;
105362306a36Sopenharmony_ci	r->prev = prev;
105462306a36Sopenharmony_ci	r->next = next;
105562306a36Sopenharmony_ci	r->unmap = unmap;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	return fn->sm_step_remap(&op, priv);
105862306a36Sopenharmony_ci}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_cistatic int
106162306a36Sopenharmony_ciop_unmap_cb(const struct drm_gpuva_fn_ops *fn, void *priv,
106262306a36Sopenharmony_ci	    struct drm_gpuva *va, bool merge)
106362306a36Sopenharmony_ci{
106462306a36Sopenharmony_ci	struct drm_gpuva_op op = {};
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	op.op = DRM_GPUVA_OP_UNMAP;
106762306a36Sopenharmony_ci	op.unmap.va = va;
106862306a36Sopenharmony_ci	op.unmap.keep = merge;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	return fn->sm_step_unmap(&op, priv);
107162306a36Sopenharmony_ci}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_cistatic int
107462306a36Sopenharmony_ci__drm_gpuva_sm_map(struct drm_gpuva_manager *mgr,
107562306a36Sopenharmony_ci		   const struct drm_gpuva_fn_ops *ops, void *priv,
107662306a36Sopenharmony_ci		   u64 req_addr, u64 req_range,
107762306a36Sopenharmony_ci		   struct drm_gem_object *req_obj, u64 req_offset)
107862306a36Sopenharmony_ci{
107962306a36Sopenharmony_ci	struct drm_gpuva *va, *next;
108062306a36Sopenharmony_ci	u64 req_end = req_addr + req_range;
108162306a36Sopenharmony_ci	int ret;
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	if (unlikely(!drm_gpuva_range_valid(mgr, req_addr, req_range)))
108462306a36Sopenharmony_ci		return -EINVAL;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	drm_gpuva_for_each_va_range_safe(va, next, mgr, req_addr, req_end) {
108762306a36Sopenharmony_ci		struct drm_gem_object *obj = va->gem.obj;
108862306a36Sopenharmony_ci		u64 offset = va->gem.offset;
108962306a36Sopenharmony_ci		u64 addr = va->va.addr;
109062306a36Sopenharmony_ci		u64 range = va->va.range;
109162306a36Sopenharmony_ci		u64 end = addr + range;
109262306a36Sopenharmony_ci		bool merge = !!va->gem.obj;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci		if (addr == req_addr) {
109562306a36Sopenharmony_ci			merge &= obj == req_obj &&
109662306a36Sopenharmony_ci				 offset == req_offset;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci			if (end == req_end) {
109962306a36Sopenharmony_ci				ret = op_unmap_cb(ops, priv, va, merge);
110062306a36Sopenharmony_ci				if (ret)
110162306a36Sopenharmony_ci					return ret;
110262306a36Sopenharmony_ci				break;
110362306a36Sopenharmony_ci			}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci			if (end < req_end) {
110662306a36Sopenharmony_ci				ret = op_unmap_cb(ops, priv, va, merge);
110762306a36Sopenharmony_ci				if (ret)
110862306a36Sopenharmony_ci					return ret;
110962306a36Sopenharmony_ci				continue;
111062306a36Sopenharmony_ci			}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci			if (end > req_end) {
111362306a36Sopenharmony_ci				struct drm_gpuva_op_map n = {
111462306a36Sopenharmony_ci					.va.addr = req_end,
111562306a36Sopenharmony_ci					.va.range = range - req_range,
111662306a36Sopenharmony_ci					.gem.obj = obj,
111762306a36Sopenharmony_ci					.gem.offset = offset + req_range,
111862306a36Sopenharmony_ci				};
111962306a36Sopenharmony_ci				struct drm_gpuva_op_unmap u = {
112062306a36Sopenharmony_ci					.va = va,
112162306a36Sopenharmony_ci					.keep = merge,
112262306a36Sopenharmony_ci				};
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci				ret = op_remap_cb(ops, priv, NULL, &n, &u);
112562306a36Sopenharmony_ci				if (ret)
112662306a36Sopenharmony_ci					return ret;
112762306a36Sopenharmony_ci				break;
112862306a36Sopenharmony_ci			}
112962306a36Sopenharmony_ci		} else if (addr < req_addr) {
113062306a36Sopenharmony_ci			u64 ls_range = req_addr - addr;
113162306a36Sopenharmony_ci			struct drm_gpuva_op_map p = {
113262306a36Sopenharmony_ci				.va.addr = addr,
113362306a36Sopenharmony_ci				.va.range = ls_range,
113462306a36Sopenharmony_ci				.gem.obj = obj,
113562306a36Sopenharmony_ci				.gem.offset = offset,
113662306a36Sopenharmony_ci			};
113762306a36Sopenharmony_ci			struct drm_gpuva_op_unmap u = { .va = va };
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci			merge &= obj == req_obj &&
114062306a36Sopenharmony_ci				 offset + ls_range == req_offset;
114162306a36Sopenharmony_ci			u.keep = merge;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci			if (end == req_end) {
114462306a36Sopenharmony_ci				ret = op_remap_cb(ops, priv, &p, NULL, &u);
114562306a36Sopenharmony_ci				if (ret)
114662306a36Sopenharmony_ci					return ret;
114762306a36Sopenharmony_ci				break;
114862306a36Sopenharmony_ci			}
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci			if (end < req_end) {
115162306a36Sopenharmony_ci				ret = op_remap_cb(ops, priv, &p, NULL, &u);
115262306a36Sopenharmony_ci				if (ret)
115362306a36Sopenharmony_ci					return ret;
115462306a36Sopenharmony_ci				continue;
115562306a36Sopenharmony_ci			}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci			if (end > req_end) {
115862306a36Sopenharmony_ci				struct drm_gpuva_op_map n = {
115962306a36Sopenharmony_ci					.va.addr = req_end,
116062306a36Sopenharmony_ci					.va.range = end - req_end,
116162306a36Sopenharmony_ci					.gem.obj = obj,
116262306a36Sopenharmony_ci					.gem.offset = offset + ls_range +
116362306a36Sopenharmony_ci						      req_range,
116462306a36Sopenharmony_ci				};
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci				ret = op_remap_cb(ops, priv, &p, &n, &u);
116762306a36Sopenharmony_ci				if (ret)
116862306a36Sopenharmony_ci					return ret;
116962306a36Sopenharmony_ci				break;
117062306a36Sopenharmony_ci			}
117162306a36Sopenharmony_ci		} else if (addr > req_addr) {
117262306a36Sopenharmony_ci			merge &= obj == req_obj &&
117362306a36Sopenharmony_ci				 offset == req_offset +
117462306a36Sopenharmony_ci					   (addr - req_addr);
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci			if (end == req_end) {
117762306a36Sopenharmony_ci				ret = op_unmap_cb(ops, priv, va, merge);
117862306a36Sopenharmony_ci				if (ret)
117962306a36Sopenharmony_ci					return ret;
118062306a36Sopenharmony_ci				break;
118162306a36Sopenharmony_ci			}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci			if (end < req_end) {
118462306a36Sopenharmony_ci				ret = op_unmap_cb(ops, priv, va, merge);
118562306a36Sopenharmony_ci				if (ret)
118662306a36Sopenharmony_ci					return ret;
118762306a36Sopenharmony_ci				continue;
118862306a36Sopenharmony_ci			}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci			if (end > req_end) {
119162306a36Sopenharmony_ci				struct drm_gpuva_op_map n = {
119262306a36Sopenharmony_ci					.va.addr = req_end,
119362306a36Sopenharmony_ci					.va.range = end - req_end,
119462306a36Sopenharmony_ci					.gem.obj = obj,
119562306a36Sopenharmony_ci					.gem.offset = offset + req_end - addr,
119662306a36Sopenharmony_ci				};
119762306a36Sopenharmony_ci				struct drm_gpuva_op_unmap u = {
119862306a36Sopenharmony_ci					.va = va,
119962306a36Sopenharmony_ci					.keep = merge,
120062306a36Sopenharmony_ci				};
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci				ret = op_remap_cb(ops, priv, NULL, &n, &u);
120362306a36Sopenharmony_ci				if (ret)
120462306a36Sopenharmony_ci					return ret;
120562306a36Sopenharmony_ci				break;
120662306a36Sopenharmony_ci			}
120762306a36Sopenharmony_ci		}
120862306a36Sopenharmony_ci	}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	return op_map_cb(ops, priv,
121162306a36Sopenharmony_ci			 req_addr, req_range,
121262306a36Sopenharmony_ci			 req_obj, req_offset);
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_cistatic int
121662306a36Sopenharmony_ci__drm_gpuva_sm_unmap(struct drm_gpuva_manager *mgr,
121762306a36Sopenharmony_ci		     const struct drm_gpuva_fn_ops *ops, void *priv,
121862306a36Sopenharmony_ci		     u64 req_addr, u64 req_range)
121962306a36Sopenharmony_ci{
122062306a36Sopenharmony_ci	struct drm_gpuva *va, *next;
122162306a36Sopenharmony_ci	u64 req_end = req_addr + req_range;
122262306a36Sopenharmony_ci	int ret;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	if (unlikely(!drm_gpuva_range_valid(mgr, req_addr, req_range)))
122562306a36Sopenharmony_ci		return -EINVAL;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	drm_gpuva_for_each_va_range_safe(va, next, mgr, req_addr, req_end) {
122862306a36Sopenharmony_ci		struct drm_gpuva_op_map prev = {}, next = {};
122962306a36Sopenharmony_ci		bool prev_split = false, next_split = false;
123062306a36Sopenharmony_ci		struct drm_gem_object *obj = va->gem.obj;
123162306a36Sopenharmony_ci		u64 offset = va->gem.offset;
123262306a36Sopenharmony_ci		u64 addr = va->va.addr;
123362306a36Sopenharmony_ci		u64 range = va->va.range;
123462306a36Sopenharmony_ci		u64 end = addr + range;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci		if (addr < req_addr) {
123762306a36Sopenharmony_ci			prev.va.addr = addr;
123862306a36Sopenharmony_ci			prev.va.range = req_addr - addr;
123962306a36Sopenharmony_ci			prev.gem.obj = obj;
124062306a36Sopenharmony_ci			prev.gem.offset = offset;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci			prev_split = true;
124362306a36Sopenharmony_ci		}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci		if (end > req_end) {
124662306a36Sopenharmony_ci			next.va.addr = req_end;
124762306a36Sopenharmony_ci			next.va.range = end - req_end;
124862306a36Sopenharmony_ci			next.gem.obj = obj;
124962306a36Sopenharmony_ci			next.gem.offset = offset + (req_end - addr);
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci			next_split = true;
125262306a36Sopenharmony_ci		}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci		if (prev_split || next_split) {
125562306a36Sopenharmony_ci			struct drm_gpuva_op_unmap unmap = { .va = va };
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci			ret = op_remap_cb(ops, priv,
125862306a36Sopenharmony_ci					  prev_split ? &prev : NULL,
125962306a36Sopenharmony_ci					  next_split ? &next : NULL,
126062306a36Sopenharmony_ci					  &unmap);
126162306a36Sopenharmony_ci			if (ret)
126262306a36Sopenharmony_ci				return ret;
126362306a36Sopenharmony_ci		} else {
126462306a36Sopenharmony_ci			ret = op_unmap_cb(ops, priv, va, false);
126562306a36Sopenharmony_ci			if (ret)
126662306a36Sopenharmony_ci				return ret;
126762306a36Sopenharmony_ci		}
126862306a36Sopenharmony_ci	}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	return 0;
127162306a36Sopenharmony_ci}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci/**
127462306a36Sopenharmony_ci * drm_gpuva_sm_map() - creates the &drm_gpuva_op split/merge steps
127562306a36Sopenharmony_ci * @mgr: the &drm_gpuva_manager representing the GPU VA space
127662306a36Sopenharmony_ci * @req_addr: the start address of the new mapping
127762306a36Sopenharmony_ci * @req_range: the range of the new mapping
127862306a36Sopenharmony_ci * @req_obj: the &drm_gem_object to map
127962306a36Sopenharmony_ci * @req_offset: the offset within the &drm_gem_object
128062306a36Sopenharmony_ci * @priv: pointer to a driver private data structure
128162306a36Sopenharmony_ci *
128262306a36Sopenharmony_ci * This function iterates the given range of the GPU VA space. It utilizes the
128362306a36Sopenharmony_ci * &drm_gpuva_fn_ops to call back into the driver providing the split and merge
128462306a36Sopenharmony_ci * steps.
128562306a36Sopenharmony_ci *
128662306a36Sopenharmony_ci * Drivers may use these callbacks to update the GPU VA space right away within
128762306a36Sopenharmony_ci * the callback. In case the driver decides to copy and store the operations for
128862306a36Sopenharmony_ci * later processing neither this function nor &drm_gpuva_sm_unmap is allowed to
128962306a36Sopenharmony_ci * be called before the &drm_gpuva_manager's view of the GPU VA space was
129062306a36Sopenharmony_ci * updated with the previous set of operations. To update the
129162306a36Sopenharmony_ci * &drm_gpuva_manager's view of the GPU VA space drm_gpuva_insert(),
129262306a36Sopenharmony_ci * drm_gpuva_destroy_locked() and/or drm_gpuva_destroy_unlocked() should be
129362306a36Sopenharmony_ci * used.
129462306a36Sopenharmony_ci *
129562306a36Sopenharmony_ci * A sequence of callbacks can contain map, unmap and remap operations, but
129662306a36Sopenharmony_ci * the sequence of callbacks might also be empty if no operation is required,
129762306a36Sopenharmony_ci * e.g. if the requested mapping already exists in the exact same way.
129862306a36Sopenharmony_ci *
129962306a36Sopenharmony_ci * There can be an arbitrary amount of unmap operations, a maximum of two remap
130062306a36Sopenharmony_ci * operations and a single map operation. The latter one represents the original
130162306a36Sopenharmony_ci * map operation requested by the caller.
130262306a36Sopenharmony_ci *
130362306a36Sopenharmony_ci * Returns: 0 on success or a negative error code
130462306a36Sopenharmony_ci */
130562306a36Sopenharmony_ciint
130662306a36Sopenharmony_cidrm_gpuva_sm_map(struct drm_gpuva_manager *mgr, void *priv,
130762306a36Sopenharmony_ci		 u64 req_addr, u64 req_range,
130862306a36Sopenharmony_ci		 struct drm_gem_object *req_obj, u64 req_offset)
130962306a36Sopenharmony_ci{
131062306a36Sopenharmony_ci	const struct drm_gpuva_fn_ops *ops = mgr->ops;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	if (unlikely(!(ops && ops->sm_step_map &&
131362306a36Sopenharmony_ci		       ops->sm_step_remap &&
131462306a36Sopenharmony_ci		       ops->sm_step_unmap)))
131562306a36Sopenharmony_ci		return -EINVAL;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	return __drm_gpuva_sm_map(mgr, ops, priv,
131862306a36Sopenharmony_ci				  req_addr, req_range,
131962306a36Sopenharmony_ci				  req_obj, req_offset);
132062306a36Sopenharmony_ci}
132162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_sm_map);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci/**
132462306a36Sopenharmony_ci * drm_gpuva_sm_unmap() - creates the &drm_gpuva_ops to split on unmap
132562306a36Sopenharmony_ci * @mgr: the &drm_gpuva_manager representing the GPU VA space
132662306a36Sopenharmony_ci * @priv: pointer to a driver private data structure
132762306a36Sopenharmony_ci * @req_addr: the start address of the range to unmap
132862306a36Sopenharmony_ci * @req_range: the range of the mappings to unmap
132962306a36Sopenharmony_ci *
133062306a36Sopenharmony_ci * This function iterates the given range of the GPU VA space. It utilizes the
133162306a36Sopenharmony_ci * &drm_gpuva_fn_ops to call back into the driver providing the operations to
133262306a36Sopenharmony_ci * unmap and, if required, split existent mappings.
133362306a36Sopenharmony_ci *
133462306a36Sopenharmony_ci * Drivers may use these callbacks to update the GPU VA space right away within
133562306a36Sopenharmony_ci * the callback. In case the driver decides to copy and store the operations for
133662306a36Sopenharmony_ci * later processing neither this function nor &drm_gpuva_sm_map is allowed to be
133762306a36Sopenharmony_ci * called before the &drm_gpuva_manager's view of the GPU VA space was updated
133862306a36Sopenharmony_ci * with the previous set of operations. To update the &drm_gpuva_manager's view
133962306a36Sopenharmony_ci * of the GPU VA space drm_gpuva_insert(), drm_gpuva_destroy_locked() and/or
134062306a36Sopenharmony_ci * drm_gpuva_destroy_unlocked() should be used.
134162306a36Sopenharmony_ci *
134262306a36Sopenharmony_ci * A sequence of callbacks can contain unmap and remap operations, depending on
134362306a36Sopenharmony_ci * whether there are actual overlapping mappings to split.
134462306a36Sopenharmony_ci *
134562306a36Sopenharmony_ci * There can be an arbitrary amount of unmap operations and a maximum of two
134662306a36Sopenharmony_ci * remap operations.
134762306a36Sopenharmony_ci *
134862306a36Sopenharmony_ci * Returns: 0 on success or a negative error code
134962306a36Sopenharmony_ci */
135062306a36Sopenharmony_ciint
135162306a36Sopenharmony_cidrm_gpuva_sm_unmap(struct drm_gpuva_manager *mgr, void *priv,
135262306a36Sopenharmony_ci		   u64 req_addr, u64 req_range)
135362306a36Sopenharmony_ci{
135462306a36Sopenharmony_ci	const struct drm_gpuva_fn_ops *ops = mgr->ops;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	if (unlikely(!(ops && ops->sm_step_remap &&
135762306a36Sopenharmony_ci		       ops->sm_step_unmap)))
135862306a36Sopenharmony_ci		return -EINVAL;
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	return __drm_gpuva_sm_unmap(mgr, ops, priv,
136162306a36Sopenharmony_ci				    req_addr, req_range);
136262306a36Sopenharmony_ci}
136362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_sm_unmap);
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_cistatic struct drm_gpuva_op *
136662306a36Sopenharmony_cigpuva_op_alloc(struct drm_gpuva_manager *mgr)
136762306a36Sopenharmony_ci{
136862306a36Sopenharmony_ci	const struct drm_gpuva_fn_ops *fn = mgr->ops;
136962306a36Sopenharmony_ci	struct drm_gpuva_op *op;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	if (fn && fn->op_alloc)
137262306a36Sopenharmony_ci		op = fn->op_alloc();
137362306a36Sopenharmony_ci	else
137462306a36Sopenharmony_ci		op = kzalloc(sizeof(*op), GFP_KERNEL);
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	if (unlikely(!op))
137762306a36Sopenharmony_ci		return NULL;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	return op;
138062306a36Sopenharmony_ci}
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_cistatic void
138362306a36Sopenharmony_cigpuva_op_free(struct drm_gpuva_manager *mgr,
138462306a36Sopenharmony_ci	      struct drm_gpuva_op *op)
138562306a36Sopenharmony_ci{
138662306a36Sopenharmony_ci	const struct drm_gpuva_fn_ops *fn = mgr->ops;
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	if (fn && fn->op_free)
138962306a36Sopenharmony_ci		fn->op_free(op);
139062306a36Sopenharmony_ci	else
139162306a36Sopenharmony_ci		kfree(op);
139262306a36Sopenharmony_ci}
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_cistatic int
139562306a36Sopenharmony_cidrm_gpuva_sm_step(struct drm_gpuva_op *__op,
139662306a36Sopenharmony_ci		  void *priv)
139762306a36Sopenharmony_ci{
139862306a36Sopenharmony_ci	struct {
139962306a36Sopenharmony_ci		struct drm_gpuva_manager *mgr;
140062306a36Sopenharmony_ci		struct drm_gpuva_ops *ops;
140162306a36Sopenharmony_ci	} *args = priv;
140262306a36Sopenharmony_ci	struct drm_gpuva_manager *mgr = args->mgr;
140362306a36Sopenharmony_ci	struct drm_gpuva_ops *ops = args->ops;
140462306a36Sopenharmony_ci	struct drm_gpuva_op *op;
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	op = gpuva_op_alloc(mgr);
140762306a36Sopenharmony_ci	if (unlikely(!op))
140862306a36Sopenharmony_ci		goto err;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	memcpy(op, __op, sizeof(*op));
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	if (op->op == DRM_GPUVA_OP_REMAP) {
141362306a36Sopenharmony_ci		struct drm_gpuva_op_remap *__r = &__op->remap;
141462306a36Sopenharmony_ci		struct drm_gpuva_op_remap *r = &op->remap;
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci		r->unmap = kmemdup(__r->unmap, sizeof(*r->unmap),
141762306a36Sopenharmony_ci				   GFP_KERNEL);
141862306a36Sopenharmony_ci		if (unlikely(!r->unmap))
141962306a36Sopenharmony_ci			goto err_free_op;
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci		if (__r->prev) {
142262306a36Sopenharmony_ci			r->prev = kmemdup(__r->prev, sizeof(*r->prev),
142362306a36Sopenharmony_ci					  GFP_KERNEL);
142462306a36Sopenharmony_ci			if (unlikely(!r->prev))
142562306a36Sopenharmony_ci				goto err_free_unmap;
142662306a36Sopenharmony_ci		}
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci		if (__r->next) {
142962306a36Sopenharmony_ci			r->next = kmemdup(__r->next, sizeof(*r->next),
143062306a36Sopenharmony_ci					  GFP_KERNEL);
143162306a36Sopenharmony_ci			if (unlikely(!r->next))
143262306a36Sopenharmony_ci				goto err_free_prev;
143362306a36Sopenharmony_ci		}
143462306a36Sopenharmony_ci	}
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	list_add_tail(&op->entry, &ops->list);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	return 0;
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_cierr_free_unmap:
144162306a36Sopenharmony_ci	kfree(op->remap.unmap);
144262306a36Sopenharmony_cierr_free_prev:
144362306a36Sopenharmony_ci	kfree(op->remap.prev);
144462306a36Sopenharmony_cierr_free_op:
144562306a36Sopenharmony_ci	gpuva_op_free(mgr, op);
144662306a36Sopenharmony_cierr:
144762306a36Sopenharmony_ci	return -ENOMEM;
144862306a36Sopenharmony_ci}
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_cistatic const struct drm_gpuva_fn_ops gpuva_list_ops = {
145162306a36Sopenharmony_ci	.sm_step_map = drm_gpuva_sm_step,
145262306a36Sopenharmony_ci	.sm_step_remap = drm_gpuva_sm_step,
145362306a36Sopenharmony_ci	.sm_step_unmap = drm_gpuva_sm_step,
145462306a36Sopenharmony_ci};
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci/**
145762306a36Sopenharmony_ci * drm_gpuva_sm_map_ops_create() - creates the &drm_gpuva_ops to split and merge
145862306a36Sopenharmony_ci * @mgr: the &drm_gpuva_manager representing the GPU VA space
145962306a36Sopenharmony_ci * @req_addr: the start address of the new mapping
146062306a36Sopenharmony_ci * @req_range: the range of the new mapping
146162306a36Sopenharmony_ci * @req_obj: the &drm_gem_object to map
146262306a36Sopenharmony_ci * @req_offset: the offset within the &drm_gem_object
146362306a36Sopenharmony_ci *
146462306a36Sopenharmony_ci * This function creates a list of operations to perform splitting and merging
146562306a36Sopenharmony_ci * of existent mapping(s) with the newly requested one.
146662306a36Sopenharmony_ci *
146762306a36Sopenharmony_ci * The list can be iterated with &drm_gpuva_for_each_op and must be processed
146862306a36Sopenharmony_ci * in the given order. It can contain map, unmap and remap operations, but it
146962306a36Sopenharmony_ci * also can be empty if no operation is required, e.g. if the requested mapping
147062306a36Sopenharmony_ci * already exists is the exact same way.
147162306a36Sopenharmony_ci *
147262306a36Sopenharmony_ci * There can be an arbitrary amount of unmap operations, a maximum of two remap
147362306a36Sopenharmony_ci * operations and a single map operation. The latter one represents the original
147462306a36Sopenharmony_ci * map operation requested by the caller.
147562306a36Sopenharmony_ci *
147662306a36Sopenharmony_ci * Note that before calling this function again with another mapping request it
147762306a36Sopenharmony_ci * is necessary to update the &drm_gpuva_manager's view of the GPU VA space. The
147862306a36Sopenharmony_ci * previously obtained operations must be either processed or abandoned. To
147962306a36Sopenharmony_ci * update the &drm_gpuva_manager's view of the GPU VA space drm_gpuva_insert(),
148062306a36Sopenharmony_ci * drm_gpuva_destroy_locked() and/or drm_gpuva_destroy_unlocked() should be
148162306a36Sopenharmony_ci * used.
148262306a36Sopenharmony_ci *
148362306a36Sopenharmony_ci * After the caller finished processing the returned &drm_gpuva_ops, they must
148462306a36Sopenharmony_ci * be freed with &drm_gpuva_ops_free.
148562306a36Sopenharmony_ci *
148662306a36Sopenharmony_ci * Returns: a pointer to the &drm_gpuva_ops on success, an ERR_PTR on failure
148762306a36Sopenharmony_ci */
148862306a36Sopenharmony_cistruct drm_gpuva_ops *
148962306a36Sopenharmony_cidrm_gpuva_sm_map_ops_create(struct drm_gpuva_manager *mgr,
149062306a36Sopenharmony_ci			    u64 req_addr, u64 req_range,
149162306a36Sopenharmony_ci			    struct drm_gem_object *req_obj, u64 req_offset)
149262306a36Sopenharmony_ci{
149362306a36Sopenharmony_ci	struct drm_gpuva_ops *ops;
149462306a36Sopenharmony_ci	struct {
149562306a36Sopenharmony_ci		struct drm_gpuva_manager *mgr;
149662306a36Sopenharmony_ci		struct drm_gpuva_ops *ops;
149762306a36Sopenharmony_ci	} args;
149862306a36Sopenharmony_ci	int ret;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	ops = kzalloc(sizeof(*ops), GFP_KERNEL);
150162306a36Sopenharmony_ci	if (unlikely(!ops))
150262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	INIT_LIST_HEAD(&ops->list);
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	args.mgr = mgr;
150762306a36Sopenharmony_ci	args.ops = ops;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	ret = __drm_gpuva_sm_map(mgr, &gpuva_list_ops, &args,
151062306a36Sopenharmony_ci				 req_addr, req_range,
151162306a36Sopenharmony_ci				 req_obj, req_offset);
151262306a36Sopenharmony_ci	if (ret)
151362306a36Sopenharmony_ci		goto err_free_ops;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	return ops;
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_cierr_free_ops:
151862306a36Sopenharmony_ci	drm_gpuva_ops_free(mgr, ops);
151962306a36Sopenharmony_ci	return ERR_PTR(ret);
152062306a36Sopenharmony_ci}
152162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_sm_map_ops_create);
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci/**
152462306a36Sopenharmony_ci * drm_gpuva_sm_unmap_ops_create() - creates the &drm_gpuva_ops to split on
152562306a36Sopenharmony_ci * unmap
152662306a36Sopenharmony_ci * @mgr: the &drm_gpuva_manager representing the GPU VA space
152762306a36Sopenharmony_ci * @req_addr: the start address of the range to unmap
152862306a36Sopenharmony_ci * @req_range: the range of the mappings to unmap
152962306a36Sopenharmony_ci *
153062306a36Sopenharmony_ci * This function creates a list of operations to perform unmapping and, if
153162306a36Sopenharmony_ci * required, splitting of the mappings overlapping the unmap range.
153262306a36Sopenharmony_ci *
153362306a36Sopenharmony_ci * The list can be iterated with &drm_gpuva_for_each_op and must be processed
153462306a36Sopenharmony_ci * in the given order. It can contain unmap and remap operations, depending on
153562306a36Sopenharmony_ci * whether there are actual overlapping mappings to split.
153662306a36Sopenharmony_ci *
153762306a36Sopenharmony_ci * There can be an arbitrary amount of unmap operations and a maximum of two
153862306a36Sopenharmony_ci * remap operations.
153962306a36Sopenharmony_ci *
154062306a36Sopenharmony_ci * Note that before calling this function again with another range to unmap it
154162306a36Sopenharmony_ci * is necessary to update the &drm_gpuva_manager's view of the GPU VA space. The
154262306a36Sopenharmony_ci * previously obtained operations must be processed or abandoned. To update the
154362306a36Sopenharmony_ci * &drm_gpuva_manager's view of the GPU VA space drm_gpuva_insert(),
154462306a36Sopenharmony_ci * drm_gpuva_destroy_locked() and/or drm_gpuva_destroy_unlocked() should be
154562306a36Sopenharmony_ci * used.
154662306a36Sopenharmony_ci *
154762306a36Sopenharmony_ci * After the caller finished processing the returned &drm_gpuva_ops, they must
154862306a36Sopenharmony_ci * be freed with &drm_gpuva_ops_free.
154962306a36Sopenharmony_ci *
155062306a36Sopenharmony_ci * Returns: a pointer to the &drm_gpuva_ops on success, an ERR_PTR on failure
155162306a36Sopenharmony_ci */
155262306a36Sopenharmony_cistruct drm_gpuva_ops *
155362306a36Sopenharmony_cidrm_gpuva_sm_unmap_ops_create(struct drm_gpuva_manager *mgr,
155462306a36Sopenharmony_ci			      u64 req_addr, u64 req_range)
155562306a36Sopenharmony_ci{
155662306a36Sopenharmony_ci	struct drm_gpuva_ops *ops;
155762306a36Sopenharmony_ci	struct {
155862306a36Sopenharmony_ci		struct drm_gpuva_manager *mgr;
155962306a36Sopenharmony_ci		struct drm_gpuva_ops *ops;
156062306a36Sopenharmony_ci	} args;
156162306a36Sopenharmony_ci	int ret;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	ops = kzalloc(sizeof(*ops), GFP_KERNEL);
156462306a36Sopenharmony_ci	if (unlikely(!ops))
156562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	INIT_LIST_HEAD(&ops->list);
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	args.mgr = mgr;
157062306a36Sopenharmony_ci	args.ops = ops;
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	ret = __drm_gpuva_sm_unmap(mgr, &gpuva_list_ops, &args,
157362306a36Sopenharmony_ci				   req_addr, req_range);
157462306a36Sopenharmony_ci	if (ret)
157562306a36Sopenharmony_ci		goto err_free_ops;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	return ops;
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_cierr_free_ops:
158062306a36Sopenharmony_ci	drm_gpuva_ops_free(mgr, ops);
158162306a36Sopenharmony_ci	return ERR_PTR(ret);
158262306a36Sopenharmony_ci}
158362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_sm_unmap_ops_create);
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci/**
158662306a36Sopenharmony_ci * drm_gpuva_prefetch_ops_create() - creates the &drm_gpuva_ops to prefetch
158762306a36Sopenharmony_ci * @mgr: the &drm_gpuva_manager representing the GPU VA space
158862306a36Sopenharmony_ci * @addr: the start address of the range to prefetch
158962306a36Sopenharmony_ci * @range: the range of the mappings to prefetch
159062306a36Sopenharmony_ci *
159162306a36Sopenharmony_ci * This function creates a list of operations to perform prefetching.
159262306a36Sopenharmony_ci *
159362306a36Sopenharmony_ci * The list can be iterated with &drm_gpuva_for_each_op and must be processed
159462306a36Sopenharmony_ci * in the given order. It can contain prefetch operations.
159562306a36Sopenharmony_ci *
159662306a36Sopenharmony_ci * There can be an arbitrary amount of prefetch operations.
159762306a36Sopenharmony_ci *
159862306a36Sopenharmony_ci * After the caller finished processing the returned &drm_gpuva_ops, they must
159962306a36Sopenharmony_ci * be freed with &drm_gpuva_ops_free.
160062306a36Sopenharmony_ci *
160162306a36Sopenharmony_ci * Returns: a pointer to the &drm_gpuva_ops on success, an ERR_PTR on failure
160262306a36Sopenharmony_ci */
160362306a36Sopenharmony_cistruct drm_gpuva_ops *
160462306a36Sopenharmony_cidrm_gpuva_prefetch_ops_create(struct drm_gpuva_manager *mgr,
160562306a36Sopenharmony_ci			      u64 addr, u64 range)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	struct drm_gpuva_ops *ops;
160862306a36Sopenharmony_ci	struct drm_gpuva_op *op;
160962306a36Sopenharmony_ci	struct drm_gpuva *va;
161062306a36Sopenharmony_ci	u64 end = addr + range;
161162306a36Sopenharmony_ci	int ret;
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	ops = kzalloc(sizeof(*ops), GFP_KERNEL);
161462306a36Sopenharmony_ci	if (!ops)
161562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	INIT_LIST_HEAD(&ops->list);
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	drm_gpuva_for_each_va_range(va, mgr, addr, end) {
162062306a36Sopenharmony_ci		op = gpuva_op_alloc(mgr);
162162306a36Sopenharmony_ci		if (!op) {
162262306a36Sopenharmony_ci			ret = -ENOMEM;
162362306a36Sopenharmony_ci			goto err_free_ops;
162462306a36Sopenharmony_ci		}
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci		op->op = DRM_GPUVA_OP_PREFETCH;
162762306a36Sopenharmony_ci		op->prefetch.va = va;
162862306a36Sopenharmony_ci		list_add_tail(&op->entry, &ops->list);
162962306a36Sopenharmony_ci	}
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	return ops;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_cierr_free_ops:
163462306a36Sopenharmony_ci	drm_gpuva_ops_free(mgr, ops);
163562306a36Sopenharmony_ci	return ERR_PTR(ret);
163662306a36Sopenharmony_ci}
163762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_prefetch_ops_create);
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci/**
164062306a36Sopenharmony_ci * drm_gpuva_gem_unmap_ops_create() - creates the &drm_gpuva_ops to unmap a GEM
164162306a36Sopenharmony_ci * @mgr: the &drm_gpuva_manager representing the GPU VA space
164262306a36Sopenharmony_ci * @obj: the &drm_gem_object to unmap
164362306a36Sopenharmony_ci *
164462306a36Sopenharmony_ci * This function creates a list of operations to perform unmapping for every
164562306a36Sopenharmony_ci * GPUVA attached to a GEM.
164662306a36Sopenharmony_ci *
164762306a36Sopenharmony_ci * The list can be iterated with &drm_gpuva_for_each_op and consists out of an
164862306a36Sopenharmony_ci * arbitrary amount of unmap operations.
164962306a36Sopenharmony_ci *
165062306a36Sopenharmony_ci * After the caller finished processing the returned &drm_gpuva_ops, they must
165162306a36Sopenharmony_ci * be freed with &drm_gpuva_ops_free.
165262306a36Sopenharmony_ci *
165362306a36Sopenharmony_ci * It is the callers responsibility to protect the GEMs GPUVA list against
165462306a36Sopenharmony_ci * concurrent access using the GEMs dma_resv lock.
165562306a36Sopenharmony_ci *
165662306a36Sopenharmony_ci * Returns: a pointer to the &drm_gpuva_ops on success, an ERR_PTR on failure
165762306a36Sopenharmony_ci */
165862306a36Sopenharmony_cistruct drm_gpuva_ops *
165962306a36Sopenharmony_cidrm_gpuva_gem_unmap_ops_create(struct drm_gpuva_manager *mgr,
166062306a36Sopenharmony_ci			       struct drm_gem_object *obj)
166162306a36Sopenharmony_ci{
166262306a36Sopenharmony_ci	struct drm_gpuva_ops *ops;
166362306a36Sopenharmony_ci	struct drm_gpuva_op *op;
166462306a36Sopenharmony_ci	struct drm_gpuva *va;
166562306a36Sopenharmony_ci	int ret;
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	drm_gem_gpuva_assert_lock_held(obj);
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	ops = kzalloc(sizeof(*ops), GFP_KERNEL);
167062306a36Sopenharmony_ci	if (!ops)
167162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	INIT_LIST_HEAD(&ops->list);
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	drm_gem_for_each_gpuva(va, obj) {
167662306a36Sopenharmony_ci		op = gpuva_op_alloc(mgr);
167762306a36Sopenharmony_ci		if (!op) {
167862306a36Sopenharmony_ci			ret = -ENOMEM;
167962306a36Sopenharmony_ci			goto err_free_ops;
168062306a36Sopenharmony_ci		}
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci		op->op = DRM_GPUVA_OP_UNMAP;
168362306a36Sopenharmony_ci		op->unmap.va = va;
168462306a36Sopenharmony_ci		list_add_tail(&op->entry, &ops->list);
168562306a36Sopenharmony_ci	}
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	return ops;
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_cierr_free_ops:
169062306a36Sopenharmony_ci	drm_gpuva_ops_free(mgr, ops);
169162306a36Sopenharmony_ci	return ERR_PTR(ret);
169262306a36Sopenharmony_ci}
169362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_gem_unmap_ops_create);
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci/**
169662306a36Sopenharmony_ci * drm_gpuva_ops_free() - free the given &drm_gpuva_ops
169762306a36Sopenharmony_ci * @mgr: the &drm_gpuva_manager the ops were created for
169862306a36Sopenharmony_ci * @ops: the &drm_gpuva_ops to free
169962306a36Sopenharmony_ci *
170062306a36Sopenharmony_ci * Frees the given &drm_gpuva_ops structure including all the ops associated
170162306a36Sopenharmony_ci * with it.
170262306a36Sopenharmony_ci */
170362306a36Sopenharmony_civoid
170462306a36Sopenharmony_cidrm_gpuva_ops_free(struct drm_gpuva_manager *mgr,
170562306a36Sopenharmony_ci		   struct drm_gpuva_ops *ops)
170662306a36Sopenharmony_ci{
170762306a36Sopenharmony_ci	struct drm_gpuva_op *op, *next;
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	drm_gpuva_for_each_op_safe(op, next, ops) {
171062306a36Sopenharmony_ci		list_del(&op->entry);
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci		if (op->op == DRM_GPUVA_OP_REMAP) {
171362306a36Sopenharmony_ci			kfree(op->remap.prev);
171462306a36Sopenharmony_ci			kfree(op->remap.next);
171562306a36Sopenharmony_ci			kfree(op->remap.unmap);
171662306a36Sopenharmony_ci		}
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci		gpuva_op_free(mgr, op);
171962306a36Sopenharmony_ci	}
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	kfree(ops);
172262306a36Sopenharmony_ci}
172362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gpuva_ops_free);
1724