• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "HalBufferTracker.h"
18 
19 #include <android-base/macros.h>
20 
21 #include <memory>
22 #include <mutex>
23 #include <set>
24 #include <stack>
25 #include <utility>
26 #include <vector>
27 
28 #include "CpuExecutor.h"
29 #include "HalInterfaces.h"
30 #include "Utils.h"
31 #include "nnapi/TypeUtils.h"
32 
33 namespace android::nn {
34 
create(uint32_t size,std::set<HalPreparedModelRole> roles,const Operand & operand)35 std::shared_ptr<HalManagedBuffer> HalManagedBuffer::create(uint32_t size,
36                                                            std::set<HalPreparedModelRole> roles,
37                                                            const Operand& operand) {
38     std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[size]);
39     if (buffer == nullptr) {
40         return nullptr;
41     }
42     if (isExtension(operand.type)) {
43         LOG(ERROR) << "HalManagedBuffer cannot handle extension operands.";
44         return nullptr;
45     }
46     return std::make_shared<HalManagedBuffer>(std::move(buffer), size, std::move(roles), operand);
47 }
48 
HalManagedBuffer(std::unique_ptr<uint8_t[]> buffer,uint32_t size,std::set<HalPreparedModelRole> roles,const Operand & operand)49 HalManagedBuffer::HalManagedBuffer(std::unique_ptr<uint8_t[]> buffer, uint32_t size,
50                                    std::set<HalPreparedModelRole> roles, const Operand& operand)
51     : kBuffer(std::move(buffer)),
52       kSize(size),
53       kRoles(std::move(roles)),
54       kOperandType(operand.type),
55       kInitialDimensions(operand.dimensions),
56       mUpdatedDimensions(operand.dimensions) {
57     CHECK(!isExtension(kOperandType));
58 }
59 
validateRequest(uint32_t poolIndex,const Request & request,const V1_3::IPreparedModel * preparedModel) const60 ErrorStatus HalManagedBuffer::validateRequest(uint32_t poolIndex, const Request& request,
61                                               const V1_3::IPreparedModel* preparedModel) const {
62     CHECK_LT(poolIndex, request.pools.size());
63     CHECK(std::holds_alternative<Request::MemoryDomainToken>(request.pools[poolIndex]));
64     std::lock_guard<std::mutex> guard(mMutex);
65 
66     bool usedAsInput = false, usedAsOutput = false;
67     for (uint32_t i = 0; i < request.inputs.size(); i++) {
68         if (request.inputs[i].lifetime != Request::Argument::LifeTime::POOL) continue;
69         if (request.inputs[i].location.poolIndex != poolIndex) continue;
70         // Validate if the input role is specified during allocation.
71         if (kRoles.count({preparedModel, IOType::INPUT, i}) == 0) {
72             LOG(ERROR) << "HalManagedBuffer::validateRequest -- invalid buffer role.";
73             return ErrorStatus::INVALID_ARGUMENT;
74         }
75         if (!mInitialized) {
76             LOG(ERROR)
77                     << "HalManagedBuffer::validateRequest -- using uninitialized buffer as input "
78                        "request.";
79             return ErrorStatus::GENERAL_FAILURE;
80         }
81         auto combined = combineDimensions(mUpdatedDimensions, request.inputs[i].dimensions);
82         if (!combined.has_value()) {
83             LOG(ERROR) << "HalManagedBuffer::validateRequest -- incompatible dimensions ("
84                        << toString(mUpdatedDimensions) << " vs "
85                        << toString(request.inputs[i].dimensions) << ")";
86             return ErrorStatus::INVALID_ARGUMENT;
87         }
88         usedAsInput = true;
89     }
90     for (uint32_t i = 0; i < request.outputs.size(); i++) {
91         if (request.outputs[i].lifetime != Request::Argument::LifeTime::POOL) continue;
92         if (request.outputs[i].location.poolIndex != poolIndex) continue;
93         if (usedAsInput || usedAsOutput) {
94             LOG(ERROR) << "HalManagedBuffer::validateRequest -- using the same device memory for "
95                           "input/output or multiple outputs";
96             return ErrorStatus::INVALID_ARGUMENT;
97         }
98         // Validate if the output role is specified during allocation.
99         if (kRoles.count({preparedModel, IOType::OUTPUT, i}) == 0) {
100             LOG(ERROR) << "HalManagedBuffer::validateRequest -- invalid buffer role.";
101             return ErrorStatus::INVALID_ARGUMENT;
102         }
103         auto combined = combineDimensions(kInitialDimensions, request.outputs[i].dimensions);
104         if (!combined.has_value()) {
105             LOG(ERROR) << "HalManagedBuffer::validateRequest -- incompatible dimensions ("
106                        << toString(kInitialDimensions) << " vs "
107                        << toString(request.outputs[i].dimensions) << ")";
108             return ErrorStatus::INVALID_ARGUMENT;
109         }
110         usedAsOutput = true;
111     }
112     return ErrorStatus::NONE;
113 }
114 
validateCopyFrom(const std::vector<uint32_t> & dimensions,uint32_t size) const115 ErrorStatus HalManagedBuffer::validateCopyFrom(const std::vector<uint32_t>& dimensions,
116                                                uint32_t size) const {
117     if (size != kSize) {
118         LOG(ERROR) << "HalManagedBuffer::validateCopyFrom -- invalid memory size: " << kSize
119                    << " vs " << size;
120         return ErrorStatus::INVALID_ARGUMENT;
121     }
122 
123     if (nonExtensionOperandTypeIsScalar(static_cast<int>(kOperandType))) {
124         if (!dimensions.empty()) {
125             LOG(ERROR) << "HalManagedBuffer::validateCopyFrom -- invalid dimensions for scalar "
126                           "operand: "
127                        << toString(dimensions);
128             return ErrorStatus::INVALID_ARGUMENT;
129         }
130         return ErrorStatus::NONE;
131     }
132 
133     if (dimensions.empty()) {
134         if (tensorHasUnspecifiedDimensions(kOperandType, kInitialDimensions)) {
135             LOG(ERROR)
136                     << "HalManagedBuffer::validateCopyFrom -- the initial dimensions are not fully "
137                        "specified and no dimension update is provided: "
138                     << toString(kInitialDimensions);
139             return ErrorStatus::INVALID_ARGUMENT;
140         }
141     } else {
142         if (tensorHasUnspecifiedDimensions(kOperandType, dimensions)) {
143             LOG(ERROR)
144                     << "HalManagedBuffer::validateCopyFrom -- the updated dimensions are not fully "
145                        "specified: "
146                     << toString(dimensions);
147             return ErrorStatus::INVALID_ARGUMENT;
148         }
149     }
150 
151     const auto combined = combineDimensions(kInitialDimensions, dimensions);
152     if (!combined.has_value()) {
153         LOG(ERROR) << "HalManagedBuffer::validateCopyFrom -- incompatible dimensions ("
154                    << toString(kInitialDimensions) << " vs " << toString(dimensions) << ")";
155         return ErrorStatus::INVALID_ARGUMENT;
156     }
157     return ErrorStatus::NONE;
158 }
159 
validateCopyTo(uint32_t size) const160 ErrorStatus HalManagedBuffer::validateCopyTo(uint32_t size) const {
161     if (size != kSize) {
162         LOG(ERROR) << "HalManagedBuffer::validateCopyTo -- invalid memory size: " << kSize << " vs "
163                    << size;
164         return ErrorStatus::INVALID_ARGUMENT;
165     }
166     std::lock_guard<std::mutex> guard(mMutex);
167     if (!mInitialized) {
168         LOG(ERROR) << "HalManagedBuffer::validateCopyTo -- using uninitialized buffer as source.";
169         return ErrorStatus::GENERAL_FAILURE;
170     }
171     return ErrorStatus::NONE;
172 }
173 
updateDimensions(const std::vector<uint32_t> & dimensions)174 bool HalManagedBuffer::updateDimensions(const std::vector<uint32_t>& dimensions) {
175     auto combined = combineDimensions(kInitialDimensions, dimensions);
176     if (!combined.has_value()) {
177         LOG(ERROR) << "HalManagedBuffer::updateDimensions -- incompatible dimensions ("
178                    << toString(kInitialDimensions) << " vs " << toString(dimensions) << ")";
179         return false;
180     }
181     std::lock_guard<std::mutex> guard(mMutex);
182     mUpdatedDimensions = std::move(combined.value());
183     return true;
184 }
185 
setInitialized(bool initialized)186 void HalManagedBuffer::setInitialized(bool initialized) {
187     std::lock_guard<std::mutex> guard(mMutex);
188     mInitialized = initialized;
189 }
190 
add(std::shared_ptr<HalManagedBuffer> buffer)191 std::unique_ptr<HalBufferTracker::Token> HalBufferTracker::add(
192         std::shared_ptr<HalManagedBuffer> buffer) {
193     if (buffer == nullptr) {
194         return nullptr;
195     }
196     std::lock_guard<std::mutex> guard(mMutex);
197     uint32_t token = 0;
198     if (mFreeTokens.empty()) {
199         token = mTokenToBuffers.size();
200         mTokenToBuffers.push_back(std::move(buffer));
201     } else {
202         token = mFreeTokens.top();
203         mFreeTokens.pop();
204         mTokenToBuffers[token] = std::move(buffer);
205     }
206     VLOG(MEMORY) << "HalBufferTracker::add -- new token = " << token;
207     return std::make_unique<Token>(token, shared_from_this());
208 }
209 
get(uint32_t token) const210 std::shared_ptr<HalManagedBuffer> HalBufferTracker::get(uint32_t token) const {
211     std::lock_guard<std::mutex> guard(mMutex);
212     if (mTokenToBuffers.size() <= token || mTokenToBuffers[token] == nullptr) {
213         LOG(ERROR) << "HalBufferTracker::get -- unknown token " << token;
214         return nullptr;
215     }
216     return mTokenToBuffers[token];
217 }
218 
free(uint32_t token)219 void HalBufferTracker::free(uint32_t token) {
220     std::lock_guard<std::mutex> guard(mMutex);
221     CHECK_LT(token, mTokenToBuffers.size());
222     CHECK(mTokenToBuffers[token] != nullptr);
223     VLOG(MEMORY) << "HalBufferTracker::free -- release token = " << token;
224     mTokenToBuffers[token] = nullptr;
225     mFreeTokens.push(token);
226 }
227 
228 }  // namespace android::nn
229