SPLIT AND MERGE(9)
π Split and Merge π
okay bestie, this is Split and Merge, section 9 of the Kernel API Manual, and it's giving βGPU VA manager does mapping algebraβ in the most iconic way. β¨
Besides its capability to manage and represent a GPU VA space, the GPU VA manager also provides functions to let drm_gpuvm calculate a sequence of operations to satisfy a given map or unmap request. No cap.
So the DRM GPU VA manager provides an algorithm implementing splitting and merging of existing GPU VA mappings with the ones that are requested to be mapped or unmapped. This feature is required by the Vulkan API to implement Vulkan 'Sparse Memory Bindings' β drivers' UAPIs often refer to this as VM BIND. π«Ά
Drivers can call drm_gpuvm_sm_map to receive a sequence of callbacks containing map, unmap and remap operations for a given newly requested mapping. That sequence of callbacks represents the set of operations to execute in order to integrate the new mapping cleanly into the current state of the GPU VA space. π
Depending on how the new GPU VA mapping intersects with the existing mappings of the GPU VA space, the drm_gpuvm_ops callbacks contain an arbitrary amount of unmap operations, a maximum of two remap operations and a single map operation. The caller might receive no callback at all if no operation is required, e.g. if the requested mapping already exists in the exact same way. iconic no-op behavior, fr fr.
The single map operation represents the original map operation requested by the caller.
drm_gpuva_op_unmap contains a 'keep' field, which indicates whether the drm_gpuva to unmap is physically contiguous with the original mapping request. Optionally, if 'keep' is set, drivers may keep the actual page table entries for this drm_gpuva, adding the missing page table entries only and update the drm_gpuvm's view of things accordingly. π
Drivers may do the same optimization, namely delta page table updates, also for remap operations. This is possible since drm_gpuva_op_remap consists of one unmap operation and one or two map operations, such that drivers can derive the page table update delta accordingly.
Note that there can't be more than two existing mappings to split up β one at the beginning and one at the end of the new mapping β hence there is a maximum of two remap operations. π
Analogous to drm_gpuvm_sm_map, drm_gpuvm_sm_unmap uses drm_gpuvm_ops to call back into the driver in order to unmap a range of GPU VA space. The logic behind this function is way simpler though: for all existing mappings enclosed by the given range, unmap operations are created. For mappings which are only partially located within the given range, remap operations are created such that those mappings are split up and re-mapped partially. π¦
As an alternative to drm_gpuvm_sm_map and drm_gpuvm_sm_unmap, drm_gpuvm_sm_map_ops_create and drm_gpuvm_sm_unmap_ops_create can be used to directly obtain an instance of struct drm_gpuva_ops containing a list of drm_gpuva_op, which can be iterated with drm_gpuva_for_each_op. This list contains the drm_gpuva_ops analogous to the callbacks one would receive when calling drm_gpuvm_sm_map or drm_gpuvm_sm_unmap. While this way requires more memory (to allocate the drm_gpuva_ops), it provides drivers a way to iterate the drm_gpuva_op multiple times, e.g. once in a context where memory allocations are possible (e.g. to allocate GPU page tables) and once in the dma-fence signalling critical path. β¨
To update the drm_gpuvm's view of the GPU VA space, drm_gpuva_insert and drm_gpuva_remove may be used. These functions can safely be used from drm_gpuvm_ops callbacks originating from drm_gpuvm_sm_map or drm_gpuvm_sm_unmap. However, it might be more convenient to use the provided helper functions drm_gpuva_map, drm_gpuva_remap and drm_gpuva_unmap instead. π
The following diagram depicts the basic relationships of existing GPU VA mappings, a newly requested mapping and the resulting mappings as implemented by drm_gpuvm_sm_map β it doesn't cover any arbitrary combinations of these. It's a little art gallery of mapping glow-ups, ngl. πΌοΈβ¨
1. Requested mapping is identical β replace it, but indicate the backing PTEs could be kept π
:
0 a 1
old: |-----------| (bo_offset=n)
0 a 1
req: |-----------| (bo_offset=n)
0 a 1
new: |-----------| (bo_offset=n)
2. Requested mapping is identical, except for the BO offset β hence replace the mapping π«
:
0 a 1
old: |-----------| (bo_offset=n)
0 a 1
req: |-----------| (bo_offset=m)
0 a 1
new: |-----------| (bo_offset=m)
3. Requested mapping is identical, except for the backing BO β hence replace the mapping π¦
:
0 a 1
old: |-----------| (bo_offset=n)
0 b 1
req: |-----------| (bo_offset=n)
0 b 1
new: |-----------| (bo_offset=n)
4. Existent mapping is a left aligned subset of the requested one β hence replace the existing one πΈ
:
0 a 1
old: |-----| (bo_offset=n)
0 a 2
req: |-----------| (bo_offset=n)
0 a 2
new: |-----------| (bo_offset=n)
note: We expect to see the same result for a request with a different BO and/or non-contiguous BO offset. π
5. Requested mapping's range is a left aligned subset of the existing one, but backed by a different BO β hence map the requested mapping and split the existing one, adjusting its BO offset π
:
0 a 2
old: |-----------| (bo_offset=n)
0 b 1
req: |-----| (bo_offset=n)
0 b 1 a' 2
new: |-----|-----| (b.bo_offset=n, a.bo_offset=n+1)
note: We expect to see the same result for a request with a different BO and/or non-contiguous BO offset. π
6. Existent mapping is a superset of the requested mapping β split it up, but indicate that the backing PTEs could be kept β¨
:
0 a 2
old: |-----------| (bo_offset=n)
0 a 1
req: |-----| (bo_offset=n)
0 a 1 a' 2
new: |-----|-----| (a.bo_offset=n, a'.bo_offset=n+1)
7. Requested mapping's range is a right aligned subset of the existing one, but backed by a different BO β hence map the requested mapping and split the existing one, without adjusting the BO offset π
:
0 a 2
old: |-----------| (bo_offset=n)
1 b 2
req: |-----| (bo_offset=m)
0 a 1 b 2
new: |-----|-----| (a.bo_offset=n,b.bo_offset=m)
8. Existent mapping is a superset of the requested mapping β split it up, but indicate that the backing PTEs could be kept π«Ά
:
0 a 2
old: |-----------| (bo_offset=n)
1 a 2
req: |-----| (bo_offset=n+1)
0 a' 1 a 2
new: |-----|-----| (a'.bo_offset=n, a.bo_offset=n+1)
9. Existent mapping is overlapped at the end by the requested mapping backed by a different BO β hence map the requested mapping and split up the existing one, without adjusting the BO offset π₯
:
0 a 2
old: |-----------| (bo_offset=n)
1 b 3
req: |-----------| (bo_offset=m)
0 a 1 b 3
new: |-----|-----------| (a.bo_offset=n,b.bo_offset=m)
10. Existent mapping is overlapped by the requested mapping, both having the same backing BO with a contiguous offset β indicate the backing PTEs of the old mapping could be kept π
:
0 a 2
old: |-----------| (bo_offset=n)
1 a 3
req: |-----------| (bo_offset=n+1)
0 a' 1 a 3
new: |-----|-----------| (a'.bo_offset=n, a.bo_offset=n+1)
11. Requested mapping's range is a centered subset of the existing one having a different backing BO β hence map the requested mapping and split up the existing one in two mappings, adjusting the BO offset of the right one accordingly π§
:
0 a 3
old: |-----------------| (bo_offset=n)
1 b 2
req: |-----| (bo_offset=m)
0 a 1 b 2 a' 3
new: |-----|-----|-----| (a.bo_offset=n,b.bo_offset=m,a'.bo_offset=n+2)
12. Requested mapping is a contiguous subset of the existing one β split it up, but indicate that the backing PTEs could be kept π
:
0 a 3
old: |-----------------| (bo_offset=n)
1 a 2
req: |-----| (bo_offset=n+1)
0 a' 1 a 2 a'' 3
old: |-----|-----|-----| (a'.bo_offset=n, a.bo_offset=n+1, a''.bo_offset=n+2)
13. Existent mapping is a right aligned subset of the requested one β hence replace the existing one β
:
1 a 2
old: |-----| (bo_offset=n+1)
0 a 2
req: |-----------| (bo_offset=n)
0 a 2
new: |-----------| (bo_offset=n)
note: We expect to see the same result for a request with a differentboand/or non-contiguousbo_offset. π
14. Existent mapping is a centered subset of the requested one β hence replace the existing one π
:
1 a 2
old: |-----| (bo_offset=n+1)
0 a 3
req: |----------------| (bo_offset=n)
0 a 3
new: |----------------| (bo_offset=n)
note: We expect to see the same result for a request with a differentboand/or non-contiguousbo_offset. π
15. Existent mappings is overlapped at the beginning by the requested mapping backed by a different BO β hence map the requested mapping and split up the existing one, adjusting its BO offset accordingly π
:
1 a 3
old: |-----------| (bo_offset=n)
0 b 2
req: |-----------| (bo_offset=m)
0 b 2 a' 3
new: |-----------|-----| (b.bo_offset=m,a.bo_offset=n+2)
β¨ SEE ALSO β¨
okay so like, this is the part where I hand you the whole friend group π every single one of these is somebody you're gonna want in your contacts, no cap. Start with the actual source file and the big-picture pages, then we get into the individual besties.
./drivers/gpu/drm/drm_gpuvm.cβ the kernel file, the mothership, the main character herself πOverview(9)β the vibe-setting origin story, read this first frMadvise Logic - Splitting and Traversal(9)β literally the whole reason this page existsLocking(9)β the rules about who gets to touch what and when, please respect them πExamples(9)β actual working code instead of me just telling you things
π§ the vm_bo list helpers (the little goblins)
π§ bo lifecycle & zombie behaviour
drm_gpuvm_bo_is_zombie(9)β yes, things here can be undead, and yes, you have to checkdrm_gpuvm_bo_list_add(9)drm_gpuvm_bo_list_del_init(9)drm_gpuvm_bo_list_del(9)
π©· range + reservation setup
drm_gpuvm_range_valid(9)drm_gpuvm_resv_object_alloc(9)drm_gpuvm_init(9)β the birth certificatedrm_gpuvm_put(9)β the goodbye, pls do it properly
π prepare & lock era
drm_gpuvm_prepare_vm(9)drm_gpuvm_prepare_objects(9)drm_gpuvm_prepare_range(9)drm_gpuvm_exec_lock(9)drm_gpuvm_exec_lock_array(9)drm_gpuvm_exec_lock_range(9)
β validate & fences
drm_gpuvm_validate(9)β vibe check for your buffers, basicallydrm_gpuvm_resv_add_fence(9)
π¦ the bo grab-bag (create / put / find)
drm_gpuvm_bo_create(9)drm_gpuvm_bo_put(9)drm_gpuvm_bo_put_deferred(9)drm_gpuvm_bo_deferred_cleanup(9)drm_gpuvm_bo_find(9)drm_gpuvm_bo_obtain_locked(9)drm_gpuvm_bo_obtain_prealloc(9)drm_gpuvm_bo_extobj_add(9)drm_gpuvm_bo_evict(9)
πΈ raw gpuva operations (no thoughts, just insert and remove)
drm_gpuva_insert(9)drm_gpuva_remove(9)drm_gpuva_link(9)drm_gpuva_unlink(9)drm_gpuva_unlink_defer(9)
π finding things + empty intervals
π map / remap / unmap trio
π split & merge, the actual headline acts
drm_gpuvm_sm_map(9)drm_gpuvm_sm_unmap(9)drm_gpuvm_sm_map_exec_lock(9)drm_gpuvm_sm_unmap_exec_lock(9)
π«Ά ops create / free squad
drm_gpuvm_sm_map_ops_create(9)drm_gpuvm_madvise_ops_create(9)drm_gpuvm_sm_unmap_ops_create(9)drm_gpuvm_prefetch_ops_create(9)drm_gpuvm_bo_unmap_ops_create(9)drm_gpuva_ops_free(9)β always clean up, I'm begging π
bestie that's 49 functions plus the kernel file and four overview pages. if you only remember one thing: read Overview(9) before you go poking at anything in here, okay? iykyk π
and that's a wrap on part 2!! π this whole thing lives under the source path ./drivers/gpu/drm, dated October 2026, and it's the Split and Merge(9) manual page β go forth and merge responsibly, my love β¨π¦π
π¦ where this page comes from
- nixpkgs unstable: linux-manual 7.3.1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms