Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux

Pull s390 updates from Martin Schwidefsky:

 - three merges for KVM/s390 with changes for vfio-ccw and cpacf. The
   patches are included in the KVM tree as well, let git sort it out.

 - add the new 'trng' random number generator

 - provide the secure key verification API for the pkey interface

 - introduce the z13 cpu counters to perf

 - add a new system call to set up the guarded storage facility

 - simplify TASK_SIZE and arch_get_unmapped_area

 - export the raw STSI data related to CPU topology to user space

 - ... and the usual churn of bug-fixes and cleanups.

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (74 commits)
  s390/crypt: use the correct module alias for paes_s390.
  s390/cpacf: Introduce kma instruction
  s390/cpacf: query instructions use unique parameters for compatibility with KMA
  s390/trng: Introduce s390 TRNG device driver.
  s390/crypto: Provide s390 specific arch random functionality.
  s390/crypto: Add new subfunctions to the cpacf PRNO function.
  s390/crypto: Renaming PPNO to PRNO.
  s390/pageattr: avoid unnecessary page table splitting
  s390/mm: simplify arch_get_unmapped_area[_topdown]
  s390/mm: make TASK_SIZE independent from the number of page table levels
  s390/gs: add regset for the guarded storage broadcast control block
  s390/kvm: Add use_cmma field to mm_context_t
  s390/kvm: Add PGSTE manipulation functions
  vfio: ccw: improve error handling for vfio_ccw_mdev_remove
  vfio: ccw: remove unnecessary NULL checks of a pointer
  s390/spinlock: remove compare and delay instruction
  s390/spinlock: use atomic primitives for spinlocks
  s390/cpumf: simplify detection of guest samples
  s390/pci: remove forward declaration
  s390/pci: increase the PCI_NR_FUNCTIONS default
  ...
This commit is contained in:
Linus Torvalds
2017-05-02 09:50:09 -07:00
115 changed files with 4225 additions and 563 deletions

View File

@@ -51,14 +51,12 @@ CFLAGS_dumpstack.o += -fno-optimize-sibling-calls
#
CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'
CFLAGS_sysinfo.o += -w
obj-y := traps.o time.o process.o base.o early.o setup.o idle.o vtime.o
obj-y += processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o
obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o als.o
obj-y += sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o
obj-y += runtime_instr.o cache.o fpu.o dumpstack.o
obj-y += entry.o reipl.o relocate_kernel.o
obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o
obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o
extra-y += head.o head64.o vmlinux.lds

View File

@@ -175,7 +175,7 @@ int main(void)
/* software defined ABI-relevant lowcore locations 0xe00 - 0xe20 */
OFFSET(__LC_DUMP_REIPL, lowcore, ipib);
/* hardware defined lowcore locations 0x1000 - 0x18ff */
OFFSET(__LC_VX_SAVE_AREA_ADDR, lowcore, vector_save_area_addr);
OFFSET(__LC_MCESAD, lowcore, mcesad);
OFFSET(__LC_EXT_PARAMS2, lowcore, ext_params2);
OFFSET(__LC_FPREGS_SAVE_AREA, lowcore, floating_pt_save_area);
OFFSET(__LC_GPREGS_SAVE_AREA, lowcore, gpregs_save_area);

View File

@@ -178,4 +178,5 @@ COMPAT_SYSCALL_WRAP3(getpeername, int, fd, struct sockaddr __user *, usockaddr,
COMPAT_SYSCALL_WRAP6(sendto, int, fd, void __user *, buff, size_t, len, unsigned int, flags, struct sockaddr __user *, addr, int, addr_len);
COMPAT_SYSCALL_WRAP3(mlock2, unsigned long, start, size_t, len, int, flags);
COMPAT_SYSCALL_WRAP6(copy_file_range, int, fd_in, loff_t __user *, off_in, int, fd_out, loff_t __user *, off_out, size_t, len, unsigned int, flags);
COMPAT_SYSCALL_WRAP2(s390_guarded_storage, int, command, struct gs_cb *, gs_cb);
COMPAT_SYSCALL_WRAP5(statx, int, dfd, const char __user *, path, unsigned, flags, unsigned, mask, struct statx __user *, buffer);

View File

@@ -428,6 +428,20 @@ static void *nt_vmcoreinfo(void *ptr)
return nt_init_name(ptr, 0, vmcoreinfo, size, "VMCOREINFO");
}
/*
* Initialize final note (needed for /proc/vmcore code)
*/
static void *nt_final(void *ptr)
{
Elf64_Nhdr *note;
note = (Elf64_Nhdr *) ptr;
note->n_namesz = 0;
note->n_descsz = 0;
note->n_type = 0;
return PTR_ADD(ptr, sizeof(Elf64_Nhdr));
}
/*
* Initialize ELF header (new kernel)
*/
@@ -515,6 +529,7 @@ static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset)
if (sa->prefix != 0)
ptr = fill_cpu_elf_notes(ptr, cpu++, sa);
ptr = nt_vmcoreinfo(ptr);
ptr = nt_final(ptr);
memset(phdr, 0, sizeof(*phdr));
phdr->p_type = PT_NOTE;
phdr->p_offset = notes_offset;

View File

@@ -231,9 +231,29 @@ static noinline __init void detect_machine_type(void)
S390_lowcore.machine_flags |= MACHINE_FLAG_VM;
}
/* Remove leading, trailing and double whitespace. */
static inline void strim_all(char *str)
{
char *s;
s = strim(str);
if (s != str)
memmove(str, s, strlen(s));
while (*str) {
if (!isspace(*str++))
continue;
if (isspace(*str)) {
s = skip_spaces(str);
memmove(str, s, strlen(s) + 1);
}
}
}
static noinline __init void setup_arch_string(void)
{
struct sysinfo_1_1_1 *mach = (struct sysinfo_1_1_1 *)&sysinfo_page;
struct sysinfo_3_2_2 *vm = (struct sysinfo_3_2_2 *)&sysinfo_page;
char mstr[80], hvstr[17];
if (stsi(mach, 1, 1, 1))
return;
@@ -241,14 +261,21 @@ static noinline __init void setup_arch_string(void)
EBCASC(mach->type, sizeof(mach->type));
EBCASC(mach->model, sizeof(mach->model));
EBCASC(mach->model_capacity, sizeof(mach->model_capacity));
dump_stack_set_arch_desc("%-16.16s %-4.4s %-16.16s %-16.16s (%s)",
mach->manufacturer,
mach->type,
mach->model,
mach->model_capacity,
MACHINE_IS_LPAR ? "LPAR" :
MACHINE_IS_VM ? "z/VM" :
MACHINE_IS_KVM ? "KVM" : "unknown");
sprintf(mstr, "%-16.16s %-4.4s %-16.16s %-16.16s",
mach->manufacturer, mach->type,
mach->model, mach->model_capacity);
strim_all(mstr);
if (stsi(vm, 3, 2, 2) == 0 && vm->count) {
EBCASC(vm->vm[0].cpi, sizeof(vm->vm[0].cpi));
sprintf(hvstr, "%-16.16s", vm->vm[0].cpi);
strim_all(hvstr);
} else {
sprintf(hvstr, "%s",
MACHINE_IS_LPAR ? "LPAR" :
MACHINE_IS_VM ? "z/VM" :
MACHINE_IS_KVM ? "KVM" : "unknown");
}
dump_stack_set_arch_desc("%s (%s)", mstr, hvstr);
}
static __init void setup_topology(void)
@@ -358,6 +385,8 @@ static __init void detect_machine_facilities(void)
S390_lowcore.machine_flags |= MACHINE_FLAG_NX;
__ctl_set_bit(0, 20);
}
if (test_facility(133))
S390_lowcore.machine_flags |= MACHINE_FLAG_GS;
}
static inline void save_vector_registers(void)
@@ -375,7 +404,7 @@ static int __init topology_setup(char *str)
rc = kstrtobool(str, &enabled);
if (!rc && !enabled)
S390_lowcore.machine_flags &= ~MACHINE_HAS_TOPOLOGY;
S390_lowcore.machine_flags &= ~MACHINE_FLAG_TOPOLOGY;
return rc;
}
early_param("topology", topology_setup);
@@ -405,23 +434,16 @@ early_param("noexec", noexec_setup);
static int __init cad_setup(char *str)
{
int val;
bool enabled;
int rc;
get_option(&str, &val);
if (val && test_facility(128))
S390_lowcore.machine_flags |= MACHINE_FLAG_CAD;
return 0;
}
early_param("cad", cad_setup);
static int __init cad_init(void)
{
if (MACHINE_HAS_CAD)
rc = kstrtobool(str, &enabled);
if (!rc && enabled && test_facility(128))
/* Enable problem state CAD. */
__ctl_set_bit(2, 3);
return 0;
return rc;
}
early_initcall(cad_init);
early_param("cad", cad_setup);
static __init void memmove_early(void *dst, const void *src, size_t n)
{

View File

@@ -47,7 +47,7 @@ STACK_SIZE = 1 << STACK_SHIFT
STACK_INIT = STACK_SIZE - STACK_FRAME_OVERHEAD - __PT_SIZE
_TIF_WORK = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \
_TIF_UPROBE)
_TIF_UPROBE | _TIF_GUARDED_STORAGE)
_TIF_TRACE = (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP | \
_TIF_SYSCALL_TRACEPOINT)
_CIF_WORK = (_CIF_MCCK_PENDING | _CIF_ASCE_PRIMARY | \
@@ -189,8 +189,6 @@ ENTRY(__switch_to)
stg %r3,__LC_CURRENT # store task struct of next
stg %r15,__LC_KERNEL_STACK # store end of kernel stack
lg %r15,__THREAD_ksp(%r1) # load kernel stack of next
/* c4 is used in guest detection: arch/s390/kernel/perf_cpum_sf.c */
lctl %c4,%c4,__TASK_pid(%r3) # load pid to control reg. 4
mvc __LC_CURRENT_PID(4,%r0),__TASK_pid(%r3) # store pid of next
lmg %r6,%r15,__SF_GPRS(%r15) # load gprs of next task
TSTMSK __LC_MACHINE_FLAGS,MACHINE_FLAG_LPP
@@ -332,6 +330,8 @@ ENTRY(system_call)
TSTMSK __TI_flags(%r12),_TIF_UPROBE
jo .Lsysc_uprobe_notify
#endif
TSTMSK __TI_flags(%r12),_TIF_GUARDED_STORAGE
jo .Lsysc_guarded_storage
TSTMSK __PT_FLAGS(%r11),_PIF_PER_TRAP
jo .Lsysc_singlestep
TSTMSK __TI_flags(%r12),_TIF_SIGPENDING
@@ -408,6 +408,14 @@ ENTRY(system_call)
jg uprobe_notify_resume
#endif
#
# _TIF_GUARDED_STORAGE is set, call guarded_storage_load
#
.Lsysc_guarded_storage:
lgr %r2,%r11 # pass pointer to pt_regs
larl %r14,.Lsysc_return
jg gs_load_bc_cb
#
# _PIF_PER_TRAP is set, call do_per_trap
#
@@ -663,6 +671,8 @@ ENTRY(io_int_handler)
jo .Lio_sigpending
TSTMSK __TI_flags(%r12),_TIF_NOTIFY_RESUME
jo .Lio_notify_resume
TSTMSK __TI_flags(%r12),_TIF_GUARDED_STORAGE
jo .Lio_guarded_storage
TSTMSK __LC_CPU_FLAGS,_CIF_FPU
jo .Lio_vxrs
TSTMSK __LC_CPU_FLAGS,(_CIF_ASCE_PRIMARY|_CIF_ASCE_SECONDARY)
@@ -696,6 +706,18 @@ ENTRY(io_int_handler)
larl %r14,.Lio_return
jg load_fpu_regs
#
# _TIF_GUARDED_STORAGE is set, call guarded_storage_load
#
.Lio_guarded_storage:
# TRACE_IRQS_ON already done at .Lio_return
ssm __LC_SVC_NEW_PSW # reenable interrupts
lgr %r2,%r11 # pass pointer to pt_regs
brasl %r14,gs_load_bc_cb
ssm __LC_PGM_NEW_PSW # disable I/O and ext. interrupts
TRACE_IRQS_OFF
j .Lio_return
#
# _TIF_NEED_RESCHED is set, call schedule
#

View File

@@ -74,12 +74,14 @@ long sys_sigreturn(void);
long sys_s390_personality(unsigned int personality);
long sys_s390_runtime_instr(int command, int signum);
long sys_s390_guarded_storage(int command, struct gs_cb __user *);
long sys_s390_pci_mmio_write(unsigned long, const void __user *, size_t);
long sys_s390_pci_mmio_read(unsigned long, void __user *, size_t);
DECLARE_PER_CPU(u64, mt_cycles[8]);
void verify_facilities(void);
void gs_load_bc_cb(struct pt_regs *regs);
void set_fs_fixup(void);
#endif /* _ENTRY_H */

View File

@@ -0,0 +1,128 @@
/*
* Copyright IBM Corp. 2016
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/signal.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <asm/guarded_storage.h>
#include "entry.h"
void exit_thread_gs(void)
{
kfree(current->thread.gs_cb);
kfree(current->thread.gs_bc_cb);
current->thread.gs_cb = current->thread.gs_bc_cb = NULL;
}
static int gs_enable(void)
{
struct gs_cb *gs_cb;
if (!current->thread.gs_cb) {
gs_cb = kzalloc(sizeof(*gs_cb), GFP_KERNEL);
if (!gs_cb)
return -ENOMEM;
gs_cb->gsd = 25;
preempt_disable();
__ctl_set_bit(2, 4);
load_gs_cb(gs_cb);
current->thread.gs_cb = gs_cb;
preempt_enable();
}
return 0;
}
static int gs_disable(void)
{
if (current->thread.gs_cb) {
preempt_disable();
kfree(current->thread.gs_cb);
current->thread.gs_cb = NULL;
__ctl_clear_bit(2, 4);
preempt_enable();
}
return 0;
}
static int gs_set_bc_cb(struct gs_cb __user *u_gs_cb)
{
struct gs_cb *gs_cb;
gs_cb = current->thread.gs_bc_cb;
if (!gs_cb) {
gs_cb = kzalloc(sizeof(*gs_cb), GFP_KERNEL);
if (!gs_cb)
return -ENOMEM;
current->thread.gs_bc_cb = gs_cb;
}
if (copy_from_user(gs_cb, u_gs_cb, sizeof(*gs_cb)))
return -EFAULT;
return 0;
}
static int gs_clear_bc_cb(void)
{
struct gs_cb *gs_cb;
gs_cb = current->thread.gs_bc_cb;
current->thread.gs_bc_cb = NULL;
kfree(gs_cb);
return 0;
}
void gs_load_bc_cb(struct pt_regs *regs)
{
struct gs_cb *gs_cb;
preempt_disable();
clear_thread_flag(TIF_GUARDED_STORAGE);
gs_cb = current->thread.gs_bc_cb;
if (gs_cb) {
kfree(current->thread.gs_cb);
current->thread.gs_bc_cb = NULL;
__ctl_set_bit(2, 4);
load_gs_cb(gs_cb);
current->thread.gs_cb = gs_cb;
}
preempt_enable();
}
static int gs_broadcast(void)
{
struct task_struct *sibling;
read_lock(&tasklist_lock);
for_each_thread(current, sibling) {
if (!sibling->thread.gs_bc_cb)
continue;
if (test_and_set_tsk_thread_flag(sibling, TIF_GUARDED_STORAGE))
kick_process(sibling);
}
read_unlock(&tasklist_lock);
return 0;
}
SYSCALL_DEFINE2(s390_guarded_storage, int, command,
struct gs_cb __user *, gs_cb)
{
if (!MACHINE_HAS_GS)
return -EOPNOTSUPP;
switch (command) {
case GS_ENABLE:
return gs_enable();
case GS_DISABLE:
return gs_disable();
case GS_SET_BC_CB:
return gs_set_bc_cb(gs_cb);
case GS_CLEAR_BC_CB:
return gs_clear_bc_cb();
case GS_BROADCAST:
return gs_broadcast();
default:
return -EINVAL;
}
}

View File

@@ -25,7 +25,6 @@
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
#include <asm/thread_info.h>
#include <asm/facility.h>
#include <asm/page.h>
#include <asm/ptrace.h>

View File

@@ -52,7 +52,7 @@ ENTRY(startup_continue)
.quad 0 # cr1: primary space segment table
.quad .Lduct # cr2: dispatchable unit control table
.quad 0 # cr3: instruction authorization
.quad 0 # cr4: instruction authorization
.quad 0xffff # cr4: instruction authorization
.quad .Lduct # cr5: primary-aste origin
.quad 0 # cr6: I/O interrupts
.quad 0 # cr7: secondary space segment table

View File

@@ -0,0 +1,15 @@
#include <linux/debugfs.h>
#include <linux/export.h>
#include <linux/init.h>
struct dentry *arch_debugfs_dir;
EXPORT_SYMBOL(arch_debugfs_dir);
static int __init arch_kdebugfs_init(void)
{
arch_debugfs_dir = debugfs_create_dir("s390", NULL);
if (IS_ERR(arch_debugfs_dir))
arch_debugfs_dir = NULL;
return 0;
}
postcore_initcall(arch_kdebugfs_init);

View File

@@ -27,6 +27,7 @@
#include <asm/cacheflush.h>
#include <asm/os_info.h>
#include <asm/switch_to.h>
#include <asm/nmi.h>
typedef void (*relocate_kernel_t)(kimage_entry_t *, unsigned long);
@@ -102,6 +103,8 @@ static void __do_machine_kdump(void *image)
*/
static noinline void __machine_kdump(void *image)
{
struct mcesa *mcesa;
unsigned long cr2_old, cr2_new;
int this_cpu, cpu;
lgr_info_log();
@@ -114,8 +117,16 @@ static noinline void __machine_kdump(void *image)
continue;
}
/* Store status of the boot CPU */
mcesa = (struct mcesa *)(S390_lowcore.mcesad & MCESA_ORIGIN_MASK);
if (MACHINE_HAS_VX)
save_vx_regs((void *) &S390_lowcore.vector_save_area);
save_vx_regs((__vector128 *) mcesa->vector_save_area);
if (MACHINE_HAS_GS) {
__ctl_store(cr2_old, 2, 2);
cr2_new = cr2_old | (1UL << 4);
__ctl_load(cr2_new, 2, 2);
save_gs_cb((struct gs_cb *) mcesa->guarded_storage_save_area);
__ctl_load(cr2_old, 2, 2);
}
/*
* To create a good backchain for this CPU in the dump store_status
* is passed the address of a function. The address is saved into

View File

@@ -106,6 +106,7 @@ static int notrace s390_validate_registers(union mci mci, int umode)
int kill_task;
u64 zero;
void *fpt_save_area;
struct mcesa *mcesa;
kill_task = 0;
zero = 0;
@@ -165,6 +166,7 @@ static int notrace s390_validate_registers(union mci mci, int umode)
: : "Q" (S390_lowcore.fpt_creg_save_area));
}
mcesa = (struct mcesa *)(S390_lowcore.mcesad & MCESA_ORIGIN_MASK);
if (!MACHINE_HAS_VX) {
/* Validate floating point registers */
asm volatile(
@@ -209,8 +211,8 @@ static int notrace s390_validate_registers(union mci mci, int umode)
" la 1,%0\n"
" .word 0xe70f,0x1000,0x0036\n" /* vlm 0,15,0(1) */
" .word 0xe70f,0x1100,0x0c36\n" /* vlm 16,31,256(1) */
: : "Q" (*(struct vx_array *)
&S390_lowcore.vector_save_area) : "1");
: : "Q" (*(struct vx_array *) mcesa->vector_save_area)
: "1");
__ctl_load(S390_lowcore.cregs_save_area[0], 0, 0);
}
/* Validate access registers */
@@ -224,6 +226,19 @@ static int notrace s390_validate_registers(union mci mci, int umode)
*/
kill_task = 1;
}
/* Validate guarded storage registers */
if (MACHINE_HAS_GS && (S390_lowcore.cregs_save_area[2] & (1UL << 4))) {
if (!mci.gs)
/*
* Guarded storage register can't be restored and
* the current processes uses guarded storage.
* It has to be terminated.
*/
kill_task = 1;
else
load_gs_cb((struct gs_cb *)
mcesa->guarded_storage_save_area);
}
/*
* We don't even try to validate the TOD register, since we simply
* can't write something sensible into that register.

View File

@@ -1,7 +1,7 @@
/*
* Performance event support for s390x - CPU-measurement Counter Facility
*
* Copyright IBM Corp. 2012
* Copyright IBM Corp. 2012, 2017
* Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -22,19 +22,12 @@
#include <asm/irq.h>
#include <asm/cpu_mf.h>
/* CPU-measurement counter facility supports these CPU counter sets:
* For CPU counter sets:
* Basic counter set: 0-31
* Problem-state counter set: 32-63
* Crypto-activity counter set: 64-127
* Extented counter set: 128-159
*/
enum cpumf_ctr_set {
/* CPU counter sets */
CPUMF_CTR_SET_BASIC = 0,
CPUMF_CTR_SET_USER = 1,
CPUMF_CTR_SET_CRYPTO = 2,
CPUMF_CTR_SET_EXT = 3,
CPUMF_CTR_SET_BASIC = 0, /* Basic Counter Set */
CPUMF_CTR_SET_USER = 1, /* Problem-State Counter Set */
CPUMF_CTR_SET_CRYPTO = 2, /* Crypto-Activity Counter Set */
CPUMF_CTR_SET_EXT = 3, /* Extended Counter Set */
CPUMF_CTR_SET_MT_DIAG = 4, /* MT-diagnostic Counter Set */
/* Maximum number of counter sets */
CPUMF_CTR_SET_MAX,
@@ -47,6 +40,7 @@ static const u64 cpumf_state_ctl[CPUMF_CTR_SET_MAX] = {
[CPUMF_CTR_SET_USER] = 0x04,
[CPUMF_CTR_SET_CRYPTO] = 0x08,
[CPUMF_CTR_SET_EXT] = 0x01,
[CPUMF_CTR_SET_MT_DIAG] = 0x20,
};
static void ctr_set_enable(u64 *state, int ctr_set)
@@ -76,19 +70,20 @@ struct cpu_hw_events {
};
static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
.ctr_set = {
[CPUMF_CTR_SET_BASIC] = ATOMIC_INIT(0),
[CPUMF_CTR_SET_USER] = ATOMIC_INIT(0),
[CPUMF_CTR_SET_CRYPTO] = ATOMIC_INIT(0),
[CPUMF_CTR_SET_EXT] = ATOMIC_INIT(0),
[CPUMF_CTR_SET_BASIC] = ATOMIC_INIT(0),
[CPUMF_CTR_SET_USER] = ATOMIC_INIT(0),
[CPUMF_CTR_SET_CRYPTO] = ATOMIC_INIT(0),
[CPUMF_CTR_SET_EXT] = ATOMIC_INIT(0),
[CPUMF_CTR_SET_MT_DIAG] = ATOMIC_INIT(0),
},
.state = 0,
.flags = 0,
.txn_flags = 0,
};
static int get_counter_set(u64 event)
static enum cpumf_ctr_set get_counter_set(u64 event)
{
int set = -1;
int set = CPUMF_CTR_SET_MAX;
if (event < 32)
set = CPUMF_CTR_SET_BASIC;
@@ -98,34 +93,17 @@ static int get_counter_set(u64 event)
set = CPUMF_CTR_SET_CRYPTO;
else if (event < 256)
set = CPUMF_CTR_SET_EXT;
else if (event >= 448 && event < 496)
set = CPUMF_CTR_SET_MT_DIAG;
return set;
}
static int validate_event(const struct hw_perf_event *hwc)
{
switch (hwc->config_base) {
case CPUMF_CTR_SET_BASIC:
case CPUMF_CTR_SET_USER:
case CPUMF_CTR_SET_CRYPTO:
case CPUMF_CTR_SET_EXT:
/* check for reserved counters */
if ((hwc->config >= 6 && hwc->config <= 31) ||
(hwc->config >= 38 && hwc->config <= 63) ||
(hwc->config >= 80 && hwc->config <= 127))
return -EOPNOTSUPP;
break;
default:
return -EINVAL;
}
return 0;
}
static int validate_ctr_version(const struct hw_perf_event *hwc)
{
struct cpu_hw_events *cpuhw;
int err = 0;
u16 mtdiag_ctl;
cpuhw = &get_cpu_var(cpu_hw_events);
@@ -145,6 +123,27 @@ static int validate_ctr_version(const struct hw_perf_event *hwc)
(cpuhw->info.csvn > 2 && hwc->config > 255))
err = -EOPNOTSUPP;
break;
case CPUMF_CTR_SET_MT_DIAG:
if (cpuhw->info.csvn <= 3)
err = -EOPNOTSUPP;
/*
* MT-diagnostic counters are read-only. The counter set
* is automatically enabled and activated on all CPUs with
* multithreading (SMT). Deactivation of multithreading
* also disables the counter set. State changes are ignored
* by lcctl(). Because Linux controls SMT enablement through
* a kernel parameter only, the counter set is either disabled
* or enabled and active.
*
* Thus, the counters can only be used if SMT is on and the
* counter set is enabled and active.
*/
mtdiag_ctl = cpumf_state_ctl[CPUMF_CTR_SET_MT_DIAG];
if (!((cpuhw->info.auth_ctl & mtdiag_ctl) &&
(cpuhw->info.enable_ctl & mtdiag_ctl) &&
(cpuhw->info.act_ctl & mtdiag_ctl)))
err = -EOPNOTSUPP;
break;
}
put_cpu_var(cpu_hw_events);
@@ -250,6 +249,11 @@ static void cpumf_measurement_alert(struct ext_code ext_code,
/* loss of counter data alert */
if (alert & CPU_MF_INT_CF_LCDA)
pr_err("CPU[%i] Counter data was lost\n", smp_processor_id());
/* loss of MT counter data alert */
if (alert & CPU_MF_INT_CF_MTDA)
pr_warn("CPU[%i] MT counter data was lost\n",
smp_processor_id());
}
#define PMC_INIT 0
@@ -330,6 +334,7 @@ static int __hw_perf_event_init(struct perf_event *event)
{
struct perf_event_attr *attr = &event->attr;
struct hw_perf_event *hwc = &event->hw;
enum cpumf_ctr_set set;
int err;
u64 ev;
@@ -370,25 +375,30 @@ static int __hw_perf_event_init(struct perf_event *event)
if (ev == -1)
return -ENOENT;
if (ev >= PERF_CPUM_CF_MAX_CTR)
if (ev > PERF_CPUM_CF_MAX_CTR)
return -EINVAL;
/* Use the hardware perf event structure to store the counter number
* in 'config' member and the counter set to which the counter belongs
* in the 'config_base'. The counter set (config_base) is then used
* to enable/disable the counters.
*/
hwc->config = ev;
hwc->config_base = get_counter_set(ev);
/* Validate the counter that is assigned to this event.
* Because the counter facility can use numerous counters at the
* same time without constraints, it is not necessary to explicitly
* validate event groups (event->group_leader != event).
*/
err = validate_event(hwc);
if (err)
return err;
/* Obtain the counter set to which the specified counter belongs */
set = get_counter_set(ev);
switch (set) {
case CPUMF_CTR_SET_BASIC:
case CPUMF_CTR_SET_USER:
case CPUMF_CTR_SET_CRYPTO:
case CPUMF_CTR_SET_EXT:
case CPUMF_CTR_SET_MT_DIAG:
/*
* Use the hardware perf event structure to store the
* counter number in the 'config' member and the counter
* set number in the 'config_base'. The counter set number
* is then later used to enable/disable the counter(s).
*/
hwc->config = ev;
hwc->config_base = set;
break;
case CPUMF_CTR_SET_MAX:
/* The counter could not be associated to a counter set */
return -EINVAL;
};
/* Initialize for using the CPU-measurement counter facility */
if (!atomic_inc_not_zero(&num_events)) {
@@ -452,7 +462,7 @@ static int hw_perf_event_reset(struct perf_event *event)
return err;
}
static int hw_perf_event_update(struct perf_event *event)
static void hw_perf_event_update(struct perf_event *event)
{
u64 prev, new, delta;
int err;
@@ -461,14 +471,12 @@ static int hw_perf_event_update(struct perf_event *event)
prev = local64_read(&event->hw.prev_count);
err = ecctr(event->hw.config, &new);
if (err)
goto out;
return;
} while (local64_cmpxchg(&event->hw.prev_count, prev, new) != prev);
delta = (prev <= new) ? new - prev
: (-1ULL - prev) + new + 1; /* overflow */
local64_add(delta, &event->count);
out:
return err;
}
static void cpumf_pmu_read(struct perf_event *event)

View File

@@ -114,8 +114,64 @@ CPUMF_EVENT_ATTR(cf_zec12, L1I_OFFBOOK_L3_SOURCED_WRITES_IV, 0x00a1);
CPUMF_EVENT_ATTR(cf_zec12, TX_NC_TABORT, 0x00b1);
CPUMF_EVENT_ATTR(cf_zec12, TX_C_TABORT_NO_SPECIAL, 0x00b2);
CPUMF_EVENT_ATTR(cf_zec12, TX_C_TABORT_SPECIAL, 0x00b3);
CPUMF_EVENT_ATTR(cf_z13, L1D_WRITES_RO_EXCL, 0x0080);
CPUMF_EVENT_ATTR(cf_z13, DTLB1_WRITES, 0x0081);
CPUMF_EVENT_ATTR(cf_z13, DTLB1_MISSES, 0x0082);
CPUMF_EVENT_ATTR(cf_z13, DTLB1_HPAGE_WRITES, 0x0083);
CPUMF_EVENT_ATTR(cf_z13, DTLB1_GPAGE_WRITES, 0x0084);
CPUMF_EVENT_ATTR(cf_z13, L1D_L2D_SOURCED_WRITES, 0x0085);
CPUMF_EVENT_ATTR(cf_z13, ITLB1_WRITES, 0x0086);
CPUMF_EVENT_ATTR(cf_z13, ITLB1_MISSES, 0x0087);
CPUMF_EVENT_ATTR(cf_z13, L1I_L2I_SOURCED_WRITES, 0x0088);
CPUMF_EVENT_ATTR(cf_z13, TLB2_PTE_WRITES, 0x0089);
CPUMF_EVENT_ATTR(cf_z13, TLB2_CRSTE_HPAGE_WRITES, 0x008a);
CPUMF_EVENT_ATTR(cf_z13, TLB2_CRSTE_WRITES, 0x008b);
CPUMF_EVENT_ATTR(cf_z13, TX_C_TEND, 0x008c);
CPUMF_EVENT_ATTR(cf_z13, TX_NC_TEND, 0x008d);
CPUMF_EVENT_ATTR(cf_z13, L1C_TLB1_MISSES, 0x008f);
CPUMF_EVENT_ATTR(cf_z13, L1D_ONCHIP_L3_SOURCED_WRITES, 0x0090);
CPUMF_EVENT_ATTR(cf_z13, L1D_ONCHIP_L3_SOURCED_WRITES_IV, 0x0091);
CPUMF_EVENT_ATTR(cf_z13, L1D_ONNODE_L4_SOURCED_WRITES, 0x0092);
CPUMF_EVENT_ATTR(cf_z13, L1D_ONNODE_L3_SOURCED_WRITES_IV, 0x0093);
CPUMF_EVENT_ATTR(cf_z13, L1D_ONNODE_L3_SOURCED_WRITES, 0x0094);
CPUMF_EVENT_ATTR(cf_z13, L1D_ONDRAWER_L4_SOURCED_WRITES, 0x0095);
CPUMF_EVENT_ATTR(cf_z13, L1D_ONDRAWER_L3_SOURCED_WRITES_IV, 0x0096);
CPUMF_EVENT_ATTR(cf_z13, L1D_ONDRAWER_L3_SOURCED_WRITES, 0x0097);
CPUMF_EVENT_ATTR(cf_z13, L1D_OFFDRAWER_SCOL_L4_SOURCED_WRITES, 0x0098);
CPUMF_EVENT_ATTR(cf_z13, L1D_OFFDRAWER_SCOL_L3_SOURCED_WRITES_IV, 0x0099);
CPUMF_EVENT_ATTR(cf_z13, L1D_OFFDRAWER_SCOL_L3_SOURCED_WRITES, 0x009a);
CPUMF_EVENT_ATTR(cf_z13, L1D_OFFDRAWER_FCOL_L4_SOURCED_WRITES, 0x009b);
CPUMF_EVENT_ATTR(cf_z13, L1D_OFFDRAWER_FCOL_L3_SOURCED_WRITES_IV, 0x009c);
CPUMF_EVENT_ATTR(cf_z13, L1D_OFFDRAWER_FCOL_L3_SOURCED_WRITES, 0x009d);
CPUMF_EVENT_ATTR(cf_z13, L1D_ONNODE_MEM_SOURCED_WRITES, 0x009e);
CPUMF_EVENT_ATTR(cf_z13, L1D_ONDRAWER_MEM_SOURCED_WRITES, 0x009f);
CPUMF_EVENT_ATTR(cf_z13, L1D_OFFDRAWER_MEM_SOURCED_WRITES, 0x00a0);
CPUMF_EVENT_ATTR(cf_z13, L1D_ONCHIP_MEM_SOURCED_WRITES, 0x00a1);
CPUMF_EVENT_ATTR(cf_z13, L1I_ONCHIP_L3_SOURCED_WRITES, 0x00a2);
CPUMF_EVENT_ATTR(cf_z13, L1I_ONCHIP_L3_SOURCED_WRITES_IV, 0x00a3);
CPUMF_EVENT_ATTR(cf_z13, L1I_ONNODE_L4_SOURCED_WRITES, 0x00a4);
CPUMF_EVENT_ATTR(cf_z13, L1I_ONNODE_L3_SOURCED_WRITES_IV, 0x00a5);
CPUMF_EVENT_ATTR(cf_z13, L1I_ONNODE_L3_SOURCED_WRITES, 0x00a6);
CPUMF_EVENT_ATTR(cf_z13, L1I_ONDRAWER_L4_SOURCED_WRITES, 0x00a7);
CPUMF_EVENT_ATTR(cf_z13, L1I_ONDRAWER_L3_SOURCED_WRITES_IV, 0x00a8);
CPUMF_EVENT_ATTR(cf_z13, L1I_ONDRAWER_L3_SOURCED_WRITES, 0x00a9);
CPUMF_EVENT_ATTR(cf_z13, L1I_OFFDRAWER_SCOL_L4_SOURCED_WRITES, 0x00aa);
CPUMF_EVENT_ATTR(cf_z13, L1I_OFFDRAWER_SCOL_L3_SOURCED_WRITES_IV, 0x00ab);
CPUMF_EVENT_ATTR(cf_z13, L1I_OFFDRAWER_SCOL_L3_SOURCED_WRITES, 0x00ac);
CPUMF_EVENT_ATTR(cf_z13, L1I_OFFDRAWER_FCOL_L4_SOURCED_WRITES, 0x00ad);
CPUMF_EVENT_ATTR(cf_z13, L1I_OFFDRAWER_FCOL_L3_SOURCED_WRITES_IV, 0x00ae);
CPUMF_EVENT_ATTR(cf_z13, L1I_OFFDRAWER_FCOL_L3_SOURCED_WRITES, 0x00af);
CPUMF_EVENT_ATTR(cf_z13, L1I_ONNODE_MEM_SOURCED_WRITES, 0x00b0);
CPUMF_EVENT_ATTR(cf_z13, L1I_ONDRAWER_MEM_SOURCED_WRITES, 0x00b1);
CPUMF_EVENT_ATTR(cf_z13, L1I_OFFDRAWER_MEM_SOURCED_WRITES, 0x00b2);
CPUMF_EVENT_ATTR(cf_z13, L1I_ONCHIP_MEM_SOURCED_WRITES, 0x00b3);
CPUMF_EVENT_ATTR(cf_z13, TX_NC_TABORT, 0x00da);
CPUMF_EVENT_ATTR(cf_z13, TX_C_TABORT_NO_SPECIAL, 0x00db);
CPUMF_EVENT_ATTR(cf_z13, TX_C_TABORT_SPECIAL, 0x00dc);
CPUMF_EVENT_ATTR(cf_z13, MT_DIAG_CYCLES_ONE_THR_ACTIVE, 0x01c0);
CPUMF_EVENT_ATTR(cf_z13, MT_DIAG_CYCLES_TWO_THR_ACTIVE, 0x01c1);
static struct attribute *cpumcf_pmu_event_attr[] = {
static struct attribute *cpumcf_pmu_event_attr[] __initdata = {
CPUMF_EVENT_PTR(cf, CPU_CYCLES),
CPUMF_EVENT_PTR(cf, INSTRUCTIONS),
CPUMF_EVENT_PTR(cf, L1I_DIR_WRITES),
@@ -236,28 +292,87 @@ static struct attribute *cpumcf_zec12_pmu_event_attr[] __initdata = {
NULL,
};
static struct attribute *cpumcf_z13_pmu_event_attr[] __initdata = {
CPUMF_EVENT_PTR(cf_z13, L1D_WRITES_RO_EXCL),
CPUMF_EVENT_PTR(cf_z13, DTLB1_WRITES),
CPUMF_EVENT_PTR(cf_z13, DTLB1_MISSES),
CPUMF_EVENT_PTR(cf_z13, DTLB1_HPAGE_WRITES),
CPUMF_EVENT_PTR(cf_z13, DTLB1_GPAGE_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1D_L2D_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, ITLB1_WRITES),
CPUMF_EVENT_PTR(cf_z13, ITLB1_MISSES),
CPUMF_EVENT_PTR(cf_z13, L1I_L2I_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, TLB2_PTE_WRITES),
CPUMF_EVENT_PTR(cf_z13, TLB2_CRSTE_HPAGE_WRITES),
CPUMF_EVENT_PTR(cf_z13, TLB2_CRSTE_WRITES),
CPUMF_EVENT_PTR(cf_z13, TX_C_TEND),
CPUMF_EVENT_PTR(cf_z13, TX_NC_TEND),
CPUMF_EVENT_PTR(cf_z13, L1C_TLB1_MISSES),
CPUMF_EVENT_PTR(cf_z13, L1D_ONCHIP_L3_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1D_ONCHIP_L3_SOURCED_WRITES_IV),
CPUMF_EVENT_PTR(cf_z13, L1D_ONNODE_L4_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1D_ONNODE_L3_SOURCED_WRITES_IV),
CPUMF_EVENT_PTR(cf_z13, L1D_ONNODE_L3_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1D_ONDRAWER_L4_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1D_ONDRAWER_L3_SOURCED_WRITES_IV),
CPUMF_EVENT_PTR(cf_z13, L1D_ONDRAWER_L3_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1D_OFFDRAWER_SCOL_L4_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1D_OFFDRAWER_SCOL_L3_SOURCED_WRITES_IV),
CPUMF_EVENT_PTR(cf_z13, L1D_OFFDRAWER_SCOL_L3_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1D_OFFDRAWER_FCOL_L4_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1D_OFFDRAWER_FCOL_L3_SOURCED_WRITES_IV),
CPUMF_EVENT_PTR(cf_z13, L1D_OFFDRAWER_FCOL_L3_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1D_ONNODE_MEM_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1D_ONDRAWER_MEM_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1D_OFFDRAWER_MEM_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1D_ONCHIP_MEM_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1I_ONCHIP_L3_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1I_ONCHIP_L3_SOURCED_WRITES_IV),
CPUMF_EVENT_PTR(cf_z13, L1I_ONNODE_L4_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1I_ONNODE_L3_SOURCED_WRITES_IV),
CPUMF_EVENT_PTR(cf_z13, L1I_ONNODE_L3_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1I_ONDRAWER_L4_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1I_ONDRAWER_L3_SOURCED_WRITES_IV),
CPUMF_EVENT_PTR(cf_z13, L1I_ONDRAWER_L3_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1I_OFFDRAWER_SCOL_L4_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1I_OFFDRAWER_SCOL_L3_SOURCED_WRITES_IV),
CPUMF_EVENT_PTR(cf_z13, L1I_OFFDRAWER_SCOL_L3_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1I_OFFDRAWER_FCOL_L4_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1I_OFFDRAWER_FCOL_L3_SOURCED_WRITES_IV),
CPUMF_EVENT_PTR(cf_z13, L1I_OFFDRAWER_FCOL_L3_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1I_ONNODE_MEM_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1I_ONDRAWER_MEM_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1I_OFFDRAWER_MEM_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, L1I_ONCHIP_MEM_SOURCED_WRITES),
CPUMF_EVENT_PTR(cf_z13, TX_NC_TABORT),
CPUMF_EVENT_PTR(cf_z13, TX_C_TABORT_NO_SPECIAL),
CPUMF_EVENT_PTR(cf_z13, TX_C_TABORT_SPECIAL),
CPUMF_EVENT_PTR(cf_z13, MT_DIAG_CYCLES_ONE_THR_ACTIVE),
CPUMF_EVENT_PTR(cf_z13, MT_DIAG_CYCLES_TWO_THR_ACTIVE),
NULL,
};
/* END: CPUM_CF COUNTER DEFINITIONS ===================================== */
static struct attribute_group cpumsf_pmu_events_group = {
static struct attribute_group cpumcf_pmu_events_group = {
.name = "events",
.attrs = cpumcf_pmu_event_attr,
};
PMU_FORMAT_ATTR(event, "config:0-63");
static struct attribute *cpumsf_pmu_format_attr[] = {
static struct attribute *cpumcf_pmu_format_attr[] = {
&format_attr_event.attr,
NULL,
};
static struct attribute_group cpumsf_pmu_format_group = {
static struct attribute_group cpumcf_pmu_format_group = {
.name = "format",
.attrs = cpumsf_pmu_format_attr,
.attrs = cpumcf_pmu_format_attr,
};
static const struct attribute_group *cpumsf_pmu_attr_groups[] = {
&cpumsf_pmu_events_group,
&cpumsf_pmu_format_group,
static const struct attribute_group *cpumcf_pmu_attr_groups[] = {
&cpumcf_pmu_events_group,
&cpumcf_pmu_format_group,
NULL,
};
@@ -290,6 +405,7 @@ static __init struct attribute **merge_attr(struct attribute **a,
__init const struct attribute_group **cpumf_cf_event_group(void)
{
struct attribute **combined, **model;
struct attribute *none[] = { NULL };
struct cpuid cpu_id;
get_cpu_id(&cpu_id);
@@ -306,17 +422,17 @@ __init const struct attribute_group **cpumf_cf_event_group(void)
case 0x2828:
model = cpumcf_zec12_pmu_event_attr;
break;
case 0x2964:
case 0x2965:
model = cpumcf_z13_pmu_event_attr;
break;
default:
model = NULL;
model = none;
break;
}
if (!model)
goto out;
combined = merge_attr(cpumcf_pmu_event_attr, model);
if (combined)
cpumsf_pmu_events_group.attrs = combined;
out:
return cpumsf_pmu_attr_groups;
cpumcf_pmu_events_group.attrs = combined;
return cpumcf_pmu_attr_groups;
}

View File

@@ -1009,8 +1009,8 @@ static int perf_push_sample(struct perf_event *event, struct sf_raw_sample *sfr)
* sample. Some early samples or samples from guests without
* lpp usage would be misaccounted to the host. We use the asn
* value as an addon heuristic to detect most of these guest samples.
* If the value differs from the host hpp value, we assume to be a
* KVM guest.
* If the value differs from 0xffff (the host value), we assume to
* be a KVM guest.
*/
switch (sfr->basic.CL) {
case 1: /* logical partition */
@@ -1020,8 +1020,7 @@ static int perf_push_sample(struct perf_event *event, struct sf_raw_sample *sfr)
sde_regs->in_guest = 1;
break;
default: /* old machine, use heuristics */
if (sfr->basic.gpp ||
sfr->basic.prim_asn != (u16)sfr->basic.hpp)
if (sfr->basic.gpp || sfr->basic.prim_asn != 0xffff)
sde_regs->in_guest = 1;
break;
}

View File

@@ -73,8 +73,10 @@ extern void kernel_thread_starter(void);
*/
void exit_thread(struct task_struct *tsk)
{
if (tsk == current)
if (tsk == current) {
exit_thread_runtime_instr();
exit_thread_gs();
}
}
void flush_thread(void)
@@ -159,6 +161,9 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long new_stackp,
/* Don't copy runtime instrumentation info */
p->thread.ri_cb = NULL;
frame->childregs.psw.mask &= ~PSW_MASK_RI;
/* Don't copy guarded storage control block */
p->thread.gs_cb = NULL;
p->thread.gs_bc_cb = NULL;
/* Set a new TLS ? */
if (clone_flags & CLONE_SETTLS) {

View File

@@ -7,6 +7,7 @@
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#include <linux/cpufeature.h>
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/sched/mm.h>
#include <linux/init.h>
@@ -91,11 +92,23 @@ int cpu_have_feature(unsigned int num)
}
EXPORT_SYMBOL(cpu_have_feature);
static void show_facilities(struct seq_file *m)
{
unsigned int bit;
long *facilities;
facilities = (long *)&S390_lowcore.stfle_fac_list;
seq_puts(m, "facilities :");
for_each_set_bit_inv(bit, facilities, MAX_FACILITY_BIT)
seq_printf(m, " %d", bit);
seq_putc(m, '\n');
}
static void show_cpu_summary(struct seq_file *m, void *v)
{
static const char *hwcap_str[] = {
"esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp",
"edat", "etf3eh", "highgprs", "te", "vx", "vxd", "vxe"
"edat", "etf3eh", "highgprs", "te", "vx", "vxd", "vxe", "gs"
};
static const char * const int_hwcap_str[] = {
"sie"
@@ -116,6 +129,7 @@ static void show_cpu_summary(struct seq_file *m, void *v)
if (int_hwcap_str[i] && (int_hwcap & (1UL << i)))
seq_printf(m, "%s ", int_hwcap_str[i]);
seq_puts(m, "\n");
show_facilities(m);
show_cacheinfo(m);
for_each_online_cpu(cpu) {
struct cpuid *id = &per_cpu(cpu_info.cpu_id, cpu);

View File

@@ -44,30 +44,42 @@ void update_cr_regs(struct task_struct *task)
struct pt_regs *regs = task_pt_regs(task);
struct thread_struct *thread = &task->thread;
struct per_regs old, new;
unsigned long cr0_old, cr0_new;
unsigned long cr2_old, cr2_new;
int cr0_changed, cr2_changed;
__ctl_store(cr0_old, 0, 0);
__ctl_store(cr2_old, 2, 2);
cr0_new = cr0_old;
cr2_new = cr2_old;
/* Take care of the enable/disable of transactional execution. */
if (MACHINE_HAS_TE) {
unsigned long cr, cr_new;
__ctl_store(cr, 0, 0);
/* Set or clear transaction execution TXC bit 8. */
cr_new = cr | (1UL << 55);
cr0_new |= (1UL << 55);
if (task->thread.per_flags & PER_FLAG_NO_TE)
cr_new &= ~(1UL << 55);
if (cr_new != cr)
__ctl_load(cr_new, 0, 0);
cr0_new &= ~(1UL << 55);
/* Set or clear transaction execution TDC bits 62 and 63. */
__ctl_store(cr, 2, 2);
cr_new = cr & ~3UL;
cr2_new &= ~3UL;
if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND) {
if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND_TEND)
cr_new |= 1UL;
cr2_new |= 1UL;
else
cr_new |= 2UL;
cr2_new |= 2UL;
}
if (cr_new != cr)
__ctl_load(cr_new, 2, 2);
}
/* Take care of enable/disable of guarded storage. */
if (MACHINE_HAS_GS) {
cr2_new &= ~(1UL << 4);
if (task->thread.gs_cb)
cr2_new |= (1UL << 4);
}
/* Load control register 0/2 iff changed */
cr0_changed = cr0_new != cr0_old;
cr2_changed = cr2_new != cr2_old;
if (cr0_changed)
__ctl_load(cr0_new, 0, 0);
if (cr2_changed)
__ctl_load(cr2_new, 2, 2);
/* Copy user specified PER registers */
new.control = thread->per_user.control;
new.start = thread->per_user.start;
@@ -1137,6 +1149,74 @@ static int s390_system_call_set(struct task_struct *target,
data, 0, sizeof(unsigned int));
}
static int s390_gs_cb_get(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf)
{
struct gs_cb *data = target->thread.gs_cb;
if (!MACHINE_HAS_GS)
return -ENODEV;
if (!data)
return -ENODATA;
return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
data, 0, sizeof(struct gs_cb));
}
static int s390_gs_cb_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
struct gs_cb *data = target->thread.gs_cb;
if (!MACHINE_HAS_GS)
return -ENODEV;
if (!data) {
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
target->thread.gs_cb = data;
}
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
data, 0, sizeof(struct gs_cb));
}
static int s390_gs_bc_get(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf)
{
struct gs_cb *data = target->thread.gs_bc_cb;
if (!MACHINE_HAS_GS)
return -ENODEV;
if (!data)
return -ENODATA;
return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
data, 0, sizeof(struct gs_cb));
}
static int s390_gs_bc_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
struct gs_cb *data = target->thread.gs_bc_cb;
if (!MACHINE_HAS_GS)
return -ENODEV;
if (!data) {
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
target->thread.gs_bc_cb = data;
}
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
data, 0, sizeof(struct gs_cb));
}
static const struct user_regset s390_regsets[] = {
{
.core_note_type = NT_PRSTATUS,
@@ -1194,6 +1274,22 @@ static const struct user_regset s390_regsets[] = {
.get = s390_vxrs_high_get,
.set = s390_vxrs_high_set,
},
{
.core_note_type = NT_S390_GS_CB,
.n = sizeof(struct gs_cb) / sizeof(__u64),
.size = sizeof(__u64),
.align = sizeof(__u64),
.get = s390_gs_cb_get,
.set = s390_gs_cb_set,
},
{
.core_note_type = NT_S390_GS_BC,
.n = sizeof(struct gs_cb) / sizeof(__u64),
.size = sizeof(__u64),
.align = sizeof(__u64),
.get = s390_gs_bc_get,
.set = s390_gs_bc_set,
},
};
static const struct user_regset_view user_s390_view = {
@@ -1422,6 +1518,14 @@ static const struct user_regset s390_compat_regsets[] = {
.get = s390_compat_regs_high_get,
.set = s390_compat_regs_high_set,
},
{
.core_note_type = NT_S390_GS_CB,
.n = sizeof(struct gs_cb) / sizeof(__u64),
.size = sizeof(__u64),
.align = sizeof(__u64),
.get = s390_gs_cb_get,
.set = s390_gs_cb_set,
},
};
static const struct user_regset_view user_s390_compat_view = {

View File

@@ -339,9 +339,15 @@ static void __init setup_lowcore(void)
lc->stfl_fac_list = S390_lowcore.stfl_fac_list;
memcpy(lc->stfle_fac_list, S390_lowcore.stfle_fac_list,
MAX_FACILITY_BIT/8);
if (MACHINE_HAS_VX)
lc->vector_save_area_addr =
(unsigned long) &lc->vector_save_area;
if (MACHINE_HAS_VX || MACHINE_HAS_GS) {
unsigned long bits, size;
bits = MACHINE_HAS_GS ? 11 : 10;
size = 1UL << bits;
lc->mcesad = (__u64) memblock_virt_alloc(size, size);
if (MACHINE_HAS_GS)
lc->mcesad |= bits;
}
lc->vdso_per_cpu_data = (unsigned long) &lc->paste[0];
lc->sync_enter_timer = S390_lowcore.sync_enter_timer;
lc->async_enter_timer = S390_lowcore.async_enter_timer;
@@ -779,6 +785,12 @@ static int __init setup_hwcaps(void)
elf_hwcap |= HWCAP_S390_VXRS_BCD;
}
/*
* Guarded storage support HWCAP_S390_GS is bit 12.
*/
if (MACHINE_HAS_GS)
elf_hwcap |= HWCAP_S390_GS;
get_cpu_id(&cpu_id);
add_device_randomness(&cpu_id, sizeof(cpu_id));
switch (cpu_id.machine) {

View File

@@ -51,6 +51,7 @@
#include <asm/os_info.h>
#include <asm/sigp.h>
#include <asm/idle.h>
#include <asm/nmi.h>
#include "entry.h"
enum {
@@ -78,6 +79,8 @@ struct pcpu {
static u8 boot_core_type;
static struct pcpu pcpu_devices[NR_CPUS];
static struct kmem_cache *pcpu_mcesa_cache;
unsigned int smp_cpu_mt_shift;
EXPORT_SYMBOL(smp_cpu_mt_shift);
@@ -188,8 +191,10 @@ static void pcpu_ec_call(struct pcpu *pcpu, int ec_bit)
static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu)
{
unsigned long async_stack, panic_stack;
unsigned long mcesa_origin, mcesa_bits;
struct lowcore *lc;
mcesa_origin = mcesa_bits = 0;
if (pcpu != &pcpu_devices[0]) {
pcpu->lowcore = (struct lowcore *)
__get_free_pages(GFP_KERNEL | GFP_DMA, LC_ORDER);
@@ -197,20 +202,27 @@ static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu)
panic_stack = __get_free_page(GFP_KERNEL);
if (!pcpu->lowcore || !panic_stack || !async_stack)
goto out;
if (MACHINE_HAS_VX || MACHINE_HAS_GS) {
mcesa_origin = (unsigned long)
kmem_cache_alloc(pcpu_mcesa_cache, GFP_KERNEL);
if (!mcesa_origin)
goto out;
mcesa_bits = MACHINE_HAS_GS ? 11 : 0;
}
} else {
async_stack = pcpu->lowcore->async_stack - ASYNC_FRAME_OFFSET;
panic_stack = pcpu->lowcore->panic_stack - PANIC_FRAME_OFFSET;
mcesa_origin = pcpu->lowcore->mcesad & MCESA_ORIGIN_MASK;
mcesa_bits = pcpu->lowcore->mcesad & MCESA_LC_MASK;
}
lc = pcpu->lowcore;
memcpy(lc, &S390_lowcore, 512);
memset((char *) lc + 512, 0, sizeof(*lc) - 512);
lc->async_stack = async_stack + ASYNC_FRAME_OFFSET;
lc->panic_stack = panic_stack + PANIC_FRAME_OFFSET;
lc->mcesad = mcesa_origin | mcesa_bits;
lc->cpu_nr = cpu;
lc->spinlock_lockval = arch_spin_lockval(cpu);
if (MACHINE_HAS_VX)
lc->vector_save_area_addr =
(unsigned long) &lc->vector_save_area;
if (vdso_alloc_per_cpu(lc))
goto out;
lowcore_ptr[cpu] = lc;
@@ -218,6 +230,9 @@ static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu)
return 0;
out:
if (pcpu != &pcpu_devices[0]) {
if (mcesa_origin)
kmem_cache_free(pcpu_mcesa_cache,
(void *) mcesa_origin);
free_page(panic_stack);
free_pages(async_stack, ASYNC_ORDER);
free_pages((unsigned long) pcpu->lowcore, LC_ORDER);
@@ -229,11 +244,17 @@ out:
static void pcpu_free_lowcore(struct pcpu *pcpu)
{
unsigned long mcesa_origin;
pcpu_sigp_retry(pcpu, SIGP_SET_PREFIX, 0);
lowcore_ptr[pcpu - pcpu_devices] = NULL;
vdso_free_per_cpu(pcpu->lowcore);
if (pcpu == &pcpu_devices[0])
return;
if (MACHINE_HAS_VX || MACHINE_HAS_GS) {
mcesa_origin = pcpu->lowcore->mcesad & MCESA_ORIGIN_MASK;
kmem_cache_free(pcpu_mcesa_cache, (void *) mcesa_origin);
}
free_page(pcpu->lowcore->panic_stack-PANIC_FRAME_OFFSET);
free_pages(pcpu->lowcore->async_stack-ASYNC_FRAME_OFFSET, ASYNC_ORDER);
free_pages((unsigned long) pcpu->lowcore, LC_ORDER);
@@ -550,9 +571,11 @@ int smp_store_status(int cpu)
if (__pcpu_sigp_relax(pcpu->address, SIGP_STORE_STATUS_AT_ADDRESS,
pa) != SIGP_CC_ORDER_CODE_ACCEPTED)
return -EIO;
if (!MACHINE_HAS_VX)
if (!MACHINE_HAS_VX && !MACHINE_HAS_GS)
return 0;
pa = __pa(pcpu->lowcore->vector_save_area_addr);
pa = __pa(pcpu->lowcore->mcesad & MCESA_ORIGIN_MASK);
if (MACHINE_HAS_GS)
pa |= pcpu->lowcore->mcesad & MCESA_LC_MASK;
if (__pcpu_sigp_relax(pcpu->address, SIGP_STORE_ADDITIONAL_STATUS,
pa) != SIGP_CC_ORDER_CODE_ACCEPTED)
return -EIO;
@@ -897,12 +920,22 @@ void __init smp_fill_possible_mask(void)
void __init smp_prepare_cpus(unsigned int max_cpus)
{
unsigned long size;
/* request the 0x1201 emergency signal external interrupt */
if (register_external_irq(EXT_IRQ_EMERGENCY_SIG, do_ext_call_interrupt))
panic("Couldn't request external interrupt 0x1201");
/* request the 0x1202 external call external interrupt */
if (register_external_irq(EXT_IRQ_EXTERNAL_CALL, do_ext_call_interrupt))
panic("Couldn't request external interrupt 0x1202");
/* create slab cache for the machine-check-extended-save-areas */
if (MACHINE_HAS_VX || MACHINE_HAS_GS) {
size = 1UL << (MACHINE_HAS_GS ? 11 : 10);
pcpu_mcesa_cache = kmem_cache_create("nmi_save_areas",
size, size, 0, NULL);
if (!pcpu_mcesa_cache)
panic("Couldn't create nmi save area cache");
}
}
void __init smp_prepare_boot_cpu(void)

View File

@@ -386,5 +386,5 @@ SYSCALL(sys_mlock2,compat_sys_mlock2)
SYSCALL(sys_copy_file_range,compat_sys_copy_file_range) /* 375 */
SYSCALL(sys_preadv2,compat_sys_preadv2)
SYSCALL(sys_pwritev2,compat_sys_pwritev2)
NI_SYSCALL
SYSCALL(sys_s390_guarded_storage,compat_sys_s390_guarded_storage) /* 378 */
SYSCALL(sys_statx,compat_sys_statx)

View File

@@ -4,6 +4,7 @@
* Martin Schwidefsky <schwidefsky@de.ibm.com>,
*/
#include <linux/debugfs.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
@@ -13,6 +14,7 @@
#include <linux/export.h>
#include <linux/slab.h>
#include <asm/ebcdic.h>
#include <asm/debug.h>
#include <asm/sysinfo.h>
#include <asm/cpcmd.h>
#include <asm/topology.h>
@@ -485,3 +487,99 @@ void calibrate_delay(void)
"%lu.%02lu BogoMIPS preset\n", loops_per_jiffy/(500000/HZ),
(loops_per_jiffy/(5000/HZ)) % 100);
}
#ifdef CONFIG_DEBUG_FS
#define STSI_FILE(fc, s1, s2) \
static int stsi_open_##fc##_##s1##_##s2(struct inode *inode, struct file *file)\
{ \
file->private_data = (void *) get_zeroed_page(GFP_KERNEL); \
if (!file->private_data) \
return -ENOMEM; \
if (stsi(file->private_data, fc, s1, s2)) { \
free_page((unsigned long)file->private_data); \
file->private_data = NULL; \
return -EACCES; \
} \
return nonseekable_open(inode, file); \
} \
\
static const struct file_operations stsi_##fc##_##s1##_##s2##_fs_ops = { \
.open = stsi_open_##fc##_##s1##_##s2, \
.release = stsi_release, \
.read = stsi_read, \
.llseek = no_llseek, \
};
static int stsi_release(struct inode *inode, struct file *file)
{
free_page((unsigned long)file->private_data);
return 0;
}
static ssize_t stsi_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
return simple_read_from_buffer(buf, size, ppos, file->private_data, PAGE_SIZE);
}
STSI_FILE( 1, 1, 1);
STSI_FILE( 1, 2, 1);
STSI_FILE( 1, 2, 2);
STSI_FILE( 2, 2, 1);
STSI_FILE( 2, 2, 2);
STSI_FILE( 3, 2, 2);
STSI_FILE(15, 1, 2);
STSI_FILE(15, 1, 3);
STSI_FILE(15, 1, 4);
STSI_FILE(15, 1, 5);
STSI_FILE(15, 1, 6);
struct stsi_file {
const struct file_operations *fops;
char *name;
};
static struct stsi_file stsi_file[] __initdata = {
{.fops = &stsi_1_1_1_fs_ops, .name = "1_1_1"},
{.fops = &stsi_1_2_1_fs_ops, .name = "1_2_1"},
{.fops = &stsi_1_2_2_fs_ops, .name = "1_2_2"},
{.fops = &stsi_2_2_1_fs_ops, .name = "2_2_1"},
{.fops = &stsi_2_2_2_fs_ops, .name = "2_2_2"},
{.fops = &stsi_3_2_2_fs_ops, .name = "3_2_2"},
{.fops = &stsi_15_1_2_fs_ops, .name = "15_1_2"},
{.fops = &stsi_15_1_3_fs_ops, .name = "15_1_3"},
{.fops = &stsi_15_1_4_fs_ops, .name = "15_1_4"},
{.fops = &stsi_15_1_5_fs_ops, .name = "15_1_5"},
{.fops = &stsi_15_1_6_fs_ops, .name = "15_1_6"},
};
static u8 stsi_0_0_0;
static __init int stsi_init_debugfs(void)
{
struct dentry *stsi_root;
struct stsi_file *sf;
int lvl, i;
stsi_root = debugfs_create_dir("stsi", arch_debugfs_dir);
if (IS_ERR_OR_NULL(stsi_root))
return 0;
lvl = stsi(NULL, 0, 0, 0);
if (lvl > 0)
stsi_0_0_0 = lvl;
debugfs_create_u8("0_0_0", 0400, stsi_root, &stsi_0_0_0);
for (i = 0; i < ARRAY_SIZE(stsi_file); i++) {
sf = &stsi_file[i];
debugfs_create_file(sf->name, 0400, stsi_root, NULL, sf->fops);
}
if (IS_ENABLED(CONFIG_SCHED_TOPOLOGY) && MACHINE_HAS_TOPOLOGY) {
char link_to[10];
sprintf(link_to, "15_1_%d", topology_mnest_limit());
debugfs_create_symlink("topology", stsi_root, link_to);
}
return 0;
}
device_initcall(stsi_init_debugfs);
#endif /* CONFIG_DEBUG_FS */

View File

@@ -83,6 +83,8 @@ static cpumask_t cpu_thread_map(unsigned int cpu)
return mask;
}
#define TOPOLOGY_CORE_BITS 64
static void add_cpus_to_mask(struct topology_core *tl_core,
struct mask_info *drawer,
struct mask_info *book,
@@ -91,7 +93,7 @@ static void add_cpus_to_mask(struct topology_core *tl_core,
struct cpu_topology_s390 *topo;
unsigned int core;
for_each_set_bit(core, &tl_core->mask[0], TOPOLOGY_CORE_BITS) {
for_each_set_bit(core, &tl_core->mask, TOPOLOGY_CORE_BITS) {
unsigned int rcore;
int lcpu, i;
@@ -244,7 +246,7 @@ static void update_cpu_masks(void)
void store_topology(struct sysinfo_15_1_x *info)
{
stsi(info, 15, 1, min(topology_max_mnest, 4));
stsi(info, 15, 1, topology_mnest_limit());
}
static int __arch_update_cpu_topology(void)