Skip to content

Commit 0eb2a18

Browse files
committed
drm/xe: Implement VM snapshot support for BO's and userptr
Since we cannot immediately capture the BO's and userptr, perform it in 2 stages. The immediate stage takes a reference to each BO and userptr, while a delayed worker captures the contents and then frees the reference. This is required because in signaling context, no locks can be taken, no memory can be allocated, and no waits on userspace can be performed. With the delayed worker, all of this can be performed very easily, without having to resort to hacks. Changes since v1: - Fix crash on NULL captured vm. - Use ascii85_encode to capture BO contents and save some space. - Add length to coredump output for each captured area. Changes since v2: - Dump each mapping on their own line, to simplify tooling. - Fix null pointer deref in xe_vm_snapshot_free. Changes since v3: - Don't add uninitialized value to snap->ofs. (Souza) - Use kernel types for u32 and u64. - Move snap_mutex destruction to final vm destruction. (Souza) Changes since v4: - Remove extra memset. (Souza) Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Reviewed-by: José Roberto de Souza <jose.souza@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20240221133024.898315-6-maarten.lankhorst@linux.intel.com
1 parent 0cd9904 commit 0eb2a18

4 files changed

Lines changed: 211 additions & 4 deletions

File tree

drivers/gpu/drm/xe/xe_devcoredump.c

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "xe_guc_submit.h"
1818
#include "xe_hw_engine.h"
1919
#include "xe_sched_job.h"
20+
#include "xe_vm.h"
2021

2122
/**
2223
* DOC: Xe device coredump
@@ -59,12 +60,22 @@ static struct xe_guc *exec_queue_to_guc(struct xe_exec_queue *q)
5960
return &q->gt->uc.guc;
6061
}
6162

63+
static void xe_devcoredump_deferred_snap_work(struct work_struct *work)
64+
{
65+
struct xe_devcoredump_snapshot *ss = container_of(work, typeof(*ss), work);
66+
67+
xe_force_wake_get(gt_to_fw(ss->gt), XE_FORCEWAKE_ALL);
68+
if (ss->vm)
69+
xe_vm_snapshot_capture_delayed(ss->vm);
70+
xe_force_wake_put(gt_to_fw(ss->gt), XE_FORCEWAKE_ALL);
71+
}
72+
6273
static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
6374
size_t count, void *data, size_t datalen)
6475
{
6576
struct xe_devcoredump *coredump = data;
6677
struct xe_device *xe = coredump_to_xe(coredump);
67-
struct xe_devcoredump_snapshot *ss;
78+
struct xe_devcoredump_snapshot *ss = &coredump->snapshot;
6879
struct drm_printer p;
6980
struct drm_print_iterator iter;
7081
struct timespec64 ts;
@@ -74,12 +85,14 @@ static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
7485
if (!data || !coredump_to_xe(coredump))
7586
return -ENODEV;
7687

88+
/* Ensure delayed work is captured before continuing */
89+
flush_work(&ss->work);
90+
7791
iter.data = buffer;
7892
iter.offset = 0;
7993
iter.start = offset;
8094
iter.remain = count;
8195

82-
ss = &coredump->snapshot;
8396
p = drm_coredump_printer(&iter);
8497

8598
drm_printf(&p, "**** Xe Device Coredump ****\n");
@@ -104,6 +117,10 @@ static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
104117
if (coredump->snapshot.hwe[i])
105118
xe_hw_engine_snapshot_print(coredump->snapshot.hwe[i],
106119
&p);
120+
if (coredump->snapshot.vm) {
121+
drm_printf(&p, "\n**** VM state ****\n");
122+
xe_vm_snapshot_print(coredump->snapshot.vm, &p);
123+
}
107124

108125
return count - iter.remain;
109126
}
@@ -117,12 +134,15 @@ static void xe_devcoredump_free(void *data)
117134
if (!data || !coredump_to_xe(coredump))
118135
return;
119136

137+
cancel_work_sync(&coredump->snapshot.work);
138+
120139
xe_guc_ct_snapshot_free(coredump->snapshot.ct);
121140
xe_guc_exec_queue_snapshot_free(coredump->snapshot.ge);
122141
xe_sched_job_snapshot_free(coredump->snapshot.job);
123142
for (i = 0; i < XE_NUM_HW_ENGINES; i++)
124143
if (coredump->snapshot.hwe[i])
125144
xe_hw_engine_snapshot_free(coredump->snapshot.hwe[i]);
145+
xe_vm_snapshot_free(coredump->snapshot.vm);
126146

127147
/* To prevent stale data on next snapshot, clear everything */
128148
memset(&coredump->snapshot, 0, sizeof(coredump->snapshot));
@@ -147,6 +167,9 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
147167
ss->snapshot_time = ktime_get_real();
148168
ss->boot_time = ktime_get_boottime();
149169

170+
ss->gt = q->gt;
171+
INIT_WORK(&ss->work, xe_devcoredump_deferred_snap_work);
172+
150173
cookie = dma_fence_begin_signalling();
151174
for (i = 0; q->width > 1 && i < XE_HW_ENGINE_MAX_INSTANCE;) {
152175
if (adj_logical_mask & BIT(i)) {
@@ -162,6 +185,7 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
162185
coredump->snapshot.ct = xe_guc_ct_snapshot_capture(&guc->ct, true);
163186
coredump->snapshot.ge = xe_guc_exec_queue_snapshot_capture(job);
164187
coredump->snapshot.job = xe_sched_job_snapshot_capture(job);
188+
coredump->snapshot.vm = xe_vm_snapshot_capture(q->vm);
165189

166190
for_each_hw_engine(hwe, q->gt, id) {
167191
if (hwe->class != q->hwe->class ||
@@ -172,6 +196,9 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
172196
coredump->snapshot.hwe[id] = xe_hw_engine_snapshot_capture(hwe);
173197
}
174198

199+
if (ss->vm)
200+
queue_work(system_unbound_wq, &ss->work);
201+
175202
xe_force_wake_put(gt_to_fw(q->gt), XE_FORCEWAKE_ALL);
176203
dma_fence_end_signalling(cookie);
177204
}
@@ -205,3 +232,4 @@ void xe_devcoredump(struct xe_sched_job *job)
205232
xe_devcoredump_read, xe_devcoredump_free);
206233
}
207234
#endif
235+

drivers/gpu/drm/xe/xe_devcoredump_types.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "xe_hw_engine_types.h"
1313

1414
struct xe_device;
15+
struct xe_gt;
1516

1617
/**
1718
* struct xe_devcoredump_snapshot - Crash snapshot
@@ -26,6 +27,11 @@ struct xe_devcoredump_snapshot {
2627
/** @boot_time: Relative boot time so the uptime can be calculated. */
2728
ktime_t boot_time;
2829

30+
/** @gt: Affected GT, used by forcewake for delayed capture */
31+
struct xe_gt *gt;
32+
/** @work: Workqueue for deferred capture outside of signaling context */
33+
struct work_struct work;
34+
2935
/* GuC snapshots */
3036
/** @ct: GuC CT snapshot */
3137
struct xe_guc_ct_snapshot *ct;
@@ -36,6 +42,8 @@ struct xe_devcoredump_snapshot {
3642
struct xe_hw_engine_snapshot *hwe[XE_NUM_HW_ENGINES];
3743
/** @job: Snapshot of job state */
3844
struct xe_sched_job_snapshot *job;
45+
/** @vm: Snapshot of VM state */
46+
struct xe_vm_snapshot *vm;
3947
};
4048

4149
/**

drivers/gpu/drm/xe/xe_vm.c

Lines changed: 168 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <drm/ttm/ttm_execbuf_util.h>
1414
#include <drm/ttm/ttm_tt.h>
1515
#include <drm/xe_drm.h>
16+
#include <linux/ascii85.h>
1617
#include <linux/delay.h>
1718
#include <linux/kthread.h>
1819
#include <linux/mm.h>
@@ -1523,8 +1524,6 @@ void xe_vm_close_and_put(struct xe_vm *vm)
15231524

15241525
up_write(&vm->lock);
15251526

1526-
mutex_destroy(&vm->snap_mutex);
1527-
15281527
mutex_lock(&xe->usm.lock);
15291528
if (vm->flags & XE_VM_FLAG_FAULT_MODE)
15301529
xe->usm.num_vm_in_fault_mode--;
@@ -1550,6 +1549,8 @@ static void vm_destroy_work_func(struct work_struct *w)
15501549
/* xe_vm_close_and_put was not called? */
15511550
xe_assert(xe, !vm->size);
15521551

1552+
mutex_destroy(&vm->snap_mutex);
1553+
15531554
if (!(vm->flags & XE_VM_FLAG_MIGRATION)) {
15541555
xe_device_mem_access_put(xe);
15551556

@@ -3269,3 +3270,168 @@ int xe_analyze_vm(struct drm_printer *p, struct xe_vm *vm, int gt_id)
32693270

32703271
return 0;
32713272
}
3273+
3274+
struct xe_vm_snapshot {
3275+
unsigned long num_snaps;
3276+
struct {
3277+
u64 ofs, bo_ofs;
3278+
unsigned long len;
3279+
struct xe_bo *bo;
3280+
void *data;
3281+
struct mm_struct *mm;
3282+
} snap[];
3283+
};
3284+
3285+
struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm)
3286+
{
3287+
unsigned long num_snaps = 0, i;
3288+
struct xe_vm_snapshot *snap = NULL;
3289+
struct drm_gpuva *gpuva;
3290+
3291+
if (!vm)
3292+
return NULL;
3293+
3294+
mutex_lock(&vm->snap_mutex);
3295+
drm_gpuvm_for_each_va(gpuva, &vm->gpuvm) {
3296+
if (gpuva->flags & XE_VMA_DUMPABLE)
3297+
num_snaps++;
3298+
}
3299+
3300+
if (num_snaps)
3301+
snap = kvzalloc(offsetof(struct xe_vm_snapshot, snap[num_snaps]), GFP_NOWAIT);
3302+
if (!snap)
3303+
goto out_unlock;
3304+
3305+
snap->num_snaps = num_snaps;
3306+
i = 0;
3307+
drm_gpuvm_for_each_va(gpuva, &vm->gpuvm) {
3308+
struct xe_vma *vma = gpuva_to_vma(gpuva);
3309+
struct xe_bo *bo = vma->gpuva.gem.obj ?
3310+
gem_to_xe_bo(vma->gpuva.gem.obj) : NULL;
3311+
3312+
if (!(gpuva->flags & XE_VMA_DUMPABLE))
3313+
continue;
3314+
3315+
snap->snap[i].ofs = xe_vma_start(vma);
3316+
snap->snap[i].len = xe_vma_size(vma);
3317+
if (bo) {
3318+
snap->snap[i].bo = xe_bo_get(bo);
3319+
snap->snap[i].bo_ofs = xe_vma_bo_offset(vma);
3320+
} else if (xe_vma_is_userptr(vma)) {
3321+
struct mm_struct *mm =
3322+
to_userptr_vma(vma)->userptr.notifier.mm;
3323+
3324+
if (mmget_not_zero(mm))
3325+
snap->snap[i].mm = mm;
3326+
else
3327+
snap->snap[i].data = ERR_PTR(-EFAULT);
3328+
3329+
snap->snap[i].bo_ofs = xe_vma_userptr(vma);
3330+
} else {
3331+
snap->snap[i].data = ERR_PTR(-ENOENT);
3332+
}
3333+
i++;
3334+
}
3335+
3336+
out_unlock:
3337+
mutex_unlock(&vm->snap_mutex);
3338+
return snap;
3339+
}
3340+
3341+
void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap)
3342+
{
3343+
for (int i = 0; i < snap->num_snaps; i++) {
3344+
struct xe_bo *bo = snap->snap[i].bo;
3345+
struct iosys_map src;
3346+
int err;
3347+
3348+
if (IS_ERR(snap->snap[i].data))
3349+
continue;
3350+
3351+
snap->snap[i].data = kvmalloc(snap->snap[i].len, GFP_USER);
3352+
if (!snap->snap[i].data) {
3353+
snap->snap[i].data = ERR_PTR(-ENOMEM);
3354+
goto cleanup_bo;
3355+
}
3356+
3357+
if (bo) {
3358+
dma_resv_lock(bo->ttm.base.resv, NULL);
3359+
err = ttm_bo_vmap(&bo->ttm, &src);
3360+
if (!err) {
3361+
xe_map_memcpy_from(xe_bo_device(bo),
3362+
snap->snap[i].data,
3363+
&src, snap->snap[i].bo_ofs,
3364+
snap->snap[i].len);
3365+
ttm_bo_vunmap(&bo->ttm, &src);
3366+
}
3367+
dma_resv_unlock(bo->ttm.base.resv);
3368+
} else {
3369+
void __user *userptr = (void __user *)(size_t)snap->snap[i].bo_ofs;
3370+
3371+
kthread_use_mm(snap->snap[i].mm);
3372+
if (!copy_from_user(snap->snap[i].data, userptr, snap->snap[i].len))
3373+
err = 0;
3374+
else
3375+
err = -EFAULT;
3376+
kthread_unuse_mm(snap->snap[i].mm);
3377+
3378+
mmput(snap->snap[i].mm);
3379+
snap->snap[i].mm = NULL;
3380+
}
3381+
3382+
if (err) {
3383+
kvfree(snap->snap[i].data);
3384+
snap->snap[i].data = ERR_PTR(err);
3385+
}
3386+
3387+
cleanup_bo:
3388+
xe_bo_put(bo);
3389+
snap->snap[i].bo = NULL;
3390+
}
3391+
}
3392+
3393+
void xe_vm_snapshot_print(struct xe_vm_snapshot *snap, struct drm_printer *p)
3394+
{
3395+
unsigned long i, j;
3396+
3397+
for (i = 0; i < snap->num_snaps; i++) {
3398+
if (IS_ERR(snap->snap[i].data))
3399+
goto uncaptured;
3400+
3401+
drm_printf(p, "[%llx].length: 0x%lx\n", snap->snap[i].ofs, snap->snap[i].len);
3402+
drm_printf(p, "[%llx].data: ",
3403+
snap->snap[i].ofs);
3404+
3405+
for (j = 0; j < snap->snap[i].len; j += sizeof(u32)) {
3406+
u32 *val = snap->snap[i].data + j;
3407+
char dumped[ASCII85_BUFSZ];
3408+
3409+
drm_puts(p, ascii85_encode(*val, dumped));
3410+
}
3411+
3412+
drm_puts(p, "\n");
3413+
continue;
3414+
3415+
uncaptured:
3416+
drm_printf(p, "Unable to capture range [%llx-%llx]: %li\n",
3417+
snap->snap[i].ofs, snap->snap[i].ofs + snap->snap[i].len - 1,
3418+
PTR_ERR(snap->snap[i].data));
3419+
}
3420+
}
3421+
3422+
void xe_vm_snapshot_free(struct xe_vm_snapshot *snap)
3423+
{
3424+
unsigned long i;
3425+
3426+
if (!snap)
3427+
return;
3428+
3429+
for (i = 0; i < snap->num_snaps; i++) {
3430+
if (!IS_ERR(snap->snap[i].data))
3431+
kvfree(snap->snap[i].data);
3432+
xe_bo_put(snap->snap[i].bo);
3433+
if (snap->snap[i].mm)
3434+
mmput(snap->snap[i].mm);
3435+
}
3436+
kvfree(snap);
3437+
}

drivers/gpu/drm/xe/xe_vm.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,8 @@ static inline void vm_dbg(const struct drm_device *dev,
271271
{ /* noop */ }
272272
#endif
273273
#endif
274+
275+
struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm);
276+
void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap);
277+
void xe_vm_snapshot_print(struct xe_vm_snapshot *snap, struct drm_printer *p);
278+
void xe_vm_snapshot_free(struct xe_vm_snapshot *snap);

0 commit comments

Comments
 (0)