* [PATCH 1/3] misc: sgi-gru: remove interrupt-context page-table walks
2026-07-30 11:12 [PATCH 0/3] misc: sgi-gru: Remove broken page-table walks Muhammad Usama Anjum
@ 2026-07-30 11:12 ` Muhammad Usama Anjum
2026-07-30 11:12 ` [PATCH 2/3] misc: sgi-gru: remove obsolete atomic fault-handling state Muhammad Usama Anjum
` (2 subsequent siblings)
3 siblings, 0 replies; 10+ messages in thread
From: Muhammad Usama Anjum @ 2026-07-30 11:12 UTC (permalink / raw)
To: Dimitri Sivanich, Arnd Bergmann, Greg Kroah-Hartman, linux-kernel
Cc: Muhammad Usama Anjum
The GRU TLB miss handler walks a process's page tables without holding
page-table locks or a reference to the mapped page. It also uses a kernel
page-table accessor on user page tables and supports only PMD-level large
mappings on x86-64.
Remove the direct walker. Send interrupt faults directly to user polling
mode so the existing call-OS fallback retries them in process context.
Remove the mmap-lock failure statistic that can no longer be incremented.
Fixes: 142586409c8b ("GRU Driver: page faults & exceptions")
Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
---
Only build tested.
---
drivers/misc/sgi-gru/grufault.c | 101 ++++---------------------------
drivers/misc/sgi-gru/gruprocfs.c | 1 -
drivers/misc/sgi-gru/grutables.h | 1 -
3 files changed, 12 insertions(+), 91 deletions(-)
diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c
index 3557d78ee47a2..5a87c12f444a3 100644
--- a/drivers/misc/sgi-gru/grufault.c
+++ b/drivers/misc/sgi-gru/grufault.c
@@ -166,13 +166,8 @@ static void get_clear_fault_map(struct gru_state *gru,
}
/*
- * Atomic (interrupt context) & non-atomic (user context) functions to
- * convert a vaddr into a physical address. The size of the page
- * is returned in pageshift.
- * returns:
- * 0 - successful
- * < 0 - error code
- * 1 - (atomic only) try again in non-atomic context
+ * Convert a user virtual address to a physical address in process context.
+ * The size of the page is returned in pageshift.
*/
static int non_atomic_pte_lookup(struct vm_area_struct *vma,
unsigned long vaddr, int write,
@@ -192,87 +187,25 @@ static int non_atomic_pte_lookup(struct vm_area_struct *vma,
return 0;
}
-/*
- * atomic_pte_lookup
- *
- * Convert a user virtual address to a physical address
- * Only supports Intel large pages (2MB only) on x86_64.
- * ZZZ - hugepage support is incomplete
- *
- * NOTE: mmap_lock is already held on entry to this function. This
- * guarantees existence of the page tables.
- */
-static int atomic_pte_lookup(struct vm_area_struct *vma, unsigned long vaddr,
- int write, unsigned long *paddr, int *pageshift)
-{
- pgd_t *pgdp;
- p4d_t *p4dp;
- pud_t *pudp;
- pmd_t *pmdp;
- pte_t pte;
-
- pgdp = pgd_offset(vma->vm_mm, vaddr);
- if (unlikely(pgd_none(*pgdp)))
- goto err;
-
- p4dp = p4d_offset(pgdp, vaddr);
- if (unlikely(p4d_none(*p4dp)))
- goto err;
-
- pudp = pud_offset(p4dp, vaddr);
- if (unlikely(pud_none(*pudp)))
- goto err;
-
- pmdp = pmd_offset(pudp, vaddr);
- if (unlikely(pmd_none(*pmdp)))
- goto err;
-#ifdef CONFIG_X86_64
- if (unlikely(pmd_leaf(*pmdp)))
- pte = ptep_get((pte_t *)pmdp);
- else
-#endif
- pte = *pte_offset_kernel(pmdp, vaddr);
-
- if (unlikely(!pte_present(pte) ||
- (write && (!pte_write(pte) || !pte_dirty(pte)))))
- return 1;
-
- *paddr = pte_pfn(pte) << PAGE_SHIFT;
-#ifdef CONFIG_HUGETLB_PAGE
- *pageshift = is_vm_hugetlb_page(vma) ? HPAGE_SHIFT : PAGE_SHIFT;
-#else
- *pageshift = PAGE_SHIFT;
-#endif
- return 0;
-
-err:
- return 1;
-}
-
static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr,
int write, int atomic, unsigned long *gpa, int *pageshift)
{
struct mm_struct *mm = gts->ts_mm;
struct vm_area_struct *vma;
unsigned long paddr;
- int ret, ps;
+ int ps;
vma = find_vma(mm, vaddr);
if (!vma)
goto inval;
- /*
- * Atomic lookup is faster & usually works even if called in non-atomic
- * context.
- */
- rmb(); /* Must/check ms_range_active before loading PTEs */
- ret = atomic_pte_lookup(vma, vaddr, write, &paddr, &ps);
- if (ret) {
- if (atomic)
- goto upm;
- if (non_atomic_pte_lookup(vma, vaddr, write, &paddr, &ps))
- goto inval;
- }
+ if (atomic)
+ goto upm;
+
+ /* Order the caller's ms_range_active check before loading PTEs. */
+ rmb();
+ if (non_atomic_pte_lookup(vma, vaddr, write, &paddr, &ps))
+ goto inval;
if (is_gru_paddr(paddr))
goto inval;
paddr = paddr & ~((1UL << ps) - 1);
@@ -569,19 +502,9 @@ static irqreturn_t gru_intr(int chiplet, int blade)
continue;
}
- /*
- * This is running in interrupt context. Trylock the mmap_lock.
- * If it fails, retry the fault in user context.
- */
+ /* Address translation may sleep, so retry the fault in user context. */
gts->ustats.fmm_tlbmiss++;
- if (!gts->ts_force_cch_reload &&
- mmap_read_trylock(gts->ts_mm)) {
- gru_try_dropin(gru, gts, tfh, NULL);
- mmap_read_unlock(gts->ts_mm);
- } else {
- tfh_user_polling_mode(tfh);
- STAT(intr_mm_lock_failed);
- }
+ tfh_user_polling_mode(tfh);
}
return IRQ_HANDLED;
}
diff --git a/drivers/misc/sgi-gru/gruprocfs.c b/drivers/misc/sgi-gru/gruprocfs.c
index 97b8b38ab47df..b8139c27bc7f8 100644
--- a/drivers/misc/sgi-gru/gruprocfs.c
+++ b/drivers/misc/sgi-gru/gruprocfs.c
@@ -54,7 +54,6 @@ static int statistics_show(struct seq_file *s, void *p)
printstat(s, intr_cbr);
printstat(s, intr_tfh);
printstat(s, intr_spurious);
- printstat(s, intr_mm_lock_failed);
printstat(s, call_os);
printstat(s, call_os_wait_queue);
printstat(s, user_flush_tlb);
diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h
index 640daf1994df7..3348552925c61 100644
--- a/drivers/misc/sgi-gru/grutables.h
+++ b/drivers/misc/sgi-gru/grutables.h
@@ -182,7 +182,6 @@ struct gru_stats_s {
atomic_long_t intr_cbr;
atomic_long_t intr_tfh;
atomic_long_t intr_spurious;
- atomic_long_t intr_mm_lock_failed;
atomic_long_t call_os;
atomic_long_t call_os_wait_queue;
atomic_long_t user_flush_tlb;
--
2.47.3
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 2/3] misc: sgi-gru: remove obsolete atomic fault-handling state
2026-07-30 11:12 [PATCH 0/3] misc: sgi-gru: Remove broken page-table walks Muhammad Usama Anjum
2026-07-30 11:12 ` [PATCH 1/3] misc: sgi-gru: remove interrupt-context " Muhammad Usama Anjum
@ 2026-07-30 11:12 ` Muhammad Usama Anjum
2026-07-30 11:12 ` [PATCH 3/3] misc: sgi-gru: inline the user CBR status update Muhammad Usama Anjum
2026-07-30 12:22 ` [PATCH 0/3] misc: sgi-gru: Remove broken page-table walks Dimitri Sivanich
3 siblings, 0 replies; 10+ messages in thread
From: Muhammad Usama Anjum @ 2026-07-30 11:12 UTC (permalink / raw)
To: Dimitri Sivanich, Arnd Bergmann, Greg Kroah-Hartman, linux-kernel
Cc: Muhammad Usama Anjum
After interrupt faults are sent directly to user polling mode, only the
process-context call path remains. Remove the atomic flag passed through
the fault-handling interfaces and the related dead code.
No functional change.
Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
---
Only build tested.
---
drivers/misc/sgi-gru/grufault.c | 68 +++++++++++++--------------------
1 file changed, 26 insertions(+), 42 deletions(-)
diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c
index 5a87c12f444a3..e43fdea01cb6f 100644
--- a/drivers/misc/sgi-gru/grufault.c
+++ b/drivers/misc/sgi-gru/grufault.c
@@ -31,7 +31,6 @@
/* Return codes for vtop functions */
#define VTOP_SUCCESS 0
#define VTOP_INVALID -1
-#define VTOP_RETRY -2
/*
@@ -188,7 +187,7 @@ static int non_atomic_pte_lookup(struct vm_area_struct *vma,
}
static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr,
- int write, int atomic, unsigned long *gpa, int *pageshift)
+ int write, unsigned long *gpa, int *pageshift)
{
struct mm_struct *mm = gts->ts_mm;
struct vm_area_struct *vma;
@@ -199,9 +198,6 @@ static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr,
if (!vma)
goto inval;
- if (atomic)
- goto upm;
-
/* Order the caller's ms_range_active check before loading PTEs. */
rmb();
if (non_atomic_pte_lookup(vma, vaddr, write, &paddr, &ps))
@@ -215,8 +211,6 @@ static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr,
inval:
return VTOP_INVALID;
-upm:
- return VTOP_RETRY;
}
@@ -240,7 +234,7 @@ static void gru_flush_cache_cbe(struct gru_control_block_extended *cbe)
* the end of the bcopy tranfer, whichever is smaller.
*/
static void gru_preload_tlb(struct gru_state *gru,
- struct gru_thread_state *gts, int atomic,
+ struct gru_thread_state *gts,
unsigned long fault_vaddr, int asid, int write,
unsigned char tlb_preload_count,
struct gru_tlb_fault_handle *tfh,
@@ -262,13 +256,13 @@ static void gru_preload_tlb(struct gru_state *gru,
vaddr = min(vaddr, fault_vaddr + tlb_preload_count * PAGE_SIZE);
while (vaddr > fault_vaddr) {
- ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift);
+ ret = gru_vtop(gts, vaddr, write, &gpa, &pageshift);
if (ret || tfh_write_only(tfh, gpa, GAA_RAM, vaddr, asid, write,
GRU_PAGESIZE(pageshift)))
return;
gru_dbg(grudev,
- "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, rw %d, ps %d, gpa 0x%lx\n",
- atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh,
+ "gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, rw %d, ps %d, gpa 0x%lx\n",
+ gru->gs_gid, gts, tfh,
vaddr, asid, write, pageshift, gpa);
vaddr -= PAGE_SIZE;
STAT(tlb_preload_page);
@@ -276,13 +270,13 @@ static void gru_preload_tlb(struct gru_state *gru,
}
/*
- * Drop a TLB entry into the GRU. The fault is described by info in an TFH.
- * Input:
- * cb Address of user CBR. Null if not running in user context
- * Return:
- * 0 = dropin, exception, or switch to UPM successful
- * 1 = range invalidate active
- * < 0 = error code
+ * Drop a TLB entry into the GRU. The fault is described by info in a TFH.
+ * Input:
+ * cbk Address of the user CBR
+ * Return:
+ * 0 = dropin, exception, or switch to UPM successful
+ * 1 = retry required
+ * < 0 = error code
*
*/
static int gru_try_dropin(struct gru_state *gru,
@@ -292,7 +286,7 @@ static int gru_try_dropin(struct gru_state *gru,
{
struct gru_control_block_extended *cbe = NULL;
unsigned char tlb_preload_count = gts->ts_tlb_preload_count;
- int pageshift = 0, asid, write, ret, atomic = !cbk, indexway;
+ int pageshift = 0, asid, write, ret, indexway;
unsigned long gpa = 0, vaddr = 0;
/*
@@ -324,7 +318,7 @@ static int gru_try_dropin(struct gru_state *gru,
}
if (tfh->state == TFHSTATE_IDLE)
goto failidle;
- if (tfh->state == TFHSTATE_MISS_FMM && cbk)
+ if (tfh->state == TFHSTATE_MISS_FMM)
goto failfmm;
write = (tfh->cause & TFHCAUSE_TLB_MOD) != 0;
@@ -343,22 +337,20 @@ static int gru_try_dropin(struct gru_state *gru,
if (atomic_read(>s->ts_gms->ms_range_active))
goto failactive;
- ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift);
+ ret = gru_vtop(gts, vaddr, write, &gpa, &pageshift);
if (ret == VTOP_INVALID)
goto failinval;
- if (ret == VTOP_RETRY)
- goto failupm;
if (!(gts->ts_sizeavail & GRU_SIZEAVAIL(pageshift))) {
gts->ts_sizeavail |= GRU_SIZEAVAIL(pageshift);
- if (atomic || !gru_update_cch(gts)) {
+ if (!gru_update_cch(gts)) {
gts->ts_force_cch_reload = 1;
goto failupm;
}
}
if (unlikely(cbe) && pageshift == PAGE_SHIFT) {
- gru_preload_tlb(gru, gts, atomic, vaddr, asid, write, tlb_preload_count, tfh, cbe);
+ gru_preload_tlb(gru, gts, vaddr, asid, write, tlb_preload_count, tfh, cbe);
gru_flush_cache_cbe(cbe);
}
@@ -367,9 +359,9 @@ static int gru_try_dropin(struct gru_state *gru,
tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write,
GRU_PAGESIZE(pageshift));
gru_dbg(grudev,
- "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, indexway 0x%x,"
+ "gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, indexway 0x%x,"
" rw %d, ps %d, gpa 0x%lx\n",
- atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh, vaddr, asid,
+ gru->gs_gid, gts, tfh, vaddr, asid,
indexway, write, pageshift, gpa);
STAT(tlb_dropin);
return 0;
@@ -378,15 +370,12 @@ static int gru_try_dropin(struct gru_state *gru,
/* No asid (delayed unload). */
STAT(tlb_dropin_fail_no_asid);
gru_dbg(grudev, "FAILED no_asid tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr);
- if (!cbk)
- tfh_user_polling_mode(tfh);
- else
- gru_flush_cache(tfh);
+ gru_flush_cache(tfh);
gru_flush_cache_cbe(cbe);
return -EAGAIN;
failupm:
- /* Atomic failure switch CBR to UPM */
+ /* CCH update failure switches the CBR back to UPM. */
tfh_user_polling_mode(tfh);
gru_flush_cache_cbe(cbe);
STAT(tlb_dropin_fail_upm);
@@ -405,8 +394,7 @@ static int gru_try_dropin(struct gru_state *gru,
/* TFH status did not show exception pending */
gru_flush_cache(tfh);
gru_flush_cache_cbe(cbe);
- if (cbk)
- gru_flush_cache(cbk);
+ gru_flush_cache(cbk);
STAT(tlb_dropin_fail_no_exception);
gru_dbg(grudev, "FAILED non-exception tfh: 0x%p, status %d, state %d\n",
tfh, tfh->status, tfh->state);
@@ -416,14 +404,13 @@ static int gru_try_dropin(struct gru_state *gru,
/* TFH state was idle - no miss pending */
gru_flush_cache(tfh);
gru_flush_cache_cbe(cbe);
- if (cbk)
- gru_flush_cache(cbk);
+ gru_flush_cache(cbk);
STAT(tlb_dropin_fail_idle);
gru_dbg(grudev, "FAILED idle tfh: 0x%p, state %d\n", tfh, tfh->state);
return 0;
failinval:
- /* All errors (atomic & non-atomic) switch CBR to EXCEPTION state */
+ /* Invalid translations switch the CBR to EXCEPTION state. */
tfh_exception(tfh);
gru_flush_cache_cbe(cbe);
STAT(tlb_dropin_fail_invalid);
@@ -431,11 +418,8 @@ static int gru_try_dropin(struct gru_state *gru,
return -EFAULT;
failactive:
- /* Range invalidate active. Switch to UPM iff atomic */
- if (!cbk)
- tfh_user_polling_mode(tfh);
- else
- gru_flush_cache(tfh);
+ /* Retry after the active range invalidation completes. */
+ gru_flush_cache(tfh);
gru_flush_cache_cbe(cbe);
STAT(tlb_dropin_fail_range_active);
gru_dbg(grudev, "FAILED range active: tfh 0x%p, vaddr 0x%lx\n",
--
2.47.3
^ permalink raw reply related [flat|nested] 10+ messages in thread