From: Lars Marowsky-Bree <[email-masked]>

Make the sense data available in the bio end_io path on request.

Signed-off-by: Lars Marowsky-Bree <[email-masked]>
Signed-off-by: Jens Axboe <[email-masked]>
---

 block/ll_rw_blk.c       |   22 ++++++++++++++++++++++
 drivers/scsi/scsi_lib.c |   15 +++++++++++++++
 fs/bio.c                |    1 +
 include/linux/bio.h     |   23 +++++++++++++++++++++++
 4 files changed, 61 insertions(+)

Index: linux-2.6.21/block/ll_rw_blk.c
===================================================================
--- linux-2.6.21.orig/block/ll_rw_blk.c	2007-05-01 17:41:13.000000000 +0100
+++ linux-2.6.21/block/ll_rw_blk.c	2007-05-03 17:21:02.000000000 +0100
@@ -3328,7 +3328,9 @@ static int __end_that_request_first(stru
 				    int nr_bytes)
 {
 	int total_bytes, bio_nbytes, error, next_idx = 0;
+	unsigned char *s = NULL;
 	struct bio *bio;
+	unsigned sense_key, asc, ascq;
 
 	blk_add_trace_rq(req->q, req, BLK_TA_COMPLETE);
 
@@ -3339,6 +3341,21 @@ static int __end_that_request_first(stru
 	if (end_io_error(uptodate))
 		error = !uptodate ? -EIO : uptodate;
 
+	if (req->sense && req->sense_len) {
+		s = req->sense;
+		if ((s[0] & 0x7f) == 0x70) { /* current sense in fixed format */
+			sense_key = s[2] & 0x0f;
+			asc = s[12];
+			ascq = s[13];
+		} else if ((s[0] & 0x7f) == 0x72) { /* current sense in descriptor format */
+			sense_key = s[1] & 0x0f;
+			asc = s[2];
+			ascq = s[3];
+		} else {
+			s = NULL;
+		}
+	}
+
 	/*
 	 * for a REQ_BLOCK_PC request, we want to carry any eventual
 	 * sense key with us all the way through
@@ -3363,6 +3380,11 @@ static int __end_that_request_first(stru
 	while ((bio = req->bio) != NULL) {
 		int nbytes;
 
+		if (s)
+			bio_set_sense(bio, sense_key, asc, ascq);
+		else if (error < 0)
+			bio_set_errno(bio, -error);
+
 		if (nr_bytes >= bio->bi_size) {
 			req->bio = bio->bi_next;
 			nbytes = bio->bi_size;
Index: linux-2.6.21/drivers/scsi/scsi_lib.c
===================================================================
--- linux-2.6.21.orig/drivers/scsi/scsi_lib.c	2007-05-01 17:41:13.000000000 +0100
+++ linux-2.6.21/drivers/scsi/scsi_lib.c	2007-05-03 17:21:02.000000000 +0100
@@ -656,9 +656,21 @@ static struct scsi_cmnd *scsi_end_reques
 {
 	request_queue_t *q = cmd->device->request_queue;
 	struct request *req = cmd->request;
+	int sense_override = 0;
 	unsigned long flags;
 
 	/*
+	 * if room for sense wasn't supplied for this request, override with
+	 * what we have stored for the duration of the end_io handling. this
+	 * allows passing of sense to the block layer.
+	 */
+	if (!req->sense && (cmd->sense_buffer[0] & 0x70)) {
+		req->sense = cmd->sense_buffer;
+		req->sense_len = 8 + cmd->sense_buffer[7];
+		sense_override = 1;
+	}
+
+	/*
 	 * If there are blocks left over at the end, set up the command
 	 * to queue the remainder of them.
 	 */
@@ -685,6 +697,9 @@ static struct scsi_cmnd *scsi_end_reques
 		}
 	}
 
+	if (sense_override)
+		req->sense = NULL;
+
 	add_disk_randomness(req->rq_disk);
 
 	spin_lock_irqsave(q->queue_lock, flags);
Index: linux-2.6.21/fs/bio.c
===================================================================
--- linux-2.6.21.orig/fs/bio.c	2007-05-03 14:08:39.000000000 +0100
+++ linux-2.6.21/fs/bio.c	2007-05-03 17:21:02.000000000 +0100
@@ -131,6 +131,7 @@ void bio_init(struct bio *bio)
 	bio->bi_bdev = NULL;
 	bio->bi_flags = 1 << BIO_UPTODATE;
 	bio->bi_rw = 0;
+	bio->bi_error = 0;
 	bio->bi_vcnt = 0;
 	bio->bi_idx = 0;
 	bio->bi_phys_segments = 0;
Index: linux-2.6.21/include/linux/bio.h
===================================================================
--- linux-2.6.21.orig/include/linux/bio.h	2007-05-01 17:41:13.000000000 +0100
+++ linux-2.6.21/include/linux/bio.h	2007-05-03 17:21:02.000000000 +0100
@@ -78,6 +78,7 @@ struct bio {
 	unsigned long		bi_rw;		/* bottom bits READ/WRITE,
 						 * top bits priority
 						 */
+	unsigned int		bi_error;	/* -Exx or sense */
 
 	unsigned short		bi_vcnt;	/* how many bio_vec's */
 	unsigned short		bi_idx;		/* current index into bvl_vec */
@@ -254,6 +255,28 @@ struct bio {
  */
 #define bio_get(bio)	atomic_inc(&(bio)->bi_cnt)
 
+enum {
+	BIO_ERROR_ERRNO = 1,
+	BIO_ERROR_SENSE,
+};
+
+/*
+ * Extended error reporting. The upper 8 bits are flag values, the bottom
+ * 24 can be used for extended errors (such as sense).
+ */
+static inline void bio_set_sense(struct bio *bio, char key, char asc, char ascq)
+{
+	bio->bi_error = (BIO_ERROR_SENSE << 24) | (key << 16) | (asc << 8) | ascq;
+}
+
+static inline void bio_set_errno(struct bio *bio, int error)
+{
+	bio->bi_error = (BIO_ERROR_ERRNO << 24) | error;
+}
+
+#define bio_errno_valid(bio)	((bio)->bi_error & (BIO_ERROR_ERRNO << 24))
+#define bio_sense_valid(bio)	((bio)->bi_error & (BIO_ERROR_SENSE << 24))
+#define bio_sense_value(bio)	((bio)->bi_error & 0xffffff)
 
 /*
  * A bio_pair is used when we need to split a bio.