The add_to_swap aims to allocate swap_space(ie, swap slot and swapcache)
so if it fails due to lack of space in case of THP or something(hdd swap
but tries THP swapout) *caller* rather than add_to_swap itself should
split the THP page and retry it with base page which is more natural.
Link: http://lkml.kernel.org/r/20170515112522.32457-4-ying.huang@intel.com
Signed-off-by: Minchan Kim <minchan@kernel.org>
Signed-off-by: "Huang, Ying" <ying.huang@intel.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: Ebru Akagunduz <ebru.akagunduz@gmail.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Rik van Riel <riel@redhat.com>
Cc: Shaohua Li <shli@kernel.org>
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
                >> SWAP_ADDRESS_SPACE_SHIFT])
 extern unsigned long total_swapcache_pages(void);
 extern void show_swap_cache_info(void);
-extern int add_to_swap(struct page *, struct list_head *list);
+extern int add_to_swap(struct page *page);
 extern int add_to_swap_cache(struct page *, swp_entry_t, gfp_t);
 extern int __add_to_swap_cache(struct page *page, swp_entry_t entry);
 extern void __delete_from_swap_cache(struct page *);
        return NULL;
 }
 
-static inline int add_to_swap(struct page *page, struct list_head *list)
+static inline int add_to_swap(struct page *page)
 {
        return 0;
 }
 
  * Allocate swap space for the page and add the page to the
  * swap cache.  Caller needs to hold the page lock. 
  */
-int add_to_swap(struct page *page, struct list_head *list)
+int add_to_swap(struct page *page)
 {
        swp_entry_t entry;
        int err;
        VM_BUG_ON_PAGE(!PageLocked(page), page);
        VM_BUG_ON_PAGE(!PageUptodate(page), page);
 
-retry:
        entry = get_swap_page(page);
        if (!entry.val)
-               goto fail;
+               return 0;
+
        if (mem_cgroup_try_charge_swap(page, entry))
-               goto fail_free;
+               goto fail;
 
        /*
         * Radix-tree node allocations from PF_MEMALLOC contexts could
                 * add_to_swap_cache() doesn't return -EEXIST, so we can safely
                 * clear SWAP_HAS_CACHE flag.
                 */
-               goto fail_free;
-
-       if (PageTransHuge(page)) {
-               err = split_huge_page_to_list(page, list);
-               if (err) {
-                       delete_from_swap_cache(page);
-                       return 0;
-               }
-       }
+               goto fail;
 
        return 1;
 
-fail_free:
-       put_swap_page(page, entry);
 fail:
-       if (PageTransHuge(page) && !split_huge_page_to_list(page, list))
-               goto retry;
+       put_swap_page(page, entry);
        return 0;
 }
 
 
                    !PageSwapCache(page)) {
                        if (!(sc->gfp_mask & __GFP_IO))
                                goto keep_locked;
-                       if (!add_to_swap(page, page_list))
+                       if (!add_to_swap(page)) {
+                               if (!PageTransHuge(page))
+                                       goto activate_locked;
+                               /* Split THP and swap individual base pages */
+                               if (split_huge_page_to_list(page, page_list))
+                                       goto activate_locked;
+                               if (!add_to_swap(page))
+                                       goto activate_locked;
+                       }
+
+                       /* XXX: We don't support THP writes */
+                       if (PageTransHuge(page) &&
+                                 split_huge_page_to_list(page, page_list)) {
+                               delete_from_swap_cache(page);
                                goto activate_locked;
+                       }
+
                        may_enter_fs = 1;
 
                        /* Adding to swap updated mapping */