diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2015-07-30 21:28:13 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2015-09-11 03:31:39 -0400 |
commit | 8f9b565482c537821588444e09ff732c7d65ed6e (patch) | |
tree | b411dd3479eb5f3f86e2eb390c1c973e4b31ee12 | |
parent | 13a3cf08fa1e4b3a252f24202d47a556242aea03 (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>
-rw-r--r-- | drivers/scsi/qla2xxx/tcm_qla2xxx.c | 5 | ||||
-rw-r--r-- | drivers/target/target_core_spc.c | 13 | ||||
-rw-r--r-- | drivers/target/target_core_transport.c | 51 | ||||
-rw-r--r-- | include/target/target_core_fabric.h | 13 |
4 files changed, 78 insertions, 4 deletions
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index c621623abeed..edeb3aefa6fe 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c | |||
@@ -1808,6 +1808,11 @@ static const struct target_core_fabric_ops tcm_qla2xxx_ops = { | |||
1808 | .module = THIS_MODULE, | 1808 | .module = THIS_MODULE, |
1809 | .name = "qla2xxx", | 1809 | .name = "qla2xxx", |
1810 | .node_acl_size = sizeof(struct tcm_qla2xxx_nacl), | 1810 | .node_acl_size = sizeof(struct tcm_qla2xxx_nacl), |
1811 | /* | ||
1812 | * XXX: Limit assumes single page per scatter-gather-list entry. | ||
1813 | * Current maximum is ~4.9 MB per se_cmd->t_data_sg with PAGE_SIZE=4096 | ||
1814 | */ | ||
1815 | .max_data_sg_nents = 1200, | ||
1811 | .get_fabric_name = tcm_qla2xxx_get_fabric_name, | 1816 | .get_fabric_name = tcm_qla2xxx_get_fabric_name, |
1812 | .tpg_get_wwn = tcm_qla2xxx_get_fabric_wwn, | 1817 | .tpg_get_wwn = tcm_qla2xxx_get_fabric_wwn, |
1813 | .tpg_get_tag = tcm_qla2xxx_get_tag, | 1818 | .tpg_get_tag = tcm_qla2xxx_get_tag, |
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 | |||
477 | spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf) | 477 | spc_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 | } |
1076 | EXPORT_SYMBOL(transport_set_vpd_ident); | 1076 | EXPORT_SYMBOL(transport_set_vpd_ident); |
1077 | 1077 | ||
1078 | static sense_reason_t | ||
1079 | target_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 | |||
1078 | sense_reason_t | 1127 | sense_reason_t |
1079 | target_cmd_size_check(struct se_cmd *cmd, unsigned int size) | 1128 | target_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 | ||
diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h index 69355feabd1d..7fb2557a760e 100644 --- a/include/target/target_core_fabric.h +++ b/include/target/target_core_fabric.h | |||
@@ -5,6 +5,19 @@ struct target_core_fabric_ops { | |||
5 | struct module *module; | 5 | struct module *module; |
6 | const char *name; | 6 | const char *name; |
7 | size_t node_acl_size; | 7 | size_t node_acl_size; |
8 | /* | ||
9 | * Limits number of scatterlist entries per SCF_SCSI_DATA_CDB payload. | ||
10 | * Setting this value tells target-core to enforce this limit, and | ||
11 | * report as INQUIRY EVPD=b0 MAXIMUM TRANSFER LENGTH. | ||
12 | * | ||
13 | * target-core will currently reset se_cmd->data_length to this | ||
14 | * maximum size, and set UNDERFLOW residual count if length exceeds | ||
15 | * this limit. | ||
16 | * | ||
17 | * XXX: Not all initiator hosts honor this block-limit EVPD | ||
18 | * XXX: Currently assumes single PAGE_SIZE per scatterlist entry | ||
19 | */ | ||
20 | u32 max_data_sg_nents; | ||
8 | char *(*get_fabric_name)(void); | 21 | char *(*get_fabric_name)(void); |
9 | char *(*tpg_get_wwn)(struct se_portal_group *); | 22 | char *(*tpg_get_wwn)(struct se_portal_group *); |
10 | u16 (*tpg_get_tag)(struct se_portal_group *); | 23 | u16 (*tpg_get_tag)(struct se_portal_group *); |