Skip to contentSkip to conversation

libata: improve sense data generation

PATCH New Tejun Heo 2 participants 7 messages Nov 14, 2006 libata

lore
Share & tools
lore

Tejun Heo

author
#0 lore raw

PATCH 1/1 libata: improve sense data generation

Hello, Jeff.

This patchset fixes and improves SCSI sense data generation.

* resul_tf->flags is synced to command tf->flags so that sense generation functions can tell which address format is in use

* length field in passthru sense data fixed

* failed block is now properly reported for all three address formats
- CHS, LBA and LBA48.

* other cleanups

Thanks.

--
tejun

author
#1 lore raw

PATCH 1/5 libata: sync result_tf.flags w/ command tf.flags

libata didn't initialize result_tf.flags which indicates transfer type (RW/FUA) and address type (CHS/LBA/LBA48). ata_gen_fixed_sense() assumed result_tf.flags equals command tf.flags and failed to report the first failed block to SCSI layer because zero tf flags indicates CHS and bad block reporting for CHS is not implemented.

Implement fill_result_tf() which sets result_tf.flags to command tf.flags and use it to fill result_tf.

---
 drivers/ata/libata-core.c |   14 +++++++++++---
 1 files changed, 11 insertions(+), 3 deletions(-)
drivers/ata/libata-core.c +11 -3
index 994bc27..476df94 100644
@@ -4516,6 +4516,14 @@ void __ata_qc_complete(struct ata_queued
 	qc->complete_fn(qc);
 }
 
+static void fill_result_tf(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+
+	ap->ops->tf_read(ap, &qc->result_tf);
+	qc->result_tf.flags = qc->tf.flags;
+}
+
 /**
  *	ata_qc_complete - Complete an active ATA command
  *	@qc: Command to complete
@@ -4553,7 +4561,7 @@ void ata_qc_complete(struct ata_queued_c
 		if (unlikely(qc->flags & ATA_QCFLAG_FAILED)) {
 			if (!ata_tag_internal(qc->tag)) {
 				/* always fill result TF for failed qc */
-				ap->ops->tf_read(ap, &qc->result_tf);
+				fill_result_tf(qc);
 				ata_qc_schedule_eh(qc);
 				return;
 			}
@@ -4561,7 +4569,7 @@ void ata_qc_complete(struct ata_queued_c
 
 		/* read result TF if requested */
 		if (qc->flags & ATA_QCFLAG_RESULT_TF)
-			ap->ops->tf_read(ap, &qc->result_tf);
+			fill_result_tf(qc);
 
 		__ata_qc_complete(qc);
 	} else {
@@ -4570,7 +4578,7 @@ void ata_qc_complete(struct ata_queued_c
 
 		/* read result TF if failed or requested */
 		if (qc->err_mask || qc->flags & ATA_QCFLAG_RESULT_TF)
-			ap->ops->tf_read(ap, &qc->result_tf);
+			fill_result_tf(qc);
 
 		__ata_qc_complete(qc);
 	}
Signature · 1 line
1.4.3.3
#2 lore raw

Re: [PATCH 1/5] libata: sync result_tf.flags w/ command tf.flags

Quoted 10 lines · Tejun Heo

libata didn't initialize result_tf.flags which indicates transfer type (RW/FUA) and address type (CHS/LBA/LBA48). ata_gen_fixed_sense() assumed result_tf.flags equals command tf.flags and failed to report the first failed block to SCSI layer because zero tf flags indicates CHS and bad block reporting for CHS is not implemented.

Implement fill_result_tf() which sets result_tf.flags to command tf.flags and use it to fill result_tf.

applied patches 1-5

consider combining patch #4 and patch #5, when a similar situation arises in the future

author
#3 lore raw

PATCH 2/5 libata: fix passthru sense data header

In reply to Tejun Heo

sb[7] should contain the length of whole information sense data descriptor while desc[1] should contain the number of following bytes in the descriptor. ie. 14 for sb[7] but 12 for desc[1].

---
 drivers/ata/libata-scsi.c |    9 +++------
 1 files changed, 3 insertions(+), 6 deletions(-)
drivers/ata/libata-scsi.c +3 -6
index a0b6ee1..8c9fa9e 100644
@@ -713,12 +713,9 @@ void ata_gen_ata_desc_sense(struct ata_q
 
 	desc[0] = 0x09;
 
-	/*
-	 * Set length of additional sense data.
-	 * Since we only populate descriptor 0, the total
-	 * length is the same (fixed) length as descriptor 0.
-	 */
-	desc[1] = sb[7] = 14;
+	/* set length of additional sense data */
+	sb[7] = 14;
+	desc[1] = 12;
 
 	/*
 	 * Copy registers into sense buffer.
Signature · 1 line
1.4.3.3
author
#4 lore raw

PATCH 3/5 libata: cosmetic changes to sense generation functions

In reply to Tejun Heo

* s/ata_gen_ata_desc_sense/ata_gen_passthru_sense/

* s/ata_gen_fixed_sense/ata_gen_ata_sense/

* make both functions static

* neither function has locking requirement, change it to None.

---
 drivers/ata/libata-scsi.c |   22 +++++++++++-----------
 1 files changed, 11 insertions(+), 11 deletions(-)
drivers/ata/libata-scsi.c +11 -11
index 8c9fa9e..2004a76 100644
@@ -671,7 +671,7 @@ void ata_to_sense_error(unsigned id, u8
 }
 
 /*
- *	ata_gen_ata_desc_sense - Generate check condition sense block.
+ *	ata_gen_passthru_sense - Generate check condition sense block.
  *	@qc: Command that completed.
  *
  *	This function is specific to the ATA descriptor format sense
@@ -681,9 +681,9 @@ void ata_to_sense_error(unsigned id, u8
  *	block. Clear sense key, ASC & ASCQ if there is no error.
  *
  *	LOCKING:
- *	spin_lock_irqsave(host lock)
+ *	None.
  */
-void ata_gen_ata_desc_sense(struct ata_queued_cmd *qc)
+static void ata_gen_passthru_sense(struct ata_queued_cmd *qc)
 {
 	struct scsi_cmnd *cmd = qc->scsicmd;
 	struct ata_taskfile *tf = &qc->result_tf;
@@ -743,16 +743,16 @@ void ata_gen_ata_desc_sense(struct ata_q
 }
 
 /**
- *	ata_gen_fixed_sense - generate a SCSI fixed sense block
+ *	ata_gen_ata_sense - generate a SCSI fixed sense block
  *	@qc: Command that we are erroring out
  *
  *	Leverage ata_to_sense_error() to give us the codes.  Fit our
  *	LBA in here if there's room.
  *
  *	LOCKING:
- *	inherited from caller
+ *	None.
  */
-void ata_gen_fixed_sense(struct ata_queued_cmd *qc)
+static void ata_gen_ata_sense(struct ata_queued_cmd *qc)
 {
 	struct scsi_cmnd *cmd = qc->scsicmd;
 	struct ata_taskfile *tf = &qc->result_tf;
@@ -1459,7 +1459,7 @@ static void ata_scsi_qc_complete(struct
 	 */
 	if (((cdb[0] == ATA_16) || (cdb[0] == ATA_12)) &&
  	    ((cdb[2] & 0x20) || need_sense)) {
- 		ata_gen_ata_desc_sense(qc);
+		ata_gen_passthru_sense(qc);
 	} else {
 		if (!need_sense) {
 			cmd->result = SAM_STAT_GOOD;
@@ -1470,7 +1470,7 @@ static void ata_scsi_qc_complete(struct
 			 * good for smaller LBA (and maybe CHS?)
 			 * devices.
 			 */
-			ata_gen_fixed_sense(qc);
+			ata_gen_ata_sense(qc);
 		}
 	}
 
@@ -2301,7 +2301,7 @@ static void atapi_sense_complete(struct
 		 * a sense descriptors, since that's only
 		 * correct for ATA, not ATAPI
 		 */
-		ata_gen_ata_desc_sense(qc);
+		ata_gen_passthru_sense(qc);
 	}
 
 	qc->scsidone(qc->scsicmd);
@@ -2376,7 +2376,7 @@ static void atapi_qc_complete(struct ata
 			 * sense descriptors, since that's only
 			 * correct for ATA, not ATAPI
 			 */
-			ata_gen_ata_desc_sense(qc);
+			ata_gen_passthru_sense(qc);
 		}
 
 		/* SCSI EH automatically locks door if sdev->locked is
@@ -2409,7 +2409,7 @@ static void atapi_qc_complete(struct ata
 		 * a sense descriptors, since that's only
 		 * correct for ATA, not ATAPI
 		 */
-		ata_gen_ata_desc_sense(qc);
+		ata_gen_passthru_sense(qc);
 	} else {
 		u8 *scsicmd = cmd->cmnd;
 
Signature · 1 line
1.4.3.3
author
#5 lore raw

PATCH 4/5 libata: implement ata_tf_read_block()

In reply to Tejun Heo

Implement ata_tf_read_block().

---
 drivers/ata/libata-core.c |   43 +++++++++++++++++++++++++++++++++++++++++++
 drivers/ata/libata.h      |    1 +
 2 files changed, 44 insertions(+), 0 deletions(-)
drivers/ata/libata-core.c +43 -0
index 476df94..dab577c 100644
@@ -240,6 +240,49 @@ int ata_rwcmd_protocol(struct ata_queued
 }
 
 /**
+ *	ata_tf_read_block - Read block address from ATA taskfile
+ *	@tf: ATA taskfile of interest
+ *	@dev: ATA device @tf belongs to
+ *
+ *	LOCKING:
+ *	None.
+ *
+ *	Read block address from @tf.  This function can handle all
+ *	three address formats - LBA, LBA48 and CHS.  tf->protocol and
+ *	flags select the address format to use.
+ *
+ *	RETURNS:
+ *	Block address read from @tf.
+ */
+u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev)
+{
+	u64 block = 0;
+
+	if (tf->flags & ATA_TFLAG_LBA) {
+		if (tf->flags & ATA_TFLAG_LBA48) {
+			block |= (u64)tf->hob_lbah << 40;
+			block |= (u64)tf->hob_lbam << 32;
+			block |= tf->hob_lbal << 24;
+		} else
+			block |= (tf->device & 0xf) << 24;
+
+		block |= tf->lbah << 16;
+		block |= tf->lbam << 8;
+		block |= tf->lbal;
+	} else {
+		u32 cyl, head, sect;
+
+		cyl = tf->lbam | (tf->lbah << 8);
+		head = tf->device & 0xf;
+		sect = tf->lbal;
+
+		block = (cyl * dev->heads + head) * dev->sectors + sect;
+	}
+
+	return block;
+}
+
+/**
  *	ata_pack_xfermask - Pack pio, mwdma and udma masks into xfer_mask
  *	@pio_mask: pio_mask
  *	@mwdma_mask: mwdma_mask
drivers/ata/libata.h +1 -0
index e4ffb2e..0549b19 100644
@@ -45,6 +45,7 @@ extern int atapi_dmadir;
 extern int libata_fua;
 extern struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev);
 extern int ata_rwcmd_protocol(struct ata_queued_cmd *qc);
+extern u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev);
 extern void ata_dev_disable(struct ata_device *dev);
 extern void ata_port_flush_task(struct ata_port *ap);
 extern unsigned ata_exec_internal(struct ata_device *dev,
Signature · 1 line
1.4.3.3
author
#6 lore raw

PATCH 5/5 libata: improve SCSI sense data generation

In reply to Tejun Heo

Update ata_gen_ata_sense() to use desc format sense data to report the first failed block. The first failed block is read from result_tf using ata_tf_read_block() which can handle all three address formats.

---
 drivers/ata/libata-scsi.c |   46 ++++++++++++++++++++++----------------------
 1 files changed, 23 insertions(+), 23 deletions(-)
drivers/ata/libata-scsi.c +23 -23
index 2004a76..9b0d59d 100644
@@ -746,53 +746,53 @@ static void ata_gen_passthru_sense(struc
  *	ata_gen_ata_sense - generate a SCSI fixed sense block
  *	@qc: Command that we are erroring out
  *
- *	Leverage ata_to_sense_error() to give us the codes.  Fit our
- *	LBA in here if there's room.
+ *	Generate sense block for a failed ATA command @qc.  Descriptor
+ *	format is used to accomodate LBA48 block address.
  *
  *	LOCKING:
  *	None.
  */
 static void ata_gen_ata_sense(struct ata_queued_cmd *qc)
 {
+	struct ata_device *dev = qc->dev;
 	struct scsi_cmnd *cmd = qc->scsicmd;
 	struct ata_taskfile *tf = &qc->result_tf;
 	unsigned char *sb = cmd->sense_buffer;
+	unsigned char *desc = sb + 8;
 	int verbose = qc->ap->ops->error_handler == NULL;
+	u64 block;
 
 	memset(sb, 0, SCSI_SENSE_BUFFERSIZE);
 
 	cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
 
-	/*
-	 * Use ata_to_sense_error() to map status register bits
+	/* sense data is current and format is descriptor */
+	sb[0] = 0x72;
+
+	/* Use ata_to_sense_error() to map status register bits
 	 * onto sense key, asc & ascq.
 	 */
 	if (qc->err_mask ||
 	    tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) {
 		ata_to_sense_error(qc->ap->id, tf->command, tf->feature,
-				   &sb[2], &sb[12], &sb[13], verbose);
-		sb[2] &= 0x0f;
+				   &sb[1], &sb[2], &sb[3], verbose);
+		sb[1] &= 0x0f;
 	}
 
-	sb[0] = 0x70;
-	sb[7] = 0x0a;
+	block = ata_tf_read_block(&qc->result_tf, dev);
 
-	if (tf->flags & ATA_TFLAG_LBA48) {
-		/* TODO: find solution for LBA48 descriptors */
-	}
+	/* information sense data descriptor */
+	sb[7] = 12;
+	desc[0] = 0x00;
+	desc[1] = 10;
 
-	else if (tf->flags & ATA_TFLAG_LBA) {
-		/* A small (28b) LBA will fit in the 32b info field */
-		sb[0] |= 0x80;		/* set valid bit */
-		sb[3] = tf->device & 0x0f;
-		sb[4] = tf->lbah;
-		sb[5] = tf->lbam;
-		sb[6] = tf->lbal;
-	}
-
-	else {
-		/* TODO: C/H/S */
-	}
+	desc[2] |= 0x80;	/* valid */
+	desc[6] = block >> 40;
+	desc[7] = block >> 32;
+	desc[8] = block >> 24;
+	desc[9] = block >> 16;
+	desc[10] = block >> 8;
+	desc[11] = block;
 }
 
 static void ata_scsi_sdev_config(struct scsi_device *sdev)
Signature · 1 line
1.4.3.3