]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
mm: shmem: support large folio swap out
authorBaolin Wang <baolin.wang@linux.alibaba.com>
Mon, 12 Aug 2024 07:42:10 +0000 (15:42 +0800)
committerAndrew Morton <akpm@linux-foundation.org>
Sat, 17 Aug 2024 00:53:15 +0000 (17:53 -0700)
Shmem will support large folio allocation [1] [2] to get a better
performance, however, the memory reclaim still splits the precious large
folios when trying to swap out shmem, which may lead to the memory
fragmentation issue and can not take advantage of the large folio for
shmeme.

Moreover, the swap code already supports for swapping out large folio
without split, hence this patch set supports the large folio swap out for
shmem.

Note the i915_gem_shmem driver still need to be split when swapping, thus
add a new flag 'split_large_folio' for writeback_control to indicate
spliting the large folio.

[1] https://lore.kernel.org/all/cover.1717495894.git.baolin.wang@linux.alibaba.com/
[2] https://lore.kernel.org/all/20240515055719.32577-1-da.gomez@samsung.com/
Link: https://lkml.kernel.org/r/d80c21abd20e1b0f5ca66b330f074060fb2f082d.1723434324.git.baolin.wang@linux.alibaba.com
Signed-off-by: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Barry Song <baohua@kernel.org>
Cc: Chris Li <chrisl@kernel.org>
Cc: David Hildenbrand <david@redhat.com>
Cc: "Huang, Ying" <ying.huang@intel.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Lance Yang <ioworker0@gmail.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Pankaj Raghav <p.raghav@samsung.com>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Yang Shi <shy828301@gmail.com>
Cc: Zi Yan <ziy@nvidia.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
drivers/gpu/drm/i915/gem/i915_gem_shmem.c
include/linux/writeback.h
mm/shmem.c
mm/vmscan.c

index c5e1c718a6d2620cc839dec2ef41d4bf623e90fa..c66cb9c585e1dbd3f5e50d765d8edbf17873b192 100644 (file)
@@ -308,6 +308,7 @@ void __shmem_writeback(size_t size, struct address_space *mapping)
                .range_start = 0,
                .range_end = LLONG_MAX,
                .for_reclaim = 1,
+               .split_large_folio = 1,
        };
        unsigned long i;
 
index 1a54676d843abe86bf81d56caf242e4a0253e209..10100e22d5c60f3071e720e719ecbf11d62e9371 100644 (file)
@@ -63,6 +63,7 @@ struct writeback_control {
        unsigned range_cyclic:1;        /* range_start is cyclic */
        unsigned for_sync:1;            /* sync(2) WB_SYNC_ALL writeback */
        unsigned unpinned_netfs_wb:1;   /* Cleared I_PINNING_NETFS_WB */
+       unsigned split_large_folio:1;   /* Split large folio for shmem writeback */
 
        /*
         * When writeback IOs are bounced through async layers, only the
@@ -79,6 +80,9 @@ struct writeback_control {
         */
        struct swap_iocb **swap_plug;
 
+       /* Target list for splitting a large folio */
+       struct list_head *list;
+
        /* internal fields used by the ->writepages implementation: */
        struct folio_batch fbatch;
        pgoff_t index;
index c2b1e818b93f16c73d47f0dbbcf2e2f0f421d78c..dd384d4ab035fe78a6fc9b76f46fb51984d76dfa 100644 (file)
@@ -795,7 +795,6 @@ static int shmem_add_to_page_cache(struct folio *folio,
        VM_BUG_ON_FOLIO(index != round_down(index, nr), folio);
        VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
        VM_BUG_ON_FOLIO(!folio_test_swapbacked(folio), folio);
-       VM_BUG_ON(expected && folio_test_large(folio));
 
        folio_ref_add(folio, nr);
        folio->mapping = mapping;
@@ -1482,10 +1481,11 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
         * "force", drivers/gpu/drm/i915/gem/i915_gem_shmem.c gets huge pages,
         * and its shmem_writeback() needs them to be split when swapping.
         */
-       if (folio_test_large(folio)) {
+       if (wbc->split_large_folio && folio_test_large(folio)) {
+try_split:
                /* Ensure the subpages are still dirty */
                folio_test_set_dirty(folio);
-               if (split_huge_page(page) < 0)
+               if (split_huge_page_to_list_to_order(page, wbc->list, 0))
                        goto redirty;
                folio = page_folio(page);
                folio_clear_dirty(folio);
@@ -1527,8 +1527,12 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
        }
 
        swap = folio_alloc_swap(folio);
-       if (!swap.val)
+       if (!swap.val) {
+               if (nr_pages > 1)
+                       goto try_split;
+
                goto redirty;
+       }
 
        /*
         * Add inode to shmem_unuse()'s list of swapped-out inodes,
index 75a55a855fefb7e3f3ea263e9e3508e6565ea1fb..6fa7b8216a47e61bc9f730c67e9a8a78e3c3456f 100644 (file)
@@ -628,7 +628,7 @@ typedef enum {
  * Calls ->writepage().
  */
 static pageout_t pageout(struct folio *folio, struct address_space *mapping,
-                        struct swap_iocb **plug)
+                        struct swap_iocb **plug, struct list_head *folio_list)
 {
        /*
         * If the folio is dirty, only perform writeback if that write
@@ -676,6 +676,16 @@ static pageout_t pageout(struct folio *folio, struct address_space *mapping,
                        .swap_plug = plug,
                };
 
+               /*
+                * The large shmem folio can be split if CONFIG_THP_SWAP is
+                * not enabled or contiguous swap entries are failed to
+                * allocate.
+                */
+               if (shmem_mapping(mapping) && folio_test_large(folio)) {
+                       wbc.list = folio_list;
+                       wbc.split_large_folio = !IS_ENABLED(CONFIG_THP_SWAP);
+               }
+
                folio_set_reclaim(folio);
                res = mapping->a_ops->writepage(&folio->page, &wbc);
                if (res < 0)
@@ -1257,11 +1267,6 @@ retry:
                                                goto activate_locked_split;
                                }
                        }
-               } else if (folio_test_swapbacked(folio) &&
-                          folio_test_large(folio)) {
-                       /* Split shmem folio */
-                       if (split_folio_to_list(folio, folio_list))
-                               goto keep_locked;
                }
 
                /*
@@ -1362,12 +1367,25 @@ retry:
                         * starts and then write it out here.
                         */
                        try_to_unmap_flush_dirty();
-                       switch (pageout(folio, mapping, &plug)) {
+                       switch (pageout(folio, mapping, &plug, folio_list)) {
                        case PAGE_KEEP:
                                goto keep_locked;
                        case PAGE_ACTIVATE:
+                               /*
+                                * If shmem folio is split when writeback to swap,
+                                * the tail pages will make their own pass through
+                                * this function and be accounted then.
+                                */
+                               if (nr_pages > 1 && !folio_test_large(folio)) {
+                                       sc->nr_scanned -= (nr_pages - 1);
+                                       nr_pages = 1;
+                               }
                                goto activate_locked;
                        case PAGE_SUCCESS:
+                               if (nr_pages > 1 && !folio_test_large(folio)) {
+                                       sc->nr_scanned -= (nr_pages - 1);
+                                       nr_pages = 1;
+                               }
                                stat->nr_pageout += nr_pages;
 
                                if (folio_test_writeback(folio))