Examples(9)
β¨ Examples(9) β Kernel API Manual β¨
Okay bestie, this is Examples(9) from the Kernel API Manual, and itβs giving βtwo very specific DRM GPUVA Manager examplesβ energy. π¦
This section gives two examples on how to let the DRM GPUVA Manager generate drm_gpuva_op in order to satisfy a given map or unmap request and how to make use of them. Periodt.
The code below is strictly limited to illustrate the generic usage pattern. To maintain simplicity, it doesn't make use of any abstractions for common code, different (asynchronous) stages with fence signalling critical paths, any other helpers or error handling in terms of freeing memory and dropping previously taken locks. Not me warning you that this is snack-sized, not a full meal π
1) Obtain a list of drm_gpuva_op to create a new mapping
This one is the list-first vibe: you get a list of drm_gpuva_ops, then walk it. The snippet shows driver_gpuva_alloc(), driver_mapping_create(), the VA-space lock/unlock, drm_gpuvm_sm_map_ops_create(), drm_gpuvm_bo_obtain(), drm_gpuva_for_each_op(), and the DRM_GPUVA_OP_MAP, DRM_GPUVA_OP_REMAP, DRM_GPUVA_OP_UNMAP, and default cases. Slay. π
// Allocates a new drm_gpuva.
struct drm_gpuva * driver_gpuva_alloc(void);
// Typically drivers would embed the drm_gpuvm and drm_gpuva
// structure in individual driver structures and lock the dma-resv with
// drm_exec or similar helpers.
int driver_mapping_create(struct drm_gpuvm *gpuvm,
u64 addr, u64 range,
struct drm_gem_object *obj, u64 offset)
{
struct drm_gpuvm_map_req map_req = {
.map.va.addr = addr,
.map.va.range = range,
.map.gem.obj = obj,
.map.gem.offset = offset,
};
struct drm_gpuva_ops *ops;
struct drm_gpuva_op *op
struct drm_gpuvm_bo *vm_bo;
driver_lock_va_space;
ops = drm_gpuvm_sm_map_ops_create(gpuvm, map_req);
if (IS_ERR(ops))
return PTR_ERR(ops);
vm_bo = drm_gpuvm_bo_obtain(gpuvm, obj);
if (IS_ERR(vm_bo))
return PTR_ERR(vm_bo);
drm_gpuva_for_each_op(op, ops) {
struct drm_gpuva *va;
switch (op->op) {
case DRM_GPUVA_OP_MAP:
va = driver_gpuva_alloc;
if (!va)
; // unwind previous VA space updates,
// free memory and unlock
driver_vm_map;
drm_gpuva_map(gpuvm, va, op->map);
drm_gpuva_link(va, vm_bo);
break;
case DRM_GPUVA_OP_REMAP: {
struct drm_gpuva *prev = NULL, *next = NULL;
va = op->remap.unmap->va;
if (op->remap.prev) {
prev = driver_gpuva_alloc;
if (!prev)
; // unwind previous VA space
// updates, free memory and
// unlock
}
if (op->remap.next) {
next = driver_gpuva_alloc;
if (!next)
; // unwind previous VA space
// updates, free memory and
// unlock
}
driver_vm_remap;
drm_gpuva_remap(prev, next, op->remap);
if (prev)
drm_gpuva_link(prev, va->vm_bo);
if (next)
drm_gpuva_link(next, va->vm_bo);
drm_gpuva_unlink(va);
break;
}
case DRM_GPUVA_OP_UNMAP:
va = op->unmap->va;
driver_vm_unmap;
drm_gpuva_unlink(va);
drm_gpuva_unmap(op->unmap);
break;
default:
break;
}
}
drm_gpuvm_bo_put(vm_bo);
driver_unlock_va_space;
return 0;
}
2) Receive a callback for each drm_gpuva_op to create a new mapping
This one is callback-core: struct driver_context holds gpuvm, vm_bo, new_va, prev_va, and next_va. The driver_gpuvm_ops struct sets .sm_step_map = driver_gpuva_map, .sm_step_remap = driver_gpuva_remap, and .sm_step_unmap = driver_gpuva_unmap. The driver_mapping_create() function does kzalloc(..., GFP_KERNEL) for new_va, prev_va, and next_va; calls drm_gpuvm_bo_create(gpuvm, obj); checks !ctx.new_va || !ctx.prev_va || !ctx.next_va || !vm_bo; sets ret = -ENOMEM; uses drm_gpuvm_bo_obtain_prealloc(ctx.vm_bo); locks; calls drm_gpuvm_sm_map(gpuvm, ctx, addr, range, obj, offset); unlocks; and at out: does drm_gpuvm_bo_put(ctx.vm_bo) plus kfree on all three VAs. The callbacks are driver_gpuva_map, driver_gpuva_remap, and driver_gpuva_unmap. No cap, itβs all right there. π
struct driver_context {
struct drm_gpuvm *gpuvm;
struct drm_gpuvm_bo *vm_bo;
struct drm_gpuva *new_va;
struct drm_gpuva *prev_va;
struct drm_gpuva *next_va;
};
// ops to pass to drm_gpuvm_init
static const struct drm_gpuvm_ops driver_gpuvm_ops = {
.sm_step_map = driver_gpuva_map,
.sm_step_remap = driver_gpuva_remap,
.sm_step_unmap = driver_gpuva_unmap,
};
// Typically drivers would embed the drm_gpuvm and drm_gpuva
// structure in individual driver structures and lock the dma-resv with
// drm_exec or similar helpers.
int driver_mapping_create(struct drm_gpuvm *gpuvm,
u64 addr, u64 range,
struct drm_gem_object *obj, u64 offset)
{
struct driver_context ctx;
struct drm_gpuvm_bo *vm_bo;
struct drm_gpuva_ops *ops;
struct drm_gpuva_op *op;
int ret = 0;
ctx.gpuvm = gpuvm;
ctx.new_va = kzalloc(sizeof(*ctx.new_va), GFP_KERNEL);
ctx.prev_va = kzalloc(sizeof(*ctx.prev_va), GFP_KERNEL);
ctx.next_va = kzalloc(sizeof(*ctx.next_va), GFP_KERNEL);
ctx.vm_bo = drm_gpuvm_bo_create(gpuvm, obj);
if (!ctx.new_va || !ctx.prev_va || !ctx.next_va || !vm_bo) {
ret = -ENOMEM;
goto out;
}
// Typically protected with a driver specific GEM gpuva lock
// used in the fence signaling path for drm_gpuva_link and
// drm_gpuva_unlink, hence pre-allocate.
ctx.vm_bo = drm_gpuvm_bo_obtain_prealloc(ctx.vm_bo);
driver_lock_va_space;
ret = drm_gpuvm_sm_map(gpuvm, ctx, addr, range, obj, offset);
driver_unlock_va_space;
out:
drm_gpuvm_bo_put(ctx.vm_bo);
kfree(ctx.new_va);
kfree(ctx.prev_va);
kfree(ctx.next_va);
return ret;
}
int driver_gpuva_map(struct drm_gpuva_op *op, void *__ctx)
{
struct driver_context *ctx = __ctx;
drm_gpuva_map(ctx->vm, ctx->new_va, op->map);
drm_gpuva_link(ctx->new_va, ctx->vm_bo);
// prevent the new GPUVA from being freed in
// driver_mapping_create
ctx->new_va = NULL;
return 0;
}
int driver_gpuva_remap(struct drm_gpuva_op *op, void *__ctx)
{
struct driver_context *ctx = __ctx;
struct drm_gpuva *va = op->remap.unmap->va;
drm_gpuva_remap(ctx->prev_va, ctx->next_va, op->remap);
if (op->remap.prev) {
drm_gpuva_link(ctx->prev_va, va->vm_bo);
ctx->prev_va = NULL;
}
if (op->remap.next) {
drm_gpuva_link(ctx->next_va, va->vm_bo);
ctx->next_va = NULL;
}
drm_gpuva_unlink(va);
kfree(va);
return 0;
}
int driver_gpuva_unmap(struct drm_gpuva_op *op, void *__ctx)
{
drm_gpuva_unlink(op->unmap.va);
drm_gpuva_unmap(op->unmap);
kfree(op->unmap.va);
return 0;
}
π SEE ALSO π
- Kernel file
./drivers/gpu/drm/drm_gpuvm.cOverview(9) Split and Merge(9)Madvise Logic - Splitting and Traversal(9)Locking(9)get_next_vm_bo_from_list(9)for_each_vm_bo_in_list(9)restore_vm_bo_list(9)drm_gpuvm_bo_is_zombie(9)drm_gpuvm_bo_list_add(9)drm_gpuvm_bo_list_del_init(9)drm_gpuvm_bo_list_del(9)drm_gpuvm_range_valid(9)drm_gpuvm_resv_object_alloc(9)drm_gpuvm_init(9)drm_gpuvm_put(9)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)drm_gpuvm_validate(9)drm_gpuvm_resv_add_fence(9)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)drm_gpuva_insert(9)drm_gpuva_remove(9)drm_gpuva_link(9)drm_gpuva_unlink(9)drm_gpuva_unlink_defer(9)drm_gpuva_find_first(9)drm_gpuva_find(9)drm_gpuvm_interval_empty(9)drm_gpuva_map(9)drm_gpuva_remap(9)drm_gpuva_unmap(9)drm_gpuvm_sm_map(9)drm_gpuvm_sm_unmap(9)drm_gpuvm_sm_map_exec_lock(9)drm_gpuvm_sm_unmap_exec_lock(9)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)
Source/version footer vibes, bestie: this page lives under ./drivers/gpu/drm, dated October 2026, and its manual name is Examples(9). Go be the main character of your VA space π
β¨
π¦ 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