요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
====================
Asynchronous VM_BIND
====================
Nomenclature:
=============
* ``VRAM``: On-device memory. Sometimes referred to as device local memory.
* ``gpu_vm``: A virtual GPU address space. Typically per process, but
can be shared by multiple processes.
* ``VM_BIND``: An operation or a list of operations to modify a gpu_vm using
an IOCTL. The operations include mapping and unmapping system- or
VRAM memory.
* ``syncobj``: A container that abstracts synchronization objects. The
synchronization objects can be either generic, like dma-fences or
driver specific. A syncobj typically indicates the type of the
underlying synchronization object.
* ``in-syncobj``: Argument to a VM_BIND IOCTL, the VM_BIND operation waits
for these before starting.
* ``out-syncobj``: Argument to a VM_BIND_IOCTL, the VM_BIND operation
signals these when the bind operation is complete.
* ``dma-fence``: A cross-driver synchronization object. A basic
understanding of dma-fences is required to digest this
document. Please refer to the ``DMA Fences`` section of the
:doc:`dma-buf doc </driver-api/dma-buf>`.
* ``memory fence``: A synchronization object, different from a dma-fence.
A memory fence uses the value of a specified memory location to determine
signaled status. A memory fence can be awaited and signaled by both
the GPU and CPU. Memory fences are sometimes referred to as
user-fences, userspace-fences or gpu futexes and do not necessarily obey
the dma-fence rule of signaling within a "reasonable amount of time".
The kernel should thus avoid waiting for memory fences with locks held.
* ``long-running workload``: A workload that may take more than the
current stipulated dma-fence maximum signal delay to complete and
which therefore needs to set the gpu_vm or the GPU execution context in
a certain mode that disallows completion dma-fences.
* ``exec function``: An exec function is a function that revalidates all
affected gpu_vmas, submits a GPU command batch and registers the
dma_fence representing the GPU command's activity with all affected
dma_resvs. For completeness, although not covered by this document,
it's worth mentioning that an exec function may also be the
revalidation worker that is used by some drivers in compute /
long-running mode.
* ``bind context``: A context identifier used for the VM_BIND
operation. VM_BIND operations that use the same bind context can be
assumed, where it matters, to complete in order of submission. No such
assumptions can be made for VM_BIND operations using separate bind contexts.
* ``UMD``: User-mode driver.
* ``KMD``: Kernel-mode driver.
Synchronous / Asynchronous VM_BIND operation
============================================
Synchronous VM_BIND
___________________
With Synchronous VM_BIND, the VM_BIND operations all complete before the
IOCTL returns. A synchronous VM_BIND takes neither in-fences nor
out-fences. Synchronous VM_BIND may block and wait for GPU operations;
for example swap-in or clearing, or even previous binds.
Asynchronous VM_BIND
____________________
Asynchronous VM_BIND accepts both in-syncobjs and out-syncobjs. While the
IOCTL may return immediately, the VM_BIND operations wait for the in-syncobjs
before modifying the GPU page-tables, and signal the out-syncobjs when
the modification is done in the sense that the next exec function that
awaits for the out-syncobjs will see the change. Errors are reported
synchronously.
In low-memory situations the implementation may block, performing the
VM_BIND synchronously, because there might not be enough memory
immediately available for preparing the asynchronous operation.
If the VM_BIND IOCTL takes a list or an array of operations as an argument,
the in-syncobjs needs to signal before the first operation starts to
execute, and the out-syncobjs signal after the last operation
completes. Operations in the operation list can be assumed, where it
matters, to complete in order.
Since asynchronous VM_BIND operations may use dma-fences embedded in
out-syncobjs and internally in KMD to signal bind completion, any
memory fences given as VM_BIND in-fences need to be awaited
synchronously before the VM_BIND ioctl returns, since dma-fences,
required to signal in a reasonable amount of time, can never be made
to depend on memory fences that don't have such a restriction.
The purpose of an Asynchronous VM_BIND operation is for user-mode
drivers to be able to pipeline interleaved gpu_vm modifications and
exec functions. For long-running workloads, such pipelining of a bind
operation is not allowed and any in-fences need to be awaited
synchronously. The reason for this is twofold. First, any memory
fences gated by a long-running workload and used as in-syncobjs for the
VM_BIND operation will need to be awaited synchronously anyway (see
above). Second, any dma-fences used as in-syncobjs for VM_BIND
operations for long-running workloads will not allow for pipelining
anyway since long-running workloads don't allow for dma-fences as
out-syncobjs, so while theoretically possible the use of them is
questionable and should be rejected until there is a valuable use-case.
Note that this is not a limitation imposed by dma-fence rules, but
rather a limitation imposed to keep KMD implementation simple. It does
not affect using dma-fences as dependencies for the long-running
workload itself, which is allowed by dma-fence rules, but rather for
the VM_BIND operation only.
An asynchronous VM_BIND operation may take substantial time to
complete and signal the out_fence. In particular if the operation is
deeply pipelined behind other VM_BIND operations and workloads
submitted using exec functions. In that case, UMD might want to avoid a
subsequent VM_BIND operation to be queued behind the first one if
there are no explicit dependencies. In order to circumvent such a queue-up, a
VM_BIND implementation may allow for VM_BIND contexts to be
created. For each context, VM_BIND operations will be guaranteed to
complete in the order they were submitted, but that is not the case
for VM_BIND operations executing on separate VM_BIND contexts. Instead
KMD will attempt to execute such VM_BIND operations in parallel but
leaving no guarantee that they will actually be executed in
parallel. There may be internal implicit dependencies that only KMD knows
about, for example page-table structure changes. A way to attempt
to avoid such internal dependencies is to have different VM_BIND
contexts use separate regions of a VM.
Also for VM_BINDS for long-running gpu_vms the user-mode driver should typically
select memory fences as out-fences since that gives greater flexibility for
the kernel mode driver to inject other operations into the bind /
unbind operations. Like for example inserting breakpoints into batch
buffers. The workload execution can then easily be pipelined behind
the bind completion using the memory out-fence as the signal condition
for a GPU semaphore embedded by UMD in the workload.
There is no difference in the operations supported or in
multi-operation support between asynchronous VM_BIND and synchronous VM_BIND.
Multi-operation VM_BIND IOCTL error handling and interrupts
===========================================================
The VM_BIND operations of the IOCTL may error for various reasons, for
example due to lack of resources to complete and due to interrupted
waits.
In these situations UMD should preferably restart the IOCTL after
taking suitable action.
If UMD has over-committed a memory resource, an -ENOSPC error will be
returned, and UMD may then unbind resources that are not used at the
moment and rerun the IOCTL. On -EINTR, UMD should simply rerun the
IOCTL and on -ENOMEM user-space may either attempt to free known
system memory resources or fail. In case of UMD deciding to fail a
bind operation, due to an error return, no additional action is needed
to clean up the failed operation, and the VM is left in the same state
as it was before the failing IOCTL.
Unbind operations are guaranteed not to return any errors due to
resource constraints, but may return errors due to, for example,
invalid arguments or the gpu_vm being banned.
In the case an unexpected error happens during the asynchronous bind
process, the gpu_vm will be banned, and attempts to use it after banning
will return -ENOENT.
Example: The Xe VM_BIND uAPI
============================
Starting with the VM_BIND operation struct, the IOCTL call can take
zero, one or many such operations. A zero number means only the
synchronization part of the IOCTL is carried out: an asynchronous
VM_BIND updates the syncobjects, whereas a sync VM_BIND waits for the
implicit dependencies to be fulfilled.
.. code-block:: c
struct drm_xe_vm_bind_op {
/**
* @obj: GEM object to operate on, MBZ for MAP_USERPTR, MBZ for UNMAP
*/
__u32 obj;
/** @pad: MBZ */
__u32 pad;
union {
/**
* @obj_offset: Offset into the object for MAP.
*/
__u64 obj_offset;
/** @userptr: user virtual address for MAP_USERPTR */
__u64 userptr;
};
/**
* @range: Number of bytes from the object to bind to addr, MBZ for UNMAP_ALL
*/
__u64 range;
/** @addr: Address to operate on, MBZ for UNMAP_ALL */
__u64 addr;
/**
* @tile_mask: Mask for which tiles to create binds for, 0 == All tiles,
* only applies to creating new VMAs
*/
__u64 tile_mask;
/* Map (parts of) an object into the GPU virtual address range.
#define XE_VM_BIND_OP_MAP 0x0
/* Unmap a GPU virtual address range */
#define XE_VM_BIND_OP_UNMAP 0x1
/*
* Map a CPU virtual address range into a GPU virtual
* address range.
*/
#define XE_VM_BIND_OP_MAP_USERPTR 0x2
/* Unmap a gem object from the VM. */
#define XE_VM_BIND_OP_UNMAP_ALL 0x3
/*
* Make the backing memory of an address range resident if
* possible. Note that this doesn't pin backing memory.
*/
#define XE_VM_BIND_OP_PREFETCH 0x4
/* Make the GPU map readonly. */
#define XE_VM_BIND_FLAG_READONLY (0x1 << 16)
/*
* Valid on a faulting VM only, do the MAP operation immediately rather
* than deferring the MAP to the page fault handler.
*/
#define XE_VM_BIND_FLAG_IMMEDIATE (0x1 << 17)
/*
* When the NULL flag is set, the page tables are setup with a special
* bit which indicates writes are dropped and all reads return zero. In
* the future, the NULL flags will only be valid for XE_VM_BIND_OP_MAP
* operations, the BO handle MBZ, and the BO offset MBZ. This flag is
* intended to implement VK sparse bindings.
*/
#define XE_VM_BIND_FLAG_NULL (0x1 << 18)
/** @op: Operation to perform (lower 16 bits) and flags (upper 16 bits) */
__u32 op;
/** @mem_region: Memory region to prefetch VMA to, instance not a mask */
__u32 region;
/** @reserved: Reserved */
__u64 reserved[2];
};
The VM_BIND IOCTL argument itself, looks like follows. Note that for
synchronous VM_BIND, the num_syncs and syncs fields must be zero. Here
the ``exec_queue_id`` field is the VM_BIND context discussed previously
that is used to facilitate out-of-order VM_BINDs.
.. code-block:: c
struct drm_xe_vm_bind {
/** @extensions: Pointer to the first extension struct, if any */
__u64 extensions;
/** @vm_id: The ID of the VM to bind to */
__u32 vm_id;
/**
* @exec_queue_id: exec_queue_id, must be of class DRM_XE_ENGINE_CLASS_VM_BIND
* and exec queue must have same vm_id. If zero, the default VM bind engine
* is used.
*/
__u32 exec_queue_id;
/** @num_binds: number of binds in this IOCTL */
__u32 num_binds;
/* If set, perform an async VM_BIND, if clear a sync VM_BIND */
#define XE_VM_BIND_IOCTL_FLAG_ASYNC (0x1 << 0)
/** @flag: Flags controlling all operations in this ioctl. */
__u32 flags;
union {
/** @bind: used if num_binds == 1 */
struct drm_xe_vm_bind_op bind;
/**
* @vector_of_binds: userptr to array of struct
* drm_xe_vm_bind_op if num_binds > 1
*/
__u64 vector_of_binds;
};
/** @num_syncs: amount of syncs to wait for or to signal on completion. */
__u32 num_syncs;
/** @pad2: MBZ */
__u32 pad2;
/** @syncs: pointer to struct drm_xe_sync array */
__u64 syncs;
/** @reserved: Reserved */
__u64 reserved[2];
};
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
VM_BIND 동기화 용어
1-65`VRAM`은 device에 탑재된 memory이며 device-local memory라고도 합니다. `gpu_vm`은 virtual GPU address space로 보통 process마다 하나씩 있지만 여러 process가 공유할 수도 있습니다.
`VM_BIND`는 IOCTL을 사용해 `gpu_vm`을 변경하는 단일 operation 또는 operation list입니다. System memory나 VRAM을 map하거나 unmap하는 동작을 포함합니다.
`syncobj`는 synchronization object를 추상화하는 container입니다. 내부 object는 dma-fence처럼 generic할 수도 있고 driver-specific일 수도 있으며, syncobj는 보통 내부 synchronization object의 type을 나타냅니다.
`in-syncobj`는 VM_BIND IOCTL이 시작하기 전에 기다릴 object이고, `out-syncobj`는 bind operation이 완료되면 VM_BIND IOCTL이 signal할 object입니다.
`dma-fence`는 cross-driver synchronization object입니다. 이 문서를 이해하려면 `/driver-api/dma-buf` 문서의 `DMA Fences` 절에 설명된 기본 규칙을 알아야 합니다.
`memory fence`는 dma-fence와 다른 synchronization object로, 지정된 memory location의 값으로 signaled 상태를 판단합니다. GPU와 CPU 모두 기다리거나 signal할 수 있으며 user-fence, userspace-fence, GPU futex라고도 합니다.
Memory fence는 합리적인 시간 안에 signal해야 한다는 dma-fence 규칙을 반드시 따르지는 않습니다. 따라서 kernel은 lock을 잡은 채 memory fence를 기다리지 않아야 합니다.
`long-running workload`는 현재 정해진 dma-fence maximum signal delay보다 오래 걸릴 수 있는 workload입니다. 이런 workload는 completion dma-fence를 허용하지 않는 특정 mode로 `gpu_vm` 또는 GPU execution context를 설정해야 합니다.
`exec function`은 영향받는 모든 `gpu_vma`를 revalidate하고 GPU command batch를 submit한 다음, GPU command activity를 나타내는 `dma_fence`를 영향받는 모든 `dma_resv`에 등록합니다. 일부 compute 또는 long-running mode driver에서는 revalidation worker가 이 역할을 할 수도 있습니다.
`bind context`는 VM_BIND operation에 사용하는 context identifier입니다. 같은 bind context를 사용하는 VM_BIND는 필요한 경우 submission order대로 완료된다고 가정할 수 있지만, 서로 다른 bind context 사이에는 그런 가정을 할 수 없습니다. `UMD`는 user-mode driver, `KMD`는 kernel-mode driver를 뜻합니다.
Address-space 변경과 동기화에 참여하는 object를 구분합니다.
GPU command가 제출되고 activity fence가 reservation에 연결되는 순서입니다.
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
====================
Asynchronous VM_BIND
====================
Nomenclature:
=============
* ``VRAM``: On-device memory. Sometimes referred to as device local memory.
* ``gpu_vm``: A virtual GPU address space. Typically per process, but
can be shared by multiple processes.
* ``VM_BIND``: An operation or a list of operations to modify a gpu_vm using
an IOCTL. The operations include mapping and unmapping system- or
VRAM memory.
* ``syncobj``: A container that abstracts synchronization objects. The
synchronization objects can be either generic, like dma-fences or
driver specific. A syncobj typically indicates the type of the
underlying synchronization object.
* ``in-syncobj``: Argument to a VM_BIND IOCTL, the VM_BIND operation waits
for these before starting.
* ``out-syncobj``: Argument to a VM_BIND_IOCTL, the VM_BIND operation
signals these when the bind operation is complete.
* ``dma-fence``: A cross-driver synchronization object. A basic
understanding of dma-fences is required to digest this
document. Please refer to the ``DMA Fences`` section of the
:doc:`dma-buf doc </driver-api/dma-buf>`.
* ``memory fence``: A synchronization object, different from a dma-fence.
A memory fence uses the value of a specified memory location to determine
signaled status. A memory fence can be awaited and signaled by both
the GPU and CPU. Memory fences are sometimes referred to as
user-fences, userspace-fences or gpu futexes and do not necessarily obey
the dma-fence rule of signaling within a "reasonable amount of time".
The kernel should thus avoid waiting for memory fences with locks held.
* ``long-running workload``: A workload that may take more than the
current stipulated dma-fence maximum signal delay to complete and
which therefore needs to set the gpu_vm or the GPU execution context in
a certain mode that disallows completion dma-fences.
* ``exec function``: An exec function is a function that revalidates all
affected gpu_vmas, submits a GPU command batch and registers the
dma_fence representing the GPU command's activity with all affected
dma_resvs. For completeness, although not covered by this document,
it's worth mentioning that an exec function may also be the
revalidation worker that is used by some drivers in compute /
long-running mode.
* ``bind context``: A context identifier used for the VM_BIND
operation. VM_BIND operations that use the same bind context can be
assumed, where it matters, to complete in order of submission. No such
assumptions can be made for VM_BIND operations using separate bind contexts.
* ``UMD``: User-mode driver.
* ``KMD``: Kernel-mode driver.
Synchronous와 asynchronous VM_BIND
66-100Synchronous VM_BIND에서는 모든 VM_BIND operation이 완료된 뒤 IOCTL이 반환됩니다. In-fence와 out-fence를 받지 않으며 swap-in, clearing 또는 이전 bind 같은 GPU operation을 기다리느라 block할 수 있습니다.
Asynchronous VM_BIND는 in-syncobj와 out-syncobj를 모두 받습니다. IOCTL 자체는 즉시 반환할 수 있지만 operation은 GPU page table을 바꾸기 전에 in-syncobj를 기다리고, 변경이 완료되어 out-syncobj를 기다리는 다음 exec function이 그 변경을 볼 수 있게 되면 out-syncobj를 signal합니다. Error는 synchronous하게 보고합니다.
Low-memory 상황에서는 asynchronous operation을 준비할 memory가 즉시 충분하지 않을 수 있으므로 구현이 block하여 VM_BIND를 synchronous하게 수행할 수 있습니다.
VM_BIND IOCTL이 operation list 또는 array를 받으면 첫 operation이 시작되기 전에 in-syncobj가 signal되어야 하고 마지막 operation이 끝난 뒤 out-syncobj가 signal됩니다. 필요한 경우 list 안의 operation은 순서대로 완료된다고 가정할 수 있습니다.
Asynchronous VM_BIND는 bind completion을 알리기 위해 out-syncobj 안과 KMD 내부에서 dma-fence를 사용할 수 있습니다. 그러므로 VM_BIND in-fence로 주어진 memory fence는 IOCTL이 반환하기 전에 synchronous하게 기다려야 합니다.
이 제약은 합리적인 시간 안에 반드시 signal해야 하는 dma-fence가 그런 제한이 없는 memory fence에 의존하는 상황을 막습니다. 무기한 지연될 수 있는 memory fence를 dma-fence dependency chain 아래에 둘 수 없습니다.
두 mode의 반환 시점과 fence 계약입니다.
여러 operation을 하나의 IOCTL로 제출할 때 fence가 적용되는 범위입니다.
dma-fence의 bounded signaling 보장을 지키기 위한 wait 위치입니다.
Synchronous / Asynchronous VM_BIND operation
============================================
Synchronous VM_BIND
___________________
With Synchronous VM_BIND, the VM_BIND operations all complete before the
IOCTL returns. A synchronous VM_BIND takes neither in-fences nor
out-fences. Synchronous VM_BIND may block and wait for GPU operations;
for example swap-in or clearing, or even previous binds.
Asynchronous VM_BIND
____________________
Asynchronous VM_BIND accepts both in-syncobjs and out-syncobjs. While the
IOCTL may return immediately, the VM_BIND operations wait for the in-syncobjs
before modifying the GPU page-tables, and signal the out-syncobjs when
the modification is done in the sense that the next exec function that
awaits for the out-syncobjs will see the change. Errors are reported
synchronously.
In low-memory situations the implementation may block, performing the
VM_BIND synchronously, because there might not be enough memory
immediately available for preparing the asynchronous operation.
If the VM_BIND IOCTL takes a list or an array of operations as an argument,
the in-syncobjs needs to signal before the first operation starts to
execute, and the out-syncobjs signal after the last operation
completes. Operations in the operation list can be assumed, where it
matters, to complete in order.
Since asynchronous VM_BIND operations may use dma-fences embedded in
out-syncobjs and internally in KMD to signal bind completion, any
memory fences given as VM_BIND in-fences need to be awaited
synchronously before the VM_BIND ioctl returns, since dma-fences,
required to signal in a reasonable amount of time, can never be made
to depend on memory fences that don't have such a restriction.
Pipelining, long-running workload와 bind context
101-146Asynchronous VM_BIND의 목적은 UMD가 `gpu_vm` 변경과 exec function을 서로 끼워 넣어 pipeline할 수 있게 하는 것입니다.
Long-running workload에서는 bind operation을 이렇게 pipeline할 수 없고 모든 in-fence를 synchronous하게 기다려야 합니다. 첫째, long-running workload에 의해 gated된 memory fence가 VM_BIND in-syncobj이면 어차피 synchronous wait가 필요합니다.
둘째, long-running workload는 dma-fence를 out-syncobj로 허용하지 않으므로 VM_BIND in-syncobj로 dma-fence를 사용해도 실질적으로 pipeline할 수 없습니다. 이 사용은 이론적으로 가능하더라도 유용한 use case가 생길 때까지 거부해야 합니다.
이는 dma-fence 규칙이 강제한 제한이 아니라 KMD 구현을 단순하게 유지하기 위한 VM_BIND 제한입니다. Dma-fence를 long-running workload 자체의 dependency로 사용하는 것은 dma-fence 규칙상 허용되며 영향을 받지 않습니다.
Asynchronous VM_BIND가 다른 VM_BIND와 exec workload 뒤에 깊게 pipeline되면 out-fence를 signal하기까지 상당한 시간이 걸릴 수 있습니다. 명시적 dependency가 없는 후속 VM_BIND가 그 뒤에 불필요하게 queue되는 것을 피하려면 구현이 여러 VM_BIND context를 만들 수 있게 할 수 있습니다.
같은 context 안의 VM_BIND operation은 submission order대로 완료됨이 보장됩니다. 서로 다른 context의 operation은 KMD가 parallel execution을 시도하지만 실제 병렬 실행은 보장하지 않습니다. Page-table structure 변경처럼 KMD만 아는 implicit dependency가 있을 수 있기 때문입니다.
서로 다른 VM_BIND context가 VM의 서로 다른 region을 사용하면 이런 internal dependency를 줄이는 데 도움이 됩니다.
Long-running `gpu_vm`의 VM_BIND에서 UMD는 보통 memory fence를 out-fence로 선택해야 합니다. 그러면 KMD가 batch buffer breakpoint 삽입 같은 operation을 bind·unbind 사이에 유연하게 주입할 수 있습니다. UMD는 workload 안에 넣은 GPU semaphore의 signal condition으로 memory out-fence를 사용해 bind completion 뒤에 execution을 pipeline할 수 있습니다.
Asynchronous VM_BIND와 synchronous VM_BIND 사이에는 지원 operation이나 multi-operation 지원 범위의 차이가 없습니다.
UMD가 address-space 변경과 execution을 겹치는 기본 흐름입니다.
Context 경계에 따라 UMD가 가정할 수 있는 ordering입니다.
Completion dma-fence 대신 memory fence를 사용해 KMD 유연성을 확보합니다.
The purpose of an Asynchronous VM_BIND operation is for user-mode
drivers to be able to pipeline interleaved gpu_vm modifications and
exec functions. For long-running workloads, such pipelining of a bind
operation is not allowed and any in-fences need to be awaited
synchronously. The reason for this is twofold. First, any memory
fences gated by a long-running workload and used as in-syncobjs for the
VM_BIND operation will need to be awaited synchronously anyway (see
above). Second, any dma-fences used as in-syncobjs for VM_BIND
operations for long-running workloads will not allow for pipelining
anyway since long-running workloads don't allow for dma-fences as
out-syncobjs, so while theoretically possible the use of them is
questionable and should be rejected until there is a valuable use-case.
Note that this is not a limitation imposed by dma-fence rules, but
rather a limitation imposed to keep KMD implementation simple. It does
not affect using dma-fences as dependencies for the long-running
workload itself, which is allowed by dma-fence rules, but rather for
the VM_BIND operation only.
An asynchronous VM_BIND operation may take substantial time to
complete and signal the out_fence. In particular if the operation is
deeply pipelined behind other VM_BIND operations and workloads
submitted using exec functions. In that case, UMD might want to avoid a
subsequent VM_BIND operation to be queued behind the first one if
there are no explicit dependencies. In order to circumvent such a queue-up, a
VM_BIND implementation may allow for VM_BIND contexts to be
created. For each context, VM_BIND operations will be guaranteed to
complete in the order they were submitted, but that is not the case
for VM_BIND operations executing on separate VM_BIND contexts. Instead
KMD will attempt to execute such VM_BIND operations in parallel but
leaving no guarantee that they will actually be executed in
parallel. There may be internal implicit dependencies that only KMD knows
about, for example page-table structure changes. A way to attempt
to avoid such internal dependencies is to have different VM_BIND
contexts use separate regions of a VM.
Also for VM_BINDS for long-running gpu_vms the user-mode driver should typically
select memory fences as out-fences since that gives greater flexibility for
the kernel mode driver to inject other operations into the bind /
unbind operations. Like for example inserting breakpoints into batch
buffers. The workload execution can then easily be pipelined behind
the bind completion using the memory out-fence as the signal condition
for a GPU semaphore embedded by UMD in the workload.
There is no difference in the operations supported or in
multi-operation support between asynchronous VM_BIND and synchronous VM_BIND.
Multi-operation 오류와 interrupt 복구
147-169VM_BIND operation은 완료 resource 부족이나 interrupted wait 등 여러 이유로 실패할 수 있습니다. 이런 경우 UMD는 적절한 조치를 취한 뒤 IOCTL을 다시 시작하는 것이 바람직합니다.
Memory resource를 over-commit해 `-ENOSPC`가 반환되면 UMD는 현재 사용하지 않는 resource를 unbind한 뒤 IOCTL을 다시 실행할 수 있습니다. `-EINTR`이면 그대로 다시 실행하고, `-ENOMEM`이면 userspace가 알고 있는 system memory resource를 해제해 재시도하거나 실패 처리할 수 있습니다.
Error return 때문에 UMD가 bind operation을 실패 처리하더라도 실패한 operation을 별도로 cleanup할 필요는 없습니다. VM은 실패한 IOCTL 이전과 같은 상태로 남습니다.
Unbind operation은 resource constraint 때문에 error를 반환하지 않음이 보장됩니다. 다만 invalid argument나 banned `gpu_vm` 같은 이유로는 실패할 수 있습니다.
Asynchronous bind process 중 예상하지 못한 error가 발생하면 `gpu_vm`이 banned 상태가 됩니다. Banning 이후 해당 VM을 사용하려는 시도는 `-ENOENT`를 반환합니다.
재시도 가능한 오류와 VM 상태를 구분합니다.
Multi-operation IOCTL이 error를 반환한 뒤의 안전한 분기입니다.
Multi-operation VM_BIND IOCTL error handling and interrupts
===========================================================
The VM_BIND operations of the IOCTL may error for various reasons, for
example due to lack of resources to complete and due to interrupted
waits.
In these situations UMD should preferably restart the IOCTL after
taking suitable action.
If UMD has over-committed a memory resource, an -ENOSPC error will be
returned, and UMD may then unbind resources that are not used at the
moment and rerun the IOCTL. On -EINTR, UMD should simply rerun the
IOCTL and on -ENOMEM user-space may either attempt to free known
system memory resources or fail. In case of UMD deciding to fail a
bind operation, due to an error return, no additional action is needed
to clean up the failed operation, and the VM is left in the same state
as it was before the failing IOCTL.
Unbind operations are guaranteed not to return any errors due to
resource constraints, but may return errors due to, for example,
invalid arguments or the gpu_vm being banned.
In the case an unexpected error happens during the asynchronous bind
process, the gpu_vm will be banned, and attempts to use it after banning
will return -ENOENT.
Xe VM_BIND operation 구조체
170-256Xe VM_BIND IOCTL은 `struct drm_xe_vm_bind_op` operation을 0개, 1개 또는 여러 개 받을 수 있습니다. 0개이면 synchronization 부분만 수행합니다. Asynchronous VM_BIND는 syncobj를 update하고 synchronous VM_BIND는 implicit dependency가 충족되기를 기다립니다.
`obj`는 조작할 GEM object이며 `MAP_USERPTR`와 `UNMAP`에서는 MBZ(must be zero)입니다. `pad`도 MBZ입니다. Union의 `obj_offset`은 MAP에서 object 내부 offset이고 `userptr`는 `MAP_USERPTR`의 user virtual address입니다.
`range`는 `addr`에 bind할 byte 수이며 `UNMAP_ALL`에서는 MBZ입니다. `addr` 역시 `UNMAP_ALL`에서 MBZ입니다. `tile_mask`는 새 VMA를 만들 tile mask이고 0은 모든 tile을 뜻합니다.
`XE_VM_BIND_OP_MAP`은 object 일부를 GPU virtual address range에 map하고, `XE_VM_BIND_OP_UNMAP`은 GPU virtual range를 unmap합니다. `XE_VM_BIND_OP_MAP_USERPTR`은 CPU virtual range를 GPU virtual range에 map하며, `XE_VM_BIND_OP_UNMAP_ALL`은 GEM object를 VM에서 모두 unmap합니다.
`XE_VM_BIND_OP_PREFETCH`는 가능한 경우 address range의 backing memory를 resident로 만들지만 backing memory를 pin하지는 않습니다.
`XE_VM_BIND_FLAG_READONLY`는 GPU mapping을 read-only로 만듭니다. `XE_VM_BIND_FLAG_IMMEDIATE`는 faulting VM에서만 유효하며 MAP을 page-fault handler까지 미루지 않고 즉시 수행합니다.
`XE_VM_BIND_FLAG_NULL`은 write를 버리고 모든 read가 0을 반환하도록 page table에 특별한 bit를 설정합니다. 향후에는 `XE_VM_BIND_OP_MAP`에만 유효하고 BO handle과 BO offset이 MBZ여야 하며, VK sparse binding 구현을 위한 flag입니다.
`op` field의 lower 16 bit에는 operation을, upper 16 bit에는 flag를 넣습니다. `region`은 VMA를 prefetch할 memory region instance로 mask가 아닙니다. `reserved[2]`는 예약 영역입니다.
Lower 16-bit operation 값의 의미입니다.
Upper 16-bit flag와 적용 조건입니다.
struct drm_xe_vm_bind_op {
/**
* @obj: GEM object to operate on, MBZ for MAP_USERPTR, MBZ for UNMAP
*/
__u32 obj;
/** @pad: MBZ */
__u32 pad;
union {
/**
* @obj_offset: Offset into the object for MAP.
*/
__u64 obj_offset;
/** @userptr: user virtual address for MAP_USERPTR */
__u64 userptr;
};
/**
* @range: Number of bytes from the object to bind to addr, MBZ for UNMAP_ALL
*/
__u64 range;
/** @addr: Address to operate on, MBZ for UNMAP_ALL */
__u64 addr;
/**
* @tile_mask: Mask for which tiles to create binds for, 0 == All tiles,
* only applies to creating new VMAs
*/
__u64 tile_mask;
/* Map (parts of) an object into the GPU virtual address range.
#define XE_VM_BIND_OP_MAP 0x0
/* Unmap a GPU virtual address range */
#define XE_VM_BIND_OP_UNMAP 0x1
/*
* Map a CPU virtual address range into a GPU virtual
* address range.
*/
#define XE_VM_BIND_OP_MAP_USERPTR 0x2
/* Unmap a gem object from the VM. */
#define XE_VM_BIND_OP_UNMAP_ALL 0x3
/*
* Make the backing memory of an address range resident if
* possible. Note that this doesn't pin backing memory.
*/
#define XE_VM_BIND_OP_PREFETCH 0x4
/* Make the GPU map readonly. */
#define XE_VM_BIND_FLAG_READONLY (0x1 << 16)
/*
* Valid on a faulting VM only, do the MAP operation immediately rather
* than deferring the MAP to the page fault handler.
*/
#define XE_VM_BIND_FLAG_IMMEDIATE (0x1 << 17)
/*
* When the NULL flag is set, the page tables are setup with a special
* bit which indicates writes are dropped and all reads return zero. In
* the future, the NULL flags will only be valid for XE_VM_BIND_OP_MAP
* operations, the BO handle MBZ, and the BO offset MBZ. This flag is
* intended to implement VK sparse bindings.
*/
#define XE_VM_BIND_FLAG_NULL (0x1 << 18)
/** @op: Operation to perform (lower 16 bits) and flags (upper 16 bits) */
__u32 op;
/** @mem_region: Memory region to prefetch VMA to, instance not a mask */
__u32 region;
/** @reserved: Reserved */
__u64 reserved[2];
};
Example: The Xe VM_BIND uAPI
============================
Starting with the VM_BIND operation struct, the IOCTL call can take
zero, one or many such operations. A zero number means only the
synchronization part of the IOCTL is carried out: an asynchronous
VM_BIND updates the syncobjects, whereas a sync VM_BIND waits for the
implicit dependencies to be fulfilled.
.. code-block:: c
struct drm_xe_vm_bind_op {
/**
* @obj: GEM object to operate on, MBZ for MAP_USERPTR, MBZ for UNMAP
*/
__u32 obj;
/** @pad: MBZ */
__u32 pad;
union {
/**
* @obj_offset: Offset into the object for MAP.
*/
__u64 obj_offset;
/** @userptr: user virtual address for MAP_USERPTR */
__u64 userptr;
};
/**
* @range: Number of bytes from the object to bind to addr, MBZ for UNMAP_ALL
*/
__u64 range;
/** @addr: Address to operate on, MBZ for UNMAP_ALL */
__u64 addr;
/**
* @tile_mask: Mask for which tiles to create binds for, 0 == All tiles,
* only applies to creating new VMAs
*/
__u64 tile_mask;
/* Map (parts of) an object into the GPU virtual address range.
#define XE_VM_BIND_OP_MAP 0x0
/* Unmap a GPU virtual address range */
#define XE_VM_BIND_OP_UNMAP 0x1
/*
* Map a CPU virtual address range into a GPU virtual
* address range.
*/
#define XE_VM_BIND_OP_MAP_USERPTR 0x2
/* Unmap a gem object from the VM. */
#define XE_VM_BIND_OP_UNMAP_ALL 0x3
/*
* Make the backing memory of an address range resident if
* possible. Note that this doesn't pin backing memory.
*/
#define XE_VM_BIND_OP_PREFETCH 0x4
/* Make the GPU map readonly. */
#define XE_VM_BIND_FLAG_READONLY (0x1 << 16)
/*
* Valid on a faulting VM only, do the MAP operation immediately rather
* than deferring the MAP to the page fault handler.
*/
#define XE_VM_BIND_FLAG_IMMEDIATE (0x1 << 17)
/*
* When the NULL flag is set, the page tables are setup with a special
* bit which indicates writes are dropped and all reads return zero. In
* the future, the NULL flags will only be valid for XE_VM_BIND_OP_MAP
* operations, the BO handle MBZ, and the BO offset MBZ. This flag is
* intended to implement VK sparse bindings.
*/
#define XE_VM_BIND_FLAG_NULL (0x1 << 18)
/** @op: Operation to perform (lower 16 bits) and flags (upper 16 bits) */
__u32 op;
/** @mem_region: Memory region to prefetch VMA to, instance not a mask */
__u32 region;
/** @reserved: Reserved */
__u64 reserved[2];
};
Xe VM_BIND IOCTL argument
257-309VM_BIND IOCTL argument는 `struct drm_xe_vm_bind`입니다. Synchronous VM_BIND에서는 `num_syncs`와 `syncs` field가 반드시 0이어야 합니다.
`extensions`는 첫 extension struct pointer이고 `vm_id`는 bind 대상 VM의 ID입니다. `exec_queue_id`는 앞에서 설명한 out-of-order VM_BIND를 돕는 bind context입니다.
`exec_queue_id`는 `DRM_XE_ENGINE_CLASS_VM_BIND` class여야 하고 exec queue의 `vm_id`가 이 구조체의 `vm_id`와 같아야 합니다. 0이면 default VM bind engine을 사용합니다.
`num_binds`는 이 IOCTL의 bind 수입니다. `XE_VM_BIND_IOCTL_FLAG_ASYNC`가 설정되면 asynchronous VM_BIND, clear이면 synchronous VM_BIND입니다. `flags`는 IOCTL 안의 모든 operation을 제어합니다.
`num_binds == 1`이면 union의 `bind`에 `struct drm_xe_vm_bind_op`를 직접 넣습니다. `num_binds > 1`이면 `vector_of_binds`가 `struct drm_xe_vm_bind_op` array를 가리키는 user pointer입니다.
`num_syncs`는 시작 전에 기다리거나 completion 시 signal할 sync 수이고, `syncs`는 `struct drm_xe_sync` array pointer입니다. `pad2`는 MBZ이며 `reserved[2]`는 예약 영역입니다.
IOCTL 전체에 적용되는 context·operation·sync field입니다.
Operation 수가 0·1·복수일 때 union과 synchronization의 의미입니다.
struct drm_xe_vm_bind {
/** @extensions: Pointer to the first extension struct, if any */
__u64 extensions;
/** @vm_id: The ID of the VM to bind to */
__u32 vm_id;
/**
* @exec_queue_id: exec_queue_id, must be of class DRM_XE_ENGINE_CLASS_VM_BIND
* and exec queue must have same vm_id. If zero, the default VM bind engine
* is used.
*/
__u32 exec_queue_id;
/** @num_binds: number of binds in this IOCTL */
__u32 num_binds;
/* If set, perform an async VM_BIND, if clear a sync VM_BIND */
#define XE_VM_BIND_IOCTL_FLAG_ASYNC (0x1 << 0)
/** @flag: Flags controlling all operations in this ioctl. */
__u32 flags;
union {
/** @bind: used if num_binds == 1 */
struct drm_xe_vm_bind_op bind;
/**
* @vector_of_binds: userptr to array of struct
* drm_xe_vm_bind_op if num_binds > 1
*/
__u64 vector_of_binds;
};
/** @num_syncs: amount of syncs to wait for or to signal on completion. */
__u32 num_syncs;
/** @pad2: MBZ */
__u32 pad2;
/** @syncs: pointer to struct drm_xe_sync array */
__u64 syncs;
/** @reserved: Reserved */
__u64 reserved[2];
};
The VM_BIND IOCTL argument itself, looks like follows. Note that for
synchronous VM_BIND, the num_syncs and syncs fields must be zero. Here
the ``exec_queue_id`` field is the VM_BIND context discussed previously
that is used to facilitate out-of-order VM_BINDs.
.. code-block:: c
struct drm_xe_vm_bind {
/** @extensions: Pointer to the first extension struct, if any */
__u64 extensions;
/** @vm_id: The ID of the VM to bind to */
__u32 vm_id;
/**
* @exec_queue_id: exec_queue_id, must be of class DRM_XE_ENGINE_CLASS_VM_BIND
* and exec queue must have same vm_id. If zero, the default VM bind engine
* is used.
*/
__u32 exec_queue_id;
/** @num_binds: number of binds in this IOCTL */
__u32 num_binds;
/* If set, perform an async VM_BIND, if clear a sync VM_BIND */
#define XE_VM_BIND_IOCTL_FLAG_ASYNC (0x1 << 0)
/** @flag: Flags controlling all operations in this ioctl. */
__u32 flags;
union {
/** @bind: used if num_binds == 1 */
struct drm_xe_vm_bind_op bind;
/**
* @vector_of_binds: userptr to array of struct
* drm_xe_vm_bind_op if num_binds > 1
*/
__u64 vector_of_binds;
};
/** @num_syncs: amount of syncs to wait for or to signal on completion. */
__u32 num_syncs;
/** @pad2: MBZ */
__u32 pad2;
/** @syncs: pointer to struct drm_xe_sync array */
__u64 syncs;
/** @reserved: Reserved */
__u64 reserved[2];
};
요약·해설
drm-vm-bind-async.rst:1-309VM_BIND의 synchronous·asynchronous 실행과 fence dependency 규칙을 정의하고, long-running workload·bind context·multi-operation 오류 복구를 설명합니다. 후반에는 Xe의 `drm_xe_vm_bind_op`와 `drm_xe_vm_bind` uAPI를 field·operation·flag 단위로 해설하며 원문 C code를 그대로 보존합니다.
UMD·KMD 구현에서 놓치기 쉬운 핵심 계약입니다.