mirror of https://github.com/ipxe/ipxe.git
Add iscsi_rx_buffered_data() and supporting infrastructure.
parent
16b81e442f
commit
3a7d762c1c
|
@ -583,6 +583,8 @@ struct iscsi_session {
|
||||||
enum iscsi_tx_state tx_state;
|
enum iscsi_tx_state tx_state;
|
||||||
/** Byte offset within the current TX state */
|
/** Byte offset within the current TX state */
|
||||||
size_t tx_offset;
|
size_t tx_offset;
|
||||||
|
/** Length of the current TX state */
|
||||||
|
size_t tx_len;
|
||||||
|
|
||||||
/** Basic header segment for current RX PDU */
|
/** Basic header segment for current RX PDU */
|
||||||
union iscsi_bhs rx_bhs;
|
union iscsi_bhs rx_bhs;
|
||||||
|
@ -590,6 +592,10 @@ struct iscsi_session {
|
||||||
enum iscsi_rx_state rx_state;
|
enum iscsi_rx_state rx_state;
|
||||||
/** Byte offset within the current RX state */
|
/** Byte offset within the current RX state */
|
||||||
size_t rx_offset;
|
size_t rx_offset;
|
||||||
|
/** Length of the current RX state */
|
||||||
|
size_t rx_len;
|
||||||
|
/** Buffer for received data (not always used) */
|
||||||
|
void *rx_buffer;
|
||||||
/** State of strings received during login phase */
|
/** State of strings received during login phase */
|
||||||
struct iscsi_string_state string;
|
struct iscsi_string_state string;
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,46 @@ static void iscsi_start_tx ( struct iscsi_session *iscsi );
|
||||||
static void iscsi_start_data_out ( struct iscsi_session *iscsi,
|
static void iscsi_start_data_out ( struct iscsi_session *iscsi,
|
||||||
unsigned int datasn );
|
unsigned int datasn );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive PDU data into buffer
|
||||||
|
*
|
||||||
|
* @v iscsi iSCSI session
|
||||||
|
* @v data Data to receive
|
||||||
|
* @v len Length of data
|
||||||
|
* @ret rc Return status code
|
||||||
|
*
|
||||||
|
* This can be used when the RX PDU type handler wishes to buffer up
|
||||||
|
* all received data and process the PDU as a single unit. The caller
|
||||||
|
* is repsonsible for calling iscsi_rx_buffered_data_done() after
|
||||||
|
* processing the data.
|
||||||
|
*/
|
||||||
|
static int iscsi_rx_buffered_data ( struct iscsi_session *iscsi,
|
||||||
|
const void *data, size_t len ) {
|
||||||
|
|
||||||
|
/* Allocate buffer on first call */
|
||||||
|
if ( ! iscsi->rx_buffer ) {
|
||||||
|
iscsi->rx_buffer = malloc ( iscsi->rx_len );
|
||||||
|
if ( ! iscsi->rx_buffer )
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy data to buffer */
|
||||||
|
assert ( ( iscsi->rx_offset + len ) <= iscsi->rx_len );
|
||||||
|
memcpy ( ( iscsi->rx_buffer + iscsi->rx_offset ), data, len );
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finish receiving PDU data into buffer
|
||||||
|
*
|
||||||
|
* @v iscsi iSCSI session
|
||||||
|
*/
|
||||||
|
static void iscsi_rx_buffered_data_done ( struct iscsi_session *iscsi ) {
|
||||||
|
free ( iscsi->rx_buffer );
|
||||||
|
iscsi->rx_buffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark iSCSI operation as complete
|
* Mark iSCSI operation as complete
|
||||||
*
|
*
|
||||||
|
@ -52,10 +92,14 @@ static void iscsi_start_data_out ( struct iscsi_session *iscsi,
|
||||||
* should both be idle.
|
* should both be idle.
|
||||||
*/
|
*/
|
||||||
static void iscsi_done ( struct iscsi_session *iscsi, int rc ) {
|
static void iscsi_done ( struct iscsi_session *iscsi, int rc ) {
|
||||||
|
|
||||||
/* Clear current SCSI command */
|
/* Clear current SCSI command */
|
||||||
iscsi->command = NULL;
|
iscsi->command = NULL;
|
||||||
/* Free any memory that may have been used for CHAP */
|
|
||||||
|
/* Free any dynamically allocated memory */
|
||||||
chap_finish ( &iscsi->chap );
|
chap_finish ( &iscsi->chap );
|
||||||
|
iscsi_rx_buffered_data_done ( iscsi );
|
||||||
|
|
||||||
/* Mark asynchronous operation as complete */
|
/* Mark asynchronous operation as complete */
|
||||||
async_done ( &iscsi->aop, rc );
|
async_done ( &iscsi->aop, rc );
|
||||||
}
|
}
|
||||||
|
@ -283,7 +327,7 @@ static void iscsi_tx_data_out ( struct iscsi_session *iscsi,
|
||||||
|
|
||||||
offset = ( iscsi->transfer_offset + ntohl ( data_out->offset ) +
|
offset = ( iscsi->transfer_offset + ntohl ( data_out->offset ) +
|
||||||
iscsi->tx_offset );
|
iscsi->tx_offset );
|
||||||
remaining = ( ISCSI_DATA_LEN ( data_out->lengths ) - iscsi->tx_offset);
|
remaining = ( iscsi->tx_len - iscsi->tx_offset );
|
||||||
assert ( iscsi->command != NULL );
|
assert ( iscsi->command != NULL );
|
||||||
assert ( iscsi->command->data_out );
|
assert ( iscsi->command->data_out );
|
||||||
assert ( ( offset + remaining ) <= iscsi->command->data_out_len );
|
assert ( ( offset + remaining ) <= iscsi->command->data_out_len );
|
||||||
|
@ -693,6 +737,14 @@ static void iscsi_handle_string_byte ( struct iscsi_session *iscsi,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void iscsi_handle_strings ( struct iscsi_session *iscsi,
|
||||||
|
const char *strings, size_t len ) {
|
||||||
|
for ( ; len-- ; strings++ ) {
|
||||||
|
iscsi_handle_string_byte ( iscsi,
|
||||||
|
* ( ( uint8_t * ) strings ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receive data segment of an iSCSI login response PDU
|
* Receive data segment of an iSCSI login response PDU
|
||||||
*
|
*
|
||||||
|
@ -706,6 +758,20 @@ static void iscsi_rx_login_response ( struct iscsi_session *iscsi, void *data,
|
||||||
size_t len, size_t remaining ) {
|
size_t len, size_t remaining ) {
|
||||||
struct iscsi_bhs_login_response *response
|
struct iscsi_bhs_login_response *response
|
||||||
= &iscsi->rx_bhs.login_response;
|
= &iscsi->rx_bhs.login_response;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Buffer up the PDU data */
|
||||||
|
if ( ( rc = iscsi_rx_buffered_data ( iscsi, data, len ) ) != 0 ) {
|
||||||
|
DBG ( "iSCSI %p could not buffer login response\n", iscsi );
|
||||||
|
iscsi_close ( iscsi, rc );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( remaining )
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Process string data and discard string buffer */
|
||||||
|
iscsi_handle_strings ( iscsi, iscsi->rx_buffer, iscsi->rx_len );
|
||||||
|
iscsi_rx_buffered_data_done ( iscsi );
|
||||||
|
|
||||||
/* Check for fatal errors */
|
/* Check for fatal errors */
|
||||||
if ( response->status_class != 0 ) {
|
if ( response->status_class != 0 ) {
|
||||||
|
@ -715,13 +781,6 @@ static void iscsi_rx_login_response ( struct iscsi_session *iscsi, void *data,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process strings data */
|
|
||||||
for ( ; len-- ; data++ ) {
|
|
||||||
iscsi_handle_string_byte ( iscsi, * ( ( uint8_t * ) data ) );
|
|
||||||
}
|
|
||||||
if ( remaining )
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Handle login transitions */
|
/* Handle login transitions */
|
||||||
if ( response->flags & ISCSI_LOGIN_FLAG_TRANSITION ) {
|
if ( response->flags & ISCSI_LOGIN_FLAG_TRANSITION ) {
|
||||||
switch ( response->flags & ISCSI_LOGIN_NSG_MASK ) {
|
switch ( response->flags & ISCSI_LOGIN_NSG_MASK ) {
|
||||||
|
@ -848,26 +907,25 @@ static void iscsi_tx_done ( struct iscsi_session *iscsi ) {
|
||||||
static void iscsi_acked ( struct tcp_connection *conn, size_t len ) {
|
static void iscsi_acked ( struct tcp_connection *conn, size_t len ) {
|
||||||
struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
|
struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
|
||||||
struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
|
struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
|
||||||
size_t max_tx_offset;
|
|
||||||
enum iscsi_tx_state next_state;
|
enum iscsi_tx_state next_state;
|
||||||
|
|
||||||
iscsi->tx_offset += len;
|
iscsi->tx_offset += len;
|
||||||
while ( 1 ) {
|
while ( 1 ) {
|
||||||
switch ( iscsi->tx_state ) {
|
switch ( iscsi->tx_state ) {
|
||||||
case ISCSI_TX_BHS:
|
case ISCSI_TX_BHS:
|
||||||
max_tx_offset = sizeof ( iscsi->tx_bhs );
|
iscsi->tx_len = sizeof ( iscsi->tx_bhs );
|
||||||
next_state = ISCSI_TX_AHS;
|
next_state = ISCSI_TX_AHS;
|
||||||
break;
|
break;
|
||||||
case ISCSI_TX_AHS:
|
case ISCSI_TX_AHS:
|
||||||
max_tx_offset = 4 * ISCSI_AHS_LEN ( common->lengths );
|
iscsi->tx_len = 4 * ISCSI_AHS_LEN ( common->lengths );
|
||||||
next_state = ISCSI_TX_DATA;
|
next_state = ISCSI_TX_DATA;
|
||||||
break;
|
break;
|
||||||
case ISCSI_TX_DATA:
|
case ISCSI_TX_DATA:
|
||||||
max_tx_offset = ISCSI_DATA_LEN ( common->lengths );
|
iscsi->tx_len = ISCSI_DATA_LEN ( common->lengths );
|
||||||
next_state = ISCSI_TX_DATA_PADDING;
|
next_state = ISCSI_TX_DATA_PADDING;
|
||||||
break;
|
break;
|
||||||
case ISCSI_TX_DATA_PADDING:
|
case ISCSI_TX_DATA_PADDING:
|
||||||
max_tx_offset = ISCSI_DATA_PAD_LEN ( common->lengths );
|
iscsi->tx_len = ISCSI_DATA_PAD_LEN ( common->lengths );
|
||||||
next_state = ISCSI_TX_IDLE;
|
next_state = ISCSI_TX_IDLE;
|
||||||
break;
|
break;
|
||||||
case ISCSI_TX_IDLE:
|
case ISCSI_TX_IDLE:
|
||||||
|
@ -876,12 +934,12 @@ static void iscsi_acked ( struct tcp_connection *conn, size_t len ) {
|
||||||
assert ( 0 );
|
assert ( 0 );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert ( iscsi->tx_offset <= max_tx_offset );
|
assert ( iscsi->tx_offset <= iscsi->tx_len );
|
||||||
|
|
||||||
/* If the whole of the current portion has not yet
|
/* If the whole of the current portion has not yet
|
||||||
* been acked, stay in this state for now.
|
* been acked, stay in this state for now.
|
||||||
*/
|
*/
|
||||||
if ( iscsi->tx_offset != max_tx_offset )
|
if ( iscsi->tx_offset != iscsi->tx_len )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Move to next state. Call iscsi_tx_done() when PDU
|
/* Move to next state. Call iscsi_tx_done() when PDU
|
||||||
|
@ -1033,7 +1091,6 @@ static void iscsi_newdata ( struct tcp_connection *conn, void *data,
|
||||||
struct iscsi_bhs_common *common = &iscsi->rx_bhs.common;
|
struct iscsi_bhs_common *common = &iscsi->rx_bhs.common;
|
||||||
void ( *process ) ( struct iscsi_session *iscsi, void *data,
|
void ( *process ) ( struct iscsi_session *iscsi, void *data,
|
||||||
size_t len, size_t remaining );
|
size_t len, size_t remaining );
|
||||||
size_t max_rx_offset;
|
|
||||||
enum iscsi_rx_state next_state;
|
enum iscsi_rx_state next_state;
|
||||||
size_t frag_len;
|
size_t frag_len;
|
||||||
size_t remaining;
|
size_t remaining;
|
||||||
|
@ -1042,22 +1099,22 @@ static void iscsi_newdata ( struct tcp_connection *conn, void *data,
|
||||||
switch ( iscsi->rx_state ) {
|
switch ( iscsi->rx_state ) {
|
||||||
case ISCSI_RX_BHS:
|
case ISCSI_RX_BHS:
|
||||||
process = iscsi_rx_bhs;
|
process = iscsi_rx_bhs;
|
||||||
max_rx_offset = sizeof ( iscsi->rx_bhs );
|
iscsi->rx_len = sizeof ( iscsi->rx_bhs );
|
||||||
next_state = ISCSI_RX_AHS;
|
next_state = ISCSI_RX_AHS;
|
||||||
break;
|
break;
|
||||||
case ISCSI_RX_AHS:
|
case ISCSI_RX_AHS:
|
||||||
process = iscsi_rx_discard;
|
process = iscsi_rx_discard;
|
||||||
max_rx_offset = 4 * ISCSI_AHS_LEN ( common->lengths );
|
iscsi->rx_len = 4 * ISCSI_AHS_LEN ( common->lengths );
|
||||||
next_state = ISCSI_RX_DATA;
|
next_state = ISCSI_RX_DATA;
|
||||||
break;
|
break;
|
||||||
case ISCSI_RX_DATA:
|
case ISCSI_RX_DATA:
|
||||||
process = iscsi_rx_data;
|
process = iscsi_rx_data;
|
||||||
max_rx_offset = ISCSI_DATA_LEN ( common->lengths );
|
iscsi->rx_len = ISCSI_DATA_LEN ( common->lengths );
|
||||||
next_state = ISCSI_RX_DATA_PADDING;
|
next_state = ISCSI_RX_DATA_PADDING;
|
||||||
break;
|
break;
|
||||||
case ISCSI_RX_DATA_PADDING:
|
case ISCSI_RX_DATA_PADDING:
|
||||||
process = iscsi_rx_discard;
|
process = iscsi_rx_discard;
|
||||||
max_rx_offset = ISCSI_DATA_PAD_LEN ( common->lengths );
|
iscsi->rx_len = ISCSI_DATA_PAD_LEN ( common->lengths );
|
||||||
next_state = ISCSI_RX_BHS;
|
next_state = ISCSI_RX_BHS;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1065,10 +1122,10 @@ static void iscsi_newdata ( struct tcp_connection *conn, void *data,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
frag_len = max_rx_offset - iscsi->rx_offset;
|
frag_len = iscsi->rx_len - iscsi->rx_offset;
|
||||||
if ( frag_len > len )
|
if ( frag_len > len )
|
||||||
frag_len = len;
|
frag_len = len;
|
||||||
remaining = max_rx_offset - iscsi->rx_offset - frag_len;
|
remaining = iscsi->rx_len - iscsi->rx_offset - frag_len;
|
||||||
process ( iscsi, data, frag_len, remaining );
|
process ( iscsi, data, frag_len, remaining );
|
||||||
|
|
||||||
iscsi->rx_offset += frag_len;
|
iscsi->rx_offset += frag_len;
|
||||||
|
@ -1078,7 +1135,7 @@ static void iscsi_newdata ( struct tcp_connection *conn, void *data,
|
||||||
/* If all the data for this state has not yet been
|
/* If all the data for this state has not yet been
|
||||||
* received, stay in this state for now.
|
* received, stay in this state for now.
|
||||||
*/
|
*/
|
||||||
if ( iscsi->rx_offset != max_rx_offset )
|
if ( iscsi->rx_offset != iscsi->rx_len )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
iscsi->rx_state = next_state;
|
iscsi->rx_state = next_state;
|
||||||
|
|
Loading…
Reference in New Issue