Skip to content

Commit

Permalink
[iscsi] Limit maximum transfer size to MaxBurstLength
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
mcb30 committed Feb 16, 2023
1 parent cff8574 commit 2733c47
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 4 deletions.
4 changes: 4 additions & 0 deletions src/drivers/block/scsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 );

Expand Down
12 changes: 12 additions & 0 deletions src/include/ipxe/iscsi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down Expand Up @@ -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
Expand Down
65 changes: 61 additions & 4 deletions src/net/tcp/iscsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down Expand Up @@ -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 \
Expand Down Expand Up @@ -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 );

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -908,6 +920,31 @@ static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
return 0;
}

/**
* 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
*
Expand Down Expand Up @@ -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 },
Expand Down Expand Up @@ -1847,6 +1885,24 @@ static int iscsi_scsi_command ( struct iscsi_session *iscsi,
return iscsi->itt;
}

/**
* 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
*
Expand All @@ -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 ),
Expand Down

0 comments on commit 2733c47

Please sign in to comment.