Skip to contentSkip to conversation

riscv/mm: fix soft-dirty migration PMDs being treated as present

PATCH In review Lance Yang 2 participants 8 messages Oct 4 – Oct 26

Subsystems (3)
lore
Share & tools
lore

Lance Yang

author
#0 lore raw

PATCH 1/1 riscv/mm: fix soft-dirty migration PMDs being treated as present

RISC-V uses _PAGE_EXEC for swap soft-dirty tracking when CONFIG_MEM_SOFT_DIRTY is enabled and Svrsw60t59b is available. That's a problem for PMD migration entries, since pmd_present() also checks _PAGE_LEAF (R/W/X) to recognize THPs with _PAGE_PRESENT temporarily cleared during splitting.

When a soft-dirty THP is migrated, set_pmd_migration_entry() preserves soft-dirty with pmd_swp_mksoft_dirty(), setting the X bit in the migration PMD. Even with _PAGE_PRESENT clear, we end up treating a migration PMD as a present THP! The fault handler skips pmd_migration_entry_wait(), and a write fault can end up in do_huge_pmd_wp_page(), where pmd_page() decodes the migration entry as a mapped PFN.

Move the swap soft-dirty bit to bit 12 and start the swap offset at bit 13 when CONFIG_MEM_SOFT_DIRTY is enabled. This keeps R/W/X clear in migration PMDs and lets us keep the existing pmd_present() check for invalidated THPs. Leave the offset at bit 12 when soft-dirty tracking is disabled.

Fixes 2a3ebad4db63 ("riscv: mm: add soft-dirty page tracking support")

---
I found this while reviewing Usama's PMD-level swap entries series [1] and comparing the swap flag bits with the PMD presence checks across architectures.

[1] https://lore.kernel.org/all/[email protected]/

 arch/riscv/include/asm/pgtable-bits.h |  6 +++---
 arch/riscv/include/asm/pgtable.h      | 10 ++++++----
 2 files changed, 9 insertions(+), 7 deletions(-)
arch/riscv/include/asm/pgtable-bits.h +3 -3
index d5a86b4df3ce6..d84b459e010c1 100644
@@ -27,12 +27,12 @@
 	((riscv_has_extension_unlikely(RISCV_ISA_EXT_SVRSW60T59B)) ?	\
 	 (1UL << 59) : 0)
 /*
- * Bit 3 is always zero for swap entry computation, so we
- * can borrow it for swap page soft-dirty tracking.
+ * Bit 12 is reserved for swap soft-dirty tracking. The swap offset
+ * starts at bit 13 when CONFIG_MEM_SOFT_DIRTY is enabled.
  */
 #define _PAGE_SWP_SOFT_DIRTY						\
 	((riscv_has_extension_unlikely(RISCV_ISA_EXT_SVRSW60T59B)) ?	\
-	 _PAGE_EXEC : 0)
+	 (1UL << 12) : 0)
 #else
 #define _PAGE_SOFT_DIRTY	0
 #define _PAGE_SWP_SOFT_DIRTY	0
arch/riscv/include/asm/pgtable.h +6 -4
index b644db16bda94..acaf6d3b54d04 100644
@@ -1178,18 +1178,20 @@ static inline pud_t pud_modify(pud_t pud, pgprot_t newprot)
  *
  * Format of swap PTE:
  *	bit            0:	_PAGE_PRESENT (zero)
- *	bit       1 to 2:	(zero)
- *	bit            3:	_PAGE_SWP_SOFT_DIRTY
+ *	bit       1 to 3:	_PAGE_LEAF (zero)
  *	bit            4:	_PAGE_SWP_UFFD
  *	bit            5:	_PAGE_PROT_NONE (zero)
  *	bit            6:	exclusive marker
  *	bits      7 to 11:	swap type
- *	bits 12 to XLEN-1:	swap offset
+ *	bit           12:	_PAGE_SWP_SOFT_DIRTY (CONFIG_MEM_SOFT_DIRTY)
+ *	bits 12/13 to XLEN-1:	swap offset (without/with CONFIG_MEM_SOFT_DIRTY)
  */
 #define __SWP_TYPE_SHIFT	7
 #define __SWP_TYPE_BITS		5
 #define __SWP_TYPE_MASK		((1UL << __SWP_TYPE_BITS) - 1)
-#define __SWP_OFFSET_SHIFT	(__SWP_TYPE_BITS + __SWP_TYPE_SHIFT)
+#define __SWP_OFFSET_SHIFT \
+	(__SWP_TYPE_BITS + __SWP_TYPE_SHIFT + \
+	 IS_ENABLED(CONFIG_MEM_SOFT_DIRTY))
 
 #define MAX_SWAPFILES_CHECK()	\
 	BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > __SWP_TYPE_BITS)
Signature · 1 line
2.49.0
#1 lore raw
Quoted 5 lines · Lance Yang

RISC-V uses _PAGE_EXEC for swap soft-dirty tracking when CONFIG_MEM_SOFT_DIRTY is enabled and Svrsw60t59b is available. That's a problem for PMD migration entries, since pmd_present() also checks _PAGE_LEAF (R/W/X) to recognize THPs with _PAGE_PRESENT temporarily cleared during splitting.

I'm curious: why do we have to set leaf indications for non-present things? The HW sure will ignore it, right?

Is this a sw problem? Who needs that?

Quoted 8 lines

When a soft-dirty THP is migrated, set_pmd_migration_entry() preserves soft-dirty with pmd_swp_mksoft_dirty(), setting the X bit in the migration PMD. Even with _PAGE_PRESENT clear, we end up treating a migration PMD as a present THP! The fault handler skips pmd_migration_entry_wait(), and a write fault can end up in do_huge_pmd_wp_page(), where pmd_page() decodes the migration entry as a mapped PFN.

That sounds bad.

Quoted 6 lines

Move the swap soft-dirty bit to bit 12 and start the swap offset at bit 13 when CONFIG_MEM_SOFT_DIRTY is enabled. This keeps R/W/X clear in migration PMDs and lets us keep the existing pmd_present() check for invalidated THPs. Leave the offset at bit 12 when soft-dirty tracking is disabled.

That reduces the effective swap size (and PFN we can store). Could that be a problem?

Quoted 64 lines
Fixes 2a3ebad4db63 ("riscv: mm: add soft-dirty page tracking support")

---
I found this while reviewing Usama's PMD-level swap entries series [1] and comparing the swap flag bits with the PMD presence checks across architectures.

[1] https://lore.kernel.org/all/[email protected]/

 arch/riscv/include/asm/pgtable-bits.h |  6 +++---
 arch/riscv/include/asm/pgtable.h      | 10 ++++++----
 2 files changed, 9 insertions(+), 7 deletions(-)
arch/riscv/include/asm/pgtable-bits.h +3 -3
index d5a86b4df3ce6..d84b459e010c1 100644
@@ -27,12 +27,12 @@
 	((riscv_has_extension_unlikely(RISCV_ISA_EXT_SVRSW60T59B)) ?	\
 	 (1UL << 59) : 0)
 /*
- * Bit 3 is always zero for swap entry computation, so we
- * can borrow it for swap page soft-dirty tracking.
+ * Bit 12 is reserved for swap soft-dirty tracking. The swap offset
+ * starts at bit 13 when CONFIG_MEM_SOFT_DIRTY is enabled.
  */
 #define _PAGE_SWP_SOFT_DIRTY						\
 	((riscv_has_extension_unlikely(RISCV_ISA_EXT_SVRSW60T59B)) ?	\
-	 _PAGE_EXEC : 0)
+	 (1UL << 12) : 0)
 #else
 #define _PAGE_SOFT_DIRTY	0
 #define _PAGE_SWP_SOFT_DIRTY	0
arch/riscv/include/asm/pgtable.h +6 -4
index b644db16bda94..acaf6d3b54d04 100644
@@ -1178,18 +1178,20 @@ static inline pud_t pud_modify(pud_t pud, pgprot_t newprot)
  *
  * Format of swap PTE:
  *	bit            0:	_PAGE_PRESENT (zero)
- *	bit       1 to 2:	(zero)
- *	bit            3:	_PAGE_SWP_SOFT_DIRTY
+ *	bit       1 to 3:	_PAGE_LEAF (zero)
  *	bit            4:	_PAGE_SWP_UFFD
  *	bit            5:	_PAGE_PROT_NONE (zero)
  *	bit            6:	exclusive marker
  *	bits      7 to 11:	swap type
- *	bits 12 to XLEN-1:	swap offset
+ *	bit           12:	_PAGE_SWP_SOFT_DIRTY (CONFIG_MEM_SOFT_DIRTY)
+ *	bits 12/13 to XLEN-1:	swap offset (without/with CONFIG_MEM_SOFT_DIRTY)
  */
 #define __SWP_TYPE_SHIFT	7
 #define __SWP_TYPE_BITS		5
 #define __SWP_TYPE_MASK		((1UL << __SWP_TYPE_BITS) - 1)
-#define __SWP_OFFSET_SHIFT	(__SWP_TYPE_BITS + __SWP_TYPE_SHIFT)
+#define __SWP_OFFSET_SHIFT \
+	(__SWP_TYPE_BITS + __SWP_TYPE_SHIFT + \
+	 IS_ENABLED(CONFIG_MEM_SOFT_DIRTY))
 
 #define MAX_SWAPFILES_CHECK()	\
 	BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > __SWP_TYPE_BITS)
Signature · 3 lines
Cheers,

David
author
#2 lore raw
Quoted 9 lines · David Hildenbrand (Arm)
Quoted 5 lines · Lance Yang

RISC-V uses _PAGE_EXEC for swap soft-dirty tracking when CONFIG_MEM_SOFT_DIRTY is enabled and Svrsw60t59b is available. That's a problem for PMD migration entries, since pmd_present() also checks _PAGE_LEAF (R/W/X) to recognize THPs with _PAGE_PRESENT temporarily cleared during splitting.

I'm curious: why do we have to set leaf indications for non-present things? The HW sure will ignore it, right?

Yeah, that surprised me too :) Still wrapping my head around the details
...

Quoted 1 line

Is this a sw problem? Who needs that?

IIUC, it's for software during a PMD split.

__split_huge_pmd_locked() invalidates the huge PMD and flushes the TLB before installing the PTE table. Software still needs pmd_present() and pmd_trans_huge() to recognize the THP in between.

RISC-V clears V but keeps the R/W/X bits for that, so the entry is invalid to hardware but still identifiable as a THP by software.

Hopefully I didn't miss something.

Quoted 10 lines
Quoted 8 lines

When a soft-dirty THP is migrated, set_pmd_migration_entry() preserves soft-dirty with pmd_swp_mksoft_dirty(), setting the X bit in the migration PMD. Even with _PAGE_PRESENT clear, we end up treating a migration PMD as a present THP! The fault handler skips pmd_migration_entry_wait(), and a write fault can end up in do_huge_pmd_wp_page(), where pmd_page() decodes the migration entry as a mapped PFN.

That sounds bad.

YES, looks a bit off ...

Quoted 9 lines
Quoted 6 lines

Move the swap soft-dirty bit to bit 12 and start the swap offset at bit 13 when CONFIG_MEM_SOFT_DIRTY is enabled. This keeps R/W/X clear in migration PMDs and lets us keep the existing pmd_present() check for invalidated THPs. Leave the offset at bit 12 when soft-dirty tracking is disabled.

That reduces the effective swap size (and PFN we can store). Could that be a problem?

We don't need all 52 bits of the swap offset.

RV64 PFNs only need 44 bits for migration entries, and actual swap is already limited to about 16 TiB per area with 4 KiB pages by last_page (__u32) and swap_info_struct.max (unsigned int).

So there's room to reserve a bit without reducing the supported swap size or PFN range.

CONFIG_MEM_SOFT_DIRTY is only available on RV64, so RV32 keeps its 20-bit offset.

[...]

Cheers, Lance

author
#3 lore raw
Quoted 15 lines · Lance Yang
Quoted 9 lines · David Hildenbrand (Arm)
Quoted 5 lines · Lance Yang

RISC-V uses _PAGE_EXEC for swap soft-dirty tracking when CONFIG_MEM_SOFT_DIRTY is enabled and Svrsw60t59b is available. That's a problem for PMD migration entries, since pmd_present() also checks _PAGE_LEAF (R/W/X) to recognize THPs with _PAGE_PRESENT temporarily cleared during splitting.

I'm curious: why do we have to set leaf indications for non-present things? The HW sure will ignore it, right?

Yeah, that surprised me too :) Still wrapping my head around the details
...

Quoted 7 lines
Quoted 1 line

Is this a sw problem? Who needs that?

IIUC, it's for software during a PMD split.

__split_huge_pmd_locked() invalidates the huge PMD and flushes the TLB before installing the PTE table. Software still needs pmd_present() and pmd_trans_huge() to recognize the THP in between.

Also, x86 keeps _PAGE_PSE to identify the huge PMD. RISC-V doesn't have a separate leaf bit, so it keeps the R/W/X bits to identify the PMD as a leaf entry :)

Quoted 44 lines

RISC-V clears V but keeps the R/W/X bits for that, so the entry is invalid to hardware but still identifiable as a THP by software.

Hopefully I didn't miss something.

Quoted 10 lines
Quoted 8 lines

When a soft-dirty THP is migrated, set_pmd_migration_entry() preserves soft-dirty with pmd_swp_mksoft_dirty(), setting the X bit in the migration PMD. Even with _PAGE_PRESENT clear, we end up treating a migration PMD as a present THP! The fault handler skips pmd_migration_entry_wait(), and a write fault can end up in do_huge_pmd_wp_page(), where pmd_page() decodes the migration entry as a mapped PFN.

That sounds bad.

YES, looks a bit off ...

Quoted 9 lines
Quoted 6 lines

Move the swap soft-dirty bit to bit 12 and start the swap offset at bit 13 when CONFIG_MEM_SOFT_DIRTY is enabled. This keeps R/W/X clear in migration PMDs and lets us keep the existing pmd_present() check for invalidated THPs. Leave the offset at bit 12 when soft-dirty tracking is disabled.

That reduces the effective swap size (and PFN we can store). Could that be a problem?

We don't need all 52 bits of the swap offset.

RV64 PFNs only need 44 bits for migration entries, and actual swap is already limited to about 16 TiB per area with 4 KiB pages by last_page (__u32) and swap_info_struct.max (unsigned int).

So there's room to reserve a bit without reducing the supported swap size or PFN range.

CONFIG_MEM_SOFT_DIRTY is only available on RV64, so RV32 keeps its 20-bit offset.

[...]

Cheers, Lance

#4 lore raw
In reply to Lance Yang
Quoted 19 lines · Lance Yang
Quoted 9 lines · David Hildenbrand (Arm)
Quoted 5 lines · Lance Yang

RISC-V uses _PAGE_EXEC for swap soft-dirty tracking when CONFIG_MEM_SOFT_DIRTY is enabled and Svrsw60t59b is available. That's a problem for PMD migration entries, since pmd_present() also checks _PAGE_LEAF (R/W/X) to recognize THPs with _PAGE_PRESENT temporarily cleared during splitting.

I'm curious: why do we have to set leaf indications for non-present things? The HW sure will ignore it, right?

Yeah, that surprised me too :) Still wrapping my head around the details
...

Quoted 1 line

Is this a sw problem? Who needs that?

IIUC, it's for software during a PMD split.

Quoted 8 lines

__split_huge_pmd_locked() invalidates the huge PMD and flushes the TLB before installing the PTE table. Software still needs pmd_present() and pmd_trans_huge() to recognize the THP in between.

RISC-V clears V but keeps the R/W/X bits for that, so the entry is invalid to hardware but still identifiable as a THP by software.

Hopefully I didn't miss something.

I thought some change we performed to GUP-fast wouldn't require that anymore. But my memory is a bit vague ... :)

Quoted 12 lines
Quoted 11 lines
Quoted 9 lines

When a soft-dirty THP is migrated, set_pmd_migration_entry() preserves soft-dirty with pmd_swp_mksoft_dirty(), setting the X bit in the migration PMD. Even with _PAGE_PRESENT clear, we end up treating a migration PMD as a present THP! The fault handler skips pmd_migration_entry_wait(), and a write fault can end up in do_huge_pmd_wp_page(), where pmd_page() decodes the migration entry as a mapped PFN.

That sounds bad.

[...]

Quoted 10 lines

YES, looks a bit off ...

Quoted 2 lines

That reduces the effective swap size (and PFN we can store). Could that be a problem?

We don't need all 52 bits of the swap offset.

RV64 PFNs only need 44 bits for migration entries, and actual swap is already limited to about 16 TiB per area with 4 KiB pages by last_page (__u32) and swap_info_struct.max (unsigned int).

Quoted 5 lines

So there's room to reserve a bit without reducing the supported swap size or PFN range.

CONFIG_MEM_SOFT_DIRTY is only available on RV64, so RV32 keeps its 20-bit offset.

Good, can you spell that out in the patch description?

Signature · 3 lines
Cheers,

David
author
#5 lore raw
Quoted 24 lines · David Hildenbrand (Arm)
Quoted 23 lines · Lance Yang
Quoted 9 lines · David Hildenbrand (Arm)
Quoted 5 lines · Lance Yang

RISC-V uses _PAGE_EXEC for swap soft-dirty tracking when CONFIG_MEM_SOFT_DIRTY is enabled and Svrsw60t59b is available. That's a problem for PMD migration entries, since pmd_present() also checks _PAGE_LEAF (R/W/X) to recognize THPs with _PAGE_PRESENT temporarily cleared during splitting.

I'm curious: why do we have to set leaf indications for non-present things? The HW sure will ignore it, right?

Yeah, that surprised me too :) Still wrapping my head around the details
...

Quoted 1 line

Is this a sw problem? Who needs that?

IIUC, it's for software during a PMD split.

__split_huge_pmd_locked() invalidates the huge PMD and flushes the TLB before installing the PTE table. Software still needs pmd_present() and pmd_trans_huge() to recognize the THP in between.

Quoted 7 lines
Quoted 4 lines

RISC-V clears V but keeps the R/W/X bits for that, so the entry is invalid to hardware but still identifiable as a THP by software.

Hopefully I didn't miss something.

I thought some change we performed to GUP-fast wouldn't require that anymore. But my memory is a bit vague ... :)

Yeah, I'll have another look at that :)

Quoted 25 lines
Quoted 12 lines
Quoted 11 lines
Quoted 9 lines

When a soft-dirty THP is migrated, set_pmd_migration_entry() preserves soft-dirty with pmd_swp_mksoft_dirty(), setting the X bit in the migration PMD. Even with _PAGE_PRESENT clear, we end up treating a migration PMD as a present THP! The fault handler skips pmd_migration_entry_wait(), and a write fault can end up in do_huge_pmd_wp_page(), where pmd_page() decodes the migration entry as a mapped PFN.

That sounds bad.

[...]

Quoted 10 lines

YES, looks a bit off ...

Quoted 2 lines

That reduces the effective swap size (and PFN we can store). Could that be a problem?

We don't need all 52 bits of the swap offset.

RV64 PFNs only need 44 bits for migration entries, and actual swap is already limited to about 16 TiB per area with 4 KiB pages by last_page (__u32) and swap_info_struct.max (unsigned int).

Quoted 7 lines
Quoted 5 lines

So there's room to reserve a bit without reducing the supported swap size or PFN range.

CONFIG_MEM_SOFT_DIRTY is only available on RV64, so RV32 keeps its 20-bit offset.

Good, can you spell that out in the patch description?

Sure, will add that to the commit message in v2 once I've heard back from maintainers :)

Cheers, Lance

author
#6 lore raw
Quoted 29 lines · Lance Yang
Quoted 27 lines · David Hildenbrand (Arm)
Quoted 26 lines · Lance Yang
Quoted 9 lines · David Hildenbrand (Arm)
Quoted 5 lines · Lance Yang

RISC-V uses _PAGE_EXEC for swap soft-dirty tracking when CONFIG_MEM_SOFT_DIRTY is enabled and Svrsw60t59b is available. That's a problem for PMD migration entries, since pmd_present() also checks _PAGE_LEAF (R/W/X) to recognize THPs with _PAGE_PRESENT temporarily cleared during splitting.

I'm curious: why do we have to set leaf indications for non-present things? The HW sure will ignore it, right?

Yeah, that surprised me too :) Still wrapping my head around the details
...

Quoted 1 line

Is this a sw problem? Who needs that?

IIUC, it's for software during a PMD split.

__split_huge_pmd_locked() invalidates the huge PMD and flushes the TLB before installing the PTE table. Software still needs pmd_present() and pmd_trans_huge() to recognize the THP in between.

RISC-V clears V but keeps the R/W/X bits for that, so the entry is invalid to hardware but still identifiable as a THP by software.

Quoted 6 lines
Quoted 4 lines
Quoted 1 line

Hopefully I didn't miss something.

I thought some change we performed to GUP-fast wouldn't require that anymore. But my memory is a bit vague ... :)

Yeah, I'll have another look at that :)

Emm ... I'm back. Do you mean 4b471e8898c3, which removed the GUP-fast pmd_trans_splitting() check after the THP refcounting rework? That check made GUP-fast bail out during the old THP split. The refcounting rework made extra GUP references prevent the physical split :)

RISC-V gained THP support in 2021 (e88b333142e4), after pmd_trans_splitting() had been removed. It never implemented that helper.

Keeping R/W/X in an invalidated RISC-V PMD preserves its leaf identity for software page-table walks. That requirement is separate from the old GUP-fast splitting check.

For RISC-V, pmd_mkinvalid() clears V and PROT_NONE but keeps R/W/X, so pmd_present() and pmd_trans_huge() still return true:

#define _PAGE_LEAF (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC)

static inline pmd_t pmd_mkinvalid(pmd_t pmd)

{
	return __pmd(pmd_val(pmd) & ~(_PAGE_PRESENT|_PAGE_PROT_NONE));
}

static inline int pmd_present(pmd_t pmd)
{
/*
* Checking for _PAGE_LEAF is needed too because:
* When splitting a THP, split_huge_page() will temporarily clear
* the present bit, in this situation, pmd_present() and
* pmd_trans_huge() still needs to return true.
*/
return (pmd_val(pmd) & (_PAGE_PRESENT | _PAGE_PROT_NONE | _PAGE_LEAF));
}

static inline bool pmd_leaf(pmd_t pmd)

{
	return pmd_present(pmd) && (pmd_val(pmd) & _PAGE_LEAF);
}

static inline int pmd_trans_huge(pmd_t pmd)

{
	return pmd_leaf(pmd);
}

And the rmap walk also needs that leaf indication. page_vma_mapped_walk() checks pmd_trans_huge() before taking the PMD lock, then rereads the entry under the lock. Keeping R/W/X lets it recognize the invalidated THP and wait for the split to finish :)

Anyway, R/W/X still have a job there (RISC-V doesn't have a separate leaf bit), and I don't think we can just drop them yet :D

Cheers, Lance

[...]

author
#7 lore raw
In reply to Lance Yang

Gently ping ... Hi RISC-V folks, any thoughts on the proposed fix?

Quoted 87 lines · Lance Yang

RISC-V uses _PAGE_EXEC for swap soft-dirty tracking when CONFIG_MEM_SOFT_DIRTY is enabled and Svrsw60t59b is available. That's a problem for PMD migration entries, since pmd_present() also checks _PAGE_LEAF (R/W/X) to recognize THPs with _PAGE_PRESENT temporarily cleared during splitting.

When a soft-dirty THP is migrated, set_pmd_migration_entry() preserves soft-dirty with pmd_swp_mksoft_dirty(), setting the X bit in the migration PMD. Even with _PAGE_PRESENT clear, we end up treating a migration PMD as a present THP! The fault handler skips pmd_migration_entry_wait(), and a write fault can end up in do_huge_pmd_wp_page(), where pmd_page() decodes the migration entry as a mapped PFN.

Move the swap soft-dirty bit to bit 12 and start the swap offset at bit 13 when CONFIG_MEM_SOFT_DIRTY is enabled. This keeps R/W/X clear in migration PMDs and lets us keep the existing pmd_present() check for invalidated THPs. Leave the offset at bit 12 when soft-dirty tracking is disabled.

Fixes 2a3ebad4db63 ("riscv: mm: add soft-dirty page tracking support")

---
I found this while reviewing Usama's PMD-level swap entries series [1] and comparing the swap flag bits with the PMD presence checks across architectures.

[1] https://lore.kernel.org/all/[email protected]/

arch/riscv/include/asm/pgtable-bits.h | 6 +++---
arch/riscv/include/asm/pgtable.h | 10 ++++++----

2 files changed, 9 insertions(+), 7 deletions(-)
arch/riscv/include/asm/pgtable-bits.h +3 -3
index d5a86b4df3ce6..d84b459e010c1 100644
@@ -27,12 +27,12 @@
 	((riscv_has_extension_unlikely(RISCV_ISA_EXT_SVRSW60T59B)) ?	\
 	 (1UL << 59) : 0)
 /*
- * Bit 3 is always zero for swap entry computation, so we
- * can borrow it for swap page soft-dirty tracking.
+ * Bit 12 is reserved for swap soft-dirty tracking. The swap offset
+ * starts at bit 13 when CONFIG_MEM_SOFT_DIRTY is enabled.
 */
 #define _PAGE_SWP_SOFT_DIRTY						\
 	((riscv_has_extension_unlikely(RISCV_ISA_EXT_SVRSW60T59B)) ?	\
-	 _PAGE_EXEC : 0)
+	 (1UL << 12) : 0)
 #else
 #define _PAGE_SOFT_DIRTY	0
 #define _PAGE_SWP_SOFT_DIRTY	0
arch/riscv/include/asm/pgtable.h +6 -4
index b644db16bda94..acaf6d3b54d04 100644
@@ -1178,18 +1178,20 @@ static inline pud_t pud_modify(pud_t pud, pgprot_t newprot)
 *
 * Format of swap PTE:
 *	bit            0:	_PAGE_PRESENT (zero)
- *	bit       1 to 2:	(zero)
- *	bit            3:	_PAGE_SWP_SOFT_DIRTY
+ *	bit       1 to 3:	_PAGE_LEAF (zero)
 *	bit            4:	_PAGE_SWP_UFFD
 *	bit            5:	_PAGE_PROT_NONE (zero)
 *	bit            6:	exclusive marker
 *	bits      7 to 11:	swap type
- *	bits 12 to XLEN-1:	swap offset
+ *	bit           12:	_PAGE_SWP_SOFT_DIRTY (CONFIG_MEM_SOFT_DIRTY)
+ *	bits 12/13 to XLEN-1:	swap offset (without/with CONFIG_MEM_SOFT_DIRTY)
 */
 #define __SWP_TYPE_SHIFT	7
 #define __SWP_TYPE_BITS		5
 #define __SWP_TYPE_MASK		((1UL << __SWP_TYPE_BITS) - 1)
-#define __SWP_OFFSET_SHIFT	(__SWP_TYPE_BITS + __SWP_TYPE_SHIFT)
+#define __SWP_OFFSET_SHIFT \
+	(__SWP_TYPE_BITS + __SWP_TYPE_SHIFT + \
+	 IS_ENABLED(CONFIG_MEM_SOFT_DIRTY))
 
 #define MAX_SWAPFILES_CHECK()	\
 	BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > __SWP_TYPE_BITS)
Signature · 1 line
2.49.0