aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/target
diff options
context:
space:
mode:
authorNicholas Bellinger <nab@linux-iscsi.org>2015-07-30 21:28:13 -0400
committerNicholas Bellinger <nab@linux-iscsi.org>2015-09-11 03:31:39 -0400
commit8f9b565482c537821588444e09ff732c7d65ed6e (patch)
treeb411dd3479eb5f3f86e2eb390c1c973e4b31ee12 /drivers/target
parent13a3cf08fa1e4b3a252f24202d47a556242aea03 (diff)
target/qla2xxx: Honor max_data_sg_nents I/O transfer limit
This patch adds an optional fabric driver provided SGL limit that target-core will honor as it's own internal I/O maximum transfer length limit, as exposed by EVPD=0xb0 block limits parameters. This is required for handling cases when host I/O transfer length exceeds the requested EVPD block limits maximum transfer length. The initial user of this logic is qla2xxx, so that we can avoid having to reject I/Os from some legacy FC hosts where EVPD=0xb0 parameters are not honored. When se_cmd payload length exceeds the provided limit in target_check_max_data_sg_nents() code, se_cmd->data_length + se_cmd->prot_length are reset with se_cmd->residual_count plus underflow bit for outgoing TFO response callbacks. It also checks for existing CDB level underflow + overflow and recalculates final residual_count as necessary. Note this patch currently assumes 1:1 mapping of PAGE_SIZE per struct scatterlist entry. Reported-by: Craig Watson <craig.watson@vanguard-rugged.com> Cc: Craig Watson <craig.watson@vanguard-rugged.com> Tested-by: Himanshu Madhani <himanshu.madhani@qlogic.com> Cc: Roland Dreier <roland@purestorage.com> Cc: Arun Easi <arun.easi@qlogic.com> Cc: Giridhar Malavali <giridhar.malavali@qlogic.com> Cc: Andrew Vasquez <andrew.vasquez@qlogic.com> Cc: Christoph Hellwig <hch@lst.de> Cc: Hannes Reinecke <hare@suse.de> Cc: Martin K. Petersen <martin.petersen@oracle.com> Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/target')
-rw-r--r--drivers/target/target_core_spc.c13
-rw-r--r--drivers/target/target_core_transport.c51
2 files changed, 60 insertions, 4 deletions
diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c
index a07d455e0dd5..0e0456f6a282 100644
--- a/drivers/target/target_core_spc.c
+++ b/drivers/target/target_core_spc.c
@@ -477,8 +477,8 @@ static sense_reason_t
477spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf) 477spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf)
478{ 478{
479 struct se_device *dev = cmd->se_dev; 479 struct se_device *dev = cmd->se_dev;
480 int have_tp = 0; 480 u32 mtl = 0;
481 int opt, min; 481 int have_tp = 0, opt, min;
482 482
483 /* 483 /*
484 * Following spc3r22 section 6.5.3 Block Limits VPD page, when 484 * Following spc3r22 section 6.5.3 Block Limits VPD page, when
@@ -509,8 +509,15 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf)
509 509
510 /* 510 /*
511 * Set MAXIMUM TRANSFER LENGTH 511 * Set MAXIMUM TRANSFER LENGTH
512 *
513 * XXX: Currently assumes single PAGE_SIZE per scatterlist for fabrics
514 * enforcing maximum HW scatter-gather-list entry limit
512 */ 515 */
513 put_unaligned_be32(dev->dev_attrib.hw_max_sectors, &buf[8]); 516 if (cmd->se_tfo->max_data_sg_nents) {
517 mtl = (cmd->se_tfo->max_data_sg_nents * PAGE_SIZE) /
518 dev->dev_attrib.block_size;
519 }
520 put_unaligned_be32(min_not_zero(mtl, dev->dev_attrib.hw_max_sectors), &buf[8]);
514 521
515 /* 522 /*
516 * Set OPTIMAL TRANSFER LENGTH 523 * Set OPTIMAL TRANSFER LENGTH
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 3f0b50082de4..62bafaa670f4 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1075,6 +1075,55 @@ transport_set_vpd_ident(struct t10_vpd *vpd, unsigned char *page_83)
1075} 1075}
1076EXPORT_SYMBOL(transport_set_vpd_ident); 1076EXPORT_SYMBOL(transport_set_vpd_ident);
1077 1077
1078static sense_reason_t
1079target_check_max_data_sg_nents(struct se_cmd *cmd, struct se_device *dev,
1080 unsigned int size)
1081{
1082 u32 mtl;
1083
1084 if (!cmd->se_tfo->max_data_sg_nents)
1085 return TCM_NO_SENSE;
1086 /*
1087 * Check if fabric enforced maximum SGL entries per I/O descriptor
1088 * exceeds se_cmd->data_length. If true, set SCF_UNDERFLOW_BIT +
1089 * residual_count and reduce original cmd->data_length to maximum
1090 * length based on single PAGE_SIZE entry scatter-lists.
1091 */
1092 mtl = (cmd->se_tfo->max_data_sg_nents * PAGE_SIZE);
1093 if (cmd->data_length > mtl) {
1094 /*
1095 * If an existing CDB overflow is present, calculate new residual
1096 * based on CDB size minus fabric maximum transfer length.
1097 *
1098 * If an existing CDB underflow is present, calculate new residual
1099 * based on original cmd->data_length minus fabric maximum transfer
1100 * length.
1101 *
1102 * Otherwise, set the underflow residual based on cmd->data_length
1103 * minus fabric maximum transfer length.
1104 */
1105 if (cmd->se_cmd_flags & SCF_OVERFLOW_BIT) {
1106 cmd->residual_count = (size - mtl);
1107 } else if (cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) {
1108 u32 orig_dl = size + cmd->residual_count;
1109 cmd->residual_count = (orig_dl - mtl);
1110 } else {
1111 cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT;
1112 cmd->residual_count = (cmd->data_length - mtl);
1113 }
1114 cmd->data_length = mtl;
1115 /*
1116 * Reset sbc_check_prot() calculated protection payload
1117 * length based upon the new smaller MTL.
1118 */
1119 if (cmd->prot_length) {
1120 u32 sectors = (mtl / dev->dev_attrib.block_size);
1121 cmd->prot_length = dev->prot_length * sectors;
1122 }
1123 }
1124 return TCM_NO_SENSE;
1125}
1126
1078sense_reason_t 1127sense_reason_t
1079target_cmd_size_check(struct se_cmd *cmd, unsigned int size) 1128target_cmd_size_check(struct se_cmd *cmd, unsigned int size)
1080{ 1129{
@@ -1120,7 +1169,7 @@ target_cmd_size_check(struct se_cmd *cmd, unsigned int size)
1120 } 1169 }
1121 } 1170 }
1122 1171
1123 return 0; 1172 return target_check_max_data_sg_nents(cmd, dev, size);
1124 1173
1125} 1174}
1126 1175