block: Add T10 Protection Information functions
authorMartin K. Petersen <martin.petersen@oracle.com>
Fri, 26 Sep 2014 23:20:07 +0000 (19:20 -0400)
committerJens Axboe <axboe@fb.com>
Sat, 27 Sep 2014 15:14:59 +0000 (09:14 -0600)
The T10 Protection Information format is also used by some devices that
do not go through the SCSI layer (virtual block devices, NVMe). Relocate
the relevant functions to a block layer library that can be used without
involving SCSI.

Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jens Axboe <axboe@fb.com>
block/Kconfig
block/Makefile
block/t10-pi.c [new file with mode: 0644]
drivers/scsi/Kconfig
drivers/scsi/sd_dif.c
include/linux/crc-t10dif.h
include/linux/t10-pi.h [new file with mode: 0644]

index 2429515c05c2ed1b6f457a60def817c33c178651..161491d0a879ed11ce2276a1923a60bc1d92479e 100644 (file)
@@ -77,6 +77,7 @@ config BLK_DEV_BSGLIB
 
 config BLK_DEV_INTEGRITY
        bool "Block layer data integrity support"
+       select CRC_T10DIF if BLK_DEV_INTEGRITY
        ---help---
        Some storage devices allow extra information to be
        stored/retrieved to help protect the data.  The block layer
index a2ce6ac935ecf566ec8e956f666f76feec4e9682..00ecc97629db783d735798c961579ee1a531c367 100644 (file)
@@ -20,6 +20,6 @@ obj-$(CONFIG_IOSCHED_DEADLINE)        += deadline-iosched.o
 obj-$(CONFIG_IOSCHED_CFQ)      += cfq-iosched.o
 
 obj-$(CONFIG_BLOCK_COMPAT)     += compat_ioctl.o
-obj-$(CONFIG_BLK_DEV_INTEGRITY)        += blk-integrity.o
 obj-$(CONFIG_BLK_CMDLINE_PARSER)       += cmdline-parser.o
-obj-$(CONFIG_BLK_DEV_INTEGRITY) += bio-integrity.o
+obj-$(CONFIG_BLK_DEV_INTEGRITY) += bio-integrity.o blk-integrity.o t10-pi.o
+
diff --git a/block/t10-pi.c b/block/t10-pi.c
new file mode 100644 (file)
index 0000000..24d6e97
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * t10_pi.c - Functions for generating and verifying T10 Protection
+ *           Information.
+ *
+ * Copyright (C) 2007, 2008, 2014 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/t10-pi.h>
+#include <linux/blkdev.h>
+#include <linux/crc-t10dif.h>
+#include <net/checksum.h>
+
+typedef __be16 (csum_fn) (void *, unsigned int);
+
+static const __be16 APP_ESCAPE = (__force __be16) 0xffff;
+static const __be32 REF_ESCAPE = (__force __be32) 0xffffffff;
+
+static __be16 t10_pi_crc_fn(void *data, unsigned int len)
+{
+       return cpu_to_be16(crc_t10dif(data, len));
+}
+
+static __be16 t10_pi_ip_fn(void *data, unsigned int len)
+{
+       return (__force __be16)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. Type 3 does not define the ref
+ * tag.
+ */
+static int t10_pi_generate(struct blk_integrity_iter *iter, csum_fn *fn,
+                          unsigned int type)
+{
+       unsigned int i;
+
+       for (i = 0 ; i < iter->data_size ; i += iter->interval) {
+               struct t10_pi_tuple *pi = iter->prot_buf;
+
+               pi->guard_tag = fn(iter->data_buf, iter->interval);
+               pi->app_tag = 0;
+
+               if (type == 1)
+                       pi->ref_tag = cpu_to_be32(lower_32_bits(iter->seed));
+               else
+                       pi->ref_tag = 0;
+
+               iter->data_buf += iter->interval;
+               iter->prot_buf += sizeof(struct t10_pi_tuple);
+               iter->seed++;
+       }
+
+       return 0;
+}
+
+static int t10_pi_verify(struct blk_integrity_iter *iter, csum_fn *fn,
+                               unsigned int type)
+{
+       unsigned int i;
+
+       for (i = 0 ; i < iter->data_size ; i += iter->interval) {
+               struct t10_pi_tuple *pi = iter->prot_buf;
+               __be16 csum;
+
+               switch (type) {
+               case 1:
+               case 2:
+                       if (pi->app_tag == APP_ESCAPE)
+                               goto next;
+
+                       if (be32_to_cpu(pi->ref_tag) !=
+                           lower_32_bits(iter->seed)) {
+                               pr_err("%s: ref tag error at location %llu " \
+                                      "(rcvd %u)\n", iter->disk_name,
+                                      (unsigned long long)
+                                      iter->seed, be32_to_cpu(pi->ref_tag));
+                               return -EILSEQ;
+                       }
+                       break;
+               case 3:
+                       if (pi->app_tag == APP_ESCAPE &&
+                           pi->ref_tag == REF_ESCAPE)
+                               goto next;
+                       break;
+               }
+
+               csum = fn(iter->data_buf, iter->interval);
+
+               if (pi->guard_tag != csum) {
+                       pr_err("%s: guard tag error at sector %llu " \
+                              "(rcvd %04x, want %04x)\n", iter->disk_name,
+                              (unsigned long long)iter->seed,
+                              be16_to_cpu(pi->guard_tag), be16_to_cpu(csum));
+                       return -EILSEQ;
+               }
+
+next:
+               iter->data_buf += iter->interval;
+               iter->prot_buf += sizeof(struct t10_pi_tuple);
+               iter->seed++;
+       }
+
+       return 0;
+}
+
+static int t10_pi_type1_generate_crc(struct blk_integrity_iter *iter)
+{
+       return t10_pi_generate(iter, t10_pi_crc_fn, 1);
+}
+
+static int t10_pi_type1_generate_ip(struct blk_integrity_iter *iter)
+{
+       return t10_pi_generate(iter, t10_pi_ip_fn, 1);
+}
+
+static int t10_pi_type1_verify_crc(struct blk_integrity_iter *iter)
+{
+       return t10_pi_verify(iter, t10_pi_crc_fn, 1);
+}
+
+static int t10_pi_type1_verify_ip(struct blk_integrity_iter *iter)
+{
+       return t10_pi_verify(iter, t10_pi_ip_fn, 1);
+}
+
+static int t10_pi_type3_generate_crc(struct blk_integrity_iter *iter)
+{
+       return t10_pi_generate(iter, t10_pi_crc_fn, 3);
+}
+
+static int t10_pi_type3_generate_ip(struct blk_integrity_iter *iter)
+{
+       return t10_pi_generate(iter, t10_pi_ip_fn, 3);
+}
+
+static int t10_pi_type3_verify_crc(struct blk_integrity_iter *iter)
+{
+       return t10_pi_verify(iter, t10_pi_crc_fn, 3);
+}
+
+static int t10_pi_type3_verify_ip(struct blk_integrity_iter *iter)
+{
+       return t10_pi_verify(iter, t10_pi_ip_fn, 3);
+}
+
+struct blk_integrity t10_pi_type1_crc = {
+       .name                   = "T10-DIF-TYPE1-CRC",
+       .generate_fn            = t10_pi_type1_generate_crc,
+       .verify_fn              = t10_pi_type1_verify_crc,
+       .tuple_size             = sizeof(struct t10_pi_tuple),
+       .tag_size               = 0,
+};
+EXPORT_SYMBOL(t10_pi_type1_crc);
+
+struct blk_integrity t10_pi_type1_ip = {
+       .name                   = "T10-DIF-TYPE1-IP",
+       .generate_fn            = t10_pi_type1_generate_ip,
+       .verify_fn              = t10_pi_type1_verify_ip,
+       .tuple_size             = sizeof(struct t10_pi_tuple),
+       .tag_size               = 0,
+};
+EXPORT_SYMBOL(t10_pi_type1_ip);
+
+struct blk_integrity t10_pi_type3_crc = {
+       .name                   = "T10-DIF-TYPE3-CRC",
+       .generate_fn            = t10_pi_type3_generate_crc,
+       .verify_fn              = t10_pi_type3_verify_crc,
+       .tuple_size             = sizeof(struct t10_pi_tuple),
+       .tag_size               = 0,
+};
+EXPORT_SYMBOL(t10_pi_type3_crc);
+
+struct blk_integrity t10_pi_type3_ip = {
+       .name                   = "T10-DIF-TYPE3-IP",
+       .generate_fn            = t10_pi_type3_generate_ip,
+       .verify_fn              = t10_pi_type3_verify_ip,
+       .tuple_size             = sizeof(struct t10_pi_tuple),
+       .tag_size               = 0,
+};
+EXPORT_SYMBOL(t10_pi_type3_ip);
index 18a3358eb1d47c9f8cd6acd9e62816691afbff66..9ece13f922a6db33a10c11819aa854a86b94105e 100644 (file)
@@ -62,7 +62,6 @@ comment "SCSI support type (disk, tape, CD-ROM)"
 config BLK_DEV_SD
        tristate "SCSI disk support"
        depends on SCSI
-       select CRC_T10DIF if BLK_DEV_INTEGRITY
        ---help---
          If you want to use SCSI hard disks, Fibre Channel disks,
          Serial ATA (SATA) or Parallel ATA (PATA) hard disks,
index 2198abee619eeaebe939b920c01975220efff7ed..b7eaeadc18f950782bcdc7d3fe87381c97926e96 100644 (file)
@@ -21,7 +21,7 @@
  */
 
 #include <linux/blkdev.h>
-#include <linux/crc-t10dif.h>
+#include <linux/t10-pi.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.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_iter *iter, csum_fn *fn)
-{
-       void *buf = iter->data_buf;
-       struct sd_dif_tuple *sdt = iter->prot_buf;
-       sector_t seed = iter->seed;
-       unsigned int i;
-
-       for (i = 0 ; i < iter->data_size ; i += iter->interval, sdt++) {
-               sdt->guard_tag = fn(buf, iter->interval);
-               sdt->ref_tag = cpu_to_be32(seed & 0xffffffff);
-               sdt->app_tag = 0;
-
-               buf += iter->interval;
-               seed++;
-       }
-}
-
-static int sd_dif_type1_generate_crc(struct blk_integrity_iter *iter)
-{
-       sd_dif_type1_generate(iter, sd_dif_crc_fn);
-       return 0;
-}
-
-static int sd_dif_type1_generate_ip(struct blk_integrity_iter *iter)
-{
-       sd_dif_type1_generate(iter, sd_dif_ip_fn);
-       return 0;
-}
-
-static int sd_dif_type1_verify(struct blk_integrity_iter *iter, csum_fn *fn)
-{
-       void *buf = iter->data_buf;
-       struct sd_dif_tuple *sdt = iter->prot_buf;
-       sector_t seed = iter->seed;
-       unsigned int i;
-       __u16 csum;
-
-       for (i = 0 ; i < iter->data_size ; i += iter->interval, sdt++) {
-               /* Unwritten sectors */
-               if (sdt->app_tag == 0xffff)
-                       return 0;
-
-               if (be32_to_cpu(sdt->ref_tag) != (seed & 0xffffffff)) {
-                       printk(KERN_ERR
-                              "%s: ref tag error on sector %lu (rcvd %u)\n",
-                              iter->disk_name, (unsigned long)seed,
-                              be32_to_cpu(sdt->ref_tag));
-                       return -EIO;
-               }
-
-               csum = fn(buf, iter->interval);
-
-               if (sdt->guard_tag != csum) {
-                       printk(KERN_ERR "%s: guard tag error on sector %lu " \
-                              "(rcvd %04x, data %04x)\n", iter->disk_name,
-                              (unsigned long)seed,
-                              be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum));
-                       return -EIO;
-               }
-
-               buf += iter->interval;
-               seed++;
-       }
-
-       return 0;
-}
-
-static int sd_dif_type1_verify_crc(struct blk_integrity_iter *iter)
-{
-       return sd_dif_type1_verify(iter, sd_dif_crc_fn);
-}
-
-static int sd_dif_type1_verify_ip(struct blk_integrity_iter *iter)
-{
-       return sd_dif_type1_verify(iter, sd_dif_ip_fn);
-}
-
-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,
-       .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,
-       .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_iter *iter, csum_fn *fn)
-{
-       void *buf = iter->data_buf;
-       struct sd_dif_tuple *sdt = iter->prot_buf;
-       unsigned int i;
-
-       for (i = 0 ; i < iter->data_size ; i += iter->interval, sdt++) {
-               sdt->guard_tag = fn(buf, iter->interval);
-               sdt->ref_tag = 0;
-               sdt->app_tag = 0;
-
-               buf += iter->interval;
-       }
-}
-
-static int sd_dif_type3_generate_crc(struct blk_integrity_iter *iter)
-{
-       sd_dif_type3_generate(iter, sd_dif_crc_fn);
-       return 0;
-}
-
-static int sd_dif_type3_generate_ip(struct blk_integrity_iter *iter)
-{
-       sd_dif_type3_generate(iter, sd_dif_ip_fn);
-       return 0;
-}
-
-static int sd_dif_type3_verify(struct blk_integrity_iter *iter, csum_fn *fn)
-{
-       void *buf = iter->data_buf;
-       struct sd_dif_tuple *sdt = iter->prot_buf;
-       sector_t seed = iter->seed;
-       unsigned int i;
-       __u16 csum;
-
-       for (i = 0 ; i < iter->data_size ; i += iter->interval, sdt++) {
-               /* Unwritten sectors */
-               if (sdt->app_tag == 0xffff && sdt->ref_tag == 0xffffffff)
-                       return 0;
-
-               csum = fn(buf, iter->interval);
-
-               if (sdt->guard_tag != csum) {
-                       printk(KERN_ERR "%s: guard tag error on sector %lu " \
-                              "(rcvd %04x, data %04x)\n", iter->disk_name,
-                              (unsigned long)seed,
-                              be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum));
-                       return -EIO;
-               }
-
-               buf += iter->interval;
-               seed++;
-       }
-
-       return 0;
-}
-
-static int sd_dif_type3_verify_crc(struct blk_integrity_iter *iter)
-{
-       return sd_dif_type3_verify(iter, sd_dif_crc_fn);
-}
-
-static int sd_dif_type3_verify_ip(struct blk_integrity_iter *iter)
-{
-       return sd_dif_type3_verify(iter, sd_dif_ip_fn);
-}
-
-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,
-       .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,
-       .tuple_size             = sizeof(struct sd_dif_tuple),
-       .tag_size               = 0,
-};
-
 /*
  * Configure exchange of protection information between OS and HBA.
  */
@@ -257,16 +58,16 @@ void sd_dif_config_host(struct scsi_disk *sdkp)
        /* Enable DMA of protection information */
        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);
+                       blk_integrity_register(disk, &t10_pi_type3_ip);
                else
-                       blk_integrity_register(disk, &dif_type1_integrity_ip);
+                       blk_integrity_register(disk, &t10_pi_type1_ip);
 
                disk->integrity->flags |= BLK_INTEGRITY_IP_CHECKSUM;
        } else
                if (type == SD_DIF_TYPE3_PROTECTION)
-                       blk_integrity_register(disk, &dif_type3_integrity_crc);
+                       blk_integrity_register(disk, &t10_pi_type3_crc);
                else
-                       blk_integrity_register(disk, &dif_type1_integrity_crc);
+                       blk_integrity_register(disk, &t10_pi_type1_crc);
 
        sd_printk(KERN_NOTICE, sdkp,
                  "Enabling DIX %s protection\n", disk->integrity->name);
@@ -308,10 +109,10 @@ void sd_dif_config_host(struct scsi_disk *sdkp)
 void sd_dif_prepare(struct request *rq, sector_t hw_sector,
                    unsigned int sector_sz)
 {
-       const int tuple_sz = sizeof(struct sd_dif_tuple);
+       const int tuple_sz = sizeof(struct t10_pi_tuple);
        struct bio *bio;
        struct scsi_disk *sdkp;
-       struct sd_dif_tuple *sdt;
+       struct t10_pi_tuple *pi;
        u32 phys, virt;
 
        sdkp = rq->bio->bi_bdev->bd_disk->private_data;
@@ -334,19 +135,18 @@ void sd_dif_prepare(struct request *rq, sector_t hw_sector,
                virt = bip_get_seed(bip) & 0xffffffff;
 
                bip_for_each_vec(iv, bip, iter) {
-                       sdt = kmap_atomic(iv.bv_page)
-                               + iv.bv_offset;
+                       pi = kmap_atomic(iv.bv_page) + iv.bv_offset;
 
-                       for (j = 0; j < iv.bv_len; j += tuple_sz, sdt++) {
+                       for (j = 0; j < iv.bv_len; j += tuple_sz, pi++) {
 
-                               if (be32_to_cpu(sdt->ref_tag) == virt)
-                                       sdt->ref_tag = cpu_to_be32(phys);
+                               if (be32_to_cpu(pi->ref_tag) == virt)
+                                       pi->ref_tag = cpu_to_be32(phys);
 
                                virt++;
                                phys++;
                        }
 
-                       kunmap_atomic(sdt);
+                       kunmap_atomic(pi);
                }
 
                bip->bip_flags |= BIP_MAPPED_INTEGRITY;
@@ -359,10 +159,10 @@ void sd_dif_prepare(struct request *rq, sector_t hw_sector,
  */
 void sd_dif_complete(struct scsi_cmnd *scmd, unsigned int good_bytes)
 {
-       const int tuple_sz = sizeof(struct sd_dif_tuple);
+       const int tuple_sz = sizeof(struct t10_pi_tuple);
        struct scsi_disk *sdkp;
        struct bio *bio;
-       struct sd_dif_tuple *sdt;
+       struct t10_pi_tuple *pi;
        unsigned int j, sectors, sector_sz;
        u32 phys, virt;
 
@@ -386,25 +186,24 @@ void sd_dif_complete(struct scsi_cmnd *scmd, unsigned int good_bytes)
                virt = bip_get_seed(bip) & 0xffffffff;
 
                bip_for_each_vec(iv, bip, iter) {
-                       sdt = kmap_atomic(iv.bv_page)
-                               + iv.bv_offset;
+                       pi = kmap_atomic(iv.bv_page) + iv.bv_offset;
 
-                       for (j = 0; j < iv.bv_len; j += tuple_sz, sdt++) {
+                       for (j = 0; j < iv.bv_len; j += tuple_sz, pi++) {
 
                                if (sectors == 0) {
-                                       kunmap_atomic(sdt);
+                                       kunmap_atomic(pi);
                                        return;
                                }
 
-                               if (be32_to_cpu(sdt->ref_tag) == phys)
-                                       sdt->ref_tag = cpu_to_be32(virt);
+                               if (be32_to_cpu(pi->ref_tag) == phys)
+                                       pi->ref_tag = cpu_to_be32(virt);
 
                                virt++;
                                phys++;
                                sectors--;
                        }
 
-                       kunmap_atomic(sdt);
+                       kunmap_atomic(pi);
                }
        }
 }
index b3cb71f0d3b0d19b8c08e70348dee1bb5e5ba6ad..cf53d0773ce3a24623354fd16dee2b3b17597a9b 100644 (file)
@@ -6,7 +6,8 @@
 #define CRC_T10DIF_DIGEST_SIZE 2
 #define CRC_T10DIF_BLOCK_SIZE 1
 
-__u16 crc_t10dif_generic(__u16 crc, const unsigned char *buffer, size_t len);
-__u16 crc_t10dif(unsigned char const *, size_t);
+extern __u16 crc_t10dif_generic(__u16 crc, const unsigned char *buffer,
+                               size_t len);
+extern __u16 crc_t10dif(unsigned char const *, size_t);
 
 #endif
diff --git a/include/linux/t10-pi.h b/include/linux/t10-pi.h
new file mode 100644 (file)
index 0000000..6a8b994
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef _LINUX_T10_PI_H
+#define _LINUX_T10_PI_H
+
+#include <linux/types.h>
+#include <linux/blkdev.h>
+
+/*
+ * T10 Protection Information tuple.
+ */
+struct t10_pi_tuple {
+       __be16 guard_tag;       /* Checksum */
+       __be16 app_tag;         /* Opaque storage */
+       __be32 ref_tag;         /* Target LBA or indirect LBA */
+};
+
+
+extern struct blk_integrity t10_pi_type1_crc;
+extern struct blk_integrity t10_pi_type1_ip;
+extern struct blk_integrity t10_pi_type3_crc;
+extern struct blk_integrity t10_pi_type3_ip;
+
+#endif