aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 ),