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 | |
parent | cff857461be443339aa39d614635d9a4eae8f8b2 (diff) | |
download | ipxe-2733c4763a50b9eb0c206e7430d4d0638451e5e9.zip ipxe-2733c4763a50b9eb0c206e7430d4d0638451e5e9.tar.gz ipxe-2733c4763a50b9eb0c206e7430d4d0638451e5e9.tar.bz2 |
[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>
-rw-r--r-- | src/drivers/block/scsi.c | 4 | ||||
-rw-r--r-- | src/include/ipxe/iscsi.h | 12 | ||||
-rw-r--r-- | src/net/tcp/iscsi.c | 65 |
3 files changed, 77 insertions, 4 deletions
diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c index f765c97..ff415f5 100644 --- a/src/drivers/block/scsi.c +++ b/src/drivers/block/scsi.c @@ -609,6 +609,7 @@ static void scsicmd_read_capacity_cmd ( struct scsi_command *scsicmd, */ static void scsicmd_read_capacity_done ( struct scsi_command *scsicmd, int rc ) { + struct scsi_device *scsidev = scsicmd->scsidev; struct scsi_read_capacity_private *priv = scsicmd_priv ( scsicmd ); struct scsi_capacity_16 *capacity16 = &priv->capacity.capacity16; struct scsi_capacity_10 *capacity10 = &priv->capacity.capacity10; @@ -645,6 +646,9 @@ static void scsicmd_read_capacity_done ( struct scsi_command *scsicmd, } capacity.max_count = -1U; + /* Allow transport layer to update capacity */ + block_capacity ( &scsidev->scsi, &capacity ); + /* Return capacity to caller */ block_capacity ( &scsicmd->block, &capacity ); diff --git a/src/include/ipxe/iscsi.h b/src/include/ipxe/iscsi.h index 966cf52..a25eec2 100644 --- a/src/include/ipxe/iscsi.h +++ b/src/include/ipxe/iscsi.h @@ -22,6 +22,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Default iSCSI port */ #define ISCSI_PORT 3260 +/** Default iSCSI first burst length */ +#define ISCSI_FIRST_BURST_LEN 65536 + +/** Default iSCSI maximum burst length */ +#define ISCSI_MAX_BURST_LEN 262144 + +/** Default iSCSI maximum receive data segment length */ +#define ISCSI_MAX_RECV_DATA_SEG_LEN 8192 + /** * iSCSI segment lengths * @@ -577,6 +586,9 @@ struct iscsi_session { /** CHAP response (used for both initiator and target auth) */ struct chap_response chap; + /** Maximum burst length */ + size_t max_burst_len; + /** Initiator session ID (IANA format) qualifier * * This is part of the ISID. It is generated randomly diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index e36d561..dd20849 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 ), |