This patch add a interface to gendisk that SWAP device can use it to
control the swap cache rule.

Signed-off-by: Hui Zhu <zhu...@xiaomi.com>
---
 include/linux/genhd.h |  3 +++
 include/linux/swap.h  |  8 ++++++
 mm/Kconfig            | 10 +++++++
 mm/memory.c           |  2 +-
 mm/swapfile.c         | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 mm/vmscan.c           |  2 +-
 6 files changed, 96 insertions(+), 3 deletions(-)

diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index e0341af..6baec46 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -215,6 +215,9 @@ struct gendisk {
 #endif /* CONFIG_BLK_DEV_INTEGRITY */
        int node_id;
        struct badblocks *bb;
+#ifdef CONFIG_SWAP_CACHE_RULE
+       bool swap_cache_not_keep;
+#endif
 };
 
 static inline struct gendisk *part_to_disk(struct hd_struct *part)
diff --git a/include/linux/swap.h b/include/linux/swap.h
index a56523c..6fa11ca 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -582,5 +582,13 @@ static inline bool mem_cgroup_swap_full(struct page *page)
 }
 #endif
 
+#ifdef CONFIG_SWAP_CACHE_RULE
+extern bool swap_not_keep_cache(struct page *page);
+extern void swap_cache_rule_update(void);
+#else
+#define swap_not_keep_cache(p)         mem_cgroup_swap_full(p)
+#define swap_cache_rule_update()
+#endif
+
 #endif /* __KERNEL__*/
 #endif /* _LINUX_SWAP_H */
diff --git a/mm/Kconfig b/mm/Kconfig
index 86e3e0e..6623e87 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -711,3 +711,13 @@ config ARCH_USES_HIGH_VMA_FLAGS
        bool
 config ARCH_HAS_PKEYS
        bool
+
+config SWAP_CACHE_RULE
+       bool "Swap cache rule support"
+       depends on SWAP
+       default n
+       help
+         add a interface to gendisk that SWAP device can use it to
+         control the swap cache rule.
+
+         If unsure, say "n".
diff --git a/mm/memory.c b/mm/memory.c
index e18c57b..099cb5b 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -2654,7 +2654,7 @@ int do_swap_page(struct fault_env *fe, pte_t orig_pte)
        }
 
        swap_free(entry);
-       if (mem_cgroup_swap_full(page) ||
+       if (swap_not_keep_cache(page) ||
            (vma->vm_flags & VM_LOCKED) || PageMlocked(page))
                try_to_free_swap(page);
        unlock_page(page);
diff --git a/mm/swapfile.c b/mm/swapfile.c
index f304389..9837261 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1019,7 +1019,7 @@ int free_swap_and_cache(swp_entry_t entry)
                 * Also recheck PageSwapCache now page is locked (above).
                 */
                if (PageSwapCache(page) && !PageWriteback(page) &&
-                   (!page_mapped(page) || mem_cgroup_swap_full(page))) {
+                   (!page_mapped(page) || swap_not_keep_cache(page))) {
                        delete_from_swap_cache(page);
                        SetPageDirty(page);
                }
@@ -1992,6 +1992,8 @@ static void reinsert_swap_info(struct swap_info_struct *p)
        filp_close(victim, NULL);
 out:
        putname(pathname);
+       if (!err)
+               swap_cache_rule_update();
        return err;
 }
 
@@ -2576,6 +2578,8 @@ static bool swap_discardable(struct swap_info_struct *si)
                putname(name);
        if (inode && S_ISREG(inode->i_mode))
                inode_unlock(inode);
+       if (!error)
+               swap_cache_rule_update();
        return error;
 }
 
@@ -2954,3 +2958,71 @@ static void free_swap_count_continuations(struct 
swap_info_struct *si)
                }
        }
 }
+
+#ifdef CONFIG_SWAP_CACHE_RULE
+enum swap_cache_rule_type {
+       SWAP_CACHE_UNKNOWN = 0,
+       SWAP_CACHE_SPECIAL_RULE,
+       SWAP_CACHE_NOT_KEEP,
+       SWAP_CACHE_NEED_CHECK,
+};
+
+static enum swap_cache_rule_type swap_cache_rule __read_mostly;
+
+bool swap_not_keep_cache(struct page *page)
+{
+       enum swap_cache_rule_type rule = READ_ONCE(swap_cache_rule);
+
+       if (rule == SWAP_CACHE_NOT_KEEP)
+               return true;
+
+       if (unlikely(rule == SWAP_CACHE_SPECIAL_RULE)) {
+               struct swap_info_struct *sis;
+
+               BUG_ON(!PageSwapCache(page));
+
+               sis = page_swap_info(page);
+               if (sis->flags & SWP_BLKDEV) {
+                       struct gendisk *disk = sis->bdev->bd_disk;
+
+                       if (READ_ONCE(disk->swap_cache_not_keep))
+                               return true;
+               }
+       }
+
+       return mem_cgroup_swap_full(page);
+}
+
+void swap_cache_rule_update(void)
+{
+       enum swap_cache_rule_type rule = SWAP_CACHE_UNKNOWN;
+       int type;
+
+       spin_lock(&swap_lock);
+       for (type = 0; type < nr_swapfiles; type++) {
+               struct swap_info_struct *sis = swap_info[type];
+               enum swap_cache_rule_type current_rule = SWAP_CACHE_NEED_CHECK;
+
+               if (!(sis->flags & SWP_USED))
+                       continue;
+
+               if (sis->flags & SWP_BLKDEV) {
+                       struct gendisk *disk = sis->bdev->bd_disk;
+
+                       if (READ_ONCE(disk->swap_cache_not_keep))
+                               current_rule = SWAP_CACHE_NOT_KEEP;
+               }
+
+               if (rule == SWAP_CACHE_UNKNOWN)
+                       rule = current_rule;
+               else if (rule != current_rule) {
+                       rule = SWAP_CACHE_SPECIAL_RULE;
+                       break;
+               }
+       }
+       spin_unlock(&swap_lock);
+
+       WRITE_ONCE(swap_cache_rule, rule);
+}
+EXPORT_SYMBOL(swap_cache_rule_update);
+#endif
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 76fda22..52c67fe 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -1239,7 +1239,7 @@ static unsigned long shrink_page_list(struct list_head 
*page_list,
 
 activate_locked:
                /* Not a candidate for swapping, so reclaim swap space. */
-               if (PageSwapCache(page) && mem_cgroup_swap_full(page))
+               if (PageSwapCache(page) && swap_not_keep_cache(page))
                        try_to_free_swap(page);
                VM_BUG_ON_PAGE(PageActive(page), page);
                SetPageActive(page);
-- 
1.9.1

Reply via email to