diff options
Diffstat (limited to 'drivers')
30 files changed, 2334 insertions, 48 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 */ |
diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index 71bf959732fe..dc24ecfac2d1 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig | |||
@@ -102,6 +102,19 @@ config SCLP_ASYNC | |||
102 | want for inform other people about your kernel panics, | 102 | want for inform other people about your kernel panics, |
103 | need this feature and intend to run your kernel in LPAR. | 103 | need this feature and intend to run your kernel in LPAR. |
104 | 104 | ||
105 | config HMC_DRV | ||
106 | def_tristate m | ||
107 | prompt "Support for file transfers from HMC drive CD/DVD-ROM" | ||
108 | depends on 64BIT | ||
109 | select CRC16 | ||
110 | help | ||
111 | This option enables support for file transfers from a Hardware | ||
112 | Management Console (HMC) drive CD/DVD-ROM. It is available as a | ||
113 | module, called 'hmcdrv', and also as kernel built-in. There is one | ||
114 | optional parameter for this module: cachesize=N, which modifies the | ||
115 | transfer cache size from it's default value 0.5MB to N bytes. If N | ||
116 | is zero, then no caching is performed. | ||
117 | |||
105 | config S390_TAPE | 118 | config S390_TAPE |
106 | def_tristate m | 119 | def_tristate m |
107 | prompt "S/390 tape device support" | 120 | prompt "S/390 tape device support" |
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 78b6ace7edcb..6fa9364d1c07 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile | |||
@@ -33,3 +33,6 @@ obj-$(CONFIG_S390_VMUR) += vmur.o | |||
33 | 33 | ||
34 | zcore_mod-objs := sclp_sdias.o zcore.o | 34 | zcore_mod-objs := sclp_sdias.o zcore.o |
35 | obj-$(CONFIG_CRASH_DUMP) += zcore_mod.o | 35 | obj-$(CONFIG_CRASH_DUMP) += zcore_mod.o |
36 | |||
37 | hmcdrv-objs := hmcdrv_mod.o hmcdrv_dev.o hmcdrv_ftp.o hmcdrv_cache.o diag_ftp.o sclp_ftp.o | ||
38 | obj-$(CONFIG_HMC_DRV) += hmcdrv.o | ||
diff --git a/drivers/s390/char/diag_ftp.c b/drivers/s390/char/diag_ftp.c new file mode 100644 index 000000000000..93889632fdf9 --- /dev/null +++ b/drivers/s390/char/diag_ftp.c | |||
@@ -0,0 +1,237 @@ | |||
1 | /* | ||
2 | * DIAGNOSE X'2C4' instruction based HMC FTP services, useable on z/VM | ||
3 | * | ||
4 | * Copyright IBM Corp. 2013 | ||
5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | #define KMSG_COMPONENT "hmcdrv" | ||
10 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <linux/irq.h> | ||
15 | #include <linux/wait.h> | ||
16 | #include <linux/string.h> | ||
17 | #include <asm/ctl_reg.h> | ||
18 | |||
19 | #include "hmcdrv_ftp.h" | ||
20 | #include "diag_ftp.h" | ||
21 | |||
22 | /* DIAGNOSE X'2C4' return codes in Ry */ | ||
23 | #define DIAG_FTP_RET_OK 0 /* HMC FTP started successfully */ | ||
24 | #define DIAG_FTP_RET_EBUSY 4 /* HMC FTP service currently busy */ | ||
25 | #define DIAG_FTP_RET_EIO 8 /* HMC FTP service I/O error */ | ||
26 | /* and an artificial extension */ | ||
27 | #define DIAG_FTP_RET_EPERM 2 /* HMC FTP service privilege error */ | ||
28 | |||
29 | /* FTP service status codes (after INTR at guest real location 133) */ | ||
30 | #define DIAG_FTP_STAT_OK 0U /* request completed successfully */ | ||
31 | #define DIAG_FTP_STAT_PGCC 4U /* program check condition */ | ||
32 | #define DIAG_FTP_STAT_PGIOE 8U /* paging I/O error */ | ||
33 | #define DIAG_FTP_STAT_TIMEOUT 12U /* timeout */ | ||
34 | #define DIAG_FTP_STAT_EBASE 16U /* base of error codes from SCLP */ | ||
35 | #define DIAG_FTP_STAT_LDFAIL (DIAG_FTP_STAT_EBASE + 1U) /* failed */ | ||
36 | #define DIAG_FTP_STAT_LDNPERM (DIAG_FTP_STAT_EBASE + 2U) /* not allowed */ | ||
37 | #define DIAG_FTP_STAT_LDRUNS (DIAG_FTP_STAT_EBASE + 3U) /* runs */ | ||
38 | #define DIAG_FTP_STAT_LDNRUNS (DIAG_FTP_STAT_EBASE + 4U) /* not runs */ | ||
39 | |||
40 | /** | ||
41 | * struct diag_ftp_ldfpl - load file FTP parameter list (LDFPL) | ||
42 | * @bufaddr: real buffer address (at 4k boundary) | ||
43 | * @buflen: length of buffer | ||
44 | * @offset: dir/file offset | ||
45 | * @intparm: interruption parameter (unused) | ||
46 | * @transferred: bytes transferred | ||
47 | * @fsize: file size, filled on GET | ||
48 | * @failaddr: failing address | ||
49 | * @spare: padding | ||
50 | * @fident: file name - ASCII | ||
51 | */ | ||
52 | struct diag_ftp_ldfpl { | ||
53 | u64 bufaddr; | ||
54 | u64 buflen; | ||
55 | u64 offset; | ||
56 | u64 intparm; | ||
57 | u64 transferred; | ||
58 | u64 fsize; | ||
59 | u64 failaddr; | ||
60 | u64 spare; | ||
61 | u8 fident[HMCDRV_FTP_FIDENT_MAX]; | ||
62 | } __packed; | ||
63 | |||
64 | static DECLARE_COMPLETION(diag_ftp_rx_complete); | ||
65 | static int diag_ftp_subcode; | ||
66 | |||
67 | /** | ||
68 | * diag_ftp_handler() - FTP services IRQ handler | ||
69 | * @extirq: external interrupt (sub-) code | ||
70 | * @param32: 32-bit interruption parameter from &struct diag_ftp_ldfpl | ||
71 | * @param64: unused (for 64-bit interrupt parameters) | ||
72 | */ | ||
73 | static void diag_ftp_handler(struct ext_code extirq, | ||
74 | unsigned int param32, | ||
75 | unsigned long param64) | ||
76 | { | ||
77 | if ((extirq.subcode >> 8) != 8) | ||
78 | return; /* not a FTP services sub-code */ | ||
79 | |||
80 | inc_irq_stat(IRQEXT_FTP); | ||
81 | diag_ftp_subcode = extirq.subcode & 0xffU; | ||
82 | complete(&diag_ftp_rx_complete); | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * diag_ftp_2c4() - DIAGNOSE X'2C4' service call | ||
87 | * @fpl: pointer to prepared LDFPL | ||
88 | * @cmd: FTP command to be executed | ||
89 | * | ||
90 | * Performs a DIAGNOSE X'2C4' call with (input/output) FTP parameter list | ||
91 | * @fpl and FTP function code @cmd. In case of an error the function does | ||
92 | * nothing and returns an (negative) error code. | ||
93 | * | ||
94 | * Notes: | ||
95 | * 1. This function only initiates a transfer, so the caller must wait | ||
96 | * for completion (asynchronous execution). | ||
97 | * 2. The FTP parameter list @fpl must be aligned to a double-word boundary. | ||
98 | * 3. fpl->bufaddr must be a real address, 4k aligned | ||
99 | */ | ||
100 | static int diag_ftp_2c4(struct diag_ftp_ldfpl *fpl, | ||
101 | enum hmcdrv_ftp_cmdid cmd) | ||
102 | { | ||
103 | int rc; | ||
104 | |||
105 | asm volatile( | ||
106 | " diag %[addr],%[cmd],0x2c4\n" | ||
107 | "0: j 2f\n" | ||
108 | "1: la %[rc],%[err]\n" | ||
109 | "2:\n" | ||
110 | EX_TABLE(0b, 1b) | ||
111 | : [rc] "=d" (rc), "+m" (*fpl) | ||
112 | : [cmd] "0" (cmd), [addr] "d" (virt_to_phys(fpl)), | ||
113 | [err] "i" (DIAG_FTP_RET_EPERM) | ||
114 | : "cc"); | ||
115 | |||
116 | switch (rc) { | ||
117 | case DIAG_FTP_RET_OK: | ||
118 | return 0; | ||
119 | case DIAG_FTP_RET_EBUSY: | ||
120 | return -EBUSY; | ||
121 | case DIAG_FTP_RET_EPERM: | ||
122 | return -EPERM; | ||
123 | case DIAG_FTP_RET_EIO: | ||
124 | default: | ||
125 | return -EIO; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * diag_ftp_cmd() - executes a DIAG X'2C4' FTP command, targeting a HMC | ||
131 | * @ftp: pointer to FTP command specification | ||
132 | * @fsize: return of file size (or NULL if undesirable) | ||
133 | * | ||
134 | * Attention: Notice that this function is not reentrant - so the caller | ||
135 | * must ensure locking. | ||
136 | * | ||
137 | * Return: number of bytes read/written or a (negative) error code | ||
138 | */ | ||
139 | ssize_t diag_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize) | ||
140 | { | ||
141 | struct diag_ftp_ldfpl *ldfpl; | ||
142 | ssize_t len; | ||
143 | #ifdef DEBUG | ||
144 | unsigned long start_jiffies; | ||
145 | |||
146 | pr_debug("starting DIAG X'2C4' on '%s', requesting %zd bytes\n", | ||
147 | ftp->fname, ftp->len); | ||
148 | start_jiffies = jiffies; | ||
149 | #endif | ||
150 | init_completion(&diag_ftp_rx_complete); | ||
151 | |||
152 | ldfpl = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
153 | if (!ldfpl) { | ||
154 | len = -ENOMEM; | ||
155 | goto out; | ||
156 | } | ||
157 | |||
158 | len = strlcpy(ldfpl->fident, ftp->fname, sizeof(ldfpl->fident)); | ||
159 | if (len >= HMCDRV_FTP_FIDENT_MAX) { | ||
160 | len = -EINVAL; | ||
161 | goto out_free; | ||
162 | } | ||
163 | |||
164 | ldfpl->transferred = 0; | ||
165 | ldfpl->fsize = 0; | ||
166 | ldfpl->offset = ftp->ofs; | ||
167 | ldfpl->buflen = ftp->len; | ||
168 | ldfpl->bufaddr = virt_to_phys(ftp->buf); | ||
169 | |||
170 | len = diag_ftp_2c4(ldfpl, ftp->id); | ||
171 | if (len) | ||
172 | goto out_free; | ||
173 | |||
174 | /* | ||
175 | * There is no way to cancel the running diag X'2C4', the code | ||
176 | * needs to wait unconditionally until the transfer is complete. | ||
177 | */ | ||
178 | wait_for_completion(&diag_ftp_rx_complete); | ||
179 | |||
180 | #ifdef DEBUG | ||
181 | pr_debug("completed DIAG X'2C4' after %lu ms\n", | ||
182 | (jiffies - start_jiffies) * 1000 / HZ); | ||
183 | pr_debug("status of DIAG X'2C4' is %u, with %lld/%lld bytes\n", | ||
184 | diag_ftp_subcode, ldfpl->transferred, ldfpl->fsize); | ||
185 | #endif | ||
186 | |||
187 | switch (diag_ftp_subcode) { | ||
188 | case DIAG_FTP_STAT_OK: /* success */ | ||
189 | len = ldfpl->transferred; | ||
190 | if (fsize) | ||
191 | *fsize = ldfpl->fsize; | ||
192 | break; | ||
193 | case DIAG_FTP_STAT_LDNPERM: | ||
194 | len = -EPERM; | ||
195 | break; | ||
196 | case DIAG_FTP_STAT_LDRUNS: | ||
197 | len = -EBUSY; | ||
198 | break; | ||
199 | case DIAG_FTP_STAT_LDFAIL: | ||
200 | len = -ENOENT; /* no such file or media */ | ||
201 | break; | ||
202 | default: | ||
203 | len = -EIO; | ||
204 | break; | ||
205 | } | ||
206 | |||
207 | out_free: | ||
208 | free_page((unsigned long) ldfpl); | ||
209 | out: | ||
210 | return len; | ||
211 | } | ||
212 | |||
213 | /** | ||
214 | * diag_ftp_startup() - startup of FTP services, when running on z/VM | ||
215 | * | ||
216 | * Return: 0 on success, else an (negative) error code | ||
217 | */ | ||
218 | int diag_ftp_startup(void) | ||
219 | { | ||
220 | int rc; | ||
221 | |||
222 | rc = register_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler); | ||
223 | if (rc) | ||
224 | return rc; | ||
225 | |||
226 | ctl_set_bit(0, 63 - 22); | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | /** | ||
231 | * diag_ftp_shutdown() - shutdown of FTP services, when running on z/VM | ||
232 | */ | ||
233 | void diag_ftp_shutdown(void) | ||
234 | { | ||
235 | ctl_clear_bit(0, 63 - 22); | ||
236 | unregister_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler); | ||
237 | } | ||
diff --git a/drivers/s390/char/diag_ftp.h b/drivers/s390/char/diag_ftp.h new file mode 100644 index 000000000000..3abd2614053a --- /dev/null +++ b/drivers/s390/char/diag_ftp.h | |||
@@ -0,0 +1,21 @@ | |||
1 | /* | ||
2 | * DIAGNOSE X'2C4' instruction based SE/HMC FTP Services, useable on z/VM | ||
3 | * | ||
4 | * Notice that all functions exported here are not reentrant. | ||
5 | * So usage should be exclusive, ensured by the caller (e.g. using a | ||
6 | * mutex). | ||
7 | * | ||
8 | * Copyright IBM Corp. 2013 | ||
9 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
10 | */ | ||
11 | |||
12 | #ifndef __DIAG_FTP_H__ | ||
13 | #define __DIAG_FTP_H__ | ||
14 | |||
15 | #include "hmcdrv_ftp.h" | ||
16 | |||
17 | int diag_ftp_startup(void); | ||
18 | void diag_ftp_shutdown(void); | ||
19 | ssize_t diag_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize); | ||
20 | |||
21 | #endif /* __DIAG_FTP_H__ */ | ||
diff --git a/drivers/s390/char/hmcdrv_cache.c b/drivers/s390/char/hmcdrv_cache.c new file mode 100644 index 000000000000..4cda5ada143a --- /dev/null +++ b/drivers/s390/char/hmcdrv_cache.c | |||
@@ -0,0 +1,252 @@ | |||
1 | /* | ||
2 | * SE/HMC Drive (Read) Cache Functions | ||
3 | * | ||
4 | * Copyright IBM Corp. 2013 | ||
5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | #define KMSG_COMPONENT "hmcdrv" | ||
10 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <linux/jiffies.h> | ||
15 | |||
16 | #include "hmcdrv_ftp.h" | ||
17 | #include "hmcdrv_cache.h" | ||
18 | |||
19 | #define HMCDRV_CACHE_TIMEOUT 30 /* aging timeout in seconds */ | ||
20 | |||
21 | /** | ||
22 | * struct hmcdrv_cache_entry - file cache (only used on read/dir) | ||
23 | * @id: FTP command ID | ||
24 | * @content: kernel-space buffer, 4k aligned | ||
25 | * @len: size of @content cache (0 if caching disabled) | ||
26 | * @ofs: start of content within file (-1 if no cached content) | ||
27 | * @fname: file name | ||
28 | * @fsize: file size | ||
29 | * @timeout: cache timeout in jiffies | ||
30 | * | ||
31 | * Notice that the first three members (id, fname, fsize) are cached on all | ||
32 | * read/dir requests. But content is cached only under some preconditions. | ||
33 | * Uncached content is signalled by a negative value of @ofs. | ||
34 | */ | ||
35 | struct hmcdrv_cache_entry { | ||
36 | enum hmcdrv_ftp_cmdid id; | ||
37 | char fname[HMCDRV_FTP_FIDENT_MAX]; | ||
38 | size_t fsize; | ||
39 | loff_t ofs; | ||
40 | unsigned long timeout; | ||
41 | void *content; | ||
42 | size_t len; | ||
43 | }; | ||
44 | |||
45 | static int hmcdrv_cache_order; /* cache allocated page order */ | ||
46 | |||
47 | static struct hmcdrv_cache_entry hmcdrv_cache_file = { | ||
48 | .fsize = SIZE_MAX, | ||
49 | .ofs = -1, | ||
50 | .len = 0, | ||
51 | .fname = {'\0'} | ||
52 | }; | ||
53 | |||
54 | /** | ||
55 | * hmcdrv_cache_get() - looks for file data/content in read cache | ||
56 | * @ftp: pointer to FTP command specification | ||
57 | * | ||
58 | * Return: number of bytes read from cache or a negative number if nothing | ||
59 | * in content cache (for the file/cmd specified in @ftp) | ||
60 | */ | ||
61 | static ssize_t hmcdrv_cache_get(const struct hmcdrv_ftp_cmdspec *ftp) | ||
62 | { | ||
63 | loff_t pos; /* position in cache (signed) */ | ||
64 | ssize_t len; | ||
65 | |||
66 | if ((ftp->id != hmcdrv_cache_file.id) || | ||
67 | strcmp(hmcdrv_cache_file.fname, ftp->fname)) | ||
68 | return -1; | ||
69 | |||
70 | if (ftp->ofs >= hmcdrv_cache_file.fsize) /* EOF ? */ | ||
71 | return 0; | ||
72 | |||
73 | if ((hmcdrv_cache_file.ofs < 0) || /* has content? */ | ||
74 | time_after(jiffies, hmcdrv_cache_file.timeout)) | ||
75 | return -1; | ||
76 | |||
77 | /* there seems to be cached content - calculate the maximum number | ||
78 | * of bytes that can be returned (regarding file size and offset) | ||
79 | */ | ||
80 | len = hmcdrv_cache_file.fsize - ftp->ofs; | ||
81 | |||
82 | if (len > ftp->len) | ||
83 | len = ftp->len; | ||
84 | |||
85 | /* check if the requested chunk falls into our cache (which starts | ||
86 | * at offset 'hmcdrv_cache_file.ofs' in the file of interest) | ||
87 | */ | ||
88 | pos = ftp->ofs - hmcdrv_cache_file.ofs; | ||
89 | |||
90 | if ((pos >= 0) && | ||
91 | ((pos + len) <= hmcdrv_cache_file.len)) { | ||
92 | |||
93 | memcpy(ftp->buf, | ||
94 | hmcdrv_cache_file.content + pos, | ||
95 | len); | ||
96 | pr_debug("using cached content of '%s', returning %zd/%zd bytes\n", | ||
97 | hmcdrv_cache_file.fname, len, | ||
98 | hmcdrv_cache_file.fsize); | ||
99 | |||
100 | return len; | ||
101 | } | ||
102 | |||
103 | return -1; | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * hmcdrv_cache_do() - do a HMC drive CD/DVD transfer with cache update | ||
108 | * @ftp: pointer to FTP command specification | ||
109 | * @func: FTP transfer function to be used | ||
110 | * | ||
111 | * Return: number of bytes read/written or a (negative) error code | ||
112 | */ | ||
113 | static ssize_t hmcdrv_cache_do(const struct hmcdrv_ftp_cmdspec *ftp, | ||
114 | hmcdrv_cache_ftpfunc func) | ||
115 | { | ||
116 | ssize_t len; | ||
117 | |||
118 | /* only cache content if the read/dir cache really exists | ||
119 | * (hmcdrv_cache_file.len > 0), is large enough to handle the | ||
120 | * request (hmcdrv_cache_file.len >= ftp->len) and there is a need | ||
121 | * to do so (ftp->len > 0) | ||
122 | */ | ||
123 | if ((ftp->len > 0) && (hmcdrv_cache_file.len >= ftp->len)) { | ||
124 | |||
125 | /* because the cache is not located at ftp->buf, we have to | ||
126 | * assemble a new HMC drive FTP cmd specification (pointing | ||
127 | * to our cache, and using the increased size) | ||
128 | */ | ||
129 | struct hmcdrv_ftp_cmdspec cftp = *ftp; /* make a copy */ | ||
130 | cftp.buf = hmcdrv_cache_file.content; /* and update */ | ||
131 | cftp.len = hmcdrv_cache_file.len; /* buffer data */ | ||
132 | |||
133 | len = func(&cftp, &hmcdrv_cache_file.fsize); /* now do */ | ||
134 | |||
135 | if (len > 0) { | ||
136 | pr_debug("caching %zd bytes content for '%s'\n", | ||
137 | len, ftp->fname); | ||
138 | |||
139 | if (len > ftp->len) | ||
140 | len = ftp->len; | ||
141 | |||
142 | hmcdrv_cache_file.ofs = ftp->ofs; | ||
143 | hmcdrv_cache_file.timeout = jiffies + | ||
144 | HMCDRV_CACHE_TIMEOUT * HZ; | ||
145 | memcpy(ftp->buf, hmcdrv_cache_file.content, len); | ||
146 | } | ||
147 | } else { | ||
148 | len = func(ftp, &hmcdrv_cache_file.fsize); | ||
149 | hmcdrv_cache_file.ofs = -1; /* invalidate content */ | ||
150 | } | ||
151 | |||
152 | if (len > 0) { | ||
153 | /* cache some file info (FTP command, file name and file | ||
154 | * size) unconditionally | ||
155 | */ | ||
156 | strlcpy(hmcdrv_cache_file.fname, ftp->fname, | ||
157 | HMCDRV_FTP_FIDENT_MAX); | ||
158 | hmcdrv_cache_file.id = ftp->id; | ||
159 | pr_debug("caching cmd %d, file size %zu for '%s'\n", | ||
160 | ftp->id, hmcdrv_cache_file.fsize, ftp->fname); | ||
161 | } | ||
162 | |||
163 | return len; | ||
164 | } | ||
165 | |||
166 | /** | ||
167 | * hmcdrv_cache_cmd() - perform a cached HMC drive CD/DVD transfer | ||
168 | * @ftp: pointer to FTP command specification | ||
169 | * @func: FTP transfer function to be used | ||
170 | * | ||
171 | * Attention: Notice that this function is not reentrant - so the caller | ||
172 | * must ensure exclusive execution. | ||
173 | * | ||
174 | * Return: number of bytes read/written or a (negative) error code | ||
175 | */ | ||
176 | ssize_t hmcdrv_cache_cmd(const struct hmcdrv_ftp_cmdspec *ftp, | ||
177 | hmcdrv_cache_ftpfunc func) | ||
178 | { | ||
179 | ssize_t len; | ||
180 | |||
181 | if ((ftp->id == HMCDRV_FTP_DIR) || /* read cache */ | ||
182 | (ftp->id == HMCDRV_FTP_NLIST) || | ||
183 | (ftp->id == HMCDRV_FTP_GET)) { | ||
184 | |||
185 | len = hmcdrv_cache_get(ftp); | ||
186 | |||
187 | if (len >= 0) /* got it from cache ? */ | ||
188 | return len; /* yes */ | ||
189 | |||
190 | len = hmcdrv_cache_do(ftp, func); | ||
191 | |||
192 | if (len >= 0) | ||
193 | return len; | ||
194 | |||
195 | } else { | ||
196 | len = func(ftp, NULL); /* simply do original command */ | ||
197 | } | ||
198 | |||
199 | /* invalidate the (read) cache in case there was a write operation | ||
200 | * or an error on read/dir | ||
201 | */ | ||
202 | hmcdrv_cache_file.id = HMCDRV_FTP_NOOP; | ||
203 | hmcdrv_cache_file.fsize = LLONG_MAX; | ||
204 | hmcdrv_cache_file.ofs = -1; | ||
205 | |||
206 | return len; | ||
207 | } | ||
208 | |||
209 | /** | ||
210 | * hmcdrv_cache_startup() - startup of HMC drive cache | ||
211 | * @cachesize: cache size | ||
212 | * | ||
213 | * Return: 0 on success, else a (negative) error code | ||
214 | */ | ||
215 | int hmcdrv_cache_startup(size_t cachesize) | ||
216 | { | ||
217 | if (cachesize > 0) { /* perform caching ? */ | ||
218 | hmcdrv_cache_order = get_order(cachesize); | ||
219 | hmcdrv_cache_file.content = | ||
220 | (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, | ||
221 | hmcdrv_cache_order); | ||
222 | |||
223 | if (!hmcdrv_cache_file.content) { | ||
224 | pr_err("Allocating the requested cache size of %zu bytes failed\n", | ||
225 | cachesize); | ||
226 | return -ENOMEM; | ||
227 | } | ||
228 | |||
229 | pr_debug("content cache enabled, size is %zu bytes\n", | ||
230 | cachesize); | ||
231 | } | ||
232 | |||
233 | hmcdrv_cache_file.len = cachesize; | ||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | /** | ||
238 | * hmcdrv_cache_shutdown() - shutdown of HMC drive cache | ||
239 | */ | ||
240 | void hmcdrv_cache_shutdown(void) | ||
241 | { | ||
242 | if (hmcdrv_cache_file.content) { | ||
243 | free_pages((unsigned long) hmcdrv_cache_file.content, | ||
244 | hmcdrv_cache_order); | ||
245 | hmcdrv_cache_file.content = NULL; | ||
246 | } | ||
247 | |||
248 | hmcdrv_cache_file.id = HMCDRV_FTP_NOOP; | ||
249 | hmcdrv_cache_file.fsize = LLONG_MAX; | ||
250 | hmcdrv_cache_file.ofs = -1; | ||
251 | hmcdrv_cache_file.len = 0; /* no cache */ | ||
252 | } | ||
diff --git a/drivers/s390/char/hmcdrv_cache.h b/drivers/s390/char/hmcdrv_cache.h new file mode 100644 index 000000000000..a14b57526781 --- /dev/null +++ b/drivers/s390/char/hmcdrv_cache.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /* | ||
2 | * SE/HMC Drive (Read) Cache Functions | ||
3 | * | ||
4 | * Copyright IBM Corp. 2013 | ||
5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
6 | */ | ||
7 | |||
8 | #ifndef __HMCDRV_CACHE_H__ | ||
9 | #define __HMCDRV_CACHE_H__ | ||
10 | |||
11 | #include <linux/mmzone.h> | ||
12 | #include "hmcdrv_ftp.h" | ||
13 | |||
14 | #define HMCDRV_CACHE_SIZE_DFLT (MAX_ORDER_NR_PAGES * PAGE_SIZE / 2UL) | ||
15 | |||
16 | typedef ssize_t (*hmcdrv_cache_ftpfunc)(const struct hmcdrv_ftp_cmdspec *ftp, | ||
17 | size_t *fsize); | ||
18 | |||
19 | ssize_t hmcdrv_cache_cmd(const struct hmcdrv_ftp_cmdspec *ftp, | ||
20 | hmcdrv_cache_ftpfunc func); | ||
21 | int hmcdrv_cache_startup(size_t cachesize); | ||
22 | void hmcdrv_cache_shutdown(void); | ||
23 | |||
24 | #endif /* __HMCDRV_CACHE_H__ */ | ||
diff --git a/drivers/s390/char/hmcdrv_dev.c b/drivers/s390/char/hmcdrv_dev.c new file mode 100644 index 000000000000..0c5176179c17 --- /dev/null +++ b/drivers/s390/char/hmcdrv_dev.c | |||
@@ -0,0 +1,370 @@ | |||
1 | /* | ||
2 | * HMC Drive CD/DVD Device | ||
3 | * | ||
4 | * Copyright IBM Corp. 2013 | ||
5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
6 | * | ||
7 | * This file provides a Linux "misc" character device for access to an | ||
8 | * assigned HMC drive CD/DVD-ROM. It works as follows: First create the | ||
9 | * device by calling hmcdrv_dev_init(). After open() a lseek(fd, 0, | ||
10 | * SEEK_END) indicates that a new FTP command follows (not needed on the | ||
11 | * first command after open). Then write() the FTP command ASCII string | ||
12 | * to it, e.g. "dir /" or "nls <directory>" or "get <filename>". At the | ||
13 | * end read() the response. | ||
14 | */ | ||
15 | |||
16 | #define KMSG_COMPONENT "hmcdrv" | ||
17 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | ||
18 | |||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/fs.h> | ||
23 | #include <linux/cdev.h> | ||
24 | #include <linux/miscdevice.h> | ||
25 | #include <linux/device.h> | ||
26 | #include <linux/capability.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <linux/uaccess.h> | ||
29 | |||
30 | #include "hmcdrv_dev.h" | ||
31 | #include "hmcdrv_ftp.h" | ||
32 | |||
33 | /* If the following macro is defined, then the HMC device creates it's own | ||
34 | * separated device class (and dynamically assigns a major number). If not | ||
35 | * defined then the HMC device is assigned to the "misc" class devices. | ||
36 | * | ||
37 | #define HMCDRV_DEV_CLASS "hmcftp" | ||
38 | */ | ||
39 | |||
40 | #define HMCDRV_DEV_NAME "hmcdrv" | ||
41 | #define HMCDRV_DEV_BUSY_DELAY 500 /* delay between -EBUSY trials in ms */ | ||
42 | #define HMCDRV_DEV_BUSY_RETRIES 3 /* number of retries on -EBUSY */ | ||
43 | |||
44 | struct hmcdrv_dev_node { | ||
45 | |||
46 | #ifdef HMCDRV_DEV_CLASS | ||
47 | struct cdev dev; /* character device structure */ | ||
48 | umode_t mode; /* mode of device node (unused, zero) */ | ||
49 | #else | ||
50 | struct miscdevice dev; /* "misc" device structure */ | ||
51 | #endif | ||
52 | |||
53 | }; | ||
54 | |||
55 | static int hmcdrv_dev_open(struct inode *inode, struct file *fp); | ||
56 | static int hmcdrv_dev_release(struct inode *inode, struct file *fp); | ||
57 | static loff_t hmcdrv_dev_seek(struct file *fp, loff_t pos, int whence); | ||
58 | static ssize_t hmcdrv_dev_read(struct file *fp, char __user *ubuf, | ||
59 | size_t len, loff_t *pos); | ||
60 | static ssize_t hmcdrv_dev_write(struct file *fp, const char __user *ubuf, | ||
61 | size_t len, loff_t *pos); | ||
62 | static ssize_t hmcdrv_dev_transfer(char __kernel *cmd, loff_t offset, | ||
63 | char __user *buf, size_t len); | ||
64 | |||
65 | /* | ||
66 | * device operations | ||
67 | */ | ||
68 | static const struct file_operations hmcdrv_dev_fops = { | ||
69 | .open = hmcdrv_dev_open, | ||
70 | .llseek = hmcdrv_dev_seek, | ||
71 | .release = hmcdrv_dev_release, | ||
72 | .read = hmcdrv_dev_read, | ||
73 | .write = hmcdrv_dev_write, | ||
74 | }; | ||
75 | |||
76 | static struct hmcdrv_dev_node hmcdrv_dev; /* HMC device struct (static) */ | ||
77 | |||
78 | #ifdef HMCDRV_DEV_CLASS | ||
79 | |||
80 | static struct class *hmcdrv_dev_class; /* device class pointer */ | ||
81 | static dev_t hmcdrv_dev_no; /* device number (major/minor) */ | ||
82 | |||
83 | /** | ||
84 | * hmcdrv_dev_name() - provides a naming hint for a device node in /dev | ||
85 | * @dev: device for which the naming/mode hint is | ||
86 | * @mode: file mode for device node created in /dev | ||
87 | * | ||
88 | * See: devtmpfs.c, function devtmpfs_create_node() | ||
89 | * | ||
90 | * Return: recommended device file name in /dev | ||
91 | */ | ||
92 | static char *hmcdrv_dev_name(struct device *dev, umode_t *mode) | ||
93 | { | ||
94 | char *nodename = NULL; | ||
95 | const char *devname = dev_name(dev); /* kernel device name */ | ||
96 | |||
97 | if (devname) | ||
98 | nodename = kasprintf(GFP_KERNEL, "%s", devname); | ||
99 | |||
100 | /* on device destroy (rmmod) the mode pointer may be NULL | ||
101 | */ | ||
102 | if (mode) | ||
103 | *mode = hmcdrv_dev.mode; | ||
104 | |||
105 | return nodename; | ||
106 | } | ||
107 | |||
108 | #endif /* HMCDRV_DEV_CLASS */ | ||
109 | |||
110 | /* | ||
111 | * open() | ||
112 | */ | ||
113 | static int hmcdrv_dev_open(struct inode *inode, struct file *fp) | ||
114 | { | ||
115 | int rc; | ||
116 | |||
117 | /* check for non-blocking access, which is really unsupported | ||
118 | */ | ||
119 | if (fp->f_flags & O_NONBLOCK) | ||
120 | return -EINVAL; | ||
121 | |||
122 | /* Because it makes no sense to open this device read-only (then a | ||
123 | * FTP command cannot be emitted), we respond with an error. | ||
124 | */ | ||
125 | if ((fp->f_flags & O_ACCMODE) == O_RDONLY) | ||
126 | return -EINVAL; | ||
127 | |||
128 | /* prevent unloading this module as long as anyone holds the | ||
129 | * device file open - so increment the reference count here | ||
130 | */ | ||
131 | if (!try_module_get(THIS_MODULE)) | ||
132 | return -ENODEV; | ||
133 | |||
134 | fp->private_data = NULL; /* no command yet */ | ||
135 | rc = hmcdrv_ftp_startup(); | ||
136 | if (rc) | ||
137 | module_put(THIS_MODULE); | ||
138 | |||
139 | pr_debug("open file '/dev/%s' with return code %d\n", | ||
140 | fp->f_dentry->d_name.name, rc); | ||
141 | return rc; | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * release() | ||
146 | */ | ||
147 | static int hmcdrv_dev_release(struct inode *inode, struct file *fp) | ||
148 | { | ||
149 | pr_debug("closing file '/dev/%s'\n", fp->f_dentry->d_name.name); | ||
150 | kfree(fp->private_data); | ||
151 | fp->private_data = NULL; | ||
152 | hmcdrv_ftp_shutdown(); | ||
153 | module_put(THIS_MODULE); | ||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * lseek() | ||
159 | */ | ||
160 | static loff_t hmcdrv_dev_seek(struct file *fp, loff_t pos, int whence) | ||
161 | { | ||
162 | switch (whence) { | ||
163 | case SEEK_CUR: /* relative to current file position */ | ||
164 | pos += fp->f_pos; /* new position stored in 'pos' */ | ||
165 | break; | ||
166 | |||
167 | case SEEK_SET: /* absolute (relative to beginning of file) */ | ||
168 | break; /* SEEK_SET */ | ||
169 | |||
170 | /* We use SEEK_END as a special indicator for a SEEK_SET | ||
171 | * (set absolute position), combined with a FTP command | ||
172 | * clear. | ||
173 | */ | ||
174 | case SEEK_END: | ||
175 | if (fp->private_data) { | ||
176 | kfree(fp->private_data); | ||
177 | fp->private_data = NULL; | ||
178 | } | ||
179 | |||
180 | break; /* SEEK_END */ | ||
181 | |||
182 | default: /* SEEK_DATA, SEEK_HOLE: unsupported */ | ||
183 | return -EINVAL; | ||
184 | } | ||
185 | |||
186 | if (pos < 0) | ||
187 | return -EINVAL; | ||
188 | |||
189 | if (fp->f_pos != pos) | ||
190 | ++fp->f_version; | ||
191 | |||
192 | fp->f_pos = pos; | ||
193 | return pos; | ||
194 | } | ||
195 | |||
196 | /* | ||
197 | * transfer (helper function) | ||
198 | */ | ||
199 | static ssize_t hmcdrv_dev_transfer(char __kernel *cmd, loff_t offset, | ||
200 | char __user *buf, size_t len) | ||
201 | { | ||
202 | ssize_t retlen; | ||
203 | unsigned trials = HMCDRV_DEV_BUSY_RETRIES; | ||
204 | |||
205 | do { | ||
206 | retlen = hmcdrv_ftp_cmd(cmd, offset, buf, len); | ||
207 | |||
208 | if (retlen != -EBUSY) | ||
209 | break; | ||
210 | |||
211 | msleep(HMCDRV_DEV_BUSY_DELAY); | ||
212 | |||
213 | } while (--trials > 0); | ||
214 | |||
215 | return retlen; | ||
216 | } | ||
217 | |||
218 | /* | ||
219 | * read() | ||
220 | */ | ||
221 | static ssize_t hmcdrv_dev_read(struct file *fp, char __user *ubuf, | ||
222 | size_t len, loff_t *pos) | ||
223 | { | ||
224 | ssize_t retlen; | ||
225 | |||
226 | if (((fp->f_flags & O_ACCMODE) == O_WRONLY) || | ||
227 | (fp->private_data == NULL)) { /* no FTP cmd defined ? */ | ||
228 | return -EBADF; | ||
229 | } | ||
230 | |||
231 | retlen = hmcdrv_dev_transfer((char *) fp->private_data, | ||
232 | *pos, ubuf, len); | ||
233 | |||
234 | pr_debug("read from file '/dev/%s' at %lld returns %zd/%zu\n", | ||
235 | fp->f_dentry->d_name.name, (long long) *pos, retlen, len); | ||
236 | |||
237 | if (retlen > 0) | ||
238 | *pos += retlen; | ||
239 | |||
240 | return retlen; | ||
241 | } | ||
242 | |||
243 | /* | ||
244 | * write() | ||
245 | */ | ||
246 | static ssize_t hmcdrv_dev_write(struct file *fp, const char __user *ubuf, | ||
247 | size_t len, loff_t *pos) | ||
248 | { | ||
249 | ssize_t retlen; | ||
250 | |||
251 | pr_debug("writing file '/dev/%s' at pos. %lld with length %zd\n", | ||
252 | fp->f_dentry->d_name.name, (long long) *pos, len); | ||
253 | |||
254 | if (!fp->private_data) { /* first expect a cmd write */ | ||
255 | fp->private_data = kmalloc(len + 1, GFP_KERNEL); | ||
256 | |||
257 | if (!fp->private_data) | ||
258 | return -ENOMEM; | ||
259 | |||
260 | if (!copy_from_user(fp->private_data, ubuf, len)) { | ||
261 | ((char *)fp->private_data)[len] = '\0'; | ||
262 | return len; | ||
263 | } | ||
264 | |||
265 | kfree(fp->private_data); | ||
266 | fp->private_data = NULL; | ||
267 | return -EFAULT; | ||
268 | } | ||
269 | |||
270 | retlen = hmcdrv_dev_transfer((char *) fp->private_data, | ||
271 | *pos, (char __user *) ubuf, len); | ||
272 | if (retlen > 0) | ||
273 | *pos += retlen; | ||
274 | |||
275 | pr_debug("write to file '/dev/%s' returned %zd\n", | ||
276 | fp->f_dentry->d_name.name, retlen); | ||
277 | |||
278 | return retlen; | ||
279 | } | ||
280 | |||
281 | /** | ||
282 | * hmcdrv_dev_init() - creates a HMC drive CD/DVD device | ||
283 | * | ||
284 | * This function creates a HMC drive CD/DVD kernel device and an associated | ||
285 | * device under /dev, using a dynamically allocated major number. | ||
286 | * | ||
287 | * Return: 0 on success, else an error code. | ||
288 | */ | ||
289 | int hmcdrv_dev_init(void) | ||
290 | { | ||
291 | int rc; | ||
292 | |||
293 | #ifdef HMCDRV_DEV_CLASS | ||
294 | struct device *dev; | ||
295 | |||
296 | rc = alloc_chrdev_region(&hmcdrv_dev_no, 0, 1, HMCDRV_DEV_NAME); | ||
297 | |||
298 | if (rc) | ||
299 | goto out_err; | ||
300 | |||
301 | cdev_init(&hmcdrv_dev.dev, &hmcdrv_dev_fops); | ||
302 | hmcdrv_dev.dev.owner = THIS_MODULE; | ||
303 | rc = cdev_add(&hmcdrv_dev.dev, hmcdrv_dev_no, 1); | ||
304 | |||
305 | if (rc) | ||
306 | goto out_unreg; | ||
307 | |||
308 | /* At this point the character device exists in the kernel (see | ||
309 | * /proc/devices), but not under /dev nor /sys/devices/virtual. So | ||
310 | * we have to create an associated class (see /sys/class). | ||
311 | */ | ||
312 | hmcdrv_dev_class = class_create(THIS_MODULE, HMCDRV_DEV_CLASS); | ||
313 | |||
314 | if (IS_ERR(hmcdrv_dev_class)) { | ||
315 | rc = PTR_ERR(hmcdrv_dev_class); | ||
316 | goto out_devdel; | ||
317 | } | ||
318 | |||
319 | /* Finally a device node in /dev has to be established (as 'mkdev' | ||
320 | * does from the command line). Notice that assignment of a device | ||
321 | * node name/mode function is optional (only for mode != 0600). | ||
322 | */ | ||
323 | hmcdrv_dev.mode = 0; /* "unset" */ | ||
324 | hmcdrv_dev_class->devnode = hmcdrv_dev_name; | ||
325 | |||
326 | dev = device_create(hmcdrv_dev_class, NULL, hmcdrv_dev_no, NULL, | ||
327 | "%s", HMCDRV_DEV_NAME); | ||
328 | if (!IS_ERR(dev)) | ||
329 | return 0; | ||
330 | |||
331 | rc = PTR_ERR(dev); | ||
332 | class_destroy(hmcdrv_dev_class); | ||
333 | hmcdrv_dev_class = NULL; | ||
334 | |||
335 | out_devdel: | ||
336 | cdev_del(&hmcdrv_dev.dev); | ||
337 | |||
338 | out_unreg: | ||
339 | unregister_chrdev_region(hmcdrv_dev_no, 1); | ||
340 | |||
341 | out_err: | ||
342 | |||
343 | #else /* !HMCDRV_DEV_CLASS */ | ||
344 | hmcdrv_dev.dev.minor = MISC_DYNAMIC_MINOR; | ||
345 | hmcdrv_dev.dev.name = HMCDRV_DEV_NAME; | ||
346 | hmcdrv_dev.dev.fops = &hmcdrv_dev_fops; | ||
347 | hmcdrv_dev.dev.mode = 0; /* finally produces 0600 */ | ||
348 | rc = misc_register(&hmcdrv_dev.dev); | ||
349 | #endif /* HMCDRV_DEV_CLASS */ | ||
350 | |||
351 | return rc; | ||
352 | } | ||
353 | |||
354 | /** | ||
355 | * hmcdrv_dev_exit() - destroys a HMC drive CD/DVD device | ||
356 | */ | ||
357 | void hmcdrv_dev_exit(void) | ||
358 | { | ||
359 | #ifdef HMCDRV_DEV_CLASS | ||
360 | if (!IS_ERR_OR_NULL(hmcdrv_dev_class)) { | ||
361 | device_destroy(hmcdrv_dev_class, hmcdrv_dev_no); | ||
362 | class_destroy(hmcdrv_dev_class); | ||
363 | } | ||
364 | |||
365 | cdev_del(&hmcdrv_dev.dev); | ||
366 | unregister_chrdev_region(hmcdrv_dev_no, 1); | ||
367 | #else /* !HMCDRV_DEV_CLASS */ | ||
368 | misc_deregister(&hmcdrv_dev.dev); | ||
369 | #endif /* HMCDRV_DEV_CLASS */ | ||
370 | } | ||
diff --git a/drivers/s390/char/hmcdrv_dev.h b/drivers/s390/char/hmcdrv_dev.h new file mode 100644 index 000000000000..cb17f07e02de --- /dev/null +++ b/drivers/s390/char/hmcdrv_dev.h | |||
@@ -0,0 +1,14 @@ | |||
1 | /* | ||
2 | * SE/HMC Drive FTP Device | ||
3 | * | ||
4 | * Copyright IBM Corp. 2013 | ||
5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
6 | */ | ||
7 | |||
8 | #ifndef __HMCDRV_DEV_H__ | ||
9 | #define __HMCDRV_DEV_H__ | ||
10 | |||
11 | int hmcdrv_dev_init(void); | ||
12 | void hmcdrv_dev_exit(void); | ||
13 | |||
14 | #endif /* __HMCDRV_DEV_H__ */ | ||
diff --git a/drivers/s390/char/hmcdrv_ftp.c b/drivers/s390/char/hmcdrv_ftp.c new file mode 100644 index 000000000000..4bd63322fc29 --- /dev/null +++ b/drivers/s390/char/hmcdrv_ftp.c | |||
@@ -0,0 +1,343 @@ | |||
1 | /* | ||
2 | * HMC Drive FTP Services | ||
3 | * | ||
4 | * Copyright IBM Corp. 2013 | ||
5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
6 | */ | ||
7 | |||
8 | #define KMSG_COMPONENT "hmcdrv" | ||
9 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/uaccess.h> | ||
14 | #include <linux/export.h> | ||
15 | |||
16 | #include <linux/ctype.h> | ||
17 | #include <linux/crc16.h> | ||
18 | |||
19 | #include "hmcdrv_ftp.h" | ||
20 | #include "hmcdrv_cache.h" | ||
21 | #include "sclp_ftp.h" | ||
22 | #include "diag_ftp.h" | ||
23 | |||
24 | /** | ||
25 | * struct hmcdrv_ftp_ops - HMC drive FTP operations | ||
26 | * @startup: startup function | ||
27 | * @shutdown: shutdown function | ||
28 | * @cmd: FTP transfer function | ||
29 | */ | ||
30 | struct hmcdrv_ftp_ops { | ||
31 | int (*startup)(void); | ||
32 | void (*shutdown)(void); | ||
33 | ssize_t (*transfer)(const struct hmcdrv_ftp_cmdspec *ftp, | ||
34 | size_t *fsize); | ||
35 | }; | ||
36 | |||
37 | static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len); | ||
38 | static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp); | ||
39 | |||
40 | static struct hmcdrv_ftp_ops *hmcdrv_ftp_funcs; /* current operations */ | ||
41 | static DEFINE_MUTEX(hmcdrv_ftp_mutex); /* mutex for hmcdrv_ftp_funcs */ | ||
42 | static unsigned hmcdrv_ftp_refcnt; /* start/shutdown reference counter */ | ||
43 | |||
44 | /** | ||
45 | * hmcdrv_ftp_cmd_getid() - determine FTP command ID from a command string | ||
46 | * @cmd: FTP command string (NOT zero-terminated) | ||
47 | * @len: length of FTP command string in @cmd | ||
48 | */ | ||
49 | static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len) | ||
50 | { | ||
51 | /* HMC FTP command descriptor */ | ||
52 | struct hmcdrv_ftp_cmd_desc { | ||
53 | const char *str; /* command string */ | ||
54 | enum hmcdrv_ftp_cmdid cmd; /* associated command as enum */ | ||
55 | }; | ||
56 | |||
57 | /* Description of all HMC drive FTP commands | ||
58 | * | ||
59 | * Notes: | ||
60 | * 1. Array size should be a prime number. | ||
61 | * 2. Do not change the order of commands in table (because the | ||
62 | * index is determined by CRC % ARRAY_SIZE). | ||
63 | * 3. Original command 'nlist' was renamed, else the CRC would | ||
64 | * collide with 'append' (see point 2). | ||
65 | */ | ||
66 | static const struct hmcdrv_ftp_cmd_desc ftpcmds[7] = { | ||
67 | |||
68 | {.str = "get", /* [0] get (CRC = 0x68eb) */ | ||
69 | .cmd = HMCDRV_FTP_GET}, | ||
70 | {.str = "dir", /* [1] dir (CRC = 0x6a9e) */ | ||
71 | .cmd = HMCDRV_FTP_DIR}, | ||
72 | {.str = "delete", /* [2] delete (CRC = 0x53ae) */ | ||
73 | .cmd = HMCDRV_FTP_DELETE}, | ||
74 | {.str = "nls", /* [3] nls (CRC = 0xf87c) */ | ||
75 | .cmd = HMCDRV_FTP_NLIST}, | ||
76 | {.str = "put", /* [4] put (CRC = 0xac56) */ | ||
77 | .cmd = HMCDRV_FTP_PUT}, | ||
78 | {.str = "append", /* [5] append (CRC = 0xf56e) */ | ||
79 | .cmd = HMCDRV_FTP_APPEND}, | ||
80 | {.str = NULL} /* [6] unused */ | ||
81 | }; | ||
82 | |||
83 | const struct hmcdrv_ftp_cmd_desc *pdesc; | ||
84 | |||
85 | u16 crc = 0xffffU; | ||
86 | |||
87 | if (len == 0) | ||
88 | return HMCDRV_FTP_NOOP; /* error indiactor */ | ||
89 | |||
90 | crc = crc16(crc, cmd, len); | ||
91 | pdesc = ftpcmds + (crc % ARRAY_SIZE(ftpcmds)); | ||
92 | pr_debug("FTP command '%s' has CRC 0x%04x, at table pos. %lu\n", | ||
93 | cmd, crc, (crc % ARRAY_SIZE(ftpcmds))); | ||
94 | |||
95 | if (!pdesc->str || strncmp(pdesc->str, cmd, len)) | ||
96 | return HMCDRV_FTP_NOOP; | ||
97 | |||
98 | pr_debug("FTP command '%s' found, with ID %d\n", | ||
99 | pdesc->str, pdesc->cmd); | ||
100 | |||
101 | return pdesc->cmd; | ||
102 | } | ||
103 | |||
104 | /** | ||
105 | * hmcdrv_ftp_parse() - HMC drive FTP command parser | ||
106 | * @cmd: FTP command string "<cmd> <filename>" | ||
107 | * @ftp: Pointer to FTP command specification buffer (output) | ||
108 | * | ||
109 | * Return: 0 on success, else a (negative) error code | ||
110 | */ | ||
111 | static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp) | ||
112 | { | ||
113 | char *start; | ||
114 | int argc = 0; | ||
115 | |||
116 | ftp->id = HMCDRV_FTP_NOOP; | ||
117 | ftp->fname = NULL; | ||
118 | |||
119 | while (*cmd != '\0') { | ||
120 | |||
121 | while (isspace(*cmd)) | ||
122 | ++cmd; | ||
123 | |||
124 | if (*cmd == '\0') | ||
125 | break; | ||
126 | |||
127 | start = cmd; | ||
128 | |||
129 | switch (argc) { | ||
130 | case 0: /* 1st argument (FTP command) */ | ||
131 | while ((*cmd != '\0') && !isspace(*cmd)) | ||
132 | ++cmd; | ||
133 | ftp->id = hmcdrv_ftp_cmd_getid(start, cmd - start); | ||
134 | break; | ||
135 | case 1: /* 2nd / last argument (rest of line) */ | ||
136 | while ((*cmd != '\0') && !iscntrl(*cmd)) | ||
137 | ++cmd; | ||
138 | ftp->fname = start; | ||
139 | /* fall through */ | ||
140 | default: | ||
141 | *cmd = '\0'; | ||
142 | break; | ||
143 | } /* switch */ | ||
144 | |||
145 | ++argc; | ||
146 | } /* while */ | ||
147 | |||
148 | if (!ftp->fname || (ftp->id == HMCDRV_FTP_NOOP)) | ||
149 | return -EINVAL; | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | /** | ||
155 | * hmcdrv_ftp_do() - perform a HMC drive FTP, with data from kernel-space | ||
156 | * @ftp: pointer to FTP command specification | ||
157 | * | ||
158 | * Return: number of bytes read/written or a negative error code | ||
159 | */ | ||
160 | ssize_t hmcdrv_ftp_do(const struct hmcdrv_ftp_cmdspec *ftp) | ||
161 | { | ||
162 | ssize_t len; | ||
163 | |||
164 | mutex_lock(&hmcdrv_ftp_mutex); | ||
165 | |||
166 | if (hmcdrv_ftp_funcs && hmcdrv_ftp_refcnt) { | ||
167 | pr_debug("starting transfer, cmd %d for '%s' at %lld with %zd bytes\n", | ||
168 | ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len); | ||
169 | len = hmcdrv_cache_cmd(ftp, hmcdrv_ftp_funcs->transfer); | ||
170 | } else { | ||
171 | len = -ENXIO; | ||
172 | } | ||
173 | |||
174 | mutex_unlock(&hmcdrv_ftp_mutex); | ||
175 | return len; | ||
176 | } | ||
177 | EXPORT_SYMBOL(hmcdrv_ftp_do); | ||
178 | |||
179 | /** | ||
180 | * hmcdrv_ftp_probe() - probe for the HMC drive FTP service | ||
181 | * | ||
182 | * Return: 0 if service is available, else an (negative) error code | ||
183 | */ | ||
184 | int hmcdrv_ftp_probe(void) | ||
185 | { | ||
186 | int rc; | ||
187 | |||
188 | struct hmcdrv_ftp_cmdspec ftp = { | ||
189 | .id = HMCDRV_FTP_NOOP, | ||
190 | .ofs = 0, | ||
191 | .fname = "", | ||
192 | .len = PAGE_SIZE | ||
193 | }; | ||
194 | |||
195 | ftp.buf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
196 | |||
197 | if (!ftp.buf) | ||
198 | return -ENOMEM; | ||
199 | |||
200 | rc = hmcdrv_ftp_startup(); | ||
201 | |||
202 | if (rc) | ||
203 | return rc; | ||
204 | |||
205 | rc = hmcdrv_ftp_do(&ftp); | ||
206 | free_page((unsigned long) ftp.buf); | ||
207 | hmcdrv_ftp_shutdown(); | ||
208 | |||
209 | switch (rc) { | ||
210 | case -ENOENT: /* no such file/media or currently busy, */ | ||
211 | case -EBUSY: /* but service seems to be available */ | ||
212 | rc = 0; | ||
213 | break; | ||
214 | default: /* leave 'rc' as it is for [0, -EPERM, -E...] */ | ||
215 | if (rc > 0) | ||
216 | rc = 0; /* clear length (success) */ | ||
217 | break; | ||
218 | } /* switch */ | ||
219 | |||
220 | return rc; | ||
221 | } | ||
222 | EXPORT_SYMBOL(hmcdrv_ftp_probe); | ||
223 | |||
224 | /** | ||
225 | * hmcdrv_ftp_cmd() - Perform a HMC drive FTP, with data from user-space | ||
226 | * | ||
227 | * @cmd: FTP command string "<cmd> <filename>" | ||
228 | * @offset: file position to read/write | ||
229 | * @buf: user-space buffer for read/written directory/file | ||
230 | * @len: size of @buf (read/dir) or number of bytes to write | ||
231 | * | ||
232 | * This function must not be called before hmcdrv_ftp_startup() was called. | ||
233 | * | ||
234 | * Return: number of bytes read/written or a negative error code | ||
235 | */ | ||
236 | ssize_t hmcdrv_ftp_cmd(char __kernel *cmd, loff_t offset, | ||
237 | char __user *buf, size_t len) | ||
238 | { | ||
239 | int order; | ||
240 | |||
241 | struct hmcdrv_ftp_cmdspec ftp = {.len = len, .ofs = offset}; | ||
242 | ssize_t retlen = hmcdrv_ftp_parse(cmd, &ftp); | ||
243 | |||
244 | if (retlen) | ||
245 | return retlen; | ||
246 | |||
247 | order = get_order(ftp.len); | ||
248 | ftp.buf = (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, order); | ||
249 | |||
250 | if (!ftp.buf) | ||
251 | return -ENOMEM; | ||
252 | |||
253 | switch (ftp.id) { | ||
254 | case HMCDRV_FTP_DIR: | ||
255 | case HMCDRV_FTP_NLIST: | ||
256 | case HMCDRV_FTP_GET: | ||
257 | retlen = hmcdrv_ftp_do(&ftp); | ||
258 | |||
259 | if ((retlen >= 0) && | ||
260 | copy_to_user(buf, ftp.buf, retlen)) | ||
261 | retlen = -EFAULT; | ||
262 | break; | ||
263 | |||
264 | case HMCDRV_FTP_PUT: | ||
265 | case HMCDRV_FTP_APPEND: | ||
266 | if (!copy_from_user(ftp.buf, buf, ftp.len)) | ||
267 | retlen = hmcdrv_ftp_do(&ftp); | ||
268 | else | ||
269 | retlen = -EFAULT; | ||
270 | break; | ||
271 | |||
272 | case HMCDRV_FTP_DELETE: | ||
273 | retlen = hmcdrv_ftp_do(&ftp); | ||
274 | break; | ||
275 | |||
276 | default: | ||
277 | retlen = -EOPNOTSUPP; | ||
278 | break; | ||
279 | } | ||
280 | |||
281 | free_pages((unsigned long) ftp.buf, order); | ||
282 | return retlen; | ||
283 | } | ||
284 | |||
285 | /** | ||
286 | * hmcdrv_ftp_startup() - startup of HMC drive FTP functionality for a | ||
287 | * dedicated (owner) instance | ||
288 | * | ||
289 | * Return: 0 on success, else an (negative) error code | ||
290 | */ | ||
291 | int hmcdrv_ftp_startup(void) | ||
292 | { | ||
293 | static struct hmcdrv_ftp_ops hmcdrv_ftp_zvm = { | ||
294 | .startup = diag_ftp_startup, | ||
295 | .shutdown = diag_ftp_shutdown, | ||
296 | .transfer = diag_ftp_cmd | ||
297 | }; | ||
298 | |||
299 | static struct hmcdrv_ftp_ops hmcdrv_ftp_lpar = { | ||
300 | .startup = sclp_ftp_startup, | ||
301 | .shutdown = sclp_ftp_shutdown, | ||
302 | .transfer = sclp_ftp_cmd | ||
303 | }; | ||
304 | |||
305 | int rc = 0; | ||
306 | |||
307 | mutex_lock(&hmcdrv_ftp_mutex); /* block transfers while start-up */ | ||
308 | |||
309 | if (hmcdrv_ftp_refcnt == 0) { | ||
310 | if (MACHINE_IS_VM) | ||
311 | hmcdrv_ftp_funcs = &hmcdrv_ftp_zvm; | ||
312 | else if (MACHINE_IS_LPAR || MACHINE_IS_KVM) | ||
313 | hmcdrv_ftp_funcs = &hmcdrv_ftp_lpar; | ||
314 | else | ||
315 | rc = -EOPNOTSUPP; | ||
316 | |||
317 | if (hmcdrv_ftp_funcs) | ||
318 | rc = hmcdrv_ftp_funcs->startup(); | ||
319 | } | ||
320 | |||
321 | if (!rc) | ||
322 | ++hmcdrv_ftp_refcnt; | ||
323 | |||
324 | mutex_unlock(&hmcdrv_ftp_mutex); | ||
325 | return rc; | ||
326 | } | ||
327 | EXPORT_SYMBOL(hmcdrv_ftp_startup); | ||
328 | |||
329 | /** | ||
330 | * hmcdrv_ftp_shutdown() - shutdown of HMC drive FTP functionality for a | ||
331 | * dedicated (owner) instance | ||
332 | */ | ||
333 | void hmcdrv_ftp_shutdown(void) | ||
334 | { | ||
335 | mutex_lock(&hmcdrv_ftp_mutex); | ||
336 | --hmcdrv_ftp_refcnt; | ||
337 | |||
338 | if ((hmcdrv_ftp_refcnt == 0) && hmcdrv_ftp_funcs) | ||
339 | hmcdrv_ftp_funcs->shutdown(); | ||
340 | |||
341 | mutex_unlock(&hmcdrv_ftp_mutex); | ||
342 | } | ||
343 | EXPORT_SYMBOL(hmcdrv_ftp_shutdown); | ||
diff --git a/drivers/s390/char/hmcdrv_ftp.h b/drivers/s390/char/hmcdrv_ftp.h new file mode 100644 index 000000000000..f3643a7b3676 --- /dev/null +++ b/drivers/s390/char/hmcdrv_ftp.h | |||
@@ -0,0 +1,63 @@ | |||
1 | /* | ||
2 | * SE/HMC Drive FTP Services | ||
3 | * | ||
4 | * Copyright IBM Corp. 2013 | ||
5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
6 | */ | ||
7 | |||
8 | #ifndef __HMCDRV_FTP_H__ | ||
9 | #define __HMCDRV_FTP_H__ | ||
10 | |||
11 | #include <linux/types.h> /* size_t, loff_t */ | ||
12 | |||
13 | /* | ||
14 | * HMC drive FTP Service max. length of path (w/ EOS) | ||
15 | */ | ||
16 | #define HMCDRV_FTP_FIDENT_MAX 192 | ||
17 | |||
18 | /** | ||
19 | * enum hmcdrv_ftp_cmdid - HMC drive FTP commands | ||
20 | * @HMCDRV_FTP_NOOP: do nothing (only for probing) | ||
21 | * @HMCDRV_FTP_GET: read a file | ||
22 | * @HMCDRV_FTP_PUT: (over-) write a file | ||
23 | * @HMCDRV_FTP_APPEND: append to a file | ||
24 | * @HMCDRV_FTP_DIR: list directory long (ls -l) | ||
25 | * @HMCDRV_FTP_NLIST: list files, no directories (name list) | ||
26 | * @HMCDRV_FTP_DELETE: delete a file | ||
27 | * @HMCDRV_FTP_CANCEL: cancel operation (SCLP/LPAR only) | ||
28 | */ | ||
29 | enum hmcdrv_ftp_cmdid { | ||
30 | HMCDRV_FTP_NOOP = 0, | ||
31 | HMCDRV_FTP_GET = 1, | ||
32 | HMCDRV_FTP_PUT = 2, | ||
33 | HMCDRV_FTP_APPEND = 3, | ||
34 | HMCDRV_FTP_DIR = 4, | ||
35 | HMCDRV_FTP_NLIST = 5, | ||
36 | HMCDRV_FTP_DELETE = 6, | ||
37 | HMCDRV_FTP_CANCEL = 7 | ||
38 | }; | ||
39 | |||
40 | /** | ||
41 | * struct hmcdrv_ftp_cmdspec - FTP command specification | ||
42 | * @id: FTP command ID | ||
43 | * @ofs: offset in file | ||
44 | * @fname: filename (ASCII), null-terminated | ||
45 | * @buf: kernel-space transfer data buffer, 4k aligned | ||
46 | * @len: (max) number of bytes to transfer from/to @buf | ||
47 | */ | ||
48 | struct hmcdrv_ftp_cmdspec { | ||
49 | enum hmcdrv_ftp_cmdid id; | ||
50 | loff_t ofs; | ||
51 | const char *fname; | ||
52 | void __kernel *buf; | ||
53 | size_t len; | ||
54 | }; | ||
55 | |||
56 | int hmcdrv_ftp_startup(void); | ||
57 | void hmcdrv_ftp_shutdown(void); | ||
58 | int hmcdrv_ftp_probe(void); | ||
59 | ssize_t hmcdrv_ftp_do(const struct hmcdrv_ftp_cmdspec *ftp); | ||
60 | ssize_t hmcdrv_ftp_cmd(char __kernel *cmd, loff_t offset, | ||
61 | char __user *buf, size_t len); | ||
62 | |||
63 | #endif /* __HMCDRV_FTP_H__ */ | ||
diff --git a/drivers/s390/char/hmcdrv_mod.c b/drivers/s390/char/hmcdrv_mod.c new file mode 100644 index 000000000000..505c6a78ee1a --- /dev/null +++ b/drivers/s390/char/hmcdrv_mod.c | |||
@@ -0,0 +1,64 @@ | |||
1 | /* | ||
2 | * HMC Drive DVD Module | ||
3 | * | ||
4 | * Copyright IBM Corp. 2013 | ||
5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
6 | */ | ||
7 | |||
8 | #define KMSG_COMPONENT "hmcdrv" | ||
9 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/moduleparam.h> | ||
14 | #include <linux/version.h> | ||
15 | #include <linux/stat.h> | ||
16 | |||
17 | #include "hmcdrv_ftp.h" | ||
18 | #include "hmcdrv_dev.h" | ||
19 | #include "hmcdrv_cache.h" | ||
20 | |||
21 | MODULE_LICENSE("GPL"); | ||
22 | MODULE_AUTHOR("Copyright 2013 IBM Corporation"); | ||
23 | MODULE_DESCRIPTION("HMC drive DVD access"); | ||
24 | |||
25 | /* | ||
26 | * module parameter 'cachesize' | ||
27 | */ | ||
28 | static size_t hmcdrv_mod_cachesize = HMCDRV_CACHE_SIZE_DFLT; | ||
29 | module_param_named(cachesize, hmcdrv_mod_cachesize, ulong, S_IRUGO); | ||
30 | |||
31 | /** | ||
32 | * hmcdrv_mod_init() - module init function | ||
33 | */ | ||
34 | static int __init hmcdrv_mod_init(void) | ||
35 | { | ||
36 | int rc = hmcdrv_ftp_probe(); /* perform w/o cache */ | ||
37 | |||
38 | if (rc) | ||
39 | return rc; | ||
40 | |||
41 | rc = hmcdrv_cache_startup(hmcdrv_mod_cachesize); | ||
42 | |||
43 | if (rc) | ||
44 | return rc; | ||
45 | |||
46 | rc = hmcdrv_dev_init(); | ||
47 | |||
48 | if (rc) | ||
49 | hmcdrv_cache_shutdown(); | ||
50 | |||
51 | return rc; | ||
52 | } | ||
53 | |||
54 | /** | ||
55 | * hmcdrv_mod_exit() - module exit function | ||
56 | */ | ||
57 | static void __exit hmcdrv_mod_exit(void) | ||
58 | { | ||
59 | hmcdrv_dev_exit(); | ||
60 | hmcdrv_cache_shutdown(); | ||
61 | } | ||
62 | |||
63 | module_init(hmcdrv_mod_init); | ||
64 | module_exit(hmcdrv_mod_exit); | ||
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index a68b5ec7d042..a88069f8c677 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h | |||
@@ -19,6 +19,7 @@ | |||
19 | 19 | ||
20 | #define EVTYP_OPCMD 0x01 | 20 | #define EVTYP_OPCMD 0x01 |
21 | #define EVTYP_MSG 0x02 | 21 | #define EVTYP_MSG 0x02 |
22 | #define EVTYP_DIAG_TEST 0x07 | ||
22 | #define EVTYP_STATECHANGE 0x08 | 23 | #define EVTYP_STATECHANGE 0x08 |
23 | #define EVTYP_PMSGCMD 0x09 | 24 | #define EVTYP_PMSGCMD 0x09 |
24 | #define EVTYP_CNTLPROGOPCMD 0x20 | 25 | #define EVTYP_CNTLPROGOPCMD 0x20 |
@@ -32,6 +33,7 @@ | |||
32 | 33 | ||
33 | #define EVTYP_OPCMD_MASK 0x80000000 | 34 | #define EVTYP_OPCMD_MASK 0x80000000 |
34 | #define EVTYP_MSG_MASK 0x40000000 | 35 | #define EVTYP_MSG_MASK 0x40000000 |
36 | #define EVTYP_DIAG_TEST_MASK 0x02000000 | ||
35 | #define EVTYP_STATECHANGE_MASK 0x01000000 | 37 | #define EVTYP_STATECHANGE_MASK 0x01000000 |
36 | #define EVTYP_PMSGCMD_MASK 0x00800000 | 38 | #define EVTYP_PMSGCMD_MASK 0x00800000 |
37 | #define EVTYP_CTLPROGOPCMD_MASK 0x00000001 | 39 | #define EVTYP_CTLPROGOPCMD_MASK 0x00000001 |
diff --git a/drivers/s390/char/sclp_diag.h b/drivers/s390/char/sclp_diag.h new file mode 100644 index 000000000000..59c4afa5e670 --- /dev/null +++ b/drivers/s390/char/sclp_diag.h | |||
@@ -0,0 +1,89 @@ | |||
1 | /* | ||
2 | * Copyright IBM Corp. 2013 | ||
3 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
4 | */ | ||
5 | |||
6 | #ifndef _SCLP_DIAG_H | ||
7 | #define _SCLP_DIAG_H | ||
8 | |||
9 | #include <linux/types.h> | ||
10 | |||
11 | /* return codes for Diagnostic Test FTP Service, as indicated in member | ||
12 | * sclp_diag_ftp::ldflg | ||
13 | */ | ||
14 | #define SCLP_DIAG_FTP_OK 0x80U /* success */ | ||
15 | #define SCLP_DIAG_FTP_LDFAIL 0x01U /* load failed */ | ||
16 | #define SCLP_DIAG_FTP_LDNPERM 0x02U /* not allowed */ | ||
17 | #define SCLP_DIAG_FTP_LDRUNS 0x03U /* LD runs */ | ||
18 | #define SCLP_DIAG_FTP_LDNRUNS 0x04U /* LD does not run */ | ||
19 | |||
20 | #define SCLP_DIAG_FTP_XPCX 0x80 /* PCX communication code */ | ||
21 | #define SCLP_DIAG_FTP_ROUTE 4 /* routing code for new FTP service */ | ||
22 | |||
23 | /* | ||
24 | * length of Diagnostic Test FTP Service event buffer | ||
25 | */ | ||
26 | #define SCLP_DIAG_FTP_EVBUF_LEN \ | ||
27 | (offsetof(struct sclp_diag_evbuf, mdd) + \ | ||
28 | sizeof(struct sclp_diag_ftp)) | ||
29 | |||
30 | /** | ||
31 | * struct sclp_diag_ftp - Diagnostic Test FTP Service model-dependent data | ||
32 | * @pcx: code for PCX communication (should be 0x80) | ||
33 | * @ldflg: load flag (see defines above) | ||
34 | * @cmd: FTP command | ||
35 | * @pgsize: page size (0 = 4kB, 1 = large page size) | ||
36 | * @srcflg: source flag | ||
37 | * @spare: reserved (zeroes) | ||
38 | * @offset: file offset | ||
39 | * @fsize: file size | ||
40 | * @length: buffer size resp. bytes transferred | ||
41 | * @failaddr: failing address | ||
42 | * @bufaddr: buffer address, virtual | ||
43 | * @asce: region or segment table designation | ||
44 | * @fident: file name (ASCII, zero-terminated) | ||
45 | */ | ||
46 | struct sclp_diag_ftp { | ||
47 | u8 pcx; | ||
48 | u8 ldflg; | ||
49 | u8 cmd; | ||
50 | u8 pgsize; | ||
51 | u8 srcflg; | ||
52 | u8 spare; | ||
53 | u64 offset; | ||
54 | u64 fsize; | ||
55 | u64 length; | ||
56 | u64 failaddr; | ||
57 | u64 bufaddr; | ||
58 | u64 asce; | ||
59 | |||
60 | u8 fident[256]; | ||
61 | } __packed; | ||
62 | |||
63 | /** | ||
64 | * struct sclp_diag_evbuf - Diagnostic Test (ET7) Event Buffer | ||
65 | * @hdr: event buffer header | ||
66 | * @route: diagnostic route | ||
67 | * @mdd: model-dependent data (@route dependent) | ||
68 | */ | ||
69 | struct sclp_diag_evbuf { | ||
70 | struct evbuf_header hdr; | ||
71 | u16 route; | ||
72 | |||
73 | union { | ||
74 | struct sclp_diag_ftp ftp; | ||
75 | } mdd; | ||
76 | } __packed; | ||
77 | |||
78 | /** | ||
79 | * struct sclp_diag_sccb - Diagnostic Test (ET7) SCCB | ||
80 | * @hdr: SCCB header | ||
81 | * @evbuf: event buffer | ||
82 | */ | ||
83 | struct sclp_diag_sccb { | ||
84 | |||
85 | struct sccb_header hdr; | ||
86 | struct sclp_diag_evbuf evbuf; | ||
87 | } __packed; | ||
88 | |||
89 | #endif /* _SCLP_DIAG_H */ | ||
diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c index 1918d9dff45d..5bd6cb145a87 100644 --- a/drivers/s390/char/sclp_early.c +++ b/drivers/s390/char/sclp_early.c | |||
@@ -281,7 +281,7 @@ out: | |||
281 | 281 | ||
282 | static unsigned int __init sclp_con_check_linemode(struct init_sccb *sccb) | 282 | static unsigned int __init sclp_con_check_linemode(struct init_sccb *sccb) |
283 | { | 283 | { |
284 | if (!(sccb->sclp_send_mask & (EVTYP_OPCMD_MASK | EVTYP_PMSGCMD_MASK))) | 284 | if (!(sccb->sclp_send_mask & EVTYP_OPCMD_MASK)) |
285 | return 0; | 285 | return 0; |
286 | if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK))) | 286 | if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK))) |
287 | return 0; | 287 | return 0; |
diff --git a/drivers/s390/char/sclp_ftp.c b/drivers/s390/char/sclp_ftp.c new file mode 100644 index 000000000000..6561cc5b2d5d --- /dev/null +++ b/drivers/s390/char/sclp_ftp.c | |||
@@ -0,0 +1,275 @@ | |||
1 | /* | ||
2 | * SCLP Event Type (ET) 7 - Diagnostic Test FTP Services, useable on LPAR | ||
3 | * | ||
4 | * Copyright IBM Corp. 2013 | ||
5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | #define KMSG_COMPONENT "hmcdrv" | ||
10 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/wait.h> | ||
17 | #include <linux/string.h> | ||
18 | #include <linux/jiffies.h> | ||
19 | #include <asm/sysinfo.h> | ||
20 | #include <asm/ebcdic.h> | ||
21 | |||
22 | #include "sclp.h" | ||
23 | #include "sclp_diag.h" | ||
24 | #include "sclp_ftp.h" | ||
25 | |||
26 | static DECLARE_COMPLETION(sclp_ftp_rx_complete); | ||
27 | static u8 sclp_ftp_ldflg; | ||
28 | static u64 sclp_ftp_fsize; | ||
29 | static u64 sclp_ftp_length; | ||
30 | |||
31 | /** | ||
32 | * sclp_ftp_txcb() - Diagnostic Test FTP services SCLP command callback | ||
33 | */ | ||
34 | static void sclp_ftp_txcb(struct sclp_req *req, void *data) | ||
35 | { | ||
36 | struct completion *completion = data; | ||
37 | |||
38 | #ifdef DEBUG | ||
39 | pr_debug("SCLP (ET7) TX-IRQ, SCCB @ 0x%p: %*phN\n", | ||
40 | req->sccb, 24, req->sccb); | ||
41 | #endif | ||
42 | complete(completion); | ||
43 | } | ||
44 | |||
45 | /** | ||
46 | * sclp_ftp_rxcb() - Diagnostic Test FTP services receiver event callback | ||
47 | */ | ||
48 | static void sclp_ftp_rxcb(struct evbuf_header *evbuf) | ||
49 | { | ||
50 | struct sclp_diag_evbuf *diag = (struct sclp_diag_evbuf *) evbuf; | ||
51 | |||
52 | /* | ||
53 | * Check for Diagnostic Test FTP Service | ||
54 | */ | ||
55 | if (evbuf->type != EVTYP_DIAG_TEST || | ||
56 | diag->route != SCLP_DIAG_FTP_ROUTE || | ||
57 | diag->mdd.ftp.pcx != SCLP_DIAG_FTP_XPCX || | ||
58 | evbuf->length < SCLP_DIAG_FTP_EVBUF_LEN) | ||
59 | return; | ||
60 | |||
61 | #ifdef DEBUG | ||
62 | pr_debug("SCLP (ET7) RX-IRQ, Event @ 0x%p: %*phN\n", | ||
63 | evbuf, 24, evbuf); | ||
64 | #endif | ||
65 | |||
66 | /* | ||
67 | * Because the event buffer is located in a page which is owned | ||
68 | * by the SCLP core, all data of interest must be copied. The | ||
69 | * error indication is in 'sclp_ftp_ldflg' | ||
70 | */ | ||
71 | sclp_ftp_ldflg = diag->mdd.ftp.ldflg; | ||
72 | sclp_ftp_fsize = diag->mdd.ftp.fsize; | ||
73 | sclp_ftp_length = diag->mdd.ftp.length; | ||
74 | |||
75 | complete(&sclp_ftp_rx_complete); | ||
76 | } | ||
77 | |||
78 | /** | ||
79 | * sclp_ftp_et7() - start a Diagnostic Test FTP Service SCLP request | ||
80 | * @ftp: pointer to FTP descriptor | ||
81 | * | ||
82 | * Return: 0 on success, else a (negative) error code | ||
83 | */ | ||
84 | static int sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec *ftp) | ||
85 | { | ||
86 | struct completion completion; | ||
87 | struct sclp_diag_sccb *sccb; | ||
88 | struct sclp_req *req; | ||
89 | size_t len; | ||
90 | int rc; | ||
91 | |||
92 | req = kzalloc(sizeof(*req), GFP_KERNEL); | ||
93 | sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
94 | if (!req || !sccb) { | ||
95 | rc = -ENOMEM; | ||
96 | goto out_free; | ||
97 | } | ||
98 | |||
99 | sccb->hdr.length = SCLP_DIAG_FTP_EVBUF_LEN + | ||
100 | sizeof(struct sccb_header); | ||
101 | sccb->evbuf.hdr.type = EVTYP_DIAG_TEST; | ||
102 | sccb->evbuf.hdr.length = SCLP_DIAG_FTP_EVBUF_LEN; | ||
103 | sccb->evbuf.hdr.flags = 0; /* clear processed-buffer */ | ||
104 | sccb->evbuf.route = SCLP_DIAG_FTP_ROUTE; | ||
105 | sccb->evbuf.mdd.ftp.pcx = SCLP_DIAG_FTP_XPCX; | ||
106 | sccb->evbuf.mdd.ftp.srcflg = 0; | ||
107 | sccb->evbuf.mdd.ftp.pgsize = 0; | ||
108 | sccb->evbuf.mdd.ftp.asce = _ASCE_REAL_SPACE; | ||
109 | sccb->evbuf.mdd.ftp.ldflg = SCLP_DIAG_FTP_LDFAIL; | ||
110 | sccb->evbuf.mdd.ftp.fsize = 0; | ||
111 | sccb->evbuf.mdd.ftp.cmd = ftp->id; | ||
112 | sccb->evbuf.mdd.ftp.offset = ftp->ofs; | ||
113 | sccb->evbuf.mdd.ftp.length = ftp->len; | ||
114 | sccb->evbuf.mdd.ftp.bufaddr = virt_to_phys(ftp->buf); | ||
115 | |||
116 | len = strlcpy(sccb->evbuf.mdd.ftp.fident, ftp->fname, | ||
117 | HMCDRV_FTP_FIDENT_MAX); | ||
118 | if (len >= HMCDRV_FTP_FIDENT_MAX) { | ||
119 | rc = -EINVAL; | ||
120 | goto out_free; | ||
121 | } | ||
122 | |||
123 | req->command = SCLP_CMDW_WRITE_EVENT_DATA; | ||
124 | req->sccb = sccb; | ||
125 | req->status = SCLP_REQ_FILLED; | ||
126 | req->callback = sclp_ftp_txcb; | ||
127 | req->callback_data = &completion; | ||
128 | |||
129 | init_completion(&completion); | ||
130 | |||
131 | rc = sclp_add_request(req); | ||
132 | if (rc) | ||
133 | goto out_free; | ||
134 | |||
135 | /* Wait for end of ftp sclp command. */ | ||
136 | wait_for_completion(&completion); | ||
137 | |||
138 | #ifdef DEBUG | ||
139 | pr_debug("status of SCLP (ET7) request is 0x%04x (0x%02x)\n", | ||
140 | sccb->hdr.response_code, sccb->evbuf.hdr.flags); | ||
141 | #endif | ||
142 | |||
143 | /* | ||
144 | * Check if sclp accepted the request. The data transfer runs | ||
145 | * asynchronously and the completion is indicated with an | ||
146 | * sclp ET7 event. | ||
147 | */ | ||
148 | if (req->status != SCLP_REQ_DONE || | ||
149 | (sccb->evbuf.hdr.flags & 0x80) == 0 || /* processed-buffer */ | ||
150 | (sccb->hdr.response_code & 0xffU) != 0x20U) { | ||
151 | rc = -EIO; | ||
152 | } | ||
153 | |||
154 | out_free: | ||
155 | free_page((unsigned long) sccb); | ||
156 | kfree(req); | ||
157 | return rc; | ||
158 | } | ||
159 | |||
160 | /** | ||
161 | * sclp_ftp_cmd() - executes a HMC related SCLP Diagnose (ET7) FTP command | ||
162 | * @ftp: pointer to FTP command specification | ||
163 | * @fsize: return of file size (or NULL if undesirable) | ||
164 | * | ||
165 | * Attention: Notice that this function is not reentrant - so the caller | ||
166 | * must ensure locking. | ||
167 | * | ||
168 | * Return: number of bytes read/written or a (negative) error code | ||
169 | */ | ||
170 | ssize_t sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize) | ||
171 | { | ||
172 | ssize_t len; | ||
173 | #ifdef DEBUG | ||
174 | unsigned long start_jiffies; | ||
175 | |||
176 | pr_debug("starting SCLP (ET7), cmd %d for '%s' at %lld with %zd bytes\n", | ||
177 | ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len); | ||
178 | start_jiffies = jiffies; | ||
179 | #endif | ||
180 | |||
181 | init_completion(&sclp_ftp_rx_complete); | ||
182 | |||
183 | /* Start ftp sclp command. */ | ||
184 | len = sclp_ftp_et7(ftp); | ||
185 | if (len) | ||
186 | goto out_unlock; | ||
187 | |||
188 | /* | ||
189 | * There is no way to cancel the sclp ET7 request, the code | ||
190 | * needs to wait unconditionally until the transfer is complete. | ||
191 | */ | ||
192 | wait_for_completion(&sclp_ftp_rx_complete); | ||
193 | |||
194 | #ifdef DEBUG | ||
195 | pr_debug("completed SCLP (ET7) request after %lu ms (all)\n", | ||
196 | (jiffies - start_jiffies) * 1000 / HZ); | ||
197 | pr_debug("return code of SCLP (ET7) FTP Service is 0x%02x, with %lld/%lld bytes\n", | ||
198 | sclp_ftp_ldflg, sclp_ftp_length, sclp_ftp_fsize); | ||
199 | #endif | ||
200 | |||
201 | switch (sclp_ftp_ldflg) { | ||
202 | case SCLP_DIAG_FTP_OK: | ||
203 | len = sclp_ftp_length; | ||
204 | if (fsize) | ||
205 | *fsize = sclp_ftp_fsize; | ||
206 | break; | ||
207 | case SCLP_DIAG_FTP_LDNPERM: | ||
208 | len = -EPERM; | ||
209 | break; | ||
210 | case SCLP_DIAG_FTP_LDRUNS: | ||
211 | len = -EBUSY; | ||
212 | break; | ||
213 | case SCLP_DIAG_FTP_LDFAIL: | ||
214 | len = -ENOENT; | ||
215 | break; | ||
216 | default: | ||
217 | len = -EIO; | ||
218 | break; | ||
219 | } | ||
220 | |||
221 | out_unlock: | ||
222 | return len; | ||
223 | } | ||
224 | |||
225 | /* | ||
226 | * ET7 event listener | ||
227 | */ | ||
228 | static struct sclp_register sclp_ftp_event = { | ||
229 | .send_mask = EVTYP_DIAG_TEST_MASK, /* want tx events */ | ||
230 | .receive_mask = EVTYP_DIAG_TEST_MASK, /* want rx events */ | ||
231 | .receiver_fn = sclp_ftp_rxcb, /* async callback (rx) */ | ||
232 | .state_change_fn = NULL, | ||
233 | .pm_event_fn = NULL, | ||
234 | }; | ||
235 | |||
236 | /** | ||
237 | * sclp_ftp_startup() - startup of FTP services, when running on LPAR | ||
238 | */ | ||
239 | int sclp_ftp_startup(void) | ||
240 | { | ||
241 | #ifdef DEBUG | ||
242 | unsigned long info; | ||
243 | #endif | ||
244 | int rc; | ||
245 | |||
246 | rc = sclp_register(&sclp_ftp_event); | ||
247 | if (rc) | ||
248 | return rc; | ||
249 | |||
250 | #ifdef DEBUG | ||
251 | info = get_zeroed_page(GFP_KERNEL); | ||
252 | |||
253 | if (info != 0) { | ||
254 | struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info; | ||
255 | |||
256 | if (!stsi(info222, 2, 2, 2)) { /* get SYSIB 2.2.2 */ | ||
257 | info222->name[sizeof(info222->name) - 1] = '\0'; | ||
258 | EBCASC_500(info222->name, sizeof(info222->name) - 1); | ||
259 | pr_debug("SCLP (ET7) FTP Service working on LPAR %u (%s)\n", | ||
260 | info222->lpar_number, info222->name); | ||
261 | } | ||
262 | |||
263 | free_page(info); | ||
264 | } | ||
265 | #endif /* DEBUG */ | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | /** | ||
270 | * sclp_ftp_shutdown() - shutdown of FTP services, when running on LPAR | ||
271 | */ | ||
272 | void sclp_ftp_shutdown(void) | ||
273 | { | ||
274 | sclp_unregister(&sclp_ftp_event); | ||
275 | } | ||
diff --git a/drivers/s390/char/sclp_ftp.h b/drivers/s390/char/sclp_ftp.h new file mode 100644 index 000000000000..98ba3183e7d9 --- /dev/null +++ b/drivers/s390/char/sclp_ftp.h | |||
@@ -0,0 +1,21 @@ | |||
1 | /* | ||
2 | * SCLP Event Type (ET) 7 - Diagnostic Test FTP Services, useable on LPAR | ||
3 | * | ||
4 | * Notice that all functions exported here are not reentrant. | ||
5 | * So usage should be exclusive, ensured by the caller (e.g. using a | ||
6 | * mutex). | ||
7 | * | ||
8 | * Copyright IBM Corp. 2013 | ||
9 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
10 | */ | ||
11 | |||
12 | #ifndef __SCLP_FTP_H__ | ||
13 | #define __SCLP_FTP_H__ | ||
14 | |||
15 | #include "hmcdrv_ftp.h" | ||
16 | |||
17 | int sclp_ftp_startup(void); | ||
18 | void sclp_ftp_shutdown(void); | ||
19 | ssize_t sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize); | ||
20 | |||
21 | #endif /* __SCLP_FTP_H__ */ | ||
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c index 3b13d58fe87b..35a84af875ee 100644 --- a/drivers/s390/char/sclp_rw.c +++ b/drivers/s390/char/sclp_rw.c | |||
@@ -33,7 +33,7 @@ static void sclp_rw_pm_event(struct sclp_register *reg, | |||
33 | 33 | ||
34 | /* Event type structure for write message and write priority message */ | 34 | /* Event type structure for write message and write priority message */ |
35 | static struct sclp_register sclp_rw_event = { | 35 | static struct sclp_register sclp_rw_event = { |
36 | .send_mask = EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK, | 36 | .send_mask = EVTYP_MSG_MASK, |
37 | .pm_event_fn = sclp_rw_pm_event, | 37 | .pm_event_fn = sclp_rw_pm_event, |
38 | }; | 38 | }; |
39 | 39 | ||
@@ -456,14 +456,9 @@ sclp_emit_buffer(struct sclp_buffer *buffer, | |||
456 | return -EIO; | 456 | return -EIO; |
457 | 457 | ||
458 | sccb = buffer->sccb; | 458 | sccb = buffer->sccb; |
459 | if (sclp_rw_event.sclp_receive_mask & EVTYP_MSG_MASK) | 459 | /* Use normal write message */ |
460 | /* Use normal write message */ | 460 | sccb->msg_buf.header.type = EVTYP_MSG; |
461 | sccb->msg_buf.header.type = EVTYP_MSG; | 461 | |
462 | else if (sclp_rw_event.sclp_receive_mask & EVTYP_PMSGCMD_MASK) | ||
463 | /* Use write priority message */ | ||
464 | sccb->msg_buf.header.type = EVTYP_PMSGCMD; | ||
465 | else | ||
466 | return -EOPNOTSUPP; | ||
467 | buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA; | 462 | buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA; |
468 | buffer->request.status = SCLP_REQ_FILLED; | 463 | buffer->request.status = SCLP_REQ_FILLED; |
469 | buffer->request.callback = sclp_writedata_callback; | 464 | buffer->request.callback = sclp_writedata_callback; |
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index b9a9f721716d..ae67386c03d3 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c | |||
@@ -206,10 +206,6 @@ sclp_vt220_callback(struct sclp_req *request, void *data) | |||
206 | static int | 206 | static int |
207 | __sclp_vt220_emit(struct sclp_vt220_request *request) | 207 | __sclp_vt220_emit(struct sclp_vt220_request *request) |
208 | { | 208 | { |
209 | if (!(sclp_vt220_register.sclp_receive_mask & EVTYP_VT220MSG_MASK)) { | ||
210 | request->sclp_req.status = SCLP_REQ_FAILED; | ||
211 | return -EIO; | ||
212 | } | ||
213 | request->sclp_req.command = SCLP_CMDW_WRITE_EVENT_DATA; | 209 | request->sclp_req.command = SCLP_CMDW_WRITE_EVENT_DATA; |
214 | request->sclp_req.status = SCLP_REQ_FILLED; | 210 | request->sclp_req.status = SCLP_REQ_FILLED; |
215 | request->sclp_req.callback = sclp_vt220_callback; | 211 | request->sclp_req.callback = sclp_vt220_callback; |
diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c index 6dc60725de92..77f9b9c2f701 100644 --- a/drivers/s390/char/tape_char.c +++ b/drivers/s390/char/tape_char.c | |||
@@ -402,7 +402,9 @@ __tapechar_ioctl(struct tape_device *device, | |||
402 | memset(&get, 0, sizeof(get)); | 402 | memset(&get, 0, sizeof(get)); |
403 | get.mt_type = MT_ISUNKNOWN; | 403 | get.mt_type = MT_ISUNKNOWN; |
404 | get.mt_resid = 0 /* device->devstat.rescnt */; | 404 | get.mt_resid = 0 /* device->devstat.rescnt */; |
405 | get.mt_dsreg = device->tape_state; | 405 | get.mt_dsreg = |
406 | ((device->char_data.block_size << MT_ST_BLKSIZE_SHIFT) | ||
407 | & MT_ST_BLKSIZE_MASK); | ||
406 | /* FIXME: mt_gstat, mt_erreg, mt_fileno */ | 408 | /* FIXME: mt_gstat, mt_erreg, mt_fileno */ |
407 | get.mt_gstat = 0; | 409 | get.mt_gstat = 0; |
408 | get.mt_erreg = 0; | 410 | get.mt_erreg = 0; |
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 1884653e4472..efcf48481c5f 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <asm/processor.h> | 28 | #include <asm/processor.h> |
29 | #include <asm/irqflags.h> | 29 | #include <asm/irqflags.h> |
30 | #include <asm/checksum.h> | 30 | #include <asm/checksum.h> |
31 | #include <asm/switch_to.h> | ||
31 | #include "sclp.h" | 32 | #include "sclp.h" |
32 | 33 | ||
33 | #define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x) | 34 | #define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x) |
@@ -149,18 +150,21 @@ static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count) | |||
149 | 150 | ||
150 | static int __init init_cpu_info(enum arch_id arch) | 151 | static int __init init_cpu_info(enum arch_id arch) |
151 | { | 152 | { |
152 | struct save_area *sa; | 153 | struct save_area_ext *sa_ext; |
153 | 154 | ||
154 | /* get info for boot cpu from lowcore, stored in the HSA */ | 155 | /* get info for boot cpu from lowcore, stored in the HSA */ |
155 | 156 | ||
156 | sa = dump_save_area_create(0); | 157 | sa_ext = dump_save_area_create(0); |
157 | if (!sa) | 158 | if (!sa_ext) |
158 | return -ENOMEM; | 159 | return -ENOMEM; |
159 | if (memcpy_hsa_kernel(sa, sys_info.sa_base, sys_info.sa_size) < 0) { | 160 | if (memcpy_hsa_kernel(&sa_ext->sa, sys_info.sa_base, |
161 | sys_info.sa_size) < 0) { | ||
160 | TRACE("could not copy from HSA\n"); | 162 | TRACE("could not copy from HSA\n"); |
161 | kfree(sa); | 163 | kfree(sa_ext); |
162 | return -EIO; | 164 | return -EIO; |
163 | } | 165 | } |
166 | if (MACHINE_HAS_VX) | ||
167 | save_vx_regs_safe(sa_ext->vx_regs); | ||
164 | return 0; | 168 | return 0; |
165 | } | 169 | } |
166 | 170 | ||
@@ -258,7 +262,7 @@ static int zcore_add_lc(char __user *buf, unsigned long start, size_t count) | |||
258 | unsigned long sa_start, sa_end; /* save area range */ | 262 | unsigned long sa_start, sa_end; /* save area range */ |
259 | unsigned long prefix; | 263 | unsigned long prefix; |
260 | unsigned long sa_off, len, buf_off; | 264 | unsigned long sa_off, len, buf_off; |
261 | struct save_area *save_area = dump_save_areas.areas[i]; | 265 | struct save_area *save_area = &dump_save_areas.areas[i]->sa; |
262 | 266 | ||
263 | prefix = save_area->pref_reg; | 267 | prefix = save_area->pref_reg; |
264 | sa_start = prefix + sys_info.sa_base; | 268 | sa_start = prefix + sys_info.sa_base; |
@@ -612,7 +616,7 @@ static void __init zcore_header_init(int arch, struct zcore_header *hdr, | |||
612 | hdr->tod = get_tod_clock(); | 616 | hdr->tod = get_tod_clock(); |
613 | get_cpu_id(&hdr->cpu_id); | 617 | get_cpu_id(&hdr->cpu_id); |
614 | for (i = 0; i < dump_save_areas.count; i++) { | 618 | for (i = 0; i < dump_save_areas.count; i++) { |
615 | prefix = dump_save_areas.areas[i]->pref_reg; | 619 | prefix = dump_save_areas.areas[i]->sa.pref_reg; |
616 | hdr->real_cpu_cnt++; | 620 | hdr->real_cpu_cnt++; |
617 | if (!prefix) | 621 | if (!prefix) |
618 | continue; | 622 | continue; |
diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index 00bfbee0af9e..56eb4ee4deba 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c | |||
@@ -87,7 +87,7 @@ static irqreturn_t do_airq_interrupt(int irq, void *dummy) | |||
87 | struct airq_struct *airq; | 87 | struct airq_struct *airq; |
88 | struct hlist_head *head; | 88 | struct hlist_head *head; |
89 | 89 | ||
90 | __this_cpu_write(s390_idle.nohz_delay, 1); | 90 | set_cpu_flag(CIF_NOHZ_DELAY); |
91 | tpi_info = (struct tpi_info *) &get_irq_regs()->int_code; | 91 | tpi_info = (struct tpi_info *) &get_irq_regs()->int_code; |
92 | head = &airq_lists[tpi_info->isc]; | 92 | head = &airq_lists[tpi_info->isc]; |
93 | rcu_read_lock(); | 93 | rcu_read_lock(); |
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 2905d8b0ec95..d5a6f287d2fe 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c | |||
@@ -561,7 +561,7 @@ static irqreturn_t do_cio_interrupt(int irq, void *dummy) | |||
561 | struct subchannel *sch; | 561 | struct subchannel *sch; |
562 | struct irb *irb; | 562 | struct irb *irb; |
563 | 563 | ||
564 | __this_cpu_write(s390_idle.nohz_delay, 1); | 564 | set_cpu_flag(CIF_NOHZ_DELAY); |
565 | tpi_info = (struct tpi_info *) &get_irq_regs()->int_code; | 565 | tpi_info = (struct tpi_info *) &get_irq_regs()->int_code; |
566 | irb = &__get_cpu_var(cio_irb); | 566 | irb = &__get_cpu_var(cio_irb); |
567 | sch = (struct subchannel *)(unsigned long) tpi_info->intparm; | 567 | sch = (struct subchannel *)(unsigned long) tpi_info->intparm; |
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 4038437ff033..99485415dcc2 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c | |||
@@ -664,6 +664,17 @@ static ssize_t ap_hwtype_show(struct device *dev, | |||
664 | } | 664 | } |
665 | 665 | ||
666 | static DEVICE_ATTR(hwtype, 0444, ap_hwtype_show, NULL); | 666 | static DEVICE_ATTR(hwtype, 0444, ap_hwtype_show, NULL); |
667 | |||
668 | static ssize_t ap_raw_hwtype_show(struct device *dev, | ||
669 | struct device_attribute *attr, char *buf) | ||
670 | { | ||
671 | struct ap_device *ap_dev = to_ap_dev(dev); | ||
672 | |||
673 | return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->raw_hwtype); | ||
674 | } | ||
675 | |||
676 | static DEVICE_ATTR(raw_hwtype, 0444, ap_raw_hwtype_show, NULL); | ||
677 | |||
667 | static ssize_t ap_depth_show(struct device *dev, struct device_attribute *attr, | 678 | static ssize_t ap_depth_show(struct device *dev, struct device_attribute *attr, |
668 | char *buf) | 679 | char *buf) |
669 | { | 680 | { |
@@ -734,6 +745,7 @@ static DEVICE_ATTR(ap_functions, 0444, ap_functions_show, NULL); | |||
734 | 745 | ||
735 | static struct attribute *ap_dev_attrs[] = { | 746 | static struct attribute *ap_dev_attrs[] = { |
736 | &dev_attr_hwtype.attr, | 747 | &dev_attr_hwtype.attr, |
748 | &dev_attr_raw_hwtype.attr, | ||
737 | &dev_attr_depth.attr, | 749 | &dev_attr_depth.attr, |
738 | &dev_attr_request_count.attr, | 750 | &dev_attr_request_count.attr, |
739 | &dev_attr_requestq_count.attr, | 751 | &dev_attr_requestq_count.attr, |
@@ -1188,6 +1200,10 @@ static int ap_select_domain(void) | |||
1188 | ap_qid_t qid; | 1200 | ap_qid_t qid; |
1189 | int rc, i, j; | 1201 | int rc, i, j; |
1190 | 1202 | ||
1203 | /* IF APXA isn't installed, only 16 domains could be defined */ | ||
1204 | if (!ap_configuration->ap_extended && (ap_domain_index > 15)) | ||
1205 | return -EINVAL; | ||
1206 | |||
1191 | /* | 1207 | /* |
1192 | * We want to use a single domain. Either the one specified with | 1208 | * We want to use a single domain. Either the one specified with |
1193 | * the "domain=" parameter or the domain with the maximum number | 1209 | * the "domain=" parameter or the domain with the maximum number |
@@ -1413,9 +1429,13 @@ static void ap_scan_bus(struct work_struct *unused) | |||
1413 | continue; | 1429 | continue; |
1414 | } | 1430 | } |
1415 | break; | 1431 | break; |
1432 | case 11: | ||
1433 | ap_dev->device_type = 10; | ||
1434 | break; | ||
1416 | default: | 1435 | default: |
1417 | ap_dev->device_type = device_type; | 1436 | ap_dev->device_type = device_type; |
1418 | } | 1437 | } |
1438 | ap_dev->raw_hwtype = device_type; | ||
1419 | 1439 | ||
1420 | rc = ap_query_functions(qid, &device_functions); | 1440 | rc = ap_query_functions(qid, &device_functions); |
1421 | if (!rc) | 1441 | if (!rc) |
@@ -1900,9 +1920,15 @@ static void ap_reset_all(void) | |||
1900 | { | 1920 | { |
1901 | int i, j; | 1921 | int i, j; |
1902 | 1922 | ||
1903 | for (i = 0; i < AP_DOMAINS; i++) | 1923 | for (i = 0; i < AP_DOMAINS; i++) { |
1904 | for (j = 0; j < AP_DEVICES; j++) | 1924 | if (!ap_test_config_domain(i)) |
1925 | continue; | ||
1926 | for (j = 0; j < AP_DEVICES; j++) { | ||
1927 | if (!ap_test_config_card_id(j)) | ||
1928 | continue; | ||
1905 | ap_reset_queue(AP_MKQID(j, i)); | 1929 | ap_reset_queue(AP_MKQID(j, i)); |
1930 | } | ||
1931 | } | ||
1906 | } | 1932 | } |
1907 | 1933 | ||
1908 | static struct reset_call ap_reset_call = { | 1934 | static struct reset_call ap_reset_call = { |
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 6405ae24a7a6..055a0f956d17 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h | |||
@@ -31,7 +31,7 @@ | |||
31 | #include <linux/types.h> | 31 | #include <linux/types.h> |
32 | 32 | ||
33 | #define AP_DEVICES 64 /* Number of AP devices. */ | 33 | #define AP_DEVICES 64 /* Number of AP devices. */ |
34 | #define AP_DOMAINS 16 /* Number of AP domains. */ | 34 | #define AP_DOMAINS 256 /* Number of AP domains. */ |
35 | #define AP_MAX_RESET 90 /* Maximum number of resets. */ | 35 | #define AP_MAX_RESET 90 /* Maximum number of resets. */ |
36 | #define AP_RESET_TIMEOUT (HZ*0.7) /* Time in ticks for reset timeouts. */ | 36 | #define AP_RESET_TIMEOUT (HZ*0.7) /* Time in ticks for reset timeouts. */ |
37 | #define AP_CONFIG_TIME 30 /* Time in seconds between AP bus rescans. */ | 37 | #define AP_CONFIG_TIME 30 /* Time in seconds between AP bus rescans. */ |
@@ -45,9 +45,9 @@ extern int ap_domain_index; | |||
45 | */ | 45 | */ |
46 | typedef unsigned int ap_qid_t; | 46 | typedef unsigned int ap_qid_t; |
47 | 47 | ||
48 | #define AP_MKQID(_device,_queue) (((_device) & 63) << 8 | ((_queue) & 15)) | 48 | #define AP_MKQID(_device, _queue) (((_device) & 63) << 8 | ((_queue) & 255)) |
49 | #define AP_QID_DEVICE(_qid) (((_qid) >> 8) & 63) | 49 | #define AP_QID_DEVICE(_qid) (((_qid) >> 8) & 63) |
50 | #define AP_QID_QUEUE(_qid) ((_qid) & 15) | 50 | #define AP_QID_QUEUE(_qid) ((_qid) & 255) |
51 | 51 | ||
52 | /** | 52 | /** |
53 | * structy ap_queue_status - Holds the AP queue status. | 53 | * structy ap_queue_status - Holds the AP queue status. |
@@ -161,6 +161,7 @@ struct ap_device { | |||
161 | ap_qid_t qid; /* AP queue id. */ | 161 | ap_qid_t qid; /* AP queue id. */ |
162 | int queue_depth; /* AP queue depth.*/ | 162 | int queue_depth; /* AP queue depth.*/ |
163 | int device_type; /* AP device type. */ | 163 | int device_type; /* AP device type. */ |
164 | int raw_hwtype; /* AP raw hardware type. */ | ||
164 | unsigned int functions; /* AP device function bitfield. */ | 165 | unsigned int functions; /* AP device function bitfield. */ |
165 | int unregistered; /* marks AP device as unregistered */ | 166 | int unregistered; /* marks AP device as unregistered */ |
166 | struct timer_list timeout; /* Timer for request timeouts. */ | 167 | struct timer_list timeout; /* Timer for request timeouts. */ |
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 0e18c5dcd91f..08f1830cbfc4 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c | |||
@@ -343,10 +343,11 @@ struct zcrypt_ops *__ops_lookup(unsigned char *name, int variant) | |||
343 | break; | 343 | break; |
344 | } | 344 | } |
345 | } | 345 | } |
346 | if (!found || !try_module_get(zops->owner)) | ||
347 | zops = NULL; | ||
348 | |||
346 | spin_unlock_bh(&zcrypt_ops_list_lock); | 349 | spin_unlock_bh(&zcrypt_ops_list_lock); |
347 | 350 | ||
348 | if (!found) | ||
349 | return NULL; | ||
350 | return zops; | 351 | return zops; |
351 | } | 352 | } |
352 | 353 | ||
@@ -359,8 +360,6 @@ struct zcrypt_ops *zcrypt_msgtype_request(unsigned char *name, int variant) | |||
359 | request_module("%s", name); | 360 | request_module("%s", name); |
360 | zops = __ops_lookup(name, variant); | 361 | zops = __ops_lookup(name, variant); |
361 | } | 362 | } |
362 | if ((!zops) || (!try_module_get(zops->owner))) | ||
363 | return NULL; | ||
364 | return zops; | 363 | return zops; |
365 | } | 364 | } |
366 | EXPORT_SYMBOL(zcrypt_msgtype_request); | 365 | EXPORT_SYMBOL(zcrypt_msgtype_request); |