aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2023-02-16 12:54:47 +0000
committerMichael Brown <mcb30@ipxe.org>2023-02-16 13:27:25 +0000
commit2733c4763a50b9eb0c206e7430d4d0638451e5e9 (patch)
tree7424d7a6708b8435aac95fe27cd477c68f897a10
parentcff857461be443339aa39d614635d9a4eae8f8b2 (diff)
downloadipxe-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.c4
-rw-r--r--src/include/ipxe/iscsi.h12
-rw-r--r--src/net/tcp/iscsi.c65
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 ),