diff options
Diffstat (limited to 'src/drivers/block/srp.c')
| -rw-r--r-- | src/drivers/block/srp.c | 830 |
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; -} |
