/*
* 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,
};