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     if (isDataValid_) {
101         Unpin();
102     }
103 
104     return;
105 }
106 
BeginWrite()107 bool PurgeableMemBase::BeginWrite()
108 {
109     PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString().c_str());
110     std::lock_guard<std::mutex> lock(dataLock_);
111     if (dataPtr_ == nullptr) {
112         return false;
113     }
114     IF_NULL_LOG_ACTION(dataPtr_, "dataPtr is nullptr in BeginWrite", return false);
115     IF_NULL_LOG_ACTION(builder_, "builder_ is nullptr in BeginWrite", return false);
116 
117     Pin();
118     PMState err = PM_OK;
119     do {
120         if (!IfNeedRebuild()) {
121             /* data is not purged, return true */
122             break;
123         }
124         /* data purged, rebuild it */
125         if (BuildContent()) {
126             /* data rebuild succ, return true */
127             AfterRebuildSucc();
128             break;
129         }
130         err = PMB_BUILD_ALL_FAIL;
131     } while (0);
132 
133     if (err == PM_OK) {
134         return true;
135     }
136 
137     PM_HILOG_ERROR(LOG_CORE, "%{public}s: err %{public}s, UxptePut.", __func__, GetPMStateName(err));
138     Unpin();
139     return false;
140 }
141 
EndWrite()142 void PurgeableMemBase::EndWrite()
143 {
144     PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString().c_str());
145     Unpin();
146 }
147 
ModifyContentByBuilder(std::unique_ptr<PurgeableMemBuilder> modifier)148 bool PurgeableMemBase::ModifyContentByBuilder(std::unique_ptr<PurgeableMemBuilder> modifier)
149 {
150     IF_NULL_LOG_ACTION(modifier, "input modifier is nullptr", return false);
151     std::lock_guard<std::mutex> lock(dataLock_);
152     if (!modifier->Build(dataPtr_, dataSizeInput_)) {
153         PM_HILOG_ERROR(LOG_CORE, "%{public}s: modify content by builder fail!!", __func__);
154         return false;
155     }
156     /* log modify */
157     if (builder_) {
158         builder_->AppendBuilder(std::move(modifier));
159     } else {
160         builder_ = std::move(modifier);
161     }
162     return true;
163 }
164 
IfNeedRebuild()165 bool PurgeableMemBase::IfNeedRebuild()
166 {
167     if (buildDataCount_ == 0 || IsPurged()) {
168         return true;
169     }
170     return false;
171 }
172 
AfterRebuildSucc()173 void PurgeableMemBase::AfterRebuildSucc()
174 {
175 }
176 
GetContent()177 void *PurgeableMemBase::GetContent()
178 {
179     std::lock_guard<std::mutex> lock(dataLock_);
180     return dataPtr_;
181 }
182 
GetContentSize()183 size_t PurgeableMemBase::GetContentSize()
184 {
185     std::lock_guard<std::mutex> lock(dataLock_);
186     return dataSizeInput_;
187 }
188 
IsPurged()189 bool PurgeableMemBase::IsPurged()
190 {
191     return false;
192 }
193 
BuildContent()194 bool PurgeableMemBase::BuildContent()
195 {
196     bool succ = false;
197     /* clear content before rebuild */
198     if (memset_s(dataPtr_, RoundUp(dataSizeInput_, PAGE_SIZE), 0, dataSizeInput_) != EOK) {
199         PM_HILOG_ERROR(LOG_CORE, "%{public}s, clear content fail", __func__);
200         return succ;
201     }
202     /* builder_ and dataPtr_ is never nullptr since it is checked by BeginAccess() before */
203     succ = builder_->BuildAll(dataPtr_, dataSizeInput_);
204     if (succ) {
205         buildDataCount_++;
206     }
207     return succ;
208 }
209 
ResizeData(size_t newSize)210 void PurgeableMemBase::ResizeData(size_t newSize)
211 {
212 }
213 
Pin()214 bool PurgeableMemBase::Pin()
215 {
216     return false;
217 }
218 
Unpin()219 bool PurgeableMemBase::Unpin()
220 {
221     return false;
222 }
223 
GetPinStatus() const224 int PurgeableMemBase::GetPinStatus() const
225 {
226     return 0;
227 }
228 
ToString() const229 inline std::string PurgeableMemBase::ToString() const
230 {
231     return "";
232 }
233 
SetRebuildSuccessCallback(std::function<void ()> & callback)234 void PurgeableMemBase::SetRebuildSuccessCallback(std::function<void()> &callback)
235 {
236     if (builder_) {
237         builder_->SetRebuildSuccessCallback(callback);
238     }
239 }
240 
IsDataValid()241 bool PurgeableMemBase::IsDataValid()
242 {
243     return isDataValid_;
244 }
245 
SetDataValid(bool target)246 void PurgeableMemBase::SetDataValid(bool target)
247 {
248     isDataValid_ = target;
249 }
250 } /* namespace PurgeableMem */
251 } /* namespace OHOS */
252