diff options
| author | Mark Brown <broonie@kernel.org> | 2026-08-21 13:56:38 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-08-21 13:56:38 +0100 |
| commit | d2cac287140d890b72b3a82a643b25465ba60f9a (patch) | |
| tree | a96dcafdbc9c57d89a375032f8b8af52f297c0f5 | |
| parent | 3b03372426b41970eca731093e8cccf3d3aab784 (diff) | |
| parent | 53158917ea1a0219f6886a0ae10f6f276ecfa2a6 (diff) | |
| download | linux-next-d2cac287140d890b72b3a82a643b25465ba60f9a.tar.gz linux-next-d2cac287140d890b72b3a82a643b25465ba60f9a.zip | |
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/s390/linux.git
# Conflicts:
# arch/s390/mm/init.c
83 files changed, 1147 insertions, 534 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 4c705f401de0..b141e6e7e92c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6857,6 +6857,13 @@ L: linux-riscv@lists.infradead.org S: Maintained F: drivers/cpuidle/cpuidle-riscv-sbi.c +CPUIDLE DRIVER - S390 +M: Mete Durlu <meted@linux.ibm.com> +L: linux-pm@vger.kernel.org +L: linux-s390@vger.kernel.org +S: Maintained +F: drivers/cpuidle/cpuidle-s390.c + CPUMASK API [RUST] M: Viresh Kumar <viresh.kumar@linaro.org> R: Yury Norov <yury.norov@gmail.com> @@ -23906,6 +23913,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux.git F: Documentation/driver-api/s390-drivers.rst F: Documentation/arch/s390/ F: arch/s390/ +F: drivers/cpuidle/cpuidle-s390.c F: drivers/s390/ F: drivers/watchdog/diag288_wdt.c diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 7ea7b549edf0..4b51bc6e8948 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -20,6 +20,9 @@ config ARCH_HAS_ILOG2_U64 config ARCH_PROC_KCORE_TEXT def_bool y +config ARCH_HAS_CPU_RELAX + def_bool y + config GENERIC_HWEIGHT def_bool !HAVE_MARCH_Z196_FEATURES @@ -145,6 +148,7 @@ config S390 select ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE select ARCH_STACKWALK select ARCH_SUPPORTS_ATOMIC_RMW + select ARCH_SUPPORTS_CFI select ARCH_SUPPORTS_DEBUG_PAGEALLOC select ARCH_SUPPORTS_HUGETLBFS select ARCH_SUPPORTS_INT128 if CC_HAS_INT128 && CC_IS_CLANG @@ -153,6 +157,7 @@ config S390 select ARCH_SUPPORTS_NUMA_BALANCING select ARCH_SUPPORTS_PAGE_TABLE_CHECK select ARCH_SUPPORTS_PER_VMA_LOCK + select ARCH_USES_CFI_GENERIC_LLVM_PASS if CC_IS_CLANG select ARCH_USE_BUILTIN_BSWAP select ARCH_USE_CMPXCHG_LOCKREF select ARCH_USE_SYM_ANNOTATIONS @@ -165,6 +170,7 @@ config S390 select ARCH_WANTS_THP_SWAP select BUILDTIME_TABLE_SORT select CLONE_BACKWARDS2 + select DCACHE_WORD_ACCESS if !KMSAN select DYNAMIC_FTRACE if FUNCTION_TRACER select FUNCTION_ALIGNMENT_8B if CC_IS_GCC select FUNCTION_ALIGNMENT_16B if !CC_IS_GCC @@ -277,6 +283,8 @@ config S390 select HAS_SEPARATE_PREEMPT_RESCHED_BITS select SPARSE_IRQ select SWIOTLB + select SWIOTLB_DYNAMIC + select DMA_COHERENT_POOL select SYSCTL_EXCEPTION_TRACE select SYSTEM_DATA_VERIFICATION if KEXEC_SIG select THREAD_INFO_IN_TASK @@ -707,6 +715,8 @@ config KERNEL_IMAGE_BASE endmenu +source "drivers/cpuidle/Kconfig" + menu "Memory setup" config ARCH_SPARSEMEM_ENABLE diff --git a/arch/s390/appldata/Makefile b/arch/s390/appldata/Makefile index b06def4a4f2f..132381f16fb1 100644 --- a/arch/s390/appldata/Makefile +++ b/arch/s390/appldata/Makefile @@ -3,6 +3,8 @@ # Makefile for the Linux - z/VM Monitor Stream. # +CONTEXT_ANALYSIS := y + obj-$(CONFIG_APPLDATA_BASE) += appldata_base.o obj-$(CONFIG_APPLDATA_MEM) += appldata_mem.o obj-$(CONFIG_APPLDATA_OS) += appldata_os.o diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig index 54637be87fb7..3dae71474333 100644 --- a/arch/s390/configs/debug_defconfig +++ b/arch/s390/configs/debug_defconfig @@ -29,6 +29,8 @@ CONFIG_CGROUP_PIDS=y CONFIG_CGROUP_RDMA=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_HUGETLB=y +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_TEO=y CONFIG_CPUSETS=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y @@ -867,7 +869,7 @@ CONFIG_DEBUG_PREEMPT=y CONFIG_PROVE_LOCKING=y CONFIG_LOCK_STAT=y CONFIG_LOCKDEP_BITS=16 -CONFIG_LOCKDEP_CHAINS_BITS=17 +CONFIG_LOCKDEP_CHAINS_BITS=18 CONFIG_DEBUG_ATOMIC_SLEEP=y CONFIG_DEBUG_LOCKING_API_SELFTESTS=y CONFIG_DEBUG_IRQFLAGS=y diff --git a/arch/s390/configs/defconfig b/arch/s390/configs/defconfig index 5f5114a253cf..6f5722634b4d 100644 --- a/arch/s390/configs/defconfig +++ b/arch/s390/configs/defconfig @@ -27,6 +27,8 @@ CONFIG_CGROUP_PIDS=y CONFIG_CGROUP_RDMA=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_HUGETLB=y +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_TEO=y CONFIG_CPUSETS=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y diff --git a/arch/s390/crypto/paes_s390.c b/arch/s390/crypto/paes_s390.c index 8cfe6166c193..973436592318 100644 --- a/arch/s390/crypto/paes_s390.c +++ b/arch/s390/crypto/paes_s390.c @@ -548,16 +548,7 @@ static int ecb_paes_do_one_request(struct crypto_engine *engine, void *areq) rc = ecb_paes_do_crypt(ctx, req_ctx, tested, true); if (rc == -EKEYEXPIRED) { - /* - * Protected key expired, conversion is in process. - * Trigger a re-schedule of this request by returning - * -ENOSPC ("hardware queue is full") to the crypto engine. - * To avoid immediately re-invocation of this callback, - * tell the scheduler to voluntarily give up the CPU here. - */ - cond_resched(); - pr_debug("rescheduling request\n"); - return -ENOSPC; + return pkey_handle_expired(); } else if (rc) { skcipher_walk_done(walk, rc); } @@ -814,16 +805,7 @@ static int cbc_paes_do_one_request(struct crypto_engine *engine, void *areq) rc = cbc_paes_do_crypt(ctx, req_ctx, tested, true); if (rc == -EKEYEXPIRED) { - /* - * Protected key expired, conversion is in process. - * Trigger a re-schedule of this request by returning - * -ENOSPC ("hardware queue is full") to the crypto engine. - * To avoid immediately re-invocation of this callback, - * tell the scheduler to voluntarily give up the CPU here. - */ - cond_resched(); - pr_debug("rescheduling request\n"); - return -ENOSPC; + return pkey_handle_expired(); } else if (rc) { skcipher_walk_done(walk, rc); } @@ -1122,16 +1104,7 @@ static int ctr_paes_do_one_request(struct crypto_engine *engine, void *areq) rc = ctr_paes_do_crypt(ctx, req_ctx, tested, true); if (rc == -EKEYEXPIRED) { - /* - * Protected key expired, conversion is in process. - * Trigger a re-schedule of this request by returning - * -ENOSPC ("hardware queue is full") to the crypto engine. - * To avoid immediately re-invocation of this callback, - * tell the scheduler to voluntarily give up the CPU here. - */ - cond_resched(); - pr_debug("rescheduling request\n"); - return -ENOSPC; + return pkey_handle_expired(); } else if (rc) { skcipher_walk_done(walk, rc); } @@ -1565,16 +1538,7 @@ static int xts_paes_do_one_request(struct crypto_engine *engine, void *areq) rc = xts_paes_do_crypt(ctx, req_ctx, tested, true); if (rc == -EKEYEXPIRED) { - /* - * Protected key expired, conversion is in process. - * Trigger a re-schedule of this request by returning - * -ENOSPC ("hardware queue is full") to the crypto engine. - * To avoid immediately re-invocation of this callback, - * tell the scheduler to voluntarily give up the CPU here. - */ - cond_resched(); - pr_debug("rescheduling request\n"); - return -ENOSPC; + return pkey_handle_expired(); } else if (rc) { skcipher_walk_done(walk, rc); } diff --git a/arch/s390/crypto/phmac_s390.c b/arch/s390/crypto/phmac_s390.c index 03ca33ffe6cc..020a1beb2e22 100644 --- a/arch/s390/crypto/phmac_s390.c +++ b/arch/s390/crypto/phmac_s390.c @@ -887,16 +887,7 @@ static int phmac_do_one_request(struct crypto_engine *engine, void *areq) case OP_FINUP: rc = phmac_kmac_update(req, true); if (rc == -EKEYEXPIRED) { - /* - * Protected key expired, conversion is in process. - * Trigger a re-schedule of this request by returning - * -ENOSPC ("hardware queue full") to the crypto engine. - * To avoid immediately re-invocation of this callback, - * tell scheduler to voluntarily give up the CPU here. - */ - pr_debug("rescheduling request\n"); - cond_resched(); - return -ENOSPC; + return pkey_handle_expired(); } else if (rc) { hwh_advance(hwh, rc); goto out; @@ -907,18 +898,8 @@ static int phmac_do_one_request(struct crypto_engine *engine, void *areq) fallthrough; case OP_FINAL: rc = phmac_kmac_final(req, true); - if (rc == -EKEYEXPIRED) { - /* - * Protected key expired, conversion is in process. - * Trigger a re-schedule of this request by returning - * -ENOSPC ("hardware queue full") to the crypto engine. - * To avoid immediately re-invocation of this callback, - * tell scheduler to voluntarily give up the CPU here. - */ - pr_debug("rescheduling request\n"); - cond_resched(); - return -ENOSPC; - } + if (rc == -EKEYEXPIRED) + return pkey_handle_expired(); break; default: /* unknown/unsupported/unimplemented asynch op */ diff --git a/arch/s390/hypfs/Makefile b/arch/s390/hypfs/Makefile index c34854d298f8..5a3daeafe926 100644 --- a/arch/s390/hypfs/Makefile +++ b/arch/s390/hypfs/Makefile @@ -3,6 +3,8 @@ # Makefile for the linux hypfs filesystem routines. # +CONTEXT_ANALYSIS := y + obj-$(CONFIG_S390_HYPFS) += hypfs_dbfs.o obj-$(CONFIG_S390_HYPFS) += hypfs_diag.o obj-$(CONFIG_S390_HYPFS) += hypfs_diag0c.o diff --git a/arch/s390/include/asm/ap.h b/arch/s390/include/asm/ap.h index 3b95c6531a67..c91b6ace199d 100644 --- a/arch/s390/include/asm/ap.h +++ b/arch/s390/include/asm/ap.h @@ -125,8 +125,8 @@ struct ap_tapq_hwinfo { unsigned int nd : 8; /* nr of domains */ unsigned int : 4; unsigned int ml : 4; /* apxl ml */ - unsigned int : 4; - unsigned int qd : 4; /* queue depth */ + unsigned int : 3; + unsigned int qd : 5; /* queue depth */ }; }; }; diff --git a/arch/s390/include/asm/asm-extable.h b/arch/s390/include/asm/asm-extable.h index 99748c20e767..d23ea0c94e4e 100644 --- a/arch/s390/include/asm/asm-extable.h +++ b/arch/s390/include/asm/asm-extable.h @@ -12,6 +12,7 @@ #define EX_TYPE_UA_FAULT 3 #define EX_TYPE_UA_LOAD_REG 5 #define EX_TYPE_UA_LOAD_REGPAIR 6 +#define EX_TYPE_ZEROPAD 7 #define EX_TYPE_FPC 8 #define EX_TYPE_UA_MVCOS_TO 9 #define EX_TYPE_UA_MVCOS_FROM 10 @@ -79,6 +80,9 @@ #define EX_TABLE_UA_LOAD_REGPAIR(_fault, _target, _regerr, _regzero) \ __EX_TABLE(__ex_table, _fault, _target, EX_TYPE_UA_LOAD_REGPAIR, _regerr, _regzero, 0) +#define EX_TABLE_ZEROPAD(_fault, _target, _regdata, _regaddr) \ + __EX_TABLE(__ex_table, _fault, _target, EX_TYPE_ZEROPAD, _regdata, _regaddr, 0) + #define EX_TABLE_FPC(_fault, _target) \ __EX_TABLE(__ex_table, _fault, _target, EX_TYPE_FPC, __stringify(%%r0), __stringify(%%r0), 0) diff --git a/arch/s390/include/asm/cfi.h b/arch/s390/include/asm/cfi.h new file mode 100644 index 000000000000..9af2c7cb70ca --- /dev/null +++ b/arch/s390/include/asm/cfi.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_S390_CFI_H +#define _ASM_S390_CFI_H + +#define __bpfcall + +#endif /* _ASM_S390_CFI_H */ diff --git a/arch/s390/include/asm/hugetlb.h b/arch/s390/include/asm/hugetlb.h index 02821a7d9d91..aea754b67c89 100644 --- a/arch/s390/include/asm/hugetlb.h +++ b/arch/s390/include/asm/hugetlb.h @@ -42,9 +42,9 @@ static inline void huge_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep, unsigned long sz) { if ((pte_val(ptep_get(ptep)) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) - set_pte(ptep, __pte(_REGION3_ENTRY_EMPTY)); + set_pud((pud_t *)ptep, __pud(_REGION3_ENTRY_EMPTY)); else - set_pte(ptep, __pte(_SEGMENT_ENTRY_EMPTY)); + set_pmd((pmd_t *)ptep, __pmd(_SEGMENT_ENTRY_EMPTY)); } #define __HAVE_ARCH_HUGE_PTEP_CLEAR_FLUSH diff --git a/arch/s390/include/asm/irqflags.h b/arch/s390/include/asm/irqflags.h index bcab456dfb80..6eb6de6a99f2 100644 --- a/arch/s390/include/asm/irqflags.h +++ b/arch/s390/include/asm/irqflags.h @@ -37,18 +37,24 @@ static __always_inline void __arch_local_irq_ssm(unsigned long flags) asm volatile("ssm %0" : : "Q" (flags) : "memory"); } -#ifdef CONFIG_KMSAN -#define arch_local_irq_attributes noinline notrace __no_sanitize_memory __maybe_unused +#if defined(CONFIG_KMSAN) && !defined(__DECOMPRESSOR) +unsigned long arch_local_save_flags(void); +unsigned long arch_local_irq_save(void); +void arch_local_irq_enable_external(void); +void arch_local_irq_enable(void); #else -#define arch_local_irq_attributes __always_inline +#define arch_local_save_flags __arch_local_save_flags +#define arch_local_irq_save __arch_local_irq_save +#define arch_local_irq_enable_external __arch_local_irq_enable_external +#define arch_local_irq_enable __arch_local_irq_enable #endif -static arch_local_irq_attributes unsigned long arch_local_save_flags(void) +static __always_inline unsigned long __arch_local_save_flags(void) { return __arch_local_irq_stnsm(0xff); } -static arch_local_irq_attributes unsigned long arch_local_irq_save(void) +static __always_inline unsigned long __arch_local_irq_save(void) { return __arch_local_irq_stnsm(0xfc); } @@ -58,12 +64,12 @@ static __always_inline void arch_local_irq_disable(void) arch_local_irq_save(); } -static arch_local_irq_attributes void arch_local_irq_enable_external(void) +static __always_inline void __arch_local_irq_enable_external(void) { __arch_local_irq_stosm(0x01); } -static arch_local_irq_attributes void arch_local_irq_enable(void) +static __always_inline void __arch_local_irq_enable(void) { __arch_local_irq_stosm(0x03); } diff --git a/arch/s390/include/asm/percpu.h b/arch/s390/include/asm/percpu.h index 1d955dd0defa..feb58acf87d6 100644 --- a/arch/s390/include/asm/percpu.h +++ b/arch/s390/include/asm/percpu.h @@ -107,8 +107,8 @@ " .endif\n" \ ".endr\n" \ ".endm\n" \ - ALTERNATIVE("GEN_MVIY " __stringify(disp) " " __stringify(reg) "\n", \ - "GEN_MVIY " __stringify(dispalt) " " __stringify(reg) "\n", \ + ALTERNATIVE("GEN_MVIY " disp ", " reg "\n", \ + "GEN_MVIY " dispalt ", " reg "\n", \ ALT_FEATURE(MFEATURE_LOWCORE)) \ ".purgem GEN_MVIY\n" diff --git a/arch/s390/include/asm/pkey.h b/arch/s390/include/asm/pkey.h index 0af5ac4f646b..82e403322e89 100644 --- a/arch/s390/include/asm/pkey.h +++ b/arch/s390/include/asm/pkey.h @@ -13,6 +13,7 @@ #include <linux/ioctl.h> #include <linux/types.h> +#include <linux/delay.h> #include <uapi/asm/pkey.h> /* @@ -44,4 +45,19 @@ int pkey_key2protkey(const u8 *key, u32 keylen, */ #define PKEY_XFLAG_NOCLEARKEY 0x0002 +static inline int pkey_handle_expired(void) +{ + /* + * Protected key expired due to relocation to another host. The long + * running re-wrap has no asynchronous completion notification, so + * polling is required. Trigger a re-schedule of this request by + * returning -ENOSPC ("hardware queue full") to the crypto engine. + * To avoid immediately re-invocation of this callback, + * tell the scheduler to voluntarily give up the CPU here. + */ + msleep(1); + pr_debug("rescheduling request\n"); + return -ENOSPC; +} + #endif /* _KAPI_PKEY_H */ diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index ecd3341686eb..be8369115f6d 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -14,12 +14,10 @@ #include <linux/bits.h> -#define CIF_NOHZ_DELAY 2 /* delay HZ disable for a tick */ #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_ENABLED_WAIT BIT(CIF_ENABLED_WAIT) #define _CIF_MCCK_GUEST BIT(CIF_MCCK_GUEST) #define _CIF_DEDICATED_CPU BIT(CIF_DEDICATED_CPU) @@ -97,8 +95,6 @@ static __always_inline bool test_cpu_flag_of(int flag, int cpu) return test_bit(flag, &per_cpu(pcpu_devices, cpu).flags); } -#define arch_needs_cpu() test_cpu_flag(CIF_NOHZ_DELAY) - static inline void get_cpu_id(struct cpuid *ptr) { asm volatile("stidp %0" : "=Q" (*ptr)); diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h index 495e310c3d6d..6411e3584283 100644 --- a/arch/s390/include/asm/ptrace.h +++ b/arch/s390/include/asm/ptrace.h @@ -16,13 +16,11 @@ #define PIF_SYSCALL 0 /* inside a system call */ #define PIF_PSW_ADDR_ADJUSTED 1 /* psw address has been adjusted */ #define PIF_SYSCALL_RET_SET 2 /* return value was set via ptrace */ -#define PIF_GUEST_FAULT 3 /* indicates program check in sie64a */ -#define PIF_FTRACE_FULL_REGS 4 /* all register contents valid (ftrace) */ +#define PIF_FTRACE_FULL_REGS 3 /* all register contents valid (ftrace) */ #define _PIF_SYSCALL BIT(PIF_SYSCALL) #define _PIF_ADDR_PSW_ADJUSTED BIT(PIF_PSW_ADDR_ADJUSTED) #define _PIF_SYSCALL_RET_SET BIT(PIF_SYSCALL_RET_SET) -#define _PIF_GUEST_FAULT BIT(PIF_GUEST_FAULT) #define _PIF_FTRACE_FULL_REGS BIT(PIF_FTRACE_FULL_REGS) #define PSW32_MASK_PER _AC(0x40000000, UL) diff --git a/arch/s390/include/asm/sclp.h b/arch/s390/include/asm/sclp.h index f2d490558054..44066550b9b1 100644 --- a/arch/s390/include/asm/sclp.h +++ b/arch/s390/include/asm/sclp.h @@ -21,6 +21,9 @@ #define SCLP_ERRNOTIFY_AQ_INFO_LOG 2 #define SCLP_ERRNOTIFY_AQ_OPTICS_DATA 3 #define SCLP_ERRNOTIFY_AQ_NVME_SMART_LOG 4 +#define SCLP_ERRNOTIFY_AQ_ADAPTER_INITIALIZED 5 +#define SCLP_ERRNOTIFY_AQ_RECOVERABLE_ERROR 6 +#define SCLP_ERRNOTIFY_AQ_TELEMETRY_DATA 7 #ifndef __ASSEMBLER__ #include <linux/uio.h> diff --git a/arch/s390/include/asm/thread_info.h b/arch/s390/include/asm/thread_info.h index 6a548a819400..2554cef3a6a5 100644 --- a/arch/s390/include/asm/thread_info.h +++ b/arch/s390/include/asm/thread_info.h @@ -61,6 +61,7 @@ void arch_setup_new_exec(void); */ #define HAVE_TIF_NEED_RESCHED_LAZY #define HAVE_TIF_RESTORE_SIGMASK +#define HAVE_TIF_POLLING_NRFLAG #include <asm-generic/thread_info_tif.h> diff --git a/arch/s390/include/asm/uv.h b/arch/s390/include/asm/uv.h index d919e69662f5..508075e9e8a9 100644 --- a/arch/s390/include/asm/uv.h +++ b/arch/s390/include/asm/uv.h @@ -454,18 +454,6 @@ static inline int uv_call(unsigned long r1, unsigned long r2) return cc; } -/* Low level uv_call that avoids stalls for long running busy conditions */ -static inline int uv_call_sched(unsigned long r1, unsigned long r2) -{ - int cc; - - do { - cc = __uv_call(r1, r2); - cond_resched(); - } while (cc > 1); - return cc; -} - /* * special variant of uv_call that only transports the cpu or guest * handle and the command, like destroy or verify. @@ -480,7 +468,7 @@ static inline int uv_cmd_nodata(u64 handle, u16 cmd, u16 *rc, u16 *rrc) int cc; WARN(!handle, "No handle provided to Ultravisor call cmd %x\n", cmd); - cc = uv_call_sched(0, (u64)&uvcb); + cc = uv_call(0, (u64)&uvcb); *rc = uvcb.header.rc; *rrc = uvcb.header.rrc; return cc ? -EINVAL : 0; @@ -518,7 +506,7 @@ static inline int uv_list_secrets(struct uv_secret_list *buf, u16 start_idx, .start_idx = start_idx, .list_addr = (u64)buf, }; - int cc = uv_call_sched(0, (u64)&uvcb); + int cc = uv_call(0, (u64)&uvcb); if (rc) *rc = uvcb.header.rc; @@ -635,6 +623,8 @@ int s390_wiggle_split_folio(struct mm_struct *mm, struct folio *folio); int __make_folio_secure(struct folio *folio, struct uv_cb_header *uvcb); int uv_convert_from_secure(unsigned long paddr); int uv_convert_from_secure_folio(struct folio *folio); +void *uv_alloc_stor_var(unsigned long size); +void uv_free_stor_var(void *stor_var); void setup_uv(void); diff --git a/arch/s390/include/asm/word-at-a-time.h b/arch/s390/include/asm/word-at-a-time.h index e9287036392d..eaa19dee7699 100644 --- a/arch/s390/include/asm/word-at-a-time.h +++ b/arch/s390/include/asm/word-at-a-time.h @@ -4,6 +4,7 @@ #include <linux/bitops.h> #include <linux/wordpart.h> +#include <asm/asm-extable.h> #include <asm/bitsperlong.h> struct word_at_a_time { @@ -40,4 +41,25 @@ static inline unsigned long zero_bytemask(unsigned long data) return ~1UL << data; } +/* + * Load an unaligned word from kernel space. + * + * In the (very unlikely) case of the word being a page-crosser + * and the next page not being mapped, take the exception and + * return zeroes in the non-existing part. + */ +static inline unsigned long load_unaligned_zeropad(const void *addr) +{ + unsigned long data; + + asm_inline volatile( + "0: lg %[data],0(%[addr])\n" + "1: nopr %%r7\n" + EX_TABLE_ZEROPAD(0b, 1b, %[data], %[addr]) + EX_TABLE_ZEROPAD(1b, 1b, %[data], %[addr]) + : [data] "=d" (data) + : [addr] "a" (addr), "m" (*(unsigned long *)addr)); + return data; +} + #endif /* _ASM_WORD_AT_A_TIME_H */ diff --git a/arch/s390/include/uapi/asm/Kbuild b/arch/s390/include/uapi/asm/Kbuild index 46c1ff0b842a..378151cef3cc 100644 --- a/arch/s390/include/uapi/asm/Kbuild +++ b/arch/s390/include/uapi/asm/Kbuild @@ -1,4 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -generated-y += unistd_32.h generated-y += unistd_64.h diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 6c88476d79a3..f84b8fb41331 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -3,6 +3,8 @@ # Makefile for the linux kernel. # +CONTEXT_ANALYSIS := y + ifdef CONFIG_FUNCTION_TRACER # Do not trace tracer code @@ -72,6 +74,7 @@ obj-$(CONFIG_STACKPROTECTOR) += stackprotector.o obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_image.o obj-$(CONFIG_KEXEC_FILE) += kexec_elf.o obj-$(CONFIG_CERT_STORE) += cert_store.o +obj-$(CONFIG_KMSAN) += irqflags.o obj-$(CONFIG_PERF_EVENTS) += perf_event.o obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf.o perf_cpum_sf.o diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c index dbf430f479bd..14d2b58ad093 100644 --- a/arch/s390/kernel/debug.c +++ b/arch/s390/kernel/debug.c @@ -993,8 +993,8 @@ void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas) mutex_unlock(&debug_mutex); } -/* Remove debugfs entries and remove from internal list. */ -static void _debug_unregister(debug_info_t *id) +/* Remove debugfs entries. */ +static void _debug_unregister_debugfs(debug_info_t *id) { int i; @@ -1004,6 +1004,11 @@ static void _debug_unregister(debug_info_t *id) debugfs_remove(id->debugfs_entries[i]); } debugfs_remove(id->debugfs_root_entry); +} + +/* Remove from internal list. */ +static void _debug_unregister(debug_info_t *id) +{ if (id == debug_area_first) debug_area_first = id->next; if (id == debug_area_last) @@ -1029,6 +1034,7 @@ void debug_unregister(debug_info_t *id) mutex_lock(&debug_mutex); _debug_unregister(id); mutex_unlock(&debug_mutex); + _debug_unregister_debugfs(id); debug_info_put(id); } diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 79a45efae23d..10dd9bbdf985 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -31,6 +31,7 @@ #include <asm/nospec-insn.h> #include <asm/lowcore.h> #include <asm/machine.h> +#include "entry.h" _LPP_OFFSET = __LC_LPP @@ -321,7 +322,7 @@ SYM_CODE_START(pgm_check_handler) jz 1f BPENTER __SF_SIE_FLAGS(%r15),_TIF_ISOLATE_BP_GUEST SIEEXIT __SF_SIE_CONTROL(%r15),%r13 - lghi %r10,_PIF_GUEST_FAULT + lghi %r10,PGM_FLAG_GUEST_FAULT #endif 1: tmhh %r8,0x4000 # PER bit set in old PSW ? jnz 2f # -> enabled, can't be a double fault @@ -332,7 +333,7 @@ SYM_CODE_START(pgm_check_handler) CHECK_VMAP_STACK __LC_SAVE_AREA,%r13,4f 3: lg %r15,__LC_KERNEL_STACK(%r13) 4: la %r11,STACK_FRAME_OVERHEAD(%r15) - stg %r10,__PT_FLAGS(%r11) + xc __PT_FLAGS(8,%r11),__PT_FLAGS(%r11) xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) stmg %r0,%r7,__PT_R0(%r11) mvc __PT_R8(64,%r11),__LC_SAVE_AREA(%r13) @@ -341,13 +342,13 @@ SYM_CODE_START(pgm_check_handler) # clear user controlled registers to prevent speculative use xgr %r0,%r0 xgr %r1,%r1 - xgr %r3,%r3 xgr %r4,%r4 xgr %r5,%r5 xgr %r6,%r6 xgr %r7,%r7 xgr %r12,%r12 lgr %r2,%r11 + lgr %r3,%r10 brasl %r14,__do_pgm_check tmhh %r8,0x0001 # returning to user space? jno .Lpgm_exit_kernel @@ -367,7 +368,7 @@ SYM_CODE_START(pgm_check_handler) mvc __LC_RETURN_PSW(8,%r13),__LC_SVC_NEW_PSW(%r13) larl %r14,.Lsysc_per stg %r14,__LC_RETURN_PSW+8(%r13) - lghi %r14,1 + lghi %r14,SYSCALL_FLAG_PER_TRAP LBEAR __LC_PGM_LAST_BREAK(%r13) LPSWEY __LC_RETURN_PSW,__LC_RETURN_LPSWE # branch to .Lsysc_per SYM_CODE_END(pgm_check_handler) diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index fb67b4abe68c..dc84ab497251 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h @@ -2,6 +2,11 @@ #ifndef _ENTRY_H #define _ENTRY_H +#define PGM_FLAG_GUEST_FAULT 1 +#define SYSCALL_FLAG_PER_TRAP 1 + +#ifndef __ASSEMBLER__ + #include <linux/percpu.h> #include <linux/types.h> #include <linux/signal.h> @@ -21,8 +26,8 @@ void early_pgm_check_handler(void); struct task_struct *__switch_to_asm(struct task_struct *prev, struct task_struct *next); void __ret_from_fork(struct task_struct *prev, struct pt_regs *regs); -void __do_pgm_check(struct pt_regs *regs); -void __do_syscall(struct pt_regs *regs, int per_trap); +void __do_pgm_check(struct pt_regs *regs, unsigned long flags); +void __do_syscall(struct pt_regs *regs, unsigned long flags); void __do_early_pgm_check(struct pt_regs *regs); void do_protection_exception(struct pt_regs *regs); @@ -70,4 +75,5 @@ extern struct exception_table_entry _stop_amode31_ex_table[]; #define __amode31_ref __section(".amode31.refs") extern long _start_amode31_refs[], _end_amode31_refs[]; +#endif /* __ASSEMBLER__ */ #endif /* _ENTRY_H */ diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c index 08f3520c6785..dd12efb58201 100644 --- a/arch/s390/kernel/idle.c +++ b/arch/s390/kernel/idle.c @@ -136,7 +136,6 @@ void noinstr arch_cpu_idle(void) /* Wait for external, I/O or machine check interrupt. */ psw_mask = PSW_KERNEL_BITS | PSW_MASK_WAIT | PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; - clear_cpu_flag(CIF_NOHZ_DELAY); set_cpu_flag(CIF_ENABLED_WAIT); if (smp_cpu_mtid) stcctm(MT_DIAG, smp_cpu_mtid, (u64 *)&idle->mt_cycles_enter); diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 3c346b02ceb9..d74ef30155aa 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -2021,8 +2021,11 @@ static int vmcmd_init(void) return sysfs_create_group(&vmcmd_kset->kobj, &vmcmd_attr_group); } -static struct shutdown_action vmcmd_action = {SHUTDOWN_ACTION_VMCMD_STR, - vmcmd_run, vmcmd_init}; +static struct shutdown_action vmcmd_action = { + .name = SHUTDOWN_ACTION_VMCMD_STR, + .fn = vmcmd_run, + .init = vmcmd_init +}; /* * stop shutdown action: Stop Linux on shutdown. @@ -2036,15 +2039,21 @@ static void stop_run(struct shutdown_trigger *trigger) smp_stop_cpu(); } -static struct shutdown_action stop_action = {SHUTDOWN_ACTION_STOP_STR, - stop_run, NULL}; +static struct shutdown_action stop_action = { + .name = SHUTDOWN_ACTION_STOP_STR, + .fn = stop_run +}; /* action list */ static struct shutdown_action *shutdown_actions_list[] = { - &ipl_action, &reipl_action, &dump_reipl_action, &dump_action, - &vmcmd_action, &stop_action}; -#define SHUTDOWN_ACTIONS_COUNT (sizeof(shutdown_actions_list) / sizeof(void *)) + &ipl_action, + &reipl_action, + &dump_reipl_action, + &dump_action, + &vmcmd_action, + &stop_action +}; /* * Trigger section @@ -2057,7 +2066,7 @@ static int set_trigger(const char *buf, struct shutdown_trigger *trigger, { int i; - for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) { + for (i = 0; i < ARRAY_SIZE(shutdown_actions_list); i++) { if (sysfs_streq(buf, shutdown_actions_list[i]->name)) { if (shutdown_actions_list[i]->init_rc) { return shutdown_actions_list[i]->init_rc; @@ -2072,8 +2081,10 @@ static int set_trigger(const char *buf, struct shutdown_trigger *trigger, /* on reipl */ -static struct shutdown_trigger on_reboot_trigger = {ON_REIPL_STR, - &reipl_action}; +static struct shutdown_trigger on_reboot_trigger = { + .name = ON_REIPL_STR, + .action = &reipl_action +}; static ssize_t on_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, char *page) @@ -2098,8 +2109,10 @@ static void do_machine_restart(char *__unused) void (*_machine_restart)(char *command) = do_machine_restart; /* on panic */ - -static struct shutdown_trigger on_panic_trigger = {ON_PANIC_STR, &stop_action}; +static struct shutdown_trigger on_panic_trigger = { + .name = ON_PANIC_STR, + .action = &stop_action +}; static ssize_t on_panic_show(struct kobject *kobj, struct kobj_attribute *attr, char *page) @@ -2123,9 +2136,10 @@ static void do_panic(void) } /* on restart */ - -static struct shutdown_trigger on_restart_trigger = {ON_RESTART_STR, - &stop_action}; +static struct shutdown_trigger on_restart_trigger = { + .name = ON_RESTART_STR, + .action = &stop_action +}; static ssize_t on_restart_show(struct kobject *kobj, struct kobj_attribute *attr, char *page) @@ -2160,8 +2174,10 @@ void do_restart(void *arg) } /* on halt */ - -static struct shutdown_trigger on_halt_trigger = {ON_HALT_STR, &stop_action}; +static struct shutdown_trigger on_halt_trigger = { + .name = ON_HALT_STR, + .action = &stop_action +}; static ssize_t on_halt_show(struct kobject *kobj, struct kobj_attribute *attr, char *page) @@ -2186,8 +2202,10 @@ static void do_machine_halt(void) void (*_machine_halt)(void) = do_machine_halt; /* on power off */ - -static struct shutdown_trigger on_poff_trigger = {ON_POFF_STR, &stop_action}; +static struct shutdown_trigger on_poff_trigger = { + .name = ON_POFF_STR, + .action = &stop_action +}; static ssize_t on_poff_show(struct kobject *kobj, struct kobj_attribute *attr, char *page) @@ -2242,7 +2260,7 @@ static void __init shutdown_actions_init(void) { int i; - for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) { + for (i = 0; i < ARRAY_SIZE(shutdown_actions_list); i++) { if (!shutdown_actions_list[i]->init) continue; shutdown_actions_list[i]->init_rc = diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 04d528639b94..c923496aa7b4 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -166,7 +166,6 @@ void noinstr do_io_irq(struct pt_regs *regs) if (from_idle) account_idle_time_irq(); - set_cpu_flag(CIF_NOHZ_DELAY); do { regs->tpi_info = get_lowcore()->tpi_info; if (get_lowcore()->tpi_info.adapter_IO) @@ -369,9 +368,6 @@ static irqreturn_t do_ext_interrupt(int irq, void *dummy) int index; ext_code.int_code = regs->int_code; - if (ext_code.code != EXT_IRQ_CLK_COMP) - set_cpu_flag(CIF_NOHZ_DELAY); - index = ext_hash(ext_code.code); rcu_read_lock(); hlist_for_each_entry_rcu(p, &ext_int_hash[index], entry) { diff --git a/arch/s390/kernel/irqflags.c b/arch/s390/kernel/irqflags.c new file mode 100644 index 000000000000..e192f59f8918 --- /dev/null +++ b/arch/s390/kernel/irqflags.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/export.h> +#include <asm/irqflags.h> + +noinstr unsigned long arch_local_save_flags(void) +{ + return __arch_local_save_flags(); +} +EXPORT_SYMBOL(arch_local_save_flags); + +noinstr unsigned long arch_local_irq_save(void) +{ + return __arch_local_irq_save(); +} +EXPORT_SYMBOL(arch_local_irq_save); + +noinstr void arch_local_irq_enable_external(void) +{ + __arch_local_irq_enable_external(); +} +EXPORT_SYMBOL(arch_local_irq_enable_external); + +noinstr void arch_local_irq_enable(void) +{ + __arch_local_irq_enable(); +} +EXPORT_SYMBOL(arch_local_irq_enable); diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S index 1fec370fecf4..6bc44c767642 100644 --- a/arch/s390/kernel/mcount.S +++ b/arch/s390/kernel/mcount.S @@ -4,6 +4,7 @@ * */ +#include <linux/cfi_types.h> #include <linux/linkage.h> #include <asm/asm-offsets.h> #include <asm/ftrace.h> @@ -34,10 +35,16 @@ .section .kprobes.text, "ax" -SYM_FUNC_START(ftrace_stub) +SYM_TYPED_FUNC_START(ftrace_stub) BR_EX %r14 SYM_FUNC_END(ftrace_stub) +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +SYM_TYPED_FUNC_START(ftrace_stub_graph) + BR_EX %r14 +SYM_FUNC_END(ftrace_stub_graph) +#endif + SYM_CODE_START(ftrace_stub_direct_tramp) lgr %r1, %r0 BR_EX %r1 diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c index 2076ac22e2c4..69fa303cd479 100644 --- a/arch/s390/kernel/perf_cpum_cf.c +++ b/arch/s390/kernel/perf_cpum_cf.c @@ -110,6 +110,7 @@ struct cpu_cf_ptr { static struct cpu_cf_root { /* Anchor to per CPU data */ refcount_t refcnt; /* Overall active events */ + unsigned int tskctx; /* Users tracking all CPUs (cpu == -1) */ struct cpu_cf_ptr __percpu *cfptr; } cpu_cf_root; @@ -118,13 +119,15 @@ static struct cpu_cf_root { /* Anchor to per CPU data */ * user space in task context with perf_event_open() and close() * system calls. * - * This mutex serializes functions cpum_cf_alloc_cpu() called at event - * initialization via cpumf_pmu_event_init() and function cpum_cf_free_cpu() - * called at event removal via call back function hw_perf_event_destroy() - * when the event is deleted. They are serialized to enforce correct - * bookkeeping of pointer and reference counts anchored by - * struct cpu_cf_root and the access to cpu_cf_root::refcnt and the - * per CPU pointers stored in cpu_cf_root::cfptr. + * This mutex serializes the allocation and removal of the per CPU counter + * data via cpum_cf_alloc_cpu() and cpum_cf_free_cpu(). They are called with + * this mutex held at event initialization via cpumf_pmu_event_init(), at + * event removal via call back function hw_perf_event_destroy() when the + * event is deleted, and from the CPU hotplug prepare/dead callbacks. The + * mutex enforces correct bookkeeping of pointer and reference counts + * anchored by struct cpu_cf_root and protects the access to + * cpu_cf_root::refcnt, cpu_cf_root::tskctx and the per CPU pointers + * stored in cpu_cf_root::cfptr. */ static DEFINE_MUTEX(pmc_reserve_mutex); @@ -167,12 +170,14 @@ static void cpum_cf_reset_cpu(void *flags) } /* Free per CPU data when the last event is removed. */ -static void cpum_cf_free_root(void) +static void cpum_cf_free_root(unsigned int num) { - if (!refcount_dec_and_test(&cpu_cf_root.refcnt)) + struct cpu_cf_ptr __percpu *p = cpu_cf_root.cfptr; + + if (!refcount_sub_and_test(num, &cpu_cf_root.refcnt)) return; - free_percpu(cpu_cf_root.cfptr); cpu_cf_root.cfptr = NULL; + free_percpu(p); irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT); on_each_cpu(cpum_cf_reset_cpu, NULL, 1); debug_sprintf_event(cf_dbg, 4, "%s root.refcnt %u cfptr %d\n", @@ -186,17 +191,17 @@ static void cpum_cf_free_root(void) * CPUs possible, which might be larger than the number of CPUs currently * online. */ -static int cpum_cf_alloc_root(void) +static int cpum_cf_alloc_root(unsigned int num) { int rc = 0; - if (refcount_inc_not_zero(&cpu_cf_root.refcnt)) + if (refcount_add_not_zero(num, &cpu_cf_root.refcnt)) return rc; /* The memory is already zeroed. */ cpu_cf_root.cfptr = alloc_percpu(struct cpu_cf_ptr); if (cpu_cf_root.cfptr) { - refcount_set(&cpu_cf_root.refcnt, 1); + refcount_set(&cpu_cf_root.refcnt, num); on_each_cpu(cpum_cf_reset_cpu, NULL, 1); irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT); } else { @@ -206,20 +211,23 @@ static int cpum_cf_alloc_root(void) return rc; } -/* Free CPU counter data structure for a PMU */ -static void cpum_cf_free_cpu(int cpu) +/* + * Remove num references to the CPU counter data structure of a PMU. + * Called with pmc_reserve_mutex held. + */ +static void cpum_cf_free_cpu(int cpu, unsigned int num) { struct cpu_cf_events *cpuhw; struct cpu_cf_ptr *p; - mutex_lock(&pmc_reserve_mutex); + lockdep_assert_held(&pmc_reserve_mutex); /* * When invoked via CPU hotplug handler, there might be no events * installed or that particular CPU might not have an * event installed. This anchor pointer can be NULL! */ if (!cpu_cf_root.cfptr) - goto out; + return; p = per_cpu_ptr(cpu_cf_root.cfptr, cpu); cpuhw = p->cpucf; /* @@ -227,28 +235,29 @@ static void cpum_cf_free_cpu(int cpu) * installed on that CPU, but on different CPUs. */ if (!cpuhw) - goto out; + return; - if (refcount_dec_and_test(&cpuhw->refcnt)) { - kfree(cpuhw); + if (refcount_sub_and_test(num, &cpuhw->refcnt)) { p->cpucf = NULL; + kfree(cpuhw); } - cpum_cf_free_root(); -out: - mutex_unlock(&pmc_reserve_mutex); + cpum_cf_free_root(num); } -/* Allocate CPU counter data structure for a PMU. Called under mutex lock. */ -static int cpum_cf_alloc_cpu(int cpu) +/* + * Add num references to the CPU counter data structure of a PMU and + * allocate it when necessary. Called with pmc_reserve_mutex held. + */ +static int cpum_cf_alloc_cpu(int cpu, unsigned int num) { struct cpu_cf_events *cpuhw; struct cpu_cf_ptr *p; int rc; - mutex_lock(&pmc_reserve_mutex); - rc = cpum_cf_alloc_root(); + lockdep_assert_held(&pmc_reserve_mutex); + rc = cpum_cf_alloc_root(num); if (rc) - goto unlock; + return rc; p = per_cpu_ptr(cpu_cf_root.cfptr, cpu); cpuhw = p->cpucf; @@ -256,12 +265,12 @@ static int cpum_cf_alloc_cpu(int cpu) cpuhw = kzalloc_obj(*cpuhw); if (cpuhw) { p->cpucf = cpuhw; - refcount_set(&cpuhw->refcnt, 1); + refcount_set(&cpuhw->refcnt, num); } else { rc = -ENOMEM; } } else { - refcount_inc(&cpuhw->refcnt); + refcount_add(num, &cpuhw->refcnt); } if (rc) { /* @@ -269,10 +278,8 @@ static int cpum_cf_alloc_cpu(int cpu) * cpu_cf_event in not created, its destroy() function is not * invoked. Adjust the reference counter for the anchor. */ - cpum_cf_free_root(); + cpum_cf_free_root(num); } -unlock: - mutex_unlock(&pmc_reserve_mutex); return rc; } @@ -284,39 +291,70 @@ unlock: * perf_event_open() with task context and /dev/hwctr interface. * If cpu is non-zero install event on this CPU only. This setup handles * perf_event_open() with CPU context. + * Users with cpu == -1 are counted in cpu_cf_root::tskctx. The CPU hotplug + * prepare and dead callbacks use this count to install and remove the per + * CPU counter data on a new or dying CPU. */ -static int cpum_cf_alloc(int cpu) +static int cpum_cf_alloc_cpuslocked(int cpu) { cpumask_var_t mask; int rc; + lockdep_assert_cpus_held(); if (cpu == -1) { if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) return -ENOMEM; + mutex_lock(&pmc_reserve_mutex); for_each_online_cpu(cpu) { - rc = cpum_cf_alloc_cpu(cpu); + rc = cpum_cf_alloc_cpu(cpu, 1); if (rc) { for_each_cpu(cpu, mask) - cpum_cf_free_cpu(cpu); + cpum_cf_free_cpu(cpu, 1); break; } cpumask_set_cpu(cpu, mask); } + if (!rc) + cpu_cf_root.tskctx++; + mutex_unlock(&pmc_reserve_mutex); free_cpumask_var(mask); } else { - rc = cpum_cf_alloc_cpu(cpu); + mutex_lock(&pmc_reserve_mutex); + rc = cpum_cf_alloc_cpu(cpu, 1); + mutex_unlock(&pmc_reserve_mutex); } return rc; } -static void cpum_cf_free(int cpu) +static int cpum_cf_alloc(int cpu) +{ + int rc; + + cpus_read_lock(); + rc = cpum_cf_alloc_cpuslocked(cpu); + cpus_read_unlock(); + return rc; +} + +static void cpum_cf_free_cpuslocked(int cpu) { + lockdep_assert_cpus_held(); + mutex_lock(&pmc_reserve_mutex); if (cpu == -1) { + cpu_cf_root.tskctx--; for_each_online_cpu(cpu) - cpum_cf_free_cpu(cpu); + cpum_cf_free_cpu(cpu, 1); } else { - cpum_cf_free_cpu(cpu); + cpum_cf_free_cpu(cpu, 1); } + mutex_unlock(&pmc_reserve_mutex); +} + +static void cpum_cf_free(int cpu) +{ + cpus_read_lock(); + cpum_cf_free_cpuslocked(cpu); + cpus_read_unlock(); } #define CF_DIAG_CTRSET_DEF 0xfeef /* Counter set header mark */ @@ -1090,53 +1128,67 @@ static refcount_t cfset_opencnt = REFCOUNT_INIT(0); /* Access count */ static DEFINE_MUTEX(cfset_ctrset_mutex); /* - * CPU hotplug handles only /dev/hwctr device. - * For perf_event_open() the CPU hotplug handling is done on kernel common - * code: + * CPU hotplug handling: + * + * cpum_cf_prepare_cpu() and cpum_cf_dead_cpu() run while the new or dying + * CPU is offline. They create and remove the per CPU counter data for all + * users tracking every CPU (cpu == -1), that is perf_event_open() events + * with task context and /dev/hwctr device sessions. Each such user holds + * one reference to the per CPU counter data of each CPU. Therefore install + * and remove one reference per user, tracked in cpu_cf_root::tskctx. This + * guarantees the per CPU counter data exists before the new CPU executes + * its first task and is removed only after the dying CPU is gone. + * + * cpum_cf_online_cpu() and cpum_cf_offline_cpu() run while the new or + * dying CPU is online. They handle only the counter set state of open + * /dev/hwctr device sessions on that CPU. For perf_event_open() events + * nothing is done: * - CPU add: Nothing is done since a file descriptor can not be created * and returned to the user. * - CPU delete: Handled by common code via pmu_disable(), pmu_stop() and - * pmu_delete(). The event itself is removed when the file descriptor is - * closed. + * pmu_delete(). During task exit processing of grouped perf events + * triggered by CPU hotplug processing, pmu_disable() is called as part + * of perf context removal process. The event itself is removed when the + * event file descriptor is closed. */ +static int cpum_cf_prepare_cpu(unsigned int cpu) +{ + int rc = 0; + + mutex_lock(&pmc_reserve_mutex); + if (cpu_cf_root.tskctx) + rc = cpum_cf_alloc_cpu(cpu, cpu_cf_root.tskctx); + mutex_unlock(&pmc_reserve_mutex); + return rc; +} + +static int cpum_cf_dead_cpu(unsigned int cpu) +{ + mutex_lock(&pmc_reserve_mutex); + if (cpu_cf_root.tskctx) + cpum_cf_free_cpu(cpu, cpu_cf_root.tskctx); + mutex_unlock(&pmc_reserve_mutex); + return 0; +} + static int cfset_online_cpu(unsigned int cpu); static int cpum_cf_online_cpu(unsigned int cpu) { - int rc = 0; - - /* - * Ignore notification for perf_event_open(). - * Handle only /dev/hwctr device sessions. - */ mutex_lock(&cfset_ctrset_mutex); - if (refcount_read(&cfset_opencnt)) { - rc = cpum_cf_alloc_cpu(cpu); - if (!rc) - cfset_online_cpu(cpu); - } + if (refcount_read(&cfset_opencnt)) + cfset_online_cpu(cpu); mutex_unlock(&cfset_ctrset_mutex); - return rc; + return 0; } static int cfset_offline_cpu(unsigned int cpu); static int cpum_cf_offline_cpu(unsigned int cpu) { - /* - * During task exit processing of grouped perf events triggered by CPU - * hotplug processing, pmu_disable() is called as part of perf context - * removal process. Therefore do not trigger event removal now for - * perf_event_open() created events. Perf common code triggers event - * destruction when the event file descriptor is closed. - * - * Handle only /dev/hwctr device sessions. - */ mutex_lock(&cfset_ctrset_mutex); - if (refcount_read(&cfset_opencnt)) { + if (refcount_read(&cfset_opencnt)) cfset_offline_cpu(cpu); - cpum_cf_free_cpu(cpu); - } mutex_unlock(&cfset_ctrset_mutex); return 0; } @@ -1183,7 +1235,7 @@ static void cpumf_measurement_alert(struct ext_code ext_code, static int cfset_init(void); static int __init cpumf_pmu_init(void) { - int rc; + int state, rc; /* Extract counter measurement facility information */ if (!cpum_cf_avail() || qctri(&cpumf_ctr_info)) @@ -1225,11 +1277,24 @@ static int __init cpumf_pmu_init(void) cfset_init(); } + rc = cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, + "perf/s390/cf:prepare", + cpum_cf_prepare_cpu, cpum_cf_dead_cpu); + if (rc < 0) + goto out3; + state = rc; + rc = cpuhp_setup_state(CPUHP_AP_PERF_S390_CF_ONLINE, "perf/s390/cf:online", cpum_cf_online_cpu, cpum_cf_offline_cpu); - return rc; + if (rc < 0) + goto out4; + return 0; +out4: + cpuhp_remove_state(state); +out3: + perf_pmu_unregister(&cpumf_pmu); out2: debug_unregister_view(cf_dbg, &debug_sprintf_view); debug_unregister(cf_dbg); @@ -1385,6 +1450,7 @@ static void cfset_all_stop(struct cfset_request *req) */ static int cfset_release(struct inode *inode, struct file *file) { + cpus_read_lock(); mutex_lock(&cfset_ctrset_mutex); /* Open followed by close/exit has no private_data */ if (file->private_data) { @@ -1395,9 +1461,10 @@ static int cfset_release(struct inode *inode, struct file *file) } if (refcount_dec_and_test(&cfset_opencnt)) { /* Last close */ on_each_cpu(cfset_release_cpu, NULL, 1); - cpum_cf_free(-1); + cpum_cf_free_cpuslocked(-1); } mutex_unlock(&cfset_ctrset_mutex); + cpus_read_unlock(); return 0; } @@ -1416,15 +1483,17 @@ static int cfset_open(struct inode *inode, struct file *file) return -EPERM; file->private_data = NULL; + cpus_read_lock(); mutex_lock(&cfset_ctrset_mutex); if (!refcount_inc_not_zero(&cfset_opencnt)) { /* First open */ - rc = cpum_cf_alloc(-1); + rc = cpum_cf_alloc_cpuslocked(-1); if (!rc) { cfset_session_init(); refcount_set(&cfset_opencnt, 1); } } mutex_unlock(&cfset_ctrset_mutex); + cpus_read_unlock(); /* nonseekable_open() never fails */ return rc ?: nonseekable_open(inode, file); @@ -1496,7 +1565,6 @@ static int cfset_all_copy(unsigned long arg, cpumask_t *mask) goto out; } uptr += sizeof(struct s390_ctrset_cpudata) + cpuhw->used; - cond_resched(); } cpus = cpumask_weight(mask); if (put_user(cpus, &ctrset_read->no_cpus)) diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 0ba7f89b8161..167c72803ccf 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -909,7 +909,6 @@ int __cpu_disable(void) cregs[6].val &= ~0xff000000UL; /* disable all I/O interrupts */ cregs[14].val &= ~0x1f000000UL; /* disable most machine checks */ __local_ctl_load(0, 15, cregs); - clear_cpu_flag(CIF_NOHZ_DELAY); return 0; } @@ -1039,6 +1038,7 @@ static ssize_t cpu_configure_store(struct device *dev, per_cpu(pcpu_devices, cpu + i).state = CPU_STATE_STANDBY; smp_cpu_set_polarization(cpu + i, POLARIZATION_UNKNOWN); + set_cpu_enabled(cpu + i, false); } topology_expect_change(); break; @@ -1054,6 +1054,7 @@ static ssize_t cpu_configure_store(struct device *dev, per_cpu(pcpu_devices, cpu + i).state = CPU_STATE_CONFIGURED; smp_cpu_set_polarization(cpu + i, POLARIZATION_UNKNOWN); + set_cpu_enabled(cpu + i, true); } topology_expect_change(); break; @@ -1091,6 +1092,7 @@ bool arch_cpu_is_hotpluggable(int cpu) int arch_register_cpu(int cpu) { + struct pcpu *pcpu = per_cpu_ptr(&pcpu_devices, cpu); struct cpu *c = per_cpu_ptr(&cpu_devices, cpu); int rc; @@ -1104,6 +1106,8 @@ int arch_register_cpu(int cpu) rc = topology_cpu_init(c); if (rc) goto out_topology; + if (pcpu->state != CPU_STATE_CONFIGURED) + set_cpu_enabled(cpu, false); return 0; out_topology: diff --git a/arch/s390/kernel/syscall.c b/arch/s390/kernel/syscall.c index 4ac80e2478d7..bcc0b76bd868 100644 --- a/arch/s390/kernel/syscall.c +++ b/arch/s390/kernel/syscall.c @@ -93,7 +93,7 @@ SYSCALL_DEFINE0(ni_syscall) return -ENOSYS; } -void noinstr __do_syscall(struct pt_regs *regs, int per_trap) +void noinstr __do_syscall(struct pt_regs *regs, unsigned long flags) { unsigned long nr; bool permit; @@ -107,7 +107,7 @@ void noinstr __do_syscall(struct pt_regs *regs, int per_trap) current->thread.last_break = regs->last_break; local_irq_enable(); regs->orig_gpr2 = regs->gprs[2]; - if (unlikely(per_trap)) + if (unlikely(flags & SYSCALL_FLAG_PER_TRAP)) set_thread_flag(TIF_PER_TRAP); regs->flags = 0; set_pt_regs_flag(regs, PIF_SYSCALL); diff --git a/arch/s390/kernel/sysinfo.c b/arch/s390/kernel/sysinfo.c index 33ca3e47a0e6..45b4f448fe3d 100644 --- a/arch/s390/kernel/sysinfo.c +++ b/arch/s390/kernel/sysinfo.c @@ -325,6 +325,7 @@ int unregister_service_level(struct service_level *slr) EXPORT_SYMBOL(unregister_service_level); static void *service_level_start(struct seq_file *m, loff_t *pos) +__acquires_shared(service_level_sem) { down_read(&service_level_sem); return seq_list_start(&service_level_list, *pos); @@ -336,6 +337,7 @@ static void *service_level_next(struct seq_file *m, void *p, loff_t *pos) } static void service_level_stop(struct seq_file *m, void *p) +__releases_shared(service_level_sem) { up_read(&service_level_sem); } diff --git a/arch/s390/kernel/text_amode31.S b/arch/s390/kernel/text_amode31.S index 26f2981aa09e..f007d892d0c5 100644 --- a/arch/s390/kernel/text_amode31.S +++ b/arch/s390/kernel/text_amode31.S @@ -5,6 +5,7 @@ * Copyright IBM Corp. 2019 */ +#include <linux/cfi_types.h> #include <linux/linkage.h> #include <asm/asm-extable.h> #include <asm/errno.h> @@ -26,7 +27,7 @@ /* * int _diag14_amode31(unsigned long rx, unsigned long ry1, unsigned long subcode) */ -SYM_FUNC_START(_diag14_amode31) +SYM_TYPED_FUNC_START(_diag14_amode31) lgr %r1,%r2 lgr %r2,%r3 lgr %r3,%r4 @@ -46,7 +47,7 @@ SYM_FUNC_END(_diag14_amode31) /* * int _diag210_amode31(struct diag210 *addr) */ -SYM_FUNC_START(_diag210_amode31) +SYM_TYPED_FUNC_START(_diag210_amode31) lgr %r1,%r2 lhi %r2,-1 sam31 @@ -64,7 +65,7 @@ SYM_FUNC_END(_diag210_amode31) /* * int diag8c(struct diag8c *addr, struct ccw_dev_id *devno, size_t len) */ -SYM_FUNC_START(_diag8c_amode31) +SYM_TYPED_FUNC_START(_diag8c_amode31) llgf %r3,0(%r3) sam31 diag %r2,%r4,0x8c @@ -77,7 +78,7 @@ SYM_FUNC_END(_diag8c_amode31) /* * int _diag26c_amode31(void *req, void *resp, enum diag26c_sc subcode) */ -SYM_FUNC_START(_diag26c_amode31) +SYM_TYPED_FUNC_START(_diag26c_amode31) lghi %r5,-EOPNOTSUPP sam31 diag %r2,%r4,0x26c @@ -91,7 +92,7 @@ SYM_FUNC_END(_diag26c_amode31) /* * void _diag0c_amode31(unsigned long rx) */ -SYM_FUNC_START(_diag0c_amode31) +SYM_TYPED_FUNC_START(_diag0c_amode31) sam31 diag %r2,%r2,0x0c sam64 @@ -103,7 +104,7 @@ SYM_FUNC_END(_diag0c_amode31) * * Calls diag 308 subcode 1 and continues execution */ -SYM_FUNC_START(_diag308_reset_amode31) +SYM_TYPED_FUNC_START(_diag308_reset_amode31) larl %r4,ctlregs # Save control registers stctg %c0,%c15,0(%r4) lg %r2,0(%r4) # Disable lowcore protection diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 564403496a7c..b6ba4465f59d 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -9,7 +9,9 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ +#include <linux/capability.h> #include <linux/cpufeature.h> +#include <linux/debugfs.h> #include <linux/kprobes.h> #include <linux/kdebug.h> #include <linux/randomize_kstack.h> @@ -33,6 +35,12 @@ #include <asm/fault.h> #include "entry.h" +struct pgm_stat { + unsigned int count[128]; +}; + +static DEFINE_PER_CPU_SHARED_ALIGNED(struct pgm_stat, pgm_stat); + static inline void __user *get_trap_ip(struct pt_regs *regs) { unsigned long address; @@ -327,11 +335,12 @@ void __init trap_init(void) static void (*pgm_check_table[128])(struct pt_regs *regs); -void noinstr __do_pgm_check(struct pt_regs *regs) +void noinstr __do_pgm_check(struct pt_regs *regs, unsigned long flags) { struct lowcore *lc = get_lowcore(); bool percpu_needs_fixup; irqentry_state_t state; + struct pgm_stat *stat; unsigned int trapnr; union teid teid; @@ -339,6 +348,10 @@ void noinstr __do_pgm_check(struct pt_regs *regs) regs->int_code = lc->pgm_int_code; regs->int_parm_long = teid.val; regs->monitor_code = lc->monitor_code; + + trapnr = regs->int_code & PGM_INT_CODE_MASK; + stat = this_cpu_ptr(&pgm_stat); + stat->count[trapnr]++; /* * In case of a guest fault, short-circuit the fault handler and return. * This way the sie64a() function will return 0; fault address and @@ -346,7 +359,7 @@ void noinstr __do_pgm_check(struct pt_regs *regs) * the fault number in current->thread.gmap_int_code. KVM will be * able to use this information to handle the fault. */ - if (test_pt_regs_flag(regs, PIF_GUEST_FAULT)) { + if (flags & PGM_FLAG_GUEST_FAULT) { current->thread.gmap_teid.val = regs->int_parm_long; current->thread.gmap_int_code = regs->int_code & 0xffff; return; @@ -383,7 +396,6 @@ void noinstr __do_pgm_check(struct pt_regs *regs) if (!irqs_disabled_flags(regs->psw.mask)) trace_hardirqs_on(); __arch_local_irq_ssm(regs->psw.mask & ~PSW_MASK_PER); - trapnr = regs->int_code & PGM_INT_CODE_MASK; if (trapnr) pgm_check_table[trapnr](regs); out: @@ -393,6 +405,33 @@ out: percpu_exit(regs, percpu_needs_fixup); } +static int pgm_check_stat_show(struct seq_file *p, void *v) +{ + int i, cpu; + + cpus_read_lock(); + seq_puts(p, " "); + for_each_online_cpu(cpu) + seq_printf(p, "CPU%-8d", cpu); + seq_putc(p, '\n'); + for (i = 0; i < 128; i++) { + seq_printf(p, "%02x: ", i); + for_each_online_cpu(cpu) + seq_printf(p, "%10u ", per_cpu(pgm_stat, cpu).count[i]); + seq_putc(p, '\n'); + } + cpus_read_unlock(); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(pgm_check_stat); + +static int __init debugfs_pgm_check_init(void) +{ + debugfs_create_file("exceptions", 0400, arch_debugfs_dir, NULL, &pgm_check_stat_fops); + return 0; +} +late_initcall(debugfs_pgm_check_init); + /* * The program check table contains exactly 128 (0x00-0x7f) entries. Each * line defines the function to be called corresponding to the program check diff --git a/arch/s390/kernel/uv.c b/arch/s390/kernel/uv.c index a284f98d9716..dc14ebc0105b 100644 --- a/arch/s390/kernel/uv.c +++ b/arch/s390/kernel/uv.c @@ -16,6 +16,7 @@ #include <linux/swap.h> #include <linux/pagewalk.h> #include <linux/backing-dev.h> +#include <linux/vmalloc.h> #include <asm/facility.h> #include <asm/sections.h> #include <asm/uv.h> @@ -209,6 +210,70 @@ int uv_convert_from_secure_pte(pte_t pte) return uv_convert_from_secure_folio(pfn_folio(pte_pfn(pte))); } +static int uv_free_range_cb(pte_t *ptep, unsigned long addr, void *data) +{ + pte_t pte = ptep_get(ptep); + + if (!pte_present(pte)) + return 0; + /* + * Note: do not update the pte here, since there is no code which + * accesses the memory range, besides bugs. The invalidation of ptes + * and TLB flushing is deferred like for regular vfree() calls. + */ + __free_page(pte_page(pte)); + return 0; +} + +void uv_free_stor_var(void *stor_var) +{ + unsigned long addr, size; + struct vm_struct *area; + + if (!stor_var) + return; + area = find_vm_area(stor_var); + if (WARN_ON_ONCE(!area || !(area->flags & VM_SPARSE))) + return; + size = get_vm_area_size(area); + addr = (unsigned long)area->addr; + apply_to_existing_page_range(&init_mm, addr, size, uv_free_range_cb, NULL); + free_vm_area(area); +} +EXPORT_SYMBOL_FOR_MODULES(uv_free_stor_var, "kvm"); + +static int uv_alloc_range_cb(pte_t *ptep, unsigned long addr, void *data) +{ + struct page *page; + pte_t pte; + + page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO); + if (!page) + return -ENOMEM; + pte = __pte(page_to_phys(page) | pgprot_val(PAGE_KERNEL)); + set_pte(ptep, pte); + return 0; +} + +void *uv_alloc_stor_var(unsigned long size) +{ + struct vm_struct *area; + unsigned long addr; + + size = PAGE_ALIGN(size); + area = get_vm_area(size, VM_SPARSE); + if (!area) + return NULL; + addr = (unsigned long)area->addr; + if (apply_to_page_range(&init_mm, addr, size, uv_alloc_range_cb, NULL)) + goto out; + return area->addr; +out: + uv_free_stor_var(area->addr); + return NULL; +} +EXPORT_SYMBOL_FOR_MODULES(uv_alloc_stor_var, "kvm"); + /* * Calculate the expected ref_count for a folio that would otherwise have no * further pins. This was cribbed from similar functions in other places in @@ -831,7 +896,7 @@ int uv_retrieve_secret(u16 secret_idx, u8 *buf, size_t buf_size) .buf_size = buf_size, }; - uv_call_sched(0, (u64)&uvcb); + uv_call(0, (u64)&uvcb); switch (uvcb.header.rc) { case UVC_RC_EXECUTED: diff --git a/arch/s390/kernel/vdso/Makefile b/arch/s390/kernel/vdso/Makefile index fece5d975eaf..35c834b895ec 100644 --- a/arch/s390/kernel/vdso/Makefile +++ b/arch/s390/kernel/vdso/Makefile @@ -30,7 +30,8 @@ KBUILD_CFLAGS_VDSO := $(filter-out -fno-asynchronous-unwind-tables,$(KBUILD_CFLA KBUILD_CFLAGS_VDSO += -fPIC -fno-common -fno-builtin -fasynchronous-unwind-tables KBUILD_CFLAGS_VDSO += -fno-stack-protector $(DISABLE_KSTACK_ERASE) ldflags-y := -shared -soname=linux-vdso.so.1 \ - --hash-style=both --build-id=sha1 -T + --hash-style=both --build-id=sha1 \ + $(call ld-option, --eh-frame-hdr) -T $(targets:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_VDSO) $(targets:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_VDSO) diff --git a/arch/s390/kernel/vdso/vdso.lds.S b/arch/s390/kernel/vdso/vdso.lds.S index 7bec4de0e8e0..841daeec4be2 100644 --- a/arch/s390/kernel/vdso/vdso.lds.S +++ b/arch/s390/kernel/vdso/vdso.lds.S @@ -82,12 +82,15 @@ SECTIONS * We must supply the ELF program headers explicitly to get just one * PT_LOAD segment, and set the flags explicitly to make segments read-only. */ +#define PF_R FLAGS(4) +#define PF_RX FLAGS(5) + PHDRS { - text PT_LOAD FILEHDR PHDRS FLAGS(5); /* PF_R|PF_X */ - dynamic PT_DYNAMIC FLAGS(4); /* PF_R */ - note PT_NOTE FLAGS(4); /* PF_R */ - eh_frame_hdr PT_GNU_EH_FRAME; + text PT_LOAD PF_RX FILEHDR PHDRS; + dynamic PT_DYNAMIC PF_R; + note PT_NOTE PF_R; + eh_frame_hdr PT_GNU_EH_FRAME PF_R; } /* diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c index 36102b2727fb..0d389a302a70 100644 --- a/arch/s390/kvm/gaccess.c +++ b/arch/s390/kvm/gaccess.c @@ -151,10 +151,8 @@ retry: ic = &kvm->arch.sca->ipte_control; old = READ_ONCE(*ic); do { - if (old.k) { - cond_resched(); + if (old.k) goto retry; - } new = old; new.k = 1; } while (!try_cmpxchg(&ic->val, &old.val, new.val)); @@ -189,10 +187,8 @@ retry: ic = &kvm->arch.sca->ipte_control; old = READ_ONCE(*ic); do { - if (old.kg) { - cond_resched(); + if (old.kg) goto retry; - } new = old; new.k = 1; new.kh++; diff --git a/arch/s390/kvm/gmap.c b/arch/s390/kvm/gmap.c index 8abb4f55b306..3758e4009709 100644 --- a/arch/s390/kvm/gmap.c +++ b/arch/s390/kvm/gmap.c @@ -941,7 +941,6 @@ void gmap_split_huge_pages(struct gmap *gmap) scoped_guard(read_lock, &gmap->kvm->mmu_lock) start = _dat_walk_gfn_range(start, asce_end(gmap->asce), gmap->asce, &ops, DAT_WALK_IGN_HOLES, gmap); - cond_resched(); } while (start); } @@ -963,7 +962,6 @@ static int _gmap_enable_skeys(struct gmap *gmap) do { scoped_guard(write_lock, &gmap->kvm->mmu_lock) start = dat_reset_skeys(gmap->asce, start); - cond_resched(); } while (start); return 0; } @@ -1019,7 +1017,6 @@ int gmap_pv_destroy_range(struct gmap *gmap, gfn_t start, gfn_t end, bool interr DAT_WALK_IGN_HOLES, NULL); if (interruptible && fatal_signal_pending(current)) return -EINTR; - cond_resched(); } while (start && start < end); return 0; } @@ -1138,7 +1135,6 @@ void _gmap_set_cmma_all(struct gmap *gmap, bool dirty) gfn = _dat_walk_gfn_range(gfn, asce_end(gmap->asce), gmap->asce, &ops, DAT_WALK_IGN_HOLES, &gmap->kvm->arch.cmma_dirty_pages); - cond_resched(); } while (gfn); } diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 91b975835457..e2795390c1c4 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -1028,7 +1028,6 @@ static int kvm_s390_set_mem_control(struct kvm *kvm, struct kvm_device_attr *att do { scoped_guard(read_lock, &kvm->mmu_lock) start_gfn = dat_reset_cmma(kvm->arch.gmap->asce, start_gfn); - cond_resched(); } while (start_gfn); ret = 0; break; diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c index 98a9a57f71b9..7f168c3c3e1a 100644 --- a/arch/s390/kvm/pv.c +++ b/arch/s390/kvm/pv.c @@ -342,7 +342,7 @@ int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc) /* only free resources when the destroy was successful */ static void kvm_s390_pv_dealloc_vm(struct kvm *kvm) { - vfree(kvm->arch.pv.stor_var); + uv_free_stor_var(kvm->arch.pv.stor_var); free_pages(kvm->arch.pv.stor_base, get_order(uv_info.guest_base_stor_len)); kvm_s390_clear_pv_state(kvm); @@ -374,7 +374,7 @@ static int kvm_s390_pv_alloc_vm(struct kvm *kvm) /* Allocate variable storage */ vlen = ALIGN(virt * ((npages * PAGE_SIZE) / HPAGE_SIZE), PAGE_SIZE); vlen += uv_info.guest_virt_base_stor_len; - kvm->arch.pv.stor_var = vzalloc(vlen); + kvm->arch.pv.stor_var = uv_alloc_stor_var(vlen); if (!kvm->arch.pv.stor_var) goto out_err; return 0; @@ -419,7 +419,7 @@ static int kvm_s390_pv_dispose_one_leftover(struct kvm *kvm, */ free_pages(leftover->stor_base, get_order(uv_info.guest_base_stor_len)); free_pages(leftover->old_gmap_table, CRST_ALLOC_ORDER); - vfree(leftover->stor_var); + uv_free_stor_var(leftover->stor_var); done_fast: atomic_dec(&kvm->mm->context.protected_count); return 0; @@ -434,7 +434,7 @@ static int kvm_s390_pv_deinit_vm_fast(struct kvm *kvm, u16 *rc, u16 *rrc) }; int cc; - cc = uv_call_sched(0, (u64)&uvcb); + cc = uv_call(0, (u64)&uvcb); if (rc) *rc = uvcb.header.rc; if (rrc) @@ -751,7 +751,7 @@ int kvm_s390_pv_init_vm(struct kvm *kvm, u16 *rc, u16 *rrc) } gmap_split_huge_pages(kvm->arch.gmap); - cc = uv_call_sched(0, (u64)&uvcb); + cc = uv_call(0, (u64)&uvcb); *rc = uvcb.header.rc; *rrc = uvcb.header.rrc; KVM_UV_EVENT(kvm, 3, "PROTVIRT CREATE VM: handle %llx len %llx rc %x rrc %x flags %04x", @@ -837,7 +837,6 @@ int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size, while (offset < size) { ret = unpack_one(kvm, addr, tweak, offset, rc, rrc); if (ret == -EAGAIN) { - cond_resched(); if (fatal_signal_pending(current)) break; continue; @@ -880,7 +879,7 @@ int kvm_s390_pv_dump_cpu(struct kvm_vcpu *vcpu, void *buff, u16 *rc, u16 *rrc) }; int cc; - cc = uv_call_sched(0, (u64)&uvcb); + cc = uv_call(0, (u64)&uvcb); *rc = uvcb.header.rc; *rrc = uvcb.header.rrc; return cc; @@ -964,7 +963,7 @@ int kvm_s390_pv_dump_stor_state(struct kvm *kvm, void __user *buff_user, /* We will loop until the user buffer is filled or an error occurs */ do { /* Get 1MB worth of guest storage state data */ - cc = uv_call_sched(0, (u64)&uvcb); + cc = uv_call(0, (u64)&uvcb); /* All or nothing */ if (cc) { @@ -1042,7 +1041,7 @@ int kvm_s390_pv_dump_complete(struct kvm *kvm, void __user *buff_user, return -ENOMEM; complete.dump_area_origin = (u64)compl_data; - ret = uv_call_sched(0, (u64)&complete); + ret = uv_call(0, (u64)&complete); *rc = complete.header.rc; *rrc = complete.header.rrc; KVM_UV_EVENT(kvm, 3, "PROTVIRT DUMP COMPLETE: rc %x rrc %x", diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c index 0a7d8dfab6b7..95e9f0baed8d 100644 --- a/arch/s390/kvm/vsie.c +++ b/arch/s390/kvm/vsie.c @@ -1435,7 +1435,6 @@ static int vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) } if (sg) sg = gmap_put(sg); - cond_resched(); } if (sg) sg = gmap_put(sg); diff --git a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile index aa6cc6a1fe88..bb707700d7af 100644 --- a/arch/s390/lib/Makefile +++ b/arch/s390/lib/Makefile @@ -3,6 +3,8 @@ # Makefile for s390-specific library files.. # +CONTEXT_ANALYSIS := y + # string.o implements standard library functions like memset/memcpy etc. # Use -ffreestanding to ensure that the compiler does not try to "optimize" # them into calls to themselves. diff --git a/arch/s390/lib/spinlock.c b/arch/s390/lib/spinlock.c index 10db1e56a811..dbabca35c008 100644 --- a/arch/s390/lib/spinlock.c +++ b/arch/s390/lib/spinlock.c @@ -18,6 +18,7 @@ #include <asm/alternative.h> #include <asm/machine.h> #include <asm/asm.h> +#include <trace/events/lock.h> int spin_retry = -1; @@ -281,10 +282,12 @@ static inline void arch_spin_lock_classic(arch_spinlock_t *lp) void arch_spin_lock_wait(arch_spinlock_t *lp) { + trace_contention_begin(lp, LCB_F_SPIN); if (test_cpu_flag(CIF_DEDICATED_CPU)) arch_spin_lock_queued(lp); else arch_spin_lock_classic(lp); + trace_contention_end(lp, 0); } EXPORT_SYMBOL(arch_spin_lock_wait); diff --git a/arch/s390/mm/Makefile b/arch/s390/mm/Makefile index 193899c39ca7..7dea37a5ad3b 100644 --- a/arch/s390/mm/Makefile +++ b/arch/s390/mm/Makefile @@ -3,6 +3,8 @@ # Makefile for the linux s390-specific parts of the memory manager. # +CONTEXT_ANALYSIS := y + obj-y := init.o fault.o extmem.o mmap.o vmem.o maccess.o obj-y += page-states.o pageattr.o pgtable.o pgalloc.o extable.o diff --git a/arch/s390/mm/cmm.c b/arch/s390/mm/cmm.c index eb7ef63fab1e..d342b68ee9be 100644 --- a/arch/s390/mm/cmm.c +++ b/arch/s390/mm/cmm.c @@ -95,7 +95,6 @@ static long cmm_alloc_pages(long nr, long *counter, (*counter)++; spin_unlock(&cmm_lock); nr--; - cond_resched(); } return nr; } @@ -134,7 +133,6 @@ static long cmm_free_pages(long nr, long *counter, struct cmm_page_array **list) inc = __cmm_free_pages(inc, counter, list); if (inc) break; - cond_resched(); } return nr + inc; } diff --git a/arch/s390/mm/extable.c b/arch/s390/mm/extable.c index 063b4346742d..7498e858c401 100644 --- a/arch/s390/mm/extable.c +++ b/arch/s390/mm/extable.c @@ -50,6 +50,22 @@ static bool ex_handler_ua_load_reg(const struct exception_table_entry *ex, return true; } +static bool ex_handler_zeropad(const struct exception_table_entry *ex, struct pt_regs *regs) +{ + unsigned int reg_addr = FIELD_GET(EX_DATA_REG_ADDR, ex->data); + unsigned int reg_data = FIELD_GET(EX_DATA_REG_ERR, ex->data); + unsigned long data, addr, offset; + + addr = regs->gprs[reg_addr]; + offset = addr & (sizeof(unsigned long) - 1); + addr &= ~(sizeof(unsigned long) - 1); + data = *(unsigned long *)addr; + data <<= BITS_PER_BYTE * offset; + regs->gprs[reg_data] = data; + regs->psw.addr = extable_fixup(ex); + return true; +} + static bool ex_handler_fpc(const struct exception_table_entry *ex, struct pt_regs *regs) { fpu_sfpc(0); @@ -118,6 +134,8 @@ bool fixup_exception(struct pt_regs *regs) return ex_handler_ua_load_reg(ex, false, regs); case EX_TYPE_UA_LOAD_REGPAIR: return ex_handler_ua_load_reg(ex, true, regs); + case EX_TYPE_ZEROPAD: + return ex_handler_zeropad(ex, regs); case EX_TYPE_FPC: return ex_handler_fpc(ex, regs); case EX_TYPE_UA_MVCOS_TO: diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 028aeb9c48d6..46d828926009 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -406,12 +406,13 @@ NOKPROBE_SYMBOL(do_dat_exception); #if IS_ENABLED(CONFIG_KVM) void do_secure_storage_access(struct pt_regs *regs) +__context_unsafe(/* folio_walk_end() not instrumented */) { union teid teid = { .val = regs->int_parm_long }; unsigned long addr = get_fault_address(regs); + struct mm_struct *mm = current->mm; struct vm_area_struct *vma; struct folio_walk fw; - struct mm_struct *mm; struct folio *folio; int rc; @@ -427,10 +428,8 @@ void do_secure_storage_access(struct pt_regs *regs) * was not supposed to do, e.g. branching into secure * memory. Trigger a segmentation fault. */ - if (user_mode(regs)) { - send_sig(SIGSEGV, current, 0); - return; - } + if (user_mode(regs)) + return handle_fault_error_nolock(regs, SEGV_ACCERR); /* * The kernel should never run into this case and * there is no way out of this situation. @@ -438,13 +437,9 @@ void do_secure_storage_access(struct pt_regs *regs) panic("Unexpected PGM 0x3d with TEID bit 61=0"); } if (is_kernel_fault(regs)) { - folio = virt_to_folio((void *)addr); - if (unlikely(!folio_try_get(folio))) - return; - rc = uv_convert_from_secure(folio_to_phys(folio)); - if (!rc) - clear_bit(PG_arch_1, &folio->flags.f); - folio_put(folio); + if (is_vmalloc_addr((void *)addr)) + return handle_fault_error_nolock(regs, 0); + rc = uv_convert_from_secure(__pa(addr)); /* * There are some valid fixup types for kernel * accesses to donated secure memory. zeropad is one @@ -453,25 +448,22 @@ void do_secure_storage_access(struct pt_regs *regs) if (rc) return handle_fault_error_nolock(regs, 0); } else { - if (faulthandler_disabled()) + if (faulthandler_disabled() || !mm) return handle_fault_error_nolock(regs, 0); - mm = current->mm; - mmap_read_lock(mm); - vma = find_vma(mm, addr); + vma = lock_mm_and_find_vma(mm, addr, regs); if (!vma) - return handle_fault_error(regs, SEGV_MAPERR); + return handle_fault_error_nolock(regs, SEGV_MAPERR); folio = folio_walk_start(&fw, vma, addr, 0); - if (!folio) { - mmap_read_unlock(mm); - return; - } + if (!folio) + goto out; /* arch_make_folio_accessible() needs a raised refcount. */ folio_get(folio); rc = arch_make_folio_accessible(folio); folio_put(folio); folio_walk_end(&fw, vma); if (rc) - send_sig(SIGSEGV, current, 0); + return handle_fault_error(regs, SEGV_ACCERR); +out: mmap_read_unlock(mm); } } diff --git a/arch/s390/mm/gmap_helpers.c b/arch/s390/mm/gmap_helpers.c index cd5fded159c0..ff63ffb1dbd2 100644 --- a/arch/s390/mm/gmap_helpers.c +++ b/arch/s390/mm/gmap_helpers.c @@ -40,6 +40,7 @@ * and locked. */ pte_t *try_get_locked_pte(struct mm_struct *mm, unsigned long vmaddr, spinlock_t **ptl) +__context_unsafe(/* Returns nonnull if lock taken or not taken */) { pmd_t *pmdp, pmd, pmdval; pud_t *pudp, pud; @@ -90,6 +91,7 @@ EXPORT_SYMBOL_GPL(try_get_locked_pte); * Context: needs to be called while holding the mmap lock. */ void gmap_helper_zap_one_page(struct mm_struct *mm, unsigned long vmaddr) +__context_unsafe(/* pte_unmap_unlock() not instrumented */) { struct vm_area_struct *vma; spinlock_t *ptl; /* Lock for the host (userspace) page table */ @@ -161,6 +163,7 @@ EXPORT_SYMBOL_GPL(gmap_helper_discard); * disabled. */ void gmap_helper_try_set_pte_unused(struct mm_struct *mm, unsigned long vmaddr) +__context_unsafe(/* pte_unmap_unlock() not instrumented */) { spinlock_t *ptl; /* Lock for the host (userspace) page table */ pte_t *ptep; @@ -272,7 +275,6 @@ retry: * truncation. In that case, the shared zeropage would be gone * and we can simply retry and make progress. */ - cond_resched(); goto retry; } diff --git a/arch/s390/mm/hugetlbpage.c b/arch/s390/mm/hugetlbpage.c index db35d8fe8609..f84aa9265430 100644 --- a/arch/s390/mm/hugetlbpage.c +++ b/arch/s390/mm/hugetlbpage.c @@ -147,10 +147,12 @@ void __set_huge_pte_at(struct mm_struct *mm, unsigned long addr, if (likely(pte_present(pte))) rste |= _REGION3_ENTRY_LARGE; rste |= _REGION_ENTRY_TYPE_R3; - } else if (likely(pte_present(pte))) - rste |= _SEGMENT_ENTRY_LARGE; - - set_pte(ptep, __pte(rste)); + set_pud((pud_t *)ptep, __pud(rste)); + } else { + if (likely(pte_present(pte))) + rste |= _SEGMENT_ENTRY_LARGE; + set_pmd((pmd_t *)ptep, __pmd(rste)); + } } void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 8d1de5a2e554..be7e009e7b59 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -166,7 +166,7 @@ static void __init pv_init(void) virtio_set_mem_acc_cb(virtio_require_restricted_mem_acc); /* make sure bounce buffers are shared */ - swiotlb_init(true, SWIOTLB_VERBOSE); + swiotlb_init(true, SWIOTLB_VERBOSE | SWIOTLB_ANY); swiotlb_update_mem_attributes(); } diff --git a/arch/s390/mm/maccess.c b/arch/s390/mm/maccess.c index cfd219fe495c..f39968dd8063 100644 --- a/arch/s390/mm/maccess.c +++ b/arch/s390/mm/maccess.c @@ -96,7 +96,7 @@ size_t memcpy_real_iter(struct iov_iter *iter, unsigned long src, size_t count) pte = mk_pte_phys(phys, PAGE_KERNEL_RO); mutex_lock(&memcpy_real_mutex); - if (pte_val(pte) != pte_val(*memcpy_real_ptep)) { + if (pte_val(pte) != pte_val(ptep_get(memcpy_real_ptep))) { __ptep_ipte(__memcpy_real_area, memcpy_real_ptep, 0, 0, IPTE_GLOBAL); set_pte(memcpy_real_ptep, pte); } diff --git a/arch/s390/mm/pageattr.c b/arch/s390/mm/pageattr.c index e6f788696dd1..1e202e3d08e7 100644 --- a/arch/s390/mm/pageattr.c +++ b/arch/s390/mm/pageattr.c @@ -105,7 +105,6 @@ static int walk_pte_level(pmd_t *pmdp, unsigned long addr, unsigned long end, pgt_set((unsigned long *)ptep, pte_val(new), addr, CRDTE_DTT_PAGE); ptep++; addr += PAGE_SIZE; - cond_resched(); } while (addr < end); return 0; } @@ -194,7 +193,6 @@ static int walk_pmd_level(pud_t *pudp, unsigned long addr, unsigned long end, } pmdp++; addr = next; - cond_resched(); } while (addr < end); return rc; } @@ -281,7 +279,6 @@ static int walk_pud_level(p4d_t *p4d, unsigned long addr, unsigned long end, } pudp++; addr = next; - cond_resched(); } while (addr < end && !rc); return rc; } @@ -301,7 +298,6 @@ static int walk_p4d_level(pgd_t *pgd, unsigned long addr, unsigned long end, rc = walk_pud_level(p4dp, addr, next, flags); p4dp++; addr = next; - cond_resched(); } while (addr < end && !rc); return rc; } @@ -323,7 +319,6 @@ static int change_page_attr(unsigned long addr, unsigned long end, rc = walk_p4d_level(pgdp, addr, next, flags); if (rc) break; - cond_resched(); } while (pgdp++, addr = next, addr < end && !rc); return rc; } diff --git a/arch/s390/mm/pgalloc.c b/arch/s390/mm/pgalloc.c index 7ac44543e051..9610770fcf6d 100644 --- a/arch/s390/mm/pgalloc.c +++ b/arch/s390/mm/pgalloc.c @@ -268,7 +268,6 @@ static int base_segment_walk(unsigned long *origin, unsigned long addr, return rc; if (!alloc) base_pgt_free(table); - cond_resched(); } while (ste++, addr = next, addr < end); return 0; } diff --git a/arch/s390/net/Makefile b/arch/s390/net/Makefile index 9275cf63192a..0a7323c9b372 100644 --- a/arch/s390/net/Makefile +++ b/arch/s390/net/Makefile @@ -2,5 +2,8 @@ # # Arch-specific network modules # + +CONTEXT_ANALYSIS := y + obj-$(CONFIG_BPF_JIT) += bpf_jit_comp.o bpf_timed_may_goto.o obj-$(CONFIG_HAVE_PNETID) += pnet.o diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c index c46872b071ce..c4b47070bb59 100644 --- a/arch/s390/net/bpf_jit_comp.c +++ b/arch/s390/net/bpf_jit_comp.c @@ -21,6 +21,7 @@ #include <linux/filter.h> #include <linux/init.h> #include <linux/bpf.h> +#include <linux/cfi.h> #include <linux/mm.h> #include <linux/kernel.h> #include <asm/cacheflush.h> @@ -356,6 +357,19 @@ static void emit6_pcrel_rilc(struct bpf_jit *jit, u32 op, u8 mask, s64 pcrel) } \ }) +static inline void emit_u32_data(const u32 data, struct bpf_jit *jit) +{ + if (jit->prg_buf) + *(u32 *)(jit->prg_buf + jit->prg) = data; + jit->prg += 4; +} + +static inline void emit_kcfi(u32 hash, struct bpf_jit *jit) +{ + if (IS_ENABLED(CONFIG_CFI)) + emit_u32_data(hash, jit); +} + /* * Return whether this is the first pass. The first pass is special, since we * don't know any sizes yet, and thus must be conservative. @@ -597,6 +611,8 @@ static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp) { BUILD_BUG_ON(sizeof(struct prog_frame) != STACK_FRAME_OVERHEAD); + emit_kcfi(bpf_is_subprog(fp) ? cfi_bpf_subprog_hash : cfi_bpf_hash, jit); + /* No-op for hotpatching */ /* brcl 0,prologue_plt */ EMIT6_PCREL_RILC(0xc0040000, 0, jit->prologue_plt); @@ -616,7 +632,7 @@ static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp) bpf_skip(jit, 6); } /* Tail calls have to skip above initialization */ - jit->tail_call_start = jit->prg; + jit->tail_call_start = jit->prg - cfi_get_offset(); if (fp->aux->exception_cb) { /* * Switch stack, the new address is in the 2nd parameter. @@ -2431,11 +2447,13 @@ skip_init_ctx: jit_data->ctx = jit; jit_data->pass = pass; } - fp->bpf_func = (void *) jit.prg_buf; + fp->bpf_func = (void *)jit.prg_buf + cfi_get_offset(); fp->jited = 1; - fp->jited_len = jit.size; + fp->jited_len = jit.size - cfi_get_offset(); if (!fp->is_func || extra_pass) { + for (int i = 0; i < fp->len; i++) + jit.addrs[i] -= cfi_get_offset(); bpf_prog_fill_jited_linfo(fp, jit.addrs + 1); free_addrs: kvfree(jit.addrs); @@ -2701,8 +2719,10 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, return -ENOTSUPP; /* Return to %r14 in the struct_ops case. */ - if (flags & BPF_TRAMP_F_INDIRECT) + if (flags & BPF_TRAMP_F_INDIRECT) { flags |= BPF_TRAMP_F_SKIP_FRAME; + emit_kcfi(cfi_get_func_hash(func_addr), jit); + } /* * Compute how many arguments we need to pass to BPF programs. diff --git a/arch/s390/pci/Makefile b/arch/s390/pci/Makefile index 1810e0944a4e..b8be1316491b 100644 --- a/arch/s390/pci/Makefile +++ b/arch/s390/pci/Makefile @@ -3,6 +3,8 @@ # Makefile for the s390 PCI subsystem. # +CONTEXT_ANALYSIS := y + obj-$(CONFIG_PCI) += pci.o pci_irq.o pci_clp.o \ pci_event.o pci_debug.o pci_insn.o pci_mmio.o \ pci_bus.o pci_kvm_hook.o pci_report.o pci_fixup.o diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 2d377c2e194d..d64c544b32b0 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -1174,10 +1174,8 @@ int zpci_scan_devices(void) return rc; zpci_add_devices(&scan_list); - zpci_bus_for_each(zbus) { + zpci_bus_for_each(zbus) zpci_bus_scan_bus(zbus); - cond_resched(); - } return 0; } diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c index 5829ad398800..f317a1465dad 100644 --- a/arch/s390/pci/pci_event.c +++ b/arch/s390/pci/pci_event.c @@ -323,6 +323,12 @@ static void zpci_event_io_failure(struct pci_dev *pdev, pci_channel_state_t es, pci_dev_unlock(pdev); } +static void __zpci_event_print_error(struct pci_dev *pdev, struct zpci_ccdf_err *ccdf) +{ + pr_err("%s: Event 0x%x reports an error for PCI function 0x%x\n", + pdev ? pci_name(pdev) : "n/a", ccdf->pec, ccdf->fid); +} + static void __zpci_event_error(struct zpci_ccdf_err *ccdf) { struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid); @@ -336,24 +342,24 @@ static void __zpci_event_error(struct zpci_ccdf_err *ccdf) zpci_err("error CCDF:\n"); zpci_err_hex(ccdf, sizeof(*ccdf)); - if (zdev) { - mutex_lock(&zdev->state_lock); - rc = clp_refresh_fh(zdev->fid, &fh); - if (rc) - goto no_pdev; - if (!fh || ccdf->fh != fh) { - /* Ignore events with stale handles */ - zpci_dbg(3, "err fid:%x, fh:%x (stale %x)\n", - ccdf->fid, fh, ccdf->fh); - goto no_pdev; - } - zpci_update_fh(zdev, ccdf->fh); - if (zdev->zbus->bus) - pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn); + if (!zdev) + return __zpci_event_print_error(NULL, ccdf); + + mutex_lock(&zdev->state_lock); + rc = clp_refresh_fh(zdev->fid, &fh); + if (rc) + goto no_pdev; + if (!fh || ccdf->fh != fh) { + /* Ignore events with stale handles */ + zpci_dbg(3, "err fid:%x, fh:%x (stale %x)\n", + ccdf->fid, fh, ccdf->fh); + goto no_pdev; } + zpci_update_fh(zdev, ccdf->fh); + if (zdev->zbus->bus) + pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn); - pr_err("%s: Event 0x%x reports an error for PCI function 0x%x\n", - pdev ? pci_name(pdev) : "n/a", ccdf->pec, ccdf->fid); + __zpci_event_print_error(pdev, ccdf); if (!pdev) goto no_pdev; @@ -375,8 +381,7 @@ static void __zpci_event_error(struct zpci_ccdf_err *ccdf) } pci_dev_put(pdev); no_pdev: - if (zdev) - mutex_unlock(&zdev->state_lock); + mutex_unlock(&zdev->state_lock); zpci_zdev_put(zdev); } @@ -417,98 +422,106 @@ static void zpci_event_reappear(struct zpci_dev *zdev) zpci_dbg(1, "rea fid:%x, fh:%x\n", zdev->fid, zdev->fh); } -static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf) +static bool zpci_event_avail_any_device(struct zpci_ccdf_avail *ccdf) { - struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid); - bool existing_zdev = !!zdev; - enum zpci_state state; - - zpci_dbg(3, "avl fid:%x, fh:%x, pec:%x\n", - ccdf->fid, ccdf->fh, ccdf->pec); + /* 0x0306 - No handle or fid stored */ + if (ccdf->pec != 0x0306) + return false; + /* 0x308 or 0x302 for multiple devices */ + zpci_remove_reserved_devices(); + zpci_scan_devices(); + return true; +} - if (existing_zdev) - mutex_lock(&zdev->state_lock); +static void zpci_event_avail_new_device(struct zpci_ccdf_avail *ccdf) +{ + struct zpci_dev *zdev; switch (ccdf->pec) { case 0x0301: /* Reserved|Standby -> Configured */ - if (!zdev) { - zdev = zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_CONFIGURED); - if (IS_ERR(zdev)) - break; - if (zpci_add_device(zdev)) { - kfree(zdev); - break; - } - } else { - if (zdev->state == ZPCI_FN_STATE_RESERVED) - zpci_event_reappear(zdev); - /* the configuration request may be stale */ - else if (zdev->state != ZPCI_FN_STATE_STANDBY) - break; - zdev->state = ZPCI_FN_STATE_CONFIGURED; + zdev = zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_CONFIGURED); + if (IS_ERR(zdev)) + break; + if (zpci_add_device(zdev)) { + kfree(zdev); + break; } zpci_scan_configured_device(zdev, ccdf->fh); break; case 0x0302: /* Reserved -> Standby */ - if (!zdev) { - zdev = zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_STANDBY); - if (IS_ERR(zdev)) - break; - if (zpci_add_device(zdev)) { - kfree(zdev); - break; - } - } else { - if (zdev->state == ZPCI_FN_STATE_RESERVED) - zpci_event_reappear(zdev); - zpci_update_fh(zdev, ccdf->fh); + zdev = zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_STANDBY); + if (IS_ERR(zdev)) + break; + if (zpci_add_device(zdev)) { + kfree(zdev); + break; } break; + } +} + +static void zpci_event_avail_existing_device(struct zpci_dev *zdev, struct zpci_ccdf_avail *ccdf) +{ + enum zpci_state state; + + switch (ccdf->pec) { + case 0x0301: /* Reserved|Standby -> Configured */ + if (zdev->state == ZPCI_FN_STATE_RESERVED) + zpci_event_reappear(zdev); + /* the configuration request may be stale */ + else if (zdev->state != ZPCI_FN_STATE_STANDBY) + break; + zdev->state = ZPCI_FN_STATE_CONFIGURED; + zpci_scan_configured_device(zdev, ccdf->fh); + break; + case 0x0302: /* Reserved -> Standby */ + if (zdev->state == ZPCI_FN_STATE_RESERVED) + zpci_event_reappear(zdev); + zpci_update_fh(zdev, ccdf->fh); + break; case 0x0303: /* Deconfiguration requested */ - if (zdev) { - /* The event may have been queued before we configured - * the device. - */ - if (zdev->state != ZPCI_FN_STATE_CONFIGURED) - break; - zpci_update_fh(zdev, ccdf->fh); - zpci_deconfigure_device(zdev); - } + /* The event may have been queued before we configured + * the device. + */ + if (zdev->state != ZPCI_FN_STATE_CONFIGURED) + break; + zpci_update_fh(zdev, ccdf->fh); + zpci_deconfigure_device(zdev); break; case 0x0304: /* Configured -> Standby|Reserved */ - if (zdev) { - /* The event may have been queued before we configured - * the device.: - */ - if (zdev->state == ZPCI_FN_STATE_CONFIGURED) - zpci_event_hard_deconfigured(zdev, ccdf->fh); - /* The 0x0304 event may immediately reserve the device */ - if (!clp_get_state(zdev->fid, &state) && - state == ZPCI_FN_STATE_RESERVED) { - zpci_device_reserved(zdev); - } + /* The event may have been queued before we configured + * the device.: + */ + if (zdev->state == ZPCI_FN_STATE_CONFIGURED) + zpci_event_hard_deconfigured(zdev, ccdf->fh); + /* The 0x0304 event may immediately reserve the device */ + if (!clp_get_state(zdev->fid, &state) && + state == ZPCI_FN_STATE_RESERVED) { + zpci_device_reserved(zdev); } break; - case 0x0306: /* 0x308 or 0x302 for multiple devices */ - zpci_remove_reserved_devices(); - zpci_scan_devices(); - break; case 0x0308: /* Standby -> Reserved */ - if (!zdev) - break; zpci_device_reserved(zdev); break; - default: - break; - } - if (existing_zdev) { - mutex_unlock(&zdev->state_lock); - zpci_zdev_put(zdev); } } void zpci_event_availability(void *data) { - if (zpci_is_enabled()) - __zpci_event_availability(data); + struct zpci_ccdf_avail *ccdf = data; + struct zpci_dev *zdev; + + if (!zpci_is_enabled()) + return; + zpci_dbg(3, "avl fid:%x, fh:%x, pec:%x\n", + ccdf->fid, ccdf->fh, ccdf->pec); + if (zpci_event_avail_any_device(ccdf)) + return; + zdev = get_zdev_by_fid(ccdf->fid); + if (!zdev) + return zpci_event_avail_new_device(ccdf); + mutex_lock(&zdev->state_lock); + zpci_event_avail_existing_device(zdev, ccdf); + mutex_unlock(&zdev->state_lock); + zpci_zdev_put(zdev); } diff --git a/arch/s390/purgatory/Makefile b/arch/s390/purgatory/Makefile index e74410bb1b88..b2f17e0d7fd6 100644 --- a/arch/s390/purgatory/Makefile +++ b/arch/s390/purgatory/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 +CONTEXT_ANALYSIS := y + purgatory-y := head.o purgatory.o string.o sha256.o targets += $(purgatory-y) purgatory.lds purgatory purgatory.chk purgatory.ro diff --git a/arch/s390/tools/relocs.c b/arch/s390/tools/relocs.c index 30a732c808f3..72178dc90a4c 100644 --- a/arch/s390/tools/relocs.c +++ b/arch/s390/tools/relocs.c @@ -41,6 +41,10 @@ static Elf_Ehdr ehdr; static unsigned long shnum; static unsigned int shstrndx; +static unsigned int shsymtabndx; +static unsigned int shxsymtabndx; + +static int sym_index(Elf_Sym *sym); struct relocs { uint32_t *offset; @@ -54,11 +58,40 @@ static struct relocs relocs64; struct section { Elf_Shdr shdr; struct section *link; + Elf_Sym *symtab; + Elf32_Word *xsymtab; Elf_Rel *reltab; + char *strtab; }; static struct section *secs; +static const char *sec_name(unsigned shndx) +{ + const char *sec_strtab; + const char *name = "<noname>"; + sec_strtab = secs[shstrndx].strtab; + + if (shndx < shnum) + name = sec_strtab + secs[shndx].shdr.sh_name; + else if (shndx == SHN_ABS) + name = "ABSOLUTE"; + else if (shndx == SHN_COMMON) + name = "COMMON"; + return name; +} + +static const char *sym_name(const char *sym_strtab, Elf_Sym *sym) +{ + const char *name; + + if (sym->st_name) + name = sym_strtab + sym->st_name; + else + name = sec_name(sym_index(sym)); + return name; +} + #if BYTE_ORDER == LITTLE_ENDIAN #define le16_to_cpu(val) (val) #define le32_to_cpu(val) (val) @@ -105,6 +138,23 @@ static uint64_t elf64_to_cpu(uint64_t val) #define elf_off_to_cpu(x) elf64_to_cpu(x) #define elf_xword_to_cpu(x) elf64_to_cpu(x) +static int sym_index(Elf_Sym *sym) +{ + Elf_Sym *symtab = secs[shsymtabndx].symtab; + Elf32_Word *xsymtab = secs[shxsymtabndx].xsymtab; + unsigned long offset; + int index; + + if (sym->st_shndx != SHN_XINDEX) + return sym->st_shndx; + + /* calculate offset of sym from head of table. */ + offset = (unsigned long)sym - (unsigned long)symtab; + index = offset / sizeof(*sym); + + return elf32_to_cpu(xsymtab[index]); +} + static void die(char *fmt, ...) { va_list ap; @@ -216,6 +266,81 @@ static void read_shdrs(FILE *fp) } +static void read_strtabs(FILE *fp) +{ + int i; + + for (i = 0; i < shnum; i++) { + struct section *sec = &secs[i]; + + if (sec->shdr.sh_type != SHT_STRTAB) + continue; + + sec->strtab = malloc(sec->shdr.sh_size); + if (!sec->strtab) + die("malloc of %" FMT " bytes for strtab failed\n", sec->shdr.sh_size); + + if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) + die("Seek to %" FMT " failed: %s\n", sec->shdr.sh_offset, strerror(errno)); + + if (fread(sec->strtab, 1, sec->shdr.sh_size, fp) != sec->shdr.sh_size) + die("Cannot read symbol table: %s\n", strerror(errno)); + } +} + +static void read_symtabs(FILE *fp) +{ + int i, j; + + for (i = 0; i < shnum; i++) { + struct section *sec = &secs[i]; + int num_syms; + + switch (sec->shdr.sh_type) { + case SHT_SYMTAB_SHNDX: + sec->xsymtab = malloc(sec->shdr.sh_size); + if (!sec->xsymtab) + die("malloc of %" FMT " bytes for xsymtab failed\n", sec->shdr.sh_size); + + if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) + die("Seek to %" FMT " failed: %s\n", sec->shdr.sh_offset, strerror(errno)); + + if (fread(sec->xsymtab, 1, sec->shdr.sh_size, fp) != sec->shdr.sh_size) + die("Cannot read extended symbol table: %s\n", strerror(errno)); + + shxsymtabndx = i; + continue; + + case SHT_SYMTAB: + num_syms = sec->shdr.sh_size / sizeof(Elf_Sym); + + sec->symtab = malloc(sec->shdr.sh_size); + if (!sec->symtab) + die("malloc of %" FMT " bytes for symtab failed\n", sec->shdr.sh_size); + + if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) + die("Seek to %" FMT " failed: %s\n", sec->shdr.sh_offset, strerror(errno)); + + if (fread(sec->symtab, 1, sec->shdr.sh_size, fp) != sec->shdr.sh_size) + die("Cannot read symbol table: %s\n", strerror(errno)); + + for (j = 0; j < num_syms; j++) { + Elf_Sym *sym = &sec->symtab[j]; + + sym->st_name = elf_word_to_cpu(sym->st_name); + sym->st_value = elf_addr_to_cpu(sym->st_value); + sym->st_size = elf_xword_to_cpu(sym->st_size); + sym->st_shndx = elf_half_to_cpu(sym->st_shndx); + } + shsymtabndx = i; + continue; + + default: + continue; + } + } +} + static void read_relocs(FILE *fp) { int i, j; @@ -263,7 +388,8 @@ static void add_reloc(struct relocs *r, uint32_t offset) r->offset[r->count++] = offset; } -static int do_reloc(struct section *sec, Elf_Rel *rel) +static int do_reloc(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym, + const char *symname) { unsigned int r_type = ELF64_R_TYPE(rel->r_info); ElfW(Addr) offset = rel->r_offset; @@ -279,6 +405,22 @@ static int do_reloc(struct section *sec, Elf_Rel *rel) case R_390_GOTPCDBL: case R_390_GOTOFF64: break; + case R_390_32: { + static const char kcfipfx[] = "__kcfi_typeid_"; + + if (sym->st_shndx != SHN_ABS) + die("Unsupported relocation type: %d\n", r_type); + /* + * Symbols with __kcfi_typeid_ prefix have constant values, + * which do not change if bzImage is loaded at a different + * physical address than the address for which it has been + * compiled. + */ + if (!strncmp(kcfipfx, symname, sizeof(kcfipfx) - 1)) + break; + die("Invalid absolute R_390_32 relocation: %s\n", symname); + break; + } case R_390_64: add_reloc(&relocs64, offset); break; @@ -296,21 +438,29 @@ static void walk_relocs(void) /* Walk through the relocations */ for (i = 0; i < shnum; i++) { - struct section *sec_applies; + char *sym_strtab; + Elf_Sym *sh_symtab; + struct section *sec_applies, *sec_symtab; int j; struct section *sec = &secs[i]; if (sec->shdr.sh_type != SHT_REL_TYPE) continue; + sec_symtab = sec->link; sec_applies = &secs[sec->shdr.sh_info]; if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) continue; + sh_symtab = sec_symtab->symtab; + sym_strtab = sec_symtab->link->strtab; + for (j = 0; j < sec->shdr.sh_size / sizeof(Elf_Rel); j++) { Elf_Rel *rel = &sec->reltab[j]; + Elf_Sym *sym = &sh_symtab[ELF_R_SYM(rel->r_info)]; + const char *symname = sym_name(sym_strtab, sym); - do_reloc(sec, rel); + do_reloc(sec, rel, sym, symname); } } } @@ -349,6 +499,8 @@ static void process(FILE *fp) { read_ehdr(fp); read_shdrs(fp); + read_strtabs(fp); + read_symtabs(fp); read_relocs(fp); emit_relocs(); } diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index d6d8386d3f02..00e2562041fd 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -71,6 +71,11 @@ depends on RISCV source "drivers/cpuidle/Kconfig.riscv" endmenu +menu "S390 CPU Idle Drivers" +depends on S390 +source "drivers/cpuidle/Kconfig.s390" +endmenu + config HALTPOLL_CPUIDLE tristate "Halt poll cpuidle driver" depends on X86 && KVM_GUEST diff --git a/drivers/cpuidle/Kconfig.s390 b/drivers/cpuidle/Kconfig.s390 new file mode 100644 index 000000000000..c9acf7ba8eee --- /dev/null +++ b/drivers/cpuidle/Kconfig.s390 @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# S390 CPU Idle drivers +# + +config S390_CPUIDLE + bool "S390 CPU idle driver" + default y + help + Select this option to enable processor idle state management + through cpuidle subsystem. diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 1de9e92c5b0f..88cbc2a7aea8 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -42,3 +42,7 @@ obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o ############################################################################### # RISC-V drivers obj-$(CONFIG_RISCV_SBI_CPUIDLE) += cpuidle-riscv-sbi.o + +############################################################################### +# S390 drivers +obj-$(CONFIG_S390_CPUIDLE) += cpuidle-s390.o diff --git a/drivers/cpuidle/cpuidle-s390.c b/drivers/cpuidle/cpuidle-s390.c new file mode 100644 index 000000000000..1d02a77ebce0 --- /dev/null +++ b/drivers/cpuidle/cpuidle-s390.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * s390 generic CPU idle driver. + * + * Copyright IBM Corp. 2026 + */ + +#define pr_fmt(fmt) "CPUidle s390: " fmt + +#include <linux/init.h> +#include <linux/cpuidle.h> +#include <linux/cpu.h> +#include <linux/sched/clock.h> + +static __cpuidle int s390_enter_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + arch_cpu_idle(); + return index; +} + +static struct cpuidle_driver s390_cpuidle_driver = { + .cpumask = (struct cpumask *)cpu_present_mask, + .name = "s390-idle", + .states = { + { /* entry 0 is for polling */}, + { + .enter = s390_enter_idle, + .name = "IDLE", + .desc = "ENABLED WAIT", + }, + }, + .safe_state_index = 0, + .state_count = 2, +}; + +static int s390_cpuidle_cpu_online(unsigned int cpu) +{ + struct cpuidle_device *dev = &per_cpu(cpuidle_dev, cpu); + int rc; + + if (dev->registered) { + cpuidle_pause_and_lock(); + rc = cpuidle_enable_device(dev); + cpuidle_resume_and_unlock(); + if (rc) + pr_err("Failed to enable cpuidle device on cpu %u\n", cpu); + } else { + dev->cpu = cpu; + rc = cpuidle_register_device(dev); + if (rc) + pr_err("Failed to register cpuidle driver on cpu %u\n", cpu); + } + return rc; +} + +static int s390_cpuidle_cpu_dead(unsigned int cpu) +{ + struct cpuidle_device *dev = &per_cpu(cpuidle_dev, cpu); + + if (!dev->registered) + return 0; + cpuidle_pause_and_lock(); + cpuidle_disable_device(dev); + cpuidle_resume_and_unlock(); + return 0; +} + +/* + * The target_residency and exit_latency values are benchmark-derived estimates + * that remain non-deterministic due to s390's virtualized architecture. + * + * Configuration strategy: + * - Poll idle state: Values derived from the next enabled idle state (EW) + * - Enabled Wait state: Values selected based on idle behavior and empirical + * measurement data + * + * Goal is to improve responsiveness for workloads with frequent sleep/wakeup + * cycles while minimizing any side effects. + */ +static void __init s390_cpuidle_ew_tune(void) +{ + struct cpuidle_state *state = &s390_cpuidle_driver.states[1]; + + if (machine_is_lpar()) { + state->target_residency = 5; + state->exit_latency = 5; + } else { + state->target_residency = 1; + state->exit_latency = 1; + } +} + +static int __init s390_cpuidle_init(void) +{ + int rc; + + s390_cpuidle_ew_tune(); + cpuidle_poll_state_init(&s390_cpuidle_driver); + rc = cpuidle_register(&s390_cpuidle_driver, NULL); + if (rc) + return rc; + rc = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, + "cpuidle/s390:online", + s390_cpuidle_cpu_online, + s390_cpuidle_cpu_dead); + if (rc < 0) { + cpuidle_unregister(&s390_cpuidle_driver); + pr_err("Failed to allocate hotplug state: cpuidle/s390:online\n"); + return rc; + } + return 0; +} +device_initcall(s390_cpuidle_init); diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 126a87c3c6f8..0ad960d000a2 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -3,6 +3,8 @@ # S/390 character devices # +CONTEXT_ANALYSIS := y + ifdef CONFIG_FUNCTION_TRACER # Do not trace early setup code CFLAGS_REMOVE_sclp_early_core.o = $(CC_FLAGS_FTRACE) diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 46265f4fbfd8..57fefa8986d6 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -481,6 +481,7 @@ static unsigned int raw3215_drop(struct raw3215_info *raw) */ static unsigned int raw3215_make_room(struct raw3215_info *raw, unsigned int length, bool drop) + __must_hold(get_ccwdev_lock(raw->cdev)) { while (RAW3215_BUFFER_SIZE - raw->count < length) { if (drop) diff --git a/drivers/s390/char/sclp_pci.c b/drivers/s390/char/sclp_pci.c index d61a7fc0dd61..b79ef7006e46 100644 --- a/drivers/s390/char/sclp_pci.c +++ b/drivers/s390/char/sclp_pci.c @@ -99,6 +99,9 @@ static int sclp_pci_check_report(struct zpci_report_error_header *report) case SCLP_ERRNOTIFY_AQ_INFO_LOG: case SCLP_ERRNOTIFY_AQ_OPTICS_DATA: case SCLP_ERRNOTIFY_AQ_NVME_SMART_LOG: + case SCLP_ERRNOTIFY_AQ_ADAPTER_INITIALIZED: + case SCLP_ERRNOTIFY_AQ_RECOVERABLE_ERROR: + case SCLP_ERRNOTIFY_AQ_TELEMETRY_DATA: break; default: return -EINVAL; diff --git a/drivers/s390/char/uvdevice.c b/drivers/s390/char/uvdevice.c index e6a264c996ce..4d274a2ee84d 100644 --- a/drivers/s390/char/uvdevice.c +++ b/drivers/s390/char/uvdevice.c @@ -224,7 +224,7 @@ static int uvio_attestation(struct uvio_ioctl_cb *uv_ioctl) if (ret) goto out; - uv_call_sched(0, (u64)uvcb_attest); + uv_call(0, (u64)uvcb_attest); uv_ioctl->uv_rc = uvcb_attest->header.rc; uv_ioctl->uv_rrc = uvcb_attest->header.rrc; @@ -291,7 +291,7 @@ static int uvio_add_secret(struct uvio_ioctl_cb *uv_ioctl) ret = 0; uvcb.addr = (u64)asrcb; - uv_call_sched(0, (u64)&uvcb); + uv_call(0, (u64)&uvcb); uv_ioctl->uv_rc = uvcb.header.rc; uv_ioctl->uv_rrc = uvcb.header.rrc; @@ -457,7 +457,7 @@ static int uvio_retr_secret(struct uvio_ioctl_cb *uv_ioctl) uvcb.buf_addr = (u64)buf; uvcb.buf_size = buf_len; - uv_call_sched(0, (u64)&uvcb); + uv_call(0, (u64)&uvcb); if (copy_to_user((__user void *)uv_ioctl->argument_addr, buf, buf_len)) goto err; diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile index 3bd1c245183f..611a39103df9 100644 --- a/drivers/s390/cio/Makefile +++ b/drivers/s390/cio/Makefile @@ -3,6 +3,8 @@ # Makefile for the S/390 common i/o drivers # +CONTEXT_ANALYSIS := y + # The following is required for define_trace.h to find ./trace.h CFLAGS_trace.o := -I$(src) CFLAGS_vfio_ccw_trace.o := -I$(src) diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index e5a0ec6b4e3e..54b8b8639694 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -618,11 +618,6 @@ static int slow_eval_known_fn(struct subchannel *sch, void *data) rc = css_evaluate_known_subchannel(sch, 1); if (rc == -EAGAIN) css_schedule_eval(sch->schid); - /* - * The loop might take long time for platforms with lots of - * known devices. Allow scheduling here. - */ - cond_resched(); } return 0; } @@ -654,9 +649,6 @@ static int slow_eval_unknown_fn(struct subchannel_id schid, void *data) default: rc = 0; } - /* Allow scheduling here since the containing loop might - * take a while. */ - cond_resched(); } return rc; } diff --git a/drivers/s390/cio/vfio_ccw_drv.c b/drivers/s390/cio/vfio_ccw_drv.c index ab6b518cc353..7546bd137e4e 100644 --- a/drivers/s390/cio/vfio_ccw_drv.c +++ b/drivers/s390/cio/vfio_ccw_drv.c @@ -35,6 +35,7 @@ debug_info_t *vfio_ccw_debug_trace_id; * Helpers */ int vfio_ccw_sch_quiesce(struct subchannel *sch) + __must_hold(&sch->lock) { struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev); struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev); diff --git a/drivers/s390/cio/vfio_ccw_private.h b/drivers/s390/cio/vfio_ccw_private.h index 3bd0171d38d0..fd92b41af656 100644 --- a/drivers/s390/cio/vfio_ccw_private.h +++ b/drivers/s390/cio/vfio_ccw_private.h @@ -134,7 +134,8 @@ struct vfio_ccw_private { struct work_struct notoper_work; } __aligned(8); -int vfio_ccw_sch_quiesce(struct subchannel *sch); +int vfio_ccw_sch_quiesce(struct subchannel *sch) + __must_hold(&sch->lock); void vfio_ccw_sch_io_todo(struct work_struct *work); void vfio_ccw_crw_todo(struct work_struct *work); void vfio_ccw_notoper_todo(struct work_struct *work); diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 2d4e00a1e48c..d82df5b4e2db 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -1173,8 +1173,8 @@ bool ap_bus_force_rescan(void) * for the lock which means the other task has finished and * stored the result in ap_scan_bus_result. */ - if (mutex_lock_interruptible(&ap_scan_bus_mutex)) { - /* some error occurred, ignore and go out */ + if (mutex_lock_killable(&ap_scan_bus_mutex)) { + /* fatal signal received, go out */ goto out; } rc = ap_scan_bus_result; diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index b2e57e5d6c3f..fb4d678336e4 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -181,7 +181,7 @@ struct ap_card { bool chkstop; /* checkstop state */ }; -#define TAPQ_CARD_HWINFO_MASK 0xFFFF0000FFFF0F0FUL +#define TAPQ_CARD_HWINFO_MASK 0xFFFF0000FFFF0F1FUL #define ASSOC_IDX_INVALID 0x10000 #define to_ap_card(x) container_of((x), struct ap_card, ap_dev.device) diff --git a/drivers/s390/crypto/ap_card.c b/drivers/s390/crypto/ap_card.c index e827fbeb76fb..c86397f4ddcd 100644 --- a/drivers/s390/crypto/ap_card.c +++ b/drivers/s390/crypto/ap_card.c @@ -241,7 +241,7 @@ struct ap_card *ap_card_create(int id, struct ap_tapq_hwinfo hwinfo, ac->ap_dev.device_type = comp_type; ac->hwinfo = hwinfo; ac->id = id; - ac->maxmsgsize = hwinfo.ml > 0 ? + ac->maxmsgsize = hwinfo.ml > 3 ? hwinfo.ml * AP_TAPQ_ML_FIELD_CHUNK_SIZE : AP_DEFAULT_MAX_MSG_SIZE; return ac; diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c index 5d8f63f390a8..0530b1ce228e 100644 --- a/drivers/s390/crypto/pkey_api.c +++ b/drivers/s390/crypto/pkey_api.c @@ -169,8 +169,8 @@ static int pkey_ioctl_clr2protk(struct pkey_clr2protk __user *ucp) { struct pkey_clr2protk kcp; struct clearkeytoken *t; + u8 *tmpbuf = NULL; u32 keylen; - u8 *tmpbuf; int rc; if (copy_from_user(&kcp, ucp, sizeof(kcp))) @@ -181,13 +181,13 @@ static int pkey_ioctl_clr2protk(struct pkey_clr2protk __user *ucp) if (!keylen) { PKEY_DBF_ERR("%s unknown/unsupported keytype %u\n", __func__, kcp.keytype); - memzero_explicit(&kcp, sizeof(kcp)); - return -EINVAL; + rc = -EINVAL; + goto out; } tmpbuf = kzalloc(sizeof(*t) + keylen, GFP_KERNEL); if (!tmpbuf) { - memzero_explicit(&kcp, sizeof(kcp)); - return -ENOMEM; + rc = -ENOMEM; + goto out; } t = (struct clearkeytoken *)tmpbuf; t->type = TOKTYPE_NON_CCA; @@ -202,20 +202,22 @@ static int pkey_ioctl_clr2protk(struct pkey_clr2protk __user *ucp) kcp.protkey.protkey, &kcp.protkey.len, &kcp.protkey.type, 0); pr_debug("key2protkey()=%d\n", rc); + if (rc) + goto out; - kfree_sensitive(tmpbuf); - - if (!rc && copy_to_user(ucp, &kcp, sizeof(kcp))) + if (copy_to_user(ucp, &kcp, sizeof(kcp))) rc = -EFAULT; - memzero_explicit(&kcp, sizeof(kcp)); +out: + memzero_explicit(&kcp, sizeof(kcp)); + kfree_sensitive(tmpbuf); return rc; } static int pkey_ioctl_findcard(struct pkey_findcard __user *ufc) { + struct pkey_apqn *apqns = NULL; struct pkey_findcard kfc; - struct pkey_apqn *apqns; size_t nr_apqns; int rc; @@ -224,8 +226,10 @@ static int pkey_ioctl_findcard(struct pkey_findcard __user *ufc) nr_apqns = MAXAPQNSINLIST; apqns = kmalloc_objs(struct pkey_apqn, nr_apqns); - if (!apqns) - return -ENOMEM; + if (!apqns) { + rc = -ENOMEM; + goto out; + } rc = pkey_handler_apqns_for_key(kfc.seckey.seckey, sizeof(kfc.seckey.seckey), @@ -237,17 +241,18 @@ static int pkey_ioctl_findcard(struct pkey_findcard __user *ufc) PKEY_FLAGS_MATCH_ALT_MKVP, apqns, &nr_apqns, 0); pr_debug("apqns_for_key()=%d\n", rc); - if (rc) { - kfree(apqns); - return rc; - } + if (rc) + goto out; + kfc.cardnr = apqns[0].card; kfc.domain = apqns[0].domain; - kfree(apqns); + if (copy_to_user(ufc, &kfc, sizeof(kfc))) - return -EFAULT; + rc = -EFAULT; - return 0; +out: + kfree(apqns); + return rc; } static int pkey_ioctl_skey2pkey(struct pkey_skey2pkey __user *usp) @@ -327,7 +332,7 @@ static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp) { struct pkey_verifyprotk kvp; struct protaeskeytoken *t; - u8 *tmpbuf; + u8 *tmpbuf = NULL; int rc; if (copy_from_user(&kvp, uvp, sizeof(kvp))) @@ -336,15 +341,15 @@ static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp) if (kvp.protkey.len > sizeof(kvp.protkey.protkey)) { PKEY_DBF_ERR("%s protkey length %u exceeds protkey buffer size\n", __func__, kvp.protkey.len); - memzero_explicit(&kvp, sizeof(kvp)); - return -EINVAL; + rc = -EINVAL; + goto out; } /* build a 'protected key token' from the raw protected key */ tmpbuf = kzalloc(sizeof(*t), GFP_KERNEL); if (!tmpbuf) { - memzero_explicit(&kvp, sizeof(kvp)); - return -ENOMEM; + rc = -ENOMEM; + goto out; } t = (struct protaeskeytoken *)tmpbuf; t->type = TOKTYPE_NON_CCA; @@ -357,42 +362,48 @@ static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp) NULL, NULL, NULL, NULL, NULL, 0); pr_debug("verify_key()=%d\n", rc); - kfree_sensitive(tmpbuf); +out: memzero_explicit(&kvp, sizeof(kvp)); - + kfree_sensitive(tmpbuf); return rc; } static int pkey_ioctl_kblob2protk(struct pkey_kblob2pkey __user *utp) { struct pkey_kblob2pkey ktp; - u8 *kkey; + u8 *kkey = NULL; int rc; if (copy_from_user(&ktp, utp, sizeof(ktp))) return -EFAULT; kkey = _copy_key_from_user(ktp.key, ktp.keylen); - if (IS_ERR(kkey)) - return PTR_ERR(kkey); + if (IS_ERR(kkey)) { + rc = PTR_ERR(kkey); + kkey = NULL; + goto out; + } ktp.protkey.len = sizeof(ktp.protkey.protkey); rc = key2protkey(NULL, 0, kkey, ktp.keylen, ktp.protkey.protkey, &ktp.protkey.len, &ktp.protkey.type, 0); pr_debug("key2protkey()=%d\n", rc); - kfree_sensitive(kkey); - if (!rc && copy_to_user(utp, &ktp, sizeof(ktp))) + if (rc) + goto out; + if (copy_to_user(utp, &ktp, sizeof(ktp))) rc = -EFAULT; - memzero_explicit(&ktp, sizeof(ktp)); +out: + memzero_explicit(&ktp, sizeof(ktp)); + kfree_sensitive(kkey); return rc; } static int pkey_ioctl_genseck2(struct pkey_genseck2 __user *ugs) { + struct pkey_apqn *apqns = NULL; u32 klen = KEYBLOBBUFSIZE; struct pkey_genseck2 kgs; - struct pkey_apqn *apqns; - u8 *kkey; + u8 *kkey = NULL; int rc; u32 u; @@ -405,46 +416,48 @@ static int pkey_ioctl_genseck2(struct pkey_genseck2 __user *ugs) return -EINVAL; } apqns = _copy_apqns_from_user(kgs.apqns, kgs.apqn_entries); - if (IS_ERR(apqns)) - return PTR_ERR(apqns); + if (IS_ERR(apqns)) { + rc = PTR_ERR(apqns); + apqns = NULL; + goto out; + } kkey = kzalloc(klen, GFP_KERNEL); if (!kkey) { - kfree(apqns); - return -ENOMEM; + rc = -ENOMEM; + goto out; } rc = pkey_handler_gen_key(apqns, kgs.apqn_entries, u, kgs.type, kgs.size, kgs.keygenflags, kkey, &klen, NULL, 0); pr_debug("gen_key()=%d\n", rc); - kfree(apqns); - if (rc) { - kfree_sensitive(kkey); - return rc; - } + if (rc) + goto out; if (kgs.key) { if (kgs.keylen < klen) { - kfree_sensitive(kkey); - return -EINVAL; + rc = -EINVAL; + goto out; } if (copy_to_user(kgs.key, kkey, klen)) { - kfree_sensitive(kkey); - return -EFAULT; + rc = -EFAULT; + goto out; } } kgs.keylen = klen; if (copy_to_user(ugs, &kgs, sizeof(kgs))) rc = -EFAULT; - kfree_sensitive(kkey); +out: + kfree_sensitive(kkey); + kfree(apqns); return rc; } static int pkey_ioctl_clr2seck2(struct pkey_clr2seck2 __user *ucs) { + struct pkey_apqn *apqns = NULL; u32 klen = KEYBLOBBUFSIZE; struct pkey_clr2seck2 kcs; - struct pkey_apqn *apqns; - u8 *kkey; + u8 *kkey = NULL; int rc; u32 u; @@ -454,49 +467,45 @@ static int pkey_ioctl_clr2seck2(struct pkey_clr2seck2 __user *ucs) if (!u) { PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n", __func__, kcs.size); - memzero_explicit(&kcs, sizeof(kcs)); - return -EINVAL; + rc = -EINVAL; + goto out; } apqns = _copy_apqns_from_user(kcs.apqns, kcs.apqn_entries); if (IS_ERR(apqns)) { - memzero_explicit(&kcs, sizeof(kcs)); - return PTR_ERR(apqns); + rc = PTR_ERR(apqns); + apqns = NULL; + goto out; } kkey = kzalloc(klen, GFP_KERNEL); if (!kkey) { - kfree(apqns); - memzero_explicit(&kcs, sizeof(kcs)); - return -ENOMEM; + rc = -ENOMEM; + goto out; } rc = pkey_handler_clr_to_key(apqns, kcs.apqn_entries, u, kcs.type, kcs.size, kcs.keygenflags, kcs.clrkey.clrkey, kcs.size / 8, kkey, &klen, NULL, 0); pr_debug("clr_to_key()=%d\n", rc); - kfree(apqns); - if (rc) { - kfree_sensitive(kkey); - memzero_explicit(&kcs, sizeof(kcs)); - return rc; - } + if (rc) + goto out; if (kcs.key) { if (kcs.keylen < klen) { - kfree_sensitive(kkey); - memzero_explicit(&kcs, sizeof(kcs)); - return -EINVAL; + rc = -EINVAL; + goto out; } if (copy_to_user(kcs.key, kkey, klen)) { - kfree_sensitive(kkey); - memzero_explicit(&kcs, sizeof(kcs)); - return -EFAULT; + rc = -EFAULT; + goto out; } } kcs.keylen = klen; if (copy_to_user(ucs, &kcs, sizeof(kcs))) rc = -EFAULT; + +out: memzero_explicit(&kcs, sizeof(kcs)); kfree_sensitive(kkey); - + kfree(apqns); return rc; } @@ -509,18 +518,23 @@ static int pkey_ioctl_verifykey2(struct pkey_verifykey2 __user *uvk) if (copy_from_user(&kvk, uvk, sizeof(kvk))) return -EFAULT; kkey = _copy_key_from_user(kvk.key, kvk.keylen); - if (IS_ERR(kkey)) - return PTR_ERR(kkey); + if (IS_ERR(kkey)) { + rc = PTR_ERR(kkey); + kkey = NULL; + goto out; + } rc = pkey_handler_verify_key(kkey, kvk.keylen, &kvk.cardnr, &kvk.domain, &kvk.type, &kvk.size, &kvk.flags, 0); pr_debug("verify_key()=%d\n", rc); + if (rc) + goto out; + if (copy_to_user(uvk, &kvk, sizeof(kvk))) + rc = -EFAULT; +out: kfree_sensitive(kkey); - if (!rc && copy_to_user(uvk, &kvk, sizeof(kvk))) - return -EFAULT; - return rc; } @@ -528,30 +542,37 @@ static int pkey_ioctl_kblob2protk2(struct pkey_kblob2pkey2 __user *utp) { struct pkey_apqn *apqns = NULL; struct pkey_kblob2pkey2 ktp; - u8 *kkey; + u8 *kkey = NULL; int rc; if (copy_from_user(&ktp, utp, sizeof(ktp))) return -EFAULT; apqns = _copy_apqns_from_user(ktp.apqns, ktp.apqn_entries); - if (IS_ERR(apqns)) - return PTR_ERR(apqns); + if (IS_ERR(apqns)) { + rc = PTR_ERR(apqns); + apqns = NULL; + goto out; + } kkey = _copy_key_from_user(ktp.key, ktp.keylen); if (IS_ERR(kkey)) { - kfree(apqns); - return PTR_ERR(kkey); + rc = PTR_ERR(kkey); + kkey = NULL; + goto out; } ktp.protkey.len = sizeof(ktp.protkey.protkey); rc = key2protkey(apqns, ktp.apqn_entries, kkey, ktp.keylen, ktp.protkey.protkey, &ktp.protkey.len, &ktp.protkey.type, 0); pr_debug("key2protkey()=%d\n", rc); - kfree(apqns); - kfree_sensitive(kkey); - if (!rc && copy_to_user(utp, &ktp, sizeof(ktp))) + if (rc) + goto out; + if (copy_to_user(utp, &ktp, sizeof(ktp))) rc = -EFAULT; - memzero_explicit(&ktp, sizeof(ktp)); +out: + memzero_explicit(&ktp, sizeof(ktp)); + kfree_sensitive(kkey); + kfree(apqns); return rc; } @@ -560,7 +581,7 @@ static int pkey_ioctl_apqns4k(struct pkey_apqns4key __user *uak) struct pkey_apqn *apqns = NULL; struct pkey_apqns4key kak; size_t nr_apqns, len; - u8 *kkey; + u8 *kkey = NULL; int rc; if (copy_from_user(&kak, uak, sizeof(kak))) @@ -568,40 +589,42 @@ static int pkey_ioctl_apqns4k(struct pkey_apqns4key __user *uak) nr_apqns = kak.apqn_entries; if (nr_apqns) { apqns = kmalloc_objs(struct pkey_apqn, nr_apqns); - if (!apqns) - return -ENOMEM; + if (!apqns) { + rc = -ENOMEM; + goto out; + } } kkey = _copy_key_from_user(kak.key, kak.keylen); if (IS_ERR(kkey)) { - kfree(apqns); - return PTR_ERR(kkey); + rc = PTR_ERR(kkey); + kkey = NULL; + goto out; } rc = pkey_handler_apqns_for_key(kkey, kak.keylen, kak.flags, apqns, &nr_apqns, 0); pr_debug("apqns_for_key()=%d\n", rc); - kfree_sensitive(kkey); - if (rc && rc != -ENOSPC) { - kfree(apqns); - return rc; - } + if (rc && rc != -ENOSPC) + goto out; if (!rc && kak.apqns) { if (nr_apqns > kak.apqn_entries) { - kfree(apqns); - return -EINVAL; + rc = -EINVAL; + goto out; } len = nr_apqns * sizeof(struct pkey_apqn); if (len) { if (copy_to_user(kak.apqns, apqns, len)) { - kfree(apqns); - return -EFAULT; + rc = -EFAULT; + goto out; } } } kak.apqn_entries = nr_apqns; if (copy_to_user(uak, &kak, sizeof(kak))) rc = -EFAULT; - kfree(apqns); +out: + kfree_sensitive(kkey); + kfree(apqns); return rc; } @@ -617,87 +640,90 @@ static int pkey_ioctl_apqns4kt(struct pkey_apqns4keytype __user *uat) nr_apqns = kat.apqn_entries; if (nr_apqns) { apqns = kmalloc_objs(struct pkey_apqn, nr_apqns); - if (!apqns) - return -ENOMEM; + if (!apqns) { + rc = -ENOMEM; + goto out; + } } rc = pkey_handler_apqns_for_keytype(kat.type, kat.cur_mkvp, kat.alt_mkvp, kat.flags, apqns, &nr_apqns, 0); pr_debug("apqns_for_keytype()=%d\n", rc); - if (rc && rc != -ENOSPC) { - kfree(apqns); - return rc; - } + if (rc && rc != -ENOSPC) + goto out; if (!rc && kat.apqns) { if (nr_apqns > kat.apqn_entries) { - kfree(apqns); - return -EINVAL; + rc = -EINVAL; + goto out; } len = nr_apqns * sizeof(struct pkey_apqn); if (len) { if (copy_to_user(kat.apqns, apqns, len)) { - kfree(apqns); - return -EFAULT; + rc = -EFAULT; + goto out; } } } kat.apqn_entries = nr_apqns; if (copy_to_user(uat, &kat, sizeof(kat))) rc = -EFAULT; - kfree(apqns); +out: + kfree(apqns); return rc; } static int pkey_ioctl_kblob2protk3(struct pkey_kblob2pkey3 __user *utp) { u32 protkeylen = PROTKEYBLOBBUFSIZE; + u8 *kkey = NULL, *protkey = NULL; struct pkey_apqn *apqns = NULL; struct pkey_kblob2pkey3 ktp; - u8 *kkey, *protkey; int rc; if (copy_from_user(&ktp, utp, sizeof(ktp))) return -EFAULT; apqns = _copy_apqns_from_user(ktp.apqns, ktp.apqn_entries); - if (IS_ERR(apqns)) - return PTR_ERR(apqns); + if (IS_ERR(apqns)) { + rc = PTR_ERR(apqns); + apqns = NULL; + goto out; + } kkey = _copy_key_from_user(ktp.key, ktp.keylen); if (IS_ERR(kkey)) { - kfree(apqns); - return PTR_ERR(kkey); + rc = PTR_ERR(kkey); + kkey = NULL; + goto out; } protkey = kmalloc(protkeylen, GFP_KERNEL); if (!protkey) { - kfree(apqns); - kfree_sensitive(kkey); - return -ENOMEM; + rc = -ENOMEM; + goto out; } rc = key2protkey(apqns, ktp.apqn_entries, kkey, ktp.keylen, protkey, &protkeylen, &ktp.pkeytype, 0); pr_debug("key2protkey()=%d\n", rc); - kfree(apqns); - kfree_sensitive(kkey); - if (rc) { - kfree_sensitive(protkey); - return rc; - } + if (rc) + goto out; if (ktp.pkey && ktp.pkeylen) { if (protkeylen > ktp.pkeylen) { - kfree_sensitive(protkey); - return -EINVAL; + rc = -EINVAL; + goto out; } if (copy_to_user(ktp.pkey, protkey, protkeylen)) { - kfree_sensitive(protkey); - return -EFAULT; + rc = -EFAULT; + goto out; } } - kfree_sensitive(protkey); ktp.pkeylen = protkeylen; if (copy_to_user(utp, &ktp, sizeof(ktp))) - return -EFAULT; + rc = -EFAULT; - return 0; +out: + kfree_sensitive(protkey); + kfree_sensitive(kkey); + kfree(apqns); + return rc; } static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, diff --git a/drivers/s390/virtio/Makefile b/drivers/s390/virtio/Makefile index 2dc4d9aab634..cab140fe0b29 100644 --- a/drivers/s390/virtio/Makefile +++ b/drivers/s390/virtio/Makefile @@ -3,4 +3,6 @@ # # Copyright IBM Corp. 2008 +CONTEXT_ANALYSIS := y + obj-$(CONFIG_S390_GUEST) += virtio_ccw.o diff --git a/include/linux/tick.h b/include/linux/tick.h index 47750ccd1314..b121c5d53e30 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -46,9 +46,6 @@ static inline void tick_unfreeze(void) { } #ifdef CONFIG_TICK_ONESHOT extern void tick_irq_enter(void); -# ifndef arch_needs_cpu -# define arch_needs_cpu() (0) -# endif # else static inline void tick_irq_enter(void) { } #endif diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 98a9cae915c0..6c3fea386713 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -828,8 +828,7 @@ static ktime_t tick_nohz_next_event(struct tick_sched *ts, int cpu) ts->timer_expires_base = basemono; /* - * Keep the periodic tick, when RCU, architecture or irq_work - * requests it. + * Keep the periodic tick, when RCU or irq_work requests it. * Aside of that, check whether the local timer softirq is * pending. If so, its a bad idea to call get_next_timer_interrupt(), * because there is an already expired timer, so it will request @@ -837,8 +836,8 @@ static ktime_t tick_nohz_next_event(struct tick_sched *ts, int cpu) * minimal delta, which brings us back to this place * immediately. Lather, rinse and repeat... */ - if (rcu_needs_cpu() || arch_needs_cpu() || - irq_work_needs_cpu() || local_timer_softirq_pending()) { + if (rcu_needs_cpu() || irq_work_needs_cpu() || + local_timer_softirq_pending()) { next_tick = basemono + TICK_NSEC; } else { /* |
