These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / target / target_core_sbc.c
index 733824e..98698d8 100644 (file)
@@ -25,7 +25,7 @@
 #include <linux/ratelimit.h>
 #include <linux/crc-t10dif.h>
 #include <asm/unaligned.h>
-#include <scsi/scsi.h>
+#include <scsi/scsi_proto.h>
 #include <scsi/scsi_tcq.h>
 
 #include <target/target_core_base.h>
@@ -38,6 +38,7 @@
 
 static sense_reason_t
 sbc_check_prot(struct se_device *, struct se_cmd *, unsigned char *, u32, bool);
+static sense_reason_t sbc_execute_unmap(struct se_cmd *cmd);
 
 static sense_reason_t
 sbc_emulate_readcapacity(struct se_cmd *cmd)
@@ -153,6 +154,38 @@ sbc_emulate_readcapacity_16(struct se_cmd *cmd)
        return 0;
 }
 
+static sense_reason_t
+sbc_emulate_startstop(struct se_cmd *cmd)
+{
+       unsigned char *cdb = cmd->t_task_cdb;
+
+       /*
+        * See sbc3r36 section 5.25
+        * Immediate bit should be set since there is nothing to complete
+        * POWER CONDITION MODIFIER 0h
+        */
+       if (!(cdb[1] & 1) || cdb[2] || cdb[3])
+               return TCM_INVALID_CDB_FIELD;
+
+       /*
+        * See sbc3r36 section 5.25
+        * POWER CONDITION 0h START_VALID - process START and LOEJ
+        */
+       if (cdb[4] >> 4 & 0xf)
+               return TCM_INVALID_CDB_FIELD;
+
+       /*
+        * See sbc3r36 section 5.25
+        * LOEJ 0h - nothing to load or unload
+        * START 1h - we are ready
+        */
+       if (!(cdb[4] & 1) || (cdb[4] & 2) || (cdb[4] & 4))
+               return TCM_INVALID_CDB_FIELD;
+
+       target_complete_cmd(cmd, SAM_STAT_GOOD);
+       return 0;
+}
+
 sector_t sbc_get_write_same_sectors(struct se_cmd *cmd)
 {
        u32 num_blocks;
@@ -176,6 +209,23 @@ sector_t sbc_get_write_same_sectors(struct se_cmd *cmd)
 }
 EXPORT_SYMBOL(sbc_get_write_same_sectors);
 
+static sense_reason_t
+sbc_execute_write_same_unmap(struct se_cmd *cmd)
+{
+       struct sbc_ops *ops = cmd->protocol_data;
+       sector_t nolb = sbc_get_write_same_sectors(cmd);
+       sense_reason_t ret;
+
+       if (nolb) {
+               ret = ops->execute_unmap(cmd, cmd->t_task_lba, nolb);
+               if (ret)
+                       return ret;
+       }
+
+       target_complete_cmd(cmd, GOOD);
+       return 0;
+}
+
 static sense_reason_t
 sbc_emulate_noop(struct se_cmd *cmd)
 {
@@ -299,7 +349,7 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *o
         * translated into block discard requests within backend code.
         */
        if (flags[0] & 0x08) {
-               if (!ops->execute_write_same_unmap)
+               if (!ops->execute_unmap)
                        return TCM_UNSUPPORTED_SCSI_OPCODE;
 
                if (!dev->dev_attrib.emulate_tpws) {
@@ -307,7 +357,7 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *o
                               " has emulate_tpws disabled\n");
                        return TCM_UNSUPPORTED_SCSI_OPCODE;
                }
-               cmd->execute_cmd = ops->execute_write_same_unmap;
+               cmd->execute_cmd = sbc_execute_write_same_unmap;
                return 0;
        }
        if (!ops->execute_write_same)
@@ -321,7 +371,8 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *o
        return 0;
 }
 
-static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd, bool success)
+static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd, bool success,
+                                          int *post_ret)
 {
        unsigned char *buf, *addr;
        struct scatterlist *sg;
@@ -381,11 +432,14 @@ out:
 static sense_reason_t
 sbc_execute_rw(struct se_cmd *cmd)
 {
-       return cmd->execute_rw(cmd, cmd->t_data_sg, cmd->t_data_nents,
+       struct sbc_ops *ops = cmd->protocol_data;
+
+       return ops->execute_rw(cmd, cmd->t_data_sg, cmd->t_data_nents,
                               cmd->data_direction);
 }
 
-static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success)
+static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
+                                            int *post_ret)
 {
        struct se_device *dev = cmd->se_dev;
 
@@ -395,8 +449,10 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success)
         * sent to the backend driver.
         */
        spin_lock_irq(&cmd->t_state_lock);
-       if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status)
+       if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status) {
                cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST;
+               *post_ret = 1;
+       }
        spin_unlock_irq(&cmd->t_state_lock);
 
        /*
@@ -408,7 +464,8 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success)
        return TCM_NO_SENSE;
 }
 
-static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success)
+static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success,
+                                                int *post_ret)
 {
        struct se_device *dev = cmd->se_dev;
        struct scatterlist *write_sg = NULL, *sg;
@@ -504,11 +561,11 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes
 
                if (block_size < PAGE_SIZE) {
                        sg_set_page(&write_sg[i], m.page, block_size,
-                                   block_size);
+                                   m.piter.sg->offset + block_size);
                } else {
                        sg_miter_next(&m);
                        sg_set_page(&write_sg[i], m.page, block_size,
-                                   0);
+                                   m.piter.sg->offset);
                }
                len -= block_size;
                i++;
@@ -560,6 +617,7 @@ out:
 static sense_reason_t
 sbc_compare_and_write(struct se_cmd *cmd)
 {
+       struct sbc_ops *ops = cmd->protocol_data;
        struct se_device *dev = cmd->se_dev;
        sense_reason_t ret;
        int rc;
@@ -579,7 +637,7 @@ sbc_compare_and_write(struct se_cmd *cmd)
         */
        cmd->data_length = cmd->t_task_nolb * dev->dev_attrib.block_size;
 
-       ret = cmd->execute_rw(cmd, cmd->t_bidi_data_sg, cmd->t_bidi_data_nents,
+       ret = ops->execute_rw(cmd, cmd->t_bidi_data_sg, cmd->t_bidi_data_nents,
                              DMA_FROM_DEVICE);
        if (ret) {
                cmd->transport_complete_callback = NULL;
@@ -738,14 +796,15 @@ static int
 sbc_check_dpofua(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb)
 {
        if (cdb[1] & 0x10) {
-               if (!dev->dev_attrib.emulate_dpo) {
+               /* see explanation in spc_emulate_modesense */
+               if (!target_check_fua(dev)) {
                        pr_err("Got CDB: 0x%02x with DPO bit set, but device"
                               " does not advertise support for DPO\n", cdb[0]);
                        return -EINVAL;
                }
        }
        if (cdb[1] & 0x8) {
-               if (!dev->dev_attrib.emulate_fua_write || !se_dev_check_wce(dev)) {
+               if (!target_check_fua(dev)) {
                        pr_err("Got CDB: 0x%02x with FUA bit set, but device"
                               " does not advertise support for FUA write\n",
                               cdb[0]);
@@ -765,12 +824,13 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
        u32 sectors = 0;
        sense_reason_t ret;
 
+       cmd->protocol_data = ops;
+
        switch (cdb[0]) {
        case READ_6:
                sectors = transport_get_sectors_6(cdb);
                cmd->t_task_lba = transport_lba_21(cdb);
                cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
-               cmd->execute_rw = ops->execute_rw;
                cmd->execute_cmd = sbc_execute_rw;
                break;
        case READ_10:
@@ -785,7 +845,6 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                        return ret;
 
                cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
-               cmd->execute_rw = ops->execute_rw;
                cmd->execute_cmd = sbc_execute_rw;
                break;
        case READ_12:
@@ -800,7 +859,6 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                        return ret;
 
                cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
-               cmd->execute_rw = ops->execute_rw;
                cmd->execute_cmd = sbc_execute_rw;
                break;
        case READ_16:
@@ -815,14 +873,12 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                        return ret;
 
                cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
-               cmd->execute_rw = ops->execute_rw;
                cmd->execute_cmd = sbc_execute_rw;
                break;
        case WRITE_6:
                sectors = transport_get_sectors_6(cdb);
                cmd->t_task_lba = transport_lba_21(cdb);
                cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
-               cmd->execute_rw = ops->execute_rw;
                cmd->execute_cmd = sbc_execute_rw;
                break;
        case WRITE_10:
@@ -838,7 +894,6 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                        return ret;
 
                cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
-               cmd->execute_rw = ops->execute_rw;
                cmd->execute_cmd = sbc_execute_rw;
                break;
        case WRITE_12:
@@ -853,7 +908,6 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                        return ret;
 
                cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
-               cmd->execute_rw = ops->execute_rw;
                cmd->execute_cmd = sbc_execute_rw;
                break;
        case WRITE_16:
@@ -868,7 +922,6 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                        return ret;
 
                cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
-               cmd->execute_rw = ops->execute_rw;
                cmd->execute_cmd = sbc_execute_rw;
                break;
        case XDWRITEREAD_10:
@@ -886,7 +939,6 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                /*
                 * Setup BIDI XOR callback to be run after I/O completion.
                 */
-               cmd->execute_rw = ops->execute_rw;
                cmd->execute_cmd = sbc_execute_rw;
                cmd->transport_complete_callback = &xdreadwrite_callback;
                break;
@@ -910,7 +962,6 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                         * Setup BIDI XOR callback to be run during after I/O
                         * completion.
                         */
-                       cmd->execute_rw = ops->execute_rw;
                        cmd->execute_cmd = sbc_execute_rw;
                        cmd->transport_complete_callback = &xdreadwrite_callback;
                        break;
@@ -946,6 +997,9 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                               " than 1\n", sectors);
                        return TCM_INVALID_CDB_FIELD;
                }
+               if (sbc_check_dpofua(dev, cmd, cdb))
+                       return TCM_INVALID_CDB_FIELD;
+
                /*
                 * Double size because we have two buffers, note that
                 * zero is not an error..
@@ -954,7 +1008,6 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                cmd->t_task_lba = get_unaligned_be64(&cdb[2]);
                cmd->t_task_nolb = sectors;
                cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB | SCF_COMPARE_AND_WRITE;
-               cmd->execute_rw = ops->execute_rw;
                cmd->execute_cmd = sbc_compare_and_write;
                cmd->transport_complete_callback = compare_and_write_callback;
                break;
@@ -1004,7 +1057,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                        return TCM_UNSUPPORTED_SCSI_OPCODE;
                }
                size = get_unaligned_be16(&cdb[7]);
-               cmd->execute_cmd = ops->execute_unmap;
+               cmd->execute_cmd = sbc_execute_unmap;
                break;
        case WRITE_SAME_16:
                sectors = transport_get_sectors_16(cdb);
@@ -1056,6 +1109,10 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                size = 0;
                cmd->execute_cmd = sbc_emulate_noop;
                break;
+       case START_STOP:
+               size = 0;
+               cmd->execute_cmd = sbc_emulate_startstop;
+               break;
        default:
                ret = spc_parse_cdb(cmd, &size);
                if (ret)
@@ -1092,12 +1149,10 @@ u32 sbc_get_device_type(struct se_device *dev)
 }
 EXPORT_SYMBOL(sbc_get_device_type);
 
-sense_reason_t
-sbc_execute_unmap(struct se_cmd *cmd,
-       sense_reason_t (*do_unmap_fn)(struct se_cmd *, void *,
-                                     sector_t, sector_t),
-       void *priv)
+static sense_reason_t
+sbc_execute_unmap(struct se_cmd *cmd)
 {
+       struct sbc_ops *ops = cmd->protocol_data;
        struct se_device *dev = cmd->se_dev;
        unsigned char *buf, *ptr = NULL;
        sector_t lba;
@@ -1161,7 +1216,7 @@ sbc_execute_unmap(struct se_cmd *cmd,
                        goto err;
                }
 
-               ret = do_unmap_fn(cmd, priv, lba, range);
+               ret = ops->execute_unmap(cmd, lba, range);
                if (ret)
                        goto err;
 
@@ -1175,34 +1230,56 @@ err:
                target_complete_cmd(cmd, GOOD);
        return ret;
 }
-EXPORT_SYMBOL(sbc_execute_unmap);
 
 void
 sbc_dif_generate(struct se_cmd *cmd)
 {
        struct se_device *dev = cmd->se_dev;
-       struct se_dif_v1_tuple *sdt;
-       struct scatterlist *dsg, *psg = cmd->t_prot_sg;
+       struct t10_pi_tuple *sdt;
+       struct scatterlist *dsg = cmd->t_data_sg, *psg;
        sector_t sector = cmd->t_task_lba;
        void *daddr, *paddr;
        int i, j, offset = 0;
+       unsigned int block_size = dev->dev_attrib.block_size;
 
-       for_each_sg(cmd->t_data_sg, dsg, cmd->t_data_nents, i) {
-               daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
+       for_each_sg(cmd->t_prot_sg, psg, cmd->t_prot_nents, i) {
                paddr = kmap_atomic(sg_page(psg)) + psg->offset;
+               daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
 
-               for (j = 0; j < dsg->length; j += dev->dev_attrib.block_size) {
+               for (j = 0; j < psg->length;
+                               j += sizeof(*sdt)) {
+                       __u16 crc;
+                       unsigned int avail;
+
+                       if (offset >= dsg->length) {
+                               offset -= dsg->length;
+                               kunmap_atomic(daddr - dsg->offset);
+                               dsg = sg_next(dsg);
+                               if (!dsg) {
+                                       kunmap_atomic(paddr - psg->offset);
+                                       return;
+                               }
+                               daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
+                       }
 
-                       if (offset >= psg->length) {
-                               kunmap_atomic(paddr);
-                               psg = sg_next(psg);
-                               paddr = kmap_atomic(sg_page(psg)) + psg->offset;
-                               offset = 0;
+                       sdt = paddr + j;
+                       avail = min(block_size, dsg->length - offset);
+                       crc = crc_t10dif(daddr + offset, avail);
+                       if (avail < block_size) {
+                               kunmap_atomic(daddr - dsg->offset);
+                               dsg = sg_next(dsg);
+                               if (!dsg) {
+                                       kunmap_atomic(paddr - psg->offset);
+                                       return;
+                               }
+                               daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
+                               offset = block_size - avail;
+                               crc = crc_t10dif_update(crc, daddr, offset);
+                       } else {
+                               offset += block_size;
                        }
 
-                       sdt = paddr + offset;
-                       sdt->guard_tag = cpu_to_be16(crc_t10dif(daddr + j,
-                                               dev->dev_attrib.block_size));
+                       sdt->guard_tag = cpu_to_be16(crc);
                        if (cmd->prot_type == TARGET_DIF_TYPE1_PROT)
                                sdt->ref_tag = cpu_to_be32(sector & 0xffffffff);
                        sdt->app_tag = 0;
@@ -1215,26 +1292,23 @@ sbc_dif_generate(struct se_cmd *cmd)
                                 be32_to_cpu(sdt->ref_tag));
 
                        sector++;
-                       offset += sizeof(struct se_dif_v1_tuple);
                }
 
-               kunmap_atomic(paddr);
-               kunmap_atomic(daddr);
+               kunmap_atomic(daddr - dsg->offset);
+               kunmap_atomic(paddr - psg->offset);
        }
 }
 
 static sense_reason_t
-sbc_dif_v1_verify(struct se_cmd *cmd, struct se_dif_v1_tuple *sdt,
-                 const void *p, sector_t sector, unsigned int ei_lba)
+sbc_dif_v1_verify(struct se_cmd *cmd, struct t10_pi_tuple *sdt,
+                 __u16 crc, sector_t sector, unsigned int ei_lba)
 {
-       struct se_device *dev = cmd->se_dev;
-       int block_size = dev->dev_attrib.block_size;
        __be16 csum;
 
        if (!(cmd->prot_checks & TARGET_DIF_CHECK_GUARD))
                goto check_ref;
 
-       csum = cpu_to_be16(crc_t10dif(p, block_size));
+       csum = cpu_to_be16(crc);
 
        if (sdt->guard_tag != csum) {
                pr_err("DIFv1 checksum failed on sector %llu guard tag 0x%04x"
@@ -1266,9 +1340,8 @@ check_ref:
        return 0;
 }
 
-static void
-sbc_dif_copy_prot(struct se_cmd *cmd, unsigned int sectors, bool read,
-                 struct scatterlist *sg, int sg_off)
+void sbc_dif_copy_prot(struct se_cmd *cmd, unsigned int sectors, bool read,
+                      struct scatterlist *sg, int sg_off)
 {
        struct se_device *dev = cmd->se_dev;
        struct scatterlist *psg;
@@ -1300,100 +1373,54 @@ sbc_dif_copy_prot(struct se_cmd *cmd, unsigned int sectors, bool read,
                        copied += len;
                        psg_len -= len;
 
+                       kunmap_atomic(addr - sg->offset - offset);
+
                        if (offset >= sg->length) {
                                sg = sg_next(sg);
                                offset = 0;
                        }
-                       kunmap_atomic(addr);
                }
-               kunmap_atomic(paddr);
+               kunmap_atomic(paddr - psg->offset);
        }
 }
+EXPORT_SYMBOL(sbc_dif_copy_prot);
 
 sense_reason_t
-sbc_dif_verify_write(struct se_cmd *cmd, sector_t start, unsigned int sectors,
-                    unsigned int ei_lba, struct scatterlist *sg, int sg_off)
+sbc_dif_verify(struct se_cmd *cmd, sector_t start, unsigned int sectors,
+              unsigned int ei_lba, struct scatterlist *psg, int psg_off)
 {
        struct se_device *dev = cmd->se_dev;
-       struct se_dif_v1_tuple *sdt;
-       struct scatterlist *dsg, *psg = cmd->t_prot_sg;
+       struct t10_pi_tuple *sdt;
+       struct scatterlist *dsg = cmd->t_data_sg;
        sector_t sector = start;
        void *daddr, *paddr;
-       int i, j, offset = 0;
+       int i;
        sense_reason_t rc;
+       int dsg_off = 0;
+       unsigned int block_size = dev->dev_attrib.block_size;
 
-       for_each_sg(cmd->t_data_sg, dsg, cmd->t_data_nents, i) {
-               daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
+       for (; psg && sector < start + sectors; psg = sg_next(psg)) {
                paddr = kmap_atomic(sg_page(psg)) + psg->offset;
-
-               for (j = 0; j < dsg->length; j += dev->dev_attrib.block_size) {
-
-                       if (offset >= psg->length) {
-                               kunmap_atomic(paddr);
-                               psg = sg_next(psg);
-                               paddr = kmap_atomic(sg_page(psg)) + psg->offset;
-                               offset = 0;
-                       }
-
-                       sdt = paddr + offset;
-
-                       pr_debug("DIF WRITE sector: %llu guard_tag: 0x%04x"
-                                " app_tag: 0x%04x ref_tag: %u\n",
-                                (unsigned long long)sector, sdt->guard_tag,
-                                sdt->app_tag, be32_to_cpu(sdt->ref_tag));
-
-                       rc = sbc_dif_v1_verify(cmd, sdt, daddr + j, sector,
-                                              ei_lba);
-                       if (rc) {
-                               kunmap_atomic(paddr);
-                               kunmap_atomic(daddr);
-                               cmd->bad_sector = sector;
-                               return rc;
-                       }
-
-                       sector++;
-                       ei_lba++;
-                       offset += sizeof(struct se_dif_v1_tuple);
-               }
-
-               kunmap_atomic(paddr);
-               kunmap_atomic(daddr);
-       }
-       if (!sg)
-               return 0;
-
-       sbc_dif_copy_prot(cmd, sectors, false, sg, sg_off);
-
-       return 0;
-}
-EXPORT_SYMBOL(sbc_dif_verify_write);
-
-static sense_reason_t
-__sbc_dif_verify_read(struct se_cmd *cmd, sector_t start, unsigned int sectors,
-                     unsigned int ei_lba, struct scatterlist *sg, int sg_off)
-{
-       struct se_device *dev = cmd->se_dev;
-       struct se_dif_v1_tuple *sdt;
-       struct scatterlist *dsg, *psg = sg;
-       sector_t sector = start;
-       void *daddr, *paddr;
-       int i, j, offset = sg_off;
-       sense_reason_t rc;
-
-       for_each_sg(cmd->t_data_sg, dsg, cmd->t_data_nents, i) {
                daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
-               paddr = kmap_atomic(sg_page(psg)) + sg->offset;
 
-               for (j = 0; j < dsg->length; j += dev->dev_attrib.block_size) {
-
-                       if (offset >= psg->length) {
-                               kunmap_atomic(paddr);
-                               psg = sg_next(psg);
-                               paddr = kmap_atomic(sg_page(psg)) + psg->offset;
-                               offset = 0;
+               for (i = psg_off; i < psg->length &&
+                               sector < start + sectors;
+                               i += sizeof(*sdt)) {
+                       __u16 crc;
+                       unsigned int avail;
+
+                       if (dsg_off >= dsg->length) {
+                               dsg_off -= dsg->length;
+                               kunmap_atomic(daddr - dsg->offset);
+                               dsg = sg_next(dsg);
+                               if (!dsg) {
+                                       kunmap_atomic(paddr - psg->offset);
+                                       return 0;
+                               }
+                               daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
                        }
 
-                       sdt = paddr + offset;
+                       sdt = paddr + i;
 
                        pr_debug("DIF READ sector: %llu guard_tag: 0x%04x"
                                 " app_tag: 0x%04x ref_tag: %u\n",
@@ -1401,53 +1428,43 @@ __sbc_dif_verify_read(struct se_cmd *cmd, sector_t start, unsigned int sectors,
                                 sdt->app_tag, be32_to_cpu(sdt->ref_tag));
 
                        if (sdt->app_tag == cpu_to_be16(0xffff)) {
-                               sector++;
-                               offset += sizeof(struct se_dif_v1_tuple);
-                               continue;
+                               dsg_off += block_size;
+                               goto next;
+                       }
+
+                       avail = min(block_size, dsg->length - dsg_off);
+                       crc = crc_t10dif(daddr + dsg_off, avail);
+                       if (avail < block_size) {
+                               kunmap_atomic(daddr - dsg->offset);
+                               dsg = sg_next(dsg);
+                               if (!dsg) {
+                                       kunmap_atomic(paddr - psg->offset);
+                                       return 0;
+                               }
+                               daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
+                               dsg_off = block_size - avail;
+                               crc = crc_t10dif_update(crc, daddr, dsg_off);
+                       } else {
+                               dsg_off += block_size;
                        }
 
-                       rc = sbc_dif_v1_verify(cmd, sdt, daddr + j, sector,
-                                              ei_lba);
+                       rc = sbc_dif_v1_verify(cmd, sdt, crc, sector, ei_lba);
                        if (rc) {
-                               kunmap_atomic(paddr);
-                               kunmap_atomic(daddr);
+                               kunmap_atomic(daddr - dsg->offset);
+                               kunmap_atomic(paddr - psg->offset);
                                cmd->bad_sector = sector;
                                return rc;
                        }
-
+next:
                        sector++;
                        ei_lba++;
-                       offset += sizeof(struct se_dif_v1_tuple);
                }
 
-               kunmap_atomic(paddr);
-               kunmap_atomic(daddr);
+               psg_off = 0;
+               kunmap_atomic(daddr - dsg->offset);
+               kunmap_atomic(paddr - psg->offset);
        }
 
        return 0;
 }
-
-sense_reason_t
-sbc_dif_read_strip(struct se_cmd *cmd)
-{
-       struct se_device *dev = cmd->se_dev;
-       u32 sectors = cmd->prot_length / dev->prot_length;
-
-       return __sbc_dif_verify_read(cmd, cmd->t_task_lba, sectors, 0,
-                                    cmd->t_prot_sg, 0);
-}
-
-sense_reason_t
-sbc_dif_verify_read(struct se_cmd *cmd, sector_t start, unsigned int sectors,
-                   unsigned int ei_lba, struct scatterlist *sg, int sg_off)
-{
-       sense_reason_t rc;
-
-       rc = __sbc_dif_verify_read(cmd, start, sectors, ei_lba, sg, sg_off);
-       if (rc)
-               return rc;
-
-       sbc_dif_copy_prot(cmd, sectors, true, sg, sg_off);
-       return 0;
-}
-EXPORT_SYMBOL(sbc_dif_verify_read);
+EXPORT_SYMBOL(sbc_dif_verify);