Home | History | Annotate | Download | only in block
      1 /*
      2  * Copyright (C) 2009 Fen Systems Ltd <mbrown (at) fensystems.co.uk>.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *
      9  *   Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  *   Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in
     14  *   the documentation and/or other materials provided with the
     15  *   distribution.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     20  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     21  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
     22  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
     28  * OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 FILE_LICENCE ( BSD2 );
     32 
     33 #include <stdlib.h>
     34 #include <string.h>
     35 #include <errno.h>
     36 #include <gpxe/scsi.h>
     37 #include <gpxe/xfer.h>
     38 #include <gpxe/features.h>
     39 #include <gpxe/ib_srp.h>
     40 #include <gpxe/srp.h>
     41 
     42 /**
     43  * @file
     44  *
     45  * SCSI RDMA Protocol
     46  *
     47  */
     48 
     49 FEATURE ( FEATURE_PROTOCOL, "SRP", DHCP_EB_FEATURE_SRP, 1 );
     50 
     51 /** Tag to be used for next SRP IU */
     52 static unsigned int srp_tag = 0;
     53 
     54 static void srp_login ( struct srp_device *srp );
     55 static void srp_cmd ( struct srp_device *srp );
     56 
     57 /**
     58  * Mark SRP SCSI command as complete
     59  *
     60  * @v srp		SRP device
     61  * @v rc		Status code
     62  */
     63 static void srp_scsi_done ( struct srp_device *srp, int rc ) {
     64 	if ( srp->command )
     65 		srp->command->rc = rc;
     66 	srp->command = NULL;
     67 }
     68 
     69 /**
     70  * Handle SRP session failure
     71  *
     72  * @v srp		SRP device
     73  * @v rc 		Reason for failure
     74  */
     75 static void srp_fail ( struct srp_device *srp, int rc ) {
     76 
     77 	/* Close underlying socket */
     78 	xfer_close ( &srp->socket, rc );
     79 
     80 	/* Clear session state */
     81 	srp->state = 0;
     82 
     83 	/* If we have reached the retry limit, report the failure */
     84 	if ( srp->retry_count >= SRP_MAX_RETRIES ) {
     85 		srp_scsi_done ( srp, rc );
     86 		return;
     87 	}
     88 
     89 	/* Otherwise, increment the retry count and try to reopen the
     90 	 * connection
     91 	 */
     92 	srp->retry_count++;
     93 	srp_login ( srp );
     94 }
     95 
     96 /**
     97  * Initiate SRP login
     98  *
     99  * @v srp		SRP device
    100  */
    101 static void srp_login ( struct srp_device *srp ) {
    102 	struct io_buffer *iobuf;
    103 	struct srp_login_req *login_req;
    104 	int rc;
    105 
    106 	assert ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) );
    107 
    108 	/* Open underlying socket */
    109 	if ( ( rc = srp->transport->connect ( srp ) ) != 0 ) {
    110 		DBGC ( srp, "SRP %p could not open socket: %s\n",
    111 		       srp, strerror ( rc ) );
    112 		goto err;
    113 	}
    114 	srp->state |= SRP_STATE_SOCKET_OPEN;
    115 
    116 	/* Allocate I/O buffer */
    117 	iobuf = xfer_alloc_iob ( &srp->socket, sizeof ( *login_req ) );
    118 	if ( ! iobuf ) {
    119 		rc = -ENOMEM;
    120 		goto err;
    121 	}
    122 
    123 	/* Construct login request IU */
    124 	login_req = iob_put ( iobuf, sizeof ( *login_req ) );
    125 	memset ( login_req, 0, sizeof ( *login_req ) );
    126 	login_req->type = SRP_LOGIN_REQ;
    127 	login_req->tag.dwords[1] = htonl ( ++srp_tag );
    128 	login_req->max_i_t_iu_len = htonl ( SRP_MAX_I_T_IU_LEN );
    129 	login_req->required_buffer_formats = SRP_LOGIN_REQ_FMT_DDBD;
    130 	memcpy ( &login_req->port_ids, &srp->port_ids,
    131 		 sizeof ( login_req->port_ids ) );
    132 
    133 	DBGC2 ( srp, "SRP %p TX login request tag %08x%08x\n",
    134 		srp, ntohl ( login_req->tag.dwords[0] ),
    135 		ntohl ( login_req->tag.dwords[1] ) );
    136 	DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) );
    137 
    138 	/* Send login request IU */
    139 	if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) {
    140 		DBGC ( srp, "SRP %p could not send login request: %s\n",
    141 		       srp, strerror ( rc ) );
    142 		goto err;
    143 	}
    144 
    145 	return;
    146 
    147  err:
    148 	srp_fail ( srp, rc );
    149 }
    150 
    151 /**
    152  * Handle SRP login response
    153  *
    154  * @v srp		SRP device
    155  * @v iobuf		I/O buffer
    156  * @ret rc		Return status code
    157  */
    158 static int srp_login_rsp ( struct srp_device *srp, struct io_buffer *iobuf ) {
    159 	struct srp_login_rsp *login_rsp = iobuf->data;
    160 	int rc;
    161 
    162 	DBGC2 ( srp, "SRP %p RX login response tag %08x%08x\n",
    163 		srp, ntohl ( login_rsp->tag.dwords[0] ),
    164 		ntohl ( login_rsp->tag.dwords[1] ) );
    165 
    166 	/* Sanity check */
    167 	if ( iob_len ( iobuf ) < sizeof ( *login_rsp ) ) {
    168 		DBGC ( srp, "SRP %p RX login response too short (%zd bytes)\n",
    169 		       srp, iob_len ( iobuf ) );
    170 		rc = -EINVAL;
    171 		goto out;
    172 	}
    173 
    174 	DBGC ( srp, "SRP %p logged in\n", srp );
    175 
    176 	/* Mark as logged in */
    177 	srp->state |= SRP_STATE_LOGGED_IN;
    178 
    179 	/* Reset error counter */
    180 	srp->retry_count = 0;
    181 
    182 	/* Issue pending command */
    183 	srp_cmd ( srp );
    184 
    185 	rc = 0;
    186  out:
    187 	free_iob ( iobuf );
    188 	return rc;
    189 }
    190 
    191 /**
    192  * Handle SRP login rejection
    193  *
    194  * @v srp		SRP device
    195  * @v iobuf		I/O buffer
    196  * @ret rc		Return status code
    197  */
    198 static int srp_login_rej ( struct srp_device *srp, struct io_buffer *iobuf ) {
    199 	struct srp_login_rej *login_rej = iobuf->data;
    200 	int rc;
    201 
    202 	DBGC2 ( srp, "SRP %p RX login rejection tag %08x%08x\n",
    203 		srp, ntohl ( login_rej->tag.dwords[0] ),
    204 		ntohl ( login_rej->tag.dwords[1] ) );
    205 
    206 	/* Sanity check */
    207 	if ( iob_len ( iobuf ) < sizeof ( *login_rej ) ) {
    208 		DBGC ( srp, "SRP %p RX login rejection too short (%zd "
    209 		       "bytes)\n", srp, iob_len ( iobuf ) );
    210 		rc = -EINVAL;
    211 		goto out;
    212 	}
    213 
    214 	/* Login rejection always indicates an error */
    215 	DBGC ( srp, "SRP %p login rejected (reason %08x)\n",
    216 	       srp, ntohl ( login_rej->reason ) );
    217 	rc = -EPERM;
    218 
    219  out:
    220 	free_iob ( iobuf );
    221 	return rc;
    222 }
    223 
    224 /**
    225  * Transmit SRP SCSI command
    226  *
    227  * @v srp		SRP device
    228  */
    229 static void srp_cmd ( struct srp_device *srp ) {
    230 	struct io_buffer *iobuf;
    231 	struct srp_cmd *cmd;
    232 	struct srp_memory_descriptor *data_out;
    233 	struct srp_memory_descriptor *data_in;
    234 	int rc;
    235 
    236 	assert ( srp->state & SRP_STATE_LOGGED_IN );
    237 
    238 	/* Allocate I/O buffer */
    239 	iobuf = xfer_alloc_iob ( &srp->socket, SRP_MAX_I_T_IU_LEN );
    240 	if ( ! iobuf ) {
    241 		rc = -ENOMEM;
    242 		goto err;
    243 	}
    244 
    245 	/* Construct base portion */
    246 	cmd = iob_put ( iobuf, sizeof ( *cmd ) );
    247 	memset ( cmd, 0, sizeof ( *cmd ) );
    248 	cmd->type = SRP_CMD;
    249 	cmd->tag.dwords[1] = htonl ( ++srp_tag );
    250 	cmd->lun = srp->lun;
    251 	memcpy ( &cmd->cdb, &srp->command->cdb, sizeof ( cmd->cdb ) );
    252 
    253 	/* Construct data-out descriptor, if present */
    254 	if ( srp->command->data_out ) {
    255 		cmd->data_buffer_formats |= SRP_CMD_DO_FMT_DIRECT;
    256 		data_out = iob_put ( iobuf, sizeof ( *data_out ) );
    257 		data_out->address =
    258 		    cpu_to_be64 ( user_to_phys ( srp->command->data_out, 0 ) );
    259 		data_out->handle = ntohl ( srp->memory_handle );
    260 		data_out->len = ntohl ( srp->command->data_out_len );
    261 	}
    262 
    263 	/* Construct data-in descriptor, if present */
    264 	if ( srp->command->data_in ) {
    265 		cmd->data_buffer_formats |= SRP_CMD_DI_FMT_DIRECT;
    266 		data_in = iob_put ( iobuf, sizeof ( *data_in ) );
    267 		data_in->address =
    268 		     cpu_to_be64 ( user_to_phys ( srp->command->data_in, 0 ) );
    269 		data_in->handle = ntohl ( srp->memory_handle );
    270 		data_in->len = ntohl ( srp->command->data_in_len );
    271 	}
    272 
    273 	DBGC2 ( srp, "SRP %p TX SCSI command tag %08x%08x\n", srp,
    274 		ntohl ( cmd->tag.dwords[0] ), ntohl ( cmd->tag.dwords[1] ) );
    275 	DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) );
    276 
    277 	/* Send IU */
    278 	if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) {
    279 		DBGC ( srp, "SRP %p could not send command: %s\n",
    280 		       srp, strerror ( rc ) );
    281 		goto err;
    282 	}
    283 
    284 	return;
    285 
    286  err:
    287 	srp_fail ( srp, rc );
    288 }
    289 
    290 /**
    291  * Handle SRP SCSI response
    292  *
    293  * @v srp		SRP device
    294  * @v iobuf		I/O buffer
    295  * @ret rc		Returns status code
    296  */
    297 static int srp_rsp ( struct srp_device *srp, struct io_buffer *iobuf ) {
    298 	struct srp_rsp *rsp = iobuf->data;
    299 	int rc;
    300 
    301 	DBGC2 ( srp, "SRP %p RX SCSI response tag %08x%08x\n", srp,
    302 		ntohl ( rsp->tag.dwords[0] ), ntohl ( rsp->tag.dwords[1] ) );
    303 
    304 	/* Sanity check */
    305 	if ( iob_len ( iobuf ) < sizeof ( *rsp ) ) {
    306 		DBGC ( srp, "SRP %p RX SCSI response too short (%zd bytes)\n",
    307 		       srp, iob_len ( iobuf ) );
    308 		rc = -EINVAL;
    309 		goto out;
    310 	}
    311 
    312 	/* Report SCSI errors */
    313 	if ( rsp->status != 0 ) {
    314 		DBGC ( srp, "SRP %p response status %02x\n",
    315 		       srp, rsp->status );
    316 		if ( srp_rsp_sense_data ( rsp ) ) {
    317 			DBGC ( srp, "SRP %p sense data:\n", srp );
    318 			DBGC_HDA ( srp, 0, srp_rsp_sense_data ( rsp ),
    319 				   srp_rsp_sense_data_len ( rsp ) );
    320 		}
    321 	}
    322 	if ( rsp->valid & ( SRP_RSP_VALID_DOUNDER | SRP_RSP_VALID_DOOVER ) ) {
    323 		DBGC ( srp, "SRP %p response data-out %srun by %#x bytes\n",
    324 		       srp, ( ( rsp->valid & SRP_RSP_VALID_DOUNDER )
    325 			      ? "under" : "over" ),
    326 		       ntohl ( rsp->data_out_residual_count ) );
    327 	}
    328 	if ( rsp->valid & ( SRP_RSP_VALID_DIUNDER | SRP_RSP_VALID_DIOVER ) ) {
    329 		DBGC ( srp, "SRP %p response data-in %srun by %#x bytes\n",
    330 		       srp, ( ( rsp->valid & SRP_RSP_VALID_DIUNDER )
    331 			      ? "under" : "over" ),
    332 		       ntohl ( rsp->data_in_residual_count ) );
    333 	}
    334 	srp->command->status = rsp->status;
    335 
    336 	/* Mark SCSI command as complete */
    337 	srp_scsi_done ( srp, 0 );
    338 
    339 	rc = 0;
    340  out:
    341 	free_iob ( iobuf );
    342 	return rc;
    343 }
    344 
    345 /**
    346  * Handle SRP unrecognised response
    347  *
    348  * @v srp		SRP device
    349  * @v iobuf		I/O buffer
    350  * @ret rc		Returns status code
    351  */
    352 static int srp_unrecognised ( struct srp_device *srp,
    353 			      struct io_buffer *iobuf ) {
    354 	struct srp_common *common = iobuf->data;
    355 
    356 	DBGC ( srp, "SRP %p RX unrecognised IU tag %08x%08x type %02x\n",
    357 	       srp, ntohl ( common->tag.dwords[0] ),
    358 	       ntohl ( common->tag.dwords[1] ), common->type );
    359 
    360 	free_iob ( iobuf );
    361 	return -ENOTSUP;
    362 }
    363 
    364 /**
    365  * Receive data from underlying socket
    366  *
    367  * @v xfer		Data transfer interface
    368  * @v iobuf		Datagram I/O buffer
    369  * @v meta		Data transfer metadata
    370  * @ret rc		Return status code
    371  */
    372 static int srp_xfer_deliver_iob ( struct xfer_interface *xfer,
    373 				  struct io_buffer *iobuf,
    374 				  struct xfer_metadata *meta __unused ) {
    375 	struct srp_device *srp =
    376 		container_of ( xfer, struct srp_device, socket );
    377 	struct srp_common *common = iobuf->data;
    378 	int ( * type ) ( struct srp_device *srp, struct io_buffer *iobuf );
    379 	int rc;
    380 
    381 	/* Determine IU type */
    382 	switch ( common->type ) {
    383 	case SRP_LOGIN_RSP:
    384 		type = srp_login_rsp;
    385 		break;
    386 	case SRP_LOGIN_REJ:
    387 		type = srp_login_rej;
    388 		break;
    389 	case SRP_RSP:
    390 		type = srp_rsp;
    391 		break;
    392 	default:
    393 		type = srp_unrecognised;
    394 		break;
    395 	}
    396 
    397 	/* Handle IU */
    398 	if ( ( rc = type ( srp, iobuf ) ) != 0 )
    399 		goto err;
    400 
    401 	return 0;
    402 
    403  err:
    404 	srp_fail ( srp, rc );
    405 	return rc;
    406 }
    407 
    408 /**
    409  * Underlying socket closed
    410  *
    411  * @v xfer		Data transfer interface
    412  * @v rc		Reason for close
    413  */
    414 static void srp_xfer_close ( struct xfer_interface *xfer, int rc ) {
    415 	struct srp_device *srp =
    416 		container_of ( xfer, struct srp_device, socket );
    417 
    418 	DBGC ( srp, "SRP %p socket closed: %s\n", srp, strerror ( rc ) );
    419 
    420 	srp_fail ( srp, rc );
    421 }
    422 
    423 /** SRP data transfer interface operations */
    424 static struct xfer_interface_operations srp_xfer_operations = {
    425 	.close		= srp_xfer_close,
    426 	.vredirect	= ignore_xfer_vredirect,
    427 	.window		= unlimited_xfer_window,
    428 	.alloc_iob	= default_xfer_alloc_iob,
    429 	.deliver_iob	= srp_xfer_deliver_iob,
    430 	.deliver_raw	= xfer_deliver_as_iob,
    431 };
    432 
    433 /**
    434  * Issue SCSI command via SRP
    435  *
    436  * @v scsi		SCSI device
    437  * @v command		SCSI command
    438  * @ret rc		Return status code
    439  */
    440 static int srp_command ( struct scsi_device *scsi,
    441 			 struct scsi_command *command ) {
    442 	struct srp_device *srp =
    443 		container_of ( scsi->backend, struct srp_device, refcnt );
    444 
    445 	/* Store SCSI command */
    446 	if ( srp->command ) {
    447 		DBGC ( srp, "SRP %p cannot handle concurrent SCSI commands\n",
    448 		       srp );
    449 		return -EBUSY;
    450 	}
    451 	srp->command = command;
    452 
    453 	/* Log in or issue command as appropriate */
    454 	if ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) ) {
    455 		srp_login ( srp );
    456 	} else if ( srp->state & SRP_STATE_LOGGED_IN ) {
    457 		srp_cmd ( srp );
    458 	} else {
    459 		/* Still waiting for login; do nothing */
    460 	}
    461 
    462 	return 0;
    463 }
    464 
    465 /**
    466  * Attach SRP device
    467  *
    468  * @v scsi		SCSI device
    469  * @v root_path		Root path
    470  */
    471 int srp_attach ( struct scsi_device *scsi, const char *root_path ) {
    472 	struct srp_transport_type *transport;
    473 	struct srp_device *srp;
    474 	int rc;
    475 
    476 	/* Hard-code an IB SRP back-end for now */
    477 	transport = &ib_srp_transport;
    478 
    479 	/* Allocate and initialise structure */
    480 	srp = zalloc ( sizeof ( *srp ) + transport->priv_len );
    481 	if ( ! srp ) {
    482 		rc = -ENOMEM;
    483 		goto err_alloc;
    484 	}
    485 	xfer_init ( &srp->socket, &srp_xfer_operations, &srp->refcnt );
    486 	srp->transport = transport;
    487 	DBGC ( srp, "SRP %p using %s\n", srp, root_path );
    488 
    489 	/* Parse root path */
    490 	if ( ( rc = transport->parse_root_path ( srp, root_path ) ) != 0 ) {
    491 		DBGC ( srp, "SRP %p could not parse root path: %s\n",
    492 		       srp, strerror ( rc ) );
    493 		goto err_parse_root_path;
    494 	}
    495 
    496 	/* Attach parent interface, mortalise self, and return */
    497 	scsi->backend = ref_get ( &srp->refcnt );
    498 	scsi->command = srp_command;
    499 	ref_put ( &srp->refcnt );
    500 	return 0;
    501 
    502  err_parse_root_path:
    503 	ref_put ( &srp->refcnt );
    504  err_alloc:
    505 	return rc;
    506 }
    507 
    508 /**
    509  * Detach SRP device
    510  *
    511  * @v scsi		SCSI device
    512  */
    513 void srp_detach ( struct scsi_device *scsi ) {
    514 	struct srp_device *srp =
    515 		container_of ( scsi->backend, struct srp_device, refcnt );
    516 
    517 	/* Close socket */
    518 	xfer_nullify ( &srp->socket );
    519 	xfer_close ( &srp->socket, 0 );
    520 	scsi->command = scsi_detached_command;
    521 	ref_put ( scsi->backend );
    522 	scsi->backend = NULL;
    523 }
    524