[Intel-xe] [RFC] drm/xe: Add VM snapshot support
Maarten Lankhorst
dev at lankhorst.se
Sun Sep 24 20:02:26 UTC 2023
Just an idea I had so far. Some opens:
- Do we want to set a flag on a VM_BIND or on a BO to choose what to
snapshot? Likely VM_BIND.
- Handle BO mapping in atomic context? Right now I bind the mapping on VM_BIND,
because it's easier there when we have all the locks. Due to signaling
context usage, we can never take the BO lock there reliably..
Signed-off-by: Maarten Lankhorst <dev at lankhorst.se>
---
drivers/gpu/drm/xe/xe_bo.c | 5 +-
drivers/gpu/drm/xe/xe_devcoredump.c | 9 ++
drivers/gpu/drm/xe/xe_devcoredump_types.h | 2 +
drivers/gpu/drm/xe/xe_vm.c | 126 ++++++++++++++++++++++
drivers/gpu/drm/xe/xe_vm.h | 6 ++
drivers/gpu/drm/xe/xe_vm_types.h | 19 ++++
6 files changed, 166 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
index 27726d4f3423..1f6229da2b2d 100644
--- a/drivers/gpu/drm/xe/xe_bo.c
+++ b/drivers/gpu/drm/xe/xe_bo.c
@@ -469,6 +469,8 @@ static int xe_bo_trigger_rebind(struct xe_device *xe, struct xe_bo *bo,
trace_xe_vma_evict(vma);
+ xe_vma_move_notify(vma);
+
if (xe_vm_in_fault_mode(vm)) {
/* Wait for pending binds / unbinds. */
long timeout;
@@ -1799,7 +1801,8 @@ int xe_gem_create_ioctl(struct drm_device *dev, void *data,
return -EINVAL;
bo_flags |= XE_BO_NEEDS_CPU_ACCESS;
- }
+ } else if (!(bo_flags & XE_BO_CREATE_VRAM_MASK))
+ bo_flags |= XE_BO_NEEDS_CPU_ACCESS;
if (args->vm_id) {
vm = xe_vm_lookup(xef, args->vm_id);
diff --git a/drivers/gpu/drm/xe/xe_devcoredump.c b/drivers/gpu/drm/xe/xe_devcoredump.c
index 68abc0b195be..298be162ed0c 100644
--- a/drivers/gpu/drm/xe/xe_devcoredump.c
+++ b/drivers/gpu/drm/xe/xe_devcoredump.c
@@ -16,6 +16,7 @@
#include "xe_guc_ct.h"
#include "xe_guc_submit.h"
#include "xe_hw_engine.h"
+#include "xe_vm.h"
/**
* DOC: Xe device coredump
@@ -98,6 +99,10 @@ static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
if (coredump->snapshot.hwe[i])
xe_hw_engine_snapshot_print(coredump->snapshot.hwe[i],
&p);
+ if (coredump->snapshot.vm) {
+ drm_printf(&p, "\n**** VM state ****\n");
+ xe_vm_snapshot_print(coredump->snapshot.vm, &p);
+ }
return count - iter.remain;
}
@@ -116,6 +121,7 @@ static void xe_devcoredump_free(void *data)
for (i = 0; i < XE_NUM_HW_ENGINES; i++)
if (coredump->snapshot.hwe[i])
xe_hw_engine_snapshot_free(coredump->snapshot.hwe[i]);
+ xe_vm_snapshot_free(coredump->snapshot.vm);
coredump->captured = false;
drm_info(&coredump_to_xe(coredump)->drm,
@@ -151,6 +157,8 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
coredump->snapshot.ct = xe_guc_ct_snapshot_capture(&guc->ct, true);
coredump->snapshot.ge = xe_guc_exec_queue_snapshot_capture(q);
+ if (q->vm)
+ coredump->snapshot.vm = xe_vm_snapshot_capture(q->vm);
for_each_hw_engine(hwe, q->gt, id) {
if (hwe->class != q->hwe->class ||
@@ -194,3 +202,4 @@ void xe_devcoredump(struct xe_exec_queue *q)
xe_devcoredump_read, xe_devcoredump_free);
}
#endif
+
diff --git a/drivers/gpu/drm/xe/xe_devcoredump_types.h b/drivers/gpu/drm/xe/xe_devcoredump_types.h
index 7fdad9c3d3dd..93c2ad7bdc54 100644
--- a/drivers/gpu/drm/xe/xe_devcoredump_types.h
+++ b/drivers/gpu/drm/xe/xe_devcoredump_types.h
@@ -33,6 +33,8 @@ struct xe_devcoredump_snapshot {
struct xe_guc_submit_exec_queue_snapshot *ge;
/** @hwe: HW Engine snapshot array */
struct xe_hw_engine_snapshot *hwe[XE_NUM_HW_ENGINES];
+ /** @vm: Snapshot of VM state */
+ struct xe_vm_snapshot *vm;
};
/**
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 2b225c0692a6..276b03847ecc 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -889,6 +889,11 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm,
if (is_null)
vma->gpuva.flags |= DRM_GPUVA_SPARSE;
+ if (bo && bo->flags & XE_BO_NEEDS_CPU_ACCESS) {
+ INIT_LIST_HEAD(&vma->snap.link);
+ vma->gpuva.flags |= XE_VMA_SNAPSHOTTABLE;
+ }
+
if (tile_mask) {
vma->tile_mask = tile_mask;
} else {
@@ -1238,6 +1243,9 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
INIT_LIST_HEAD(&vm->extobj.list);
+ mutex_init(&vm->snap.lock);
+ INIT_LIST_HEAD(&vm->snap.list);
+
if (!(flags & XE_VM_FLAG_MIGRATION))
xe_device_mem_access_get(xe);
@@ -1354,6 +1362,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
dma_resv_unlock(&vm->resv);
drm_gpuva_manager_destroy(&vm->mgr);
err_put:
+ mutex_destroy(&vm->snap.lock);
dma_resv_fini(&vm->resv);
for_each_tile(tile, xe, id)
xe_range_fence_tree_fini(&vm->rftree[id]);
@@ -1638,6 +1647,14 @@ xe_vm_unbind_vma(struct xe_vma *vma, struct xe_exec_queue *q,
cf ? &cf->base : fence);
}
+ if (vma->gpuva.flags & XE_VMA_SNAPSHOTTABLE &&
+ !list_empty(&vma->snap.link)) {
+ mutex_lock(&vm->snap.lock);
+ list_del(&vma->snap.link);
+ vm->snap.num--;
+ mutex_unlock(&vm->snap.lock);
+ }
+
return cf ? &cf->base : !fence ? dma_fence_get_stub() : fence;
err_fences:
@@ -1669,6 +1686,13 @@ xe_vm_bind_vma(struct xe_vma *vma, struct xe_exec_queue *q,
trace_xe_vma_bind(vma);
+ /* Map for coredump */
+ if (vma->gpuva.flags & XE_VMA_SNAPSHOTTABLE) {
+ err = xe_bo_vmap(xe_vma_bo(vma));
+ if (err)
+ return ERR_PTR(err);
+ }
+
if (number_tiles > 1) {
fences = kmalloc_array(number_tiles, sizeof(*fences),
GFP_KERNEL);
@@ -1715,6 +1739,14 @@ xe_vm_bind_vma(struct xe_vma *vma, struct xe_exec_queue *q,
cf ? &cf->base : fence);
}
+ if (vma->gpuva.flags & XE_VMA_SNAPSHOTTABLE &&
+ list_empty(&vma->snap.link)) {
+ mutex_lock(&vm->snap.lock);
+ list_add_tail(&vma->snap.link, &vm->snap.list);
+ vm->snap.num++;
+ mutex_unlock(&vm->snap.lock);
+ }
+
return cf ? &cf->base : fence;
err_fences:
@@ -3561,3 +3593,97 @@ int xe_analyze_vm(struct drm_printer *p, struct xe_vm *vm, int gt_id)
return 0;
}
+
+struct xe_vm_snapshot {
+ unsigned long num_snaps;
+ struct {
+ uint64_t ofs;
+ unsigned long len;
+ void *data;
+ } snap[];
+};
+
+struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm)
+{
+ unsigned long num_snaps, i;
+ struct xe_vm_snapshot *snap;
+ struct xe_vma *vma;
+
+ mutex_lock(&vm->snap.lock);
+ num_snaps = vm->snap.num;
+
+ snap = kvmalloc(offsetof(struct xe_vm_snapshot, snap[num_snaps]), GFP_NOWAIT);
+ if (!snap)
+ goto out_unlock;
+
+ snap->num_snaps = num_snaps;
+ i = 0;
+ list_for_each_entry(vma, &vm->snap.list, snap.link) {
+ struct xe_bo *bo = gem_to_xe_bo(vma->gpuva.gem.obj);
+ unsigned long bo_ofs = xe_vma_bo_offset(vma);
+
+ snap->snap[i].ofs = xe_vma_start(vma);
+ snap->snap[i].len = xe_vma_size(vma);
+ snap->snap[i].data = kvmalloc(snap->snap[i].len, GFP_NOWAIT);
+ if (!snap->snap[i].data)
+ goto next;
+
+ /* TODO: Some way around trylock? */
+ xe_map_memcpy_from(vm->xe, snap->snap[i].data,
+ &bo->vmap, bo_ofs, snap->snap[i].len);
+
+next:
+ i++;
+ }
+
+out_unlock:
+ mutex_unlock(&vm->snap.lock);
+ return snap;
+}
+
+void xe_vm_snapshot_print(struct xe_vm_snapshot *snap, struct drm_printer *p)
+{
+ unsigned long i, j;
+
+ for (i = 0; i < snap->num_snaps; i++) {
+ if (!snap->snap[i].data) {
+ drm_printf(p, "Unable to capture range [%llx-%llx]\n",
+ snap->snap[i].ofs, snap->snap[i].ofs + snap->snap[i].len - 1);
+ continue;
+ }
+
+ for (j = 0; j < snap->snap[i].len; j += 64) {
+ uint32_t *x = snap->snap[i].data + j;
+
+ drm_printf(p, "[%llx] = { %x, %x, %x, %x, %x, %x, %x, %x, %x, %x, %x, %x, %x, %x, %x, %x }\n",
+ snap->snap[i].ofs + j, x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7],
+ x[8], x[9], x[10], x[11], x[12], x[13], x[14], x[15]);
+ }
+ }
+}
+
+void xe_vm_snapshot_free(struct xe_vm_snapshot *snap)
+{
+ unsigned long i;
+
+ for (i = 0; i < snap->num_snaps; i++)
+ kvfree(snap->snap[i].data);
+ kvfree(snap);
+}
+
+void xe_vma_move_notify(struct xe_vma *vma)
+{
+ struct xe_vm *vm = xe_vma_vm(vma);
+
+ if (!(vma->gpuva.flags & XE_VMA_SNAPSHOTTABLE))
+ return;
+
+ if (list_empty(&vma->snap.link))
+ return;
+
+ mutex_lock(&vm->snap.lock);
+ list_del(&vma->snap.link);
+ vm->snap.num--;
+ mutex_unlock(&vm->snap.lock);
+}
+
diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
index f966ed39b711..b0b96f158f8b 100644
--- a/drivers/gpu/drm/xe/xe_vm.h
+++ b/drivers/gpu/drm/xe/xe_vm.h
@@ -234,3 +234,9 @@ static inline void vm_dbg(const struct drm_device *dev,
{ /* noop */ }
#endif
#endif
+
+struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm);
+void xe_vm_snapshot_print(struct xe_vm_snapshot *snap, struct drm_printer *p);
+void xe_vm_snapshot_free(struct xe_vm_snapshot *snap);
+void xe_vma_move_notify(struct xe_vma *vma);
+
diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h
index 52e5eaed91c3..eb558e5a7f27 100644
--- a/drivers/gpu/drm/xe/xe_vm_types.h
+++ b/drivers/gpu/drm/xe/xe_vm_types.h
@@ -33,6 +33,7 @@ struct xe_vm;
#define XE_VMA_PTE_4K (DRM_GPUVA_USERBITS << 5)
#define XE_VMA_PTE_2M (DRM_GPUVA_USERBITS << 6)
#define XE_VMA_PTE_1G (DRM_GPUVA_USERBITS << 7)
+#define XE_VMA_SNAPSHOTTABLE (DRM_GPUVA_USERBITS << 8)
/** struct xe_userptr - User pointer */
struct xe_userptr {
@@ -123,6 +124,14 @@ struct xe_vma {
struct list_head link;
} extobj;
+ struct {
+ /**
+ * @snap.link: Link into list of xe_vm's snapshottable vma's.
+ * protected by vm->snap.lock.
+ */
+ struct list_head link;
+ } snap;
+
/**
* @userptr: user pointer state, only allocated for VMAs that are
* user pointers
@@ -336,6 +345,16 @@ struct xe_vm {
/** @batch_invalidate_tlb: Always invalidate TLB before batch start */
bool batch_invalidate_tlb;
+
+ /** @snap: Snapshot support structures */
+ struct {
+ /** @mutex: Mutex held in signaling context */
+ struct mutex lock;
+ /** @list: List of all vma's to snapshot */
+ struct list_head list;
+ /** @num: Number of snapshottable vma's */
+ unsigned long num;
+ } snap;
};
/** struct xe_vma_op_map - VMA map operation */
--
2.40.1
More information about the Intel-xe
mailing list