summaryrefslogtreecommitdiff
path: root/arch/s390/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390/kernel')
-rw-r--r--arch/s390/kernel/Makefile3
-rw-r--r--arch/s390/kernel/debug.c64
-rw-r--r--arch/s390/kernel/diag/diag324.c3
-rw-r--r--arch/s390/kernel/entry.S9
-rw-r--r--arch/s390/kernel/entry.h10
-rw-r--r--arch/s390/kernel/idle.c1
-rw-r--r--arch/s390/kernel/ipl.c70
-rw-r--r--arch/s390/kernel/irq.c4
-rw-r--r--arch/s390/kernel/irqflags.c28
-rw-r--r--arch/s390/kernel/mcount.S9
-rw-r--r--arch/s390/kernel/nmi.c5
-rw-r--r--arch/s390/kernel/perf_cpum_cf.c216
-rw-r--r--arch/s390/kernel/perf_pai.c223
-rw-r--r--arch/s390/kernel/smp.c22
-rw-r--r--arch/s390/kernel/syscall.c15
-rw-r--r--arch/s390/kernel/syscalls/syscall.tbl1
-rw-r--r--arch/s390/kernel/sysinfo.c2
-rw-r--r--arch/s390/kernel/text_amode31.S13
-rw-r--r--arch/s390/kernel/topology.c2
-rw-r--r--arch/s390/kernel/traps.c45
-rw-r--r--arch/s390/kernel/uv.c67
-rw-r--r--arch/s390/kernel/vdso/Makefile3
-rw-r--r--arch/s390/kernel/vdso/vdso.lds.S11
-rw-r--r--arch/s390/kernel/vtime.c6
24 files changed, 595 insertions, 237 deletions
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
index 6c88476d79a3..f84b8fb41331 100644
--- a/arch/s390/kernel/Makefile
+++ b/arch/s390/kernel/Makefile
@@ -3,6 +3,8 @@
# Makefile for the linux kernel.
#
+CONTEXT_ANALYSIS := y
+
ifdef CONFIG_FUNCTION_TRACER
# Do not trace tracer code
@@ -72,6 +74,7 @@ obj-$(CONFIG_STACKPROTECTOR) += stackprotector.o
obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_image.o
obj-$(CONFIG_KEXEC_FILE) += kexec_elf.o
obj-$(CONFIG_CERT_STORE) += cert_store.o
+obj-$(CONFIG_KMSAN) += irqflags.o
obj-$(CONFIG_PERF_EVENTS) += perf_event.o
obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf.o perf_cpum_sf.o
diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c
index dbf430f479bd..b5bf8284dbfc 100644
--- a/arch/s390/kernel/debug.c
+++ b/arch/s390/kernel/debug.c
@@ -182,7 +182,7 @@ static struct debug_param_t {
static int debug_param_num;
/* functions */
-static void debug_get_param(const char *name, int *level, int *pages)
+static void debug_get_param(const char *name, int *level, int *pages, bool quiet)
{
struct debug_param_t *p;
int i;
@@ -192,11 +192,13 @@ static void debug_get_param(const char *name, int *level, int *pages)
if (!glob_match(p->name, name))
continue;
if (level && p->level != PARAM_UNSET) {
- pr_info("%s: override level to %d\n", name, p->level);
+ if (!quiet)
+ pr_info("%s: override level to %d\n", name, p->level);
*level = p->level;
}
if (pages && p->pages != PARAM_UNSET) {
- pr_info("%s: override pages to %d\n", name, p->pages);
+ if (!quiet)
+ pr_info("%s: override pages to %d\n", name, p->pages);
*pages = p->pages;
}
}
@@ -251,7 +253,7 @@ static int __init s390dbf_parse(char *arg)
* regular memory allocations are possible.
*/
for (i = 0, id = __s390dbf_info; &id[i] < __s390dbf_info_end; i++)
- debug_get_param(id[i]->name, &id[i]->level, NULL);
+ debug_get_param(id[i]->name, &id[i]->level, NULL, false);
return rc;
}
@@ -395,7 +397,7 @@ static debug_info_t *debug_info_create(const char *name, int pages_per_area,
int level = DEBUG_DEFAULT_LEVEL;
debug_info_t *rc;
- debug_get_param(name, &level, &pages_per_area);
+ debug_get_param(name, &level, &pages_per_area, false);
rc = debug_info_alloc(name, pages_per_area, nr_areas, buf_size, level, ALL_AREAS);
if (!rc)
goto out;
@@ -960,7 +962,7 @@ void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)
return;
}
- debug_get_param(id->name, &id->level, &pages_per_area);
+ debug_get_param(id->name, &id->level, &pages_per_area, false);
copy = debug_info_alloc("", pages_per_area, nr_areas, id->buf_size,
id->level, ALL_AREAS);
if (!copy) {
@@ -993,8 +995,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 +1006,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 +1036,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);
}
@@ -1068,9 +1076,6 @@ static void _debug_set_level(debug_info_t *id, int new_level)
{
unsigned long flags;
- if (!id)
- return;
-
if (new_level == DEBUG_OFF_LEVEL) {
pr_info("%s: switched off\n", id->name);
} else if ((new_level > DEBUG_MAX_LEVEL) || (new_level < 0)) {
@@ -1095,8 +1100,14 @@ static void _debug_set_level(debug_info_t *id, int new_level)
*/
void debug_set_level(debug_info_t *id, int new_level)
{
- /* Level specified via kernel parameter takes precedence */
- debug_get_param(id->name, &new_level, NULL);
+ if (!id)
+ return;
+
+ /*
+ * Level specified via kernel parameter takes precedence. The override
+ * was already announced during registration, so stay quiet here.
+ */
+ debug_get_param(id->name, &new_level, NULL, true);
_debug_set_level(id, new_level);
}
@@ -1272,7 +1283,7 @@ void debug_set_critical(void)
debug_entry_t *debug_event_common(debug_info_t *id, int level, const void *buf,
int len)
{
- debug_entry_t *active;
+ debug_entry_t *active = NULL;
unsigned long flags;
if (!debug_active || !id->areas)
@@ -1283,6 +1294,8 @@ debug_entry_t *debug_event_common(debug_info_t *id, int level, const void *buf,
} else {
raw_spin_lock_irqsave(&id->lock, flags);
}
+ if (!id->areas)
+ goto out;
do {
active = get_active_entry(id);
memcpy(DEBUG_DATA(active), buf, min(len, id->buf_size));
@@ -1292,7 +1305,7 @@ debug_entry_t *debug_event_common(debug_info_t *id, int level, const void *buf,
len -= id->buf_size;
buf += id->buf_size;
} while (len > 0);
-
+out:
raw_spin_unlock_irqrestore(&id->lock, flags);
return active;
}
@@ -1305,7 +1318,7 @@ EXPORT_SYMBOL(debug_event_common);
debug_entry_t *debug_exception_common(debug_info_t *id, int level,
const void *buf, int len)
{
- debug_entry_t *active;
+ debug_entry_t *active = NULL;
unsigned long flags;
if (!debug_active || !id->areas)
@@ -1316,6 +1329,8 @@ debug_entry_t *debug_exception_common(debug_info_t *id, int level,
} else {
raw_spin_lock_irqsave(&id->lock, flags);
}
+ if (!id->areas)
+ goto out;
do {
active = get_active_entry(id);
memcpy(DEBUG_DATA(active), buf, min(len, id->buf_size));
@@ -1325,7 +1340,7 @@ debug_entry_t *debug_exception_common(debug_info_t *id, int level,
len -= id->buf_size;
buf += id->buf_size;
} while (len > 0);
-
+out:
raw_spin_unlock_irqrestore(&id->lock, flags);
return active;
}
@@ -1351,7 +1366,7 @@ static inline int debug_count_numargs(char *string)
debug_entry_t *__debug_sprintf_event(debug_info_t *id, int level, char *string, ...)
{
debug_sprintf_entry_t *curr_event;
- debug_entry_t *active;
+ debug_entry_t *active = NULL;
unsigned long flags;
int numargs, idx;
va_list ap;
@@ -1366,6 +1381,8 @@ debug_entry_t *__debug_sprintf_event(debug_info_t *id, int level, char *string,
} else {
raw_spin_lock_irqsave(&id->lock, flags);
}
+ if (!id->areas)
+ goto out;
active = get_active_entry(id);
curr_event = (debug_sprintf_entry_t *) DEBUG_DATA(active);
va_start(ap, string);
@@ -1374,6 +1391,7 @@ debug_entry_t *__debug_sprintf_event(debug_info_t *id, int level, char *string,
curr_event->args[idx] = va_arg(ap, long);
va_end(ap);
debug_finish_entry(id, active, level, 0);
+out:
raw_spin_unlock_irqrestore(&id->lock, flags);
return active;
@@ -1386,7 +1404,7 @@ EXPORT_SYMBOL(__debug_sprintf_event);
debug_entry_t *__debug_sprintf_exception(debug_info_t *id, int level, char *string, ...)
{
debug_sprintf_entry_t *curr_event;
- debug_entry_t *active;
+ debug_entry_t *active = NULL;
unsigned long flags;
int numargs, idx;
va_list ap;
@@ -1402,6 +1420,8 @@ debug_entry_t *__debug_sprintf_exception(debug_info_t *id, int level, char *stri
} else {
raw_spin_lock_irqsave(&id->lock, flags);
}
+ if (!id->areas)
+ goto out;
active = get_active_entry(id);
curr_event = (debug_sprintf_entry_t *)DEBUG_DATA(active);
va_start(ap, string);
@@ -1410,6 +1430,7 @@ debug_entry_t *__debug_sprintf_exception(debug_info_t *id, int level, char *stri
curr_event->args[idx] = va_arg(ap, long);
va_end(ap);
debug_finish_entry(id, active, level, 1);
+out:
raw_spin_unlock_irqrestore(&id->lock, flags);
return active;
@@ -1652,9 +1673,11 @@ static void debug_flush(debug_info_t *id, int area)
unsigned long flags;
int i, j;
- if (!id || !id->areas)
+ if (!id)
return;
raw_spin_lock_irqsave(&id->lock, flags);
+ if (!id->areas)
+ goto out;
if (area == DEBUG_FLUSH_ALL) {
id->active_area = 0;
memset(id->active_entries, 0, id->nr_areas * sizeof(int));
@@ -1669,6 +1692,7 @@ static void debug_flush(debug_info_t *id, int area)
for (i = 0; i < id->pages_per_area; i++)
memset(id->areas[area][i], 0, PAGE_SIZE);
}
+out:
raw_spin_unlock_irqrestore(&id->lock, flags);
}
diff --git a/arch/s390/kernel/diag/diag324.c b/arch/s390/kernel/diag/diag324.c
index fe325c2a2d0d..3eec0cc8fb9e 100644
--- a/arch/s390/kernel/diag/diag324.c
+++ b/arch/s390/kernel/diag/diag324.c
@@ -182,8 +182,7 @@ long diag324_pibbuf(unsigned long arg)
goto out;
rc = copy_to_user((void __user *)address, data->pib, data->pib->len);
rc |= put_user(data->sequence, &udata->sequence);
- if (rc)
- rc = -EFAULT;
+ rc = rc ? -EFAULT : data->rc;
out:
mutex_unlock(&pibmutex);
return rc;
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
index 79a45efae23d..10dd9bbdf985 100644
--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -31,6 +31,7 @@
#include <asm/nospec-insn.h>
#include <asm/lowcore.h>
#include <asm/machine.h>
+#include "entry.h"
_LPP_OFFSET = __LC_LPP
@@ -321,7 +322,7 @@ SYM_CODE_START(pgm_check_handler)
jz 1f
BPENTER __SF_SIE_FLAGS(%r15),_TIF_ISOLATE_BP_GUEST
SIEEXIT __SF_SIE_CONTROL(%r15),%r13
- lghi %r10,_PIF_GUEST_FAULT
+ lghi %r10,PGM_FLAG_GUEST_FAULT
#endif
1: tmhh %r8,0x4000 # PER bit set in old PSW ?
jnz 2f # -> enabled, can't be a double fault
@@ -332,7 +333,7 @@ SYM_CODE_START(pgm_check_handler)
CHECK_VMAP_STACK __LC_SAVE_AREA,%r13,4f
3: lg %r15,__LC_KERNEL_STACK(%r13)
4: la %r11,STACK_FRAME_OVERHEAD(%r15)
- stg %r10,__PT_FLAGS(%r11)
+ xc __PT_FLAGS(8,%r11),__PT_FLAGS(%r11)
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
stmg %r0,%r7,__PT_R0(%r11)
mvc __PT_R8(64,%r11),__LC_SAVE_AREA(%r13)
@@ -341,13 +342,13 @@ SYM_CODE_START(pgm_check_handler)
# clear user controlled registers to prevent speculative use
xgr %r0,%r0
xgr %r1,%r1
- xgr %r3,%r3
xgr %r4,%r4
xgr %r5,%r5
xgr %r6,%r6
xgr %r7,%r7
xgr %r12,%r12
lgr %r2,%r11
+ lgr %r3,%r10
brasl %r14,__do_pgm_check
tmhh %r8,0x0001 # returning to user space?
jno .Lpgm_exit_kernel
@@ -367,7 +368,7 @@ SYM_CODE_START(pgm_check_handler)
mvc __LC_RETURN_PSW(8,%r13),__LC_SVC_NEW_PSW(%r13)
larl %r14,.Lsysc_per
stg %r14,__LC_RETURN_PSW+8(%r13)
- lghi %r14,1
+ lghi %r14,SYSCALL_FLAG_PER_TRAP
LBEAR __LC_PGM_LAST_BREAK(%r13)
LPSWEY __LC_RETURN_PSW,__LC_RETURN_LPSWE # branch to .Lsysc_per
SYM_CODE_END(pgm_check_handler)
diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h
index fb67b4abe68c..dc84ab497251 100644
--- a/arch/s390/kernel/entry.h
+++ b/arch/s390/kernel/entry.h
@@ -2,6 +2,11 @@
#ifndef _ENTRY_H
#define _ENTRY_H
+#define PGM_FLAG_GUEST_FAULT 1
+#define SYSCALL_FLAG_PER_TRAP 1
+
+#ifndef __ASSEMBLER__
+
#include <linux/percpu.h>
#include <linux/types.h>
#include <linux/signal.h>
@@ -21,8 +26,8 @@ void early_pgm_check_handler(void);
struct task_struct *__switch_to_asm(struct task_struct *prev, struct task_struct *next);
void __ret_from_fork(struct task_struct *prev, struct pt_regs *regs);
-void __do_pgm_check(struct pt_regs *regs);
-void __do_syscall(struct pt_regs *regs, int per_trap);
+void __do_pgm_check(struct pt_regs *regs, unsigned long flags);
+void __do_syscall(struct pt_regs *regs, unsigned long flags);
void __do_early_pgm_check(struct pt_regs *regs);
void do_protection_exception(struct pt_regs *regs);
@@ -70,4 +75,5 @@ extern struct exception_table_entry _stop_amode31_ex_table[];
#define __amode31_ref __section(".amode31.refs")
extern long _start_amode31_refs[], _end_amode31_refs[];
+#endif /* __ASSEMBLER__ */
#endif /* _ENTRY_H */
diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c
index 08f3520c6785..dd12efb58201 100644
--- a/arch/s390/kernel/idle.c
+++ b/arch/s390/kernel/idle.c
@@ -136,7 +136,6 @@ void noinstr arch_cpu_idle(void)
/* Wait for external, I/O or machine check interrupt. */
psw_mask = PSW_KERNEL_BITS | PSW_MASK_WAIT |
PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK;
- clear_cpu_flag(CIF_NOHZ_DELAY);
set_cpu_flag(CIF_ENABLED_WAIT);
if (smp_cpu_mtid)
stcctm(MT_DIAG, smp_cpu_mtid, (u64 *)&idle->mt_cycles_enter);
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
index 3c346b02ceb9..b1e798f8e1dd 100644
--- a/arch/s390/kernel/ipl.c
+++ b/arch/s390/kernel/ipl.c
@@ -1157,6 +1157,8 @@ static struct attribute_group reipl_nss_attr_group = {
void set_os_info_reipl_block(void)
{
+ if (!reipl_block_actual)
+ return;
os_info_entry_add_data(OS_INFO_REIPL_BLOCK, reipl_block_actual,
reipl_block_actual->hdr.len);
}
@@ -1927,7 +1929,8 @@ static struct shutdown_action __refdata dump_action = {
static void dump_reipl_run(struct shutdown_trigger *trigger)
{
struct lowcore *abs_lc;
- unsigned int csum;
+ unsigned long ipib = 0;
+ unsigned int csum = 0;
/*
* Set REIPL_CLEAR flag in os_info flags entry indicating
@@ -1943,9 +1946,12 @@ static void dump_reipl_run(struct shutdown_trigger *trigger)
reipl_type == IPL_TYPE_UNKNOWN)
os_info_flags |= OS_INFO_FLAG_REIPL_CLEAR;
os_info_entry_add_data(OS_INFO_FLAGS_ENTRY, &os_info_flags, sizeof(os_info_flags));
- csum = (__force unsigned int)cksm(reipl_block_actual, reipl_block_actual->hdr.len, 0);
+ if (reipl_block_actual) {
+ ipib = __pa(reipl_block_actual);
+ csum = (__force unsigned int)cksm(reipl_block_actual, reipl_block_actual->hdr.len, 0);
+ }
abs_lc = get_abs_lowcore();
- abs_lc->ipib = __pa(reipl_block_actual);
+ abs_lc->ipib = ipib;
abs_lc->ipib_checksum = csum;
put_abs_lowcore(abs_lc);
dump_run(trigger);
@@ -2021,8 +2027,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 +2045,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 +2072,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 +2087,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 +2115,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 +2142,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 +2180,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 +2208,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 +2266,7 @@ static void __init shutdown_actions_init(void)
{
int i;
- for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) {
+ for (i = 0; i < ARRAY_SIZE(shutdown_actions_list); i++) {
if (!shutdown_actions_list[i]->init)
continue;
shutdown_actions_list[i]->init_rc =
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c
index 04d528639b94..c923496aa7b4 100644
--- a/arch/s390/kernel/irq.c
+++ b/arch/s390/kernel/irq.c
@@ -166,7 +166,6 @@ void noinstr do_io_irq(struct pt_regs *regs)
if (from_idle)
account_idle_time_irq();
- set_cpu_flag(CIF_NOHZ_DELAY);
do {
regs->tpi_info = get_lowcore()->tpi_info;
if (get_lowcore()->tpi_info.adapter_IO)
@@ -369,9 +368,6 @@ static irqreturn_t do_ext_interrupt(int irq, void *dummy)
int index;
ext_code.int_code = regs->int_code;
- if (ext_code.code != EXT_IRQ_CLK_COMP)
- set_cpu_flag(CIF_NOHZ_DELAY);
-
index = ext_hash(ext_code.code);
rcu_read_lock();
hlist_for_each_entry_rcu(p, &ext_int_hash[index], entry) {
diff --git a/arch/s390/kernel/irqflags.c b/arch/s390/kernel/irqflags.c
new file mode 100644
index 000000000000..e192f59f8918
--- /dev/null
+++ b/arch/s390/kernel/irqflags.c
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/export.h>
+#include <asm/irqflags.h>
+
+noinstr unsigned long arch_local_save_flags(void)
+{
+ return __arch_local_save_flags();
+}
+EXPORT_SYMBOL(arch_local_save_flags);
+
+noinstr unsigned long arch_local_irq_save(void)
+{
+ return __arch_local_irq_save();
+}
+EXPORT_SYMBOL(arch_local_irq_save);
+
+noinstr void arch_local_irq_enable_external(void)
+{
+ __arch_local_irq_enable_external();
+}
+EXPORT_SYMBOL(arch_local_irq_enable_external);
+
+noinstr void arch_local_irq_enable(void)
+{
+ __arch_local_irq_enable();
+}
+EXPORT_SYMBOL(arch_local_irq_enable);
diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S
index 1fec370fecf4..6bc44c767642 100644
--- a/arch/s390/kernel/mcount.S
+++ b/arch/s390/kernel/mcount.S
@@ -4,6 +4,7 @@
*
*/
+#include <linux/cfi_types.h>
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
#include <asm/ftrace.h>
@@ -34,10 +35,16 @@
.section .kprobes.text, "ax"
-SYM_FUNC_START(ftrace_stub)
+SYM_TYPED_FUNC_START(ftrace_stub)
BR_EX %r14
SYM_FUNC_END(ftrace_stub)
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+SYM_TYPED_FUNC_START(ftrace_stub_graph)
+ BR_EX %r14
+SYM_FUNC_END(ftrace_stub_graph)
+#endif
+
SYM_CODE_START(ftrace_stub_direct_tramp)
lgr %r1, %r0
BR_EX %r1
diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c
index e17a59d4d5a4..17297a8b63d9 100644
--- a/arch/s390/kernel/nmi.c
+++ b/arch/s390/kernel/nmi.c
@@ -344,8 +344,7 @@ static void notrace s390_backup_mcck_info(struct pt_regs *regs)
sie_page = container_of(sie_block, struct sie_page, sie_block);
mcck_backup = &sie_page->mcck_info;
- mcck_backup->mcic = get_lowcore()->mcck_interruption_code &
- ~(MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE);
+ mcck_backup->mcic = get_lowcore()->mcck_interruption_code & ~MCCK_CODE_NO_GUEST;
mcck_backup->ext_damage_code = get_lowcore()->external_damage_code;
mcck_backup->failing_storage_address = get_lowcore()->failing_storage_address;
}
@@ -357,8 +356,6 @@ NOKPROBE_SYMBOL(s390_backup_mcck_info);
#define ED_STP_ISLAND 6 /* External damage STP island check */
#define ED_STP_SYNC 7 /* External damage STP sync check */
-#define MCCK_CODE_NO_GUEST (MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE)
-
/*
* machine check handler.
*/
diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c
index 2076ac22e2c4..69fa303cd479 100644
--- a/arch/s390/kernel/perf_cpum_cf.c
+++ b/arch/s390/kernel/perf_cpum_cf.c
@@ -110,6 +110,7 @@ struct cpu_cf_ptr {
static struct cpu_cf_root { /* Anchor to per CPU data */
refcount_t refcnt; /* Overall active events */
+ unsigned int tskctx; /* Users tracking all CPUs (cpu == -1) */
struct cpu_cf_ptr __percpu *cfptr;
} cpu_cf_root;
@@ -118,13 +119,15 @@ static struct cpu_cf_root { /* Anchor to per CPU data */
* user space in task context with perf_event_open() and close()
* system calls.
*
- * This mutex serializes functions cpum_cf_alloc_cpu() called at event
- * initialization via cpumf_pmu_event_init() and function cpum_cf_free_cpu()
- * called at event removal via call back function hw_perf_event_destroy()
- * when the event is deleted. They are serialized to enforce correct
- * bookkeeping of pointer and reference counts anchored by
- * struct cpu_cf_root and the access to cpu_cf_root::refcnt and the
- * per CPU pointers stored in cpu_cf_root::cfptr.
+ * This mutex serializes the allocation and removal of the per CPU counter
+ * data via cpum_cf_alloc_cpu() and cpum_cf_free_cpu(). They are called with
+ * this mutex held at event initialization via cpumf_pmu_event_init(), at
+ * event removal via call back function hw_perf_event_destroy() when the
+ * event is deleted, and from the CPU hotplug prepare/dead callbacks. The
+ * mutex enforces correct bookkeeping of pointer and reference counts
+ * anchored by struct cpu_cf_root and protects the access to
+ * cpu_cf_root::refcnt, cpu_cf_root::tskctx and the per CPU pointers
+ * stored in cpu_cf_root::cfptr.
*/
static DEFINE_MUTEX(pmc_reserve_mutex);
@@ -167,12 +170,14 @@ static void cpum_cf_reset_cpu(void *flags)
}
/* Free per CPU data when the last event is removed. */
-static void cpum_cf_free_root(void)
+static void cpum_cf_free_root(unsigned int num)
{
- if (!refcount_dec_and_test(&cpu_cf_root.refcnt))
+ struct cpu_cf_ptr __percpu *p = cpu_cf_root.cfptr;
+
+ if (!refcount_sub_and_test(num, &cpu_cf_root.refcnt))
return;
- free_percpu(cpu_cf_root.cfptr);
cpu_cf_root.cfptr = NULL;
+ free_percpu(p);
irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT);
on_each_cpu(cpum_cf_reset_cpu, NULL, 1);
debug_sprintf_event(cf_dbg, 4, "%s root.refcnt %u cfptr %d\n",
@@ -186,17 +191,17 @@ static void cpum_cf_free_root(void)
* CPUs possible, which might be larger than the number of CPUs currently
* online.
*/
-static int cpum_cf_alloc_root(void)
+static int cpum_cf_alloc_root(unsigned int num)
{
int rc = 0;
- if (refcount_inc_not_zero(&cpu_cf_root.refcnt))
+ if (refcount_add_not_zero(num, &cpu_cf_root.refcnt))
return rc;
/* The memory is already zeroed. */
cpu_cf_root.cfptr = alloc_percpu(struct cpu_cf_ptr);
if (cpu_cf_root.cfptr) {
- refcount_set(&cpu_cf_root.refcnt, 1);
+ refcount_set(&cpu_cf_root.refcnt, num);
on_each_cpu(cpum_cf_reset_cpu, NULL, 1);
irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT);
} else {
@@ -206,20 +211,23 @@ static int cpum_cf_alloc_root(void)
return rc;
}
-/* Free CPU counter data structure for a PMU */
-static void cpum_cf_free_cpu(int cpu)
+/*
+ * Remove num references to the CPU counter data structure of a PMU.
+ * Called with pmc_reserve_mutex held.
+ */
+static void cpum_cf_free_cpu(int cpu, unsigned int num)
{
struct cpu_cf_events *cpuhw;
struct cpu_cf_ptr *p;
- mutex_lock(&pmc_reserve_mutex);
+ lockdep_assert_held(&pmc_reserve_mutex);
/*
* When invoked via CPU hotplug handler, there might be no events
* installed or that particular CPU might not have an
* event installed. This anchor pointer can be NULL!
*/
if (!cpu_cf_root.cfptr)
- goto out;
+ return;
p = per_cpu_ptr(cpu_cf_root.cfptr, cpu);
cpuhw = p->cpucf;
/*
@@ -227,28 +235,29 @@ static void cpum_cf_free_cpu(int cpu)
* installed on that CPU, but on different CPUs.
*/
if (!cpuhw)
- goto out;
+ return;
- if (refcount_dec_and_test(&cpuhw->refcnt)) {
- kfree(cpuhw);
+ if (refcount_sub_and_test(num, &cpuhw->refcnt)) {
p->cpucf = NULL;
+ kfree(cpuhw);
}
- cpum_cf_free_root();
-out:
- mutex_unlock(&pmc_reserve_mutex);
+ cpum_cf_free_root(num);
}
-/* Allocate CPU counter data structure for a PMU. Called under mutex lock. */
-static int cpum_cf_alloc_cpu(int cpu)
+/*
+ * Add num references to the CPU counter data structure of a PMU and
+ * allocate it when necessary. Called with pmc_reserve_mutex held.
+ */
+static int cpum_cf_alloc_cpu(int cpu, unsigned int num)
{
struct cpu_cf_events *cpuhw;
struct cpu_cf_ptr *p;
int rc;
- mutex_lock(&pmc_reserve_mutex);
- rc = cpum_cf_alloc_root();
+ lockdep_assert_held(&pmc_reserve_mutex);
+ rc = cpum_cf_alloc_root(num);
if (rc)
- goto unlock;
+ return rc;
p = per_cpu_ptr(cpu_cf_root.cfptr, cpu);
cpuhw = p->cpucf;
@@ -256,12 +265,12 @@ static int cpum_cf_alloc_cpu(int cpu)
cpuhw = kzalloc_obj(*cpuhw);
if (cpuhw) {
p->cpucf = cpuhw;
- refcount_set(&cpuhw->refcnt, 1);
+ refcount_set(&cpuhw->refcnt, num);
} else {
rc = -ENOMEM;
}
} else {
- refcount_inc(&cpuhw->refcnt);
+ refcount_add(num, &cpuhw->refcnt);
}
if (rc) {
/*
@@ -269,10 +278,8 @@ static int cpum_cf_alloc_cpu(int cpu)
* cpu_cf_event in not created, its destroy() function is not
* invoked. Adjust the reference counter for the anchor.
*/
- cpum_cf_free_root();
+ cpum_cf_free_root(num);
}
-unlock:
- mutex_unlock(&pmc_reserve_mutex);
return rc;
}
@@ -284,39 +291,70 @@ unlock:
* perf_event_open() with task context and /dev/hwctr interface.
* If cpu is non-zero install event on this CPU only. This setup handles
* perf_event_open() with CPU context.
+ * Users with cpu == -1 are counted in cpu_cf_root::tskctx. The CPU hotplug
+ * prepare and dead callbacks use this count to install and remove the per
+ * CPU counter data on a new or dying CPU.
*/
-static int cpum_cf_alloc(int cpu)
+static int cpum_cf_alloc_cpuslocked(int cpu)
{
cpumask_var_t mask;
int rc;
+ lockdep_assert_cpus_held();
if (cpu == -1) {
if (!zalloc_cpumask_var(&mask, GFP_KERNEL))
return -ENOMEM;
+ mutex_lock(&pmc_reserve_mutex);
for_each_online_cpu(cpu) {
- rc = cpum_cf_alloc_cpu(cpu);
+ rc = cpum_cf_alloc_cpu(cpu, 1);
if (rc) {
for_each_cpu(cpu, mask)
- cpum_cf_free_cpu(cpu);
+ cpum_cf_free_cpu(cpu, 1);
break;
}
cpumask_set_cpu(cpu, mask);
}
+ if (!rc)
+ cpu_cf_root.tskctx++;
+ mutex_unlock(&pmc_reserve_mutex);
free_cpumask_var(mask);
} else {
- rc = cpum_cf_alloc_cpu(cpu);
+ mutex_lock(&pmc_reserve_mutex);
+ rc = cpum_cf_alloc_cpu(cpu, 1);
+ mutex_unlock(&pmc_reserve_mutex);
}
return rc;
}
-static void cpum_cf_free(int cpu)
+static int cpum_cf_alloc(int cpu)
+{
+ int rc;
+
+ cpus_read_lock();
+ rc = cpum_cf_alloc_cpuslocked(cpu);
+ cpus_read_unlock();
+ return rc;
+}
+
+static void cpum_cf_free_cpuslocked(int cpu)
{
+ lockdep_assert_cpus_held();
+ mutex_lock(&pmc_reserve_mutex);
if (cpu == -1) {
+ cpu_cf_root.tskctx--;
for_each_online_cpu(cpu)
- cpum_cf_free_cpu(cpu);
+ cpum_cf_free_cpu(cpu, 1);
} else {
- cpum_cf_free_cpu(cpu);
+ cpum_cf_free_cpu(cpu, 1);
}
+ mutex_unlock(&pmc_reserve_mutex);
+}
+
+static void cpum_cf_free(int cpu)
+{
+ cpus_read_lock();
+ cpum_cf_free_cpuslocked(cpu);
+ cpus_read_unlock();
}
#define CF_DIAG_CTRSET_DEF 0xfeef /* Counter set header mark */
@@ -1090,53 +1128,67 @@ static refcount_t cfset_opencnt = REFCOUNT_INIT(0); /* Access count */
static DEFINE_MUTEX(cfset_ctrset_mutex);
/*
- * CPU hotplug handles only /dev/hwctr device.
- * For perf_event_open() the CPU hotplug handling is done on kernel common
- * code:
+ * CPU hotplug handling:
+ *
+ * cpum_cf_prepare_cpu() and cpum_cf_dead_cpu() run while the new or dying
+ * CPU is offline. They create and remove the per CPU counter data for all
+ * users tracking every CPU (cpu == -1), that is perf_event_open() events
+ * with task context and /dev/hwctr device sessions. Each such user holds
+ * one reference to the per CPU counter data of each CPU. Therefore install
+ * and remove one reference per user, tracked in cpu_cf_root::tskctx. This
+ * guarantees the per CPU counter data exists before the new CPU executes
+ * its first task and is removed only after the dying CPU is gone.
+ *
+ * cpum_cf_online_cpu() and cpum_cf_offline_cpu() run while the new or
+ * dying CPU is online. They handle only the counter set state of open
+ * /dev/hwctr device sessions on that CPU. For perf_event_open() events
+ * nothing is done:
* - CPU add: Nothing is done since a file descriptor can not be created
* and returned to the user.
* - CPU delete: Handled by common code via pmu_disable(), pmu_stop() and
- * pmu_delete(). The event itself is removed when the file descriptor is
- * closed.
+ * pmu_delete(). During task exit processing of grouped perf events
+ * triggered by CPU hotplug processing, pmu_disable() is called as part
+ * of perf context removal process. The event itself is removed when the
+ * event file descriptor is closed.
*/
+static int cpum_cf_prepare_cpu(unsigned int cpu)
+{
+ int rc = 0;
+
+ mutex_lock(&pmc_reserve_mutex);
+ if (cpu_cf_root.tskctx)
+ rc = cpum_cf_alloc_cpu(cpu, cpu_cf_root.tskctx);
+ mutex_unlock(&pmc_reserve_mutex);
+ return rc;
+}
+
+static int cpum_cf_dead_cpu(unsigned int cpu)
+{
+ mutex_lock(&pmc_reserve_mutex);
+ if (cpu_cf_root.tskctx)
+ cpum_cf_free_cpu(cpu, cpu_cf_root.tskctx);
+ mutex_unlock(&pmc_reserve_mutex);
+ return 0;
+}
+
static int cfset_online_cpu(unsigned int cpu);
static int cpum_cf_online_cpu(unsigned int cpu)
{
- int rc = 0;
-
- /*
- * Ignore notification for perf_event_open().
- * Handle only /dev/hwctr device sessions.
- */
mutex_lock(&cfset_ctrset_mutex);
- if (refcount_read(&cfset_opencnt)) {
- rc = cpum_cf_alloc_cpu(cpu);
- if (!rc)
- cfset_online_cpu(cpu);
- }
+ if (refcount_read(&cfset_opencnt))
+ cfset_online_cpu(cpu);
mutex_unlock(&cfset_ctrset_mutex);
- return rc;
+ return 0;
}
static int cfset_offline_cpu(unsigned int cpu);
static int cpum_cf_offline_cpu(unsigned int cpu)
{
- /*
- * During task exit processing of grouped perf events triggered by CPU
- * hotplug processing, pmu_disable() is called as part of perf context
- * removal process. Therefore do not trigger event removal now for
- * perf_event_open() created events. Perf common code triggers event
- * destruction when the event file descriptor is closed.
- *
- * Handle only /dev/hwctr device sessions.
- */
mutex_lock(&cfset_ctrset_mutex);
- if (refcount_read(&cfset_opencnt)) {
+ if (refcount_read(&cfset_opencnt))
cfset_offline_cpu(cpu);
- cpum_cf_free_cpu(cpu);
- }
mutex_unlock(&cfset_ctrset_mutex);
return 0;
}
@@ -1183,7 +1235,7 @@ static void cpumf_measurement_alert(struct ext_code ext_code,
static int cfset_init(void);
static int __init cpumf_pmu_init(void)
{
- int rc;
+ int state, rc;
/* Extract counter measurement facility information */
if (!cpum_cf_avail() || qctri(&cpumf_ctr_info))
@@ -1225,11 +1277,24 @@ static int __init cpumf_pmu_init(void)
cfset_init();
}
+ rc = cpuhp_setup_state(CPUHP_BP_PREPARE_DYN,
+ "perf/s390/cf:prepare",
+ cpum_cf_prepare_cpu, cpum_cf_dead_cpu);
+ if (rc < 0)
+ goto out3;
+ state = rc;
+
rc = cpuhp_setup_state(CPUHP_AP_PERF_S390_CF_ONLINE,
"perf/s390/cf:online",
cpum_cf_online_cpu, cpum_cf_offline_cpu);
- return rc;
+ if (rc < 0)
+ goto out4;
+ return 0;
+out4:
+ cpuhp_remove_state(state);
+out3:
+ perf_pmu_unregister(&cpumf_pmu);
out2:
debug_unregister_view(cf_dbg, &debug_sprintf_view);
debug_unregister(cf_dbg);
@@ -1385,6 +1450,7 @@ static void cfset_all_stop(struct cfset_request *req)
*/
static int cfset_release(struct inode *inode, struct file *file)
{
+ cpus_read_lock();
mutex_lock(&cfset_ctrset_mutex);
/* Open followed by close/exit has no private_data */
if (file->private_data) {
@@ -1395,9 +1461,10 @@ static int cfset_release(struct inode *inode, struct file *file)
}
if (refcount_dec_and_test(&cfset_opencnt)) { /* Last close */
on_each_cpu(cfset_release_cpu, NULL, 1);
- cpum_cf_free(-1);
+ cpum_cf_free_cpuslocked(-1);
}
mutex_unlock(&cfset_ctrset_mutex);
+ cpus_read_unlock();
return 0;
}
@@ -1416,15 +1483,17 @@ static int cfset_open(struct inode *inode, struct file *file)
return -EPERM;
file->private_data = NULL;
+ cpus_read_lock();
mutex_lock(&cfset_ctrset_mutex);
if (!refcount_inc_not_zero(&cfset_opencnt)) { /* First open */
- rc = cpum_cf_alloc(-1);
+ rc = cpum_cf_alloc_cpuslocked(-1);
if (!rc) {
cfset_session_init();
refcount_set(&cfset_opencnt, 1);
}
}
mutex_unlock(&cfset_ctrset_mutex);
+ cpus_read_unlock();
/* nonseekable_open() never fails */
return rc ?: nonseekable_open(inode, file);
@@ -1496,7 +1565,6 @@ static int cfset_all_copy(unsigned long arg, cpumask_t *mask)
goto out;
}
uptr += sizeof(struct s390_ctrset_cpudata) + cpuhw->used;
- cond_resched();
}
cpus = cpumask_weight(mask);
if (put_user(cpus, &ctrset_read->no_cpus))
diff --git a/arch/s390/kernel/perf_pai.c b/arch/s390/kernel/perf_pai.c
index cdb8006220ca..013c3dae21ec 100644
--- a/arch/s390/kernel/perf_pai.c
+++ b/arch/s390/kernel/perf_pai.c
@@ -67,6 +67,7 @@ struct pai_mapptr {
static struct pai_root { /* Anchor to per CPU data */
refcount_t refcnt; /* Overall active events */
+ atomic_t tskctx; /* Overall per-task events */
struct pai_mapptr __percpu *mapptr;
} pai_root[PAI_PMU_MAX];
@@ -93,14 +94,15 @@ struct pai_pmu { /* Define PAI PMU characteristics */
static struct pai_pmu pai_pmu[]; /* Forward declaration */
/* Free per CPU data when the last event is removed. */
-static void pai_root_free(int idx)
+static void pai_root_free(int idx, int tasks)
{
- if (refcount_dec_and_test(&pai_root[idx].refcnt)) {
+ if (refcount_sub_and_test(tasks, &pai_root[idx].refcnt)) {
free_percpu(pai_root[idx].mapptr);
pai_root[idx].mapptr = NULL;
}
- debug_sprintf_event(paidbg, 5, "%s root[%d].refcount %d\n", __func__,
- idx, refcount_read(&pai_root[idx].refcnt));
+ debug_sprintf_event(paidbg, 5, "%s root[%d].refcount %d tskctx %d\n",
+ __func__, idx, refcount_read(&pai_root[idx].refcnt),
+ atomic_read(&pai_root[idx].tskctx));
}
/*
@@ -137,40 +139,54 @@ static void pai_free(struct pai_mapptr *mp)
mp->mapptr = NULL;
}
-/* Adjust usage counters and remove allocated memory when all users are
- * gone.
- */
-static void pai_event_destroy_cpu(struct perf_event *event, int cpu)
+/* Called under mutex_lock */
+static void pai_event_destroy_cpu(int idx, int cpu, bool hotplug)
{
- int idx = PAI_PMU_IDX(event);
- struct pai_mapptr *mp = per_cpu_ptr(pai_root[idx].mapptr, cpu);
- struct pai_map *cpump = mp->mapptr;
+ struct pai_mapptr *mp;
+ struct pai_map *cpump;
+ int tasks = 1;
- mutex_lock(&pai_reserve_mutex);
- debug_sprintf_event(paidbg, 5, "%s event %#llx idx %d cpu %d users %d "
- "refcnt %u\n", __func__, event->attr.config, idx,
- event->cpu, cpump->active_events,
- refcount_read(&cpump->refcnt));
- if (refcount_dec_and_test(&cpump->refcnt))
+ /* Check reference count and return when all gone.
+ * 1. An event is installed on online CPU X.
+ * 2. CPU x is offlined and the per-CPU data is removed.
+ * 3. Event is destroyed via close system call.
+ */
+ if (!refcount_read(&pai_root[idx].refcnt))
+ return; /* No events at all */
+ mp = per_cpu_ptr(pai_root[idx].mapptr, cpu);
+ if (!mp || !mp->mapptr) /* No events on that CPU */
+ return;
+
+ /* When hotplug is true, invocation is from CPU hotplug callback.
+ * Delete per-CPU resource and adjust refcnt when per-task events
+ * are currently active. This can be more than one.
+ * In this case adjust counters.
+ */
+ if (hotplug)
+ tasks = atomic_read(&pai_root[idx].tskctx);
+
+ cpump = mp->mapptr;
+ if (refcount_sub_and_test(tasks, &cpump->refcnt))
pai_free(mp);
- pai_root_free(idx);
- mutex_unlock(&pai_reserve_mutex);
+ pai_root_free(idx, tasks);
}
static void pai_event_destroy(struct perf_event *event)
{
- int cpu;
+ int cpu = 0, idx = PAI_PMU_IDX(event);
free_page(PAI_SAVE_AREA(event));
+ cpus_read_lock();
+ mutex_lock(&pai_reserve_mutex);
if (event->cpu == -1) {
- struct cpumask *mask = PAI_CPU_MASK(event);
-
- for_each_cpu(cpu, mask)
- pai_event_destroy_cpu(event, cpu);
- kfree(mask);
+ atomic_dec(&pai_root[idx].tskctx);
+ for_each_online_cpu(cpu)
+ pai_event_destroy_cpu(idx, cpu, false);
} else {
- pai_event_destroy_cpu(event, event->cpu);
+ pai_event_destroy_cpu(idx, event->cpu, false);
}
+ mutex_unlock(&pai_reserve_mutex);
+ cpus_read_unlock();
}
static void paicrypt_event_destroy(struct perf_event *event)
@@ -234,25 +250,30 @@ static u64 paicrypt_getall(struct perf_event *event)
return sum;
}
-/* Check concurrent access of counting and sampling for crypto events.
- * This function is called in process context and it is save to block.
- * When the event initialization functions fails, no other call back will
- * be invoked.
- *
- * Allocate the memory for the event.
- */
-static int pai_alloc_cpu(struct perf_event *event, int cpu)
+/* Called under mutex_lock */
+static int pai_alloc_cpu(int idx, int cpu, bool hotplug)
{
- int rc, idx = PAI_PMU_IDX(event);
struct pai_map *cpump = NULL;
bool need_paiext_cb = false;
struct pai_mapptr *mp;
+ int tasks = 1, rc = 0;
+
+ /* When hotplug is true, invocation is from CPU hotplug callback.
+ * Allocate per-CPU resource when per-task events are currently active.
+ * This can be more than one. In this case adjust all reference
+ * counters. Otherwise return, this ensures memory is only allocated
+ * when needed.
+ */
+ if (hotplug) {
+ tasks = atomic_read(&pai_root[idx].tskctx);
+ if (!tasks)
+ goto out;
+ }
- mutex_lock(&pai_reserve_mutex);
/* Allocate root node */
rc = pai_root_alloc(idx);
if (rc)
- goto unlock;
+ goto out;
/* Allocate node for this event */
mp = per_cpu_ptr(pai_root[idx].mapptr, cpu);
@@ -296,28 +317,45 @@ static int pai_alloc_cpu(struct perf_event *event, int cpu)
goto undo;
}
INIT_LIST_HEAD(&cpump->syswide_list);
- refcount_set(&cpump->refcnt, 1);
+ refcount_set(&cpump->refcnt, tasks);
rc = 0;
} else {
- refcount_inc(&cpump->refcnt);
+ refcount_add(tasks, &cpump->refcnt);
}
+ /* If tasks is greater than 1, we are called from CPU hotplug path
+ * and need to adjust the pai_root[idx].refcnt by the number of
+ * per-process events. Function pai_root_alloc(idx) already
+ * incremented by one. Adjust for the rest.
+ */
+ if (tasks > 1)
+ refcount_add(tasks - 1, &pai_root[idx].refcnt);
undo:
if (rc) {
/* Error in allocation of event, decrement anchor. Since
* the event in not created, its destroy() function is never
* invoked. Adjust the reference counter for the anchor.
+ * The failure happened in the case of variable
+ * cpump == NULL branch above. The pai_root[XXX].refcnt has
+ * been incremented by one. Then the per-CPU allocation
+ * failed, so decrement it by one, regardless of tasks.
*/
- pai_root_free(idx);
+ pai_root_free(idx, 1);
}
-unlock:
- mutex_unlock(&pai_reserve_mutex);
+out:
/* If rc is non-zero, no increment of counter/sampler was done. */
return rc;
}
+/* Check concurrent access of counting and sampling for PAI events.
+ * This function is called in process context and it is safe to block.
+ * When the event initialization functions fails, no other call back will
+ * be invoked.
+ * Called under mutex_lock.
+ */
static int pai_alloc(struct perf_event *event)
{
+ int idx = PAI_PMU_IDX(event);
struct cpumask *maskptr;
int cpu, rc = -ENOMEM;
@@ -326,24 +364,20 @@ static int pai_alloc(struct perf_event *event)
goto out;
for_each_online_cpu(cpu) {
- rc = pai_alloc_cpu(event, cpu);
+ rc = pai_alloc_cpu(idx, cpu, false);
if (rc) {
for_each_cpu(cpu, maskptr)
- pai_event_destroy_cpu(event, cpu);
- kfree(maskptr);
- goto out;
+ pai_event_destroy_cpu(idx, cpu, false);
+ goto undo;
}
cpumask_set_cpu(cpu, maskptr);
}
- /*
- * On error all cpumask are freed and all events have been destroyed.
- * Save of which CPUs data structures have been allocated for.
- * Release them in pai_event_destroy call back function
- * for this event.
- */
- PAI_CPU_MASK(event) = maskptr;
rc = 0;
+ /* Trace per-task events for CPU hotplug. */
+ atomic_inc(&pai_root[idx].tskctx);
+undo:
+ kfree(maskptr);
out:
return rc;
}
@@ -391,10 +425,14 @@ static int pai_event_init(struct perf_event *event, int idx)
}
}
+ cpus_read_lock();
+ mutex_lock(&pai_reserve_mutex);
if (event->cpu >= 0)
- rc = pai_alloc_cpu(event, event->cpu);
+ rc = pai_alloc_cpu(idx, event->cpu, false);
else
rc = pai_alloc(event);
+ mutex_unlock(&pai_reserve_mutex);
+ cpus_read_unlock();
if (rc) {
free_page(PAI_SAVE_AREA(event));
goto out;
@@ -464,6 +502,7 @@ static void pai_start(struct perf_event *event, int flags,
cpump->event = event;
}
}
+ event->hw.state &= ~PERF_HES_STOPPED;
}
static void paicrypt_start(struct perf_event *event, int flags)
@@ -510,6 +549,13 @@ static void pai_stop(struct perf_event *event, int flags)
struct pai_mapptr *mp = this_cpu_ptr(pai_root[idx].mapptr);
struct pai_map *cpump = mp->mapptr;
+ /* Cope with multiple invocations:
+ * 1. perf_event_throttle() --> PMU->stop()
+ * 2. task schedules out --> PMU->stop()
+ * Check for event already stopped.
+ */
+ if (event->hw.state & PERF_HES_STOPPED)
+ return;
if (!event->attr.sample_period) { /* Counting */
pai_pmu[idx].pmu->read(event);
} else { /* Sampling */
@@ -672,9 +718,9 @@ static void pai_have_samples(int idx)
{
struct pai_mapptr *mp = this_cpu_ptr(pai_root[idx].mapptr);
struct pai_map *cpump = mp->mapptr;
- struct perf_event *event;
+ struct perf_event *event, *e2;
- list_for_each_entry(event, &cpump->syswide_list, hw.tp_list)
+ list_for_each_entry_safe(event, e2, &cpump->syswide_list, hw.tp_list)
pai_have_sample(event, cpump);
}
@@ -691,6 +737,17 @@ static void paicrypt_sched_task(struct perf_event_pmu_context *pmu_ctx,
pai_have_samples(PAI_PMU_CRYPTO);
}
+/* Prevent ioctl(fd, PERF_EVENT_IOC_PERIOD, ...) call.
+ * It sets perf_event::event_limit to a positive value and causes
+ * perf_event_overflow() to invoke pai_stop() call back function when
+ * perf_event::event_limit hits zero. This is not supported because the
+ * sample events CRYPTO_ALL and NNPA_ALL are always taken at schedule out
+ * of a task.
+ */
+static int pai_check_period(struct perf_event *event, u64 value)
+{
+ return -EINVAL;
+}
/* ============================= paiext ====================================*/
static void paiext_event_destroy(struct perf_event *event)
@@ -804,6 +861,7 @@ static struct pmu paicrypt = {
.stop = paicrypt_stop,
.read = paicrypt_read,
.sched_task = paicrypt_sched_task,
+ .check_period = pai_check_period,
.attr_groups = paicrypt_attr_groups
};
@@ -1015,6 +1073,7 @@ static struct pmu paiext = {
.stop = paiext_stop,
.read = paiext_read,
.sched_task = paiext_sched_task,
+ .check_period = pai_check_period,
.attr_groups = paiext_attr_groups,
};
@@ -1218,23 +1277,61 @@ static int __init paipmu_setup(void)
return install_ok;
}
+static int pai_online_cpu(unsigned int cpu)
+{
+ int rc;
+
+ mutex_lock(&pai_reserve_mutex);
+ rc = pai_alloc_cpu(PAI_PMU_CRYPTO, cpu, true);
+ if (rc)
+ goto out;
+ rc = pai_alloc_cpu(PAI_PMU_EXT, cpu, true);
+ if (rc)
+ pai_event_destroy_cpu(PAI_PMU_CRYPTO, cpu, true);
+out:
+ mutex_unlock(&pai_reserve_mutex);
+ return rc;
+}
+
+static int pai_offline_cpu(unsigned int cpu)
+{
+ mutex_lock(&pai_reserve_mutex);
+ pai_event_destroy_cpu(PAI_PMU_CRYPTO, cpu, true);
+ pai_event_destroy_cpu(PAI_PMU_EXT, cpu, true);
+ mutex_unlock(&pai_reserve_mutex);
+ return 0;
+}
+
static int __init pai_init(void)
{
+ int state, rc;
+
/* Setup s390dbf facility */
- paidbg = debug_register("pai", 32, 256, 128);
+ paidbg = debug_register("pai", 1, 1, 128);
if (!paidbg) {
pr_err("Registration of s390dbf pai failed\n");
return -ENOMEM;
}
debug_register_view(paidbg, &debug_sprintf_view);
- if (!paipmu_setup()) {
- /* No PMU registration, no need for debug buffer */
- debug_unregister_view(paidbg, &debug_sprintf_view);
- debug_unregister(paidbg);
- return -ENODEV;
- }
+ /* CPUHP_BP_PREPARE_DYN --> before CPU is brought online */
+ state = cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "perf/pai:prepare",
+ pai_online_cpu, pai_offline_cpu);
+ rc = state < 0 ? state : 0;
+ if (rc < 0)
+ goto out_debug;
+
+ rc = -ENODEV;
+ if (!paipmu_setup())
+ goto out_cpuhp;
return 0;
+
+out_cpuhp:
+ cpuhp_remove_state(state);
+out_debug:
+ debug_unregister_view(paidbg, &debug_sprintf_view);
+ debug_unregister(paidbg);
+ return rc;
}
device_initcall(pai_init);
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index 0ba7f89b8161..32499cad86f0 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -659,23 +659,13 @@ int smp_cpu_get_polarization(int cpu)
return per_cpu(pcpu_devices, cpu).polarization;
}
-void smp_cpu_set_capacity(int cpu, unsigned long val)
-{
- per_cpu(pcpu_devices, cpu).capacity = val;
-}
-
-unsigned long smp_cpu_get_capacity(int cpu)
-{
- return per_cpu(pcpu_devices, cpu).capacity;
-}
-
void smp_set_core_capacity(int cpu, unsigned long val)
{
int i;
cpu = smp_get_base_cpu(cpu);
for (i = cpu; (i <= cpu + smp_cpu_mtid) && (i < nr_cpu_ids); i++)
- smp_cpu_set_capacity(i, val);
+ topology_set_cpu_scale(i, val);
}
int smp_cpu_get_cpu_address(int cpu)
@@ -727,7 +717,7 @@ static int smp_add_core(struct sclp_core_entry *core, cpumask_t *avail,
else
pcpu->state = CPU_STATE_STANDBY;
smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
- smp_cpu_set_capacity(cpu, CPU_CAPACITY_HIGH);
+ topology_set_cpu_scale(cpu, CPU_CAPACITY_HIGH);
set_cpu_present(cpu, true);
if (!early && arch_register_cpu(cpu))
set_cpu_present(cpu, false);
@@ -909,7 +899,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;
}
@@ -968,7 +957,7 @@ void __init smp_prepare_boot_cpu(void)
ipl_pcpu->state = CPU_STATE_CONFIGURED;
lc->pcpu = (unsigned long)ipl_pcpu;
smp_cpu_set_polarization(0, POLARIZATION_UNKNOWN);
- smp_cpu_set_capacity(0, CPU_CAPACITY_HIGH);
+ topology_set_cpu_scale(0, CPU_CAPACITY_HIGH);
}
void __init smp_setup_processor_id(void)
@@ -1039,6 +1028,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 +1044,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 +1082,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 +1096,8 @@ int arch_register_cpu(int cpu)
rc = topology_cpu_init(c);
if (rc)
goto out_topology;
+ if (pcpu->state != CPU_STATE_CONFIGURED)
+ set_cpu_enabled(cpu, false);
return 0;
out_topology:
diff --git a/arch/s390/kernel/syscall.c b/arch/s390/kernel/syscall.c
index 75d5a3cab14e..bcc0b76bd868 100644
--- a/arch/s390/kernel/syscall.c
+++ b/arch/s390/kernel/syscall.c
@@ -93,12 +93,13 @@ 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;
+
+ enter_from_user_mode_randomize_stack(regs);
- enter_from_user_mode(regs);
- add_random_kstack_offset();
regs->psw = get_lowcore()->svc_old_psw;
regs->int_code = get_lowcore()->svc_int_code;
update_timer_sys();
@@ -106,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);
@@ -121,7 +122,9 @@ void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
regs->psw.addr = current->restart_block.arch_data;
current->restart_block.arch_data = 1;
}
- nr = syscall_enter_from_user_mode_work(regs, nr);
+
+ permit = syscall_enter_from_user_mode_work(regs, &nr);
+
/*
* In the s390 ptrace ABI, both the syscall number and the return value
* use gpr2. However, userspace puts the syscall number either in the
@@ -129,7 +132,7 @@ void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
* work, the ptrace code sets PIF_SYSCALL_RET_SET, which is checked here
* and if set, the syscall will be skipped.
*/
- if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET)))
+ if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET) || !permit))
goto out;
regs->gprs[2] = -ENOSYS;
if (likely(nr < NR_syscalls)) {
diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl
index 09a7ef04d979..1b45e68a217b 100644
--- a/arch/s390/kernel/syscalls/syscall.tbl
+++ b/arch/s390/kernel/syscalls/syscall.tbl
@@ -398,3 +398,4 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common fchroot sys_fchroot
diff --git a/arch/s390/kernel/sysinfo.c b/arch/s390/kernel/sysinfo.c
index 33ca3e47a0e6..45b4f448fe3d 100644
--- a/arch/s390/kernel/sysinfo.c
+++ b/arch/s390/kernel/sysinfo.c
@@ -325,6 +325,7 @@ int unregister_service_level(struct service_level *slr)
EXPORT_SYMBOL(unregister_service_level);
static void *service_level_start(struct seq_file *m, loff_t *pos)
+__acquires_shared(service_level_sem)
{
down_read(&service_level_sem);
return seq_list_start(&service_level_list, *pos);
@@ -336,6 +337,7 @@ static void *service_level_next(struct seq_file *m, void *p, loff_t *pos)
}
static void service_level_stop(struct seq_file *m, void *p)
+__releases_shared(service_level_sem)
{
up_read(&service_level_sem);
}
diff --git a/arch/s390/kernel/text_amode31.S b/arch/s390/kernel/text_amode31.S
index 26f2981aa09e..f007d892d0c5 100644
--- a/arch/s390/kernel/text_amode31.S
+++ b/arch/s390/kernel/text_amode31.S
@@ -5,6 +5,7 @@
* Copyright IBM Corp. 2019
*/
+#include <linux/cfi_types.h>
#include <linux/linkage.h>
#include <asm/asm-extable.h>
#include <asm/errno.h>
@@ -26,7 +27,7 @@
/*
* int _diag14_amode31(unsigned long rx, unsigned long ry1, unsigned long subcode)
*/
-SYM_FUNC_START(_diag14_amode31)
+SYM_TYPED_FUNC_START(_diag14_amode31)
lgr %r1,%r2
lgr %r2,%r3
lgr %r3,%r4
@@ -46,7 +47,7 @@ SYM_FUNC_END(_diag14_amode31)
/*
* int _diag210_amode31(struct diag210 *addr)
*/
-SYM_FUNC_START(_diag210_amode31)
+SYM_TYPED_FUNC_START(_diag210_amode31)
lgr %r1,%r2
lhi %r2,-1
sam31
@@ -64,7 +65,7 @@ SYM_FUNC_END(_diag210_amode31)
/*
* int diag8c(struct diag8c *addr, struct ccw_dev_id *devno, size_t len)
*/
-SYM_FUNC_START(_diag8c_amode31)
+SYM_TYPED_FUNC_START(_diag8c_amode31)
llgf %r3,0(%r3)
sam31
diag %r2,%r4,0x8c
@@ -77,7 +78,7 @@ SYM_FUNC_END(_diag8c_amode31)
/*
* int _diag26c_amode31(void *req, void *resp, enum diag26c_sc subcode)
*/
-SYM_FUNC_START(_diag26c_amode31)
+SYM_TYPED_FUNC_START(_diag26c_amode31)
lghi %r5,-EOPNOTSUPP
sam31
diag %r2,%r4,0x26c
@@ -91,7 +92,7 @@ SYM_FUNC_END(_diag26c_amode31)
/*
* void _diag0c_amode31(unsigned long rx)
*/
-SYM_FUNC_START(_diag0c_amode31)
+SYM_TYPED_FUNC_START(_diag0c_amode31)
sam31
diag %r2,%r2,0x0c
sam64
@@ -103,7 +104,7 @@ SYM_FUNC_END(_diag0c_amode31)
*
* Calls diag 308 subcode 1 and continues execution
*/
-SYM_FUNC_START(_diag308_reset_amode31)
+SYM_TYPED_FUNC_START(_diag308_reset_amode31)
larl %r4,ctlregs # Save control registers
stctg %c0,%c15,0(%r4)
lg %r2,0(%r4) # Disable lowcore protection
diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c
index 1377c6f3f670..42fc0294f543 100644
--- a/arch/s390/kernel/topology.c
+++ b/arch/s390/kernel/topology.c
@@ -147,7 +147,7 @@ static void add_cpus_to_mask(struct topology_core *tl_core,
cpumask_set_cpu(cpu, &book->mask);
cpumask_set_cpu(cpu, &socket->mask);
smp_cpu_set_polarization(cpu, tl_core->pp);
- smp_cpu_set_capacity(cpu, CPU_CAPACITY_HIGH);
+ topology_set_cpu_scale(cpu, CPU_CAPACITY_HIGH);
}
}
}
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
index 564403496a7c..b6ba4465f59d 100644
--- a/arch/s390/kernel/traps.c
+++ b/arch/s390/kernel/traps.c
@@ -9,7 +9,9 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
+#include <linux/capability.h>
#include <linux/cpufeature.h>
+#include <linux/debugfs.h>
#include <linux/kprobes.h>
#include <linux/kdebug.h>
#include <linux/randomize_kstack.h>
@@ -33,6 +35,12 @@
#include <asm/fault.h>
#include "entry.h"
+struct pgm_stat {
+ unsigned int count[128];
+};
+
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct pgm_stat, pgm_stat);
+
static inline void __user *get_trap_ip(struct pt_regs *regs)
{
unsigned long address;
@@ -327,11 +335,12 @@ void __init trap_init(void)
static void (*pgm_check_table[128])(struct pt_regs *regs);
-void noinstr __do_pgm_check(struct pt_regs *regs)
+void noinstr __do_pgm_check(struct pt_regs *regs, unsigned long flags)
{
struct lowcore *lc = get_lowcore();
bool percpu_needs_fixup;
irqentry_state_t state;
+ struct pgm_stat *stat;
unsigned int trapnr;
union teid teid;
@@ -339,6 +348,10 @@ void noinstr __do_pgm_check(struct pt_regs *regs)
regs->int_code = lc->pgm_int_code;
regs->int_parm_long = teid.val;
regs->monitor_code = lc->monitor_code;
+
+ trapnr = regs->int_code & PGM_INT_CODE_MASK;
+ stat = this_cpu_ptr(&pgm_stat);
+ stat->count[trapnr]++;
/*
* In case of a guest fault, short-circuit the fault handler and return.
* This way the sie64a() function will return 0; fault address and
@@ -346,7 +359,7 @@ void noinstr __do_pgm_check(struct pt_regs *regs)
* the fault number in current->thread.gmap_int_code. KVM will be
* able to use this information to handle the fault.
*/
- if (test_pt_regs_flag(regs, PIF_GUEST_FAULT)) {
+ if (flags & PGM_FLAG_GUEST_FAULT) {
current->thread.gmap_teid.val = regs->int_parm_long;
current->thread.gmap_int_code = regs->int_code & 0xffff;
return;
@@ -383,7 +396,6 @@ void noinstr __do_pgm_check(struct pt_regs *regs)
if (!irqs_disabled_flags(regs->psw.mask))
trace_hardirqs_on();
__arch_local_irq_ssm(regs->psw.mask & ~PSW_MASK_PER);
- trapnr = regs->int_code & PGM_INT_CODE_MASK;
if (trapnr)
pgm_check_table[trapnr](regs);
out:
@@ -393,6 +405,33 @@ out:
percpu_exit(regs, percpu_needs_fixup);
}
+static int pgm_check_stat_show(struct seq_file *p, void *v)
+{
+ int i, cpu;
+
+ cpus_read_lock();
+ seq_puts(p, " ");
+ for_each_online_cpu(cpu)
+ seq_printf(p, "CPU%-8d", cpu);
+ seq_putc(p, '\n');
+ for (i = 0; i < 128; i++) {
+ seq_printf(p, "%02x: ", i);
+ for_each_online_cpu(cpu)
+ seq_printf(p, "%10u ", per_cpu(pgm_stat, cpu).count[i]);
+ seq_putc(p, '\n');
+ }
+ cpus_read_unlock();
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(pgm_check_stat);
+
+static int __init debugfs_pgm_check_init(void)
+{
+ debugfs_create_file("exceptions", 0400, arch_debugfs_dir, NULL, &pgm_check_stat_fops);
+ return 0;
+}
+late_initcall(debugfs_pgm_check_init);
+
/*
* The program check table contains exactly 128 (0x00-0x7f) entries. Each
* line defines the function to be called corresponding to the program check
diff --git a/arch/s390/kernel/uv.c b/arch/s390/kernel/uv.c
index a284f98d9716..dc14ebc0105b 100644
--- a/arch/s390/kernel/uv.c
+++ b/arch/s390/kernel/uv.c
@@ -16,6 +16,7 @@
#include <linux/swap.h>
#include <linux/pagewalk.h>
#include <linux/backing-dev.h>
+#include <linux/vmalloc.h>
#include <asm/facility.h>
#include <asm/sections.h>
#include <asm/uv.h>
@@ -209,6 +210,70 @@ int uv_convert_from_secure_pte(pte_t pte)
return uv_convert_from_secure_folio(pfn_folio(pte_pfn(pte)));
}
+static int uv_free_range_cb(pte_t *ptep, unsigned long addr, void *data)
+{
+ pte_t pte = ptep_get(ptep);
+
+ if (!pte_present(pte))
+ return 0;
+ /*
+ * Note: do not update the pte here, since there is no code which
+ * accesses the memory range, besides bugs. The invalidation of ptes
+ * and TLB flushing is deferred like for regular vfree() calls.
+ */
+ __free_page(pte_page(pte));
+ return 0;
+}
+
+void uv_free_stor_var(void *stor_var)
+{
+ unsigned long addr, size;
+ struct vm_struct *area;
+
+ if (!stor_var)
+ return;
+ area = find_vm_area(stor_var);
+ if (WARN_ON_ONCE(!area || !(area->flags & VM_SPARSE)))
+ return;
+ size = get_vm_area_size(area);
+ addr = (unsigned long)area->addr;
+ apply_to_existing_page_range(&init_mm, addr, size, uv_free_range_cb, NULL);
+ free_vm_area(area);
+}
+EXPORT_SYMBOL_FOR_MODULES(uv_free_stor_var, "kvm");
+
+static int uv_alloc_range_cb(pte_t *ptep, unsigned long addr, void *data)
+{
+ struct page *page;
+ pte_t pte;
+
+ page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
+ if (!page)
+ return -ENOMEM;
+ pte = __pte(page_to_phys(page) | pgprot_val(PAGE_KERNEL));
+ set_pte(ptep, pte);
+ return 0;
+}
+
+void *uv_alloc_stor_var(unsigned long size)
+{
+ struct vm_struct *area;
+ unsigned long addr;
+
+ size = PAGE_ALIGN(size);
+ area = get_vm_area(size, VM_SPARSE);
+ if (!area)
+ return NULL;
+ addr = (unsigned long)area->addr;
+ if (apply_to_page_range(&init_mm, addr, size, uv_alloc_range_cb, NULL))
+ goto out;
+ return area->addr;
+out:
+ uv_free_stor_var(area->addr);
+ return NULL;
+}
+EXPORT_SYMBOL_FOR_MODULES(uv_alloc_stor_var, "kvm");
+
/*
* Calculate the expected ref_count for a folio that would otherwise have no
* further pins. This was cribbed from similar functions in other places in
@@ -831,7 +896,7 @@ int uv_retrieve_secret(u16 secret_idx, u8 *buf, size_t buf_size)
.buf_size = buf_size,
};
- uv_call_sched(0, (u64)&uvcb);
+ uv_call(0, (u64)&uvcb);
switch (uvcb.header.rc) {
case UVC_RC_EXECUTED:
diff --git a/arch/s390/kernel/vdso/Makefile b/arch/s390/kernel/vdso/Makefile
index fece5d975eaf..35c834b895ec 100644
--- a/arch/s390/kernel/vdso/Makefile
+++ b/arch/s390/kernel/vdso/Makefile
@@ -30,7 +30,8 @@ KBUILD_CFLAGS_VDSO := $(filter-out -fno-asynchronous-unwind-tables,$(KBUILD_CFLA
KBUILD_CFLAGS_VDSO += -fPIC -fno-common -fno-builtin -fasynchronous-unwind-tables
KBUILD_CFLAGS_VDSO += -fno-stack-protector $(DISABLE_KSTACK_ERASE)
ldflags-y := -shared -soname=linux-vdso.so.1 \
- --hash-style=both --build-id=sha1 -T
+ --hash-style=both --build-id=sha1 \
+ $(call ld-option, --eh-frame-hdr) -T
$(targets:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_VDSO)
$(targets:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_VDSO)
diff --git a/arch/s390/kernel/vdso/vdso.lds.S b/arch/s390/kernel/vdso/vdso.lds.S
index 7bec4de0e8e0..841daeec4be2 100644
--- a/arch/s390/kernel/vdso/vdso.lds.S
+++ b/arch/s390/kernel/vdso/vdso.lds.S
@@ -82,12 +82,15 @@ SECTIONS
* We must supply the ELF program headers explicitly to get just one
* PT_LOAD segment, and set the flags explicitly to make segments read-only.
*/
+#define PF_R FLAGS(4)
+#define PF_RX FLAGS(5)
+
PHDRS
{
- text PT_LOAD FILEHDR PHDRS FLAGS(5); /* PF_R|PF_X */
- dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
- note PT_NOTE FLAGS(4); /* PF_R */
- eh_frame_hdr PT_GNU_EH_FRAME;
+ text PT_LOAD PF_RX FILEHDR PHDRS;
+ dynamic PT_DYNAMIC PF_R;
+ note PT_NOTE PF_R;
+ eh_frame_hdr PT_GNU_EH_FRAME PF_R;
}
/*
diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c
index d804e1140c2e..efcbf406f03e 100644
--- a/arch/s390/kernel/vtime.c
+++ b/arch/s390/kernel/vtime.c
@@ -32,7 +32,7 @@ static atomic64_t virt_timer_elapsed;
DEFINE_PER_CPU(u64, mt_cycles[8]);
static DEFINE_PER_CPU(u64, mt_scaling_mult) = { 1 };
static DEFINE_PER_CPU(u64, mt_scaling_div) = { 1 };
-static DEFINE_PER_CPU(u64, mt_scaling_jiffies);
+static DEFINE_PER_CPU(unsigned long, mt_scaling_jiffies);
static inline void set_vtimer(u64 expires)
{
@@ -81,7 +81,7 @@ static void update_mt_scaling(void)
memcpy(cycles_old, cycles_new,
sizeof(u64) * (smp_cpu_mtid + 1));
}
- __this_cpu_write(mt_scaling_jiffies, jiffies_64);
+ __this_cpu_write(mt_scaling_jiffies, jiffies);
}
static inline u64 update_tsk_timer(unsigned long *tsk_vtime, u64 new)
@@ -144,7 +144,7 @@ static int do_account_vtime(struct task_struct *tsk)
lc->system_timer += timer;
/* Update MT utilization calculation */
- if (smp_cpu_mtid && time_after64(jiffies_64, __this_cpu_read(mt_scaling_jiffies)))
+ if (smp_cpu_mtid && time_after(jiffies, __this_cpu_read(mt_scaling_jiffies)))
update_mt_scaling();
/* Calculate cputime delta */