AVERTISSEMENT : N'utilisez pas ce correctif si vous avez activé l'échange, car deux utilisateurs ont signalé des effets pires. J'ai seulement testé ce patch avec le swap désactivé dans le noyau ! (c'est-à-dire que CONFIG_SWAP n'est pas défini)
Jusqu'à nouvel ordre (ou si quelqu'un propose quelque chose de mieux), j'utilise (et cela fonctionne, pour moi) le patch suivant afin d'éviter tout blocage de disque / gel du système d'exploitation lorsque vous êtes sur le point d'exécuter Out Of Memory et donc le OOM-killer se déclenche dès que possible (max 1 sec):
revision 3
preliminary patch to avoid disk thrashing (constant reading) under memory pressure before OOM-killer triggers
more info: https://gist.github.com/constantoverride/84eba764f487049ed642eb2111a20830
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 32699b2..7636498 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -208,7 +208,7 @@ enum lru_list {
#define for_each_lru(lru) for (lru = 0; lru < NR_LRU_LISTS; lru++)
-#define for_each_evictable_lru(lru) for (lru = 0; lru <= LRU_ACTIVE_FILE; lru++)
+#define for_each_evictable_lru(lru) for (lru = 0; lru <= LRU_INACTIVE_FILE; lru++)
static inline int is_file_lru(enum lru_list lru)
{
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 03822f8..1f3ffb5 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2086,9 +2086,9 @@ static unsigned long shrink_list(enum lr
struct scan_control *sc)
{
if (is_active_lru(lru)) {
- if (inactive_list_is_low(lruvec, is_file_lru(lru),
- memcg, sc, true))
- shrink_active_list(nr_to_scan, lruvec, sc, lru);
+ //if (inactive_list_is_low(lruvec, is_file_lru(lru),
+ // memcg, sc, true))
+ // shrink_active_list(nr_to_scan, lruvec, sc, lru);
return 0;
}
@@ -2234,7 +2234,7 @@ static void get_scan_count(struct lruvec *lruvec, struct mem_cgroup *memcg,
anon = lruvec_lru_size(lruvec, LRU_ACTIVE_ANON, MAX_NR_ZONES) +
lruvec_lru_size(lruvec, LRU_INACTIVE_ANON, MAX_NR_ZONES);
- file = lruvec_lru_size(lruvec, LRU_ACTIVE_FILE, MAX_NR_ZONES) +
+ file = //lruvec_lru_size(lruvec, LRU_ACTIVE_FILE, MAX_NR_ZONES) +
lruvec_lru_size(lruvec, LRU_INACTIVE_FILE, MAX_NR_ZONES);
spin_lock_irq(&pgdat->lru_lock);
@@ -2345,7 +2345,7 @@ static void shrink_node_memcg(struct pglist_data *pgdat, struct mem_cgroup *memc
sc->priority == DEF_PRIORITY);
blk_start_plug(&plug);
- while (nr[LRU_INACTIVE_ANON] || nr[LRU_ACTIVE_FILE] ||
+ while (nr[LRU_INACTIVE_ANON] || //nr[LRU_ACTIVE_FILE] ||
nr[LRU_INACTIVE_FILE]) {
unsigned long nr_anon, nr_file, percentage;
unsigned long nr_scanned;
@@ -2372,7 +2372,8 @@ static void shrink_node_memcg(struct pglist_data *pgdat, struct mem_cgroup *memc
* stop reclaiming one LRU and reduce the amount scanning
* proportional to the original scan target.
*/
- nr_file = nr[LRU_INACTIVE_FILE] + nr[LRU_ACTIVE_FILE];
+ nr_file = nr[LRU_INACTIVE_FILE] //+ nr[LRU_ACTIVE_FILE]
+ ;
nr_anon = nr[LRU_INACTIVE_ANON] + nr[LRU_ACTIVE_ANON];
/*
@@ -2391,7 +2392,8 @@ static void shrink_node_memcg(struct pglist_data *pgdat, struct mem_cgroup *memc
percentage = nr_anon * 100 / scan_target;
} else {
unsigned long scan_target = targets[LRU_INACTIVE_FILE] +
- targets[LRU_ACTIVE_FILE] + 1;
+ //targets[LRU_ACTIVE_FILE] +
+ 1;
lru = LRU_FILE;
percentage = nr_file * 100 / scan_target;
}
@@ -2409,10 +2411,12 @@ static void shrink_node_memcg(struct pgl
nr[lru] = targets[lru] * (100 - percentage) / 100;
nr[lru] -= min(nr[lru], nr_scanned);
+ if (LRU_FILE != lru) { //avoid this block for LRU_ACTIVE_FILE
lru += LRU_ACTIVE;
nr_scanned = targets[lru] - nr[lru];
nr[lru] = targets[lru] * (100 - percentage) / 100;
nr[lru] -= min(nr[lru], nr_scanned);
+ }
scan_adjusted = true;
}
Malheureusement, les onglets ci-dessus ont été convertis en espaces, donc si vous voulez le patch brut, c'est ici.
Ce que fait ce patch n'est pas d'expulser le Active(file)
pages lorsque la mémoire est sollicitée et ne cause donc pas kswapd0
(mais vu dans iotop
comme chaque programme lui-même) pour relire les pages exécutables de chaque processus en cours d'exécution chaque fois qu'il y a un changement de contexte afin de permettre au programme de (continuer à) s'exécuter. Ainsi, une tonne de raclements de disque est évitée et le système d'exploitation ne se fige pas dans une analyse.
Ce qui précède a été testé avec le noyau 4.18.5 (et teste maintenant 4.18.7) dans le dom0 (Fedora 25) de Qubes OS 4.0 et toutes les machines virtuelles (Fedora 28) que j'utilise.
Pour la première version de ce patch, qui fonctionne aussi (apparemment), voir le EDIT
sur la question même à laquelle ceci est une réponse.
MISE À JOUR : Après avoir utilisé ce patch pendant un certain temps sur un ordinateur portable ArchLinux avec 16 Go de RAM (moins 512 Mo réservés pour la carte graphique intégrée) et aucun swap (désactivé également dans le noyau), je peux dire que le système peut manquer de mémoire plus tôt que sans le le9d.patch (rev. 3), et donc OOM-killer se déclenche pour Xorg ou chromium ou autre alors qu'il n'aurait pas pu le faire sans le patch. Et donc comme atténuation, cela semble fonctionner pour moi jusqu'à présent, j'ai exécuté echo 1 > /proc/sys/vm/drop_caches
chaque fois que le Active(file)
le nombre dans /proc/meminfo est supérieur à 2G, soit 2000000 Ko (par exemple, obtenez le nombre de Ko via ce code :grep 'Active(file):' /proc/meminfo|tr -d ' '|cut -f2 -d:|sed 's/kB//'
) et faire cette vérification avec un sleep 5
ensuite. Mais dernièrement, pour compiler firefox-hg dans /tmp qui est tmpfs et qui utilise finalement 12G et s'assurer qu'il ne soit pas tué par OOM, j'ai utilisé 500000 au lieu de 2000000 KB. C'est certainement mieux que de geler tout le système (c'est-à-dire sans le9d.patch), ce qui se serait produit dans ce cas de compilation Firefox. Sans cette vérification, Active(file)
ne dépasse pas 4G, mais cela suffit pour OOM-kill Xorg si quelque chose veut plus de mémoire, comme dans ce cas de compilation firefox ou même lors de la copie de plusieurs gigaoctets via le commandant de minuit (si je me souviens bien).
Le paramètre memory.min dans le contrôleur de mémoire cgroups-v2 devrait aider.
À savoir, permettez-moi de citer :
"Protection de la mémoire dure. Si l'utilisation de la mémoire d'un groupe de contrôle est dans sa limite minimale effective, la mémoire du groupe de contrôle ne sera récupérée sous aucune condition. S'il n'y a pas de mémoire récupérable non protégée disponible, OOM killer est invoqué."
https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html
Pour répondre à la question, voici un patch simple/préliminaire pour ne pas évincer Active(file)
(comme on le voit dans /proc/meminfo
) s'il est inférieur à 256 Mio, cela semble fonctionner correctement (pas d'écrasement de disque) avec linux-stable 5.2.4 :
diff --git a/mm/vmscan.c b/mm/vmscan.c
index dbdc46a84f63..7a0b7e32ff45 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2445,6 +2445,13 @@ static void get_scan_count(struct lruvec *lruvec, struct mem_cgroup *memcg,
BUG();
}
+ if (NR_ACTIVE_FILE == lru) {
+ long long kib_active_file_now=global_node_page_state(NR_ACTIVE_FILE) * MAX_NR_ZONES;
+ if (kib_active_file_now <= 256*1024) {
+ nr[lru] = 0; //don't reclaim any Active(file) (see /proc/meminfo) if they are under 256MiB
+ continue;
+ }
+ }
*lru_pages += size;
nr[lru] = scan;
}
Notez que certains encore-à-trouver la régression sur le noyau 5.3.0-rc4-gd45331b00ddb provoquera un gel du système (sans écrasement du disque, et sysrq fonctionnera toujours) même sans ce correctif.
(tout nouveau développement lié à cela devrait se produire ici.)