[scsi] Use data-transfer buffers for data-in and data-out

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2026-06-15 14:20:16 +01:00
parent 13a83f4ab3
commit 06f36026d6
6 changed files with 120 additions and 102 deletions
+10 -8
View File
@@ -422,6 +422,8 @@ static int scsicmd_command ( struct scsi_command *scsicmd ) {
/* Construct command */
memset ( &command, 0, sizeof ( command ) );
memcpy ( &command.lun, &scsidev->lun, sizeof ( command.lun ) );
xferbuf_void_init ( &command.data_in );
xferbuf_void_init ( &command.data_out );
scsicmd->type->cmd ( scsicmd, &command );
/* Issue command */
@@ -517,8 +519,8 @@ static void scsicmd_read_cmd ( struct scsi_command *scsicmd,
command->cdb.read10.lba = cpu_to_be32 ( scsicmd->lba );
command->cdb.read10.len = cpu_to_be16 ( scsicmd->count );
}
command->data_in = scsicmd->buffer;
command->data_in_len = scsicmd->len;
xferbuf_fixed_init ( &command->data_in, scsicmd->buffer,
scsicmd->len );
}
/** SCSI READ command type */
@@ -548,8 +550,8 @@ static void scsicmd_write_cmd ( struct scsi_command *scsicmd,
command->cdb.write10.lba = cpu_to_be32 ( scsicmd->lba );
command->cdb.write10.len = cpu_to_be16 ( scsicmd->count );
}
command->data_out = scsicmd->buffer;
command->data_out_len = scsicmd->len;
xferbuf_fixed_init ( &command->data_out, scsicmd->buffer,
scsicmd->len );
}
/** SCSI WRITE command type */
@@ -592,13 +594,13 @@ static void scsicmd_read_capacity_cmd ( struct scsi_command *scsicmd,
readcap16->service_action =
SCSI_SERVICE_ACTION_READ_CAPACITY_16;
readcap16->len = cpu_to_be32 ( sizeof ( *capacity16 ) );
command->data_in = capacity16;
command->data_in_len = sizeof ( *capacity16 );
xferbuf_fixed_init ( &command->data_in, capacity16,
sizeof ( *capacity16 ) );
} else {
/* Use READ CAPACITY (10) */
readcap10->opcode = SCSI_OPCODE_READ_CAPACITY_10;
command->data_in = capacity10;
command->data_in_len = sizeof ( *capacity10 );
xferbuf_fixed_init ( &command->data_in, capacity10,
sizeof ( *capacity10 ) );
}
}
+6 -6
View File
@@ -424,23 +424,23 @@ static int srp_cmd ( struct srp_device *srpdev,
memcpy ( &cmd->cdb, &command->cdb, sizeof ( cmd->cdb ) );
/* Construct data-out descriptor, if present */
if ( command->data_out ) {
if ( command->data_out.len ) {
cmd->data_buffer_formats |= SRP_CMD_DO_FMT_DIRECT;
data_out = iob_put ( iobuf, sizeof ( *data_out ) );
data_out->address =
cpu_to_be64 ( virt_to_phys ( command->data_out ) );
cpu_to_be64 ( virt_to_phys ( command->data_out.data ) );
data_out->handle = ntohl ( srpdev->memory_handle );
data_out->len = ntohl ( command->data_out_len );
data_out->len = ntohl ( command->data_out.len );
}
/* Construct data-in descriptor, if present */
if ( command->data_in ) {
if ( command->data_in.len ) {
cmd->data_buffer_formats |= SRP_CMD_DI_FMT_DIRECT;
data_in = iob_put ( iobuf, sizeof ( *data_in ) );
data_in->address =
cpu_to_be64 ( virt_to_phys ( command->data_in ) );
cpu_to_be64 ( virt_to_phys ( command->data_in.data ) );
data_in->handle = ntohl ( srpdev->memory_handle );
data_in->len = ntohl ( command->data_in_len );
data_in->len = ntohl ( command->data_in.len );
}
DBGC2 ( srpdev, "SRP %p tag %08x CMD " SCSI_CDB_FORMAT "\n",
+27 -22
View File
@@ -154,7 +154,7 @@ static int usbblk_out_command ( struct usbblk_device *usbblk ) {
/* Sanity checks */
assert ( cmd->tag );
assert ( ! ( cmd->scsi.data_in_len && cmd->scsi.data_out_len ) );
assert ( ! ( cmd->scsi.data_in.len && cmd->scsi.data_out.len ) );
/* Allocate I/O buffer */
iobuf = alloc_iob ( sizeof ( *wrapper ) );
@@ -168,10 +168,10 @@ static int usbblk_out_command ( struct usbblk_device *usbblk ) {
memset ( wrapper, 0, sizeof ( *wrapper ) );
wrapper->signature = cpu_to_le32 ( USBBLK_COMMAND_SIGNATURE );
wrapper->tag = cmd->tag; /* non-endian */
if ( cmd->scsi.data_out_len ) {
wrapper->len = cpu_to_le32 ( cmd->scsi.data_out_len );
if ( cmd->scsi.data_out.len ) {
wrapper->len = cpu_to_le32 ( cmd->scsi.data_out.len );
} else {
wrapper->len = cpu_to_le32 ( cmd->scsi.data_in_len );
wrapper->len = cpu_to_le32 ( cmd->scsi.data_in.len );
wrapper->flags = USB_DIR_IN;
}
wrapper->lun = ntohs ( cmd->scsi.lun.u16[0] );
@@ -207,9 +207,9 @@ static int usbblk_out_data ( struct usbblk_device *usbblk ) {
/* Calculate length */
assert ( cmd->tag );
assert ( cmd->scsi.data_out != NULL );
assert ( cmd->offset < cmd->scsi.data_out_len );
len = ( cmd->scsi.data_out_len - cmd->offset );
assert ( cmd->scsi.data_out.len != 0 );
assert ( cmd->offset < cmd->scsi.data_out.len );
len = ( cmd->scsi.data_out.len - cmd->offset );
if ( len > USBBLK_MAX_LEN )
len = USBBLK_MAX_LEN;
assert ( ( len % usbblk->out.mtu ) == 0 );
@@ -222,8 +222,9 @@ static int usbblk_out_data ( struct usbblk_device *usbblk ) {
}
/* Populate I/O buffer */
memcpy ( iob_put ( iobuf, len ),
( cmd->scsi.data_out + cmd->offset ), len );
if ( ( rc = xferbuf_read ( &cmd->scsi.data_out, cmd->offset,
iob_put ( iobuf, len ), len ) ) != 0 )
goto err_read;
/* Send data */
if ( ( rc = usb_stream ( &usbblk->out, iobuf, 0 ) ) != 0 ) {
@@ -238,6 +239,7 @@ static int usbblk_out_data ( struct usbblk_device *usbblk ) {
return 0;
err_stream:
err_read:
free_iob ( iobuf );
err_alloc:
return rc;
@@ -257,7 +259,7 @@ static int usbblk_out_refill ( struct usbblk_device *usbblk ) {
assert ( cmd->tag );
/* Refill endpoint */
while ( ( cmd->offset < cmd->scsi.data_out_len ) &&
while ( ( cmd->offset < cmd->scsi.data_out.len ) &&
( usbblk->out.fill < USBBLK_MAX_FILL ) ) {
if ( ( rc = usbblk_out_data ( usbblk ) ) != 0 )
return rc;
@@ -294,7 +296,7 @@ static void usbblk_out_complete ( struct usb_endpoint *ep,
}
/* Trigger refill process, if applicable */
if ( cmd->offset < cmd->scsi.data_out_len )
if ( cmd->offset < cmd->scsi.data_out.len )
process_add ( &usbblk->process );
drop:
@@ -331,15 +333,18 @@ static struct usb_endpoint_driver_operations usbblk_out_operations = {
static int usbblk_in_data ( struct usbblk_device *usbblk, const void *data,
size_t len ) {
struct usbblk_command *cmd = &usbblk->cmd;
int rc;
/* Sanity checks */
assert ( cmd->tag );
assert ( cmd->scsi.data_in != NULL );
assert ( cmd->offset <= cmd->scsi.data_in_len );
assert ( len <= ( cmd->scsi.data_in_len - cmd->offset ) );
assert ( cmd->scsi.data_in.len != 0 );
assert ( cmd->offset <= cmd->scsi.data_in.len );
assert ( len <= ( cmd->scsi.data_in.len - cmd->offset ) );
/* Store data */
memcpy ( ( cmd->scsi.data_in + cmd->offset ), data, len );
if ( ( rc = xferbuf_write ( &cmd->scsi.data_in, cmd->offset,
data, len ) ) != 0 )
return rc;
cmd->offset += len;
return 0;
@@ -426,9 +431,9 @@ static int usbblk_in_refill ( struct usbblk_device *usbblk ) {
/* Calculate maximum required refill */
remaining = sizeof ( *stat );
if ( cmd->scsi.data_in_len ) {
assert ( cmd->offset <= cmd->scsi.data_in_len );
remaining += ( cmd->scsi.data_in_len - cmd->offset );
if ( cmd->scsi.data_in.len ) {
assert ( cmd->offset <= cmd->scsi.data_in.len );
remaining += ( cmd->scsi.data_in.len - cmd->offset );
}
max = ( ( remaining + USBBLK_MAX_LEN - 1 ) / USBBLK_MAX_LEN );
@@ -472,9 +477,9 @@ static void usbblk_in_complete ( struct usb_endpoint *ep,
process_add ( &usbblk->process );
/* Handle data portion, if any */
if ( cmd->scsi.data_in_len ) {
assert ( cmd->offset <= cmd->scsi.data_in_len );
remaining = ( cmd->scsi.data_in_len - cmd->offset );
if ( cmd->scsi.data_in.len ) {
assert ( cmd->offset <= cmd->scsi.data_in.len );
remaining = ( cmd->scsi.data_in.len - cmd->offset );
len = iob_len ( iobuf );
if ( len > remaining )
len = remaining;
@@ -591,7 +596,7 @@ static int usbblk_start ( struct usbblk_device *usbblk,
}
/* Refuse bidirectional commands */
if ( scsicmd->data_in_len && scsicmd->data_out_len ) {
if ( scsicmd->data_in.len && scsicmd->data_out.len ) {
rc = -EOPNOTSUPP;
DBGC ( usbblk, "USBBLK %s cannot support bidirectional "
"commands\n", usbblk->func->name );
+5 -14
View File
@@ -3,6 +3,7 @@
#include <stdint.h>
#include <ipxe/interface.h>
#include <ipxe/xferbuf.h>
/** @file
*
@@ -251,20 +252,10 @@ struct scsi_cmd {
struct scsi_lun lun;
/** CDB for this command */
union scsi_cdb cdb;
/** Data-out buffer (may be NULL) */
void *data_out;
/** Data-out buffer length
*
* Must be zero if @c data_out is NULL
*/
size_t data_out_len;
/** Data-in buffer (may be NULL) */
void *data_in;
/** Data-in buffer length
*
* Must be zero if @c data_in is NULL
*/
size_t data_in_len;
/** Data-out buffer */
struct xfer_buffer data_out;
/** Data-in buffer */
struct xfer_buffer data_in;
};
/** SCSI fixed-format sense data */
+31 -29
View File
@@ -326,7 +326,7 @@ static int fcpcmd_send_cmnd ( struct fcp_command *fcpcmd ) {
int rc;
/* Sanity check */
if ( command->data_in_len && command->data_out_len ) {
if ( command->data_in.len && command->data_out.len ) {
DBGC ( fcpdev, "FCP %p xchg %04x cannot handle bidirectional "
"command\n", fcpdev, fcpcmd->xchg_id );
return -ENOTSUP;
@@ -344,13 +344,13 @@ static int fcpcmd_send_cmnd ( struct fcp_command *fcpcmd ) {
cmnd = iob_put ( iobuf, sizeof ( *cmnd ) );
memset ( cmnd, 0, sizeof ( *cmnd ) );
memcpy ( &cmnd->lun, &command->lun, sizeof ( cmnd->lun ) );
assert ( ! ( command->data_in_len && command->data_out_len ) );
if ( command->data_in_len )
assert ( ! ( command->data_in.len && command->data_out.len ) );
if ( command->data_in.len )
cmnd->dirn |= FCP_CMND_RDDATA;
if ( command->data_out_len )
if ( command->data_out.len )
cmnd->dirn |= FCP_CMND_WRDATA;
memcpy ( &cmnd->cdb, &fcpcmd->command.cdb, sizeof ( cmnd->cdb ) );
cmnd->len = htonl ( command->data_in_len + command->data_out_len );
cmnd->len = htonl ( command->data_in.len + command->data_out.len );
memset ( &meta, 0, sizeof ( meta ) );
meta.flags = ( XFER_FL_CMD_STAT | XFER_FL_OVER );
DBGC2 ( fcpdev, "FCP %p xchg %04x CMND " SCSI_CDB_FORMAT " %04x\n",
@@ -402,20 +402,18 @@ static int fcpcmd_recv_rddata ( struct fcp_command *fcpcmd,
rc = -ERANGE_READ_DATA_ORDERING;
goto done;
}
if ( ( offset + len ) > command->data_in_len ) {
DBGC ( fcpdev, "FCP %p xchg %04x read data overrun (max %zd, "
"received %zd)\n", fcpdev, fcpcmd->xchg_id,
command->data_in_len, ( offset + len ) );
rc = -ERANGE_READ_DATA_OVERRUN;
goto done;
}
DBGC2 ( fcpdev, "FCP %p xchg %04x RDDATA [%08zx,%08zx)\n",
fcpdev, fcpcmd->xchg_id, offset, ( offset + len ) );
/* Copy to user buffer */
memcpy ( ( command->data_in + offset ), iobuf->data, len );
if ( ( rc = xferbuf_write ( &command->data_in, offset,
iobuf->data, len ) ) != 0 ) {
DBGC ( fcpdev, "FCP %p xchg %04x buffer overrun: %s\n",
fcpdev, fcpcmd->xchg_id, strerror ( rc ) );
goto done;
}
fcpcmd->offset += len;
assert ( fcpcmd->offset <= command->data_in_len );
assert ( fcpcmd->offset <= command->data_in.len );
rc = 0;
done:
@@ -446,13 +444,8 @@ static int fcpcmd_send_wrdata ( struct fcp_command *fcpcmd ) {
if ( len == 0 ) {
DBGC ( fcpdev, "FCP %p xchg %04x write data stuck\n",
fcpdev, fcpcmd->xchg_id );
return -ERANGE_WRITE_DATA_STUCK;
}
if ( ( fcpcmd->offset + len ) > command->data_out_len ) {
DBGC ( fcpdev, "FCP %p xchg %04x write data overrun (max %zd, "
"requested %zd)\n", fcpdev, fcpcmd->xchg_id,
command->data_out_len, ( fcpcmd->offset + len ) );
return -ERANGE_WRITE_DATA_OVERRUN;
rc = -ERANGE_WRITE_DATA_STUCK;
goto err_sanity;
}
/* Allocate I/O buffer */
@@ -460,12 +453,14 @@ static int fcpcmd_send_wrdata ( struct fcp_command *fcpcmd ) {
if ( ! iobuf ) {
DBGC ( fcpdev, "FCP %p xchg %04x cannot allocate write data "
"IU for %zd bytes\n", fcpdev, fcpcmd->xchg_id, len );
return -ENOMEM;
rc = -ENOMEM;
goto err_alloc;
}
/* Construct data IU frame */
memcpy ( iob_put ( iobuf, len ),
( command->data_out + fcpcmd->offset ), len );
if ( ( rc = xferbuf_read ( &command->data_out, fcpcmd->offset,
iob_put ( iobuf, len ), len ) ) != 0 )
goto err_read;
memset ( &meta, 0, sizeof ( meta ) );
meta.flags = ( XFER_FL_RESPONSE | XFER_FL_ABS_OFFSET );
meta.offset = fcpcmd->offset;
@@ -477,7 +472,7 @@ static int fcpcmd_send_wrdata ( struct fcp_command *fcpcmd ) {
assert ( len <= fcpcmd->remaining );
fcpcmd->offset += len;
fcpcmd->remaining -= len;
assert ( fcpcmd->offset <= command->data_out_len );
assert ( fcpcmd->offset <= command->data_out.len );
if ( fcpcmd->remaining == 0 ) {
fcpcmd_stop_send ( fcpcmd );
meta.flags |= XFER_FL_OVER;
@@ -488,10 +483,17 @@ static int fcpcmd_send_wrdata ( struct fcp_command *fcpcmd ) {
&meta ) ) != 0 ) {
DBGC ( fcpdev, "FCP %p xchg %04x cannot deliver write data "
"IU: %s\n", fcpdev, fcpcmd->xchg_id, strerror ( rc ) );
return rc;
goto err_deliver;
}
return 0;
err_deliver:
err_read:
free_iob ( iobuf );
err_alloc:
err_sanity:
return rc;
}
/**
@@ -590,11 +592,11 @@ static int fcpcmd_recv_rsp ( struct fcp_command *fcpcmd,
/* Check for locally-detected command underrun */
if ( ( rsp->status == 0 ) &&
( fcpcmd->offset != ( command->data_in_len +
command->data_out_len ) ) ) {
( fcpcmd->offset != ( command->data_in.len +
command->data_out.len ) ) ) {
DBGC ( fcpdev, "FCP %p xchg %04x data underrun (expected %zd, "
"got %zd)\n", fcpdev, fcpcmd->xchg_id,
( command->data_in_len + command->data_out_len ),
( command->data_in.len + command->data_out.len ),
fcpcmd->offset );
rc = -ERANGE_DATA_UNDERRUN;
goto done;
+41 -23
View File
@@ -377,32 +377,32 @@ static void iscsi_scsi_done ( struct iscsi_session *iscsi, int rc,
static void iscsi_start_command ( struct iscsi_session *iscsi ) {
struct iscsi_bhs_scsi_command *command = &iscsi->tx_bhs.scsi_command;
assert ( ! ( iscsi->command->data_in && iscsi->command->data_out ) );
assert ( ! ( iscsi->command->data_in.len &&
iscsi->command->data_out.len ) );
/* Construct BHS and initiate transmission */
iscsi_start_tx ( iscsi );
command->opcode = ISCSI_OPCODE_SCSI_COMMAND;
command->flags = ( ISCSI_FLAG_FINAL |
ISCSI_COMMAND_ATTR_SIMPLE );
if ( iscsi->command->data_in )
if ( iscsi->command->data_in.len )
command->flags |= ISCSI_COMMAND_FLAG_READ;
if ( iscsi->command->data_out )
if ( iscsi->command->data_out.len )
command->flags |= ISCSI_COMMAND_FLAG_WRITE;
/* lengths left as zero */
memcpy ( &command->lun, &iscsi->command->lun,
sizeof ( command->lun ) );
command->itt = htonl ( iscsi->itt );
command->exp_len = htonl ( iscsi->command->data_in_len |
iscsi->command->data_out_len );
command->exp_len = htonl ( iscsi->command->data_in.len |
iscsi->command->data_out.len );
command->cmdsn = htonl ( iscsi->cmdsn );
command->expstatsn = htonl ( iscsi->statsn + 1 );
memcpy ( &command->cdb, &iscsi->command->cdb, sizeof ( command->cdb ));
DBGC2 ( iscsi, "iSCSI %p start " SCSI_CDB_FORMAT " %s %#zx\n",
iscsi, SCSI_CDB_DATA ( command->cdb ),
( iscsi->command->data_in ? "in" : "out" ),
( iscsi->command->data_in ?
iscsi->command->data_in_len :
iscsi->command->data_out_len ) );
( iscsi->command->data_in.len ? "in" : "out" ),
( iscsi->command->data_in.len +
iscsi->command->data_out.len ) );
}
/**
@@ -472,13 +472,14 @@ static int iscsi_rx_data_in ( struct iscsi_session *iscsi,
size_t remaining ) {
struct iscsi_bhs_data_in *data_in = &iscsi->rx_bhs.data_in;
unsigned long offset;
int rc;
/* Copy data to data-in buffer */
offset = ntohl ( data_in->offset ) + iscsi->rx_offset;
assert ( iscsi->command != NULL );
assert ( iscsi->command->data_in );
assert ( ( offset + len ) <= iscsi->command->data_in_len );
memcpy ( ( iscsi->command->data_in + offset ), data, len );
if ( ( rc = xferbuf_write ( &iscsi->command->data_in, offset,
data, len ) ) != 0 )
return rc;
/* Wait for whole SCSI response to arrive */
if ( remaining )
@@ -486,7 +487,7 @@ static int iscsi_rx_data_in ( struct iscsi_session *iscsi,
/* Mark as completed if status is present */
if ( data_in->flags & ISCSI_DATA_FLAG_STATUS ) {
assert ( ( offset + len ) == iscsi->command->data_in_len );
assert ( ( offset + len ) == iscsi->command->data_in.len );
assert ( data_in->flags & ISCSI_FLAG_FINAL );
/* iSCSI cannot return an error status via a data-in */
iscsi_scsi_done ( iscsi, 0, NULL );
@@ -585,24 +586,41 @@ static int iscsi_tx_data_out ( struct iscsi_session *iscsi ) {
unsigned long offset;
size_t len;
size_t pad_len;
int rc;
/* Calculate offset and lengths */
offset = ntohl ( data_out->offset );
len = ISCSI_DATA_LEN ( data_out->lengths );
pad_len = ISCSI_DATA_PAD_LEN ( data_out->lengths );
assert ( iscsi->command != NULL );
assert ( iscsi->command->data_out );
assert ( ( offset + len ) <= iscsi->command->data_out_len );
/* Allocate I/O buffer */
iobuf = xfer_alloc_iob ( &iscsi->socket, ( len + pad_len ) );
if ( ! iobuf )
return -ENOMEM;
memcpy ( iob_put ( iobuf, len ),
( iscsi->command->data_out + offset ), len );
if ( ! iobuf ) {
rc = -ENOMEM;
goto err_alloc;
}
/* Copy data to I/O buffer */
assert ( iscsi->command != NULL );
if ( ( rc = xferbuf_read ( &iscsi->command->data_out, offset,
iob_put ( iobuf, len ), len ) ) != 0 ) {
goto err_read;
}
memset ( iob_put ( iobuf, pad_len ), 0, pad_len );
return xfer_deliver_iob ( &iscsi->socket, iobuf );
/* Deliver I/O buffer */
if ( ( rc = xfer_deliver_iob ( &iscsi->socket,
iob_disown ( iobuf ) ) ) != 0 ) {
goto err_deliver;
}
return 0;
err_deliver:
err_read:
free_iob ( iobuf );
err_alloc:
return rc;
}
/**