From e9031c4c46ac0daabcf4b737e14b334ae55a327b Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Mon, 8 Apr 2024 16:08:51 +0100 Subject: [PATCH] hal remote library This adds support for generic client and server hal support to allow sending commands as binary requests over a `hal_transmitter`. Added encoders and decoders for converting hal commands to binary format Add hal_server base class which uses the encoders and decoders and a hal_transmitter to receive hal commands remotely and act on the given hal device. Add a hal_client and hal_device_client base class which can be used with a transmitter to send to a remote server binary commands. Added a hal socket cpu client. Added hal cpu socket based server program. --- CMakeLists.txt | 1 + examples/CMakeLists.txt | 20 + examples/hal_cpu_remote_server/CMakeLists.txt | 38 ++ .../hal_cpu_executable.cpp | 60 ++ examples/hals/hal_cpu_client/CMakeLists.txt | 21 + .../hals/hal_cpu_client/source/CMakeLists.txt | 41 ++ .../source/hal_cpu_executable.cpp | 52 ++ .../hals/hal_cpu_client/source/hal_main.cpp | 61 ++ hal/hal_remote/CMakeLists.txt | 8 + .../include/hal_remote/hal_binary_decoder.h | 388 +++++++++++++ .../include/hal_remote/hal_binary_encoder.h | 351 +++++++++++ .../include/hal_remote/hal_client.h | 115 ++++ .../include/hal_remote/hal_device_client.h | 99 ++++ .../include/hal_remote/hal_server.h | 93 +++ .../include/hal_remote/hal_socket_client.h | 51 ++ hal/hal_remote/source/hal_device_client.cpp | 255 ++++++++ hal/hal_remote/source/hal_server.cpp | 545 ++++++++++++++++++ .../source/hal_socket_transmitter.cpp | 12 - hal/source/hal_library.cpp | 26 +- .../cpu_client_riscv.json | 9 + 20 files changed, 2222 insertions(+), 24 deletions(-) create mode 100644 examples/CMakeLists.txt create mode 100644 examples/hal_cpu_remote_server/CMakeLists.txt create mode 100644 examples/hal_cpu_remote_server/hal_cpu_executable.cpp create mode 100644 examples/hals/hal_cpu_client/CMakeLists.txt create mode 100644 examples/hals/hal_cpu_client/source/CMakeLists.txt create mode 100644 examples/hals/hal_cpu_client/source/hal_cpu_executable.cpp create mode 100644 examples/hals/hal_cpu_client/source/hal_main.cpp create mode 100644 hal/hal_remote/include/hal_remote/hal_binary_decoder.h create mode 100644 hal/hal_remote/include/hal_remote/hal_binary_encoder.h create mode 100644 hal/hal_remote/include/hal_remote/hal_client.h create mode 100644 hal/hal_remote/include/hal_remote/hal_device_client.h create mode 100644 hal/hal_remote/include/hal_remote/hal_server.h create mode 100644 hal/hal_remote/include/hal_remote/hal_socket_client.h create mode 100644 hal/hal_remote/source/hal_device_client.cpp create mode 100644 hal/hal_remote/source/hal_server.cpp create mode 100644 scripts/new_target_templates/cpu_client_riscv.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 737a5a7a1..294916875 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -295,6 +295,7 @@ add_subdirectory(source) if(CA_ENABLE_DOCUMENTATION) add_subdirectory(doc) endif() +add_subdirectory(examples) # Add the deferred add_ca_extension_subdirectory() entries until all # dependencies are available for use in the extension's CMake. diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 000000000..524fd59be --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) Codeplay Software Limited +# +# Licensed under the Apache License, Version 2.0 (the "License") with LLVM +# Exceptions; you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + # Only builds on linux at the moment + add_subdirectory(hal_cpu_remote_server) +endif() diff --git a/examples/hal_cpu_remote_server/CMakeLists.txt b/examples/hal_cpu_remote_server/CMakeLists.txt new file mode 100644 index 000000000..2df78c264 --- /dev/null +++ b/examples/hal_cpu_remote_server/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (C) Codeplay Software Limited +# +# Licensed under the Apache License, Version 2.0 (the "License") with LLVM +# Exceptions; you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set(HAL_CPU_REMOTE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../clik/external/hal_cpu) + +# Add the files in directly, rather than drag in as a shared library or take +# all the extra clik code +add_executable(hal_cpu_server_bin + hal_cpu_executable.cpp + ${HAL_CPU_REMOTE_SOURCE_DIR}/source/cpu_hal.cpp + ${HAL_CPU_REMOTE_SOURCE_DIR}/source/hal_main.cpp +) + +target_compile_definitions(hal_cpu_server_bin PUBLIC -DHAL_CPU_MODE=HAL_CPU_WG_MODE) +target_include_directories(hal_cpu_server_bin PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ${HAL_CPU_REMOTE_SOURCE_DIR}/include +) + +target_link_libraries(hal_cpu_server_bin hal_remote hal_common dl pthread) + + +install(TARGETS hal_cpu_server_bin + LIBRARY DESTINATION bin +) diff --git a/examples/hal_cpu_remote_server/hal_cpu_executable.cpp b/examples/hal_cpu_remote_server/hal_cpu_executable.cpp new file mode 100644 index 000000000..48ab65404 --- /dev/null +++ b/examples/hal_cpu_remote_server/hal_cpu_executable.cpp @@ -0,0 +1,60 @@ +// Copyright (C) Codeplay Software Limited +// +// Licensed under the Apache License, Version 2.0 (the "License") with LLVM +// Exceptions; you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include + +#include "hal_remote/hal_server.h" +#include "hal_remote/hal_socket_client.h" +#include "hal_remote/hal_socket_transmitter.h" + +int main(int argc, char **argv) { + uint32_t api_version; + auto *hal = get_hal(api_version); + uint16_t port = 0; + std::string node = "127.0.0.1"; + if (const char *env = getenv("HAL_REMOTE_PORT")) { + port = std::stoi(env); + } + + if (const char *env = getenv("HAL_REMOTE_NODE")) { + node = env; + } + + hal::hal_socket_transmitter transmitter(node.c_str(), port); + hal::hal_server::error_code last_error = hal::hal_server::status_success; + auto res = transmitter.start_server(true); + if (res != hal::hal_socket_transmitter::success) { + (void)fprintf(stderr, "Unable to start server on requested port %d\n", + port); + return 1; + } + + hal::hal_server server(&transmitter, hal); + last_error = server.process_commands(); + // Check if the failure was due the transmitter, if so check if connection + // closed, otherwise we have a real error. + if (last_error == hal::hal_server::status_transmitter_failed) { + if (transmitter.get_last_error() != + hal::hal_socket_transmitter::connection_closed) { + (void)fprintf(stderr, "Error with tcp/ip connection\n"); + return 1; + } else { + return 0; + } + } + + return 1; +} diff --git a/examples/hals/hal_cpu_client/CMakeLists.txt b/examples/hals/hal_cpu_client/CMakeLists.txt new file mode 100644 index 000000000..af3cddbd4 --- /dev/null +++ b/examples/hals/hal_cpu_client/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (C) Codeplay Software Limited +# +# Licensed under the Apache License, Version 2.0 (the "License") with LLVM +# Exceptions; you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set_property(GLOBAL APPEND PROPERTY KNOWN_HAL_DEVICES "cpu_client") +set_property(GLOBAL PROPERTY HAL_CPU_CLIENT_NAME "cpu_remote") +set_property(GLOBAL PROPERTY HAL_CPU_CLIENT_DEVICE_NAME "ock cpu") + +add_subdirectory(source) diff --git a/examples/hals/hal_cpu_client/source/CMakeLists.txt b/examples/hals/hal_cpu_client/source/CMakeLists.txt new file mode 100644 index 000000000..3e00aba1a --- /dev/null +++ b/examples/hals/hal_cpu_client/source/CMakeLists.txt @@ -0,0 +1,41 @@ +# Copyright (C) Codeplay Software Limited +# +# Licensed under the Apache License, Version 2.0 (the "License") with LLVM +# Exceptions; you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set(HAL_CPU_REMOTE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../clik/external/hal_cpu) +add_library(hal_cpu_client SHARED + # Even though it is remote we need hal_cpu.cpp to get the info + ${HAL_CPU_REMOTE_SOURCE_DIR}/source/cpu_hal.cpp + hal_main.cpp +) + +set(HAL_CPU_WG_MODE ON) + +target_compile_definitions(hal_cpu_client PUBLIC -DBUILD_HAL_DLL) +target_compile_definitions(hal_cpu_client PUBLIC -DHAL_CPU_MODE=HAL_CPU_WG_MODE) + +target_include_directories(hal_cpu_client PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ${HAL_CPU_REMOTE_SOURCE_DIR}/include +) + +target_link_libraries(hal_cpu_client hal_remote dl pthread) + +set_target_properties(hal_cpu_client PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) + +install(TARGETS hal_cpu_client + LIBRARY DESTINATION lib +) diff --git a/examples/hals/hal_cpu_client/source/hal_cpu_executable.cpp b/examples/hals/hal_cpu_client/source/hal_cpu_executable.cpp new file mode 100644 index 000000000..a4a156e7d --- /dev/null +++ b/examples/hals/hal_cpu_client/source/hal_cpu_executable.cpp @@ -0,0 +1,52 @@ +// Copyright (C) Codeplay Software Limited +// +// Licensed under the Apache License, Version 2.0 (the "License") with LLVM +// Exceptions; you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +int main(int argc, char **argv) { + uint32_t api_version; + auto *hal = get_hal(api_version); + uint16_t port = 0; + if (const char *env = getenv("HAL_REMOTE_PORT")) { + port = atoi(env); + } + + hal::hal_socket_transmitter transmitter(port); + hal::hal_server::error_code last_error = hal::hal_server::status_success; + do { + auto res = transmitter.start_server(true); + if (res != hal::hal_socket_transmitter::success) { + printf("Unable to start server on requested port %d\n", port); + fflush(stdout); + exit(1); + } + + hal::hal_server server(&transmitter, hal); + last_error = server.process_commands(); + // Check if the failure was due the transmitter, if so check if connection + // closed, otherwise we have a real error. + if (last_error == hal::hal_server::status_transmitter_failed && + transmitter.get_last_error() != + hal::hal_socket_transmitter::connection_closed) { + fprintf(stderr, "Error with tcp/ip connection\n"); + exit(1); + } + + } while (last_error == hal::hal_server::status_transmitter_failed); +} \ No newline at end of file diff --git a/examples/hals/hal_cpu_client/source/hal_main.cpp b/examples/hals/hal_cpu_client/source/hal_main.cpp new file mode 100644 index 000000000..65513c9b4 --- /dev/null +++ b/examples/hals/hal_cpu_client/source/hal_main.cpp @@ -0,0 +1,61 @@ +// Copyright (C) Codeplay Software Limited +// +// Licensed under the Apache License, Version 2.0 (the "License") with LLVM +// Exceptions; you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include + +namespace hal { +class hal_cpu_socket_client : public hal::hal_socket_client { + public: + hal_cpu_socket_client() : hal_socket_client(0) { + constexpr static uint32_t implemented_api_version = 6; + static_assert( + implemented_api_version == hal_t::api_version, + "Implemented API version for hal_socket_client does not match hal.h"); + hal_device_info = cpu_hal::setup_cpu_hal_device_info(); + hal_info.platform_name = hal_device_info.target_name; + hal_info.num_devices = 1; + hal_info.api_version = implemented_api_version; + uint32_t port = 0; + if (const char *env = getenv("HAL_REMOTE_PORT")) { + port = atoi(env); + } + set_port(port); + } + + // return generic platform information + const hal::hal_info_t &get_info() override { + std::lock_guard locker(lock); + return hal_info; + } + + // return generic target information + const hal::hal_device_info_t *device_get_info(uint32_t index) override { + std::lock_guard locker(lock); + return &hal_device_info; + } + + private: + hal::hal_info_t hal_info; + hal::hal_device_info_t hal_device_info; +}; +} // namespace hal +static hal::hal_cpu_socket_client hal_object; + +hal::hal_t *get_hal(uint32_t &api_version) { + api_version = hal_object.get_info().api_version; + return &hal_object; +} diff --git a/hal/hal_remote/CMakeLists.txt b/hal/hal_remote/CMakeLists.txt index b8c566b97..2820e32ed 100644 --- a/hal/hal_remote/CMakeLists.txt +++ b/hal/hal_remote/CMakeLists.txt @@ -15,10 +15,18 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception set(HAL_SOURCE + ${CMAKE_CURRENT_SOURCE_DIR}/include/hal_remote/hal_binary_encoder.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/hal_remote/hal_binary_decoder.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/hal_remote/hal_client.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/hal_remote/hal_device_client.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/hal_remote/hal_server.h ${CMAKE_CURRENT_SOURCE_DIR}/include/hal_remote/hal_transmitter.h + ${CMAKE_CURRENT_SOURCE_DIR}/source/hal_device_client.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/source/hal_server.cpp ) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + list (APPEND HAL_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/include/hal_remote/hal_socket_client.h) list (APPEND HAL_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/include/hal_remote/hal_socket_transmitter.h) list (APPEND HAL_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/source/hal_socket_transmitter.cpp) endif() diff --git a/hal/hal_remote/include/hal_remote/hal_binary_decoder.h b/hal/hal_remote/include/hal_remote/hal_binary_decoder.h new file mode 100644 index 000000000..49bdcfa5f --- /dev/null +++ b/hal/hal_remote/include/hal_remote/hal_binary_decoder.h @@ -0,0 +1,388 @@ +// Copyright (C) Codeplay Software Limited +// +// Licensed under the Apache License, Version 2.0 (the "License") with LLVM +// Exceptions; you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef _HAL_BINARY_DECODER_H +#define _HAL_BINARY_DECODER_H + +#include +#include +#include + +namespace hal { + +/// @brief Decoder for hal binary commands +/// @note This decodes a command and can return information about the number of +/// bytes needed or decode it into internal structure `message` which contains +/// the information that can be used to call hal functions. +class hal_binary_decoder { + public: + hal_binary_decoder() {} + + static const uint32_t data_required_unknown = 0xffffffff; + + /// @brief get the number of additional bytes needed for a command + /// @param command enum representing a command + /// @return number of bytes needed or data_required_unknown (0xffffffff) if + /// unknown command + uint32_t decode_command_data_required(hal_binary_encoder::COMMAND command) { + switch (command) { + case hal_binary_encoder::COMMAND::MEM_ALLOC: + return sizeof(message.alloc.size) + sizeof(message.alloc.alignment); + case hal_binary_encoder::COMMAND::MEM_FREE: + return sizeof(message.free.addr); + case hal_binary_encoder::COMMAND::MEM_WRITE: + return sizeof(message.write.dst) + sizeof(message.write.size); + case hal_binary_encoder::COMMAND::MEM_READ: + return sizeof(message.read.src) + sizeof(message.read.size); + case hal_binary_encoder::COMMAND::MEM_FILL: + return sizeof(message.fill.dst) + sizeof(message.fill.pattern_size) + + sizeof(message.fill.size); + case hal_binary_encoder::COMMAND::MEM_COPY: + return sizeof(message.copy.dst) + sizeof(message.read.src) + + sizeof(message.read.size); + case hal_binary_encoder::COMMAND::PROGRAM_FREE: + return sizeof(message.prog_free.program); + case hal_binary_encoder::COMMAND::PROGRAM_LOAD: + return sizeof(message.prog_load.size); + case hal_binary_encoder::COMMAND::FIND_KERNEL: + return sizeof(message.prog_find_kernel.kernel_name_size) + + sizeof(message.prog_find_kernel.program); + case hal_binary_encoder::COMMAND::MEM_ALLOC_REPLY: + return sizeof(message.alloc_reply); + case hal_binary_encoder::COMMAND::PROGRAM_LOAD_REPLY: + return sizeof(message.prog_load_reply); + case hal_binary_encoder::COMMAND::KERNEL_EXEC: + return sizeof(message.kernel_exec.program) + + // 8 + sizeof(message.kernel_exec.kernel) + + // 16 + sizeof(message.kernel_exec.num_args) + + // 20 + sizeof(message.kernel_exec.nd_range.offset[0]) * 9 + + // 56 + + sizeof(message.kernel_exec.work_dim) + + sizeof(message.kernel_exec.args_data_size); + // 64 + case hal_binary_encoder::COMMAND::DEVICE_CREATE: + case hal_binary_encoder::COMMAND::DEVICE_DELETE: + return 0; + case hal_binary_encoder::COMMAND::FIND_KERNEL_REPLY: + return sizeof(message.find_kernel_reply); + case hal_binary_encoder::COMMAND::MEM_READ_REPLY: + case hal_binary_encoder::COMMAND::MEM_WRITE_REPLY: + case hal_binary_encoder::COMMAND::MEM_FILL_REPLY: + case hal_binary_encoder::COMMAND::MEM_FREE_REPLY: + case hal_binary_encoder::COMMAND::MEM_COPY_REPLY: + case hal_binary_encoder::COMMAND::PROGRAM_FREE_REPLY: + case hal_binary_encoder::COMMAND::KERNEL_EXEC_REPLY: + case hal_binary_encoder::COMMAND::DEVICE_CREATE_REPLY: + case hal_binary_encoder::COMMAND::DEVICE_DELETE_REPLY: + return sizeof(uint32_t); // bool as 32 bit + break; + default: + printf("Unknown command : %d\n", (int)command); + assert(0 && "Command cannot be decoded"); + } + return data_required_unknown; + } + + /// @brief decode a command given data of a particular sise + /// @param command + /// @param data + /// @param size length of data in bytes + /// @return true if succeeded otherwise false. + bool decode(hal_binary_encoder::COMMAND command, const uint8_t *data, + uint32_t size) { + uint32_t offset = 0; + bool ok = true; + // This assumes both the command and device have been read. + switch (command) { + case hal_binary_encoder::COMMAND::MEM_ALLOC: { + ok = ok && pull(message.alloc.size, data, offset, size); + ok = ok && pull(message.alloc.alignment, data, offset, size); + break; + } + + case hal_binary_encoder::COMMAND::MEM_ALLOC_REPLY: { + ok = ok && pull(message.alloc_reply, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::MEM_FREE: { + ok = ok && pull(message.free.addr, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::MEM_FREE_REPLY: { + ok = ok && pull(message.free_reply, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::MEM_WRITE: { + ok = ok && pull(message.write.dst, data, offset, size); + ok = ok && pull(message.write.size, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::MEM_WRITE_REPLY: { + ok = ok && pull(message.write_reply, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::MEM_READ: { + ok = ok && pull(message.read.src, data, offset, size); + ok = ok && pull(message.read.size, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::MEM_READ_REPLY: { + ok = ok && pull(message.read_reply, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::MEM_COPY: { + ok = ok && pull(message.copy.dst, data, offset, size); + ok = ok && pull(message.copy.src, data, offset, size); + ok = ok && pull(message.copy.size, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::MEM_COPY_REPLY: { + ok = ok && pull(message.copy_reply, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::PROGRAM_FREE: { + ok = ok && pull(message.prog_free.program, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::PROGRAM_FREE_REPLY: { + ok = ok && pull(message.prog_free_reply, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::PROGRAM_LOAD: { + ok = ok && pull(message.prog_load.size, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::PROGRAM_LOAD_REPLY: { + ok = ok && pull(message.prog_load_reply, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::FIND_KERNEL: { + ok = ok && pull(message.prog_find_kernel.program, data, offset, size); + ok = ok && pull(message.prog_find_kernel.kernel_name_size, data, offset, + size); + break; + } + case hal_binary_encoder::COMMAND::FIND_KERNEL_REPLY: { + ok = ok && pull(message.find_kernel_reply, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::KERNEL_EXEC: { + ok = ok && pull(message.kernel_exec.program, data, offset, size); + ok = ok && pull(message.kernel_exec.kernel, data, offset, size); + ok = ok && pull(message.kernel_exec.num_args, data, offset, size); + for (uint32_t d = 0; d < 3; d++) { + ok = ok && + pull(message.kernel_exec.nd_range.offset[d], data, offset, size); + } + for (uint32_t d = 0; d < 3; d++) { + ok = ok && + pull(message.kernel_exec.nd_range.global[d], data, offset, size); + } + for (uint32_t d = 0; d < 3; d++) { + ok = ok && + pull(message.kernel_exec.nd_range.local[d], data, offset, size); + } + ok = ok && pull(message.kernel_exec.work_dim, data, offset, size); + ok = ok && pull(message.kernel_exec.args_data_size, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::KERNEL_EXEC_REPLY: { + ok = ok && pull(message.kernel_exec_reply, data, offset, size); + break; + } + // device index is implicit in all commands + case hal_binary_encoder::COMMAND::DEVICE_CREATE: + case hal_binary_encoder::COMMAND::DEVICE_DELETE: + break; + case hal_binary_encoder::COMMAND::DEVICE_CREATE_REPLY: { + ok = ok && pull(message.device_create_reply, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::DEVICE_DELETE_REPLY: { + ok = ok && pull(message.device_delete_reply, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::MEM_FILL: { + ok = ok && pull(message.fill.dst, data, offset, size); + ok = ok && pull(message.fill.pattern_size, data, offset, size); + ok = ok && pull(message.fill.size, data, offset, size); + break; + } + case hal_binary_encoder::COMMAND::MEM_FILL_REPLY: { + ok = ok && pull(message.mem_fill_reply, data, offset, size); + break; + } + default: + assert(0 && "Unable to decode message"); + return false; + } + assert(offset <= size); + return ok; + } + + bool decode_kernel_exec_args(uint8_t *data, uint32_t num_args, + uint32_t size) { + uint32_t offset = 0; + kernel_args.clear(); + bool ok = true; + for (uint32_t a = 0; a < num_args; a++) { + hal::hal_arg_t arg; + uint32_t value; + ok = ok && pull(value, data, offset, size); + if (ok) { + arg.kind = static_cast(value); + } + ok = ok && pull(value, data, offset, size); + if (ok) { + arg.space = static_cast(value); + } + ok = ok && pull(arg.size, data, offset, size); + if (ok) { + if (arg.kind == hal::hal_arg_kind_t::hal_arg_address) { + ok = ok && pull(arg.address, data, offset, size); + } else { + // point the pod_data to the allocated data. + arg.pod_data = data + offset; + offset += arg.size; + } + } + if (!ok) { + break; + } + kernel_args.push_back(arg); + } + return ok; + } + + enum hal_binary_encoder::COMMAND command; + struct MemAlloc { + hal::hal_size_t size; + hal::hal_addr_t alignment; + }; + struct MemWrite { + hal::hal_addr_t dst; + hal::hal_size_t size; + // We don't encode the actual data here which is expected to follow + }; + struct MemRead { + hal::hal_addr_t src; + hal::hal_size_t size; + }; + struct MemFill { + hal::hal_addr_t dst; + hal::hal_size_t pattern_size; + hal::hal_size_t size; + // We don't encode the actual pattern data here which is expected to follow + }; + struct MemFree { + hal::hal_addr_t addr; + }; + struct MemCopy { + hal::hal_addr_t dst; + hal::hal_addr_t src; + hal::hal_size_t size; + }; + struct ProgramFree { + hal::hal_program_t program; + }; + struct ProgramLoad { + hal::hal_size_t size; + // data to follow + }; + struct ProgramFindKernel { + hal::hal_program_t program; + hal::hal_size_t kernel_name_size; + }; + struct KernelExec { + hal::hal_program_t program; + hal::hal_kernel_t kernel; + hal::hal_ndrange_t nd_range; + uint32_t num_args; + uint32_t work_dim; + uint32_t args_data_size; + // actual args passed separately + }; + struct DeviceCreate { + uint32_t size; + // data to follow + }; + union { + MemAlloc alloc; + MemWrite write; + MemRead read; + MemFill fill; + MemFree free; + MemCopy copy; + ProgramFree prog_free; + ProgramLoad prog_load; + ProgramFindKernel prog_find_kernel; + KernelExec kernel_exec; + hal::hal_addr_t alloc_reply; + bool fill_reply; + bool write_reply; + bool read_reply; + bool free_reply; + bool copy_reply; + bool prog_free_reply; + bool kernel_exec_reply; + bool device_create_reply; + bool device_delete_reply; + bool mem_fill_reply; + hal::hal_program_t prog_load_reply; + hal::hal_kernel_t find_kernel_reply; + } message; + std::vector kernel_args; + + private: + bool pull(uint64_t &value, const uint8_t *data, uint32_t &offset, + uint32_t size) { + const uint32_t size_required = sizeof(uint64_t); + if (offset + size_required > size) { + return false; + } + std::memcpy(&value, data + offset, size_required); + offset += size_required; + return true; + } + bool pull(uint32_t &value, const uint8_t *data, uint32_t &offset, + uint32_t size) { + const uint32_t size_required = sizeof(uint32_t); + if (offset + size_required > size) { + return false; + } + std::memcpy(&value, data + offset, sizeof(uint32_t)); + offset += sizeof(uint32_t); + return true; + } + bool pull(bool &value, const uint8_t *data, uint32_t &offset, uint32_t size) { + const uint32_t size_required = sizeof(uint32_t); + uint32_t val32 = 0; + if (offset + size_required > size) { + return false; + } + std::memcpy(&val32, data + offset, sizeof(uint32_t)); + value = static_cast(val32); + offset += sizeof(uint32_t); + return true; + } +}; +} // namespace hal + +#endif diff --git a/hal/hal_remote/include/hal_remote/hal_binary_encoder.h b/hal/hal_remote/include/hal_remote/hal_binary_encoder.h new file mode 100644 index 000000000..d64707598 --- /dev/null +++ b/hal/hal_remote/include/hal_remote/hal_binary_encoder.h @@ -0,0 +1,351 @@ +// Copyright (C) Codeplay Software Limited +// +// Licensed under the Apache License, Version 2.0 (the "License") with LLVM +// Exceptions; you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef _HAL_BINARY_ENCODER_H +#define _HAL_BINARY_ENCODER_H + +#include + +#include +#include + +namespace hal { + +/// @brief A simple encoder class for converting hal related commands into a +/// binary format. Note this does not encrypt in any way; if this is required +/// this should be done on top of this. +/// @note Unlike the decoder this also encodes the command and a device id for +/// the non-reply commands. +class hal_binary_encoder { + public: + enum class COMMAND : uint32_t { + UNKNOWN, // To aid debugging, force a non-zero command for real commands + MEM_ALLOC = 1, + MEM_ALLOC_REPLY = 2, + MEM_WRITE = 3, + MEM_WRITE_REPLY = 4, + MEM_READ = 5, + MEM_READ_REPLY = 6, + MEM_FILL = 7, + MEM_FILL_REPLY = 8, + MEM_FREE = 9, + MEM_FREE_REPLY = 10, + MEM_COPY = 11, + MEM_COPY_REPLY = 12, + PROGRAM_FREE = 13, + PROGRAM_FREE_REPLY = 14, + KERNEL_EXEC = 15, + KERNEL_EXEC_REPLY = 16, + PROGRAM_LOAD = 17, + PROGRAM_LOAD_REPLY = 18, + FIND_KERNEL = 19, + FIND_KERNEL_REPLY = 20, + DEVICE_CREATE = 21, + DEVICE_CREATE_REPLY = 22, + DEVICE_DELETE = 23, + DEVICE_DELETE_REPLY = 24 + }; + + hal_binary_encoder(uint32_t device = 0) { device = device; } + + /// @brief encode mem alloc on a device + /// @param size + /// @param alignment + void encode_mem_alloc(hal::hal_size_t size, hal::hal_size_t alignment) { + push(COMMAND::MEM_ALLOC); + push(device); + push(size); + push(alignment); + } + + /// @brief Encode mem alloc address as a reply + /// @param reply + void encode_mem_alloc_reply(hal::hal_addr_t reply) { + push(COMMAND::MEM_ALLOC_REPLY); + push(reply); + } + + /// @brief encode device mem free + /// @param addr + void encode_mem_free(hal::hal_addr_t addr) { + push(COMMAND::MEM_FREE); + push(device); + push(addr); + } + + /// @brief encode mem free reply of true/false + /// @param reply + void encode_mem_free_reply(bool reply) { + push(COMMAND::MEM_FREE_REPLY); + push(reply); + } + + /// @brief encode device mem write + /// @param size + /// @param alignment + void encode_mem_write(hal::hal_addr_t dst, hal::hal_size_t size) { + push(COMMAND::MEM_WRITE); + push(device); + push(dst); + push(size); + } + + /// @brief Encode mem write reply of true/false + /// @param reply + void encode_mem_write_reply(bool reply) { + push(COMMAND::MEM_WRITE_REPLY); + push(reply); + } + + /// @brief Encode device mem/fill + /// @param dst + /// @param pattern_size + /// @param size + /// @note that we need to encode the pattern data separately, which should + /// be pushed as a `size` bytes + void encode_mem_fill(hal::hal_addr_t dst, hal::hal_size_t pattern_size, + hal::hal_size_t size) { + push(COMMAND::MEM_FILL); + push(device); + push(dst); + push(pattern_size); + push(size); + } + + /// @brief Encode mem fill reply as a boolean + /// @param reply + void encode_mem_fill_reply(bool reply) { + push(COMMAND::MEM_FILL_REPLY); + push(reply); + } + + /// @brief Encode device mem read + /// @param src + /// @param size + void encode_mem_read(hal::hal_addr_t src, hal::hal_size_t size) { + push(COMMAND::MEM_READ); + push(device); + push(src); + push(size); + } + + /// @brief Encode mem read reply as a bool + /// @param reply + void encode_mem_read_reply(bool reply) { + push(COMMAND::MEM_READ_REPLY); + push(reply); + } + + /// @brief Encode device mem copy + /// @param dst + /// @param src + /// @param size + void encode_mem_copy(hal::hal_addr_t dst, hal::hal_addr_t src, + hal::hal_size_t size) { + push(COMMAND::MEM_COPY); + push(device); + push(dst); + push(src); + push(size); + } + + /// @brief Encode mem copy reply as a bool + /// @param reply + void encode_mem_copy_reply(bool reply) { + push(COMMAND::MEM_COPY_REPLY); + push(reply); + } + + /// @brief Encode device find kernel, the name_length should be the length of + /// the string + 1 for the null terminator + /// @param program + /// @param name_length + /// @note The name string should follow this, including the null terminator + void encode_find_kernel(hal::hal_program_t program, + hal::hal_size_t name_length) { + push(COMMAND::FIND_KERNEL); + push(device); + push(program); + push(name_length); + } + + /// @brief Encode find kernel reply as a `hal::hal_kernel_t` + /// @param reply + void encode_find_kernel_reply(hal::hal_kernel_t reply) { + push(COMMAND::FIND_KERNEL_REPLY); + push(reply); + } + + /// @brief Encode device program load, `size` should be the length of the + /// executable + /// @param size + /// @note The executable should be sent immediately after this. Note this does + /// no encryption, it is up to the user to encrypt if needed. + void encode_program_load(hal::hal_size_t size) { + push(COMMAND::PROGRAM_LOAD); + push(device); + push(size); + } + + /// @brief Encode program load reply as a hal_program_t + /// @param reply + void encode_program_load_reply(hal::hal_program_t reply) { + push(COMMAND::PROGRAM_LOAD_REPLY); + push(reply); + } + + /// @brief Encode device program free + /// @param program + void encode_program_free(hal::hal_program_t program) { + push(COMMAND::PROGRAM_FREE); + push(device); + push(program); + } + + /// @brief encode program free reply as a boolean + /// @param reply + void encode_program_free_reply(bool reply) { + push(COMMAND::PROGRAM_FREE_REPLY); + push(reply); + } + + /// @brief Encode + /// @param program + /// @param kernel + /// @param nd_range + /// @param num_args + /// @param work_dim + void encode_kernel_exec(hal::hal_program_t program, hal::hal_kernel_t kernel, + const hal::hal_ndrange_t *nd_range, uint32_t num_args, + uint32_t work_dim) { + encoding.clear(); + push(COMMAND::KERNEL_EXEC); + push(device); + push(program); + push(kernel); + push(num_args); + for (uint32_t d = 0; d < 3; d++) { + push(nd_range->offset[d]); + } + for (uint32_t d = 0; d < 3; d++) { + push(nd_range->global[d]); + } + for (uint32_t d = 0; d < 3; d++) { + push(nd_range->local[d]); + } + push(work_dim); + } + void encode_kernel_exec_args(const hal::hal_arg_t *args, uint32_t num_args) { + /* + Encoding this information + hal::hal_arg_kind_t kind; + hal_addr_space_t space; + hal::hal_size_t size; + union { + hal::hal_addr_t address; + void *pod_data; + } + };*/ + uint32_t bytes_required = 0; + uint32_t pod_data_required = 0; + for (uint32_t a = 0; a < num_args; a++) { + auto &arg = args[a]; + // kind, space (32 bit) and size (64 bit) + bytes_required += sizeof(uint32_t) * 2 + sizeof(arg.size); + if (arg.kind == hal::hal_arg_kind_t::hal_arg_address) { + bytes_required += sizeof(arg.address); + } else { + pod_data_required += arg.size; + } + } + bytes_required += pod_data_required; + push(bytes_required); + + for (uint32_t a = 0; a < num_args; a++) { + auto &arg = args[a]; + push(static_cast(arg.kind)); + push(static_cast(arg.space)); + push(arg.size); + if (arg.kind == hal::hal_arg_kind_t::hal_arg_address) { + push(arg.address); + } else { + push(arg.pod_data, arg.size); + } + } + } + void encode_kernel_exec_reply(bool reply) { + push(COMMAND::KERNEL_EXEC_REPLY); + push(reply); + } + + void encode_device_create() { + push(COMMAND::DEVICE_CREATE); + push(device); + } + void encode_device_create_reply(bool success) { + push(COMMAND::DEVICE_CREATE_REPLY); + push(success); + } + void encode_device_delete() { + push(COMMAND::DEVICE_DELETE); + push(device); + } + void encode_device_delete_reply(bool success) { + push(COMMAND::DEVICE_DELETE_REPLY); + push(success); + } + + uint8_t *data() { return encoding.data(); } + uint32_t size() { return encoding.size(); } + + void clear() { encoding.clear(); } + + private: + void expand(uint32_t size) { encoding.resize(encoding.size() + size); } + void push(hal::hal_size_t size) { + const uint32_t offset = encoding.size(); + expand(sizeof(hal::hal_size_t)); + std::memcpy(encoding.data() + offset, &size, sizeof(hal::hal_size_t)); + } + + void push(uint32_t val) { + const uint32_t offset = encoding.size(); + expand(sizeof(val)); + std::memcpy(encoding.data() + offset, &val, sizeof(val)); + } + void push(bool val) { + const uint32_t val32 = val; + push(val32); + } + + void push(const void *data, uint32_t size) { + const uint32_t offset = encoding.size(); + expand(size); + std::memcpy(encoding.data() + offset, data, size); + } + + void push(enum COMMAND command) { + const uint32_t offset = encoding.size(); + expand(sizeof(command)); + std::memcpy(encoding.data() + offset, &command, sizeof(command)); + } + std::vector encoding; + uint32_t offset = 0; + uint32_t device = 0; +}; +} // namespace hal + +#endif diff --git a/hal/hal_remote/include/hal_remote/hal_client.h b/hal/hal_remote/include/hal_remote/hal_client.h new file mode 100644 index 000000000..60c70eb67 --- /dev/null +++ b/hal/hal_remote/include/hal_remote/hal_client.h @@ -0,0 +1,115 @@ +// Copyright (C) Codeplay Software Limited +// +// Licensed under the Apache License, Version 2.0 (the "License") with LLVM +// Exceptions; you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef _HAL_CLIENT_H +#define _HAL_CLIENT_H + +#include +#include +#include +#include + +#include + +namespace hal { + +/// @brief Base class for hal_client, is a type of `hal::hal_t`, which can be +/// used to communicate with a remote server and will create `hal_device_client` +/// when requested via requests to the server. +/// @note It currently has the limitation of only supporting a single device. +class hal_client : public hal::hal_t { + protected: + std::mutex lock; + bool made_connection = false; + hal::hal_info_t hal_info; + hal::hal_device_info_t hal_device_info; + uint16_t required_port; + + public: + hal_client() {} + virtual bool make_connection() = 0; + virtual hal::hal_transmitter &get_transmitter() = 0; + // request the creation of a new hal + hal::hal_device_t *device_create(uint32_t index) override { + const std::lock_guard locker(lock); + if (index > 0) { + return nullptr; + } + + if (!made_connection) { + if (!make_connection()) { + return nullptr; + } + made_connection = true; + } + + hal::hal_binary_encoder encoder(index); + hal::hal_binary_decoder decoder; + hal::hal_binary_encoder::COMMAND command; + encoder.encode_device_create(); + get_transmitter().send(encoder.data(), encoder.size(), true); + bool ok = get_transmitter().receive( + (uint8_t *)&command, sizeof(hal::hal_binary_encoder::COMMAND)); + if (command == hal::hal_binary_encoder::COMMAND::DEVICE_CREATE_REPLY) { + const uint32_t data_required = decoder.decode_command_data_required( + hal::hal_binary_encoder::COMMAND::DEVICE_CREATE_REPLY); + std::vector payload(data_required); + + ok = ok && get_transmitter().receive(payload.data(), data_required); + if (!ok) { + return nullptr; + } + if (decoder.decode(command, payload.data(), payload.size())) { + if (decoder.message.device_create_reply) { + return new hal::hal_device_client(&hal_device_info, lock, + &get_transmitter()); + } + } + } + return nullptr; + } + + // destroy a device instance + bool device_delete(hal::hal_device_t *device) override { + if (!made_connection) { + made_connection = true; + + if (make_connection()) { + return false; + } + } + + hal::hal_binary_encoder encoder(0); // Assume is first device + hal::hal_binary_decoder decoder; + hal::hal_binary_encoder::COMMAND command; + encoder.encode_device_delete(); // Assume is first device + get_transmitter().send(encoder.data(), encoder.size(), true); + bool ok = get_transmitter().receive( + (uint8_t *)&command, sizeof(hal::hal_binary_encoder::COMMAND)); + if (command == hal::hal_binary_encoder::COMMAND::DEVICE_DELETE_REPLY) { + const uint32_t data_required = decoder.decode_command_data_required( + hal::hal_binary_encoder::COMMAND::DEVICE_DELETE_REPLY); + std::vector payload(data_required); + ok = ok && get_transmitter().receive(payload.data(), data_required); + ok = ok && decoder.decode(command, payload.data(), payload.size()); + ok = ok && decoder.message.device_delete_reply; + } + delete static_cast(device); + return ok; + } +}; +} // namespace hal +#endif diff --git a/hal/hal_remote/include/hal_remote/hal_device_client.h b/hal/hal_remote/include/hal_remote/hal_device_client.h new file mode 100644 index 000000000..4bfaaf057 --- /dev/null +++ b/hal/hal_remote/include/hal_remote/hal_device_client.h @@ -0,0 +1,99 @@ +// Copyright (C) Codeplay Software Limited +// +// Licensed under the Apache License, Version 2.0 (the "License") with LLVM +// Exceptions; you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef _HAL_DEVICE_CLIENT_H +#define _HAL_DEVICE_CLIENT_H + +#include +#include +#include +#include + +#include + +namespace hal { + +/// @brief A hal device which will communicate with a remote server +/// to perform the hal device actions. +/// @note This will use a default encoding through the `hal_binary_encoder` +/// class but all functions are overridable so that a user may encode +/// in their own way. For example `kernel_exec` could encrypt the kernel. +/// +/// @note This uses a `hal_transmitter` to send and receive the information +/// This is a simple abstract class that could use different methods of +/// communication e.g. sockets, file descriptors etc. Also it should be noted +/// that this does not deal with device creation which should be done by the +/// `hal` and use the same transmitter. +class hal_device_client : public hal::hal_device_t { + public: + hal_device_client(hal::hal_device_info_t *info, std::mutex &hal_lock, + hal::hal_transmitter *transmitter); + + // @brief find program kernel based on `name` + hal::hal_kernel_t program_find_kernel(hal::hal_program_t program, + const char *name) override; + + // @brief load an ELF file into target memory + // returns `hal_invalid_program` if the program could not be loaded + hal::hal_program_t program_load(const void *data, + hal::hal_size_t size) override; + + // @brief execute a kernel on the target + bool kernel_exec(hal::hal_program_t program, hal::hal_kernel_t kernel, + const hal::hal_ndrange_t *nd_range, + const hal::hal_arg_t *args, uint32_t num_args, + uint32_t work_dim) override; + + // @brief unload a program from the target + bool program_free(hal::hal_program_t program) override; + + // @brief allocate a memory range on the target + // @return allocated address or `hal_nullptr` if the operation was + // unsuccessful + hal::hal_addr_t mem_alloc(hal::hal_size_t size, + hal::hal_size_t alignment) override; + + // @brief copy memory between target buffers + bool mem_copy(hal::hal_addr_t dst, hal::hal_addr_t src, + hal::hal_size_t size) override; + + // @brief free a memory range on the target + bool mem_free(hal::hal_addr_t addr) override; + + // @brief fill memory with a pattern + bool mem_fill(hal::hal_addr_t dst, const void *pattern, + hal::hal_size_t pattern_size, hal::hal_size_t size) override; + + // @brief read memory from the target to the host + bool mem_read(void *dst, hal::hal_addr_t src, hal::hal_size_t size) override; + + // @brief write host memory to the target + bool mem_write(hal::hal_addr_t dst, const void *src, + hal::hal_size_t size) override; + bool hal_debug() { return debug; } + + protected: + /// @brief A small helper function to check the reply and download the related + /// data into `decoder` + /// @return true if the receive and decode worked. + bool receive_decode_reply(hal_binary_encoder::COMMAND expected_command, + hal_binary_decoder &decoder); + hal::hal_transmitter *transmitter; + bool debug = false; + std::mutex &hal_lock; +}; +} // namespace hal +#endif diff --git a/hal/hal_remote/include/hal_remote/hal_server.h b/hal/hal_remote/include/hal_remote/hal_server.h new file mode 100644 index 000000000..1d5444903 --- /dev/null +++ b/hal/hal_remote/include/hal_remote/hal_server.h @@ -0,0 +1,93 @@ +// Copyright (C) Codeplay Software Limited +// +// Licensed under the Apache License, Version 2.0 (the "License") with LLVM +// Exceptions; you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef _HAL_SERVER_H +#define _HAL_SERVER_H + +#include +#include + +#include + +namespace hal { +class hal_t; +class hal_device_t; + +/// @brief A server for taking binary data and translating into actions +/// on a HAL and HAL devices +/// @note This uses an abstract class `hal_transmitter` which can be used to +/// read and write the binary data. This transmitter may use sockets for example +/// but other approaches are possible. It is designed to only talk to one end +/// point and it is expected that end point will only make a single request at a +/// time and wait for the result. All requests to the server take a 4 byte +/// command (which maps the hal_binary_encoder::COMMAND), and a 4 byte device +/// id. The actual command will dictate how many more bytes are required using +/// the hal_binary_decoder. +/// +/// The actual command will be processed will be done in process_command. +/// This will only process that first 8 bytes before calling one of the +/// process_* functions, which are expected to fetch the rest, all of which can +/// be overridden This takes a hal as an input, but is currently only able to +/// handle hals which support a single device. This could be remedied by storing +/// more than a single `hal_device` - and mapping indexes to devices and +/// vice-versa +/// +/// `process_commands()` provides a method of repeatedly calling +/// `process_command()` until an error condition happens. +/// + +class hal_server { + public: + enum error_code { + status_success, + status_transmitter_failed, + status_device_not_supported, + status_unknown_command, + status_decode_failed + }; + hal_server(hal::hal_transmitter *transmitter, hal::hal_t *hal); + ~hal_server(); + + virtual error_code process_commands(); + + protected: + virtual error_code process_command(); + virtual hal_server::error_code process_mem_alloc(uint32_t device); + virtual hal_server::error_code process_mem_free(uint32_t device); + virtual hal_server::error_code process_mem_write(uint32_t device); + virtual hal_server::error_code process_mem_read(uint32_t device); + virtual hal_server::error_code process_mem_fill(uint32_t device); + virtual hal_server::error_code process_mem_copy(uint32_t device); + virtual hal_server::error_code process_program_free(uint32_t device); + virtual hal_server::error_code process_find_kernel(uint32_t device); + virtual hal_server::error_code process_program_load(uint32_t device); + virtual hal_server::error_code process_kernel_exec(uint32_t device); + virtual hal_server::error_code process_device_create(uint32_t device); + virtual hal_server::error_code process_device_delete(uint32_t device); + + error_code receive_payload(hal_binary_encoder::COMMAND command, + std::vector &payload); + uint8_t *receive_data(uint32_t size); + + hal::hal_t *hal; + hal::hal_device_t *hal_device = nullptr; + hal_transmitter *transmitter; + std::vector payload; + bool debug = false; +}; +} // namespace hal + +#endif diff --git a/hal/hal_remote/include/hal_remote/hal_socket_client.h b/hal/hal_remote/include/hal_remote/hal_socket_client.h new file mode 100644 index 000000000..fbf08dd11 --- /dev/null +++ b/hal/hal_remote/include/hal_remote/hal_socket_client.h @@ -0,0 +1,51 @@ +// Copyright (C) Codeplay Software Limited +// +// Licensed under the Apache License, Version 2.0 (the "License") with LLVM +// Exceptions; you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef _HAL_SOCKET_CLIENT_H +#define _HAL_SOCKET_CLIENT_H + +#include + +#include "hal.h" +#include "hal_remote/hal_client.h" +#include "hal_remote/hal_socket_transmitter.h" + +namespace hal { + +/// @brief A simple socket based version of `hal_client`. +class hal_socket_client : public hal::hal_client { + protected: + hal::hal_socket_transmitter transmitter; + uint16_t required_port; + + public: + hal_socket_client(uint16_t port) : hal_client() { required_port = port; } + + void set_port(uint16_t port) { required_port = port; } + + bool make_connection() { + transmitter.set_port(required_port); + const hal::hal_socket_transmitter::error_code res = + transmitter.make_connection(); + if (res != hal::hal_socket_transmitter::success) { + return false; + } + return true; + } + hal::hal_transmitter &get_transmitter() { return transmitter; } +}; +} // namespace hal +#endif diff --git a/hal/hal_remote/source/hal_device_client.cpp b/hal/hal_remote/source/hal_device_client.cpp new file mode 100644 index 000000000..d8806678e --- /dev/null +++ b/hal/hal_remote/source/hal_device_client.cpp @@ -0,0 +1,255 @@ +// Copyright (C) Codeplay Software Limited +// +// Licensed under the Apache License, Version 2.0 (the "License") with LLVM +// Exceptions; you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include + +namespace hal { + +hal_device_client::hal_device_client(hal::hal_device_info_t *info, + std::mutex &hal_lock, + hal::hal_transmitter *transmitter) + : hal::hal_device_t(info), transmitter(transmitter), hal_lock(hal_lock) { + if (const char *env = getenv("CA_HAL_DEBUG")) { + if (*env == '1') debug = true; + } +} + +hal::hal_addr_t hal_device_client::mem_alloc(hal::hal_size_t size, + hal::hal_size_t alignment) { + const std::lock_guard locker(hal_lock); + hal_binary_encoder encoder; + hal_binary_decoder decoder; + + encoder.encode_mem_alloc(size, alignment); + bool ok = transmitter->send(encoder.data(), encoder.size(), true); + ok = ok && receive_decode_reply(hal_binary_encoder::COMMAND::MEM_ALLOC_REPLY, + decoder); + + if (ok) { + return decoder.message.alloc_reply; + } else { + assert(0 && "Error Failed to get MEM_ALLOC_REPLY\n"); + return 0; + } +} + +bool hal_device_client::mem_write(hal::hal_addr_t dst, const void *src, + hal::hal_size_t size) { + const std::lock_guard locker(hal_lock); + hal_binary_encoder encoder; + hal_binary_decoder decoder; + encoder.encode_mem_write(dst, size); + bool ok = transmitter->send(encoder.data(), encoder.size(), false); + ok = ok && transmitter->send(static_cast(src), size, true); + ok = ok && receive_decode_reply(hal_binary_encoder::COMMAND::MEM_WRITE_REPLY, + decoder); + + if (ok) { + return decoder.message.write_reply; + } else { + assert(0 && "Error Failed to get MEM_WRITE_REPLY\n"); + return false; + } +} + +bool hal_device_client::mem_fill(hal::hal_addr_t dst, const void *pattern, + hal::hal_size_t pattern_size, + hal::hal_size_t size) { + const std::lock_guard locker(hal_lock); + hal_binary_encoder encoder; + hal_binary_decoder decoder; + encoder.encode_mem_fill(dst, pattern_size, size); + bool ok = transmitter->send(encoder.data(), encoder.size(), false); + ok = ok && transmitter->send(static_cast(pattern), + pattern_size, true); + ok = ok && receive_decode_reply(hal_binary_encoder::COMMAND::MEM_FILL_REPLY, + decoder); + if (ok) { + return decoder.message.fill_reply; + } else { + assert(0 && "Error Failed to get MEM_FILL_REPLY\n"); + return false; + } +} + +bool hal_device_client::program_free(hal::hal_program_t program) { + const std::lock_guard locker(hal_lock); + hal_binary_encoder encoder; + hal_binary_decoder decoder; + encoder.encode_program_free(program); + bool ok = transmitter->send(encoder.data(), encoder.size(), true); + ok = ok && receive_decode_reply( + hal_binary_encoder::COMMAND::PROGRAM_FREE_REPLY, decoder); + if (ok) { + return decoder.message.free_reply; + } else { + assert(0 && "Error Failed to get PROGRAM_FREE_REPLY\n"); + return false; + } +} + +hal::hal_program_t hal_device_client::program_load(const void *data, + hal::hal_size_t size) { + const std::lock_guard locker(hal_lock); + hal_binary_encoder encoder; + hal_binary_decoder decoder; + encoder.encode_program_load(size); + bool ok = transmitter->send(encoder.data(), encoder.size(), false); + ok = ok && + transmitter->send(reinterpret_cast(data), size, true); + ok = ok && receive_decode_reply( + hal_binary_encoder::COMMAND::PROGRAM_LOAD_REPLY, decoder); + if (ok) { + return decoder.message.prog_load_reply; + } else { + assert(0 && "Error Failed to get PROGRAM_LOAD_REPLY\n"); + return false; + } +} + +hal::hal_kernel_t hal_device_client::program_find_kernel( + hal::hal_program_t program, const char *name) { + const std::lock_guard locker(hal_lock); + hal_binary_encoder encoder; + hal_binary_decoder decoder; + const uint32_t name_length = strlen(name) + 1; + encoder.encode_find_kernel(program, name_length); + bool ok = transmitter->send(encoder.data(), encoder.size(), false); + ok = ok && transmitter->send(reinterpret_cast(name), + name_length, true); + ok = ok && receive_decode_reply( + hal_binary_encoder::COMMAND::FIND_KERNEL_REPLY, decoder); + if (ok) { + return decoder.message.find_kernel_reply; + } else { + assert(0 && "Error Failed to get FIND_KERNEL_REPLY\n"); + return false; + } +} + +bool hal_device_client::mem_free(hal::hal_addr_t addr) { + const std::lock_guard locker(hal_lock); + hal_binary_encoder encoder; + hal_binary_decoder decoder; + + encoder.encode_mem_free(addr); + bool ok = transmitter->send(encoder.data(), encoder.size(), true); + ok = ok && receive_decode_reply(hal_binary_encoder::COMMAND::MEM_FREE_REPLY, + decoder); + + if (ok) { + return decoder.message.free_reply; + } else { + assert(0 && "Error Failed to get MEM_FREE_REPLY\n"); + return false; + } +} + +bool hal_device_client::mem_copy(hal::hal_addr_t dst, hal::hal_addr_t src, + hal::hal_size_t size) { + const std::lock_guard locker(hal_lock); + hal_binary_encoder encoder; + hal_binary_decoder decoder; + encoder.encode_mem_copy(dst, src, size); + bool ok = transmitter->send(encoder.data(), encoder.size(), true); + ok = ok && receive_decode_reply(hal_binary_encoder::COMMAND::MEM_COPY_REPLY, + decoder); + + if (ok) { + return decoder.message.copy_reply; + } else { + assert(0 && "Error Failed to get MEM_COPY_REPLY\n"); + return false; + } +} + +bool hal_device_client::kernel_exec(hal::hal_program_t program, + hal::hal_kernel_t kernel, + const hal::hal_ndrange_t *nd_range, + const hal::hal_arg_t *args, + uint32_t num_args, uint32_t work_dim) { + const std::lock_guard locker(hal_lock); + if (hal_debug()) { + (void)fprintf(stderr, + "hal_device_client::kernel_exec(kernel=0x%08lx, num_args=%d, " + "global=<%ld:%ld:%ld>, local=<%ld:%ld:%ld>)\n", + kernel, num_args, nd_range->global[0], nd_range->global[1], + nd_range->global[2], nd_range->local[0], nd_range->local[1], + nd_range->local[2]); + } + hal_binary_encoder encoder; + hal_binary_decoder decoder; + encoder.encode_kernel_exec(program, kernel, nd_range, num_args, work_dim); + bool ok = transmitter->send(encoder.data(), encoder.size(), false); + encoder.clear(); + encoder.encode_kernel_exec_args(args, num_args); + ok = ok && transmitter->send(encoder.data(), encoder.size(), true); + ok = ok && receive_decode_reply( + hal_binary_encoder::COMMAND::KERNEL_EXEC_REPLY, decoder); + + if (ok) { + return decoder.message.kernel_exec_reply; + } else { + assert(0 && "Error Failed to get KERNEL_EXEC_REPLY\n"); + return false; + } +} +bool hal_device_client::mem_read(void *dst, hal::hal_addr_t src, + hal::hal_size_t size) { + const std::lock_guard locker(hal_lock); + hal_binary_encoder encoder; + hal_binary_decoder decoder; + encoder.encode_mem_read(src, size); + + bool ok = transmitter->send(encoder.data(), encoder.size(), true); + ok = ok && receive_decode_reply(hal_binary_encoder::COMMAND::MEM_READ_REPLY, + decoder); + if (ok) { + ok = ok && transmitter->receive((uint8_t *)dst, size); + return ok && decoder.message.read_reply; + } else { + assert(0 && "Error Failed to get MEM_READ_REPLY\n"); + return false; + } + return ok; +} + +bool hal_device_client::receive_decode_reply( + hal_binary_encoder::COMMAND expected_command, hal_binary_decoder &decoder) { + hal_binary_encoder::COMMAND command; + bool ok = true; + ok = ok && transmitter->receive((uint8_t *)&command, + sizeof(hal_binary_encoder::COMMAND)); + assert(ok); + ok = ok && (command == expected_command); + assert(ok); + if (ok) { + const uint32_t data_required = + decoder.decode_command_data_required(command); + std::vector payload(data_required); + ok = ok && transmitter->receive(payload.data(), data_required); + if (ok) { + ok = ok && decoder.decode(command, payload.data(), data_required); + assert(ok); + } + } else { + return false; + } + return ok; +} + +} // namespace hal diff --git a/hal/hal_remote/source/hal_server.cpp b/hal/hal_remote/source/hal_server.cpp new file mode 100644 index 000000000..d64e4038e --- /dev/null +++ b/hal/hal_remote/source/hal_server.cpp @@ -0,0 +1,545 @@ +// Copyright (C) Codeplay Software Limited +// +// Licensed under the Apache License, Version 2.0 (the "License") with LLVM +// Exceptions; you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include + +namespace hal { +hal_server::hal_server(hal_transmitter *transmitter, hal::hal_t *hal) + : hal(hal), transmitter(transmitter) { + if (getenv("HAL_DEBUG_SERVER") && *getenv("HAL_DEBUG_SERVER") == '1') { + debug = true; + } +} + +hal_server::~hal_server() { + if (hal_device) { + hal->device_delete(hal_device); + } +} + +uint8_t *hal_server::receive_data(uint32_t size) { + payload.resize(size); + if (!transmitter->receive(payload.data(), size)) { + return nullptr; + } + return payload.data(); +} + +hal_server::error_code hal_server::process_commands() { + hal_server::error_code ret = hal_server::status_success; + do { + ret = process_command(); + } while (ret == hal::hal_server::status_success); + return ret; +} + +hal_server::error_code hal_server::receive_payload( + hal_binary_encoder::COMMAND command, std::vector &payload) { + hal_binary_decoder decoder; + const uint32_t data_required = decoder.decode_command_data_required(command); + if (data_required == hal_binary_decoder::data_required_unknown) { + return hal_server::status_decode_failed; + } + payload.resize(data_required); + bool receive_ok = true; + if (data_required) { + receive_ok = transmitter->receive(payload.data(), data_required); + } + if (!receive_ok) { + return hal_server::status_transmitter_failed; + } else { + return hal_server::status_success; + } +} + +hal_server::error_code hal_server::process_mem_alloc(uint32_t device) { + auto payload_status = + receive_payload(hal_binary_encoder::COMMAND::MEM_ALLOC, payload); + if (payload_status != hal_server::status_success) { + return payload_status; + } + hal_binary_decoder decoder; + const bool decoder_status = decoder.decode( + hal_binary_encoder::COMMAND::MEM_ALLOC, payload.data(), payload.size()); + if (!decoder_status) { + return hal::hal_server::status_decode_failed; + } + + auto res = hal_device->mem_alloc(decoder.message.alloc.size, + decoder.message.alloc.alignment); + if (debug) { + printf("Hal Server:: mem alloc %lu %lu -> %d\n", decoder.message.alloc.size, + decoder.message.alloc.alignment, (int)res); + (void)fflush(stdout); + } + hal_binary_encoder encode_reply; + encode_reply.encode_mem_alloc_reply(res); + auto send_res = + transmitter->send(encode_reply.data(), encode_reply.size(), true); + if (!send_res) { + return hal_server::status_transmitter_failed; + } + return hal_server::status_success; +} + +hal_server::error_code hal_server::process_mem_free(uint32_t device) { + hal_binary_decoder decoder; + auto payload_status = + receive_payload(hal_binary_encoder::COMMAND::MEM_FREE, payload); + if (payload_status != hal_server::status_success) { + return payload_status; + } + const bool decoder_status = decoder.decode( + hal_binary_encoder::COMMAND::MEM_FREE, payload.data(), payload.size()); + if (!decoder_status) { + return hal::hal_server::status_decode_failed; + } + + auto res = hal_device->mem_free(decoder.message.free.addr); + if (debug) { + printf("Hal Server:: mem free %lux -> %d\n", decoder.message.free.addr, + (int)res); + (void)fflush(stdout); + } + hal_binary_encoder encode_reply; + encode_reply.encode_mem_free_reply(res); + auto send_res = + transmitter->send(encode_reply.data(), encode_reply.size(), true); + if (!send_res) { + return hal_server::status_transmitter_failed; + } + return hal_server::status_success; +} + +hal_server::error_code hal_server::process_mem_write(uint32_t device) { + auto payload_status = + receive_payload(hal_binary_encoder::COMMAND::MEM_WRITE, payload); + if (payload_status != hal_server::status_success) { + return payload_status; + } + hal_binary_decoder decoder; + const bool decoder_status = decoder.decode( + hal_binary_encoder::COMMAND::MEM_WRITE, payload.data(), payload.size()); + if (!decoder_status) { + return hal::hal_server::status_decode_failed; + } + const uint8_t *src_data = receive_data(decoder.message.write.size); + if (!src_data) { + return hal_server::status_transmitter_failed; + } + + assert(src_data && "Src data must be non Null"); + auto res = hal_device->mem_write(decoder.message.write.dst, src_data, + decoder.message.write.size); + if (debug) { + printf("Hal Server:: mem_write %lu %p %lu-> %d\n", + decoder.message.write.dst, src_data, decoder.message.write.size, + (int)res); + (void)fflush(stdout); + } + + hal_binary_encoder encode_reply; + encode_reply.encode_mem_write_reply(res); + auto send_res = + transmitter->send(encode_reply.data(), encode_reply.size(), true); + if (!send_res) { + return hal_server::status_transmitter_failed; + } + return hal_server::status_success; +} + +hal_server::error_code hal_server::process_mem_fill(uint32_t device) { + hal_binary_decoder decoder; + auto payload_status = + receive_payload(hal_binary_encoder::COMMAND::MEM_FILL, payload); + if (payload_status != hal_server::status_success) { + return payload_status; + } + const bool decoder_status = decoder.decode( + hal_binary_encoder::COMMAND::MEM_FILL, payload.data(), payload.size()); + if (!decoder_status) { + return hal::hal_server::status_decode_failed; + } + const uint8_t *pattern_data = receive_data(decoder.message.fill.pattern_size); + if (!pattern_data) { + return hal::hal_server::status_transmitter_failed; + } + auto res = hal_device->mem_fill(decoder.message.fill.dst, pattern_data, + decoder.message.fill.pattern_size, + decoder.message.fill.size); + hal_binary_encoder encode_reply; + encode_reply.encode_mem_fill_reply(res); + auto send_res = + transmitter->send(encode_reply.data(), encode_reply.size(), true); + if (!send_res) { + return hal_server::status_transmitter_failed; + } + return hal_server::status_success; +} + +hal_server::error_code hal_server::process_mem_read(uint32_t device) { + hal_binary_decoder decoder; + auto payload_status = + receive_payload(hal_binary_encoder::COMMAND::MEM_READ, payload); + if (payload_status != hal_server::status_success) { + return payload_status; + } + const bool decoder_status = decoder.decode( + hal_binary_encoder::COMMAND::MEM_READ, payload.data(), payload.size()); + if (!decoder_status) { + return hal::hal_server::status_decode_failed; + } + std::vector dst; + dst.resize(decoder.message.read.size); + auto res = hal_device->mem_read(dst.data(), decoder.message.read.src, + decoder.message.read.size); + if (debug) { + printf("Hal Server:: mem_read %p %lux %lu-> %d\n", dst.data(), + decoder.message.read.src, decoder.message.read.size, res); + (void)fflush(stdout); + } + hal_binary_encoder encode_reply; + encode_reply.encode_mem_read_reply(res); + auto send_res = + transmitter->send(encode_reply.data(), encode_reply.size(), true); + send_res = send_res && + transmitter->send(dst.data(), decoder.message.read.size, true); + if (!send_res) { + return hal_server::status_transmitter_failed; + } + return hal_server::status_success; +} + +hal_server::error_code hal_server::process_mem_copy(uint32_t device) { + hal_binary_decoder decoder; + auto payload_status = + receive_payload(hal_binary_encoder::COMMAND::MEM_COPY, payload); + if (payload_status != hal_server::status_success) { + return payload_status; + } + const bool decoder_status = decoder.decode( + hal_binary_encoder::COMMAND::MEM_COPY, payload.data(), payload.size()); + if (!decoder_status) { + return hal::hal_server::status_decode_failed; + } + auto res = + hal_device->mem_copy(decoder.message.copy.dst, decoder.message.copy.src, + decoder.message.copy.size); + hal_binary_encoder encode_reply; + encode_reply.encode_mem_copy_reply(res); + auto send_res = + transmitter->send(encode_reply.data(), encode_reply.size(), true); + + if (!send_res) { + return hal_server::status_transmitter_failed; + } + return hal_server::status_success; +} + +hal_server::error_code hal_server::process_program_free(uint32_t device) { + hal_binary_decoder decoder; + auto payload_status = + receive_payload(hal_binary_encoder::COMMAND::PROGRAM_FREE, payload); + if (payload_status != hal_server::status_success) { + return payload_status; + } + const bool decoder_status = + decoder.decode(hal_binary_encoder::COMMAND::PROGRAM_FREE, payload.data(), + payload.size()); + if (!decoder_status) { + return hal::hal_server::status_decode_failed; + } + auto res = hal_device->program_free(decoder.message.prog_free.program); + if (debug) { + printf("Hal Server:: program_free %lu -> %d\n", + decoder.message.prog_free.program, (int)res); + (void)fflush(stdout); + } + hal_binary_encoder encode_reply; + encode_reply.encode_program_free_reply(res); + auto send_res = + transmitter->send(encode_reply.data(), encode_reply.size(), true); + + if (!send_res) { + return hal_server::status_transmitter_failed; + } + return hal_server::status_success; +} + +hal_server::error_code hal_server::process_find_kernel(uint32_t device) { + hal_binary_decoder decoder; + auto payload_status = + receive_payload(hal_binary_encoder::COMMAND::FIND_KERNEL, payload); + if (payload_status != hal_server::status_success) { + return payload_status; + } + const bool decoder_status = decoder.decode( + hal_binary_encoder::COMMAND::FIND_KERNEL, payload.data(), payload.size()); + if (!decoder_status) { + return hal::hal_server::status_decode_failed; + } + + const uint8_t *name_data = + receive_data(decoder.message.prog_find_kernel.kernel_name_size); + if (!name_data) { + return hal::hal_server::status_transmitter_failed; + } + + auto res = hal_device->program_find_kernel( + decoder.message.prog_find_kernel.program, (const char *)name_data); + if (debug) { + printf("Hal Server:: program_find_kernel %lx %s -> %d\n", + decoder.message.prog_find_kernel.program, (const char *)name_data, + (int)res); + (void)fflush(stdout); + } + + hal_binary_encoder encode_reply; + encode_reply.encode_find_kernel_reply(res); + auto send_res = + transmitter->send(encode_reply.data(), encode_reply.size(), true); + + if (!send_res) { + return hal_server::status_transmitter_failed; + } + return hal_server::status_success; +} + +hal_server::error_code hal_server::process_program_load(uint32_t device) { + hal_binary_decoder decoder; + auto payload_status = + receive_payload(hal_binary_encoder::COMMAND::PROGRAM_LOAD, payload); + if (payload_status != hal_server::status_success) { + return payload_status; + } + const bool decoder_status = + decoder.decode(hal_binary_encoder::COMMAND::PROGRAM_LOAD, payload.data(), + payload.size()); + if (!decoder_status) { + return hal::hal_server::status_decode_failed; + } + const uint8_t *data = receive_data(decoder.message.prog_load.size); + if (!data) { + return hal::hal_server::status_transmitter_failed; + } + + auto res = hal_device->program_load(data, decoder.message.prog_load.size); + if (debug) { + printf("Hal Server:: program_load %lu (%p) -> %lux\n", + decoder.message.prog_load.size, data, res); + } + hal_binary_encoder encode_reply; + encode_reply.encode_program_load_reply(res); + auto send_res = + transmitter->send(encode_reply.data(), encode_reply.size(), true); + + if (!send_res) { + return hal_server::status_transmitter_failed; + } + return hal_server::status_success; +} + +hal_server::error_code hal_server::process_kernel_exec(uint32_t device) { + hal_binary_decoder decoder; + auto payload_status = + receive_payload(hal_binary_encoder::COMMAND::KERNEL_EXEC, payload); + if (payload_status != hal_server::status_success) { + return payload_status; + } + // Download and convert args + const bool decoder_status = decoder.decode( + hal_binary_encoder::COMMAND::KERNEL_EXEC, payload.data(), payload.size()); + if (!decoder_status) { + return hal::hal_server::status_decode_failed; + } + uint8_t *data = nullptr; + if (decoder.message.kernel_exec.args_data_size) { + data = receive_data(decoder.message.kernel_exec.args_data_size); + + const bool receive_ok = decoder.decode_kernel_exec_args( + data, decoder.message.kernel_exec.num_args, + decoder.message.kernel_exec.args_data_size); + if (!receive_ok) { + return hal_server::status_transmitter_failed; + } + } + + auto res = hal_device->kernel_exec( + decoder.message.kernel_exec.program, decoder.message.kernel_exec.kernel, + &decoder.message.kernel_exec.nd_range, decoder.kernel_args.data(), + decoder.message.kernel_exec.num_args, + decoder.message.kernel_exec.work_dim); + if (debug) { + printf( + "Hal Server:: kernel_exec (%d) %lux %lux (%lu %lu %lu):(%lu %lu " + "%lu) %p %d %d-> %d\n", + decoder.message.kernel_exec.args_data_size, + decoder.message.kernel_exec.program, decoder.message.kernel_exec.kernel, + decoder.message.kernel_exec.nd_range.global[0], + decoder.message.kernel_exec.nd_range.global[1], + decoder.message.kernel_exec.nd_range.global[2], + decoder.message.kernel_exec.nd_range.local[0], + decoder.message.kernel_exec.nd_range.local[1], + decoder.message.kernel_exec.nd_range.local[2], + decoder.kernel_args.data(), decoder.message.kernel_exec.num_args, + decoder.message.kernel_exec.work_dim, res); + } + hal_binary_encoder encode_reply; + encode_reply.encode_kernel_exec_reply(res); + auto send_res = + transmitter->send(encode_reply.data(), encode_reply.size(), true); + + if (!send_res) { + return hal_server::status_transmitter_failed; + } + return hal_server::status_success; +} + +hal_server::error_code hal_server::process_device_create(uint32_t device) { + hal_binary_decoder decoder; + auto payload_status = + receive_payload(hal_binary_encoder::COMMAND::DEVICE_CREATE, payload); + if (payload_status != hal_server::status_success) { + return payload_status; + } + const bool decoder_status = + decoder.decode(hal_binary_encoder::COMMAND::DEVICE_CREATE, payload.data(), + payload.size()); + if (!decoder_status) { + return hal::hal_server::status_decode_failed; + } + hal_device = hal->device_create(device); + if (debug) { + printf("Hal Server:: device_create %u-> %p\n", device, hal_device); + } + hal_binary_encoder encode_reply; + encode_reply.encode_device_create_reply(hal_device ? true : false); + auto send_res = + transmitter->send(encode_reply.data(), encode_reply.size(), true); + + if (!send_res) { + return hal_server::status_transmitter_failed; + } + return hal_server::status_success; +} + +hal_server::error_code hal_server::process_device_delete(uint32_t device) { + hal_binary_decoder decoder; + auto payload_status = + receive_payload(hal_binary_encoder::COMMAND::DEVICE_DELETE, payload); + if (payload_status != hal_server::status_success) { + return payload_status; + } + // TODO - call delete with looked up device + const bool decoder_status = + decoder.decode(hal_binary_encoder::COMMAND::DEVICE_DELETE, payload.data(), + payload.size()); + if (!decoder_status) { + return hal::hal_server::status_decode_failed; + } + hal->device_delete(hal_device); + hal_device = nullptr; + if (debug) { + printf("Hal Server:: device_delete %u\n", device); + } + hal_binary_encoder encode_reply; + encode_reply.encode_device_delete_reply(true); + auto send_res = + transmitter->send(encode_reply.data(), encode_reply.size(), true); + if (!send_res) { + return hal_server::status_transmitter_failed; + } + return hal_server::status_success; +} + +hal_server::error_code hal_server::process_command() { + struct __attribute__((packed)) { + hal_binary_encoder::COMMAND command; + uint32_t device; + } prefix; + static_assert(sizeof(prefix) == 8); + + const bool receive_ok = transmitter->receive((uint8_t *)&prefix, 8); + if (!receive_ok) { + return hal_server::status_transmitter_failed; + } + if (debug) { + printf("Hal Server:: received command %d\n", (int)prefix.command); + (void)fflush(stdout); + } + assert(prefix.device == 0); + + // Don't support device != 0, so shutdown for now, we'll look to implement + // this further down the line. + if (prefix.device != 0) { + return hal_server::status_device_not_supported; + } + + auto ret_val = hal_server::status_success; + switch (prefix.command) { + case hal_binary_encoder::COMMAND::MEM_ALLOC: + ret_val = process_mem_alloc(prefix.device); + break; + case hal_binary_encoder::COMMAND::MEM_FREE: + ret_val = process_mem_free(prefix.device); + break; + case hal_binary_encoder::COMMAND::MEM_WRITE: + ret_val = process_mem_write(prefix.device); + break; + case hal_binary_encoder::COMMAND::MEM_READ: + ret_val = process_mem_read(prefix.device); + break; + case hal_binary_encoder::COMMAND::MEM_FILL: + ret_val = process_mem_fill(prefix.device); + break; + case hal_binary_encoder::COMMAND::MEM_COPY: + ret_val = process_mem_copy(prefix.device); + break; + case hal_binary_encoder::COMMAND::PROGRAM_FREE: + ret_val = process_program_free(prefix.device); + break; + case hal_binary_encoder::COMMAND::FIND_KERNEL: + ret_val = process_find_kernel(prefix.device); + break; + case hal_binary_encoder::COMMAND::PROGRAM_LOAD: + ret_val = process_program_load(prefix.device); + break; + case hal_binary_encoder::COMMAND::KERNEL_EXEC: + ret_val = process_kernel_exec(prefix.device); + break; + case hal_binary_encoder::COMMAND::DEVICE_CREATE: + ret_val = process_device_create(prefix.device); + break; + case hal_binary_encoder::COMMAND::DEVICE_DELETE: + ret_val = process_device_delete(prefix.device); + break; + default: + assert(0 && "Unknown command"); + return hal_server::status_unknown_command; + } + if (!receive_ok) { + return hal_server::status_transmitter_failed; + } + + return ret_val; +} +} // namespace hal diff --git a/hal/hal_remote/source/hal_socket_transmitter.cpp b/hal/hal_remote/source/hal_socket_transmitter.cpp index 6cdec0338..714f03b69 100644 --- a/hal/hal_remote/source/hal_socket_transmitter.cpp +++ b/hal/hal_remote/source/hal_socket_transmitter.cpp @@ -39,11 +39,7 @@ hal_socket_transmitter::~hal_socket_transmitter() { hal_socket_transmitter::error_code hal_socket_transmitter::start_server( bool print_port) { if (!setup_connection_done) { -<<<<<<< HEAD const hal_socket_transmitter::error_code res = setup_connection(true); -======= - hal_socket_transmitter::error_code res = setup_connection(true); ->>>>>>> Update to use getaddrinfo and set to allow only from a certain node. if (res != hal_socket_transmitter::success) { last_error = res; return res; @@ -142,22 +138,14 @@ hal_socket_transmitter::error_code hal_socket_transmitter::setup_connection( hints.ai_flags = AI_NUMERICSERV; hints.ai_protocol = IPPROTO_TCP; -<<<<<<< HEAD const std::string port_str = std::to_string(port_requested); -======= - std::string port_str = std::to_string(port_requested); ->>>>>>> Update to use getaddrinfo and set to allow only from a certain node. s = getaddrinfo(node.c_str(), port_str.c_str(), &hints, &result); // Use getaddrinfo on the node and port. This may return one or // more entries we can bind to in priority order. We take the first // one that matches. if (s != 0) { if (debug_enabled()) { -<<<<<<< HEAD (void)fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); -======= - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); ->>>>>>> Update to use getaddrinfo and set to allow only from a certain node. } return hal_socket_transmitter::getaddrinfo_failed; } else { diff --git a/hal/source/hal_library.cpp b/hal/source/hal_library.cpp index 489ef1309..cc5346119 100644 --- a/hal/source/hal_library.cpp +++ b/hal/source/hal_library.cpp @@ -85,28 +85,30 @@ hal_t *load_hal_library(const char *library_path, uint32_t expected_api_version, // https://stackoverflow.com/questions/51209268/using-stdthread-in-a-library-loaded-with-dlopen-leads-to-a-sigsev handle = dlopen(library_path, RTLD_LAZY | RTLD_GLOBAL); if (!handle) { - fprintf(stderr, "error: could not load '%s'\n", library_path); + (void)fprintf(stderr, "error: could not load '%s' : '%s'\n", library_path, + dlerror()); return nullptr; } create_hal_fn hal_entry_fn = (create_hal_fn)dlsym(handle, "get_hal"); if (!hal_entry_fn) { - fprintf(stderr, "error: could not find the HAL entry point function\n"); + (void)fprintf(stderr, + "error: could not find the HAL entry point function\n"); dlclose(handle); return nullptr; } uint32_t reported_api_version = 0; hal_t *hal = hal_entry_fn(reported_api_version); if (!hal) { - fprintf(stderr, "error: creating the HAL failed\n"); + (void)fprintf(stderr, "error: creating the HAL failed\n"); dlclose(handle); return nullptr; } if (expected_api_version != 0 && reported_api_version != expected_api_version) { - fprintf(stderr, - "error: expected HAL API version %" PRIu32 ", but %" PRIu32 - " is the version reported by the loaded HAL\n", - expected_api_version, reported_api_version); + (void)fprintf(stderr, + "error: expected HAL API version %" PRIu32 ", but %" PRIu32 + " is the version reported by the loaded HAL\n", + expected_api_version, reported_api_version); dlclose(handle); return nullptr; } @@ -149,13 +151,13 @@ hal_t *load_hal(const char *default_device, uint32_t expected_api_version, expected_api_version, handle)) { return hal; } else { - fprintf(stderr, "error: unable to load hal library from '%s'\n", - library_path.c_str()); + (void)fprintf(stderr, "error: unable to load hal library from '%s'\n", + library_path.c_str()); } } else { - fprintf(stderr, - "error: no default device was specified and couldn't load " - "CA_HAL_DEVICE\n"); + (void)fprintf(stderr, + "error: no default device was specified and couldn't load " + "CA_HAL_DEVICE\n"); } return nullptr; } diff --git a/scripts/new_target_templates/cpu_client_riscv.json b/scripts/new_target_templates/cpu_client_riscv.json new file mode 100644 index 000000000..65e0d07a3 --- /dev/null +++ b/scripts/new_target_templates/cpu_client_riscv.json @@ -0,0 +1,9 @@ +{ + "target_name": "cpu_client", + "llvm_name": "RISCV", + "llvm_cpu": "'generic-rv64'", + "llvm_features": "'+m,+f,+a,+d'", + "llvm_triple": "'riscv64-unknown-elf'", + "link" : "true", + "link_shared": "true" +}