18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> 48c2ecf20Sopenharmony_ci * Bugreports.to..: <Linux390@de.ibm.com> 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 1999, 2009 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "dasd-fba" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/stddef.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <asm/debug.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/hdreg.h> /* HDIO_GETGEO */ 168c2ecf20Sopenharmony_ci#include <linux/bio.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <asm/idals.h> 218c2ecf20Sopenharmony_ci#include <asm/ebcdic.h> 228c2ecf20Sopenharmony_ci#include <asm/io.h> 238c2ecf20Sopenharmony_ci#include <asm/ccwdev.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "dasd_int.h" 268c2ecf20Sopenharmony_ci#include "dasd_fba.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#ifdef PRINTK_HEADER 298c2ecf20Sopenharmony_ci#undef PRINTK_HEADER 308c2ecf20Sopenharmony_ci#endif /* PRINTK_HEADER */ 318c2ecf20Sopenharmony_ci#define PRINTK_HEADER "dasd(fba):" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define FBA_DEFAULT_RETRIES 32 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define DASD_FBA_CCW_WRITE 0x41 368c2ecf20Sopenharmony_ci#define DASD_FBA_CCW_READ 0x42 378c2ecf20Sopenharmony_ci#define DASD_FBA_CCW_LOCATE 0x43 388c2ecf20Sopenharmony_ci#define DASD_FBA_CCW_DEFINE_EXTENT 0x63 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic struct dasd_discipline dasd_fba_discipline; 438c2ecf20Sopenharmony_cistatic void *dasd_fba_zero_page; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistruct dasd_fba_private { 468c2ecf20Sopenharmony_ci struct dasd_fba_characteristics rdc_data; 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic struct ccw_device_id dasd_fba_ids[] = { 508c2ecf20Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x6310, 0, 0x9336, 0), .driver_info = 0x1}, 518c2ecf20Sopenharmony_ci { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3370, 0), .driver_info = 0x2}, 528c2ecf20Sopenharmony_ci { /* end of list */ }, 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(ccw, dasd_fba_ids); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic struct ccw_driver dasd_fba_driver; /* see below */ 588c2ecf20Sopenharmony_cistatic int 598c2ecf20Sopenharmony_cidasd_fba_probe(struct ccw_device *cdev) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci return dasd_generic_probe(cdev, &dasd_fba_discipline); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int 658c2ecf20Sopenharmony_cidasd_fba_set_online(struct ccw_device *cdev) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci return dasd_generic_set_online(cdev, &dasd_fba_discipline); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic struct ccw_driver dasd_fba_driver = { 718c2ecf20Sopenharmony_ci .driver = { 728c2ecf20Sopenharmony_ci .name = "dasd-fba", 738c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 748c2ecf20Sopenharmony_ci }, 758c2ecf20Sopenharmony_ci .ids = dasd_fba_ids, 768c2ecf20Sopenharmony_ci .probe = dasd_fba_probe, 778c2ecf20Sopenharmony_ci .remove = dasd_generic_remove, 788c2ecf20Sopenharmony_ci .set_offline = dasd_generic_set_offline, 798c2ecf20Sopenharmony_ci .set_online = dasd_fba_set_online, 808c2ecf20Sopenharmony_ci .notify = dasd_generic_notify, 818c2ecf20Sopenharmony_ci .path_event = dasd_generic_path_event, 828c2ecf20Sopenharmony_ci .freeze = dasd_generic_pm_freeze, 838c2ecf20Sopenharmony_ci .thaw = dasd_generic_restore_device, 848c2ecf20Sopenharmony_ci .restore = dasd_generic_restore_device, 858c2ecf20Sopenharmony_ci .int_class = IRQIO_DAS, 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic void 898c2ecf20Sopenharmony_cidefine_extent(struct ccw1 * ccw, struct DE_fba_data *data, int rw, 908c2ecf20Sopenharmony_ci int blksize, int beg, int nr) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_FBA_CCW_DEFINE_EXTENT; 938c2ecf20Sopenharmony_ci ccw->flags = 0; 948c2ecf20Sopenharmony_ci ccw->count = 16; 958c2ecf20Sopenharmony_ci ccw->cda = (__u32) __pa(data); 968c2ecf20Sopenharmony_ci memset(data, 0, sizeof (struct DE_fba_data)); 978c2ecf20Sopenharmony_ci if (rw == WRITE) 988c2ecf20Sopenharmony_ci (data->mask).perm = 0x0; 998c2ecf20Sopenharmony_ci else if (rw == READ) 1008c2ecf20Sopenharmony_ci (data->mask).perm = 0x1; 1018c2ecf20Sopenharmony_ci else 1028c2ecf20Sopenharmony_ci data->mask.perm = 0x2; 1038c2ecf20Sopenharmony_ci data->blk_size = blksize; 1048c2ecf20Sopenharmony_ci data->ext_loc = beg; 1058c2ecf20Sopenharmony_ci data->ext_end = nr - 1; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic void 1098c2ecf20Sopenharmony_cilocate_record(struct ccw1 * ccw, struct LO_fba_data *data, int rw, 1108c2ecf20Sopenharmony_ci int block_nr, int block_ct) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_FBA_CCW_LOCATE; 1138c2ecf20Sopenharmony_ci ccw->flags = 0; 1148c2ecf20Sopenharmony_ci ccw->count = 8; 1158c2ecf20Sopenharmony_ci ccw->cda = (__u32) __pa(data); 1168c2ecf20Sopenharmony_ci memset(data, 0, sizeof (struct LO_fba_data)); 1178c2ecf20Sopenharmony_ci if (rw == WRITE) 1188c2ecf20Sopenharmony_ci data->operation.cmd = 0x5; 1198c2ecf20Sopenharmony_ci else if (rw == READ) 1208c2ecf20Sopenharmony_ci data->operation.cmd = 0x6; 1218c2ecf20Sopenharmony_ci else 1228c2ecf20Sopenharmony_ci data->operation.cmd = 0x8; 1238c2ecf20Sopenharmony_ci data->blk_nr = block_nr; 1248c2ecf20Sopenharmony_ci data->blk_ct = block_ct; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int 1288c2ecf20Sopenharmony_cidasd_fba_check_characteristics(struct dasd_device *device) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct dasd_fba_private *private = device->private; 1318c2ecf20Sopenharmony_ci struct ccw_device *cdev = device->cdev; 1328c2ecf20Sopenharmony_ci struct dasd_block *block; 1338c2ecf20Sopenharmony_ci int readonly, rc; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (!private) { 1368c2ecf20Sopenharmony_ci private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA); 1378c2ecf20Sopenharmony_ci if (!private) { 1388c2ecf20Sopenharmony_ci dev_warn(&device->cdev->dev, 1398c2ecf20Sopenharmony_ci "Allocating memory for private DASD " 1408c2ecf20Sopenharmony_ci "data failed\n"); 1418c2ecf20Sopenharmony_ci return -ENOMEM; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci device->private = private; 1448c2ecf20Sopenharmony_ci } else { 1458c2ecf20Sopenharmony_ci memset(private, 0, sizeof(*private)); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci block = dasd_alloc_block(); 1488c2ecf20Sopenharmony_ci if (IS_ERR(block)) { 1498c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", "could not allocate " 1508c2ecf20Sopenharmony_ci "dasd block structure"); 1518c2ecf20Sopenharmony_ci device->private = NULL; 1528c2ecf20Sopenharmony_ci kfree(private); 1538c2ecf20Sopenharmony_ci return PTR_ERR(block); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci device->block = block; 1568c2ecf20Sopenharmony_ci block->base = device; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Read Device Characteristics */ 1598c2ecf20Sopenharmony_ci rc = dasd_generic_read_dev_chars(device, DASD_FBA_MAGIC, 1608c2ecf20Sopenharmony_ci &private->rdc_data, 32); 1618c2ecf20Sopenharmony_ci if (rc) { 1628c2ecf20Sopenharmony_ci DBF_EVENT_DEVID(DBF_WARNING, cdev, "Read device " 1638c2ecf20Sopenharmony_ci "characteristics returned error %d", rc); 1648c2ecf20Sopenharmony_ci device->block = NULL; 1658c2ecf20Sopenharmony_ci dasd_free_block(block); 1668c2ecf20Sopenharmony_ci device->private = NULL; 1678c2ecf20Sopenharmony_ci kfree(private); 1688c2ecf20Sopenharmony_ci return rc; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci device->default_expires = DASD_EXPIRES; 1728c2ecf20Sopenharmony_ci device->default_retries = FBA_DEFAULT_RETRIES; 1738c2ecf20Sopenharmony_ci dasd_path_set_opm(device, LPM_ANYPATH); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci readonly = dasd_device_is_ro(device); 1768c2ecf20Sopenharmony_ci if (readonly) 1778c2ecf20Sopenharmony_ci set_bit(DASD_FLAG_DEVICE_RO, &device->flags); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* FBA supports discard, set the according feature bit */ 1808c2ecf20Sopenharmony_ci dasd_set_feature(cdev, DASD_FEATURE_DISCARD, 1); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci dev_info(&device->cdev->dev, 1838c2ecf20Sopenharmony_ci "New FBA DASD %04X/%02X (CU %04X/%02X) with %d MB " 1848c2ecf20Sopenharmony_ci "and %d B/blk%s\n", 1858c2ecf20Sopenharmony_ci cdev->id.dev_type, 1868c2ecf20Sopenharmony_ci cdev->id.dev_model, 1878c2ecf20Sopenharmony_ci cdev->id.cu_type, 1888c2ecf20Sopenharmony_ci cdev->id.cu_model, 1898c2ecf20Sopenharmony_ci ((private->rdc_data.blk_bdsa * 1908c2ecf20Sopenharmony_ci (private->rdc_data.blk_size >> 9)) >> 11), 1918c2ecf20Sopenharmony_ci private->rdc_data.blk_size, 1928c2ecf20Sopenharmony_ci readonly ? ", read-only device" : ""); 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int dasd_fba_do_analysis(struct dasd_block *block) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct dasd_fba_private *private = block->base->private; 1998c2ecf20Sopenharmony_ci int sb, rc; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci rc = dasd_check_blocksize(private->rdc_data.blk_size); 2028c2ecf20Sopenharmony_ci if (rc) { 2038c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, block->base, "unknown blocksize %d", 2048c2ecf20Sopenharmony_ci private->rdc_data.blk_size); 2058c2ecf20Sopenharmony_ci return rc; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci block->blocks = private->rdc_data.blk_bdsa; 2088c2ecf20Sopenharmony_ci block->bp_block = private->rdc_data.blk_size; 2098c2ecf20Sopenharmony_ci block->s2b_shift = 0; /* bits to shift 512 to get a block */ 2108c2ecf20Sopenharmony_ci for (sb = 512; sb < private->rdc_data.blk_size; sb = sb << 1) 2118c2ecf20Sopenharmony_ci block->s2b_shift++; 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int dasd_fba_fill_geometry(struct dasd_block *block, 2168c2ecf20Sopenharmony_ci struct hd_geometry *geo) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci if (dasd_check_blocksize(block->bp_block) != 0) 2198c2ecf20Sopenharmony_ci return -EINVAL; 2208c2ecf20Sopenharmony_ci geo->cylinders = (block->blocks << block->s2b_shift) >> 10; 2218c2ecf20Sopenharmony_ci geo->heads = 16; 2228c2ecf20Sopenharmony_ci geo->sectors = 128 >> block->s2b_shift; 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic dasd_erp_fn_t 2278c2ecf20Sopenharmony_cidasd_fba_erp_action(struct dasd_ccw_req * cqr) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci return dasd_default_erp_action; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic dasd_erp_fn_t 2338c2ecf20Sopenharmony_cidasd_fba_erp_postaction(struct dasd_ccw_req * cqr) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci if (cqr->function == dasd_default_erp_action) 2368c2ecf20Sopenharmony_ci return dasd_default_erp_postaction; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, cqr->startdev, "unknown ERP action %p", 2398c2ecf20Sopenharmony_ci cqr->function); 2408c2ecf20Sopenharmony_ci return NULL; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic void dasd_fba_check_for_device_change(struct dasd_device *device, 2448c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr, 2458c2ecf20Sopenharmony_ci struct irb *irb) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci char mask; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* first of all check for state change pending interrupt */ 2508c2ecf20Sopenharmony_ci mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; 2518c2ecf20Sopenharmony_ci if ((irb->scsw.cmd.dstat & mask) == mask) 2528c2ecf20Sopenharmony_ci dasd_generic_handle_state_change(device); 2538c2ecf20Sopenharmony_ci}; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/* 2578c2ecf20Sopenharmony_ci * Builds a CCW with no data payload 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_cistatic void ccw_write_no_data(struct ccw1 *ccw) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_FBA_CCW_WRITE; 2628c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 2638c2ecf20Sopenharmony_ci ccw->count = 0; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* 2678c2ecf20Sopenharmony_ci * Builds a CCW that writes only zeroes. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_cistatic void ccw_write_zero(struct ccw1 *ccw, int count) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_FBA_CCW_WRITE; 2728c2ecf20Sopenharmony_ci ccw->flags |= CCW_FLAG_SLI; 2738c2ecf20Sopenharmony_ci ccw->count = count; 2748c2ecf20Sopenharmony_ci ccw->cda = (__u32) (addr_t) dasd_fba_zero_page; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci/* 2788c2ecf20Sopenharmony_ci * Helper function to count the amount of necessary CCWs within a given range 2798c2ecf20Sopenharmony_ci * with 4k alignment and command chaining in mind. 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_cistatic int count_ccws(sector_t first_rec, sector_t last_rec, 2828c2ecf20Sopenharmony_ci unsigned int blocks_per_page) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci sector_t wz_stop = 0, d_stop = 0; 2858c2ecf20Sopenharmony_ci int cur_pos = 0; 2868c2ecf20Sopenharmony_ci int count = 0; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (first_rec % blocks_per_page != 0) { 2898c2ecf20Sopenharmony_ci wz_stop = first_rec + blocks_per_page - 2908c2ecf20Sopenharmony_ci (first_rec % blocks_per_page) - 1; 2918c2ecf20Sopenharmony_ci if (wz_stop > last_rec) 2928c2ecf20Sopenharmony_ci wz_stop = last_rec; 2938c2ecf20Sopenharmony_ci cur_pos = wz_stop - first_rec + 1; 2948c2ecf20Sopenharmony_ci count++; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (last_rec - (first_rec + cur_pos) + 1 >= blocks_per_page) { 2988c2ecf20Sopenharmony_ci if ((last_rec - blocks_per_page + 1) % blocks_per_page != 0) 2998c2ecf20Sopenharmony_ci d_stop = last_rec - ((last_rec - blocks_per_page + 1) % 3008c2ecf20Sopenharmony_ci blocks_per_page); 3018c2ecf20Sopenharmony_ci else 3028c2ecf20Sopenharmony_ci d_stop = last_rec; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci cur_pos += d_stop - (first_rec + cur_pos) + 1; 3058c2ecf20Sopenharmony_ci count++; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (cur_pos == 0 || first_rec + cur_pos - 1 < last_rec) 3098c2ecf20Sopenharmony_ci count++; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return count; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci/* 3158c2ecf20Sopenharmony_ci * This function builds a CCW request for block layer discard requests. 3168c2ecf20Sopenharmony_ci * Each page in the z/VM hypervisor that represents certain records of an FBA 3178c2ecf20Sopenharmony_ci * device will be padded with zeros. This is a special behaviour of the WRITE 3188c2ecf20Sopenharmony_ci * command which is triggered when no data payload is added to the CCW. 3198c2ecf20Sopenharmony_ci * 3208c2ecf20Sopenharmony_ci * Note: Due to issues in some z/VM versions, we can't fully utilise this 3218c2ecf20Sopenharmony_ci * special behaviour. We have to keep a 4k (or 8 block) alignment in mind to 3228c2ecf20Sopenharmony_ci * work around those issues and write actual zeroes to the unaligned parts in 3238c2ecf20Sopenharmony_ci * the request. This workaround might be removed in the future. 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_fba_build_cp_discard( 3268c2ecf20Sopenharmony_ci struct dasd_device *memdev, 3278c2ecf20Sopenharmony_ci struct dasd_block *block, 3288c2ecf20Sopenharmony_ci struct request *req) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct LO_fba_data *LO_data; 3318c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 3328c2ecf20Sopenharmony_ci struct ccw1 *ccw; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci sector_t wz_stop = 0, d_stop = 0; 3358c2ecf20Sopenharmony_ci sector_t first_rec, last_rec; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci unsigned int blksize = block->bp_block; 3388c2ecf20Sopenharmony_ci unsigned int blocks_per_page; 3398c2ecf20Sopenharmony_ci int wz_count = 0; 3408c2ecf20Sopenharmony_ci int d_count = 0; 3418c2ecf20Sopenharmony_ci int cur_pos = 0; /* Current position within the extent */ 3428c2ecf20Sopenharmony_ci int count = 0; 3438c2ecf20Sopenharmony_ci int cplength; 3448c2ecf20Sopenharmony_ci int datasize; 3458c2ecf20Sopenharmony_ci int nr_ccws; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci first_rec = blk_rq_pos(req) >> block->s2b_shift; 3488c2ecf20Sopenharmony_ci last_rec = 3498c2ecf20Sopenharmony_ci (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift; 3508c2ecf20Sopenharmony_ci count = last_rec - first_rec + 1; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci blocks_per_page = BLOCKS_PER_PAGE(blksize); 3538c2ecf20Sopenharmony_ci nr_ccws = count_ccws(first_rec, last_rec, blocks_per_page); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* define extent + nr_ccws * locate record + nr_ccws * single CCW */ 3568c2ecf20Sopenharmony_ci cplength = 1 + 2 * nr_ccws; 3578c2ecf20Sopenharmony_ci datasize = sizeof(struct DE_fba_data) + 3588c2ecf20Sopenharmony_ci nr_ccws * (sizeof(struct LO_fba_data) + sizeof(struct ccw1)); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev, 3618c2ecf20Sopenharmony_ci blk_mq_rq_to_pdu(req)); 3628c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) 3638c2ecf20Sopenharmony_ci return cqr; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci define_extent(ccw++, cqr->data, WRITE, blksize, first_rec, count); 3688c2ecf20Sopenharmony_ci LO_data = cqr->data + sizeof(struct DE_fba_data); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* First part is not aligned. Calculate range to write zeroes. */ 3718c2ecf20Sopenharmony_ci if (first_rec % blocks_per_page != 0) { 3728c2ecf20Sopenharmony_ci wz_stop = first_rec + blocks_per_page - 3738c2ecf20Sopenharmony_ci (first_rec % blocks_per_page) - 1; 3748c2ecf20Sopenharmony_ci if (wz_stop > last_rec) 3758c2ecf20Sopenharmony_ci wz_stop = last_rec; 3768c2ecf20Sopenharmony_ci wz_count = wz_stop - first_rec + 1; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 3798c2ecf20Sopenharmony_ci locate_record(ccw++, LO_data++, WRITE, cur_pos, wz_count); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 3828c2ecf20Sopenharmony_ci ccw_write_zero(ccw++, wz_count * blksize); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci cur_pos = wz_count; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* We can do proper discard when we've got at least blocks_per_page blocks. */ 3888c2ecf20Sopenharmony_ci if (last_rec - (first_rec + cur_pos) + 1 >= blocks_per_page) { 3898c2ecf20Sopenharmony_ci /* is last record at page boundary? */ 3908c2ecf20Sopenharmony_ci if ((last_rec - blocks_per_page + 1) % blocks_per_page != 0) 3918c2ecf20Sopenharmony_ci d_stop = last_rec - ((last_rec - blocks_per_page + 1) % 3928c2ecf20Sopenharmony_ci blocks_per_page); 3938c2ecf20Sopenharmony_ci else 3948c2ecf20Sopenharmony_ci d_stop = last_rec; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci d_count = d_stop - (first_rec + cur_pos) + 1; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 3998c2ecf20Sopenharmony_ci locate_record(ccw++, LO_data++, WRITE, cur_pos, d_count); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 4028c2ecf20Sopenharmony_ci ccw_write_no_data(ccw++); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci cur_pos += d_count; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* We might still have some bits left which need to be zeroed. */ 4088c2ecf20Sopenharmony_ci if (cur_pos == 0 || first_rec + cur_pos - 1 < last_rec) { 4098c2ecf20Sopenharmony_ci if (d_stop != 0) 4108c2ecf20Sopenharmony_ci wz_count = last_rec - d_stop; 4118c2ecf20Sopenharmony_ci else if (wz_stop != 0) 4128c2ecf20Sopenharmony_ci wz_count = last_rec - wz_stop; 4138c2ecf20Sopenharmony_ci else 4148c2ecf20Sopenharmony_ci wz_count = count; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 4178c2ecf20Sopenharmony_ci locate_record(ccw++, LO_data++, WRITE, cur_pos, wz_count); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 4208c2ecf20Sopenharmony_ci ccw_write_zero(ccw++, wz_count * blksize); 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (blk_noretry_request(req) || 4248c2ecf20Sopenharmony_ci block->base->features & DASD_FEATURE_FAILFAST) 4258c2ecf20Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci cqr->startdev = memdev; 4288c2ecf20Sopenharmony_ci cqr->memdev = memdev; 4298c2ecf20Sopenharmony_ci cqr->block = block; 4308c2ecf20Sopenharmony_ci cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */ 4318c2ecf20Sopenharmony_ci cqr->retries = memdev->default_retries; 4328c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 4338c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return cqr; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_fba_build_cp_regular( 4398c2ecf20Sopenharmony_ci struct dasd_device *memdev, 4408c2ecf20Sopenharmony_ci struct dasd_block *block, 4418c2ecf20Sopenharmony_ci struct request *req) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct dasd_fba_private *private = block->base->private; 4448c2ecf20Sopenharmony_ci unsigned long *idaws; 4458c2ecf20Sopenharmony_ci struct LO_fba_data *LO_data; 4468c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 4478c2ecf20Sopenharmony_ci struct ccw1 *ccw; 4488c2ecf20Sopenharmony_ci struct req_iterator iter; 4498c2ecf20Sopenharmony_ci struct bio_vec bv; 4508c2ecf20Sopenharmony_ci char *dst; 4518c2ecf20Sopenharmony_ci int count, cidaw, cplength, datasize; 4528c2ecf20Sopenharmony_ci sector_t recid, first_rec, last_rec; 4538c2ecf20Sopenharmony_ci unsigned int blksize, off; 4548c2ecf20Sopenharmony_ci unsigned char cmd; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (rq_data_dir(req) == READ) { 4578c2ecf20Sopenharmony_ci cmd = DASD_FBA_CCW_READ; 4588c2ecf20Sopenharmony_ci } else if (rq_data_dir(req) == WRITE) { 4598c2ecf20Sopenharmony_ci cmd = DASD_FBA_CCW_WRITE; 4608c2ecf20Sopenharmony_ci } else 4618c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4628c2ecf20Sopenharmony_ci blksize = block->bp_block; 4638c2ecf20Sopenharmony_ci /* Calculate record id of first and last block. */ 4648c2ecf20Sopenharmony_ci first_rec = blk_rq_pos(req) >> block->s2b_shift; 4658c2ecf20Sopenharmony_ci last_rec = 4668c2ecf20Sopenharmony_ci (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift; 4678c2ecf20Sopenharmony_ci /* Check struct bio and count the number of blocks for the request. */ 4688c2ecf20Sopenharmony_ci count = 0; 4698c2ecf20Sopenharmony_ci cidaw = 0; 4708c2ecf20Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 4718c2ecf20Sopenharmony_ci if (bv.bv_len & (blksize - 1)) 4728c2ecf20Sopenharmony_ci /* Fba can only do full blocks. */ 4738c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4748c2ecf20Sopenharmony_ci count += bv.bv_len >> (block->s2b_shift + 9); 4758c2ecf20Sopenharmony_ci if (idal_is_needed (page_address(bv.bv_page), bv.bv_len)) 4768c2ecf20Sopenharmony_ci cidaw += bv.bv_len / blksize; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci /* Paranoia. */ 4798c2ecf20Sopenharmony_ci if (count != last_rec - first_rec + 1) 4808c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4818c2ecf20Sopenharmony_ci /* 1x define extent + 1x locate record + number of blocks */ 4828c2ecf20Sopenharmony_ci cplength = 2 + count; 4838c2ecf20Sopenharmony_ci /* 1x define extent + 1x locate record */ 4848c2ecf20Sopenharmony_ci datasize = sizeof(struct DE_fba_data) + sizeof(struct LO_fba_data) + 4858c2ecf20Sopenharmony_ci cidaw * sizeof(unsigned long); 4868c2ecf20Sopenharmony_ci /* 4878c2ecf20Sopenharmony_ci * Find out number of additional locate record ccws if the device 4888c2ecf20Sopenharmony_ci * can't do data chaining. 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci if (private->rdc_data.mode.bits.data_chain == 0) { 4918c2ecf20Sopenharmony_ci cplength += count - 1; 4928c2ecf20Sopenharmony_ci datasize += (count - 1)*sizeof(struct LO_fba_data); 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci /* Allocate the ccw request. */ 4958c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev, 4968c2ecf20Sopenharmony_ci blk_mq_rq_to_pdu(req)); 4978c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) 4988c2ecf20Sopenharmony_ci return cqr; 4998c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 5008c2ecf20Sopenharmony_ci /* First ccw is define extent. */ 5018c2ecf20Sopenharmony_ci define_extent(ccw++, cqr->data, rq_data_dir(req), 5028c2ecf20Sopenharmony_ci block->bp_block, blk_rq_pos(req), blk_rq_sectors(req)); 5038c2ecf20Sopenharmony_ci /* Build locate_record + read/write ccws. */ 5048c2ecf20Sopenharmony_ci idaws = (unsigned long *) (cqr->data + sizeof(struct DE_fba_data)); 5058c2ecf20Sopenharmony_ci LO_data = (struct LO_fba_data *) (idaws + cidaw); 5068c2ecf20Sopenharmony_ci /* Locate record for all blocks for smart devices. */ 5078c2ecf20Sopenharmony_ci if (private->rdc_data.mode.bits.data_chain != 0) { 5088c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 5098c2ecf20Sopenharmony_ci locate_record(ccw++, LO_data++, rq_data_dir(req), 0, count); 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci recid = first_rec; 5128c2ecf20Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 5138c2ecf20Sopenharmony_ci dst = page_address(bv.bv_page) + bv.bv_offset; 5148c2ecf20Sopenharmony_ci if (dasd_page_cache) { 5158c2ecf20Sopenharmony_ci char *copy = kmem_cache_alloc(dasd_page_cache, 5168c2ecf20Sopenharmony_ci GFP_DMA | __GFP_NOWARN); 5178c2ecf20Sopenharmony_ci if (copy && rq_data_dir(req) == WRITE) 5188c2ecf20Sopenharmony_ci memcpy(copy + bv.bv_offset, dst, bv.bv_len); 5198c2ecf20Sopenharmony_ci if (copy) 5208c2ecf20Sopenharmony_ci dst = copy + bv.bv_offset; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci for (off = 0; off < bv.bv_len; off += blksize) { 5238c2ecf20Sopenharmony_ci /* Locate record for stupid devices. */ 5248c2ecf20Sopenharmony_ci if (private->rdc_data.mode.bits.data_chain == 0) { 5258c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 5268c2ecf20Sopenharmony_ci locate_record(ccw, LO_data++, 5278c2ecf20Sopenharmony_ci rq_data_dir(req), 5288c2ecf20Sopenharmony_ci recid - first_rec, 1); 5298c2ecf20Sopenharmony_ci ccw->flags = CCW_FLAG_CC; 5308c2ecf20Sopenharmony_ci ccw++; 5318c2ecf20Sopenharmony_ci } else { 5328c2ecf20Sopenharmony_ci if (recid > first_rec) 5338c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_DC; 5348c2ecf20Sopenharmony_ci else 5358c2ecf20Sopenharmony_ci ccw[-1].flags |= CCW_FLAG_CC; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci ccw->cmd_code = cmd; 5388c2ecf20Sopenharmony_ci ccw->count = block->bp_block; 5398c2ecf20Sopenharmony_ci if (idal_is_needed(dst, blksize)) { 5408c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) idaws; 5418c2ecf20Sopenharmony_ci ccw->flags = CCW_FLAG_IDA; 5428c2ecf20Sopenharmony_ci idaws = idal_create_words(idaws, dst, blksize); 5438c2ecf20Sopenharmony_ci } else { 5448c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) dst; 5458c2ecf20Sopenharmony_ci ccw->flags = 0; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci ccw++; 5488c2ecf20Sopenharmony_ci dst += blksize; 5498c2ecf20Sopenharmony_ci recid++; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci if (blk_noretry_request(req) || 5538c2ecf20Sopenharmony_ci block->base->features & DASD_FEATURE_FAILFAST) 5548c2ecf20Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 5558c2ecf20Sopenharmony_ci cqr->startdev = memdev; 5568c2ecf20Sopenharmony_ci cqr->memdev = memdev; 5578c2ecf20Sopenharmony_ci cqr->block = block; 5588c2ecf20Sopenharmony_ci cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */ 5598c2ecf20Sopenharmony_ci cqr->retries = memdev->default_retries; 5608c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 5618c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 5628c2ecf20Sopenharmony_ci return cqr; 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device *memdev, 5668c2ecf20Sopenharmony_ci struct dasd_block *block, 5678c2ecf20Sopenharmony_ci struct request *req) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci if (req_op(req) == REQ_OP_DISCARD || req_op(req) == REQ_OP_WRITE_ZEROES) 5708c2ecf20Sopenharmony_ci return dasd_fba_build_cp_discard(memdev, block, req); 5718c2ecf20Sopenharmony_ci else 5728c2ecf20Sopenharmony_ci return dasd_fba_build_cp_regular(memdev, block, req); 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic int 5768c2ecf20Sopenharmony_cidasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci struct dasd_fba_private *private = cqr->block->base->private; 5798c2ecf20Sopenharmony_ci struct ccw1 *ccw; 5808c2ecf20Sopenharmony_ci struct req_iterator iter; 5818c2ecf20Sopenharmony_ci struct bio_vec bv; 5828c2ecf20Sopenharmony_ci char *dst, *cda; 5838c2ecf20Sopenharmony_ci unsigned int blksize, off; 5848c2ecf20Sopenharmony_ci int status; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (!dasd_page_cache) 5878c2ecf20Sopenharmony_ci goto out; 5888c2ecf20Sopenharmony_ci blksize = cqr->block->bp_block; 5898c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 5908c2ecf20Sopenharmony_ci /* Skip over define extent & locate record. */ 5918c2ecf20Sopenharmony_ci ccw++; 5928c2ecf20Sopenharmony_ci if (private->rdc_data.mode.bits.data_chain != 0) 5938c2ecf20Sopenharmony_ci ccw++; 5948c2ecf20Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 5958c2ecf20Sopenharmony_ci dst = page_address(bv.bv_page) + bv.bv_offset; 5968c2ecf20Sopenharmony_ci for (off = 0; off < bv.bv_len; off += blksize) { 5978c2ecf20Sopenharmony_ci /* Skip locate record. */ 5988c2ecf20Sopenharmony_ci if (private->rdc_data.mode.bits.data_chain == 0) 5998c2ecf20Sopenharmony_ci ccw++; 6008c2ecf20Sopenharmony_ci if (dst) { 6018c2ecf20Sopenharmony_ci if (ccw->flags & CCW_FLAG_IDA) 6028c2ecf20Sopenharmony_ci cda = *((char **)((addr_t) ccw->cda)); 6038c2ecf20Sopenharmony_ci else 6048c2ecf20Sopenharmony_ci cda = (char *)((addr_t) ccw->cda); 6058c2ecf20Sopenharmony_ci if (dst != cda) { 6068c2ecf20Sopenharmony_ci if (rq_data_dir(req) == READ) 6078c2ecf20Sopenharmony_ci memcpy(dst, cda, bv.bv_len); 6088c2ecf20Sopenharmony_ci kmem_cache_free(dasd_page_cache, 6098c2ecf20Sopenharmony_ci (void *)((addr_t)cda & PAGE_MASK)); 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci dst = NULL; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci ccw++; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ciout: 6178c2ecf20Sopenharmony_ci status = cqr->status == DASD_CQR_DONE; 6188c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 6198c2ecf20Sopenharmony_ci return status; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic void dasd_fba_handle_terminated_request(struct dasd_ccw_req *cqr) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci if (cqr->retries < 0) 6258c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FAILED; 6268c2ecf20Sopenharmony_ci else 6278c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 6288c2ecf20Sopenharmony_ci}; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic int 6318c2ecf20Sopenharmony_cidasd_fba_fill_info(struct dasd_device * device, 6328c2ecf20Sopenharmony_ci struct dasd_information2_t * info) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct dasd_fba_private *private = device->private; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci info->label_block = 1; 6378c2ecf20Sopenharmony_ci info->FBA_layout = 1; 6388c2ecf20Sopenharmony_ci info->format = DASD_FORMAT_LDL; 6398c2ecf20Sopenharmony_ci info->characteristics_size = sizeof(private->rdc_data); 6408c2ecf20Sopenharmony_ci memcpy(info->characteristics, &private->rdc_data, 6418c2ecf20Sopenharmony_ci sizeof(private->rdc_data)); 6428c2ecf20Sopenharmony_ci info->confdata_size = 0; 6438c2ecf20Sopenharmony_ci return 0; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic void 6478c2ecf20Sopenharmony_cidasd_fba_dump_sense_dbf(struct dasd_device *device, struct irb *irb, 6488c2ecf20Sopenharmony_ci char *reason) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci u64 *sense; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci sense = (u64 *) dasd_get_sense(irb); 6538c2ecf20Sopenharmony_ci if (sense) { 6548c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_EMERG, device, 6558c2ecf20Sopenharmony_ci "%s: %s %02x%02x%02x %016llx %016llx %016llx " 6568c2ecf20Sopenharmony_ci "%016llx", reason, 6578c2ecf20Sopenharmony_ci scsw_is_tm(&irb->scsw) ? "t" : "c", 6588c2ecf20Sopenharmony_ci scsw_cc(&irb->scsw), scsw_cstat(&irb->scsw), 6598c2ecf20Sopenharmony_ci scsw_dstat(&irb->scsw), sense[0], sense[1], 6608c2ecf20Sopenharmony_ci sense[2], sense[3]); 6618c2ecf20Sopenharmony_ci } else { 6628c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_EMERG, device, "%s", 6638c2ecf20Sopenharmony_ci "SORRY - NO VALID SENSE AVAILABLE\n"); 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic void 6698c2ecf20Sopenharmony_cidasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, 6708c2ecf20Sopenharmony_ci struct irb *irb) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci char *page; 6738c2ecf20Sopenharmony_ci struct ccw1 *act, *end, *last; 6748c2ecf20Sopenharmony_ci int len, sl, sct, count; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci page = (char *) get_zeroed_page(GFP_ATOMIC); 6778c2ecf20Sopenharmony_ci if (page == NULL) { 6788c2ecf20Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 6798c2ecf20Sopenharmony_ci "No memory to dump sense data"); 6808c2ecf20Sopenharmony_ci return; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci len = sprintf(page, PRINTK_HEADER 6838c2ecf20Sopenharmony_ci " I/O status report for device %s:\n", 6848c2ecf20Sopenharmony_ci dev_name(&device->cdev->dev)); 6858c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 6868c2ecf20Sopenharmony_ci " in req: %p CS: 0x%02X DS: 0x%02X\n", req, 6878c2ecf20Sopenharmony_ci irb->scsw.cmd.cstat, irb->scsw.cmd.dstat); 6888c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 6898c2ecf20Sopenharmony_ci " device %s: Failing CCW: %p\n", 6908c2ecf20Sopenharmony_ci dev_name(&device->cdev->dev), 6918c2ecf20Sopenharmony_ci (void *) (addr_t) irb->scsw.cmd.cpa); 6928c2ecf20Sopenharmony_ci if (irb->esw.esw0.erw.cons) { 6938c2ecf20Sopenharmony_ci for (sl = 0; sl < 4; sl++) { 6948c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 6958c2ecf20Sopenharmony_ci " Sense(hex) %2d-%2d:", 6968c2ecf20Sopenharmony_ci (8 * sl), ((8 * sl) + 7)); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci for (sct = 0; sct < 8; sct++) { 6998c2ecf20Sopenharmony_ci len += sprintf(page + len, " %02x", 7008c2ecf20Sopenharmony_ci irb->ecw[8 * sl + sct]); 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci len += sprintf(page + len, "\n"); 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci } else { 7058c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 7068c2ecf20Sopenharmony_ci " SORRY - NO VALID SENSE AVAILABLE\n"); 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci printk(KERN_ERR "%s", page); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci /* dump the Channel Program */ 7118c2ecf20Sopenharmony_ci /* print first CCWs (maximum 8) */ 7128c2ecf20Sopenharmony_ci act = req->cpaddr; 7138c2ecf20Sopenharmony_ci for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); 7148c2ecf20Sopenharmony_ci end = min(act + 8, last); 7158c2ecf20Sopenharmony_ci len = sprintf(page, PRINTK_HEADER " Related CP in req: %p\n", req); 7168c2ecf20Sopenharmony_ci while (act <= end) { 7178c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 7188c2ecf20Sopenharmony_ci " CCW %p: %08X %08X DAT:", 7198c2ecf20Sopenharmony_ci act, ((int *) act)[0], ((int *) act)[1]); 7208c2ecf20Sopenharmony_ci for (count = 0; count < 32 && count < act->count; 7218c2ecf20Sopenharmony_ci count += sizeof(int)) 7228c2ecf20Sopenharmony_ci len += sprintf(page + len, " %08X", 7238c2ecf20Sopenharmony_ci ((int *) (addr_t) act->cda) 7248c2ecf20Sopenharmony_ci [(count>>2)]); 7258c2ecf20Sopenharmony_ci len += sprintf(page + len, "\n"); 7268c2ecf20Sopenharmony_ci act++; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci printk(KERN_ERR "%s", page); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* print failing CCW area */ 7328c2ecf20Sopenharmony_ci len = 0; 7338c2ecf20Sopenharmony_ci if (act < ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2) { 7348c2ecf20Sopenharmony_ci act = ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2; 7358c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER "......\n"); 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci end = min((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa + 2, last); 7388c2ecf20Sopenharmony_ci while (act <= end) { 7398c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 7408c2ecf20Sopenharmony_ci " CCW %p: %08X %08X DAT:", 7418c2ecf20Sopenharmony_ci act, ((int *) act)[0], ((int *) act)[1]); 7428c2ecf20Sopenharmony_ci for (count = 0; count < 32 && count < act->count; 7438c2ecf20Sopenharmony_ci count += sizeof(int)) 7448c2ecf20Sopenharmony_ci len += sprintf(page + len, " %08X", 7458c2ecf20Sopenharmony_ci ((int *) (addr_t) act->cda) 7468c2ecf20Sopenharmony_ci [(count>>2)]); 7478c2ecf20Sopenharmony_ci len += sprintf(page + len, "\n"); 7488c2ecf20Sopenharmony_ci act++; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci /* print last CCWs */ 7528c2ecf20Sopenharmony_ci if (act < last - 2) { 7538c2ecf20Sopenharmony_ci act = last - 2; 7548c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER "......\n"); 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ci while (act <= last) { 7578c2ecf20Sopenharmony_ci len += sprintf(page + len, PRINTK_HEADER 7588c2ecf20Sopenharmony_ci " CCW %p: %08X %08X DAT:", 7598c2ecf20Sopenharmony_ci act, ((int *) act)[0], ((int *) act)[1]); 7608c2ecf20Sopenharmony_ci for (count = 0; count < 32 && count < act->count; 7618c2ecf20Sopenharmony_ci count += sizeof(int)) 7628c2ecf20Sopenharmony_ci len += sprintf(page + len, " %08X", 7638c2ecf20Sopenharmony_ci ((int *) (addr_t) act->cda) 7648c2ecf20Sopenharmony_ci [(count>>2)]); 7658c2ecf20Sopenharmony_ci len += sprintf(page + len, "\n"); 7668c2ecf20Sopenharmony_ci act++; 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci if (len > 0) 7698c2ecf20Sopenharmony_ci printk(KERN_ERR "%s", page); 7708c2ecf20Sopenharmony_ci free_page((unsigned long) page); 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci/* 7748c2ecf20Sopenharmony_ci * Initialize block layer request queue. 7758c2ecf20Sopenharmony_ci */ 7768c2ecf20Sopenharmony_cistatic void dasd_fba_setup_blk_queue(struct dasd_block *block) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci unsigned int logical_block_size = block->bp_block; 7798c2ecf20Sopenharmony_ci struct request_queue *q = block->request_queue; 7808c2ecf20Sopenharmony_ci unsigned int max_bytes, max_discard_sectors; 7818c2ecf20Sopenharmony_ci int max; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci max = DASD_FBA_MAX_BLOCKS << block->s2b_shift; 7848c2ecf20Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_NONROT, q); 7858c2ecf20Sopenharmony_ci q->limits.max_dev_sectors = max; 7868c2ecf20Sopenharmony_ci blk_queue_logical_block_size(q, logical_block_size); 7878c2ecf20Sopenharmony_ci blk_queue_max_hw_sectors(q, max); 7888c2ecf20Sopenharmony_ci blk_queue_max_segments(q, USHRT_MAX); 7898c2ecf20Sopenharmony_ci /* With page sized segments each segment can be translated into one idaw/tidaw */ 7908c2ecf20Sopenharmony_ci blk_queue_max_segment_size(q, PAGE_SIZE); 7918c2ecf20Sopenharmony_ci blk_queue_segment_boundary(q, PAGE_SIZE - 1); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci q->limits.discard_granularity = logical_block_size; 7948c2ecf20Sopenharmony_ci q->limits.discard_alignment = PAGE_SIZE; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* Calculate max_discard_sectors and make it PAGE aligned */ 7978c2ecf20Sopenharmony_ci max_bytes = USHRT_MAX * logical_block_size; 7988c2ecf20Sopenharmony_ci max_bytes = ALIGN_DOWN(max_bytes, PAGE_SIZE); 7998c2ecf20Sopenharmony_ci max_discard_sectors = max_bytes / logical_block_size; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci blk_queue_max_discard_sectors(q, max_discard_sectors); 8028c2ecf20Sopenharmony_ci blk_queue_max_write_zeroes_sectors(q, max_discard_sectors); 8038c2ecf20Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_DISCARD, q); 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cistatic int dasd_fba_pe_handler(struct dasd_device *device, __u8 tbvpm) 8078c2ecf20Sopenharmony_ci{ 8088c2ecf20Sopenharmony_ci return dasd_generic_verify_path(device, tbvpm); 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_cistatic struct dasd_discipline dasd_fba_discipline = { 8128c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 8138c2ecf20Sopenharmony_ci .name = "FBA ", 8148c2ecf20Sopenharmony_ci .ebcname = "FBA ", 8158c2ecf20Sopenharmony_ci .check_device = dasd_fba_check_characteristics, 8168c2ecf20Sopenharmony_ci .do_analysis = dasd_fba_do_analysis, 8178c2ecf20Sopenharmony_ci .pe_handler = dasd_fba_pe_handler, 8188c2ecf20Sopenharmony_ci .setup_blk_queue = dasd_fba_setup_blk_queue, 8198c2ecf20Sopenharmony_ci .fill_geometry = dasd_fba_fill_geometry, 8208c2ecf20Sopenharmony_ci .start_IO = dasd_start_IO, 8218c2ecf20Sopenharmony_ci .term_IO = dasd_term_IO, 8228c2ecf20Sopenharmony_ci .handle_terminated_request = dasd_fba_handle_terminated_request, 8238c2ecf20Sopenharmony_ci .erp_action = dasd_fba_erp_action, 8248c2ecf20Sopenharmony_ci .erp_postaction = dasd_fba_erp_postaction, 8258c2ecf20Sopenharmony_ci .check_for_device_change = dasd_fba_check_for_device_change, 8268c2ecf20Sopenharmony_ci .build_cp = dasd_fba_build_cp, 8278c2ecf20Sopenharmony_ci .free_cp = dasd_fba_free_cp, 8288c2ecf20Sopenharmony_ci .dump_sense = dasd_fba_dump_sense, 8298c2ecf20Sopenharmony_ci .dump_sense_dbf = dasd_fba_dump_sense_dbf, 8308c2ecf20Sopenharmony_ci .fill_info = dasd_fba_fill_info, 8318c2ecf20Sopenharmony_ci}; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic int __init 8348c2ecf20Sopenharmony_cidasd_fba_init(void) 8358c2ecf20Sopenharmony_ci{ 8368c2ecf20Sopenharmony_ci int ret; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci ASCEBC(dasd_fba_discipline.ebcname, 4); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci dasd_fba_zero_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); 8418c2ecf20Sopenharmony_ci if (!dasd_fba_zero_page) 8428c2ecf20Sopenharmony_ci return -ENOMEM; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci ret = ccw_driver_register(&dasd_fba_driver); 8458c2ecf20Sopenharmony_ci if (!ret) 8468c2ecf20Sopenharmony_ci wait_for_device_probe(); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci return ret; 8498c2ecf20Sopenharmony_ci} 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_cistatic void __exit 8528c2ecf20Sopenharmony_cidasd_fba_cleanup(void) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci ccw_driver_unregister(&dasd_fba_driver); 8558c2ecf20Sopenharmony_ci free_page((unsigned long)dasd_fba_zero_page); 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cimodule_init(dasd_fba_init); 8598c2ecf20Sopenharmony_cimodule_exit(dasd_fba_cleanup); 860