Skip to contentSkip to conversation

SG_IO ioctl for block devices

PATCH New Douglas Gilbert 1 participant 1 message Dec 4, 2002

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 SG_IO ioctl for block devices

I have been playing around with the SG_IO ioctl() that recently appeared in drivers/block/scsi_ioctl.c .

First a warning: using sg's write()/read() interface is a dangerous thing to do to anything other than an sg device (especially a disk). The utilities in sg3_utils (and sg_utils) took the precaution of sending a "sg" ioctl and assumed if it worked then the file descriptor referred to a sg device. Now _all_ block devices respond to the ioctl()s I chose :-(

IMO sg's write()/read() interface is still useful for queuing and high speed applications. However for the great majority of cases the SG_IO ioctl is sufficient. It turns out that sg3_utils version 1.01 doesn't scribble on disks (as outlined above), more by luck than good management. Several things fail (e.g. sg_logs) that can easily be fixed while sg_inq and sg_dd work. [sg_dd works because it treats /dev/sda as a normal file rather than a sg device.] I will release sg3_utils version 1.02 shortly.

The error processing of the SG_IO ioctl() is broken as far as I can determine. My attached patch drives output values to 0 (their inert values) and rescues the scsi status value from the scsi_request structure. Notice the hack in drivers/scsi/scsi_lib.c to stop the gratuitous clearing of req->errors. The sense buffer is not conveyed into struct request and there seems to be no code in scsi_lib.c to do that.

To keep the block SG_IO consistent with sg's version, I removed the EIO errno conditions. CHECK CONDITION does not (necessarily) indicate an error. Witness the code fix around lk 2.4.18 to correctly handle RECOVERED_ERROR. So a return value of zero implies that a consistent set of values has been written back into the sg_io_hdr instance (and the application best check them).

The patch is against lk 2.5.50bk3

Doug Gilbert

drivers/block/scsi_ioctl.c2550sgio1 renamed from drivers/block/scsi_ioctl.c +23 -12
@@ -126,7 +126,7 @@
 		 struct sg_io_hdr *uptr)
 {
 	unsigned long uaddr, start_time;
-	int err, reading, writing, nr_sectors;
+	int reading, writing, nr_sectors;
 	struct sg_io_hdr hdr;
 	struct request *rq;
 	struct bio *bio;
@@ -265,26 +265,36 @@
 
 	start_time = jiffies;
 
-	/*
-	 * return -EIO if we didn't transfer all data, caller can look at
-	 * residual count to find out how much did succeed
+	/* ignore return value. All information is passed back to caller
+	 * (if he doesn't check that is his problem).
+	 * N.B. a non-zero SCSI status is _not_ necessarily an error.
 	 */
-	err = blk_do_rq(q, bdev, rq);
-	if (rq->data_len > 0)
-		err = -EIO;
+	blk_do_rq(q, bdev, rq);
 	
 	if (bio) {
 		bio_unmap_user(bio, reading);
 		bio_put(bio);
 	}
 
-	hdr.status = rq->errors;
+	/* write to all output members */
+	hdr.status = rq->errors;	
+	hdr.masked_status = (hdr.status >> 1) & 0x1f;
+	hdr.msg_status = 0;
+	hdr.host_status = 0;
+	hdr.driver_status = 0;
+	hdr.info = 0;
+	if (hdr.masked_status || hdr.host_status || hdr.driver_status)
+		hdr.info |= SG_INFO_CHECK;
 	hdr.resid = rq->data_len;
 	hdr.duration = (jiffies - start_time) * (1000 / HZ);
+	hdr.sb_len_wr = 0;
 
 	if (rq->sense_len && hdr.sbp) {
-		if (!copy_to_user(hdr.sbp,rq->sense, rq->sense_len))
-			hdr.sb_len_wr = rq->sense_len;
+		int len = (hdr.mx_sb_len < rq->sense_len) ? 
+				hdr.mx_sb_len : rq->sense_len;
+
+		if (!copy_to_user(hdr.sbp, rq->sense, len))
+			hdr.sb_len_wr = len;
 	}
 
 	blk_put_request(rq);
@@ -297,8 +307,9 @@
 
 		kfree(buffer);
 	}
-
-	return err;
+	/* may not have succeeded, but output values written to control
+	 * structure (struct sg_io_hdr).  */
+	return 0;
 }
 
 #define FORMAT_UNIT_TIMEOUT		(2 * 60 * 60 * HZ)
drivers/scsi/scsi_lib.c2550bk3sgio1 renamed from drivers/scsi/scsi_lib.c +7 -2
@@ -441,6 +441,7 @@
 	int this_count = SCpnt->bufflen >> 9;
 	request_queue_t *q = &SCpnt->device->request_queue;
 	struct request *req = SCpnt->request;
+	int clear_errors = 1;
 
 	/*
 	 * We must do one of several things here:
@@ -481,9 +482,12 @@
 	}
 
 	if (blk_pc_request(req)) {
-		req->errors = result & 0xff;
+		req->errors = (driver_byte(result) & DRIVER_SENSE) ?
+				(CHECK_CONDITION << 1) : (result & 0xff);
 		if (!result)
 			req->data_len -= SCpnt->bufflen;
+		else
+			clear_errors = 0;  
 	}
 
 	/*
@@ -504,7 +508,8 @@
 					      req->nr_sectors, good_sectors));
 		SCSI_LOG_HLCOMPLETE(1, printk("use_sg is %d\n ", SCpnt->use_sg));
 
-		req->errors = 0;
+		if (clear_errors)
+			req->errors = 0;
 		/*
 		 * If multiple sectors are requested in one buffer, then
 		 * they will have been finished off by the first command.