Skip to content

Commit 0fd44ab

Browse files
Liu Shixinakpm00
authored andcommitted
mm/readahead: break read-ahead loop if filemap_add_folio return -ENOMEM
Patch series "Fix I/O high when memory almost met memcg limit", v2. Recently, when install package in a docker which almost reached its memory limit, the installer has no respond severely for more than 15 minutes. During this period, I/O stays high(~1G/s) and influence the whole machine. I've constructed a use case as follows: 1. create a docker: $ cat test.sh #!/bin/bash docker rm centos7 --force docker create --name centos7 --memory 4G --memory-swap 6G centos:7 /usr/sbin/init docker start centos7 sleep 1 docker cp ./alloc_page centos7:/ docker cp ./reproduce.sh centos7:/ docker exec -it centos7 /bin/bash 2. try reproduce the problem in docker: $ cat reproduce.sh #!/bin/bash while true; do flag=$(ps -ef | grep -v grep | grep alloc_page| wc -l) if [ "$flag" -eq 0 ]; then /alloc_page & fi sleep 30 start_time=$(date +%s) yum install -y expect > /dev/null 2>&1 end_time=$(date +%s) elapsed_time=$((end_time - start_time)) echo "$elapsed_time seconds" yum remove -y expect > /dev/null 2>&1 done $ cat alloc_page.c: #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define SIZE 1*1024*1024 //1M int main() { void *addr = NULL; int i; for (i = 0; i < 1024 * 6 - 50;i++) { addr = (void *)malloc(SIZE); if (!addr) return -1; memset(addr, 0, SIZE); } sleep(99999); return 0; } We found that this problem is caused by a lot ot meaningless read-ahead. Since the docker is almost met memory limit, the page will be reclaimed immediately after read-ahead and will read-ahead again immediately. The program is executed slowly and waste a lot of I/O resource. These two patch aim to break the read-ahead in above scenario. [1] https://lore.kernel.org/linux-mm/[email protected]/T/ [2] https://lore.kernel.org/all/[email protected]/ [3] https://lore.kernel.org/all/20240201173130.frpaqpy7iyzias5j@quack3/ This patch (of 2): When filemap_add_folio() return -ENOMEM, break read-ahead loop like what filemap_alloc_folio() does. Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Liu Shixin <[email protected]> Signed-off-by: Jinjiang Tu <[email protected]> Reviewed-by: Jan Kara <[email protected]> Cc: Al Viro <[email protected]> Cc: Christian Brauner <[email protected]> Cc: Liu Shixin <[email protected]> Cc: Matthew Wilcox (Oracle) <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent f238b8c commit 0fd44ab

File tree

1 file changed

+6
-2
lines changed

1 file changed

+6
-2
lines changed

mm/readahead.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
228228
*/
229229
for (i = 0; i < nr_to_read; i++) {
230230
struct folio *folio = xa_load(&mapping->i_pages, index + i);
231+
int ret;
231232

232233
if (folio && !xa_is_value(folio)) {
233234
/*
@@ -247,9 +248,12 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
247248
folio = filemap_alloc_folio(gfp_mask, 0);
248249
if (!folio)
249250
break;
250-
if (filemap_add_folio(mapping, folio, index + i,
251-
gfp_mask) < 0) {
251+
252+
ret = filemap_add_folio(mapping, folio, index + i, gfp_mask);
253+
if (ret < 0) {
252254
folio_put(folio);
255+
if (ret == -ENOMEM)
256+
break;
253257
read_pages(ractl);
254258
ractl->_index++;
255259
i = ractl->_index + ractl->_nr_pages - index - 1;

0 commit comments

Comments
 (0)