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 | if (!hwctx->cus) | 875 | if (!hwctx->cus) |
| 876 | return -ENOMEM; | 876 | return -ENOMEM; |
| 877 | 877 | ||
| 878 | ret = amdxdna_pm_resume_get_locked(xdna); | 878 | ret = amdxdna_pm_resume_get(xdna); |
| 879 | if (ret) | 879 | if (ret) |
| 880 | goto free_cus; | 880 | goto free_cus; |
| 881 | 881 | ||
| @@ -900,13 +900,16 @@ free_cus: · 18 unmodified lines | |||
| 900 | static void aie2_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq) | 900 | static void aie2_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq) |
| 901 | { | 901 | { |
| 902 | struct dma_fence *out_fence = aie2_cmd_get_out_fence(hwctx, seq); | 902 | struct dma_fence *out_fence = aie2_cmd_get_out_fence(hwctx, seq); |
| 903 | struct amdxdna_dev *xdna = hwctx->client->xdna; | ||
| 903 | 904 | ||
| 904 | if (!out_fence) { | 905 | if (!out_fence) { |
| 905 | XDNA_ERR(hwctx->client->xdna, "Failed to get fence"); | 906 | XDNA_ERR(xdna, "Failed to get fence"); |
| 906 | return; | 907 | return; |
| 907 | } | 908 | } |
| 908 | 909 | ||
| 910 | mutex_unlock(&xdna->dev_lock); | ||
| 909 | dma_fence_wait_timeout(out_fence, false, MAX_SCHEDULE_TIMEOUT); | 911 | dma_fence_wait_timeout(out_fence, false, MAX_SCHEDULE_TIMEOUT); |
| 912 | mutex_lock(&xdna->dev_lock); | ||
| 910 | dma_fence_put(out_fence); | 913 | dma_fence_put(out_fence); |
| 911 | } | 914 | } |
| 912 | 915 | ||
| @@ -1039,7 +1042,7 @@ again: · 126 unmodified lines | |||
| 1039 | found = false; | 1042 | found = false; |
| 1040 | down_write(&xdna->notifier_lock); | 1043 | down_write(&xdna->notifier_lock); |
| 1041 | list_for_each_entry(mapp, &abo->mem.umap_list, node) { | 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 | found = true; | 1046 | found = true; |
| 1044 | break; | 1047 | break; |
| 1045 | } | 1048 | } |
| @@ -1050,11 +1053,9 @@ again: · 4 unmodified lines | |||
| 1050 | up_write(&xdna->notifier_lock); | 1053 | up_write(&xdna->notifier_lock); |
| 1051 | return 0; | 1054 | return 0; |
| 1052 | } | 1055 | } |
| 1053 | kref_get(&mapp->refcnt); | 1056 | |
| 1054 | up_write(&xdna->notifier_lock); | 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 | mm = mapp->notifier.mm; | 1059 | mm = mapp->notifier.mm; |
| 1059 | if (!mmget_not_zero(mm)) { | 1060 | if (!mmget_not_zero(mm)) { |
| 1060 | amdxdna_umap_put(mapp); | 1061 | amdxdna_umap_put(mapp); |
| @@ -1221,10 +1222,6 @@ int aie2_hwctx_heap_expand(struct amdxdna_hwctx *hwctx, · 160 unmodified lines | |||
| 1221 | u64 addr; | 1222 | u64 addr; |
| 1222 | int ret; | 1223 | int ret; |
| 1223 | 1224 | ||
| 1224 | ret = amdxdna_pm_resume_get_locked(xdna); | ||
| 1225 | if (ret) | ||
| 1226 | return ret; | ||
| 1227 | |||
| 1228 | addr = amdxdna_obj_dma_addr(heap); | 1225 | addr = amdxdna_obj_dma_addr(heap); |
| 1229 | ret = aie2_add_host_buf(xdna->dev_handle, hwctx->fw_ctx_id, | 1226 | ret = aie2_add_host_buf(xdna->dev_handle, hwctx->fw_ctx_id, |
| 1230 | addr, heap->mem.size); | 1227 | addr, heap->mem.size); |
| @@ -1233,7 +1230,5 @@ int aie2_hwctx_heap_expand(struct amdxdna_hwctx *hwctx, · 2 unmodified lines | |||
| 1233 | hwctx->name, heap->mem.size, ret); | 1230 | hwctx->name, heap->mem.size, ret); |
| 1234 | } | 1231 | } |
| 1235 | 1232 | ||
| 1236 | amdxdna_pm_suspend_put(xdna); | ||
| 1237 | |||
| 1238 | return ret; | 1233 | return ret; |
| 1239 | } | 1234 | } |
| @@ -840,7 +840,7 @@ static struct aie2_exec_msg_ops npu_exec_message_ops = { · 839 unmodified lines | |||
| 840 | static int aie2_init_exec_req(void *req, struct amdxdna_gem_obj *cmd_abo, | 840 | static int aie2_init_exec_req(void *req, struct amdxdna_gem_obj *cmd_abo, |
| 841 | size_t *size, u32 *msg_op) | 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 | int ret; | 844 | int ret; |
| 845 | u32 op; | 845 | u32 op; |
| 846 | 846 | ||
| @@ -874,7 +874,7 @@ static int · 27 unmodified lines | |||
| 874 | aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo, | 874 | aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo, |
| 875 | size_t *size, u32 *cmd_op) | 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 | int ret; | 878 | int ret; |
| 879 | u32 op; | 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 | if (!drm_dev_enter(dev, &idx)) | 310 | if (!drm_dev_enter(dev, &idx)) |
| 311 | return -ENODEV; | 311 | return -ENODEV; |
| 312 | 312 | ||
| 313 | mutex_lock(&xdna->client_lock); | ||
| 313 | mutex_lock(&xdna->dev_lock); | 314 | mutex_lock(&xdna->dev_lock); |
| 314 | hwctx = xa_erase(&client->hwctx_xa, args->handle); | 315 | hwctx = xa_erase(&client->hwctx_xa, args->handle); |
| 315 | if (!hwctx) { | 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 | XDNA_DBG(xdna, "PID %d destroyed HW context %d", client->pid, args->handle); | 329 | XDNA_DBG(xdna, "PID %d destroyed HW context %d", client->pid, args->handle); |
| 329 | out: | 330 | out: |
| 330 | mutex_unlock(&xdna->dev_lock); | 331 | mutex_unlock(&xdna->dev_lock); |
| 332 | mutex_unlock(&xdna->client_lock); | ||
| 331 | drm_dev_exit(idx); | 333 | drm_dev_exit(idx); |
| 332 | return ret; | 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 | return -EINVAL; | 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 | hwctx = xa_load(&client->hwctx_xa, args->handle); | 395 | hwctx = xa_load(&client->hwctx_xa, args->handle); |
| 387 | if (!hwctx) { | 396 | if (!hwctx) { |
| 388 | XDNA_DBG(xdna, "PID %d failed to get hwctx %d", client->pid, args->handle); | 397 | XDNA_DBG(xdna, "PID %d failed to get hwctx %d", client->pid, args->handle); |
| 389 | ret = -EINVAL; | 398 | ret = -EINVAL; |
| 390 | goto free_buf; | 399 | goto unlock; |
| 391 | } | 400 | } |
| 392 | 401 | ||
| 393 | ret = xdna->dev_info->ops->hwctx_config(hwctx, args->param_type, val, buf, buf_size); | 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 | free_buf: | 408 | free_buf: |
| 396 | kfree(buf); | 409 | kfree(buf); |
| 397 | return ret; | 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 | if (!gobj) | 425 | if (!gobj) |
| 413 | return -EINVAL; | 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 | abo = to_xdna_obj(gobj); | 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 | hwctx = xa_load(&client->hwctx_xa, abo->assigned_hwctx); | 437 | hwctx = xa_load(&client->hwctx_xa, abo->assigned_hwctx); |
| 418 | if (!hwctx) { | 438 | if (!hwctx) { |
| 419 | ret = -EINVAL; | 439 | ret = -EINVAL; |
| 420 | goto put_obj; | 440 | goto unlock; |
| 421 | } | 441 | } |
| 422 | 442 | ||
| 423 | ret = xdna->dev_info->ops->hwctx_sync_debug_bo(hwctx, debug_bo_hdl); | 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 | put_obj: | 449 | put_obj: |
| 426 | drm_gem_object_put(gobj); | 450 | drm_gem_object_put(gobj); |
| 427 | return ret; | 451 | return ret; |
| @@ -448,9 +472,7 @@ static int amdxdna_hwctx_expand_heap(struct amdxdna_hwctx *hwctx) · 20 unmodified lines | |||
| 448 | break; | 472 | break; |
| 449 | } | 473 | } |
| 450 | 474 | ||
| 451 | mutex_unlock(&client->mm_lock); | ||
| 452 | ret = xdna->dev_info->ops->hwctx_heap_expand(hwctx, heap); | 475 | ret = xdna->dev_info->ops->hwctx_heap_expand(hwctx, heap); |
| 453 | mutex_lock(&client->mm_lock); | ||
| 454 | if (ret) { | 476 | if (ret) { |
| 455 | amdxdna_gem_unpin(heap); | 477 | amdxdna_gem_unpin(heap); |
| 456 | drm_gem_object_put(to_gobj(heap)); | 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 | unsigned long hwctx_id; | 491 | unsigned long hwctx_id; |
| 470 | int ret; | 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) | 498 | mutex_lock(&client->mm_lock); |
| 475 | return amdxdna_hwctx_expand_heap(hwctx); | ||
| 476 | 499 | ||
| 477 | amdxdna_for_each_hwctx(client, hwctx_id, hwctx) { | 500 | if (hwctx) { |
| 478 | ret = amdxdna_hwctx_expand_heap(hwctx); | 501 | ret = amdxdna_hwctx_expand_heap(hwctx); |
| 479 | if (ret) | 502 | } else { |
| 480 | return ret; | 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 | static void | 516 | static void |
| @@ -198,6 +198,7 @@ amdxdna_gem_destroy_obj(struct amdxdna_gem_obj *abo) · 197 unmodified lines | |||
| 198 | */ | 198 | */ |
| 199 | void *amdxdna_gem_vmap(struct amdxdna_gem_obj *abo) | 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 | struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL); | 202 | struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL); |
| 202 | int ret; | 203 | int ret; |
| 203 | 204 | ||
| @@ -210,7 +211,7 @@ void *amdxdna_gem_vmap(struct amdxdna_gem_obj *abo) · 6 unmodified lines | |||
| 210 | if (!abo->mem.kva) { | 211 | if (!abo->mem.kva) { |
| 211 | ret = drm_gem_vmap(to_gobj(abo), &map); | 212 | ret = drm_gem_vmap(to_gobj(abo), &map); |
| 212 | if (ret) | 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 | else | 215 | else |
| 215 | abo->mem.kva = map.vaddr; | 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 | xdna = to_xdna_dev(to_gobj(abo)->dev); | 256 | xdna = to_xdna_dev(to_gobj(abo)->dev); |
| 256 | XDNA_DBG(xdna, "Invalidating range 0x%lx, 0x%lx, type %d", | 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 | if (!mmu_notifier_range_blockable(range)) | 260 | if (!mmu_notifier_range_blockable(range)) |
| 260 | return false; | 261 | return false; |
| @@ -284,15 +285,23 @@ static const struct mmu_interval_notifier_ops amdxdna_hmm_ops = { · 23 unmodified lines | |||
| 284 | .invalidate = amdxdna_hmm_invalidate, | 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 | static void amdxdna_hmm_unregister(struct amdxdna_gem_obj *abo, | 296 | static void amdxdna_hmm_unregister(struct amdxdna_gem_obj *abo, |
| 288 | struct vm_area_struct *vma) | 297 | struct vm_area_struct *vma) |
| 289 | { | 298 | { |
| 290 | struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev); | 299 | struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev); |
| 291 | struct amdxdna_umap *mapp; | 300 | struct amdxdna_umap *mapp; |
| 292 | 301 | ||
| 293 | down_read(&xdna->notifier_lock); | 302 | down_write(&xdna->notifier_lock); |
| 294 | list_for_each_entry(mapp, &abo->mem.umap_list, node) { | 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 | if (!mapp->unmapped) { | 305 | if (!mapp->unmapped) { |
| 297 | queue_work(xdna->notifier_wq, &mapp->hmm_unreg_work); | 306 | queue_work(xdna->notifier_wq, &mapp->hmm_unreg_work); |
| 298 | mapp->unmapped = true; | 307 | mapp->unmapped = true; |
| @@ -301,19 +310,16 @@ static void amdxdna_hmm_unregister(struct amdxdna_gem_obj *abo, · 2 unmodified lines | |||
| 301 | break; | 310 | break; |
| 302 | } | 311 | } |
| 303 | } | 312 | } |
| 304 | up_read(&xdna->notifier_lock); | 313 | up_write(&xdna->notifier_lock); |
| 305 | } | 314 | } |
| 306 | 315 | ||
| 307 | static void amdxdna_umap_release(struct kref *ref) | 316 | static void amdxdna_umap_release(struct kref *ref) |
| 308 | { | 317 | { |
| 309 | struct amdxdna_umap *mapp = container_of(ref, struct amdxdna_umap, refcnt); | 318 | struct amdxdna_umap *mapp = container_of(ref, struct amdxdna_umap, refcnt); |
| 310 | struct amdxdna_gem_obj *abo = mapp->abo; | 319 | struct amdxdna_gem_obj *abo = mapp->abo; |
| 311 | struct vm_area_struct *vma = mapp->vma; | ||
| 312 | struct amdxdna_dev *xdna; | 320 | struct amdxdna_dev *xdna; |
| 313 | 321 | ||
| 314 | mmu_interval_notifier_remove(&mapp->notifier); | 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 | xdna = to_xdna_dev(to_gobj(mapp->abo)->dev); | 324 | xdna = to_xdna_dev(to_gobj(mapp->abo)->dev); |
| 319 | down_write(&xdna->notifier_lock); | 325 | down_write(&xdna->notifier_lock); |
| @@ -346,15 +352,30 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, · 26 unmodified lines | |||
| 346 | unsigned long len = vma->vm_end - vma->vm_start; | 352 | unsigned long len = vma->vm_end - vma->vm_start; |
| 347 | unsigned long addr = vma->vm_start; | 353 | unsigned long addr = vma->vm_start; |
| 348 | struct amdxdna_umap *mapp; | 354 | struct amdxdna_umap *mapp; |
| 349 | u32 nr_pages; | 355 | unsigned long nr_pages; |
| 350 | int ret; | 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 | /* Need to set uva for heap uva validation */ | 365 | /* Need to set uva for heap uva validation */ |
| 354 | abo->mem.uva = addr; | 366 | abo->mem.uva = addr; |
| 355 | return 0; | 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 | mapp = kzalloc_obj(*mapp); | 379 | mapp = kzalloc_obj(*mapp); |
| 359 | if (!mapp) | 380 | if (!mapp) |
| 360 | return -ENOMEM; | 381 | return -ENOMEM; |
| @@ -380,13 +401,10 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, · 19 unmodified lines | |||
| 380 | mapp->range.start = vma->vm_start; | 401 | mapp->range.start = vma->vm_start; |
| 381 | mapp->range.end = vma->vm_end; | 402 | mapp->range.end = vma->vm_end; |
| 382 | mapp->range.default_flags = HMM_PFN_REQ_FAULT; | 403 | mapp->range.default_flags = HMM_PFN_REQ_FAULT; |
| 383 | mapp->vma = vma; | ||
| 384 | mapp->abo = abo; | 404 | mapp->abo = abo; |
| 385 | kref_init(&mapp->refcnt); | 405 | kref_init(&mapp->refcnt); |
| 386 | 406 | ||
| 387 | INIT_WORK(&mapp->hmm_unreg_work, amdxdna_hmm_unreg_work); | 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 | down_write(&xdna->notifier_lock); | 409 | down_write(&xdna->notifier_lock); |
| 392 | if (list_empty(&abo->mem.umap_list)) | 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 | close_vma: | 546 | close_vma: |
| 529 | vma->vm_ops->close(vma); | 547 | vma->vm_ops->close(vma); |
| 548 | return ret; | ||
| 530 | put_obj: | 549 | put_obj: |
| 531 | drm_gem_object_put(gobj); | 550 | drm_gem_object_put(gobj); |
| 532 | return ret; | 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 | /* No need to set up dma addr mapping in PASID mode. */ | 671 | /* No need to set up dma addr mapping in PASID mode. */ |
| 653 | if (!amdxdna_pasid_on(abo->client)) { | 672 | if (!amdxdna_pasid_on(abo->client)) { |
| 654 | ret = amdxdna_dma_map_bo(xdna, abo); | 673 | ret = amdxdna_dma_map_bo(xdna, abo); |
| 655 | if (ret) | 674 | if (ret) { |
| 675 | abo->open_ref--; | ||
| 676 | abo->client = NULL; | ||
| 656 | return ret; | 677 | return ret; |
| 678 | } | ||
| 657 | } | 679 | } |
| 658 | 680 | ||
| 659 | amdxdna_gem_add_bo_usage(abo); | 681 | amdxdna_gem_add_bo_usage(abo); |
| @@ -12,7 +12,6 @@ · 11 unmodified lines | |||
| 12 | #include "amdxdna_pci_drv.h" | 12 | #include "amdxdna_pci_drv.h" |
| 13 | 13 | ||
| 14 | struct amdxdna_umap { | 14 | struct amdxdna_umap { |
| 15 | struct vm_area_struct *vma; | ||
| 16 | struct mmu_interval_notifier notifier; | 15 | struct mmu_interval_notifier notifier; |
| 17 | struct hmm_range range; | 16 | struct hmm_range range; |
| 18 | struct work_struct hmm_unreg_work; | 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 | static inline u64 amdxdna_dev_bo_offset(struct amdxdna_gem_obj *abo) | 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 | static inline u64 amdxdna_obj_dma_addr(struct amdxdna_gem_obj *abo) | 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 | void amdxdna_umap_put(struct amdxdna_umap *mapp); | 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 | struct amdxdna_dev *xdna = to_xdna_dev(ddev); | 110 | struct amdxdna_dev *xdna = to_xdna_dev(ddev); |
| 111 | struct amdxdna_client *client; | 111 | struct amdxdna_client *client; |
| 112 | int ret; | ||
| 112 | 113 | ||
| 113 | client = kzalloc_obj(*client); | 114 | client = kzalloc_obj(*client); |
| 114 | if (!client) | 115 | if (!client) |
| 115 | return -ENOMEM; | 116 | return -ENOMEM; |
| 116 | 117 | ||
| 118 | ret = init_srcu_struct(&client->hwctx_srcu); | ||
| 119 | if (ret) | ||
| 120 | goto free_client; | ||
| 121 | |||
| 117 | client->pid = pid_nr(rcu_access_pointer(filp->pid)); | 122 | client->pid = pid_nr(rcu_access_pointer(filp->pid)); |
| 118 | client->xdna = xdna; | 123 | client->xdna = xdna; |
| 119 | client->pasid = IOMMU_PASID_INVALID; | 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 | XDNA_WARN(xdna, "PASID not available for pid %d", client->pid); | 130 | XDNA_WARN(xdna, "PASID not available for pid %d", client->pid); |
| 126 | if (!amdxdna_use_carveout(xdna)) { | 131 | if (!amdxdna_use_carveout(xdna)) { |
| 127 | XDNA_ERR(xdna, "PASID unavailable and carveout not configured"); | 132 | XDNA_ERR(xdna, "PASID unavailable and carveout not configured"); |
| 128 | kfree(client); | 133 | ret = -EINVAL; |
| 129 | return -EINVAL; | 134 | goto cleanup_srcu; |
| 130 | } | 135 | } |
| 131 | } | 136 | } |
| 132 | } | 137 | } |
| 133 | mmgrab(client->mm); | 138 | mmgrab(client->mm); |
| 134 | init_srcu_struct(&client->hwctx_srcu); | ||
| 135 | xa_init_flags(&client->hwctx_xa, XA_FLAGS_ALLOC); | 139 | xa_init_flags(&client->hwctx_xa, XA_FLAGS_ALLOC); |
| 136 | xa_init_flags(&client->dev_heap_xa, XA_FLAGS_ALLOC); | 140 | xa_init_flags(&client->dev_heap_xa, XA_FLAGS_ALLOC); |
| 137 | drm_mm_init(&client->dev_heap_mm, xdna->dev_info->dev_mem_base, | 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 | XDNA_DBG(xdna, "pid %d opened", client->pid); | 154 | XDNA_DBG(xdna, "pid %d opened", client->pid); |
| 151 | return 0; | 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 | static void amdxdna_client_cleanup(struct amdxdna_client *client) | 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 | if (ret) | 383 | if (ret) |
| 374 | return ret; | 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 | init_rwsem(&xdna->notifier_lock); | 390 | init_rwsem(&xdna->notifier_lock); |
| 378 | INIT_LIST_HEAD(&xdna->client_list); | 391 | INIT_LIST_HEAD(&xdna->client_list); |
| 379 | pci_set_drvdata(pdev, xdna); | 392 | pci_set_drvdata(pdev, xdna); |
| @@ -1084,22 +1084,30 @@ static int __gpu_buddy_alloc_range(struct gpu_buddy *mm, · 1083 unmodified lines | |||
| 1084 | blocks, total_allocated_on_err); | 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 | static int __alloc_contig_try_harder(struct gpu_buddy *mm, | 1098 | static int __alloc_contig_try_harder(struct gpu_buddy *mm, |
| 1088 | u64 size, | 1099 | u64 size, |
| 1089 | u64 min_block_size, | 1100 | u64 min_block_size, |
| 1090 | struct list_head *blocks) | 1101 | struct list_head *blocks) |
| 1091 | { | 1102 | { |
| 1092 | u64 rhs_offset, lhs_offset, lhs_size, filled; | 1103 | u64 rhs_offset, lhs_offset, filled; |
| 1093 | struct gpu_buddy_block *block; | 1104 | struct gpu_buddy_block *block; |
| 1094 | unsigned int tree, order; | 1105 | unsigned int tree, order; |
| 1095 | LIST_HEAD(blocks_lhs); | ||
| 1096 | unsigned long pages; | ||
| 1097 | u64 modify_size; | 1106 | u64 modify_size; |
| 1098 | int err; | 1107 | int err; |
| 1099 | 1108 | ||
| 1100 | modify_size = rounddown_pow_of_two(size); | 1109 | modify_size = rounddown_pow_of_two(size); |
| 1101 | pages = modify_size >> ilog2(mm->chunk_size); | 1110 | order = ilog2(modify_size) - ilog2(mm->chunk_size); |
| 1102 | order = fls(pages) - 1; | ||
| 1103 | if (order == 0) | 1111 | if (order == 0) |
| 1104 | return -ENOSPC; | 1112 | return -ENOSPC; |
| 1105 | 1113 | ||
| @@ -1115,31 +1123,48 @@ static int __alloc_contig_try_harder(struct gpu_buddy *mm, · 9 unmodified lines | |||
| 1115 | while (iter) { | 1123 | while (iter) { |
| 1116 | block = rbtree_get_free_block(iter); | 1124 | block = rbtree_get_free_block(iter); |
| 1117 | 1125 | ||
| 1118 | /* Allocate blocks traversing RHS */ | ||
| 1119 | rhs_offset = gpu_buddy_block_offset(block); | 1126 | rhs_offset = gpu_buddy_block_offset(block); |
| 1127 | |||
| 1128 | /* Allocate blocks traversing RHS */ | ||
| 1120 | err = __gpu_buddy_alloc_range(mm, rhs_offset, size, | 1129 | err = __gpu_buddy_alloc_range(mm, rhs_offset, size, |
| 1121 | &filled, blocks); | 1130 | &filled, blocks); |
| 1122 | if (!err || err != -ENOSPC) | 1131 | if (err && err != -ENOSPC) |
| 1123 | return err; | 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); | 1151 | if (size - filled > rhs_offset) |
| 1126 | if (!IS_ALIGNED(lhs_size, min_block_size)) | 1152 | goto next; |
| 1127 | lhs_size = round_up(lhs_size, min_block_size); | ||
| 1128 | 1153 | ||
| 1129 | /* Allocate blocks traversing LHS */ | 1154 | lhs_offset = rhs_offset - (size - filled); |
| 1130 | lhs_offset = gpu_buddy_block_offset(block) - lhs_size; | 1155 | |
| 1131 | err = __gpu_buddy_alloc_range(mm, lhs_offset, lhs_size, | 1156 | /* Allocate the unaligned LHS offset using round_down */ |
| 1132 | NULL, &blocks_lhs); | 1157 | gpu_buddy_free_list_internal(mm, blocks); |
| 1133 | if (!err) { | 1158 | err = __alloc_contig_aligned_retry(mm, lhs_offset, size, |
| 1134 | list_splice(&blocks_lhs, blocks); | 1159 | min_block_size, blocks); |
| 1160 | if (!err) | ||
| 1135 | return 0; | 1161 | return 0; |
| 1136 | } else if (err != -ENOSPC) { | 1162 | if (err != -ENOSPC) { |
| 1137 | gpu_buddy_free_list_internal(mm, blocks); | 1163 | gpu_buddy_free_list_internal(mm, blocks); |
| 1138 | return err; | 1164 | return err; |
| 1139 | } | 1165 | } |
| 1140 | /* Free blocks for the next iteration */ | 1166 | next: |
| 1141 | gpu_buddy_free_list_internal(mm, blocks); | 1167 | gpu_buddy_free_list_internal(mm, blocks); |
| 1142 | |||
| 1143 | iter = rb_prev(iter); | 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 | is_compute_context = vm->is_compute_context; | 3015 | is_compute_context = vm->is_compute_context; |
| 3016 | 3016 | ||
| 3017 | if (is_compute_context) { | 3017 | if (is_compute_context) { |
| 3018 | __label__ drm_exec_retry; | ||
| 3019 | |||
| 3018 | /* Release the root PD lock since svm_range_restore_pages | 3020 | /* Release the root PD lock since svm_range_restore_pages |
| 3019 | * might try to take it. | 3021 | * might try to take it. |
| 3020 | * TODO: rework svm_range_restore_pages so that this isn't | 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 | lane_count = dp->link_train.lane_count; | 309 | lane_count = dp->link_train.lane_count; |
| 310 | for (lane = 0; lane < lane_count; lane++) { | 310 | for (lane = 0; lane < lane_count; lane++) { |
| 311 | voltage_swing = drm_dp_get_adjust_request_voltage(link_status, lane); | 311 | voltage_swing = drm_dp_get_adjust_request_voltage(link_status, lane); |
| 312 | voltage_swing >>= DP_TRAIN_VOLTAGE_SWING_SHIFT; | ||
| 312 | pre_emphasis = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); | 313 | pre_emphasis = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); |
| 314 | pre_emphasis >>= DP_TRAIN_PRE_EMPHASIS_SHIFT; | ||
| 313 | training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | | 315 | training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | |
| 314 | DPCD_PRE_EMPHASIS_SET(pre_emphasis); | 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 | for (lane = 0; lane < lane_count; lane++) { | 357 | for (lane = 0; lane < lane_count; lane++) { |
| 356 | training_lane = analogix_dp_get_lane_link_training(dp, lane); | 358 | training_lane = analogix_dp_get_lane_link_training(dp, lane); |
| 357 | voltage_swing = drm_dp_get_adjust_request_voltage(link_status, lane); | 359 | voltage_swing = drm_dp_get_adjust_request_voltage(link_status, lane); |
| 360 | voltage_swing >>= DP_TRAIN_VOLTAGE_SWING_SHIFT; | ||
| 358 | pre_emphasis = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); | 361 | pre_emphasis = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); |
| 362 | pre_emphasis >>= DP_TRAIN_PRE_EMPHASIS_SHIFT; | ||
| 359 | 363 | ||
| 360 | if (DPCD_VOLTAGE_SWING_GET(training_lane) == voltage_swing && | 364 | if (DPCD_VOLTAGE_SWING_GET(training_lane) == voltage_swing && |
| 361 | DPCD_PRE_EMPHASIS_GET(training_lane) == pre_emphasis) | 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 | console_unlock(); | 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 | static void drm_fb_helper_fb_dirty(struct drm_fb_helper *helper) | 315 | static void drm_fb_helper_fb_dirty(struct drm_fb_helper *helper) |
| 229 | { | 316 | { |
| 230 | struct drm_device *dev = helper->dev; | 317 | struct drm_device *dev = helper->dev; |
| 231 | struct drm_clip_rect *clip = &helper->damage_clip; | 318 | struct drm_clip_rect *clip = &helper->damage_clip; |
| 232 | struct drm_clip_rect clip_copy; | 319 | struct drm_clip_rect clip_copy; |
| 320 | int crtc_index; | ||
| 233 | unsigned long flags; | 321 | unsigned long flags; |
| 234 | int ret; | 322 | int ret; |
| 235 | 323 | ||
| 236 | mutex_lock(&helper->lock); | 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 | mutex_unlock(&helper->lock); | 328 | mutex_unlock(&helper->lock); |
| 239 | 329 | ||
| 240 | if (drm_WARN_ON_ONCE(dev, !helper->funcs->fb_dirty)) | 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 | return -EINVAL; | 373 | return -EINVAL; |
| 374 | file_priv->supports_virtualized_cursor_plane = req->value; | 374 | file_priv->supports_virtualized_cursor_plane = req->value; |
| 375 | break; | 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 | if (!file_priv->atomic) | 380 | if (!file_priv->atomic) |
| 378 | return -EINVAL; | 381 | return -EINVAL; |
| 379 | if (req->value > 1) | 382 | if (req->value > 1) |
| 380 | return -EINVAL; | 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 | file_priv->plane_color_pipeline = req->value; | 392 | file_priv->plane_color_pipeline = req->value; |
| 382 | break; | 393 | break; |
| 394 | } | ||
| 383 | default: | 395 | default: |
| 384 | return -EINVAL; | 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 | goto err_free_ctx; | 309 | goto err_free_ctx; |
| 310 | 310 | ||
| 311 | ctx->vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle); | 311 | ctx->vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle); |
| 312 | if (IS_ERR(ctx->vm_ctx)) { | 312 | if (!ctx->vm_ctx) { |
| 313 | err = PTR_ERR(ctx->vm_ctx); | 313 | err = -EINVAL; |
| 314 | goto err_free_ctx; | 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 | return 0; | 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 | .set = pvr_fw_trace_init_mask_set, | 75 | .set = pvr_fw_trace_init_mask_set, |
| 76 | .get = param_get_hexint, | 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 | struct drm_exec exec; | 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 | KUNIT_SUCCEED(test); | 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 | sizeof(indirect_csd.wg_uniform_offsets)); | 495 | sizeof(indirect_csd.wg_uniform_offsets)); |
| 496 | 496 | ||
| 497 | info->indirect = drm_gem_object_lookup(file_priv, indirect_csd.indirect); | 497 | info->indirect = drm_gem_object_lookup(file_priv, indirect_csd.indirect); |
| 498 | if (!info->indirect) | ||
| 499 | return -ENOENT; | ||
| 498 | 500 | ||
| 499 | return v3d_setup_csd_jobs_and_bos(file_priv, v3d, &indirect_csd.submit, | 501 | return v3d_setup_csd_jobs_and_bos(file_priv, v3d, &indirect_csd.submit, |
| 500 | &info->job, &info->clean_job, | 502 | &info->job, &info->clean_job, |
| @@ -101,17 +101,6 @@ drm_exec_obj(struct drm_exec *exec, unsigned long index) · 100 unmodified lines | |||
| 101 | #define drm_exec_for_each_locked_object_reverse(exec, obj) \ | 101 | #define drm_exec_for_each_locked_object_reverse(exec, obj) \ |
| 102 | __drm_exec_for_each_locked_object_reverse(exec, obj, __UNIQUE_ID(drm_exec)) | 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 | * drm_exec_until_all_locked - loop until all GEM objects are locked | 105 | * drm_exec_until_all_locked - loop until all GEM objects are locked |
| 117 | * @exec: drm_exec object | 106 | * @exec: drm_exec object |
| @@ -119,9 +108,18 @@ _label: \ · 1 unmodified line | |||
| 119 | * Core functionality of the drm_exec object. Loops until all GEM objects are | 108 | * Core functionality of the drm_exec object. Loops until all GEM objects are |
| 120 | * locked and no more contention exists. At the beginning of the loop it is | 109 | * locked and no more contention exists. At the beginning of the loop it is |
| 121 | * guaranteed that no GEM object is locked. | 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 | #define drm_exec_until_all_locked(exec) \ | 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 | * drm_exec_retry_on_contention - restart the loop to grap all locks | 125 | * drm_exec_retry_on_contention - restart the loop to grap all locks |
| @@ -129,12 +127,14 @@ _label: \ · 1 unmodified line | |||
| 129 | * | 127 | * |
| 130 | * Control flow helper to continue when a contention was detected and we need to | 128 | * Control flow helper to continue when a contention was detected and we need to |
| 131 | * clean up and re-start the loop to prepare all GEM objects. | 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 | #define drm_exec_retry_on_contention(exec) \ | 133 | #define drm_exec_retry_on_contention(exec) \ |
| 134 | do { \ | 134 | do { \ |
| 135 | if (unlikely(drm_exec_is_contended(exec))) \ | 135 | if (unlikely(drm_exec_is_contended(exec))) \ |
| 136 | goto *__drm_exec_retry_ptr; \ | 136 | goto drm_exec_retry; \ |
| 137 | } while (0) | 137 | } while (__drm_exec_loop) |
| 138 | 138 | ||
| 139 | /** | 139 | /** |
| 140 | * drm_exec_is_contended - check for contention | 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 | * Unconditionally retry the loop to lock all objects. For consistency, | 155 | * Unconditionally retry the loop to lock all objects. For consistency, |
| 156 | * the exec object needs to be newly initialized. | 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 | #define drm_exec_retry(_exec) \ | 160 | #define drm_exec_retry(_exec) \ |
| 159 | do { \ | 161 | do { \ |
| 160 | WARN_ON((_exec)->contended != DRM_EXEC_DUMMY); \ | 162 | WARN_ON((_exec)->contended != DRM_EXEC_DUMMY); \ |
| 161 | goto *__drm_exec_retry_ptr; \ | 163 | goto drm_exec_retry; \ |
| 162 | } while (0) | 164 | } while (__drm_exec_loop) |
| 163 | 165 | ||
| 164 | /** | 166 | /** |
| 165 | * drm_exec_ticket - return the ww_acquire_ctx for this exec context | 167 | * drm_exec_ticket - return the ww_acquire_ctx for this exec context |