xref: /aosp_15_r20/external/executorch/backends/qualcomm/runtime/backends/QnnImplementation.cpp (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
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