summaryrefslogtreecommitdiffstats
path: root/src/drivers/block/srp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/block/srp.c')
-rw-r--r--src/drivers/block/srp.c830
1 files changed, 553 insertions, 277 deletions
diff --git a/src/drivers/block/srp.c b/src/drivers/block/srp.c
index a8deab15d..4b592e95c 100644
--- a/src/drivers/block/srp.c
+++ b/src/drivers/block/srp.c
@@ -36,7 +36,6 @@ FILE_LICENCE ( BSD2 );
#include <ipxe/scsi.h>
#include <ipxe/xfer.h>
#include <ipxe/features.h>
-#include <ipxe/ib_srp.h>
#include <ipxe/srp.h>
/**
@@ -48,334 +47,555 @@ FILE_LICENCE ( BSD2 );
FEATURE ( FEATURE_PROTOCOL, "SRP", DHCP_EB_FEATURE_SRP, 1 );
-/** Tag to be used for next SRP IU */
-static unsigned int srp_tag = 0;
+/** Maximum length of any initiator-to-target IU that we will send
+ *
+ * The longest IU is a SRP_CMD with no additional CDB and two direct
+ * data buffer descriptors, which comes to 80 bytes.
+ */
+#define SRP_MAX_I_T_IU_LEN 80
+
+/** An SRP device */
+struct srp_device {
+ /** Reference count */
+ struct refcnt refcnt;
+
+ /** SCSI command issuing interface */
+ struct interface scsi;
+ /** Underlying data transfer interface */
+ struct interface socket;
+
+ /** RDMA memory handle */
+ uint32_t memory_handle;
+ /** Login completed successfully */
+ int logged_in;
+
+ /** Initiator port ID (for boot firmware table) */
+ union srp_port_id initiator;
+ /** Target port ID (for boot firmware table) */
+ union srp_port_id target;
+ /** SCSI LUN (for boot firmware table) */
+ struct scsi_lun lun;
+
+ /** List of active commands */
+ struct list_head commands;
+};
+
+/** An SRP command */
+struct srp_command {
+ /** Reference count */
+ struct refcnt refcnt;
+ /** SRP device */
+ struct srp_device *srpdev;
+ /** List of active commands */
+ struct list_head list;
+
+ /** SCSI command interface */
+ struct interface scsi;
+ /** Command tag */
+ uint32_t tag;
+};
+
+/**
+ * Get reference to SRP device
+ *
+ * @v srpdev SRP device
+ * @ret srpdev SRP device
+ */
+static inline __attribute__ (( always_inline )) struct srp_device *
+srpdev_get ( struct srp_device *srpdev ) {
+ ref_get ( &srpdev->refcnt );
+ return srpdev;
+}
-static void srp_login ( struct srp_device *srp );
-static void srp_cmd ( struct srp_device *srp );
+/**
+ * Drop reference to SRP device
+ *
+ * @v srpdev SRP device
+ */
+static inline __attribute__ (( always_inline )) void
+srpdev_put ( struct srp_device *srpdev ) {
+ ref_put ( &srpdev->refcnt );
+}
+
+/**
+ * Get reference to SRP command
+ *
+ * @v srpcmd SRP command
+ * @ret srpcmd SRP command
+ */
+static inline __attribute__ (( always_inline )) struct srp_command *
+srpcmd_get ( struct srp_command *srpcmd ) {
+ ref_get ( &srpcmd->refcnt );
+ return srpcmd;
+}
/**
- * Mark SRP SCSI command as complete
+ * Drop reference to SRP command
*
- * @v srp SRP device
- * @v rc Status code
+ * @v srpcmd SRP command
*/
-static void srp_scsi_done ( struct srp_device *srp, int rc ) {
- if ( srp->command )
- srp->command->rc = rc;
- srp->command = NULL;
+static inline __attribute__ (( always_inline )) void
+srpcmd_put ( struct srp_command *srpcmd ) {
+ ref_put ( &srpcmd->refcnt );
}
/**
- * Handle SRP session failure
+ * Free SRP command
*
- * @v srp SRP device
- * @v rc Reason for failure
+ * @v refcnt Reference count
*/
-static void srp_fail ( struct srp_device *srp, int rc ) {
+static void srpcmd_free ( struct refcnt *refcnt ) {
+ struct srp_command *srpcmd =
+ container_of ( refcnt, struct srp_command, refcnt );
- /* Close underlying socket */
- intf_restart ( &srp->socket, rc );
+ assert ( list_empty ( &srpcmd->list ) );
- /* Clear session state */
- srp->state = 0;
+ srpdev_put ( srpcmd->srpdev );
+ free ( srpcmd );
+}
- /* If we have reached the retry limit, report the failure */
- if ( srp->retry_count >= SRP_MAX_RETRIES ) {
- srp_scsi_done ( srp, rc );
- return;
+/**
+ * Close SRP command
+ *
+ * @v srpcmd SRP command
+ * @v rc Reason for close
+ */
+static void srpcmd_close ( struct srp_command *srpcmd, int rc ) {
+ struct srp_device *srpdev = srpcmd->srpdev;
+
+ if ( rc != 0 ) {
+ DBGC ( srpdev, "SRP %p tag %08x closed: %s\n",
+ srpdev, srpcmd->tag, strerror ( rc ) );
}
- /* Otherwise, increment the retry count and try to reopen the
- * connection
- */
- srp->retry_count++;
- srp_login ( srp );
+ /* Remove from list of commands */
+ if ( ! list_empty ( &srpcmd->list ) ) {
+ list_del ( &srpcmd->list );
+ INIT_LIST_HEAD ( &srpcmd->list );
+ srpcmd_put ( srpcmd );
+ }
+
+ /* Shut down interfaces */
+ intf_shutdown ( &srpcmd->scsi, rc );
}
/**
- * Initiate SRP login
+ * Close SRP device
*
- * @v srp SRP device
+ * @v srpdev SRP device
+ * @v rc Reason for close
*/
-static void srp_login ( struct srp_device *srp ) {
- struct io_buffer *iobuf;
- struct srp_login_req *login_req;
- int rc;
+static void srpdev_close ( struct srp_device *srpdev, int rc ) {
+ struct srp_command *srpcmd;
+ struct srp_command *tmp;
- assert ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) );
+ if ( rc != 0 ) {
+ DBGC ( srpdev, "SRP %p closed: %s\n",
+ srpdev, strerror ( rc ) );
+ }
- /* Open underlying socket */
- if ( ( rc = srp->transport->connect ( srp ) ) != 0 ) {
- DBGC ( srp, "SRP %p could not open socket: %s\n",
- srp, strerror ( rc ) );
- goto err;
+ /* Shut down interfaces */
+ intf_shutdown ( &srpdev->socket, rc );
+ intf_shutdown ( &srpdev->scsi, rc );
+
+ /* Shut down any active commands */
+ list_for_each_entry_safe ( srpcmd, tmp, &srpdev->commands, list ) {
+ srpcmd_get ( srpcmd );
+ srpcmd_close ( srpcmd, rc );
+ srpcmd_put ( srpcmd );
}
- srp->state |= SRP_STATE_SOCKET_OPEN;
+}
- /* Allocate I/O buffer */
- iobuf = xfer_alloc_iob ( &srp->socket, sizeof ( *login_req ) );
- if ( ! iobuf ) {
- rc = -ENOMEM;
- goto err;
+/**
+ * Identify SRP command by tag
+ *
+ * @v srpdev SRP device
+ * @v tag Command tag
+ * @ret srpcmd SRP command, or NULL
+ */
+static struct srp_command * srp_find_tag ( struct srp_device *srpdev,
+ uint32_t tag ) {
+ struct srp_command *srpcmd;
+
+ list_for_each_entry ( srpcmd, &srpdev->commands, list ) {
+ if ( srpcmd->tag == tag )
+ return srpcmd;
+ }
+ return NULL;
+}
+
+/**
+ * Choose an SRP command tag
+ *
+ * @v srpdev SRP device
+ * @ret tag New tag, or negative error
+ */
+static int srp_new_tag ( struct srp_device *srpdev ) {
+ static uint16_t tag_idx;
+ unsigned int i;
+
+ for ( i = 0 ; i < 65536 ; i++ ) {
+ tag_idx++;
+ if ( srp_find_tag ( srpdev, tag_idx ) == NULL )
+ return tag_idx;
}
+ return -EADDRINUSE;
+}
+
+/**
+ * Transmit SRP login request
+ *
+ * @v srpdev SRP device
+ * @v initiator Initiator port ID
+ * @v target Target port ID
+ * @v tag Command tag
+ * @ret rc Return status code
+ */
+static int srp_login ( struct srp_device *srpdev, union srp_port_id *initiator,
+ union srp_port_id *target, uint32_t tag ) {
+ struct io_buffer *iobuf;
+ struct srp_login_req *login_req;
+ int rc;
+
+ /* Allocate I/O buffer */
+ iobuf = xfer_alloc_iob ( &srpdev->socket, sizeof ( *login_req ) );
+ if ( ! iobuf )
+ return -ENOMEM;
/* Construct login request IU */
login_req = iob_put ( iobuf, sizeof ( *login_req ) );
memset ( login_req, 0, sizeof ( *login_req ) );
login_req->type = SRP_LOGIN_REQ;
- login_req->tag.dwords[1] = htonl ( ++srp_tag );
+ login_req->tag.dwords[0] = htonl ( SRP_TAG_MAGIC );
+ login_req->tag.dwords[1] = htonl ( tag );
login_req->max_i_t_iu_len = htonl ( SRP_MAX_I_T_IU_LEN );
login_req->required_buffer_formats = SRP_LOGIN_REQ_FMT_DDBD;
- memcpy ( &login_req->port_ids, &srp->port_ids,
- sizeof ( login_req->port_ids ) );
+ memcpy ( &login_req->initiator, initiator,
+ sizeof ( login_req->initiator ) );
+ memcpy ( &login_req->target, target, sizeof ( login_req->target ) );
- DBGC2 ( srp, "SRP %p TX login request tag %08x%08x\n",
- srp, ntohl ( login_req->tag.dwords[0] ),
- ntohl ( login_req->tag.dwords[1] ) );
- DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) );
+ DBGC ( srpdev, "SRP %p tag %08x LOGIN_REQ:\n", srpdev, tag );
+ DBGC_HDA ( srpdev, 0, iobuf->data, iob_len ( iobuf ) );
/* Send login request IU */
- if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) {
- DBGC ( srp, "SRP %p could not send login request: %s\n",
- srp, strerror ( rc ) );
- goto err;
+ if ( ( rc = xfer_deliver_iob ( &srpdev->socket, iobuf ) ) != 0 ) {
+ DBGC ( srpdev, "SRP %p tag %08x could not send LOGIN_REQ: "
+ "%s\n", srpdev, tag, strerror ( rc ) );
+ return rc;
}
- return;
-
- err:
- srp_fail ( srp, rc );
+ return 0;
}
/**
- * Handle SRP login response
+ * Receive SRP login response
*
- * @v srp SRP device
- * @v iobuf I/O buffer
+ * @v srpdev SRP device
+ * @v data SRP IU
+ * @v len Length of SRP IU
* @ret rc Return status code
*/
-static int srp_login_rsp ( struct srp_device *srp, struct io_buffer *iobuf ) {
- struct srp_login_rsp *login_rsp = iobuf->data;
- int rc;
-
- DBGC2 ( srp, "SRP %p RX login response tag %08x%08x\n",
- srp, ntohl ( login_rsp->tag.dwords[0] ),
- ntohl ( login_rsp->tag.dwords[1] ) );
+static int srp_login_rsp ( struct srp_device *srpdev,
+ const void *data, size_t len ) {
+ const struct srp_login_rsp *login_rsp = data;
/* Sanity check */
- if ( iob_len ( iobuf ) < sizeof ( *login_rsp ) ) {
- DBGC ( srp, "SRP %p RX login response too short (%zd bytes)\n",
- srp, iob_len ( iobuf ) );
- rc = -EINVAL;
- goto out;
+ if ( len < sizeof ( *login_rsp ) ) {
+ DBGC ( srpdev, "SRP %p LOGIN_RSP too short (%zd bytes)\n",
+ srpdev, len );
+ return -EINVAL;
}
-
- DBGC ( srp, "SRP %p logged in\n", srp );
+ DBGC ( srpdev, "SRP %p tag %08x LOGIN_RSP:\n",
+ srpdev, ntohl ( login_rsp->tag.dwords[1] ) );
+ DBGC_HDA ( srpdev, 0, data, len );
/* Mark as logged in */
- srp->state |= SRP_STATE_LOGGED_IN;
+ srpdev->logged_in = 1;
+ DBGC ( srpdev, "SRP %p logged in\n", srpdev );
- /* Reset error counter */
- srp->retry_count = 0;
+ /* Notify of window change */
+ xfer_window_changed ( &srpdev->scsi );
- /* Issue pending command */
- srp_cmd ( srp );
-
- rc = 0;
- out:
- free_iob ( iobuf );
- return rc;
+ return 0;
}
/**
- * Handle SRP login rejection
+ * Receive SRP login rejection
*
- * @v srp SRP device
- * @v iobuf I/O buffer
+ * @v srpdev SRP device
+ * @v data SRP IU
+ * @v len Length of SRP IU
* @ret rc Return status code
*/
-static int srp_login_rej ( struct srp_device *srp, struct io_buffer *iobuf ) {
- struct srp_login_rej *login_rej = iobuf->data;
- int rc;
-
- DBGC2 ( srp, "SRP %p RX login rejection tag %08x%08x\n",
- srp, ntohl ( login_rej->tag.dwords[0] ),
- ntohl ( login_rej->tag.dwords[1] ) );
+static int srp_login_rej ( struct srp_device *srpdev,
+ const void *data, size_t len ) {
+ const struct srp_login_rej *login_rej = data;
/* Sanity check */
- if ( iob_len ( iobuf ) < sizeof ( *login_rej ) ) {
- DBGC ( srp, "SRP %p RX login rejection too short (%zd "
- "bytes)\n", srp, iob_len ( iobuf ) );
- rc = -EINVAL;
- goto out;
+ if ( len < sizeof ( *login_rej ) ) {
+ DBGC ( srpdev, "SRP %p LOGIN_REJ too short (%zd bytes)\n",
+ srpdev, len );
+ return -EINVAL;
}
+ DBGC ( srpdev, "SRP %p tag %08x LOGIN_REJ:\n",
+ srpdev, ntohl ( login_rej->tag.dwords[1] ) );
+ DBGC_HDA ( srpdev, 0, data, len );
/* Login rejection always indicates an error */
- DBGC ( srp, "SRP %p login rejected (reason %08x)\n",
- srp, ntohl ( login_rej->reason ) );
- rc = -EPERM;
-
- out:
- free_iob ( iobuf );
- return rc;
+ DBGC ( srpdev, "SRP %p login rejected (reason %08x)\n",
+ srpdev, ntohl ( login_rej->reason ) );
+ return -EPERM;
}
/**
* Transmit SRP SCSI command
*
- * @v srp SRP device
+ * @v srpdev SRP device
+ * @v command SCSI command
+ * @v tag Command tag
+ * @ret rc Return status code
*/
-static void srp_cmd ( struct srp_device *srp ) {
+static int srp_cmd ( struct srp_device *srpdev,
+ struct scsi_cmd *command,
+ uint32_t tag ) {
struct io_buffer *iobuf;
struct srp_cmd *cmd;
struct srp_memory_descriptor *data_out;
struct srp_memory_descriptor *data_in;
int rc;
- assert ( srp->state & SRP_STATE_LOGGED_IN );
+ /* Sanity check */
+ if ( ! srpdev->logged_in ) {
+ DBGC ( srpdev, "SRP %p tag %08x cannot send CMD before "
+ "login completes\n", srpdev, tag );
+ return -EBUSY;
+ }
/* Allocate I/O buffer */
- iobuf = xfer_alloc_iob ( &srp->socket, SRP_MAX_I_T_IU_LEN );
- if ( ! iobuf ) {
- rc = -ENOMEM;
- goto err;
- }
+ iobuf = xfer_alloc_iob ( &srpdev->socket, SRP_MAX_I_T_IU_LEN );
+ if ( ! iobuf )
+ return -ENOMEM;
/* Construct base portion */
cmd = iob_put ( iobuf, sizeof ( *cmd ) );
memset ( cmd, 0, sizeof ( *cmd ) );
cmd->type = SRP_CMD;
- cmd->tag.dwords[1] = htonl ( ++srp_tag );
- cmd->lun = srp->lun;
- memcpy ( &cmd->cdb, &srp->command->cdb, sizeof ( cmd->cdb ) );
+ cmd->tag.dwords[0] = htonl ( SRP_TAG_MAGIC );
+ cmd->tag.dwords[1] = htonl ( tag );
+ memcpy ( &cmd->lun, &command->lun, sizeof ( cmd->lun ) );
+ memcpy ( &cmd->cdb, &command->cdb, sizeof ( cmd->cdb ) );
/* Construct data-out descriptor, if present */
- if ( srp->command->data_out ) {
+ if ( command->data_out ) {
cmd->data_buffer_formats |= SRP_CMD_DO_FMT_DIRECT;
data_out = iob_put ( iobuf, sizeof ( *data_out ) );
data_out->address =
- cpu_to_be64 ( user_to_phys ( srp->command->data_out, 0 ) );
- data_out->handle = ntohl ( srp->memory_handle );
- data_out->len = ntohl ( srp->command->data_out_len );
+ cpu_to_be64 ( user_to_phys ( command->data_out, 0 ) );
+ data_out->handle = ntohl ( srpdev->memory_handle );
+ data_out->len = ntohl ( command->data_out_len );
}
/* Construct data-in descriptor, if present */
- if ( srp->command->data_in ) {
+ if ( command->data_in ) {
cmd->data_buffer_formats |= SRP_CMD_DI_FMT_DIRECT;
data_in = iob_put ( iobuf, sizeof ( *data_in ) );
data_in->address =
- cpu_to_be64 ( user_to_phys ( srp->command->data_in, 0 ) );
- data_in->handle = ntohl ( srp->memory_handle );
- data_in->len = ntohl ( srp->command->data_in_len );
+ cpu_to_be64 ( user_to_phys ( command->data_in, 0 ) );
+ data_in->handle = ntohl ( srpdev->memory_handle );
+ data_in->len = ntohl ( command->data_in_len );
}
- DBGC2 ( srp, "SRP %p TX SCSI command tag %08x%08x\n", srp,
- ntohl ( cmd->tag.dwords[0] ), ntohl ( cmd->tag.dwords[1] ) );
- DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) );
+ DBGC2 ( srpdev, "SRP %p tag %08x CMD " SCSI_CDB_FORMAT "\n",
+ srpdev, tag, SCSI_CDB_DATA ( cmd->cdb ) );
/* Send IU */
- if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) {
- DBGC ( srp, "SRP %p could not send command: %s\n",
- srp, strerror ( rc ) );
- goto err;
+ if ( ( rc = xfer_deliver_iob ( &srpdev->socket, iobuf ) ) != 0 ) {
+ DBGC ( srpdev, "SRP %p tag %08x could not send CMD: %s\n",
+ srpdev, tag, strerror ( rc ) );
+ return rc;
}
- return;
-
- err:
- srp_fail ( srp, rc );
+ return 0;
}
/**
- * Handle SRP SCSI response
+ * Receive SRP SCSI response
*
- * @v srp SRP device
- * @v iobuf I/O buffer
+ * @v srpdev SRP device
+ * @v data SRP IU
+ * @v len Length of SRP IU
* @ret rc Returns status code
*/
-static int srp_rsp ( struct srp_device *srp, struct io_buffer *iobuf ) {
- struct srp_rsp *rsp = iobuf->data;
- int rc;
-
- DBGC2 ( srp, "SRP %p RX SCSI response tag %08x%08x\n", srp,
- ntohl ( rsp->tag.dwords[0] ), ntohl ( rsp->tag.dwords[1] ) );
+static int srp_rsp ( struct srp_device *srpdev,
+ const void *data, size_t len ) {
+ const struct srp_rsp *rsp = data;
+ struct srp_command *srpcmd;
+ struct scsi_rsp response;
+ const void *sense;
+ ssize_t data_out_residual_count;
+ ssize_t data_in_residual_count;
/* Sanity check */
- if ( iob_len ( iobuf ) < sizeof ( *rsp ) ) {
- DBGC ( srp, "SRP %p RX SCSI response too short (%zd bytes)\n",
- srp, iob_len ( iobuf ) );
- rc = -EINVAL;
- goto out;
+ if ( len < sizeof ( *rsp ) ) {
+ DBGC ( srpdev, "SRP %p RSP too short (%zd bytes)\n",
+ srpdev, len );
+ return -EINVAL;
}
-
- /* Report SCSI errors */
- if ( rsp->status != 0 ) {
- DBGC ( srp, "SRP %p response status %02x\n",
- srp, rsp->status );
- if ( srp_rsp_sense_data ( rsp ) ) {
- DBGC ( srp, "SRP %p sense data:\n", srp );
- DBGC_HDA ( srp, 0, srp_rsp_sense_data ( rsp ),
- srp_rsp_sense_data_len ( rsp ) );
- }
+ DBGC2 ( srpdev, "SRP %p tag %08x RSP stat %02x dores %08x dires "
+ "%08x valid %02x%s%s%s%s%s%s\n",
+ srpdev, ntohl ( rsp->tag.dwords[1] ), rsp->status,
+ ntohl ( rsp->data_out_residual_count ),
+ ntohl ( rsp->data_in_residual_count ), rsp->valid,
+ ( ( rsp->valid & SRP_RSP_VALID_DIUNDER ) ? " diunder" : "" ),
+ ( ( rsp->valid & SRP_RSP_VALID_DIOVER ) ? " diover" : "" ),
+ ( ( rsp->valid & SRP_RSP_VALID_DOUNDER ) ? " dounder" : "" ),
+ ( ( rsp->valid & SRP_RSP_VALID_DOOVER ) ? " doover" : "" ),
+ ( ( rsp->valid & SRP_RSP_VALID_SNSVALID ) ? " sns" : "" ),
+ ( ( rsp->valid & SRP_RSP_VALID_RSPVALID ) ? " rsp" : "" ) );
+
+ /* Identify command by tag */
+ srpcmd = srp_find_tag ( srpdev, ntohl ( rsp->tag.dwords[1] ) );
+ if ( ! srpcmd ) {
+ DBGC ( srpdev, "SRP %p tag %08x unrecognised RSP\n",
+ srpdev, ntohl ( rsp->tag.dwords[1] ) );
+ return -ENOENT;
}
- if ( rsp->valid & ( SRP_RSP_VALID_DOUNDER | SRP_RSP_VALID_DOOVER ) ) {
- DBGC ( srp, "SRP %p response data-out %srun by %#x bytes\n",
- srp, ( ( rsp->valid & SRP_RSP_VALID_DOUNDER )
- ? "under" : "over" ),
- ntohl ( rsp->data_out_residual_count ) );
- }
- if ( rsp->valid & ( SRP_RSP_VALID_DIUNDER | SRP_RSP_VALID_DIOVER ) ) {
- DBGC ( srp, "SRP %p response data-in %srun by %#x bytes\n",
- srp, ( ( rsp->valid & SRP_RSP_VALID_DIUNDER )
- ? "under" : "over" ),
- ntohl ( rsp->data_in_residual_count ) );
+
+ /* Hold command reference for remainder of function */
+ srpcmd_get ( srpcmd );
+
+ /* Build SCSI response */
+ memset ( &response, 0, sizeof ( response ) );
+ response.status = rsp->status;
+ data_out_residual_count = ntohl ( rsp->data_out_residual_count );
+ data_in_residual_count = ntohl ( rsp->data_in_residual_count );
+ if ( rsp->valid & SRP_RSP_VALID_DOOVER ) {
+ response.overrun = data_out_residual_count;
+ } else if ( rsp->valid & SRP_RSP_VALID_DOUNDER ) {
+ response.overrun = -(data_out_residual_count);
+ } else if ( rsp->valid & SRP_RSP_VALID_DIOVER ) {
+ response.overrun = data_in_residual_count;
+ } else if ( rsp->valid & SRP_RSP_VALID_DIUNDER ) {
+ response.overrun = -(data_in_residual_count);
}
- srp->command->status = rsp->status;
+ sense = srp_rsp_sense_data ( rsp );
+ if ( sense )
+ memcpy ( &response.sense, sense, sizeof ( response.sense ) );
- /* Mark SCSI command as complete */
- srp_scsi_done ( srp, 0 );
+ /* Report SCSI response */
+ scsi_response ( &srpcmd->scsi, &response );
- rc = 0;
- out:
- free_iob ( iobuf );
- return rc;
+ /* Close SCSI command */
+ srpcmd_close ( srpcmd, 0 );
+
+ /* Drop temporary command reference */
+ srpcmd_put ( srpcmd );
+
+ return 0;
}
/**
- * Handle SRP unrecognised response
+ * Receive SRP unrecognised response IU
*
- * @v srp SRP device
- * @v iobuf I/O buffer
+ * @v srpdev SRP device
+ * @v data SRP IU
+ * @v len Length of SRP IU
* @ret rc Returns status code
*/
-static int srp_unrecognised ( struct srp_device *srp,
- struct io_buffer *iobuf ) {
- struct srp_common *common = iobuf->data;
+static int srp_unrecognised ( struct srp_device *srpdev,
+ const void *data, size_t len ) {
+ const struct srp_common *common = data;
- DBGC ( srp, "SRP %p RX unrecognised IU tag %08x%08x type %02x\n",
- srp, ntohl ( common->tag.dwords[0] ),
- ntohl ( common->tag.dwords[1] ), common->type );
+ DBGC ( srpdev, "SRP %p tag %08x unrecognised IU type %02x:\n",
+ srpdev, ntohl ( common->tag.dwords[1] ), common->type );
+ DBGC_HDA ( srpdev, 0, data, len );
- free_iob ( iobuf );
return -ENOTSUP;
}
+/** SRP command SCSI interface operations */
+static struct interface_operation srpcmd_scsi_op[] = {
+ INTF_OP ( intf_close, struct srp_command *, srpcmd_close ),
+};
+
+/** SRP command SCSI interface descriptor */
+static struct interface_descriptor srpcmd_scsi_desc =
+ INTF_DESC ( struct srp_command, scsi, srpcmd_scsi_op );
+
+/**
+ * Issue SRP SCSI command
+ *
+ * @v srpdev SRP device
+ * @v parent Parent interface
+ * @v command SCSI command
+ * @ret tag Command tag, or negative error
+ */
+static int srpdev_scsi_command ( struct srp_device *srpdev,
+ struct interface *parent,
+ struct scsi_cmd *command ) {
+ struct srp_command *srpcmd;
+ int tag;
+ int rc;
+
+ /* Allocate command tag */
+ tag = srp_new_tag ( srpdev );
+ if ( tag < 0 ) {
+ rc = tag;
+ goto err_tag;
+ }
+
+ /* Allocate and initialise structure */
+ srpcmd = zalloc ( sizeof ( *srpcmd ) );
+ if ( ! srpcmd ) {
+ rc = -ENOMEM;
+ goto err_zalloc;
+ }
+ ref_init ( &srpcmd->refcnt, srpcmd_free );
+ intf_init ( &srpcmd->scsi, &srpcmd_scsi_desc, &srpcmd->refcnt );
+ srpcmd->srpdev = srpdev_get ( srpdev );
+ list_add ( &srpcmd->list, &srpdev->commands );
+ srpcmd->tag = tag;
+
+ /* Send command IU */
+ if ( ( rc = srp_cmd ( srpdev, command, srpcmd->tag ) ) != 0 )
+ goto err_cmd;
+
+ /* Attach to parent interface, leave reference with command
+ * list, and return.
+ */
+ intf_plug_plug ( &srpcmd->scsi, parent );
+ return srpcmd->tag;
+
+ err_cmd:
+ srpcmd_close ( srpcmd, rc );
+ err_zalloc:
+ err_tag:
+ return rc;
+}
+
/**
- * Receive data from underlying socket
+ * Receive data from SRP socket
*
- * @v srp SRP device
+ * @v srpdev SRP device
* @v iobuf Datagram I/O buffer
* @v meta Data transfer metadata
* @ret rc Return status code
*/
-static int srp_xfer_deliver ( struct srp_device *srp,
- struct io_buffer *iobuf,
- struct xfer_metadata *meta __unused ) {
+static int srpdev_deliver ( struct srp_device *srpdev,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta __unused ) {
struct srp_common *common = iobuf->data;
- int ( * type ) ( struct srp_device *srp, struct io_buffer *iobuf );
+ int ( * type ) ( struct srp_device *srp, const void *data, size_t len );
int rc;
+ /* Sanity check */
+ if ( iob_len ( iobuf ) < sizeof ( *common ) ) {
+ DBGC ( srpdev, "SRP %p IU too short (%zd bytes)\n",
+ srpdev, iob_len ( iobuf ) );
+ rc = -EINVAL;
+ goto err;
+ }
+
/* Determine IU type */
switch ( common->type ) {
case SRP_LOGIN_RSP:
@@ -393,114 +613,170 @@ static int srp_xfer_deliver ( struct srp_device *srp,
}
/* Handle IU */
- if ( ( rc = type ( srp, iobuf ) ) != 0 )
+ if ( ( rc = type ( srpdev, iobuf->data, iob_len ( iobuf ) ) ) != 0 )
goto err;
+ free_iob ( iobuf );
return 0;
err:
- srp_fail ( srp, rc );
+ DBGC ( srpdev, "SRP %p closing due to received IU (%s):\n",
+ srpdev, strerror ( rc ) );
+ DBGC_HDA ( srpdev, 0, iobuf->data, iob_len ( iobuf ) );
+ free_iob ( iobuf );
+ srpdev_close ( srpdev, rc );
return rc;
}
-/** SRP data transfer interface operations */
-static struct interface_operation srp_xfer_operations[] = {
- INTF_OP ( xfer_deliver, struct srp_device *, srp_xfer_deliver ),
- INTF_OP ( intf_close, struct srp_device *, srp_fail ),
-};
+/**
+ * Check SRP device flow-control window
+ *
+ * @v srpdev SRP device
+ * @ret len Length of window
+ */
+static size_t srpdev_window ( struct srp_device *srpdev ) {
+ return ( srpdev->logged_in ? ~( ( size_t ) 0 ) : 0 );
+}
-/** SRP data transfer interface descriptor */
-static struct interface_descriptor srp_xfer_desc =
- INTF_DESC ( struct srp_device, socket, srp_xfer_operations );
+/**
+ * A (transport-independent) sBFT created by iPXE
+ */
+struct ipxe_sbft {
+ /** The table header */
+ struct sbft_table table;
+ /** The SCSI subtable */
+ struct sbft_scsi_subtable scsi;
+ /** The SRP subtable */
+ struct sbft_srp_subtable srp;
+} __attribute__ (( packed, aligned ( 16 ) ));
/**
- * Issue SCSI command via SRP
+ * Describe SRP device in an ACPI table
*
- * @v scsi SCSI device
- * @v command SCSI command
+ * @v srpdev SRP device
+ * @v acpi ACPI table
+ * @v len Length of ACPI table
* @ret rc Return status code
*/
-static int srp_command ( struct scsi_device *scsi,
- struct scsi_command *command ) {
- struct srp_device *srp =
- container_of ( scsi->backend, struct srp_device, refcnt );
-
- /* Store SCSI command */
- if ( srp->command ) {
- DBGC ( srp, "SRP %p cannot handle concurrent SCSI commands\n",
- srp );
- return -EBUSY;
- }
- srp->command = command;
-
- /* Log in or issue command as appropriate */
- if ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) ) {
- srp_login ( srp );
- } else if ( srp->state & SRP_STATE_LOGGED_IN ) {
- srp_cmd ( srp );
- } else {
- /* Still waiting for login; do nothing */
+static int srpdev_describe ( struct srp_device *srpdev,
+ struct acpi_description_header *acpi,
+ size_t len ) {
+ struct ipxe_sbft *sbft =
+ container_of ( acpi, struct ipxe_sbft, table.acpi );
+ int rc;
+
+ /* Sanity check */
+ if ( len < sizeof ( *sbft ) )
+ return -ENOBUFS;
+
+ /* Populate table */
+ sbft->table.acpi.signature = cpu_to_le32 ( SBFT_SIG );
+ sbft->table.acpi.length = cpu_to_le32 ( sizeof ( *sbft ) );
+ sbft->table.acpi.revision = 1;
+ sbft->table.scsi_offset =
+ cpu_to_le16 ( offsetof ( typeof ( *sbft ), scsi ) );
+ memcpy ( &sbft->scsi.lun, &srpdev->lun, sizeof ( sbft->scsi.lun ) );
+ sbft->table.srp_offset =
+ cpu_to_le16 ( offsetof ( typeof ( *sbft ), srp ) );
+ memcpy ( &sbft->srp.initiator, &srpdev->initiator,
+ sizeof ( sbft->srp.initiator ) );
+ memcpy ( &sbft->srp.target, &srpdev->target,
+ sizeof ( sbft->srp.target ) );
+
+ /* Ask transport layer to describe transport-specific portions */
+ if ( ( rc = acpi_describe ( &srpdev->socket, acpi, len ) ) != 0 ) {
+ DBGC ( srpdev, "SRP %p cannot describe transport layer: %s\n",
+ srpdev, strerror ( rc ) );
+ return rc;
}
return 0;
}
+/** SRP device socket interface operations */
+static struct interface_operation srpdev_socket_op[] = {
+ INTF_OP ( xfer_deliver, struct srp_device *, srpdev_deliver ),
+ INTF_OP ( intf_close, struct srp_device *, srpdev_close ),
+};
+
+/** SRP device socket interface descriptor */
+static struct interface_descriptor srpdev_socket_desc =
+ INTF_DESC ( struct srp_device, socket, srpdev_socket_op );
+
+/** SRP device SCSI interface operations */
+static struct interface_operation srpdev_scsi_op[] = {
+ INTF_OP ( scsi_command, struct srp_device *, srpdev_scsi_command ),
+ INTF_OP ( xfer_window, struct srp_device *, srpdev_window ),
+ INTF_OP ( intf_close, struct srp_device *, srpdev_close ),
+ INTF_OP ( acpi_describe, struct srp_device *, srpdev_describe ),
+};
+
+/** SRP device SCSI interface descriptor */
+static struct interface_descriptor srpdev_scsi_desc =
+ INTF_DESC ( struct srp_device, scsi, srpdev_scsi_op );
+
/**
- * Attach SRP device
+ * Open SRP device
*
- * @v scsi SCSI device
- * @v root_path Root path
+ * @v block Block control interface
+ * @v socket Socket interface
+ * @v initiator Initiator port ID
+ * @v target Target port ID
+ * @v memory_handle RDMA memory handle
+ * @v lun SCSI LUN
+ * @ret rc Return status code
*/
-int srp_attach ( struct scsi_device *scsi, const char *root_path ) {
- struct srp_transport_type *transport;
- struct srp_device *srp;
+int srp_open ( struct interface *block, struct interface *socket,
+ union srp_port_id *initiator, union srp_port_id *target,
+ uint32_t memory_handle, struct scsi_lun *lun ) {
+ struct srp_device *srpdev;
+ int tag;
int rc;
- /* Hard-code an IB SRP back-end for now */
- transport = &ib_srp_transport;
-
/* Allocate and initialise structure */
- srp = zalloc ( sizeof ( *srp ) + transport->priv_len );
- if ( ! srp ) {
+ srpdev = zalloc ( sizeof ( *srpdev ) );
+ if ( ! srpdev ) {
rc = -ENOMEM;
- goto err_alloc;
+ goto err_zalloc;
}
- ref_init ( &srp->refcnt, NULL );
- intf_init ( &srp->socket, &srp_xfer_desc, &srp->refcnt );
- srp->transport = transport;
- DBGC ( srp, "SRP %p using %s\n", srp, root_path );
-
- /* Parse root path */
- if ( ( rc = transport->parse_root_path ( srp, root_path ) ) != 0 ) {
- DBGC ( srp, "SRP %p could not parse root path: %s\n",
- srp, strerror ( rc ) );
- goto err_parse_root_path;
+ ref_init ( &srpdev->refcnt, NULL );
+ intf_init ( &srpdev->scsi, &srpdev_scsi_desc, &srpdev->refcnt );
+ intf_init ( &srpdev->socket, &srpdev_socket_desc, &srpdev->refcnt );
+ INIT_LIST_HEAD ( &srpdev->commands );
+ srpdev->memory_handle = memory_handle;
+ DBGC ( srpdev, "SRP %p %08x%08x%08x%08x->%08x%08x%08x%08x\n", srpdev,
+ ntohl ( initiator->dwords[0] ), ntohl ( initiator->dwords[1] ),
+ ntohl ( initiator->dwords[2] ), ntohl ( initiator->dwords[3] ),
+ ntohl ( target->dwords[0] ), ntohl ( target->dwords[1] ),
+ ntohl ( target->dwords[2] ), ntohl ( target->dwords[3] ) );
+
+ /* Preserve parameters required for boot firmware table */
+ memcpy ( &srpdev->initiator, initiator, sizeof ( srpdev->initiator ) );
+ memcpy ( &srpdev->target, target, sizeof ( srpdev->target ) );
+ memcpy ( &srpdev->lun, lun, sizeof ( srpdev->lun ) );
+
+ /* Attach to socket interface and initiate login */
+ intf_plug_plug ( &srpdev->socket, socket );
+ tag = srp_new_tag ( srpdev );
+ assert ( tag >= 0 ); /* Cannot fail when no commands in progress */
+ if ( ( rc = srp_login ( srpdev, initiator, target, tag ) ) != 0 )
+ goto err_login;
+
+ /* Attach SCSI device to parent interface */
+ if ( ( rc = scsi_open ( block, &srpdev->scsi, lun ) ) != 0 ) {
+ DBGC ( srpdev, "SRP %p could not create SCSI device: %s\n",
+ srpdev, strerror ( rc ) );
+ goto err_scsi_open;
}
- /* Attach parent interface, mortalise self, and return */
- scsi->backend = ref_get ( &srp->refcnt );
- scsi->command = srp_command;
- ref_put ( &srp->refcnt );
+ /* Mortalise self and return */
+ ref_put ( &srpdev->refcnt );
return 0;
- err_parse_root_path:
- ref_put ( &srp->refcnt );
- err_alloc:
+ err_scsi_open:
+ err_login:
+ srpdev_close ( srpdev, rc );
+ ref_put ( &srpdev->refcnt );
+ err_zalloc:
return rc;
}
-
-/**
- * Detach SRP device
- *
- * @v scsi SCSI device
- */
-void srp_detach ( struct scsi_device *scsi ) {
- struct srp_device *srp =
- container_of ( scsi->backend, struct srp_device, refcnt );
-
- /* Close socket */
- intf_shutdown ( &srp->socket, 0 );
- scsi->command = scsi_detached_command;
- ref_put ( scsi->backend );
- scsi->backend = NULL;
-}