aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
authorPeter Oberparleiter <peter.oberparleiter@de.ibm.com>2008-01-26 08:10:48 -0500
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2008-01-26 08:11:03 -0500
commite82a1567e4b22eb035da2499d20ddd573c9acf75 (patch)
tree0cf697f96e734a846ee1cbc598beebcc7be10117 /drivers/s390
parent4beee64685e116b01c47655daf6d88df87e053c8 (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.c98
-rw-r--r--drivers/s390/cio/css.c120
-rw-r--r--drivers/s390/cio/css.h3
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
139static int 139static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data)
140s390_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
209static int 204static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data)
210s390_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
256static int 250static 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);
284out: 270out:
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
312static int 299static 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
503static int 490static 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
518static int 504static 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
571static void __s390_subchannel_vary_chpid(struct subchannel *sch, 550static 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
619static int s390_subchannel_vary_chpid_off(struct device *dev, void *data) 598static 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
631static int s390_subchannel_vary_chpid_on(struct device *dev, void *data) 606static 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
54struct 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
61static 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
73static 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
83int 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);
104out:
105 idset_free(cb.set);
106
107 return rc;
108}
109
54static struct subchannel * 110static struct subchannel *
55css_alloc_subchannel(struct subchannel_id schid) 111css_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
405static void css_slow_path_func(struct work_struct *unused) 461static 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
478static 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
506static 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
421static DECLARE_WORK(slow_path_work, css_slow_path_func); 513static 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. */
445static int reprobe_subchannel(struct subchannel_id schid, void *data) 537static 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 *);
91extern void css_sch_device_unregister(struct subchannel *); 91extern void css_sch_device_unregister(struct subchannel *);
92extern struct subchannel * get_subchannel_by_schid(struct subchannel_id); 92extern struct subchannel * get_subchannel_by_schid(struct subchannel_id);
93extern int css_init_done; 93extern int css_init_done;
94int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *),
95 int (*fn_unknown)(struct subchannel_id,
96 void *), void *data);
94extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *); 97extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
95extern void css_process_crw(int, int); 98extern void css_process_crw(int, int);
96extern void css_reiterate_subchannels(void); 99extern void css_reiterate_subchannels(void);