Skip to contentSkip to conversation

SCSI Data Integrity Support

PATCH New Martin K. Petersen 2 participants 13 messages Jun 25, 2008 – Jun 30, 2008

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

PATCH 1/1 SCSI Data Integrity Support

Another post of my SCSI DIF changes. These patches require the block layer integrity infrastructure in Jens' tree as well as the scsi_data_buffer patch that's in scsi-misc already.

The complete patch series against 2.6.26rc8 is available here:

    http://oss.oracle.com/~mkp/patches/

There have been no functional changes since my last patch kit. However, based upon feedback I have made a clearer boundary between the scsi_host protection bits and the scsi_disk ditto.

I have also split up the changes into patches that are easier to digest.

Signature · 1 line
Martin K. Petersen	Oracle Linux Engineering
#1 lore raw

PATCH 1/1 scsi: Support devices with protection information (DIF)

- Add support for an extra scatter-gather list containing protection information.

- Remember devices with protection information turned on in INQUIRY.

---

4 files changed, 62 insertions(+)
drivers/scsi/scsi_lib.c    |   38 ++++++++++++++++++++++++++++++++++++++
drivers/scsi/scsi_scan.c   |    3 +++
include/scsi/scsi_cmnd.h   |   20 ++++++++++++++++++++
include/scsi/scsi_device.h |    1 +
drivers/scsi/scsi_lib.c +38 -0
@@ -778,6 +778,11 @@ void scsi_release_buffers(struct scsi_cm
 		kmem_cache_free(scsi_sdb_cache, bidi_sdb);
 		cmd->request->next_rq->special = NULL;
 	}
+
+	if (scsi_prot_sg_count(cmd)) {
+		scsi_free_sgtable(cmd->prot_sdb);
+		kmem_cache_free(scsi_sdb_cache, cmd->prot_sdb);
+	}
 }
 EXPORT_SYMBOL(scsi_release_buffers);
 
@@ -1031,6 +1036,32 @@ static int scsi_init_sgtable(struct requ
 	return BLKPREP_OK;
 }
 
+static int scsi_protect_io(struct scsi_cmnd *cmd, gfp_t gfp_mask)
+{
+	struct request *req;
+	struct scsi_data_buffer *pdb;
+	int ivecs, count;
+
+	req = cmd->request;
+
+	pdb = kmem_cache_zalloc(scsi_sdb_cache, gfp_mask);
+	if (unlikely(pdb == NULL))
+		return BLKPREP_DEFER;
+
+	ivecs = blk_rq_count_integrity_sg(req);
+
+	if (unlikely(scsi_alloc_sgtable(pdb, ivecs, gfp_mask)))
+		return BLKPREP_DEFER;
+
+	count = blk_rq_map_integrity_sg(req, pdb->table.sgl);
+	BUG_ON(unlikely(count > ivecs));
+
+	cmd->prot_sdb = pdb;
+	cmd->prot_sdb->table.nents = count;
+
+	return BLKPREP_OK;
+}
+
 /*
  * Function:    scsi_init_io()
  *
@@ -1060,6 +1091,13 @@ int scsi_init_io(struct scsi_cmnd *cmd, 
 		error = scsi_init_sgtable(cmd->request->next_rq, bidi_sdb,
 								    GFP_ATOMIC);
 		if (error)
+			goto err_exit;
+	}
+
+	if (blk_integrity_rq(cmd->request)) {
+		error = scsi_protect_io(cmd, gfp_mask);
+
+		if (error != BLKPREP_OK)
 			goto err_exit;
 	}
 
drivers/scsi/scsi_scan.c +3 -0
@@ -882,6 +882,9 @@ static int scsi_add_lun(struct scsi_devi
 
 	if (*bflags & BLIST_USE_10_BYTE_MS)
 		sdev->use_10_for_ms = 1;
+
+	if (inq_result[5] & 0x1)
+		sdev->protection = 1;
 
 	/* set the device running here so that slave configure
 	 * may do I/O */
include/scsi/scsi_cmnd.h +20 -0
@@ -88,6 +88,8 @@ struct scsi_cmnd {
 
 	/* These elements define the operation we ultimately want to perform */
 	struct scsi_data_buffer sdb;
+	struct scsi_data_buffer *prot_sdb;
+
 	unsigned underflow;	/* Return error if less than
 				   this amount is transferred */
 
@@ -209,4 +211,22 @@ static inline int scsi_sg_copy_to_buffer
 				 buf, buflen);
 }
 
+static inline unsigned scsi_prot_sg_count(struct scsi_cmnd *cmd)
+{
+	return cmd->prot_sdb ? cmd->prot_sdb->table.nents : 0;
+}
+
+static inline struct scatterlist *scsi_prot_sglist(struct scsi_cmnd *cmd)
+{
+	return cmd->prot_sdb ? cmd->prot_sdb->table.sgl : NULL;
+}
+
+static inline struct scsi_data_buffer *scsi_prot(struct scsi_cmnd *cmd)
+{
+	return cmd->prot_sdb;
+}
+
+#define scsi_for_each_prot_sg(cmd, sg, nseg, __i)		\
+	for_each_sg(scsi_prot_sglist(cmd), sg, nseg, __i)
+
 #endif /* _SCSI_SCSI_CMND_H */
include/scsi/scsi_device.h +1 -0
@@ -140,6 +140,7 @@ struct scsi_device {
 	unsigned guess_capacity:1;	/* READ_CAPACITY might be too high by 1 */
 	unsigned retry_hwerror:1;	/* Retry HARDWARE_ERROR */
 	unsigned last_sector_bug:1;	/* Always read last sector in a 1 sector read */
+	unsigned protection:1;		/* Data Integrity Field */
 
 	DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */
 	struct list_head event_list;	/* asserted events */
maintainer
#2 lore raw

Re: [PATCH 04 of 10] scsi: Support devices with protection information (DIF)

Quoted 36 lines · Martin K. Petersen

- Add support for an extra scatter-gather list containing protection information.

- Remember devices with protection information turned on in INQUIRY.

---

4 files changed, 62 insertions(+)
drivers/scsi/scsi_lib.c    |   38 ++++++++++++++++++++++++++++++++++++++
drivers/scsi/scsi_scan.c   |    3 +++
include/scsi/scsi_cmnd.h   |   20 ++++++++++++++++++++
include/scsi/scsi_device.h |    1 +
drivers/scsi/scsi_lib.c +6 -0
@@ -778,6 +778,11 @@ void scsi_release_buffers(struct scsi_cm
 		kmem_cache_free(scsi_sdb_cache, bidi_sdb);
 		cmd->request->next_rq->special = NULL;
 	}
+
+	if (scsi_prot_sg_count(cmd)) {
+		scsi_free_sgtable(cmd->prot_sdb);
+		kmem_cache_free(scsi_sdb_cache, cmd->prot_sdb);
+	}
 }
 EXPORT_SYMBOL(scsi_release_buffers);
 
@@ -1031,6 +1036,32 @@ static int scsi_init_sgtable(struct requ
 	return BLKPREP_OK;
 }
 
+static int scsi_protect_io(struct scsi_cmnd *cmd, gfp_t gfp_mask)

drivers/scsi/scsi_lib.c static int scsi_init_sgtable(struct requ · line 1049

Quoted 10 lines
drivers/scsi/scsi_lib.c +10 -0
+{
+	struct request *req;
+	struct scsi_data_buffer *pdb;
+	int ivecs, count;
+
+	req = cmd->request;
+
+	pdb = kmem_cache_zalloc(scsi_sdb_cache, gfp_mask);
+	if (unlikely(pdb == NULL))
+		return BLKPREP_DEFER;

I'm afraid you can't do it like this because it will violate our forward progress guarantees. If this is the last spare command required for writeout, you need to guarantee either this allocation will succeed, or we can proceed without the DIF data.

I know you copied it from the bidirectional stuff, but they don't need forward progress guarantees; something in the mainline does because we could get into a vmwriteout failure case even for a DIF protected device.

Quoted 36 lines
drivers/scsi/scsi_lib.c +22 -0
+
+	ivecs = blk_rq_count_integrity_sg(req);
+
+	if (unlikely(scsi_alloc_sgtable(pdb, ivecs, gfp_mask)))
+		return BLKPREP_DEFER;
+
+	count = blk_rq_map_integrity_sg(req, pdb->table.sgl);
+	BUG_ON(unlikely(count > ivecs));
+
+	cmd->prot_sdb = pdb;
+	cmd->prot_sdb->table.nents = count;
+
+	return BLKPREP_OK;
+}
+
 /*
  * Function:    scsi_init_io()
  *
@@ -1060,6 +1091,13 @@ int scsi_init_io(struct scsi_cmnd *cmd, 
 		error = scsi_init_sgtable(cmd->request->next_rq, bidi_sdb,
 								    GFP_ATOMIC);
 		if (error)
+			goto err_exit;
+	}
+
+	if (blk_integrity_rq(cmd->request)) {
+		error = scsi_protect_io(cmd, gfp_mask);
+
+		if (error != BLKPREP_OK)
 			goto err_exit;
 	}
 

drivers/scsi/scsi_scan.c static int scsi_add_lun(struct scsi_devi · line 887

Quoted 6 lines
drivers/scsi/scsi_scan.c +3 -0
@@ -882,6 +882,9 @@ static int scsi_add_lun(struct scsi_devi
 
 	if (*bflags & BLIST_USE_10_BYTE_MS)
 		sdev->use_10_for_ms = 1;
+
+	if (inq_result[5] & 0x1)
+		sdev->protection = 1;

Is there a reason to have a separate flag here and not just do something like the usual in scsi_device.h:

static inline int scsi_device_protection(struct scsi_device *sdev)

{
	return sdev->inquiry[5] & (1<<0);
}

James

#3 lore raw

Re: [PATCH 04 of 10] scsi: Support devices with protection information (DIF)

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

"James" == James Bottomley <[email protected]> writes:

[Protection sgl alloc + init]

James> I'm afraid you can't do it like this because it will violate James> our forward progress guarantees. If this is the last spare James> command required for writeout, you need to guarantee either James> this allocation will succeed, or we can proceed without the DIF James> data.

We can't just turn off DIF -- that would violate the integrity guarantees. But I'll move the protection sgl to the host command pool.

Quoted 1 line
Quoted 1 line

+ if (inq_result[5] & 0x1) + sdev->protection = 1;

James> Is there a reason to have a separate flag here

Not really. I used to key off of sdp->protection quite a bit but I think the only place I use it now is during sd discovery. So this can simply go away...

Signature · 1 line
Martin K. Petersen	Oracle Linux Engineering
#4 lore raw

PATCH 1/1 sd: Move scsi_disk() accessor function to sd.h

In reply to Martin K. Petersen

---

2 files changed, 5 insertions(+), 5 deletions(-)
drivers/scsi/sd.c |    5 -----
drivers/scsi/sd.h |    5 +++++
drivers/scsi/sd.c +0 -5
@@ -293,11 +293,6 @@ static int sd_major(int major_idx)
 		BUG();
 		return 0;	/* shut up gcc */
 	}
-}
-
-static inline struct scsi_disk *scsi_disk(struct gendisk *disk)
-{
-	return container_of(disk->private_data, struct scsi_disk, driver);
 }
 
 static struct scsi_disk *__scsi_disk_get(struct gendisk *disk)
drivers/scsi/sd.h +5 -0
@@ -48,6 +48,11 @@ struct scsi_disk {
 };
 #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,dev)
 
+static inline struct scsi_disk *scsi_disk(struct gendisk *disk)
+{
+	return container_of(disk->private_data, struct scsi_disk, driver);
+}
+
 #define sd_printk(prefix, sdsk, fmt, a...)				\
         (sdsk)->disk ?							\
 	sdev_printk(prefix, (sdsk)->device, "[%s] " fmt,		\
#5 lore raw

PATCH 1/1 lib: Add support for the T10 Data Integrity Field CRC

In reply to Martin K. Petersen

The SCSI Block Protocol uses this 16-bit CRC to verify the integrity of each data sector. crc_t10dif() is used by sd_dif.c when performing I/O to or from disks formatted with protection information.

---

4 files changed, 83 insertions(+)
include/linux/crc-t10dif.h |    8 +++++
lib/Kconfig                |    7 ++++
lib/Makefile               |    1 
lib/crc-t10dif.c           |   67 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/crc-t10dif.h new file +8 -0
new file mode 100644
@@ -0,0 +1,8 @@
+#ifndef _LINUX_CRC_T10DIF_H
+#define _LINUX_CRC_T10DIF_H
+
+#include <linux/types.h>
+
+__u16 crc_t10dif(unsigned char const *, size_t);
+
+#endif
lib/Kconfig +7 -0
@@ -28,6 +28,13 @@ config CRC16
 	  modules require CRC16 functions, but a module built outside
 	  the kernel tree does. Such modules that use library CRC16
 	  functions require M here.
+
+config CRC_T10DIF
+	tristate "CRC calculation for the T10 Data Integrity Field"
+	help
+	  This option is only needed if a module that's not in the
+	  kernel tree needs to calculate CRC checks for use with the
+	  SCSI data integrity subsystem.
 
 config CRC_ITU_T
 	tristate "CRC ITU-T V.41 functions"
lib/Makefile +1 -0
@@ -45,6 +45,7 @@ obj-$(CONFIG_BITREVERSE) += bitrev.o
 obj-$(CONFIG_BITREVERSE) += bitrev.o
 obj-$(CONFIG_CRC_CCITT)	+= crc-ccitt.o
 obj-$(CONFIG_CRC16)	+= crc16.o
+obj-$(CONFIG_CRC_T10DIF)+= crc-t10dif.o
 obj-$(CONFIG_CRC_ITU_T)	+= crc-itu-t.o
 obj-$(CONFIG_CRC32)	+= crc32.o
 obj-$(CONFIG_CRC7)	+= crc7.o
lib/crc-t10dif.c new file +67 -0

67 lines not on this page · raw message

#6 lore raw

PATCH 1/1 sd: Move sd.h header file

In reply to Martin K. Petersen

Christoph objected to having sd.h in include/scsi since it is internal to the sd driver. Move it to drivers/scsi/sd.h.

---

3 files changed, 58 insertions(+), 58 deletions(-)
drivers/scsi/sd.c |    2 -
drivers/scsi/sd.h |   57 +++++++++++++++++++++++++++++++++++++++++++++++++++++
include/scsi/sd.h |   57 -----------------------------------------------------
drivers/scsi/sd.c +1 -1
@@ -58,8 +58,8 @@
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_ioctl.h>
 #include <scsi/scsicam.h>
-#include <scsi/sd.h>
 
+#include "sd.h"
 #include "scsi_logging.h"
 
 MODULE_AUTHOR("Eric Youngdale");
drivers/scsi/sd.h new file +57 -0

57 lines not on this page · raw message

include/scsi/sd.h deleted +0 -57

57 lines not on this page · raw message

#7 lore raw

PATCH 1/1 scsi: Host protection capabilities

In reply to Martin K. Petersen

Controllers that support protection information must indicate this to the SCSI midlayer so that the ULD can prepare scsi_cmnds accordingly.

This patch implements a host mask and various types of protection:

- DIF Type 1-3 (between HBA and disk)
- Type H (identical to Type 1 for use with non-DIF disks)
- DMA of protection information in a separate scatterlist

The patch also allows the HBA to set the guard type to something different than the T10-mandated CRC.

---

2 files changed, 76 insertions(+)

drivers/scsi/scsi_sysfs.c | 4 ++
include/scsi/scsi_host.h | 72 +++++++++++++++++++++++++++++++++++++++++++++

drivers/scsi/scsi_sysfs.c +4 -0
@@ -249,6 +249,8 @@ shost_rd_attr(can_queue, "%hd\n");
 shost_rd_attr(can_queue, "%hd\n");
 shost_rd_attr(sg_tablesize, "%hu\n");
 shost_rd_attr(unchecked_isa_dma, "%d\n");
+shost_rd_attr(prot_capabilities, "%u\n");
+shost_rd_attr(prot_guard_type, "%hd\n");
 shost_rd_attr2(proc_name, hostt->proc_name, "%s\n");
 
 static struct attribute *scsi_sysfs_shost_attrs[] = {
@@ -263,6 +265,8 @@ static struct attribute *scsi_sysfs_shos
 	&dev_attr_hstate.attr,
 	&dev_attr_supported_mode.attr,
 	&dev_attr_active_mode.attr,
+	&dev_attr_prot_capabilities.attr,
+	&dev_attr_prot_guard_type.attr,
 	NULL
 };
 
include/scsi/scsi_host.h +72 -0

84 lines not on this page · raw message

#8 lore raw

PATCH 1/1 scsi: Do not retry a request whose data integrity check failed

In reply to Martin K. Petersen

If initiator or target reject the I/O due to DIF errors there is no point in retrying.

---

2 files changed, 7 insertions(+)
drivers/scsi/scsi_error.c |    3 +++
drivers/scsi/scsi_lib.c   |    4 ++++
drivers/scsi/scsi_error.c +3 -0
@@ -333,6 +333,9 @@ static int scsi_check_sense(struct scsi_
 		return /* soft_error */ SUCCESS;
 
 	case ABORTED_COMMAND:
+		if (sshdr.asc == 0x10) /* DIF */
+			return SUCCESS;
+
 		return NEEDS_RETRY;
 	case NOT_READY:
 	case UNIT_ATTENTION:
drivers/scsi/scsi_lib.c +4 -0
@@ -945,6 +945,10 @@ void scsi_io_completion(struct scsi_cmnd
 				scsi_requeue_command(q, cmd);
 				return;
 			} else {
+				if (sshdr.asc == 0x10) { /* DIF */
+					scsi_print_result(cmd);
+					scsi_print_sense("", cmd);
+				}
 				scsi_end_request(cmd, -EIO, this_count, 1);
 				return;
 			}
#9 lore raw

PATCH 1/1 scsi: Command protection operation

In reply to Martin K. Petersen

Controllers that support DMA of protection information must be told explicitly how to handle the I/O. The controller has no knowledge of the protection capabilities of the target device so this information must be passed in the scsi_cmnd.

The protection operation tells the HBA whether to generate, strip or verify protection information.

When a scsi_cmnd is reused for error handling, the protection operation must be cleared and saved while error handling is in progress.

---

3 files changed, 32 insertions(+)
drivers/scsi/scsi_error.c |    3 +++
include/scsi/scsi_cmnd.h  |   28 ++++++++++++++++++++++++++++
include/scsi/scsi_eh.h    |    1 +
drivers/scsi/scsi_error.c +3 -0
@@ -653,7 +653,9 @@ void scsi_eh_prep_cmnd(struct scsi_cmnd 
 	ses->sdb = scmd->sdb;
 	ses->next_rq = scmd->request->next_rq;
 	ses->result = scmd->result;
+	ses->prot_op = scmd->prot_op;
 
+	scmd->prot_op = SCSI_PROT_NORMAL;
 	scmd->cmnd = ses->eh_cmnd;
 	memset(scmd->cmnd, 0, BLK_MAX_CDB);
 	memset(&scmd->sdb, 0, sizeof(scmd->sdb));
@@ -711,6 +713,7 @@ void scsi_eh_restore_cmnd(struct scsi_cm
 	scmd->sdb = ses->sdb;
 	scmd->request->next_rq = ses->next_rq;
 	scmd->result = ses->result;
+	scmd->prot_op = ses->prot_op;
 }
 EXPORT_SYMBOL(scsi_eh_restore_cmnd);
 
include/scsi/scsi_cmnd.h +28 -0
@@ -77,6 +77,8 @@ struct scsi_cmnd {
 	int retries;
 	int allowed;
 	int timeout_per_command;
+
+	unsigned char prot_op;
 
 	unsigned short cmd_len;
 	enum dma_data_direction sc_data_direction;
@@ -211,6 +213,32 @@ static inline int scsi_sg_copy_to_buffer
 				 buf, buflen);
 }
 
+/*
+ * The operations below are hints that tell the controller driver how
+ * to handle I/Os with DIF or similar types of protection information.
+ */
+
+enum scsi_prot_operations {
+	/* Normal I/O */
+	SCSI_PROT_NORMAL = 0,
+
+	/* OS-HBA: Protected, HBA-Target: Unprotected */
+	SCSI_PROT_READ_INSERT,
+	SCSI_PROT_WRITE_STRIP,
+
+	/* OS-HBA: Unprotected, HBA-Target: Protected */
+	SCSI_PROT_READ_STRIP,
+	SCSI_PROT_WRITE_INSERT,
+
+	/* OS-HBA: Protected, HBA-Target: Protected */
+	SCSI_PROT_READ_PASS,
+	SCSI_PROT_WRITE_PASS,
+
+	/* OS-HBA: Protected, HBA-Target: Protected, checksum conversion */
+	SCSI_PROT_READ_CONVERT,
+	SCSI_PROT_WRITE_CONVERT,
+};
+
 static inline unsigned scsi_prot_sg_count(struct scsi_cmnd *cmd)
 {
 	return cmd->prot_sdb ? cmd->prot_sdb->table.nents : 0;
include/scsi/scsi_eh.h +1 -0
@@ -75,6 +75,7 @@ struct scsi_eh_save {
 	int result;
 	enum dma_data_direction data_direction;
 	unsigned char cmd_len;
+	unsigned char prot_op;
 	unsigned char *cmnd;
 	struct scsi_data_buffer sdb;
 	struct request *next_rq;
#10 lore raw

PATCH 1/1 scsi: Documentation for the SCSI protection information/DIF support

In reply to Martin K. Petersen

---

1 file changed, 57 insertions(+)

Documentation/scsi/data-integrity.txt | 57 +++++++++++++++++++++++++++++++++

Documentation/scsi/data-integrity.txt new file +57 -0
new file mode 100644
@@ -0,0 +1,57 @@
+----------------------------------------------------------------------
+1.0 INTRODUCTION
+
+For a general overview of the data integrity framework please consult
+Documentation/block/data-integrity.txt.
+
+----------------------------------------------------------------------
+2.0 SCSI LAYER IMPLEMENTATION DETAILS
+
+The scsi_command has been extended with a scatterlist for the
+integrity metadata.  Note that all SCSI mid layer changes refer to
+this using the term "protection information" which is what it is
+called in the T10 spec.
+
+The term DIF (Data Integrity Field) is specific to SCSI disks (SBC).
+The SCSI midlayer doesn't know, or care, about the contents of the
+protection scatterlist, except it calls blk_rq_map_integrity_sg()
+during command initialization.
+
+
+2.1 SCSI DEVICE SCANNING
+
+A SCSI device has the PROTECT bit set in the standard INQUIRY page if
+it supports protection information.  The state of this bit is saved in
+the scsi_device struct.
+
+
+2.2 SCSI DISK SETUP
+
+In the case of a SCSI disk the actual DIF protection format is
+contained in in result of READ CAPACITY(16).  Consequently we have to
+use the 16-byte READ CAPACITY variant if the device is
+protection-capable.
+
+If the device has DIF-enabled we'll negotiate capabilities with the
+HBA.  And if the HBA is capable of protection DMA, the blk_integrity
+profile will be registered.
+
+Currently we only support Type 1 and Type 3.  Type 2 is only defined
+for 32-byte CDBs and is awaiting varlen CDB support.
+
+The controller may support checksum conversion as an optimization.
+Initial benchmarks showed that calculating a 16-bit CRC for each 512
+bytes of an I/O was expensive.  Emulex' hardware had the capability to
+convert an IP checksum to the T10 CRC on the wire.  So as part of the
+negotiation process the checksum algorithm will be selected and the
+blk_integrity profile set accordingly.
+
+----------------------------------------------------------------------
+3.0 HBA INTERFACE
+
+See the following doc:
+
+http://oss.oracle.com/projects/data-integrity/dist/documentation/linux-hba.pdf
+
+----------------------------------------------------------------------
+2007-12-24 Martin K. Petersen <[email protected]>
#11 lore raw

PATCH 1/1 sd: Identify DIF protection type and application tag ownership

In reply to Martin K. Petersen

If a disk is formatted with protection information (Inquiry bit PROTECT=1) it is required to support Read Capacity(16). Force use of the 16-bit command in this case and extract the P_TYPE field which indicates whether the disk is formatted using DIF Type 1, 2 or 3.

The ATO (App Tag Own) bit in the Control Mode Page indicates whether the storage device or the initiator own the contents of the DIF application tag.

---

2 files changed, 149 insertions(+), 4 deletions(-)

drivers/scsi/sd.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++--
drivers/scsi/sd.h | 23 +++++++++

drivers/scsi/sd.c +126 -4
@@ -234,6 +234,24 @@ sd_show_allow_restart(struct device *dev
 	return snprintf(buf, 40, "%d\n", sdkp->device->allow_restart);
 }
 
+static ssize_t
+sd_show_protection_type(struct device *dev, struct device_attribute *attr,
+			char *buf)
+{
+	struct scsi_disk *sdkp = to_scsi_disk(dev);
+
+	return snprintf(buf, 20, "%u\n", sdkp->protection_type);
+}
+
+static ssize_t
+sd_show_app_tag_own(struct device *dev, struct device_attribute *attr,
+		    char *buf)
+{
+	struct scsi_disk *sdkp = to_scsi_disk(dev);
+
+	return snprintf(buf, 20, "%u\n", sdkp->ATO);
+}
+
 static struct device_attribute sd_disk_attrs[] = {
 	__ATTR(cache_type, S_IRUGO|S_IWUSR, sd_show_cache_type,
 	       sd_store_cache_type),
@@ -242,6 +260,8 @@ static struct device_attribute sd_disk_a
 	       sd_store_allow_restart),
 	__ATTR(manage_start_stop, S_IRUGO|S_IWUSR, sd_show_manage_start_stop,
 	       sd_store_manage_start_stop),
+	__ATTR(protection_type, S_IRUGO, sd_show_protection_type, NULL),
+	__ATTR(app_tag_own, S_IRUGO, sd_show_app_tag_own, NULL),
 	__ATTR_NULL,
 };
 
@@ -1163,6 +1183,53 @@ sd_spinup_disk(struct scsi_disk *sdkp)
 	}
 }
 
+
+/*
+ * Determine whether disk supports Data Integrity Field.
+ */
+void sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer)
+{
+	struct scsi_device *sdp = sdkp->device;
+	u8 type;
+
+	if (sdp->protection == 0 || (buffer[12] & 1) == 0)
+		type = 0;
+	else
+		type = ((buffer[12] >> 1) & 7) + 1; /* P_TYPE 0 = Type 1 */
+
+	switch (type) {
+	case SD_DIF_TYPE0_PROTECTION:
+		sd_printk(KERN_NOTICE, sdkp, "formatted without data " \
+			  "integrity protection\n");
+		sdkp->protection_type = 0;
+		break;
+
+	case SD_DIF_TYPE1_PROTECTION:
+	case SD_DIF_TYPE3_PROTECTION:
+		sd_printk(KERN_NOTICE, sdkp, "formatted with DIF Type %d " \
+			  "protection\n", type);
+		sdkp->protection_type = type;
+		break;
+
+	case SD_DIF_TYPE2_PROTECTION:
+		sd_printk(KERN_ERR, sdkp, "formatted with DIF Type 2 "	\
+			  "protection which is currently unsupported. "	\
+			  "Disabling disk!\n");
+		goto disable;
+
+	default:
+		sd_printk(KERN_ERR, sdkp, "formatted with unknown "	\
+			  "protection type %d. Disabling disk!\n", type);
+		goto disable;
+	}
+
+	return;
+
+disable:
+	sdkp->protection_type = 0;
+	sdkp->capacity = 0;
+}
+
 /*
  * read disk capacity
  */
@@ -1172,7 +1239,8 @@ sd_read_capacity(struct scsi_disk *sdkp,
 	unsigned char cmd[16];
 	int the_result, retries;
 	int sector_size = 0;
-	int longrc = 0;
+	/* Force READ CAPACITY(16) when PROTECT=1 */
+	int longrc = sdkp->device->protection ? 1 : 0;
 	struct scsi_sense_hdr sshdr;
 	int sense_valid = 0;
 	struct scsi_device *sdp = sdkp->device;
@@ -1184,8 +1252,8 @@ repeat:
 			memset((void *) cmd, 0, 16);
 			cmd[0] = SERVICE_ACTION_IN;
 			cmd[1] = SAI_READ_CAPACITY_16;
-			cmd[13] = 12;
-			memset((void *) buffer, 0, 12);
+			cmd[13] = 13;
+			memset((void *) buffer, 0, 13);
 		} else {
 			cmd[0] = READ_CAPACITY;
 			memset((void *) &cmd[1], 0, 9);
@@ -1193,7 +1261,7 @@ repeat:
 		}
 		
 		the_result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE,
-					      buffer, longrc ? 12 : 8, &sshdr,
+					      buffer, longrc ? 13 : 8, &sshdr,
 					      SD_TIMEOUT, SD_MAX_RETRIES);
 
 		if (media_not_present(sdkp, &sshdr))
@@ -1268,6 +1336,8 @@ repeat:
 			
 		sector_size = (buffer[8] << 24) |
 			(buffer[9] << 16) | (buffer[10] << 8) | buffer[11];
+
+		sd_read_protection_type(sdkp, buffer);
 	}	
 
 	/* Some devices return the total number of sectors, not the
@@ -1529,6 +1599,56 @@ defaults:
 	sdkp->DPOFUA = 0;
 }
 
+/*
+ * The ATO bit indicates whether the DIF application tag is available
+ * for use by the operating system.
+ */
+void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer)
+{
+	int res, offset;
+	struct scsi_device *sdp = sdkp->device;
+	struct scsi_mode_data data;
+	struct scsi_sense_hdr sshdr;
+
+	if (sdp->type != TYPE_DISK)
+		return;
+
+	if (sdkp->protection_type == 0)
+		return;
+
+	res = scsi_mode_sense(sdp, 1, 0x0a, buffer, 36, SD_TIMEOUT,
+			      SD_MAX_RETRIES, &data, &sshdr);
+
+	if (!scsi_status_is_good(res) || !data.header_length ||
+	    data.length < 6) {
+		sd_printk(KERN_WARNING, sdkp,
+			  "getting Control mode page failed, assume no ATO\n");
+
+		if (scsi_sense_valid(&sshdr))
+			sd_print_sense_hdr(sdkp, &sshdr);
+
+		goto no_ato;
+	}
+
+	offset = data.header_length + data.block_descriptor_length;
+
+	if ((buffer[offset] & 0x3f) != 0x0a) {
+		sd_printk(KERN_ERR, sdkp, "ATO Got wrong page\n");
+		goto no_ato;
+	}
+
+	if ((buffer[offset + 5] & 0x80) == 0)
+		goto no_ato;
+
+	sdkp->ATO = 1;
+	sd_printk(KERN_NOTICE, sdkp, "ATO Enabled\n");
+
+	return;
+
+no_ato:
+	sd_printk(KERN_NOTICE, sdkp, "ATO Disabled\n");
+}
+
 /**
  *	sd_revalidate_disk - called the first time a new disk is seen,
  *	performs disk spin up, read_capacity, etc.
@@ -1565,6 +1685,7 @@ static int sd_revalidate_disk(struct gen
 	sdkp->write_prot = 0;
 	sdkp->WCE = 0;
 	sdkp->RCD = 0;
+	sdkp->ATO = 0;
 
 	sd_spinup_disk(sdkp);
 
@@ -1576,6 +1697,7 @@ static int sd_revalidate_disk(struct gen
 		sd_read_capacity(sdkp, buffer);
 		sd_read_write_protect_flag(sdkp, buffer);
 		sd_read_cache_type(sdkp, buffer);
+		sd_read_app_tag_own(sdkp, buffer);
 	}
 
 	/*
drivers/scsi/sd.h +23 -0
@@ -41,7 +41,9 @@ struct scsi_disk {
 	u32		index;
 	u8		media_present;
 	u8		write_prot;
+	u8		protection_type;/* Data Integrity Field */
 	unsigned	previous_state : 1;
+	unsigned	ATO : 1;	/* state of disk ATO bit */
 	unsigned	WCE : 1;	/* state of disk WCE bit */
 	unsigned	RCD : 1;	/* state of disk RCD bit, unused */
 	unsigned	DPOFUA : 1;	/* state of disk DPOFUA bit */
@@ -59,4 +61,25 @@ static inline struct scsi_disk *scsi_dis
 		    (sdsk)->disk->disk_name, ##a) :			\
 	sdev_printk(prefix, (sdsk)->device, fmt, ##a)
 
+/*
+ * A DIF-capable target device can be formatted with different
+ * protection schemes.  Currently 0 through 3 are defined:
+ *
+ * Type 0 is regular (unprotected) I/O
+ *
+ * Type 1 defines the contents of the guard and reference tags
+ *
+ * Type 2 defines the contents of the guard and reference tags and
+ * uses 32-byte commands to seed the latter
+ *
+ * Type 3 defines the contents of the guard tag only
+ */
+
+enum sd_dif_target_protection_types {
+	SD_DIF_TYPE0_PROTECTION = 0x0,
+	SD_DIF_TYPE1_PROTECTION = 0x1,
+	SD_DIF_TYPE2_PROTECTION = 0x2,
+	SD_DIF_TYPE3_PROTECTION = 0x3,
+};
+
 #endif /* _SCSI_DISK_H */
#12 lore raw

PATCH 1/1 sd: Support for SCSI disk (SBC) Data Integrity Field

In reply to Martin K. Petersen

Support for controllers and disks that implement DIF protection information:

- During command preparation the RDPROTECT/WRPROTECT must be set correctly if the target has DIF enabled.

- READ(6) and WRITE(6) are not supported when DIF is on.

- The controller must be told how to hadle the I/O via the protection operation field in scsi_cmnd.

- sd_dif.c implements the functions required to prepare and complete requests with protection information attached.

---

5 files changed, 591 insertions(+), 3 deletions(-)
drivers/scsi/Kconfig  |    1 
drivers/scsi/Makefile |    2 
drivers/scsi/sd.c     |   26 ++
drivers/scsi/sd.h     |   27 ++
drivers/scsi/sd_dif.c |  538 +++++++++++++++++++++++++++++++++++++++++++++++++
drivers/scsi/Kconfig +1 -0
@@ -63,6 +63,7 @@ config BLK_DEV_SD
 config BLK_DEV_SD
 	tristate "SCSI disk support"
 	depends on SCSI
+	select CRC_T10DIF
 	---help---
 	  If you want to use SCSI hard disks, Fibre Channel disks,
 	  Serial ATA (SATA) or Parallel ATA (PATA) hard disks,
drivers/scsi/Makefile +2 -0
@@ -149,6 +149,8 @@ scsi_tgt-y			+= scsi_tgt_lib.o scsi_tgt_
 scsi_tgt-y			+= scsi_tgt_lib.o scsi_tgt_if.o
 
 sd_mod-objs	:= sd.o
+sd_mod-$(CONFIG_BLK_DEV_INTEGRITY) += sd_dif.o
+
 sr_mod-objs	:= sr.o sr_ioctl.o sr_vendor.o
 ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \
 		:= -DCONFIG_NCR53C8XX_PREFETCH -DSCSI_NCR_BIG_ENDIAN \
drivers/scsi/sd.c +23 -3
@@ -374,6 +374,7 @@ static int sd_prep_fn(struct request_que
 	struct scsi_cmnd *SCpnt;
 	struct scsi_device *sdp = q->queuedata;
 	struct gendisk *disk = rq->rq_disk;
+	struct scsi_disk *sdkp;
 	sector_t block = rq->sector;
 	unsigned int this_count = rq->nr_sectors;
 	unsigned int timeout = sdp->timeout;
@@ -390,6 +391,7 @@ static int sd_prep_fn(struct request_que
 	if (ret != BLKPREP_OK)
 		goto out;
 	SCpnt = rq->special;
+	sdkp = scsi_disk(disk);
 
 	/* from here on until we're complete, any goto out
 	 * is used for a killable error condition */
@@ -479,6 +481,11 @@ static int sd_prep_fn(struct request_que
 		}
 		SCpnt->cmnd[0] = WRITE_6;
 		SCpnt->sc_data_direction = DMA_TO_DEVICE;
+
+		if (blk_integrity_rq(rq) &&
+		    sd_dif_prepare(rq, block, sdp->sector_size) == -EIO)
+			goto out;
+
 	} else if (rq_data_dir(rq) == READ) {
 		SCpnt->cmnd[0] = READ_6;
 		SCpnt->sc_data_direction = DMA_FROM_DEVICE;
@@ -493,8 +500,15 @@ static int sd_prep_fn(struct request_que
 					"writing" : "reading", this_count,
 					rq->nr_sectors));
 
-	SCpnt->cmnd[1] = 0;
-	
+	if (sdkp->protection_type || scsi_prot_sg_count(SCpnt))
+		sd_dif_op(SCpnt);
+
+	/* Set RDPROTECT/WRPROTECT if disk is formatted with DIF */
+	if (scsi_host_dif_type(sdp->host, sdkp->protection_type))
+		SCpnt->cmnd[1] = 1 << 5;
+	else
+		SCpnt->cmnd[1] = 0;
+
 	if (block > 0xffffffff) {
 		SCpnt->cmnd[0] += READ_16 - READ_6;
 		SCpnt->cmnd[1] |= blk_fua_rq(rq) ? 0x8 : 0;
@@ -512,6 +526,7 @@ static int sd_prep_fn(struct request_que
 		SCpnt->cmnd[13] = (unsigned char) this_count & 0xff;
 		SCpnt->cmnd[14] = SCpnt->cmnd[15] = 0;
 	} else if ((this_count > 0xff) || (block > 0x1fffff) ||
+		   SCpnt->device->protection ||
 		   SCpnt->device->use_10_for_rw) {
 		if (this_count > 0xffff)
 			this_count = 0xffff;
@@ -1025,7 +1040,8 @@ static int sd_done(struct scsi_cmnd *SCp
 		good_bytes = xfer_size;
 		break;
 	case ILLEGAL_REQUEST:
-		if (SCpnt->device->use_10_for_rw &&
+		if (SCpnt->device->protection == 0 &&
+		    SCpnt->device->use_10_for_rw &&
 		    (SCpnt->cmnd[0] == READ_10 ||
 		     SCpnt->cmnd[0] == WRITE_10))
 			SCpnt->device->use_10_for_rw = 0;
@@ -1038,6 +1054,9 @@ static int sd_done(struct scsi_cmnd *SCp
 		break;
 	}
  out:
+	if (rq_data_dir(SCpnt->request) == READ && scsi_prot_sg_count(SCpnt))
+		sd_dif_complete(SCpnt, good_bytes);
+
 	return good_bytes;
 }
 
@@ -1831,6 +1850,7 @@ static int sd_probe(struct device *dev)
 
 	dev_set_drvdata(dev, sdkp);
 	add_disk(gd);
+	sd_dif_config_host(sdkp);
 
 	sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
 		  sdp->removable ? "removable " : "");
drivers/scsi/sd.h +27 -0
@@ -82,4 +82,31 @@ enum sd_dif_target_protection_types {
 	SD_DIF_TYPE3_PROTECTION = 0x3,
 };
 
+/*
+ * Data Integrity Field tuple.
+ */
+struct sd_dif_tuple {
+       __be16 guard_tag;	/* Checksum */
+       __be16 app_tag;		/* Opaque storage */
+       __be32 ref_tag;		/* Target LBA or indirect LBA */
+};
+
+#if defined(CONFIG_BLK_DEV_INTEGRITY)
+
+extern unsigned char sd_dif_op(struct scsi_cmnd *);
+extern void sd_dif_config_disk(struct scsi_disk *, unsigned char *);
+extern void sd_dif_config_host(struct scsi_disk *);
+extern int sd_dif_prepare(struct request *rq, sector_t, unsigned int);
+extern void sd_dif_complete(struct scsi_cmnd *, unsigned int);
+
+#else /* CONFIG_BLK_DEV_INTEGRITY */
+
+#define sd_dif_op(a)				(0)
+#define sd_dif_config_disk(a, b)		do { } while (0)
+#define sd_dif_config_host(a)			do { } while (0)
+#define sd_dif_prepare(a, b, c)			(0)
+#define sd_dif_complete(a, b)			(0)
+
+#endif /* CONFIG_BLK_DEV_INTEGRITY */
+
 #endif /* _SCSI_DISK_H */
drivers/scsi/sd_dif.c new file +538 -0

538 lines not on this page · raw message