aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/target/Kconfig1
-rw-r--r--drivers/target/target_core_sbc.c178
-rw-r--r--include/target/target_core_backend.h4
3 files changed, 183 insertions, 0 deletions
diff --git a/drivers/target/Kconfig b/drivers/target/Kconfig
index 18303686eb58..50aad2eeed6e 100644
--- a/drivers/target/Kconfig
+++ b/drivers/target/Kconfig
@@ -3,6 +3,7 @@ menuconfig TARGET_CORE
3 tristate "Generic Target Core Mod (TCM) and ConfigFS Infrastructure" 3 tristate "Generic Target Core Mod (TCM) and ConfigFS Infrastructure"
4 depends on SCSI && BLOCK 4 depends on SCSI && BLOCK
5 select CONFIGFS_FS 5 select CONFIGFS_FS
6 select CRC_T10DIF
6 default n 7 default n
7 help 8 help
8 Say Y or M here to enable the TCM Storage Engine and ConfigFS enabled 9 Say Y or M here to enable the TCM Storage Engine and ConfigFS enabled
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index 91a92f354e9f..26e8bfb27787 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -23,6 +23,7 @@
23#include <linux/kernel.h> 23#include <linux/kernel.h>
24#include <linux/module.h> 24#include <linux/module.h>
25#include <linux/ratelimit.h> 25#include <linux/ratelimit.h>
26#include <linux/crc-t10dif.h>
26#include <asm/unaligned.h> 27#include <asm/unaligned.h>
27#include <scsi/scsi.h> 28#include <scsi/scsi.h>
28#include <scsi/scsi_tcq.h> 29#include <scsi/scsi_tcq.h>
@@ -1024,3 +1025,180 @@ err:
1024 return ret; 1025 return ret;
1025} 1026}
1026EXPORT_SYMBOL(sbc_execute_unmap); 1027EXPORT_SYMBOL(sbc_execute_unmap);
1028
1029static sense_reason_t
1030sbc_dif_v1_verify(struct se_device *dev, struct se_dif_v1_tuple *sdt,
1031 const void *p, sector_t sector, unsigned int ei_lba)
1032{
1033 int block_size = dev->dev_attrib.block_size;
1034 __be16 csum;
1035
1036 csum = cpu_to_be16(crc_t10dif(p, block_size));
1037
1038 if (sdt->guard_tag != csum) {
1039 pr_err("DIFv1 checksum failed on sector %llu guard tag 0x%04x"
1040 " csum 0x%04x\n", (unsigned long long)sector,
1041 be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum));
1042 return TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED;
1043 }
1044
1045 if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE1_PROT &&
1046 be32_to_cpu(sdt->ref_tag) != (sector & 0xffffffff)) {
1047 pr_err("DIFv1 Type 1 reference failed on sector: %llu tag: 0x%08x"
1048 " sector MSB: 0x%08x\n", (unsigned long long)sector,
1049 be32_to_cpu(sdt->ref_tag), (u32)(sector & 0xffffffff));
1050 return TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED;
1051 }
1052
1053 if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE2_PROT &&
1054 be32_to_cpu(sdt->ref_tag) != ei_lba) {
1055 pr_err("DIFv1 Type 2 reference failed on sector: %llu tag: 0x%08x"
1056 " ei_lba: 0x%08x\n", (unsigned long long)sector,
1057 be32_to_cpu(sdt->ref_tag), ei_lba);
1058 return TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED;
1059 }
1060
1061 return 0;
1062}
1063
1064static void
1065sbc_dif_copy_prot(struct se_cmd *cmd, unsigned int sectors, bool read,
1066 struct scatterlist *sg, int sg_off)
1067{
1068 struct se_device *dev = cmd->se_dev;
1069 struct scatterlist *psg;
1070 void *paddr, *addr;
1071 unsigned int i, len, left;
1072
1073 left = sectors * dev->prot_length;
1074
1075 for_each_sg(cmd->t_prot_sg, psg, cmd->t_prot_nents, i) {
1076
1077 len = min(psg->length, left);
1078 paddr = kmap_atomic(sg_page(psg)) + psg->offset;
1079 addr = kmap_atomic(sg_page(sg)) + sg_off;
1080
1081 if (read)
1082 memcpy(paddr, addr, len);
1083 else
1084 memcpy(addr, paddr, len);
1085
1086 left -= len;
1087 kunmap_atomic(paddr);
1088 kunmap_atomic(addr);
1089 }
1090}
1091
1092sense_reason_t
1093sbc_dif_verify_write(struct se_cmd *cmd, sector_t start, unsigned int sectors,
1094 unsigned int ei_lba, struct scatterlist *sg, int sg_off)
1095{
1096 struct se_device *dev = cmd->se_dev;
1097 struct se_dif_v1_tuple *sdt;
1098 struct scatterlist *dsg, *psg = cmd->t_prot_sg;
1099 sector_t sector = start;
1100 void *daddr, *paddr;
1101 int i, j, offset = 0;
1102 sense_reason_t rc;
1103
1104 for_each_sg(cmd->t_data_sg, dsg, cmd->t_data_nents, i) {
1105 daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
1106 paddr = kmap_atomic(sg_page(psg)) + psg->offset;
1107
1108 for (j = 0; j < dsg->length; j += dev->dev_attrib.block_size) {
1109
1110 if (offset >= psg->length) {
1111 kunmap_atomic(paddr);
1112 psg = sg_next(psg);
1113 paddr = kmap_atomic(sg_page(psg)) + psg->offset;
1114 offset = 0;
1115 }
1116
1117 sdt = paddr + offset;
1118
1119 pr_debug("DIF WRITE sector: %llu guard_tag: 0x%04x"
1120 " app_tag: 0x%04x ref_tag: %u\n",
1121 (unsigned long long)sector, sdt->guard_tag,
1122 sdt->app_tag, be32_to_cpu(sdt->ref_tag));
1123
1124 rc = sbc_dif_v1_verify(dev, sdt, daddr + j, sector,
1125 ei_lba);
1126 if (rc) {
1127 kunmap_atomic(paddr);
1128 kunmap_atomic(daddr);
1129 return rc;
1130 }
1131
1132 sector++;
1133 ei_lba++;
1134 offset += sizeof(struct se_dif_v1_tuple);
1135 }
1136
1137 kunmap_atomic(paddr);
1138 kunmap_atomic(daddr);
1139 }
1140 sbc_dif_copy_prot(cmd, sectors, false, sg, sg_off);
1141
1142 return 0;
1143}
1144EXPORT_SYMBOL(sbc_dif_verify_write);
1145
1146sense_reason_t
1147sbc_dif_verify_read(struct se_cmd *cmd, sector_t start, unsigned int sectors,
1148 unsigned int ei_lba, struct scatterlist *sg, int sg_off)
1149{
1150 struct se_device *dev = cmd->se_dev;
1151 struct se_dif_v1_tuple *sdt;
1152 struct scatterlist *dsg;
1153 sector_t sector = start;
1154 void *daddr, *paddr;
1155 int i, j, offset = sg_off;
1156 sense_reason_t rc;
1157
1158 for_each_sg(cmd->t_data_sg, dsg, cmd->t_data_nents, i) {
1159 daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
1160 paddr = kmap_atomic(sg_page(sg)) + sg->offset;
1161
1162 for (j = 0; j < dsg->length; j += dev->dev_attrib.block_size) {
1163
1164 if (offset >= sg->length) {
1165 kunmap_atomic(paddr);
1166 sg = sg_next(sg);
1167 paddr = kmap_atomic(sg_page(sg)) + sg->offset;
1168 offset = 0;
1169 }
1170
1171 sdt = paddr + offset;
1172
1173 pr_debug("DIF READ sector: %llu guard_tag: 0x%04x"
1174 " app_tag: 0x%04x ref_tag: %u\n",
1175 (unsigned long long)sector, sdt->guard_tag,
1176 sdt->app_tag, be32_to_cpu(sdt->ref_tag));
1177
1178 if (sdt->app_tag == cpu_to_be16(0xffff)) {
1179 sector++;
1180 offset += sizeof(struct se_dif_v1_tuple);
1181 continue;
1182 }
1183
1184 rc = sbc_dif_v1_verify(dev, sdt, daddr + j, sector,
1185 ei_lba);
1186 if (rc) {
1187 kunmap_atomic(paddr);
1188 kunmap_atomic(daddr);
1189 return rc;
1190 }
1191
1192 sector++;
1193 ei_lba++;
1194 offset += sizeof(struct se_dif_v1_tuple);
1195 }
1196
1197 kunmap_atomic(paddr);
1198 kunmap_atomic(daddr);
1199 }
1200 sbc_dif_copy_prot(cmd, sectors, true, sg, sg_off);
1201
1202 return 0;
1203}
1204EXPORT_SYMBOL(sbc_dif_verify_read);
diff --git a/include/target/target_core_backend.h b/include/target/target_core_backend.h
index 0dc2745b9b18..7020e33e742e 100644
--- a/include/target/target_core_backend.h
+++ b/include/target/target_core_backend.h
@@ -73,6 +73,10 @@ sense_reason_t sbc_execute_unmap(struct se_cmd *cmd,
73 sense_reason_t (*do_unmap_fn)(struct se_cmd *cmd, void *priv, 73 sense_reason_t (*do_unmap_fn)(struct se_cmd *cmd, void *priv,
74 sector_t lba, sector_t nolb), 74 sector_t lba, sector_t nolb),
75 void *priv); 75 void *priv);
76sense_reason_t sbc_dif_verify_write(struct se_cmd *, sector_t, unsigned int,
77 unsigned int, struct scatterlist *, int);
78sense_reason_t sbc_dif_verify_read(struct se_cmd *, sector_t, unsigned int,
79 unsigned int, struct scatterlist *, int);
76 80
77void transport_set_vpd_proto_id(struct t10_vpd *, unsigned char *); 81void transport_set_vpd_proto_id(struct t10_vpd *, unsigned char *);
78int transport_set_vpd_assoc(struct t10_vpd *, unsigned char *); 82int transport_set_vpd_assoc(struct t10_vpd *, unsigned char *);