162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> 462306a36Sopenharmony_ci * Horst Hummel <Horst.Hummel@de.ibm.com> 562306a36Sopenharmony_ci * Carsten Otte <Cotte@de.ibm.com> 662306a36Sopenharmony_ci * Martin Schwidefsky <schwidefsky@de.ibm.com> 762306a36Sopenharmony_ci * Bugreports.to..: <Linux390@de.ibm.com> 862306a36Sopenharmony_ci * Copyright IBM Corp. 1999, 2009 962306a36Sopenharmony_ci * EMC Symmetrix ioctl Copyright EMC Corporation, 2008 1062306a36Sopenharmony_ci * Author.........: Nigel Hislop <hislop_nigel@emc.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define KMSG_COMPONENT "dasd-eckd" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/stddef.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/hdreg.h> /* HDIO_GETGEO */ 1962306a36Sopenharmony_ci#include <linux/bio.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/compat.h> 2262306a36Sopenharmony_ci#include <linux/init.h> 2362306a36Sopenharmony_ci#include <linux/seq_file.h> 2462306a36Sopenharmony_ci#include <linux/uaccess.h> 2562306a36Sopenharmony_ci#include <linux/io.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <asm/css_chars.h> 2862306a36Sopenharmony_ci#include <asm/debug.h> 2962306a36Sopenharmony_ci#include <asm/idals.h> 3062306a36Sopenharmony_ci#include <asm/ebcdic.h> 3162306a36Sopenharmony_ci#include <asm/cio.h> 3262306a36Sopenharmony_ci#include <asm/ccwdev.h> 3362306a36Sopenharmony_ci#include <asm/itcw.h> 3462306a36Sopenharmony_ci#include <asm/schid.h> 3562306a36Sopenharmony_ci#include <asm/chpid.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include "dasd_int.h" 3862306a36Sopenharmony_ci#include "dasd_eckd.h" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#ifdef PRINTK_HEADER 4162306a36Sopenharmony_ci#undef PRINTK_HEADER 4262306a36Sopenharmony_ci#endif /* PRINTK_HEADER */ 4362306a36Sopenharmony_ci#define PRINTK_HEADER "dasd(eckd):" 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * raw track access always map to 64k in memory 4762306a36Sopenharmony_ci * so it maps to 16 blocks of 4k per track 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci#define DASD_RAW_BLOCK_PER_TRACK 16 5062306a36Sopenharmony_ci#define DASD_RAW_BLOCKSIZE 4096 5162306a36Sopenharmony_ci/* 64k are 128 x 512 byte sectors */ 5262306a36Sopenharmony_ci#define DASD_RAW_SECTORS_PER_TRACK 128 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic struct dasd_discipline dasd_eckd_discipline; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* The ccw bus type uses this table to find devices that it sends to 5962306a36Sopenharmony_ci * dasd_eckd_probe */ 6062306a36Sopenharmony_cistatic struct ccw_device_id dasd_eckd_ids[] = { 6162306a36Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3390, 0), .driver_info = 0x1}, 6262306a36Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3390, 0), .driver_info = 0x2}, 6362306a36Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3380, 0), .driver_info = 0x3}, 6462306a36Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3380, 0), .driver_info = 0x4}, 6562306a36Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3380, 0), .driver_info = 0x5}, 6662306a36Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x9343, 0, 0x9345, 0), .driver_info = 0x6}, 6762306a36Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3390, 0), .driver_info = 0x7}, 6862306a36Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3380, 0), .driver_info = 0x8}, 6962306a36Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3390, 0), .driver_info = 0x9}, 7062306a36Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3380, 0), .driver_info = 0xa}, 7162306a36Sopenharmony_ci { /* end of list */ }, 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(ccw, dasd_eckd_ids); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic struct ccw_driver dasd_eckd_driver; /* see below */ 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void *rawpadpage; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define INIT_CQR_OK 0 8162306a36Sopenharmony_ci#define INIT_CQR_UNFORMATTED 1 8262306a36Sopenharmony_ci#define INIT_CQR_ERROR 2 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* emergency request for reserve/release */ 8562306a36Sopenharmony_cistatic struct { 8662306a36Sopenharmony_ci struct dasd_ccw_req cqr; 8762306a36Sopenharmony_ci struct ccw1 ccw; 8862306a36Sopenharmony_ci char data[32]; 8962306a36Sopenharmony_ci} *dasd_reserve_req; 9062306a36Sopenharmony_cistatic DEFINE_MUTEX(dasd_reserve_mutex); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic struct { 9362306a36Sopenharmony_ci struct dasd_ccw_req cqr; 9462306a36Sopenharmony_ci struct ccw1 ccw[2]; 9562306a36Sopenharmony_ci char data[40]; 9662306a36Sopenharmony_ci} *dasd_vol_info_req; 9762306a36Sopenharmony_cistatic DEFINE_MUTEX(dasd_vol_info_mutex); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistruct ext_pool_exhaust_work_data { 10062306a36Sopenharmony_ci struct work_struct worker; 10162306a36Sopenharmony_ci struct dasd_device *device; 10262306a36Sopenharmony_ci struct dasd_device *base; 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* definitions for the path verification worker */ 10662306a36Sopenharmony_cistruct pe_handler_work_data { 10762306a36Sopenharmony_ci struct work_struct worker; 10862306a36Sopenharmony_ci struct dasd_device *device; 10962306a36Sopenharmony_ci struct dasd_ccw_req cqr; 11062306a36Sopenharmony_ci struct ccw1 ccw; 11162306a36Sopenharmony_ci __u8 rcd_buffer[DASD_ECKD_RCD_DATA_SIZE]; 11262306a36Sopenharmony_ci int isglobal; 11362306a36Sopenharmony_ci __u8 tbvpm; 11462306a36Sopenharmony_ci __u8 fcsecpm; 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_cistatic struct pe_handler_work_data *pe_handler_worker; 11762306a36Sopenharmony_cistatic DEFINE_MUTEX(dasd_pe_handler_mutex); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistruct check_attention_work_data { 12062306a36Sopenharmony_ci struct work_struct worker; 12162306a36Sopenharmony_ci struct dasd_device *device; 12262306a36Sopenharmony_ci __u8 lpum; 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int dasd_eckd_ext_pool_id(struct dasd_device *); 12662306a36Sopenharmony_cistatic int prepare_itcw(struct itcw *, unsigned int, unsigned int, int, 12762306a36Sopenharmony_ci struct dasd_device *, struct dasd_device *, 12862306a36Sopenharmony_ci unsigned int, int, unsigned int, unsigned int, 12962306a36Sopenharmony_ci unsigned int, unsigned int); 13062306a36Sopenharmony_cistatic int dasd_eckd_query_pprc_status(struct dasd_device *, 13162306a36Sopenharmony_ci struct dasd_pprc_data_sc4 *); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* initial attempt at a probe function. this can be simplified once 13462306a36Sopenharmony_ci * the other detection code is gone */ 13562306a36Sopenharmony_cistatic int 13662306a36Sopenharmony_cidasd_eckd_probe (struct ccw_device *cdev) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci int ret; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* set ECKD specific ccw-device options */ 14162306a36Sopenharmony_ci ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE | 14262306a36Sopenharmony_ci CCWDEV_DO_PATHGROUP | CCWDEV_DO_MULTIPATH); 14362306a36Sopenharmony_ci if (ret) { 14462306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", 14562306a36Sopenharmony_ci "dasd_eckd_probe: could not set " 14662306a36Sopenharmony_ci "ccw-device options"); 14762306a36Sopenharmony_ci return ret; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci ret = dasd_generic_probe(cdev); 15062306a36Sopenharmony_ci return ret; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int 15462306a36Sopenharmony_cidasd_eckd_set_online(struct ccw_device *cdev) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci return dasd_generic_set_online(cdev, &dasd_eckd_discipline); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic const int sizes_trk0[] = { 28, 148, 84 }; 16062306a36Sopenharmony_ci#define LABEL_SIZE 140 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/* head and record addresses of count_area read in analysis ccw */ 16362306a36Sopenharmony_cistatic const int count_area_head[] = { 0, 0, 0, 0, 1 }; 16462306a36Sopenharmony_cistatic const int count_area_rec[] = { 1, 2, 3, 4, 1 }; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic inline unsigned int 16762306a36Sopenharmony_ciceil_quot(unsigned int d1, unsigned int d2) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci return (d1 + (d2 - 1)) / d2; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic unsigned int 17362306a36Sopenharmony_cirecs_per_track(struct dasd_eckd_characteristics * rdc, 17462306a36Sopenharmony_ci unsigned int kl, unsigned int dl) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci int dn, kn; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci switch (rdc->dev_type) { 17962306a36Sopenharmony_ci case 0x3380: 18062306a36Sopenharmony_ci if (kl) 18162306a36Sopenharmony_ci return 1499 / (15 + 7 + ceil_quot(kl + 12, 32) + 18262306a36Sopenharmony_ci ceil_quot(dl + 12, 32)); 18362306a36Sopenharmony_ci else 18462306a36Sopenharmony_ci return 1499 / (15 + ceil_quot(dl + 12, 32)); 18562306a36Sopenharmony_ci case 0x3390: 18662306a36Sopenharmony_ci dn = ceil_quot(dl + 6, 232) + 1; 18762306a36Sopenharmony_ci if (kl) { 18862306a36Sopenharmony_ci kn = ceil_quot(kl + 6, 232) + 1; 18962306a36Sopenharmony_ci return 1729 / (10 + 9 + ceil_quot(kl + 6 * kn, 34) + 19062306a36Sopenharmony_ci 9 + ceil_quot(dl + 6 * dn, 34)); 19162306a36Sopenharmony_ci } else 19262306a36Sopenharmony_ci return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34)); 19362306a36Sopenharmony_ci case 0x9345: 19462306a36Sopenharmony_ci dn = ceil_quot(dl + 6, 232) + 1; 19562306a36Sopenharmony_ci if (kl) { 19662306a36Sopenharmony_ci kn = ceil_quot(kl + 6, 232) + 1; 19762306a36Sopenharmony_ci return 1420 / (18 + 7 + ceil_quot(kl + 6 * kn, 34) + 19862306a36Sopenharmony_ci ceil_quot(dl + 6 * dn, 34)); 19962306a36Sopenharmony_ci } else 20062306a36Sopenharmony_ci return 1420 / (18 + 7 + ceil_quot(dl + 6 * dn, 34)); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci return 0; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void set_ch_t(struct ch_t *geo, __u32 cyl, __u8 head) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci geo->cyl = (__u16) cyl; 20862306a36Sopenharmony_ci geo->head = cyl >> 16; 20962306a36Sopenharmony_ci geo->head <<= 4; 21062306a36Sopenharmony_ci geo->head |= head; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* 21462306a36Sopenharmony_ci * calculate failing track from sense data depending if 21562306a36Sopenharmony_ci * it is an EAV device or not 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_cistatic int dasd_eckd_track_from_irb(struct irb *irb, struct dasd_device *device, 21862306a36Sopenharmony_ci sector_t *track) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 22162306a36Sopenharmony_ci u8 *sense = NULL; 22262306a36Sopenharmony_ci u32 cyl; 22362306a36Sopenharmony_ci u8 head; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci sense = dasd_get_sense(irb); 22662306a36Sopenharmony_ci if (!sense) { 22762306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 22862306a36Sopenharmony_ci "ESE error no sense data\n"); 22962306a36Sopenharmony_ci return -EINVAL; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci if (!(sense[27] & DASD_SENSE_BIT_2)) { 23262306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 23362306a36Sopenharmony_ci "ESE error no valid track data\n"); 23462306a36Sopenharmony_ci return -EINVAL; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (sense[27] & DASD_SENSE_BIT_3) { 23862306a36Sopenharmony_ci /* enhanced addressing */ 23962306a36Sopenharmony_ci cyl = sense[30] << 20; 24062306a36Sopenharmony_ci cyl |= (sense[31] & 0xF0) << 12; 24162306a36Sopenharmony_ci cyl |= sense[28] << 8; 24262306a36Sopenharmony_ci cyl |= sense[29]; 24362306a36Sopenharmony_ci } else { 24462306a36Sopenharmony_ci cyl = sense[29] << 8; 24562306a36Sopenharmony_ci cyl |= sense[30]; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci head = sense[31] & 0x0F; 24862306a36Sopenharmony_ci *track = cyl * private->rdc_data.trk_per_cyl + head; 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int set_timestamp(struct ccw1 *ccw, struct DE_eckd_data *data, 25362306a36Sopenharmony_ci struct dasd_device *device) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 25662306a36Sopenharmony_ci int rc; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci rc = get_phys_clock(&data->ep_sys_time); 25962306a36Sopenharmony_ci /* 26062306a36Sopenharmony_ci * Ignore return code if XRC is not supported or 26162306a36Sopenharmony_ci * sync clock is switched off 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci if ((rc && !private->rdc_data.facilities.XRC_supported) || 26462306a36Sopenharmony_ci rc == -EOPNOTSUPP || rc == -EACCES) 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* switch on System Time Stamp - needed for XRC Support */ 26862306a36Sopenharmony_ci data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */ 26962306a36Sopenharmony_ci data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */ 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (ccw) { 27262306a36Sopenharmony_ci ccw->count = sizeof(struct DE_eckd_data); 27362306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return rc; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int 28062306a36Sopenharmony_cidefine_extent(struct ccw1 *ccw, struct DE_eckd_data *data, unsigned int trk, 28162306a36Sopenharmony_ci unsigned int totrk, int cmd, struct dasd_device *device, 28262306a36Sopenharmony_ci int blksize) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 28562306a36Sopenharmony_ci u16 heads, beghead, endhead; 28662306a36Sopenharmony_ci u32 begcyl, endcyl; 28762306a36Sopenharmony_ci int rc = 0; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (ccw) { 29062306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT; 29162306a36Sopenharmony_ci ccw->flags = 0; 29262306a36Sopenharmony_ci ccw->count = 16; 29362306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(data); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci memset(data, 0, sizeof(struct DE_eckd_data)); 29762306a36Sopenharmony_ci switch (cmd) { 29862306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_HOME_ADDRESS: 29962306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_RECORD_ZERO: 30062306a36Sopenharmony_ci case DASD_ECKD_CCW_READ: 30162306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_MT: 30262306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_CKD: 30362306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_CKD_MT: 30462306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_KD: 30562306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_KD_MT: 30662306a36Sopenharmony_ci data->mask.perm = 0x1; 30762306a36Sopenharmony_ci data->attributes.operation = private->attrib.operation; 30862306a36Sopenharmony_ci break; 30962306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_COUNT: 31062306a36Sopenharmony_ci data->mask.perm = 0x1; 31162306a36Sopenharmony_ci data->attributes.operation = DASD_BYPASS_CACHE; 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_TRACK: 31462306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_TRACK_DATA: 31562306a36Sopenharmony_ci data->mask.perm = 0x1; 31662306a36Sopenharmony_ci data->attributes.operation = private->attrib.operation; 31762306a36Sopenharmony_ci data->blk_size = 0; 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE: 32062306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_MT: 32162306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_KD: 32262306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_KD_MT: 32362306a36Sopenharmony_ci data->mask.perm = 0x02; 32462306a36Sopenharmony_ci data->attributes.operation = private->attrib.operation; 32562306a36Sopenharmony_ci rc = set_timestamp(ccw, data, device); 32662306a36Sopenharmony_ci break; 32762306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_CKD: 32862306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_CKD_MT: 32962306a36Sopenharmony_ci data->attributes.operation = DASD_BYPASS_CACHE; 33062306a36Sopenharmony_ci rc = set_timestamp(ccw, data, device); 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci case DASD_ECKD_CCW_ERASE: 33362306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: 33462306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_RECORD_ZERO: 33562306a36Sopenharmony_ci data->mask.perm = 0x3; 33662306a36Sopenharmony_ci data->mask.auth = 0x1; 33762306a36Sopenharmony_ci data->attributes.operation = DASD_BYPASS_CACHE; 33862306a36Sopenharmony_ci rc = set_timestamp(ccw, data, device); 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_FULL_TRACK: 34162306a36Sopenharmony_ci data->mask.perm = 0x03; 34262306a36Sopenharmony_ci data->attributes.operation = private->attrib.operation; 34362306a36Sopenharmony_ci data->blk_size = 0; 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_TRACK_DATA: 34662306a36Sopenharmony_ci data->mask.perm = 0x02; 34762306a36Sopenharmony_ci data->attributes.operation = private->attrib.operation; 34862306a36Sopenharmony_ci data->blk_size = blksize; 34962306a36Sopenharmony_ci rc = set_timestamp(ccw, data, device); 35062306a36Sopenharmony_ci break; 35162306a36Sopenharmony_ci default: 35262306a36Sopenharmony_ci dev_err(&device->cdev->dev, 35362306a36Sopenharmony_ci "0x%x is not a known command\n", cmd); 35462306a36Sopenharmony_ci break; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci data->attributes.mode = 0x3; /* ECKD */ 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if ((private->rdc_data.cu_type == 0x2105 || 36062306a36Sopenharmony_ci private->rdc_data.cu_type == 0x2107 || 36162306a36Sopenharmony_ci private->rdc_data.cu_type == 0x1750) 36262306a36Sopenharmony_ci && !(private->uses_cdl && trk < 2)) 36362306a36Sopenharmony_ci data->ga_extended |= 0x40; /* Regular Data Format Mode */ 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci heads = private->rdc_data.trk_per_cyl; 36662306a36Sopenharmony_ci begcyl = trk / heads; 36762306a36Sopenharmony_ci beghead = trk % heads; 36862306a36Sopenharmony_ci endcyl = totrk / heads; 36962306a36Sopenharmony_ci endhead = totrk % heads; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* check for sequential prestage - enhance cylinder range */ 37262306a36Sopenharmony_ci if (data->attributes.operation == DASD_SEQ_PRESTAGE || 37362306a36Sopenharmony_ci data->attributes.operation == DASD_SEQ_ACCESS) { 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (endcyl + private->attrib.nr_cyl < private->real_cyl) 37662306a36Sopenharmony_ci endcyl += private->attrib.nr_cyl; 37762306a36Sopenharmony_ci else 37862306a36Sopenharmony_ci endcyl = (private->real_cyl - 1); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci set_ch_t(&data->beg_ext, begcyl, beghead); 38262306a36Sopenharmony_ci set_ch_t(&data->end_ext, endcyl, endhead); 38362306a36Sopenharmony_ci return rc; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic void locate_record_ext(struct ccw1 *ccw, struct LRE_eckd_data *data, 38862306a36Sopenharmony_ci unsigned int trk, unsigned int rec_on_trk, 38962306a36Sopenharmony_ci int count, int cmd, struct dasd_device *device, 39062306a36Sopenharmony_ci unsigned int reclen, unsigned int tlf) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 39362306a36Sopenharmony_ci int sector; 39462306a36Sopenharmony_ci int dn, d; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (ccw) { 39762306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD_EXT; 39862306a36Sopenharmony_ci ccw->flags = 0; 39962306a36Sopenharmony_ci if (cmd == DASD_ECKD_CCW_WRITE_FULL_TRACK) 40062306a36Sopenharmony_ci ccw->count = 22; 40162306a36Sopenharmony_ci else 40262306a36Sopenharmony_ci ccw->count = 20; 40362306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(data); 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci memset(data, 0, sizeof(*data)); 40762306a36Sopenharmony_ci sector = 0; 40862306a36Sopenharmony_ci if (rec_on_trk) { 40962306a36Sopenharmony_ci switch (private->rdc_data.dev_type) { 41062306a36Sopenharmony_ci case 0x3390: 41162306a36Sopenharmony_ci dn = ceil_quot(reclen + 6, 232); 41262306a36Sopenharmony_ci d = 9 + ceil_quot(reclen + 6 * (dn + 1), 34); 41362306a36Sopenharmony_ci sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8; 41462306a36Sopenharmony_ci break; 41562306a36Sopenharmony_ci case 0x3380: 41662306a36Sopenharmony_ci d = 7 + ceil_quot(reclen + 12, 32); 41762306a36Sopenharmony_ci sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7; 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci data->sector = sector; 42262306a36Sopenharmony_ci /* note: meaning of count depends on the operation 42362306a36Sopenharmony_ci * for record based I/O it's the number of records, but for 42462306a36Sopenharmony_ci * track based I/O it's the number of tracks 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_ci data->count = count; 42762306a36Sopenharmony_ci switch (cmd) { 42862306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: 42962306a36Sopenharmony_ci data->operation.orientation = 0x3; 43062306a36Sopenharmony_ci data->operation.operation = 0x03; 43162306a36Sopenharmony_ci break; 43262306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_HOME_ADDRESS: 43362306a36Sopenharmony_ci data->operation.orientation = 0x3; 43462306a36Sopenharmony_ci data->operation.operation = 0x16; 43562306a36Sopenharmony_ci break; 43662306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_RECORD_ZERO: 43762306a36Sopenharmony_ci data->operation.orientation = 0x1; 43862306a36Sopenharmony_ci data->operation.operation = 0x03; 43962306a36Sopenharmony_ci data->count++; 44062306a36Sopenharmony_ci break; 44162306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_RECORD_ZERO: 44262306a36Sopenharmony_ci data->operation.orientation = 0x3; 44362306a36Sopenharmony_ci data->operation.operation = 0x16; 44462306a36Sopenharmony_ci data->count++; 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE: 44762306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_MT: 44862306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_KD: 44962306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_KD_MT: 45062306a36Sopenharmony_ci data->auxiliary.length_valid = 0x1; 45162306a36Sopenharmony_ci data->length = reclen; 45262306a36Sopenharmony_ci data->operation.operation = 0x01; 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_CKD: 45562306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_CKD_MT: 45662306a36Sopenharmony_ci data->auxiliary.length_valid = 0x1; 45762306a36Sopenharmony_ci data->length = reclen; 45862306a36Sopenharmony_ci data->operation.operation = 0x03; 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_FULL_TRACK: 46162306a36Sopenharmony_ci data->operation.orientation = 0x0; 46262306a36Sopenharmony_ci data->operation.operation = 0x3F; 46362306a36Sopenharmony_ci data->extended_operation = 0x11; 46462306a36Sopenharmony_ci data->length = 0; 46562306a36Sopenharmony_ci data->extended_parameter_length = 0x02; 46662306a36Sopenharmony_ci if (data->count > 8) { 46762306a36Sopenharmony_ci data->extended_parameter[0] = 0xFF; 46862306a36Sopenharmony_ci data->extended_parameter[1] = 0xFF; 46962306a36Sopenharmony_ci data->extended_parameter[1] <<= (16 - count); 47062306a36Sopenharmony_ci } else { 47162306a36Sopenharmony_ci data->extended_parameter[0] = 0xFF; 47262306a36Sopenharmony_ci data->extended_parameter[0] <<= (8 - count); 47362306a36Sopenharmony_ci data->extended_parameter[1] = 0x00; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci data->sector = 0xFF; 47662306a36Sopenharmony_ci break; 47762306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_TRACK_DATA: 47862306a36Sopenharmony_ci data->auxiliary.length_valid = 0x1; 47962306a36Sopenharmony_ci data->length = reclen; /* not tlf, as one might think */ 48062306a36Sopenharmony_ci data->operation.operation = 0x3F; 48162306a36Sopenharmony_ci data->extended_operation = 0x23; 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci case DASD_ECKD_CCW_READ: 48462306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_MT: 48562306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_KD: 48662306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_KD_MT: 48762306a36Sopenharmony_ci data->auxiliary.length_valid = 0x1; 48862306a36Sopenharmony_ci data->length = reclen; 48962306a36Sopenharmony_ci data->operation.operation = 0x06; 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_CKD: 49262306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_CKD_MT: 49362306a36Sopenharmony_ci data->auxiliary.length_valid = 0x1; 49462306a36Sopenharmony_ci data->length = reclen; 49562306a36Sopenharmony_ci data->operation.operation = 0x16; 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_COUNT: 49862306a36Sopenharmony_ci data->operation.operation = 0x06; 49962306a36Sopenharmony_ci break; 50062306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_TRACK: 50162306a36Sopenharmony_ci data->operation.orientation = 0x1; 50262306a36Sopenharmony_ci data->operation.operation = 0x0C; 50362306a36Sopenharmony_ci data->extended_parameter_length = 0; 50462306a36Sopenharmony_ci data->sector = 0xFF; 50562306a36Sopenharmony_ci break; 50662306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_TRACK_DATA: 50762306a36Sopenharmony_ci data->auxiliary.length_valid = 0x1; 50862306a36Sopenharmony_ci data->length = tlf; 50962306a36Sopenharmony_ci data->operation.operation = 0x0C; 51062306a36Sopenharmony_ci break; 51162306a36Sopenharmony_ci case DASD_ECKD_CCW_ERASE: 51262306a36Sopenharmony_ci data->length = reclen; 51362306a36Sopenharmony_ci data->auxiliary.length_valid = 0x1; 51462306a36Sopenharmony_ci data->operation.operation = 0x0b; 51562306a36Sopenharmony_ci break; 51662306a36Sopenharmony_ci default: 51762306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_ERR, device, 51862306a36Sopenharmony_ci "fill LRE unknown opcode 0x%x", cmd); 51962306a36Sopenharmony_ci BUG(); 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci set_ch_t(&data->seek_addr, 52262306a36Sopenharmony_ci trk / private->rdc_data.trk_per_cyl, 52362306a36Sopenharmony_ci trk % private->rdc_data.trk_per_cyl); 52462306a36Sopenharmony_ci data->search_arg.cyl = data->seek_addr.cyl; 52562306a36Sopenharmony_ci data->search_arg.head = data->seek_addr.head; 52662306a36Sopenharmony_ci data->search_arg.record = rec_on_trk; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic int prefix_LRE(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, 53062306a36Sopenharmony_ci unsigned int trk, unsigned int totrk, int cmd, 53162306a36Sopenharmony_ci struct dasd_device *basedev, struct dasd_device *startdev, 53262306a36Sopenharmony_ci unsigned int format, unsigned int rec_on_trk, int count, 53362306a36Sopenharmony_ci unsigned int blksize, unsigned int tlf) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct dasd_eckd_private *basepriv, *startpriv; 53662306a36Sopenharmony_ci struct LRE_eckd_data *lredata; 53762306a36Sopenharmony_ci struct DE_eckd_data *dedata; 53862306a36Sopenharmony_ci int rc = 0; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci basepriv = basedev->private; 54162306a36Sopenharmony_ci startpriv = startdev->private; 54262306a36Sopenharmony_ci dedata = &pfxdata->define_extent; 54362306a36Sopenharmony_ci lredata = &pfxdata->locate_record; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PFX; 54662306a36Sopenharmony_ci ccw->flags = 0; 54762306a36Sopenharmony_ci if (cmd == DASD_ECKD_CCW_WRITE_FULL_TRACK) { 54862306a36Sopenharmony_ci ccw->count = sizeof(*pfxdata) + 2; 54962306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(pfxdata); 55062306a36Sopenharmony_ci memset(pfxdata, 0, sizeof(*pfxdata) + 2); 55162306a36Sopenharmony_ci } else { 55262306a36Sopenharmony_ci ccw->count = sizeof(*pfxdata); 55362306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(pfxdata); 55462306a36Sopenharmony_ci memset(pfxdata, 0, sizeof(*pfxdata)); 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* prefix data */ 55862306a36Sopenharmony_ci if (format > 1) { 55962306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_ERR, basedev, 56062306a36Sopenharmony_ci "PFX LRE unknown format 0x%x", format); 56162306a36Sopenharmony_ci BUG(); 56262306a36Sopenharmony_ci return -EINVAL; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci pfxdata->format = format; 56562306a36Sopenharmony_ci pfxdata->base_address = basepriv->conf.ned->unit_addr; 56662306a36Sopenharmony_ci pfxdata->base_lss = basepriv->conf.ned->ID; 56762306a36Sopenharmony_ci pfxdata->validity.define_extent = 1; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* private uid is kept up to date, conf_data may be outdated */ 57062306a36Sopenharmony_ci if (startpriv->uid.type == UA_BASE_PAV_ALIAS) 57162306a36Sopenharmony_ci pfxdata->validity.verify_base = 1; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (startpriv->uid.type == UA_HYPER_PAV_ALIAS) { 57462306a36Sopenharmony_ci pfxdata->validity.verify_base = 1; 57562306a36Sopenharmony_ci pfxdata->validity.hyper_pav = 1; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci rc = define_extent(NULL, dedata, trk, totrk, cmd, basedev, blksize); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* 58162306a36Sopenharmony_ci * For some commands the System Time Stamp is set in the define extent 58262306a36Sopenharmony_ci * data when XRC is supported. The validity of the time stamp must be 58362306a36Sopenharmony_ci * reflected in the prefix data as well. 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci if (dedata->ga_extended & 0x08 && dedata->ga_extended & 0x02) 58662306a36Sopenharmony_ci pfxdata->validity.time_stamp = 1; /* 'Time Stamp Valid' */ 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (format == 1) { 58962306a36Sopenharmony_ci locate_record_ext(NULL, lredata, trk, rec_on_trk, count, cmd, 59062306a36Sopenharmony_ci basedev, blksize, tlf); 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci return rc; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, 59762306a36Sopenharmony_ci unsigned int trk, unsigned int totrk, int cmd, 59862306a36Sopenharmony_ci struct dasd_device *basedev, struct dasd_device *startdev) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci return prefix_LRE(ccw, pfxdata, trk, totrk, cmd, basedev, startdev, 60162306a36Sopenharmony_ci 0, 0, 0, 0, 0); 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic void 60562306a36Sopenharmony_cilocate_record(struct ccw1 *ccw, struct LO_eckd_data *data, unsigned int trk, 60662306a36Sopenharmony_ci unsigned int rec_on_trk, int no_rec, int cmd, 60762306a36Sopenharmony_ci struct dasd_device * device, int reclen) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 61062306a36Sopenharmony_ci int sector; 61162306a36Sopenharmony_ci int dn, d; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_INFO, device, 61462306a36Sopenharmony_ci "Locate: trk %d, rec %d, no_rec %d, cmd %d, reclen %d", 61562306a36Sopenharmony_ci trk, rec_on_trk, no_rec, cmd, reclen); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD; 61862306a36Sopenharmony_ci ccw->flags = 0; 61962306a36Sopenharmony_ci ccw->count = 16; 62062306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(data); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci memset(data, 0, sizeof(struct LO_eckd_data)); 62362306a36Sopenharmony_ci sector = 0; 62462306a36Sopenharmony_ci if (rec_on_trk) { 62562306a36Sopenharmony_ci switch (private->rdc_data.dev_type) { 62662306a36Sopenharmony_ci case 0x3390: 62762306a36Sopenharmony_ci dn = ceil_quot(reclen + 6, 232); 62862306a36Sopenharmony_ci d = 9 + ceil_quot(reclen + 6 * (dn + 1), 34); 62962306a36Sopenharmony_ci sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8; 63062306a36Sopenharmony_ci break; 63162306a36Sopenharmony_ci case 0x3380: 63262306a36Sopenharmony_ci d = 7 + ceil_quot(reclen + 12, 32); 63362306a36Sopenharmony_ci sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7; 63462306a36Sopenharmony_ci break; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci data->sector = sector; 63862306a36Sopenharmony_ci data->count = no_rec; 63962306a36Sopenharmony_ci switch (cmd) { 64062306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: 64162306a36Sopenharmony_ci data->operation.orientation = 0x3; 64262306a36Sopenharmony_ci data->operation.operation = 0x03; 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_HOME_ADDRESS: 64562306a36Sopenharmony_ci data->operation.orientation = 0x3; 64662306a36Sopenharmony_ci data->operation.operation = 0x16; 64762306a36Sopenharmony_ci break; 64862306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_RECORD_ZERO: 64962306a36Sopenharmony_ci data->operation.orientation = 0x1; 65062306a36Sopenharmony_ci data->operation.operation = 0x03; 65162306a36Sopenharmony_ci data->count++; 65262306a36Sopenharmony_ci break; 65362306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_RECORD_ZERO: 65462306a36Sopenharmony_ci data->operation.orientation = 0x3; 65562306a36Sopenharmony_ci data->operation.operation = 0x16; 65662306a36Sopenharmony_ci data->count++; 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE: 65962306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_MT: 66062306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_KD: 66162306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_KD_MT: 66262306a36Sopenharmony_ci data->auxiliary.last_bytes_used = 0x1; 66362306a36Sopenharmony_ci data->length = reclen; 66462306a36Sopenharmony_ci data->operation.operation = 0x01; 66562306a36Sopenharmony_ci break; 66662306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_CKD: 66762306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_CKD_MT: 66862306a36Sopenharmony_ci data->auxiliary.last_bytes_used = 0x1; 66962306a36Sopenharmony_ci data->length = reclen; 67062306a36Sopenharmony_ci data->operation.operation = 0x03; 67162306a36Sopenharmony_ci break; 67262306a36Sopenharmony_ci case DASD_ECKD_CCW_READ: 67362306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_MT: 67462306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_KD: 67562306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_KD_MT: 67662306a36Sopenharmony_ci data->auxiliary.last_bytes_used = 0x1; 67762306a36Sopenharmony_ci data->length = reclen; 67862306a36Sopenharmony_ci data->operation.operation = 0x06; 67962306a36Sopenharmony_ci break; 68062306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_CKD: 68162306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_CKD_MT: 68262306a36Sopenharmony_ci data->auxiliary.last_bytes_used = 0x1; 68362306a36Sopenharmony_ci data->length = reclen; 68462306a36Sopenharmony_ci data->operation.operation = 0x16; 68562306a36Sopenharmony_ci break; 68662306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_COUNT: 68762306a36Sopenharmony_ci data->operation.operation = 0x06; 68862306a36Sopenharmony_ci break; 68962306a36Sopenharmony_ci case DASD_ECKD_CCW_ERASE: 69062306a36Sopenharmony_ci data->length = reclen; 69162306a36Sopenharmony_ci data->auxiliary.last_bytes_used = 0x1; 69262306a36Sopenharmony_ci data->operation.operation = 0x0b; 69362306a36Sopenharmony_ci break; 69462306a36Sopenharmony_ci default: 69562306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_ERR, device, "unknown locate record " 69662306a36Sopenharmony_ci "opcode 0x%x", cmd); 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci set_ch_t(&data->seek_addr, 69962306a36Sopenharmony_ci trk / private->rdc_data.trk_per_cyl, 70062306a36Sopenharmony_ci trk % private->rdc_data.trk_per_cyl); 70162306a36Sopenharmony_ci data->search_arg.cyl = data->seek_addr.cyl; 70262306a36Sopenharmony_ci data->search_arg.head = data->seek_addr.head; 70362306a36Sopenharmony_ci data->search_arg.record = rec_on_trk; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci/* 70762306a36Sopenharmony_ci * Returns 1 if the block is one of the special blocks that needs 70862306a36Sopenharmony_ci * to get read/written with the KD variant of the command. 70962306a36Sopenharmony_ci * That is DASD_ECKD_READ_KD_MT instead of DASD_ECKD_READ_MT and 71062306a36Sopenharmony_ci * DASD_ECKD_WRITE_KD_MT instead of DASD_ECKD_WRITE_MT. 71162306a36Sopenharmony_ci * Luckily the KD variants differ only by one bit (0x08) from the 71262306a36Sopenharmony_ci * normal variant. So don't wonder about code like: 71362306a36Sopenharmony_ci * if (dasd_eckd_cdl_special(blk_per_trk, recid)) 71462306a36Sopenharmony_ci * ccw->cmd_code |= 0x8; 71562306a36Sopenharmony_ci */ 71662306a36Sopenharmony_cistatic inline int 71762306a36Sopenharmony_cidasd_eckd_cdl_special(int blk_per_trk, int recid) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci if (recid < 3) 72062306a36Sopenharmony_ci return 1; 72162306a36Sopenharmony_ci if (recid < blk_per_trk) 72262306a36Sopenharmony_ci return 0; 72362306a36Sopenharmony_ci if (recid < 2 * blk_per_trk) 72462306a36Sopenharmony_ci return 1; 72562306a36Sopenharmony_ci return 0; 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci/* 72962306a36Sopenharmony_ci * Returns the record size for the special blocks of the cdl format. 73062306a36Sopenharmony_ci * Only returns something useful if dasd_eckd_cdl_special is true 73162306a36Sopenharmony_ci * for the recid. 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_cistatic inline int 73462306a36Sopenharmony_cidasd_eckd_cdl_reclen(int recid) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci if (recid < 3) 73762306a36Sopenharmony_ci return sizes_trk0[recid]; 73862306a36Sopenharmony_ci return LABEL_SIZE; 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci/* create unique id from private structure. */ 74162306a36Sopenharmony_cistatic void create_uid(struct dasd_conf *conf, struct dasd_uid *uid) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci int count; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci memset(uid, 0, sizeof(struct dasd_uid)); 74662306a36Sopenharmony_ci memcpy(uid->vendor, conf->ned->HDA_manufacturer, 74762306a36Sopenharmony_ci sizeof(uid->vendor) - 1); 74862306a36Sopenharmony_ci EBCASC(uid->vendor, sizeof(uid->vendor) - 1); 74962306a36Sopenharmony_ci memcpy(uid->serial, &conf->ned->serial, 75062306a36Sopenharmony_ci sizeof(uid->serial) - 1); 75162306a36Sopenharmony_ci EBCASC(uid->serial, sizeof(uid->serial) - 1); 75262306a36Sopenharmony_ci uid->ssid = conf->gneq->subsystemID; 75362306a36Sopenharmony_ci uid->real_unit_addr = conf->ned->unit_addr; 75462306a36Sopenharmony_ci if (conf->sneq) { 75562306a36Sopenharmony_ci uid->type = conf->sneq->sua_flags; 75662306a36Sopenharmony_ci if (uid->type == UA_BASE_PAV_ALIAS) 75762306a36Sopenharmony_ci uid->base_unit_addr = conf->sneq->base_unit_addr; 75862306a36Sopenharmony_ci } else { 75962306a36Sopenharmony_ci uid->type = UA_BASE_DEVICE; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci if (conf->vdsneq) { 76262306a36Sopenharmony_ci for (count = 0; count < 16; count++) { 76362306a36Sopenharmony_ci sprintf(uid->vduit+2*count, "%02x", 76462306a36Sopenharmony_ci conf->vdsneq->uit[count]); 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci/* 77062306a36Sopenharmony_ci * Generate device unique id that specifies the physical device. 77162306a36Sopenharmony_ci */ 77262306a36Sopenharmony_cistatic int dasd_eckd_generate_uid(struct dasd_device *device) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 77562306a36Sopenharmony_ci unsigned long flags; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (!private) 77862306a36Sopenharmony_ci return -ENODEV; 77962306a36Sopenharmony_ci if (!private->conf.ned || !private->conf.gneq) 78062306a36Sopenharmony_ci return -ENODEV; 78162306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 78262306a36Sopenharmony_ci create_uid(&private->conf, &private->uid); 78362306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 78462306a36Sopenharmony_ci return 0; 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic int dasd_eckd_get_uid(struct dasd_device *device, struct dasd_uid *uid) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 79062306a36Sopenharmony_ci unsigned long flags; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (private) { 79362306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 79462306a36Sopenharmony_ci *uid = private->uid; 79562306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 79662306a36Sopenharmony_ci return 0; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci return -EINVAL; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci/* 80262306a36Sopenharmony_ci * compare device UID with data of a given dasd_eckd_private structure 80362306a36Sopenharmony_ci * return 0 for match 80462306a36Sopenharmony_ci */ 80562306a36Sopenharmony_cistatic int dasd_eckd_compare_path_uid(struct dasd_device *device, 80662306a36Sopenharmony_ci struct dasd_conf *path_conf) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci struct dasd_uid device_uid; 80962306a36Sopenharmony_ci struct dasd_uid path_uid; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci create_uid(path_conf, &path_uid); 81262306a36Sopenharmony_ci dasd_eckd_get_uid(device, &device_uid); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci return memcmp(&device_uid, &path_uid, sizeof(struct dasd_uid)); 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistatic void dasd_eckd_fill_rcd_cqr(struct dasd_device *device, 81862306a36Sopenharmony_ci struct dasd_ccw_req *cqr, 81962306a36Sopenharmony_ci __u8 *rcd_buffer, 82062306a36Sopenharmony_ci __u8 lpm) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci struct ccw1 *ccw; 82362306a36Sopenharmony_ci /* 82462306a36Sopenharmony_ci * buffer has to start with EBCDIC "V1.0" to show 82562306a36Sopenharmony_ci * support for virtual device SNEQ 82662306a36Sopenharmony_ci */ 82762306a36Sopenharmony_ci rcd_buffer[0] = 0xE5; 82862306a36Sopenharmony_ci rcd_buffer[1] = 0xF1; 82962306a36Sopenharmony_ci rcd_buffer[2] = 0x4B; 83062306a36Sopenharmony_ci rcd_buffer[3] = 0xF0; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci ccw = cqr->cpaddr; 83362306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RCD; 83462306a36Sopenharmony_ci ccw->flags = 0; 83562306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(rcd_buffer); 83662306a36Sopenharmony_ci ccw->count = DASD_ECKD_RCD_DATA_SIZE; 83762306a36Sopenharmony_ci cqr->magic = DASD_ECKD_MAGIC; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci cqr->startdev = device; 84062306a36Sopenharmony_ci cqr->memdev = device; 84162306a36Sopenharmony_ci cqr->block = NULL; 84262306a36Sopenharmony_ci cqr->expires = 10*HZ; 84362306a36Sopenharmony_ci cqr->lpm = lpm; 84462306a36Sopenharmony_ci cqr->retries = 256; 84562306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 84662306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 84762306a36Sopenharmony_ci set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags); 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci/* 85162306a36Sopenharmony_ci * Wakeup helper for read_conf 85262306a36Sopenharmony_ci * if the cqr is not done and needs some error recovery 85362306a36Sopenharmony_ci * the buffer has to be re-initialized with the EBCDIC "V1.0" 85462306a36Sopenharmony_ci * to show support for virtual device SNEQ 85562306a36Sopenharmony_ci */ 85662306a36Sopenharmony_cistatic void read_conf_cb(struct dasd_ccw_req *cqr, void *data) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci struct ccw1 *ccw; 85962306a36Sopenharmony_ci __u8 *rcd_buffer; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (cqr->status != DASD_CQR_DONE) { 86262306a36Sopenharmony_ci ccw = cqr->cpaddr; 86362306a36Sopenharmony_ci rcd_buffer = phys_to_virt(ccw->cda); 86462306a36Sopenharmony_ci memset(rcd_buffer, 0, sizeof(*rcd_buffer)); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci rcd_buffer[0] = 0xE5; 86762306a36Sopenharmony_ci rcd_buffer[1] = 0xF1; 86862306a36Sopenharmony_ci rcd_buffer[2] = 0x4B; 86962306a36Sopenharmony_ci rcd_buffer[3] = 0xF0; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci dasd_wakeup_cb(cqr, data); 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic int dasd_eckd_read_conf_immediately(struct dasd_device *device, 87562306a36Sopenharmony_ci struct dasd_ccw_req *cqr, 87662306a36Sopenharmony_ci __u8 *rcd_buffer, 87762306a36Sopenharmony_ci __u8 lpm) 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci struct ciw *ciw; 88062306a36Sopenharmony_ci int rc; 88162306a36Sopenharmony_ci /* 88262306a36Sopenharmony_ci * sanity check: scan for RCD command in extended SenseID data 88362306a36Sopenharmony_ci * some devices do not support RCD 88462306a36Sopenharmony_ci */ 88562306a36Sopenharmony_ci ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD); 88662306a36Sopenharmony_ci if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD) 88762306a36Sopenharmony_ci return -EOPNOTSUPP; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buffer, lpm); 89062306a36Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); 89162306a36Sopenharmony_ci set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags); 89262306a36Sopenharmony_ci cqr->retries = 5; 89362306a36Sopenharmony_ci cqr->callback = read_conf_cb; 89462306a36Sopenharmony_ci rc = dasd_sleep_on_immediatly(cqr); 89562306a36Sopenharmony_ci return rc; 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic int dasd_eckd_read_conf_lpm(struct dasd_device *device, 89962306a36Sopenharmony_ci void **rcd_buffer, 90062306a36Sopenharmony_ci int *rcd_buffer_size, __u8 lpm) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci struct ciw *ciw; 90362306a36Sopenharmony_ci char *rcd_buf = NULL; 90462306a36Sopenharmony_ci int ret; 90562306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci /* 90862306a36Sopenharmony_ci * sanity check: scan for RCD command in extended SenseID data 90962306a36Sopenharmony_ci * some devices do not support RCD 91062306a36Sopenharmony_ci */ 91162306a36Sopenharmony_ci ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD); 91262306a36Sopenharmony_ci if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD) { 91362306a36Sopenharmony_ci ret = -EOPNOTSUPP; 91462306a36Sopenharmony_ci goto out_error; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci rcd_buf = kzalloc(DASD_ECKD_RCD_DATA_SIZE, GFP_KERNEL | GFP_DMA); 91762306a36Sopenharmony_ci if (!rcd_buf) { 91862306a36Sopenharmony_ci ret = -ENOMEM; 91962306a36Sopenharmony_ci goto out_error; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */, 92262306a36Sopenharmony_ci 0, /* use rcd_buf as data ara */ 92362306a36Sopenharmony_ci device, NULL); 92462306a36Sopenharmony_ci if (IS_ERR(cqr)) { 92562306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 92662306a36Sopenharmony_ci "Could not allocate RCD request"); 92762306a36Sopenharmony_ci ret = -ENOMEM; 92862306a36Sopenharmony_ci goto out_error; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buf, lpm); 93162306a36Sopenharmony_ci cqr->callback = read_conf_cb; 93262306a36Sopenharmony_ci ret = dasd_sleep_on(cqr); 93362306a36Sopenharmony_ci /* 93462306a36Sopenharmony_ci * on success we update the user input parms 93562306a36Sopenharmony_ci */ 93662306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 93762306a36Sopenharmony_ci if (ret) 93862306a36Sopenharmony_ci goto out_error; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci *rcd_buffer_size = DASD_ECKD_RCD_DATA_SIZE; 94162306a36Sopenharmony_ci *rcd_buffer = rcd_buf; 94262306a36Sopenharmony_ci return 0; 94362306a36Sopenharmony_ciout_error: 94462306a36Sopenharmony_ci kfree(rcd_buf); 94562306a36Sopenharmony_ci *rcd_buffer = NULL; 94662306a36Sopenharmony_ci *rcd_buffer_size = 0; 94762306a36Sopenharmony_ci return ret; 94862306a36Sopenharmony_ci} 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_cistatic int dasd_eckd_identify_conf_parts(struct dasd_conf *conf) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci struct dasd_sneq *sneq; 95462306a36Sopenharmony_ci int i, count; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci conf->ned = NULL; 95762306a36Sopenharmony_ci conf->sneq = NULL; 95862306a36Sopenharmony_ci conf->vdsneq = NULL; 95962306a36Sopenharmony_ci conf->gneq = NULL; 96062306a36Sopenharmony_ci count = conf->len / sizeof(struct dasd_sneq); 96162306a36Sopenharmony_ci sneq = (struct dasd_sneq *)conf->data; 96262306a36Sopenharmony_ci for (i = 0; i < count; ++i) { 96362306a36Sopenharmony_ci if (sneq->flags.identifier == 1 && sneq->format == 1) 96462306a36Sopenharmony_ci conf->sneq = sneq; 96562306a36Sopenharmony_ci else if (sneq->flags.identifier == 1 && sneq->format == 4) 96662306a36Sopenharmony_ci conf->vdsneq = (struct vd_sneq *)sneq; 96762306a36Sopenharmony_ci else if (sneq->flags.identifier == 2) 96862306a36Sopenharmony_ci conf->gneq = (struct dasd_gneq *)sneq; 96962306a36Sopenharmony_ci else if (sneq->flags.identifier == 3 && sneq->res1 == 1) 97062306a36Sopenharmony_ci conf->ned = (struct dasd_ned *)sneq; 97162306a36Sopenharmony_ci sneq++; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci if (!conf->ned || !conf->gneq) { 97462306a36Sopenharmony_ci conf->ned = NULL; 97562306a36Sopenharmony_ci conf->sneq = NULL; 97662306a36Sopenharmony_ci conf->vdsneq = NULL; 97762306a36Sopenharmony_ci conf->gneq = NULL; 97862306a36Sopenharmony_ci return -EINVAL; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci return 0; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci}; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic unsigned char dasd_eckd_path_access(void *conf_data, int conf_len) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci struct dasd_gneq *gneq; 98762306a36Sopenharmony_ci int i, count, found; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci count = conf_len / sizeof(*gneq); 99062306a36Sopenharmony_ci gneq = (struct dasd_gneq *)conf_data; 99162306a36Sopenharmony_ci found = 0; 99262306a36Sopenharmony_ci for (i = 0; i < count; ++i) { 99362306a36Sopenharmony_ci if (gneq->flags.identifier == 2) { 99462306a36Sopenharmony_ci found = 1; 99562306a36Sopenharmony_ci break; 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci gneq++; 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci if (found) 100062306a36Sopenharmony_ci return ((char *)gneq)[18] & 0x07; 100162306a36Sopenharmony_ci else 100262306a36Sopenharmony_ci return 0; 100362306a36Sopenharmony_ci} 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_cistatic void dasd_eckd_store_conf_data(struct dasd_device *device, 100662306a36Sopenharmony_ci struct dasd_conf_data *conf_data, int chp) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 100962306a36Sopenharmony_ci struct channel_path_desc_fmt0 *chp_desc; 101062306a36Sopenharmony_ci struct subchannel_id sch_id; 101162306a36Sopenharmony_ci void *cdp; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci /* 101462306a36Sopenharmony_ci * path handling and read_conf allocate data 101562306a36Sopenharmony_ci * free it before replacing the pointer 101662306a36Sopenharmony_ci * also replace the old private->conf_data pointer 101762306a36Sopenharmony_ci * with the new one if this points to the same data 101862306a36Sopenharmony_ci */ 101962306a36Sopenharmony_ci cdp = device->path[chp].conf_data; 102062306a36Sopenharmony_ci if (private->conf.data == cdp) { 102162306a36Sopenharmony_ci private->conf.data = (void *)conf_data; 102262306a36Sopenharmony_ci dasd_eckd_identify_conf_parts(&private->conf); 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci ccw_device_get_schid(device->cdev, &sch_id); 102562306a36Sopenharmony_ci device->path[chp].conf_data = conf_data; 102662306a36Sopenharmony_ci device->path[chp].cssid = sch_id.cssid; 102762306a36Sopenharmony_ci device->path[chp].ssid = sch_id.ssid; 102862306a36Sopenharmony_ci chp_desc = ccw_device_get_chp_desc(device->cdev, chp); 102962306a36Sopenharmony_ci if (chp_desc) 103062306a36Sopenharmony_ci device->path[chp].chpid = chp_desc->chpid; 103162306a36Sopenharmony_ci kfree(chp_desc); 103262306a36Sopenharmony_ci kfree(cdp); 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_cistatic void dasd_eckd_clear_conf_data(struct dasd_device *device) 103662306a36Sopenharmony_ci{ 103762306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 103862306a36Sopenharmony_ci int i; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci private->conf.data = NULL; 104162306a36Sopenharmony_ci private->conf.len = 0; 104262306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 104362306a36Sopenharmony_ci kfree(device->path[i].conf_data); 104462306a36Sopenharmony_ci device->path[i].conf_data = NULL; 104562306a36Sopenharmony_ci device->path[i].cssid = 0; 104662306a36Sopenharmony_ci device->path[i].ssid = 0; 104762306a36Sopenharmony_ci device->path[i].chpid = 0; 104862306a36Sopenharmony_ci dasd_path_notoper(device, i); 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cistatic void dasd_eckd_read_fc_security(struct dasd_device *device) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 105562306a36Sopenharmony_ci u8 esm_valid; 105662306a36Sopenharmony_ci u8 esm[8]; 105762306a36Sopenharmony_ci int chp; 105862306a36Sopenharmony_ci int rc; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci rc = chsc_scud(private->uid.ssid, (u64 *)esm, &esm_valid); 106162306a36Sopenharmony_ci if (rc) { 106262306a36Sopenharmony_ci for (chp = 0; chp < 8; chp++) 106362306a36Sopenharmony_ci device->path[chp].fc_security = 0; 106462306a36Sopenharmony_ci return; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci for (chp = 0; chp < 8; chp++) { 106862306a36Sopenharmony_ci if (esm_valid & (0x80 >> chp)) 106962306a36Sopenharmony_ci device->path[chp].fc_security = esm[chp]; 107062306a36Sopenharmony_ci else 107162306a36Sopenharmony_ci device->path[chp].fc_security = 0; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_cistatic void dasd_eckd_get_uid_string(struct dasd_conf *conf, 107662306a36Sopenharmony_ci char *print_uid) 107762306a36Sopenharmony_ci{ 107862306a36Sopenharmony_ci struct dasd_uid uid; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci create_uid(conf, &uid); 108162306a36Sopenharmony_ci if (strlen(uid.vduit) > 0) 108262306a36Sopenharmony_ci snprintf(print_uid, DASD_UID_STRLEN, 108362306a36Sopenharmony_ci "%s.%s.%04x.%02x.%s", 108462306a36Sopenharmony_ci uid.vendor, uid.serial, uid.ssid, 108562306a36Sopenharmony_ci uid.real_unit_addr, uid.vduit); 108662306a36Sopenharmony_ci else 108762306a36Sopenharmony_ci snprintf(print_uid, DASD_UID_STRLEN, 108862306a36Sopenharmony_ci "%s.%s.%04x.%02x", 108962306a36Sopenharmony_ci uid.vendor, uid.serial, uid.ssid, 109062306a36Sopenharmony_ci uid.real_unit_addr); 109162306a36Sopenharmony_ci} 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic int dasd_eckd_check_cabling(struct dasd_device *device, 109462306a36Sopenharmony_ci void *conf_data, __u8 lpm) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci char print_path_uid[DASD_UID_STRLEN], print_device_uid[DASD_UID_STRLEN]; 109762306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 109862306a36Sopenharmony_ci struct dasd_conf path_conf; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci path_conf.data = conf_data; 110162306a36Sopenharmony_ci path_conf.len = DASD_ECKD_RCD_DATA_SIZE; 110262306a36Sopenharmony_ci if (dasd_eckd_identify_conf_parts(&path_conf)) 110362306a36Sopenharmony_ci return 1; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci if (dasd_eckd_compare_path_uid(device, &path_conf)) { 110662306a36Sopenharmony_ci dasd_eckd_get_uid_string(&path_conf, print_path_uid); 110762306a36Sopenharmony_ci dasd_eckd_get_uid_string(&private->conf, print_device_uid); 110862306a36Sopenharmony_ci dev_err(&device->cdev->dev, 110962306a36Sopenharmony_ci "Not all channel paths lead to the same device, path %02X leads to device %s instead of %s\n", 111062306a36Sopenharmony_ci lpm, print_path_uid, print_device_uid); 111162306a36Sopenharmony_ci return 1; 111262306a36Sopenharmony_ci } 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci return 0; 111562306a36Sopenharmony_ci} 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_cistatic int dasd_eckd_read_conf(struct dasd_device *device) 111862306a36Sopenharmony_ci{ 111962306a36Sopenharmony_ci void *conf_data; 112062306a36Sopenharmony_ci int conf_len, conf_data_saved; 112162306a36Sopenharmony_ci int rc, path_err, pos; 112262306a36Sopenharmony_ci __u8 lpm, opm; 112362306a36Sopenharmony_ci struct dasd_eckd_private *private; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci private = device->private; 112662306a36Sopenharmony_ci opm = ccw_device_get_path_mask(device->cdev); 112762306a36Sopenharmony_ci conf_data_saved = 0; 112862306a36Sopenharmony_ci path_err = 0; 112962306a36Sopenharmony_ci /* get configuration data per operational path */ 113062306a36Sopenharmony_ci for (lpm = 0x80; lpm; lpm>>= 1) { 113162306a36Sopenharmony_ci if (!(lpm & opm)) 113262306a36Sopenharmony_ci continue; 113362306a36Sopenharmony_ci rc = dasd_eckd_read_conf_lpm(device, &conf_data, 113462306a36Sopenharmony_ci &conf_len, lpm); 113562306a36Sopenharmony_ci if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ 113662306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 113762306a36Sopenharmony_ci "Read configuration data returned " 113862306a36Sopenharmony_ci "error %d", rc); 113962306a36Sopenharmony_ci return rc; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci if (conf_data == NULL) { 114262306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 114362306a36Sopenharmony_ci "No configuration data " 114462306a36Sopenharmony_ci "retrieved"); 114562306a36Sopenharmony_ci /* no further analysis possible */ 114662306a36Sopenharmony_ci dasd_path_add_opm(device, opm); 114762306a36Sopenharmony_ci continue; /* no error */ 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci /* save first valid configuration data */ 115062306a36Sopenharmony_ci if (!conf_data_saved) { 115162306a36Sopenharmony_ci /* initially clear previously stored conf_data */ 115262306a36Sopenharmony_ci dasd_eckd_clear_conf_data(device); 115362306a36Sopenharmony_ci private->conf.data = conf_data; 115462306a36Sopenharmony_ci private->conf.len = conf_len; 115562306a36Sopenharmony_ci if (dasd_eckd_identify_conf_parts(&private->conf)) { 115662306a36Sopenharmony_ci private->conf.data = NULL; 115762306a36Sopenharmony_ci private->conf.len = 0; 115862306a36Sopenharmony_ci kfree(conf_data); 115962306a36Sopenharmony_ci continue; 116062306a36Sopenharmony_ci } 116162306a36Sopenharmony_ci /* 116262306a36Sopenharmony_ci * build device UID that other path data 116362306a36Sopenharmony_ci * can be compared to it 116462306a36Sopenharmony_ci */ 116562306a36Sopenharmony_ci dasd_eckd_generate_uid(device); 116662306a36Sopenharmony_ci conf_data_saved++; 116762306a36Sopenharmony_ci } else if (dasd_eckd_check_cabling(device, conf_data, lpm)) { 116862306a36Sopenharmony_ci dasd_path_add_cablepm(device, lpm); 116962306a36Sopenharmony_ci path_err = -EINVAL; 117062306a36Sopenharmony_ci kfree(conf_data); 117162306a36Sopenharmony_ci continue; 117262306a36Sopenharmony_ci } 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci pos = pathmask_to_pos(lpm); 117562306a36Sopenharmony_ci dasd_eckd_store_conf_data(device, conf_data, pos); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci switch (dasd_eckd_path_access(conf_data, conf_len)) { 117862306a36Sopenharmony_ci case 0x02: 117962306a36Sopenharmony_ci dasd_path_add_nppm(device, lpm); 118062306a36Sopenharmony_ci break; 118162306a36Sopenharmony_ci case 0x03: 118262306a36Sopenharmony_ci dasd_path_add_ppm(device, lpm); 118362306a36Sopenharmony_ci break; 118462306a36Sopenharmony_ci } 118562306a36Sopenharmony_ci if (!dasd_path_get_opm(device)) { 118662306a36Sopenharmony_ci dasd_path_set_opm(device, lpm); 118762306a36Sopenharmony_ci dasd_generic_path_operational(device); 118862306a36Sopenharmony_ci } else { 118962306a36Sopenharmony_ci dasd_path_add_opm(device, lpm); 119062306a36Sopenharmony_ci } 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci return path_err; 119462306a36Sopenharmony_ci} 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_cistatic u32 get_fcx_max_data(struct dasd_device *device) 119762306a36Sopenharmony_ci{ 119862306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 119962306a36Sopenharmony_ci int fcx_in_css, fcx_in_gneq, fcx_in_features; 120062306a36Sopenharmony_ci unsigned int mdc; 120162306a36Sopenharmony_ci int tpm; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci if (dasd_nofcx) 120462306a36Sopenharmony_ci return 0; 120562306a36Sopenharmony_ci /* is transport mode supported? */ 120662306a36Sopenharmony_ci fcx_in_css = css_general_characteristics.fcx; 120762306a36Sopenharmony_ci fcx_in_gneq = private->conf.gneq->reserved2[7] & 0x04; 120862306a36Sopenharmony_ci fcx_in_features = private->features.feature[40] & 0x80; 120962306a36Sopenharmony_ci tpm = fcx_in_css && fcx_in_gneq && fcx_in_features; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci if (!tpm) 121262306a36Sopenharmony_ci return 0; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci mdc = ccw_device_get_mdc(device->cdev, 0); 121562306a36Sopenharmony_ci if (mdc == 0) { 121662306a36Sopenharmony_ci dev_warn(&device->cdev->dev, "Detecting the maximum supported data size for zHPF requests failed\n"); 121762306a36Sopenharmony_ci return 0; 121862306a36Sopenharmony_ci } else { 121962306a36Sopenharmony_ci return (u32)mdc * FCX_MAX_DATA_FACTOR; 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci} 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_cistatic int verify_fcx_max_data(struct dasd_device *device, __u8 lpm) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 122662306a36Sopenharmony_ci unsigned int mdc; 122762306a36Sopenharmony_ci u32 fcx_max_data; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci if (private->fcx_max_data) { 123062306a36Sopenharmony_ci mdc = ccw_device_get_mdc(device->cdev, lpm); 123162306a36Sopenharmony_ci if (mdc == 0) { 123262306a36Sopenharmony_ci dev_warn(&device->cdev->dev, 123362306a36Sopenharmony_ci "Detecting the maximum data size for zHPF " 123462306a36Sopenharmony_ci "requests failed (rc=%d) for a new path %x\n", 123562306a36Sopenharmony_ci mdc, lpm); 123662306a36Sopenharmony_ci return mdc; 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci fcx_max_data = (u32)mdc * FCX_MAX_DATA_FACTOR; 123962306a36Sopenharmony_ci if (fcx_max_data < private->fcx_max_data) { 124062306a36Sopenharmony_ci dev_warn(&device->cdev->dev, 124162306a36Sopenharmony_ci "The maximum data size for zHPF requests %u " 124262306a36Sopenharmony_ci "on a new path %x is below the active maximum " 124362306a36Sopenharmony_ci "%u\n", fcx_max_data, lpm, 124462306a36Sopenharmony_ci private->fcx_max_data); 124562306a36Sopenharmony_ci return -EACCES; 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci } 124862306a36Sopenharmony_ci return 0; 124962306a36Sopenharmony_ci} 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_cistatic int rebuild_device_uid(struct dasd_device *device, 125262306a36Sopenharmony_ci struct pe_handler_work_data *data) 125362306a36Sopenharmony_ci{ 125462306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 125562306a36Sopenharmony_ci __u8 lpm, opm = dasd_path_get_opm(device); 125662306a36Sopenharmony_ci int rc = -ENODEV; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci for (lpm = 0x80; lpm; lpm >>= 1) { 125962306a36Sopenharmony_ci if (!(lpm & opm)) 126062306a36Sopenharmony_ci continue; 126162306a36Sopenharmony_ci memset(&data->rcd_buffer, 0, sizeof(data->rcd_buffer)); 126262306a36Sopenharmony_ci memset(&data->cqr, 0, sizeof(data->cqr)); 126362306a36Sopenharmony_ci data->cqr.cpaddr = &data->ccw; 126462306a36Sopenharmony_ci rc = dasd_eckd_read_conf_immediately(device, &data->cqr, 126562306a36Sopenharmony_ci data->rcd_buffer, 126662306a36Sopenharmony_ci lpm); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci if (rc) { 126962306a36Sopenharmony_ci if (rc == -EOPNOTSUPP) /* -EOPNOTSUPP is ok */ 127062306a36Sopenharmony_ci continue; 127162306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 127262306a36Sopenharmony_ci "Read configuration data " 127362306a36Sopenharmony_ci "returned error %d", rc); 127462306a36Sopenharmony_ci break; 127562306a36Sopenharmony_ci } 127662306a36Sopenharmony_ci memcpy(private->conf.data, data->rcd_buffer, 127762306a36Sopenharmony_ci DASD_ECKD_RCD_DATA_SIZE); 127862306a36Sopenharmony_ci if (dasd_eckd_identify_conf_parts(&private->conf)) { 127962306a36Sopenharmony_ci rc = -ENODEV; 128062306a36Sopenharmony_ci } else /* first valid path is enough */ 128162306a36Sopenharmony_ci break; 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci if (!rc) 128562306a36Sopenharmony_ci rc = dasd_eckd_generate_uid(device); 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci return rc; 128862306a36Sopenharmony_ci} 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_cistatic void dasd_eckd_path_available_action(struct dasd_device *device, 129162306a36Sopenharmony_ci struct pe_handler_work_data *data) 129262306a36Sopenharmony_ci{ 129362306a36Sopenharmony_ci __u8 path_rcd_buf[DASD_ECKD_RCD_DATA_SIZE]; 129462306a36Sopenharmony_ci __u8 lpm, opm, npm, ppm, epm, hpfpm, cablepm; 129562306a36Sopenharmony_ci struct dasd_conf_data *conf_data; 129662306a36Sopenharmony_ci char print_uid[DASD_UID_STRLEN]; 129762306a36Sopenharmony_ci struct dasd_conf path_conf; 129862306a36Sopenharmony_ci unsigned long flags; 129962306a36Sopenharmony_ci int rc, pos; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci opm = 0; 130262306a36Sopenharmony_ci npm = 0; 130362306a36Sopenharmony_ci ppm = 0; 130462306a36Sopenharmony_ci epm = 0; 130562306a36Sopenharmony_ci hpfpm = 0; 130662306a36Sopenharmony_ci cablepm = 0; 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci for (lpm = 0x80; lpm; lpm >>= 1) { 130962306a36Sopenharmony_ci if (!(lpm & data->tbvpm)) 131062306a36Sopenharmony_ci continue; 131162306a36Sopenharmony_ci memset(&data->rcd_buffer, 0, sizeof(data->rcd_buffer)); 131262306a36Sopenharmony_ci memset(&data->cqr, 0, sizeof(data->cqr)); 131362306a36Sopenharmony_ci data->cqr.cpaddr = &data->ccw; 131462306a36Sopenharmony_ci rc = dasd_eckd_read_conf_immediately(device, &data->cqr, 131562306a36Sopenharmony_ci data->rcd_buffer, 131662306a36Sopenharmony_ci lpm); 131762306a36Sopenharmony_ci if (!rc) { 131862306a36Sopenharmony_ci switch (dasd_eckd_path_access(data->rcd_buffer, 131962306a36Sopenharmony_ci DASD_ECKD_RCD_DATA_SIZE) 132062306a36Sopenharmony_ci ) { 132162306a36Sopenharmony_ci case 0x02: 132262306a36Sopenharmony_ci npm |= lpm; 132362306a36Sopenharmony_ci break; 132462306a36Sopenharmony_ci case 0x03: 132562306a36Sopenharmony_ci ppm |= lpm; 132662306a36Sopenharmony_ci break; 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci opm |= lpm; 132962306a36Sopenharmony_ci } else if (rc == -EOPNOTSUPP) { 133062306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 133162306a36Sopenharmony_ci "path verification: No configuration " 133262306a36Sopenharmony_ci "data retrieved"); 133362306a36Sopenharmony_ci opm |= lpm; 133462306a36Sopenharmony_ci } else if (rc == -EAGAIN) { 133562306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 133662306a36Sopenharmony_ci "path verification: device is stopped," 133762306a36Sopenharmony_ci " try again later"); 133862306a36Sopenharmony_ci epm |= lpm; 133962306a36Sopenharmony_ci } else { 134062306a36Sopenharmony_ci dev_warn(&device->cdev->dev, 134162306a36Sopenharmony_ci "Reading device feature codes failed " 134262306a36Sopenharmony_ci "(rc=%d) for new path %x\n", rc, lpm); 134362306a36Sopenharmony_ci continue; 134462306a36Sopenharmony_ci } 134562306a36Sopenharmony_ci if (verify_fcx_max_data(device, lpm)) { 134662306a36Sopenharmony_ci opm &= ~lpm; 134762306a36Sopenharmony_ci npm &= ~lpm; 134862306a36Sopenharmony_ci ppm &= ~lpm; 134962306a36Sopenharmony_ci hpfpm |= lpm; 135062306a36Sopenharmony_ci continue; 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci /* 135462306a36Sopenharmony_ci * save conf_data for comparison after 135562306a36Sopenharmony_ci * rebuild_device_uid may have changed 135662306a36Sopenharmony_ci * the original data 135762306a36Sopenharmony_ci */ 135862306a36Sopenharmony_ci memcpy(&path_rcd_buf, data->rcd_buffer, 135962306a36Sopenharmony_ci DASD_ECKD_RCD_DATA_SIZE); 136062306a36Sopenharmony_ci path_conf.data = (void *)&path_rcd_buf; 136162306a36Sopenharmony_ci path_conf.len = DASD_ECKD_RCD_DATA_SIZE; 136262306a36Sopenharmony_ci if (dasd_eckd_identify_conf_parts(&path_conf)) { 136362306a36Sopenharmony_ci path_conf.data = NULL; 136462306a36Sopenharmony_ci path_conf.len = 0; 136562306a36Sopenharmony_ci continue; 136662306a36Sopenharmony_ci } 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci /* 136962306a36Sopenharmony_ci * compare path UID with device UID only if at least 137062306a36Sopenharmony_ci * one valid path is left 137162306a36Sopenharmony_ci * in other case the device UID may have changed and 137262306a36Sopenharmony_ci * the first working path UID will be used as device UID 137362306a36Sopenharmony_ci */ 137462306a36Sopenharmony_ci if (dasd_path_get_opm(device) && 137562306a36Sopenharmony_ci dasd_eckd_compare_path_uid(device, &path_conf)) { 137662306a36Sopenharmony_ci /* 137762306a36Sopenharmony_ci * the comparison was not successful 137862306a36Sopenharmony_ci * rebuild the device UID with at least one 137962306a36Sopenharmony_ci * known path in case a z/VM hyperswap command 138062306a36Sopenharmony_ci * has changed the device 138162306a36Sopenharmony_ci * 138262306a36Sopenharmony_ci * after this compare again 138362306a36Sopenharmony_ci * 138462306a36Sopenharmony_ci * if either the rebuild or the recompare fails 138562306a36Sopenharmony_ci * the path can not be used 138662306a36Sopenharmony_ci */ 138762306a36Sopenharmony_ci if (rebuild_device_uid(device, data) || 138862306a36Sopenharmony_ci dasd_eckd_compare_path_uid( 138962306a36Sopenharmony_ci device, &path_conf)) { 139062306a36Sopenharmony_ci dasd_eckd_get_uid_string(&path_conf, print_uid); 139162306a36Sopenharmony_ci dev_err(&device->cdev->dev, 139262306a36Sopenharmony_ci "The newly added channel path %02X " 139362306a36Sopenharmony_ci "will not be used because it leads " 139462306a36Sopenharmony_ci "to a different device %s\n", 139562306a36Sopenharmony_ci lpm, print_uid); 139662306a36Sopenharmony_ci opm &= ~lpm; 139762306a36Sopenharmony_ci npm &= ~lpm; 139862306a36Sopenharmony_ci ppm &= ~lpm; 139962306a36Sopenharmony_ci cablepm |= lpm; 140062306a36Sopenharmony_ci continue; 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci } 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci conf_data = kzalloc(DASD_ECKD_RCD_DATA_SIZE, GFP_KERNEL); 140562306a36Sopenharmony_ci if (conf_data) { 140662306a36Sopenharmony_ci memcpy(conf_data, data->rcd_buffer, 140762306a36Sopenharmony_ci DASD_ECKD_RCD_DATA_SIZE); 140862306a36Sopenharmony_ci } else { 140962306a36Sopenharmony_ci /* 141062306a36Sopenharmony_ci * path is operational but path config data could not 141162306a36Sopenharmony_ci * be stored due to low mem condition 141262306a36Sopenharmony_ci * add it to the error path mask and schedule a path 141362306a36Sopenharmony_ci * verification later that this could be added again 141462306a36Sopenharmony_ci */ 141562306a36Sopenharmony_ci epm |= lpm; 141662306a36Sopenharmony_ci } 141762306a36Sopenharmony_ci pos = pathmask_to_pos(lpm); 141862306a36Sopenharmony_ci dasd_eckd_store_conf_data(device, conf_data, pos); 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci /* 142162306a36Sopenharmony_ci * There is a small chance that a path is lost again between 142262306a36Sopenharmony_ci * above path verification and the following modification of 142362306a36Sopenharmony_ci * the device opm mask. We could avoid that race here by using 142462306a36Sopenharmony_ci * yet another path mask, but we rather deal with this unlikely 142562306a36Sopenharmony_ci * situation in dasd_start_IO. 142662306a36Sopenharmony_ci */ 142762306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 142862306a36Sopenharmony_ci if (!dasd_path_get_opm(device) && opm) { 142962306a36Sopenharmony_ci dasd_path_set_opm(device, opm); 143062306a36Sopenharmony_ci dasd_generic_path_operational(device); 143162306a36Sopenharmony_ci } else { 143262306a36Sopenharmony_ci dasd_path_add_opm(device, opm); 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci dasd_path_add_nppm(device, npm); 143562306a36Sopenharmony_ci dasd_path_add_ppm(device, ppm); 143662306a36Sopenharmony_ci if (epm) { 143762306a36Sopenharmony_ci dasd_path_add_tbvpm(device, epm); 143862306a36Sopenharmony_ci dasd_device_set_timer(device, 50); 143962306a36Sopenharmony_ci } 144062306a36Sopenharmony_ci dasd_path_add_cablepm(device, cablepm); 144162306a36Sopenharmony_ci dasd_path_add_nohpfpm(device, hpfpm); 144262306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci dasd_path_create_kobj(device, pos); 144562306a36Sopenharmony_ci } 144662306a36Sopenharmony_ci} 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_cistatic void do_pe_handler_work(struct work_struct *work) 144962306a36Sopenharmony_ci{ 145062306a36Sopenharmony_ci struct pe_handler_work_data *data; 145162306a36Sopenharmony_ci struct dasd_device *device; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci data = container_of(work, struct pe_handler_work_data, worker); 145462306a36Sopenharmony_ci device = data->device; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci /* delay path verification until device was resumed */ 145762306a36Sopenharmony_ci if (test_bit(DASD_FLAG_SUSPENDED, &device->flags)) { 145862306a36Sopenharmony_ci schedule_work(work); 145962306a36Sopenharmony_ci return; 146062306a36Sopenharmony_ci } 146162306a36Sopenharmony_ci /* check if path verification already running and delay if so */ 146262306a36Sopenharmony_ci if (test_and_set_bit(DASD_FLAG_PATH_VERIFY, &device->flags)) { 146362306a36Sopenharmony_ci schedule_work(work); 146462306a36Sopenharmony_ci return; 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci if (data->tbvpm) 146862306a36Sopenharmony_ci dasd_eckd_path_available_action(device, data); 146962306a36Sopenharmony_ci if (data->fcsecpm) 147062306a36Sopenharmony_ci dasd_eckd_read_fc_security(device); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci clear_bit(DASD_FLAG_PATH_VERIFY, &device->flags); 147362306a36Sopenharmony_ci dasd_put_device(device); 147462306a36Sopenharmony_ci if (data->isglobal) 147562306a36Sopenharmony_ci mutex_unlock(&dasd_pe_handler_mutex); 147662306a36Sopenharmony_ci else 147762306a36Sopenharmony_ci kfree(data); 147862306a36Sopenharmony_ci} 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_cistatic int dasd_eckd_pe_handler(struct dasd_device *device, 148162306a36Sopenharmony_ci __u8 tbvpm, __u8 fcsecpm) 148262306a36Sopenharmony_ci{ 148362306a36Sopenharmony_ci struct pe_handler_work_data *data; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_ATOMIC | GFP_DMA); 148662306a36Sopenharmony_ci if (!data) { 148762306a36Sopenharmony_ci if (mutex_trylock(&dasd_pe_handler_mutex)) { 148862306a36Sopenharmony_ci data = pe_handler_worker; 148962306a36Sopenharmony_ci data->isglobal = 1; 149062306a36Sopenharmony_ci } else { 149162306a36Sopenharmony_ci return -ENOMEM; 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci } 149462306a36Sopenharmony_ci INIT_WORK(&data->worker, do_pe_handler_work); 149562306a36Sopenharmony_ci dasd_get_device(device); 149662306a36Sopenharmony_ci data->device = device; 149762306a36Sopenharmony_ci data->tbvpm = tbvpm; 149862306a36Sopenharmony_ci data->fcsecpm = fcsecpm; 149962306a36Sopenharmony_ci schedule_work(&data->worker); 150062306a36Sopenharmony_ci return 0; 150162306a36Sopenharmony_ci} 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_cistatic void dasd_eckd_reset_path(struct dasd_device *device, __u8 pm) 150462306a36Sopenharmony_ci{ 150562306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 150662306a36Sopenharmony_ci unsigned long flags; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci if (!private->fcx_max_data) 150962306a36Sopenharmony_ci private->fcx_max_data = get_fcx_max_data(device); 151062306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 151162306a36Sopenharmony_ci dasd_path_set_tbvpm(device, pm ? : dasd_path_get_notoperpm(device)); 151262306a36Sopenharmony_ci dasd_schedule_device_bh(device); 151362306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 151462306a36Sopenharmony_ci} 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_cistatic int dasd_eckd_read_features(struct dasd_device *device) 151762306a36Sopenharmony_ci{ 151862306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 151962306a36Sopenharmony_ci struct dasd_psf_prssd_data *prssdp; 152062306a36Sopenharmony_ci struct dasd_rssd_features *features; 152162306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 152262306a36Sopenharmony_ci struct ccw1 *ccw; 152362306a36Sopenharmony_ci int rc; 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci memset(&private->features, 0, sizeof(struct dasd_rssd_features)); 152662306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, 152762306a36Sopenharmony_ci (sizeof(struct dasd_psf_prssd_data) + 152862306a36Sopenharmony_ci sizeof(struct dasd_rssd_features)), 152962306a36Sopenharmony_ci device, NULL); 153062306a36Sopenharmony_ci if (IS_ERR(cqr)) { 153162306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "Could not " 153262306a36Sopenharmony_ci "allocate initialization request"); 153362306a36Sopenharmony_ci return PTR_ERR(cqr); 153462306a36Sopenharmony_ci } 153562306a36Sopenharmony_ci cqr->startdev = device; 153662306a36Sopenharmony_ci cqr->memdev = device; 153762306a36Sopenharmony_ci cqr->block = NULL; 153862306a36Sopenharmony_ci cqr->retries = 256; 153962306a36Sopenharmony_ci cqr->expires = 10 * HZ; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci /* Prepare for Read Subsystem Data */ 154262306a36Sopenharmony_ci prssdp = (struct dasd_psf_prssd_data *) cqr->data; 154362306a36Sopenharmony_ci memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); 154462306a36Sopenharmony_ci prssdp->order = PSF_ORDER_PRSSD; 154562306a36Sopenharmony_ci prssdp->suborder = 0x41; /* Read Feature Codes */ 154662306a36Sopenharmony_ci /* all other bytes of prssdp must be zero */ 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci ccw = cqr->cpaddr; 154962306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 155062306a36Sopenharmony_ci ccw->count = sizeof(struct dasd_psf_prssd_data); 155162306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_CC; 155262306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(prssdp); 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci /* Read Subsystem Data - feature codes */ 155562306a36Sopenharmony_ci features = (struct dasd_rssd_features *) (prssdp + 1); 155662306a36Sopenharmony_ci memset(features, 0, sizeof(struct dasd_rssd_features)); 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci ccw++; 155962306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RSSD; 156062306a36Sopenharmony_ci ccw->count = sizeof(struct dasd_rssd_features); 156162306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(features); 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 156462306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 156562306a36Sopenharmony_ci rc = dasd_sleep_on(cqr); 156662306a36Sopenharmony_ci if (rc == 0) { 156762306a36Sopenharmony_ci prssdp = (struct dasd_psf_prssd_data *) cqr->data; 156862306a36Sopenharmony_ci features = (struct dasd_rssd_features *) (prssdp + 1); 156962306a36Sopenharmony_ci memcpy(&private->features, features, 157062306a36Sopenharmony_ci sizeof(struct dasd_rssd_features)); 157162306a36Sopenharmony_ci } else 157262306a36Sopenharmony_ci dev_warn(&device->cdev->dev, "Reading device feature codes" 157362306a36Sopenharmony_ci " failed with rc=%d\n", rc); 157462306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 157562306a36Sopenharmony_ci return rc; 157662306a36Sopenharmony_ci} 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci/* Read Volume Information - Volume Storage Query */ 157962306a36Sopenharmony_cistatic int dasd_eckd_read_vol_info(struct dasd_device *device) 158062306a36Sopenharmony_ci{ 158162306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 158262306a36Sopenharmony_ci struct dasd_psf_prssd_data *prssdp; 158362306a36Sopenharmony_ci struct dasd_rssd_vsq *vsq; 158462306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 158562306a36Sopenharmony_ci struct ccw1 *ccw; 158662306a36Sopenharmony_ci int useglobal; 158762306a36Sopenharmony_ci int rc; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci /* This command cannot be executed on an alias device */ 159062306a36Sopenharmony_ci if (private->uid.type == UA_BASE_PAV_ALIAS || 159162306a36Sopenharmony_ci private->uid.type == UA_HYPER_PAV_ALIAS) 159262306a36Sopenharmony_ci return 0; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci useglobal = 0; 159562306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2 /* PSF + RSSD */, 159662306a36Sopenharmony_ci sizeof(*prssdp) + sizeof(*vsq), device, NULL); 159762306a36Sopenharmony_ci if (IS_ERR(cqr)) { 159862306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 159962306a36Sopenharmony_ci "Could not allocate initialization request"); 160062306a36Sopenharmony_ci mutex_lock(&dasd_vol_info_mutex); 160162306a36Sopenharmony_ci useglobal = 1; 160262306a36Sopenharmony_ci cqr = &dasd_vol_info_req->cqr; 160362306a36Sopenharmony_ci memset(cqr, 0, sizeof(*cqr)); 160462306a36Sopenharmony_ci memset(dasd_vol_info_req, 0, sizeof(*dasd_vol_info_req)); 160562306a36Sopenharmony_ci cqr->cpaddr = &dasd_vol_info_req->ccw; 160662306a36Sopenharmony_ci cqr->data = &dasd_vol_info_req->data; 160762306a36Sopenharmony_ci cqr->magic = DASD_ECKD_MAGIC; 160862306a36Sopenharmony_ci } 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci /* Prepare for Read Subsystem Data */ 161162306a36Sopenharmony_ci prssdp = cqr->data; 161262306a36Sopenharmony_ci prssdp->order = PSF_ORDER_PRSSD; 161362306a36Sopenharmony_ci prssdp->suborder = PSF_SUBORDER_VSQ; /* Volume Storage Query */ 161462306a36Sopenharmony_ci prssdp->lss = private->conf.ned->ID; 161562306a36Sopenharmony_ci prssdp->volume = private->conf.ned->unit_addr; 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci ccw = cqr->cpaddr; 161862306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 161962306a36Sopenharmony_ci ccw->count = sizeof(*prssdp); 162062306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_CC; 162162306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(prssdp); 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci /* Read Subsystem Data - Volume Storage Query */ 162462306a36Sopenharmony_ci vsq = (struct dasd_rssd_vsq *)(prssdp + 1); 162562306a36Sopenharmony_ci memset(vsq, 0, sizeof(*vsq)); 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci ccw++; 162862306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RSSD; 162962306a36Sopenharmony_ci ccw->count = sizeof(*vsq); 163062306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 163162306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(vsq); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 163462306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 163562306a36Sopenharmony_ci cqr->startdev = device; 163662306a36Sopenharmony_ci cqr->memdev = device; 163762306a36Sopenharmony_ci cqr->block = NULL; 163862306a36Sopenharmony_ci cqr->retries = 256; 163962306a36Sopenharmony_ci cqr->expires = device->default_expires * HZ; 164062306a36Sopenharmony_ci /* The command might not be supported. Suppress the error output */ 164162306a36Sopenharmony_ci __set_bit(DASD_CQR_SUPPRESS_CR, &cqr->flags); 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci rc = dasd_sleep_on_interruptible(cqr); 164462306a36Sopenharmony_ci if (rc == 0) { 164562306a36Sopenharmony_ci memcpy(&private->vsq, vsq, sizeof(*vsq)); 164662306a36Sopenharmony_ci } else { 164762306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 164862306a36Sopenharmony_ci "Reading the volume storage information failed with rc=%d", rc); 164962306a36Sopenharmony_ci } 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci if (useglobal) 165262306a36Sopenharmony_ci mutex_unlock(&dasd_vol_info_mutex); 165362306a36Sopenharmony_ci else 165462306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci return rc; 165762306a36Sopenharmony_ci} 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_cistatic int dasd_eckd_is_ese(struct dasd_device *device) 166062306a36Sopenharmony_ci{ 166162306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci return private->vsq.vol_info.ese; 166462306a36Sopenharmony_ci} 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_cistatic int dasd_eckd_ext_pool_id(struct dasd_device *device) 166762306a36Sopenharmony_ci{ 166862306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci return private->vsq.extent_pool_id; 167162306a36Sopenharmony_ci} 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci/* 167462306a36Sopenharmony_ci * This value represents the total amount of available space. As more space is 167562306a36Sopenharmony_ci * allocated by ESE volumes, this value will decrease. 167662306a36Sopenharmony_ci * The data for this value is therefore updated on any call. 167762306a36Sopenharmony_ci */ 167862306a36Sopenharmony_cistatic int dasd_eckd_space_configured(struct dasd_device *device) 167962306a36Sopenharmony_ci{ 168062306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 168162306a36Sopenharmony_ci int rc; 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci rc = dasd_eckd_read_vol_info(device); 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci return rc ? : private->vsq.space_configured; 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci/* 168962306a36Sopenharmony_ci * The value of space allocated by an ESE volume may have changed and is 169062306a36Sopenharmony_ci * therefore updated on any call. 169162306a36Sopenharmony_ci */ 169262306a36Sopenharmony_cistatic int dasd_eckd_space_allocated(struct dasd_device *device) 169362306a36Sopenharmony_ci{ 169462306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 169562306a36Sopenharmony_ci int rc; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci rc = dasd_eckd_read_vol_info(device); 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci return rc ? : private->vsq.space_allocated; 170062306a36Sopenharmony_ci} 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_cistatic int dasd_eckd_logical_capacity(struct dasd_device *device) 170362306a36Sopenharmony_ci{ 170462306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci return private->vsq.logical_capacity; 170762306a36Sopenharmony_ci} 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_cistatic void dasd_eckd_ext_pool_exhaust_work(struct work_struct *work) 171062306a36Sopenharmony_ci{ 171162306a36Sopenharmony_ci struct ext_pool_exhaust_work_data *data; 171262306a36Sopenharmony_ci struct dasd_device *device; 171362306a36Sopenharmony_ci struct dasd_device *base; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci data = container_of(work, struct ext_pool_exhaust_work_data, worker); 171662306a36Sopenharmony_ci device = data->device; 171762306a36Sopenharmony_ci base = data->base; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci if (!base) 172062306a36Sopenharmony_ci base = device; 172162306a36Sopenharmony_ci if (dasd_eckd_space_configured(base) != 0) { 172262306a36Sopenharmony_ci dasd_generic_space_avail(device); 172362306a36Sopenharmony_ci } else { 172462306a36Sopenharmony_ci dev_warn(&device->cdev->dev, "No space left in the extent pool\n"); 172562306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", "out of space"); 172662306a36Sopenharmony_ci } 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci dasd_put_device(device); 172962306a36Sopenharmony_ci kfree(data); 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_cistatic int dasd_eckd_ext_pool_exhaust(struct dasd_device *device, 173362306a36Sopenharmony_ci struct dasd_ccw_req *cqr) 173462306a36Sopenharmony_ci{ 173562306a36Sopenharmony_ci struct ext_pool_exhaust_work_data *data; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_ATOMIC); 173862306a36Sopenharmony_ci if (!data) 173962306a36Sopenharmony_ci return -ENOMEM; 174062306a36Sopenharmony_ci INIT_WORK(&data->worker, dasd_eckd_ext_pool_exhaust_work); 174162306a36Sopenharmony_ci dasd_get_device(device); 174262306a36Sopenharmony_ci data->device = device; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci if (cqr->block) 174562306a36Sopenharmony_ci data->base = cqr->block->base; 174662306a36Sopenharmony_ci else if (cqr->basedev) 174762306a36Sopenharmony_ci data->base = cqr->basedev; 174862306a36Sopenharmony_ci else 174962306a36Sopenharmony_ci data->base = NULL; 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci schedule_work(&data->worker); 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci return 0; 175462306a36Sopenharmony_ci} 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_cistatic void dasd_eckd_cpy_ext_pool_data(struct dasd_device *device, 175762306a36Sopenharmony_ci struct dasd_rssd_lcq *lcq) 175862306a36Sopenharmony_ci{ 175962306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 176062306a36Sopenharmony_ci int pool_id = dasd_eckd_ext_pool_id(device); 176162306a36Sopenharmony_ci struct dasd_ext_pool_sum eps; 176262306a36Sopenharmony_ci int i; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci for (i = 0; i < lcq->pool_count; i++) { 176562306a36Sopenharmony_ci eps = lcq->ext_pool_sum[i]; 176662306a36Sopenharmony_ci if (eps.pool_id == pool_id) { 176762306a36Sopenharmony_ci memcpy(&private->eps, &eps, 176862306a36Sopenharmony_ci sizeof(struct dasd_ext_pool_sum)); 176962306a36Sopenharmony_ci } 177062306a36Sopenharmony_ci } 177162306a36Sopenharmony_ci} 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci/* Read Extent Pool Information - Logical Configuration Query */ 177462306a36Sopenharmony_cistatic int dasd_eckd_read_ext_pool_info(struct dasd_device *device) 177562306a36Sopenharmony_ci{ 177662306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 177762306a36Sopenharmony_ci struct dasd_psf_prssd_data *prssdp; 177862306a36Sopenharmony_ci struct dasd_rssd_lcq *lcq; 177962306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 178062306a36Sopenharmony_ci struct ccw1 *ccw; 178162306a36Sopenharmony_ci int rc; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci /* This command cannot be executed on an alias device */ 178462306a36Sopenharmony_ci if (private->uid.type == UA_BASE_PAV_ALIAS || 178562306a36Sopenharmony_ci private->uid.type == UA_HYPER_PAV_ALIAS) 178662306a36Sopenharmony_ci return 0; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2 /* PSF + RSSD */, 178962306a36Sopenharmony_ci sizeof(*prssdp) + sizeof(*lcq), device, NULL); 179062306a36Sopenharmony_ci if (IS_ERR(cqr)) { 179162306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 179262306a36Sopenharmony_ci "Could not allocate initialization request"); 179362306a36Sopenharmony_ci return PTR_ERR(cqr); 179462306a36Sopenharmony_ci } 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci /* Prepare for Read Subsystem Data */ 179762306a36Sopenharmony_ci prssdp = cqr->data; 179862306a36Sopenharmony_ci memset(prssdp, 0, sizeof(*prssdp)); 179962306a36Sopenharmony_ci prssdp->order = PSF_ORDER_PRSSD; 180062306a36Sopenharmony_ci prssdp->suborder = PSF_SUBORDER_LCQ; /* Logical Configuration Query */ 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci ccw = cqr->cpaddr; 180362306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 180462306a36Sopenharmony_ci ccw->count = sizeof(*prssdp); 180562306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_CC; 180662306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(prssdp); 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci lcq = (struct dasd_rssd_lcq *)(prssdp + 1); 180962306a36Sopenharmony_ci memset(lcq, 0, sizeof(*lcq)); 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci ccw++; 181262306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RSSD; 181362306a36Sopenharmony_ci ccw->count = sizeof(*lcq); 181462306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 181562306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(lcq); 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 181862306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 181962306a36Sopenharmony_ci cqr->startdev = device; 182062306a36Sopenharmony_ci cqr->memdev = device; 182162306a36Sopenharmony_ci cqr->block = NULL; 182262306a36Sopenharmony_ci cqr->retries = 256; 182362306a36Sopenharmony_ci cqr->expires = device->default_expires * HZ; 182462306a36Sopenharmony_ci /* The command might not be supported. Suppress the error output */ 182562306a36Sopenharmony_ci __set_bit(DASD_CQR_SUPPRESS_CR, &cqr->flags); 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci rc = dasd_sleep_on_interruptible(cqr); 182862306a36Sopenharmony_ci if (rc == 0) { 182962306a36Sopenharmony_ci dasd_eckd_cpy_ext_pool_data(device, lcq); 183062306a36Sopenharmony_ci } else { 183162306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 183262306a36Sopenharmony_ci "Reading the logical configuration failed with rc=%d", rc); 183362306a36Sopenharmony_ci } 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci return rc; 183862306a36Sopenharmony_ci} 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci/* 184162306a36Sopenharmony_ci * Depending on the device type, the extent size is specified either as 184262306a36Sopenharmony_ci * cylinders per extent (CKD) or size per extent (FBA) 184362306a36Sopenharmony_ci * A 1GB size corresponds to 1113cyl, and 16MB to 21cyl. 184462306a36Sopenharmony_ci */ 184562306a36Sopenharmony_cistatic int dasd_eckd_ext_size(struct dasd_device *device) 184662306a36Sopenharmony_ci{ 184762306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 184862306a36Sopenharmony_ci struct dasd_ext_pool_sum eps = private->eps; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci if (!eps.flags.extent_size_valid) 185162306a36Sopenharmony_ci return 0; 185262306a36Sopenharmony_ci if (eps.extent_size.size_1G) 185362306a36Sopenharmony_ci return 1113; 185462306a36Sopenharmony_ci if (eps.extent_size.size_16M) 185562306a36Sopenharmony_ci return 21; 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci return 0; 185862306a36Sopenharmony_ci} 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_cistatic int dasd_eckd_ext_pool_warn_thrshld(struct dasd_device *device) 186162306a36Sopenharmony_ci{ 186262306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci return private->eps.warn_thrshld; 186562306a36Sopenharmony_ci} 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_cistatic int dasd_eckd_ext_pool_cap_at_warnlevel(struct dasd_device *device) 186862306a36Sopenharmony_ci{ 186962306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci return private->eps.flags.capacity_at_warnlevel; 187262306a36Sopenharmony_ci} 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci/* 187562306a36Sopenharmony_ci * Extent Pool out of space 187662306a36Sopenharmony_ci */ 187762306a36Sopenharmony_cistatic int dasd_eckd_ext_pool_oos(struct dasd_device *device) 187862306a36Sopenharmony_ci{ 187962306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci return private->eps.flags.pool_oos; 188262306a36Sopenharmony_ci} 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci/* 188562306a36Sopenharmony_ci * Build CP for Perform Subsystem Function - SSC. 188662306a36Sopenharmony_ci */ 188762306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device, 188862306a36Sopenharmony_ci int enable_pav) 188962306a36Sopenharmony_ci{ 189062306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 189162306a36Sopenharmony_ci struct dasd_psf_ssc_data *psf_ssc_data; 189262306a36Sopenharmony_ci struct ccw1 *ccw; 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ , 189562306a36Sopenharmony_ci sizeof(struct dasd_psf_ssc_data), 189662306a36Sopenharmony_ci device, NULL); 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci if (IS_ERR(cqr)) { 189962306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 190062306a36Sopenharmony_ci "Could not allocate PSF-SSC request"); 190162306a36Sopenharmony_ci return cqr; 190262306a36Sopenharmony_ci } 190362306a36Sopenharmony_ci psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data; 190462306a36Sopenharmony_ci psf_ssc_data->order = PSF_ORDER_SSC; 190562306a36Sopenharmony_ci psf_ssc_data->suborder = 0xc0; 190662306a36Sopenharmony_ci if (enable_pav) { 190762306a36Sopenharmony_ci psf_ssc_data->suborder |= 0x08; 190862306a36Sopenharmony_ci psf_ssc_data->reserved[0] = 0x88; 190962306a36Sopenharmony_ci } 191062306a36Sopenharmony_ci ccw = cqr->cpaddr; 191162306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 191262306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(psf_ssc_data); 191362306a36Sopenharmony_ci ccw->count = 66; 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci cqr->startdev = device; 191662306a36Sopenharmony_ci cqr->memdev = device; 191762306a36Sopenharmony_ci cqr->block = NULL; 191862306a36Sopenharmony_ci cqr->retries = 256; 191962306a36Sopenharmony_ci cqr->expires = 10*HZ; 192062306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 192162306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 192262306a36Sopenharmony_ci return cqr; 192362306a36Sopenharmony_ci} 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci/* 192662306a36Sopenharmony_ci * Perform Subsystem Function. 192762306a36Sopenharmony_ci * It is necessary to trigger CIO for channel revalidation since this 192862306a36Sopenharmony_ci * call might change behaviour of DASD devices. 192962306a36Sopenharmony_ci */ 193062306a36Sopenharmony_cistatic int 193162306a36Sopenharmony_cidasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav, 193262306a36Sopenharmony_ci unsigned long flags) 193362306a36Sopenharmony_ci{ 193462306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 193562306a36Sopenharmony_ci int rc; 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci cqr = dasd_eckd_build_psf_ssc(device, enable_pav); 193862306a36Sopenharmony_ci if (IS_ERR(cqr)) 193962306a36Sopenharmony_ci return PTR_ERR(cqr); 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci /* 194262306a36Sopenharmony_ci * set flags e.g. turn on failfast, to prevent blocking 194362306a36Sopenharmony_ci * the calling function should handle failed requests 194462306a36Sopenharmony_ci */ 194562306a36Sopenharmony_ci cqr->flags |= flags; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci rc = dasd_sleep_on(cqr); 194862306a36Sopenharmony_ci if (!rc) 194962306a36Sopenharmony_ci /* trigger CIO to reprobe devices */ 195062306a36Sopenharmony_ci css_schedule_reprobe(); 195162306a36Sopenharmony_ci else if (cqr->intrc == -EAGAIN) 195262306a36Sopenharmony_ci rc = -EAGAIN; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 195562306a36Sopenharmony_ci return rc; 195662306a36Sopenharmony_ci} 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci/* 195962306a36Sopenharmony_ci * Valide storage server of current device. 196062306a36Sopenharmony_ci */ 196162306a36Sopenharmony_cistatic int dasd_eckd_validate_server(struct dasd_device *device, 196262306a36Sopenharmony_ci unsigned long flags) 196362306a36Sopenharmony_ci{ 196462306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 196562306a36Sopenharmony_ci int enable_pav, rc; 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci if (private->uid.type == UA_BASE_PAV_ALIAS || 196862306a36Sopenharmony_ci private->uid.type == UA_HYPER_PAV_ALIAS) 196962306a36Sopenharmony_ci return 0; 197062306a36Sopenharmony_ci if (dasd_nopav || MACHINE_IS_VM) 197162306a36Sopenharmony_ci enable_pav = 0; 197262306a36Sopenharmony_ci else 197362306a36Sopenharmony_ci enable_pav = 1; 197462306a36Sopenharmony_ci rc = dasd_eckd_psf_ssc(device, enable_pav, flags); 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci /* may be requested feature is not available on server, 197762306a36Sopenharmony_ci * therefore just report error and go ahead */ 197862306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x " 197962306a36Sopenharmony_ci "returned rc=%d", private->uid.ssid, rc); 198062306a36Sopenharmony_ci return rc; 198162306a36Sopenharmony_ci} 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci/* 198462306a36Sopenharmony_ci * worker to do a validate server in case of a lost pathgroup 198562306a36Sopenharmony_ci */ 198662306a36Sopenharmony_cistatic void dasd_eckd_do_validate_server(struct work_struct *work) 198762306a36Sopenharmony_ci{ 198862306a36Sopenharmony_ci struct dasd_device *device = container_of(work, struct dasd_device, 198962306a36Sopenharmony_ci kick_validate); 199062306a36Sopenharmony_ci unsigned long flags = 0; 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &flags); 199362306a36Sopenharmony_ci if (dasd_eckd_validate_server(device, flags) 199462306a36Sopenharmony_ci == -EAGAIN) { 199562306a36Sopenharmony_ci /* schedule worker again if failed */ 199662306a36Sopenharmony_ci schedule_work(&device->kick_validate); 199762306a36Sopenharmony_ci return; 199862306a36Sopenharmony_ci } 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci dasd_put_device(device); 200162306a36Sopenharmony_ci} 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_cistatic void dasd_eckd_kick_validate_server(struct dasd_device *device) 200462306a36Sopenharmony_ci{ 200562306a36Sopenharmony_ci dasd_get_device(device); 200662306a36Sopenharmony_ci /* exit if device not online or in offline processing */ 200762306a36Sopenharmony_ci if (test_bit(DASD_FLAG_OFFLINE, &device->flags) || 200862306a36Sopenharmony_ci device->state < DASD_STATE_ONLINE) { 200962306a36Sopenharmony_ci dasd_put_device(device); 201062306a36Sopenharmony_ci return; 201162306a36Sopenharmony_ci } 201262306a36Sopenharmony_ci /* queue call to do_validate_server to the kernel event daemon. */ 201362306a36Sopenharmony_ci if (!schedule_work(&device->kick_validate)) 201462306a36Sopenharmony_ci dasd_put_device(device); 201562306a36Sopenharmony_ci} 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci/* 201862306a36Sopenharmony_ci * return if the device is the copy relation primary if a copy relation is active 201962306a36Sopenharmony_ci */ 202062306a36Sopenharmony_cistatic int dasd_device_is_primary(struct dasd_device *device) 202162306a36Sopenharmony_ci{ 202262306a36Sopenharmony_ci if (!device->copy) 202362306a36Sopenharmony_ci return 1; 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci if (device->copy->active->device == device) 202662306a36Sopenharmony_ci return 1; 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci return 0; 202962306a36Sopenharmony_ci} 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_cistatic int dasd_eckd_alloc_block(struct dasd_device *device) 203262306a36Sopenharmony_ci{ 203362306a36Sopenharmony_ci struct dasd_block *block; 203462306a36Sopenharmony_ci struct dasd_uid temp_uid; 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci if (!dasd_device_is_primary(device)) 203762306a36Sopenharmony_ci return 0; 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci dasd_eckd_get_uid(device, &temp_uid); 204062306a36Sopenharmony_ci if (temp_uid.type == UA_BASE_DEVICE) { 204162306a36Sopenharmony_ci block = dasd_alloc_block(); 204262306a36Sopenharmony_ci if (IS_ERR(block)) { 204362306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 204462306a36Sopenharmony_ci "could not allocate dasd block structure"); 204562306a36Sopenharmony_ci return PTR_ERR(block); 204662306a36Sopenharmony_ci } 204762306a36Sopenharmony_ci device->block = block; 204862306a36Sopenharmony_ci block->base = device; 204962306a36Sopenharmony_ci } 205062306a36Sopenharmony_ci return 0; 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_cistatic bool dasd_eckd_pprc_enabled(struct dasd_device *device) 205462306a36Sopenharmony_ci{ 205562306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci return private->rdc_data.facilities.PPRC_enabled; 205862306a36Sopenharmony_ci} 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci/* 206162306a36Sopenharmony_ci * Check device characteristics. 206262306a36Sopenharmony_ci * If the device is accessible using ECKD discipline, the device is enabled. 206362306a36Sopenharmony_ci */ 206462306a36Sopenharmony_cistatic int 206562306a36Sopenharmony_cidasd_eckd_check_characteristics(struct dasd_device *device) 206662306a36Sopenharmony_ci{ 206762306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 206862306a36Sopenharmony_ci int rc, i; 206962306a36Sopenharmony_ci int readonly; 207062306a36Sopenharmony_ci unsigned long value; 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci /* setup work queue for validate server*/ 207362306a36Sopenharmony_ci INIT_WORK(&device->kick_validate, dasd_eckd_do_validate_server); 207462306a36Sopenharmony_ci /* setup work queue for summary unit check */ 207562306a36Sopenharmony_ci INIT_WORK(&device->suc_work, dasd_alias_handle_summary_unit_check); 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci if (!ccw_device_is_pathgroup(device->cdev)) { 207862306a36Sopenharmony_ci dev_warn(&device->cdev->dev, 207962306a36Sopenharmony_ci "A channel path group could not be established\n"); 208062306a36Sopenharmony_ci return -EIO; 208162306a36Sopenharmony_ci } 208262306a36Sopenharmony_ci if (!ccw_device_is_multipath(device->cdev)) { 208362306a36Sopenharmony_ci dev_info(&device->cdev->dev, 208462306a36Sopenharmony_ci "The DASD is not operating in multipath mode\n"); 208562306a36Sopenharmony_ci } 208662306a36Sopenharmony_ci if (!private) { 208762306a36Sopenharmony_ci private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA); 208862306a36Sopenharmony_ci if (!private) { 208962306a36Sopenharmony_ci dev_warn(&device->cdev->dev, 209062306a36Sopenharmony_ci "Allocating memory for private DASD data " 209162306a36Sopenharmony_ci "failed\n"); 209262306a36Sopenharmony_ci return -ENOMEM; 209362306a36Sopenharmony_ci } 209462306a36Sopenharmony_ci device->private = private; 209562306a36Sopenharmony_ci } else { 209662306a36Sopenharmony_ci memset(private, 0, sizeof(*private)); 209762306a36Sopenharmony_ci } 209862306a36Sopenharmony_ci /* Invalidate status of initial analysis. */ 209962306a36Sopenharmony_ci private->init_cqr_status = -1; 210062306a36Sopenharmony_ci /* Set default cache operations. */ 210162306a36Sopenharmony_ci private->attrib.operation = DASD_NORMAL_CACHE; 210262306a36Sopenharmony_ci private->attrib.nr_cyl = 0; 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci /* Read Configuration Data */ 210562306a36Sopenharmony_ci rc = dasd_eckd_read_conf(device); 210662306a36Sopenharmony_ci if (rc) 210762306a36Sopenharmony_ci goto out_err1; 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci /* set some default values */ 211062306a36Sopenharmony_ci device->default_expires = DASD_EXPIRES; 211162306a36Sopenharmony_ci device->default_retries = DASD_RETRIES; 211262306a36Sopenharmony_ci device->path_thrhld = DASD_ECKD_PATH_THRHLD; 211362306a36Sopenharmony_ci device->path_interval = DASD_ECKD_PATH_INTERVAL; 211462306a36Sopenharmony_ci device->aq_timeouts = DASD_RETRIES_MAX; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci if (private->conf.gneq) { 211762306a36Sopenharmony_ci value = 1; 211862306a36Sopenharmony_ci for (i = 0; i < private->conf.gneq->timeout.value; i++) 211962306a36Sopenharmony_ci value = 10 * value; 212062306a36Sopenharmony_ci value = value * private->conf.gneq->timeout.number; 212162306a36Sopenharmony_ci /* do not accept useless values */ 212262306a36Sopenharmony_ci if (value != 0 && value <= DASD_EXPIRES_MAX) 212362306a36Sopenharmony_ci device->default_expires = value; 212462306a36Sopenharmony_ci } 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci /* Read Device Characteristics */ 212762306a36Sopenharmony_ci rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC, 212862306a36Sopenharmony_ci &private->rdc_data, 64); 212962306a36Sopenharmony_ci if (rc) { 213062306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 213162306a36Sopenharmony_ci "Read device characteristic failed, rc=%d", rc); 213262306a36Sopenharmony_ci goto out_err1; 213362306a36Sopenharmony_ci } 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci /* setup PPRC for device from devmap */ 213662306a36Sopenharmony_ci rc = dasd_devmap_set_device_copy_relation(device->cdev, 213762306a36Sopenharmony_ci dasd_eckd_pprc_enabled(device)); 213862306a36Sopenharmony_ci if (rc) { 213962306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 214062306a36Sopenharmony_ci "copy relation setup failed, rc=%d", rc); 214162306a36Sopenharmony_ci goto out_err1; 214262306a36Sopenharmony_ci } 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci /* check if block device is needed and allocate in case */ 214562306a36Sopenharmony_ci rc = dasd_eckd_alloc_block(device); 214662306a36Sopenharmony_ci if (rc) 214762306a36Sopenharmony_ci goto out_err1; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci /* register lcu with alias handling, enable PAV */ 215062306a36Sopenharmony_ci rc = dasd_alias_make_device_known_to_lcu(device); 215162306a36Sopenharmony_ci if (rc) 215262306a36Sopenharmony_ci goto out_err2; 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci dasd_eckd_validate_server(device, 0); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci /* device may report different configuration data after LCU setup */ 215762306a36Sopenharmony_ci rc = dasd_eckd_read_conf(device); 215862306a36Sopenharmony_ci if (rc) 215962306a36Sopenharmony_ci goto out_err3; 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci dasd_eckd_read_fc_security(device); 216262306a36Sopenharmony_ci dasd_path_create_kobjects(device); 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci /* Read Feature Codes */ 216562306a36Sopenharmony_ci dasd_eckd_read_features(device); 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci /* Read Volume Information */ 216862306a36Sopenharmony_ci dasd_eckd_read_vol_info(device); 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci /* Read Extent Pool Information */ 217162306a36Sopenharmony_ci dasd_eckd_read_ext_pool_info(device); 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci if ((device->features & DASD_FEATURE_USERAW) && 217462306a36Sopenharmony_ci !(private->rdc_data.facilities.RT_in_LR)) { 217562306a36Sopenharmony_ci dev_err(&device->cdev->dev, "The storage server does not " 217662306a36Sopenharmony_ci "support raw-track access\n"); 217762306a36Sopenharmony_ci rc = -EINVAL; 217862306a36Sopenharmony_ci goto out_err3; 217962306a36Sopenharmony_ci } 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci /* find the valid cylinder size */ 218262306a36Sopenharmony_ci if (private->rdc_data.no_cyl == LV_COMPAT_CYL && 218362306a36Sopenharmony_ci private->rdc_data.long_no_cyl) 218462306a36Sopenharmony_ci private->real_cyl = private->rdc_data.long_no_cyl; 218562306a36Sopenharmony_ci else 218662306a36Sopenharmony_ci private->real_cyl = private->rdc_data.no_cyl; 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci private->fcx_max_data = get_fcx_max_data(device); 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci readonly = dasd_device_is_ro(device); 219162306a36Sopenharmony_ci if (readonly) 219262306a36Sopenharmony_ci set_bit(DASD_FLAG_DEVICE_RO, &device->flags); 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci dev_info(&device->cdev->dev, "New DASD %04X/%02X (CU %04X/%02X) " 219562306a36Sopenharmony_ci "with %d cylinders, %d heads, %d sectors%s\n", 219662306a36Sopenharmony_ci private->rdc_data.dev_type, 219762306a36Sopenharmony_ci private->rdc_data.dev_model, 219862306a36Sopenharmony_ci private->rdc_data.cu_type, 219962306a36Sopenharmony_ci private->rdc_data.cu_model.model, 220062306a36Sopenharmony_ci private->real_cyl, 220162306a36Sopenharmony_ci private->rdc_data.trk_per_cyl, 220262306a36Sopenharmony_ci private->rdc_data.sec_per_trk, 220362306a36Sopenharmony_ci readonly ? ", read-only device" : ""); 220462306a36Sopenharmony_ci return 0; 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ciout_err3: 220762306a36Sopenharmony_ci dasd_alias_disconnect_device_from_lcu(device); 220862306a36Sopenharmony_ciout_err2: 220962306a36Sopenharmony_ci dasd_free_block(device->block); 221062306a36Sopenharmony_ci device->block = NULL; 221162306a36Sopenharmony_ciout_err1: 221262306a36Sopenharmony_ci dasd_eckd_clear_conf_data(device); 221362306a36Sopenharmony_ci dasd_path_remove_kobjects(device); 221462306a36Sopenharmony_ci kfree(device->private); 221562306a36Sopenharmony_ci device->private = NULL; 221662306a36Sopenharmony_ci return rc; 221762306a36Sopenharmony_ci} 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_cistatic void dasd_eckd_uncheck_device(struct dasd_device *device) 222062306a36Sopenharmony_ci{ 222162306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci if (!private) 222462306a36Sopenharmony_ci return; 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci dasd_alias_disconnect_device_from_lcu(device); 222762306a36Sopenharmony_ci private->conf.ned = NULL; 222862306a36Sopenharmony_ci private->conf.sneq = NULL; 222962306a36Sopenharmony_ci private->conf.vdsneq = NULL; 223062306a36Sopenharmony_ci private->conf.gneq = NULL; 223162306a36Sopenharmony_ci dasd_eckd_clear_conf_data(device); 223262306a36Sopenharmony_ci dasd_path_remove_kobjects(device); 223362306a36Sopenharmony_ci} 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_cistatic struct dasd_ccw_req * 223662306a36Sopenharmony_cidasd_eckd_analysis_ccw(struct dasd_device *device) 223762306a36Sopenharmony_ci{ 223862306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 223962306a36Sopenharmony_ci struct eckd_count *count_data; 224062306a36Sopenharmony_ci struct LO_eckd_data *LO_data; 224162306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 224262306a36Sopenharmony_ci struct ccw1 *ccw; 224362306a36Sopenharmony_ci int cplength, datasize; 224462306a36Sopenharmony_ci int i; 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci cplength = 8; 224762306a36Sopenharmony_ci datasize = sizeof(struct DE_eckd_data) + 2*sizeof(struct LO_eckd_data); 224862306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device, 224962306a36Sopenharmony_ci NULL); 225062306a36Sopenharmony_ci if (IS_ERR(cqr)) 225162306a36Sopenharmony_ci return cqr; 225262306a36Sopenharmony_ci ccw = cqr->cpaddr; 225362306a36Sopenharmony_ci /* Define extent for the first 2 tracks. */ 225462306a36Sopenharmony_ci define_extent(ccw++, cqr->data, 0, 1, 225562306a36Sopenharmony_ci DASD_ECKD_CCW_READ_COUNT, device, 0); 225662306a36Sopenharmony_ci LO_data = cqr->data + sizeof(struct DE_eckd_data); 225762306a36Sopenharmony_ci /* Locate record for the first 4 records on track 0. */ 225862306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 225962306a36Sopenharmony_ci locate_record(ccw++, LO_data++, 0, 0, 4, 226062306a36Sopenharmony_ci DASD_ECKD_CCW_READ_COUNT, device, 0); 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci count_data = private->count_area; 226362306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 226462306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 226562306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT; 226662306a36Sopenharmony_ci ccw->flags = 0; 226762306a36Sopenharmony_ci ccw->count = 8; 226862306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(count_data); 226962306a36Sopenharmony_ci ccw++; 227062306a36Sopenharmony_ci count_data++; 227162306a36Sopenharmony_ci } 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci /* Locate record for the first record on track 1. */ 227462306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 227562306a36Sopenharmony_ci locate_record(ccw++, LO_data++, 1, 0, 1, 227662306a36Sopenharmony_ci DASD_ECKD_CCW_READ_COUNT, device, 0); 227762306a36Sopenharmony_ci /* Read count ccw. */ 227862306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 227962306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT; 228062306a36Sopenharmony_ci ccw->flags = 0; 228162306a36Sopenharmony_ci ccw->count = 8; 228262306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(count_data); 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci cqr->block = NULL; 228562306a36Sopenharmony_ci cqr->startdev = device; 228662306a36Sopenharmony_ci cqr->memdev = device; 228762306a36Sopenharmony_ci cqr->retries = 255; 228862306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 228962306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 229062306a36Sopenharmony_ci /* Set flags to suppress output for expected errors */ 229162306a36Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags); 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci return cqr; 229462306a36Sopenharmony_ci} 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci/* differentiate between 'no record found' and any other error */ 229762306a36Sopenharmony_cistatic int dasd_eckd_analysis_evaluation(struct dasd_ccw_req *init_cqr) 229862306a36Sopenharmony_ci{ 229962306a36Sopenharmony_ci char *sense; 230062306a36Sopenharmony_ci if (init_cqr->status == DASD_CQR_DONE) 230162306a36Sopenharmony_ci return INIT_CQR_OK; 230262306a36Sopenharmony_ci else if (init_cqr->status == DASD_CQR_NEED_ERP || 230362306a36Sopenharmony_ci init_cqr->status == DASD_CQR_FAILED) { 230462306a36Sopenharmony_ci sense = dasd_get_sense(&init_cqr->irb); 230562306a36Sopenharmony_ci if (sense && (sense[1] & SNS1_NO_REC_FOUND)) 230662306a36Sopenharmony_ci return INIT_CQR_UNFORMATTED; 230762306a36Sopenharmony_ci else 230862306a36Sopenharmony_ci return INIT_CQR_ERROR; 230962306a36Sopenharmony_ci } else 231062306a36Sopenharmony_ci return INIT_CQR_ERROR; 231162306a36Sopenharmony_ci} 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci/* 231462306a36Sopenharmony_ci * This is the callback function for the init_analysis cqr. It saves 231562306a36Sopenharmony_ci * the status of the initial analysis ccw before it frees it and kicks 231662306a36Sopenharmony_ci * the device to continue the startup sequence. This will call 231762306a36Sopenharmony_ci * dasd_eckd_do_analysis again (if the devices has not been marked 231862306a36Sopenharmony_ci * for deletion in the meantime). 231962306a36Sopenharmony_ci */ 232062306a36Sopenharmony_cistatic void dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, 232162306a36Sopenharmony_ci void *data) 232262306a36Sopenharmony_ci{ 232362306a36Sopenharmony_ci struct dasd_device *device = init_cqr->startdev; 232462306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_ci private->init_cqr_status = dasd_eckd_analysis_evaluation(init_cqr); 232762306a36Sopenharmony_ci dasd_sfree_request(init_cqr, device); 232862306a36Sopenharmony_ci dasd_kick_device(device); 232962306a36Sopenharmony_ci} 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_cistatic int dasd_eckd_start_analysis(struct dasd_block *block) 233262306a36Sopenharmony_ci{ 233362306a36Sopenharmony_ci struct dasd_ccw_req *init_cqr; 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci init_cqr = dasd_eckd_analysis_ccw(block->base); 233662306a36Sopenharmony_ci if (IS_ERR(init_cqr)) 233762306a36Sopenharmony_ci return PTR_ERR(init_cqr); 233862306a36Sopenharmony_ci init_cqr->callback = dasd_eckd_analysis_callback; 233962306a36Sopenharmony_ci init_cqr->callback_data = NULL; 234062306a36Sopenharmony_ci init_cqr->expires = 5*HZ; 234162306a36Sopenharmony_ci /* first try without ERP, so we can later handle unformatted 234262306a36Sopenharmony_ci * devices as special case 234362306a36Sopenharmony_ci */ 234462306a36Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &init_cqr->flags); 234562306a36Sopenharmony_ci init_cqr->retries = 0; 234662306a36Sopenharmony_ci dasd_add_request_head(init_cqr); 234762306a36Sopenharmony_ci return -EAGAIN; 234862306a36Sopenharmony_ci} 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_cistatic int dasd_eckd_end_analysis(struct dasd_block *block) 235162306a36Sopenharmony_ci{ 235262306a36Sopenharmony_ci struct dasd_device *device = block->base; 235362306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 235462306a36Sopenharmony_ci struct eckd_count *count_area; 235562306a36Sopenharmony_ci unsigned int sb, blk_per_trk; 235662306a36Sopenharmony_ci int status, i; 235762306a36Sopenharmony_ci struct dasd_ccw_req *init_cqr; 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci status = private->init_cqr_status; 236062306a36Sopenharmony_ci private->init_cqr_status = -1; 236162306a36Sopenharmony_ci if (status == INIT_CQR_ERROR) { 236262306a36Sopenharmony_ci /* try again, this time with full ERP */ 236362306a36Sopenharmony_ci init_cqr = dasd_eckd_analysis_ccw(device); 236462306a36Sopenharmony_ci dasd_sleep_on(init_cqr); 236562306a36Sopenharmony_ci status = dasd_eckd_analysis_evaluation(init_cqr); 236662306a36Sopenharmony_ci dasd_sfree_request(init_cqr, device); 236762306a36Sopenharmony_ci } 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_ci if (device->features & DASD_FEATURE_USERAW) { 237062306a36Sopenharmony_ci block->bp_block = DASD_RAW_BLOCKSIZE; 237162306a36Sopenharmony_ci blk_per_trk = DASD_RAW_BLOCK_PER_TRACK; 237262306a36Sopenharmony_ci block->s2b_shift = 3; 237362306a36Sopenharmony_ci goto raw; 237462306a36Sopenharmony_ci } 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_ci if (status == INIT_CQR_UNFORMATTED) { 237762306a36Sopenharmony_ci dev_warn(&device->cdev->dev, "The DASD is not formatted\n"); 237862306a36Sopenharmony_ci return -EMEDIUMTYPE; 237962306a36Sopenharmony_ci } else if (status == INIT_CQR_ERROR) { 238062306a36Sopenharmony_ci dev_err(&device->cdev->dev, 238162306a36Sopenharmony_ci "Detecting the DASD disk layout failed because " 238262306a36Sopenharmony_ci "of an I/O error\n"); 238362306a36Sopenharmony_ci return -EIO; 238462306a36Sopenharmony_ci } 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci private->uses_cdl = 1; 238762306a36Sopenharmony_ci /* Check Track 0 for Compatible Disk Layout */ 238862306a36Sopenharmony_ci count_area = NULL; 238962306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 239062306a36Sopenharmony_ci if (private->count_area[i].kl != 4 || 239162306a36Sopenharmony_ci private->count_area[i].dl != dasd_eckd_cdl_reclen(i) - 4 || 239262306a36Sopenharmony_ci private->count_area[i].cyl != 0 || 239362306a36Sopenharmony_ci private->count_area[i].head != count_area_head[i] || 239462306a36Sopenharmony_ci private->count_area[i].record != count_area_rec[i]) { 239562306a36Sopenharmony_ci private->uses_cdl = 0; 239662306a36Sopenharmony_ci break; 239762306a36Sopenharmony_ci } 239862306a36Sopenharmony_ci } 239962306a36Sopenharmony_ci if (i == 3) 240062306a36Sopenharmony_ci count_area = &private->count_area[3]; 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci if (private->uses_cdl == 0) { 240362306a36Sopenharmony_ci for (i = 0; i < 5; i++) { 240462306a36Sopenharmony_ci if ((private->count_area[i].kl != 0) || 240562306a36Sopenharmony_ci (private->count_area[i].dl != 240662306a36Sopenharmony_ci private->count_area[0].dl) || 240762306a36Sopenharmony_ci private->count_area[i].cyl != 0 || 240862306a36Sopenharmony_ci private->count_area[i].head != count_area_head[i] || 240962306a36Sopenharmony_ci private->count_area[i].record != count_area_rec[i]) 241062306a36Sopenharmony_ci break; 241162306a36Sopenharmony_ci } 241262306a36Sopenharmony_ci if (i == 5) 241362306a36Sopenharmony_ci count_area = &private->count_area[0]; 241462306a36Sopenharmony_ci } else { 241562306a36Sopenharmony_ci if (private->count_area[3].record == 1) 241662306a36Sopenharmony_ci dev_warn(&device->cdev->dev, 241762306a36Sopenharmony_ci "Track 0 has no records following the VTOC\n"); 241862306a36Sopenharmony_ci } 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci if (count_area != NULL && count_area->kl == 0) { 242162306a36Sopenharmony_ci /* we found notthing violating our disk layout */ 242262306a36Sopenharmony_ci if (dasd_check_blocksize(count_area->dl) == 0) 242362306a36Sopenharmony_ci block->bp_block = count_area->dl; 242462306a36Sopenharmony_ci } 242562306a36Sopenharmony_ci if (block->bp_block == 0) { 242662306a36Sopenharmony_ci dev_warn(&device->cdev->dev, 242762306a36Sopenharmony_ci "The disk layout of the DASD is not supported\n"); 242862306a36Sopenharmony_ci return -EMEDIUMTYPE; 242962306a36Sopenharmony_ci } 243062306a36Sopenharmony_ci block->s2b_shift = 0; /* bits to shift 512 to get a block */ 243162306a36Sopenharmony_ci for (sb = 512; sb < block->bp_block; sb = sb << 1) 243262306a36Sopenharmony_ci block->s2b_shift++; 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block); 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_ciraw: 243762306a36Sopenharmony_ci block->blocks = ((unsigned long) private->real_cyl * 243862306a36Sopenharmony_ci private->rdc_data.trk_per_cyl * 243962306a36Sopenharmony_ci blk_per_trk); 244062306a36Sopenharmony_ci 244162306a36Sopenharmony_ci dev_info(&device->cdev->dev, 244262306a36Sopenharmony_ci "DASD with %u KB/block, %lu KB total size, %u KB/track, " 244362306a36Sopenharmony_ci "%s\n", (block->bp_block >> 10), 244462306a36Sopenharmony_ci (((unsigned long) private->real_cyl * 244562306a36Sopenharmony_ci private->rdc_data.trk_per_cyl * 244662306a36Sopenharmony_ci blk_per_trk * (block->bp_block >> 9)) >> 1), 244762306a36Sopenharmony_ci ((blk_per_trk * block->bp_block) >> 10), 244862306a36Sopenharmony_ci private->uses_cdl ? 244962306a36Sopenharmony_ci "compatible disk layout" : "linux disk layout"); 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci return 0; 245262306a36Sopenharmony_ci} 245362306a36Sopenharmony_ci 245462306a36Sopenharmony_cistatic int dasd_eckd_do_analysis(struct dasd_block *block) 245562306a36Sopenharmony_ci{ 245662306a36Sopenharmony_ci struct dasd_eckd_private *private = block->base->private; 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci if (private->init_cqr_status < 0) 245962306a36Sopenharmony_ci return dasd_eckd_start_analysis(block); 246062306a36Sopenharmony_ci else 246162306a36Sopenharmony_ci return dasd_eckd_end_analysis(block); 246262306a36Sopenharmony_ci} 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_cistatic int dasd_eckd_basic_to_ready(struct dasd_device *device) 246562306a36Sopenharmony_ci{ 246662306a36Sopenharmony_ci return dasd_alias_add_device(device); 246762306a36Sopenharmony_ci}; 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_cistatic int dasd_eckd_online_to_ready(struct dasd_device *device) 247062306a36Sopenharmony_ci{ 247162306a36Sopenharmony_ci if (cancel_work_sync(&device->reload_device)) 247262306a36Sopenharmony_ci dasd_put_device(device); 247362306a36Sopenharmony_ci if (cancel_work_sync(&device->kick_validate)) 247462306a36Sopenharmony_ci dasd_put_device(device); 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci return 0; 247762306a36Sopenharmony_ci}; 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_cistatic int dasd_eckd_basic_to_known(struct dasd_device *device) 248062306a36Sopenharmony_ci{ 248162306a36Sopenharmony_ci return dasd_alias_remove_device(device); 248262306a36Sopenharmony_ci}; 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_cistatic int 248562306a36Sopenharmony_cidasd_eckd_fill_geometry(struct dasd_block *block, struct hd_geometry *geo) 248662306a36Sopenharmony_ci{ 248762306a36Sopenharmony_ci struct dasd_eckd_private *private = block->base->private; 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci if (dasd_check_blocksize(block->bp_block) == 0) { 249062306a36Sopenharmony_ci geo->sectors = recs_per_track(&private->rdc_data, 249162306a36Sopenharmony_ci 0, block->bp_block); 249262306a36Sopenharmony_ci } 249362306a36Sopenharmony_ci geo->cylinders = private->rdc_data.no_cyl; 249462306a36Sopenharmony_ci geo->heads = private->rdc_data.trk_per_cyl; 249562306a36Sopenharmony_ci return 0; 249662306a36Sopenharmony_ci} 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci/* 249962306a36Sopenharmony_ci * Build the TCW request for the format check 250062306a36Sopenharmony_ci */ 250162306a36Sopenharmony_cistatic struct dasd_ccw_req * 250262306a36Sopenharmony_cidasd_eckd_build_check_tcw(struct dasd_device *base, struct format_data_t *fdata, 250362306a36Sopenharmony_ci int enable_pav, struct eckd_count *fmt_buffer, 250462306a36Sopenharmony_ci int rpt) 250562306a36Sopenharmony_ci{ 250662306a36Sopenharmony_ci struct dasd_eckd_private *start_priv; 250762306a36Sopenharmony_ci struct dasd_device *startdev = NULL; 250862306a36Sopenharmony_ci struct tidaw *last_tidaw = NULL; 250962306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 251062306a36Sopenharmony_ci struct itcw *itcw; 251162306a36Sopenharmony_ci int itcw_size; 251262306a36Sopenharmony_ci int count; 251362306a36Sopenharmony_ci int rc; 251462306a36Sopenharmony_ci int i; 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci if (enable_pav) 251762306a36Sopenharmony_ci startdev = dasd_alias_get_start_dev(base); 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ci if (!startdev) 252062306a36Sopenharmony_ci startdev = base; 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_ci start_priv = startdev->private; 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci count = rpt * (fdata->stop_unit - fdata->start_unit + 1); 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci /* 252762306a36Sopenharmony_ci * we're adding 'count' amount of tidaw to the itcw. 252862306a36Sopenharmony_ci * calculate the corresponding itcw_size 252962306a36Sopenharmony_ci */ 253062306a36Sopenharmony_ci itcw_size = itcw_calc_size(0, count, 0); 253162306a36Sopenharmony_ci 253262306a36Sopenharmony_ci cqr = dasd_fmalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev); 253362306a36Sopenharmony_ci if (IS_ERR(cqr)) 253462306a36Sopenharmony_ci return cqr; 253562306a36Sopenharmony_ci 253662306a36Sopenharmony_ci start_priv->count++; 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_ci itcw = itcw_init(cqr->data, itcw_size, ITCW_OP_READ, 0, count, 0); 253962306a36Sopenharmony_ci if (IS_ERR(itcw)) { 254062306a36Sopenharmony_ci rc = -EINVAL; 254162306a36Sopenharmony_ci goto out_err; 254262306a36Sopenharmony_ci } 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci cqr->cpaddr = itcw_get_tcw(itcw); 254562306a36Sopenharmony_ci rc = prepare_itcw(itcw, fdata->start_unit, fdata->stop_unit, 254662306a36Sopenharmony_ci DASD_ECKD_CCW_READ_COUNT_MT, base, startdev, 0, count, 254762306a36Sopenharmony_ci sizeof(struct eckd_count), 254862306a36Sopenharmony_ci count * sizeof(struct eckd_count), 0, rpt); 254962306a36Sopenharmony_ci if (rc) 255062306a36Sopenharmony_ci goto out_err; 255162306a36Sopenharmony_ci 255262306a36Sopenharmony_ci for (i = 0; i < count; i++) { 255362306a36Sopenharmony_ci last_tidaw = itcw_add_tidaw(itcw, 0, fmt_buffer++, 255462306a36Sopenharmony_ci sizeof(struct eckd_count)); 255562306a36Sopenharmony_ci if (IS_ERR(last_tidaw)) { 255662306a36Sopenharmony_ci rc = -EINVAL; 255762306a36Sopenharmony_ci goto out_err; 255862306a36Sopenharmony_ci } 255962306a36Sopenharmony_ci } 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci last_tidaw->flags |= TIDAW_FLAGS_LAST; 256262306a36Sopenharmony_ci itcw_finalize(itcw); 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci cqr->cpmode = 1; 256562306a36Sopenharmony_ci cqr->startdev = startdev; 256662306a36Sopenharmony_ci cqr->memdev = startdev; 256762306a36Sopenharmony_ci cqr->basedev = base; 256862306a36Sopenharmony_ci cqr->retries = startdev->default_retries; 256962306a36Sopenharmony_ci cqr->expires = startdev->default_expires * HZ; 257062306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 257162306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 257262306a36Sopenharmony_ci /* Set flags to suppress output for expected errors */ 257362306a36Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags); 257462306a36Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags); 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_ci return cqr; 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ciout_err: 257962306a36Sopenharmony_ci dasd_sfree_request(cqr, startdev); 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_ci return ERR_PTR(rc); 258262306a36Sopenharmony_ci} 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci/* 258562306a36Sopenharmony_ci * Build the CCW request for the format check 258662306a36Sopenharmony_ci */ 258762306a36Sopenharmony_cistatic struct dasd_ccw_req * 258862306a36Sopenharmony_cidasd_eckd_build_check(struct dasd_device *base, struct format_data_t *fdata, 258962306a36Sopenharmony_ci int enable_pav, struct eckd_count *fmt_buffer, int rpt) 259062306a36Sopenharmony_ci{ 259162306a36Sopenharmony_ci struct dasd_eckd_private *start_priv; 259262306a36Sopenharmony_ci struct dasd_eckd_private *base_priv; 259362306a36Sopenharmony_ci struct dasd_device *startdev = NULL; 259462306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 259562306a36Sopenharmony_ci struct ccw1 *ccw; 259662306a36Sopenharmony_ci void *data; 259762306a36Sopenharmony_ci int cplength, datasize; 259862306a36Sopenharmony_ci int use_prefix; 259962306a36Sopenharmony_ci int count; 260062306a36Sopenharmony_ci int i; 260162306a36Sopenharmony_ci 260262306a36Sopenharmony_ci if (enable_pav) 260362306a36Sopenharmony_ci startdev = dasd_alias_get_start_dev(base); 260462306a36Sopenharmony_ci 260562306a36Sopenharmony_ci if (!startdev) 260662306a36Sopenharmony_ci startdev = base; 260762306a36Sopenharmony_ci 260862306a36Sopenharmony_ci start_priv = startdev->private; 260962306a36Sopenharmony_ci base_priv = base->private; 261062306a36Sopenharmony_ci 261162306a36Sopenharmony_ci count = rpt * (fdata->stop_unit - fdata->start_unit + 1); 261262306a36Sopenharmony_ci 261362306a36Sopenharmony_ci use_prefix = base_priv->features.feature[8] & 0x01; 261462306a36Sopenharmony_ci 261562306a36Sopenharmony_ci if (use_prefix) { 261662306a36Sopenharmony_ci cplength = 1; 261762306a36Sopenharmony_ci datasize = sizeof(struct PFX_eckd_data); 261862306a36Sopenharmony_ci } else { 261962306a36Sopenharmony_ci cplength = 2; 262062306a36Sopenharmony_ci datasize = sizeof(struct DE_eckd_data) + 262162306a36Sopenharmony_ci sizeof(struct LO_eckd_data); 262262306a36Sopenharmony_ci } 262362306a36Sopenharmony_ci cplength += count; 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_ci cqr = dasd_fmalloc_request(DASD_ECKD_MAGIC, cplength, datasize, startdev); 262662306a36Sopenharmony_ci if (IS_ERR(cqr)) 262762306a36Sopenharmony_ci return cqr; 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_ci start_priv->count++; 263062306a36Sopenharmony_ci data = cqr->data; 263162306a36Sopenharmony_ci ccw = cqr->cpaddr; 263262306a36Sopenharmony_ci 263362306a36Sopenharmony_ci if (use_prefix) { 263462306a36Sopenharmony_ci prefix_LRE(ccw++, data, fdata->start_unit, fdata->stop_unit, 263562306a36Sopenharmony_ci DASD_ECKD_CCW_READ_COUNT, base, startdev, 1, 0, 263662306a36Sopenharmony_ci count, 0, 0); 263762306a36Sopenharmony_ci } else { 263862306a36Sopenharmony_ci define_extent(ccw++, data, fdata->start_unit, fdata->stop_unit, 263962306a36Sopenharmony_ci DASD_ECKD_CCW_READ_COUNT, startdev, 0); 264062306a36Sopenharmony_ci 264162306a36Sopenharmony_ci data += sizeof(struct DE_eckd_data); 264262306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 264362306a36Sopenharmony_ci 264462306a36Sopenharmony_ci locate_record(ccw++, data, fdata->start_unit, 0, count, 264562306a36Sopenharmony_ci DASD_ECKD_CCW_READ_COUNT, base, 0); 264662306a36Sopenharmony_ci } 264762306a36Sopenharmony_ci 264862306a36Sopenharmony_ci for (i = 0; i < count; i++) { 264962306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 265062306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT; 265162306a36Sopenharmony_ci ccw->flags = CCW_FLAG_SLI; 265262306a36Sopenharmony_ci ccw->count = 8; 265362306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(fmt_buffer); 265462306a36Sopenharmony_ci ccw++; 265562306a36Sopenharmony_ci fmt_buffer++; 265662306a36Sopenharmony_ci } 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci cqr->startdev = startdev; 265962306a36Sopenharmony_ci cqr->memdev = startdev; 266062306a36Sopenharmony_ci cqr->basedev = base; 266162306a36Sopenharmony_ci cqr->retries = DASD_RETRIES; 266262306a36Sopenharmony_ci cqr->expires = startdev->default_expires * HZ; 266362306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 266462306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 266562306a36Sopenharmony_ci /* Set flags to suppress output for expected errors */ 266662306a36Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags); 266762306a36Sopenharmony_ci 266862306a36Sopenharmony_ci return cqr; 266962306a36Sopenharmony_ci} 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_cistatic struct dasd_ccw_req * 267262306a36Sopenharmony_cidasd_eckd_build_format(struct dasd_device *base, struct dasd_device *startdev, 267362306a36Sopenharmony_ci struct format_data_t *fdata, int enable_pav) 267462306a36Sopenharmony_ci{ 267562306a36Sopenharmony_ci struct dasd_eckd_private *base_priv; 267662306a36Sopenharmony_ci struct dasd_eckd_private *start_priv; 267762306a36Sopenharmony_ci struct dasd_ccw_req *fcp; 267862306a36Sopenharmony_ci struct eckd_count *ect; 267962306a36Sopenharmony_ci struct ch_t address; 268062306a36Sopenharmony_ci struct ccw1 *ccw; 268162306a36Sopenharmony_ci void *data; 268262306a36Sopenharmony_ci int rpt; 268362306a36Sopenharmony_ci int cplength, datasize; 268462306a36Sopenharmony_ci int i, j; 268562306a36Sopenharmony_ci int intensity = 0; 268662306a36Sopenharmony_ci int r0_perm; 268762306a36Sopenharmony_ci int nr_tracks; 268862306a36Sopenharmony_ci int use_prefix; 268962306a36Sopenharmony_ci 269062306a36Sopenharmony_ci if (enable_pav) 269162306a36Sopenharmony_ci startdev = dasd_alias_get_start_dev(base); 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci if (!startdev) 269462306a36Sopenharmony_ci startdev = base; 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci start_priv = startdev->private; 269762306a36Sopenharmony_ci base_priv = base->private; 269862306a36Sopenharmony_ci 269962306a36Sopenharmony_ci rpt = recs_per_track(&base_priv->rdc_data, 0, fdata->blksize); 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_ci nr_tracks = fdata->stop_unit - fdata->start_unit + 1; 270262306a36Sopenharmony_ci 270362306a36Sopenharmony_ci /* 270462306a36Sopenharmony_ci * fdata->intensity is a bit string that tells us what to do: 270562306a36Sopenharmony_ci * Bit 0: write record zero 270662306a36Sopenharmony_ci * Bit 1: write home address, currently not supported 270762306a36Sopenharmony_ci * Bit 2: invalidate tracks 270862306a36Sopenharmony_ci * Bit 3: use OS/390 compatible disk layout (cdl) 270962306a36Sopenharmony_ci * Bit 4: do not allow storage subsystem to modify record zero 271062306a36Sopenharmony_ci * Only some bit combinations do make sense. 271162306a36Sopenharmony_ci */ 271262306a36Sopenharmony_ci if (fdata->intensity & 0x10) { 271362306a36Sopenharmony_ci r0_perm = 0; 271462306a36Sopenharmony_ci intensity = fdata->intensity & ~0x10; 271562306a36Sopenharmony_ci } else { 271662306a36Sopenharmony_ci r0_perm = 1; 271762306a36Sopenharmony_ci intensity = fdata->intensity; 271862306a36Sopenharmony_ci } 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_ci use_prefix = base_priv->features.feature[8] & 0x01; 272162306a36Sopenharmony_ci 272262306a36Sopenharmony_ci switch (intensity) { 272362306a36Sopenharmony_ci case 0x00: /* Normal format */ 272462306a36Sopenharmony_ci case 0x08: /* Normal format, use cdl. */ 272562306a36Sopenharmony_ci cplength = 2 + (rpt*nr_tracks); 272662306a36Sopenharmony_ci if (use_prefix) 272762306a36Sopenharmony_ci datasize = sizeof(struct PFX_eckd_data) + 272862306a36Sopenharmony_ci sizeof(struct LO_eckd_data) + 272962306a36Sopenharmony_ci rpt * nr_tracks * sizeof(struct eckd_count); 273062306a36Sopenharmony_ci else 273162306a36Sopenharmony_ci datasize = sizeof(struct DE_eckd_data) + 273262306a36Sopenharmony_ci sizeof(struct LO_eckd_data) + 273362306a36Sopenharmony_ci rpt * nr_tracks * sizeof(struct eckd_count); 273462306a36Sopenharmony_ci break; 273562306a36Sopenharmony_ci case 0x01: /* Write record zero and format track. */ 273662306a36Sopenharmony_ci case 0x09: /* Write record zero and format track, use cdl. */ 273762306a36Sopenharmony_ci cplength = 2 + rpt * nr_tracks; 273862306a36Sopenharmony_ci if (use_prefix) 273962306a36Sopenharmony_ci datasize = sizeof(struct PFX_eckd_data) + 274062306a36Sopenharmony_ci sizeof(struct LO_eckd_data) + 274162306a36Sopenharmony_ci sizeof(struct eckd_count) + 274262306a36Sopenharmony_ci rpt * nr_tracks * sizeof(struct eckd_count); 274362306a36Sopenharmony_ci else 274462306a36Sopenharmony_ci datasize = sizeof(struct DE_eckd_data) + 274562306a36Sopenharmony_ci sizeof(struct LO_eckd_data) + 274662306a36Sopenharmony_ci sizeof(struct eckd_count) + 274762306a36Sopenharmony_ci rpt * nr_tracks * sizeof(struct eckd_count); 274862306a36Sopenharmony_ci break; 274962306a36Sopenharmony_ci case 0x04: /* Invalidate track. */ 275062306a36Sopenharmony_ci case 0x0c: /* Invalidate track, use cdl. */ 275162306a36Sopenharmony_ci cplength = 3; 275262306a36Sopenharmony_ci if (use_prefix) 275362306a36Sopenharmony_ci datasize = sizeof(struct PFX_eckd_data) + 275462306a36Sopenharmony_ci sizeof(struct LO_eckd_data) + 275562306a36Sopenharmony_ci sizeof(struct eckd_count); 275662306a36Sopenharmony_ci else 275762306a36Sopenharmony_ci datasize = sizeof(struct DE_eckd_data) + 275862306a36Sopenharmony_ci sizeof(struct LO_eckd_data) + 275962306a36Sopenharmony_ci sizeof(struct eckd_count); 276062306a36Sopenharmony_ci break; 276162306a36Sopenharmony_ci default: 276262306a36Sopenharmony_ci dev_warn(&startdev->cdev->dev, 276362306a36Sopenharmony_ci "An I/O control call used incorrect flags 0x%x\n", 276462306a36Sopenharmony_ci fdata->intensity); 276562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 276662306a36Sopenharmony_ci } 276762306a36Sopenharmony_ci 276862306a36Sopenharmony_ci fcp = dasd_fmalloc_request(DASD_ECKD_MAGIC, cplength, datasize, startdev); 276962306a36Sopenharmony_ci if (IS_ERR(fcp)) 277062306a36Sopenharmony_ci return fcp; 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_ci start_priv->count++; 277362306a36Sopenharmony_ci data = fcp->data; 277462306a36Sopenharmony_ci ccw = fcp->cpaddr; 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_ci switch (intensity & ~0x08) { 277762306a36Sopenharmony_ci case 0x00: /* Normal format. */ 277862306a36Sopenharmony_ci if (use_prefix) { 277962306a36Sopenharmony_ci prefix(ccw++, (struct PFX_eckd_data *) data, 278062306a36Sopenharmony_ci fdata->start_unit, fdata->stop_unit, 278162306a36Sopenharmony_ci DASD_ECKD_CCW_WRITE_CKD, base, startdev); 278262306a36Sopenharmony_ci /* grant subsystem permission to format R0 */ 278362306a36Sopenharmony_ci if (r0_perm) 278462306a36Sopenharmony_ci ((struct PFX_eckd_data *)data) 278562306a36Sopenharmony_ci ->define_extent.ga_extended |= 0x04; 278662306a36Sopenharmony_ci data += sizeof(struct PFX_eckd_data); 278762306a36Sopenharmony_ci } else { 278862306a36Sopenharmony_ci define_extent(ccw++, (struct DE_eckd_data *) data, 278962306a36Sopenharmony_ci fdata->start_unit, fdata->stop_unit, 279062306a36Sopenharmony_ci DASD_ECKD_CCW_WRITE_CKD, startdev, 0); 279162306a36Sopenharmony_ci /* grant subsystem permission to format R0 */ 279262306a36Sopenharmony_ci if (r0_perm) 279362306a36Sopenharmony_ci ((struct DE_eckd_data *) data) 279462306a36Sopenharmony_ci ->ga_extended |= 0x04; 279562306a36Sopenharmony_ci data += sizeof(struct DE_eckd_data); 279662306a36Sopenharmony_ci } 279762306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 279862306a36Sopenharmony_ci locate_record(ccw++, (struct LO_eckd_data *) data, 279962306a36Sopenharmony_ci fdata->start_unit, 0, rpt*nr_tracks, 280062306a36Sopenharmony_ci DASD_ECKD_CCW_WRITE_CKD, base, 280162306a36Sopenharmony_ci fdata->blksize); 280262306a36Sopenharmony_ci data += sizeof(struct LO_eckd_data); 280362306a36Sopenharmony_ci break; 280462306a36Sopenharmony_ci case 0x01: /* Write record zero + format track. */ 280562306a36Sopenharmony_ci if (use_prefix) { 280662306a36Sopenharmony_ci prefix(ccw++, (struct PFX_eckd_data *) data, 280762306a36Sopenharmony_ci fdata->start_unit, fdata->stop_unit, 280862306a36Sopenharmony_ci DASD_ECKD_CCW_WRITE_RECORD_ZERO, 280962306a36Sopenharmony_ci base, startdev); 281062306a36Sopenharmony_ci data += sizeof(struct PFX_eckd_data); 281162306a36Sopenharmony_ci } else { 281262306a36Sopenharmony_ci define_extent(ccw++, (struct DE_eckd_data *) data, 281362306a36Sopenharmony_ci fdata->start_unit, fdata->stop_unit, 281462306a36Sopenharmony_ci DASD_ECKD_CCW_WRITE_RECORD_ZERO, startdev, 0); 281562306a36Sopenharmony_ci data += sizeof(struct DE_eckd_data); 281662306a36Sopenharmony_ci } 281762306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 281862306a36Sopenharmony_ci locate_record(ccw++, (struct LO_eckd_data *) data, 281962306a36Sopenharmony_ci fdata->start_unit, 0, rpt * nr_tracks + 1, 282062306a36Sopenharmony_ci DASD_ECKD_CCW_WRITE_RECORD_ZERO, base, 282162306a36Sopenharmony_ci base->block->bp_block); 282262306a36Sopenharmony_ci data += sizeof(struct LO_eckd_data); 282362306a36Sopenharmony_ci break; 282462306a36Sopenharmony_ci case 0x04: /* Invalidate track. */ 282562306a36Sopenharmony_ci if (use_prefix) { 282662306a36Sopenharmony_ci prefix(ccw++, (struct PFX_eckd_data *) data, 282762306a36Sopenharmony_ci fdata->start_unit, fdata->stop_unit, 282862306a36Sopenharmony_ci DASD_ECKD_CCW_WRITE_CKD, base, startdev); 282962306a36Sopenharmony_ci data += sizeof(struct PFX_eckd_data); 283062306a36Sopenharmony_ci } else { 283162306a36Sopenharmony_ci define_extent(ccw++, (struct DE_eckd_data *) data, 283262306a36Sopenharmony_ci fdata->start_unit, fdata->stop_unit, 283362306a36Sopenharmony_ci DASD_ECKD_CCW_WRITE_CKD, startdev, 0); 283462306a36Sopenharmony_ci data += sizeof(struct DE_eckd_data); 283562306a36Sopenharmony_ci } 283662306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 283762306a36Sopenharmony_ci locate_record(ccw++, (struct LO_eckd_data *) data, 283862306a36Sopenharmony_ci fdata->start_unit, 0, 1, 283962306a36Sopenharmony_ci DASD_ECKD_CCW_WRITE_CKD, base, 8); 284062306a36Sopenharmony_ci data += sizeof(struct LO_eckd_data); 284162306a36Sopenharmony_ci break; 284262306a36Sopenharmony_ci } 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_ci for (j = 0; j < nr_tracks; j++) { 284562306a36Sopenharmony_ci /* calculate cylinder and head for the current track */ 284662306a36Sopenharmony_ci set_ch_t(&address, 284762306a36Sopenharmony_ci (fdata->start_unit + j) / 284862306a36Sopenharmony_ci base_priv->rdc_data.trk_per_cyl, 284962306a36Sopenharmony_ci (fdata->start_unit + j) % 285062306a36Sopenharmony_ci base_priv->rdc_data.trk_per_cyl); 285162306a36Sopenharmony_ci if (intensity & 0x01) { /* write record zero */ 285262306a36Sopenharmony_ci ect = (struct eckd_count *) data; 285362306a36Sopenharmony_ci data += sizeof(struct eckd_count); 285462306a36Sopenharmony_ci ect->cyl = address.cyl; 285562306a36Sopenharmony_ci ect->head = address.head; 285662306a36Sopenharmony_ci ect->record = 0; 285762306a36Sopenharmony_ci ect->kl = 0; 285862306a36Sopenharmony_ci ect->dl = 8; 285962306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 286062306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_WRITE_RECORD_ZERO; 286162306a36Sopenharmony_ci ccw->flags = CCW_FLAG_SLI; 286262306a36Sopenharmony_ci ccw->count = 8; 286362306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(ect); 286462306a36Sopenharmony_ci ccw++; 286562306a36Sopenharmony_ci } 286662306a36Sopenharmony_ci if ((intensity & ~0x08) & 0x04) { /* erase track */ 286762306a36Sopenharmony_ci ect = (struct eckd_count *) data; 286862306a36Sopenharmony_ci data += sizeof(struct eckd_count); 286962306a36Sopenharmony_ci ect->cyl = address.cyl; 287062306a36Sopenharmony_ci ect->head = address.head; 287162306a36Sopenharmony_ci ect->record = 1; 287262306a36Sopenharmony_ci ect->kl = 0; 287362306a36Sopenharmony_ci ect->dl = 0; 287462306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 287562306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD; 287662306a36Sopenharmony_ci ccw->flags = CCW_FLAG_SLI; 287762306a36Sopenharmony_ci ccw->count = 8; 287862306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(ect); 287962306a36Sopenharmony_ci } else { /* write remaining records */ 288062306a36Sopenharmony_ci for (i = 0; i < rpt; i++) { 288162306a36Sopenharmony_ci ect = (struct eckd_count *) data; 288262306a36Sopenharmony_ci data += sizeof(struct eckd_count); 288362306a36Sopenharmony_ci ect->cyl = address.cyl; 288462306a36Sopenharmony_ci ect->head = address.head; 288562306a36Sopenharmony_ci ect->record = i + 1; 288662306a36Sopenharmony_ci ect->kl = 0; 288762306a36Sopenharmony_ci ect->dl = fdata->blksize; 288862306a36Sopenharmony_ci /* 288962306a36Sopenharmony_ci * Check for special tracks 0-1 289062306a36Sopenharmony_ci * when formatting CDL 289162306a36Sopenharmony_ci */ 289262306a36Sopenharmony_ci if ((intensity & 0x08) && 289362306a36Sopenharmony_ci address.cyl == 0 && address.head == 0) { 289462306a36Sopenharmony_ci if (i < 3) { 289562306a36Sopenharmony_ci ect->kl = 4; 289662306a36Sopenharmony_ci ect->dl = sizes_trk0[i] - 4; 289762306a36Sopenharmony_ci } 289862306a36Sopenharmony_ci } 289962306a36Sopenharmony_ci if ((intensity & 0x08) && 290062306a36Sopenharmony_ci address.cyl == 0 && address.head == 1) { 290162306a36Sopenharmony_ci ect->kl = 44; 290262306a36Sopenharmony_ci ect->dl = LABEL_SIZE - 44; 290362306a36Sopenharmony_ci } 290462306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 290562306a36Sopenharmony_ci if (i != 0 || j == 0) 290662306a36Sopenharmony_ci ccw->cmd_code = 290762306a36Sopenharmony_ci DASD_ECKD_CCW_WRITE_CKD; 290862306a36Sopenharmony_ci else 290962306a36Sopenharmony_ci ccw->cmd_code = 291062306a36Sopenharmony_ci DASD_ECKD_CCW_WRITE_CKD_MT; 291162306a36Sopenharmony_ci ccw->flags = CCW_FLAG_SLI; 291262306a36Sopenharmony_ci ccw->count = 8; 291362306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(ect); 291462306a36Sopenharmony_ci ccw++; 291562306a36Sopenharmony_ci } 291662306a36Sopenharmony_ci } 291762306a36Sopenharmony_ci } 291862306a36Sopenharmony_ci 291962306a36Sopenharmony_ci fcp->startdev = startdev; 292062306a36Sopenharmony_ci fcp->memdev = startdev; 292162306a36Sopenharmony_ci fcp->basedev = base; 292262306a36Sopenharmony_ci fcp->retries = 256; 292362306a36Sopenharmony_ci fcp->expires = startdev->default_expires * HZ; 292462306a36Sopenharmony_ci fcp->buildclk = get_tod_clock(); 292562306a36Sopenharmony_ci fcp->status = DASD_CQR_FILLED; 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_ci return fcp; 292862306a36Sopenharmony_ci} 292962306a36Sopenharmony_ci 293062306a36Sopenharmony_ci/* 293162306a36Sopenharmony_ci * Wrapper function to build a CCW request depending on input data 293262306a36Sopenharmony_ci */ 293362306a36Sopenharmony_cistatic struct dasd_ccw_req * 293462306a36Sopenharmony_cidasd_eckd_format_build_ccw_req(struct dasd_device *base, 293562306a36Sopenharmony_ci struct format_data_t *fdata, int enable_pav, 293662306a36Sopenharmony_ci int tpm, struct eckd_count *fmt_buffer, int rpt) 293762306a36Sopenharmony_ci{ 293862306a36Sopenharmony_ci struct dasd_ccw_req *ccw_req; 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci if (!fmt_buffer) { 294162306a36Sopenharmony_ci ccw_req = dasd_eckd_build_format(base, NULL, fdata, enable_pav); 294262306a36Sopenharmony_ci } else { 294362306a36Sopenharmony_ci if (tpm) 294462306a36Sopenharmony_ci ccw_req = dasd_eckd_build_check_tcw(base, fdata, 294562306a36Sopenharmony_ci enable_pav, 294662306a36Sopenharmony_ci fmt_buffer, rpt); 294762306a36Sopenharmony_ci else 294862306a36Sopenharmony_ci ccw_req = dasd_eckd_build_check(base, fdata, enable_pav, 294962306a36Sopenharmony_ci fmt_buffer, rpt); 295062306a36Sopenharmony_ci } 295162306a36Sopenharmony_ci 295262306a36Sopenharmony_ci return ccw_req; 295362306a36Sopenharmony_ci} 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_ci/* 295662306a36Sopenharmony_ci * Sanity checks on format_data 295762306a36Sopenharmony_ci */ 295862306a36Sopenharmony_cistatic int dasd_eckd_format_sanity_checks(struct dasd_device *base, 295962306a36Sopenharmony_ci struct format_data_t *fdata) 296062306a36Sopenharmony_ci{ 296162306a36Sopenharmony_ci struct dasd_eckd_private *private = base->private; 296262306a36Sopenharmony_ci 296362306a36Sopenharmony_ci if (fdata->start_unit >= 296462306a36Sopenharmony_ci (private->real_cyl * private->rdc_data.trk_per_cyl)) { 296562306a36Sopenharmony_ci dev_warn(&base->cdev->dev, 296662306a36Sopenharmony_ci "Start track number %u used in formatting is too big\n", 296762306a36Sopenharmony_ci fdata->start_unit); 296862306a36Sopenharmony_ci return -EINVAL; 296962306a36Sopenharmony_ci } 297062306a36Sopenharmony_ci if (fdata->stop_unit >= 297162306a36Sopenharmony_ci (private->real_cyl * private->rdc_data.trk_per_cyl)) { 297262306a36Sopenharmony_ci dev_warn(&base->cdev->dev, 297362306a36Sopenharmony_ci "Stop track number %u used in formatting is too big\n", 297462306a36Sopenharmony_ci fdata->stop_unit); 297562306a36Sopenharmony_ci return -EINVAL; 297662306a36Sopenharmony_ci } 297762306a36Sopenharmony_ci if (fdata->start_unit > fdata->stop_unit) { 297862306a36Sopenharmony_ci dev_warn(&base->cdev->dev, 297962306a36Sopenharmony_ci "Start track %u used in formatting exceeds end track\n", 298062306a36Sopenharmony_ci fdata->start_unit); 298162306a36Sopenharmony_ci return -EINVAL; 298262306a36Sopenharmony_ci } 298362306a36Sopenharmony_ci if (dasd_check_blocksize(fdata->blksize) != 0) { 298462306a36Sopenharmony_ci dev_warn(&base->cdev->dev, 298562306a36Sopenharmony_ci "The DASD cannot be formatted with block size %u\n", 298662306a36Sopenharmony_ci fdata->blksize); 298762306a36Sopenharmony_ci return -EINVAL; 298862306a36Sopenharmony_ci } 298962306a36Sopenharmony_ci return 0; 299062306a36Sopenharmony_ci} 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_ci/* 299362306a36Sopenharmony_ci * This function will process format_data originally coming from an IOCTL 299462306a36Sopenharmony_ci */ 299562306a36Sopenharmony_cistatic int dasd_eckd_format_process_data(struct dasd_device *base, 299662306a36Sopenharmony_ci struct format_data_t *fdata, 299762306a36Sopenharmony_ci int enable_pav, int tpm, 299862306a36Sopenharmony_ci struct eckd_count *fmt_buffer, int rpt, 299962306a36Sopenharmony_ci struct irb *irb) 300062306a36Sopenharmony_ci{ 300162306a36Sopenharmony_ci struct dasd_eckd_private *private = base->private; 300262306a36Sopenharmony_ci struct dasd_ccw_req *cqr, *n; 300362306a36Sopenharmony_ci struct list_head format_queue; 300462306a36Sopenharmony_ci struct dasd_device *device; 300562306a36Sopenharmony_ci char *sense = NULL; 300662306a36Sopenharmony_ci int old_start, old_stop, format_step; 300762306a36Sopenharmony_ci int step, retry; 300862306a36Sopenharmony_ci int rc; 300962306a36Sopenharmony_ci 301062306a36Sopenharmony_ci rc = dasd_eckd_format_sanity_checks(base, fdata); 301162306a36Sopenharmony_ci if (rc) 301262306a36Sopenharmony_ci return rc; 301362306a36Sopenharmony_ci 301462306a36Sopenharmony_ci INIT_LIST_HEAD(&format_queue); 301562306a36Sopenharmony_ci 301662306a36Sopenharmony_ci old_start = fdata->start_unit; 301762306a36Sopenharmony_ci old_stop = fdata->stop_unit; 301862306a36Sopenharmony_ci 301962306a36Sopenharmony_ci if (!tpm && fmt_buffer != NULL) { 302062306a36Sopenharmony_ci /* Command Mode / Format Check */ 302162306a36Sopenharmony_ci format_step = 1; 302262306a36Sopenharmony_ci } else if (tpm && fmt_buffer != NULL) { 302362306a36Sopenharmony_ci /* Transport Mode / Format Check */ 302462306a36Sopenharmony_ci format_step = DASD_CQR_MAX_CCW / rpt; 302562306a36Sopenharmony_ci } else { 302662306a36Sopenharmony_ci /* Normal Formatting */ 302762306a36Sopenharmony_ci format_step = DASD_CQR_MAX_CCW / 302862306a36Sopenharmony_ci recs_per_track(&private->rdc_data, 0, fdata->blksize); 302962306a36Sopenharmony_ci } 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci do { 303262306a36Sopenharmony_ci retry = 0; 303362306a36Sopenharmony_ci while (fdata->start_unit <= old_stop) { 303462306a36Sopenharmony_ci step = fdata->stop_unit - fdata->start_unit + 1; 303562306a36Sopenharmony_ci if (step > format_step) { 303662306a36Sopenharmony_ci fdata->stop_unit = 303762306a36Sopenharmony_ci fdata->start_unit + format_step - 1; 303862306a36Sopenharmony_ci } 303962306a36Sopenharmony_ci 304062306a36Sopenharmony_ci cqr = dasd_eckd_format_build_ccw_req(base, fdata, 304162306a36Sopenharmony_ci enable_pav, tpm, 304262306a36Sopenharmony_ci fmt_buffer, rpt); 304362306a36Sopenharmony_ci if (IS_ERR(cqr)) { 304462306a36Sopenharmony_ci rc = PTR_ERR(cqr); 304562306a36Sopenharmony_ci if (rc == -ENOMEM) { 304662306a36Sopenharmony_ci if (list_empty(&format_queue)) 304762306a36Sopenharmony_ci goto out; 304862306a36Sopenharmony_ci /* 304962306a36Sopenharmony_ci * not enough memory available, start 305062306a36Sopenharmony_ci * requests retry after first requests 305162306a36Sopenharmony_ci * were finished 305262306a36Sopenharmony_ci */ 305362306a36Sopenharmony_ci retry = 1; 305462306a36Sopenharmony_ci break; 305562306a36Sopenharmony_ci } 305662306a36Sopenharmony_ci goto out_err; 305762306a36Sopenharmony_ci } 305862306a36Sopenharmony_ci list_add_tail(&cqr->blocklist, &format_queue); 305962306a36Sopenharmony_ci 306062306a36Sopenharmony_ci if (fmt_buffer) { 306162306a36Sopenharmony_ci step = fdata->stop_unit - fdata->start_unit + 1; 306262306a36Sopenharmony_ci fmt_buffer += rpt * step; 306362306a36Sopenharmony_ci } 306462306a36Sopenharmony_ci fdata->start_unit = fdata->stop_unit + 1; 306562306a36Sopenharmony_ci fdata->stop_unit = old_stop; 306662306a36Sopenharmony_ci } 306762306a36Sopenharmony_ci 306862306a36Sopenharmony_ci rc = dasd_sleep_on_queue(&format_queue); 306962306a36Sopenharmony_ci 307062306a36Sopenharmony_ciout_err: 307162306a36Sopenharmony_ci list_for_each_entry_safe(cqr, n, &format_queue, blocklist) { 307262306a36Sopenharmony_ci device = cqr->startdev; 307362306a36Sopenharmony_ci private = device->private; 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci if (cqr->status == DASD_CQR_FAILED) { 307662306a36Sopenharmony_ci /* 307762306a36Sopenharmony_ci * Only get sense data if called by format 307862306a36Sopenharmony_ci * check 307962306a36Sopenharmony_ci */ 308062306a36Sopenharmony_ci if (fmt_buffer && irb) { 308162306a36Sopenharmony_ci sense = dasd_get_sense(&cqr->irb); 308262306a36Sopenharmony_ci memcpy(irb, &cqr->irb, sizeof(*irb)); 308362306a36Sopenharmony_ci } 308462306a36Sopenharmony_ci rc = -EIO; 308562306a36Sopenharmony_ci } 308662306a36Sopenharmony_ci list_del_init(&cqr->blocklist); 308762306a36Sopenharmony_ci dasd_ffree_request(cqr, device); 308862306a36Sopenharmony_ci private->count--; 308962306a36Sopenharmony_ci } 309062306a36Sopenharmony_ci 309162306a36Sopenharmony_ci if (rc && rc != -EIO) 309262306a36Sopenharmony_ci goto out; 309362306a36Sopenharmony_ci if (rc == -EIO) { 309462306a36Sopenharmony_ci /* 309562306a36Sopenharmony_ci * In case fewer than the expected records are on the 309662306a36Sopenharmony_ci * track, we will most likely get a 'No Record Found' 309762306a36Sopenharmony_ci * error (in command mode) or a 'File Protected' error 309862306a36Sopenharmony_ci * (in transport mode). Those particular cases shouldn't 309962306a36Sopenharmony_ci * pass the -EIO to the IOCTL, therefore reset the rc 310062306a36Sopenharmony_ci * and continue. 310162306a36Sopenharmony_ci */ 310262306a36Sopenharmony_ci if (sense && 310362306a36Sopenharmony_ci (sense[1] & SNS1_NO_REC_FOUND || 310462306a36Sopenharmony_ci sense[1] & SNS1_FILE_PROTECTED)) 310562306a36Sopenharmony_ci retry = 1; 310662306a36Sopenharmony_ci else 310762306a36Sopenharmony_ci goto out; 310862306a36Sopenharmony_ci } 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci } while (retry); 311162306a36Sopenharmony_ci 311262306a36Sopenharmony_ciout: 311362306a36Sopenharmony_ci fdata->start_unit = old_start; 311462306a36Sopenharmony_ci fdata->stop_unit = old_stop; 311562306a36Sopenharmony_ci 311662306a36Sopenharmony_ci return rc; 311762306a36Sopenharmony_ci} 311862306a36Sopenharmony_ci 311962306a36Sopenharmony_cistatic int dasd_eckd_format_device(struct dasd_device *base, 312062306a36Sopenharmony_ci struct format_data_t *fdata, int enable_pav) 312162306a36Sopenharmony_ci{ 312262306a36Sopenharmony_ci return dasd_eckd_format_process_data(base, fdata, enable_pav, 0, NULL, 312362306a36Sopenharmony_ci 0, NULL); 312462306a36Sopenharmony_ci} 312562306a36Sopenharmony_ci 312662306a36Sopenharmony_cistatic bool test_and_set_format_track(struct dasd_format_entry *to_format, 312762306a36Sopenharmony_ci struct dasd_ccw_req *cqr) 312862306a36Sopenharmony_ci{ 312962306a36Sopenharmony_ci struct dasd_block *block = cqr->block; 313062306a36Sopenharmony_ci struct dasd_format_entry *format; 313162306a36Sopenharmony_ci unsigned long flags; 313262306a36Sopenharmony_ci bool rc = false; 313362306a36Sopenharmony_ci 313462306a36Sopenharmony_ci spin_lock_irqsave(&block->format_lock, flags); 313562306a36Sopenharmony_ci if (cqr->trkcount != atomic_read(&block->trkcount)) { 313662306a36Sopenharmony_ci /* 313762306a36Sopenharmony_ci * The number of formatted tracks has changed after request 313862306a36Sopenharmony_ci * start and we can not tell if the current track was involved. 313962306a36Sopenharmony_ci * To avoid data corruption treat it as if the current track is 314062306a36Sopenharmony_ci * involved 314162306a36Sopenharmony_ci */ 314262306a36Sopenharmony_ci rc = true; 314362306a36Sopenharmony_ci goto out; 314462306a36Sopenharmony_ci } 314562306a36Sopenharmony_ci list_for_each_entry(format, &block->format_list, list) { 314662306a36Sopenharmony_ci if (format->track == to_format->track) { 314762306a36Sopenharmony_ci rc = true; 314862306a36Sopenharmony_ci goto out; 314962306a36Sopenharmony_ci } 315062306a36Sopenharmony_ci } 315162306a36Sopenharmony_ci list_add_tail(&to_format->list, &block->format_list); 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_ciout: 315462306a36Sopenharmony_ci spin_unlock_irqrestore(&block->format_lock, flags); 315562306a36Sopenharmony_ci return rc; 315662306a36Sopenharmony_ci} 315762306a36Sopenharmony_ci 315862306a36Sopenharmony_cistatic void clear_format_track(struct dasd_format_entry *format, 315962306a36Sopenharmony_ci struct dasd_block *block) 316062306a36Sopenharmony_ci{ 316162306a36Sopenharmony_ci unsigned long flags; 316262306a36Sopenharmony_ci 316362306a36Sopenharmony_ci spin_lock_irqsave(&block->format_lock, flags); 316462306a36Sopenharmony_ci atomic_inc(&block->trkcount); 316562306a36Sopenharmony_ci list_del_init(&format->list); 316662306a36Sopenharmony_ci spin_unlock_irqrestore(&block->format_lock, flags); 316762306a36Sopenharmony_ci} 316862306a36Sopenharmony_ci 316962306a36Sopenharmony_ci/* 317062306a36Sopenharmony_ci * Callback function to free ESE format requests. 317162306a36Sopenharmony_ci */ 317262306a36Sopenharmony_cistatic void dasd_eckd_ese_format_cb(struct dasd_ccw_req *cqr, void *data) 317362306a36Sopenharmony_ci{ 317462306a36Sopenharmony_ci struct dasd_device *device = cqr->startdev; 317562306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 317662306a36Sopenharmony_ci struct dasd_format_entry *format = data; 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ci clear_format_track(format, cqr->basedev->block); 317962306a36Sopenharmony_ci private->count--; 318062306a36Sopenharmony_ci dasd_ffree_request(cqr, device); 318162306a36Sopenharmony_ci} 318262306a36Sopenharmony_ci 318362306a36Sopenharmony_cistatic struct dasd_ccw_req * 318462306a36Sopenharmony_cidasd_eckd_ese_format(struct dasd_device *startdev, struct dasd_ccw_req *cqr, 318562306a36Sopenharmony_ci struct irb *irb) 318662306a36Sopenharmony_ci{ 318762306a36Sopenharmony_ci struct dasd_eckd_private *private; 318862306a36Sopenharmony_ci struct dasd_format_entry *format; 318962306a36Sopenharmony_ci struct format_data_t fdata; 319062306a36Sopenharmony_ci unsigned int recs_per_trk; 319162306a36Sopenharmony_ci struct dasd_ccw_req *fcqr; 319262306a36Sopenharmony_ci struct dasd_device *base; 319362306a36Sopenharmony_ci struct dasd_block *block; 319462306a36Sopenharmony_ci unsigned int blksize; 319562306a36Sopenharmony_ci struct request *req; 319662306a36Sopenharmony_ci sector_t first_trk; 319762306a36Sopenharmony_ci sector_t last_trk; 319862306a36Sopenharmony_ci sector_t curr_trk; 319962306a36Sopenharmony_ci int rc; 320062306a36Sopenharmony_ci 320162306a36Sopenharmony_ci req = dasd_get_callback_data(cqr); 320262306a36Sopenharmony_ci block = cqr->block; 320362306a36Sopenharmony_ci base = block->base; 320462306a36Sopenharmony_ci private = base->private; 320562306a36Sopenharmony_ci blksize = block->bp_block; 320662306a36Sopenharmony_ci recs_per_trk = recs_per_track(&private->rdc_data, 0, blksize); 320762306a36Sopenharmony_ci format = &startdev->format_entry; 320862306a36Sopenharmony_ci 320962306a36Sopenharmony_ci first_trk = blk_rq_pos(req) >> block->s2b_shift; 321062306a36Sopenharmony_ci sector_div(first_trk, recs_per_trk); 321162306a36Sopenharmony_ci last_trk = 321262306a36Sopenharmony_ci (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift; 321362306a36Sopenharmony_ci sector_div(last_trk, recs_per_trk); 321462306a36Sopenharmony_ci rc = dasd_eckd_track_from_irb(irb, base, &curr_trk); 321562306a36Sopenharmony_ci if (rc) 321662306a36Sopenharmony_ci return ERR_PTR(rc); 321762306a36Sopenharmony_ci 321862306a36Sopenharmony_ci if (curr_trk < first_trk || curr_trk > last_trk) { 321962306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, startdev, 322062306a36Sopenharmony_ci "ESE error track %llu not within range %llu - %llu\n", 322162306a36Sopenharmony_ci curr_trk, first_trk, last_trk); 322262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 322362306a36Sopenharmony_ci } 322462306a36Sopenharmony_ci format->track = curr_trk; 322562306a36Sopenharmony_ci /* test if track is already in formatting by another thread */ 322662306a36Sopenharmony_ci if (test_and_set_format_track(format, cqr)) { 322762306a36Sopenharmony_ci /* this is no real error so do not count down retries */ 322862306a36Sopenharmony_ci cqr->retries++; 322962306a36Sopenharmony_ci return ERR_PTR(-EEXIST); 323062306a36Sopenharmony_ci } 323162306a36Sopenharmony_ci 323262306a36Sopenharmony_ci fdata.start_unit = curr_trk; 323362306a36Sopenharmony_ci fdata.stop_unit = curr_trk; 323462306a36Sopenharmony_ci fdata.blksize = blksize; 323562306a36Sopenharmony_ci fdata.intensity = private->uses_cdl ? DASD_FMT_INT_COMPAT : 0; 323662306a36Sopenharmony_ci 323762306a36Sopenharmony_ci rc = dasd_eckd_format_sanity_checks(base, &fdata); 323862306a36Sopenharmony_ci if (rc) 323962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 324062306a36Sopenharmony_ci 324162306a36Sopenharmony_ci /* 324262306a36Sopenharmony_ci * We're building the request with PAV disabled as we're reusing 324362306a36Sopenharmony_ci * the former startdev. 324462306a36Sopenharmony_ci */ 324562306a36Sopenharmony_ci fcqr = dasd_eckd_build_format(base, startdev, &fdata, 0); 324662306a36Sopenharmony_ci if (IS_ERR(fcqr)) 324762306a36Sopenharmony_ci return fcqr; 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_ci fcqr->callback = dasd_eckd_ese_format_cb; 325062306a36Sopenharmony_ci fcqr->callback_data = (void *) format; 325162306a36Sopenharmony_ci 325262306a36Sopenharmony_ci return fcqr; 325362306a36Sopenharmony_ci} 325462306a36Sopenharmony_ci 325562306a36Sopenharmony_ci/* 325662306a36Sopenharmony_ci * When data is read from an unformatted area of an ESE volume, this function 325762306a36Sopenharmony_ci * returns zeroed data and thereby mimics a read of zero data. 325862306a36Sopenharmony_ci * 325962306a36Sopenharmony_ci * The first unformatted track is the one that got the NRF error, the address is 326062306a36Sopenharmony_ci * encoded in the sense data. 326162306a36Sopenharmony_ci * 326262306a36Sopenharmony_ci * All tracks before have returned valid data and should not be touched. 326362306a36Sopenharmony_ci * All tracks after the unformatted track might be formatted or not. This is 326462306a36Sopenharmony_ci * currently not known, remember the processed data and return the remainder of 326562306a36Sopenharmony_ci * the request to the blocklayer in __dasd_cleanup_cqr(). 326662306a36Sopenharmony_ci */ 326762306a36Sopenharmony_cistatic int dasd_eckd_ese_read(struct dasd_ccw_req *cqr, struct irb *irb) 326862306a36Sopenharmony_ci{ 326962306a36Sopenharmony_ci struct dasd_eckd_private *private; 327062306a36Sopenharmony_ci sector_t first_trk, last_trk; 327162306a36Sopenharmony_ci sector_t first_blk, last_blk; 327262306a36Sopenharmony_ci unsigned int blksize, off; 327362306a36Sopenharmony_ci unsigned int recs_per_trk; 327462306a36Sopenharmony_ci struct dasd_device *base; 327562306a36Sopenharmony_ci struct req_iterator iter; 327662306a36Sopenharmony_ci struct dasd_block *block; 327762306a36Sopenharmony_ci unsigned int skip_block; 327862306a36Sopenharmony_ci unsigned int blk_count; 327962306a36Sopenharmony_ci struct request *req; 328062306a36Sopenharmony_ci struct bio_vec bv; 328162306a36Sopenharmony_ci sector_t curr_trk; 328262306a36Sopenharmony_ci sector_t end_blk; 328362306a36Sopenharmony_ci char *dst; 328462306a36Sopenharmony_ci int rc; 328562306a36Sopenharmony_ci 328662306a36Sopenharmony_ci req = (struct request *) cqr->callback_data; 328762306a36Sopenharmony_ci base = cqr->block->base; 328862306a36Sopenharmony_ci blksize = base->block->bp_block; 328962306a36Sopenharmony_ci block = cqr->block; 329062306a36Sopenharmony_ci private = base->private; 329162306a36Sopenharmony_ci skip_block = 0; 329262306a36Sopenharmony_ci blk_count = 0; 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_ci recs_per_trk = recs_per_track(&private->rdc_data, 0, blksize); 329562306a36Sopenharmony_ci first_trk = first_blk = blk_rq_pos(req) >> block->s2b_shift; 329662306a36Sopenharmony_ci sector_div(first_trk, recs_per_trk); 329762306a36Sopenharmony_ci last_trk = last_blk = 329862306a36Sopenharmony_ci (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift; 329962306a36Sopenharmony_ci sector_div(last_trk, recs_per_trk); 330062306a36Sopenharmony_ci rc = dasd_eckd_track_from_irb(irb, base, &curr_trk); 330162306a36Sopenharmony_ci if (rc) 330262306a36Sopenharmony_ci return rc; 330362306a36Sopenharmony_ci 330462306a36Sopenharmony_ci /* sanity check if the current track from sense data is valid */ 330562306a36Sopenharmony_ci if (curr_trk < first_trk || curr_trk > last_trk) { 330662306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, base, 330762306a36Sopenharmony_ci "ESE error track %llu not within range %llu - %llu\n", 330862306a36Sopenharmony_ci curr_trk, first_trk, last_trk); 330962306a36Sopenharmony_ci return -EINVAL; 331062306a36Sopenharmony_ci } 331162306a36Sopenharmony_ci 331262306a36Sopenharmony_ci /* 331362306a36Sopenharmony_ci * if not the first track got the NRF error we have to skip over valid 331462306a36Sopenharmony_ci * blocks 331562306a36Sopenharmony_ci */ 331662306a36Sopenharmony_ci if (curr_trk != first_trk) 331762306a36Sopenharmony_ci skip_block = curr_trk * recs_per_trk - first_blk; 331862306a36Sopenharmony_ci 331962306a36Sopenharmony_ci /* we have no information beyond the current track */ 332062306a36Sopenharmony_ci end_blk = (curr_trk + 1) * recs_per_trk; 332162306a36Sopenharmony_ci 332262306a36Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 332362306a36Sopenharmony_ci dst = bvec_virt(&bv); 332462306a36Sopenharmony_ci for (off = 0; off < bv.bv_len; off += blksize) { 332562306a36Sopenharmony_ci if (first_blk + blk_count >= end_blk) { 332662306a36Sopenharmony_ci cqr->proc_bytes = blk_count * blksize; 332762306a36Sopenharmony_ci return 0; 332862306a36Sopenharmony_ci } 332962306a36Sopenharmony_ci if (dst && !skip_block) 333062306a36Sopenharmony_ci memset(dst, 0, blksize); 333162306a36Sopenharmony_ci else 333262306a36Sopenharmony_ci skip_block--; 333362306a36Sopenharmony_ci dst += blksize; 333462306a36Sopenharmony_ci blk_count++; 333562306a36Sopenharmony_ci } 333662306a36Sopenharmony_ci } 333762306a36Sopenharmony_ci return 0; 333862306a36Sopenharmony_ci} 333962306a36Sopenharmony_ci 334062306a36Sopenharmony_ci/* 334162306a36Sopenharmony_ci * Helper function to count consecutive records of a single track. 334262306a36Sopenharmony_ci */ 334362306a36Sopenharmony_cistatic int dasd_eckd_count_records(struct eckd_count *fmt_buffer, int start, 334462306a36Sopenharmony_ci int max) 334562306a36Sopenharmony_ci{ 334662306a36Sopenharmony_ci int head; 334762306a36Sopenharmony_ci int i; 334862306a36Sopenharmony_ci 334962306a36Sopenharmony_ci head = fmt_buffer[start].head; 335062306a36Sopenharmony_ci 335162306a36Sopenharmony_ci /* 335262306a36Sopenharmony_ci * There are 3 conditions where we stop counting: 335362306a36Sopenharmony_ci * - if data reoccurs (same head and record may reoccur), which may 335462306a36Sopenharmony_ci * happen due to the way DASD_ECKD_CCW_READ_COUNT works 335562306a36Sopenharmony_ci * - when the head changes, because we're iterating over several tracks 335662306a36Sopenharmony_ci * then (DASD_ECKD_CCW_READ_COUNT_MT) 335762306a36Sopenharmony_ci * - when we've reached the end of sensible data in the buffer (the 335862306a36Sopenharmony_ci * record will be 0 then) 335962306a36Sopenharmony_ci */ 336062306a36Sopenharmony_ci for (i = start; i < max; i++) { 336162306a36Sopenharmony_ci if (i > start) { 336262306a36Sopenharmony_ci if ((fmt_buffer[i].head == head && 336362306a36Sopenharmony_ci fmt_buffer[i].record == 1) || 336462306a36Sopenharmony_ci fmt_buffer[i].head != head || 336562306a36Sopenharmony_ci fmt_buffer[i].record == 0) 336662306a36Sopenharmony_ci break; 336762306a36Sopenharmony_ci } 336862306a36Sopenharmony_ci } 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_ci return i - start; 337162306a36Sopenharmony_ci} 337262306a36Sopenharmony_ci 337362306a36Sopenharmony_ci/* 337462306a36Sopenharmony_ci * Evaluate a given range of tracks. Data like number of records, blocksize, 337562306a36Sopenharmony_ci * record ids, and key length are compared with expected data. 337662306a36Sopenharmony_ci * 337762306a36Sopenharmony_ci * If a mismatch occurs, the corresponding error bit is set, as well as 337862306a36Sopenharmony_ci * additional information, depending on the error. 337962306a36Sopenharmony_ci */ 338062306a36Sopenharmony_cistatic void dasd_eckd_format_evaluate_tracks(struct eckd_count *fmt_buffer, 338162306a36Sopenharmony_ci struct format_check_t *cdata, 338262306a36Sopenharmony_ci int rpt_max, int rpt_exp, 338362306a36Sopenharmony_ci int trk_per_cyl, int tpm) 338462306a36Sopenharmony_ci{ 338562306a36Sopenharmony_ci struct ch_t geo; 338662306a36Sopenharmony_ci int max_entries; 338762306a36Sopenharmony_ci int count = 0; 338862306a36Sopenharmony_ci int trkcount; 338962306a36Sopenharmony_ci int blksize; 339062306a36Sopenharmony_ci int pos = 0; 339162306a36Sopenharmony_ci int i, j; 339262306a36Sopenharmony_ci int kl; 339362306a36Sopenharmony_ci 339462306a36Sopenharmony_ci trkcount = cdata->expect.stop_unit - cdata->expect.start_unit + 1; 339562306a36Sopenharmony_ci max_entries = trkcount * rpt_max; 339662306a36Sopenharmony_ci 339762306a36Sopenharmony_ci for (i = cdata->expect.start_unit; i <= cdata->expect.stop_unit; i++) { 339862306a36Sopenharmony_ci /* Calculate the correct next starting position in the buffer */ 339962306a36Sopenharmony_ci if (tpm) { 340062306a36Sopenharmony_ci while (fmt_buffer[pos].record == 0 && 340162306a36Sopenharmony_ci fmt_buffer[pos].dl == 0) { 340262306a36Sopenharmony_ci if (pos++ > max_entries) 340362306a36Sopenharmony_ci break; 340462306a36Sopenharmony_ci } 340562306a36Sopenharmony_ci } else { 340662306a36Sopenharmony_ci if (i != cdata->expect.start_unit) 340762306a36Sopenharmony_ci pos += rpt_max - count; 340862306a36Sopenharmony_ci } 340962306a36Sopenharmony_ci 341062306a36Sopenharmony_ci /* Calculate the expected geo values for the current track */ 341162306a36Sopenharmony_ci set_ch_t(&geo, i / trk_per_cyl, i % trk_per_cyl); 341262306a36Sopenharmony_ci 341362306a36Sopenharmony_ci /* Count and check number of records */ 341462306a36Sopenharmony_ci count = dasd_eckd_count_records(fmt_buffer, pos, pos + rpt_max); 341562306a36Sopenharmony_ci 341662306a36Sopenharmony_ci if (count < rpt_exp) { 341762306a36Sopenharmony_ci cdata->result = DASD_FMT_ERR_TOO_FEW_RECORDS; 341862306a36Sopenharmony_ci break; 341962306a36Sopenharmony_ci } 342062306a36Sopenharmony_ci if (count > rpt_exp) { 342162306a36Sopenharmony_ci cdata->result = DASD_FMT_ERR_TOO_MANY_RECORDS; 342262306a36Sopenharmony_ci break; 342362306a36Sopenharmony_ci } 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci for (j = 0; j < count; j++, pos++) { 342662306a36Sopenharmony_ci blksize = cdata->expect.blksize; 342762306a36Sopenharmony_ci kl = 0; 342862306a36Sopenharmony_ci 342962306a36Sopenharmony_ci /* 343062306a36Sopenharmony_ci * Set special values when checking CDL formatted 343162306a36Sopenharmony_ci * devices. 343262306a36Sopenharmony_ci */ 343362306a36Sopenharmony_ci if ((cdata->expect.intensity & 0x08) && 343462306a36Sopenharmony_ci geo.cyl == 0 && geo.head == 0) { 343562306a36Sopenharmony_ci if (j < 3) { 343662306a36Sopenharmony_ci blksize = sizes_trk0[j] - 4; 343762306a36Sopenharmony_ci kl = 4; 343862306a36Sopenharmony_ci } 343962306a36Sopenharmony_ci } 344062306a36Sopenharmony_ci if ((cdata->expect.intensity & 0x08) && 344162306a36Sopenharmony_ci geo.cyl == 0 && geo.head == 1) { 344262306a36Sopenharmony_ci blksize = LABEL_SIZE - 44; 344362306a36Sopenharmony_ci kl = 44; 344462306a36Sopenharmony_ci } 344562306a36Sopenharmony_ci 344662306a36Sopenharmony_ci /* Check blocksize */ 344762306a36Sopenharmony_ci if (fmt_buffer[pos].dl != blksize) { 344862306a36Sopenharmony_ci cdata->result = DASD_FMT_ERR_BLKSIZE; 344962306a36Sopenharmony_ci goto out; 345062306a36Sopenharmony_ci } 345162306a36Sopenharmony_ci /* Check if key length is 0 */ 345262306a36Sopenharmony_ci if (fmt_buffer[pos].kl != kl) { 345362306a36Sopenharmony_ci cdata->result = DASD_FMT_ERR_KEY_LENGTH; 345462306a36Sopenharmony_ci goto out; 345562306a36Sopenharmony_ci } 345662306a36Sopenharmony_ci /* Check if record_id is correct */ 345762306a36Sopenharmony_ci if (fmt_buffer[pos].cyl != geo.cyl || 345862306a36Sopenharmony_ci fmt_buffer[pos].head != geo.head || 345962306a36Sopenharmony_ci fmt_buffer[pos].record != (j + 1)) { 346062306a36Sopenharmony_ci cdata->result = DASD_FMT_ERR_RECORD_ID; 346162306a36Sopenharmony_ci goto out; 346262306a36Sopenharmony_ci } 346362306a36Sopenharmony_ci } 346462306a36Sopenharmony_ci } 346562306a36Sopenharmony_ci 346662306a36Sopenharmony_ciout: 346762306a36Sopenharmony_ci /* 346862306a36Sopenharmony_ci * In case of no errors, we need to decrease by one 346962306a36Sopenharmony_ci * to get the correct positions. 347062306a36Sopenharmony_ci */ 347162306a36Sopenharmony_ci if (!cdata->result) { 347262306a36Sopenharmony_ci i--; 347362306a36Sopenharmony_ci pos--; 347462306a36Sopenharmony_ci } 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_ci cdata->unit = i; 347762306a36Sopenharmony_ci cdata->num_records = count; 347862306a36Sopenharmony_ci cdata->rec = fmt_buffer[pos].record; 347962306a36Sopenharmony_ci cdata->blksize = fmt_buffer[pos].dl; 348062306a36Sopenharmony_ci cdata->key_length = fmt_buffer[pos].kl; 348162306a36Sopenharmony_ci} 348262306a36Sopenharmony_ci 348362306a36Sopenharmony_ci/* 348462306a36Sopenharmony_ci * Check the format of a range of tracks of a DASD. 348562306a36Sopenharmony_ci */ 348662306a36Sopenharmony_cistatic int dasd_eckd_check_device_format(struct dasd_device *base, 348762306a36Sopenharmony_ci struct format_check_t *cdata, 348862306a36Sopenharmony_ci int enable_pav) 348962306a36Sopenharmony_ci{ 349062306a36Sopenharmony_ci struct dasd_eckd_private *private = base->private; 349162306a36Sopenharmony_ci struct eckd_count *fmt_buffer; 349262306a36Sopenharmony_ci struct irb irb; 349362306a36Sopenharmony_ci int rpt_max, rpt_exp; 349462306a36Sopenharmony_ci int fmt_buffer_size; 349562306a36Sopenharmony_ci int trk_per_cyl; 349662306a36Sopenharmony_ci int trkcount; 349762306a36Sopenharmony_ci int tpm = 0; 349862306a36Sopenharmony_ci int rc; 349962306a36Sopenharmony_ci 350062306a36Sopenharmony_ci trk_per_cyl = private->rdc_data.trk_per_cyl; 350162306a36Sopenharmony_ci 350262306a36Sopenharmony_ci /* Get maximum and expected amount of records per track */ 350362306a36Sopenharmony_ci rpt_max = recs_per_track(&private->rdc_data, 0, 512) + 1; 350462306a36Sopenharmony_ci rpt_exp = recs_per_track(&private->rdc_data, 0, cdata->expect.blksize); 350562306a36Sopenharmony_ci 350662306a36Sopenharmony_ci trkcount = cdata->expect.stop_unit - cdata->expect.start_unit + 1; 350762306a36Sopenharmony_ci fmt_buffer_size = trkcount * rpt_max * sizeof(struct eckd_count); 350862306a36Sopenharmony_ci 350962306a36Sopenharmony_ci fmt_buffer = kzalloc(fmt_buffer_size, GFP_KERNEL | GFP_DMA); 351062306a36Sopenharmony_ci if (!fmt_buffer) 351162306a36Sopenharmony_ci return -ENOMEM; 351262306a36Sopenharmony_ci 351362306a36Sopenharmony_ci /* 351462306a36Sopenharmony_ci * A certain FICON feature subset is needed to operate in transport 351562306a36Sopenharmony_ci * mode. Additionally, the support for transport mode is implicitly 351662306a36Sopenharmony_ci * checked by comparing the buffer size with fcx_max_data. As long as 351762306a36Sopenharmony_ci * the buffer size is smaller we can operate in transport mode and 351862306a36Sopenharmony_ci * process multiple tracks. If not, only one track at once is being 351962306a36Sopenharmony_ci * processed using command mode. 352062306a36Sopenharmony_ci */ 352162306a36Sopenharmony_ci if ((private->features.feature[40] & 0x04) && 352262306a36Sopenharmony_ci fmt_buffer_size <= private->fcx_max_data) 352362306a36Sopenharmony_ci tpm = 1; 352462306a36Sopenharmony_ci 352562306a36Sopenharmony_ci rc = dasd_eckd_format_process_data(base, &cdata->expect, enable_pav, 352662306a36Sopenharmony_ci tpm, fmt_buffer, rpt_max, &irb); 352762306a36Sopenharmony_ci if (rc && rc != -EIO) 352862306a36Sopenharmony_ci goto out; 352962306a36Sopenharmony_ci if (rc == -EIO) { 353062306a36Sopenharmony_ci /* 353162306a36Sopenharmony_ci * If our first attempt with transport mode enabled comes back 353262306a36Sopenharmony_ci * with an incorrect length error, we're going to retry the 353362306a36Sopenharmony_ci * check with command mode. 353462306a36Sopenharmony_ci */ 353562306a36Sopenharmony_ci if (tpm && scsw_cstat(&irb.scsw) == 0x40) { 353662306a36Sopenharmony_ci tpm = 0; 353762306a36Sopenharmony_ci rc = dasd_eckd_format_process_data(base, &cdata->expect, 353862306a36Sopenharmony_ci enable_pav, tpm, 353962306a36Sopenharmony_ci fmt_buffer, rpt_max, 354062306a36Sopenharmony_ci &irb); 354162306a36Sopenharmony_ci if (rc) 354262306a36Sopenharmony_ci goto out; 354362306a36Sopenharmony_ci } else { 354462306a36Sopenharmony_ci goto out; 354562306a36Sopenharmony_ci } 354662306a36Sopenharmony_ci } 354762306a36Sopenharmony_ci 354862306a36Sopenharmony_ci dasd_eckd_format_evaluate_tracks(fmt_buffer, cdata, rpt_max, rpt_exp, 354962306a36Sopenharmony_ci trk_per_cyl, tpm); 355062306a36Sopenharmony_ci 355162306a36Sopenharmony_ciout: 355262306a36Sopenharmony_ci kfree(fmt_buffer); 355362306a36Sopenharmony_ci 355462306a36Sopenharmony_ci return rc; 355562306a36Sopenharmony_ci} 355662306a36Sopenharmony_ci 355762306a36Sopenharmony_cistatic void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) 355862306a36Sopenharmony_ci{ 355962306a36Sopenharmony_ci if (cqr->retries < 0) { 356062306a36Sopenharmony_ci cqr->status = DASD_CQR_FAILED; 356162306a36Sopenharmony_ci return; 356262306a36Sopenharmony_ci } 356362306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 356462306a36Sopenharmony_ci if (cqr->block && (cqr->startdev != cqr->block->base)) { 356562306a36Sopenharmony_ci dasd_eckd_reset_ccw_to_base_io(cqr); 356662306a36Sopenharmony_ci cqr->startdev = cqr->block->base; 356762306a36Sopenharmony_ci cqr->lpm = dasd_path_get_opm(cqr->block->base); 356862306a36Sopenharmony_ci } 356962306a36Sopenharmony_ci}; 357062306a36Sopenharmony_ci 357162306a36Sopenharmony_cistatic dasd_erp_fn_t 357262306a36Sopenharmony_cidasd_eckd_erp_action(struct dasd_ccw_req * cqr) 357362306a36Sopenharmony_ci{ 357462306a36Sopenharmony_ci struct dasd_device *device = (struct dasd_device *) cqr->startdev; 357562306a36Sopenharmony_ci struct ccw_device *cdev = device->cdev; 357662306a36Sopenharmony_ci 357762306a36Sopenharmony_ci switch (cdev->id.cu_type) { 357862306a36Sopenharmony_ci case 0x3990: 357962306a36Sopenharmony_ci case 0x2105: 358062306a36Sopenharmony_ci case 0x2107: 358162306a36Sopenharmony_ci case 0x1750: 358262306a36Sopenharmony_ci return dasd_3990_erp_action; 358362306a36Sopenharmony_ci case 0x9343: 358462306a36Sopenharmony_ci case 0x3880: 358562306a36Sopenharmony_ci default: 358662306a36Sopenharmony_ci return dasd_default_erp_action; 358762306a36Sopenharmony_ci } 358862306a36Sopenharmony_ci} 358962306a36Sopenharmony_ci 359062306a36Sopenharmony_cistatic dasd_erp_fn_t 359162306a36Sopenharmony_cidasd_eckd_erp_postaction(struct dasd_ccw_req * cqr) 359262306a36Sopenharmony_ci{ 359362306a36Sopenharmony_ci return dasd_default_erp_postaction; 359462306a36Sopenharmony_ci} 359562306a36Sopenharmony_ci 359662306a36Sopenharmony_cistatic void dasd_eckd_check_for_device_change(struct dasd_device *device, 359762306a36Sopenharmony_ci struct dasd_ccw_req *cqr, 359862306a36Sopenharmony_ci struct irb *irb) 359962306a36Sopenharmony_ci{ 360062306a36Sopenharmony_ci char mask; 360162306a36Sopenharmony_ci char *sense = NULL; 360262306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 360362306a36Sopenharmony_ci 360462306a36Sopenharmony_ci /* first of all check for state change pending interrupt */ 360562306a36Sopenharmony_ci mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; 360662306a36Sopenharmony_ci if ((scsw_dstat(&irb->scsw) & mask) == mask) { 360762306a36Sopenharmony_ci /* 360862306a36Sopenharmony_ci * for alias only, not in offline processing 360962306a36Sopenharmony_ci * and only if not suspended 361062306a36Sopenharmony_ci */ 361162306a36Sopenharmony_ci if (!device->block && private->lcu && 361262306a36Sopenharmony_ci device->state == DASD_STATE_ONLINE && 361362306a36Sopenharmony_ci !test_bit(DASD_FLAG_OFFLINE, &device->flags) && 361462306a36Sopenharmony_ci !test_bit(DASD_FLAG_SUSPENDED, &device->flags)) { 361562306a36Sopenharmony_ci /* schedule worker to reload device */ 361662306a36Sopenharmony_ci dasd_reload_device(device); 361762306a36Sopenharmony_ci } 361862306a36Sopenharmony_ci dasd_generic_handle_state_change(device); 361962306a36Sopenharmony_ci return; 362062306a36Sopenharmony_ci } 362162306a36Sopenharmony_ci 362262306a36Sopenharmony_ci sense = dasd_get_sense(irb); 362362306a36Sopenharmony_ci if (!sense) 362462306a36Sopenharmony_ci return; 362562306a36Sopenharmony_ci 362662306a36Sopenharmony_ci /* summary unit check */ 362762306a36Sopenharmony_ci if ((sense[27] & DASD_SENSE_BIT_0) && (sense[7] == 0x0D) && 362862306a36Sopenharmony_ci (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK)) { 362962306a36Sopenharmony_ci if (test_and_set_bit(DASD_FLAG_SUC, &device->flags)) { 363062306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 363162306a36Sopenharmony_ci "eckd suc: device already notified"); 363262306a36Sopenharmony_ci return; 363362306a36Sopenharmony_ci } 363462306a36Sopenharmony_ci sense = dasd_get_sense(irb); 363562306a36Sopenharmony_ci if (!sense) { 363662306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 363762306a36Sopenharmony_ci "eckd suc: no reason code available"); 363862306a36Sopenharmony_ci clear_bit(DASD_FLAG_SUC, &device->flags); 363962306a36Sopenharmony_ci return; 364062306a36Sopenharmony_ci 364162306a36Sopenharmony_ci } 364262306a36Sopenharmony_ci private->suc_reason = sense[8]; 364362306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_NOTICE, device, "%s %x", 364462306a36Sopenharmony_ci "eckd handle summary unit check: reason", 364562306a36Sopenharmony_ci private->suc_reason); 364662306a36Sopenharmony_ci dasd_get_device(device); 364762306a36Sopenharmony_ci if (!schedule_work(&device->suc_work)) 364862306a36Sopenharmony_ci dasd_put_device(device); 364962306a36Sopenharmony_ci 365062306a36Sopenharmony_ci return; 365162306a36Sopenharmony_ci } 365262306a36Sopenharmony_ci 365362306a36Sopenharmony_ci /* service information message SIM */ 365462306a36Sopenharmony_ci if (!cqr && !(sense[27] & DASD_SENSE_BIT_0) && 365562306a36Sopenharmony_ci ((sense[6] & DASD_SIM_SENSE) == DASD_SIM_SENSE)) { 365662306a36Sopenharmony_ci dasd_3990_erp_handle_sim(device, sense); 365762306a36Sopenharmony_ci return; 365862306a36Sopenharmony_ci } 365962306a36Sopenharmony_ci 366062306a36Sopenharmony_ci /* loss of device reservation is handled via base devices only 366162306a36Sopenharmony_ci * as alias devices may be used with several bases 366262306a36Sopenharmony_ci */ 366362306a36Sopenharmony_ci if (device->block && (sense[27] & DASD_SENSE_BIT_0) && 366462306a36Sopenharmony_ci (sense[7] == 0x3F) && 366562306a36Sopenharmony_ci (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK) && 366662306a36Sopenharmony_ci test_bit(DASD_FLAG_IS_RESERVED, &device->flags)) { 366762306a36Sopenharmony_ci if (device->features & DASD_FEATURE_FAILONSLCK) 366862306a36Sopenharmony_ci set_bit(DASD_FLAG_LOCK_STOLEN, &device->flags); 366962306a36Sopenharmony_ci clear_bit(DASD_FLAG_IS_RESERVED, &device->flags); 367062306a36Sopenharmony_ci dev_err(&device->cdev->dev, 367162306a36Sopenharmony_ci "The device reservation was lost\n"); 367262306a36Sopenharmony_ci } 367362306a36Sopenharmony_ci} 367462306a36Sopenharmony_ci 367562306a36Sopenharmony_cistatic int dasd_eckd_ras_sanity_checks(struct dasd_device *device, 367662306a36Sopenharmony_ci unsigned int first_trk, 367762306a36Sopenharmony_ci unsigned int last_trk) 367862306a36Sopenharmony_ci{ 367962306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 368062306a36Sopenharmony_ci unsigned int trks_per_vol; 368162306a36Sopenharmony_ci int rc = 0; 368262306a36Sopenharmony_ci 368362306a36Sopenharmony_ci trks_per_vol = private->real_cyl * private->rdc_data.trk_per_cyl; 368462306a36Sopenharmony_ci 368562306a36Sopenharmony_ci if (first_trk >= trks_per_vol) { 368662306a36Sopenharmony_ci dev_warn(&device->cdev->dev, 368762306a36Sopenharmony_ci "Start track number %u used in the space release command is too big\n", 368862306a36Sopenharmony_ci first_trk); 368962306a36Sopenharmony_ci rc = -EINVAL; 369062306a36Sopenharmony_ci } else if (last_trk >= trks_per_vol) { 369162306a36Sopenharmony_ci dev_warn(&device->cdev->dev, 369262306a36Sopenharmony_ci "Stop track number %u used in the space release command is too big\n", 369362306a36Sopenharmony_ci last_trk); 369462306a36Sopenharmony_ci rc = -EINVAL; 369562306a36Sopenharmony_ci } else if (first_trk > last_trk) { 369662306a36Sopenharmony_ci dev_warn(&device->cdev->dev, 369762306a36Sopenharmony_ci "Start track %u used in the space release command exceeds the end track\n", 369862306a36Sopenharmony_ci first_trk); 369962306a36Sopenharmony_ci rc = -EINVAL; 370062306a36Sopenharmony_ci } 370162306a36Sopenharmony_ci return rc; 370262306a36Sopenharmony_ci} 370362306a36Sopenharmony_ci 370462306a36Sopenharmony_ci/* 370562306a36Sopenharmony_ci * Helper function to count the amount of involved extents within a given range 370662306a36Sopenharmony_ci * with extent alignment in mind. 370762306a36Sopenharmony_ci */ 370862306a36Sopenharmony_cistatic int count_exts(unsigned int from, unsigned int to, int trks_per_ext) 370962306a36Sopenharmony_ci{ 371062306a36Sopenharmony_ci int cur_pos = 0; 371162306a36Sopenharmony_ci int count = 0; 371262306a36Sopenharmony_ci int tmp; 371362306a36Sopenharmony_ci 371462306a36Sopenharmony_ci if (from == to) 371562306a36Sopenharmony_ci return 1; 371662306a36Sopenharmony_ci 371762306a36Sopenharmony_ci /* Count first partial extent */ 371862306a36Sopenharmony_ci if (from % trks_per_ext != 0) { 371962306a36Sopenharmony_ci tmp = from + trks_per_ext - (from % trks_per_ext) - 1; 372062306a36Sopenharmony_ci if (tmp > to) 372162306a36Sopenharmony_ci tmp = to; 372262306a36Sopenharmony_ci cur_pos = tmp - from + 1; 372362306a36Sopenharmony_ci count++; 372462306a36Sopenharmony_ci } 372562306a36Sopenharmony_ci /* Count full extents */ 372662306a36Sopenharmony_ci if (to - (from + cur_pos) + 1 >= trks_per_ext) { 372762306a36Sopenharmony_ci tmp = to - ((to - trks_per_ext + 1) % trks_per_ext); 372862306a36Sopenharmony_ci count += (tmp - (from + cur_pos) + 1) / trks_per_ext; 372962306a36Sopenharmony_ci cur_pos = tmp; 373062306a36Sopenharmony_ci } 373162306a36Sopenharmony_ci /* Count last partial extent */ 373262306a36Sopenharmony_ci if (cur_pos < to) 373362306a36Sopenharmony_ci count++; 373462306a36Sopenharmony_ci 373562306a36Sopenharmony_ci return count; 373662306a36Sopenharmony_ci} 373762306a36Sopenharmony_ci 373862306a36Sopenharmony_cistatic int dasd_in_copy_relation(struct dasd_device *device) 373962306a36Sopenharmony_ci{ 374062306a36Sopenharmony_ci struct dasd_pprc_data_sc4 *temp; 374162306a36Sopenharmony_ci int rc; 374262306a36Sopenharmony_ci 374362306a36Sopenharmony_ci if (!dasd_eckd_pprc_enabled(device)) 374462306a36Sopenharmony_ci return 0; 374562306a36Sopenharmony_ci 374662306a36Sopenharmony_ci temp = kzalloc(sizeof(*temp), GFP_KERNEL); 374762306a36Sopenharmony_ci if (!temp) 374862306a36Sopenharmony_ci return -ENOMEM; 374962306a36Sopenharmony_ci 375062306a36Sopenharmony_ci rc = dasd_eckd_query_pprc_status(device, temp); 375162306a36Sopenharmony_ci if (!rc) 375262306a36Sopenharmony_ci rc = temp->dev_info[0].state; 375362306a36Sopenharmony_ci 375462306a36Sopenharmony_ci kfree(temp); 375562306a36Sopenharmony_ci return rc; 375662306a36Sopenharmony_ci} 375762306a36Sopenharmony_ci 375862306a36Sopenharmony_ci/* 375962306a36Sopenharmony_ci * Release allocated space for a given range or an entire volume. 376062306a36Sopenharmony_ci */ 376162306a36Sopenharmony_cistatic struct dasd_ccw_req * 376262306a36Sopenharmony_cidasd_eckd_dso_ras(struct dasd_device *device, struct dasd_block *block, 376362306a36Sopenharmony_ci struct request *req, unsigned int first_trk, 376462306a36Sopenharmony_ci unsigned int last_trk, int by_extent) 376562306a36Sopenharmony_ci{ 376662306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 376762306a36Sopenharmony_ci struct dasd_dso_ras_ext_range *ras_range; 376862306a36Sopenharmony_ci struct dasd_rssd_features *features; 376962306a36Sopenharmony_ci struct dasd_dso_ras_data *ras_data; 377062306a36Sopenharmony_ci u16 heads, beg_head, end_head; 377162306a36Sopenharmony_ci int cur_to_trk, cur_from_trk; 377262306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 377362306a36Sopenharmony_ci u32 beg_cyl, end_cyl; 377462306a36Sopenharmony_ci int copy_relation; 377562306a36Sopenharmony_ci struct ccw1 *ccw; 377662306a36Sopenharmony_ci int trks_per_ext; 377762306a36Sopenharmony_ci size_t ras_size; 377862306a36Sopenharmony_ci size_t size; 377962306a36Sopenharmony_ci int nr_exts; 378062306a36Sopenharmony_ci void *rq; 378162306a36Sopenharmony_ci int i; 378262306a36Sopenharmony_ci 378362306a36Sopenharmony_ci if (dasd_eckd_ras_sanity_checks(device, first_trk, last_trk)) 378462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 378562306a36Sopenharmony_ci 378662306a36Sopenharmony_ci copy_relation = dasd_in_copy_relation(device); 378762306a36Sopenharmony_ci if (copy_relation < 0) 378862306a36Sopenharmony_ci return ERR_PTR(copy_relation); 378962306a36Sopenharmony_ci 379062306a36Sopenharmony_ci rq = req ? blk_mq_rq_to_pdu(req) : NULL; 379162306a36Sopenharmony_ci 379262306a36Sopenharmony_ci features = &private->features; 379362306a36Sopenharmony_ci 379462306a36Sopenharmony_ci trks_per_ext = dasd_eckd_ext_size(device) * private->rdc_data.trk_per_cyl; 379562306a36Sopenharmony_ci nr_exts = 0; 379662306a36Sopenharmony_ci if (by_extent) 379762306a36Sopenharmony_ci nr_exts = count_exts(first_trk, last_trk, trks_per_ext); 379862306a36Sopenharmony_ci ras_size = sizeof(*ras_data); 379962306a36Sopenharmony_ci size = ras_size + (nr_exts * sizeof(*ras_range)); 380062306a36Sopenharmony_ci 380162306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, size, device, rq); 380262306a36Sopenharmony_ci if (IS_ERR(cqr)) { 380362306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 380462306a36Sopenharmony_ci "Could not allocate RAS request"); 380562306a36Sopenharmony_ci return cqr; 380662306a36Sopenharmony_ci } 380762306a36Sopenharmony_ci 380862306a36Sopenharmony_ci ras_data = cqr->data; 380962306a36Sopenharmony_ci memset(ras_data, 0, size); 381062306a36Sopenharmony_ci 381162306a36Sopenharmony_ci ras_data->order = DSO_ORDER_RAS; 381262306a36Sopenharmony_ci ras_data->flags.vol_type = 0; /* CKD volume */ 381362306a36Sopenharmony_ci /* Release specified extents or entire volume */ 381462306a36Sopenharmony_ci ras_data->op_flags.by_extent = by_extent; 381562306a36Sopenharmony_ci /* 381662306a36Sopenharmony_ci * This bit guarantees initialisation of tracks within an extent that is 381762306a36Sopenharmony_ci * not fully specified, but is only supported with a certain feature 381862306a36Sopenharmony_ci * subset and for devices not in a copy relation. 381962306a36Sopenharmony_ci */ 382062306a36Sopenharmony_ci if (features->feature[56] & 0x01 && !copy_relation) 382162306a36Sopenharmony_ci ras_data->op_flags.guarantee_init = 1; 382262306a36Sopenharmony_ci 382362306a36Sopenharmony_ci ras_data->lss = private->conf.ned->ID; 382462306a36Sopenharmony_ci ras_data->dev_addr = private->conf.ned->unit_addr; 382562306a36Sopenharmony_ci ras_data->nr_exts = nr_exts; 382662306a36Sopenharmony_ci 382762306a36Sopenharmony_ci if (by_extent) { 382862306a36Sopenharmony_ci heads = private->rdc_data.trk_per_cyl; 382962306a36Sopenharmony_ci cur_from_trk = first_trk; 383062306a36Sopenharmony_ci cur_to_trk = first_trk + trks_per_ext - 383162306a36Sopenharmony_ci (first_trk % trks_per_ext) - 1; 383262306a36Sopenharmony_ci if (cur_to_trk > last_trk) 383362306a36Sopenharmony_ci cur_to_trk = last_trk; 383462306a36Sopenharmony_ci ras_range = (struct dasd_dso_ras_ext_range *)(cqr->data + ras_size); 383562306a36Sopenharmony_ci 383662306a36Sopenharmony_ci for (i = 0; i < nr_exts; i++) { 383762306a36Sopenharmony_ci beg_cyl = cur_from_trk / heads; 383862306a36Sopenharmony_ci beg_head = cur_from_trk % heads; 383962306a36Sopenharmony_ci end_cyl = cur_to_trk / heads; 384062306a36Sopenharmony_ci end_head = cur_to_trk % heads; 384162306a36Sopenharmony_ci 384262306a36Sopenharmony_ci set_ch_t(&ras_range->beg_ext, beg_cyl, beg_head); 384362306a36Sopenharmony_ci set_ch_t(&ras_range->end_ext, end_cyl, end_head); 384462306a36Sopenharmony_ci 384562306a36Sopenharmony_ci cur_from_trk = cur_to_trk + 1; 384662306a36Sopenharmony_ci cur_to_trk = cur_from_trk + trks_per_ext - 1; 384762306a36Sopenharmony_ci if (cur_to_trk > last_trk) 384862306a36Sopenharmony_ci cur_to_trk = last_trk; 384962306a36Sopenharmony_ci ras_range++; 385062306a36Sopenharmony_ci } 385162306a36Sopenharmony_ci } 385262306a36Sopenharmony_ci 385362306a36Sopenharmony_ci ccw = cqr->cpaddr; 385462306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(cqr->data); 385562306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_DSO; 385662306a36Sopenharmony_ci ccw->count = size; 385762306a36Sopenharmony_ci 385862306a36Sopenharmony_ci cqr->startdev = device; 385962306a36Sopenharmony_ci cqr->memdev = device; 386062306a36Sopenharmony_ci cqr->block = block; 386162306a36Sopenharmony_ci cqr->retries = 256; 386262306a36Sopenharmony_ci cqr->expires = device->default_expires * HZ; 386362306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 386462306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 386562306a36Sopenharmony_ci 386662306a36Sopenharmony_ci return cqr; 386762306a36Sopenharmony_ci} 386862306a36Sopenharmony_ci 386962306a36Sopenharmony_cistatic int dasd_eckd_release_space_full(struct dasd_device *device) 387062306a36Sopenharmony_ci{ 387162306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 387262306a36Sopenharmony_ci int rc; 387362306a36Sopenharmony_ci 387462306a36Sopenharmony_ci cqr = dasd_eckd_dso_ras(device, NULL, NULL, 0, 0, 0); 387562306a36Sopenharmony_ci if (IS_ERR(cqr)) 387662306a36Sopenharmony_ci return PTR_ERR(cqr); 387762306a36Sopenharmony_ci 387862306a36Sopenharmony_ci rc = dasd_sleep_on_interruptible(cqr); 387962306a36Sopenharmony_ci 388062306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 388162306a36Sopenharmony_ci 388262306a36Sopenharmony_ci return rc; 388362306a36Sopenharmony_ci} 388462306a36Sopenharmony_ci 388562306a36Sopenharmony_cistatic int dasd_eckd_release_space_trks(struct dasd_device *device, 388662306a36Sopenharmony_ci unsigned int from, unsigned int to) 388762306a36Sopenharmony_ci{ 388862306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 388962306a36Sopenharmony_ci struct dasd_block *block = device->block; 389062306a36Sopenharmony_ci struct dasd_ccw_req *cqr, *n; 389162306a36Sopenharmony_ci struct list_head ras_queue; 389262306a36Sopenharmony_ci unsigned int device_exts; 389362306a36Sopenharmony_ci int trks_per_ext; 389462306a36Sopenharmony_ci int stop, step; 389562306a36Sopenharmony_ci int cur_pos; 389662306a36Sopenharmony_ci int rc = 0; 389762306a36Sopenharmony_ci int retry; 389862306a36Sopenharmony_ci 389962306a36Sopenharmony_ci INIT_LIST_HEAD(&ras_queue); 390062306a36Sopenharmony_ci 390162306a36Sopenharmony_ci device_exts = private->real_cyl / dasd_eckd_ext_size(device); 390262306a36Sopenharmony_ci trks_per_ext = dasd_eckd_ext_size(device) * private->rdc_data.trk_per_cyl; 390362306a36Sopenharmony_ci 390462306a36Sopenharmony_ci /* Make sure device limits are not exceeded */ 390562306a36Sopenharmony_ci step = trks_per_ext * min(device_exts, DASD_ECKD_RAS_EXTS_MAX); 390662306a36Sopenharmony_ci cur_pos = from; 390762306a36Sopenharmony_ci 390862306a36Sopenharmony_ci do { 390962306a36Sopenharmony_ci retry = 0; 391062306a36Sopenharmony_ci while (cur_pos < to) { 391162306a36Sopenharmony_ci stop = cur_pos + step - 391262306a36Sopenharmony_ci ((cur_pos + step) % trks_per_ext) - 1; 391362306a36Sopenharmony_ci if (stop > to) 391462306a36Sopenharmony_ci stop = to; 391562306a36Sopenharmony_ci 391662306a36Sopenharmony_ci cqr = dasd_eckd_dso_ras(device, NULL, NULL, cur_pos, stop, 1); 391762306a36Sopenharmony_ci if (IS_ERR(cqr)) { 391862306a36Sopenharmony_ci rc = PTR_ERR(cqr); 391962306a36Sopenharmony_ci if (rc == -ENOMEM) { 392062306a36Sopenharmony_ci if (list_empty(&ras_queue)) 392162306a36Sopenharmony_ci goto out; 392262306a36Sopenharmony_ci retry = 1; 392362306a36Sopenharmony_ci break; 392462306a36Sopenharmony_ci } 392562306a36Sopenharmony_ci goto err_out; 392662306a36Sopenharmony_ci } 392762306a36Sopenharmony_ci 392862306a36Sopenharmony_ci spin_lock_irq(&block->queue_lock); 392962306a36Sopenharmony_ci list_add_tail(&cqr->blocklist, &ras_queue); 393062306a36Sopenharmony_ci spin_unlock_irq(&block->queue_lock); 393162306a36Sopenharmony_ci cur_pos = stop + 1; 393262306a36Sopenharmony_ci } 393362306a36Sopenharmony_ci 393462306a36Sopenharmony_ci rc = dasd_sleep_on_queue_interruptible(&ras_queue); 393562306a36Sopenharmony_ci 393662306a36Sopenharmony_cierr_out: 393762306a36Sopenharmony_ci list_for_each_entry_safe(cqr, n, &ras_queue, blocklist) { 393862306a36Sopenharmony_ci device = cqr->startdev; 393962306a36Sopenharmony_ci private = device->private; 394062306a36Sopenharmony_ci 394162306a36Sopenharmony_ci spin_lock_irq(&block->queue_lock); 394262306a36Sopenharmony_ci list_del_init(&cqr->blocklist); 394362306a36Sopenharmony_ci spin_unlock_irq(&block->queue_lock); 394462306a36Sopenharmony_ci dasd_sfree_request(cqr, device); 394562306a36Sopenharmony_ci private->count--; 394662306a36Sopenharmony_ci } 394762306a36Sopenharmony_ci } while (retry); 394862306a36Sopenharmony_ci 394962306a36Sopenharmony_ciout: 395062306a36Sopenharmony_ci return rc; 395162306a36Sopenharmony_ci} 395262306a36Sopenharmony_ci 395362306a36Sopenharmony_cistatic int dasd_eckd_release_space(struct dasd_device *device, 395462306a36Sopenharmony_ci struct format_data_t *rdata) 395562306a36Sopenharmony_ci{ 395662306a36Sopenharmony_ci if (rdata->intensity & DASD_FMT_INT_ESE_FULL) 395762306a36Sopenharmony_ci return dasd_eckd_release_space_full(device); 395862306a36Sopenharmony_ci else if (rdata->intensity == 0) 395962306a36Sopenharmony_ci return dasd_eckd_release_space_trks(device, rdata->start_unit, 396062306a36Sopenharmony_ci rdata->stop_unit); 396162306a36Sopenharmony_ci else 396262306a36Sopenharmony_ci return -EINVAL; 396362306a36Sopenharmony_ci} 396462306a36Sopenharmony_ci 396562306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( 396662306a36Sopenharmony_ci struct dasd_device *startdev, 396762306a36Sopenharmony_ci struct dasd_block *block, 396862306a36Sopenharmony_ci struct request *req, 396962306a36Sopenharmony_ci sector_t first_rec, 397062306a36Sopenharmony_ci sector_t last_rec, 397162306a36Sopenharmony_ci sector_t first_trk, 397262306a36Sopenharmony_ci sector_t last_trk, 397362306a36Sopenharmony_ci unsigned int first_offs, 397462306a36Sopenharmony_ci unsigned int last_offs, 397562306a36Sopenharmony_ci unsigned int blk_per_trk, 397662306a36Sopenharmony_ci unsigned int blksize) 397762306a36Sopenharmony_ci{ 397862306a36Sopenharmony_ci struct dasd_eckd_private *private; 397962306a36Sopenharmony_ci unsigned long *idaws; 398062306a36Sopenharmony_ci struct LO_eckd_data *LO_data; 398162306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 398262306a36Sopenharmony_ci struct ccw1 *ccw; 398362306a36Sopenharmony_ci struct req_iterator iter; 398462306a36Sopenharmony_ci struct bio_vec bv; 398562306a36Sopenharmony_ci char *dst; 398662306a36Sopenharmony_ci unsigned int off; 398762306a36Sopenharmony_ci int count, cidaw, cplength, datasize; 398862306a36Sopenharmony_ci sector_t recid; 398962306a36Sopenharmony_ci unsigned char cmd, rcmd; 399062306a36Sopenharmony_ci int use_prefix; 399162306a36Sopenharmony_ci struct dasd_device *basedev; 399262306a36Sopenharmony_ci 399362306a36Sopenharmony_ci basedev = block->base; 399462306a36Sopenharmony_ci private = basedev->private; 399562306a36Sopenharmony_ci if (rq_data_dir(req) == READ) 399662306a36Sopenharmony_ci cmd = DASD_ECKD_CCW_READ_MT; 399762306a36Sopenharmony_ci else if (rq_data_dir(req) == WRITE) 399862306a36Sopenharmony_ci cmd = DASD_ECKD_CCW_WRITE_MT; 399962306a36Sopenharmony_ci else 400062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 400162306a36Sopenharmony_ci 400262306a36Sopenharmony_ci /* Check struct bio and count the number of blocks for the request. */ 400362306a36Sopenharmony_ci count = 0; 400462306a36Sopenharmony_ci cidaw = 0; 400562306a36Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 400662306a36Sopenharmony_ci if (bv.bv_len & (blksize - 1)) 400762306a36Sopenharmony_ci /* Eckd can only do full blocks. */ 400862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 400962306a36Sopenharmony_ci count += bv.bv_len >> (block->s2b_shift + 9); 401062306a36Sopenharmony_ci if (idal_is_needed (page_address(bv.bv_page), bv.bv_len)) 401162306a36Sopenharmony_ci cidaw += bv.bv_len >> (block->s2b_shift + 9); 401262306a36Sopenharmony_ci } 401362306a36Sopenharmony_ci /* Paranoia. */ 401462306a36Sopenharmony_ci if (count != last_rec - first_rec + 1) 401562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 401662306a36Sopenharmony_ci 401762306a36Sopenharmony_ci /* use the prefix command if available */ 401862306a36Sopenharmony_ci use_prefix = private->features.feature[8] & 0x01; 401962306a36Sopenharmony_ci if (use_prefix) { 402062306a36Sopenharmony_ci /* 1x prefix + number of blocks */ 402162306a36Sopenharmony_ci cplength = 2 + count; 402262306a36Sopenharmony_ci /* 1x prefix + cidaws*sizeof(long) */ 402362306a36Sopenharmony_ci datasize = sizeof(struct PFX_eckd_data) + 402462306a36Sopenharmony_ci sizeof(struct LO_eckd_data) + 402562306a36Sopenharmony_ci cidaw * sizeof(unsigned long); 402662306a36Sopenharmony_ci } else { 402762306a36Sopenharmony_ci /* 1x define extent + 1x locate record + number of blocks */ 402862306a36Sopenharmony_ci cplength = 2 + count; 402962306a36Sopenharmony_ci /* 1x define extent + 1x locate record + cidaws*sizeof(long) */ 403062306a36Sopenharmony_ci datasize = sizeof(struct DE_eckd_data) + 403162306a36Sopenharmony_ci sizeof(struct LO_eckd_data) + 403262306a36Sopenharmony_ci cidaw * sizeof(unsigned long); 403362306a36Sopenharmony_ci } 403462306a36Sopenharmony_ci /* Find out the number of additional locate record ccws for cdl. */ 403562306a36Sopenharmony_ci if (private->uses_cdl && first_rec < 2*blk_per_trk) { 403662306a36Sopenharmony_ci if (last_rec >= 2*blk_per_trk) 403762306a36Sopenharmony_ci count = 2*blk_per_trk - first_rec; 403862306a36Sopenharmony_ci cplength += count; 403962306a36Sopenharmony_ci datasize += count*sizeof(struct LO_eckd_data); 404062306a36Sopenharmony_ci } 404162306a36Sopenharmony_ci /* Allocate the ccw request. */ 404262306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, 404362306a36Sopenharmony_ci startdev, blk_mq_rq_to_pdu(req)); 404462306a36Sopenharmony_ci if (IS_ERR(cqr)) 404562306a36Sopenharmony_ci return cqr; 404662306a36Sopenharmony_ci ccw = cqr->cpaddr; 404762306a36Sopenharmony_ci /* First ccw is define extent or prefix. */ 404862306a36Sopenharmony_ci if (use_prefix) { 404962306a36Sopenharmony_ci if (prefix(ccw++, cqr->data, first_trk, 405062306a36Sopenharmony_ci last_trk, cmd, basedev, startdev) == -EAGAIN) { 405162306a36Sopenharmony_ci /* Clock not in sync and XRC is enabled. 405262306a36Sopenharmony_ci * Try again later. 405362306a36Sopenharmony_ci */ 405462306a36Sopenharmony_ci dasd_sfree_request(cqr, startdev); 405562306a36Sopenharmony_ci return ERR_PTR(-EAGAIN); 405662306a36Sopenharmony_ci } 405762306a36Sopenharmony_ci idaws = (unsigned long *) (cqr->data + 405862306a36Sopenharmony_ci sizeof(struct PFX_eckd_data)); 405962306a36Sopenharmony_ci } else { 406062306a36Sopenharmony_ci if (define_extent(ccw++, cqr->data, first_trk, 406162306a36Sopenharmony_ci last_trk, cmd, basedev, 0) == -EAGAIN) { 406262306a36Sopenharmony_ci /* Clock not in sync and XRC is enabled. 406362306a36Sopenharmony_ci * Try again later. 406462306a36Sopenharmony_ci */ 406562306a36Sopenharmony_ci dasd_sfree_request(cqr, startdev); 406662306a36Sopenharmony_ci return ERR_PTR(-EAGAIN); 406762306a36Sopenharmony_ci } 406862306a36Sopenharmony_ci idaws = (unsigned long *) (cqr->data + 406962306a36Sopenharmony_ci sizeof(struct DE_eckd_data)); 407062306a36Sopenharmony_ci } 407162306a36Sopenharmony_ci /* Build locate_record+read/write/ccws. */ 407262306a36Sopenharmony_ci LO_data = (struct LO_eckd_data *) (idaws + cidaw); 407362306a36Sopenharmony_ci recid = first_rec; 407462306a36Sopenharmony_ci if (private->uses_cdl == 0 || recid > 2*blk_per_trk) { 407562306a36Sopenharmony_ci /* Only standard blocks so there is just one locate record. */ 407662306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 407762306a36Sopenharmony_ci locate_record(ccw++, LO_data++, first_trk, first_offs + 1, 407862306a36Sopenharmony_ci last_rec - recid + 1, cmd, basedev, blksize); 407962306a36Sopenharmony_ci } 408062306a36Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 408162306a36Sopenharmony_ci dst = bvec_virt(&bv); 408262306a36Sopenharmony_ci if (dasd_page_cache) { 408362306a36Sopenharmony_ci char *copy = kmem_cache_alloc(dasd_page_cache, 408462306a36Sopenharmony_ci GFP_DMA | __GFP_NOWARN); 408562306a36Sopenharmony_ci if (copy && rq_data_dir(req) == WRITE) 408662306a36Sopenharmony_ci memcpy(copy + bv.bv_offset, dst, bv.bv_len); 408762306a36Sopenharmony_ci if (copy) 408862306a36Sopenharmony_ci dst = copy + bv.bv_offset; 408962306a36Sopenharmony_ci } 409062306a36Sopenharmony_ci for (off = 0; off < bv.bv_len; off += blksize) { 409162306a36Sopenharmony_ci sector_t trkid = recid; 409262306a36Sopenharmony_ci unsigned int recoffs = sector_div(trkid, blk_per_trk); 409362306a36Sopenharmony_ci rcmd = cmd; 409462306a36Sopenharmony_ci count = blksize; 409562306a36Sopenharmony_ci /* Locate record for cdl special block ? */ 409662306a36Sopenharmony_ci if (private->uses_cdl && recid < 2*blk_per_trk) { 409762306a36Sopenharmony_ci if (dasd_eckd_cdl_special(blk_per_trk, recid)){ 409862306a36Sopenharmony_ci rcmd |= 0x8; 409962306a36Sopenharmony_ci count = dasd_eckd_cdl_reclen(recid); 410062306a36Sopenharmony_ci if (count < blksize && 410162306a36Sopenharmony_ci rq_data_dir(req) == READ) 410262306a36Sopenharmony_ci memset(dst + count, 0xe5, 410362306a36Sopenharmony_ci blksize - count); 410462306a36Sopenharmony_ci } 410562306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 410662306a36Sopenharmony_ci locate_record(ccw++, LO_data++, 410762306a36Sopenharmony_ci trkid, recoffs + 1, 410862306a36Sopenharmony_ci 1, rcmd, basedev, count); 410962306a36Sopenharmony_ci } 411062306a36Sopenharmony_ci /* Locate record for standard blocks ? */ 411162306a36Sopenharmony_ci if (private->uses_cdl && recid == 2*blk_per_trk) { 411262306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 411362306a36Sopenharmony_ci locate_record(ccw++, LO_data++, 411462306a36Sopenharmony_ci trkid, recoffs + 1, 411562306a36Sopenharmony_ci last_rec - recid + 1, 411662306a36Sopenharmony_ci cmd, basedev, count); 411762306a36Sopenharmony_ci } 411862306a36Sopenharmony_ci /* Read/write ccw. */ 411962306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 412062306a36Sopenharmony_ci ccw->cmd_code = rcmd; 412162306a36Sopenharmony_ci ccw->count = count; 412262306a36Sopenharmony_ci if (idal_is_needed(dst, blksize)) { 412362306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(idaws); 412462306a36Sopenharmony_ci ccw->flags = CCW_FLAG_IDA; 412562306a36Sopenharmony_ci idaws = idal_create_words(idaws, dst, blksize); 412662306a36Sopenharmony_ci } else { 412762306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(dst); 412862306a36Sopenharmony_ci ccw->flags = 0; 412962306a36Sopenharmony_ci } 413062306a36Sopenharmony_ci ccw++; 413162306a36Sopenharmony_ci dst += blksize; 413262306a36Sopenharmony_ci recid++; 413362306a36Sopenharmony_ci } 413462306a36Sopenharmony_ci } 413562306a36Sopenharmony_ci if (blk_noretry_request(req) || 413662306a36Sopenharmony_ci block->base->features & DASD_FEATURE_FAILFAST) 413762306a36Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 413862306a36Sopenharmony_ci cqr->startdev = startdev; 413962306a36Sopenharmony_ci cqr->memdev = startdev; 414062306a36Sopenharmony_ci cqr->block = block; 414162306a36Sopenharmony_ci cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ 414262306a36Sopenharmony_ci cqr->lpm = dasd_path_get_ppm(startdev); 414362306a36Sopenharmony_ci cqr->retries = startdev->default_retries; 414462306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 414562306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 414662306a36Sopenharmony_ci 414762306a36Sopenharmony_ci /* Set flags to suppress output for expected errors */ 414862306a36Sopenharmony_ci if (dasd_eckd_is_ese(basedev)) { 414962306a36Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags); 415062306a36Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags); 415162306a36Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags); 415262306a36Sopenharmony_ci } 415362306a36Sopenharmony_ci 415462306a36Sopenharmony_ci return cqr; 415562306a36Sopenharmony_ci} 415662306a36Sopenharmony_ci 415762306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( 415862306a36Sopenharmony_ci struct dasd_device *startdev, 415962306a36Sopenharmony_ci struct dasd_block *block, 416062306a36Sopenharmony_ci struct request *req, 416162306a36Sopenharmony_ci sector_t first_rec, 416262306a36Sopenharmony_ci sector_t last_rec, 416362306a36Sopenharmony_ci sector_t first_trk, 416462306a36Sopenharmony_ci sector_t last_trk, 416562306a36Sopenharmony_ci unsigned int first_offs, 416662306a36Sopenharmony_ci unsigned int last_offs, 416762306a36Sopenharmony_ci unsigned int blk_per_trk, 416862306a36Sopenharmony_ci unsigned int blksize) 416962306a36Sopenharmony_ci{ 417062306a36Sopenharmony_ci unsigned long *idaws; 417162306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 417262306a36Sopenharmony_ci struct ccw1 *ccw; 417362306a36Sopenharmony_ci struct req_iterator iter; 417462306a36Sopenharmony_ci struct bio_vec bv; 417562306a36Sopenharmony_ci char *dst, *idaw_dst; 417662306a36Sopenharmony_ci unsigned int cidaw, cplength, datasize; 417762306a36Sopenharmony_ci unsigned int tlf; 417862306a36Sopenharmony_ci sector_t recid; 417962306a36Sopenharmony_ci unsigned char cmd; 418062306a36Sopenharmony_ci struct dasd_device *basedev; 418162306a36Sopenharmony_ci unsigned int trkcount, count, count_to_trk_end; 418262306a36Sopenharmony_ci unsigned int idaw_len, seg_len, part_len, len_to_track_end; 418362306a36Sopenharmony_ci unsigned char new_track, end_idaw; 418462306a36Sopenharmony_ci sector_t trkid; 418562306a36Sopenharmony_ci unsigned int recoffs; 418662306a36Sopenharmony_ci 418762306a36Sopenharmony_ci basedev = block->base; 418862306a36Sopenharmony_ci if (rq_data_dir(req) == READ) 418962306a36Sopenharmony_ci cmd = DASD_ECKD_CCW_READ_TRACK_DATA; 419062306a36Sopenharmony_ci else if (rq_data_dir(req) == WRITE) 419162306a36Sopenharmony_ci cmd = DASD_ECKD_CCW_WRITE_TRACK_DATA; 419262306a36Sopenharmony_ci else 419362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 419462306a36Sopenharmony_ci 419562306a36Sopenharmony_ci /* Track based I/O needs IDAWs for each page, and not just for 419662306a36Sopenharmony_ci * 64 bit addresses. We need additional idals for pages 419762306a36Sopenharmony_ci * that get filled from two tracks, so we use the number 419862306a36Sopenharmony_ci * of records as upper limit. 419962306a36Sopenharmony_ci */ 420062306a36Sopenharmony_ci cidaw = last_rec - first_rec + 1; 420162306a36Sopenharmony_ci trkcount = last_trk - first_trk + 1; 420262306a36Sopenharmony_ci 420362306a36Sopenharmony_ci /* 1x prefix + one read/write ccw per track */ 420462306a36Sopenharmony_ci cplength = 1 + trkcount; 420562306a36Sopenharmony_ci 420662306a36Sopenharmony_ci datasize = sizeof(struct PFX_eckd_data) + cidaw * sizeof(unsigned long); 420762306a36Sopenharmony_ci 420862306a36Sopenharmony_ci /* Allocate the ccw request. */ 420962306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, 421062306a36Sopenharmony_ci startdev, blk_mq_rq_to_pdu(req)); 421162306a36Sopenharmony_ci if (IS_ERR(cqr)) 421262306a36Sopenharmony_ci return cqr; 421362306a36Sopenharmony_ci ccw = cqr->cpaddr; 421462306a36Sopenharmony_ci /* transfer length factor: how many bytes to read from the last track */ 421562306a36Sopenharmony_ci if (first_trk == last_trk) 421662306a36Sopenharmony_ci tlf = last_offs - first_offs + 1; 421762306a36Sopenharmony_ci else 421862306a36Sopenharmony_ci tlf = last_offs + 1; 421962306a36Sopenharmony_ci tlf *= blksize; 422062306a36Sopenharmony_ci 422162306a36Sopenharmony_ci if (prefix_LRE(ccw++, cqr->data, first_trk, 422262306a36Sopenharmony_ci last_trk, cmd, basedev, startdev, 422362306a36Sopenharmony_ci 1 /* format */, first_offs + 1, 422462306a36Sopenharmony_ci trkcount, blksize, 422562306a36Sopenharmony_ci tlf) == -EAGAIN) { 422662306a36Sopenharmony_ci /* Clock not in sync and XRC is enabled. 422762306a36Sopenharmony_ci * Try again later. 422862306a36Sopenharmony_ci */ 422962306a36Sopenharmony_ci dasd_sfree_request(cqr, startdev); 423062306a36Sopenharmony_ci return ERR_PTR(-EAGAIN); 423162306a36Sopenharmony_ci } 423262306a36Sopenharmony_ci 423362306a36Sopenharmony_ci /* 423462306a36Sopenharmony_ci * The translation of request into ccw programs must meet the 423562306a36Sopenharmony_ci * following conditions: 423662306a36Sopenharmony_ci * - all idaws but the first and the last must address full pages 423762306a36Sopenharmony_ci * (or 2K blocks on 31-bit) 423862306a36Sopenharmony_ci * - the scope of a ccw and it's idal ends with the track boundaries 423962306a36Sopenharmony_ci */ 424062306a36Sopenharmony_ci idaws = (unsigned long *) (cqr->data + sizeof(struct PFX_eckd_data)); 424162306a36Sopenharmony_ci recid = first_rec; 424262306a36Sopenharmony_ci new_track = 1; 424362306a36Sopenharmony_ci end_idaw = 0; 424462306a36Sopenharmony_ci len_to_track_end = 0; 424562306a36Sopenharmony_ci idaw_dst = NULL; 424662306a36Sopenharmony_ci idaw_len = 0; 424762306a36Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 424862306a36Sopenharmony_ci dst = bvec_virt(&bv); 424962306a36Sopenharmony_ci seg_len = bv.bv_len; 425062306a36Sopenharmony_ci while (seg_len) { 425162306a36Sopenharmony_ci if (new_track) { 425262306a36Sopenharmony_ci trkid = recid; 425362306a36Sopenharmony_ci recoffs = sector_div(trkid, blk_per_trk); 425462306a36Sopenharmony_ci count_to_trk_end = blk_per_trk - recoffs; 425562306a36Sopenharmony_ci count = min((last_rec - recid + 1), 425662306a36Sopenharmony_ci (sector_t)count_to_trk_end); 425762306a36Sopenharmony_ci len_to_track_end = count * blksize; 425862306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 425962306a36Sopenharmony_ci ccw->cmd_code = cmd; 426062306a36Sopenharmony_ci ccw->count = len_to_track_end; 426162306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(idaws); 426262306a36Sopenharmony_ci ccw->flags = CCW_FLAG_IDA; 426362306a36Sopenharmony_ci ccw++; 426462306a36Sopenharmony_ci recid += count; 426562306a36Sopenharmony_ci new_track = 0; 426662306a36Sopenharmony_ci /* first idaw for a ccw may start anywhere */ 426762306a36Sopenharmony_ci if (!idaw_dst) 426862306a36Sopenharmony_ci idaw_dst = dst; 426962306a36Sopenharmony_ci } 427062306a36Sopenharmony_ci /* If we start a new idaw, we must make sure that it 427162306a36Sopenharmony_ci * starts on an IDA_BLOCK_SIZE boundary. 427262306a36Sopenharmony_ci * If we continue an idaw, we must make sure that the 427362306a36Sopenharmony_ci * current segment begins where the so far accumulated 427462306a36Sopenharmony_ci * idaw ends 427562306a36Sopenharmony_ci */ 427662306a36Sopenharmony_ci if (!idaw_dst) { 427762306a36Sopenharmony_ci if ((__u32)virt_to_phys(dst) & (IDA_BLOCK_SIZE - 1)) { 427862306a36Sopenharmony_ci dasd_sfree_request(cqr, startdev); 427962306a36Sopenharmony_ci return ERR_PTR(-ERANGE); 428062306a36Sopenharmony_ci } else 428162306a36Sopenharmony_ci idaw_dst = dst; 428262306a36Sopenharmony_ci } 428362306a36Sopenharmony_ci if ((idaw_dst + idaw_len) != dst) { 428462306a36Sopenharmony_ci dasd_sfree_request(cqr, startdev); 428562306a36Sopenharmony_ci return ERR_PTR(-ERANGE); 428662306a36Sopenharmony_ci } 428762306a36Sopenharmony_ci part_len = min(seg_len, len_to_track_end); 428862306a36Sopenharmony_ci seg_len -= part_len; 428962306a36Sopenharmony_ci dst += part_len; 429062306a36Sopenharmony_ci idaw_len += part_len; 429162306a36Sopenharmony_ci len_to_track_end -= part_len; 429262306a36Sopenharmony_ci /* collected memory area ends on an IDA_BLOCK border, 429362306a36Sopenharmony_ci * -> create an idaw 429462306a36Sopenharmony_ci * idal_create_words will handle cases where idaw_len 429562306a36Sopenharmony_ci * is larger then IDA_BLOCK_SIZE 429662306a36Sopenharmony_ci */ 429762306a36Sopenharmony_ci if (!((__u32)virt_to_phys(idaw_dst + idaw_len) & (IDA_BLOCK_SIZE - 1))) 429862306a36Sopenharmony_ci end_idaw = 1; 429962306a36Sopenharmony_ci /* We also need to end the idaw at track end */ 430062306a36Sopenharmony_ci if (!len_to_track_end) { 430162306a36Sopenharmony_ci new_track = 1; 430262306a36Sopenharmony_ci end_idaw = 1; 430362306a36Sopenharmony_ci } 430462306a36Sopenharmony_ci if (end_idaw) { 430562306a36Sopenharmony_ci idaws = idal_create_words(idaws, idaw_dst, 430662306a36Sopenharmony_ci idaw_len); 430762306a36Sopenharmony_ci idaw_dst = NULL; 430862306a36Sopenharmony_ci idaw_len = 0; 430962306a36Sopenharmony_ci end_idaw = 0; 431062306a36Sopenharmony_ci } 431162306a36Sopenharmony_ci } 431262306a36Sopenharmony_ci } 431362306a36Sopenharmony_ci 431462306a36Sopenharmony_ci if (blk_noretry_request(req) || 431562306a36Sopenharmony_ci block->base->features & DASD_FEATURE_FAILFAST) 431662306a36Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 431762306a36Sopenharmony_ci cqr->startdev = startdev; 431862306a36Sopenharmony_ci cqr->memdev = startdev; 431962306a36Sopenharmony_ci cqr->block = block; 432062306a36Sopenharmony_ci cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ 432162306a36Sopenharmony_ci cqr->lpm = dasd_path_get_ppm(startdev); 432262306a36Sopenharmony_ci cqr->retries = startdev->default_retries; 432362306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 432462306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 432562306a36Sopenharmony_ci 432662306a36Sopenharmony_ci /* Set flags to suppress output for expected errors */ 432762306a36Sopenharmony_ci if (dasd_eckd_is_ese(basedev)) 432862306a36Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags); 432962306a36Sopenharmony_ci 433062306a36Sopenharmony_ci return cqr; 433162306a36Sopenharmony_ci} 433262306a36Sopenharmony_ci 433362306a36Sopenharmony_cistatic int prepare_itcw(struct itcw *itcw, 433462306a36Sopenharmony_ci unsigned int trk, unsigned int totrk, int cmd, 433562306a36Sopenharmony_ci struct dasd_device *basedev, 433662306a36Sopenharmony_ci struct dasd_device *startdev, 433762306a36Sopenharmony_ci unsigned int rec_on_trk, int count, 433862306a36Sopenharmony_ci unsigned int blksize, 433962306a36Sopenharmony_ci unsigned int total_data_size, 434062306a36Sopenharmony_ci unsigned int tlf, 434162306a36Sopenharmony_ci unsigned int blk_per_trk) 434262306a36Sopenharmony_ci{ 434362306a36Sopenharmony_ci struct PFX_eckd_data pfxdata; 434462306a36Sopenharmony_ci struct dasd_eckd_private *basepriv, *startpriv; 434562306a36Sopenharmony_ci struct DE_eckd_data *dedata; 434662306a36Sopenharmony_ci struct LRE_eckd_data *lredata; 434762306a36Sopenharmony_ci struct dcw *dcw; 434862306a36Sopenharmony_ci 434962306a36Sopenharmony_ci u32 begcyl, endcyl; 435062306a36Sopenharmony_ci u16 heads, beghead, endhead; 435162306a36Sopenharmony_ci u8 pfx_cmd; 435262306a36Sopenharmony_ci 435362306a36Sopenharmony_ci int rc = 0; 435462306a36Sopenharmony_ci int sector = 0; 435562306a36Sopenharmony_ci int dn, d; 435662306a36Sopenharmony_ci 435762306a36Sopenharmony_ci 435862306a36Sopenharmony_ci /* setup prefix data */ 435962306a36Sopenharmony_ci basepriv = basedev->private; 436062306a36Sopenharmony_ci startpriv = startdev->private; 436162306a36Sopenharmony_ci dedata = &pfxdata.define_extent; 436262306a36Sopenharmony_ci lredata = &pfxdata.locate_record; 436362306a36Sopenharmony_ci 436462306a36Sopenharmony_ci memset(&pfxdata, 0, sizeof(pfxdata)); 436562306a36Sopenharmony_ci pfxdata.format = 1; /* PFX with LRE */ 436662306a36Sopenharmony_ci pfxdata.base_address = basepriv->conf.ned->unit_addr; 436762306a36Sopenharmony_ci pfxdata.base_lss = basepriv->conf.ned->ID; 436862306a36Sopenharmony_ci pfxdata.validity.define_extent = 1; 436962306a36Sopenharmony_ci 437062306a36Sopenharmony_ci /* private uid is kept up to date, conf_data may be outdated */ 437162306a36Sopenharmony_ci if (startpriv->uid.type == UA_BASE_PAV_ALIAS) 437262306a36Sopenharmony_ci pfxdata.validity.verify_base = 1; 437362306a36Sopenharmony_ci 437462306a36Sopenharmony_ci if (startpriv->uid.type == UA_HYPER_PAV_ALIAS) { 437562306a36Sopenharmony_ci pfxdata.validity.verify_base = 1; 437662306a36Sopenharmony_ci pfxdata.validity.hyper_pav = 1; 437762306a36Sopenharmony_ci } 437862306a36Sopenharmony_ci 437962306a36Sopenharmony_ci switch (cmd) { 438062306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_TRACK_DATA: 438162306a36Sopenharmony_ci dedata->mask.perm = 0x1; 438262306a36Sopenharmony_ci dedata->attributes.operation = basepriv->attrib.operation; 438362306a36Sopenharmony_ci dedata->blk_size = blksize; 438462306a36Sopenharmony_ci dedata->ga_extended |= 0x42; 438562306a36Sopenharmony_ci lredata->operation.orientation = 0x0; 438662306a36Sopenharmony_ci lredata->operation.operation = 0x0C; 438762306a36Sopenharmony_ci lredata->auxiliary.check_bytes = 0x01; 438862306a36Sopenharmony_ci pfx_cmd = DASD_ECKD_CCW_PFX_READ; 438962306a36Sopenharmony_ci break; 439062306a36Sopenharmony_ci case DASD_ECKD_CCW_WRITE_TRACK_DATA: 439162306a36Sopenharmony_ci dedata->mask.perm = 0x02; 439262306a36Sopenharmony_ci dedata->attributes.operation = basepriv->attrib.operation; 439362306a36Sopenharmony_ci dedata->blk_size = blksize; 439462306a36Sopenharmony_ci rc = set_timestamp(NULL, dedata, basedev); 439562306a36Sopenharmony_ci dedata->ga_extended |= 0x42; 439662306a36Sopenharmony_ci lredata->operation.orientation = 0x0; 439762306a36Sopenharmony_ci lredata->operation.operation = 0x3F; 439862306a36Sopenharmony_ci lredata->extended_operation = 0x23; 439962306a36Sopenharmony_ci lredata->auxiliary.check_bytes = 0x2; 440062306a36Sopenharmony_ci /* 440162306a36Sopenharmony_ci * If XRC is supported the System Time Stamp is set. The 440262306a36Sopenharmony_ci * validity of the time stamp must be reflected in the prefix 440362306a36Sopenharmony_ci * data as well. 440462306a36Sopenharmony_ci */ 440562306a36Sopenharmony_ci if (dedata->ga_extended & 0x08 && dedata->ga_extended & 0x02) 440662306a36Sopenharmony_ci pfxdata.validity.time_stamp = 1; /* 'Time Stamp Valid' */ 440762306a36Sopenharmony_ci pfx_cmd = DASD_ECKD_CCW_PFX; 440862306a36Sopenharmony_ci break; 440962306a36Sopenharmony_ci case DASD_ECKD_CCW_READ_COUNT_MT: 441062306a36Sopenharmony_ci dedata->mask.perm = 0x1; 441162306a36Sopenharmony_ci dedata->attributes.operation = DASD_BYPASS_CACHE; 441262306a36Sopenharmony_ci dedata->ga_extended |= 0x42; 441362306a36Sopenharmony_ci dedata->blk_size = blksize; 441462306a36Sopenharmony_ci lredata->operation.orientation = 0x2; 441562306a36Sopenharmony_ci lredata->operation.operation = 0x16; 441662306a36Sopenharmony_ci lredata->auxiliary.check_bytes = 0x01; 441762306a36Sopenharmony_ci pfx_cmd = DASD_ECKD_CCW_PFX_READ; 441862306a36Sopenharmony_ci break; 441962306a36Sopenharmony_ci default: 442062306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_ERR, basedev, 442162306a36Sopenharmony_ci "prepare itcw, unknown opcode 0x%x", cmd); 442262306a36Sopenharmony_ci BUG(); 442362306a36Sopenharmony_ci break; 442462306a36Sopenharmony_ci } 442562306a36Sopenharmony_ci if (rc) 442662306a36Sopenharmony_ci return rc; 442762306a36Sopenharmony_ci 442862306a36Sopenharmony_ci dedata->attributes.mode = 0x3; /* ECKD */ 442962306a36Sopenharmony_ci 443062306a36Sopenharmony_ci heads = basepriv->rdc_data.trk_per_cyl; 443162306a36Sopenharmony_ci begcyl = trk / heads; 443262306a36Sopenharmony_ci beghead = trk % heads; 443362306a36Sopenharmony_ci endcyl = totrk / heads; 443462306a36Sopenharmony_ci endhead = totrk % heads; 443562306a36Sopenharmony_ci 443662306a36Sopenharmony_ci /* check for sequential prestage - enhance cylinder range */ 443762306a36Sopenharmony_ci if (dedata->attributes.operation == DASD_SEQ_PRESTAGE || 443862306a36Sopenharmony_ci dedata->attributes.operation == DASD_SEQ_ACCESS) { 443962306a36Sopenharmony_ci 444062306a36Sopenharmony_ci if (endcyl + basepriv->attrib.nr_cyl < basepriv->real_cyl) 444162306a36Sopenharmony_ci endcyl += basepriv->attrib.nr_cyl; 444262306a36Sopenharmony_ci else 444362306a36Sopenharmony_ci endcyl = (basepriv->real_cyl - 1); 444462306a36Sopenharmony_ci } 444562306a36Sopenharmony_ci 444662306a36Sopenharmony_ci set_ch_t(&dedata->beg_ext, begcyl, beghead); 444762306a36Sopenharmony_ci set_ch_t(&dedata->end_ext, endcyl, endhead); 444862306a36Sopenharmony_ci 444962306a36Sopenharmony_ci dedata->ep_format = 0x20; /* records per track is valid */ 445062306a36Sopenharmony_ci dedata->ep_rec_per_track = blk_per_trk; 445162306a36Sopenharmony_ci 445262306a36Sopenharmony_ci if (rec_on_trk) { 445362306a36Sopenharmony_ci switch (basepriv->rdc_data.dev_type) { 445462306a36Sopenharmony_ci case 0x3390: 445562306a36Sopenharmony_ci dn = ceil_quot(blksize + 6, 232); 445662306a36Sopenharmony_ci d = 9 + ceil_quot(blksize + 6 * (dn + 1), 34); 445762306a36Sopenharmony_ci sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8; 445862306a36Sopenharmony_ci break; 445962306a36Sopenharmony_ci case 0x3380: 446062306a36Sopenharmony_ci d = 7 + ceil_quot(blksize + 12, 32); 446162306a36Sopenharmony_ci sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7; 446262306a36Sopenharmony_ci break; 446362306a36Sopenharmony_ci } 446462306a36Sopenharmony_ci } 446562306a36Sopenharmony_ci 446662306a36Sopenharmony_ci if (cmd == DASD_ECKD_CCW_READ_COUNT_MT) { 446762306a36Sopenharmony_ci lredata->auxiliary.length_valid = 0; 446862306a36Sopenharmony_ci lredata->auxiliary.length_scope = 0; 446962306a36Sopenharmony_ci lredata->sector = 0xff; 447062306a36Sopenharmony_ci } else { 447162306a36Sopenharmony_ci lredata->auxiliary.length_valid = 1; 447262306a36Sopenharmony_ci lredata->auxiliary.length_scope = 1; 447362306a36Sopenharmony_ci lredata->sector = sector; 447462306a36Sopenharmony_ci } 447562306a36Sopenharmony_ci lredata->auxiliary.imbedded_ccw_valid = 1; 447662306a36Sopenharmony_ci lredata->length = tlf; 447762306a36Sopenharmony_ci lredata->imbedded_ccw = cmd; 447862306a36Sopenharmony_ci lredata->count = count; 447962306a36Sopenharmony_ci set_ch_t(&lredata->seek_addr, begcyl, beghead); 448062306a36Sopenharmony_ci lredata->search_arg.cyl = lredata->seek_addr.cyl; 448162306a36Sopenharmony_ci lredata->search_arg.head = lredata->seek_addr.head; 448262306a36Sopenharmony_ci lredata->search_arg.record = rec_on_trk; 448362306a36Sopenharmony_ci 448462306a36Sopenharmony_ci dcw = itcw_add_dcw(itcw, pfx_cmd, 0, 448562306a36Sopenharmony_ci &pfxdata, sizeof(pfxdata), total_data_size); 448662306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(dcw); 448762306a36Sopenharmony_ci} 448862306a36Sopenharmony_ci 448962306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( 449062306a36Sopenharmony_ci struct dasd_device *startdev, 449162306a36Sopenharmony_ci struct dasd_block *block, 449262306a36Sopenharmony_ci struct request *req, 449362306a36Sopenharmony_ci sector_t first_rec, 449462306a36Sopenharmony_ci sector_t last_rec, 449562306a36Sopenharmony_ci sector_t first_trk, 449662306a36Sopenharmony_ci sector_t last_trk, 449762306a36Sopenharmony_ci unsigned int first_offs, 449862306a36Sopenharmony_ci unsigned int last_offs, 449962306a36Sopenharmony_ci unsigned int blk_per_trk, 450062306a36Sopenharmony_ci unsigned int blksize) 450162306a36Sopenharmony_ci{ 450262306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 450362306a36Sopenharmony_ci struct req_iterator iter; 450462306a36Sopenharmony_ci struct bio_vec bv; 450562306a36Sopenharmony_ci char *dst; 450662306a36Sopenharmony_ci unsigned int trkcount, ctidaw; 450762306a36Sopenharmony_ci unsigned char cmd; 450862306a36Sopenharmony_ci struct dasd_device *basedev; 450962306a36Sopenharmony_ci unsigned int tlf; 451062306a36Sopenharmony_ci struct itcw *itcw; 451162306a36Sopenharmony_ci struct tidaw *last_tidaw = NULL; 451262306a36Sopenharmony_ci int itcw_op; 451362306a36Sopenharmony_ci size_t itcw_size; 451462306a36Sopenharmony_ci u8 tidaw_flags; 451562306a36Sopenharmony_ci unsigned int seg_len, part_len, len_to_track_end; 451662306a36Sopenharmony_ci unsigned char new_track; 451762306a36Sopenharmony_ci sector_t recid, trkid; 451862306a36Sopenharmony_ci unsigned int offs; 451962306a36Sopenharmony_ci unsigned int count, count_to_trk_end; 452062306a36Sopenharmony_ci int ret; 452162306a36Sopenharmony_ci 452262306a36Sopenharmony_ci basedev = block->base; 452362306a36Sopenharmony_ci if (rq_data_dir(req) == READ) { 452462306a36Sopenharmony_ci cmd = DASD_ECKD_CCW_READ_TRACK_DATA; 452562306a36Sopenharmony_ci itcw_op = ITCW_OP_READ; 452662306a36Sopenharmony_ci } else if (rq_data_dir(req) == WRITE) { 452762306a36Sopenharmony_ci cmd = DASD_ECKD_CCW_WRITE_TRACK_DATA; 452862306a36Sopenharmony_ci itcw_op = ITCW_OP_WRITE; 452962306a36Sopenharmony_ci } else 453062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 453162306a36Sopenharmony_ci 453262306a36Sopenharmony_ci /* trackbased I/O needs address all memory via TIDAWs, 453362306a36Sopenharmony_ci * not just for 64 bit addresses. This allows us to map 453462306a36Sopenharmony_ci * each segment directly to one tidaw. 453562306a36Sopenharmony_ci * In the case of write requests, additional tidaws may 453662306a36Sopenharmony_ci * be needed when a segment crosses a track boundary. 453762306a36Sopenharmony_ci */ 453862306a36Sopenharmony_ci trkcount = last_trk - first_trk + 1; 453962306a36Sopenharmony_ci ctidaw = 0; 454062306a36Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 454162306a36Sopenharmony_ci ++ctidaw; 454262306a36Sopenharmony_ci } 454362306a36Sopenharmony_ci if (rq_data_dir(req) == WRITE) 454462306a36Sopenharmony_ci ctidaw += (last_trk - first_trk); 454562306a36Sopenharmony_ci 454662306a36Sopenharmony_ci /* Allocate the ccw request. */ 454762306a36Sopenharmony_ci itcw_size = itcw_calc_size(0, ctidaw, 0); 454862306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev, 454962306a36Sopenharmony_ci blk_mq_rq_to_pdu(req)); 455062306a36Sopenharmony_ci if (IS_ERR(cqr)) 455162306a36Sopenharmony_ci return cqr; 455262306a36Sopenharmony_ci 455362306a36Sopenharmony_ci /* transfer length factor: how many bytes to read from the last track */ 455462306a36Sopenharmony_ci if (first_trk == last_trk) 455562306a36Sopenharmony_ci tlf = last_offs - first_offs + 1; 455662306a36Sopenharmony_ci else 455762306a36Sopenharmony_ci tlf = last_offs + 1; 455862306a36Sopenharmony_ci tlf *= blksize; 455962306a36Sopenharmony_ci 456062306a36Sopenharmony_ci itcw = itcw_init(cqr->data, itcw_size, itcw_op, 0, ctidaw, 0); 456162306a36Sopenharmony_ci if (IS_ERR(itcw)) { 456262306a36Sopenharmony_ci ret = -EINVAL; 456362306a36Sopenharmony_ci goto out_error; 456462306a36Sopenharmony_ci } 456562306a36Sopenharmony_ci cqr->cpaddr = itcw_get_tcw(itcw); 456662306a36Sopenharmony_ci if (prepare_itcw(itcw, first_trk, last_trk, 456762306a36Sopenharmony_ci cmd, basedev, startdev, 456862306a36Sopenharmony_ci first_offs + 1, 456962306a36Sopenharmony_ci trkcount, blksize, 457062306a36Sopenharmony_ci (last_rec - first_rec + 1) * blksize, 457162306a36Sopenharmony_ci tlf, blk_per_trk) == -EAGAIN) { 457262306a36Sopenharmony_ci /* Clock not in sync and XRC is enabled. 457362306a36Sopenharmony_ci * Try again later. 457462306a36Sopenharmony_ci */ 457562306a36Sopenharmony_ci ret = -EAGAIN; 457662306a36Sopenharmony_ci goto out_error; 457762306a36Sopenharmony_ci } 457862306a36Sopenharmony_ci len_to_track_end = 0; 457962306a36Sopenharmony_ci /* 458062306a36Sopenharmony_ci * A tidaw can address 4k of memory, but must not cross page boundaries 458162306a36Sopenharmony_ci * We can let the block layer handle this by setting 458262306a36Sopenharmony_ci * blk_queue_segment_boundary to page boundaries and 458362306a36Sopenharmony_ci * blk_max_segment_size to page size when setting up the request queue. 458462306a36Sopenharmony_ci * For write requests, a TIDAW must not cross track boundaries, because 458562306a36Sopenharmony_ci * we have to set the CBC flag on the last tidaw for each track. 458662306a36Sopenharmony_ci */ 458762306a36Sopenharmony_ci if (rq_data_dir(req) == WRITE) { 458862306a36Sopenharmony_ci new_track = 1; 458962306a36Sopenharmony_ci recid = first_rec; 459062306a36Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 459162306a36Sopenharmony_ci dst = bvec_virt(&bv); 459262306a36Sopenharmony_ci seg_len = bv.bv_len; 459362306a36Sopenharmony_ci while (seg_len) { 459462306a36Sopenharmony_ci if (new_track) { 459562306a36Sopenharmony_ci trkid = recid; 459662306a36Sopenharmony_ci offs = sector_div(trkid, blk_per_trk); 459762306a36Sopenharmony_ci count_to_trk_end = blk_per_trk - offs; 459862306a36Sopenharmony_ci count = min((last_rec - recid + 1), 459962306a36Sopenharmony_ci (sector_t)count_to_trk_end); 460062306a36Sopenharmony_ci len_to_track_end = count * blksize; 460162306a36Sopenharmony_ci recid += count; 460262306a36Sopenharmony_ci new_track = 0; 460362306a36Sopenharmony_ci } 460462306a36Sopenharmony_ci part_len = min(seg_len, len_to_track_end); 460562306a36Sopenharmony_ci seg_len -= part_len; 460662306a36Sopenharmony_ci len_to_track_end -= part_len; 460762306a36Sopenharmony_ci /* We need to end the tidaw at track end */ 460862306a36Sopenharmony_ci if (!len_to_track_end) { 460962306a36Sopenharmony_ci new_track = 1; 461062306a36Sopenharmony_ci tidaw_flags = TIDAW_FLAGS_INSERT_CBC; 461162306a36Sopenharmony_ci } else 461262306a36Sopenharmony_ci tidaw_flags = 0; 461362306a36Sopenharmony_ci last_tidaw = itcw_add_tidaw(itcw, tidaw_flags, 461462306a36Sopenharmony_ci dst, part_len); 461562306a36Sopenharmony_ci if (IS_ERR(last_tidaw)) { 461662306a36Sopenharmony_ci ret = -EINVAL; 461762306a36Sopenharmony_ci goto out_error; 461862306a36Sopenharmony_ci } 461962306a36Sopenharmony_ci dst += part_len; 462062306a36Sopenharmony_ci } 462162306a36Sopenharmony_ci } 462262306a36Sopenharmony_ci } else { 462362306a36Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 462462306a36Sopenharmony_ci dst = bvec_virt(&bv); 462562306a36Sopenharmony_ci last_tidaw = itcw_add_tidaw(itcw, 0x00, 462662306a36Sopenharmony_ci dst, bv.bv_len); 462762306a36Sopenharmony_ci if (IS_ERR(last_tidaw)) { 462862306a36Sopenharmony_ci ret = -EINVAL; 462962306a36Sopenharmony_ci goto out_error; 463062306a36Sopenharmony_ci } 463162306a36Sopenharmony_ci } 463262306a36Sopenharmony_ci } 463362306a36Sopenharmony_ci last_tidaw->flags |= TIDAW_FLAGS_LAST; 463462306a36Sopenharmony_ci last_tidaw->flags &= ~TIDAW_FLAGS_INSERT_CBC; 463562306a36Sopenharmony_ci itcw_finalize(itcw); 463662306a36Sopenharmony_ci 463762306a36Sopenharmony_ci if (blk_noretry_request(req) || 463862306a36Sopenharmony_ci block->base->features & DASD_FEATURE_FAILFAST) 463962306a36Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 464062306a36Sopenharmony_ci cqr->cpmode = 1; 464162306a36Sopenharmony_ci cqr->startdev = startdev; 464262306a36Sopenharmony_ci cqr->memdev = startdev; 464362306a36Sopenharmony_ci cqr->block = block; 464462306a36Sopenharmony_ci cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ 464562306a36Sopenharmony_ci cqr->lpm = dasd_path_get_ppm(startdev); 464662306a36Sopenharmony_ci cqr->retries = startdev->default_retries; 464762306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 464862306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 464962306a36Sopenharmony_ci 465062306a36Sopenharmony_ci /* Set flags to suppress output for expected errors */ 465162306a36Sopenharmony_ci if (dasd_eckd_is_ese(basedev)) { 465262306a36Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags); 465362306a36Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags); 465462306a36Sopenharmony_ci set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags); 465562306a36Sopenharmony_ci } 465662306a36Sopenharmony_ci 465762306a36Sopenharmony_ci return cqr; 465862306a36Sopenharmony_ciout_error: 465962306a36Sopenharmony_ci dasd_sfree_request(cqr, startdev); 466062306a36Sopenharmony_ci return ERR_PTR(ret); 466162306a36Sopenharmony_ci} 466262306a36Sopenharmony_ci 466362306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, 466462306a36Sopenharmony_ci struct dasd_block *block, 466562306a36Sopenharmony_ci struct request *req) 466662306a36Sopenharmony_ci{ 466762306a36Sopenharmony_ci int cmdrtd, cmdwtd; 466862306a36Sopenharmony_ci int use_prefix; 466962306a36Sopenharmony_ci int fcx_multitrack; 467062306a36Sopenharmony_ci struct dasd_eckd_private *private; 467162306a36Sopenharmony_ci struct dasd_device *basedev; 467262306a36Sopenharmony_ci sector_t first_rec, last_rec; 467362306a36Sopenharmony_ci sector_t first_trk, last_trk; 467462306a36Sopenharmony_ci unsigned int first_offs, last_offs; 467562306a36Sopenharmony_ci unsigned int blk_per_trk, blksize; 467662306a36Sopenharmony_ci int cdlspecial; 467762306a36Sopenharmony_ci unsigned int data_size; 467862306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 467962306a36Sopenharmony_ci 468062306a36Sopenharmony_ci basedev = block->base; 468162306a36Sopenharmony_ci private = basedev->private; 468262306a36Sopenharmony_ci 468362306a36Sopenharmony_ci /* Calculate number of blocks/records per track. */ 468462306a36Sopenharmony_ci blksize = block->bp_block; 468562306a36Sopenharmony_ci blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize); 468662306a36Sopenharmony_ci if (blk_per_trk == 0) 468762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 468862306a36Sopenharmony_ci /* Calculate record id of first and last block. */ 468962306a36Sopenharmony_ci first_rec = first_trk = blk_rq_pos(req) >> block->s2b_shift; 469062306a36Sopenharmony_ci first_offs = sector_div(first_trk, blk_per_trk); 469162306a36Sopenharmony_ci last_rec = last_trk = 469262306a36Sopenharmony_ci (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift; 469362306a36Sopenharmony_ci last_offs = sector_div(last_trk, blk_per_trk); 469462306a36Sopenharmony_ci cdlspecial = (private->uses_cdl && first_rec < 2*blk_per_trk); 469562306a36Sopenharmony_ci 469662306a36Sopenharmony_ci fcx_multitrack = private->features.feature[40] & 0x20; 469762306a36Sopenharmony_ci data_size = blk_rq_bytes(req); 469862306a36Sopenharmony_ci if (data_size % blksize) 469962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 470062306a36Sopenharmony_ci /* tpm write request add CBC data on each track boundary */ 470162306a36Sopenharmony_ci if (rq_data_dir(req) == WRITE) 470262306a36Sopenharmony_ci data_size += (last_trk - first_trk) * 4; 470362306a36Sopenharmony_ci 470462306a36Sopenharmony_ci /* is read track data and write track data in command mode supported? */ 470562306a36Sopenharmony_ci cmdrtd = private->features.feature[9] & 0x20; 470662306a36Sopenharmony_ci cmdwtd = private->features.feature[12] & 0x40; 470762306a36Sopenharmony_ci use_prefix = private->features.feature[8] & 0x01; 470862306a36Sopenharmony_ci 470962306a36Sopenharmony_ci cqr = NULL; 471062306a36Sopenharmony_ci if (cdlspecial || dasd_page_cache) { 471162306a36Sopenharmony_ci /* do nothing, just fall through to the cmd mode single case */ 471262306a36Sopenharmony_ci } else if ((data_size <= private->fcx_max_data) 471362306a36Sopenharmony_ci && (fcx_multitrack || (first_trk == last_trk))) { 471462306a36Sopenharmony_ci cqr = dasd_eckd_build_cp_tpm_track(startdev, block, req, 471562306a36Sopenharmony_ci first_rec, last_rec, 471662306a36Sopenharmony_ci first_trk, last_trk, 471762306a36Sopenharmony_ci first_offs, last_offs, 471862306a36Sopenharmony_ci blk_per_trk, blksize); 471962306a36Sopenharmony_ci if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) && 472062306a36Sopenharmony_ci (PTR_ERR(cqr) != -ENOMEM)) 472162306a36Sopenharmony_ci cqr = NULL; 472262306a36Sopenharmony_ci } else if (use_prefix && 472362306a36Sopenharmony_ci (((rq_data_dir(req) == READ) && cmdrtd) || 472462306a36Sopenharmony_ci ((rq_data_dir(req) == WRITE) && cmdwtd))) { 472562306a36Sopenharmony_ci cqr = dasd_eckd_build_cp_cmd_track(startdev, block, req, 472662306a36Sopenharmony_ci first_rec, last_rec, 472762306a36Sopenharmony_ci first_trk, last_trk, 472862306a36Sopenharmony_ci first_offs, last_offs, 472962306a36Sopenharmony_ci blk_per_trk, blksize); 473062306a36Sopenharmony_ci if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) && 473162306a36Sopenharmony_ci (PTR_ERR(cqr) != -ENOMEM)) 473262306a36Sopenharmony_ci cqr = NULL; 473362306a36Sopenharmony_ci } 473462306a36Sopenharmony_ci if (!cqr) 473562306a36Sopenharmony_ci cqr = dasd_eckd_build_cp_cmd_single(startdev, block, req, 473662306a36Sopenharmony_ci first_rec, last_rec, 473762306a36Sopenharmony_ci first_trk, last_trk, 473862306a36Sopenharmony_ci first_offs, last_offs, 473962306a36Sopenharmony_ci blk_per_trk, blksize); 474062306a36Sopenharmony_ci return cqr; 474162306a36Sopenharmony_ci} 474262306a36Sopenharmony_ci 474362306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp_raw(struct dasd_device *startdev, 474462306a36Sopenharmony_ci struct dasd_block *block, 474562306a36Sopenharmony_ci struct request *req) 474662306a36Sopenharmony_ci{ 474762306a36Sopenharmony_ci sector_t start_padding_sectors, end_sector_offset, end_padding_sectors; 474862306a36Sopenharmony_ci unsigned int seg_len, len_to_track_end; 474962306a36Sopenharmony_ci unsigned int cidaw, cplength, datasize; 475062306a36Sopenharmony_ci sector_t first_trk, last_trk, sectors; 475162306a36Sopenharmony_ci struct dasd_eckd_private *base_priv; 475262306a36Sopenharmony_ci struct dasd_device *basedev; 475362306a36Sopenharmony_ci struct req_iterator iter; 475462306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 475562306a36Sopenharmony_ci unsigned int trkcount; 475662306a36Sopenharmony_ci unsigned long *idaws; 475762306a36Sopenharmony_ci unsigned int size; 475862306a36Sopenharmony_ci unsigned char cmd; 475962306a36Sopenharmony_ci struct bio_vec bv; 476062306a36Sopenharmony_ci struct ccw1 *ccw; 476162306a36Sopenharmony_ci int use_prefix; 476262306a36Sopenharmony_ci void *data; 476362306a36Sopenharmony_ci char *dst; 476462306a36Sopenharmony_ci 476562306a36Sopenharmony_ci /* 476662306a36Sopenharmony_ci * raw track access needs to be mutiple of 64k and on 64k boundary 476762306a36Sopenharmony_ci * For read requests we can fix an incorrect alignment by padding 476862306a36Sopenharmony_ci * the request with dummy pages. 476962306a36Sopenharmony_ci */ 477062306a36Sopenharmony_ci start_padding_sectors = blk_rq_pos(req) % DASD_RAW_SECTORS_PER_TRACK; 477162306a36Sopenharmony_ci end_sector_offset = (blk_rq_pos(req) + blk_rq_sectors(req)) % 477262306a36Sopenharmony_ci DASD_RAW_SECTORS_PER_TRACK; 477362306a36Sopenharmony_ci end_padding_sectors = (DASD_RAW_SECTORS_PER_TRACK - end_sector_offset) % 477462306a36Sopenharmony_ci DASD_RAW_SECTORS_PER_TRACK; 477562306a36Sopenharmony_ci basedev = block->base; 477662306a36Sopenharmony_ci if ((start_padding_sectors || end_padding_sectors) && 477762306a36Sopenharmony_ci (rq_data_dir(req) == WRITE)) { 477862306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_ERR, basedev, 477962306a36Sopenharmony_ci "raw write not track aligned (%llu,%llu) req %p", 478062306a36Sopenharmony_ci start_padding_sectors, end_padding_sectors, req); 478162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 478262306a36Sopenharmony_ci } 478362306a36Sopenharmony_ci 478462306a36Sopenharmony_ci first_trk = blk_rq_pos(req) / DASD_RAW_SECTORS_PER_TRACK; 478562306a36Sopenharmony_ci last_trk = (blk_rq_pos(req) + blk_rq_sectors(req) - 1) / 478662306a36Sopenharmony_ci DASD_RAW_SECTORS_PER_TRACK; 478762306a36Sopenharmony_ci trkcount = last_trk - first_trk + 1; 478862306a36Sopenharmony_ci 478962306a36Sopenharmony_ci if (rq_data_dir(req) == READ) 479062306a36Sopenharmony_ci cmd = DASD_ECKD_CCW_READ_TRACK; 479162306a36Sopenharmony_ci else if (rq_data_dir(req) == WRITE) 479262306a36Sopenharmony_ci cmd = DASD_ECKD_CCW_WRITE_FULL_TRACK; 479362306a36Sopenharmony_ci else 479462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 479562306a36Sopenharmony_ci 479662306a36Sopenharmony_ci /* 479762306a36Sopenharmony_ci * Raw track based I/O needs IDAWs for each page, 479862306a36Sopenharmony_ci * and not just for 64 bit addresses. 479962306a36Sopenharmony_ci */ 480062306a36Sopenharmony_ci cidaw = trkcount * DASD_RAW_BLOCK_PER_TRACK; 480162306a36Sopenharmony_ci 480262306a36Sopenharmony_ci /* 480362306a36Sopenharmony_ci * struct PFX_eckd_data and struct LRE_eckd_data can have up to 2 bytes 480462306a36Sopenharmony_ci * of extended parameter. This is needed for write full track. 480562306a36Sopenharmony_ci */ 480662306a36Sopenharmony_ci base_priv = basedev->private; 480762306a36Sopenharmony_ci use_prefix = base_priv->features.feature[8] & 0x01; 480862306a36Sopenharmony_ci if (use_prefix) { 480962306a36Sopenharmony_ci cplength = 1 + trkcount; 481062306a36Sopenharmony_ci size = sizeof(struct PFX_eckd_data) + 2; 481162306a36Sopenharmony_ci } else { 481262306a36Sopenharmony_ci cplength = 2 + trkcount; 481362306a36Sopenharmony_ci size = sizeof(struct DE_eckd_data) + 481462306a36Sopenharmony_ci sizeof(struct LRE_eckd_data) + 2; 481562306a36Sopenharmony_ci } 481662306a36Sopenharmony_ci size = ALIGN(size, 8); 481762306a36Sopenharmony_ci 481862306a36Sopenharmony_ci datasize = size + cidaw * sizeof(unsigned long); 481962306a36Sopenharmony_ci 482062306a36Sopenharmony_ci /* Allocate the ccw request. */ 482162306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, 482262306a36Sopenharmony_ci datasize, startdev, blk_mq_rq_to_pdu(req)); 482362306a36Sopenharmony_ci if (IS_ERR(cqr)) 482462306a36Sopenharmony_ci return cqr; 482562306a36Sopenharmony_ci 482662306a36Sopenharmony_ci ccw = cqr->cpaddr; 482762306a36Sopenharmony_ci data = cqr->data; 482862306a36Sopenharmony_ci 482962306a36Sopenharmony_ci if (use_prefix) { 483062306a36Sopenharmony_ci prefix_LRE(ccw++, data, first_trk, last_trk, cmd, basedev, 483162306a36Sopenharmony_ci startdev, 1, 0, trkcount, 0, 0); 483262306a36Sopenharmony_ci } else { 483362306a36Sopenharmony_ci define_extent(ccw++, data, first_trk, last_trk, cmd, basedev, 0); 483462306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 483562306a36Sopenharmony_ci 483662306a36Sopenharmony_ci data += sizeof(struct DE_eckd_data); 483762306a36Sopenharmony_ci locate_record_ext(ccw++, data, first_trk, 0, 483862306a36Sopenharmony_ci trkcount, cmd, basedev, 0, 0); 483962306a36Sopenharmony_ci } 484062306a36Sopenharmony_ci 484162306a36Sopenharmony_ci idaws = (unsigned long *)(cqr->data + size); 484262306a36Sopenharmony_ci len_to_track_end = 0; 484362306a36Sopenharmony_ci if (start_padding_sectors) { 484462306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 484562306a36Sopenharmony_ci ccw->cmd_code = cmd; 484662306a36Sopenharmony_ci /* maximum 3390 track size */ 484762306a36Sopenharmony_ci ccw->count = 57326; 484862306a36Sopenharmony_ci /* 64k map to one track */ 484962306a36Sopenharmony_ci len_to_track_end = 65536 - start_padding_sectors * 512; 485062306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(idaws); 485162306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_IDA; 485262306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 485362306a36Sopenharmony_ci ccw++; 485462306a36Sopenharmony_ci for (sectors = 0; sectors < start_padding_sectors; sectors += 8) 485562306a36Sopenharmony_ci idaws = idal_create_words(idaws, rawpadpage, PAGE_SIZE); 485662306a36Sopenharmony_ci } 485762306a36Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 485862306a36Sopenharmony_ci dst = bvec_virt(&bv); 485962306a36Sopenharmony_ci seg_len = bv.bv_len; 486062306a36Sopenharmony_ci if (cmd == DASD_ECKD_CCW_READ_TRACK) 486162306a36Sopenharmony_ci memset(dst, 0, seg_len); 486262306a36Sopenharmony_ci if (!len_to_track_end) { 486362306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 486462306a36Sopenharmony_ci ccw->cmd_code = cmd; 486562306a36Sopenharmony_ci /* maximum 3390 track size */ 486662306a36Sopenharmony_ci ccw->count = 57326; 486762306a36Sopenharmony_ci /* 64k map to one track */ 486862306a36Sopenharmony_ci len_to_track_end = 65536; 486962306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(idaws); 487062306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_IDA; 487162306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 487262306a36Sopenharmony_ci ccw++; 487362306a36Sopenharmony_ci } 487462306a36Sopenharmony_ci len_to_track_end -= seg_len; 487562306a36Sopenharmony_ci idaws = idal_create_words(idaws, dst, seg_len); 487662306a36Sopenharmony_ci } 487762306a36Sopenharmony_ci for (sectors = 0; sectors < end_padding_sectors; sectors += 8) 487862306a36Sopenharmony_ci idaws = idal_create_words(idaws, rawpadpage, PAGE_SIZE); 487962306a36Sopenharmony_ci if (blk_noretry_request(req) || 488062306a36Sopenharmony_ci block->base->features & DASD_FEATURE_FAILFAST) 488162306a36Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 488262306a36Sopenharmony_ci cqr->startdev = startdev; 488362306a36Sopenharmony_ci cqr->memdev = startdev; 488462306a36Sopenharmony_ci cqr->block = block; 488562306a36Sopenharmony_ci cqr->expires = startdev->default_expires * HZ; 488662306a36Sopenharmony_ci cqr->lpm = dasd_path_get_ppm(startdev); 488762306a36Sopenharmony_ci cqr->retries = startdev->default_retries; 488862306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 488962306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 489062306a36Sopenharmony_ci 489162306a36Sopenharmony_ci return cqr; 489262306a36Sopenharmony_ci} 489362306a36Sopenharmony_ci 489462306a36Sopenharmony_ci 489562306a36Sopenharmony_cistatic int 489662306a36Sopenharmony_cidasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req) 489762306a36Sopenharmony_ci{ 489862306a36Sopenharmony_ci struct dasd_eckd_private *private; 489962306a36Sopenharmony_ci struct ccw1 *ccw; 490062306a36Sopenharmony_ci struct req_iterator iter; 490162306a36Sopenharmony_ci struct bio_vec bv; 490262306a36Sopenharmony_ci char *dst, *cda; 490362306a36Sopenharmony_ci unsigned int blksize, blk_per_trk, off; 490462306a36Sopenharmony_ci sector_t recid; 490562306a36Sopenharmony_ci int status; 490662306a36Sopenharmony_ci 490762306a36Sopenharmony_ci if (!dasd_page_cache) 490862306a36Sopenharmony_ci goto out; 490962306a36Sopenharmony_ci private = cqr->block->base->private; 491062306a36Sopenharmony_ci blksize = cqr->block->bp_block; 491162306a36Sopenharmony_ci blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize); 491262306a36Sopenharmony_ci recid = blk_rq_pos(req) >> cqr->block->s2b_shift; 491362306a36Sopenharmony_ci ccw = cqr->cpaddr; 491462306a36Sopenharmony_ci /* Skip over define extent & locate record. */ 491562306a36Sopenharmony_ci ccw++; 491662306a36Sopenharmony_ci if (private->uses_cdl == 0 || recid > 2*blk_per_trk) 491762306a36Sopenharmony_ci ccw++; 491862306a36Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 491962306a36Sopenharmony_ci dst = bvec_virt(&bv); 492062306a36Sopenharmony_ci for (off = 0; off < bv.bv_len; off += blksize) { 492162306a36Sopenharmony_ci /* Skip locate record. */ 492262306a36Sopenharmony_ci if (private->uses_cdl && recid <= 2*blk_per_trk) 492362306a36Sopenharmony_ci ccw++; 492462306a36Sopenharmony_ci if (dst) { 492562306a36Sopenharmony_ci if (ccw->flags & CCW_FLAG_IDA) 492662306a36Sopenharmony_ci cda = *((char **)phys_to_virt(ccw->cda)); 492762306a36Sopenharmony_ci else 492862306a36Sopenharmony_ci cda = phys_to_virt(ccw->cda); 492962306a36Sopenharmony_ci if (dst != cda) { 493062306a36Sopenharmony_ci if (rq_data_dir(req) == READ) 493162306a36Sopenharmony_ci memcpy(dst, cda, bv.bv_len); 493262306a36Sopenharmony_ci kmem_cache_free(dasd_page_cache, 493362306a36Sopenharmony_ci (void *)((addr_t)cda & PAGE_MASK)); 493462306a36Sopenharmony_ci } 493562306a36Sopenharmony_ci dst = NULL; 493662306a36Sopenharmony_ci } 493762306a36Sopenharmony_ci ccw++; 493862306a36Sopenharmony_ci recid++; 493962306a36Sopenharmony_ci } 494062306a36Sopenharmony_ci } 494162306a36Sopenharmony_ciout: 494262306a36Sopenharmony_ci status = cqr->status == DASD_CQR_DONE; 494362306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 494462306a36Sopenharmony_ci return status; 494562306a36Sopenharmony_ci} 494662306a36Sopenharmony_ci 494762306a36Sopenharmony_ci/* 494862306a36Sopenharmony_ci * Modify ccw/tcw in cqr so it can be started on a base device. 494962306a36Sopenharmony_ci * 495062306a36Sopenharmony_ci * Note that this is not enough to restart the cqr! 495162306a36Sopenharmony_ci * Either reset cqr->startdev as well (summary unit check handling) 495262306a36Sopenharmony_ci * or restart via separate cqr (as in ERP handling). 495362306a36Sopenharmony_ci */ 495462306a36Sopenharmony_civoid dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *cqr) 495562306a36Sopenharmony_ci{ 495662306a36Sopenharmony_ci struct ccw1 *ccw; 495762306a36Sopenharmony_ci struct PFX_eckd_data *pfxdata; 495862306a36Sopenharmony_ci struct tcw *tcw; 495962306a36Sopenharmony_ci struct tccb *tccb; 496062306a36Sopenharmony_ci struct dcw *dcw; 496162306a36Sopenharmony_ci 496262306a36Sopenharmony_ci if (cqr->cpmode == 1) { 496362306a36Sopenharmony_ci tcw = cqr->cpaddr; 496462306a36Sopenharmony_ci tccb = tcw_get_tccb(tcw); 496562306a36Sopenharmony_ci dcw = (struct dcw *)&tccb->tca[0]; 496662306a36Sopenharmony_ci pfxdata = (struct PFX_eckd_data *)&dcw->cd[0]; 496762306a36Sopenharmony_ci pfxdata->validity.verify_base = 0; 496862306a36Sopenharmony_ci pfxdata->validity.hyper_pav = 0; 496962306a36Sopenharmony_ci } else { 497062306a36Sopenharmony_ci ccw = cqr->cpaddr; 497162306a36Sopenharmony_ci pfxdata = cqr->data; 497262306a36Sopenharmony_ci if (ccw->cmd_code == DASD_ECKD_CCW_PFX) { 497362306a36Sopenharmony_ci pfxdata->validity.verify_base = 0; 497462306a36Sopenharmony_ci pfxdata->validity.hyper_pav = 0; 497562306a36Sopenharmony_ci } 497662306a36Sopenharmony_ci } 497762306a36Sopenharmony_ci} 497862306a36Sopenharmony_ci 497962306a36Sopenharmony_ci#define DASD_ECKD_CHANQ_MAX_SIZE 4 498062306a36Sopenharmony_ci 498162306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_alias_cp(struct dasd_device *base, 498262306a36Sopenharmony_ci struct dasd_block *block, 498362306a36Sopenharmony_ci struct request *req) 498462306a36Sopenharmony_ci{ 498562306a36Sopenharmony_ci struct dasd_eckd_private *private; 498662306a36Sopenharmony_ci struct dasd_device *startdev; 498762306a36Sopenharmony_ci unsigned long flags; 498862306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 498962306a36Sopenharmony_ci 499062306a36Sopenharmony_ci startdev = dasd_alias_get_start_dev(base); 499162306a36Sopenharmony_ci if (!startdev) 499262306a36Sopenharmony_ci startdev = base; 499362306a36Sopenharmony_ci private = startdev->private; 499462306a36Sopenharmony_ci if (private->count >= DASD_ECKD_CHANQ_MAX_SIZE) 499562306a36Sopenharmony_ci return ERR_PTR(-EBUSY); 499662306a36Sopenharmony_ci 499762306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(startdev->cdev), flags); 499862306a36Sopenharmony_ci private->count++; 499962306a36Sopenharmony_ci if ((base->features & DASD_FEATURE_USERAW)) 500062306a36Sopenharmony_ci cqr = dasd_eckd_build_cp_raw(startdev, block, req); 500162306a36Sopenharmony_ci else 500262306a36Sopenharmony_ci cqr = dasd_eckd_build_cp(startdev, block, req); 500362306a36Sopenharmony_ci if (IS_ERR(cqr)) 500462306a36Sopenharmony_ci private->count--; 500562306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(startdev->cdev), flags); 500662306a36Sopenharmony_ci return cqr; 500762306a36Sopenharmony_ci} 500862306a36Sopenharmony_ci 500962306a36Sopenharmony_cistatic int dasd_eckd_free_alias_cp(struct dasd_ccw_req *cqr, 501062306a36Sopenharmony_ci struct request *req) 501162306a36Sopenharmony_ci{ 501262306a36Sopenharmony_ci struct dasd_eckd_private *private; 501362306a36Sopenharmony_ci unsigned long flags; 501462306a36Sopenharmony_ci 501562306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(cqr->memdev->cdev), flags); 501662306a36Sopenharmony_ci private = cqr->memdev->private; 501762306a36Sopenharmony_ci private->count--; 501862306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(cqr->memdev->cdev), flags); 501962306a36Sopenharmony_ci return dasd_eckd_free_cp(cqr, req); 502062306a36Sopenharmony_ci} 502162306a36Sopenharmony_ci 502262306a36Sopenharmony_cistatic int 502362306a36Sopenharmony_cidasd_eckd_fill_info(struct dasd_device * device, 502462306a36Sopenharmony_ci struct dasd_information2_t * info) 502562306a36Sopenharmony_ci{ 502662306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 502762306a36Sopenharmony_ci 502862306a36Sopenharmony_ci info->label_block = 2; 502962306a36Sopenharmony_ci info->FBA_layout = private->uses_cdl ? 0 : 1; 503062306a36Sopenharmony_ci info->format = private->uses_cdl ? DASD_FORMAT_CDL : DASD_FORMAT_LDL; 503162306a36Sopenharmony_ci info->characteristics_size = sizeof(private->rdc_data); 503262306a36Sopenharmony_ci memcpy(info->characteristics, &private->rdc_data, 503362306a36Sopenharmony_ci sizeof(private->rdc_data)); 503462306a36Sopenharmony_ci info->confdata_size = min_t(unsigned long, private->conf.len, 503562306a36Sopenharmony_ci sizeof(info->configuration_data)); 503662306a36Sopenharmony_ci memcpy(info->configuration_data, private->conf.data, 503762306a36Sopenharmony_ci info->confdata_size); 503862306a36Sopenharmony_ci return 0; 503962306a36Sopenharmony_ci} 504062306a36Sopenharmony_ci 504162306a36Sopenharmony_ci/* 504262306a36Sopenharmony_ci * SECTION: ioctl functions for eckd devices. 504362306a36Sopenharmony_ci */ 504462306a36Sopenharmony_ci 504562306a36Sopenharmony_ci/* 504662306a36Sopenharmony_ci * Release device ioctl. 504762306a36Sopenharmony_ci * Buils a channel programm to releases a prior reserved 504862306a36Sopenharmony_ci * (see dasd_eckd_reserve) device. 504962306a36Sopenharmony_ci */ 505062306a36Sopenharmony_cistatic int 505162306a36Sopenharmony_cidasd_eckd_release(struct dasd_device *device) 505262306a36Sopenharmony_ci{ 505362306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 505462306a36Sopenharmony_ci int rc; 505562306a36Sopenharmony_ci struct ccw1 *ccw; 505662306a36Sopenharmony_ci int useglobal; 505762306a36Sopenharmony_ci 505862306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 505962306a36Sopenharmony_ci return -EACCES; 506062306a36Sopenharmony_ci 506162306a36Sopenharmony_ci useglobal = 0; 506262306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL); 506362306a36Sopenharmony_ci if (IS_ERR(cqr)) { 506462306a36Sopenharmony_ci mutex_lock(&dasd_reserve_mutex); 506562306a36Sopenharmony_ci useglobal = 1; 506662306a36Sopenharmony_ci cqr = &dasd_reserve_req->cqr; 506762306a36Sopenharmony_ci memset(cqr, 0, sizeof(*cqr)); 506862306a36Sopenharmony_ci memset(&dasd_reserve_req->ccw, 0, 506962306a36Sopenharmony_ci sizeof(dasd_reserve_req->ccw)); 507062306a36Sopenharmony_ci cqr->cpaddr = &dasd_reserve_req->ccw; 507162306a36Sopenharmony_ci cqr->data = &dasd_reserve_req->data; 507262306a36Sopenharmony_ci cqr->magic = DASD_ECKD_MAGIC; 507362306a36Sopenharmony_ci } 507462306a36Sopenharmony_ci ccw = cqr->cpaddr; 507562306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RELEASE; 507662306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 507762306a36Sopenharmony_ci ccw->count = 32; 507862306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(cqr->data); 507962306a36Sopenharmony_ci cqr->startdev = device; 508062306a36Sopenharmony_ci cqr->memdev = device; 508162306a36Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); 508262306a36Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 508362306a36Sopenharmony_ci cqr->retries = 2; /* set retry counter to enable basic ERP */ 508462306a36Sopenharmony_ci cqr->expires = 2 * HZ; 508562306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 508662306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 508762306a36Sopenharmony_ci 508862306a36Sopenharmony_ci rc = dasd_sleep_on_immediatly(cqr); 508962306a36Sopenharmony_ci if (!rc) 509062306a36Sopenharmony_ci clear_bit(DASD_FLAG_IS_RESERVED, &device->flags); 509162306a36Sopenharmony_ci 509262306a36Sopenharmony_ci if (useglobal) 509362306a36Sopenharmony_ci mutex_unlock(&dasd_reserve_mutex); 509462306a36Sopenharmony_ci else 509562306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 509662306a36Sopenharmony_ci return rc; 509762306a36Sopenharmony_ci} 509862306a36Sopenharmony_ci 509962306a36Sopenharmony_ci/* 510062306a36Sopenharmony_ci * Reserve device ioctl. 510162306a36Sopenharmony_ci * Options are set to 'synchronous wait for interrupt' and 510262306a36Sopenharmony_ci * 'timeout the request'. This leads to a terminate IO if 510362306a36Sopenharmony_ci * the interrupt is outstanding for a certain time. 510462306a36Sopenharmony_ci */ 510562306a36Sopenharmony_cistatic int 510662306a36Sopenharmony_cidasd_eckd_reserve(struct dasd_device *device) 510762306a36Sopenharmony_ci{ 510862306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 510962306a36Sopenharmony_ci int rc; 511062306a36Sopenharmony_ci struct ccw1 *ccw; 511162306a36Sopenharmony_ci int useglobal; 511262306a36Sopenharmony_ci 511362306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 511462306a36Sopenharmony_ci return -EACCES; 511562306a36Sopenharmony_ci 511662306a36Sopenharmony_ci useglobal = 0; 511762306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL); 511862306a36Sopenharmony_ci if (IS_ERR(cqr)) { 511962306a36Sopenharmony_ci mutex_lock(&dasd_reserve_mutex); 512062306a36Sopenharmony_ci useglobal = 1; 512162306a36Sopenharmony_ci cqr = &dasd_reserve_req->cqr; 512262306a36Sopenharmony_ci memset(cqr, 0, sizeof(*cqr)); 512362306a36Sopenharmony_ci memset(&dasd_reserve_req->ccw, 0, 512462306a36Sopenharmony_ci sizeof(dasd_reserve_req->ccw)); 512562306a36Sopenharmony_ci cqr->cpaddr = &dasd_reserve_req->ccw; 512662306a36Sopenharmony_ci cqr->data = &dasd_reserve_req->data; 512762306a36Sopenharmony_ci cqr->magic = DASD_ECKD_MAGIC; 512862306a36Sopenharmony_ci } 512962306a36Sopenharmony_ci ccw = cqr->cpaddr; 513062306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RESERVE; 513162306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 513262306a36Sopenharmony_ci ccw->count = 32; 513362306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(cqr->data); 513462306a36Sopenharmony_ci cqr->startdev = device; 513562306a36Sopenharmony_ci cqr->memdev = device; 513662306a36Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); 513762306a36Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 513862306a36Sopenharmony_ci cqr->retries = 2; /* set retry counter to enable basic ERP */ 513962306a36Sopenharmony_ci cqr->expires = 2 * HZ; 514062306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 514162306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 514262306a36Sopenharmony_ci 514362306a36Sopenharmony_ci rc = dasd_sleep_on_immediatly(cqr); 514462306a36Sopenharmony_ci if (!rc) 514562306a36Sopenharmony_ci set_bit(DASD_FLAG_IS_RESERVED, &device->flags); 514662306a36Sopenharmony_ci 514762306a36Sopenharmony_ci if (useglobal) 514862306a36Sopenharmony_ci mutex_unlock(&dasd_reserve_mutex); 514962306a36Sopenharmony_ci else 515062306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 515162306a36Sopenharmony_ci return rc; 515262306a36Sopenharmony_ci} 515362306a36Sopenharmony_ci 515462306a36Sopenharmony_ci/* 515562306a36Sopenharmony_ci * Steal lock ioctl - unconditional reserve device. 515662306a36Sopenharmony_ci * Buils a channel programm to break a device's reservation. 515762306a36Sopenharmony_ci * (unconditional reserve) 515862306a36Sopenharmony_ci */ 515962306a36Sopenharmony_cistatic int 516062306a36Sopenharmony_cidasd_eckd_steal_lock(struct dasd_device *device) 516162306a36Sopenharmony_ci{ 516262306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 516362306a36Sopenharmony_ci int rc; 516462306a36Sopenharmony_ci struct ccw1 *ccw; 516562306a36Sopenharmony_ci int useglobal; 516662306a36Sopenharmony_ci 516762306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 516862306a36Sopenharmony_ci return -EACCES; 516962306a36Sopenharmony_ci 517062306a36Sopenharmony_ci useglobal = 0; 517162306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL); 517262306a36Sopenharmony_ci if (IS_ERR(cqr)) { 517362306a36Sopenharmony_ci mutex_lock(&dasd_reserve_mutex); 517462306a36Sopenharmony_ci useglobal = 1; 517562306a36Sopenharmony_ci cqr = &dasd_reserve_req->cqr; 517662306a36Sopenharmony_ci memset(cqr, 0, sizeof(*cqr)); 517762306a36Sopenharmony_ci memset(&dasd_reserve_req->ccw, 0, 517862306a36Sopenharmony_ci sizeof(dasd_reserve_req->ccw)); 517962306a36Sopenharmony_ci cqr->cpaddr = &dasd_reserve_req->ccw; 518062306a36Sopenharmony_ci cqr->data = &dasd_reserve_req->data; 518162306a36Sopenharmony_ci cqr->magic = DASD_ECKD_MAGIC; 518262306a36Sopenharmony_ci } 518362306a36Sopenharmony_ci ccw = cqr->cpaddr; 518462306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_SLCK; 518562306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 518662306a36Sopenharmony_ci ccw->count = 32; 518762306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(cqr->data); 518862306a36Sopenharmony_ci cqr->startdev = device; 518962306a36Sopenharmony_ci cqr->memdev = device; 519062306a36Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); 519162306a36Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 519262306a36Sopenharmony_ci cqr->retries = 2; /* set retry counter to enable basic ERP */ 519362306a36Sopenharmony_ci cqr->expires = 2 * HZ; 519462306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 519562306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 519662306a36Sopenharmony_ci 519762306a36Sopenharmony_ci rc = dasd_sleep_on_immediatly(cqr); 519862306a36Sopenharmony_ci if (!rc) 519962306a36Sopenharmony_ci set_bit(DASD_FLAG_IS_RESERVED, &device->flags); 520062306a36Sopenharmony_ci 520162306a36Sopenharmony_ci if (useglobal) 520262306a36Sopenharmony_ci mutex_unlock(&dasd_reserve_mutex); 520362306a36Sopenharmony_ci else 520462306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 520562306a36Sopenharmony_ci return rc; 520662306a36Sopenharmony_ci} 520762306a36Sopenharmony_ci 520862306a36Sopenharmony_ci/* 520962306a36Sopenharmony_ci * SNID - Sense Path Group ID 521062306a36Sopenharmony_ci * This ioctl may be used in situations where I/O is stalled due to 521162306a36Sopenharmony_ci * a reserve, so if the normal dasd_smalloc_request fails, we use the 521262306a36Sopenharmony_ci * preallocated dasd_reserve_req. 521362306a36Sopenharmony_ci */ 521462306a36Sopenharmony_cistatic int dasd_eckd_snid(struct dasd_device *device, 521562306a36Sopenharmony_ci void __user *argp) 521662306a36Sopenharmony_ci{ 521762306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 521862306a36Sopenharmony_ci int rc; 521962306a36Sopenharmony_ci struct ccw1 *ccw; 522062306a36Sopenharmony_ci int useglobal; 522162306a36Sopenharmony_ci struct dasd_snid_ioctl_data usrparm; 522262306a36Sopenharmony_ci 522362306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 522462306a36Sopenharmony_ci return -EACCES; 522562306a36Sopenharmony_ci 522662306a36Sopenharmony_ci if (copy_from_user(&usrparm, argp, sizeof(usrparm))) 522762306a36Sopenharmony_ci return -EFAULT; 522862306a36Sopenharmony_ci 522962306a36Sopenharmony_ci useglobal = 0; 523062306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 523162306a36Sopenharmony_ci sizeof(struct dasd_snid_data), device, 523262306a36Sopenharmony_ci NULL); 523362306a36Sopenharmony_ci if (IS_ERR(cqr)) { 523462306a36Sopenharmony_ci mutex_lock(&dasd_reserve_mutex); 523562306a36Sopenharmony_ci useglobal = 1; 523662306a36Sopenharmony_ci cqr = &dasd_reserve_req->cqr; 523762306a36Sopenharmony_ci memset(cqr, 0, sizeof(*cqr)); 523862306a36Sopenharmony_ci memset(&dasd_reserve_req->ccw, 0, 523962306a36Sopenharmony_ci sizeof(dasd_reserve_req->ccw)); 524062306a36Sopenharmony_ci cqr->cpaddr = &dasd_reserve_req->ccw; 524162306a36Sopenharmony_ci cqr->data = &dasd_reserve_req->data; 524262306a36Sopenharmony_ci cqr->magic = DASD_ECKD_MAGIC; 524362306a36Sopenharmony_ci } 524462306a36Sopenharmony_ci ccw = cqr->cpaddr; 524562306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_SNID; 524662306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 524762306a36Sopenharmony_ci ccw->count = 12; 524862306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(cqr->data); 524962306a36Sopenharmony_ci cqr->startdev = device; 525062306a36Sopenharmony_ci cqr->memdev = device; 525162306a36Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); 525262306a36Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 525362306a36Sopenharmony_ci set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags); 525462306a36Sopenharmony_ci cqr->retries = 5; 525562306a36Sopenharmony_ci cqr->expires = 10 * HZ; 525662306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 525762306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 525862306a36Sopenharmony_ci cqr->lpm = usrparm.path_mask; 525962306a36Sopenharmony_ci 526062306a36Sopenharmony_ci rc = dasd_sleep_on_immediatly(cqr); 526162306a36Sopenharmony_ci /* verify that I/O processing didn't modify the path mask */ 526262306a36Sopenharmony_ci if (!rc && usrparm.path_mask && (cqr->lpm != usrparm.path_mask)) 526362306a36Sopenharmony_ci rc = -EIO; 526462306a36Sopenharmony_ci if (!rc) { 526562306a36Sopenharmony_ci usrparm.data = *((struct dasd_snid_data *)cqr->data); 526662306a36Sopenharmony_ci if (copy_to_user(argp, &usrparm, sizeof(usrparm))) 526762306a36Sopenharmony_ci rc = -EFAULT; 526862306a36Sopenharmony_ci } 526962306a36Sopenharmony_ci 527062306a36Sopenharmony_ci if (useglobal) 527162306a36Sopenharmony_ci mutex_unlock(&dasd_reserve_mutex); 527262306a36Sopenharmony_ci else 527362306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 527462306a36Sopenharmony_ci return rc; 527562306a36Sopenharmony_ci} 527662306a36Sopenharmony_ci 527762306a36Sopenharmony_ci/* 527862306a36Sopenharmony_ci * Read performance statistics 527962306a36Sopenharmony_ci */ 528062306a36Sopenharmony_cistatic int 528162306a36Sopenharmony_cidasd_eckd_performance(struct dasd_device *device, void __user *argp) 528262306a36Sopenharmony_ci{ 528362306a36Sopenharmony_ci struct dasd_psf_prssd_data *prssdp; 528462306a36Sopenharmony_ci struct dasd_rssd_perf_stats_t *stats; 528562306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 528662306a36Sopenharmony_ci struct ccw1 *ccw; 528762306a36Sopenharmony_ci int rc; 528862306a36Sopenharmony_ci 528962306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, 529062306a36Sopenharmony_ci (sizeof(struct dasd_psf_prssd_data) + 529162306a36Sopenharmony_ci sizeof(struct dasd_rssd_perf_stats_t)), 529262306a36Sopenharmony_ci device, NULL); 529362306a36Sopenharmony_ci if (IS_ERR(cqr)) { 529462306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 529562306a36Sopenharmony_ci "Could not allocate initialization request"); 529662306a36Sopenharmony_ci return PTR_ERR(cqr); 529762306a36Sopenharmony_ci } 529862306a36Sopenharmony_ci cqr->startdev = device; 529962306a36Sopenharmony_ci cqr->memdev = device; 530062306a36Sopenharmony_ci cqr->retries = 0; 530162306a36Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); 530262306a36Sopenharmony_ci cqr->expires = 10 * HZ; 530362306a36Sopenharmony_ci 530462306a36Sopenharmony_ci /* Prepare for Read Subsystem Data */ 530562306a36Sopenharmony_ci prssdp = (struct dasd_psf_prssd_data *) cqr->data; 530662306a36Sopenharmony_ci memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); 530762306a36Sopenharmony_ci prssdp->order = PSF_ORDER_PRSSD; 530862306a36Sopenharmony_ci prssdp->suborder = 0x01; /* Performance Statistics */ 530962306a36Sopenharmony_ci prssdp->varies[1] = 0x01; /* Perf Statistics for the Subsystem */ 531062306a36Sopenharmony_ci 531162306a36Sopenharmony_ci ccw = cqr->cpaddr; 531262306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 531362306a36Sopenharmony_ci ccw->count = sizeof(struct dasd_psf_prssd_data); 531462306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_CC; 531562306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(prssdp); 531662306a36Sopenharmony_ci 531762306a36Sopenharmony_ci /* Read Subsystem Data - Performance Statistics */ 531862306a36Sopenharmony_ci stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1); 531962306a36Sopenharmony_ci memset(stats, 0, sizeof(struct dasd_rssd_perf_stats_t)); 532062306a36Sopenharmony_ci 532162306a36Sopenharmony_ci ccw++; 532262306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RSSD; 532362306a36Sopenharmony_ci ccw->count = sizeof(struct dasd_rssd_perf_stats_t); 532462306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(stats); 532562306a36Sopenharmony_ci 532662306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 532762306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 532862306a36Sopenharmony_ci rc = dasd_sleep_on(cqr); 532962306a36Sopenharmony_ci if (rc == 0) { 533062306a36Sopenharmony_ci prssdp = (struct dasd_psf_prssd_data *) cqr->data; 533162306a36Sopenharmony_ci stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1); 533262306a36Sopenharmony_ci if (copy_to_user(argp, stats, 533362306a36Sopenharmony_ci sizeof(struct dasd_rssd_perf_stats_t))) 533462306a36Sopenharmony_ci rc = -EFAULT; 533562306a36Sopenharmony_ci } 533662306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 533762306a36Sopenharmony_ci return rc; 533862306a36Sopenharmony_ci} 533962306a36Sopenharmony_ci 534062306a36Sopenharmony_ci/* 534162306a36Sopenharmony_ci * Get attributes (cache operations) 534262306a36Sopenharmony_ci * Returnes the cache attributes used in Define Extend (DE). 534362306a36Sopenharmony_ci */ 534462306a36Sopenharmony_cistatic int 534562306a36Sopenharmony_cidasd_eckd_get_attrib(struct dasd_device *device, void __user *argp) 534662306a36Sopenharmony_ci{ 534762306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 534862306a36Sopenharmony_ci struct attrib_data_t attrib = private->attrib; 534962306a36Sopenharmony_ci int rc; 535062306a36Sopenharmony_ci 535162306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 535262306a36Sopenharmony_ci return -EACCES; 535362306a36Sopenharmony_ci if (!argp) 535462306a36Sopenharmony_ci return -EINVAL; 535562306a36Sopenharmony_ci 535662306a36Sopenharmony_ci rc = 0; 535762306a36Sopenharmony_ci if (copy_to_user(argp, (long *) &attrib, 535862306a36Sopenharmony_ci sizeof(struct attrib_data_t))) 535962306a36Sopenharmony_ci rc = -EFAULT; 536062306a36Sopenharmony_ci 536162306a36Sopenharmony_ci return rc; 536262306a36Sopenharmony_ci} 536362306a36Sopenharmony_ci 536462306a36Sopenharmony_ci/* 536562306a36Sopenharmony_ci * Set attributes (cache operations) 536662306a36Sopenharmony_ci * Stores the attributes for cache operation to be used in Define Extend (DE). 536762306a36Sopenharmony_ci */ 536862306a36Sopenharmony_cistatic int 536962306a36Sopenharmony_cidasd_eckd_set_attrib(struct dasd_device *device, void __user *argp) 537062306a36Sopenharmony_ci{ 537162306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 537262306a36Sopenharmony_ci struct attrib_data_t attrib; 537362306a36Sopenharmony_ci 537462306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 537562306a36Sopenharmony_ci return -EACCES; 537662306a36Sopenharmony_ci if (!argp) 537762306a36Sopenharmony_ci return -EINVAL; 537862306a36Sopenharmony_ci 537962306a36Sopenharmony_ci if (copy_from_user(&attrib, argp, sizeof(struct attrib_data_t))) 538062306a36Sopenharmony_ci return -EFAULT; 538162306a36Sopenharmony_ci private->attrib = attrib; 538262306a36Sopenharmony_ci 538362306a36Sopenharmony_ci dev_info(&device->cdev->dev, 538462306a36Sopenharmony_ci "The DASD cache mode was set to %x (%i cylinder prestage)\n", 538562306a36Sopenharmony_ci private->attrib.operation, private->attrib.nr_cyl); 538662306a36Sopenharmony_ci return 0; 538762306a36Sopenharmony_ci} 538862306a36Sopenharmony_ci 538962306a36Sopenharmony_ci/* 539062306a36Sopenharmony_ci * Issue syscall I/O to EMC Symmetrix array. 539162306a36Sopenharmony_ci * CCWs are PSF and RSSD 539262306a36Sopenharmony_ci */ 539362306a36Sopenharmony_cistatic int dasd_symm_io(struct dasd_device *device, void __user *argp) 539462306a36Sopenharmony_ci{ 539562306a36Sopenharmony_ci struct dasd_symmio_parms usrparm; 539662306a36Sopenharmony_ci char *psf_data, *rssd_result; 539762306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 539862306a36Sopenharmony_ci struct ccw1 *ccw; 539962306a36Sopenharmony_ci char psf0, psf1; 540062306a36Sopenharmony_ci int rc; 540162306a36Sopenharmony_ci 540262306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) 540362306a36Sopenharmony_ci return -EACCES; 540462306a36Sopenharmony_ci psf0 = psf1 = 0; 540562306a36Sopenharmony_ci 540662306a36Sopenharmony_ci /* Copy parms from caller */ 540762306a36Sopenharmony_ci rc = -EFAULT; 540862306a36Sopenharmony_ci if (copy_from_user(&usrparm, argp, sizeof(usrparm))) 540962306a36Sopenharmony_ci goto out; 541062306a36Sopenharmony_ci if (is_compat_task()) { 541162306a36Sopenharmony_ci /* Make sure pointers are sane even on 31 bit. */ 541262306a36Sopenharmony_ci rc = -EINVAL; 541362306a36Sopenharmony_ci if ((usrparm.psf_data >> 32) != 0) 541462306a36Sopenharmony_ci goto out; 541562306a36Sopenharmony_ci if ((usrparm.rssd_result >> 32) != 0) 541662306a36Sopenharmony_ci goto out; 541762306a36Sopenharmony_ci usrparm.psf_data &= 0x7fffffffULL; 541862306a36Sopenharmony_ci usrparm.rssd_result &= 0x7fffffffULL; 541962306a36Sopenharmony_ci } 542062306a36Sopenharmony_ci /* at least 2 bytes are accessed and should be allocated */ 542162306a36Sopenharmony_ci if (usrparm.psf_data_len < 2) { 542262306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, 542362306a36Sopenharmony_ci "Symmetrix ioctl invalid data length %d", 542462306a36Sopenharmony_ci usrparm.psf_data_len); 542562306a36Sopenharmony_ci rc = -EINVAL; 542662306a36Sopenharmony_ci goto out; 542762306a36Sopenharmony_ci } 542862306a36Sopenharmony_ci /* alloc I/O data area */ 542962306a36Sopenharmony_ci psf_data = kzalloc(usrparm.psf_data_len, GFP_KERNEL | GFP_DMA); 543062306a36Sopenharmony_ci rssd_result = kzalloc(usrparm.rssd_result_len, GFP_KERNEL | GFP_DMA); 543162306a36Sopenharmony_ci if (!psf_data || !rssd_result) { 543262306a36Sopenharmony_ci rc = -ENOMEM; 543362306a36Sopenharmony_ci goto out_free; 543462306a36Sopenharmony_ci } 543562306a36Sopenharmony_ci 543662306a36Sopenharmony_ci /* get syscall header from user space */ 543762306a36Sopenharmony_ci rc = -EFAULT; 543862306a36Sopenharmony_ci if (copy_from_user(psf_data, 543962306a36Sopenharmony_ci (void __user *)(unsigned long) usrparm.psf_data, 544062306a36Sopenharmony_ci usrparm.psf_data_len)) 544162306a36Sopenharmony_ci goto out_free; 544262306a36Sopenharmony_ci psf0 = psf_data[0]; 544362306a36Sopenharmony_ci psf1 = psf_data[1]; 544462306a36Sopenharmony_ci 544562306a36Sopenharmony_ci /* setup CCWs for PSF + RSSD */ 544662306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2, 0, device, NULL); 544762306a36Sopenharmony_ci if (IS_ERR(cqr)) { 544862306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 544962306a36Sopenharmony_ci "Could not allocate initialization request"); 545062306a36Sopenharmony_ci rc = PTR_ERR(cqr); 545162306a36Sopenharmony_ci goto out_free; 545262306a36Sopenharmony_ci } 545362306a36Sopenharmony_ci 545462306a36Sopenharmony_ci cqr->startdev = device; 545562306a36Sopenharmony_ci cqr->memdev = device; 545662306a36Sopenharmony_ci cqr->retries = 3; 545762306a36Sopenharmony_ci cqr->expires = 10 * HZ; 545862306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 545962306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 546062306a36Sopenharmony_ci 546162306a36Sopenharmony_ci /* Build the ccws */ 546262306a36Sopenharmony_ci ccw = cqr->cpaddr; 546362306a36Sopenharmony_ci 546462306a36Sopenharmony_ci /* PSF ccw */ 546562306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 546662306a36Sopenharmony_ci ccw->count = usrparm.psf_data_len; 546762306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_CC; 546862306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(psf_data); 546962306a36Sopenharmony_ci 547062306a36Sopenharmony_ci ccw++; 547162306a36Sopenharmony_ci 547262306a36Sopenharmony_ci /* RSSD ccw */ 547362306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RSSD; 547462306a36Sopenharmony_ci ccw->count = usrparm.rssd_result_len; 547562306a36Sopenharmony_ci ccw->flags = CCW_FLAG_SLI ; 547662306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(rssd_result); 547762306a36Sopenharmony_ci 547862306a36Sopenharmony_ci rc = dasd_sleep_on(cqr); 547962306a36Sopenharmony_ci if (rc) 548062306a36Sopenharmony_ci goto out_sfree; 548162306a36Sopenharmony_ci 548262306a36Sopenharmony_ci rc = -EFAULT; 548362306a36Sopenharmony_ci if (copy_to_user((void __user *)(unsigned long) usrparm.rssd_result, 548462306a36Sopenharmony_ci rssd_result, usrparm.rssd_result_len)) 548562306a36Sopenharmony_ci goto out_sfree; 548662306a36Sopenharmony_ci rc = 0; 548762306a36Sopenharmony_ci 548862306a36Sopenharmony_ciout_sfree: 548962306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 549062306a36Sopenharmony_ciout_free: 549162306a36Sopenharmony_ci kfree(rssd_result); 549262306a36Sopenharmony_ci kfree(psf_data); 549362306a36Sopenharmony_ciout: 549462306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, 549562306a36Sopenharmony_ci "Symmetrix ioctl (0x%02x 0x%02x): rc=%d", 549662306a36Sopenharmony_ci (int) psf0, (int) psf1, rc); 549762306a36Sopenharmony_ci return rc; 549862306a36Sopenharmony_ci} 549962306a36Sopenharmony_ci 550062306a36Sopenharmony_cistatic int 550162306a36Sopenharmony_cidasd_eckd_ioctl(struct dasd_block *block, unsigned int cmd, void __user *argp) 550262306a36Sopenharmony_ci{ 550362306a36Sopenharmony_ci struct dasd_device *device = block->base; 550462306a36Sopenharmony_ci 550562306a36Sopenharmony_ci switch (cmd) { 550662306a36Sopenharmony_ci case BIODASDGATTR: 550762306a36Sopenharmony_ci return dasd_eckd_get_attrib(device, argp); 550862306a36Sopenharmony_ci case BIODASDSATTR: 550962306a36Sopenharmony_ci return dasd_eckd_set_attrib(device, argp); 551062306a36Sopenharmony_ci case BIODASDPSRD: 551162306a36Sopenharmony_ci return dasd_eckd_performance(device, argp); 551262306a36Sopenharmony_ci case BIODASDRLSE: 551362306a36Sopenharmony_ci return dasd_eckd_release(device); 551462306a36Sopenharmony_ci case BIODASDRSRV: 551562306a36Sopenharmony_ci return dasd_eckd_reserve(device); 551662306a36Sopenharmony_ci case BIODASDSLCK: 551762306a36Sopenharmony_ci return dasd_eckd_steal_lock(device); 551862306a36Sopenharmony_ci case BIODASDSNID: 551962306a36Sopenharmony_ci return dasd_eckd_snid(device, argp); 552062306a36Sopenharmony_ci case BIODASDSYMMIO: 552162306a36Sopenharmony_ci return dasd_symm_io(device, argp); 552262306a36Sopenharmony_ci default: 552362306a36Sopenharmony_ci return -ENOTTY; 552462306a36Sopenharmony_ci } 552562306a36Sopenharmony_ci} 552662306a36Sopenharmony_ci 552762306a36Sopenharmony_ci/* 552862306a36Sopenharmony_ci * Dump the range of CCWs into 'page' buffer 552962306a36Sopenharmony_ci * and return number of printed chars. 553062306a36Sopenharmony_ci */ 553162306a36Sopenharmony_cistatic void 553262306a36Sopenharmony_cidasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page) 553362306a36Sopenharmony_ci{ 553462306a36Sopenharmony_ci int len, count; 553562306a36Sopenharmony_ci char *datap; 553662306a36Sopenharmony_ci 553762306a36Sopenharmony_ci len = 0; 553862306a36Sopenharmony_ci while (from <= to) { 553962306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 554062306a36Sopenharmony_ci " CCW %p: %08X %08X DAT:", 554162306a36Sopenharmony_ci from, ((int *) from)[0], ((int *) from)[1]); 554262306a36Sopenharmony_ci 554362306a36Sopenharmony_ci /* get pointer to data (consider IDALs) */ 554462306a36Sopenharmony_ci if (from->flags & CCW_FLAG_IDA) 554562306a36Sopenharmony_ci datap = (char *)*((addr_t *)phys_to_virt(from->cda)); 554662306a36Sopenharmony_ci else 554762306a36Sopenharmony_ci datap = phys_to_virt(from->cda); 554862306a36Sopenharmony_ci 554962306a36Sopenharmony_ci /* dump data (max 128 bytes) */ 555062306a36Sopenharmony_ci for (count = 0; count < from->count && count < 128; count++) { 555162306a36Sopenharmony_ci if (count % 32 == 0) 555262306a36Sopenharmony_ci len += sprintf(page + len, "\n"); 555362306a36Sopenharmony_ci if (count % 8 == 0) 555462306a36Sopenharmony_ci len += sprintf(page + len, " "); 555562306a36Sopenharmony_ci if (count % 4 == 0) 555662306a36Sopenharmony_ci len += sprintf(page + len, " "); 555762306a36Sopenharmony_ci len += sprintf(page + len, "%02x", datap[count]); 555862306a36Sopenharmony_ci } 555962306a36Sopenharmony_ci len += sprintf(page + len, "\n"); 556062306a36Sopenharmony_ci from++; 556162306a36Sopenharmony_ci } 556262306a36Sopenharmony_ci if (len > 0) 556362306a36Sopenharmony_ci printk(KERN_ERR "%s", page); 556462306a36Sopenharmony_ci} 556562306a36Sopenharmony_ci 556662306a36Sopenharmony_cistatic void 556762306a36Sopenharmony_cidasd_eckd_dump_sense_dbf(struct dasd_device *device, struct irb *irb, 556862306a36Sopenharmony_ci char *reason) 556962306a36Sopenharmony_ci{ 557062306a36Sopenharmony_ci u64 *sense; 557162306a36Sopenharmony_ci u64 *stat; 557262306a36Sopenharmony_ci 557362306a36Sopenharmony_ci sense = (u64 *) dasd_get_sense(irb); 557462306a36Sopenharmony_ci stat = (u64 *) &irb->scsw; 557562306a36Sopenharmony_ci if (sense) { 557662306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_EMERG, device, "%s: %016llx %08x : " 557762306a36Sopenharmony_ci "%016llx %016llx %016llx %016llx", 557862306a36Sopenharmony_ci reason, *stat, *((u32 *) (stat + 1)), 557962306a36Sopenharmony_ci sense[0], sense[1], sense[2], sense[3]); 558062306a36Sopenharmony_ci } else { 558162306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_EMERG, device, "%s: %016llx %08x : %s", 558262306a36Sopenharmony_ci reason, *stat, *((u32 *) (stat + 1)), 558362306a36Sopenharmony_ci "NO VALID SENSE"); 558462306a36Sopenharmony_ci } 558562306a36Sopenharmony_ci} 558662306a36Sopenharmony_ci 558762306a36Sopenharmony_ci/* 558862306a36Sopenharmony_ci * Print sense data and related channel program. 558962306a36Sopenharmony_ci * Parts are printed because printk buffer is only 1024 bytes. 559062306a36Sopenharmony_ci */ 559162306a36Sopenharmony_cistatic void dasd_eckd_dump_sense_ccw(struct dasd_device *device, 559262306a36Sopenharmony_ci struct dasd_ccw_req *req, struct irb *irb) 559362306a36Sopenharmony_ci{ 559462306a36Sopenharmony_ci char *page; 559562306a36Sopenharmony_ci struct ccw1 *first, *last, *fail, *from, *to; 559662306a36Sopenharmony_ci int len, sl, sct; 559762306a36Sopenharmony_ci 559862306a36Sopenharmony_ci page = (char *) get_zeroed_page(GFP_ATOMIC); 559962306a36Sopenharmony_ci if (page == NULL) { 560062306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 560162306a36Sopenharmony_ci "No memory to dump sense data\n"); 560262306a36Sopenharmony_ci return; 560362306a36Sopenharmony_ci } 560462306a36Sopenharmony_ci /* dump the sense data */ 560562306a36Sopenharmony_ci len = sprintf(page, PRINTK_HEADER 560662306a36Sopenharmony_ci " I/O status report for device %s:\n", 560762306a36Sopenharmony_ci dev_name(&device->cdev->dev)); 560862306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 560962306a36Sopenharmony_ci " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X " 561062306a36Sopenharmony_ci "CS:%02X RC:%d\n", 561162306a36Sopenharmony_ci req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw), 561262306a36Sopenharmony_ci scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw), 561362306a36Sopenharmony_ci scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw), 561462306a36Sopenharmony_ci req ? req->intrc : 0); 561562306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 561662306a36Sopenharmony_ci " device %s: Failing CCW: %p\n", 561762306a36Sopenharmony_ci dev_name(&device->cdev->dev), 561862306a36Sopenharmony_ci phys_to_virt(irb->scsw.cmd.cpa)); 561962306a36Sopenharmony_ci if (irb->esw.esw0.erw.cons) { 562062306a36Sopenharmony_ci for (sl = 0; sl < 4; sl++) { 562162306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 562262306a36Sopenharmony_ci " Sense(hex) %2d-%2d:", 562362306a36Sopenharmony_ci (8 * sl), ((8 * sl) + 7)); 562462306a36Sopenharmony_ci 562562306a36Sopenharmony_ci for (sct = 0; sct < 8; sct++) { 562662306a36Sopenharmony_ci len += sprintf(page + len, " %02x", 562762306a36Sopenharmony_ci irb->ecw[8 * sl + sct]); 562862306a36Sopenharmony_ci } 562962306a36Sopenharmony_ci len += sprintf(page + len, "\n"); 563062306a36Sopenharmony_ci } 563162306a36Sopenharmony_ci 563262306a36Sopenharmony_ci if (irb->ecw[27] & DASD_SENSE_BIT_0) { 563362306a36Sopenharmony_ci /* 24 Byte Sense Data */ 563462306a36Sopenharmony_ci sprintf(page + len, PRINTK_HEADER 563562306a36Sopenharmony_ci " 24 Byte: %x MSG %x, " 563662306a36Sopenharmony_ci "%s MSGb to SYSOP\n", 563762306a36Sopenharmony_ci irb->ecw[7] >> 4, irb->ecw[7] & 0x0f, 563862306a36Sopenharmony_ci irb->ecw[1] & 0x10 ? "" : "no"); 563962306a36Sopenharmony_ci } else { 564062306a36Sopenharmony_ci /* 32 Byte Sense Data */ 564162306a36Sopenharmony_ci sprintf(page + len, PRINTK_HEADER 564262306a36Sopenharmony_ci " 32 Byte: Format: %x " 564362306a36Sopenharmony_ci "Exception class %x\n", 564462306a36Sopenharmony_ci irb->ecw[6] & 0x0f, irb->ecw[22] >> 4); 564562306a36Sopenharmony_ci } 564662306a36Sopenharmony_ci } else { 564762306a36Sopenharmony_ci sprintf(page + len, PRINTK_HEADER 564862306a36Sopenharmony_ci " SORRY - NO VALID SENSE AVAILABLE\n"); 564962306a36Sopenharmony_ci } 565062306a36Sopenharmony_ci printk(KERN_ERR "%s", page); 565162306a36Sopenharmony_ci 565262306a36Sopenharmony_ci if (req) { 565362306a36Sopenharmony_ci /* req == NULL for unsolicited interrupts */ 565462306a36Sopenharmony_ci /* dump the Channel Program (max 140 Bytes per line) */ 565562306a36Sopenharmony_ci /* Count CCW and print first CCWs (maximum 7) */ 565662306a36Sopenharmony_ci first = req->cpaddr; 565762306a36Sopenharmony_ci for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); 565862306a36Sopenharmony_ci to = min(first + 6, last); 565962306a36Sopenharmony_ci printk(KERN_ERR PRINTK_HEADER " Related CP in req: %p\n", req); 566062306a36Sopenharmony_ci dasd_eckd_dump_ccw_range(first, to, page); 566162306a36Sopenharmony_ci 566262306a36Sopenharmony_ci /* print failing CCW area (maximum 4) */ 566362306a36Sopenharmony_ci /* scsw->cda is either valid or zero */ 566462306a36Sopenharmony_ci from = ++to; 566562306a36Sopenharmony_ci fail = phys_to_virt(irb->scsw.cmd.cpa); /* failing CCW */ 566662306a36Sopenharmony_ci if (from < fail - 2) { 566762306a36Sopenharmony_ci from = fail - 2; /* there is a gap - print header */ 566862306a36Sopenharmony_ci printk(KERN_ERR PRINTK_HEADER "......\n"); 566962306a36Sopenharmony_ci } 567062306a36Sopenharmony_ci to = min(fail + 1, last); 567162306a36Sopenharmony_ci dasd_eckd_dump_ccw_range(from, to, page + len); 567262306a36Sopenharmony_ci 567362306a36Sopenharmony_ci /* print last CCWs (maximum 2) */ 567462306a36Sopenharmony_ci len = 0; 567562306a36Sopenharmony_ci from = max(from, ++to); 567662306a36Sopenharmony_ci if (from < last - 1) { 567762306a36Sopenharmony_ci from = last - 1; /* there is a gap - print header */ 567862306a36Sopenharmony_ci printk(KERN_ERR PRINTK_HEADER "......\n"); 567962306a36Sopenharmony_ci } 568062306a36Sopenharmony_ci dasd_eckd_dump_ccw_range(from, last, page + len); 568162306a36Sopenharmony_ci } 568262306a36Sopenharmony_ci free_page((unsigned long) page); 568362306a36Sopenharmony_ci} 568462306a36Sopenharmony_ci 568562306a36Sopenharmony_ci 568662306a36Sopenharmony_ci/* 568762306a36Sopenharmony_ci * Print sense data from a tcw. 568862306a36Sopenharmony_ci */ 568962306a36Sopenharmony_cistatic void dasd_eckd_dump_sense_tcw(struct dasd_device *device, 569062306a36Sopenharmony_ci struct dasd_ccw_req *req, struct irb *irb) 569162306a36Sopenharmony_ci{ 569262306a36Sopenharmony_ci char *page; 569362306a36Sopenharmony_ci int len, sl, sct, residual; 569462306a36Sopenharmony_ci struct tsb *tsb; 569562306a36Sopenharmony_ci u8 *sense, *rcq; 569662306a36Sopenharmony_ci 569762306a36Sopenharmony_ci page = (char *) get_zeroed_page(GFP_ATOMIC); 569862306a36Sopenharmony_ci if (page == NULL) { 569962306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, " %s", 570062306a36Sopenharmony_ci "No memory to dump sense data"); 570162306a36Sopenharmony_ci return; 570262306a36Sopenharmony_ci } 570362306a36Sopenharmony_ci /* dump the sense data */ 570462306a36Sopenharmony_ci len = sprintf(page, PRINTK_HEADER 570562306a36Sopenharmony_ci " I/O status report for device %s:\n", 570662306a36Sopenharmony_ci dev_name(&device->cdev->dev)); 570762306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 570862306a36Sopenharmony_ci " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X " 570962306a36Sopenharmony_ci "CS:%02X fcxs:%02X schxs:%02X RC:%d\n", 571062306a36Sopenharmony_ci req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw), 571162306a36Sopenharmony_ci scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw), 571262306a36Sopenharmony_ci scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw), 571362306a36Sopenharmony_ci irb->scsw.tm.fcxs, 571462306a36Sopenharmony_ci (irb->scsw.tm.ifob << 7) | irb->scsw.tm.sesq, 571562306a36Sopenharmony_ci req ? req->intrc : 0); 571662306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 571762306a36Sopenharmony_ci " device %s: Failing TCW: %p\n", 571862306a36Sopenharmony_ci dev_name(&device->cdev->dev), 571962306a36Sopenharmony_ci phys_to_virt(irb->scsw.tm.tcw)); 572062306a36Sopenharmony_ci 572162306a36Sopenharmony_ci tsb = NULL; 572262306a36Sopenharmony_ci sense = NULL; 572362306a36Sopenharmony_ci if (irb->scsw.tm.tcw && (irb->scsw.tm.fcxs & 0x01)) 572462306a36Sopenharmony_ci tsb = tcw_get_tsb(phys_to_virt(irb->scsw.tm.tcw)); 572562306a36Sopenharmony_ci 572662306a36Sopenharmony_ci if (tsb) { 572762306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 572862306a36Sopenharmony_ci " tsb->length %d\n", tsb->length); 572962306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 573062306a36Sopenharmony_ci " tsb->flags %x\n", tsb->flags); 573162306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 573262306a36Sopenharmony_ci " tsb->dcw_offset %d\n", tsb->dcw_offset); 573362306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 573462306a36Sopenharmony_ci " tsb->count %d\n", tsb->count); 573562306a36Sopenharmony_ci residual = tsb->count - 28; 573662306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 573762306a36Sopenharmony_ci " residual %d\n", residual); 573862306a36Sopenharmony_ci 573962306a36Sopenharmony_ci switch (tsb->flags & 0x07) { 574062306a36Sopenharmony_ci case 1: /* tsa_iostat */ 574162306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 574262306a36Sopenharmony_ci " tsb->tsa.iostat.dev_time %d\n", 574362306a36Sopenharmony_ci tsb->tsa.iostat.dev_time); 574462306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 574562306a36Sopenharmony_ci " tsb->tsa.iostat.def_time %d\n", 574662306a36Sopenharmony_ci tsb->tsa.iostat.def_time); 574762306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 574862306a36Sopenharmony_ci " tsb->tsa.iostat.queue_time %d\n", 574962306a36Sopenharmony_ci tsb->tsa.iostat.queue_time); 575062306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 575162306a36Sopenharmony_ci " tsb->tsa.iostat.dev_busy_time %d\n", 575262306a36Sopenharmony_ci tsb->tsa.iostat.dev_busy_time); 575362306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 575462306a36Sopenharmony_ci " tsb->tsa.iostat.dev_act_time %d\n", 575562306a36Sopenharmony_ci tsb->tsa.iostat.dev_act_time); 575662306a36Sopenharmony_ci sense = tsb->tsa.iostat.sense; 575762306a36Sopenharmony_ci break; 575862306a36Sopenharmony_ci case 2: /* ts_ddpc */ 575962306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 576062306a36Sopenharmony_ci " tsb->tsa.ddpc.rc %d\n", tsb->tsa.ddpc.rc); 576162306a36Sopenharmony_ci for (sl = 0; sl < 2; sl++) { 576262306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 576362306a36Sopenharmony_ci " tsb->tsa.ddpc.rcq %2d-%2d: ", 576462306a36Sopenharmony_ci (8 * sl), ((8 * sl) + 7)); 576562306a36Sopenharmony_ci rcq = tsb->tsa.ddpc.rcq; 576662306a36Sopenharmony_ci for (sct = 0; sct < 8; sct++) { 576762306a36Sopenharmony_ci len += sprintf(page + len, " %02x", 576862306a36Sopenharmony_ci rcq[8 * sl + sct]); 576962306a36Sopenharmony_ci } 577062306a36Sopenharmony_ci len += sprintf(page + len, "\n"); 577162306a36Sopenharmony_ci } 577262306a36Sopenharmony_ci sense = tsb->tsa.ddpc.sense; 577362306a36Sopenharmony_ci break; 577462306a36Sopenharmony_ci case 3: /* tsa_intrg */ 577562306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 577662306a36Sopenharmony_ci " tsb->tsa.intrg.: not supported yet\n"); 577762306a36Sopenharmony_ci break; 577862306a36Sopenharmony_ci } 577962306a36Sopenharmony_ci 578062306a36Sopenharmony_ci if (sense) { 578162306a36Sopenharmony_ci for (sl = 0; sl < 4; sl++) { 578262306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 578362306a36Sopenharmony_ci " Sense(hex) %2d-%2d:", 578462306a36Sopenharmony_ci (8 * sl), ((8 * sl) + 7)); 578562306a36Sopenharmony_ci for (sct = 0; sct < 8; sct++) { 578662306a36Sopenharmony_ci len += sprintf(page + len, " %02x", 578762306a36Sopenharmony_ci sense[8 * sl + sct]); 578862306a36Sopenharmony_ci } 578962306a36Sopenharmony_ci len += sprintf(page + len, "\n"); 579062306a36Sopenharmony_ci } 579162306a36Sopenharmony_ci 579262306a36Sopenharmony_ci if (sense[27] & DASD_SENSE_BIT_0) { 579362306a36Sopenharmony_ci /* 24 Byte Sense Data */ 579462306a36Sopenharmony_ci sprintf(page + len, PRINTK_HEADER 579562306a36Sopenharmony_ci " 24 Byte: %x MSG %x, " 579662306a36Sopenharmony_ci "%s MSGb to SYSOP\n", 579762306a36Sopenharmony_ci sense[7] >> 4, sense[7] & 0x0f, 579862306a36Sopenharmony_ci sense[1] & 0x10 ? "" : "no"); 579962306a36Sopenharmony_ci } else { 580062306a36Sopenharmony_ci /* 32 Byte Sense Data */ 580162306a36Sopenharmony_ci sprintf(page + len, PRINTK_HEADER 580262306a36Sopenharmony_ci " 32 Byte: Format: %x " 580362306a36Sopenharmony_ci "Exception class %x\n", 580462306a36Sopenharmony_ci sense[6] & 0x0f, sense[22] >> 4); 580562306a36Sopenharmony_ci } 580662306a36Sopenharmony_ci } else { 580762306a36Sopenharmony_ci sprintf(page + len, PRINTK_HEADER 580862306a36Sopenharmony_ci " SORRY - NO VALID SENSE AVAILABLE\n"); 580962306a36Sopenharmony_ci } 581062306a36Sopenharmony_ci } else { 581162306a36Sopenharmony_ci sprintf(page + len, PRINTK_HEADER 581262306a36Sopenharmony_ci " SORRY - NO TSB DATA AVAILABLE\n"); 581362306a36Sopenharmony_ci } 581462306a36Sopenharmony_ci printk(KERN_ERR "%s", page); 581562306a36Sopenharmony_ci free_page((unsigned long) page); 581662306a36Sopenharmony_ci} 581762306a36Sopenharmony_ci 581862306a36Sopenharmony_cistatic void dasd_eckd_dump_sense(struct dasd_device *device, 581962306a36Sopenharmony_ci struct dasd_ccw_req *req, struct irb *irb) 582062306a36Sopenharmony_ci{ 582162306a36Sopenharmony_ci u8 *sense = dasd_get_sense(irb); 582262306a36Sopenharmony_ci 582362306a36Sopenharmony_ci if (scsw_is_tm(&irb->scsw)) { 582462306a36Sopenharmony_ci /* 582562306a36Sopenharmony_ci * In some cases the 'File Protected' or 'Incorrect Length' 582662306a36Sopenharmony_ci * error might be expected and log messages shouldn't be written 582762306a36Sopenharmony_ci * then. Check if the according suppress bit is set. 582862306a36Sopenharmony_ci */ 582962306a36Sopenharmony_ci if (sense && (sense[1] & SNS1_FILE_PROTECTED) && 583062306a36Sopenharmony_ci test_bit(DASD_CQR_SUPPRESS_FP, &req->flags)) 583162306a36Sopenharmony_ci return; 583262306a36Sopenharmony_ci if (scsw_cstat(&irb->scsw) == 0x40 && 583362306a36Sopenharmony_ci test_bit(DASD_CQR_SUPPRESS_IL, &req->flags)) 583462306a36Sopenharmony_ci return; 583562306a36Sopenharmony_ci 583662306a36Sopenharmony_ci dasd_eckd_dump_sense_tcw(device, req, irb); 583762306a36Sopenharmony_ci } else { 583862306a36Sopenharmony_ci /* 583962306a36Sopenharmony_ci * In some cases the 'Command Reject' or 'No Record Found' 584062306a36Sopenharmony_ci * error might be expected and log messages shouldn't be 584162306a36Sopenharmony_ci * written then. Check if the according suppress bit is set. 584262306a36Sopenharmony_ci */ 584362306a36Sopenharmony_ci if (sense && sense[0] & SNS0_CMD_REJECT && 584462306a36Sopenharmony_ci test_bit(DASD_CQR_SUPPRESS_CR, &req->flags)) 584562306a36Sopenharmony_ci return; 584662306a36Sopenharmony_ci 584762306a36Sopenharmony_ci if (sense && sense[1] & SNS1_NO_REC_FOUND && 584862306a36Sopenharmony_ci test_bit(DASD_CQR_SUPPRESS_NRF, &req->flags)) 584962306a36Sopenharmony_ci return; 585062306a36Sopenharmony_ci 585162306a36Sopenharmony_ci dasd_eckd_dump_sense_ccw(device, req, irb); 585262306a36Sopenharmony_ci } 585362306a36Sopenharmony_ci} 585462306a36Sopenharmony_ci 585562306a36Sopenharmony_cistatic int dasd_eckd_reload_device(struct dasd_device *device) 585662306a36Sopenharmony_ci{ 585762306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 585862306a36Sopenharmony_ci char print_uid[DASD_UID_STRLEN]; 585962306a36Sopenharmony_ci int rc, old_base; 586062306a36Sopenharmony_ci struct dasd_uid uid; 586162306a36Sopenharmony_ci unsigned long flags; 586262306a36Sopenharmony_ci 586362306a36Sopenharmony_ci /* 586462306a36Sopenharmony_ci * remove device from alias handling to prevent new requests 586562306a36Sopenharmony_ci * from being scheduled on the wrong alias device 586662306a36Sopenharmony_ci */ 586762306a36Sopenharmony_ci dasd_alias_remove_device(device); 586862306a36Sopenharmony_ci 586962306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 587062306a36Sopenharmony_ci old_base = private->uid.base_unit_addr; 587162306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 587262306a36Sopenharmony_ci 587362306a36Sopenharmony_ci /* Read Configuration Data */ 587462306a36Sopenharmony_ci rc = dasd_eckd_read_conf(device); 587562306a36Sopenharmony_ci if (rc) 587662306a36Sopenharmony_ci goto out_err; 587762306a36Sopenharmony_ci 587862306a36Sopenharmony_ci dasd_eckd_read_fc_security(device); 587962306a36Sopenharmony_ci 588062306a36Sopenharmony_ci rc = dasd_eckd_generate_uid(device); 588162306a36Sopenharmony_ci if (rc) 588262306a36Sopenharmony_ci goto out_err; 588362306a36Sopenharmony_ci /* 588462306a36Sopenharmony_ci * update unit address configuration and 588562306a36Sopenharmony_ci * add device to alias management 588662306a36Sopenharmony_ci */ 588762306a36Sopenharmony_ci dasd_alias_update_add_device(device); 588862306a36Sopenharmony_ci 588962306a36Sopenharmony_ci dasd_eckd_get_uid(device, &uid); 589062306a36Sopenharmony_ci 589162306a36Sopenharmony_ci if (old_base != uid.base_unit_addr) { 589262306a36Sopenharmony_ci dasd_eckd_get_uid_string(&private->conf, print_uid); 589362306a36Sopenharmony_ci dev_info(&device->cdev->dev, 589462306a36Sopenharmony_ci "An Alias device was reassigned to a new base device " 589562306a36Sopenharmony_ci "with UID: %s\n", print_uid); 589662306a36Sopenharmony_ci } 589762306a36Sopenharmony_ci return 0; 589862306a36Sopenharmony_ci 589962306a36Sopenharmony_ciout_err: 590062306a36Sopenharmony_ci return -1; 590162306a36Sopenharmony_ci} 590262306a36Sopenharmony_ci 590362306a36Sopenharmony_cistatic int dasd_eckd_read_message_buffer(struct dasd_device *device, 590462306a36Sopenharmony_ci struct dasd_rssd_messages *messages, 590562306a36Sopenharmony_ci __u8 lpum) 590662306a36Sopenharmony_ci{ 590762306a36Sopenharmony_ci struct dasd_rssd_messages *message_buf; 590862306a36Sopenharmony_ci struct dasd_psf_prssd_data *prssdp; 590962306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 591062306a36Sopenharmony_ci struct ccw1 *ccw; 591162306a36Sopenharmony_ci int rc; 591262306a36Sopenharmony_ci 591362306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, 591462306a36Sopenharmony_ci (sizeof(struct dasd_psf_prssd_data) + 591562306a36Sopenharmony_ci sizeof(struct dasd_rssd_messages)), 591662306a36Sopenharmony_ci device, NULL); 591762306a36Sopenharmony_ci if (IS_ERR(cqr)) { 591862306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 591962306a36Sopenharmony_ci "Could not allocate read message buffer request"); 592062306a36Sopenharmony_ci return PTR_ERR(cqr); 592162306a36Sopenharmony_ci } 592262306a36Sopenharmony_ci 592362306a36Sopenharmony_ci cqr->lpm = lpum; 592462306a36Sopenharmony_ciretry: 592562306a36Sopenharmony_ci cqr->startdev = device; 592662306a36Sopenharmony_ci cqr->memdev = device; 592762306a36Sopenharmony_ci cqr->block = NULL; 592862306a36Sopenharmony_ci cqr->expires = 10 * HZ; 592962306a36Sopenharmony_ci set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags); 593062306a36Sopenharmony_ci /* dasd_sleep_on_immediatly does not do complex error 593162306a36Sopenharmony_ci * recovery so clear erp flag and set retry counter to 593262306a36Sopenharmony_ci * do basic erp */ 593362306a36Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); 593462306a36Sopenharmony_ci cqr->retries = 256; 593562306a36Sopenharmony_ci 593662306a36Sopenharmony_ci /* Prepare for Read Subsystem Data */ 593762306a36Sopenharmony_ci prssdp = (struct dasd_psf_prssd_data *) cqr->data; 593862306a36Sopenharmony_ci memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); 593962306a36Sopenharmony_ci prssdp->order = PSF_ORDER_PRSSD; 594062306a36Sopenharmony_ci prssdp->suborder = 0x03; /* Message Buffer */ 594162306a36Sopenharmony_ci /* all other bytes of prssdp must be zero */ 594262306a36Sopenharmony_ci 594362306a36Sopenharmony_ci ccw = cqr->cpaddr; 594462306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 594562306a36Sopenharmony_ci ccw->count = sizeof(struct dasd_psf_prssd_data); 594662306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_CC; 594762306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 594862306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(prssdp); 594962306a36Sopenharmony_ci 595062306a36Sopenharmony_ci /* Read Subsystem Data - message buffer */ 595162306a36Sopenharmony_ci message_buf = (struct dasd_rssd_messages *) (prssdp + 1); 595262306a36Sopenharmony_ci memset(message_buf, 0, sizeof(struct dasd_rssd_messages)); 595362306a36Sopenharmony_ci 595462306a36Sopenharmony_ci ccw++; 595562306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RSSD; 595662306a36Sopenharmony_ci ccw->count = sizeof(struct dasd_rssd_messages); 595762306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 595862306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(message_buf); 595962306a36Sopenharmony_ci 596062306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 596162306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 596262306a36Sopenharmony_ci rc = dasd_sleep_on_immediatly(cqr); 596362306a36Sopenharmony_ci if (rc == 0) { 596462306a36Sopenharmony_ci prssdp = (struct dasd_psf_prssd_data *) cqr->data; 596562306a36Sopenharmony_ci message_buf = (struct dasd_rssd_messages *) 596662306a36Sopenharmony_ci (prssdp + 1); 596762306a36Sopenharmony_ci memcpy(messages, message_buf, 596862306a36Sopenharmony_ci sizeof(struct dasd_rssd_messages)); 596962306a36Sopenharmony_ci } else if (cqr->lpm) { 597062306a36Sopenharmony_ci /* 597162306a36Sopenharmony_ci * on z/VM we might not be able to do I/O on the requested path 597262306a36Sopenharmony_ci * but instead we get the required information on any path 597362306a36Sopenharmony_ci * so retry with open path mask 597462306a36Sopenharmony_ci */ 597562306a36Sopenharmony_ci cqr->lpm = 0; 597662306a36Sopenharmony_ci goto retry; 597762306a36Sopenharmony_ci } else 597862306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 597962306a36Sopenharmony_ci "Reading messages failed with rc=%d\n" 598062306a36Sopenharmony_ci , rc); 598162306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 598262306a36Sopenharmony_ci return rc; 598362306a36Sopenharmony_ci} 598462306a36Sopenharmony_ci 598562306a36Sopenharmony_cistatic int dasd_eckd_query_host_access(struct dasd_device *device, 598662306a36Sopenharmony_ci struct dasd_psf_query_host_access *data) 598762306a36Sopenharmony_ci{ 598862306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 598962306a36Sopenharmony_ci struct dasd_psf_query_host_access *host_access; 599062306a36Sopenharmony_ci struct dasd_psf_prssd_data *prssdp; 599162306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 599262306a36Sopenharmony_ci struct ccw1 *ccw; 599362306a36Sopenharmony_ci int rc; 599462306a36Sopenharmony_ci 599562306a36Sopenharmony_ci /* not available for HYPER PAV alias devices */ 599662306a36Sopenharmony_ci if (!device->block && private->lcu->pav == HYPER_PAV) 599762306a36Sopenharmony_ci return -EOPNOTSUPP; 599862306a36Sopenharmony_ci 599962306a36Sopenharmony_ci /* may not be supported by the storage server */ 600062306a36Sopenharmony_ci if (!(private->features.feature[14] & 0x80)) 600162306a36Sopenharmony_ci return -EOPNOTSUPP; 600262306a36Sopenharmony_ci 600362306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, 600462306a36Sopenharmony_ci sizeof(struct dasd_psf_prssd_data) + 1, 600562306a36Sopenharmony_ci device, NULL); 600662306a36Sopenharmony_ci if (IS_ERR(cqr)) { 600762306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 600862306a36Sopenharmony_ci "Could not allocate read message buffer request"); 600962306a36Sopenharmony_ci return PTR_ERR(cqr); 601062306a36Sopenharmony_ci } 601162306a36Sopenharmony_ci host_access = kzalloc(sizeof(*host_access), GFP_KERNEL | GFP_DMA); 601262306a36Sopenharmony_ci if (!host_access) { 601362306a36Sopenharmony_ci dasd_sfree_request(cqr, device); 601462306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 601562306a36Sopenharmony_ci "Could not allocate host_access buffer"); 601662306a36Sopenharmony_ci return -ENOMEM; 601762306a36Sopenharmony_ci } 601862306a36Sopenharmony_ci cqr->startdev = device; 601962306a36Sopenharmony_ci cqr->memdev = device; 602062306a36Sopenharmony_ci cqr->block = NULL; 602162306a36Sopenharmony_ci cqr->retries = 256; 602262306a36Sopenharmony_ci cqr->expires = 10 * HZ; 602362306a36Sopenharmony_ci 602462306a36Sopenharmony_ci /* Prepare for Read Subsystem Data */ 602562306a36Sopenharmony_ci prssdp = (struct dasd_psf_prssd_data *) cqr->data; 602662306a36Sopenharmony_ci memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); 602762306a36Sopenharmony_ci prssdp->order = PSF_ORDER_PRSSD; 602862306a36Sopenharmony_ci prssdp->suborder = PSF_SUBORDER_QHA; /* query host access */ 602962306a36Sopenharmony_ci /* LSS and Volume that will be queried */ 603062306a36Sopenharmony_ci prssdp->lss = private->conf.ned->ID; 603162306a36Sopenharmony_ci prssdp->volume = private->conf.ned->unit_addr; 603262306a36Sopenharmony_ci /* all other bytes of prssdp must be zero */ 603362306a36Sopenharmony_ci 603462306a36Sopenharmony_ci ccw = cqr->cpaddr; 603562306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 603662306a36Sopenharmony_ci ccw->count = sizeof(struct dasd_psf_prssd_data); 603762306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_CC; 603862306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 603962306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(prssdp); 604062306a36Sopenharmony_ci 604162306a36Sopenharmony_ci /* Read Subsystem Data - query host access */ 604262306a36Sopenharmony_ci ccw++; 604362306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RSSD; 604462306a36Sopenharmony_ci ccw->count = sizeof(struct dasd_psf_query_host_access); 604562306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 604662306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(host_access); 604762306a36Sopenharmony_ci 604862306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 604962306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 605062306a36Sopenharmony_ci /* the command might not be supported, suppress error message */ 605162306a36Sopenharmony_ci __set_bit(DASD_CQR_SUPPRESS_CR, &cqr->flags); 605262306a36Sopenharmony_ci rc = dasd_sleep_on_interruptible(cqr); 605362306a36Sopenharmony_ci if (rc == 0) { 605462306a36Sopenharmony_ci *data = *host_access; 605562306a36Sopenharmony_ci } else { 605662306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 605762306a36Sopenharmony_ci "Reading host access data failed with rc=%d\n", 605862306a36Sopenharmony_ci rc); 605962306a36Sopenharmony_ci rc = -EOPNOTSUPP; 606062306a36Sopenharmony_ci } 606162306a36Sopenharmony_ci 606262306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 606362306a36Sopenharmony_ci kfree(host_access); 606462306a36Sopenharmony_ci return rc; 606562306a36Sopenharmony_ci} 606662306a36Sopenharmony_ci/* 606762306a36Sopenharmony_ci * return number of grouped devices 606862306a36Sopenharmony_ci */ 606962306a36Sopenharmony_cistatic int dasd_eckd_host_access_count(struct dasd_device *device) 607062306a36Sopenharmony_ci{ 607162306a36Sopenharmony_ci struct dasd_psf_query_host_access *access; 607262306a36Sopenharmony_ci struct dasd_ckd_path_group_entry *entry; 607362306a36Sopenharmony_ci struct dasd_ckd_host_information *info; 607462306a36Sopenharmony_ci int count = 0; 607562306a36Sopenharmony_ci int rc, i; 607662306a36Sopenharmony_ci 607762306a36Sopenharmony_ci access = kzalloc(sizeof(*access), GFP_NOIO); 607862306a36Sopenharmony_ci if (!access) { 607962306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 608062306a36Sopenharmony_ci "Could not allocate access buffer"); 608162306a36Sopenharmony_ci return -ENOMEM; 608262306a36Sopenharmony_ci } 608362306a36Sopenharmony_ci rc = dasd_eckd_query_host_access(device, access); 608462306a36Sopenharmony_ci if (rc) { 608562306a36Sopenharmony_ci kfree(access); 608662306a36Sopenharmony_ci return rc; 608762306a36Sopenharmony_ci } 608862306a36Sopenharmony_ci 608962306a36Sopenharmony_ci info = (struct dasd_ckd_host_information *) 609062306a36Sopenharmony_ci access->host_access_information; 609162306a36Sopenharmony_ci for (i = 0; i < info->entry_count; i++) { 609262306a36Sopenharmony_ci entry = (struct dasd_ckd_path_group_entry *) 609362306a36Sopenharmony_ci (info->entry + i * info->entry_size); 609462306a36Sopenharmony_ci if (entry->status_flags & DASD_ECKD_PG_GROUPED) 609562306a36Sopenharmony_ci count++; 609662306a36Sopenharmony_ci } 609762306a36Sopenharmony_ci 609862306a36Sopenharmony_ci kfree(access); 609962306a36Sopenharmony_ci return count; 610062306a36Sopenharmony_ci} 610162306a36Sopenharmony_ci 610262306a36Sopenharmony_ci/* 610362306a36Sopenharmony_ci * write host access information to a sequential file 610462306a36Sopenharmony_ci */ 610562306a36Sopenharmony_cistatic int dasd_hosts_print(struct dasd_device *device, struct seq_file *m) 610662306a36Sopenharmony_ci{ 610762306a36Sopenharmony_ci struct dasd_psf_query_host_access *access; 610862306a36Sopenharmony_ci struct dasd_ckd_path_group_entry *entry; 610962306a36Sopenharmony_ci struct dasd_ckd_host_information *info; 611062306a36Sopenharmony_ci char sysplex[9] = ""; 611162306a36Sopenharmony_ci int rc, i; 611262306a36Sopenharmony_ci 611362306a36Sopenharmony_ci access = kzalloc(sizeof(*access), GFP_NOIO); 611462306a36Sopenharmony_ci if (!access) { 611562306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 611662306a36Sopenharmony_ci "Could not allocate access buffer"); 611762306a36Sopenharmony_ci return -ENOMEM; 611862306a36Sopenharmony_ci } 611962306a36Sopenharmony_ci rc = dasd_eckd_query_host_access(device, access); 612062306a36Sopenharmony_ci if (rc) { 612162306a36Sopenharmony_ci kfree(access); 612262306a36Sopenharmony_ci return rc; 612362306a36Sopenharmony_ci } 612462306a36Sopenharmony_ci 612562306a36Sopenharmony_ci info = (struct dasd_ckd_host_information *) 612662306a36Sopenharmony_ci access->host_access_information; 612762306a36Sopenharmony_ci for (i = 0; i < info->entry_count; i++) { 612862306a36Sopenharmony_ci entry = (struct dasd_ckd_path_group_entry *) 612962306a36Sopenharmony_ci (info->entry + i * info->entry_size); 613062306a36Sopenharmony_ci /* PGID */ 613162306a36Sopenharmony_ci seq_printf(m, "pgid %*phN\n", 11, entry->pgid); 613262306a36Sopenharmony_ci /* FLAGS */ 613362306a36Sopenharmony_ci seq_printf(m, "status_flags %02x\n", entry->status_flags); 613462306a36Sopenharmony_ci /* SYSPLEX NAME */ 613562306a36Sopenharmony_ci memcpy(&sysplex, &entry->sysplex_name, sizeof(sysplex) - 1); 613662306a36Sopenharmony_ci EBCASC(sysplex, sizeof(sysplex)); 613762306a36Sopenharmony_ci seq_printf(m, "sysplex_name %8s\n", sysplex); 613862306a36Sopenharmony_ci /* SUPPORTED CYLINDER */ 613962306a36Sopenharmony_ci seq_printf(m, "supported_cylinder %d\n", entry->cylinder); 614062306a36Sopenharmony_ci /* TIMESTAMP */ 614162306a36Sopenharmony_ci seq_printf(m, "timestamp %lu\n", (unsigned long) 614262306a36Sopenharmony_ci entry->timestamp); 614362306a36Sopenharmony_ci } 614462306a36Sopenharmony_ci kfree(access); 614562306a36Sopenharmony_ci 614662306a36Sopenharmony_ci return 0; 614762306a36Sopenharmony_ci} 614862306a36Sopenharmony_ci 614962306a36Sopenharmony_cistatic struct dasd_device 615062306a36Sopenharmony_ci*copy_relation_find_device(struct dasd_copy_relation *copy, 615162306a36Sopenharmony_ci char *busid) 615262306a36Sopenharmony_ci{ 615362306a36Sopenharmony_ci int i; 615462306a36Sopenharmony_ci 615562306a36Sopenharmony_ci for (i = 0; i < DASD_CP_ENTRIES; i++) { 615662306a36Sopenharmony_ci if (copy->entry[i].configured && 615762306a36Sopenharmony_ci strncmp(copy->entry[i].busid, busid, DASD_BUS_ID_SIZE) == 0) 615862306a36Sopenharmony_ci return copy->entry[i].device; 615962306a36Sopenharmony_ci } 616062306a36Sopenharmony_ci return NULL; 616162306a36Sopenharmony_ci} 616262306a36Sopenharmony_ci 616362306a36Sopenharmony_ci/* 616462306a36Sopenharmony_ci * set the new active/primary device 616562306a36Sopenharmony_ci */ 616662306a36Sopenharmony_cistatic void copy_pair_set_active(struct dasd_copy_relation *copy, char *new_busid, 616762306a36Sopenharmony_ci char *old_busid) 616862306a36Sopenharmony_ci{ 616962306a36Sopenharmony_ci int i; 617062306a36Sopenharmony_ci 617162306a36Sopenharmony_ci for (i = 0; i < DASD_CP_ENTRIES; i++) { 617262306a36Sopenharmony_ci if (copy->entry[i].configured && 617362306a36Sopenharmony_ci strncmp(copy->entry[i].busid, new_busid, 617462306a36Sopenharmony_ci DASD_BUS_ID_SIZE) == 0) { 617562306a36Sopenharmony_ci copy->active = ©->entry[i]; 617662306a36Sopenharmony_ci copy->entry[i].primary = true; 617762306a36Sopenharmony_ci } else if (copy->entry[i].configured && 617862306a36Sopenharmony_ci strncmp(copy->entry[i].busid, old_busid, 617962306a36Sopenharmony_ci DASD_BUS_ID_SIZE) == 0) { 618062306a36Sopenharmony_ci copy->entry[i].primary = false; 618162306a36Sopenharmony_ci } 618262306a36Sopenharmony_ci } 618362306a36Sopenharmony_ci} 618462306a36Sopenharmony_ci 618562306a36Sopenharmony_ci/* 618662306a36Sopenharmony_ci * The function will swap the role of a given copy pair. 618762306a36Sopenharmony_ci * During the swap operation the relation of the blockdevice is disconnected 618862306a36Sopenharmony_ci * from the old primary and connected to the new. 618962306a36Sopenharmony_ci * 619062306a36Sopenharmony_ci * IO is paused on the block queue before swap and may be resumed afterwards. 619162306a36Sopenharmony_ci */ 619262306a36Sopenharmony_cistatic int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid, 619362306a36Sopenharmony_ci char *sec_busid) 619462306a36Sopenharmony_ci{ 619562306a36Sopenharmony_ci struct dasd_device *primary, *secondary; 619662306a36Sopenharmony_ci struct dasd_copy_relation *copy; 619762306a36Sopenharmony_ci struct dasd_block *block; 619862306a36Sopenharmony_ci struct gendisk *gdp; 619962306a36Sopenharmony_ci 620062306a36Sopenharmony_ci copy = device->copy; 620162306a36Sopenharmony_ci if (!copy) 620262306a36Sopenharmony_ci return DASD_COPYPAIRSWAP_INVALID; 620362306a36Sopenharmony_ci primary = copy->active->device; 620462306a36Sopenharmony_ci if (!primary) 620562306a36Sopenharmony_ci return DASD_COPYPAIRSWAP_INVALID; 620662306a36Sopenharmony_ci /* double check if swap has correct primary */ 620762306a36Sopenharmony_ci if (strncmp(dev_name(&primary->cdev->dev), prim_busid, DASD_BUS_ID_SIZE) != 0) 620862306a36Sopenharmony_ci return DASD_COPYPAIRSWAP_PRIMARY; 620962306a36Sopenharmony_ci 621062306a36Sopenharmony_ci secondary = copy_relation_find_device(copy, sec_busid); 621162306a36Sopenharmony_ci if (!secondary) 621262306a36Sopenharmony_ci return DASD_COPYPAIRSWAP_SECONDARY; 621362306a36Sopenharmony_ci 621462306a36Sopenharmony_ci /* 621562306a36Sopenharmony_ci * usually the device should be quiesced for swap 621662306a36Sopenharmony_ci * for paranoia stop device and requeue requests again 621762306a36Sopenharmony_ci */ 621862306a36Sopenharmony_ci dasd_device_set_stop_bits(primary, DASD_STOPPED_PPRC); 621962306a36Sopenharmony_ci dasd_device_set_stop_bits(secondary, DASD_STOPPED_PPRC); 622062306a36Sopenharmony_ci dasd_generic_requeue_all_requests(primary); 622162306a36Sopenharmony_ci 622262306a36Sopenharmony_ci /* swap DASD internal device <> block assignment */ 622362306a36Sopenharmony_ci block = primary->block; 622462306a36Sopenharmony_ci primary->block = NULL; 622562306a36Sopenharmony_ci secondary->block = block; 622662306a36Sopenharmony_ci block->base = secondary; 622762306a36Sopenharmony_ci /* set new primary device in COPY relation */ 622862306a36Sopenharmony_ci copy_pair_set_active(copy, sec_busid, prim_busid); 622962306a36Sopenharmony_ci 623062306a36Sopenharmony_ci /* swap blocklayer device link */ 623162306a36Sopenharmony_ci gdp = block->gdp; 623262306a36Sopenharmony_ci dasd_add_link_to_gendisk(gdp, secondary); 623362306a36Sopenharmony_ci 623462306a36Sopenharmony_ci /* re-enable device */ 623562306a36Sopenharmony_ci dasd_device_remove_stop_bits(primary, DASD_STOPPED_PPRC); 623662306a36Sopenharmony_ci dasd_device_remove_stop_bits(secondary, DASD_STOPPED_PPRC); 623762306a36Sopenharmony_ci dasd_schedule_device_bh(secondary); 623862306a36Sopenharmony_ci 623962306a36Sopenharmony_ci return DASD_COPYPAIRSWAP_SUCCESS; 624062306a36Sopenharmony_ci} 624162306a36Sopenharmony_ci 624262306a36Sopenharmony_ci/* 624362306a36Sopenharmony_ci * Perform Subsystem Function - Peer-to-Peer Remote Copy Extended Query 624462306a36Sopenharmony_ci */ 624562306a36Sopenharmony_cistatic int dasd_eckd_query_pprc_status(struct dasd_device *device, 624662306a36Sopenharmony_ci struct dasd_pprc_data_sc4 *data) 624762306a36Sopenharmony_ci{ 624862306a36Sopenharmony_ci struct dasd_pprc_data_sc4 *pprc_data; 624962306a36Sopenharmony_ci struct dasd_psf_prssd_data *prssdp; 625062306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 625162306a36Sopenharmony_ci struct ccw1 *ccw; 625262306a36Sopenharmony_ci int rc; 625362306a36Sopenharmony_ci 625462306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, 625562306a36Sopenharmony_ci sizeof(*prssdp) + sizeof(*pprc_data) + 1, 625662306a36Sopenharmony_ci device, NULL); 625762306a36Sopenharmony_ci if (IS_ERR(cqr)) { 625862306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 625962306a36Sopenharmony_ci "Could not allocate query PPRC status request"); 626062306a36Sopenharmony_ci return PTR_ERR(cqr); 626162306a36Sopenharmony_ci } 626262306a36Sopenharmony_ci cqr->startdev = device; 626362306a36Sopenharmony_ci cqr->memdev = device; 626462306a36Sopenharmony_ci cqr->block = NULL; 626562306a36Sopenharmony_ci cqr->retries = 256; 626662306a36Sopenharmony_ci cqr->expires = 10 * HZ; 626762306a36Sopenharmony_ci 626862306a36Sopenharmony_ci /* Prepare for Read Subsystem Data */ 626962306a36Sopenharmony_ci prssdp = (struct dasd_psf_prssd_data *)cqr->data; 627062306a36Sopenharmony_ci memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); 627162306a36Sopenharmony_ci prssdp->order = PSF_ORDER_PRSSD; 627262306a36Sopenharmony_ci prssdp->suborder = PSF_SUBORDER_PPRCEQ; 627362306a36Sopenharmony_ci prssdp->varies[0] = PPRCEQ_SCOPE_4; 627462306a36Sopenharmony_ci pprc_data = (struct dasd_pprc_data_sc4 *)(prssdp + 1); 627562306a36Sopenharmony_ci 627662306a36Sopenharmony_ci ccw = cqr->cpaddr; 627762306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 627862306a36Sopenharmony_ci ccw->count = sizeof(struct dasd_psf_prssd_data); 627962306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_CC; 628062306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 628162306a36Sopenharmony_ci ccw->cda = (__u32)(addr_t)prssdp; 628262306a36Sopenharmony_ci 628362306a36Sopenharmony_ci /* Read Subsystem Data - query host access */ 628462306a36Sopenharmony_ci ccw++; 628562306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_RSSD; 628662306a36Sopenharmony_ci ccw->count = sizeof(*pprc_data); 628762306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 628862306a36Sopenharmony_ci ccw->cda = (__u32)(addr_t)pprc_data; 628962306a36Sopenharmony_ci 629062306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 629162306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 629262306a36Sopenharmony_ci 629362306a36Sopenharmony_ci rc = dasd_sleep_on_interruptible(cqr); 629462306a36Sopenharmony_ci if (rc == 0) { 629562306a36Sopenharmony_ci *data = *pprc_data; 629662306a36Sopenharmony_ci } else { 629762306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 629862306a36Sopenharmony_ci "PPRC Extended Query failed with rc=%d\n", 629962306a36Sopenharmony_ci rc); 630062306a36Sopenharmony_ci rc = -EOPNOTSUPP; 630162306a36Sopenharmony_ci } 630262306a36Sopenharmony_ci 630362306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 630462306a36Sopenharmony_ci return rc; 630562306a36Sopenharmony_ci} 630662306a36Sopenharmony_ci 630762306a36Sopenharmony_ci/* 630862306a36Sopenharmony_ci * ECKD NOP - no operation 630962306a36Sopenharmony_ci */ 631062306a36Sopenharmony_cistatic int dasd_eckd_nop(struct dasd_device *device) 631162306a36Sopenharmony_ci{ 631262306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 631362306a36Sopenharmony_ci struct ccw1 *ccw; 631462306a36Sopenharmony_ci int rc; 631562306a36Sopenharmony_ci 631662306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 1, device, NULL); 631762306a36Sopenharmony_ci if (IS_ERR(cqr)) { 631862306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 631962306a36Sopenharmony_ci "Could not allocate NOP request"); 632062306a36Sopenharmony_ci return PTR_ERR(cqr); 632162306a36Sopenharmony_ci } 632262306a36Sopenharmony_ci cqr->startdev = device; 632362306a36Sopenharmony_ci cqr->memdev = device; 632462306a36Sopenharmony_ci cqr->block = NULL; 632562306a36Sopenharmony_ci cqr->retries = 1; 632662306a36Sopenharmony_ci cqr->expires = 10 * HZ; 632762306a36Sopenharmony_ci 632862306a36Sopenharmony_ci ccw = cqr->cpaddr; 632962306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_NOP; 633062306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 633162306a36Sopenharmony_ci 633262306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 633362306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 633462306a36Sopenharmony_ci 633562306a36Sopenharmony_ci rc = dasd_sleep_on_interruptible(cqr); 633662306a36Sopenharmony_ci if (rc != 0) { 633762306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 633862306a36Sopenharmony_ci "NOP failed with rc=%d\n", rc); 633962306a36Sopenharmony_ci rc = -EOPNOTSUPP; 634062306a36Sopenharmony_ci } 634162306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 634262306a36Sopenharmony_ci return rc; 634362306a36Sopenharmony_ci} 634462306a36Sopenharmony_ci 634562306a36Sopenharmony_cistatic int dasd_eckd_device_ping(struct dasd_device *device) 634662306a36Sopenharmony_ci{ 634762306a36Sopenharmony_ci return dasd_eckd_nop(device); 634862306a36Sopenharmony_ci} 634962306a36Sopenharmony_ci 635062306a36Sopenharmony_ci/* 635162306a36Sopenharmony_ci * Perform Subsystem Function - CUIR response 635262306a36Sopenharmony_ci */ 635362306a36Sopenharmony_cistatic int 635462306a36Sopenharmony_cidasd_eckd_psf_cuir_response(struct dasd_device *device, int response, 635562306a36Sopenharmony_ci __u32 message_id, __u8 lpum) 635662306a36Sopenharmony_ci{ 635762306a36Sopenharmony_ci struct dasd_psf_cuir_response *psf_cuir; 635862306a36Sopenharmony_ci int pos = pathmask_to_pos(lpum); 635962306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 636062306a36Sopenharmony_ci struct ccw1 *ccw; 636162306a36Sopenharmony_ci int rc; 636262306a36Sopenharmony_ci 636362306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ , 636462306a36Sopenharmony_ci sizeof(struct dasd_psf_cuir_response), 636562306a36Sopenharmony_ci device, NULL); 636662306a36Sopenharmony_ci 636762306a36Sopenharmony_ci if (IS_ERR(cqr)) { 636862306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 636962306a36Sopenharmony_ci "Could not allocate PSF-CUIR request"); 637062306a36Sopenharmony_ci return PTR_ERR(cqr); 637162306a36Sopenharmony_ci } 637262306a36Sopenharmony_ci 637362306a36Sopenharmony_ci psf_cuir = (struct dasd_psf_cuir_response *)cqr->data; 637462306a36Sopenharmony_ci psf_cuir->order = PSF_ORDER_CUIR_RESPONSE; 637562306a36Sopenharmony_ci psf_cuir->cc = response; 637662306a36Sopenharmony_ci psf_cuir->chpid = device->path[pos].chpid; 637762306a36Sopenharmony_ci psf_cuir->message_id = message_id; 637862306a36Sopenharmony_ci psf_cuir->cssid = device->path[pos].cssid; 637962306a36Sopenharmony_ci psf_cuir->ssid = device->path[pos].ssid; 638062306a36Sopenharmony_ci ccw = cqr->cpaddr; 638162306a36Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_PSF; 638262306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(psf_cuir); 638362306a36Sopenharmony_ci ccw->flags = CCW_FLAG_SLI; 638462306a36Sopenharmony_ci ccw->count = sizeof(struct dasd_psf_cuir_response); 638562306a36Sopenharmony_ci 638662306a36Sopenharmony_ci cqr->startdev = device; 638762306a36Sopenharmony_ci cqr->memdev = device; 638862306a36Sopenharmony_ci cqr->block = NULL; 638962306a36Sopenharmony_ci cqr->retries = 256; 639062306a36Sopenharmony_ci cqr->expires = 10*HZ; 639162306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 639262306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 639362306a36Sopenharmony_ci set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags); 639462306a36Sopenharmony_ci 639562306a36Sopenharmony_ci rc = dasd_sleep_on(cqr); 639662306a36Sopenharmony_ci 639762306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 639862306a36Sopenharmony_ci return rc; 639962306a36Sopenharmony_ci} 640062306a36Sopenharmony_ci 640162306a36Sopenharmony_ci/* 640262306a36Sopenharmony_ci * return configuration data that is referenced by record selector 640362306a36Sopenharmony_ci * if a record selector is specified or per default return the 640462306a36Sopenharmony_ci * conf_data pointer for the path specified by lpum 640562306a36Sopenharmony_ci */ 640662306a36Sopenharmony_cistatic struct dasd_conf_data *dasd_eckd_get_ref_conf(struct dasd_device *device, 640762306a36Sopenharmony_ci __u8 lpum, 640862306a36Sopenharmony_ci struct dasd_cuir_message *cuir) 640962306a36Sopenharmony_ci{ 641062306a36Sopenharmony_ci struct dasd_conf_data *conf_data; 641162306a36Sopenharmony_ci int path, pos; 641262306a36Sopenharmony_ci 641362306a36Sopenharmony_ci if (cuir->record_selector == 0) 641462306a36Sopenharmony_ci goto out; 641562306a36Sopenharmony_ci for (path = 0x80, pos = 0; path; path >>= 1, pos++) { 641662306a36Sopenharmony_ci conf_data = device->path[pos].conf_data; 641762306a36Sopenharmony_ci if (conf_data->gneq.record_selector == 641862306a36Sopenharmony_ci cuir->record_selector) 641962306a36Sopenharmony_ci return conf_data; 642062306a36Sopenharmony_ci } 642162306a36Sopenharmony_ciout: 642262306a36Sopenharmony_ci return device->path[pathmask_to_pos(lpum)].conf_data; 642362306a36Sopenharmony_ci} 642462306a36Sopenharmony_ci 642562306a36Sopenharmony_ci/* 642662306a36Sopenharmony_ci * This function determines the scope of a reconfiguration request by 642762306a36Sopenharmony_ci * analysing the path and device selection data provided in the CUIR request. 642862306a36Sopenharmony_ci * Returns a path mask containing CUIR affected paths for the give device. 642962306a36Sopenharmony_ci * 643062306a36Sopenharmony_ci * If the CUIR request does not contain the required information return the 643162306a36Sopenharmony_ci * path mask of the path the attention message for the CUIR request was reveived 643262306a36Sopenharmony_ci * on. 643362306a36Sopenharmony_ci */ 643462306a36Sopenharmony_cistatic int dasd_eckd_cuir_scope(struct dasd_device *device, __u8 lpum, 643562306a36Sopenharmony_ci struct dasd_cuir_message *cuir) 643662306a36Sopenharmony_ci{ 643762306a36Sopenharmony_ci struct dasd_conf_data *ref_conf_data; 643862306a36Sopenharmony_ci unsigned long bitmask = 0, mask = 0; 643962306a36Sopenharmony_ci struct dasd_conf_data *conf_data; 644062306a36Sopenharmony_ci unsigned int pos, path; 644162306a36Sopenharmony_ci char *ref_gneq, *gneq; 644262306a36Sopenharmony_ci char *ref_ned, *ned; 644362306a36Sopenharmony_ci int tbcpm = 0; 644462306a36Sopenharmony_ci 644562306a36Sopenharmony_ci /* if CUIR request does not specify the scope use the path 644662306a36Sopenharmony_ci the attention message was presented on */ 644762306a36Sopenharmony_ci if (!cuir->ned_map || 644862306a36Sopenharmony_ci !(cuir->neq_map[0] | cuir->neq_map[1] | cuir->neq_map[2])) 644962306a36Sopenharmony_ci return lpum; 645062306a36Sopenharmony_ci 645162306a36Sopenharmony_ci /* get reference conf data */ 645262306a36Sopenharmony_ci ref_conf_data = dasd_eckd_get_ref_conf(device, lpum, cuir); 645362306a36Sopenharmony_ci /* reference ned is determined by ned_map field */ 645462306a36Sopenharmony_ci pos = 8 - ffs(cuir->ned_map); 645562306a36Sopenharmony_ci ref_ned = (char *)&ref_conf_data->neds[pos]; 645662306a36Sopenharmony_ci ref_gneq = (char *)&ref_conf_data->gneq; 645762306a36Sopenharmony_ci /* transfer 24 bit neq_map to mask */ 645862306a36Sopenharmony_ci mask = cuir->neq_map[2]; 645962306a36Sopenharmony_ci mask |= cuir->neq_map[1] << 8; 646062306a36Sopenharmony_ci mask |= cuir->neq_map[0] << 16; 646162306a36Sopenharmony_ci 646262306a36Sopenharmony_ci for (path = 0; path < 8; path++) { 646362306a36Sopenharmony_ci /* initialise data per path */ 646462306a36Sopenharmony_ci bitmask = mask; 646562306a36Sopenharmony_ci conf_data = device->path[path].conf_data; 646662306a36Sopenharmony_ci pos = 8 - ffs(cuir->ned_map); 646762306a36Sopenharmony_ci ned = (char *) &conf_data->neds[pos]; 646862306a36Sopenharmony_ci /* compare reference ned and per path ned */ 646962306a36Sopenharmony_ci if (memcmp(ref_ned, ned, sizeof(*ned)) != 0) 647062306a36Sopenharmony_ci continue; 647162306a36Sopenharmony_ci gneq = (char *)&conf_data->gneq; 647262306a36Sopenharmony_ci /* compare reference gneq and per_path gneq under 647362306a36Sopenharmony_ci 24 bit mask where mask bit 0 equals byte 7 of 647462306a36Sopenharmony_ci the gneq and mask bit 24 equals byte 31 */ 647562306a36Sopenharmony_ci while (bitmask) { 647662306a36Sopenharmony_ci pos = ffs(bitmask) - 1; 647762306a36Sopenharmony_ci if (memcmp(&ref_gneq[31 - pos], &gneq[31 - pos], 1) 647862306a36Sopenharmony_ci != 0) 647962306a36Sopenharmony_ci break; 648062306a36Sopenharmony_ci clear_bit(pos, &bitmask); 648162306a36Sopenharmony_ci } 648262306a36Sopenharmony_ci if (bitmask) 648362306a36Sopenharmony_ci continue; 648462306a36Sopenharmony_ci /* device and path match the reference values 648562306a36Sopenharmony_ci add path to CUIR scope */ 648662306a36Sopenharmony_ci tbcpm |= 0x80 >> path; 648762306a36Sopenharmony_ci } 648862306a36Sopenharmony_ci return tbcpm; 648962306a36Sopenharmony_ci} 649062306a36Sopenharmony_ci 649162306a36Sopenharmony_cistatic void dasd_eckd_cuir_notify_user(struct dasd_device *device, 649262306a36Sopenharmony_ci unsigned long paths, int action) 649362306a36Sopenharmony_ci{ 649462306a36Sopenharmony_ci int pos; 649562306a36Sopenharmony_ci 649662306a36Sopenharmony_ci while (paths) { 649762306a36Sopenharmony_ci /* get position of bit in mask */ 649862306a36Sopenharmony_ci pos = 8 - ffs(paths); 649962306a36Sopenharmony_ci /* get channel path descriptor from this position */ 650062306a36Sopenharmony_ci if (action == CUIR_QUIESCE) 650162306a36Sopenharmony_ci pr_warn("Service on the storage server caused path %x.%02x to go offline", 650262306a36Sopenharmony_ci device->path[pos].cssid, 650362306a36Sopenharmony_ci device->path[pos].chpid); 650462306a36Sopenharmony_ci else if (action == CUIR_RESUME) 650562306a36Sopenharmony_ci pr_info("Path %x.%02x is back online after service on the storage server", 650662306a36Sopenharmony_ci device->path[pos].cssid, 650762306a36Sopenharmony_ci device->path[pos].chpid); 650862306a36Sopenharmony_ci clear_bit(7 - pos, &paths); 650962306a36Sopenharmony_ci } 651062306a36Sopenharmony_ci} 651162306a36Sopenharmony_ci 651262306a36Sopenharmony_cistatic int dasd_eckd_cuir_remove_path(struct dasd_device *device, __u8 lpum, 651362306a36Sopenharmony_ci struct dasd_cuir_message *cuir) 651462306a36Sopenharmony_ci{ 651562306a36Sopenharmony_ci unsigned long tbcpm; 651662306a36Sopenharmony_ci 651762306a36Sopenharmony_ci tbcpm = dasd_eckd_cuir_scope(device, lpum, cuir); 651862306a36Sopenharmony_ci /* nothing to do if path is not in use */ 651962306a36Sopenharmony_ci if (!(dasd_path_get_opm(device) & tbcpm)) 652062306a36Sopenharmony_ci return 0; 652162306a36Sopenharmony_ci if (!(dasd_path_get_opm(device) & ~tbcpm)) { 652262306a36Sopenharmony_ci /* no path would be left if the CUIR action is taken 652362306a36Sopenharmony_ci return error */ 652462306a36Sopenharmony_ci return -EINVAL; 652562306a36Sopenharmony_ci } 652662306a36Sopenharmony_ci /* remove device from operational path mask */ 652762306a36Sopenharmony_ci dasd_path_remove_opm(device, tbcpm); 652862306a36Sopenharmony_ci dasd_path_add_cuirpm(device, tbcpm); 652962306a36Sopenharmony_ci return tbcpm; 653062306a36Sopenharmony_ci} 653162306a36Sopenharmony_ci 653262306a36Sopenharmony_ci/* 653362306a36Sopenharmony_ci * walk through all devices and build a path mask to quiesce them 653462306a36Sopenharmony_ci * return an error if the last path to a device would be removed 653562306a36Sopenharmony_ci * 653662306a36Sopenharmony_ci * if only part of the devices are quiesced and an error 653762306a36Sopenharmony_ci * occurs no onlining necessary, the storage server will 653862306a36Sopenharmony_ci * notify the already set offline devices again 653962306a36Sopenharmony_ci */ 654062306a36Sopenharmony_cistatic int dasd_eckd_cuir_quiesce(struct dasd_device *device, __u8 lpum, 654162306a36Sopenharmony_ci struct dasd_cuir_message *cuir) 654262306a36Sopenharmony_ci{ 654362306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 654462306a36Sopenharmony_ci struct alias_pav_group *pavgroup, *tempgroup; 654562306a36Sopenharmony_ci struct dasd_device *dev, *n; 654662306a36Sopenharmony_ci unsigned long paths = 0; 654762306a36Sopenharmony_ci unsigned long flags; 654862306a36Sopenharmony_ci int tbcpm; 654962306a36Sopenharmony_ci 655062306a36Sopenharmony_ci /* active devices */ 655162306a36Sopenharmony_ci list_for_each_entry_safe(dev, n, &private->lcu->active_devices, 655262306a36Sopenharmony_ci alias_list) { 655362306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags); 655462306a36Sopenharmony_ci tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir); 655562306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(dev->cdev), flags); 655662306a36Sopenharmony_ci if (tbcpm < 0) 655762306a36Sopenharmony_ci goto out_err; 655862306a36Sopenharmony_ci paths |= tbcpm; 655962306a36Sopenharmony_ci } 656062306a36Sopenharmony_ci /* inactive devices */ 656162306a36Sopenharmony_ci list_for_each_entry_safe(dev, n, &private->lcu->inactive_devices, 656262306a36Sopenharmony_ci alias_list) { 656362306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags); 656462306a36Sopenharmony_ci tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir); 656562306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(dev->cdev), flags); 656662306a36Sopenharmony_ci if (tbcpm < 0) 656762306a36Sopenharmony_ci goto out_err; 656862306a36Sopenharmony_ci paths |= tbcpm; 656962306a36Sopenharmony_ci } 657062306a36Sopenharmony_ci /* devices in PAV groups */ 657162306a36Sopenharmony_ci list_for_each_entry_safe(pavgroup, tempgroup, 657262306a36Sopenharmony_ci &private->lcu->grouplist, group) { 657362306a36Sopenharmony_ci list_for_each_entry_safe(dev, n, &pavgroup->baselist, 657462306a36Sopenharmony_ci alias_list) { 657562306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags); 657662306a36Sopenharmony_ci tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir); 657762306a36Sopenharmony_ci spin_unlock_irqrestore( 657862306a36Sopenharmony_ci get_ccwdev_lock(dev->cdev), flags); 657962306a36Sopenharmony_ci if (tbcpm < 0) 658062306a36Sopenharmony_ci goto out_err; 658162306a36Sopenharmony_ci paths |= tbcpm; 658262306a36Sopenharmony_ci } 658362306a36Sopenharmony_ci list_for_each_entry_safe(dev, n, &pavgroup->aliaslist, 658462306a36Sopenharmony_ci alias_list) { 658562306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags); 658662306a36Sopenharmony_ci tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir); 658762306a36Sopenharmony_ci spin_unlock_irqrestore( 658862306a36Sopenharmony_ci get_ccwdev_lock(dev->cdev), flags); 658962306a36Sopenharmony_ci if (tbcpm < 0) 659062306a36Sopenharmony_ci goto out_err; 659162306a36Sopenharmony_ci paths |= tbcpm; 659262306a36Sopenharmony_ci } 659362306a36Sopenharmony_ci } 659462306a36Sopenharmony_ci /* notify user about all paths affected by CUIR action */ 659562306a36Sopenharmony_ci dasd_eckd_cuir_notify_user(device, paths, CUIR_QUIESCE); 659662306a36Sopenharmony_ci return 0; 659762306a36Sopenharmony_ciout_err: 659862306a36Sopenharmony_ci return tbcpm; 659962306a36Sopenharmony_ci} 660062306a36Sopenharmony_ci 660162306a36Sopenharmony_cistatic int dasd_eckd_cuir_resume(struct dasd_device *device, __u8 lpum, 660262306a36Sopenharmony_ci struct dasd_cuir_message *cuir) 660362306a36Sopenharmony_ci{ 660462306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 660562306a36Sopenharmony_ci struct alias_pav_group *pavgroup, *tempgroup; 660662306a36Sopenharmony_ci struct dasd_device *dev, *n; 660762306a36Sopenharmony_ci unsigned long paths = 0; 660862306a36Sopenharmony_ci int tbcpm; 660962306a36Sopenharmony_ci 661062306a36Sopenharmony_ci /* 661162306a36Sopenharmony_ci * the path may have been added through a generic path event before 661262306a36Sopenharmony_ci * only trigger path verification if the path is not already in use 661362306a36Sopenharmony_ci */ 661462306a36Sopenharmony_ci list_for_each_entry_safe(dev, n, 661562306a36Sopenharmony_ci &private->lcu->active_devices, 661662306a36Sopenharmony_ci alias_list) { 661762306a36Sopenharmony_ci tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir); 661862306a36Sopenharmony_ci paths |= tbcpm; 661962306a36Sopenharmony_ci if (!(dasd_path_get_opm(dev) & tbcpm)) { 662062306a36Sopenharmony_ci dasd_path_add_tbvpm(dev, tbcpm); 662162306a36Sopenharmony_ci dasd_schedule_device_bh(dev); 662262306a36Sopenharmony_ci } 662362306a36Sopenharmony_ci } 662462306a36Sopenharmony_ci list_for_each_entry_safe(dev, n, 662562306a36Sopenharmony_ci &private->lcu->inactive_devices, 662662306a36Sopenharmony_ci alias_list) { 662762306a36Sopenharmony_ci tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir); 662862306a36Sopenharmony_ci paths |= tbcpm; 662962306a36Sopenharmony_ci if (!(dasd_path_get_opm(dev) & tbcpm)) { 663062306a36Sopenharmony_ci dasd_path_add_tbvpm(dev, tbcpm); 663162306a36Sopenharmony_ci dasd_schedule_device_bh(dev); 663262306a36Sopenharmony_ci } 663362306a36Sopenharmony_ci } 663462306a36Sopenharmony_ci /* devices in PAV groups */ 663562306a36Sopenharmony_ci list_for_each_entry_safe(pavgroup, tempgroup, 663662306a36Sopenharmony_ci &private->lcu->grouplist, 663762306a36Sopenharmony_ci group) { 663862306a36Sopenharmony_ci list_for_each_entry_safe(dev, n, 663962306a36Sopenharmony_ci &pavgroup->baselist, 664062306a36Sopenharmony_ci alias_list) { 664162306a36Sopenharmony_ci tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir); 664262306a36Sopenharmony_ci paths |= tbcpm; 664362306a36Sopenharmony_ci if (!(dasd_path_get_opm(dev) & tbcpm)) { 664462306a36Sopenharmony_ci dasd_path_add_tbvpm(dev, tbcpm); 664562306a36Sopenharmony_ci dasd_schedule_device_bh(dev); 664662306a36Sopenharmony_ci } 664762306a36Sopenharmony_ci } 664862306a36Sopenharmony_ci list_for_each_entry_safe(dev, n, 664962306a36Sopenharmony_ci &pavgroup->aliaslist, 665062306a36Sopenharmony_ci alias_list) { 665162306a36Sopenharmony_ci tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir); 665262306a36Sopenharmony_ci paths |= tbcpm; 665362306a36Sopenharmony_ci if (!(dasd_path_get_opm(dev) & tbcpm)) { 665462306a36Sopenharmony_ci dasd_path_add_tbvpm(dev, tbcpm); 665562306a36Sopenharmony_ci dasd_schedule_device_bh(dev); 665662306a36Sopenharmony_ci } 665762306a36Sopenharmony_ci } 665862306a36Sopenharmony_ci } 665962306a36Sopenharmony_ci /* notify user about all paths affected by CUIR action */ 666062306a36Sopenharmony_ci dasd_eckd_cuir_notify_user(device, paths, CUIR_RESUME); 666162306a36Sopenharmony_ci return 0; 666262306a36Sopenharmony_ci} 666362306a36Sopenharmony_ci 666462306a36Sopenharmony_cistatic void dasd_eckd_handle_cuir(struct dasd_device *device, void *messages, 666562306a36Sopenharmony_ci __u8 lpum) 666662306a36Sopenharmony_ci{ 666762306a36Sopenharmony_ci struct dasd_cuir_message *cuir = messages; 666862306a36Sopenharmony_ci int response; 666962306a36Sopenharmony_ci 667062306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, 667162306a36Sopenharmony_ci "CUIR request: %016llx %016llx %016llx %08x", 667262306a36Sopenharmony_ci ((u64 *)cuir)[0], ((u64 *)cuir)[1], ((u64 *)cuir)[2], 667362306a36Sopenharmony_ci ((u32 *)cuir)[3]); 667462306a36Sopenharmony_ci 667562306a36Sopenharmony_ci if (cuir->code == CUIR_QUIESCE) { 667662306a36Sopenharmony_ci /* quiesce */ 667762306a36Sopenharmony_ci if (dasd_eckd_cuir_quiesce(device, lpum, cuir)) 667862306a36Sopenharmony_ci response = PSF_CUIR_LAST_PATH; 667962306a36Sopenharmony_ci else 668062306a36Sopenharmony_ci response = PSF_CUIR_COMPLETED; 668162306a36Sopenharmony_ci } else if (cuir->code == CUIR_RESUME) { 668262306a36Sopenharmony_ci /* resume */ 668362306a36Sopenharmony_ci dasd_eckd_cuir_resume(device, lpum, cuir); 668462306a36Sopenharmony_ci response = PSF_CUIR_COMPLETED; 668562306a36Sopenharmony_ci } else 668662306a36Sopenharmony_ci response = PSF_CUIR_NOT_SUPPORTED; 668762306a36Sopenharmony_ci 668862306a36Sopenharmony_ci dasd_eckd_psf_cuir_response(device, response, 668962306a36Sopenharmony_ci cuir->message_id, lpum); 669062306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, 669162306a36Sopenharmony_ci "CUIR response: %d on message ID %08x", response, 669262306a36Sopenharmony_ci cuir->message_id); 669362306a36Sopenharmony_ci /* to make sure there is no attention left schedule work again */ 669462306a36Sopenharmony_ci device->discipline->check_attention(device, lpum); 669562306a36Sopenharmony_ci} 669662306a36Sopenharmony_ci 669762306a36Sopenharmony_cistatic void dasd_eckd_oos_resume(struct dasd_device *device) 669862306a36Sopenharmony_ci{ 669962306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 670062306a36Sopenharmony_ci struct alias_pav_group *pavgroup, *tempgroup; 670162306a36Sopenharmony_ci struct dasd_device *dev, *n; 670262306a36Sopenharmony_ci unsigned long flags; 670362306a36Sopenharmony_ci 670462306a36Sopenharmony_ci spin_lock_irqsave(&private->lcu->lock, flags); 670562306a36Sopenharmony_ci list_for_each_entry_safe(dev, n, &private->lcu->active_devices, 670662306a36Sopenharmony_ci alias_list) { 670762306a36Sopenharmony_ci if (dev->stopped & DASD_STOPPED_NOSPC) 670862306a36Sopenharmony_ci dasd_generic_space_avail(dev); 670962306a36Sopenharmony_ci } 671062306a36Sopenharmony_ci list_for_each_entry_safe(dev, n, &private->lcu->inactive_devices, 671162306a36Sopenharmony_ci alias_list) { 671262306a36Sopenharmony_ci if (dev->stopped & DASD_STOPPED_NOSPC) 671362306a36Sopenharmony_ci dasd_generic_space_avail(dev); 671462306a36Sopenharmony_ci } 671562306a36Sopenharmony_ci /* devices in PAV groups */ 671662306a36Sopenharmony_ci list_for_each_entry_safe(pavgroup, tempgroup, 671762306a36Sopenharmony_ci &private->lcu->grouplist, 671862306a36Sopenharmony_ci group) { 671962306a36Sopenharmony_ci list_for_each_entry_safe(dev, n, &pavgroup->baselist, 672062306a36Sopenharmony_ci alias_list) { 672162306a36Sopenharmony_ci if (dev->stopped & DASD_STOPPED_NOSPC) 672262306a36Sopenharmony_ci dasd_generic_space_avail(dev); 672362306a36Sopenharmony_ci } 672462306a36Sopenharmony_ci list_for_each_entry_safe(dev, n, &pavgroup->aliaslist, 672562306a36Sopenharmony_ci alias_list) { 672662306a36Sopenharmony_ci if (dev->stopped & DASD_STOPPED_NOSPC) 672762306a36Sopenharmony_ci dasd_generic_space_avail(dev); 672862306a36Sopenharmony_ci } 672962306a36Sopenharmony_ci } 673062306a36Sopenharmony_ci spin_unlock_irqrestore(&private->lcu->lock, flags); 673162306a36Sopenharmony_ci} 673262306a36Sopenharmony_ci 673362306a36Sopenharmony_cistatic void dasd_eckd_handle_oos(struct dasd_device *device, void *messages, 673462306a36Sopenharmony_ci __u8 lpum) 673562306a36Sopenharmony_ci{ 673662306a36Sopenharmony_ci struct dasd_oos_message *oos = messages; 673762306a36Sopenharmony_ci 673862306a36Sopenharmony_ci switch (oos->code) { 673962306a36Sopenharmony_ci case REPO_WARN: 674062306a36Sopenharmony_ci case POOL_WARN: 674162306a36Sopenharmony_ci dev_warn(&device->cdev->dev, 674262306a36Sopenharmony_ci "Extent pool usage has reached a critical value\n"); 674362306a36Sopenharmony_ci dasd_eckd_oos_resume(device); 674462306a36Sopenharmony_ci break; 674562306a36Sopenharmony_ci case REPO_EXHAUST: 674662306a36Sopenharmony_ci case POOL_EXHAUST: 674762306a36Sopenharmony_ci dev_warn(&device->cdev->dev, 674862306a36Sopenharmony_ci "Extent pool is exhausted\n"); 674962306a36Sopenharmony_ci break; 675062306a36Sopenharmony_ci case REPO_RELIEVE: 675162306a36Sopenharmony_ci case POOL_RELIEVE: 675262306a36Sopenharmony_ci dev_info(&device->cdev->dev, 675362306a36Sopenharmony_ci "Extent pool physical space constraint has been relieved\n"); 675462306a36Sopenharmony_ci break; 675562306a36Sopenharmony_ci } 675662306a36Sopenharmony_ci 675762306a36Sopenharmony_ci /* In any case, update related data */ 675862306a36Sopenharmony_ci dasd_eckd_read_ext_pool_info(device); 675962306a36Sopenharmony_ci 676062306a36Sopenharmony_ci /* to make sure there is no attention left schedule work again */ 676162306a36Sopenharmony_ci device->discipline->check_attention(device, lpum); 676262306a36Sopenharmony_ci} 676362306a36Sopenharmony_ci 676462306a36Sopenharmony_cistatic void dasd_eckd_check_attention_work(struct work_struct *work) 676562306a36Sopenharmony_ci{ 676662306a36Sopenharmony_ci struct check_attention_work_data *data; 676762306a36Sopenharmony_ci struct dasd_rssd_messages *messages; 676862306a36Sopenharmony_ci struct dasd_device *device; 676962306a36Sopenharmony_ci int rc; 677062306a36Sopenharmony_ci 677162306a36Sopenharmony_ci data = container_of(work, struct check_attention_work_data, worker); 677262306a36Sopenharmony_ci device = data->device; 677362306a36Sopenharmony_ci messages = kzalloc(sizeof(*messages), GFP_KERNEL); 677462306a36Sopenharmony_ci if (!messages) { 677562306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 677662306a36Sopenharmony_ci "Could not allocate attention message buffer"); 677762306a36Sopenharmony_ci goto out; 677862306a36Sopenharmony_ci } 677962306a36Sopenharmony_ci rc = dasd_eckd_read_message_buffer(device, messages, data->lpum); 678062306a36Sopenharmony_ci if (rc) 678162306a36Sopenharmony_ci goto out; 678262306a36Sopenharmony_ci 678362306a36Sopenharmony_ci if (messages->length == ATTENTION_LENGTH_CUIR && 678462306a36Sopenharmony_ci messages->format == ATTENTION_FORMAT_CUIR) 678562306a36Sopenharmony_ci dasd_eckd_handle_cuir(device, messages, data->lpum); 678662306a36Sopenharmony_ci if (messages->length == ATTENTION_LENGTH_OOS && 678762306a36Sopenharmony_ci messages->format == ATTENTION_FORMAT_OOS) 678862306a36Sopenharmony_ci dasd_eckd_handle_oos(device, messages, data->lpum); 678962306a36Sopenharmony_ci 679062306a36Sopenharmony_ciout: 679162306a36Sopenharmony_ci dasd_put_device(device); 679262306a36Sopenharmony_ci kfree(messages); 679362306a36Sopenharmony_ci kfree(data); 679462306a36Sopenharmony_ci} 679562306a36Sopenharmony_ci 679662306a36Sopenharmony_cistatic int dasd_eckd_check_attention(struct dasd_device *device, __u8 lpum) 679762306a36Sopenharmony_ci{ 679862306a36Sopenharmony_ci struct check_attention_work_data *data; 679962306a36Sopenharmony_ci 680062306a36Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_ATOMIC); 680162306a36Sopenharmony_ci if (!data) 680262306a36Sopenharmony_ci return -ENOMEM; 680362306a36Sopenharmony_ci INIT_WORK(&data->worker, dasd_eckd_check_attention_work); 680462306a36Sopenharmony_ci dasd_get_device(device); 680562306a36Sopenharmony_ci data->device = device; 680662306a36Sopenharmony_ci data->lpum = lpum; 680762306a36Sopenharmony_ci schedule_work(&data->worker); 680862306a36Sopenharmony_ci return 0; 680962306a36Sopenharmony_ci} 681062306a36Sopenharmony_ci 681162306a36Sopenharmony_cistatic int dasd_eckd_disable_hpf_path(struct dasd_device *device, __u8 lpum) 681262306a36Sopenharmony_ci{ 681362306a36Sopenharmony_ci if (~lpum & dasd_path_get_opm(device)) { 681462306a36Sopenharmony_ci dasd_path_add_nohpfpm(device, lpum); 681562306a36Sopenharmony_ci dasd_path_remove_opm(device, lpum); 681662306a36Sopenharmony_ci dev_err(&device->cdev->dev, 681762306a36Sopenharmony_ci "Channel path %02X lost HPF functionality and is disabled\n", 681862306a36Sopenharmony_ci lpum); 681962306a36Sopenharmony_ci return 1; 682062306a36Sopenharmony_ci } 682162306a36Sopenharmony_ci return 0; 682262306a36Sopenharmony_ci} 682362306a36Sopenharmony_ci 682462306a36Sopenharmony_cistatic void dasd_eckd_disable_hpf_device(struct dasd_device *device) 682562306a36Sopenharmony_ci{ 682662306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 682762306a36Sopenharmony_ci 682862306a36Sopenharmony_ci dev_err(&device->cdev->dev, 682962306a36Sopenharmony_ci "High Performance FICON disabled\n"); 683062306a36Sopenharmony_ci private->fcx_max_data = 0; 683162306a36Sopenharmony_ci} 683262306a36Sopenharmony_ci 683362306a36Sopenharmony_cistatic int dasd_eckd_hpf_enabled(struct dasd_device *device) 683462306a36Sopenharmony_ci{ 683562306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 683662306a36Sopenharmony_ci 683762306a36Sopenharmony_ci return private->fcx_max_data ? 1 : 0; 683862306a36Sopenharmony_ci} 683962306a36Sopenharmony_ci 684062306a36Sopenharmony_cistatic void dasd_eckd_handle_hpf_error(struct dasd_device *device, 684162306a36Sopenharmony_ci struct irb *irb) 684262306a36Sopenharmony_ci{ 684362306a36Sopenharmony_ci struct dasd_eckd_private *private = device->private; 684462306a36Sopenharmony_ci 684562306a36Sopenharmony_ci if (!private->fcx_max_data) { 684662306a36Sopenharmony_ci /* sanity check for no HPF, the error makes no sense */ 684762306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 684862306a36Sopenharmony_ci "Trying to disable HPF for a non HPF device"); 684962306a36Sopenharmony_ci return; 685062306a36Sopenharmony_ci } 685162306a36Sopenharmony_ci if (irb->scsw.tm.sesq == SCSW_SESQ_DEV_NOFCX) { 685262306a36Sopenharmony_ci dasd_eckd_disable_hpf_device(device); 685362306a36Sopenharmony_ci } else if (irb->scsw.tm.sesq == SCSW_SESQ_PATH_NOFCX) { 685462306a36Sopenharmony_ci if (dasd_eckd_disable_hpf_path(device, irb->esw.esw1.lpum)) 685562306a36Sopenharmony_ci return; 685662306a36Sopenharmony_ci dasd_eckd_disable_hpf_device(device); 685762306a36Sopenharmony_ci dasd_path_set_tbvpm(device, 685862306a36Sopenharmony_ci dasd_path_get_hpfpm(device)); 685962306a36Sopenharmony_ci } 686062306a36Sopenharmony_ci /* 686162306a36Sopenharmony_ci * prevent that any new I/O ist started on the device and schedule a 686262306a36Sopenharmony_ci * requeue of existing requests 686362306a36Sopenharmony_ci */ 686462306a36Sopenharmony_ci dasd_device_set_stop_bits(device, DASD_STOPPED_NOT_ACC); 686562306a36Sopenharmony_ci dasd_schedule_requeue(device); 686662306a36Sopenharmony_ci} 686762306a36Sopenharmony_ci 686862306a36Sopenharmony_ci/* 686962306a36Sopenharmony_ci * Initialize block layer request queue. 687062306a36Sopenharmony_ci */ 687162306a36Sopenharmony_cistatic void dasd_eckd_setup_blk_queue(struct dasd_block *block) 687262306a36Sopenharmony_ci{ 687362306a36Sopenharmony_ci unsigned int logical_block_size = block->bp_block; 687462306a36Sopenharmony_ci struct request_queue *q = block->gdp->queue; 687562306a36Sopenharmony_ci struct dasd_device *device = block->base; 687662306a36Sopenharmony_ci int max; 687762306a36Sopenharmony_ci 687862306a36Sopenharmony_ci if (device->features & DASD_FEATURE_USERAW) { 687962306a36Sopenharmony_ci /* 688062306a36Sopenharmony_ci * the max_blocks value for raw_track access is 256 688162306a36Sopenharmony_ci * it is higher than the native ECKD value because we 688262306a36Sopenharmony_ci * only need one ccw per track 688362306a36Sopenharmony_ci * so the max_hw_sectors are 688462306a36Sopenharmony_ci * 2048 x 512B = 1024kB = 16 tracks 688562306a36Sopenharmony_ci */ 688662306a36Sopenharmony_ci max = DASD_ECKD_MAX_BLOCKS_RAW << block->s2b_shift; 688762306a36Sopenharmony_ci } else { 688862306a36Sopenharmony_ci max = DASD_ECKD_MAX_BLOCKS << block->s2b_shift; 688962306a36Sopenharmony_ci } 689062306a36Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_NONROT, q); 689162306a36Sopenharmony_ci q->limits.max_dev_sectors = max; 689262306a36Sopenharmony_ci blk_queue_logical_block_size(q, logical_block_size); 689362306a36Sopenharmony_ci blk_queue_max_hw_sectors(q, max); 689462306a36Sopenharmony_ci blk_queue_max_segments(q, USHRT_MAX); 689562306a36Sopenharmony_ci /* With page sized segments each segment can be translated into one idaw/tidaw */ 689662306a36Sopenharmony_ci blk_queue_max_segment_size(q, PAGE_SIZE); 689762306a36Sopenharmony_ci blk_queue_segment_boundary(q, PAGE_SIZE - 1); 689862306a36Sopenharmony_ci blk_queue_dma_alignment(q, PAGE_SIZE - 1); 689962306a36Sopenharmony_ci} 690062306a36Sopenharmony_ci 690162306a36Sopenharmony_cistatic struct ccw_driver dasd_eckd_driver = { 690262306a36Sopenharmony_ci .driver = { 690362306a36Sopenharmony_ci .name = "dasd-eckd", 690462306a36Sopenharmony_ci .owner = THIS_MODULE, 690562306a36Sopenharmony_ci .dev_groups = dasd_dev_groups, 690662306a36Sopenharmony_ci }, 690762306a36Sopenharmony_ci .ids = dasd_eckd_ids, 690862306a36Sopenharmony_ci .probe = dasd_eckd_probe, 690962306a36Sopenharmony_ci .remove = dasd_generic_remove, 691062306a36Sopenharmony_ci .set_offline = dasd_generic_set_offline, 691162306a36Sopenharmony_ci .set_online = dasd_eckd_set_online, 691262306a36Sopenharmony_ci .notify = dasd_generic_notify, 691362306a36Sopenharmony_ci .path_event = dasd_generic_path_event, 691462306a36Sopenharmony_ci .shutdown = dasd_generic_shutdown, 691562306a36Sopenharmony_ci .uc_handler = dasd_generic_uc_handler, 691662306a36Sopenharmony_ci .int_class = IRQIO_DAS, 691762306a36Sopenharmony_ci}; 691862306a36Sopenharmony_ci 691962306a36Sopenharmony_cistatic struct dasd_discipline dasd_eckd_discipline = { 692062306a36Sopenharmony_ci .owner = THIS_MODULE, 692162306a36Sopenharmony_ci .name = "ECKD", 692262306a36Sopenharmony_ci .ebcname = "ECKD", 692362306a36Sopenharmony_ci .check_device = dasd_eckd_check_characteristics, 692462306a36Sopenharmony_ci .uncheck_device = dasd_eckd_uncheck_device, 692562306a36Sopenharmony_ci .do_analysis = dasd_eckd_do_analysis, 692662306a36Sopenharmony_ci .pe_handler = dasd_eckd_pe_handler, 692762306a36Sopenharmony_ci .basic_to_ready = dasd_eckd_basic_to_ready, 692862306a36Sopenharmony_ci .online_to_ready = dasd_eckd_online_to_ready, 692962306a36Sopenharmony_ci .basic_to_known = dasd_eckd_basic_to_known, 693062306a36Sopenharmony_ci .setup_blk_queue = dasd_eckd_setup_blk_queue, 693162306a36Sopenharmony_ci .fill_geometry = dasd_eckd_fill_geometry, 693262306a36Sopenharmony_ci .start_IO = dasd_start_IO, 693362306a36Sopenharmony_ci .term_IO = dasd_term_IO, 693462306a36Sopenharmony_ci .handle_terminated_request = dasd_eckd_handle_terminated_request, 693562306a36Sopenharmony_ci .format_device = dasd_eckd_format_device, 693662306a36Sopenharmony_ci .check_device_format = dasd_eckd_check_device_format, 693762306a36Sopenharmony_ci .erp_action = dasd_eckd_erp_action, 693862306a36Sopenharmony_ci .erp_postaction = dasd_eckd_erp_postaction, 693962306a36Sopenharmony_ci .check_for_device_change = dasd_eckd_check_for_device_change, 694062306a36Sopenharmony_ci .build_cp = dasd_eckd_build_alias_cp, 694162306a36Sopenharmony_ci .free_cp = dasd_eckd_free_alias_cp, 694262306a36Sopenharmony_ci .dump_sense = dasd_eckd_dump_sense, 694362306a36Sopenharmony_ci .dump_sense_dbf = dasd_eckd_dump_sense_dbf, 694462306a36Sopenharmony_ci .fill_info = dasd_eckd_fill_info, 694562306a36Sopenharmony_ci .ioctl = dasd_eckd_ioctl, 694662306a36Sopenharmony_ci .reload = dasd_eckd_reload_device, 694762306a36Sopenharmony_ci .get_uid = dasd_eckd_get_uid, 694862306a36Sopenharmony_ci .kick_validate = dasd_eckd_kick_validate_server, 694962306a36Sopenharmony_ci .check_attention = dasd_eckd_check_attention, 695062306a36Sopenharmony_ci .host_access_count = dasd_eckd_host_access_count, 695162306a36Sopenharmony_ci .hosts_print = dasd_hosts_print, 695262306a36Sopenharmony_ci .handle_hpf_error = dasd_eckd_handle_hpf_error, 695362306a36Sopenharmony_ci .disable_hpf = dasd_eckd_disable_hpf_device, 695462306a36Sopenharmony_ci .hpf_enabled = dasd_eckd_hpf_enabled, 695562306a36Sopenharmony_ci .reset_path = dasd_eckd_reset_path, 695662306a36Sopenharmony_ci .is_ese = dasd_eckd_is_ese, 695762306a36Sopenharmony_ci .space_allocated = dasd_eckd_space_allocated, 695862306a36Sopenharmony_ci .space_configured = dasd_eckd_space_configured, 695962306a36Sopenharmony_ci .logical_capacity = dasd_eckd_logical_capacity, 696062306a36Sopenharmony_ci .release_space = dasd_eckd_release_space, 696162306a36Sopenharmony_ci .ext_pool_id = dasd_eckd_ext_pool_id, 696262306a36Sopenharmony_ci .ext_size = dasd_eckd_ext_size, 696362306a36Sopenharmony_ci .ext_pool_cap_at_warnlevel = dasd_eckd_ext_pool_cap_at_warnlevel, 696462306a36Sopenharmony_ci .ext_pool_warn_thrshld = dasd_eckd_ext_pool_warn_thrshld, 696562306a36Sopenharmony_ci .ext_pool_oos = dasd_eckd_ext_pool_oos, 696662306a36Sopenharmony_ci .ext_pool_exhaust = dasd_eckd_ext_pool_exhaust, 696762306a36Sopenharmony_ci .ese_format = dasd_eckd_ese_format, 696862306a36Sopenharmony_ci .ese_read = dasd_eckd_ese_read, 696962306a36Sopenharmony_ci .pprc_status = dasd_eckd_query_pprc_status, 697062306a36Sopenharmony_ci .pprc_enabled = dasd_eckd_pprc_enabled, 697162306a36Sopenharmony_ci .copy_pair_swap = dasd_eckd_copy_pair_swap, 697262306a36Sopenharmony_ci .device_ping = dasd_eckd_device_ping, 697362306a36Sopenharmony_ci}; 697462306a36Sopenharmony_ci 697562306a36Sopenharmony_cistatic int __init 697662306a36Sopenharmony_cidasd_eckd_init(void) 697762306a36Sopenharmony_ci{ 697862306a36Sopenharmony_ci int ret; 697962306a36Sopenharmony_ci 698062306a36Sopenharmony_ci ASCEBC(dasd_eckd_discipline.ebcname, 4); 698162306a36Sopenharmony_ci dasd_reserve_req = kmalloc(sizeof(*dasd_reserve_req), 698262306a36Sopenharmony_ci GFP_KERNEL | GFP_DMA); 698362306a36Sopenharmony_ci if (!dasd_reserve_req) 698462306a36Sopenharmony_ci return -ENOMEM; 698562306a36Sopenharmony_ci dasd_vol_info_req = kmalloc(sizeof(*dasd_vol_info_req), 698662306a36Sopenharmony_ci GFP_KERNEL | GFP_DMA); 698762306a36Sopenharmony_ci if (!dasd_vol_info_req) { 698862306a36Sopenharmony_ci kfree(dasd_reserve_req); 698962306a36Sopenharmony_ci return -ENOMEM; 699062306a36Sopenharmony_ci } 699162306a36Sopenharmony_ci pe_handler_worker = kmalloc(sizeof(*pe_handler_worker), 699262306a36Sopenharmony_ci GFP_KERNEL | GFP_DMA); 699362306a36Sopenharmony_ci if (!pe_handler_worker) { 699462306a36Sopenharmony_ci kfree(dasd_reserve_req); 699562306a36Sopenharmony_ci kfree(dasd_vol_info_req); 699662306a36Sopenharmony_ci return -ENOMEM; 699762306a36Sopenharmony_ci } 699862306a36Sopenharmony_ci rawpadpage = (void *)__get_free_page(GFP_KERNEL); 699962306a36Sopenharmony_ci if (!rawpadpage) { 700062306a36Sopenharmony_ci kfree(pe_handler_worker); 700162306a36Sopenharmony_ci kfree(dasd_reserve_req); 700262306a36Sopenharmony_ci kfree(dasd_vol_info_req); 700362306a36Sopenharmony_ci return -ENOMEM; 700462306a36Sopenharmony_ci } 700562306a36Sopenharmony_ci ret = ccw_driver_register(&dasd_eckd_driver); 700662306a36Sopenharmony_ci if (!ret) 700762306a36Sopenharmony_ci wait_for_device_probe(); 700862306a36Sopenharmony_ci else { 700962306a36Sopenharmony_ci kfree(pe_handler_worker); 701062306a36Sopenharmony_ci kfree(dasd_reserve_req); 701162306a36Sopenharmony_ci kfree(dasd_vol_info_req); 701262306a36Sopenharmony_ci free_page((unsigned long)rawpadpage); 701362306a36Sopenharmony_ci } 701462306a36Sopenharmony_ci return ret; 701562306a36Sopenharmony_ci} 701662306a36Sopenharmony_ci 701762306a36Sopenharmony_cistatic void __exit 701862306a36Sopenharmony_cidasd_eckd_cleanup(void) 701962306a36Sopenharmony_ci{ 702062306a36Sopenharmony_ci ccw_driver_unregister(&dasd_eckd_driver); 702162306a36Sopenharmony_ci kfree(pe_handler_worker); 702262306a36Sopenharmony_ci kfree(dasd_reserve_req); 702362306a36Sopenharmony_ci free_page((unsigned long)rawpadpage); 702462306a36Sopenharmony_ci} 702562306a36Sopenharmony_ci 702662306a36Sopenharmony_cimodule_init(dasd_eckd_init); 702762306a36Sopenharmony_cimodule_exit(dasd_eckd_cleanup); 7028