diff options
author | Stefan Weinhuber <wein@de.ibm.com> | 2009-03-26 15:23:48 +0100 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2009-03-26 15:24:05 +0100 |
commit | f3eb5384cf0325c02e306b1d81e70f81a03d7432 (patch) | |
tree | 4d75517ad2c61ac2f8b6431eafd62b5d32c188ed /drivers/s390 | |
parent | b44b0ab3bac16356f03e94b1b49ba9305710c445 (diff) |
[S390] dasd: add High Performance FICON support
To support High Performance FICON, the DASD device driver has to
translate I/O requests into the new transport mode control words (TCW)
instead of the traditional (command mode) CCW requests.
Signed-off-by: Stefan Weinhuber <wein@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/block/dasd.c | 70 | ||||
-rw-r--r-- | drivers/s390/block/dasd_3990_erp.c | 141 | ||||
-rw-r--r-- | drivers/s390/block/dasd_alias.c | 25 | ||||
-rw-r--r-- | drivers/s390/block/dasd_devmap.c | 7 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 972 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.h | 40 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eer.c | 21 | ||||
-rw-r--r-- | drivers/s390/block/dasd_int.h | 5 |
8 files changed, 1118 insertions, 163 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 93972ed7f2d..00f7d24b337 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -22,6 +22,7 @@ #include <asm/ebcdic.h> #include <asm/idals.h> #include <asm/todclk.h> +#include <asm/itcw.h> /* This is ugly... */ #define PRINTK_HEADER "dasd:" @@ -852,8 +853,13 @@ int dasd_start_IO(struct dasd_ccw_req *cqr) cqr->startclk = get_clock(); cqr->starttime = jiffies; cqr->retries--; - rc = ccw_device_start(device->cdev, cqr->cpaddr, (long) cqr, - cqr->lpm, 0); + if (cqr->cpmode == 1) { + rc = ccw_device_tm_start(device->cdev, cqr->cpaddr, + (long) cqr, cqr->lpm); + } else { + rc = ccw_device_start(device->cdev, cqr->cpaddr, + (long) cqr, cqr->lpm, 0); + } switch (rc) { case 0: cqr->status = DASD_CQR_IN_IO; @@ -881,9 +887,12 @@ int dasd_start_IO(struct dasd_ccw_req *cqr) " retry on all pathes"); break; case -ENODEV: + DBF_DEV_EVENT(DBF_DEBUG, device, "%s", + "start_IO: -ENODEV device gone, retry"); + break; case -EIO: DBF_DEV_EVENT(DBF_ERR, device, "%s", - "start_IO: device gone, retry"); + "start_IO: -EIO device gone, retry"); break; default: DEV_MESSAGE(KERN_ERR, device, @@ -1015,9 +1024,9 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, /* check for unsolicited interrupts */ cqr = (struct dasd_ccw_req *) intparm; - if (!cqr || ((irb->scsw.cmd.cc == 1) && - (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) && - (irb->scsw.cmd.stctl & SCSW_STCTL_STATUS_PEND))) { + if (!cqr || ((scsw_cc(&irb->scsw) == 1) && + (scsw_fctl(&irb->scsw) & SCSW_FCTL_START_FUNC) && + (scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND))) { if (cqr && cqr->status == DASD_CQR_IN_IO) cqr->status = DASD_CQR_QUEUED; device = dasd_device_from_cdev_locked(cdev); @@ -1040,7 +1049,7 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, /* Check for clear pending */ if (cqr->status == DASD_CQR_CLEAR_PENDING && - irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC) { + scsw_fctl(&irb->scsw) & SCSW_FCTL_CLEAR_FUNC) { cqr->status = DASD_CQR_CLEARED; dasd_device_clear_timer(device); wake_up(&dasd_flush_wq); @@ -1048,7 +1057,7 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, return; } - /* check status - the request might have been killed by dyn detach */ + /* check status - the request might have been killed by dyn detach */ if (cqr->status != DASD_CQR_IN_IO) { MESSAGE(KERN_DEBUG, "invalid status: bus_id %s, status %02x", @@ -1059,8 +1068,8 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, ((irb->scsw.cmd.cstat << 8) | irb->scsw.cmd.dstat), cqr); next = NULL; expires = 0; - if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) && - irb->scsw.cmd.cstat == 0 && !irb->esw.esw0.erw.cons) { + if (scsw_dstat(&irb->scsw) == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) && + scsw_cstat(&irb->scsw) == 0) { /* request was completed successfully */ cqr->status = DASD_CQR_SUCCESS; cqr->stopclk = now; @@ -1991,8 +2000,11 @@ static void dasd_setup_queue(struct dasd_block *block) blk_queue_max_sectors(block->request_queue, max); blk_queue_max_phys_segments(block->request_queue, -1L); blk_queue_max_hw_segments(block->request_queue, -1L); - blk_queue_max_segment_size(block->request_queue, -1L); - blk_queue_segment_boundary(block->request_queue, -1L); + /* with page sized segments we can translate each segement into + * one idaw/tidaw + */ + blk_queue_max_segment_size(block->request_queue, PAGE_SIZE); + blk_queue_segment_boundary(block->request_queue, PAGE_SIZE - 1); blk_queue_ordered(block->request_queue, QUEUE_ORDERED_DRAIN, NULL); } @@ -2432,6 +2444,40 @@ int dasd_generic_read_dev_chars(struct dasd_device *device, char *magic, } EXPORT_SYMBOL_GPL(dasd_generic_read_dev_chars); +/* + * In command mode and transport mode we need to look for sense + * data in different places. The sense data itself is allways + * an array of 32 bytes, so we can unify the sense data access + * for both modes. + */ +char *dasd_get_sense(struct irb *irb) +{ + struct tsb *tsb = NULL; + char *sense = NULL; + + if (scsw_is_tm(&irb->scsw) && (irb->scsw.tm.fcxs == 0x01)) { + if (irb->scsw.tm.tcw) + tsb = tcw_get_tsb((struct tcw *)(unsigned long) + irb->scsw.tm.tcw); + if (tsb && tsb->length == 64 && tsb->flags) + switch (tsb->flags & 0x07) { + case 1: /* tsa_iostat */ + sense = tsb->tsa.iostat.sense; + break; + case 2: /* tsa_ddpc */ + sense = tsb->tsa.ddpc.sense; + break; + default: + /* currently we don't use interrogate data */ + break; + } + } else if (irb->esw.esw0.erw.cons) { + sense = irb->ecw; + } + return sense; +} +EXPORT_SYMBOL_GPL(dasd_get_sense); + static int __init dasd_init(void) { int rc; diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index d82aad5224f..4cee4591614 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -1561,6 +1561,13 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) cqr = cqr->refers; } + if (scsw_is_tm(&cqr->irb.scsw)) { + DBF_DEV_EVENT(DBF_WARNING, device, "%s", + "32 bit sense, action 1B is not defined" + " in transport mode - just retry"); + return default_erp; + } + /* for imprecise ending just do default erp */ if (sense[1] & 0x01) { @@ -1599,7 +1606,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) oldccw = cqr->cpaddr; if (oldccw->cmd_code == DASD_ECKD_CCW_PFX) { PFX_data = cqr->data; - memcpy(DE_data, &PFX_data->define_extend, + memcpy(DE_data, &PFX_data->define_extent, sizeof(struct DE_eckd_data)); } else memcpy(DE_data, cqr->data, sizeof(struct DE_eckd_data)); @@ -1712,6 +1719,13 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) cqr = cqr->refers; } + if (scsw_is_tm(&cqr->irb.scsw)) { + DBF_DEV_EVENT(DBF_WARNING, device, "%s", + "32 bit sense, action 1B, update," + " in transport mode - just retry"); + return previous_erp; + } + /* for imprecise ending just do default erp */ if (sense[1] & 0x01) { @@ -2171,7 +2185,7 @@ dasd_3990_erp_control_check(struct dasd_ccw_req *erp) { struct dasd_device *device = erp->startdev; - if (erp->refers->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK + if (scsw_cstat(&erp->refers->irb.scsw) & (SCHN_STAT_INTF_CTRL_CHK | SCHN_STAT_CHN_CTRL_CHK)) { DEV_MESSAGE(KERN_DEBUG, device, "%s", "channel or interface control check"); @@ -2193,21 +2207,23 @@ dasd_3990_erp_control_check(struct dasd_ccw_req *erp) * erp_new contens was possibly modified */ static struct dasd_ccw_req * -dasd_3990_erp_inspect(struct dasd_ccw_req * erp) +dasd_3990_erp_inspect(struct dasd_ccw_req *erp) { struct dasd_ccw_req *erp_new = NULL; - /* sense data are located in the refers record of the */ - /* already set up new ERP ! */ - char *sense = erp->refers->irb.ecw; + char *sense; /* if this problem occured on an alias retry on base */ erp_new = dasd_3990_erp_inspect_alias(erp); if (erp_new) return erp_new; - /* check if no concurrent sens is available */ - if (!erp->refers->irb.esw.esw0.erw.cons) + /* sense data are located in the refers record of the + * already set up new ERP ! + * check if concurrent sens is available + */ + sense = dasd_get_sense(&erp->refers->irb); + if (!sense) erp_new = dasd_3990_erp_control_check(erp); /* distinguish between 24 and 32 byte sense data */ else if (sense[27] & DASD_SENSE_BIT_0) { @@ -2231,7 +2247,11 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp) * DESCRIPTION * This funtion adds an additional request block (ERP) to the head of * the given cqr (or erp). - * This erp is initialized as an default erp (retry TIC) + * For a command mode cqr the erp is initialized as an default erp + * (retry TIC). + * For transport mode we make a copy of the original TCW (points to + * the original TCCB, TIDALs, etc.) but give it a fresh + * TSB so the original sense data will not be changed. * * PARAMETER * cqr head of the current ERP-chain (or single cqr if @@ -2239,17 +2259,27 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp) * RETURN VALUES * erp pointer to new ERP-chain head */ -static struct dasd_ccw_req * -dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) +static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) { struct dasd_device *device = cqr->startdev; struct ccw1 *ccw; - - /* allocate additional request block */ struct dasd_ccw_req *erp; + int cplength, datasize; + struct tcw *tcw; + struct tsb *tsb; + + if (cqr->cpmode == 1) { + cplength = 0; + datasize = sizeof(struct tcw) + sizeof(struct tsb); + } else { + cplength = 2; + datasize = 0; + } - erp = dasd_alloc_erp_request((char *) &cqr->magic, 2, 0, device); + /* allocate additional request block */ + erp = dasd_alloc_erp_request((char *) &cqr->magic, + cplength, datasize, device); if (IS_ERR(erp)) { if (cqr->retries <= 0) { DEV_MESSAGE(KERN_ERR, device, "%s", @@ -2266,13 +2296,24 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) return cqr; } - /* initialize request with default TIC to current ERP/CQR */ - ccw = erp->cpaddr; - ccw->cmd_code = CCW_CMD_NOOP; - ccw->flags = CCW_FLAG_CC; - ccw++; - ccw->cmd_code = CCW_CMD_TIC; - ccw->cda = (long)(cqr->cpaddr); + if (cqr->cpmode == 1) { + /* make a shallow copy of the original tcw but set new tsb */ + erp->cpmode = 1; + erp->cpaddr = erp->data; + tcw = erp->data; + tsb = (struct tsb *) &tcw[1]; + *tcw = *((struct tcw *)cqr->cpaddr); + tcw->tsb = (long)tsb; + } else { + /* initialize request with default TIC to current ERP/CQR */ + ccw = erp->cpaddr; + ccw->cmd_code = CCW_CMD_NOOP; + ccw->flags = CCW_FLAG_CC; + ccw++; + ccw->cmd_code = CCW_CMD_TIC; + ccw->cda = (long)(cqr->cpaddr); + } + erp->function = dasd_3990_erp_add_erp; erp->refers = cqr; erp->startdev = device; @@ -2282,7 +2323,6 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) erp->expires = 0; erp->retries = 256; erp->buildclk = get_clock(); - erp->status = DASD_CQR_FILLED; return erp; @@ -2340,28 +2380,33 @@ dasd_3990_erp_additional_erp(struct dasd_ccw_req * cqr) * match 'boolean' for match found * returns 1 if match found, otherwise 0. */ -static int -dasd_3990_erp_error_match(struct dasd_ccw_req *cqr1, struct dasd_ccw_req *cqr2) +static int dasd_3990_erp_error_match(struct dasd_ccw_req *cqr1, + struct dasd_ccw_req *cqr2) { + char *sense1, *sense2; if (cqr1->startdev != cqr2->startdev) return 0; - if (cqr1->irb.esw.esw0.erw.cons != cqr2->irb.esw.esw0.erw.cons) - return 0; + sense1 = dasd_get_sense(&cqr1->irb); + sense2 = dasd_get_sense(&cqr2->irb); - if ((cqr1->irb.esw.esw0.erw.cons == 0) && - (cqr2->irb.esw.esw0.erw.cons == 0)) { - if ((cqr1->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK | - SCHN_STAT_CHN_CTRL_CHK)) == - (cqr2->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK | - SCHN_STAT_CHN_CTRL_CHK))) + /* one request has sense data, the other not -> no match, return 0 */ + if (!sense1 != !sense2) + return 0; + /* no sense data in both cases -> check cstat for IFCC */ + if (!sense1 && !sense2) { + if ((scsw_cstat(&cqr1->irb.scsw) & (SCHN_STAT_INTF_CTRL_CHK | + SCHN_STAT_CHN_CTRL_CHK)) == + (scsw_cstat(&cqr2->irb.scsw) & (SCHN_STAT_INTF_CTRL_CHK | + SCHN_STAT_CHN_CTRL_CHK))) return 1; /* match with ifcc*/ } /* check sense data; byte 0-2,25,27 */ - if (!((memcmp (cqr1->irb.ecw, cqr2->irb.ecw, 3) == 0) && - (cqr1->irb.ecw[27] == cqr2->irb.ecw[27]) && - (cqr1->irb.ecw[25] == cqr2->irb.ecw[25]))) { + if (!(sense1 && sense2 && + (memcmp(sense1, sense2, 3) == 0) && + (sense1[27] == sense2[27]) && + (sense1[25] == sense2[25]))) { return 0; /* sense doesn't match */ } @@ -2434,7 +2479,7 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) { struct dasd_device *device = erp->startdev; - char *sense = erp->irb.ecw; + char *sense = dasd_get_sense(&erp->irb); /* check for 24 byte sense ERP */ if ((erp->function == dasd_3990_erp_bus_out) || @@ -2449,7 +2494,7 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) /* prepare erp for retry on different channel path */ erp = dasd_3990_erp_action_1(erp); - if (!(sense[2] & DASD_SENSE_BIT_0)) { + if (sense && !(sense[2] & DASD_SENSE_BIT_0)) { /* issue a Diagnostic Control command with an * Inhibit Write subcommand */ @@ -2479,10 +2524,11 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) } /* check for 32 byte sense ERP */ - } else if ((erp->function == dasd_3990_erp_compound_retry) || - (erp->function == dasd_3990_erp_compound_path) || - (erp->function == dasd_3990_erp_compound_code) || - (erp->function == dasd_3990_erp_compound_config)) { + } else if (sense && + ((erp->function == dasd_3990_erp_compound_retry) || + (erp->function == dasd_3990_erp_compound_path) || + (erp->function == dasd_3990_erp_compound_code) || + (erp->function == dasd_3990_erp_compound_config))) { erp = dasd_3990_erp_compound(erp, sense); @@ -2548,18 +2594,19 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, if (erp->retries > 0) { - char *sense = erp->refers->irb.ecw; + char *sense = dasd_get_sense(&erp->refers->irb); /* check for special retries */ - if (erp->function == dasd_3990_erp_action_4) { + if (sense && erp->function == dasd_3990_erp_action_4) { erp = dasd_3990_erp_action_4(erp, sense); - } else if (erp->function == dasd_3990_erp_action_1B_32) { + } else if (sense && + erp->function == dasd_3990_erp_action_1B_32) { erp = dasd_3990_update_1B(erp, sense); - } else if (erp->function == dasd_3990_erp_int_req) { + } else if (sense && erp->function == dasd_3990_erp_int_req) { erp = dasd_3990_erp_int_req(erp); @@ -2622,8 +2669,8 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) } /* double-check if current erp/cqr was successful */ - if ((cqr->irb.scsw.cmd.cstat == 0x00) && - (cqr->irb.scsw.cmd.dstat == + if ((scsw_cstat(&cqr->irb.scsw) == 0x00) && + (scsw_dstat(&cqr->irb.scsw) == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))) { DEV_MESSAGE(KERN_DEBUG, device, diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c index 20676cdef4a..219bee7bd77 100644 --- a/drivers/s390/block/dasd_alias.c +++ b/drivers/s390/block/dasd_alias.c @@ -646,14 +646,16 @@ static int reset_summary_unit_check(struct alias_lcu *lcu, { struct dasd_ccw_req *cqr; int rc = 0; + struct ccw1 *ccw; cqr = lcu->rsu_cqr; strncpy((char *) &cqr->magic, "ECKD", 4); ASCEBC((char *) &cqr->magic, 4); - cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RSCK; - cqr->cpaddr->flags = 0 ; - cqr->cpaddr->count = 16; - cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; + ccw = cqr->cpaddr; + ccw->cmd_code = DASD_ECKD_CCW_RSCK; + ccw->flags = 0 ; + ccw->count = 16; + ccw->cda = (__u32)(addr_t) cqr->data; ((char *)cqr->data)[0] = reason; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); @@ -855,12 +857,21 @@ void dasd_alias_handle_summary_unit_check(struct dasd_device *device, struct alias_lcu *lcu; char reason; struct dasd_eckd_private *private; + char *sense; private = (struct dasd_eckd_private *) device->private; - reason = irb->ecw[8]; - DEV_MESSAGE(KERN_WARNING, device, "%s %x", - "eckd handle summary unit check: reason", reason); + sense = dasd_get_sense(irb); + if (sense) { + reason = sense[8]; + DBF_DEV_EVENT(DBF_NOTICE, device, "%s %x", + "eckd handle summary unit check: reason", reason); + } else { + DBF_DEV_EVENT(DBF_WARNING, device, "%s", + "eckd handle summary unit check:" + " no reason code available"); + return; + } lcu = private->lcu; if (!lcu) { diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 34339902efb..da231a787ce 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -67,6 +67,8 @@ int dasd_probeonly = 0; /* is true, when probeonly mode is active */ int dasd_autodetect = 0; /* is true, when autodetection is active */ int dasd_nopav = 0; /* is true, when PAV is disabled */ EXPORT_SYMBOL_GPL(dasd_nopav); +int dasd_nofcx; /* disable High Performance Ficon */ +EXPORT_SYMBOL_GPL(dasd_nofcx); /* * char *dasd[] is intended to hold the ranges supplied by the dasd= statement @@ -272,6 +274,11 @@ dasd_parse_keyword( char *parsestring ) { } return residual_str; } + if (strncmp("nofcx", parsestring, length) == 0) { + dasd_nofcx = 1; + MESSAGE(KERN_INFO, "%s", "disable High Performance Ficon"); + return residual_str; + } if (strncmp("fixedbuffers", parsestring, length) == 0) { if (dasd_page_cache) return residual_str; diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 69f93e626fd..1e4c89b8b30 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -27,9 +27,12 @@ #include <asm/uaccess.h> #include <asm/cio.h> #include <asm/ccwdev.h> +#include <asm/itcw.h> #include "dasd_int.h" #include "dasd_eckd.h" +#include "../cio/chsc.h" + #ifdef PRINTK_HEADER #undef PRINTK_HEADER @@ -245,7 +248,8 @@ define_extent(struct ccw1 *ccw, struct DE_eckd_data *data, unsigned int trk, rc = check_XRC (ccw, data, device); break; default: - DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd); + DBF_DEV_EVENT(DBF_ERR, device, + "PFX LRE unknown opcode 0x%x", cmd); break; } @@ -289,30 +293,145 @@ static int check_XRC_on_prefix(struct PFX_eckd_data *pfxdata, return 0; /* switch on System Time Stamp - needed for XRC Support */ - pfxdata->define_extend.ga_extended |= 0x08; /* 'Time Stamp Valid' */ - pfxdata->define_extend.ga_extended |= 0x02; /* 'Extended Parameter' */ + pfxdata->define_extent.ga_extended |= 0x08; /* 'Time Stamp Valid' */ + pfxdata->define_extent.ga_extended |= 0x02; /* 'Extended Parameter' */ pfxdata->validity.time_stamp = 1; /* 'Time Stamp Valid' */ - rc = get_sync_clock(&pfxdata->define_extend.ep_sys_time); + rc = get_sync_clock(&pfxdata->define_extent.ep_sys_time); /* Ignore return code if sync clock is switched off. */ if (rc == -ENOSYS || rc == -EACCES) rc = 0; return rc; } -static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, - unsigned int trk, unsigned int totrk, int cmd, - struct dasd_device *basedev, struct dasd_device *startdev) +static void fill_LRE_data(struct LRE_eckd_data *data, unsigned int trk, + unsigned int rec_on_trk, int count, int cmd, + struct dasd_device *device, unsigned int reclen, + unsigned int tlf) +{ + struct dasd_eckd_private *private; + int sector; + int dn, d; + + private = (struct dasd_eckd_private *) device->private; + + memset(data, 0, sizeof(*data)); + sector = 0; + if (rec_on_trk) { + switch (private->rdc_data.dev_type) { + case 0x3390: + dn = ceil_quot(reclen + 6, 232); + d = 9 + ceil_quot(reclen + 6 * (dn + 1), 34); + sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8; + break; + case 0x3380: + d = 7 + ceil_quot(reclen + 12, 32); + sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7; + break; + } + } + data->sector = sector; + /* note: meaning of count depends on the operation + * for record based I/O it's the number of records, but for + * track based I/O it's the number of tracks + */ + data->count = count; + switch (cmd) { + case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: + data->operation.orientation = 0x3; + data->operation.operation = 0x03; + break; + case DASD_ECKD_CCW_READ_HOME_ADDRESS: + data->operation.orientation = 0x3; + data->operation.operation = 0x16; + break; + case DASD_ECKD_CCW_WRITE_RECORD_ZERO: + data->operation.orientation = 0x1; + data->operation.operation = 0x03; + data->count++; + break; + case DASD_ECKD_CCW_READ_RECORD_ZERO: + data->operation.orientation = 0x3; + data->operation.operation = 0x16; + data->count++; + break; + case DASD_ECKD_CCW_WRITE: + case DASD_ECKD_CCW_WRITE_MT: + case DASD_ECKD_CCW_WRITE_KD: + case DASD_ECKD_CCW_WRITE_KD_MT: + data->auxiliary.length_valid = 0x1; + data->length = reclen; + data->operation.operation = 0x01; + break; + case DASD_ECKD_CCW_WRITE_CKD: + case DASD_ECKD_CCW_WRITE_CKD_MT: + data->auxiliary.length_valid = 0x1; + data->length = reclen; + data->operation.operation = 0x03; + break; + case DASD_ECKD_CCW_WRITE_TRACK_DATA: + data->auxiliary.length_valid = 0x1; + data->length = reclen; /* not tlf, as one might think */ + data->operation.operation = 0x3F; + data->extended_operation = 0x23; + break; + case DASD_ECKD_CCW_READ: + case DASD_ECKD_CCW_READ_MT: + case DASD_ECKD_CCW_READ_KD: + case DASD_ECKD_CCW_READ_KD_MT: + data->auxiliary.length_valid = 0x1; + data->length = reclen; + data->operation.operation = 0x06; + break; + case DASD_ECKD_CCW_READ_CKD: + case DASD_ECKD_CCW_READ_CKD_MT: + data->auxiliary.length_valid = 0x1; + data->length = reclen; + data->operation.operation = 0x16; + break; + case DASD_ECKD_CCW_READ_COUNT: + data->operation.operation = 0x06; + break; + case DASD_ECKD_CCW_READ_TRACK_DATA: + data->auxiliary.length_valid = 0x1; + data->length = tlf; + data->operation.operation = 0x0C; + break; + case DASD_ECKD_CCW_ERASE: + data->length = reclen; + data->auxiliary.length_valid = 0x1; + data->operation.operation = 0x0b; + break; + default: + DBF_DEV_EVENT(DBF_ERR, device, + "fill LRE unknown opcode 0x%x", cmd); + BUG(); + } + set_ch_t(&data->seek_addr, + trk / private->rdc_data.trk_per_cyl, + trk % private->rdc_data.trk_per_cyl); + data->search_arg.cyl = data->seek_addr.cyl; + data->search_arg.head = data->seek_addr.head; + data->search_arg.record = rec_on_trk; +} + +static int prefix_LRE(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, + unsigned int trk, unsigned int totrk, int cmd, + struct dasd_device *basedev, struct dasd_device *startdev, + unsigned char format, unsigned int rec_on_trk, int count, + unsigned int blksize, unsigned int tlf) { struct dasd_eckd_private *basepriv, *startpriv; - struct DE_eckd_data *data; + struct DE_eckd_data *dedata; + struct LRE_eckd_data *lredata; u32 begcyl, endcyl; u16 heads, beghead, endhead; int rc = 0; basepriv = (struct dasd_eckd_private *) basedev->private; startpriv = (struct dasd_eckd_private *) startdev->private; - data = &pfxdata->define_extend; + dedata = &pfxdata->define_extent; + lredata = &pfxdata->locate_record; ccw->cmd_code = DASD_ECKD_CCW_PFX; ccw->flags = 0; @@ -321,10 +440,16 @@ static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, memset(pfxdata, 0, sizeof(*pfxdata)); /* prefix data */ - pfxdata->format = 0; + if (format > 1) { + DBF_DEV_EVENT(DBF_ERR, basedev, + "PFX LRE unknown format 0x%x", format); + BUG(); + return -EINVAL; + } + pfxdata->format = format; pfxdata->base_address = basepriv->ned->unit_addr; pfxdata->base_lss = basepriv->ned->ID; - pfxdata->validity.define_extend = 1; + pfxdata->validity.define_extent = 1; /* private uid is kept up to date, conf_data may be outdated */ if (startpriv->uid.type != UA_BASE_DEVICE) { @@ -344,42 +469,55 @@ static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, case DASD_ECKD_CCW_READ_KD: case DASD_ECKD_CCW_READ_KD_MT: case DASD_ECKD_CCW_READ_COUNT: - data->mask.perm = 0x1; - data->attributes.operation = basepriv->attrib.operation; + dedata->mask.perm = 0x1; + dedata->attributes.operation = basepriv->attrib.operation; + break; + case DASD_ECKD_CCW_READ_TRACK_DATA: + dedata->mask.perm = 0x1; + dedata->attributes.operation = basepriv->attrib.operation; + dedata->blk_size = 0; break; case DASD_ECKD_CCW_WRITE: case DASD_ECKD_CCW_WRITE_MT: case DASD_ECKD_CCW_WRITE_KD: case DASD_ECKD_CCW_WRITE_KD_MT: - data->mask.perm = 0x02; - data->attributes.operation = basepriv->attrib.operation; + dedata->mask.perm = 0x02; + dedata->attributes.operation = basepriv->attrib.operation; rc = check_XRC_on_prefix(pfxdata, basedev); break; case DASD_ECKD_CCW_WRITE_CKD: case DASD_ECKD_CCW_WRITE_CKD_MT: - data->attributes.operation = DASD_BYPASS_CACHE; + dedata->attributes.operation = DASD_BYPASS_CACHE; rc = check_XRC_on_prefix(pfxdata, basedev); break; case DASD_ECKD_CCW_ERASE: case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: case DASD_ECKD_CCW_WRITE_RECORD_ZERO: - data->mask.perm = 0x3; - data->mask.auth = 0x1; - data->attributes.operation = DASD_BYPASS_CACHE; + dedata->mask.perm = 0x3; + dedata->mask.auth = 0x1; + dedata->attributes.operation = DASD_BYPASS_CACHE; rc = check_XRC_on_prefix(pfxdata, basedev); break; - default: - DEV_MESSAGE(KERN_ERR, basedev, "unknown opcode 0x%x", cmd); + case DASD_ECKD_CCW_WRITE_TRACK_DATA: + dedata->mask.perm = 0x02; + dedata->attributes.operation = basepriv->attrib.operation; + dedata->blk_size = blksize; + rc = check_XRC_on_prefix(pfxdata, basedev); break; + default: + DBF_DEV_EVENT(DBF_ERR, basedev, + "PFX LRE unknown opcode 0x%x", cmd); + BUG(); + return -EINVAL; } - data->attributes.mode = 0x3; /* ECKD */ + dedata->attributes.mode = 0x3; /* ECKD */ if ((basepriv->rdc_data.cu_type == 0x2105 || basepriv->rdc_data.cu_type == 0x2107 || basepriv->rdc_data.cu_type == 0x1750) && !(basepriv->uses_cdl && trk < 2)) - data->ga_extended |= 0x40; /* Regular Data Format Mode */ + dedata->ga_extended |= 0x40; /* Regular Data Format Mode */ heads = basepriv->rdc_data.trk_per_cyl; begcyl = trk / heads; @@ -388,8 +526,8 @@ static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, endhead = totrk % heads; /* check for sequential prestage - enhance cylinder range */ - if (data->attributes.operation == DASD_SEQ_PRESTAGE || - data->attributes.operation == DASD_SEQ_ACCESS) { + if (dedata->attributes.operation == DASD_SEQ_PRESTAGE || + dedata->attributes.operation == DASD_SEQ_ACCESS) { if (endcyl + basepriv->attrib.nr_cyl < basepriv->real_cyl) endcyl += basepriv->attrib.nr_cyl; @@ -397,11 +535,25 @@ static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, endcyl = (basepriv->real_cyl - 1); } - set_ch_t(&data->beg_ext, begcyl, beghead); - set_ch_t(&data->end_ext, endcyl, endhead); + set_ch_t(&dedata->beg_ext, begcyl, beghead); + set_ch_t(&dedata->end_ext, endcyl, endhead); + + if (format == 1) { + fill_LRE_data(lredata, trk, rec_on_trk, count, cmd, + basedev, blksize, tlf); + } + return rc; } +static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, + unsigned int trk, unsigned int totrk, int cmd, + struct dasd_device *basedev, struct dasd_device *startdev) +{ + return prefix_LRE(ccw, pfxdata, trk, totrk, cmd, basedev, startdev, + 0, 0, 0, 0, 0); +} + static void locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, unsigned int trk, unsigned int rec_on_trk, int no_rec, int cmd, @@ -845,7 +997,8 @@ static int dasd_eckd_read_features(struct dasd_device *device) /* * Build CP for Perform Subsystem Function - SSC. */ -static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device) +static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device, + int enable_pav) { struct dasd_ccw_req *cqr; struct dasd_psf_ssc_data *psf_ssc_data; @@ -862,9 +1015,11 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device) } psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data; psf_ssc_data->order = PSF_ORDER_SSC; - psf_ssc_data->suborder = 0x88; - psf_ssc_data->reserved[0] = 0x88; - + psf_ssc_data->suborder = 0x40; + if (enable_pav) { + psf_ssc_data->suborder |= 0x88; + psf_ssc_data->reserved[0] = 0x88; + } ccw = cqr->cpaddr; ccw->cmd_code = DASD_ECKD_CCW_PSF; ccw->cda = (__u32)(addr_t)psf_ssc_data; @@ -885,12 +1040,12 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device) * call might change behaviour of DASD devices. */ static int -dasd_eckd_psf_ssc(struct dasd_device *device) +dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav) { struct dasd_ccw_req *cqr; int rc; - cqr = dasd_eckd_build_psf_ssc(device); + cqr = dasd_eckd_build_psf_ssc(device, enable_pav); if (IS_ERR(cqr)) return PTR_ERR(cqr); @@ -909,12 +1064,13 @@ static int dasd_eckd_validate_server(struct dasd_device *device) { int rc; struct dasd_eckd_private *private; + int enable_pav; - /* Currently PAV is the only reason to 'validate' server on LPAR */ if (dasd_nopav || MACHINE_IS_VM) - return 0; - - rc = dasd_eckd_psf_ssc(device); + enable_pav = 0; + else + enable_pav = 1; + rc = dasd_eckd_psf_ssc(device, enable_pav); /* may be requested feature is not available on server, * therefore just report error and go ahead */ private = (struct dasd_eckd_private *) device->private; @@ -1504,40 +1660,41 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device, struct irb *irb) { char mask; + char *sense = NULL; /* 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 ((scsw_dstat(&irb->scsw) & mask) == mask) { dasd_generic_handle_state_change(device); return; } /* summary unit check */ - if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) && + if ((scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK) && (irb->ecw[7] == 0x0D)) { dasd_alias_handle_summary_unit_check(device, irb); return; } - + sense = dasd_get_sense(irb); /* service information message SIM */ - if (irb->esw.esw0.erw.cons && !(irb->ecw[27] & DASD_SENSE_BIT_0) && - ((irb->ecw[6] & DASD_SIM_SENSE) == DASD_SIM_SENSE)) { - dasd_3990_erp_handle_sim(device, irb->ecw); + if (sense && !(sense[27] & DASD_SENSE_BIT_0) && + ((sense[6] & DASD_SIM_SENSE) == DASD_SIM_SENSE)) { + dasd_3990_erp_handle_sim(device, sense); dasd_schedule_device_bh(device); return; } - if ((irb->scsw.cmd.cc == 1) && - (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) && - (irb->scsw.cmd.actl & SCSW_ACTL_START_PEND) && - (irb->scsw.cmd.stctl & SCSW_STCTL_STATUS_PEND)) { + if ((scsw_cc(&irb->scsw) == 1) && + (scsw_fctl(&irb->scsw) & SCSW_FCTL_START_FUNC) && + (scsw_actl(&irb->scsw) & SCSW_ACTL_START_PEND) && + (scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND)) { /* fake irb do nothing, they are handled elsewhere */ dasd_schedule_device_bh(device); return; } - if (!(irb->esw.esw0.erw.cons)) { + if (!sense) { /* just report other unsolicited interrupts */ DEV_MESSAGE(KERN_ERR, device, "%s", "unsolicited interrupt received"); @@ -1552,9 +1709,19 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device, return; }; -static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, + +static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( + struct dasd_device *startdev, struct dasd_block *block, - struct request *req) + struct request *req, + sector_t first_rec, + sector_t last_rec, + sector_t first_trk, + sector_t last_trk, + unsigned int first_offs, + unsigned int last_offs, + unsigned int blk_per_trk, + unsigned int blksize) { struct dasd_eckd_private *private; unsigned long *idaws; @@ -1564,11 +1731,9 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, struct req_iterator iter; struct bio_vec *bv; char *dst; - unsigned int blksize, blk_per_trk, off; + unsigned int off; int count, cidaw, cplength, datasize; - sector_t recid, first_rec, last_rec; - sector_t first_trk, last_trk; - unsigned int first_offs, last_offs; + sector_t recid; unsigned char cmd, rcmd; int use_prefix; struct dasd_device *basedev; @@ -1581,15 |