X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fs390%2Fblock%2Fdasd_fba.c;h=b857fd5893fdc4d35586e929e3060a070aa5b4d5;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=35b8f37b1d53887ea544200ee6346e43095dd789;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 35b8f37b1..b857fd589 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -1,13 +1,11 @@ -/* +/* * File...........: linux/drivers/s390/block/dasd_fba.c * Author(s)......: Holger Smolinski * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * - * $Revision: 1.33 $ */ -#include #include #include #include @@ -46,8 +44,8 @@ struct dasd_fba_private { }; static struct ccw_device_id dasd_fba_ids[] = { - { CCW_DEVICE_DEVTYPE (0x6310, 0, 0x9336, 0), driver_info: 0x1}, - { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3370, 0), driver_info: 0x2}, + { CCW_DEVICE_DEVTYPE (0x6310, 0, 0x9336, 0), .driver_info = 0x1}, + { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3370, 0), .driver_info = 0x2}, { /* end of list */ }, }; @@ -57,19 +55,13 @@ static struct ccw_driver dasd_fba_driver; /* see below */ static int dasd_fba_probe(struct ccw_device *cdev) { - int ret; - - ret = dasd_generic_probe (cdev, &dasd_fba_discipline); - if (ret) - return ret; - ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP); - return 0; + return dasd_generic_probe(cdev, &dasd_fba_discipline); } static int dasd_fba_set_online(struct ccw_device *cdev) { - return dasd_generic_set_online (cdev, &dasd_fba_discipline); + return dasd_generic_set_online(cdev, &dasd_fba_discipline); } static struct ccw_driver dasd_fba_driver = { @@ -126,16 +118,17 @@ static int dasd_fba_check_characteristics(struct dasd_device *device) { struct dasd_fba_private *private; - struct ccw_device *cdev = device->cdev; + struct ccw_device *cdev = device->cdev; void *rdc_data; int rc; private = (struct dasd_fba_private *) device->private; if (private == NULL) { - private = kmalloc(sizeof(struct dasd_fba_private), GFP_KERNEL); + private = kzalloc(sizeof(struct dasd_fba_private), GFP_KERNEL); if (private == NULL) { - MESSAGE(KERN_WARNING, "%s", - "memory allocation failed for private data"); + DEV_MESSAGE(KERN_WARNING, device, "%s", + "memory allocation failed for private " + "data"); return -ENOMEM; } device->private = (void *) private; @@ -144,8 +137,9 @@ dasd_fba_check_characteristics(struct dasd_device *device) rdc_data = (void *) &(private->rdc_data); rc = read_dev_chars(device->cdev, &rdc_data, 32); if (rc) { - MESSAGE(KERN_WARNING, - "Read device characteristics returned error %d", rc); + DEV_MESSAGE(KERN_WARNING, device, + "Read device characteristics returned error %d", + rc); return rc; } @@ -203,7 +197,7 @@ dasd_fba_examine_error(struct dasd_ccw_req * cqr, struct irb * irb) if (irb->scsw.cstat == 0x00 && irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) return dasd_era_none; - + cdev = device->cdev; switch (cdev->id.dev_type) { case 0x3370: @@ -227,8 +221,8 @@ dasd_fba_erp_postaction(struct dasd_ccw_req * cqr) if (cqr->function == dasd_default_erp_action) return dasd_default_erp_postaction; - MESSAGE(KERN_WARNING, "unknown ERP action %p", cqr->function); - + DEV_MESSAGE(KERN_WARNING, cqr->device, "unknown ERP action %p", + cqr->function); return NULL; } @@ -269,9 +263,10 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) /* Fba can only do full blocks. */ return ERR_PTR(-EINVAL); count += bv->bv_len >> (device->s2b_shift + 9); -#if defined(CONFIG_ARCH_S390X) - cidaw += idal_nr_words(page_address(bv->bv_page) + - bv->bv_offset, bv->bv_len); +#if defined(CONFIG_64BIT) + if (idal_is_needed (page_address(bv->bv_page), + bv->bv_len)) + cidaw += bv->bv_len / blksize; #endif } } @@ -311,6 +306,14 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) recid = first_rec; rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) { dst = page_address(bv->bv_page) + bv->bv_offset; + if (dasd_page_cache) { + char *copy = kmem_cache_alloc(dasd_page_cache, + GFP_DMA | __GFP_NOWARN); + if (copy && rq_data_dir(req) == WRITE) + memcpy(copy + bv->bv_offset, dst, bv->bv_len); + if (copy) + dst = copy + bv->bv_offset; + } for (off = 0; off < bv->bv_len; off += blksize) { /* Locate record for stupid devices. */ if (private->rdc_data.mode.bits.data_chain == 0) { @@ -341,12 +344,64 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) recid++; } } + if (req->cmd_flags & REQ_FAILFAST) + set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->device = device; cqr->expires = 5 * 60 * HZ; /* 5 minutes */ + cqr->retries = 32; + cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; return cqr; } +static int +dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) +{ + struct dasd_fba_private *private; + struct ccw1 *ccw; + struct bio *bio; + struct bio_vec *bv; + char *dst, *cda; + unsigned int blksize, off; + int i, status; + + if (!dasd_page_cache) + goto out; + private = (struct dasd_fba_private *) cqr->device->private; + blksize = cqr->device->bp_block; + ccw = cqr->cpaddr; + /* Skip over define extent & locate record. */ + ccw++; + if (private->rdc_data.mode.bits.data_chain != 0) + ccw++; + rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) { + dst = page_address(bv->bv_page) + bv->bv_offset; + for (off = 0; off < bv->bv_len; off += blksize) { + /* Skip locate record. */ + if (private->rdc_data.mode.bits.data_chain == 0) + ccw++; + if (dst) { + if (ccw->flags & CCW_FLAG_IDA) + cda = *((char **)((addr_t) ccw->cda)); + else + cda = (char *)((addr_t) ccw->cda); + if (dst != cda) { + if (rq_data_dir(req) == READ) + memcpy(dst, cda, bv->bv_len); + kmem_cache_free(dasd_page_cache, + (void *)((addr_t)cda & PAGE_MASK)); + } + dst = NULL; + } + ccw++; + } + } +out: + status = cqr->status == DASD_CQR_DONE; + dasd_sfree_request(cqr, cqr->device); + return status; +} + static int dasd_fba_fill_info(struct dasd_device * device, struct dasd_information2_t * info) @@ -367,18 +422,107 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, struct irb *irb) { char *page; + struct ccw1 *act, *end, *last; + int len, sl, sct, count; - page = (char *) get_zeroed_page(GFP_KERNEL); + page = (char *) get_zeroed_page(GFP_ATOMIC); if (page == NULL) { - MESSAGE(KERN_ERR, "%s", "No memory to dump sense data"); + DEV_MESSAGE(KERN_ERR, device, " %s", + "No memory to dump sense data"); return; } - sprintf(page, KERN_WARNING PRINTK_HEADER - "device %s: I/O status report:\n", - device->cdev->dev.bus_id); + len = sprintf(page, KERN_ERR PRINTK_HEADER + " I/O status report for device %s:\n", + device->cdev->dev.bus_id); + len += sprintf(page + len, KERN_ERR PRINTK_HEADER + " in req: %p CS: 0x%02X DS: 0x%02X\n", req, + irb->scsw.cstat, irb->scsw.dstat); + len += sprintf(page + len, KERN_ERR PRINTK_HEADER + " device %s: Failing CCW: %p\n", + device->cdev->dev.bus_id, + (void *) (addr_t) irb->scsw.cpa); + if (irb->esw.esw0.erw.cons) { + for (sl = 0; sl < 4; sl++) { + len += sprintf(page + len, KERN_ERR PRINTK_HEADER + " Sense(hex) %2d-%2d:", + (8 * sl), ((8 * sl) + 7)); + + for (sct = 0; sct < 8; sct++) { + len += sprintf(page + len, " %02x", + irb->ecw[8 * sl + sct]); + } + len += sprintf(page + len, "\n"); + } + } else { + len += sprintf(page + len, KERN_ERR PRINTK_HEADER + " SORRY - NO VALID SENSE AVAILABLE\n"); + } + MESSAGE_LOG(KERN_ERR, "%s", + page + sizeof(KERN_ERR PRINTK_HEADER)); + + /* dump the Channel Program */ + /* print first CCWs (maximum 8) */ + act = req->cpaddr; + for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); + end = min(act + 8, last); + len = sprintf(page, KERN_ERR PRINTK_HEADER + " Related CP in req: %p\n", req); + while (act <= end) { + len += sprintf(page + len, KERN_ERR PRINTK_HEADER + " CCW %p: %08X %08X DAT:", + act, ((int *) act)[0], ((int *) act)[1]); + for (count = 0; count < 32 && count < act->count; + count += sizeof(int)) + len += sprintf(page + len, " %08X", + ((int *) (addr_t) act->cda) + [(count>>2)]); + len += sprintf(page + len, "\n"); + act++; + } + MESSAGE_LOG(KERN_ERR, "%s", + page + sizeof(KERN_ERR PRINTK_HEADER)); - MESSAGE(KERN_ERR, "Sense data:\n%s", page); + /* print failing CCW area */ + len = 0; + if (act < ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2) { + act = ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2; + len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); + } + end = min((struct ccw1 *)(addr_t) irb->scsw.cpa + 2, last); + while (act <= end) { + len += sprintf(page + len, KERN_ERR PRINTK_HEADER + " CCW %p: %08X %08X DAT:", + act, ((int *) act)[0], ((int *) act)[1]); + for (count = 0; count < 32 && count < act->count; + count += sizeof(int)) + len += sprintf(page + len, " %08X", + ((int *) (addr_t) act->cda) + [(count>>2)]); + len += sprintf(page + len, "\n"); + act++; + } + + /* print last CCWs */ + if (act < last - 2) { + act = last - 2; + len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); + } + while (act <= last) { + len += sprintf(page + len, KERN_ERR PRINTK_HEADER + " CCW %p: %08X %08X DAT:", + act, ((int *) act)[0], ((int *) act)[1]); + for (count = 0; count < 32 && count < act->count; + count += sizeof(int)) + len += sprintf(page + len, " %08X", + ((int *) (addr_t) act->cda) + [(count>>2)]); + len += sprintf(page + len, "\n"); + act++; + } + if (len > 0) + MESSAGE_LOG(KERN_ERR, "%s", + page + sizeof(KERN_ERR PRINTK_HEADER)); free_page((unsigned long) page); } @@ -388,7 +532,7 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, * 8192 bytes (=2 pages). For 64 bit one dasd_mchunkt_t structure has * 24 bytes, the struct dasd_ccw_req has 136 bytes and each block can use * up to 16 bytes (8 for the ccw and 8 for the idal pointer). In - * addition we have one define extent ccw + 16 bytes of data and a + * addition we have one define extent ccw + 16 bytes of data and a * locate record ccw for each block (stupid devices!) + 16 bytes of data. * That makes: * (8192 - 24 - 136 - 8 - 16) / 40 = 200.2 blocks at maximum. @@ -410,6 +554,7 @@ static struct dasd_discipline dasd_fba_discipline = { .erp_action = dasd_fba_erp_action, .erp_postaction = dasd_fba_erp_postaction, .build_cp = dasd_fba_build_cp, + .free_cp = dasd_fba_free_cp, .dump_sense = dasd_fba_dump_sense, .fill_info = dasd_fba_fill_info, }; @@ -417,16 +562,8 @@ static struct dasd_discipline dasd_fba_discipline = { static int __init dasd_fba_init(void) { - int ret; - ASCEBC(dasd_fba_discipline.ebcname, 4); - - ret = ccw_driver_register(&dasd_fba_driver); - if (ret) - return ret; - - dasd_generic_auto_online(&dasd_fba_driver); - return 0; + return ccw_driver_register(&dasd_fba_driver); } static void __exit @@ -437,22 +574,3 @@ dasd_fba_cleanup(void) module_init(dasd_fba_init); module_exit(dasd_fba_cleanup); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 4 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -4 - * c-argdecl-indent: 4 - * c-label-offset: -4 - * c-continued-statement-offset: 4 - * c-continued-brace-offset: 0 - * indent-tabs-mode: 1 - * tab-width: 8 - * End: - */