Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[S390] dasd: Add support for raw ECKD access.

Normal I/O operations through the DASD device driver give only access
to the data fields of an ECKD device even for track based I/O.
This patch extends the DASD device driver to give access to whole
ECKD tracks including count, key and data fields.

Signed-off-by: Stefan Haberland <stefan.haberland@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

authored by

Stefan Haberland and committed by
Martin Schwidefsky
e4dbb0f2 6f272b9c

+268 -8
+2
arch/s390/include/asm/dasd.h
··· 73 73 * 0x02: use diag discipline (diag) 74 74 * 0x04: set the device initially online (internal use only) 75 75 * 0x08: enable ERP related logging 76 + * 0x20: give access to raw eckd data 76 77 */ 77 78 #define DASD_FEATURE_DEFAULT 0x00 78 79 #define DASD_FEATURE_READONLY 0x01 ··· 82 81 #define DASD_FEATURE_ERPLOG 0x08 83 82 #define DASD_FEATURE_FAILFAST 0x10 84 83 #define DASD_FEATURE_FAILONSLCK 0x20 84 + #define DASD_FEATURE_USERAW 0x40 85 85 86 86 #define DASD_PARTN_BITS 2 87 87
+20 -3
drivers/s390/block/dasd.c
··· 369 369 device->state = DASD_STATE_ONLINE; 370 370 if (device->block) { 371 371 dasd_schedule_block_bh(device->block); 372 + if ((device->features & DASD_FEATURE_USERAW)) { 373 + disk = device->block->gdp; 374 + kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE); 375 + return 0; 376 + } 372 377 disk = device->block->bdev->bd_disk; 373 378 disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0); 374 379 while ((part = disk_part_iter_next(&piter))) ··· 399 394 return rc; 400 395 } 401 396 device->state = DASD_STATE_READY; 402 - if (device->block) { 397 + if (device->block && !(device->features & DASD_FEATURE_USERAW)) { 403 398 disk = device->block->bdev->bd_disk; 404 399 disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0); 405 400 while ((part = disk_part_iter_next(&piter))) ··· 2263 2258 { 2264 2259 int max; 2265 2260 2266 - blk_queue_logical_block_size(block->request_queue, block->bp_block); 2267 - max = block->base->discipline->max_blocks << block->s2b_shift; 2261 + if (block->base->features & DASD_FEATURE_USERAW) { 2262 + /* 2263 + * the max_blocks value for raw_track access is 256 2264 + * it is higher than the native ECKD value because we 2265 + * only need one ccw per track 2266 + * so the max_hw_sectors are 2267 + * 2048 x 512B = 1024kB = 16 tracks 2268 + */ 2269 + max = 2048; 2270 + } else { 2271 + max = block->base->discipline->max_blocks << block->s2b_shift; 2272 + } 2273 + blk_queue_logical_block_size(block->request_queue, 2274 + block->bp_block); 2268 2275 blk_queue_max_hw_sectors(block->request_queue, max); 2269 2276 blk_queue_max_segments(block->request_queue, -1L); 2270 2277 /* with page sized segments we can translate each segement into
+54 -1
drivers/s390/block/dasd_devmap.c
··· 208 208 features |= DASD_FEATURE_READONLY; 209 209 else if (len == 4 && !strncmp(str, "diag", 4)) 210 210 features |= DASD_FEATURE_USEDIAG; 211 + else if (len == 3 && !strncmp(str, "raw", 3)) 212 + features |= DASD_FEATURE_USERAW; 211 213 else if (len == 6 && !strncmp(str, "erplog", 6)) 212 214 features |= DASD_FEATURE_ERPLOG; 213 215 else if (len == 8 && !strncmp(str, "failfast", 8)) ··· 859 857 spin_lock(&dasd_devmap_lock); 860 858 /* Changing diag discipline flag is only allowed in offline state. */ 861 859 rc = count; 862 - if (!devmap->device) { 860 + if (!devmap->device && !(devmap->features & DASD_FEATURE_USERAW)) { 863 861 if (val) 864 862 devmap->features |= DASD_FEATURE_USEDIAG; 865 863 else ··· 871 869 } 872 870 873 871 static DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store); 872 + 873 + /* 874 + * use_raw controls whether the driver should give access to raw eckd data or 875 + * operate in standard mode 876 + */ 877 + static ssize_t 878 + dasd_use_raw_show(struct device *dev, struct device_attribute *attr, char *buf) 879 + { 880 + struct dasd_devmap *devmap; 881 + int use_raw; 882 + 883 + devmap = dasd_find_busid(dev_name(dev)); 884 + if (!IS_ERR(devmap)) 885 + use_raw = (devmap->features & DASD_FEATURE_USERAW) != 0; 886 + else 887 + use_raw = (DASD_FEATURE_DEFAULT & DASD_FEATURE_USERAW) != 0; 888 + return sprintf(buf, use_raw ? "1\n" : "0\n"); 889 + } 890 + 891 + static ssize_t 892 + dasd_use_raw_store(struct device *dev, struct device_attribute *attr, 893 + const char *buf, size_t count) 894 + { 895 + struct dasd_devmap *devmap; 896 + ssize_t rc; 897 + unsigned long val; 898 + 899 + devmap = dasd_devmap_from_cdev(to_ccwdev(dev)); 900 + if (IS_ERR(devmap)) 901 + return PTR_ERR(devmap); 902 + 903 + if ((strict_strtoul(buf, 10, &val) != 0) || val > 1) 904 + return -EINVAL; 905 + 906 + spin_lock(&dasd_devmap_lock); 907 + /* Changing diag discipline flag is only allowed in offline state. */ 908 + rc = count; 909 + if (!devmap->device && !(devmap->features & DASD_FEATURE_USEDIAG)) { 910 + if (val) 911 + devmap->features |= DASD_FEATURE_USERAW; 912 + else 913 + devmap->features &= ~DASD_FEATURE_USERAW; 914 + } else 915 + rc = -EPERM; 916 + spin_unlock(&dasd_devmap_lock); 917 + return rc; 918 + } 919 + 920 + static DEVICE_ATTR(raw_track_access, 0644, dasd_use_raw_show, 921 + dasd_use_raw_store); 874 922 875 923 static ssize_t 876 924 dasd_discipline_show(struct device *dev, struct device_attribute *attr, ··· 1284 1232 &dev_attr_vendor.attr, 1285 1233 &dev_attr_uid.attr, 1286 1234 &dev_attr_use_diag.attr, 1235 + &dev_attr_raw_track_access.attr, 1287 1236 &dev_attr_eer_enabled.attr, 1288 1237 &dev_attr_erplog.attr, 1289 1238 &dev_attr_failfast.attr,
+190 -4
drivers/s390/block/dasd_eckd.c
··· 54 54 #define ECKD_F7(i) (i->factor7) 55 55 #define ECKD_F8(i) (i->factor8) 56 56 57 + /* 58 + * raw track access always map to 64k in memory 59 + * so it maps to 16 blocks of 4k per track 60 + */ 61 + #define DASD_RAW_BLOCK_PER_TRACK 16 62 + #define DASD_RAW_BLOCKSIZE 4096 63 + /* 64k are 128 x 512 byte sectors */ 64 + #define DASD_RAW_SECTORS_PER_TRACK 128 65 + 57 66 MODULE_LICENSE("GPL"); 58 67 59 68 static struct dasd_discipline dasd_eckd_discipline; ··· 394 385 data->length = reclen; 395 386 data->operation.operation = 0x03; 396 387 break; 388 + case DASD_ECKD_CCW_WRITE_FULL_TRACK: 389 + data->operation.orientation = 0x0; 390 + data->operation.operation = 0x3F; 391 + data->extended_operation = 0x11; 392 + data->length = 0; 393 + data->extended_parameter_length = 0x02; 394 + if (data->count > 8) { 395 + data->extended_parameter[0] = 0xFF; 396 + data->extended_parameter[1] = 0xFF; 397 + data->extended_parameter[1] <<= (16 - count); 398 + } else { 399 + data->extended_parameter[0] = 0xFF; 400 + data->extended_parameter[0] <<= (8 - count); 401 + data->extended_parameter[1] = 0x00; 402 + } 403 + data->sector = 0xFF; 404 + break; 397 405 case DASD_ECKD_CCW_WRITE_TRACK_DATA: 398 406 data->auxiliary.length_valid = 0x1; 399 407 data->length = reclen; /* not tlf, as one might think */ ··· 433 407 break; 434 408 case DASD_ECKD_CCW_READ_COUNT: 435 409 data->operation.operation = 0x06; 410 + break; 411 + case DASD_ECKD_CCW_READ_TRACK: 412 + data->operation.orientation = 0x1; 413 + data->operation.operation = 0x0C; 414 + data->extended_parameter_length = 0; 415 + data->sector = 0xFF; 436 416 break; 437 417 case DASD_ECKD_CCW_READ_TRACK_DATA: 438 418 data->auxiliary.length_valid = 0x1; ··· 483 451 484 452 ccw->cmd_code = DASD_ECKD_CCW_PFX; 485 453 ccw->flags = 0; 486 - ccw->count = sizeof(*pfxdata); 487 - ccw->cda = (__u32) __pa(pfxdata); 454 + if (cmd == DASD_ECKD_CCW_WRITE_FULL_TRACK) { 455 + ccw->count = sizeof(*pfxdata) + 2; 456 + ccw->cda = (__u32) __pa(pfxdata); 457 + memset(pfxdata, 0, sizeof(*pfxdata) + 2); 458 + } else { 459 + ccw->count = sizeof(*pfxdata); 460 + ccw->cda = (__u32) __pa(pfxdata); 461 + memset(pfxdata, 0, sizeof(*pfxdata)); 462 + } 488 463 489 - memset(pfxdata, 0, sizeof(*pfxdata)); 490 464 /* prefix data */ 491 465 if (format > 1) { 492 466 DBF_DEV_EVENT(DBF_ERR, basedev, ··· 526 488 dedata->mask.perm = 0x1; 527 489 dedata->attributes.operation = basepriv->attrib.operation; 528 490 break; 491 + case DASD_ECKD_CCW_READ_TRACK: 529 492 case DASD_ECKD_CCW_READ_TRACK_DATA: 530 493 dedata->mask.perm = 0x1; 531 494 dedata->attributes.operation = basepriv->attrib.operation; ··· 552 513 dedata->mask.auth = 0x1; 553 514 dedata->attributes.operation = DASD_BYPASS_CACHE; 554 515 rc = check_XRC_on_prefix(pfxdata, basedev); 516 + break; 517 + case DASD_ECKD_CCW_WRITE_FULL_TRACK: 518 + dedata->mask.perm = 0x03; 519 + dedata->attributes.operation = basepriv->attrib.operation; 520 + dedata->blk_size = 0; 555 521 break; 556 522 case DASD_ECKD_CCW_WRITE_TRACK_DATA: 557 523 dedata->mask.perm = 0x02; ··· 1651 1607 dasd_sfree_request(init_cqr, device); 1652 1608 } 1653 1609 1610 + if (device->features & DASD_FEATURE_USERAW) { 1611 + block->bp_block = DASD_RAW_BLOCKSIZE; 1612 + blk_per_trk = DASD_RAW_BLOCK_PER_TRACK; 1613 + block->s2b_shift = 3; 1614 + goto raw; 1615 + } 1616 + 1654 1617 if (status == INIT_CQR_UNFORMATTED) { 1655 1618 dev_warn(&device->cdev->dev, "The DASD is not formatted\n"); 1656 1619 return -EMEDIUMTYPE; ··· 1695 1644 dev_warn(&device->cdev->dev, 1696 1645 "Track 0 has no records following the VTOC\n"); 1697 1646 } 1647 + 1698 1648 if (count_area != NULL && count_area->kl == 0) { 1699 1649 /* we found notthing violating our disk layout */ 1700 1650 if (dasd_check_blocksize(count_area->dl) == 0) ··· 1711 1659 block->s2b_shift++; 1712 1660 1713 1661 blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block); 1662 + 1663 + raw: 1714 1664 block->blocks = (private->real_cyl * 1715 1665 private->rdc_data.trk_per_cyl * 1716 1666 blk_per_trk); ··· 2795 2741 return cqr; 2796 2742 } 2797 2743 2744 + static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, 2745 + struct dasd_block *block, 2746 + struct request *req) 2747 + { 2748 + struct dasd_eckd_private *private; 2749 + unsigned long *idaws; 2750 + struct dasd_device *basedev; 2751 + struct dasd_ccw_req *cqr; 2752 + struct ccw1 *ccw; 2753 + struct req_iterator iter; 2754 + struct bio_vec *bv; 2755 + char *dst; 2756 + unsigned char cmd; 2757 + unsigned int trkcount; 2758 + unsigned int seg_len, len_to_track_end; 2759 + unsigned int first_offs; 2760 + unsigned int cidaw, cplength, datasize; 2761 + sector_t first_trk, last_trk; 2762 + unsigned int pfx_datasize; 2763 + 2764 + /* 2765 + * raw track access needs to be mutiple of 64k and on 64k boundary 2766 + */ 2767 + if ((blk_rq_pos(req) % DASD_RAW_SECTORS_PER_TRACK) != 0) { 2768 + cqr = ERR_PTR(-EINVAL); 2769 + goto out; 2770 + } 2771 + if (((blk_rq_pos(req) + blk_rq_sectors(req)) % 2772 + DASD_RAW_SECTORS_PER_TRACK) != 0) { 2773 + cqr = ERR_PTR(-EINVAL); 2774 + goto out; 2775 + } 2776 + 2777 + first_trk = blk_rq_pos(req) / DASD_RAW_SECTORS_PER_TRACK; 2778 + last_trk = (blk_rq_pos(req) + blk_rq_sectors(req) - 1) / 2779 + DASD_RAW_SECTORS_PER_TRACK; 2780 + trkcount = last_trk - first_trk + 1; 2781 + first_offs = 0; 2782 + basedev = block->base; 2783 + private = (struct dasd_eckd_private *) basedev->private; 2784 + 2785 + if (rq_data_dir(req) == READ) 2786 + cmd = DASD_ECKD_CCW_READ_TRACK; 2787 + else if (rq_data_dir(req) == WRITE) 2788 + cmd = DASD_ECKD_CCW_WRITE_FULL_TRACK; 2789 + else { 2790 + cqr = ERR_PTR(-EINVAL); 2791 + goto out; 2792 + } 2793 + 2794 + /* 2795 + * Raw track based I/O needs IDAWs for each page, 2796 + * and not just for 64 bit addresses. 2797 + */ 2798 + cidaw = trkcount * DASD_RAW_BLOCK_PER_TRACK; 2799 + 2800 + /* 1x prefix + one read/write ccw per track */ 2801 + cplength = 1 + trkcount; 2802 + 2803 + /* 2804 + * struct PFX_eckd_data has up to 2 byte as extended parameter 2805 + * this is needed for write full track and has to be mentioned 2806 + * seperately 2807 + * add 8 instead of 2 to keep 8 byte boundary 2808 + */ 2809 + pfx_datasize = sizeof(struct PFX_eckd_data) + 8; 2810 + 2811 + datasize = pfx_datasize + cidaw * sizeof(unsigned long long); 2812 + 2813 + /* Allocate the ccw request. */ 2814 + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, 2815 + datasize, startdev); 2816 + if (IS_ERR(cqr)) 2817 + goto out; 2818 + ccw = cqr->cpaddr; 2819 + 2820 + if (prefix_LRE(ccw++, cqr->data, first_trk, last_trk, cmd, 2821 + basedev, startdev, 1 /* format */, first_offs + 1, 2822 + trkcount, 0, 0) == -EAGAIN) { 2823 + /* Clock not in sync and XRC is enabled. 2824 + * Try again later. 2825 + */ 2826 + dasd_sfree_request(cqr, startdev); 2827 + cqr = ERR_PTR(-EAGAIN); 2828 + goto out; 2829 + } 2830 + 2831 + idaws = (unsigned long *)(cqr->data + pfx_datasize); 2832 + 2833 + len_to_track_end = 0; 2834 + 2835 + rq_for_each_segment(bv, req, iter) { 2836 + dst = page_address(bv->bv_page) + bv->bv_offset; 2837 + seg_len = bv->bv_len; 2838 + if (!len_to_track_end) { 2839 + ccw[-1].flags |= CCW_FLAG_CC; 2840 + ccw->cmd_code = cmd; 2841 + /* maximum 3390 track size */ 2842 + ccw->count = 57326; 2843 + /* 64k map to one track */ 2844 + len_to_track_end = 65536; 2845 + ccw->cda = (__u32)(addr_t)idaws; 2846 + ccw->flags |= CCW_FLAG_IDA; 2847 + ccw->flags |= CCW_FLAG_SLI; 2848 + ccw++; 2849 + } 2850 + len_to_track_end -= seg_len; 2851 + idaws = idal_create_words(idaws, dst, seg_len); 2852 + } 2853 + 2854 + if (blk_noretry_request(req) || 2855 + block->base->features & DASD_FEATURE_FAILFAST) 2856 + set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 2857 + cqr->startdev = startdev; 2858 + cqr->memdev = startdev; 2859 + cqr->block = block; 2860 + cqr->expires = startdev->default_expires * HZ; 2861 + cqr->lpm = startdev->path_data.ppm; 2862 + cqr->retries = 256; 2863 + cqr->buildclk = get_clock(); 2864 + cqr->status = DASD_CQR_FILLED; 2865 + 2866 + if (IS_ERR(cqr) && PTR_ERR(cqr) != -EAGAIN) 2867 + cqr = NULL; 2868 + out: 2869 + return cqr; 2870 + } 2871 + 2872 + 2798 2873 static int 2799 2874 dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req) 2800 2875 { ··· 3028 2845 3029 2846 spin_lock_irqsave(get_ccwdev_lock(startdev->cdev), flags); 3030 2847 private->count++; 3031 - cqr = dasd_eckd_build_cp(startdev, block, req); 2848 + if ((base->features & DASD_FEATURE_USERAW)) 2849 + cqr = dasd_raw_build_cp(startdev, block, req); 2850 + else 2851 + cqr = dasd_eckd_build_cp(startdev, block, req); 3032 2852 if (IS_ERR(cqr)) 3033 2853 private->count--; 3034 2854 spin_unlock_irqrestore(get_ccwdev_lock(startdev->cdev), flags);
+2
drivers/s390/block/dasd_eckd.h
··· 37 37 #define DASD_ECKD_CCW_WRITE_KD_MT 0x8d 38 38 #define DASD_ECKD_CCW_READ_KD_MT 0x8e 39 39 #define DASD_ECKD_CCW_RELEASE 0x94 40 + #define DASD_ECKD_CCW_WRITE_FULL_TRACK 0x95 40 41 #define DASD_ECKD_CCW_READ_CKD_MT 0x9e 41 42 #define DASD_ECKD_CCW_WRITE_CKD_MT 0x9d 42 43 #define DASD_ECKD_CCW_WRITE_TRACK_DATA 0xA5 43 44 #define DASD_ECKD_CCW_READ_TRACK_DATA 0xA6 44 45 #define DASD_ECKD_CCW_RESERVE 0xB4 46 + #define DASD_ECKD_CCW_READ_TRACK 0xDE 45 47 #define DASD_ECKD_CCW_PFX 0xE7 46 48 #define DASD_ECKD_CCW_PFX_READ 0xEA 47 49 #define DASD_ECKD_CCW_RSCK 0xF9