1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <sys/mman.h> /* mmap */
17
18 #include "securec.h"
19 #include "pm_util.h"
20 #include "pm_state_c.h"
21 #include "pm_smartptr_util.h"
22 #include "pm_log.h"
23
24 #include "purgeable_mem_base.h"
25
26 namespace OHOS {
27 namespace PurgeableMem {
28 #ifdef LOG_TAG
29 #undef LOG_TAG
30 #endif
31 #define LOG_TAG "PurgeableMem"
32 const int MAX_BUILD_TRYTIMES = 3;
33
RoundUp(size_t val,size_t align)34 static inline size_t RoundUp(size_t val, size_t align)
35 {
36 if (align == 0) {
37 return val;
38 }
39 return ((val + align - 1) / align) * align;
40 }
41
PurgeableMemBase()42 PurgeableMemBase::PurgeableMemBase()
43 {
44 }
45
~PurgeableMemBase()46 PurgeableMemBase::~PurgeableMemBase()
47 {
48 }
49
BeginRead()50 bool PurgeableMemBase::BeginRead()
51 {
52 bool succ = false;
53 bool ret = false;
54 int tryTimes = 0;
55
56 PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString().c_str());
57 IF_NULL_LOG_ACTION(dataPtr_, "dataPtr is nullptr in BeginRead", return false);
58 IF_NULL_LOG_ACTION(builder_, "builder_ is nullptr in BeginRead", return false);
59 Pin();
60 PMState err = PM_OK;
61 while (true) {
62 try {
63 rwlock_.lock_shared();
64 } catch (...) {
65 err = PM_LOCK_READ_FAIL;
66 break;
67 }
68 if (!IfNeedRebuild_()) {
69 PM_HILOG_DEBUG(LOG_CORE, "%{public}s: not purged, return true. MAP_PUR=0x%{public}x",
70 __func__, MAP_PURGEABLE);
71 ret = true;
72 break;
73 }
74 /* data is purged, will rebuild it */
75 rwlock_.unlock_shared();
76 try {
77 rwlock_.lock();
78 } catch (...) {
79 err = PM_LOCK_WRITE_FAIL;
80 break;
81 }
82 if (IfNeedRebuild_()) {
83 succ = BuildContent_();
84 if (succ) {
85 AfterRebuildSucc();
86 }
87 PM_HILOG_DEBUG(LOG_CORE, "%{public}s: purged, built %{public}s", __func__, succ ? "succ" : "fail");
88 }
89 tryTimes++;
90 rwlock_.unlock();
91 if (!succ || tryTimes > MAX_BUILD_TRYTIMES) {
92 err = PMB_BUILD_ALL_FAIL;
93 break;
94 }
95 }
96
97 if (!ret) {
98 PM_HILOG_ERROR(LOG_CORE, "%{public}s: err %{public}s, UxptePut. tryTime:%{public}d",
99 __func__, GetPMStateName(err), tryTimes);
100 Unpin();
101 }
102 return ret;
103 }
104
EndRead()105 void PurgeableMemBase::EndRead()
106 {
107 PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString().c_str());
108 rwlock_.unlock_shared();
109 Unpin();
110 }
111
BeginWrite()112 bool PurgeableMemBase::BeginWrite()
113 {
114 PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString().c_str());
115 if (dataPtr_ == nullptr) {
116 return false;
117 }
118 IF_NULL_LOG_ACTION(dataPtr_, "dataPtr is nullptr in BeginWrite", return false);
119 IF_NULL_LOG_ACTION(builder_, "builder_ is nullptr in BeginWrite", return false);
120
121 Pin();
122 PMState err = PM_OK;
123 do {
124 try {
125 rwlock_.lock();
126 } catch (...) {
127 err = PM_LOCK_WRITE_FAIL;
128 break;
129 }
130 if (!IfNeedRebuild_()) {
131 /* data is not purged, return true */
132 break;
133 }
134 /* data purged, rebuild it */
135 if (BuildContent_()) {
136 /* data rebuild succ, return true */
137 AfterRebuildSucc();
138 break;
139 }
140 err = PMB_BUILD_ALL_FAIL;
141 } while (0);
142
143 if (err == PM_OK) {
144 return true;
145 }
146
147 rwlock_.unlock();
148 PM_HILOG_ERROR(LOG_CORE, "%{public}s: err %{public}s, UxptePut.", __func__, GetPMStateName(err));
149 Unpin();
150 return false;
151 }
152
EndWrite()153 void PurgeableMemBase::EndWrite()
154 {
155 PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString().c_str());
156 rwlock_.unlock();
157 Unpin();
158 }
159
BeginReadWithDataLock()160 bool PurgeableMemBase::BeginReadWithDataLock()
161 {
162 if (!isDataValid_) {
163 return false;
164 }
165
166 std::lock_guard<ffrt::mutex> lock(dataLock_);
167 if (!isDataValid_) {
168 return false;
169 }
170
171 bool succ = false;
172 bool ret = false;
173 int tryTimes = 0;
174
175 PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString().c_str());
176 IF_NULL_LOG_ACTION(dataPtr_, "dataPtr is nullptr in BeginRead", return false);
177 IF_NULL_LOG_ACTION(builder_, "builder_ is nullptr in BeginRead", return false);
178 Pin();
179 PMState err = PM_OK;
180 while (true) {
181 if (!IfNeedRebuild_()) {
182 PM_HILOG_DEBUG(LOG_CORE, "%{public}s: not purged, return true. MAP_PUR=0x%{public}x",
183 __func__, MAP_PURGEABLE);
184 ret = true;
185 break;
186 }
187
188 succ = BuildContent_();
189 if (succ) {
190 AfterRebuildSucc();
191 }
192 PM_HILOG_DEBUG(LOG_CORE, "%{public}s: purged, built %{public}s", __func__, succ ? "succ" : "fail");
193
194 tryTimes++;
195 if (!succ || tryTimes > MAX_BUILD_TRYTIMES) {
196 err = PMB_BUILD_ALL_FAIL;
197 break;
198 }
199 }
200
201 if (!ret) {
202 PM_HILOG_ERROR(LOG_CORE, "%{public}s: err %{public}s, UxptePut. tryTime:%{public}d",
203 __func__, GetPMStateName(err), tryTimes);
204 Unpin();
205 }
206 return ret;
207 }
208
EndReadWithDataLock()209 void PurgeableMemBase::EndReadWithDataLock()
210 {
211 if (isDataValid_) {
212 EndRead();
213 }
214
215 return;
216 }
217
ModifyContentByBuilder(std::unique_ptr<PurgeableMemBuilder> modifier)218 bool PurgeableMemBase::ModifyContentByBuilder(std::unique_ptr<PurgeableMemBuilder> modifier)
219 {
220 IF_NULL_LOG_ACTION(modifier, "input modifier is nullptr", return false);
221 if (!modifier->Build(dataPtr_, dataSizeInput_)) {
222 PM_HILOG_ERROR(LOG_CORE, "%{public}s: modify content by builder fail!!", __func__);
223 return false;
224 }
225 /* log modify */
226 if (builder_) {
227 builder_->AppendBuilder(std::move(modifier));
228 } else {
229 builder_ = std::move(modifier);
230 }
231 return true;
232 }
233
IfNeedRebuild_()234 bool PurgeableMemBase::IfNeedRebuild_()
235 {
236 if (buildDataCount_ == 0 || IsPurged()) {
237 return true;
238 }
239 return false;
240 }
241
AfterRebuildSucc()242 void PurgeableMemBase::AfterRebuildSucc()
243 {
244 }
245
GetContent()246 void *PurgeableMemBase::GetContent()
247 {
248 return dataPtr_;
249 }
250
GetContentSize()251 size_t PurgeableMemBase::GetContentSize()
252 {
253 return dataSizeInput_;
254 }
255
IsPurged()256 bool PurgeableMemBase::IsPurged()
257 {
258 return false;
259 }
260
BuildContent_()261 bool PurgeableMemBase::BuildContent_()
262 {
263 bool succ = false;
264 /* clear content before rebuild */
265 if (memset_s(dataPtr_, RoundUp(dataSizeInput_, PAGE_SIZE), 0, dataSizeInput_) != EOK) {
266 PM_HILOG_ERROR(LOG_CORE, "%{public}s, clear content fail", __func__);
267 return succ;
268 }
269 /* builder_ and dataPtr_ is never nullptr since it is checked by BeginAccess() before */
270 succ = builder_->BuildAll(dataPtr_, dataSizeInput_);
271 if (succ) {
272 buildDataCount_++;
273 }
274 return succ;
275 }
276
ResizeData(size_t newSize)277 void PurgeableMemBase::ResizeData(size_t newSize)
278 {
279 }
280
Pin()281 bool PurgeableMemBase::Pin()
282 {
283 return false;
284 }
285
Unpin()286 bool PurgeableMemBase::Unpin()
287 {
288 return false;
289 }
290
GetPinStatus() const291 int PurgeableMemBase::GetPinStatus() const
292 {
293 return 0;
294 }
295
ToString() const296 inline std::string PurgeableMemBase::ToString() const
297 {
298 return "";
299 }
300
SetRebuildSuccessCallback(std::function<void ()> & callback)301 void PurgeableMemBase::SetRebuildSuccessCallback(std::function<void()> &callback)
302 {
303 if (builder_) {
304 builder_->SetRebuildSuccessCallback(callback);
305 }
306 }
307
IsDataValid()308 bool PurgeableMemBase::IsDataValid()
309 {
310 return isDataValid_;
311 }
312
SetDataValid(bool target)313 void PurgeableMemBase::SetDataValid(bool target)
314 {
315 isDataValid_ = target;
316 }
317 } /* namespace PurgeableMem */
318 } /* namespace OHOS */
319