summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-28 09:01:33 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-28 09:01:33 -0800
commit201795a1b72570374e6d2c72d5c1c23c9cfc3929 (patch)
treed4b3fc9c1a49bec234bfe2a75863ca44214bd948
parent4d349ee5c7782f8b27f6cb550f112c5e26fff38d (diff)
parentd879ac6756b662a085a743e76023c768c3241579 (diff)
Merge tag 's390-7.0-3' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull s390 fixes from Vasily Gorbik: - Fix guest pfault init to pass a physical address to DIAG 0x258, restoring pfault interrupts and avoiding vCPU stalls during host page-in - Fix kexec/kdump hangs with stack protector by marking s390_reset_system() __no_stack_protector; set_prefix(0) switches lowcore and the canary no longer matches - Fix idle/vtime cputime accounting (idle-exit ordering, vtimer double-forwarding) and small cleanups * tag 's390-7.0-3' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: s390/pfault: Fix virtual vs physical address confusion s390/kexec: Disable stack protector in s390_reset_system() s390/idle: Remove psw_idle() prototype s390/vtime: Use lockdep_assert_irqs_disabled() instead of BUG_ON() s390/vtime: Use __this_cpu_read() / get rid of READ_ONCE() s390/irq/idle: Remove psw bits early s390/idle: Inline update_timer_idle() s390/idle: Slightly optimize idle time accounting s390/idle: Add comment for non obvious code s390/vtime: Fix virtual timer forwarding s390/idle: Fix cpu idle exit cpu time accounting
-rw-r--r--arch/s390/include/asm/idle.h4
-rw-r--r--arch/s390/include/asm/vtime.h34
-rw-r--r--arch/s390/kernel/entry.h2
-rw-r--r--arch/s390/kernel/idle.c25
-rw-r--r--arch/s390/kernel/ipl.c2
-rw-r--r--arch/s390/kernel/irq.c20
-rw-r--r--arch/s390/kernel/vtime.c42
-rw-r--r--arch/s390/mm/pfault.c4
8 files changed, 66 insertions, 67 deletions
diff --git a/arch/s390/include/asm/idle.h b/arch/s390/include/asm/idle.h
index 09f763b9eb40..32536ee34aa0 100644
--- a/arch/s390/include/asm/idle.h
+++ b/arch/s390/include/asm/idle.h
@@ -19,9 +19,9 @@ struct s390_idle_data {
unsigned long mt_cycles_enter[8];
};
+DECLARE_PER_CPU(struct s390_idle_data, s390_idle);
+
extern struct device_attribute dev_attr_idle_count;
extern struct device_attribute dev_attr_idle_time_us;
-void psw_idle(struct s390_idle_data *data, unsigned long psw_mask);
-
#endif /* _S390_IDLE_H */
diff --git a/arch/s390/include/asm/vtime.h b/arch/s390/include/asm/vtime.h
index 9d25fb35a042..b1db75d14e9d 100644
--- a/arch/s390/include/asm/vtime.h
+++ b/arch/s390/include/asm/vtime.h
@@ -2,6 +2,12 @@
#ifndef _S390_VTIME_H
#define _S390_VTIME_H
+#include <asm/lowcore.h>
+#include <asm/cpu_mf.h>
+#include <asm/idle.h>
+
+DECLARE_PER_CPU(u64, mt_cycles[8]);
+
static inline void update_timer_sys(void)
{
struct lowcore *lc = get_lowcore();
@@ -20,4 +26,32 @@ static inline void update_timer_mcck(void)
lc->last_update_timer = lc->mcck_enter_timer;
}
+static inline void update_timer_idle(void)
+{
+ struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);
+ struct lowcore *lc = get_lowcore();
+ u64 cycles_new[8];
+ int i, mtid;
+
+ mtid = smp_cpu_mtid;
+ if (mtid) {
+ stcctm(MT_DIAG, mtid, cycles_new);
+ for (i = 0; i < mtid; i++)
+ __this_cpu_add(mt_cycles[i], cycles_new[i] - idle->mt_cycles_enter[i]);
+ }
+ /*
+ * This is a bit subtle: Forward last_update_clock so it excludes idle
+ * time. For correct steal time calculation in do_account_vtime() add
+ * passed wall time before idle_enter to steal_timer:
+ * During the passed wall time before idle_enter CPU time may have
+ * been accounted to system, hardirq, softirq, etc. lowcore fields.
+ * The accounted CPU times will be subtracted again from steal_timer
+ * when accumulated steal time is calculated in do_account_vtime().
+ */
+ lc->steal_timer += idle->clock_idle_enter - lc->last_update_clock;
+ lc->last_update_clock = lc->int_clock;
+ lc->system_timer += lc->last_update_timer - idle->timer_idle_enter;
+ lc->last_update_timer = lc->sys_enter_timer;
+}
+
#endif /* _S390_VTIME_H */
diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h
index dd55cc6bbc28..fb67b4abe68c 100644
--- a/arch/s390/kernel/entry.h
+++ b/arch/s390/kernel/entry.h
@@ -56,8 +56,6 @@ long sys_s390_pci_mmio_write(unsigned long, const void __user *, size_t);
long sys_s390_pci_mmio_read(unsigned long, void __user *, size_t);
long sys_s390_sthyi(unsigned long function_code, void __user *buffer, u64 __user *return_code, unsigned long flags);
-DECLARE_PER_CPU(u64, mt_cycles[8]);
-
unsigned long stack_alloc(void);
void stack_free(unsigned long stack);
diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c
index 39cb8d0ae348..1f1b06b6b4ef 100644
--- a/arch/s390/kernel/idle.c
+++ b/arch/s390/kernel/idle.c
@@ -15,37 +15,22 @@
#include <trace/events/power.h>
#include <asm/cpu_mf.h>
#include <asm/cputime.h>
+#include <asm/idle.h>
#include <asm/nmi.h>
#include <asm/smp.h>
-#include "entry.h"
-static DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
+DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
void account_idle_time_irq(void)
{
struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);
- struct lowcore *lc = get_lowcore();
unsigned long idle_time;
- u64 cycles_new[8];
- int i;
- if (smp_cpu_mtid) {
- stcctm(MT_DIAG, smp_cpu_mtid, cycles_new);
- for (i = 0; i < smp_cpu_mtid; i++)
- this_cpu_add(mt_cycles[i], cycles_new[i] - idle->mt_cycles_enter[i]);
- }
-
- idle_time = lc->int_clock - idle->clock_idle_enter;
-
- lc->steal_timer += idle->clock_idle_enter - lc->last_update_clock;
- lc->last_update_clock = lc->int_clock;
-
- lc->system_timer += lc->last_update_timer - idle->timer_idle_enter;
- lc->last_update_timer = lc->sys_enter_timer;
+ idle_time = get_lowcore()->int_clock - idle->clock_idle_enter;
/* Account time spent with enabled wait psw loaded as idle time. */
- WRITE_ONCE(idle->idle_time, READ_ONCE(idle->idle_time) + idle_time);
- WRITE_ONCE(idle->idle_count, READ_ONCE(idle->idle_count) + 1);
+ __atomic64_add(idle_time, &idle->idle_time);
+ __atomic64_add_const(1, &idle->idle_count);
account_idle_time(cputime_to_nsecs(idle_time));
}
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
index dcdc7e274848..049c557c452f 100644
--- a/arch/s390/kernel/ipl.c
+++ b/arch/s390/kernel/ipl.c
@@ -2377,7 +2377,7 @@ void __init setup_ipl(void)
atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
}
-void s390_reset_system(void)
+void __no_stack_protector s390_reset_system(void)
{
/* Disable prefixing */
set_prefix(0);
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c
index f81723bc8856..7fdf960191d3 100644
--- a/arch/s390/kernel/irq.c
+++ b/arch/s390/kernel/irq.c
@@ -146,6 +146,12 @@ void noinstr do_io_irq(struct pt_regs *regs)
struct pt_regs *old_regs = set_irq_regs(regs);
bool from_idle;
+ from_idle = test_and_clear_cpu_flag(CIF_ENABLED_WAIT);
+ if (from_idle) {
+ update_timer_idle();
+ regs->psw.mask &= ~(PSW_MASK_EXT | PSW_MASK_IO | PSW_MASK_WAIT);
+ }
+
irq_enter_rcu();
if (user_mode(regs)) {
@@ -154,7 +160,6 @@ void noinstr do_io_irq(struct pt_regs *regs)
current->thread.last_break = regs->last_break;
}
- from_idle = test_and_clear_cpu_flag(CIF_ENABLED_WAIT);
if (from_idle)
account_idle_time_irq();
@@ -171,9 +176,6 @@ void noinstr do_io_irq(struct pt_regs *regs)
set_irq_regs(old_regs);
irqentry_exit(regs, state);
-
- if (from_idle)
- regs->psw.mask &= ~(PSW_MASK_EXT | PSW_MASK_IO | PSW_MASK_WAIT);
}
void noinstr do_ext_irq(struct pt_regs *regs)
@@ -182,6 +184,12 @@ void noinstr do_ext_irq(struct pt_regs *regs)
struct pt_regs *old_regs = set_irq_regs(regs);
bool from_idle;
+ from_idle = test_and_clear_cpu_flag(CIF_ENABLED_WAIT);
+ if (from_idle) {
+ update_timer_idle();
+ regs->psw.mask &= ~(PSW_MASK_EXT | PSW_MASK_IO | PSW_MASK_WAIT);
+ }
+
irq_enter_rcu();
if (user_mode(regs)) {
@@ -194,7 +202,6 @@ void noinstr do_ext_irq(struct pt_regs *regs)
regs->int_parm = get_lowcore()->ext_params;
regs->int_parm_long = get_lowcore()->ext_params2;
- from_idle = test_and_clear_cpu_flag(CIF_ENABLED_WAIT);
if (from_idle)
account_idle_time_irq();
@@ -203,9 +210,6 @@ void noinstr do_ext_irq(struct pt_regs *regs)
irq_exit_rcu();
set_irq_regs(old_regs);
irqentry_exit(regs, state);
-
- if (from_idle)
- regs->psw.mask &= ~(PSW_MASK_EXT | PSW_MASK_IO | PSW_MASK_WAIT);
}
static void show_msi_interrupt(struct seq_file *p, int irq)
diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c
index 234a0ba30510..bf48744d0912 100644
--- a/arch/s390/kernel/vtime.c
+++ b/arch/s390/kernel/vtime.c
@@ -48,8 +48,7 @@ static inline void set_vtimer(u64 expires)
static inline int virt_timer_forward(u64 elapsed)
{
- BUG_ON(!irqs_disabled());
-
+ lockdep_assert_irqs_disabled();
if (list_empty(&virt_timer_list))
return 0;
elapsed = atomic64_add_return(elapsed, &virt_timer_elapsed);
@@ -137,23 +136,16 @@ static int do_account_vtime(struct task_struct *tsk)
lc->system_timer += timer;
/* Update MT utilization calculation */
- if (smp_cpu_mtid &&
- time_after64(jiffies_64, this_cpu_read(mt_scaling_jiffies)))
+ if (smp_cpu_mtid && time_after64(jiffies_64, __this_cpu_read(mt_scaling_jiffies)))
update_mt_scaling();
/* Calculate cputime delta */
- user = update_tsk_timer(&tsk->thread.user_timer,
- READ_ONCE(lc->user_timer));
- guest = update_tsk_timer(&tsk->thread.guest_timer,
- READ_ONCE(lc->guest_timer));
- system = update_tsk_timer(&tsk->thread.system_timer,
- READ_ONCE(lc->system_timer));
- hardirq = update_tsk_timer(&tsk->thread.hardirq_timer,
- READ_ONCE(lc->hardirq_timer));
- softirq = update_tsk_timer(&tsk->thread.softirq_timer,
- READ_ONCE(lc->softirq_timer));
- lc->steal_timer +=
- clock - user - guest - system - hardirq - softirq;
+ user = update_tsk_timer(&tsk->thread.user_timer, lc->user_timer);
+ guest = update_tsk_timer(&tsk->thread.guest_timer, lc->guest_timer);
+ system = update_tsk_timer(&tsk->thread.system_timer, lc->system_timer);
+ hardirq = update_tsk_timer(&tsk->thread.hardirq_timer, lc->hardirq_timer);
+ softirq = update_tsk_timer(&tsk->thread.softirq_timer, lc->softirq_timer);
+ lc->steal_timer += clock - user - guest - system - hardirq - softirq;
/* Push account value */
if (user) {
@@ -225,10 +217,6 @@ static u64 vtime_delta(void)
return timer - lc->last_update_timer;
}
-/*
- * Update process times based on virtual cpu times stored by entry.S
- * to the lowcore fields user_timer, system_timer & steal_clock.
- */
void vtime_account_kernel(struct task_struct *tsk)
{
struct lowcore *lc = get_lowcore();
@@ -238,27 +226,17 @@ void vtime_account_kernel(struct task_struct *tsk)
lc->guest_timer += delta;
else
lc->system_timer += delta;
-
- virt_timer_forward(delta);
}
EXPORT_SYMBOL_GPL(vtime_account_kernel);
void vtime_account_softirq(struct task_struct *tsk)
{
- u64 delta = vtime_delta();
-
- get_lowcore()->softirq_timer += delta;
-
- virt_timer_forward(delta);
+ get_lowcore()->softirq_timer += vtime_delta();
}
void vtime_account_hardirq(struct task_struct *tsk)
{
- u64 delta = vtime_delta();
-
- get_lowcore()->hardirq_timer += delta;
-
- virt_timer_forward(delta);
+ get_lowcore()->hardirq_timer += vtime_delta();
}
/*
diff --git a/arch/s390/mm/pfault.c b/arch/s390/mm/pfault.c
index 2f829448c719..6ecd6b0a22a8 100644
--- a/arch/s390/mm/pfault.c
+++ b/arch/s390/mm/pfault.c
@@ -62,7 +62,7 @@ int __pfault_init(void)
"0: nopr %%r7\n"
EX_TABLE(0b, 0b)
: [rc] "+d" (rc)
- : [refbk] "a" (&pfault_init_refbk), "m" (pfault_init_refbk)
+ : [refbk] "a" (virt_to_phys(&pfault_init_refbk)), "m" (pfault_init_refbk)
: "cc");
return rc;
}
@@ -84,7 +84,7 @@ void __pfault_fini(void)
"0: nopr %%r7\n"
EX_TABLE(0b, 0b)
:
- : [refbk] "a" (&pfault_fini_refbk), "m" (pfault_fini_refbk)
+ : [refbk] "a" (virt_to_phys(&pfault_fini_refbk)), "m" (pfault_fini_refbk)
: "cc");
}