1 /*
2 * Copyright (c) Qualcomm Innovation Center, Inc.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8 #include <dlfcn.h>
9 #include <executorch/backends/qualcomm/runtime/backends/QnnImplementation.h>
10
11 #include "QnnInterface.h"
12 namespace executorch {
13 namespace backends {
14 namespace qnn {
15
16 using executorch::runtime::Error;
17
18 template <typename Fn>
loadQnnFunction(void * handle,const char * function_name)19 Fn loadQnnFunction(void* handle, const char* function_name) {
20 return reinterpret_cast<Fn>(dlsym(handle, function_name)); // NOLINT
21 }
22
InitBackend(void * const lib_handle,const QnnSaver_Config_t ** saver_config)23 Error QnnImplementation::InitBackend(
24 void* const lib_handle,
25 const QnnSaver_Config_t** saver_config) {
26 Qnn_ErrorHandle_t error = QNN_SUCCESS;
27 // saver_config must be set before backend initialization
28 auto saver_initialize =
29 loadQnnFunction<QnnSaverInitializeFn*>(lib_handle, "QnnSaver_initialize");
30 if (saver_initialize != nullptr) {
31 error = saver_initialize(saver_config);
32 if (error != QNN_SUCCESS) {
33 QNN_EXECUTORCH_LOG_ERROR(
34 "[Qnn Delegate] QnnSaver Backend Failed to "
35 "saver_initialize. Error %d",
36 QNN_GET_ERROR_CODE(error));
37 return Error::Internal;
38 }
39 }
40 return Error::Ok;
41 }
42
43 // instantiate static members
44 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
45 std::unordered_map<std::string, QnnImplementation::BackendIdType>
46 QnnImplementation::lib_path_to_backend_id_;
47 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
48 std::unordered_map<QnnImplementation::BackendIdType, const QnnInterface_t*>
49 QnnImplementation::loaded_backend_;
50 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
51 std::unordered_map<QnnImplementation::BackendIdType, void*>
52 QnnImplementation::loaded_lib_handle_;
53 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
54 std::mutex QnnImplementation::be_init_mutex_;
55
StartBackend(const std::string & lib_path,const QnnSaver_Config_t ** saver_config)56 Error QnnImplementation::StartBackend(
57 const std::string& lib_path,
58 const QnnSaver_Config_t** saver_config) {
59 Qnn_ErrorHandle_t error = QNN_SUCCESS;
60 void* lib_handle = dlopen(lib_path.c_str(), RTLD_NOW | RTLD_GLOBAL);
61
62 if (lib_handle == nullptr) {
63 QNN_EXECUTORCH_LOG_ERROR(
64 "Cannot Open QNN library %s, with error: %s",
65 lib_path.c_str(),
66 dlerror());
67 return Error::Internal;
68 }
69
70 // load get_provider function
71 auto get_providers = loadQnnFunction<QnnInterfaceGetProvidersFn*>(
72 lib_handle, "QnnInterface_getProviders");
73
74 if (get_providers == nullptr) {
75 QNN_EXECUTORCH_LOG_ERROR(
76 "QnnImplementation::Load Cannot load symbol "
77 "QnnInterface_getProviders : %s",
78 dlerror());
79 return Error::Internal;
80 }
81
82 // Get QnnInterface Providers
83 std::uint32_t num_providers;
84 const QnnInterface_t** provider_list = nullptr;
85 error = get_providers(&provider_list, &num_providers);
86
87 if (error != QNN_SUCCESS) {
88 QNN_EXECUTORCH_LOG_ERROR(
89 "Qnn Interface failed to get providers. Error %d",
90 QNN_GET_ERROR_CODE(error));
91 return Error::Internal;
92 }
93
94 if (num_providers != required_num_providers_) {
95 QNN_EXECUTORCH_LOG_ERROR(
96 "Qnn Interface Num Providers is "
97 "%d instead of required %d",
98 num_providers,
99 required_num_providers_);
100 return Error::Internal;
101 }
102
103 BackendIdType backend_id = provider_list[0]->backendId;
104
105 // store everything
106 lib_path_to_backend_id_[lib_path] = backend_id;
107
108 // we use lib_path as the first unique key.
109 // Users can get wrong like, he or she assigns
110 // library_path=libQnnHtp_1.so
111 // library_path=libQnnHtp_2.so
112 // for different QnnBackend instances.
113 // So we warning out here.
114 if (loaded_backend_.count(backend_id) > 0) {
115 QNN_EXECUTORCH_LOG_WARN(
116 "lib_path %s is loaded, but backend %d "
117 "already exists. Overwriting previous loaded backend...",
118 lib_path.c_str(),
119 backend_id);
120 }
121 loaded_backend_[backend_id] = provider_list[0];
122
123 if (loaded_lib_handle_.count(backend_id) > 0) {
124 QNN_EXECUTORCH_LOG_WARN("closing %pK...", loaded_lib_handle_[backend_id]);
125
126 int dlclose_error = dlclose(loaded_lib_handle_[backend_id]);
127 if (dlclose_error != 0) {
128 QNN_EXECUTORCH_LOG_WARN(
129 "Sadly, fail to close %pK with error %s",
130 loaded_lib_handle_[backend_id],
131 dlerror());
132 }
133 }
134 loaded_lib_handle_[backend_id] = lib_handle;
135
136 // Saver backend need initialization.
137 Error be_init_st = InitBackend(loaded_lib_handle_[backend_id], saver_config);
138
139 if (be_init_st != Error::Ok) {
140 // backend init fails. clear things
141 lib_path_to_backend_id_.erase(lib_path);
142 loaded_backend_.erase(backend_id);
143
144 int dlclose_error = dlclose(loaded_lib_handle_[backend_id]);
145 if (dlclose_error != 0) {
146 QNN_EXECUTORCH_LOG_WARN(
147 "fail to close %pK after backend-init "
148 "failure, with error %s",
149 loaded_lib_handle_[backend_id],
150 dlerror());
151 }
152
153 loaded_lib_handle_.erase(backend_id);
154 return be_init_st;
155 }
156
157 return Error::Ok;
158 }
159
TerminateAllBackends()160 Error QnnImplementation::TerminateAllBackends() {
161 Error ret_status = Error::Ok;
162
163 loaded_backend_.clear();
164
165 for (auto& it : loaded_lib_handle_) {
166 int dlclose_error = dlclose(it.second);
167 if (dlclose_error != 0) {
168 QNN_EXECUTORCH_LOG_ERROR(
169 "Fail to close QNN backend %d with error %s", it.first, dlerror());
170 ret_status = Error::Internal;
171 }
172 }
173 loaded_lib_handle_.clear();
174 lib_path_to_backend_id_.clear();
175
176 return ret_status;
177 }
178
Load(const QnnSaver_Config_t ** saver_config)179 Error QnnImplementation::Load(const QnnSaver_Config_t** saver_config) {
180 BackendIdType backend_id = QNN_BACKEND_ID_NULL;
181 {
182 const std::lock_guard<std::mutex> lock(be_init_mutex_);
183
184 if (lib_path_to_backend_id_.count(lib_path_) == 0) {
185 Error st = StartBackend(lib_path_, saver_config);
186 ET_CHECK_OR_RETURN_ERROR(
187 st == Error::Ok, Internal, "Fail to start backend");
188 }
189
190 // Get backend ID
191 backend_id = lib_path_to_backend_id_[lib_path_];
192
193 // really don't expect.
194 if (loaded_backend_.count(backend_id) == 0 ||
195 loaded_lib_handle_.count(backend_id) == 0) {
196 QNN_EXECUTORCH_LOG_ERROR(
197 "library %s is loaded but "
198 "loaded backend count=%zu, "
199 "loaded lib_handle count=%zu",
200 lib_path_.c_str(),
201 loaded_backend_.count(backend_id),
202 loaded_lib_handle_.count(backend_id));
203 return Error::Internal;
204 }
205 } // be_init_mutex_ release.
206
207 // Connect QnnInterface
208 qnn_interface_.SetQnnInterface(loaded_backend_[backend_id]);
209
210 return Error::Ok;
211 }
212
GetQnnInterface() const213 const QnnInterface& QnnImplementation::GetQnnInterface() const {
214 if (!qnn_interface_.IsLoaded()) {
215 QNN_EXECUTORCH_LOG_WARN(
216 "GetQnnInterface, returning a QNN interface "
217 "which is not loaded yet.");
218 }
219 return qnn_interface_;
220 }
221 } // namespace qnn
222 } // namespace backends
223 } // namespace executorch
224