diff options
Diffstat (limited to 'drivers/s390/cio/chsc.c')
-rw-r--r-- | drivers/s390/cio/chsc.c | 379 |
1 files changed, 150 insertions, 229 deletions
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 5de86908b0d0..65264a38057d 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c | |||
@@ -2,8 +2,7 @@ | |||
2 | * drivers/s390/cio/chsc.c | 2 | * drivers/s390/cio/chsc.c |
3 | * S/390 common I/O routines -- channel subsystem call | 3 | * S/390 common I/O routines -- channel subsystem call |
4 | * | 4 | * |
5 | * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, | 5 | * Copyright IBM Corp. 1999,2008 |
6 | * IBM Corporation | ||
7 | * Author(s): Ingo Adlung (adlung@de.ibm.com) | 6 | * Author(s): Ingo Adlung (adlung@de.ibm.com) |
8 | * Cornelia Huck (cornelia.huck@de.ibm.com) | 7 | * Cornelia Huck (cornelia.huck@de.ibm.com) |
9 | * Arnd Bergmann (arndb@de.ibm.com) | 8 | * Arnd Bergmann (arndb@de.ibm.com) |
@@ -16,7 +15,9 @@ | |||
16 | 15 | ||
17 | #include <asm/cio.h> | 16 | #include <asm/cio.h> |
18 | #include <asm/chpid.h> | 17 | #include <asm/chpid.h> |
18 | #include <asm/chsc.h> | ||
19 | 19 | ||
20 | #include "../s390mach.h" | ||
20 | #include "css.h" | 21 | #include "css.h" |
21 | #include "cio.h" | 22 | #include "cio.h" |
22 | #include "cio_debug.h" | 23 | #include "cio_debug.h" |
@@ -127,77 +128,12 @@ out_free: | |||
127 | return ret; | 128 | return ret; |
128 | } | 129 | } |
129 | 130 | ||
130 | static int check_for_io_on_path(struct subchannel *sch, int mask) | ||
131 | { | ||
132 | int cc; | ||
133 | |||
134 | cc = stsch(sch->schid, &sch->schib); | ||
135 | if (cc) | ||
136 | return 0; | ||
137 | if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask) | ||
138 | return 1; | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static void terminate_internal_io(struct subchannel *sch) | ||
143 | { | ||
144 | if (cio_clear(sch)) { | ||
145 | /* Recheck device in case clear failed. */ | ||
146 | sch->lpm = 0; | ||
147 | if (device_trigger_verify(sch) != 0) | ||
148 | css_schedule_eval(sch->schid); | ||
149 | return; | ||
150 | } | ||
151 | /* Request retry of internal operation. */ | ||
152 | device_set_intretry(sch); | ||
153 | /* Call handler. */ | ||
154 | if (sch->driver && sch->driver->termination) | ||
155 | sch->driver->termination(sch); | ||
156 | } | ||
157 | |||
158 | static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data) | 131 | static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data) |
159 | { | 132 | { |
160 | int j; | ||
161 | int mask; | ||
162 | struct chp_id *chpid = data; | ||
163 | struct schib schib; | ||
164 | |||
165 | for (j = 0; j < 8; j++) { | ||
166 | mask = 0x80 >> j; | ||
167 | if ((sch->schib.pmcw.pim & mask) && | ||
168 | (sch->schib.pmcw.chpid[j] == chpid->id)) | ||
169 | break; | ||
170 | } | ||
171 | if (j >= 8) | ||
172 | return 0; | ||
173 | |||
174 | spin_lock_irq(sch->lock); | 133 | spin_lock_irq(sch->lock); |
175 | 134 | if (sch->driver && sch->driver->chp_event) | |
176 | stsch(sch->schid, &schib); | 135 | if (sch->driver->chp_event(sch, data, CHP_OFFLINE) != 0) |
177 | if (!css_sch_is_valid(&schib)) | ||
178 | goto out_unreg; | ||
179 | memcpy(&sch->schib, &schib, sizeof(struct schib)); | ||
180 | /* Check for single path devices. */ | ||
181 | if (sch->schib.pmcw.pim == 0x80) | ||
182 | goto out_unreg; | ||
183 | |||
184 | if (check_for_io_on_path(sch, mask)) { | ||
185 | if (device_is_online(sch)) | ||
186 | device_kill_io(sch); | ||
187 | else { | ||
188 | terminate_internal_io(sch); | ||
189 | /* Re-start path verification. */ | ||
190 | if (sch->driver && sch->driver->verify) | ||
191 | sch->driver->verify(sch); | ||
192 | } | ||
193 | } else { | ||
194 | /* trigger path verification. */ | ||
195 | if (sch->driver && sch->driver->verify) | ||
196 | sch->driver->verify(sch); | ||
197 | else if (sch->lpm == mask) | ||
198 | goto out_unreg; | 136 | goto out_unreg; |
199 | } | ||
200 | |||
201 | spin_unlock_irq(sch->lock); | 137 | spin_unlock_irq(sch->lock); |
202 | return 0; | 138 | return 0; |
203 | 139 | ||
@@ -211,15 +147,18 @@ out_unreg: | |||
211 | void chsc_chp_offline(struct chp_id chpid) | 147 | void chsc_chp_offline(struct chp_id chpid) |
212 | { | 148 | { |
213 | char dbf_txt[15]; | 149 | char dbf_txt[15]; |
150 | struct chp_link link; | ||
214 | 151 | ||
215 | sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id); | 152 | sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id); |
216 | CIO_TRACE_EVENT(2, dbf_txt); | 153 | CIO_TRACE_EVENT(2, dbf_txt); |
217 | 154 | ||
218 | if (chp_get_status(chpid) <= 0) | 155 | if (chp_get_status(chpid) <= 0) |
219 | return; | 156 | return; |
157 | memset(&link, 0, sizeof(struct chp_link)); | ||
158 | link.chpid = chpid; | ||
220 | /* Wait until previous actions have settled. */ | 159 | /* Wait until previous actions have settled. */ |
221 | css_wait_for_slow_path(); | 160 | css_wait_for_slow_path(); |
222 | for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &chpid); | 161 | for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &link); |
223 | } | 162 | } |
224 | 163 | ||
225 | static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data) | 164 | static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data) |
@@ -242,67 +181,25 @@ static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data) | |||
242 | return 0; | 181 | return 0; |
243 | } | 182 | } |
244 | 183 | ||
245 | struct res_acc_data { | ||
246 | struct chp_id chpid; | ||
247 | u32 fla_mask; | ||
248 | u16 fla; | ||
249 | }; | ||
250 | |||
251 | static int get_res_chpid_mask(struct chsc_ssd_info *ssd, | ||
252 | struct res_acc_data *data) | ||
253 | { | ||
254 | int i; | ||
255 | int mask; | ||
256 | |||
257 | for (i = 0; i < 8; i++) { | ||
258 | mask = 0x80 >> i; | ||
259 | if (!(ssd->path_mask & mask)) | ||
260 | continue; | ||
261 | if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid)) | ||
262 | continue; | ||
263 | if ((ssd->fla_valid_mask & mask) && | ||
264 | ((ssd->fla[i] & data->fla_mask) != data->fla)) | ||
265 | continue; | ||
266 | return mask; | ||
267 | } | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static int __s390_process_res_acc(struct subchannel *sch, void *data) | 184 | static int __s390_process_res_acc(struct subchannel *sch, void *data) |
272 | { | 185 | { |
273 | int chp_mask, old_lpm; | ||
274 | struct res_acc_data *res_data = data; | ||
275 | |||
276 | spin_lock_irq(sch->lock); | 186 | spin_lock_irq(sch->lock); |
277 | chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data); | 187 | if (sch->driver && sch->driver->chp_event) |
278 | if (chp_mask == 0) | 188 | sch->driver->chp_event(sch, data, CHP_ONLINE); |
279 | goto out; | ||
280 | if (stsch(sch->schid, &sch->schib)) | ||
281 | goto out; | ||
282 | old_lpm = sch->lpm; | ||
283 | sch->lpm = ((sch->schib.pmcw.pim & | ||
284 | sch->schib.pmcw.pam & | ||
285 | sch->schib.pmcw.pom) | ||
286 | | chp_mask) & sch->opm; | ||
287 | if (!old_lpm && sch->lpm) | ||
288 | device_trigger_reprobe(sch); | ||
289 | else if (sch->driver && sch->driver->verify) | ||
290 | sch->driver->verify(sch); | ||
291 | out: | ||
292 | spin_unlock_irq(sch->lock); | 189 | spin_unlock_irq(sch->lock); |
293 | 190 | ||
294 | return 0; | 191 | return 0; |
295 | } | 192 | } |
296 | 193 | ||
297 | static void s390_process_res_acc (struct res_acc_data *res_data) | 194 | static void s390_process_res_acc(struct chp_link *link) |
298 | { | 195 | { |
299 | char dbf_txt[15]; | 196 | char dbf_txt[15]; |
300 | 197 | ||
301 | sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid, | 198 | sprintf(dbf_txt, "accpr%x.%02x", link->chpid.cssid, |
302 | res_data->chpid.id); | 199 | link->chpid.id); |
303 | CIO_TRACE_EVENT( 2, dbf_txt); | 200 | CIO_TRACE_EVENT( 2, dbf_txt); |
304 | if (res_data->fla != 0) { | 201 | if (link->fla != 0) { |
305 | sprintf(dbf_txt, "fla%x", res_data->fla); | 202 | sprintf(dbf_txt, "fla%x", link->fla); |
306 | CIO_TRACE_EVENT( 2, dbf_txt); | 203 | CIO_TRACE_EVENT( 2, dbf_txt); |
307 | } | 204 | } |
308 | /* Wait until previous actions have settled. */ | 205 | /* Wait until previous actions have settled. */ |
@@ -315,7 +212,7 @@ static void s390_process_res_acc (struct res_acc_data *res_data) | |||
315 | * will we have to do. | 212 | * will we have to do. |
316 | */ | 213 | */ |
317 | for_each_subchannel_staged(__s390_process_res_acc, | 214 | for_each_subchannel_staged(__s390_process_res_acc, |
318 | s390_process_res_acc_new_sch, res_data); | 215 | s390_process_res_acc_new_sch, link); |
319 | } | 216 | } |
320 | 217 | ||
321 | static int | 218 | static int |
@@ -388,7 +285,7 @@ static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) | |||
388 | 285 | ||
389 | static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) | 286 | static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) |
390 | { | 287 | { |
391 | struct res_acc_data res_data; | 288 | struct chp_link link; |
392 | struct chp_id chpid; | 289 | struct chp_id chpid; |
393 | int status; | 290 | int status; |
394 | 291 | ||
@@ -404,18 +301,18 @@ static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) | |||
404 | chp_new(chpid); | 301 | chp_new(chpid); |
405 | else if (!status) | 302 | else if (!status) |
406 | return; | 303 | return; |
407 | memset(&res_data, 0, sizeof(struct res_acc_data)); | 304 | memset(&link, 0, sizeof(struct chp_link)); |
408 | res_data.chpid = chpid; | 305 | link.chpid = chpid; |
409 | if ((sei_area->vf & 0xc0) != 0) { | 306 | if ((sei_area->vf & 0xc0) != 0) { |
410 | res_data.fla = sei_area->fla; | 307 | link.fla = sei_area->fla; |
411 | if ((sei_area->vf & 0xc0) == 0xc0) | 308 | if ((sei_area->vf & 0xc0) == 0xc0) |
412 | /* full link address */ | 309 | /* full link address */ |
413 | res_data.fla_mask = 0xffff; | 310 | link.fla_mask = 0xffff; |
414 | else | 311 | else |
415 | /* link address */ | 312 | /* link address */ |
416 | res_data.fla_mask = 0xff00; | 313 | link.fla_mask = 0xff00; |
417 | } | 314 | } |
418 | s390_process_res_acc(&res_data); | 315 | s390_process_res_acc(&link); |
419 | } | 316 | } |
420 | 317 | ||
421 | struct chp_config_data { | 318 | struct chp_config_data { |
@@ -480,17 +377,25 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area) | |||
480 | } | 377 | } |
481 | } | 378 | } |
482 | 379 | ||
483 | void chsc_process_crw(void) | 380 | static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow) |
484 | { | 381 | { |
485 | struct chsc_sei_area *sei_area; | 382 | struct chsc_sei_area *sei_area; |
486 | 383 | ||
384 | if (overflow) { | ||
385 | css_schedule_eval_all(); | ||
386 | return; | ||
387 | } | ||
388 | CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, " | ||
389 | "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", | ||
390 | crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc, | ||
391 | crw0->erc, crw0->rsid); | ||
487 | if (!sei_page) | 392 | if (!sei_page) |
488 | return; | 393 | return; |
489 | /* Access to sei_page is serialized through machine check handler | 394 | /* Access to sei_page is serialized through machine check handler |
490 | * thread, so no need for locking. */ | 395 | * thread, so no need for locking. */ |
491 | sei_area = sei_page; | 396 | sei_area = sei_page; |
492 | 397 | ||
493 | CIO_TRACE_EVENT( 2, "prcss"); | 398 | CIO_TRACE_EVENT(2, "prcss"); |
494 | do { | 399 | do { |
495 | memset(sei_area, 0, sizeof(*sei_area)); | 400 | memset(sei_area, 0, sizeof(*sei_area)); |
496 | sei_area->request.length = 0x0010; | 401 | sei_area->request.length = 0x0010; |
@@ -509,114 +414,36 @@ void chsc_process_crw(void) | |||
509 | } while (sei_area->flags & 0x80); | 414 | } while (sei_area->flags & 0x80); |
510 | } | 415 | } |
511 | 416 | ||
512 | static int __chp_add_new_sch(struct subchannel_id schid, void *data) | ||
513 | { | ||
514 | struct schib schib; | ||
515 | |||
516 | if (stsch_err(schid, &schib)) | ||
517 | /* We're through */ | ||
518 | return -ENXIO; | ||
519 | |||
520 | /* Put it on the slow path. */ | ||
521 | css_schedule_eval(schid); | ||
522 | return 0; | ||
523 | } | ||
524 | |||
525 | |||
526 | static int __chp_add(struct subchannel *sch, void *data) | ||
527 | { | ||
528 | int i, mask; | ||
529 | struct chp_id *chpid = data; | ||
530 | |||
531 | spin_lock_irq(sch->lock); | ||
532 | for (i=0; i<8; i++) { | ||
533 | mask = 0x80 >> i; | ||
534 | if ((sch->schib.pmcw.pim & mask) && | ||
535 | (sch->schib.pmcw.chpid[i] == chpid->id)) | ||
536 | break; | ||
537 | } | ||
538 | if (i==8) { | ||
539 | spin_unlock_irq(sch->lock); | ||
540 | return 0; | ||
541 | } | ||
542 | if (stsch(sch->schid, &sch->schib)) { | ||
543 | spin_unlock_irq(sch->lock); | ||
544 | css_schedule_eval(sch->schid); | ||
545 | return 0; | ||
546 | } | ||
547 | sch->lpm = ((sch->schib.pmcw.pim & | ||
548 | sch->schib.pmcw.pam & | ||
549 | sch->schib.pmcw.pom) | ||
550 | | mask) & sch->opm; | ||
551 | |||
552 | if (sch->driver && sch->driver->verify) | ||
553 | sch->driver->verify(sch); | ||
554 | |||
555 | spin_unlock_irq(sch->lock); | ||
556 | |||
557 | return 0; | ||
558 | } | ||
559 | |||
560 | void chsc_chp_online(struct chp_id chpid) | 417 | void chsc_chp_online(struct chp_id chpid) |
561 | { | 418 | { |
562 | char dbf_txt[15]; | 419 | char dbf_txt[15]; |
420 | struct chp_link link; | ||
563 | 421 | ||
564 | sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id); | 422 | sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id); |
565 | CIO_TRACE_EVENT(2, dbf_txt); | 423 | CIO_TRACE_EVENT(2, dbf_txt); |
566 | 424 | ||
567 | if (chp_get_status(chpid) != 0) { | 425 | if (chp_get_status(chpid) != 0) { |
426 | memset(&link, 0, sizeof(struct chp_link)); | ||
427 | link.chpid = chpid; | ||
568 | /* Wait until previous actions have settled. */ | 428 | /* Wait until previous actions have settled. */ |
569 | css_wait_for_slow_path(); | 429 | css_wait_for_slow_path(); |
570 | for_each_subchannel_staged(__chp_add, __chp_add_new_sch, | 430 | for_each_subchannel_staged(__s390_process_res_acc, NULL, |
571 | &chpid); | 431 | &link); |
572 | } | 432 | } |
573 | } | 433 | } |
574 | 434 | ||
575 | static void __s390_subchannel_vary_chpid(struct subchannel *sch, | 435 | static void __s390_subchannel_vary_chpid(struct subchannel *sch, |
576 | struct chp_id chpid, int on) | 436 | struct chp_id chpid, int on) |
577 | { | 437 | { |
578 | int chp, old_lpm; | ||
579 | int mask; | ||
580 | unsigned long flags; | 438 | unsigned long flags; |
439 | struct chp_link link; | ||
581 | 440 | ||
441 | memset(&link, 0, sizeof(struct chp_link)); | ||
442 | link.chpid = chpid; | ||
582 | spin_lock_irqsave(sch->lock, flags); | 443 | spin_lock_irqsave(sch->lock, flags); |
583 | old_lpm = sch->lpm; | 444 | if (sch->driver && sch->driver->chp_event) |
584 | for (chp = 0; chp < 8; chp++) { | 445 | sch->driver->chp_event(sch, &link, |
585 | mask = 0x80 >> chp; | 446 | on ? CHP_VARY_ON : CHP_VARY_OFF); |
586 | if (!(sch->ssd_info.path_mask & mask)) | ||
587 | continue; | ||
588 | if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid)) | ||
589 | continue; | ||
590 | |||
591 | if (on) { | ||
592 | sch->opm |= mask; | ||
593 | sch->lpm |= mask; | ||
594 | if (!old_lpm) | ||
595 | device_trigger_reprobe(sch); | ||
596 | else if (sch->driver && sch->driver->verify) | ||
597 | sch->driver->verify(sch); | ||
598 | break; | ||
599 | } | ||
600 | sch->opm &= ~mask; | ||
601 | sch->lpm &= ~mask; | ||
602 | if (check_for_io_on_path(sch, mask)) { | ||
603 | if (device_is_online(sch)) | ||
604 | /* Path verification is done after killing. */ | ||
605 | device_kill_io(sch); | ||
606 | else { | ||
607 | /* Kill and retry internal I/O. */ | ||
608 | terminate_internal_io(sch); | ||
609 | /* Re-start path verification. */ | ||
610 | if (sch->driver && sch->driver->verify) | ||
611 | sch->driver->verify(sch); | ||
612 | } | ||
613 | } else if (!sch->lpm) { | ||
614 | if (device_trigger_verify(sch) != 0) | ||
615 | css_schedule_eval(sch->schid); | ||
616 | } else if (sch->driver && sch->driver->verify) | ||
617 | sch->driver->verify(sch); | ||
618 | break; | ||
619 | } | ||
620 | spin_unlock_irqrestore(sch->lock, flags); | 447 | spin_unlock_irqrestore(sch->lock, flags); |
621 | } | 448 | } |
622 | 449 | ||
@@ -656,6 +483,10 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data) | |||
656 | */ | 483 | */ |
657 | int chsc_chp_vary(struct chp_id chpid, int on) | 484 | int chsc_chp_vary(struct chp_id chpid, int on) |
658 | { | 485 | { |
486 | struct chp_link link; | ||
487 | |||
488 | memset(&link, 0, sizeof(struct chp_link)); | ||
489 | link.chpid = chpid; | ||
659 | /* Wait until previous actions have settled. */ | 490 | /* Wait until previous actions have settled. */ |
660 | css_wait_for_slow_path(); | 491 | css_wait_for_slow_path(); |
661 | /* | 492 | /* |
@@ -664,10 +495,10 @@ int chsc_chp_vary(struct chp_id chpid, int on) | |||
664 | 495 | ||
665 | if (on) | 496 | if (on) |
666 | for_each_subchannel_staged(s390_subchannel_vary_chpid_on, | 497 | for_each_subchannel_staged(s390_subchannel_vary_chpid_on, |
667 | __s390_vary_chpid_on, &chpid); | 498 | __s390_vary_chpid_on, &link); |
668 | else | 499 | else |
669 | for_each_subchannel_staged(s390_subchannel_vary_chpid_off, | 500 | for_each_subchannel_staged(s390_subchannel_vary_chpid_off, |
670 | NULL, &chpid); | 501 | NULL, &link); |
671 | 502 | ||
672 | return 0; | 503 | return 0; |
673 | } | 504 | } |
@@ -797,23 +628,33 @@ chsc_secm(struct channel_subsystem *css, int enable) | |||
797 | return ret; | 628 | return ret; |
798 | } | 629 | } |
799 | 630 | ||
800 | int chsc_determine_channel_path_description(struct chp_id chpid, | 631 | int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, |
801 | struct channel_path_desc *desc) | 632 | int c, int m, |
633 | struct chsc_response_struct *resp) | ||
802 | { | 634 | { |
803 | int ccode, ret; | 635 | int ccode, ret; |
804 | 636 | ||
805 | struct { | 637 | struct { |
806 | struct chsc_header request; | 638 | struct chsc_header request; |
807 | u32 : 24; | 639 | u32 : 2; |
640 | u32 m : 1; | ||
641 | u32 c : 1; | ||
642 | u32 fmt : 4; | ||
643 | u32 cssid : 8; | ||
644 | u32 : 4; | ||
645 | u32 rfmt : 4; | ||
808 | u32 first_chpid : 8; | 646 | u32 first_chpid : 8; |
809 | u32 : 24; | 647 | u32 : 24; |
810 | u32 last_chpid : 8; | 648 | u32 last_chpid : 8; |
811 | u32 zeroes1; | 649 | u32 zeroes1; |
812 | struct chsc_header response; | 650 | struct chsc_header response; |
813 | u32 zeroes2; | 651 | u8 data[PAGE_SIZE - 20]; |
814 | struct channel_path_desc desc; | ||
815 | } __attribute__ ((packed)) *scpd_area; | 652 | } __attribute__ ((packed)) *scpd_area; |
816 | 653 | ||
654 | if ((rfmt == 1) && !css_general_characteristics.fcs) | ||
655 | return -EINVAL; | ||
656 | if ((rfmt == 2) && !css_general_characteristics.cib) | ||
657 | return -EINVAL; | ||
817 | scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 658 | scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); |
818 | if (!scpd_area) | 659 | if (!scpd_area) |
819 | return -ENOMEM; | 660 | return -ENOMEM; |
@@ -821,8 +662,13 @@ int chsc_determine_channel_path_description(struct chp_id chpid, | |||
821 | scpd_area->request.length = 0x0010; | 662 | scpd_area->request.length = 0x0010; |
822 | scpd_area->request.code = 0x0002; | 663 | scpd_area->request.code = 0x0002; |
823 | 664 | ||
665 | scpd_area->cssid = chpid.cssid; | ||
824 | scpd_area->first_chpid = chpid.id; | 666 | scpd_area->first_chpid = chpid.id; |
825 | scpd_area->last_chpid = chpid.id; | 667 | scpd_area->last_chpid = chpid.id; |
668 | scpd_area->m = m; | ||
669 | scpd_area->c = c; | ||
670 | scpd_area->fmt = fmt; | ||
671 | scpd_area->rfmt = rfmt; | ||
826 | 672 | ||
827 | ccode = chsc(scpd_area); | 673 | ccode = chsc(scpd_area); |
828 | if (ccode > 0) { | 674 | if (ccode > 0) { |
@@ -833,8 +679,7 @@ int chsc_determine_channel_path_description(struct chp_id chpid, | |||
833 | ret = chsc_error_from_response(scpd_area->response.code); | 679 | ret = chsc_error_from_response(scpd_area->response.code); |
834 | if (ret == 0) | 680 | if (ret == 0) |
835 | /* Success. */ | 681 | /* Success. */ |
836 | memcpy(desc, &scpd_area->desc, | 682 | memcpy(resp, &scpd_area->response, scpd_area->response.length); |
837 | sizeof(struct channel_path_desc)); | ||
838 | else | 683 | else |
839 | CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n", | 684 | CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n", |
840 | scpd_area->response.code); | 685 | scpd_area->response.code); |
@@ -842,6 +687,25 @@ out: | |||
842 | free_page((unsigned long)scpd_area); | 687 | free_page((unsigned long)scpd_area); |
843 | return ret; | 688 | return ret; |
844 | } | 689 | } |
690 | EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc); | ||
691 | |||
692 | int chsc_determine_base_channel_path_desc(struct chp_id chpid, | ||
693 | struct channel_path_desc *desc) | ||
694 | { | ||
695 | struct chsc_response_struct *chsc_resp; | ||
696 | int ret; | ||
697 | |||
698 | chsc_resp = kzalloc(sizeof(*chsc_resp), GFP_KERNEL); | ||
699 | if (!chsc_resp) | ||
700 | return -ENOMEM; | ||
701 | ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, chsc_resp); | ||
702 | if (ret) | ||
703 | goto out_free; | ||
704 | memcpy(desc, &chsc_resp->data, chsc_resp->length); | ||
705 | out_free: | ||
706 | kfree(chsc_resp); | ||
707 | return ret; | ||
708 | } | ||
845 | 709 | ||
846 | static void | 710 | static void |
847 | chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, | 711 | chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, |
@@ -937,15 +801,23 @@ out: | |||
937 | 801 | ||
938 | int __init chsc_alloc_sei_area(void) | 802 | int __init chsc_alloc_sei_area(void) |
939 | { | 803 | { |
804 | int ret; | ||
805 | |||
940 | sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 806 | sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); |
941 | if (!sei_page) | 807 | if (!sei_page) { |
942 | CIO_MSG_EVENT(0, "Can't allocate page for processing of " | 808 | CIO_MSG_EVENT(0, "Can't allocate page for processing of " |
943 | "chsc machine checks!\n"); | 809 | "chsc machine checks!\n"); |
944 | return (sei_page ? 0 : -ENOMEM); | 810 | return -ENOMEM; |
811 | } | ||
812 | ret = s390_register_crw_handler(CRW_RSC_CSS, chsc_process_crw); | ||
813 | if (ret) | ||
814 | kfree(sei_page); | ||
815 | return ret; | ||
945 | } | 816 | } |
946 | 817 | ||
947 | void __init chsc_free_sei_area(void) | 818 | void __init chsc_free_sei_area(void) |
948 | { | 819 | { |
820 | s390_unregister_crw_handler(CRW_RSC_CSS); | ||
949 | kfree(sei_page); | 821 | kfree(sei_page); |
950 | } | 822 | } |
951 | 823 | ||
@@ -1043,3 +915,52 @@ exit: | |||
1043 | 915 | ||
1044 | EXPORT_SYMBOL_GPL(css_general_characteristics); | 916 | EXPORT_SYMBOL_GPL(css_general_characteristics); |
1045 | EXPORT_SYMBOL_GPL(css_chsc_characteristics); | 917 | EXPORT_SYMBOL_GPL(css_chsc_characteristics); |
918 | |||
919 | int chsc_sstpc(void *page, unsigned int op, u16 ctrl) | ||
920 | { | ||
921 | struct { | ||
922 | struct chsc_header request; | ||
923 | unsigned int rsvd0; | ||
924 | unsigned int op : 8; | ||
925 | unsigned int rsvd1 : 8; | ||
926 | unsigned int ctrl : 16; | ||
927 | unsigned int rsvd2[5]; | ||
928 | struct chsc_header response; | ||
929 | unsigned int rsvd3[7]; | ||
930 | } __attribute__ ((packed)) *rr; | ||
931 | int rc; | ||
932 | |||
933 | memset(page, 0, PAGE_SIZE); | ||
934 | rr = page; | ||
935 | rr->request.length = 0x0020; | ||
936 | rr->request.code = 0x0033; | ||
937 | rr->op = op; | ||
938 | rr->ctrl = ctrl; | ||
939 | rc = chsc(rr); | ||
940 | if (rc) | ||
941 | return -EIO; | ||
942 | rc = (rr->response.code == 0x0001) ? 0 : -EIO; | ||
943 | return rc; | ||
944 | } | ||
945 | |||
946 | int chsc_sstpi(void *page, void *result, size_t size) | ||
947 | { | ||
948 | struct { | ||
949 | struct chsc_header request; | ||
950 | unsigned int rsvd0[3]; | ||
951 | struct chsc_header response; | ||
952 | char data[size]; | ||
953 | } __attribute__ ((packed)) *rr; | ||
954 | int rc; | ||
955 | |||
956 | memset(page, 0, PAGE_SIZE); | ||
957 | rr = page; | ||
958 | rr->request.length = 0x0010; | ||
959 | rr->request.code = 0x0038; | ||
960 | rc = chsc(rr); | ||
961 | if (rc) | ||
962 | return -EIO; | ||
963 | memcpy(result, &rr->data, size); | ||
964 | return (rr->response.code == 0x0001) ? 0 : -EIO; | ||
965 | } | ||
966 | |||