aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSagi Grimberg <sagig@mellanox.com>2014-03-05 07:05:09 -0500
committerNicholas Bellinger <nab@linux-iscsi.org>2014-03-06 23:52:11 -0500
commit16c0ae028989df40bdab46b7f241d331ddc97c59 (patch)
tree425a754b9d8ee86b957760ddc1c9e781950bb731
parentebbe442183b7b8192c963266f1c89048fefc63a5 (diff)
Target/sbc: Fix sbc_copy_prot for offset scatters
When copying between device and command protection scatters we must take into account that device scatters might be offset and we might copy outside scatter range. Thus for each cmd prot scatter we must take the min between cmd prot scatter, dev prot scatter, and whats left (and loop in case we havn't copied enough from/to cmd prot scatter). Example (single t_prot_sg of len 2048): kernel: sbc_dif_copy_prot: se_cmd=ffff880380aaf970, left=2048, len=2048, dev_prot_sg_offset=3072, dev_prot_sg_len=4096 kernel: isert: se_cmd=ffff880380aaf970 PI error found type 0 at sector 0x2600 expected 0x0 vs actual 0x725f, lba=2580 Instead of copying 2048 from offset 3072 (copying junk outside sg limit 4096), we must to copy 1024 and continue to next sg until we complete cmd prot scatter. This issue was found using iSER T10-PI offload over rd_mcp (wasn't discovered with fileio since file_dev prot sglists are never offset). Changes from v1: - Fix sbc_copy_prot copy length miss-calculation Changes from v0: - Removed psg->offset consideration for psg_len computation - Removed sg->offset consideration for offset condition - Added copied consideraiton for len computation - Added copied offset to paddr when doing memcpy Signed-off-by: Sagi Grimberg <sagig@mellanox.com> Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
-rw-r--r--drivers/target/target_core_sbc.c38
1 files changed, 22 insertions, 16 deletions
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index 42f18fc1067b..77e6531fb0a1 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -1079,25 +1079,31 @@ sbc_dif_copy_prot(struct se_cmd *cmd, unsigned int sectors, bool read,
1079 left = sectors * dev->prot_length; 1079 left = sectors * dev->prot_length;
1080 1080
1081 for_each_sg(cmd->t_prot_sg, psg, cmd->t_prot_nents, i) { 1081 for_each_sg(cmd->t_prot_sg, psg, cmd->t_prot_nents, i) {
1082 1082 unsigned int psg_len, copied = 0;
1083 len = min(psg->length, left);
1084 if (offset >= sg->length) {
1085 sg = sg_next(sg);
1086 offset = 0;
1087 }
1088 1083
1089 paddr = kmap_atomic(sg_page(psg)) + psg->offset; 1084 paddr = kmap_atomic(sg_page(psg)) + psg->offset;
1090 addr = kmap_atomic(sg_page(sg)) + sg->offset + offset; 1085 psg_len = min(left, psg->length);
1091 1086 while (psg_len) {
1092 if (read) 1087 len = min(psg_len, sg->length - offset);
1093 memcpy(paddr, addr, len); 1088 addr = kmap_atomic(sg_page(sg)) + sg->offset + offset;
1094 else 1089
1095 memcpy(addr, paddr, len); 1090 if (read)
1096 1091 memcpy(paddr + copied, addr, len);
1097 left -= len; 1092 else
1098 offset += len; 1093 memcpy(addr, paddr + copied, len);
1094
1095 left -= len;
1096 offset += len;
1097 copied += len;
1098 psg_len -= len;
1099
1100 if (offset >= sg->length) {
1101 sg = sg_next(sg);
1102 offset = 0;
1103 }
1104 kunmap_atomic(addr);
1105 }
1099 kunmap_atomic(paddr); 1106 kunmap_atomic(paddr);
1100 kunmap_atomic(addr);
1101 } 1107 }
1102} 1108}
1103 1109