* [PATCH 00 of 10] SCSI Data Integrity Support
@ 2008-06-25 15:22 Martin K. Petersen
2008-06-25 15:22 ` [PATCH 01 of 10] sd: Move sd.h header file Martin K. Petersen
` (9 more replies)
0 siblings, 10 replies; 13+ messages in thread
From: Martin K. Petersen @ 2008-06-25 15:22 UTC (permalink / raw)
To: James.Bottomley, linux-scsi
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.
--
Martin K. Petersen Oracle Linux Engineering
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 01 of 10] sd: Move sd.h header file
2008-06-25 15:22 [PATCH 00 of 10] SCSI Data Integrity Support Martin K. Petersen
@ 2008-06-25 15:22 ` Martin K. Petersen
2008-06-25 15:22 ` [PATCH 02 of 10] sd: Move scsi_disk() accessor function to sd.h Martin K. Petersen
` (8 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Martin K. Petersen @ 2008-06-25 15:22 UTC (permalink / raw)
To: James.Bottomley, linux-scsi
Christoph objected to having sd.h in include/scsi since it is internal
to the sd driver. Move it to drivers/scsi/sd.h.
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
---
3 files changed, 58 insertions(+), 58 deletions(-)
drivers/scsi/sd.c | 2 -
drivers/scsi/sd.h | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++
include/scsi/sd.h | 57 -----------------------------------------------------
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -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");
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/sd.h
@@ -0,0 +1,57 @@
+#ifndef _SCSI_DISK_H
+#define _SCSI_DISK_H
+
+/*
+ * More than enough for everybody ;) The huge number of majors
+ * is a leftover from 16bit dev_t days, we don't really need that
+ * much numberspace.
+ */
+#define SD_MAJORS 16
+
+/*
+ * This is limited by the naming scheme enforced in sd_probe,
+ * add another character to it if you really need more disks.
+ */
+#define SD_MAX_DISKS (((26 * 26) + 26 + 1) * 26)
+
+/*
+ * Time out in seconds for disks and Magneto-opticals (which are slower).
+ */
+#define SD_TIMEOUT (30 * HZ)
+#define SD_MOD_TIMEOUT (75 * HZ)
+
+/*
+ * Number of allowed retries
+ */
+#define SD_MAX_RETRIES 5
+#define SD_PASSTHROUGH_RETRIES 1
+
+/*
+ * Size of the initial data buffer for mode and read capacity data
+ */
+#define SD_BUF_SIZE 512
+
+struct scsi_disk {
+ struct scsi_driver *driver; /* always &sd_template */
+ struct scsi_device *device;
+ struct device dev;
+ struct gendisk *disk;
+ unsigned int openers; /* protected by BKL for now, yuck */
+ sector_t capacity; /* size in 512-byte sectors */
+ u32 index;
+ u8 media_present;
+ u8 write_prot;
+ unsigned previous_state : 1;
+ 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 */
+};
+#define to_scsi_disk(obj) container_of(obj,struct scsi_disk,dev)
+
+#define sd_printk(prefix, sdsk, fmt, a...) \
+ (sdsk)->disk ? \
+ sdev_printk(prefix, (sdsk)->device, "[%s] " fmt, \
+ (sdsk)->disk->disk_name, ##a) : \
+ sdev_printk(prefix, (sdsk)->device, fmt, ##a)
+
+#endif /* _SCSI_DISK_H */
diff --git a/include/scsi/sd.h b/include/scsi/sd.h
deleted file mode 100644
--- a/include/scsi/sd.h
+++ /dev/null
@@ -1,57 +0,0 @@
-#ifndef _SCSI_DISK_H
-#define _SCSI_DISK_H
-
-/*
- * More than enough for everybody ;) The huge number of majors
- * is a leftover from 16bit dev_t days, we don't really need that
- * much numberspace.
- */
-#define SD_MAJORS 16
-
-/*
- * This is limited by the naming scheme enforced in sd_probe,
- * add another character to it if you really need more disks.
- */
-#define SD_MAX_DISKS (((26 * 26) + 26 + 1) * 26)
-
-/*
- * Time out in seconds for disks and Magneto-opticals (which are slower).
- */
-#define SD_TIMEOUT (30 * HZ)
-#define SD_MOD_TIMEOUT (75 * HZ)
-
-/*
- * Number of allowed retries
- */
-#define SD_MAX_RETRIES 5
-#define SD_PASSTHROUGH_RETRIES 1
-
-/*
- * Size of the initial data buffer for mode and read capacity data
- */
-#define SD_BUF_SIZE 512
-
-struct scsi_disk {
- struct scsi_driver *driver; /* always &sd_template */
- struct scsi_device *device;
- struct device dev;
- struct gendisk *disk;
- unsigned int openers; /* protected by BKL for now, yuck */
- sector_t capacity; /* size in 512-byte sectors */
- u32 index;
- u8 media_present;
- u8 write_prot;
- unsigned previous_state : 1;
- 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 */
-};
-#define to_scsi_disk(obj) container_of(obj,struct scsi_disk,dev)
-
-#define sd_printk(prefix, sdsk, fmt, a...) \
- (sdsk)->disk ? \
- sdev_printk(prefix, (sdsk)->device, "[%s] " fmt, \
- (sdsk)->disk->disk_name, ##a) : \
- sdev_printk(prefix, (sdsk)->device, fmt, ##a)
-
-#endif /* _SCSI_DISK_H */
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 02 of 10] sd: Move scsi_disk() accessor function to sd.h
2008-06-25 15:22 [PATCH 00 of 10] SCSI Data Integrity Support Martin K. Petersen
2008-06-25 15:22 ` [PATCH 01 of 10] sd: Move sd.h header file Martin K. Petersen
@ 2008-06-25 15:22 ` Martin K. Petersen
2008-06-25 15:22 ` [PATCH 03 of 10] lib: Add support for the T10 Data Integrity Field CRC Martin K. Petersen
` (7 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Martin K. Petersen @ 2008-06-25 15:22 UTC (permalink / raw)
To: James.Bottomley, linux-scsi
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
---
2 files changed, 5 insertions(+), 5 deletions(-)
drivers/scsi/sd.c | 5 -----
drivers/scsi/sd.h | 5 +++++
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -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)
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -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, \
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 03 of 10] lib: Add support for the T10 Data Integrity Field CRC
2008-06-25 15:22 [PATCH 00 of 10] SCSI Data Integrity Support Martin K. Petersen
2008-06-25 15:22 ` [PATCH 01 of 10] sd: Move sd.h header file Martin K. Petersen
2008-06-25 15:22 ` [PATCH 02 of 10] sd: Move scsi_disk() accessor function to sd.h Martin K. Petersen
@ 2008-06-25 15:22 ` Martin K. Petersen
2008-06-25 15:22 ` [PATCH 04 of 10] scsi: Support devices with protection information (DIF) Martin K. Petersen
` (6 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Martin K. Petersen @ 2008-06-25 15:22 UTC (permalink / raw)
To: James.Bottomley, linux-scsi
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.
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
---
4 files changed, 83 insertions(+)
include/linux/crc-t10dif.h | 8 +++++
lib/Kconfig | 7 ++++
lib/Makefile | 1
lib/crc-t10dif.c | 67 ++++++++++++++++++++++++++++++++++++++++++++
diff --git a/include/linux/crc-t10dif.h b/include/linux/crc-t10dif.h
new file mode 100644
--- /dev/null
+++ b/include/linux/crc-t10dif.h
@@ -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
diff --git a/lib/Kconfig b/lib/Kconfig
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -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"
diff --git a/lib/Makefile b/lib/Makefile
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -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
diff --git a/lib/crc-t10dif.c b/lib/crc-t10dif.c
new file mode 100644
--- /dev/null
+++ b/lib/crc-t10dif.c
@@ -0,0 +1,67 @@
+/*
+ * T10 Data Integrity Field CRC16 calculation
+ *
+ * Copyright (c) 2007 Oracle Corporation. All rights reserved.
+ * Written by Martin K. Petersen <martin.petersen@oracle.com>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/crc-t10dif.h>
+
+/* Table generated using the following polynomium:
+ * x^16 + x^15 + x^11 + x^9 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
+ * gt: 0x8bb7
+ */
+static const __u16 t10_dif_crc_table[256] = {
+ 0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B,
+ 0xEFBD, 0x640A, 0x7364, 0xF8D3, 0x5DB8, 0xD60F, 0xC161, 0x4AD6,
+ 0x54CD, 0xDF7A, 0xC814, 0x43A3, 0xE6C8, 0x6D7F, 0x7A11, 0xF1A6,
+ 0xBB70, 0x30C7, 0x27A9, 0xAC1E, 0x0975, 0x82C2, 0x95AC, 0x1E1B,
+ 0xA99A, 0x222D, 0x3543, 0xBEF4, 0x1B9F, 0x9028, 0x8746, 0x0CF1,
+ 0x4627, 0xCD90, 0xDAFE, 0x5149, 0xF422, 0x7F95, 0x68FB, 0xE34C,
+ 0xFD57, 0x76E0, 0x618E, 0xEA39, 0x4F52, 0xC4E5, 0xD38B, 0x583C,
+ 0x12EA, 0x995D, 0x8E33, 0x0584, 0xA0EF, 0x2B58, 0x3C36, 0xB781,
+ 0xD883, 0x5334, 0x445A, 0xCFED, 0x6A86, 0xE131, 0xF65F, 0x7DE8,
+ 0x373E, 0xBC89, 0xABE7, 0x2050, 0x853B, 0x0E8C, 0x19E2, 0x9255,
+ 0x8C4E, 0x07F9, 0x1097, 0x9B20, 0x3E4B, 0xB5FC, 0xA292, 0x2925,
+ 0x63F3, 0xE844, 0xFF2A, 0x749D, 0xD1F6, 0x5A41, 0x4D2F, 0xC698,
+ 0x7119, 0xFAAE, 0xEDC0, 0x6677, 0xC31C, 0x48AB, 0x5FC5, 0xD472,
+ 0x9EA4, 0x1513, 0x027D, 0x89CA, 0x2CA1, 0xA716, 0xB078, 0x3BCF,
+ 0x25D4, 0xAE63, 0xB90D, 0x32BA, 0x97D1, 0x1C66, 0x0B08, 0x80BF,
+ 0xCA69, 0x41DE, 0x56B0, 0xDD07, 0x786C, 0xF3DB, 0xE4B5, 0x6F02,
+ 0x3AB1, 0xB106, 0xA668, 0x2DDF, 0x88B4, 0x0303, 0x146D, 0x9FDA,
+ 0xD50C, 0x5EBB, 0x49D5, 0xC262, 0x6709, 0xECBE, 0xFBD0, 0x7067,
+ 0x6E7C, 0xE5CB, 0xF2A5, 0x7912, 0xDC79, 0x57CE, 0x40A0, 0xCB17,
+ 0x81C1, 0x0A76, 0x1D18, 0x96AF, 0x33C4, 0xB873, 0xAF1D, 0x24AA,
+ 0x932B, 0x189C, 0x0FF2, 0x8445, 0x212E, 0xAA99, 0xBDF7, 0x3640,
+ 0x7C96, 0xF721, 0xE04F, 0x6BF8, 0xCE93, 0x4524, 0x524A, 0xD9FD,
+ 0xC7E6, 0x4C51, 0x5B3F, 0xD088, 0x75E3, 0xFE54, 0xE93A, 0x628D,
+ 0x285B, 0xA3EC, 0xB482, 0x3F35, 0x9A5E, 0x11E9, 0x0687, 0x8D30,
+ 0xE232, 0x6985, 0x7EEB, 0xF55C, 0x5037, 0xDB80, 0xCCEE, 0x4759,
+ 0x0D8F, 0x8638, 0x9156, 0x1AE1, 0xBF8A, 0x343D, 0x2353, 0xA8E4,
+ 0xB6FF, 0x3D48, 0x2A26, 0xA191, 0x04FA, 0x8F4D, 0x9823, 0x1394,
+ 0x5942, 0xD2F5, 0xC59B, 0x4E2C, 0xEB47, 0x60F0, 0x779E, 0xFC29,
+ 0x4BA8, 0xC01F, 0xD771, 0x5CC6, 0xF9AD, 0x721A, 0x6574, 0xEEC3,
+ 0xA415, 0x2FA2, 0x38CC, 0xB37B, 0x1610, 0x9DA7, 0x8AC9, 0x017E,
+ 0x1F65, 0x94D2, 0x83BC, 0x080B, 0xAD60, 0x26D7, 0x31B9, 0xBA0E,
+ 0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3
+};
+
+__u16 crc_t10dif(const unsigned char *buffer, size_t len)
+{
+ __u16 crc = 0;
+ unsigned int i;
+
+ for (i = 0 ; i < len ; i++)
+ crc = (crc << 8) ^ t10_dif_crc_table[((crc >> 8) ^ buffer[i]) & 0xff];
+
+ return crc;
+}
+EXPORT_SYMBOL(crc_t10dif);
+
+MODULE_DESCRIPTION("T10 DIF CRC calculation");
+MODULE_LICENSE("GPL");
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 04 of 10] scsi: Support devices with protection information (DIF)
2008-06-25 15:22 [PATCH 00 of 10] SCSI Data Integrity Support Martin K. Petersen
` (2 preceding siblings ...)
2008-06-25 15:22 ` [PATCH 03 of 10] lib: Add support for the T10 Data Integrity Field CRC Martin K. Petersen
@ 2008-06-25 15:22 ` Martin K. Petersen
2008-06-28 15:07 ` James Bottomley
2008-06-25 15:22 ` [PATCH 05 of 10] scsi: Host protection capabilities Martin K. Petersen
` (5 subsequent siblings)
9 siblings, 1 reply; 13+ messages in thread
From: Martin K. Petersen @ 2008-06-25 15:22 UTC (permalink / raw)
To: James.Bottomley, linux-scsi
- Add support for an extra scatter-gather list containing protection
information.
- Remember devices with protection information turned on in INQUIRY.
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
---
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 +
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -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;
}
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -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 */
diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
--- a/include/scsi/scsi_cmnd.h
+++ b/include/scsi/scsi_cmnd.h
@@ -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 */
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -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 */
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 05 of 10] scsi: Host protection capabilities
2008-06-25 15:22 [PATCH 00 of 10] SCSI Data Integrity Support Martin K. Petersen
` (3 preceding siblings ...)
2008-06-25 15:22 ` [PATCH 04 of 10] scsi: Support devices with protection information (DIF) Martin K. Petersen
@ 2008-06-25 15:22 ` Martin K. Petersen
2008-06-25 15:22 ` [PATCH 06 of 10] scsi: Command protection operation Martin K. Petersen
` (4 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Martin K. Petersen @ 2008-06-25 15:22 UTC (permalink / raw)
To: James.Bottomley, linux-scsi
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.
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
---
2 files changed, 76 insertions(+)
drivers/scsi/scsi_sysfs.c | 4 ++
include/scsi/scsi_host.h | 72 +++++++++++++++++++++++++++++++++++++++++++++
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -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
};
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -636,6 +636,10 @@ struct Scsi_Host {
*/
unsigned int max_host_blocked;
+ /* Protection Information */
+ unsigned int prot_capabilities;
+ unsigned char prot_guard_type;
+
/*
* q used for scsi_tgt msgs, async events or any other requests that
* need to be processed in userspace
@@ -756,6 +760,74 @@ extern void scsi_free_host_dev(struct sc
extern void scsi_free_host_dev(struct scsi_device *);
extern struct scsi_device *scsi_get_host_dev(struct Scsi_Host *);
+/*
+ * DIF defines the exchange of protection information between
+ * initiator and SBC block device.
+ *
+ * DIX defines the exchange of protection information between OS and
+ * initiator.
+ */
+enum scsi_host_prot_capabilities {
+ SHOST_DIF_TYPE1_PROTECTION = 1 << 0, /* T10 DIF Type 1 */
+ SHOST_DIF_TYPE2_PROTECTION = 1 << 1, /* T10 DIF Type 2 */
+ SHOST_DIF_TYPE3_PROTECTION = 1 << 2, /* T10 DIF Type 3 */
+
+ SHOST_DIX_TYPEH_PROTECTION = 1 << 6, /* DIX between OS and HBA */
+ SHOST_DIX_PROTECTION_DMA = 1 << 7, /* DIX with DIF Type 1-3 */
+};
+
+/*
+ * SCSI hosts which support the Data Integrity Extensions must
+ * indicate their capabilities by setting the prot_capabilities using
+ * this call.
+ */
+static inline void scsi_host_set_prot(struct Scsi_Host *shost, unsigned int mask)
+{
+ shost->prot_capabilities = mask;
+}
+
+static inline unsigned int scsi_host_get_prot(struct Scsi_Host *shost)
+{
+ return shost->prot_capabilities;
+}
+
+static inline unsigned int scsi_host_dif_type(struct Scsi_Host *shost, unsigned int target_type)
+{
+ if (target_type == 0)
+ return shost->prot_capabilities & SHOST_DIX_TYPEH_PROTECTION;
+
+ return shost->prot_capabilities & (1 << (target_type - 1));
+}
+
+static inline unsigned int scsi_host_dif_dma(struct Scsi_Host *shost)
+{
+ return shost->prot_capabilities & SHOST_DIX_PROTECTION_DMA;
+}
+
+/*
+ * All DIX-capable initiators must support the T10-mandated CRC
+ * checksum. Controllers can optionally implement the IP checksum
+ * scheme which has much lower impact on system performance. Note
+ * that the main rationale for the checksum is to match integrity
+ * metadata with data. Detecting bit errors are a job for ECC memory
+ * and buses.
+ */
+
+enum scsi_host_guard_type {
+ SHOST_DIX_GUARD_CRC = 1 << 0,
+ SHOST_DIX_GUARD_IP = 1 << 1,
+};
+
+static inline void scsi_host_set_guard(struct Scsi_Host *shost, unsigned char type)
+{
+ shost->prot_guard_type = type;
+}
+
+static inline unsigned char scsi_host_get_guard(struct Scsi_Host *shost)
+{
+ return shost->prot_guard_type;
+}
+
/* legacy interfaces */
extern struct Scsi_Host *scsi_register(struct scsi_host_template *, int);
extern void scsi_unregister(struct Scsi_Host *);
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 06 of 10] scsi: Command protection operation
2008-06-25 15:22 [PATCH 00 of 10] SCSI Data Integrity Support Martin K. Petersen
` (4 preceding siblings ...)
2008-06-25 15:22 ` [PATCH 05 of 10] scsi: Host protection capabilities Martin K. Petersen
@ 2008-06-25 15:22 ` Martin K. Petersen
2008-06-25 15:22 ` [PATCH 07 of 10] scsi: Do not retry a request whose data integrity check failed Martin K. Petersen
` (3 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Martin K. Petersen @ 2008-06-25 15:22 UTC (permalink / raw)
To: James.Bottomley, linux-scsi
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.
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
---
3 files changed, 32 insertions(+)
drivers/scsi/scsi_error.c | 3 +++
include/scsi/scsi_cmnd.h | 28 ++++++++++++++++++++++++++++
include/scsi/scsi_eh.h | 1 +
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -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);
diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
--- a/include/scsi/scsi_cmnd.h
+++ b/include/scsi/scsi_cmnd.h
@@ -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;
diff --git a/include/scsi/scsi_eh.h b/include/scsi/scsi_eh.h
--- a/include/scsi/scsi_eh.h
+++ b/include/scsi/scsi_eh.h
@@ -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;
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 07 of 10] scsi: Do not retry a request whose data integrity check failed
2008-06-25 15:22 [PATCH 00 of 10] SCSI Data Integrity Support Martin K. Petersen
` (5 preceding siblings ...)
2008-06-25 15:22 ` [PATCH 06 of 10] scsi: Command protection operation Martin K. Petersen
@ 2008-06-25 15:22 ` Martin K. Petersen
2008-06-25 15:22 ` [PATCH 08 of 10] scsi: Documentation for the SCSI protection information/DIF support Martin K. Petersen
` (2 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Martin K. Petersen @ 2008-06-25 15:22 UTC (permalink / raw)
To: James.Bottomley, linux-scsi
If initiator or target reject the I/O due to DIF errors there is no
point in retrying.
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
---
2 files changed, 7 insertions(+)
drivers/scsi/scsi_error.c | 3 +++
drivers/scsi/scsi_lib.c | 4 ++++
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -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:
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -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;
}
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 08 of 10] scsi: Documentation for the SCSI protection information/DIF support
2008-06-25 15:22 [PATCH 00 of 10] SCSI Data Integrity Support Martin K. Petersen
` (6 preceding siblings ...)
2008-06-25 15:22 ` [PATCH 07 of 10] scsi: Do not retry a request whose data integrity check failed Martin K. Petersen
@ 2008-06-25 15:22 ` Martin K. Petersen
2008-06-25 15:22 ` [PATCH 09 of 10] sd: Identify DIF protection type and application tag ownership Martin K. Petersen
2008-06-25 15:22 ` [PATCH 10 of 10] sd: Support for SCSI disk (SBC) Data Integrity Field Martin K. Petersen
9 siblings, 0 replies; 13+ messages in thread
From: Martin K. Petersen @ 2008-06-25 15:22 UTC (permalink / raw)
To: James.Bottomley, linux-scsi
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
---
1 file changed, 57 insertions(+)
Documentation/scsi/data-integrity.txt | 57 +++++++++++++++++++++++++++++++++
diff --git a/Documentation/scsi/data-integrity.txt b/Documentation/scsi/data-integrity.txt
new file mode 100644
--- /dev/null
+++ b/Documentation/scsi/data-integrity.txt
@@ -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 <martin.petersen@oracle.com>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 09 of 10] sd: Identify DIF protection type and application tag ownership
2008-06-25 15:22 [PATCH 00 of 10] SCSI Data Integrity Support Martin K. Petersen
` (7 preceding siblings ...)
2008-06-25 15:22 ` [PATCH 08 of 10] scsi: Documentation for the SCSI protection information/DIF support Martin K. Petersen
@ 2008-06-25 15:22 ` Martin K. Petersen
2008-06-25 15:22 ` [PATCH 10 of 10] sd: Support for SCSI disk (SBC) Data Integrity Field Martin K. Petersen
9 siblings, 0 replies; 13+ messages in thread
From: Martin K. Petersen @ 2008-06-25 15:22 UTC (permalink / raw)
To: James.Bottomley, linux-scsi
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.
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
---
2 files changed, 149 insertions(+), 4 deletions(-)
drivers/scsi/sd.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++--
drivers/scsi/sd.h | 23 +++++++++
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -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);
}
/*
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -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 */
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 10 of 10] sd: Support for SCSI disk (SBC) Data Integrity Field
2008-06-25 15:22 [PATCH 00 of 10] SCSI Data Integrity Support Martin K. Petersen
` (8 preceding siblings ...)
2008-06-25 15:22 ` [PATCH 09 of 10] sd: Identify DIF protection type and application tag ownership Martin K. Petersen
@ 2008-06-25 15:22 ` Martin K. Petersen
9 siblings, 0 replies; 13+ messages in thread
From: Martin K. Petersen @ 2008-06-25 15:22 UTC (permalink / raw)
To: James.Bottomley, linux-scsi
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.
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
---
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 +++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -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,
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -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 \
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -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 " : "");
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -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 */
diff --git a/drivers/scsi/sd_dif.c b/drivers/scsi/sd_dif.c
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/sd_dif.c
@@ -0,0 +1,538 @@
+/*
+ * sd_dif.c - SCSI Data Integrity Field
+ *
+ * Copyright (C) 2007, 2008 Oracle Corporation
+ * Written by: Martin K. Petersen <martin.petersen@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ * USA.
+ *
+ */
+
+#include <linux/blkdev.h>
+#include <linux/crc-t10dif.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_driver.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsicam.h>
+
+#include <net/checksum.h>
+
+#include "sd.h"
+
+typedef __u16 (csum_fn) (void *, unsigned int);
+
+static __u16 sd_dif_crc_fn(void *data, unsigned int len)
+{
+ return cpu_to_be16(crc_t10dif(data, len));
+}
+
+static __u16 sd_dif_ip_fn(void *data, unsigned int len)
+{
+ return ip_compute_csum(data, len);
+}
+
+/*
+ * Type 1 and Type 2 protection use the same format: 16 bit guard tag,
+ * 16 bit app tag, 32 bit reference tag.
+ */
+static void sd_dif_type1_generate(struct blk_integrity_exchg *bix, csum_fn *fn)
+{
+ void *buf = bix->data_buf;
+ struct sd_dif_tuple *sdt = bix->prot_buf;
+ sector_t sector = bix->sector;
+ unsigned int i;
+
+ for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) {
+ sdt->guard_tag = fn(buf, bix->sector_size);
+ sdt->ref_tag = cpu_to_be32(sector & 0xffffffff);
+ sdt->app_tag = 0;
+
+ buf += bix->sector_size;
+ sector++;
+ }
+}
+
+static void sd_dif_type1_generate_crc(struct blk_integrity_exchg *bix)
+{
+ sd_dif_type1_generate(bix, sd_dif_crc_fn);
+}
+
+static void sd_dif_type1_generate_ip(struct blk_integrity_exchg *bix)
+{
+ sd_dif_type1_generate(bix, sd_dif_ip_fn);
+}
+
+static int sd_dif_type1_verify(struct blk_integrity_exchg *bix, csum_fn *fn)
+{
+ void *buf = bix->data_buf;
+ struct sd_dif_tuple *sdt = bix->prot_buf;
+ sector_t sector = bix->sector;
+ unsigned int i;
+ __u16 csum;
+
+ for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) {
+ /* Unwritten sectors */
+ if (sdt->app_tag == 0xffff)
+ return 0;
+
+ /* Bad ref tag received from disk */
+ if (sdt->ref_tag == 0xffffffff) {
+ printk(KERN_ERR
+ "%s: bad phys ref tag on sector %lu\n",
+ bix->disk_name, (unsigned long)sector);
+ return -EIO;
+ }
+
+ if (be32_to_cpu(sdt->ref_tag) != (sector & 0xffffffff)) {
+ printk(KERN_ERR
+ "%s: ref tag error on sector %lu (rcvd %u)\n",
+ bix->disk_name, (unsigned long)sector,
+ be32_to_cpu(sdt->ref_tag));
+ return -EIO;
+ }
+
+ csum = fn(buf, bix->sector_size);
+
+ if (sdt->guard_tag != csum) {
+ printk(KERN_ERR "%s: guard tag error on sector %lu " \
+ "(rcvd %04x, data %04x)\n", bix->disk_name,
+ (unsigned long)sector,
+ be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum));
+ return -EIO;
+ }
+
+ buf += bix->sector_size;
+ sector++;
+ }
+
+ return 0;
+}
+
+static int sd_dif_type1_verify_crc(struct blk_integrity_exchg *bix)
+{
+ return sd_dif_type1_verify(bix, sd_dif_crc_fn);
+}
+
+static int sd_dif_type1_verify_ip(struct blk_integrity_exchg *bix)
+{
+ return sd_dif_type1_verify(bix, sd_dif_ip_fn);
+}
+
+/*
+ * Functions for interleaving and deinterleaving application tags
+ */
+static void sd_dif_type1_set_tag(void *prot, void *tag_buf, unsigned int sectors)
+{
+ struct sd_dif_tuple *sdt = prot;
+ char *tag = tag_buf;
+ unsigned int i, j;
+
+ for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) {
+ sdt->app_tag = tag[j] << 8 | tag[j+1];
+ BUG_ON(sdt->app_tag == 0xffff);
+ }
+}
+
+static void sd_dif_type1_get_tag(void *prot, void *tag_buf, unsigned int sectors)
+{
+ struct sd_dif_tuple *sdt = prot;
+ char *tag = tag_buf;
+ unsigned int i, j;
+
+ for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) {
+ tag[j] = (sdt->app_tag & 0xff00) >> 8;
+ tag[j+1] = sdt->app_tag & 0xff;
+ }
+}
+
+static struct blk_integrity dif_type1_integrity_crc = {
+ .name = "T10-DIF-TYPE1-CRC",
+ .generate_fn = sd_dif_type1_generate_crc,
+ .verify_fn = sd_dif_type1_verify_crc,
+ .get_tag_fn = sd_dif_type1_get_tag,
+ .set_tag_fn = sd_dif_type1_set_tag,
+ .tuple_size = sizeof(struct sd_dif_tuple),
+ .tag_size = 0,
+};
+
+static struct blk_integrity dif_type1_integrity_ip = {
+ .name = "T10-DIF-TYPE1-IP",
+ .generate_fn = sd_dif_type1_generate_ip,
+ .verify_fn = sd_dif_type1_verify_ip,
+ .get_tag_fn = sd_dif_type1_get_tag,
+ .set_tag_fn = sd_dif_type1_set_tag,
+ .tuple_size = sizeof(struct sd_dif_tuple),
+ .tag_size = 0,
+};
+
+
+/*
+ * Type 3 protection has a 16-bit guard tag and 16 + 32 bits of opaque tag space.
+ */
+static void sd_dif_type3_generate(struct blk_integrity_exchg *bix, csum_fn *fn)
+{
+ void *buf = bix->data_buf;
+ struct sd_dif_tuple *sdt = bix->prot_buf;
+ unsigned int i;
+
+ for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) {
+ sdt->guard_tag = fn(buf, bix->sector_size);
+ sdt->ref_tag = 0;
+ sdt->app_tag = 0;
+
+ buf += bix->sector_size;
+ }
+}
+
+static void sd_dif_type3_generate_crc(struct blk_integrity_exchg *bix)
+{
+ sd_dif_type3_generate(bix, sd_dif_crc_fn);
+}
+
+static void sd_dif_type3_generate_ip(struct blk_integrity_exchg *bix)
+{
+ sd_dif_type3_generate(bix, sd_dif_ip_fn);
+}
+
+static int sd_dif_type3_verify(struct blk_integrity_exchg *bix, csum_fn *fn)
+{
+ void *buf = bix->data_buf;
+ struct sd_dif_tuple *sdt = bix->prot_buf;
+ sector_t sector = bix->sector;
+ unsigned int i;
+ __u16 csum;
+
+ for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) {
+ /* Unwritten sectors */
+ if (sdt->app_tag == 0xffff && sdt->ref_tag == 0xffffffff)
+ return 0;
+
+ csum = fn(buf, bix->sector_size);
+
+ if (sdt->guard_tag != csum) {
+ printk(KERN_ERR "%s: guard tag error on sector %lu " \
+ "(rcvd %04x, data %04x)\n", bix->disk_name,
+ (unsigned long)sector,
+ be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum));
+ return -EIO;
+ }
+
+ buf += bix->sector_size;
+ sector++;
+ }
+
+ return 0;
+}
+
+static int sd_dif_type3_verify_crc(struct blk_integrity_exchg *bix)
+{
+ return sd_dif_type3_verify(bix, sd_dif_crc_fn);
+}
+
+static int sd_dif_type3_verify_ip(struct blk_integrity_exchg *bix)
+{
+ return sd_dif_type3_verify(bix, sd_dif_ip_fn);
+}
+
+static void sd_dif_type3_set_tag(void *prot, void *tag_buf, unsigned int sectors)
+{
+ struct sd_dif_tuple *sdt = prot;
+ char *tag = tag_buf;
+ unsigned int i, j;
+
+ for (i = 0, j = 0 ; i < sectors ; i++, j += 6, sdt++) {
+ sdt->app_tag = tag[j] << 8 | tag[j+1];
+ sdt->ref_tag = tag[j+2] << 24 | tag[j+3] << 16 |
+ tag[j+4] << 8 | tag[j+5];
+ }
+}
+
+static void sd_dif_type3_get_tag(void *prot, void *tag_buf, unsigned int sectors)
+{
+ struct sd_dif_tuple *sdt = prot;
+ char *tag = tag_buf;
+ unsigned int i, j;
+
+ for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) {
+ tag[j] = (sdt->app_tag & 0xff00) >> 8;
+ tag[j+1] = sdt->app_tag & 0xff;
+ tag[j+2] = (sdt->ref_tag & 0xff000000) >> 24;
+ tag[j+3] = (sdt->ref_tag & 0xff0000) >> 16;
+ tag[j+4] = (sdt->ref_tag & 0xff00) >> 8;
+ tag[j+5] = sdt->ref_tag & 0xff;
+ BUG_ON(sdt->app_tag == 0xffff || sdt->ref_tag == 0xffffffff);
+ }
+}
+
+static struct blk_integrity dif_type3_integrity_crc = {
+ .name = "T10-DIF-TYPE3-CRC",
+ .generate_fn = sd_dif_type3_generate_crc,
+ .verify_fn = sd_dif_type3_verify_crc,
+ .get_tag_fn = sd_dif_type3_get_tag,
+ .set_tag_fn = sd_dif_type3_set_tag,
+ .tuple_size = sizeof(struct sd_dif_tuple),
+ .tag_size = 0,
+};
+
+static struct blk_integrity dif_type3_integrity_ip = {
+ .name = "T10-DIF-TYPE3-IP",
+ .generate_fn = sd_dif_type3_generate_ip,
+ .verify_fn = sd_dif_type3_verify_ip,
+ .get_tag_fn = sd_dif_type3_get_tag,
+ .set_tag_fn = sd_dif_type3_set_tag,
+ .tuple_size = sizeof(struct sd_dif_tuple),
+ .tag_size = 0,
+};
+
+/*
+ * Configure exchange of protection information between OS and HBA.
+ */
+void sd_dif_config_host(struct scsi_disk *sdkp)
+{
+ struct scsi_device *sdp = sdkp->device;
+ struct gendisk *disk = sdkp->disk;
+ u8 type = sdkp->protection_type;
+
+ /* Does HBA support protection DMA? */
+ if (scsi_host_dif_dma(sdp->host) == 0) {
+
+ if (type)
+ sd_printk(KERN_NOTICE, sdkp, "Type %d protection " \
+ "unsupported by HBA. No protection DMA!\n",
+ type);
+
+ return;
+ }
+
+ /* Does HBA support this type? */
+ if (scsi_host_dif_type(sdp->host, type) == 0) {
+ sd_printk(KERN_NOTICE, sdkp, "Type %d protection " \
+ "unsupported by HBA. Disabling DIF!\n", type);
+ sdkp->protection_type = 0;
+
+ return;
+ }
+
+ if (scsi_host_get_guard(sdkp->device->host) & SHOST_DIX_GUARD_IP)
+ if (type == SD_DIF_TYPE3_PROTECTION)
+ blk_integrity_register(disk, &dif_type3_integrity_ip);
+ else
+ blk_integrity_register(disk, &dif_type1_integrity_ip);
+ else
+ if (type == SD_DIF_TYPE3_PROTECTION)
+ blk_integrity_register(disk, &dif_type3_integrity_crc);
+ else
+ blk_integrity_register(disk, &dif_type1_integrity_crc);
+
+ sd_printk(KERN_INFO, sdkp,
+ "Enabling %s integrity protection\n", disk->integrity->name);
+
+ /* Signal to block layer that we support sector tagging */
+ if (type && sdkp->ATO) {
+ if (type == SD_DIF_TYPE3_PROTECTION)
+ disk->integrity->tag_size = sizeof(u16) + sizeof(u32);
+ else
+ disk->integrity->tag_size = sizeof(u16);
+
+ sd_printk(KERN_INFO, sdkp, "DIF application tag size %u\n",
+ disk->integrity->tag_size);
+ }
+}
+
+/*
+ * DIF DMA operation magic decoder ring. DIF-capable HBA drivers
+ * should call this function in their queuecommand to determine how to
+ * handle the I/O.
+ */
+unsigned char sd_dif_op(struct scsi_cmnd *scmd)
+{
+ struct request *rq = scmd->request;
+ struct scsi_disk *sdkp = scsi_disk(rq->rq_disk);
+ int hba_to_disk, os_to_hba, csum_convert;
+
+ hba_to_disk = sdkp->protection_type;
+ os_to_hba = scsi_prot_sg_count(scmd);
+
+ /* Convert checksum? */
+ if (scsi_host_get_guard(scmd->device->host) == SHOST_DIX_GUARD_IP)
+ csum_convert = 1;
+ else
+ csum_convert = 0;
+
+ switch (scmd->cmnd[0]) {
+ case READ_10:
+ case READ_12:
+ case READ_16:
+ if (hba_to_disk && os_to_hba)
+ return csum_convert ?
+ SCSI_PROT_READ_CONVERT :
+ SCSI_PROT_READ_PASS;
+
+ else if (hba_to_disk && !os_to_hba)
+ return SCSI_PROT_READ_STRIP;
+
+ else if (!hba_to_disk && os_to_hba)
+ return SCSI_PROT_READ_INSERT;
+
+ break;
+
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_16:
+ if (hba_to_disk && os_to_hba)
+ return csum_convert ?
+ SCSI_PROT_WRITE_CONVERT :
+ SCSI_PROT_WRITE_PASS;
+
+ else if (hba_to_disk && !os_to_hba)
+ return SCSI_PROT_WRITE_INSERT;
+
+ else if (!hba_to_disk && os_to_hba)
+ return SCSI_PROT_WRITE_STRIP;
+
+ break;
+ }
+
+ return SCSI_PROT_NORMAL;
+}
+
+/*
+ * The virtual start sector is the one that was originally submitted
+ * by the block layer. Due to partitioning, MD/DM cloning, etc. the
+ * actual physical start sector is likely to be different. Remap
+ * protection information to match the physical LBA.
+ *
+ * From a protocol perspective there's a slight difference between
+ * Type 1 and 2. The latter uses 32-byte CDBs exclusively, and the
+ * reference tag is seeded in the CDB. This gives us the potential to
+ * avoid virt->phys remapping during write. However, at read time we
+ * don't know whether the virt sector is the same as when we wrote it
+ * (we could be reading from real disk as opposed to MD/DM device. So
+ * we always remap Type 2 making it identical to Type 1.
+ *
+ * Type 3 does not have a reference tag so no remapping is required.
+ */
+int sd_dif_prepare(struct request *rq, sector_t hw_sector, unsigned int sector_sz)
+{
+ const int tuple_sz = sizeof(struct sd_dif_tuple);
+ struct bio *bio;
+ struct scsi_disk *sdkp;
+ struct sd_dif_tuple *sdt;
+ unsigned int i, j;
+ u32 phys, virt;
+
+ /* Already remapped? */
+ if (rq->cmd_flags & REQ_INTEGRITY)
+ return 0;
+
+ sdkp = rq->bio->bi_bdev->bd_disk->private_data;
+
+ if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION)
+ return 0;
+
+ rq->cmd_flags |= REQ_INTEGRITY;
+ phys = hw_sector & 0xffffffff;
+
+ __rq_for_each_bio(bio, rq) {
+ struct bio_vec *iv;
+
+ virt = bio->bi_integrity->bip_sector & 0xffffffff;
+
+ bip_for_each_vec(iv, bio->bi_integrity, i) {
+ sdt = kmap_atomic(iv->bv_page, KM_USER0) + iv->bv_offset;
+
+ for (j = 0 ; j < iv->bv_len ; j += tuple_sz, sdt++) {
+
+ if (be32_to_cpu(sdt->ref_tag) != virt)
+ goto error;
+
+ sdt->ref_tag = cpu_to_be32(phys);
+ virt++;
+ phys++;
+ }
+
+ kunmap_atomic(sdt, KM_USER0);
+ }
+ }
+
+ return 0;
+
+error:
+ sd_printk(KERN_ERR, sdkp, "%s: virt %u, phys %u, ref %u\n",
+ __func__, virt, phys, be32_to_cpu(sdt->ref_tag));
+
+ return -EIO;
+}
+
+/*
+ * Remap physical sector values in the reference tag to the virtual
+ * values expected by the block layer.
+ */
+void sd_dif_complete(struct scsi_cmnd *scmd, unsigned int good_bytes)
+{
+ const int tuple_sz = sizeof(struct sd_dif_tuple);
+ struct scsi_disk *sdkp;
+ struct bio *bio;
+ struct sd_dif_tuple *sdt;
+ unsigned int i, j, sectors, sector_sz;
+ u32 phys, virt;
+
+ sdkp = scsi_disk(scmd->request->rq_disk);
+
+ if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION)
+ return;
+
+ sector_sz = scmd->device->sector_size;
+ sectors = good_bytes / sector_sz;
+
+ phys = scmd->request->sector & 0xffffffff;
+ if (sector_sz == 4096)
+ phys >>= 3;
+
+ __rq_for_each_bio(bio, scmd->request) {
+ struct bio_vec *iv;
+
+ virt = bio->bi_integrity->bip_sector & 0xffffffff;
+
+ bip_for_each_vec(iv, bio->bi_integrity, i) {
+ sdt = kmap_atomic(iv->bv_page, KM_USER0) + iv->bv_offset;
+
+ for (j = 0 ; j < iv->bv_len ; j += tuple_sz, sdt++) {
+
+ if (sectors == 0)
+ return;
+
+ if (be32_to_cpu(sdt->ref_tag) != phys &&
+ sdt->app_tag != 0xffff)
+ sdt->ref_tag = 0xffffffff; /* Bad ref */
+ else
+ sdt->ref_tag = cpu_to_be32(virt);
+
+ virt++;
+ phys++;
+ sectors--;
+ }
+
+ kunmap_atomic(sdt, KM_USER0);
+ }
+ }
+}
+
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 04 of 10] scsi: Support devices with protection information (DIF)
2008-06-25 15:22 ` [PATCH 04 of 10] scsi: Support devices with protection information (DIF) Martin K. Petersen
@ 2008-06-28 15:07 ` James Bottomley
2008-06-30 15:24 ` Martin K. Petersen
0 siblings, 1 reply; 13+ messages in thread
From: James Bottomley @ 2008-06-28 15:07 UTC (permalink / raw)
To: Martin K. Petersen; +Cc: linux-scsi
On Wed, 2008-06-25 at 11:22 -0400, Martin K. Petersen wrote:
> - Add support for an extra scatter-gather list containing protection
> information.
>
> - Remember devices with protection information turned on in INQUIRY.
>
> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
>
> ---
> 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 +
>
>
>
> diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
> --- a/drivers/scsi/scsi_lib.c
> +++ b/drivers/scsi/scsi_lib.c
> @@ -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;
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.
> +
> + 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;
> }
>
> diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
> --- a/drivers/scsi/scsi_scan.c
> +++ b/drivers/scsi/scsi_scan.c
> @@ -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
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 04 of 10] scsi: Support devices with protection information (DIF)
2008-06-28 15:07 ` James Bottomley
@ 2008-06-30 15:24 ` Martin K. Petersen
0 siblings, 0 replies; 13+ messages in thread
From: Martin K. Petersen @ 2008-06-30 15:24 UTC (permalink / raw)
To: James Bottomley; +Cc: Martin K. Petersen, linux-scsi
>>>>> "James" == James Bottomley <James.Bottomley@HansenPartnership.com> 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.
>> + 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...
--
Martin K. Petersen Oracle Linux Engineering
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2008-06-30 15:24 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-06-25 15:22 [PATCH 00 of 10] SCSI Data Integrity Support Martin K. Petersen
2008-06-25 15:22 ` [PATCH 01 of 10] sd: Move sd.h header file Martin K. Petersen
2008-06-25 15:22 ` [PATCH 02 of 10] sd: Move scsi_disk() accessor function to sd.h Martin K. Petersen
2008-06-25 15:22 ` [PATCH 03 of 10] lib: Add support for the T10 Data Integrity Field CRC Martin K. Petersen
2008-06-25 15:22 ` [PATCH 04 of 10] scsi: Support devices with protection information (DIF) Martin K. Petersen
2008-06-28 15:07 ` James Bottomley
2008-06-30 15:24 ` Martin K. Petersen
2008-06-25 15:22 ` [PATCH 05 of 10] scsi: Host protection capabilities Martin K. Petersen
2008-06-25 15:22 ` [PATCH 06 of 10] scsi: Command protection operation Martin K. Petersen
2008-06-25 15:22 ` [PATCH 07 of 10] scsi: Do not retry a request whose data integrity check failed Martin K. Petersen
2008-06-25 15:22 ` [PATCH 08 of 10] scsi: Documentation for the SCSI protection information/DIF support Martin K. Petersen
2008-06-25 15:22 ` [PATCH 09 of 10] sd: Identify DIF protection type and application tag ownership Martin K. Petersen
2008-06-25 15:22 ` [PATCH 10 of 10] sd: Support for SCSI disk (SBC) Data Integrity Field Martin K. Petersen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).