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 #include "los_oom.h"
33 #include "los_init.h"
34 #include "los_vm_dump.h"
35 #include "los_vm_lock.h"
36 #include "los_vm_phys.h"
37 #include "los_vm_filemap.h"
38 #include "los_process_pri.h"
39 #ifdef LOSCFG_BASE_CORE_SWTMR_ENABLE
40 #include "los_swtmr_pri.h"
41 #endif
42
43 #ifdef LOSCFG_FS_VFS
44 #include "console.h"
45 #endif
46
47
48 #ifdef LOSCFG_KERNEL_VM
49
50 LITE_OS_SEC_BSS OomCB *g_oomCB = NULL;
51 static SPIN_LOCK_INIT(g_oomSpinLock);
52
OomScoreProcess(LosProcessCB * candidateProcess)53 LITE_OS_SEC_TEXT_MINOR STATIC UINT32 OomScoreProcess(LosProcessCB *candidateProcess)
54 {
55 UINT32 actualPm;
56
57 #ifndef LOSCFG_KERNEL_SMP
58 (VOID)LOS_MuxAcquire(&candidateProcess->vmSpace->regionMux);
59 #endif
60 /* we only consider actual physical memory here. */
61 OsUProcessPmUsage(candidateProcess->vmSpace, NULL, &actualPm);
62 #ifndef LOSCFG_KERNEL_SMP
63 (VOID)LOS_MuxRelease(&candidateProcess->vmSpace->regionMux);
64 #endif
65 return actualPm;
66 }
67
OomKillProcess(UINTPTR param)68 LITE_OS_SEC_TEXT_MINOR STATIC UINT32 OomKillProcess(UINTPTR param)
69 {
70 /* we will not kill process, and do nothing here */
71 return LOS_OK;
72 }
73
OomForceShrinkMemory(VOID)74 LITE_OS_SEC_TEXT_MINOR STATIC UINT32 OomForceShrinkMemory(VOID)
75 {
76 UINT32 i;
77 UINT32 reclaimMemPages = 0;
78
79 /*
80 * TryShrinkMemory maybe reclaim 0 pages in the first time from active list
81 * to inactive list, and in the second time reclaim memory from inactive list.
82 */
83 for (i = 0; i < MAX_SHRINK_PAGECACHE_TRY; i++) {
84 reclaimMemPages += OsTryShrinkMemory(0);
85 }
86
87 return reclaimMemPages;
88 }
89
OomReclaimPageCache(VOID)90 LITE_OS_SEC_TEXT_MINOR STATIC BOOL OomReclaimPageCache(VOID)
91 {
92 UINT32 totalPm = 0;
93 UINT32 usedPm = 0;
94 BOOL isReclaimMemory = FALSE;
95 UINT32 reclaimMemPages;
96 UINT32 i;
97
98 for (i = 0; i < MAX_SHRINK_PAGECACHE_TRY; i++) {
99 OsVmPhysUsedInfoGet(&usedPm, &totalPm);
100 isReclaimMemory = ((totalPm - usedPm) << PAGE_SHIFT) < g_oomCB->reclaimMemThreshold;
101 if (isReclaimMemory) {
102 /*
103 * we do force memory reclaim from page cache here.
104 * if we get memory, we will reclaim pagecache memory again.
105 * if there is no memory to reclaim, we will return.
106 */
107 reclaimMemPages = OomForceShrinkMemory();
108 if (reclaimMemPages > 0) {
109 continue;
110 }
111 }
112 break;
113 }
114
115 return isReclaimMemory;
116 }
117
118 /*
119 * check is low memory or not, if low memory, try to kill process.
120 * return is kill process or not.
121 */
OomCheckProcess(VOID)122 LITE_OS_SEC_TEXT_MINOR BOOL OomCheckProcess(VOID)
123 {
124 UINT32 totalPm;
125 UINT32 usedPm;
126 BOOL isLowMemory = FALSE;
127
128 /*
129 * spinlock the current core schedule, make sure oom process atomic
130 * spinlock other place entering OomCheckProcess, make sure oom process mutex
131 */
132 LOS_SpinLock(&g_oomSpinLock);
133
134 /* first we will check if we need to reclaim pagecache memory */
135 if (OomReclaimPageCache() == FALSE) {
136 LOS_SpinUnlock(&g_oomSpinLock);
137 goto NO_VICTIM_PROCESS;
138 }
139
140 /* get free bytes */
141 OsVmPhysUsedInfoGet(&usedPm, &totalPm);
142 isLowMemory = ((totalPm - usedPm) << PAGE_SHIFT) < g_oomCB->lowMemThreshold;
143
144 LOS_SpinUnlock(&g_oomSpinLock);
145
146 if (isLowMemory) {
147 PRINTK("[oom] OS is in low memory state\n"
148 "total physical memory: %#x(byte), used: %#x(byte),"
149 "free: %#x(byte), low memory threshold: %#x(byte)\n",
150 totalPm << PAGE_SHIFT, usedPm << PAGE_SHIFT,
151 (totalPm - usedPm) << PAGE_SHIFT, g_oomCB->lowMemThreshold);
152 }
153
154 NO_VICTIM_PROCESS:
155 return isLowMemory;
156 }
157
158 #ifdef LOSCFG_ENABLE_OOM_LOOP_TASK
OomWriteEvent(VOID)159 STATIC VOID OomWriteEvent(VOID)
160 {
161 OsWriteResourceEvent(OS_RESOURCE_EVENT_OOM);
162 }
163 #endif
164
OomInfodump(VOID)165 LITE_OS_SEC_TEXT_MINOR VOID OomInfodump(VOID)
166 {
167 PRINTK("[oom] oom loop task status: %s\n"
168 " oom low memory threshold: %#x(byte)\n"
169 " oom reclaim memory threshold: %#x(byte)\n"
170 " oom check interval: %d(microsecond)\n",
171 g_oomCB->enabled ? "enabled" : "disabled",
172 g_oomCB->lowMemThreshold, g_oomCB->reclaimMemThreshold,
173 g_oomCB->checkInterval);
174 }
175
OomSetLowMemThreashold(UINT32 lowMemThreshold)176 LITE_OS_SEC_TEXT_MINOR VOID OomSetLowMemThreashold(UINT32 lowMemThreshold)
177 {
178 if ((lowMemThreshold > OOM_DEFAULT_LOW_MEM_THRESHOLD_MAX)) {
179 PRINTK("[oom] low memory threshold %#x(byte) invalid,"
180 "should be in [%#x, %#x](byte)\n",
181 lowMemThreshold, OOM_DEFAULT_LOW_MEM_THRESHOLD_MIN,
182 OOM_DEFAULT_LOW_MEM_THRESHOLD_MAX);
183 } else {
184 g_oomCB->lowMemThreshold = lowMemThreshold;
185 PRINTK("[oom] set oom low memory threshold %#x(byte) successful\n",
186 g_oomCB->lowMemThreshold);
187 }
188 }
189
OomSetReclaimMemThreashold(UINT32 reclaimMemThreshold)190 LITE_OS_SEC_TEXT_MINOR VOID OomSetReclaimMemThreashold(UINT32 reclaimMemThreshold)
191 {
192 UINT32 totalPm = 0;
193 UINT32 usedPm = 0;
194
195 OsVmPhysUsedInfoGet(&usedPm, &totalPm);
196 if ((reclaimMemThreshold >= (totalPm << PAGE_SHIFT)) ||
197 (reclaimMemThreshold < g_oomCB->lowMemThreshold)) {
198 PRINTK("[oom] reclaim memory threshold %#x(byte) invalid,"
199 "should be in [%#x, %#x)(byte)\n",
200 reclaimMemThreshold, g_oomCB->lowMemThreshold, (totalPm << PAGE_SHIFT));
201 } else {
202 g_oomCB->reclaimMemThreshold = reclaimMemThreshold;
203 PRINTK("[oom] set oom reclaim memory threshold %#x(byte) successful\n",
204 g_oomCB->reclaimMemThreshold);
205 }
206 }
207
OomSetCheckInterval(UINT32 checkInterval)208 LITE_OS_SEC_TEXT_MINOR VOID OomSetCheckInterval(UINT32 checkInterval)
209 {
210 if ((checkInterval >= OOM_CHECK_MIN) && (checkInterval <= OOM_CHECK_MAX)) {
211 g_oomCB->checkInterval = checkInterval;
212 PRINTK("[oom] set oom check interval (%d)ms successful\n",
213 g_oomCB->checkInterval);
214 } else {
215 PRINTK("[oom] set oom check interval (%d)ms failed, should be in [%d, %d]\n",
216 g_oomCB->checkInterval, OOM_CHECK_MIN, OOM_CHECK_MAX);
217 }
218 }
219
OomTaskInit(VOID)220 LITE_OS_SEC_TEXT_MINOR UINT32 OomTaskInit(VOID)
221 {
222 g_oomCB = (OomCB *)LOS_MemAlloc(m_aucSysMem0, sizeof(OomCB));
223 if (g_oomCB == NULL) {
224 VM_ERR("oom task init failed, malloc OomCB failed.");
225 return LOS_NOK;
226 }
227
228 g_oomCB->lowMemThreshold = OOM_DEFAULT_LOW_MEM_THRESHOLD;
229 g_oomCB->reclaimMemThreshold = OOM_DEFAULT_RECLAIM_MEM_THRESHOLD;
230 g_oomCB->checkInterval = OOM_DEFAULT_CHECK_INTERVAL;
231 g_oomCB->processVictimCB = (OomFn)OomKillProcess;
232 g_oomCB->scoreCB = (OomFn)OomScoreProcess;
233 g_oomCB->enabled = FALSE;
234
235 #ifdef LOSCFG_ENABLE_OOM_LOOP_TASK
236 g_oomCB->enabled = TRUE;
237 UINT32 ret = LOS_SwtmrCreate(g_oomCB->checkInterval, LOS_SWTMR_MODE_PERIOD, (SWTMR_PROC_FUNC)OomWriteEvent,
238 &g_oomCB->swtmrID, (UINTPTR)g_oomCB);
239 if (ret != LOS_OK) {
240 return ret;
241 }
242
243 return LOS_SwtmrStart(g_oomCB->swtmrID);
244 #else
245 return LOS_OK;
246 #endif
247 }
248
249 LOS_MODULE_INIT(OomTaskInit, LOS_INIT_LEVEL_KMOD_TASK);
250
251 #endif
252
253