18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> 48c2ecf20Sopenharmony_ci * Horst Hummel <Horst.Hummel@de.ibm.com> 58c2ecf20Sopenharmony_ci * Carsten Otte <Cotte@de.ibm.com> 68c2ecf20Sopenharmony_ci * Martin Schwidefsky <schwidefsky@de.ibm.com> 78c2ecf20Sopenharmony_ci * Bugreports.to..: <Linux390@de.ibm.com> 88c2ecf20Sopenharmony_ci * Copyright IBM Corp. 1999, 2009 98c2ecf20Sopenharmony_ci * EMC Symmetrix ioctl Copyright EMC Corporation, 2008 108c2ecf20Sopenharmony_ci * Author.........: Nigel Hislop <hislop_nigel@emc.com> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "dasd-eckd" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/stddef.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/hdreg.h> /* HDIO_GETGEO */ 198c2ecf20Sopenharmony_ci#include <linux/bio.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/compat.h> 228c2ecf20Sopenharmony_ci#include <linux/init.h> 238c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <asm/css_chars.h> 268c2ecf20Sopenharmony_ci#include <asm/debug.h> 278c2ecf20Sopenharmony_ci#include <asm/idals.h> 288c2ecf20Sopenharmony_ci#include <asm/ebcdic.h> 298c2ecf20Sopenharmony_ci#include <asm/io.h> 308c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 318c2ecf20Sopenharmony_ci#include <asm/cio.h> 328c2ecf20Sopenharmony_ci#include <asm/ccwdev.h> 338c2ecf20Sopenharmony_ci#include <asm/itcw.h> 348c2ecf20Sopenharmony_ci#include <asm/schid.h> 358c2ecf20Sopenharmony_ci#include <asm/chpid.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include "dasd_int.h" 388c2ecf20Sopenharmony_ci#include "dasd_eckd.h" 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#ifdef PRINTK_HEADER 418c2ecf20Sopenharmony_ci#undef PRINTK_HEADER 428c2ecf20Sopenharmony_ci#endif /* PRINTK_HEADER */ 438c2ecf20Sopenharmony_ci#define PRINTK_HEADER "dasd(eckd):" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * raw track access always map to 64k in memory 478c2ecf20Sopenharmony_ci * so it maps to 16 blocks of 4k per track 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci#define DASD_RAW_BLOCK_PER_TRACK 16 508c2ecf20Sopenharmony_ci#define DASD_RAW_BLOCKSIZE 4096 518c2ecf20Sopenharmony_ci/* 64k are 128 x 512 byte sectors */ 528c2ecf20Sopenharmony_ci#define DASD_RAW_SECTORS_PER_TRACK 128 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic struct dasd_discipline dasd_eckd_discipline; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* The ccw bus type uses this table to find devices that it sends to 598c2ecf20Sopenharmony_ci * dasd_eckd_probe */ 608c2ecf20Sopenharmony_cistatic struct ccw_device_id dasd_eckd_ids[] = { 618c2ecf20Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3390, 0), .driver_info = 0x1}, 628c2ecf20Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3390, 0), .driver_info = 0x2}, 638c2ecf20Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3380, 0), .driver_info = 0x3}, 648c2ecf20Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3380, 0), .driver_info = 0x4}, 658c2ecf20Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3380, 0), .driver_info = 0x5}, 668c2ecf20Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x9343, 0, 0x9345, 0), .driver_info = 0x6}, 678c2ecf20Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3390, 0), .driver_info = 0x7}, 688c2ecf20Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3380, 0), .driver_info = 0x8}, 698c2ecf20Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3390, 0), .driver_info = 0x9}, 708c2ecf20Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3380, 0), .driver_info = 0xa}, 718c2ecf20Sopenharmony_ci { /* end of list */ }, 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(ccw, dasd_eckd_ids); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic struct ccw_driver dasd_eckd_driver; /* see below */ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void *rawpadpage; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define INIT_CQR_OK 0 818c2ecf20Sopenharmony_ci#define INIT_CQR_UNFORMATTED 1 828c2ecf20Sopenharmony_ci#define INIT_CQR_ERROR 2 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* emergency request for reserve/release */ 858c2ecf20Sopenharmony_cistatic struct { 868c2ecf20Sopenharmony_ci struct dasd_ccw_req cqr; 878c2ecf20Sopenharmony_ci struct ccw1 ccw; 888c2ecf20Sopenharmony_ci char data[32]; 898c2ecf20Sopenharmony_ci} *dasd_reserve_req; 908c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(dasd_reserve_mutex); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic struct { 938c2ecf20Sopenharmony_ci struct dasd_ccw_req cqr; 948c2ecf20Sopenharmony_ci struct ccw1 ccw[2]; 958c2ecf20Sopenharmony_ci char data[40]; 968c2ecf20Sopenharmony_ci} *dasd_vol_info_req; 978c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(dasd_vol_info_mutex); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistruct ext_pool_exhaust_work_data { 1008c2ecf20Sopenharmony_ci struct work_struct worker; 1018c2ecf20Sopenharmony_ci struct dasd_device *device; 1028c2ecf20Sopenharmony_ci struct dasd_device *base; 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* definitions for the path verification worker */ 1068c2ecf20Sopenharmony_cistruct pe_handler_work_data { 1078c2ecf20Sopenharmony_ci struct work_struct worker; 1088c2ecf20Sopenharmony_ci struct dasd_device *device; 1098c2ecf20Sopenharmony_ci struct dasd_ccw_req cqr; 1108c2ecf20Sopenharmony_ci struct ccw1 ccw; 1118c2ecf20Sopenharmony_ci __u8 rcd_buffer[DASD_ECKD_RCD_DATA_SIZE]; 1128c2ecf20Sopenharmony_ci int isglobal; 1138c2ecf20Sopenharmony_ci __u8 tbvpm; 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_cistatic struct pe_handler_work_data *pe_handler_worker; 1168c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(dasd_pe_handler_mutex); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistruct check_attention_work_data { 1198c2ecf20Sopenharmony_ci struct work_struct worker; 1208c2ecf20Sopenharmony_ci struct dasd_device *device; 1218c2ecf20Sopenharmony_ci __u8 lpum; 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int dasd_eckd_ext_pool_id(struct dasd_device *); 1258c2ecf20Sopenharmony_cistatic int prepare_itcw(struct itcw *, unsigned int, unsigned int, int, 1268c2ecf20Sopenharmony_ci struct dasd_device *, struct dasd_device *, 1278c2ecf20Sopenharmony_ci unsigned int, int, unsigned int, unsigned int, 1288c2ecf20Sopenharmony_ci unsigned int, unsigned int); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* initial attempt at a probe function. this can be simplified once 1318c2ecf20Sopenharmony_ci * the other detection code is gone */ 1328c2ecf20Sopenharmony_cistatic int 1338c2ecf20Sopenharmony_cidasd_eckd_probe (struct ccw_device *cdev) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci int ret; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* set ECKD specific ccw-device options */ 1388c2ecf20Sopenharmony_ci ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE | 1398c2ecf20Sopenharmony_ci CCWDEV_DO_PATHGROUP | CCWDEV_DO_MULTIPATH); 1408c2ecf20Sopenharmony_ci if (ret) { 1418c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", 1428c2ecf20Sopenharmony_ci "dasd_eckd_probe: could not set " 1438c2ecf20Sopenharmony_ci "ccw-device options"); 1448c2ecf20Sopenharmony_ci return ret; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci ret = dasd_generic_probe(cdev, &dasd_eckd_discipline); 1478c2ecf20Sopenharmony_ci return ret; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int 1518c2ecf20Sopenharmony_cidasd_eckd_set_online(struct ccw_device *cdev) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci return dasd_generic_set_online(cdev, &dasd_eckd_discipline); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic const int sizes_trk0[] = { 28, 148, 84 }; 1578c2ecf20Sopenharmony_ci#define LABEL_SIZE 140 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/* head and record addresses of count_area read in analysis ccw */ 1608c2ecf20Sopenharmony_cistatic const int count_area_head[] = { 0, 0, 0, 0, 1 }; 1618c2ecf20Sopenharmony_cistatic const int count_area_rec[] = { 1, 2, 3, 4, 1 }; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic inline unsigned int 1648c2ecf20Sopenharmony_ciceil_quot(unsigned int d1, unsigned int d2) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci return (d1 + (d2 - 1)) / d2; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic unsigned int 1708c2ecf20Sopenharmony_cirecs_per_track(struct dasd_eckd_characteristics * rdc, 1718c2ecf20Sopenharmony_ci unsigned int kl, unsigned int dl) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci int dn, kn; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci switch (rdc->dev_type) { 1768c2ecf20Sopenharmony_ci case 0x3380: 1778c2ecf20Sopenharmony_ci if (kl) 1788c2ecf20Sopenharmony_ci return 1499 / (15 + 7 + ceil_quot(kl + 12, 32) + 1798c2ecf20Sopenharmony_ci ceil_quot(dl + 12, 32)); 1808c2ecf20Sopenharmony_ci else 1818c2ecf20Sopenharmony_ci return 1499 / (15 + ceil_quot(dl + 12, 32)); 1828c2ecf20Sopenharmony_ci case 0x3390: 1838c2ecf20Sopenharmony_ci dn = ceil_quot(dl + 6, 232) + 1; 1848c2ecf20Sopenharmony_ci if (kl) { 1858c2ecf20Sopenharmony_ci kn = ceil_quot(kl + 6, 232) + 1; 1868c2ecf20Sopenharmony_ci return 1729 / (10 + 9 + ceil_quot(kl + 6 * kn, 34) + 1878c2ecf20Sopenharmony_ci 9 + ceil_quot(dl + 6 * dn, 34)); 1888c2ecf20Sopenharmony_ci } else 1898c2ecf20Sopenharmony_ci return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34)); 1908c2ecf20Sopenharmony_ci case 0x9345: 1918c2ecf20Sopenharmony_ci dn = ceil_quot(dl + 6, 232) + 1; 1928c2ecf20Sopenharmony_ci if (kl) { 1938c2ecf20Sopenharmony_ci kn = ceil_quot(kl + 6, 232) + 1; 1948c2ecf20Sopenharmony_ci return 1420 / (18 + 7 + ceil_quot(kl + 6 * kn, 34) + 1958c2ecf20Sopenharmony_ci ceil_quot(dl + 6 * dn, 34)); 1968c2ecf20Sopenharmony_ci } else 1978c2ecf20Sopenharmony_ci return 1420 / (18 + 7 + ceil_quot(dl + 6 * dn, 34)); 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic void set_ch_t(struct ch_t *geo, __u32 cyl, __u8 head) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci geo->cyl = (__u16) cyl; 2058c2ecf20Sopenharmony_ci geo->head = cyl >> 16; 2068c2ecf20Sopenharmony_ci geo->head <<= 4; 2078c2ecf20Sopenharmony_ci geo->head |= head; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci/* 2118c2ecf20Sopenharmony_ci * calculate failing track from sense data depending if 2128c2ecf20Sopenharmony_ci * it is an EAV device or not 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_cistatic int dasd_eckd_track_from_irb(struct irb *irb, struct dasd_device *device, 2158c2ecf20Sopenharmony_ci sector_t *track) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 2188c2ecf20Sopenharmony_ci u8 *sense = NULL; 2198c2ecf20Sopenharmony_ci u32 cyl; 2208c2ecf20Sopenharmony_ci u8 head; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci sense = dasd_get_sense(irb); 2238c2ecf20Sopenharmony_ci if (!sense) { 2248c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 2258c2ecf20Sopenharmony_ci "ESE error no sense data\n"); 2268c2ecf20Sopenharmony_ci return -EINVAL; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci if (!(sense[27] & DASD_SENSE_BIT_2)) { 2298c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 2308c2ecf20Sopenharmony_ci "ESE error no valid track data\n"); 2318c2ecf20Sopenharmony_ci return -EINVAL; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (sense[27] & DASD_SENSE_BIT_3) { 2358c2ecf20Sopenharmony_ci /* enhanced addressing */ 2368c2ecf20Sopenharmony_ci cyl = sense[30] << 20; 2378c2ecf20Sopenharmony_ci cyl |= (sense[31] & 0xF0) << 12; 2388c2ecf20Sopenharmony_ci cyl |= sense[28] << 8; 2398c2ecf20Sopenharmony_ci cyl |= sense[29]; 2408c2ecf20Sopenharmony_ci } else { 2418c2ecf20Sopenharmony_ci cyl = sense[29] << 8; 2428c2ecf20Sopenharmony_ci cyl |= sense[30]; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci head = sense[31] & 0x0F; 2458c2ecf20Sopenharmony_ci *track = cyl * private->rdc_data.trk_per_cyl + head; 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int set_timestamp(struct ccw1 *ccw, struct DE_eckd_data *data, 2508c2ecf20Sopenharmony_ci struct dasd_device *device) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 2538c2ecf20Sopenharmony_ci int rc; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci rc = get_phys_clock(&data->ep_sys_time); 2568c2ecf20Sopenharmony_ci /* 2578c2ecf20Sopenharmony_ci * Ignore return code if XRC is not supported or 2588c2ecf20Sopenharmony_ci * sync clock is switched off 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci if ((rc && !private->rdc_data.facilities.XRC_supported) || 2618c2ecf20Sopenharmony_ci rc == -EOPNOTSUPP || rc == -EACCES) 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* switch on System Time Stamp - needed for XRC Support */ 2658c2ecf20Sopenharmony_ci data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */ 2668c2ecf20Sopenharmony_ci data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */ 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (ccw) { 2698c2ecf20Sopenharmony_ci ccw->count = sizeof(struct DE_eckd_data); 2708c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return rc; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int 2778c2ecf20Sopenharmony_cidefine_extent(struct ccw1 *ccw, struct DE_eckd_data *data, unsigned int trk, 2788c2ecf20Sopenharmony_ci unsigned int totrk, int cmd, struct dasd_device *device, 2798c2ecf20Sopenharmony_ci int blksize) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 2828c2ecf20Sopenharmony_ci u16 heads, beghead, endhead; 2838c2ecf20Sopenharmony_ci u32 begcyl, endcyl; 2848c2ecf20Sopenharmony_ci int rc = 0; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (ccw) { 2878c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT; 2888c2ecf20Sopenharmony_ci ccw->flags = 0; 2898c2ecf20Sopenharmony_ci ccw->count = 16; 2908c2ecf20Sopenharmony_ci ccw->cda = (__u32)__pa(data); 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci memset(data, 0, sizeof(struct DE_eckd_data)); 2948c2ecf20Sopenharmony_ci switch (cmd) { 2958c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_HOME_ADDRESS: 2968c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_RECORD_ZERO: 2978c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ: 2988c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_MT: 2998c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_CKD: 3008c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_CKD_MT: 3018c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_KD: 3028c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_KD_MT: 3038c2ecf20Sopenharmony_ci data->mask.perm = 0x1; 3048c2ecf20Sopenharmony_ci data->attributes.operation = private->attrib.operation; 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_COUNT: 3078c2ecf20Sopenharmony_ci data->mask.perm = 0x1; 3088c2ecf20Sopenharmony_ci data->attributes.operation = DASD_BYPASS_CACHE; 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_TRACK: 3118c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_TRACK_DATA: 3128c2ecf20Sopenharmony_ci data->mask.perm = 0x1; 3138c2ecf20Sopenharmony_ci data->attributes.operation = private->attrib.operation; 3148c2ecf20Sopenharmony_ci data->blk_size = 0; 3158c2ecf20Sopenharmony_ci break; 3168c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE: 3178c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_MT: 3188c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_KD: 3198c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_KD_MT: 3208c2ecf20Sopenharmony_ci data->mask.perm = 0x02; 3218c2ecf20Sopenharmony_ci data->attributes.operation = private->attrib.operation; 3228c2ecf20Sopenharmony_ci rc = set_timestamp(ccw, data, device); 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_CKD: 3258c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_CKD_MT: 3268c2ecf20Sopenharmony_ci data->attributes.operation = DASD_BYPASS_CACHE; 3278c2ecf20Sopenharmony_ci rc = set_timestamp(ccw, data, device); 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_ERASE: 3308c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: 3318c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_RECORD_ZERO: 3328c2ecf20Sopenharmony_ci data->mask.perm = 0x3; 3338c2ecf20Sopenharmony_ci data->mask.auth = 0x1; 3348c2ecf20Sopenharmony_ci data->attributes.operation = DASD_BYPASS_CACHE; 3358c2ecf20Sopenharmony_ci rc = set_timestamp(ccw, data, device); 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_FULL_TRACK: 3388c2ecf20Sopenharmony_ci data->mask.perm = 0x03; 3398c2ecf20Sopenharmony_ci data->attributes.operation = private->attrib.operation; 3408c2ecf20Sopenharmony_ci data->blk_size = 0; 3418c2ecf20Sopenharmony_ci break; 3428c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_TRACK_DATA: 3438c2ecf20Sopenharmony_ci data->mask.perm = 0x02; 3448c2ecf20Sopenharmony_ci data->attributes.operation = private->attrib.operation; 3458c2ecf20Sopenharmony_ci data->blk_size = blksize; 3468c2ecf20Sopenharmony_ci rc = set_timestamp(ccw, data, device); 3478c2ecf20Sopenharmony_ci break; 3488c2ecf20Sopenharmony_ci default: 3498c2ecf20Sopenharmony_ci dev_err(&device->cdev->dev, 3508c2ecf20Sopenharmony_ci "0x%x is not a known command\n", cmd); 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci data->attributes.mode = 0x3; /* ECKD */ 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if ((private->rdc_data.cu_type == 0x2105 || 3578c2ecf20Sopenharmony_ci private->rdc_data.cu_type == 0x2107 || 3588c2ecf20Sopenharmony_ci private->rdc_data.cu_type == 0x1750) 3598c2ecf20Sopenharmony_ci && !(private->uses_cdl && trk < 2)) 3608c2ecf20Sopenharmony_ci data->ga_extended |= 0x40; /* Regular Data Format Mode */ 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci heads = private->rdc_data.trk_per_cyl; 3638c2ecf20Sopenharmony_ci begcyl = trk / heads; 3648c2ecf20Sopenharmony_ci beghead = trk % heads; 3658c2ecf20Sopenharmony_ci endcyl = totrk / heads; 3668c2ecf20Sopenharmony_ci endhead = totrk % heads; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* check for sequential prestage - enhance cylinder range */ 3698c2ecf20Sopenharmony_ci if (data->attributes.operation == DASD_SEQ_PRESTAGE || 3708c2ecf20Sopenharmony_ci data->attributes.operation == DASD_SEQ_ACCESS) { 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (endcyl + private->attrib.nr_cyl < private->real_cyl) 3738c2ecf20Sopenharmony_ci endcyl += private->attrib.nr_cyl; 3748c2ecf20Sopenharmony_ci else 3758c2ecf20Sopenharmony_ci endcyl = (private->real_cyl - 1); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci set_ch_t(&data->beg_ext, begcyl, beghead); 3798c2ecf20Sopenharmony_ci set_ch_t(&data->end_ext, endcyl, endhead); 3808c2ecf20Sopenharmony_ci return rc; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic void locate_record_ext(struct ccw1 *ccw, struct LRE_eckd_data *data, 3858c2ecf20Sopenharmony_ci unsigned int trk, unsigned int rec_on_trk, 3868c2ecf20Sopenharmony_ci int count, int cmd, struct dasd_device *device, 3878c2ecf20Sopenharmony_ci unsigned int reclen, unsigned int tlf) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 3908c2ecf20Sopenharmony_ci int sector; 3918c2ecf20Sopenharmony_ci int dn, d; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (ccw) { 3948c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD_EXT; 3958c2ecf20Sopenharmony_ci ccw->flags = 0; 3968c2ecf20Sopenharmony_ci if (cmd == DASD_ECKD_CCW_WRITE_FULL_TRACK) 3978c2ecf20Sopenharmony_ci ccw->count = 22; 3988c2ecf20Sopenharmony_ci else 3998c2ecf20Sopenharmony_ci ccw->count = 20; 4008c2ecf20Sopenharmony_ci ccw->cda = (__u32)__pa(data); 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci memset(data, 0, sizeof(*data)); 4048c2ecf20Sopenharmony_ci sector = 0; 4058c2ecf20Sopenharmony_ci if (rec_on_trk) { 4068c2ecf20Sopenharmony_ci switch (private->rdc_data.dev_type) { 4078c2ecf20Sopenharmony_ci case 0x3390: 4088c2ecf20Sopenharmony_ci dn = ceil_quot(reclen + 6, 232); 4098c2ecf20Sopenharmony_ci d = 9 + ceil_quot(reclen + 6 * (dn + 1), 34); 4108c2ecf20Sopenharmony_ci sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8; 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci case 0x3380: 4138c2ecf20Sopenharmony_ci d = 7 + ceil_quot(reclen + 12, 32); 4148c2ecf20Sopenharmony_ci sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7; 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci data->sector = sector; 4198c2ecf20Sopenharmony_ci /* note: meaning of count depends on the operation 4208c2ecf20Sopenharmony_ci * for record based I/O it's the number of records, but for 4218c2ecf20Sopenharmony_ci * track based I/O it's the number of tracks 4228c2ecf20Sopenharmony_ci */ 4238c2ecf20Sopenharmony_ci data->count = count; 4248c2ecf20Sopenharmony_ci switch (cmd) { 4258c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: 4268c2ecf20Sopenharmony_ci data->operation.orientation = 0x3; 4278c2ecf20Sopenharmony_ci data->operation.operation = 0x03; 4288c2ecf20Sopenharmony_ci break; 4298c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_HOME_ADDRESS: 4308c2ecf20Sopenharmony_ci data->operation.orientation = 0x3; 4318c2ecf20Sopenharmony_ci data->operation.operation = 0x16; 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_RECORD_ZERO: 4348c2ecf20Sopenharmony_ci data->operation.orientation = 0x1; 4358c2ecf20Sopenharmony_ci data->operation.operation = 0x03; 4368c2ecf20Sopenharmony_ci data->count++; 4378c2ecf20Sopenharmony_ci break; 4388c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_RECORD_ZERO: 4398c2ecf20Sopenharmony_ci data->operation.orientation = 0x3; 4408c2ecf20Sopenharmony_ci data->operation.operation = 0x16; 4418c2ecf20Sopenharmony_ci data->count++; 4428c2ecf20Sopenharmony_ci break; 4438c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE: 4448c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_MT: 4458c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_KD: 4468c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_KD_MT: 4478c2ecf20Sopenharmony_ci data->auxiliary.length_valid = 0x1; 4488c2ecf20Sopenharmony_ci data->length = reclen; 4498c2ecf20Sopenharmony_ci data->operation.operation = 0x01; 4508c2ecf20Sopenharmony_ci break; 4518c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_CKD: 4528c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_CKD_MT: 4538c2ecf20Sopenharmony_ci data->auxiliary.length_valid = 0x1; 4548c2ecf20Sopenharmony_ci data->length = reclen; 4558c2ecf20Sopenharmony_ci data->operation.operation = 0x03; 4568c2ecf20Sopenharmony_ci break; 4578c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_FULL_TRACK: 4588c2ecf20Sopenharmony_ci data->operation.orientation = 0x0; 4598c2ecf20Sopenharmony_ci data->operation.operation = 0x3F; 4608c2ecf20Sopenharmony_ci data->extended_operation = 0x11; 4618c2ecf20Sopenharmony_ci data->length = 0; 4628c2ecf20Sopenharmony_ci data->extended_parameter_length = 0x02; 4638c2ecf20Sopenharmony_ci if (data->count > 8) { 4648c2ecf20Sopenharmony_ci data->extended_parameter[0] = 0xFF; 4658c2ecf20Sopenharmony_ci data->extended_parameter[1] = 0xFF; 4668c2ecf20Sopenharmony_ci data->extended_parameter[1] <<= (16 - count); 4678c2ecf20Sopenharmony_ci } else { 4688c2ecf20Sopenharmony_ci data->extended_parameter[0] = 0xFF; 4698c2ecf20Sopenharmony_ci data->extended_parameter[0] <<= (8 - count); 4708c2ecf20Sopenharmony_ci data->extended_parameter[1] = 0x00; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci data->sector = 0xFF; 4738c2ecf20Sopenharmony_ci break; 4748c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_TRACK_DATA: 4758c2ecf20Sopenharmony_ci data->auxiliary.length_valid = 0x1; 4768c2ecf20Sopenharmony_ci data->length = reclen; /* not tlf, as one might think */ 4778c2ecf20Sopenharmony_ci data->operation.operation = 0x3F; 4788c2ecf20Sopenharmony_ci data->extended_operation = 0x23; 4798c2ecf20Sopenharmony_ci break; 4808c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ: 4818c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_MT: 4828c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_KD: 4838c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_KD_MT: 4848c2ecf20Sopenharmony_ci data->auxiliary.length_valid = 0x1; 4858c2ecf20Sopenharmony_ci data->length = reclen; 4868c2ecf20Sopenharmony_ci data->operation.operation = 0x06; 4878c2ecf20Sopenharmony_ci break; 4888c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_CKD: 4898c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_CKD_MT: 4908c2ecf20Sopenharmony_ci data->auxiliary.length_valid = 0x1; 4918c2ecf20Sopenharmony_ci data->length = reclen; 4928c2ecf20Sopenharmony_ci data->operation.operation = 0x16; 4938c2ecf20Sopenharmony_ci break; 4948c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_COUNT: 4958c2ecf20Sopenharmony_ci data->operation.operation = 0x06; 4968c2ecf20Sopenharmony_ci break; 4978c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_TRACK: 4988c2ecf20Sopenharmony_ci data->operation.orientation = 0x1; 4998c2ecf20Sopenharmony_ci data->operation.operation = 0x0C; 5008c2ecf20Sopenharmony_ci data->extended_parameter_length = 0; 5018c2ecf20Sopenharmony_ci data->sector = 0xFF; 5028c2ecf20Sopenharmony_ci break; 5038c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_TRACK_DATA: 5048c2ecf20Sopenharmony_ci data->auxiliary.length_valid = 0x1; 5058c2ecf20Sopenharmony_ci data->length = tlf; 5068c2ecf20Sopenharmony_ci data->operation.operation = 0x0C; 5078c2ecf20Sopenharmony_ci break; 5088c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_ERASE: 5098c2ecf20Sopenharmony_ci data->length = reclen; 5108c2ecf20Sopenharmony_ci data->auxiliary.length_valid = 0x1; 5118c2ecf20Sopenharmony_ci data->operation.operation = 0x0b; 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci default: 5148c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_ERR, device, 5158c2ecf20Sopenharmony_ci "fill LRE unknown opcode 0x%x", cmd); 5168c2ecf20Sopenharmony_ci BUG(); 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci set_ch_t(&data->seek_addr, 5198c2ecf20Sopenharmony_ci trk / private->rdc_data.trk_per_cyl, 5208c2ecf20Sopenharmony_ci trk % private->rdc_data.trk_per_cyl); 5218c2ecf20Sopenharmony_ci data->search_arg.cyl = data->seek_addr.cyl; 5228c2ecf20Sopenharmony_ci data->search_arg.head = data->seek_addr.head; 5238c2ecf20Sopenharmony_ci data->search_arg.record = rec_on_trk; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic int prefix_LRE(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, 5278c2ecf20Sopenharmony_ci unsigned int trk, unsigned int totrk, int cmd, 5288c2ecf20Sopenharmony_ci struct dasd_device *basedev, struct dasd_device *startdev, 5298c2ecf20Sopenharmony_ci unsigned int format, unsigned int rec_on_trk, int count, 5308c2ecf20Sopenharmony_ci unsigned int blksize, unsigned int tlf) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci struct dasd_eckd_private *basepriv, *startpriv; 5338c2ecf20Sopenharmony_ci struct LRE_eckd_data *lredata; 5348c2ecf20Sopenharmony_ci struct DE_eckd_data *dedata; 5358c2ecf20Sopenharmony_ci int rc = 0; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci basepriv = basedev->private; 5388c2ecf20Sopenharmony_ci startpriv = startdev->private; 5398c2ecf20Sopenharmony_ci dedata = &pfxdata->define_extent; 5408c2ecf20Sopenharmony_ci lredata = &pfxdata->locate_record; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PFX; 5438c2ecf20Sopenharmony_ci ccw->flags = 0; 5448c2ecf20Sopenharmony_ci if (cmd == DASD_ECKD_CCW_WRITE_FULL_TRACK) { 5458c2ecf20Sopenharmony_ci ccw->count = sizeof(*pfxdata) + 2; 5468c2ecf20Sopenharmony_ci ccw->cda = (__u32) __pa(pfxdata); 5478c2ecf20Sopenharmony_ci memset(pfxdata, 0, sizeof(*pfxdata) + 2); 5488c2ecf20Sopenharmony_ci } else { 5498c2ecf20Sopenharmony_ci ccw->count = sizeof(*pfxdata); 5508c2ecf20Sopenharmony_ci ccw->cda = (__u32) __pa(pfxdata); 5518c2ecf20Sopenharmony_ci memset(pfxdata, 0, sizeof(*pfxdata)); 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* prefix data */ 5558c2ecf20Sopenharmony_ci if (format > 1) { 5568c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_ERR, basedev, 5578c2ecf20Sopenharmony_ci "PFX LRE unknown format 0x%x", format); 5588c2ecf20Sopenharmony_ci BUG(); 5598c2ecf20Sopenharmony_ci return -EINVAL; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci pfxdata->format = format; 5628c2ecf20Sopenharmony_ci pfxdata->base_address = basepriv->ned->unit_addr; 5638c2ecf20Sopenharmony_ci pfxdata->base_lss = basepriv->ned->ID; 5648c2ecf20Sopenharmony_ci pfxdata->validity.define_extent = 1; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* private uid is kept up to date, conf_data may be outdated */ 5678c2ecf20Sopenharmony_ci if (startpriv->uid.type == UA_BASE_PAV_ALIAS) 5688c2ecf20Sopenharmony_ci pfxdata->validity.verify_base = 1; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (startpriv->uid.type == UA_HYPER_PAV_ALIAS) { 5718c2ecf20Sopenharmony_ci pfxdata->validity.verify_base = 1; 5728c2ecf20Sopenharmony_ci pfxdata->validity.hyper_pav = 1; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci rc = define_extent(NULL, dedata, trk, totrk, cmd, basedev, blksize); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* 5788c2ecf20Sopenharmony_ci * For some commands the System Time Stamp is set in the define extent 5798c2ecf20Sopenharmony_ci * data when XRC is supported. The validity of the time stamp must be 5808c2ecf20Sopenharmony_ci * reflected in the prefix data as well. 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_ci if (dedata->ga_extended & 0x08 && dedata->ga_extended & 0x02) 5838c2ecf20Sopenharmony_ci pfxdata->validity.time_stamp = 1; /* 'Time Stamp Valid' */ 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (format == 1) { 5868c2ecf20Sopenharmony_ci locate_record_ext(NULL, lredata, trk, rec_on_trk, count, cmd, 5878c2ecf20Sopenharmony_ci basedev, blksize, tlf); 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci return rc; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, 5948c2ecf20Sopenharmony_ci unsigned int trk, unsigned int totrk, int cmd, 5958c2ecf20Sopenharmony_ci struct dasd_device *basedev, struct dasd_device *startdev) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci return prefix_LRE(ccw, pfxdata, trk, totrk, cmd, basedev, startdev, 5988c2ecf20Sopenharmony_ci 0, 0, 0, 0, 0); 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic void 6028c2ecf20Sopenharmony_cilocate_record(struct ccw1 *ccw, struct LO_eckd_data *data, unsigned int trk, 6038c2ecf20Sopenharmony_ci unsigned int rec_on_trk, int no_rec, int cmd, 6048c2ecf20Sopenharmony_ci struct dasd_device * device, int reclen) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 6078c2ecf20Sopenharmony_ci int sector; 6088c2ecf20Sopenharmony_ci int dn, d; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_INFO, device, 6118c2ecf20Sopenharmony_ci "Locate: trk %d, rec %d, no_rec %d, cmd %d, reclen %d", 6128c2ecf20Sopenharmony_ci trk, rec_on_trk, no_rec, cmd, reclen); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD; 6158c2ecf20Sopenharmony_ci ccw->flags = 0; 6168c2ecf20Sopenharmony_ci ccw->count = 16; 6178c2ecf20Sopenharmony_ci ccw->cda = (__u32) __pa(data); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci memset(data, 0, sizeof(struct LO_eckd_data)); 6208c2ecf20Sopenharmony_ci sector = 0; 6218c2ecf20Sopenharmony_ci if (rec_on_trk) { 6228c2ecf20Sopenharmony_ci switch (private->rdc_data.dev_type) { 6238c2ecf20Sopenharmony_ci case 0x3390: 6248c2ecf20Sopenharmony_ci dn = ceil_quot(reclen + 6, 232); 6258c2ecf20Sopenharmony_ci d = 9 + ceil_quot(reclen + 6 * (dn + 1), 34); 6268c2ecf20Sopenharmony_ci sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8; 6278c2ecf20Sopenharmony_ci break; 6288c2ecf20Sopenharmony_ci case 0x3380: 6298c2ecf20Sopenharmony_ci d = 7 + ceil_quot(reclen + 12, 32); 6308c2ecf20Sopenharmony_ci sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7; 6318c2ecf20Sopenharmony_ci break; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci data->sector = sector; 6358c2ecf20Sopenharmony_ci data->count = no_rec; 6368c2ecf20Sopenharmony_ci switch (cmd) { 6378c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: 6388c2ecf20Sopenharmony_ci data->operation.orientation = 0x3; 6398c2ecf20Sopenharmony_ci data->operation.operation = 0x03; 6408c2ecf20Sopenharmony_ci break; 6418c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_HOME_ADDRESS: 6428c2ecf20Sopenharmony_ci data->operation.orientation = 0x3; 6438c2ecf20Sopenharmony_ci data->operation.operation = 0x16; 6448c2ecf20Sopenharmony_ci break; 6458c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_RECORD_ZERO: 6468c2ecf20Sopenharmony_ci data->operation.orientation = 0x1; 6478c2ecf20Sopenharmony_ci data->operation.operation = 0x03; 6488c2ecf20Sopenharmony_ci data->count++; 6498c2ecf20Sopenharmony_ci break; 6508c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_RECORD_ZERO: 6518c2ecf20Sopenharmony_ci data->operation.orientation = 0x3; 6528c2ecf20Sopenharmony_ci data->operation.operation = 0x16; 6538c2ecf20Sopenharmony_ci data->count++; 6548c2ecf20Sopenharmony_ci break; 6558c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE: 6568c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_MT: 6578c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_KD: 6588c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_KD_MT: 6598c2ecf20Sopenharmony_ci data->auxiliary.last_bytes_used = 0x1; 6608c2ecf20Sopenharmony_ci data->length = reclen; 6618c2ecf20Sopenharmony_ci data->operation.operation = 0x01; 6628c2ecf20Sopenharmony_ci break; 6638c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_CKD: 6648c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_CKD_MT: 6658c2ecf20Sopenharmony_ci data->auxiliary.last_bytes_used = 0x1; 6668c2ecf20Sopenharmony_ci data->length = reclen; 6678c2ecf20Sopenharmony_ci data->operation.operation = 0x03; 6688c2ecf20Sopenharmony_ci break; 6698c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ: 6708c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_MT: 6718c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_KD: 6728c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_KD_MT: 6738c2ecf20Sopenharmony_ci data->auxiliary.last_bytes_used = 0x1; 6748c2ecf20Sopenharmony_ci data->length = reclen; 6758c2ecf20Sopenharmony_ci data->operation.operation = 0x06; 6768c2ecf20Sopenharmony_ci break; 6778c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_CKD: 6788c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_CKD_MT: 6798c2ecf20Sopenharmony_ci data->auxiliary.last_bytes_used = 0x1; 6808c2ecf20Sopenharmony_ci data->length = reclen; 6818c2ecf20Sopenharmony_ci data->operation.operation = 0x16; 6828c2ecf20Sopenharmony_ci break; 6838c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_COUNT: 6848c2ecf20Sopenharmony_ci data->operation.operation = 0x06; 6858c2ecf20Sopenharmony_ci break; 6868c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_ERASE: 6878c2ecf20Sopenharmony_ci data->length = reclen; 6888c2ecf20Sopenharmony_ci data->auxiliary.last_bytes_used = 0x1; 6898c2ecf20Sopenharmony_ci data->operation.operation = 0x0b; 6908c2ecf20Sopenharmony_ci break; 6918c2ecf20Sopenharmony_ci default: 6928c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_ERR, device, "unknown locate record " 6938c2ecf20Sopenharmony_ci "opcode 0x%x", cmd); 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci set_ch_t(&data->seek_addr, 6968c2ecf20Sopenharmony_ci trk / private->rdc_data.trk_per_cyl, 6978c2ecf20Sopenharmony_ci trk % private->rdc_data.trk_per_cyl); 6988c2ecf20Sopenharmony_ci data->search_arg.cyl = data->seek_addr.cyl; 6998c2ecf20Sopenharmony_ci data->search_arg.head = data->seek_addr.head; 7008c2ecf20Sopenharmony_ci data->search_arg.record = rec_on_trk; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci/* 7048c2ecf20Sopenharmony_ci * Returns 1 if the block is one of the special blocks that needs 7058c2ecf20Sopenharmony_ci * to get read/written with the KD variant of the command. 7068c2ecf20Sopenharmony_ci * That is DASD_ECKD_READ_KD_MT instead of DASD_ECKD_READ_MT and 7078c2ecf20Sopenharmony_ci * DASD_ECKD_WRITE_KD_MT instead of DASD_ECKD_WRITE_MT. 7088c2ecf20Sopenharmony_ci * Luckily the KD variants differ only by one bit (0x08) from the 7098c2ecf20Sopenharmony_ci * normal variant. So don't wonder about code like: 7108c2ecf20Sopenharmony_ci * if (dasd_eckd_cdl_special(blk_per_trk, recid)) 7118c2ecf20Sopenharmony_ci * ccw->cmd_code |= 0x8; 7128c2ecf20Sopenharmony_ci */ 7138c2ecf20Sopenharmony_cistatic inline int 7148c2ecf20Sopenharmony_cidasd_eckd_cdl_special(int blk_per_trk, int recid) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci if (recid < 3) 7178c2ecf20Sopenharmony_ci return 1; 7188c2ecf20Sopenharmony_ci if (recid < blk_per_trk) 7198c2ecf20Sopenharmony_ci return 0; 7208c2ecf20Sopenharmony_ci if (recid < 2 * blk_per_trk) 7218c2ecf20Sopenharmony_ci return 1; 7228c2ecf20Sopenharmony_ci return 0; 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci/* 7268c2ecf20Sopenharmony_ci * Returns the record size for the special blocks of the cdl format. 7278c2ecf20Sopenharmony_ci * Only returns something useful if dasd_eckd_cdl_special is true 7288c2ecf20Sopenharmony_ci * for the recid. 7298c2ecf20Sopenharmony_ci */ 7308c2ecf20Sopenharmony_cistatic inline int 7318c2ecf20Sopenharmony_cidasd_eckd_cdl_reclen(int recid) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci if (recid < 3) 7348c2ecf20Sopenharmony_ci return sizes_trk0[recid]; 7358c2ecf20Sopenharmony_ci return LABEL_SIZE; 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci/* create unique id from private structure. */ 7388c2ecf20Sopenharmony_cistatic void create_uid(struct dasd_eckd_private *private) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci int count; 7418c2ecf20Sopenharmony_ci struct dasd_uid *uid; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci uid = &private->uid; 7448c2ecf20Sopenharmony_ci memset(uid, 0, sizeof(struct dasd_uid)); 7458c2ecf20Sopenharmony_ci memcpy(uid->vendor, private->ned->HDA_manufacturer, 7468c2ecf20Sopenharmony_ci sizeof(uid->vendor) - 1); 7478c2ecf20Sopenharmony_ci EBCASC(uid->vendor, sizeof(uid->vendor) - 1); 7488c2ecf20Sopenharmony_ci memcpy(uid->serial, private->ned->HDA_location, 7498c2ecf20Sopenharmony_ci sizeof(uid->serial) - 1); 7508c2ecf20Sopenharmony_ci EBCASC(uid->serial, sizeof(uid->serial) - 1); 7518c2ecf20Sopenharmony_ci uid->ssid = private->gneq->subsystemID; 7528c2ecf20Sopenharmony_ci uid->real_unit_addr = private->ned->unit_addr; 7538c2ecf20Sopenharmony_ci if (private->sneq) { 7548c2ecf20Sopenharmony_ci uid->type = private->sneq->sua_flags; 7558c2ecf20Sopenharmony_ci if (uid->type == UA_BASE_PAV_ALIAS) 7568c2ecf20Sopenharmony_ci uid->base_unit_addr = private->sneq->base_unit_addr; 7578c2ecf20Sopenharmony_ci } else { 7588c2ecf20Sopenharmony_ci uid->type = UA_BASE_DEVICE; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci if (private->vdsneq) { 7618c2ecf20Sopenharmony_ci for (count = 0; count < 16; count++) { 7628c2ecf20Sopenharmony_ci sprintf(uid->vduit+2*count, "%02x", 7638c2ecf20Sopenharmony_ci private->vdsneq->uit[count]); 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci/* 7698c2ecf20Sopenharmony_ci * Generate device unique id that specifies the physical device. 7708c2ecf20Sopenharmony_ci */ 7718c2ecf20Sopenharmony_cistatic int dasd_eckd_generate_uid(struct dasd_device *device) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 7748c2ecf20Sopenharmony_ci unsigned long flags; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci if (!private) 7778c2ecf20Sopenharmony_ci return -ENODEV; 7788c2ecf20Sopenharmony_ci if (!private->ned || !private->gneq) 7798c2ecf20Sopenharmony_ci return -ENODEV; 7808c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 7818c2ecf20Sopenharmony_ci create_uid(private); 7828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 7838c2ecf20Sopenharmony_ci return 0; 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_cistatic int dasd_eckd_get_uid(struct dasd_device *device, struct dasd_uid *uid) 7878c2ecf20Sopenharmony_ci{ 7888c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 7898c2ecf20Sopenharmony_ci unsigned long flags; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci if (private) { 7928c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 7938c2ecf20Sopenharmony_ci *uid = private->uid; 7948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 7958c2ecf20Sopenharmony_ci return 0; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci return -EINVAL; 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci/* 8018c2ecf20Sopenharmony_ci * compare device UID with data of a given dasd_eckd_private structure 8028c2ecf20Sopenharmony_ci * return 0 for match 8038c2ecf20Sopenharmony_ci */ 8048c2ecf20Sopenharmony_cistatic int dasd_eckd_compare_path_uid(struct dasd_device *device, 8058c2ecf20Sopenharmony_ci struct dasd_eckd_private *private) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci struct dasd_uid device_uid; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci create_uid(private); 8108c2ecf20Sopenharmony_ci dasd_eckd_get_uid(device, &device_uid); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci return memcmp(&device_uid, &private->uid, sizeof(struct dasd_uid)); 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_cistatic void dasd_eckd_fill_rcd_cqr(struct dasd_device *device, 8168c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr, 8178c2ecf20Sopenharmony_ci __u8 *rcd_buffer, 8188c2ecf20Sopenharmony_ci __u8 lpm) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci struct ccw1 *ccw; 8218c2ecf20Sopenharmony_ci /* 8228c2ecf20Sopenharmony_ci * buffer has to start with EBCDIC "V1.0" to show 8238c2ecf20Sopenharmony_ci * support for virtual device SNEQ 8248c2ecf20Sopenharmony_ci */ 8258c2ecf20Sopenharmony_ci rcd_buffer[0] = 0xE5; 8268c2ecf20Sopenharmony_ci rcd_buffer[1] = 0xF1; 8278c2ecf20Sopenharmony_ci rcd_buffer[2] = 0x4B; 8288c2ecf20Sopenharmony_ci rcd_buffer[3] = 0xF0; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 8318c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RCD; 8328c2ecf20Sopenharmony_ci ccw->flags = 0; 8338c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t)rcd_buffer; 8348c2ecf20Sopenharmony_ci ccw->count = DASD_ECKD_RCD_DATA_SIZE; 8358c2ecf20Sopenharmony_ci cqr->magic = DASD_ECKD_MAGIC; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci cqr->startdev = device; 8388c2ecf20Sopenharmony_ci cqr->memdev = device; 8398c2ecf20Sopenharmony_ci cqr->block = NULL; 8408c2ecf20Sopenharmony_ci cqr->expires = 10*HZ; 8418c2ecf20Sopenharmony_ci cqr->lpm = lpm; 8428c2ecf20Sopenharmony_ci cqr->retries = 256; 8438c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 8448c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 8458c2ecf20Sopenharmony_ci set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags); 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci/* 8498c2ecf20Sopenharmony_ci * Wakeup helper for read_conf 8508c2ecf20Sopenharmony_ci * if the cqr is not done and needs some error recovery 8518c2ecf20Sopenharmony_ci * the buffer has to be re-initialized with the EBCDIC "V1.0" 8528c2ecf20Sopenharmony_ci * to show support for virtual device SNEQ 8538c2ecf20Sopenharmony_ci */ 8548c2ecf20Sopenharmony_cistatic void read_conf_cb(struct dasd_ccw_req *cqr, void *data) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci struct ccw1 *ccw; 8578c2ecf20Sopenharmony_ci __u8 *rcd_buffer; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (cqr->status != DASD_CQR_DONE) { 8608c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 8618c2ecf20Sopenharmony_ci rcd_buffer = (__u8 *)((addr_t) ccw->cda); 8628c2ecf20Sopenharmony_ci memset(rcd_buffer, 0, sizeof(*rcd_buffer)); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci rcd_buffer[0] = 0xE5; 8658c2ecf20Sopenharmony_ci rcd_buffer[1] = 0xF1; 8668c2ecf20Sopenharmony_ci rcd_buffer[2] = 0x4B; 8678c2ecf20Sopenharmony_ci rcd_buffer[3] = 0xF0; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci dasd_wakeup_cb(cqr, data); 8708c2ecf20Sopenharmony_ci} 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cistatic int dasd_eckd_read_conf_immediately(struct dasd_device *device, 8738c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr, 8748c2ecf20Sopenharmony_ci __u8 *rcd_buffer, 8758c2ecf20Sopenharmony_ci __u8 lpm) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci struct ciw *ciw; 8788c2ecf20Sopenharmony_ci int rc; 8798c2ecf20Sopenharmony_ci /* 8808c2ecf20Sopenharmony_ci * sanity check: scan for RCD command in extended SenseID data 8818c2ecf20Sopenharmony_ci * some devices do not support RCD 8828c2ecf20Sopenharmony_ci */ 8838c2ecf20Sopenharmony_ci ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD); 8848c2ecf20Sopenharmony_ci if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD) 8858c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buffer, lpm); 8888c2ecf20Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); 8898c2ecf20Sopenharmony_ci set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags); 8908c2ecf20Sopenharmony_ci cqr->retries = 5; 8918c2ecf20Sopenharmony_ci cqr->callback = read_conf_cb; 8928c2ecf20Sopenharmony_ci rc = dasd_sleep_on_immediatly(cqr); 8938c2ecf20Sopenharmony_ci return rc; 8948c2ecf20Sopenharmony_ci} 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_cistatic int dasd_eckd_read_conf_lpm(struct dasd_device *device, 8978c2ecf20Sopenharmony_ci void **rcd_buffer, 8988c2ecf20Sopenharmony_ci int *rcd_buffer_size, __u8 lpm) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci struct ciw *ciw; 9018c2ecf20Sopenharmony_ci char *rcd_buf = NULL; 9028c2ecf20Sopenharmony_ci int ret; 9038c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci /* 9068c2ecf20Sopenharmony_ci * sanity check: scan for RCD command in extended SenseID data 9078c2ecf20Sopenharmony_ci * some devices do not support RCD 9088c2ecf20Sopenharmony_ci */ 9098c2ecf20Sopenharmony_ci ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD); 9108c2ecf20Sopenharmony_ci if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD) { 9118c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 9128c2ecf20Sopenharmony_ci goto out_error; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci rcd_buf = kzalloc(DASD_ECKD_RCD_DATA_SIZE, GFP_KERNEL | GFP_DMA); 9158c2ecf20Sopenharmony_ci if (!rcd_buf) { 9168c2ecf20Sopenharmony_ci ret = -ENOMEM; 9178c2ecf20Sopenharmony_ci goto out_error; 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */, 9208c2ecf20Sopenharmony_ci 0, /* use rcd_buf as data ara */ 9218c2ecf20Sopenharmony_ci device, NULL); 9228c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 9238c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 9248c2ecf20Sopenharmony_ci "Could not allocate RCD request"); 9258c2ecf20Sopenharmony_ci ret = -ENOMEM; 9268c2ecf20Sopenharmony_ci goto out_error; 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buf, lpm); 9298c2ecf20Sopenharmony_ci cqr->callback = read_conf_cb; 9308c2ecf20Sopenharmony_ci ret = dasd_sleep_on(cqr); 9318c2ecf20Sopenharmony_ci /* 9328c2ecf20Sopenharmony_ci * on success we update the user input parms 9338c2ecf20Sopenharmony_ci */ 9348c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 9358c2ecf20Sopenharmony_ci if (ret) 9368c2ecf20Sopenharmony_ci goto out_error; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci *rcd_buffer_size = DASD_ECKD_RCD_DATA_SIZE; 9398c2ecf20Sopenharmony_ci *rcd_buffer = rcd_buf; 9408c2ecf20Sopenharmony_ci return 0; 9418c2ecf20Sopenharmony_ciout_error: 9428c2ecf20Sopenharmony_ci kfree(rcd_buf); 9438c2ecf20Sopenharmony_ci *rcd_buffer = NULL; 9448c2ecf20Sopenharmony_ci *rcd_buffer_size = 0; 9458c2ecf20Sopenharmony_ci return ret; 9468c2ecf20Sopenharmony_ci} 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cistatic int dasd_eckd_identify_conf_parts(struct dasd_eckd_private *private) 9498c2ecf20Sopenharmony_ci{ 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci struct dasd_sneq *sneq; 9528c2ecf20Sopenharmony_ci int i, count; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci private->ned = NULL; 9558c2ecf20Sopenharmony_ci private->sneq = NULL; 9568c2ecf20Sopenharmony_ci private->vdsneq = NULL; 9578c2ecf20Sopenharmony_ci private->gneq = NULL; 9588c2ecf20Sopenharmony_ci count = private->conf_len / sizeof(struct dasd_sneq); 9598c2ecf20Sopenharmony_ci sneq = (struct dasd_sneq *)private->conf_data; 9608c2ecf20Sopenharmony_ci for (i = 0; i < count; ++i) { 9618c2ecf20Sopenharmony_ci if (sneq->flags.identifier == 1 && sneq->format == 1) 9628c2ecf20Sopenharmony_ci private->sneq = sneq; 9638c2ecf20Sopenharmony_ci else if (sneq->flags.identifier == 1 && sneq->format == 4) 9648c2ecf20Sopenharmony_ci private->vdsneq = (struct vd_sneq *)sneq; 9658c2ecf20Sopenharmony_ci else if (sneq->flags.identifier == 2) 9668c2ecf20Sopenharmony_ci private->gneq = (struct dasd_gneq *)sneq; 9678c2ecf20Sopenharmony_ci else if (sneq->flags.identifier == 3 && sneq->res1 == 1) 9688c2ecf20Sopenharmony_ci private->ned = (struct dasd_ned *)sneq; 9698c2ecf20Sopenharmony_ci sneq++; 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci if (!private->ned || !private->gneq) { 9728c2ecf20Sopenharmony_ci private->ned = NULL; 9738c2ecf20Sopenharmony_ci private->sneq = NULL; 9748c2ecf20Sopenharmony_ci private->vdsneq = NULL; 9758c2ecf20Sopenharmony_ci private->gneq = NULL; 9768c2ecf20Sopenharmony_ci return -EINVAL; 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci return 0; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci}; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_cistatic unsigned char dasd_eckd_path_access(void *conf_data, int conf_len) 9838c2ecf20Sopenharmony_ci{ 9848c2ecf20Sopenharmony_ci struct dasd_gneq *gneq; 9858c2ecf20Sopenharmony_ci int i, count, found; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci count = conf_len / sizeof(*gneq); 9888c2ecf20Sopenharmony_ci gneq = (struct dasd_gneq *)conf_data; 9898c2ecf20Sopenharmony_ci found = 0; 9908c2ecf20Sopenharmony_ci for (i = 0; i < count; ++i) { 9918c2ecf20Sopenharmony_ci if (gneq->flags.identifier == 2) { 9928c2ecf20Sopenharmony_ci found = 1; 9938c2ecf20Sopenharmony_ci break; 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci gneq++; 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci if (found) 9988c2ecf20Sopenharmony_ci return ((char *)gneq)[18] & 0x07; 9998c2ecf20Sopenharmony_ci else 10008c2ecf20Sopenharmony_ci return 0; 10018c2ecf20Sopenharmony_ci} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cistatic void dasd_eckd_clear_conf_data(struct dasd_device *device) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 10068c2ecf20Sopenharmony_ci int i; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci private->conf_data = NULL; 10098c2ecf20Sopenharmony_ci private->conf_len = 0; 10108c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 10118c2ecf20Sopenharmony_ci kfree(device->path[i].conf_data); 10128c2ecf20Sopenharmony_ci device->path[i].conf_data = NULL; 10138c2ecf20Sopenharmony_ci device->path[i].cssid = 0; 10148c2ecf20Sopenharmony_ci device->path[i].ssid = 0; 10158c2ecf20Sopenharmony_ci device->path[i].chpid = 0; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_cistatic int dasd_eckd_read_conf(struct dasd_device *device) 10218c2ecf20Sopenharmony_ci{ 10228c2ecf20Sopenharmony_ci void *conf_data; 10238c2ecf20Sopenharmony_ci int conf_len, conf_data_saved; 10248c2ecf20Sopenharmony_ci int rc, path_err, pos; 10258c2ecf20Sopenharmony_ci __u8 lpm, opm; 10268c2ecf20Sopenharmony_ci struct dasd_eckd_private *private, path_private; 10278c2ecf20Sopenharmony_ci struct dasd_uid *uid; 10288c2ecf20Sopenharmony_ci char print_path_uid[60], print_device_uid[60]; 10298c2ecf20Sopenharmony_ci struct channel_path_desc_fmt0 *chp_desc; 10308c2ecf20Sopenharmony_ci struct subchannel_id sch_id; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci private = device->private; 10338c2ecf20Sopenharmony_ci opm = ccw_device_get_path_mask(device->cdev); 10348c2ecf20Sopenharmony_ci ccw_device_get_schid(device->cdev, &sch_id); 10358c2ecf20Sopenharmony_ci conf_data_saved = 0; 10368c2ecf20Sopenharmony_ci path_err = 0; 10378c2ecf20Sopenharmony_ci /* get configuration data per operational path */ 10388c2ecf20Sopenharmony_ci for (lpm = 0x80; lpm; lpm>>= 1) { 10398c2ecf20Sopenharmony_ci if (!(lpm & opm)) 10408c2ecf20Sopenharmony_ci continue; 10418c2ecf20Sopenharmony_ci rc = dasd_eckd_read_conf_lpm(device, &conf_data, 10428c2ecf20Sopenharmony_ci &conf_len, lpm); 10438c2ecf20Sopenharmony_ci if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ 10448c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 10458c2ecf20Sopenharmony_ci "Read configuration data returned " 10468c2ecf20Sopenharmony_ci "error %d", rc); 10478c2ecf20Sopenharmony_ci return rc; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci if (conf_data == NULL) { 10508c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 10518c2ecf20Sopenharmony_ci "No configuration data " 10528c2ecf20Sopenharmony_ci "retrieved"); 10538c2ecf20Sopenharmony_ci /* no further analysis possible */ 10548c2ecf20Sopenharmony_ci dasd_path_add_opm(device, opm); 10558c2ecf20Sopenharmony_ci continue; /* no error */ 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci /* save first valid configuration data */ 10588c2ecf20Sopenharmony_ci if (!conf_data_saved) { 10598c2ecf20Sopenharmony_ci /* initially clear previously stored conf_data */ 10608c2ecf20Sopenharmony_ci dasd_eckd_clear_conf_data(device); 10618c2ecf20Sopenharmony_ci private->conf_data = conf_data; 10628c2ecf20Sopenharmony_ci private->conf_len = conf_len; 10638c2ecf20Sopenharmony_ci if (dasd_eckd_identify_conf_parts(private)) { 10648c2ecf20Sopenharmony_ci private->conf_data = NULL; 10658c2ecf20Sopenharmony_ci private->conf_len = 0; 10668c2ecf20Sopenharmony_ci kfree(conf_data); 10678c2ecf20Sopenharmony_ci continue; 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci pos = pathmask_to_pos(lpm); 10708c2ecf20Sopenharmony_ci /* store per path conf_data */ 10718c2ecf20Sopenharmony_ci device->path[pos].conf_data = conf_data; 10728c2ecf20Sopenharmony_ci device->path[pos].cssid = sch_id.cssid; 10738c2ecf20Sopenharmony_ci device->path[pos].ssid = sch_id.ssid; 10748c2ecf20Sopenharmony_ci chp_desc = ccw_device_get_chp_desc(device->cdev, pos); 10758c2ecf20Sopenharmony_ci if (chp_desc) 10768c2ecf20Sopenharmony_ci device->path[pos].chpid = chp_desc->chpid; 10778c2ecf20Sopenharmony_ci kfree(chp_desc); 10788c2ecf20Sopenharmony_ci /* 10798c2ecf20Sopenharmony_ci * build device UID that other path data 10808c2ecf20Sopenharmony_ci * can be compared to it 10818c2ecf20Sopenharmony_ci */ 10828c2ecf20Sopenharmony_ci dasd_eckd_generate_uid(device); 10838c2ecf20Sopenharmony_ci conf_data_saved++; 10848c2ecf20Sopenharmony_ci } else { 10858c2ecf20Sopenharmony_ci path_private.conf_data = conf_data; 10868c2ecf20Sopenharmony_ci path_private.conf_len = DASD_ECKD_RCD_DATA_SIZE; 10878c2ecf20Sopenharmony_ci if (dasd_eckd_identify_conf_parts( 10888c2ecf20Sopenharmony_ci &path_private)) { 10898c2ecf20Sopenharmony_ci path_private.conf_data = NULL; 10908c2ecf20Sopenharmony_ci path_private.conf_len = 0; 10918c2ecf20Sopenharmony_ci kfree(conf_data); 10928c2ecf20Sopenharmony_ci continue; 10938c2ecf20Sopenharmony_ci } 10948c2ecf20Sopenharmony_ci if (dasd_eckd_compare_path_uid( 10958c2ecf20Sopenharmony_ci device, &path_private)) { 10968c2ecf20Sopenharmony_ci uid = &path_private.uid; 10978c2ecf20Sopenharmony_ci if (strlen(uid->vduit) > 0) 10988c2ecf20Sopenharmony_ci snprintf(print_path_uid, 10998c2ecf20Sopenharmony_ci sizeof(print_path_uid), 11008c2ecf20Sopenharmony_ci "%s.%s.%04x.%02x.%s", 11018c2ecf20Sopenharmony_ci uid->vendor, uid->serial, 11028c2ecf20Sopenharmony_ci uid->ssid, uid->real_unit_addr, 11038c2ecf20Sopenharmony_ci uid->vduit); 11048c2ecf20Sopenharmony_ci else 11058c2ecf20Sopenharmony_ci snprintf(print_path_uid, 11068c2ecf20Sopenharmony_ci sizeof(print_path_uid), 11078c2ecf20Sopenharmony_ci "%s.%s.%04x.%02x", 11088c2ecf20Sopenharmony_ci uid->vendor, uid->serial, 11098c2ecf20Sopenharmony_ci uid->ssid, 11108c2ecf20Sopenharmony_ci uid->real_unit_addr); 11118c2ecf20Sopenharmony_ci uid = &private->uid; 11128c2ecf20Sopenharmony_ci if (strlen(uid->vduit) > 0) 11138c2ecf20Sopenharmony_ci snprintf(print_device_uid, 11148c2ecf20Sopenharmony_ci sizeof(print_device_uid), 11158c2ecf20Sopenharmony_ci "%s.%s.%04x.%02x.%s", 11168c2ecf20Sopenharmony_ci uid->vendor, uid->serial, 11178c2ecf20Sopenharmony_ci uid->ssid, uid->real_unit_addr, 11188c2ecf20Sopenharmony_ci uid->vduit); 11198c2ecf20Sopenharmony_ci else 11208c2ecf20Sopenharmony_ci snprintf(print_device_uid, 11218c2ecf20Sopenharmony_ci sizeof(print_device_uid), 11228c2ecf20Sopenharmony_ci "%s.%s.%04x.%02x", 11238c2ecf20Sopenharmony_ci uid->vendor, uid->serial, 11248c2ecf20Sopenharmony_ci uid->ssid, 11258c2ecf20Sopenharmony_ci uid->real_unit_addr); 11268c2ecf20Sopenharmony_ci dev_err(&device->cdev->dev, 11278c2ecf20Sopenharmony_ci "Not all channel paths lead to " 11288c2ecf20Sopenharmony_ci "the same device, path %02X leads to " 11298c2ecf20Sopenharmony_ci "device %s instead of %s\n", lpm, 11308c2ecf20Sopenharmony_ci print_path_uid, print_device_uid); 11318c2ecf20Sopenharmony_ci path_err = -EINVAL; 11328c2ecf20Sopenharmony_ci dasd_path_add_cablepm(device, lpm); 11338c2ecf20Sopenharmony_ci continue; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci pos = pathmask_to_pos(lpm); 11368c2ecf20Sopenharmony_ci /* store per path conf_data */ 11378c2ecf20Sopenharmony_ci device->path[pos].conf_data = conf_data; 11388c2ecf20Sopenharmony_ci device->path[pos].cssid = sch_id.cssid; 11398c2ecf20Sopenharmony_ci device->path[pos].ssid = sch_id.ssid; 11408c2ecf20Sopenharmony_ci chp_desc = ccw_device_get_chp_desc(device->cdev, pos); 11418c2ecf20Sopenharmony_ci if (chp_desc) 11428c2ecf20Sopenharmony_ci device->path[pos].chpid = chp_desc->chpid; 11438c2ecf20Sopenharmony_ci kfree(chp_desc); 11448c2ecf20Sopenharmony_ci path_private.conf_data = NULL; 11458c2ecf20Sopenharmony_ci path_private.conf_len = 0; 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci switch (dasd_eckd_path_access(conf_data, conf_len)) { 11488c2ecf20Sopenharmony_ci case 0x02: 11498c2ecf20Sopenharmony_ci dasd_path_add_nppm(device, lpm); 11508c2ecf20Sopenharmony_ci break; 11518c2ecf20Sopenharmony_ci case 0x03: 11528c2ecf20Sopenharmony_ci dasd_path_add_ppm(device, lpm); 11538c2ecf20Sopenharmony_ci break; 11548c2ecf20Sopenharmony_ci } 11558c2ecf20Sopenharmony_ci if (!dasd_path_get_opm(device)) { 11568c2ecf20Sopenharmony_ci dasd_path_set_opm(device, lpm); 11578c2ecf20Sopenharmony_ci dasd_generic_path_operational(device); 11588c2ecf20Sopenharmony_ci } else { 11598c2ecf20Sopenharmony_ci dasd_path_add_opm(device, lpm); 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci } 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci return path_err; 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_cistatic u32 get_fcx_max_data(struct dasd_device *device) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 11698c2ecf20Sopenharmony_ci int fcx_in_css, fcx_in_gneq, fcx_in_features; 11708c2ecf20Sopenharmony_ci unsigned int mdc; 11718c2ecf20Sopenharmony_ci int tpm; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci if (dasd_nofcx) 11748c2ecf20Sopenharmony_ci return 0; 11758c2ecf20Sopenharmony_ci /* is transport mode supported? */ 11768c2ecf20Sopenharmony_ci fcx_in_css = css_general_characteristics.fcx; 11778c2ecf20Sopenharmony_ci fcx_in_gneq = private->gneq->reserved2[7] & 0x04; 11788c2ecf20Sopenharmony_ci fcx_in_features = private->features.feature[40] & 0x80; 11798c2ecf20Sopenharmony_ci tpm = fcx_in_css && fcx_in_gneq && fcx_in_features; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci if (!tpm) 11828c2ecf20Sopenharmony_ci return 0; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci mdc = ccw_device_get_mdc(device->cdev, 0); 11858c2ecf20Sopenharmony_ci if (mdc == 0) { 11868c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, "Detecting the maximum supported data size for zHPF requests failed\n"); 11878c2ecf20Sopenharmony_ci return 0; 11888c2ecf20Sopenharmony_ci } else { 11898c2ecf20Sopenharmony_ci return (u32)mdc * FCX_MAX_DATA_FACTOR; 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci} 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cistatic int verify_fcx_max_data(struct dasd_device *device, __u8 lpm) 11948c2ecf20Sopenharmony_ci{ 11958c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 11968c2ecf20Sopenharmony_ci unsigned int mdc; 11978c2ecf20Sopenharmony_ci u32 fcx_max_data; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci if (private->fcx_max_data) { 12008c2ecf20Sopenharmony_ci mdc = ccw_device_get_mdc(device->cdev, lpm); 12018c2ecf20Sopenharmony_ci if (mdc == 0) { 12028c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, 12038c2ecf20Sopenharmony_ci "Detecting the maximum data size for zHPF " 12048c2ecf20Sopenharmony_ci "requests failed (rc=%d) for a new path %x\n", 12058c2ecf20Sopenharmony_ci mdc, lpm); 12068c2ecf20Sopenharmony_ci return mdc; 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci fcx_max_data = (u32)mdc * FCX_MAX_DATA_FACTOR; 12098c2ecf20Sopenharmony_ci if (fcx_max_data < private->fcx_max_data) { 12108c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, 12118c2ecf20Sopenharmony_ci "The maximum data size for zHPF requests %u " 12128c2ecf20Sopenharmony_ci "on a new path %x is below the active maximum " 12138c2ecf20Sopenharmony_ci "%u\n", fcx_max_data, lpm, 12148c2ecf20Sopenharmony_ci private->fcx_max_data); 12158c2ecf20Sopenharmony_ci return -EACCES; 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci } 12188c2ecf20Sopenharmony_ci return 0; 12198c2ecf20Sopenharmony_ci} 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_cistatic int rebuild_device_uid(struct dasd_device *device, 12228c2ecf20Sopenharmony_ci struct pe_handler_work_data *data) 12238c2ecf20Sopenharmony_ci{ 12248c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 12258c2ecf20Sopenharmony_ci __u8 lpm, opm = dasd_path_get_opm(device); 12268c2ecf20Sopenharmony_ci int rc = -ENODEV; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci for (lpm = 0x80; lpm; lpm >>= 1) { 12298c2ecf20Sopenharmony_ci if (!(lpm & opm)) 12308c2ecf20Sopenharmony_ci continue; 12318c2ecf20Sopenharmony_ci memset(&data->rcd_buffer, 0, sizeof(data->rcd_buffer)); 12328c2ecf20Sopenharmony_ci memset(&data->cqr, 0, sizeof(data->cqr)); 12338c2ecf20Sopenharmony_ci data->cqr.cpaddr = &data->ccw; 12348c2ecf20Sopenharmony_ci rc = dasd_eckd_read_conf_immediately(device, &data->cqr, 12358c2ecf20Sopenharmony_ci data->rcd_buffer, 12368c2ecf20Sopenharmony_ci lpm); 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci if (rc) { 12398c2ecf20Sopenharmony_ci if (rc == -EOPNOTSUPP) /* -EOPNOTSUPP is ok */ 12408c2ecf20Sopenharmony_ci continue; 12418c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 12428c2ecf20Sopenharmony_ci "Read configuration data " 12438c2ecf20Sopenharmony_ci "returned error %d", rc); 12448c2ecf20Sopenharmony_ci break; 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci memcpy(private->conf_data, data->rcd_buffer, 12478c2ecf20Sopenharmony_ci DASD_ECKD_RCD_DATA_SIZE); 12488c2ecf20Sopenharmony_ci if (dasd_eckd_identify_conf_parts(private)) { 12498c2ecf20Sopenharmony_ci rc = -ENODEV; 12508c2ecf20Sopenharmony_ci } else /* first valid path is enough */ 12518c2ecf20Sopenharmony_ci break; 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci if (!rc) 12558c2ecf20Sopenharmony_ci rc = dasd_eckd_generate_uid(device); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci return rc; 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_cistatic void dasd_eckd_path_available_action(struct dasd_device *device, 12618c2ecf20Sopenharmony_ci struct pe_handler_work_data *data) 12628c2ecf20Sopenharmony_ci{ 12638c2ecf20Sopenharmony_ci struct dasd_eckd_private path_private; 12648c2ecf20Sopenharmony_ci struct dasd_uid *uid; 12658c2ecf20Sopenharmony_ci __u8 path_rcd_buf[DASD_ECKD_RCD_DATA_SIZE]; 12668c2ecf20Sopenharmony_ci __u8 lpm, opm, npm, ppm, epm, hpfpm, cablepm; 12678c2ecf20Sopenharmony_ci unsigned long flags; 12688c2ecf20Sopenharmony_ci char print_uid[60]; 12698c2ecf20Sopenharmony_ci int rc; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci opm = 0; 12728c2ecf20Sopenharmony_ci npm = 0; 12738c2ecf20Sopenharmony_ci ppm = 0; 12748c2ecf20Sopenharmony_ci epm = 0; 12758c2ecf20Sopenharmony_ci hpfpm = 0; 12768c2ecf20Sopenharmony_ci cablepm = 0; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci for (lpm = 0x80; lpm; lpm >>= 1) { 12798c2ecf20Sopenharmony_ci if (!(lpm & data->tbvpm)) 12808c2ecf20Sopenharmony_ci continue; 12818c2ecf20Sopenharmony_ci memset(&data->rcd_buffer, 0, sizeof(data->rcd_buffer)); 12828c2ecf20Sopenharmony_ci memset(&data->cqr, 0, sizeof(data->cqr)); 12838c2ecf20Sopenharmony_ci data->cqr.cpaddr = &data->ccw; 12848c2ecf20Sopenharmony_ci rc = dasd_eckd_read_conf_immediately(device, &data->cqr, 12858c2ecf20Sopenharmony_ci data->rcd_buffer, 12868c2ecf20Sopenharmony_ci lpm); 12878c2ecf20Sopenharmony_ci if (!rc) { 12888c2ecf20Sopenharmony_ci switch (dasd_eckd_path_access(data->rcd_buffer, 12898c2ecf20Sopenharmony_ci DASD_ECKD_RCD_DATA_SIZE) 12908c2ecf20Sopenharmony_ci ) { 12918c2ecf20Sopenharmony_ci case 0x02: 12928c2ecf20Sopenharmony_ci npm |= lpm; 12938c2ecf20Sopenharmony_ci break; 12948c2ecf20Sopenharmony_ci case 0x03: 12958c2ecf20Sopenharmony_ci ppm |= lpm; 12968c2ecf20Sopenharmony_ci break; 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci opm |= lpm; 12998c2ecf20Sopenharmony_ci } else if (rc == -EOPNOTSUPP) { 13008c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 13018c2ecf20Sopenharmony_ci "path verification: No configuration " 13028c2ecf20Sopenharmony_ci "data retrieved"); 13038c2ecf20Sopenharmony_ci opm |= lpm; 13048c2ecf20Sopenharmony_ci } else if (rc == -EAGAIN) { 13058c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 13068c2ecf20Sopenharmony_ci "path verification: device is stopped," 13078c2ecf20Sopenharmony_ci " try again later"); 13088c2ecf20Sopenharmony_ci epm |= lpm; 13098c2ecf20Sopenharmony_ci } else { 13108c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, 13118c2ecf20Sopenharmony_ci "Reading device feature codes failed " 13128c2ecf20Sopenharmony_ci "(rc=%d) for new path %x\n", rc, lpm); 13138c2ecf20Sopenharmony_ci continue; 13148c2ecf20Sopenharmony_ci } 13158c2ecf20Sopenharmony_ci if (verify_fcx_max_data(device, lpm)) { 13168c2ecf20Sopenharmony_ci opm &= ~lpm; 13178c2ecf20Sopenharmony_ci npm &= ~lpm; 13188c2ecf20Sopenharmony_ci ppm &= ~lpm; 13198c2ecf20Sopenharmony_ci hpfpm |= lpm; 13208c2ecf20Sopenharmony_ci continue; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci /* 13248c2ecf20Sopenharmony_ci * save conf_data for comparison after 13258c2ecf20Sopenharmony_ci * rebuild_device_uid may have changed 13268c2ecf20Sopenharmony_ci * the original data 13278c2ecf20Sopenharmony_ci */ 13288c2ecf20Sopenharmony_ci memcpy(&path_rcd_buf, data->rcd_buffer, 13298c2ecf20Sopenharmony_ci DASD_ECKD_RCD_DATA_SIZE); 13308c2ecf20Sopenharmony_ci path_private.conf_data = (void *) &path_rcd_buf; 13318c2ecf20Sopenharmony_ci path_private.conf_len = DASD_ECKD_RCD_DATA_SIZE; 13328c2ecf20Sopenharmony_ci if (dasd_eckd_identify_conf_parts(&path_private)) { 13338c2ecf20Sopenharmony_ci path_private.conf_data = NULL; 13348c2ecf20Sopenharmony_ci path_private.conf_len = 0; 13358c2ecf20Sopenharmony_ci continue; 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci /* 13398c2ecf20Sopenharmony_ci * compare path UID with device UID only if at least 13408c2ecf20Sopenharmony_ci * one valid path is left 13418c2ecf20Sopenharmony_ci * in other case the device UID may have changed and 13428c2ecf20Sopenharmony_ci * the first working path UID will be used as device UID 13438c2ecf20Sopenharmony_ci */ 13448c2ecf20Sopenharmony_ci if (dasd_path_get_opm(device) && 13458c2ecf20Sopenharmony_ci dasd_eckd_compare_path_uid(device, &path_private)) { 13468c2ecf20Sopenharmony_ci /* 13478c2ecf20Sopenharmony_ci * the comparison was not successful 13488c2ecf20Sopenharmony_ci * rebuild the device UID with at least one 13498c2ecf20Sopenharmony_ci * known path in case a z/VM hyperswap command 13508c2ecf20Sopenharmony_ci * has changed the device 13518c2ecf20Sopenharmony_ci * 13528c2ecf20Sopenharmony_ci * after this compare again 13538c2ecf20Sopenharmony_ci * 13548c2ecf20Sopenharmony_ci * if either the rebuild or the recompare fails 13558c2ecf20Sopenharmony_ci * the path can not be used 13568c2ecf20Sopenharmony_ci */ 13578c2ecf20Sopenharmony_ci if (rebuild_device_uid(device, data) || 13588c2ecf20Sopenharmony_ci dasd_eckd_compare_path_uid( 13598c2ecf20Sopenharmony_ci device, &path_private)) { 13608c2ecf20Sopenharmony_ci uid = &path_private.uid; 13618c2ecf20Sopenharmony_ci if (strlen(uid->vduit) > 0) 13628c2ecf20Sopenharmony_ci snprintf(print_uid, sizeof(print_uid), 13638c2ecf20Sopenharmony_ci "%s.%s.%04x.%02x.%s", 13648c2ecf20Sopenharmony_ci uid->vendor, uid->serial, 13658c2ecf20Sopenharmony_ci uid->ssid, uid->real_unit_addr, 13668c2ecf20Sopenharmony_ci uid->vduit); 13678c2ecf20Sopenharmony_ci else 13688c2ecf20Sopenharmony_ci snprintf(print_uid, sizeof(print_uid), 13698c2ecf20Sopenharmony_ci "%s.%s.%04x.%02x", 13708c2ecf20Sopenharmony_ci uid->vendor, uid->serial, 13718c2ecf20Sopenharmony_ci uid->ssid, 13728c2ecf20Sopenharmony_ci uid->real_unit_addr); 13738c2ecf20Sopenharmony_ci dev_err(&device->cdev->dev, 13748c2ecf20Sopenharmony_ci "The newly added channel path %02X " 13758c2ecf20Sopenharmony_ci "will not be used because it leads " 13768c2ecf20Sopenharmony_ci "to a different device %s\n", 13778c2ecf20Sopenharmony_ci lpm, print_uid); 13788c2ecf20Sopenharmony_ci opm &= ~lpm; 13798c2ecf20Sopenharmony_ci npm &= ~lpm; 13808c2ecf20Sopenharmony_ci ppm &= ~lpm; 13818c2ecf20Sopenharmony_ci cablepm |= lpm; 13828c2ecf20Sopenharmony_ci continue; 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci } 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci /* 13878c2ecf20Sopenharmony_ci * There is a small chance that a path is lost again between 13888c2ecf20Sopenharmony_ci * above path verification and the following modification of 13898c2ecf20Sopenharmony_ci * the device opm mask. We could avoid that race here by using 13908c2ecf20Sopenharmony_ci * yet another path mask, but we rather deal with this unlikely 13918c2ecf20Sopenharmony_ci * situation in dasd_start_IO. 13928c2ecf20Sopenharmony_ci */ 13938c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 13948c2ecf20Sopenharmony_ci if (!dasd_path_get_opm(device) && opm) { 13958c2ecf20Sopenharmony_ci dasd_path_set_opm(device, opm); 13968c2ecf20Sopenharmony_ci dasd_generic_path_operational(device); 13978c2ecf20Sopenharmony_ci } else { 13988c2ecf20Sopenharmony_ci dasd_path_add_opm(device, opm); 13998c2ecf20Sopenharmony_ci } 14008c2ecf20Sopenharmony_ci dasd_path_add_nppm(device, npm); 14018c2ecf20Sopenharmony_ci dasd_path_add_ppm(device, ppm); 14028c2ecf20Sopenharmony_ci dasd_path_add_tbvpm(device, epm); 14038c2ecf20Sopenharmony_ci dasd_path_add_cablepm(device, cablepm); 14048c2ecf20Sopenharmony_ci dasd_path_add_nohpfpm(device, hpfpm); 14058c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 14068c2ecf20Sopenharmony_ci } 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_cistatic void do_pe_handler_work(struct work_struct *work) 14108c2ecf20Sopenharmony_ci{ 14118c2ecf20Sopenharmony_ci struct pe_handler_work_data *data; 14128c2ecf20Sopenharmony_ci struct dasd_device *device; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci data = container_of(work, struct pe_handler_work_data, worker); 14158c2ecf20Sopenharmony_ci device = data->device; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci /* delay path verification until device was resumed */ 14188c2ecf20Sopenharmony_ci if (test_bit(DASD_FLAG_SUSPENDED, &device->flags)) { 14198c2ecf20Sopenharmony_ci schedule_work(work); 14208c2ecf20Sopenharmony_ci return; 14218c2ecf20Sopenharmony_ci } 14228c2ecf20Sopenharmony_ci /* check if path verification already running and delay if so */ 14238c2ecf20Sopenharmony_ci if (test_and_set_bit(DASD_FLAG_PATH_VERIFY, &device->flags)) { 14248c2ecf20Sopenharmony_ci schedule_work(work); 14258c2ecf20Sopenharmony_ci return; 14268c2ecf20Sopenharmony_ci } 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci dasd_eckd_path_available_action(device, data); 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci clear_bit(DASD_FLAG_PATH_VERIFY, &device->flags); 14318c2ecf20Sopenharmony_ci dasd_put_device(device); 14328c2ecf20Sopenharmony_ci if (data->isglobal) 14338c2ecf20Sopenharmony_ci mutex_unlock(&dasd_pe_handler_mutex); 14348c2ecf20Sopenharmony_ci else 14358c2ecf20Sopenharmony_ci kfree(data); 14368c2ecf20Sopenharmony_ci} 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_cistatic int dasd_eckd_pe_handler(struct dasd_device *device, __u8 lpm) 14398c2ecf20Sopenharmony_ci{ 14408c2ecf20Sopenharmony_ci struct pe_handler_work_data *data; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci data = kmalloc(sizeof(*data), GFP_ATOMIC | GFP_DMA); 14438c2ecf20Sopenharmony_ci if (!data) { 14448c2ecf20Sopenharmony_ci if (mutex_trylock(&dasd_pe_handler_mutex)) { 14458c2ecf20Sopenharmony_ci data = pe_handler_worker; 14468c2ecf20Sopenharmony_ci data->isglobal = 1; 14478c2ecf20Sopenharmony_ci } else { 14488c2ecf20Sopenharmony_ci return -ENOMEM; 14498c2ecf20Sopenharmony_ci } 14508c2ecf20Sopenharmony_ci } else { 14518c2ecf20Sopenharmony_ci memset(data, 0, sizeof(*data)); 14528c2ecf20Sopenharmony_ci data->isglobal = 0; 14538c2ecf20Sopenharmony_ci } 14548c2ecf20Sopenharmony_ci INIT_WORK(&data->worker, do_pe_handler_work); 14558c2ecf20Sopenharmony_ci dasd_get_device(device); 14568c2ecf20Sopenharmony_ci data->device = device; 14578c2ecf20Sopenharmony_ci data->tbvpm = lpm; 14588c2ecf20Sopenharmony_ci schedule_work(&data->worker); 14598c2ecf20Sopenharmony_ci return 0; 14608c2ecf20Sopenharmony_ci} 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_cistatic void dasd_eckd_reset_path(struct dasd_device *device, __u8 pm) 14638c2ecf20Sopenharmony_ci{ 14648c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 14658c2ecf20Sopenharmony_ci unsigned long flags; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci if (!private->fcx_max_data) 14688c2ecf20Sopenharmony_ci private->fcx_max_data = get_fcx_max_data(device); 14698c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 14708c2ecf20Sopenharmony_ci dasd_path_set_tbvpm(device, pm ? : dasd_path_get_notoperpm(device)); 14718c2ecf20Sopenharmony_ci dasd_schedule_device_bh(device); 14728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 14738c2ecf20Sopenharmony_ci} 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_cistatic int dasd_eckd_read_features(struct dasd_device *device) 14768c2ecf20Sopenharmony_ci{ 14778c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 14788c2ecf20Sopenharmony_ci struct dasd_psf_prssd_data *prssdp; 14798c2ecf20Sopenharmony_ci struct dasd_rssd_features *features; 14808c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 14818c2ecf20Sopenharmony_ci struct ccw1 *ccw; 14828c2ecf20Sopenharmony_ci int rc; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci memset(&private->features, 0, sizeof(struct dasd_rssd_features)); 14858c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, 14868c2ecf20Sopenharmony_ci (sizeof(struct dasd_psf_prssd_data) + 14878c2ecf20Sopenharmony_ci sizeof(struct dasd_rssd_features)), 14888c2ecf20Sopenharmony_ci device, NULL); 14898c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 14908c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "Could not " 14918c2ecf20Sopenharmony_ci "allocate initialization request"); 14928c2ecf20Sopenharmony_ci return PTR_ERR(cqr); 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci cqr->startdev = device; 14958c2ecf20Sopenharmony_ci cqr->memdev = device; 14968c2ecf20Sopenharmony_ci cqr->block = NULL; 14978c2ecf20Sopenharmony_ci cqr->retries = 256; 14988c2ecf20Sopenharmony_ci cqr->expires = 10 * HZ; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci /* Prepare for Read Subsystem Data */ 15018c2ecf20Sopenharmony_ci prssdp = (struct dasd_psf_prssd_data *) cqr->data; 15028c2ecf20Sopenharmony_ci memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); 15038c2ecf20Sopenharmony_ci prssdp->order = PSF_ORDER_PRSSD; 15048c2ecf20Sopenharmony_ci prssdp->suborder = 0x41; /* Read Feature Codes */ 15058c2ecf20Sopenharmony_ci /* all other bytes of prssdp must be zero */ 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 15088c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 15098c2ecf20Sopenharmony_ci ccw->count = sizeof(struct dasd_psf_prssd_data); 15108c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_CC; 15118c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) prssdp; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci /* Read Subsystem Data - feature codes */ 15148c2ecf20Sopenharmony_ci features = (struct dasd_rssd_features *) (prssdp + 1); 15158c2ecf20Sopenharmony_ci memset(features, 0, sizeof(struct dasd_rssd_features)); 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci ccw++; 15188c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RSSD; 15198c2ecf20Sopenharmony_ci ccw->count = sizeof(struct dasd_rssd_features); 15208c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) features; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 15238c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 15248c2ecf20Sopenharmony_ci rc = dasd_sleep_on(cqr); 15258c2ecf20Sopenharmony_ci if (rc == 0) { 15268c2ecf20Sopenharmony_ci prssdp = (struct dasd_psf_prssd_data *) cqr->data; 15278c2ecf20Sopenharmony_ci features = (struct dasd_rssd_features *) (prssdp + 1); 15288c2ecf20Sopenharmony_ci memcpy(&private->features, features, 15298c2ecf20Sopenharmony_ci sizeof(struct dasd_rssd_features)); 15308c2ecf20Sopenharmony_ci } else 15318c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, "Reading device feature codes" 15328c2ecf20Sopenharmony_ci " failed with rc=%d\n", rc); 15338c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 15348c2ecf20Sopenharmony_ci return rc; 15358c2ecf20Sopenharmony_ci} 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci/* Read Volume Information - Volume Storage Query */ 15388c2ecf20Sopenharmony_cistatic int dasd_eckd_read_vol_info(struct dasd_device *device) 15398c2ecf20Sopenharmony_ci{ 15408c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 15418c2ecf20Sopenharmony_ci struct dasd_psf_prssd_data *prssdp; 15428c2ecf20Sopenharmony_ci struct dasd_rssd_vsq *vsq; 15438c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 15448c2ecf20Sopenharmony_ci struct ccw1 *ccw; 15458c2ecf20Sopenharmony_ci int useglobal; 15468c2ecf20Sopenharmony_ci int rc; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci /* This command cannot be executed on an alias device */ 15498c2ecf20Sopenharmony_ci if (private->uid.type == UA_BASE_PAV_ALIAS || 15508c2ecf20Sopenharmony_ci private->uid.type == UA_HYPER_PAV_ALIAS) 15518c2ecf20Sopenharmony_ci return 0; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci useglobal = 0; 15548c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2 /* PSF + RSSD */, 15558c2ecf20Sopenharmony_ci sizeof(*prssdp) + sizeof(*vsq), device, NULL); 15568c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 15578c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 15588c2ecf20Sopenharmony_ci "Could not allocate initialization request"); 15598c2ecf20Sopenharmony_ci mutex_lock(&dasd_vol_info_mutex); 15608c2ecf20Sopenharmony_ci useglobal = 1; 15618c2ecf20Sopenharmony_ci cqr = &dasd_vol_info_req->cqr; 15628c2ecf20Sopenharmony_ci memset(cqr, 0, sizeof(*cqr)); 15638c2ecf20Sopenharmony_ci memset(dasd_vol_info_req, 0, sizeof(*dasd_vol_info_req)); 15648c2ecf20Sopenharmony_ci cqr->cpaddr = &dasd_vol_info_req->ccw; 15658c2ecf20Sopenharmony_ci cqr->data = &dasd_vol_info_req->data; 15668c2ecf20Sopenharmony_ci cqr->magic = DASD_ECKD_MAGIC; 15678c2ecf20Sopenharmony_ci } 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci /* Prepare for Read Subsystem Data */ 15708c2ecf20Sopenharmony_ci prssdp = cqr->data; 15718c2ecf20Sopenharmony_ci prssdp->order = PSF_ORDER_PRSSD; 15728c2ecf20Sopenharmony_ci prssdp->suborder = PSF_SUBORDER_VSQ; /* Volume Storage Query */ 15738c2ecf20Sopenharmony_ci prssdp->lss = private->ned->ID; 15748c2ecf20Sopenharmony_ci prssdp->volume = private->ned->unit_addr; 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 15778c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 15788c2ecf20Sopenharmony_ci ccw->count = sizeof(*prssdp); 15798c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_CC; 15808c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t)prssdp; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci /* Read Subsystem Data - Volume Storage Query */ 15838c2ecf20Sopenharmony_ci vsq = (struct dasd_rssd_vsq *)(prssdp + 1); 15848c2ecf20Sopenharmony_ci memset(vsq, 0, sizeof(*vsq)); 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci ccw++; 15878c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RSSD; 15888c2ecf20Sopenharmony_ci ccw->count = sizeof(*vsq); 15898c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 15908c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t)vsq; 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 15938c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 15948c2ecf20Sopenharmony_ci cqr->startdev = device; 15958c2ecf20Sopenharmony_ci cqr->memdev = device; 15968c2ecf20Sopenharmony_ci cqr->block = NULL; 15978c2ecf20Sopenharmony_ci cqr->retries = 256; 15988c2ecf20Sopenharmony_ci cqr->expires = device->default_expires * HZ; 15998c2ecf20Sopenharmony_ci /* The command might not be supported. Suppress the error output */ 16008c2ecf20Sopenharmony_ci __set_bit(DASD_CQR_SUPPRESS_CR, &cqr->flags); 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci rc = dasd_sleep_on_interruptible(cqr); 16038c2ecf20Sopenharmony_ci if (rc == 0) { 16048c2ecf20Sopenharmony_ci memcpy(&private->vsq, vsq, sizeof(*vsq)); 16058c2ecf20Sopenharmony_ci } else { 16068c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 16078c2ecf20Sopenharmony_ci "Reading the volume storage information failed with rc=%d", rc); 16088c2ecf20Sopenharmony_ci } 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci if (useglobal) 16118c2ecf20Sopenharmony_ci mutex_unlock(&dasd_vol_info_mutex); 16128c2ecf20Sopenharmony_ci else 16138c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci return rc; 16168c2ecf20Sopenharmony_ci} 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_cistatic int dasd_eckd_is_ese(struct dasd_device *device) 16198c2ecf20Sopenharmony_ci{ 16208c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci return private->vsq.vol_info.ese; 16238c2ecf20Sopenharmony_ci} 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_cistatic int dasd_eckd_ext_pool_id(struct dasd_device *device) 16268c2ecf20Sopenharmony_ci{ 16278c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci return private->vsq.extent_pool_id; 16308c2ecf20Sopenharmony_ci} 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci/* 16338c2ecf20Sopenharmony_ci * This value represents the total amount of available space. As more space is 16348c2ecf20Sopenharmony_ci * allocated by ESE volumes, this value will decrease. 16358c2ecf20Sopenharmony_ci * The data for this value is therefore updated on any call. 16368c2ecf20Sopenharmony_ci */ 16378c2ecf20Sopenharmony_cistatic int dasd_eckd_space_configured(struct dasd_device *device) 16388c2ecf20Sopenharmony_ci{ 16398c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 16408c2ecf20Sopenharmony_ci int rc; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci rc = dasd_eckd_read_vol_info(device); 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci return rc ? : private->vsq.space_configured; 16458c2ecf20Sopenharmony_ci} 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci/* 16488c2ecf20Sopenharmony_ci * The value of space allocated by an ESE volume may have changed and is 16498c2ecf20Sopenharmony_ci * therefore updated on any call. 16508c2ecf20Sopenharmony_ci */ 16518c2ecf20Sopenharmony_cistatic int dasd_eckd_space_allocated(struct dasd_device *device) 16528c2ecf20Sopenharmony_ci{ 16538c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 16548c2ecf20Sopenharmony_ci int rc; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci rc = dasd_eckd_read_vol_info(device); 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci return rc ? : private->vsq.space_allocated; 16598c2ecf20Sopenharmony_ci} 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_cistatic int dasd_eckd_logical_capacity(struct dasd_device *device) 16628c2ecf20Sopenharmony_ci{ 16638c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci return private->vsq.logical_capacity; 16668c2ecf20Sopenharmony_ci} 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_cistatic void dasd_eckd_ext_pool_exhaust_work(struct work_struct *work) 16698c2ecf20Sopenharmony_ci{ 16708c2ecf20Sopenharmony_ci struct ext_pool_exhaust_work_data *data; 16718c2ecf20Sopenharmony_ci struct dasd_device *device; 16728c2ecf20Sopenharmony_ci struct dasd_device *base; 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci data = container_of(work, struct ext_pool_exhaust_work_data, worker); 16758c2ecf20Sopenharmony_ci device = data->device; 16768c2ecf20Sopenharmony_ci base = data->base; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci if (!base) 16798c2ecf20Sopenharmony_ci base = device; 16808c2ecf20Sopenharmony_ci if (dasd_eckd_space_configured(base) != 0) { 16818c2ecf20Sopenharmony_ci dasd_generic_space_avail(device); 16828c2ecf20Sopenharmony_ci } else { 16838c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, "No space left in the extent pool\n"); 16848c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", "out of space"); 16858c2ecf20Sopenharmony_ci } 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci dasd_put_device(device); 16888c2ecf20Sopenharmony_ci kfree(data); 16898c2ecf20Sopenharmony_ci} 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_cistatic int dasd_eckd_ext_pool_exhaust(struct dasd_device *device, 16928c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr) 16938c2ecf20Sopenharmony_ci{ 16948c2ecf20Sopenharmony_ci struct ext_pool_exhaust_work_data *data; 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_ATOMIC); 16978c2ecf20Sopenharmony_ci if (!data) 16988c2ecf20Sopenharmony_ci return -ENOMEM; 16998c2ecf20Sopenharmony_ci INIT_WORK(&data->worker, dasd_eckd_ext_pool_exhaust_work); 17008c2ecf20Sopenharmony_ci dasd_get_device(device); 17018c2ecf20Sopenharmony_ci data->device = device; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci if (cqr->block) 17048c2ecf20Sopenharmony_ci data->base = cqr->block->base; 17058c2ecf20Sopenharmony_ci else if (cqr->basedev) 17068c2ecf20Sopenharmony_ci data->base = cqr->basedev; 17078c2ecf20Sopenharmony_ci else 17088c2ecf20Sopenharmony_ci data->base = NULL; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci schedule_work(&data->worker); 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci return 0; 17138c2ecf20Sopenharmony_ci} 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_cistatic void dasd_eckd_cpy_ext_pool_data(struct dasd_device *device, 17168c2ecf20Sopenharmony_ci struct dasd_rssd_lcq *lcq) 17178c2ecf20Sopenharmony_ci{ 17188c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 17198c2ecf20Sopenharmony_ci int pool_id = dasd_eckd_ext_pool_id(device); 17208c2ecf20Sopenharmony_ci struct dasd_ext_pool_sum eps; 17218c2ecf20Sopenharmony_ci int i; 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci for (i = 0; i < lcq->pool_count; i++) { 17248c2ecf20Sopenharmony_ci eps = lcq->ext_pool_sum[i]; 17258c2ecf20Sopenharmony_ci if (eps.pool_id == pool_id) { 17268c2ecf20Sopenharmony_ci memcpy(&private->eps, &eps, 17278c2ecf20Sopenharmony_ci sizeof(struct dasd_ext_pool_sum)); 17288c2ecf20Sopenharmony_ci } 17298c2ecf20Sopenharmony_ci } 17308c2ecf20Sopenharmony_ci} 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci/* Read Extent Pool Information - Logical Configuration Query */ 17338c2ecf20Sopenharmony_cistatic int dasd_eckd_read_ext_pool_info(struct dasd_device *device) 17348c2ecf20Sopenharmony_ci{ 17358c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 17368c2ecf20Sopenharmony_ci struct dasd_psf_prssd_data *prssdp; 17378c2ecf20Sopenharmony_ci struct dasd_rssd_lcq *lcq; 17388c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 17398c2ecf20Sopenharmony_ci struct ccw1 *ccw; 17408c2ecf20Sopenharmony_ci int rc; 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci /* This command cannot be executed on an alias device */ 17438c2ecf20Sopenharmony_ci if (private->uid.type == UA_BASE_PAV_ALIAS || 17448c2ecf20Sopenharmony_ci private->uid.type == UA_HYPER_PAV_ALIAS) 17458c2ecf20Sopenharmony_ci return 0; 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2 /* PSF + RSSD */, 17488c2ecf20Sopenharmony_ci sizeof(*prssdp) + sizeof(*lcq), device, NULL); 17498c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 17508c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 17518c2ecf20Sopenharmony_ci "Could not allocate initialization request"); 17528c2ecf20Sopenharmony_ci return PTR_ERR(cqr); 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci /* Prepare for Read Subsystem Data */ 17568c2ecf20Sopenharmony_ci prssdp = cqr->data; 17578c2ecf20Sopenharmony_ci memset(prssdp, 0, sizeof(*prssdp)); 17588c2ecf20Sopenharmony_ci prssdp->order = PSF_ORDER_PRSSD; 17598c2ecf20Sopenharmony_ci prssdp->suborder = PSF_SUBORDER_LCQ; /* Logical Configuration Query */ 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 17628c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 17638c2ecf20Sopenharmony_ci ccw->count = sizeof(*prssdp); 17648c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_CC; 17658c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t)prssdp; 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci lcq = (struct dasd_rssd_lcq *)(prssdp + 1); 17688c2ecf20Sopenharmony_ci memset(lcq, 0, sizeof(*lcq)); 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci ccw++; 17718c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RSSD; 17728c2ecf20Sopenharmony_ci ccw->count = sizeof(*lcq); 17738c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 17748c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t)lcq; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 17778c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 17788c2ecf20Sopenharmony_ci cqr->startdev = device; 17798c2ecf20Sopenharmony_ci cqr->memdev = device; 17808c2ecf20Sopenharmony_ci cqr->block = NULL; 17818c2ecf20Sopenharmony_ci cqr->retries = 256; 17828c2ecf20Sopenharmony_ci cqr->expires = device->default_expires * HZ; 17838c2ecf20Sopenharmony_ci /* The command might not be supported. Suppress the error output */ 17848c2ecf20Sopenharmony_ci __set_bit(DASD_CQR_SUPPRESS_CR, &cqr->flags); 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci rc = dasd_sleep_on_interruptible(cqr); 17878c2ecf20Sopenharmony_ci if (rc == 0) { 17888c2ecf20Sopenharmony_ci dasd_eckd_cpy_ext_pool_data(device, lcq); 17898c2ecf20Sopenharmony_ci } else { 17908c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 17918c2ecf20Sopenharmony_ci "Reading the logical configuration failed with rc=%d", rc); 17928c2ecf20Sopenharmony_ci } 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci return rc; 17978c2ecf20Sopenharmony_ci} 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci/* 18008c2ecf20Sopenharmony_ci * Depending on the device type, the extent size is specified either as 18018c2ecf20Sopenharmony_ci * cylinders per extent (CKD) or size per extent (FBA) 18028c2ecf20Sopenharmony_ci * A 1GB size corresponds to 1113cyl, and 16MB to 21cyl. 18038c2ecf20Sopenharmony_ci */ 18048c2ecf20Sopenharmony_cistatic int dasd_eckd_ext_size(struct dasd_device *device) 18058c2ecf20Sopenharmony_ci{ 18068c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 18078c2ecf20Sopenharmony_ci struct dasd_ext_pool_sum eps = private->eps; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci if (!eps.flags.extent_size_valid) 18108c2ecf20Sopenharmony_ci return 0; 18118c2ecf20Sopenharmony_ci if (eps.extent_size.size_1G) 18128c2ecf20Sopenharmony_ci return 1113; 18138c2ecf20Sopenharmony_ci if (eps.extent_size.size_16M) 18148c2ecf20Sopenharmony_ci return 21; 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci return 0; 18178c2ecf20Sopenharmony_ci} 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_cistatic int dasd_eckd_ext_pool_warn_thrshld(struct dasd_device *device) 18208c2ecf20Sopenharmony_ci{ 18218c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci return private->eps.warn_thrshld; 18248c2ecf20Sopenharmony_ci} 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_cistatic int dasd_eckd_ext_pool_cap_at_warnlevel(struct dasd_device *device) 18278c2ecf20Sopenharmony_ci{ 18288c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci return private->eps.flags.capacity_at_warnlevel; 18318c2ecf20Sopenharmony_ci} 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci/* 18348c2ecf20Sopenharmony_ci * Extent Pool out of space 18358c2ecf20Sopenharmony_ci */ 18368c2ecf20Sopenharmony_cistatic int dasd_eckd_ext_pool_oos(struct dasd_device *device) 18378c2ecf20Sopenharmony_ci{ 18388c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci return private->eps.flags.pool_oos; 18418c2ecf20Sopenharmony_ci} 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci/* 18448c2ecf20Sopenharmony_ci * Build CP for Perform Subsystem Function - SSC. 18458c2ecf20Sopenharmony_ci */ 18468c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device, 18478c2ecf20Sopenharmony_ci int enable_pav) 18488c2ecf20Sopenharmony_ci{ 18498c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 18508c2ecf20Sopenharmony_ci struct dasd_psf_ssc_data *psf_ssc_data; 18518c2ecf20Sopenharmony_ci struct ccw1 *ccw; 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ , 18548c2ecf20Sopenharmony_ci sizeof(struct dasd_psf_ssc_data), 18558c2ecf20Sopenharmony_ci device, NULL); 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 18588c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 18598c2ecf20Sopenharmony_ci "Could not allocate PSF-SSC request"); 18608c2ecf20Sopenharmony_ci return cqr; 18618c2ecf20Sopenharmony_ci } 18628c2ecf20Sopenharmony_ci psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data; 18638c2ecf20Sopenharmony_ci psf_ssc_data->order = PSF_ORDER_SSC; 18648c2ecf20Sopenharmony_ci psf_ssc_data->suborder = 0xc0; 18658c2ecf20Sopenharmony_ci if (enable_pav) { 18668c2ecf20Sopenharmony_ci psf_ssc_data->suborder |= 0x08; 18678c2ecf20Sopenharmony_ci psf_ssc_data->reserved[0] = 0x88; 18688c2ecf20Sopenharmony_ci } 18698c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 18708c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 18718c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t)psf_ssc_data; 18728c2ecf20Sopenharmony_ci ccw->count = 66; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci cqr->startdev = device; 18758c2ecf20Sopenharmony_ci cqr->memdev = device; 18768c2ecf20Sopenharmony_ci cqr->block = NULL; 18778c2ecf20Sopenharmony_ci cqr->retries = 256; 18788c2ecf20Sopenharmony_ci cqr->expires = 10*HZ; 18798c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 18808c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 18818c2ecf20Sopenharmony_ci return cqr; 18828c2ecf20Sopenharmony_ci} 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci/* 18858c2ecf20Sopenharmony_ci * Perform Subsystem Function. 18868c2ecf20Sopenharmony_ci * It is necessary to trigger CIO for channel revalidation since this 18878c2ecf20Sopenharmony_ci * call might change behaviour of DASD devices. 18888c2ecf20Sopenharmony_ci */ 18898c2ecf20Sopenharmony_cistatic int 18908c2ecf20Sopenharmony_cidasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav, 18918c2ecf20Sopenharmony_ci unsigned long flags) 18928c2ecf20Sopenharmony_ci{ 18938c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 18948c2ecf20Sopenharmony_ci int rc; 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci cqr = dasd_eckd_build_psf_ssc(device, enable_pav); 18978c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) 18988c2ecf20Sopenharmony_ci return PTR_ERR(cqr); 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci /* 19018c2ecf20Sopenharmony_ci * set flags e.g. turn on failfast, to prevent blocking 19028c2ecf20Sopenharmony_ci * the calling function should handle failed requests 19038c2ecf20Sopenharmony_ci */ 19048c2ecf20Sopenharmony_ci cqr->flags |= flags; 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci rc = dasd_sleep_on(cqr); 19078c2ecf20Sopenharmony_ci if (!rc) 19088c2ecf20Sopenharmony_ci /* trigger CIO to reprobe devices */ 19098c2ecf20Sopenharmony_ci css_schedule_reprobe(); 19108c2ecf20Sopenharmony_ci else if (cqr->intrc == -EAGAIN) 19118c2ecf20Sopenharmony_ci rc = -EAGAIN; 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 19148c2ecf20Sopenharmony_ci return rc; 19158c2ecf20Sopenharmony_ci} 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci/* 19188c2ecf20Sopenharmony_ci * Valide storage server of current device. 19198c2ecf20Sopenharmony_ci */ 19208c2ecf20Sopenharmony_cistatic int dasd_eckd_validate_server(struct dasd_device *device, 19218c2ecf20Sopenharmony_ci unsigned long flags) 19228c2ecf20Sopenharmony_ci{ 19238c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 19248c2ecf20Sopenharmony_ci int enable_pav, rc; 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci if (private->uid.type == UA_BASE_PAV_ALIAS || 19278c2ecf20Sopenharmony_ci private->uid.type == UA_HYPER_PAV_ALIAS) 19288c2ecf20Sopenharmony_ci return 0; 19298c2ecf20Sopenharmony_ci if (dasd_nopav || MACHINE_IS_VM) 19308c2ecf20Sopenharmony_ci enable_pav = 0; 19318c2ecf20Sopenharmony_ci else 19328c2ecf20Sopenharmony_ci enable_pav = 1; 19338c2ecf20Sopenharmony_ci rc = dasd_eckd_psf_ssc(device, enable_pav, flags); 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci /* may be requested feature is not available on server, 19368c2ecf20Sopenharmony_ci * therefore just report error and go ahead */ 19378c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x " 19388c2ecf20Sopenharmony_ci "returned rc=%d", private->uid.ssid, rc); 19398c2ecf20Sopenharmony_ci return rc; 19408c2ecf20Sopenharmony_ci} 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci/* 19438c2ecf20Sopenharmony_ci * worker to do a validate server in case of a lost pathgroup 19448c2ecf20Sopenharmony_ci */ 19458c2ecf20Sopenharmony_cistatic void dasd_eckd_do_validate_server(struct work_struct *work) 19468c2ecf20Sopenharmony_ci{ 19478c2ecf20Sopenharmony_ci struct dasd_device *device = container_of(work, struct dasd_device, 19488c2ecf20Sopenharmony_ci kick_validate); 19498c2ecf20Sopenharmony_ci unsigned long flags = 0; 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &flags); 19528c2ecf20Sopenharmony_ci if (dasd_eckd_validate_server(device, flags) 19538c2ecf20Sopenharmony_ci == -EAGAIN) { 19548c2ecf20Sopenharmony_ci /* schedule worker again if failed */ 19558c2ecf20Sopenharmony_ci schedule_work(&device->kick_validate); 19568c2ecf20Sopenharmony_ci return; 19578c2ecf20Sopenharmony_ci } 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci dasd_put_device(device); 19608c2ecf20Sopenharmony_ci} 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_cistatic void dasd_eckd_kick_validate_server(struct dasd_device *device) 19638c2ecf20Sopenharmony_ci{ 19648c2ecf20Sopenharmony_ci dasd_get_device(device); 19658c2ecf20Sopenharmony_ci /* exit if device not online or in offline processing */ 19668c2ecf20Sopenharmony_ci if (test_bit(DASD_FLAG_OFFLINE, &device->flags) || 19678c2ecf20Sopenharmony_ci device->state < DASD_STATE_ONLINE) { 19688c2ecf20Sopenharmony_ci dasd_put_device(device); 19698c2ecf20Sopenharmony_ci return; 19708c2ecf20Sopenharmony_ci } 19718c2ecf20Sopenharmony_ci /* queue call to do_validate_server to the kernel event daemon. */ 19728c2ecf20Sopenharmony_ci if (!schedule_work(&device->kick_validate)) 19738c2ecf20Sopenharmony_ci dasd_put_device(device); 19748c2ecf20Sopenharmony_ci} 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci/* 19778c2ecf20Sopenharmony_ci * Check device characteristics. 19788c2ecf20Sopenharmony_ci * If the device is accessible using ECKD discipline, the device is enabled. 19798c2ecf20Sopenharmony_ci */ 19808c2ecf20Sopenharmony_cistatic int 19818c2ecf20Sopenharmony_cidasd_eckd_check_characteristics(struct dasd_device *device) 19828c2ecf20Sopenharmony_ci{ 19838c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 19848c2ecf20Sopenharmony_ci struct dasd_block *block; 19858c2ecf20Sopenharmony_ci struct dasd_uid temp_uid; 19868c2ecf20Sopenharmony_ci int rc, i; 19878c2ecf20Sopenharmony_ci int readonly; 19888c2ecf20Sopenharmony_ci unsigned long value; 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci /* setup work queue for validate server*/ 19918c2ecf20Sopenharmony_ci INIT_WORK(&device->kick_validate, dasd_eckd_do_validate_server); 19928c2ecf20Sopenharmony_ci /* setup work queue for summary unit check */ 19938c2ecf20Sopenharmony_ci INIT_WORK(&device->suc_work, dasd_alias_handle_summary_unit_check); 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci if (!ccw_device_is_pathgroup(device->cdev)) { 19968c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, 19978c2ecf20Sopenharmony_ci "A channel path group could not be established\n"); 19988c2ecf20Sopenharmony_ci return -EIO; 19998c2ecf20Sopenharmony_ci } 20008c2ecf20Sopenharmony_ci if (!ccw_device_is_multipath(device->cdev)) { 20018c2ecf20Sopenharmony_ci dev_info(&device->cdev->dev, 20028c2ecf20Sopenharmony_ci "The DASD is not operating in multipath mode\n"); 20038c2ecf20Sopenharmony_ci } 20048c2ecf20Sopenharmony_ci if (!private) { 20058c2ecf20Sopenharmony_ci private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA); 20068c2ecf20Sopenharmony_ci if (!private) { 20078c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, 20088c2ecf20Sopenharmony_ci "Allocating memory for private DASD data " 20098c2ecf20Sopenharmony_ci "failed\n"); 20108c2ecf20Sopenharmony_ci return -ENOMEM; 20118c2ecf20Sopenharmony_ci } 20128c2ecf20Sopenharmony_ci device->private = private; 20138c2ecf20Sopenharmony_ci } else { 20148c2ecf20Sopenharmony_ci memset(private, 0, sizeof(*private)); 20158c2ecf20Sopenharmony_ci } 20168c2ecf20Sopenharmony_ci /* Invalidate status of initial analysis. */ 20178c2ecf20Sopenharmony_ci private->init_cqr_status = -1; 20188c2ecf20Sopenharmony_ci /* Set default cache operations. */ 20198c2ecf20Sopenharmony_ci private->attrib.operation = DASD_NORMAL_CACHE; 20208c2ecf20Sopenharmony_ci private->attrib.nr_cyl = 0; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci /* Read Configuration Data */ 20238c2ecf20Sopenharmony_ci rc = dasd_eckd_read_conf(device); 20248c2ecf20Sopenharmony_ci if (rc) 20258c2ecf20Sopenharmony_ci goto out_err1; 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci /* set some default values */ 20288c2ecf20Sopenharmony_ci device->default_expires = DASD_EXPIRES; 20298c2ecf20Sopenharmony_ci device->default_retries = DASD_RETRIES; 20308c2ecf20Sopenharmony_ci device->path_thrhld = DASD_ECKD_PATH_THRHLD; 20318c2ecf20Sopenharmony_ci device->path_interval = DASD_ECKD_PATH_INTERVAL; 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci if (private->gneq) { 20348c2ecf20Sopenharmony_ci value = 1; 20358c2ecf20Sopenharmony_ci for (i = 0; i < private->gneq->timeout.value; i++) 20368c2ecf20Sopenharmony_ci value = 10 * value; 20378c2ecf20Sopenharmony_ci value = value * private->gneq->timeout.number; 20388c2ecf20Sopenharmony_ci /* do not accept useless values */ 20398c2ecf20Sopenharmony_ci if (value != 0 && value <= DASD_EXPIRES_MAX) 20408c2ecf20Sopenharmony_ci device->default_expires = value; 20418c2ecf20Sopenharmony_ci } 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci dasd_eckd_get_uid(device, &temp_uid); 20448c2ecf20Sopenharmony_ci if (temp_uid.type == UA_BASE_DEVICE) { 20458c2ecf20Sopenharmony_ci block = dasd_alloc_block(); 20468c2ecf20Sopenharmony_ci if (IS_ERR(block)) { 20478c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 20488c2ecf20Sopenharmony_ci "could not allocate dasd " 20498c2ecf20Sopenharmony_ci "block structure"); 20508c2ecf20Sopenharmony_ci rc = PTR_ERR(block); 20518c2ecf20Sopenharmony_ci goto out_err1; 20528c2ecf20Sopenharmony_ci } 20538c2ecf20Sopenharmony_ci device->block = block; 20548c2ecf20Sopenharmony_ci block->base = device; 20558c2ecf20Sopenharmony_ci } 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci /* register lcu with alias handling, enable PAV */ 20588c2ecf20Sopenharmony_ci rc = dasd_alias_make_device_known_to_lcu(device); 20598c2ecf20Sopenharmony_ci if (rc) 20608c2ecf20Sopenharmony_ci goto out_err2; 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci dasd_eckd_validate_server(device, 0); 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci /* device may report different configuration data after LCU setup */ 20658c2ecf20Sopenharmony_ci rc = dasd_eckd_read_conf(device); 20668c2ecf20Sopenharmony_ci if (rc) 20678c2ecf20Sopenharmony_ci goto out_err3; 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci /* Read Feature Codes */ 20708c2ecf20Sopenharmony_ci dasd_eckd_read_features(device); 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci /* Read Volume Information */ 20738c2ecf20Sopenharmony_ci dasd_eckd_read_vol_info(device); 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci /* Read Extent Pool Information */ 20768c2ecf20Sopenharmony_ci dasd_eckd_read_ext_pool_info(device); 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci /* Read Device Characteristics */ 20798c2ecf20Sopenharmony_ci rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC, 20808c2ecf20Sopenharmony_ci &private->rdc_data, 64); 20818c2ecf20Sopenharmony_ci if (rc) { 20828c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 20838c2ecf20Sopenharmony_ci "Read device characteristic failed, rc=%d", rc); 20848c2ecf20Sopenharmony_ci goto out_err3; 20858c2ecf20Sopenharmony_ci } 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci if ((device->features & DASD_FEATURE_USERAW) && 20888c2ecf20Sopenharmony_ci !(private->rdc_data.facilities.RT_in_LR)) { 20898c2ecf20Sopenharmony_ci dev_err(&device->cdev->dev, "The storage server does not " 20908c2ecf20Sopenharmony_ci "support raw-track access\n"); 20918c2ecf20Sopenharmony_ci rc = -EINVAL; 20928c2ecf20Sopenharmony_ci goto out_err3; 20938c2ecf20Sopenharmony_ci } 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci /* find the valid cylinder size */ 20968c2ecf20Sopenharmony_ci if (private->rdc_data.no_cyl == LV_COMPAT_CYL && 20978c2ecf20Sopenharmony_ci private->rdc_data.long_no_cyl) 20988c2ecf20Sopenharmony_ci private->real_cyl = private->rdc_data.long_no_cyl; 20998c2ecf20Sopenharmony_ci else 21008c2ecf20Sopenharmony_ci private->real_cyl = private->rdc_data.no_cyl; 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci private->fcx_max_data = get_fcx_max_data(device); 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci readonly = dasd_device_is_ro(device); 21058c2ecf20Sopenharmony_ci if (readonly) 21068c2ecf20Sopenharmony_ci set_bit(DASD_FLAG_DEVICE_RO, &device->flags); 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci dev_info(&device->cdev->dev, "New DASD %04X/%02X (CU %04X/%02X) " 21098c2ecf20Sopenharmony_ci "with %d cylinders, %d heads, %d sectors%s\n", 21108c2ecf20Sopenharmony_ci private->rdc_data.dev_type, 21118c2ecf20Sopenharmony_ci private->rdc_data.dev_model, 21128c2ecf20Sopenharmony_ci private->rdc_data.cu_type, 21138c2ecf20Sopenharmony_ci private->rdc_data.cu_model.model, 21148c2ecf20Sopenharmony_ci private->real_cyl, 21158c2ecf20Sopenharmony_ci private->rdc_data.trk_per_cyl, 21168c2ecf20Sopenharmony_ci private->rdc_data.sec_per_trk, 21178c2ecf20Sopenharmony_ci readonly ? ", read-only device" : ""); 21188c2ecf20Sopenharmony_ci return 0; 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ciout_err3: 21218c2ecf20Sopenharmony_ci dasd_alias_disconnect_device_from_lcu(device); 21228c2ecf20Sopenharmony_ciout_err2: 21238c2ecf20Sopenharmony_ci dasd_free_block(device->block); 21248c2ecf20Sopenharmony_ci device->block = NULL; 21258c2ecf20Sopenharmony_ciout_err1: 21268c2ecf20Sopenharmony_ci dasd_eckd_clear_conf_data(device); 21278c2ecf20Sopenharmony_ci kfree(device->private); 21288c2ecf20Sopenharmony_ci device->private = NULL; 21298c2ecf20Sopenharmony_ci return rc; 21308c2ecf20Sopenharmony_ci} 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_cistatic void dasd_eckd_uncheck_device(struct dasd_device *device) 21338c2ecf20Sopenharmony_ci{ 21348c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci if (!private) 21378c2ecf20Sopenharmony_ci return; 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci dasd_alias_disconnect_device_from_lcu(device); 21408c2ecf20Sopenharmony_ci private->ned = NULL; 21418c2ecf20Sopenharmony_ci private->sneq = NULL; 21428c2ecf20Sopenharmony_ci private->vdsneq = NULL; 21438c2ecf20Sopenharmony_ci private->gneq = NULL; 21448c2ecf20Sopenharmony_ci dasd_eckd_clear_conf_data(device); 21458c2ecf20Sopenharmony_ci} 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_cistatic struct dasd_ccw_req * 21488c2ecf20Sopenharmony_cidasd_eckd_analysis_ccw(struct dasd_device *device) 21498c2ecf20Sopenharmony_ci{ 21508c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 21518c2ecf20Sopenharmony_ci struct eckd_count *count_data; 21528c2ecf20Sopenharmony_ci struct LO_eckd_data *LO_data; 21538c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 21548c2ecf20Sopenharmony_ci struct ccw1 *ccw; 21558c2ecf20Sopenharmony_ci int cplength, datasize; 21568c2ecf20Sopenharmony_ci int i; 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci cplength = 8; 21598c2ecf20Sopenharmony_ci datasize = sizeof(struct DE_eckd_data) + 2*sizeof(struct LO_eckd_data); 21608c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device, 21618c2ecf20Sopenharmony_ci NULL); 21628c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) 21638c2ecf20Sopenharmony_ci return cqr; 21648c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 21658c2ecf20Sopenharmony_ci /* Define extent for the first 2 tracks. */ 21668c2ecf20Sopenharmony_ci define_extent(ccw++, cqr->data, 0, 1, 21678c2ecf20Sopenharmony_ci DASD_ECKD_CCW_READ_COUNT, device, 0); 21688c2ecf20Sopenharmony_ci LO_data = cqr->data + sizeof(struct DE_eckd_data); 21698c2ecf20Sopenharmony_ci /* Locate record for the first 4 records on track 0. */ 21708c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 21718c2ecf20Sopenharmony_ci locate_record(ccw++, LO_data++, 0, 0, 4, 21728c2ecf20Sopenharmony_ci DASD_ECKD_CCW_READ_COUNT, device, 0); 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci count_data = private->count_area; 21758c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 21768c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 21778c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT; 21788c2ecf20Sopenharmony_ci ccw->flags = 0; 21798c2ecf20Sopenharmony_ci ccw->count = 8; 21808c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) count_data; 21818c2ecf20Sopenharmony_ci ccw++; 21828c2ecf20Sopenharmony_ci count_data++; 21838c2ecf20Sopenharmony_ci } 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci /* Locate record for the first record on track 1. */ 21868c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 21878c2ecf20Sopenharmony_ci locate_record(ccw++, LO_data++, 1, 0, 1, 21888c2ecf20Sopenharmony_ci DASD_ECKD_CCW_READ_COUNT, device, 0); 21898c2ecf20Sopenharmony_ci /* Read count ccw. */ 21908c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 21918c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT; 21928c2ecf20Sopenharmony_ci ccw->flags = 0; 21938c2ecf20Sopenharmony_ci ccw->count = 8; 21948c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) count_data; 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci cqr->block = NULL; 21978c2ecf20Sopenharmony_ci cqr->startdev = device; 21988c2ecf20Sopenharmony_ci cqr->memdev = device; 21998c2ecf20Sopenharmony_ci cqr->retries = 255; 22008c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 22018c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 22028c2ecf20Sopenharmony_ci /* Set flags to suppress output for expected errors */ 22038c2ecf20Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags); 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci return cqr; 22068c2ecf20Sopenharmony_ci} 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci/* differentiate between 'no record found' and any other error */ 22098c2ecf20Sopenharmony_cistatic int dasd_eckd_analysis_evaluation(struct dasd_ccw_req *init_cqr) 22108c2ecf20Sopenharmony_ci{ 22118c2ecf20Sopenharmony_ci char *sense; 22128c2ecf20Sopenharmony_ci if (init_cqr->status == DASD_CQR_DONE) 22138c2ecf20Sopenharmony_ci return INIT_CQR_OK; 22148c2ecf20Sopenharmony_ci else if (init_cqr->status == DASD_CQR_NEED_ERP || 22158c2ecf20Sopenharmony_ci init_cqr->status == DASD_CQR_FAILED) { 22168c2ecf20Sopenharmony_ci sense = dasd_get_sense(&init_cqr->irb); 22178c2ecf20Sopenharmony_ci if (sense && (sense[1] & SNS1_NO_REC_FOUND)) 22188c2ecf20Sopenharmony_ci return INIT_CQR_UNFORMATTED; 22198c2ecf20Sopenharmony_ci else 22208c2ecf20Sopenharmony_ci return INIT_CQR_ERROR; 22218c2ecf20Sopenharmony_ci } else 22228c2ecf20Sopenharmony_ci return INIT_CQR_ERROR; 22238c2ecf20Sopenharmony_ci} 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci/* 22268c2ecf20Sopenharmony_ci * This is the callback function for the init_analysis cqr. It saves 22278c2ecf20Sopenharmony_ci * the status of the initial analysis ccw before it frees it and kicks 22288c2ecf20Sopenharmony_ci * the device to continue the startup sequence. This will call 22298c2ecf20Sopenharmony_ci * dasd_eckd_do_analysis again (if the devices has not been marked 22308c2ecf20Sopenharmony_ci * for deletion in the meantime). 22318c2ecf20Sopenharmony_ci */ 22328c2ecf20Sopenharmony_cistatic void dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, 22338c2ecf20Sopenharmony_ci void *data) 22348c2ecf20Sopenharmony_ci{ 22358c2ecf20Sopenharmony_ci struct dasd_device *device = init_cqr->startdev; 22368c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci private->init_cqr_status = dasd_eckd_analysis_evaluation(init_cqr); 22398c2ecf20Sopenharmony_ci dasd_sfree_request(init_cqr, device); 22408c2ecf20Sopenharmony_ci dasd_kick_device(device); 22418c2ecf20Sopenharmony_ci} 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_cistatic int dasd_eckd_start_analysis(struct dasd_block *block) 22448c2ecf20Sopenharmony_ci{ 22458c2ecf20Sopenharmony_ci struct dasd_ccw_req *init_cqr; 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci init_cqr = dasd_eckd_analysis_ccw(block->base); 22488c2ecf20Sopenharmony_ci if (IS_ERR(init_cqr)) 22498c2ecf20Sopenharmony_ci return PTR_ERR(init_cqr); 22508c2ecf20Sopenharmony_ci init_cqr->callback = dasd_eckd_analysis_callback; 22518c2ecf20Sopenharmony_ci init_cqr->callback_data = NULL; 22528c2ecf20Sopenharmony_ci init_cqr->expires = 5*HZ; 22538c2ecf20Sopenharmony_ci /* first try without ERP, so we can later handle unformatted 22548c2ecf20Sopenharmony_ci * devices as special case 22558c2ecf20Sopenharmony_ci */ 22568c2ecf20Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &init_cqr->flags); 22578c2ecf20Sopenharmony_ci init_cqr->retries = 0; 22588c2ecf20Sopenharmony_ci dasd_add_request_head(init_cqr); 22598c2ecf20Sopenharmony_ci return -EAGAIN; 22608c2ecf20Sopenharmony_ci} 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_cistatic int dasd_eckd_end_analysis(struct dasd_block *block) 22638c2ecf20Sopenharmony_ci{ 22648c2ecf20Sopenharmony_ci struct dasd_device *device = block->base; 22658c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 22668c2ecf20Sopenharmony_ci struct eckd_count *count_area; 22678c2ecf20Sopenharmony_ci unsigned int sb, blk_per_trk; 22688c2ecf20Sopenharmony_ci int status, i; 22698c2ecf20Sopenharmony_ci struct dasd_ccw_req *init_cqr; 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_ci status = private->init_cqr_status; 22728c2ecf20Sopenharmony_ci private->init_cqr_status = -1; 22738c2ecf20Sopenharmony_ci if (status == INIT_CQR_ERROR) { 22748c2ecf20Sopenharmony_ci /* try again, this time with full ERP */ 22758c2ecf20Sopenharmony_ci init_cqr = dasd_eckd_analysis_ccw(device); 22768c2ecf20Sopenharmony_ci dasd_sleep_on(init_cqr); 22778c2ecf20Sopenharmony_ci status = dasd_eckd_analysis_evaluation(init_cqr); 22788c2ecf20Sopenharmony_ci dasd_sfree_request(init_cqr, device); 22798c2ecf20Sopenharmony_ci } 22808c2ecf20Sopenharmony_ci 22818c2ecf20Sopenharmony_ci if (device->features & DASD_FEATURE_USERAW) { 22828c2ecf20Sopenharmony_ci block->bp_block = DASD_RAW_BLOCKSIZE; 22838c2ecf20Sopenharmony_ci blk_per_trk = DASD_RAW_BLOCK_PER_TRACK; 22848c2ecf20Sopenharmony_ci block->s2b_shift = 3; 22858c2ecf20Sopenharmony_ci goto raw; 22868c2ecf20Sopenharmony_ci } 22878c2ecf20Sopenharmony_ci 22888c2ecf20Sopenharmony_ci if (status == INIT_CQR_UNFORMATTED) { 22898c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, "The DASD is not formatted\n"); 22908c2ecf20Sopenharmony_ci return -EMEDIUMTYPE; 22918c2ecf20Sopenharmony_ci } else if (status == INIT_CQR_ERROR) { 22928c2ecf20Sopenharmony_ci dev_err(&device->cdev->dev, 22938c2ecf20Sopenharmony_ci "Detecting the DASD disk layout failed because " 22948c2ecf20Sopenharmony_ci "of an I/O error\n"); 22958c2ecf20Sopenharmony_ci return -EIO; 22968c2ecf20Sopenharmony_ci } 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci private->uses_cdl = 1; 22998c2ecf20Sopenharmony_ci /* Check Track 0 for Compatible Disk Layout */ 23008c2ecf20Sopenharmony_ci count_area = NULL; 23018c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 23028c2ecf20Sopenharmony_ci if (private->count_area[i].kl != 4 || 23038c2ecf20Sopenharmony_ci private->count_area[i].dl != dasd_eckd_cdl_reclen(i) - 4 || 23048c2ecf20Sopenharmony_ci private->count_area[i].cyl != 0 || 23058c2ecf20Sopenharmony_ci private->count_area[i].head != count_area_head[i] || 23068c2ecf20Sopenharmony_ci private->count_area[i].record != count_area_rec[i]) { 23078c2ecf20Sopenharmony_ci private->uses_cdl = 0; 23088c2ecf20Sopenharmony_ci break; 23098c2ecf20Sopenharmony_ci } 23108c2ecf20Sopenharmony_ci } 23118c2ecf20Sopenharmony_ci if (i == 3) 23128c2ecf20Sopenharmony_ci count_area = &private->count_area[3]; 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci if (private->uses_cdl == 0) { 23158c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) { 23168c2ecf20Sopenharmony_ci if ((private->count_area[i].kl != 0) || 23178c2ecf20Sopenharmony_ci (private->count_area[i].dl != 23188c2ecf20Sopenharmony_ci private->count_area[0].dl) || 23198c2ecf20Sopenharmony_ci private->count_area[i].cyl != 0 || 23208c2ecf20Sopenharmony_ci private->count_area[i].head != count_area_head[i] || 23218c2ecf20Sopenharmony_ci private->count_area[i].record != count_area_rec[i]) 23228c2ecf20Sopenharmony_ci break; 23238c2ecf20Sopenharmony_ci } 23248c2ecf20Sopenharmony_ci if (i == 5) 23258c2ecf20Sopenharmony_ci count_area = &private->count_area[0]; 23268c2ecf20Sopenharmony_ci } else { 23278c2ecf20Sopenharmony_ci if (private->count_area[3].record == 1) 23288c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, 23298c2ecf20Sopenharmony_ci "Track 0 has no records following the VTOC\n"); 23308c2ecf20Sopenharmony_ci } 23318c2ecf20Sopenharmony_ci 23328c2ecf20Sopenharmony_ci if (count_area != NULL && count_area->kl == 0) { 23338c2ecf20Sopenharmony_ci /* we found notthing violating our disk layout */ 23348c2ecf20Sopenharmony_ci if (dasd_check_blocksize(count_area->dl) == 0) 23358c2ecf20Sopenharmony_ci block->bp_block = count_area->dl; 23368c2ecf20Sopenharmony_ci } 23378c2ecf20Sopenharmony_ci if (block->bp_block == 0) { 23388c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, 23398c2ecf20Sopenharmony_ci "The disk layout of the DASD is not supported\n"); 23408c2ecf20Sopenharmony_ci return -EMEDIUMTYPE; 23418c2ecf20Sopenharmony_ci } 23428c2ecf20Sopenharmony_ci block->s2b_shift = 0; /* bits to shift 512 to get a block */ 23438c2ecf20Sopenharmony_ci for (sb = 512; sb < block->bp_block; sb = sb << 1) 23448c2ecf20Sopenharmony_ci block->s2b_shift++; 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block); 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ciraw: 23498c2ecf20Sopenharmony_ci block->blocks = ((unsigned long) private->real_cyl * 23508c2ecf20Sopenharmony_ci private->rdc_data.trk_per_cyl * 23518c2ecf20Sopenharmony_ci blk_per_trk); 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_ci dev_info(&device->cdev->dev, 23548c2ecf20Sopenharmony_ci "DASD with %u KB/block, %lu KB total size, %u KB/track, " 23558c2ecf20Sopenharmony_ci "%s\n", (block->bp_block >> 10), 23568c2ecf20Sopenharmony_ci (((unsigned long) private->real_cyl * 23578c2ecf20Sopenharmony_ci private->rdc_data.trk_per_cyl * 23588c2ecf20Sopenharmony_ci blk_per_trk * (block->bp_block >> 9)) >> 1), 23598c2ecf20Sopenharmony_ci ((blk_per_trk * block->bp_block) >> 10), 23608c2ecf20Sopenharmony_ci private->uses_cdl ? 23618c2ecf20Sopenharmony_ci "compatible disk layout" : "linux disk layout"); 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_ci return 0; 23648c2ecf20Sopenharmony_ci} 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_cistatic int dasd_eckd_do_analysis(struct dasd_block *block) 23678c2ecf20Sopenharmony_ci{ 23688c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = block->base->private; 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci if (private->init_cqr_status < 0) 23718c2ecf20Sopenharmony_ci return dasd_eckd_start_analysis(block); 23728c2ecf20Sopenharmony_ci else 23738c2ecf20Sopenharmony_ci return dasd_eckd_end_analysis(block); 23748c2ecf20Sopenharmony_ci} 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_cistatic int dasd_eckd_basic_to_ready(struct dasd_device *device) 23778c2ecf20Sopenharmony_ci{ 23788c2ecf20Sopenharmony_ci return dasd_alias_add_device(device); 23798c2ecf20Sopenharmony_ci}; 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_cistatic int dasd_eckd_online_to_ready(struct dasd_device *device) 23828c2ecf20Sopenharmony_ci{ 23838c2ecf20Sopenharmony_ci if (cancel_work_sync(&device->reload_device)) 23848c2ecf20Sopenharmony_ci dasd_put_device(device); 23858c2ecf20Sopenharmony_ci if (cancel_work_sync(&device->kick_validate)) 23868c2ecf20Sopenharmony_ci dasd_put_device(device); 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_ci return 0; 23898c2ecf20Sopenharmony_ci}; 23908c2ecf20Sopenharmony_ci 23918c2ecf20Sopenharmony_cistatic int dasd_eckd_basic_to_known(struct dasd_device *device) 23928c2ecf20Sopenharmony_ci{ 23938c2ecf20Sopenharmony_ci return dasd_alias_remove_device(device); 23948c2ecf20Sopenharmony_ci}; 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_cistatic int 23978c2ecf20Sopenharmony_cidasd_eckd_fill_geometry(struct dasd_block *block, struct hd_geometry *geo) 23988c2ecf20Sopenharmony_ci{ 23998c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = block->base->private; 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci if (dasd_check_blocksize(block->bp_block) == 0) { 24028c2ecf20Sopenharmony_ci geo->sectors = recs_per_track(&private->rdc_data, 24038c2ecf20Sopenharmony_ci 0, block->bp_block); 24048c2ecf20Sopenharmony_ci } 24058c2ecf20Sopenharmony_ci geo->cylinders = private->rdc_data.no_cyl; 24068c2ecf20Sopenharmony_ci geo->heads = private->rdc_data.trk_per_cyl; 24078c2ecf20Sopenharmony_ci return 0; 24088c2ecf20Sopenharmony_ci} 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci/* 24118c2ecf20Sopenharmony_ci * Build the TCW request for the format check 24128c2ecf20Sopenharmony_ci */ 24138c2ecf20Sopenharmony_cistatic struct dasd_ccw_req * 24148c2ecf20Sopenharmony_cidasd_eckd_build_check_tcw(struct dasd_device *base, struct format_data_t *fdata, 24158c2ecf20Sopenharmony_ci int enable_pav, struct eckd_count *fmt_buffer, 24168c2ecf20Sopenharmony_ci int rpt) 24178c2ecf20Sopenharmony_ci{ 24188c2ecf20Sopenharmony_ci struct dasd_eckd_private *start_priv; 24198c2ecf20Sopenharmony_ci struct dasd_device *startdev = NULL; 24208c2ecf20Sopenharmony_ci struct tidaw *last_tidaw = NULL; 24218c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 24228c2ecf20Sopenharmony_ci struct itcw *itcw; 24238c2ecf20Sopenharmony_ci int itcw_size; 24248c2ecf20Sopenharmony_ci int count; 24258c2ecf20Sopenharmony_ci int rc; 24268c2ecf20Sopenharmony_ci int i; 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_ci if (enable_pav) 24298c2ecf20Sopenharmony_ci startdev = dasd_alias_get_start_dev(base); 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_ci if (!startdev) 24328c2ecf20Sopenharmony_ci startdev = base; 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_ci start_priv = startdev->private; 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci count = rpt * (fdata->stop_unit - fdata->start_unit + 1); 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_ci /* 24398c2ecf20Sopenharmony_ci * we're adding 'count' amount of tidaw to the itcw. 24408c2ecf20Sopenharmony_ci * calculate the corresponding itcw_size 24418c2ecf20Sopenharmony_ci */ 24428c2ecf20Sopenharmony_ci itcw_size = itcw_calc_size(0, count, 0); 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_ci cqr = dasd_fmalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev); 24458c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) 24468c2ecf20Sopenharmony_ci return cqr; 24478c2ecf20Sopenharmony_ci 24488c2ecf20Sopenharmony_ci start_priv->count++; 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ci itcw = itcw_init(cqr->data, itcw_size, ITCW_OP_READ, 0, count, 0); 24518c2ecf20Sopenharmony_ci if (IS_ERR(itcw)) { 24528c2ecf20Sopenharmony_ci rc = -EINVAL; 24538c2ecf20Sopenharmony_ci goto out_err; 24548c2ecf20Sopenharmony_ci } 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ci cqr->cpaddr = itcw_get_tcw(itcw); 24578c2ecf20Sopenharmony_ci rc = prepare_itcw(itcw, fdata->start_unit, fdata->stop_unit, 24588c2ecf20Sopenharmony_ci DASD_ECKD_CCW_READ_COUNT_MT, base, startdev, 0, count, 24598c2ecf20Sopenharmony_ci sizeof(struct eckd_count), 24608c2ecf20Sopenharmony_ci count * sizeof(struct eckd_count), 0, rpt); 24618c2ecf20Sopenharmony_ci if (rc) 24628c2ecf20Sopenharmony_ci goto out_err; 24638c2ecf20Sopenharmony_ci 24648c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 24658c2ecf20Sopenharmony_ci last_tidaw = itcw_add_tidaw(itcw, 0, fmt_buffer++, 24668c2ecf20Sopenharmony_ci sizeof(struct eckd_count)); 24678c2ecf20Sopenharmony_ci if (IS_ERR(last_tidaw)) { 24688c2ecf20Sopenharmony_ci rc = -EINVAL; 24698c2ecf20Sopenharmony_ci goto out_err; 24708c2ecf20Sopenharmony_ci } 24718c2ecf20Sopenharmony_ci } 24728c2ecf20Sopenharmony_ci 24738c2ecf20Sopenharmony_ci last_tidaw->flags |= TIDAW_FLAGS_LAST; 24748c2ecf20Sopenharmony_ci itcw_finalize(itcw); 24758c2ecf20Sopenharmony_ci 24768c2ecf20Sopenharmony_ci cqr->cpmode = 1; 24778c2ecf20Sopenharmony_ci cqr->startdev = startdev; 24788c2ecf20Sopenharmony_ci cqr->memdev = startdev; 24798c2ecf20Sopenharmony_ci cqr->basedev = base; 24808c2ecf20Sopenharmony_ci cqr->retries = startdev->default_retries; 24818c2ecf20Sopenharmony_ci cqr->expires = startdev->default_expires * HZ; 24828c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 24838c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 24848c2ecf20Sopenharmony_ci /* Set flags to suppress output for expected errors */ 24858c2ecf20Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags); 24868c2ecf20Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags); 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_ci return cqr; 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ciout_err: 24918c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, startdev); 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci return ERR_PTR(rc); 24948c2ecf20Sopenharmony_ci} 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_ci/* 24978c2ecf20Sopenharmony_ci * Build the CCW request for the format check 24988c2ecf20Sopenharmony_ci */ 24998c2ecf20Sopenharmony_cistatic struct dasd_ccw_req * 25008c2ecf20Sopenharmony_cidasd_eckd_build_check(struct dasd_device *base, struct format_data_t *fdata, 25018c2ecf20Sopenharmony_ci int enable_pav, struct eckd_count *fmt_buffer, int rpt) 25028c2ecf20Sopenharmony_ci{ 25038c2ecf20Sopenharmony_ci struct dasd_eckd_private *start_priv; 25048c2ecf20Sopenharmony_ci struct dasd_eckd_private *base_priv; 25058c2ecf20Sopenharmony_ci struct dasd_device *startdev = NULL; 25068c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 25078c2ecf20Sopenharmony_ci struct ccw1 *ccw; 25088c2ecf20Sopenharmony_ci void *data; 25098c2ecf20Sopenharmony_ci int cplength, datasize; 25108c2ecf20Sopenharmony_ci int use_prefix; 25118c2ecf20Sopenharmony_ci int count; 25128c2ecf20Sopenharmony_ci int i; 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_ci if (enable_pav) 25158c2ecf20Sopenharmony_ci startdev = dasd_alias_get_start_dev(base); 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci if (!startdev) 25188c2ecf20Sopenharmony_ci startdev = base; 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci start_priv = startdev->private; 25218c2ecf20Sopenharmony_ci base_priv = base->private; 25228c2ecf20Sopenharmony_ci 25238c2ecf20Sopenharmony_ci count = rpt * (fdata->stop_unit - fdata->start_unit + 1); 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_ci use_prefix = base_priv->features.feature[8] & 0x01; 25268c2ecf20Sopenharmony_ci 25278c2ecf20Sopenharmony_ci if (use_prefix) { 25288c2ecf20Sopenharmony_ci cplength = 1; 25298c2ecf20Sopenharmony_ci datasize = sizeof(struct PFX_eckd_data); 25308c2ecf20Sopenharmony_ci } else { 25318c2ecf20Sopenharmony_ci cplength = 2; 25328c2ecf20Sopenharmony_ci datasize = sizeof(struct DE_eckd_data) + 25338c2ecf20Sopenharmony_ci sizeof(struct LO_eckd_data); 25348c2ecf20Sopenharmony_ci } 25358c2ecf20Sopenharmony_ci cplength += count; 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci cqr = dasd_fmalloc_request(DASD_ECKD_MAGIC, cplength, datasize, startdev); 25388c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) 25398c2ecf20Sopenharmony_ci return cqr; 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci start_priv->count++; 25428c2ecf20Sopenharmony_ci data = cqr->data; 25438c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 25448c2ecf20Sopenharmony_ci 25458c2ecf20Sopenharmony_ci if (use_prefix) { 25468c2ecf20Sopenharmony_ci prefix_LRE(ccw++, data, fdata->start_unit, fdata->stop_unit, 25478c2ecf20Sopenharmony_ci DASD_ECKD_CCW_READ_COUNT, base, startdev, 1, 0, 25488c2ecf20Sopenharmony_ci count, 0, 0); 25498c2ecf20Sopenharmony_ci } else { 25508c2ecf20Sopenharmony_ci define_extent(ccw++, data, fdata->start_unit, fdata->stop_unit, 25518c2ecf20Sopenharmony_ci DASD_ECKD_CCW_READ_COUNT, startdev, 0); 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci data += sizeof(struct DE_eckd_data); 25548c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci locate_record(ccw++, data, fdata->start_unit, 0, count, 25578c2ecf20Sopenharmony_ci DASD_ECKD_CCW_READ_COUNT, base, 0); 25588c2ecf20Sopenharmony_ci } 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 25618c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 25628c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT; 25638c2ecf20Sopenharmony_ci ccw->flags = CCW_FLAG_SLI; 25648c2ecf20Sopenharmony_ci ccw->count = 8; 25658c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) fmt_buffer; 25668c2ecf20Sopenharmony_ci ccw++; 25678c2ecf20Sopenharmony_ci fmt_buffer++; 25688c2ecf20Sopenharmony_ci } 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_ci cqr->startdev = startdev; 25718c2ecf20Sopenharmony_ci cqr->memdev = startdev; 25728c2ecf20Sopenharmony_ci cqr->basedev = base; 25738c2ecf20Sopenharmony_ci cqr->retries = DASD_RETRIES; 25748c2ecf20Sopenharmony_ci cqr->expires = startdev->default_expires * HZ; 25758c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 25768c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 25778c2ecf20Sopenharmony_ci /* Set flags to suppress output for expected errors */ 25788c2ecf20Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags); 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci return cqr; 25818c2ecf20Sopenharmony_ci} 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_cistatic struct dasd_ccw_req * 25848c2ecf20Sopenharmony_cidasd_eckd_build_format(struct dasd_device *base, struct dasd_device *startdev, 25858c2ecf20Sopenharmony_ci struct format_data_t *fdata, int enable_pav) 25868c2ecf20Sopenharmony_ci{ 25878c2ecf20Sopenharmony_ci struct dasd_eckd_private *base_priv; 25888c2ecf20Sopenharmony_ci struct dasd_eckd_private *start_priv; 25898c2ecf20Sopenharmony_ci struct dasd_ccw_req *fcp; 25908c2ecf20Sopenharmony_ci struct eckd_count *ect; 25918c2ecf20Sopenharmony_ci struct ch_t address; 25928c2ecf20Sopenharmony_ci struct ccw1 *ccw; 25938c2ecf20Sopenharmony_ci void *data; 25948c2ecf20Sopenharmony_ci int rpt; 25958c2ecf20Sopenharmony_ci int cplength, datasize; 25968c2ecf20Sopenharmony_ci int i, j; 25978c2ecf20Sopenharmony_ci int intensity = 0; 25988c2ecf20Sopenharmony_ci int r0_perm; 25998c2ecf20Sopenharmony_ci int nr_tracks; 26008c2ecf20Sopenharmony_ci int use_prefix; 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_ci if (enable_pav) 26038c2ecf20Sopenharmony_ci startdev = dasd_alias_get_start_dev(base); 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci if (!startdev) 26068c2ecf20Sopenharmony_ci startdev = base; 26078c2ecf20Sopenharmony_ci 26088c2ecf20Sopenharmony_ci start_priv = startdev->private; 26098c2ecf20Sopenharmony_ci base_priv = base->private; 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_ci rpt = recs_per_track(&base_priv->rdc_data, 0, fdata->blksize); 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci nr_tracks = fdata->stop_unit - fdata->start_unit + 1; 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci /* 26168c2ecf20Sopenharmony_ci * fdata->intensity is a bit string that tells us what to do: 26178c2ecf20Sopenharmony_ci * Bit 0: write record zero 26188c2ecf20Sopenharmony_ci * Bit 1: write home address, currently not supported 26198c2ecf20Sopenharmony_ci * Bit 2: invalidate tracks 26208c2ecf20Sopenharmony_ci * Bit 3: use OS/390 compatible disk layout (cdl) 26218c2ecf20Sopenharmony_ci * Bit 4: do not allow storage subsystem to modify record zero 26228c2ecf20Sopenharmony_ci * Only some bit combinations do make sense. 26238c2ecf20Sopenharmony_ci */ 26248c2ecf20Sopenharmony_ci if (fdata->intensity & 0x10) { 26258c2ecf20Sopenharmony_ci r0_perm = 0; 26268c2ecf20Sopenharmony_ci intensity = fdata->intensity & ~0x10; 26278c2ecf20Sopenharmony_ci } else { 26288c2ecf20Sopenharmony_ci r0_perm = 1; 26298c2ecf20Sopenharmony_ci intensity = fdata->intensity; 26308c2ecf20Sopenharmony_ci } 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci use_prefix = base_priv->features.feature[8] & 0x01; 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_ci switch (intensity) { 26358c2ecf20Sopenharmony_ci case 0x00: /* Normal format */ 26368c2ecf20Sopenharmony_ci case 0x08: /* Normal format, use cdl. */ 26378c2ecf20Sopenharmony_ci cplength = 2 + (rpt*nr_tracks); 26388c2ecf20Sopenharmony_ci if (use_prefix) 26398c2ecf20Sopenharmony_ci datasize = sizeof(struct PFX_eckd_data) + 26408c2ecf20Sopenharmony_ci sizeof(struct LO_eckd_data) + 26418c2ecf20Sopenharmony_ci rpt * nr_tracks * sizeof(struct eckd_count); 26428c2ecf20Sopenharmony_ci else 26438c2ecf20Sopenharmony_ci datasize = sizeof(struct DE_eckd_data) + 26448c2ecf20Sopenharmony_ci sizeof(struct LO_eckd_data) + 26458c2ecf20Sopenharmony_ci rpt * nr_tracks * sizeof(struct eckd_count); 26468c2ecf20Sopenharmony_ci break; 26478c2ecf20Sopenharmony_ci case 0x01: /* Write record zero and format track. */ 26488c2ecf20Sopenharmony_ci case 0x09: /* Write record zero and format track, use cdl. */ 26498c2ecf20Sopenharmony_ci cplength = 2 + rpt * nr_tracks; 26508c2ecf20Sopenharmony_ci if (use_prefix) 26518c2ecf20Sopenharmony_ci datasize = sizeof(struct PFX_eckd_data) + 26528c2ecf20Sopenharmony_ci sizeof(struct LO_eckd_data) + 26538c2ecf20Sopenharmony_ci sizeof(struct eckd_count) + 26548c2ecf20Sopenharmony_ci rpt * nr_tracks * sizeof(struct eckd_count); 26558c2ecf20Sopenharmony_ci else 26568c2ecf20Sopenharmony_ci datasize = sizeof(struct DE_eckd_data) + 26578c2ecf20Sopenharmony_ci sizeof(struct LO_eckd_data) + 26588c2ecf20Sopenharmony_ci sizeof(struct eckd_count) + 26598c2ecf20Sopenharmony_ci rpt * nr_tracks * sizeof(struct eckd_count); 26608c2ecf20Sopenharmony_ci break; 26618c2ecf20Sopenharmony_ci case 0x04: /* Invalidate track. */ 26628c2ecf20Sopenharmony_ci case 0x0c: /* Invalidate track, use cdl. */ 26638c2ecf20Sopenharmony_ci cplength = 3; 26648c2ecf20Sopenharmony_ci if (use_prefix) 26658c2ecf20Sopenharmony_ci datasize = sizeof(struct PFX_eckd_data) + 26668c2ecf20Sopenharmony_ci sizeof(struct LO_eckd_data) + 26678c2ecf20Sopenharmony_ci sizeof(struct eckd_count); 26688c2ecf20Sopenharmony_ci else 26698c2ecf20Sopenharmony_ci datasize = sizeof(struct DE_eckd_data) + 26708c2ecf20Sopenharmony_ci sizeof(struct LO_eckd_data) + 26718c2ecf20Sopenharmony_ci sizeof(struct eckd_count); 26728c2ecf20Sopenharmony_ci break; 26738c2ecf20Sopenharmony_ci default: 26748c2ecf20Sopenharmony_ci dev_warn(&startdev->cdev->dev, 26758c2ecf20Sopenharmony_ci "An I/O control call used incorrect flags 0x%x\n", 26768c2ecf20Sopenharmony_ci fdata->intensity); 26778c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 26788c2ecf20Sopenharmony_ci } 26798c2ecf20Sopenharmony_ci 26808c2ecf20Sopenharmony_ci fcp = dasd_fmalloc_request(DASD_ECKD_MAGIC, cplength, datasize, startdev); 26818c2ecf20Sopenharmony_ci if (IS_ERR(fcp)) 26828c2ecf20Sopenharmony_ci return fcp; 26838c2ecf20Sopenharmony_ci 26848c2ecf20Sopenharmony_ci start_priv->count++; 26858c2ecf20Sopenharmony_ci data = fcp->data; 26868c2ecf20Sopenharmony_ci ccw = fcp->cpaddr; 26878c2ecf20Sopenharmony_ci 26888c2ecf20Sopenharmony_ci switch (intensity & ~0x08) { 26898c2ecf20Sopenharmony_ci case 0x00: /* Normal format. */ 26908c2ecf20Sopenharmony_ci if (use_prefix) { 26918c2ecf20Sopenharmony_ci prefix(ccw++, (struct PFX_eckd_data *) data, 26928c2ecf20Sopenharmony_ci fdata->start_unit, fdata->stop_unit, 26938c2ecf20Sopenharmony_ci DASD_ECKD_CCW_WRITE_CKD, base, startdev); 26948c2ecf20Sopenharmony_ci /* grant subsystem permission to format R0 */ 26958c2ecf20Sopenharmony_ci if (r0_perm) 26968c2ecf20Sopenharmony_ci ((struct PFX_eckd_data *)data) 26978c2ecf20Sopenharmony_ci ->define_extent.ga_extended |= 0x04; 26988c2ecf20Sopenharmony_ci data += sizeof(struct PFX_eckd_data); 26998c2ecf20Sopenharmony_ci } else { 27008c2ecf20Sopenharmony_ci define_extent(ccw++, (struct DE_eckd_data *) data, 27018c2ecf20Sopenharmony_ci fdata->start_unit, fdata->stop_unit, 27028c2ecf20Sopenharmony_ci DASD_ECKD_CCW_WRITE_CKD, startdev, 0); 27038c2ecf20Sopenharmony_ci /* grant subsystem permission to format R0 */ 27048c2ecf20Sopenharmony_ci if (r0_perm) 27058c2ecf20Sopenharmony_ci ((struct DE_eckd_data *) data) 27068c2ecf20Sopenharmony_ci ->ga_extended |= 0x04; 27078c2ecf20Sopenharmony_ci data += sizeof(struct DE_eckd_data); 27088c2ecf20Sopenharmony_ci } 27098c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 27108c2ecf20Sopenharmony_ci locate_record(ccw++, (struct LO_eckd_data *) data, 27118c2ecf20Sopenharmony_ci fdata->start_unit, 0, rpt*nr_tracks, 27128c2ecf20Sopenharmony_ci DASD_ECKD_CCW_WRITE_CKD, base, 27138c2ecf20Sopenharmony_ci fdata->blksize); 27148c2ecf20Sopenharmony_ci data += sizeof(struct LO_eckd_data); 27158c2ecf20Sopenharmony_ci break; 27168c2ecf20Sopenharmony_ci case 0x01: /* Write record zero + format track. */ 27178c2ecf20Sopenharmony_ci if (use_prefix) { 27188c2ecf20Sopenharmony_ci prefix(ccw++, (struct PFX_eckd_data *) data, 27198c2ecf20Sopenharmony_ci fdata->start_unit, fdata->stop_unit, 27208c2ecf20Sopenharmony_ci DASD_ECKD_CCW_WRITE_RECORD_ZERO, 27218c2ecf20Sopenharmony_ci base, startdev); 27228c2ecf20Sopenharmony_ci data += sizeof(struct PFX_eckd_data); 27238c2ecf20Sopenharmony_ci } else { 27248c2ecf20Sopenharmony_ci define_extent(ccw++, (struct DE_eckd_data *) data, 27258c2ecf20Sopenharmony_ci fdata->start_unit, fdata->stop_unit, 27268c2ecf20Sopenharmony_ci DASD_ECKD_CCW_WRITE_RECORD_ZERO, startdev, 0); 27278c2ecf20Sopenharmony_ci data += sizeof(struct DE_eckd_data); 27288c2ecf20Sopenharmony_ci } 27298c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 27308c2ecf20Sopenharmony_ci locate_record(ccw++, (struct LO_eckd_data *) data, 27318c2ecf20Sopenharmony_ci fdata->start_unit, 0, rpt * nr_tracks + 1, 27328c2ecf20Sopenharmony_ci DASD_ECKD_CCW_WRITE_RECORD_ZERO, base, 27338c2ecf20Sopenharmony_ci base->block->bp_block); 27348c2ecf20Sopenharmony_ci data += sizeof(struct LO_eckd_data); 27358c2ecf20Sopenharmony_ci break; 27368c2ecf20Sopenharmony_ci case 0x04: /* Invalidate track. */ 27378c2ecf20Sopenharmony_ci if (use_prefix) { 27388c2ecf20Sopenharmony_ci prefix(ccw++, (struct PFX_eckd_data *) data, 27398c2ecf20Sopenharmony_ci fdata->start_unit, fdata->stop_unit, 27408c2ecf20Sopenharmony_ci DASD_ECKD_CCW_WRITE_CKD, base, startdev); 27418c2ecf20Sopenharmony_ci data += sizeof(struct PFX_eckd_data); 27428c2ecf20Sopenharmony_ci } else { 27438c2ecf20Sopenharmony_ci define_extent(ccw++, (struct DE_eckd_data *) data, 27448c2ecf20Sopenharmony_ci fdata->start_unit, fdata->stop_unit, 27458c2ecf20Sopenharmony_ci DASD_ECKD_CCW_WRITE_CKD, startdev, 0); 27468c2ecf20Sopenharmony_ci data += sizeof(struct DE_eckd_data); 27478c2ecf20Sopenharmony_ci } 27488c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 27498c2ecf20Sopenharmony_ci locate_record(ccw++, (struct LO_eckd_data *) data, 27508c2ecf20Sopenharmony_ci fdata->start_unit, 0, 1, 27518c2ecf20Sopenharmony_ci DASD_ECKD_CCW_WRITE_CKD, base, 8); 27528c2ecf20Sopenharmony_ci data += sizeof(struct LO_eckd_data); 27538c2ecf20Sopenharmony_ci break; 27548c2ecf20Sopenharmony_ci } 27558c2ecf20Sopenharmony_ci 27568c2ecf20Sopenharmony_ci for (j = 0; j < nr_tracks; j++) { 27578c2ecf20Sopenharmony_ci /* calculate cylinder and head for the current track */ 27588c2ecf20Sopenharmony_ci set_ch_t(&address, 27598c2ecf20Sopenharmony_ci (fdata->start_unit + j) / 27608c2ecf20Sopenharmony_ci base_priv->rdc_data.trk_per_cyl, 27618c2ecf20Sopenharmony_ci (fdata->start_unit + j) % 27628c2ecf20Sopenharmony_ci base_priv->rdc_data.trk_per_cyl); 27638c2ecf20Sopenharmony_ci if (intensity & 0x01) { /* write record zero */ 27648c2ecf20Sopenharmony_ci ect = (struct eckd_count *) data; 27658c2ecf20Sopenharmony_ci data += sizeof(struct eckd_count); 27668c2ecf20Sopenharmony_ci ect->cyl = address.cyl; 27678c2ecf20Sopenharmony_ci ect->head = address.head; 27688c2ecf20Sopenharmony_ci ect->record = 0; 27698c2ecf20Sopenharmony_ci ect->kl = 0; 27708c2ecf20Sopenharmony_ci ect->dl = 8; 27718c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 27728c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_WRITE_RECORD_ZERO; 27738c2ecf20Sopenharmony_ci ccw->flags = CCW_FLAG_SLI; 27748c2ecf20Sopenharmony_ci ccw->count = 8; 27758c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) ect; 27768c2ecf20Sopenharmony_ci ccw++; 27778c2ecf20Sopenharmony_ci } 27788c2ecf20Sopenharmony_ci if ((intensity & ~0x08) & 0x04) { /* erase track */ 27798c2ecf20Sopenharmony_ci ect = (struct eckd_count *) data; 27808c2ecf20Sopenharmony_ci data += sizeof(struct eckd_count); 27818c2ecf20Sopenharmony_ci ect->cyl = address.cyl; 27828c2ecf20Sopenharmony_ci ect->head = address.head; 27838c2ecf20Sopenharmony_ci ect->record = 1; 27848c2ecf20Sopenharmony_ci ect->kl = 0; 27858c2ecf20Sopenharmony_ci ect->dl = 0; 27868c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 27878c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD; 27888c2ecf20Sopenharmony_ci ccw->flags = CCW_FLAG_SLI; 27898c2ecf20Sopenharmony_ci ccw->count = 8; 27908c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) ect; 27918c2ecf20Sopenharmony_ci } else { /* write remaining records */ 27928c2ecf20Sopenharmony_ci for (i = 0; i < rpt; i++) { 27938c2ecf20Sopenharmony_ci ect = (struct eckd_count *) data; 27948c2ecf20Sopenharmony_ci data += sizeof(struct eckd_count); 27958c2ecf20Sopenharmony_ci ect->cyl = address.cyl; 27968c2ecf20Sopenharmony_ci ect->head = address.head; 27978c2ecf20Sopenharmony_ci ect->record = i + 1; 27988c2ecf20Sopenharmony_ci ect->kl = 0; 27998c2ecf20Sopenharmony_ci ect->dl = fdata->blksize; 28008c2ecf20Sopenharmony_ci /* 28018c2ecf20Sopenharmony_ci * Check for special tracks 0-1 28028c2ecf20Sopenharmony_ci * when formatting CDL 28038c2ecf20Sopenharmony_ci */ 28048c2ecf20Sopenharmony_ci if ((intensity & 0x08) && 28058c2ecf20Sopenharmony_ci address.cyl == 0 && address.head == 0) { 28068c2ecf20Sopenharmony_ci if (i < 3) { 28078c2ecf20Sopenharmony_ci ect->kl = 4; 28088c2ecf20Sopenharmony_ci ect->dl = sizes_trk0[i] - 4; 28098c2ecf20Sopenharmony_ci } 28108c2ecf20Sopenharmony_ci } 28118c2ecf20Sopenharmony_ci if ((intensity & 0x08) && 28128c2ecf20Sopenharmony_ci address.cyl == 0 && address.head == 1) { 28138c2ecf20Sopenharmony_ci ect->kl = 44; 28148c2ecf20Sopenharmony_ci ect->dl = LABEL_SIZE - 44; 28158c2ecf20Sopenharmony_ci } 28168c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 28178c2ecf20Sopenharmony_ci if (i != 0 || j == 0) 28188c2ecf20Sopenharmony_ci ccw->cmd_code = 28198c2ecf20Sopenharmony_ci DASD_ECKD_CCW_WRITE_CKD; 28208c2ecf20Sopenharmony_ci else 28218c2ecf20Sopenharmony_ci ccw->cmd_code = 28228c2ecf20Sopenharmony_ci DASD_ECKD_CCW_WRITE_CKD_MT; 28238c2ecf20Sopenharmony_ci ccw->flags = CCW_FLAG_SLI; 28248c2ecf20Sopenharmony_ci ccw->count = 8; 28258c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) ect; 28268c2ecf20Sopenharmony_ci ccw++; 28278c2ecf20Sopenharmony_ci } 28288c2ecf20Sopenharmony_ci } 28298c2ecf20Sopenharmony_ci } 28308c2ecf20Sopenharmony_ci 28318c2ecf20Sopenharmony_ci fcp->startdev = startdev; 28328c2ecf20Sopenharmony_ci fcp->memdev = startdev; 28338c2ecf20Sopenharmony_ci fcp->basedev = base; 28348c2ecf20Sopenharmony_ci fcp->retries = 256; 28358c2ecf20Sopenharmony_ci fcp->expires = startdev->default_expires * HZ; 28368c2ecf20Sopenharmony_ci fcp->buildclk = get_tod_clock(); 28378c2ecf20Sopenharmony_ci fcp->status = DASD_CQR_FILLED; 28388c2ecf20Sopenharmony_ci 28398c2ecf20Sopenharmony_ci return fcp; 28408c2ecf20Sopenharmony_ci} 28418c2ecf20Sopenharmony_ci 28428c2ecf20Sopenharmony_ci/* 28438c2ecf20Sopenharmony_ci * Wrapper function to build a CCW request depending on input data 28448c2ecf20Sopenharmony_ci */ 28458c2ecf20Sopenharmony_cistatic struct dasd_ccw_req * 28468c2ecf20Sopenharmony_cidasd_eckd_format_build_ccw_req(struct dasd_device *base, 28478c2ecf20Sopenharmony_ci struct format_data_t *fdata, int enable_pav, 28488c2ecf20Sopenharmony_ci int tpm, struct eckd_count *fmt_buffer, int rpt) 28498c2ecf20Sopenharmony_ci{ 28508c2ecf20Sopenharmony_ci struct dasd_ccw_req *ccw_req; 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_ci if (!fmt_buffer) { 28538c2ecf20Sopenharmony_ci ccw_req = dasd_eckd_build_format(base, NULL, fdata, enable_pav); 28548c2ecf20Sopenharmony_ci } else { 28558c2ecf20Sopenharmony_ci if (tpm) 28568c2ecf20Sopenharmony_ci ccw_req = dasd_eckd_build_check_tcw(base, fdata, 28578c2ecf20Sopenharmony_ci enable_pav, 28588c2ecf20Sopenharmony_ci fmt_buffer, rpt); 28598c2ecf20Sopenharmony_ci else 28608c2ecf20Sopenharmony_ci ccw_req = dasd_eckd_build_check(base, fdata, enable_pav, 28618c2ecf20Sopenharmony_ci fmt_buffer, rpt); 28628c2ecf20Sopenharmony_ci } 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci return ccw_req; 28658c2ecf20Sopenharmony_ci} 28668c2ecf20Sopenharmony_ci 28678c2ecf20Sopenharmony_ci/* 28688c2ecf20Sopenharmony_ci * Sanity checks on format_data 28698c2ecf20Sopenharmony_ci */ 28708c2ecf20Sopenharmony_cistatic int dasd_eckd_format_sanity_checks(struct dasd_device *base, 28718c2ecf20Sopenharmony_ci struct format_data_t *fdata) 28728c2ecf20Sopenharmony_ci{ 28738c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = base->private; 28748c2ecf20Sopenharmony_ci 28758c2ecf20Sopenharmony_ci if (fdata->start_unit >= 28768c2ecf20Sopenharmony_ci (private->real_cyl * private->rdc_data.trk_per_cyl)) { 28778c2ecf20Sopenharmony_ci dev_warn(&base->cdev->dev, 28788c2ecf20Sopenharmony_ci "Start track number %u used in formatting is too big\n", 28798c2ecf20Sopenharmony_ci fdata->start_unit); 28808c2ecf20Sopenharmony_ci return -EINVAL; 28818c2ecf20Sopenharmony_ci } 28828c2ecf20Sopenharmony_ci if (fdata->stop_unit >= 28838c2ecf20Sopenharmony_ci (private->real_cyl * private->rdc_data.trk_per_cyl)) { 28848c2ecf20Sopenharmony_ci dev_warn(&base->cdev->dev, 28858c2ecf20Sopenharmony_ci "Stop track number %u used in formatting is too big\n", 28868c2ecf20Sopenharmony_ci fdata->stop_unit); 28878c2ecf20Sopenharmony_ci return -EINVAL; 28888c2ecf20Sopenharmony_ci } 28898c2ecf20Sopenharmony_ci if (fdata->start_unit > fdata->stop_unit) { 28908c2ecf20Sopenharmony_ci dev_warn(&base->cdev->dev, 28918c2ecf20Sopenharmony_ci "Start track %u used in formatting exceeds end track\n", 28928c2ecf20Sopenharmony_ci fdata->start_unit); 28938c2ecf20Sopenharmony_ci return -EINVAL; 28948c2ecf20Sopenharmony_ci } 28958c2ecf20Sopenharmony_ci if (dasd_check_blocksize(fdata->blksize) != 0) { 28968c2ecf20Sopenharmony_ci dev_warn(&base->cdev->dev, 28978c2ecf20Sopenharmony_ci "The DASD cannot be formatted with block size %u\n", 28988c2ecf20Sopenharmony_ci fdata->blksize); 28998c2ecf20Sopenharmony_ci return -EINVAL; 29008c2ecf20Sopenharmony_ci } 29018c2ecf20Sopenharmony_ci return 0; 29028c2ecf20Sopenharmony_ci} 29038c2ecf20Sopenharmony_ci 29048c2ecf20Sopenharmony_ci/* 29058c2ecf20Sopenharmony_ci * This function will process format_data originally coming from an IOCTL 29068c2ecf20Sopenharmony_ci */ 29078c2ecf20Sopenharmony_cistatic int dasd_eckd_format_process_data(struct dasd_device *base, 29088c2ecf20Sopenharmony_ci struct format_data_t *fdata, 29098c2ecf20Sopenharmony_ci int enable_pav, int tpm, 29108c2ecf20Sopenharmony_ci struct eckd_count *fmt_buffer, int rpt, 29118c2ecf20Sopenharmony_ci struct irb *irb) 29128c2ecf20Sopenharmony_ci{ 29138c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = base->private; 29148c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr, *n; 29158c2ecf20Sopenharmony_ci struct list_head format_queue; 29168c2ecf20Sopenharmony_ci struct dasd_device *device; 29178c2ecf20Sopenharmony_ci char *sense = NULL; 29188c2ecf20Sopenharmony_ci int old_start, old_stop, format_step; 29198c2ecf20Sopenharmony_ci int step, retry; 29208c2ecf20Sopenharmony_ci int rc; 29218c2ecf20Sopenharmony_ci 29228c2ecf20Sopenharmony_ci rc = dasd_eckd_format_sanity_checks(base, fdata); 29238c2ecf20Sopenharmony_ci if (rc) 29248c2ecf20Sopenharmony_ci return rc; 29258c2ecf20Sopenharmony_ci 29268c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&format_queue); 29278c2ecf20Sopenharmony_ci 29288c2ecf20Sopenharmony_ci old_start = fdata->start_unit; 29298c2ecf20Sopenharmony_ci old_stop = fdata->stop_unit; 29308c2ecf20Sopenharmony_ci 29318c2ecf20Sopenharmony_ci if (!tpm && fmt_buffer != NULL) { 29328c2ecf20Sopenharmony_ci /* Command Mode / Format Check */ 29338c2ecf20Sopenharmony_ci format_step = 1; 29348c2ecf20Sopenharmony_ci } else if (tpm && fmt_buffer != NULL) { 29358c2ecf20Sopenharmony_ci /* Transport Mode / Format Check */ 29368c2ecf20Sopenharmony_ci format_step = DASD_CQR_MAX_CCW / rpt; 29378c2ecf20Sopenharmony_ci } else { 29388c2ecf20Sopenharmony_ci /* Normal Formatting */ 29398c2ecf20Sopenharmony_ci format_step = DASD_CQR_MAX_CCW / 29408c2ecf20Sopenharmony_ci recs_per_track(&private->rdc_data, 0, fdata->blksize); 29418c2ecf20Sopenharmony_ci } 29428c2ecf20Sopenharmony_ci 29438c2ecf20Sopenharmony_ci do { 29448c2ecf20Sopenharmony_ci retry = 0; 29458c2ecf20Sopenharmony_ci while (fdata->start_unit <= old_stop) { 29468c2ecf20Sopenharmony_ci step = fdata->stop_unit - fdata->start_unit + 1; 29478c2ecf20Sopenharmony_ci if (step > format_step) { 29488c2ecf20Sopenharmony_ci fdata->stop_unit = 29498c2ecf20Sopenharmony_ci fdata->start_unit + format_step - 1; 29508c2ecf20Sopenharmony_ci } 29518c2ecf20Sopenharmony_ci 29528c2ecf20Sopenharmony_ci cqr = dasd_eckd_format_build_ccw_req(base, fdata, 29538c2ecf20Sopenharmony_ci enable_pav, tpm, 29548c2ecf20Sopenharmony_ci fmt_buffer, rpt); 29558c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 29568c2ecf20Sopenharmony_ci rc = PTR_ERR(cqr); 29578c2ecf20Sopenharmony_ci if (rc == -ENOMEM) { 29588c2ecf20Sopenharmony_ci if (list_empty(&format_queue)) 29598c2ecf20Sopenharmony_ci goto out; 29608c2ecf20Sopenharmony_ci /* 29618c2ecf20Sopenharmony_ci * not enough memory available, start 29628c2ecf20Sopenharmony_ci * requests retry after first requests 29638c2ecf20Sopenharmony_ci * were finished 29648c2ecf20Sopenharmony_ci */ 29658c2ecf20Sopenharmony_ci retry = 1; 29668c2ecf20Sopenharmony_ci break; 29678c2ecf20Sopenharmony_ci } 29688c2ecf20Sopenharmony_ci goto out_err; 29698c2ecf20Sopenharmony_ci } 29708c2ecf20Sopenharmony_ci list_add_tail(&cqr->blocklist, &format_queue); 29718c2ecf20Sopenharmony_ci 29728c2ecf20Sopenharmony_ci if (fmt_buffer) { 29738c2ecf20Sopenharmony_ci step = fdata->stop_unit - fdata->start_unit + 1; 29748c2ecf20Sopenharmony_ci fmt_buffer += rpt * step; 29758c2ecf20Sopenharmony_ci } 29768c2ecf20Sopenharmony_ci fdata->start_unit = fdata->stop_unit + 1; 29778c2ecf20Sopenharmony_ci fdata->stop_unit = old_stop; 29788c2ecf20Sopenharmony_ci } 29798c2ecf20Sopenharmony_ci 29808c2ecf20Sopenharmony_ci rc = dasd_sleep_on_queue(&format_queue); 29818c2ecf20Sopenharmony_ci 29828c2ecf20Sopenharmony_ciout_err: 29838c2ecf20Sopenharmony_ci list_for_each_entry_safe(cqr, n, &format_queue, blocklist) { 29848c2ecf20Sopenharmony_ci device = cqr->startdev; 29858c2ecf20Sopenharmony_ci private = device->private; 29868c2ecf20Sopenharmony_ci 29878c2ecf20Sopenharmony_ci if (cqr->status == DASD_CQR_FAILED) { 29888c2ecf20Sopenharmony_ci /* 29898c2ecf20Sopenharmony_ci * Only get sense data if called by format 29908c2ecf20Sopenharmony_ci * check 29918c2ecf20Sopenharmony_ci */ 29928c2ecf20Sopenharmony_ci if (fmt_buffer && irb) { 29938c2ecf20Sopenharmony_ci sense = dasd_get_sense(&cqr->irb); 29948c2ecf20Sopenharmony_ci memcpy(irb, &cqr->irb, sizeof(*irb)); 29958c2ecf20Sopenharmony_ci } 29968c2ecf20Sopenharmony_ci rc = -EIO; 29978c2ecf20Sopenharmony_ci } 29988c2ecf20Sopenharmony_ci list_del_init(&cqr->blocklist); 29998c2ecf20Sopenharmony_ci dasd_ffree_request(cqr, device); 30008c2ecf20Sopenharmony_ci private->count--; 30018c2ecf20Sopenharmony_ci } 30028c2ecf20Sopenharmony_ci 30038c2ecf20Sopenharmony_ci if (rc && rc != -EIO) 30048c2ecf20Sopenharmony_ci goto out; 30058c2ecf20Sopenharmony_ci if (rc == -EIO) { 30068c2ecf20Sopenharmony_ci /* 30078c2ecf20Sopenharmony_ci * In case fewer than the expected records are on the 30088c2ecf20Sopenharmony_ci * track, we will most likely get a 'No Record Found' 30098c2ecf20Sopenharmony_ci * error (in command mode) or a 'File Protected' error 30108c2ecf20Sopenharmony_ci * (in transport mode). Those particular cases shouldn't 30118c2ecf20Sopenharmony_ci * pass the -EIO to the IOCTL, therefore reset the rc 30128c2ecf20Sopenharmony_ci * and continue. 30138c2ecf20Sopenharmony_ci */ 30148c2ecf20Sopenharmony_ci if (sense && 30158c2ecf20Sopenharmony_ci (sense[1] & SNS1_NO_REC_FOUND || 30168c2ecf20Sopenharmony_ci sense[1] & SNS1_FILE_PROTECTED)) 30178c2ecf20Sopenharmony_ci retry = 1; 30188c2ecf20Sopenharmony_ci else 30198c2ecf20Sopenharmony_ci goto out; 30208c2ecf20Sopenharmony_ci } 30218c2ecf20Sopenharmony_ci 30228c2ecf20Sopenharmony_ci } while (retry); 30238c2ecf20Sopenharmony_ci 30248c2ecf20Sopenharmony_ciout: 30258c2ecf20Sopenharmony_ci fdata->start_unit = old_start; 30268c2ecf20Sopenharmony_ci fdata->stop_unit = old_stop; 30278c2ecf20Sopenharmony_ci 30288c2ecf20Sopenharmony_ci return rc; 30298c2ecf20Sopenharmony_ci} 30308c2ecf20Sopenharmony_ci 30318c2ecf20Sopenharmony_cistatic int dasd_eckd_format_device(struct dasd_device *base, 30328c2ecf20Sopenharmony_ci struct format_data_t *fdata, int enable_pav) 30338c2ecf20Sopenharmony_ci{ 30348c2ecf20Sopenharmony_ci return dasd_eckd_format_process_data(base, fdata, enable_pav, 0, NULL, 30358c2ecf20Sopenharmony_ci 0, NULL); 30368c2ecf20Sopenharmony_ci} 30378c2ecf20Sopenharmony_ci 30388c2ecf20Sopenharmony_cistatic bool test_and_set_format_track(struct dasd_format_entry *to_format, 30398c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr) 30408c2ecf20Sopenharmony_ci{ 30418c2ecf20Sopenharmony_ci struct dasd_block *block = cqr->block; 30428c2ecf20Sopenharmony_ci struct dasd_format_entry *format; 30438c2ecf20Sopenharmony_ci unsigned long flags; 30448c2ecf20Sopenharmony_ci bool rc = false; 30458c2ecf20Sopenharmony_ci 30468c2ecf20Sopenharmony_ci spin_lock_irqsave(&block->format_lock, flags); 30478c2ecf20Sopenharmony_ci if (cqr->trkcount != atomic_read(&block->trkcount)) { 30488c2ecf20Sopenharmony_ci /* 30498c2ecf20Sopenharmony_ci * The number of formatted tracks has changed after request 30508c2ecf20Sopenharmony_ci * start and we can not tell if the current track was involved. 30518c2ecf20Sopenharmony_ci * To avoid data corruption treat it as if the current track is 30528c2ecf20Sopenharmony_ci * involved 30538c2ecf20Sopenharmony_ci */ 30548c2ecf20Sopenharmony_ci rc = true; 30558c2ecf20Sopenharmony_ci goto out; 30568c2ecf20Sopenharmony_ci } 30578c2ecf20Sopenharmony_ci list_for_each_entry(format, &block->format_list, list) { 30588c2ecf20Sopenharmony_ci if (format->track == to_format->track) { 30598c2ecf20Sopenharmony_ci rc = true; 30608c2ecf20Sopenharmony_ci goto out; 30618c2ecf20Sopenharmony_ci } 30628c2ecf20Sopenharmony_ci } 30638c2ecf20Sopenharmony_ci list_add_tail(&to_format->list, &block->format_list); 30648c2ecf20Sopenharmony_ci 30658c2ecf20Sopenharmony_ciout: 30668c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&block->format_lock, flags); 30678c2ecf20Sopenharmony_ci return rc; 30688c2ecf20Sopenharmony_ci} 30698c2ecf20Sopenharmony_ci 30708c2ecf20Sopenharmony_cistatic void clear_format_track(struct dasd_format_entry *format, 30718c2ecf20Sopenharmony_ci struct dasd_block *block) 30728c2ecf20Sopenharmony_ci{ 30738c2ecf20Sopenharmony_ci unsigned long flags; 30748c2ecf20Sopenharmony_ci 30758c2ecf20Sopenharmony_ci spin_lock_irqsave(&block->format_lock, flags); 30768c2ecf20Sopenharmony_ci atomic_inc(&block->trkcount); 30778c2ecf20Sopenharmony_ci list_del_init(&format->list); 30788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&block->format_lock, flags); 30798c2ecf20Sopenharmony_ci} 30808c2ecf20Sopenharmony_ci 30818c2ecf20Sopenharmony_ci/* 30828c2ecf20Sopenharmony_ci * Callback function to free ESE format requests. 30838c2ecf20Sopenharmony_ci */ 30848c2ecf20Sopenharmony_cistatic void dasd_eckd_ese_format_cb(struct dasd_ccw_req *cqr, void *data) 30858c2ecf20Sopenharmony_ci{ 30868c2ecf20Sopenharmony_ci struct dasd_device *device = cqr->startdev; 30878c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 30888c2ecf20Sopenharmony_ci struct dasd_format_entry *format = data; 30898c2ecf20Sopenharmony_ci 30908c2ecf20Sopenharmony_ci clear_format_track(format, cqr->basedev->block); 30918c2ecf20Sopenharmony_ci private->count--; 30928c2ecf20Sopenharmony_ci dasd_ffree_request(cqr, device); 30938c2ecf20Sopenharmony_ci} 30948c2ecf20Sopenharmony_ci 30958c2ecf20Sopenharmony_cistatic struct dasd_ccw_req * 30968c2ecf20Sopenharmony_cidasd_eckd_ese_format(struct dasd_device *startdev, struct dasd_ccw_req *cqr, 30978c2ecf20Sopenharmony_ci struct irb *irb) 30988c2ecf20Sopenharmony_ci{ 30998c2ecf20Sopenharmony_ci struct dasd_eckd_private *private; 31008c2ecf20Sopenharmony_ci struct dasd_format_entry *format; 31018c2ecf20Sopenharmony_ci struct format_data_t fdata; 31028c2ecf20Sopenharmony_ci unsigned int recs_per_trk; 31038c2ecf20Sopenharmony_ci struct dasd_ccw_req *fcqr; 31048c2ecf20Sopenharmony_ci struct dasd_device *base; 31058c2ecf20Sopenharmony_ci struct dasd_block *block; 31068c2ecf20Sopenharmony_ci unsigned int blksize; 31078c2ecf20Sopenharmony_ci struct request *req; 31088c2ecf20Sopenharmony_ci sector_t first_trk; 31098c2ecf20Sopenharmony_ci sector_t last_trk; 31108c2ecf20Sopenharmony_ci sector_t curr_trk; 31118c2ecf20Sopenharmony_ci int rc; 31128c2ecf20Sopenharmony_ci 31138c2ecf20Sopenharmony_ci req = dasd_get_callback_data(cqr); 31148c2ecf20Sopenharmony_ci block = cqr->block; 31158c2ecf20Sopenharmony_ci base = block->base; 31168c2ecf20Sopenharmony_ci private = base->private; 31178c2ecf20Sopenharmony_ci blksize = block->bp_block; 31188c2ecf20Sopenharmony_ci recs_per_trk = recs_per_track(&private->rdc_data, 0, blksize); 31198c2ecf20Sopenharmony_ci format = &startdev->format_entry; 31208c2ecf20Sopenharmony_ci 31218c2ecf20Sopenharmony_ci first_trk = blk_rq_pos(req) >> block->s2b_shift; 31228c2ecf20Sopenharmony_ci sector_div(first_trk, recs_per_trk); 31238c2ecf20Sopenharmony_ci last_trk = 31248c2ecf20Sopenharmony_ci (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift; 31258c2ecf20Sopenharmony_ci sector_div(last_trk, recs_per_trk); 31268c2ecf20Sopenharmony_ci rc = dasd_eckd_track_from_irb(irb, base, &curr_trk); 31278c2ecf20Sopenharmony_ci if (rc) 31288c2ecf20Sopenharmony_ci return ERR_PTR(rc); 31298c2ecf20Sopenharmony_ci 31308c2ecf20Sopenharmony_ci if (curr_trk < first_trk || curr_trk > last_trk) { 31318c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, startdev, 31328c2ecf20Sopenharmony_ci "ESE error track %llu not within range %llu - %llu\n", 31338c2ecf20Sopenharmony_ci curr_trk, first_trk, last_trk); 31348c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 31358c2ecf20Sopenharmony_ci } 31368c2ecf20Sopenharmony_ci format->track = curr_trk; 31378c2ecf20Sopenharmony_ci /* test if track is already in formatting by another thread */ 31388c2ecf20Sopenharmony_ci if (test_and_set_format_track(format, cqr)) { 31398c2ecf20Sopenharmony_ci /* this is no real error so do not count down retries */ 31408c2ecf20Sopenharmony_ci cqr->retries++; 31418c2ecf20Sopenharmony_ci return ERR_PTR(-EEXIST); 31428c2ecf20Sopenharmony_ci } 31438c2ecf20Sopenharmony_ci 31448c2ecf20Sopenharmony_ci fdata.start_unit = curr_trk; 31458c2ecf20Sopenharmony_ci fdata.stop_unit = curr_trk; 31468c2ecf20Sopenharmony_ci fdata.blksize = blksize; 31478c2ecf20Sopenharmony_ci fdata.intensity = private->uses_cdl ? DASD_FMT_INT_COMPAT : 0; 31488c2ecf20Sopenharmony_ci 31498c2ecf20Sopenharmony_ci rc = dasd_eckd_format_sanity_checks(base, &fdata); 31508c2ecf20Sopenharmony_ci if (rc) 31518c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 31528c2ecf20Sopenharmony_ci 31538c2ecf20Sopenharmony_ci /* 31548c2ecf20Sopenharmony_ci * We're building the request with PAV disabled as we're reusing 31558c2ecf20Sopenharmony_ci * the former startdev. 31568c2ecf20Sopenharmony_ci */ 31578c2ecf20Sopenharmony_ci fcqr = dasd_eckd_build_format(base, startdev, &fdata, 0); 31588c2ecf20Sopenharmony_ci if (IS_ERR(fcqr)) 31598c2ecf20Sopenharmony_ci return fcqr; 31608c2ecf20Sopenharmony_ci 31618c2ecf20Sopenharmony_ci fcqr->callback = dasd_eckd_ese_format_cb; 31628c2ecf20Sopenharmony_ci fcqr->callback_data = (void *) format; 31638c2ecf20Sopenharmony_ci 31648c2ecf20Sopenharmony_ci return fcqr; 31658c2ecf20Sopenharmony_ci} 31668c2ecf20Sopenharmony_ci 31678c2ecf20Sopenharmony_ci/* 31688c2ecf20Sopenharmony_ci * When data is read from an unformatted area of an ESE volume, this function 31698c2ecf20Sopenharmony_ci * returns zeroed data and thereby mimics a read of zero data. 31708c2ecf20Sopenharmony_ci * 31718c2ecf20Sopenharmony_ci * The first unformatted track is the one that got the NRF error, the address is 31728c2ecf20Sopenharmony_ci * encoded in the sense data. 31738c2ecf20Sopenharmony_ci * 31748c2ecf20Sopenharmony_ci * All tracks before have returned valid data and should not be touched. 31758c2ecf20Sopenharmony_ci * All tracks after the unformatted track might be formatted or not. This is 31768c2ecf20Sopenharmony_ci * currently not known, remember the processed data and return the remainder of 31778c2ecf20Sopenharmony_ci * the request to the blocklayer in __dasd_cleanup_cqr(). 31788c2ecf20Sopenharmony_ci */ 31798c2ecf20Sopenharmony_cistatic int dasd_eckd_ese_read(struct dasd_ccw_req *cqr, struct irb *irb) 31808c2ecf20Sopenharmony_ci{ 31818c2ecf20Sopenharmony_ci struct dasd_eckd_private *private; 31828c2ecf20Sopenharmony_ci sector_t first_trk, last_trk; 31838c2ecf20Sopenharmony_ci sector_t first_blk, last_blk; 31848c2ecf20Sopenharmony_ci unsigned int blksize, off; 31858c2ecf20Sopenharmony_ci unsigned int recs_per_trk; 31868c2ecf20Sopenharmony_ci struct dasd_device *base; 31878c2ecf20Sopenharmony_ci struct req_iterator iter; 31888c2ecf20Sopenharmony_ci struct dasd_block *block; 31898c2ecf20Sopenharmony_ci unsigned int skip_block; 31908c2ecf20Sopenharmony_ci unsigned int blk_count; 31918c2ecf20Sopenharmony_ci struct request *req; 31928c2ecf20Sopenharmony_ci struct bio_vec bv; 31938c2ecf20Sopenharmony_ci sector_t curr_trk; 31948c2ecf20Sopenharmony_ci sector_t end_blk; 31958c2ecf20Sopenharmony_ci char *dst; 31968c2ecf20Sopenharmony_ci int rc; 31978c2ecf20Sopenharmony_ci 31988c2ecf20Sopenharmony_ci req = (struct request *) cqr->callback_data; 31998c2ecf20Sopenharmony_ci base = cqr->block->base; 32008c2ecf20Sopenharmony_ci blksize = base->block->bp_block; 32018c2ecf20Sopenharmony_ci block = cqr->block; 32028c2ecf20Sopenharmony_ci private = base->private; 32038c2ecf20Sopenharmony_ci skip_block = 0; 32048c2ecf20Sopenharmony_ci blk_count = 0; 32058c2ecf20Sopenharmony_ci 32068c2ecf20Sopenharmony_ci recs_per_trk = recs_per_track(&private->rdc_data, 0, blksize); 32078c2ecf20Sopenharmony_ci first_trk = first_blk = blk_rq_pos(req) >> block->s2b_shift; 32088c2ecf20Sopenharmony_ci sector_div(first_trk, recs_per_trk); 32098c2ecf20Sopenharmony_ci last_trk = last_blk = 32108c2ecf20Sopenharmony_ci (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift; 32118c2ecf20Sopenharmony_ci sector_div(last_trk, recs_per_trk); 32128c2ecf20Sopenharmony_ci rc = dasd_eckd_track_from_irb(irb, base, &curr_trk); 32138c2ecf20Sopenharmony_ci if (rc) 32148c2ecf20Sopenharmony_ci return rc; 32158c2ecf20Sopenharmony_ci 32168c2ecf20Sopenharmony_ci /* sanity check if the current track from sense data is valid */ 32178c2ecf20Sopenharmony_ci if (curr_trk < first_trk || curr_trk > last_trk) { 32188c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, base, 32198c2ecf20Sopenharmony_ci "ESE error track %llu not within range %llu - %llu\n", 32208c2ecf20Sopenharmony_ci curr_trk, first_trk, last_trk); 32218c2ecf20Sopenharmony_ci return -EINVAL; 32228c2ecf20Sopenharmony_ci } 32238c2ecf20Sopenharmony_ci 32248c2ecf20Sopenharmony_ci /* 32258c2ecf20Sopenharmony_ci * if not the first track got the NRF error we have to skip over valid 32268c2ecf20Sopenharmony_ci * blocks 32278c2ecf20Sopenharmony_ci */ 32288c2ecf20Sopenharmony_ci if (curr_trk != first_trk) 32298c2ecf20Sopenharmony_ci skip_block = curr_trk * recs_per_trk - first_blk; 32308c2ecf20Sopenharmony_ci 32318c2ecf20Sopenharmony_ci /* we have no information beyond the current track */ 32328c2ecf20Sopenharmony_ci end_blk = (curr_trk + 1) * recs_per_trk; 32338c2ecf20Sopenharmony_ci 32348c2ecf20Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 32358c2ecf20Sopenharmony_ci dst = page_address(bv.bv_page) + bv.bv_offset; 32368c2ecf20Sopenharmony_ci for (off = 0; off < bv.bv_len; off += blksize) { 32378c2ecf20Sopenharmony_ci if (first_blk + blk_count >= end_blk) { 32388c2ecf20Sopenharmony_ci cqr->proc_bytes = blk_count * blksize; 32398c2ecf20Sopenharmony_ci return 0; 32408c2ecf20Sopenharmony_ci } 32418c2ecf20Sopenharmony_ci if (dst && !skip_block) 32428c2ecf20Sopenharmony_ci memset(dst, 0, blksize); 32438c2ecf20Sopenharmony_ci else 32448c2ecf20Sopenharmony_ci skip_block--; 32458c2ecf20Sopenharmony_ci dst += blksize; 32468c2ecf20Sopenharmony_ci blk_count++; 32478c2ecf20Sopenharmony_ci } 32488c2ecf20Sopenharmony_ci } 32498c2ecf20Sopenharmony_ci return 0; 32508c2ecf20Sopenharmony_ci} 32518c2ecf20Sopenharmony_ci 32528c2ecf20Sopenharmony_ci/* 32538c2ecf20Sopenharmony_ci * Helper function to count consecutive records of a single track. 32548c2ecf20Sopenharmony_ci */ 32558c2ecf20Sopenharmony_cistatic int dasd_eckd_count_records(struct eckd_count *fmt_buffer, int start, 32568c2ecf20Sopenharmony_ci int max) 32578c2ecf20Sopenharmony_ci{ 32588c2ecf20Sopenharmony_ci int head; 32598c2ecf20Sopenharmony_ci int i; 32608c2ecf20Sopenharmony_ci 32618c2ecf20Sopenharmony_ci head = fmt_buffer[start].head; 32628c2ecf20Sopenharmony_ci 32638c2ecf20Sopenharmony_ci /* 32648c2ecf20Sopenharmony_ci * There are 3 conditions where we stop counting: 32658c2ecf20Sopenharmony_ci * - if data reoccurs (same head and record may reoccur), which may 32668c2ecf20Sopenharmony_ci * happen due to the way DASD_ECKD_CCW_READ_COUNT works 32678c2ecf20Sopenharmony_ci * - when the head changes, because we're iterating over several tracks 32688c2ecf20Sopenharmony_ci * then (DASD_ECKD_CCW_READ_COUNT_MT) 32698c2ecf20Sopenharmony_ci * - when we've reached the end of sensible data in the buffer (the 32708c2ecf20Sopenharmony_ci * record will be 0 then) 32718c2ecf20Sopenharmony_ci */ 32728c2ecf20Sopenharmony_ci for (i = start; i < max; i++) { 32738c2ecf20Sopenharmony_ci if (i > start) { 32748c2ecf20Sopenharmony_ci if ((fmt_buffer[i].head == head && 32758c2ecf20Sopenharmony_ci fmt_buffer[i].record == 1) || 32768c2ecf20Sopenharmony_ci fmt_buffer[i].head != head || 32778c2ecf20Sopenharmony_ci fmt_buffer[i].record == 0) 32788c2ecf20Sopenharmony_ci break; 32798c2ecf20Sopenharmony_ci } 32808c2ecf20Sopenharmony_ci } 32818c2ecf20Sopenharmony_ci 32828c2ecf20Sopenharmony_ci return i - start; 32838c2ecf20Sopenharmony_ci} 32848c2ecf20Sopenharmony_ci 32858c2ecf20Sopenharmony_ci/* 32868c2ecf20Sopenharmony_ci * Evaluate a given range of tracks. Data like number of records, blocksize, 32878c2ecf20Sopenharmony_ci * record ids, and key length are compared with expected data. 32888c2ecf20Sopenharmony_ci * 32898c2ecf20Sopenharmony_ci * If a mismatch occurs, the corresponding error bit is set, as well as 32908c2ecf20Sopenharmony_ci * additional information, depending on the error. 32918c2ecf20Sopenharmony_ci */ 32928c2ecf20Sopenharmony_cistatic void dasd_eckd_format_evaluate_tracks(struct eckd_count *fmt_buffer, 32938c2ecf20Sopenharmony_ci struct format_check_t *cdata, 32948c2ecf20Sopenharmony_ci int rpt_max, int rpt_exp, 32958c2ecf20Sopenharmony_ci int trk_per_cyl, int tpm) 32968c2ecf20Sopenharmony_ci{ 32978c2ecf20Sopenharmony_ci struct ch_t geo; 32988c2ecf20Sopenharmony_ci int max_entries; 32998c2ecf20Sopenharmony_ci int count = 0; 33008c2ecf20Sopenharmony_ci int trkcount; 33018c2ecf20Sopenharmony_ci int blksize; 33028c2ecf20Sopenharmony_ci int pos = 0; 33038c2ecf20Sopenharmony_ci int i, j; 33048c2ecf20Sopenharmony_ci int kl; 33058c2ecf20Sopenharmony_ci 33068c2ecf20Sopenharmony_ci trkcount = cdata->expect.stop_unit - cdata->expect.start_unit + 1; 33078c2ecf20Sopenharmony_ci max_entries = trkcount * rpt_max; 33088c2ecf20Sopenharmony_ci 33098c2ecf20Sopenharmony_ci for (i = cdata->expect.start_unit; i <= cdata->expect.stop_unit; i++) { 33108c2ecf20Sopenharmony_ci /* Calculate the correct next starting position in the buffer */ 33118c2ecf20Sopenharmony_ci if (tpm) { 33128c2ecf20Sopenharmony_ci while (fmt_buffer[pos].record == 0 && 33138c2ecf20Sopenharmony_ci fmt_buffer[pos].dl == 0) { 33148c2ecf20Sopenharmony_ci if (pos++ > max_entries) 33158c2ecf20Sopenharmony_ci break; 33168c2ecf20Sopenharmony_ci } 33178c2ecf20Sopenharmony_ci } else { 33188c2ecf20Sopenharmony_ci if (i != cdata->expect.start_unit) 33198c2ecf20Sopenharmony_ci pos += rpt_max - count; 33208c2ecf20Sopenharmony_ci } 33218c2ecf20Sopenharmony_ci 33228c2ecf20Sopenharmony_ci /* Calculate the expected geo values for the current track */ 33238c2ecf20Sopenharmony_ci set_ch_t(&geo, i / trk_per_cyl, i % trk_per_cyl); 33248c2ecf20Sopenharmony_ci 33258c2ecf20Sopenharmony_ci /* Count and check number of records */ 33268c2ecf20Sopenharmony_ci count = dasd_eckd_count_records(fmt_buffer, pos, pos + rpt_max); 33278c2ecf20Sopenharmony_ci 33288c2ecf20Sopenharmony_ci if (count < rpt_exp) { 33298c2ecf20Sopenharmony_ci cdata->result = DASD_FMT_ERR_TOO_FEW_RECORDS; 33308c2ecf20Sopenharmony_ci break; 33318c2ecf20Sopenharmony_ci } 33328c2ecf20Sopenharmony_ci if (count > rpt_exp) { 33338c2ecf20Sopenharmony_ci cdata->result = DASD_FMT_ERR_TOO_MANY_RECORDS; 33348c2ecf20Sopenharmony_ci break; 33358c2ecf20Sopenharmony_ci } 33368c2ecf20Sopenharmony_ci 33378c2ecf20Sopenharmony_ci for (j = 0; j < count; j++, pos++) { 33388c2ecf20Sopenharmony_ci blksize = cdata->expect.blksize; 33398c2ecf20Sopenharmony_ci kl = 0; 33408c2ecf20Sopenharmony_ci 33418c2ecf20Sopenharmony_ci /* 33428c2ecf20Sopenharmony_ci * Set special values when checking CDL formatted 33438c2ecf20Sopenharmony_ci * devices. 33448c2ecf20Sopenharmony_ci */ 33458c2ecf20Sopenharmony_ci if ((cdata->expect.intensity & 0x08) && 33468c2ecf20Sopenharmony_ci geo.cyl == 0 && geo.head == 0) { 33478c2ecf20Sopenharmony_ci if (j < 3) { 33488c2ecf20Sopenharmony_ci blksize = sizes_trk0[j] - 4; 33498c2ecf20Sopenharmony_ci kl = 4; 33508c2ecf20Sopenharmony_ci } 33518c2ecf20Sopenharmony_ci } 33528c2ecf20Sopenharmony_ci if ((cdata->expect.intensity & 0x08) && 33538c2ecf20Sopenharmony_ci geo.cyl == 0 && geo.head == 1) { 33548c2ecf20Sopenharmony_ci blksize = LABEL_SIZE - 44; 33558c2ecf20Sopenharmony_ci kl = 44; 33568c2ecf20Sopenharmony_ci } 33578c2ecf20Sopenharmony_ci 33588c2ecf20Sopenharmony_ci /* Check blocksize */ 33598c2ecf20Sopenharmony_ci if (fmt_buffer[pos].dl != blksize) { 33608c2ecf20Sopenharmony_ci cdata->result = DASD_FMT_ERR_BLKSIZE; 33618c2ecf20Sopenharmony_ci goto out; 33628c2ecf20Sopenharmony_ci } 33638c2ecf20Sopenharmony_ci /* Check if key length is 0 */ 33648c2ecf20Sopenharmony_ci if (fmt_buffer[pos].kl != kl) { 33658c2ecf20Sopenharmony_ci cdata->result = DASD_FMT_ERR_KEY_LENGTH; 33668c2ecf20Sopenharmony_ci goto out; 33678c2ecf20Sopenharmony_ci } 33688c2ecf20Sopenharmony_ci /* Check if record_id is correct */ 33698c2ecf20Sopenharmony_ci if (fmt_buffer[pos].cyl != geo.cyl || 33708c2ecf20Sopenharmony_ci fmt_buffer[pos].head != geo.head || 33718c2ecf20Sopenharmony_ci fmt_buffer[pos].record != (j + 1)) { 33728c2ecf20Sopenharmony_ci cdata->result = DASD_FMT_ERR_RECORD_ID; 33738c2ecf20Sopenharmony_ci goto out; 33748c2ecf20Sopenharmony_ci } 33758c2ecf20Sopenharmony_ci } 33768c2ecf20Sopenharmony_ci } 33778c2ecf20Sopenharmony_ci 33788c2ecf20Sopenharmony_ciout: 33798c2ecf20Sopenharmony_ci /* 33808c2ecf20Sopenharmony_ci * In case of no errors, we need to decrease by one 33818c2ecf20Sopenharmony_ci * to get the correct positions. 33828c2ecf20Sopenharmony_ci */ 33838c2ecf20Sopenharmony_ci if (!cdata->result) { 33848c2ecf20Sopenharmony_ci i--; 33858c2ecf20Sopenharmony_ci pos--; 33868c2ecf20Sopenharmony_ci } 33878c2ecf20Sopenharmony_ci 33888c2ecf20Sopenharmony_ci cdata->unit = i; 33898c2ecf20Sopenharmony_ci cdata->num_records = count; 33908c2ecf20Sopenharmony_ci cdata->rec = fmt_buffer[pos].record; 33918c2ecf20Sopenharmony_ci cdata->blksize = fmt_buffer[pos].dl; 33928c2ecf20Sopenharmony_ci cdata->key_length = fmt_buffer[pos].kl; 33938c2ecf20Sopenharmony_ci} 33948c2ecf20Sopenharmony_ci 33958c2ecf20Sopenharmony_ci/* 33968c2ecf20Sopenharmony_ci * Check the format of a range of tracks of a DASD. 33978c2ecf20Sopenharmony_ci */ 33988c2ecf20Sopenharmony_cistatic int dasd_eckd_check_device_format(struct dasd_device *base, 33998c2ecf20Sopenharmony_ci struct format_check_t *cdata, 34008c2ecf20Sopenharmony_ci int enable_pav) 34018c2ecf20Sopenharmony_ci{ 34028c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = base->private; 34038c2ecf20Sopenharmony_ci struct eckd_count *fmt_buffer; 34048c2ecf20Sopenharmony_ci struct irb irb; 34058c2ecf20Sopenharmony_ci int rpt_max, rpt_exp; 34068c2ecf20Sopenharmony_ci int fmt_buffer_size; 34078c2ecf20Sopenharmony_ci int trk_per_cyl; 34088c2ecf20Sopenharmony_ci int trkcount; 34098c2ecf20Sopenharmony_ci int tpm = 0; 34108c2ecf20Sopenharmony_ci int rc; 34118c2ecf20Sopenharmony_ci 34128c2ecf20Sopenharmony_ci trk_per_cyl = private->rdc_data.trk_per_cyl; 34138c2ecf20Sopenharmony_ci 34148c2ecf20Sopenharmony_ci /* Get maximum and expected amount of records per track */ 34158c2ecf20Sopenharmony_ci rpt_max = recs_per_track(&private->rdc_data, 0, 512) + 1; 34168c2ecf20Sopenharmony_ci rpt_exp = recs_per_track(&private->rdc_data, 0, cdata->expect.blksize); 34178c2ecf20Sopenharmony_ci 34188c2ecf20Sopenharmony_ci trkcount = cdata->expect.stop_unit - cdata->expect.start_unit + 1; 34198c2ecf20Sopenharmony_ci fmt_buffer_size = trkcount * rpt_max * sizeof(struct eckd_count); 34208c2ecf20Sopenharmony_ci 34218c2ecf20Sopenharmony_ci fmt_buffer = kzalloc(fmt_buffer_size, GFP_KERNEL | GFP_DMA); 34228c2ecf20Sopenharmony_ci if (!fmt_buffer) 34238c2ecf20Sopenharmony_ci return -ENOMEM; 34248c2ecf20Sopenharmony_ci 34258c2ecf20Sopenharmony_ci /* 34268c2ecf20Sopenharmony_ci * A certain FICON feature subset is needed to operate in transport 34278c2ecf20Sopenharmony_ci * mode. Additionally, the support for transport mode is implicitly 34288c2ecf20Sopenharmony_ci * checked by comparing the buffer size with fcx_max_data. As long as 34298c2ecf20Sopenharmony_ci * the buffer size is smaller we can operate in transport mode and 34308c2ecf20Sopenharmony_ci * process multiple tracks. If not, only one track at once is being 34318c2ecf20Sopenharmony_ci * processed using command mode. 34328c2ecf20Sopenharmony_ci */ 34338c2ecf20Sopenharmony_ci if ((private->features.feature[40] & 0x04) && 34348c2ecf20Sopenharmony_ci fmt_buffer_size <= private->fcx_max_data) 34358c2ecf20Sopenharmony_ci tpm = 1; 34368c2ecf20Sopenharmony_ci 34378c2ecf20Sopenharmony_ci rc = dasd_eckd_format_process_data(base, &cdata->expect, enable_pav, 34388c2ecf20Sopenharmony_ci tpm, fmt_buffer, rpt_max, &irb); 34398c2ecf20Sopenharmony_ci if (rc && rc != -EIO) 34408c2ecf20Sopenharmony_ci goto out; 34418c2ecf20Sopenharmony_ci if (rc == -EIO) { 34428c2ecf20Sopenharmony_ci /* 34438c2ecf20Sopenharmony_ci * If our first attempt with transport mode enabled comes back 34448c2ecf20Sopenharmony_ci * with an incorrect length error, we're going to retry the 34458c2ecf20Sopenharmony_ci * check with command mode. 34468c2ecf20Sopenharmony_ci */ 34478c2ecf20Sopenharmony_ci if (tpm && scsw_cstat(&irb.scsw) == 0x40) { 34488c2ecf20Sopenharmony_ci tpm = 0; 34498c2ecf20Sopenharmony_ci rc = dasd_eckd_format_process_data(base, &cdata->expect, 34508c2ecf20Sopenharmony_ci enable_pav, tpm, 34518c2ecf20Sopenharmony_ci fmt_buffer, rpt_max, 34528c2ecf20Sopenharmony_ci &irb); 34538c2ecf20Sopenharmony_ci if (rc) 34548c2ecf20Sopenharmony_ci goto out; 34558c2ecf20Sopenharmony_ci } else { 34568c2ecf20Sopenharmony_ci goto out; 34578c2ecf20Sopenharmony_ci } 34588c2ecf20Sopenharmony_ci } 34598c2ecf20Sopenharmony_ci 34608c2ecf20Sopenharmony_ci dasd_eckd_format_evaluate_tracks(fmt_buffer, cdata, rpt_max, rpt_exp, 34618c2ecf20Sopenharmony_ci trk_per_cyl, tpm); 34628c2ecf20Sopenharmony_ci 34638c2ecf20Sopenharmony_ciout: 34648c2ecf20Sopenharmony_ci kfree(fmt_buffer); 34658c2ecf20Sopenharmony_ci 34668c2ecf20Sopenharmony_ci return rc; 34678c2ecf20Sopenharmony_ci} 34688c2ecf20Sopenharmony_ci 34698c2ecf20Sopenharmony_cistatic void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) 34708c2ecf20Sopenharmony_ci{ 34718c2ecf20Sopenharmony_ci if (cqr->retries < 0) { 34728c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FAILED; 34738c2ecf20Sopenharmony_ci return; 34748c2ecf20Sopenharmony_ci } 34758c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 34768c2ecf20Sopenharmony_ci if (cqr->block && (cqr->startdev != cqr->block->base)) { 34778c2ecf20Sopenharmony_ci dasd_eckd_reset_ccw_to_base_io(cqr); 34788c2ecf20Sopenharmony_ci cqr->startdev = cqr->block->base; 34798c2ecf20Sopenharmony_ci cqr->lpm = dasd_path_get_opm(cqr->block->base); 34808c2ecf20Sopenharmony_ci } 34818c2ecf20Sopenharmony_ci}; 34828c2ecf20Sopenharmony_ci 34838c2ecf20Sopenharmony_cistatic dasd_erp_fn_t 34848c2ecf20Sopenharmony_cidasd_eckd_erp_action(struct dasd_ccw_req * cqr) 34858c2ecf20Sopenharmony_ci{ 34868c2ecf20Sopenharmony_ci struct dasd_device *device = (struct dasd_device *) cqr->startdev; 34878c2ecf20Sopenharmony_ci struct ccw_device *cdev = device->cdev; 34888c2ecf20Sopenharmony_ci 34898c2ecf20Sopenharmony_ci switch (cdev->id.cu_type) { 34908c2ecf20Sopenharmony_ci case 0x3990: 34918c2ecf20Sopenharmony_ci case 0x2105: 34928c2ecf20Sopenharmony_ci case 0x2107: 34938c2ecf20Sopenharmony_ci case 0x1750: 34948c2ecf20Sopenharmony_ci return dasd_3990_erp_action; 34958c2ecf20Sopenharmony_ci case 0x9343: 34968c2ecf20Sopenharmony_ci case 0x3880: 34978c2ecf20Sopenharmony_ci default: 34988c2ecf20Sopenharmony_ci return dasd_default_erp_action; 34998c2ecf20Sopenharmony_ci } 35008c2ecf20Sopenharmony_ci} 35018c2ecf20Sopenharmony_ci 35028c2ecf20Sopenharmony_cistatic dasd_erp_fn_t 35038c2ecf20Sopenharmony_cidasd_eckd_erp_postaction(struct dasd_ccw_req * cqr) 35048c2ecf20Sopenharmony_ci{ 35058c2ecf20Sopenharmony_ci return dasd_default_erp_postaction; 35068c2ecf20Sopenharmony_ci} 35078c2ecf20Sopenharmony_ci 35088c2ecf20Sopenharmony_cistatic void dasd_eckd_check_for_device_change(struct dasd_device *device, 35098c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr, 35108c2ecf20Sopenharmony_ci struct irb *irb) 35118c2ecf20Sopenharmony_ci{ 35128c2ecf20Sopenharmony_ci char mask; 35138c2ecf20Sopenharmony_ci char *sense = NULL; 35148c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 35158c2ecf20Sopenharmony_ci 35168c2ecf20Sopenharmony_ci /* first of all check for state change pending interrupt */ 35178c2ecf20Sopenharmony_ci mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; 35188c2ecf20Sopenharmony_ci if ((scsw_dstat(&irb->scsw) & mask) == mask) { 35198c2ecf20Sopenharmony_ci /* 35208c2ecf20Sopenharmony_ci * for alias only, not in offline processing 35218c2ecf20Sopenharmony_ci * and only if not suspended 35228c2ecf20Sopenharmony_ci */ 35238c2ecf20Sopenharmony_ci if (!device->block && private->lcu && 35248c2ecf20Sopenharmony_ci device->state == DASD_STATE_ONLINE && 35258c2ecf20Sopenharmony_ci !test_bit(DASD_FLAG_OFFLINE, &device->flags) && 35268c2ecf20Sopenharmony_ci !test_bit(DASD_FLAG_SUSPENDED, &device->flags)) { 35278c2ecf20Sopenharmony_ci /* schedule worker to reload device */ 35288c2ecf20Sopenharmony_ci dasd_reload_device(device); 35298c2ecf20Sopenharmony_ci } 35308c2ecf20Sopenharmony_ci dasd_generic_handle_state_change(device); 35318c2ecf20Sopenharmony_ci return; 35328c2ecf20Sopenharmony_ci } 35338c2ecf20Sopenharmony_ci 35348c2ecf20Sopenharmony_ci sense = dasd_get_sense(irb); 35358c2ecf20Sopenharmony_ci if (!sense) 35368c2ecf20Sopenharmony_ci return; 35378c2ecf20Sopenharmony_ci 35388c2ecf20Sopenharmony_ci /* summary unit check */ 35398c2ecf20Sopenharmony_ci if ((sense[27] & DASD_SENSE_BIT_0) && (sense[7] == 0x0D) && 35408c2ecf20Sopenharmony_ci (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK)) { 35418c2ecf20Sopenharmony_ci if (test_and_set_bit(DASD_FLAG_SUC, &device->flags)) { 35428c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 35438c2ecf20Sopenharmony_ci "eckd suc: device already notified"); 35448c2ecf20Sopenharmony_ci return; 35458c2ecf20Sopenharmony_ci } 35468c2ecf20Sopenharmony_ci sense = dasd_get_sense(irb); 35478c2ecf20Sopenharmony_ci if (!sense) { 35488c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 35498c2ecf20Sopenharmony_ci "eckd suc: no reason code available"); 35508c2ecf20Sopenharmony_ci clear_bit(DASD_FLAG_SUC, &device->flags); 35518c2ecf20Sopenharmony_ci return; 35528c2ecf20Sopenharmony_ci 35538c2ecf20Sopenharmony_ci } 35548c2ecf20Sopenharmony_ci private->suc_reason = sense[8]; 35558c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_NOTICE, device, "%s %x", 35568c2ecf20Sopenharmony_ci "eckd handle summary unit check: reason", 35578c2ecf20Sopenharmony_ci private->suc_reason); 35588c2ecf20Sopenharmony_ci dasd_get_device(device); 35598c2ecf20Sopenharmony_ci if (!schedule_work(&device->suc_work)) 35608c2ecf20Sopenharmony_ci dasd_put_device(device); 35618c2ecf20Sopenharmony_ci 35628c2ecf20Sopenharmony_ci return; 35638c2ecf20Sopenharmony_ci } 35648c2ecf20Sopenharmony_ci 35658c2ecf20Sopenharmony_ci /* service information message SIM */ 35668c2ecf20Sopenharmony_ci if (!cqr && !(sense[27] & DASD_SENSE_BIT_0) && 35678c2ecf20Sopenharmony_ci ((sense[6] & DASD_SIM_SENSE) == DASD_SIM_SENSE)) { 35688c2ecf20Sopenharmony_ci dasd_3990_erp_handle_sim(device, sense); 35698c2ecf20Sopenharmony_ci return; 35708c2ecf20Sopenharmony_ci } 35718c2ecf20Sopenharmony_ci 35728c2ecf20Sopenharmony_ci /* loss of device reservation is handled via base devices only 35738c2ecf20Sopenharmony_ci * as alias devices may be used with several bases 35748c2ecf20Sopenharmony_ci */ 35758c2ecf20Sopenharmony_ci if (device->block && (sense[27] & DASD_SENSE_BIT_0) && 35768c2ecf20Sopenharmony_ci (sense[7] == 0x3F) && 35778c2ecf20Sopenharmony_ci (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK) && 35788c2ecf20Sopenharmony_ci test_bit(DASD_FLAG_IS_RESERVED, &device->flags)) { 35798c2ecf20Sopenharmony_ci if (device->features & DASD_FEATURE_FAILONSLCK) 35808c2ecf20Sopenharmony_ci set_bit(DASD_FLAG_LOCK_STOLEN, &device->flags); 35818c2ecf20Sopenharmony_ci clear_bit(DASD_FLAG_IS_RESERVED, &device->flags); 35828c2ecf20Sopenharmony_ci dev_err(&device->cdev->dev, 35838c2ecf20Sopenharmony_ci "The device reservation was lost\n"); 35848c2ecf20Sopenharmony_ci } 35858c2ecf20Sopenharmony_ci} 35868c2ecf20Sopenharmony_ci 35878c2ecf20Sopenharmony_cistatic int dasd_eckd_ras_sanity_checks(struct dasd_device *device, 35888c2ecf20Sopenharmony_ci unsigned int first_trk, 35898c2ecf20Sopenharmony_ci unsigned int last_trk) 35908c2ecf20Sopenharmony_ci{ 35918c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 35928c2ecf20Sopenharmony_ci unsigned int trks_per_vol; 35938c2ecf20Sopenharmony_ci int rc = 0; 35948c2ecf20Sopenharmony_ci 35958c2ecf20Sopenharmony_ci trks_per_vol = private->real_cyl * private->rdc_data.trk_per_cyl; 35968c2ecf20Sopenharmony_ci 35978c2ecf20Sopenharmony_ci if (first_trk >= trks_per_vol) { 35988c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, 35998c2ecf20Sopenharmony_ci "Start track number %u used in the space release command is too big\n", 36008c2ecf20Sopenharmony_ci first_trk); 36018c2ecf20Sopenharmony_ci rc = -EINVAL; 36028c2ecf20Sopenharmony_ci } else if (last_trk >= trks_per_vol) { 36038c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, 36048c2ecf20Sopenharmony_ci "Stop track number %u used in the space release command is too big\n", 36058c2ecf20Sopenharmony_ci last_trk); 36068c2ecf20Sopenharmony_ci rc = -EINVAL; 36078c2ecf20Sopenharmony_ci } else if (first_trk > last_trk) { 36088c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, 36098c2ecf20Sopenharmony_ci "Start track %u used in the space release command exceeds the end track\n", 36108c2ecf20Sopenharmony_ci first_trk); 36118c2ecf20Sopenharmony_ci rc = -EINVAL; 36128c2ecf20Sopenharmony_ci } 36138c2ecf20Sopenharmony_ci return rc; 36148c2ecf20Sopenharmony_ci} 36158c2ecf20Sopenharmony_ci 36168c2ecf20Sopenharmony_ci/* 36178c2ecf20Sopenharmony_ci * Helper function to count the amount of involved extents within a given range 36188c2ecf20Sopenharmony_ci * with extent alignment in mind. 36198c2ecf20Sopenharmony_ci */ 36208c2ecf20Sopenharmony_cistatic int count_exts(unsigned int from, unsigned int to, int trks_per_ext) 36218c2ecf20Sopenharmony_ci{ 36228c2ecf20Sopenharmony_ci int cur_pos = 0; 36238c2ecf20Sopenharmony_ci int count = 0; 36248c2ecf20Sopenharmony_ci int tmp; 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_ci if (from == to) 36278c2ecf20Sopenharmony_ci return 1; 36288c2ecf20Sopenharmony_ci 36298c2ecf20Sopenharmony_ci /* Count first partial extent */ 36308c2ecf20Sopenharmony_ci if (from % trks_per_ext != 0) { 36318c2ecf20Sopenharmony_ci tmp = from + trks_per_ext - (from % trks_per_ext) - 1; 36328c2ecf20Sopenharmony_ci if (tmp > to) 36338c2ecf20Sopenharmony_ci tmp = to; 36348c2ecf20Sopenharmony_ci cur_pos = tmp - from + 1; 36358c2ecf20Sopenharmony_ci count++; 36368c2ecf20Sopenharmony_ci } 36378c2ecf20Sopenharmony_ci /* Count full extents */ 36388c2ecf20Sopenharmony_ci if (to - (from + cur_pos) + 1 >= trks_per_ext) { 36398c2ecf20Sopenharmony_ci tmp = to - ((to - trks_per_ext + 1) % trks_per_ext); 36408c2ecf20Sopenharmony_ci count += (tmp - (from + cur_pos) + 1) / trks_per_ext; 36418c2ecf20Sopenharmony_ci cur_pos = tmp; 36428c2ecf20Sopenharmony_ci } 36438c2ecf20Sopenharmony_ci /* Count last partial extent */ 36448c2ecf20Sopenharmony_ci if (cur_pos < to) 36458c2ecf20Sopenharmony_ci count++; 36468c2ecf20Sopenharmony_ci 36478c2ecf20Sopenharmony_ci return count; 36488c2ecf20Sopenharmony_ci} 36498c2ecf20Sopenharmony_ci 36508c2ecf20Sopenharmony_ci/* 36518c2ecf20Sopenharmony_ci * Release allocated space for a given range or an entire volume. 36528c2ecf20Sopenharmony_ci */ 36538c2ecf20Sopenharmony_cistatic struct dasd_ccw_req * 36548c2ecf20Sopenharmony_cidasd_eckd_dso_ras(struct dasd_device *device, struct dasd_block *block, 36558c2ecf20Sopenharmony_ci struct request *req, unsigned int first_trk, 36568c2ecf20Sopenharmony_ci unsigned int last_trk, int by_extent) 36578c2ecf20Sopenharmony_ci{ 36588c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 36598c2ecf20Sopenharmony_ci struct dasd_dso_ras_ext_range *ras_range; 36608c2ecf20Sopenharmony_ci struct dasd_rssd_features *features; 36618c2ecf20Sopenharmony_ci struct dasd_dso_ras_data *ras_data; 36628c2ecf20Sopenharmony_ci u16 heads, beg_head, end_head; 36638c2ecf20Sopenharmony_ci int cur_to_trk, cur_from_trk; 36648c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 36658c2ecf20Sopenharmony_ci u32 beg_cyl, end_cyl; 36668c2ecf20Sopenharmony_ci struct ccw1 *ccw; 36678c2ecf20Sopenharmony_ci int trks_per_ext; 36688c2ecf20Sopenharmony_ci size_t ras_size; 36698c2ecf20Sopenharmony_ci size_t size; 36708c2ecf20Sopenharmony_ci int nr_exts; 36718c2ecf20Sopenharmony_ci void *rq; 36728c2ecf20Sopenharmony_ci int i; 36738c2ecf20Sopenharmony_ci 36748c2ecf20Sopenharmony_ci if (dasd_eckd_ras_sanity_checks(device, first_trk, last_trk)) 36758c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 36768c2ecf20Sopenharmony_ci 36778c2ecf20Sopenharmony_ci rq = req ? blk_mq_rq_to_pdu(req) : NULL; 36788c2ecf20Sopenharmony_ci 36798c2ecf20Sopenharmony_ci features = &private->features; 36808c2ecf20Sopenharmony_ci 36818c2ecf20Sopenharmony_ci trks_per_ext = dasd_eckd_ext_size(device) * private->rdc_data.trk_per_cyl; 36828c2ecf20Sopenharmony_ci nr_exts = 0; 36838c2ecf20Sopenharmony_ci if (by_extent) 36848c2ecf20Sopenharmony_ci nr_exts = count_exts(first_trk, last_trk, trks_per_ext); 36858c2ecf20Sopenharmony_ci ras_size = sizeof(*ras_data); 36868c2ecf20Sopenharmony_ci size = ras_size + (nr_exts * sizeof(*ras_range)); 36878c2ecf20Sopenharmony_ci 36888c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, size, device, rq); 36898c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 36908c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 36918c2ecf20Sopenharmony_ci "Could not allocate RAS request"); 36928c2ecf20Sopenharmony_ci return cqr; 36938c2ecf20Sopenharmony_ci } 36948c2ecf20Sopenharmony_ci 36958c2ecf20Sopenharmony_ci ras_data = cqr->data; 36968c2ecf20Sopenharmony_ci memset(ras_data, 0, size); 36978c2ecf20Sopenharmony_ci 36988c2ecf20Sopenharmony_ci ras_data->order = DSO_ORDER_RAS; 36998c2ecf20Sopenharmony_ci ras_data->flags.vol_type = 0; /* CKD volume */ 37008c2ecf20Sopenharmony_ci /* Release specified extents or entire volume */ 37018c2ecf20Sopenharmony_ci ras_data->op_flags.by_extent = by_extent; 37028c2ecf20Sopenharmony_ci /* 37038c2ecf20Sopenharmony_ci * This bit guarantees initialisation of tracks within an extent that is 37048c2ecf20Sopenharmony_ci * not fully specified, but is only supported with a certain feature 37058c2ecf20Sopenharmony_ci * subset. 37068c2ecf20Sopenharmony_ci */ 37078c2ecf20Sopenharmony_ci ras_data->op_flags.guarantee_init = !!(features->feature[56] & 0x01); 37088c2ecf20Sopenharmony_ci ras_data->lss = private->ned->ID; 37098c2ecf20Sopenharmony_ci ras_data->dev_addr = private->ned->unit_addr; 37108c2ecf20Sopenharmony_ci ras_data->nr_exts = nr_exts; 37118c2ecf20Sopenharmony_ci 37128c2ecf20Sopenharmony_ci if (by_extent) { 37138c2ecf20Sopenharmony_ci heads = private->rdc_data.trk_per_cyl; 37148c2ecf20Sopenharmony_ci cur_from_trk = first_trk; 37158c2ecf20Sopenharmony_ci cur_to_trk = first_trk + trks_per_ext - 37168c2ecf20Sopenharmony_ci (first_trk % trks_per_ext) - 1; 37178c2ecf20Sopenharmony_ci if (cur_to_trk > last_trk) 37188c2ecf20Sopenharmony_ci cur_to_trk = last_trk; 37198c2ecf20Sopenharmony_ci ras_range = (struct dasd_dso_ras_ext_range *)(cqr->data + ras_size); 37208c2ecf20Sopenharmony_ci 37218c2ecf20Sopenharmony_ci for (i = 0; i < nr_exts; i++) { 37228c2ecf20Sopenharmony_ci beg_cyl = cur_from_trk / heads; 37238c2ecf20Sopenharmony_ci beg_head = cur_from_trk % heads; 37248c2ecf20Sopenharmony_ci end_cyl = cur_to_trk / heads; 37258c2ecf20Sopenharmony_ci end_head = cur_to_trk % heads; 37268c2ecf20Sopenharmony_ci 37278c2ecf20Sopenharmony_ci set_ch_t(&ras_range->beg_ext, beg_cyl, beg_head); 37288c2ecf20Sopenharmony_ci set_ch_t(&ras_range->end_ext, end_cyl, end_head); 37298c2ecf20Sopenharmony_ci 37308c2ecf20Sopenharmony_ci cur_from_trk = cur_to_trk + 1; 37318c2ecf20Sopenharmony_ci cur_to_trk = cur_from_trk + trks_per_ext - 1; 37328c2ecf20Sopenharmony_ci if (cur_to_trk > last_trk) 37338c2ecf20Sopenharmony_ci cur_to_trk = last_trk; 37348c2ecf20Sopenharmony_ci ras_range++; 37358c2ecf20Sopenharmony_ci } 37368c2ecf20Sopenharmony_ci } 37378c2ecf20Sopenharmony_ci 37388c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 37398c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t)cqr->data; 37408c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_DSO; 37418c2ecf20Sopenharmony_ci ccw->count = size; 37428c2ecf20Sopenharmony_ci 37438c2ecf20Sopenharmony_ci cqr->startdev = device; 37448c2ecf20Sopenharmony_ci cqr->memdev = device; 37458c2ecf20Sopenharmony_ci cqr->block = block; 37468c2ecf20Sopenharmony_ci cqr->retries = 256; 37478c2ecf20Sopenharmony_ci cqr->expires = device->default_expires * HZ; 37488c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 37498c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 37508c2ecf20Sopenharmony_ci 37518c2ecf20Sopenharmony_ci return cqr; 37528c2ecf20Sopenharmony_ci} 37538c2ecf20Sopenharmony_ci 37548c2ecf20Sopenharmony_cistatic int dasd_eckd_release_space_full(struct dasd_device *device) 37558c2ecf20Sopenharmony_ci{ 37568c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 37578c2ecf20Sopenharmony_ci int rc; 37588c2ecf20Sopenharmony_ci 37598c2ecf20Sopenharmony_ci cqr = dasd_eckd_dso_ras(device, NULL, NULL, 0, 0, 0); 37608c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) 37618c2ecf20Sopenharmony_ci return PTR_ERR(cqr); 37628c2ecf20Sopenharmony_ci 37638c2ecf20Sopenharmony_ci rc = dasd_sleep_on_interruptible(cqr); 37648c2ecf20Sopenharmony_ci 37658c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 37668c2ecf20Sopenharmony_ci 37678c2ecf20Sopenharmony_ci return rc; 37688c2ecf20Sopenharmony_ci} 37698c2ecf20Sopenharmony_ci 37708c2ecf20Sopenharmony_cistatic int dasd_eckd_release_space_trks(struct dasd_device *device, 37718c2ecf20Sopenharmony_ci unsigned int from, unsigned int to) 37728c2ecf20Sopenharmony_ci{ 37738c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 37748c2ecf20Sopenharmony_ci struct dasd_block *block = device->block; 37758c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr, *n; 37768c2ecf20Sopenharmony_ci struct list_head ras_queue; 37778c2ecf20Sopenharmony_ci unsigned int device_exts; 37788c2ecf20Sopenharmony_ci int trks_per_ext; 37798c2ecf20Sopenharmony_ci int stop, step; 37808c2ecf20Sopenharmony_ci int cur_pos; 37818c2ecf20Sopenharmony_ci int rc = 0; 37828c2ecf20Sopenharmony_ci int retry; 37838c2ecf20Sopenharmony_ci 37848c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ras_queue); 37858c2ecf20Sopenharmony_ci 37868c2ecf20Sopenharmony_ci device_exts = private->real_cyl / dasd_eckd_ext_size(device); 37878c2ecf20Sopenharmony_ci trks_per_ext = dasd_eckd_ext_size(device) * private->rdc_data.trk_per_cyl; 37888c2ecf20Sopenharmony_ci 37898c2ecf20Sopenharmony_ci /* Make sure device limits are not exceeded */ 37908c2ecf20Sopenharmony_ci step = trks_per_ext * min(device_exts, DASD_ECKD_RAS_EXTS_MAX); 37918c2ecf20Sopenharmony_ci cur_pos = from; 37928c2ecf20Sopenharmony_ci 37938c2ecf20Sopenharmony_ci do { 37948c2ecf20Sopenharmony_ci retry = 0; 37958c2ecf20Sopenharmony_ci while (cur_pos < to) { 37968c2ecf20Sopenharmony_ci stop = cur_pos + step - 37978c2ecf20Sopenharmony_ci ((cur_pos + step) % trks_per_ext) - 1; 37988c2ecf20Sopenharmony_ci if (stop > to) 37998c2ecf20Sopenharmony_ci stop = to; 38008c2ecf20Sopenharmony_ci 38018c2ecf20Sopenharmony_ci cqr = dasd_eckd_dso_ras(device, NULL, NULL, cur_pos, stop, 1); 38028c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 38038c2ecf20Sopenharmony_ci rc = PTR_ERR(cqr); 38048c2ecf20Sopenharmony_ci if (rc == -ENOMEM) { 38058c2ecf20Sopenharmony_ci if (list_empty(&ras_queue)) 38068c2ecf20Sopenharmony_ci goto out; 38078c2ecf20Sopenharmony_ci retry = 1; 38088c2ecf20Sopenharmony_ci break; 38098c2ecf20Sopenharmony_ci } 38108c2ecf20Sopenharmony_ci goto err_out; 38118c2ecf20Sopenharmony_ci } 38128c2ecf20Sopenharmony_ci 38138c2ecf20Sopenharmony_ci spin_lock_irq(&block->queue_lock); 38148c2ecf20Sopenharmony_ci list_add_tail(&cqr->blocklist, &ras_queue); 38158c2ecf20Sopenharmony_ci spin_unlock_irq(&block->queue_lock); 38168c2ecf20Sopenharmony_ci cur_pos = stop + 1; 38178c2ecf20Sopenharmony_ci } 38188c2ecf20Sopenharmony_ci 38198c2ecf20Sopenharmony_ci rc = dasd_sleep_on_queue_interruptible(&ras_queue); 38208c2ecf20Sopenharmony_ci 38218c2ecf20Sopenharmony_cierr_out: 38228c2ecf20Sopenharmony_ci list_for_each_entry_safe(cqr, n, &ras_queue, blocklist) { 38238c2ecf20Sopenharmony_ci device = cqr->startdev; 38248c2ecf20Sopenharmony_ci private = device->private; 38258c2ecf20Sopenharmony_ci 38268c2ecf20Sopenharmony_ci spin_lock_irq(&block->queue_lock); 38278c2ecf20Sopenharmony_ci list_del_init(&cqr->blocklist); 38288c2ecf20Sopenharmony_ci spin_unlock_irq(&block->queue_lock); 38298c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, device); 38308c2ecf20Sopenharmony_ci private->count--; 38318c2ecf20Sopenharmony_ci } 38328c2ecf20Sopenharmony_ci } while (retry); 38338c2ecf20Sopenharmony_ci 38348c2ecf20Sopenharmony_ciout: 38358c2ecf20Sopenharmony_ci return rc; 38368c2ecf20Sopenharmony_ci} 38378c2ecf20Sopenharmony_ci 38388c2ecf20Sopenharmony_cistatic int dasd_eckd_release_space(struct dasd_device *device, 38398c2ecf20Sopenharmony_ci struct format_data_t *rdata) 38408c2ecf20Sopenharmony_ci{ 38418c2ecf20Sopenharmony_ci if (rdata->intensity & DASD_FMT_INT_ESE_FULL) 38428c2ecf20Sopenharmony_ci return dasd_eckd_release_space_full(device); 38438c2ecf20Sopenharmony_ci else if (rdata->intensity == 0) 38448c2ecf20Sopenharmony_ci return dasd_eckd_release_space_trks(device, rdata->start_unit, 38458c2ecf20Sopenharmony_ci rdata->stop_unit); 38468c2ecf20Sopenharmony_ci else 38478c2ecf20Sopenharmony_ci return -EINVAL; 38488c2ecf20Sopenharmony_ci} 38498c2ecf20Sopenharmony_ci 38508c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( 38518c2ecf20Sopenharmony_ci struct dasd_device *startdev, 38528c2ecf20Sopenharmony_ci struct dasd_block *block, 38538c2ecf20Sopenharmony_ci struct request *req, 38548c2ecf20Sopenharmony_ci sector_t first_rec, 38558c2ecf20Sopenharmony_ci sector_t last_rec, 38568c2ecf20Sopenharmony_ci sector_t first_trk, 38578c2ecf20Sopenharmony_ci sector_t last_trk, 38588c2ecf20Sopenharmony_ci unsigned int first_offs, 38598c2ecf20Sopenharmony_ci unsigned int last_offs, 38608c2ecf20Sopenharmony_ci unsigned int blk_per_trk, 38618c2ecf20Sopenharmony_ci unsigned int blksize) 38628c2ecf20Sopenharmony_ci{ 38638c2ecf20Sopenharmony_ci struct dasd_eckd_private *private; 38648c2ecf20Sopenharmony_ci unsigned long *idaws; 38658c2ecf20Sopenharmony_ci struct LO_eckd_data *LO_data; 38668c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 38678c2ecf20Sopenharmony_ci struct ccw1 *ccw; 38688c2ecf20Sopenharmony_ci struct req_iterator iter; 38698c2ecf20Sopenharmony_ci struct bio_vec bv; 38708c2ecf20Sopenharmony_ci char *dst; 38718c2ecf20Sopenharmony_ci unsigned int off; 38728c2ecf20Sopenharmony_ci int count, cidaw, cplength, datasize; 38738c2ecf20Sopenharmony_ci sector_t recid; 38748c2ecf20Sopenharmony_ci unsigned char cmd, rcmd; 38758c2ecf20Sopenharmony_ci int use_prefix; 38768c2ecf20Sopenharmony_ci struct dasd_device *basedev; 38778c2ecf20Sopenharmony_ci 38788c2ecf20Sopenharmony_ci basedev = block->base; 38798c2ecf20Sopenharmony_ci private = basedev->private; 38808c2ecf20Sopenharmony_ci if (rq_data_dir(req) == READ) 38818c2ecf20Sopenharmony_ci cmd = DASD_ECKD_CCW_READ_MT; 38828c2ecf20Sopenharmony_ci else if (rq_data_dir(req) == WRITE) 38838c2ecf20Sopenharmony_ci cmd = DASD_ECKD_CCW_WRITE_MT; 38848c2ecf20Sopenharmony_ci else 38858c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 38868c2ecf20Sopenharmony_ci 38878c2ecf20Sopenharmony_ci /* Check struct bio and count the number of blocks for the request. */ 38888c2ecf20Sopenharmony_ci count = 0; 38898c2ecf20Sopenharmony_ci cidaw = 0; 38908c2ecf20Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 38918c2ecf20Sopenharmony_ci if (bv.bv_len & (blksize - 1)) 38928c2ecf20Sopenharmony_ci /* Eckd can only do full blocks. */ 38938c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 38948c2ecf20Sopenharmony_ci count += bv.bv_len >> (block->s2b_shift + 9); 38958c2ecf20Sopenharmony_ci if (idal_is_needed (page_address(bv.bv_page), bv.bv_len)) 38968c2ecf20Sopenharmony_ci cidaw += bv.bv_len >> (block->s2b_shift + 9); 38978c2ecf20Sopenharmony_ci } 38988c2ecf20Sopenharmony_ci /* Paranoia. */ 38998c2ecf20Sopenharmony_ci if (count != last_rec - first_rec + 1) 39008c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 39018c2ecf20Sopenharmony_ci 39028c2ecf20Sopenharmony_ci /* use the prefix command if available */ 39038c2ecf20Sopenharmony_ci use_prefix = private->features.feature[8] & 0x01; 39048c2ecf20Sopenharmony_ci if (use_prefix) { 39058c2ecf20Sopenharmony_ci /* 1x prefix + number of blocks */ 39068c2ecf20Sopenharmony_ci cplength = 2 + count; 39078c2ecf20Sopenharmony_ci /* 1x prefix + cidaws*sizeof(long) */ 39088c2ecf20Sopenharmony_ci datasize = sizeof(struct PFX_eckd_data) + 39098c2ecf20Sopenharmony_ci sizeof(struct LO_eckd_data) + 39108c2ecf20Sopenharmony_ci cidaw * sizeof(unsigned long); 39118c2ecf20Sopenharmony_ci } else { 39128c2ecf20Sopenharmony_ci /* 1x define extent + 1x locate record + number of blocks */ 39138c2ecf20Sopenharmony_ci cplength = 2 + count; 39148c2ecf20Sopenharmony_ci /* 1x define extent + 1x locate record + cidaws*sizeof(long) */ 39158c2ecf20Sopenharmony_ci datasize = sizeof(struct DE_eckd_data) + 39168c2ecf20Sopenharmony_ci sizeof(struct LO_eckd_data) + 39178c2ecf20Sopenharmony_ci cidaw * sizeof(unsigned long); 39188c2ecf20Sopenharmony_ci } 39198c2ecf20Sopenharmony_ci /* Find out the number of additional locate record ccws for cdl. */ 39208c2ecf20Sopenharmony_ci if (private->uses_cdl && first_rec < 2*blk_per_trk) { 39218c2ecf20Sopenharmony_ci if (last_rec >= 2*blk_per_trk) 39228c2ecf20Sopenharmony_ci count = 2*blk_per_trk - first_rec; 39238c2ecf20Sopenharmony_ci cplength += count; 39248c2ecf20Sopenharmony_ci datasize += count*sizeof(struct LO_eckd_data); 39258c2ecf20Sopenharmony_ci } 39268c2ecf20Sopenharmony_ci /* Allocate the ccw request. */ 39278c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, 39288c2ecf20Sopenharmony_ci startdev, blk_mq_rq_to_pdu(req)); 39298c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) 39308c2ecf20Sopenharmony_ci return cqr; 39318c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 39328c2ecf20Sopenharmony_ci /* First ccw is define extent or prefix. */ 39338c2ecf20Sopenharmony_ci if (use_prefix) { 39348c2ecf20Sopenharmony_ci if (prefix(ccw++, cqr->data, first_trk, 39358c2ecf20Sopenharmony_ci last_trk, cmd, basedev, startdev) == -EAGAIN) { 39368c2ecf20Sopenharmony_ci /* Clock not in sync and XRC is enabled. 39378c2ecf20Sopenharmony_ci * Try again later. 39388c2ecf20Sopenharmony_ci */ 39398c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, startdev); 39408c2ecf20Sopenharmony_ci return ERR_PTR(-EAGAIN); 39418c2ecf20Sopenharmony_ci } 39428c2ecf20Sopenharmony_ci idaws = (unsigned long *) (cqr->data + 39438c2ecf20Sopenharmony_ci sizeof(struct PFX_eckd_data)); 39448c2ecf20Sopenharmony_ci } else { 39458c2ecf20Sopenharmony_ci if (define_extent(ccw++, cqr->data, first_trk, 39468c2ecf20Sopenharmony_ci last_trk, cmd, basedev, 0) == -EAGAIN) { 39478c2ecf20Sopenharmony_ci /* Clock not in sync and XRC is enabled. 39488c2ecf20Sopenharmony_ci * Try again later. 39498c2ecf20Sopenharmony_ci */ 39508c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, startdev); 39518c2ecf20Sopenharmony_ci return ERR_PTR(-EAGAIN); 39528c2ecf20Sopenharmony_ci } 39538c2ecf20Sopenharmony_ci idaws = (unsigned long *) (cqr->data + 39548c2ecf20Sopenharmony_ci sizeof(struct DE_eckd_data)); 39558c2ecf20Sopenharmony_ci } 39568c2ecf20Sopenharmony_ci /* Build locate_record+read/write/ccws. */ 39578c2ecf20Sopenharmony_ci LO_data = (struct LO_eckd_data *) (idaws + cidaw); 39588c2ecf20Sopenharmony_ci recid = first_rec; 39598c2ecf20Sopenharmony_ci if (private->uses_cdl == 0 || recid > 2*blk_per_trk) { 39608c2ecf20Sopenharmony_ci /* Only standard blocks so there is just one locate record. */ 39618c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 39628c2ecf20Sopenharmony_ci locate_record(ccw++, LO_data++, first_trk, first_offs + 1, 39638c2ecf20Sopenharmony_ci last_rec - recid + 1, cmd, basedev, blksize); 39648c2ecf20Sopenharmony_ci } 39658c2ecf20Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 39668c2ecf20Sopenharmony_ci dst = page_address(bv.bv_page) + bv.bv_offset; 39678c2ecf20Sopenharmony_ci if (dasd_page_cache) { 39688c2ecf20Sopenharmony_ci char *copy = kmem_cache_alloc(dasd_page_cache, 39698c2ecf20Sopenharmony_ci GFP_DMA | __GFP_NOWARN); 39708c2ecf20Sopenharmony_ci if (copy && rq_data_dir(req) == WRITE) 39718c2ecf20Sopenharmony_ci memcpy(copy + bv.bv_offset, dst, bv.bv_len); 39728c2ecf20Sopenharmony_ci if (copy) 39738c2ecf20Sopenharmony_ci dst = copy + bv.bv_offset; 39748c2ecf20Sopenharmony_ci } 39758c2ecf20Sopenharmony_ci for (off = 0; off < bv.bv_len; off += blksize) { 39768c2ecf20Sopenharmony_ci sector_t trkid = recid; 39778c2ecf20Sopenharmony_ci unsigned int recoffs = sector_div(trkid, blk_per_trk); 39788c2ecf20Sopenharmony_ci rcmd = cmd; 39798c2ecf20Sopenharmony_ci count = blksize; 39808c2ecf20Sopenharmony_ci /* Locate record for cdl special block ? */ 39818c2ecf20Sopenharmony_ci if (private->uses_cdl && recid < 2*blk_per_trk) { 39828c2ecf20Sopenharmony_ci if (dasd_eckd_cdl_special(blk_per_trk, recid)){ 39838c2ecf20Sopenharmony_ci rcmd |= 0x8; 39848c2ecf20Sopenharmony_ci count = dasd_eckd_cdl_reclen(recid); 39858c2ecf20Sopenharmony_ci if (count < blksize && 39868c2ecf20Sopenharmony_ci rq_data_dir(req) == READ) 39878c2ecf20Sopenharmony_ci memset(dst + count, 0xe5, 39888c2ecf20Sopenharmony_ci blksize - count); 39898c2ecf20Sopenharmony_ci } 39908c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 39918c2ecf20Sopenharmony_ci locate_record(ccw++, LO_data++, 39928c2ecf20Sopenharmony_ci trkid, recoffs + 1, 39938c2ecf20Sopenharmony_ci 1, rcmd, basedev, count); 39948c2ecf20Sopenharmony_ci } 39958c2ecf20Sopenharmony_ci /* Locate record for standard blocks ? */ 39968c2ecf20Sopenharmony_ci if (private->uses_cdl && recid == 2*blk_per_trk) { 39978c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 39988c2ecf20Sopenharmony_ci locate_record(ccw++, LO_data++, 39998c2ecf20Sopenharmony_ci trkid, recoffs + 1, 40008c2ecf20Sopenharmony_ci last_rec - recid + 1, 40018c2ecf20Sopenharmony_ci cmd, basedev, count); 40028c2ecf20Sopenharmony_ci } 40038c2ecf20Sopenharmony_ci /* Read/write ccw. */ 40048c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 40058c2ecf20Sopenharmony_ci ccw->cmd_code = rcmd; 40068c2ecf20Sopenharmony_ci ccw->count = count; 40078c2ecf20Sopenharmony_ci if (idal_is_needed(dst, blksize)) { 40088c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) idaws; 40098c2ecf20Sopenharmony_ci ccw->flags = CCW_FLAG_IDA; 40108c2ecf20Sopenharmony_ci idaws = idal_create_words(idaws, dst, blksize); 40118c2ecf20Sopenharmony_ci } else { 40128c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) dst; 40138c2ecf20Sopenharmony_ci ccw->flags = 0; 40148c2ecf20Sopenharmony_ci } 40158c2ecf20Sopenharmony_ci ccw++; 40168c2ecf20Sopenharmony_ci dst += blksize; 40178c2ecf20Sopenharmony_ci recid++; 40188c2ecf20Sopenharmony_ci } 40198c2ecf20Sopenharmony_ci } 40208c2ecf20Sopenharmony_ci if (blk_noretry_request(req) || 40218c2ecf20Sopenharmony_ci block->base->features & DASD_FEATURE_FAILFAST) 40228c2ecf20Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 40238c2ecf20Sopenharmony_ci cqr->startdev = startdev; 40248c2ecf20Sopenharmony_ci cqr->memdev = startdev; 40258c2ecf20Sopenharmony_ci cqr->block = block; 40268c2ecf20Sopenharmony_ci cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ 40278c2ecf20Sopenharmony_ci cqr->lpm = dasd_path_get_ppm(startdev); 40288c2ecf20Sopenharmony_ci cqr->retries = startdev->default_retries; 40298c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 40308c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 40318c2ecf20Sopenharmony_ci 40328c2ecf20Sopenharmony_ci /* Set flags to suppress output for expected errors */ 40338c2ecf20Sopenharmony_ci if (dasd_eckd_is_ese(basedev)) { 40348c2ecf20Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags); 40358c2ecf20Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags); 40368c2ecf20Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags); 40378c2ecf20Sopenharmony_ci } 40388c2ecf20Sopenharmony_ci 40398c2ecf20Sopenharmony_ci return cqr; 40408c2ecf20Sopenharmony_ci} 40418c2ecf20Sopenharmony_ci 40428c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( 40438c2ecf20Sopenharmony_ci struct dasd_device *startdev, 40448c2ecf20Sopenharmony_ci struct dasd_block *block, 40458c2ecf20Sopenharmony_ci struct request *req, 40468c2ecf20Sopenharmony_ci sector_t first_rec, 40478c2ecf20Sopenharmony_ci sector_t last_rec, 40488c2ecf20Sopenharmony_ci sector_t first_trk, 40498c2ecf20Sopenharmony_ci sector_t last_trk, 40508c2ecf20Sopenharmony_ci unsigned int first_offs, 40518c2ecf20Sopenharmony_ci unsigned int last_offs, 40528c2ecf20Sopenharmony_ci unsigned int blk_per_trk, 40538c2ecf20Sopenharmony_ci unsigned int blksize) 40548c2ecf20Sopenharmony_ci{ 40558c2ecf20Sopenharmony_ci unsigned long *idaws; 40568c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 40578c2ecf20Sopenharmony_ci struct ccw1 *ccw; 40588c2ecf20Sopenharmony_ci struct req_iterator iter; 40598c2ecf20Sopenharmony_ci struct bio_vec bv; 40608c2ecf20Sopenharmony_ci char *dst, *idaw_dst; 40618c2ecf20Sopenharmony_ci unsigned int cidaw, cplength, datasize; 40628c2ecf20Sopenharmony_ci unsigned int tlf; 40638c2ecf20Sopenharmony_ci sector_t recid; 40648c2ecf20Sopenharmony_ci unsigned char cmd; 40658c2ecf20Sopenharmony_ci struct dasd_device *basedev; 40668c2ecf20Sopenharmony_ci unsigned int trkcount, count, count_to_trk_end; 40678c2ecf20Sopenharmony_ci unsigned int idaw_len, seg_len, part_len, len_to_track_end; 40688c2ecf20Sopenharmony_ci unsigned char new_track, end_idaw; 40698c2ecf20Sopenharmony_ci sector_t trkid; 40708c2ecf20Sopenharmony_ci unsigned int recoffs; 40718c2ecf20Sopenharmony_ci 40728c2ecf20Sopenharmony_ci basedev = block->base; 40738c2ecf20Sopenharmony_ci if (rq_data_dir(req) == READ) 40748c2ecf20Sopenharmony_ci cmd = DASD_ECKD_CCW_READ_TRACK_DATA; 40758c2ecf20Sopenharmony_ci else if (rq_data_dir(req) == WRITE) 40768c2ecf20Sopenharmony_ci cmd = DASD_ECKD_CCW_WRITE_TRACK_DATA; 40778c2ecf20Sopenharmony_ci else 40788c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 40798c2ecf20Sopenharmony_ci 40808c2ecf20Sopenharmony_ci /* Track based I/O needs IDAWs for each page, and not just for 40818c2ecf20Sopenharmony_ci * 64 bit addresses. We need additional idals for pages 40828c2ecf20Sopenharmony_ci * that get filled from two tracks, so we use the number 40838c2ecf20Sopenharmony_ci * of records as upper limit. 40848c2ecf20Sopenharmony_ci */ 40858c2ecf20Sopenharmony_ci cidaw = last_rec - first_rec + 1; 40868c2ecf20Sopenharmony_ci trkcount = last_trk - first_trk + 1; 40878c2ecf20Sopenharmony_ci 40888c2ecf20Sopenharmony_ci /* 1x prefix + one read/write ccw per track */ 40898c2ecf20Sopenharmony_ci cplength = 1 + trkcount; 40908c2ecf20Sopenharmony_ci 40918c2ecf20Sopenharmony_ci datasize = sizeof(struct PFX_eckd_data) + cidaw * sizeof(unsigned long); 40928c2ecf20Sopenharmony_ci 40938c2ecf20Sopenharmony_ci /* Allocate the ccw request. */ 40948c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, 40958c2ecf20Sopenharmony_ci startdev, blk_mq_rq_to_pdu(req)); 40968c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) 40978c2ecf20Sopenharmony_ci return cqr; 40988c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 40998c2ecf20Sopenharmony_ci /* transfer length factor: how many bytes to read from the last track */ 41008c2ecf20Sopenharmony_ci if (first_trk == last_trk) 41018c2ecf20Sopenharmony_ci tlf = last_offs - first_offs + 1; 41028c2ecf20Sopenharmony_ci else 41038c2ecf20Sopenharmony_ci tlf = last_offs + 1; 41048c2ecf20Sopenharmony_ci tlf *= blksize; 41058c2ecf20Sopenharmony_ci 41068c2ecf20Sopenharmony_ci if (prefix_LRE(ccw++, cqr->data, first_trk, 41078c2ecf20Sopenharmony_ci last_trk, cmd, basedev, startdev, 41088c2ecf20Sopenharmony_ci 1 /* format */, first_offs + 1, 41098c2ecf20Sopenharmony_ci trkcount, blksize, 41108c2ecf20Sopenharmony_ci tlf) == -EAGAIN) { 41118c2ecf20Sopenharmony_ci /* Clock not in sync and XRC is enabled. 41128c2ecf20Sopenharmony_ci * Try again later. 41138c2ecf20Sopenharmony_ci */ 41148c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, startdev); 41158c2ecf20Sopenharmony_ci return ERR_PTR(-EAGAIN); 41168c2ecf20Sopenharmony_ci } 41178c2ecf20Sopenharmony_ci 41188c2ecf20Sopenharmony_ci /* 41198c2ecf20Sopenharmony_ci * The translation of request into ccw programs must meet the 41208c2ecf20Sopenharmony_ci * following conditions: 41218c2ecf20Sopenharmony_ci * - all idaws but the first and the last must address full pages 41228c2ecf20Sopenharmony_ci * (or 2K blocks on 31-bit) 41238c2ecf20Sopenharmony_ci * - the scope of a ccw and it's idal ends with the track boundaries 41248c2ecf20Sopenharmony_ci */ 41258c2ecf20Sopenharmony_ci idaws = (unsigned long *) (cqr->data + sizeof(struct PFX_eckd_data)); 41268c2ecf20Sopenharmony_ci recid = first_rec; 41278c2ecf20Sopenharmony_ci new_track = 1; 41288c2ecf20Sopenharmony_ci end_idaw = 0; 41298c2ecf20Sopenharmony_ci len_to_track_end = 0; 41308c2ecf20Sopenharmony_ci idaw_dst = NULL; 41318c2ecf20Sopenharmony_ci idaw_len = 0; 41328c2ecf20Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 41338c2ecf20Sopenharmony_ci dst = page_address(bv.bv_page) + bv.bv_offset; 41348c2ecf20Sopenharmony_ci seg_len = bv.bv_len; 41358c2ecf20Sopenharmony_ci while (seg_len) { 41368c2ecf20Sopenharmony_ci if (new_track) { 41378c2ecf20Sopenharmony_ci trkid = recid; 41388c2ecf20Sopenharmony_ci recoffs = sector_div(trkid, blk_per_trk); 41398c2ecf20Sopenharmony_ci count_to_trk_end = blk_per_trk - recoffs; 41408c2ecf20Sopenharmony_ci count = min((last_rec - recid + 1), 41418c2ecf20Sopenharmony_ci (sector_t)count_to_trk_end); 41428c2ecf20Sopenharmony_ci len_to_track_end = count * blksize; 41438c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 41448c2ecf20Sopenharmony_ci ccw->cmd_code = cmd; 41458c2ecf20Sopenharmony_ci ccw->count = len_to_track_end; 41468c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t)idaws; 41478c2ecf20Sopenharmony_ci ccw->flags = CCW_FLAG_IDA; 41488c2ecf20Sopenharmony_ci ccw++; 41498c2ecf20Sopenharmony_ci recid += count; 41508c2ecf20Sopenharmony_ci new_track = 0; 41518c2ecf20Sopenharmony_ci /* first idaw for a ccw may start anywhere */ 41528c2ecf20Sopenharmony_ci if (!idaw_dst) 41538c2ecf20Sopenharmony_ci idaw_dst = dst; 41548c2ecf20Sopenharmony_ci } 41558c2ecf20Sopenharmony_ci /* If we start a new idaw, we must make sure that it 41568c2ecf20Sopenharmony_ci * starts on an IDA_BLOCK_SIZE boundary. 41578c2ecf20Sopenharmony_ci * If we continue an idaw, we must make sure that the 41588c2ecf20Sopenharmony_ci * current segment begins where the so far accumulated 41598c2ecf20Sopenharmony_ci * idaw ends 41608c2ecf20Sopenharmony_ci */ 41618c2ecf20Sopenharmony_ci if (!idaw_dst) { 41628c2ecf20Sopenharmony_ci if (__pa(dst) & (IDA_BLOCK_SIZE-1)) { 41638c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, startdev); 41648c2ecf20Sopenharmony_ci return ERR_PTR(-ERANGE); 41658c2ecf20Sopenharmony_ci } else 41668c2ecf20Sopenharmony_ci idaw_dst = dst; 41678c2ecf20Sopenharmony_ci } 41688c2ecf20Sopenharmony_ci if ((idaw_dst + idaw_len) != dst) { 41698c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, startdev); 41708c2ecf20Sopenharmony_ci return ERR_PTR(-ERANGE); 41718c2ecf20Sopenharmony_ci } 41728c2ecf20Sopenharmony_ci part_len = min(seg_len, len_to_track_end); 41738c2ecf20Sopenharmony_ci seg_len -= part_len; 41748c2ecf20Sopenharmony_ci dst += part_len; 41758c2ecf20Sopenharmony_ci idaw_len += part_len; 41768c2ecf20Sopenharmony_ci len_to_track_end -= part_len; 41778c2ecf20Sopenharmony_ci /* collected memory area ends on an IDA_BLOCK border, 41788c2ecf20Sopenharmony_ci * -> create an idaw 41798c2ecf20Sopenharmony_ci * idal_create_words will handle cases where idaw_len 41808c2ecf20Sopenharmony_ci * is larger then IDA_BLOCK_SIZE 41818c2ecf20Sopenharmony_ci */ 41828c2ecf20Sopenharmony_ci if (!(__pa(idaw_dst + idaw_len) & (IDA_BLOCK_SIZE-1))) 41838c2ecf20Sopenharmony_ci end_idaw = 1; 41848c2ecf20Sopenharmony_ci /* We also need to end the idaw at track end */ 41858c2ecf20Sopenharmony_ci if (!len_to_track_end) { 41868c2ecf20Sopenharmony_ci new_track = 1; 41878c2ecf20Sopenharmony_ci end_idaw = 1; 41888c2ecf20Sopenharmony_ci } 41898c2ecf20Sopenharmony_ci if (end_idaw) { 41908c2ecf20Sopenharmony_ci idaws = idal_create_words(idaws, idaw_dst, 41918c2ecf20Sopenharmony_ci idaw_len); 41928c2ecf20Sopenharmony_ci idaw_dst = NULL; 41938c2ecf20Sopenharmony_ci idaw_len = 0; 41948c2ecf20Sopenharmony_ci end_idaw = 0; 41958c2ecf20Sopenharmony_ci } 41968c2ecf20Sopenharmony_ci } 41978c2ecf20Sopenharmony_ci } 41988c2ecf20Sopenharmony_ci 41998c2ecf20Sopenharmony_ci if (blk_noretry_request(req) || 42008c2ecf20Sopenharmony_ci block->base->features & DASD_FEATURE_FAILFAST) 42018c2ecf20Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 42028c2ecf20Sopenharmony_ci cqr->startdev = startdev; 42038c2ecf20Sopenharmony_ci cqr->memdev = startdev; 42048c2ecf20Sopenharmony_ci cqr->block = block; 42058c2ecf20Sopenharmony_ci cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ 42068c2ecf20Sopenharmony_ci cqr->lpm = dasd_path_get_ppm(startdev); 42078c2ecf20Sopenharmony_ci cqr->retries = startdev->default_retries; 42088c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 42098c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 42108c2ecf20Sopenharmony_ci 42118c2ecf20Sopenharmony_ci /* Set flags to suppress output for expected errors */ 42128c2ecf20Sopenharmony_ci if (dasd_eckd_is_ese(basedev)) 42138c2ecf20Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags); 42148c2ecf20Sopenharmony_ci 42158c2ecf20Sopenharmony_ci return cqr; 42168c2ecf20Sopenharmony_ci} 42178c2ecf20Sopenharmony_ci 42188c2ecf20Sopenharmony_cistatic int prepare_itcw(struct itcw *itcw, 42198c2ecf20Sopenharmony_ci unsigned int trk, unsigned int totrk, int cmd, 42208c2ecf20Sopenharmony_ci struct dasd_device *basedev, 42218c2ecf20Sopenharmony_ci struct dasd_device *startdev, 42228c2ecf20Sopenharmony_ci unsigned int rec_on_trk, int count, 42238c2ecf20Sopenharmony_ci unsigned int blksize, 42248c2ecf20Sopenharmony_ci unsigned int total_data_size, 42258c2ecf20Sopenharmony_ci unsigned int tlf, 42268c2ecf20Sopenharmony_ci unsigned int blk_per_trk) 42278c2ecf20Sopenharmony_ci{ 42288c2ecf20Sopenharmony_ci struct PFX_eckd_data pfxdata; 42298c2ecf20Sopenharmony_ci struct dasd_eckd_private *basepriv, *startpriv; 42308c2ecf20Sopenharmony_ci struct DE_eckd_data *dedata; 42318c2ecf20Sopenharmony_ci struct LRE_eckd_data *lredata; 42328c2ecf20Sopenharmony_ci struct dcw *dcw; 42338c2ecf20Sopenharmony_ci 42348c2ecf20Sopenharmony_ci u32 begcyl, endcyl; 42358c2ecf20Sopenharmony_ci u16 heads, beghead, endhead; 42368c2ecf20Sopenharmony_ci u8 pfx_cmd; 42378c2ecf20Sopenharmony_ci 42388c2ecf20Sopenharmony_ci int rc = 0; 42398c2ecf20Sopenharmony_ci int sector = 0; 42408c2ecf20Sopenharmony_ci int dn, d; 42418c2ecf20Sopenharmony_ci 42428c2ecf20Sopenharmony_ci 42438c2ecf20Sopenharmony_ci /* setup prefix data */ 42448c2ecf20Sopenharmony_ci basepriv = basedev->private; 42458c2ecf20Sopenharmony_ci startpriv = startdev->private; 42468c2ecf20Sopenharmony_ci dedata = &pfxdata.define_extent; 42478c2ecf20Sopenharmony_ci lredata = &pfxdata.locate_record; 42488c2ecf20Sopenharmony_ci 42498c2ecf20Sopenharmony_ci memset(&pfxdata, 0, sizeof(pfxdata)); 42508c2ecf20Sopenharmony_ci pfxdata.format = 1; /* PFX with LRE */ 42518c2ecf20Sopenharmony_ci pfxdata.base_address = basepriv->ned->unit_addr; 42528c2ecf20Sopenharmony_ci pfxdata.base_lss = basepriv->ned->ID; 42538c2ecf20Sopenharmony_ci pfxdata.validity.define_extent = 1; 42548c2ecf20Sopenharmony_ci 42558c2ecf20Sopenharmony_ci /* private uid is kept up to date, conf_data may be outdated */ 42568c2ecf20Sopenharmony_ci if (startpriv->uid.type == UA_BASE_PAV_ALIAS) 42578c2ecf20Sopenharmony_ci pfxdata.validity.verify_base = 1; 42588c2ecf20Sopenharmony_ci 42598c2ecf20Sopenharmony_ci if (startpriv->uid.type == UA_HYPER_PAV_ALIAS) { 42608c2ecf20Sopenharmony_ci pfxdata.validity.verify_base = 1; 42618c2ecf20Sopenharmony_ci pfxdata.validity.hyper_pav = 1; 42628c2ecf20Sopenharmony_ci } 42638c2ecf20Sopenharmony_ci 42648c2ecf20Sopenharmony_ci switch (cmd) { 42658c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_TRACK_DATA: 42668c2ecf20Sopenharmony_ci dedata->mask.perm = 0x1; 42678c2ecf20Sopenharmony_ci dedata->attributes.operation = basepriv->attrib.operation; 42688c2ecf20Sopenharmony_ci dedata->blk_size = blksize; 42698c2ecf20Sopenharmony_ci dedata->ga_extended |= 0x42; 42708c2ecf20Sopenharmony_ci lredata->operation.orientation = 0x0; 42718c2ecf20Sopenharmony_ci lredata->operation.operation = 0x0C; 42728c2ecf20Sopenharmony_ci lredata->auxiliary.check_bytes = 0x01; 42738c2ecf20Sopenharmony_ci pfx_cmd = DASD_ECKD_CCW_PFX_READ; 42748c2ecf20Sopenharmony_ci break; 42758c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_WRITE_TRACK_DATA: 42768c2ecf20Sopenharmony_ci dedata->mask.perm = 0x02; 42778c2ecf20Sopenharmony_ci dedata->attributes.operation = basepriv->attrib.operation; 42788c2ecf20Sopenharmony_ci dedata->blk_size = blksize; 42798c2ecf20Sopenharmony_ci rc = set_timestamp(NULL, dedata, basedev); 42808c2ecf20Sopenharmony_ci dedata->ga_extended |= 0x42; 42818c2ecf20Sopenharmony_ci lredata->operation.orientation = 0x0; 42828c2ecf20Sopenharmony_ci lredata->operation.operation = 0x3F; 42838c2ecf20Sopenharmony_ci lredata->extended_operation = 0x23; 42848c2ecf20Sopenharmony_ci lredata->auxiliary.check_bytes = 0x2; 42858c2ecf20Sopenharmony_ci /* 42868c2ecf20Sopenharmony_ci * If XRC is supported the System Time Stamp is set. The 42878c2ecf20Sopenharmony_ci * validity of the time stamp must be reflected in the prefix 42888c2ecf20Sopenharmony_ci * data as well. 42898c2ecf20Sopenharmony_ci */ 42908c2ecf20Sopenharmony_ci if (dedata->ga_extended & 0x08 && dedata->ga_extended & 0x02) 42918c2ecf20Sopenharmony_ci pfxdata.validity.time_stamp = 1; /* 'Time Stamp Valid' */ 42928c2ecf20Sopenharmony_ci pfx_cmd = DASD_ECKD_CCW_PFX; 42938c2ecf20Sopenharmony_ci break; 42948c2ecf20Sopenharmony_ci case DASD_ECKD_CCW_READ_COUNT_MT: 42958c2ecf20Sopenharmony_ci dedata->mask.perm = 0x1; 42968c2ecf20Sopenharmony_ci dedata->attributes.operation = DASD_BYPASS_CACHE; 42978c2ecf20Sopenharmony_ci dedata->ga_extended |= 0x42; 42988c2ecf20Sopenharmony_ci dedata->blk_size = blksize; 42998c2ecf20Sopenharmony_ci lredata->operation.orientation = 0x2; 43008c2ecf20Sopenharmony_ci lredata->operation.operation = 0x16; 43018c2ecf20Sopenharmony_ci lredata->auxiliary.check_bytes = 0x01; 43028c2ecf20Sopenharmony_ci pfx_cmd = DASD_ECKD_CCW_PFX_READ; 43038c2ecf20Sopenharmony_ci break; 43048c2ecf20Sopenharmony_ci default: 43058c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_ERR, basedev, 43068c2ecf20Sopenharmony_ci "prepare itcw, unknown opcode 0x%x", cmd); 43078c2ecf20Sopenharmony_ci BUG(); 43088c2ecf20Sopenharmony_ci break; 43098c2ecf20Sopenharmony_ci } 43108c2ecf20Sopenharmony_ci if (rc) 43118c2ecf20Sopenharmony_ci return rc; 43128c2ecf20Sopenharmony_ci 43138c2ecf20Sopenharmony_ci dedata->attributes.mode = 0x3; /* ECKD */ 43148c2ecf20Sopenharmony_ci 43158c2ecf20Sopenharmony_ci heads = basepriv->rdc_data.trk_per_cyl; 43168c2ecf20Sopenharmony_ci begcyl = trk / heads; 43178c2ecf20Sopenharmony_ci beghead = trk % heads; 43188c2ecf20Sopenharmony_ci endcyl = totrk / heads; 43198c2ecf20Sopenharmony_ci endhead = totrk % heads; 43208c2ecf20Sopenharmony_ci 43218c2ecf20Sopenharmony_ci /* check for sequential prestage - enhance cylinder range */ 43228c2ecf20Sopenharmony_ci if (dedata->attributes.operation == DASD_SEQ_PRESTAGE || 43238c2ecf20Sopenharmony_ci dedata->attributes.operation == DASD_SEQ_ACCESS) { 43248c2ecf20Sopenharmony_ci 43258c2ecf20Sopenharmony_ci if (endcyl + basepriv->attrib.nr_cyl < basepriv->real_cyl) 43268c2ecf20Sopenharmony_ci endcyl += basepriv->attrib.nr_cyl; 43278c2ecf20Sopenharmony_ci else 43288c2ecf20Sopenharmony_ci endcyl = (basepriv->real_cyl - 1); 43298c2ecf20Sopenharmony_ci } 43308c2ecf20Sopenharmony_ci 43318c2ecf20Sopenharmony_ci set_ch_t(&dedata->beg_ext, begcyl, beghead); 43328c2ecf20Sopenharmony_ci set_ch_t(&dedata->end_ext, endcyl, endhead); 43338c2ecf20Sopenharmony_ci 43348c2ecf20Sopenharmony_ci dedata->ep_format = 0x20; /* records per track is valid */ 43358c2ecf20Sopenharmony_ci dedata->ep_rec_per_track = blk_per_trk; 43368c2ecf20Sopenharmony_ci 43378c2ecf20Sopenharmony_ci if (rec_on_trk) { 43388c2ecf20Sopenharmony_ci switch (basepriv->rdc_data.dev_type) { 43398c2ecf20Sopenharmony_ci case 0x3390: 43408c2ecf20Sopenharmony_ci dn = ceil_quot(blksize + 6, 232); 43418c2ecf20Sopenharmony_ci d = 9 + ceil_quot(blksize + 6 * (dn + 1), 34); 43428c2ecf20Sopenharmony_ci sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8; 43438c2ecf20Sopenharmony_ci break; 43448c2ecf20Sopenharmony_ci case 0x3380: 43458c2ecf20Sopenharmony_ci d = 7 + ceil_quot(blksize + 12, 32); 43468c2ecf20Sopenharmony_ci sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7; 43478c2ecf20Sopenharmony_ci break; 43488c2ecf20Sopenharmony_ci } 43498c2ecf20Sopenharmony_ci } 43508c2ecf20Sopenharmony_ci 43518c2ecf20Sopenharmony_ci if (cmd == DASD_ECKD_CCW_READ_COUNT_MT) { 43528c2ecf20Sopenharmony_ci lredata->auxiliary.length_valid = 0; 43538c2ecf20Sopenharmony_ci lredata->auxiliary.length_scope = 0; 43548c2ecf20Sopenharmony_ci lredata->sector = 0xff; 43558c2ecf20Sopenharmony_ci } else { 43568c2ecf20Sopenharmony_ci lredata->auxiliary.length_valid = 1; 43578c2ecf20Sopenharmony_ci lredata->auxiliary.length_scope = 1; 43588c2ecf20Sopenharmony_ci lredata->sector = sector; 43598c2ecf20Sopenharmony_ci } 43608c2ecf20Sopenharmony_ci lredata->auxiliary.imbedded_ccw_valid = 1; 43618c2ecf20Sopenharmony_ci lredata->length = tlf; 43628c2ecf20Sopenharmony_ci lredata->imbedded_ccw = cmd; 43638c2ecf20Sopenharmony_ci lredata->count = count; 43648c2ecf20Sopenharmony_ci set_ch_t(&lredata->seek_addr, begcyl, beghead); 43658c2ecf20Sopenharmony_ci lredata->search_arg.cyl = lredata->seek_addr.cyl; 43668c2ecf20Sopenharmony_ci lredata->search_arg.head = lredata->seek_addr.head; 43678c2ecf20Sopenharmony_ci lredata->search_arg.record = rec_on_trk; 43688c2ecf20Sopenharmony_ci 43698c2ecf20Sopenharmony_ci dcw = itcw_add_dcw(itcw, pfx_cmd, 0, 43708c2ecf20Sopenharmony_ci &pfxdata, sizeof(pfxdata), total_data_size); 43718c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(dcw); 43728c2ecf20Sopenharmony_ci} 43738c2ecf20Sopenharmony_ci 43748c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( 43758c2ecf20Sopenharmony_ci struct dasd_device *startdev, 43768c2ecf20Sopenharmony_ci struct dasd_block *block, 43778c2ecf20Sopenharmony_ci struct request *req, 43788c2ecf20Sopenharmony_ci sector_t first_rec, 43798c2ecf20Sopenharmony_ci sector_t last_rec, 43808c2ecf20Sopenharmony_ci sector_t first_trk, 43818c2ecf20Sopenharmony_ci sector_t last_trk, 43828c2ecf20Sopenharmony_ci unsigned int first_offs, 43838c2ecf20Sopenharmony_ci unsigned int last_offs, 43848c2ecf20Sopenharmony_ci unsigned int blk_per_trk, 43858c2ecf20Sopenharmony_ci unsigned int blksize) 43868c2ecf20Sopenharmony_ci{ 43878c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 43888c2ecf20Sopenharmony_ci struct req_iterator iter; 43898c2ecf20Sopenharmony_ci struct bio_vec bv; 43908c2ecf20Sopenharmony_ci char *dst; 43918c2ecf20Sopenharmony_ci unsigned int trkcount, ctidaw; 43928c2ecf20Sopenharmony_ci unsigned char cmd; 43938c2ecf20Sopenharmony_ci struct dasd_device *basedev; 43948c2ecf20Sopenharmony_ci unsigned int tlf; 43958c2ecf20Sopenharmony_ci struct itcw *itcw; 43968c2ecf20Sopenharmony_ci struct tidaw *last_tidaw = NULL; 43978c2ecf20Sopenharmony_ci int itcw_op; 43988c2ecf20Sopenharmony_ci size_t itcw_size; 43998c2ecf20Sopenharmony_ci u8 tidaw_flags; 44008c2ecf20Sopenharmony_ci unsigned int seg_len, part_len, len_to_track_end; 44018c2ecf20Sopenharmony_ci unsigned char new_track; 44028c2ecf20Sopenharmony_ci sector_t recid, trkid; 44038c2ecf20Sopenharmony_ci unsigned int offs; 44048c2ecf20Sopenharmony_ci unsigned int count, count_to_trk_end; 44058c2ecf20Sopenharmony_ci int ret; 44068c2ecf20Sopenharmony_ci 44078c2ecf20Sopenharmony_ci basedev = block->base; 44088c2ecf20Sopenharmony_ci if (rq_data_dir(req) == READ) { 44098c2ecf20Sopenharmony_ci cmd = DASD_ECKD_CCW_READ_TRACK_DATA; 44108c2ecf20Sopenharmony_ci itcw_op = ITCW_OP_READ; 44118c2ecf20Sopenharmony_ci } else if (rq_data_dir(req) == WRITE) { 44128c2ecf20Sopenharmony_ci cmd = DASD_ECKD_CCW_WRITE_TRACK_DATA; 44138c2ecf20Sopenharmony_ci itcw_op = ITCW_OP_WRITE; 44148c2ecf20Sopenharmony_ci } else 44158c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 44168c2ecf20Sopenharmony_ci 44178c2ecf20Sopenharmony_ci /* trackbased I/O needs address all memory via TIDAWs, 44188c2ecf20Sopenharmony_ci * not just for 64 bit addresses. This allows us to map 44198c2ecf20Sopenharmony_ci * each segment directly to one tidaw. 44208c2ecf20Sopenharmony_ci * In the case of write requests, additional tidaws may 44218c2ecf20Sopenharmony_ci * be needed when a segment crosses a track boundary. 44228c2ecf20Sopenharmony_ci */ 44238c2ecf20Sopenharmony_ci trkcount = last_trk - first_trk + 1; 44248c2ecf20Sopenharmony_ci ctidaw = 0; 44258c2ecf20Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 44268c2ecf20Sopenharmony_ci ++ctidaw; 44278c2ecf20Sopenharmony_ci } 44288c2ecf20Sopenharmony_ci if (rq_data_dir(req) == WRITE) 44298c2ecf20Sopenharmony_ci ctidaw += (last_trk - first_trk); 44308c2ecf20Sopenharmony_ci 44318c2ecf20Sopenharmony_ci /* Allocate the ccw request. */ 44328c2ecf20Sopenharmony_ci itcw_size = itcw_calc_size(0, ctidaw, 0); 44338c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev, 44348c2ecf20Sopenharmony_ci blk_mq_rq_to_pdu(req)); 44358c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) 44368c2ecf20Sopenharmony_ci return cqr; 44378c2ecf20Sopenharmony_ci 44388c2ecf20Sopenharmony_ci /* transfer length factor: how many bytes to read from the last track */ 44398c2ecf20Sopenharmony_ci if (first_trk == last_trk) 44408c2ecf20Sopenharmony_ci tlf = last_offs - first_offs + 1; 44418c2ecf20Sopenharmony_ci else 44428c2ecf20Sopenharmony_ci tlf = last_offs + 1; 44438c2ecf20Sopenharmony_ci tlf *= blksize; 44448c2ecf20Sopenharmony_ci 44458c2ecf20Sopenharmony_ci itcw = itcw_init(cqr->data, itcw_size, itcw_op, 0, ctidaw, 0); 44468c2ecf20Sopenharmony_ci if (IS_ERR(itcw)) { 44478c2ecf20Sopenharmony_ci ret = -EINVAL; 44488c2ecf20Sopenharmony_ci goto out_error; 44498c2ecf20Sopenharmony_ci } 44508c2ecf20Sopenharmony_ci cqr->cpaddr = itcw_get_tcw(itcw); 44518c2ecf20Sopenharmony_ci if (prepare_itcw(itcw, first_trk, last_trk, 44528c2ecf20Sopenharmony_ci cmd, basedev, startdev, 44538c2ecf20Sopenharmony_ci first_offs + 1, 44548c2ecf20Sopenharmony_ci trkcount, blksize, 44558c2ecf20Sopenharmony_ci (last_rec - first_rec + 1) * blksize, 44568c2ecf20Sopenharmony_ci tlf, blk_per_trk) == -EAGAIN) { 44578c2ecf20Sopenharmony_ci /* Clock not in sync and XRC is enabled. 44588c2ecf20Sopenharmony_ci * Try again later. 44598c2ecf20Sopenharmony_ci */ 44608c2ecf20Sopenharmony_ci ret = -EAGAIN; 44618c2ecf20Sopenharmony_ci goto out_error; 44628c2ecf20Sopenharmony_ci } 44638c2ecf20Sopenharmony_ci len_to_track_end = 0; 44648c2ecf20Sopenharmony_ci /* 44658c2ecf20Sopenharmony_ci * A tidaw can address 4k of memory, but must not cross page boundaries 44668c2ecf20Sopenharmony_ci * We can let the block layer handle this by setting 44678c2ecf20Sopenharmony_ci * blk_queue_segment_boundary to page boundaries and 44688c2ecf20Sopenharmony_ci * blk_max_segment_size to page size when setting up the request queue. 44698c2ecf20Sopenharmony_ci * For write requests, a TIDAW must not cross track boundaries, because 44708c2ecf20Sopenharmony_ci * we have to set the CBC flag on the last tidaw for each track. 44718c2ecf20Sopenharmony_ci */ 44728c2ecf20Sopenharmony_ci if (rq_data_dir(req) == WRITE) { 44738c2ecf20Sopenharmony_ci new_track = 1; 44748c2ecf20Sopenharmony_ci recid = first_rec; 44758c2ecf20Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 44768c2ecf20Sopenharmony_ci dst = page_address(bv.bv_page) + bv.bv_offset; 44778c2ecf20Sopenharmony_ci seg_len = bv.bv_len; 44788c2ecf20Sopenharmony_ci while (seg_len) { 44798c2ecf20Sopenharmony_ci if (new_track) { 44808c2ecf20Sopenharmony_ci trkid = recid; 44818c2ecf20Sopenharmony_ci offs = sector_div(trkid, blk_per_trk); 44828c2ecf20Sopenharmony_ci count_to_trk_end = blk_per_trk - offs; 44838c2ecf20Sopenharmony_ci count = min((last_rec - recid + 1), 44848c2ecf20Sopenharmony_ci (sector_t)count_to_trk_end); 44858c2ecf20Sopenharmony_ci len_to_track_end = count * blksize; 44868c2ecf20Sopenharmony_ci recid += count; 44878c2ecf20Sopenharmony_ci new_track = 0; 44888c2ecf20Sopenharmony_ci } 44898c2ecf20Sopenharmony_ci part_len = min(seg_len, len_to_track_end); 44908c2ecf20Sopenharmony_ci seg_len -= part_len; 44918c2ecf20Sopenharmony_ci len_to_track_end -= part_len; 44928c2ecf20Sopenharmony_ci /* We need to end the tidaw at track end */ 44938c2ecf20Sopenharmony_ci if (!len_to_track_end) { 44948c2ecf20Sopenharmony_ci new_track = 1; 44958c2ecf20Sopenharmony_ci tidaw_flags = TIDAW_FLAGS_INSERT_CBC; 44968c2ecf20Sopenharmony_ci } else 44978c2ecf20Sopenharmony_ci tidaw_flags = 0; 44988c2ecf20Sopenharmony_ci last_tidaw = itcw_add_tidaw(itcw, tidaw_flags, 44998c2ecf20Sopenharmony_ci dst, part_len); 45008c2ecf20Sopenharmony_ci if (IS_ERR(last_tidaw)) { 45018c2ecf20Sopenharmony_ci ret = -EINVAL; 45028c2ecf20Sopenharmony_ci goto out_error; 45038c2ecf20Sopenharmony_ci } 45048c2ecf20Sopenharmony_ci dst += part_len; 45058c2ecf20Sopenharmony_ci } 45068c2ecf20Sopenharmony_ci } 45078c2ecf20Sopenharmony_ci } else { 45088c2ecf20Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 45098c2ecf20Sopenharmony_ci dst = page_address(bv.bv_page) + bv.bv_offset; 45108c2ecf20Sopenharmony_ci last_tidaw = itcw_add_tidaw(itcw, 0x00, 45118c2ecf20Sopenharmony_ci dst, bv.bv_len); 45128c2ecf20Sopenharmony_ci if (IS_ERR(last_tidaw)) { 45138c2ecf20Sopenharmony_ci ret = -EINVAL; 45148c2ecf20Sopenharmony_ci goto out_error; 45158c2ecf20Sopenharmony_ci } 45168c2ecf20Sopenharmony_ci } 45178c2ecf20Sopenharmony_ci } 45188c2ecf20Sopenharmony_ci last_tidaw->flags |= TIDAW_FLAGS_LAST; 45198c2ecf20Sopenharmony_ci last_tidaw->flags &= ~TIDAW_FLAGS_INSERT_CBC; 45208c2ecf20Sopenharmony_ci itcw_finalize(itcw); 45218c2ecf20Sopenharmony_ci 45228c2ecf20Sopenharmony_ci if (blk_noretry_request(req) || 45238c2ecf20Sopenharmony_ci block->base->features & DASD_FEATURE_FAILFAST) 45248c2ecf20Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 45258c2ecf20Sopenharmony_ci cqr->cpmode = 1; 45268c2ecf20Sopenharmony_ci cqr->startdev = startdev; 45278c2ecf20Sopenharmony_ci cqr->memdev = startdev; 45288c2ecf20Sopenharmony_ci cqr->block = block; 45298c2ecf20Sopenharmony_ci cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ 45308c2ecf20Sopenharmony_ci cqr->lpm = dasd_path_get_ppm(startdev); 45318c2ecf20Sopenharmony_ci cqr->retries = startdev->default_retries; 45328c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 45338c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 45348c2ecf20Sopenharmony_ci 45358c2ecf20Sopenharmony_ci /* Set flags to suppress output for expected errors */ 45368c2ecf20Sopenharmony_ci if (dasd_eckd_is_ese(basedev)) { 45378c2ecf20Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags); 45388c2ecf20Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags); 45398c2ecf20Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags); 45408c2ecf20Sopenharmony_ci } 45418c2ecf20Sopenharmony_ci 45428c2ecf20Sopenharmony_ci return cqr; 45438c2ecf20Sopenharmony_ciout_error: 45448c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, startdev); 45458c2ecf20Sopenharmony_ci return ERR_PTR(ret); 45468c2ecf20Sopenharmony_ci} 45478c2ecf20Sopenharmony_ci 45488c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, 45498c2ecf20Sopenharmony_ci struct dasd_block *block, 45508c2ecf20Sopenharmony_ci struct request *req) 45518c2ecf20Sopenharmony_ci{ 45528c2ecf20Sopenharmony_ci int cmdrtd, cmdwtd; 45538c2ecf20Sopenharmony_ci int use_prefix; 45548c2ecf20Sopenharmony_ci int fcx_multitrack; 45558c2ecf20Sopenharmony_ci struct dasd_eckd_private *private; 45568c2ecf20Sopenharmony_ci struct dasd_device *basedev; 45578c2ecf20Sopenharmony_ci sector_t first_rec, last_rec; 45588c2ecf20Sopenharmony_ci sector_t first_trk, last_trk; 45598c2ecf20Sopenharmony_ci unsigned int first_offs, last_offs; 45608c2ecf20Sopenharmony_ci unsigned int blk_per_trk, blksize; 45618c2ecf20Sopenharmony_ci int cdlspecial; 45628c2ecf20Sopenharmony_ci unsigned int data_size; 45638c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 45648c2ecf20Sopenharmony_ci 45658c2ecf20Sopenharmony_ci basedev = block->base; 45668c2ecf20Sopenharmony_ci private = basedev->private; 45678c2ecf20Sopenharmony_ci 45688c2ecf20Sopenharmony_ci /* Calculate number of blocks/records per track. */ 45698c2ecf20Sopenharmony_ci blksize = block->bp_block; 45708c2ecf20Sopenharmony_ci blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize); 45718c2ecf20Sopenharmony_ci if (blk_per_trk == 0) 45728c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 45738c2ecf20Sopenharmony_ci /* Calculate record id of first and last block. */ 45748c2ecf20Sopenharmony_ci first_rec = first_trk = blk_rq_pos(req) >> block->s2b_shift; 45758c2ecf20Sopenharmony_ci first_offs = sector_div(first_trk, blk_per_trk); 45768c2ecf20Sopenharmony_ci last_rec = last_trk = 45778c2ecf20Sopenharmony_ci (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift; 45788c2ecf20Sopenharmony_ci last_offs = sector_div(last_trk, blk_per_trk); 45798c2ecf20Sopenharmony_ci cdlspecial = (private->uses_cdl && first_rec < 2*blk_per_trk); 45808c2ecf20Sopenharmony_ci 45818c2ecf20Sopenharmony_ci fcx_multitrack = private->features.feature[40] & 0x20; 45828c2ecf20Sopenharmony_ci data_size = blk_rq_bytes(req); 45838c2ecf20Sopenharmony_ci if (data_size % blksize) 45848c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 45858c2ecf20Sopenharmony_ci /* tpm write request add CBC data on each track boundary */ 45868c2ecf20Sopenharmony_ci if (rq_data_dir(req) == WRITE) 45878c2ecf20Sopenharmony_ci data_size += (last_trk - first_trk) * 4; 45888c2ecf20Sopenharmony_ci 45898c2ecf20Sopenharmony_ci /* is read track data and write track data in command mode supported? */ 45908c2ecf20Sopenharmony_ci cmdrtd = private->features.feature[9] & 0x20; 45918c2ecf20Sopenharmony_ci cmdwtd = private->features.feature[12] & 0x40; 45928c2ecf20Sopenharmony_ci use_prefix = private->features.feature[8] & 0x01; 45938c2ecf20Sopenharmony_ci 45948c2ecf20Sopenharmony_ci cqr = NULL; 45958c2ecf20Sopenharmony_ci if (cdlspecial || dasd_page_cache) { 45968c2ecf20Sopenharmony_ci /* do nothing, just fall through to the cmd mode single case */ 45978c2ecf20Sopenharmony_ci } else if ((data_size <= private->fcx_max_data) 45988c2ecf20Sopenharmony_ci && (fcx_multitrack || (first_trk == last_trk))) { 45998c2ecf20Sopenharmony_ci cqr = dasd_eckd_build_cp_tpm_track(startdev, block, req, 46008c2ecf20Sopenharmony_ci first_rec, last_rec, 46018c2ecf20Sopenharmony_ci first_trk, last_trk, 46028c2ecf20Sopenharmony_ci first_offs, last_offs, 46038c2ecf20Sopenharmony_ci blk_per_trk, blksize); 46048c2ecf20Sopenharmony_ci if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) && 46058c2ecf20Sopenharmony_ci (PTR_ERR(cqr) != -ENOMEM)) 46068c2ecf20Sopenharmony_ci cqr = NULL; 46078c2ecf20Sopenharmony_ci } else if (use_prefix && 46088c2ecf20Sopenharmony_ci (((rq_data_dir(req) == READ) && cmdrtd) || 46098c2ecf20Sopenharmony_ci ((rq_data_dir(req) == WRITE) && cmdwtd))) { 46108c2ecf20Sopenharmony_ci cqr = dasd_eckd_build_cp_cmd_track(startdev, block, req, 46118c2ecf20Sopenharmony_ci first_rec, last_rec, 46128c2ecf20Sopenharmony_ci first_trk, last_trk, 46138c2ecf20Sopenharmony_ci first_offs, last_offs, 46148c2ecf20Sopenharmony_ci blk_per_trk, blksize); 46158c2ecf20Sopenharmony_ci if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) && 46168c2ecf20Sopenharmony_ci (PTR_ERR(cqr) != -ENOMEM)) 46178c2ecf20Sopenharmony_ci cqr = NULL; 46188c2ecf20Sopenharmony_ci } 46198c2ecf20Sopenharmony_ci if (!cqr) 46208c2ecf20Sopenharmony_ci cqr = dasd_eckd_build_cp_cmd_single(startdev, block, req, 46218c2ecf20Sopenharmony_ci first_rec, last_rec, 46228c2ecf20Sopenharmony_ci first_trk, last_trk, 46238c2ecf20Sopenharmony_ci first_offs, last_offs, 46248c2ecf20Sopenharmony_ci blk_per_trk, blksize); 46258c2ecf20Sopenharmony_ci return cqr; 46268c2ecf20Sopenharmony_ci} 46278c2ecf20Sopenharmony_ci 46288c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp_raw(struct dasd_device *startdev, 46298c2ecf20Sopenharmony_ci struct dasd_block *block, 46308c2ecf20Sopenharmony_ci struct request *req) 46318c2ecf20Sopenharmony_ci{ 46328c2ecf20Sopenharmony_ci sector_t start_padding_sectors, end_sector_offset, end_padding_sectors; 46338c2ecf20Sopenharmony_ci unsigned int seg_len, len_to_track_end; 46348c2ecf20Sopenharmony_ci unsigned int cidaw, cplength, datasize; 46358c2ecf20Sopenharmony_ci sector_t first_trk, last_trk, sectors; 46368c2ecf20Sopenharmony_ci struct dasd_eckd_private *base_priv; 46378c2ecf20Sopenharmony_ci struct dasd_device *basedev; 46388c2ecf20Sopenharmony_ci struct req_iterator iter; 46398c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 46408c2ecf20Sopenharmony_ci unsigned int trkcount; 46418c2ecf20Sopenharmony_ci unsigned long *idaws; 46428c2ecf20Sopenharmony_ci unsigned int size; 46438c2ecf20Sopenharmony_ci unsigned char cmd; 46448c2ecf20Sopenharmony_ci struct bio_vec bv; 46458c2ecf20Sopenharmony_ci struct ccw1 *ccw; 46468c2ecf20Sopenharmony_ci int use_prefix; 46478c2ecf20Sopenharmony_ci void *data; 46488c2ecf20Sopenharmony_ci char *dst; 46498c2ecf20Sopenharmony_ci 46508c2ecf20Sopenharmony_ci /* 46518c2ecf20Sopenharmony_ci * raw track access needs to be mutiple of 64k and on 64k boundary 46528c2ecf20Sopenharmony_ci * For read requests we can fix an incorrect alignment by padding 46538c2ecf20Sopenharmony_ci * the request with dummy pages. 46548c2ecf20Sopenharmony_ci */ 46558c2ecf20Sopenharmony_ci start_padding_sectors = blk_rq_pos(req) % DASD_RAW_SECTORS_PER_TRACK; 46568c2ecf20Sopenharmony_ci end_sector_offset = (blk_rq_pos(req) + blk_rq_sectors(req)) % 46578c2ecf20Sopenharmony_ci DASD_RAW_SECTORS_PER_TRACK; 46588c2ecf20Sopenharmony_ci end_padding_sectors = (DASD_RAW_SECTORS_PER_TRACK - end_sector_offset) % 46598c2ecf20Sopenharmony_ci DASD_RAW_SECTORS_PER_TRACK; 46608c2ecf20Sopenharmony_ci basedev = block->base; 46618c2ecf20Sopenharmony_ci if ((start_padding_sectors || end_padding_sectors) && 46628c2ecf20Sopenharmony_ci (rq_data_dir(req) == WRITE)) { 46638c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_ERR, basedev, 46648c2ecf20Sopenharmony_ci "raw write not track aligned (%llu,%llu) req %p", 46658c2ecf20Sopenharmony_ci start_padding_sectors, end_padding_sectors, req); 46668c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 46678c2ecf20Sopenharmony_ci } 46688c2ecf20Sopenharmony_ci 46698c2ecf20Sopenharmony_ci first_trk = blk_rq_pos(req) / DASD_RAW_SECTORS_PER_TRACK; 46708c2ecf20Sopenharmony_ci last_trk = (blk_rq_pos(req) + blk_rq_sectors(req) - 1) / 46718c2ecf20Sopenharmony_ci DASD_RAW_SECTORS_PER_TRACK; 46728c2ecf20Sopenharmony_ci trkcount = last_trk - first_trk + 1; 46738c2ecf20Sopenharmony_ci 46748c2ecf20Sopenharmony_ci if (rq_data_dir(req) == READ) 46758c2ecf20Sopenharmony_ci cmd = DASD_ECKD_CCW_READ_TRACK; 46768c2ecf20Sopenharmony_ci else if (rq_data_dir(req) == WRITE) 46778c2ecf20Sopenharmony_ci cmd = DASD_ECKD_CCW_WRITE_FULL_TRACK; 46788c2ecf20Sopenharmony_ci else 46798c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 46808c2ecf20Sopenharmony_ci 46818c2ecf20Sopenharmony_ci /* 46828c2ecf20Sopenharmony_ci * Raw track based I/O needs IDAWs for each page, 46838c2ecf20Sopenharmony_ci * and not just for 64 bit addresses. 46848c2ecf20Sopenharmony_ci */ 46858c2ecf20Sopenharmony_ci cidaw = trkcount * DASD_RAW_BLOCK_PER_TRACK; 46868c2ecf20Sopenharmony_ci 46878c2ecf20Sopenharmony_ci /* 46888c2ecf20Sopenharmony_ci * struct PFX_eckd_data and struct LRE_eckd_data can have up to 2 bytes 46898c2ecf20Sopenharmony_ci * of extended parameter. This is needed for write full track. 46908c2ecf20Sopenharmony_ci */ 46918c2ecf20Sopenharmony_ci base_priv = basedev->private; 46928c2ecf20Sopenharmony_ci use_prefix = base_priv->features.feature[8] & 0x01; 46938c2ecf20Sopenharmony_ci if (use_prefix) { 46948c2ecf20Sopenharmony_ci cplength = 1 + trkcount; 46958c2ecf20Sopenharmony_ci size = sizeof(struct PFX_eckd_data) + 2; 46968c2ecf20Sopenharmony_ci } else { 46978c2ecf20Sopenharmony_ci cplength = 2 + trkcount; 46988c2ecf20Sopenharmony_ci size = sizeof(struct DE_eckd_data) + 46998c2ecf20Sopenharmony_ci sizeof(struct LRE_eckd_data) + 2; 47008c2ecf20Sopenharmony_ci } 47018c2ecf20Sopenharmony_ci size = ALIGN(size, 8); 47028c2ecf20Sopenharmony_ci 47038c2ecf20Sopenharmony_ci datasize = size + cidaw * sizeof(unsigned long); 47048c2ecf20Sopenharmony_ci 47058c2ecf20Sopenharmony_ci /* Allocate the ccw request. */ 47068c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, 47078c2ecf20Sopenharmony_ci datasize, startdev, blk_mq_rq_to_pdu(req)); 47088c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) 47098c2ecf20Sopenharmony_ci return cqr; 47108c2ecf20Sopenharmony_ci 47118c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 47128c2ecf20Sopenharmony_ci data = cqr->data; 47138c2ecf20Sopenharmony_ci 47148c2ecf20Sopenharmony_ci if (use_prefix) { 47158c2ecf20Sopenharmony_ci prefix_LRE(ccw++, data, first_trk, last_trk, cmd, basedev, 47168c2ecf20Sopenharmony_ci startdev, 1, 0, trkcount, 0, 0); 47178c2ecf20Sopenharmony_ci } else { 47188c2ecf20Sopenharmony_ci define_extent(ccw++, data, first_trk, last_trk, cmd, basedev, 0); 47198c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 47208c2ecf20Sopenharmony_ci 47218c2ecf20Sopenharmony_ci data += sizeof(struct DE_eckd_data); 47228c2ecf20Sopenharmony_ci locate_record_ext(ccw++, data, first_trk, 0, 47238c2ecf20Sopenharmony_ci trkcount, cmd, basedev, 0, 0); 47248c2ecf20Sopenharmony_ci } 47258c2ecf20Sopenharmony_ci 47268c2ecf20Sopenharmony_ci idaws = (unsigned long *)(cqr->data + size); 47278c2ecf20Sopenharmony_ci len_to_track_end = 0; 47288c2ecf20Sopenharmony_ci if (start_padding_sectors) { 47298c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 47308c2ecf20Sopenharmony_ci ccw->cmd_code = cmd; 47318c2ecf20Sopenharmony_ci /* maximum 3390 track size */ 47328c2ecf20Sopenharmony_ci ccw->count = 57326; 47338c2ecf20Sopenharmony_ci /* 64k map to one track */ 47348c2ecf20Sopenharmony_ci len_to_track_end = 65536 - start_padding_sectors * 512; 47358c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t)idaws; 47368c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_IDA; 47378c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 47388c2ecf20Sopenharmony_ci ccw++; 47398c2ecf20Sopenharmony_ci for (sectors = 0; sectors < start_padding_sectors; sectors += 8) 47408c2ecf20Sopenharmony_ci idaws = idal_create_words(idaws, rawpadpage, PAGE_SIZE); 47418c2ecf20Sopenharmony_ci } 47428c2ecf20Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 47438c2ecf20Sopenharmony_ci dst = page_address(bv.bv_page) + bv.bv_offset; 47448c2ecf20Sopenharmony_ci seg_len = bv.bv_len; 47458c2ecf20Sopenharmony_ci if (cmd == DASD_ECKD_CCW_READ_TRACK) 47468c2ecf20Sopenharmony_ci memset(dst, 0, seg_len); 47478c2ecf20Sopenharmony_ci if (!len_to_track_end) { 47488c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 47498c2ecf20Sopenharmony_ci ccw->cmd_code = cmd; 47508c2ecf20Sopenharmony_ci /* maximum 3390 track size */ 47518c2ecf20Sopenharmony_ci ccw->count = 57326; 47528c2ecf20Sopenharmony_ci /* 64k map to one track */ 47538c2ecf20Sopenharmony_ci len_to_track_end = 65536; 47548c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t)idaws; 47558c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_IDA; 47568c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 47578c2ecf20Sopenharmony_ci ccw++; 47588c2ecf20Sopenharmony_ci } 47598c2ecf20Sopenharmony_ci len_to_track_end -= seg_len; 47608c2ecf20Sopenharmony_ci idaws = idal_create_words(idaws, dst, seg_len); 47618c2ecf20Sopenharmony_ci } 47628c2ecf20Sopenharmony_ci for (sectors = 0; sectors < end_padding_sectors; sectors += 8) 47638c2ecf20Sopenharmony_ci idaws = idal_create_words(idaws, rawpadpage, PAGE_SIZE); 47648c2ecf20Sopenharmony_ci if (blk_noretry_request(req) || 47658c2ecf20Sopenharmony_ci block->base->features & DASD_FEATURE_FAILFAST) 47668c2ecf20Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 47678c2ecf20Sopenharmony_ci cqr->startdev = startdev; 47688c2ecf20Sopenharmony_ci cqr->memdev = startdev; 47698c2ecf20Sopenharmony_ci cqr->block = block; 47708c2ecf20Sopenharmony_ci cqr->expires = startdev->default_expires * HZ; 47718c2ecf20Sopenharmony_ci cqr->lpm = dasd_path_get_ppm(startdev); 47728c2ecf20Sopenharmony_ci cqr->retries = startdev->default_retries; 47738c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 47748c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 47758c2ecf20Sopenharmony_ci 47768c2ecf20Sopenharmony_ci return cqr; 47778c2ecf20Sopenharmony_ci} 47788c2ecf20Sopenharmony_ci 47798c2ecf20Sopenharmony_ci 47808c2ecf20Sopenharmony_cistatic int 47818c2ecf20Sopenharmony_cidasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req) 47828c2ecf20Sopenharmony_ci{ 47838c2ecf20Sopenharmony_ci struct dasd_eckd_private *private; 47848c2ecf20Sopenharmony_ci struct ccw1 *ccw; 47858c2ecf20Sopenharmony_ci struct req_iterator iter; 47868c2ecf20Sopenharmony_ci struct bio_vec bv; 47878c2ecf20Sopenharmony_ci char *dst, *cda; 47888c2ecf20Sopenharmony_ci unsigned int blksize, blk_per_trk, off; 47898c2ecf20Sopenharmony_ci sector_t recid; 47908c2ecf20Sopenharmony_ci int status; 47918c2ecf20Sopenharmony_ci 47928c2ecf20Sopenharmony_ci if (!dasd_page_cache) 47938c2ecf20Sopenharmony_ci goto out; 47948c2ecf20Sopenharmony_ci private = cqr->block->base->private; 47958c2ecf20Sopenharmony_ci blksize = cqr->block->bp_block; 47968c2ecf20Sopenharmony_ci blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize); 47978c2ecf20Sopenharmony_ci recid = blk_rq_pos(req) >> cqr->block->s2b_shift; 47988c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 47998c2ecf20Sopenharmony_ci /* Skip over define extent & locate record. */ 48008c2ecf20Sopenharmony_ci ccw++; 48018c2ecf20Sopenharmony_ci if (private->uses_cdl == 0 || recid > 2*blk_per_trk) 48028c2ecf20Sopenharmony_ci ccw++; 48038c2ecf20Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 48048c2ecf20Sopenharmony_ci dst = page_address(bv.bv_page) + bv.bv_offset; 48058c2ecf20Sopenharmony_ci for (off = 0; off < bv.bv_len; off += blksize) { 48068c2ecf20Sopenharmony_ci /* Skip locate record. */ 48078c2ecf20Sopenharmony_ci if (private->uses_cdl && recid <= 2*blk_per_trk) 48088c2ecf20Sopenharmony_ci ccw++; 48098c2ecf20Sopenharmony_ci if (dst) { 48108c2ecf20Sopenharmony_ci if (ccw->flags & CCW_FLAG_IDA) 48118c2ecf20Sopenharmony_ci cda = *((char **)((addr_t) ccw->cda)); 48128c2ecf20Sopenharmony_ci else 48138c2ecf20Sopenharmony_ci cda = (char *)((addr_t) ccw->cda); 48148c2ecf20Sopenharmony_ci if (dst != cda) { 48158c2ecf20Sopenharmony_ci if (rq_data_dir(req) == READ) 48168c2ecf20Sopenharmony_ci memcpy(dst, cda, bv.bv_len); 48178c2ecf20Sopenharmony_ci kmem_cache_free(dasd_page_cache, 48188c2ecf20Sopenharmony_ci (void *)((addr_t)cda & PAGE_MASK)); 48198c2ecf20Sopenharmony_ci } 48208c2ecf20Sopenharmony_ci dst = NULL; 48218c2ecf20Sopenharmony_ci } 48228c2ecf20Sopenharmony_ci ccw++; 48238c2ecf20Sopenharmony_ci recid++; 48248c2ecf20Sopenharmony_ci } 48258c2ecf20Sopenharmony_ci } 48268c2ecf20Sopenharmony_ciout: 48278c2ecf20Sopenharmony_ci status = cqr->status == DASD_CQR_DONE; 48288c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 48298c2ecf20Sopenharmony_ci return status; 48308c2ecf20Sopenharmony_ci} 48318c2ecf20Sopenharmony_ci 48328c2ecf20Sopenharmony_ci/* 48338c2ecf20Sopenharmony_ci * Modify ccw/tcw in cqr so it can be started on a base device. 48348c2ecf20Sopenharmony_ci * 48358c2ecf20Sopenharmony_ci * Note that this is not enough to restart the cqr! 48368c2ecf20Sopenharmony_ci * Either reset cqr->startdev as well (summary unit check handling) 48378c2ecf20Sopenharmony_ci * or restart via separate cqr (as in ERP handling). 48388c2ecf20Sopenharmony_ci */ 48398c2ecf20Sopenharmony_civoid dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *cqr) 48408c2ecf20Sopenharmony_ci{ 48418c2ecf20Sopenharmony_ci struct ccw1 *ccw; 48428c2ecf20Sopenharmony_ci struct PFX_eckd_data *pfxdata; 48438c2ecf20Sopenharmony_ci struct tcw *tcw; 48448c2ecf20Sopenharmony_ci struct tccb *tccb; 48458c2ecf20Sopenharmony_ci struct dcw *dcw; 48468c2ecf20Sopenharmony_ci 48478c2ecf20Sopenharmony_ci if (cqr->cpmode == 1) { 48488c2ecf20Sopenharmony_ci tcw = cqr->cpaddr; 48498c2ecf20Sopenharmony_ci tccb = tcw_get_tccb(tcw); 48508c2ecf20Sopenharmony_ci dcw = (struct dcw *)&tccb->tca[0]; 48518c2ecf20Sopenharmony_ci pfxdata = (struct PFX_eckd_data *)&dcw->cd[0]; 48528c2ecf20Sopenharmony_ci pfxdata->validity.verify_base = 0; 48538c2ecf20Sopenharmony_ci pfxdata->validity.hyper_pav = 0; 48548c2ecf20Sopenharmony_ci } else { 48558c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 48568c2ecf20Sopenharmony_ci pfxdata = cqr->data; 48578c2ecf20Sopenharmony_ci if (ccw->cmd_code == DASD_ECKD_CCW_PFX) { 48588c2ecf20Sopenharmony_ci pfxdata->validity.verify_base = 0; 48598c2ecf20Sopenharmony_ci pfxdata->validity.hyper_pav = 0; 48608c2ecf20Sopenharmony_ci } 48618c2ecf20Sopenharmony_ci } 48628c2ecf20Sopenharmony_ci} 48638c2ecf20Sopenharmony_ci 48648c2ecf20Sopenharmony_ci#define DASD_ECKD_CHANQ_MAX_SIZE 4 48658c2ecf20Sopenharmony_ci 48668c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_alias_cp(struct dasd_device *base, 48678c2ecf20Sopenharmony_ci struct dasd_block *block, 48688c2ecf20Sopenharmony_ci struct request *req) 48698c2ecf20Sopenharmony_ci{ 48708c2ecf20Sopenharmony_ci struct dasd_eckd_private *private; 48718c2ecf20Sopenharmony_ci struct dasd_device *startdev; 48728c2ecf20Sopenharmony_ci unsigned long flags; 48738c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 48748c2ecf20Sopenharmony_ci 48758c2ecf20Sopenharmony_ci startdev = dasd_alias_get_start_dev(base); 48768c2ecf20Sopenharmony_ci if (!startdev) 48778c2ecf20Sopenharmony_ci startdev = base; 48788c2ecf20Sopenharmony_ci private = startdev->private; 48798c2ecf20Sopenharmony_ci if (private->count >= DASD_ECKD_CHANQ_MAX_SIZE) 48808c2ecf20Sopenharmony_ci return ERR_PTR(-EBUSY); 48818c2ecf20Sopenharmony_ci 48828c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(startdev->cdev), flags); 48838c2ecf20Sopenharmony_ci private->count++; 48848c2ecf20Sopenharmony_ci if ((base->features & DASD_FEATURE_USERAW)) 48858c2ecf20Sopenharmony_ci cqr = dasd_eckd_build_cp_raw(startdev, block, req); 48868c2ecf20Sopenharmony_ci else 48878c2ecf20Sopenharmony_ci cqr = dasd_eckd_build_cp(startdev, block, req); 48888c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) 48898c2ecf20Sopenharmony_ci private->count--; 48908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(startdev->cdev), flags); 48918c2ecf20Sopenharmony_ci return cqr; 48928c2ecf20Sopenharmony_ci} 48938c2ecf20Sopenharmony_ci 48948c2ecf20Sopenharmony_cistatic int dasd_eckd_free_alias_cp(struct dasd_ccw_req *cqr, 48958c2ecf20Sopenharmony_ci struct request *req) 48968c2ecf20Sopenharmony_ci{ 48978c2ecf20Sopenharmony_ci struct dasd_eckd_private *private; 48988c2ecf20Sopenharmony_ci unsigned long flags; 48998c2ecf20Sopenharmony_ci 49008c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(cqr->memdev->cdev), flags); 49018c2ecf20Sopenharmony_ci private = cqr->memdev->private; 49028c2ecf20Sopenharmony_ci private->count--; 49038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(cqr->memdev->cdev), flags); 49048c2ecf20Sopenharmony_ci return dasd_eckd_free_cp(cqr, req); 49058c2ecf20Sopenharmony_ci} 49068c2ecf20Sopenharmony_ci 49078c2ecf20Sopenharmony_cistatic int 49088c2ecf20Sopenharmony_cidasd_eckd_fill_info(struct dasd_device * device, 49098c2ecf20Sopenharmony_ci struct dasd_information2_t * info) 49108c2ecf20Sopenharmony_ci{ 49118c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 49128c2ecf20Sopenharmony_ci 49138c2ecf20Sopenharmony_ci info->label_block = 2; 49148c2ecf20Sopenharmony_ci info->FBA_layout = private->uses_cdl ? 0 : 1; 49158c2ecf20Sopenharmony_ci info->format = private->uses_cdl ? DASD_FORMAT_CDL : DASD_FORMAT_LDL; 49168c2ecf20Sopenharmony_ci info->characteristics_size = sizeof(private->rdc_data); 49178c2ecf20Sopenharmony_ci memcpy(info->characteristics, &private->rdc_data, 49188c2ecf20Sopenharmony_ci sizeof(private->rdc_data)); 49198c2ecf20Sopenharmony_ci info->confdata_size = min((unsigned long)private->conf_len, 49208c2ecf20Sopenharmony_ci sizeof(info->configuration_data)); 49218c2ecf20Sopenharmony_ci memcpy(info->configuration_data, private->conf_data, 49228c2ecf20Sopenharmony_ci info->confdata_size); 49238c2ecf20Sopenharmony_ci return 0; 49248c2ecf20Sopenharmony_ci} 49258c2ecf20Sopenharmony_ci 49268c2ecf20Sopenharmony_ci/* 49278c2ecf20Sopenharmony_ci * SECTION: ioctl functions for eckd devices. 49288c2ecf20Sopenharmony_ci */ 49298c2ecf20Sopenharmony_ci 49308c2ecf20Sopenharmony_ci/* 49318c2ecf20Sopenharmony_ci * Release device ioctl. 49328c2ecf20Sopenharmony_ci * Buils a channel programm to releases a prior reserved 49338c2ecf20Sopenharmony_ci * (see dasd_eckd_reserve) device. 49348c2ecf20Sopenharmony_ci */ 49358c2ecf20Sopenharmony_cistatic int 49368c2ecf20Sopenharmony_cidasd_eckd_release(struct dasd_device *device) 49378c2ecf20Sopenharmony_ci{ 49388c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 49398c2ecf20Sopenharmony_ci int rc; 49408c2ecf20Sopenharmony_ci struct ccw1 *ccw; 49418c2ecf20Sopenharmony_ci int useglobal; 49428c2ecf20Sopenharmony_ci 49438c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 49448c2ecf20Sopenharmony_ci return -EACCES; 49458c2ecf20Sopenharmony_ci 49468c2ecf20Sopenharmony_ci useglobal = 0; 49478c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL); 49488c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 49498c2ecf20Sopenharmony_ci mutex_lock(&dasd_reserve_mutex); 49508c2ecf20Sopenharmony_ci useglobal = 1; 49518c2ecf20Sopenharmony_ci cqr = &dasd_reserve_req->cqr; 49528c2ecf20Sopenharmony_ci memset(cqr, 0, sizeof(*cqr)); 49538c2ecf20Sopenharmony_ci memset(&dasd_reserve_req->ccw, 0, 49548c2ecf20Sopenharmony_ci sizeof(dasd_reserve_req->ccw)); 49558c2ecf20Sopenharmony_ci cqr->cpaddr = &dasd_reserve_req->ccw; 49568c2ecf20Sopenharmony_ci cqr->data = &dasd_reserve_req->data; 49578c2ecf20Sopenharmony_ci cqr->magic = DASD_ECKD_MAGIC; 49588c2ecf20Sopenharmony_ci } 49598c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 49608c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RELEASE; 49618c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 49628c2ecf20Sopenharmony_ci ccw->count = 32; 49638c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) cqr->data; 49648c2ecf20Sopenharmony_ci cqr->startdev = device; 49658c2ecf20Sopenharmony_ci cqr->memdev = device; 49668c2ecf20Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); 49678c2ecf20Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 49688c2ecf20Sopenharmony_ci cqr->retries = 2; /* set retry counter to enable basic ERP */ 49698c2ecf20Sopenharmony_ci cqr->expires = 2 * HZ; 49708c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 49718c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 49728c2ecf20Sopenharmony_ci 49738c2ecf20Sopenharmony_ci rc = dasd_sleep_on_immediatly(cqr); 49748c2ecf20Sopenharmony_ci if (!rc) 49758c2ecf20Sopenharmony_ci clear_bit(DASD_FLAG_IS_RESERVED, &device->flags); 49768c2ecf20Sopenharmony_ci 49778c2ecf20Sopenharmony_ci if (useglobal) 49788c2ecf20Sopenharmony_ci mutex_unlock(&dasd_reserve_mutex); 49798c2ecf20Sopenharmony_ci else 49808c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 49818c2ecf20Sopenharmony_ci return rc; 49828c2ecf20Sopenharmony_ci} 49838c2ecf20Sopenharmony_ci 49848c2ecf20Sopenharmony_ci/* 49858c2ecf20Sopenharmony_ci * Reserve device ioctl. 49868c2ecf20Sopenharmony_ci * Options are set to 'synchronous wait for interrupt' and 49878c2ecf20Sopenharmony_ci * 'timeout the request'. This leads to a terminate IO if 49888c2ecf20Sopenharmony_ci * the interrupt is outstanding for a certain time. 49898c2ecf20Sopenharmony_ci */ 49908c2ecf20Sopenharmony_cistatic int 49918c2ecf20Sopenharmony_cidasd_eckd_reserve(struct dasd_device *device) 49928c2ecf20Sopenharmony_ci{ 49938c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 49948c2ecf20Sopenharmony_ci int rc; 49958c2ecf20Sopenharmony_ci struct ccw1 *ccw; 49968c2ecf20Sopenharmony_ci int useglobal; 49978c2ecf20Sopenharmony_ci 49988c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 49998c2ecf20Sopenharmony_ci return -EACCES; 50008c2ecf20Sopenharmony_ci 50018c2ecf20Sopenharmony_ci useglobal = 0; 50028c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL); 50038c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 50048c2ecf20Sopenharmony_ci mutex_lock(&dasd_reserve_mutex); 50058c2ecf20Sopenharmony_ci useglobal = 1; 50068c2ecf20Sopenharmony_ci cqr = &dasd_reserve_req->cqr; 50078c2ecf20Sopenharmony_ci memset(cqr, 0, sizeof(*cqr)); 50088c2ecf20Sopenharmony_ci memset(&dasd_reserve_req->ccw, 0, 50098c2ecf20Sopenharmony_ci sizeof(dasd_reserve_req->ccw)); 50108c2ecf20Sopenharmony_ci cqr->cpaddr = &dasd_reserve_req->ccw; 50118c2ecf20Sopenharmony_ci cqr->data = &dasd_reserve_req->data; 50128c2ecf20Sopenharmony_ci cqr->magic = DASD_ECKD_MAGIC; 50138c2ecf20Sopenharmony_ci } 50148c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 50158c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RESERVE; 50168c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 50178c2ecf20Sopenharmony_ci ccw->count = 32; 50188c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) cqr->data; 50198c2ecf20Sopenharmony_ci cqr->startdev = device; 50208c2ecf20Sopenharmony_ci cqr->memdev = device; 50218c2ecf20Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); 50228c2ecf20Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 50238c2ecf20Sopenharmony_ci cqr->retries = 2; /* set retry counter to enable basic ERP */ 50248c2ecf20Sopenharmony_ci cqr->expires = 2 * HZ; 50258c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 50268c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 50278c2ecf20Sopenharmony_ci 50288c2ecf20Sopenharmony_ci rc = dasd_sleep_on_immediatly(cqr); 50298c2ecf20Sopenharmony_ci if (!rc) 50308c2ecf20Sopenharmony_ci set_bit(DASD_FLAG_IS_RESERVED, &device->flags); 50318c2ecf20Sopenharmony_ci 50328c2ecf20Sopenharmony_ci if (useglobal) 50338c2ecf20Sopenharmony_ci mutex_unlock(&dasd_reserve_mutex); 50348c2ecf20Sopenharmony_ci else 50358c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 50368c2ecf20Sopenharmony_ci return rc; 50378c2ecf20Sopenharmony_ci} 50388c2ecf20Sopenharmony_ci 50398c2ecf20Sopenharmony_ci/* 50408c2ecf20Sopenharmony_ci * Steal lock ioctl - unconditional reserve device. 50418c2ecf20Sopenharmony_ci * Buils a channel programm to break a device's reservation. 50428c2ecf20Sopenharmony_ci * (unconditional reserve) 50438c2ecf20Sopenharmony_ci */ 50448c2ecf20Sopenharmony_cistatic int 50458c2ecf20Sopenharmony_cidasd_eckd_steal_lock(struct dasd_device *device) 50468c2ecf20Sopenharmony_ci{ 50478c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 50488c2ecf20Sopenharmony_ci int rc; 50498c2ecf20Sopenharmony_ci struct ccw1 *ccw; 50508c2ecf20Sopenharmony_ci int useglobal; 50518c2ecf20Sopenharmony_ci 50528c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 50538c2ecf20Sopenharmony_ci return -EACCES; 50548c2ecf20Sopenharmony_ci 50558c2ecf20Sopenharmony_ci useglobal = 0; 50568c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL); 50578c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 50588c2ecf20Sopenharmony_ci mutex_lock(&dasd_reserve_mutex); 50598c2ecf20Sopenharmony_ci useglobal = 1; 50608c2ecf20Sopenharmony_ci cqr = &dasd_reserve_req->cqr; 50618c2ecf20Sopenharmony_ci memset(cqr, 0, sizeof(*cqr)); 50628c2ecf20Sopenharmony_ci memset(&dasd_reserve_req->ccw, 0, 50638c2ecf20Sopenharmony_ci sizeof(dasd_reserve_req->ccw)); 50648c2ecf20Sopenharmony_ci cqr->cpaddr = &dasd_reserve_req->ccw; 50658c2ecf20Sopenharmony_ci cqr->data = &dasd_reserve_req->data; 50668c2ecf20Sopenharmony_ci cqr->magic = DASD_ECKD_MAGIC; 50678c2ecf20Sopenharmony_ci } 50688c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 50698c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_SLCK; 50708c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 50718c2ecf20Sopenharmony_ci ccw->count = 32; 50728c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) cqr->data; 50738c2ecf20Sopenharmony_ci cqr->startdev = device; 50748c2ecf20Sopenharmony_ci cqr->memdev = device; 50758c2ecf20Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); 50768c2ecf20Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 50778c2ecf20Sopenharmony_ci cqr->retries = 2; /* set retry counter to enable basic ERP */ 50788c2ecf20Sopenharmony_ci cqr->expires = 2 * HZ; 50798c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 50808c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 50818c2ecf20Sopenharmony_ci 50828c2ecf20Sopenharmony_ci rc = dasd_sleep_on_immediatly(cqr); 50838c2ecf20Sopenharmony_ci if (!rc) 50848c2ecf20Sopenharmony_ci set_bit(DASD_FLAG_IS_RESERVED, &device->flags); 50858c2ecf20Sopenharmony_ci 50868c2ecf20Sopenharmony_ci if (useglobal) 50878c2ecf20Sopenharmony_ci mutex_unlock(&dasd_reserve_mutex); 50888c2ecf20Sopenharmony_ci else 50898c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 50908c2ecf20Sopenharmony_ci return rc; 50918c2ecf20Sopenharmony_ci} 50928c2ecf20Sopenharmony_ci 50938c2ecf20Sopenharmony_ci/* 50948c2ecf20Sopenharmony_ci * SNID - Sense Path Group ID 50958c2ecf20Sopenharmony_ci * This ioctl may be used in situations where I/O is stalled due to 50968c2ecf20Sopenharmony_ci * a reserve, so if the normal dasd_smalloc_request fails, we use the 50978c2ecf20Sopenharmony_ci * preallocated dasd_reserve_req. 50988c2ecf20Sopenharmony_ci */ 50998c2ecf20Sopenharmony_cistatic int dasd_eckd_snid(struct dasd_device *device, 51008c2ecf20Sopenharmony_ci void __user *argp) 51018c2ecf20Sopenharmony_ci{ 51028c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 51038c2ecf20Sopenharmony_ci int rc; 51048c2ecf20Sopenharmony_ci struct ccw1 *ccw; 51058c2ecf20Sopenharmony_ci int useglobal; 51068c2ecf20Sopenharmony_ci struct dasd_snid_ioctl_data usrparm; 51078c2ecf20Sopenharmony_ci 51088c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 51098c2ecf20Sopenharmony_ci return -EACCES; 51108c2ecf20Sopenharmony_ci 51118c2ecf20Sopenharmony_ci if (copy_from_user(&usrparm, argp, sizeof(usrparm))) 51128c2ecf20Sopenharmony_ci return -EFAULT; 51138c2ecf20Sopenharmony_ci 51148c2ecf20Sopenharmony_ci useglobal = 0; 51158c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 51168c2ecf20Sopenharmony_ci sizeof(struct dasd_snid_data), device, 51178c2ecf20Sopenharmony_ci NULL); 51188c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 51198c2ecf20Sopenharmony_ci mutex_lock(&dasd_reserve_mutex); 51208c2ecf20Sopenharmony_ci useglobal = 1; 51218c2ecf20Sopenharmony_ci cqr = &dasd_reserve_req->cqr; 51228c2ecf20Sopenharmony_ci memset(cqr, 0, sizeof(*cqr)); 51238c2ecf20Sopenharmony_ci memset(&dasd_reserve_req->ccw, 0, 51248c2ecf20Sopenharmony_ci sizeof(dasd_reserve_req->ccw)); 51258c2ecf20Sopenharmony_ci cqr->cpaddr = &dasd_reserve_req->ccw; 51268c2ecf20Sopenharmony_ci cqr->data = &dasd_reserve_req->data; 51278c2ecf20Sopenharmony_ci cqr->magic = DASD_ECKD_MAGIC; 51288c2ecf20Sopenharmony_ci } 51298c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 51308c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_SNID; 51318c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 51328c2ecf20Sopenharmony_ci ccw->count = 12; 51338c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) cqr->data; 51348c2ecf20Sopenharmony_ci cqr->startdev = device; 51358c2ecf20Sopenharmony_ci cqr->memdev = device; 51368c2ecf20Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); 51378c2ecf20Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 51388c2ecf20Sopenharmony_ci set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags); 51398c2ecf20Sopenharmony_ci cqr->retries = 5; 51408c2ecf20Sopenharmony_ci cqr->expires = 10 * HZ; 51418c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 51428c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 51438c2ecf20Sopenharmony_ci cqr->lpm = usrparm.path_mask; 51448c2ecf20Sopenharmony_ci 51458c2ecf20Sopenharmony_ci rc = dasd_sleep_on_immediatly(cqr); 51468c2ecf20Sopenharmony_ci /* verify that I/O processing didn't modify the path mask */ 51478c2ecf20Sopenharmony_ci if (!rc && usrparm.path_mask && (cqr->lpm != usrparm.path_mask)) 51488c2ecf20Sopenharmony_ci rc = -EIO; 51498c2ecf20Sopenharmony_ci if (!rc) { 51508c2ecf20Sopenharmony_ci usrparm.data = *((struct dasd_snid_data *)cqr->data); 51518c2ecf20Sopenharmony_ci if (copy_to_user(argp, &usrparm, sizeof(usrparm))) 51528c2ecf20Sopenharmony_ci rc = -EFAULT; 51538c2ecf20Sopenharmony_ci } 51548c2ecf20Sopenharmony_ci 51558c2ecf20Sopenharmony_ci if (useglobal) 51568c2ecf20Sopenharmony_ci mutex_unlock(&dasd_reserve_mutex); 51578c2ecf20Sopenharmony_ci else 51588c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 51598c2ecf20Sopenharmony_ci return rc; 51608c2ecf20Sopenharmony_ci} 51618c2ecf20Sopenharmony_ci 51628c2ecf20Sopenharmony_ci/* 51638c2ecf20Sopenharmony_ci * Read performance statistics 51648c2ecf20Sopenharmony_ci */ 51658c2ecf20Sopenharmony_cistatic int 51668c2ecf20Sopenharmony_cidasd_eckd_performance(struct dasd_device *device, void __user *argp) 51678c2ecf20Sopenharmony_ci{ 51688c2ecf20Sopenharmony_ci struct dasd_psf_prssd_data *prssdp; 51698c2ecf20Sopenharmony_ci struct dasd_rssd_perf_stats_t *stats; 51708c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 51718c2ecf20Sopenharmony_ci struct ccw1 *ccw; 51728c2ecf20Sopenharmony_ci int rc; 51738c2ecf20Sopenharmony_ci 51748c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, 51758c2ecf20Sopenharmony_ci (sizeof(struct dasd_psf_prssd_data) + 51768c2ecf20Sopenharmony_ci sizeof(struct dasd_rssd_perf_stats_t)), 51778c2ecf20Sopenharmony_ci device, NULL); 51788c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 51798c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 51808c2ecf20Sopenharmony_ci "Could not allocate initialization request"); 51818c2ecf20Sopenharmony_ci return PTR_ERR(cqr); 51828c2ecf20Sopenharmony_ci } 51838c2ecf20Sopenharmony_ci cqr->startdev = device; 51848c2ecf20Sopenharmony_ci cqr->memdev = device; 51858c2ecf20Sopenharmony_ci cqr->retries = 0; 51868c2ecf20Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); 51878c2ecf20Sopenharmony_ci cqr->expires = 10 * HZ; 51888c2ecf20Sopenharmony_ci 51898c2ecf20Sopenharmony_ci /* Prepare for Read Subsystem Data */ 51908c2ecf20Sopenharmony_ci prssdp = (struct dasd_psf_prssd_data *) cqr->data; 51918c2ecf20Sopenharmony_ci memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); 51928c2ecf20Sopenharmony_ci prssdp->order = PSF_ORDER_PRSSD; 51938c2ecf20Sopenharmony_ci prssdp->suborder = 0x01; /* Performance Statistics */ 51948c2ecf20Sopenharmony_ci prssdp->varies[1] = 0x01; /* Perf Statistics for the Subsystem */ 51958c2ecf20Sopenharmony_ci 51968c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 51978c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 51988c2ecf20Sopenharmony_ci ccw->count = sizeof(struct dasd_psf_prssd_data); 51998c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_CC; 52008c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) prssdp; 52018c2ecf20Sopenharmony_ci 52028c2ecf20Sopenharmony_ci /* Read Subsystem Data - Performance Statistics */ 52038c2ecf20Sopenharmony_ci stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1); 52048c2ecf20Sopenharmony_ci memset(stats, 0, sizeof(struct dasd_rssd_perf_stats_t)); 52058c2ecf20Sopenharmony_ci 52068c2ecf20Sopenharmony_ci ccw++; 52078c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RSSD; 52088c2ecf20Sopenharmony_ci ccw->count = sizeof(struct dasd_rssd_perf_stats_t); 52098c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) stats; 52108c2ecf20Sopenharmony_ci 52118c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 52128c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 52138c2ecf20Sopenharmony_ci rc = dasd_sleep_on(cqr); 52148c2ecf20Sopenharmony_ci if (rc == 0) { 52158c2ecf20Sopenharmony_ci prssdp = (struct dasd_psf_prssd_data *) cqr->data; 52168c2ecf20Sopenharmony_ci stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1); 52178c2ecf20Sopenharmony_ci if (copy_to_user(argp, stats, 52188c2ecf20Sopenharmony_ci sizeof(struct dasd_rssd_perf_stats_t))) 52198c2ecf20Sopenharmony_ci rc = -EFAULT; 52208c2ecf20Sopenharmony_ci } 52218c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 52228c2ecf20Sopenharmony_ci return rc; 52238c2ecf20Sopenharmony_ci} 52248c2ecf20Sopenharmony_ci 52258c2ecf20Sopenharmony_ci/* 52268c2ecf20Sopenharmony_ci * Get attributes (cache operations) 52278c2ecf20Sopenharmony_ci * Returnes the cache attributes used in Define Extend (DE). 52288c2ecf20Sopenharmony_ci */ 52298c2ecf20Sopenharmony_cistatic int 52308c2ecf20Sopenharmony_cidasd_eckd_get_attrib(struct dasd_device *device, void __user *argp) 52318c2ecf20Sopenharmony_ci{ 52328c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 52338c2ecf20Sopenharmony_ci struct attrib_data_t attrib = private->attrib; 52348c2ecf20Sopenharmony_ci int rc; 52358c2ecf20Sopenharmony_ci 52368c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 52378c2ecf20Sopenharmony_ci return -EACCES; 52388c2ecf20Sopenharmony_ci if (!argp) 52398c2ecf20Sopenharmony_ci return -EINVAL; 52408c2ecf20Sopenharmony_ci 52418c2ecf20Sopenharmony_ci rc = 0; 52428c2ecf20Sopenharmony_ci if (copy_to_user(argp, (long *) &attrib, 52438c2ecf20Sopenharmony_ci sizeof(struct attrib_data_t))) 52448c2ecf20Sopenharmony_ci rc = -EFAULT; 52458c2ecf20Sopenharmony_ci 52468c2ecf20Sopenharmony_ci return rc; 52478c2ecf20Sopenharmony_ci} 52488c2ecf20Sopenharmony_ci 52498c2ecf20Sopenharmony_ci/* 52508c2ecf20Sopenharmony_ci * Set attributes (cache operations) 52518c2ecf20Sopenharmony_ci * Stores the attributes for cache operation to be used in Define Extend (DE). 52528c2ecf20Sopenharmony_ci */ 52538c2ecf20Sopenharmony_cistatic int 52548c2ecf20Sopenharmony_cidasd_eckd_set_attrib(struct dasd_device *device, void __user *argp) 52558c2ecf20Sopenharmony_ci{ 52568c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 52578c2ecf20Sopenharmony_ci struct attrib_data_t attrib; 52588c2ecf20Sopenharmony_ci 52598c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 52608c2ecf20Sopenharmony_ci return -EACCES; 52618c2ecf20Sopenharmony_ci if (!argp) 52628c2ecf20Sopenharmony_ci return -EINVAL; 52638c2ecf20Sopenharmony_ci 52648c2ecf20Sopenharmony_ci if (copy_from_user(&attrib, argp, sizeof(struct attrib_data_t))) 52658c2ecf20Sopenharmony_ci return -EFAULT; 52668c2ecf20Sopenharmony_ci private->attrib = attrib; 52678c2ecf20Sopenharmony_ci 52688c2ecf20Sopenharmony_ci dev_info(&device->cdev->dev, 52698c2ecf20Sopenharmony_ci "The DASD cache mode was set to %x (%i cylinder prestage)\n", 52708c2ecf20Sopenharmony_ci private->attrib.operation, private->attrib.nr_cyl); 52718c2ecf20Sopenharmony_ci return 0; 52728c2ecf20Sopenharmony_ci} 52738c2ecf20Sopenharmony_ci 52748c2ecf20Sopenharmony_ci/* 52758c2ecf20Sopenharmony_ci * Issue syscall I/O to EMC Symmetrix array. 52768c2ecf20Sopenharmony_ci * CCWs are PSF and RSSD 52778c2ecf20Sopenharmony_ci */ 52788c2ecf20Sopenharmony_cistatic int dasd_symm_io(struct dasd_device *device, void __user *argp) 52798c2ecf20Sopenharmony_ci{ 52808c2ecf20Sopenharmony_ci struct dasd_symmio_parms usrparm; 52818c2ecf20Sopenharmony_ci char *psf_data, *rssd_result; 52828c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 52838c2ecf20Sopenharmony_ci struct ccw1 *ccw; 52848c2ecf20Sopenharmony_ci char psf0, psf1; 52858c2ecf20Sopenharmony_ci int rc; 52868c2ecf20Sopenharmony_ci 52878c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) 52888c2ecf20Sopenharmony_ci return -EACCES; 52898c2ecf20Sopenharmony_ci psf0 = psf1 = 0; 52908c2ecf20Sopenharmony_ci 52918c2ecf20Sopenharmony_ci /* Copy parms from caller */ 52928c2ecf20Sopenharmony_ci rc = -EFAULT; 52938c2ecf20Sopenharmony_ci if (copy_from_user(&usrparm, argp, sizeof(usrparm))) 52948c2ecf20Sopenharmony_ci goto out; 52958c2ecf20Sopenharmony_ci if (is_compat_task()) { 52968c2ecf20Sopenharmony_ci /* Make sure pointers are sane even on 31 bit. */ 52978c2ecf20Sopenharmony_ci rc = -EINVAL; 52988c2ecf20Sopenharmony_ci if ((usrparm.psf_data >> 32) != 0) 52998c2ecf20Sopenharmony_ci goto out; 53008c2ecf20Sopenharmony_ci if ((usrparm.rssd_result >> 32) != 0) 53018c2ecf20Sopenharmony_ci goto out; 53028c2ecf20Sopenharmony_ci usrparm.psf_data &= 0x7fffffffULL; 53038c2ecf20Sopenharmony_ci usrparm.rssd_result &= 0x7fffffffULL; 53048c2ecf20Sopenharmony_ci } 53058c2ecf20Sopenharmony_ci /* at least 2 bytes are accessed and should be allocated */ 53068c2ecf20Sopenharmony_ci if (usrparm.psf_data_len < 2) { 53078c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, 53088c2ecf20Sopenharmony_ci "Symmetrix ioctl invalid data length %d", 53098c2ecf20Sopenharmony_ci usrparm.psf_data_len); 53108c2ecf20Sopenharmony_ci rc = -EINVAL; 53118c2ecf20Sopenharmony_ci goto out; 53128c2ecf20Sopenharmony_ci } 53138c2ecf20Sopenharmony_ci /* alloc I/O data area */ 53148c2ecf20Sopenharmony_ci psf_data = kzalloc(usrparm.psf_data_len, GFP_KERNEL | GFP_DMA); 53158c2ecf20Sopenharmony_ci rssd_result = kzalloc(usrparm.rssd_result_len, GFP_KERNEL | GFP_DMA); 53168c2ecf20Sopenharmony_ci if (!psf_data || !rssd_result) { 53178c2ecf20Sopenharmony_ci rc = -ENOMEM; 53188c2ecf20Sopenharmony_ci goto out_free; 53198c2ecf20Sopenharmony_ci } 53208c2ecf20Sopenharmony_ci 53218c2ecf20Sopenharmony_ci /* get syscall header from user space */ 53228c2ecf20Sopenharmony_ci rc = -EFAULT; 53238c2ecf20Sopenharmony_ci if (copy_from_user(psf_data, 53248c2ecf20Sopenharmony_ci (void __user *)(unsigned long) usrparm.psf_data, 53258c2ecf20Sopenharmony_ci usrparm.psf_data_len)) 53268c2ecf20Sopenharmony_ci goto out_free; 53278c2ecf20Sopenharmony_ci psf0 = psf_data[0]; 53288c2ecf20Sopenharmony_ci psf1 = psf_data[1]; 53298c2ecf20Sopenharmony_ci 53308c2ecf20Sopenharmony_ci /* setup CCWs for PSF + RSSD */ 53318c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2, 0, device, NULL); 53328c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 53338c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 53348c2ecf20Sopenharmony_ci "Could not allocate initialization request"); 53358c2ecf20Sopenharmony_ci rc = PTR_ERR(cqr); 53368c2ecf20Sopenharmony_ci goto out_free; 53378c2ecf20Sopenharmony_ci } 53388c2ecf20Sopenharmony_ci 53398c2ecf20Sopenharmony_ci cqr->startdev = device; 53408c2ecf20Sopenharmony_ci cqr->memdev = device; 53418c2ecf20Sopenharmony_ci cqr->retries = 3; 53428c2ecf20Sopenharmony_ci cqr->expires = 10 * HZ; 53438c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 53448c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 53458c2ecf20Sopenharmony_ci 53468c2ecf20Sopenharmony_ci /* Build the ccws */ 53478c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 53488c2ecf20Sopenharmony_ci 53498c2ecf20Sopenharmony_ci /* PSF ccw */ 53508c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 53518c2ecf20Sopenharmony_ci ccw->count = usrparm.psf_data_len; 53528c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_CC; 53538c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) psf_data; 53548c2ecf20Sopenharmony_ci 53558c2ecf20Sopenharmony_ci ccw++; 53568c2ecf20Sopenharmony_ci 53578c2ecf20Sopenharmony_ci /* RSSD ccw */ 53588c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RSSD; 53598c2ecf20Sopenharmony_ci ccw->count = usrparm.rssd_result_len; 53608c2ecf20Sopenharmony_ci ccw->flags = CCW_FLAG_SLI ; 53618c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) rssd_result; 53628c2ecf20Sopenharmony_ci 53638c2ecf20Sopenharmony_ci rc = dasd_sleep_on(cqr); 53648c2ecf20Sopenharmony_ci if (rc) 53658c2ecf20Sopenharmony_ci goto out_sfree; 53668c2ecf20Sopenharmony_ci 53678c2ecf20Sopenharmony_ci rc = -EFAULT; 53688c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)(unsigned long) usrparm.rssd_result, 53698c2ecf20Sopenharmony_ci rssd_result, usrparm.rssd_result_len)) 53708c2ecf20Sopenharmony_ci goto out_sfree; 53718c2ecf20Sopenharmony_ci rc = 0; 53728c2ecf20Sopenharmony_ci 53738c2ecf20Sopenharmony_ciout_sfree: 53748c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 53758c2ecf20Sopenharmony_ciout_free: 53768c2ecf20Sopenharmony_ci kfree(rssd_result); 53778c2ecf20Sopenharmony_ci kfree(psf_data); 53788c2ecf20Sopenharmony_ciout: 53798c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, 53808c2ecf20Sopenharmony_ci "Symmetrix ioctl (0x%02x 0x%02x): rc=%d", 53818c2ecf20Sopenharmony_ci (int) psf0, (int) psf1, rc); 53828c2ecf20Sopenharmony_ci return rc; 53838c2ecf20Sopenharmony_ci} 53848c2ecf20Sopenharmony_ci 53858c2ecf20Sopenharmony_cistatic int 53868c2ecf20Sopenharmony_cidasd_eckd_ioctl(struct dasd_block *block, unsigned int cmd, void __user *argp) 53878c2ecf20Sopenharmony_ci{ 53888c2ecf20Sopenharmony_ci struct dasd_device *device = block->base; 53898c2ecf20Sopenharmony_ci 53908c2ecf20Sopenharmony_ci switch (cmd) { 53918c2ecf20Sopenharmony_ci case BIODASDGATTR: 53928c2ecf20Sopenharmony_ci return dasd_eckd_get_attrib(device, argp); 53938c2ecf20Sopenharmony_ci case BIODASDSATTR: 53948c2ecf20Sopenharmony_ci return dasd_eckd_set_attrib(device, argp); 53958c2ecf20Sopenharmony_ci case BIODASDPSRD: 53968c2ecf20Sopenharmony_ci return dasd_eckd_performance(device, argp); 53978c2ecf20Sopenharmony_ci case BIODASDRLSE: 53988c2ecf20Sopenharmony_ci return dasd_eckd_release(device); 53998c2ecf20Sopenharmony_ci case BIODASDRSRV: 54008c2ecf20Sopenharmony_ci return dasd_eckd_reserve(device); 54018c2ecf20Sopenharmony_ci case BIODASDSLCK: 54028c2ecf20Sopenharmony_ci return dasd_eckd_steal_lock(device); 54038c2ecf20Sopenharmony_ci case BIODASDSNID: 54048c2ecf20Sopenharmony_ci return dasd_eckd_snid(device, argp); 54058c2ecf20Sopenharmony_ci case BIODASDSYMMIO: 54068c2ecf20Sopenharmony_ci return dasd_symm_io(device, argp); 54078c2ecf20Sopenharmony_ci default: 54088c2ecf20Sopenharmony_ci return -ENOTTY; 54098c2ecf20Sopenharmony_ci } 54108c2ecf20Sopenharmony_ci} 54118c2ecf20Sopenharmony_ci 54128c2ecf20Sopenharmony_ci/* 54138c2ecf20Sopenharmony_ci * Dump the range of CCWs into 'page' buffer 54148c2ecf20Sopenharmony_ci * and return number of printed chars. 54158c2ecf20Sopenharmony_ci */ 54168c2ecf20Sopenharmony_cistatic int 54178c2ecf20Sopenharmony_cidasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page) 54188c2ecf20Sopenharmony_ci{ 54198c2ecf20Sopenharmony_ci int len, count; 54208c2ecf20Sopenharmony_ci char *datap; 54218c2ecf20Sopenharmony_ci 54228c2ecf20Sopenharmony_ci len = 0; 54238c2ecf20Sopenharmony_ci while (from <= to) { 54248c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 54258c2ecf20Sopenharmony_ci " CCW %p: %08X %08X DAT:", 54268c2ecf20Sopenharmony_ci from, ((int *) from)[0], ((int *) from)[1]); 54278c2ecf20Sopenharmony_ci 54288c2ecf20Sopenharmony_ci /* get pointer to data (consider IDALs) */ 54298c2ecf20Sopenharmony_ci if (from->flags & CCW_FLAG_IDA) 54308c2ecf20Sopenharmony_ci datap = (char *) *((addr_t *) (addr_t) from->cda); 54318c2ecf20Sopenharmony_ci else 54328c2ecf20Sopenharmony_ci datap = (char *) ((addr_t) from->cda); 54338c2ecf20Sopenharmony_ci 54348c2ecf20Sopenharmony_ci /* dump data (max 32 bytes) */ 54358c2ecf20Sopenharmony_ci for (count = 0; count < from->count && count < 32; count++) { 54368c2ecf20Sopenharmony_ci if (count % 8 == 0) len += sprintf(page + len, " "); 54378c2ecf20Sopenharmony_ci if (count % 4 == 0) len += sprintf(page + len, " "); 54388c2ecf20Sopenharmony_ci len += sprintf(page + len, "%02x", datap[count]); 54398c2ecf20Sopenharmony_ci } 54408c2ecf20Sopenharmony_ci len += sprintf(page + len, "\n"); 54418c2ecf20Sopenharmony_ci from++; 54428c2ecf20Sopenharmony_ci } 54438c2ecf20Sopenharmony_ci return len; 54448c2ecf20Sopenharmony_ci} 54458c2ecf20Sopenharmony_ci 54468c2ecf20Sopenharmony_cistatic void 54478c2ecf20Sopenharmony_cidasd_eckd_dump_sense_dbf(struct dasd_device *device, struct irb *irb, 54488c2ecf20Sopenharmony_ci char *reason) 54498c2ecf20Sopenharmony_ci{ 54508c2ecf20Sopenharmony_ci u64 *sense; 54518c2ecf20Sopenharmony_ci u64 *stat; 54528c2ecf20Sopenharmony_ci 54538c2ecf20Sopenharmony_ci sense = (u64 *) dasd_get_sense(irb); 54548c2ecf20Sopenharmony_ci stat = (u64 *) &irb->scsw; 54558c2ecf20Sopenharmony_ci if (sense) { 54568c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_EMERG, device, "%s: %016llx %08x : " 54578c2ecf20Sopenharmony_ci "%016llx %016llx %016llx %016llx", 54588c2ecf20Sopenharmony_ci reason, *stat, *((u32 *) (stat + 1)), 54598c2ecf20Sopenharmony_ci sense[0], sense[1], sense[2], sense[3]); 54608c2ecf20Sopenharmony_ci } else { 54618c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_EMERG, device, "%s: %016llx %08x : %s", 54628c2ecf20Sopenharmony_ci reason, *stat, *((u32 *) (stat + 1)), 54638c2ecf20Sopenharmony_ci "NO VALID SENSE"); 54648c2ecf20Sopenharmony_ci } 54658c2ecf20Sopenharmony_ci} 54668c2ecf20Sopenharmony_ci 54678c2ecf20Sopenharmony_ci/* 54688c2ecf20Sopenharmony_ci * Print sense data and related channel program. 54698c2ecf20Sopenharmony_ci * Parts are printed because printk buffer is only 1024 bytes. 54708c2ecf20Sopenharmony_ci */ 54718c2ecf20Sopenharmony_cistatic void dasd_eckd_dump_sense_ccw(struct dasd_device *device, 54728c2ecf20Sopenharmony_ci struct dasd_ccw_req *req, struct irb *irb) 54738c2ecf20Sopenharmony_ci{ 54748c2ecf20Sopenharmony_ci char *page; 54758c2ecf20Sopenharmony_ci struct ccw1 *first, *last, *fail, *from, *to; 54768c2ecf20Sopenharmony_ci int len, sl, sct; 54778c2ecf20Sopenharmony_ci 54788c2ecf20Sopenharmony_ci page = (char *) get_zeroed_page(GFP_ATOMIC); 54798c2ecf20Sopenharmony_ci if (page == NULL) { 54808c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 54818c2ecf20Sopenharmony_ci "No memory to dump sense data\n"); 54828c2ecf20Sopenharmony_ci return; 54838c2ecf20Sopenharmony_ci } 54848c2ecf20Sopenharmony_ci /* dump the sense data */ 54858c2ecf20Sopenharmony_ci len = sprintf(page, PRINTK_HEADER 54868c2ecf20Sopenharmony_ci " I/O status report for device %s:\n", 54878c2ecf20Sopenharmony_ci dev_name(&device->cdev->dev)); 54888c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 54898c2ecf20Sopenharmony_ci " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X " 54908c2ecf20Sopenharmony_ci "CS:%02X RC:%d\n", 54918c2ecf20Sopenharmony_ci req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw), 54928c2ecf20Sopenharmony_ci scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw), 54938c2ecf20Sopenharmony_ci scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw), 54948c2ecf20Sopenharmony_ci req ? req->intrc : 0); 54958c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 54968c2ecf20Sopenharmony_ci " device %s: Failing CCW: %p\n", 54978c2ecf20Sopenharmony_ci dev_name(&device->cdev->dev), 54988c2ecf20Sopenharmony_ci (void *) (addr_t) irb->scsw.cmd.cpa); 54998c2ecf20Sopenharmony_ci if (irb->esw.esw0.erw.cons) { 55008c2ecf20Sopenharmony_ci for (sl = 0; sl < 4; sl++) { 55018c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 55028c2ecf20Sopenharmony_ci " Sense(hex) %2d-%2d:", 55038c2ecf20Sopenharmony_ci (8 * sl), ((8 * sl) + 7)); 55048c2ecf20Sopenharmony_ci 55058c2ecf20Sopenharmony_ci for (sct = 0; sct < 8; sct++) { 55068c2ecf20Sopenharmony_ci len += sprintf(page + len, " %02x", 55078c2ecf20Sopenharmony_ci irb->ecw[8 * sl + sct]); 55088c2ecf20Sopenharmony_ci } 55098c2ecf20Sopenharmony_ci len += sprintf(page + len, "\n"); 55108c2ecf20Sopenharmony_ci } 55118c2ecf20Sopenharmony_ci 55128c2ecf20Sopenharmony_ci if (irb->ecw[27] & DASD_SENSE_BIT_0) { 55138c2ecf20Sopenharmony_ci /* 24 Byte Sense Data */ 55148c2ecf20Sopenharmony_ci sprintf(page + len, PRINTK_HEADER 55158c2ecf20Sopenharmony_ci " 24 Byte: %x MSG %x, " 55168c2ecf20Sopenharmony_ci "%s MSGb to SYSOP\n", 55178c2ecf20Sopenharmony_ci irb->ecw[7] >> 4, irb->ecw[7] & 0x0f, 55188c2ecf20Sopenharmony_ci irb->ecw[1] & 0x10 ? "" : "no"); 55198c2ecf20Sopenharmony_ci } else { 55208c2ecf20Sopenharmony_ci /* 32 Byte Sense Data */ 55218c2ecf20Sopenharmony_ci sprintf(page + len, PRINTK_HEADER 55228c2ecf20Sopenharmony_ci " 32 Byte: Format: %x " 55238c2ecf20Sopenharmony_ci "Exception class %x\n", 55248c2ecf20Sopenharmony_ci irb->ecw[6] & 0x0f, irb->ecw[22] >> 4); 55258c2ecf20Sopenharmony_ci } 55268c2ecf20Sopenharmony_ci } else { 55278c2ecf20Sopenharmony_ci sprintf(page + len, PRINTK_HEADER 55288c2ecf20Sopenharmony_ci " SORRY - NO VALID SENSE AVAILABLE\n"); 55298c2ecf20Sopenharmony_ci } 55308c2ecf20Sopenharmony_ci printk(KERN_ERR "%s", page); 55318c2ecf20Sopenharmony_ci 55328c2ecf20Sopenharmony_ci if (req) { 55338c2ecf20Sopenharmony_ci /* req == NULL for unsolicited interrupts */ 55348c2ecf20Sopenharmony_ci /* dump the Channel Program (max 140 Bytes per line) */ 55358c2ecf20Sopenharmony_ci /* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */ 55368c2ecf20Sopenharmony_ci first = req->cpaddr; 55378c2ecf20Sopenharmony_ci for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); 55388c2ecf20Sopenharmony_ci to = min(first + 6, last); 55398c2ecf20Sopenharmony_ci len = sprintf(page, PRINTK_HEADER 55408c2ecf20Sopenharmony_ci " Related CP in req: %p\n", req); 55418c2ecf20Sopenharmony_ci dasd_eckd_dump_ccw_range(first, to, page + len); 55428c2ecf20Sopenharmony_ci printk(KERN_ERR "%s", page); 55438c2ecf20Sopenharmony_ci 55448c2ecf20Sopenharmony_ci /* print failing CCW area (maximum 4) */ 55458c2ecf20Sopenharmony_ci /* scsw->cda is either valid or zero */ 55468c2ecf20Sopenharmony_ci len = 0; 55478c2ecf20Sopenharmony_ci from = ++to; 55488c2ecf20Sopenharmony_ci fail = (struct ccw1 *)(addr_t) 55498c2ecf20Sopenharmony_ci irb->scsw.cmd.cpa; /* failing CCW */ 55508c2ecf20Sopenharmony_ci if (from < fail - 2) { 55518c2ecf20Sopenharmony_ci from = fail - 2; /* there is a gap - print header */ 55528c2ecf20Sopenharmony_ci len += sprintf(page, PRINTK_HEADER "......\n"); 55538c2ecf20Sopenharmony_ci } 55548c2ecf20Sopenharmony_ci to = min(fail + 1, last); 55558c2ecf20Sopenharmony_ci len += dasd_eckd_dump_ccw_range(from, to, page + len); 55568c2ecf20Sopenharmony_ci 55578c2ecf20Sopenharmony_ci /* print last CCWs (maximum 2) */ 55588c2ecf20Sopenharmony_ci from = max(from, ++to); 55598c2ecf20Sopenharmony_ci if (from < last - 1) { 55608c2ecf20Sopenharmony_ci from = last - 1; /* there is a gap - print header */ 55618c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER "......\n"); 55628c2ecf20Sopenharmony_ci } 55638c2ecf20Sopenharmony_ci len += dasd_eckd_dump_ccw_range(from, last, page + len); 55648c2ecf20Sopenharmony_ci if (len > 0) 55658c2ecf20Sopenharmony_ci printk(KERN_ERR "%s", page); 55668c2ecf20Sopenharmony_ci } 55678c2ecf20Sopenharmony_ci free_page((unsigned long) page); 55688c2ecf20Sopenharmony_ci} 55698c2ecf20Sopenharmony_ci 55708c2ecf20Sopenharmony_ci 55718c2ecf20Sopenharmony_ci/* 55728c2ecf20Sopenharmony_ci * Print sense data from a tcw. 55738c2ecf20Sopenharmony_ci */ 55748c2ecf20Sopenharmony_cistatic void dasd_eckd_dump_sense_tcw(struct dasd_device *device, 55758c2ecf20Sopenharmony_ci struct dasd_ccw_req *req, struct irb *irb) 55768c2ecf20Sopenharmony_ci{ 55778c2ecf20Sopenharmony_ci char *page; 55788c2ecf20Sopenharmony_ci int len, sl, sct, residual; 55798c2ecf20Sopenharmony_ci struct tsb *tsb; 55808c2ecf20Sopenharmony_ci u8 *sense, *rcq; 55818c2ecf20Sopenharmony_ci 55828c2ecf20Sopenharmony_ci page = (char *) get_zeroed_page(GFP_ATOMIC); 55838c2ecf20Sopenharmony_ci if (page == NULL) { 55848c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, " %s", 55858c2ecf20Sopenharmony_ci "No memory to dump sense data"); 55868c2ecf20Sopenharmony_ci return; 55878c2ecf20Sopenharmony_ci } 55888c2ecf20Sopenharmony_ci /* dump the sense data */ 55898c2ecf20Sopenharmony_ci len = sprintf(page, PRINTK_HEADER 55908c2ecf20Sopenharmony_ci " I/O status report for device %s:\n", 55918c2ecf20Sopenharmony_ci dev_name(&device->cdev->dev)); 55928c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 55938c2ecf20Sopenharmony_ci " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X " 55948c2ecf20Sopenharmony_ci "CS:%02X fcxs:%02X schxs:%02X RC:%d\n", 55958c2ecf20Sopenharmony_ci req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw), 55968c2ecf20Sopenharmony_ci scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw), 55978c2ecf20Sopenharmony_ci scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw), 55988c2ecf20Sopenharmony_ci irb->scsw.tm.fcxs, 55998c2ecf20Sopenharmony_ci (irb->scsw.tm.ifob << 7) | irb->scsw.tm.sesq, 56008c2ecf20Sopenharmony_ci req ? req->intrc : 0); 56018c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 56028c2ecf20Sopenharmony_ci " device %s: Failing TCW: %p\n", 56038c2ecf20Sopenharmony_ci dev_name(&device->cdev->dev), 56048c2ecf20Sopenharmony_ci (void *) (addr_t) irb->scsw.tm.tcw); 56058c2ecf20Sopenharmony_ci 56068c2ecf20Sopenharmony_ci tsb = NULL; 56078c2ecf20Sopenharmony_ci sense = NULL; 56088c2ecf20Sopenharmony_ci if (irb->scsw.tm.tcw && (irb->scsw.tm.fcxs & 0x01)) 56098c2ecf20Sopenharmony_ci tsb = tcw_get_tsb( 56108c2ecf20Sopenharmony_ci (struct tcw *)(unsigned long)irb->scsw.tm.tcw); 56118c2ecf20Sopenharmony_ci 56128c2ecf20Sopenharmony_ci if (tsb) { 56138c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 56148c2ecf20Sopenharmony_ci " tsb->length %d\n", tsb->length); 56158c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 56168c2ecf20Sopenharmony_ci " tsb->flags %x\n", tsb->flags); 56178c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 56188c2ecf20Sopenharmony_ci " tsb->dcw_offset %d\n", tsb->dcw_offset); 56198c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 56208c2ecf20Sopenharmony_ci " tsb->count %d\n", tsb->count); 56218c2ecf20Sopenharmony_ci residual = tsb->count - 28; 56228c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 56238c2ecf20Sopenharmony_ci " residual %d\n", residual); 56248c2ecf20Sopenharmony_ci 56258c2ecf20Sopenharmony_ci switch (tsb->flags & 0x07) { 56268c2ecf20Sopenharmony_ci case 1: /* tsa_iostat */ 56278c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 56288c2ecf20Sopenharmony_ci " tsb->tsa.iostat.dev_time %d\n", 56298c2ecf20Sopenharmony_ci tsb->tsa.iostat.dev_time); 56308c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 56318c2ecf20Sopenharmony_ci " tsb->tsa.iostat.def_time %d\n", 56328c2ecf20Sopenharmony_ci tsb->tsa.iostat.def_time); 56338c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 56348c2ecf20Sopenharmony_ci " tsb->tsa.iostat.queue_time %d\n", 56358c2ecf20Sopenharmony_ci tsb->tsa.iostat.queue_time); 56368c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 56378c2ecf20Sopenharmony_ci " tsb->tsa.iostat.dev_busy_time %d\n", 56388c2ecf20Sopenharmony_ci tsb->tsa.iostat.dev_busy_time); 56398c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 56408c2ecf20Sopenharmony_ci " tsb->tsa.iostat.dev_act_time %d\n", 56418c2ecf20Sopenharmony_ci tsb->tsa.iostat.dev_act_time); 56428c2ecf20Sopenharmony_ci sense = tsb->tsa.iostat.sense; 56438c2ecf20Sopenharmony_ci break; 56448c2ecf20Sopenharmony_ci case 2: /* ts_ddpc */ 56458c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 56468c2ecf20Sopenharmony_ci " tsb->tsa.ddpc.rc %d\n", tsb->tsa.ddpc.rc); 56478c2ecf20Sopenharmony_ci for (sl = 0; sl < 2; sl++) { 56488c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 56498c2ecf20Sopenharmony_ci " tsb->tsa.ddpc.rcq %2d-%2d: ", 56508c2ecf20Sopenharmony_ci (8 * sl), ((8 * sl) + 7)); 56518c2ecf20Sopenharmony_ci rcq = tsb->tsa.ddpc.rcq; 56528c2ecf20Sopenharmony_ci for (sct = 0; sct < 8; sct++) { 56538c2ecf20Sopenharmony_ci len += sprintf(page + len, " %02x", 56548c2ecf20Sopenharmony_ci rcq[8 * sl + sct]); 56558c2ecf20Sopenharmony_ci } 56568c2ecf20Sopenharmony_ci len += sprintf(page + len, "\n"); 56578c2ecf20Sopenharmony_ci } 56588c2ecf20Sopenharmony_ci sense = tsb->tsa.ddpc.sense; 56598c2ecf20Sopenharmony_ci break; 56608c2ecf20Sopenharmony_ci case 3: /* tsa_intrg */ 56618c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 56628c2ecf20Sopenharmony_ci " tsb->tsa.intrg.: not supported yet\n"); 56638c2ecf20Sopenharmony_ci break; 56648c2ecf20Sopenharmony_ci } 56658c2ecf20Sopenharmony_ci 56668c2ecf20Sopenharmony_ci if (sense) { 56678c2ecf20Sopenharmony_ci for (sl = 0; sl < 4; sl++) { 56688c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 56698c2ecf20Sopenharmony_ci " Sense(hex) %2d-%2d:", 56708c2ecf20Sopenharmony_ci (8 * sl), ((8 * sl) + 7)); 56718c2ecf20Sopenharmony_ci for (sct = 0; sct < 8; sct++) { 56728c2ecf20Sopenharmony_ci len += sprintf(page + len, " %02x", 56738c2ecf20Sopenharmony_ci sense[8 * sl + sct]); 56748c2ecf20Sopenharmony_ci } 56758c2ecf20Sopenharmony_ci len += sprintf(page + len, "\n"); 56768c2ecf20Sopenharmony_ci } 56778c2ecf20Sopenharmony_ci 56788c2ecf20Sopenharmony_ci if (sense[27] & DASD_SENSE_BIT_0) { 56798c2ecf20Sopenharmony_ci /* 24 Byte Sense Data */ 56808c2ecf20Sopenharmony_ci sprintf(page + len, PRINTK_HEADER 56818c2ecf20Sopenharmony_ci " 24 Byte: %x MSG %x, " 56828c2ecf20Sopenharmony_ci "%s MSGb to SYSOP\n", 56838c2ecf20Sopenharmony_ci sense[7] >> 4, sense[7] & 0x0f, 56848c2ecf20Sopenharmony_ci sense[1] & 0x10 ? "" : "no"); 56858c2ecf20Sopenharmony_ci } else { 56868c2ecf20Sopenharmony_ci /* 32 Byte Sense Data */ 56878c2ecf20Sopenharmony_ci sprintf(page + len, PRINTK_HEADER 56888c2ecf20Sopenharmony_ci " 32 Byte: Format: %x " 56898c2ecf20Sopenharmony_ci "Exception class %x\n", 56908c2ecf20Sopenharmony_ci sense[6] & 0x0f, sense[22] >> 4); 56918c2ecf20Sopenharmony_ci } 56928c2ecf20Sopenharmony_ci } else { 56938c2ecf20Sopenharmony_ci sprintf(page + len, PRINTK_HEADER 56948c2ecf20Sopenharmony_ci " SORRY - NO VALID SENSE AVAILABLE\n"); 56958c2ecf20Sopenharmony_ci } 56968c2ecf20Sopenharmony_ci } else { 56978c2ecf20Sopenharmony_ci sprintf(page + len, PRINTK_HEADER 56988c2ecf20Sopenharmony_ci " SORRY - NO TSB DATA AVAILABLE\n"); 56998c2ecf20Sopenharmony_ci } 57008c2ecf20Sopenharmony_ci printk(KERN_ERR "%s", page); 57018c2ecf20Sopenharmony_ci free_page((unsigned long) page); 57028c2ecf20Sopenharmony_ci} 57038c2ecf20Sopenharmony_ci 57048c2ecf20Sopenharmony_cistatic void dasd_eckd_dump_sense(struct dasd_device *device, 57058c2ecf20Sopenharmony_ci struct dasd_ccw_req *req, struct irb *irb) 57068c2ecf20Sopenharmony_ci{ 57078c2ecf20Sopenharmony_ci u8 *sense = dasd_get_sense(irb); 57088c2ecf20Sopenharmony_ci 57098c2ecf20Sopenharmony_ci if (scsw_is_tm(&irb->scsw)) { 57108c2ecf20Sopenharmony_ci /* 57118c2ecf20Sopenharmony_ci * In some cases the 'File Protected' or 'Incorrect Length' 57128c2ecf20Sopenharmony_ci * error might be expected and log messages shouldn't be written 57138c2ecf20Sopenharmony_ci * then. Check if the according suppress bit is set. 57148c2ecf20Sopenharmony_ci */ 57158c2ecf20Sopenharmony_ci if (sense && (sense[1] & SNS1_FILE_PROTECTED) && 57168c2ecf20Sopenharmony_ci test_bit(DASD_CQR_SUPPRESS_FP, &req->flags)) 57178c2ecf20Sopenharmony_ci return; 57188c2ecf20Sopenharmony_ci if (scsw_cstat(&irb->scsw) == 0x40 && 57198c2ecf20Sopenharmony_ci test_bit(DASD_CQR_SUPPRESS_IL, &req->flags)) 57208c2ecf20Sopenharmony_ci return; 57218c2ecf20Sopenharmony_ci 57228c2ecf20Sopenharmony_ci dasd_eckd_dump_sense_tcw(device, req, irb); 57238c2ecf20Sopenharmony_ci } else { 57248c2ecf20Sopenharmony_ci /* 57258c2ecf20Sopenharmony_ci * In some cases the 'Command Reject' or 'No Record Found' 57268c2ecf20Sopenharmony_ci * error might be expected and log messages shouldn't be 57278c2ecf20Sopenharmony_ci * written then. Check if the according suppress bit is set. 57288c2ecf20Sopenharmony_ci */ 57298c2ecf20Sopenharmony_ci if (sense && sense[0] & SNS0_CMD_REJECT && 57308c2ecf20Sopenharmony_ci test_bit(DASD_CQR_SUPPRESS_CR, &req->flags)) 57318c2ecf20Sopenharmony_ci return; 57328c2ecf20Sopenharmony_ci 57338c2ecf20Sopenharmony_ci if (sense && sense[1] & SNS1_NO_REC_FOUND && 57348c2ecf20Sopenharmony_ci test_bit(DASD_CQR_SUPPRESS_NRF, &req->flags)) 57358c2ecf20Sopenharmony_ci return; 57368c2ecf20Sopenharmony_ci 57378c2ecf20Sopenharmony_ci dasd_eckd_dump_sense_ccw(device, req, irb); 57388c2ecf20Sopenharmony_ci } 57398c2ecf20Sopenharmony_ci} 57408c2ecf20Sopenharmony_ci 57418c2ecf20Sopenharmony_cistatic int dasd_eckd_pm_freeze(struct dasd_device *device) 57428c2ecf20Sopenharmony_ci{ 57438c2ecf20Sopenharmony_ci /* 57448c2ecf20Sopenharmony_ci * the device should be disconnected from our LCU structure 57458c2ecf20Sopenharmony_ci * on restore we will reconnect it and reread LCU specific 57468c2ecf20Sopenharmony_ci * information like PAV support that might have changed 57478c2ecf20Sopenharmony_ci */ 57488c2ecf20Sopenharmony_ci dasd_alias_remove_device(device); 57498c2ecf20Sopenharmony_ci dasd_alias_disconnect_device_from_lcu(device); 57508c2ecf20Sopenharmony_ci 57518c2ecf20Sopenharmony_ci return 0; 57528c2ecf20Sopenharmony_ci} 57538c2ecf20Sopenharmony_ci 57548c2ecf20Sopenharmony_cistatic int dasd_eckd_restore_device(struct dasd_device *device) 57558c2ecf20Sopenharmony_ci{ 57568c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 57578c2ecf20Sopenharmony_ci struct dasd_eckd_characteristics temp_rdc_data; 57588c2ecf20Sopenharmony_ci int rc; 57598c2ecf20Sopenharmony_ci struct dasd_uid temp_uid; 57608c2ecf20Sopenharmony_ci unsigned long flags; 57618c2ecf20Sopenharmony_ci unsigned long cqr_flags = 0; 57628c2ecf20Sopenharmony_ci 57638c2ecf20Sopenharmony_ci /* Read Configuration Data */ 57648c2ecf20Sopenharmony_ci rc = dasd_eckd_read_conf(device); 57658c2ecf20Sopenharmony_ci if (rc) { 57668c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 57678c2ecf20Sopenharmony_ci "Read configuration data failed, rc=%d", rc); 57688c2ecf20Sopenharmony_ci goto out_err; 57698c2ecf20Sopenharmony_ci } 57708c2ecf20Sopenharmony_ci 57718c2ecf20Sopenharmony_ci dasd_eckd_get_uid(device, &temp_uid); 57728c2ecf20Sopenharmony_ci /* Generate device unique id */ 57738c2ecf20Sopenharmony_ci rc = dasd_eckd_generate_uid(device); 57748c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 57758c2ecf20Sopenharmony_ci if (memcmp(&private->uid, &temp_uid, sizeof(struct dasd_uid)) != 0) 57768c2ecf20Sopenharmony_ci dev_err(&device->cdev->dev, "The UID of the DASD has " 57778c2ecf20Sopenharmony_ci "changed\n"); 57788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 57798c2ecf20Sopenharmony_ci if (rc) 57808c2ecf20Sopenharmony_ci goto out_err; 57818c2ecf20Sopenharmony_ci 57828c2ecf20Sopenharmony_ci /* register lcu with alias handling, enable PAV if this is a new lcu */ 57838c2ecf20Sopenharmony_ci rc = dasd_alias_make_device_known_to_lcu(device); 57848c2ecf20Sopenharmony_ci if (rc) 57858c2ecf20Sopenharmony_ci goto out_err; 57868c2ecf20Sopenharmony_ci 57878c2ecf20Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr_flags); 57888c2ecf20Sopenharmony_ci dasd_eckd_validate_server(device, cqr_flags); 57898c2ecf20Sopenharmony_ci 57908c2ecf20Sopenharmony_ci /* RE-Read Configuration Data */ 57918c2ecf20Sopenharmony_ci rc = dasd_eckd_read_conf(device); 57928c2ecf20Sopenharmony_ci if (rc) { 57938c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 57948c2ecf20Sopenharmony_ci "Read configuration data failed, rc=%d", rc); 57958c2ecf20Sopenharmony_ci goto out_err2; 57968c2ecf20Sopenharmony_ci } 57978c2ecf20Sopenharmony_ci 57988c2ecf20Sopenharmony_ci /* Read Feature Codes */ 57998c2ecf20Sopenharmony_ci dasd_eckd_read_features(device); 58008c2ecf20Sopenharmony_ci 58018c2ecf20Sopenharmony_ci /* Read Volume Information */ 58028c2ecf20Sopenharmony_ci dasd_eckd_read_vol_info(device); 58038c2ecf20Sopenharmony_ci 58048c2ecf20Sopenharmony_ci /* Read Extent Pool Information */ 58058c2ecf20Sopenharmony_ci dasd_eckd_read_ext_pool_info(device); 58068c2ecf20Sopenharmony_ci 58078c2ecf20Sopenharmony_ci /* Read Device Characteristics */ 58088c2ecf20Sopenharmony_ci rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC, 58098c2ecf20Sopenharmony_ci &temp_rdc_data, 64); 58108c2ecf20Sopenharmony_ci if (rc) { 58118c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 58128c2ecf20Sopenharmony_ci "Read device characteristic failed, rc=%d", rc); 58138c2ecf20Sopenharmony_ci goto out_err2; 58148c2ecf20Sopenharmony_ci } 58158c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 58168c2ecf20Sopenharmony_ci memcpy(&private->rdc_data, &temp_rdc_data, sizeof(temp_rdc_data)); 58178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 58188c2ecf20Sopenharmony_ci 58198c2ecf20Sopenharmony_ci /* add device to alias management */ 58208c2ecf20Sopenharmony_ci dasd_alias_add_device(device); 58218c2ecf20Sopenharmony_ci 58228c2ecf20Sopenharmony_ci return 0; 58238c2ecf20Sopenharmony_ci 58248c2ecf20Sopenharmony_ciout_err2: 58258c2ecf20Sopenharmony_ci dasd_alias_disconnect_device_from_lcu(device); 58268c2ecf20Sopenharmony_ciout_err: 58278c2ecf20Sopenharmony_ci return -1; 58288c2ecf20Sopenharmony_ci} 58298c2ecf20Sopenharmony_ci 58308c2ecf20Sopenharmony_cistatic int dasd_eckd_reload_device(struct dasd_device *device) 58318c2ecf20Sopenharmony_ci{ 58328c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 58338c2ecf20Sopenharmony_ci int rc, old_base; 58348c2ecf20Sopenharmony_ci char print_uid[60]; 58358c2ecf20Sopenharmony_ci struct dasd_uid uid; 58368c2ecf20Sopenharmony_ci unsigned long flags; 58378c2ecf20Sopenharmony_ci 58388c2ecf20Sopenharmony_ci /* 58398c2ecf20Sopenharmony_ci * remove device from alias handling to prevent new requests 58408c2ecf20Sopenharmony_ci * from being scheduled on the wrong alias device 58418c2ecf20Sopenharmony_ci */ 58428c2ecf20Sopenharmony_ci dasd_alias_remove_device(device); 58438c2ecf20Sopenharmony_ci 58448c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 58458c2ecf20Sopenharmony_ci old_base = private->uid.base_unit_addr; 58468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 58478c2ecf20Sopenharmony_ci 58488c2ecf20Sopenharmony_ci /* Read Configuration Data */ 58498c2ecf20Sopenharmony_ci rc = dasd_eckd_read_conf(device); 58508c2ecf20Sopenharmony_ci if (rc) 58518c2ecf20Sopenharmony_ci goto out_err; 58528c2ecf20Sopenharmony_ci 58538c2ecf20Sopenharmony_ci rc = dasd_eckd_generate_uid(device); 58548c2ecf20Sopenharmony_ci if (rc) 58558c2ecf20Sopenharmony_ci goto out_err; 58568c2ecf20Sopenharmony_ci /* 58578c2ecf20Sopenharmony_ci * update unit address configuration and 58588c2ecf20Sopenharmony_ci * add device to alias management 58598c2ecf20Sopenharmony_ci */ 58608c2ecf20Sopenharmony_ci dasd_alias_update_add_device(device); 58618c2ecf20Sopenharmony_ci 58628c2ecf20Sopenharmony_ci dasd_eckd_get_uid(device, &uid); 58638c2ecf20Sopenharmony_ci 58648c2ecf20Sopenharmony_ci if (old_base != uid.base_unit_addr) { 58658c2ecf20Sopenharmony_ci if (strlen(uid.vduit) > 0) 58668c2ecf20Sopenharmony_ci snprintf(print_uid, sizeof(print_uid), 58678c2ecf20Sopenharmony_ci "%s.%s.%04x.%02x.%s", uid.vendor, uid.serial, 58688c2ecf20Sopenharmony_ci uid.ssid, uid.base_unit_addr, uid.vduit); 58698c2ecf20Sopenharmony_ci else 58708c2ecf20Sopenharmony_ci snprintf(print_uid, sizeof(print_uid), 58718c2ecf20Sopenharmony_ci "%s.%s.%04x.%02x", uid.vendor, uid.serial, 58728c2ecf20Sopenharmony_ci uid.ssid, uid.base_unit_addr); 58738c2ecf20Sopenharmony_ci 58748c2ecf20Sopenharmony_ci dev_info(&device->cdev->dev, 58758c2ecf20Sopenharmony_ci "An Alias device was reassigned to a new base device " 58768c2ecf20Sopenharmony_ci "with UID: %s\n", print_uid); 58778c2ecf20Sopenharmony_ci } 58788c2ecf20Sopenharmony_ci return 0; 58798c2ecf20Sopenharmony_ci 58808c2ecf20Sopenharmony_ciout_err: 58818c2ecf20Sopenharmony_ci return -1; 58828c2ecf20Sopenharmony_ci} 58838c2ecf20Sopenharmony_ci 58848c2ecf20Sopenharmony_cistatic int dasd_eckd_read_message_buffer(struct dasd_device *device, 58858c2ecf20Sopenharmony_ci struct dasd_rssd_messages *messages, 58868c2ecf20Sopenharmony_ci __u8 lpum) 58878c2ecf20Sopenharmony_ci{ 58888c2ecf20Sopenharmony_ci struct dasd_rssd_messages *message_buf; 58898c2ecf20Sopenharmony_ci struct dasd_psf_prssd_data *prssdp; 58908c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 58918c2ecf20Sopenharmony_ci struct ccw1 *ccw; 58928c2ecf20Sopenharmony_ci int rc; 58938c2ecf20Sopenharmony_ci 58948c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, 58958c2ecf20Sopenharmony_ci (sizeof(struct dasd_psf_prssd_data) + 58968c2ecf20Sopenharmony_ci sizeof(struct dasd_rssd_messages)), 58978c2ecf20Sopenharmony_ci device, NULL); 58988c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 58998c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 59008c2ecf20Sopenharmony_ci "Could not allocate read message buffer request"); 59018c2ecf20Sopenharmony_ci return PTR_ERR(cqr); 59028c2ecf20Sopenharmony_ci } 59038c2ecf20Sopenharmony_ci 59048c2ecf20Sopenharmony_ci cqr->lpm = lpum; 59058c2ecf20Sopenharmony_ciretry: 59068c2ecf20Sopenharmony_ci cqr->startdev = device; 59078c2ecf20Sopenharmony_ci cqr->memdev = device; 59088c2ecf20Sopenharmony_ci cqr->block = NULL; 59098c2ecf20Sopenharmony_ci cqr->expires = 10 * HZ; 59108c2ecf20Sopenharmony_ci set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags); 59118c2ecf20Sopenharmony_ci /* dasd_sleep_on_immediatly does not do complex error 59128c2ecf20Sopenharmony_ci * recovery so clear erp flag and set retry counter to 59138c2ecf20Sopenharmony_ci * do basic erp */ 59148c2ecf20Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); 59158c2ecf20Sopenharmony_ci cqr->retries = 256; 59168c2ecf20Sopenharmony_ci 59178c2ecf20Sopenharmony_ci /* Prepare for Read Subsystem Data */ 59188c2ecf20Sopenharmony_ci prssdp = (struct dasd_psf_prssd_data *) cqr->data; 59198c2ecf20Sopenharmony_ci memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); 59208c2ecf20Sopenharmony_ci prssdp->order = PSF_ORDER_PRSSD; 59218c2ecf20Sopenharmony_ci prssdp->suborder = 0x03; /* Message Buffer */ 59228c2ecf20Sopenharmony_ci /* all other bytes of prssdp must be zero */ 59238c2ecf20Sopenharmony_ci 59248c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 59258c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 59268c2ecf20Sopenharmony_ci ccw->count = sizeof(struct dasd_psf_prssd_data); 59278c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_CC; 59288c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 59298c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) prssdp; 59308c2ecf20Sopenharmony_ci 59318c2ecf20Sopenharmony_ci /* Read Subsystem Data - message buffer */ 59328c2ecf20Sopenharmony_ci message_buf = (struct dasd_rssd_messages *) (prssdp + 1); 59338c2ecf20Sopenharmony_ci memset(message_buf, 0, sizeof(struct dasd_rssd_messages)); 59348c2ecf20Sopenharmony_ci 59358c2ecf20Sopenharmony_ci ccw++; 59368c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RSSD; 59378c2ecf20Sopenharmony_ci ccw->count = sizeof(struct dasd_rssd_messages); 59388c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 59398c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) message_buf; 59408c2ecf20Sopenharmony_ci 59418c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 59428c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 59438c2ecf20Sopenharmony_ci rc = dasd_sleep_on_immediatly(cqr); 59448c2ecf20Sopenharmony_ci if (rc == 0) { 59458c2ecf20Sopenharmony_ci prssdp = (struct dasd_psf_prssd_data *) cqr->data; 59468c2ecf20Sopenharmony_ci message_buf = (struct dasd_rssd_messages *) 59478c2ecf20Sopenharmony_ci (prssdp + 1); 59488c2ecf20Sopenharmony_ci memcpy(messages, message_buf, 59498c2ecf20Sopenharmony_ci sizeof(struct dasd_rssd_messages)); 59508c2ecf20Sopenharmony_ci } else if (cqr->lpm) { 59518c2ecf20Sopenharmony_ci /* 59528c2ecf20Sopenharmony_ci * on z/VM we might not be able to do I/O on the requested path 59538c2ecf20Sopenharmony_ci * but instead we get the required information on any path 59548c2ecf20Sopenharmony_ci * so retry with open path mask 59558c2ecf20Sopenharmony_ci */ 59568c2ecf20Sopenharmony_ci cqr->lpm = 0; 59578c2ecf20Sopenharmony_ci goto retry; 59588c2ecf20Sopenharmony_ci } else 59598c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 59608c2ecf20Sopenharmony_ci "Reading messages failed with rc=%d\n" 59618c2ecf20Sopenharmony_ci , rc); 59628c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 59638c2ecf20Sopenharmony_ci return rc; 59648c2ecf20Sopenharmony_ci} 59658c2ecf20Sopenharmony_ci 59668c2ecf20Sopenharmony_cistatic int dasd_eckd_query_host_access(struct dasd_device *device, 59678c2ecf20Sopenharmony_ci struct dasd_psf_query_host_access *data) 59688c2ecf20Sopenharmony_ci{ 59698c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 59708c2ecf20Sopenharmony_ci struct dasd_psf_query_host_access *host_access; 59718c2ecf20Sopenharmony_ci struct dasd_psf_prssd_data *prssdp; 59728c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 59738c2ecf20Sopenharmony_ci struct ccw1 *ccw; 59748c2ecf20Sopenharmony_ci int rc; 59758c2ecf20Sopenharmony_ci 59768c2ecf20Sopenharmony_ci /* not available for HYPER PAV alias devices */ 59778c2ecf20Sopenharmony_ci if (!device->block && private->lcu->pav == HYPER_PAV) 59788c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 59798c2ecf20Sopenharmony_ci 59808c2ecf20Sopenharmony_ci /* may not be supported by the storage server */ 59818c2ecf20Sopenharmony_ci if (!(private->features.feature[14] & 0x80)) 59828c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 59838c2ecf20Sopenharmony_ci 59848c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, 59858c2ecf20Sopenharmony_ci sizeof(struct dasd_psf_prssd_data) + 1, 59868c2ecf20Sopenharmony_ci device, NULL); 59878c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 59888c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 59898c2ecf20Sopenharmony_ci "Could not allocate read message buffer request"); 59908c2ecf20Sopenharmony_ci return PTR_ERR(cqr); 59918c2ecf20Sopenharmony_ci } 59928c2ecf20Sopenharmony_ci host_access = kzalloc(sizeof(*host_access), GFP_KERNEL | GFP_DMA); 59938c2ecf20Sopenharmony_ci if (!host_access) { 59948c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, device); 59958c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 59968c2ecf20Sopenharmony_ci "Could not allocate host_access buffer"); 59978c2ecf20Sopenharmony_ci return -ENOMEM; 59988c2ecf20Sopenharmony_ci } 59998c2ecf20Sopenharmony_ci cqr->startdev = device; 60008c2ecf20Sopenharmony_ci cqr->memdev = device; 60018c2ecf20Sopenharmony_ci cqr->block = NULL; 60028c2ecf20Sopenharmony_ci cqr->retries = 256; 60038c2ecf20Sopenharmony_ci cqr->expires = 10 * HZ; 60048c2ecf20Sopenharmony_ci 60058c2ecf20Sopenharmony_ci /* Prepare for Read Subsystem Data */ 60068c2ecf20Sopenharmony_ci prssdp = (struct dasd_psf_prssd_data *) cqr->data; 60078c2ecf20Sopenharmony_ci memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); 60088c2ecf20Sopenharmony_ci prssdp->order = PSF_ORDER_PRSSD; 60098c2ecf20Sopenharmony_ci prssdp->suborder = PSF_SUBORDER_QHA; /* query host access */ 60108c2ecf20Sopenharmony_ci /* LSS and Volume that will be queried */ 60118c2ecf20Sopenharmony_ci prssdp->lss = private->ned->ID; 60128c2ecf20Sopenharmony_ci prssdp->volume = private->ned->unit_addr; 60138c2ecf20Sopenharmony_ci /* all other bytes of prssdp must be zero */ 60148c2ecf20Sopenharmony_ci 60158c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 60168c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 60178c2ecf20Sopenharmony_ci ccw->count = sizeof(struct dasd_psf_prssd_data); 60188c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_CC; 60198c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 60208c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) prssdp; 60218c2ecf20Sopenharmony_ci 60228c2ecf20Sopenharmony_ci /* Read Subsystem Data - query host access */ 60238c2ecf20Sopenharmony_ci ccw++; 60248c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RSSD; 60258c2ecf20Sopenharmony_ci ccw->count = sizeof(struct dasd_psf_query_host_access); 60268c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 60278c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) host_access; 60288c2ecf20Sopenharmony_ci 60298c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 60308c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 60318c2ecf20Sopenharmony_ci /* the command might not be supported, suppress error message */ 60328c2ecf20Sopenharmony_ci __set_bit(DASD_CQR_SUPPRESS_CR, &cqr->flags); 60338c2ecf20Sopenharmony_ci rc = dasd_sleep_on_interruptible(cqr); 60348c2ecf20Sopenharmony_ci if (rc == 0) { 60358c2ecf20Sopenharmony_ci *data = *host_access; 60368c2ecf20Sopenharmony_ci } else { 60378c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 60388c2ecf20Sopenharmony_ci "Reading host access data failed with rc=%d\n", 60398c2ecf20Sopenharmony_ci rc); 60408c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 60418c2ecf20Sopenharmony_ci } 60428c2ecf20Sopenharmony_ci 60438c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 60448c2ecf20Sopenharmony_ci kfree(host_access); 60458c2ecf20Sopenharmony_ci return rc; 60468c2ecf20Sopenharmony_ci} 60478c2ecf20Sopenharmony_ci/* 60488c2ecf20Sopenharmony_ci * return number of grouped devices 60498c2ecf20Sopenharmony_ci */ 60508c2ecf20Sopenharmony_cistatic int dasd_eckd_host_access_count(struct dasd_device *device) 60518c2ecf20Sopenharmony_ci{ 60528c2ecf20Sopenharmony_ci struct dasd_psf_query_host_access *access; 60538c2ecf20Sopenharmony_ci struct dasd_ckd_path_group_entry *entry; 60548c2ecf20Sopenharmony_ci struct dasd_ckd_host_information *info; 60558c2ecf20Sopenharmony_ci int count = 0; 60568c2ecf20Sopenharmony_ci int rc, i; 60578c2ecf20Sopenharmony_ci 60588c2ecf20Sopenharmony_ci access = kzalloc(sizeof(*access), GFP_NOIO); 60598c2ecf20Sopenharmony_ci if (!access) { 60608c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 60618c2ecf20Sopenharmony_ci "Could not allocate access buffer"); 60628c2ecf20Sopenharmony_ci return -ENOMEM; 60638c2ecf20Sopenharmony_ci } 60648c2ecf20Sopenharmony_ci rc = dasd_eckd_query_host_access(device, access); 60658c2ecf20Sopenharmony_ci if (rc) { 60668c2ecf20Sopenharmony_ci kfree(access); 60678c2ecf20Sopenharmony_ci return rc; 60688c2ecf20Sopenharmony_ci } 60698c2ecf20Sopenharmony_ci 60708c2ecf20Sopenharmony_ci info = (struct dasd_ckd_host_information *) 60718c2ecf20Sopenharmony_ci access->host_access_information; 60728c2ecf20Sopenharmony_ci for (i = 0; i < info->entry_count; i++) { 60738c2ecf20Sopenharmony_ci entry = (struct dasd_ckd_path_group_entry *) 60748c2ecf20Sopenharmony_ci (info->entry + i * info->entry_size); 60758c2ecf20Sopenharmony_ci if (entry->status_flags & DASD_ECKD_PG_GROUPED) 60768c2ecf20Sopenharmony_ci count++; 60778c2ecf20Sopenharmony_ci } 60788c2ecf20Sopenharmony_ci 60798c2ecf20Sopenharmony_ci kfree(access); 60808c2ecf20Sopenharmony_ci return count; 60818c2ecf20Sopenharmony_ci} 60828c2ecf20Sopenharmony_ci 60838c2ecf20Sopenharmony_ci/* 60848c2ecf20Sopenharmony_ci * write host access information to a sequential file 60858c2ecf20Sopenharmony_ci */ 60868c2ecf20Sopenharmony_cistatic int dasd_hosts_print(struct dasd_device *device, struct seq_file *m) 60878c2ecf20Sopenharmony_ci{ 60888c2ecf20Sopenharmony_ci struct dasd_psf_query_host_access *access; 60898c2ecf20Sopenharmony_ci struct dasd_ckd_path_group_entry *entry; 60908c2ecf20Sopenharmony_ci struct dasd_ckd_host_information *info; 60918c2ecf20Sopenharmony_ci char sysplex[9] = ""; 60928c2ecf20Sopenharmony_ci int rc, i; 60938c2ecf20Sopenharmony_ci 60948c2ecf20Sopenharmony_ci access = kzalloc(sizeof(*access), GFP_NOIO); 60958c2ecf20Sopenharmony_ci if (!access) { 60968c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 60978c2ecf20Sopenharmony_ci "Could not allocate access buffer"); 60988c2ecf20Sopenharmony_ci return -ENOMEM; 60998c2ecf20Sopenharmony_ci } 61008c2ecf20Sopenharmony_ci rc = dasd_eckd_query_host_access(device, access); 61018c2ecf20Sopenharmony_ci if (rc) { 61028c2ecf20Sopenharmony_ci kfree(access); 61038c2ecf20Sopenharmony_ci return rc; 61048c2ecf20Sopenharmony_ci } 61058c2ecf20Sopenharmony_ci 61068c2ecf20Sopenharmony_ci info = (struct dasd_ckd_host_information *) 61078c2ecf20Sopenharmony_ci access->host_access_information; 61088c2ecf20Sopenharmony_ci for (i = 0; i < info->entry_count; i++) { 61098c2ecf20Sopenharmony_ci entry = (struct dasd_ckd_path_group_entry *) 61108c2ecf20Sopenharmony_ci (info->entry + i * info->entry_size); 61118c2ecf20Sopenharmony_ci /* PGID */ 61128c2ecf20Sopenharmony_ci seq_printf(m, "pgid %*phN\n", 11, entry->pgid); 61138c2ecf20Sopenharmony_ci /* FLAGS */ 61148c2ecf20Sopenharmony_ci seq_printf(m, "status_flags %02x\n", entry->status_flags); 61158c2ecf20Sopenharmony_ci /* SYSPLEX NAME */ 61168c2ecf20Sopenharmony_ci memcpy(&sysplex, &entry->sysplex_name, sizeof(sysplex) - 1); 61178c2ecf20Sopenharmony_ci EBCASC(sysplex, sizeof(sysplex)); 61188c2ecf20Sopenharmony_ci seq_printf(m, "sysplex_name %8s\n", sysplex); 61198c2ecf20Sopenharmony_ci /* SUPPORTED CYLINDER */ 61208c2ecf20Sopenharmony_ci seq_printf(m, "supported_cylinder %d\n", entry->cylinder); 61218c2ecf20Sopenharmony_ci /* TIMESTAMP */ 61228c2ecf20Sopenharmony_ci seq_printf(m, "timestamp %lu\n", (unsigned long) 61238c2ecf20Sopenharmony_ci entry->timestamp); 61248c2ecf20Sopenharmony_ci } 61258c2ecf20Sopenharmony_ci kfree(access); 61268c2ecf20Sopenharmony_ci 61278c2ecf20Sopenharmony_ci return 0; 61288c2ecf20Sopenharmony_ci} 61298c2ecf20Sopenharmony_ci 61308c2ecf20Sopenharmony_ci/* 61318c2ecf20Sopenharmony_ci * Perform Subsystem Function - CUIR response 61328c2ecf20Sopenharmony_ci */ 61338c2ecf20Sopenharmony_cistatic int 61348c2ecf20Sopenharmony_cidasd_eckd_psf_cuir_response(struct dasd_device *device, int response, 61358c2ecf20Sopenharmony_ci __u32 message_id, __u8 lpum) 61368c2ecf20Sopenharmony_ci{ 61378c2ecf20Sopenharmony_ci struct dasd_psf_cuir_response *psf_cuir; 61388c2ecf20Sopenharmony_ci int pos = pathmask_to_pos(lpum); 61398c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 61408c2ecf20Sopenharmony_ci struct ccw1 *ccw; 61418c2ecf20Sopenharmony_ci int rc; 61428c2ecf20Sopenharmony_ci 61438c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ , 61448c2ecf20Sopenharmony_ci sizeof(struct dasd_psf_cuir_response), 61458c2ecf20Sopenharmony_ci device, NULL); 61468c2ecf20Sopenharmony_ci 61478c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 61488c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 61498c2ecf20Sopenharmony_ci "Could not allocate PSF-CUIR request"); 61508c2ecf20Sopenharmony_ci return PTR_ERR(cqr); 61518c2ecf20Sopenharmony_ci } 61528c2ecf20Sopenharmony_ci 61538c2ecf20Sopenharmony_ci psf_cuir = (struct dasd_psf_cuir_response *)cqr->data; 61548c2ecf20Sopenharmony_ci psf_cuir->order = PSF_ORDER_CUIR_RESPONSE; 61558c2ecf20Sopenharmony_ci psf_cuir->cc = response; 61568c2ecf20Sopenharmony_ci psf_cuir->chpid = device->path[pos].chpid; 61578c2ecf20Sopenharmony_ci psf_cuir->message_id = message_id; 61588c2ecf20Sopenharmony_ci psf_cuir->cssid = device->path[pos].cssid; 61598c2ecf20Sopenharmony_ci psf_cuir->ssid = device->path[pos].ssid; 61608c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 61618c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 61628c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t)psf_cuir; 61638c2ecf20Sopenharmony_ci ccw->flags = CCW_FLAG_SLI; 61648c2ecf20Sopenharmony_ci ccw->count = sizeof(struct dasd_psf_cuir_response); 61658c2ecf20Sopenharmony_ci 61668c2ecf20Sopenharmony_ci cqr->startdev = device; 61678c2ecf20Sopenharmony_ci cqr->memdev = device; 61688c2ecf20Sopenharmony_ci cqr->block = NULL; 61698c2ecf20Sopenharmony_ci cqr->retries = 256; 61708c2ecf20Sopenharmony_ci cqr->expires = 10*HZ; 61718c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 61728c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 61738c2ecf20Sopenharmony_ci set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags); 61748c2ecf20Sopenharmony_ci 61758c2ecf20Sopenharmony_ci rc = dasd_sleep_on(cqr); 61768c2ecf20Sopenharmony_ci 61778c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 61788c2ecf20Sopenharmony_ci return rc; 61798c2ecf20Sopenharmony_ci} 61808c2ecf20Sopenharmony_ci 61818c2ecf20Sopenharmony_ci/* 61828c2ecf20Sopenharmony_ci * return configuration data that is referenced by record selector 61838c2ecf20Sopenharmony_ci * if a record selector is specified or per default return the 61848c2ecf20Sopenharmony_ci * conf_data pointer for the path specified by lpum 61858c2ecf20Sopenharmony_ci */ 61868c2ecf20Sopenharmony_cistatic struct dasd_conf_data *dasd_eckd_get_ref_conf(struct dasd_device *device, 61878c2ecf20Sopenharmony_ci __u8 lpum, 61888c2ecf20Sopenharmony_ci struct dasd_cuir_message *cuir) 61898c2ecf20Sopenharmony_ci{ 61908c2ecf20Sopenharmony_ci struct dasd_conf_data *conf_data; 61918c2ecf20Sopenharmony_ci int path, pos; 61928c2ecf20Sopenharmony_ci 61938c2ecf20Sopenharmony_ci if (cuir->record_selector == 0) 61948c2ecf20Sopenharmony_ci goto out; 61958c2ecf20Sopenharmony_ci for (path = 0x80, pos = 0; path; path >>= 1, pos++) { 61968c2ecf20Sopenharmony_ci conf_data = device->path[pos].conf_data; 61978c2ecf20Sopenharmony_ci if (conf_data->gneq.record_selector == 61988c2ecf20Sopenharmony_ci cuir->record_selector) 61998c2ecf20Sopenharmony_ci return conf_data; 62008c2ecf20Sopenharmony_ci } 62018c2ecf20Sopenharmony_ciout: 62028c2ecf20Sopenharmony_ci return device->path[pathmask_to_pos(lpum)].conf_data; 62038c2ecf20Sopenharmony_ci} 62048c2ecf20Sopenharmony_ci 62058c2ecf20Sopenharmony_ci/* 62068c2ecf20Sopenharmony_ci * This function determines the scope of a reconfiguration request by 62078c2ecf20Sopenharmony_ci * analysing the path and device selection data provided in the CUIR request. 62088c2ecf20Sopenharmony_ci * Returns a path mask containing CUIR affected paths for the give device. 62098c2ecf20Sopenharmony_ci * 62108c2ecf20Sopenharmony_ci * If the CUIR request does not contain the required information return the 62118c2ecf20Sopenharmony_ci * path mask of the path the attention message for the CUIR request was reveived 62128c2ecf20Sopenharmony_ci * on. 62138c2ecf20Sopenharmony_ci */ 62148c2ecf20Sopenharmony_cistatic int dasd_eckd_cuir_scope(struct dasd_device *device, __u8 lpum, 62158c2ecf20Sopenharmony_ci struct dasd_cuir_message *cuir) 62168c2ecf20Sopenharmony_ci{ 62178c2ecf20Sopenharmony_ci struct dasd_conf_data *ref_conf_data; 62188c2ecf20Sopenharmony_ci unsigned long bitmask = 0, mask = 0; 62198c2ecf20Sopenharmony_ci struct dasd_conf_data *conf_data; 62208c2ecf20Sopenharmony_ci unsigned int pos, path; 62218c2ecf20Sopenharmony_ci char *ref_gneq, *gneq; 62228c2ecf20Sopenharmony_ci char *ref_ned, *ned; 62238c2ecf20Sopenharmony_ci int tbcpm = 0; 62248c2ecf20Sopenharmony_ci 62258c2ecf20Sopenharmony_ci /* if CUIR request does not specify the scope use the path 62268c2ecf20Sopenharmony_ci the attention message was presented on */ 62278c2ecf20Sopenharmony_ci if (!cuir->ned_map || 62288c2ecf20Sopenharmony_ci !(cuir->neq_map[0] | cuir->neq_map[1] | cuir->neq_map[2])) 62298c2ecf20Sopenharmony_ci return lpum; 62308c2ecf20Sopenharmony_ci 62318c2ecf20Sopenharmony_ci /* get reference conf data */ 62328c2ecf20Sopenharmony_ci ref_conf_data = dasd_eckd_get_ref_conf(device, lpum, cuir); 62338c2ecf20Sopenharmony_ci /* reference ned is determined by ned_map field */ 62348c2ecf20Sopenharmony_ci pos = 8 - ffs(cuir->ned_map); 62358c2ecf20Sopenharmony_ci ref_ned = (char *)&ref_conf_data->neds[pos]; 62368c2ecf20Sopenharmony_ci ref_gneq = (char *)&ref_conf_data->gneq; 62378c2ecf20Sopenharmony_ci /* transfer 24 bit neq_map to mask */ 62388c2ecf20Sopenharmony_ci mask = cuir->neq_map[2]; 62398c2ecf20Sopenharmony_ci mask |= cuir->neq_map[1] << 8; 62408c2ecf20Sopenharmony_ci mask |= cuir->neq_map[0] << 16; 62418c2ecf20Sopenharmony_ci 62428c2ecf20Sopenharmony_ci for (path = 0; path < 8; path++) { 62438c2ecf20Sopenharmony_ci /* initialise data per path */ 62448c2ecf20Sopenharmony_ci bitmask = mask; 62458c2ecf20Sopenharmony_ci conf_data = device->path[path].conf_data; 62468c2ecf20Sopenharmony_ci pos = 8 - ffs(cuir->ned_map); 62478c2ecf20Sopenharmony_ci ned = (char *) &conf_data->neds[pos]; 62488c2ecf20Sopenharmony_ci /* compare reference ned and per path ned */ 62498c2ecf20Sopenharmony_ci if (memcmp(ref_ned, ned, sizeof(*ned)) != 0) 62508c2ecf20Sopenharmony_ci continue; 62518c2ecf20Sopenharmony_ci gneq = (char *)&conf_data->gneq; 62528c2ecf20Sopenharmony_ci /* compare reference gneq and per_path gneq under 62538c2ecf20Sopenharmony_ci 24 bit mask where mask bit 0 equals byte 7 of 62548c2ecf20Sopenharmony_ci the gneq and mask bit 24 equals byte 31 */ 62558c2ecf20Sopenharmony_ci while (bitmask) { 62568c2ecf20Sopenharmony_ci pos = ffs(bitmask) - 1; 62578c2ecf20Sopenharmony_ci if (memcmp(&ref_gneq[31 - pos], &gneq[31 - pos], 1) 62588c2ecf20Sopenharmony_ci != 0) 62598c2ecf20Sopenharmony_ci break; 62608c2ecf20Sopenharmony_ci clear_bit(pos, &bitmask); 62618c2ecf20Sopenharmony_ci } 62628c2ecf20Sopenharmony_ci if (bitmask) 62638c2ecf20Sopenharmony_ci continue; 62648c2ecf20Sopenharmony_ci /* device and path match the reference values 62658c2ecf20Sopenharmony_ci add path to CUIR scope */ 62668c2ecf20Sopenharmony_ci tbcpm |= 0x80 >> path; 62678c2ecf20Sopenharmony_ci } 62688c2ecf20Sopenharmony_ci return tbcpm; 62698c2ecf20Sopenharmony_ci} 62708c2ecf20Sopenharmony_ci 62718c2ecf20Sopenharmony_cistatic void dasd_eckd_cuir_notify_user(struct dasd_device *device, 62728c2ecf20Sopenharmony_ci unsigned long paths, int action) 62738c2ecf20Sopenharmony_ci{ 62748c2ecf20Sopenharmony_ci int pos; 62758c2ecf20Sopenharmony_ci 62768c2ecf20Sopenharmony_ci while (paths) { 62778c2ecf20Sopenharmony_ci /* get position of bit in mask */ 62788c2ecf20Sopenharmony_ci pos = 8 - ffs(paths); 62798c2ecf20Sopenharmony_ci /* get channel path descriptor from this position */ 62808c2ecf20Sopenharmony_ci if (action == CUIR_QUIESCE) 62818c2ecf20Sopenharmony_ci pr_warn("Service on the storage server caused path %x.%02x to go offline", 62828c2ecf20Sopenharmony_ci device->path[pos].cssid, 62838c2ecf20Sopenharmony_ci device->path[pos].chpid); 62848c2ecf20Sopenharmony_ci else if (action == CUIR_RESUME) 62858c2ecf20Sopenharmony_ci pr_info("Path %x.%02x is back online after service on the storage server", 62868c2ecf20Sopenharmony_ci device->path[pos].cssid, 62878c2ecf20Sopenharmony_ci device->path[pos].chpid); 62888c2ecf20Sopenharmony_ci clear_bit(7 - pos, &paths); 62898c2ecf20Sopenharmony_ci } 62908c2ecf20Sopenharmony_ci} 62918c2ecf20Sopenharmony_ci 62928c2ecf20Sopenharmony_cistatic int dasd_eckd_cuir_remove_path(struct dasd_device *device, __u8 lpum, 62938c2ecf20Sopenharmony_ci struct dasd_cuir_message *cuir) 62948c2ecf20Sopenharmony_ci{ 62958c2ecf20Sopenharmony_ci unsigned long tbcpm; 62968c2ecf20Sopenharmony_ci 62978c2ecf20Sopenharmony_ci tbcpm = dasd_eckd_cuir_scope(device, lpum, cuir); 62988c2ecf20Sopenharmony_ci /* nothing to do if path is not in use */ 62998c2ecf20Sopenharmony_ci if (!(dasd_path_get_opm(device) & tbcpm)) 63008c2ecf20Sopenharmony_ci return 0; 63018c2ecf20Sopenharmony_ci if (!(dasd_path_get_opm(device) & ~tbcpm)) { 63028c2ecf20Sopenharmony_ci /* no path would be left if the CUIR action is taken 63038c2ecf20Sopenharmony_ci return error */ 63048c2ecf20Sopenharmony_ci return -EINVAL; 63058c2ecf20Sopenharmony_ci } 63068c2ecf20Sopenharmony_ci /* remove device from operational path mask */ 63078c2ecf20Sopenharmony_ci dasd_path_remove_opm(device, tbcpm); 63088c2ecf20Sopenharmony_ci dasd_path_add_cuirpm(device, tbcpm); 63098c2ecf20Sopenharmony_ci return tbcpm; 63108c2ecf20Sopenharmony_ci} 63118c2ecf20Sopenharmony_ci 63128c2ecf20Sopenharmony_ci/* 63138c2ecf20Sopenharmony_ci * walk through all devices and build a path mask to quiesce them 63148c2ecf20Sopenharmony_ci * return an error if the last path to a device would be removed 63158c2ecf20Sopenharmony_ci * 63168c2ecf20Sopenharmony_ci * if only part of the devices are quiesced and an error 63178c2ecf20Sopenharmony_ci * occurs no onlining necessary, the storage server will 63188c2ecf20Sopenharmony_ci * notify the already set offline devices again 63198c2ecf20Sopenharmony_ci */ 63208c2ecf20Sopenharmony_cistatic int dasd_eckd_cuir_quiesce(struct dasd_device *device, __u8 lpum, 63218c2ecf20Sopenharmony_ci struct dasd_cuir_message *cuir) 63228c2ecf20Sopenharmony_ci{ 63238c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 63248c2ecf20Sopenharmony_ci struct alias_pav_group *pavgroup, *tempgroup; 63258c2ecf20Sopenharmony_ci struct dasd_device *dev, *n; 63268c2ecf20Sopenharmony_ci unsigned long paths = 0; 63278c2ecf20Sopenharmony_ci unsigned long flags; 63288c2ecf20Sopenharmony_ci int tbcpm; 63298c2ecf20Sopenharmony_ci 63308c2ecf20Sopenharmony_ci /* active devices */ 63318c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, n, &private->lcu->active_devices, 63328c2ecf20Sopenharmony_ci alias_list) { 63338c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags); 63348c2ecf20Sopenharmony_ci tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir); 63358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(dev->cdev), flags); 63368c2ecf20Sopenharmony_ci if (tbcpm < 0) 63378c2ecf20Sopenharmony_ci goto out_err; 63388c2ecf20Sopenharmony_ci paths |= tbcpm; 63398c2ecf20Sopenharmony_ci } 63408c2ecf20Sopenharmony_ci /* inactive devices */ 63418c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, n, &private->lcu->inactive_devices, 63428c2ecf20Sopenharmony_ci alias_list) { 63438c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags); 63448c2ecf20Sopenharmony_ci tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir); 63458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(dev->cdev), flags); 63468c2ecf20Sopenharmony_ci if (tbcpm < 0) 63478c2ecf20Sopenharmony_ci goto out_err; 63488c2ecf20Sopenharmony_ci paths |= tbcpm; 63498c2ecf20Sopenharmony_ci } 63508c2ecf20Sopenharmony_ci /* devices in PAV groups */ 63518c2ecf20Sopenharmony_ci list_for_each_entry_safe(pavgroup, tempgroup, 63528c2ecf20Sopenharmony_ci &private->lcu->grouplist, group) { 63538c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, n, &pavgroup->baselist, 63548c2ecf20Sopenharmony_ci alias_list) { 63558c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags); 63568c2ecf20Sopenharmony_ci tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir); 63578c2ecf20Sopenharmony_ci spin_unlock_irqrestore( 63588c2ecf20Sopenharmony_ci get_ccwdev_lock(dev->cdev), flags); 63598c2ecf20Sopenharmony_ci if (tbcpm < 0) 63608c2ecf20Sopenharmony_ci goto out_err; 63618c2ecf20Sopenharmony_ci paths |= tbcpm; 63628c2ecf20Sopenharmony_ci } 63638c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, n, &pavgroup->aliaslist, 63648c2ecf20Sopenharmony_ci alias_list) { 63658c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags); 63668c2ecf20Sopenharmony_ci tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir); 63678c2ecf20Sopenharmony_ci spin_unlock_irqrestore( 63688c2ecf20Sopenharmony_ci get_ccwdev_lock(dev->cdev), flags); 63698c2ecf20Sopenharmony_ci if (tbcpm < 0) 63708c2ecf20Sopenharmony_ci goto out_err; 63718c2ecf20Sopenharmony_ci paths |= tbcpm; 63728c2ecf20Sopenharmony_ci } 63738c2ecf20Sopenharmony_ci } 63748c2ecf20Sopenharmony_ci /* notify user about all paths affected by CUIR action */ 63758c2ecf20Sopenharmony_ci dasd_eckd_cuir_notify_user(device, paths, CUIR_QUIESCE); 63768c2ecf20Sopenharmony_ci return 0; 63778c2ecf20Sopenharmony_ciout_err: 63788c2ecf20Sopenharmony_ci return tbcpm; 63798c2ecf20Sopenharmony_ci} 63808c2ecf20Sopenharmony_ci 63818c2ecf20Sopenharmony_cistatic int dasd_eckd_cuir_resume(struct dasd_device *device, __u8 lpum, 63828c2ecf20Sopenharmony_ci struct dasd_cuir_message *cuir) 63838c2ecf20Sopenharmony_ci{ 63848c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 63858c2ecf20Sopenharmony_ci struct alias_pav_group *pavgroup, *tempgroup; 63868c2ecf20Sopenharmony_ci struct dasd_device *dev, *n; 63878c2ecf20Sopenharmony_ci unsigned long paths = 0; 63888c2ecf20Sopenharmony_ci int tbcpm; 63898c2ecf20Sopenharmony_ci 63908c2ecf20Sopenharmony_ci /* 63918c2ecf20Sopenharmony_ci * the path may have been added through a generic path event before 63928c2ecf20Sopenharmony_ci * only trigger path verification if the path is not already in use 63938c2ecf20Sopenharmony_ci */ 63948c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, n, 63958c2ecf20Sopenharmony_ci &private->lcu->active_devices, 63968c2ecf20Sopenharmony_ci alias_list) { 63978c2ecf20Sopenharmony_ci tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir); 63988c2ecf20Sopenharmony_ci paths |= tbcpm; 63998c2ecf20Sopenharmony_ci if (!(dasd_path_get_opm(dev) & tbcpm)) { 64008c2ecf20Sopenharmony_ci dasd_path_add_tbvpm(dev, tbcpm); 64018c2ecf20Sopenharmony_ci dasd_schedule_device_bh(dev); 64028c2ecf20Sopenharmony_ci } 64038c2ecf20Sopenharmony_ci } 64048c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, n, 64058c2ecf20Sopenharmony_ci &private->lcu->inactive_devices, 64068c2ecf20Sopenharmony_ci alias_list) { 64078c2ecf20Sopenharmony_ci tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir); 64088c2ecf20Sopenharmony_ci paths |= tbcpm; 64098c2ecf20Sopenharmony_ci if (!(dasd_path_get_opm(dev) & tbcpm)) { 64108c2ecf20Sopenharmony_ci dasd_path_add_tbvpm(dev, tbcpm); 64118c2ecf20Sopenharmony_ci dasd_schedule_device_bh(dev); 64128c2ecf20Sopenharmony_ci } 64138c2ecf20Sopenharmony_ci } 64148c2ecf20Sopenharmony_ci /* devices in PAV groups */ 64158c2ecf20Sopenharmony_ci list_for_each_entry_safe(pavgroup, tempgroup, 64168c2ecf20Sopenharmony_ci &private->lcu->grouplist, 64178c2ecf20Sopenharmony_ci group) { 64188c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, n, 64198c2ecf20Sopenharmony_ci &pavgroup->baselist, 64208c2ecf20Sopenharmony_ci alias_list) { 64218c2ecf20Sopenharmony_ci tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir); 64228c2ecf20Sopenharmony_ci paths |= tbcpm; 64238c2ecf20Sopenharmony_ci if (!(dasd_path_get_opm(dev) & tbcpm)) { 64248c2ecf20Sopenharmony_ci dasd_path_add_tbvpm(dev, tbcpm); 64258c2ecf20Sopenharmony_ci dasd_schedule_device_bh(dev); 64268c2ecf20Sopenharmony_ci } 64278c2ecf20Sopenharmony_ci } 64288c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, n, 64298c2ecf20Sopenharmony_ci &pavgroup->aliaslist, 64308c2ecf20Sopenharmony_ci alias_list) { 64318c2ecf20Sopenharmony_ci tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir); 64328c2ecf20Sopenharmony_ci paths |= tbcpm; 64338c2ecf20Sopenharmony_ci if (!(dasd_path_get_opm(dev) & tbcpm)) { 64348c2ecf20Sopenharmony_ci dasd_path_add_tbvpm(dev, tbcpm); 64358c2ecf20Sopenharmony_ci dasd_schedule_device_bh(dev); 64368c2ecf20Sopenharmony_ci } 64378c2ecf20Sopenharmony_ci } 64388c2ecf20Sopenharmony_ci } 64398c2ecf20Sopenharmony_ci /* notify user about all paths affected by CUIR action */ 64408c2ecf20Sopenharmony_ci dasd_eckd_cuir_notify_user(device, paths, CUIR_RESUME); 64418c2ecf20Sopenharmony_ci return 0; 64428c2ecf20Sopenharmony_ci} 64438c2ecf20Sopenharmony_ci 64448c2ecf20Sopenharmony_cistatic void dasd_eckd_handle_cuir(struct dasd_device *device, void *messages, 64458c2ecf20Sopenharmony_ci __u8 lpum) 64468c2ecf20Sopenharmony_ci{ 64478c2ecf20Sopenharmony_ci struct dasd_cuir_message *cuir = messages; 64488c2ecf20Sopenharmony_ci int response; 64498c2ecf20Sopenharmony_ci 64508c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, 64518c2ecf20Sopenharmony_ci "CUIR request: %016llx %016llx %016llx %08x", 64528c2ecf20Sopenharmony_ci ((u64 *)cuir)[0], ((u64 *)cuir)[1], ((u64 *)cuir)[2], 64538c2ecf20Sopenharmony_ci ((u32 *)cuir)[3]); 64548c2ecf20Sopenharmony_ci 64558c2ecf20Sopenharmony_ci if (cuir->code == CUIR_QUIESCE) { 64568c2ecf20Sopenharmony_ci /* quiesce */ 64578c2ecf20Sopenharmony_ci if (dasd_eckd_cuir_quiesce(device, lpum, cuir)) 64588c2ecf20Sopenharmony_ci response = PSF_CUIR_LAST_PATH; 64598c2ecf20Sopenharmony_ci else 64608c2ecf20Sopenharmony_ci response = PSF_CUIR_COMPLETED; 64618c2ecf20Sopenharmony_ci } else if (cuir->code == CUIR_RESUME) { 64628c2ecf20Sopenharmony_ci /* resume */ 64638c2ecf20Sopenharmony_ci dasd_eckd_cuir_resume(device, lpum, cuir); 64648c2ecf20Sopenharmony_ci response = PSF_CUIR_COMPLETED; 64658c2ecf20Sopenharmony_ci } else 64668c2ecf20Sopenharmony_ci response = PSF_CUIR_NOT_SUPPORTED; 64678c2ecf20Sopenharmony_ci 64688c2ecf20Sopenharmony_ci dasd_eckd_psf_cuir_response(device, response, 64698c2ecf20Sopenharmony_ci cuir->message_id, lpum); 64708c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, 64718c2ecf20Sopenharmony_ci "CUIR response: %d on message ID %08x", response, 64728c2ecf20Sopenharmony_ci cuir->message_id); 64738c2ecf20Sopenharmony_ci /* to make sure there is no attention left schedule work again */ 64748c2ecf20Sopenharmony_ci device->discipline->check_attention(device, lpum); 64758c2ecf20Sopenharmony_ci} 64768c2ecf20Sopenharmony_ci 64778c2ecf20Sopenharmony_cistatic void dasd_eckd_oos_resume(struct dasd_device *device) 64788c2ecf20Sopenharmony_ci{ 64798c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 64808c2ecf20Sopenharmony_ci struct alias_pav_group *pavgroup, *tempgroup; 64818c2ecf20Sopenharmony_ci struct dasd_device *dev, *n; 64828c2ecf20Sopenharmony_ci unsigned long flags; 64838c2ecf20Sopenharmony_ci 64848c2ecf20Sopenharmony_ci spin_lock_irqsave(&private->lcu->lock, flags); 64858c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, n, &private->lcu->active_devices, 64868c2ecf20Sopenharmony_ci alias_list) { 64878c2ecf20Sopenharmony_ci if (dev->stopped & DASD_STOPPED_NOSPC) 64888c2ecf20Sopenharmony_ci dasd_generic_space_avail(dev); 64898c2ecf20Sopenharmony_ci } 64908c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, n, &private->lcu->inactive_devices, 64918c2ecf20Sopenharmony_ci alias_list) { 64928c2ecf20Sopenharmony_ci if (dev->stopped & DASD_STOPPED_NOSPC) 64938c2ecf20Sopenharmony_ci dasd_generic_space_avail(dev); 64948c2ecf20Sopenharmony_ci } 64958c2ecf20Sopenharmony_ci /* devices in PAV groups */ 64968c2ecf20Sopenharmony_ci list_for_each_entry_safe(pavgroup, tempgroup, 64978c2ecf20Sopenharmony_ci &private->lcu->grouplist, 64988c2ecf20Sopenharmony_ci group) { 64998c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, n, &pavgroup->baselist, 65008c2ecf20Sopenharmony_ci alias_list) { 65018c2ecf20Sopenharmony_ci if (dev->stopped & DASD_STOPPED_NOSPC) 65028c2ecf20Sopenharmony_ci dasd_generic_space_avail(dev); 65038c2ecf20Sopenharmony_ci } 65048c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, n, &pavgroup->aliaslist, 65058c2ecf20Sopenharmony_ci alias_list) { 65068c2ecf20Sopenharmony_ci if (dev->stopped & DASD_STOPPED_NOSPC) 65078c2ecf20Sopenharmony_ci dasd_generic_space_avail(dev); 65088c2ecf20Sopenharmony_ci } 65098c2ecf20Sopenharmony_ci } 65108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&private->lcu->lock, flags); 65118c2ecf20Sopenharmony_ci} 65128c2ecf20Sopenharmony_ci 65138c2ecf20Sopenharmony_cistatic void dasd_eckd_handle_oos(struct dasd_device *device, void *messages, 65148c2ecf20Sopenharmony_ci __u8 lpum) 65158c2ecf20Sopenharmony_ci{ 65168c2ecf20Sopenharmony_ci struct dasd_oos_message *oos = messages; 65178c2ecf20Sopenharmony_ci 65188c2ecf20Sopenharmony_ci switch (oos->code) { 65198c2ecf20Sopenharmony_ci case REPO_WARN: 65208c2ecf20Sopenharmony_ci case POOL_WARN: 65218c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, 65228c2ecf20Sopenharmony_ci "Extent pool usage has reached a critical value\n"); 65238c2ecf20Sopenharmony_ci dasd_eckd_oos_resume(device); 65248c2ecf20Sopenharmony_ci break; 65258c2ecf20Sopenharmony_ci case REPO_EXHAUST: 65268c2ecf20Sopenharmony_ci case POOL_EXHAUST: 65278c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, 65288c2ecf20Sopenharmony_ci "Extent pool is exhausted\n"); 65298c2ecf20Sopenharmony_ci break; 65308c2ecf20Sopenharmony_ci case REPO_RELIEVE: 65318c2ecf20Sopenharmony_ci case POOL_RELIEVE: 65328c2ecf20Sopenharmony_ci dev_info(&device->cdev->dev, 65338c2ecf20Sopenharmony_ci "Extent pool physical space constraint has been relieved\n"); 65348c2ecf20Sopenharmony_ci break; 65358c2ecf20Sopenharmony_ci } 65368c2ecf20Sopenharmony_ci 65378c2ecf20Sopenharmony_ci /* In any case, update related data */ 65388c2ecf20Sopenharmony_ci dasd_eckd_read_ext_pool_info(device); 65398c2ecf20Sopenharmony_ci 65408c2ecf20Sopenharmony_ci /* to make sure there is no attention left schedule work again */ 65418c2ecf20Sopenharmony_ci device->discipline->check_attention(device, lpum); 65428c2ecf20Sopenharmony_ci} 65438c2ecf20Sopenharmony_ci 65448c2ecf20Sopenharmony_cistatic void dasd_eckd_check_attention_work(struct work_struct *work) 65458c2ecf20Sopenharmony_ci{ 65468c2ecf20Sopenharmony_ci struct check_attention_work_data *data; 65478c2ecf20Sopenharmony_ci struct dasd_rssd_messages *messages; 65488c2ecf20Sopenharmony_ci struct dasd_device *device; 65498c2ecf20Sopenharmony_ci int rc; 65508c2ecf20Sopenharmony_ci 65518c2ecf20Sopenharmony_ci data = container_of(work, struct check_attention_work_data, worker); 65528c2ecf20Sopenharmony_ci device = data->device; 65538c2ecf20Sopenharmony_ci messages = kzalloc(sizeof(*messages), GFP_KERNEL); 65548c2ecf20Sopenharmony_ci if (!messages) { 65558c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 65568c2ecf20Sopenharmony_ci "Could not allocate attention message buffer"); 65578c2ecf20Sopenharmony_ci goto out; 65588c2ecf20Sopenharmony_ci } 65598c2ecf20Sopenharmony_ci rc = dasd_eckd_read_message_buffer(device, messages, data->lpum); 65608c2ecf20Sopenharmony_ci if (rc) 65618c2ecf20Sopenharmony_ci goto out; 65628c2ecf20Sopenharmony_ci 65638c2ecf20Sopenharmony_ci if (messages->length == ATTENTION_LENGTH_CUIR && 65648c2ecf20Sopenharmony_ci messages->format == ATTENTION_FORMAT_CUIR) 65658c2ecf20Sopenharmony_ci dasd_eckd_handle_cuir(device, messages, data->lpum); 65668c2ecf20Sopenharmony_ci if (messages->length == ATTENTION_LENGTH_OOS && 65678c2ecf20Sopenharmony_ci messages->format == ATTENTION_FORMAT_OOS) 65688c2ecf20Sopenharmony_ci dasd_eckd_handle_oos(device, messages, data->lpum); 65698c2ecf20Sopenharmony_ci 65708c2ecf20Sopenharmony_ciout: 65718c2ecf20Sopenharmony_ci dasd_put_device(device); 65728c2ecf20Sopenharmony_ci kfree(messages); 65738c2ecf20Sopenharmony_ci kfree(data); 65748c2ecf20Sopenharmony_ci} 65758c2ecf20Sopenharmony_ci 65768c2ecf20Sopenharmony_cistatic int dasd_eckd_check_attention(struct dasd_device *device, __u8 lpum) 65778c2ecf20Sopenharmony_ci{ 65788c2ecf20Sopenharmony_ci struct check_attention_work_data *data; 65798c2ecf20Sopenharmony_ci 65808c2ecf20Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_ATOMIC); 65818c2ecf20Sopenharmony_ci if (!data) 65828c2ecf20Sopenharmony_ci return -ENOMEM; 65838c2ecf20Sopenharmony_ci INIT_WORK(&data->worker, dasd_eckd_check_attention_work); 65848c2ecf20Sopenharmony_ci dasd_get_device(device); 65858c2ecf20Sopenharmony_ci data->device = device; 65868c2ecf20Sopenharmony_ci data->lpum = lpum; 65878c2ecf20Sopenharmony_ci schedule_work(&data->worker); 65888c2ecf20Sopenharmony_ci return 0; 65898c2ecf20Sopenharmony_ci} 65908c2ecf20Sopenharmony_ci 65918c2ecf20Sopenharmony_cistatic int dasd_eckd_disable_hpf_path(struct dasd_device *device, __u8 lpum) 65928c2ecf20Sopenharmony_ci{ 65938c2ecf20Sopenharmony_ci if (~lpum & dasd_path_get_opm(device)) { 65948c2ecf20Sopenharmony_ci dasd_path_add_nohpfpm(device, lpum); 65958c2ecf20Sopenharmony_ci dasd_path_remove_opm(device, lpum); 65968c2ecf20Sopenharmony_ci dev_err(&device->cdev->dev, 65978c2ecf20Sopenharmony_ci "Channel path %02X lost HPF functionality and is disabled\n", 65988c2ecf20Sopenharmony_ci lpum); 65998c2ecf20Sopenharmony_ci return 1; 66008c2ecf20Sopenharmony_ci } 66018c2ecf20Sopenharmony_ci return 0; 66028c2ecf20Sopenharmony_ci} 66038c2ecf20Sopenharmony_ci 66048c2ecf20Sopenharmony_cistatic void dasd_eckd_disable_hpf_device(struct dasd_device *device) 66058c2ecf20Sopenharmony_ci{ 66068c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 66078c2ecf20Sopenharmony_ci 66088c2ecf20Sopenharmony_ci dev_err(&device->cdev->dev, 66098c2ecf20Sopenharmony_ci "High Performance FICON disabled\n"); 66108c2ecf20Sopenharmony_ci private->fcx_max_data = 0; 66118c2ecf20Sopenharmony_ci} 66128c2ecf20Sopenharmony_ci 66138c2ecf20Sopenharmony_cistatic int dasd_eckd_hpf_enabled(struct dasd_device *device) 66148c2ecf20Sopenharmony_ci{ 66158c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 66168c2ecf20Sopenharmony_ci 66178c2ecf20Sopenharmony_ci return private->fcx_max_data ? 1 : 0; 66188c2ecf20Sopenharmony_ci} 66198c2ecf20Sopenharmony_ci 66208c2ecf20Sopenharmony_cistatic void dasd_eckd_handle_hpf_error(struct dasd_device *device, 66218c2ecf20Sopenharmony_ci struct irb *irb) 66228c2ecf20Sopenharmony_ci{ 66238c2ecf20Sopenharmony_ci struct dasd_eckd_private *private = device->private; 66248c2ecf20Sopenharmony_ci 66258c2ecf20Sopenharmony_ci if (!private->fcx_max_data) { 66268c2ecf20Sopenharmony_ci /* sanity check for no HPF, the error makes no sense */ 66278c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 66288c2ecf20Sopenharmony_ci "Trying to disable HPF for a non HPF device"); 66298c2ecf20Sopenharmony_ci return; 66308c2ecf20Sopenharmony_ci } 66318c2ecf20Sopenharmony_ci if (irb->scsw.tm.sesq == SCSW_SESQ_DEV_NOFCX) { 66328c2ecf20Sopenharmony_ci dasd_eckd_disable_hpf_device(device); 66338c2ecf20Sopenharmony_ci } else if (irb->scsw.tm.sesq == SCSW_SESQ_PATH_NOFCX) { 66348c2ecf20Sopenharmony_ci if (dasd_eckd_disable_hpf_path(device, irb->esw.esw1.lpum)) 66358c2ecf20Sopenharmony_ci return; 66368c2ecf20Sopenharmony_ci dasd_eckd_disable_hpf_device(device); 66378c2ecf20Sopenharmony_ci dasd_path_set_tbvpm(device, 66388c2ecf20Sopenharmony_ci dasd_path_get_hpfpm(device)); 66398c2ecf20Sopenharmony_ci } 66408c2ecf20Sopenharmony_ci /* 66418c2ecf20Sopenharmony_ci * prevent that any new I/O ist started on the device and schedule a 66428c2ecf20Sopenharmony_ci * requeue of existing requests 66438c2ecf20Sopenharmony_ci */ 66448c2ecf20Sopenharmony_ci dasd_device_set_stop_bits(device, DASD_STOPPED_NOT_ACC); 66458c2ecf20Sopenharmony_ci dasd_schedule_requeue(device); 66468c2ecf20Sopenharmony_ci} 66478c2ecf20Sopenharmony_ci 66488c2ecf20Sopenharmony_ci/* 66498c2ecf20Sopenharmony_ci * Initialize block layer request queue. 66508c2ecf20Sopenharmony_ci */ 66518c2ecf20Sopenharmony_cistatic void dasd_eckd_setup_blk_queue(struct dasd_block *block) 66528c2ecf20Sopenharmony_ci{ 66538c2ecf20Sopenharmony_ci unsigned int logical_block_size = block->bp_block; 66548c2ecf20Sopenharmony_ci struct request_queue *q = block->request_queue; 66558c2ecf20Sopenharmony_ci struct dasd_device *device = block->base; 66568c2ecf20Sopenharmony_ci int max; 66578c2ecf20Sopenharmony_ci 66588c2ecf20Sopenharmony_ci if (device->features & DASD_FEATURE_USERAW) { 66598c2ecf20Sopenharmony_ci /* 66608c2ecf20Sopenharmony_ci * the max_blocks value for raw_track access is 256 66618c2ecf20Sopenharmony_ci * it is higher than the native ECKD value because we 66628c2ecf20Sopenharmony_ci * only need one ccw per track 66638c2ecf20Sopenharmony_ci * so the max_hw_sectors are 66648c2ecf20Sopenharmony_ci * 2048 x 512B = 1024kB = 16 tracks 66658c2ecf20Sopenharmony_ci */ 66668c2ecf20Sopenharmony_ci max = DASD_ECKD_MAX_BLOCKS_RAW << block->s2b_shift; 66678c2ecf20Sopenharmony_ci } else { 66688c2ecf20Sopenharmony_ci max = DASD_ECKD_MAX_BLOCKS << block->s2b_shift; 66698c2ecf20Sopenharmony_ci } 66708c2ecf20Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_NONROT, q); 66718c2ecf20Sopenharmony_ci q->limits.max_dev_sectors = max; 66728c2ecf20Sopenharmony_ci blk_queue_logical_block_size(q, logical_block_size); 66738c2ecf20Sopenharmony_ci blk_queue_max_hw_sectors(q, max); 66748c2ecf20Sopenharmony_ci blk_queue_max_segments(q, USHRT_MAX); 66758c2ecf20Sopenharmony_ci /* With page sized segments each segment can be translated into one idaw/tidaw */ 66768c2ecf20Sopenharmony_ci blk_queue_max_segment_size(q, PAGE_SIZE); 66778c2ecf20Sopenharmony_ci blk_queue_segment_boundary(q, PAGE_SIZE - 1); 66788c2ecf20Sopenharmony_ci} 66798c2ecf20Sopenharmony_ci 66808c2ecf20Sopenharmony_cistatic struct ccw_driver dasd_eckd_driver = { 66818c2ecf20Sopenharmony_ci .driver = { 66828c2ecf20Sopenharmony_ci .name = "dasd-eckd", 66838c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 66848c2ecf20Sopenharmony_ci }, 66858c2ecf20Sopenharmony_ci .ids = dasd_eckd_ids, 66868c2ecf20Sopenharmony_ci .probe = dasd_eckd_probe, 66878c2ecf20Sopenharmony_ci .remove = dasd_generic_remove, 66888c2ecf20Sopenharmony_ci .set_offline = dasd_generic_set_offline, 66898c2ecf20Sopenharmony_ci .set_online = dasd_eckd_set_online, 66908c2ecf20Sopenharmony_ci .notify = dasd_generic_notify, 66918c2ecf20Sopenharmony_ci .path_event = dasd_generic_path_event, 66928c2ecf20Sopenharmony_ci .shutdown = dasd_generic_shutdown, 66938c2ecf20Sopenharmony_ci .freeze = dasd_generic_pm_freeze, 66948c2ecf20Sopenharmony_ci .thaw = dasd_generic_restore_device, 66958c2ecf20Sopenharmony_ci .restore = dasd_generic_restore_device, 66968c2ecf20Sopenharmony_ci .uc_handler = dasd_generic_uc_handler, 66978c2ecf20Sopenharmony_ci .int_class = IRQIO_DAS, 66988c2ecf20Sopenharmony_ci}; 66998c2ecf20Sopenharmony_ci 67008c2ecf20Sopenharmony_cistatic struct dasd_discipline dasd_eckd_discipline = { 67018c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 67028c2ecf20Sopenharmony_ci .name = "ECKD", 67038c2ecf20Sopenharmony_ci .ebcname = "ECKD", 67048c2ecf20Sopenharmony_ci .check_device = dasd_eckd_check_characteristics, 67058c2ecf20Sopenharmony_ci .uncheck_device = dasd_eckd_uncheck_device, 67068c2ecf20Sopenharmony_ci .do_analysis = dasd_eckd_do_analysis, 67078c2ecf20Sopenharmony_ci .pe_handler = dasd_eckd_pe_handler, 67088c2ecf20Sopenharmony_ci .basic_to_ready = dasd_eckd_basic_to_ready, 67098c2ecf20Sopenharmony_ci .online_to_ready = dasd_eckd_online_to_ready, 67108c2ecf20Sopenharmony_ci .basic_to_known = dasd_eckd_basic_to_known, 67118c2ecf20Sopenharmony_ci .setup_blk_queue = dasd_eckd_setup_blk_queue, 67128c2ecf20Sopenharmony_ci .fill_geometry = dasd_eckd_fill_geometry, 67138c2ecf20Sopenharmony_ci .start_IO = dasd_start_IO, 67148c2ecf20Sopenharmony_ci .term_IO = dasd_term_IO, 67158c2ecf20Sopenharmony_ci .handle_terminated_request = dasd_eckd_handle_terminated_request, 67168c2ecf20Sopenharmony_ci .format_device = dasd_eckd_format_device, 67178c2ecf20Sopenharmony_ci .check_device_format = dasd_eckd_check_device_format, 67188c2ecf20Sopenharmony_ci .erp_action = dasd_eckd_erp_action, 67198c2ecf20Sopenharmony_ci .erp_postaction = dasd_eckd_erp_postaction, 67208c2ecf20Sopenharmony_ci .check_for_device_change = dasd_eckd_check_for_device_change, 67218c2ecf20Sopenharmony_ci .build_cp = dasd_eckd_build_alias_cp, 67228c2ecf20Sopenharmony_ci .free_cp = dasd_eckd_free_alias_cp, 67238c2ecf20Sopenharmony_ci .dump_sense = dasd_eckd_dump_sense, 67248c2ecf20Sopenharmony_ci .dump_sense_dbf = dasd_eckd_dump_sense_dbf, 67258c2ecf20Sopenharmony_ci .fill_info = dasd_eckd_fill_info, 67268c2ecf20Sopenharmony_ci .ioctl = dasd_eckd_ioctl, 67278c2ecf20Sopenharmony_ci .freeze = dasd_eckd_pm_freeze, 67288c2ecf20Sopenharmony_ci .restore = dasd_eckd_restore_device, 67298c2ecf20Sopenharmony_ci .reload = dasd_eckd_reload_device, 67308c2ecf20Sopenharmony_ci .get_uid = dasd_eckd_get_uid, 67318c2ecf20Sopenharmony_ci .kick_validate = dasd_eckd_kick_validate_server, 67328c2ecf20Sopenharmony_ci .check_attention = dasd_eckd_check_attention, 67338c2ecf20Sopenharmony_ci .host_access_count = dasd_eckd_host_access_count, 67348c2ecf20Sopenharmony_ci .hosts_print = dasd_hosts_print, 67358c2ecf20Sopenharmony_ci .handle_hpf_error = dasd_eckd_handle_hpf_error, 67368c2ecf20Sopenharmony_ci .disable_hpf = dasd_eckd_disable_hpf_device, 67378c2ecf20Sopenharmony_ci .hpf_enabled = dasd_eckd_hpf_enabled, 67388c2ecf20Sopenharmony_ci .reset_path = dasd_eckd_reset_path, 67398c2ecf20Sopenharmony_ci .is_ese = dasd_eckd_is_ese, 67408c2ecf20Sopenharmony_ci .space_allocated = dasd_eckd_space_allocated, 67418c2ecf20Sopenharmony_ci .space_configured = dasd_eckd_space_configured, 67428c2ecf20Sopenharmony_ci .logical_capacity = dasd_eckd_logical_capacity, 67438c2ecf20Sopenharmony_ci .release_space = dasd_eckd_release_space, 67448c2ecf20Sopenharmony_ci .ext_pool_id = dasd_eckd_ext_pool_id, 67458c2ecf20Sopenharmony_ci .ext_size = dasd_eckd_ext_size, 67468c2ecf20Sopenharmony_ci .ext_pool_cap_at_warnlevel = dasd_eckd_ext_pool_cap_at_warnlevel, 67478c2ecf20Sopenharmony_ci .ext_pool_warn_thrshld = dasd_eckd_ext_pool_warn_thrshld, 67488c2ecf20Sopenharmony_ci .ext_pool_oos = dasd_eckd_ext_pool_oos, 67498c2ecf20Sopenharmony_ci .ext_pool_exhaust = dasd_eckd_ext_pool_exhaust, 67508c2ecf20Sopenharmony_ci .ese_format = dasd_eckd_ese_format, 67518c2ecf20Sopenharmony_ci .ese_read = dasd_eckd_ese_read, 67528c2ecf20Sopenharmony_ci}; 67538c2ecf20Sopenharmony_ci 67548c2ecf20Sopenharmony_cistatic int __init 67558c2ecf20Sopenharmony_cidasd_eckd_init(void) 67568c2ecf20Sopenharmony_ci{ 67578c2ecf20Sopenharmony_ci int ret; 67588c2ecf20Sopenharmony_ci 67598c2ecf20Sopenharmony_ci ASCEBC(dasd_eckd_discipline.ebcname, 4); 67608c2ecf20Sopenharmony_ci dasd_reserve_req = kmalloc(sizeof(*dasd_reserve_req), 67618c2ecf20Sopenharmony_ci GFP_KERNEL | GFP_DMA); 67628c2ecf20Sopenharmony_ci if (!dasd_reserve_req) 67638c2ecf20Sopenharmony_ci return -ENOMEM; 67648c2ecf20Sopenharmony_ci dasd_vol_info_req = kmalloc(sizeof(*dasd_vol_info_req), 67658c2ecf20Sopenharmony_ci GFP_KERNEL | GFP_DMA); 67668c2ecf20Sopenharmony_ci if (!dasd_vol_info_req) { 67678c2ecf20Sopenharmony_ci kfree(dasd_reserve_req); 67688c2ecf20Sopenharmony_ci return -ENOMEM; 67698c2ecf20Sopenharmony_ci } 67708c2ecf20Sopenharmony_ci pe_handler_worker = kmalloc(sizeof(*pe_handler_worker), 67718c2ecf20Sopenharmony_ci GFP_KERNEL | GFP_DMA); 67728c2ecf20Sopenharmony_ci if (!pe_handler_worker) { 67738c2ecf20Sopenharmony_ci kfree(dasd_reserve_req); 67748c2ecf20Sopenharmony_ci kfree(dasd_vol_info_req); 67758c2ecf20Sopenharmony_ci return -ENOMEM; 67768c2ecf20Sopenharmony_ci } 67778c2ecf20Sopenharmony_ci rawpadpage = (void *)__get_free_page(GFP_KERNEL); 67788c2ecf20Sopenharmony_ci if (!rawpadpage) { 67798c2ecf20Sopenharmony_ci kfree(pe_handler_worker); 67808c2ecf20Sopenharmony_ci kfree(dasd_reserve_req); 67818c2ecf20Sopenharmony_ci kfree(dasd_vol_info_req); 67828c2ecf20Sopenharmony_ci return -ENOMEM; 67838c2ecf20Sopenharmony_ci } 67848c2ecf20Sopenharmony_ci ret = ccw_driver_register(&dasd_eckd_driver); 67858c2ecf20Sopenharmony_ci if (!ret) 67868c2ecf20Sopenharmony_ci wait_for_device_probe(); 67878c2ecf20Sopenharmony_ci else { 67888c2ecf20Sopenharmony_ci kfree(pe_handler_worker); 67898c2ecf20Sopenharmony_ci kfree(dasd_reserve_req); 67908c2ecf20Sopenharmony_ci kfree(dasd_vol_info_req); 67918c2ecf20Sopenharmony_ci free_page((unsigned long)rawpadpage); 67928c2ecf20Sopenharmony_ci } 67938c2ecf20Sopenharmony_ci return ret; 67948c2ecf20Sopenharmony_ci} 67958c2ecf20Sopenharmony_ci 67968c2ecf20Sopenharmony_cistatic void __exit 67978c2ecf20Sopenharmony_cidasd_eckd_cleanup(void) 67988c2ecf20Sopenharmony_ci{ 67998c2ecf20Sopenharmony_ci ccw_driver_unregister(&dasd_eckd_driver); 68008c2ecf20Sopenharmony_ci kfree(pe_handler_worker); 68018c2ecf20Sopenharmony_ci kfree(dasd_reserve_req); 68028c2ecf20Sopenharmony_ci free_page((unsigned long)rawpadpage); 68038c2ecf20Sopenharmony_ci} 68048c2ecf20Sopenharmony_ci 68058c2ecf20Sopenharmony_cimodule_init(dasd_eckd_init); 68068c2ecf20Sopenharmony_cimodule_exit(dasd_eckd_cleanup); 6807