11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * File...........: linux/drivers/s390/block/dasd_fba.c 31da177e4SLinus Torvalds * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> 41da177e4SLinus Torvalds * Bugreports.to..: <Linux390@de.ibm.com> 5d41dd122SStefan Haberland * Copyright IBM Corp. 1999, 2009 61da177e4SLinus Torvalds */ 71da177e4SLinus Torvalds 8ca99dab0SStefan Haberland #define KMSG_COMPONENT "dasd-fba" 9fc19f381SStefan Haberland 101da177e4SLinus Torvalds #include <linux/stddef.h> 111da177e4SLinus Torvalds #include <linux/kernel.h> 121da177e4SLinus Torvalds #include <asm/debug.h> 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds #include <linux/slab.h> 151da177e4SLinus Torvalds #include <linux/hdreg.h> /* HDIO_GETGEO */ 161da177e4SLinus Torvalds #include <linux/bio.h> 171da177e4SLinus Torvalds #include <linux/module.h> 181da177e4SLinus Torvalds #include <linux/init.h> 191da177e4SLinus Torvalds 201da177e4SLinus Torvalds #include <asm/idals.h> 211da177e4SLinus Torvalds #include <asm/ebcdic.h> 221da177e4SLinus Torvalds #include <asm/io.h> 231da177e4SLinus Torvalds #include <asm/ccwdev.h> 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds #include "dasd_int.h" 261da177e4SLinus Torvalds #include "dasd_fba.h" 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds #ifdef PRINTK_HEADER 291da177e4SLinus Torvalds #undef PRINTK_HEADER 301da177e4SLinus Torvalds #endif /* PRINTK_HEADER */ 311da177e4SLinus Torvalds #define PRINTK_HEADER "dasd(fba):" 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds #define DASD_FBA_CCW_WRITE 0x41 341da177e4SLinus Torvalds #define DASD_FBA_CCW_READ 0x42 351da177e4SLinus Torvalds #define DASD_FBA_CCW_LOCATE 0x43 361da177e4SLinus Torvalds #define DASD_FBA_CCW_DEFINE_EXTENT 0x63 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds static struct dasd_discipline dasd_fba_discipline; 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds struct dasd_fba_private { 431da177e4SLinus Torvalds struct dasd_fba_characteristics rdc_data; 441da177e4SLinus Torvalds }; 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds static struct ccw_device_id dasd_fba_ids[] = { 47d2c993d8SHeiko Carstens { CCW_DEVICE_DEVTYPE (0x6310, 0, 0x9336, 0), .driver_info = 0x1}, 48d2c993d8SHeiko Carstens { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3370, 0), .driver_info = 0x2}, 491da177e4SLinus Torvalds { /* end of list */ }, 501da177e4SLinus Torvalds }; 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds MODULE_DEVICE_TABLE(ccw, dasd_fba_ids); 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds static struct ccw_driver dasd_fba_driver; /* see below */ 551da177e4SLinus Torvalds static int 561da177e4SLinus Torvalds dasd_fba_probe(struct ccw_device *cdev) 571da177e4SLinus Torvalds { 5840545573SHorst Hummel return dasd_generic_probe(cdev, &dasd_fba_discipline); 591da177e4SLinus Torvalds } 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds static int 621da177e4SLinus Torvalds dasd_fba_set_online(struct ccw_device *cdev) 631da177e4SLinus Torvalds { 641da177e4SLinus Torvalds return dasd_generic_set_online(cdev, &dasd_fba_discipline); 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds static struct ccw_driver dasd_fba_driver = { 683bda058bSSebastian Ott .driver = { 691da177e4SLinus Torvalds .name = "dasd-fba", 701da177e4SLinus Torvalds .owner = THIS_MODULE, 713bda058bSSebastian Ott }, 721da177e4SLinus Torvalds .ids = dasd_fba_ids, 731da177e4SLinus Torvalds .probe = dasd_fba_probe, 741da177e4SLinus Torvalds .remove = dasd_generic_remove, 751da177e4SLinus Torvalds .set_offline = dasd_generic_set_offline, 761da177e4SLinus Torvalds .set_online = dasd_fba_set_online, 771da177e4SLinus Torvalds .notify = dasd_generic_notify, 78a4d26c6aSStefan Weinhuber .path_event = dasd_generic_path_event, 79d41dd122SStefan Haberland .freeze = dasd_generic_pm_freeze, 80d41dd122SStefan Haberland .thaw = dasd_generic_restore_device, 81d41dd122SStefan Haberland .restore = dasd_generic_restore_device, 821da177e4SLinus Torvalds }; 831da177e4SLinus Torvalds 844d284cacSHeiko Carstens static void 851da177e4SLinus Torvalds define_extent(struct ccw1 * ccw, struct DE_fba_data *data, int rw, 861da177e4SLinus Torvalds int blksize, int beg, int nr) 871da177e4SLinus Torvalds { 881da177e4SLinus Torvalds ccw->cmd_code = DASD_FBA_CCW_DEFINE_EXTENT; 891da177e4SLinus Torvalds ccw->flags = 0; 901da177e4SLinus Torvalds ccw->count = 16; 911da177e4SLinus Torvalds ccw->cda = (__u32) __pa(data); 921da177e4SLinus Torvalds memset(data, 0, sizeof (struct DE_fba_data)); 931da177e4SLinus Torvalds if (rw == WRITE) 941da177e4SLinus Torvalds (data->mask).perm = 0x0; 951da177e4SLinus Torvalds else if (rw == READ) 961da177e4SLinus Torvalds (data->mask).perm = 0x1; 971da177e4SLinus Torvalds else 981da177e4SLinus Torvalds data->mask.perm = 0x2; 991da177e4SLinus Torvalds data->blk_size = blksize; 1001da177e4SLinus Torvalds data->ext_loc = beg; 1011da177e4SLinus Torvalds data->ext_end = nr - 1; 1021da177e4SLinus Torvalds } 1031da177e4SLinus Torvalds 1044d284cacSHeiko Carstens static void 1051da177e4SLinus Torvalds locate_record(struct ccw1 * ccw, struct LO_fba_data *data, int rw, 1061da177e4SLinus Torvalds int block_nr, int block_ct) 1071da177e4SLinus Torvalds { 1081da177e4SLinus Torvalds ccw->cmd_code = DASD_FBA_CCW_LOCATE; 1091da177e4SLinus Torvalds ccw->flags = 0; 1101da177e4SLinus Torvalds ccw->count = 8; 1111da177e4SLinus Torvalds ccw->cda = (__u32) __pa(data); 1121da177e4SLinus Torvalds memset(data, 0, sizeof (struct LO_fba_data)); 1131da177e4SLinus Torvalds if (rw == WRITE) 1141da177e4SLinus Torvalds data->operation.cmd = 0x5; 1151da177e4SLinus Torvalds else if (rw == READ) 1161da177e4SLinus Torvalds data->operation.cmd = 0x6; 1171da177e4SLinus Torvalds else 1181da177e4SLinus Torvalds data->operation.cmd = 0x8; 1191da177e4SLinus Torvalds data->blk_nr = block_nr; 1201da177e4SLinus Torvalds data->blk_ct = block_ct; 1211da177e4SLinus Torvalds } 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds static int 1241da177e4SLinus Torvalds dasd_fba_check_characteristics(struct dasd_device *device) 1251da177e4SLinus Torvalds { 1268e09f215SStefan Weinhuber struct dasd_block *block; 1271da177e4SLinus Torvalds struct dasd_fba_private *private; 1281da177e4SLinus Torvalds struct ccw_device *cdev = device->cdev; 1291da177e4SLinus Torvalds int rc; 13033b62a30SStefan Weinhuber int readonly; 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds private = (struct dasd_fba_private *) device->private; 13392636b15SSebastian Ott if (!private) { 13492636b15SSebastian Ott private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA); 13592636b15SSebastian Ott if (!private) { 136fc19f381SStefan Haberland dev_warn(&device->cdev->dev, 137fc19f381SStefan Haberland "Allocating memory for private DASD " 138fc19f381SStefan Haberland "data failed\n"); 1391da177e4SLinus Torvalds return -ENOMEM; 1401da177e4SLinus Torvalds } 1411da177e4SLinus Torvalds device->private = (void *) private; 14292636b15SSebastian Ott } else { 14392636b15SSebastian Ott memset(private, 0, sizeof(*private)); 1441da177e4SLinus Torvalds } 1458e09f215SStefan Weinhuber block = dasd_alloc_block(); 1468e09f215SStefan Weinhuber if (IS_ERR(block)) { 147b8ed5dd5SStefan Haberland DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", "could not allocate " 148b8ed5dd5SStefan Haberland "dasd block structure"); 1497337194fSCornelia Huck device->private = NULL; 1507337194fSCornelia Huck kfree(private); 1518e09f215SStefan Weinhuber return PTR_ERR(block); 1528e09f215SStefan Weinhuber } 1538e09f215SStefan Weinhuber device->block = block; 1548e09f215SStefan Weinhuber block->base = device; 1558e09f215SStefan Weinhuber 1561da177e4SLinus Torvalds /* Read Device Characteristics */ 15768b781feSStefan Haberland rc = dasd_generic_read_dev_chars(device, DASD_FBA_MAGIC, 15868b781feSStefan Haberland &private->rdc_data, 32); 1591da177e4SLinus Torvalds if (rc) { 160b8ed5dd5SStefan Haberland DBF_EVENT_DEVID(DBF_WARNING, cdev, "Read device " 161b8ed5dd5SStefan Haberland "characteristics returned error %d", rc); 1627337194fSCornelia Huck device->block = NULL; 1637337194fSCornelia Huck dasd_free_block(block); 1647337194fSCornelia Huck device->private = NULL; 1657337194fSCornelia Huck kfree(private); 1661da177e4SLinus Torvalds return rc; 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds 1697c8faa86SStefan Haberland device->default_expires = DASD_EXPIRES; 170a4d26c6aSStefan Weinhuber device->path_data.opm = LPM_ANYPATH; 1717c8faa86SStefan Haberland 17233b62a30SStefan Weinhuber readonly = dasd_device_is_ro(device); 17333b62a30SStefan Weinhuber if (readonly) 17433b62a30SStefan Weinhuber set_bit(DASD_FLAG_DEVICE_RO, &device->flags); 17533b62a30SStefan Weinhuber 176fc19f381SStefan Haberland dev_info(&device->cdev->dev, 177fc19f381SStefan Haberland "New FBA DASD %04X/%02X (CU %04X/%02X) with %d MB " 17833b62a30SStefan Weinhuber "and %d B/blk%s\n", 1791da177e4SLinus Torvalds cdev->id.dev_type, 1801da177e4SLinus Torvalds cdev->id.dev_model, 1811da177e4SLinus Torvalds cdev->id.cu_type, 1821da177e4SLinus Torvalds cdev->id.cu_model, 1831da177e4SLinus Torvalds ((private->rdc_data.blk_bdsa * 1841da177e4SLinus Torvalds (private->rdc_data.blk_size >> 9)) >> 11), 18533b62a30SStefan Weinhuber private->rdc_data.blk_size, 18633b62a30SStefan Weinhuber readonly ? ", read-only device" : ""); 1871da177e4SLinus Torvalds return 0; 1881da177e4SLinus Torvalds } 1891da177e4SLinus Torvalds 1908e09f215SStefan Weinhuber static int dasd_fba_do_analysis(struct dasd_block *block) 1911da177e4SLinus Torvalds { 1921da177e4SLinus Torvalds struct dasd_fba_private *private; 1931da177e4SLinus Torvalds int sb, rc; 1941da177e4SLinus Torvalds 1958e09f215SStefan Weinhuber private = (struct dasd_fba_private *) block->base->private; 1961da177e4SLinus Torvalds rc = dasd_check_blocksize(private->rdc_data.blk_size); 1971da177e4SLinus Torvalds if (rc) { 198fc19f381SStefan Haberland DBF_DEV_EVENT(DBF_WARNING, block->base, "unknown blocksize %d", 1991da177e4SLinus Torvalds private->rdc_data.blk_size); 2001da177e4SLinus Torvalds return rc; 2011da177e4SLinus Torvalds } 2028e09f215SStefan Weinhuber block->blocks = private->rdc_data.blk_bdsa; 2038e09f215SStefan Weinhuber block->bp_block = private->rdc_data.blk_size; 2048e09f215SStefan Weinhuber block->s2b_shift = 0; /* bits to shift 512 to get a block */ 2051da177e4SLinus Torvalds for (sb = 512; sb < private->rdc_data.blk_size; sb = sb << 1) 2068e09f215SStefan Weinhuber block->s2b_shift++; 2071da177e4SLinus Torvalds return 0; 2081da177e4SLinus Torvalds } 2091da177e4SLinus Torvalds 2108e09f215SStefan Weinhuber static int dasd_fba_fill_geometry(struct dasd_block *block, 2118e09f215SStefan Weinhuber struct hd_geometry *geo) 2121da177e4SLinus Torvalds { 2138e09f215SStefan Weinhuber if (dasd_check_blocksize(block->bp_block) != 0) 2141da177e4SLinus Torvalds return -EINVAL; 2158e09f215SStefan Weinhuber geo->cylinders = (block->blocks << block->s2b_shift) >> 10; 2161da177e4SLinus Torvalds geo->heads = 16; 2178e09f215SStefan Weinhuber geo->sectors = 128 >> block->s2b_shift; 2181da177e4SLinus Torvalds return 0; 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds static dasd_erp_fn_t 2221da177e4SLinus Torvalds dasd_fba_erp_action(struct dasd_ccw_req * cqr) 2231da177e4SLinus Torvalds { 2241da177e4SLinus Torvalds return dasd_default_erp_action; 2251da177e4SLinus Torvalds } 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds static dasd_erp_fn_t 2281da177e4SLinus Torvalds dasd_fba_erp_postaction(struct dasd_ccw_req * cqr) 2291da177e4SLinus Torvalds { 2301da177e4SLinus Torvalds if (cqr->function == dasd_default_erp_action) 2311da177e4SLinus Torvalds return dasd_default_erp_postaction; 2321da177e4SLinus Torvalds 233fc19f381SStefan Haberland DBF_DEV_EVENT(DBF_WARNING, cqr->startdev, "unknown ERP action %p", 2341da177e4SLinus Torvalds cqr->function); 2351da177e4SLinus Torvalds return NULL; 2361da177e4SLinus Torvalds } 2371da177e4SLinus Torvalds 2385a27e60dSStefan Weinhuber static void dasd_fba_check_for_device_change(struct dasd_device *device, 2395a27e60dSStefan Weinhuber struct dasd_ccw_req *cqr, 2408e09f215SStefan Weinhuber struct irb *irb) 2418e09f215SStefan Weinhuber { 2428e09f215SStefan Weinhuber char mask; 2438e09f215SStefan Weinhuber 2448e09f215SStefan Weinhuber /* first of all check for state change pending interrupt */ 2458e09f215SStefan Weinhuber mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; 2465a27e60dSStefan Weinhuber if ((irb->scsw.cmd.dstat & mask) == mask) 2478e09f215SStefan Weinhuber dasd_generic_handle_state_change(device); 2488e09f215SStefan Weinhuber }; 2498e09f215SStefan Weinhuber 2508e09f215SStefan Weinhuber static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, 2518e09f215SStefan Weinhuber struct dasd_block *block, 2528e09f215SStefan Weinhuber struct request *req) 2531da177e4SLinus Torvalds { 2541da177e4SLinus Torvalds struct dasd_fba_private *private; 2551da177e4SLinus Torvalds unsigned long *idaws; 2561da177e4SLinus Torvalds struct LO_fba_data *LO_data; 2571da177e4SLinus Torvalds struct dasd_ccw_req *cqr; 2581da177e4SLinus Torvalds struct ccw1 *ccw; 2595705f702SNeilBrown struct req_iterator iter; 2601da177e4SLinus Torvalds struct bio_vec *bv; 2611da177e4SLinus Torvalds char *dst; 2621da177e4SLinus Torvalds int count, cidaw, cplength, datasize; 2631da177e4SLinus Torvalds sector_t recid, first_rec, last_rec; 2641da177e4SLinus Torvalds unsigned int blksize, off; 2651da177e4SLinus Torvalds unsigned char cmd; 2661da177e4SLinus Torvalds 2678e09f215SStefan Weinhuber private = (struct dasd_fba_private *) block->base->private; 2681da177e4SLinus Torvalds if (rq_data_dir(req) == READ) { 2691da177e4SLinus Torvalds cmd = DASD_FBA_CCW_READ; 2701da177e4SLinus Torvalds } else if (rq_data_dir(req) == WRITE) { 2711da177e4SLinus Torvalds cmd = DASD_FBA_CCW_WRITE; 2721da177e4SLinus Torvalds } else 2731da177e4SLinus Torvalds return ERR_PTR(-EINVAL); 2748e09f215SStefan Weinhuber blksize = block->bp_block; 2751da177e4SLinus Torvalds /* Calculate record id of first and last block. */ 27683096ebfSTejun Heo first_rec = blk_rq_pos(req) >> block->s2b_shift; 27783096ebfSTejun Heo last_rec = 27883096ebfSTejun Heo (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift; 2791da177e4SLinus Torvalds /* Check struct bio and count the number of blocks for the request. */ 2801da177e4SLinus Torvalds count = 0; 2811da177e4SLinus Torvalds cidaw = 0; 2825705f702SNeilBrown rq_for_each_segment(bv, req, iter) { 2831da177e4SLinus Torvalds if (bv->bv_len & (blksize - 1)) 2841da177e4SLinus Torvalds /* Fba can only do full blocks. */ 2851da177e4SLinus Torvalds return ERR_PTR(-EINVAL); 2868e09f215SStefan Weinhuber count += bv->bv_len >> (block->s2b_shift + 9); 287347a8dc3SMartin Schwidefsky #if defined(CONFIG_64BIT) 2886c92e699SJens Axboe if (idal_is_needed (page_address(bv->bv_page), bv->bv_len)) 2891da177e4SLinus Torvalds cidaw += bv->bv_len / blksize; 2901da177e4SLinus Torvalds #endif 2911da177e4SLinus Torvalds } 2921da177e4SLinus Torvalds /* Paranoia. */ 2931da177e4SLinus Torvalds if (count != last_rec - first_rec + 1) 2941da177e4SLinus Torvalds return ERR_PTR(-EINVAL); 2951da177e4SLinus Torvalds /* 1x define extent + 1x locate record + number of blocks */ 2961da177e4SLinus Torvalds cplength = 2 + count; 2971da177e4SLinus Torvalds /* 1x define extent + 1x locate record */ 2981da177e4SLinus Torvalds datasize = sizeof(struct DE_fba_data) + sizeof(struct LO_fba_data) + 2991da177e4SLinus Torvalds cidaw * sizeof(unsigned long); 3001da177e4SLinus Torvalds /* 3011da177e4SLinus Torvalds * Find out number of additional locate record ccws if the device 3021da177e4SLinus Torvalds * can't do data chaining. 3031da177e4SLinus Torvalds */ 3041da177e4SLinus Torvalds if (private->rdc_data.mode.bits.data_chain == 0) { 3051da177e4SLinus Torvalds cplength += count - 1; 3061da177e4SLinus Torvalds datasize += (count - 1)*sizeof(struct LO_fba_data); 3071da177e4SLinus Torvalds } 3081da177e4SLinus Torvalds /* Allocate the ccw request. */ 30968b781feSStefan Haberland cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev); 3101da177e4SLinus Torvalds if (IS_ERR(cqr)) 3111da177e4SLinus Torvalds return cqr; 3121da177e4SLinus Torvalds ccw = cqr->cpaddr; 3131da177e4SLinus Torvalds /* First ccw is define extent. */ 3141da177e4SLinus Torvalds define_extent(ccw++, cqr->data, rq_data_dir(req), 31583096ebfSTejun Heo block->bp_block, blk_rq_pos(req), blk_rq_sectors(req)); 3161da177e4SLinus Torvalds /* Build locate_record + read/write ccws. */ 3171da177e4SLinus Torvalds idaws = (unsigned long *) (cqr->data + sizeof(struct DE_fba_data)); 3181da177e4SLinus Torvalds LO_data = (struct LO_fba_data *) (idaws + cidaw); 3191da177e4SLinus Torvalds /* Locate record for all blocks for smart devices. */ 3201da177e4SLinus Torvalds if (private->rdc_data.mode.bits.data_chain != 0) { 3211da177e4SLinus Torvalds ccw[-1].flags |= CCW_FLAG_CC; 3221da177e4SLinus Torvalds locate_record(ccw++, LO_data++, rq_data_dir(req), 0, count); 3231da177e4SLinus Torvalds } 3241da177e4SLinus Torvalds recid = first_rec; 3255705f702SNeilBrown rq_for_each_segment(bv, req, iter) { 3261da177e4SLinus Torvalds dst = page_address(bv->bv_page) + bv->bv_offset; 3271da177e4SLinus Torvalds if (dasd_page_cache) { 3281da177e4SLinus Torvalds char *copy = kmem_cache_alloc(dasd_page_cache, 329441e143eSChristoph Lameter GFP_DMA | __GFP_NOWARN); 3301da177e4SLinus Torvalds if (copy && rq_data_dir(req) == WRITE) 3311da177e4SLinus Torvalds memcpy(copy + bv->bv_offset, dst, bv->bv_len); 3321da177e4SLinus Torvalds if (copy) 3331da177e4SLinus Torvalds dst = copy + bv->bv_offset; 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds for (off = 0; off < bv->bv_len; off += blksize) { 3361da177e4SLinus Torvalds /* Locate record for stupid devices. */ 3371da177e4SLinus Torvalds if (private->rdc_data.mode.bits.data_chain == 0) { 3381da177e4SLinus Torvalds ccw[-1].flags |= CCW_FLAG_CC; 3391da177e4SLinus Torvalds locate_record(ccw, LO_data++, 3401da177e4SLinus Torvalds rq_data_dir(req), 3411da177e4SLinus Torvalds recid - first_rec, 1); 3421da177e4SLinus Torvalds ccw->flags = CCW_FLAG_CC; 3431da177e4SLinus Torvalds ccw++; 3441da177e4SLinus Torvalds } else { 3451da177e4SLinus Torvalds if (recid > first_rec) 3461da177e4SLinus Torvalds ccw[-1].flags |= CCW_FLAG_DC; 3471da177e4SLinus Torvalds else 3481da177e4SLinus Torvalds ccw[-1].flags |= CCW_FLAG_CC; 3491da177e4SLinus Torvalds } 3501da177e4SLinus Torvalds ccw->cmd_code = cmd; 3518e09f215SStefan Weinhuber ccw->count = block->bp_block; 3521da177e4SLinus Torvalds if (idal_is_needed(dst, blksize)) { 3531da177e4SLinus Torvalds ccw->cda = (__u32)(addr_t) idaws; 3541da177e4SLinus Torvalds ccw->flags = CCW_FLAG_IDA; 3551da177e4SLinus Torvalds idaws = idal_create_words(idaws, dst, blksize); 3561da177e4SLinus Torvalds } else { 3571da177e4SLinus Torvalds ccw->cda = (__u32)(addr_t) dst; 3581da177e4SLinus Torvalds ccw->flags = 0; 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds ccw++; 3611da177e4SLinus Torvalds dst += blksize; 3621da177e4SLinus Torvalds recid++; 3631da177e4SLinus Torvalds } 3641da177e4SLinus Torvalds } 36513de227bSHolger Smolinski if (blk_noretry_request(req) || 36613de227bSHolger Smolinski block->base->features & DASD_FEATURE_FAILFAST) 3671c01b8a5SHorst Hummel set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 3688e09f215SStefan Weinhuber cqr->startdev = memdev; 3698e09f215SStefan Weinhuber cqr->memdev = memdev; 3708e09f215SStefan Weinhuber cqr->block = block; 3717c8faa86SStefan Haberland cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */ 372d61f6f3dSHorst Hummel cqr->retries = 32; 373d61f6f3dSHorst Hummel cqr->buildclk = get_clock(); 3741da177e4SLinus Torvalds cqr->status = DASD_CQR_FILLED; 3751da177e4SLinus Torvalds return cqr; 3761da177e4SLinus Torvalds } 3771da177e4SLinus Torvalds 3781da177e4SLinus Torvalds static int 3791da177e4SLinus Torvalds dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) 3801da177e4SLinus Torvalds { 3811da177e4SLinus Torvalds struct dasd_fba_private *private; 3821da177e4SLinus Torvalds struct ccw1 *ccw; 3835705f702SNeilBrown struct req_iterator iter; 3841da177e4SLinus Torvalds struct bio_vec *bv; 3851da177e4SLinus Torvalds char *dst, *cda; 3861da177e4SLinus Torvalds unsigned int blksize, off; 3875705f702SNeilBrown int status; 3881da177e4SLinus Torvalds 3891da177e4SLinus Torvalds if (!dasd_page_cache) 3901da177e4SLinus Torvalds goto out; 3918e09f215SStefan Weinhuber private = (struct dasd_fba_private *) cqr->block->base->private; 3928e09f215SStefan Weinhuber blksize = cqr->block->bp_block; 3931da177e4SLinus Torvalds ccw = cqr->cpaddr; 3941da177e4SLinus Torvalds /* Skip over define extent & locate record. */ 3951da177e4SLinus Torvalds ccw++; 3961da177e4SLinus Torvalds if (private->rdc_data.mode.bits.data_chain != 0) 3971da177e4SLinus Torvalds ccw++; 3985705f702SNeilBrown rq_for_each_segment(bv, req, iter) { 3991da177e4SLinus Torvalds dst = page_address(bv->bv_page) + bv->bv_offset; 4001da177e4SLinus Torvalds for (off = 0; off < bv->bv_len; off += blksize) { 4011da177e4SLinus Torvalds /* Skip locate record. */ 4021da177e4SLinus Torvalds if (private->rdc_data.mode.bits.data_chain == 0) 4031da177e4SLinus Torvalds ccw++; 4041da177e4SLinus Torvalds if (dst) { 4051da177e4SLinus Torvalds if (ccw->flags & CCW_FLAG_IDA) 4061da177e4SLinus Torvalds cda = *((char **)((addr_t) ccw->cda)); 4071da177e4SLinus Torvalds else 4081da177e4SLinus Torvalds cda = (char *)((addr_t) ccw->cda); 4091da177e4SLinus Torvalds if (dst != cda) { 4101da177e4SLinus Torvalds if (rq_data_dir(req) == READ) 4111da177e4SLinus Torvalds memcpy(dst, cda, bv->bv_len); 4121da177e4SLinus Torvalds kmem_cache_free(dasd_page_cache, 4131da177e4SLinus Torvalds (void *)((addr_t)cda & PAGE_MASK)); 4141da177e4SLinus Torvalds } 4151da177e4SLinus Torvalds dst = NULL; 4161da177e4SLinus Torvalds } 4171da177e4SLinus Torvalds ccw++; 4181da177e4SLinus Torvalds } 4191da177e4SLinus Torvalds } 4201da177e4SLinus Torvalds out: 4211da177e4SLinus Torvalds status = cqr->status == DASD_CQR_DONE; 4228e09f215SStefan Weinhuber dasd_sfree_request(cqr, cqr->memdev); 4231da177e4SLinus Torvalds return status; 4241da177e4SLinus Torvalds } 4251da177e4SLinus Torvalds 4268e09f215SStefan Weinhuber static void dasd_fba_handle_terminated_request(struct dasd_ccw_req *cqr) 4278e09f215SStefan Weinhuber { 4288e09f215SStefan Weinhuber cqr->status = DASD_CQR_FILLED; 4298e09f215SStefan Weinhuber }; 4308e09f215SStefan Weinhuber 4311da177e4SLinus Torvalds static int 4321da177e4SLinus Torvalds dasd_fba_fill_info(struct dasd_device * device, 4331da177e4SLinus Torvalds struct dasd_information2_t * info) 4341da177e4SLinus Torvalds { 4351da177e4SLinus Torvalds info->label_block = 1; 4361da177e4SLinus Torvalds info->FBA_layout = 1; 4371da177e4SLinus Torvalds info->format = DASD_FORMAT_LDL; 4381da177e4SLinus Torvalds info->characteristics_size = sizeof(struct dasd_fba_characteristics); 4391da177e4SLinus Torvalds memcpy(info->characteristics, 4401da177e4SLinus Torvalds &((struct dasd_fba_private *) device->private)->rdc_data, 4411da177e4SLinus Torvalds sizeof (struct dasd_fba_characteristics)); 4421da177e4SLinus Torvalds info->confdata_size = 0; 4431da177e4SLinus Torvalds return 0; 4441da177e4SLinus Torvalds } 4451da177e4SLinus Torvalds 4461da177e4SLinus Torvalds static void 447aeec92caSStefan Haberland dasd_fba_dump_sense_dbf(struct dasd_device *device, struct irb *irb, 448aeec92caSStefan Haberland char *reason) 449fc19f381SStefan Haberland { 450aeec92caSStefan Haberland u64 *sense; 451aeec92caSStefan Haberland 452aeec92caSStefan Haberland sense = (u64 *) dasd_get_sense(irb); 453aeec92caSStefan Haberland if (sense) { 454fc19f381SStefan Haberland DBF_DEV_EVENT(DBF_EMERG, device, 455aeec92caSStefan Haberland "%s: %s %02x%02x%02x %016llx %016llx %016llx " 456aeec92caSStefan Haberland "%016llx", reason, 457aeec92caSStefan Haberland scsw_is_tm(&irb->scsw) ? "t" : "c", 458aeec92caSStefan Haberland scsw_cc(&irb->scsw), scsw_cstat(&irb->scsw), 459aeec92caSStefan Haberland scsw_dstat(&irb->scsw), sense[0], sense[1], 460aeec92caSStefan Haberland sense[2], sense[3]); 461fc19f381SStefan Haberland } else { 462fc19f381SStefan Haberland DBF_DEV_EVENT(DBF_EMERG, device, "%s", 463fc19f381SStefan Haberland "SORRY - NO VALID SENSE AVAILABLE\n"); 464fc19f381SStefan Haberland } 465fc19f381SStefan Haberland } 466fc19f381SStefan Haberland 467fc19f381SStefan Haberland 468fc19f381SStefan Haberland static void 4691da177e4SLinus Torvalds dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, 4701da177e4SLinus Torvalds struct irb *irb) 4711da177e4SLinus Torvalds { 4721da177e4SLinus Torvalds char *page; 4731da177e4SLinus Torvalds struct ccw1 *act, *end, *last; 4741da177e4SLinus Torvalds int len, sl, sct, count; 4751da177e4SLinus Torvalds 4761da177e4SLinus Torvalds page = (char *) get_zeroed_page(GFP_ATOMIC); 4771da177e4SLinus Torvalds if (page == NULL) { 478fc19f381SStefan Haberland DBF_DEV_EVENT(DBF_WARNING, device, "%s", 4791da177e4SLinus Torvalds "No memory to dump sense data"); 4801da177e4SLinus Torvalds return; 4811da177e4SLinus Torvalds } 4821da177e4SLinus Torvalds len = sprintf(page, KERN_ERR PRINTK_HEADER 4831da177e4SLinus Torvalds " I/O status report for device %s:\n", 4842a0217d5SKay Sievers dev_name(&device->cdev->dev)); 4851da177e4SLinus Torvalds len += sprintf(page + len, KERN_ERR PRINTK_HEADER 4861da177e4SLinus Torvalds " in req: %p CS: 0x%02X DS: 0x%02X\n", req, 48723d805b6SPeter Oberparleiter irb->scsw.cmd.cstat, irb->scsw.cmd.dstat); 4881da177e4SLinus Torvalds len += sprintf(page + len, KERN_ERR PRINTK_HEADER 4891da177e4SLinus Torvalds " device %s: Failing CCW: %p\n", 4902a0217d5SKay Sievers dev_name(&device->cdev->dev), 49123d805b6SPeter Oberparleiter (void *) (addr_t) irb->scsw.cmd.cpa); 4921da177e4SLinus Torvalds if (irb->esw.esw0.erw.cons) { 4931da177e4SLinus Torvalds for (sl = 0; sl < 4; sl++) { 4941da177e4SLinus Torvalds len += sprintf(page + len, KERN_ERR PRINTK_HEADER 4951da177e4SLinus Torvalds " Sense(hex) %2d-%2d:", 4961da177e4SLinus Torvalds (8 * sl), ((8 * sl) + 7)); 4971da177e4SLinus Torvalds 4981da177e4SLinus Torvalds for (sct = 0; sct < 8; sct++) { 4991da177e4SLinus Torvalds len += sprintf(page + len, " %02x", 5001da177e4SLinus Torvalds irb->ecw[8 * sl + sct]); 5011da177e4SLinus Torvalds } 5021da177e4SLinus Torvalds len += sprintf(page + len, "\n"); 5031da177e4SLinus Torvalds } 5041da177e4SLinus Torvalds } else { 5051da177e4SLinus Torvalds len += sprintf(page + len, KERN_ERR PRINTK_HEADER 5061da177e4SLinus Torvalds " SORRY - NO VALID SENSE AVAILABLE\n"); 5071da177e4SLinus Torvalds } 508fc19f381SStefan Haberland printk(KERN_ERR "%s", page); 5091da177e4SLinus Torvalds 5101da177e4SLinus Torvalds /* dump the Channel Program */ 5111da177e4SLinus Torvalds /* print first CCWs (maximum 8) */ 5121da177e4SLinus Torvalds act = req->cpaddr; 5131da177e4SLinus Torvalds for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); 5141da177e4SLinus Torvalds end = min(act + 8, last); 5151da177e4SLinus Torvalds len = sprintf(page, KERN_ERR PRINTK_HEADER 5161da177e4SLinus Torvalds " Related CP in req: %p\n", req); 5171da177e4SLinus Torvalds while (act <= end) { 5181da177e4SLinus Torvalds len += sprintf(page + len, KERN_ERR PRINTK_HEADER 5191da177e4SLinus Torvalds " CCW %p: %08X %08X DAT:", 5201da177e4SLinus Torvalds act, ((int *) act)[0], ((int *) act)[1]); 5211da177e4SLinus Torvalds for (count = 0; count < 32 && count < act->count; 5221da177e4SLinus Torvalds count += sizeof(int)) 5231da177e4SLinus Torvalds len += sprintf(page + len, " %08X", 5241da177e4SLinus Torvalds ((int *) (addr_t) act->cda) 5251da177e4SLinus Torvalds [(count>>2)]); 5261da177e4SLinus Torvalds len += sprintf(page + len, "\n"); 5271da177e4SLinus Torvalds act++; 5281da177e4SLinus Torvalds } 529fc19f381SStefan Haberland printk(KERN_ERR "%s", page); 5301da177e4SLinus Torvalds 5311da177e4SLinus Torvalds 5321da177e4SLinus Torvalds /* print failing CCW area */ 5331da177e4SLinus Torvalds len = 0; 53423d805b6SPeter Oberparleiter if (act < ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2) { 53523d805b6SPeter Oberparleiter act = ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2; 5361da177e4SLinus Torvalds len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); 5371da177e4SLinus Torvalds } 53823d805b6SPeter Oberparleiter end = min((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa + 2, last); 5391da177e4SLinus Torvalds while (act <= end) { 5401da177e4SLinus Torvalds len += sprintf(page + len, KERN_ERR PRINTK_HEADER 5411da177e4SLinus Torvalds " CCW %p: %08X %08X DAT:", 5421da177e4SLinus Torvalds act, ((int *) act)[0], ((int *) act)[1]); 5431da177e4SLinus Torvalds for (count = 0; count < 32 && count < act->count; 5441da177e4SLinus Torvalds count += sizeof(int)) 5451da177e4SLinus Torvalds len += sprintf(page + len, " %08X", 5461da177e4SLinus Torvalds ((int *) (addr_t) act->cda) 5471da177e4SLinus Torvalds [(count>>2)]); 5481da177e4SLinus Torvalds len += sprintf(page + len, "\n"); 5491da177e4SLinus Torvalds act++; 5501da177e4SLinus Torvalds } 5511da177e4SLinus Torvalds 5521da177e4SLinus Torvalds /* print last CCWs */ 5531da177e4SLinus Torvalds if (act < last - 2) { 5541da177e4SLinus Torvalds act = last - 2; 5551da177e4SLinus Torvalds len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); 5561da177e4SLinus Torvalds } 5571da177e4SLinus Torvalds while (act <= last) { 5581da177e4SLinus Torvalds len += sprintf(page + len, KERN_ERR PRINTK_HEADER 5591da177e4SLinus Torvalds " CCW %p: %08X %08X DAT:", 5601da177e4SLinus Torvalds act, ((int *) act)[0], ((int *) act)[1]); 5611da177e4SLinus Torvalds for (count = 0; count < 32 && count < act->count; 5621da177e4SLinus Torvalds count += sizeof(int)) 5631da177e4SLinus Torvalds len += sprintf(page + len, " %08X", 5641da177e4SLinus Torvalds ((int *) (addr_t) act->cda) 5651da177e4SLinus Torvalds [(count>>2)]); 5661da177e4SLinus Torvalds len += sprintf(page + len, "\n"); 5671da177e4SLinus Torvalds act++; 5681da177e4SLinus Torvalds } 5691da177e4SLinus Torvalds if (len > 0) 570fc19f381SStefan Haberland printk(KERN_ERR "%s", page); 5711da177e4SLinus Torvalds free_page((unsigned long) page); 5721da177e4SLinus Torvalds } 5731da177e4SLinus Torvalds 5741da177e4SLinus Torvalds /* 5751da177e4SLinus Torvalds * max_blocks is dependent on the amount of storage that is available 5761da177e4SLinus Torvalds * in the static io buffer for each device. Currently each device has 5771da177e4SLinus Torvalds * 8192 bytes (=2 pages). For 64 bit one dasd_mchunkt_t structure has 5781da177e4SLinus Torvalds * 24 bytes, the struct dasd_ccw_req has 136 bytes and each block can use 5791da177e4SLinus Torvalds * up to 16 bytes (8 for the ccw and 8 for the idal pointer). In 5801da177e4SLinus Torvalds * addition we have one define extent ccw + 16 bytes of data and a 5811da177e4SLinus Torvalds * locate record ccw for each block (stupid devices!) + 16 bytes of data. 5821da177e4SLinus Torvalds * That makes: 5831da177e4SLinus Torvalds * (8192 - 24 - 136 - 8 - 16) / 40 = 200.2 blocks at maximum. 5841da177e4SLinus Torvalds * We want to fit two into the available memory so that we can immediately 5851da177e4SLinus Torvalds * start the next request if one finishes off. That makes 100.1 blocks 5861da177e4SLinus Torvalds * for one request. Give a little safety and the result is 96. 5871da177e4SLinus Torvalds */ 5881da177e4SLinus Torvalds static struct dasd_discipline dasd_fba_discipline = { 5891da177e4SLinus Torvalds .owner = THIS_MODULE, 5901da177e4SLinus Torvalds .name = "FBA ", 5911da177e4SLinus Torvalds .ebcname = "FBA ", 5921da177e4SLinus Torvalds .max_blocks = 96, 5931da177e4SLinus Torvalds .check_device = dasd_fba_check_characteristics, 5941da177e4SLinus Torvalds .do_analysis = dasd_fba_do_analysis, 595a4d26c6aSStefan Weinhuber .verify_path = dasd_generic_verify_path, 5961da177e4SLinus Torvalds .fill_geometry = dasd_fba_fill_geometry, 5971da177e4SLinus Torvalds .start_IO = dasd_start_IO, 5981da177e4SLinus Torvalds .term_IO = dasd_term_IO, 5998e09f215SStefan Weinhuber .handle_terminated_request = dasd_fba_handle_terminated_request, 6001da177e4SLinus Torvalds .erp_action = dasd_fba_erp_action, 6011da177e4SLinus Torvalds .erp_postaction = dasd_fba_erp_postaction, 6025a27e60dSStefan Weinhuber .check_for_device_change = dasd_fba_check_for_device_change, 6031da177e4SLinus Torvalds .build_cp = dasd_fba_build_cp, 6041da177e4SLinus Torvalds .free_cp = dasd_fba_free_cp, 6051da177e4SLinus Torvalds .dump_sense = dasd_fba_dump_sense, 606fc19f381SStefan Haberland .dump_sense_dbf = dasd_fba_dump_sense_dbf, 6071da177e4SLinus Torvalds .fill_info = dasd_fba_fill_info, 6081da177e4SLinus Torvalds }; 6091da177e4SLinus Torvalds 6101da177e4SLinus Torvalds static int __init 6111da177e4SLinus Torvalds dasd_fba_init(void) 6121da177e4SLinus Torvalds { 613736e6ea0SSebastian Ott int ret; 614736e6ea0SSebastian Ott 6151da177e4SLinus Torvalds ASCEBC(dasd_fba_discipline.ebcname, 4); 616736e6ea0SSebastian Ott ret = ccw_driver_register(&dasd_fba_driver); 617736e6ea0SSebastian Ott if (!ret) 618736e6ea0SSebastian Ott wait_for_device_probe(); 619736e6ea0SSebastian Ott 620736e6ea0SSebastian Ott return ret; 6211da177e4SLinus Torvalds } 6221da177e4SLinus Torvalds 6231da177e4SLinus Torvalds static void __exit 6241da177e4SLinus Torvalds dasd_fba_cleanup(void) 6251da177e4SLinus Torvalds { 6261da177e4SLinus Torvalds ccw_driver_unregister(&dasd_fba_driver); 6271da177e4SLinus Torvalds } 6281da177e4SLinus Torvalds 6291da177e4SLinus Torvalds module_init(dasd_fba_init); 6301da177e4SLinus Torvalds module_exit(dasd_fba_cleanup); 631