Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * drivers/s390/char/sclp_quiesce.c | |
3 | * signal quiesce handler | |
4 | * | |
5 | * (C) Copyright IBM Corp. 1999,2004 | |
6 | * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> | |
7 | * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | |
8 | */ | |
9 | ||
10 | #include <linux/config.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/types.h> | |
13 | #include <linux/cpumask.h> | |
14 | #include <linux/smp.h> | |
15 | #include <linux/init.h> | |
16 | #include <asm/atomic.h> | |
17 | #include <asm/ptrace.h> | |
18 | #include <asm/sigp.h> | |
19 | ||
20 | #include "sclp.h" | |
21 | ||
22 | ||
23 | #ifdef CONFIG_SMP | |
24 | /* Signal completion of shutdown process. All CPUs except the first to enter | |
25 | * this function: go to stopped state. First CPU: wait until all other | |
26 | * CPUs are in stopped or check stop state. Afterwards, load special PSW | |
27 | * to indicate completion. */ | |
28 | static void | |
29 | do_load_quiesce_psw(void * __unused) | |
30 | { | |
31 | static atomic_t cpuid = ATOMIC_INIT(-1); | |
32 | psw_t quiesce_psw; | |
33 | int cpu; | |
34 | ||
973bd993 | 35 | if (atomic_cmpxchg(&cpuid, -1, smp_processor_id()) != -1) |
1da177e4 LT |
36 | signal_processor(smp_processor_id(), sigp_stop); |
37 | /* Wait for all other cpus to enter stopped state */ | |
38 | for_each_online_cpu(cpu) { | |
39 | if (cpu == smp_processor_id()) | |
40 | continue; | |
41 | while(!smp_cpu_not_running(cpu)) | |
42 | cpu_relax(); | |
43 | } | |
44 | /* Quiesce the last cpu with the special psw */ | |
45 | quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT; | |
46 | quiesce_psw.addr = 0xfff; | |
47 | __load_psw(quiesce_psw); | |
48 | } | |
49 | ||
50 | /* Shutdown handler. Perform shutdown function on all CPUs. */ | |
51 | static void | |
52 | do_machine_quiesce(void) | |
53 | { | |
54 | on_each_cpu(do_load_quiesce_psw, NULL, 0, 0); | |
55 | } | |
56 | #else | |
57 | /* Shutdown handler. Signal completion of shutdown by loading special PSW. */ | |
58 | static void | |
59 | do_machine_quiesce(void) | |
60 | { | |
61 | psw_t quiesce_psw; | |
62 | ||
63 | quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT; | |
64 | quiesce_psw.addr = 0xfff; | |
65 | __load_psw(quiesce_psw); | |
66 | } | |
67 | #endif | |
68 | ||
69 | extern void ctrl_alt_del(void); | |
70 | ||
71 | /* Handler for quiesce event. Start shutdown procedure. */ | |
72 | static void | |
73 | sclp_quiesce_handler(struct evbuf_header *evbuf) | |
74 | { | |
75 | _machine_restart = (void *) do_machine_quiesce; | |
76 | _machine_halt = do_machine_quiesce; | |
77 | _machine_power_off = do_machine_quiesce; | |
78 | ctrl_alt_del(); | |
79 | } | |
80 | ||
81 | static struct sclp_register sclp_quiesce_event = { | |
82 | .receive_mask = EvTyp_SigQuiesce_Mask, | |
83 | .receiver_fn = sclp_quiesce_handler | |
84 | }; | |
85 | ||
86 | /* Initialize quiesce driver. */ | |
87 | static int __init | |
88 | sclp_quiesce_init(void) | |
89 | { | |
90 | int rc; | |
91 | ||
92 | rc = sclp_register(&sclp_quiesce_event); | |
93 | if (rc) | |
94 | printk(KERN_WARNING "sclp: could not register quiesce handler " | |
95 | "(rc=%d)\n", rc); | |
96 | return rc; | |
97 | } | |
98 | ||
99 | module_init(sclp_quiesce_init); |