diff options
Diffstat (limited to 'drivers/s390/block/dasd_eckd.c')
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 337 |
1 files changed, 200 insertions, 137 deletions
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 7d5a6cee4bd8..0dfab30e8089 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * File...........: linux/drivers/s390/block/dasd_eckd.c | 2 | * File...........: linux/drivers/s390/block/dasd_eckd.c |
3 | * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> | 3 | * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> |
4 | * Horst Hummel <Horst.Hummel@de.ibm.com> | 4 | * Horst Hummel <Horst.Hummel@de.ibm.com> |
5 | * Carsten Otte <Cotte@de.ibm.com> | 5 | * Carsten Otte <Cotte@de.ibm.com> |
6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
7 | * Bugreports.to..: <Linux390@de.ibm.com> | 7 | * Bugreports.to..: <Linux390@de.ibm.com> |
@@ -24,6 +24,7 @@ | |||
24 | #include <asm/io.h> | 24 | #include <asm/io.h> |
25 | #include <asm/todclk.h> | 25 | #include <asm/todclk.h> |
26 | #include <asm/uaccess.h> | 26 | #include <asm/uaccess.h> |
27 | #include <asm/cio.h> | ||
27 | #include <asm/ccwdev.h> | 28 | #include <asm/ccwdev.h> |
28 | 29 | ||
29 | #include "dasd_int.h" | 30 | #include "dasd_int.h" |
@@ -89,17 +90,22 @@ dasd_eckd_probe (struct ccw_device *cdev) | |||
89 | { | 90 | { |
90 | int ret; | 91 | int ret; |
91 | 92 | ||
92 | ret = dasd_generic_probe (cdev, &dasd_eckd_discipline); | 93 | /* set ECKD specific ccw-device options */ |
93 | if (ret) | 94 | ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE); |
95 | if (ret) { | ||
96 | printk(KERN_WARNING | ||
97 | "dasd_eckd_probe: could not set ccw-device options " | ||
98 | "for %s\n", cdev->dev.bus_id); | ||
94 | return ret; | 99 | return ret; |
95 | ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP | CCWDEV_ALLOW_FORCE); | 100 | } |
96 | return 0; | 101 | ret = dasd_generic_probe(cdev, &dasd_eckd_discipline); |
102 | return ret; | ||
97 | } | 103 | } |
98 | 104 | ||
99 | static int | 105 | static int |
100 | dasd_eckd_set_online(struct ccw_device *cdev) | 106 | dasd_eckd_set_online(struct ccw_device *cdev) |
101 | { | 107 | { |
102 | return dasd_generic_set_online (cdev, &dasd_eckd_discipline); | 108 | return dasd_generic_set_online(cdev, &dasd_eckd_discipline); |
103 | } | 109 | } |
104 | 110 | ||
105 | static struct ccw_driver dasd_eckd_driver = { | 111 | static struct ccw_driver dasd_eckd_driver = { |
@@ -210,14 +216,14 @@ check_XRC (struct ccw1 *de_ccw, | |||
210 | 216 | ||
211 | /* switch on System Time Stamp - needed for XRC Support */ | 217 | /* switch on System Time Stamp - needed for XRC Support */ |
212 | if (private->rdc_data.facilities.XRC_supported) { | 218 | if (private->rdc_data.facilities.XRC_supported) { |
213 | 219 | ||
214 | data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */ | 220 | data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */ |
215 | data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */ | 221 | data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */ |
216 | 222 | ||
217 | data->ep_sys_time = get_clock (); | 223 | data->ep_sys_time = get_clock (); |
218 | 224 | ||
219 | de_ccw->count = sizeof (struct DE_eckd_data); | 225 | de_ccw->count = sizeof (struct DE_eckd_data); |
220 | de_ccw->flags |= CCW_FLAG_SLI; | 226 | de_ccw->flags |= CCW_FLAG_SLI; |
221 | } | 227 | } |
222 | 228 | ||
223 | return; | 229 | return; |
@@ -296,8 +302,8 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, | |||
296 | /* check for sequential prestage - enhance cylinder range */ | 302 | /* check for sequential prestage - enhance cylinder range */ |
297 | if (data->attributes.operation == DASD_SEQ_PRESTAGE || | 303 | if (data->attributes.operation == DASD_SEQ_PRESTAGE || |
298 | data->attributes.operation == DASD_SEQ_ACCESS) { | 304 | data->attributes.operation == DASD_SEQ_ACCESS) { |
299 | 305 | ||
300 | if (end.cyl + private->attrib.nr_cyl < geo.cyl) | 306 | if (end.cyl + private->attrib.nr_cyl < geo.cyl) |
301 | end.cyl += private->attrib.nr_cyl; | 307 | end.cyl += private->attrib.nr_cyl; |
302 | else | 308 | else |
303 | end.cyl = (geo.cyl - 1); | 309 | end.cyl = (geo.cyl - 1); |
@@ -317,7 +323,7 @@ locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk, | |||
317 | struct dasd_eckd_private *private; | 323 | struct dasd_eckd_private *private; |
318 | int sector; | 324 | int sector; |
319 | int dn, d; | 325 | int dn, d; |
320 | 326 | ||
321 | private = (struct dasd_eckd_private *) device->private; | 327 | private = (struct dasd_eckd_private *) device->private; |
322 | 328 | ||
323 | DBF_DEV_EVENT(DBF_INFO, device, | 329 | DBF_DEV_EVENT(DBF_INFO, device, |
@@ -541,6 +547,86 @@ dasd_eckd_read_conf(struct dasd_device *device) | |||
541 | } | 547 | } |
542 | 548 | ||
543 | /* | 549 | /* |
550 | * Build CP for Perform Subsystem Function - SSC. | ||
551 | */ | ||
552 | struct dasd_ccw_req * | ||
553 | dasd_eckd_build_psf_ssc(struct dasd_device *device) | ||
554 | { | ||
555 | struct dasd_ccw_req *cqr; | ||
556 | struct dasd_psf_ssc_data *psf_ssc_data; | ||
557 | struct ccw1 *ccw; | ||
558 | |||
559 | cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ , | ||
560 | sizeof(struct dasd_psf_ssc_data), | ||
561 | device); | ||
562 | |||
563 | if (IS_ERR(cqr)) { | ||
564 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
565 | "Could not allocate PSF-SSC request"); | ||
566 | return cqr; | ||
567 | } | ||
568 | psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data; | ||
569 | psf_ssc_data->order = PSF_ORDER_SSC; | ||
570 | psf_ssc_data->suborder = 0x08; | ||
571 | |||
572 | ccw = cqr->cpaddr; | ||
573 | ccw->cmd_code = DASD_ECKD_CCW_PSF; | ||
574 | ccw->cda = (__u32)(addr_t)psf_ssc_data; | ||
575 | ccw->count = 66; | ||
576 | |||
577 | cqr->device = device; | ||
578 | cqr->expires = 10*HZ; | ||
579 | cqr->buildclk = get_clock(); | ||
580 | cqr->status = DASD_CQR_FILLED; | ||
581 | return cqr; | ||
582 | } | ||
583 | |||
584 | /* | ||
585 | * Perform Subsystem Function. | ||
586 | * It is necessary to trigger CIO for channel revalidation since this | ||
587 | * call might change behaviour of DASD devices. | ||
588 | */ | ||
589 | static int | ||
590 | dasd_eckd_psf_ssc(struct dasd_device *device) | ||
591 | { | ||
592 | struct dasd_ccw_req *cqr; | ||
593 | int rc; | ||
594 | |||
595 | cqr = dasd_eckd_build_psf_ssc(device); | ||
596 | if (IS_ERR(cqr)) | ||
597 | return PTR_ERR(cqr); | ||
598 | |||
599 | rc = dasd_sleep_on(cqr); | ||
600 | if (!rc) | ||
601 | /* trigger CIO to reprobe devices */ | ||
602 | css_schedule_reprobe(); | ||
603 | dasd_sfree_request(cqr, cqr->device); | ||
604 | return rc; | ||
605 | } | ||
606 | |||
607 | /* | ||
608 | * Valide storage server of current device. | ||
609 | */ | ||
610 | static int | ||
611 | dasd_eckd_validate_server(struct dasd_device *device) | ||
612 | { | ||
613 | int rc; | ||
614 | |||
615 | /* Currently PAV is the only reason to 'validate' server on LPAR */ | ||
616 | if (dasd_nopav || MACHINE_IS_VM) | ||
617 | return 0; | ||
618 | |||
619 | rc = dasd_eckd_psf_ssc(device); | ||
620 | if (rc) | ||
621 | /* may be requested feature is not available on server, | ||
622 | * therefore just report error and go ahead */ | ||
623 | DEV_MESSAGE(KERN_INFO, device, | ||
624 | "Perform Subsystem Function returned rc=%d", rc); | ||
625 | /* RE-Read Configuration Data */ | ||
626 | return dasd_eckd_read_conf(device); | ||
627 | } | ||
628 | |||
629 | /* | ||
544 | * Check device characteristics. | 630 | * Check device characteristics. |
545 | * If the device is accessible using ECKD discipline, the device is enabled. | 631 | * If the device is accessible using ECKD discipline, the device is enabled. |
546 | */ | 632 | */ |
@@ -554,7 +640,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device) | |||
554 | 640 | ||
555 | private = (struct dasd_eckd_private *) device->private; | 641 | private = (struct dasd_eckd_private *) device->private; |
556 | if (private == NULL) { | 642 | if (private == NULL) { |
557 | private = kmalloc(sizeof(struct dasd_eckd_private), | 643 | private = kzalloc(sizeof(struct dasd_eckd_private), |
558 | GFP_KERNEL | GFP_DMA); | 644 | GFP_KERNEL | GFP_DMA); |
559 | if (private == NULL) { | 645 | if (private == NULL) { |
560 | DEV_MESSAGE(KERN_WARNING, device, "%s", | 646 | DEV_MESSAGE(KERN_WARNING, device, "%s", |
@@ -562,7 +648,6 @@ dasd_eckd_check_characteristics(struct dasd_device *device) | |||
562 | "data"); | 648 | "data"); |
563 | return -ENOMEM; | 649 | return -ENOMEM; |
564 | } | 650 | } |
565 | memset(private, 0, sizeof(struct dasd_eckd_private)); | ||
566 | device->private = (void *) private; | 651 | device->private = (void *) private; |
567 | } | 652 | } |
568 | /* Invalidate status of initial analysis. */ | 653 | /* Invalidate status of initial analysis. */ |
@@ -571,16 +656,29 @@ dasd_eckd_check_characteristics(struct dasd_device *device) | |||
571 | private->attrib.operation = DASD_NORMAL_CACHE; | 656 | private->attrib.operation = DASD_NORMAL_CACHE; |
572 | private->attrib.nr_cyl = 0; | 657 | private->attrib.nr_cyl = 0; |
573 | 658 | ||
659 | /* Read Configuration Data */ | ||
660 | rc = dasd_eckd_read_conf(device); | ||
661 | if (rc) | ||
662 | return rc; | ||
663 | |||
664 | /* Generate device unique id and register in devmap */ | ||
665 | rc = dasd_eckd_generate_uid(device, &uid); | ||
666 | if (rc) | ||
667 | return rc; | ||
668 | rc = dasd_set_uid(device->cdev, &uid); | ||
669 | if (rc == 1) /* new server found */ | ||
670 | rc = dasd_eckd_validate_server(device); | ||
671 | if (rc) | ||
672 | return rc; | ||
673 | |||
574 | /* Read Device Characteristics */ | 674 | /* Read Device Characteristics */ |
575 | rdc_data = (void *) &(private->rdc_data); | 675 | rdc_data = (void *) &(private->rdc_data); |
576 | memset(rdc_data, 0, sizeof(rdc_data)); | 676 | memset(rdc_data, 0, sizeof(rdc_data)); |
577 | rc = read_dev_chars(device->cdev, &rdc_data, 64); | 677 | rc = read_dev_chars(device->cdev, &rdc_data, 64); |
578 | if (rc) { | 678 | if (rc) |
579 | DEV_MESSAGE(KERN_WARNING, device, | 679 | DEV_MESSAGE(KERN_WARNING, device, |
580 | "Read device characteristics returned error %d", | 680 | "Read device characteristics returned " |
581 | rc); | 681 | "rc=%d", rc); |
582 | return rc; | ||
583 | } | ||
584 | 682 | ||
585 | DEV_MESSAGE(KERN_INFO, device, | 683 | DEV_MESSAGE(KERN_INFO, device, |
586 | "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d", | 684 | "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d", |
@@ -591,19 +689,6 @@ dasd_eckd_check_characteristics(struct dasd_device *device) | |||
591 | private->rdc_data.no_cyl, | 689 | private->rdc_data.no_cyl, |
592 | private->rdc_data.trk_per_cyl, | 690 | private->rdc_data.trk_per_cyl, |
593 | private->rdc_data.sec_per_trk); | 691 | private->rdc_data.sec_per_trk); |
594 | |||
595 | /* Read Configuration Data */ | ||
596 | rc = dasd_eckd_read_conf (device); | ||
597 | if (rc) | ||
598 | return rc; | ||
599 | |||
600 | /* Generate device unique id and register in devmap */ | ||
601 | rc = dasd_eckd_generate_uid(device, &uid); | ||
602 | if (rc) | ||
603 | return rc; | ||
604 | |||
605 | rc = dasd_set_uid(device->cdev, &uid); | ||
606 | |||
607 | return rc; | 692 | return rc; |
608 | } | 693 | } |
609 | 694 | ||
@@ -773,7 +858,7 @@ dasd_eckd_end_analysis(struct dasd_device *device) | |||
773 | ((private->rdc_data.no_cyl * | 858 | ((private->rdc_data.no_cyl * |
774 | private->rdc_data.trk_per_cyl * | 859 | private->rdc_data.trk_per_cyl * |
775 | blk_per_trk * (device->bp_block >> 9)) >> 1), | 860 | blk_per_trk * (device->bp_block >> 9)) >> 1), |
776 | ((blk_per_trk * device->bp_block) >> 10), | 861 | ((blk_per_trk * device->bp_block) >> 10), |
777 | private->uses_cdl ? | 862 | private->uses_cdl ? |
778 | "compatible disk layout" : "linux disk layout"); | 863 | "compatible disk layout" : "linux disk layout"); |
779 | 864 | ||
@@ -970,7 +1055,7 @@ dasd_eckd_format_device(struct dasd_device * device, | |||
970 | if (i < 3) { | 1055 | if (i < 3) { |
971 | ect->kl = 4; | 1056 | ect->kl = 4; |
972 | ect->dl = sizes_trk0[i] - 4; | 1057 | ect->dl = sizes_trk0[i] - 4; |
973 | } | 1058 | } |
974 | } | 1059 | } |
975 | if ((fdata->intensity & 0x08) && | 1060 | if ((fdata->intensity & 0x08) && |
976 | fdata->start_unit == 1) { | 1061 | fdata->start_unit == 1) { |
@@ -1270,7 +1355,7 @@ dasd_eckd_fill_info(struct dasd_device * device, | |||
1270 | 1355 | ||
1271 | /* | 1356 | /* |
1272 | * Release device ioctl. | 1357 | * Release device ioctl. |
1273 | * Buils a channel programm to releases a prior reserved | 1358 | * Buils a channel programm to releases a prior reserved |
1274 | * (see dasd_eckd_reserve) device. | 1359 | * (see dasd_eckd_reserve) device. |
1275 | */ | 1360 | */ |
1276 | static int | 1361 | static int |
@@ -1310,8 +1395,8 @@ dasd_eckd_release(struct dasd_device *device) | |||
1310 | /* | 1395 | /* |
1311 | * Reserve device ioctl. | 1396 | * Reserve device ioctl. |
1312 | * Options are set to 'synchronous wait for interrupt' and | 1397 | * Options are set to 'synchronous wait for interrupt' and |
1313 | * 'timeout the request'. This leads to a terminate IO if | 1398 | * 'timeout the request'. This leads to a terminate IO if |
1314 | * the interrupt is outstanding for a certain time. | 1399 | * the interrupt is outstanding for a certain time. |
1315 | */ | 1400 | */ |
1316 | static int | 1401 | static int |
1317 | dasd_eckd_reserve(struct dasd_device *device) | 1402 | dasd_eckd_reserve(struct dasd_device *device) |
@@ -1349,7 +1434,7 @@ dasd_eckd_reserve(struct dasd_device *device) | |||
1349 | 1434 | ||
1350 | /* | 1435 | /* |
1351 | * Steal lock ioctl - unconditional reserve device. | 1436 | * Steal lock ioctl - unconditional reserve device. |
1352 | * Buils a channel programm to break a device's reservation. | 1437 | * Buils a channel programm to break a device's reservation. |
1353 | * (unconditional reserve) | 1438 | * (unconditional reserve) |
1354 | */ | 1439 | */ |
1355 | static int | 1440 | static int |
@@ -1522,6 +1607,40 @@ dasd_eckd_ioctl(struct dasd_device *device, unsigned int cmd, void __user *argp) | |||
1522 | } | 1607 | } |
1523 | 1608 | ||
1524 | /* | 1609 | /* |
1610 | * Dump the range of CCWs into 'page' buffer | ||
1611 | * and return number of printed chars. | ||
1612 | */ | ||
1613 | static inline int | ||
1614 | dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page) | ||
1615 | { | ||
1616 | int len, count; | ||
1617 | char *datap; | ||
1618 | |||
1619 | len = 0; | ||
1620 | while (from <= to) { | ||
1621 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
1622 | " CCW %p: %08X %08X DAT:", | ||
1623 | from, ((int *) from)[0], ((int *) from)[1]); | ||
1624 | |||
1625 | /* get pointer to data (consider IDALs) */ | ||
1626 | if (from->flags & CCW_FLAG_IDA) | ||
1627 | datap = (char *) *((addr_t *) (addr_t) from->cda); | ||
1628 | else | ||
1629 | datap = (char *) ((addr_t) from->cda); | ||
1630 | |||
1631 | /* dump data (max 32 bytes) */ | ||
1632 | for (count = 0; count < from->count && count < 32; count++) { | ||
1633 | if (count % 8 == 0) len += sprintf(page + len, " "); | ||
1634 | if (count % 4 == 0) len += sprintf(page + len, " "); | ||
1635 | len += sprintf(page + len, "%02x", datap[count]); | ||
1636 | } | ||
1637 | len += sprintf(page + len, "\n"); | ||
1638 | from++; | ||
1639 | } | ||
1640 | return len; | ||
1641 | } | ||
1642 | |||
1643 | /* | ||
1525 | * Print sense data and related channel program. | 1644 | * Print sense data and related channel program. |
1526 | * Parts are printed because printk buffer is only 1024 bytes. | 1645 | * Parts are printed because printk buffer is only 1024 bytes. |
1527 | */ | 1646 | */ |
@@ -1530,8 +1649,8 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, | |||
1530 | struct irb *irb) | 1649 | struct irb *irb) |
1531 | { | 1650 | { |
1532 | char *page; | 1651 | char *page; |
1533 | struct ccw1 *act, *end, *last; | 1652 | struct ccw1 *first, *last, *fail, *from, *to; |
1534 | int len, sl, sct, count; | 1653 | int len, sl, sct; |
1535 | 1654 | ||
1536 | page = (char *) get_zeroed_page(GFP_ATOMIC); | 1655 | page = (char *) get_zeroed_page(GFP_ATOMIC); |
1537 | if (page == NULL) { | 1656 | if (page == NULL) { |
@@ -1539,7 +1658,8 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, | |||
1539 | "No memory to dump sense data"); | 1658 | "No memory to dump sense data"); |
1540 | return; | 1659 | return; |
1541 | } | 1660 | } |
1542 | len = sprintf(page, KERN_ERR PRINTK_HEADER | 1661 | /* dump the sense data */ |
1662 | len = sprintf(page, KERN_ERR PRINTK_HEADER | ||
1543 | " I/O status report for device %s:\n", | 1663 | " I/O status report for device %s:\n", |
1544 | device->cdev->dev.bus_id); | 1664 | device->cdev->dev.bus_id); |
1545 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | 1665 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER |
@@ -1564,87 +1684,55 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, | |||
1564 | 1684 | ||
1565 | if (irb->ecw[27] & DASD_SENSE_BIT_0) { | 1685 | if (irb->ecw[27] & DASD_SENSE_BIT_0) { |
1566 | /* 24 Byte Sense Data */ | 1686 | /* 24 Byte Sense Data */ |
1567 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | 1687 | sprintf(page + len, KERN_ERR PRINTK_HEADER |
1568 | " 24 Byte: %x MSG %x, " | 1688 | " 24 Byte: %x MSG %x, " |
1569 | "%s MSGb to SYSOP\n", | 1689 | "%s MSGb to SYSOP\n", |
1570 | irb->ecw[7] >> 4, irb->ecw[7] & 0x0f, | 1690 | irb->ecw[7] >> 4, irb->ecw[7] & 0x0f, |
1571 | irb->ecw[1] & 0x10 ? "" : "no"); | 1691 | irb->ecw[1] & 0x10 ? "" : "no"); |
1572 | } else { | 1692 | } else { |
1573 | /* 32 Byte Sense Data */ | 1693 | /* 32 Byte Sense Data */ |
1574 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | 1694 | sprintf(page + len, KERN_ERR PRINTK_HEADER |
1575 | " 32 Byte: Format: %x " | 1695 | " 32 Byte: Format: %x " |
1576 | "Exception class %x\n", | 1696 | "Exception class %x\n", |
1577 | irb->ecw[6] & 0x0f, irb->ecw[22] >> 4); | 1697 | irb->ecw[6] & 0x0f, irb->ecw[22] >> 4); |
1578 | } | 1698 | } |
1579 | } else { | 1699 | } else { |
1580 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | 1700 | sprintf(page + len, KERN_ERR PRINTK_HEADER |
1581 | " SORRY - NO VALID SENSE AVAILABLE\n"); | 1701 | " SORRY - NO VALID SENSE AVAILABLE\n"); |
1582 | } | 1702 | } |
1583 | MESSAGE_LOG(KERN_ERR, "%s", | 1703 | printk("%s", page); |
1584 | page + sizeof(KERN_ERR PRINTK_HEADER)); | 1704 | |
1585 | 1705 | /* dump the Channel Program (max 140 Bytes per line) */ | |
1586 | /* dump the Channel Program */ | 1706 | /* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */ |
1587 | /* print first CCWs (maximum 8) */ | 1707 | first = req->cpaddr; |
1588 | act = req->cpaddr; | 1708 | for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); |
1589 | for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); | 1709 | to = min(first + 6, last); |
1590 | end = min(act + 8, last); | 1710 | len = sprintf(page, KERN_ERR PRINTK_HEADER |
1591 | len = sprintf(page, KERN_ERR PRINTK_HEADER | ||
1592 | " Related CP in req: %p\n", req); | 1711 | " Related CP in req: %p\n", req); |
1593 | while (act <= end) { | 1712 | dasd_eckd_dump_ccw_range(first, to, page + len); |
1594 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | 1713 | printk("%s", page); |
1595 | " CCW %p: %08X %08X DAT:", | ||
1596 | act, ((int *) act)[0], ((int *) act)[1]); | ||
1597 | for (count = 0; count < 32 && count < act->count; | ||
1598 | count += sizeof(int)) | ||
1599 | len += sprintf(page + len, " %08X", | ||
1600 | ((int *) (addr_t) act->cda) | ||
1601 | [(count>>2)]); | ||
1602 | len += sprintf(page + len, "\n"); | ||
1603 | act++; | ||
1604 | } | ||
1605 | MESSAGE_LOG(KERN_ERR, "%s", | ||
1606 | page + sizeof(KERN_ERR PRINTK_HEADER)); | ||
1607 | 1714 | ||
1608 | /* print failing CCW area */ | 1715 | /* print failing CCW area (maximum 4) */ |
1716 | /* scsw->cda is either valid or zero */ | ||
1609 | len = 0; | 1717 | len = 0; |
1610 | if (act < ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2) { | 1718 | from = ++to; |
1611 | act = ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2; | 1719 | fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */ |
1612 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); | 1720 | if (from < fail - 2) { |
1613 | } | 1721 | from = fail - 2; /* there is a gap - print header */ |
1614 | end = min((struct ccw1 *)(addr_t) irb->scsw.cpa + 2, last); | 1722 | len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n"); |
1615 | while (act <= end) { | ||
1616 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
1617 | " CCW %p: %08X %08X DAT:", | ||
1618 | act, ((int *) act)[0], ((int *) act)[1]); | ||
1619 | for (count = 0; count < 32 && count < act->count; | ||
1620 | count += sizeof(int)) | ||
1621 | len += sprintf(page + len, " %08X", | ||
1622 | ((int *) (addr_t) act->cda) | ||
1623 | [(count>>2)]); | ||
1624 | len += sprintf(page + len, "\n"); | ||
1625 | act++; | ||
1626 | } | 1723 | } |
1724 | to = min(fail + 1, last); | ||
1725 | len += dasd_eckd_dump_ccw_range(from, to, page + len); | ||
1627 | 1726 | ||
1628 | /* print last CCWs */ | 1727 | /* print last CCWs (maximum 2) */ |
1629 | if (act < last - 2) { | 1728 | from = max(from, ++to); |
1630 | act = last - 2; | 1729 | if (from < last - 1) { |
1730 | from = last - 1; /* there is a gap - print header */ | ||
1631 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); | 1731 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); |
1632 | } | 1732 | } |
1633 | while (act <= last) { | 1733 | len += dasd_eckd_dump_ccw_range(from, last, page + len); |
1634 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
1635 | " CCW %p: %08X %08X DAT:", | ||
1636 | act, ((int *) act)[0], ((int *) act)[1]); | ||
1637 | for (count = 0; count < 32 && count < act->count; | ||
1638 | count += sizeof(int)) | ||
1639 | len += sprintf(page + len, " %08X", | ||
1640 | ((int *) (addr_t) act->cda) | ||
1641 | [(count>>2)]); | ||
1642 | len += sprintf(page + len, "\n"); | ||
1643 | act++; | ||
1644 | } | ||
1645 | if (len > 0) | 1734 | if (len > 0) |
1646 | MESSAGE_LOG(KERN_ERR, "%s", | 1735 | printk("%s", page); |
1647 | page + sizeof(KERN_ERR PRINTK_HEADER)); | ||
1648 | free_page((unsigned long) page); | 1736 | free_page((unsigned long) page); |
1649 | } | 1737 | } |
1650 | 1738 | ||
@@ -1685,14 +1773,8 @@ static struct dasd_discipline dasd_eckd_discipline = { | |||
1685 | static int __init | 1773 | static int __init |
1686 | dasd_eckd_init(void) | 1774 | dasd_eckd_init(void) |
1687 | { | 1775 | { |
1688 | int ret; | ||
1689 | |||
1690 | ASCEBC(dasd_eckd_discipline.ebcname, 4); | 1776 | ASCEBC(dasd_eckd_discipline.ebcname, 4); |
1691 | 1777 | return ccw_driver_register(&dasd_eckd_driver); | |
1692 | ret = ccw_driver_register(&dasd_eckd_driver); | ||
1693 | if (!ret) | ||
1694 | dasd_generic_auto_online(&dasd_eckd_driver); | ||
1695 | return ret; | ||
1696 | } | 1778 | } |
1697 | 1779 | ||
1698 | static void __exit | 1780 | static void __exit |
@@ -1703,22 +1785,3 @@ dasd_eckd_cleanup(void) | |||
1703 | 1785 | ||
1704 | module_init(dasd_eckd_init); | 1786 | module_init(dasd_eckd_init); |
1705 | module_exit(dasd_eckd_cleanup); | 1787 | module_exit(dasd_eckd_cleanup); |
1706 | |||
1707 | /* | ||
1708 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
1709 | * Emacs will notice this stuff at the end of the file and automatically | ||
1710 | * adjust the settings for this buffer only. This must remain at the end | ||
1711 | * of the file. | ||
1712 | * --------------------------------------------------------------------------- | ||
1713 | * Local variables: | ||
1714 | * c-indent-level: 4 | ||
1715 | * c-brace-imaginary-offset: 0 | ||
1716 | * c-brace-offset: -4 | ||
1717 | * c-argdecl-indent: 4 | ||
1718 | * c-label-offset: -4 | ||
1719 | * c-continued-statement-offset: 4 | ||
1720 | * c-continued-brace-offset: 0 | ||
1721 | * indent-tabs-mode: 1 | ||
1722 | * tab-width: 8 | ||
1723 | * End: | ||
1724 | */ | ||