diff options
author | Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | 2008-01-26 08:10:48 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-01-26 08:11:03 -0500 |
commit | e82a1567e4b22eb035da2499d20ddd573c9acf75 (patch) | |
tree | 0cf697f96e734a846ee1cbc598beebcc7be10117 /drivers/s390 | |
parent | 4beee64685e116b01c47655daf6d88df87e053c8 (diff) |
[S390] cio: reduce cpu utilization during device scan
Minimize calls to cpu intensive function get_subchannel_by_schid()
by introducing function for_each_subchannel_staged() which
temporarily caches the information about registered subchannels
in a bitmap.
Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/cio/chsc.c | 98 | ||||
-rw-r--r-- | drivers/s390/cio/css.c | 120 | ||||
-rw-r--r-- | drivers/s390/cio/css.h | 3 |
3 files changed, 137 insertions, 84 deletions
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 12a344c66b46..93e6f74187ee 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c | |||
@@ -136,17 +136,13 @@ static void terminate_internal_io(struct subchannel *sch) | |||
136 | sch->driver->termination(sch); | 136 | sch->driver->termination(sch); |
137 | } | 137 | } |
138 | 138 | ||
139 | static int | 139 | static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data) |
140 | s390_subchannel_remove_chpid(struct device *dev, void *data) | ||
141 | { | 140 | { |
142 | int j; | 141 | int j; |
143 | int mask; | 142 | int mask; |
144 | struct subchannel *sch; | 143 | struct chp_id *chpid = data; |
145 | struct chp_id *chpid; | ||
146 | struct schib schib; | 144 | struct schib schib; |
147 | 145 | ||
148 | sch = to_subchannel(dev); | ||
149 | chpid = data; | ||
150 | for (j = 0; j < 8; j++) { | 146 | for (j = 0; j < 8; j++) { |
151 | mask = 0x80 >> j; | 147 | mask = 0x80 >> j; |
152 | if ((sch->schib.pmcw.pim & mask) && | 148 | if ((sch->schib.pmcw.pim & mask) && |
@@ -202,12 +198,10 @@ void chsc_chp_offline(struct chp_id chpid) | |||
202 | 198 | ||
203 | if (chp_get_status(chpid) <= 0) | 199 | if (chp_get_status(chpid) <= 0) |
204 | return; | 200 | return; |
205 | bus_for_each_dev(&css_bus_type, NULL, &chpid, | 201 | for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &chpid); |
206 | s390_subchannel_remove_chpid); | ||
207 | } | 202 | } |
208 | 203 | ||
209 | static int | 204 | static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data) |
210 | s390_process_res_acc_new_sch(struct subchannel_id schid) | ||
211 | { | 205 | { |
212 | struct schib schib; | 206 | struct schib schib; |
213 | /* | 207 | /* |
@@ -253,18 +247,10 @@ static int get_res_chpid_mask(struct chsc_ssd_info *ssd, | |||
253 | return 0; | 247 | return 0; |
254 | } | 248 | } |
255 | 249 | ||
256 | static int | 250 | static int __s390_process_res_acc(struct subchannel *sch, void *data) |
257 | __s390_process_res_acc(struct subchannel_id schid, void *data) | ||
258 | { | 251 | { |
259 | int chp_mask, old_lpm; | 252 | int chp_mask, old_lpm; |
260 | struct res_acc_data *res_data; | 253 | struct res_acc_data *res_data = data; |
261 | struct subchannel *sch; | ||
262 | |||
263 | res_data = data; | ||
264 | sch = get_subchannel_by_schid(schid); | ||
265 | if (!sch) | ||
266 | /* Check if a subchannel is newly available. */ | ||
267 | return s390_process_res_acc_new_sch(schid); | ||
268 | 254 | ||
269 | spin_lock_irq(sch->lock); | 255 | spin_lock_irq(sch->lock); |
270 | chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data); | 256 | chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data); |
@@ -283,7 +269,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) | |||
283 | sch->driver->verify(sch); | 269 | sch->driver->verify(sch); |
284 | out: | 270 | out: |
285 | spin_unlock_irq(sch->lock); | 271 | spin_unlock_irq(sch->lock); |
286 | put_device(&sch->dev); | 272 | |
287 | return 0; | 273 | return 0; |
288 | } | 274 | } |
289 | 275 | ||
@@ -306,7 +292,8 @@ static void s390_process_res_acc (struct res_acc_data *res_data) | |||
306 | * The more information we have (info), the less scanning | 292 | * The more information we have (info), the less scanning |
307 | * will we have to do. | 293 | * will we have to do. |
308 | */ | 294 | */ |
309 | for_each_subchannel(__s390_process_res_acc, res_data); | 295 | for_each_subchannel_staged(__s390_process_res_acc, |
296 | s390_process_res_acc_new_sch, res_data); | ||
310 | } | 297 | } |
311 | 298 | ||
312 | static int | 299 | static int |
@@ -500,8 +487,7 @@ void chsc_process_crw(void) | |||
500 | } while (sei_area->flags & 0x80); | 487 | } while (sei_area->flags & 0x80); |
501 | } | 488 | } |
502 | 489 | ||
503 | static int | 490 | static int __chp_add_new_sch(struct subchannel_id schid, void *data) |
504 | __chp_add_new_sch(struct subchannel_id schid) | ||
505 | { | 491 | { |
506 | struct schib schib; | 492 | struct schib schib; |
507 | 493 | ||
@@ -515,35 +501,27 @@ __chp_add_new_sch(struct subchannel_id schid) | |||
515 | } | 501 | } |
516 | 502 | ||
517 | 503 | ||
518 | static int | 504 | static int __chp_add(struct subchannel *sch, void *data) |
519 | __chp_add(struct subchannel_id schid, void *data) | ||
520 | { | 505 | { |
521 | int i, mask; | 506 | int i, mask; |
522 | struct chp_id *chpid; | 507 | struct chp_id *chpid = data; |
523 | struct subchannel *sch; | 508 | |
524 | |||
525 | chpid = data; | ||
526 | sch = get_subchannel_by_schid(schid); | ||
527 | if (!sch) | ||
528 | /* Check if the subchannel is now available. */ | ||
529 | return __chp_add_new_sch(schid); | ||
530 | spin_lock_irq(sch->lock); | 509 | spin_lock_irq(sch->lock); |
531 | for (i=0; i<8; i++) { | 510 | for (i=0; i<8; i++) { |
532 | mask = 0x80 >> i; | 511 | mask = 0x80 >> i; |
533 | if ((sch->schib.pmcw.pim & mask) && | 512 | if ((sch->schib.pmcw.pim & mask) && |
534 | (sch->schib.pmcw.chpid[i] == chpid->id)) { | 513 | (sch->schib.pmcw.chpid[i] == chpid->id)) |
535 | if (stsch(sch->schid, &sch->schib) != 0) { | ||
536 | /* Endgame. */ | ||
537 | spin_unlock_irq(sch->lock); | ||
538 | return -ENXIO; | ||
539 | } | ||
540 | break; | 514 | break; |
541 | } | ||
542 | } | 515 | } |
543 | if (i==8) { | 516 | if (i==8) { |
544 | spin_unlock_irq(sch->lock); | 517 | spin_unlock_irq(sch->lock); |
545 | return 0; | 518 | return 0; |
546 | } | 519 | } |
520 | if (stsch(sch->schid, &sch->schib)) { | ||
521 | spin_unlock_irq(sch->lock); | ||
522 | css_schedule_eval(sch->schid); | ||
523 | return 0; | ||
524 | } | ||
547 | sch->lpm = ((sch->schib.pmcw.pim & | 525 | sch->lpm = ((sch->schib.pmcw.pim & |
548 | sch->schib.pmcw.pam & | 526 | sch->schib.pmcw.pam & |
549 | sch->schib.pmcw.pom) | 527 | sch->schib.pmcw.pom) |
@@ -553,7 +531,7 @@ __chp_add(struct subchannel_id schid, void *data) | |||
553 | sch->driver->verify(sch); | 531 | sch->driver->verify(sch); |
554 | 532 | ||
555 | spin_unlock_irq(sch->lock); | 533 | spin_unlock_irq(sch->lock); |
556 | put_device(&sch->dev); | 534 | |
557 | return 0; | 535 | return 0; |
558 | } | 536 | } |
559 | 537 | ||
@@ -565,7 +543,8 @@ void chsc_chp_online(struct chp_id chpid) | |||
565 | CIO_TRACE_EVENT(2, dbf_txt); | 543 | CIO_TRACE_EVENT(2, dbf_txt); |
566 | 544 | ||
567 | if (chp_get_status(chpid) != 0) | 545 | if (chp_get_status(chpid) != 0) |
568 | for_each_subchannel(__chp_add, &chpid); | 546 | for_each_subchannel_staged(__chp_add, __chp_add_new_sch, |
547 | &chpid); | ||
569 | } | 548 | } |
570 | 549 | ||
571 | static void __s390_subchannel_vary_chpid(struct subchannel *sch, | 550 | static void __s390_subchannel_vary_chpid(struct subchannel *sch, |
@@ -616,25 +595,17 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch, | |||
616 | spin_unlock_irqrestore(sch->lock, flags); | 595 | spin_unlock_irqrestore(sch->lock, flags); |
617 | } | 596 | } |
618 | 597 | ||
619 | static int s390_subchannel_vary_chpid_off(struct device *dev, void *data) | 598 | static int s390_subchannel_vary_chpid_off(struct subchannel *sch, void *data) |
620 | { | 599 | { |
621 | struct subchannel *sch; | 600 | struct chp_id *chpid = data; |
622 | struct chp_id *chpid; | ||
623 | |||
624 | sch = to_subchannel(dev); | ||
625 | chpid = data; | ||
626 | 601 | ||
627 | __s390_subchannel_vary_chpid(sch, *chpid, 0); | 602 | __s390_subchannel_vary_chpid(sch, *chpid, 0); |
628 | return 0; | 603 | return 0; |
629 | } | 604 | } |
630 | 605 | ||
631 | static int s390_subchannel_vary_chpid_on(struct device *dev, void *data) | 606 | static int s390_subchannel_vary_chpid_on(struct subchannel *sch, void *data) |
632 | { | 607 | { |
633 | struct subchannel *sch; | 608 | struct chp_id *chpid = data; |
634 | struct chp_id *chpid; | ||
635 | |||
636 | sch = to_subchannel(dev); | ||
637 | chpid = data; | ||
638 | 609 | ||
639 | __s390_subchannel_vary_chpid(sch, *chpid, 1); | 610 | __s390_subchannel_vary_chpid(sch, *chpid, 1); |
640 | return 0; | 611 | return 0; |
@@ -644,13 +615,7 @@ static int | |||
644 | __s390_vary_chpid_on(struct subchannel_id schid, void *data) | 615 | __s390_vary_chpid_on(struct subchannel_id schid, void *data) |
645 | { | 616 | { |
646 | struct schib schib; | 617 | struct schib schib; |
647 | struct subchannel *sch; | ||
648 | 618 | ||
649 | sch = get_subchannel_by_schid(schid); | ||
650 | if (sch) { | ||
651 | put_device(&sch->dev); | ||
652 | return 0; | ||
653 | } | ||
654 | if (stsch_err(schid, &schib)) | 619 | if (stsch_err(schid, &schib)) |
655 | /* We're through */ | 620 | /* We're through */ |
656 | return -ENXIO; | 621 | return -ENXIO; |
@@ -670,12 +635,13 @@ int chsc_chp_vary(struct chp_id chpid, int on) | |||
670 | * Redo PathVerification on the devices the chpid connects to | 635 | * Redo PathVerification on the devices the chpid connects to |
671 | */ | 636 | */ |
672 | 637 | ||
673 | bus_for_each_dev(&css_bus_type, NULL, &chpid, on ? | ||
674 | s390_subchannel_vary_chpid_on : | ||
675 | s390_subchannel_vary_chpid_off); | ||
676 | if (on) | 638 | if (on) |
677 | /* Scan for new devices on varied on path. */ | 639 | for_each_subchannel_staged(s390_subchannel_vary_chpid_on, |
678 | for_each_subchannel(__s390_vary_chpid_on, NULL); | 640 | __s390_vary_chpid_on, &chpid); |
641 | else | ||
642 | for_each_subchannel_staged(s390_subchannel_vary_chpid_off, | ||
643 | NULL, &chpid); | ||
644 | |||
679 | return 0; | 645 | return 0; |
680 | } | 646 | } |
681 | 647 | ||
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 69d56c7284d1..3b45bbe6cce0 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c | |||
@@ -51,6 +51,62 @@ for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data) | |||
51 | return ret; | 51 | return ret; |
52 | } | 52 | } |
53 | 53 | ||
54 | struct cb_data { | ||
55 | void *data; | ||
56 | struct idset *set; | ||
57 | int (*fn_known_sch)(struct subchannel *, void *); | ||
58 | int (*fn_unknown_sch)(struct subchannel_id, void *); | ||
59 | }; | ||
60 | |||
61 | static int call_fn_known_sch(struct device *dev, void *data) | ||
62 | { | ||
63 | struct subchannel *sch = to_subchannel(dev); | ||
64 | struct cb_data *cb = data; | ||
65 | int rc = 0; | ||
66 | |||
67 | idset_sch_del(cb->set, sch->schid); | ||
68 | if (cb->fn_known_sch) | ||
69 | rc = cb->fn_known_sch(sch, cb->data); | ||
70 | return rc; | ||
71 | } | ||
72 | |||
73 | static int call_fn_unknown_sch(struct subchannel_id schid, void *data) | ||
74 | { | ||
75 | struct cb_data *cb = data; | ||
76 | int rc = 0; | ||
77 | |||
78 | if (idset_sch_contains(cb->set, schid)) | ||
79 | rc = cb->fn_unknown_sch(schid, cb->data); | ||
80 | return rc; | ||
81 | } | ||
82 | |||
83 | int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *), | ||
84 | int (*fn_unknown)(struct subchannel_id, | ||
85 | void *), void *data) | ||
86 | { | ||
87 | struct cb_data cb; | ||
88 | int rc; | ||
89 | |||
90 | cb.set = idset_sch_new(); | ||
91 | if (!cb.set) | ||
92 | return -ENOMEM; | ||
93 | idset_fill(cb.set); | ||
94 | cb.data = data; | ||
95 | cb.fn_known_sch = fn_known; | ||
96 | cb.fn_unknown_sch = fn_unknown; | ||
97 | /* Process registered subchannels. */ | ||
98 | rc = bus_for_each_dev(&css_bus_type, NULL, &cb, call_fn_known_sch); | ||
99 | if (rc) | ||
100 | goto out; | ||
101 | /* Process unregistered subchannels. */ | ||
102 | if (fn_unknown) | ||
103 | rc = for_each_subchannel(call_fn_unknown_sch, &cb); | ||
104 | out: | ||
105 | idset_free(cb.set); | ||
106 | |||
107 | return rc; | ||
108 | } | ||
109 | |||
54 | static struct subchannel * | 110 | static struct subchannel * |
55 | css_alloc_subchannel(struct subchannel_id schid) | 111 | css_alloc_subchannel(struct subchannel_id schid) |
56 | { | 112 | { |
@@ -402,20 +458,56 @@ static int __init slow_subchannel_init(void) | |||
402 | return 0; | 458 | return 0; |
403 | } | 459 | } |
404 | 460 | ||
405 | static void css_slow_path_func(struct work_struct *unused) | 461 | static int slow_eval_known_fn(struct subchannel *sch, void *data) |
406 | { | 462 | { |
407 | struct subchannel_id schid; | 463 | int eval; |
464 | int rc; | ||
408 | 465 | ||
409 | CIO_TRACE_EVENT(4, "slowpath"); | ||
410 | spin_lock_irq(&slow_subchannel_lock); | 466 | spin_lock_irq(&slow_subchannel_lock); |
411 | init_subchannel_id(&schid); | 467 | eval = idset_sch_contains(slow_subchannel_set, sch->schid); |
412 | while (idset_sch_get_first(slow_subchannel_set, &schid)) { | 468 | idset_sch_del(slow_subchannel_set, sch->schid); |
413 | idset_sch_del(slow_subchannel_set, schid); | 469 | spin_unlock_irq(&slow_subchannel_lock); |
414 | spin_unlock_irq(&slow_subchannel_lock); | 470 | if (eval) { |
415 | css_evaluate_subchannel(schid, 1); | 471 | rc = css_evaluate_known_subchannel(sch, 1); |
416 | spin_lock_irq(&slow_subchannel_lock); | 472 | if (rc == -EAGAIN) |
473 | css_schedule_eval(sch->schid); | ||
417 | } | 474 | } |
475 | return 0; | ||
476 | } | ||
477 | |||
478 | static int slow_eval_unknown_fn(struct subchannel_id schid, void *data) | ||
479 | { | ||
480 | int eval; | ||
481 | int rc = 0; | ||
482 | |||
483 | spin_lock_irq(&slow_subchannel_lock); | ||
484 | eval = idset_sch_contains(slow_subchannel_set, schid); | ||
485 | idset_sch_del(slow_subchannel_set, schid); | ||
418 | spin_unlock_irq(&slow_subchannel_lock); | 486 | spin_unlock_irq(&slow_subchannel_lock); |
487 | if (eval) { | ||
488 | rc = css_evaluate_new_subchannel(schid, 1); | ||
489 | switch (rc) { | ||
490 | case -EAGAIN: | ||
491 | css_schedule_eval(schid); | ||
492 | rc = 0; | ||
493 | break; | ||
494 | case -ENXIO: | ||
495 | case -ENOMEM: | ||
496 | case -EIO: | ||
497 | /* These should abort looping */ | ||
498 | break; | ||
499 | default: | ||
500 | rc = 0; | ||
501 | } | ||
502 | } | ||
503 | return rc; | ||
504 | } | ||
505 | |||
506 | static void css_slow_path_func(struct work_struct *unused) | ||
507 | { | ||
508 | CIO_TRACE_EVENT(4, "slowpath"); | ||
509 | for_each_subchannel_staged(slow_eval_known_fn, slow_eval_unknown_fn, | ||
510 | NULL); | ||
419 | } | 511 | } |
420 | 512 | ||
421 | static DECLARE_WORK(slow_path_work, css_slow_path_func); | 513 | static DECLARE_WORK(slow_path_work, css_slow_path_func); |
@@ -444,7 +536,6 @@ void css_schedule_eval_all(void) | |||
444 | /* Reprobe subchannel if unregistered. */ | 536 | /* Reprobe subchannel if unregistered. */ |
445 | static int reprobe_subchannel(struct subchannel_id schid, void *data) | 537 | static int reprobe_subchannel(struct subchannel_id schid, void *data) |
446 | { | 538 | { |
447 | struct subchannel *sch; | ||
448 | int ret; | 539 | int ret; |
449 | 540 | ||
450 | CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n", | 541 | CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n", |
@@ -452,13 +543,6 @@ static int reprobe_subchannel(struct subchannel_id schid, void *data) | |||
452 | if (need_reprobe) | 543 | if (need_reprobe) |
453 | return -EAGAIN; | 544 | return -EAGAIN; |
454 | 545 | ||
455 | sch = get_subchannel_by_schid(schid); | ||
456 | if (sch) { | ||
457 | /* Already known. */ | ||
458 | put_device(&sch->dev); | ||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | ret = css_probe_device(schid); | 546 | ret = css_probe_device(schid); |
463 | switch (ret) { | 547 | switch (ret) { |
464 | case 0: | 548 | case 0: |
@@ -486,7 +570,7 @@ static void reprobe_all(struct work_struct *unused) | |||
486 | /* Make sure initial subchannel scan is done. */ | 570 | /* Make sure initial subchannel scan is done. */ |
487 | wait_event(ccw_device_init_wq, | 571 | wait_event(ccw_device_init_wq, |
488 | atomic_read(&ccw_device_init_count) == 0); | 572 | atomic_read(&ccw_device_init_count) == 0); |
489 | ret = for_each_subchannel(reprobe_subchannel, NULL); | 573 | ret = for_each_subchannel_staged(NULL, reprobe_subchannel, NULL); |
490 | 574 | ||
491 | CIO_MSG_EVENT(2, "reprobe done (rc=%d, need_reprobe=%d)\n", ret, | 575 | CIO_MSG_EVENT(2, "reprobe done (rc=%d, need_reprobe=%d)\n", ret, |
492 | need_reprobe); | 576 | need_reprobe); |
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 40598b8ee27d..b70554523552 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h | |||
@@ -91,6 +91,9 @@ extern void css_driver_unregister(struct css_driver *); | |||
91 | extern void css_sch_device_unregister(struct subchannel *); | 91 | extern void css_sch_device_unregister(struct subchannel *); |
92 | extern struct subchannel * get_subchannel_by_schid(struct subchannel_id); | 92 | extern struct subchannel * get_subchannel_by_schid(struct subchannel_id); |
93 | extern int css_init_done; | 93 | extern int css_init_done; |
94 | int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *), | ||
95 | int (*fn_unknown)(struct subchannel_id, | ||
96 | void *), void *data); | ||
94 | extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *); | 97 | extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *); |
95 | extern void css_process_crw(int, int); | 98 | extern void css_process_crw(int, int); |
96 | extern void css_reiterate_subchannels(void); | 99 | extern void css_reiterate_subchannels(void); |