X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fs390%2Fblock%2Fdasd_eckd.c;h=df8902364762d87b1b9d5dc6579e4e09e294afca;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=27bf674af1c2b547ee76cee482950fc8ef28aad6;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 27bf674af..df8902364 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -7,7 +7,7 @@ * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * - * $Revision: 1.57 $ + * $Revision: 1.68 $ */ #include @@ -37,9 +37,12 @@ #define ECKD_C0(i) (i->home_bytes) #define ECKD_F(i) (i->formula) -#define ECKD_F1(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f1):(i->factors.f_0x02.f1)) -#define ECKD_F2(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f2):(i->factors.f_0x02.f2)) -#define ECKD_F3(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f3):(i->factors.f_0x02.f3)) +#define ECKD_F1(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f1):\ + (i->factors.f_0x02.f1)) +#define ECKD_F2(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f2):\ + (i->factors.f_0x02.f2)) +#define ECKD_F3(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f3):\ + (i->factors.f_0x02.f3)) #define ECKD_F4(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f4):0) #define ECKD_F5(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f5):0) #define ECKD_F6(i) (i->factor6) @@ -68,6 +71,10 @@ static struct ccw_device_id dasd_eckd_ids[] = { { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3380, 0), driver_info: 0x4}, { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3380, 0), driver_info: 0x5}, { CCW_DEVICE_DEVTYPE (0x9343, 0, 0x9345, 0), driver_info: 0x6}, + { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3390, 0), driver_info: 0x7}, + { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3380, 0), driver_info: 0x8}, + { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3390, 0), driver_info: 0x9}, + { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3380, 0), driver_info: 0xa}, { /* end of list */ }, }; @@ -147,7 +154,6 @@ bytes_per_record(struct dasd_eckd_characteristics *rdc, int kl, int dl) bpr = fl1 + fl2; break; default: - MESSAGE(KERN_ERR, "unknown formula%d", rdc->formula); bpr = 0; break; } @@ -205,8 +211,8 @@ check_XRC (struct ccw1 *de_ccw, /* switch on System Time Stamp - needed for XRC Support */ if (private->rdc_data.facilities.XRC_supported) { - data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */ - data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */ + data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */ + data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */ data->ep_sys_time = get_clock (); @@ -268,15 +274,17 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, check_XRC (ccw, data, device); break; default: - MESSAGE(KERN_ERR, "unknown opcode 0x%x", cmd); + DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd); break; } data->attributes.mode = 0x3; /* ECKD */ - if (private->rdc_data.cu_type == 0x2105 + if ((private->rdc_data.cu_type == 0x2105 || + private->rdc_data.cu_type == 0x2107 || + private->rdc_data.cu_type == 0x1750) && !(private->uses_cdl && trk < 2)) - data->ga_extended |= 0x40; + data->ga_extended |= 0x40; /* Regular Data Format Mode */ geo.cyl = private->rdc_data.no_cyl; geo.head = private->rdc_data.trk_per_cyl; @@ -312,7 +320,7 @@ locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk, private = (struct dasd_eckd_private *) device->private; - DBF_EVENT(DBF_INFO, + DBF_DEV_EVENT(DBF_INFO, device, "Locate: trk %d, rec %d, no_rec %d, cmd %d, reclen %d", trk, rec_on_trk, no_rec, cmd, reclen); @@ -394,7 +402,7 @@ locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk, data->operation.operation = 0x0b; break; default: - MESSAGE(KERN_ERR, "unknown opcode 0x%x", cmd); + DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd); } data->seek_addr.cyl = data->search_arg.cyl = trk / private->rdc_data.trk_per_cyl; @@ -452,8 +460,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device) private = kmalloc(sizeof(struct dasd_eckd_private), GFP_KERNEL | GFP_DMA); 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; @@ -468,8 +477,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device) rdc_data = (void *) &(private->rdc_data); rc = read_dev_chars(device->cdev, &rdc_data, 64); 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; } @@ -486,24 +496,27 @@ dasd_eckd_check_characteristics(struct dasd_device *device) /* Read Configuration Data */ rc = read_conf_data(device->cdev, &conf_data, &conf_len); if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ - MESSAGE(KERN_WARNING, - "Read configuration data returned error %d", rc); + DEV_MESSAGE(KERN_WARNING, device, + "Read configuration data returned error %d", rc); return rc; } if (conf_data == NULL) { - MESSAGE(KERN_WARNING, "%s", "No configuration data retrieved"); + DEV_MESSAGE(KERN_WARNING, device, "%s", + "No configuration data retrieved"); return 0; /* no errror */ } if (conf_len != sizeof (struct dasd_eckd_confdata)) { - MESSAGE(KERN_WARNING, - "sizes of configuration data mismatch" - "%d (read) vs %ld (expected)", - conf_len, sizeof (struct dasd_eckd_confdata)); + DEV_MESSAGE(KERN_WARNING, device, + "sizes of configuration data mismatch" + "%d (read) vs %ld (expected)", + conf_len, sizeof (struct dasd_eckd_confdata)); + + kfree(conf_data); /* allocated by read_conf_data() */ return 0; /* no errror */ } memcpy(&private->conf_data, conf_data, sizeof (struct dasd_eckd_confdata)); - + kfree(conf_data); /* allocated by read_conf_data() */ return 0; } @@ -738,8 +751,9 @@ dasd_eckd_format_device(struct dasd_device * device, return ERR_PTR(-EINVAL); } if (dasd_check_blocksize(fdata->blksize) != 0) { - MESSAGE(KERN_WARNING, "Invalid blocksize %d...terminating!", - fdata->blksize); + DEV_MESSAGE(KERN_WARNING, device, + "Invalid blocksize %d...terminating!", + fdata->blksize); return ERR_PTR(-EINVAL); } @@ -775,7 +789,8 @@ dasd_eckd_format_device(struct dasd_device * device, sizeof(struct eckd_count); break; default: - MESSAGE(KERN_WARNING, "Invalid flags 0x%x.", fdata->intensity); + DEV_MESSAGE(KERN_WARNING, device, "Invalid flags 0x%x.", + fdata->intensity); return ERR_PTR(-EINVAL); } /* Allocate the format ccw request. */ @@ -903,13 +918,15 @@ dasd_eckd_examine_error(struct dasd_ccw_req * cqr, struct irb * irb) switch (cdev->id.cu_type) { case 0x3990: case 0x2105: + case 0x2107: + case 0x1750: return dasd_3990_erp_examine(cqr, irb); case 0x9343: return dasd_9343_erp_examine(cqr, irb); case 0x3880: default: - MESSAGE(KERN_WARNING, "%s", - "default (unknown CU type) - RECOVERABLE return"); + DEV_MESSAGE(KERN_WARNING, device, "%s", + "default (unknown CU type) - RECOVERABLE return"); return dasd_era_recover; } } @@ -923,9 +940,10 @@ dasd_eckd_erp_action(struct dasd_ccw_req * cqr) switch (cdev->id.cu_type) { case 0x3990: case 0x2105: + case 0x2107: + case 0x1750: return dasd_3990_erp_action; case 0x9343: - /* Return dasd_9343_erp_action; */ case 0x3880: default: return dasd_default_erp_action; @@ -983,7 +1001,8 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) return ERR_PTR(-EINVAL); count += bv->bv_len >> (device->s2b_shift + 9); #if defined(CONFIG_ARCH_S390X) - if (idal_is_needed (page_address(bv->bv_page), bv->bv_len)) + if (idal_is_needed (page_address(bv->bv_page), + bv->bv_len)) cidaw += bv->bv_len >> (device->s2b_shift + 9); #endif } @@ -1023,6 +1042,14 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) } 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, + SLAB_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) { sector_t trkid = recid; unsigned int recoffs = sector_div(trkid, blk_per_trk); @@ -1076,6 +1103,58 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) return cqr; } +static int +dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req) +{ + struct dasd_eckd_private *private; + struct ccw1 *ccw; + struct bio *bio; + struct bio_vec *bv; + char *dst, *cda; + unsigned int blksize, blk_per_trk, off; + sector_t recid; + int i, status; + + if (!dasd_page_cache) + goto out; + private = (struct dasd_eckd_private *) cqr->device->private; + blksize = cqr->device->bp_block; + blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize); + recid = req->sector >> cqr->device->s2b_shift; + ccw = cqr->cpaddr; + /* Skip over define extent & locate record. */ + ccw++; + if (private->uses_cdl == 0 || recid > 2*blk_per_trk) + 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->uses_cdl && recid <= 2*blk_per_trk) + 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++; + recid++; + } + } +out: + status = cqr->status == DASD_CQR_DONE; + dasd_sfree_request(cqr, cqr->device); + return status; +} + static int dasd_eckd_fill_info(struct dasd_device * device, struct dasd_information2_t * info) @@ -1120,10 +1199,10 @@ dasd_eckd_release(struct block_device *bdev, int no, long args) cqr = dasd_smalloc_request(dasd_eckd_discipline.name, 1, 32, device); - if (cqr == NULL) { - MESSAGE(KERN_WARNING, "%s", - "No memory to allocate initialization request"); - return -ENOMEM; + if (IS_ERR(cqr)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "Could not allocate initialization request"); + return PTR_ERR(cqr); } cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RELEASE; cqr->cpaddr->flags |= CCW_FLAG_SLI; @@ -1164,10 +1243,10 @@ dasd_eckd_reserve(struct block_device *bdev, int no, long args) cqr = dasd_smalloc_request(dasd_eckd_discipline.name, 1, 32, device); - if (cqr == NULL) { - MESSAGE(KERN_WARNING, "%s", - "No memory to allocate initialization request"); - return -ENOMEM; + if (IS_ERR(cqr)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "Could not allocate initialization request"); + return PTR_ERR(cqr); } cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RESERVE; cqr->cpaddr->flags |= CCW_FLAG_SLI; @@ -1207,10 +1286,10 @@ dasd_eckd_steal_lock(struct block_device *bdev, int no, long args) cqr = dasd_smalloc_request(dasd_eckd_discipline.name, 1, 32, device); - if (cqr == NULL) { - MESSAGE(KERN_WARNING, "%s", - "No memory to allocate initialization request"); - return -ENOMEM; + if (IS_ERR(cqr)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "Could not allocate initialization request"); + return PTR_ERR(cqr); } cqr->cpaddr->cmd_code = DASD_ECKD_CCW_SLCK; cqr->cpaddr->flags |= CCW_FLAG_SLI; @@ -1251,10 +1330,10 @@ dasd_eckd_performance(struct block_device *bdev, int no, long args) (sizeof (struct dasd_psf_prssd_data) + sizeof (struct dasd_rssd_perf_stats_t)), device); - if (cqr == NULL) { - MESSAGE(KERN_WARNING, "%s", - "No memory to allocate initialization request"); - return -ENOMEM; + if (IS_ERR(cqr)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "Could not allocate initialization request"); + return PTR_ERR(cqr); } cqr->device = device; cqr->retries = 0; @@ -1350,58 +1429,47 @@ dasd_eckd_set_attrib(struct block_device *bdev, int no, long args) sizeof (struct attrib_data_t))) { return -EFAULT; } - private = (struct dasd_eckd_private *) device->private; - private->attrib = attrib; - DBF_DEV_EVENT(DBF_ERR, device, - "cache operation mode set to " - "%x (%i cylinder prestage)", - private->attrib.operation, private->attrib.nr_cyl); - + DEV_MESSAGE(KERN_INFO, device, + "cache operation mode set to %x (%i cylinder prestage)", + private->attrib.operation, private->attrib.nr_cyl); return 0; } +/* + * Print sense data and related channel program. + * Parts are printed because printk buffer is only 1024 bytes. + */ static void dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, struct irb *irb) { - char *page; - struct ccw1 *act; - int len, sl, sct; + struct ccw1 *act, *end, *last; + int len, sl, sct, count; 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; } len = sprintf(page, KERN_ERR PRINTK_HEADER - "device %s: I/O status report:\n", + " 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, + " 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 - "Failing CCW: %p\n", + " device %s: Failing CCW: %p\n", + device->cdev->dev.bus_id, (void *) (addr_t) irb->scsw.cpa); - act = req->cpaddr; - do { - DBF_EVENT(DBF_INFO, - "CCW %p: %08X %08X", - act, ((int *) act)[0], ((int *) act)[1]); - DBF_EVENT(DBF_INFO, - "DAT: %08X %08X %08X %08X", - ((int *) (addr_t) act->cda)[0], - ((int *) (addr_t) act->cda)[1], - ((int *) (addr_t) act->cda)[2], - ((int *) (addr_t) act->cda)[3]); - } while (act++->flags & (CCW_FLAG_CC | CCW_FLAG_DC)); if (irb->esw.esw0.erw.cons) { for (sl = 0; sl < 4; sl++) { len += sprintf(page + len, KERN_ERR PRINTK_HEADER - "Sense(hex) %2d-%2d:", + " Sense(hex) %2d-%2d:", (8 * sl), ((8 * sl) + 7)); for (sct = 0; sct < 8; sct++) { @@ -1414,23 +1482,86 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, if (irb->ecw[27] & DASD_SENSE_BIT_0) { /* 24 Byte Sense Data */ len += sprintf(page + len, KERN_ERR PRINTK_HEADER - "24 Byte: %x MSG %x, %s MSGb to SYSOP\n", + " 24 Byte: %x MSG %x, " + "%s MSGb to SYSOP\n", irb->ecw[7] >> 4, irb->ecw[7] & 0x0f, irb->ecw[1] & 0x10 ? "" : "no"); } else { /* 32 Byte Sense Data */ len += sprintf(page + len, KERN_ERR PRINTK_HEADER - "32 Byte: Format: %x " + " 32 Byte: Format: %x " "Exception class %x\n", irb->ecw[6] & 0x0f, irb->ecw[22] >> 4); } } else { len += sprintf(page + len, KERN_ERR PRINTK_HEADER - "SORRY - NO VALID SENSE AVAILABLE\n"); + " SORRY - NO VALID SENSE AVAILABLE\n"); } + MESSAGE_LOG(KERN_ERR, "%s", + page + sizeof(KERN_ERR PRINTK_HEADER)); - MESSAGE(KERN_ERR, "Sense data:\n%s", page); + /* 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)); + + /* 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); } @@ -1462,6 +1593,7 @@ static struct dasd_discipline dasd_eckd_discipline = { .erp_action = dasd_eckd_erp_action, .erp_postaction = dasd_eckd_erp_postaction, .build_cp = dasd_eckd_build_cp, + .free_cp = dasd_eckd_free_cp, .dump_sense = dasd_eckd_dump_sense, .fill_info = dasd_eckd_fill_info, };