1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2014-2018 Broadcom 4 * Copyright (C) 2023 Raspberry Pi 5 */ 6 7 #include <linux/dma-fence-unwrap.h> 8 #include <linux/overflow.h> 9 10 #include <drm/drm_print.h> 11 #include <drm/drm_syncobj.h> 12 13 #include "v3d_drv.h" 14 #include "v3d_regs.h" 15 #include "v3d_trace.h" 16 17 /* Takes the reservation lock on all the BOs being referenced, so that 18 * we can attach fences and update the reservations after pushing the job 19 * to the queue. 20 * 21 * We don't lock the RCL the tile alloc/state BOs, or overflow memory 22 * (all of which are on render->unref_list). They're entirely private 23 * to v3d, so we don't attach dma-buf fences to them. 24 */ 25 static int 26 v3d_submit_lock_reservations(struct v3d_submit *submit) 27 { 28 int i, j, ret; 29 30 drm_exec_init(&submit->exec, 31 DRM_EXEC_INTERRUPTIBLE_WAIT | DRM_EXEC_IGNORE_DUPLICATES, 0); 32 drm_exec_until_all_locked(&submit->exec) { 33 for (i = 0; i < submit->job_count; i++) { 34 struct v3d_job *job = submit->jobs[i]; 35 36 ret = drm_exec_prepare_array(&submit->exec, job->bo, 37 job->bo_count, 1); 38 if (ret) 39 break; 40 } 41 drm_exec_retry_on_contention(&submit->exec); 42 if (ret) 43 goto fail; 44 } 45 46 for (i = 0; i < submit->job_count; i++) { 47 struct v3d_job *job = submit->jobs[i]; 48 49 for (j = 0; j < job->bo_count; j++) { 50 ret = drm_sched_job_add_implicit_dependencies(&job->base, 51 job->bo[j], 52 true); 53 if (ret) 54 goto fail; 55 } 56 } 57 58 return 0; 59 60 fail: 61 drm_exec_fini(&submit->exec); 62 return ret; 63 } 64 65 static void 66 v3d_submit_unlock_reservations(struct v3d_submit *submit) 67 { 68 drm_exec_fini(&submit->exec); 69 } 70 71 /** 72 * v3d_lookup_bos() - Sets up job->bo[] with the GEM objects 73 * referenced by the job. 74 * @dev: DRM device 75 * @file_priv: DRM file for this fd 76 * @job: V3D job being set up 77 * @bo_handles: GEM handles 78 * @bo_count: Number of GEM handles passed in 79 * 80 * The command validator needs to reference BOs by their index within 81 * the submitted job's BO list. This does the validation of the job's 82 * BO list and reference counting for the lifetime of the job. 83 * 84 * Note that this function doesn't need to unreference the BOs on 85 * failure, because that will happen at `v3d_job_free()`. 86 */ 87 static int 88 v3d_lookup_bos(struct v3d_submit *submit, u64 bo_handles, u32 bo_count) 89 { 90 struct v3d_job *last_job = submit->jobs[submit->job_count - 1]; 91 92 last_job->bo_count = bo_count; 93 94 if (!last_job->bo_count) { 95 /* See comment on bo_index for why we have to check 96 * this. 97 */ 98 drm_warn(&submit->v3d->drm, "Rendering requires BOs\n"); 99 return -EINVAL; 100 } 101 102 return drm_gem_objects_lookup(submit->file_priv, 103 (void __user *)(uintptr_t)bo_handles, 104 last_job->bo_count, &last_job->bo); 105 } 106 107 static void 108 v3d_job_free(struct kref *ref) 109 { 110 struct v3d_job *job = container_of(ref, struct v3d_job, refcount); 111 int i; 112 113 if (job->bo) { 114 for (i = 0; i < job->bo_count; i++) 115 drm_gem_object_put(job->bo[i]); 116 kvfree(job->bo); 117 } 118 119 dma_fence_put(job->irq_fence); 120 dma_fence_put(job->done_fence); 121 122 if (job->perfmon) 123 v3d_perfmon_put(job->perfmon); 124 125 v3d_stats_put(job->client_stats); 126 v3d_stats_put(job->global_stats); 127 128 if (job->has_pm_ref) 129 v3d_pm_runtime_put(job->v3d); 130 131 kfree(job); 132 } 133 134 static void 135 v3d_render_job_free(struct kref *ref) 136 { 137 struct v3d_render_job *job = container_of(ref, struct v3d_render_job, 138 base.refcount); 139 struct v3d_bo *bo, *save; 140 141 list_for_each_entry_safe(bo, save, &job->unref_list, unref_head) { 142 drm_gem_object_put(&bo->base.base); 143 } 144 145 v3d_job_free(ref); 146 } 147 148 static void 149 v3d_cpu_job_free(struct kref *ref) 150 { 151 struct v3d_cpu_job *job = container_of(ref, struct v3d_cpu_job, 152 base.refcount); 153 154 v3d_timestamp_query_info_free(&job->timestamp_query, 155 job->timestamp_query.count); 156 157 v3d_performance_query_info_free(&job->performance_query, 158 job->performance_query.count); 159 160 if (job->indirect_csd.indirect) 161 drm_gem_object_put(job->indirect_csd.indirect); 162 163 v3d_job_free(ref); 164 } 165 166 void v3d_job_cleanup(struct v3d_job *job) 167 { 168 if (!job) 169 return; 170 171 drm_sched_job_cleanup(&job->base); 172 v3d_job_put(job); 173 } 174 175 void v3d_job_put(struct v3d_job *job) 176 { 177 if (!job) 178 return; 179 180 kref_put(&job->refcount, job->free); 181 } 182 183 static int 184 v3d_job_add_syncobjs(struct v3d_job *job, struct drm_file *file_priv, 185 u32 in_sync, struct v3d_submit_ext *se) 186 { 187 bool has_multisync = se && (se->flags & DRM_V3D_EXT_ID_MULTI_SYNC); 188 struct v3d_dev *v3d = job->v3d; 189 int ret = 0; 190 191 if (!has_multisync) { 192 /* Ignore syncobj if its handle is zero */ 193 if (in_sync) 194 ret = drm_sched_job_add_syncobj_dependency(&job->base, file_priv, 195 in_sync, 0); 196 return ret; 197 } 198 199 if (se->in_sync_count && se->wait_stage == job->queue) { 200 struct drm_v3d_sem __user *handle = u64_to_user_ptr(se->in_syncs); 201 202 for (int i = 0; i < se->in_sync_count; i++) { 203 struct drm_v3d_sem in; 204 205 if (copy_from_user(&in, handle++, sizeof(in))) { 206 drm_dbg(&v3d->drm, "Failed to copy wait dep handle.\n"); 207 return -EFAULT; 208 } 209 210 /* Ignore syncobj if its handle is zero */ 211 if (in.handle) { 212 ret = drm_sched_job_add_syncobj_dependency(&job->base, 213 file_priv, in.handle, 0); 214 if (ret) 215 return ret; 216 } 217 } 218 } 219 220 return 0; 221 } 222 223 static const struct { 224 size_t size; 225 void (*free)(struct kref *ref); 226 } v3d_job_types[] = { 227 [V3D_BIN] = { sizeof(struct v3d_bin_job), v3d_job_free }, 228 [V3D_RENDER] = { sizeof(struct v3d_render_job), v3d_render_job_free }, 229 [V3D_TFU] = { sizeof(struct v3d_tfu_job), v3d_job_free }, 230 [V3D_CSD] = { sizeof(struct v3d_csd_job), v3d_job_free }, 231 [V3D_CACHE_CLEAN] = { sizeof(struct v3d_job), v3d_job_free }, 232 [V3D_CPU] = { sizeof(struct v3d_cpu_job), v3d_cpu_job_free }, 233 }; 234 235 static struct v3d_job * 236 v3d_submit_add_job(struct v3d_submit *submit, enum v3d_queue queue) 237 { 238 struct v3d_file_priv *v3d_priv = submit->file_priv->driver_priv; 239 struct v3d_dev *v3d = submit->v3d; 240 struct v3d_job *job; 241 int ret; 242 243 if (queue >= V3D_MAX_QUEUES) 244 return ERR_PTR(-EINVAL); 245 246 job = kzalloc(v3d_job_types[queue].size, GFP_KERNEL); 247 if (!job) 248 return ERR_PTR(-ENOMEM); 249 250 job->v3d = v3d; 251 job->queue = queue; 252 job->file_priv = v3d_priv; 253 job->free = v3d_job_types[queue].free; 254 255 ret = drm_sched_job_init(&job->base, &v3d_priv->sched_entity[queue], 256 1, v3d_priv, submit->file_priv->client_id); 257 if (ret) 258 goto fail_free; 259 260 /* CPU jobs don't require hardware resources */ 261 if (queue != V3D_CPU) { 262 ret = v3d_pm_runtime_get(v3d); 263 if (ret) 264 goto fail_sched_job; 265 job->has_pm_ref = true; 266 } 267 268 kref_init(&job->refcount); 269 270 job->client_stats = v3d_stats_get(v3d_priv->stats[queue]); 271 job->global_stats = v3d_stats_get(v3d->queue[queue].stats); 272 273 submit->jobs[submit->job_count++] = job; 274 275 return job; 276 277 fail_sched_job: 278 drm_sched_job_cleanup(&job->base); 279 fail_free: 280 kfree(job); 281 return ERR_PTR(ret); 282 } 283 284 static void 285 v3d_submit_put_jobs(struct v3d_submit *submit) 286 { 287 for (int i = 0; i < submit->job_count; i++) 288 v3d_job_put(submit->jobs[i]); 289 } 290 291 static void 292 v3d_submit_cleanup_jobs(struct v3d_submit *submit) 293 { 294 for (int i = 0; i < submit->job_count; i++) 295 v3d_job_cleanup(submit->jobs[i]); 296 } 297 298 static int 299 v3d_attach_perfmon_to_jobs(struct v3d_submit *submit, u32 perfmon_id) 300 { 301 struct v3d_file_priv *v3d_priv = submit->file_priv->driver_priv; 302 struct v3d_dev *v3d = submit->v3d; 303 struct v3d_perfmon *perfmon; 304 305 if (!perfmon_id) 306 return 0; 307 308 scoped_guard(spinlock_irqsave, &v3d->perfmon_state.lock) { 309 if (v3d->global_perfmon) 310 return -EAGAIN; 311 } 312 313 perfmon = v3d_perfmon_find(v3d_priv, perfmon_id); 314 if (!perfmon) 315 return -ENOENT; 316 317 for (int i = 0; i < submit->job_count; i++) { 318 submit->jobs[i]->perfmon = perfmon; 319 if (i != 0) 320 v3d_perfmon_get(perfmon); 321 } 322 323 return 0; 324 } 325 326 /* 327 * Prepare fences to enforce job serialization when a perfmon is active. A job 328 * that carries a non-global perfmon must wait for every job currently in-flight 329 * across all HW queues to finish, otherwise concurrent unrelated work on the 330 * same core would pollute the performance counters. Symmetrically, while such a 331 * job is still in-flight, all subsequently submitted jobs must wait for it. 332 * 333 * We don't serialize the jobs when using a global perfmon as it's expected to 334 * track concurrent activity from all jobs. 335 */ 336 static int 337 v3d_serialize_for_perfmon(struct v3d_job *job) 338 { 339 struct v3d_dev *v3d = job->v3d; 340 struct dma_fence *merged; 341 bool is_global_perfmon; 342 int ret; 343 344 lockdep_assert_held(&v3d->sched_lock); 345 346 scoped_guard(spinlock_irqsave, &v3d->perfmon_state.lock) 347 is_global_perfmon = !!v3d->global_perfmon; 348 349 if (is_global_perfmon) 350 goto publish; 351 352 if (job->perfmon) { 353 for (enum v3d_queue q = 0; q < V3D_MAX_QUEUES; q++) { 354 struct dma_fence *f = v3d->perfmon_state.last_hw_fence[q]; 355 356 if (!f || dma_fence_is_signaled(f)) 357 continue; 358 359 ret = drm_sched_job_add_dependency(&job->base, dma_fence_get(f)); 360 if (ret) 361 return ret; 362 } 363 } else if (v3d->perfmon_state.fence && 364 !dma_fence_is_signaled(v3d->perfmon_state.fence)) { 365 ret = drm_sched_job_add_dependency(&job->base, 366 dma_fence_get(v3d->perfmon_state.fence)); 367 if (ret) 368 return ret; 369 } 370 371 publish: 372 /* 373 * Accumulate every in-flight job on this queue into one merged fence. 374 * A HW queue is fed by several scheduler entities (one per-fd), so jobs 375 * on it can complete out of order. 376 */ 377 merged = dma_fence_unwrap_merge(v3d->perfmon_state.last_hw_fence[job->queue], 378 job->done_fence); 379 if (!merged) 380 return -ENOMEM; 381 382 dma_fence_put(v3d->perfmon_state.last_hw_fence[job->queue]); 383 v3d->perfmon_state.last_hw_fence[job->queue] = merged; 384 385 if (job->perfmon && !is_global_perfmon) { 386 dma_fence_put(v3d->perfmon_state.fence); 387 v3d->perfmon_state.fence = dma_fence_get(job->done_fence); 388 } 389 390 return 0; 391 } 392 393 static void 394 v3d_submit_attach_object_fences(struct v3d_submit *submit) 395 { 396 struct v3d_job *last_job = submit->jobs[submit->job_count - 1]; 397 398 /* The submission's last fence covers the entire submission. Attach it 399 * to every BO touched by any job in the submission. 400 */ 401 for (int i = 0; i < submit->job_count; i++) { 402 struct v3d_job *job = submit->jobs[i]; 403 404 for (int j = 0; j < job->bo_count; j++) { 405 /* XXX: Use shared fences for read-only objects. */ 406 dma_resv_add_fence(job->bo[j]->resv, last_job->done_fence, 407 DMA_RESV_USAGE_WRITE); 408 } 409 } 410 } 411 412 static void 413 v3d_submit_process_post_deps(struct v3d_submit *submit, struct drm_syncobj *sync_out, 414 struct v3d_submit_ext *se) 415 { 416 bool has_multisync = se && (se->flags & DRM_V3D_EXT_ID_MULTI_SYNC); 417 struct v3d_job *last_job = submit->jobs[submit->job_count - 1]; 418 419 /* Make sure single syncobj and multisync are mutually exclusive */ 420 WARN_ON_ONCE(sync_out && has_multisync); 421 422 /* Update the return sync object for the job */ 423 /* If it only supports a single signal semaphore*/ 424 if (!has_multisync) { 425 if (sync_out) { 426 drm_syncobj_replace_fence(sync_out, last_job->done_fence); 427 drm_syncobj_put(sync_out); 428 } 429 return; 430 } 431 432 /* If multiple semaphores extension is supported */ 433 if (se->out_sync_count) { 434 for (int i = 0; i < se->out_sync_count; i++) { 435 drm_syncobj_replace_fence(se->out_syncs[i].syncobj, 436 last_job->done_fence); 437 drm_syncobj_put(se->out_syncs[i].syncobj); 438 } 439 kvfree(se->out_syncs); 440 } 441 } 442 443 static int 444 v3d_submit_jobs(struct v3d_submit *submit, struct drm_syncobj *sync_out, 445 struct v3d_submit_ext *se) 446 { 447 struct v3d_dev *v3d = submit->v3d; 448 int ret = 0; 449 450 mutex_lock(&v3d->sched_lock); 451 452 for (int i = 0; i < submit->job_count; i++) { 453 struct v3d_job *job = submit->jobs[i]; 454 455 drm_sched_job_arm(&job->base); 456 job->done_fence = dma_fence_get(&job->base.s_fence->finished); 457 458 /* put by scheduler job completion */ 459 kref_get(&job->refcount); 460 } 461 462 for (int i = 1; i < submit->job_count; i++) { 463 ret = drm_sched_job_add_dependency(&submit->jobs[i]->base, 464 dma_fence_get(submit->jobs[i - 1]->done_fence)); 465 if (ret) 466 goto err; 467 } 468 469 for (int i = 0; i < submit->job_count; i++) { 470 ret = v3d_serialize_for_perfmon(submit->jobs[i]); 471 if (ret) 472 goto err; 473 } 474 475 for (int i = 0; i < submit->job_count; i++) 476 drm_sched_entity_push_job(&submit->jobs[i]->base); 477 478 mutex_unlock(&v3d->sched_lock); 479 480 v3d_submit_attach_object_fences(submit); 481 v3d_submit_unlock_reservations(submit); 482 v3d_submit_process_post_deps(submit, sync_out, se); 483 484 v3d_submit_put_jobs(submit); 485 486 return 0; 487 488 err: 489 /* Mark every armed job as failed so run_job() skips execution */ 490 for (int i = 0; i < submit->job_count; i++) 491 dma_fence_set_error(&submit->jobs[i]->base.s_fence->finished, ret); 492 493 for (int i = 0; i < submit->job_count; i++) 494 drm_sched_entity_push_job(&submit->jobs[i]->base); 495 496 mutex_unlock(&v3d->sched_lock); 497 498 v3d_submit_unlock_reservations(submit); 499 v3d_submit_put_jobs(submit); 500 501 return ret; 502 } 503 504 static int 505 v3d_setup_csd_jobs_and_bos(struct v3d_submit *submit, 506 struct drm_v3d_submit_csd *args, 507 struct v3d_submit_ext *se) 508 { 509 struct v3d_csd_job *job; 510 struct v3d_job *clean_job; 511 int ret; 512 513 job = (struct v3d_csd_job *)v3d_submit_add_job(submit, V3D_CSD); 514 if (IS_ERR(job)) 515 return PTR_ERR(job); 516 517 ret = v3d_job_add_syncobjs(&job->base, submit->file_priv, args->in_sync, se); 518 if (ret) 519 return ret; 520 521 job->args = *args; 522 523 clean_job = v3d_submit_add_job(submit, V3D_CACHE_CLEAN); 524 if (IS_ERR(clean_job)) 525 return PTR_ERR(clean_job); 526 527 return v3d_lookup_bos(submit, args->bo_handles, args->bo_handle_count); 528 } 529 530 static void 531 v3d_submit_put_post_deps(struct drm_syncobj *sync_out, struct v3d_submit_ext *se) 532 { 533 unsigned int i; 534 535 if (sync_out) 536 drm_syncobj_put(sync_out); 537 538 if (!(se && se->out_sync_count)) 539 return; 540 541 for (i = 0; i < se->out_sync_count; i++) 542 drm_syncobj_put(se->out_syncs[i].syncobj); 543 kvfree(se->out_syncs); 544 } 545 546 static int 547 v3d_get_multisync_post_deps(struct drm_file *file_priv, 548 struct v3d_submit_ext *se, 549 u32 count, u64 handles) 550 { 551 struct v3d_file_priv *v3d_priv = file_priv->driver_priv; 552 struct v3d_dev *v3d = v3d_priv->v3d; 553 struct drm_v3d_sem __user *post_deps; 554 int i, ret; 555 556 if (!count) 557 return 0; 558 559 se->out_syncs = (struct v3d_submit_outsync *) 560 kvmalloc_objs(struct v3d_submit_outsync, count); 561 if (!se->out_syncs) 562 return -ENOMEM; 563 564 post_deps = u64_to_user_ptr(handles); 565 566 for (i = 0; i < count; i++) { 567 struct drm_v3d_sem out; 568 569 if (copy_from_user(&out, post_deps++, sizeof(out))) { 570 ret = -EFAULT; 571 drm_dbg(&v3d->drm, "Failed to copy post dep handles\n"); 572 goto fail; 573 } 574 575 se->out_syncs[i].syncobj = drm_syncobj_find(file_priv, 576 out.handle); 577 if (!se->out_syncs[i].syncobj) { 578 ret = -EINVAL; 579 goto fail; 580 } 581 } 582 se->out_sync_count = count; 583 584 return 0; 585 586 fail: 587 for (i--; i >= 0; i--) 588 drm_syncobj_put(se->out_syncs[i].syncobj); 589 kvfree(se->out_syncs); 590 591 return ret; 592 } 593 594 /* Get data for multiple binary semaphores synchronization. Parse syncobj 595 * to be signaled when job completes (out_sync). 596 */ 597 static int 598 v3d_get_multisync_submit_deps(struct drm_file *file_priv, 599 struct drm_v3d_extension __user *ext, 600 struct v3d_submit_ext *se) 601 { 602 struct v3d_file_priv *v3d_priv = file_priv->driver_priv; 603 struct v3d_dev *v3d = v3d_priv->v3d; 604 struct drm_v3d_multi_sync multisync; 605 int ret; 606 607 if (se->in_sync_count || se->out_sync_count) { 608 drm_dbg(&v3d->drm, "Two multisync extensions were added to the same job."); 609 return -EINVAL; 610 } 611 612 if (copy_from_user(&multisync, ext, sizeof(multisync))) 613 return -EFAULT; 614 615 if (multisync.pad) 616 return -EINVAL; 617 618 if (!multisync.in_sync_count && !multisync.out_sync_count) { 619 drm_dbg(&v3d->drm, "Empty multisync extension\n"); 620 return -EINVAL; 621 } 622 623 ret = v3d_get_multisync_post_deps(file_priv, se, multisync.out_sync_count, 624 multisync.out_syncs); 625 if (ret) 626 return ret; 627 628 se->in_sync_count = multisync.in_sync_count; 629 se->in_syncs = multisync.in_syncs; 630 se->flags |= DRM_V3D_EXT_ID_MULTI_SYNC; 631 se->wait_stage = multisync.wait_stage; 632 633 return 0; 634 } 635 636 /* Returns false if the CPU job has an invalid configuration. */ 637 static bool 638 v3d_validate_cpu_job(struct drm_file *file_priv, struct v3d_cpu_job *job) 639 { 640 struct v3d_file_priv *v3d_priv = file_priv->driver_priv; 641 struct v3d_dev *v3d = v3d_priv->v3d; 642 643 if (!job) { 644 drm_dbg(&v3d->drm, "CPU job extension was attached to a GPU job.\n"); 645 return false; 646 } 647 648 if (job->job_type) { 649 drm_dbg(&v3d->drm, "Two CPU job extensions were added to the same CPU job.\n"); 650 return false; 651 } 652 653 return true; 654 } 655 656 /* Get data for the indirect CSD job submission. */ 657 static int 658 v3d_get_cpu_indirect_csd_params(struct drm_file *file_priv, 659 struct drm_v3d_extension __user *ext, 660 struct v3d_cpu_job *job) 661 { 662 struct v3d_file_priv *v3d_priv = file_priv->driver_priv; 663 struct v3d_dev *v3d = v3d_priv->v3d; 664 struct drm_v3d_indirect_csd indirect_csd; 665 struct v3d_indirect_csd_info *info = &job->indirect_csd; 666 667 if (!v3d_validate_cpu_job(file_priv, job)) 668 return -EINVAL; 669 670 if (copy_from_user(&indirect_csd, ext, sizeof(indirect_csd))) 671 return -EFAULT; 672 673 if (!v3d_has_csd(v3d)) { 674 drm_warn(&v3d->drm, "Attempting CSD submit on non-CSD hardware.\n"); 675 return -EINVAL; 676 } 677 678 job->job_type = V3D_CPU_JOB_TYPE_INDIRECT_CSD; 679 info->args = indirect_csd.submit; 680 info->offset = indirect_csd.offset; 681 info->wg_size = indirect_csd.wg_size; 682 memcpy(&info->wg_uniform_offsets, &indirect_csd.wg_uniform_offsets, 683 sizeof(indirect_csd.wg_uniform_offsets)); 684 685 info->indirect = drm_gem_object_lookup(file_priv, indirect_csd.indirect); 686 687 return 0; 688 } 689 690 /* Get data for the query timestamp job submission. */ 691 static int 692 v3d_get_cpu_timestamp_query_params(struct drm_file *file_priv, 693 struct drm_v3d_extension __user *ext, 694 struct v3d_cpu_job *job) 695 { 696 u32 __user *offsets, *syncs; 697 struct drm_v3d_timestamp_query timestamp; 698 struct v3d_timestamp_query_info *query_info = &job->timestamp_query; 699 unsigned int i; 700 int err; 701 702 if (!v3d_validate_cpu_job(file_priv, job)) 703 return -EINVAL; 704 705 if (copy_from_user(×tamp, ext, sizeof(timestamp))) 706 return -EFAULT; 707 708 if (timestamp.pad) 709 return -EINVAL; 710 711 job->job_type = V3D_CPU_JOB_TYPE_TIMESTAMP_QUERY; 712 713 query_info->queries = kvmalloc_objs(struct v3d_timestamp_query, 714 timestamp.count); 715 if (!query_info->queries) 716 return -ENOMEM; 717 718 offsets = u64_to_user_ptr(timestamp.offsets); 719 syncs = u64_to_user_ptr(timestamp.syncs); 720 721 for (i = 0; i < timestamp.count; i++) { 722 u32 offset, sync; 723 724 if (get_user(offset, offsets++)) { 725 err = -EFAULT; 726 goto error; 727 } 728 729 query_info->queries[i].offset = offset; 730 731 if (get_user(sync, syncs++)) { 732 err = -EFAULT; 733 goto error; 734 } 735 736 query_info->queries[i].syncobj = drm_syncobj_find(file_priv, 737 sync); 738 if (!query_info->queries[i].syncobj) { 739 err = -ENOENT; 740 goto error; 741 } 742 } 743 query_info->count = timestamp.count; 744 745 return 0; 746 747 error: 748 v3d_timestamp_query_info_free(&job->timestamp_query, i); 749 return err; 750 } 751 752 static int 753 v3d_get_cpu_reset_timestamp_params(struct drm_file *file_priv, 754 struct drm_v3d_extension __user *ext, 755 struct v3d_cpu_job *job) 756 { 757 u32 __user *syncs; 758 struct drm_v3d_reset_timestamp_query reset; 759 struct v3d_timestamp_query_info *query_info = &job->timestamp_query; 760 unsigned int i; 761 int err; 762 763 if (!v3d_validate_cpu_job(file_priv, job)) 764 return -EINVAL; 765 766 if (copy_from_user(&reset, ext, sizeof(reset))) 767 return -EFAULT; 768 769 job->job_type = V3D_CPU_JOB_TYPE_RESET_TIMESTAMP_QUERY; 770 771 query_info->queries = kvmalloc_objs(struct v3d_timestamp_query, 772 reset.count); 773 if (!query_info->queries) 774 return -ENOMEM; 775 776 syncs = u64_to_user_ptr(reset.syncs); 777 778 for (i = 0; i < reset.count; i++) { 779 u32 sync; 780 781 query_info->queries[i].offset = reset.offset + 8 * i; 782 783 if (get_user(sync, syncs++)) { 784 err = -EFAULT; 785 goto error; 786 } 787 788 query_info->queries[i].syncobj = drm_syncobj_find(file_priv, 789 sync); 790 if (!query_info->queries[i].syncobj) { 791 err = -ENOENT; 792 goto error; 793 } 794 } 795 query_info->count = reset.count; 796 797 return 0; 798 799 error: 800 v3d_timestamp_query_info_free(&job->timestamp_query, i); 801 return err; 802 } 803 804 /* Get data for the copy timestamp query results job submission. */ 805 static int 806 v3d_get_cpu_copy_query_results_params(struct drm_file *file_priv, 807 struct drm_v3d_extension __user *ext, 808 struct v3d_cpu_job *job) 809 { 810 u32 __user *offsets, *syncs; 811 struct drm_v3d_copy_timestamp_query copy; 812 struct v3d_timestamp_query_info *query_info = &job->timestamp_query; 813 unsigned int i; 814 int err; 815 816 if (!v3d_validate_cpu_job(file_priv, job)) 817 return -EINVAL; 818 819 if (copy_from_user(©, ext, sizeof(copy))) 820 return -EFAULT; 821 822 if (copy.pad) 823 return -EINVAL; 824 825 job->job_type = V3D_CPU_JOB_TYPE_COPY_TIMESTAMP_QUERY; 826 827 query_info->queries = kvmalloc_objs(struct v3d_timestamp_query, 828 copy.count); 829 if (!query_info->queries) 830 return -ENOMEM; 831 832 offsets = u64_to_user_ptr(copy.offsets); 833 syncs = u64_to_user_ptr(copy.syncs); 834 835 for (i = 0; i < copy.count; i++) { 836 u32 offset, sync; 837 838 if (get_user(offset, offsets++)) { 839 err = -EFAULT; 840 goto error; 841 } 842 843 query_info->queries[i].offset = offset; 844 845 if (get_user(sync, syncs++)) { 846 err = -EFAULT; 847 goto error; 848 } 849 850 query_info->queries[i].syncobj = drm_syncobj_find(file_priv, 851 sync); 852 if (!query_info->queries[i].syncobj) { 853 err = -ENOENT; 854 goto error; 855 } 856 } 857 query_info->count = copy.count; 858 859 job->copy.do_64bit = copy.do_64bit; 860 job->copy.do_partial = copy.do_partial; 861 job->copy.availability_bit = copy.availability_bit; 862 job->copy.offset = copy.offset; 863 job->copy.stride = copy.stride; 864 865 return 0; 866 867 error: 868 v3d_timestamp_query_info_free(&job->timestamp_query, i); 869 return err; 870 } 871 872 static int 873 v3d_copy_query_info(struct v3d_performance_query_info *query_info, 874 unsigned int count, 875 unsigned int nperfmons, 876 u32 __user *syncs, 877 u64 __user *kperfmon_ids, 878 struct drm_file *file_priv) 879 { 880 unsigned int i, j; 881 int err; 882 883 for (i = 0; i < count; i++) { 884 struct v3d_performance_query *query = &query_info->queries[i]; 885 u32 __user *ids_pointer; 886 u32 sync, id; 887 u64 ids; 888 889 if (get_user(sync, syncs++)) { 890 err = -EFAULT; 891 goto error; 892 } 893 894 if (get_user(ids, kperfmon_ids++)) { 895 err = -EFAULT; 896 goto error; 897 } 898 899 query->kperfmon_ids = 900 kvmalloc_array(nperfmons, 901 sizeof(struct v3d_performance_query *), 902 GFP_KERNEL); 903 if (!query->kperfmon_ids) { 904 err = -ENOMEM; 905 goto error; 906 } 907 908 ids_pointer = u64_to_user_ptr(ids); 909 910 for (j = 0; j < nperfmons; j++) { 911 if (get_user(id, ids_pointer++)) { 912 kvfree(query->kperfmon_ids); 913 err = -EFAULT; 914 goto error; 915 } 916 917 query->kperfmon_ids[j] = id; 918 } 919 920 query->syncobj = drm_syncobj_find(file_priv, sync); 921 if (!query->syncobj) { 922 kvfree(query->kperfmon_ids); 923 err = -ENOENT; 924 goto error; 925 } 926 } 927 928 return 0; 929 930 error: 931 v3d_performance_query_info_free(query_info, i); 932 return err; 933 } 934 935 static int 936 v3d_get_cpu_reset_performance_params(struct drm_file *file_priv, 937 struct drm_v3d_extension __user *ext, 938 struct v3d_cpu_job *job) 939 { 940 struct v3d_performance_query_info *query_info = &job->performance_query; 941 struct drm_v3d_reset_performance_query reset; 942 int err; 943 944 if (!v3d_validate_cpu_job(file_priv, job)) 945 return -EINVAL; 946 947 if (copy_from_user(&reset, ext, sizeof(reset))) 948 return -EFAULT; 949 950 job->job_type = V3D_CPU_JOB_TYPE_RESET_PERFORMANCE_QUERY; 951 952 query_info->queries = 953 kvmalloc_objs(struct v3d_performance_query, reset.count); 954 if (!query_info->queries) 955 return -ENOMEM; 956 957 err = v3d_copy_query_info(query_info, 958 reset.count, 959 reset.nperfmons, 960 u64_to_user_ptr(reset.syncs), 961 u64_to_user_ptr(reset.kperfmon_ids), 962 file_priv); 963 if (err) 964 return err; 965 966 query_info->count = reset.count; 967 query_info->nperfmons = reset.nperfmons; 968 969 return 0; 970 } 971 972 static int 973 v3d_get_cpu_copy_performance_query_params(struct drm_file *file_priv, 974 struct drm_v3d_extension __user *ext, 975 struct v3d_cpu_job *job) 976 { 977 struct v3d_performance_query_info *query_info = &job->performance_query; 978 struct drm_v3d_copy_performance_query copy; 979 int err; 980 981 if (!v3d_validate_cpu_job(file_priv, job)) 982 return -EINVAL; 983 984 if (copy_from_user(©, ext, sizeof(copy))) 985 return -EFAULT; 986 987 if (copy.pad) 988 return -EINVAL; 989 990 job->job_type = V3D_CPU_JOB_TYPE_COPY_PERFORMANCE_QUERY; 991 992 query_info->queries = 993 kvmalloc_objs(struct v3d_performance_query, copy.count); 994 if (!query_info->queries) 995 return -ENOMEM; 996 997 err = v3d_copy_query_info(query_info, 998 copy.count, 999 copy.nperfmons, 1000 u64_to_user_ptr(copy.syncs), 1001 u64_to_user_ptr(copy.kperfmon_ids), 1002 file_priv); 1003 if (err) 1004 return err; 1005 1006 query_info->count = copy.count; 1007 query_info->nperfmons = copy.nperfmons; 1008 query_info->ncounters = copy.ncounters; 1009 1010 job->copy.do_64bit = copy.do_64bit; 1011 job->copy.do_partial = copy.do_partial; 1012 job->copy.availability_bit = copy.availability_bit; 1013 job->copy.offset = copy.offset; 1014 job->copy.stride = copy.stride; 1015 1016 return 0; 1017 } 1018 1019 /* Whenever userspace sets ioctl extensions, v3d_get_extensions parses data 1020 * according to the extension id (name). 1021 */ 1022 static int 1023 v3d_get_extensions(struct drm_file *file_priv, 1024 u64 ext_handles, 1025 struct v3d_submit_ext *se, 1026 struct v3d_cpu_job *job) 1027 { 1028 struct v3d_file_priv *v3d_priv = file_priv->driver_priv; 1029 struct v3d_dev *v3d = v3d_priv->v3d; 1030 struct drm_v3d_extension __user *user_ext; 1031 int ret; 1032 1033 user_ext = u64_to_user_ptr(ext_handles); 1034 while (user_ext) { 1035 struct drm_v3d_extension ext; 1036 1037 if (copy_from_user(&ext, user_ext, sizeof(ext))) { 1038 drm_dbg(&v3d->drm, "Failed to copy submit extension\n"); 1039 return -EFAULT; 1040 } 1041 1042 switch (ext.id) { 1043 case DRM_V3D_EXT_ID_MULTI_SYNC: 1044 ret = v3d_get_multisync_submit_deps(file_priv, user_ext, se); 1045 break; 1046 case DRM_V3D_EXT_ID_CPU_INDIRECT_CSD: 1047 ret = v3d_get_cpu_indirect_csd_params(file_priv, user_ext, job); 1048 break; 1049 case DRM_V3D_EXT_ID_CPU_TIMESTAMP_QUERY: 1050 ret = v3d_get_cpu_timestamp_query_params(file_priv, user_ext, job); 1051 break; 1052 case DRM_V3D_EXT_ID_CPU_RESET_TIMESTAMP_QUERY: 1053 ret = v3d_get_cpu_reset_timestamp_params(file_priv, user_ext, job); 1054 break; 1055 case DRM_V3D_EXT_ID_CPU_COPY_TIMESTAMP_QUERY: 1056 ret = v3d_get_cpu_copy_query_results_params(file_priv, user_ext, job); 1057 break; 1058 case DRM_V3D_EXT_ID_CPU_RESET_PERFORMANCE_QUERY: 1059 ret = v3d_get_cpu_reset_performance_params(file_priv, user_ext, job); 1060 break; 1061 case DRM_V3D_EXT_ID_CPU_COPY_PERFORMANCE_QUERY: 1062 ret = v3d_get_cpu_copy_performance_query_params(file_priv, user_ext, job); 1063 break; 1064 default: 1065 drm_dbg(&v3d->drm, "Unknown V3D extension ID: %d\n", ext.id); 1066 return -EINVAL; 1067 } 1068 1069 if (ret) 1070 return ret; 1071 1072 user_ext = u64_to_user_ptr(ext.next); 1073 } 1074 1075 return 0; 1076 } 1077 1078 /** 1079 * v3d_submit_cl_ioctl() - Submits a job (frame) to the V3D. 1080 * @dev: DRM device 1081 * @data: ioctl argument 1082 * @file_priv: DRM file for this fd 1083 * 1084 * This is the main entrypoint for userspace to submit a 3D frame to 1085 * the GPU. Userspace provides the binner command list (if 1086 * applicable), and the kernel sets up the render command list to draw 1087 * to the framebuffer described in the ioctl, using the command lists 1088 * that the 3D engine's binner will produce. 1089 */ 1090 int 1091 v3d_submit_cl_ioctl(struct drm_device *dev, void *data, 1092 struct drm_file *file_priv) 1093 { 1094 struct v3d_submit submit = { .v3d = to_v3d_dev(dev), .file_priv = file_priv }; 1095 struct drm_v3d_submit_cl *args = data; 1096 struct drm_syncobj *sync_out = NULL; 1097 struct v3d_submit_ext se = {0}; 1098 struct v3d_bin_job *bin = NULL; 1099 struct v3d_render_job *render; 1100 struct v3d_job *clean_job; 1101 int ret; 1102 1103 trace_v3d_submit_cl_ioctl(dev, args->rcl_start, args->rcl_end); 1104 1105 if (args->pad) 1106 return -EINVAL; 1107 1108 if (args->flags && 1109 args->flags & ~(DRM_V3D_SUBMIT_CL_FLUSH_CACHE | 1110 DRM_V3D_SUBMIT_EXTENSION)) { 1111 drm_dbg(dev, "invalid flags: %d\n", args->flags); 1112 return -EINVAL; 1113 } 1114 1115 if (args->flags & DRM_V3D_SUBMIT_EXTENSION) { 1116 ret = v3d_get_extensions(file_priv, args->extensions, &se, NULL); 1117 if (ret) { 1118 drm_dbg(dev, "Failed to get extensions.\n"); 1119 return ret; 1120 } 1121 } 1122 1123 /* If multisync is configured, give priority to it and ignore out_sync. */ 1124 if (args->out_sync && !(se.flags & DRM_V3D_EXT_ID_MULTI_SYNC)) { 1125 sync_out = drm_syncobj_find(file_priv, args->out_sync); 1126 if (!sync_out) 1127 return -ENOENT; 1128 } 1129 1130 if (args->bcl_start != args->bcl_end) { 1131 bin = (struct v3d_bin_job *)v3d_submit_add_job(&submit, V3D_BIN); 1132 if (IS_ERR(bin)) { 1133 ret = PTR_ERR(bin); 1134 goto fail; 1135 } 1136 1137 bin->start = args->bcl_start; 1138 bin->end = args->bcl_end; 1139 bin->qma = args->qma; 1140 bin->qms = args->qms; 1141 bin->qts = args->qts; 1142 1143 ret = v3d_job_add_syncobjs(&bin->base, file_priv, args->in_sync_bcl, 1144 &se); 1145 if (ret) 1146 goto fail; 1147 } 1148 1149 render = (struct v3d_render_job *)v3d_submit_add_job(&submit, V3D_RENDER); 1150 if (IS_ERR(render)) { 1151 ret = PTR_ERR(render); 1152 goto fail; 1153 } 1154 1155 INIT_LIST_HEAD(&render->unref_list); 1156 render->start = args->rcl_start; 1157 render->end = args->rcl_end; 1158 1159 if (bin) 1160 bin->render = render; 1161 1162 ret = v3d_job_add_syncobjs(&render->base, file_priv, args->in_sync_rcl, &se); 1163 if (ret) 1164 goto fail; 1165 1166 if (args->flags & DRM_V3D_SUBMIT_CL_FLUSH_CACHE) { 1167 clean_job = v3d_submit_add_job(&submit, V3D_CACHE_CLEAN); 1168 if (IS_ERR(clean_job)) { 1169 ret = PTR_ERR(clean_job); 1170 goto fail; 1171 } 1172 } 1173 1174 ret = v3d_attach_perfmon_to_jobs(&submit, args->perfmon_id); 1175 if (ret) 1176 goto fail; 1177 1178 ret = v3d_lookup_bos(&submit, args->bo_handles, args->bo_handle_count); 1179 if (ret) 1180 goto fail; 1181 1182 ret = v3d_submit_lock_reservations(&submit); 1183 if (ret) 1184 goto fail; 1185 1186 ret = v3d_submit_jobs(&submit, sync_out, &se); 1187 if (ret) 1188 goto fail_submit; 1189 1190 return 0; 1191 1192 fail: 1193 v3d_submit_cleanup_jobs(&submit); 1194 fail_submit: 1195 v3d_submit_put_post_deps(sync_out, &se); 1196 1197 return ret; 1198 } 1199 1200 /** 1201 * v3d_submit_tfu_ioctl() - Submits a TFU (texture formatting) job to the V3D. 1202 * @dev: DRM device 1203 * @data: ioctl argument 1204 * @file_priv: DRM file for this fd 1205 * 1206 * Userspace provides the register setup for the TFU, which we don't 1207 * need to validate since the TFU is behind the MMU. 1208 */ 1209 int 1210 v3d_submit_tfu_ioctl(struct drm_device *dev, void *data, 1211 struct drm_file *file_priv) 1212 { 1213 struct v3d_submit submit = { .v3d = to_v3d_dev(dev), .file_priv = file_priv }; 1214 struct drm_v3d_submit_tfu *args = data; 1215 struct drm_syncobj *sync_out = NULL; 1216 struct v3d_submit_ext se = {0}; 1217 struct v3d_tfu_job *job; 1218 int ret = 0; 1219 1220 trace_v3d_submit_tfu_ioctl(dev, args->iia); 1221 1222 if (args->flags && !(args->flags & DRM_V3D_SUBMIT_EXTENSION)) { 1223 drm_dbg(dev, "invalid flags: %d\n", args->flags); 1224 return -EINVAL; 1225 } 1226 1227 if (args->flags & DRM_V3D_SUBMIT_EXTENSION) { 1228 ret = v3d_get_extensions(file_priv, args->extensions, &se, NULL); 1229 if (ret) { 1230 drm_dbg(dev, "Failed to get extensions.\n"); 1231 return ret; 1232 } 1233 } 1234 1235 /* If multisync is configured, give priority to it and ignore out_sync. */ 1236 if (args->out_sync && !(se.flags & DRM_V3D_EXT_ID_MULTI_SYNC)) { 1237 sync_out = drm_syncobj_find(file_priv, args->out_sync); 1238 if (!sync_out) 1239 return -ENOENT; 1240 } 1241 1242 job = (struct v3d_tfu_job *)v3d_submit_add_job(&submit, V3D_TFU); 1243 if (IS_ERR(job)) { 1244 ret = PTR_ERR(job); 1245 goto fail; 1246 } 1247 1248 ret = v3d_job_add_syncobjs(&job->base, file_priv, args->in_sync, &se); 1249 if (ret) 1250 goto fail; 1251 1252 job->base.bo = kzalloc_objs(*job->base.bo, ARRAY_SIZE(args->bo_handles)); 1253 if (!job->base.bo) { 1254 ret = -ENOMEM; 1255 goto fail; 1256 } 1257 1258 job->args = *args; 1259 1260 for (job->base.bo_count = 0; 1261 job->base.bo_count < ARRAY_SIZE(args->bo_handles); 1262 job->base.bo_count++) { 1263 struct drm_gem_object *bo; 1264 1265 if (!args->bo_handles[job->base.bo_count]) 1266 break; 1267 1268 bo = drm_gem_object_lookup(file_priv, args->bo_handles[job->base.bo_count]); 1269 if (!bo) { 1270 drm_dbg(dev, "Failed to look up GEM BO %d: %d\n", 1271 job->base.bo_count, 1272 args->bo_handles[job->base.bo_count]); 1273 ret = -ENOENT; 1274 goto fail; 1275 } 1276 job->base.bo[job->base.bo_count] = bo; 1277 } 1278 1279 ret = v3d_submit_lock_reservations(&submit); 1280 if (ret) 1281 goto fail; 1282 1283 ret = v3d_submit_jobs(&submit, sync_out, &se); 1284 if (ret) 1285 goto fail_submit; 1286 1287 return 0; 1288 1289 fail: 1290 v3d_submit_cleanup_jobs(&submit); 1291 fail_submit: 1292 v3d_submit_put_post_deps(sync_out, &se); 1293 1294 return ret; 1295 } 1296 1297 /** 1298 * v3d_submit_csd_ioctl() - Submits a CSD (compute shader) job to the V3D. 1299 * @dev: DRM device 1300 * @data: ioctl argument 1301 * @file_priv: DRM file for this fd 1302 * 1303 * Userspace provides the register setup for the CSD, which we don't 1304 * need to validate since the CSD is behind the MMU. 1305 */ 1306 int 1307 v3d_submit_csd_ioctl(struct drm_device *dev, void *data, 1308 struct drm_file *file_priv) 1309 { 1310 struct v3d_submit submit = { .v3d = to_v3d_dev(dev), .file_priv = file_priv }; 1311 struct drm_v3d_submit_csd *args = data; 1312 struct drm_syncobj *sync_out = NULL; 1313 struct v3d_submit_ext se = {0}; 1314 int ret; 1315 1316 trace_v3d_submit_csd_ioctl(dev, args->cfg[5], args->cfg[6]); 1317 1318 if (args->pad) 1319 return -EINVAL; 1320 1321 if (!v3d_has_csd(submit.v3d)) { 1322 drm_warn(dev, "Attempting CSD submit on non-CSD hardware\n"); 1323 return -EINVAL; 1324 } 1325 1326 if (args->flags && !(args->flags & DRM_V3D_SUBMIT_EXTENSION)) { 1327 drm_dbg(dev, "invalid flags: %d\n", args->flags); 1328 return -EINVAL; 1329 } 1330 1331 if (args->flags & DRM_V3D_SUBMIT_EXTENSION) { 1332 ret = v3d_get_extensions(file_priv, args->extensions, &se, NULL); 1333 if (ret) { 1334 drm_dbg(dev, "Failed to get extensions.\n"); 1335 return ret; 1336 } 1337 } 1338 1339 /* If multisync is configured, give priority to it and ignore out_sync. */ 1340 if (args->out_sync && !(se.flags & DRM_V3D_EXT_ID_MULTI_SYNC)) { 1341 sync_out = drm_syncobj_find(file_priv, args->out_sync); 1342 if (!sync_out) 1343 return -ENOENT; 1344 } 1345 1346 ret = v3d_setup_csd_jobs_and_bos(&submit, args, &se); 1347 if (ret) 1348 goto fail; 1349 1350 ret = v3d_attach_perfmon_to_jobs(&submit, args->perfmon_id); 1351 if (ret) 1352 goto fail; 1353 1354 ret = v3d_submit_lock_reservations(&submit); 1355 if (ret) 1356 goto fail; 1357 1358 ret = v3d_submit_jobs(&submit, sync_out, &se); 1359 if (ret) 1360 goto fail_submit; 1361 1362 return 0; 1363 1364 fail: 1365 v3d_submit_cleanup_jobs(&submit); 1366 fail_submit: 1367 v3d_submit_put_post_deps(sync_out, &se); 1368 1369 return ret; 1370 } 1371 1372 static const unsigned int cpu_job_bo_handle_count[] = { 1373 [V3D_CPU_JOB_TYPE_INDIRECT_CSD] = 1, 1374 [V3D_CPU_JOB_TYPE_TIMESTAMP_QUERY] = 1, 1375 [V3D_CPU_JOB_TYPE_RESET_TIMESTAMP_QUERY] = 1, 1376 [V3D_CPU_JOB_TYPE_COPY_TIMESTAMP_QUERY] = 2, 1377 [V3D_CPU_JOB_TYPE_RESET_PERFORMANCE_QUERY] = 0, 1378 [V3D_CPU_JOB_TYPE_COPY_PERFORMANCE_QUERY] = 1, 1379 }; 1380 1381 /* Reject offset + (count - 1) * stride + write_size if it leaves the BO. */ 1382 static int 1383 v3d_check_copy_extent(struct drm_device *dev, size_t bo_size, 1384 u32 offset, u32 stride, u32 count, u64 write_size) 1385 { 1386 u64 last; 1387 1388 if (!count) 1389 return 0; 1390 1391 /* 1392 * The executors walk a u8 * cursor, so the furthest written byte is 1393 * offset + (count - 1) * stride + write_size, matching the pointer 1394 * arithmetic in v3d_copy_query_results()/v3d_copy_performance_query(). 1395 * (count - 1) * stride is a u32 * u32 product that is exact in u64, 1396 * and offset + write_size stays far below the u64 range, so a single 1397 * overflow check guards the total. 1398 */ 1399 last = write_size + offset; 1400 if (check_add_overflow((u64)(count - 1) * stride, last, &last) || 1401 last > bo_size) { 1402 drm_dbg(dev, "CPU job copy buffer exceeds the destination BO.\n"); 1403 return -EINVAL; 1404 } 1405 1406 return 0; 1407 } 1408 1409 /* Reject a query CPU job whose writes would land outside their BO. */ 1410 static int 1411 v3d_cpu_job_bounds_check(struct v3d_cpu_job *job) 1412 { 1413 struct drm_device *dev = &job->base.v3d->drm; 1414 struct v3d_timestamp_query_info *tquery = &job->timestamp_query; 1415 struct v3d_copy_query_results_info *copy = &job->copy; 1416 u32 elem = copy->do_64bit ? sizeof(u64) : sizeof(u32); 1417 struct v3d_bo *dst, *src; 1418 u64 slots, write_size; 1419 u32 i; 1420 1421 switch (job->job_type) { 1422 case V3D_CPU_JOB_TYPE_TIMESTAMP_QUERY: 1423 case V3D_CPU_JOB_TYPE_RESET_TIMESTAMP_QUERY: 1424 /* Each query writes one u64 timestamp slot into bo[0]. */ 1425 dst = to_v3d_bo(job->base.bo[0]); 1426 1427 for (i = 0; i < tquery->count; i++) { 1428 if ((u64)tquery->queries[i].offset + sizeof(u64) > 1429 dst->base.base.size) 1430 goto err_range; 1431 } 1432 return 0; 1433 case V3D_CPU_JOB_TYPE_COPY_TIMESTAMP_QUERY: 1434 /* Copies one u64 per query from bo[1] into bo[0]. */ 1435 dst = to_v3d_bo(job->base.bo[0]); 1436 src = to_v3d_bo(job->base.bo[1]); 1437 1438 for (i = 0; i < tquery->count; i++) { 1439 if ((u64)tquery->queries[i].offset + sizeof(u64) > 1440 src->base.base.size) 1441 goto err_range; 1442 } 1443 1444 write_size = (copy->availability_bit ? 2 : 1) * elem; 1445 return v3d_check_copy_extent(dev, dst->base.base.size, 1446 copy->offset, copy->stride, 1447 tquery->count, write_size); 1448 case V3D_CPU_JOB_TYPE_COPY_PERFORMANCE_QUERY: 1449 /* 1450 * Each query writes nperfmons * DRM_V3D_MAX_PERF_COUNTERS 1451 * counter slots into bo[0], plus an availability slot at 1452 * index ncounters. nperfmons and ncounters are user values, 1453 * so the slot count is computed overflow-safe. 1454 */ 1455 dst = to_v3d_bo(job->base.bo[0]); 1456 1457 slots = (u64)job->performance_query.nperfmons * 1458 DRM_V3D_MAX_PERF_COUNTERS; 1459 if (copy->availability_bit) 1460 slots = max(slots, 1461 (u64)job->performance_query.ncounters + 1); 1462 1463 write_size = slots * elem; 1464 return v3d_check_copy_extent(dev, dst->base.base.size, 1465 copy->offset, copy->stride, 1466 job->performance_query.count, 1467 write_size); 1468 case V3D_CPU_JOB_TYPE_INDIRECT_CSD: { 1469 struct v3d_indirect_csd_info *indirect_csd = &job->indirect_csd; 1470 1471 /* 3 is the three dimensions (x, y, z) of the workgroup counts. */ 1472 src = to_v3d_bo(job->base.bo[0]); 1473 if ((u64)indirect_csd->offset + 3 * sizeof(u32) > 1474 src->base.base.size) 1475 goto err_range; 1476 1477 dst = to_v3d_bo(indirect_csd->indirect); 1478 for (i = 0; i < 3; i++) { 1479 u32 uidx = indirect_csd->wg_uniform_offsets[i]; 1480 1481 /* 1482 * 0xffffffff means "skip this rewrite", so the exec 1483 * path never writes that index and it needs no check. 1484 */ 1485 if (uidx != 0xffffffff && 1486 (u64)uidx * sizeof(u32) + sizeof(u32) > 1487 dst->base.base.size) 1488 goto err_range; 1489 } 1490 return 0; 1491 } 1492 default: 1493 return 0; 1494 } 1495 1496 err_range: 1497 drm_dbg(dev, "CPU job query offset exceeds the BO.\n"); 1498 return -EINVAL; 1499 } 1500 1501 /** 1502 * v3d_submit_cpu_ioctl() - Submits a CPU job to the V3D. 1503 * @dev: DRM device 1504 * @data: ioctl argument 1505 * @file_priv: DRM file for this fd 1506 * 1507 * Userspace specifies the CPU job type and data required to perform its 1508 * operations through the drm_v3d_extension struct. 1509 */ 1510 int 1511 v3d_submit_cpu_ioctl(struct drm_device *dev, void *data, 1512 struct drm_file *file_priv) 1513 { 1514 struct v3d_submit submit = { .v3d = to_v3d_dev(dev), .file_priv = file_priv }; 1515 struct drm_v3d_submit_cpu *args = data; 1516 struct v3d_submit_ext se = {0}; 1517 struct v3d_cpu_job *cpu_job = NULL; 1518 int ret; 1519 1520 if (args->flags && !(args->flags & DRM_V3D_SUBMIT_EXTENSION)) { 1521 drm_dbg(dev, "Invalid flags: %d\n", args->flags); 1522 return -EINVAL; 1523 } 1524 1525 cpu_job = (struct v3d_cpu_job *)v3d_submit_add_job(&submit, V3D_CPU); 1526 if (IS_ERR(cpu_job)) 1527 return PTR_ERR(cpu_job); 1528 1529 if (args->flags & DRM_V3D_SUBMIT_EXTENSION) { 1530 ret = v3d_get_extensions(file_priv, args->extensions, &se, cpu_job); 1531 if (ret) { 1532 drm_dbg(dev, "Failed to get extensions.\n"); 1533 goto fail; 1534 } 1535 } 1536 1537 /* Every CPU job must have a CPU job user extension */ 1538 if (!cpu_job->job_type) { 1539 drm_dbg(dev, "CPU job must have a CPU job user extension.\n"); 1540 ret = -EINVAL; 1541 goto fail; 1542 } 1543 1544 if (args->bo_handle_count != cpu_job_bo_handle_count[cpu_job->job_type]) { 1545 drm_dbg(dev, "This CPU job was not submitted with the proper number of BOs.\n"); 1546 ret = -EINVAL; 1547 goto fail; 1548 } 1549 1550 trace_v3d_submit_cpu_ioctl(dev, cpu_job->job_type); 1551 1552 ret = v3d_job_add_syncobjs(&cpu_job->base, file_priv, 0, &se); 1553 if (ret) 1554 goto fail; 1555 1556 /* Look up the CPU jobs' BOs before v3d_setup_csd_jobs_and_bos() appends 1557 * the CSD and clean jobs in the case of indirect CSD job. 1558 */ 1559 if (args->bo_handle_count) { 1560 ret = v3d_lookup_bos(&submit, args->bo_handles, args->bo_handle_count); 1561 if (ret) 1562 goto fail; 1563 1564 ret = v3d_cpu_job_bounds_check(cpu_job); 1565 if (ret) 1566 goto fail; 1567 } 1568 1569 if (cpu_job->job_type == V3D_CPU_JOB_TYPE_INDIRECT_CSD) { 1570 ret = v3d_setup_csd_jobs_and_bos(&submit, &cpu_job->indirect_csd.args, 1571 NULL); 1572 if (ret) 1573 goto fail; 1574 1575 /* The CSD job was appended at jobs[1] */ 1576 if (WARN_ON(submit.jobs[1]->queue != V3D_CSD)) { 1577 ret = -EINVAL; 1578 goto fail; 1579 } 1580 1581 cpu_job->indirect_csd.job = container_of(submit.jobs[1], struct v3d_csd_job, 1582 base); 1583 } 1584 1585 ret = v3d_submit_lock_reservations(&submit); 1586 if (ret) 1587 goto fail; 1588 1589 ret = v3d_submit_jobs(&submit, NULL, &se); 1590 if (ret) 1591 goto fail_submit; 1592 1593 return 0; 1594 1595 fail: 1596 v3d_submit_cleanup_jobs(&submit); 1597 fail_submit: 1598 v3d_submit_put_post_deps(NULL, &se); 1599 1600 return ret; 1601 } 1602