aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio/device_pgid.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/cio/device_pgid.c')
-rw-r--r--drivers/s390/cio/device_pgid.c149
1 files changed, 120 insertions, 29 deletions
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 54cb64ed0786..1693a102dcfe 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -24,6 +24,21 @@
24#include "ioasm.h" 24#include "ioasm.h"
25 25
26/* 26/*
27 * Helper function called from interrupt context to decide whether an
28 * operation should be tried again.
29 */
30static int __ccw_device_should_retry(struct scsw *scsw)
31{
32 /* CC is only valid if start function bit is set. */
33 if ((scsw->fctl & SCSW_FCTL_START_FUNC) && scsw->cc == 1)
34 return 1;
35 /* No more activity. For sense and set PGID we stubbornly try again. */
36 if (!scsw->actl)
37 return 1;
38 return 0;
39}
40
41/*
27 * Start Sense Path Group ID helper function. Used in ccw_device_recog 42 * Start Sense Path Group ID helper function. Used in ccw_device_recog
28 * and ccw_device_sense_pgid. 43 * and ccw_device_sense_pgid.
29 */ 44 */
@@ -33,12 +48,17 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
33 struct subchannel *sch; 48 struct subchannel *sch;
34 struct ccw1 *ccw; 49 struct ccw1 *ccw;
35 int ret; 50 int ret;
51 int i;
36 52
37 sch = to_subchannel(cdev->dev.parent); 53 sch = to_subchannel(cdev->dev.parent);
54 /* Return if we already checked on all paths. */
55 if (cdev->private->imask == 0)
56 return (sch->lpm == 0) ? -ENODEV : -EACCES;
57 i = 8 - ffs(cdev->private->imask);
58
38 /* Setup sense path group id channel program. */ 59 /* Setup sense path group id channel program. */
39 ccw = cdev->private->iccws; 60 ccw = cdev->private->iccws;
40 ccw->cmd_code = CCW_CMD_SENSE_PGID; 61 ccw->cmd_code = CCW_CMD_SENSE_PGID;
41 ccw->cda = (__u32) __pa (&cdev->private->pgid);
42 ccw->count = sizeof (struct pgid); 62 ccw->count = sizeof (struct pgid);
43 ccw->flags = CCW_FLAG_SLI; 63 ccw->flags = CCW_FLAG_SLI;
44 64
@@ -48,6 +68,7 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
48 ret = -ENODEV; 68 ret = -ENODEV;
49 while (cdev->private->imask != 0) { 69 while (cdev->private->imask != 0) {
50 /* Try every path multiple times. */ 70 /* Try every path multiple times. */
71 ccw->cda = (__u32) __pa (&cdev->private->pgid[i]);
51 if (cdev->private->iretry > 0) { 72 if (cdev->private->iretry > 0) {
52 cdev->private->iretry--; 73 cdev->private->iretry--;
53 ret = cio_start (sch, cdev->private->iccws, 74 ret = cio_start (sch, cdev->private->iccws,
@@ -64,7 +85,9 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
64 } 85 }
65 cdev->private->imask >>= 1; 86 cdev->private->imask >>= 1;
66 cdev->private->iretry = 5; 87 cdev->private->iretry = 5;
88 i++;
67 } 89 }
90
68 return ret; 91 return ret;
69} 92}
70 93
@@ -76,7 +99,7 @@ ccw_device_sense_pgid_start(struct ccw_device *cdev)
76 cdev->private->state = DEV_STATE_SENSE_PGID; 99 cdev->private->state = DEV_STATE_SENSE_PGID;
77 cdev->private->imask = 0x80; 100 cdev->private->imask = 0x80;
78 cdev->private->iretry = 5; 101 cdev->private->iretry = 5;
79 memset (&cdev->private->pgid, 0, sizeof (struct pgid)); 102 memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid));
80 ret = __ccw_device_sense_pgid_start(cdev); 103 ret = __ccw_device_sense_pgid_start(cdev);
81 if (ret && ret != -EBUSY) 104 if (ret && ret != -EBUSY)
82 ccw_device_sense_pgid_done(cdev, ret); 105 ccw_device_sense_pgid_done(cdev, ret);
@@ -91,6 +114,7 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
91{ 114{
92 struct subchannel *sch; 115 struct subchannel *sch;
93 struct irb *irb; 116 struct irb *irb;
117 int i;
94 118
95 sch = to_subchannel(cdev->dev.parent); 119 sch = to_subchannel(cdev->dev.parent);
96 irb = &cdev->private->irb; 120 irb = &cdev->private->irb;
@@ -124,7 +148,8 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
124 sch->schid.sch_no, sch->orb.lpm); 148 sch->schid.sch_no, sch->orb.lpm);
125 return -EACCES; 149 return -EACCES;
126 } 150 }
127 if (cdev->private->pgid.inf.ps.state2 == SNID_STATE2_RESVD_ELSE) { 151 i = 8 - ffs(cdev->private->imask);
152 if (cdev->private->pgid[i].inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
128 CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x " 153 CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x "
129 "is reserved by someone else\n", 154 "is reserved by someone else\n",
130 cdev->private->devno, sch->schid.ssid, 155 cdev->private->devno, sch->schid.ssid,
@@ -145,10 +170,10 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
145 int ret; 170 int ret;
146 171
147 irb = (struct irb *) __LC_IRB; 172 irb = (struct irb *) __LC_IRB;
148 /* Retry sense pgid for cc=1. */ 173
149 if (irb->scsw.stctl == 174 if (irb->scsw.stctl ==
150 (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { 175 (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
151 if (irb->scsw.cc == 1) { 176 if (__ccw_device_should_retry(&irb->scsw)) {
152 ret = __ccw_device_sense_pgid_start(cdev); 177 ret = __ccw_device_sense_pgid_start(cdev);
153 if (ret && ret != -EBUSY) 178 if (ret && ret != -EBUSY)
154 ccw_device_sense_pgid_done(cdev, ret); 179 ccw_device_sense_pgid_done(cdev, ret);
@@ -162,12 +187,6 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
162 memset(&cdev->private->irb, 0, sizeof(struct irb)); 187 memset(&cdev->private->irb, 0, sizeof(struct irb));
163 switch (ret) { 188 switch (ret) {
164 /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */ 189 /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */
165 case 0: /* Sense Path Group ID successful. */
166 if (cdev->private->pgid.inf.ps.state1 == SNID_STATE1_RESET)
167 memcpy(&cdev->private->pgid, &css[0]->global_pgid,
168 sizeof(struct pgid));
169 ccw_device_sense_pgid_done(cdev, 0);
170 break;
171 case -EOPNOTSUPP: /* Sense Path Group ID not supported */ 190 case -EOPNOTSUPP: /* Sense Path Group ID not supported */
172 ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP); 191 ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP);
173 break; 192 break;
@@ -176,13 +195,15 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
176 break; 195 break;
177 case -EACCES: /* channel is not operational. */ 196 case -EACCES: /* channel is not operational. */
178 sch->lpm &= ~cdev->private->imask; 197 sch->lpm &= ~cdev->private->imask;
198 /* Fall through. */
199 case 0: /* Sense Path Group ID successful. */
179 cdev->private->imask >>= 1; 200 cdev->private->imask >>= 1;
180 cdev->private->iretry = 5; 201 cdev->private->iretry = 5;
181 /* Fall through. */ 202 /* Fall through. */
182 case -EAGAIN: /* Try again. */ 203 case -EAGAIN: /* Try again. */
183 ret = __ccw_device_sense_pgid_start(cdev); 204 ret = __ccw_device_sense_pgid_start(cdev);
184 if (ret != 0 && ret != -EBUSY) 205 if (ret != 0 && ret != -EBUSY)
185 ccw_device_sense_pgid_done(cdev, -ENODEV); 206 ccw_device_sense_pgid_done(cdev, ret);
186 break; 207 break;
187 case -EUSERS: /* device is reserved for someone else. */ 208 case -EUSERS: /* device is reserved for someone else. */
188 ccw_device_sense_pgid_done(cdev, -EUSERS); 209 ccw_device_sense_pgid_done(cdev, -EUSERS);
@@ -203,20 +224,20 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
203 sch = to_subchannel(cdev->dev.parent); 224 sch = to_subchannel(cdev->dev.parent);
204 225
205 /* Setup sense path group id channel program. */ 226 /* Setup sense path group id channel program. */
206 cdev->private->pgid.inf.fc = func; 227 cdev->private->pgid[0].inf.fc = func;
207 ccw = cdev->private->iccws; 228 ccw = cdev->private->iccws;
208 if (!cdev->private->flags.pgid_single) { 229 if (!cdev->private->flags.pgid_single) {
209 cdev->private->pgid.inf.fc |= SPID_FUNC_MULTI_PATH; 230 cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH;
210 ccw->cmd_code = CCW_CMD_SUSPEND_RECONN; 231 ccw->cmd_code = CCW_CMD_SUSPEND_RECONN;
211 ccw->cda = 0; 232 ccw->cda = 0;
212 ccw->count = 0; 233 ccw->count = 0;
213 ccw->flags = CCW_FLAG_SLI | CCW_FLAG_CC; 234 ccw->flags = CCW_FLAG_SLI | CCW_FLAG_CC;
214 ccw++; 235 ccw++;
215 } else 236 } else
216 cdev->private->pgid.inf.fc |= SPID_FUNC_SINGLE_PATH; 237 cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH;
217 238
218 ccw->cmd_code = CCW_CMD_SET_PGID; 239 ccw->cmd_code = CCW_CMD_SET_PGID;
219 ccw->cda = (__u32) __pa (&cdev->private->pgid); 240 ccw->cda = (__u32) __pa (&cdev->private->pgid[0]);
220 ccw->count = sizeof (struct pgid); 241 ccw->count = sizeof (struct pgid);
221 ccw->flags = CCW_FLAG_SLI; 242 ccw->flags = CCW_FLAG_SLI;
222 243
@@ -244,6 +265,48 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
244} 265}
245 266
246/* 267/*
268 * Helper function to send a nop ccw down a path.
269 */
270static int __ccw_device_do_nop(struct ccw_device *cdev)
271{
272 struct subchannel *sch;
273 struct ccw1 *ccw;
274 int ret;
275
276 sch = to_subchannel(cdev->dev.parent);
277
278 /* Setup nop channel program. */
279 ccw = cdev->private->iccws;
280 ccw->cmd_code = CCW_CMD_NOOP;
281 ccw->cda = 0;
282 ccw->count = 0;
283 ccw->flags = CCW_FLAG_SLI;
284
285 /* Reset device status. */
286 memset(&cdev->private->irb, 0, sizeof(struct irb));
287
288 /* Try multiple times. */
289 ret = -ENODEV;
290 if (cdev->private->iretry > 0) {
291 cdev->private->iretry--;
292 ret = cio_start (sch, cdev->private->iccws,
293 cdev->private->imask);
294 /* ret is 0, -EBUSY, -EACCES or -ENODEV */
295 if ((ret != -EACCES) && (ret != -ENODEV))
296 return ret;
297 }
298 /* nop command failed on this path. Switch it off. */
299 sch->lpm &= ~cdev->private->imask;
300 sch->vpm &= ~cdev->private->imask;
301 CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel "
302 "0.%x.%04x, lpm %02X, became 'not operational'\n",
303 cdev->private->devno, sch->schid.ssid,
304 sch->schid.sch_no, cdev->private->imask);
305 return ret;
306}
307
308
309/*
247 * Called from interrupt context to check if a valid answer 310 * Called from interrupt context to check if a valid answer
248 * to Set Path Group ID was received. 311 * to Set Path Group ID was received.
249 */ 312 */
@@ -282,6 +345,29 @@ __ccw_device_check_pgid(struct ccw_device *cdev)
282 return 0; 345 return 0;
283} 346}
284 347
348/*
349 * Called from interrupt context to check the path status after a nop has
350 * been send.
351 */
352static int __ccw_device_check_nop(struct ccw_device *cdev)
353{
354 struct subchannel *sch;
355 struct irb *irb;
356
357 sch = to_subchannel(cdev->dev.parent);
358 irb = &cdev->private->irb;
359 if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
360 return -ETIME;
361 if (irb->scsw.cc == 3) {
362 CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel 0.%x.%04x,"
363 " lpm %02X, became 'not operational'\n",
364 cdev->private->devno, sch->schid.ssid,
365 sch->schid.sch_no, cdev->private->imask);
366 return -EACCES;
367 }
368 return 0;
369}
370
285static void 371static void
286__ccw_device_verify_start(struct ccw_device *cdev) 372__ccw_device_verify_start(struct ccw_device *cdev)
287{ 373{
@@ -296,9 +382,12 @@ __ccw_device_verify_start(struct ccw_device *cdev)
296 if ((sch->vpm & imask) != (sch->lpm & imask)) 382 if ((sch->vpm & imask) != (sch->lpm & imask))
297 break; 383 break;
298 cdev->private->imask = imask; 384 cdev->private->imask = imask;
299 func = (sch->vpm & imask) ? 385 if (cdev->private->options.pgroup) {
300 SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH; 386 func = (sch->vpm & imask) ?
301 ret = __ccw_device_do_pgid(cdev, func); 387 SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH;
388 ret = __ccw_device_do_pgid(cdev, func);
389 } else
390 ret = __ccw_device_do_nop(cdev);
302 if (ret == 0 || ret == -EBUSY) 391 if (ret == 0 || ret == -EBUSY)
303 return; 392 return;
304 cdev->private->iretry = 5; 393 cdev->private->iretry = 5;
@@ -317,17 +406,20 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
317 int ret; 406 int ret;
318 407
319 irb = (struct irb *) __LC_IRB; 408 irb = (struct irb *) __LC_IRB;
320 /* Retry set pgid for cc=1. */ 409
321 if (irb->scsw.stctl == 410 if (irb->scsw.stctl ==
322 (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { 411 (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
323 if (irb->scsw.cc == 1) 412 if (__ccw_device_should_retry(&irb->scsw))
324 __ccw_device_verify_start(cdev); 413 __ccw_device_verify_start(cdev);
325 return; 414 return;
326 } 415 }
327 if (ccw_device_accumulate_and_sense(cdev, irb) != 0) 416 if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
328 return; 417 return;
329 sch = to_subchannel(cdev->dev.parent); 418 sch = to_subchannel(cdev->dev.parent);
330 ret = __ccw_device_check_pgid(cdev); 419 if (cdev->private->options.pgroup)
420 ret = __ccw_device_check_pgid(cdev);
421 else
422 ret = __ccw_device_check_nop(cdev);
331 memset(&cdev->private->irb, 0, sizeof(struct irb)); 423 memset(&cdev->private->irb, 0, sizeof(struct irb));
332 switch (ret) { 424 switch (ret) {
333 /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ 425 /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
@@ -345,11 +437,10 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
345 * One of those strange devices which claim to be able 437 * One of those strange devices which claim to be able
346 * to do multipathing but not for Set Path Group ID. 438 * to do multipathing but not for Set Path Group ID.
347 */ 439 */
348 if (cdev->private->flags.pgid_single) { 440 if (cdev->private->flags.pgid_single)
349 ccw_device_verify_done(cdev, -EOPNOTSUPP); 441 cdev->private->options.pgroup = 0;
350 break; 442 else
351 } 443 cdev->private->flags.pgid_single = 1;
352 cdev->private->flags.pgid_single = 1;
353 /* fall through. */ 444 /* fall through. */
354 case -EAGAIN: /* Try again. */ 445 case -EAGAIN: /* Try again. */
355 __ccw_device_verify_start(cdev); 446 __ccw_device_verify_start(cdev);
@@ -418,10 +509,10 @@ ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
418 int ret; 509 int ret;
419 510
420 irb = (struct irb *) __LC_IRB; 511 irb = (struct irb *) __LC_IRB;
421 /* Retry set pgid for cc=1. */ 512
422 if (irb->scsw.stctl == 513 if (irb->scsw.stctl ==
423 (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { 514 (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
424 if (irb->scsw.cc == 1) 515 if (__ccw_device_should_retry(&irb->scsw))
425 __ccw_device_disband_start(cdev); 516 __ccw_device_disband_start(cdev);
426 return; 517 return;
427 } 518 }