diff options
Diffstat (limited to 'arch/s390/kernel')
27 files changed, 1047 insertions, 372 deletions
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 9733b3f0eb6..4da52fe3174 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -23,10 +23,11 @@ CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w  obj-y	:=  bitmap.o traps.o time.o process.o base.o early.o setup.o vtime.o \  	    processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o \  	    debug.o irq.o ipl.o dis.o diag.o mem_detect.o sclp.o vdso.o \ -	    sysinfo.o jump_label.o lgr.o os_info.o +	    sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o  obj-y	+= $(if $(CONFIG_64BIT),entry64.o,entry.o)  obj-y	+= $(if $(CONFIG_64BIT),reipl64.o,reipl.o) +obj-y	+= $(if $(CONFIG_64BIT),relocate_kernel64.o,relocate_kernel.o)  extra-y				+= head.o vmlinux.lds  extra-y				+= $(if $(CONFIG_64BIT),head64.o,head31.o) @@ -48,12 +49,11 @@ obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o  obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o  obj-$(CONFIG_FTRACE_SYSCALLS)  += ftrace.o  obj-$(CONFIG_CRASH_DUMP)	+= crash_dump.o -obj-$(CONFIG_PERF_EVENTS)	+= perf_event.o perf_cpum_cf.o -# Kexec part -S390_KEXEC_OBJS := machine_kexec.o crash.o -S390_KEXEC_OBJS += $(if $(CONFIG_64BIT),relocate_kernel64.o,relocate_kernel.o) -obj-$(CONFIG_KEXEC) += $(S390_KEXEC_OBJS) +ifdef CONFIG_64BIT +obj-$(CONFIG_PERF_EVENTS)	+= perf_event.o perf_cpum_cf.o +obj-y				+= runtime_instr.o cache.o +endif  # vdso  obj-$(CONFIG_64BIT)		+= vdso64/ diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index 45ef1a7b08f..fface87056e 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -157,6 +157,8 @@ int main(void)  	DEFINE(__LC_LAST_BREAK, offsetof(struct _lowcore, breaking_event_addr));  	DEFINE(__LC_VDSO_PER_CPU, offsetof(struct _lowcore, vdso_per_cpu_data));  	DEFINE(__LC_GMAP, offsetof(struct _lowcore, gmap)); +	DEFINE(__LC_PGM_TDB, offsetof(struct _lowcore, pgm_tdb)); +	DEFINE(__THREAD_trap_tdb, offsetof(struct task_struct, thread.trap_tdb));  	DEFINE(__GMAP_ASCE, offsetof(struct gmap, asce));  #endif /* CONFIG_32BIT */  	return 0; diff --git a/arch/s390/kernel/cache.c b/arch/s390/kernel/cache.c new file mode 100644 index 00000000000..8df8d8a19c9 --- /dev/null +++ b/arch/s390/kernel/cache.c @@ -0,0 +1,385 @@ +/* + * Extract CPU cache information and expose them via sysfs. + * + *    Copyright IBM Corp. 2012 + *    Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> + */ + +#include <linux/notifier.h> +#include <linux/seq_file.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/cpu.h> +#include <asm/facility.h> + +struct cache { +	unsigned long size; +	unsigned int line_size; +	unsigned int associativity; +	unsigned int nr_sets; +	unsigned int level   : 3; +	unsigned int type    : 2; +	unsigned int private : 1; +	struct list_head list; +}; + +struct cache_dir { +	struct kobject *kobj; +	struct cache_index_dir *index; +}; + +struct cache_index_dir { +	struct kobject kobj; +	int cpu; +	struct cache *cache; +	struct cache_index_dir *next; +}; + +enum { +	CACHE_SCOPE_NOTEXISTS, +	CACHE_SCOPE_PRIVATE, +	CACHE_SCOPE_SHARED, +	CACHE_SCOPE_RESERVED, +}; + +enum { +	CACHE_TYPE_SEPARATE, +	CACHE_TYPE_DATA, +	CACHE_TYPE_INSTRUCTION, +	CACHE_TYPE_UNIFIED, +}; + +enum { +	EXTRACT_TOPOLOGY, +	EXTRACT_LINE_SIZE, +	EXTRACT_SIZE, +	EXTRACT_ASSOCIATIVITY, +}; + +enum { +	CACHE_TI_UNIFIED = 0, +	CACHE_TI_INSTRUCTION = 0, +	CACHE_TI_DATA, +}; + +struct cache_info { +	unsigned char	    : 4; +	unsigned char scope : 2; +	unsigned char type  : 2; +}; + +#define CACHE_MAX_LEVEL 8 + +union cache_topology { +	struct cache_info ci[CACHE_MAX_LEVEL]; +	unsigned long long raw; +}; + +static const char * const cache_type_string[] = { +	"Data", +	"Instruction", +	"Unified", +}; + +static struct cache_dir *cache_dir_cpu[NR_CPUS]; +static LIST_HEAD(cache_list); + +void show_cacheinfo(struct seq_file *m) +{ +	struct cache *cache; +	int index = 0; + +	list_for_each_entry(cache, &cache_list, list) { +		seq_printf(m, "cache%-11d: ", index); +		seq_printf(m, "level=%d ", cache->level); +		seq_printf(m, "type=%s ", cache_type_string[cache->type]); +		seq_printf(m, "scope=%s ", cache->private ? "Private" : "Shared"); +		seq_printf(m, "size=%luK ", cache->size >> 10); +		seq_printf(m, "line_size=%u ", cache->line_size); +		seq_printf(m, "associativity=%d", cache->associativity); +		seq_puts(m, "\n"); +		index++; +	} +} + +static inline unsigned long ecag(int ai, int li, int ti) +{ +	unsigned long cmd, val; + +	cmd = ai << 4 | li << 1 | ti; +	asm volatile(".insn	rsy,0xeb000000004c,%0,0,0(%1)" /* ecag */ +		     : "=d" (val) : "a" (cmd)); +	return val; +} + +static int __init cache_add(int level, int private, int type) +{ +	struct cache *cache; +	int ti; + +	cache = kzalloc(sizeof(*cache), GFP_KERNEL); +	if (!cache) +		return -ENOMEM; +	ti = type == CACHE_TYPE_DATA ? CACHE_TI_DATA : CACHE_TI_UNIFIED; +	cache->size = ecag(EXTRACT_SIZE, level, ti); +	cache->line_size = ecag(EXTRACT_LINE_SIZE, level, ti); +	cache->associativity = ecag(EXTRACT_ASSOCIATIVITY, level, ti); +	cache->nr_sets = cache->size / cache->associativity; +	cache->nr_sets /= cache->line_size; +	cache->private = private; +	cache->level = level + 1; +	cache->type = type - 1; +	list_add_tail(&cache->list, &cache_list); +	return 0; +} + +static void __init cache_build_info(void) +{ +	struct cache *cache, *next; +	union cache_topology ct; +	int level, private, rc; + +	ct.raw = ecag(EXTRACT_TOPOLOGY, 0, 0); +	for (level = 0; level < CACHE_MAX_LEVEL; level++) { +		switch (ct.ci[level].scope) { +		case CACHE_SCOPE_NOTEXISTS: +		case CACHE_SCOPE_RESERVED: +			return; +		case CACHE_SCOPE_SHARED: +			private = 0; +			break; +		case CACHE_SCOPE_PRIVATE: +			private = 1; +			break; +		} +		if (ct.ci[level].type == CACHE_TYPE_SEPARATE) { +			rc  = cache_add(level, private, CACHE_TYPE_DATA); +			rc |= cache_add(level, private, CACHE_TYPE_INSTRUCTION); +		} else { +			rc = cache_add(level, private, ct.ci[level].type); +		} +		if (rc) +			goto error; +	} +	return; +error: +	list_for_each_entry_safe(cache, next, &cache_list, list) { +		list_del(&cache->list); +		kfree(cache); +	} +} + +static struct cache_dir *__cpuinit cache_create_cache_dir(int cpu) +{ +	struct cache_dir *cache_dir; +	struct kobject *kobj = NULL; +	struct device *dev; + +	dev = get_cpu_device(cpu); +	if (!dev) +		goto out; +	kobj = kobject_create_and_add("cache", &dev->kobj); +	if (!kobj) +		goto out; +	cache_dir = kzalloc(sizeof(*cache_dir), GFP_KERNEL); +	if (!cache_dir) +		goto out; +	cache_dir->kobj = kobj; +	cache_dir_cpu[cpu] = cache_dir; +	return cache_dir; +out: +	kobject_put(kobj); +	return NULL; +} + +static struct cache_index_dir *kobj_to_cache_index_dir(struct kobject *kobj) +{ +	return container_of(kobj, struct cache_index_dir, kobj); +} + +static void cache_index_release(struct kobject *kobj) +{ +	struct cache_index_dir *index; + +	index = kobj_to_cache_index_dir(kobj); +	kfree(index); +} + +static ssize_t cache_index_show(struct kobject *kobj, +				struct attribute *attr, char *buf) +{ +	struct kobj_attribute *kobj_attr; + +	kobj_attr = container_of(attr, struct kobj_attribute, attr); +	return kobj_attr->show(kobj, kobj_attr, buf); +} + +#define DEFINE_CACHE_ATTR(_name, _format, _value)			\ +static ssize_t cache_##_name##_show(struct kobject *kobj,		\ +				    struct kobj_attribute *attr,	\ +				    char *buf)				\ +{									\ +	struct cache_index_dir *index;					\ +									\ +	index = kobj_to_cache_index_dir(kobj);				\ +	return sprintf(buf, _format, _value);				\ +}									\ +static struct kobj_attribute cache_##_name##_attr =			\ +	__ATTR(_name, 0444, cache_##_name##_show, NULL); + +DEFINE_CACHE_ATTR(size, "%luK\n", index->cache->size >> 10); +DEFINE_CACHE_ATTR(coherency_line_size, "%u\n", index->cache->line_size); +DEFINE_CACHE_ATTR(number_of_sets, "%u\n", index->cache->nr_sets); +DEFINE_CACHE_ATTR(ways_of_associativity, "%u\n", index->cache->associativity); +DEFINE_CACHE_ATTR(type, "%s\n", cache_type_string[index->cache->type]); +DEFINE_CACHE_ATTR(level, "%d\n", index->cache->level); + +static ssize_t shared_cpu_map_func(struct kobject *kobj, int type, char *buf) +{ +	struct cache_index_dir *index; +	int len; + +	index = kobj_to_cache_index_dir(kobj); +	len = type ? +		cpulist_scnprintf(buf, PAGE_SIZE - 2, cpumask_of(index->cpu)) : +		cpumask_scnprintf(buf, PAGE_SIZE - 2, cpumask_of(index->cpu)); +	len += sprintf(&buf[len], "\n"); +	return len; +} + +static ssize_t shared_cpu_map_show(struct kobject *kobj, +				   struct kobj_attribute *attr, char *buf) +{ +	return shared_cpu_map_func(kobj, 0, buf); +} +static struct kobj_attribute cache_shared_cpu_map_attr = +	__ATTR(shared_cpu_map, 0444, shared_cpu_map_show, NULL); + +static ssize_t shared_cpu_list_show(struct kobject *kobj, +				    struct kobj_attribute *attr, char *buf) +{ +	return shared_cpu_map_func(kobj, 1, buf); +} +static struct kobj_attribute cache_shared_cpu_list_attr = +	__ATTR(shared_cpu_list, 0444, shared_cpu_list_show, NULL); + +static struct attribute *cache_index_default_attrs[] = { +	&cache_type_attr.attr, +	&cache_size_attr.attr, +	&cache_number_of_sets_attr.attr, +	&cache_ways_of_associativity_attr.attr, +	&cache_level_attr.attr, +	&cache_coherency_line_size_attr.attr, +	&cache_shared_cpu_map_attr.attr, +	&cache_shared_cpu_list_attr.attr, +	NULL, +}; + +static const struct sysfs_ops cache_index_ops = { +	.show = cache_index_show, +}; + +static struct kobj_type cache_index_type = { +	.sysfs_ops = &cache_index_ops, +	.release = cache_index_release, +	.default_attrs = cache_index_default_attrs, +}; + +static int __cpuinit cache_create_index_dir(struct cache_dir *cache_dir, +					    struct cache *cache, int index, +					    int cpu) +{ +	struct cache_index_dir *index_dir; +	int rc; + +	index_dir = kzalloc(sizeof(*index_dir), GFP_KERNEL); +	if (!index_dir) +		return -ENOMEM; +	index_dir->cache = cache; +	index_dir->cpu = cpu; +	rc = kobject_init_and_add(&index_dir->kobj, &cache_index_type, +				  cache_dir->kobj, "index%d", index); +	if (rc) +		goto out; +	index_dir->next = cache_dir->index; +	cache_dir->index = index_dir; +	return 0; +out: +	kfree(index_dir); +	return rc; +} + +static int __cpuinit cache_add_cpu(int cpu) +{ +	struct cache_dir *cache_dir; +	struct cache *cache; +	int rc, index = 0; + +	if (list_empty(&cache_list)) +		return 0; +	cache_dir = cache_create_cache_dir(cpu); +	if (!cache_dir) +		return -ENOMEM; +	list_for_each_entry(cache, &cache_list, list) { +		if (!cache->private) +			break; +		rc = cache_create_index_dir(cache_dir, cache, index, cpu); +		if (rc) +			return rc; +		index++; +	} +	return 0; +} + +static void __cpuinit cache_remove_cpu(int cpu) +{ +	struct cache_index_dir *index, *next; +	struct cache_dir *cache_dir; + +	cache_dir = cache_dir_cpu[cpu]; +	if (!cache_dir) +		return; +	index = cache_dir->index; +	while (index) { +		next = index->next; +		kobject_put(&index->kobj); +		index = next; +	} +	kobject_put(cache_dir->kobj); +	kfree(cache_dir); +	cache_dir_cpu[cpu] = NULL; +} + +static int __cpuinit cache_hotplug(struct notifier_block *nfb, +				   unsigned long action, void *hcpu) +{ +	int cpu = (long)hcpu; +	int rc = 0; + +	switch (action & ~CPU_TASKS_FROZEN) { +	case CPU_ONLINE: +		rc = cache_add_cpu(cpu); +		if (rc) +			cache_remove_cpu(cpu); +		break; +	case CPU_DEAD: +		cache_remove_cpu(cpu); +		break; +	} +	return rc ? NOTIFY_BAD : NOTIFY_OK; +} + +static int __init cache_init(void) +{ +	int cpu; + +	if (!test_facility(34)) +		return 0; +	cache_build_info(); +	for_each_online_cpu(cpu) +		cache_add_cpu(cpu); +	hotcpu_notifier(cache_hotplug, 0); +	return 0; +} +device_initcall(cache_init); diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S index 2d82cfcbce5..3afba804fe9 100644 --- a/arch/s390/kernel/compat_wrapper.S +++ b/arch/s390/kernel/compat_wrapper.S @@ -1646,3 +1646,16 @@ ENTRY(compat_sys_process_vm_writev_wrapper)  	llgf	%r0,164(%r15)		# unsigned long  	stg	%r0,160(%r15)  	jg	compat_sys_process_vm_writev + +ENTRY(sys_s390_runtime_instr_wrapper) +	lgfr	%r2,%r2			# int +	lgfr	%r3,%r3			# int +	jg	sys_s390_runtime_instr + +ENTRY(sys_kcmp_wrapper) +	lgfr	%r2,%r2			# pid_t +	lgfr	%r3,%r3			# pid_t +	lgfr	%r4,%r4			# int +	llgfr	%r5,%r5			# unsigned long +	llgfr	%r6,%r6			# unsigned long +	jg	sys_kcmp diff --git a/arch/s390/kernel/crash.c b/arch/s390/kernel/crash.c deleted file mode 100644 index 3819153de8b..00000000000 --- a/arch/s390/kernel/crash.c +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright IBM Corp. 2005 - * - * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> - * - */ - -#include <linux/threads.h> -#include <linux/kexec.h> -#include <linux/reboot.h> - -void machine_crash_shutdown(struct pt_regs *regs) -{ -} diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c index cc1172b2687..fb8d8781a01 100644 --- a/arch/s390/kernel/crash_dump.c +++ b/arch/s390/kernel/crash_dump.c @@ -13,8 +13,9 @@  #include <linux/slab.h>  #include <linux/bootmem.h>  #include <linux/elf.h> -#include <asm/ipl.h>  #include <asm/os_info.h> +#include <asm/elf.h> +#include <asm/ipl.h>  #define PTR_ADD(x, y) (((char *) (x)) + ((unsigned long) (y)))  #define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y))) diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c index 619c5d35072..cc84a24c023 100644 --- a/arch/s390/kernel/dis.c +++ b/arch/s390/kernel/dis.c @@ -315,6 +315,11 @@ enum {  	LONG_INSN_POPCNT,  	LONG_INSN_RISBHG,  	LONG_INSN_RISBLG, +	LONG_INSN_RINEXT, +	LONG_INSN_RIEMIT, +	LONG_INSN_TABORT, +	LONG_INSN_TBEGIN, +	LONG_INSN_TBEGINC,  };  static char *long_insn_name[] = { @@ -329,7 +334,12 @@ static char *long_insn_name[] = {  	[LONG_INSN_LLGHRL] = "llghrl",  	[LONG_INSN_POPCNT] = "popcnt",  	[LONG_INSN_RISBHG] = "risbhg", -	[LONG_INSN_RISBLG] = "risblk", +	[LONG_INSN_RISBLG] = "risblg", +	[LONG_INSN_RINEXT] = "rinext", +	[LONG_INSN_RIEMIT] = "riemit", +	[LONG_INSN_TABORT] = "tabort", +	[LONG_INSN_TBEGIN] = "tbegin", +	[LONG_INSN_TBEGINC] = "tbeginc",  };  static struct insn opcode[] = { @@ -582,6 +592,17 @@ static struct insn opcode_a7[] = {  	{ "", 0, INSTR_INVALID }  }; +static struct insn opcode_aa[] = { +#ifdef CONFIG_64BIT +	{ { 0, LONG_INSN_RINEXT }, 0x00, INSTR_RI_RI }, +	{ "rion", 0x01, INSTR_RI_RI }, +	{ "tric", 0x02, INSTR_RI_RI }, +	{ "rioff", 0x03, INSTR_RI_RI }, +	{ { 0, LONG_INSN_RIEMIT }, 0x04, INSTR_RI_RI }, +#endif +	{ "", 0, INSTR_INVALID } +}; +  static struct insn opcode_b2[] = {  #ifdef CONFIG_64BIT  	{ "sske", 0x2b, INSTR_RRF_M0RR }, @@ -594,6 +615,9 @@ static struct insn opcode_b2[] = {  	{ "lpswe", 0xb2, INSTR_S_RD },  	{ "srnmt", 0xb9, INSTR_S_RD },  	{ "lfas", 0xbd, INSTR_S_RD }, +	{ "etndg", 0xec, INSTR_RRE_R0 }, +	{ { 0, LONG_INSN_TABORT }, 0xfc, INSTR_S_RD }, +	{ "tend", 0xf8, INSTR_S_RD },  #endif  	{ "stidp", 0x02, INSTR_S_RD },  	{ "sck", 0x04, INSTR_S_RD }, @@ -1150,6 +1174,7 @@ static struct insn opcode_e3[] = {  	{ "stfh", 0xcb, INSTR_RXY_RRRD },  	{ "chf", 0xcd, INSTR_RXY_RRRD },  	{ "clhf", 0xcf, INSTR_RXY_RRRD }, +	{ "ntstg", 0x25, INSTR_RXY_RRRD },  #endif  	{ "lrv", 0x1e, INSTR_RXY_RRRD },  	{ "lrvh", 0x1f, INSTR_RXY_RRRD }, @@ -1173,6 +1198,8 @@ static struct insn opcode_e5[] = {  	{ "mvhhi", 0x44, INSTR_SIL_RDI },  	{ "mvhi", 0x4c, INSTR_SIL_RDI },  	{ "mvghi", 0x48, INSTR_SIL_RDI }, +	{ { 0, LONG_INSN_TBEGIN }, 0x60, INSTR_SIL_RDU }, +	{ { 0, LONG_INSN_TBEGINC }, 0x61, INSTR_SIL_RDU },  #endif  	{ "lasp", 0x00, INSTR_SSE_RDRD },  	{ "tprot", 0x01, INSTR_SSE_RDRD }, @@ -1210,6 +1237,9 @@ static struct insn opcode_eb[] = {  	{ "cliy", 0x55, INSTR_SIY_URD },  	{ "oiy", 0x56, INSTR_SIY_URD },  	{ "xiy", 0x57, INSTR_SIY_URD }, +	{ "lric", 0x60, INSTR_RSY_RDRM }, +	{ "stric", 0x61, INSTR_RSY_RDRM }, +	{ "mric", 0x62, INSTR_RSY_RDRM },  	{ "icmh", 0x80, INSTR_RSE_RURD },  	{ "icmh", 0x80, INSTR_RSY_RURD },  	{ "icmy", 0x81, INSTR_RSY_RURD }, @@ -1408,6 +1438,9 @@ static struct insn *find_insn(unsigned char *code)  	case 0xa7:  		table = opcode_a7;  		break; +	case 0xaa: +		table = opcode_aa; +		break;  	case 0xb2:  		table = opcode_b2;  		break; @@ -1601,3 +1634,26 @@ void show_code(struct pt_regs *regs)  	}  	printk("\n");  } + +void print_fn_code(unsigned char *code, unsigned long len) +{ +	char buffer[64], *ptr; +	int opsize, i; + +	while (len) { +		ptr = buffer; +		opsize = insn_length(*code); +		ptr += sprintf(ptr, "%p: ", code); +		for (i = 0; i < opsize; i++) +			ptr += sprintf(ptr, "%02x", code[i]); +		*ptr++ = '\t'; +		if (i < 4) +			*ptr++ = '\t'; +		ptr += print_insn(ptr, code, (unsigned long) code); +		*ptr++ = '\n'; +		*ptr++ = 0; +		printk(buffer); +		code += opsize; +		len -= opsize; +	} +} diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 83c3271c442..7f4717675c1 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -215,36 +215,54 @@ static noinline __init void init_kernel_storage_key(void)  				     PAGE_DEFAULT_KEY, 0);  } -static __initdata struct sysinfo_3_2_2 vmms __aligned(PAGE_SIZE); +static __initdata char sysinfo_page[PAGE_SIZE] __aligned(PAGE_SIZE);  static noinline __init void detect_machine_type(void)  { +	struct sysinfo_3_2_2 *vmms = (struct sysinfo_3_2_2 *)&sysinfo_page; +  	/* Check current-configuration-level */ -	if ((stsi(NULL, 0, 0, 0) >> 28) <= 2) { +	if (stsi(NULL, 0, 0, 0) <= 2) {  		S390_lowcore.machine_flags |= MACHINE_FLAG_LPAR;  		return;  	}  	/* Get virtual-machine cpu information. */ -	if (stsi(&vmms, 3, 2, 2) == -ENOSYS || !vmms.count) +	if (stsi(vmms, 3, 2, 2) || !vmms->count)  		return;  	/* Running under KVM? If not we assume z/VM */ -	if (!memcmp(vmms.vm[0].cpi, "\xd2\xe5\xd4", 3)) +	if (!memcmp(vmms->vm[0].cpi, "\xd2\xe5\xd4", 3))  		S390_lowcore.machine_flags |= MACHINE_FLAG_KVM;  	else  		S390_lowcore.machine_flags |= MACHINE_FLAG_VM;  } +static __init void setup_topology(void) +{ +#ifdef CONFIG_64BIT +	int max_mnest; + +	if (!test_facility(11)) +		return; +	S390_lowcore.machine_flags |= MACHINE_FLAG_TOPOLOGY; +	for (max_mnest = 6; max_mnest > 1; max_mnest--) { +		if (stsi(&sysinfo_page, 15, 1, max_mnest) == 0) +			break; +	} +	topology_max_mnest = max_mnest; +#endif +} +  static void early_pgm_check_handler(void)  { -	unsigned long addr;  	const struct exception_table_entry *fixup; +	unsigned long addr;  	addr = S390_lowcore.program_old_psw.addr;  	fixup = search_exception_tables(addr & PSW_ADDR_INSN);  	if (!fixup)  		disabled_wait(0); -	S390_lowcore.program_old_psw.addr = fixup->fixup | PSW_ADDR_AMODE; +	S390_lowcore.program_old_psw.addr = extable_fixup(fixup)|PSW_ADDR_AMODE;  }  static noinline __init void setup_lowcore_early(void) @@ -267,12 +285,10 @@ static noinline __init void setup_facility_list(void)  static noinline __init void setup_hpage(void)  { -#ifndef CONFIG_DEBUG_PAGEALLOC  	if (!test_facility(2) || !test_facility(8))  		return;  	S390_lowcore.machine_flags |= MACHINE_FLAG_HPAGE;  	__ctl_set_bit(0, 23); -#endif  }  static __init void detect_mvpg(void) @@ -366,12 +382,12 @@ static __init void detect_machine_facilities(void)  		S390_lowcore.machine_flags |= MACHINE_FLAG_IDTE;  	if (test_facility(8))  		S390_lowcore.machine_flags |= MACHINE_FLAG_PFMF; -	if (test_facility(11)) -		S390_lowcore.machine_flags |= MACHINE_FLAG_TOPOLOGY;  	if (test_facility(27))  		S390_lowcore.machine_flags |= MACHINE_FLAG_MVCOS;  	if (test_facility(40))  		S390_lowcore.machine_flags |= MACHINE_FLAG_SPP; +	if (test_facility(50) && test_facility(73)) +		S390_lowcore.machine_flags |= MACHINE_FLAG_TE;  #endif  } @@ -441,7 +457,6 @@ static void __init setup_boot_command_line(void)  	append_to_cmdline(append_ipl_scpdata);  } -  /*   * Save ipl parameters, clear bss memory, initialize storage keys   * and create a kernel NSS at startup if the SAVESYS= parm is defined @@ -468,6 +483,7 @@ void __init startup_init(void)  	detect_diag44();  	detect_machine_facilities();  	setup_hpage(); +	setup_topology();  	sclp_facilities_detect();  	detect_memory_layout(memory_chunk);  #ifdef CONFIG_DYNAMIC_FTRACE diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index 349b7eeb348..7549985402f 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S @@ -10,6 +10,7 @@  #include <linux/init.h>  #include <linux/linkage.h> +#include <asm/processor.h>  #include <asm/cache.h>  #include <asm/errno.h>  #include <asm/ptrace.h> @@ -412,6 +413,11 @@ ENTRY(pgm_check_handler)  1:	UPDATE_VTIME %r14,__LC_SYNC_ENTER_TIMER  	LAST_BREAK %r14  	lg	%r15,__LC_KERNEL_STACK +	lg	%r14,__TI_task(%r12) +	lghi	%r13,__LC_PGM_TDB +	tm	__LC_PGM_ILC+2,0x02	# check for transaction abort +	jz	2f +	mvc	__THREAD_trap_tdb(256,%r14),0(%r13)  2:	aghi	%r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)  	la	%r11,STACK_FRAME_OVERHEAD(%r15)  	stmg	%r0,%r7,__PT_R0(%r11) @@ -422,13 +428,12 @@ ENTRY(pgm_check_handler)  	stg	%r10,__PT_ARGS(%r11)  	tm	__LC_PGM_ILC+3,0x80	# check for per exception  	jz	0f -	lg	%r1,__TI_task(%r12)  	tmhh	%r8,0x0001		# kernel per event ?  	jz	pgm_kprobe  	oi	__TI_flags+7(%r12),_TIF_PER_TRAP -	mvc	__THREAD_per_address(8,%r1),__LC_PER_ADDRESS -	mvc	__THREAD_per_cause(2,%r1),__LC_PER_CAUSE -	mvc	__THREAD_per_paid(1,%r1),__LC_PER_PAID +	mvc	__THREAD_per_address(8,%r14),__LC_PER_ADDRESS +	mvc	__THREAD_per_cause(2,%r14),__LC_PER_CAUSE +	mvc	__THREAD_per_paid(1,%r14),__LC_PER_PAID  0:	REENABLE_IRQS  	xc	__SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)  	larl	%r1,pgm_check_table @@ -1004,9 +1009,7 @@ sie_fault:  .Lhost_id:  	.quad	0 -	.section __ex_table,"a" -	.quad	sie_loop,sie_fault -	.previous +	EX_TABLE(sie_loop,sie_fault)  #endif  		.section .rodata, "a" diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index dd7630d8aab..6cdc55b26d6 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -30,33 +30,35 @@ struct irq_class {  };  static const struct irq_class intrclass_names[] = { -	{.name = "EXT" }, -	{.name = "I/O" }, -	{.name = "CLK", .desc = "[EXT] Clock Comparator" }, -	{.name = "EXC", .desc = "[EXT] External Call" }, -	{.name = "EMS", .desc = "[EXT] Emergency Signal" }, -	{.name = "TMR", .desc = "[EXT] CPU Timer" }, -	{.name = "TAL", .desc = "[EXT] Timing Alert" }, -	{.name = "PFL", .desc = "[EXT] Pseudo Page Fault" }, -	{.name = "DSD", .desc = "[EXT] DASD Diag" }, -	{.name = "VRT", .desc = "[EXT] Virtio" }, -	{.name = "SCP", .desc = "[EXT] Service Call" }, -	{.name = "IUC", .desc = "[EXT] IUCV" }, -	{.name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling" }, -	{.name = "CMC", .desc = "[EXT] CPU-Measurement: Counter" }, -	{.name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt" }, -	{.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt" }, -	{.name = "DAS", .desc = "[I/O] DASD" }, -	{.name = "C15", .desc = "[I/O] 3215" }, -	{.name = "C70", .desc = "[I/O] 3270" }, -	{.name = "TAP", .desc = "[I/O] Tape" }, -	{.name = "VMR", .desc = "[I/O] Unit Record Devices" }, -	{.name = "LCS", .desc = "[I/O] LCS" }, -	{.name = "CLW", .desc = "[I/O] CLAW" }, -	{.name = "CTC", .desc = "[I/O] CTC" }, -	{.name = "APB", .desc = "[I/O] AP Bus" }, -	{.name = "CSC", .desc = "[I/O] CHSC Subchannel" }, -	{.name = "NMI", .desc = "[NMI] Machine Check" }, +	[EXTERNAL_INTERRUPT] = {.name = "EXT"}, +	[IO_INTERRUPT]	     = {.name = "I/O"}, +	[EXTINT_CLK] = {.name = "CLK", .desc = "[EXT] Clock Comparator"}, +	[EXTINT_EXC] = {.name = "EXC", .desc = "[EXT] External Call"}, +	[EXTINT_EMS] = {.name = "EMS", .desc = "[EXT] Emergency Signal"}, +	[EXTINT_TMR] = {.name = "TMR", .desc = "[EXT] CPU Timer"}, +	[EXTINT_TLA] = {.name = "TAL", .desc = "[EXT] Timing Alert"}, +	[EXTINT_PFL] = {.name = "PFL", .desc = "[EXT] Pseudo Page Fault"}, +	[EXTINT_DSD] = {.name = "DSD", .desc = "[EXT] DASD Diag"}, +	[EXTINT_VRT] = {.name = "VRT", .desc = "[EXT] Virtio"}, +	[EXTINT_SCP] = {.name = "SCP", .desc = "[EXT] Service Call"}, +	[EXTINT_IUC] = {.name = "IUC", .desc = "[EXT] IUCV"}, +	[EXTINT_CMS] = {.name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling"}, +	[EXTINT_CMC] = {.name = "CMC", .desc = "[EXT] CPU-Measurement: Counter"}, +	[EXTINT_CMR] = {.name = "CMR", .desc = "[EXT] CPU-Measurement: RI"}, +	[IOINT_CIO]  = {.name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt"}, +	[IOINT_QAI]  = {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt"}, +	[IOINT_DAS]  = {.name = "DAS", .desc = "[I/O] DASD"}, +	[IOINT_C15]  = {.name = "C15", .desc = "[I/O] 3215"}, +	[IOINT_C70]  = {.name = "C70", .desc = "[I/O] 3270"}, +	[IOINT_TAP]  = {.name = "TAP", .desc = "[I/O] Tape"}, +	[IOINT_VMR]  = {.name = "VMR", .desc = "[I/O] Unit Record Devices"}, +	[IOINT_LCS]  = {.name = "LCS", .desc = "[I/O] LCS"}, +	[IOINT_CLW]  = {.name = "CLW", .desc = "[I/O] CLAW"}, +	[IOINT_CTC]  = {.name = "CTC", .desc = "[I/O] CTC"}, +	[IOINT_APB]  = {.name = "APB", .desc = "[I/O] AP Bus"}, +	[IOINT_ADM]  = {.name = "ADM", .desc = "[I/O] EADM Subchannel"}, +	[IOINT_CSC]  = {.name = "CSC", .desc = "[I/O] CHSC Subchannel"}, +	[NMI_NMI]    = {.name = "NMI", .desc = "[NMI] Machine Check"},  };  /* diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index 8aa634f5944..d1c7214e157 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c @@ -547,7 +547,7 @@ static int __kprobes kprobe_trap_handler(struct pt_regs *regs, int trapnr)  		 */  		entry = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);  		if (entry) { -			regs->psw.addr = entry->fixup | PSW_ADDR_AMODE; +			regs->psw.addr = extable_fixup(entry) | PSW_ADDR_AMODE;  			return 1;  		} diff --git a/arch/s390/kernel/lgr.c b/arch/s390/kernel/lgr.c index eca94e74d19..6ea6d69339b 100644 --- a/arch/s390/kernel/lgr.c +++ b/arch/s390/kernel/lgr.c @@ -51,16 +51,6 @@ static struct lgr_info lgr_info_cur;  static struct debug_info *lgr_dbf;  /* - * Return number of valid stsi levels - */ -static inline int stsi_0(void) -{ -	int rc = stsi(NULL, 0, 0, 0); - -	return rc == -ENOSYS ? rc : (((unsigned int) rc) >> 28); -} - -/*   * Copy buffer and then convert it to ASCII   */  static void cpascii(char *dst, char *src, int size) @@ -76,7 +66,7 @@ static void lgr_stsi_1_1_1(struct lgr_info *lgr_info)  {  	struct sysinfo_1_1_1 *si = (void *) lgr_page; -	if (stsi(si, 1, 1, 1) == -ENOSYS) +	if (stsi(si, 1, 1, 1))  		return;  	cpascii(lgr_info->manufacturer, si->manufacturer,  		sizeof(si->manufacturer)); @@ -93,7 +83,7 @@ static void lgr_stsi_2_2_2(struct lgr_info *lgr_info)  {  	struct sysinfo_2_2_2 *si = (void *) lgr_page; -	if (stsi(si, 2, 2, 2) == -ENOSYS) +	if (stsi(si, 2, 2, 2))  		return;  	cpascii(lgr_info->name, si->name, sizeof(si->name));  	memcpy(&lgr_info->lpar_number, &si->lpar_number, @@ -108,7 +98,7 @@ static void lgr_stsi_3_2_2(struct lgr_info *lgr_info)  	struct sysinfo_3_2_2 *si = (void *) lgr_page;  	int i; -	if (stsi(si, 3, 2, 2) == -ENOSYS) +	if (stsi(si, 3, 2, 2))  		return;  	for (i = 0; i < min_t(u8, si->count, VM_LEVEL_MAX); i++) {  		cpascii(lgr_info->vm[i].name, si->vm[i].name, @@ -124,16 +114,17 @@ static void lgr_stsi_3_2_2(struct lgr_info *lgr_info)   */  static void lgr_info_get(struct lgr_info *lgr_info)  { +	int level; +  	memset(lgr_info, 0, sizeof(*lgr_info));  	stfle(lgr_info->stfle_fac_list, ARRAY_SIZE(lgr_info->stfle_fac_list)); -	lgr_info->level = stsi_0(); -	if (lgr_info->level == -ENOSYS) -		return; -	if (lgr_info->level >= 1) +	level = stsi(NULL, 0, 0, 0); +	lgr_info->level = level; +	if (level >= 1)  		lgr_stsi_1_1_1(lgr_info); -	if (lgr_info->level >= 2) +	if (level >= 2)  		lgr_stsi_2_2_2(lgr_info); -	if (lgr_info->level >= 3) +	if (level >= 3)  		lgr_stsi_3_2_2(lgr_info);  } diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c index 493304bdf1c..b3de2770001 100644 --- a/arch/s390/kernel/machine_kexec.c +++ b/arch/s390/kernel/machine_kexec.c @@ -21,6 +21,7 @@  #include <asm/reset.h>  #include <asm/ipl.h>  #include <asm/diag.h> +#include <asm/elf.h>  #include <asm/asm-offsets.h>  #include <asm/os_info.h> @@ -31,8 +32,6 @@ extern const unsigned long long relocate_kernel_len;  #ifdef CONFIG_CRASH_DUMP -void *fill_cpu_elf_notes(void *ptr, struct save_area *sa); -  /*   * Create ELF notes for one CPU   */ @@ -159,7 +158,7 @@ int machine_kexec_prepare(struct kimage *image)  	/* Can't replace kernel image since it is read-only. */  	if (ipl_flags & IPL_NSS_VALID) -		return -ENOSYS; +		return -EOPNOTSUPP;  	if (image->type == KEXEC_TYPE_CRASH)  		return machine_kexec_prepare_kdump(); @@ -191,6 +190,10 @@ void machine_shutdown(void)  {  } +void machine_crash_shutdown(struct pt_regs *regs) +{ +} +  /*   * Do normal kexec   */ diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 733175373a4..5024be27df4 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -26,10 +26,12 @@  #include <asm/io.h>  #include <asm/processor.h>  #include <asm/vtimer.h> +#include <asm/exec.h>  #include <asm/irq.h>  #include <asm/nmi.h>  #include <asm/smp.h>  #include <asm/switch_to.h> +#include <asm/runtime_instr.h>  #include "entry.h"  asmlinkage void ret_from_fork(void) asm ("ret_from_fork"); @@ -132,6 +134,7 @@ EXPORT_SYMBOL(kernel_thread);   */  void exit_thread(void)  { +	exit_thread_runtime_instr();  }  void flush_thread(void) @@ -170,6 +173,11 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp,  	/* Save access registers to new thread structure. */  	save_access_regs(&p->thread.acrs[0]); +	/* Don't copy runtime instrumentation info */ +	p->thread.ri_cb = NULL; +	p->thread.ri_signum = 0; +	frame->childregs.psw.mask &= ~PSW_MASK_RI; +  #ifndef CONFIG_64BIT  	/*  	 * save fprs to current->thread.fp_regs to merge them with diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c index 572d4c9cb33..753c41d0ffd 100644 --- a/arch/s390/kernel/processor.c +++ b/arch/s390/kernel/processor.c @@ -39,9 +39,9 @@ void __cpuinit cpu_init(void)   */  static int show_cpuinfo(struct seq_file *m, void *v)  { -	static const char *hwcap_str[10] = { +	static const char *hwcap_str[] = {  		"esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp", -		"edat", "etf3eh", "highgprs" +		"edat", "etf3eh", "highgprs", "te"  	};  	unsigned long n = (unsigned long) v - 1;  	int i; @@ -54,10 +54,11 @@ static int show_cpuinfo(struct seq_file *m, void *v)  			   num_online_cpus(), loops_per_jiffy/(500000/HZ),  			   (loops_per_jiffy/(5000/HZ))%100);  		seq_puts(m, "features\t: "); -		for (i = 0; i < 10; i++) +		for (i = 0; i < ARRAY_SIZE(hwcap_str); i++)  			if (hwcap_str[i] && (elf_hwcap & (1UL << i)))  				seq_printf(m, "%s ", hwcap_str[i]);  		seq_puts(m, "\n"); +		show_cacheinfo(m);  	}  	get_online_cpus();  	if (cpu_online(n)) { diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index e4be113fbac..a314c57f4e9 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -42,6 +42,7 @@ enum s390_regset {  	REGSET_GENERAL,  	REGSET_FP,  	REGSET_LAST_BREAK, +	REGSET_TDB,  	REGSET_SYSTEM_CALL,  	REGSET_GENERAL_EXTENDED,  }; @@ -52,6 +53,22 @@ void update_per_regs(struct task_struct *task)  	struct thread_struct *thread = &task->thread;  	struct per_regs old, new; +#ifdef CONFIG_64BIT +	/* Take care of the enable/disable of transactional execution. */ +	if (MACHINE_HAS_TE) { +		unsigned long cr0, cr0_new; + +		__ctl_store(cr0, 0, 0); +		/* set or clear transaction execution bits 8 and 9. */ +		if (task->thread.per_flags & PER_FLAG_NO_TE) +			cr0_new = cr0 & ~(3UL << 54); +		else +			cr0_new = cr0 | (3UL << 54); +		/* Only load control register 0 if necessary. */ +		if (cr0 != cr0_new) +			__ctl_load(cr0_new, 0, 0); +	} +#endif  	/* Copy user specified PER registers */  	new.control = thread->per_user.control;  	new.start = thread->per_user.start; @@ -60,6 +77,10 @@ void update_per_regs(struct task_struct *task)  	/* merge TIF_SINGLE_STEP into user specified PER registers. */  	if (test_tsk_thread_flag(task, TIF_SINGLE_STEP)) {  		new.control |= PER_EVENT_IFETCH; +#ifdef CONFIG_64BIT +		new.control |= PER_CONTROL_SUSPENSION; +		new.control |= PER_EVENT_TRANSACTION_END; +#endif  		new.start = 0;  		new.end = PSW_ADDR_INSN;  	} @@ -100,6 +121,7 @@ void ptrace_disable(struct task_struct *task)  	memset(&task->thread.per_event, 0, sizeof(task->thread.per_event));  	clear_tsk_thread_flag(task, TIF_SINGLE_STEP);  	clear_tsk_thread_flag(task, TIF_PER_TRAP); +	task->thread.per_flags = 0;  }  #ifndef CONFIG_64BIT @@ -416,6 +438,16 @@ long arch_ptrace(struct task_struct *child, long request,  		put_user(task_thread_info(child)->last_break,  			 (unsigned long __user *) data);  		return 0; +	case PTRACE_ENABLE_TE: +		if (!MACHINE_HAS_TE) +			return -EIO; +		child->thread.per_flags &= ~PER_FLAG_NO_TE; +		return 0; +	case PTRACE_DISABLE_TE: +		if (!MACHINE_HAS_TE) +			return -EIO; +		child->thread.per_flags |= PER_FLAG_NO_TE; +		return 0;  	default:  		/* Removing high order bit from addr (only for 31 bit). */  		addr &= PSW_ADDR_INSN; @@ -903,6 +935,28 @@ static int s390_last_break_set(struct task_struct *target,  	return 0;  } +static int s390_tdb_get(struct task_struct *target, +			const struct user_regset *regset, +			unsigned int pos, unsigned int count, +			void *kbuf, void __user *ubuf) +{ +	struct pt_regs *regs = task_pt_regs(target); +	unsigned char *data; + +	if (!(regs->int_code & 0x200)) +		return -ENODATA; +	data = target->thread.trap_tdb; +	return user_regset_copyout(&pos, &count, &kbuf, &ubuf, data, 0, 256); +} + +static int s390_tdb_set(struct task_struct *target, +			const struct user_regset *regset, +			unsigned int pos, unsigned int count, +			const void *kbuf, const void __user *ubuf) +{ +	return 0; +} +  #endif  static int s390_system_call_get(struct task_struct *target, @@ -951,6 +1005,14 @@ static const struct user_regset s390_regsets[] = {  		.get = s390_last_break_get,  		.set = s390_last_break_set,  	}, +	[REGSET_TDB] = { +		.core_note_type = NT_S390_TDB, +		.n = 1, +		.size = 256, +		.align = 1, +		.get = s390_tdb_get, +		.set = s390_tdb_set, +	},  #endif  	[REGSET_SYSTEM_CALL] = {  		.core_note_type = NT_S390_SYSTEM_CALL, @@ -1148,6 +1210,14 @@ static const struct user_regset s390_compat_regsets[] = {  		.get = s390_compat_last_break_get,  		.set = s390_compat_last_break_set,  	}, +	[REGSET_TDB] = { +		.core_note_type = NT_S390_TDB, +		.n = 1, +		.size = 256, +		.align = 1, +		.get = s390_tdb_get, +		.set = s390_tdb_set, +	},  	[REGSET_SYSTEM_CALL] = {  		.core_note_type = NT_S390_SYSTEM_CALL,  		.n = 1, diff --git a/arch/s390/kernel/runtime_instr.c b/arch/s390/kernel/runtime_instr.c new file mode 100644 index 00000000000..61066f6f71a --- /dev/null +++ b/arch/s390/kernel/runtime_instr.c @@ -0,0 +1,150 @@ +/* + * Copyright IBM Corp. 2012 + * Author(s): Jan Glauber <jang@linux.vnet.ibm.com> + */ + +#include <linux/kernel.h> +#include <linux/syscalls.h> +#include <linux/signal.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <asm/runtime_instr.h> +#include <asm/cpu_mf.h> +#include <asm/irq.h> + +/* empty control block to disable RI by loading it */ +struct runtime_instr_cb runtime_instr_empty_cb; + +static int runtime_instr_avail(void) +{ +	return test_facility(64); +} + +static void disable_runtime_instr(void) +{ +	struct pt_regs *regs = task_pt_regs(current); + +	load_runtime_instr_cb(&runtime_instr_empty_cb); + +	/* +	 * Make sure the RI bit is deleted from the PSW. If the user did not +	 * switch off RI before the system call the process will get a +	 * specification exception otherwise. +	 */ +	regs->psw.mask &= ~PSW_MASK_RI; +} + +static void init_runtime_instr_cb(struct runtime_instr_cb *cb) +{ +	cb->buf_limit = 0xfff; +	if (s390_user_mode == HOME_SPACE_MODE) +		cb->home_space = 1; +	cb->int_requested = 1; +	cb->pstate = 1; +	cb->pstate_set_buf = 1; +	cb->pstate_sample = 1; +	cb->pstate_collect = 1; +	cb->key = PAGE_DEFAULT_KEY; +	cb->valid = 1; +} + +void exit_thread_runtime_instr(void) +{ +	struct task_struct *task = current; + +	if (!task->thread.ri_cb) +		return; +	disable_runtime_instr(); +	kfree(task->thread.ri_cb); +	task->thread.ri_signum = 0; +	task->thread.ri_cb = NULL; +} + +static void runtime_instr_int_handler(struct ext_code ext_code, +				unsigned int param32, unsigned long param64) +{ +	struct siginfo info; + +	if (!(param32 & CPU_MF_INT_RI_MASK)) +		return; + +	kstat_cpu(smp_processor_id()).irqs[EXTINT_CMR]++; + +	if (!current->thread.ri_cb) +		return; +	if (current->thread.ri_signum < SIGRTMIN || +	    current->thread.ri_signum > SIGRTMAX) { +		WARN_ON_ONCE(1); +		return; +	} + +	memset(&info, 0, sizeof(info)); +	info.si_signo = current->thread.ri_signum; +	info.si_code = SI_QUEUE; +	if (param32 & CPU_MF_INT_RI_BUF_FULL) +		info.si_int = ENOBUFS; +	else if (param32 & CPU_MF_INT_RI_HALTED) +		info.si_int = ECANCELED; +	else +		return; /* unknown reason */ + +	send_sig_info(current->thread.ri_signum, &info, current); +} + +SYSCALL_DEFINE2(s390_runtime_instr, int, command, int, signum) +{ +	struct runtime_instr_cb *cb; + +	if (!runtime_instr_avail()) +		return -EOPNOTSUPP; + +	if (command == S390_RUNTIME_INSTR_STOP) { +		preempt_disable(); +		exit_thread_runtime_instr(); +		preempt_enable(); +		return 0; +	} + +	if (command != S390_RUNTIME_INSTR_START || +	    (signum < SIGRTMIN || signum > SIGRTMAX)) +		return -EINVAL; + +	if (!current->thread.ri_cb) { +		cb = kzalloc(sizeof(*cb), GFP_KERNEL); +		if (!cb) +			return -ENOMEM; +	} else { +		cb = current->thread.ri_cb; +		memset(cb, 0, sizeof(*cb)); +	} + +	init_runtime_instr_cb(cb); +	current->thread.ri_signum = signum; + +	/* now load the control block to make it available */ +	preempt_disable(); +	current->thread.ri_cb = cb; +	load_runtime_instr_cb(cb); +	preempt_enable(); +	return 0; +} + +static int __init runtime_instr_init(void) +{ +	int rc; + +	if (!runtime_instr_avail()) +		return 0; + +	measurement_alert_subclass_register(); +	rc = register_external_interrupt(0x1407, runtime_instr_int_handler); +	if (rc) +		measurement_alert_subclass_unregister(); +	else +		pr_info("Runtime instrumentation facility initialized\n"); +	return rc; +} +device_initcall(runtime_instr_init); diff --git a/arch/s390/kernel/s390_ksyms.c b/arch/s390/kernel/s390_ksyms.c index 57b536649b0..9bdbcef1da9 100644 --- a/arch/s390/kernel/s390_ksyms.c +++ b/arch/s390/kernel/s390_ksyms.c @@ -8,3 +8,5 @@ EXPORT_SYMBOL(_mcount);  #if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE)  EXPORT_SYMBOL(sie64a);  #endif +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memset); diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 40b57693de3..afa9fdba200 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -302,10 +302,10 @@ static int __init parse_vmalloc(char *arg)  }  early_param("vmalloc", parse_vmalloc); -unsigned int addressing_mode = HOME_SPACE_MODE; -EXPORT_SYMBOL_GPL(addressing_mode); +unsigned int s390_user_mode = PRIMARY_SPACE_MODE; +EXPORT_SYMBOL_GPL(s390_user_mode); -static int set_amode_primary(void) +static void __init set_user_mode_primary(void)  {  	psw_kernel_bits = (psw_kernel_bits & ~PSW_MASK_ASC) | PSW_ASC_HOME;  	psw_user_bits = (psw_user_bits & ~PSW_MASK_ASC) | PSW_ASC_PRIMARY; @@ -313,48 +313,30 @@ static int set_amode_primary(void)  	psw32_user_bits =  		(psw32_user_bits & ~PSW32_MASK_ASC) | PSW32_ASC_PRIMARY;  #endif - -	if (MACHINE_HAS_MVCOS) { -		memcpy(&uaccess, &uaccess_mvcos_switch, sizeof(uaccess)); -		return 1; -	} else { -		memcpy(&uaccess, &uaccess_pt, sizeof(uaccess)); -		return 0; -	} -} - -/* - * Switch kernel/user addressing modes? - */ -static int __init early_parse_switch_amode(char *p) -{ -	addressing_mode = PRIMARY_SPACE_MODE; -	return 0; +	uaccess = MACHINE_HAS_MVCOS ? uaccess_mvcos_switch : uaccess_pt;  } -early_param("switch_amode", early_parse_switch_amode);  static int __init early_parse_user_mode(char *p)  {  	if (p && strcmp(p, "primary") == 0) -		addressing_mode = PRIMARY_SPACE_MODE; +		s390_user_mode = PRIMARY_SPACE_MODE;  	else if (!p || strcmp(p, "home") == 0) -		addressing_mode = HOME_SPACE_MODE; +		s390_user_mode = HOME_SPACE_MODE;  	else  		return 1;  	return 0;  }  early_param("user_mode", early_parse_user_mode); -static void setup_addressing_mode(void) +static void __init setup_addressing_mode(void)  { -	if (addressing_mode == PRIMARY_SPACE_MODE) { -		if (set_amode_primary()) -			pr_info("Address spaces switched, " -				"mvcos available\n"); -		else -			pr_info("Address spaces switched, " -				"mvcos not available\n"); -	} +	if (s390_user_mode != PRIMARY_SPACE_MODE) +		return; +	set_user_mode_primary(); +	if (MACHINE_HAS_MVCOS) +		pr_info("Address spaces switched, mvcos available\n"); +	else +		pr_info("Address spaces switched, mvcos not available\n");  }  void *restart_stack __attribute__((__section__(".data"))); @@ -602,9 +584,7 @@ static void __init setup_memory_end(void)  static void __init setup_vmcoreinfo(void)  { -#ifdef CONFIG_KEXEC  	mem_assign_absolute(S390_lowcore.vmcore_info, paddr_vmcoreinfo_note()); -#endif  }  #ifdef CONFIG_CRASH_DUMP @@ -980,6 +960,12 @@ static void __init setup_hwcaps(void)  	 * HWCAP_S390_HIGH_GPRS is bit 9.  	 */  	elf_hwcap |= HWCAP_S390_HIGH_GPRS; + +	/* +	 * Transactional execution support HWCAP_S390_TE is bit 10. +	 */ +	if (test_facility(50) && test_facility(73)) +		elf_hwcap |= HWCAP_S390_TE;  #endif  	get_cpu_id(&cpu_id); diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 720fda1620f..ea431e551c6 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -66,7 +66,7 @@ struct pcpu {  	unsigned long panic_stack;	/* panic stack for the cpu */  	unsigned long ec_mask;		/* bit mask for ec_xxx functions */  	int state;			/* physical cpu state */ -	u32 status;			/* last status received via sigp */ +	int polarization;		/* physical polarization */  	u16 address;			/* physical cpu address */  }; @@ -74,6 +74,10 @@ static u8 boot_cpu_type;  static u16 boot_cpu_address;  static struct pcpu pcpu_devices[NR_CPUS]; +/* + * The smp_cpu_state_mutex must be held when changing the state or polarization + * member of a pcpu data structure within the pcpu_devices arreay. + */  DEFINE_MUTEX(smp_cpu_state_mutex);  /* @@ -99,7 +103,7 @@ static inline int __pcpu_sigp_relax(u16 addr, u8 order, u32 parm, u32 *status)  	int cc;  	while (1) { -		cc = __pcpu_sigp(addr, order, parm, status); +		cc = __pcpu_sigp(addr, order, parm, NULL);  		if (cc != SIGP_CC_BUSY)  			return cc;  		cpu_relax(); @@ -111,7 +115,7 @@ static int pcpu_sigp_retry(struct pcpu *pcpu, u8 order, u32 parm)  	int cc, retry;  	for (retry = 0; ; retry++) { -		cc = __pcpu_sigp(pcpu->address, order, parm, &pcpu->status); +		cc = __pcpu_sigp(pcpu->address, order, parm, NULL);  		if (cc != SIGP_CC_BUSY)  			break;  		if (retry >= 3) @@ -122,16 +126,18 @@ static int pcpu_sigp_retry(struct pcpu *pcpu, u8 order, u32 parm)  static inline int pcpu_stopped(struct pcpu *pcpu)  { +	u32 uninitialized_var(status); +  	if (__pcpu_sigp(pcpu->address, SIGP_SENSE, -			0, &pcpu->status) != SIGP_CC_STATUS_STORED) +			0, &status) != SIGP_CC_STATUS_STORED)  		return 0; -	return !!(pcpu->status & (SIGP_STATUS_CHECK_STOP|SIGP_STATUS_STOPPED)); +	return !!(status & (SIGP_STATUS_CHECK_STOP|SIGP_STATUS_STOPPED));  }  static inline int pcpu_running(struct pcpu *pcpu)  {  	if (__pcpu_sigp(pcpu->address, SIGP_SENSE_RUNNING, -			0, &pcpu->status) != SIGP_CC_STATUS_STORED) +			0, NULL) != SIGP_CC_STATUS_STORED)  		return 1;  	/* Status stored condition code is equivalent to cpu not running. */  	return 0; @@ -586,6 +592,16 @@ static inline void smp_get_save_area(int cpu, u16 address) { }  #endif /* CONFIG_ZFCPDUMP || CONFIG_CRASH_DUMP */ +void smp_cpu_set_polarization(int cpu, int val) +{ +	pcpu_devices[cpu].polarization = val; +} + +int smp_cpu_get_polarization(int cpu) +{ +	return pcpu_devices[cpu].polarization; +} +  static struct sclp_cpu_info *smp_get_cpu_info(void)  {  	static int use_sigp_detection; @@ -628,7 +644,7 @@ static int __devinit __smp_rescan_cpus(struct sclp_cpu_info *info,  		pcpu->address = info->cpu[i].address;  		pcpu->state = (cpu >= info->configured) ?  			CPU_STATE_STANDBY : CPU_STATE_CONFIGURED; -		cpu_set_polarization(cpu, POLARIZATION_UNKNOWN); +		smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);  		set_cpu_present(cpu, true);  		if (sysfs_add && smp_add_present_cpu(cpu) != 0)  			set_cpu_present(cpu, false); @@ -796,7 +812,7 @@ void __init smp_prepare_boot_cpu(void)  	pcpu->async_stack = S390_lowcore.async_stack - ASYNC_SIZE;  	pcpu->panic_stack = S390_lowcore.panic_stack - PAGE_SIZE;  	S390_lowcore.percpu_offset = __per_cpu_offset[0]; -	cpu_set_polarization(0, POLARIZATION_UNKNOWN); +	smp_cpu_set_polarization(0, POLARIZATION_UNKNOWN);  	set_cpu_present(0, true);  	set_cpu_online(0, true);  } @@ -862,7 +878,7 @@ static ssize_t cpu_configure_store(struct device *dev,  		if (rc)  			break;  		pcpu->state = CPU_STATE_STANDBY; -		cpu_set_polarization(cpu, POLARIZATION_UNKNOWN); +		smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);  		topology_expect_change();  		break;  	case 1: @@ -872,7 +888,7 @@ static ssize_t cpu_configure_store(struct device *dev,  		if (rc)  			break;  		pcpu->state = CPU_STATE_CONFIGURED; -		cpu_set_polarization(cpu, POLARIZATION_UNKNOWN); +		smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);  		topology_expect_change();  		break;  	default: @@ -959,23 +975,17 @@ static int __cpuinit smp_cpu_notify(struct notifier_block *self,  	struct device *s = &c->dev;  	int err = 0; -	switch (action) { +	switch (action & ~CPU_TASKS_FROZEN) {  	case CPU_ONLINE: -	case CPU_ONLINE_FROZEN:  		err = sysfs_create_group(&s->kobj, &cpu_online_attr_group);  		break;  	case CPU_DEAD: -	case CPU_DEAD_FROZEN:  		sysfs_remove_group(&s->kobj, &cpu_online_attr_group);  		break;  	}  	return notifier_from_errno(err);  } -static struct notifier_block __cpuinitdata smp_cpu_nb = { -	.notifier_call = smp_cpu_notify, -}; -  static int __devinit smp_add_present_cpu(int cpu)  {  	struct cpu *c = &pcpu_devices[cpu].cpu; @@ -1050,7 +1060,7 @@ static int __init s390_smp_init(void)  {  	int cpu, rc; -	register_cpu_notifier(&smp_cpu_nb); +	hotcpu_notifier(smp_cpu_notify, 0);  #ifdef CONFIG_HOTPLUG_CPU  	rc = device_create_file(cpu_subsys.dev_root, &dev_attr_rescan);  	if (rc) diff --git a/arch/s390/kernel/syscalls.S b/arch/s390/kernel/syscalls.S index bcab2f04ba5..48174850f3b 100644 --- a/arch/s390/kernel/syscalls.S +++ b/arch/s390/kernel/syscalls.S @@ -350,3 +350,5 @@ SYSCALL(sys_syncfs,sys_syncfs,sys_syncfs_wrapper)  SYSCALL(sys_setns,sys_setns,sys_setns_wrapper)  SYSCALL(sys_process_vm_readv,sys_process_vm_readv,compat_sys_process_vm_readv_wrapper) /* 340 */  SYSCALL(sys_process_vm_writev,sys_process_vm_writev,compat_sys_process_vm_writev_wrapper) +SYSCALL(sys_ni_syscall,sys_s390_runtime_instr,sys_s390_runtime_instr_wrapper) +SYSCALL(sys_kcmp,sys_kcmp,sys_kcmp_wrapper) diff --git a/arch/s390/kernel/sysinfo.c b/arch/s390/kernel/sysinfo.c index fa0eb238dac..62f89d98e88 100644 --- a/arch/s390/kernel/sysinfo.c +++ b/arch/s390/kernel/sysinfo.c @@ -22,17 +22,41 @@  #include <math-emu/soft-fp.h>  #include <math-emu/single.h> -static inline int stsi_0(void) +int topology_max_mnest; + +/* + * stsi - store system information + * + * Returns the current configuration level if function code 0 was specified. + * Otherwise returns 0 on success or a negative value on error. + */ +int stsi(void *sysinfo, int fc, int sel1, int sel2)  { -	int rc = stsi(NULL, 0, 0, 0); -	return rc == -ENOSYS ? rc : (((unsigned int) rc) >> 28); +	register int r0 asm("0") = (fc << 28) | sel1; +	register int r1 asm("1") = sel2; +	int rc = 0; + +	asm volatile( +		"	stsi	0(%3)\n" +		"0:	jz	2f\n" +		"1:	lhi	%1,%4\n" +		"2:\n" +		EX_TABLE(0b, 1b) +		: "+d" (r0), "+d" (rc) +		: "d" (r1), "a" (sysinfo), "K" (-EOPNOTSUPP) +		: "cc", "memory"); +	if (rc) +		return rc; +	return fc ? 0 : ((unsigned int) r0) >> 28;  } +EXPORT_SYMBOL(stsi); -static int stsi_1_1_1(struct sysinfo_1_1_1 *info, char *page, int len) +static void stsi_1_1_1(struct seq_file *m, struct sysinfo_1_1_1 *info)  { -	if (stsi(info, 1, 1, 1) == -ENOSYS) -		return len; +	int i; +	if (stsi(info, 1, 1, 1)) +		return;  	EBCASC(info->manufacturer, sizeof(info->manufacturer));  	EBCASC(info->type, sizeof(info->type));  	EBCASC(info->model, sizeof(info->model)); @@ -41,242 +65,197 @@ static int stsi_1_1_1(struct sysinfo_1_1_1 *info, char *page, int len)  	EBCASC(info->model_capacity, sizeof(info->model_capacity));  	EBCASC(info->model_perm_cap, sizeof(info->model_perm_cap));  	EBCASC(info->model_temp_cap, sizeof(info->model_temp_cap)); -	len += sprintf(page + len, "Manufacturer:         %-16.16s\n", -		       info->manufacturer); -	len += sprintf(page + len, "Type:                 %-4.4s\n", -		       info->type); +	seq_printf(m, "Manufacturer:         %-16.16s\n", info->manufacturer); +	seq_printf(m, "Type:                 %-4.4s\n", info->type); +	/* +	 * Sigh: the model field has been renamed with System z9 +	 * to model_capacity and a new model field has been added +	 * after the plant field. To avoid confusing older programs +	 * the "Model:" prints "model_capacity model" or just +	 * "model_capacity" if the model string is empty . +	 */ +	seq_printf(m, "Model:                %-16.16s", info->model_capacity);  	if (info->model[0] != '\0') -		/* -		 * Sigh: the model field has been renamed with System z9 -		 * to model_capacity and a new model field has been added -		 * after the plant field. To avoid confusing older programs -		 * the "Model:" prints "model_capacity model" or just -		 * "model_capacity" if the model string is empty . -		 */ -		len += sprintf(page + len, -			       "Model:                %-16.16s %-16.16s\n", -			       info->model_capacity, info->model); -	else -		len += sprintf(page + len, "Model:                %-16.16s\n", -			       info->model_capacity); -	len += sprintf(page + len, "Sequence Code:        %-16.16s\n", -		       info->sequence); -	len += sprintf(page + len, "Plant:                %-4.4s\n", -		       info->plant); -	len += sprintf(page + len, "Model Capacity:       %-16.16s %08u\n", -		       info->model_capacity, *(u32 *) info->model_cap_rating); -	if (info->model_perm_cap[0] != '\0') -		len += sprintf(page + len, -			       "Model Perm. Capacity: %-16.16s %08u\n", -			       info->model_perm_cap, -			       *(u32 *) info->model_perm_cap_rating); -	if (info->model_temp_cap[0] != '\0') -		len += sprintf(page + len, -			       "Model Temp. Capacity: %-16.16s %08u\n", -			       info->model_temp_cap, -			       *(u32 *) info->model_temp_cap_rating); +		seq_printf(m, " %-16.16s", info->model); +	seq_putc(m, '\n'); +	seq_printf(m, "Sequence Code:        %-16.16s\n", info->sequence); +	seq_printf(m, "Plant:                %-4.4s\n", info->plant); +	seq_printf(m, "Model Capacity:       %-16.16s %08u\n", +		   info->model_capacity, info->model_cap_rating); +	if (info->model_perm_cap_rating) +		seq_printf(m, "Model Perm. Capacity: %-16.16s %08u\n", +			   info->model_perm_cap, +			   info->model_perm_cap_rating); +	if (info->model_temp_cap_rating) +		seq_printf(m, "Model Temp. Capacity: %-16.16s %08u\n", +			   info->model_temp_cap, +			   info->model_temp_cap_rating); +	if (info->ncr) +		seq_printf(m, "Nominal Cap. Rating:  %08u\n", info->ncr); +	if (info->npr) +		seq_printf(m, "Nominal Perm. Rating: %08u\n", info->npr); +	if (info->ntr) +		seq_printf(m, "Nominal Temp. Rating: %08u\n", info->ntr);  	if (info->cai) { -		len += sprintf(page + len, -			       "Capacity Adj. Ind.:   %d\n", -			       info->cai); -		len += sprintf(page + len, "Capacity Ch. Reason:  %d\n", -			       info->ccr); +		seq_printf(m, "Capacity Adj. Ind.:   %d\n", info->cai); +		seq_printf(m, "Capacity Ch. Reason:  %d\n", info->ccr); +		seq_printf(m, "Capacity Transient:   %d\n", info->t); +	} +	if (info->p) { +		for (i = 1; i <= ARRAY_SIZE(info->typepct); i++) { +			seq_printf(m, "Type %d Percentage:    %d\n", +				   i, info->typepct[i - 1]); +		}  	} -	return len;  } -static int stsi_15_1_x(struct sysinfo_15_1_x *info, char *page, int len) +static void stsi_15_1_x(struct seq_file *m, struct sysinfo_15_1_x *info)  {  	static int max_mnest;  	int i, rc; -	len += sprintf(page + len, "\n"); +	seq_putc(m, '\n');  	if (!MACHINE_HAS_TOPOLOGY) -		return len; -	if (max_mnest) { -		stsi(info, 15, 1, max_mnest); -	} else { -		for (max_mnest = 6; max_mnest > 1; max_mnest--) { -			rc = stsi(info, 15, 1, max_mnest); -			if (rc != -ENOSYS) -				break; -		} -	} -	len += sprintf(page + len, "CPU Topology HW:     "); +		return; +	if (stsi(info, 15, 1, topology_max_mnest)) +		return; +	seq_printf(m, "CPU Topology HW:     ");  	for (i = 0; i < TOPOLOGY_NR_MAG; i++) -		len += sprintf(page + len, " %d", info->mag[i]); -	len += sprintf(page + len, "\n"); +		seq_printf(m, " %d", info->mag[i]); +	seq_putc(m, '\n');  #ifdef CONFIG_SCHED_MC  	store_topology(info); -	len += sprintf(page + len, "CPU Topology SW:     "); +	seq_printf(m, "CPU Topology SW:     ");  	for (i = 0; i < TOPOLOGY_NR_MAG; i++) -		len += sprintf(page + len, " %d", info->mag[i]); -	len += sprintf(page + len, "\n"); +		seq_printf(m, " %d", info->mag[i]); +	seq_putc(m, '\n');  #endif -	return len;  } -static int stsi_1_2_2(struct sysinfo_1_2_2 *info, char *page, int len) +static void stsi_1_2_2(struct seq_file *m, struct sysinfo_1_2_2 *info)  {  	struct sysinfo_1_2_2_extension *ext;  	int i; -	if (stsi(info, 1, 2, 2) == -ENOSYS) -		return len; +	if (stsi(info, 1, 2, 2)) +		return;  	ext = (struct sysinfo_1_2_2_extension *)  		((unsigned long) info + info->acc_offset); - -	len += sprintf(page + len, "CPUs Total:           %d\n", -		       info->cpus_total); -	len += sprintf(page + len, "CPUs Configured:      %d\n", -		       info->cpus_configured); -	len += sprintf(page + len, "CPUs Standby:         %d\n", -		       info->cpus_standby); -	len += sprintf(page + len, "CPUs Reserved:        %d\n", -		       info->cpus_reserved); - -	if (info->format == 1) { -		/* -		 * Sigh 2. According to the specification the alternate -		 * capability field is a 32 bit floating point number -		 * if the higher order 8 bits are not zero. Printing -		 * a floating point number in the kernel is a no-no, -		 * always print the number as 32 bit unsigned integer. -		 * The user-space needs to know about the strange -		 * encoding of the alternate cpu capability. -		 */ -		len += sprintf(page + len, "Capability:           %u %u\n", -			       info->capability, ext->alt_capability); -		for (i = 2; i <= info->cpus_total; i++) -			len += sprintf(page + len, -				       "Adjustment %02d-way:    %u %u\n", -				       i, info->adjustment[i-2], -				       ext->alt_adjustment[i-2]); - -	} else { -		len += sprintf(page + len, "Capability:           %u\n", -			       info->capability); -		for (i = 2; i <= info->cpus_total; i++) -			len += sprintf(page + len, -				       "Adjustment %02d-way:    %u\n", -				       i, info->adjustment[i-2]); +	seq_printf(m, "CPUs Total:           %d\n", info->cpus_total); +	seq_printf(m, "CPUs Configured:      %d\n", info->cpus_configured); +	seq_printf(m, "CPUs Standby:         %d\n", info->cpus_standby); +	seq_printf(m, "CPUs Reserved:        %d\n", info->cpus_reserved); +	/* +	 * Sigh 2. According to the specification the alternate +	 * capability field is a 32 bit floating point number +	 * if the higher order 8 bits are not zero. Printing +	 * a floating point number in the kernel is a no-no, +	 * always print the number as 32 bit unsigned integer. +	 * The user-space needs to know about the strange +	 * encoding of the alternate cpu capability. +	 */ +	seq_printf(m, "Capability:           %u", info->capability); +	if (info->format == 1) +		seq_printf(m, " %u", ext->alt_capability); +	seq_putc(m, '\n'); +	if (info->nominal_cap) +		seq_printf(m, "Nominal Capability:   %d\n", info->nominal_cap); +	if (info->secondary_cap) +		seq_printf(m, "Secondary Capability: %d\n", info->secondary_cap); +	for (i = 2; i <= info->cpus_total; i++) { +		seq_printf(m, "Adjustment %02d-way:    %u", +			   i, info->adjustment[i-2]); +		if (info->format == 1) +			seq_printf(m, " %u", ext->alt_adjustment[i-2]); +		seq_putc(m, '\n');  	} - -	if (info->secondary_capability != 0) -		len += sprintf(page + len, "Secondary Capability: %d\n", -			       info->secondary_capability); -	return len;  } -static int stsi_2_2_2(struct sysinfo_2_2_2 *info, char *page, int len) +static void stsi_2_2_2(struct seq_file *m, struct sysinfo_2_2_2 *info)  { -	if (stsi(info, 2, 2, 2) == -ENOSYS) -		return len; - +	if (stsi(info, 2, 2, 2)) +		return;  	EBCASC(info->name, sizeof(info->name)); - -	len += sprintf(page + len, "\n"); -	len += sprintf(page + len, "LPAR Number:          %d\n", -		       info->lpar_number); - -	len += sprintf(page + len, "LPAR Characteristics: "); +	seq_putc(m, '\n'); +	seq_printf(m, "LPAR Number:          %d\n", info->lpar_number); +	seq_printf(m, "LPAR Characteristics: ");  	if (info->characteristics & LPAR_CHAR_DEDICATED) -		len += sprintf(page + len, "Dedicated "); +		seq_printf(m, "Dedicated ");  	if (info->characteristics & LPAR_CHAR_SHARED) -		len += sprintf(page + len, "Shared "); +		seq_printf(m, "Shared ");  	if (info->characteristics & LPAR_CHAR_LIMITED) -		len += sprintf(page + len, "Limited "); -	len += sprintf(page + len, "\n"); - -	len += sprintf(page + len, "LPAR Name:            %-8.8s\n", -		       info->name); - -	len += sprintf(page + len, "LPAR Adjustment:      %d\n", -		       info->caf); - -	len += sprintf(page + len, "LPAR CPUs Total:      %d\n", -		       info->cpus_total); -	len += sprintf(page + len, "LPAR CPUs Configured: %d\n", -		       info->cpus_configured); -	len += sprintf(page + len, "LPAR CPUs Standby:    %d\n", -		       info->cpus_standby); -	len += sprintf(page + len, "LPAR CPUs Reserved:   %d\n", -		       info->cpus_reserved); -	len += sprintf(page + len, "LPAR CPUs Dedicated:  %d\n", -		       info->cpus_dedicated); -	len += sprintf(page + len, "LPAR CPUs Shared:     %d\n", -		       info->cpus_shared); -	return len; +		seq_printf(m, "Limited "); +	seq_putc(m, '\n'); +	seq_printf(m, "LPAR Name:            %-8.8s\n", info->name); +	seq_printf(m, "LPAR Adjustment:      %d\n", info->caf); +	seq_printf(m, "LPAR CPUs Total:      %d\n", info->cpus_total); +	seq_printf(m, "LPAR CPUs Configured: %d\n", info->cpus_configured); +	seq_printf(m, "LPAR CPUs Standby:    %d\n", info->cpus_standby); +	seq_printf(m, "LPAR CPUs Reserved:   %d\n", info->cpus_reserved); +	seq_printf(m, "LPAR CPUs Dedicated:  %d\n", info->cpus_dedicated); +	seq_printf(m, "LPAR CPUs Shared:     %d\n", info->cpus_shared);  } -static int stsi_3_2_2(struct sysinfo_3_2_2 *info, char *page, int len) +static void stsi_3_2_2(struct seq_file *m, struct sysinfo_3_2_2 *info)  {  	int i; -	if (stsi(info, 3, 2, 2) == -ENOSYS) -		return len; +	if (stsi(info, 3, 2, 2)) +		return;  	for (i = 0; i < info->count; i++) {  		EBCASC(info->vm[i].name, sizeof(info->vm[i].name));  		EBCASC(info->vm[i].cpi, sizeof(info->vm[i].cpi)); -		len += sprintf(page + len, "\n"); -		len += sprintf(page + len, "VM%02d Name:            %-8.8s\n", -			       i, info->vm[i].name); -		len += sprintf(page + len, "VM%02d Control Program: %-16.16s\n", -			       i, info->vm[i].cpi); - -		len += sprintf(page + len, "VM%02d Adjustment:      %d\n", -			       i, info->vm[i].caf); - -		len += sprintf(page + len, "VM%02d CPUs Total:      %d\n", -			       i, info->vm[i].cpus_total); -		len += sprintf(page + len, "VM%02d CPUs Configured: %d\n", -			       i, info->vm[i].cpus_configured); -		len += sprintf(page + len, "VM%02d CPUs Standby:    %d\n", -			       i, info->vm[i].cpus_standby); -		len += sprintf(page + len, "VM%02d CPUs Reserved:   %d\n", -			       i, info->vm[i].cpus_reserved); +		seq_putc(m, '\n'); +		seq_printf(m, "VM%02d Name:            %-8.8s\n", i, info->vm[i].name); +		seq_printf(m, "VM%02d Control Program: %-16.16s\n", i, info->vm[i].cpi); +		seq_printf(m, "VM%02d Adjustment:      %d\n", i, info->vm[i].caf); +		seq_printf(m, "VM%02d CPUs Total:      %d\n", i, info->vm[i].cpus_total); +		seq_printf(m, "VM%02d CPUs Configured: %d\n", i, info->vm[i].cpus_configured); +		seq_printf(m, "VM%02d CPUs Standby:    %d\n", i, info->vm[i].cpus_standby); +		seq_printf(m, "VM%02d CPUs Reserved:   %d\n", i, info->vm[i].cpus_reserved);  	} -	return len;  } -static int proc_read_sysinfo(char *page, char **start, -			     off_t off, int count, -			     int *eof, void *data) +static int sysinfo_show(struct seq_file *m, void *v)  { -	unsigned long info = get_zeroed_page(GFP_KERNEL); -	int level, len; +	void *info = (void *)get_zeroed_page(GFP_KERNEL); +	int level;  	if (!info)  		return 0; - -	len = 0; -	level = stsi_0(); +	level = stsi(NULL, 0, 0, 0);  	if (level >= 1) -		len = stsi_1_1_1((struct sysinfo_1_1_1 *) info, page, len); - +		stsi_1_1_1(m, info);  	if (level >= 1) -		len = stsi_15_1_x((struct sysinfo_15_1_x *) info, page, len); - +		stsi_15_1_x(m, info);  	if (level >= 1) -		len = stsi_1_2_2((struct sysinfo_1_2_2 *) info, page, len); - +		stsi_1_2_2(m, info);  	if (level >= 2) -		len = stsi_2_2_2((struct sysinfo_2_2_2 *) info, page, len); - +		stsi_2_2_2(m, info);  	if (level >= 3) -		len = stsi_3_2_2((struct sysinfo_3_2_2 *) info, page, len); +		stsi_3_2_2(m, info); +	free_page((unsigned long)info); +	return 0; +} -	free_page(info); -	return len; +static int sysinfo_open(struct inode *inode, struct file *file) +{ +	return single_open(file, sysinfo_show, NULL);  } -static __init int create_proc_sysinfo(void) +static const struct file_operations sysinfo_fops = { +	.open		= sysinfo_open, +	.read		= seq_read, +	.llseek		= seq_lseek, +	.release	= single_release, +}; + +static int __init sysinfo_create_proc(void)  { -	create_proc_read_entry("sysinfo", 0444, NULL, -			       proc_read_sysinfo, NULL); +	proc_create("sysinfo", 0444, NULL, &sysinfo_fops);  	return 0;  } -device_initcall(create_proc_sysinfo); +device_initcall(sysinfo_create_proc);  /*   * Service levels interface. @@ -407,7 +386,7 @@ void s390_adjust_jiffies(void)  	if (!info)  		return; -	if (stsi(info, 1, 2, 2) != -ENOSYS) { +	if (stsi(info, 1, 2, 2) == 0) {  		/*  		 * Major sigh. The cpu capability encoding is "special".  		 * If the first 9 bits of info->capability are 0 then it diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index dcec960fc72..2db1011b8b1 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -329,7 +329,7 @@ static unsigned long clock_sync_flags;   * The synchronous get_clock function. It will write the current clock   * value to the clock pointer and return 0 if the clock is in sync with   * the external time source. If the clock mode is local it will return - * -ENOSYS and -EAGAIN if the clock is not in sync with the external + * -EOPNOTSUPP and -EAGAIN if the clock is not in sync with the external   * reference.   */  int get_sync_clock(unsigned long long *clock) @@ -347,7 +347,7 @@ int get_sync_clock(unsigned long long *clock)  		return 0;  	if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags) &&  	    !test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags)) -		return -ENOSYS; +		return -EOPNOTSUPP;  	if (!test_bit(CLOCK_SYNC_ETR, &clock_sync_flags) &&  	    !test_bit(CLOCK_SYNC_STP, &clock_sync_flags))  		return -EACCES; diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c index 05151e06c38..54d93f4b681 100644 --- a/arch/s390/kernel/topology.c +++ b/arch/s390/kernel/topology.c @@ -17,6 +17,7 @@  #include <linux/cpu.h>  #include <linux/smp.h>  #include <linux/mm.h> +#include <asm/sysinfo.h>  #define PTF_HORIZONTAL	(0UL)  #define PTF_VERTICAL	(1UL) @@ -44,9 +45,6 @@ static struct mask_info book_info;  cpumask_t cpu_book_map[NR_CPUS];  unsigned char cpu_book_id[NR_CPUS]; -/* smp_cpu_state_mutex must be held when accessing this array */ -int cpu_polarization[NR_CPUS]; -  static cpumask_t cpu_group_map(struct mask_info *info, unsigned int cpu)  {  	cpumask_t mask; @@ -75,10 +73,7 @@ static struct mask_info *add_cpus_to_mask(struct topology_cpu *tl_cpu,  {  	unsigned int cpu; -	for (cpu = find_first_bit(&tl_cpu->mask[0], TOPOLOGY_CPU_BITS); -	     cpu < TOPOLOGY_CPU_BITS; -	     cpu = find_next_bit(&tl_cpu->mask[0], TOPOLOGY_CPU_BITS, cpu + 1)) -	{ +	for_each_set_bit(cpu, &tl_cpu->mask[0], TOPOLOGY_CPU_BITS) {  		unsigned int rcpu;  		int lcpu; @@ -94,7 +89,7 @@ static struct mask_info *add_cpus_to_mask(struct topology_cpu *tl_cpu,  			} else {  				cpu_core_id[lcpu] = core->id;  			} -			cpu_set_polarization(lcpu, tl_cpu->pp); +			smp_cpu_set_polarization(lcpu, tl_cpu->pp);  		}  	}  	return core; @@ -201,7 +196,7 @@ static void topology_update_polarization_simple(void)  	mutex_lock(&smp_cpu_state_mutex);  	for_each_possible_cpu(cpu) -		cpu_set_polarization(cpu, POLARIZATION_HRZ); +		smp_cpu_set_polarization(cpu, POLARIZATION_HRZ);  	mutex_unlock(&smp_cpu_state_mutex);  } @@ -231,7 +226,7 @@ int topology_set_cpu_management(int fc)  	if (rc)  		return -EBUSY;  	for_each_possible_cpu(cpu) -		cpu_set_polarization(cpu, POLARIZATION_UNKNOWN); +		smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);  	return rc;  } @@ -250,12 +245,10 @@ static void update_cpu_core_map(void)  void store_topology(struct sysinfo_15_1_x *info)  { -	int rc; - -	rc = stsi(info, 15, 1, 3); -	if (rc != -ENOSYS) -		return; -	stsi(info, 15, 1, 2); +	if (topology_max_mnest >= 3) +		stsi(info, 15, 1, 3); +	else +		stsi(info, 15, 1, 2);  }  int arch_update_cpu_topology(void) @@ -415,7 +408,7 @@ static ssize_t cpu_polarization_show(struct device *dev,  	ssize_t count;  	mutex_lock(&smp_cpu_state_mutex); -	switch (cpu_read_polarization(cpu)) { +	switch (smp_cpu_get_polarization(cpu)) {  	case POLARIZATION_HRZ:  		count = sprintf(buf, "horizontal\n");  		break; diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 01775c04a90..3d2b0fa37db 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -57,6 +57,23 @@ static int kstack_depth_to_print = 12;  static int kstack_depth_to_print = 20;  #endif /* CONFIG_64BIT */ +static inline void __user *get_trap_ip(struct pt_regs *regs) +{ +#ifdef CONFIG_64BIT +	unsigned long address; + +	if (regs->int_code & 0x200) +		address = *(unsigned long *)(current->thread.trap_tdb + 24); +	else +		address = regs->psw.addr; +	return (void __user *) +		((address - (regs->int_code >> 16)) & PSW_ADDR_INSN); +#else +	return (void __user *) +		((regs->psw.addr - (regs->int_code >> 16)) & PSW_ADDR_INSN); +#endif +} +  /*   * For show_trace we have tree different stack to consider:   *   - the panic stack which is used if the kernel stack has overflown @@ -214,7 +231,6 @@ void show_registers(struct pt_regs *regs)  void show_regs(struct pt_regs *regs)  { -	print_modules();  	printk("CPU: %d %s %s %.*s\n",  	       task_thread_info(current)->cpu, print_tainted(),  	       init_utsname()->release, @@ -254,6 +270,7 @@ void die(struct pt_regs *regs, const char *str)  #endif  	printk("\n");  	notify_die(DIE_OOPS, str, regs, 0, regs->int_code & 0xffff, SIGSEGV); +	print_modules();  	show_regs(regs);  	bust_spinlocks(0);  	add_taint(TAINT_DIE); @@ -285,12 +302,6 @@ int is_valid_bugaddr(unsigned long addr)  	return 1;  } -static inline void __user *get_psw_address(struct pt_regs *regs) -{ -	return (void __user *) -		((regs->psw.addr - (regs->int_code >> 16)) & PSW_ADDR_INSN); -} -  static void __kprobes do_trap(struct pt_regs *regs,  			      int si_signo, int si_code, char *str)  { @@ -304,14 +315,14 @@ static void __kprobes do_trap(struct pt_regs *regs,  		info.si_signo = si_signo;  		info.si_errno = 0;  		info.si_code = si_code; -		info.si_addr = get_psw_address(regs); +		info.si_addr = get_trap_ip(regs);  		force_sig_info(si_signo, &info, current);  		report_user_fault(regs, si_signo);          } else {                  const struct exception_table_entry *fixup;                  fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);                  if (fixup) -                        regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE; +			regs->psw.addr = extable_fixup(fixup) | PSW_ADDR_AMODE;  		else {  			enum bug_trap_type btt; @@ -381,6 +392,11 @@ DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN,  DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN,  	      "translation exception") +#ifdef CONFIG_64BIT +DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN, +	      "transaction constraint exception") +#endif +  static inline void do_fp_trap(struct pt_regs *regs, int fpc)  {  	int si_code = 0; @@ -408,7 +424,7 @@ static void __kprobes illegal_op(struct pt_regs *regs)  	__u16 __user *location;  	int signal = 0; -	location = get_psw_address(regs); +	location = get_trap_ip(regs);  	if (user_mode(regs)) {  		if (get_user(*((__u16 *) opcode), (__u16 __user *) location)) @@ -476,7 +492,7 @@ void specification_exception(struct pt_regs *regs)  	__u16 __user *location = NULL;  	int signal = 0; -	location = (__u16 __user *) get_psw_address(regs); +	location = (__u16 __user *) get_trap_ip(regs);  	if (user_mode(regs)) {  		get_user(*((__u16 *) opcode), location); @@ -525,7 +541,7 @@ static void data_exception(struct pt_regs *regs)  	__u16 __user *location;  	int signal = 0; -	location = get_psw_address(regs); +	location = get_trap_ip(regs);  	if (MACHINE_HAS_IEEE)  		asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc)); @@ -641,6 +657,7 @@ void __init trap_init(void)          pgm_check_table[0x12] = &translation_exception;          pgm_check_table[0x13] = &special_op_exception;  #ifdef CONFIG_64BIT +	pgm_check_table[0x18] = &transaction_exception;  	pgm_check_table[0x38] = &do_asce_exception;  	pgm_check_table[0x39] = &do_dat_exception;  	pgm_check_table[0x3A] = &do_dat_exception; diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c index 9a19ca367c1..d7776281cb6 100644 --- a/arch/s390/kernel/vdso.c +++ b/arch/s390/kernel/vdso.c @@ -85,7 +85,7 @@ struct vdso_data *vdso_data = &vdso_data_store.data;  static void vdso_init_data(struct vdso_data *vd)  {  	vd->ectg_available = -		addressing_mode != HOME_SPACE_MODE && test_facility(31); +		s390_user_mode != HOME_SPACE_MODE && test_facility(31);  }  #ifdef CONFIG_64BIT @@ -102,7 +102,7 @@ int vdso_alloc_per_cpu(struct _lowcore *lowcore)  	lowcore->vdso_per_cpu_data = __LC_PASTE; -	if (addressing_mode == HOME_SPACE_MODE || !vdso_enabled) +	if (s390_user_mode == HOME_SPACE_MODE || !vdso_enabled)  		return 0;  	segment_table = __get_free_pages(GFP_KERNEL, SEGMENT_ORDER); @@ -147,7 +147,7 @@ void vdso_free_per_cpu(struct _lowcore *lowcore)  	unsigned long segment_table, page_table, page_frame;  	u32 *psal, *aste; -	if (addressing_mode == HOME_SPACE_MODE || !vdso_enabled) +	if (s390_user_mode == HOME_SPACE_MODE || !vdso_enabled)  		return;  	psal = (u32 *)(addr_t) lowcore->paste[4]; @@ -165,7 +165,7 @@ static void vdso_init_cr5(void)  {  	unsigned long cr5; -	if (addressing_mode == HOME_SPACE_MODE || !vdso_enabled) +	if (s390_user_mode == HOME_SPACE_MODE || !vdso_enabled)  		return;  	cr5 = offsetof(struct _lowcore, paste);  	__ctl_load(cr5, 5, 5); diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index 4fc97b40a6e..79033442789 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -99,7 +99,7 @@ static int do_account_vtime(struct task_struct *tsk, int hardirq_offset)  	return virt_timer_forward(user + system);  } -void account_vtime(struct task_struct *prev, struct task_struct *next) +void vtime_task_switch(struct task_struct *prev)  {  	struct thread_info *ti; @@ -107,7 +107,7 @@ void account_vtime(struct task_struct *prev, struct task_struct *next)  	ti = task_thread_info(prev);  	ti->user_timer = S390_lowcore.user_timer;  	ti->system_timer = S390_lowcore.system_timer; -	ti = task_thread_info(next); +	ti = task_thread_info(current);  	S390_lowcore.user_timer = ti->user_timer;  	S390_lowcore.system_timer = ti->system_timer;  } @@ -122,7 +122,7 @@ void account_process_tick(struct task_struct *tsk, int user_tick)   * Update process times based on virtual cpu times stored by entry.S   * to the lowcore fields user_timer, system_timer & steal_clock.   */ -void account_system_vtime(struct task_struct *tsk) +void vtime_account(struct task_struct *tsk)  {  	struct thread_info *ti = task_thread_info(tsk);  	u64 timer, system; @@ -138,7 +138,7 @@ void account_system_vtime(struct task_struct *tsk)  	virt_timer_forward(system);  } -EXPORT_SYMBOL_GPL(account_system_vtime); +EXPORT_SYMBOL_GPL(vtime_account);  void __kprobes vtime_stop_cpu(void)  { @@ -378,9 +378,8 @@ static int __cpuinit s390_nohz_notify(struct notifier_block *self,  	long cpu = (long) hcpu;  	idle = &per_cpu(s390_idle, cpu); -	switch (action) { +	switch (action & ~CPU_TASKS_FROZEN) {  	case CPU_DYING: -	case CPU_DYING_FROZEN:  		idle->nohz_delay = 0;  	default:  		break;  |