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 (val + align < val || val + align < align) {
37         PM_HILOG_ERROR(LOG_CORE, "%{public}s: Addition overflow!", __func__);
38         return val;
39     }
40     if (align == 0) {
41         return val;
42     }
43     return ((val + align - 1) / align) * align;
44 }
45 
PurgeableMemBase()46 PurgeableMemBase::PurgeableMemBase()
47 {
48 }
49 
~PurgeableMemBase()50 PurgeableMemBase::~PurgeableMemBase()
51 {
52 }
53 
BeginRead()54 bool PurgeableMemBase::BeginRead()
55 {
56     std::lock_guard<std::mutex> lock(dataLock_);
57     if (!isDataValid_) {
58         return false;
59     }
60 
61     bool ret = false;
62     int tryTimes = 0;
63 
64     PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString().c_str());
65     IF_NULL_LOG_ACTION(dataPtr_, "dataPtr is nullptr in BeginRead", return false);
66     IF_NULL_LOG_ACTION(builder_, "builder_ is nullptr in BeginRead", return false);
67     Pin();
68     PMState err = PM_OK;
69     while (true) {
70         if (!IfNeedRebuild()) {
71             PM_HILOG_DEBUG(LOG_CORE, "%{public}s: not purged, return true. MAP_PUR=0x%{public}x",
72                 __func__, MAP_PURGEABLE);
73             ret = true;
74             break;
75         }
76 
77         bool succ = BuildContent();
78         if (succ) {
79             AfterRebuildSucc();
80         }
81         PM_HILOG_DEBUG(LOG_CORE, "%{public}s: purged, built %{public}s", __func__, succ ? "succ" : "fail");
82 
83         tryTimes++;
84         if (!succ || tryTimes > MAX_BUILD_TRYTIMES) {
85             err = PMB_BUILD_ALL_FAIL;
86             break;
87         }
88     }
89 
90     if (!ret) {
91         PM_HILOG_ERROR(LOG_CORE, "%{public}s: err %{public}s, UxptePut. tryTime:%{public}d",
92             __func__, GetPMStateName(err), tryTimes);
93         Unpin();
94     }
95     return ret;
96 }
97 
EndRead()98 void PurgeableMemBase::EndRead()
99 {
100     std::lock_guard<std::mutex> lock(dataLock_);
101     if (isDataValid_) {
102         Unpin();
103     }
104 
105     return;
106 }
107 
BeginWrite()108 bool PurgeableMemBase::BeginWrite()
109 {
110     PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString().c_str());
111     std::lock_guard<std::mutex> lock(dataLock_);
112     if (dataPtr_ == nullptr) {
113         return false;
114     }
115     IF_NULL_LOG_ACTION(dataPtr_, "dataPtr is nullptr in BeginWrite", return false);
116     IF_NULL_LOG_ACTION(builder_, "builder_ is nullptr in BeginWrite", return false);
117 
118     Pin();
119     PMState err = PM_OK;
120     do {
121         if (!IfNeedRebuild()) {
122             /* data is not purged, return true */
123             break;
124         }
125         /* data purged, rebuild it */
126         if (BuildContent()) {
127             /* data rebuild succ, return true */
128             AfterRebuildSucc();
129             break;
130         }
131         err = PMB_BUILD_ALL_FAIL;
132     } while (0);
133 
134     if (err == PM_OK) {
135         return true;
136     }
137 
138     PM_HILOG_ERROR(LOG_CORE, "%{public}s: err %{public}s, UxptePut.", __func__, GetPMStateName(err));
139     Unpin();
140     return false;
141 }
142 
EndWrite()143 void PurgeableMemBase::EndWrite()
144 {
145     std::lock_guard<std::mutex> lock(dataLock_);
146     PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString().c_str());
147     Unpin();
148 }
149 
ModifyContentByBuilder(std::unique_ptr<PurgeableMemBuilder> modifier)150 bool PurgeableMemBase::ModifyContentByBuilder(std::unique_ptr<PurgeableMemBuilder> modifier)
151 {
152     IF_NULL_LOG_ACTION(modifier, "input modifier is nullptr", return false);
153     std::lock_guard<std::mutex> lock(dataLock_);
154     if (!modifier->Build(dataPtr_, dataSizeInput_)) {
155         PM_HILOG_ERROR(LOG_CORE, "%{public}s: modify content by builder fail!!", __func__);
156         return false;
157     }
158     /* log modify */
159     if (builder_) {
160         builder_->AppendBuilder(std::move(modifier));
161     } else {
162         builder_ = std::move(modifier);
163     }
164     return true;
165 }
166 
IfNeedRebuild()167 bool PurgeableMemBase::IfNeedRebuild()
168 {
169     if (buildDataCount_ == 0 || IsPurged()) {
170         return true;
171     }
172     return false;
173 }
174 
AfterRebuildSucc()175 void PurgeableMemBase::AfterRebuildSucc()
176 {
177 }
178 
GetContent()179 void *PurgeableMemBase::GetContent()
180 {
181     std::lock_guard<std::mutex> lock(dataLock_);
182     return dataPtr_;
183 }
184 
GetContentSize()185 size_t PurgeableMemBase::GetContentSize()
186 {
187     std::lock_guard<std::mutex> lock(dataLock_);
188     return dataSizeInput_;
189 }
190 
IsPurged()191 bool PurgeableMemBase::IsPurged()
192 {
193     return false;
194 }
195 
BuildContent()196 bool PurgeableMemBase::BuildContent()
197 {
198     bool succ = false;
199     /* clear content before rebuild */
200     if (memset_s(dataPtr_, RoundUp(dataSizeInput_, PAGE_SIZE), 0, dataSizeInput_) != EOK) {
201         PM_HILOG_ERROR(LOG_CORE, "%{public}s, clear content fail", __func__);
202         return succ;
203     }
204     /* builder_ and dataPtr_ is never nullptr since it is checked by BeginAccess() before */
205     succ = builder_->BuildAll(dataPtr_, dataSizeInput_);
206     if (succ) {
207         buildDataCount_++;
208     }
209     return succ;
210 }
211 
ResizeData(size_t newSize)212 void PurgeableMemBase::ResizeData(size_t newSize)
213 {
214 }
215 
Pin()216 bool PurgeableMemBase::Pin()
217 {
218     return false;
219 }
220 
Unpin()221 bool PurgeableMemBase::Unpin()
222 {
223     return false;
224 }
225 
GetPinStatus() const226 int PurgeableMemBase::GetPinStatus() const
227 {
228     return 0;
229 }
230 
ToString() const231 inline std::string PurgeableMemBase::ToString() const
232 {
233     return "";
234 }
235 
SetRebuildSuccessCallback(std::function<void()> &callback)236 void PurgeableMemBase::SetRebuildSuccessCallback(std::function<void()> &callback)
237 {
238     std::lock_guard<std::mutex> lock(dataLock_);
239     if (builder_) {
240         builder_->SetRebuildSuccessCallback(callback);
241     }
242 }
243 
IsDataValid()244 bool PurgeableMemBase::IsDataValid()
245 {
246     std::lock_guard<std::mutex> lock(dataLock_);
247     return isDataValid_;
248 }
249 
SetDataValid(bool target)250 void PurgeableMemBase::SetDataValid(bool target)
251 {
252     std::lock_guard<std::mutex> lock(dataLock_);
253     isDataValid_ = target;
254 }
255 } /* namespace PurgeableMem */
256 } /* namespace OHOS */
257