patch-pre2.0.12 linux/drivers/scsi/sd.c

Next file: linux/drivers/scsi/sr.c
Previous file: linux/drivers/scsi/scsi_ioctl.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file pre2.0.11/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
@@ -218,11 +218,45 @@
 {
     int result = SCpnt->result;
     int this_count = SCpnt->bufflen >> 9;
+    int good_sectors = (result == 0 ? this_count : 0);
+    int block_sectors = 1;
     
 #ifdef DEBUG
     printk("sd%c : rw_intr(%d, %d)\n", 'a' + MINOR(SCpnt->request.rq_dev), 
 	   SCpnt->host->host_no, result);
 #endif
+
+    /*
+      Handle MEDIUM ERRORs that indicate partial success.  Since this is a
+      relatively rare error condition, no care is taken to avoid unnecessary
+      additional work such as memcpy's that could be avoided.
+    */
+
+    if (driver_byte(result) != 0 &&		    /* An error occurred */
+	SCpnt->sense_buffer[0] == 0xF0 &&	    /* Sense data is valid */
+	SCpnt->sense_buffer[2] == MEDIUM_ERROR)
+      {
+	long error_sector = (SCpnt->sense_buffer[3] << 24) |
+			    (SCpnt->sense_buffer[4] << 16) |
+			    (SCpnt->sense_buffer[5] << 8) |
+			    SCpnt->sense_buffer[6];
+	int sector_size =
+	  rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].sector_size;
+	if (SCpnt->request.bh != NULL)
+	  block_sectors = SCpnt->request.bh->b_size >> 9;
+	if (sector_size == 1024)
+	  {
+	    error_sector <<= 1;
+	    if (block_sectors < 2) block_sectors = 2;
+	  }
+	else if (sector_size == 256)
+	  error_sector >>= 1;
+	error_sector -= sd[MINOR(SCpnt->request.rq_dev)].start_sect;
+	error_sector &= ~ (block_sectors - 1);
+	good_sectors = error_sector - SCpnt->request.sector;
+	if (good_sectors < 0 || good_sectors >= this_count)
+	  good_sectors = 0;
+      }
     
     /*
      * First case : we assume that the command succeeded.  One of two things 
@@ -230,7 +264,7 @@
      * sectors that we were unable to read last time.
      */
 
-    if (!result) {
+    if (good_sectors > 0) {
 	
 #ifdef DEBUG
 	printk("sd%c : %d sectors remain.\n", 'a' + MINOR(SCpnt->request.rq_dev),
@@ -291,11 +325,16 @@
 		      SCpnt->request.sector, this_count);
 	    }
 	}
-	SCpnt = end_scsi_request(SCpnt, 1, this_count);
-	requeue_sd_request(SCpnt);
-	return;
+	SCpnt = end_scsi_request(SCpnt, 1, good_sectors);
+	if (result == 0)
+	  {
+	    requeue_sd_request(SCpnt);
+	    return;
+	  }
     }
     
+    if (good_sectors == 0) {
+
     /* Free up any indirection buffers we allocated for DMA purposes. */
     if (SCpnt->use_sg) {
 	struct scatterlist * sgpnt;
@@ -319,7 +358,8 @@
 	if (SCpnt->buffer != SCpnt->request.buffer)
 	    scsi_free(SCpnt->buffer, SCpnt->bufflen);
     }
-    
+    }
+
     /*
      * Now, if we were good little boys and girls, Santa left us a request
      * sense buffer.  We can extract information from this, so we
@@ -382,6 +422,17 @@
 		/* ???? */
 	    }
 	}
+
+	if (SCpnt->sense_buffer[2] == MEDIUM_ERROR) {
+	    printk("scsi%d: MEDIUM ERROR on channel %d, id %d, lun %d, CDB: ",
+		   SCpnt->host->host_no, (int) SCpnt->channel, 
+		   (int) SCpnt->target, (int) SCpnt->lun);
+	    print_command(SCpnt->cmnd);
+	    print_sense("sr", SCpnt);
+	    SCpnt = end_scsi_request(SCpnt, 0, block_sectors);
+	    requeue_sd_request(SCpnt);
+	    return;
+	 }
     }  /* driver byte != 0 */
     if (result) {
 	printk("SCSI disk error : host %d channel %d id %d lun %d return code = %x\n",

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this