Alpha arch development list
 help / color / mirror / Atom feed
* Re: [PATCH] alpha: remove unnecessary architecture-specific <asm/device.h>
From: Magnus Lindholm @ 2026-06-02  4:58 UTC (permalink / raw)
  To: Ethan Nelson-Moore; +Cc: linux-alpha, Richard Henderson, Matt Turner
In-Reply-To: <20260517233246.23915-1-enelsonmoore@gmail.com>

On Mon, May 18, 2026 at 1:32 AM Ethan Nelson-Moore
<enelsonmoore@gmail.com> wrote:
>
> arch/alpha/include/asm/device.h simply includes <asm-generic/device.h>,
> and therefore the Alpha-specific version is unnecessary. Remove it.
>
> Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
> ---
>  arch/alpha/include/asm/device.h | 6 ------
>  1 file changed, 6 deletions(-)
>  delete mode 100644 arch/alpha/include/asm/device.h
>
> diff --git a/arch/alpha/include/asm/device.h b/arch/alpha/include/asm/device.h
> deleted file mode 100644
> index 9ca75a7db23e..000000000000
> --- a/arch/alpha/include/asm/device.h
> +++ /dev/null
> @@ -1,6 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0-only */
> -/*
> - * Arch specific extensions to struct device
> - */
> -#include <asm-generic/device.h>
> -
> --
> 2.43.0
>

Looks good to me. Alpha does not add anything architecture-specific here,
so falling back to the generic header is fine.

Reviewed-by: Magnus Lindholm <linmag7@gmail.com>

^ permalink raw reply

* Re: [PATCH 0/3] alpha SMP fixes for EV7/Marvel
From: Magnus Lindholm @ 2026-05-31  8:24 UTC (permalink / raw)
  To: Matt Turner; +Cc: linux-alpha, linux-kernel, Richard Henderson, Ivan Kokshaysky
In-Reply-To: <20260530202544.59231-1-mattst88@gmail.com>

On Sat, May 30, 2026 at 10:25 PM Matt Turner <mattst88@gmail.com> wrote:
>
> I acquired an AlphaServer ES47 in 2010, and it's never been stable --
> deadlocking after random amounts of time. I could never make any
> connections with load, uptime, etc.
>
> The only dots I could connect was that the git test suite would always
> trigger the deadlock.
>
> I spent some time over the last week playing with Claude and have found
> *a* solution. With the first two patches in place, I've successfully run
> the git test suite 6 times in a row. I've never previously seen it run
> successfully without deadlocking the system.
>
> The first patch is generally applicable (not specific to EV7/Marvel).
> I'm unsure why this would never have caused problems on other systems
> (or why it would only be relevant for EV7/Marvel). That gives me some
> pause.
>
> The second patch applies only to EV7/Marvel, I believe. tl;dr: IPIs seem
> to be lost.
>
> The third patch adds some accounting to /proc/interrupts to report the
> number of lost interrupts, confirming the problem from patch 2.
>
> Please review.
>
> Matt
>

Hi Matt,

Thanks for working on this. This is very impressive work, and it looks like
you're close to nailing down some long-standing bugs and making the Marvel
platform a lot more usable with SMP kernels. The lost-edge IPI diagnosis looks
plausible, but I hit a few issues while reviewing/testing the series.

First, after applying the series I hit a build failure. Patch 1 adds:

extern spinlock_t alpha_smp_ipi_lock;

to arch/alpha/include/asm/smp.h, but that header can be included before
spinlock_t is defined, e.g. while building kernel/sched/rq-offsets.s:

arch/alpha/include/asm/smp.h:60:8: error: unknown type name 'spinlock_t'

Including <linux/spinlock_types.h> from asm/smp.h, or avoiding exposing
spinlock_t from that early header, fixes that part.

Patch 2 also appears not to be buildable independently: it updates
cpu_data[].rescued_{reschedule,call_func,cpu_stop}_count, but those fields are
only introduced in patch 3. Please either move the struct additions into patch
2, move the accounting into patch 3, or squash those patches.

I also wonder if alpha_drain_ipi() should disable interrupts before looking at
the per-CPU IPI word. That would avoid reading ipi_data[smp_processor_id()].bits
before local IRQs are disabled, and would keep the CPU lookup and pending-bit
check in the same IRQ-disabled section:

local_irq_save(flags);
cpu = smp_processor_id();
if (READ_ONCE(ipi_data[cpu].bits))
handle_ipi(NULL);
local_irq_restore(flags);

That looks safer than reading ipi_data[smp_processor_id()].bits before
local_irq_save().

On the design side, patch 1 says it serializes all synchronous IPI operations,
but it seems to only wrap the Alpha arch TLB/icache/IMB users. Either the commit
message should narrow that claim, or the serialization needs to live lower in
the IPI/call-function path. The patch seems to do: "serialize a subset of Alpha
arch synchronous IPI users, mainly TLB/cache/IMB flushes"

Also, the series does not apply cleanly to current v7.1-rc1 directly.
It appears to
depend on the Alpha GENERIC_ENTRY series:

Link: https://lore.kernel.org/linux-alpha/20260529142322.1362438-1-linmag7@gmail.com/T/#t

which is still under review and not in mainline yet. Please mention
that dependency
in the cover letter and include the base commit and/or a lore link to
the prerequisite
series.

Finally, this adds a global spin_trylock()/spin_unlock() around hot paths such
as migrate_flush_tlb_page(). That has no impact on non-Alpha architectures, but
it serializes these operations for all Alpha SMP systems, while the bug
description is EV7/Marvel/IO7-specific. Can this be justified for non-EV7
systems, or gated to the affected platform?

Thanks,
Magnus

^ permalink raw reply

* [PATCH 3/3] alpha: Break down rescued IPI counter by type in /proc/interrupts
From: Matt Turner @ 2026-05-30 20:25 UTC (permalink / raw)
  To: linux-alpha
  Cc: linux-kernel, Richard Henderson, Magnus Lindholm, Ivan Kokshaysky,
	Matt Turner
In-Reply-To: <20260530202544.59231-1-mattst88@gmail.com>

Add per-type rescued IPI counters to cpuinfo_alpha:
  rescued_reschedule_count (RIP:)
  rescued_call_func_count  (RIF:)
  rescued_cpu_stop_count   (RIS:)

alpha_poll_ipi_inirq() peeks at ipi_data[cpu].bits before handle_ipi()
clears them via xchg(), then increments the appropriate per-CPU counter.
Expose all three as separate rows in /proc/interrupts alongside the
existing "IPI:" row.

This lets us distinguish the deadlock-causing subset (RIF: IPI_CALL_FUNC,
of which wait=1 callers are the ones that deadlock) from the harmless
majority (RIP: reschedule). A non-zero RIF count confirms the EV7/IO7
edge-triggered IPI loss hypothesis.

Note: handle_ipi() also increments ipi_count unconditionally, so the
"IPI:" row in /proc/interrupts includes both normal and rescued deliveries.
The RIP:/RIF:/RIS: counters sample bits before the xchg(); bits that
arrive after the READ_ONCE are drained by handle_ipi() but not reflected
in these counters — they are approximate but sufficient for diagnosis.

Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/include/asm/smp.h |  3 +++
 arch/alpha/kernel/irq.c      | 12 ++++++++++++
 2 files changed, 15 insertions(+)

diff --git ./arch/alpha/include/asm/smp.h ./arch/alpha/include/asm/smp.h
index 8bd529376cf6..98f522ee367f 100644
--- ./arch/alpha/include/asm/smp.h
+++ ./arch/alpha/include/asm/smp.h
@@ -31,6 +31,9 @@ struct cpuinfo_alpha {
 	int need_new_asn;
 	int asn_lock;
 	unsigned long ipi_count;
+	unsigned long rescued_reschedule_count;
+	unsigned long rescued_call_func_count;
+	unsigned long rescued_cpu_stop_count;
 	unsigned long prof_multiplier;
 	unsigned long prof_counter;
 	unsigned char mcheck_expected;
diff --git ./arch/alpha/kernel/irq.c ./arch/alpha/kernel/irq.c
index c67047c5d830..34709e1c42c5 100644
--- ./arch/alpha/kernel/irq.c
+++ ./arch/alpha/kernel/irq.c
@@ -76,6 +76,18 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 	for_each_online_cpu(j)
 		seq_printf(p, "%10lu ", cpu_data[j].ipi_count);
 	seq_putc(p, '\n');
+	seq_puts(p, "RIP: ");
+	for_each_online_cpu(j)
+		seq_printf(p, "%10lu ", cpu_data[j].rescued_reschedule_count);
+	seq_puts(p, "          Rescued IPIs: reschedule\n");
+	seq_puts(p, "RIF: ");
+	for_each_online_cpu(j)
+		seq_printf(p, "%10lu ", cpu_data[j].rescued_call_func_count);
+	seq_puts(p, "          Rescued IPIs: call function\n");
+	seq_puts(p, "RIS: ");
+	for_each_online_cpu(j)
+		seq_printf(p, "%10lu ", cpu_data[j].rescued_cpu_stop_count);
+	seq_puts(p, "          Rescued IPIs: cpu stop\n");
 #endif
 	seq_puts(p, "PMI: ");
 	for_each_online_cpu(j)
-- 
2.53.0


^ permalink raw reply related

* [PATCH 2/3] alpha: Fix SMP IPI loss when target CPU is in interrupt handler
From: Matt Turner @ 2026-05-30 20:25 UTC (permalink / raw)
  To: linux-alpha
  Cc: linux-kernel, Richard Henderson, Magnus Lindholm, Ivan Kokshaysky,
	Matt Turner
In-Reply-To: <20260530202544.59231-1-mattst88@gmail.com>

On EV7/IO7, the wripir PALcall delivers IPIs as edge-triggered hardware
signals through the IO7 I/O controller. If the target CPU is already
executing at IPL=7 inside do_entInt handling another interrupt, the IPI
edge is lost: the hardware never re-delivers it when the CPU drops back
to IPL=0.

The software IPI bit in ipi_data[cpu].bits is set before wripir is
called, so it remains set after the interrupt handler returns. But
because no hardware edge fires, handle_ipi() is never invoked again,
and the sending CPU spins forever in csd_lock_wait.

This race is the root cause of a 15-year SMP deadlock on EV7/Marvel
systems. It is reliably triggered by workloads that generate many
synchronous IPIs (TLB flushes via on_each_cpu(wait=1)) while the
target CPU receives concurrent I/O or RTC interrupts.

Fix: add alpha_poll_ipi_inirq(), called from do_entInt within each
interrupt handler's irq_enter/irq_exit bracket. It checks
ipi_data[smp_processor_id()].bits and drains any pending IPIs that
arrived while we were at IPL=7, before irq_exit() opens the softirq
window where a TLB-flush softirq could itself deadlock on
alpha_smp_ipi_lock. The check is a single READ_ONCE so there is no
overhead when no IPI was missed.

For the RTC interrupt (case 1 in do_entInt), handle_irq() already calls
its own irq_enter()/irq_exit() internally. The outer irq_enter/irq_exit
pair added here is intentional: it keeps irq_count > 0 while handle_irq()
runs, so handle_irq()'s inner irq_exit() sees a non-zero count and skips
the softirq window. The softirq window is deferred until the outer
irq_exit(), which runs after alpha_poll_ipi_inirq() has already drained
any pending IPIs. Without this outer bracket, irq_exit() inside
handle_irq() could open the softirq window before any missed IPIs are
rescued, risking a deadlock on alpha_smp_ipi_lock.

Approximately 98% of rescued IPIs are IPI_CALL_FUNC (the TLB-flush
type), confirming that IO7 genuinely drops the hardware edge rather than
holding it pending until IPL falls.

A lost IPI_CALL_FUNC only deadlocks when the sender is blocking (wait=1).
wait=0 callers do not hang, but silently skip the function on the remote
CPU, which may be a correctness issue in its own right.

This fix is complementary to the alpha_smp_ipi_lock serialization
(previous commit). Both are required:
  - Serialization prevents two CPUs simultaneously issuing wait=1 IPIs
    from deadlocking each other in csd_lock_wait.
  - This fix prevents a single wait=1 caller from deadlocking due to an
    IPI edge lost to an IPL=7 window on the remote CPU.

Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/kernel/irq_alpha.c | 29 ++++++++++++++++++++++++++++-
 arch/alpha/kernel/proto.h     |  1 +
 arch/alpha/kernel/smp.c       | 35 +++++++++++++++++++++++++++++++++++
 3 files changed, 64 insertions(+), 1 deletion(-)

diff --git ./arch/alpha/kernel/irq_alpha.c ./arch/alpha/kernel/irq_alpha.c
index ac941172ae66..0e4234ef7ea0 100644
--- ./arch/alpha/kernel/irq_alpha.c
+++ ./arch/alpha/kernel/irq_alpha.c
@@ -69,22 +69,49 @@ do_entInt(unsigned long type, unsigned long vector,
 		break;
 #endif
 	case 1:
-		/* handle_irq() already does irq_enter()/irq_exit() */
+		/*
+		 * Wrap handle_irq() in our own irq_enter/irq_exit so that the
+		 * inner irq_exit() inside handle_irq() does not run softirqs
+		 * (irq_count remains > 0). We poll for lost IPIs before the
+		 * outer irq_exit(), which is where softirqs may run. This
+		 * prevents a TLB flush softirq from deadlocking on
+		 * alpha_smp_ipi_lock while the sending CPU waits for our ACK.
+		 */
+		irq_enter();
 		handle_irq(RTC_IRQ);
+#ifdef CONFIG_SMP
+		alpha_poll_ipi_inirq(regs);
+#endif
+		irq_exit();
 		break;
 	case 2:
 		irq_enter();
 		alpha_mv.machine_check(vector, la_ptr);
+#ifdef CONFIG_SMP
+		alpha_poll_ipi_inirq(regs);
+#endif
 		irq_exit();
 		break;
 	case 3:
 		irq_enter();
 		alpha_mv.device_interrupt(vector);
+#ifdef CONFIG_SMP
+		/*
+		 * Drain any IPIs whose edge was lost while we were at IPL=7.
+		 * Must be called before irq_exit() to prevent softirqs (e.g.
+		 * a TLB flush) from deadlocking on alpha_smp_ipi_lock while
+		 * the sending CPU spins in csd_lock_wait.
+		 */
+		alpha_poll_ipi_inirq(regs);
+#endif
 		irq_exit();
 		break;
 	case 4:
 		irq_enter();
 		perf_irq(la_ptr, regs);
+#ifdef CONFIG_SMP
+		alpha_poll_ipi_inirq(regs);
+#endif
 		irq_exit();
 		break;
 	default:
diff --git ./arch/alpha/kernel/proto.h ./arch/alpha/kernel/proto.h
index f138bd494628..04879e0b2932 100644
--- ./arch/alpha/kernel/proto.h
+++ ./arch/alpha/kernel/proto.h
@@ -120,6 +120,7 @@ extern void unregister_srm_console(void);
 /* smp.c */
 extern void setup_smp(void);
 extern void handle_ipi(struct pt_regs *);
+extern void alpha_poll_ipi_inirq(struct pt_regs *);
 extern void __init smp_callin(void);
 
 /* bios32.c */
diff --git ./arch/alpha/kernel/smp.c ./arch/alpha/kernel/smp.c
index d900da49b0d8..099e1ac6a0d6 100644
--- ./arch/alpha/kernel/smp.c
+++ ./arch/alpha/kernel/smp.c
@@ -557,6 +557,41 @@ handle_ipi(struct pt_regs *regs)
 		recv_secondary_console_msg();
 }
 
+/*
+ * On EV7/IO7, IPI signals are edge-triggered. If an IPI arrives while this
+ * CPU is executing at IPL=7 (inside another interrupt handler), the hardware
+ * edge is lost. The software bit in ipi_data[] remains set but handle_ipi()
+ * is never re-invoked, causing the sending CPU to spin forever in csd_lock_wait.
+ *
+ * Call this from within hardirq context (between irq_enter and irq_exit) to
+ * drain any IPIs that arrived while we were running at IPL=7, before irq_exit()
+ * opens the softirq window where a TLB flush could deadlock on alpha_smp_ipi_lock.
+ */
+void alpha_poll_ipi_inirq(struct pt_regs *regs)
+{
+	int cpu = smp_processor_id();
+	unsigned long bits = READ_ONCE(ipi_data[cpu].bits);
+
+	if (!bits)
+		return;
+
+	/*
+	 * Peek at type bits before handle_ipi() clears them via xchg().
+	 * Bits arriving after this READ_ONCE are drained but not counted;
+	 * the counters are approximate but sufficient for diagnosis.
+	 * Note: handle_ipi() also increments ipi_count, so the "IPI:" row
+	 * in /proc/interrupts includes both normal and rescued deliveries.
+	 */
+	if (bits & (1UL << IPI_RESCHEDULE))
+		cpu_data[cpu].rescued_reschedule_count++;
+	if (bits & (1UL << IPI_CALL_FUNC))
+		cpu_data[cpu].rescued_call_func_count++;
+	if (bits & (1UL << IPI_CPU_STOP))
+		cpu_data[cpu].rescued_cpu_stop_count++;
+
+	handle_ipi(regs);
+}
+
 void
 arch_smp_send_reschedule(int cpu)
 {
-- 
2.53.0


^ permalink raw reply related

* [PATCH 1/3] alpha: smp: Serialize all synchronous IPI operations to fix SMP deadlock
From: Matt Turner @ 2026-05-30 20:25 UTC (permalink / raw)
  To: linux-alpha
  Cc: linux-kernel, Richard Henderson, Magnus Lindholm, Ivan Kokshaysky,
	Matt Turner
In-Reply-To: <20260530202544.59231-1-mattst88@gmail.com>

Two or more CPUs simultaneously calling any function that uses
on_each_cpu(wait=1) or smp_call_function(wait=1) deadlock: each blocks
in csd_lock_wait spinning while waiting for the remote CPU to signal CSD
completion. While spinning, neither CPU can receive the other's IPI, so
neither completion signal arrives — permanent hang.

Affected callers: smp_imb, flush_tlb_all, flush_tlb_mm, flush_tlb_page,
flush_icache_user_page (smp.c) and migrate_flush_tlb_page (tlbflush.c).

Introduce alpha_smp_ipi_lock (plain spinlock, defined in smp.c, declared
in asm/smp.h) and apply it to all six callers. Rather than spin_lock(),
use a trylock loop with alpha_drain_ipi(): if the lock is held, the loser
actively drains any pending IPI bits on the local CPU before retrying.
This is necessary because some callers hold IRQs disabled (e.g. paths
that take spin_lock_irqsave), so no RTC interrupt will fire to rescue a
lost wripir edge via alpha_poll_ipi_inirq(). alpha_drain_ipi() calls
handle_ipi() under local_irq_save/restore, satisfying handle_ipi()'s
requirement that IRQs be disabled, without touching lockdep
hardirq-context state.

This fix is necessary but not sufficient. A separate, independent
deadlock path exists: if the target CPU is inside do_entInt at IPL=7
when wripir fires, the hardware IPI edge is lost and the sending CPU
spins forever even when only one CPU is issuing a wait=1 call. That
race is fixed independently by alpha_poll_ipi_inirq() (see follow-on
commit). Both fixes are required for a complete solution.

The deadlock has been observed on EV7/Marvel under workloads generating
a high rate of synchronous TLB flush IPIs (e.g. the git test suite).

Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/include/asm/smp.h |  9 ++++++
 arch/alpha/kernel/smp.c      | 62 ++++++++++++++++++++++++++++++++++++
 arch/alpha/mm/tlbflush.c     |  3 ++
 3 files changed, 74 insertions(+)

diff --git ./arch/alpha/include/asm/smp.h ./arch/alpha/include/asm/smp.h
index 2264ae72673b..8bd529376cf6 100644
--- ./arch/alpha/include/asm/smp.h
+++ ./arch/alpha/include/asm/smp.h
@@ -48,6 +48,15 @@ extern int smp_num_cpus;
 extern void arch_send_call_function_single_ipi(int cpu);
 extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
 
+/*
+ * Global spinlock serializing all synchronous (wait=1) IPI callers.
+ * Callers must use the trylock+alpha_drain_ipi() pattern, not spin_lock(),
+ * because some call sites hold IRQs disabled and cannot rely on the RTC
+ * interrupt to rescue a lost wripir edge.
+ */
+extern spinlock_t alpha_smp_ipi_lock;
+extern void alpha_drain_ipi(void);
+
 #else /* CONFIG_SMP */
 
 #define hard_smp_processor_id()		0
diff --git ./arch/alpha/kernel/smp.c ./arch/alpha/kernel/smp.c
index ed06367ece57..d900da49b0d8 100644
--- ./arch/alpha/kernel/smp.c
+++ ./arch/alpha/kernel/smp.c
@@ -597,11 +597,61 @@ ipi_imb(void *ignored)
 	imb();
 }
 
+/*
+ * Serialize all synchronous (wait=1) IPI operations to prevent cross-CPU
+ * deadlock on EV7/Marvel.  If two CPUs simultaneously call any function that
+ * uses on_each_cpu(wait=1) or smp_call_function(wait=1), each blocks in
+ * csd_lock_wait spinning for the remote CPU to signal completion.  While
+ * spinning, neither CPU can receive the other's IPI, so neither completion
+ * signal arrives — permanent hang.
+ *
+ * A plain spinlock (not irqsave) is intentional: the CPU that loses the lock
+ * race spins with IRQs enabled and can service the winner's IPI before
+ * taking the lock itself.
+ *
+ * All callers of synchronous IPIs — including migrate_flush_tlb_page in
+ * tlbflush.c — must hold this lock.
+ */
+DEFINE_SPINLOCK(alpha_smp_ipi_lock);
+
+/*
+ * Drain any pending IPIs for this CPU while spinning on alpha_smp_ipi_lock.
+ *
+ * The lock holder has already sent a wripir but is blocked in csd_lock_wait
+ * waiting for our IPI ACK.  We cannot simply spin on the lock: if IRQs are
+ * disabled (e.g. caller holds a spin_lock_irqsave), no RTC interrupt will
+ * fire and the lost wripir edge is never rescued by alpha_poll_ipi_inirq.
+ *
+ * Call this from the trylock loop so the IPI is processed even with IRQs
+ * disabled, breaking the circular wait.
+ *
+ * handle_ipi() requires IRQs disabled: generic_smp_call_function_interrupt
+ * asserts lockdep_assert_irqs_disabled().  Use local_irq_save/restore so
+ * this is safe whether the caller has IRQs enabled (e.g. page fault path)
+ * or disabled (e.g. spin_lock_irqsave holder).  Avoid __irq_enter_raw/
+ * __irq_exit_raw: those manipulate lockdep hardirq-context state and trigger
+ * a lockdep WARNING when called while lockdep already tracks hardirq context.
+ */
+void alpha_drain_ipi(void)
+{
+	unsigned long flags;
+
+	if (!READ_ONCE(ipi_data[smp_processor_id()].bits))
+		return;
+
+	local_irq_save(flags);
+	handle_ipi(NULL); /* regs unused in handle_ipi() */
+	local_irq_restore(flags);
+}
+
 void
 smp_imb(void)
 {
 	/* Must wait other processors to flush their icache before continue. */
+	while (!spin_trylock(&alpha_smp_ipi_lock))
+		alpha_drain_ipi();
 	on_each_cpu(ipi_imb, NULL, 1);
+	spin_unlock(&alpha_smp_ipi_lock);
 }
 EXPORT_SYMBOL(smp_imb);
 
@@ -616,7 +666,10 @@ flush_tlb_all(void)
 {
 	/* Although we don't have any data to pass, we do want to
 	   synchronize with the other processors.  */
+	while (!spin_trylock(&alpha_smp_ipi_lock))
+		alpha_drain_ipi();
 	on_each_cpu(ipi_flush_tlb_all, NULL, 1);
+	spin_unlock(&alpha_smp_ipi_lock);
 }
 
 #define asn_locked() (cpu_data[smp_processor_id()].asn_lock)
@@ -651,7 +704,10 @@ flush_tlb_mm(struct mm_struct *mm)
 		}
 	}
 
+	while (!spin_trylock(&alpha_smp_ipi_lock))
+		alpha_drain_ipi();
 	smp_call_function(ipi_flush_tlb_mm, mm, 1);
+	spin_unlock(&alpha_smp_ipi_lock);
 
 	preempt_enable();
 }
@@ -702,7 +758,10 @@ flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
 	data.mm = mm;
 	data.addr = addr;
 
+	while (!spin_trylock(&alpha_smp_ipi_lock))
+		alpha_drain_ipi();
 	smp_call_function(ipi_flush_tlb_page, &data, 1);
+	spin_unlock(&alpha_smp_ipi_lock);
 
 	preempt_enable();
 }
@@ -752,7 +811,10 @@ flush_icache_user_page(struct vm_area_struct *vma, struct page *page,
 		}
 	}
 
+	while (!spin_trylock(&alpha_smp_ipi_lock))
+		alpha_drain_ipi();
 	smp_call_function(ipi_flush_icache_page, mm, 1);
+	spin_unlock(&alpha_smp_ipi_lock);
 
 	preempt_enable();
 }
diff --git ./arch/alpha/mm/tlbflush.c ./arch/alpha/mm/tlbflush.c
index ccbc317b9a34..37607d08796b 100644
--- ./arch/alpha/mm/tlbflush.c
+++ ./arch/alpha/mm/tlbflush.c
@@ -89,7 +89,10 @@ void migrate_flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
 	 * This is the "combined" version of flush_tlb_mm + per-page invalidate.
 	 */
 	preempt_disable();
+	while (!spin_trylock(&alpha_smp_ipi_lock))
+		alpha_drain_ipi();
 	on_each_cpu(ipi_flush_mm_and_page, &d, 1);
+	spin_unlock(&alpha_smp_ipi_lock);
 
 	/*
 	 * mimic flush_tlb_mm()'s mm_users<=1 optimization.
-- 
2.53.0


^ permalink raw reply related

* [PATCH 0/3] alpha SMP fixes for EV7/Marvel
From: Matt Turner @ 2026-05-30 20:25 UTC (permalink / raw)
  To: linux-alpha
  Cc: linux-kernel, Richard Henderson, Magnus Lindholm, Ivan Kokshaysky,
	Matt Turner

I acquired an AlphaServer ES47 in 2010, and it's never been stable --
deadlocking after random amounts of time. I could never make any
connections with load, uptime, etc.

The only dots I could connect was that the git test suite would always
trigger the deadlock.

I spent some time over the last week playing with Claude and have found
*a* solution. With the first two patches in place, I've successfully run
the git test suite 6 times in a row. I've never previously seen it run
successfully without deadlocking the system.

The first patch is generally applicable (not specific to EV7/Marvel).
I'm unsure why this would never have caused problems on other systems
(or why it would only be relevant for EV7/Marvel). That gives me some
pause.

The second patch applies only to EV7/Marvel, I believe. tl;dr: IPIs seem
to be lost.

The third patch adds some accounting to /proc/interrupts to report the
number of lost interrupts, confirming the problem from patch 2.

Please review.

Matt

Matt Turner (3):
  alpha: smp: Serialize all synchronous IPI operations to fix SMP
    deadlock
  alpha: Fix SMP IPI loss when target CPU is in interrupt handler
  alpha: Break down rescued IPI counter by type in /proc/interrupts

 arch/alpha/include/asm/smp.h  | 12 +++++
 arch/alpha/kernel/irq.c       | 12 +++++
 arch/alpha/kernel/irq_alpha.c | 29 ++++++++++-
 arch/alpha/kernel/proto.h     |  1 +
 arch/alpha/kernel/smp.c       | 97 +++++++++++++++++++++++++++++++++++
 arch/alpha/mm/tlbflush.c      |  3 ++
 6 files changed, 153 insertions(+), 1 deletion(-)

-- 
2.53.0


^ permalink raw reply

* [PATCH v9 14/37] mm: remove arch vma_alloc_zeroed_movable_folio overrides
From: Michael S. Tsirkin @ 2026-05-29 15:23 UTC (permalink / raw)
  To: linux-kernel
  Cc: David Hildenbrand (Arm), Jason Wang, Xuan Zhuo,
	Eugenio Pérez, Muchun Song, Oscar Salvador, Andrew Morton,
	Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
	Johannes Weiner, Zi Yan, Baolin Wang, Nico Pache, Ryan Roberts,
	Dev Jain, Barry Song, Lance Yang, Hugh Dickins, Matthew Brost,
	Joshua Hahn, Rakie Kim, Byungchul Park, Gregory Price, Ying Huang,
	Alistair Popple, Christoph Lameter, David Rientjes,
	Roman Gushchin, Harry Yoo, Axel Rasmussen, Yuanchu Xie, Wei Xu,
	Chris Li, Kairui Song, Kemeng Shi, Nhat Pham, Baoquan He,
	virtualization, linux-mm, Andrea Arcangeli, Magnus Lindholm,
	Greg Ungerer, Geert Uytterhoeven, Richard Henderson, Matt Turner,
	Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger, Sven Schnelle, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	linux-alpha, linux-m68k, linux-s390
In-Reply-To: <cover.1780067977.git.mst@redhat.com>

Now that the generic vma_alloc_zeroed_movable_folio() uses
__GFP_ZERO, the arch-specific macros on alpha, m68k, s390, and
x86 that did the same thing are redundant.  Remove them.

arm64 is not affected: it has a real function override that
handles MTE tag zeroing, not just __GFP_ZERO.

Suggested-by: David Hildenbrand <david@kernel.org>
Acked-by: Magnus Lindholm <linmag7@gmail.com>
Acked-by: Greg Ungerer <gerg@linux-m68k.org>
Acked-by: Geert Uytterhoeven <geert@linux-m68k.org> # m68k
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Assisted-by: Claude:claude-opus-4-6
Reviewed-by: Gregory Price <gourry@gourry.net>
---
 arch/alpha/include/asm/page.h   | 3 ---
 arch/m68k/include/asm/page_no.h | 3 ---
 arch/s390/include/asm/page.h    | 3 ---
 arch/x86/include/asm/page.h     | 3 ---
 include/linux/highmem.h         | 8 +++++---
 5 files changed, 5 insertions(+), 15 deletions(-)

diff --git a/arch/alpha/include/asm/page.h b/arch/alpha/include/asm/page.h
index 59d01f9b77f6..4327029cd660 100644
--- a/arch/alpha/include/asm/page.h
+++ b/arch/alpha/include/asm/page.h
@@ -12,9 +12,6 @@
 
 extern void clear_page(void *page);
 
-#define vma_alloc_zeroed_movable_folio(vma, vaddr) \
-	vma_alloc_folio(GFP_HIGHUSER_MOVABLE | __GFP_ZERO, 0, vma, vaddr)
-
 extern void copy_page(void * _to, void * _from);
 #define copy_user_page(to, from, vaddr, pg)	copy_page(to, from)
 
diff --git a/arch/m68k/include/asm/page_no.h b/arch/m68k/include/asm/page_no.h
index d2532bc407ef..f511b763a235 100644
--- a/arch/m68k/include/asm/page_no.h
+++ b/arch/m68k/include/asm/page_no.h
@@ -12,9 +12,6 @@ extern unsigned long memory_end;
 
 #define copy_user_page(to, from, vaddr, pg)	copy_page(to, from)
 
-#define vma_alloc_zeroed_movable_folio(vma, vaddr) \
-	vma_alloc_folio(GFP_HIGHUSER_MOVABLE | __GFP_ZERO, 0, vma, vaddr)
-
 #define __pa(vaddr)		((unsigned long)(vaddr))
 #define __va(paddr)		((void *)((unsigned long)(paddr)))
 
diff --git a/arch/s390/include/asm/page.h b/arch/s390/include/asm/page.h
index 56da819a79e6..e995d2a413f9 100644
--- a/arch/s390/include/asm/page.h
+++ b/arch/s390/include/asm/page.h
@@ -67,9 +67,6 @@ static inline void copy_page(void *to, void *from)
 
 #define copy_user_page(to, from, vaddr, pg)	copy_page(to, from)
 
-#define vma_alloc_zeroed_movable_folio(vma, vaddr) \
-	vma_alloc_folio(GFP_HIGHUSER_MOVABLE | __GFP_ZERO, 0, vma, vaddr)
-
 #ifdef CONFIG_STRICT_MM_TYPECHECKS
 #define STRICT_MM_TYPECHECKS
 #endif
diff --git a/arch/x86/include/asm/page.h b/arch/x86/include/asm/page.h
index 416dc88e35c1..92fa975b46f3 100644
--- a/arch/x86/include/asm/page.h
+++ b/arch/x86/include/asm/page.h
@@ -28,9 +28,6 @@ static inline void copy_user_page(void *to, void *from, unsigned long vaddr,
 	copy_page(to, from);
 }
 
-#define vma_alloc_zeroed_movable_folio(vma, vaddr) \
-	vma_alloc_folio(GFP_HIGHUSER_MOVABLE | __GFP_ZERO, 0, vma, vaddr)
-
 #ifndef __pa
 #define __pa(x)		__phys_addr((unsigned long)(x))
 #endif
diff --git a/include/linux/highmem.h b/include/linux/highmem.h
index ffa683f64f1d..7b5955bf9336 100644
--- a/include/linux/highmem.h
+++ b/include/linux/highmem.h
@@ -303,7 +303,6 @@ static inline void clear_user_highpages(struct page *page, unsigned long vaddr,
 #endif
 }
 
-#ifndef vma_alloc_zeroed_movable_folio
 /**
  * vma_alloc_zeroed_movable_folio - Allocate a zeroed page for a VMA.
  * @vma: The VMA the page is to be allocated for.
@@ -317,12 +316,15 @@ static inline void clear_user_highpages(struct page *page, unsigned long vaddr,
  * we are out of memory.
  */
 static inline
-struct folio *vma_alloc_zeroed_movable_folio(struct vm_area_struct *vma,
+struct folio *vma_alloc_zeroed_movable_folio_noprof(struct vm_area_struct *vma,
 				   unsigned long vaddr)
 {
-	return vma_alloc_folio(GFP_HIGHUSER_MOVABLE | __GFP_ZERO,
+	return vma_alloc_folio_noprof(GFP_HIGHUSER_MOVABLE | __GFP_ZERO,
 			      0, vma, vaddr);
 }
+#ifndef vma_alloc_zeroed_movable_folio
+#define vma_alloc_zeroed_movable_folio(...) \
+	alloc_hooks(vma_alloc_zeroed_movable_folio_noprof(__VA_ARGS__))
 #endif
 
 static inline void clear_highpage(struct page *page)
-- 
MST


^ permalink raw reply related

* [PATCH v2 8/8] alpha: enable GENERIC_ENTRY and GENERIC_IRQ_ENTRY
From: Magnus Lindholm @ 2026-05-29 14:22 UTC (permalink / raw)
  To: richard.henderson, mattst88, linux-kernel, linux-alpha
  Cc: glaubitz, mcree, ink, macro, Magnus Lindholm
In-Reply-To: <20260529142322.1362438-1-linmag7@gmail.com>

Wire Alpha into the generic entry code for syscall entry/exit and
return-to-user handling, while keeping the low-level PALcode return paths
Alpha-specific.

Move most of the syscall entry/exit logic out of entSys and into C helpers
built around the generic entry API. Syscall entry now uses
syscall_enter_from_user_mode(), records Alpha-local syscall metadata in
thread_info, handles the ptrace/seccomp skip decision, and selects the
syscall table target in C.  The final target call remains in entry.S so
Alpha can preserve its existing syscall ABI and assembly syscall-table
wrappers.

On return from syscalls, finish Alpha's r0/r19 result encoding and
skipped-syscall restart handling in C before calling
syscall_exit_to_user_mode(). Non-syscall returns to user mode use a
separate alpha_exit_to_user_mode() helper, which disables interrupts,
runs irqentry_exit_to_user_mode_prepare(), and then enters the common
exit_to_user_mode() path.

Keep the remaining PALcode restore handling in assembly. In particular,
kernel-mode returns still need Alpha-specific lockdep IRQ-state annotation
based on the saved processor status, while user-mode returns are handed to
the generic exit-to-user code.

Add the generic-entry support bits needed by common code, including
thread_info.syscall_work, syscall trace support, ptrace sysemu request
numbers, and arch_syscall_is_vdso_sigreturn().

This has been tested by booting Alpha with GENERIC_ENTRY enabled, checking
lockdep IRQ-state accounting, running fork/clone-heavy package builds, and
running the seccomp as well as strace test suites.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
 arch/alpha/Kconfig                    |   3 +
 arch/alpha/include/asm/entry-common.h |  14 ++
 arch/alpha/include/asm/ptrace.h       |  14 +-
 arch/alpha/include/asm/stacktrace.h   |  20 ++
 arch/alpha/include/asm/syscall.h      |  11 +-
 arch/alpha/include/asm/thread_info.h  |  30 +--
 arch/alpha/kernel/asm-offsets.c       |   5 +
 arch/alpha/kernel/entry.S             | 322 +++++++-------------------
 arch/alpha/kernel/irq_alpha.c         |  14 +-
 arch/alpha/kernel/proto.h             |   9 +-
 arch/alpha/kernel/ptrace.c            | 135 ++++++-----
 arch/alpha/kernel/signal.c            | 155 ++++++++++---
 12 files changed, 367 insertions(+), 365 deletions(-)
 create mode 100644 arch/alpha/include/asm/entry-common.h
 create mode 100644 arch/alpha/include/asm/stacktrace.h

diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index e53ef2d88463..74795e22aafa 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -2,6 +2,9 @@
 config ALPHA
 	bool
 	default y
+	select GENERIC_IRQ_ENTRY
+	select GENERIC_ENTRY
+	select HAVE_SYSCALL_TRACEPOINTS
 	select ARCH_32BIT_USTAT_F_TINODE
 	select ARCH_HAS_CURRENT_STACK_POINTER
 	select ARCH_HAS_DMA_OPS if PCI
diff --git a/arch/alpha/include/asm/entry-common.h b/arch/alpha/include/asm/entry-common.h
new file mode 100644
index 000000000000..a811c73454d2
--- /dev/null
+++ b/arch/alpha/include/asm/entry-common.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef ARCH_ALPHA_ENTRY_COMMON_H
+#define ARCH_ALPHA_ENTRY_COMMON_H
+
+#include <asm/stacktrace.h> /* For on_thread_stack() */
+#include <asm/syscall.h>
+
+#define arch_exit_to_user_mode_work arch_exit_to_user_mode_work
+
+static __always_inline void arch_exit_to_user_mode_work(struct pt_regs *regs,
+				unsigned long ti_work)
+{
+}
+#endif
diff --git a/arch/alpha/include/asm/ptrace.h b/arch/alpha/include/asm/ptrace.h
index 8e0a589e2d15..430e8dc27ff7 100644
--- a/arch/alpha/include/asm/ptrace.h
+++ b/arch/alpha/include/asm/ptrace.h
@@ -3,7 +3,7 @@
 #define _ASMAXP_PTRACE_H
 
 #include <uapi/asm/ptrace.h>
-
+#include <asm/irqflags.h>
 
 #define arch_has_single_step()		(1)
 #define user_mode(regs) (((regs)->ps & 8) != 0)
@@ -17,7 +17,9 @@
 #define current_pt_regs() \
   ((struct pt_regs *) ((char *)current_thread_info() + 2*PAGE_SIZE) - 1)
 
-#define force_successful_syscall_return() (current_pt_regs()->r0 = 0)
+#define force_successful_syscall_return() \
+	(current_thread_info()->syscall_meta \
+	|= ALPHA_SYSCALL_META_FORCE_SUCCESS)
 
 static inline unsigned long regs_return_value(struct pt_regs *regs)
 {
@@ -31,4 +33,12 @@ static inline unsigned long user_stack_pointer(struct pt_regs *regs)
 	return regs->usp;
 }
 
+static __always_inline bool regs_irqs_disabled(struct pt_regs *regs)
+{
+	return arch_irqs_disabled_flags(regs->ps);
+}
+
+/* Syscall emulation defines */
+#define PTRACE_SYSEMU			0x1d
+#define PTRACE_SYSEMU_SINGLESTEP	0x1e
 #endif
diff --git a/arch/alpha/include/asm/stacktrace.h b/arch/alpha/include/asm/stacktrace.h
new file mode 100644
index 000000000000..f006d6f00fd0
--- /dev/null
+++ b/arch/alpha/include/asm/stacktrace.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_ALPHA_STACKTRACE_H
+#define _ASM_ALPHA_STACKTRACE_H
+
+#include <linux/compiler_attributes.h>
+#include <linux/types.h>
+
+#include <asm/current.h>
+#include <asm/processor.h>
+#include <asm/thread_info.h>
+
+static __always_inline bool on_thread_stack(void)
+{
+	unsigned long base = (unsigned long)current->stack;
+	unsigned long sp = (unsigned long)current_stack_pointer;
+
+	return !((base ^ sp) & ~(THREAD_SIZE - 1));
+}
+
+#endif /* _ASM_ALPHA_STACKTRACE_H */
diff --git a/arch/alpha/include/asm/syscall.h b/arch/alpha/include/asm/syscall.h
index 1e78cbd46faf..c1394910f584 100644
--- a/arch/alpha/include/asm/syscall.h
+++ b/arch/alpha/include/asm/syscall.h
@@ -8,6 +8,8 @@
 #include <linux/types.h>
 #include <asm/ptrace.h>
 
+extern void *sys_call_table[];
+
 static inline int syscall_get_arch(struct task_struct *task)
 {
 	return AUDIT_ARCH_ALPHA;
@@ -104,10 +106,17 @@ static inline void syscall_set_return_value(struct task_struct *task,
 }
 
 /* Restore the original syscall nr after seccomp/ptrace modified regs->r1. */
+
 static inline void syscall_rollback(struct task_struct *task,
 					struct pt_regs *regs)
 {
-	regs->r1 = regs->r2;
+	unsigned long nr = task_thread_info(task)->syscall_saved_nr;
+
+	regs->r1 = nr;
 }
 
+static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
+{
+	return false;
+}
 #endif	/* _ASM_ALPHA_SYSCALL_H */
diff --git a/arch/alpha/include/asm/thread_info.h b/arch/alpha/include/asm/thread_info.h
index 1552ecca8520..d781ac79106d 100644
--- a/arch/alpha/include/asm/thread_info.h
+++ b/arch/alpha/include/asm/thread_info.h
@@ -12,16 +12,21 @@
 #endif
 
 #ifndef __ASSEMBLER__
+
 struct thread_info {
 	struct pcb_struct	pcb;		/* palcode state */
 
 	struct task_struct	*task;		/* main task structure */
-	unsigned int		flags;		/* low level flags */
+	unsigned long		flags;		/* low level flags */
+	unsigned long		syscall_work;	/* SYSCALL_WORK_* flags */
 	unsigned int		ieee_state;	/* see fpu.h */
 
 	unsigned		cpu;		/* current CPU */
-	int			preempt_count; /* 0 => preemptable, <0 => BUG */
+	int			preempt_count;	/* 0 => preemptable, <0 => BUG */
 	unsigned int		status;		/* thread-synchronous flags */
+	unsigned long		syscall_saved_r19;
+	unsigned long		syscall_meta;
+	unsigned long		syscall_saved_nr;
 
 	int bpt_nsaved;
 	unsigned long bpt_addr[2];		/* breakpoint handling  */
@@ -50,6 +55,9 @@ register unsigned long *current_stack_pointer __asm__ ("$30");
 #define THREAD_SIZE_ORDER 1
 #define THREAD_SIZE (2*PAGE_SIZE)
 
+#define ALPHA_SYSCALL_META_SKIP			0x2
+#define ALPHA_SYSCALL_META_FORCE_SUCCESS	0x4
+
 /*
  * Thread information flags:
  * - these are process state flags and used from assembly
@@ -68,6 +76,7 @@ register unsigned long *current_stack_pointer __asm__ ("$30");
 #define TIF_SECCOMP		6	/* seccomp syscall filtering active */
 #define	TIF_SYSCALL_TRACEPOINT	7	/* syscall tracepoint instrumentation */
 #define TIF_DIE_IF_KERNEL	9	/* dik recursion lock */
+#define TIF_UPROBE		10	/* uprobe breakpoint or singlestep */
 #define TIF_MEMDIE		13	/* is terminating due to OOM killer */
 #define TIF_POLLING_NRFLAG	14	/* idle is polling for TIF_NEED_RESCHED */
 
@@ -80,22 +89,7 @@ register unsigned long *current_stack_pointer __asm__ ("$30");
 #define _TIF_SECCOMP		(1<<TIF_SECCOMP)
 #define _TIF_POLLING_NRFLAG	(1<<TIF_POLLING_NRFLAG)
 #define _TIF_SYSCALL_TRACEPOINT	(1<<TIF_SYSCALL_TRACEPOINT)
-
-/*
- * Work to do on syscall entry (in entry.S).
- * If you want this to exactly mirror what entry.S checks, keep it aligned
- * with the mask used before branching to syscall_trace_enter().
- */
-#ifdef CONFIG_AUDITSYSCALL
-# define _TIF_SYSCALL_WORK	(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP \
-				| _TIF_SYSCALL_TRACEPOINT)
-#else
-# define _TIF_SYSCALL_WORK	(_TIF_SYSCALL_TRACE | _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT)
-#endif
-
-/* Work to do on interrupt/exception return.  */
-#define _TIF_WORK_MASK		(_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
-				 _TIF_NOTIFY_RESUME | _TIF_NOTIFY_SIGNAL)
+#define _TIF_UPROBE		(1 << TIF_UPROBE)
 
 #define TS_UAC_NOPRINT		0x0001	/* ! Preserve the following three */
 #define TS_UAC_NOFIX		0x0002	/* ! flags as they match          */
diff --git a/arch/alpha/kernel/asm-offsets.c b/arch/alpha/kernel/asm-offsets.c
index 1d3bfca319ae..c89792c86044 100644
--- a/arch/alpha/kernel/asm-offsets.c
+++ b/arch/alpha/kernel/asm-offsets.c
@@ -22,6 +22,7 @@ static void __used foo(void)
 
 	DEFINE(SP_OFF, offsetof(struct pt_regs, ps));
 	DEFINE(SIZEOF_PT_REGS, sizeof(struct pt_regs));
+	DEFINE(TI_SYSCALL_META, offsetof(struct thread_info, syscall_meta));
 	BLANK();
 
 	DEFINE(SWITCH_STACK_SIZE, sizeof(struct switch_stack));
@@ -30,4 +31,8 @@ static void __used foo(void)
 	DEFINE(HAE_CACHE, offsetof(struct alpha_machine_vector, hae_cache));
 	DEFINE(HAE_REG, offsetof(struct alpha_machine_vector, hae_register));
 	DEFINE(PT_REGS_USP, offsetof(struct pt_regs, usp));
+
+	DEFINE(TI_FLAGS,        offsetof(struct thread_info, flags));
+	DEFINE(TI_SYSCALL_WORK, offsetof(struct thread_info, syscall_work));
+	DEFINE(TI_STATUS,       offsetof(struct thread_info, status));
 }
diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S
index 9f2608de2544..bfa116d455fb 100644
--- a/arch/alpha/kernel/entry.S
+++ b/arch/alpha/kernel/entry.S
@@ -36,67 +36,10 @@
 	.size	\func, . - \func
 .endm
 
-/*
- * SYSCALL_SKIP_RETURN_RESTART_GATE
- *
- * Used when syscall dispatch is skipped (seccomp/ptrace injected nr=-1).
- *  - Ensure we never return r0==-1 with a3==0 (success); convert to ENOSYS.
- *  - Gate whether syscall restart is allowed by preserving restart context
- *    only for ERESTART* returns. Result:
- *        $26 = 0  => restart allowed
- *        $26 = 1  => restart NOT allowed
- *        $18 = preserved syscall nr (regs->r2) if restart allowed, else 0
- */
-.macro  SYSCALL_SKIP_RETURN_RESTART_GATE
-	/* Fix up invalid "-1 success" return state. */
-	ldq	$19, 72($sp)		/* a3 */
-	bne	$19, 1f			/* already error => skip fixup */
-
-	ldq	$20, 0($sp)		/* r0 */
-	lda	$21, -1($31)
-	cmpeq	$20, $21, $22
-	beq	$22, 1f			/* r0 != -1 => skip fixup */
-
-
-	lda	$20, ENOSYS($31)
-	stq	$20, 0($sp)		/* r0 = ENOSYS */
-	lda	$19, 1($31)
-	stq	$19, 72($sp)		/* a3 = 1 */
-1:
-	/* Restart gating: success is never restartable here. */
-	ldq	$19, 72($sp)		/* a3 */
-	beq	$19, 3f			/* success => not restartable */
-
-	ldq	$20, 0($sp)		/* r0 (positive errno if a3==1) */
-	lda	$21, ERESTARTSYS($31)
-	cmpeq	$20, $21, $22
-	bne	$22, 2f
-	lda	$21, ERESTARTNOINTR($31)
-	cmpeq	$20, $21, $22
-	bne	$22, 2f
-	lda	$21, ERESTARTNOHAND($31)
-	cmpeq	$20, $21, $22
-	bne	$22, 2f
-	lda	$21, ERESTART_RESTARTBLOCK($31)
-	cmpeq	$20, $21, $22
-	bne	$22, 2f
-
-3:	/* Not a restart code (or success) => restart NOT allowed. */
-	addq	$31, 1, $26		/* $26=1 => restart NOT allowed */
-	mov	0, $18
-	br	4f
-
-2:	/* Restart allowed. */
-	ldq	$18, 16($sp)		/* preserved syscall nr (regs->r2) */
-	mov	$31, $26		/* $26=0 => restart allowed */
-	br	4f
-4:
-.endm
-
-.macro LOCKDEP_HARDIRQS_ON_RESTORE
+.macro LOCKDEP_HARDIRQS_ON_RESTORE psreg
 #ifdef CONFIG_PROVE_LOCKING
 	/* a0 = saved PS */
-	ldq	$16, SP_OFF($sp)
+	ldq	$16, \psreg
 
 	/* a1 = callsite IP for lockdep */
 	lda	$17, 1f
@@ -248,7 +191,7 @@
 CFI_START_OSF_FRAME entInt
 	SAVE_ALL
 	lda	$8, 0x3fff
-	lda	$26, ret_from_sys_call
+	lda	$26, ret_from_exception
 	bic	$sp, $8, $8
 	mov	$sp, $19
 	jsr	$31, do_entInt
@@ -257,7 +200,7 @@ CFI_END_OSF_FRAME entInt
 CFI_START_OSF_FRAME entArith
 	SAVE_ALL
 	lda	$8, 0x3fff
-	lda	$26, ret_from_sys_call
+	lda	$26, ret_from_exception
 	bic	$sp, $8, $8
 	mov	$sp, $18
 	jsr	$31, do_entArith
@@ -305,13 +248,13 @@ CFI_START_OSF_FRAME entMM
 	.cfi_restore	$15
 	.cfi_adjust_cfa_offset	-64
 /* finish up the syscall as normal.  */
-	br	ret_from_sys_call
+	br	ret_from_exception
 CFI_END_OSF_FRAME entMM
 
 CFI_START_OSF_FRAME entIF
 	SAVE_ALL
 	lda	$8, 0x3fff
-	lda	$26, ret_from_sys_call
+	lda	$26, ret_from_exception
 	bic	$sp, $8, $8
 	mov	$sp, $17
 	jsr	$31, do_entIF
@@ -440,7 +383,7 @@ CFI_START_OSF_FRAME entUna
 	.cfi_restore	$28
 	.cfi_restore	$29
 	.cfi_adjust_cfa_offset	-256
-	LOCKDEP_HARDIRQS_ON_RESTORE
+	LOCKDEP_HARDIRQS_ON_RESTORE SP_OFF($sp)
 	call_pal PAL_rti
 
 	.align	4
@@ -487,18 +430,19 @@ entUnaUser:
 	.cfi_restore	$14
 	.cfi_restore	$15
 	.cfi_adjust_cfa_offset	-64
-	br	ret_from_sys_call
+	br	ret_from_exception
 CFI_END_OSF_FRAME entUna
 
 CFI_START_OSF_FRAME entDbg
 	SAVE_ALL
 	lda	$8, 0x3fff
-	lda	$26, ret_from_sys_call
+	lda	$26, ret_from_exception
 	bic	$sp, $8, $8
 	mov	$sp, $16
 	jsr	$31, do_entDbg
 CFI_END_OSF_FRAME entDbg
 
+
 /*
  * The system call entry point is special.  Most importantly, it looks
  * like a function call to userspace as far as clobbered registers.  We
@@ -516,9 +460,7 @@ CFI_END_OSF_FRAME entDbg
  * For seccomp/ptrace/generic syscall helpers we track the syscall
  * number separately:
  *   - regs->r1: current (mutable) syscall number (may be changed or set to -1)
- *   - regs->r2: original syscall number for restart/rollback
  *
- * On entry PAL provides the syscall number in r0; copy it into r1/r2.
  */
 
 	.align	4
@@ -531,203 +473,117 @@ CFI_END_OSF_FRAME entDbg
 	.cfi_rel_offset	$gp, 16
 entSys:
 	SAVE_ALL
-        ldq     $1, 0($sp)          /* syscall nr from saved r0 */
-        stq     $1, 8($sp)          /* regs->r1 = shadow syscall nr */
-        stq     $1, 16($sp)         /* regs->r2 = restart syscall nr */
-	/* Syscalls always enter from user mode: snapshot USP into pt_regs->usp */
+	ldq	$1, 0($sp)	/* syscall nr from saved r0 */
+	stq	$1, 8($sp)	/* regs->r1 = shadow syscall nr */
+
 	mov	$0, $8
 	call_pal PAL_rdusp
-	stq      $0, PT_REGS_USP($sp)
+	stq	$0, PT_REGS_USP($sp)
 	mov	$8, $0
 
-
 	lda	$8, 0x3fff
 	bic	$sp, $8, $8
-	lda	$4, NR_syscalls($31)
+
 	stq	$16, SP_OFF+24($sp)
-	lda	$5, sys_call_table
-	lda	$27, sys_ni_syscall
-	cmpult	$0, $4, $4
-	ldl	$3, TI_FLAGS($8)
 	stq	$17, SP_OFF+32($sp)
-	s8addq	$0, $5, $5
 	stq	$18, SP_OFF+40($sp)
-	.cfi_rel_offset	$16, SP_OFF+24
-	.cfi_rel_offset	$17, SP_OFF+32
-	.cfi_rel_offset	$18, SP_OFF+40
-	lda     $6, _TIF_SYSCALL_WORK
-	and     $3, $6, $3
-	bne     $3, strace
 
-	beq	$4, 1f
-	ldq	$27, 0($5)
-1:	ldq	$0, 8($sp)		/* syscall nr shadow (regs->r1) */
+	mov	$0, $1
+	lda	$16, 7
+	call_pal PAL_swpipl
+	mov	$1, $0
+	mov	$sp, $16
+	mov	$0, $17	 /* pv = selected syscall function */
+
+	DO_SWITCH_STACK
+	jsr	$26, alpha_syscall_enter_select
+	ldgp	$gp, 0($26)
+	UNDO_SWITCH_STACK
+
+	/*
+	 * C returned syscall function pointer in $0.
+	 * It also stored SKIP in TI_SYSCALL_META if dispatch is skipped.
+	 */
+	lda	$8, 0x3fff
+	bic	$sp, $8, $8
+	ldq	$3, TI_SYSCALL_META($8)
+	lda	$6, ALPHA_SYSCALL_META_SKIP
+	and	$3, $6, $6
+	bne	$6, skip_dispatch
+
+
+	mov	$0, $27
+	ldq	$16, SP_OFF+24($sp)
+	ldq	$17, SP_OFF+32($sp)
+	ldq	$18, SP_OFF+40($sp)
+	ldq	$19, 72($sp)
+	ldq	$20, 80($sp)
+	ldq	$21, 88($sp)
 
 	jsr	$26, ($27), sys_ni_syscall
 	ldgp	$gp, 0($26)
-	blt	$0, $syscall_error	/* the call failed */
-$ret_success:
-	stq	$0, 0($sp)
-	stq	$31, 72($sp)		/* a3=0 => no error */
 
-	.align	4
-	.globl	ret_from_sys_call
-ret_from_sys_call:
-	cmovne	$26, 0, $18		/* $18 = 0 => non-restartable */
-	ldq	$0, SP_OFF($sp)
-	and	$0, 8, $0
-	beq	$0, ret_to_kernel
-ret_to_user:
-	/* Make sure need_resched and sigpending don't change between
-		sampling and the rti.  */
-	lda	$16, 7
-	call_pal PAL_swpipl
-	ldl	$17, TI_FLAGS($8)
-	and	$17, _TIF_WORK_MASK, $2
-	bne	$2, work_pending
+skip_dispatch:
+	mov	$0, $17		/* raw ret; ignored if SKIP is set */
+	mov	$sp, $16	/* regs */
+
+	DO_SWITCH_STACK
+	jsr	$26, alpha_finish_syscall_to_user_mode
+	ldgp	$gp, 0($26)
+	UNDO_SWITCH_STACK
+
 restore_all:
+	lda	$8, 0x3fff
+	bic	$sp, $8, $8
 	ldl	$2, TI_STATUS($8)
 	and	$2, TS_SAVED_FP | TS_RESTORE_FP, $3
 	bne	$3, restore_fpu
 restore_other:
 	.cfi_remember_state
-	LOCKDEP_HARDIRQS_ON_RESTORE
 	RESTORE_ALL
 	call_pal PAL_rti
 
-ret_to_kernel:
-	.cfi_restore_state
-	lda	$16, 7
-	call_pal PAL_swpipl
-	br restore_other
-
-	.align 3
-$syscall_error:
-        /* Restart syscall nr comes from saved r2 (preserved even if r0 overwritten). */
-	ldq	$18, 16($sp)	/* old syscall nr for restart */
-
-	ldq	$19, 72($sp)	/* .. and this a3 */
-	subq	$31, $0, $0	/* with error in v0 */
-	addq	$31, 1, $1	/* set a3 for errno return */
-	stq	$0, 0($sp)
-	mov	$31, $26	/* tell "ret_from_sys_call" we can restart */
-	stq	$1, 72($sp)	/* a3 for return */
-	br	ret_from_sys_call
-
-/*
- * Do all cleanup when returning from all interrupts and system calls.
- *
- * Arguments:
- *       $8: current.
- *      $17: TI_FLAGS.
- *      $18: The old syscall number, or zero if this is not a return
- *           from a syscall that errored and is possibly restartable.
- *      $19: The old a3 value
- */
-
-	.align	4
-	.type	work_pending, @function
-work_pending:
-	and	$17, _TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL, $2
-	bne	$2, $work_notifysig
-
-$work_resched:
-	/*
-	 * We can get here only if we returned from syscall without SIGPENDING
-	 * or got through work_notifysig already.  Either case means no syscall
-	 * restarts for us, so let $18 and $19 burn.
-	 */
-	jsr	$26, alpha_schedule_user_work
-	mov	0, $18
-	br	ret_to_user
+ret_to_user_from_syscall:
+	lda	$8, 0x3fff
+	bic	$sp, $8, $8
 
-$work_notifysig:
 	mov	$sp, $16
 	DO_SWITCH_STACK
-	jsr	$26, do_work_pending
+	jsr	$26, alpha_syscall_exit_to_user_mode
+	ldgp	$gp, 0($26)
 	UNDO_SWITCH_STACK
 	br	restore_all
 
-/*
- * PTRACE syscall handler
- */
-
 	.align	4
-	.type	strace, @function
-strace:
-	/* set up signal stack, call syscall_trace */
-	// NB: if anyone adds preemption, this block will need to be protected
-	ldl	$1, TI_STATUS($8)
-	and	$1, TS_SAVED_FP, $3
-	or	$1, TS_SAVED_FP, $2
-	bne	$3, 1f
-	stl	$2, TI_STATUS($8)
-	bsr	$26, __save_fpu
-1:
-	DO_SWITCH_STACK
-	jsr	$26, syscall_trace_enter /* returns the syscall number */
-	UNDO_SWITCH_STACK
-
-	stq     $0, 8($sp)		/* regs->r1 = shadow syscall nr */
-
-	/* get the arguments back.. */
-	ldq	$16, SP_OFF+24($sp)
-	ldq	$17, SP_OFF+32($sp)
-	ldq	$18, SP_OFF+40($sp)
-	ldq	$19, 72($sp)
-	ldq	$20, 80($sp)
-	ldq	$21, 88($sp)
+	.globl	ret_from_sys_call
+ret_from_sys_call:
+	ldq	$0, SP_OFF($sp)
+	and	$0, 8, $0
+	beq	$0, ret_to_kernel
+	br	ret_to_user_from_syscall
 
-	/* nr == -1: internal skip-dispatch or userspace syscall(-1)? */
-        lda     $6, -1($31)
-        cmpeq   $0, $6, $6
-	bne	$6, $strace_skip_call	/* nr == -1 => dispatch */
-
-	/* get the system call pointer.. */
-	lda	$1, NR_syscalls($31)
-	lda	$2, sys_call_table
-	lda	$27, sys_ni_syscall
-	cmpult	$0, $1, $1
-	s8addq	$0, $2, $2
-	beq	$1, 1f
-	ldq	$27, 0($2)
-1:	jsr	$26, ($27), sys_gettimeofday
-ret_from_straced:
-	ldgp	$gp, 0($26)
+ret_from_exception:
+	ldq	$0, SP_OFF($sp)
+	and	$0, 8, $0
+	beq	$0, ret_to_kernel
+	br	ret_to_user_from_exception
 
-	/* check return.. */
-	blt	$0, $strace_error	/* the call failed */
-$strace_success:
-	stq	$31, 72($sp)		/* a3=0 => no error */
-	stq	$0, 0($sp)		/* save return value */
+ret_to_user_from_exception:
 
-$strace_skip_call:
-	SYSCALL_SKIP_RETURN_RESTART_GATE
+	mov	$sp, $16
 	DO_SWITCH_STACK
-	jsr	$26, syscall_trace_leave
+	jsr	$26, alpha_exit_to_user_mode
+	ldgp	$gp, 0($26)
 	UNDO_SWITCH_STACK
-	br	$31, ret_from_sys_call
-
-	.align	3
-$strace_error:
-	ldq	$18, 16($sp)	/* restart syscall nr */
-	ldq	$19, 72($sp)	/* .. and this a3 */
-
-	subq	$31, $0, $0	/* with error in v0 */
-	addq	$31, 1, $1	/* set a3 for errno return */
-	stq	$0, 0($sp)
-	stq	$1, 72($sp)	/* a3 for return */
+	br	restore_all
 
-	DO_SWITCH_STACK
-	mov	$18, $9		/* save old syscall number */
-	mov	$19, $10	/* save old a3 */
-	jsr	$26, syscall_trace_leave
-	mov	$9, $18
-	mov	$10, $19
-	UNDO_SWITCH_STACK
+ret_to_kernel:
+	.cfi_restore_state
+	lda	$16, 7
+	call_pal PAL_swpipl
+	LOCKDEP_HARDIRQS_ON_RESTORE SP_OFF($sp)
+	br restore_other
 
-	mov	$31, $26	/* tell "ret_from_sys_call" we can restart */
-	br	ret_from_sys_call
 CFI_END_OSF_FRAME entSys
 
 /*
@@ -815,7 +671,6 @@ restore_fpu:
 	br restore_other
 #undef V
 
-\f
 /*
  * The meat of the context switch code.
  */
@@ -851,7 +706,7 @@ alpha_switch_to:
 	.align	4
 	.ent	ret_from_fork
 ret_from_fork:
-	lda	$26, ret_to_user
+	lda	$26, ret_to_user_from_exception
 	mov	$17, $16
 	jmp	$31, schedule_tail
 .end ret_from_fork
@@ -868,7 +723,7 @@ ret_from_kernel_thread:
 	mov	$9, $27
 	mov	$10, $16
 	jsr	$26, ($9)
-	br	$31, ret_to_user
+	br	$31, ret_to_user_from_exception
 .end ret_from_kernel_thread
 
 \f
@@ -910,12 +765,9 @@ fork_like clone3
 	.ent	sys_\name
 sys_\name:
 	.prologue 0
-	lda	$9, ret_from_straced
-	cmpult	$26, $9, $9
+	mov	$sp, $10
 	lda	$sp, -SWITCH_STACK_SIZE($sp)
 	jsr	$26, do_\name
-	bne	$9, 1f
-	jsr	$26, syscall_trace_leave
 1:	br	$1, undo_switch_stack
 	br	ret_from_sys_call
 .end sys_\name
diff --git a/arch/alpha/kernel/irq_alpha.c b/arch/alpha/kernel/irq_alpha.c
index 736294d3dd51..ac941172ae66 100644
--- a/arch/alpha/kernel/irq_alpha.c
+++ b/arch/alpha/kernel/irq_alpha.c
@@ -105,20 +105,24 @@ void notrace lockdep_on_restore(unsigned long ps,
 				unsigned long ip)
 {
 #ifdef CONFIG_PROVE_LOCKING
-	/* Restoring IPL==7 means interrupts remain disabled. */
+	/*
+	 * If PAL_rti will restore IPL == 7, IRQs remain disabled.
+	 * There is no hardirqs-on transition to annotate.
+	 */
 	if ((ps & 7) == 7)
 		return;
 
 	/*
-	 * If hardware IRQs are already enabled here, then emitting a
-	 * hardirqs-on transition is redundant.
+	 * This helper is meant to run before PAL_rti, after entry.S has
+	 * forced IPL to 7.  If IRQs are already enabled, do not emit a
+	 * fake transition.
 	 */
 	if (!irqs_disabled())
 		return;
 
 	/*
-	 * Only emit the transition if lockdep currently believes
-	 * hardirqs are off.
+	 * Only emit an ON transition if lockdep currently tracks hardirqs
+	 * as off.
 	 */
 	if (lockdep_hardirqs_enabled())
 		return;
diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h
index 9b262ef09a3a..f138bd494628 100644
--- a/arch/alpha/kernel/proto.h
+++ b/arch/alpha/kernel/proto.h
@@ -2,6 +2,7 @@
 #include <linux/interrupt.h>
 #include <linux/screen_info.h>
 #include <linux/io.h>
+#include <asm/ptrace.h>
 
 /* Prototypes of functions used across modules here in this directory.  */
 
@@ -164,16 +165,18 @@ extern void pcibios_claim_one_bus(struct pci_bus *);
 /* ptrace.c */
 extern int ptrace_set_bpt (struct task_struct *child);
 extern int ptrace_cancel_bpt (struct task_struct *child);
-extern void syscall_trace_leave(void);
-extern unsigned long syscall_trace_enter(void);
 
 /* signal.c */
 struct sigcontext;
 extern void do_sigreturn(struct sigcontext __user *);
 struct rt_sigframe;
 extern void do_rt_sigreturn(struct rt_sigframe __user *);
-extern void do_work_pending(struct pt_regs *, unsigned long, unsigned long, unsigned long);
 extern void alpha_schedule_user_work(void);
+extern void do_signal(struct pt_regs *regs, unsigned long r0, unsigned long r19);
+extern void alpha_syscall_exit_to_user_mode(struct pt_regs *regs);
+extern void alpha_exit_to_user_mode(struct pt_regs *regs);
+extern void alpha_finish_syscall_to_user_mode(struct pt_regs *regs, long ret);
+extern unsigned long alpha_syscall_enter_select(struct pt_regs *regs, long syscall);
 
 /* traps.c */
 extern void dik_show_regs(struct pt_regs *regs, unsigned long *r9_15);
diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c
index 69eb337347df..d4a8937985be 100644
--- a/arch/alpha/kernel/ptrace.c
+++ b/arch/alpha/kernel/ptrace.c
@@ -134,18 +134,51 @@ get_reg_addr(struct task_struct * task, unsigned long regno)
 /*
  * Get contents of register REGNO in task TASK.
  */
-static unsigned long
-get_reg(struct task_struct * task, unsigned long regno)
+
+static bool
+valid_regno(unsigned long regno)
 {
-	/* Special hack for fpcr -- combine hardware and software bits.  */
+	switch (regno) {
+	case 0 ... 31:
+	case 63:
+	case 65:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static long
+get_reg(struct task_struct *task, unsigned long regno)
+{
+	unsigned long *addr;
+
+	if (!valid_regno(regno))
+		return -EIO;
+
+	/*
+	 * Special hack for fpcr -- combine hardware and software bits.
+	 */
 	if (regno == 63) {
-		unsigned long fpcr = *get_reg_addr(task, regno);
-		unsigned long swcr
-		  = task_thread_info(task)->ieee_state & IEEE_SW_MASK;
+		unsigned long fpcr;
+		unsigned long swcr;
+
+		addr = get_reg_addr(task, regno);
+		if (!addr)
+			return -EIO;
+
+		fpcr = *addr;
+		swcr = task_thread_info(task)->ieee_state & IEEE_SW_MASK;
 		swcr = swcr_update_status(swcr, fpcr);
+
 		return fpcr | swcr;
 	}
-	return *get_reg_addr(task, regno);
+
+	addr = get_reg_addr(task, regno);
+	if (!addr)
+		return -EIO;
+
+	return *addr;
 }
 
 static void alpha_elf_fpregs_get(struct task_struct *target,
@@ -271,14 +304,15 @@ static void alpha_elf_gregs_set(struct task_struct *child,
 		pt->r19 = 1;
 }
 
-
-/*
- * Write contents of register REGNO in task TASK.
- */
 static int
 put_reg(struct task_struct *task, unsigned long regno, unsigned long data)
 {
 	struct pt_regs *regs = task_pt_regs(task);
+	unsigned long *addr;
+	unsigned long old_r0 = regs->r0;
+
+	if (regno == 31)
+		return 0;
 
 	if (regno == 63) {
 		task_thread_info(task)->ieee_state
@@ -287,24 +321,30 @@ put_reg(struct task_struct *task, unsigned long regno, unsigned long data)
 		data = (data & FPCR_DYN_MASK) | ieee_swcr_to_fpcr(data);
 	}
 
-	*get_reg_addr(task, regno) = data;
+	addr = get_reg_addr(task, regno);
+	if (!addr)
+		return -EIO;
+
+	*addr = data;
 
 	/*
 	 * Alpha historically exposes r0/v0 as the syscall number at a
 	 * syscall-entry stop.  The generic-entry conversion keeps the
-	 * mutable syscall number in regs->r1, so old ptrace users such
-	 * as strace that skip a syscall by poking r0 to -1 must also
-	 * update the internal shadow syscall number.
-	 *
-	 * Do not mirror other r0 writes.  strace later pokes r0 to the
-	 * injected return value, e.g. 42, while r1 must remain -1.
+	 * mutable syscall number in regs->r1.
 	 */
 
-	if (regno == 0 && data == (unsigned long)-1) {
+	if (regno == 0 && regs->r1 == old_r0 &&
+		(data == (unsigned long)-1 ||
+		(regs->r19 == 0 && data < NR_syscalls))) {
 		regs->r1 = data;
-		regs->r19 = 0;
-	}
 
+	/*
+	 * Keep the skip path looking like a clean entry-side syscall
+	 * rewrite.  Do not touch r19 for ordinary syscall substitution.
+	 */
+		if (data == (unsigned long)-1)
+			regs->r19 = 0;
+	}
 	return 0;
 }
 
@@ -435,25 +475,24 @@ long arch_ptrace(struct task_struct *child, long request,
 
 	switch (request) {
 	/* When I and D space are separate, these will need to be fixed.  */
-	case PTRACE_PEEKTEXT: /* read word at location addr. */
+	case PTRACE_PEEKTEXT:
 	case PTRACE_PEEKDATA:
 		copied = ptrace_access_vm(child, addr, &tmp, sizeof(tmp),
 				FOLL_FORCE);
 		ret = -EIO;
 		if (copied != sizeof(tmp))
 			break;
-		
 		force_successful_syscall_return();
 		ret = tmp;
 		break;
 
-	/* Read register number ADDR. */
 	case PTRACE_PEEKUSR:
-		force_successful_syscall_return();
 		ret = get_reg(child, addr);
-		DBG(DBG_MEM, ("peek $%lu->%#lx\n", addr, ret));
-		break;
+		if (ret == -EIO)
+			break;
 
+		force_successful_syscall_return();
+		break;
 	/* When I and D space are separate, this will have to be fixed.  */
 	case PTRACE_POKETEXT: /* write the word at location addr. */
 	case PTRACE_POKEDATA:
@@ -471,47 +510,6 @@ long arch_ptrace(struct task_struct *child, long request,
 	return ret;
 }
 
-asmlinkage unsigned long syscall_trace_enter(void)
-{
-	struct pt_regs *regs = current_pt_regs();
-
-	if (test_thread_flag(TIF_SYSCALL_TRACE) &&
-		ptrace_report_syscall_entry(regs)) {
-		syscall_set_nr(current, regs, -1);
-		if (regs->r19 == 0 && regs->r0 == (unsigned long)-1)
-			syscall_set_return_value(current, regs, -ENOSYS, 0);
-		return -1UL;
-	}
-
-	/*
-	 * Do the secure computing after ptrace; failures should be fast.
-	 * If this fails, seccomp may already have set up the return value
-	 * (e.g. SECCOMP_RET_ERRNO / TRACE).
-	 */
-	if (secure_computing() == -1) {
-		if (regs->r19 == 0 && regs->r0 == (unsigned long)-1)
-			syscall_set_return_value(current, regs, -ENOSYS, 0);
-		syscall_set_nr(current, regs, -1);
-		return -1UL;
-	}
-
-#ifdef CONFIG_AUDITSYSCALL
-	audit_syscall_entry(syscall_get_nr(current, regs),
-		regs->r16, regs->r17, regs->r18, regs->r19);
-#endif
-	return syscall_get_nr(current, regs);
-}
-
-
-
-asmlinkage void
-syscall_trace_leave(void)
-{
-	audit_syscall_exit(current_pt_regs());
-	if (test_thread_flag(TIF_SYSCALL_TRACE))
-		ptrace_report_syscall_exit(current_pt_regs(), 0);
-}
-
 /*
  * Minimal regset support for Alpha.
  *
@@ -522,7 +520,6 @@ syscall_trace_leave(void)
  *    regset_get should return 0 on success. So call dump_elf_thread()
  *    directly and return membuf_write()'s result.
  */
-
 static int alpha_regset_set(struct task_struct *target,
 			    const struct user_regset *regset,
 			    unsigned int pos, unsigned int count,
diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c
index ce40a49b8496..9dae17f288c4 100644
--- a/arch/alpha/kernel/signal.c
+++ b/arch/alpha/kernel/signal.c
@@ -27,10 +27,9 @@
 #include <linux/uaccess.h>
 #include <asm/sigcontext.h>
 #include <asm/ucontext.h>
-
+#include <linux/entry-common.h>
 #include "proto.h"
 
-
 #define DEBUG_SIG 0
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
@@ -41,14 +40,6 @@ asmlinkage void ret_from_sys_call(void);
  * The OSF/1 sigprocmask calling sequence is different from the
  * C sigprocmask() sequence..
  */
-
-asmlinkage void alpha_schedule_user_work(void)
-{
-	local_irq_enable();
-	schedule();
-	local_irq_disable();
-}
-
 SYSCALL_DEFINE2(osf_sigprocmask, int, how, unsigned long, newmask)
 {
 	sigset_t oldmask;
@@ -465,6 +456,7 @@ syscall_restart(unsigned long r0, unsigned long r19,
 		fallthrough;
 	case ERESTARTNOINTR:
 		regs->r0 = r0;	/* reset v0 and a3 and replay syscall */
+		regs->r1 = r0;
 		regs->r19 = r19;
 		regs->pc -= 4;
 		break;
@@ -488,7 +480,7 @@ syscall_restart(unsigned long r0, unsigned long r19,
  * restart. "r0" is also used as an indicator whether we can restart at
  * all (if we get here from anything but a syscall return, it will be 0)
  */
-static void
+void
 do_signal(struct pt_regs *regs, unsigned long r0, unsigned long r19)
 {
 	unsigned long single_stepping = ptrace_cancel_bpt(current);
@@ -511,12 +503,14 @@ do_signal(struct pt_regs *regs, unsigned long r0, unsigned long r19)
 			case ERESTARTNOINTR:
 				/* Reset v0 and a3 and replay syscall.  */
 				regs->r0 = r0;
+				regs->r1 = r0;
 				regs->r19 = r19;
 				regs->pc -= 4;
 				break;
 			case ERESTART_RESTARTBLOCK:
 				/* Set v0 to the restart_syscall and replay */
 				regs->r0 = __NR_restart_syscall;
+				regs->r1 = __NR_restart_syscall;
 				regs->pc -= 4;
 				break;
 			}
@@ -527,27 +521,124 @@ do_signal(struct pt_regs *regs, unsigned long r0, unsigned long r19)
 		ptrace_set_bpt(current);	/* re-set breakpoint */
 }
 
-void
-do_work_pending(struct pt_regs *regs, unsigned long thread_flags,
-		 unsigned long r0, unsigned long r19)
+asmlinkage void alpha_exit_to_user_mode(struct pt_regs *regs)
 {
-	do {
-		if (thread_flags & _TIF_NEED_RESCHED) {
-			local_irq_enable();
-			schedule();
-		} else {
-			local_irq_enable();
-			if (thread_flags & (_TIF_SIGPENDING|_TIF_NOTIFY_SIGNAL)) {
-				preempt_disable();
-				save_fpu();
-				preempt_enable();
-				do_signal(regs, r0, r19);
-				r0 = 0;
-			} else {
-				resume_user_mode_work(regs);
-			}
+	local_irq_disable();
+	irqentry_exit_to_user_mode_prepare(regs);
+	exit_to_user_mode();
+}
+
+/*
+ * Syscall return reaches here after Alpha-specific r0/a3 result encoding.
+ * Delegate syscall-exit work and final exit-to-user handling to generic
+ * entry code; low-level PAL restore remains in assembly.
+ */
+asmlinkage void alpha_syscall_exit_to_user_mode(struct pt_regs *regs)
+{
+	syscall_exit_to_user_mode(regs);
+}
+
+void arch_do_signal_or_restart(struct pt_regs *regs)
+{
+	struct thread_info *ti = current_thread_info();
+
+	do_signal(regs, ti->syscall_saved_nr, ti->syscall_saved_r19);
+}
+
+asmlinkage unsigned long
+alpha_syscall_enter_select(struct pt_regs *regs, long syscall)
+{
+	struct thread_info *ti = current_thread_info();
+	unsigned long work;
+	unsigned long nr;
+	unsigned long fn = (unsigned long)sys_ni_syscall;
+
+	ti->syscall_meta = 0;
+	ti->syscall_saved_nr = syscall;
+
+	if (!(ti->status & TS_SAVED_FP)) {
+		ti->status |= TS_SAVED_FP;
+		__save_fpu();
+	}
+
+	work = READ_ONCE(ti->syscall_work) & SYSCALL_WORK_ENTER;
+
+	nr = syscall_enter_from_user_mode(regs, syscall);
+
+	syscall_set_nr(current, regs, nr);
+	/*
+	 * In the unified path, nr == -1 is ambiguous:
+	 *   - without syscall work: syscall(-1), dispatch to sys_ni_syscall
+	 *   - with syscall work: ptrace/seccomp skip marker
+	 */
+	if (work && (long)nr == -1L) {
+		ti->syscall_meta = ALPHA_SYSCALL_META_SKIP;
+		return fn; /* ignored by asm when SKIP is set */
+	}
+
+	instrumentation_begin();
+	if (likely(nr < (unsigned long)NR_syscalls)) {
+		nr = array_index_nospec(nr, NR_syscalls);
+		fn = (unsigned long)sys_call_table[nr];
+	}
+	instrumentation_end();
+
+	return fn;
+}
+
+asmlinkage noinstr void
+alpha_finish_syscall_to_user_mode(struct pt_regs *regs, long ret)
+{
+	struct thread_info *ti = current_thread_info();
+	unsigned long meta = ti->syscall_meta;
+
+	ti->syscall_meta = 0;
+	ti->syscall_saved_r19 = regs->r19;
+
+	instrumentation_begin();
+
+	if (meta & ALPHA_SYSCALL_META_SKIP) {
+	/*
+	 * Skip-dispatch path: ptrace/seccomp may already have installed
+	 * the return state in r0/r19. Preserve it unless the syscall was
+	 * skipped with no explicit return value.
+	 *
+	 * Generic PTRACE_SET_SYSCALL_INFO changes only the syscall-number
+	 * shadow, so r1 == -1 while r0 still contains the original Alpha
+	 * syscall number. Legacy PTRACE_POKEUSR based skipping can leave
+	 * r0 == -1 with a3/r19 still indicating success. Both represent
+	 * an unhandled skipped syscall and should become ENOSYS/a3=1.
+	 */
+		if (regs->r1 == (unsigned long)-1 &&
+		   (regs->r0 == ti->syscall_saved_nr ||
+		    regs->r0 == (unsigned long)-1)) {
+			regs->r0 = ENOSYS;
+			regs->r19 = 1;
 		}
-		local_irq_disable();
-		thread_flags = read_thread_flags();
-	} while (thread_flags & _TIF_WORK_MASK);
+
+		instrumentation_end();
+
+		syscall_exit_to_user_mode(regs);
+		return;
+	}
+
+	/*
+	 * Some successful syscalls, notably legacy ptrace PEEK requests,
+	 * return arbitrary data in r0.  That data may have the bit pattern
+	 * of a negative errno, so do not infer failure from ret < 0 when
+	 * arch code explicitly requested a successful Alpha return.
+	 */
+	if (meta & ALPHA_SYSCALL_META_FORCE_SUCCESS) {
+		regs->r0 = ret;
+		regs->r19 = 0;
+	} else if (ret < 0) {
+		regs->r0 = -ret;
+		regs->r19 = 1;
+	} else {
+		regs->r0 = ret;
+		regs->r19 = 0;
+	}
+
+	instrumentation_end();
+	syscall_exit_to_user_mode(regs);
 }
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 7/8] alpha: enable lockdep hardirq state tracking
From: Magnus Lindholm @ 2026-05-29 14:22 UTC (permalink / raw)
  To: richard.henderson, mattst88, linux-kernel, linux-alpha
  Cc: glaubitz, mcree, ink, macro, Magnus Lindholm
In-Reply-To: <20260529142322.1362438-1-linmag7@gmail.com>

Alpha masks interrupts through the PAL IPL state, so lockdep cannot infer
hardirq state transitions from generic code alone. Add explicit
hardirq on/off annotations to the low-level entry and return paths so
lockdep's IRQ state follows the hardware IPL state.

Annotate the PAL IPL transitions and the shared return-to-user/kernel
paths where interrupts become enabled or disabled.  With the preceding
irqflags, raw-lock, sysfs, and ftrace return-address preparations in
place, select LOCKDEP_SUPPORT and TRACE_IRQFLAGS_SUPPORT for Alpha.

This keeps CONFIG_PROVE_LOCKING usable on Alpha instead of disabling
debug_locks due to IRQ-state mismatches.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
 .../features/locking/lockdep/arch-support.txt |  2 +-
 arch/alpha/Kconfig                            |  5 ++
 arch/alpha/kernel/entry.S                     | 17 ++++-
 arch/alpha/kernel/irq_alpha.c                 | 74 ++++++++++++++-----
 arch/alpha/kernel/proto.h                     |  4 +
 arch/alpha/kernel/signal.c                    |  9 +++
 6 files changed, 91 insertions(+), 20 deletions(-)

diff --git a/Documentation/features/locking/lockdep/arch-support.txt b/Documentation/features/locking/lockdep/arch-support.txt
index b6b00469f7d0..87a534c89636 100644
--- a/Documentation/features/locking/lockdep/arch-support.txt
+++ b/Documentation/features/locking/lockdep/arch-support.txt
@@ -6,7 +6,7 @@
     -----------------------
     |         arch |status|
     -----------------------
-    |       alpha: | TODO |
+    |       alpha: |  ok  |
     |         arc: |  ok  |
     |         arm: |  ok  |
     |       arm64: |  ok  |
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index 7ac435c56845..e53ef2d88463 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -45,6 +45,8 @@ config ALPHA
 	select MMU_GATHER_RCU_TABLE_FREE
 	select SPARSEMEM_EXTREME if SPARSEMEM
 	select ZONE_DMA
+	select TRACE_IRQFLAGS_SUPPORT
+	select ARCH_WANT_FRAME_POINTERS
 	help
 	  The Alpha is a 64-bit general-purpose processor designed and
 	  marketed by the Digital Equipment Corporation of blessed memory,
@@ -84,6 +86,9 @@ config AUDIT_ARCH
 config STACKTRACE_SUPPORT
 	def_bool y
 
+config LOCKDEP_SUPPORT
+	def_bool y
+
 menu "System setup"
 
 choice
diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S
index 449092a31eef..9f2608de2544 100644
--- a/arch/alpha/kernel/entry.S
+++ b/arch/alpha/kernel/entry.S
@@ -93,6 +93,19 @@
 4:
 .endm
 
+.macro LOCKDEP_HARDIRQS_ON_RESTORE
+#ifdef CONFIG_PROVE_LOCKING
+	/* a0 = saved PS */
+	ldq	$16, SP_OFF($sp)
+
+	/* a1 = callsite IP for lockdep */
+	lda	$17, 1f
+
+	jsr	$26, lockdep_on_restore
+	ldgp	$gp, 0($26)
+1:
+#endif
+.endm
 
 /*
  * This defines the normal kernel pt-regs layout.
@@ -427,6 +440,7 @@ CFI_START_OSF_FRAME entUna
 	.cfi_restore	$28
 	.cfi_restore	$29
 	.cfi_adjust_cfa_offset	-256
+	LOCKDEP_HARDIRQS_ON_RESTORE
 	call_pal PAL_rti
 
 	.align	4
@@ -577,6 +591,7 @@ restore_all:
 	bne	$3, restore_fpu
 restore_other:
 	.cfi_remember_state
+	LOCKDEP_HARDIRQS_ON_RESTORE
 	RESTORE_ALL
 	call_pal PAL_rti
 
@@ -622,7 +637,7 @@ $work_resched:
 	 * or got through work_notifysig already.  Either case means no syscall
 	 * restarts for us, so let $18 and $19 burn.
 	 */
-	jsr	$26, schedule
+	jsr	$26, alpha_schedule_user_work
 	mov	0, $18
 	br	ret_to_user
 
diff --git a/arch/alpha/kernel/irq_alpha.c b/arch/alpha/kernel/irq_alpha.c
index d17e44c99df9..736294d3dd51 100644
--- a/arch/alpha/kernel/irq_alpha.c
+++ b/arch/alpha/kernel/irq_alpha.c
@@ -41,7 +41,7 @@ EXPORT_SYMBOL(perf_irq);
  * The main interrupt entry point.
  */
 
-asmlinkage void 
+asmlinkage void
 do_entInt(unsigned long type, unsigned long vector,
 	  unsigned long la_ptr, struct pt_regs *regs)
 {
@@ -54,40 +54,78 @@ do_entInt(unsigned long type, unsigned long vector,
 	 * (namely LX164).
 	 */
 	local_irq_disable();
+	old_regs = set_irq_regs(regs);
+
 	switch (type) {
 	case 0:
 #ifdef CONFIG_SMP
+		irq_enter();
 		handle_ipi(regs);
-		return;
+		irq_exit();
+		break;
 #else
 		irq_err_count++;
-		printk(KERN_CRIT "Interprocessor interrupt? "
-		       "You must be kidding!\n");
-#endif
+		pr_crit("Interprocessor interrupt? You must be kidding!\n");
 		break;
+#endif
 	case 1:
-		old_regs = set_irq_regs(regs);
+		/* handle_irq() already does irq_enter()/irq_exit() */
 		handle_irq(RTC_IRQ);
-		set_irq_regs(old_regs);
-		return;
+		break;
 	case 2:
-		old_regs = set_irq_regs(regs);
+		irq_enter();
 		alpha_mv.machine_check(vector, la_ptr);
-		set_irq_regs(old_regs);
-		return;
+		irq_exit();
+		break;
 	case 3:
-		old_regs = set_irq_regs(regs);
+		irq_enter();
 		alpha_mv.device_interrupt(vector);
-		set_irq_regs(old_regs);
-		return;
+		irq_exit();
+		break;
 	case 4:
+		irq_enter();
 		perf_irq(la_ptr, regs);
-		return;
+		irq_exit();
+		break;
 	default:
-		printk(KERN_CRIT "Hardware intr %ld %lx? Huh?\n",
-		       type, vector);
+		pr_crit("Hardware intr %lu %lx? Huh?\n", type, vector);
+		pr_crit("PC = %016lx PS=%04lx\n", regs->pc, regs->ps);
+		break;
 	}
-	printk(KERN_CRIT "PC = %016lx PS=%04lx\n", regs->pc, regs->ps);
+
+	set_irq_regs(old_regs);
+
+	/*
+	 * Intentionally no local_irq_enable(): Alpha historically avoids
+	 * enabling at IPL0 here due to PAL/RTI issues (LX164/MILO note).
+	 */
+}
+
+void notrace lockdep_on_restore(unsigned long ps,
+				unsigned long ip)
+{
+#ifdef CONFIG_PROVE_LOCKING
+	/* Restoring IPL==7 means interrupts remain disabled. */
+	if ((ps & 7) == 7)
+		return;
+
+	/*
+	 * If hardware IRQs are already enabled here, then emitting a
+	 * hardirqs-on transition is redundant.
+	 */
+	if (!irqs_disabled())
+		return;
+
+	/*
+	 * Only emit the transition if lockdep currently believes
+	 * hardirqs are off.
+	 */
+	if (lockdep_hardirqs_enabled())
+		return;
+
+	lockdep_hardirqs_on_prepare();
+	lockdep_hardirqs_on(ip);
+#endif
 }
 
 void __init
diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h
index a8bc3ead776b..9b262ef09a3a 100644
--- a/arch/alpha/kernel/proto.h
+++ b/arch/alpha/kernel/proto.h
@@ -173,6 +173,7 @@ extern void do_sigreturn(struct sigcontext __user *);
 struct rt_sigframe;
 extern void do_rt_sigreturn(struct rt_sigframe __user *);
 extern void do_work_pending(struct pt_regs *, unsigned long, unsigned long, unsigned long);
+extern void alpha_schedule_user_work(void);
 
 /* traps.c */
 extern void dik_show_regs(struct pt_regs *regs, unsigned long *r9_15);
@@ -185,6 +186,9 @@ struct allregs;
 extern void do_entUna(void *, unsigned long, unsigned long, struct allregs *);
 extern void do_entUnaUser(void __user *, unsigned long, unsigned long, struct pt_regs *);
 
+/* irq_alpha.c */
+extern void notrace lockdep_on_restore(unsigned long ps, unsigned long ip);
+
 /* sys_titan.c */
 extern void titan_dispatch_irqs(u64);
 
diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c
index e62d1d461b1f..ce40a49b8496 100644
--- a/arch/alpha/kernel/signal.c
+++ b/arch/alpha/kernel/signal.c
@@ -41,6 +41,14 @@ asmlinkage void ret_from_sys_call(void);
  * The OSF/1 sigprocmask calling sequence is different from the
  * C sigprocmask() sequence..
  */
+
+asmlinkage void alpha_schedule_user_work(void)
+{
+	local_irq_enable();
+	schedule();
+	local_irq_disable();
+}
+
 SYSCALL_DEFINE2(osf_sigprocmask, int, how, unsigned long, newmask)
 {
 	sigset_t oldmask;
@@ -525,6 +533,7 @@ do_work_pending(struct pt_regs *regs, unsigned long thread_flags,
 {
 	do {
 		if (thread_flags & _TIF_NEED_RESCHED) {
+			local_irq_enable();
 			schedule();
 		} else {
 			local_irq_enable();
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 6/8] alpha: use raw spinlocks for low-level platform locks
From: Magnus Lindholm @ 2026-05-29 14:22 UTC (permalink / raw)
  To: richard.henderson, mattst88, linux-kernel, linux-alpha
  Cc: glaubitz, mcree, ink, macro, Magnus Lindholm
In-Reply-To: <20260529142322.1362438-1-linmag7@gmail.com>

Some Alpha platform locks are used as low-level hardware serialization
locks in interrupt-controller and chipset access paths. These paths can
run while IRQ state is being changed or while lockdep is tracking that
state, so regular spinlock instrumentation is not appropriate once
lockdep is enabled.

Convert the affected Tsunami and Rawhide platform locks to
raw_spinlock_t. This keeps the locks as simple hardware serialization
locks and avoids lockdep recursion or IRQ-state mismatches when
CONFIG_PROVE_LOCKING is enabled.

This is a preparatory change for enabling lockdep hardirq state tracking
on Alpha.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
 arch/alpha/kernel/irq_i8259.c   | 19 +++++++++++------
 arch/alpha/kernel/sys_dp264.c   | 38 ++++++++++++++++++++++-----------
 arch/alpha/kernel/sys_rawhide.c | 17 +++++++++------
 3 files changed, 47 insertions(+), 27 deletions(-)

diff --git a/arch/alpha/kernel/irq_i8259.c b/arch/alpha/kernel/irq_i8259.c
index 29c6c477ac35..28f7b0680564 100644
--- a/arch/alpha/kernel/irq_i8259.c
+++ b/arch/alpha/kernel/irq_i8259.c
@@ -22,7 +22,7 @@
 
 /* Note mask bit is true for DISABLED irqs.  */
 static unsigned int cached_irq_mask = 0xffff;
-static DEFINE_SPINLOCK(i8259_irq_lock);
+static DEFINE_RAW_SPINLOCK(i8259_irq_lock);
 
 static inline void
 i8259_update_irq_hw(unsigned int irq, unsigned long mask)
@@ -36,9 +36,11 @@ i8259_update_irq_hw(unsigned int irq, unsigned long mask)
 inline void
 i8259a_enable_irq(struct irq_data *d)
 {
-	spin_lock(&i8259_irq_lock);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&i8259_irq_lock, flags);
 	i8259_update_irq_hw(d->irq, cached_irq_mask &= ~(1 << d->irq));
-	spin_unlock(&i8259_irq_lock);
+	raw_spin_unlock_irqrestore(&i8259_irq_lock, flags);
 }
 
 static inline void
@@ -50,17 +52,20 @@ __i8259a_disable_irq(unsigned int irq)
 void
 i8259a_disable_irq(struct irq_data *d)
 {
-	spin_lock(&i8259_irq_lock);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&i8259_irq_lock, flags);
 	__i8259a_disable_irq(d->irq);
-	spin_unlock(&i8259_irq_lock);
+	raw_spin_unlock_irqrestore(&i8259_irq_lock, flags);
 }
 
 void
 i8259a_mask_and_ack_irq(struct irq_data *d)
 {
 	unsigned int irq = d->irq;
+	unsigned long flags;
 
-	spin_lock(&i8259_irq_lock);
+	raw_spin_lock_irqsave(&i8259_irq_lock, flags);
 	__i8259a_disable_irq(irq);
 
 	/* Ack the interrupt making it the lowest priority.  */
@@ -69,7 +74,7 @@ i8259a_mask_and_ack_irq(struct irq_data *d)
 		irq = 2;
 	}
 	outb(0xE0 | irq, 0x20);			/* ack the master */
-	spin_unlock(&i8259_irq_lock);
+	raw_spin_unlock_irqrestore(&i8259_irq_lock, flags);
 }
 
 struct irq_chip i8259a_irq_type = {
diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c
index 9fb445d7dca5..0a2d319bb1c8 100644
--- a/arch/alpha/kernel/sys_dp264.c
+++ b/arch/alpha/kernel/sys_dp264.c
@@ -41,7 +41,7 @@ static unsigned long cached_irq_mask;
 /* dp264 boards handle at max four CPUs */
 static unsigned long cpu_irq_affinity[4] = { 0UL, 0UL, 0UL, 0UL };
 
-DEFINE_SPINLOCK(dp264_irq_lock);
+static DEFINE_RAW_SPINLOCK(dp264_irq_lock);
 
 static void
 tsunami_update_irq_hw(unsigned long mask)
@@ -99,37 +99,45 @@ tsunami_update_irq_hw(unsigned long mask)
 static void
 dp264_enable_irq(struct irq_data *d)
 {
-	spin_lock(&dp264_irq_lock);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&dp264_irq_lock, flags);
 	cached_irq_mask |= 1UL << d->irq;
 	tsunami_update_irq_hw(cached_irq_mask);
-	spin_unlock(&dp264_irq_lock);
+	raw_spin_unlock_irqrestore(&dp264_irq_lock, flags);
 }
 
 static void
 dp264_disable_irq(struct irq_data *d)
 {
-	spin_lock(&dp264_irq_lock);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&dp264_irq_lock, flags);
 	cached_irq_mask &= ~(1UL << d->irq);
 	tsunami_update_irq_hw(cached_irq_mask);
-	spin_unlock(&dp264_irq_lock);
+	raw_spin_unlock_irqrestore(&dp264_irq_lock, flags);
 }
 
 static void
 clipper_enable_irq(struct irq_data *d)
 {
-	spin_lock(&dp264_irq_lock);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&dp264_irq_lock, flags);
 	cached_irq_mask |= 1UL << (d->irq - 16);
 	tsunami_update_irq_hw(cached_irq_mask);
-	spin_unlock(&dp264_irq_lock);
+	raw_spin_unlock_irqrestore(&dp264_irq_lock, flags);
 }
 
 static void
 clipper_disable_irq(struct irq_data *d)
 {
-	spin_lock(&dp264_irq_lock);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&dp264_irq_lock, flags);
 	cached_irq_mask &= ~(1UL << (d->irq - 16));
 	tsunami_update_irq_hw(cached_irq_mask);
-	spin_unlock(&dp264_irq_lock);
+	raw_spin_unlock_irqrestore(&dp264_irq_lock, flags);
 }
 
 static void
@@ -151,10 +159,12 @@ static int
 dp264_set_affinity(struct irq_data *d, const struct cpumask *affinity,
 		   bool force)
 {
-	spin_lock(&dp264_irq_lock);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&dp264_irq_lock, flags);
 	cpu_set_irq_affinity(d->irq, *affinity);
 	tsunami_update_irq_hw(cached_irq_mask);
-	spin_unlock(&dp264_irq_lock);
+	raw_spin_unlock_irqrestore(&dp264_irq_lock, flags);
 
 	return 0;
 }
@@ -163,10 +173,12 @@ static int
 clipper_set_affinity(struct irq_data *d, const struct cpumask *affinity,
 		     bool force)
 {
-	spin_lock(&dp264_irq_lock);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&dp264_irq_lock, flags);
 	cpu_set_irq_affinity(d->irq - 16, *affinity);
 	tsunami_update_irq_hw(cached_irq_mask);
-	spin_unlock(&dp264_irq_lock);
+	raw_spin_unlock_irqrestore(&dp264_irq_lock, flags);
 
 	return 0;
 }
diff --git a/arch/alpha/kernel/sys_rawhide.c b/arch/alpha/kernel/sys_rawhide.c
index b5846ffdadce..b4a08890dce9 100644
--- a/arch/alpha/kernel/sys_rawhide.c
+++ b/arch/alpha/kernel/sys_rawhide.c
@@ -41,7 +41,7 @@ static unsigned int hose_irq_masks[4] = {
 	0xff0000, 0xfe0000, 0xff0000, 0xff0000
 };
 static unsigned int cached_irq_masks[4];
-DEFINE_SPINLOCK(rawhide_irq_lock);
+DEFINE_RAW_SPINLOCK(rawhide_irq_lock);
 
 static inline void
 rawhide_update_irq_hw(int hose, int mask)
@@ -59,6 +59,7 @@ rawhide_enable_irq(struct irq_data *d)
 {
 	unsigned int mask, hose;
 	unsigned int irq = d->irq;
+	unsigned long flags;
 
 	irq -= 16;
 	hose = irq / 24;
@@ -68,11 +69,11 @@ rawhide_enable_irq(struct irq_data *d)
 	irq -= hose * 24;
 	mask = 1 << irq;
 
-	spin_lock(&rawhide_irq_lock);
+	raw_spin_lock_irqsave(&rawhide_irq_lock, flags);
 	mask |= cached_irq_masks[hose];
 	cached_irq_masks[hose] = mask;
 	rawhide_update_irq_hw(hose, mask);
-	spin_unlock(&rawhide_irq_lock);
+	raw_spin_unlock_irqrestore(&rawhide_irq_lock, flags);
 }
 
 static void 
@@ -80,6 +81,7 @@ rawhide_disable_irq(struct irq_data *d)
 {
 	unsigned int mask, hose;
 	unsigned int irq = d->irq;
+	unsigned long flags;
 
 	irq -= 16;
 	hose = irq / 24;
@@ -89,11 +91,11 @@ rawhide_disable_irq(struct irq_data *d)
 	irq -= hose * 24;
 	mask = ~(1 << irq) | hose_irq_masks[hose];
 
-	spin_lock(&rawhide_irq_lock);
+	raw_spin_lock_irqsave(&rawhide_irq_lock, flags);
 	mask &= cached_irq_masks[hose];
 	cached_irq_masks[hose] = mask;
 	rawhide_update_irq_hw(hose, mask);
-	spin_unlock(&rawhide_irq_lock);
+	raw_spin_unlock_irqrestore(&rawhide_irq_lock, flags);
 }
 
 static void
@@ -101,6 +103,7 @@ rawhide_mask_and_ack_irq(struct irq_data *d)
 {
 	unsigned int mask, mask1, hose;
 	unsigned int irq = d->irq;
+	unsigned long flags;
 
 	irq -= 16;
 	hose = irq / 24;
@@ -111,7 +114,7 @@ rawhide_mask_and_ack_irq(struct irq_data *d)
 	mask1 = 1 << irq;
 	mask = ~mask1 | hose_irq_masks[hose];
 
-	spin_lock(&rawhide_irq_lock);
+	raw_spin_lock_irqsave(&rawhide_irq_lock, flags);
 
 	mask &= cached_irq_masks[hose];
 	cached_irq_masks[hose] = mask;
@@ -120,7 +123,7 @@ rawhide_mask_and_ack_irq(struct irq_data *d)
 	/* Clear the interrupt.  */
 	*(vuip)MCPCIA_INT_REQ(MCPCIA_HOSE2MID(hose)) = mask1;
 
-	spin_unlock(&rawhide_irq_lock);
+	raw_spin_unlock_irqrestore(&rawhide_irq_lock, flags);
 }
 
 static struct irq_chip rawhide_irq_type = {
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 5/8] alpha: provide ftrace return address support for lockdep
From: Magnus Lindholm @ 2026-05-29 14:22 UTC (permalink / raw)
  To: richard.henderson, mattst88, linux-kernel, linux-alpha
  Cc: glaubitz, mcree, ink, macro, Magnus Lindholm
In-Reply-To: <20260529142322.1362438-1-linmag7@gmail.com>

Lockdep uses ftrace_return_address() to report useful call sites for
lock acquisition and IRQ-state tracking diagnostics. Provide the Alpha
architecture hook using the compiler return-address builtin when frame
pointers are available.

Return zero when frame pointers are disabled, matching the existing
fallback behavior of architectures that cannot provide a reliable return
address.

This is a preparatory change for enabling lockdep support on Alpha.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
 arch/alpha/include/asm/ftrace.h | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/arch/alpha/include/asm/ftrace.h b/arch/alpha/include/asm/ftrace.h
index 40a8c178f10d..7ec44134c804 100644
--- a/arch/alpha/include/asm/ftrace.h
+++ b/arch/alpha/include/asm/ftrace.h
@@ -1 +1,29 @@
-/* empty */
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_ALPHA_FTRACE_H
+#define _ASM_ALPHA_FTRACE_H
+
+#ifdef CONFIG_FRAME_POINTER
+
+static void *alpha_ftrace_return_address0(void)
+	noinline notrace;
+static void *alpha_ftrace_return_address0(void)
+{
+	return __builtin_return_address(0);
+}
+
+#define ftrace_return_address0 alpha_ftrace_return_address0()
+
+/*
+ * __builtin_return_address() requires a constant integer argument.
+ * Keep this as a macro so the value is seen at the callsite.
+ */
+#define ftrace_return_address(n) __builtin_return_address(n)
+
+#else  /* !CONFIG_FRAME_POINTER */
+
+#define ftrace_return_address0 0UL
+#define ftrace_return_address(n) ((void)(n), 0UL)
+
+#endif /* CONFIG_FRAME_POINTER */
+
+#endif /* _ASM_ALPHA_FTRACE_H */
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 4/8] alpha: initialize PCI sysfs bin attributes for lockdep
From: Magnus Lindholm @ 2026-05-29 14:22 UTC (permalink / raw)
  To: richard.henderson, mattst88, linux-kernel, linux-alpha
  Cc: glaubitz, mcree, ink, macro, Magnus Lindholm
In-Reply-To: <20260529142322.1362438-1-linmag7@gmail.com>

Alpha allocates PCI resource sysfs bin attributes dynamically.  When
lockdep is enabled, dynamically allocated sysfs attributes need their
lockdep metadata initialized before registration.

Call sysfs_bin_attr_init() before registering the resource bin attribute
with sysfs.  This avoids unrelated sysfs lock-class warnings once Alpha
enables lockdep support.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
 arch/alpha/kernel/pci-sysfs.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c
index 3048758304b5..ba08dbb43521 100644
--- a/arch/alpha/kernel/pci-sysfs.c
+++ b/arch/alpha/kernel/pci-sysfs.c
@@ -159,6 +159,8 @@ static int pci_create_one_attr(struct pci_dev *pdev, int num, char *name,
 {
 	size_t size = pci_resource_len(pdev, num);
 
+	sysfs_bin_attr_init(res_attr);
+
 	sprintf(name, "resource%d%s", num, suffix);
 	res_attr->mmap = sparse ? pci_mmap_resource_sparse :
 				  pci_mmap_resource_dense;
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 3/8] alpha: make irqflags helpers operate on IPL state
From: Magnus Lindholm @ 2026-05-29 14:21 UTC (permalink / raw)
  To: richard.henderson, mattst88, linux-kernel, linux-alpha
  Cc: glaubitz, mcree, ink, macro, Magnus Lindholm
In-Reply-To: <20260529142322.1362438-1-linmag7@gmail.com>

Alpha interrupt masking is controlled by the PAL IPL value, not by the
full processor status word.  Make arch_local_save_flags() return the
current IPL directly, and make arch_local_irq_restore() and
arch_irqs_disabled_flags() treat their argument as IPL state.

Mask the low IPL bits in the restore and test helpers so callers which
still pass a saved PS value continue to behave as expected.

This prepares the irqflags helpers for lockdep IRQ-state tracking, where
the saved flags value is used to determine whether hard IRQs are enabled
or disabled.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
 arch/alpha/include/asm/irqflags.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/alpha/include/asm/irqflags.h b/arch/alpha/include/asm/irqflags.h
index 9f25d4e0d37e..f207544f52de 100644
--- a/arch/alpha/include/asm/irqflags.h
+++ b/arch/alpha/include/asm/irqflags.h
@@ -26,7 +26,7 @@ extern int __min_ipl;
 
 static inline unsigned long arch_local_save_flags(void)
 {
-	return rdps();
+	return getipl();
 }
 
 static inline void arch_local_irq_disable(void)
@@ -51,13 +51,13 @@ static inline void arch_local_irq_enable(void)
 static inline void arch_local_irq_restore(unsigned long flags)
 {
 	barrier();
-	setipl(flags);
+	setipl(flags & 7);
 	barrier();
 }
 
 static inline bool arch_irqs_disabled_flags(unsigned long flags)
 {
-	return flags == IPL_MAX;
+	return (flags & 7) == IPL_MAX;
 }
 
 static inline bool arch_irqs_disabled(void)
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 2/8] alpha: add ARCH_STACKWALK-based stacktrace support
From: Magnus Lindholm @ 2026-05-29 14:21 UTC (permalink / raw)
  To: richard.henderson, mattst88, linux-kernel, linux-alpha
  Cc: glaubitz, mcree, ink, macro, Magnus Lindholm
In-Reply-To: <20260529142322.1362438-1-linmag7@gmail.com>

Implement arch_stack_walk() for Alpha using a simple kernel
stack scanning walker. Start from regs+1 for current tasks
to skip pt_regs and use pcb.ksp for blocked tasks. Filter
candidates with __kernel_text_address() and stop at stack
bounds via kstack_end().

Enable CONFIG_STACKTRACE_SUPPORT and CONFIG_ARCH_STACKWALK
so generic stacktrace users (dump_stack(), /proc/*/stack,
SysRq backtraces, etc.) work on Alpha.

This provides functional in-kernel stack traces without
requiring frame pointer unwinding.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
 arch/alpha/Kconfig              |  4 +++
 arch/alpha/kernel/Makefile      |  3 +-
 arch/alpha/kernel/stacktrace.c  | 61 +++++++++++++++++++++++++++++++++
 arch/alpha/kernel/vmlinux.lds.S |  2 ++
 4 files changed, 69 insertions(+), 1 deletion(-)
 create mode 100644 arch/alpha/kernel/stacktrace.c

diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index f3b882835617..7ac435c56845 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -39,6 +39,7 @@ config ALPHA
 	select MODULES_USE_ELF_RELA
 	select ODD_RT_SIGACTION
 	select OLD_SIGSUSPEND
+	select ARCH_STACKWALK
 	select CPU_NO_EFFICIENT_FFS if !ALPHA_EV67
 	select MMU_GATHER_NO_RANGE
 	select MMU_GATHER_RCU_TABLE_FREE
@@ -80,6 +81,9 @@ config PGTABLE_LEVELS
 config AUDIT_ARCH
 	bool
 
+config STACKTRACE_SUPPORT
+	def_bool y
+
 menu "System setup"
 
 choice
diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile
index 187cd8df2faf..4ea5c189e60e 100644
--- a/arch/alpha/kernel/Makefile
+++ b/arch/alpha/kernel/Makefile
@@ -9,7 +9,8 @@ ccflags-y	:= -Wno-sign-compare
 
 obj-y    := head.o entry.o traps.o process.o osf_sys.o irq.o \
 	    irq_alpha.o signal.o setup.o ptrace.o time.o \
-	    systbls.o err_common.o io.o bugs.o termios.o
+	    systbls.o err_common.o io.o bugs.o termios.o \
+	    stacktrace.o
 
 obj-$(CONFIG_VGA_HOSE)	+= console.o
 obj-$(CONFIG_SMP)	+= smp.o
diff --git a/arch/alpha/kernel/stacktrace.c b/arch/alpha/kernel/stacktrace.c
new file mode 100644
index 000000000000..74d95f591039
--- /dev/null
+++ b/arch/alpha/kernel/stacktrace.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/sched.h>
+#include <linux/sched/task_stack.h>
+#include <linux/stacktrace.h>
+#include <linux/kallsyms.h>
+
+#include <asm/thread_info.h>
+#include <asm/ptrace.h>
+
+static __always_inline unsigned long alpha_get_current_ksp(void)
+{
+	unsigned long sp;
+
+	asm volatile("mov $30, %0" : "=r"(sp));
+	return sp;
+}
+
+static void alpha_scan_kernel_stack(unsigned long ksp,
+				    stack_trace_consume_fn consume_entry,
+				    void *cookie)
+{
+	unsigned long *p = (unsigned long *)ksp;
+
+	if (unlikely(ksp & (sizeof(unsigned long) - 1)))
+		return;
+
+	while (!kstack_end(p)) {
+		unsigned long addr = READ_ONCE_NOCHECK(*p++);
+
+		if (!__kernel_text_address(addr))
+			continue;
+
+		if (!consume_entry(cookie, addr))
+			break;
+	}
+}
+
+noinline void arch_stack_walk(stack_trace_consume_fn consume_entry,
+				      void *cookie,
+				      struct task_struct *task,
+				      struct pt_regs *regs)
+{
+	unsigned long ksp;
+
+	if (!task)
+		task = current;
+
+	if (regs && task == current) {
+		/*
+		 * pt_regs is stored on the kernel stack; regs+1 matches
+		 * what arch/alpha/kernel/traps.c uses as the trace start.
+		 */
+		ksp = (unsigned long)(regs + 1);
+	} else if (task == current) {
+		ksp = alpha_get_current_ksp();
+	} else {
+		ksp = task_thread_info(task)->pcb.ksp;
+	}
+
+	alpha_scan_kernel_stack(ksp, consume_entry, cookie);
+}
diff --git a/arch/alpha/kernel/vmlinux.lds.S b/arch/alpha/kernel/vmlinux.lds.S
index 2d136c63db16..95704e64b6a6 100644
--- a/arch/alpha/kernel/vmlinux.lds.S
+++ b/arch/alpha/kernel/vmlinux.lds.S
@@ -28,6 +28,8 @@ SECTIONS
 		TEXT_TEXT
 		SCHED_TEXT
 		LOCK_TEXT
+		IRQENTRY_TEXT
+		SOFTIRQENTRY_TEXT
 		*(.fixup)
 		*(.gnu.warning)
 	} :text
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 1/8] alpha: enable regset-based ptrace and core dumps
From: Magnus Lindholm @ 2026-05-29 14:21 UTC (permalink / raw)
  To: richard.henderson, mattst88, linux-kernel, linux-alpha
  Cc: glaubitz, mcree, ink, macro, Magnus Lindholm
In-Reply-To: <20260529142322.1362438-1-linmag7@gmail.com>

Add a user_regset_view for Alpha and switch ELF core dumping to
CORE_DUMP_USE_REGSET.  General-purpose registers are exported in
ELF gregs layout, including callee-saved registers and a correct
user stack pointer.

The user stack pointer is not preserved in pt_regs on Alpha, so expose
it from the PCB, or via rdusp() for the current task, when building the
ELF register image.  This makes the user stack pointer consistent for
core dumps, ptrace regsets, and PTRACE_GET_SYSCALL_INFO.

Implement regset get/set callbacks for both NT_PRSTATUS and NT_PRFPREG.
The callbacks translate between Alpha's pt_regs/thread state and the
ELF-visible register layouts, while the common ptrace regset code handles
PTRACE_GETREGSET and PTRACE_SETREGSET iovec semantics.  This avoids
duplicating subtle short-buffer and oversized-buffer behavior in
arch_ptrace().

With these changes Alpha satisfies the requirements for
HAVE_ARCH_TRACEHOOK and selects it, enabling generic tracehook and
ptrace syscall-info code paths without changing the existing syscall
entry ABI.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
 .../features/core/tracehook/arch-support.txt  |   2 +-
 arch/alpha/Kconfig                            |   1 +
 arch/alpha/include/asm/elf.h                  |   1 +
 arch/alpha/include/asm/ptrace.h               |   7 +
 arch/alpha/include/asm/syscall.h              |   7 +
 arch/alpha/include/asm/thread_info.h          |   7 +-
 arch/alpha/include/uapi/asm/ptrace.h          |   2 +-
 arch/alpha/kernel/asm-offsets.c               |   1 +
 arch/alpha/kernel/entry.S                     |  15 +-
 arch/alpha/kernel/ptrace.c                    | 320 +++++++++++++++---
 arch/alpha/kernel/traps.c                     |   8 +
 11 files changed, 312 insertions(+), 59 deletions(-)

diff --git a/Documentation/features/core/tracehook/arch-support.txt b/Documentation/features/core/tracehook/arch-support.txt
index 4f36fcbfb6d5..654f38413d16 100644
--- a/Documentation/features/core/tracehook/arch-support.txt
+++ b/Documentation/features/core/tracehook/arch-support.txt
@@ -6,7 +6,7 @@
     -----------------------
     |         arch |status|
     -----------------------
-    |       alpha: | TODO |
+    |       alpha: |  ok  |
     |         arc: |  ok  |
     |         arm: |  ok  |
     |       arm64: |  ok  |
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index 7b7dafe7d9df..f3b882835617 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -33,6 +33,7 @@ config ALPHA
 	select HAVE_ARCH_AUDITSYSCALL
 	select HAVE_ARCH_SECCOMP
 	select HAVE_ARCH_SECCOMP_FILTER
+	select HAVE_ARCH_TRACEHOOK
 	select HAVE_MOD_ARCH_SPECIFIC
 	select LOCK_MM_AND_FIND_VMA
 	select MODULES_USE_ELF_RELA
diff --git a/arch/alpha/include/asm/elf.h b/arch/alpha/include/asm/elf.h
index 50c82187e60e..b15946621d57 100644
--- a/arch/alpha/include/asm/elf.h
+++ b/arch/alpha/include/asm/elf.h
@@ -53,6 +53,7 @@
 
 #define EF_ALPHA_32BIT		1	/* All addresses are below 2GB */
 
+#define CORE_DUMP_USE_REGSET	1
 /*
  * ELF register definitions..
  */
diff --git a/arch/alpha/include/asm/ptrace.h b/arch/alpha/include/asm/ptrace.h
index 3557ce64ed21..8e0a589e2d15 100644
--- a/arch/alpha/include/asm/ptrace.h
+++ b/arch/alpha/include/asm/ptrace.h
@@ -24,4 +24,11 @@ static inline unsigned long regs_return_value(struct pt_regs *regs)
 	return regs->r0;
 }
 
+/* Helpers for working with the user stack pointer */
+static inline unsigned long user_stack_pointer(struct pt_regs *regs)
+{
+	/* Valid for user-mode regs */
+	return regs->usp;
+}
+
 #endif
diff --git a/arch/alpha/include/asm/syscall.h b/arch/alpha/include/asm/syscall.h
index 584b1ab2e325..1e78cbd46faf 100644
--- a/arch/alpha/include/asm/syscall.h
+++ b/arch/alpha/include/asm/syscall.h
@@ -19,6 +19,13 @@ static inline long syscall_get_return_value(struct task_struct *task,
 	return regs->r19 ? -(long)regs->r0 : (long)regs->r0;
 }
 
+static inline long syscall_get_error(struct task_struct *task,
+				     struct pt_regs *regs)
+{
+	return regs->r19 ? -(long)regs->r0 : 0;
+}
+
+
 /*
  * Alpha syscall ABI / kernel conventions:
  *  - PAL provides syscall number in r0 on entry.
diff --git a/arch/alpha/include/asm/thread_info.h b/arch/alpha/include/asm/thread_info.h
index 94ef9cfa30f5..1552ecca8520 100644
--- a/arch/alpha/include/asm/thread_info.h
+++ b/arch/alpha/include/asm/thread_info.h
@@ -66,6 +66,7 @@ register unsigned long *current_stack_pointer __asm__ ("$30");
 #define TIF_SYSCALL_AUDIT	4	/* syscall audit active */
 #define TIF_NOTIFY_SIGNAL	5	/* signal notifications exist */
 #define TIF_SECCOMP		6	/* seccomp syscall filtering active */
+#define	TIF_SYSCALL_TRACEPOINT	7	/* syscall tracepoint instrumentation */
 #define TIF_DIE_IF_KERNEL	9	/* dik recursion lock */
 #define TIF_MEMDIE		13	/* is terminating due to OOM killer */
 #define TIF_POLLING_NRFLAG	14	/* idle is polling for TIF_NEED_RESCHED */
@@ -78,6 +79,7 @@ register unsigned long *current_stack_pointer __asm__ ("$30");
 #define _TIF_NOTIFY_SIGNAL	(1<<TIF_NOTIFY_SIGNAL)
 #define _TIF_SECCOMP		(1<<TIF_SECCOMP)
 #define _TIF_POLLING_NRFLAG	(1<<TIF_POLLING_NRFLAG)
+#define _TIF_SYSCALL_TRACEPOINT	(1<<TIF_SYSCALL_TRACEPOINT)
 
 /*
  * Work to do on syscall entry (in entry.S).
@@ -85,9 +87,10 @@ register unsigned long *current_stack_pointer __asm__ ("$30");
  * with the mask used before branching to syscall_trace_enter().
  */
 #ifdef CONFIG_AUDITSYSCALL
-# define _TIF_SYSCALL_WORK	(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP)
+# define _TIF_SYSCALL_WORK	(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP \
+				| _TIF_SYSCALL_TRACEPOINT)
 #else
-# define _TIF_SYSCALL_WORK	(_TIF_SYSCALL_TRACE | _TIF_SECCOMP)
+# define _TIF_SYSCALL_WORK	(_TIF_SYSCALL_TRACE | _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT)
 #endif
 
 /* Work to do on interrupt/exception return.  */
diff --git a/arch/alpha/include/uapi/asm/ptrace.h b/arch/alpha/include/uapi/asm/ptrace.h
index 72ed913a910f..9d86b2a1526e 100644
--- a/arch/alpha/include/uapi/asm/ptrace.h
+++ b/arch/alpha/include/uapi/asm/ptrace.h
@@ -43,7 +43,7 @@ struct pt_regs {
 	unsigned long trap_a1;
 	unsigned long trap_a2;
 /* This makes the stack 16-byte aligned as GCC expects */
-	unsigned long __pad0;
+	unsigned long usp;
 /* These are saved by PAL-code: */
 	unsigned long ps;
 	unsigned long pc;
diff --git a/arch/alpha/kernel/asm-offsets.c b/arch/alpha/kernel/asm-offsets.c
index 1ebb05890499..1d3bfca319ae 100644
--- a/arch/alpha/kernel/asm-offsets.c
+++ b/arch/alpha/kernel/asm-offsets.c
@@ -29,4 +29,5 @@ static void __used foo(void)
 
 	DEFINE(HAE_CACHE, offsetof(struct alpha_machine_vector, hae_cache));
 	DEFINE(HAE_REG, offsetof(struct alpha_machine_vector, hae_register));
+	DEFINE(PT_REGS_USP, offsetof(struct pt_regs, usp));
 }
diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S
index fcfd06529b12..449092a31eef 100644
--- a/arch/alpha/kernel/entry.S
+++ b/arch/alpha/kernel/entry.S
@@ -520,6 +520,12 @@ entSys:
         ldq     $1, 0($sp)          /* syscall nr from saved r0 */
         stq     $1, 8($sp)          /* regs->r1 = shadow syscall nr */
         stq     $1, 16($sp)         /* regs->r2 = restart syscall nr */
+	/* Syscalls always enter from user mode: snapshot USP into pt_regs->usp */
+	mov	$0, $8
+	call_pal PAL_rdusp
+	stq      $0, PT_REGS_USP($sp)
+	mov	$8, $0
+
 
 	lda	$8, 0x3fff
 	bic	$sp, $8, $8
@@ -535,15 +541,10 @@ entSys:
 	.cfi_rel_offset	$16, SP_OFF+24
 	.cfi_rel_offset	$17, SP_OFF+32
 	.cfi_rel_offset	$18, SP_OFF+40
-#ifdef CONFIG_AUDITSYSCALL
-	lda     $6, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP
-	and     $3, $6, $3
-	bne     $3, strace
-#else
-	lda     $6, _TIF_SYSCALL_TRACE | _TIF_SECCOMP
+	lda     $6, _TIF_SYSCALL_WORK
 	and     $3, $6, $3
 	bne     $3, strace
-#endif
+
 	beq	$4, 1f
 	ldq	$27, 0($5)
 1:	ldq	$0, 8($sp)		/* syscall nr shadow (regs->r1) */
diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c
index 0687760ea466..69eb337347df 100644
--- a/arch/alpha/kernel/ptrace.c
+++ b/arch/alpha/kernel/ptrace.c
@@ -24,10 +24,15 @@
 
 #include "proto.h"
 #include <linux/uio.h>
+#include <linux/regset.h>
 
 #define DEBUG	DBG_MEM
 #undef DEBUG
 
+#ifndef NT_FPREGSET
+#define NT_FPREGSET NT_PRFPREG
+#endif
+
 #ifdef DEBUG
 enum {
 	DBG_MEM		= (1<<0),
@@ -143,19 +148,163 @@ get_reg(struct task_struct * task, unsigned long regno)
 	return *get_reg_addr(task, regno);
 }
 
+static void alpha_elf_fpregs_get(struct task_struct *target,
+			 elf_fpreg_t *fpregs)  /* points to ELF_NFPREG entries */
+{
+	memcpy(fpregs, task_thread_info(target)->fp, sizeof(elf_fpregset_t));
+}
+
+static void alpha_elf_fpregs_set(struct task_struct *target,
+			 const elf_fpreg_t *fpregs,
+			 size_t nwords)
+{
+	size_t n = min_t(size_t, nwords, ELF_NFPREG);
+
+	memcpy(task_thread_info(target)->fp, fpregs, n * sizeof(elf_fpreg_t));
+}
+
+static void alpha_elf_gregs_set(struct task_struct *child,
+			const elf_greg_t *src,
+			size_t nwords)
+{
+	struct pt_regs *pt = task_pt_regs(child);
+	struct thread_info *ti = task_thread_info(child);
+	struct switch_stack *sw = ((struct switch_stack *)pt) - 1;
+
+	/* GPRs r0..r8 live in pt_regs */
+	if (nwords > 0)
+		pt->r0 = src[0];
+	if (nwords > 1)
+		pt->r1 = src[1];
+	if (nwords > 2)
+		pt->r2 = src[2];
+	if (nwords > 3)
+		pt->r3 = src[3];
+	if (nwords > 4)
+		pt->r4 = src[4];
+	if (nwords > 5)
+		pt->r5 = src[5];
+	if (nwords > 6)
+		pt->r6 = src[6];
+	if (nwords > 7)
+		pt->r7 = src[7];
+	if (nwords > 8)
+		pt->r8 = src[8];
+
+	/* r9..r15 live in switch_stack */
+	if (nwords > 9)
+		sw->r9 = src[9];
+	if (nwords > 10)
+		sw->r10 = src[10];
+	if (nwords > 11)
+		sw->r11 = src[11];
+	if (nwords > 12)
+		sw->r12 = src[12];
+	if (nwords > 13)
+		sw->r13 = src[13];
+	if (nwords > 14)
+		sw->r14 = src[14];
+	if (nwords > 15)
+		sw->r15 = src[15];
+
+	/* r16..r28 live in pt_regs */
+	if (nwords > 16)
+		pt->r16 = src[16];
+	if (nwords > 17)
+		pt->r17 = src[17];
+	if (nwords > 18)
+		pt->r18 = src[18];
+	if (nwords > 19)
+		pt->r19 = src[19];
+	if (nwords > 20)
+		pt->r20 = src[20];
+	if (nwords > 21)
+		pt->r21 = src[21];
+	if (nwords > 22)
+		pt->r22 = src[22];
+	if (nwords > 23)
+		pt->r23 = src[23];
+	if (nwords > 24)
+		pt->r24 = src[24];
+	if (nwords > 25)
+		pt->r25 = src[25];
+	if (nwords > 26)
+		pt->r26 = src[26];
+	if (nwords > 27)
+		pt->r27 = src[27];
+	if (nwords > 28)
+		pt->r28 = src[28];
+
+	/* gp, usp, pc, unique */
+	if (nwords > 29)
+		pt->gp = src[29];
+
+	if (nwords > 30) {
+		ti->pcb.usp = src[30];
+		/*
+		 * If someone ever does this to current (rare), keep the
+		 * hardware usp consistent.
+		 */
+		if (child == current)
+			wrusp(src[30]);
+	}
+
+	if (nwords > 31)
+		pt->pc = src[31];
+
+	if (nwords > 32)
+		ti->pcb.unique = src[32];
+
+/*
+ * PTRACE_SETREGSET can be used at a syscall-entry stop to skip the
+ * syscall by setting the syscall number to -1.  The seccomp/ptrace
+ * selftests use this to synthesize errno returns.
+ *
+ * Alpha uses r19/a3 as the error flag, so a skipped syscall with a
+ * small positive r0 and a clear r19 must be normalized to an error
+ * return.
+ */
+	if (pt->r1 == (unsigned long)-1 &&
+	    pt->r19 == 0 &&
+	    pt->r0 > 0 &&
+	    pt->r0 < MAX_ERRNO)
+		pt->r19 = 1;
+}
+
+
 /*
  * Write contents of register REGNO in task TASK.
  */
 static int
 put_reg(struct task_struct *task, unsigned long regno, unsigned long data)
 {
+	struct pt_regs *regs = task_pt_regs(task);
+
 	if (regno == 63) {
 		task_thread_info(task)->ieee_state
 		  = ((task_thread_info(task)->ieee_state & ~IEEE_SW_MASK)
 		     | (data & IEEE_SW_MASK));
 		data = (data & FPCR_DYN_MASK) | ieee_swcr_to_fpcr(data);
 	}
+
 	*get_reg_addr(task, regno) = data;
+
+	/*
+	 * Alpha historically exposes r0/v0 as the syscall number at a
+	 * syscall-entry stop.  The generic-entry conversion keeps the
+	 * mutable syscall number in regs->r1, so old ptrace users such
+	 * as strace that skip a syscall by poking r0 to -1 must also
+	 * update the internal shadow syscall number.
+	 *
+	 * Do not mirror other r0 writes.  strace later pokes r0 to the
+	 * injected return value, e.g. 42, while r1 must remain -1.
+	 */
+
+	if (regno == 0 && data == (unsigned long)-1) {
+		regs->r1 = data;
+		regs->r19 = 0;
+	}
+
 	return 0;
 }
 
@@ -315,54 +464,6 @@ long arch_ptrace(struct task_struct *child, long request,
 		DBG(DBG_MEM, ("poke $%lu<-%#lx\n", addr, data));
 		ret = put_reg(child, addr, data);
 		break;
-	case PTRACE_GETREGSET:
-	case PTRACE_SETREGSET: {
-		struct iovec __user *uiov = (struct iovec __user *)data;
-		struct iovec iov;
-		struct pt_regs *regs;
-		size_t len;
-
-		/* Only support NT_PRSTATUS (general registers) for now. */
-		if (addr != NT_PRSTATUS) {
-			ret = -EIO;
-			break;
-		}
-
-		if (copy_from_user(&iov, uiov, sizeof(iov))) {
-			ret = -EFAULT;
-			break;
-		}
-
-		regs = task_pt_regs(child);
-		len = min_t(size_t, iov.iov_len, sizeof(*regs));
-
-		if (request == PTRACE_GETREGSET) {
-			if (copy_to_user(iov.iov_base, regs, len)) {
-				ret = -EFAULT;
-				break;
-			}
-		} else {
-		/*
-		 * Allow writing back regs. This is needed by the TRACE_syscall
-		 * tests (they change PC/syscall nr/retval).
-		 */
-			if (copy_from_user(regs, iov.iov_base, len)) {
-				ret = -EFAULT;
-				break;
-			}
-		}
-
-		/* Per API, update iov_len with amount transferred. */
-		iov.iov_len = len;
-		if (copy_to_user(uiov, &iov, sizeof(iov))) {
-			ret = -EFAULT;
-			break;
-		}
-
-		ret = 0;
-		break;
-	}
-
 	default:
 		ret = ptrace_request(child, request, addr, data);
 		break;
@@ -410,3 +511,126 @@ syscall_trace_leave(void)
 	if (test_thread_flag(TIF_SYSCALL_TRACE))
 		ptrace_report_syscall_exit(current_pt_regs(), 0);
 }
+
+/*
+ * Minimal regset support for Alpha.
+ *
+ * Alpha-specific notes:
+ *  - Do NOT use ELF_CORE_COPY_REGS(): it uses current_thread_info(),
+ *    which is wrong for non-current tasks.
+ *  - dump_elf_task() returns 1 unconditionally in this tree, while
+ *    regset_get should return 0 on success. So call dump_elf_thread()
+ *    directly and return membuf_write()'s result.
+ */
+
+static int alpha_regset_set(struct task_struct *target,
+			    const struct user_regset *regset,
+			    unsigned int pos, unsigned int count,
+			    const void *kbuf,
+			    const void __user *ubuf)
+{
+	elf_gregset_t gregs;
+	unsigned int nwords;
+
+	if (pos + count > sizeof(gregs))
+		return -EIO;
+
+	/*
+	 * Preserve registers outside the written range.
+	 */
+	dump_elf_thread(gregs, task_pt_regs(target),
+			task_thread_info(target));
+
+	if (user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+				gregs, 0, sizeof(gregs)))
+		return -EFAULT;
+
+	nwords = sizeof(gregs) / sizeof(elf_greg_t);
+	alpha_elf_gregs_set(target, gregs, nwords);
+
+	return 0;
+}
+
+static int alpha_fpregset_set(struct task_struct *target,
+			      const struct user_regset *regset,
+			      unsigned int pos, unsigned int count,
+			      const void *kbuf,
+			      const void __user *ubuf)
+{
+	elf_fpregset_t fpregs;
+	unsigned int nwords;
+
+	if (pos + count > sizeof(fpregs))
+		return -EIO;
+
+	alpha_elf_fpregs_get(target, fpregs);
+
+	if (user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+				fpregs, 0, sizeof(fpregs)))
+		return -EFAULT;
+
+	nwords = sizeof(fpregs) / sizeof(elf_fpreg_t);
+	alpha_elf_fpregs_set(target, fpregs, nwords);
+
+	return 0;
+}
+
+static int alpha_regset_get(struct task_struct *target,
+			    const struct user_regset *regset,
+			    struct membuf to)
+{
+	struct pt_regs *pt = task_pt_regs(target);
+	struct thread_info *ti = task_thread_info(target);
+	elf_gregset_t gregs;
+
+	dump_elf_thread(gregs, pt, ti);
+	return membuf_write(&to, gregs, sizeof(gregs));
+}
+
+static int alpha_fpregset_get(struct task_struct *target,
+			      const struct user_regset *regset,
+			      struct membuf to)
+{
+	elf_fpregset_t fpregs;
+
+	alpha_elf_fpregs_get(target, fpregs);
+	return membuf_write(&to, fpregs, sizeof(fpregs));
+}
+
+enum alpha_regset {
+	REGSET_GPR,
+	REGSET_FPR,
+};
+
+static const struct user_regset alpha_user_regsets[] = {
+	[REGSET_GPR] = {
+		.core_note_type	= NT_PRSTATUS,
+		.n		= ELF_NGREG,
+		.size		= sizeof(elf_greg_t),
+		.align		= sizeof(elf_greg_t),
+		.regset_get	= alpha_regset_get,
+		.set		= alpha_regset_set,
+	},
+	[REGSET_FPR] = {
+		.core_note_type	= NT_PRFPREG,
+		.core_note_name	= "CORE",
+		.n		= ELF_NFPREG,
+		.size		= sizeof(elf_fpreg_t),
+		.align		= sizeof(elf_fpreg_t),
+		.regset_get	= alpha_fpregset_get,
+		.set		= alpha_fpregset_set,
+	},
+};
+
+static const struct user_regset_view user_alpha_view = {
+	.name		= "alpha",
+	.e_machine	= EM_ALPHA,
+	.ei_osabi	= ELF_OSABI,
+	.regsets	= alpha_user_regsets,
+	.n		= ARRAY_SIZE(alpha_user_regsets),
+};
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+	return &user_alpha_view;
+}
diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c
index 7004397937cf..7631129ac914 100644
--- a/arch/alpha/kernel/traps.c
+++ b/arch/alpha/kernel/traps.c
@@ -30,6 +30,12 @@
 
 #include "proto.h"
 
+static __always_inline void alpha_snapshot_usp(struct pt_regs *regs)
+{
+	if (user_mode(regs))
+		regs->usp = rdusp();
+}
+
 void
 dik_show_regs(struct pt_regs *regs, unsigned long *r9_15)
 {
@@ -180,6 +186,7 @@ do_entArith(unsigned long summary, unsigned long write_mask,
 {
 	long si_code = FPE_FLTINV;
 
+	alpha_snapshot_usp(regs);
 	if (summary & 1) {
 		/* Software-completion summary bit is set, so try to
 		   emulate the instruction.  If the processor supports
@@ -201,6 +208,7 @@ do_entIF(unsigned long type, struct pt_regs *regs)
 {
 	int signo, code;
 
+	alpha_snapshot_usp(regs);
 	if (type == 3) { /* FEN fault */
 		/* Irritating users can call PAL_clrfen to disable the
 		   FPU for the process.  The kernel will then trap in
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 0/8] alpha: enable generic entry infrastructure
From: Magnus Lindholm @ 2026-05-29 14:21 UTC (permalink / raw)
  To: richard.henderson, mattst88, linux-kernel, linux-alpha
  Cc: glaubitz, mcree, ink, macro, Magnus Lindholm

Hi,

This series moves Alpha closer to the common architecture infrastructure
and enables GENERIC_ENTRY and GENERIC_IRQ_ENTRY. This is v2 of this patch
series, please see below for changes since v1.

The main motivation is to reduce Alpha-specific syscall tracing, ptrace,
seccomp, and return-to-user handling where common code already provides
the required infrastructure. Alpha still has ABI-sensitive syscall
details, most notably the assembly syscall invocation and the r0/r19
return-value encoding, so those remain Alpha-specific. The surrounding
entry/exit decisions are moved towards common code.

The series first adds regset-based ptrace and core dump support. Alpha
now provides a user_regset_view for NT_PRSTATUS and NT_PRFPREG, exports
the ELF-visible general and floating-point register layouts, and uses the
common ptrace regset implementation for PTRACE_GETREGSET and
PTRACE_SETREGSET instead of open-coding iovec handling in arch_ptrace().
This also provides the basis for HAVE_ARCH_TRACEHOOK and common ptrace
syscall-info support.

It then adds ARCH_STACKWALK support. The implementation uses a simple
kernel stack scanning walker, starting after pt_regs for current tasks
and using the saved PCB kernel stack pointer for blocked tasks. This
enables common stacktrace users such as dump_stack(), /proc/*/stack, and
SysRq backtraces, without introducing a frame-pointer-based unwinder. It
also avoids relying on the legacy weak save_stack_trace_*() fallbacks,
which is needed by the later generic IRQ-entry configuration in this
series.

The next group of patches prepares Alpha for lockdep IRQ-state tracking.
Alpha interrupt masking is controlled by the PAL IPL state, so the
irqflags helpers are made to operate on IPL state rather than treating
the full processor status word as the saved interrupt state. The series
also initializes dynamically allocated PCI sysfs bin attributes for
lockdep, provides ftrace return-address support for lockdep call-site
reporting, and converts low-level platform locks to raw spinlocks where
regular spinlock instrumentation is not appropriate for the hardware and
interrupt paths involved.

The lockdep patch then adds hardirq state tracking in Alpha's low-level
entry/exit paths. It annotates PAL IPL transitions and the shared return
path so lockdep can keep its interrupt state in sync with the hardware
IPL state. This allows CONFIG_PROVE_LOCKING to remain useful on Alpha
instead of quickly disabling debug_locks due to IRQ state mismatches.

The final patch enables GENERIC_ENTRY and GENERIC_IRQ_ENTRY. Syscall
entry now uses C helpers built around syscall_enter_from_user_mode().
The C entry helper handles the common syscall-entry work, including
ptrace/seccomp entry processing, skip decisions, syscall-number
shadowing, and syscall table lookup. It returns the selected syscall
function pointer to assembly.

Assembly then performs only the Alpha-specific dispatch: it reloads the
syscall arguments, preserves the required GP/stack conventions, and
performs the indirect call through the function pointer returned by C.
Skipped syscalls bypass that dispatch and return through the Alpha
syscall-exit helper.

Syscall exit is split in the same way. Alpha first performs its
ABI-specific result handling: r0/v0 carries the return value or positive
errno, and r19/a3 carries the error flag. It also handles skipped
syscall restart state and successful syscalls whose return values may
look like negative errnos, such as legacy ptrace PEEK requests. Once the
Alpha-specific r0/r19 state is encoded, the path enters
syscall_exit_to_user_mode() for the common syscall-exit and
exit-to-user-mode work.

The syscall number used by generic entry is kept separately from the
return-value register. entSys copies the raw r0 syscall number into r1,
and r1 is used as Alpha's generic-entry syscall-number shadow. Restart
bookkeeping that used to be carried by low-level assembly state is now
kept explicitly in thread_info. This avoids using user-visible general
registers as hidden restart state while preserving Alpha's restart and
ptrace semantics.

Testing was performed on an Alpha EV68/Tsunami SMP system (UP2000+) as
well as an Alphaserver 4100 (rawhide) system.

For the regset, ptrace, tracehook, and generic-entry changes, testing
included:

  - custom ptrace/regset round-trip tests for both GPR and FPR regsets
  - PTRACE_GET_SYSCALL_INFO validation, including the reported user
    stack pointer
  - ELF core dump inspection with readelf
  - strace testsuite (make check), with the remaining failures comparable
    to the pre-series baseline on the same system.

Kernel tools/testing/selftests:
  - ptrace: syscall-info tests improved over the baseline.
  - seccomp: seccomp_bpf passes all applicable tests.
  - exec: check-exec now passes once loop block-device support is enabled.
  - ipc: ipc selftests improved over the baseline.
  - acct: acct selftests improved over the baseline.

Overall, the selftest comparison did not show an obvious regression
introduced by this series. Instead, the patched kernel reduced the
number of failing top-level selftests and fixed several failures in areas
that exercise the new ptrace, tracehook, seccomp, and generic-entry
paths. Some unrelated failures remain and need separate investigation,
but they are either also present in the baseline run or explained by
missing kernel configuration or test-environment support.

For the stacktrace changes, the following scenarios were verified:

  - SysRq 'l' prints backtraces for all CPUs, including syscall and
    interrupt contexts
  - /proc/<pid>/stack returns sensible call chains for both the current
    task and sleeping tasks, such as nanosleep
  - stack traces remain stable under scheduler and I/O load, including
    ping, ext4 writes, and background workqueues
  - no crashes, lockups, or obvious mis-unwinds were observed

For the lockdep changes, /proc/lockdep_stats shows debug_locks remaining
enabled, with no redundant hardirq or softirq on/off annotations observed
during testing.

Review feedback is very welcome. Additional testing on other Alpha
systems, CPU variants, and workloads would also be much appreciated.

Patches:

  1. alpha: enable regset-based ptrace and core dumps
  2. alpha: add ARCH_STACKWALK-based stacktrace support
  3. alpha: make irqflags helpers operate on IPL state
  4. alpha: initialize PCI sysfs bin attributes for lockdep
  5. alpha: provide ftrace return address support for lockdep
  6. alpha: convert low-level platform locks to raw spinlocks
  7. alpha: enable lockdep hardirq state tracking
  8. alpha: enable GENERIC_ENTRY and GENERIC_IRQ_ENTRY

Thanks,

Magnus

---
Changes since v1:
 - Fixed skipped-syscall exit handling so PTRACE_SET_SYSCALL_INFO and legacy
   POKEUSR skips return ENOSYS/a3=1 only when no ptrace/seccomp return value
   was installed.

Magnus Lindholm (8):
  alpha: enable regset-based ptrace and core dumps
  alpha: add ARCH_STACKWALK-based stacktrace support
  alpha: make irqflags helpers operate on IPL state
  alpha: initialize PCI sysfs bin attributes for lockdep
  alpha: provide ftrace return address support for lockdep
  alpha: use raw spinlocks for low-level platform locks
  alpha: enable lockdep hardirq state tracking
  alpha: enable GENERIC_ENTRY and GENERIC_IRQ_ENTRY

 .../features/core/tracehook/arch-support.txt  |   2 +-
 .../features/locking/lockdep/arch-support.txt |   2 +-
 arch/alpha/Kconfig                            |  13 +
 arch/alpha/include/asm/elf.h                  |   1 +
 arch/alpha/include/asm/entry-common.h         |  14 +
 arch/alpha/include/asm/ftrace.h               |  30 +-
 arch/alpha/include/asm/irqflags.h             |   6 +-
 arch/alpha/include/asm/ptrace.h               |  21 +-
 arch/alpha/include/asm/stacktrace.h           |  20 +
 arch/alpha/include/asm/syscall.h              |  18 +-
 arch/alpha/include/asm/thread_info.h          |  31 +-
 arch/alpha/include/uapi/asm/ptrace.h          |   2 +-
 arch/alpha/kernel/Makefile                    |   3 +-
 arch/alpha/kernel/asm-offsets.c               |   6 +
 arch/alpha/kernel/entry.S                     | 328 +++++---------
 arch/alpha/kernel/irq_alpha.c                 |  78 +++-
 arch/alpha/kernel/irq_i8259.c                 |  19 +-
 arch/alpha/kernel/pci-sysfs.c                 |   2 +
 arch/alpha/kernel/proto.h                     |  13 +-
 arch/alpha/kernel/ptrace.c                    | 405 ++++++++++++++----
 arch/alpha/kernel/signal.c                    | 146 ++++++-
 arch/alpha/kernel/stacktrace.c                |  61 +++
 arch/alpha/kernel/sys_dp264.c                 |  38 +-
 arch/alpha/kernel/sys_rawhide.c               |  17 +-
 arch/alpha/kernel/traps.c                     |   8 +
 arch/alpha/kernel/vmlinux.lds.S               |   2 +
 26 files changed, 865 insertions(+), 421 deletions(-)
 create mode 100644 arch/alpha/include/asm/entry-common.h
 create mode 100644 arch/alpha/include/asm/stacktrace.h
 create mode 100644 arch/alpha/kernel/stacktrace.c

-- 
2.53.0


^ permalink raw reply

* Re: [PATCH] alpha: Fix SMP shutdown hang due to missing memory barriers
From: Magnus Lindholm @ 2026-05-29 13:50 UTC (permalink / raw)
  To: Matt Turner; +Cc: linux-alpha, linux-kernel, Richard Henderson, stable
In-Reply-To: <20260528231043.1842326-1-mattst88@gmail.com>

On Fri, May 29, 2026 at 1:10 AM Matt Turner <mattst88@gmail.com> wrote:
>
> Alpha has a very weak memory model. halt() makes no guarantee that
> pending stores have drained from the store buffer. If set_cpu_present()
> stores are still buffered when a secondary CPU halts, they are lost,
> and the boot CPU spins forever in the cpu_present_mask wait loop.
>
> Add mb() before halt() on secondary CPUs to flush the store buffer,
> and use smp_mb() in the boot CPU's poll loop instead of the
> compiler-only barrier() to ensure it observes secondary CPUs' stores.
>
> This avoids a deadlock on shutdown on EV7/Marvel platforms.
>
> Cc: stable@vger.kernel.org
> Assisted-by: Claude:claude-sonnet-4-6
> Signed-off-by: Matt Turner <mattst88@gmail.com>
> ---
>  arch/alpha/kernel/process.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git ./arch/alpha/kernel/process.c ./arch/alpha/kernel/process.c
> index 06522451f018..d50f9cfd8333 100644
> --- ./arch/alpha/kernel/process.c
> +++ ./arch/alpha/kernel/process.c
> @@ -99,6 +99,7 @@ common_shutdown_1(void *generic_ptr)
>                 *pflags = flags;
>                 set_cpu_present(cpuid, false);
>                 set_cpu_possible(cpuid, false);
> +               mb();
>                 halt();
>         }
>  #endif
> @@ -127,7 +128,7 @@ common_shutdown_1(void *generic_ptr)
>         set_cpu_present(boot_cpuid, false);
>         set_cpu_possible(boot_cpuid, false);
>         while (!cpumask_empty(cpu_present_mask))
> -               barrier();
> +               smp_mb();
>  #endif
>
>         /* If booted from SRM, reset some of the original environment. */
> --
> 2.53.0
>

This looks correct to me. halt() is not a memory-ordering primitive, so on
Alpha the secondary CPU needs a real mb() before stopping. Replacing the
boot CPU's compiler-only barrier() with smp_mb() also looks appropriate for
the polling loop. Looks like you have nailed down a long-standing memory
ordering bug, nice work!

I've applied this patch and, for what it's worth, tested it on my
AlphaServer ES40 to make sure there are no obvious regressions on a
non-EV7 platform. The system shuts down/reboots as expected with this
change applied.

Tested-by: Magnus Lindholm <linmag7@gmail.com>
Reviewed-by: Magnus Lindholm <linmag7@gmail.com>

^ permalink raw reply

* Re: [PATCH 2/2] alpha: marvel: Fix lock ordering in init_io7_irqs()
From: Magnus Lindholm @ 2026-05-29 10:16 UTC (permalink / raw)
  To: Matt Turner
  Cc: linux-alpha, linux-kernel, Richard Henderson, Thomas Gleixner,
	stable
In-Reply-To: <20260528230516.1839694-2-mattst88@gmail.com>

On Fri, May 29, 2026 at 1:05 AM Matt Turner <mattst88@gmail.com> wrote:
>
> Move irq_set_chip_and_handler() and irq_set_status_flags() calls
> outside the io7->irq_lock raw spinlock.  These functions take
> sparse_irq_lock, which is a mutex, and taking a sleeping lock while
> holding a raw spinlock is invalid.  The raw spinlock only needs to
> protect the hardware CSR accesses.
>
> This fixes the following lockdep splat during boot:
>
>   [ BUG: Invalid wait context ]
>   swapper/0/0 is trying to lock:
>   sparse_irq_lock{....}-{4:4}, at: irq_mark_irq
>   other info that might help us debug this:
>   context-{5:5}
>   1 lock held by swapper/0/0:
>    #0: &io7->irq_lock{....}-{2:2}, at: init_io7_irqs.constprop.0
>
> Cc: stable@vger.kernel.org
> Assisted-by: Claude:claude-opus-4-6
> Signed-off-by: Matt Turner <mattst88@gmail.com>
> ---
>  arch/alpha/kernel/sys_marvel.c | 25 ++++++++++++-------------
>  1 file changed, 12 insertions(+), 13 deletions(-)
>
> diff --git ./arch/alpha/kernel/sys_marvel.c ./arch/alpha/kernel/sys_marvel.c
> index bebeea3c286d..a37707e05e34 100644
> --- ./arch/alpha/kernel/sys_marvel.c
> +++ ./arch/alpha/kernel/sys_marvel.c
> @@ -263,6 +263,18 @@ init_io7_irqs(struct io7 *io7,
>          */
>         printk("  Interrupts reported to CPU at PE %u\n", boot_cpuid);
>
> +       /* Set up the lsi irqs.  */
> +       for (i = 0; i < 128; ++i) {
> +               irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq);
> +               irq_set_status_flags(base + i, IRQ_LEVEL);
> +       }
> +
> +       /* Set up the msi irqs.  */
> +       for (i = 128; i < (128 + 512); ++i) {
> +               irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq);
> +               irq_set_status_flags(base + i, IRQ_LEVEL);
> +       }
> +
>         raw_spin_lock(&io7->irq_lock);
>
>         /* set up the error irqs */
> @@ -272,12 +284,6 @@ init_io7_irqs(struct io7 *io7,
>         io7_redirect_irq(io7, &io7->csrs->STV_CTL.csr, boot_cpuid);
>         io7_redirect_irq(io7, &io7->csrs->HEI_CTL.csr, boot_cpuid);
>
> -       /* Set up the lsi irqs.  */
> -       for (i = 0; i < 128; ++i) {
> -               irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq);
> -               irq_set_status_flags(base + i, IRQ_LEVEL);
> -       }
> -
>         /* Disable the implemented irqs in hardware.  */
>         for (i = 0; i < 0x60; ++i)
>                 init_one_io7_lsi(io7, i, boot_cpuid);
> @@ -285,13 +291,6 @@ init_io7_irqs(struct io7 *io7,
>         init_one_io7_lsi(io7, 0x74, boot_cpuid);
>         init_one_io7_lsi(io7, 0x75, boot_cpuid);
>
> -
> -       /* Set up the msi irqs.  */
> -       for (i = 128; i < (128 + 512); ++i) {
> -               irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq);
> -               irq_set_status_flags(base + i, IRQ_LEVEL);
> -       }
> -
>         for (i = 0; i < 16; ++i)
>                 init_one_io7_msi(io7, i, boot_cpuid);
>
> --
> 2.53.0
>

With the preceding irq_set_status_flags(base + i, ...) fix applied, this
looks correct to me. The generic IRQ descriptor setup is moved outside
io7->irq_lock, while the raw spinlock still protects the IO7 hardware CSR
accesses. That matches the lockdep report and avoids taking sparse_irq_lock
from raw-spinlock context.

Reviewed-by: Magnus Lindholm <linmag7@gmail.com>

^ permalink raw reply

* Re: [PATCH 1/2] alpha: marvel: Fix irq_set_status_flags to use correct IRQ number
From: Magnus Lindholm @ 2026-05-29 10:13 UTC (permalink / raw)
  To: Matt Turner
  Cc: linux-alpha, linux-kernel, Richard Henderson, Thomas Gleixner,
	stable
In-Reply-To: <20260528230516.1839694-1-mattst88@gmail.com>

On Fri, May 29, 2026 at 1:05 AM Matt Turner <mattst88@gmail.com> wrote:
>
> Pass base + i to irq_set_status_flags() to match the IRQ number
> used in irq_set_chip_and_handler(). Previously, IRQ_LEVEL was set
> on the wrong (low-numbered) IRQ descriptors rather than the IO7
> IRQs at base + i.
>
> Cc: stable@vger.kernel.org
> Fixes: 08876fe8519c ("alpha: marvel: Convert irq_chip functions")
> Signed-off-by: Matt Turner <mattst88@gmail.com>
> ---
>  arch/alpha/kernel/sys_marvel.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git ./arch/alpha/kernel/sys_marvel.c ./arch/alpha/kernel/sys_marvel.c
> index 1f99b03effc2..bebeea3c286d 100644
> --- ./arch/alpha/kernel/sys_marvel.c
> +++ ./arch/alpha/kernel/sys_marvel.c
> @@ -275,7 +275,7 @@ init_io7_irqs(struct io7 *io7,
>         /* Set up the lsi irqs.  */
>         for (i = 0; i < 128; ++i) {
>                 irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq);
> -               irq_set_status_flags(i, IRQ_LEVEL);
> +               irq_set_status_flags(base + i, IRQ_LEVEL);
>         }
>
>         /* Disable the implemented irqs in hardware.  */
> @@ -289,7 +289,7 @@ init_io7_irqs(struct io7 *io7,
>         /* Set up the msi irqs.  */
>         for (i = 128; i < (128 + 512); ++i) {
>                 irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq);
> -               irq_set_status_flags(i, IRQ_LEVEL);
> +               irq_set_status_flags(base + i, IRQ_LEVEL);
>         }
>
>         for (i = 0; i < 16; ++i)
> --
> 2.53.0
>

This looks correct to me. irq_set_status_flags() should use the same Linux
IRQ number as irq_set_chip_and_handler(), i.e. base + i, otherwise IRQ_LEVEL
is applied to the wrong low-numbered descriptors rather than the IO7 IRQs.

Reviewed-by: Magnus Lindholm <linmag7@gmail.com>

^ permalink raw reply

* Re: [PATCH] alpha: Use work_on_cpu() for cross-CPU RTC access
From: Magnus Lindholm @ 2026-05-29  8:44 UTC (permalink / raw)
  To: Matt Turner; +Cc: linux-alpha, linux-kernel, Richard Henderson, stable
In-Reply-To: <20260528230750.1840681-1-mattst88@gmail.com>

On Fri, May 29, 2026 at 1:07 AM Matt Turner <mattst88@gmail.com> wrote:
>
> smp_call_function_single() runs its callback in IPI (hardirq)
> context. mc146818_set_time() and mc146818_get_time() take rtc_lock
> (spinlock_t), which is a sleeping lock on PREEMPT_RT, triggering
> a lockdep "Invalid wait context" splat on Marvel SMP.
>
> work_on_cpu() runs the callback in a kthread (process) context,
> which can acquire sleeping locks.
>
> Cc: stable@vger.kernel.org
> Assisted-by: Claude:claude-sonnet-4-6
> Signed-off-by: Matt Turner <mattst88@gmail.com>
> ---
>  arch/alpha/kernel/rtc.c | 11 +++++++----
>  1 file changed, 7 insertions(+), 4 deletions(-)
>
> diff --git ./arch/alpha/kernel/rtc.c ./arch/alpha/kernel/rtc.c
> index cfdf90bc8b3f..4ad5846a1d71 100644
> --- ./arch/alpha/kernel/rtc.c
> +++ ./arch/alpha/kernel/rtc.c
> @@ -15,6 +15,7 @@
>  #include <linux/bcd.h>
>  #include <linux/rtc.h>
>  #include <linux/platform_device.h>
> +#include <linux/workqueue.h>
>
>  #include "proto.h"
>
> @@ -155,11 +156,12 @@ union remote_data {
>         long retval;
>  };
>
> -static void
> +static long
>  do_remote_read(void *data)
>  {
>         union remote_data *x = data;
>         x->retval = alpha_rtc_read_time(NULL, x->tm);
> +       return 0;
>  }
>
>  static int
> @@ -168,17 +170,18 @@ remote_read_time(struct device *dev, struct rtc_time *tm)
>         union remote_data x;
>         if (smp_processor_id() != boot_cpuid) {
>                 x.tm = tm;
> -               smp_call_function_single(boot_cpuid, do_remote_read, &x, 1);
> +               work_on_cpu(boot_cpuid, do_remote_read, &x);
>                 return x.retval;
>         }
>         return alpha_rtc_read_time(NULL, tm);
>  }
>
> -static void
> +static long
>  do_remote_set(void *data)
>  {
>         union remote_data *x = data;
>         x->retval = alpha_rtc_set_time(NULL, x->tm);
> +       return 0;
>  }
>
>  static int
> @@ -187,7 +190,7 @@ remote_set_time(struct device *dev, struct rtc_time *tm)
>         union remote_data x;
>         if (smp_processor_id() != boot_cpuid) {
>                 x.tm = tm;
> -               smp_call_function_single(boot_cpuid, do_remote_set, &x, 1);
> +               work_on_cpu(boot_cpuid, do_remote_set, &x);
>                 return x.retval;
>         }
>         return alpha_rtc_set_time(NULL, tm);
> --
> 2.53.0
>


Hi Matt,

Very impressive works, thanks alot for taking the time to do this!

The overall approach makes sense for RT: smp_call_function_single()
runs the callback from the IPI path, so calling into mc146818 code that
takes rtc_lock is not valid once spinlock_t can sleep.

However, I don't think this should ignore the return value from
work_on_cpu(). work_on_cpu() returns fn(arg), so the callbacks can return
alpha_rtc_{read,set}_time() directly and remote_{read,set}_time() should
return work_on_cpu(...). That also avoids depending on x.retval if
work_on_cpu() itself fails.

Also, now that this path is intentionally process-context/sleepable, the
existing smp_processor_id() direct-call fast path deserves another look.
A task could test that it is on boot_cpuid and then migrate before the
direct alpha_rtc_*() call. If the access must be on boot_cpuid, either
always use work_on_cpu(boot_cpuid, ...) or protect the direct path
appropriately.

So I agree with the overall approach here. I wonder if we could simplify
the conversion by returning the alpha_rtc_{read,set}_time() result directly
from the work_on_cpu() callback and then returning work_on_cpu()
from remote_{read,set}_time(). Also, now that this path is intentionally
process-context/sleepable, do you think the existing smp_processor_id()
fast path is still safe against migration, or should we route the access
through work_on_cpu() unconditionally?

Regards

Magnus

^ permalink raw reply

* [PATCH] alpha: Fix SMP shutdown hang due to missing memory barriers
From: Matt Turner @ 2026-05-28 23:10 UTC (permalink / raw)
  To: linux-alpha
  Cc: linux-kernel, Richard Henderson, Magnus Lindholm, Matt Turner,
	stable

Alpha has a very weak memory model. halt() makes no guarantee that
pending stores have drained from the store buffer. If set_cpu_present()
stores are still buffered when a secondary CPU halts, they are lost,
and the boot CPU spins forever in the cpu_present_mask wait loop.

Add mb() before halt() on secondary CPUs to flush the store buffer,
and use smp_mb() in the boot CPU's poll loop instead of the
compiler-only barrier() to ensure it observes secondary CPUs' stores.

This avoids a deadlock on shutdown on EV7/Marvel platforms.

Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/kernel/process.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git ./arch/alpha/kernel/process.c ./arch/alpha/kernel/process.c
index 06522451f018..d50f9cfd8333 100644
--- ./arch/alpha/kernel/process.c
+++ ./arch/alpha/kernel/process.c
@@ -99,6 +99,7 @@ common_shutdown_1(void *generic_ptr)
 		*pflags = flags;
 		set_cpu_present(cpuid, false);
 		set_cpu_possible(cpuid, false);
+		mb();
 		halt();
 	}
 #endif
@@ -127,7 +128,7 @@ common_shutdown_1(void *generic_ptr)
 	set_cpu_present(boot_cpuid, false);
 	set_cpu_possible(boot_cpuid, false);
 	while (!cpumask_empty(cpu_present_mask))
-		barrier();
+		smp_mb();
 #endif
 
 	/* If booted from SRM, reset some of the original environment. */
-- 
2.53.0


^ permalink raw reply related

* [PATCH] alpha: Use work_on_cpu() for cross-CPU RTC access
From: Matt Turner @ 2026-05-28 23:07 UTC (permalink / raw)
  To: linux-alpha
  Cc: linux-kernel, Richard Henderson, Magnus Lindholm, Matt Turner,
	stable

smp_call_function_single() runs its callback in IPI (hardirq)
context. mc146818_set_time() and mc146818_get_time() take rtc_lock
(spinlock_t), which is a sleeping lock on PREEMPT_RT, triggering
a lockdep "Invalid wait context" splat on Marvel SMP.

work_on_cpu() runs the callback in a kthread (process) context,
which can acquire sleeping locks.

Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/kernel/rtc.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git ./arch/alpha/kernel/rtc.c ./arch/alpha/kernel/rtc.c
index cfdf90bc8b3f..4ad5846a1d71 100644
--- ./arch/alpha/kernel/rtc.c
+++ ./arch/alpha/kernel/rtc.c
@@ -15,6 +15,7 @@
 #include <linux/bcd.h>
 #include <linux/rtc.h>
 #include <linux/platform_device.h>
+#include <linux/workqueue.h>
 
 #include "proto.h"
 
@@ -155,11 +156,12 @@ union remote_data {
 	long retval;
 };
 
-static void
+static long
 do_remote_read(void *data)
 {
 	union remote_data *x = data;
 	x->retval = alpha_rtc_read_time(NULL, x->tm);
+	return 0;
 }
 
 static int
@@ -168,17 +170,18 @@ remote_read_time(struct device *dev, struct rtc_time *tm)
 	union remote_data x;
 	if (smp_processor_id() != boot_cpuid) {
 		x.tm = tm;
-		smp_call_function_single(boot_cpuid, do_remote_read, &x, 1);
+		work_on_cpu(boot_cpuid, do_remote_read, &x);
 		return x.retval;
 	}
 	return alpha_rtc_read_time(NULL, tm);
 }
 
-static void
+static long
 do_remote_set(void *data)
 {
 	union remote_data *x = data;
 	x->retval = alpha_rtc_set_time(NULL, x->tm);
+	return 0;
 }
 
 static int
@@ -187,7 +190,7 @@ remote_set_time(struct device *dev, struct rtc_time *tm)
 	union remote_data x;
 	if (smp_processor_id() != boot_cpuid) {
 		x.tm = tm;
-		smp_call_function_single(boot_cpuid, do_remote_set, &x, 1);
+		work_on_cpu(boot_cpuid, do_remote_set, &x);
 		return x.retval;
 	}
 	return alpha_rtc_set_time(NULL, tm);
-- 
2.53.0


^ permalink raw reply related

* [PATCH 2/2] alpha: marvel: Fix lock ordering in init_io7_irqs()
From: Matt Turner @ 2026-05-28 23:05 UTC (permalink / raw)
  To: linux-alpha
  Cc: linux-kernel, Richard Henderson, Magnus Lindholm, Thomas Gleixner,
	Matt Turner, stable
In-Reply-To: <20260528230516.1839694-1-mattst88@gmail.com>

Move irq_set_chip_and_handler() and irq_set_status_flags() calls
outside the io7->irq_lock raw spinlock.  These functions take
sparse_irq_lock, which is a mutex, and taking a sleeping lock while
holding a raw spinlock is invalid.  The raw spinlock only needs to
protect the hardware CSR accesses.

This fixes the following lockdep splat during boot:

  [ BUG: Invalid wait context ]
  swapper/0/0 is trying to lock:
  sparse_irq_lock{....}-{4:4}, at: irq_mark_irq
  other info that might help us debug this:
  context-{5:5}
  1 lock held by swapper/0/0:
   #0: &io7->irq_lock{....}-{2:2}, at: init_io7_irqs.constprop.0

Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/kernel/sys_marvel.c | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git ./arch/alpha/kernel/sys_marvel.c ./arch/alpha/kernel/sys_marvel.c
index bebeea3c286d..a37707e05e34 100644
--- ./arch/alpha/kernel/sys_marvel.c
+++ ./arch/alpha/kernel/sys_marvel.c
@@ -263,6 +263,18 @@ init_io7_irqs(struct io7 *io7,
 	 */
 	printk("  Interrupts reported to CPU at PE %u\n", boot_cpuid);
 
+	/* Set up the lsi irqs.  */
+	for (i = 0; i < 128; ++i) {
+		irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq);
+		irq_set_status_flags(base + i, IRQ_LEVEL);
+	}
+
+	/* Set up the msi irqs.  */
+	for (i = 128; i < (128 + 512); ++i) {
+		irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq);
+		irq_set_status_flags(base + i, IRQ_LEVEL);
+	}
+
 	raw_spin_lock(&io7->irq_lock);
 
 	/* set up the error irqs */
@@ -272,12 +284,6 @@ init_io7_irqs(struct io7 *io7,
 	io7_redirect_irq(io7, &io7->csrs->STV_CTL.csr, boot_cpuid);
 	io7_redirect_irq(io7, &io7->csrs->HEI_CTL.csr, boot_cpuid);
 
-	/* Set up the lsi irqs.  */
-	for (i = 0; i < 128; ++i) {
-		irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq);
-		irq_set_status_flags(base + i, IRQ_LEVEL);
-	}
-
 	/* Disable the implemented irqs in hardware.  */
 	for (i = 0; i < 0x60; ++i) 
 		init_one_io7_lsi(io7, i, boot_cpuid);
@@ -285,13 +291,6 @@ init_io7_irqs(struct io7 *io7,
 	init_one_io7_lsi(io7, 0x74, boot_cpuid);
 	init_one_io7_lsi(io7, 0x75, boot_cpuid);
 
-
-	/* Set up the msi irqs.  */
-	for (i = 128; i < (128 + 512); ++i) {
-		irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq);
-		irq_set_status_flags(base + i, IRQ_LEVEL);
-	}
-
 	for (i = 0; i < 16; ++i)
 		init_one_io7_msi(io7, i, boot_cpuid);
 
-- 
2.53.0


^ permalink raw reply related

* [PATCH 1/2] alpha: marvel: Fix irq_set_status_flags to use correct IRQ number
From: Matt Turner @ 2026-05-28 23:05 UTC (permalink / raw)
  To: linux-alpha
  Cc: linux-kernel, Richard Henderson, Magnus Lindholm, Thomas Gleixner,
	Matt Turner, stable

Pass base + i to irq_set_status_flags() to match the IRQ number
used in irq_set_chip_and_handler(). Previously, IRQ_LEVEL was set
on the wrong (low-numbered) IRQ descriptors rather than the IO7
IRQs at base + i.

Cc: stable@vger.kernel.org
Fixes: 08876fe8519c ("alpha: marvel: Convert irq_chip functions")
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/kernel/sys_marvel.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git ./arch/alpha/kernel/sys_marvel.c ./arch/alpha/kernel/sys_marvel.c
index 1f99b03effc2..bebeea3c286d 100644
--- ./arch/alpha/kernel/sys_marvel.c
+++ ./arch/alpha/kernel/sys_marvel.c
@@ -275,7 +275,7 @@ init_io7_irqs(struct io7 *io7,
 	/* Set up the lsi irqs.  */
 	for (i = 0; i < 128; ++i) {
 		irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq);
-		irq_set_status_flags(i, IRQ_LEVEL);
+		irq_set_status_flags(base + i, IRQ_LEVEL);
 	}
 
 	/* Disable the implemented irqs in hardware.  */
@@ -289,7 +289,7 @@ init_io7_irqs(struct io7 *io7,
 	/* Set up the msi irqs.  */
 	for (i = 128; i < (128 + 512); ++i) {
 		irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq);
-		irq_set_status_flags(i, IRQ_LEVEL);
+		irq_set_status_flags(base + i, IRQ_LEVEL);
 	}
 
 	for (i = 0; i < 16; ++i)
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH] fix typo in alpha/lib Makefile comment
From: Mike Hlavac @ 2026-05-27 11:48 UTC (permalink / raw)
  To: Michael Cree; +Cc: linux-alpha
In-Reply-To: <ahZGbHHNVIuYyx9t@creeky>



> On May 26, 2026, at 9:18 PM, Michael Cree <mcree@orcon.net.nz> wrote:
> 
> On Tue, May 26, 2026 at 07:00:34PM -0400, Mike Hlavac wrote:
>> Fix a typo in a comment in arch/alpha/lib/Makefile.  “Iff” -> “if"
>> 
>> Signed-off-by:   Mike Hlavac (Mike@flyingpenguins.org)
>> 
>> --- /home/griffin/kernel-hacking/alpha-lib-typo/Makefile-orig	2026-05-26 14:49:18.246555089 -0400
>> +++ arch/alpha/lib/Makefile	2026-05-26 14:50:32.805555377 -0400
>> @@ -6,7 +6,7 @@
>> asflags-y := $(KBUILD_CFLAGS)
>> 
>> # Many of these routines have implementations tuned for ev6.
>> -# Choose them iff we're targeting ev6 specifically.
>> +# Choose them if we're targeting ev6 specifically.
> 
> I wouldn't have called that a typo.  I would take it to be shorthand
> for "if and only if".
> 
> Cheers,
> Michael.

Interesting.  I withdraw my patch request.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox