1 /*
2 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this list of
9 * conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 * of conditions and the following disclaimer in the documentation and/or other materials
13 * provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16 * to endorse or promote products derived from this software without specific prior written
17 * permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #ifdef LOSCFG_FS_VFS
33
34 #include "fs/file.h"
35 #include "los_vm_filemap.h"
36
37 #ifdef LOSCFG_KERNEL_VM
38
39 /* unmap a lru page by map record info caller need lru lock */
OsUnmapPageLocked(LosFilePage * page,LosMapInfo * info)40 VOID OsUnmapPageLocked(LosFilePage *page, LosMapInfo *info)
41 {
42 if (page == NULL || info == NULL) {
43 VM_ERR("UnmapPage error input null!");
44 return;
45 }
46 page->n_maps--;
47 LOS_ListDelete(&info->node);
48 LOS_AtomicDec(&page->vmPage->refCounts);
49 LOS_ArchMmuUnmap(info->archMmu, info->vaddr, 1);
50 LOS_MemFree(m_aucSysMem0, info);
51 }
52
OsUnmapAllLocked(LosFilePage * page)53 VOID OsUnmapAllLocked(LosFilePage *page)
54 {
55 LosMapInfo *info = NULL;
56 LosMapInfo *next = NULL;
57 LOS_DL_LIST *immap = &page->i_mmap;
58
59 LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(info, next, immap, LosMapInfo, node) {
60 OsUnmapPageLocked(page, info);
61 }
62 }
63
64 /* add a new lru node to lru list, lruType can be file or anon */
OsLruCacheAdd(LosFilePage * fpage,enum OsLruList lruType)65 VOID OsLruCacheAdd(LosFilePage *fpage, enum OsLruList lruType)
66 {
67 UINT32 intSave;
68 LosVmPhysSeg *physSeg = fpage->physSeg;
69 LosVmPage *page = fpage->vmPage;
70
71 LOS_SpinLockSave(&physSeg->lruLock, &intSave);
72 OsSetPageActive(page);
73 OsCleanPageReferenced(page);
74 physSeg->lruSize[lruType]++;
75 LOS_ListTailInsert(&physSeg->lruList[lruType], &fpage->lru);
76
77 LOS_SpinUnlockRestore(&physSeg->lruLock, intSave);
78 }
79
80 /* delete a lru node, caller need hold lru_lock */
OsLruCacheDel(LosFilePage * fpage)81 VOID OsLruCacheDel(LosFilePage *fpage)
82 {
83 LosVmPhysSeg *physSeg = fpage->physSeg;
84 int type = OsIsPageActive(fpage->vmPage) ? VM_LRU_ACTIVE_FILE : VM_LRU_INACTIVE_FILE;
85
86 physSeg->lruSize[type]--;
87 LOS_ListDelete(&fpage->lru);
88 }
89
OsInactiveListIsLow(LosVmPhysSeg * physSeg)90 BOOL OsInactiveListIsLow(LosVmPhysSeg *physSeg)
91 {
92 return (physSeg->lruSize[VM_LRU_ACTIVE_FILE] >
93 physSeg->lruSize[VM_LRU_INACTIVE_FILE]) ? TRUE : FALSE;
94 }
95
96 /* move a page from inactive list to active list head */
OsMoveToActiveList(LosFilePage * fpage)97 STATIC INLINE VOID OsMoveToActiveList(LosFilePage *fpage)
98 {
99 LosVmPhysSeg *physSeg = fpage->physSeg;
100
101 physSeg->lruSize[VM_LRU_ACTIVE_FILE]++;
102 physSeg->lruSize[VM_LRU_INACTIVE_FILE]--;
103 LOS_ListDelete(&fpage->lru);
104 LOS_ListTailInsert(&physSeg->lruList[VM_LRU_ACTIVE_FILE], &fpage->lru);
105 }
106
107 /* move a page from active list to inactive list head */
OsMoveToInactiveList(LosFilePage * fpage)108 STATIC INLINE VOID OsMoveToInactiveList(LosFilePage *fpage)
109 {
110 LosVmPhysSeg *physSeg = fpage->physSeg;
111
112 physSeg->lruSize[VM_LRU_ACTIVE_FILE]--;
113 physSeg->lruSize[VM_LRU_INACTIVE_FILE]++;
114 LOS_ListDelete(&fpage->lru);
115 LOS_ListTailInsert(&physSeg->lruList[VM_LRU_INACTIVE_FILE], &fpage->lru);
116 }
117
118 /* move a page to the most active pos in lru list(active head) */
OsMoveToActiveHead(LosFilePage * fpage)119 STATIC INLINE VOID OsMoveToActiveHead(LosFilePage *fpage)
120 {
121 LosVmPhysSeg *physSeg = fpage->physSeg;
122 LOS_ListDelete(&fpage->lru);
123 LOS_ListTailInsert(&physSeg->lruList[VM_LRU_ACTIVE_FILE], &fpage->lru);
124 }
125
126 /* move a page to the most active pos in lru list(inactive head) */
OsMoveToInactiveHead(LosFilePage * fpage)127 STATIC INLINE VOID OsMoveToInactiveHead(LosFilePage *fpage)
128 {
129 LosVmPhysSeg *physSeg = fpage->physSeg;
130 LOS_ListDelete(&fpage->lru);
131 LOS_ListTailInsert(&physSeg->lruList[VM_LRU_INACTIVE_FILE], &fpage->lru);
132 }
133
134 /* page referced add: (call by page cache get)
135 ----------inactive----------|----------active------------
136 [ref:0,act:0], [ref:1,act:0]|[ref:0,act:1], [ref:1,act:1]
137 ref:0, act:0 --> ref:1, act:0
138 ref:1, act:0 --> ref:0, act:1
139 ref:0, act:1 --> ref:1, act:1
140 */
OsPageRefIncLocked(LosFilePage * fpage)141 VOID OsPageRefIncLocked(LosFilePage *fpage)
142 {
143 BOOL isOrgActive;
144 UINT32 intSave;
145 LosVmPage *page = NULL;
146
147 if (fpage == NULL) {
148 return;
149 }
150
151 LOS_SpinLockSave(&fpage->physSeg->lruLock, &intSave);
152
153 page = fpage->vmPage;
154 isOrgActive = OsIsPageActive(page);
155
156 if (OsIsPageReferenced(page) && !OsIsPageActive(page)) {
157 OsCleanPageReferenced(page);
158 OsSetPageActive(page);
159 } else if (!OsIsPageReferenced(page)) {
160 OsSetPageReferenced(page);
161 }
162
163 if (!isOrgActive && OsIsPageActive(page)) {
164 /* move inactive to active */
165 OsMoveToActiveList(fpage);
166 /* no change, move head */
167 } else {
168 if (OsIsPageActive(page)) {
169 OsMoveToActiveHead(fpage);
170 } else {
171 OsMoveToInactiveHead(fpage);
172 }
173 }
174
175 LOS_SpinUnlockRestore(&fpage->physSeg->lruLock, intSave);
176 }
177
178 /* page referced dec: (call by shrinker)
179 ----------inactive----------|----------active------------
180 [ref:0,act:0], [ref:1,act:0]|[ref:0,act:1], [ref:1,act:1]
181 ref:1, act:1 --> ref:0, act:1
182 ref:0, act:1 --> ref:1, act:0
183 ref:1, act:0 --> ref:0, act:0
184 */
OsPageRefDecNoLock(LosFilePage * fpage)185 VOID OsPageRefDecNoLock(LosFilePage *fpage)
186 {
187 BOOL isOrgActive;
188 LosVmPage *page = NULL;
189
190 if (fpage == NULL) {
191 return;
192 }
193
194 page = fpage->vmPage;
195 isOrgActive = OsIsPageActive(page);
196
197 if (!OsIsPageReferenced(page) && OsIsPageActive(page)) {
198 OsCleanPageActive(page);
199 OsSetPageReferenced(page);
200 } else if (OsIsPageReferenced(page)) {
201 OsCleanPageReferenced(page);
202 }
203
204 if (isOrgActive && !OsIsPageActive(page)) {
205 OsMoveToInactiveList(fpage);
206 }
207 }
208
OsShrinkActiveList(LosVmPhysSeg * physSeg,int nScan)209 VOID OsShrinkActiveList(LosVmPhysSeg *physSeg, int nScan)
210 {
211 LosFilePage *fpage = NULL;
212 LosFilePage *fnext = NULL;
213 LOS_DL_LIST *activeFile = &physSeg->lruList[VM_LRU_ACTIVE_FILE];
214
215 LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, activeFile, LosFilePage, lru) {
216 if (LOS_SpinTrylock(&fpage->mapping->list_lock) != LOS_OK) {
217 continue;
218 }
219
220 /* happened when caller hold cache lock and try reclaim this page */
221 if (OsIsPageLocked(fpage->vmPage)) {
222 LOS_SpinUnlock(&fpage->mapping->list_lock);
223 continue;
224 }
225
226 if (OsIsPageMapped(fpage) && (fpage->flags & VM_MAP_REGION_FLAG_PERM_EXECUTE)) {
227 LOS_SpinUnlock(&fpage->mapping->list_lock);
228 continue;
229 }
230
231 OsPageRefDecNoLock(fpage);
232
233 LOS_SpinUnlock(&fpage->mapping->list_lock);
234
235 if (--nScan <= 0) {
236 break;
237 }
238 }
239 }
240
OsShrinkInactiveList(LosVmPhysSeg * physSeg,int nScan,LOS_DL_LIST * list)241 int OsShrinkInactiveList(LosVmPhysSeg *physSeg, int nScan, LOS_DL_LIST *list)
242 {
243 UINT32 nrReclaimed = 0;
244 LosVmPage *page = NULL;
245 SPIN_LOCK_S *flock = NULL;
246 LosFilePage *fpage = NULL;
247 LosFilePage *fnext = NULL;
248 LosFilePage *ftemp = NULL;
249 LOS_DL_LIST *inactive_file = &physSeg->lruList[VM_LRU_INACTIVE_FILE];
250
251 LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, inactive_file, LosFilePage, lru) {
252 flock = &fpage->mapping->list_lock;
253
254 if (LOS_SpinTrylock(flock) != LOS_OK) {
255 continue;
256 }
257
258 page = fpage->vmPage;
259 if (OsIsPageLocked(page)) {
260 LOS_SpinUnlock(flock);
261 continue;
262 }
263
264 if (OsIsPageMapped(fpage) && (OsIsPageDirty(page) || (fpage->flags & VM_MAP_REGION_FLAG_PERM_EXECUTE))) {
265 LOS_SpinUnlock(flock);
266 continue;
267 }
268
269 if (OsIsPageDirty(page)) {
270 ftemp = OsDumpDirtyPage(fpage);
271 if (ftemp != NULL) {
272 LOS_ListTailInsert(list, &ftemp->node);
273 }
274 }
275
276 OsDeletePageCacheLru(fpage);
277 LOS_SpinUnlock(flock);
278 nrReclaimed++;
279
280 if (--nScan <= 0) {
281 break;
282 }
283 }
284
285 return nrReclaimed;
286 }
287
288 #ifdef LOSCFG_FS_VFS
OsTryShrinkMemory(size_t nPage)289 int OsTryShrinkMemory(size_t nPage)
290 {
291 UINT32 intSave;
292 size_t totalPages;
293 size_t nReclaimed = 0;
294 LosVmPhysSeg *physSeg = NULL;
295 UINT32 index;
296 LOS_DL_LIST_HEAD(dirtyList);
297 LosFilePage *fpage = NULL;
298 LosFilePage *fnext = NULL;
299
300 if (nPage == 0) {
301 nPage = VM_FILEMAP_MIN_SCAN;
302 }
303
304 if (nPage > VM_FILEMAP_MAX_SCAN) {
305 nPage = VM_FILEMAP_MAX_SCAN;
306 }
307
308 for (index = 0; index < g_vmPhysSegNum; index++) {
309 physSeg = &g_vmPhysSeg[index];
310 LOS_SpinLockSave(&physSeg->lruLock, &intSave);
311 totalPages = physSeg->lruSize[VM_LRU_ACTIVE_FILE] + physSeg->lruSize[VM_LRU_INACTIVE_FILE];
312 if (totalPages < VM_FILEMAP_MIN_SCAN) {
313 LOS_SpinUnlockRestore(&physSeg->lruLock, intSave);
314 continue;
315 }
316
317 if (OsInactiveListIsLow(physSeg)) {
318 OsShrinkActiveList(physSeg, (nPage < VM_FILEMAP_MIN_SCAN) ? VM_FILEMAP_MIN_SCAN : nPage);
319 }
320
321 nReclaimed += OsShrinkInactiveList(physSeg, nPage, &dirtyList);
322 LOS_SpinUnlockRestore(&physSeg->lruLock, intSave);
323
324 if (nReclaimed >= nPage) {
325 break;
326 }
327 }
328
329 LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(fpage, fnext, &dirtyList, LosFilePage, node) {
330 OsDoFlushDirtyPage(fpage);
331 }
332
333 return nReclaimed;
334 }
335 #else
OsTryShrinkMemory(size_t nPage)336 int OsTryShrinkMemory(size_t nPage)
337 {
338 return 0;
339 }
340 #endif
341 #endif
342
343 #endif
344