mm: avoid memory thrashing caused by mismatched free memory calculation
authorSung-hun Kim <sfoon.kim@samsung.com>
Tue, 8 Sep 2020 12:44:30 +0000 (21:44 +0900)
committerSung-hun Kim <sfoon.kim@samsung.com>
Tue, 8 Sep 2020 12:44:30 +0000 (21:44 +0900)
Previously, the kernel calculates system's free memory by including cma free memory.
Unfortunately, the kernel doesn't use cma free memory for allocating unmovable pages.

In Tizen, GEM memory is allocated in unmovable pages by using shmem allocation. Because
of that, the kernel suffers from lack of free unmovable pages. To deal with it, the kernel
tries to steal movable pages and reclaim pages to make free unmovable pages. If user-level
daemon can take care of it in a proper way (e.g., call sigkill to make free memory), this
problem can be mitigated. But, they cannot catch the problem because system's free memory
always include cma free memory. As a result, the kernel repeatedly shrinks its page cache
and slab cache and reads files to launch an app, and finally it makes a catastrophic
thrashing problem.

This patch decouples page allocation of movable pages and calculation of free memory from
cma free memory pool. Thanks to Marek Szyprowski for his patches, the idea looks so feasible
to our system environment. This patch more generalized version of his patch (see the below link).

https://review.tizen.org/gerrit/#/c/platform/kernel/linux-rpi/+/243485/
https://review.tizen.org/gerrit/#/c/platform/kernel/linux-rpi/+/243486/

Change-Id: Idf4d8a0cc120363f8c8f340e0bac85783c530509
Signed-off-by: Sung-hun Kim <sfoon.kim@samsung.com>
include/linux/vmstat.h
mm/Kconfig
mm/compaction.c
mm/page_alloc.c

index bdeda4b..f509a9d 100644 (file)
@@ -374,7 +374,11 @@ static inline void drain_zonestat(struct zone *zone,
 static inline void __mod_zone_freepage_state(struct zone *zone, int nr_pages,
                                             int migratetype)
 {
-       __mod_zone_page_state(zone, NR_FREE_PAGES, nr_pages);
+       if (IS_ENABLED(CONFIG_CMA_EXCLUDE)) {
+               if (!is_migrate_cma(migratetype))
+                       __mod_zone_page_state(zone, NR_FREE_PAGES, nr_pages);
+       } else
+               __mod_zone_page_state(zone, NR_FREE_PAGES, nr_pages);
        if (is_migrate_cma(migratetype))
                __mod_zone_page_state(zone, NR_FREE_CMA_PAGES, nr_pages);
 }
index 33c4643..1c516c5 100644 (file)
@@ -535,6 +535,16 @@ config CMA_AREAS
 
          If unsure, leave the default value "7".
 
+config CMA_EXCLUDE
+       bool "Exclude CMA from free memory"
+       depends on CMA
+       help
+         This excludes CMA free memory from system free memory states
+         to give exact memory information for user-level.
+         If this option is enabled, allocation for movable pages cannot
+         steal CMA free pages.
+
+
 config MEM_SOFT_DIRTY
        bool "Track memory changes"
        depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
index 672d3c7..edbe53e 100644 (file)
@@ -1896,10 +1896,12 @@ static enum compact_result __compact_finished(struct compact_control *cc)
 
 #ifdef CONFIG_CMA
                /* MIGRATE_MOVABLE can fallback on MIGRATE_CMA */
+#ifndef CONFIG_CMA_EXCLUDE
                if (migratetype == MIGRATE_MOVABLE &&
                        !free_area_empty(area, MIGRATE_CMA))
                        return COMPACT_SUCCESS;
 #endif
+#endif
                /*
                 * Job done if allocation would steal freepages from
                 * other migratetype buddy lists.
index 93f642e..90e5401 100644 (file)
@@ -2718,7 +2718,7 @@ __rmqueue(struct zone *zone, unsigned int order, int migratetype,
 retry:
        page = __rmqueue_smallest(zone, order, migratetype);
        if (unlikely(!page)) {
-               if (migratetype == MIGRATE_MOVABLE)
+               if (!IS_ENABLED(CONFIG_CMA_EXCLUDE) && migratetype == MIGRATE_MOVABLE)
                        page = __rmqueue_cma_fallback(zone, order);
 
                if (!page && __rmqueue_fallback(zone, order, migratetype,
@@ -2740,6 +2740,9 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
                        int migratetype, unsigned int alloc_flags)
 {
        int i, alloced = 0;
+#ifdef CONFIG_CMA_EXCLUDE
+       int cma_alloc = 0;
+#endif
 
        spin_lock(&zone->lock);
        for (i = 0; i < count; ++i) {
@@ -2763,9 +2766,17 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
                 */
                list_add_tail(&page->lru, list);
                alloced++;
+#ifdef CONFIG_CMA_EXCLUDE
+               if (is_migrate_cma(get_pcppage_migratetype(page))) {
+                       __mod_zone_page_state(zone, NR_FREE_CMA_PAGES,
+                                             -(1 << order));
+                       cma_alloc++;
+               }
+#else
                if (is_migrate_cma(get_pcppage_migratetype(page)))
                        __mod_zone_page_state(zone, NR_FREE_CMA_PAGES,
                                              -(1 << order));
+#endif
        }
 
        /*
@@ -2774,7 +2785,11 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
         * on i. Do not confuse with 'alloced' which is the number of
         * pages added to the pcp list.
         */
+#ifdef CONFIG_CMA_EXCLUDE
+       __mod_zone_page_state(zone, NR_FREE_PAGES, -((i - cma_alloc) << order));
+#else
        __mod_zone_page_state(zone, NR_FREE_PAGES, -(i << order));
+#endif
        spin_unlock(&zone->lock);
        return alloced;
 }
@@ -3428,7 +3443,7 @@ bool __zone_watermark_ok(struct zone *z, unsigned int order, unsigned long mark,
 
 #ifdef CONFIG_CMA
        /* If allocation can't use CMA areas don't use free CMA pages */
-       if (!(alloc_flags & ALLOC_CMA))
+       if (!IS_ENABLED(CONFIG_CMA_EXCLUDE) && !(alloc_flags & ALLOC_CMA))
                free_pages -= zone_page_state(z, NR_FREE_CMA_PAGES);
 #endif
 
@@ -3485,7 +3500,7 @@ static inline bool zone_watermark_fast(struct zone *z, unsigned int order,
 
 #ifdef CONFIG_CMA
        /* If allocation can't use CMA areas don't use free CMA pages */
-       if (!(alloc_flags & ALLOC_CMA))
+       if (!IS_ENABLED(CONFIG_CMA_EXCLUDE) && !(alloc_flags & ALLOC_CMA))
                cma_pages = zone_page_state(z, NR_FREE_CMA_PAGES);
 #endif
 
@@ -4208,9 +4223,11 @@ gfp_to_alloc_flags(gfp_t gfp_mask)
                alloc_flags |= ALLOC_KSWAPD;
 
 #ifdef CONFIG_CMA
+#ifndef CONFIG_CMA_EXCLUDE
        if (gfpflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE)
                alloc_flags |= ALLOC_CMA;
 #endif
+#endif
        return alloc_flags;
 }
 
@@ -4690,7 +4707,8 @@ static inline bool prepare_alloc_pages(gfp_t gfp_mask, unsigned int order,
        if (should_fail_alloc_page(gfp_mask, order))
                return false;
 
-       if (IS_ENABLED(CONFIG_CMA) && ac->migratetype == MIGRATE_MOVABLE)
+       if (IS_ENABLED(CONFIG_CMA) && !IS_ENABLED(CONFIG_CMA_EXCLUDE) &&
+                       ac->migratetype == MIGRATE_MOVABLE)
                *alloc_flags |= ALLOC_CMA;
 
        return true;