/* * ============================================================================= * ROC Runtime Conformance Release License * ============================================================================= * The University of Illinois/NCSA * Open Source License (NCSA) * * Copyright (c) 2021-2021, Advanced Micro Devices, Inc. * All rights reserved. * * Developed by: * * AMD Research and AMD ROC Software Development * * Advanced Micro Devices, Inc. * * www.amd.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal with 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: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimers. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimers in * the documentation and/or other materials provided with the distribution. * - Neither the names of , * nor the names of its contributors may be used to endorse or promote * products derived from this Software without specific prior written * permission. * * 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 CONTRIBUTORS OR COPYRIGHT HOLDERS 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 WITH THE SOFTWARE. * */ #include "suites/functional/cu_masking.h" #include "common/base_rocr_utils.h" #include "common/common.h" #include "common/os.h" #include "common/helper_funcs.h" #include "gtest/gtest.h" #include "hsa/hsa.h" #include "hsa/hsa_ext_amd.h" #include #include #include #include #include CU_Masking::CU_Masking() : TestBase() { std::string name; std::string desc; name = "CU Masking"; desc = "This test checks CU masking functionality via hsa_amd_queue_cu_get(set)_mask and HSA_CU_MASK."; set_title(name); set_description(desc); set_kernel_file_name("cu_mask_kernels.hsaco"); } void CU_Masking::Run() { hsa_status_t err; TestBase::Run(); printf("Running %lu iterations\n", RealIterationNum()); // Random source std::mt19937 rand(std::chrono::system_clock::now().time_since_epoch().count()); // Store cu masking variable std::string mask_var; char* temp = getenv("HSA_CU_MASK"); if(temp!=nullptr) mask_var = temp; unsetenv("HSA_CU_MASK"); std::string mask_init_var; temp = getenv("HSA_CU_MASK_SKIP_INIT"); if(temp!=nullptr) mask_init_var = temp; unsetenv("HSA_CU_MASK_SKIP_INIT"); // Loop over and test all GPUs uint32_t idx = 0; while(true) { Device* gpu; CodeObject* obj; Kernel kern; struct args_t { uint32_t* hw_ids; OCLHiddenArgs _; }; args_t* args; hsa_signal_t signal; hsa_queue_t* q; uint32_t cu_count; uint32_t group_size; uint32_t max_grid_size; uint32_t threads; auto init = [&]() { System::Init(); if(idx == System::gpu().size()) return false; gpu = &System::gpu()[idx]; std::string filename = rocrtst::LocateKernelFile(kernel_file_name(), gpu->agent); obj = new CodeObject(filename, *gpu); err = hsa_agent_get_info(gpu->agent, (hsa_agent_info_t)HSA_AMD_AGENT_INFO_COMPUTE_UNIT_COUNT, &cu_count); CHECK(err); err = hsa_agent_get_info(gpu->agent, (hsa_agent_info_t)HSA_AGENT_INFO_WORKGROUP_MAX_SIZE, &group_size); CHECK(err); err = hsa_agent_get_info(gpu->agent, (hsa_agent_info_t)HSA_AGENT_INFO_GRID_MAX_SIZE, &max_grid_size); CHECK(err); uint64_t max_threads = uint64_t(cu_count)*group_size*10; threads = max_threads < max_grid_size ? max_threads : max_grid_size; threads = (threads / group_size) * group_size; // All CU enabled check if(!obj->GetKernel("get_hw_id", kern)) { ADD_FAILURE(); return false; } args = (args_t*)hsaMalloc(sizeof(args_t), System::kernarg()); memset(args, 0, sizeof(args_t)); args->hw_ids = (uint32_t*)hsaMalloc(sizeof(uint32_t)*threads, System::kernarg()); err = hsa_signal_create(1, 0, nullptr, &signal); CHECK(err); err = hsa_queue_create(gpu->agent, 4096, HSA_QUEUE_TYPE_SINGLE, nullptr, nullptr, 0, 0, &q); CHECK(err); return true; }; auto fini = [&]() { err = hsa_queue_destroy(q); CHECK(err); err = hsa_signal_destroy(signal); CHECK(err); err = hsa_memory_free(args->hw_ids); CHECK(err); err = hsa_memory_free(args); CHECK(err); delete obj; gpu = nullptr; System::Shutdown(); }; auto dispatch = [&]() { memset(args->hw_ids, 0, sizeof(uint32_t)*threads); Aql pkt = { }; pkt.header.type = HSA_PACKET_TYPE_KERNEL_DISPATCH; pkt.header.acquire = HSA_FENCE_SCOPE_SYSTEM; pkt.header.release = HSA_FENCE_SCOPE_SYSTEM; pkt.dispatch.kernel_object = kern.handle; pkt.dispatch.private_segment_size = kern.scratch; pkt.dispatch.group_segment_size = kern.group; pkt.dispatch.setup = 1; pkt.dispatch.workgroup_size_x = group_size; pkt.dispatch.workgroup_size_y = 1; pkt.dispatch.workgroup_size_z = 1; pkt.dispatch.grid_size_x = threads; pkt.dispatch.grid_size_y = 1; pkt.dispatch.grid_size_z = 1; pkt.dispatch.kernarg_address = args; pkt.dispatch.completion_signal = signal; SubmitPacket(q, pkt); hsa_signal_wait_scacquire(signal, HSA_SIGNAL_CONDITION_EQ, 0, -1ull, HSA_WAIT_STATE_BLOCKED); hsa_signal_store_relaxed(signal, 1); }; auto getHwIds = [&](std::vector& ids){ dispatch(); std::sort(&args->hw_ids[0], &args->hw_ids[threads]); uint32_t* end = std::unique(&args->hw_ids[0], &args->hw_ids[threads]); ids.clear(); ids.insert(ids.begin(), &args->hw_ids[0], end); }; // Check fully unconstrained. unsetenv("HSA_CU_MASK_SKIP_INIT"); setenv("HSA_CU_MASK_SKIP_INIT", "1", 1); if(!init()) break; { char name[64]; hsa_agent_get_info(gpu->agent, HSA_AGENT_INFO_NAME, name); name[63]='\0'; printf("Testing gpu index %u, %s\n", idx, name); } std::vector left, right, isect; // Check unconstrained cu set. getHwIds(left); printf("Expecting %u CUs, found %lu with HSA_CU_MASK_SKIP_INIT.\n", cu_count, left.size()); ASSERT_EQ(cu_count, left.size()); fini(); unsetenv("HSA_CU_MASK_SKIP_INIT"); // Check fully enabled, but mask used, set. setenv("HSA_CU_MASK", (std::to_string(idx)+":0-"+std::to_string(cu_count-1)).c_str(), 1); init(); getHwIds(right); printf("Expecting %u CUs, found %lu with HSA_CU_MASK.\n", cu_count, right.size()); if(cu_count != right.size()) { isect.resize(left.size()); auto isect_end = std::set_difference(left.begin(), left.end(), right.begin(), right.end(), isect.begin()); isect.resize(isect_end - isect.begin()); printf("Missing CUs: "); for(auto cu : isect) printf("%u ", cu); printf("\n"); } ASSERT_EQ(cu_count, right.size()); fini(); unsetenv("HSA_CU_MASK"); // Check rocr default mask. init(); getHwIds(right); printf("Expecting %u CUs, found %lu.\n", cu_count, right.size()); if(cu_count != right.size()) { isect.resize(left.size()); auto isect_end = std::set_difference(left.begin(), left.end(), right.begin(), right.end(), isect.begin()); isect.resize(isect_end - isect.begin()); printf("Missing CUs: "); for(auto cu : isect) printf("%u ", cu); printf("\n"); } ASSERT_EQ(cu_count, right.size()); fini(); std::vector bits; for(uint32_t i=0; i bitmask, resultmask; uint32_t dwords = (cu_count + 31) / 32; bitmask.resize(dwords); resultmask.resize(dwords); for(size_t iteration=0; iteration& array) { assert(array.size() == dwords && "Bitmask array has incorrect size."); for(uint32_t i=0; i& hw_ids) { setBits(start, stop, bitmask); err = hsa_amd_queue_cu_set_mask(q, dwords*32, &bitmask[0]); if((err!=HSA_STATUS_SUCCESS) && (err!=(hsa_status_t)HSA_STATUS_CU_MASK_REDUCED)) CHECK(err); err = hsa_amd_queue_cu_get_mask(q, dwords*32, &resultmask[0]); CHECK(err); getHwIds(hw_ids); }; auto getIsect = [&]() { isect.resize(left.size()); auto isect_end = std::set_intersection(left.begin(), left.end(), right.begin(), right.end(), isect.begin()); isect.resize(isect_end - isect.begin()); }; auto printMask = [](std::vector& mask) { printf("0x"); for(size_t i=1; i env_mask(&bits[0], &bits[mask_index]); // Convert to string range syntax std::sort(env_mask.begin(), env_mask.end()); uint32_t start, stop; start=stop=env_mask[0]; std::vector ranges; // Append invalid bit so that final loop will emit the last range. env_mask.push_back(-1); for(size_t j=1; j "); printMask(env_mask); printf("\n"); init(); getHwIds(left); printf("Expecting %u CUs, found %lu\n", mask_index, left.size()); ASSERT_EQ(left.size(), mask_index); // Check that HSA_CU_MASK constrains the API // Find at least partially enabled CU mask. [&]() { while(true) { std::shuffle(bits.begin(), bits.end(), rand); split_index = (rand() % (cu_count - 2)) + 1; setBits(0, split_index, bitmask); for(uint32_t i=0; i