diff options
Diffstat (limited to 'drivers/s390/block/dasd_fba.c')
| -rw-r--r-- | drivers/s390/block/dasd_fba.c | 178 |
1 files changed, 98 insertions, 80 deletions
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index a3eb6fd1467..2c8e68bf9a1 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -1,12 +1,10 @@ /* - * File...........: linux/drivers/s390/block/dasd_fba.c * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> - * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 - * + * Copyright IBM Corp. 1999, 2009 */ -#define KMSG_COMPONENT "dasd" +#define KMSG_COMPONENT "dasd-fba" #include <linux/stddef.h> #include <linux/kernel.h> @@ -21,7 +19,6 @@ #include <asm/idals.h> #include <asm/ebcdic.h> #include <asm/io.h> -#include <asm/todclk.h> #include <asm/ccwdev.h> #include "dasd_int.h" @@ -32,6 +29,8 @@ #endif /* PRINTK_HEADER */ #define PRINTK_HEADER "dasd(fba):" +#define FBA_DEFAULT_RETRIES 32 + #define DASD_FBA_CCW_WRITE 0x41 #define DASD_FBA_CCW_READ 0x42 #define DASD_FBA_CCW_LOCATE 0x43 @@ -67,14 +66,21 @@ dasd_fba_set_online(struct ccw_device *cdev) } static struct ccw_driver dasd_fba_driver = { - .name = "dasd-fba", - .owner = THIS_MODULE, + .driver = { + .name = "dasd-fba", + .owner = THIS_MODULE, + }, .ids = dasd_fba_ids, .probe = dasd_fba_probe, .remove = dasd_generic_remove, .set_offline = dasd_generic_set_offline, .set_online = dasd_fba_set_online, .notify = dasd_generic_notify, + .path_event = dasd_generic_path_event, + .freeze = dasd_generic_pm_freeze, + .thaw = dasd_generic_restore_device, + .restore = dasd_generic_restore_device, + .int_class = IRQIO_DAS, }; static void @@ -122,26 +128,26 @@ dasd_fba_check_characteristics(struct dasd_device *device) struct dasd_block *block; struct dasd_fba_private *private; struct ccw_device *cdev = device->cdev; - void *rdc_data; int rc; + int readonly; private = (struct dasd_fba_private *) device->private; - if (private == NULL) { - private = kzalloc(sizeof(struct dasd_fba_private), - GFP_KERNEL | GFP_DMA); - if (private == NULL) { + if (!private) { + private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA); + if (!private) { dev_warn(&device->cdev->dev, "Allocating memory for private DASD " "data failed\n"); return -ENOMEM; } device->private = (void *) private; + } else { + memset(private, 0, sizeof(*private)); } block = dasd_alloc_block(); if (IS_ERR(block)) { - DBF_EVENT(DBF_WARNING, "could not allocate dasd block " - "structure for device: %s", - dev_name(&device->cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", "could not allocate " + "dasd block structure"); device->private = NULL; kfree(private); return PTR_ERR(block); @@ -150,12 +156,11 @@ dasd_fba_check_characteristics(struct dasd_device *device) block->base = device; /* Read Device Characteristics */ - rdc_data = (void *) &(private->rdc_data); - rc = dasd_generic_read_dev_chars(device, "FBA ", &rdc_data, 32); + rc = dasd_generic_read_dev_chars(device, DASD_FBA_MAGIC, + &private->rdc_data, 32); if (rc) { - DBF_EVENT(DBF_WARNING, "Read device characteristics returned " - "error %d for device: %s", - rc, dev_name(&device->cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, cdev, "Read device " + "characteristics returned error %d", rc); device->block = NULL; dasd_free_block(block); device->private = NULL; @@ -163,16 +168,25 @@ dasd_fba_check_characteristics(struct dasd_device *device) return rc; } + device->default_expires = DASD_EXPIRES; + device->default_retries = FBA_DEFAULT_RETRIES; + device->path_data.opm = LPM_ANYPATH; + + readonly = dasd_device_is_ro(device); + if (readonly) + set_bit(DASD_FLAG_DEVICE_RO, &device->flags); + dev_info(&device->cdev->dev, "New FBA DASD %04X/%02X (CU %04X/%02X) with %d MB " - "and %d B/blk\n", + "and %d B/blk%s\n", cdev->id.dev_type, cdev->id.dev_model, cdev->id.cu_type, cdev->id.cu_model, ((private->rdc_data.blk_bdsa * (private->rdc_data.blk_size >> 9)) >> 11), - private->rdc_data.blk_size); + private->rdc_data.blk_size, + readonly ? ", read-only device" : ""); return 0; } @@ -224,24 +238,16 @@ dasd_fba_erp_postaction(struct dasd_ccw_req * cqr) return NULL; } -static void dasd_fba_handle_unsolicited_interrupt(struct dasd_device *device, - struct irb *irb) +static void dasd_fba_check_for_device_change(struct dasd_device *device, + struct dasd_ccw_req *cqr, + struct irb *irb) { char mask; /* first of all check for state change pending interrupt */ mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; - if ((irb->scsw.cmd.dstat & mask) == mask) { + if ((irb->scsw.cmd.dstat & mask) == mask) dasd_generic_handle_state_change(device); - return; - } - - /* check for unsolicited interrupts */ - DBF_DEV_EVENT(DBF_WARNING, device, "%s", - "unsolicited interrupt received"); - device->discipline->dump_sense_dbf(device, NULL, irb, "unsolicited"); - dasd_schedule_device_bh(device); - return; }; static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, @@ -254,7 +260,7 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, struct dasd_ccw_req *cqr; struct ccw1 *ccw; struct req_iterator iter; - struct bio_vec *bv; + struct bio_vec bv; char *dst; int count, cidaw, cplength, datasize; sector_t recid, first_rec, last_rec; @@ -270,19 +276,20 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, return ERR_PTR(-EINVAL); blksize = block->bp_block; /* Calculate record id of first and last block. */ - first_rec = req->sector >> block->s2b_shift; - last_rec = (req->sector + req->nr_sectors - 1) >> block->s2b_shift; + first_rec = blk_rq_pos(req) >> block->s2b_shift; + last_rec = + (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift; /* Check struct bio and count the number of blocks for the request. */ count = 0; cidaw = 0; rq_for_each_segment(bv, req, iter) { - if (bv->bv_len & (blksize - 1)) + if (bv.bv_len & (blksize - 1)) /* Fba can only do full blocks. */ return ERR_PTR(-EINVAL); - count += bv->bv_len >> (block->s2b_shift + 9); + count += bv.bv_len >> (block->s2b_shift + 9); #if defined(CONFIG_64BIT) - if (idal_is_needed (page_address(bv->bv_page), bv->bv_len)) - cidaw += bv->bv_len / blksize; + if (idal_is_needed (page_address(bv.bv_page), bv.bv_len)) + cidaw += bv.bv_len / blksize; #endif } /* Paranoia. */ @@ -302,14 +309,13 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, datasize += (count - 1)*sizeof(struct LO_fba_data); } /* Allocate the ccw request. */ - cqr = dasd_smalloc_request(dasd_fba_discipline.name, - cplength, datasize, memdev); + cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev); if (IS_ERR(cqr)) return cqr; ccw = cqr->cpaddr; /* First ccw is define extent. */ define_extent(ccw++, cqr->data, rq_data_dir(req), - block->bp_block, req->sector, req->nr_sectors); + block->bp_block, blk_rq_pos(req), blk_rq_sectors(req)); /* Build locate_record + read/write ccws. */ idaws = (unsigned long *) (cqr->data + sizeof(struct DE_fba_data)); LO_data = (struct LO_fba_data *) (idaws + cidaw); @@ -320,16 +326,16 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, } recid = first_rec; rq_for_each_segment(bv, req, iter) { - dst = page_address(bv->bv_page) + bv->bv_offset; + 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); + memcpy(copy + bv.bv_offset, dst, bv.bv_len); if (copy) - dst = copy + bv->bv_offset; + dst = copy + bv.bv_offset; } - for (off = 0; off < bv->bv_len; off += blksize) { + for (off = 0; off < bv.bv_len; off += blksize) { /* Locate record for stupid devices. */ if (private->rdc_data.mode.bits.data_chain == 0) { ccw[-1].flags |= CCW_FLAG_CC; @@ -365,9 +371,9 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, cqr->startdev = memdev; cqr->memdev = memdev; cqr->block = block; - cqr->expires = 5 * 60 * HZ; /* 5 minutes */ - cqr->retries = 32; - cqr->buildclk = get_clock(); + cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */ + cqr->retries = memdev->default_retries; + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; } @@ -378,7 +384,7 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) struct dasd_fba_private *private; struct ccw1 *ccw; struct req_iterator iter; - struct bio_vec *bv; + struct bio_vec bv; char *dst, *cda; unsigned int blksize, off; int status; @@ -393,8 +399,8 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) if (private->rdc_data.mode.bits.data_chain != 0) ccw++; rq_for_each_segment(bv, req, iter) { - dst = page_address(bv->bv_page) + bv->bv_offset; - for (off = 0; off < bv->bv_len; off += blksize) { + 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++; @@ -405,7 +411,7 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) cda = (char *)((addr_t) ccw->cda); if (dst != cda) { if (rq_data_dir(req) == READ) - memcpy(dst, cda, bv->bv_len); + memcpy(dst, cda, bv.bv_len); kmem_cache_free(dasd_page_cache, (void *)((addr_t)cda & PAGE_MASK)); } @@ -422,7 +428,10 @@ out: static void dasd_fba_handle_terminated_request(struct dasd_ccw_req *cqr) { - cqr->status = DASD_CQR_FILLED; + if (cqr->retries < 0) + cqr->status = DASD_CQR_FAILED; + else + cqr->status = DASD_CQR_FILLED; }; static int @@ -441,17 +450,20 @@ dasd_fba_fill_info(struct dasd_device * device, } static void -dasd_fba_dump_sense_dbf(struct dasd_device *device, struct dasd_ccw_req *req, - struct irb *irb, char *reason) +dasd_fba_dump_sense_dbf(struct dasd_device *device, struct irb *irb, + char *reason) { - int sl; - if (irb->esw.esw0.erw.cons) { - for (sl = 0; sl < 4; sl++) { - DBF_DEV_EVENT(DBF_EMERG, device, - "%s: %08x %08x %08x %08x", - reason, irb->ecw[8 * 0], irb->ecw[8 * 1], - irb->ecw[8 * 2], irb->ecw[8 * 3]); - } + u64 *sense; + + sense = (u64 *) dasd_get_sense(irb); + if (sense) { + DBF_DEV_EVENT(DBF_EMERG, device, + "%s: %s %02x%02x%02x %016llx %016llx %016llx " + "%016llx", reason, + scsw_is_tm(&irb->scsw) ? "t" : "c", + scsw_cc(&irb->scsw), scsw_cstat(&irb->scsw), + scsw_dstat(&irb->scsw), sense[0], sense[1], + sense[2], sense[3]); } else { DBF_DEV_EVENT(DBF_EMERG, device, "%s", "SORRY - NO VALID SENSE AVAILABLE\n"); @@ -473,19 +485,19 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, "No memory to dump sense data"); return; } - len = sprintf(page, KERN_ERR PRINTK_HEADER + len = sprintf(page, PRINTK_HEADER " I/O status report for device %s:\n", dev_name(&device->cdev->dev)); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " in req: %p CS: 0x%02X DS: 0x%02X\n", req, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " device %s: Failing CCW: %p\n", dev_name(&device->cdev->dev), (void *) (addr_t) irb->scsw.cmd.cpa); if (irb->esw.esw0.erw.cons) { for (sl = 0; sl < 4; sl++) { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " Sense(hex) %2d-%2d:", (8 * sl), ((8 * sl) + 7)); @@ -496,7 +508,7 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, len += sprintf(page + len, "\n"); } } else { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " SORRY - NO VALID SENSE AVAILABLE\n"); } printk(KERN_ERR "%s", page); @@ -506,10 +518,9 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, 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); + len = sprintf(page, PRINTK_HEADER " Related CP in req: %p\n", req); while (act <= end) { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " CCW %p: %08X %08X DAT:", act, ((int *) act)[0], ((int *) act)[1]); for (count = 0; count < 32 && count < act->count; @@ -527,11 +538,11 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, len = 0; if (act < ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2) { act = ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2; - len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); + len += sprintf(page + len, PRINTK_HEADER "......\n"); } end = min((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa + 2, last); while (act <= end) { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " CCW %p: %08X %08X DAT:", act, ((int *) act)[0], ((int *) act)[1]); for (count = 0; count < 32 && count < act->count; @@ -546,10 +557,10 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, /* print last CCWs */ if (act < last - 2) { act = last - 2; - len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); + len += sprintf(page + len, PRINTK_HEADER "......\n"); } while (act <= last) { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER + len += sprintf(page + len, PRINTK_HEADER " CCW %p: %08X %08X DAT:", act, ((int *) act)[0], ((int *) act)[1]); for (count = 0; count < 32 && count < act->count; @@ -586,13 +597,14 @@ static struct dasd_discipline dasd_fba_discipline = { .max_blocks = 96, .check_device = dasd_fba_check_characteristics, .do_analysis = dasd_fba_do_analysis, + .verify_path = dasd_generic_verify_path, .fill_geometry = dasd_fba_fill_geometry, .start_IO = dasd_start_IO, .term_IO = dasd_term_IO, .handle_terminated_request = dasd_fba_handle_terminated_request, .erp_action = dasd_fba_erp_action, .erp_postaction = dasd_fba_erp_postaction, - .handle_unsolicited_interrupt = dasd_fba_handle_unsolicited_interrupt, + .check_for_device_change = dasd_fba_check_for_device_change, .build_cp = dasd_fba_build_cp, .free_cp = dasd_fba_free_cp, .dump_sense = dasd_fba_dump_sense, @@ -603,8 +615,14 @@ static struct dasd_discipline dasd_fba_discipline = { static int __init dasd_fba_init(void) { + int ret; + ASCEBC(dasd_fba_discipline.ebcname, 4); - return ccw_driver_register(&dasd_fba_driver); + ret = ccw_driver_register(&dasd_fba_driver); + if (!ret) + wait_for_device_probe(); + + return ret; } static void __exit |
