Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

drm/msm: Add VM logging for VM_BIND updates

When userspace opts in to VM_BIND, the submit no longer holds references
keeping the VMA alive. This makes it difficult to distinguish between
UMD/KMD/app bugs. So add a debug option for logging the most recent VM
updates and capturing these in GPU devcoredumps.

The submitqueue id is also captured, a value of zero means the operation
did not go via a submitqueue (ie. comes from msm_gem_vm_close() tearing
down the remaining mappings when the device file is closed.

Signed-off-by: Rob Clark <robdclark@chromium.org>
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
Tested-by: Antonino Maniscalco <antomani103@gmail.com>
Reviewed-by: Antonino Maniscalco <antomani103@gmail.com>
Patchwork: https://patchwork.freedesktop.org/patch/661518/

authored by

Rob Clark and committed by
Rob Clark
9edc5296 2e6a8a1f

+204 -15
+11
drivers/gpu/drm/msm/adreno/adreno_gpu.c
··· 833 833 for (i = 0; state->bos && i < state->nr_bos; i++) 834 834 kvfree(state->bos[i].data); 835 835 836 + kfree(state->vm_logs); 836 837 kfree(state->bos); 837 838 kfree(state->comm); 838 839 kfree(state->cmd); ··· 972 971 drm_printf(p, " - asid: %d\n", info->asid); 973 972 drm_printf(p, " - ptes: %.16llx %.16llx %.16llx %.16llx\n", 974 973 info->ptes[0], info->ptes[1], info->ptes[2], info->ptes[3]); 974 + } 975 + 976 + if (state->vm_logs) { 977 + drm_puts(p, "vm-log:\n"); 978 + for (i = 0; i < state->nr_vm_logs; i++) { 979 + struct msm_gem_vm_log_entry *e = &state->vm_logs[i]; 980 + drm_printf(p, " - %s:%d: 0x%016llx-0x%016llx\n", 981 + e->op, e->queue_id, e->iova, 982 + e->iova + e->range); 983 + } 975 984 } 976 985 977 986 drm_printf(p, "rbbm-status: 0x%08x\n", state->rbbm_status);
+24
drivers/gpu/drm/msm/msm_gem.h
··· 25 25 #define MSM_BO_MAP_PRIV 0x20000000 /* use IOMMU_PRIV when mapping */ 26 26 27 27 /** 28 + * struct msm_gem_vm_log_entry - An entry in the VM log 29 + * 30 + * For userspace managed VMs, a log of recent VM updates is tracked and 31 + * captured in GPU devcore dumps, to aid debugging issues caused by (for 32 + * example) incorrectly synchronized VM updates 33 + */ 34 + struct msm_gem_vm_log_entry { 35 + const char *op; 36 + uint64_t iova; 37 + uint64_t range; 38 + int queue_id; 39 + }; 40 + 41 + /** 28 42 * struct msm_gem_vm - VM object 29 43 * 30 44 * A VM object representing a GPU (or display or GMU or ...) virtual address ··· 99 85 /** @last_fence: Fence for last pending work scheduled on the VM */ 100 86 struct dma_fence *last_fence; 101 87 88 + /** @log: A log of recent VM updates */ 89 + struct msm_gem_vm_log_entry *log; 90 + 91 + /** @log_shift: length of @log is (1 << @log_shift) */ 92 + uint32_t log_shift; 93 + 94 + /** @log_idx: index of next @log entry to write */ 95 + uint32_t log_idx; 96 + 102 97 /** @faults: the number of GPU hangs associated with this address space */ 103 98 int faults; 104 99 ··· 138 115 u64 va_start, u64 va_size, bool managed); 139 116 140 117 void msm_gem_vm_close(struct drm_gpuvm *gpuvm); 118 + void msm_gem_vm_unusable(struct drm_gpuvm *gpuvm); 141 119 142 120 struct msm_fence_context; 143 121
+118 -10
drivers/gpu/drm/msm/msm_gem_vma.c
··· 17 17 18 18 #define vm_dbg(fmt, ...) pr_debug("%s:%d: "fmt"\n", __func__, __LINE__, ##__VA_ARGS__) 19 19 20 + static uint vm_log_shift = 0; 21 + MODULE_PARM_DESC(vm_log_shift, "Length of VM op log"); 22 + module_param_named(vm_log_shift, vm_log_shift, uint, 0600); 23 + 20 24 /** 21 25 * struct msm_vm_map_op - create new pgtable mapping 22 26 */ ··· 35 31 struct sg_table *sgt; 36 32 /** @prot: the mapping protection flags */ 37 33 int prot; 34 + 35 + /** 36 + * @queue_id: The id of the submitqueue the operation is performed 37 + * on, or zero for (in particular) UNMAP ops triggered outside of 38 + * a submitqueue (ie. process cleanup) 39 + */ 40 + int queue_id; 38 41 }; 39 42 40 43 /** ··· 52 41 uint64_t iova; 53 42 /** @range: size of region to unmap */ 54 43 uint64_t range; 44 + 45 + /** 46 + * @queue_id: The id of the submitqueue the operation is performed 47 + * on, or zero for (in particular) UNMAP ops triggered outside of 48 + * a submitqueue (ie. process cleanup) 49 + */ 50 + int queue_id; 55 51 }; 56 52 57 53 /** ··· 162 144 vm->mmu->funcs->destroy(vm->mmu); 163 145 dma_fence_put(vm->last_fence); 164 146 put_pid(vm->pid); 147 + kfree(vm->log); 165 148 kfree(vm); 149 + } 150 + 151 + /** 152 + * msm_gem_vm_unusable() - Mark a VM as unusable 153 + * @vm: the VM to mark unusable 154 + */ 155 + void 156 + msm_gem_vm_unusable(struct drm_gpuvm *gpuvm) 157 + { 158 + struct msm_gem_vm *vm = to_msm_vm(gpuvm); 159 + uint32_t vm_log_len = (1 << vm->log_shift); 160 + uint32_t vm_log_mask = vm_log_len - 1; 161 + uint32_t nr_vm_logs; 162 + int first; 163 + 164 + vm->unusable = true; 165 + 166 + /* Bail if no log, or empty log: */ 167 + if (!vm->log || !vm->log[0].op) 168 + return; 169 + 170 + mutex_lock(&vm->mmu_lock); 171 + 172 + /* 173 + * log_idx is the next entry to overwrite, meaning it is the oldest, or 174 + * first, entry (other than the special case handled below where the 175 + * log hasn't wrapped around yet) 176 + */ 177 + first = vm->log_idx; 178 + 179 + if (!vm->log[first].op) { 180 + /* 181 + * If the next log entry has not been written yet, then only 182 + * entries 0 to idx-1 are valid (ie. we haven't wrapped around 183 + * yet) 184 + */ 185 + nr_vm_logs = MAX(0, first - 1); 186 + first = 0; 187 + } else { 188 + nr_vm_logs = vm_log_len; 189 + } 190 + 191 + pr_err("vm-log:\n"); 192 + for (int i = 0; i < nr_vm_logs; i++) { 193 + int idx = (i + first) & vm_log_mask; 194 + struct msm_gem_vm_log_entry *e = &vm->log[idx]; 195 + pr_err(" - %s:%d: 0x%016llx-0x%016llx\n", 196 + e->op, e->queue_id, e->iova, 197 + e->iova + e->range); 198 + } 199 + 200 + mutex_unlock(&vm->mmu_lock); 201 + } 202 + 203 + static void 204 + vm_log(struct msm_gem_vm *vm, const char *op, uint64_t iova, uint64_t range, int queue_id) 205 + { 206 + int idx; 207 + 208 + if (!vm->managed) 209 + lockdep_assert_held(&vm->mmu_lock); 210 + 211 + vm_dbg("%s:%p:%d: %016llx %016llx", op, vm, queue_id, iova, iova + range); 212 + 213 + if (!vm->log) 214 + return; 215 + 216 + idx = vm->log_idx; 217 + vm->log[idx].op = op; 218 + vm->log[idx].iova = iova; 219 + vm->log[idx].range = range; 220 + vm->log[idx].queue_id = queue_id; 221 + vm->log_idx = (vm->log_idx + 1) & ((1 << vm->log_shift) - 1); 166 222 } 167 223 168 224 static void 169 225 vm_unmap_op(struct msm_gem_vm *vm, const struct msm_vm_unmap_op *op) 170 226 { 171 - if (!vm->managed) 172 - lockdep_assert_held(&vm->mmu_lock); 173 - 174 - vm_dbg("%p: %016llx %016llx", vm, op->iova, op->iova + op->range); 227 + vm_log(vm, "unmap", op->iova, op->range, op->queue_id); 175 228 176 229 vm->mmu->funcs->unmap(vm->mmu, op->iova, op->range); 177 230 } ··· 250 161 static int 251 162 vm_map_op(struct msm_gem_vm *vm, const struct msm_vm_map_op *op) 252 163 { 253 - if (!vm->managed) 254 - lockdep_assert_held(&vm->mmu_lock); 255 - 256 - vm_dbg("%p: %016llx %016llx", vm, op->iova, op->iova + op->range); 164 + vm_log(vm, "map", op->iova, op->range, op->queue_id); 257 165 258 166 return vm->mmu->funcs->map(vm->mmu, op->iova, op->sgt, op->offset, 259 167 op->range, op->prot); ··· 468 382 static int 469 383 msm_gem_vm_sm_step_map(struct drm_gpuva_op *op, void *arg) 470 384 { 385 + struct msm_vm_bind_job *job = ((struct op_arg *)arg)->job; 471 386 struct drm_gem_object *obj = op->map.gem.obj; 472 387 struct drm_gpuva *vma; 473 388 struct sg_table *sgt; ··· 499 412 .range = vma->va.range, 500 413 .offset = vma->gem.offset, 501 414 .prot = prot, 415 + .queue_id = job->queue->id, 502 416 }, 503 417 .obj = vma->gem.obj, 504 418 }); ··· 533 445 .unmap = { 534 446 .iova = unmap_start, 535 447 .range = unmap_range, 448 + .queue_id = job->queue->id, 536 449 }, 537 450 .obj = orig_vma->gem.obj, 538 451 }); ··· 595 506 static int 596 507 msm_gem_vm_sm_step_unmap(struct drm_gpuva_op *op, void *arg) 597 508 { 509 + struct msm_vm_bind_job *job = ((struct op_arg *)arg)->job; 598 510 struct drm_gpuva *vma = op->unmap.va; 599 511 struct msm_gem_vma *msm_vma = to_msm_vma(vma); 600 512 ··· 610 520 .unmap = { 611 521 .iova = vma->va.addr, 612 522 .range = vma->va.range, 523 + .queue_id = job->queue->id, 613 524 }, 614 525 .obj = vma->gem.obj, 615 526 }); ··· 675 584 * now the VM is in an undefined state. Game over! 676 585 */ 677 586 if (ret) 678 - vm->unusable = true; 587 + msm_gem_vm_unusable(job->vm); 679 588 680 589 job_foreach_bo (obj, job) { 681 590 msm_gem_lock(obj); ··· 785 694 vm->managed = managed; 786 695 787 696 drm_mm_init(&vm->mm, va_start, va_size); 697 + 698 + /* 699 + * We don't really need vm log for kernel managed VMs, as the kernel 700 + * is responsible for ensuring that GEM objs are mapped if they are 701 + * used by a submit. Furthermore we piggyback on mmu_lock to serialize 702 + * access to the log. 703 + * 704 + * Limit the max log_shift to 8 to prevent userspace from asking us 705 + * for an unreasonable log size. 706 + */ 707 + if (!managed) 708 + vm->log_shift = MIN(vm_log_shift, 8); 709 + 710 + if (vm->log_shift) { 711 + vm->log = kmalloc_array(1 << vm->log_shift, sizeof(vm->log[0]), 712 + GFP_KERNEL | __GFP_ZERO); 713 + } 788 714 789 715 return &vm->base; 790 716 ··· 1270 1162 * state the vm is in. So throw up our hands! 1271 1163 */ 1272 1164 if (i > 0) 1273 - vm->unusable = true; 1165 + msm_gem_vm_unusable(job->vm); 1274 1166 return ret; 1275 1167 } 1276 1168 }
+47 -5
drivers/gpu/drm/msm/msm_gpu.c
··· 259 259 { 260 260 extern bool rd_full; 261 261 262 - if (!submit) 263 - return; 264 - 265 262 if (msm_context_is_vmbind(submit->queue->ctx)) { 266 263 struct drm_exec exec; 267 264 struct drm_gpuva *vma; ··· 315 318 } 316 319 } 317 320 321 + static void crashstate_get_vm_logs(struct msm_gpu_state *state, struct msm_gem_vm *vm) 322 + { 323 + uint32_t vm_log_len = (1 << vm->log_shift); 324 + uint32_t vm_log_mask = vm_log_len - 1; 325 + int first; 326 + 327 + /* Bail if no log, or empty log: */ 328 + if (!vm->log || !vm->log[0].op) 329 + return; 330 + 331 + mutex_lock(&vm->mmu_lock); 332 + 333 + /* 334 + * log_idx is the next entry to overwrite, meaning it is the oldest, or 335 + * first, entry (other than the special case handled below where the 336 + * log hasn't wrapped around yet) 337 + */ 338 + first = vm->log_idx; 339 + 340 + if (!vm->log[first].op) { 341 + /* 342 + * If the next log entry has not been written yet, then only 343 + * entries 0 to idx-1 are valid (ie. we haven't wrapped around 344 + * yet) 345 + */ 346 + state->nr_vm_logs = MAX(0, first - 1); 347 + first = 0; 348 + } else { 349 + state->nr_vm_logs = vm_log_len; 350 + } 351 + 352 + state->vm_logs = kmalloc_array( 353 + state->nr_vm_logs, sizeof(vm->log[0]), GFP_KERNEL); 354 + for (int i = 0; i < state->nr_vm_logs; i++) { 355 + int idx = (i + first) & vm_log_mask; 356 + 357 + state->vm_logs[i] = vm->log[idx]; 358 + } 359 + 360 + mutex_unlock(&vm->mmu_lock); 361 + } 362 + 318 363 static void msm_gpu_crashstate_capture(struct msm_gpu *gpu, 319 364 struct msm_gem_submit *submit, struct msm_gpu_fault_info *fault_info, 320 365 char *comm, char *cmd) ··· 390 351 msm_iommu_pagetable_walk(mmu, info->iova, info->ptes); 391 352 } 392 353 393 - crashstate_get_bos(state, submit); 354 + if (submit) { 355 + crashstate_get_vm_logs(state, to_msm_vm(submit->vm)); 356 + crashstate_get_bos(state, submit); 357 + } 394 358 395 359 /* Set the active crash state to be dumped on failure */ 396 360 gpu->crashstate = state; ··· 494 452 * VM_BIND) 495 453 */ 496 454 if (!vm->managed) 497 - vm->unusable = true; 455 + msm_gem_vm_unusable(submit->vm); 498 456 } 499 457 500 458 get_comm_cmdline(submit, &comm, &cmd);
+4
drivers/gpu/drm/msm/msm_gpu.h
··· 20 20 #include "msm_gem.h" 21 21 22 22 struct msm_gem_submit; 23 + struct msm_gem_vm_log_entry; 23 24 struct msm_gpu_perfcntr; 24 25 struct msm_gpu_state; 25 26 struct msm_context; ··· 603 602 char *cmd; 604 603 605 604 struct msm_gpu_fault_info fault_info; 605 + 606 + int nr_vm_logs; 607 + struct msm_gem_vm_log_entry *vm_logs; 606 608 607 609 int nr_bos; 608 610 struct msm_gpu_state_bo *bos;