| /* |
| * Copyright (C) 2009 Fen Systems Ltd <mbrown@fensystems.co.uk>. |
| * 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 <stdlib.h> |
| #include <errno.h> |
| #include <gpxe/srp.h> |
| #include <gpxe/infiniband.h> |
| #include <gpxe/ib_cmrc.h> |
| #include <gpxe/ib_srp.h> |
| |
| /** |
| * @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_DEFAULT ) ) < 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, |
| }; |