/****************************************************************************** * Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. * * 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 * AUTHORS 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 * IN THE SOFTWARE. *****************************************************************************/ #include "context_ib_device.hpp" #include #include "config.h" // NOLINT(build/include_subdir) #include "roc_shmem/roc_shmem.hpp" #include "../backend_type.hpp" #include "../context_incl.hpp" #include "backend_ib.hpp" #include "queue_pair.hpp" namespace rocshmem { __host__ GPUIBContext::GPUIBContext(Backend *backend, bool option, int idx) : Context(backend, option) { GPUIBBackend *b{static_cast(backend)}; ctx_idx = idx; networkImpl = b->networkImpl; base_heap = b->heap.get_heap_bases().data(); networkImpl.networkHostInit(this, idx); barrier_sync = b->barrier_sync; ipcImpl_.ipc_bases = b->ipcImpl.ipc_bases; ipcImpl_.shm_size = b->ipcImpl.shm_size; } __device__ void GPUIBContext::ctx_create() { /* Nothing to do in the GPU_IB backend */ return; } /* * TODO(bpotter): these will go in a policy class based on DC/RC. * I am not completely sure at this point what else is needed in said class, * so just leave them up here for now. */ __device__ __host__ QueuePair *GPUIBContext::getQueuePair(int pe) { return networkImpl.getQueuePair(device_qp_proxy, pe); } __device__ __host__ int GPUIBContext::getNumQueuePairs() { return networkImpl.getNumQueuePairs(); } __device__ __host__ int GPUIBContext::getNumDest() { return networkImpl.getNumDest(); } __device__ void GPUIBContext::fence() { #ifdef USE_SINGLE_NODE threadfence_system(); #else for (int k = 0; k < getNumDest(); k++) { getQueuePair(k)->fence(k); } fence_.flush(); #endif } __device__ void GPUIBContext::fence(int pe) { #ifdef USE_SINGLE_NODE threadfence_system(); #else getQueuePair(pe)->fence(pe); fence_.flush(); #endif } __device__ void GPUIBContext::putmem_nbi(void *dest, const void *source, size_t nelems, int pe) { uint64_t L_offset = reinterpret_cast(dest) - base_heap[my_pe]; if (ipcImpl_.isIpcAvailable(my_pe, pe)) { int local_pe = pe % ipcImpl_.shm_size; ipcImpl_.ipcCopy(ipcImpl_.ipc_bases[local_pe] + L_offset, const_cast(source), nelems); } else { bool must_send_message = wf_coal_.coalesce(pe, source, dest, &nelems); if (!must_send_message) { return; } auto *qp = getQueuePair(pe); qp->put_nbi(base_heap[pe] + L_offset, source, nelems, pe, true); } } __device__ void GPUIBContext::getmem_nbi(void *dest, const void *source, size_t nelems, int pe) { const char *src_typed = reinterpret_cast(source); uint64_t L_offset = const_cast(src_typed) - base_heap[my_pe]; if (ipcImpl_.isIpcAvailable(my_pe, pe)) { int local_pe = pe % ipcImpl_.shm_size; ipcImpl_.ipcCopy(dest, ipcImpl_.ipc_bases[local_pe] + L_offset, nelems); } else { bool must_send_message = wf_coal_.coalesce(pe, source, dest, &nelems); if (!must_send_message) { return; } auto *qp = getQueuePair(pe); qp->get_nbi(base_heap[pe] + L_offset, dest, nelems, pe, true); } } __device__ void GPUIBContext::quiet() { #ifdef USE_SINGLE_NODE threadfence_system(); for (int pe = 0; pe < ipcImpl_.shm_size; pe++) { if (pe != my_pe) { ipcImpl_.zero_byte_read(pe); } } #else for (int k = 0; k < getNumDest(); k++) { getQueuePair(k)->quiet_single_heavy(k); } fence_.flush(); #endif } __device__ void *GPUIBContext::shmem_ptr(const void *dest, int pe) { void *ret = nullptr; if (ipcImpl_.isIpcAvailable(my_pe, pe)) { void *dst = const_cast(dest); uint64_t L_offset = reinterpret_cast(dst) - base_heap[my_pe]; int local_pe = pe % ipcImpl_.shm_size; ret = ipcImpl_.ipc_bases[local_pe] + L_offset; } return ret; } __device__ void GPUIBContext::threadfence_system() { int thread_id = get_flat_block_id(); if (thread_id % WF_SIZE == lowerID()) { #ifdef USE_SINGLE_NODE // Flush current PE HDP HdpPolicy::hdp_flush( reinterpret_cast(networkImpl.hdp_address)); // Flush the rest of the HDPs for (int pe = 0; pe < ipcImpl_.shm_size; pe++) { auto target_address = networkImpl.hdp_address; const int value = HdpPolicy::HDP_FLUSH_VAL; if (pe != my_pe) { const int value = HdpPolicy::HDP_FLUSH_VAL; auto mapped_address = shmem_ptr(reinterpret_cast(target_address), pe); __hip_atomic_store(static_cast(mapped_address), value, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_SYSTEM); } } #else getQueuePair(my_pe)->hdp_policy->flushCoherency(); #endif } __threadfence_system(); } __device__ void GPUIBContext::getmem(void *dest, const void *source, size_t nelems, int pe) { const char *src_typed = reinterpret_cast(source); uint64_t L_offset = const_cast(src_typed) - base_heap[my_pe]; if (ipcImpl_.isIpcAvailable(my_pe, pe)) { int local_pe = pe % ipcImpl_.shm_size; ipcImpl_.ipcCopy(dest, ipcImpl_.ipc_bases[local_pe] + L_offset, nelems); } else { bool must_send_message = wf_coal_.coalesce(pe, source, dest, &nelems); if (!must_send_message) { return; } auto *qp = getQueuePair(pe); qp->get_nbi_cqe(base_heap[pe] + L_offset, dest, nelems, pe, true); qp->quiet_single(); } fence_.flush(); } __device__ void GPUIBContext::putmem(void *dest, const void *source, size_t nelems, int pe) { uint64_t L_offset = reinterpret_cast(dest) - base_heap[my_pe]; if (ipcImpl_.isIpcAvailable(my_pe, pe)) { int local_pe = pe % ipcImpl_.shm_size; ipcImpl_.ipcCopy(ipcImpl_.ipc_bases[local_pe] + L_offset, const_cast(source), nelems); threadfence_system(); ipcImpl_.zero_byte_read(pe); } else { bool must_send_message = wf_coal_.coalesce(pe, source, dest, &nelems); if (!must_send_message) { return; } auto *qp = getQueuePair(pe); qp->put_nbi_cqe(base_heap[pe] + L_offset, source, nelems, pe, true); qp->quiet_single(); } fence_.flush(); } /****************************************************************************** ************************ WORKGROUP/WAVE-LEVEL RMA API ************************ *****************************************************************************/ __device__ void GPUIBContext::putmem_nbi_wg(void *dest, const void *source, size_t nelems, int pe) { uint64_t L_offset = reinterpret_cast(dest) - base_heap[my_pe]; if (ipcImpl_.isIpcAvailable(my_pe, pe)) { int local_pe = pe % ipcImpl_.shm_size; ipcImpl_.ipcCopy_wg(ipcImpl_.ipc_bases[local_pe] + L_offset, const_cast(source), nelems); } else { if (is_thread_zero_in_block()) { auto *qp = getQueuePair(pe); qp->put_nbi(base_heap[pe] + L_offset, source, nelems, pe, true); } } __syncthreads(); } __device__ void GPUIBContext::putmem_nbi_wave(void *dest, const void *source, size_t nelems, int pe) { uint64_t L_offset = reinterpret_cast(dest) - base_heap[my_pe]; if (ipcImpl_.isIpcAvailable(my_pe, pe)) { int local_pe = pe % ipcImpl_.shm_size; ipcImpl_.ipcCopy_wave(ipcImpl_.ipc_bases[local_pe] + L_offset, const_cast(source), nelems); } else { if (is_thread_zero_in_wave()) { auto *qp = getQueuePair(pe); qp->put_nbi(base_heap[pe] + L_offset, source, nelems, pe, true); } } } __device__ void GPUIBContext::putmem_wg(void *dest, const void *source, size_t nelems, int pe) { uint64_t L_offset = reinterpret_cast(dest) - base_heap[my_pe]; if (ipcImpl_.isIpcAvailable(my_pe, pe)) { int local_pe = pe % ipcImpl_.shm_size; ipcImpl_.ipcCopy_wg(ipcImpl_.ipc_bases[local_pe] + L_offset, const_cast(source), nelems); __syncthreads(); threadfence_system(); ipcImpl_.zero_byte_read(pe); } else { auto *qp = getQueuePair(pe); if (is_thread_zero_in_block()) { qp->put_nbi_cqe(base_heap[pe] + L_offset, source, nelems, pe, true); } qp->quiet_single(); } __syncthreads(); fence_.flush(); } __device__ void GPUIBContext::putmem_wave(void *dest, const void *source, size_t nelems, int pe) { uint64_t L_offset = reinterpret_cast(dest) - base_heap[my_pe]; auto *qp = getQueuePair(pe); if (ipcImpl_.isIpcAvailable(my_pe, pe)) { int local_pe = pe % ipcImpl_.shm_size; ipcImpl_.ipcCopy_wave(ipcImpl_.ipc_bases[local_pe] + L_offset, const_cast(source), nelems); threadfence_system(); ipcImpl_.zero_byte_read(pe); } else { if (is_thread_zero_in_wave()) { qp->put_nbi_cqe(base_heap[pe] + L_offset, source, nelems, pe, true); } qp->quiet_single(); } fence_.flush(); } __device__ void GPUIBContext::getmem_wg(void *dest, const void *source, size_t nelems, int pe) { const char *src_typed = reinterpret_cast(source); uint64_t L_offset = const_cast(src_typed) - base_heap[my_pe]; auto *qp = getQueuePair(pe); if (ipcImpl_.isIpcAvailable(my_pe, pe)) { int local_pe = pe % ipcImpl_.shm_size; ipcImpl_.ipcCopy_wg(dest, ipcImpl_.ipc_bases[local_pe] + L_offset, nelems); } else { if (is_thread_zero_in_block()) { qp->get_nbi_cqe(base_heap[pe] + L_offset, dest, nelems, pe, true); } qp->quiet_single(); } __syncthreads(); fence_.flush(); } __device__ void GPUIBContext::getmem_wave(void *dest, const void *source, size_t nelems, int pe) { const char *src_typed = reinterpret_cast(source); uint64_t L_offset = const_cast(src_typed) - base_heap[my_pe]; auto *qp = getQueuePair(pe); if (ipcImpl_.isIpcAvailable(my_pe, pe)) { int local_pe = pe % ipcImpl_.shm_size; ipcImpl_.ipcCopy_wave(dest, ipcImpl_.ipc_bases[local_pe] + L_offset, nelems); } else { if (is_thread_zero_in_wave()) { qp->get_nbi_cqe(base_heap[pe] + L_offset, dest, nelems, pe, true); } qp->quiet_single(); } fence_.flush(); } __device__ void GPUIBContext::getmem_nbi_wg(void *dest, const void *source, size_t nelems, int pe) { const char *src_typed = reinterpret_cast(source); uint64_t L_offset = const_cast(src_typed) - base_heap[my_pe]; if (ipcImpl_.isIpcAvailable(my_pe, pe)) { int local_pe = pe % ipcImpl_.shm_size; ipcImpl_.ipcCopy_wg(dest, ipcImpl_.ipc_bases[local_pe] + L_offset, nelems); } else { if (is_thread_zero_in_block()) { auto *qp = getQueuePair(pe); qp->get_nbi(base_heap[pe] + L_offset, dest, nelems, pe, true); } } __syncthreads(); } __device__ void GPUIBContext::getmem_nbi_wave(void *dest, const void *source, size_t nelems, int pe) { const char *src_typed = reinterpret_cast(source); uint64_t L_offset = const_cast(src_typed) - base_heap[my_pe]; if (ipcImpl_.isIpcAvailable(my_pe, pe)) { int local_pe = pe % ipcImpl_.shm_size; ipcImpl_.ipcCopy_wave(dest, ipcImpl_.ipc_bases[local_pe] + L_offset, nelems); } else { if (is_thread_zero_in_wave()) { auto *qp = getQueuePair(pe); qp->get_nbi(base_heap[pe] + L_offset, dest, nelems, pe, true); } } } } // namespace rocshmem