mirror of https://gitee.com/openkylin/linux.git
[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>
This commit is contained in:
parent
b44b0ab3ba
commit
f3eb5384cf
|
@ -44,24 +44,18 @@ idal_is_needed(void *vaddr, unsigned int length)
|
|||
/*
|
||||
* Return the number of idal words needed for an address/length pair.
|
||||
*/
|
||||
static inline unsigned int
|
||||
idal_nr_words(void *vaddr, unsigned int length)
|
||||
static inline unsigned int idal_nr_words(void *vaddr, unsigned int length)
|
||||
{
|
||||
#ifdef __s390x__
|
||||
if (idal_is_needed(vaddr, length))
|
||||
return ((__pa(vaddr) & (IDA_BLOCK_SIZE-1)) + length +
|
||||
(IDA_BLOCK_SIZE-1)) >> IDA_SIZE_LOG;
|
||||
#endif
|
||||
return 0;
|
||||
return ((__pa(vaddr) & (IDA_BLOCK_SIZE-1)) + length +
|
||||
(IDA_BLOCK_SIZE-1)) >> IDA_SIZE_LOG;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the list of idal words for an address/length pair.
|
||||
*/
|
||||
static inline unsigned long *
|
||||
idal_create_words(unsigned long *idaws, void *vaddr, unsigned int length)
|
||||
static inline unsigned long *idal_create_words(unsigned long *idaws,
|
||||
void *vaddr, unsigned int length)
|
||||
{
|
||||
#ifdef __s390x__
|
||||
unsigned long paddr;
|
||||
unsigned int cidaw;
|
||||
|
||||
|
@ -74,7 +68,6 @@ idal_create_words(unsigned long *idaws, void *vaddr, unsigned int length)
|
|||
paddr += IDA_BLOCK_SIZE;
|
||||
*idaws++ = paddr;
|
||||
}
|
||||
#endif
|
||||
return idaws;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
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;
|
||||
}
|
||||
|
||||
/* allocate additional request block */
|
||||
struct dasd_ccw_req *erp;
|
||||
|
||||
erp = dasd_alloc_erp_request((char *) &cqr->magic, 2, 0, device);
|
||||
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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -38,8 +38,11 @@
|
|||
#define DASD_ECKD_CCW_RELEASE 0x94
|
||||
#define DASD_ECKD_CCW_READ_CKD_MT 0x9e
|
||||
#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_PFX 0xE7
|
||||
#define DASD_ECKD_CCW_PFX_READ 0xEA
|
||||
#define DASD_ECKD_CCW_RSCK 0xF9
|
||||
|
||||
/*
|
||||
|
@ -123,7 +126,9 @@ struct DE_eckd_data {
|
|||
unsigned long long ep_sys_time; /* Ext Parameter - System Time Stamp */
|
||||
__u8 ep_format; /* Extended Parameter format 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));
|
||||
|
||||
struct LO_eckd_data {
|
||||
|
@ -144,11 +149,37 @@ struct LO_eckd_data {
|
|||
__u16 length;
|
||||
} __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 */
|
||||
struct PFX_eckd_data {
|
||||
unsigned char format;
|
||||
struct {
|
||||
unsigned char define_extend:1;
|
||||
unsigned char define_extent:1;
|
||||
unsigned char time_stamp:1;
|
||||
unsigned char verify_base:1;
|
||||
unsigned char hyper_pav:1;
|
||||
|
@ -158,9 +189,8 @@ struct PFX_eckd_data {
|
|||
__u8 aux;
|
||||
__u8 base_lss;
|
||||
__u8 reserved[7];
|
||||
struct DE_eckd_data define_extend;
|
||||
struct LO_eckd_data locate_record;
|
||||
__u8 LO_extended_data[4];
|
||||
struct DE_eckd_data define_extent;
|
||||
struct LRE_eckd_data locate_record;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct dasd_eckd_characteristics {
|
||||
|
|
|
@ -297,11 +297,12 @@ static void dasd_eer_write_standard_trigger(struct dasd_device *device,
|
|||
struct dasd_eer_header header;
|
||||
unsigned long flags;
|
||||
struct eerbuffer *eerb;
|
||||
char *sense;
|
||||
|
||||
/* go through cqr chain and count the valid sense data sets */
|
||||
data_size = 0;
|
||||
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;
|
||||
|
||||
header.total_size = sizeof(header) + data_size + 4; /* "EOR" */
|
||||
|
@ -316,9 +317,11 @@ static void dasd_eer_write_standard_trigger(struct dasd_device *device,
|
|||
list_for_each_entry(eerb, &bufferlist, list) {
|
||||
dasd_eer_start_record(eerb, header.total_size);
|
||||
dasd_eer_write_buffer(eerb, (char *) &header, sizeof(header));
|
||||
for (temp_cqr = cqr; temp_cqr; temp_cqr = temp_cqr->refers)
|
||||
if (temp_cqr->irb.esw.esw0.erw.cons)
|
||||
dasd_eer_write_buffer(eerb, cqr->irb.ecw, 32);
|
||||
for (temp_cqr = cqr; temp_cqr; temp_cqr = temp_cqr->refers) {
|
||||
sense = dasd_get_sense(&temp_cqr->irb);
|
||||
if (sense)
|
||||
dasd_eer_write_buffer(eerb, sense, 32);
|
||||
}
|
||||
dasd_eer_write_buffer(eerb, "EOR", 4);
|
||||
}
|
||||
spin_unlock_irqrestore(&bufferlock, flags);
|
||||
|
@ -451,6 +454,7 @@ int dasd_eer_enable(struct dasd_device *device)
|
|||
{
|
||||
struct dasd_ccw_req *cqr;
|
||||
unsigned long flags;
|
||||
struct ccw1 *ccw;
|
||||
|
||||
if (device->eer_cqr)
|
||||
return 0;
|
||||
|
@ -468,10 +472,11 @@ int dasd_eer_enable(struct dasd_device *device)
|
|||
cqr->expires = 10 * HZ;
|
||||
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
|
||||
|
||||
cqr->cpaddr->cmd_code = DASD_ECKD_CCW_SNSS;
|
||||
cqr->cpaddr->count = SNSS_DATA_SIZE;
|
||||
cqr->cpaddr->flags = 0;
|
||||
cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
|
||||
ccw = cqr->cpaddr;
|
||||
ccw->cmd_code = DASD_ECKD_CCW_SNSS;
|
||||
ccw->count = SNSS_DATA_SIZE;
|
||||
ccw->flags = 0;
|
||||
ccw->cda = (__u32)(addr_t) cqr->data;
|
||||
|
||||
cqr->buildclk = get_clock();
|
||||
cqr->status = DASD_CQR_FILLED;
|
||||
|
|
|
@ -157,7 +157,8 @@ struct dasd_ccw_req {
|
|||
struct dasd_block *block; /* the originating block device */
|
||||
struct dasd_device *memdev; /* the device used to allocate this */
|
||||
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 */
|
||||
short retries; /* A retry counter */
|
||||
unsigned long flags; /* flags of this request */
|
||||
|
@ -573,12 +574,14 @@ int dasd_generic_notify(struct ccw_device *, int);
|
|||
void dasd_generic_handle_state_change(struct dasd_device *);
|
||||
|
||||
int dasd_generic_read_dev_chars(struct dasd_device *, char *, void **, int);
|
||||
char *dasd_get_sense(struct irb *);
|
||||
|
||||
/* externals in dasd_devmap.c */
|
||||
extern int dasd_max_devindex;
|
||||
extern int dasd_probeonly;
|
||||
extern int dasd_autodetect;
|
||||
extern int dasd_nopav;
|
||||
extern int dasd_nofcx;
|
||||
|
||||
int dasd_devmap_init(void);
|
||||
void dasd_devmap_exit(void);
|
||||
|
|
Loading…
Reference in New Issue