[opencl] Add Fuchsia loader to OpenCL-ICD-Loader
Find and load OpenCL libraries via the fuchsia.opencl.loader.Loader service.
fixes: 91263
tests: fuchsia-pkg://fuchsia.com/libopencl_test#meta/libopencl_test.cm
Change-Id: I889e664aaf02ed36eaa3e8322c17b35463af50ec
diff --git a/fuchsia/BUILD.gn b/fuchsia/BUILD.gn
index aeb2059..caff16a 100644
--- a/fuchsia/BUILD.gn
+++ b/fuchsia/BUILD.gn
@@ -17,10 +17,19 @@
import("//build/cpp/sdk_shared_library.gni")
import("//build/sdk/sdk_documentation.gni")
-source_set("opencl_dlopen") {
+source_set("opencl_fuchsia") {
+
+ include_dirs = [
+ "../loader",
+ "../loader/fuchsia",
+ ]
+
+ defines = [ "CL_TARGET_OPENCL_VERSION=300" ]
+
sources = [
"../loader/fuchsia/dlopen_fuchsia.cc",
"../loader/fuchsia/dlopen_fuchsia.h",
+ "../loader/fuchsia/icd_fuchsia.cc",
"../loader/fuchsia/loader_service.cc",
"../loader/fuchsia/loader_service.h",
]
@@ -31,6 +40,8 @@
"//zircon/system/ulib/service:service-llcpp",
"//zircon/system/ulib/syslog",
]
+
+ public_deps = [ "//third_party/OpenCL-Headers:opencl_headers" ]
}
source_set("opencl_icd") {
@@ -38,8 +49,11 @@
defines = [ "CL_TARGET_OPENCL_VERSION=300" ]
+ configs -= [ "//build/config:symbol_visibility_hidden" ]
+
sources = [
"../loader/adapters.h",
+ "../loader/icd.c",
"../loader/icd.h",
"../loader/icd_dispatch.c",
"../loader/icd_dispatch.h",
@@ -56,13 +70,8 @@
libcxx_linkage = "static"
no_headers = true
- include_dirs = [
- "loader",
- "loader/fuchsia",
- ]
-
deps = [
- ":opencl_dlopen",
+ ":opencl_fuchsia",
":opencl_icd",
"//sdk/lib/fdio",
]
diff --git a/loader/fuchsia/dlopen_fuchsia.cc b/loader/fuchsia/dlopen_fuchsia.cc
index c082a11..9885924 100644
--- a/loader/fuchsia/dlopen_fuchsia.cc
+++ b/loader/fuchsia/dlopen_fuchsia.cc
@@ -64,8 +64,8 @@
static void *dlopen_from_opencl_loader(const char *name, int mode) {
// Connect to the opencl loader service to request this library.
- auto &loader_svc = get_opencl_loader_service();
- if (!loader_svc.client_end()) {
+ auto &loader_svc = opencl_loader::get_opencl_loader_service();
+ if (!loader_svc.is_valid()) {
snprintf(g_error, sizeof(g_error),
"libopencl.so:dlopen_fuchsia: no connection to loader svc\n");
return nullptr;
diff --git a/loader/fuchsia/icd_fuchsia.cc b/loader/fuchsia/icd_fuchsia.cc
new file mode 100644
index 0000000..eb41fdc
--- /dev/null
+++ b/loader/fuchsia/icd_fuchsia.cc
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2021 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ *
+ * OpenCL is a trademark of Apple Inc. used under license by Khronos.
+ */
+
+#include "dlopen_fuchsia.h"
+#include "loader_service.h"
+
+#include <dlfcn.h>
+#include <lib/syslog/global.h>
+#include <pthread.h>
+
+extern "C" {
+ #include "icd.h"
+}
+
+static pthread_once_t initialized = PTHREAD_ONCE_INIT;
+
+/*
+ *
+ * Vendor enumeration functions
+ *
+ */
+
+// go through the list of vendors in the two configuration files
+void khrIcdOsVendorsEnumerate(void)
+{
+ auto &loader_svc = opencl_loader::get_opencl_loader_service();
+ if (!loader_svc.is_valid()) {
+ FX_LOGF(ERROR, opencl_loader::kTag, "no connection to loader svc");
+ return;
+ }
+
+ // TODO(fxbug.dev/91413): Get the library name from the loader_service.
+ khrIcdVendorAdd("libopencl_fake.so");
+}
+
+// go through the list of vendors only once
+void khrIcdOsVendorsEnumerateOnce(void)
+{
+ pthread_once(&initialized, khrIcdOsVendorsEnumerate);
+}
+
+/*
+ *
+ * Dynamic library loading functions
+ *
+ */
+
+// dynamically load a library. returns NULL on failure
+void *khrIcdOsLibraryLoad(const char *libraryName)
+{
+ return dlopen_fuchsia(libraryName, RTLD_NOW, true);
+}
+
+// get a function pointer from a loaded library. returns NULL on failure.
+void *khrIcdOsLibraryGetFunctionAddress(void *library, const char *functionName)
+{
+ return dlsym(library, functionName);
+}
+
+// unload a library
+void khrIcdOsLibraryUnload(void *library)
+{
+ dlclose(library);
+}
diff --git a/loader/fuchsia/loader_service.cc b/loader/fuchsia/loader_service.cc
index bf1b540..238caba 100644
--- a/loader/fuchsia/loader_service.cc
+++ b/loader/fuchsia/loader_service.cc
@@ -15,9 +15,9 @@
* limitations under the License.
*
*/
-
#include "loader_service.h"
+#include <fcntl.h>
#include <fidl/fuchsia.io/cpp/wire.h>
#include <fidl/fuchsia.opencl.loader/cpp/wire.h>
#include <lib/fdio/directory.h>
@@ -27,78 +27,99 @@
#include <lib/service/llcpp/service.h>
#include <lib/syslog/global.h>
#include <stdio.h>
+#include <string.h>
#include <threads.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
+namespace opencl_loader {
+
static fidl::WireSyncClient<fuchsia_opencl_loader::Loader> opencl_loader_svc;
static zx_handle_t device_fs = ZX_HANDLE_INVALID;
-constexpr const char* kTag = "opencl";
+static int manifest_fs_fd = -1;
void connect_to_opencl_loader_svc() {
- auto svc = service::OpenServiceRoot();
- auto client_end = service::ConnectAt<fuchsia_opencl_loader::Loader>(*svc);
- if (!client_end.is_ok()) {
- FX_LOGF(ERROR, kTag, "connect_to_opencl_loader_svc: Failed to connect to loader service: %s",
- client_end.status_string());
- return;
- }
- auto client = fidl::BindSyncClient(std::move(*client_end));
- auto feature_result = client->GetSupportedFeatures();
- fuchsia_opencl_loader::wire::Features features;
- if (!feature_result.ok()) {
- FX_LOGF(
- ERROR, kTag,
- "connect_to_opencl_loader_svc: Failed to get supported features, error \"%s\". Retrying.",
- feature_result.error().lossy_description());
- // If GetSupportedFeatures isn't available the connection will be closed.
- // If that happens, reconnect and assume a default set of features.
+ auto svc = service::OpenServiceRoot();
auto client_end = service::ConnectAt<fuchsia_opencl_loader::Loader>(*svc);
if (!client_end.is_ok()) {
- FX_LOGF(ERROR, NULL,
- "connect_to_opencl_loader_svc: Failed to reconnect to loader service: %s",
- client_end.status_string());
- return;
+ FX_LOGF(ERROR, kTag, "connect_to_opencl_loader_svc: Failed to connect to loader service: %s", client_end.status_string());
+ return;
}
- client = fidl::BindSyncClient(std::move(*client_end));
- features = fuchsia_opencl_loader::wire::Features::kGet;
- } else {
+ auto client = fidl::BindSyncClient(std::move(*client_end));
+ auto feature_result = client->GetSupportedFeatures();
+ fuchsia_opencl_loader::wire::Features features;
+ if (!feature_result.ok()) {
+ FX_LOGF(ERROR, kTag, "connect_to_opencl_loader_svc: Failed to get supported features, error \"%s\".",
+ feature_result.error().lossy_description());
+ return;
+ }
features = feature_result->features;
- }
- zx::channel device_fs_client;
- if (features & fuchsia_opencl_loader::wire::Features::kConnectToDeviceFs) {
- zx::channel device_fs_server;
- zx_status_t status = zx::channel::create(0, &device_fs_server, &device_fs_client);
- if (status != ZX_OK) {
- FX_LOGF(ERROR, kTag, "connect_to_opencl_loader_svc: Failed to create channel: %s",
- zx_status_get_string(status));
- return;
+ constexpr fuchsia_opencl_loader::wire::Features kMandatoryFeatures =
+ fuchsia_opencl_loader::wire::Features::kConnectToDeviceFs | fuchsia_opencl_loader::wire::Features::kConnectToManifestFs |
+ fuchsia_opencl_loader::wire::Features::kGet;
+ if ((features & kMandatoryFeatures) != kMandatoryFeatures) {
+ FX_LOGF(ERROR, kTag, "connect_to_opencl_loader_svc: Missing mandatory feature 0x%x",
+ static_cast<uint32_t>(kMandatoryFeatures & ~features));
+ return;
}
-
- auto result = client->ConnectToDeviceFs(std::move(device_fs_server));
- if (!result.ok()) {
- FX_LOGF(ERROR, kTag, "connect_to_opencl_loader_svc: Failed to connect to device fs: %s",
- result.status_string());
- return;
+ zx::channel device_fs_client;
+ {
+ zx::channel device_fs_server;
+ zx_status_t status = zx::channel::create(0, &device_fs_server, &device_fs_client);
+ if (status != ZX_OK) {
+ FX_LOGF(ERROR, kTag, "connect_to_opencl_loader_svc: Failed to create channel: %s",
+ zx_status_get_string(status));
+ return;
+ }
+ auto result = client->ConnectToDeviceFs(std::move(device_fs_server));
+ if (!result.ok()) {
+ FX_LOGF(ERROR, kTag, "connect_to_opencl_loader_svc: Failed to connect to device fs: %s", result.status_string());
+ return;
+ }
}
- }
-
- opencl_loader_svc = std::move(client);
- device_fs = device_fs_client.release();
+ {
+ zx::channel manifest_fs_client;
+ zx::channel manifest_fs_server;
+ zx_status_t status = zx::channel::create(0, &manifest_fs_server, &manifest_fs_client);
+ if (status != ZX_OK) {
+ FX_LOGF(ERROR, kTag, "connect_to_opencl_loader_svc: Failed to create channel: %s",
+ zx_status_get_string(status));
+ return;
+ }
+ // Wait for idle so clients will be sure that any existing ICDs will be completely available.
+ auto result = client->ConnectToManifestFs(fuchsia_opencl_loader::wire::ConnectToManifestOptions::kWaitForIdle,
+ std::move(manifest_fs_server));
+ if (!result.ok()) {
+ FX_LOGF(ERROR, kTag, "connect_to_opencl_loader_svc: Failed to connect to manifest fs: %s", result.status_string());
+ return;
+ }
+ status = fdio_fd_create(manifest_fs_client.release(), &manifest_fs_fd);
+ if (status != ZX_OK) {
+ FX_LOGF(ERROR, kTag, "connect_to_opencl_loader_svc: Failed to create manifest fs fd: %s", zx_status_get_string(status));
+ return;
+ }
+ }
+ opencl_loader_svc = std::move(client);
+ device_fs = device_fs_client.release();
}
static once_flag svc_connect_once_flag = ONCE_FLAG_INIT;
fidl::WireSyncClient<fuchsia_opencl_loader::Loader>& get_opencl_loader_service() {
- call_once(&svc_connect_once_flag, connect_to_opencl_loader_svc);
-
- return opencl_loader_svc;
+ call_once(&svc_connect_once_flag, connect_to_opencl_loader_svc);
+ return opencl_loader_svc;
}
zx_handle_t get_device_fs() {
- call_once(&svc_connect_once_flag, connect_to_opencl_loader_svc);
+ call_once(&svc_connect_once_flag, connect_to_opencl_loader_svc);
+ return device_fs;
+}
- return device_fs;
+int get_manifest_fs_fd(void) {
+ call_once(&svc_connect_once_flag, connect_to_opencl_loader_svc);
+ return manifest_fs_fd;
+}
+
}
diff --git a/loader/fuchsia/loader_service.h b/loader/fuchsia/loader_service.h
index ceb3a1f..0c25fe9 100644
--- a/loader/fuchsia/loader_service.h
+++ b/loader/fuchsia/loader_service.h
@@ -22,6 +22,10 @@
#include <fidl/fuchsia.opencl.loader/cpp/wire.h>
#include <zircon/types.h>
+namespace opencl_loader {
+
+constexpr auto kTag = "opencl_loader";
+
// Returns a singleton handle to the fuchsia.opencl.loader.Loader service. Does not transfer
// ownership.
fidl::WireSyncClient<fuchsia_opencl_loader::Loader>& get_opencl_loader_service();
@@ -30,4 +34,6 @@
// fuchsia.opencl.loader/Loader.ConnectToDeviceFs. Does not transfer ownership.
zx_handle_t get_device_fs();
+}
+
#endif // THIRD_PARTY_OPENCL_ICD_LOADER_LOADER_FUCHSIA_LOADER_SERVICE_H_
diff --git a/loader/icd.c b/loader/icd.c
index aa677b0..46f8eeb 100644
--- a/loader/icd.c
+++ b/loader/icd.c
@@ -318,6 +318,7 @@
}
#endif // defined(CL_ENABLE_LAYERS)
+#if !defined(__Fuchsia__)
// Get next file or dirname given a string list or registry key path.
// Note: the input string may be modified!
static char *loader_get_next_path(char *path) {
@@ -357,6 +358,7 @@
khrIcd_free_getenv(icdFilenames);
}
}
+#endif
#if defined(CL_ENABLE_LAYERS)
void khrIcdLayersEnumerateEnv(void)
diff --git a/loader/icd_platform.h b/loader/icd_platform.h
index f84f519..4d0db0d 100644
--- a/loader/icd_platform.h
+++ b/loader/icd_platform.h
@@ -19,7 +19,7 @@
#ifndef _ICD_PLATFORM_H_
#define _ICD_PLATFORM_H_
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__Fuchsia__)
#define PATH_SEPARATOR ':'
#define DIRECTORY_SYMBOL '/'