diff options
Diffstat (limited to 'drivers/s390/cio/css.c')
-rw-r--r-- | drivers/s390/cio/css.c | 69 |
1 files changed, 38 insertions, 31 deletions
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 7e4d57b4266f..5137dafd1e8d 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c | |||
@@ -33,7 +33,7 @@ struct device css_bus_device = { | |||
33 | }; | 33 | }; |
34 | 34 | ||
35 | static struct subchannel * | 35 | static struct subchannel * |
36 | css_alloc_subchannel(int irq) | 36 | css_alloc_subchannel(struct subchannel_id schid) |
37 | { | 37 | { |
38 | struct subchannel *sch; | 38 | struct subchannel *sch; |
39 | int ret; | 39 | int ret; |
@@ -41,13 +41,11 @@ css_alloc_subchannel(int irq) | |||
41 | sch = kmalloc (sizeof (*sch), GFP_KERNEL | GFP_DMA); | 41 | sch = kmalloc (sizeof (*sch), GFP_KERNEL | GFP_DMA); |
42 | if (sch == NULL) | 42 | if (sch == NULL) |
43 | return ERR_PTR(-ENOMEM); | 43 | return ERR_PTR(-ENOMEM); |
44 | ret = cio_validate_subchannel (sch, irq); | 44 | ret = cio_validate_subchannel (sch, schid); |
45 | if (ret < 0) { | 45 | if (ret < 0) { |
46 | kfree(sch); | 46 | kfree(sch); |
47 | return ERR_PTR(ret); | 47 | return ERR_PTR(ret); |
48 | } | 48 | } |
49 | if (irq > highest_subchannel) | ||
50 | highest_subchannel = irq; | ||
51 | 49 | ||
52 | if (sch->st != SUBCHANNEL_TYPE_IO) { | 50 | if (sch->st != SUBCHANNEL_TYPE_IO) { |
53 | /* For now we ignore all non-io subchannels. */ | 51 | /* For now we ignore all non-io subchannels. */ |
@@ -87,7 +85,7 @@ css_subchannel_release(struct device *dev) | |||
87 | struct subchannel *sch; | 85 | struct subchannel *sch; |
88 | 86 | ||
89 | sch = to_subchannel(dev); | 87 | sch = to_subchannel(dev); |
90 | if (!cio_is_console(sch->irq)) | 88 | if (!cio_is_console(sch->schid)) |
91 | kfree(sch); | 89 | kfree(sch); |
92 | } | 90 | } |
93 | 91 | ||
@@ -114,12 +112,12 @@ css_register_subchannel(struct subchannel *sch) | |||
114 | } | 112 | } |
115 | 113 | ||
116 | int | 114 | int |
117 | css_probe_device(int irq) | 115 | css_probe_device(struct subchannel_id schid) |
118 | { | 116 | { |
119 | int ret; | 117 | int ret; |
120 | struct subchannel *sch; | 118 | struct subchannel *sch; |
121 | 119 | ||
122 | sch = css_alloc_subchannel(irq); | 120 | sch = css_alloc_subchannel(schid); |
123 | if (IS_ERR(sch)) | 121 | if (IS_ERR(sch)) |
124 | return PTR_ERR(sch); | 122 | return PTR_ERR(sch); |
125 | ret = css_register_subchannel(sch); | 123 | ret = css_register_subchannel(sch); |
@@ -132,26 +130,26 @@ static int | |||
132 | check_subchannel(struct device * dev, void * data) | 130 | check_subchannel(struct device * dev, void * data) |
133 | { | 131 | { |
134 | struct subchannel *sch; | 132 | struct subchannel *sch; |
135 | int irq = (unsigned long)data; | 133 | struct subchannel_id *schid = data; |
136 | 134 | ||
137 | sch = to_subchannel(dev); | 135 | sch = to_subchannel(dev); |
138 | return (sch->irq == irq); | 136 | return schid_equal(&sch->schid, schid); |
139 | } | 137 | } |
140 | 138 | ||
141 | struct subchannel * | 139 | struct subchannel * |
142 | get_subchannel_by_schid(int irq) | 140 | get_subchannel_by_schid(struct subchannel_id schid) |
143 | { | 141 | { |
144 | struct device *dev; | 142 | struct device *dev; |
145 | 143 | ||
146 | dev = bus_find_device(&css_bus_type, NULL, | 144 | dev = bus_find_device(&css_bus_type, NULL, |
147 | (void *)(unsigned long)irq, check_subchannel); | 145 | (void *)&schid, check_subchannel); |
148 | 146 | ||
149 | return dev ? to_subchannel(dev) : NULL; | 147 | return dev ? to_subchannel(dev) : NULL; |
150 | } | 148 | } |
151 | 149 | ||
152 | 150 | ||
153 | static inline int | 151 | static inline int |
154 | css_get_subchannel_status(struct subchannel *sch, int schid) | 152 | css_get_subchannel_status(struct subchannel *sch, struct subchannel_id schid) |
155 | { | 153 | { |
156 | struct schib schib; | 154 | struct schib schib; |
157 | int cc; | 155 | int cc; |
@@ -170,13 +168,13 @@ css_get_subchannel_status(struct subchannel *sch, int schid) | |||
170 | } | 168 | } |
171 | 169 | ||
172 | static int | 170 | static int |
173 | css_evaluate_subchannel(int irq, int slow) | 171 | css_evaluate_subchannel(struct subchannel_id schid, int slow) |
174 | { | 172 | { |
175 | int event, ret, disc; | 173 | int event, ret, disc; |
176 | struct subchannel *sch; | 174 | struct subchannel *sch; |
177 | unsigned long flags; | 175 | unsigned long flags; |
178 | 176 | ||
179 | sch = get_subchannel_by_schid(irq); | 177 | sch = get_subchannel_by_schid(schid); |
180 | disc = sch ? device_is_disconnected(sch) : 0; | 178 | disc = sch ? device_is_disconnected(sch) : 0; |
181 | if (disc && slow) { | 179 | if (disc && slow) { |
182 | if (sch) | 180 | if (sch) |
@@ -194,9 +192,10 @@ css_evaluate_subchannel(int irq, int slow) | |||
194 | put_device(&sch->dev); | 192 | put_device(&sch->dev); |
195 | return -EAGAIN; /* Will be done on the slow path. */ | 193 | return -EAGAIN; /* Will be done on the slow path. */ |
196 | } | 194 | } |
197 | event = css_get_subchannel_status(sch, irq); | 195 | event = css_get_subchannel_status(sch, schid); |
198 | CIO_MSG_EVENT(4, "Evaluating schid %04x, event %d, %s, %s path.\n", | 196 | CIO_MSG_EVENT(4, "Evaluating schid %04x, event %d, %s, %s path.\n", |
199 | irq, event, sch?(disc?"disconnected":"normal"):"unknown", | 197 | schid.sch_no, event, |
198 | sch?(disc?"disconnected":"normal"):"unknown", | ||
200 | slow?"slow":"fast"); | 199 | slow?"slow":"fast"); |
201 | switch (event) { | 200 | switch (event) { |
202 | case CIO_NO_PATH: | 201 | case CIO_NO_PATH: |
@@ -253,7 +252,7 @@ css_evaluate_subchannel(int irq, int slow) | |||
253 | sch->schib.pmcw.intparm = 0; | 252 | sch->schib.pmcw.intparm = 0; |
254 | cio_modify(sch); | 253 | cio_modify(sch); |
255 | put_device(&sch->dev); | 254 | put_device(&sch->dev); |
256 | ret = css_probe_device(irq); | 255 | ret = css_probe_device(schid); |
257 | } else { | 256 | } else { |
258 | /* | 257 | /* |
259 | * We can't immediately deregister the disconnected | 258 | * We can't immediately deregister the disconnected |
@@ -272,7 +271,7 @@ css_evaluate_subchannel(int irq, int slow) | |||
272 | device_trigger_reprobe(sch); | 271 | device_trigger_reprobe(sch); |
273 | spin_unlock_irqrestore(&sch->lock, flags); | 272 | spin_unlock_irqrestore(&sch->lock, flags); |
274 | } | 273 | } |
275 | ret = sch ? 0 : css_probe_device(irq); | 274 | ret = sch ? 0 : css_probe_device(schid); |
276 | break; | 275 | break; |
277 | default: | 276 | default: |
278 | BUG(); | 277 | BUG(); |
@@ -284,10 +283,12 @@ css_evaluate_subchannel(int irq, int slow) | |||
284 | static void | 283 | static void |
285 | css_rescan_devices(void) | 284 | css_rescan_devices(void) |
286 | { | 285 | { |
287 | int irq, ret; | 286 | int ret; |
287 | struct subchannel_id schid; | ||
288 | 288 | ||
289 | for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) { | 289 | init_subchannel_id(&schid); |
290 | ret = css_evaluate_subchannel(irq, 1); | 290 | do { |
291 | ret = css_evaluate_subchannel(schid, 1); | ||
291 | /* No more memory. It doesn't make sense to continue. No | 292 | /* No more memory. It doesn't make sense to continue. No |
292 | * panic because this can happen in midflight and just | 293 | * panic because this can happen in midflight and just |
293 | * because we can't use a new device is no reason to crash | 294 | * because we can't use a new device is no reason to crash |
@@ -297,12 +298,12 @@ css_rescan_devices(void) | |||
297 | /* -ENXIO indicates that there are no more subchannels. */ | 298 | /* -ENXIO indicates that there are no more subchannels. */ |
298 | if (ret == -ENXIO) | 299 | if (ret == -ENXIO) |
299 | break; | 300 | break; |
300 | } | 301 | } while (schid.sch_no++ < __MAX_SUBCHANNEL); |
301 | } | 302 | } |
302 | 303 | ||
303 | struct slow_subchannel { | 304 | struct slow_subchannel { |
304 | struct list_head slow_list; | 305 | struct list_head slow_list; |
305 | unsigned long schid; | 306 | struct subchannel_id schid; |
306 | }; | 307 | }; |
307 | 308 | ||
308 | static LIST_HEAD(slow_subchannels_head); | 309 | static LIST_HEAD(slow_subchannels_head); |
@@ -357,20 +358,24 @@ int | |||
357 | css_process_crw(int irq) | 358 | css_process_crw(int irq) |
358 | { | 359 | { |
359 | int ret; | 360 | int ret; |
361 | struct subchannel_id mchk_schid; | ||
360 | 362 | ||
361 | CIO_CRW_EVENT(2, "source is subchannel %04X\n", irq); | 363 | CIO_CRW_EVENT(2, "source is subchannel %04X\n", irq); |
362 | 364 | ||
363 | if (need_rescan) | 365 | if (need_rescan) |
364 | /* We need to iterate all subchannels anyway. */ | 366 | /* We need to iterate all subchannels anyway. */ |
365 | return -EAGAIN; | 367 | return -EAGAIN; |
368 | |||
369 | init_subchannel_id(&mchk_schid); | ||
370 | mchk_schid.sch_no = irq; | ||
366 | /* | 371 | /* |
367 | * Since we are always presented with IPI in the CRW, we have to | 372 | * Since we are always presented with IPI in the CRW, we have to |
368 | * use stsch() to find out if the subchannel in question has come | 373 | * use stsch() to find out if the subchannel in question has come |
369 | * or gone. | 374 | * or gone. |
370 | */ | 375 | */ |
371 | ret = css_evaluate_subchannel(irq, 0); | 376 | ret = css_evaluate_subchannel(mchk_schid, 0); |
372 | if (ret == -EAGAIN) { | 377 | if (ret == -EAGAIN) { |
373 | if (css_enqueue_subchannel_slow(irq)) { | 378 | if (css_enqueue_subchannel_slow(mchk_schid)) { |
374 | css_clear_subchannel_slow_list(); | 379 | css_clear_subchannel_slow_list(); |
375 | need_rescan = 1; | 380 | need_rescan = 1; |
376 | } | 381 | } |
@@ -404,7 +409,8 @@ css_generate_pgid(void) | |||
404 | static int __init | 409 | static int __init |
405 | init_channel_subsystem (void) | 410 | init_channel_subsystem (void) |
406 | { | 411 | { |
407 | int ret, irq; | 412 | int ret; |
413 | struct subchannel_id schid; | ||
408 | 414 | ||
409 | if (chsc_determine_css_characteristics() == 0) | 415 | if (chsc_determine_css_characteristics() == 0) |
410 | css_characteristics_avail = 1; | 416 | css_characteristics_avail = 1; |
@@ -420,13 +426,14 @@ init_channel_subsystem (void) | |||
420 | 426 | ||
421 | ctl_set_bit(6, 28); | 427 | ctl_set_bit(6, 28); |
422 | 428 | ||
423 | for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) { | 429 | init_subchannel_id(&schid); |
430 | do { | ||
424 | struct subchannel *sch; | 431 | struct subchannel *sch; |
425 | 432 | ||
426 | if (cio_is_console(irq)) | 433 | if (cio_is_console(schid)) |
427 | sch = cio_get_console_subchannel(); | 434 | sch = cio_get_console_subchannel(); |
428 | else { | 435 | else { |
429 | sch = css_alloc_subchannel(irq); | 436 | sch = css_alloc_subchannel(schid); |
430 | if (IS_ERR(sch)) | 437 | if (IS_ERR(sch)) |
431 | ret = PTR_ERR(sch); | 438 | ret = PTR_ERR(sch); |
432 | else | 439 | else |
@@ -448,7 +455,7 @@ init_channel_subsystem (void) | |||
448 | * console subchannel. | 455 | * console subchannel. |
449 | */ | 456 | */ |
450 | css_register_subchannel(sch); | 457 | css_register_subchannel(sch); |
451 | } | 458 | } while (schid.sch_no++ < __MAX_SUBCHANNEL); |
452 | return 0; | 459 | return 0; |
453 | 460 | ||
454 | out_bus: | 461 | out_bus: |
@@ -482,7 +489,7 @@ struct bus_type css_bus_type = { | |||
482 | subsys_initcall(init_channel_subsystem); | 489 | subsys_initcall(init_channel_subsystem); |
483 | 490 | ||
484 | int | 491 | int |
485 | css_enqueue_subchannel_slow(unsigned long schid) | 492 | css_enqueue_subchannel_slow(struct subchannel_id schid) |
486 | { | 493 | { |
487 | struct slow_subchannel *new_slow_sch; | 494 | struct slow_subchannel *new_slow_sch; |
488 | unsigned long flags; | 495 | unsigned long flags; |