aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSebastian Ott <sebott@linux.vnet.ibm.com>2008-12-25 07:39:13 -0500
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2008-12-25 07:39:10 -0500
commit13952ec12dfeea793ff83c2a96139ed57eb0b897 (patch)
tree3311df62a1794bf95f78b2c57f25f0a79dc84523 /drivers
parentcdb912a40df8b8507ab60b3d52f9980c0ba1f44d (diff)
[S390] cio: introduce cio_commit_config
To change the configuration of a subchannel we alter the modifiable bits of the subchannel's schib field and issue a modify subchannel. There can be the case that not all changes were applied -or worse- quietly overwritten by the hardware. With the next store subchannel we obtain the current state of the hardware but lose our target configuration. With this patch we introduce a subchannel_config structure which contains the target subchannel configuration. Additionally the msch wrapper cio_modify is replaced with cio_commit_config which copies the desired changes to a temporary schib. msch is then called with the temporary schib. This schib is only written back to the subchannel if all changes were applied. Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/s390/cio/cio.c134
-rw-r--r--drivers/s390/cio/cio.h16
-rw-r--r--drivers/s390/cio/cmf.c55
-rw-r--r--drivers/s390/cio/css.c4
-rw-r--r--drivers/s390/cio/device.c36
-rw-r--r--drivers/s390/cio/device.h1
-rw-r--r--drivers/s390/cio/device_fsm.c11
7 files changed, 123 insertions, 134 deletions
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 9bdb463765c7..8f1cec499532 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -330,30 +330,70 @@ cio_cancel (struct subchannel *sch)
330 } 330 }
331} 331}
332 332
333
334static void cio_apply_config(struct subchannel *sch, struct schib *schib)
335{
336 schib->pmcw.intparm = sch->config.intparm;
337 schib->pmcw.mbi = sch->config.mbi;
338 schib->pmcw.isc = sch->config.isc;
339 schib->pmcw.ena = sch->config.ena;
340 schib->pmcw.mme = sch->config.mme;
341 schib->pmcw.mp = sch->config.mp;
342 schib->pmcw.csense = sch->config.csense;
343 schib->pmcw.mbfc = sch->config.mbfc;
344 if (sch->config.mbfc)
345 schib->mba = sch->config.mba;
346}
347
348static int cio_check_config(struct subchannel *sch, struct schib *schib)
349{
350 return (schib->pmcw.intparm == sch->config.intparm) &&
351 (schib->pmcw.mbi == sch->config.mbi) &&
352 (schib->pmcw.isc == sch->config.isc) &&
353 (schib->pmcw.ena == sch->config.ena) &&
354 (schib->pmcw.mme == sch->config.mme) &&
355 (schib->pmcw.mp == sch->config.mp) &&
356 (schib->pmcw.csense == sch->config.csense) &&
357 (schib->pmcw.mbfc == sch->config.mbfc) &&
358 (!sch->config.mbfc || (schib->mba == sch->config.mba));
359}
360
333/* 361/*
334 * Function: cio_modify 362 * cio_commit_config - apply configuration to the subchannel
335 * Issues a "Modify Subchannel" on the specified subchannel
336 */ 363 */
337int 364int cio_commit_config(struct subchannel *sch)
338cio_modify (struct subchannel *sch)
339{ 365{
340 int ccode, retry, ret; 366 struct schib schib;
367 int ccode, retry, ret = 0;
368
369 if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib))
370 return -ENODEV;
341 371
342 ret = 0;
343 for (retry = 0; retry < 5; retry++) { 372 for (retry = 0; retry < 5; retry++) {
344 ccode = msch_err (sch->schid, &sch->schib); 373 /* copy desired changes to local schib */
345 if (ccode < 0) /* -EIO if msch gets a program check. */ 374 cio_apply_config(sch, &schib);
375 ccode = msch_err(sch->schid, &schib);
376 if (ccode < 0) /* -EIO if msch gets a program check. */
346 return ccode; 377 return ccode;
347 switch (ccode) { 378 switch (ccode) {
348 case 0: /* successfull */ 379 case 0: /* successfull */
349 return 0; 380 if (stsch(sch->schid, &schib) ||
350 case 1: /* status pending */ 381 !css_sch_is_valid(&schib))
382 return -ENODEV;
383 if (cio_check_config(sch, &schib)) {
384 /* commit changes from local schib */
385 memcpy(&sch->schib, &schib, sizeof(schib));
386 return 0;
387 }
388 ret = -EAGAIN;
389 break;
390 case 1: /* status pending */
351 return -EBUSY; 391 return -EBUSY;
352 case 2: /* busy */ 392 case 2: /* busy */
353 udelay (100); /* allow for recovery */ 393 udelay(100); /* allow for recovery */
354 ret = -EBUSY; 394 ret = -EBUSY;
355 break; 395 break;
356 case 3: /* not operational */ 396 case 3: /* not operational */
357 return -ENODEV; 397 return -ENODEV;
358 } 398 }
359 } 399 }
@@ -396,32 +436,24 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
396 if (cio_update_schib(sch)) 436 if (cio_update_schib(sch))
397 return -ENODEV; 437 return -ENODEV;
398 438
399 for (retry = 5, ret = 0; retry > 0; retry--) { 439 sch->config.ena = 1;
400 sch->schib.pmcw.ena = 1; 440 sch->config.isc = sch->isc;
401 sch->schib.pmcw.isc = sch->isc; 441 sch->config.intparm = intparm;
402 sch->schib.pmcw.intparm = intparm; 442
403 ret = cio_modify(sch); 443 for (retry = 0; retry < 3; retry++) {
404 if (ret == -ENODEV) 444 ret = cio_commit_config(sch);
405 break; 445 if (ret == -EIO) {
406 if (ret == -EIO)
407 /* 446 /*
408 * Got a program check in cio_modify. Try without 447 * Got a program check in msch. Try without
409 * the concurrent sense bit the next time. 448 * the concurrent sense bit the next time.
410 */ 449 */
411 sch->schib.pmcw.csense = 0; 450 sch->config.csense = 0;
412 if (ret == 0) { 451 } else if (ret == -EBUSY) {
413 if (cio_update_schib(sch)) {
414 ret = -ENODEV;
415 break;
416 }
417 if (sch->schib.pmcw.ena)
418 break;
419 }
420 if (ret == -EBUSY) {
421 struct irb irb; 452 struct irb irb;
422 if (tsch(sch->schid, &irb) != 0) 453 if (tsch(sch->schid, &irb) != 0)
423 break; 454 break;
424 } 455 } else
456 break;
425 } 457 }
426 sprintf (dbf_txt, "ret:%d", ret); 458 sprintf (dbf_txt, "ret:%d", ret);
427 CIO_TRACE_EVENT (2, dbf_txt); 459 CIO_TRACE_EVENT (2, dbf_txt);
@@ -436,7 +468,6 @@ EXPORT_SYMBOL_GPL(cio_enable_subchannel);
436int cio_disable_subchannel(struct subchannel *sch) 468int cio_disable_subchannel(struct subchannel *sch)
437{ 469{
438 char dbf_txt[15]; 470 char dbf_txt[15];
439 int retry;
440 int ret; 471 int ret;
441 472
442 CIO_TRACE_EVENT (2, "dissch"); 473 CIO_TRACE_EVENT (2, "dissch");
@@ -454,27 +485,9 @@ int cio_disable_subchannel(struct subchannel *sch)
454 */ 485 */
455 return -EBUSY; 486 return -EBUSY;
456 487
457 for (retry = 5, ret = 0; retry > 0; retry--) { 488 sch->config.ena = 0;
458 sch->schib.pmcw.ena = 0; 489 ret = cio_commit_config(sch);
459 ret = cio_modify(sch); 490
460 if (ret == -ENODEV)
461 break;
462 if (ret == -EBUSY)
463 /*
464 * The subchannel is busy or status pending.
465 * We'll disable when the next interrupt was delivered
466 * via the state machine.
467 */
468 break;
469 if (ret == 0) {
470 if (cio_update_schib(sch)) {
471 ret = -ENODEV;
472 break;
473 }
474 if (!sch->schib.pmcw.ena)
475 break;
476 }
477 }
478 sprintf (dbf_txt, "ret:%d", ret); 491 sprintf (dbf_txt, "ret:%d", ret);
479 CIO_TRACE_EVENT (2, dbf_txt); 492 CIO_TRACE_EVENT (2, dbf_txt);
480 return ret; 493 return ret;
@@ -817,10 +830,9 @@ cio_probe_console(void)
817 * enable console I/O-interrupt subclass 830 * enable console I/O-interrupt subclass
818 */ 831 */
819 isc_register(CONSOLE_ISC); 832 isc_register(CONSOLE_ISC);
820 console_subchannel.schib.pmcw.isc = CONSOLE_ISC; 833 console_subchannel.config.isc = CONSOLE_ISC;
821 console_subchannel.schib.pmcw.intparm = 834 console_subchannel.config.intparm = (u32)(addr_t)&console_subchannel;
822 (u32)(addr_t)&console_subchannel; 835 ret = cio_commit_config(&console_subchannel);
823 ret = cio_modify(&console_subchannel);
824 if (ret) { 836 if (ret) {
825 isc_unregister(CONSOLE_ISC); 837 isc_unregister(CONSOLE_ISC);
826 console_subchannel_in_use = 0; 838 console_subchannel_in_use = 0;
@@ -832,8 +844,8 @@ cio_probe_console(void)
832void 844void
833cio_release_console(void) 845cio_release_console(void)
834{ 846{
835 console_subchannel.schib.pmcw.intparm = 0; 847 console_subchannel.config.intparm = 0;
836 cio_modify(&console_subchannel); 848 cio_commit_config(&console_subchannel);
837 isc_unregister(CONSOLE_ISC); 849 isc_unregister(CONSOLE_ISC);
838 console_subchannel_in_use = 0; 850 console_subchannel_in_use = 0;
839} 851}
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index fb125efd6891..5150fba742ac 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -45,6 +45,19 @@ struct pmcw {
45 /* ... in an operand exception. */ 45 /* ... in an operand exception. */
46} __attribute__ ((packed)); 46} __attribute__ ((packed));
47 47
48/* Target SCHIB configuration. */
49struct schib_config {
50 u64 mba;
51 u32 intparm;
52 u16 mbi;
53 u32 isc:3;
54 u32 ena:1;
55 u32 mme:2;
56 u32 mp:1;
57 u32 csense:1;
58 u32 mbfc:1;
59} __attribute__ ((packed));
60
48/* 61/*
49 * subchannel information block 62 * subchannel information block
50 */ 63 */
@@ -83,6 +96,7 @@ struct subchannel {
83 struct css_driver *driver; 96 struct css_driver *driver;
84 void *private; /* private per subchannel type data */ 97 void *private; /* private per subchannel type data */
85 struct work_struct work; 98 struct work_struct work;
99 struct schib_config config;
86} __attribute__ ((aligned(8))); 100} __attribute__ ((aligned(8)));
87 101
88#define IO_INTERRUPT_TYPE 0 /* I/O interrupt type */ 102#define IO_INTERRUPT_TYPE 0 /* I/O interrupt type */
@@ -101,8 +115,8 @@ extern int cio_start_key (struct subchannel *, struct ccw1 *, __u8, __u8);
101extern int cio_cancel (struct subchannel *); 115extern int cio_cancel (struct subchannel *);
102extern int cio_set_options (struct subchannel *, int); 116extern int cio_set_options (struct subchannel *, int);
103extern int cio_get_options (struct subchannel *); 117extern int cio_get_options (struct subchannel *);
104extern int cio_modify (struct subchannel *);
105extern int cio_update_schib(struct subchannel *sch); 118extern int cio_update_schib(struct subchannel *sch);
119extern int cio_commit_config(struct subchannel *sch);
106 120
107int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key); 121int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key);
108int cio_tm_intrg(struct subchannel *sch); 122int cio_tm_intrg(struct subchannel *sch);
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 288482b21048..6ddd02308e14 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -185,58 +185,19 @@ static inline void cmf_activate(void *area, unsigned int onoff)
185static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc, 185static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc,
186 unsigned long address) 186 unsigned long address)
187{ 187{
188 int ret;
189 int retry;
190 struct subchannel *sch; 188 struct subchannel *sch;
191 struct schib *schib;
192 189
193 sch = to_subchannel(cdev->dev.parent); 190 sch = to_subchannel(cdev->dev.parent);
194 schib = &sch->schib;
195 /* msch can silently fail, so do it again if necessary */
196 for (retry = 0; retry < 3; retry++) {
197 /* prepare schib */
198 if (cio_update_schib(sch))
199 return -ENODEV;
200 schib->pmcw.mme = mme;
201 schib->pmcw.mbfc = mbfc;
202 /* address can be either a block address or a block index */
203 if (mbfc)
204 schib->mba = address;
205 else
206 schib->pmcw.mbi = address;
207
208 /* try to submit it */
209 switch(ret = msch_err(sch->schid, schib)) {
210 case 0:
211 break;
212 case 1:
213 case 2: /* in I/O or status pending */
214 ret = -EBUSY;
215 break;
216 case 3: /* subchannel is no longer valid */
217 ret = -ENODEV;
218 break;
219 default: /* msch caught an exception */
220 ret = -EINVAL;
221 break;
222 }
223 if (cio_update_schib(sch))
224 return -ENODEV;
225
226 if (ret)
227 break;
228 191
229 /* check if it worked */ 192 sch->config.mme = mme;
230 if (schib->pmcw.mme == mme && 193 sch->config.mbfc = mbfc;
231 schib->pmcw.mbfc == mbfc && 194 /* address can be either a block address or a block index */
232 (mbfc ? (schib->mba == address) 195 if (mbfc)
233 : (schib->pmcw.mbi == address))) 196 sch->config.mba = address;
234 return 0; 197 else
198 sch->config.mbi = address;
235 199
236 ret = -EINVAL; 200 return cio_commit_config(sch);
237 }
238
239 return ret;
240} 201}
241 202
242struct set_schib_struct { 203struct set_schib_struct {
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 76bbb1e74c29..1b2d5149de5a 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -128,8 +128,8 @@ css_free_subchannel(struct subchannel *sch)
128{ 128{
129 if (sch) { 129 if (sch) {
130 /* Reset intparm to zeroes. */ 130 /* Reset intparm to zeroes. */
131 sch->schib.pmcw.intparm = 0; 131 sch->config.intparm = 0;
132 cio_modify(sch); 132 cio_commit_config(sch);
133 kfree(sch->lock); 133 kfree(sch->lock);
134 kfree(sch); 134 kfree(sch);
135 } 135 }
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 51e94212bf66..d3127018fb81 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -1020,8 +1020,8 @@ static void ccw_device_call_sch_unregister(struct work_struct *work)
1020 sch = to_subchannel(cdev->dev.parent); 1020 sch = to_subchannel(cdev->dev.parent);
1021 css_sch_device_unregister(sch); 1021 css_sch_device_unregister(sch);
1022 /* Reset intparm to zeroes. */ 1022 /* Reset intparm to zeroes. */
1023 sch->schib.pmcw.intparm = 0; 1023 sch->config.intparm = 0;
1024 cio_modify(sch); 1024 cio_commit_config(sch);
1025 /* Release cdev reference for workqueue processing.*/ 1025 /* Release cdev reference for workqueue processing.*/
1026 put_device(&cdev->dev); 1026 put_device(&cdev->dev);
1027 /* Release subchannel reference for local processing. */ 1027 /* Release subchannel reference for local processing. */
@@ -1148,8 +1148,8 @@ static void ccw_device_move_to_sch(struct work_struct *work)
1148 spin_unlock_irq(former_parent->lock); 1148 spin_unlock_irq(former_parent->lock);
1149 css_sch_device_unregister(former_parent); 1149 css_sch_device_unregister(former_parent);
1150 /* Reset intparm to zeroes. */ 1150 /* Reset intparm to zeroes. */
1151 former_parent->schib.pmcw.intparm = 0; 1151 former_parent->config.intparm = 0;
1152 cio_modify(former_parent); 1152 cio_commit_config(former_parent);
1153 } 1153 }
1154 sch_attach_device(sch, cdev); 1154 sch_attach_device(sch, cdev);
1155out: 1155out:
@@ -1170,6 +1170,14 @@ static void io_subchannel_irq(struct subchannel *sch)
1170 dev_fsm_event(cdev, DEV_EVENT_INTERRUPT); 1170 dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
1171} 1171}
1172 1172
1173void io_subchannel_init_config(struct subchannel *sch)
1174{
1175 memset(&sch->config, 0, sizeof(sch->config));
1176 sch->config.csense = 1;
1177 if ((sch->lpm & (sch->lpm - 1)) != 0)
1178 sch->config.mp = 1;
1179}
1180
1173static void io_subchannel_init_fields(struct subchannel *sch) 1181static void io_subchannel_init_fields(struct subchannel *sch)
1174{ 1182{
1175 if (cio_is_console(sch->schid)) 1183 if (cio_is_console(sch->schid))
@@ -1184,16 +1192,8 @@ static void io_subchannel_init_fields(struct subchannel *sch)
1184 sch->schib.pmcw.dev, sch->schid.ssid, 1192 sch->schib.pmcw.dev, sch->schid.ssid,
1185 sch->schid.sch_no, sch->schib.pmcw.pim, 1193 sch->schid.sch_no, sch->schib.pmcw.pim,
1186 sch->schib.pmcw.pam, sch->schib.pmcw.pom); 1194 sch->schib.pmcw.pam, sch->schib.pmcw.pom);
1187 /* Initially set up some fields in the pmcw. */ 1195
1188 sch->schib.pmcw.ena = 0; 1196 io_subchannel_init_config(sch);
1189 sch->schib.pmcw.csense = 1; /* concurrent sense */
1190 if ((sch->lpm & (sch->lpm - 1)) != 0)
1191 sch->schib.pmcw.mp = 1; /* multipath mode */
1192 /* clean up possible residual cmf stuff */
1193 sch->schib.pmcw.mme = 0;
1194 sch->schib.pmcw.mbfc = 0;
1195 sch->schib.pmcw.mbi = 0;
1196 sch->schib.mba = 0;
1197} 1197}
1198 1198
1199static void io_subchannel_do_unreg(struct work_struct *work) 1199static void io_subchannel_do_unreg(struct work_struct *work)
@@ -1203,8 +1203,8 @@ static void io_subchannel_do_unreg(struct work_struct *work)
1203 sch = container_of(work, struct subchannel, work); 1203 sch = container_of(work, struct subchannel, work);
1204 css_sch_device_unregister(sch); 1204 css_sch_device_unregister(sch);
1205 /* Reset intparm to zeroes. */ 1205 /* Reset intparm to zeroes. */
1206 sch->schib.pmcw.intparm = 0; 1206 sch->config.intparm = 0;
1207 cio_modify(sch); 1207 cio_commit_config(sch);
1208 put_device(&sch->dev); 1208 put_device(&sch->dev);
1209} 1209}
1210 1210
@@ -1680,8 +1680,8 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow)
1680 spin_lock_irqsave(sch->lock, flags); 1680 spin_lock_irqsave(sch->lock, flags);
1681 1681
1682 /* Reset intparm to zeroes. */ 1682 /* Reset intparm to zeroes. */
1683 sch->schib.pmcw.intparm = 0; 1683 sch->config.intparm = 0;
1684 cio_modify(sch); 1684 cio_commit_config(sch);
1685 break; 1685 break;
1686 case REPROBE: 1686 case REPROBE:
1687 ccw_device_trigger_reprobe(cdev); 1687 ccw_device_trigger_reprobe(cdev);
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 104ed669db43..0f2e63ea48de 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -76,6 +76,7 @@ extern wait_queue_head_t ccw_device_init_wq;
76extern atomic_t ccw_device_init_count; 76extern atomic_t ccw_device_init_count;
77 77
78void io_subchannel_recog_done(struct ccw_device *cdev); 78void io_subchannel_recog_done(struct ccw_device *cdev);
79void io_subchannel_init_config(struct subchannel *sch);
79 80
80int ccw_device_cancel_halt_clear(struct ccw_device *); 81int ccw_device_cancel_halt_clear(struct ccw_device *);
81 82
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index e1a3786779ba..9e249675c98d 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -1026,11 +1026,12 @@ void ccw_device_trigger_reprobe(struct ccw_device *cdev)
1026 * we have before performing device selection :/ 1026 * we have before performing device selection :/
1027 */ 1027 */
1028 sch->lpm = sch->schib.pmcw.pam & sch->opm; 1028 sch->lpm = sch->schib.pmcw.pam & sch->opm;
1029 /* Re-set some bits in the pmcw that were lost. */ 1029 /*
1030 sch->schib.pmcw.csense = 1; 1030 * Use the initial configuration since we can't be shure that the old
1031 sch->schib.pmcw.ena = 0; 1031 * paths are valid.
1032 if ((sch->lpm & (sch->lpm - 1)) != 0) 1032 */
1033 sch->schib.pmcw.mp = 1; 1033 io_subchannel_init_config(sch);
1034
1034 /* We should also udate ssd info, but this has to wait. */ 1035 /* We should also udate ssd info, but this has to wait. */
1035 /* Check if this is another device which appeared on the same sch. */ 1036 /* Check if this is another device which appeared on the same sch. */
1036 if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { 1037 if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {