Browse Source

[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>
Stefan Weinhuber 16 years ago
parent
commit
f3eb5384cf

+ 5 - 12
arch/s390/include/asm/idals.h

@@ -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;
 }
 

+ 58 - 12
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;

+ 94 - 47
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,

+ 18 - 7
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) {

+ 7 - 0
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;

File diff suppressed because it is too large
+ 728 - 69
drivers/s390/block/dasd_eckd.c


+ 35 - 5
drivers/s390/block/dasd_eckd.h

@@ -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 {

+ 13 - 8
drivers/s390/block/dasd_eer.c

@@ -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;

+ 4 - 1
drivers/s390/block/dasd_int.h

@@ -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);

Some files were not shown because too many files changed in this diff