diff options
-rw-r--r-- | lmkd/README.md | 5 | ||||
-rw-r--r-- | lmkd/lmkd.c | 105 |
2 files changed, 85 insertions, 25 deletions
diff --git a/lmkd/README.md b/lmkd/README.md index ba2e83d06..656a6ea0a 100644 --- a/lmkd/README.md +++ b/lmkd/README.md | |||
@@ -29,6 +29,11 @@ properties: | |||
29 | ro.config.low_ram: choose between low-memory vs high-performance | 29 | ro.config.low_ram: choose between low-memory vs high-performance |
30 | device. Default = false. | 30 | device. Default = false. |
31 | 31 | ||
32 | ro.lmk.use_minfree_levels: use free memory and file cache thresholds for | ||
33 | making decisions when to kill. This mode works | ||
34 | the same way kernel lowmemorykiller driver used | ||
35 | to work. Default = false | ||
36 | |||
32 | ro.lmk.low: min oom_adj score for processes eligible to be | 37 | ro.lmk.low: min oom_adj score for processes eligible to be |
33 | killed at low vmpressure level. Default = 1001 | 38 | killed at low vmpressure level. Default = 1001 |
34 | (disabled) | 39 | (disabled) |
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c index 5eb3fbe15..151e1dc7b 100644 --- a/lmkd/lmkd.c +++ b/lmkd/lmkd.c | |||
@@ -109,6 +109,7 @@ static int64_t downgrade_pressure; | |||
109 | static bool low_ram_device; | 109 | static bool low_ram_device; |
110 | static bool kill_heaviest_task; | 110 | static bool kill_heaviest_task; |
111 | static unsigned long kill_timeout_ms; | 111 | static unsigned long kill_timeout_ms; |
112 | static bool use_minfree_levels; | ||
112 | 113 | ||
113 | /* data required to handle events */ | 114 | /* data required to handle events */ |
114 | struct event_handler_info { | 115 | struct event_handler_info { |
@@ -972,11 +973,10 @@ static int kill_one_process(struct proc* procp, int min_score_adj, | |||
972 | * Returns the size of the killed processes. | 973 | * Returns the size of the killed processes. |
973 | */ | 974 | */ |
974 | static int find_and_kill_processes(enum vmpressure_level level, | 975 | static int find_and_kill_processes(enum vmpressure_level level, |
975 | int pages_to_free) { | 976 | int min_score_adj, int pages_to_free) { |
976 | int i; | 977 | int i; |
977 | int killed_size; | 978 | int killed_size; |
978 | int pages_freed = 0; | 979 | int pages_freed = 0; |
979 | int min_score_adj = level_oomadj[level]; | ||
980 | 980 | ||
981 | for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) { | 981 | for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) { |
982 | struct proc *procp; | 982 | struct proc *procp; |
@@ -1071,9 +1071,14 @@ static void mp_event_common(int data, uint32_t events __unused) { | |||
1071 | int64_t mem_pressure; | 1071 | int64_t mem_pressure; |
1072 | enum vmpressure_level lvl; | 1072 | enum vmpressure_level lvl; |
1073 | union meminfo mi; | 1073 | union meminfo mi; |
1074 | union zoneinfo zi; | ||
1074 | static struct timeval last_report_tm; | 1075 | static struct timeval last_report_tm; |
1075 | static unsigned long skip_count = 0; | 1076 | static unsigned long skip_count = 0; |
1076 | enum vmpressure_level level = (enum vmpressure_level)data; | 1077 | enum vmpressure_level level = (enum vmpressure_level)data; |
1078 | long other_free = 0, other_file = 0; | ||
1079 | int min_score_adj; | ||
1080 | int pages_to_free = 0; | ||
1081 | int minfree = 0; | ||
1077 | static struct reread_data mem_usage_file_data = { | 1082 | static struct reread_data mem_usage_file_data = { |
1078 | .filename = MEMCG_MEMORY_USAGE, | 1083 | .filename = MEMCG_MEMORY_USAGE, |
1079 | .fd = -1, | 1084 | .fd = -1, |
@@ -1114,11 +1119,40 @@ static void mp_event_common(int data, uint32_t events __unused) { | |||
1114 | skip_count = 0; | 1119 | skip_count = 0; |
1115 | } | 1120 | } |
1116 | 1121 | ||
1117 | if (meminfo_parse(&mi) < 0) { | 1122 | if (meminfo_parse(&mi) < 0 || zoneinfo_parse(&zi) < 0) { |
1118 | ALOGE("Failed to get free memory!"); | 1123 | ALOGE("Failed to get free memory!"); |
1119 | return; | 1124 | return; |
1120 | } | 1125 | } |
1121 | 1126 | ||
1127 | if (use_minfree_levels) { | ||
1128 | int i; | ||
1129 | |||
1130 | other_free = mi.field.nr_free_pages - zi.field.totalreserve_pages; | ||
1131 | if (mi.field.nr_file_pages > (mi.field.shmem + mi.field.unevictable + mi.field.swap_cached)) { | ||
1132 | other_file = (mi.field.nr_file_pages - mi.field.shmem - | ||
1133 | mi.field.unevictable - mi.field.swap_cached); | ||
1134 | } else { | ||
1135 | other_file = 0; | ||
1136 | } | ||
1137 | |||
1138 | min_score_adj = OOM_SCORE_ADJ_MAX + 1; | ||
1139 | for (i = 0; i < lowmem_targets_size; i++) { | ||
1140 | minfree = lowmem_minfree[i]; | ||
1141 | if (other_free < minfree && other_file < minfree) { | ||
1142 | min_score_adj = lowmem_adj[i]; | ||
1143 | break; | ||
1144 | } | ||
1145 | } | ||
1146 | |||
1147 | if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) | ||
1148 | return; | ||
1149 | |||
1150 | /* Free up enough pages to push over the highest minfree level */ | ||
1151 | pages_to_free = lowmem_minfree[lowmem_targets_size - 1] - | ||
1152 | ((other_free < other_file) ? other_free : other_file); | ||
1153 | goto do_kill; | ||
1154 | } | ||
1155 | |||
1122 | if (level == VMPRESS_LEVEL_LOW) { | 1156 | if (level == VMPRESS_LEVEL_LOW) { |
1123 | record_low_pressure_levels(&mi); | 1157 | record_low_pressure_levels(&mi); |
1124 | } | 1158 | } |
@@ -1167,39 +1201,58 @@ static void mp_event_common(int data, uint32_t events __unused) { | |||
1167 | do_kill: | 1201 | do_kill: |
1168 | if (low_ram_device) { | 1202 | if (low_ram_device) { |
1169 | /* For Go devices kill only one task */ | 1203 | /* For Go devices kill only one task */ |
1170 | if (find_and_kill_processes(level, 0) == 0) { | 1204 | if (find_and_kill_processes(level, level_oomadj[level], 0) == 0) { |
1171 | if (debug_process_killing) { | 1205 | if (debug_process_killing) { |
1172 | ALOGI("Nothing to kill"); | 1206 | ALOGI("Nothing to kill"); |
1173 | } | 1207 | } |
1174 | } | 1208 | } |
1175 | } else { | 1209 | } else { |
1176 | /* If pressure level is less than critical and enough free swap then ignore */ | 1210 | int pages_freed; |
1177 | if (level < VMPRESS_LEVEL_CRITICAL && | 1211 | |
1178 | mi.field.free_swap > low_pressure_mem.max_nr_free_pages) { | 1212 | if (!use_minfree_levels) { |
1213 | /* If pressure level is less than critical and enough free swap then ignore */ | ||
1214 | if (level < VMPRESS_LEVEL_CRITICAL && | ||
1215 | mi.field.free_swap > low_pressure_mem.max_nr_free_pages) { | ||
1216 | if (debug_process_killing) { | ||
1217 | ALOGI("Ignoring pressure since %" PRId64 | ||
1218 | " swap pages are available ", | ||
1219 | mi.field.free_swap); | ||
1220 | } | ||
1221 | return; | ||
1222 | } | ||
1223 | /* Free up enough memory to downgrate the memory pressure to low level */ | ||
1224 | if (mi.field.nr_free_pages < low_pressure_mem.max_nr_free_pages) { | ||
1225 | pages_to_free = low_pressure_mem.max_nr_free_pages - | ||
1226 | mi.field.nr_free_pages; | ||
1227 | } else { | ||
1228 | if (debug_process_killing) { | ||
1229 | ALOGI("Ignoring pressure since more memory is " | ||
1230 | "available (%" PRId64 ") than watermark (%" PRId64 ")", | ||
1231 | mi.field.nr_free_pages, low_pressure_mem.max_nr_free_pages); | ||
1232 | } | ||
1233 | return; | ||
1234 | } | ||
1235 | min_score_adj = level_oomadj[level]; | ||
1236 | } else { | ||
1179 | if (debug_process_killing) { | 1237 | if (debug_process_killing) { |
1180 | ALOGI("Ignoring pressure since %" PRId64 | 1238 | ALOGI("Killing because cache %ldkB is below " |
1181 | " swap pages are available ", | 1239 | "limit %ldkB for oom_adj %d\n" |
1182 | mi.field.free_swap); | 1240 | " Free memory is %ldkB %s reserved", |
1241 | other_file * page_k, minfree * page_k, min_score_adj, | ||
1242 | other_free * page_k, other_free >= 0 ? "above" : "below"); | ||
1183 | } | 1243 | } |
1184 | return; | ||
1185 | } | 1244 | } |
1186 | 1245 | ||
1187 | /* Free up enough memory to downgrate the memory pressure to low level */ | 1246 | if (debug_process_killing) { |
1188 | if (mi.field.nr_free_pages < low_pressure_mem.max_nr_free_pages) { | 1247 | ALOGI("Trying to free %d pages", pages_to_free); |
1189 | int pages_to_free = low_pressure_mem.max_nr_free_pages - | 1248 | } |
1190 | mi.field.nr_free_pages; | 1249 | pages_freed = find_and_kill_processes(level, min_score_adj, pages_to_free); |
1250 | if (pages_freed < pages_to_free) { | ||
1191 | if (debug_process_killing) { | 1251 | if (debug_process_killing) { |
1192 | ALOGI("Trying to free %d pages", pages_to_free); | 1252 | ALOGI("Unable to free enough memory (pages freed=%d)", pages_freed); |
1193 | } | ||
1194 | int pages_freed = find_and_kill_processes(level, pages_to_free); | ||
1195 | if (pages_freed < pages_to_free) { | ||
1196 | if (debug_process_killing) { | ||
1197 | ALOGI("Unable to free enough memory (pages freed=%d)", | ||
1198 | pages_freed); | ||
1199 | } | ||
1200 | } else { | ||
1201 | gettimeofday(&last_report_tm, NULL); | ||
1202 | } | 1253 | } |
1254 | } else { | ||
1255 | gettimeofday(&last_report_tm, NULL); | ||
1203 | } | 1256 | } |
1204 | } | 1257 | } |
1205 | } | 1258 | } |
@@ -1409,6 +1462,8 @@ int main(int argc __unused, char **argv __unused) { | |||
1409 | low_ram_device = property_get_bool("ro.config.low_ram", false); | 1462 | low_ram_device = property_get_bool("ro.config.low_ram", false); |
1410 | kill_timeout_ms = | 1463 | kill_timeout_ms = |
1411 | (unsigned long)property_get_int32("ro.lmk.kill_timeout_ms", 0); | 1464 | (unsigned long)property_get_int32("ro.lmk.kill_timeout_ms", 0); |
1465 | use_minfree_levels = | ||
1466 | property_get_bool("ro.lmk.use_minfree_levels", false); | ||
1412 | 1467 | ||
1413 | if (!init()) { | 1468 | if (!init()) { |
1414 | if (!use_inkernel_interface) { | 1469 | if (!use_inkernel_interface) { |