[S390] dasd_eckd_dump_sense bug.

The ccw dump function dasd_eckd_dump_ccw_range can crash because
it does not take care about the IDAL flag in the ccw.
Check for IDALs flag set in CCW and follow the indirect list to
print the data that is refered by the ccw.

Signed-off-by: Horst Hummel <horst.hummel@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 7d5a6cee..7565d30 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1522,6 +1522,40 @@
 }
 
 /*
+ * Dump the range of CCWs into 'page' buffer
+ * and return number of printed chars.
+ */
+static inline int
+dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page)
+{
+	int len, count;
+	char *datap;
+
+	len = 0;
+	while (from <= to) {
+		len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+			       " CCW %p: %08X %08X DAT:",
+			       from, ((int *) from)[0], ((int *) from)[1]);
+
+		/* get pointer to data (consider IDALs) */
+		if (from->flags & CCW_FLAG_IDA)
+			datap = (char *) *((addr_t *) (addr_t) from->cda);
+		else
+			datap = (char *) ((addr_t) from->cda);
+
+		/* dump data (max 32 bytes) */
+		for (count = 0; count < from->count && count < 32; count++) {
+			if (count % 8 == 0) len += sprintf(page + len, " ");
+			if (count % 4 == 0) len += sprintf(page + len, " ");
+			len += sprintf(page + len, "%02x", datap[count]);
+		}
+		len += sprintf(page + len, "\n");
+		from++;
+	}
+	return len;
+}
+
+/*
  * Print sense data and related channel program.
  * Parts are printed because printk buffer is only 1024 bytes.
  */
@@ -1530,8 +1564,8 @@
 		     struct irb *irb)
 {
 	char *page;
-	struct ccw1 *act, *end, *last;
-	int len, sl, sct, count;
+	struct ccw1 *first, *last, *fail, *from, *to;
+	int len, sl, sct;
 
 	page = (char *) get_zeroed_page(GFP_ATOMIC);
 	if (page == NULL) {
@@ -1539,7 +1573,8 @@
 			    "No memory to dump sense data");
 		return;
 	}
-	len = sprintf(page, KERN_ERR PRINTK_HEADER
+	/* dump the sense data */
+	len = sprintf(page,  KERN_ERR PRINTK_HEADER
 		      " I/O status report for device %s:\n",
 		      device->cdev->dev.bus_id);
 	len += sprintf(page + len, KERN_ERR PRINTK_HEADER
@@ -1564,87 +1599,55 @@
 
 		if (irb->ecw[27] & DASD_SENSE_BIT_0) {
 			/* 24 Byte Sense Data */
-			len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-				       " 24 Byte: %x MSG %x, "
-				       "%s MSGb to SYSOP\n",
-				       irb->ecw[7] >> 4, irb->ecw[7] & 0x0f,
-				       irb->ecw[1] & 0x10 ? "" : "no");
+			sprintf(page + len, KERN_ERR PRINTK_HEADER
+				" 24 Byte: %x MSG %x, "
+				"%s MSGb to SYSOP\n",
+				irb->ecw[7] >> 4, irb->ecw[7] & 0x0f,
+				irb->ecw[1] & 0x10 ? "" : "no");
 		} else {
 			/* 32 Byte Sense Data */
-			len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-				       " 32 Byte: Format: %x "
-				       "Exception class %x\n",
-				       irb->ecw[6] & 0x0f, irb->ecw[22] >> 4);
+			sprintf(page + len, KERN_ERR PRINTK_HEADER
+				" 32 Byte: Format: %x "
+				"Exception class %x\n",
+				irb->ecw[6] & 0x0f, irb->ecw[22] >> 4);
 		}
 	} else {
-	        len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-			       " SORRY - NO VALID SENSE AVAILABLE\n");
+		sprintf(page + len, KERN_ERR PRINTK_HEADER
+			" SORRY - NO VALID SENSE AVAILABLE\n");
 	}
-	MESSAGE_LOG(KERN_ERR, "%s",
-		    page + sizeof(KERN_ERR PRINTK_HEADER));
+	printk("%s", page);
 
-	/* dump the Channel Program */
-	/* print first CCWs (maximum 8) */
-	act = req->cpaddr;
-        for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
-	end = min(act + 8, last);
-	len = sprintf(page, KERN_ERR PRINTK_HEADER
+	/* dump the Channel Program (max 140 Bytes per line) */
+	/* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */
+	first = req->cpaddr;
+	for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
+	to = min(first + 6, last);
+	len = sprintf(page,  KERN_ERR PRINTK_HEADER
 		      " Related CP in req: %p\n", req);
-	while (act <= end) {
-		len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-			       " CCW %p: %08X %08X DAT:",
-			       act, ((int *) act)[0], ((int *) act)[1]);
-		for (count = 0; count < 32 && count < act->count;
-		     count += sizeof(int))
-			len += sprintf(page + len, " %08X",
-				       ((int *) (addr_t) act->cda)
-				       [(count>>2)]);
-		len += sprintf(page + len, "\n");
-		act++;
-	}
-	MESSAGE_LOG(KERN_ERR, "%s",
-		    page + sizeof(KERN_ERR PRINTK_HEADER));
+	dasd_eckd_dump_ccw_range(first, to, page + len);
+	printk("%s", page);
 
-	/* print failing CCW area */
+	/* print failing CCW area (maximum 4) */
+	/* scsw->cda is either valid or zero  */
 	len = 0;
-	if (act <  ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2) {
-		act = ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2;
-		len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
+	from = ++to;
+	fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */
+	if (from <  fail - 2) {
+		from = fail - 2;     /* there is a gap - print header */
+		len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n");
 	}
-	end = min((struct ccw1 *)(addr_t) irb->scsw.cpa + 2, last);
-	while (act <= end) {
-		len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-			       " CCW %p: %08X %08X DAT:",
-			       act, ((int *) act)[0], ((int *) act)[1]);
-		for (count = 0; count < 32 && count < act->count;
-		     count += sizeof(int))
-			len += sprintf(page + len, " %08X",
-				       ((int *) (addr_t) act->cda)
-				       [(count>>2)]);
-		len += sprintf(page + len, "\n");
-		act++;
-	}
+	to = min(fail + 1, last);
+	len += dasd_eckd_dump_ccw_range(from, to, page + len);
 
-	/* print last CCWs */
-	if (act <  last - 2) {
-		act = last - 2;
+	/* print last CCWs (maximum 2) */
+	from = max(from, ++to);
+	if (from < last - 1) {
+		from = last - 1;     /* there is a gap - print header */
 		len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
 	}
-	while (act <= last) {
-		len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-			       " CCW %p: %08X %08X DAT:",
-			       act, ((int *) act)[0], ((int *) act)[1]);
-		for (count = 0; count < 32 && count < act->count;
-		     count += sizeof(int))
-			len += sprintf(page + len, " %08X",
-				       ((int *) (addr_t) act->cda)
-				       [(count>>2)]);
-		len += sprintf(page + len, "\n");
-		act++;
-	}
+	len += dasd_eckd_dump_ccw_range(from, last, page + len);
 	if (len > 0)
-		MESSAGE_LOG(KERN_ERR, "%s",
-			    page + sizeof(KERN_ERR PRINTK_HEADER));
+		printk("%s", page);
 	free_page((unsigned long) page);
 }