mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 10:31:33 -04:00
Merge tag 's390-7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull s390 updates from Vasily Gorbik: - Add a cpuidle driver with polling and enabled wait states using the existing CPU idle infrastructure and idle governor to improve latency for frequent sleep/wakeup cycles. Remove the obsolete tick delay heuristic and generic arch_needs_cpu() hook. Add the corresponding driver entry to MAINTAINERS - Add kCFI support using the generic support provided by Clang - Enable Clang CONTEXT_ANALYSIS for various architecture code and for char, PCI, CIO and virtio drivers. Add required lock annotations, exclude unsupported mm helpers and remove conditional PCI locking - Fix secure storage access exception handling and reintroduce DCACHE_WORD_ACCESS previously removed as a workaround - Fix cpum_cf perf crashes when CPUs are brought online while per-task events are active. Allocate and remove per-CPU counter data from CPU hotplug callbacks - Fix a deadlock when an s390dbf debug area is unregistered while one of its debugfs files is being written to - Fix MVIY_PERCPU() with binutils older than 2.39, where an assembler macro silently omitted an instruction needed to repair interrupted operations after CPU migration - Remove/replace cond_resched() calls which are no-ops with the supported s390 preemption models - Fix AP queue depth and maximum message length decoding according to the architecture. Current hardware is not affected, but future hardware could report values which were handled incorrectly - Reflect the configured CPU state in cpu_enabled_mask so deconfigured CPUs are not presented as available for onlining - Restore the vDSO GNU_EH_FRAME program header which was lost when the build switched to direct linker invocation, and mark it read-only - Add SCLP action qualifiers used by Spyre for card initialization, recoverable error and telemetry reporting - Move KMSAN interrupt flag helpers out of line to fix -Wstatic-in-inline build warnings - Use level-specific page table entry accessors for hugetlb entries and ptep_get() when accessing crashed kernel memory in kdump - Make forced AP bus rescans killable so that a user process blocked behind an ongoing scan can still be terminated with SIGKILL - Rework pkey ioctl error paths to remove duplicated cleanup code and avoid freeing error pointers - Allow the protected guest SWIOTLB buffer to be allocated outside the first 2GB. Also enable dynamic SWIOTLB growth and the coherent atomic pool fallback to improve I/O behavior when the initial pool is exhausted - Add program check statistics and spinlock contention tracepoints. Increase the lockdep chain capacity to keep lockdep enabled for complex code paths such as btrfs - Simplify IPL, trap and syscall code and remove the obsolete unistd_32.h generation entry * tag 's390-7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (59 commits) s390/percpu: Fix MVIY_PERCPU() with older binutils s390/debug: Fix deadlock during unregister s390/cpum_cf: Handle CPU hotplug via prepare/dead callbacks s390: Enable CONTEXT_ANALYSIS for various directories s390/mm: Add __context_unsafe() attribute to gmap helper functions s390/mm: Add __context_unsafe() attribute to do_secure_storage_access() s390/sysinfo: Add context analysis attributes s390/irqflags: Add out-of-line definitions of arch_local_irq_*() for KMSAN s390/virtio: Enable CONTEXT_ANALYSIS s390/cio: Enable CONTEXT_ANALYSIS s390/vfio_ccw: Add __must_hold() attribute to vfio_ccw_sch_quiesce() s390/pci: Enable CONTEXT_ANALYSIS s390/pci: Rework __zpci_event_availability() to remove conditional locking s390/pci: Rework __zpci_event_error() to remove conditional locking s390/char: Enable CONTEXT_ANALYSIS s390/con3215: Add __must_hold() attribute to raw3215_make_room() s390/ap: Fix MAPML computation s390/cio: Remove cond_resched() calls s390: Remove cond_resched() calls KVM: s390: Remove cond_resched() calls ...
This commit is contained in:
@@ -6856,6 +6856,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>
|
||||
@@ -23928,6 +23935,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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -144,6 +147,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
|
||||
@@ -152,6 +156,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
|
||||
@@ -164,6 +169,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
|
||||
@@ -276,6 +282,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
|
||||
@@ -706,6 +714,8 @@ config KERNEL_IMAGE_BASE
|
||||
|
||||
endmenu
|
||||
|
||||
source "drivers/cpuidle/Kconfig"
|
||||
|
||||
menu "Memory setup"
|
||||
|
||||
config ARCH_SPARSEMEM_ENABLE
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
7
arch/s390/include/asm/cfi.h
Normal file
7
arch/s390/include/asm/cfi.h
Normal file
@@ -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 */
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
generated-y += unistd_32.h
|
||||
generated-y += unistd_64.h
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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) {
|
||||
|
||||
28
arch/s390/kernel/irqflags.c
Normal file
28
arch/s390/kernel/irqflags.c
Normal file
@@ -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);
|
||||
@@ -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
|
||||
|
||||
@@ -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 @@ static int cpum_cf_alloc_cpu(int cpu)
|
||||
* 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 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, 1);
|
||||
} else {
|
||||
cpum_cf_free_cpu(cpu, 1);
|
||||
}
|
||||
mutex_unlock(&pmc_reserve_mutex);
|
||||
}
|
||||
|
||||
static void cpum_cf_free(int cpu)
|
||||
{
|
||||
if (cpu == -1) {
|
||||
for_each_online_cpu(cpu)
|
||||
cpum_cf_free_cpu(cpu);
|
||||
} else {
|
||||
cpum_cf_free_cpu(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))
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 @@ void noinstr __do_pgm_check(struct pt_regs *regs)
|
||||
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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -151,10 +151,8 @@ static void ipte_lock_simple(struct kvm *kvm)
|
||||
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 @@ static void ipte_lock_siif(struct kvm *kvm)
|
||||
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++;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1027,7 +1027,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;
|
||||
|
||||
@@ -338,7 +338,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);
|
||||
@@ -370,7 +370,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;
|
||||
@@ -415,7 +415,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;
|
||||
@@ -430,7 +430,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)
|
||||
@@ -747,7 +747,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",
|
||||
@@ -833,7 +833,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;
|
||||
@@ -876,7 +875,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;
|
||||
@@ -960,7 +959,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) {
|
||||
@@ -1038,7 +1037,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",
|
||||
|
||||
@@ -1434,7 +1434,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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 @@ static int __gmap_helper_unshare_zeropages(struct mm_struct *mm)
|
||||
* truncation. In that case, the shared zeropage would be gone
|
||||
* and we can simply retry and make progress.
|
||||
*/
|
||||
cond_resched();
|
||||
goto retry;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -151,7 +151,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_FORCE | SWIOTLB_VERBOSE);
|
||||
swiotlb_init(true, SWIOTLB_FORCE | SWIOTLB_VERBOSE | SWIOTLB_ANY);
|
||||
swiotlb_update_mem_attributes();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
|
||||
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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
__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;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
zpci_dbg(3, "avl fid:%x, fh:%x, pec:%x\n",
|
||||
ccdf->fid, ccdf->fh, ccdf->pec);
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x0306: /* 0x308 or 0x302 for multiple devices */
|
||||
zpci_remove_reserved_devices();
|
||||
zpci_scan_devices();
|
||||
break;
|
||||
case 0x0308: /* Standby -> Reserved */
|
||||
if (!zdev)
|
||||
zdev = zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_STANDBY);
|
||||
if (IS_ERR(zdev))
|
||||
break;
|
||||
zpci_device_reserved(zdev);
|
||||
break;
|
||||
default:
|
||||
if (zpci_add_device(zdev)) {
|
||||
kfree(zdev);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (existing_zdev) {
|
||||
mutex_unlock(&zdev->state_lock);
|
||||
zpci_zdev_put(zdev);
|
||||
}
|
||||
|
||||
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 */
|
||||
/* 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 */
|
||||
/* 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 0x0308: /* Standby -> Reserved */
|
||||
zpci_device_reserved(zdev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
11
drivers/cpuidle/Kconfig.s390
Normal file
11
drivers/cpuidle/Kconfig.s390
Normal file
@@ -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.
|
||||
@@ -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
|
||||
|
||||
115
drivers/cpuidle/cpuidle-s390.c
Normal file
115
drivers/cpuidle/cpuidle-s390.c
Normal file
@@ -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);
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
return 0;
|
||||
if (copy_to_user(ufc, &kfc, sizeof(kfc)))
|
||||
rc = -EFAULT;
|
||||
|
||||
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,
|
||||
|
||||
@@ -3,4 +3,6 @@
|
||||
#
|
||||
# Copyright IBM Corp. 2008
|
||||
|
||||
CONTEXT_ANALYSIS := y
|
||||
|
||||
obj-$(CONFIG_S390_GUEST) += virtio_ccw.o
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user