aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/char/sclp_cmd.c
diff options
context:
space:
mode:
authorHeiko Carstens <heiko.carstens@de.ibm.com>2008-01-26 08:10:56 -0500
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2008-01-26 08:11:09 -0500
commit08d07968277cd898c88bf12b7720d89c02c4f139 (patch)
tree1c91768976c389883842eb7650141e93b7dbe334 /drivers/s390/char/sclp_cmd.c
parentc05ffc4f2b208da8ba7d3a9b5ab886c76f8939b5 (diff)
[S390] Standby cpu activation/deactivation.
Add a new interface so that cpus can be put into standby state and configured state. Only offline cpus can be put into standby state or configured state. For that the new percpu sysfs attribute "configure" must be used. To put a cpu in standby state a "0" must be written to the attribute. In order to switch it into configured state a "1" must be written to the attribute. Only cpus in configured state can be brought online. In addition this patch introduces a static mapping of physical to logical cpus. As a result only the sysfs directories of present cpus will be created. To scan for new cpus the new sysfs attribute "rescan" must be used. Writing to /sys/devices/system/cpu/rescan will trigger a rescan of cpus and will create directories for new cpus. On IPL only configured cpus will be used. And on reboot/shutdown all cpus will remain in their current state (configured/standby). Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/char/sclp_cmd.c')
-rw-r--r--drivers/s390/char/sclp_cmd.c319
1 files changed, 319 insertions, 0 deletions
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
new file mode 100644
index 000000000000..ba004fd43c05
--- /dev/null
+++ b/drivers/s390/char/sclp_cmd.c
@@ -0,0 +1,319 @@
1/*
2 * drivers/s390/char/sclp_cmd.c
3 *
4 * Copyright IBM Corp. 2007
5 * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
6 */
7
8#include <linux/completion.h>
9#include <linux/init.h>
10#include <linux/errno.h>
11#include <linux/slab.h>
12#include <linux/string.h>
13#include <asm/sclp.h>
14#include "sclp.h"
15
16#define TAG "sclp_cmd: "
17
18#define SCLP_CMDW_READ_SCP_INFO 0x00020001
19#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
20
21struct read_info_sccb {
22 struct sccb_header header; /* 0-7 */
23 u16 rnmax; /* 8-9 */
24 u8 rnsize; /* 10 */
25 u8 _reserved0[24 - 11]; /* 11-15 */
26 u8 loadparm[8]; /* 24-31 */
27 u8 _reserved1[48 - 32]; /* 32-47 */
28 u64 facilities; /* 48-55 */
29 u8 _reserved2[84 - 56]; /* 56-83 */
30 u8 fac84; /* 84 */
31 u8 _reserved3[91 - 85]; /* 85-90 */
32 u8 flags; /* 91 */
33 u8 _reserved4[100 - 92]; /* 92-99 */
34 u32 rnsize2; /* 100-103 */
35 u64 rnmax2; /* 104-111 */
36 u8 _reserved5[4096 - 112]; /* 112-4095 */
37} __attribute__((packed, aligned(PAGE_SIZE)));
38
39static struct read_info_sccb __initdata early_read_info_sccb;
40static int __initdata early_read_info_sccb_valid;
41
42u64 sclp_facilities;
43static u8 sclp_fac84;
44
45static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb)
46{
47 int rc;
48
49 __ctl_set_bit(0, 9);
50 rc = sclp_service_call(cmd, sccb);
51 if (rc)
52 goto out;
53 __load_psw_mask(PSW_BASE_BITS | PSW_MASK_EXT |
54 PSW_MASK_WAIT | PSW_DEFAULT_KEY);
55 local_irq_disable();
56out:
57 /* Contents of the sccb might have changed. */
58 barrier();
59 __ctl_clear_bit(0, 9);
60 return rc;
61}
62
63void __init sclp_read_info_early(void)
64{
65 int rc;
66 int i;
67 struct read_info_sccb *sccb;
68 sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED,
69 SCLP_CMDW_READ_SCP_INFO};
70
71 sccb = &early_read_info_sccb;
72 for (i = 0; i < ARRAY_SIZE(commands); i++) {
73 do {
74 memset(sccb, 0, sizeof(*sccb));
75 sccb->header.length = sizeof(*sccb);
76 sccb->header.control_mask[2] = 0x80;
77 rc = sclp_cmd_sync_early(commands[i], sccb);
78 } while (rc == -EBUSY);
79
80 if (rc)
81 break;
82 if (sccb->header.response_code == 0x10) {
83 early_read_info_sccb_valid = 1;
84 break;
85 }
86 if (sccb->header.response_code != 0x1f0)
87 break;
88 }
89}
90
91void __init sclp_facilities_detect(void)
92{
93 if (!early_read_info_sccb_valid)
94 return;
95 sclp_facilities = early_read_info_sccb.facilities;
96 sclp_fac84 = early_read_info_sccb.fac84;
97}
98
99unsigned long long __init sclp_memory_detect(void)
100{
101 unsigned long long memsize;
102 struct read_info_sccb *sccb;
103
104 if (!early_read_info_sccb_valid)
105 return 0;
106 sccb = &early_read_info_sccb;
107 if (sccb->rnsize)
108 memsize = sccb->rnsize << 20;
109 else
110 memsize = sccb->rnsize2 << 20;
111 if (sccb->rnmax)
112 memsize *= sccb->rnmax;
113 else
114 memsize *= sccb->rnmax2;
115 return memsize;
116}
117
118/*
119 * This function will be called after sclp_memory_detect(), which gets called
120 * early from early.c code. Therefore the sccb should have valid contents.
121 */
122void __init sclp_get_ipl_info(struct sclp_ipl_info *info)
123{
124 struct read_info_sccb *sccb;
125
126 if (!early_read_info_sccb_valid)
127 return;
128 sccb = &early_read_info_sccb;
129 info->is_valid = 1;
130 if (sccb->flags & 0x2)
131 info->has_dump = 1;
132 memcpy(&info->loadparm, &sccb->loadparm, LOADPARM_LEN);
133}
134
135static void sclp_sync_callback(struct sclp_req *req, void *data)
136{
137 struct completion *completion = data;
138
139 complete(completion);
140}
141
142static int do_sync_request(sclp_cmdw_t cmd, void *sccb)
143{
144 struct completion completion;
145 struct sclp_req *request;
146 int rc;
147
148 request = kzalloc(sizeof(*request), GFP_KERNEL);
149 if (!request)
150 return -ENOMEM;
151 request->command = cmd;
152 request->sccb = sccb;
153 request->status = SCLP_REQ_FILLED;
154 request->callback = sclp_sync_callback;
155 request->callback_data = &completion;
156 init_completion(&completion);
157
158 /* Perform sclp request. */
159 rc = sclp_add_request(request);
160 if (rc)
161 goto out;
162 wait_for_completion(&completion);
163
164 /* Check response. */
165 if (request->status != SCLP_REQ_DONE) {
166 printk(KERN_WARNING TAG "sync request failed "
167 "(cmd=0x%08x, status=0x%02x)\n", cmd, request->status);
168 rc = -EIO;
169 }
170out:
171 kfree(request);
172 return rc;
173}
174
175/*
176 * CPU configuration related functions.
177 */
178
179#define SCLP_CMDW_READ_CPU_INFO 0x00010001
180#define SCLP_CMDW_CONFIGURE_CPU 0x00110001
181#define SCLP_CMDW_DECONFIGURE_CPU 0x00100001
182
183struct read_cpu_info_sccb {
184 struct sccb_header header;
185 u16 nr_configured;
186 u16 offset_configured;
187 u16 nr_standby;
188 u16 offset_standby;
189 u8 reserved[4096 - 16];
190} __attribute__((packed, aligned(PAGE_SIZE)));
191
192static struct read_cpu_info_sccb __initdata early_read_cpu_info_sccb;
193static struct sclp_cpu_info __initdata sclp_cpu_info;
194
195static void sclp_fill_cpu_info(struct sclp_cpu_info *info,
196 struct read_cpu_info_sccb *sccb)
197{
198 char *page = (char *) sccb;
199
200 memset(info, 0, sizeof(*info));
201 info->configured = sccb->nr_configured;
202 info->standby = sccb->nr_standby;
203 info->combined = sccb->nr_configured + sccb->nr_standby;
204 info->has_cpu_type = sclp_fac84 & 0x1;
205 memcpy(&info->cpu, page + sccb->offset_configured,
206 info->combined * sizeof(struct sclp_cpu_entry));
207}
208
209void __init sclp_read_cpu_info_early(void)
210{
211 int rc;
212 struct read_cpu_info_sccb *sccb;
213
214 if (!SCLP_HAS_CPU_INFO)
215 return;
216
217 sccb = &early_read_cpu_info_sccb;
218 do {
219 memset(sccb, 0, sizeof(*sccb));
220 sccb->header.length = sizeof(*sccb);
221 rc = sclp_cmd_sync_early(SCLP_CMDW_READ_CPU_INFO, sccb);
222 } while (rc == -EBUSY);
223
224 if (rc)
225 return;
226 if (sccb->header.response_code != 0x10)
227 return;
228 sclp_fill_cpu_info(&sclp_cpu_info, sccb);
229}
230
231static int __init sclp_get_cpu_info_early(struct sclp_cpu_info *info)
232{
233 if (!SCLP_HAS_CPU_INFO)
234 return -EOPNOTSUPP;
235 *info = sclp_cpu_info;
236 return 0;
237}
238
239static int sclp_get_cpu_info_late(struct sclp_cpu_info *info)
240{
241 int rc;
242 struct read_cpu_info_sccb *sccb;
243
244 if (!SCLP_HAS_CPU_INFO)
245 return -EOPNOTSUPP;
246 sccb = (struct read_cpu_info_sccb *) __get_free_page(GFP_KERNEL
247 | GFP_DMA);
248 if (!sccb)
249 return -ENOMEM;
250 memset(sccb, 0, sizeof(*sccb));
251 sccb->header.length = sizeof(*sccb);
252 rc = do_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb);
253 if (rc)
254 goto out;
255 if (sccb->header.response_code != 0x0010) {
256 printk(KERN_WARNING TAG "readcpuinfo failed "
257 "(response=0x%04x)\n", sccb->header.response_code);
258 rc = -EIO;
259 goto out;
260 }
261 sclp_fill_cpu_info(info, sccb);
262out:
263 free_page((unsigned long) sccb);
264 return rc;
265}
266
267int __init_refok sclp_get_cpu_info(struct sclp_cpu_info *info)
268{
269 if (slab_is_available())
270 return sclp_get_cpu_info_late(info);
271 return sclp_get_cpu_info_early(info);
272}
273
274struct cpu_configure_sccb {
275 struct sccb_header header;
276} __attribute__((packed, aligned(8)));
277
278static int do_cpu_configure(sclp_cmdw_t cmd)
279{
280 struct cpu_configure_sccb *sccb;
281 int rc;
282
283 if (!SCLP_HAS_CPU_RECONFIG)
284 return -EOPNOTSUPP;
285 /*
286 * This is not going to cross a page boundary since we force
287 * kmalloc to have a minimum alignment of 8 bytes on s390.
288 */
289 sccb = kzalloc(sizeof(*sccb), GFP_KERNEL | GFP_DMA);
290 if (!sccb)
291 return -ENOMEM;
292 sccb->header.length = sizeof(*sccb);
293 rc = do_sync_request(cmd, sccb);
294 if (rc)
295 goto out;
296 switch (sccb->header.response_code) {
297 case 0x0020:
298 case 0x0120:
299 break;
300 default:
301 printk(KERN_WARNING TAG "configure cpu failed (cmd=0x%08x, "
302 "response=0x%04x)\n", cmd, sccb->header.response_code);
303 rc = -EIO;
304 break;
305 }
306out:
307 kfree(sccb);
308 return rc;
309}
310
311int sclp_cpu_configure(u8 cpu)
312{
313 return do_cpu_configure(SCLP_CMDW_CONFIGURE_CPU | cpu << 8);
314}
315
316int sclp_cpu_deconfigure(u8 cpu)
317{
318 return do_cpu_configure(SCLP_CMDW_DECONFIGURE_CPU | cpu << 8);
319}