s390/mm: Enable THP_SWAP and THP_MIGRATION
authorGerald Schaefer <gerald.schaefer@linux.ibm.com>
Tue, 29 Jul 2025 11:16:19 +0000 (13:16 +0200)
committerAlexander Gordeev <agordeev@linux.ibm.com>
Thu, 31 Jul 2025 16:59:26 +0000 (18:59 +0200)
After hugetlbfs PTE_MARKER support for s390 introduced region-third and
segment table swap entries, it is now possible to also enable THP_SWAP
and THP_MIGRATION for s390.

s390 has different layout for PTE and region / segment table entries
(RSTE). This is also true for swap entries, and their swap type and offset
encoding. For hugetlbfs PTE_MARKER support, s390 has internal
__swp_type_rste() and __swp_offset_rste() helpers to correctly handle RSTE
swap entries.

But common swap code does not know about this difference, and only uses
__swp_type(), __swp_offset() and __swp_entry() helpers for conversion
between arch-dependent and arch-independent representation of swp_entry_t
for all pagetable levels. On s390, those helpers only work for PTE swap
entries.

Therefore, implement __pmd_to_swp_entry() to build a fake PTE swap entry
and return the arch-dependent representation of that. Correspondingly,
implement __swp_entry_to_pmd() to convert that into a proper PMD swap
entry again. With this, the arch-dependent swp_entry_t representation will
always look like a PTE swap entry in common code.

This is somewhat similar to fake PTEs in hugetlbfs code for s390, but only
requires conversion of the swap type and offset, and not all the possible
PTE bits.

For PMD swap entry SOFT_DIRTY handling, use the same helpers as for normal
PMDs. Similar to PTEs, the SOFT_DIRTY bit location is the same for swap
and normal entries.

Reviewed-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Gerald Schaefer <gerald.schaefer@linux.ibm.com>
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
arch/s390/Kconfig
arch/s390/include/asm/pgtable.h

index ac162e62da86c9f9273a008b8eea254d727a67a0..f41f604f0297461a3e88702596bc5dfcb6adc4dd 100644 (file)
@@ -74,6 +74,7 @@ config S390
        select ARCH_ENABLE_MEMORY_HOTPLUG if SPARSEMEM
        select ARCH_ENABLE_MEMORY_HOTREMOVE
        select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2
+       select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE
        select ARCH_HAS_CPU_FINALIZE_INIT
        select ARCH_HAS_CURRENT_STACK_POINTER
        select ARCH_HAS_DEBUG_VIRTUAL
@@ -150,6 +151,7 @@ config S390
        select ARCH_WANT_KERNEL_PMD_MKWRITE
        select ARCH_WANT_LD_ORPHAN_WARN
        select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP
+       select ARCH_WANTS_THP_SWAP
        select BUILDTIME_TABLE_SORT
        select CLONE_BACKWARDS2
        select DCACHE_WORD_ACCESS if !KMSAN
index 6d8bc27a366e4f17abc0e04d01dff5fcf20eb2e7..c1a7a92f057511edeba14ff1f6685923c9a23f27 100644 (file)
@@ -963,6 +963,12 @@ static inline pmd_t pmd_clear_soft_dirty(pmd_t pmd)
        return clear_pmd_bit(pmd, __pgprot(_SEGMENT_ENTRY_SOFT_DIRTY));
 }
 
+#ifdef CONFIG_ARCH_ENABLE_THP_MIGRATION
+#define pmd_swp_soft_dirty(pmd)                pmd_soft_dirty(pmd)
+#define pmd_swp_mksoft_dirty(pmd)      pmd_mksoft_dirty(pmd)
+#define pmd_swp_clear_soft_dirty(pmd)  pmd_clear_soft_dirty(pmd)
+#endif
+
 /*
  * query functions pte_write/pte_dirty/pte_young only work if
  * pte_present() is true. Undefined behaviour if not..
@@ -1979,6 +1985,45 @@ static inline unsigned long __swp_offset_rste(swp_entry_t entry)
 
 #define __rste_to_swp_entry(rste)      ((swp_entry_t) { rste })
 
+/*
+ * s390 has different layout for PTE and region / segment table entries (RSTE).
+ * This is also true for swap entries, and their swap type and offset encoding.
+ * For hugetlbfs PTE_MARKER support, s390 has internal __swp_type_rste() and
+ * __swp_offset_rste() helpers to correctly handle RSTE swap entries.
+ *
+ * But common swap code does not know about this difference, and only uses
+ * __swp_type(), __swp_offset() and __swp_entry() helpers for conversion between
+ * arch-dependent and arch-independent representation of swp_entry_t for all
+ * pagetable levels. On s390, those helpers only work for PTE swap entries.
+ *
+ * Therefore, implement __pmd_to_swp_entry() to build a fake PTE swap entry
+ * and return the arch-dependent representation of that. Correspondingly,
+ * implement __swp_entry_to_pmd() to convert that into a proper PMD swap
+ * entry again. With this, the arch-dependent swp_entry_t representation will
+ * always look like a PTE swap entry in common code.
+ *
+ * This is somewhat similar to fake PTEs in hugetlbfs code for s390, but only
+ * requires conversion of the swap type and offset, and not all the possible
+ * PTE bits.
+ */
+static inline swp_entry_t __pmd_to_swp_entry(pmd_t pmd)
+{
+       swp_entry_t arch_entry;
+       pte_t pte;
+
+       arch_entry = __rste_to_swp_entry(pmd_val(pmd));
+       pte = mk_swap_pte(__swp_type_rste(arch_entry), __swp_offset_rste(arch_entry));
+       return __pte_to_swp_entry(pte);
+}
+
+static inline pmd_t __swp_entry_to_pmd(swp_entry_t arch_entry)
+{
+       pmd_t pmd;
+
+       pmd = __pmd(mk_swap_rste(__swp_type(arch_entry), __swp_offset(arch_entry)));
+       return pmd;
+}
+
 extern int vmem_add_mapping(unsigned long start, unsigned long size);
 extern void vmem_remove_mapping(unsigned long start, unsigned long size);
 extern int __vmem_map_4k_page(unsigned long addr, unsigned long phys, pgprot_t prot, bool alloc);