diff options
| -rw-r--r-- | drivers/target/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/target/target_core_sbc.c | 178 | ||||
| -rw-r--r-- | include/target/target_core_backend.h | 4 |
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 | } |
| 1026 | EXPORT_SYMBOL(sbc_execute_unmap); | 1027 | EXPORT_SYMBOL(sbc_execute_unmap); |
| 1028 | |||
| 1029 | static sense_reason_t | ||
| 1030 | sbc_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 | |||
| 1064 | static void | ||
| 1065 | sbc_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 | |||
| 1092 | sense_reason_t | ||
| 1093 | sbc_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 | } | ||
| 1144 | EXPORT_SYMBOL(sbc_dif_verify_write); | ||
| 1145 | |||
| 1146 | sense_reason_t | ||
| 1147 | sbc_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 | } | ||
| 1204 | EXPORT_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); |
| 76 | sense_reason_t sbc_dif_verify_write(struct se_cmd *, sector_t, unsigned int, | ||
| 77 | unsigned int, struct scatterlist *, int); | ||
| 78 | sense_reason_t sbc_dif_verify_read(struct se_cmd *, sector_t, unsigned int, | ||
| 79 | unsigned int, struct scatterlist *, int); | ||
| 76 | 80 | ||
| 77 | void transport_set_vpd_proto_id(struct t10_vpd *, unsigned char *); | 81 | void transport_set_vpd_proto_id(struct t10_vpd *, unsigned char *); |
| 78 | int transport_set_vpd_assoc(struct t10_vpd *, unsigned char *); | 82 | int transport_set_vpd_assoc(struct t10_vpd *, unsigned char *); |
