drm-misc-fixes for v7.2-rc3: - Fix uaf in amdxdna mmap failure path. - A lot of deadlocks, access races and return value fixes in amdxdna. - Fix analogix_dp bitshifts during link training. - Use direct label in drm_exec. - Fix absent indirect bo handling in v3d. - Sync on first active crtc in fb_dirty, rather than first crtc. - Rework try_harder in the buddy allocator. - Make imagination function static to solve compiler warning. - Fix imagination error checking.
Unified · Split
| @@ -875,7 +875,7 @@ static int aie2_hwctx_cu_config(struct amdxdna_hwctx *hwctx, void *buf, u32 size · 874 unmodified lines | ||
| 875 | 875 | if (!hwctx->cus) |
| 876 | 876 | return -ENOMEM; |
| 877 | 877 | |
| 878 | − ret = amdxdna_pm_resume_get_locked(xdna); | |
| 878 | + ret = amdxdna_pm_resume_get(xdna); | |
| 879 | 879 | if (ret) |
| 880 | 880 | goto free_cus; |
| 881 | 881 | |
| @@ -900,13 +900,16 @@ free_cus: · 18 unmodified lines | ||
| 900 | 900 | static void aie2_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq) |
| 901 | 901 | { |
| 902 | 902 | struct dma_fence *out_fence = aie2_cmd_get_out_fence(hwctx, seq); |
| 903 | + struct amdxdna_dev *xdna = hwctx->client->xdna; | |
| 903 | 904 | |
| 904 | 905 | if (!out_fence) { |
| 905 | − XDNA_ERR(hwctx->client->xdna, "Failed to get fence"); | |
| 906 | + XDNA_ERR(xdna, "Failed to get fence"); | |
| 906 | 907 | return; |
| 907 | 908 | } |
| 908 | 909 | |
| 910 | + mutex_unlock(&xdna->dev_lock); | |
| 909 | 911 | dma_fence_wait_timeout(out_fence, false, MAX_SCHEDULE_TIMEOUT); |
| 912 | + mutex_lock(&xdna->dev_lock); | |
| 910 | 913 | dma_fence_put(out_fence); |
| 911 | 914 | } |
| 912 | 915 | |
| @@ -1039,7 +1042,7 @@ again: · 126 unmodified lines | ||
| 1039 | 1042 | found = false; |
| 1040 | 1043 | down_write(&xdna->notifier_lock); |
| 1041 | 1044 | list_for_each_entry(mapp, &abo->mem.umap_list, node) { |
| 1042 | − if (mapp->invalid) { | |
| 1045 | + if (mapp->invalid && kref_get_unless_zero(&mapp->refcnt)) { | |
| 1043 | 1046 | found = true; |
| 1044 | 1047 | break; |
| 1045 | 1048 | } |
| @@ -1050,11 +1053,9 @@ again: · 4 unmodified lines | ||
| 1050 | 1053 | up_write(&xdna->notifier_lock); |
| 1051 | 1054 | return 0; |
| 1052 | 1055 | } |
| 1053 | − kref_get(&mapp->refcnt); | |
| 1056 | + | |
| 1054 | 1057 | up_write(&xdna->notifier_lock); |
| 1055 | 1058 | |
| 1056 | − XDNA_DBG(xdna, "populate memory range %lx %lx", | |
| 1057 | − mapp->vma->vm_start, mapp->vma->vm_end); | |
| 1058 | 1059 | mm = mapp->notifier.mm; |
| 1059 | 1060 | if (!mmget_not_zero(mm)) { |
| 1060 | 1061 | amdxdna_umap_put(mapp); |
| @@ -1221,10 +1222,6 @@ int aie2_hwctx_heap_expand(struct amdxdna_hwctx *hwctx, · 160 unmodified lines | ||
| 1221 | 1222 | u64 addr; |
| 1222 | 1223 | int ret; |
| 1223 | 1224 | |
| 1224 | − ret = amdxdna_pm_resume_get_locked(xdna); | |
| 1225 | − if (ret) | |
| 1226 | − return ret; | |
| 1227 | − | |
| 1228 | 1225 | addr = amdxdna_obj_dma_addr(heap); |
| 1229 | 1226 | ret = aie2_add_host_buf(xdna->dev_handle, hwctx->fw_ctx_id, |
| 1230 | 1227 | addr, heap->mem.size); |
| @@ -1233,7 +1230,5 @@ int aie2_hwctx_heap_expand(struct amdxdna_hwctx *hwctx, · 2 unmodified lines | ||
| 1233 | 1230 | hwctx->name, heap->mem.size, ret); |
| 1234 | 1231 | } |
| 1235 | 1232 | |
| 1236 | − amdxdna_pm_suspend_put(xdna); | |
| 1237 | − | |
| 1238 | 1233 | return ret; |
| 1239 | 1234 | } |
| @@ -840,7 +840,7 @@ static struct aie2_exec_msg_ops npu_exec_message_ops = { · 839 unmodified lines | ||
| 840 | 840 | static int aie2_init_exec_req(void *req, struct amdxdna_gem_obj *cmd_abo, |
| 841 | 841 | size_t *size, u32 *msg_op) |
| 842 | 842 | { |
| 843 | − struct amdxdna_dev *xdna = cmd_abo->client->xdna; | |
| 843 | + struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(cmd_abo)->dev); | |
| 844 | 844 | int ret; |
| 845 | 845 | u32 op; |
| 846 | 846 | |
| @@ -874,7 +874,7 @@ static int · 27 unmodified lines | ||
| 874 | 874 | aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo, |
| 875 | 875 | size_t *size, u32 *cmd_op) |
| 876 | 876 | { |
| 877 | − struct amdxdna_dev *xdna = cmd_abo->client->xdna; | |
| 877 | + struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(cmd_abo)->dev); | |
| 878 | 878 | int ret; |
| 879 | 879 | u32 op; |
| 880 | 880 | |
| @@ -310,6 +310,7 @@ int amdxdna_drm_destroy_hwctx_ioctl(struct drm_device *dev, void *data, struct d · 309 unmodified lines | ||
| 310 | 310 | if (!drm_dev_enter(dev, &idx)) |
| 311 | 311 | return -ENODEV; |
| 312 | 312 | |
| 313 | + mutex_lock(&xdna->client_lock); | |
| 313 | 314 | mutex_lock(&xdna->dev_lock); |
| 314 | 315 | hwctx = xa_erase(&client->hwctx_xa, args->handle); |
| 315 | 316 | if (!hwctx) { |
| @@ -328,6 +329,7 @@ int amdxdna_drm_destroy_hwctx_ioctl(struct drm_device *dev, void *data, struct d · 12 unmodified lines | ||
| 328 | 329 | XDNA_DBG(xdna, "PID %d destroyed HW context %d", client->pid, args->handle); |
| 329 | 330 | out: |
| 330 | 331 | mutex_unlock(&xdna->dev_lock); |
| 332 | + mutex_unlock(&xdna->client_lock); | |
| 331 | 333 | drm_dev_exit(idx); |
| 332 | 334 | return ret; |
| 333 | 335 | } |
| @@ -382,16 +384,27 @@ int amdxdna_drm_config_hwctx_ioctl(struct drm_device *dev, void *data, struct dr · 48 unmodified lines | ||
| 382 | 384 | return -EINVAL; |
| 383 | 385 | } |
| 384 | 386 | |
| 385 | − guard(mutex)(&xdna->dev_lock); | |
| 387 | + ret = amdxdna_pm_resume_get(xdna); | |
| 388 | + if (ret) { | |
| 389 | + XDNA_ERR(xdna, "Resume failed, ret %d", ret); | |
| 390 | + goto free_buf; | |
| 391 | + } | |
| 392 | + | |
| 393 | + mutex_lock(&xdna->client_lock); | |
| 394 | + mutex_lock(&xdna->dev_lock); | |
| 386 | 395 | hwctx = xa_load(&client->hwctx_xa, args->handle); |
| 387 | 396 | if (!hwctx) { |
| 388 | 397 | XDNA_DBG(xdna, "PID %d failed to get hwctx %d", client->pid, args->handle); |
| 389 | 398 | ret = -EINVAL; |
| 390 | − goto free_buf; | |
| 399 | + goto unlock; | |
| 391 | 400 | } |
| 392 | 401 | |
| 393 | 402 | ret = xdna->dev_info->ops->hwctx_config(hwctx, args->param_type, val, buf, buf_size); |
| 394 | 403 | |
| 404 | +unlock: | |
| 405 | + mutex_unlock(&xdna->dev_lock); | |
| 406 | + mutex_unlock(&xdna->client_lock); | |
| 407 | + amdxdna_pm_suspend_put(xdna); | |
| 395 | 408 | free_buf: |
| 396 | 409 | kfree(buf); |
| 397 | 410 | return ret; |
| @@ -412,16 +425,27 @@ int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl) · 14 unmodified lines | ||
| 412 | 425 | if (!gobj) |
| 413 | 426 | return -EINVAL; |
| 414 | 427 | |
| 428 | + ret = amdxdna_pm_resume_get(xdna); | |
| 429 | + if (ret) { | |
| 430 | + XDNA_ERR(xdna, "Resume failed, ret %d", ret); | |
| 431 | + goto put_obj; | |
| 432 | + } | |
| 433 | + | |
| 415 | 434 | abo = to_xdna_obj(gobj); |
| 416 | − guard(mutex)(&xdna->dev_lock); | |
| 435 | + mutex_lock(&xdna->client_lock); | |
| 436 | + mutex_lock(&xdna->dev_lock); | |
| 417 | 437 | hwctx = xa_load(&client->hwctx_xa, abo->assigned_hwctx); |
| 418 | 438 | if (!hwctx) { |
| 419 | 439 | ret = -EINVAL; |
| 420 | − goto put_obj; | |
| 440 | + goto unlock; | |
| 421 | 441 | } |
| 422 | 442 | |
| 423 | 443 | ret = xdna->dev_info->ops->hwctx_sync_debug_bo(hwctx, debug_bo_hdl); |
| 424 | 444 | |
| 445 | +unlock: | |
| 446 | + mutex_unlock(&xdna->dev_lock); | |
| 447 | + mutex_unlock(&xdna->client_lock); | |
| 448 | + amdxdna_pm_suspend_put(xdna); | |
| 425 | 449 | put_obj: |
| 426 | 450 | drm_gem_object_put(gobj); |
| 427 | 451 | return ret; |
| @@ -448,9 +472,7 @@ static int amdxdna_hwctx_expand_heap(struct amdxdna_hwctx *hwctx) · 20 unmodified lines | ||
| 448 | 472 | break; |
| 449 | 473 | } |
| 450 | 474 | |
| 451 | − mutex_unlock(&client->mm_lock); | |
| 452 | 475 | ret = xdna->dev_info->ops->hwctx_heap_expand(hwctx, heap); |
| 453 | − mutex_lock(&client->mm_lock); | |
| 454 | 476 | if (ret) { |
| 455 | 477 | amdxdna_gem_unpin(heap); |
| 456 | 478 | drm_gem_object_put(to_gobj(heap)); |
| @@ -469,18 +491,26 @@ int amdxdna_update_heap(struct amdxdna_client *client, struct amdxdna_hwctx *hwc · 12 unmodified lines | ||
| 469 | 491 | unsigned long hwctx_id; |
| 470 | 492 | int ret; |
| 471 | 493 | |
| 472 | − guard(mutex)(&client->mm_lock); | |
| 494 | + ret = amdxdna_pm_resume_get_locked(client->xdna); | |
| 495 | + if (ret) | |
| 496 | + return ret; | |
| 473 | 497 | |
| 474 | − if (hwctx) | |
| 475 | − return amdxdna_hwctx_expand_heap(hwctx); | |
| 498 | + mutex_lock(&client->mm_lock); | |
| 476 | 499 | |
| 477 | − amdxdna_for_each_hwctx(client, hwctx_id, hwctx) { | |
| 500 | + if (hwctx) { | |
| 478 | 501 | ret = amdxdna_hwctx_expand_heap(hwctx); |
| 479 | − if (ret) | |
| 480 | − return ret; | |
| 502 | + } else { | |
| 503 | + amdxdna_for_each_hwctx(client, hwctx_id, hwctx) { | |
| 504 | + ret = amdxdna_hwctx_expand_heap(hwctx); | |
| 505 | + if (ret) | |
| 506 | + break; | |
| 507 | + } | |
| 481 | 508 | } |
| 509 | + mutex_unlock(&client->mm_lock); | |
| 482 | 510 | |
| 483 | − return 0; | |
| 511 | + amdxdna_pm_suspend_put(client->xdna); | |
| 512 | + | |
| 513 | + return ret; | |
| 484 | 514 | } |
| 485 | 515 | |
| 486 | 516 | static void |
| @@ -198,6 +198,7 @@ amdxdna_gem_destroy_obj(struct amdxdna_gem_obj *abo) · 197 unmodified lines | ||
| 198 | 198 | */ |
| 199 | 199 | void *amdxdna_gem_vmap(struct amdxdna_gem_obj *abo) |
| 200 | 200 | { |
| 201 | + struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev); | |
| 201 | 202 | struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL); |
| 202 | 203 | int ret; |
| 203 | 204 | |
| @@ -210,7 +211,7 @@ void *amdxdna_gem_vmap(struct amdxdna_gem_obj *abo) · 6 unmodified lines | ||
| 210 | 211 | if (!abo->mem.kva) { |
| 211 | 212 | ret = drm_gem_vmap(to_gobj(abo), &map); |
| 212 | 213 | if (ret) |
| 213 | − XDNA_ERR(abo->client->xdna, "Vmap bo failed, ret %d", ret); | |
| 214 | + XDNA_ERR(xdna, "Vmap bo failed, ret %d", ret); | |
| 214 | 215 | else |
| 215 | 216 | abo->mem.kva = map.vaddr; |
| 216 | 217 | } |
| @@ -254,7 +255,7 @@ static bool amdxdna_hmm_invalidate(struct mmu_interval_notifier *mni, · 37 unmodified lines | ||
| 254 | 255 | |
| 255 | 256 | xdna = to_xdna_dev(to_gobj(abo)->dev); |
| 256 | 257 | XDNA_DBG(xdna, "Invalidating range 0x%lx, 0x%lx, type %d", |
| 257 | − mapp->vma->vm_start, mapp->vma->vm_end, abo->type); | |
| 258 | + mapp->range.start, mapp->range.end, abo->type); | |
| 258 | 259 | |
| 259 | 260 | if (!mmu_notifier_range_blockable(range)) |
| 260 | 261 | return false; |
| @@ -284,15 +285,23 @@ static const struct mmu_interval_notifier_ops amdxdna_hmm_ops = { · 23 unmodified lines | ||
| 284 | 285 | .invalidate = amdxdna_hmm_invalidate, |
| 285 | 286 | }; |
| 286 | 287 | |
| 288 | +static inline bool compare_range(struct amdxdna_umap *mapp, | |
| 289 | + struct mm_struct *mm, | |
| 290 | + unsigned long start, unsigned long end) | |
| 291 | +{ | |
| 292 | + return (!mapp->unmapped && mapp->notifier.mm == mm && | |
| 293 | + mapp->range.start == start && mapp->range.end == end); | |
| 294 | +} | |
| 295 | + | |
| 287 | 296 | static void amdxdna_hmm_unregister(struct amdxdna_gem_obj *abo, |
| 288 | 297 | struct vm_area_struct *vma) |
| 289 | 298 | { |
| 290 | 299 | struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev); |
| 291 | 300 | struct amdxdna_umap *mapp; |
| 292 | 301 | |
| 293 | − down_read(&xdna->notifier_lock); | |
| 302 | + down_write(&xdna->notifier_lock); | |
| 294 | 303 | list_for_each_entry(mapp, &abo->mem.umap_list, node) { |
| 295 | − if (!vma || mapp->vma == vma) { | |
| 304 | + if (!vma || compare_range(mapp, vma->vm_mm, vma->vm_start, vma->vm_end)) { | |
| 296 | 305 | if (!mapp->unmapped) { |
| 297 | 306 | queue_work(xdna->notifier_wq, &mapp->hmm_unreg_work); |
| 298 | 307 | mapp->unmapped = true; |
| @@ -301,19 +310,16 @@ static void amdxdna_hmm_unregister(struct amdxdna_gem_obj *abo, · 2 unmodified lines | ||
| 301 | 310 | break; |
| 302 | 311 | } |
| 303 | 312 | } |
| 304 | − up_read(&xdna->notifier_lock); | |
| 313 | + up_write(&xdna->notifier_lock); | |
| 305 | 314 | } |
| 306 | 315 | |
| 307 | 316 | static void amdxdna_umap_release(struct kref *ref) |
| 308 | 317 | { |
| 309 | 318 | struct amdxdna_umap *mapp = container_of(ref, struct amdxdna_umap, refcnt); |
| 310 | 319 | struct amdxdna_gem_obj *abo = mapp->abo; |
| 311 | − struct vm_area_struct *vma = mapp->vma; | |
| 312 | 320 | struct amdxdna_dev *xdna; |
| 313 | 321 | |
| 314 | 322 | mmu_interval_notifier_remove(&mapp->notifier); |
| 315 | − if (is_import_bo(abo) && vma->vm_file && vma->vm_file->f_mapping) | |
| 316 | − mapping_clear_unevictable(vma->vm_file->f_mapping); | |
| 317 | 323 | |
| 318 | 324 | xdna = to_xdna_dev(to_gobj(mapp->abo)->dev); |
| 319 | 325 | down_write(&xdna->notifier_lock); |
| @@ -346,15 +352,30 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, · 26 unmodified lines | ||
| 346 | 352 | unsigned long len = vma->vm_end - vma->vm_start; |
| 347 | 353 | unsigned long addr = vma->vm_start; |
| 348 | 354 | struct amdxdna_umap *mapp; |
| 349 | − u32 nr_pages; | |
| 355 | + unsigned long nr_pages; | |
| 350 | 356 | int ret; |
| 351 | 357 | |
| 352 | − if (!amdxdna_pasid_on(abo->client)) { | |
| 358 | + /* | |
| 359 | + * When PASID is off, amdxdna_gem_obj_open() called amdxdna_dma_map_bo() | |
| 360 | + * and mem.dma_addr is valid; use the DMA address directly and skip HMM. | |
| 361 | + * Avoid dereferencing abo->client which may be NULL (cleared in close()) | |
| 362 | + * while internal kernel references are still held. | |
| 363 | + */ | |
| 364 | + if (abo->mem.dma_addr != AMDXDNA_INVALID_ADDR) { | |
| 353 | 365 | /* Need to set uva for heap uva validation */ |
| 354 | 366 | abo->mem.uva = addr; |
| 355 | 367 | return 0; |
| 356 | 368 | } |
| 357 | 369 | |
| 370 | + down_read(&xdna->notifier_lock); | |
| 371 | + list_for_each_entry(mapp, &abo->mem.umap_list, node) { | |
| 372 | + if (compare_range(mapp, current->mm, addr, addr + len)) { | |
| 373 | + up_read(&xdna->notifier_lock); | |
| 374 | + return 0; | |
| 375 | + } | |
| 376 | + } | |
| 377 | + up_read(&xdna->notifier_lock); | |
| 378 | + | |
| 358 | 379 | mapp = kzalloc_obj(*mapp); |
| 359 | 380 | if (!mapp) |
| 360 | 381 | return -ENOMEM; |
| @@ -380,13 +401,10 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, · 19 unmodified lines | ||
| 380 | 401 | mapp->range.start = vma->vm_start; |
| 381 | 402 | mapp->range.end = vma->vm_end; |
| 382 | 403 | mapp->range.default_flags = HMM_PFN_REQ_FAULT; |
| 383 | − mapp->vma = vma; | |
| 384 | 404 | mapp->abo = abo; |
| 385 | 405 | kref_init(&mapp->refcnt); |
| 386 | 406 | |
| 387 | 407 | INIT_WORK(&mapp->hmm_unreg_work, amdxdna_hmm_unreg_work); |
| 388 | − if (is_import_bo(abo) && vma->vm_file && vma->vm_file->f_mapping) | |
| 389 | − mapping_set_unevictable(vma->vm_file->f_mapping); | |
| 390 | 408 | |
| 391 | 409 | down_write(&xdna->notifier_lock); |
| 392 | 410 | if (list_empty(&abo->mem.umap_list)) |
| @@ -527,6 +545,7 @@ static int amdxdna_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struc · 134 unmodified lines | ||
| 527 | 545 | |
| 528 | 546 | close_vma: |
| 529 | 547 | vma->vm_ops->close(vma); |
| 548 | + return ret; | |
| 530 | 549 | put_obj: |
| 531 | 550 | drm_gem_object_put(gobj); |
| 532 | 551 | return ret; |
| @@ -652,8 +671,11 @@ static int amdxdna_gem_obj_open(struct drm_gem_object *gobj, struct drm_file *fi · 119 unmodified lines | ||
| 652 | 671 | /* No need to set up dma addr mapping in PASID mode. */ |
| 653 | 672 | if (!amdxdna_pasid_on(abo->client)) { |
| 654 | 673 | ret = amdxdna_dma_map_bo(xdna, abo); |
| 655 | − if (ret) | |
| 674 | + if (ret) { | |
| 675 | + abo->open_ref--; | |
| 676 | + abo->client = NULL; | |
| 656 | 677 | return ret; |
| 678 | + } | |
| 657 | 679 | } |
| 658 | 680 | |
| 659 | 681 | amdxdna_gem_add_bo_usage(abo); |
| @@ -12,7 +12,6 @@ · 11 unmodified lines | ||
| 12 | 12 | #include "amdxdna_pci_drv.h" |
| 13 | 13 | |
| 14 | 14 | struct amdxdna_umap { |
| 15 | − struct vm_area_struct *vma; | |
| 16 | 15 | struct mmu_interval_notifier notifier; |
| 17 | 16 | struct hmm_range range; |
| 18 | 17 | struct work_struct hmm_unreg_work; |
| @@ -89,12 +88,19 @@ u64 amdxdna_gem_dev_addr(struct amdxdna_gem_obj *abo); · 70 unmodified lines | ||
| 89 | 88 | |
| 90 | 89 | static inline u64 amdxdna_dev_bo_offset(struct amdxdna_gem_obj *abo) |
| 91 | 90 | { |
| 92 | − return amdxdna_gem_dev_addr(abo) - abo->client->xdna->dev_info->dev_mem_base; | |
| 91 | + return amdxdna_gem_dev_addr(abo) - to_xdna_dev(to_gobj(abo)->dev)->dev_info->dev_mem_base; | |
| 93 | 92 | } |
| 94 | 93 | |
| 95 | 94 | static inline u64 amdxdna_obj_dma_addr(struct amdxdna_gem_obj *abo) |
| 96 | 95 | { |
| 97 | − return amdxdna_pasid_on(abo->client) ? amdxdna_gem_uva(abo) : abo->mem.dma_addr; | |
| 96 | + /* | |
| 97 | + * amdxdna_gem_obj_open() calls amdxdna_dma_map_bo() only when PASID is | |
| 98 | + * off, leaving mem.dma_addr at AMDXDNA_INVALID_ADDR when PASID is on. | |
| 99 | + * Avoid dereferencing abo->client, which is cleared to NULL by | |
| 100 | + * amdxdna_gem_obj_close() while internal kernel references remain. | |
| 101 | + */ | |
| 102 | + return (abo->mem.dma_addr != AMDXDNA_INVALID_ADDR) ? | |
| 103 | + abo->mem.dma_addr : amdxdna_gem_uva(abo); | |
| 98 | 104 | } |
| 99 | 105 | |
| 100 | 106 | void amdxdna_umap_put(struct amdxdna_umap *mapp); |
| @@ -109,11 +109,16 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp) · 108 unmodified lines | ||
| 109 | 109 | { |
| 110 | 110 | struct amdxdna_dev *xdna = to_xdna_dev(ddev); |
| 111 | 111 | struct amdxdna_client *client; |
| 112 | + int ret; | |
| 112 | 113 | |
| 113 | 114 | client = kzalloc_obj(*client); |
| 114 | 115 | if (!client) |
| 115 | 116 | return -ENOMEM; |
| 116 | 117 | |
| 118 | + ret = init_srcu_struct(&client->hwctx_srcu); | |
| 119 | + if (ret) | |
| 120 | + goto free_client; | |
| 121 | + | |
| 117 | 122 | client->pid = pid_nr(rcu_access_pointer(filp->pid)); |
| 118 | 123 | client->xdna = xdna; |
| 119 | 124 | client->pasid = IOMMU_PASID_INVALID; |
| @@ -125,13 +130,12 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp) · 5 unmodified lines | ||
| 125 | 130 | XDNA_WARN(xdna, "PASID not available for pid %d", client->pid); |
| 126 | 131 | if (!amdxdna_use_carveout(xdna)) { |
| 127 | 132 | XDNA_ERR(xdna, "PASID unavailable and carveout not configured"); |
| 128 | − kfree(client); | |
| 129 | − return -EINVAL; | |
| 133 | + ret = -EINVAL; | |
| 134 | + goto cleanup_srcu; | |
| 130 | 135 | } |
| 131 | 136 | } |
| 132 | 137 | } |
| 133 | 138 | mmgrab(client->mm); |
| 134 | − init_srcu_struct(&client->hwctx_srcu); | |
| 135 | 139 | xa_init_flags(&client->hwctx_xa, XA_FLAGS_ALLOC); |
| 136 | 140 | xa_init_flags(&client->dev_heap_xa, XA_FLAGS_ALLOC); |
| 137 | 141 | drm_mm_init(&client->dev_heap_mm, xdna->dev_info->dev_mem_base, |
| @@ -149,6 +153,12 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp) · 11 unmodified lines | ||
| 149 | 153 | |
| 150 | 154 | XDNA_DBG(xdna, "pid %d opened", client->pid); |
| 151 | 155 | return 0; |
| 156 | + | |
| 157 | +cleanup_srcu: | |
| 158 | + cleanup_srcu_struct(&client->hwctx_srcu); | |
| 159 | +free_client: | |
| 160 | + kfree(client); | |
| 161 | + return ret; | |
| 152 | 162 | } |
| 153 | 163 | |
| 154 | 164 | static void amdxdna_client_cleanup(struct amdxdna_client *client) |
| @@ -373,7 +383,10 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id) · 218 unmodified lines | ||
| 373 | 383 | if (ret) |
| 374 | 384 | return ret; |
| 375 | 385 | |
| 376 | − drmm_mutex_init(ddev, &xdna->dev_lock); | |
| 386 | + ret = drmm_mutex_init(ddev, &xdna->dev_lock); | |
| 387 | + if (ret) | |
| 388 | + return ret; | |
| 389 | + | |
| 377 | 390 | init_rwsem(&xdna->notifier_lock); |
| 378 | 391 | INIT_LIST_HEAD(&xdna->client_list); |
| 379 | 392 | pci_set_drvdata(pdev, xdna); |
| @@ -1084,22 +1084,30 @@ static int __gpu_buddy_alloc_range(struct gpu_buddy *mm, · 1083 unmodified lines | ||
| 1084 | 1084 | blocks, total_allocated_on_err); |
| 1085 | 1085 | } |
| 1086 | 1086 | |
| 1087 | +static int __alloc_contig_aligned_retry(struct gpu_buddy *mm, | |
| 1088 | + u64 unaligned_offset, | |
| 1089 | + u64 size, | |
| 1090 | + u64 min_block_size, | |
| 1091 | + struct list_head *blocks) | |
| 1092 | +{ | |
| 1093 | + u64 aligned_offset = round_down(unaligned_offset, min_block_size); | |
| 1094 | + | |
| 1095 | + return __gpu_buddy_alloc_range(mm, aligned_offset, size, NULL, blocks); | |
| 1096 | +} | |
| 1097 | + | |
| 1087 | 1098 | static int __alloc_contig_try_harder(struct gpu_buddy *mm, |
| 1088 | 1099 | u64 size, |
| 1089 | 1100 | u64 min_block_size, |
| 1090 | 1101 | struct list_head *blocks) |
| 1091 | 1102 | { |
| 1092 | − u64 rhs_offset, lhs_offset, lhs_size, filled; | |
| 1103 | + u64 rhs_offset, lhs_offset, filled; | |
| 1093 | 1104 | struct gpu_buddy_block *block; |
| 1094 | 1105 | unsigned int tree, order; |
| 1095 | − LIST_HEAD(blocks_lhs); | |
| 1096 | − unsigned long pages; | |
| 1097 | 1106 | u64 modify_size; |
| 1098 | 1107 | int err; |
| 1099 | 1108 | |
| 1100 | 1109 | modify_size = rounddown_pow_of_two(size); |
| 1101 | − pages = modify_size >> ilog2(mm->chunk_size); | |
| 1102 | − order = fls(pages) - 1; | |
| 1110 | + order = ilog2(modify_size) - ilog2(mm->chunk_size); | |
| 1103 | 1111 | if (order == 0) |
| 1104 | 1112 | return -ENOSPC; |
| 1105 | 1113 | |
| @@ -1115,31 +1123,48 @@ static int __alloc_contig_try_harder(struct gpu_buddy *mm, · 9 unmodified lines | ||
| 1115 | 1123 | while (iter) { |
| 1116 | 1124 | block = rbtree_get_free_block(iter); |
| 1117 | 1125 | |
| 1118 | − /* Allocate blocks traversing RHS */ | |
| 1119 | 1126 | rhs_offset = gpu_buddy_block_offset(block); |
| 1127 | + | |
| 1128 | + /* Allocate blocks traversing RHS */ | |
| 1120 | 1129 | err = __gpu_buddy_alloc_range(mm, rhs_offset, size, |
| 1121 | 1130 | &filled, blocks); |
| 1122 | − if (!err || err != -ENOSPC) | |
| 1131 | + if (err && err != -ENOSPC) | |
| 1123 | 1132 | return err; |
| 1133 | + if (!err && IS_ALIGNED(rhs_offset, min_block_size)) | |
| 1134 | + return 0; | |
| 1135 | + if (!err) { | |
| 1136 | + /* Allocate the unaligned RHS offset using round_down */ | |
| 1137 | + gpu_buddy_free_list_internal(mm, blocks); | |
| 1138 | + err = __alloc_contig_aligned_retry(mm, rhs_offset, | |
| 1139 | + size, | |
| 1140 | + min_block_size, | |
| 1141 | + blocks); | |
| 1142 | + if (!err) | |
| 1143 | + return 0; | |
| 1144 | + if (err != -ENOSPC) { | |
| 1145 | + gpu_buddy_free_list_internal(mm, blocks); | |
| 1146 | + return err; | |
| 1147 | + } | |
| 1148 | + goto next; | |
| 1149 | + } | |
| 1124 | 1150 | |
| 1125 | − lhs_size = max((size - filled), min_block_size); | |
| 1126 | − if (!IS_ALIGNED(lhs_size, min_block_size)) | |
| 1127 | − lhs_size = round_up(lhs_size, min_block_size); | |
| 1151 | + if (size - filled > rhs_offset) | |
| 1152 | + goto next; | |
| 1128 | 1153 | |
| 1129 | − /* Allocate blocks traversing LHS */ | |
| 1130 | − lhs_offset = gpu_buddy_block_offset(block) - lhs_size; | |
| 1131 | − err = __gpu_buddy_alloc_range(mm, lhs_offset, lhs_size, | |
| 1132 | − NULL, &blocks_lhs); | |
| 1133 | − if (!err) { | |
| 1134 | − list_splice(&blocks_lhs, blocks); | |
| 1154 | + lhs_offset = rhs_offset - (size - filled); | |
| 1155 | + | |
| 1156 | + /* Allocate the unaligned LHS offset using round_down */ | |
| 1157 | + gpu_buddy_free_list_internal(mm, blocks); | |
| 1158 | + err = __alloc_contig_aligned_retry(mm, lhs_offset, size, | |
| 1159 | + min_block_size, blocks); | |
| 1160 | + if (!err) | |
| 1135 | 1161 | return 0; |
| 1136 | − } else if (err != -ENOSPC) { | |
| 1162 | + if (err != -ENOSPC) { | |
| 1137 | 1163 | gpu_buddy_free_list_internal(mm, blocks); |
| 1138 | 1164 | return err; |
| 1139 | 1165 | } |
| 1140 | − /* Free blocks for the next iteration */ | |
| 1166 | +next: | |
| 1141 | 1167 | gpu_buddy_free_list_internal(mm, blocks); |
| 1142 | − | |
| 1143 | 1168 | iter = rb_prev(iter); |
| 1144 | 1169 | } |
| 1145 | 1170 | } |
| @@ -3015,6 +3015,8 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid, · 3014 unmodified lines | ||
| 3015 | 3015 | is_compute_context = vm->is_compute_context; |
| 3016 | 3016 | |
| 3017 | 3017 | if (is_compute_context) { |
| 3018 | + __label__ drm_exec_retry; | |
| 3019 | + | |
| 3018 | 3020 | /* Release the root PD lock since svm_range_restore_pages |
| 3019 | 3021 | * might try to take it. |
| 3020 | 3022 | * TODO: rework svm_range_restore_pages so that this isn't |
| @@ -309,7 +309,9 @@ static void analogix_dp_get_adjust_training_lane(struct analogix_dp_device *dp, · 308 unmodified lines | ||
| 309 | 309 | lane_count = dp->link_train.lane_count; |
| 310 | 310 | for (lane = 0; lane < lane_count; lane++) { |
| 311 | 311 | voltage_swing = drm_dp_get_adjust_request_voltage(link_status, lane); |
| 312 | + voltage_swing >>= DP_TRAIN_VOLTAGE_SWING_SHIFT; | |
| 312 | 313 | pre_emphasis = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); |
| 314 | + pre_emphasis >>= DP_TRAIN_PRE_EMPHASIS_SHIFT; | |
| 313 | 315 | training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | |
| 314 | 316 | DPCD_PRE_EMPHASIS_SET(pre_emphasis); |
| 315 | 317 | |
| @@ -355,7 +357,9 @@ static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp) · 39 unmodified lines | ||
| 355 | 357 | for (lane = 0; lane < lane_count; lane++) { |
| 356 | 358 | training_lane = analogix_dp_get_lane_link_training(dp, lane); |
| 357 | 359 | voltage_swing = drm_dp_get_adjust_request_voltage(link_status, lane); |
| 360 | + voltage_swing >>= DP_TRAIN_VOLTAGE_SWING_SHIFT; | |
| 358 | 361 | pre_emphasis = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); |
| 362 | + pre_emphasis >>= DP_TRAIN_PRE_EMPHASIS_SHIFT; | |
| 359 | 363 | |
| 360 | 364 | if (DPCD_VOLTAGE_SWING_GET(training_lane) == voltage_swing && |
| 361 | 365 | DPCD_PRE_EMPHASIS_GET(training_lane) == pre_emphasis) |
| @@ -225,16 +225,106 @@ static void drm_fb_helper_resume_worker(struct work_struct *work) · 224 unmodified lines | ||
| 225 | 225 | console_unlock(); |
| 226 | 226 | } |
| 227 | 227 | |
| 228 | +static int find_crtc_index_atomic(struct drm_fb_helper *helper) | |
| 229 | +{ | |
| 230 | + struct drm_device *dev = helper->dev; | |
| 231 | + int crtc_index = -EINVAL; | |
| 232 | + struct drm_modeset_acquire_ctx ctx; | |
| 233 | + struct drm_plane *plane; | |
| 234 | + int ret = 0; | |
| 235 | + | |
| 236 | + drm_modeset_acquire_init(&ctx, 0); | |
| 237 | + | |
| 238 | +retry: | |
| 239 | + drm_for_each_plane(plane, dev) { | |
| 240 | + const struct drm_plane_state *plane_state; | |
| 241 | + | |
| 242 | + if (plane->type != DRM_PLANE_TYPE_PRIMARY) | |
| 243 | + continue; | |
| 244 | + | |
| 245 | + ret = drm_modeset_lock(&plane->mutex, &ctx); | |
| 246 | + if (ret) | |
| 247 | + goto err_drm_modeset_lock; | |
| 248 | + plane_state = plane->state; | |
| 249 | + | |
| 250 | + if (plane_state->fb == helper->fb && plane_state->crtc) { | |
| 251 | + struct drm_crtc *crtc = plane_state->crtc; | |
| 252 | + | |
| 253 | + ret = drm_modeset_lock(&crtc->mutex, &ctx); | |
| 254 | + if (ret) | |
| 255 | + goto err_drm_modeset_lock; | |
| 256 | + if (crtc->state->active) | |
| 257 | + crtc_index = crtc->index; | |
| 258 | + drm_modeset_unlock(&crtc->mutex); | |
| 259 | + } | |
| 260 | + drm_modeset_unlock(&plane->mutex); | |
| 261 | + | |
| 262 | + if (crtc_index >= 0) | |
| 263 | + break; | |
| 264 | + } | |
| 265 | + | |
| 266 | + drm_modeset_drop_locks(&ctx); | |
| 267 | + drm_modeset_acquire_fini(&ctx); | |
| 268 | + | |
| 269 | + return crtc_index; | |
| 270 | + | |
| 271 | +err_drm_modeset_lock: | |
| 272 | + if (ret == -EDEADLK) { | |
| 273 | + drm_modeset_backoff(&ctx); | |
| 274 | + goto retry; | |
| 275 | + } | |
| 276 | + return ret; | |
| 277 | +} | |
| 278 | + | |
| 279 | +static int find_crtc_index_legacy(struct drm_fb_helper *helper) | |
| 280 | +{ | |
| 281 | + struct drm_device *dev = helper->dev; | |
| 282 | + struct drm_crtc *crtc; | |
| 283 | + | |
| 284 | + drm_for_each_crtc(crtc, dev) { | |
| 285 | + struct drm_plane *plane = crtc->primary; | |
| 286 | + | |
| 287 | + if (!crtc->enabled) | |
| 288 | + continue; | |
| 289 | + if (!plane || plane->fb != helper->fb) | |
| 290 | + continue; /* CRTC doesn't display fbdev emulation */ | |
| 291 | + | |
| 292 | + return crtc->index; | |
| 293 | + } | |
| 294 | + | |
| 295 | + return -EINVAL; | |
| 296 | +} | |
| 297 | + | |
| 298 | +static int drm_fb_helper_find_crtc_index(struct drm_fb_helper *helper) | |
| 299 | +{ | |
| 300 | + struct drm_device *dev = helper->dev; | |
| 301 | + int crtc_index; | |
| 302 | + | |
| 303 | + mutex_lock(&dev->mode_config.mutex); | |
| 304 | + | |
| 305 | + if (drm_drv_uses_atomic_modeset(dev)) | |
| 306 | + crtc_index = find_crtc_index_atomic(helper); | |
| 307 | + else | |
| 308 | + crtc_index = find_crtc_index_legacy(helper); | |
| 309 | + | |
| 310 | + mutex_unlock(&dev->mode_config.mutex); | |
| 311 | + | |
| 312 | + return crtc_index; | |
| 313 | +} | |
| 314 | + | |
| 228 | 315 | static void drm_fb_helper_fb_dirty(struct drm_fb_helper *helper) |
| 229 | 316 | { |
| 230 | 317 | struct drm_device *dev = helper->dev; |
| 231 | 318 | struct drm_clip_rect *clip = &helper->damage_clip; |
| 232 | 319 | struct drm_clip_rect clip_copy; |
| 320 | + int crtc_index; | |
| 233 | 321 | unsigned long flags; |
| 234 | 322 | int ret; |
| 235 | 323 | |
| 236 | 324 | mutex_lock(&helper->lock); |
| 237 | − drm_client_modeset_wait_for_vblank(&helper->client, 0); | |
| 325 | + crtc_index = drm_fb_helper_find_crtc_index(helper); | |
| 326 | + if (crtc_index >= 0) | |
| 327 | + drm_client_modeset_wait_for_vblank(&helper->client, crtc_index); | |
| 238 | 328 | mutex_unlock(&helper->lock); |
| 239 | 329 | |
| 240 | 330 | if (drm_WARN_ON_ONCE(dev, !helper->funcs->fb_dirty)) |
| @@ -373,13 +373,25 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv) · 372 unmodified lines | ||
| 373 | 373 | return -EINVAL; |
| 374 | 374 | file_priv->supports_virtualized_cursor_plane = req->value; |
| 375 | 375 | break; |
| 376 | − case DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE: | |
| 376 | + case DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE: { | |
| 377 | + struct drm_plane *plane; | |
| 378 | + bool has_plane_with_color_pipeline = false; | |
| 379 | + | |
| 377 | 380 | if (!file_priv->atomic) |
| 378 | 381 | return -EINVAL; |
| 379 | 382 | if (req->value > 1) |
| 380 | 383 | return -EINVAL; |
| 384 | + drm_for_each_plane(plane, dev) { | |
| 385 | + if (plane->color_pipeline_property) { | |
| 386 | + has_plane_with_color_pipeline = true; | |
| 387 | + break; | |
| 388 | + } | |
| 389 | + } | |
| 390 | + if (!has_plane_with_color_pipeline) | |
| 391 | + return -EOPNOTSUPP; | |
| 381 | 392 | file_priv->plane_color_pipeline = req->value; |
| 382 | 393 | break; |
| 394 | + } | |
| 383 | 395 | default: |
| 384 | 396 | return -EINVAL; |
| 385 | 397 | } |
| @@ -309,8 +309,8 @@ int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_co · 308 unmodified lines | ||
| 309 | 309 | goto err_free_ctx; |
| 310 | 310 | |
| 311 | 311 | ctx->vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle); |
| 312 | − if (IS_ERR(ctx->vm_ctx)) { | |
| 313 | − err = PTR_ERR(ctx->vm_ctx); | |
| 312 | + if (!ctx->vm_ctx) { | |
| 313 | + err = -EINVAL; | |
| 314 | 314 | goto err_free_ctx; |
| 315 | 315 | } |
| 316 | 316 | |
| @@ -71,7 +71,7 @@ pvr_fw_trace_init_mask_set(const char *val, const struct kernel_param *kp) · 70 unmodified lines | ||
| 71 | 71 | return 0; |
| 72 | 72 | } |
| 73 | 73 | |
| 74 | −const struct kernel_param_ops pvr_fw_trace_init_mask_ops = { | |
| 74 | +static const struct kernel_param_ops pvr_fw_trace_init_mask_ops = { | |
| 75 | 75 | .set = pvr_fw_trace_init_mask_set, |
| 76 | 76 | .get = param_get_hexint, |
| 77 | 77 | }; |
| @@ -180,19 +180,27 @@ static void test_multiple_loops(struct kunit *test) · 179 unmodified lines | ||
| 180 | 180 | { |
| 181 | 181 | struct drm_exec exec; |
| 182 | 182 | |
| 183 | − drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0); | |
| 184 | − drm_exec_until_all_locked(&exec) | |
| 185 | 183 | { |
| 186 | − break; | |
| 184 | + __label__ drm_exec_retry; | |
| 185 | + | |
| 186 | + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0); | |
| 187 | + drm_exec_until_all_locked(&exec) | |
| 188 | + { | |
| 189 | + break; | |
| 190 | + } | |
| 191 | + drm_exec_fini(&exec); | |
| 187 | 192 | } |
| 188 | − drm_exec_fini(&exec); | |
| 189 | 193 | |
| 190 | − drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0); | |
| 191 | − drm_exec_until_all_locked(&exec) | |
| 192 | 194 | { |
| 193 | − break; | |
| 195 | + __label__ drm_exec_retry; | |
| 196 | + | |
| 197 | + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0); | |
| 198 | + drm_exec_until_all_locked(&exec) | |
| 199 | + { | |
| 200 | + break; | |
| 201 | + } | |
| 202 | + drm_exec_fini(&exec); | |
| 194 | 203 | } |
| 195 | − drm_exec_fini(&exec); | |
| 196 | 204 | KUNIT_SUCCEED(test); |
| 197 | 205 | } |
| 198 | 206 | |
| @@ -495,6 +495,8 @@ v3d_get_cpu_indirect_csd_params(struct drm_file *file_priv, · 494 unmodified lines | ||
| 495 | 495 | sizeof(indirect_csd.wg_uniform_offsets)); |
| 496 | 496 | |
| 497 | 497 | info->indirect = drm_gem_object_lookup(file_priv, indirect_csd.indirect); |
| 498 | + if (!info->indirect) | |
| 499 | + return -ENOENT; | |
| 498 | 500 | |
| 499 | 501 | return v3d_setup_csd_jobs_and_bos(file_priv, v3d, &indirect_csd.submit, |
| 500 | 502 | &info->job, &info->clean_job, |
| @@ -101,17 +101,6 @@ drm_exec_obj(struct drm_exec *exec, unsigned long index) · 100 unmodified lines | ||
| 101 | 101 | #define drm_exec_for_each_locked_object_reverse(exec, obj) \ |
| 102 | 102 | __drm_exec_for_each_locked_object_reverse(exec, obj, __UNIQUE_ID(drm_exec)) |
| 103 | 103 | |
| 104 | −/* | |
| 105 | − * Helper to drm_exec_until_all_locked(). Don't use directly. | |
| 106 | − * | |
| 107 | − * Since labels can't be defined local to the loop's body we use a jump pointer | |
| 108 | − * to make sure that the retry is only used from within the loop's body. | |
| 109 | − */ | |
| 110 | −#define __drm_exec_until_all_locked(exec, _label) \ | |
| 111 | −_label: \ | |
| 112 | − for (void *const __maybe_unused __drm_exec_retry_ptr = &&_label; \ | |
| 113 | − drm_exec_cleanup(exec);) | |
| 114 | − | |
| 115 | 104 | /** |
| 116 | 105 | * drm_exec_until_all_locked - loop until all GEM objects are locked |
| 117 | 106 | * @exec: drm_exec object |
| @@ -119,9 +108,18 @@ _label: \ · 1 unmodified line | ||
| 119 | 108 | * Core functionality of the drm_exec object. Loops until all GEM objects are |
| 120 | 109 | * locked and no more contention exists. At the beginning of the loop it is |
| 121 | 110 | * guaranteed that no GEM object is locked. |
| 111 | + * | |
| 112 | + * A global label name drm_exec_retry is used, if you need to use more than one | |
| 113 | + * instance of this macro in the same function the label needs to be made local | |
| 114 | + * to the block with the __label__ keyword. | |
| 122 | 115 | */ |
| 123 | 116 | #define drm_exec_until_all_locked(exec) \ |
| 124 | − __drm_exec_until_all_locked(exec, __UNIQUE_ID(drm_exec)) | |
| 117 | + for (bool const __maybe_unused __drm_exec_loop = false; \ | |
| 118 | + drm_exec_cleanup(exec);) \ | |
| 119 | + if (false) { \ | |
| 120 | +drm_exec_retry: __maybe_unused; \ | |
| 121 | + continue; \ | |
| 122 | + } else | |
| 125 | 123 | |
| 126 | 124 | /** |
| 127 | 125 | * drm_exec_retry_on_contention - restart the loop to grap all locks |
| @@ -129,12 +127,14 @@ _label: \ · 1 unmodified line | ||
| 129 | 127 | * |
| 130 | 128 | * Control flow helper to continue when a contention was detected and we need to |
| 131 | 129 | * clean up and re-start the loop to prepare all GEM objects. |
| 130 | + * The __drm_exec_loop check exists to prevent usage outside of an | |
| 131 | + * drm_exec_until_all_locked() loop. | |
| 132 | 132 | */ |
| 133 | 133 | #define drm_exec_retry_on_contention(exec) \ |
| 134 | 134 | do { \ |
| 135 | 135 | if (unlikely(drm_exec_is_contended(exec))) \ |
| 136 | − goto *__drm_exec_retry_ptr; \ | |
| 137 | − } while (0) | |
| 136 | + goto drm_exec_retry; \ | |
| 137 | + } while (__drm_exec_loop) | |
| 138 | 138 | |
| 139 | 139 | /** |
| 140 | 140 | * drm_exec_is_contended - check for contention |
| @@ -154,12 +154,14 @@ static inline bool drm_exec_is_contended(struct drm_exec *exec) · 13 unmodified lines | ||
| 154 | 154 | * |
| 155 | 155 | * Unconditionally retry the loop to lock all objects. For consistency, |
| 156 | 156 | * the exec object needs to be newly initialized. |
| 157 | + * The __drm_exec_loop check exists to prevent usage outside of an | |
| 158 | + * drm_exec_until_all_locked() loop. | |
| 157 | 159 | */ |
| 158 | 160 | #define drm_exec_retry(_exec) \ |
| 159 | 161 | do { \ |
| 160 | 162 | WARN_ON((_exec)->contended != DRM_EXEC_DUMMY); \ |
| 161 | − goto *__drm_exec_retry_ptr; \ | |
| 162 | − } while (0) | |
| 163 | + goto drm_exec_retry; \ | |
| 164 | + } while (__drm_exec_loop) | |
| 163 | 165 | |
| 164 | 166 | /** |
| 165 | 167 | * drm_exec_ticket - return the ww_acquire_ctx for this exec context |