Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iscsi-target: Fix sendpage breakage with proper padding+DataDigest iovec offsets

This patch fixes a bug in the iscsit_fe_sendpage_sg() transmit codepath that
was originally introduced with the v3.1 iscsi-target merge that incorrectly
uses hardcoded cmd->iov_data_count values to determine cmd->iov_data[] offsets
for extra outgoing padding and DataDigest payload vectors.

This code is obviously incorrect for the DataDigest enabled case with sendpage
offload, and this fix ensures correct operation for padding + DataDigest,
padding only, and DataDigest only cases. The bug was introduced during a
pre-merge change in iscsit_fe_sendpage_sg() to natively use struct scatterlist
instead of the legacy v3.0 struct se_mem logic.

Cc: Andy Grover <agrover@redhat.com>
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>

+15 -7
+15 -7
drivers/target/iscsi/iscsi_target_util.c
··· 1256 1256 struct kvec iov; 1257 1257 u32 tx_hdr_size, data_len; 1258 1258 u32 offset = cmd->first_data_sg_off; 1259 - int tx_sent; 1259 + int tx_sent, iov_off; 1260 1260 1261 1261 send_hdr: 1262 1262 tx_hdr_size = ISCSI_HDR_LEN; ··· 1276 1276 } 1277 1277 1278 1278 data_len = cmd->tx_size - tx_hdr_size - cmd->padding; 1279 - if (conn->conn_ops->DataDigest) 1279 + /* 1280 + * Set iov_off used by padding and data digest tx_data() calls below 1281 + * in order to determine proper offset into cmd->iov_data[] 1282 + */ 1283 + if (conn->conn_ops->DataDigest) { 1280 1284 data_len -= ISCSI_CRC_LEN; 1281 - 1285 + if (cmd->padding) 1286 + iov_off = (cmd->iov_data_count - 2); 1287 + else 1288 + iov_off = (cmd->iov_data_count - 1); 1289 + } else { 1290 + iov_off = (cmd->iov_data_count - 1); 1291 + } 1282 1292 /* 1283 1293 * Perform sendpage() for each page in the scatterlist 1284 1294 */ ··· 1317 1307 1318 1308 send_padding: 1319 1309 if (cmd->padding) { 1320 - struct kvec *iov_p = 1321 - &cmd->iov_data[cmd->iov_data_count-1]; 1310 + struct kvec *iov_p = &cmd->iov_data[iov_off++]; 1322 1311 1323 1312 tx_sent = tx_data(conn, iov_p, 1, cmd->padding); 1324 1313 if (cmd->padding != tx_sent) { ··· 1331 1322 1332 1323 send_datacrc: 1333 1324 if (conn->conn_ops->DataDigest) { 1334 - struct kvec *iov_d = 1335 - &cmd->iov_data[cmd->iov_data_count]; 1325 + struct kvec *iov_d = &cmd->iov_data[iov_off]; 1336 1326 1337 1327 tx_sent = tx_data(conn, iov_d, 1, ISCSI_CRC_LEN); 1338 1328 if (ISCSI_CRC_LEN != tx_sent) {