summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-08-21 13:56:38 +0100
committerMark Brown <broonie@kernel.org>2026-08-21 13:56:38 +0100
commitd2cac287140d890b72b3a82a643b25465ba60f9a (patch)
treea96dcafdbc9c57d89a375032f8b8af52f297c0f5
parent3b03372426b41970eca731093e8cccf3d3aab784 (diff)
parent53158917ea1a0219f6886a0ae10f6f276ecfa2a6 (diff)
downloadlinux-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
-rw-r--r--MAINTAINERS8
-rw-r--r--arch/s390/Kconfig10
-rw-r--r--arch/s390/appldata/Makefile2
-rw-r--r--arch/s390/configs/debug_defconfig4
-rw-r--r--arch/s390/configs/defconfig2
-rw-r--r--arch/s390/crypto/paes_s390.c44
-rw-r--r--arch/s390/crypto/phmac_s390.c25
-rw-r--r--arch/s390/hypfs/Makefile2
-rw-r--r--arch/s390/include/asm/ap.h4
-rw-r--r--arch/s390/include/asm/asm-extable.h4
-rw-r--r--arch/s390/include/asm/cfi.h7
-rw-r--r--arch/s390/include/asm/hugetlb.h4
-rw-r--r--arch/s390/include/asm/irqflags.h20
-rw-r--r--arch/s390/include/asm/percpu.h4
-rw-r--r--arch/s390/include/asm/pkey.h16
-rw-r--r--arch/s390/include/asm/processor.h4
-rw-r--r--arch/s390/include/asm/ptrace.h4
-rw-r--r--arch/s390/include/asm/sclp.h3
-rw-r--r--arch/s390/include/asm/thread_info.h1
-rw-r--r--arch/s390/include/asm/uv.h18
-rw-r--r--arch/s390/include/asm/word-at-a-time.h22
-rw-r--r--arch/s390/include/uapi/asm/Kbuild1
-rw-r--r--arch/s390/kernel/Makefile3
-rw-r--r--arch/s390/kernel/debug.c10
-rw-r--r--arch/s390/kernel/entry.S9
-rw-r--r--arch/s390/kernel/entry.h10
-rw-r--r--arch/s390/kernel/idle.c1
-rw-r--r--arch/s390/kernel/ipl.c58
-rw-r--r--arch/s390/kernel/irq.c4
-rw-r--r--arch/s390/kernel/irqflags.c28
-rw-r--r--arch/s390/kernel/mcount.S9
-rw-r--r--arch/s390/kernel/perf_cpum_cf.c216
-rw-r--r--arch/s390/kernel/smp.c6
-rw-r--r--arch/s390/kernel/syscall.c4
-rw-r--r--arch/s390/kernel/sysinfo.c2
-rw-r--r--arch/s390/kernel/text_amode31.S13
-rw-r--r--arch/s390/kernel/traps.c45
-rw-r--r--arch/s390/kernel/uv.c67
-rw-r--r--arch/s390/kernel/vdso/Makefile3
-rw-r--r--arch/s390/kernel/vdso/vdso.lds.S11
-rw-r--r--arch/s390/kvm/gaccess.c8
-rw-r--r--arch/s390/kvm/gmap.c4
-rw-r--r--arch/s390/kvm/kvm-s390.c1
-rw-r--r--arch/s390/kvm/pv.c17
-rw-r--r--arch/s390/kvm/vsie.c1
-rw-r--r--arch/s390/lib/Makefile2
-rw-r--r--arch/s390/lib/spinlock.c3
-rw-r--r--arch/s390/mm/Makefile2
-rw-r--r--arch/s390/mm/cmm.c2
-rw-r--r--arch/s390/mm/extable.c18
-rw-r--r--arch/s390/mm/fault.c36
-rw-r--r--arch/s390/mm/gmap_helpers.c4
-rw-r--r--arch/s390/mm/hugetlbpage.c10
-rw-r--r--arch/s390/mm/init.c2
-rw-r--r--arch/s390/mm/maccess.c2
-rw-r--r--arch/s390/mm/pageattr.c5
-rw-r--r--arch/s390/mm/pgalloc.c1
-rw-r--r--arch/s390/net/Makefile3
-rw-r--r--arch/s390/net/bpf_jit_comp.c28
-rw-r--r--arch/s390/pci/Makefile2
-rw-r--r--arch/s390/pci/pci.c4
-rw-r--r--arch/s390/pci/pci_event.c189
-rw-r--r--arch/s390/purgatory/Makefile2
-rw-r--r--arch/s390/tools/relocs.c158
-rw-r--r--drivers/cpuidle/Kconfig5
-rw-r--r--drivers/cpuidle/Kconfig.s39011
-rw-r--r--drivers/cpuidle/Makefile4
-rw-r--r--drivers/cpuidle/cpuidle-s390.c115
-rw-r--r--drivers/s390/char/Makefile2
-rw-r--r--drivers/s390/char/con3215.c1
-rw-r--r--drivers/s390/char/sclp_pci.c3
-rw-r--r--drivers/s390/char/uvdevice.c6
-rw-r--r--drivers/s390/cio/Makefile2
-rw-r--r--drivers/s390/cio/css.c8
-rw-r--r--drivers/s390/cio/vfio_ccw_drv.c1
-rw-r--r--drivers/s390/cio/vfio_ccw_private.h3
-rw-r--r--drivers/s390/crypto/ap_bus.c4
-rw-r--r--drivers/s390/crypto/ap_bus.h2
-rw-r--r--drivers/s390/crypto/ap_card.c2
-rw-r--r--drivers/s390/crypto/pkey_api.c288
-rw-r--r--drivers/s390/virtio/Makefile2
-rw-r--r--include/linux/tick.h3
-rw-r--r--kernel/time/tick-sched.c7
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 {
/*