david/ipxe
Archived
1
0
This repository has been archived on 2020-12-06. You can view files and clone it, but cannot push or open issues or pull requests.
ipxe/src/drivers/block/srp.c
Michael Brown 8406115834 [build] Rename gPXE to iPXE
Access to the gpxe.org and etherboot.org domains and associated
resources has been revoked by the registrant of the domain.  Work
around this problem by renaming project from gPXE to iPXE, and
updating URLs to match.

Also update README, LOG and COPYRIGHTS to remove obsolete information.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-04-19 23:43:39 +01:00

524 lines
13 KiB
C

/*
* 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 <string.h>
#include <errno.h>
#include <ipxe/scsi.h>
#include <ipxe/xfer.h>
#include <ipxe/features.h>
#include <ipxe/ib_srp.h>
#include <ipxe/srp.h>
/**
* @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;
/* If we have reached the retry limit, report the failure */
if ( srp->retry_count >= SRP_MAX_RETRIES ) {
srp_scsi_done ( srp, rc );
return;
}
/* Otherwise, increment the retry count and try to reopen the
* connection
*/
srp->retry_count++;
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 );
/* 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;
}