Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/msm: Add syncobj support.

This

1) Enables core DRM syncobj support.
2) Adds options to the submission ioctl to wait/signal syncobjs.

Just like the wait fence fd, this does inline waits. Using the
scheduler would be nice but I believe it is out of scope for
this work.

Support for timeline syncobjs is implemented and the interface
is ready for it, but I'm not enabling it yet until there is
some code for turnip to use it.

The reset is mostly in there because in the presence of waiting
and signalling the same semaphores, resetting them after
signalling can become very annoying.

v2:
- Fixed style issues
- Removed a cleanup issue in a failure case
- Moved to a copy_from_user per syncobj

v3:
- Fixed a missing declaration introduced in v2
- Reworked to use ERR_PTR/PTR_ERR
- Simplified failure gotos.

Used by: https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/2769

Signed-off-by: Bas Nieuwenhuizen <bas@basnieuwenhuizen.nl>
Reviewed-by: Jordan Crouse <jcrouse@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@chromium.org>

authored by

Bas Nieuwenhuizen and committed by
Rob Clark
ab723b7a 6a523388

+258 -4
+4 -2
drivers/gpu/drm/msm/msm_drv.c
··· 37 37 * - 1.4.0 - softpin, MSM_RELOC_BO_DUMP, and GEM_INFO support to set/get 38 38 * GEM object's debug name 39 39 * - 1.5.0 - Add SUBMITQUERY_QUERY ioctl 40 + * - 1.6.0 - Syncobj support 40 41 */ 41 42 #define MSM_VERSION_MAJOR 1 42 - #define MSM_VERSION_MINOR 5 43 + #define MSM_VERSION_MINOR 6 43 44 #define MSM_VERSION_PATCHLEVEL 0 44 45 45 46 static const struct drm_mode_config_funcs mode_config_funcs = { ··· 1003 1002 .driver_features = DRIVER_GEM | 1004 1003 DRIVER_RENDER | 1005 1004 DRIVER_ATOMIC | 1006 - DRIVER_MODESET, 1005 + DRIVER_MODESET | 1006 + DRIVER_SYNCOBJ, 1007 1007 .open = msm_open, 1008 1008 .postclose = msm_postclose, 1009 1009 .lastclose = drm_fb_helper_lastclose,
+231 -1
drivers/gpu/drm/msm/msm_gem_submit.c
··· 8 8 #include <linux/sync_file.h> 9 9 #include <linux/uaccess.h> 10 10 11 + #include <drm/drm_drv.h> 11 12 #include <drm/drm_file.h> 13 + #include <drm/drm_syncobj.h> 12 14 13 15 #include "msm_drv.h" 14 16 #include "msm_gpu.h" ··· 393 391 } 394 392 } 395 393 394 + 395 + struct msm_submit_post_dep { 396 + struct drm_syncobj *syncobj; 397 + uint64_t point; 398 + struct dma_fence_chain *chain; 399 + }; 400 + 401 + static struct drm_syncobj **msm_wait_deps(struct drm_device *dev, 402 + struct drm_file *file, 403 + uint64_t in_syncobjs_addr, 404 + uint32_t nr_in_syncobjs, 405 + size_t syncobj_stride, 406 + struct msm_ringbuffer *ring) 407 + { 408 + struct drm_syncobj **syncobjs = NULL; 409 + struct drm_msm_gem_submit_syncobj syncobj_desc = {0}; 410 + int ret = 0; 411 + uint32_t i, j; 412 + 413 + syncobjs = kcalloc(nr_in_syncobjs, sizeof(*syncobjs), 414 + GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY); 415 + if (!syncobjs) 416 + return ERR_PTR(-ENOMEM); 417 + 418 + for (i = 0; i < nr_in_syncobjs; ++i) { 419 + uint64_t address = in_syncobjs_addr + i * syncobj_stride; 420 + struct dma_fence *fence; 421 + 422 + if (copy_from_user(&syncobj_desc, 423 + u64_to_user_ptr(address), 424 + min(syncobj_stride, sizeof(syncobj_desc)))) { 425 + ret = -EFAULT; 426 + break; 427 + } 428 + 429 + if (syncobj_desc.point && 430 + !drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE)) { 431 + ret = -EOPNOTSUPP; 432 + break; 433 + } 434 + 435 + if (syncobj_desc.flags & ~MSM_SUBMIT_SYNCOBJ_FLAGS) { 436 + ret = -EINVAL; 437 + break; 438 + } 439 + 440 + ret = drm_syncobj_find_fence(file, syncobj_desc.handle, 441 + syncobj_desc.point, 0, &fence); 442 + if (ret) 443 + break; 444 + 445 + if (!dma_fence_match_context(fence, ring->fctx->context)) 446 + ret = dma_fence_wait(fence, true); 447 + 448 + dma_fence_put(fence); 449 + if (ret) 450 + break; 451 + 452 + if (syncobj_desc.flags & MSM_SUBMIT_SYNCOBJ_RESET) { 453 + syncobjs[i] = 454 + drm_syncobj_find(file, syncobj_desc.handle); 455 + if (!syncobjs[i]) { 456 + ret = -EINVAL; 457 + break; 458 + } 459 + } 460 + } 461 + 462 + if (ret) { 463 + for (j = 0; j <= i; ++j) { 464 + if (syncobjs[j]) 465 + drm_syncobj_put(syncobjs[j]); 466 + } 467 + kfree(syncobjs); 468 + return ERR_PTR(ret); 469 + } 470 + return syncobjs; 471 + } 472 + 473 + static void msm_reset_syncobjs(struct drm_syncobj **syncobjs, 474 + uint32_t nr_syncobjs) 475 + { 476 + uint32_t i; 477 + 478 + for (i = 0; syncobjs && i < nr_syncobjs; ++i) { 479 + if (syncobjs[i]) 480 + drm_syncobj_replace_fence(syncobjs[i], NULL); 481 + } 482 + } 483 + 484 + static struct msm_submit_post_dep *msm_parse_post_deps(struct drm_device *dev, 485 + struct drm_file *file, 486 + uint64_t syncobjs_addr, 487 + uint32_t nr_syncobjs, 488 + size_t syncobj_stride) 489 + { 490 + struct msm_submit_post_dep *post_deps; 491 + struct drm_msm_gem_submit_syncobj syncobj_desc = {0}; 492 + int ret = 0; 493 + uint32_t i, j; 494 + 495 + post_deps = kmalloc_array(nr_syncobjs, sizeof(*post_deps), 496 + GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY); 497 + if (!post_deps) 498 + return ERR_PTR(-ENOMEM); 499 + 500 + for (i = 0; i < nr_syncobjs; ++i) { 501 + uint64_t address = syncobjs_addr + i * syncobj_stride; 502 + 503 + if (copy_from_user(&syncobj_desc, 504 + u64_to_user_ptr(address), 505 + min(syncobj_stride, sizeof(syncobj_desc)))) { 506 + ret = -EFAULT; 507 + break; 508 + } 509 + 510 + post_deps[i].point = syncobj_desc.point; 511 + post_deps[i].chain = NULL; 512 + 513 + if (syncobj_desc.flags) { 514 + ret = -EINVAL; 515 + break; 516 + } 517 + 518 + if (syncobj_desc.point) { 519 + if (!drm_core_check_feature(dev, 520 + DRIVER_SYNCOBJ_TIMELINE)) { 521 + ret = -EOPNOTSUPP; 522 + break; 523 + } 524 + 525 + post_deps[i].chain = 526 + kmalloc(sizeof(*post_deps[i].chain), 527 + GFP_KERNEL); 528 + if (!post_deps[i].chain) { 529 + ret = -ENOMEM; 530 + break; 531 + } 532 + } 533 + 534 + post_deps[i].syncobj = 535 + drm_syncobj_find(file, syncobj_desc.handle); 536 + if (!post_deps[i].syncobj) { 537 + ret = -EINVAL; 538 + break; 539 + } 540 + } 541 + 542 + if (ret) { 543 + for (j = 0; j <= i; ++j) { 544 + kfree(post_deps[j].chain); 545 + if (post_deps[j].syncobj) 546 + drm_syncobj_put(post_deps[j].syncobj); 547 + } 548 + 549 + kfree(post_deps); 550 + return ERR_PTR(ret); 551 + } 552 + 553 + return post_deps; 554 + } 555 + 556 + static void msm_process_post_deps(struct msm_submit_post_dep *post_deps, 557 + uint32_t count, struct dma_fence *fence) 558 + { 559 + uint32_t i; 560 + 561 + for (i = 0; post_deps && i < count; ++i) { 562 + if (post_deps[i].chain) { 563 + drm_syncobj_add_point(post_deps[i].syncobj, 564 + post_deps[i].chain, 565 + fence, post_deps[i].point); 566 + post_deps[i].chain = NULL; 567 + } else { 568 + drm_syncobj_replace_fence(post_deps[i].syncobj, 569 + fence); 570 + } 571 + } 572 + } 573 + 396 574 int msm_ioctl_gem_submit(struct drm_device *dev, void *data, 397 575 struct drm_file *file) 398 576 { ··· 585 403 struct sync_file *sync_file = NULL; 586 404 struct msm_gpu_submitqueue *queue; 587 405 struct msm_ringbuffer *ring; 406 + struct msm_submit_post_dep *post_deps = NULL; 407 + struct drm_syncobj **syncobjs_to_reset = NULL; 588 408 int out_fence_fd = -1; 589 409 struct pid *pid = get_pid(task_pid(current)); 590 410 bool has_ww_ticket = false; ··· 594 410 int ret, submitid; 595 411 if (!gpu) 596 412 return -ENXIO; 413 + 414 + if (args->pad) 415 + return -EINVAL; 597 416 598 417 /* for now, we just have 3d pipe.. eventually this would need to 599 418 * be more clever to dispatch to appropriate gpu module: ··· 645 458 return ret; 646 459 } 647 460 461 + if (args->flags & MSM_SUBMIT_SYNCOBJ_IN) { 462 + syncobjs_to_reset = msm_wait_deps(dev, file, 463 + args->in_syncobjs, 464 + args->nr_in_syncobjs, 465 + args->syncobj_stride, ring); 466 + if (IS_ERR(syncobjs_to_reset)) 467 + return PTR_ERR(syncobjs_to_reset); 468 + } 469 + 470 + if (args->flags & MSM_SUBMIT_SYNCOBJ_OUT) { 471 + post_deps = msm_parse_post_deps(dev, file, 472 + args->out_syncobjs, 473 + args->nr_out_syncobjs, 474 + args->syncobj_stride); 475 + if (IS_ERR(post_deps)) { 476 + ret = PTR_ERR(post_deps); 477 + goto out_post_unlock; 478 + } 479 + } 480 + 648 481 ret = mutex_lock_interruptible(&dev->struct_mutex); 649 482 if (ret) 650 - return ret; 483 + goto out_post_unlock; 651 484 652 485 if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) { 653 486 out_fence_fd = get_unused_fd_flags(O_CLOEXEC); ··· 794 587 args->fence_fd = out_fence_fd; 795 588 } 796 589 590 + msm_reset_syncobjs(syncobjs_to_reset, args->nr_in_syncobjs); 591 + msm_process_post_deps(post_deps, args->nr_out_syncobjs, 592 + submit->fence); 593 + 594 + 797 595 out: 798 596 submit_cleanup(submit); 799 597 if (has_ww_ticket) ··· 809 597 if (ret && (out_fence_fd >= 0)) 810 598 put_unused_fd(out_fence_fd); 811 599 mutex_unlock(&dev->struct_mutex); 600 + 601 + out_post_unlock: 602 + if (!IS_ERR_OR_NULL(post_deps)) { 603 + for (i = 0; i < args->nr_out_syncobjs; ++i) { 604 + kfree(post_deps[i].chain); 605 + drm_syncobj_put(post_deps[i].syncobj); 606 + } 607 + kfree(post_deps); 608 + } 609 + 610 + if (!IS_ERR_OR_NULL(syncobjs_to_reset)) { 611 + for (i = 0; i < args->nr_in_syncobjs; ++i) { 612 + if (syncobjs_to_reset[i]) 613 + drm_syncobj_put(syncobjs_to_reset[i]); 614 + } 615 + kfree(syncobjs_to_reset); 616 + } 617 + 812 618 return ret; 813 619 }
+23 -1
include/uapi/drm/msm_drm.h
··· 217 217 #define MSM_SUBMIT_FENCE_FD_IN 0x40000000 /* enable input fence_fd */ 218 218 #define MSM_SUBMIT_FENCE_FD_OUT 0x20000000 /* enable output fence_fd */ 219 219 #define MSM_SUBMIT_SUDO 0x10000000 /* run submitted cmds from RB */ 220 + #define MSM_SUBMIT_SYNCOBJ_IN 0x08000000 /* enable input syncobj */ 221 + #define MSM_SUBMIT_SYNCOBJ_OUT 0x04000000 /* enable output syncobj */ 220 222 #define MSM_SUBMIT_FLAGS ( \ 221 223 MSM_SUBMIT_NO_IMPLICIT | \ 222 224 MSM_SUBMIT_FENCE_FD_IN | \ 223 225 MSM_SUBMIT_FENCE_FD_OUT | \ 224 226 MSM_SUBMIT_SUDO | \ 227 + MSM_SUBMIT_SYNCOBJ_IN | \ 228 + MSM_SUBMIT_SYNCOBJ_OUT | \ 225 229 0) 230 + 231 + #define MSM_SUBMIT_SYNCOBJ_RESET 0x00000001 /* Reset syncobj after wait. */ 232 + #define MSM_SUBMIT_SYNCOBJ_FLAGS ( \ 233 + MSM_SUBMIT_SYNCOBJ_RESET | \ 234 + 0) 235 + 236 + struct drm_msm_gem_submit_syncobj { 237 + __u32 handle; /* in, syncobj handle. */ 238 + __u32 flags; /* in, from MSM_SUBMIT_SYNCOBJ_FLAGS */ 239 + __u64 point; /* in, timepoint for timeline syncobjs. */ 240 + }; 226 241 227 242 /* Each cmdstream submit consists of a table of buffers involved, and 228 243 * one or more cmdstream buffers. This allows for conditional execution ··· 251 236 __u64 bos; /* in, ptr to array of submit_bo's */ 252 237 __u64 cmds; /* in, ptr to array of submit_cmd's */ 253 238 __s32 fence_fd; /* in/out fence fd (see MSM_SUBMIT_FENCE_FD_IN/OUT) */ 254 - __u32 queueid; /* in, submitqueue id */ 239 + __u32 queueid; /* in, submitqueue id */ 240 + __u64 in_syncobjs; /* in, ptr to to array of drm_msm_gem_submit_syncobj */ 241 + __u64 out_syncobjs; /* in, ptr to to array of drm_msm_gem_submit_syncobj */ 242 + __u32 nr_in_syncobjs; /* in, number of entries in in_syncobj */ 243 + __u32 nr_out_syncobjs; /* in, number of entries in out_syncobj. */ 244 + __u32 syncobj_stride; /* in, stride of syncobj arrays. */ 245 + __u32 pad; /*in, reserved for future use, always 0. */ 246 + 255 247 }; 256 248 257 249 /* The normal way to synchronize with the GPU is just to CPU_PREP on