1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2014-2018 Broadcom 4 * Copyright (C) 2023 Raspberry Pi 5 */ 6 7 #include <drm/drm_syncobj.h> 8 9 #include "v3d_drv.h" 10 #include "v3d_regs.h" 11 #include "v3d_trace.h" 12 13 /* Takes the reservation lock on all the BOs being referenced, so that 14 * at queue submit time we can update the reservations. 15 * 16 * We don't lock the RCL the tile alloc/state BOs, or overflow memory 17 * (all of which are on exec->unref_list). They're entirely private 18 * to v3d, so we don't attach dma-buf fences to them. 19 */ 20 static int 21 v3d_lock_bo_reservations(struct v3d_job *job, 22 struct ww_acquire_ctx *acquire_ctx) 23 { 24 int i, ret; 25 26 ret = drm_gem_lock_reservations(job->bo, job->bo_count, acquire_ctx); 27 if (ret) 28 return ret; 29 30 for (i = 0; i < job->bo_count; i++) { 31 ret = dma_resv_reserve_fences(job->bo[i]->resv, 1); 32 if (ret) 33 goto fail; 34 35 ret = drm_sched_job_add_implicit_dependencies(&job->base, 36 job->bo[i], true); 37 if (ret) 38 goto fail; 39 } 40 41 return 0; 42 43 fail: 44 drm_gem_unlock_reservations(job->bo, job->bo_count, acquire_ctx); 45 return ret; 46 } 47 48 /** 49 * v3d_lookup_bos() - Sets up job->bo[] with the GEM objects 50 * referenced by the job. 51 * @dev: DRM device 52 * @file_priv: DRM file for this fd 53 * @job: V3D job being set up 54 * @bo_handles: GEM handles 55 * @bo_count: Number of GEM handles passed in 56 * 57 * The command validator needs to reference BOs by their index within 58 * the submitted job's BO list. This does the validation of the job's 59 * BO list and reference counting for the lifetime of the job. 60 * 61 * Note that this function doesn't need to unreference the BOs on 62 * failure, because that will happen at v3d_exec_cleanup() time. 63 */ 64 static int 65 v3d_lookup_bos(struct drm_device *dev, 66 struct drm_file *file_priv, 67 struct v3d_job *job, 68 u64 bo_handles, 69 u32 bo_count) 70 { 71 job->bo_count = bo_count; 72 73 if (!job->bo_count) { 74 /* See comment on bo_index for why we have to check 75 * this. 76 */ 77 DRM_DEBUG("Rendering requires BOs\n"); 78 return -EINVAL; 79 } 80 81 return drm_gem_objects_lookup(file_priv, 82 (void __user *)(uintptr_t)bo_handles, 83 job->bo_count, &job->bo); 84 } 85 86 static void 87 v3d_job_free(struct kref *ref) 88 { 89 struct v3d_job *job = container_of(ref, struct v3d_job, refcount); 90 int i; 91 92 if (job->bo) { 93 for (i = 0; i < job->bo_count; i++) 94 drm_gem_object_put(job->bo[i]); 95 kvfree(job->bo); 96 } 97 98 dma_fence_put(job->irq_fence); 99 dma_fence_put(job->done_fence); 100 101 if (job->perfmon) 102 v3d_perfmon_put(job->perfmon); 103 104 kfree(job); 105 } 106 107 static void 108 v3d_render_job_free(struct kref *ref) 109 { 110 struct v3d_render_job *job = container_of(ref, struct v3d_render_job, 111 base.refcount); 112 struct v3d_bo *bo, *save; 113 114 list_for_each_entry_safe(bo, save, &job->unref_list, unref_head) { 115 drm_gem_object_put(&bo->base.base); 116 } 117 118 v3d_job_free(ref); 119 } 120 121 void v3d_job_cleanup(struct v3d_job *job) 122 { 123 if (!job) 124 return; 125 126 drm_sched_job_cleanup(&job->base); 127 v3d_job_put(job); 128 } 129 130 void v3d_job_put(struct v3d_job *job) 131 { 132 if (!job) 133 return; 134 135 kref_put(&job->refcount, job->free); 136 } 137 138 static int 139 v3d_job_allocate(void **container, size_t size) 140 { 141 *container = kcalloc(1, size, GFP_KERNEL); 142 if (!*container) { 143 DRM_ERROR("Cannot allocate memory for V3D job.\n"); 144 return -ENOMEM; 145 } 146 147 return 0; 148 } 149 150 static int 151 v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv, 152 struct v3d_job *job, void (*free)(struct kref *ref), 153 u32 in_sync, struct v3d_submit_ext *se, enum v3d_queue queue) 154 { 155 struct v3d_file_priv *v3d_priv = file_priv->driver_priv; 156 bool has_multisync = se && (se->flags & DRM_V3D_EXT_ID_MULTI_SYNC); 157 int ret, i; 158 159 job->v3d = v3d; 160 job->free = free; 161 job->file = file_priv; 162 163 ret = drm_sched_job_init(&job->base, &v3d_priv->sched_entity[queue], 164 1, v3d_priv); 165 if (ret) 166 return ret; 167 168 if (has_multisync) { 169 if (se->in_sync_count && se->wait_stage == queue) { 170 struct drm_v3d_sem __user *handle = u64_to_user_ptr(se->in_syncs); 171 172 for (i = 0; i < se->in_sync_count; i++) { 173 struct drm_v3d_sem in; 174 175 if (copy_from_user(&in, handle++, sizeof(in))) { 176 ret = -EFAULT; 177 DRM_DEBUG("Failed to copy wait dep handle.\n"); 178 goto fail_deps; 179 } 180 ret = drm_sched_job_add_syncobj_dependency(&job->base, file_priv, in.handle, 0); 181 182 // TODO: Investigate why this was filtered out for the IOCTL. 183 if (ret && ret != -ENOENT) 184 goto fail_deps; 185 } 186 } 187 } else { 188 ret = drm_sched_job_add_syncobj_dependency(&job->base, file_priv, in_sync, 0); 189 190 // TODO: Investigate why this was filtered out for the IOCTL. 191 if (ret && ret != -ENOENT) 192 goto fail_deps; 193 } 194 195 kref_init(&job->refcount); 196 197 return 0; 198 199 fail_deps: 200 drm_sched_job_cleanup(&job->base); 201 return ret; 202 } 203 204 static void 205 v3d_push_job(struct v3d_job *job) 206 { 207 drm_sched_job_arm(&job->base); 208 209 job->done_fence = dma_fence_get(&job->base.s_fence->finished); 210 211 /* put by scheduler job completion */ 212 kref_get(&job->refcount); 213 214 drm_sched_entity_push_job(&job->base); 215 } 216 217 static void 218 v3d_attach_fences_and_unlock_reservation(struct drm_file *file_priv, 219 struct v3d_job *job, 220 struct ww_acquire_ctx *acquire_ctx, 221 u32 out_sync, 222 struct v3d_submit_ext *se, 223 struct dma_fence *done_fence) 224 { 225 struct drm_syncobj *sync_out; 226 bool has_multisync = se && (se->flags & DRM_V3D_EXT_ID_MULTI_SYNC); 227 int i; 228 229 for (i = 0; i < job->bo_count; i++) { 230 /* XXX: Use shared fences for read-only objects. */ 231 dma_resv_add_fence(job->bo[i]->resv, job->done_fence, 232 DMA_RESV_USAGE_WRITE); 233 } 234 235 drm_gem_unlock_reservations(job->bo, job->bo_count, acquire_ctx); 236 237 /* Update the return sync object for the job */ 238 /* If it only supports a single signal semaphore*/ 239 if (!has_multisync) { 240 sync_out = drm_syncobj_find(file_priv, out_sync); 241 if (sync_out) { 242 drm_syncobj_replace_fence(sync_out, done_fence); 243 drm_syncobj_put(sync_out); 244 } 245 return; 246 } 247 248 /* If multiple semaphores extension is supported */ 249 if (se->out_sync_count) { 250 for (i = 0; i < se->out_sync_count; i++) { 251 drm_syncobj_replace_fence(se->out_syncs[i].syncobj, 252 done_fence); 253 drm_syncobj_put(se->out_syncs[i].syncobj); 254 } 255 kvfree(se->out_syncs); 256 } 257 } 258 259 static int 260 v3d_setup_csd_jobs_and_bos(struct drm_file *file_priv, 261 struct v3d_dev *v3d, 262 struct drm_v3d_submit_csd *args, 263 struct v3d_csd_job **job, 264 struct v3d_job **clean_job, 265 struct v3d_submit_ext *se, 266 struct ww_acquire_ctx *acquire_ctx) 267 { 268 int ret; 269 270 ret = v3d_job_allocate((void *)job, sizeof(**job)); 271 if (ret) 272 return ret; 273 274 ret = v3d_job_init(v3d, file_priv, &(*job)->base, 275 v3d_job_free, args->in_sync, se, V3D_CSD); 276 if (ret) 277 return ret; 278 279 ret = v3d_job_allocate((void *)clean_job, sizeof(**clean_job)); 280 if (ret) 281 return ret; 282 283 ret = v3d_job_init(v3d, file_priv, *clean_job, 284 v3d_job_free, 0, NULL, V3D_CACHE_CLEAN); 285 if (ret) 286 return ret; 287 288 (*job)->args = *args; 289 290 ret = v3d_lookup_bos(&v3d->drm, file_priv, *clean_job, 291 args->bo_handles, args->bo_handle_count); 292 if (ret) 293 return ret; 294 295 return v3d_lock_bo_reservations(*clean_job, acquire_ctx); 296 } 297 298 static void 299 v3d_put_multisync_post_deps(struct v3d_submit_ext *se) 300 { 301 unsigned int i; 302 303 if (!(se && se->out_sync_count)) 304 return; 305 306 for (i = 0; i < se->out_sync_count; i++) 307 drm_syncobj_put(se->out_syncs[i].syncobj); 308 kvfree(se->out_syncs); 309 } 310 311 static int 312 v3d_get_multisync_post_deps(struct drm_file *file_priv, 313 struct v3d_submit_ext *se, 314 u32 count, u64 handles) 315 { 316 struct drm_v3d_sem __user *post_deps; 317 int i, ret; 318 319 if (!count) 320 return 0; 321 322 se->out_syncs = (struct v3d_submit_outsync *) 323 kvmalloc_array(count, 324 sizeof(struct v3d_submit_outsync), 325 GFP_KERNEL); 326 if (!se->out_syncs) 327 return -ENOMEM; 328 329 post_deps = u64_to_user_ptr(handles); 330 331 for (i = 0; i < count; i++) { 332 struct drm_v3d_sem out; 333 334 if (copy_from_user(&out, post_deps++, sizeof(out))) { 335 ret = -EFAULT; 336 DRM_DEBUG("Failed to copy post dep handles\n"); 337 goto fail; 338 } 339 340 se->out_syncs[i].syncobj = drm_syncobj_find(file_priv, 341 out.handle); 342 if (!se->out_syncs[i].syncobj) { 343 ret = -EINVAL; 344 goto fail; 345 } 346 } 347 se->out_sync_count = count; 348 349 return 0; 350 351 fail: 352 for (i--; i >= 0; i--) 353 drm_syncobj_put(se->out_syncs[i].syncobj); 354 kvfree(se->out_syncs); 355 356 return ret; 357 } 358 359 /* Get data for multiple binary semaphores synchronization. Parse syncobj 360 * to be signaled when job completes (out_sync). 361 */ 362 static int 363 v3d_get_multisync_submit_deps(struct drm_file *file_priv, 364 struct drm_v3d_extension __user *ext, 365 struct v3d_submit_ext *se) 366 { 367 struct drm_v3d_multi_sync multisync; 368 int ret; 369 370 if (se->in_sync_count || se->out_sync_count) { 371 DRM_DEBUG("Two multisync extensions were added to the same job."); 372 return -EINVAL; 373 } 374 375 if (copy_from_user(&multisync, ext, sizeof(multisync))) 376 return -EFAULT; 377 378 if (multisync.pad) 379 return -EINVAL; 380 381 ret = v3d_get_multisync_post_deps(file_priv, se, multisync.out_sync_count, 382 multisync.out_syncs); 383 if (ret) 384 return ret; 385 386 se->in_sync_count = multisync.in_sync_count; 387 se->in_syncs = multisync.in_syncs; 388 se->flags |= DRM_V3D_EXT_ID_MULTI_SYNC; 389 se->wait_stage = multisync.wait_stage; 390 391 return 0; 392 } 393 394 /* Whenever userspace sets ioctl extensions, v3d_get_extensions parses data 395 * according to the extension id (name). 396 */ 397 static int 398 v3d_get_extensions(struct drm_file *file_priv, 399 u64 ext_handles, 400 struct v3d_submit_ext *se, 401 struct v3d_cpu_job *job) 402 { 403 struct drm_v3d_extension __user *user_ext; 404 int ret; 405 406 user_ext = u64_to_user_ptr(ext_handles); 407 while (user_ext) { 408 struct drm_v3d_extension ext; 409 410 if (copy_from_user(&ext, user_ext, sizeof(ext))) { 411 DRM_DEBUG("Failed to copy submit extension\n"); 412 return -EFAULT; 413 } 414 415 switch (ext.id) { 416 case DRM_V3D_EXT_ID_MULTI_SYNC: 417 ret = v3d_get_multisync_submit_deps(file_priv, user_ext, se); 418 break; 419 default: 420 DRM_DEBUG_DRIVER("Unknown extension id: %d\n", ext.id); 421 return -EINVAL; 422 } 423 424 if (ret) 425 return ret; 426 427 user_ext = u64_to_user_ptr(ext.next); 428 } 429 430 return 0; 431 } 432 433 /** 434 * v3d_submit_cl_ioctl() - Submits a job (frame) to the V3D. 435 * @dev: DRM device 436 * @data: ioctl argument 437 * @file_priv: DRM file for this fd 438 * 439 * This is the main entrypoint for userspace to submit a 3D frame to 440 * the GPU. Userspace provides the binner command list (if 441 * applicable), and the kernel sets up the render command list to draw 442 * to the framebuffer described in the ioctl, using the command lists 443 * that the 3D engine's binner will produce. 444 */ 445 int 446 v3d_submit_cl_ioctl(struct drm_device *dev, void *data, 447 struct drm_file *file_priv) 448 { 449 struct v3d_dev *v3d = to_v3d_dev(dev); 450 struct v3d_file_priv *v3d_priv = file_priv->driver_priv; 451 struct drm_v3d_submit_cl *args = data; 452 struct v3d_submit_ext se = {0}; 453 struct v3d_bin_job *bin = NULL; 454 struct v3d_render_job *render = NULL; 455 struct v3d_job *clean_job = NULL; 456 struct v3d_job *last_job; 457 struct ww_acquire_ctx acquire_ctx; 458 int ret = 0; 459 460 trace_v3d_submit_cl_ioctl(&v3d->drm, args->rcl_start, args->rcl_end); 461 462 if (args->pad) 463 return -EINVAL; 464 465 if (args->flags && 466 args->flags & ~(DRM_V3D_SUBMIT_CL_FLUSH_CACHE | 467 DRM_V3D_SUBMIT_EXTENSION)) { 468 DRM_INFO("invalid flags: %d\n", args->flags); 469 return -EINVAL; 470 } 471 472 if (args->flags & DRM_V3D_SUBMIT_EXTENSION) { 473 ret = v3d_get_extensions(file_priv, args->extensions, &se, NULL); 474 if (ret) { 475 DRM_DEBUG("Failed to get extensions.\n"); 476 return ret; 477 } 478 } 479 480 ret = v3d_job_allocate((void *)&render, sizeof(*render)); 481 if (ret) 482 return ret; 483 484 ret = v3d_job_init(v3d, file_priv, &render->base, 485 v3d_render_job_free, args->in_sync_rcl, &se, V3D_RENDER); 486 if (ret) 487 goto fail; 488 489 render->start = args->rcl_start; 490 render->end = args->rcl_end; 491 INIT_LIST_HEAD(&render->unref_list); 492 493 if (args->bcl_start != args->bcl_end) { 494 ret = v3d_job_allocate((void *)&bin, sizeof(*bin)); 495 if (ret) 496 goto fail; 497 498 ret = v3d_job_init(v3d, file_priv, &bin->base, 499 v3d_job_free, args->in_sync_bcl, &se, V3D_BIN); 500 if (ret) 501 goto fail; 502 503 bin->start = args->bcl_start; 504 bin->end = args->bcl_end; 505 bin->qma = args->qma; 506 bin->qms = args->qms; 507 bin->qts = args->qts; 508 bin->render = render; 509 } 510 511 if (args->flags & DRM_V3D_SUBMIT_CL_FLUSH_CACHE) { 512 ret = v3d_job_allocate((void *)&clean_job, sizeof(*clean_job)); 513 if (ret) 514 goto fail; 515 516 ret = v3d_job_init(v3d, file_priv, clean_job, 517 v3d_job_free, 0, NULL, V3D_CACHE_CLEAN); 518 if (ret) 519 goto fail; 520 521 last_job = clean_job; 522 } else { 523 last_job = &render->base; 524 } 525 526 ret = v3d_lookup_bos(dev, file_priv, last_job, 527 args->bo_handles, args->bo_handle_count); 528 if (ret) 529 goto fail; 530 531 ret = v3d_lock_bo_reservations(last_job, &acquire_ctx); 532 if (ret) 533 goto fail; 534 535 if (args->perfmon_id) { 536 render->base.perfmon = v3d_perfmon_find(v3d_priv, 537 args->perfmon_id); 538 539 if (!render->base.perfmon) { 540 ret = -ENOENT; 541 goto fail_perfmon; 542 } 543 } 544 545 mutex_lock(&v3d->sched_lock); 546 if (bin) { 547 bin->base.perfmon = render->base.perfmon; 548 v3d_perfmon_get(bin->base.perfmon); 549 v3d_push_job(&bin->base); 550 551 ret = drm_sched_job_add_dependency(&render->base.base, 552 dma_fence_get(bin->base.done_fence)); 553 if (ret) 554 goto fail_unreserve; 555 } 556 557 v3d_push_job(&render->base); 558 559 if (clean_job) { 560 struct dma_fence *render_fence = 561 dma_fence_get(render->base.done_fence); 562 ret = drm_sched_job_add_dependency(&clean_job->base, 563 render_fence); 564 if (ret) 565 goto fail_unreserve; 566 clean_job->perfmon = render->base.perfmon; 567 v3d_perfmon_get(clean_job->perfmon); 568 v3d_push_job(clean_job); 569 } 570 571 mutex_unlock(&v3d->sched_lock); 572 573 v3d_attach_fences_and_unlock_reservation(file_priv, 574 last_job, 575 &acquire_ctx, 576 args->out_sync, 577 &se, 578 last_job->done_fence); 579 580 v3d_job_put(&bin->base); 581 v3d_job_put(&render->base); 582 v3d_job_put(clean_job); 583 584 return 0; 585 586 fail_unreserve: 587 mutex_unlock(&v3d->sched_lock); 588 fail_perfmon: 589 drm_gem_unlock_reservations(last_job->bo, 590 last_job->bo_count, &acquire_ctx); 591 fail: 592 v3d_job_cleanup((void *)bin); 593 v3d_job_cleanup((void *)render); 594 v3d_job_cleanup(clean_job); 595 v3d_put_multisync_post_deps(&se); 596 597 return ret; 598 } 599 600 /** 601 * v3d_submit_tfu_ioctl() - Submits a TFU (texture formatting) job to the V3D. 602 * @dev: DRM device 603 * @data: ioctl argument 604 * @file_priv: DRM file for this fd 605 * 606 * Userspace provides the register setup for the TFU, which we don't 607 * need to validate since the TFU is behind the MMU. 608 */ 609 int 610 v3d_submit_tfu_ioctl(struct drm_device *dev, void *data, 611 struct drm_file *file_priv) 612 { 613 struct v3d_dev *v3d = to_v3d_dev(dev); 614 struct drm_v3d_submit_tfu *args = data; 615 struct v3d_submit_ext se = {0}; 616 struct v3d_tfu_job *job = NULL; 617 struct ww_acquire_ctx acquire_ctx; 618 int ret = 0; 619 620 trace_v3d_submit_tfu_ioctl(&v3d->drm, args->iia); 621 622 if (args->flags && !(args->flags & DRM_V3D_SUBMIT_EXTENSION)) { 623 DRM_DEBUG("invalid flags: %d\n", args->flags); 624 return -EINVAL; 625 } 626 627 if (args->flags & DRM_V3D_SUBMIT_EXTENSION) { 628 ret = v3d_get_extensions(file_priv, args->extensions, &se, NULL); 629 if (ret) { 630 DRM_DEBUG("Failed to get extensions.\n"); 631 return ret; 632 } 633 } 634 635 ret = v3d_job_allocate((void *)&job, sizeof(*job)); 636 if (ret) 637 return ret; 638 639 ret = v3d_job_init(v3d, file_priv, &job->base, 640 v3d_job_free, args->in_sync, &se, V3D_TFU); 641 if (ret) 642 goto fail; 643 644 job->base.bo = kcalloc(ARRAY_SIZE(args->bo_handles), 645 sizeof(*job->base.bo), GFP_KERNEL); 646 if (!job->base.bo) { 647 ret = -ENOMEM; 648 goto fail; 649 } 650 651 job->args = *args; 652 653 for (job->base.bo_count = 0; 654 job->base.bo_count < ARRAY_SIZE(args->bo_handles); 655 job->base.bo_count++) { 656 struct drm_gem_object *bo; 657 658 if (!args->bo_handles[job->base.bo_count]) 659 break; 660 661 bo = drm_gem_object_lookup(file_priv, args->bo_handles[job->base.bo_count]); 662 if (!bo) { 663 DRM_DEBUG("Failed to look up GEM BO %d: %d\n", 664 job->base.bo_count, 665 args->bo_handles[job->base.bo_count]); 666 ret = -ENOENT; 667 goto fail; 668 } 669 job->base.bo[job->base.bo_count] = bo; 670 } 671 672 ret = v3d_lock_bo_reservations(&job->base, &acquire_ctx); 673 if (ret) 674 goto fail; 675 676 mutex_lock(&v3d->sched_lock); 677 v3d_push_job(&job->base); 678 mutex_unlock(&v3d->sched_lock); 679 680 v3d_attach_fences_and_unlock_reservation(file_priv, 681 &job->base, &acquire_ctx, 682 args->out_sync, 683 &se, 684 job->base.done_fence); 685 686 v3d_job_put(&job->base); 687 688 return 0; 689 690 fail: 691 v3d_job_cleanup((void *)job); 692 v3d_put_multisync_post_deps(&se); 693 694 return ret; 695 } 696 697 /** 698 * v3d_submit_csd_ioctl() - Submits a CSD (compute shader) job to the V3D. 699 * @dev: DRM device 700 * @data: ioctl argument 701 * @file_priv: DRM file for this fd 702 * 703 * Userspace provides the register setup for the CSD, which we don't 704 * need to validate since the CSD is behind the MMU. 705 */ 706 int 707 v3d_submit_csd_ioctl(struct drm_device *dev, void *data, 708 struct drm_file *file_priv) 709 { 710 struct v3d_dev *v3d = to_v3d_dev(dev); 711 struct v3d_file_priv *v3d_priv = file_priv->driver_priv; 712 struct drm_v3d_submit_csd *args = data; 713 struct v3d_submit_ext se = {0}; 714 struct v3d_csd_job *job = NULL; 715 struct v3d_job *clean_job = NULL; 716 struct ww_acquire_ctx acquire_ctx; 717 int ret; 718 719 trace_v3d_submit_csd_ioctl(&v3d->drm, args->cfg[5], args->cfg[6]); 720 721 if (args->pad) 722 return -EINVAL; 723 724 if (!v3d_has_csd(v3d)) { 725 DRM_DEBUG("Attempting CSD submit on non-CSD hardware\n"); 726 return -EINVAL; 727 } 728 729 if (args->flags && !(args->flags & DRM_V3D_SUBMIT_EXTENSION)) { 730 DRM_INFO("invalid flags: %d\n", args->flags); 731 return -EINVAL; 732 } 733 734 if (args->flags & DRM_V3D_SUBMIT_EXTENSION) { 735 ret = v3d_get_extensions(file_priv, args->extensions, &se, NULL); 736 if (ret) { 737 DRM_DEBUG("Failed to get extensions.\n"); 738 return ret; 739 } 740 } 741 742 ret = v3d_setup_csd_jobs_and_bos(file_priv, v3d, args, 743 &job, &clean_job, &se, 744 &acquire_ctx); 745 if (ret) 746 goto fail; 747 748 if (args->perfmon_id) { 749 job->base.perfmon = v3d_perfmon_find(v3d_priv, 750 args->perfmon_id); 751 if (!job->base.perfmon) { 752 ret = -ENOENT; 753 goto fail_perfmon; 754 } 755 } 756 757 mutex_lock(&v3d->sched_lock); 758 v3d_push_job(&job->base); 759 760 ret = drm_sched_job_add_dependency(&clean_job->base, 761 dma_fence_get(job->base.done_fence)); 762 if (ret) 763 goto fail_unreserve; 764 765 v3d_push_job(clean_job); 766 mutex_unlock(&v3d->sched_lock); 767 768 v3d_attach_fences_and_unlock_reservation(file_priv, 769 clean_job, 770 &acquire_ctx, 771 args->out_sync, 772 &se, 773 clean_job->done_fence); 774 775 v3d_job_put(&job->base); 776 v3d_job_put(clean_job); 777 778 return 0; 779 780 fail_unreserve: 781 mutex_unlock(&v3d->sched_lock); 782 fail_perfmon: 783 drm_gem_unlock_reservations(clean_job->bo, clean_job->bo_count, 784 &acquire_ctx); 785 fail: 786 v3d_job_cleanup((void *)job); 787 v3d_job_cleanup(clean_job); 788 v3d_put_multisync_post_deps(&se); 789 790 return ret; 791 } 792 793 static const unsigned int cpu_job_bo_handle_count[] = { }; 794 795 /** 796 * v3d_submit_cpu_ioctl() - Submits a CPU job to the V3D. 797 * @dev: DRM device 798 * @data: ioctl argument 799 * @file_priv: DRM file for this fd 800 * 801 * Userspace specifies the CPU job type and data required to perform its 802 * operations through the drm_v3d_extension struct. 803 */ 804 int 805 v3d_submit_cpu_ioctl(struct drm_device *dev, void *data, 806 struct drm_file *file_priv) 807 { 808 struct v3d_dev *v3d = to_v3d_dev(dev); 809 struct drm_v3d_submit_cpu *args = data; 810 struct v3d_submit_ext se = {0}; 811 struct v3d_cpu_job *cpu_job = NULL; 812 struct ww_acquire_ctx acquire_ctx; 813 int ret; 814 815 if (args->flags && !(args->flags & DRM_V3D_SUBMIT_EXTENSION)) { 816 DRM_INFO("Invalid flags: %d\n", args->flags); 817 return -EINVAL; 818 } 819 820 ret = v3d_job_allocate((void *)&cpu_job, sizeof(*cpu_job)); 821 if (ret) 822 return ret; 823 824 if (args->flags & DRM_V3D_SUBMIT_EXTENSION) { 825 ret = v3d_get_extensions(file_priv, args->extensions, &se, cpu_job); 826 if (ret) { 827 DRM_DEBUG("Failed to get extensions.\n"); 828 goto fail; 829 } 830 } 831 832 /* Every CPU job must have a CPU job user extension */ 833 if (!cpu_job->job_type) { 834 DRM_DEBUG("CPU job must have a CPU job user extension.\n"); 835 goto fail; 836 } 837 838 if (args->bo_handle_count != cpu_job_bo_handle_count[cpu_job->job_type]) { 839 DRM_DEBUG("This CPU job was not submitted with the proper number of BOs.\n"); 840 goto fail; 841 } 842 843 trace_v3d_submit_cpu_ioctl(&v3d->drm, cpu_job->job_type); 844 845 ret = v3d_job_init(v3d, file_priv, &cpu_job->base, 846 v3d_job_free, 0, &se, V3D_CPU); 847 if (ret) 848 goto fail; 849 850 if (args->bo_handle_count) { 851 ret = v3d_lookup_bos(dev, file_priv, &cpu_job->base, 852 args->bo_handles, args->bo_handle_count); 853 if (ret) 854 goto fail; 855 856 ret = v3d_lock_bo_reservations(&cpu_job->base, &acquire_ctx); 857 if (ret) 858 goto fail; 859 } 860 861 mutex_lock(&v3d->sched_lock); 862 v3d_push_job(&cpu_job->base); 863 mutex_unlock(&v3d->sched_lock); 864 865 v3d_attach_fences_and_unlock_reservation(file_priv, 866 &cpu_job->base, 867 &acquire_ctx, 0, 868 NULL, cpu_job->base.done_fence); 869 870 v3d_job_put(&cpu_job->base); 871 872 return 0; 873 874 fail: 875 v3d_job_cleanup((void *)cpu_job); 876 v3d_put_multisync_post_deps(&se); 877 878 return ret; 879 } 880