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