diff options
author | Michael Brown <mcb30@ipxe.org> | 2023-02-16 12:54:47 +0000 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2023-02-16 13:27:25 +0000 |
commit | 2733c4763a50b9eb0c206e7430d4d0638451e5e9 (patch) | |
tree | 7424d7a6708b8435aac95fe27cd477c68f897a10 /src/net/tcp/iscsi.c | |
parent | cff857461be443339aa39d614635d9a4eae8f8b2 (diff) | |
download | ipxe-2733c4763a50b9eb0c206e7430d4d0638451e5e9.tar.gz |
[iscsi] Limit maximum transfer size to MaxBurstLengthiscsicap
We currently specify only the iSCSI default value for MaxBurstLength
and ignore any negotiated value, since our internal block device API
allows only for receiving directly into caller-allocated buffers and
so we have no intrinsic limit on burst length.
A conscientious target may however refuse to attempt a transfer that
we request for a number of blocks that would exceed the negotiated
maximum burst length.
Fix by recording the negotiated maximum burst length and using it to
limit the maximum number of blocks per transfer as reported by the
SCSI layer.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/net/tcp/iscsi.c')
-rw-r--r-- | src/net/tcp/iscsi.c | 65 |
1 files changed, 61 insertions, 4 deletions
diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index e36d5619d..dd20849ce 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -46,6 +46,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/base16.h> #include <ipxe/base64.h> #include <ipxe/ibft.h> +#include <ipxe/blockdev.h> #include <ipxe/efi/efi_path.h> #include <ipxe/iscsi.h> @@ -86,6 +87,10 @@ FEATURE ( FEATURE_PROTOCOL, "iSCSI", DHCP_EB_FEATURE_ISCSI, 1 ); __einfo_error ( EINFO_EINVAL_NO_INITIATOR_IQN ) #define EINFO_EINVAL_NO_INITIATOR_IQN \ __einfo_uniqify ( EINFO_EINVAL, 0x05, "No initiator IQN" ) +#define EINVAL_MAXBURSTLENGTH \ + __einfo_error ( EINFO_EINVAL_MAXBURSTLENGTH ) +#define EINFO_EINVAL_MAXBURSTLENGTH \ + __einfo_uniqify ( EINFO_EINVAL, 0x06, "Invalid MaxBurstLength" ) #define EIO_TARGET_UNAVAILABLE \ __einfo_error ( EINFO_EIO_TARGET_UNAVAILABLE ) #define EINFO_EIO_TARGET_UNAVAILABLE \ @@ -281,6 +286,9 @@ static int iscsi_open_connection ( struct iscsi_session *iscsi ) { /* Assign fresh initiator task tag */ iscsi_new_itt ( iscsi ); + /* Set default operational parameters */ + iscsi->max_burst_len = ISCSI_MAX_BURST_LEN; + /* Initiate login */ iscsi_start_login ( iscsi ); @@ -736,16 +744,20 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, "MaxConnections=1%c" "InitialR2T=Yes%c" "ImmediateData=No%c" - "MaxRecvDataSegmentLength=8192%c" - "MaxBurstLength=262144%c" - "FirstBurstLength=65536%c" + "MaxRecvDataSegmentLength=%d%c" + "MaxBurstLength=%d%c" + "FirstBurstLength=%d%c" "DefaultTime2Wait=0%c" "DefaultTime2Retain=0%c" "MaxOutstandingR2T=1%c" "DataPDUInOrder=Yes%c" "DataSequenceInOrder=Yes%c" "ErrorRecoveryLevel=0%c", - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + 0, 0, 0, 0, 0, + ISCSI_MAX_RECV_DATA_SEG_LEN, 0, + ISCSI_MAX_BURST_LEN, 0, + ISCSI_FIRST_BURST_LEN, 0, + 0, 0, 0, 0, 0, 0 ); } return used; @@ -909,6 +921,31 @@ static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi, } /** + * Handle iSCSI MaxBurstLength text value + * + * @v iscsi iSCSI session + * @v value MaxBurstLength value + * @ret rc Return status code + */ +static int iscsi_handle_maxburstlength_value ( struct iscsi_session *iscsi, + const char *value ) { + unsigned long max_burst_len; + char *end; + + /* Update maximum burst length */ + max_burst_len = strtoul ( value, &end, 0 ); + if ( *end ) { + DBGC ( iscsi, "iSCSI %p invalid MaxBurstLength \"%s\"\n", + iscsi, value ); + return -EINVAL_MAXBURSTLENGTH; + } + if ( max_burst_len < iscsi->max_burst_len ) + iscsi->max_burst_len = max_burst_len; + + return 0; +} + +/** * Handle iSCSI CHAP_A text value * * @v iscsi iSCSI session @@ -1148,6 +1185,7 @@ struct iscsi_string_type { /** iSCSI text strings that we want to handle */ static struct iscsi_string_type iscsi_string_types[] = { { "TargetAddress", iscsi_handle_targetaddress_value }, + { "MaxBurstLength", iscsi_handle_maxburstlength_value }, { "AuthMethod", iscsi_handle_authmethod_value }, { "CHAP_A", iscsi_handle_chap_a_value }, { "CHAP_I", iscsi_handle_chap_i_value }, @@ -1848,6 +1886,24 @@ static int iscsi_scsi_command ( struct iscsi_session *iscsi, } /** + * Update SCSI block device capacity + * + * @v iscsi iSCSI session + * @v capacity Block device capacity + */ +static void iscsi_scsi_capacity ( struct iscsi_session *iscsi, + struct block_device_capacity *capacity ) { + unsigned int max_count; + + /* Limit maximum number of blocks per transfer to fit MaxBurstLength */ + if ( capacity->blksize ) { + max_count = ( iscsi->max_burst_len / capacity->blksize ); + if ( max_count < capacity->max_count ) + capacity->max_count = max_count; + } +} + +/** * Get iSCSI ACPI descriptor * * @v iscsi iSCSI session @@ -1862,6 +1918,7 @@ static struct acpi_descriptor * iscsi_describe ( struct iscsi_session *iscsi ) { static struct interface_operation iscsi_control_op[] = { INTF_OP ( scsi_command, struct iscsi_session *, iscsi_scsi_command ), INTF_OP ( xfer_window, struct iscsi_session *, iscsi_scsi_window ), + INTF_OP ( block_capacity, struct iscsi_session *, iscsi_scsi_capacity ), INTF_OP ( intf_close, struct iscsi_session *, iscsi_close ), INTF_OP ( acpi_describe, struct iscsi_session *, iscsi_describe ), EFI_INTF_OP ( efi_describe, struct iscsi_session *, efi_iscsi_path ), |