Commit f3eb5384 authored by Stefan Weinhuber's avatar Stefan Weinhuber Committed by Martin Schwidefsky

[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: default avatarStefan Weinhuber <wein@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent b44b0ab3
...@@ -44,24 +44,18 @@ idal_is_needed(void *vaddr, unsigned int length) ...@@ -44,24 +44,18 @@ idal_is_needed(void *vaddr, unsigned int length)
/* /*
* Return the number of idal words needed for an address/length pair. * Return the number of idal words needed for an address/length pair.
*/ */
static inline unsigned int static inline unsigned int idal_nr_words(void *vaddr, unsigned int length)
idal_nr_words(void *vaddr, unsigned int length)
{ {
#ifdef __s390x__ return ((__pa(vaddr) & (IDA_BLOCK_SIZE-1)) + length +
if (idal_is_needed(vaddr, length)) (IDA_BLOCK_SIZE-1)) >> IDA_SIZE_LOG;
return ((__pa(vaddr) & (IDA_BLOCK_SIZE-1)) + length +
(IDA_BLOCK_SIZE-1)) >> IDA_SIZE_LOG;
#endif
return 0;
} }
/* /*
* Create the list of idal words for an address/length pair. * Create the list of idal words for an address/length pair.
*/ */
static inline unsigned long * static inline unsigned long *idal_create_words(unsigned long *idaws,
idal_create_words(unsigned long *idaws, void *vaddr, unsigned int length) void *vaddr, unsigned int length)
{ {
#ifdef __s390x__
unsigned long paddr; unsigned long paddr;
unsigned int cidaw; unsigned int cidaw;
...@@ -74,7 +68,6 @@ idal_create_words(unsigned long *idaws, void *vaddr, unsigned int length) ...@@ -74,7 +68,6 @@ idal_create_words(unsigned long *idaws, void *vaddr, unsigned int length)
paddr += IDA_BLOCK_SIZE; paddr += IDA_BLOCK_SIZE;
*idaws++ = paddr; *idaws++ = paddr;
} }
#endif
return idaws; return idaws;
} }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <asm/ebcdic.h> #include <asm/ebcdic.h>
#include <asm/idals.h> #include <asm/idals.h>
#include <asm/todclk.h> #include <asm/todclk.h>
#include <asm/itcw.h>
/* This is ugly... */ /* This is ugly... */
#define PRINTK_HEADER "dasd:" #define PRINTK_HEADER "dasd:"
...@@ -852,8 +853,13 @@ int dasd_start_IO(struct dasd_ccw_req *cqr) ...@@ -852,8 +853,13 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
cqr->startclk = get_clock(); cqr->startclk = get_clock();
cqr->starttime = jiffies; cqr->starttime = jiffies;
cqr->retries--; cqr->retries--;
rc = ccw_device_start(device->cdev, cqr->cpaddr, (long) cqr, if (cqr->cpmode == 1) {
cqr->lpm, 0); 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) { switch (rc) {
case 0: case 0:
cqr->status = DASD_CQR_IN_IO; cqr->status = DASD_CQR_IN_IO;
...@@ -881,9 +887,12 @@ int dasd_start_IO(struct dasd_ccw_req *cqr) ...@@ -881,9 +887,12 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
" retry on all pathes"); " retry on all pathes");
break; break;
case -ENODEV: case -ENODEV:
DBF_DEV_EVENT(DBF_DEBUG, device, "%s",
"start_IO: -ENODEV device gone, retry");
break;
case -EIO: case -EIO:
DBF_DEV_EVENT(DBF_ERR, device, "%s", DBF_DEV_EVENT(DBF_ERR, device, "%s",
"start_IO: device gone, retry"); "start_IO: -EIO device gone, retry");
break; break;
default: default:
DEV_MESSAGE(KERN_ERR, device, DEV_MESSAGE(KERN_ERR, device,
...@@ -1015,9 +1024,9 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, ...@@ -1015,9 +1024,9 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
/* check for unsolicited interrupts */ /* check for unsolicited interrupts */
cqr = (struct dasd_ccw_req *) intparm; cqr = (struct dasd_ccw_req *) intparm;
if (!cqr || ((irb->scsw.cmd.cc == 1) && if (!cqr || ((scsw_cc(&irb->scsw) == 1) &&
(irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) && (scsw_fctl(&irb->scsw) & SCSW_FCTL_START_FUNC) &&
(irb->scsw.cmd.stctl & SCSW_STCTL_STATUS_PEND))) { (scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND))) {
if (cqr && cqr->status == DASD_CQR_IN_IO) if (cqr && cqr->status == DASD_CQR_IN_IO)
cqr->status = DASD_CQR_QUEUED; cqr->status = DASD_CQR_QUEUED;
device = dasd_device_from_cdev_locked(cdev); device = dasd_device_from_cdev_locked(cdev);
...@@ -1040,7 +1049,7 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, ...@@ -1040,7 +1049,7 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
/* Check for clear pending */ /* Check for clear pending */
if (cqr->status == DASD_CQR_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; cqr->status = DASD_CQR_CLEARED;
dasd_device_clear_timer(device); dasd_device_clear_timer(device);
wake_up(&dasd_flush_wq); wake_up(&dasd_flush_wq);
...@@ -1048,7 +1057,7 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, ...@@ -1048,7 +1057,7 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
return; 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) { if (cqr->status != DASD_CQR_IN_IO) {
MESSAGE(KERN_DEBUG, MESSAGE(KERN_DEBUG,
"invalid status: bus_id %s, status %02x", "invalid status: bus_id %s, status %02x",
...@@ -1059,8 +1068,8 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, ...@@ -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); ((irb->scsw.cmd.cstat << 8) | irb->scsw.cmd.dstat), cqr);
next = NULL; next = NULL;
expires = 0; expires = 0;
if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) && if (scsw_dstat(&irb->scsw) == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
irb->scsw.cmd.cstat == 0 && !irb->esw.esw0.erw.cons) { scsw_cstat(&irb->scsw) == 0) {
/* request was completed successfully */ /* request was completed successfully */
cqr->status = DASD_CQR_SUCCESS; cqr->status = DASD_CQR_SUCCESS;
cqr->stopclk = now; cqr->stopclk = now;
...@@ -1991,8 +2000,11 @@ static void dasd_setup_queue(struct dasd_block *block) ...@@ -1991,8 +2000,11 @@ static void dasd_setup_queue(struct dasd_block *block)
blk_queue_max_sectors(block->request_queue, max); blk_queue_max_sectors(block->request_queue, max);
blk_queue_max_phys_segments(block->request_queue, -1L); blk_queue_max_phys_segments(block->request_queue, -1L);
blk_queue_max_hw_segments(block->request_queue, -1L); blk_queue_max_hw_segments(block->request_queue, -1L);
blk_queue_max_segment_size(block->request_queue, -1L); /* with page sized segments we can translate each segement into
blk_queue_segment_boundary(block->request_queue, -1L); * 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); 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, ...@@ -2432,6 +2444,40 @@ int dasd_generic_read_dev_chars(struct dasd_device *device, char *magic,
} }
EXPORT_SYMBOL_GPL(dasd_generic_read_dev_chars); 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) static int __init dasd_init(void)
{ {
int rc; int rc;
......
...@@ -1561,6 +1561,13 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) ...@@ -1561,6 +1561,13 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
cqr = cqr->refers; 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 */ /* for imprecise ending just do default erp */
if (sense[1] & 0x01) { if (sense[1] & 0x01) {
...@@ -1599,7 +1606,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) ...@@ -1599,7 +1606,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
oldccw = cqr->cpaddr; oldccw = cqr->cpaddr;
if (oldccw->cmd_code == DASD_ECKD_CCW_PFX) { if (oldccw->cmd_code == DASD_ECKD_CCW_PFX) {
PFX_data = cqr->data; PFX_data = cqr->data;
memcpy(DE_data, &PFX_data->define_extend, memcpy(DE_data, &PFX_data->define_extent,
sizeof(struct DE_eckd_data)); sizeof(struct DE_eckd_data));
} else } else
memcpy(DE_data, cqr->data, sizeof(struct DE_eckd_data)); 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) ...@@ -1712,6 +1719,13 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
cqr = cqr->refers; 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 */ /* for imprecise ending just do default erp */
if (sense[1] & 0x01) { if (sense[1] & 0x01) {
...@@ -2171,7 +2185,7 @@ dasd_3990_erp_control_check(struct dasd_ccw_req *erp) ...@@ -2171,7 +2185,7 @@ dasd_3990_erp_control_check(struct dasd_ccw_req *erp)
{ {
struct dasd_device *device = erp->startdev; 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)) { | SCHN_STAT_CHN_CTRL_CHK)) {
DEV_MESSAGE(KERN_DEBUG, device, "%s", DEV_MESSAGE(KERN_DEBUG, device, "%s",
"channel or interface control check"); "channel or interface control check");
...@@ -2193,21 +2207,23 @@ dasd_3990_erp_control_check(struct dasd_ccw_req *erp) ...@@ -2193,21 +2207,23 @@ dasd_3990_erp_control_check(struct dasd_ccw_req *erp)
* erp_new contens was possibly modified * erp_new contens was possibly modified
*/ */
static struct dasd_ccw_req * 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; struct dasd_ccw_req *erp_new = NULL;
/* sense data are located in the refers record of the */ char *sense;
/* already set up new ERP ! */
char *sense = erp->refers->irb.ecw;
/* if this problem occured on an alias retry on base */ /* if this problem occured on an alias retry on base */
erp_new = dasd_3990_erp_inspect_alias(erp); erp_new = dasd_3990_erp_inspect_alias(erp);
if (erp_new) if (erp_new)
return erp_new; return erp_new;
/* check if no concurrent sens is available */ /* sense data are located in the refers record of the
if (!erp->refers->irb.esw.esw0.erw.cons) * 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); erp_new = dasd_3990_erp_control_check(erp);
/* distinguish between 24 and 32 byte sense data */ /* distinguish between 24 and 32 byte sense data */
else if (sense[27] & DASD_SENSE_BIT_0) { else if (sense[27] & DASD_SENSE_BIT_0) {
...@@ -2231,7 +2247,11 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp) ...@@ -2231,7 +2247,11 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp)
* DESCRIPTION * DESCRIPTION
* This funtion adds an additional request block (ERP) to the head of * This funtion adds an additional request block (ERP) to the head of
* the given cqr (or erp). * 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 * PARAMETER
* cqr head of the current ERP-chain (or single cqr if * cqr head of the current ERP-chain (or single cqr if
...@@ -2239,17 +2259,27 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp) ...@@ -2239,17 +2259,27 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp)
* RETURN VALUES * RETURN VALUES
* erp pointer to new ERP-chain head * erp pointer to new ERP-chain head
*/ */
static struct dasd_ccw_req * static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr)
dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr)
{ {
struct dasd_device *device = cqr->startdev; struct dasd_device *device = cqr->startdev;
struct ccw1 *ccw; struct ccw1 *ccw;
/* allocate additional request block */
struct dasd_ccw_req *erp; 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 (IS_ERR(erp)) {
if (cqr->retries <= 0) { if (cqr->retries <= 0) {
DEV_MESSAGE(KERN_ERR, device, "%s", DEV_MESSAGE(KERN_ERR, device, "%s",
...@@ -2266,13 +2296,24 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) ...@@ -2266,13 +2296,24 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr)
return cqr; return cqr;
} }
/* initialize request with default TIC to current ERP/CQR */ if (cqr->cpmode == 1) {
ccw = erp->cpaddr; /* make a shallow copy of the original tcw but set new tsb */
ccw->cmd_code = CCW_CMD_NOOP; erp->cpmode = 1;
ccw->flags = CCW_FLAG_CC; erp->cpaddr = erp->data;
ccw++; tcw = erp->data;
ccw->cmd_code = CCW_CMD_TIC; tsb = (struct tsb *) &tcw[1];
ccw->cda = (long)(cqr->cpaddr); *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->function = dasd_3990_erp_add_erp;
erp->refers = cqr; erp->refers = cqr;
erp->startdev = device; erp->startdev = device;
...@@ -2282,7 +2323,6 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) ...@@ -2282,7 +2323,6 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr)
erp->expires = 0; erp->expires = 0;
erp->retries = 256; erp->retries = 256;
erp->buildclk = get_clock(); erp->buildclk = get_clock();
erp->status = DASD_CQR_FILLED; erp->status = DASD_CQR_FILLED;
return erp; return erp;
...@@ -2340,28 +2380,33 @@ dasd_3990_erp_additional_erp(struct dasd_ccw_req * cqr) ...@@ -2340,28 +2380,33 @@ dasd_3990_erp_additional_erp(struct dasd_ccw_req * cqr)
* match 'boolean' for match found * match 'boolean' for match found
* returns 1 if match found, otherwise 0. * returns 1 if match found, otherwise 0.
*/ */
static int static int dasd_3990_erp_error_match(struct dasd_ccw_req *cqr1,
dasd_3990_erp_error_match(struct dasd_ccw_req *cqr1, struct dasd_ccw_req *cqr2) struct dasd_ccw_req *cqr2)
{ {
char *sense1, *sense2;
if (cqr1->startdev != cqr2->startdev) if (cqr1->startdev != cqr2->startdev)
return 0; return 0;
if (cqr1->irb.esw.esw0.erw.cons != cqr2->irb.esw.esw0.erw.cons) sense1 = dasd_get_sense(&cqr1->irb);
return 0; sense2 = dasd_get_sense(&cqr2->irb);
if ((cqr1->irb.esw.esw0.erw.cons == 0) && /* one request has sense data, the other not -> no match, return 0 */
(cqr2->irb.esw.esw0.erw.cons == 0)) { if (!sense1 != !sense2)
if ((cqr1->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK | return 0;
SCHN_STAT_CHN_CTRL_CHK)) == /* no sense data in both cases -> check cstat for IFCC */
(cqr2->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK | if (!sense1 && !sense2) {
SCHN_STAT_CHN_CTRL_CHK))) 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*/ return 1; /* match with ifcc*/
} }
/* check sense data; byte 0-2,25,27 */ /* check sense data; byte 0-2,25,27 */
if (!((memcmp (cqr1->irb.ecw, cqr2->irb.ecw, 3) == 0) && if (!(sense1 && sense2 &&
(cqr1->irb.ecw[27] == cqr2->irb.ecw[27]) && (memcmp(sense1, sense2, 3) == 0) &&
(cqr1->irb.ecw[25] == cqr2->irb.ecw[25]))) { (sense1[27] == sense2[27]) &&
(sense1[25] == sense2[25]))) {
return 0; /* sense doesn't match */ return 0; /* sense doesn't match */
} }
...@@ -2434,7 +2479,7 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) ...@@ -2434,7 +2479,7 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp)
{ {
struct dasd_device *device = erp->startdev; struct dasd_device *device = erp->startdev;
char *sense = erp->irb.ecw; char *sense = dasd_get_sense(&erp->irb);
/* check for 24 byte sense ERP */ /* check for 24 byte sense ERP */
if ((erp->function == dasd_3990_erp_bus_out) || if ((erp->function == dasd_3990_erp_bus_out) ||
...@@ -2449,7 +2494,7 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) ...@@ -2449,7 +2494,7 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp)
/* prepare erp for retry on different channel path */ /* prepare erp for retry on different channel path */
erp = dasd_3990_erp_action_1(erp); 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 /* issue a Diagnostic Control command with an
* Inhibit Write subcommand */ * Inhibit Write subcommand */
...@@ -2479,10 +2524,11 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) ...@@ -2479,10 +2524,11 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp)
} }
/* check for 32 byte sense ERP */ /* check for 32 byte sense ERP */
} else if ((erp->function == dasd_3990_erp_compound_retry) || } else if (sense &&
(erp->function == dasd_3990_erp_compound_path) || ((erp->function == dasd_3990_erp_compound_retry) ||
(erp->function == dasd_3990_erp_compound_code) || (erp->function == dasd_3990_erp_compound_path) ||
(erp->function == dasd_3990_erp_compound_config)) { (erp->function == dasd_3990_erp_compound_code) ||
(erp->function == dasd_3990_erp_compound_config))) {
erp = dasd_3990_erp_compound(erp, sense); erp = dasd_3990_erp_compound(erp, sense);
...@@ -2548,18 +2594,19 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, ...@@ -2548,18 +2594,19 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
if (erp->retries > 0) { if (erp->retries > 0) {
char *sense = erp->refers->irb.ecw; char *sense = dasd_get_sense(&erp->refers->irb);
/* check for special retries */ /* 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); 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); 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); erp = dasd_3990_erp_int_req(erp);
...@@ -2622,8 +2669,8 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) ...@@ -2622,8 +2669,8 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
} }
/* double-check if current erp/cqr was successful */ /* double-check if current erp/cqr was successful */
if ((cqr->irb.scsw.cmd.cstat == 0x00) && if ((scsw_cstat(&cqr->irb.scsw) == 0x00) &&
(cqr->irb.scsw.cmd.dstat == (scsw_dstat(&cqr->irb.scsw) ==
(DEV_STAT_CHN_END | DEV_STAT_DEV_END))) { (DEV_STAT_CHN_END | DEV_STAT_DEV_END))) {
DEV_MESSAGE(KERN_DEBUG, device, DEV_MESSAGE(KERN_DEBUG, device,
......
...@@ -646,14 +646,16 @@ static int reset_summary_unit_check(struct alias_lcu *lcu, ...@@ -646,14 +646,16 @@ static int reset_summary_unit_check(struct alias_lcu *lcu,
{ {
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
int rc = 0; int rc = 0;
struct ccw1 *ccw;
cqr = lcu->rsu_cqr; cqr = lcu->rsu_cqr;
strncpy((char *) &cqr->magic, "ECKD", 4); strncpy((char *) &cqr->magic, "ECKD", 4);
ASCEBC((char *) &cqr->magic, 4); ASCEBC((char *) &cqr->magic, 4);
cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RSCK; ccw = cqr->cpaddr;
cqr->cpaddr->flags = 0 ; ccw->cmd_code = DASD_ECKD_CCW_RSCK;
cqr->cpaddr->count = 16; ccw->flags = 0 ;
cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; ccw->count = 16;
ccw->cda = (__u32)(addr_t) cqr->data;
((char *)cqr->data)[0] = reason; ((char *)cqr->data)[0] = reason;
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
...@@ -855,12 +857,21 @@ void dasd_alias_handle_summary_unit_check(struct dasd_device *device, ...@@ -855,12 +857,21 @@ void dasd_alias_handle_summary_unit_check(struct dasd_device *device,
struct alias_lcu *lcu; struct alias_lcu *lcu;
char reason; char reason;
struct dasd_eckd_private *private; struct dasd_eckd_private *private;
char *sense;
private = (struct dasd_eckd_private *) device->private; private = (struct dasd_eckd_private *) device->private;
reason = irb->ecw[8]; sense = dasd_get_sense(irb);
DEV_MESSAGE(KERN_WARNING, device, "%s %x", if (sense) {
"eckd handle summary unit check: reason", reason); 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; lcu = private->lcu;
if (!lcu) { if (!lcu) {
......
...@@ -67,6 +67,8 @@ int dasd_probeonly = 0; /* is true, when probeonly mode is active */ ...@@ -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_autodetect = 0; /* is true, when autodetection is active */
int dasd_nopav = 0; /* is true, when PAV is disabled */ int dasd_nopav = 0; /* is true, when PAV is disabled */
EXPORT_SYMBOL_GPL(dasd_nopav); 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 * char *dasd[] is intended to hold the ranges supplied by the dasd= statement
...@@ -272,6 +274,11 @@ dasd_parse_keyword( char *parsestring ) { ...@@ -272,6 +274,11 @@ dasd_parse_keyword( char *parsestring ) {
} }
return residual_str; 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 (strncmp("fixedbuffers", parsestring, length) == 0) {
if (dasd_page_cache) if (dasd_page_cache)
return residual_str; return residual_str;
......
This diff is collapsed.
...@@ -38,8 +38,11 @@ ...@@ -38,8 +38,11 @@
#define DASD_ECKD_CCW_RELEASE 0x94 #define DASD_ECKD_CCW_RELEASE 0x94
#define DASD_ECKD_CCW_READ_CKD_MT 0x9e #define DASD_ECKD_CCW_READ_CKD_MT 0x9e
#define DASD_ECKD_CCW_WRITE_CKD_MT 0x9d #define DASD_ECKD_CCW_WRITE_CKD_MT 0x9d
#define DASD_ECKD_CCW_WRITE_TRACK_DATA 0xA5
#define DASD_ECKD_CCW_READ_TRACK_DATA 0xA6
#define DASD_ECKD_CCW_RESERVE 0xB4 #define DASD_ECKD_CCW_RESERVE 0xB4
#define DASD_ECKD_CCW_PFX 0xE7 #define DASD_ECKD_CCW_PFX 0xE7
#define DASD_ECKD_CCW_PFX_READ 0xEA
#define DASD_ECKD_CCW_RSCK 0xF9 #define DASD_ECKD_CCW_RSCK 0xF9
/* /*
...@@ -123,7 +126,9 @@ struct DE_eckd_data { ...@@ -123,7 +126,9 @@ struct DE_eckd_data {
unsigned long long ep_sys_time; /* Ext Parameter - System Time Stamp */ unsigned long long ep_sys_time; /* Ext Parameter - System Time Stamp */
__u8 ep_format; /* Extended Parameter format byte */ __u8 ep_format; /* Extended Parameter format byte */
__u8 ep_prio; /* Extended Parameter priority I/O byte */ __u8 ep_prio; /* Extended Parameter priority I/O byte */
__u8 ep_reserved[6]; /* Extended Parameter Reserved */ __u8 ep_reserved1; /* Extended Parameter Reserved */
__u8 ep_rec_per_track; /* Number of records on a track */
__u8 ep_reserved[4]; /* Extended Parameter Reserved */
} __attribute__ ((packed)); } __attribute__ ((packed));
struct LO_eckd_data { struct LO_eckd_data {
...@@ -144,11 +149,37 @@ struct LO_eckd_data { ...@@ -144,11 +149,37 @@ struct LO_eckd_data {
__u16 length; __u16 length;
} __attribute__ ((packed)); } __attribute__ ((packed));
struct LRE_eckd_data {
struct {
unsigned char orientation:2;
unsigned char operation:6;
} __attribute__ ((packed)) operation;
struct {
unsigned char length_valid:1;
unsigned char length_scope:1;
unsigned char imbedded_ccw_valid:1;
unsigned char check_bytes:2;
unsigned char imbedded_count_valid:1;
unsigned char reserved:1;
unsigned char read_count_suffix:1;
} __attribute__ ((packed)) auxiliary;
__u8 imbedded_ccw;
__u8 count;
struct ch_t seek_addr;
struct chr_t search_arg;
__u8 sector;
__u16 length;
__u8 imbedded_count;
__u8 extended_operation;
__u16 extended_parameter_length;
__u8 extended_parameter[0];
} __attribute__ ((packed));
/* Prefix data for format 0x00 and 0x01 */ /* Prefix data for format 0x00 and 0x01 */
struct PFX_eckd_data { struct PFX_eckd_data {
unsigned char format; unsigned char format;
struct { struct {
unsigned char define_extend:1; unsigned char define_extent:1;
unsigned char time_stamp:1; unsigned char time_stamp:1;
unsigned char verify_base:1; unsigned char verify_base:1;
unsigned char hyper_pav:1; unsigned char hyper_pav:1;
...@@ -158,9 +189,8 @@ struct PFX_eckd_data { ...@@ -158,9 +189,8 @@ struct PFX_eckd_data {
__u8 aux; __u8 aux;
__u8 base_lss; __u8 base_lss;
__u8 reserved[7]; __u8 reserved[7];
struct DE_eckd_data define_extend; struct DE_eckd_data define_extent;
struct LO_eckd_data locate_record; struct LRE_eckd_data locate_record;
__u8 LO_extended_data[4];
} __attribute__ ((packed)); } __attribute__ ((packed));
struct dasd_eckd_characteristics { struct dasd_eckd_characteristics {
......
...@@ -297,11 +297,12 @@ static void dasd_eer_write_standard_trigger(struct dasd_device *device, ...@@ -297,11 +297,12 @@ static void dasd_eer_write_standard_trigger(struct dasd_device *device,
struct dasd_eer_header header; struct dasd_eer_header header;
unsigned long flags; unsigned long flags;
struct eerbuffer *eerb; struct eerbuffer *eerb;
char *sense;
/* go through cqr chain and count the valid sense data sets */ /* go through cqr chain and count the valid sense data sets */
data_size = 0; data_size = 0;
for (temp_cqr = cqr; temp_cqr; temp_cqr = temp_cqr->refers) for (temp_cqr = cqr; temp_cqr; temp_cqr = temp_cqr->refers)
if (temp_cqr->irb.esw.esw0.erw.cons) if (dasd_get_sense(&temp_cqr->irb))
data_size += 32; data_size += 32;
header.total_size = sizeof(header) + data_size + 4; /* "EOR" */ header.total_size = sizeof(header) + data_size + 4; /* "EOR" */
...@@ -316,9 +317,11 @@ static void dasd_eer_write_standard_trigger(struct dasd_device *device, ...@@ -316,9 +317,11 @@ static void dasd_eer_write_standard_trigger(struct dasd_device *device,
list_for_each_entry(eerb, &bufferlist, list) { list_for_each_entry(eerb, &bufferlist, list) {
dasd_eer_start_record(eerb, header.total_size); dasd_eer_start_record(eerb, header.total_size);
dasd_eer_write_buffer(eerb, (char *) &header, sizeof(header)); dasd_eer_write_buffer(eerb, (char *) &header, sizeof(header));
for (temp_cqr = cqr; temp_cqr; temp_cqr = temp_cqr->refers) for (temp_cqr = cqr; temp_cqr; temp_cqr = temp_cqr->refers) {
if (temp_cqr->irb.esw.esw0.erw.cons) sense = dasd_get_sense(&temp_cqr->irb);
dasd_eer_write_buffer(eerb, cqr->irb.ecw, 32); if (sense)
dasd_eer_write_buffer(eerb, sense, 32);
}
dasd_eer_write_buffer(eerb, "EOR", 4); dasd_eer_write_buffer(eerb, "EOR", 4);
} }
spin_unlock_irqrestore(&bufferlock, flags); spin_unlock_irqrestore(&bufferlock, flags);
...@@ -451,6 +454,7 @@ int dasd_eer_enable(struct dasd_device *device) ...@@ -451,6 +454,7 @@ int dasd_eer_enable(struct dasd_device *device)
{ {
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
unsigned long flags; unsigned long flags;
struct ccw1 *ccw;
if (device->eer_cqr) if (device->eer_cqr)
return 0; return 0;
...@@ -468,10 +472,11 @@ int dasd_eer_enable(struct dasd_device *device) ...@@ -468,10 +472,11 @@ int dasd_eer_enable(struct dasd_device *device)
cqr->expires = 10 * HZ; cqr->expires = 10 * HZ;
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
cqr->cpaddr->cmd_code = DASD_ECKD_CCW_SNSS; ccw = cqr->cpaddr;
cqr->cpaddr->count = SNSS_DATA_SIZE; ccw->cmd_code = DASD_ECKD_CCW_SNSS;
cqr->cpaddr->flags = 0; ccw->count = SNSS_DATA_SIZE;
cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; ccw->flags = 0;
ccw->cda = (__u32)(addr_t) cqr->data;
cqr->buildclk = get_clock(); cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED; cqr->status = DASD_CQR_FILLED;
......
...@@ -157,7 +157,8 @@ struct dasd_ccw_req { ...@@ -157,7 +157,8 @@ struct dasd_ccw_req {
struct dasd_block *block; /* the originating block device */ struct dasd_block *block; /* the originating block device */
struct dasd_device *memdev; /* the device used to allocate this */ struct dasd_device *memdev; /* the device used to allocate this */
struct dasd_device *startdev; /* device the request is started on */ struct dasd_device *startdev; /* device the request is started on */
struct ccw1 *cpaddr; /* address of channel program */ void *cpaddr; /* address of ccw or tcw */
unsigned char cpmode; /* 0 = cmd mode, 1 = itcw */
char status; /* status of this request */ char status; /* status of this request */
short retries; /* A retry counter */ short retries; /* A retry counter */
unsigned long flags; /* flags of this request */ unsigned long flags; /* flags of this request */
...@@ -573,12 +574,14 @@ int dasd_generic_notify(struct ccw_device *, int); ...@@ -573,12 +574,14 @@ int dasd_generic_notify(struct ccw_device *, int);
void dasd_generic_handle_state_change(struct dasd_device *); void dasd_generic_handle_state_change(struct dasd_device *);
int dasd_generic_read_dev_chars(struct dasd_device *, char *, void **, int); int dasd_generic_read_dev_chars(struct dasd_device *, char *, void **, int);
char *dasd_get_sense(struct irb *);
/* externals in dasd_devmap.c */ /* externals in dasd_devmap.c */
extern int dasd_max_devindex; extern int dasd_max_devindex;
extern int dasd_probeonly; extern int dasd_probeonly;
extern int dasd_autodetect; extern int dasd_autodetect;
extern int dasd_nopav; extern int dasd_nopav;
extern int dasd_nofcx;
int dasd_devmap_init(void); int dasd_devmap_init(void);
void dasd_devmap_exit(void); void dasd_devmap_exit(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment