123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523 |
- /*
- * 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 <gpxe/scsi.h>
- #include <gpxe/xfer.h>
- #include <gpxe/features.h>
- #include <gpxe/ib_srp.h>
- #include <gpxe/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;
- }
|