Hi Linus, please pull some more small updates for s390. This is mainly to decouple udelay() and arch_cpu_idle() and simplify both of them, like I brought it up after the lockdep breakage in 5.10-rc6. Thanks, Heiko The following changes since commit 586592478b1fa8bb8cd6875a9191468e9b1a8b13: Merge tag 's390-5.11-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux (2020-12-14 16:22:26 -0800) are available in the Git repository at: git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux.git tags/s390-5.11-2 for you to fetch changes up to dfdc6e73cdcf011a04568231132916c6d06b861f: s390/zcrypt: convert comma to semicolon (2020-12-16 14:55:50 +0100) ---------------------------------------------------------------- - Always initialize kernel stack backchain when entering the kernel, so that unwinding works properly. - Fix stack unwinder test case to avoid rare interrupt stack corruption. - Simplify udelay() and just let it busy loop instead of implementing a complex logic. - arch_cpu_idle() cleanup. - Some other minor improvements. ---------------------------------------------------------------- Heiko Carstens (10): s390: always clear kernel stack backchain before calling functions s390: make calls to TRACE_IRQS_OFF/TRACE_IRQS_ON balanced s390/test_unwind: fix CALL_ON_STACK tests s390/test_unwind: use timer instead of udelay s390/delay: simplify udelay s390/irq: select HAVE_IRQ_EXIT_ON_IRQ_STACK s390/delay: remove udelay_simple() s390/idle: merge enabled_wait() and arch_cpu_idle() s390/idle: remove raw_local_irq_save()/restore() from arch_cpu_idle() s390/idle: allow arch_cpu_idle() to be kprobed Zheng Yongjun (1): s390/zcrypt: convert comma to semicolon arch/s390/Kconfig | 1 + arch/s390/include/asm/delay.h | 12 ++--- arch/s390/include/asm/processor.h | 7 --- arch/s390/kernel/entry.S | 16 +++--- arch/s390/kernel/idle.c | 18 ++----- arch/s390/kernel/ipl.c | 2 +- arch/s390/kernel/setup.c | 1 - arch/s390/lib/delay.c | 105 ++++--------------------------------- arch/s390/lib/test_unwind.c | 31 ++++++----- drivers/s390/cio/device.c | 2 +- drivers/s390/crypto/zcrypt_cex2a.c | 2 +- drivers/s390/crypto/zcrypt_cex4.c | 2 +- 12 files changed, 44 insertions(+), 155 deletions(-) diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index a60cc523d810..9e8895cb9ee7 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -153,6 +153,7 @@ config S390 select HAVE_FUTEX_CMPXCHG if FUTEX select HAVE_GCC_PLUGINS select HAVE_GENERIC_VDSO + select HAVE_IRQ_EXIT_ON_IRQ_STACK select HAVE_KERNEL_BZIP2 select HAVE_KERNEL_GZIP select HAVE_KERNEL_LZ4 diff --git a/arch/s390/include/asm/delay.h b/arch/s390/include/asm/delay.h index 4a08379cd1eb..21a8fe18fe66 100644 --- a/arch/s390/include/asm/delay.h +++ b/arch/s390/include/asm/delay.h @@ -13,14 +13,12 @@ #ifndef _S390_DELAY_H #define _S390_DELAY_H -void udelay_enable(void); -void __ndelay(unsigned long long nsecs); -void __udelay(unsigned long long usecs); -void udelay_simple(unsigned long long usecs); +void __ndelay(unsigned long nsecs); +void __udelay(unsigned long usecs); void __delay(unsigned long loops); -#define ndelay(n) __ndelay((unsigned long long) (n)) -#define udelay(n) __udelay((unsigned long long) (n)) -#define mdelay(n) __udelay((unsigned long long) (n) * 1000) +#define ndelay(n) __ndelay((unsigned long)(n)) +#define udelay(n) __udelay((unsigned long)(n)) +#define mdelay(n) __udelay((unsigned long)(n) * 1000) #endif /* defined(_S390_DELAY_H) */ diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index 6b7269f51f83..2058a435add4 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -16,14 +16,12 @@ #define CIF_NOHZ_DELAY 2 /* delay HZ disable for a tick */ #define CIF_FPU 3 /* restore FPU registers */ -#define CIF_IGNORE_IRQ 4 /* ignore interrupt (for udelay) */ #define CIF_ENABLED_WAIT 5 /* in enabled wait state */ #define CIF_MCCK_GUEST 6 /* machine check happening in guest */ #define CIF_DEDICATED_CPU 7 /* this CPU is dedicated */ #define _CIF_NOHZ_DELAY BIT(CIF_NOHZ_DELAY) #define _CIF_FPU BIT(CIF_FPU) -#define _CIF_IGNORE_IRQ BIT(CIF_IGNORE_IRQ) #define _CIF_ENABLED_WAIT BIT(CIF_ENABLED_WAIT) #define _CIF_MCCK_GUEST BIT(CIF_MCCK_GUEST) #define _CIF_DEDICATED_CPU BIT(CIF_DEDICATED_CPU) @@ -292,11 +290,6 @@ static inline unsigned long __rewind_psw(psw_t psw, unsigned long ilc) return (psw.addr - ilc) & mask; } -/* - * Function to stop a processor until the next interrupt occurs - */ -void enabled_wait(void); - /* * Function to drop a processor into disabled wait state */ diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 8bb9ebb71c4b..87e83164c330 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -413,6 +413,7 @@ ENTRY(system_call) mvc __PT_PSW(16,%r11),__LC_SVC_OLD_PSW mvc __PT_INT_CODE(4,%r11),__LC_SVC_ILC stg %r14,__PT_FLAGS(%r11) + xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) ENABLE_INTS .Lsysc_do_svc: # clear user controlled register to prevent speculative use @@ -429,7 +430,6 @@ ENTRY(system_call) jnl .Lsysc_nr_ok slag %r8,%r1,3 .Lsysc_nr_ok: - xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) stg %r2,__PT_ORIG_GPR2(%r11) stg %r7,STACK_FRAME_OVERHEAD(%r15) lg %r9,0(%r8,%r10) # get system call add. @@ -698,8 +698,8 @@ ENTRY(pgm_check_handler) mvc __THREAD_per_address(8,%r14),__LC_PER_ADDRESS mvc __THREAD_per_cause(2,%r14),__LC_PER_CODE mvc __THREAD_per_paid(1,%r14),__LC_PER_ACCESS_ID -6: RESTORE_SM_CLEAR_PER - xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) +6: xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) + RESTORE_SM_CLEAR_PER larl %r1,pgm_check_table llgh %r10,__PT_INT_CODE+2(%r11) nill %r10,0x007f @@ -730,8 +730,8 @@ ENTRY(pgm_check_handler) # PER event in supervisor state, must be kprobes # .Lpgm_kprobe: - RESTORE_SM_CLEAR_PER xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) + RESTORE_SM_CLEAR_PER lgr %r2,%r11 # pass pointer to pt_regs brasl %r14,do_per_trap j .Lpgm_return @@ -777,10 +777,8 @@ ENTRY(io_int_handler) .Lio_skip_asce: mvc __PT_INT_CODE(12,%r11),__LC_SUBCHANNEL_ID xc __PT_FLAGS(8,%r11),__PT_FLAGS(%r11) - TSTMSK __LC_CPU_FLAGS,_CIF_IGNORE_IRQ - jo .Lio_restore - TRACE_IRQS_OFF xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) + TRACE_IRQS_OFF .Lio_loop: lgr %r2,%r11 # pass pointer to pt_regs lghi %r3,IO_INTERRUPT @@ -965,10 +963,8 @@ ENTRY(ext_int_handler) mvc __PT_INT_PARM(4,%r11),__LC_EXT_PARAMS mvc __PT_INT_PARM_LONG(8,%r11),0(%r1) xc __PT_FLAGS(8,%r11),__PT_FLAGS(%r11) - TSTMSK __LC_CPU_FLAGS,_CIF_IGNORE_IRQ - jo .Lio_restore - TRACE_IRQS_OFF xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) + TRACE_IRQS_OFF lgr %r2,%r11 # pass pointer to pt_regs lghi %r3,EXT_INTERRUPT brasl %r14,do_IRQ diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c index 2b85096964f8..a5d4d80d6ede 100644 --- a/arch/s390/kernel/idle.c +++ b/arch/s390/kernel/idle.c @@ -9,7 +9,6 @@ #include <linux/kernel.h> #include <linux/kernel_stat.h> -#include <linux/kprobes.h> #include <linux/notifier.h> #include <linux/init.h> #include <linux/cpu.h> @@ -21,22 +20,19 @@ static DEFINE_PER_CPU(struct s390_idle_data, s390_idle); -void enabled_wait(void) +void arch_cpu_idle(void) { struct s390_idle_data *idle = this_cpu_ptr(&s390_idle); unsigned long long idle_time; - unsigned long psw_mask, flags; - + unsigned long psw_mask; /* Wait for external, I/O or machine check interrupt. */ psw_mask = PSW_KERNEL_BITS | PSW_MASK_WAIT | PSW_MASK_DAT | PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; clear_cpu_flag(CIF_NOHZ_DELAY); - raw_local_irq_save(flags); - /* Call the assembler magic in entry.S */ + /* psw_idle() returns with interrupts disabled. */ psw_idle(idle, psw_mask); - raw_local_irq_restore(flags); /* Account time spent with enabled wait psw loaded as idle time. */ raw_write_seqcount_begin(&idle->seqcount); @@ -46,8 +42,8 @@ void enabled_wait(void) idle->idle_count++; account_idle_time(cputime_to_nsecs(idle_time)); raw_write_seqcount_end(&idle->seqcount); + raw_local_irq_enable(); } -NOKPROBE_SYMBOL(enabled_wait); static ssize_t show_idle_count(struct device *dev, struct device_attribute *attr, char *buf) @@ -120,12 +116,6 @@ void arch_cpu_idle_enter(void) { } -void arch_cpu_idle(void) -{ - enabled_wait(); - raw_local_irq_enable(); -} - void arch_cpu_idle_exit(void) { } diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 98b3aca1de8e..7a21eca498aa 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -1512,7 +1512,7 @@ static void diag308_dump(void *dump_block) while (1) { if (diag308(DIAG308_LOAD_NORMAL_DUMP, NULL) != 0x302) break; - udelay_simple(USEC_PER_SEC); + udelay(USEC_PER_SEC); } } diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 1f16a03be995..1fbed91c73bc 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -335,7 +335,6 @@ int __init arch_early_irq_init(void) if (!stack) panic("Couldn't allocate async stack"); S390_lowcore.async_stack = stack + STACK_INIT_OFFSET; - udelay_enable(); return 0; } diff --git a/arch/s390/lib/delay.c b/arch/s390/lib/delay.c index 68d61f2835df..f289afeb3f31 100644 --- a/arch/s390/lib/delay.c +++ b/arch/s390/lib/delay.c @@ -19,13 +19,6 @@ #include <asm/div64.h> #include <asm/idle.h> -static DEFINE_STATIC_KEY_FALSE(udelay_ready); - -void __init udelay_enable(void) -{ - static_branch_enable(&udelay_ready); -} - void __delay(unsigned long loops) { /* @@ -39,105 +32,25 @@ void __delay(unsigned long loops) } EXPORT_SYMBOL(__delay); -static void __udelay_disabled(unsigned long long usecs) +static void delay_loop(unsigned long delta) { - unsigned long cr0, cr0_new, psw_mask; - struct s390_idle_data idle; - u64 end; + unsigned long end; - end = get_tod_clock() + (usecs << 12); - __ctl_store(cr0, 0, 0); - cr0_new = cr0 & ~CR0_IRQ_SUBCLASS_MASK; - cr0_new |= (1UL << (63 - 52)); /* enable clock comparator irq */ - __ctl_load(cr0_new, 0, 0); - psw_mask = __extract_psw() | PSW_MASK_EXT | PSW_MASK_WAIT; - set_clock_comparator(end); - set_cpu_flag(CIF_IGNORE_IRQ); - psw_idle(&idle, psw_mask); - trace_hardirqs_off(); - clear_cpu_flag(CIF_IGNORE_IRQ); - set_clock_comparator(S390_lowcore.clock_comparator); - __ctl_load(cr0, 0, 0); + end = get_tod_clock_monotonic() + delta; + while (!tod_after(get_tod_clock_monotonic(), end)) + cpu_relax(); } -static void __udelay_enabled(unsigned long long usecs) +void __udelay(unsigned long usecs) { - u64 clock_saved, end; - - end = get_tod_clock_fast() + (usecs << 12); - do { - clock_saved = 0; - if (tod_after(S390_lowcore.clock_comparator, end)) { - clock_saved = local_tick_disable(); - set_clock_comparator(end); - } - enabled_wait(); - if (clock_saved) - local_tick_enable(clock_saved); - } while (get_tod_clock_fast() < end); -} - -/* - * Waits for 'usecs' microseconds using the TOD clock comparator. - */ -void __udelay(unsigned long long usecs) -{ - unsigned long flags; - - if (!static_branch_likely(&udelay_ready)) { - udelay_simple(usecs); - return; - } - - preempt_disable(); - local_irq_save(flags); - if (in_irq()) { - __udelay_disabled(usecs); - goto out; - } - if (in_softirq()) { - if (raw_irqs_disabled_flags(flags)) - __udelay_disabled(usecs); - else - __udelay_enabled(usecs); - goto out; - } - if (raw_irqs_disabled_flags(flags)) { - local_bh_disable(); - __udelay_disabled(usecs); - _local_bh_enable(); - goto out; - } - __udelay_enabled(usecs); -out: - local_irq_restore(flags); - preempt_enable(); + delay_loop(usecs << 12); } EXPORT_SYMBOL(__udelay); -/* - * Simple udelay variant. To be used on startup and reboot - * when the interrupt handler isn't working. - */ -void udelay_simple(unsigned long long usecs) -{ - u64 end; - - end = get_tod_clock_fast() + (usecs << 12); - while (get_tod_clock_fast() < end) - cpu_relax(); -} - -void __ndelay(unsigned long long nsecs) +void __ndelay(unsigned long nsecs) { - u64 end; - nsecs <<= 9; do_div(nsecs, 125); - end = get_tod_clock_fast() + nsecs; - if (nsecs & ~0xfffUL) - __udelay(nsecs >> 12); - while (get_tod_clock_fast() < end) - barrier(); + delay_loop(nsecs); } EXPORT_SYMBOL(__ndelay); diff --git a/arch/s390/lib/test_unwind.c b/arch/s390/lib/test_unwind.c index 7c988994931f..dcd8946255be 100644 --- a/arch/s390/lib/test_unwind.c +++ b/arch/s390/lib/test_unwind.c @@ -9,12 +9,12 @@ #include <linux/kallsyms.h> #include <linux/kthread.h> #include <linux/module.h> +#include <linux/timer.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/kprobes.h> #include <linux/wait.h> #include <asm/irq.h> -#include <asm/delay.h> #define BT_BUF_SIZE (PAGE_SIZE * 4) @@ -205,12 +205,15 @@ static noinline int unwindme_func3(struct unwindme *u) /* This function must appear in the backtrace. */ static noinline int unwindme_func2(struct unwindme *u) { + unsigned long flags; int rc; if (u->flags & UWM_SWITCH_STACK) { - preempt_disable(); + local_irq_save(flags); + local_mcck_disable(); rc = CALL_ON_STACK(unwindme_func3, S390_lowcore.nodat_stack, 1, u); - preempt_enable(); + local_mcck_enable(); + local_irq_restore(flags); return rc; } else { return unwindme_func3(u); @@ -223,31 +226,27 @@ static noinline int unwindme_func1(void *u) return unwindme_func2((struct unwindme *)u); } -static void unwindme_irq_handler(struct ext_code ext_code, - unsigned int param32, - unsigned long param64) +static void unwindme_timer_fn(struct timer_list *unused) { struct unwindme *u = READ_ONCE(unwindme); - if (u && u->task == current) { + if (u) { unwindme = NULL; u->task = NULL; u->ret = unwindme_func1(u); + complete(&u->task_ready); } } +static struct timer_list unwind_timer; + static int test_unwind_irq(struct unwindme *u) { - preempt_disable(); - if (register_external_irq(EXT_IRQ_CLK_COMP, unwindme_irq_handler)) { - pr_info("Couldn't register external interrupt handler"); - return -1; - } - u->task = current; unwindme = u; - udelay(1); - unregister_external_irq(EXT_IRQ_CLK_COMP, unwindme_irq_handler); - preempt_enable(); + init_completion(&u->task_ready); + timer_setup(&unwind_timer, unwindme_timer_fn, 0); + mod_timer(&unwind_timer, jiffies + 1); + wait_for_completion(&u->task_ready); return u->ret; } diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e0005a4fc978..9e3c8b4e7fa8 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1668,7 +1668,7 @@ void ccw_device_wait_idle(struct ccw_device *cdev) cio_tsch(sch); if (sch->schib.scsw.cmd.actl == 0) break; - udelay_simple(100); + udelay(100); } } #endif diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c index 226a5612e855..62ceeb7fc125 100644 --- a/drivers/s390/crypto/zcrypt_cex2a.c +++ b/drivers/s390/crypto/zcrypt_cex2a.c @@ -175,7 +175,7 @@ static int zcrypt_cex2a_queue_probe(struct ap_device *ap_dev) atomic_set(&zq->load, 0); ap_queue_init_state(aq); ap_queue_init_reply(aq, &zq->reply); - aq->request_timeout = CEX2A_CLEANUP_TIME, + aq->request_timeout = CEX2A_CLEANUP_TIME; aq->private = zq; rc = zcrypt_queue_register(zq); if (rc) { diff --git a/drivers/s390/crypto/zcrypt_cex4.c b/drivers/s390/crypto/zcrypt_cex4.c index f5195bca1d85..f4a6d3744241 100644 --- a/drivers/s390/crypto/zcrypt_cex4.c +++ b/drivers/s390/crypto/zcrypt_cex4.c @@ -631,7 +631,7 @@ static int zcrypt_cex4_queue_probe(struct ap_device *ap_dev) atomic_set(&zq->load, 0); ap_queue_init_state(aq); ap_queue_init_reply(aq, &zq->reply); - aq->request_timeout = CEX4_CLEANUP_TIME, + aq->request_timeout = CEX4_CLEANUP_TIME; aq->private = zq; rc = zcrypt_queue_register(zq); if (rc) {