| /* | |
| * Copyright 2017 Advanced Micro Devices, Inc. | |
| * | |
| * Permission is hereby granted, free of charge, to any person obtaining a | |
| * copy of this software and associated documentation files (the "Software"), | |
| * to deal in the Software without restriction, including without limitation | |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| * and/or sell copies of the Software, and to permit persons to whom the | |
| * Software is furnished to do so, subject to the following conditions: | |
| * | |
| * The above copyright notice and this permission notice shall be included in | |
| * all copies or substantial portions of the Software. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| * OTHER DEALINGS IN THE SOFTWARE. | |
| * | |
| */ | |
| #include "CUnit/Basic.h" | |
| #include "xf86drm.h" | |
| #include "amdgpu_test.h" | |
| #include "amdgpu_drm.h" | |
| #include "amdgpu_internal.h" | |
| #include <pthread.h> | |
| static amdgpu_device_handle device_handle; | |
| static uint32_t major_version; | |
| static uint32_t minor_version; | |
| static void amdgpu_syncobj_timeline_test(void); | |
| CU_BOOL suite_syncobj_timeline_tests_enable(void) | |
| { | |
| int r; | |
| uint64_t cap = 0; | |
| r = drmGetCap(drm_amdgpu[0], DRM_CAP_SYNCOBJ_TIMELINE, &cap); | |
| if (r || cap == 0) | |
| return CU_FALSE; | |
| return CU_TRUE; | |
| } | |
| int suite_syncobj_timeline_tests_init(void) | |
| { | |
| int r; | |
| r = amdgpu_device_initialize(drm_amdgpu[0], &major_version, | |
| &minor_version, &device_handle); | |
| if (r) { | |
| if ((r == -EACCES) && (errno == EACCES)) | |
| printf("\n\nError:%s. " | |
| "Hint:Try to run this test program as root.", | |
| strerror(errno)); | |
| return CUE_SINIT_FAILED; | |
| } | |
| return CUE_SUCCESS; | |
| } | |
| int suite_syncobj_timeline_tests_clean(void) | |
| { | |
| int r = amdgpu_device_deinitialize(device_handle); | |
| if (r == 0) | |
| return CUE_SUCCESS; | |
| else | |
| return CUE_SCLEAN_FAILED; | |
| } | |
| CU_TestInfo syncobj_timeline_tests[] = { | |
| { "syncobj timeline test", amdgpu_syncobj_timeline_test }, | |
| CU_TEST_INFO_NULL, | |
| }; | |
| #define GFX_COMPUTE_NOP 0xffff1000 | |
| #define SDMA_NOP 0x0 | |
| static int syncobj_command_submission_helper(uint32_t syncobj_handle, bool | |
| wait_or_signal, uint64_t point) | |
| { | |
| amdgpu_context_handle context_handle; | |
| amdgpu_bo_handle ib_result_handle; | |
| void *ib_result_cpu; | |
| uint64_t ib_result_mc_address; | |
| struct drm_amdgpu_cs_chunk chunks[2]; | |
| struct drm_amdgpu_cs_chunk_data chunk_data; | |
| struct drm_amdgpu_cs_chunk_syncobj syncobj_data; | |
| struct amdgpu_cs_fence fence_status; | |
| amdgpu_bo_list_handle bo_list; | |
| amdgpu_va_handle va_handle; | |
| uint32_t expired; | |
| int i, r; | |
| uint64_t seq_no; | |
| uint32_t *ptr; | |
| r = amdgpu_cs_ctx_create(device_handle, &context_handle); | |
| CU_ASSERT_EQUAL(r, 0); | |
| r = amdgpu_bo_alloc_and_map(device_handle, 4096, 4096, | |
| AMDGPU_GEM_DOMAIN_GTT, 0, | |
| &ib_result_handle, &ib_result_cpu, | |
| &ib_result_mc_address, &va_handle); | |
| CU_ASSERT_EQUAL(r, 0); | |
| r = amdgpu_get_bo_list(device_handle, ib_result_handle, NULL, | |
| &bo_list); | |
| CU_ASSERT_EQUAL(r, 0); | |
| ptr = ib_result_cpu; | |
| for (i = 0; i < 16; ++i) | |
| ptr[i] = wait_or_signal ? GFX_COMPUTE_NOP: SDMA_NOP; | |
| chunks[0].chunk_id = AMDGPU_CHUNK_ID_IB; | |
| chunks[0].length_dw = sizeof(struct drm_amdgpu_cs_chunk_ib) / 4; | |
| chunks[0].chunk_data = (uint64_t)(uintptr_t)&chunk_data; | |
| chunk_data.ib_data._pad = 0; | |
| chunk_data.ib_data.va_start = ib_result_mc_address; | |
| chunk_data.ib_data.ib_bytes = 16 * 4; | |
| chunk_data.ib_data.ip_type = wait_or_signal ? AMDGPU_HW_IP_GFX : | |
| AMDGPU_HW_IP_DMA; | |
| chunk_data.ib_data.ip_instance = 0; | |
| chunk_data.ib_data.ring = 0; | |
| chunk_data.ib_data.flags = 0; | |
| chunks[1].chunk_id = wait_or_signal ? | |
| AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_WAIT : | |
| AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_SIGNAL; | |
| chunks[1].length_dw = sizeof(struct drm_amdgpu_cs_chunk_syncobj) / 4; | |
| chunks[1].chunk_data = (uint64_t)(uintptr_t)&syncobj_data; | |
| syncobj_data.handle = syncobj_handle; | |
| syncobj_data.point = point; | |
| syncobj_data.flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT; | |
| r = amdgpu_cs_submit_raw(device_handle, | |
| context_handle, | |
| bo_list, | |
| 2, | |
| chunks, | |
| &seq_no); | |
| CU_ASSERT_EQUAL(r, 0); | |
| memset(&fence_status, 0, sizeof(struct amdgpu_cs_fence)); | |
| fence_status.context = context_handle; | |
| fence_status.ip_type = wait_or_signal ? AMDGPU_HW_IP_GFX: | |
| AMDGPU_HW_IP_DMA; | |
| fence_status.ip_instance = 0; | |
| fence_status.ring = 0; | |
| fence_status.fence = seq_no; | |
| r = amdgpu_cs_query_fence_status(&fence_status, | |
| AMDGPU_TIMEOUT_INFINITE,0, &expired); | |
| CU_ASSERT_EQUAL(r, 0); | |
| r = amdgpu_bo_list_destroy(bo_list); | |
| CU_ASSERT_EQUAL(r, 0); | |
| r = amdgpu_bo_unmap_and_free(ib_result_handle, va_handle, | |
| ib_result_mc_address, 4096); | |
| CU_ASSERT_EQUAL(r, 0); | |
| r = amdgpu_cs_ctx_free(context_handle); | |
| CU_ASSERT_EQUAL(r, 0); | |
| return r; | |
| } | |
| struct syncobj_point { | |
| uint32_t syncobj_handle; | |
| uint64_t point; | |
| }; | |
| static void *syncobj_wait(void *data) | |
| { | |
| struct syncobj_point *sp = (struct syncobj_point *)data; | |
| int r; | |
| r = syncobj_command_submission_helper(sp->syncobj_handle, true, | |
| sp->point); | |
| CU_ASSERT_EQUAL(r, 0); | |
| return (void *)(long)r; | |
| } | |
| static void *syncobj_signal(void *data) | |
| { | |
| struct syncobj_point *sp = (struct syncobj_point *)data; | |
| int r; | |
| r = syncobj_command_submission_helper(sp->syncobj_handle, false, | |
| sp->point); | |
| CU_ASSERT_EQUAL(r, 0); | |
| return (void *)(long)r; | |
| } | |
| static void amdgpu_syncobj_timeline_test(void) | |
| { | |
| static pthread_t wait_thread; | |
| static pthread_t signal_thread; | |
| static pthread_t c_thread; | |
| struct syncobj_point sp1, sp2, sp3; | |
| uint32_t syncobj_handle; | |
| uint64_t payload; | |
| uint64_t wait_point, signal_point; | |
| uint64_t timeout; | |
| struct timespec tp; | |
| int r, sync_fd; | |
| void *tmp; | |
| r = amdgpu_cs_create_syncobj2(device_handle, 0, &syncobj_handle); | |
| CU_ASSERT_EQUAL(r, 0); | |
| // wait on point 5 | |
| sp1.syncobj_handle = syncobj_handle; | |
| sp1.point = 5; | |
| r = pthread_create(&wait_thread, NULL, syncobj_wait, &sp1); | |
| CU_ASSERT_EQUAL(r, 0); | |
| // signal on point 10 | |
| sp2.syncobj_handle = syncobj_handle; | |
| sp2.point = 10; | |
| r = pthread_create(&signal_thread, NULL, syncobj_signal, &sp2); | |
| CU_ASSERT_EQUAL(r, 0); | |
| r = pthread_join(wait_thread, &tmp); | |
| CU_ASSERT_EQUAL(r, 0); | |
| CU_ASSERT_EQUAL(tmp, 0); | |
| r = pthread_join(signal_thread, &tmp); | |
| CU_ASSERT_EQUAL(r, 0); | |
| CU_ASSERT_EQUAL(tmp, 0); | |
| //query timeline payload | |
| r = amdgpu_cs_syncobj_query(device_handle, &syncobj_handle, | |
| &payload, 1); | |
| CU_ASSERT_EQUAL(r, 0); | |
| CU_ASSERT_EQUAL(payload, 10); | |
| //signal on point 16 | |
| sp3.syncobj_handle = syncobj_handle; | |
| sp3.point = 16; | |
| r = pthread_create(&c_thread, NULL, syncobj_signal, &sp3); | |
| CU_ASSERT_EQUAL(r, 0); | |
| //CPU wait on point 16 | |
| wait_point = 16; | |
| timeout = 0; | |
| clock_gettime(CLOCK_MONOTONIC, &tp); | |
| timeout = tp.tv_sec * 1000000000ULL + tp.tv_nsec; | |
| timeout += 0x10000000000; //10s | |
| r = amdgpu_cs_syncobj_timeline_wait(device_handle, &syncobj_handle, | |
| &wait_point, 1, timeout, | |
| DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL | | |
| DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, | |
| NULL); | |
| CU_ASSERT_EQUAL(r, 0); | |
| r = pthread_join(c_thread, &tmp); | |
| CU_ASSERT_EQUAL(r, 0); | |
| CU_ASSERT_EQUAL(tmp, 0); | |
| // export point 16 and import to point 18 | |
| r = amdgpu_cs_syncobj_export_sync_file2(device_handle, syncobj_handle, | |
| 16, | |
| DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, | |
| &sync_fd); | |
| CU_ASSERT_EQUAL(r, 0); | |
| r = amdgpu_cs_syncobj_import_sync_file2(device_handle, syncobj_handle, | |
| 18, sync_fd); | |
| CU_ASSERT_EQUAL(r, 0); | |
| r = amdgpu_cs_syncobj_query(device_handle, &syncobj_handle, | |
| &payload, 1); | |
| CU_ASSERT_EQUAL(r, 0); | |
| CU_ASSERT_EQUAL(payload, 18); | |
| // CPU signal on point 20 | |
| signal_point = 20; | |
| r = amdgpu_cs_syncobj_timeline_signal(device_handle, &syncobj_handle, | |
| &signal_point, 1); | |
| CU_ASSERT_EQUAL(r, 0); | |
| r = amdgpu_cs_syncobj_query(device_handle, &syncobj_handle, | |
| &payload, 1); | |
| CU_ASSERT_EQUAL(r, 0); | |
| CU_ASSERT_EQUAL(payload, 20); | |
| r = amdgpu_cs_destroy_syncobj(device_handle, syncobj_handle); | |
| CU_ASSERT_EQUAL(r, 0); | |
| } |