diff options
Diffstat (limited to 'arch/s390')
-rw-r--r-- | arch/s390/include/asm/smp.h | 12 | ||||
-rw-r--r-- | arch/s390/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/s390/kernel/ipl.c | 31 | ||||
-rw-r--r-- | arch/s390/kernel/machine_kexec.c | 10 | ||||
-rw-r--r-- | arch/s390/kernel/smp.c | 36 | ||||
-rw-r--r-- | arch/s390/kernel/switch_cpu.S | 59 | ||||
-rw-r--r-- | arch/s390/kernel/switch_cpu64.S | 52 |
7 files changed, 191 insertions, 11 deletions
diff --git a/arch/s390/include/asm/smp.h b/arch/s390/include/asm/smp.h index 9d2acb0b4847..c2d0e638f892 100644 --- a/arch/s390/include/asm/smp.h +++ b/arch/s390/include/asm/smp.h | |||
@@ -31,6 +31,18 @@ extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); | |||
31 | 31 | ||
32 | extern struct save_area *zfcpdump_save_areas[NR_CPUS + 1]; | 32 | extern struct save_area *zfcpdump_save_areas[NR_CPUS + 1]; |
33 | 33 | ||
34 | extern void smp_switch_to_ipl_cpu(void (*func)(void *), void *); | ||
35 | extern void smp_switch_to_cpu(void (*)(void *), void *, unsigned long sp, | ||
36 | int from, int to); | ||
37 | extern void smp_restart_cpu(void); | ||
38 | |||
39 | #else /* CONFIG_SMP */ | ||
40 | |||
41 | static inline void smp_switch_to_ipl_cpu(void (*func)(void *), void *data) | ||
42 | { | ||
43 | func(data); | ||
44 | } | ||
45 | |||
34 | #endif /* CONFIG_SMP */ | 46 | #endif /* CONFIG_SMP */ |
35 | 47 | ||
36 | #ifdef CONFIG_HOTPLUG_CPU | 48 | #ifdef CONFIG_HOTPLUG_CPU |
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 683f6381cc59..20f861256774 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile | |||
@@ -32,6 +32,8 @@ extra-y += head.o init_task.o vmlinux.lds | |||
32 | 32 | ||
33 | obj-$(CONFIG_MODULES) += s390_ksyms.o module.o | 33 | obj-$(CONFIG_MODULES) += s390_ksyms.o module.o |
34 | obj-$(CONFIG_SMP) += smp.o topology.o | 34 | obj-$(CONFIG_SMP) += smp.o topology.o |
35 | obj-$(CONFIG_SMP) += $(if $(CONFIG_64BIT),switch_cpu64.o, \ | ||
36 | switch_cpu.o) | ||
35 | obj-$(CONFIG_HIBERNATION) += suspend.o swsusp_asm64.o | 37 | obj-$(CONFIG_HIBERNATION) += suspend.o swsusp_asm64.o |
36 | obj-$(CONFIG_AUDIT) += audit.o | 38 | obj-$(CONFIG_AUDIT) += audit.o |
37 | compat-obj-$(CONFIG_AUDIT) += compat_audit.o | 39 | compat-obj-$(CONFIG_AUDIT) += compat_audit.o |
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 4d73296fed74..0a7c39dee6b7 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c | |||
@@ -553,7 +553,7 @@ out: | |||
553 | return rc; | 553 | return rc; |
554 | } | 554 | } |
555 | 555 | ||
556 | static void ipl_run(struct shutdown_trigger *trigger) | 556 | static void __ipl_run(void *unused) |
557 | { | 557 | { |
558 | diag308(DIAG308_IPL, NULL); | 558 | diag308(DIAG308_IPL, NULL); |
559 | if (MACHINE_IS_VM) | 559 | if (MACHINE_IS_VM) |
@@ -562,6 +562,11 @@ static void ipl_run(struct shutdown_trigger *trigger) | |||
562 | reipl_ccw_dev(&ipl_info.data.ccw.dev_id); | 562 | reipl_ccw_dev(&ipl_info.data.ccw.dev_id); |
563 | } | 563 | } |
564 | 564 | ||
565 | static void ipl_run(struct shutdown_trigger *trigger) | ||
566 | { | ||
567 | smp_switch_to_ipl_cpu(__ipl_run, NULL); | ||
568 | } | ||
569 | |||
565 | static int __init ipl_init(void) | 570 | static int __init ipl_init(void) |
566 | { | 571 | { |
567 | int rc; | 572 | int rc; |
@@ -1039,7 +1044,7 @@ static void get_ipl_string(char *dst, struct ipl_parameter_block *ipb, | |||
1039 | sprintf(dst + pos, " PARM %s", vmparm); | 1044 | sprintf(dst + pos, " PARM %s", vmparm); |
1040 | } | 1045 | } |
1041 | 1046 | ||
1042 | static void reipl_run(struct shutdown_trigger *trigger) | 1047 | static void __reipl_run(void *unused) |
1043 | { | 1048 | { |
1044 | struct ccw_dev_id devid; | 1049 | struct ccw_dev_id devid; |
1045 | static char buf[128]; | 1050 | static char buf[128]; |
@@ -1087,6 +1092,11 @@ static void reipl_run(struct shutdown_trigger *trigger) | |||
1087 | disabled_wait((unsigned long) __builtin_return_address(0)); | 1092 | disabled_wait((unsigned long) __builtin_return_address(0)); |
1088 | } | 1093 | } |
1089 | 1094 | ||
1095 | static void reipl_run(struct shutdown_trigger *trigger) | ||
1096 | { | ||
1097 | smp_switch_to_ipl_cpu(__reipl_run, NULL); | ||
1098 | } | ||
1099 | |||
1090 | static void reipl_block_ccw_init(struct ipl_parameter_block *ipb) | 1100 | static void reipl_block_ccw_init(struct ipl_parameter_block *ipb) |
1091 | { | 1101 | { |
1092 | ipb->hdr.len = IPL_PARM_BLK_CCW_LEN; | 1102 | ipb->hdr.len = IPL_PARM_BLK_CCW_LEN; |
@@ -1369,20 +1379,18 @@ static struct kobj_attribute dump_type_attr = | |||
1369 | 1379 | ||
1370 | static struct kset *dump_kset; | 1380 | static struct kset *dump_kset; |
1371 | 1381 | ||
1372 | static void dump_run(struct shutdown_trigger *trigger) | 1382 | static void __dump_run(void *unused) |
1373 | { | 1383 | { |
1374 | struct ccw_dev_id devid; | 1384 | struct ccw_dev_id devid; |
1375 | static char buf[100]; | 1385 | static char buf[100]; |
1376 | 1386 | ||
1377 | switch (dump_method) { | 1387 | switch (dump_method) { |
1378 | case DUMP_METHOD_CCW_CIO: | 1388 | case DUMP_METHOD_CCW_CIO: |
1379 | smp_send_stop(); | ||
1380 | devid.devno = dump_block_ccw->ipl_info.ccw.devno; | 1389 | devid.devno = dump_block_ccw->ipl_info.ccw.devno; |
1381 | devid.ssid = 0; | 1390 | devid.ssid = 0; |
1382 | reipl_ccw_dev(&devid); | 1391 | reipl_ccw_dev(&devid); |
1383 | break; | 1392 | break; |
1384 | case DUMP_METHOD_CCW_VM: | 1393 | case DUMP_METHOD_CCW_VM: |
1385 | smp_send_stop(); | ||
1386 | sprintf(buf, "STORE STATUS"); | 1394 | sprintf(buf, "STORE STATUS"); |
1387 | __cpcmd(buf, NULL, 0, NULL); | 1395 | __cpcmd(buf, NULL, 0, NULL); |
1388 | sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno); | 1396 | sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno); |
@@ -1396,10 +1404,17 @@ static void dump_run(struct shutdown_trigger *trigger) | |||
1396 | diag308(DIAG308_SET, dump_block_fcp); | 1404 | diag308(DIAG308_SET, dump_block_fcp); |
1397 | diag308(DIAG308_DUMP, NULL); | 1405 | diag308(DIAG308_DUMP, NULL); |
1398 | break; | 1406 | break; |
1399 | case DUMP_METHOD_NONE: | 1407 | default: |
1400 | return; | 1408 | break; |
1401 | } | 1409 | } |
1402 | printk(KERN_EMERG "Dump failed!\n"); | 1410 | } |
1411 | |||
1412 | static void dump_run(struct shutdown_trigger *trigger) | ||
1413 | { | ||
1414 | if (dump_method == DUMP_METHOD_NONE) | ||
1415 | return; | ||
1416 | smp_send_stop(); | ||
1417 | smp_switch_to_ipl_cpu(__dump_run, NULL); | ||
1403 | } | 1418 | } |
1404 | 1419 | ||
1405 | static int __init dump_ccw_init(void) | 1420 | static int __init dump_ccw_init(void) |
diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c index 131d7ee8b416..a922d51df6bf 100644 --- a/arch/s390/kernel/machine_kexec.c +++ b/arch/s390/kernel/machine_kexec.c | |||
@@ -54,11 +54,11 @@ void machine_shutdown(void) | |||
54 | { | 54 | { |
55 | } | 55 | } |
56 | 56 | ||
57 | void machine_kexec(struct kimage *image) | 57 | static void __machine_kexec(void *data) |
58 | { | 58 | { |
59 | relocate_kernel_t data_mover; | 59 | relocate_kernel_t data_mover; |
60 | struct kimage *image = data; | ||
60 | 61 | ||
61 | smp_send_stop(); | ||
62 | pfault_fini(); | 62 | pfault_fini(); |
63 | s390_reset_system(); | 63 | s390_reset_system(); |
64 | 64 | ||
@@ -68,3 +68,9 @@ void machine_kexec(struct kimage *image) | |||
68 | (*data_mover)(&image->head, image->start); | 68 | (*data_mover)(&image->head, image->start); |
69 | for (;;); | 69 | for (;;); |
70 | } | 70 | } |
71 | |||
72 | void machine_kexec(struct kimage *image) | ||
73 | { | ||
74 | smp_send_stop(); | ||
75 | smp_switch_to_ipl_cpu(__machine_kexec, image); | ||
76 | } | ||
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 384a6846a65e..b39f596d71bd 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c | |||
@@ -90,6 +90,39 @@ static int cpu_stopped(int cpu) | |||
90 | return 0; | 90 | return 0; |
91 | } | 91 | } |
92 | 92 | ||
93 | void smp_switch_to_ipl_cpu(void (*func)(void *), void *data) | ||
94 | { | ||
95 | struct _lowcore *lc, *current_lc; | ||
96 | struct stack_frame *sf; | ||
97 | struct pt_regs *regs; | ||
98 | unsigned long sp; | ||
99 | |||
100 | if (smp_processor_id() == 0) | ||
101 | func(data); | ||
102 | __load_psw_mask(PSW_BASE_BITS | PSW_DEFAULT_KEY); | ||
103 | /* Disable lowcore protection */ | ||
104 | __ctl_clear_bit(0, 28); | ||
105 | current_lc = lowcore_ptr[smp_processor_id()]; | ||
106 | lc = lowcore_ptr[0]; | ||
107 | if (!lc) | ||
108 | lc = current_lc; | ||
109 | lc->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY; | ||
110 | lc->restart_psw.addr = PSW_ADDR_AMODE | (unsigned long) smp_restart_cpu; | ||
111 | if (!cpu_online(0)) | ||
112 | smp_switch_to_cpu(func, data, 0, stap(), __cpu_logical_map[0]); | ||
113 | while (signal_processor(0, sigp_stop_and_store_status) == sigp_busy) | ||
114 | cpu_relax(); | ||
115 | sp = lc->panic_stack; | ||
116 | sp -= sizeof(struct pt_regs); | ||
117 | regs = (struct pt_regs *) sp; | ||
118 | memcpy(®s->gprs, ¤t_lc->gpregs_save_area, sizeof(regs->gprs)); | ||
119 | memcpy(®s->psw, ¤t_lc->st_status_fixed_logout, sizeof(psw_t)); | ||
120 | sp -= STACK_FRAME_OVERHEAD; | ||
121 | sf = (struct stack_frame *) sp; | ||
122 | sf->back_chain = regs->gprs[15]; | ||
123 | smp_switch_to_cpu(func, data, sp, stap(), __cpu_logical_map[0]); | ||
124 | } | ||
125 | |||
93 | void smp_send_stop(void) | 126 | void smp_send_stop(void) |
94 | { | 127 | { |
95 | int cpu, rc; | 128 | int cpu, rc; |
@@ -752,7 +785,8 @@ static ssize_t cpu_configure_store(struct sys_device *dev, | |||
752 | get_online_cpus(); | 785 | get_online_cpus(); |
753 | mutex_lock(&smp_cpu_state_mutex); | 786 | mutex_lock(&smp_cpu_state_mutex); |
754 | rc = -EBUSY; | 787 | rc = -EBUSY; |
755 | if (cpu_online(cpu)) | 788 | /* disallow configuration changes of online cpus and cpu 0 */ |
789 | if (cpu_online(cpu) || cpu == 0) | ||
756 | goto out; | 790 | goto out; |
757 | rc = 0; | 791 | rc = 0; |
758 | switch (val) { | 792 | switch (val) { |
diff --git a/arch/s390/kernel/switch_cpu.S b/arch/s390/kernel/switch_cpu.S new file mode 100644 index 000000000000..c05ee6c221d8 --- /dev/null +++ b/arch/s390/kernel/switch_cpu.S | |||
@@ -0,0 +1,59 @@ | |||
1 | /* | ||
2 | * 31-bit switch cpu code | ||
3 | * | ||
4 | * Copyright IBM Corp. 2009 | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #include <asm/asm-offsets.h> | ||
9 | #include <asm/lowcore.h> | ||
10 | #include <asm/ptrace.h> | ||
11 | |||
12 | # smp_switch_to_cpu switches to destination cpu and executes the passed function | ||
13 | # Parameter: %r2 - function to call | ||
14 | # %r3 - function parameter | ||
15 | # %r4 - stack poiner | ||
16 | # %r5 - current cpu | ||
17 | # %r6 - destination cpu | ||
18 | |||
19 | .section .text | ||
20 | .align 4 | ||
21 | .globl smp_switch_to_cpu | ||
22 | smp_switch_to_cpu: | ||
23 | stm %r6,%r15,__SF_GPRS(%r15) | ||
24 | lr %r1,%r15 | ||
25 | ahi %r15,-STACK_FRAME_OVERHEAD | ||
26 | st %r1,__SF_BACKCHAIN(%r15) | ||
27 | basr %r13,0 | ||
28 | 0: la %r1,.gprregs_addr-0b(%r13) | ||
29 | l %r1,0(%r1) | ||
30 | stm %r0,%r15,0(%r1) | ||
31 | 1: sigp %r0,%r6,__SIGP_RESTART /* start destination CPU */ | ||
32 | brc 2,1b /* busy, try again */ | ||
33 | 2: sigp %r0,%r5,__SIGP_STOP /* stop current CPU */ | ||
34 | brc 2,2b /* busy, try again */ | ||
35 | 3: j 3b | ||
36 | |||
37 | .globl smp_restart_cpu | ||
38 | smp_restart_cpu: | ||
39 | basr %r13,0 | ||
40 | 0: la %r1,.gprregs_addr-0b(%r13) | ||
41 | l %r1,0(%r1) | ||
42 | lm %r0,%r15,0(%r1) | ||
43 | 1: sigp %r0,%r5,__SIGP_SENSE /* Wait for calling CPU */ | ||
44 | brc 10,1b /* busy, accepted (status 0), running */ | ||
45 | tmll %r0,0x40 /* Test if calling CPU is stopped */ | ||
46 | jz 1b | ||
47 | ltr %r4,%r4 /* New stack ? */ | ||
48 | jz 1f | ||
49 | lr %r15,%r4 | ||
50 | 1: basr %r14,%r2 | ||
51 | |||
52 | .gprregs_addr: | ||
53 | .long .gprregs | ||
54 | |||
55 | .section .data,"aw",@progbits | ||
56 | .gprregs: | ||
57 | .rept 16 | ||
58 | .long 0 | ||
59 | .endr | ||
diff --git a/arch/s390/kernel/switch_cpu64.S b/arch/s390/kernel/switch_cpu64.S new file mode 100644 index 000000000000..c73ede3c1d68 --- /dev/null +++ b/arch/s390/kernel/switch_cpu64.S | |||
@@ -0,0 +1,52 @@ | |||
1 | /* | ||
2 | * 64-bit switch cpu code | ||
3 | * | ||
4 | * Copyright IBM Corp. 2009 | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #include <asm/asm-offsets.h> | ||
9 | #include <asm/lowcore.h> | ||
10 | #include <asm/ptrace.h> | ||
11 | |||
12 | # smp_switch_to_cpu switches to destination cpu and executes the passed function | ||
13 | # Parameter: %r2 - function to call | ||
14 | # %r3 - function parameter | ||
15 | # %r4 - stack poiner | ||
16 | # %r5 - current cpu | ||
17 | # %r6 - destination cpu | ||
18 | |||
19 | .section .text | ||
20 | .align 4 | ||
21 | .globl smp_switch_to_cpu | ||
22 | smp_switch_to_cpu: | ||
23 | stmg %r6,%r15,__SF_GPRS(%r15) | ||
24 | lgr %r1,%r15 | ||
25 | aghi %r15,-STACK_FRAME_OVERHEAD | ||
26 | stg %r1,__SF_BACKCHAIN(%r15) | ||
27 | larl %r1,.gprregs | ||
28 | stmg %r0,%r15,0(%r1) | ||
29 | 1: sigp %r0,%r6,__SIGP_RESTART /* start destination CPU */ | ||
30 | brc 2,1b /* busy, try again */ | ||
31 | 2: sigp %r0,%r5,__SIGP_STOP /* stop current CPU */ | ||
32 | brc 2,2b /* busy, try again */ | ||
33 | 3: j 3b | ||
34 | |||
35 | .globl smp_restart_cpu | ||
36 | smp_restart_cpu: | ||
37 | larl %r1,.gprregs | ||
38 | lmg %r0,%r15,0(%r1) | ||
39 | 1: sigp %r0,%r5,__SIGP_SENSE /* Wait for calling CPU */ | ||
40 | brc 10,1b /* busy, accepted (status 0), running */ | ||
41 | tmll %r0,0x40 /* Test if calling CPU is stopped */ | ||
42 | jz 1b | ||
43 | ltgr %r4,%r4 /* New stack ? */ | ||
44 | jz 1f | ||
45 | lgr %r15,%r4 | ||
46 | 1: basr %r14,%r2 | ||
47 | |||
48 | .section .data,"aw",@progbits | ||
49 | .gprregs: | ||
50 | .rept 16 | ||
51 | .quad 0 | ||
52 | .endr | ||