diff options
Diffstat (limited to 'drivers/s390/cio/device_pgid.c')
| -rw-r--r-- | drivers/s390/cio/device_pgid.c | 122 |
1 files changed, 99 insertions, 23 deletions
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 54cb64ed0786..32610fd8868e 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c | |||
| @@ -33,12 +33,17 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev) | |||
| 33 | struct subchannel *sch; | 33 | struct subchannel *sch; |
| 34 | struct ccw1 *ccw; | 34 | struct ccw1 *ccw; |
| 35 | int ret; | 35 | int ret; |
| 36 | int i; | ||
| 36 | 37 | ||
| 37 | sch = to_subchannel(cdev->dev.parent); | 38 | sch = to_subchannel(cdev->dev.parent); |
| 39 | /* Return if we already checked on all paths. */ | ||
| 40 | if (cdev->private->imask == 0) | ||
| 41 | return (sch->lpm == 0) ? -ENODEV : -EACCES; | ||
| 42 | i = 8 - ffs(cdev->private->imask); | ||
| 43 | |||
| 38 | /* Setup sense path group id channel program. */ | 44 | /* Setup sense path group id channel program. */ |
| 39 | ccw = cdev->private->iccws; | 45 | ccw = cdev->private->iccws; |
| 40 | ccw->cmd_code = CCW_CMD_SENSE_PGID; | 46 | ccw->cmd_code = CCW_CMD_SENSE_PGID; |
| 41 | ccw->cda = (__u32) __pa (&cdev->private->pgid); | ||
| 42 | ccw->count = sizeof (struct pgid); | 47 | ccw->count = sizeof (struct pgid); |
| 43 | ccw->flags = CCW_FLAG_SLI; | 48 | ccw->flags = CCW_FLAG_SLI; |
| 44 | 49 | ||
| @@ -48,6 +53,7 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev) | |||
| 48 | ret = -ENODEV; | 53 | ret = -ENODEV; |
| 49 | while (cdev->private->imask != 0) { | 54 | while (cdev->private->imask != 0) { |
| 50 | /* Try every path multiple times. */ | 55 | /* Try every path multiple times. */ |
| 56 | ccw->cda = (__u32) __pa (&cdev->private->pgid[i]); | ||
| 51 | if (cdev->private->iretry > 0) { | 57 | if (cdev->private->iretry > 0) { |
| 52 | cdev->private->iretry--; | 58 | cdev->private->iretry--; |
| 53 | ret = cio_start (sch, cdev->private->iccws, | 59 | ret = cio_start (sch, cdev->private->iccws, |
| @@ -64,7 +70,9 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev) | |||
| 64 | } | 70 | } |
| 65 | cdev->private->imask >>= 1; | 71 | cdev->private->imask >>= 1; |
| 66 | cdev->private->iretry = 5; | 72 | cdev->private->iretry = 5; |
| 73 | i++; | ||
| 67 | } | 74 | } |
| 75 | |||
| 68 | return ret; | 76 | return ret; |
| 69 | } | 77 | } |
| 70 | 78 | ||
| @@ -76,7 +84,7 @@ ccw_device_sense_pgid_start(struct ccw_device *cdev) | |||
| 76 | cdev->private->state = DEV_STATE_SENSE_PGID; | 84 | cdev->private->state = DEV_STATE_SENSE_PGID; |
| 77 | cdev->private->imask = 0x80; | 85 | cdev->private->imask = 0x80; |
| 78 | cdev->private->iretry = 5; | 86 | cdev->private->iretry = 5; |
| 79 | memset (&cdev->private->pgid, 0, sizeof (struct pgid)); | 87 | memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid)); |
| 80 | ret = __ccw_device_sense_pgid_start(cdev); | 88 | ret = __ccw_device_sense_pgid_start(cdev); |
| 81 | if (ret && ret != -EBUSY) | 89 | if (ret && ret != -EBUSY) |
| 82 | ccw_device_sense_pgid_done(cdev, ret); | 90 | ccw_device_sense_pgid_done(cdev, ret); |
| @@ -91,6 +99,7 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev) | |||
| 91 | { | 99 | { |
| 92 | struct subchannel *sch; | 100 | struct subchannel *sch; |
| 93 | struct irb *irb; | 101 | struct irb *irb; |
| 102 | int i; | ||
| 94 | 103 | ||
| 95 | sch = to_subchannel(cdev->dev.parent); | 104 | sch = to_subchannel(cdev->dev.parent); |
| 96 | irb = &cdev->private->irb; | 105 | irb = &cdev->private->irb; |
| @@ -124,7 +133,8 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev) | |||
| 124 | sch->schid.sch_no, sch->orb.lpm); | 133 | sch->schid.sch_no, sch->orb.lpm); |
| 125 | return -EACCES; | 134 | return -EACCES; |
| 126 | } | 135 | } |
| 127 | if (cdev->private->pgid.inf.ps.state2 == SNID_STATE2_RESVD_ELSE) { | 136 | i = 8 - ffs(cdev->private->imask); |
| 137 | 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 " | 138 | CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x " |
| 129 | "is reserved by someone else\n", | 139 | "is reserved by someone else\n", |
| 130 | cdev->private->devno, sch->schid.ssid, | 140 | cdev->private->devno, sch->schid.ssid, |
| @@ -162,12 +172,6 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event) | |||
| 162 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | 172 | memset(&cdev->private->irb, 0, sizeof(struct irb)); |
| 163 | switch (ret) { | 173 | switch (ret) { |
| 164 | /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */ | 174 | /* 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 */ | 175 | case -EOPNOTSUPP: /* Sense Path Group ID not supported */ |
| 172 | ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP); | 176 | ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP); |
| 173 | break; | 177 | break; |
| @@ -176,13 +180,15 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event) | |||
| 176 | break; | 180 | break; |
| 177 | case -EACCES: /* channel is not operational. */ | 181 | case -EACCES: /* channel is not operational. */ |
| 178 | sch->lpm &= ~cdev->private->imask; | 182 | sch->lpm &= ~cdev->private->imask; |
| 183 | /* Fall through. */ | ||
| 184 | case 0: /* Sense Path Group ID successful. */ | ||
| 179 | cdev->private->imask >>= 1; | 185 | cdev->private->imask >>= 1; |
| 180 | cdev->private->iretry = 5; | 186 | cdev->private->iretry = 5; |
| 181 | /* Fall through. */ | 187 | /* Fall through. */ |
| 182 | case -EAGAIN: /* Try again. */ | 188 | case -EAGAIN: /* Try again. */ |
| 183 | ret = __ccw_device_sense_pgid_start(cdev); | 189 | ret = __ccw_device_sense_pgid_start(cdev); |
| 184 | if (ret != 0 && ret != -EBUSY) | 190 | if (ret != 0 && ret != -EBUSY) |
| 185 | ccw_device_sense_pgid_done(cdev, -ENODEV); | 191 | ccw_device_sense_pgid_done(cdev, ret); |
| 186 | break; | 192 | break; |
| 187 | case -EUSERS: /* device is reserved for someone else. */ | 193 | case -EUSERS: /* device is reserved for someone else. */ |
| 188 | ccw_device_sense_pgid_done(cdev, -EUSERS); | 194 | ccw_device_sense_pgid_done(cdev, -EUSERS); |
| @@ -203,20 +209,20 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func) | |||
| 203 | sch = to_subchannel(cdev->dev.parent); | 209 | sch = to_subchannel(cdev->dev.parent); |
| 204 | 210 | ||
| 205 | /* Setup sense path group id channel program. */ | 211 | /* Setup sense path group id channel program. */ |
| 206 | cdev->private->pgid.inf.fc = func; | 212 | cdev->private->pgid[0].inf.fc = func; |
| 207 | ccw = cdev->private->iccws; | 213 | ccw = cdev->private->iccws; |
| 208 | if (!cdev->private->flags.pgid_single) { | 214 | if (!cdev->private->flags.pgid_single) { |
| 209 | cdev->private->pgid.inf.fc |= SPID_FUNC_MULTI_PATH; | 215 | cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH; |
| 210 | ccw->cmd_code = CCW_CMD_SUSPEND_RECONN; | 216 | ccw->cmd_code = CCW_CMD_SUSPEND_RECONN; |
| 211 | ccw->cda = 0; | 217 | ccw->cda = 0; |
| 212 | ccw->count = 0; | 218 | ccw->count = 0; |
| 213 | ccw->flags = CCW_FLAG_SLI | CCW_FLAG_CC; | 219 | ccw->flags = CCW_FLAG_SLI | CCW_FLAG_CC; |
| 214 | ccw++; | 220 | ccw++; |
| 215 | } else | 221 | } else |
| 216 | cdev->private->pgid.inf.fc |= SPID_FUNC_SINGLE_PATH; | 222 | cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH; |
| 217 | 223 | ||
| 218 | ccw->cmd_code = CCW_CMD_SET_PGID; | 224 | ccw->cmd_code = CCW_CMD_SET_PGID; |
| 219 | ccw->cda = (__u32) __pa (&cdev->private->pgid); | 225 | ccw->cda = (__u32) __pa (&cdev->private->pgid[0]); |
| 220 | ccw->count = sizeof (struct pgid); | 226 | ccw->count = sizeof (struct pgid); |
| 221 | ccw->flags = CCW_FLAG_SLI; | 227 | ccw->flags = CCW_FLAG_SLI; |
| 222 | 228 | ||
| @@ -244,6 +250,48 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func) | |||
| 244 | } | 250 | } |
| 245 | 251 | ||
| 246 | /* | 252 | /* |
| 253 | * Helper function to send a nop ccw down a path. | ||
| 254 | */ | ||
| 255 | static int __ccw_device_do_nop(struct ccw_device *cdev) | ||
| 256 | { | ||
| 257 | struct subchannel *sch; | ||
| 258 | struct ccw1 *ccw; | ||
| 259 | int ret; | ||
| 260 | |||
| 261 | sch = to_subchannel(cdev->dev.parent); | ||
| 262 | |||
| 263 | /* Setup nop channel program. */ | ||
| 264 | ccw = cdev->private->iccws; | ||
| 265 | ccw->cmd_code = CCW_CMD_NOOP; | ||
| 266 | ccw->cda = 0; | ||
| 267 | ccw->count = 0; | ||
| 268 | ccw->flags = CCW_FLAG_SLI; | ||
| 269 | |||
| 270 | /* Reset device status. */ | ||
| 271 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | ||
| 272 | |||
| 273 | /* Try multiple times. */ | ||
| 274 | ret = -ENODEV; | ||
| 275 | if (cdev->private->iretry > 0) { | ||
| 276 | cdev->private->iretry--; | ||
| 277 | ret = cio_start (sch, cdev->private->iccws, | ||
| 278 | cdev->private->imask); | ||
| 279 | /* ret is 0, -EBUSY, -EACCES or -ENODEV */ | ||
| 280 | if ((ret != -EACCES) && (ret != -ENODEV)) | ||
| 281 | return ret; | ||
| 282 | } | ||
| 283 | /* nop command failed on this path. Switch it off. */ | ||
| 284 | sch->lpm &= ~cdev->private->imask; | ||
| 285 | sch->vpm &= ~cdev->private->imask; | ||
| 286 | CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel " | ||
| 287 | "0.%x.%04x, lpm %02X, became 'not operational'\n", | ||
| 288 | cdev->private->devno, sch->schid.ssid, | ||
| 289 | sch->schid.sch_no, cdev->private->imask); | ||
| 290 | return ret; | ||
| 291 | } | ||
| 292 | |||
| 293 | |||
| 294 | /* | ||
| 247 | * Called from interrupt context to check if a valid answer | 295 | * Called from interrupt context to check if a valid answer |
| 248 | * to Set Path Group ID was received. | 296 | * to Set Path Group ID was received. |
| 249 | */ | 297 | */ |
| @@ -282,6 +330,29 @@ __ccw_device_check_pgid(struct ccw_device *cdev) | |||
| 282 | return 0; | 330 | return 0; |
| 283 | } | 331 | } |
| 284 | 332 | ||
| 333 | /* | ||
| 334 | * Called from interrupt context to check the path status after a nop has | ||
| 335 | * been send. | ||
| 336 | */ | ||
| 337 | static int __ccw_device_check_nop(struct ccw_device *cdev) | ||
| 338 | { | ||
| 339 | struct subchannel *sch; | ||
| 340 | struct irb *irb; | ||
| 341 | |||
| 342 | sch = to_subchannel(cdev->dev.parent); | ||
| 343 | irb = &cdev->private->irb; | ||
| 344 | if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) | ||
| 345 | return -ETIME; | ||
| 346 | if (irb->scsw.cc == 3) { | ||
| 347 | CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel 0.%x.%04x," | ||
| 348 | " lpm %02X, became 'not operational'\n", | ||
| 349 | cdev->private->devno, sch->schid.ssid, | ||
| 350 | sch->schid.sch_no, cdev->private->imask); | ||
| 351 | return -EACCES; | ||
| 352 | } | ||
| 353 | return 0; | ||
| 354 | } | ||
| 355 | |||
| 285 | static void | 356 | static void |
| 286 | __ccw_device_verify_start(struct ccw_device *cdev) | 357 | __ccw_device_verify_start(struct ccw_device *cdev) |
| 287 | { | 358 | { |
| @@ -296,9 +367,12 @@ __ccw_device_verify_start(struct ccw_device *cdev) | |||
| 296 | if ((sch->vpm & imask) != (sch->lpm & imask)) | 367 | if ((sch->vpm & imask) != (sch->lpm & imask)) |
| 297 | break; | 368 | break; |
| 298 | cdev->private->imask = imask; | 369 | cdev->private->imask = imask; |
| 299 | func = (sch->vpm & imask) ? | 370 | if (cdev->private->options.pgroup) { |
| 300 | SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH; | 371 | func = (sch->vpm & imask) ? |
| 301 | ret = __ccw_device_do_pgid(cdev, func); | 372 | SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH; |
| 373 | ret = __ccw_device_do_pgid(cdev, func); | ||
| 374 | } else | ||
| 375 | ret = __ccw_device_do_nop(cdev); | ||
| 302 | if (ret == 0 || ret == -EBUSY) | 376 | if (ret == 0 || ret == -EBUSY) |
| 303 | return; | 377 | return; |
| 304 | cdev->private->iretry = 5; | 378 | cdev->private->iretry = 5; |
| @@ -327,7 +401,10 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event) | |||
| 327 | if (ccw_device_accumulate_and_sense(cdev, irb) != 0) | 401 | if (ccw_device_accumulate_and_sense(cdev, irb) != 0) |
| 328 | return; | 402 | return; |
| 329 | sch = to_subchannel(cdev->dev.parent); | 403 | sch = to_subchannel(cdev->dev.parent); |
| 330 | ret = __ccw_device_check_pgid(cdev); | 404 | if (cdev->private->options.pgroup) |
| 405 | ret = __ccw_device_check_pgid(cdev); | ||
| 406 | else | ||
| 407 | ret = __ccw_device_check_nop(cdev); | ||
| 331 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | 408 | memset(&cdev->private->irb, 0, sizeof(struct irb)); |
| 332 | switch (ret) { | 409 | switch (ret) { |
| 333 | /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ | 410 | /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ |
| @@ -345,11 +422,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 | 422 | * One of those strange devices which claim to be able |
| 346 | * to do multipathing but not for Set Path Group ID. | 423 | * to do multipathing but not for Set Path Group ID. |
| 347 | */ | 424 | */ |
| 348 | if (cdev->private->flags.pgid_single) { | 425 | if (cdev->private->flags.pgid_single) |
| 349 | ccw_device_verify_done(cdev, -EOPNOTSUPP); | 426 | cdev->private->options.pgroup = 0; |
| 350 | break; | 427 | else |
| 351 | } | 428 | cdev->private->flags.pgid_single = 1; |
| 352 | cdev->private->flags.pgid_single = 1; | ||
| 353 | /* fall through. */ | 429 | /* fall through. */ |
| 354 | case -EAGAIN: /* Try again. */ | 430 | case -EAGAIN: /* Try again. */ |
| 355 | __ccw_device_verify_start(cdev); | 431 | __ccw_device_verify_start(cdev); |
