[PATCH RFC 07/18] x86/mm/pat: introduce cpa_handle_large_page()

From: Mike Rapoport (Microsoft)

Date: Tue Jul 21 2026 - 14:02:05 EST


Currently should_split_large_page() and split_large_page() duplicate
some of the operations, like PTE lookup inside the lock including
detection of NX and RW bits and large page size calculation.

This duplication can be avoided if the split parameters are passed
between these two functions in a structure and the page table allocation
and locking order is slightly reorganized.

As the first step in this refactoring, introduce cpa_handle_large_page()
helper that wraps should_split_large_page() and split_large_page().

No functional change intended.

Assisted-by: Copilot:claude-opus-4.8
Signed-off-by: Mike Rapoport (Microsoft) <rppt@xxxxxxxxxx>
---
arch/x86/mm/pat/set_memory.c | 54 ++++++++++++++++++++++++++++----------------
1 file changed, 34 insertions(+), 20 deletions(-)

diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c
index 2a087dcedf84..a9d270e986ff 100644
--- a/arch/x86/mm/pat/set_memory.c
+++ b/arch/x86/mm/pat/set_memory.c
@@ -1191,6 +1191,35 @@ static int split_large_page(struct cpa_data *cpa, pte_t *kpte,
return 0;
}

+static int cpa_handle_large_page(struct cpa_data *cpa, pte_t *kpte,
+ unsigned long address)
+{
+ int do_split, err;
+
+ /*
+ * Check, whether we can keep the large page intact
+ * and just change the pte:
+ */
+ do_split = should_split_large_page(kpte, address, cpa);
+ /*
+ * When the range fits into the existing large page, no split is
+ * required.
+ * should_split_large_page() updated the large page attributes and
+ * cpa->numpages and cpa->pfn.
+ */
+ if (do_split <= 0)
+ return do_split;
+
+ /*
+ * We have to split the large page:
+ */
+ err = split_large_page(cpa, kpte, address);
+ if (err)
+ return err;
+
+ return do_split;
+}
+
static int collapse_pmd_page(pmd_t *pmd, unsigned long addr,
struct list_head *pgtables)
{
@@ -1790,7 +1819,7 @@ static int __cpa_process_fault(struct cpa_data *cpa, unsigned long vaddr,
static int __change_page_attr(struct cpa_data *cpa, int primary)
{
unsigned long address;
- int do_split, err;
+ int split_res;
unsigned int level;
pte_t *kpte, old_pte;
bool nx, rw;
@@ -1842,27 +1871,12 @@ static int __change_page_attr(struct cpa_data *cpa, int primary)
return 0;
}

- /*
- * Check, whether we can keep the large page intact
- * and just change the pte:
- */
- do_split = should_split_large_page(kpte, address, cpa);
- /*
- * When the range fits into the existing large page,
- * return. cp->numpages and cpa->tlbflush have been updated in
- * try_large_page:
- */
- if (do_split <= 0)
- return do_split;
-
- /*
- * We have to split the large page:
- */
- err = split_large_page(cpa, kpte, address);
- if (!err)
+ /* Update large page or split it */
+ split_res = cpa_handle_large_page(cpa, kpte, address);
+ if (split_res > 0)
goto repeat;

- return err;
+ return split_res;
}

static int __change_page_attr_set_clr(struct cpa_data *cpa, int primary);

--
2.53.0