162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> 462306a36Sopenharmony_ci * Bugreports.to..: <Linux390@de.ibm.com> 562306a36Sopenharmony_ci * Copyright IBM Corp. 1999, 2009 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define KMSG_COMPONENT "dasd-fba" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/stddef.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <asm/debug.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/hdreg.h> /* HDIO_GETGEO */ 1662306a36Sopenharmony_ci#include <linux/bio.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/init.h> 1962306a36Sopenharmony_ci#include <linux/io.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <asm/idals.h> 2262306a36Sopenharmony_ci#include <asm/ebcdic.h> 2362306a36Sopenharmony_ci#include <asm/ccwdev.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "dasd_int.h" 2662306a36Sopenharmony_ci#include "dasd_fba.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#ifdef PRINTK_HEADER 2962306a36Sopenharmony_ci#undef PRINTK_HEADER 3062306a36Sopenharmony_ci#endif /* PRINTK_HEADER */ 3162306a36Sopenharmony_ci#define PRINTK_HEADER "dasd(fba):" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define FBA_DEFAULT_RETRIES 32 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define DASD_FBA_CCW_WRITE 0x41 3662306a36Sopenharmony_ci#define DASD_FBA_CCW_READ 0x42 3762306a36Sopenharmony_ci#define DASD_FBA_CCW_LOCATE 0x43 3862306a36Sopenharmony_ci#define DASD_FBA_CCW_DEFINE_EXTENT 0x63 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic struct dasd_discipline dasd_fba_discipline; 4362306a36Sopenharmony_cistatic void *dasd_fba_zero_page; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct dasd_fba_private { 4662306a36Sopenharmony_ci struct dasd_fba_characteristics rdc_data; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic struct ccw_device_id dasd_fba_ids[] = { 5062306a36Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x6310, 0, 0x9336, 0), .driver_info = 0x1}, 5162306a36Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3370, 0), .driver_info = 0x2}, 5262306a36Sopenharmony_ci { /* end of list */ }, 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(ccw, dasd_fba_ids); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int 5862306a36Sopenharmony_cidasd_fba_set_online(struct ccw_device *cdev) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci return dasd_generic_set_online(cdev, &dasd_fba_discipline); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic struct ccw_driver dasd_fba_driver = { 6462306a36Sopenharmony_ci .driver = { 6562306a36Sopenharmony_ci .name = "dasd-fba", 6662306a36Sopenharmony_ci .owner = THIS_MODULE, 6762306a36Sopenharmony_ci .dev_groups = dasd_dev_groups, 6862306a36Sopenharmony_ci }, 6962306a36Sopenharmony_ci .ids = dasd_fba_ids, 7062306a36Sopenharmony_ci .probe = dasd_generic_probe, 7162306a36Sopenharmony_ci .remove = dasd_generic_remove, 7262306a36Sopenharmony_ci .set_offline = dasd_generic_set_offline, 7362306a36Sopenharmony_ci .set_online = dasd_fba_set_online, 7462306a36Sopenharmony_ci .notify = dasd_generic_notify, 7562306a36Sopenharmony_ci .path_event = dasd_generic_path_event, 7662306a36Sopenharmony_ci .int_class = IRQIO_DAS, 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void 8062306a36Sopenharmony_cidefine_extent(struct ccw1 * ccw, struct DE_fba_data *data, int rw, 8162306a36Sopenharmony_ci int blksize, int beg, int nr) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci ccw->cmd_code = DASD_FBA_CCW_DEFINE_EXTENT; 8462306a36Sopenharmony_ci ccw->flags = 0; 8562306a36Sopenharmony_ci ccw->count = 16; 8662306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(data); 8762306a36Sopenharmony_ci memset(data, 0, sizeof (struct DE_fba_data)); 8862306a36Sopenharmony_ci if (rw == WRITE) 8962306a36Sopenharmony_ci (data->mask).perm = 0x0; 9062306a36Sopenharmony_ci else if (rw == READ) 9162306a36Sopenharmony_ci (data->mask).perm = 0x1; 9262306a36Sopenharmony_ci else 9362306a36Sopenharmony_ci data->mask.perm = 0x2; 9462306a36Sopenharmony_ci data->blk_size = blksize; 9562306a36Sopenharmony_ci data->ext_loc = beg; 9662306a36Sopenharmony_ci data->ext_end = nr - 1; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic void 10062306a36Sopenharmony_cilocate_record(struct ccw1 * ccw, struct LO_fba_data *data, int rw, 10162306a36Sopenharmony_ci int block_nr, int block_ct) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci ccw->cmd_code = DASD_FBA_CCW_LOCATE; 10462306a36Sopenharmony_ci ccw->flags = 0; 10562306a36Sopenharmony_ci ccw->count = 8; 10662306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(data); 10762306a36Sopenharmony_ci memset(data, 0, sizeof (struct LO_fba_data)); 10862306a36Sopenharmony_ci if (rw == WRITE) 10962306a36Sopenharmony_ci data->operation.cmd = 0x5; 11062306a36Sopenharmony_ci else if (rw == READ) 11162306a36Sopenharmony_ci data->operation.cmd = 0x6; 11262306a36Sopenharmony_ci else 11362306a36Sopenharmony_ci data->operation.cmd = 0x8; 11462306a36Sopenharmony_ci data->blk_nr = block_nr; 11562306a36Sopenharmony_ci data->blk_ct = block_ct; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int 11962306a36Sopenharmony_cidasd_fba_check_characteristics(struct dasd_device *device) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct dasd_fba_private *private = device->private; 12262306a36Sopenharmony_ci struct ccw_device *cdev = device->cdev; 12362306a36Sopenharmony_ci struct dasd_block *block; 12462306a36Sopenharmony_ci int readonly, rc; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (!private) { 12762306a36Sopenharmony_ci private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA); 12862306a36Sopenharmony_ci if (!private) { 12962306a36Sopenharmony_ci dev_warn(&device->cdev->dev, 13062306a36Sopenharmony_ci "Allocating memory for private DASD " 13162306a36Sopenharmony_ci "data failed\n"); 13262306a36Sopenharmony_ci return -ENOMEM; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci device->private = private; 13562306a36Sopenharmony_ci } else { 13662306a36Sopenharmony_ci memset(private, 0, sizeof(*private)); 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci block = dasd_alloc_block(); 13962306a36Sopenharmony_ci if (IS_ERR(block)) { 14062306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", "could not allocate " 14162306a36Sopenharmony_ci "dasd block structure"); 14262306a36Sopenharmony_ci device->private = NULL; 14362306a36Sopenharmony_ci kfree(private); 14462306a36Sopenharmony_ci return PTR_ERR(block); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci device->block = block; 14762306a36Sopenharmony_ci block->base = device; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* Read Device Characteristics */ 15062306a36Sopenharmony_ci rc = dasd_generic_read_dev_chars(device, DASD_FBA_MAGIC, 15162306a36Sopenharmony_ci &private->rdc_data, 32); 15262306a36Sopenharmony_ci if (rc) { 15362306a36Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, cdev, "Read device " 15462306a36Sopenharmony_ci "characteristics returned error %d", rc); 15562306a36Sopenharmony_ci device->block = NULL; 15662306a36Sopenharmony_ci dasd_free_block(block); 15762306a36Sopenharmony_ci device->private = NULL; 15862306a36Sopenharmony_ci kfree(private); 15962306a36Sopenharmony_ci return rc; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci device->default_expires = DASD_EXPIRES; 16362306a36Sopenharmony_ci device->default_retries = FBA_DEFAULT_RETRIES; 16462306a36Sopenharmony_ci dasd_path_set_opm(device, LPM_ANYPATH); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci readonly = dasd_device_is_ro(device); 16762306a36Sopenharmony_ci if (readonly) 16862306a36Sopenharmony_ci set_bit(DASD_FLAG_DEVICE_RO, &device->flags); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* FBA supports discard, set the according feature bit */ 17162306a36Sopenharmony_ci dasd_set_feature(cdev, DASD_FEATURE_DISCARD, 1); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci dev_info(&device->cdev->dev, 17462306a36Sopenharmony_ci "New FBA DASD %04X/%02X (CU %04X/%02X) with %d MB " 17562306a36Sopenharmony_ci "and %d B/blk%s\n", 17662306a36Sopenharmony_ci cdev->id.dev_type, 17762306a36Sopenharmony_ci cdev->id.dev_model, 17862306a36Sopenharmony_ci cdev->id.cu_type, 17962306a36Sopenharmony_ci cdev->id.cu_model, 18062306a36Sopenharmony_ci ((private->rdc_data.blk_bdsa * 18162306a36Sopenharmony_ci (private->rdc_data.blk_size >> 9)) >> 11), 18262306a36Sopenharmony_ci private->rdc_data.blk_size, 18362306a36Sopenharmony_ci readonly ? ", read-only device" : ""); 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int dasd_fba_do_analysis(struct dasd_block *block) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct dasd_fba_private *private = block->base->private; 19062306a36Sopenharmony_ci int sb, rc; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci rc = dasd_check_blocksize(private->rdc_data.blk_size); 19362306a36Sopenharmony_ci if (rc) { 19462306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, block->base, "unknown blocksize %d", 19562306a36Sopenharmony_ci private->rdc_data.blk_size); 19662306a36Sopenharmony_ci return rc; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci block->blocks = private->rdc_data.blk_bdsa; 19962306a36Sopenharmony_ci block->bp_block = private->rdc_data.blk_size; 20062306a36Sopenharmony_ci block->s2b_shift = 0; /* bits to shift 512 to get a block */ 20162306a36Sopenharmony_ci for (sb = 512; sb < private->rdc_data.blk_size; sb = sb << 1) 20262306a36Sopenharmony_ci block->s2b_shift++; 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int dasd_fba_fill_geometry(struct dasd_block *block, 20762306a36Sopenharmony_ci struct hd_geometry *geo) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci if (dasd_check_blocksize(block->bp_block) != 0) 21062306a36Sopenharmony_ci return -EINVAL; 21162306a36Sopenharmony_ci geo->cylinders = (block->blocks << block->s2b_shift) >> 10; 21262306a36Sopenharmony_ci geo->heads = 16; 21362306a36Sopenharmony_ci geo->sectors = 128 >> block->s2b_shift; 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic dasd_erp_fn_t 21862306a36Sopenharmony_cidasd_fba_erp_action(struct dasd_ccw_req * cqr) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci return dasd_default_erp_action; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic dasd_erp_fn_t 22462306a36Sopenharmony_cidasd_fba_erp_postaction(struct dasd_ccw_req * cqr) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci if (cqr->function == dasd_default_erp_action) 22762306a36Sopenharmony_ci return dasd_default_erp_postaction; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, cqr->startdev, "unknown ERP action %p", 23062306a36Sopenharmony_ci cqr->function); 23162306a36Sopenharmony_ci return NULL; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic void dasd_fba_check_for_device_change(struct dasd_device *device, 23562306a36Sopenharmony_ci struct dasd_ccw_req *cqr, 23662306a36Sopenharmony_ci struct irb *irb) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci char mask; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* first of all check for state change pending interrupt */ 24162306a36Sopenharmony_ci mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; 24262306a36Sopenharmony_ci if ((irb->scsw.cmd.dstat & mask) == mask) 24362306a36Sopenharmony_ci dasd_generic_handle_state_change(device); 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* 24862306a36Sopenharmony_ci * Builds a CCW with no data payload 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_cistatic void ccw_write_no_data(struct ccw1 *ccw) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci ccw->cmd_code = DASD_FBA_CCW_WRITE; 25362306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 25462306a36Sopenharmony_ci ccw->count = 0; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/* 25862306a36Sopenharmony_ci * Builds a CCW that writes only zeroes. 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_cistatic void ccw_write_zero(struct ccw1 *ccw, int count) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci ccw->cmd_code = DASD_FBA_CCW_WRITE; 26362306a36Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 26462306a36Sopenharmony_ci ccw->count = count; 26562306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(dasd_fba_zero_page); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* 26962306a36Sopenharmony_ci * Helper function to count the amount of necessary CCWs within a given range 27062306a36Sopenharmony_ci * with 4k alignment and command chaining in mind. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_cistatic int count_ccws(sector_t first_rec, sector_t last_rec, 27362306a36Sopenharmony_ci unsigned int blocks_per_page) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci sector_t wz_stop = 0, d_stop = 0; 27662306a36Sopenharmony_ci int cur_pos = 0; 27762306a36Sopenharmony_ci int count = 0; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (first_rec % blocks_per_page != 0) { 28062306a36Sopenharmony_ci wz_stop = first_rec + blocks_per_page - 28162306a36Sopenharmony_ci (first_rec % blocks_per_page) - 1; 28262306a36Sopenharmony_ci if (wz_stop > last_rec) 28362306a36Sopenharmony_ci wz_stop = last_rec; 28462306a36Sopenharmony_ci cur_pos = wz_stop - first_rec + 1; 28562306a36Sopenharmony_ci count++; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (last_rec - (first_rec + cur_pos) + 1 >= blocks_per_page) { 28962306a36Sopenharmony_ci if ((last_rec - blocks_per_page + 1) % blocks_per_page != 0) 29062306a36Sopenharmony_ci d_stop = last_rec - ((last_rec - blocks_per_page + 1) % 29162306a36Sopenharmony_ci blocks_per_page); 29262306a36Sopenharmony_ci else 29362306a36Sopenharmony_ci d_stop = last_rec; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci cur_pos += d_stop - (first_rec + cur_pos) + 1; 29662306a36Sopenharmony_ci count++; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (cur_pos == 0 || first_rec + cur_pos - 1 < last_rec) 30062306a36Sopenharmony_ci count++; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return count; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci/* 30662306a36Sopenharmony_ci * This function builds a CCW request for block layer discard requests. 30762306a36Sopenharmony_ci * Each page in the z/VM hypervisor that represents certain records of an FBA 30862306a36Sopenharmony_ci * device will be padded with zeros. This is a special behaviour of the WRITE 30962306a36Sopenharmony_ci * command which is triggered when no data payload is added to the CCW. 31062306a36Sopenharmony_ci * 31162306a36Sopenharmony_ci * Note: Due to issues in some z/VM versions, we can't fully utilise this 31262306a36Sopenharmony_ci * special behaviour. We have to keep a 4k (or 8 block) alignment in mind to 31362306a36Sopenharmony_ci * work around those issues and write actual zeroes to the unaligned parts in 31462306a36Sopenharmony_ci * the request. This workaround might be removed in the future. 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_fba_build_cp_discard( 31762306a36Sopenharmony_ci struct dasd_device *memdev, 31862306a36Sopenharmony_ci struct dasd_block *block, 31962306a36Sopenharmony_ci struct request *req) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct LO_fba_data *LO_data; 32262306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 32362306a36Sopenharmony_ci struct ccw1 *ccw; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci sector_t wz_stop = 0, d_stop = 0; 32662306a36Sopenharmony_ci sector_t first_rec, last_rec; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci unsigned int blksize = block->bp_block; 32962306a36Sopenharmony_ci unsigned int blocks_per_page; 33062306a36Sopenharmony_ci int wz_count = 0; 33162306a36Sopenharmony_ci int d_count = 0; 33262306a36Sopenharmony_ci int cur_pos = 0; /* Current position within the extent */ 33362306a36Sopenharmony_ci int count = 0; 33462306a36Sopenharmony_ci int cplength; 33562306a36Sopenharmony_ci int datasize; 33662306a36Sopenharmony_ci int nr_ccws; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci first_rec = blk_rq_pos(req) >> block->s2b_shift; 33962306a36Sopenharmony_ci last_rec = 34062306a36Sopenharmony_ci (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift; 34162306a36Sopenharmony_ci count = last_rec - first_rec + 1; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci blocks_per_page = BLOCKS_PER_PAGE(blksize); 34462306a36Sopenharmony_ci nr_ccws = count_ccws(first_rec, last_rec, blocks_per_page); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* define extent + nr_ccws * locate record + nr_ccws * single CCW */ 34762306a36Sopenharmony_ci cplength = 1 + 2 * nr_ccws; 34862306a36Sopenharmony_ci datasize = sizeof(struct DE_fba_data) + 34962306a36Sopenharmony_ci nr_ccws * (sizeof(struct LO_fba_data) + sizeof(struct ccw1)); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev, 35262306a36Sopenharmony_ci blk_mq_rq_to_pdu(req)); 35362306a36Sopenharmony_ci if (IS_ERR(cqr)) 35462306a36Sopenharmony_ci return cqr; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci ccw = cqr->cpaddr; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci define_extent(ccw++, cqr->data, WRITE, blksize, first_rec, count); 35962306a36Sopenharmony_ci LO_data = cqr->data + sizeof(struct DE_fba_data); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* First part is not aligned. Calculate range to write zeroes. */ 36262306a36Sopenharmony_ci if (first_rec % blocks_per_page != 0) { 36362306a36Sopenharmony_ci wz_stop = first_rec + blocks_per_page - 36462306a36Sopenharmony_ci (first_rec % blocks_per_page) - 1; 36562306a36Sopenharmony_ci if (wz_stop > last_rec) 36662306a36Sopenharmony_ci wz_stop = last_rec; 36762306a36Sopenharmony_ci wz_count = wz_stop - first_rec + 1; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 37062306a36Sopenharmony_ci locate_record(ccw++, LO_data++, WRITE, cur_pos, wz_count); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 37362306a36Sopenharmony_ci ccw_write_zero(ccw++, wz_count * blksize); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci cur_pos = wz_count; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* We can do proper discard when we've got at least blocks_per_page blocks. */ 37962306a36Sopenharmony_ci if (last_rec - (first_rec + cur_pos) + 1 >= blocks_per_page) { 38062306a36Sopenharmony_ci /* is last record at page boundary? */ 38162306a36Sopenharmony_ci if ((last_rec - blocks_per_page + 1) % blocks_per_page != 0) 38262306a36Sopenharmony_ci d_stop = last_rec - ((last_rec - blocks_per_page + 1) % 38362306a36Sopenharmony_ci blocks_per_page); 38462306a36Sopenharmony_ci else 38562306a36Sopenharmony_ci d_stop = last_rec; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci d_count = d_stop - (first_rec + cur_pos) + 1; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 39062306a36Sopenharmony_ci locate_record(ccw++, LO_data++, WRITE, cur_pos, d_count); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 39362306a36Sopenharmony_ci ccw_write_no_data(ccw++); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci cur_pos += d_count; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* We might still have some bits left which need to be zeroed. */ 39962306a36Sopenharmony_ci if (cur_pos == 0 || first_rec + cur_pos - 1 < last_rec) { 40062306a36Sopenharmony_ci if (d_stop != 0) 40162306a36Sopenharmony_ci wz_count = last_rec - d_stop; 40262306a36Sopenharmony_ci else if (wz_stop != 0) 40362306a36Sopenharmony_ci wz_count = last_rec - wz_stop; 40462306a36Sopenharmony_ci else 40562306a36Sopenharmony_ci wz_count = count; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 40862306a36Sopenharmony_ci locate_record(ccw++, LO_data++, WRITE, cur_pos, wz_count); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 41162306a36Sopenharmony_ci ccw_write_zero(ccw++, wz_count * blksize); 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (blk_noretry_request(req) || 41562306a36Sopenharmony_ci block->base->features & DASD_FEATURE_FAILFAST) 41662306a36Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci cqr->startdev = memdev; 41962306a36Sopenharmony_ci cqr->memdev = memdev; 42062306a36Sopenharmony_ci cqr->block = block; 42162306a36Sopenharmony_ci cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */ 42262306a36Sopenharmony_ci cqr->retries = memdev->default_retries; 42362306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 42462306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return cqr; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_fba_build_cp_regular( 43062306a36Sopenharmony_ci struct dasd_device *memdev, 43162306a36Sopenharmony_ci struct dasd_block *block, 43262306a36Sopenharmony_ci struct request *req) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct dasd_fba_private *private = block->base->private; 43562306a36Sopenharmony_ci unsigned long *idaws; 43662306a36Sopenharmony_ci struct LO_fba_data *LO_data; 43762306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 43862306a36Sopenharmony_ci struct ccw1 *ccw; 43962306a36Sopenharmony_ci struct req_iterator iter; 44062306a36Sopenharmony_ci struct bio_vec bv; 44162306a36Sopenharmony_ci char *dst; 44262306a36Sopenharmony_ci int count, cidaw, cplength, datasize; 44362306a36Sopenharmony_ci sector_t recid, first_rec, last_rec; 44462306a36Sopenharmony_ci unsigned int blksize, off; 44562306a36Sopenharmony_ci unsigned char cmd; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (rq_data_dir(req) == READ) { 44862306a36Sopenharmony_ci cmd = DASD_FBA_CCW_READ; 44962306a36Sopenharmony_ci } else if (rq_data_dir(req) == WRITE) { 45062306a36Sopenharmony_ci cmd = DASD_FBA_CCW_WRITE; 45162306a36Sopenharmony_ci } else 45262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 45362306a36Sopenharmony_ci blksize = block->bp_block; 45462306a36Sopenharmony_ci /* Calculate record id of first and last block. */ 45562306a36Sopenharmony_ci first_rec = blk_rq_pos(req) >> block->s2b_shift; 45662306a36Sopenharmony_ci last_rec = 45762306a36Sopenharmony_ci (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift; 45862306a36Sopenharmony_ci /* Check struct bio and count the number of blocks for the request. */ 45962306a36Sopenharmony_ci count = 0; 46062306a36Sopenharmony_ci cidaw = 0; 46162306a36Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 46262306a36Sopenharmony_ci if (bv.bv_len & (blksize - 1)) 46362306a36Sopenharmony_ci /* Fba can only do full blocks. */ 46462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 46562306a36Sopenharmony_ci count += bv.bv_len >> (block->s2b_shift + 9); 46662306a36Sopenharmony_ci if (idal_is_needed (page_address(bv.bv_page), bv.bv_len)) 46762306a36Sopenharmony_ci cidaw += bv.bv_len / blksize; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci /* Paranoia. */ 47062306a36Sopenharmony_ci if (count != last_rec - first_rec + 1) 47162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 47262306a36Sopenharmony_ci /* 1x define extent + 1x locate record + number of blocks */ 47362306a36Sopenharmony_ci cplength = 2 + count; 47462306a36Sopenharmony_ci /* 1x define extent + 1x locate record */ 47562306a36Sopenharmony_ci datasize = sizeof(struct DE_fba_data) + sizeof(struct LO_fba_data) + 47662306a36Sopenharmony_ci cidaw * sizeof(unsigned long); 47762306a36Sopenharmony_ci /* 47862306a36Sopenharmony_ci * Find out number of additional locate record ccws if the device 47962306a36Sopenharmony_ci * can't do data chaining. 48062306a36Sopenharmony_ci */ 48162306a36Sopenharmony_ci if (private->rdc_data.mode.bits.data_chain == 0) { 48262306a36Sopenharmony_ci cplength += count - 1; 48362306a36Sopenharmony_ci datasize += (count - 1)*sizeof(struct LO_fba_data); 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci /* Allocate the ccw request. */ 48662306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev, 48762306a36Sopenharmony_ci blk_mq_rq_to_pdu(req)); 48862306a36Sopenharmony_ci if (IS_ERR(cqr)) 48962306a36Sopenharmony_ci return cqr; 49062306a36Sopenharmony_ci ccw = cqr->cpaddr; 49162306a36Sopenharmony_ci /* First ccw is define extent. */ 49262306a36Sopenharmony_ci define_extent(ccw++, cqr->data, rq_data_dir(req), 49362306a36Sopenharmony_ci block->bp_block, blk_rq_pos(req), blk_rq_sectors(req)); 49462306a36Sopenharmony_ci /* Build locate_record + read/write ccws. */ 49562306a36Sopenharmony_ci idaws = (unsigned long *) (cqr->data + sizeof(struct DE_fba_data)); 49662306a36Sopenharmony_ci LO_data = (struct LO_fba_data *) (idaws + cidaw); 49762306a36Sopenharmony_ci /* Locate record for all blocks for smart devices. */ 49862306a36Sopenharmony_ci if (private->rdc_data.mode.bits.data_chain != 0) { 49962306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 50062306a36Sopenharmony_ci locate_record(ccw++, LO_data++, rq_data_dir(req), 0, count); 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci recid = first_rec; 50362306a36Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 50462306a36Sopenharmony_ci dst = bvec_virt(&bv); 50562306a36Sopenharmony_ci if (dasd_page_cache) { 50662306a36Sopenharmony_ci char *copy = kmem_cache_alloc(dasd_page_cache, 50762306a36Sopenharmony_ci GFP_DMA | __GFP_NOWARN); 50862306a36Sopenharmony_ci if (copy && rq_data_dir(req) == WRITE) 50962306a36Sopenharmony_ci memcpy(copy + bv.bv_offset, dst, bv.bv_len); 51062306a36Sopenharmony_ci if (copy) 51162306a36Sopenharmony_ci dst = copy + bv.bv_offset; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci for (off = 0; off < bv.bv_len; off += blksize) { 51462306a36Sopenharmony_ci /* Locate record for stupid devices. */ 51562306a36Sopenharmony_ci if (private->rdc_data.mode.bits.data_chain == 0) { 51662306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 51762306a36Sopenharmony_ci locate_record(ccw, LO_data++, 51862306a36Sopenharmony_ci rq_data_dir(req), 51962306a36Sopenharmony_ci recid - first_rec, 1); 52062306a36Sopenharmony_ci ccw->flags = CCW_FLAG_CC; 52162306a36Sopenharmony_ci ccw++; 52262306a36Sopenharmony_ci } else { 52362306a36Sopenharmony_ci if (recid > first_rec) 52462306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_DC; 52562306a36Sopenharmony_ci else 52662306a36Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci ccw->cmd_code = cmd; 52962306a36Sopenharmony_ci ccw->count = block->bp_block; 53062306a36Sopenharmony_ci if (idal_is_needed(dst, blksize)) { 53162306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(idaws); 53262306a36Sopenharmony_ci ccw->flags = CCW_FLAG_IDA; 53362306a36Sopenharmony_ci idaws = idal_create_words(idaws, dst, blksize); 53462306a36Sopenharmony_ci } else { 53562306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(dst); 53662306a36Sopenharmony_ci ccw->flags = 0; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci ccw++; 53962306a36Sopenharmony_ci dst += blksize; 54062306a36Sopenharmony_ci recid++; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci if (blk_noretry_request(req) || 54462306a36Sopenharmony_ci block->base->features & DASD_FEATURE_FAILFAST) 54562306a36Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 54662306a36Sopenharmony_ci cqr->startdev = memdev; 54762306a36Sopenharmony_ci cqr->memdev = memdev; 54862306a36Sopenharmony_ci cqr->block = block; 54962306a36Sopenharmony_ci cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */ 55062306a36Sopenharmony_ci cqr->retries = memdev->default_retries; 55162306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 55262306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 55362306a36Sopenharmony_ci return cqr; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device *memdev, 55762306a36Sopenharmony_ci struct dasd_block *block, 55862306a36Sopenharmony_ci struct request *req) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci if (req_op(req) == REQ_OP_DISCARD || req_op(req) == REQ_OP_WRITE_ZEROES) 56162306a36Sopenharmony_ci return dasd_fba_build_cp_discard(memdev, block, req); 56262306a36Sopenharmony_ci else 56362306a36Sopenharmony_ci return dasd_fba_build_cp_regular(memdev, block, req); 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic int 56762306a36Sopenharmony_cidasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct dasd_fba_private *private = cqr->block->base->private; 57062306a36Sopenharmony_ci struct ccw1 *ccw; 57162306a36Sopenharmony_ci struct req_iterator iter; 57262306a36Sopenharmony_ci struct bio_vec bv; 57362306a36Sopenharmony_ci char *dst, *cda; 57462306a36Sopenharmony_ci unsigned int blksize, off; 57562306a36Sopenharmony_ci int status; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (!dasd_page_cache) 57862306a36Sopenharmony_ci goto out; 57962306a36Sopenharmony_ci blksize = cqr->block->bp_block; 58062306a36Sopenharmony_ci ccw = cqr->cpaddr; 58162306a36Sopenharmony_ci /* Skip over define extent & locate record. */ 58262306a36Sopenharmony_ci ccw++; 58362306a36Sopenharmony_ci if (private->rdc_data.mode.bits.data_chain != 0) 58462306a36Sopenharmony_ci ccw++; 58562306a36Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 58662306a36Sopenharmony_ci dst = bvec_virt(&bv); 58762306a36Sopenharmony_ci for (off = 0; off < bv.bv_len; off += blksize) { 58862306a36Sopenharmony_ci /* Skip locate record. */ 58962306a36Sopenharmony_ci if (private->rdc_data.mode.bits.data_chain == 0) 59062306a36Sopenharmony_ci ccw++; 59162306a36Sopenharmony_ci if (dst) { 59262306a36Sopenharmony_ci if (ccw->flags & CCW_FLAG_IDA) 59362306a36Sopenharmony_ci cda = *((char **)phys_to_virt(ccw->cda)); 59462306a36Sopenharmony_ci else 59562306a36Sopenharmony_ci cda = phys_to_virt(ccw->cda); 59662306a36Sopenharmony_ci if (dst != cda) { 59762306a36Sopenharmony_ci if (rq_data_dir(req) == READ) 59862306a36Sopenharmony_ci memcpy(dst, cda, bv.bv_len); 59962306a36Sopenharmony_ci kmem_cache_free(dasd_page_cache, 60062306a36Sopenharmony_ci (void *)((addr_t)cda & PAGE_MASK)); 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci dst = NULL; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci ccw++; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ciout: 60862306a36Sopenharmony_ci status = cqr->status == DASD_CQR_DONE; 60962306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 61062306a36Sopenharmony_ci return status; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic void dasd_fba_handle_terminated_request(struct dasd_ccw_req *cqr) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci if (cqr->retries < 0) 61662306a36Sopenharmony_ci cqr->status = DASD_CQR_FAILED; 61762306a36Sopenharmony_ci else 61862306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 61962306a36Sopenharmony_ci}; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic int 62262306a36Sopenharmony_cidasd_fba_fill_info(struct dasd_device * device, 62362306a36Sopenharmony_ci struct dasd_information2_t * info) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct dasd_fba_private *private = device->private; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci info->label_block = 1; 62862306a36Sopenharmony_ci info->FBA_layout = 1; 62962306a36Sopenharmony_ci info->format = DASD_FORMAT_LDL; 63062306a36Sopenharmony_ci info->characteristics_size = sizeof(private->rdc_data); 63162306a36Sopenharmony_ci memcpy(info->characteristics, &private->rdc_data, 63262306a36Sopenharmony_ci sizeof(private->rdc_data)); 63362306a36Sopenharmony_ci info->confdata_size = 0; 63462306a36Sopenharmony_ci return 0; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic void 63862306a36Sopenharmony_cidasd_fba_dump_sense_dbf(struct dasd_device *device, struct irb *irb, 63962306a36Sopenharmony_ci char *reason) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci u64 *sense; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci sense = (u64 *) dasd_get_sense(irb); 64462306a36Sopenharmony_ci if (sense) { 64562306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_EMERG, device, 64662306a36Sopenharmony_ci "%s: %s %02x%02x%02x %016llx %016llx %016llx " 64762306a36Sopenharmony_ci "%016llx", reason, 64862306a36Sopenharmony_ci scsw_is_tm(&irb->scsw) ? "t" : "c", 64962306a36Sopenharmony_ci scsw_cc(&irb->scsw), scsw_cstat(&irb->scsw), 65062306a36Sopenharmony_ci scsw_dstat(&irb->scsw), sense[0], sense[1], 65162306a36Sopenharmony_ci sense[2], sense[3]); 65262306a36Sopenharmony_ci } else { 65362306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_EMERG, device, "%s", 65462306a36Sopenharmony_ci "SORRY - NO VALID SENSE AVAILABLE\n"); 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic void 66062306a36Sopenharmony_cidasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, 66162306a36Sopenharmony_ci struct irb *irb) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci char *page; 66462306a36Sopenharmony_ci struct ccw1 *act, *end, *last; 66562306a36Sopenharmony_ci int len, sl, sct, count; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci page = (char *) get_zeroed_page(GFP_ATOMIC); 66862306a36Sopenharmony_ci if (page == NULL) { 66962306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 67062306a36Sopenharmony_ci "No memory to dump sense data"); 67162306a36Sopenharmony_ci return; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci len = sprintf(page, PRINTK_HEADER 67462306a36Sopenharmony_ci " I/O status report for device %s:\n", 67562306a36Sopenharmony_ci dev_name(&device->cdev->dev)); 67662306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 67762306a36Sopenharmony_ci " in req: %p CS: 0x%02X DS: 0x%02X\n", req, 67862306a36Sopenharmony_ci irb->scsw.cmd.cstat, irb->scsw.cmd.dstat); 67962306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 68062306a36Sopenharmony_ci " device %s: Failing CCW: %p\n", 68162306a36Sopenharmony_ci dev_name(&device->cdev->dev), 68262306a36Sopenharmony_ci (void *) (addr_t) irb->scsw.cmd.cpa); 68362306a36Sopenharmony_ci if (irb->esw.esw0.erw.cons) { 68462306a36Sopenharmony_ci for (sl = 0; sl < 4; sl++) { 68562306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 68662306a36Sopenharmony_ci " Sense(hex) %2d-%2d:", 68762306a36Sopenharmony_ci (8 * sl), ((8 * sl) + 7)); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci for (sct = 0; sct < 8; sct++) { 69062306a36Sopenharmony_ci len += sprintf(page + len, " %02x", 69162306a36Sopenharmony_ci irb->ecw[8 * sl + sct]); 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci len += sprintf(page + len, "\n"); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci } else { 69662306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 69762306a36Sopenharmony_ci " SORRY - NO VALID SENSE AVAILABLE\n"); 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci printk(KERN_ERR "%s", page); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci /* dump the Channel Program */ 70262306a36Sopenharmony_ci /* print first CCWs (maximum 8) */ 70362306a36Sopenharmony_ci act = req->cpaddr; 70462306a36Sopenharmony_ci for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); 70562306a36Sopenharmony_ci end = min(act + 8, last); 70662306a36Sopenharmony_ci len = sprintf(page, PRINTK_HEADER " Related CP in req: %p\n", req); 70762306a36Sopenharmony_ci while (act <= end) { 70862306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 70962306a36Sopenharmony_ci " CCW %p: %08X %08X DAT:", 71062306a36Sopenharmony_ci act, ((int *) act)[0], ((int *) act)[1]); 71162306a36Sopenharmony_ci for (count = 0; count < 32 && count < act->count; 71262306a36Sopenharmony_ci count += sizeof(int)) 71362306a36Sopenharmony_ci len += sprintf(page + len, " %08X", 71462306a36Sopenharmony_ci ((int *) (addr_t) act->cda) 71562306a36Sopenharmony_ci [(count>>2)]); 71662306a36Sopenharmony_ci len += sprintf(page + len, "\n"); 71762306a36Sopenharmony_ci act++; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci printk(KERN_ERR "%s", page); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* print failing CCW area */ 72362306a36Sopenharmony_ci len = 0; 72462306a36Sopenharmony_ci if (act < ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2) { 72562306a36Sopenharmony_ci act = ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2; 72662306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER "......\n"); 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci end = min((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa + 2, last); 72962306a36Sopenharmony_ci while (act <= end) { 73062306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 73162306a36Sopenharmony_ci " CCW %p: %08X %08X DAT:", 73262306a36Sopenharmony_ci act, ((int *) act)[0], ((int *) act)[1]); 73362306a36Sopenharmony_ci for (count = 0; count < 32 && count < act->count; 73462306a36Sopenharmony_ci count += sizeof(int)) 73562306a36Sopenharmony_ci len += sprintf(page + len, " %08X", 73662306a36Sopenharmony_ci ((int *) (addr_t) act->cda) 73762306a36Sopenharmony_ci [(count>>2)]); 73862306a36Sopenharmony_ci len += sprintf(page + len, "\n"); 73962306a36Sopenharmony_ci act++; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci /* print last CCWs */ 74362306a36Sopenharmony_ci if (act < last - 2) { 74462306a36Sopenharmony_ci act = last - 2; 74562306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER "......\n"); 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci while (act <= last) { 74862306a36Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 74962306a36Sopenharmony_ci " CCW %p: %08X %08X DAT:", 75062306a36Sopenharmony_ci act, ((int *) act)[0], ((int *) act)[1]); 75162306a36Sopenharmony_ci for (count = 0; count < 32 && count < act->count; 75262306a36Sopenharmony_ci count += sizeof(int)) 75362306a36Sopenharmony_ci len += sprintf(page + len, " %08X", 75462306a36Sopenharmony_ci ((int *) (addr_t) act->cda) 75562306a36Sopenharmony_ci [(count>>2)]); 75662306a36Sopenharmony_ci len += sprintf(page + len, "\n"); 75762306a36Sopenharmony_ci act++; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci if (len > 0) 76062306a36Sopenharmony_ci printk(KERN_ERR "%s", page); 76162306a36Sopenharmony_ci free_page((unsigned long) page); 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci/* 76562306a36Sopenharmony_ci * Initialize block layer request queue. 76662306a36Sopenharmony_ci */ 76762306a36Sopenharmony_cistatic void dasd_fba_setup_blk_queue(struct dasd_block *block) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci unsigned int logical_block_size = block->bp_block; 77062306a36Sopenharmony_ci struct request_queue *q = block->gdp->queue; 77162306a36Sopenharmony_ci unsigned int max_bytes, max_discard_sectors; 77262306a36Sopenharmony_ci int max; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci max = DASD_FBA_MAX_BLOCKS << block->s2b_shift; 77562306a36Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_NONROT, q); 77662306a36Sopenharmony_ci q->limits.max_dev_sectors = max; 77762306a36Sopenharmony_ci blk_queue_logical_block_size(q, logical_block_size); 77862306a36Sopenharmony_ci blk_queue_max_hw_sectors(q, max); 77962306a36Sopenharmony_ci blk_queue_max_segments(q, USHRT_MAX); 78062306a36Sopenharmony_ci /* With page sized segments each segment can be translated into one idaw/tidaw */ 78162306a36Sopenharmony_ci blk_queue_max_segment_size(q, PAGE_SIZE); 78262306a36Sopenharmony_ci blk_queue_segment_boundary(q, PAGE_SIZE - 1); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci q->limits.discard_granularity = logical_block_size; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci /* Calculate max_discard_sectors and make it PAGE aligned */ 78762306a36Sopenharmony_ci max_bytes = USHRT_MAX * logical_block_size; 78862306a36Sopenharmony_ci max_bytes = ALIGN_DOWN(max_bytes, PAGE_SIZE); 78962306a36Sopenharmony_ci max_discard_sectors = max_bytes / logical_block_size; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci blk_queue_max_discard_sectors(q, max_discard_sectors); 79262306a36Sopenharmony_ci blk_queue_max_write_zeroes_sectors(q, max_discard_sectors); 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic int dasd_fba_pe_handler(struct dasd_device *device, 79662306a36Sopenharmony_ci __u8 tbvpm, __u8 fcsecpm) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci return dasd_generic_verify_path(device, tbvpm); 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic struct dasd_discipline dasd_fba_discipline = { 80262306a36Sopenharmony_ci .owner = THIS_MODULE, 80362306a36Sopenharmony_ci .name = "FBA ", 80462306a36Sopenharmony_ci .ebcname = "FBA ", 80562306a36Sopenharmony_ci .check_device = dasd_fba_check_characteristics, 80662306a36Sopenharmony_ci .do_analysis = dasd_fba_do_analysis, 80762306a36Sopenharmony_ci .pe_handler = dasd_fba_pe_handler, 80862306a36Sopenharmony_ci .setup_blk_queue = dasd_fba_setup_blk_queue, 80962306a36Sopenharmony_ci .fill_geometry = dasd_fba_fill_geometry, 81062306a36Sopenharmony_ci .start_IO = dasd_start_IO, 81162306a36Sopenharmony_ci .term_IO = dasd_term_IO, 81262306a36Sopenharmony_ci .handle_terminated_request = dasd_fba_handle_terminated_request, 81362306a36Sopenharmony_ci .erp_action = dasd_fba_erp_action, 81462306a36Sopenharmony_ci .erp_postaction = dasd_fba_erp_postaction, 81562306a36Sopenharmony_ci .check_for_device_change = dasd_fba_check_for_device_change, 81662306a36Sopenharmony_ci .build_cp = dasd_fba_build_cp, 81762306a36Sopenharmony_ci .free_cp = dasd_fba_free_cp, 81862306a36Sopenharmony_ci .dump_sense = dasd_fba_dump_sense, 81962306a36Sopenharmony_ci .dump_sense_dbf = dasd_fba_dump_sense_dbf, 82062306a36Sopenharmony_ci .fill_info = dasd_fba_fill_info, 82162306a36Sopenharmony_ci}; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic int __init 82462306a36Sopenharmony_cidasd_fba_init(void) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci int ret; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci ASCEBC(dasd_fba_discipline.ebcname, 4); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci dasd_fba_zero_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); 83162306a36Sopenharmony_ci if (!dasd_fba_zero_page) 83262306a36Sopenharmony_ci return -ENOMEM; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci ret = ccw_driver_register(&dasd_fba_driver); 83562306a36Sopenharmony_ci if (!ret) 83662306a36Sopenharmony_ci wait_for_device_probe(); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci return ret; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic void __exit 84262306a36Sopenharmony_cidasd_fba_cleanup(void) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci ccw_driver_unregister(&dasd_fba_driver); 84562306a36Sopenharmony_ci free_page((unsigned long)dasd_fba_zero_page); 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cimodule_init(dasd_fba_init); 84962306a36Sopenharmony_cimodule_exit(dasd_fba_cleanup); 850