diff options
Diffstat (limited to 'drivers/s390/block')
-rw-r--r-- | drivers/s390/block/dasd.c | 33 | ||||
-rw-r--r-- | drivers/s390/block/dasd_devmap.c | 24 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 372 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.h | 63 | ||||
-rw-r--r-- | drivers/s390/block/dasd_int.h | 10 |
5 files changed, 487 insertions, 15 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 5df05f26b7d9..329db997ee66 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c | |||
@@ -1660,6 +1660,14 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, | |||
1660 | device->discipline->check_for_device_change(device, cqr, irb); | 1660 | device->discipline->check_for_device_change(device, cqr, irb); |
1661 | dasd_put_device(device); | 1661 | dasd_put_device(device); |
1662 | } | 1662 | } |
1663 | |||
1664 | /* check for for attention message */ | ||
1665 | if (scsw_dstat(&irb->scsw) & DEV_STAT_ATTENTION) { | ||
1666 | device = dasd_device_from_cdev_locked(cdev); | ||
1667 | device->discipline->check_attention(device, irb->esw.esw1.lpum); | ||
1668 | dasd_put_device(device); | ||
1669 | } | ||
1670 | |||
1663 | if (!cqr) | 1671 | if (!cqr) |
1664 | return; | 1672 | return; |
1665 | 1673 | ||
@@ -2261,8 +2269,8 @@ static inline int _wait_for_wakeup_queue(struct list_head *ccw_queue) | |||
2261 | static int _dasd_sleep_on_queue(struct list_head *ccw_queue, int interruptible) | 2269 | static int _dasd_sleep_on_queue(struct list_head *ccw_queue, int interruptible) |
2262 | { | 2270 | { |
2263 | struct dasd_device *device; | 2271 | struct dasd_device *device; |
2264 | int rc; | ||
2265 | struct dasd_ccw_req *cqr, *n; | 2272 | struct dasd_ccw_req *cqr, *n; |
2273 | int rc; | ||
2266 | 2274 | ||
2267 | retry: | 2275 | retry: |
2268 | list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { | 2276 | list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { |
@@ -2310,21 +2318,26 @@ retry: | |||
2310 | /* | 2318 | /* |
2311 | * for alias devices simplify error recovery and | 2319 | * for alias devices simplify error recovery and |
2312 | * return to upper layer | 2320 | * return to upper layer |
2321 | * do not skip ERP requests | ||
2313 | */ | 2322 | */ |
2314 | if (cqr->startdev != cqr->basedev && | 2323 | if (cqr->startdev != cqr->basedev && !cqr->refers && |
2315 | (cqr->status == DASD_CQR_TERMINATED || | 2324 | (cqr->status == DASD_CQR_TERMINATED || |
2316 | cqr->status == DASD_CQR_NEED_ERP)) | 2325 | cqr->status == DASD_CQR_NEED_ERP)) |
2317 | return -EAGAIN; | 2326 | return -EAGAIN; |
2318 | else { | 2327 | |
2319 | /* normal recovery for basedev IO */ | 2328 | /* normal recovery for basedev IO */ |
2320 | if (__dasd_sleep_on_erp(cqr)) { | 2329 | if (__dasd_sleep_on_erp(cqr)) { |
2321 | if (!cqr->status == DASD_CQR_TERMINATED && | 2330 | goto retry; |
2322 | !cqr->status == DASD_CQR_NEED_ERP) | 2331 | /* remember that ERP was needed */ |
2323 | break; | 2332 | rc = 1; |
2324 | rc = 1; | 2333 | /* skip processing for active cqr */ |
2325 | } | 2334 | if (cqr->status != DASD_CQR_TERMINATED && |
2335 | cqr->status != DASD_CQR_NEED_ERP) | ||
2336 | break; | ||
2326 | } | 2337 | } |
2327 | } | 2338 | } |
2339 | |||
2340 | /* start ERP requests in upper loop */ | ||
2328 | if (rc) | 2341 | if (rc) |
2329 | goto retry; | 2342 | goto retry; |
2330 | 2343 | ||
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 14ba80bfa571..8286f742436b 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c | |||
@@ -1432,6 +1432,29 @@ static ssize_t dasd_reservation_state_store(struct device *dev, | |||
1432 | static DEVICE_ATTR(last_known_reservation_state, 0644, | 1432 | static DEVICE_ATTR(last_known_reservation_state, 0644, |
1433 | dasd_reservation_state_show, dasd_reservation_state_store); | 1433 | dasd_reservation_state_show, dasd_reservation_state_store); |
1434 | 1434 | ||
1435 | static ssize_t dasd_pm_show(struct device *dev, | ||
1436 | struct device_attribute *attr, char *buf) | ||
1437 | { | ||
1438 | struct dasd_device *device; | ||
1439 | u8 opm, nppm, cablepm, cuirpm, hpfpm; | ||
1440 | |||
1441 | device = dasd_device_from_cdev(to_ccwdev(dev)); | ||
1442 | if (IS_ERR(device)) | ||
1443 | return sprintf(buf, "0\n"); | ||
1444 | |||
1445 | opm = device->path_data.opm; | ||
1446 | nppm = device->path_data.npm; | ||
1447 | cablepm = device->path_data.cablepm; | ||
1448 | cuirpm = device->path_data.cuirpm; | ||
1449 | hpfpm = device->path_data.hpfpm; | ||
1450 | dasd_put_device(device); | ||
1451 | |||
1452 | return sprintf(buf, "%02x %02x %02x %02x %02x\n", opm, nppm, | ||
1453 | cablepm, cuirpm, hpfpm); | ||
1454 | } | ||
1455 | |||
1456 | static DEVICE_ATTR(path_masks, 0444, dasd_pm_show, NULL); | ||
1457 | |||
1435 | static struct attribute * dasd_attrs[] = { | 1458 | static struct attribute * dasd_attrs[] = { |
1436 | &dev_attr_readonly.attr, | 1459 | &dev_attr_readonly.attr, |
1437 | &dev_attr_discipline.attr, | 1460 | &dev_attr_discipline.attr, |
@@ -1450,6 +1473,7 @@ static struct attribute * dasd_attrs[] = { | |||
1450 | &dev_attr_reservation_policy.attr, | 1473 | &dev_attr_reservation_policy.attr, |
1451 | &dev_attr_last_known_reservation_state.attr, | 1474 | &dev_attr_last_known_reservation_state.attr, |
1452 | &dev_attr_safe_offline.attr, | 1475 | &dev_attr_safe_offline.attr, |
1476 | &dev_attr_path_masks.attr, | ||
1453 | NULL, | 1477 | NULL, |
1454 | }; | 1478 | }; |
1455 | 1479 | ||
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 51dea7baf02c..d47f5b99623a 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c | |||
@@ -29,6 +29,8 @@ | |||
29 | #include <asm/cio.h> | 29 | #include <asm/cio.h> |
30 | #include <asm/ccwdev.h> | 30 | #include <asm/ccwdev.h> |
31 | #include <asm/itcw.h> | 31 | #include <asm/itcw.h> |
32 | #include <asm/schid.h> | ||
33 | #include <asm/chpid.h> | ||
32 | 34 | ||
33 | #include "dasd_int.h" | 35 | #include "dasd_int.h" |
34 | #include "dasd_eckd.h" | 36 | #include "dasd_eckd.h" |
@@ -112,6 +114,12 @@ struct path_verification_work_data { | |||
112 | static struct path_verification_work_data *path_verification_worker; | 114 | static struct path_verification_work_data *path_verification_worker; |
113 | static DEFINE_MUTEX(dasd_path_verification_mutex); | 115 | static DEFINE_MUTEX(dasd_path_verification_mutex); |
114 | 116 | ||
117 | struct check_attention_work_data { | ||
118 | struct work_struct worker; | ||
119 | struct dasd_device *device; | ||
120 | __u8 lpum; | ||
121 | }; | ||
122 | |||
115 | /* initial attempt at a probe function. this can be simplified once | 123 | /* initial attempt at a probe function. this can be simplified once |
116 | * the other detection code is gone */ | 124 | * the other detection code is gone */ |
117 | static int | 125 | static int |
@@ -1126,6 +1134,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device) | |||
1126 | "device %s instead of %s\n", lpm, | 1134 | "device %s instead of %s\n", lpm, |
1127 | print_path_uid, print_device_uid); | 1135 | print_path_uid, print_device_uid); |
1128 | path_err = -EINVAL; | 1136 | path_err = -EINVAL; |
1137 | path_data->cablepm |= lpm; | ||
1129 | continue; | 1138 | continue; |
1130 | } | 1139 | } |
1131 | 1140 | ||
@@ -1141,6 +1150,13 @@ static int dasd_eckd_read_conf(struct dasd_device *device) | |||
1141 | break; | 1150 | break; |
1142 | } | 1151 | } |
1143 | path_data->opm |= lpm; | 1152 | path_data->opm |= lpm; |
1153 | /* | ||
1154 | * if the path is used | ||
1155 | * it should not be in one of the negative lists | ||
1156 | */ | ||
1157 | path_data->cablepm &= ~lpm; | ||
1158 | path_data->hpfpm &= ~lpm; | ||
1159 | path_data->cuirpm &= ~lpm; | ||
1144 | 1160 | ||
1145 | if (conf_data != private->conf_data) | 1161 | if (conf_data != private->conf_data) |
1146 | kfree(conf_data); | 1162 | kfree(conf_data); |
@@ -1230,7 +1246,7 @@ static void do_path_verification_work(struct work_struct *work) | |||
1230 | struct dasd_eckd_private path_private; | 1246 | struct dasd_eckd_private path_private; |
1231 | struct dasd_uid *uid; | 1247 | struct dasd_uid *uid; |
1232 | __u8 path_rcd_buf[DASD_ECKD_RCD_DATA_SIZE]; | 1248 | __u8 path_rcd_buf[DASD_ECKD_RCD_DATA_SIZE]; |
1233 | __u8 lpm, opm, npm, ppm, epm; | 1249 | __u8 lpm, opm, npm, ppm, epm, hpfpm, cablepm; |
1234 | unsigned long flags; | 1250 | unsigned long flags; |
1235 | char print_uid[60]; | 1251 | char print_uid[60]; |
1236 | int rc; | 1252 | int rc; |
@@ -1248,6 +1264,9 @@ static void do_path_verification_work(struct work_struct *work) | |||
1248 | npm = 0; | 1264 | npm = 0; |
1249 | ppm = 0; | 1265 | ppm = 0; |
1250 | epm = 0; | 1266 | epm = 0; |
1267 | hpfpm = 0; | ||
1268 | cablepm = 0; | ||
1269 | |||
1251 | for (lpm = 0x80; lpm; lpm >>= 1) { | 1270 | for (lpm = 0x80; lpm; lpm >>= 1) { |
1252 | if (!(lpm & data->tbvpm)) | 1271 | if (!(lpm & data->tbvpm)) |
1253 | continue; | 1272 | continue; |
@@ -1289,6 +1308,7 @@ static void do_path_verification_work(struct work_struct *work) | |||
1289 | opm &= ~lpm; | 1308 | opm &= ~lpm; |
1290 | npm &= ~lpm; | 1309 | npm &= ~lpm; |
1291 | ppm &= ~lpm; | 1310 | ppm &= ~lpm; |
1311 | hpfpm |= lpm; | ||
1292 | continue; | 1312 | continue; |
1293 | } | 1313 | } |
1294 | 1314 | ||
@@ -1350,6 +1370,7 @@ static void do_path_verification_work(struct work_struct *work) | |||
1350 | opm &= ~lpm; | 1370 | opm &= ~lpm; |
1351 | npm &= ~lpm; | 1371 | npm &= ~lpm; |
1352 | ppm &= ~lpm; | 1372 | ppm &= ~lpm; |
1373 | cablepm |= lpm; | ||
1353 | continue; | 1374 | continue; |
1354 | } | 1375 | } |
1355 | } | 1376 | } |
@@ -1364,12 +1385,21 @@ static void do_path_verification_work(struct work_struct *work) | |||
1364 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); | 1385 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); |
1365 | if (!device->path_data.opm && opm) { | 1386 | if (!device->path_data.opm && opm) { |
1366 | device->path_data.opm = opm; | 1387 | device->path_data.opm = opm; |
1388 | device->path_data.cablepm &= ~opm; | ||
1389 | device->path_data.cuirpm &= ~opm; | ||
1390 | device->path_data.hpfpm &= ~opm; | ||
1367 | dasd_generic_path_operational(device); | 1391 | dasd_generic_path_operational(device); |
1368 | } else | 1392 | } else { |
1369 | device->path_data.opm |= opm; | 1393 | device->path_data.opm |= opm; |
1394 | device->path_data.cablepm &= ~opm; | ||
1395 | device->path_data.cuirpm &= ~opm; | ||
1396 | device->path_data.hpfpm &= ~opm; | ||
1397 | } | ||
1370 | device->path_data.npm |= npm; | 1398 | device->path_data.npm |= npm; |
1371 | device->path_data.ppm |= ppm; | 1399 | device->path_data.ppm |= ppm; |
1372 | device->path_data.tbvpm |= epm; | 1400 | device->path_data.tbvpm |= epm; |
1401 | device->path_data.cablepm |= cablepm; | ||
1402 | device->path_data.hpfpm |= hpfpm; | ||
1373 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); | 1403 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); |
1374 | } | 1404 | } |
1375 | 1405 | ||
@@ -4475,6 +4505,343 @@ out_err: | |||
4475 | return -1; | 4505 | return -1; |
4476 | } | 4506 | } |
4477 | 4507 | ||
4508 | static int dasd_eckd_read_message_buffer(struct dasd_device *device, | ||
4509 | struct dasd_rssd_messages *messages, | ||
4510 | __u8 lpum) | ||
4511 | { | ||
4512 | struct dasd_rssd_messages *message_buf; | ||
4513 | struct dasd_psf_prssd_data *prssdp; | ||
4514 | struct dasd_eckd_private *private; | ||
4515 | struct dasd_ccw_req *cqr; | ||
4516 | struct ccw1 *ccw; | ||
4517 | int rc; | ||
4518 | |||
4519 | private = (struct dasd_eckd_private *) device->private; | ||
4520 | cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, | ||
4521 | (sizeof(struct dasd_psf_prssd_data) + | ||
4522 | sizeof(struct dasd_rssd_messages)), | ||
4523 | device); | ||
4524 | if (IS_ERR(cqr)) { | ||
4525 | DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", | ||
4526 | "Could not allocate read message buffer request"); | ||
4527 | return PTR_ERR(cqr); | ||
4528 | } | ||
4529 | |||
4530 | cqr->startdev = device; | ||
4531 | cqr->memdev = device; | ||
4532 | cqr->block = NULL; | ||
4533 | cqr->retries = 256; | ||
4534 | cqr->expires = 10 * HZ; | ||
4535 | |||
4536 | /* we need to check for messages on exactly this path */ | ||
4537 | set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags); | ||
4538 | cqr->lpm = lpum; | ||
4539 | |||
4540 | /* Prepare for Read Subsystem Data */ | ||
4541 | prssdp = (struct dasd_psf_prssd_data *) cqr->data; | ||
4542 | memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); | ||
4543 | prssdp->order = PSF_ORDER_PRSSD; | ||
4544 | prssdp->suborder = 0x03; /* Message Buffer */ | ||
4545 | /* all other bytes of prssdp must be zero */ | ||
4546 | |||
4547 | ccw = cqr->cpaddr; | ||
4548 | ccw->cmd_code = DASD_ECKD_CCW_PSF; | ||
4549 | ccw->count = sizeof(struct dasd_psf_prssd_data); | ||
4550 | ccw->flags |= CCW_FLAG_CC; | ||
4551 | ccw->flags |= CCW_FLAG_SLI; | ||
4552 | ccw->cda = (__u32)(addr_t) prssdp; | ||
4553 | |||
4554 | /* Read Subsystem Data - message buffer */ | ||
4555 | message_buf = (struct dasd_rssd_messages *) (prssdp + 1); | ||
4556 | memset(message_buf, 0, sizeof(struct dasd_rssd_messages)); | ||
4557 | |||
4558 | ccw++; | ||
4559 | ccw->cmd_code = DASD_ECKD_CCW_RSSD; | ||
4560 | ccw->count = sizeof(struct dasd_rssd_messages); | ||
4561 | ccw->flags |= CCW_FLAG_SLI; | ||
4562 | ccw->cda = (__u32)(addr_t) message_buf; | ||
4563 | |||
4564 | cqr->buildclk = get_tod_clock(); | ||
4565 | cqr->status = DASD_CQR_FILLED; | ||
4566 | rc = dasd_sleep_on_immediatly(cqr); | ||
4567 | if (rc == 0) { | ||
4568 | prssdp = (struct dasd_psf_prssd_data *) cqr->data; | ||
4569 | message_buf = (struct dasd_rssd_messages *) | ||
4570 | (prssdp + 1); | ||
4571 | memcpy(messages, message_buf, | ||
4572 | sizeof(struct dasd_rssd_messages)); | ||
4573 | } else | ||
4574 | DBF_EVENT_DEVID(DBF_WARNING, device->cdev, | ||
4575 | "Reading messages failed with rc=%d\n" | ||
4576 | , rc); | ||
4577 | dasd_sfree_request(cqr, cqr->memdev); | ||
4578 | return rc; | ||
4579 | } | ||
4580 | |||
4581 | /* | ||
4582 | * Perform Subsystem Function - CUIR response | ||
4583 | */ | ||
4584 | static int | ||
4585 | dasd_eckd_psf_cuir_response(struct dasd_device *device, int response, | ||
4586 | __u32 message_id, | ||
4587 | struct channel_path_desc *desc, | ||
4588 | struct subchannel_id sch_id) | ||
4589 | { | ||
4590 | struct dasd_psf_cuir_response *psf_cuir; | ||
4591 | struct dasd_ccw_req *cqr; | ||
4592 | struct ccw1 *ccw; | ||
4593 | int rc; | ||
4594 | |||
4595 | cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ , | ||
4596 | sizeof(struct dasd_psf_cuir_response), | ||
4597 | device); | ||
4598 | |||
4599 | if (IS_ERR(cqr)) { | ||
4600 | DBF_DEV_EVENT(DBF_WARNING, device, "%s", | ||
4601 | "Could not allocate PSF-CUIR request"); | ||
4602 | return PTR_ERR(cqr); | ||
4603 | } | ||
4604 | |||
4605 | psf_cuir = (struct dasd_psf_cuir_response *)cqr->data; | ||
4606 | psf_cuir->order = PSF_ORDER_CUIR_RESPONSE; | ||
4607 | psf_cuir->cc = response; | ||
4608 | if (desc) | ||
4609 | psf_cuir->chpid = desc->chpid; | ||
4610 | psf_cuir->message_id = message_id; | ||
4611 | psf_cuir->cssid = sch_id.cssid; | ||
4612 | psf_cuir->ssid = sch_id.ssid; | ||
4613 | |||
4614 | ccw = cqr->cpaddr; | ||
4615 | ccw->cmd_code = DASD_ECKD_CCW_PSF; | ||
4616 | ccw->cda = (__u32)(addr_t)psf_cuir; | ||
4617 | ccw->count = sizeof(struct dasd_psf_cuir_response); | ||
4618 | |||
4619 | cqr->startdev = device; | ||
4620 | cqr->memdev = device; | ||
4621 | cqr->block = NULL; | ||
4622 | cqr->retries = 256; | ||
4623 | cqr->expires = 10*HZ; | ||
4624 | cqr->buildclk = get_tod_clock(); | ||
4625 | cqr->status = DASD_CQR_FILLED; | ||
4626 | |||
4627 | rc = dasd_sleep_on(cqr); | ||
4628 | |||
4629 | dasd_sfree_request(cqr, cqr->memdev); | ||
4630 | return rc; | ||
4631 | } | ||
4632 | |||
4633 | static int dasd_eckd_cuir_change_state(struct dasd_device *device, __u8 lpum) | ||
4634 | { | ||
4635 | unsigned long flags; | ||
4636 | __u8 tbcpm; | ||
4637 | |||
4638 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); | ||
4639 | tbcpm = device->path_data.opm & ~lpum; | ||
4640 | if (tbcpm) { | ||
4641 | device->path_data.opm = tbcpm; | ||
4642 | device->path_data.cuirpm |= lpum; | ||
4643 | } | ||
4644 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); | ||
4645 | return tbcpm ? 0 : PSF_CUIR_LAST_PATH; | ||
4646 | } | ||
4647 | |||
4648 | /* | ||
4649 | * walk through all devices and quiesce them | ||
4650 | * if it is the last path return error | ||
4651 | * | ||
4652 | * if only part of the devices are quiesced and an error | ||
4653 | * occurs no onlining necessary, the storage server will | ||
4654 | * notify the already set offline devices again | ||
4655 | */ | ||
4656 | static int dasd_eckd_cuir_quiesce(struct dasd_device *device, __u8 lpum, | ||
4657 | struct channel_path_desc *desc, | ||
4658 | struct subchannel_id sch_id) | ||
4659 | { | ||
4660 | struct alias_pav_group *pavgroup, *tempgroup; | ||
4661 | struct dasd_eckd_private *private; | ||
4662 | struct dasd_device *dev, *n; | ||
4663 | int rc; | ||
4664 | |||
4665 | private = (struct dasd_eckd_private *) device->private; | ||
4666 | rc = 0; | ||
4667 | |||
4668 | /* active devices */ | ||
4669 | list_for_each_entry_safe(dev, n, | ||
4670 | &private->lcu->active_devices, | ||
4671 | alias_list) { | ||
4672 | rc = dasd_eckd_cuir_change_state(dev, lpum); | ||
4673 | if (rc) | ||
4674 | goto out; | ||
4675 | } | ||
4676 | |||
4677 | /* inactive devices */ | ||
4678 | list_for_each_entry_safe(dev, n, | ||
4679 | &private->lcu->inactive_devices, | ||
4680 | alias_list) { | ||
4681 | rc = dasd_eckd_cuir_change_state(dev, lpum); | ||
4682 | if (rc) | ||
4683 | goto out; | ||
4684 | } | ||
4685 | |||
4686 | /* devices in PAV groups */ | ||
4687 | list_for_each_entry_safe(pavgroup, tempgroup, | ||
4688 | &private->lcu->grouplist, group) { | ||
4689 | list_for_each_entry_safe(dev, n, &pavgroup->baselist, | ||
4690 | alias_list) { | ||
4691 | rc = dasd_eckd_cuir_change_state(dev, lpum); | ||
4692 | if (rc) | ||
4693 | goto out; | ||
4694 | } | ||
4695 | list_for_each_entry_safe(dev, n, &pavgroup->aliaslist, | ||
4696 | alias_list) { | ||
4697 | rc = dasd_eckd_cuir_change_state(dev, lpum); | ||
4698 | if (rc) | ||
4699 | goto out; | ||
4700 | } | ||
4701 | } | ||
4702 | |||
4703 | pr_warn("Service on the storage server caused path %x.%02x to go offline", | ||
4704 | sch_id.cssid, desc ? desc->chpid : 0); | ||
4705 | rc = PSF_CUIR_COMPLETED; | ||
4706 | out: | ||
4707 | return rc; | ||
4708 | } | ||
4709 | |||
4710 | static int dasd_eckd_cuir_resume(struct dasd_device *device, __u8 lpum, | ||
4711 | struct channel_path_desc *desc, | ||
4712 | struct subchannel_id sch_id) | ||
4713 | { | ||
4714 | struct alias_pav_group *pavgroup, *tempgroup; | ||
4715 | struct dasd_eckd_private *private; | ||
4716 | struct dasd_device *dev, *n; | ||
4717 | |||
4718 | pr_info("Path %x.%02x is back online after service on the storage server", | ||
4719 | sch_id.cssid, desc ? desc->chpid : 0); | ||
4720 | private = (struct dasd_eckd_private *) device->private; | ||
4721 | |||
4722 | /* | ||
4723 | * the path may have been added through a generic path event before | ||
4724 | * only trigger path verification if the path is not already in use | ||
4725 | */ | ||
4726 | |||
4727 | list_for_each_entry_safe(dev, n, | ||
4728 | &private->lcu->active_devices, | ||
4729 | alias_list) { | ||
4730 | if (!(dev->path_data.opm & lpum)) { | ||
4731 | dev->path_data.tbvpm |= lpum; | ||
4732 | dasd_schedule_device_bh(dev); | ||
4733 | } | ||
4734 | } | ||
4735 | |||
4736 | list_for_each_entry_safe(dev, n, | ||
4737 | &private->lcu->inactive_devices, | ||
4738 | alias_list) { | ||
4739 | if (!(dev->path_data.opm & lpum)) { | ||
4740 | dev->path_data.tbvpm |= lpum; | ||
4741 | dasd_schedule_device_bh(dev); | ||
4742 | } | ||
4743 | } | ||
4744 | |||
4745 | /* devices in PAV groups */ | ||
4746 | list_for_each_entry_safe(pavgroup, tempgroup, | ||
4747 | &private->lcu->grouplist, | ||
4748 | group) { | ||
4749 | list_for_each_entry_safe(dev, n, | ||
4750 | &pavgroup->baselist, | ||
4751 | alias_list) { | ||
4752 | if (!(dev->path_data.opm & lpum)) { | ||
4753 | dev->path_data.tbvpm |= lpum; | ||
4754 | dasd_schedule_device_bh(dev); | ||
4755 | } | ||
4756 | } | ||
4757 | list_for_each_entry_safe(dev, n, | ||
4758 | &pavgroup->aliaslist, | ||
4759 | alias_list) { | ||
4760 | if (!(dev->path_data.opm & lpum)) { | ||
4761 | dev->path_data.tbvpm |= lpum; | ||
4762 | dasd_schedule_device_bh(dev); | ||
4763 | } | ||
4764 | } | ||
4765 | } | ||
4766 | return PSF_CUIR_COMPLETED; | ||
4767 | } | ||
4768 | |||
4769 | static void dasd_eckd_handle_cuir(struct dasd_device *device, void *messages, | ||
4770 | __u8 lpum) | ||
4771 | { | ||
4772 | struct dasd_cuir_message *cuir = messages; | ||
4773 | struct channel_path_desc *desc; | ||
4774 | struct subchannel_id sch_id; | ||
4775 | int pos, response; | ||
4776 | ccw_device_get_schid(device->cdev, &sch_id); | ||
4777 | |||
4778 | /* get position of path in mask */ | ||
4779 | pos = 8 - ffs(lpum); | ||
4780 | /* get channel path descriptor from this position */ | ||
4781 | desc = ccw_device_get_chp_desc(device->cdev, pos); | ||
4782 | |||
4783 | if (cuir->code == CUIR_QUIESCE) { | ||
4784 | /* quiesce */ | ||
4785 | response = dasd_eckd_cuir_quiesce(device, lpum, desc, sch_id); | ||
4786 | } else if (cuir->code == CUIR_RESUME) { | ||
4787 | /* resume */ | ||
4788 | response = dasd_eckd_cuir_resume(device, lpum, desc, sch_id); | ||
4789 | } else | ||
4790 | response = PSF_CUIR_NOT_SUPPORTED; | ||
4791 | |||
4792 | dasd_eckd_psf_cuir_response(device, response, cuir->message_id, | ||
4793 | desc, sch_id); | ||
4794 | |||
4795 | /* free descriptor copy */ | ||
4796 | kfree(desc); | ||
4797 | } | ||
4798 | |||
4799 | static void dasd_eckd_check_attention_work(struct work_struct *work) | ||
4800 | { | ||
4801 | struct check_attention_work_data *data; | ||
4802 | struct dasd_rssd_messages *messages; | ||
4803 | struct dasd_device *device; | ||
4804 | int rc; | ||
4805 | |||
4806 | data = container_of(work, struct check_attention_work_data, worker); | ||
4807 | device = data->device; | ||
4808 | |||
4809 | messages = kzalloc(sizeof(*messages), GFP_KERNEL); | ||
4810 | if (!messages) { | ||
4811 | DBF_DEV_EVENT(DBF_WARNING, device, "%s", | ||
4812 | "Could not allocate attention message buffer"); | ||
4813 | goto out; | ||
4814 | } | ||
4815 | |||
4816 | rc = dasd_eckd_read_message_buffer(device, messages, data->lpum); | ||
4817 | if (rc) | ||
4818 | goto out; | ||
4819 | |||
4820 | if (messages->length == ATTENTION_LENGTH_CUIR && | ||
4821 | messages->format == ATTENTION_FORMAT_CUIR) | ||
4822 | dasd_eckd_handle_cuir(device, messages, data->lpum); | ||
4823 | |||
4824 | out: | ||
4825 | dasd_put_device(device); | ||
4826 | kfree(messages); | ||
4827 | kfree(data); | ||
4828 | } | ||
4829 | |||
4830 | static int dasd_eckd_check_attention(struct dasd_device *device, __u8 lpum) | ||
4831 | { | ||
4832 | struct check_attention_work_data *data; | ||
4833 | |||
4834 | data = kzalloc(sizeof(*data), GFP_ATOMIC); | ||
4835 | if (!data) | ||
4836 | return -ENOMEM; | ||
4837 | INIT_WORK(&data->worker, dasd_eckd_check_attention_work); | ||
4838 | dasd_get_device(device); | ||
4839 | data->device = device; | ||
4840 | data->lpum = lpum; | ||
4841 | schedule_work(&data->worker); | ||
4842 | return 0; | ||
4843 | } | ||
4844 | |||
4478 | static struct ccw_driver dasd_eckd_driver = { | 4845 | static struct ccw_driver dasd_eckd_driver = { |
4479 | .driver = { | 4846 | .driver = { |
4480 | .name = "dasd-eckd", | 4847 | .name = "dasd-eckd", |
@@ -4539,6 +4906,7 @@ static struct dasd_discipline dasd_eckd_discipline = { | |||
4539 | .reload = dasd_eckd_reload_device, | 4906 | .reload = dasd_eckd_reload_device, |
4540 | .get_uid = dasd_eckd_get_uid, | 4907 | .get_uid = dasd_eckd_get_uid, |
4541 | .kick_validate = dasd_eckd_kick_validate_server, | 4908 | .kick_validate = dasd_eckd_kick_validate_server, |
4909 | .check_attention = dasd_eckd_check_attention, | ||
4542 | }; | 4910 | }; |
4543 | 4911 | ||
4544 | static int __init | 4912 | static int __init |
diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index 2555e494591f..ddab7df36e25 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h | |||
@@ -51,8 +51,35 @@ | |||
51 | /* | 51 | /* |
52 | * Perform Subsystem Function / Sub-Orders | 52 | * Perform Subsystem Function / Sub-Orders |
53 | */ | 53 | */ |
54 | #define PSF_ORDER_PRSSD 0x18 | 54 | #define PSF_ORDER_PRSSD 0x18 |
55 | #define PSF_ORDER_SSC 0x1D | 55 | #define PSF_ORDER_CUIR_RESPONSE 0x1A |
56 | #define PSF_ORDER_SSC 0x1D | ||
57 | |||
58 | /* | ||
59 | * CUIR response condition codes | ||
60 | */ | ||
61 | #define PSF_CUIR_INVALID 0x00 | ||
62 | #define PSF_CUIR_COMPLETED 0x01 | ||
63 | #define PSF_CUIR_NOT_SUPPORTED 0x02 | ||
64 | #define PSF_CUIR_ERROR_IN_REQ 0x03 | ||
65 | #define PSF_CUIR_DENIED 0x04 | ||
66 | #define PSF_CUIR_LAST_PATH 0x05 | ||
67 | #define PSF_CUIR_DEVICE_ONLINE 0x06 | ||
68 | #define PSF_CUIR_VARY_FAILURE 0x07 | ||
69 | #define PSF_CUIR_SOFTWARE_FAILURE 0x08 | ||
70 | #define PSF_CUIR_NOT_RECOGNIZED 0x09 | ||
71 | |||
72 | /* | ||
73 | * CUIR codes | ||
74 | */ | ||
75 | #define CUIR_QUIESCE 0x01 | ||
76 | #define CUIR_RESUME 0x02 | ||
77 | |||
78 | /* | ||
79 | * attention message definitions | ||
80 | */ | ||
81 | #define ATTENTION_LENGTH_CUIR 0x0e | ||
82 | #define ATTENTION_FORMAT_CUIR 0x01 | ||
56 | 83 | ||
57 | /* | 84 | /* |
58 | * Size that is reportet for large volumes in the old 16-bit no_cyl field | 85 | * Size that is reportet for large volumes in the old 16-bit no_cyl field |
@@ -342,6 +369,38 @@ struct dasd_rssd_features { | |||
342 | char feature[256]; | 369 | char feature[256]; |
343 | } __attribute__((packed)); | 370 | } __attribute__((packed)); |
344 | 371 | ||
372 | struct dasd_rssd_messages { | ||
373 | __u16 length; | ||
374 | __u8 format; | ||
375 | __u8 code; | ||
376 | __u32 message_id; | ||
377 | __u8 flags; | ||
378 | char messages[4087]; | ||
379 | } __packed; | ||
380 | |||
381 | struct dasd_cuir_message { | ||
382 | __u16 length; | ||
383 | __u8 format; | ||
384 | __u8 code; | ||
385 | __u32 message_id; | ||
386 | __u8 flags; | ||
387 | __u8 neq_map[3]; | ||
388 | __u8 ned_map; | ||
389 | __u8 record_selector; | ||
390 | } __packed; | ||
391 | |||
392 | struct dasd_psf_cuir_response { | ||
393 | __u8 order; | ||
394 | __u8 flags; | ||
395 | __u8 cc; | ||
396 | __u8 chpid; | ||
397 | __u16 device_nr; | ||
398 | __u16 reserved; | ||
399 | __u32 message_id; | ||
400 | __u64 system_id; | ||
401 | __u8 cssid; | ||
402 | __u8 ssid; | ||
403 | } __packed; | ||
345 | 404 | ||
346 | /* | 405 | /* |
347 | * Perform Subsystem Function - Prepare for Read Subsystem Data | 406 | * Perform Subsystem Function - Prepare for Read Subsystem Data |
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index c20170166909..8b5d4100abf7 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h | |||
@@ -357,6 +357,7 @@ struct dasd_discipline { | |||
357 | 357 | ||
358 | int (*get_uid) (struct dasd_device *, struct dasd_uid *); | 358 | int (*get_uid) (struct dasd_device *, struct dasd_uid *); |
359 | void (*kick_validate) (struct dasd_device *); | 359 | void (*kick_validate) (struct dasd_device *); |
360 | int (*check_attention)(struct dasd_device *, __u8); | ||
360 | }; | 361 | }; |
361 | 362 | ||
362 | extern struct dasd_discipline *dasd_diag_discipline_pointer; | 363 | extern struct dasd_discipline *dasd_diag_discipline_pointer; |
@@ -382,6 +383,10 @@ struct dasd_path { | |||
382 | __u8 tbvpm; | 383 | __u8 tbvpm; |
383 | __u8 ppm; | 384 | __u8 ppm; |
384 | __u8 npm; | 385 | __u8 npm; |
386 | /* paths that are not used because of a special condition */ | ||
387 | __u8 cablepm; /* miss-cabled */ | ||
388 | __u8 hpfpm; /* the HPF requirements of the other paths are not met */ | ||
389 | __u8 cuirpm; /* CUIR varied offline */ | ||
385 | }; | 390 | }; |
386 | 391 | ||
387 | struct dasd_profile_info { | 392 | struct dasd_profile_info { |
@@ -501,7 +506,10 @@ struct dasd_block { | |||
501 | struct dasd_profile profile; | 506 | struct dasd_profile profile; |
502 | }; | 507 | }; |
503 | 508 | ||
504 | 509 | struct dasd_attention_data { | |
510 | struct dasd_device *device; | ||
511 | __u8 lpum; | ||
512 | }; | ||
505 | 513 | ||
506 | /* reasons why device (ccw_device_start) was stopped */ | 514 | /* reasons why device (ccw_device_start) was stopped */ |
507 | #define DASD_STOPPED_NOT_ACC 1 /* not accessible */ | 515 | #define DASD_STOPPED_NOT_ACC 1 /* not accessible */ |