diff --git a/src/arch/i386/interface/pcbios/ib_srpboot.c b/src/arch/i386/interface/pcbios/ib_srpboot.c new file mode 100644 index 00000000..ab90c44f --- /dev/null +++ b/src/arch/i386/interface/pcbios/ib_srpboot.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +FILE_LICENCE ( GPL2_OR_LATER ); + +static int ib_srpboot ( const char *root_path ) { + struct scsi_device *scsi; + struct int13_drive *drive; + int rc; + + scsi = zalloc ( sizeof ( *scsi ) ); + if ( ! scsi ) { + rc = -ENOMEM; + goto err_alloc_scsi; + } + drive = zalloc ( sizeof ( *drive ) ); + if ( ! drive ) { + rc = -ENOMEM; + goto err_alloc_drive; + } + + if ( ( rc = srp_attach ( scsi, root_path ) ) != 0 ) { + printf ( "Could not attach IB_SRP device: %s\n", + strerror ( rc ) ); + goto err_attach; + } + if ( ( rc = init_scsidev ( scsi ) ) != 0 ) { + printf ( "Could not initialise IB_SRP device: %s\n", + strerror ( rc ) ); + goto err_init; + } + + drive->blockdev = &scsi->blockdev; + + register_int13_drive ( drive ); + printf ( "Registered as BIOS drive %#02x\n", drive->drive ); + printf ( "Booting from BIOS drive %#02x\n", drive->drive ); + rc = int13_boot ( drive->drive ); + printf ( "Boot failed\n" ); + + printf ( "Unregistering BIOS drive %#02x\n", drive->drive ); + unregister_int13_drive ( drive ); + + err_init: + srp_detach ( scsi ); + err_attach: + free ( drive ); + err_alloc_drive: + free ( scsi ); + err_alloc_scsi: + return rc; +} + +struct sanboot_protocol ib_srp_sanboot_protocol __sanboot_protocol = { + .prefix = "ib_srp:", + .boot = ib_srpboot, +}; diff --git a/src/config/general.h b/src/config/general.h index 7422a38a..ee07dfc0 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -62,6 +62,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); //#undef SANBOOT_PROTO_ISCSI /* iSCSI protocol */ //#undef SANBOOT_PROTO_AOE /* AoE protocol */ +//#undef SANBOOT_PROTO_IB_SRP /* Infiniband SCSI RDMA protocol */ /* * Name resolution modules diff --git a/src/core/config.c b/src/core/config.c index 4562e3a4..e4c05613 100644 --- a/src/core/config.c +++ b/src/core/config.c @@ -109,6 +109,9 @@ REQUIRE_OBJECT ( iscsiboot ); #ifdef SANBOOT_PROTO_AOE REQUIRE_OBJECT ( aoeboot ); #endif +#ifdef SANBOOT_PROTO_IB_SRP +REQUIRE_OBJECT ( ib_srpboot ); +#endif /* * Drag in all requested resolvers diff --git a/src/drivers/block/srp.c b/src/drivers/block/srp.c new file mode 100644 index 00000000..f50f194b --- /dev/null +++ b/src/drivers/block/srp.c @@ -0,0 +1,530 @@ +/* + * Copyright (C) 2009 Fen Systems Ltd . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +FILE_LICENCE ( BSD2 ); + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @file + * + * SCSI RDMA Protocol + * + */ + +FEATURE ( FEATURE_PROTOCOL, "SRP", DHCP_EB_FEATURE_SRP, 1 ); + +/** Tag to be used for next SRP IU */ +static unsigned int srp_tag = 0; + +static void srp_login ( struct srp_device *srp ); +static void srp_cmd ( struct srp_device *srp ); + +/** + * Mark SRP SCSI command as complete + * + * @v srp SRP device + * @v rc Status code + */ +static void srp_scsi_done ( struct srp_device *srp, int rc ) { + if ( srp->command ) + srp->command->rc = rc; + srp->command = NULL; +} + +/** + * Handle SRP session failure + * + * @v srp SRP device + * @v rc Reason for failure + */ +static void srp_fail ( struct srp_device *srp, int rc ) { + + /* Close underlying socket */ + xfer_close ( &srp->socket, rc ); + + /* Clear session state */ + srp->state = 0; + + /* Increment retry count */ + srp->retry_count++; + + /* If we have reached the retry limit, permanently abort the + * session. + */ + if ( srp->retry_count >= SRP_MAX_RETRIES ) { + srp->instant_rc = rc; + srp_scsi_done ( srp, rc ); + return; + } + + /* Otherwise, try to reopen the connection */ + srp_login ( srp ); +} + +/** + * Initiate SRP login + * + * @v srp SRP device + */ +static void srp_login ( struct srp_device *srp ) { + struct io_buffer *iobuf; + struct srp_login_req *login_req; + int rc; + + assert ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) ); + + /* 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; + } + 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; + } + + /* 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->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 ) ); + + 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 ) ); + + /* 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; + } + + return; + + err: + srp_fail ( srp, rc ); +} + +/** + * Handle SRP login response + * + * @v srp SRP device + * @v iobuf I/O buffer + * @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] ) ); + + /* 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; + } + + DBGC ( srp, "SRP %p logged in\n", srp ); + + /* Mark as logged in */ + srp->state |= SRP_STATE_LOGGED_IN; + + /* Reset error counter */ + srp->retry_count = 0; + + /* Issue pending command */ + srp_cmd ( srp ); + + rc = 0; + out: + free_iob ( iobuf ); + return rc; +} + +/** + * Handle SRP login rejection + * + * @v srp SRP device + * @v iobuf I/O buffer + * @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] ) ); + + /* 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; + } + + /* 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; +} + +/** + * Transmit SRP SCSI command + * + * @v srp SRP device + */ +static void srp_cmd ( struct srp_device *srp ) { + 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 ); + + /* Allocate I/O buffer */ + iobuf = xfer_alloc_iob ( &srp->socket, SRP_MAX_I_T_IU_LEN ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err; + } + + /* 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 ) ); + + /* Construct data-out descriptor, if present */ + if ( srp->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 ); + } + + /* Construct data-in descriptor, if present */ + if ( srp->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 ); + } + + 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 ) ); + + /* 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; + } + + return; + + err: + srp_fail ( srp, rc ); +} + +/** + * Handle SRP SCSI response + * + * @v srp SRP device + * @v iobuf I/O buffer + * @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] ) ); + + /* 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; + } + + /* 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 ) ); + } + } + 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 ) ); + } + srp->command->status = rsp->status; + + /* Mark SCSI command as complete */ + srp_scsi_done ( srp, 0 ); + + rc = 0; + out: + free_iob ( iobuf ); + return rc; +} + +/** + * Handle SRP unrecognised response + * + * @v srp SRP device + * @v iobuf I/O buffer + * @ret rc Returns status code + */ +static int srp_unrecognised ( struct srp_device *srp, + struct io_buffer *iobuf ) { + struct srp_common *common = iobuf->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 ); + + free_iob ( iobuf ); + return -ENOTSUP; +} + +/** + * Receive data from underlying socket + * + * @v xfer Data transfer interface + * @v iobuf Datagram I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int srp_xfer_deliver_iob ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta __unused ) { + struct srp_device *srp = + container_of ( xfer, struct srp_device, socket ); + struct srp_common *common = iobuf->data; + int ( * type ) ( struct srp_device *srp, struct io_buffer *iobuf ); + int rc; + + /* Determine IU type */ + switch ( common->type ) { + case SRP_LOGIN_RSP: + type = srp_login_rsp; + break; + case SRP_LOGIN_REJ: + type = srp_login_rej; + break; + case SRP_RSP: + type = srp_rsp; + break; + default: + type = srp_unrecognised; + break; + } + + /* Handle IU */ + if ( ( rc = type ( srp, iobuf ) ) != 0 ) + goto err; + + return 0; + + err: + srp_fail ( srp, rc ); + return rc; +} + +/** + * Underlying socket closed + * + * @v xfer Data transfer interface + * @v rc Reason for close + */ +static void srp_xfer_close ( struct xfer_interface *xfer, int rc ) { + struct srp_device *srp = + container_of ( xfer, struct srp_device, socket ); + + DBGC ( srp, "SRP %p socket closed: %s\n", srp, strerror ( rc ) ); + + srp_fail ( srp, rc ); +} + +/** SRP data transfer interface operations */ +static struct xfer_interface_operations srp_xfer_operations = { + .close = srp_xfer_close, + .vredirect = ignore_xfer_vredirect, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = srp_xfer_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, +}; + +/** + * Issue SCSI command via SRP + * + * @v scsi SCSI device + * @v command SCSI command + * @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 ); + + /* Return instant failure, if we have already aborted the session */ + if ( srp->instant_rc ) + return srp->instant_rc; + + /* 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 */ + } + + return 0; +} + +/** + * Attach SRP device + * + * @v scsi SCSI device + * @v root_path Root path + */ +int srp_attach ( struct scsi_device *scsi, const char *root_path ) { + struct srp_transport_type *transport; + struct srp_device *srp; + 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 ) { + rc = -ENOMEM; + goto err_alloc; + } + xfer_init ( &srp->socket, &srp_xfer_operations, &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; + } + + /* Attach parent interface, mortalise self, and return */ + scsi->backend = ref_get ( &srp->refcnt ); + scsi->command = srp_command; + ref_put ( &srp->refcnt ); + return 0; + + err_parse_root_path: + ref_put ( &srp->refcnt ); + err_alloc: + 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 */ + xfer_nullify ( &srp->socket ); + xfer_close ( &srp->socket, 0 ); + scsi->command = scsi_detached_command; + ref_put ( scsi->backend ); + scsi->backend = NULL; +} diff --git a/src/include/gpxe/errfile.h b/src/include/gpxe/errfile.h index d2305b32..a7892839 100644 --- a/src/include/gpxe/errfile.h +++ b/src/include/gpxe/errfile.h @@ -119,6 +119,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_hermon ( ERRFILE_DRIVER | 0x00720000 ) #define ERRFILE_linda ( ERRFILE_DRIVER | 0x00730000 ) #define ERRFILE_ata ( ERRFILE_DRIVER | 0x00740000 ) +#define ERRFILE_srp ( ERRFILE_DRIVER | 0x00750000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) @@ -154,6 +155,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_net80211 ( ERRFILE_NET | 0x001f0000 ) #define ERRFILE_ib_mi ( ERRFILE_NET | 0x00200000 ) #define ERRFILE_ib_cmrc ( ERRFILE_NET | 0x00210000 ) +#define ERRFILE_ib_srp ( ERRFILE_NET | 0x00220000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) @@ -186,6 +188,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_pxemenu ( ERRFILE_OTHER | 0x00150000 ) #define ERRFILE_x509 ( ERRFILE_OTHER | 0x00160000 ) #define ERRFILE_login_ui ( ERRFILE_OTHER | 0x00170000 ) +#define ERRFILE_ib_srpboot ( ERRFILE_OTHER | 0x00180000 ) /** @} */ diff --git a/src/include/gpxe/features.h b/src/include/gpxe/features.h index db485bc2..34431867 100644 --- a/src/include/gpxe/features.h +++ b/src/include/gpxe/features.h @@ -44,6 +44,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define DHCP_EB_FEATURE_BZIMAGE 0x18 /**< bzImage format */ #define DHCP_EB_FEATURE_MULTIBOOT 0x19 /**< Multiboot format */ #define DHCP_EB_FEATURE_SLAM 0x1a /**< SLAM protocol */ +#define DHCP_EB_FEATURE_SRP 0x1b /**< SRP protocol */ #define DHCP_EB_FEATURE_NBI 0x20 /**< NBI format */ #define DHCP_EB_FEATURE_PXE 0x21 /**< PXE format */ #define DHCP_EB_FEATURE_ELF 0x22 /**< ELF format */ diff --git a/src/include/gpxe/ib_srp.h b/src/include/gpxe/ib_srp.h new file mode 100644 index 00000000..cf705b30 --- /dev/null +++ b/src/include/gpxe/ib_srp.h @@ -0,0 +1,79 @@ +#ifndef _GPXE_IB_SRP_H +#define _GPXE_IB_SRP_H + +/** @file + * + * SCSI RDMA Protocol over Infiniband + * + */ + +FILE_LICENCE ( BSD2 ); + +#include +#include +#include + +/** SRP initiator port identifier for Infiniband */ +struct ib_srp_initiator_port_id { + /** Identifier extension */ + struct ib_gid_half id_ext; + /** IB channel adapter GUID */ + struct ib_gid_half hca_guid; +} __attribute__ (( packed )); + +/** SRP target port identifier for Infiniband */ +struct ib_srp_target_port_id { + /** Identifier extension */ + struct ib_gid_half id_ext; + /** I/O controller GUID */ + struct ib_gid_half ioc_guid; +} __attribute__ (( packed )); + +/** + * Get Infiniband-specific initiator port ID + * + * @v port_ids SRP port IDs + * @ret initiator_port_id Infiniband-specific initiator port ID + */ +static inline __always_inline struct ib_srp_initiator_port_id * +ib_srp_initiator_port_id ( struct srp_port_ids *port_ids ) { + return ( ( struct ib_srp_initiator_port_id * ) &port_ids->initiator ); +} + +/** + * Get Infiniband-specific target port ID + * + * @v port_ids SRP port IDs + * @ret target_port_id Infiniband-specific target port ID + */ +static inline __always_inline struct ib_srp_target_port_id * +ib_srp_target_port_id ( struct srp_port_ids *port_ids ) { + return ( ( struct ib_srp_target_port_id * ) &port_ids->target ); +} + +/** Infiniband-specific SRP parameters */ +struct ib_srp_parameters { + /** Source GID */ + struct ib_gid sgid; + /** Destination GID */ + struct ib_gid dgid; + /** Service ID */ + struct ib_gid_half service_id; + /** Partition key */ + uint16_t pkey; +}; + +/** + * Get Infiniband-specific transport parameters + * + * @v srp SRP device + * @ret ib_params Infiniband-specific transport parameters + */ +static inline __always_inline struct ib_srp_parameters * +ib_srp_params ( struct srp_device *srp ) { + return srp_transport_priv ( srp ); +} + +extern struct srp_transport_type ib_srp_transport; + +#endif /* _GPXE_IB_SRP_H */ diff --git a/src/include/gpxe/srp.h b/src/include/gpxe/srp.h new file mode 100644 index 00000000..75ec7b83 --- /dev/null +++ b/src/include/gpxe/srp.h @@ -0,0 +1,874 @@ +#ifndef _GPXE_SRP_H +#define _GPXE_SRP_H + +/** @file + * + * SCSI RDMA Protocol + * + */ + +FILE_LICENCE ( BSD2 ); + +#include +#include +#include +#include +#include + +/***************************************************************************** + * + * Common fields + * + ***************************************************************************** + */ + +/** An SRP information unit tag */ +struct srp_tag { + uint32_t dwords[2]; +} __attribute__ (( packed )); + +/** An SRP port ID */ +struct srp_port_id { + uint8_t bytes[16]; +} __attribute__ (( packed )); + +/** An SRP port ID pair */ +struct srp_port_ids { + /** Initiator port ID */ + struct srp_port_id initiator; + /** Target port ID */ + struct srp_port_id target; +} __attribute__ (( packed )); + +/** SRP information unit common fields */ +struct srp_common { + /** Information unit type */ + uint8_t type; + /** Reserved */ + uint8_t reserved0[7]; + /** Tag */ + struct srp_tag tag; +} __attribute__ (( packed )); + +/***************************************************************************** + * + * Login request + * + ***************************************************************************** + */ + +/** An SRP login request information unit */ +struct srp_login_req { + /** Information unit type + * + * This must be @c SRP_LOGIN_REQ + */ + uint8_t type; + /** Reserved */ + uint8_t reserved0[7]; + /** Tag */ + struct srp_tag tag; + /** Requested maximum initiator to target IU length */ + uint32_t max_i_t_iu_len; + /** Reserved */ + uint8_t reserved1[4]; + /** Required buffer formats + * + * This is the bitwise OR of one or more @c + * SRP_LOGIN_REQ_FMT_XXX constants. + */ + uint16_t required_buffer_formats; + /** Flags + * + * This is the bitwise OR of zero or more @c + * SRP_LOGIN_REQ_FLAG_XXX and @c SRP_LOGIN_REQ_MCA_XXX + * constants. + */ + uint8_t flags; + /** Reserved */ + uint8_t reserved2[5]; + /** Initiator and target port identifiers */ + struct srp_port_ids port_ids; +} __attribute__ (( packed )); + +/** Type of an SRP login request */ +#define SRP_LOGIN_REQ 0x00 + +/** Require indirect data buffer descriptor format */ +#define SRP_LOGIN_REQ_FMT_IDBD 0x04 + +/** Require direct data buffer descriptor format */ +#define SRP_LOGIN_REQ_FMT_DDBD 0x02 + +/** Use solicited notification for asynchronous events */ +#define SRP_LOGIN_REQ_FLAG_AESOLNT 0x40 + +/** Use solicited notification for credit request */ +#define SRP_LOGIN_REQ_FLAG_CRSOLNT 0x20 + +/** Use solicited notification for logouts */ +#define SRP_LOGIN_REQ_FLAG_LOSOLNT 0x10 + +/** Multi-channel action mask */ +#define SRP_LOGIN_REQ_MCA_MASK 0x03 + +/** Single RDMA channel operation */ +#define SRP_LOGIN_REQ_MCA_SINGLE_CHANNEL 0x00 + +/** Multiple independent RDMA channel operation */ +#define SRP_LOGIN_REQ_MCA_MULTIPLE_CHANNELS 0x01 + +/***************************************************************************** + * + * Login response + * + ***************************************************************************** + */ + +/** An SRP login response */ +struct srp_login_rsp { + /** Information unit type + * + * This must be @c SRP_LOGIN_RSP + */ + uint8_t type; + /** Reserved */ + uint8_t reserved0[3]; + /** Request limit delta */ + uint32_t request_limit_delta; + /** Tag */ + struct srp_tag tag; + /** Maximum initiator to target IU length */ + uint32_t max_i_t_iu_len; + /** Maximum target to initiator IU length */ + uint32_t max_t_i_iu_len; + /** Supported buffer formats + * + * This is the bitwise OR of one or more @c + * SRP_LOGIN_RSP_FMT_XXX constants. + */ + uint16_t supported_buffer_formats; + /** Flags + * + * This is the bitwise OR of zero or more @c + * SRP_LOGIN_RSP_FLAG_XXX and @c SRP_LOGIN_RSP_MCR_XXX + * constants. + */ + uint8_t flags; + /** Reserved */ + uint8_t reserved1[25]; +} __attribute__ (( packed )); + +/** Type of an SRP login response */ +#define SRP_LOGIN_RSP 0xc0 + +/** Indirect data buffer descriptor format supported */ +#define SRP_LOGIN_RSP_FMT_IDBD 0x04 + +/** Direct data buffer descriptor format supported */ +#define SRP_LOGIN_RSP_FMT_DDBD 0x02 + +/** Solicited notification is supported */ +#define SRP_LOGIN_RSP_FLAG_SOLNTSUP 0x10 + +/** Multi-channel result mask */ +#define SRP_LOGIN_RSP_MCR_MASK 0x03 + +/** No existing RDMA channels were associated with the same I_T nexus */ +#define SRP_LOGIN_RSP_MCR_NO_EXISTING_CHANNELS 0x00 + +/** One or more existing RDMA channels were terminated */ +#define SRP_LOGIN_RSP_MCR_EXISTING_CHANNELS_TERMINATED 0x01 + +/** One or more existing RDMA channels continue to operate independently */ +#define SRP_LOGIN_RSP_MCR_EXISTING_CHANNELS_CONTINUE 0x02 + +/***************************************************************************** + * + * Login rejection + * + ***************************************************************************** + */ + +/** An SRP login rejection */ +struct srp_login_rej { + /** Information unit type + * + * This must be @c SRP_LOGIN_REJ + */ + uint8_t type; + /** Reserved */ + uint8_t reserved0[3]; + /** Reason + * + * This is a @c SRP_LOGIN_REJ_REASON_XXX constant. + */ + uint32_t reason; + /** Tag */ + struct srp_tag tag; + /** Reserved */ + uint8_t reserved1[8]; + /** Supported buffer formats + * + * This is the bitwise OR of one or more @c + * SRP_LOGIN_REJ_FMT_XXX constants. + */ + uint16_t supported_buffer_formats; + /** Reserved */ + uint8_t reserved2[6]; +} __attribute__ (( packed )); + +/** Type of an SRP login rejection */ +#define SRP_LOGIN_REJ 0xc2 + +/** Unable to establish RDMA channel, no reason specified */ +#define SRP_LOGIN_REJ_REASON_UNKNOWN 0x00010000UL + +/** Insufficient RDMA channel resources */ +#define SRP_LOGIN_REJ_REASON_INSUFFICIENT_RESOURCES 0x00010001UL + +/** Requested maximum initiator to target IU length value too large */ +#define SRP_LOGIN_REJ_REASON_BAD_MAX_I_T_IU_LEN 0x00010002UL + +/** Unable to associate RDMA channel with specified I_T nexus */ +#define SRP_LOGIN_REJ_REASON_CANNOT_ASSOCIATE 0x00010003UL + +/** One or more requested data buffer descriptor formats are not supported */ +#define SRP_LOGIN_REJ_REASON_UNSUPPORTED_BUFFER_FORMAT 0x00010004UL + +/** SRP target port does not support multiple RDMA channels per I_T nexus */ +#define SRP_LOGIN_REJ_REASON_NO_MULTIPLE_CHANNELS 0x00010005UL + +/** RDMA channel limit reached for this initiator */ +#define SRP_LOGIN_REJ_REASON_NO_MORE_CHANNELS 0x00010006UL + +/** Indirect data buffer descriptor format supported */ +#define SRP_LOGIN_REJ_FMT_IDBD 0x04 + +/** Direct data buffer descriptor format supported */ +#define SRP_LOGIN_REJ_FMT_DDBD 0x02 + +/***************************************************************************** + * + * Initiator logout + * + ***************************************************************************** + */ + +/** An SRP initiator logout request */ +struct srp_i_logout { + /** Information unit type + * + * This must be @c SRP_I_LOGOUT + */ + uint8_t type; + /** Reserved */ + uint8_t reserved0[7]; + /** Tag */ + struct srp_tag tag; +} __attribute__ (( packed )); + +/** Type of an SRP initiator logout request */ +#define SRP_I_LOGOUT 0x03 + +/***************************************************************************** + * + * Target logout + * + ***************************************************************************** + */ + +/** An SRP target logout request */ +struct srp_t_logout { + /** Information unit type + * + * This must be @c SRP_T_LOGOUT + */ + uint8_t type; + /** Flags + * + * This is the bitwise OR of zero or more @c + * SRP_T_LOGOUT_FLAG_XXX constants. + */ + uint8_t flags; + /** Reserved */ + uint8_t reserved0[2]; + /** Reason + * + * This is a @c SRP_T_LOGOUT_REASON_XXX constant. + */ + uint32_t reason; + /** Tag */ + struct srp_tag tag; +} __attribute__ (( packed )); + +/** Type of an SRP target logout request */ +#define SRP_T_LOGOUT 0x80 + +/** The initiator specified solicited notification of logouts */ +#define SRP_T_LOGOUT_FLAG_SOLNT 0x01 + +/** No reason specified */ +#define SRP_T_LOGOUT_REASON_UNKNOWN 0x00000000UL + +/** Inactive RDMA channel (reclaiming resources) */ +#define SRP_T_LOGOUT_REASON_INACTIVE 0x00000001UL + +/** Invalid information unit type code received by SRP target port */ +#define SRP_T_LOGOUT_REASON_INVALID_TYPE 0x00000002UL + +/** SRP initiator port sent response with no corresponding request */ +#define SRP_T_LOGOUT_REASON_SPURIOUS_RESPONSE 0x00000003UL + +/** RDMA channel disconnected due to multi-channel action code in new login */ +#define SRP_T_LOGOUT_REASON_MCA 0x00000004UL + +/** Unsuppported format code value specified in data-out buffer descriptor */ +#define SRP_T_LOGOUT_UNSUPPORTED_DATA_OUT_FORMAT 0x00000005UL + +/** Unsuppported format code value specified in data-in buffer descriptor */ +#define SRP_T_LOGOUT_UNSUPPORTED_DATA_IN_FORMAT 0x00000006UL + +/** Invalid length for IU type */ +#define SRP_T_LOGOUT_INVALID_IU_LEN 0x00000008UL + +/***************************************************************************** + * + * Task management + * + ***************************************************************************** + */ + +/** An SRP task management request */ +struct srp_tsk_mgmt { + /** Information unit type + * + * This must be @c SRP_TSK_MGMT + */ + uint8_t type; + /** Flags + * + * This is the bitwise OR of zero or more + * @c SRP_TSK_MGMT_FLAG_XXX constants. + */ + uint8_t flags; + /** Reserved */ + uint8_t reserved0[6]; + /** Tag */ + struct srp_tag tag; + /** Reserved */ + uint8_t reserved1[4]; + /** Logical unit number */ + struct scsi_lun lun; + /** Reserved */ + uint8_t reserved2[2]; + /** Task management function + * + * This is a @c SRP_TASK_MGMT_FUNC_XXX constant + */ + uint8_t function; + /** Reserved */ + uint8_t reserved3[1]; + /** Tag of task to be managed */ + struct srp_tag managed_tag; + /** Reserved */ + uint8_t reserved4[8]; +} __attribute__ (( packed )); + +/** Type of an SRP task management request */ +#define SRP_TSK_MGMT 0x01 + +/** Use solicited notification for unsuccessful completions */ +#define SRP_TSK_MGMT_FLAG_UCSOLNT 0x04 + +/** Use solicited notification for successful completions */ +#define SRP_TSK_MGMT_FLAG_SCSOLNT 0x02 + +/** The task manager shall perform an ABORT TASK function */ +#define SRP_TSK_MGMT_FUNC_ABORT_TASK 0x01 + +/** The task manager shall perform an ABORT TASK SET function */ +#define SRP_TSK_MGMT_FUNC_ABORT_TASK_SET 0x02 + +/** The task manager shall perform a CLEAR TASK SET function */ +#define SRP_TSK_MGMT_FUNC_CLEAR_TASK_SET 0x04 + +/** The task manager shall perform a LOGICAL UNIT RESET function */ +#define SRP_TSK_MGMT_FUNC_LOGICAL_UNIT_RESET 0x08 + +/** The task manager shall perform a CLEAR ACA function */ +#define SRP_TSK_MGMT_FUNC_CLEAR_ACA 0x40 + +/***************************************************************************** + * + * SCSI command + * + ***************************************************************************** + */ + +/** An SRP SCSI command */ +struct srp_cmd { + /** Information unit type + * + * This must be @c SRP_CMD + */ + uint8_t type; + /** Flags + * + * This is the bitwise OR of zero or more @c SRP_CMD_FLAG_XXX + * constants. + */ + uint8_t flags; + /** Reserved */ + uint8_t reserved0[3]; + /** Data buffer descriptor formats + * + * This is the bitwise OR of one @c SRP_CMD_DO_FMT_XXX and one @c + * SRP_CMD_DI_FMT_XXX constant. + */ + uint8_t data_buffer_formats; + /** Data-out buffer descriptor count */ + uint8_t data_out_buffer_count; + /** Data-in buffer descriptor count */ + uint8_t data_in_buffer_count; + /** Tag */ + struct srp_tag tag; + /** Reserved */ + uint8_t reserved1[4]; + /** Logical unit number */ + struct scsi_lun lun; + /** Reserved */ + uint8_t reserved2[1]; + /** Task attribute + * + * This is a @c SRP_CMD_TASK_ATTR_XXX constant. + */ + uint8_t task_attr; + /** Reserved */ + uint8_t reserved3[1]; + /** Additional CDB length */ + uint8_t additional_cdb_len; + /** Command data block */ + union scsi_cdb cdb; +} __attribute__ (( packed )); + +/** Type of an SRP SCSI command */ +#define SRP_CMD 0x02 + +/** Use solicited notification for unsuccessful completions */ +#define SRP_CMD_FLAG_UCSOLNT 0x04 + +/** Use solicited notification for successful completions */ +#define SRP_CMD_FLAG_SCSOLNT 0x02 + +/** Data-out buffer format mask */ +#define SRP_CMD_DO_FMT_MASK 0xf0 + +/** Direct data-out buffer format */ +#define SRP_CMD_DO_FMT_DIRECT 0x10 + +/** Indirect data-out buffer format */ +#define SRP_CMD_DO_FMT_INDIRECT 0x20 + +/** Data-in buffer format mask */ +#define SRP_CMD_DI_FMT_MASK 0x0f + +/** Direct data-in buffer format */ +#define SRP_CMD_DI_FMT_DIRECT 0x01 + +/** Indirect data-in buffer format */ +#define SRP_CMD_DI_FMT_INDIRECT 0x02 + +/** Use the rules for a simple task attribute */ +#define SRP_CMD_TASK_ATTR_SIMPLE 0x00 + +/** Use the rules for a head of queue task attribute */ +#define SRP_CMD_TASK_ATTR_QUEUE_HEAD 0x01 + +/** Use the rules for an ordered task attribute */ +#define SRP_CMD_TASK_ATTR_ORDERED 0x02 + +/** Use the rules for an automatic contingent allegiance task attribute */ +#define SRP_CMD_TASK_ATTR_AUTOMATIC_CONTINGENT_ALLEGIANCE 0x08 + +/** An SRP memory descriptor */ +struct srp_memory_descriptor { + /** Virtual address */ + uint64_t address; + /** Memory handle */ + uint32_t handle; + /** Data length */ + uint32_t len; +} __attribute__ (( packed )); + +/***************************************************************************** + * + * SCSI response + * + ***************************************************************************** + */ + +/** An SRP SCSI response */ +struct srp_rsp { + /** Information unit type + * + * This must be @c SRP_RSP + */ + uint8_t type; + /** Flags + * + * This is the bitwise OR of zero or more @c SRP_RSP_FLAG_XXX + * constants. + */ + uint8_t flags; + /** Reserved */ + uint8_t reserved0[2]; + /** Request limit delta */ + uint32_t request_limit_delta; + /** Tag */ + struct srp_tag tag; + /** Reserved */ + uint8_t reserved1[2]; + /** Valid fields + * + * This is the bitwise OR of zero or more @c SRP_RSP_VALID_XXX + * constants. + */ + uint8_t valid; + /** Status + * + * This is the SCSI status code. + */ + uint8_t status; + /** Data-out residual count */ + uint32_t data_out_residual_count; + /** Data-in residual count */ + uint32_t data_in_residual_count; + /** Sense data list length */ + uint32_t sense_data_len; + /** Response data list length */ + uint32_t response_data_len; +} __attribute__ (( packed )); + +/** Type of an SRP SCSI response */ +#define SRP_RSP 0xc1 + +/** The initiator specified solicited notification of this response */ +#define SRP_RSP_FLAG_SOLNT 0x01 + +/** Data-in residual count field is valid and represents an underflow */ +#define SRP_RSP_VALID_DIUNDER 0x20 + +/** Data-in residual count field is valid and represents an overflow */ +#define SRP_RSP_VALID_DIOVER 0x10 + +/** Data-out residual count field is valid and represents an underflow */ +#define SRP_RSP_VALID_DOUNDER 0x08 + +/** Data-out residual count field is valid and represents an overflow */ +#define SRP_RSP_VALID_DOOVER 0x04 + +/** Sense data list length field is valid */ +#define SRP_RSP_VALID_SNSVALID 0x02 + +/** Response data list length field is valid */ +#define SRP_RSP_VALID_RSPVALID 0x01 + +/** + * Get response data portion of SCSI response + * + * @v rsp SCSI response + * @ret response_data Response data, or NULL if not present + */ +static inline void * srp_rsp_response_data ( struct srp_rsp *rsp ) { + return ( ( rsp->valid & SRP_RSP_VALID_RSPVALID ) ? + ( ( ( void * ) rsp ) + sizeof ( *rsp ) ) : NULL ); +} + +/** + * Get length of response data portion of SCSI response + * + * @v rsp SCSI response + * @ret response_data_len Response data length + */ +static inline size_t srp_rsp_response_data_len ( struct srp_rsp *rsp ) { + return ( ( rsp->valid & SRP_RSP_VALID_RSPVALID ) ? + ntohl ( rsp->response_data_len ) : 0 ); +} + +/** + * Get sense data portion of SCSI response + * + * @v rsp SCSI response + * @ret sense_data Sense data, or NULL if not present + */ +static inline void * srp_rsp_sense_data ( struct srp_rsp *rsp ) { + return ( ( rsp->valid & SRP_RSP_VALID_SNSVALID ) ? + ( ( ( void * ) rsp ) + sizeof ( *rsp ) + + srp_rsp_response_data_len ( rsp ) ) : NULL ); +} + +/** + * Get length of sense data portion of SCSI response + * + * @v rsp SCSI response + * @ret sense_data_len Sense data length + */ +static inline size_t srp_rsp_sense_data_len ( struct srp_rsp *rsp ) { + return ( ( rsp->valid & SRP_RSP_VALID_SNSVALID ) ? + ntohl ( rsp->sense_data_len ) : 0 ); +} + +/***************************************************************************** + * + * Credit request + * + ***************************************************************************** + */ + +/** An SRP credit request */ +struct srp_cred_req { + /** Information unit type + * + * This must be @c SRP_CRED_REQ + */ + uint8_t type; + /** Flags + * + * This is the bitwise OR of zero or more + * @c SRP_CRED_REQ_FLAG_XXX constants. + */ + uint8_t flags; + /** Reserved */ + uint8_t reserved0[2]; + /** Request limit delta */ + uint32_t request_limit_delta; + /** Tag */ + struct srp_tag tag; +} __attribute__ (( packed )); + +/** Type of an SRP credit request */ +#define SRP_CRED_REQ 0x81 + +/** The initiator specified solicited notification of credit requests */ +#define SRP_CRED_REQ_FLAG_SOLNT 0x01 + +/***************************************************************************** + * + * Credit response + * + ***************************************************************************** + */ + +/** An SRP credit response */ +struct srp_cred_rsp { + /** Information unit type + * + * This must be @c SRP_CRED_RSP + */ + uint8_t type; + /** Reserved */ + uint8_t reserved0[7]; + /** Tag */ + struct srp_tag tag; +} __attribute__ (( packed )); + +/** Type of an SRP credit response */ +#define SRP_CRED_RSP 0x41 + +/***************************************************************************** + * + * Asynchronous event request + * + ***************************************************************************** + */ + +/** An SRP asynchronous event request */ +struct srp_aer_req { + /** Information unit type + * + * This must be @c SRP_AER_REQ + */ + uint8_t type; + /** Flags + * + * This is the bitwise OR of zero or more @c + * SRP_AER_REQ_FLAG_XXX constants. + */ + uint8_t flags; + /** Reserved */ + uint8_t reserved0[2]; + /** Request limit delta */ + uint32_t request_limit_delta; + /** Tag */ + struct srp_tag tag; + /** Reserved */ + uint8_t reserved1[4]; + /** Logical unit number */ + struct scsi_lun lun; + /** Sense data list length */ + uint32_t sense_data_len; + /** Reserved */ + uint8_t reserved2[4]; +} __attribute__ (( packed )); + +/** Type of an SRP asynchronous event request */ +#define SRP_AER_REQ 0x82 + +/** The initiator specified solicited notification of asynchronous events */ +#define SRP_AER_REQ_FLAG_SOLNT 0x01 + +/** + * Get sense data portion of asynchronous event request + * + * @v aer_req SRP asynchronous event request + * @ret sense_data Sense data + */ +static inline __always_inline void * +srp_aer_req_sense_data ( struct srp_aer_req *aer_req ) { + return ( ( ( void * ) aer_req ) + sizeof ( *aer_req ) ); +} + +/** + * Get length of sense data portion of asynchronous event request + * + * @v aer_req SRP asynchronous event request + * @ret sense_data_len Sense data length + */ +static inline __always_inline size_t +srp_aer_req_sense_data_len ( struct srp_aer_req *aer_req ) { + return ( ntohl ( aer_req->sense_data_len ) ); +} + +/***************************************************************************** + * + * Asynchronous event response + * + ***************************************************************************** + */ + +/** An SRP asynchronous event response */ +struct srp_aer_rsp { + /** Information unit type + * + * This must be @c SRP_AER_RSP + */ + uint8_t type; + /** Reserved */ + uint8_t reserved0[7]; + /** Tag */ + struct srp_tag tag; +} __attribute__ (( packed )); + +/** Type of an SRP asynchronous event response */ +#define SRP_AER_RSP 0x42 + +/***************************************************************************** + * + * Information units + * + ***************************************************************************** + */ + +/** 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 + +/***************************************************************************** + * + * SRP device + * + ***************************************************************************** + */ + +struct srp_device; + +/** An SRP transport type */ +struct srp_transport_type { + /** Length of transport private data */ + size_t priv_len; + /** Parse root path + * + * @v srp SRP device + * @v root_path Root path + * @ret Return status code + */ + int ( * parse_root_path ) ( struct srp_device *srp, + const char *root_path ); + /** Connect SRP session + * + * @v srp SRP device + * @ret rc Return status code + * + * This method should open the underlying socket. + */ + int ( * connect ) ( struct srp_device *srp ); +}; + +/** An SRP device */ +struct srp_device { + /** Reference count */ + struct refcnt refcnt; + + /** Initiator and target port IDs */ + struct srp_port_ids port_ids; + /** Logical unit number */ + struct scsi_lun lun; + /** Memory handle */ + uint32_t memory_handle; + + /** Current state + * + * This is the bitwise-OR of zero or more @c SRP_STATE_XXX + * flags. + */ + unsigned int state; + /** Retry counter */ + unsigned int retry_count; + /** Instant return status code + * + * Used to avoid retrying the connection on every new SCSI + * command after the retry count has been exceeded. + */ + int instant_rc; + /** Current SCSI command */ + struct scsi_command *command; + + /** Underlying data transfer interface */ + struct xfer_interface socket; + + /** Transport type */ + struct srp_transport_type *transport; + /** Transport private data */ + char transport_priv[0]; +}; + +/** + * Get SRP transport private data + * + * @v srp SRP device + * @ret priv SRP transport private data + */ +static inline __always_inline void * +srp_transport_priv ( struct srp_device *srp ) { + return ( ( void * ) srp->transport_priv ); +} + +/** SRP state flags */ +enum srp_state { + /** Underlying socket is open */ + SRP_STATE_SOCKET_OPEN = 0x0001, + /** Session is logged in */ + SRP_STATE_LOGGED_IN = 0x0002, +}; + +/** Maximum number of SRP retry attempts */ +#define SRP_MAX_RETRIES 3 + +extern int srp_attach ( struct scsi_device *scsi, const char *root_path ); +extern void srp_detach ( struct scsi_device *scsi ); + +#endif /* _GPXE_SRP_H */ diff --git a/src/net/infiniband/ib_srp.c b/src/net/infiniband/ib_srp.c new file mode 100644 index 00000000..4191c869 --- /dev/null +++ b/src/net/infiniband/ib_srp.c @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2009 Fen Systems Ltd . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +FILE_LICENCE ( BSD2 ); + +#include +#include +#include +#include +#include +#include + +/** + * @file + * + * SCSI RDMA Protocol over Infiniband + * + */ + +/* Disambiguate the various possible EINVALs */ +#define EINVAL_BYTE_STRING_LEN ( EINVAL | EUNIQ_01 ) +#define EINVAL_BYTE_STRING ( EINVAL | EUNIQ_02 ) +#define EINVAL_INTEGER ( EINVAL | EUNIQ_03 ) +#define EINVAL_RP_TOO_SHORT ( EINVAL | EUNIQ_04 ) + +/** IB SRP parse flags */ +enum ib_srp_parse_flags { + IB_SRP_PARSE_REQUIRED = 0x0000, + IB_SRP_PARSE_OPTIONAL = 0x8000, + IB_SRP_PARSE_FLAG_MASK = 0xf000, +}; + +/** IB SRP root path parameters */ +struct ib_srp_root_path { + /** SCSI LUN */ + struct scsi_lun *lun; + /** SRP port IDs */ + struct srp_port_ids *port_ids; + /** IB SRP parameters */ + struct ib_srp_parameters *ib; +}; + +/** + * Parse IB SRP root path byte-string value + * + * @v rp_comp Root path component string + * @v default_value Default value to use if component string is empty + * @ret value Value + */ +static int ib_srp_parse_byte_string ( const char *rp_comp, uint8_t *bytes, + unsigned int size_flags ) { + size_t size = ( size_flags & ~IB_SRP_PARSE_FLAG_MASK ); + size_t rp_comp_len = strlen ( rp_comp ); + char buf[3]; + char *buf_end; + + /* Allow optional components to be empty */ + if ( ( rp_comp_len == 0 ) && + ( size_flags & IB_SRP_PARSE_OPTIONAL ) ) + return 0; + + /* Check string length */ + if ( rp_comp_len != ( 2 * size ) ) + return -EINVAL_BYTE_STRING_LEN; + + /* Parse byte string */ + for ( ; size ; size--, rp_comp += 2, bytes++ ) { + memcpy ( buf, rp_comp, 2 ); + buf[2] = '\0'; + *bytes = strtoul ( buf, &buf_end, 16 ); + if ( buf_end != &buf[2] ) + return -EINVAL_BYTE_STRING; + } + return 0; +} + +/** + * Parse IB SRP root path integer value + * + * @v rp_comp Root path component string + * @v default_value Default value to use if component string is empty + * @ret value Value + */ +static int ib_srp_parse_integer ( const char *rp_comp, int default_value ) { + int value; + char *end; + + value = strtoul ( rp_comp, &end, 16 ); + if ( *end ) + return -EINVAL_INTEGER; + + if ( end == rp_comp ) + return default_value; + + return value; +} + +/** + * Parse IB SRP root path literal component + * + * @v rp_comp Root path component string + * @v rp IB SRP root path + * @ret rc Return status code + */ +static int ib_srp_parse_literal ( const char *rp_comp __unused, + struct ib_srp_root_path *rp __unused ) { + /* Ignore */ + return 0; +} + +/** + * Parse IB SRP root path source GID + * + * @v rp_comp Root path component string + * @v rp IB SRP root path + * @ret rc Return status code + */ +static int ib_srp_parse_sgid ( const char *rp_comp, + struct ib_srp_root_path *rp ) { + struct ib_device *ibdev; + + /* Default to the GID of the last opened Infiniband device */ + if ( ( ibdev = last_opened_ibdev() ) != NULL ) + memcpy ( &rp->ib->sgid, &ibdev->gid, sizeof ( rp->ib->sgid ) ); + + return ib_srp_parse_byte_string ( rp_comp, rp->ib->sgid.u.bytes, + ( sizeof ( rp->ib->sgid ) | + IB_SRP_PARSE_OPTIONAL ) ); +} + +/** + * Parse IB SRP root path initiator identifier extension + * + * @v rp_comp Root path component string + * @v rp IB SRP root path + * @ret rc Return status code + */ +static int ib_srp_parse_initiator_id_ext ( const char *rp_comp, + struct ib_srp_root_path *rp ) { + struct ib_srp_initiator_port_id *port_id = + ib_srp_initiator_port_id ( rp->port_ids ); + + return ib_srp_parse_byte_string ( rp_comp, port_id->id_ext.u.bytes, + ( sizeof ( port_id->id_ext ) | + IB_SRP_PARSE_OPTIONAL ) ); +} + +/** + * Parse IB SRP root path initiator HCA GUID + * + * @v rp_comp Root path component string + * @v rp IB SRP root path + * @ret rc Return status code + */ +static int ib_srp_parse_initiator_hca_guid ( const char *rp_comp, + struct ib_srp_root_path *rp ) { + struct ib_srp_initiator_port_id *port_id = + ib_srp_initiator_port_id ( rp->port_ids ); + + /* Default to the GUID portion of the source GID */ + memcpy ( &port_id->hca_guid, &rp->ib->sgid.u.half[1], + sizeof ( port_id->hca_guid ) ); + + return ib_srp_parse_byte_string ( rp_comp, port_id->hca_guid.u.bytes, + ( sizeof ( port_id->hca_guid ) | + IB_SRP_PARSE_OPTIONAL ) ); +} + +/** + * Parse IB SRP root path destination GID + * + * @v rp_comp Root path component string + * @v rp IB SRP root path + * @ret rc Return status code + */ +static int ib_srp_parse_dgid ( const char *rp_comp, + struct ib_srp_root_path *rp ) { + return ib_srp_parse_byte_string ( rp_comp, rp->ib->dgid.u.bytes, + ( sizeof ( rp->ib->dgid ) | + IB_SRP_PARSE_REQUIRED ) ); +} + +/** + * Parse IB SRP root path partition key + * + * @v rp_comp Root path component string + * @v rp IB SRP root path + * @ret rc Return status code + */ +static int ib_srp_parse_pkey ( const char *rp_comp, + struct ib_srp_root_path *rp ) { + int pkey; + + if ( ( pkey = ib_srp_parse_integer ( rp_comp, IB_PKEY_NONE ) ) < 0 ) + return pkey; + rp->ib->pkey = pkey; + return 0; +} + +/** + * Parse IB SRP root path service ID + * + * @v rp_comp Root path component string + * @v rp IB SRP root path + * @ret rc Return status code + */ +static int ib_srp_parse_service_id ( const char *rp_comp, + struct ib_srp_root_path *rp ) { + return ib_srp_parse_byte_string ( rp_comp, rp->ib->service_id.u.bytes, + ( sizeof ( rp->ib->service_id ) | + IB_SRP_PARSE_REQUIRED ) ); +} + +/** + * Parse IB SRP root path LUN + * + * @v rp_comp Root path component string + * @v rp IB SRP root path + * @ret rc Return status code + */ +static int ib_srp_parse_lun ( const char *rp_comp, + struct ib_srp_root_path *rp ) { + return scsi_parse_lun ( rp_comp, rp->lun ); +} + +/** + * Parse IB SRP root path target identifier extension + * + * @v rp_comp Root path component string + * @v rp IB SRP root path + * @ret rc Return status code + */ +static int ib_srp_parse_target_id_ext ( const char *rp_comp, + struct ib_srp_root_path *rp ) { + struct ib_srp_target_port_id *port_id = + ib_srp_target_port_id ( rp->port_ids ); + + return ib_srp_parse_byte_string ( rp_comp, port_id->id_ext.u.bytes, + ( sizeof ( port_id->id_ext ) | + IB_SRP_PARSE_REQUIRED ) ); +} + +/** + * Parse IB SRP root path target I/O controller GUID + * + * @v rp_comp Root path component string + * @v rp IB SRP root path + * @ret rc Return status code + */ +static int ib_srp_parse_target_ioc_guid ( const char *rp_comp, + struct ib_srp_root_path *rp ) { + struct ib_srp_target_port_id *port_id = + ib_srp_target_port_id ( rp->port_ids ); + + return ib_srp_parse_byte_string ( rp_comp, port_id->ioc_guid.u.bytes, + ( sizeof ( port_id->ioc_guid ) | + IB_SRP_PARSE_REQUIRED ) ); +} + +/** IB SRP root path component parser */ +struct ib_srp_root_path_parser { + /** + * Parse IB SRP root path component + * + * @v rp_comp Root path component string + * @v rp IB SRP root path + * @ret rc Return status code + */ + int ( * parse ) ( const char *rp_comp, struct ib_srp_root_path *rp ); +}; + +/** IB SRP root path components */ +static struct ib_srp_root_path_parser ib_srp_rp_parser[] = { + { ib_srp_parse_literal }, + { ib_srp_parse_sgid }, + { ib_srp_parse_initiator_id_ext }, + { ib_srp_parse_initiator_hca_guid }, + { ib_srp_parse_dgid }, + { ib_srp_parse_pkey }, + { ib_srp_parse_service_id }, + { ib_srp_parse_lun }, + { ib_srp_parse_target_id_ext }, + { ib_srp_parse_target_ioc_guid }, +}; + +/** Number of IB SRP root path components */ +#define IB_SRP_NUM_RP_COMPONENTS \ + ( sizeof ( ib_srp_rp_parser ) / sizeof ( ib_srp_rp_parser[0] ) ) + +/** + * Parse IB SRP root path + * + * @v srp SRP device + * @v rp_string Root path + * @ret rc Return status code + */ +static int ib_srp_parse_root_path ( struct srp_device *srp, + const char *rp_string ) { + struct ib_srp_parameters *ib_params = ib_srp_params ( srp ); + struct ib_srp_root_path rp = { + .lun = &srp->lun, + .port_ids = &srp->port_ids, + .ib = ib_params, + }; + char rp_string_copy[ strlen ( rp_string ) + 1 ]; + char *rp_comp[IB_SRP_NUM_RP_COMPONENTS]; + char *rp_string_tmp = rp_string_copy; + unsigned int i = 0; + int rc; + + /* Split root path into component parts */ + strcpy ( rp_string_copy, rp_string ); + while ( 1 ) { + rp_comp[i++] = rp_string_tmp; + if ( i == IB_SRP_NUM_RP_COMPONENTS ) + break; + for ( ; *rp_string_tmp != ':' ; rp_string_tmp++ ) { + if ( ! *rp_string_tmp ) { + DBGC ( srp, "SRP %p root path \"%s\" too " + "short\n", srp, rp_string ); + return -EINVAL_RP_TOO_SHORT; + } + } + *(rp_string_tmp++) = '\0'; + } + + /* Parse root path components */ + for ( i = 0 ; i < IB_SRP_NUM_RP_COMPONENTS ; i++ ) { + if ( ( rc = ib_srp_rp_parser[i].parse ( rp_comp[i], + &rp ) ) != 0 ) { + DBGC ( srp, "SRP %p could not parse \"%s\" in root " + "path \"%s\": %s\n", srp, rp_comp[i], + rp_string, strerror ( rc ) ); + return rc; + } + } + + return 0; +} + +/** + * Connect IB SRP session + * + * @v srp SRP device + * @ret rc Return status code + */ +static int ib_srp_connect ( struct srp_device *srp ) { + struct ib_srp_parameters *ib_params = ib_srp_params ( srp ); + struct ib_device *ibdev; + int rc; + + /* Identify Infiniband device */ + ibdev = find_ibdev ( &ib_params->sgid ); + if ( ! ibdev ) { + DBGC ( srp, "SRP %p could not identify Infiniband device\n", + srp ); + return -ENODEV; + } + + /* Configure remaining SRP parameters */ + srp->memory_handle = ibdev->rdma_key; + + /* Open CMRC socket */ + if ( ( rc = ib_cmrc_open ( &srp->socket, ibdev, &ib_params->dgid, + &ib_params->service_id ) ) != 0 ) { + DBGC ( srp, "SRP %p could not open CMRC socket: %s\n", + srp, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** IB SRP transport type */ +struct srp_transport_type ib_srp_transport = { + .priv_len = sizeof ( struct ib_srp_parameters ), + .parse_root_path = ib_srp_parse_root_path, + .connect = ib_srp_connect, +};