iPXE
srp.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2009 Fen Systems Ltd <mbrown@fensystems.co.uk>.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  *   Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  *
00012  *   Redistributions in binary form must reproduce the above copyright
00013  *   notice, this list of conditions and the following disclaimer in
00014  *   the documentation and/or other materials provided with the
00015  *   distribution.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00018  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00021  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
00022  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00023  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
00024  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00025  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
00026  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00027  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
00028  * OF THE POSSIBILITY OF SUCH DAMAGE.
00029  */
00030 
00031 FILE_LICENCE ( BSD2 );
00032 
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <errno.h>
00036 #include <ipxe/scsi.h>
00037 #include <ipxe/xfer.h>
00038 #include <ipxe/features.h>
00039 #include <ipxe/srp.h>
00040 
00041 /**
00042  * @file
00043  *
00044  * SCSI RDMA Protocol
00045  *
00046  */
00047 
00048 FEATURE ( FEATURE_PROTOCOL, "SRP", DHCP_EB_FEATURE_SRP, 1 );
00049 
00050 /** Maximum length of any initiator-to-target IU that we will send
00051  *
00052  * The longest IU is a SRP_CMD with no additional CDB and two direct
00053  * data buffer descriptors, which comes to 80 bytes.
00054  */
00055 #define SRP_MAX_I_T_IU_LEN 80
00056 
00057 /* Error numbers generated by SRP login rejection */
00058 #define EINFO_SRP_LOGIN_REJ( reason, desc )                                   \
00059         __einfo_uniqify ( EINFO_EPERM, ( (reason) & 0x0f ), desc )
00060 #define EPERM_UNKNOWN                                                         \
00061         __einfo_error ( EINFO_EPERM_UNKNOWN )
00062 #define EINFO_EPERM_UNKNOWN EINFO_SRP_LOGIN_REJ (                             \
00063         SRP_LOGIN_REJ_REASON_UNKNOWN,                                         \
00064         "Unable to establish RDMA channel, no reason specified" )
00065 #define EPERM_INSUFFICIENT_RESOURCES                                          \
00066         __einfo_error ( EINFO_EPERM_INSUFFICIENT_RESOURCES )
00067 #define EINFO_EPERM_INSUFFICIENT_RESOURCES EINFO_SRP_LOGIN_REJ (              \
00068         SRP_LOGIN_REJ_REASON_INSUFFICIENT_RESOURCES,                          \
00069         "Insufficient RDMA channel resources" )
00070 #define EPERM_BAD_MAX_I_T_IU_LEN                                              \
00071         __einfo_error ( EINFO_EPERM_BAD_MAX_I_T_IU_LEN )
00072 #define EINFO_EPERM_BAD_MAX_I_T_IU_LEN EINFO_SRP_LOGIN_REJ (                  \
00073         SRP_LOGIN_REJ_REASON_BAD_MAX_I_T_IU_LEN,                              \
00074         "Requested maximum initiator to target IU length value too large" )
00075 #define EPERM_CANNOT_ASSOCIATE                                                \
00076         __einfo_error ( EINFO_EPERM_CANNOT_ASSOCIATE )
00077 #define EINFO_EPERM_CANNOT_ASSOCIATE EINFO_SRP_LOGIN_REJ (                    \
00078         SRP_LOGIN_REJ_REASON_CANNOT_ASSOCIATE,                                \
00079         "Unable to associate RDMA channel with specified I_T nexus" )
00080 #define EPERM_UNSUPPORTED_BUFFER_FORMAT                                       \
00081         __einfo_error ( EINFO_EPERM_UNSUPPORTED_BUFFER_FORMAT )
00082 #define EINFO_EPERM_UNSUPPORTED_BUFFER_FORMAT EINFO_SRP_LOGIN_REJ (           \
00083         SRP_LOGIN_REJ_REASON_UNSUPPORTED_BUFFER_FORMAT,                       \
00084         "One or more requested data buffer descriptor formats not supported" )
00085 #define EPERM_NO_MULTIPLE_CHANNELS                                            \
00086         __einfo_error ( EINFO_EPERM_NO_MULTIPLE_CHANNELS )
00087 #define EINFO_EPERM_NO_MULTIPLE_CHANNELS EINFO_SRP_LOGIN_REJ (                \
00088         SRP_LOGIN_REJ_REASON_NO_MULTIPLE_CHANNELS,                            \
00089         "SRP target does not support multiple RDMA channels per I_T nexus" )
00090 #define EPERM_NO_MORE_CHANNELS                                                \
00091         __einfo_error ( EINFO_EPERM_NO_MORE_CHANNELS )
00092 #define EINFO_EPERM_NO_MORE_CHANNELS EINFO_SRP_LOGIN_REJ (                    \
00093         SRP_LOGIN_REJ_REASON_NO_MORE_CHANNELS,                                \
00094         "RDMA channel limit reached for this initiator" )
00095 #define EPERM_LOGIN_REJ( reason_nibble )                                      \
00096         EUNIQ ( EINFO_EPERM, (reason_nibble), EPERM_UNKNOWN,                  \
00097                 EPERM_INSUFFICIENT_RESOURCES, EPERM_BAD_MAX_I_T_IU_LEN,       \
00098                 EPERM_CANNOT_ASSOCIATE, EPERM_UNSUPPORTED_BUFFER_FORMAT,      \
00099                 EPERM_NO_MULTIPLE_CHANNELS, EPERM_NO_MORE_CHANNELS )
00100 
00101 /** An SRP device */
00102 struct srp_device {
00103         /** Reference count */
00104         struct refcnt refcnt;
00105 
00106         /** SCSI command issuing interface */
00107         struct interface scsi;
00108         /** Underlying data transfer interface */
00109         struct interface socket;
00110 
00111         /** RDMA memory handle */
00112         uint32_t memory_handle;
00113         /** Login completed successfully */
00114         int logged_in;
00115 
00116         /** List of active commands */
00117         struct list_head commands;
00118 };
00119 
00120 /** An SRP command */
00121 struct srp_command {
00122         /** Reference count */
00123         struct refcnt refcnt;
00124         /** SRP device */
00125         struct srp_device *srpdev;
00126         /** List of active commands */
00127         struct list_head list;
00128 
00129         /** SCSI command interface */
00130         struct interface scsi;
00131         /** Command tag */
00132         uint32_t tag;
00133 };
00134 
00135 /**
00136  * Get reference to SRP device
00137  *
00138  * @v srpdev            SRP device
00139  * @ret srpdev          SRP device
00140  */
00141 static inline __attribute__ (( always_inline )) struct srp_device *
00142 srpdev_get ( struct srp_device *srpdev ) {
00143         ref_get ( &srpdev->refcnt );
00144         return srpdev;
00145 }
00146 
00147 /**
00148  * Drop reference to SRP device
00149  *
00150  * @v srpdev            SRP device
00151  */
00152 static inline __attribute__ (( always_inline )) void
00153 srpdev_put ( struct srp_device *srpdev ) {
00154         ref_put ( &srpdev->refcnt );
00155 }
00156 
00157 /**
00158  * Get reference to SRP command
00159  *
00160  * @v srpcmd            SRP command
00161  * @ret srpcmd          SRP command
00162  */
00163 static inline __attribute__ (( always_inline )) struct srp_command *
00164 srpcmd_get ( struct srp_command *srpcmd ) {
00165         ref_get ( &srpcmd->refcnt );
00166         return srpcmd;
00167 }
00168 
00169 /**
00170  * Drop reference to SRP command
00171  *
00172  * @v srpcmd            SRP command
00173  */
00174 static inline __attribute__ (( always_inline )) void
00175 srpcmd_put ( struct srp_command *srpcmd ) {
00176         ref_put ( &srpcmd->refcnt );
00177 }
00178 
00179 /**
00180  * Free SRP command
00181  *
00182  * @v refcnt            Reference count
00183  */
00184 static void srpcmd_free ( struct refcnt *refcnt ) {
00185         struct srp_command *srpcmd =
00186                 container_of ( refcnt, struct srp_command, refcnt );
00187 
00188         assert ( list_empty ( &srpcmd->list ) );
00189 
00190         srpdev_put ( srpcmd->srpdev );
00191         free ( srpcmd );
00192 }
00193 
00194 /**
00195  * Close SRP command
00196  *
00197  * @v srpcmd            SRP command
00198  * @v rc                Reason for close
00199  */
00200 static void srpcmd_close ( struct srp_command *srpcmd, int rc ) {
00201         struct srp_device *srpdev = srpcmd->srpdev;
00202 
00203         if ( rc != 0 ) {
00204                 DBGC ( srpdev, "SRP %p tag %08x closed: %s\n",
00205                        srpdev, srpcmd->tag, strerror ( rc ) );
00206         }
00207 
00208         /* Remove from list of commands */
00209         if ( ! list_empty ( &srpcmd->list ) ) {
00210                 list_del ( &srpcmd->list );
00211                 INIT_LIST_HEAD ( &srpcmd->list );
00212                 srpcmd_put ( srpcmd );
00213         }
00214 
00215         /* Shut down interfaces */
00216         intf_shutdown ( &srpcmd->scsi, rc );
00217 }
00218 
00219 /**
00220  * Close SRP device
00221  *
00222  * @v srpdev            SRP device
00223  * @v rc                Reason for close
00224  */
00225 static void srpdev_close ( struct srp_device *srpdev, int rc ) {
00226         struct srp_command *srpcmd;
00227         struct srp_command *tmp;
00228 
00229         if ( rc != 0 ) {
00230                 DBGC ( srpdev, "SRP %p closed: %s\n",
00231                        srpdev, strerror ( rc ) );
00232         }
00233 
00234         /* Shut down interfaces */
00235         intf_shutdown ( &srpdev->socket, rc );
00236         intf_shutdown ( &srpdev->scsi, rc );
00237 
00238         /* Shut down any active commands */
00239         list_for_each_entry_safe ( srpcmd, tmp, &srpdev->commands, list ) {
00240                 srpcmd_get ( srpcmd );
00241                 srpcmd_close ( srpcmd, rc );
00242                 srpcmd_put ( srpcmd );
00243         }
00244 }
00245 
00246 /**
00247  * Identify SRP command by tag
00248  *
00249  * @v srpdev            SRP device
00250  * @v tag               Command tag
00251  * @ret srpcmd          SRP command, or NULL
00252  */
00253 static struct srp_command * srp_find_tag ( struct srp_device *srpdev,
00254                                            uint32_t tag ) {
00255         struct srp_command *srpcmd;
00256 
00257         list_for_each_entry ( srpcmd, &srpdev->commands, list ) {
00258                 if ( srpcmd->tag == tag )
00259                         return srpcmd;
00260         }
00261         return NULL;
00262 }
00263 
00264 /**
00265  * Choose an SRP command tag
00266  *
00267  * @v srpdev            SRP device
00268  * @ret tag             New tag, or negative error
00269  */
00270 static int srp_new_tag ( struct srp_device *srpdev ) {
00271         static uint16_t tag_idx;
00272         unsigned int i;
00273 
00274         for ( i = 0 ; i < 65536 ; i++ ) {
00275                 tag_idx++;
00276                 if ( srp_find_tag ( srpdev, tag_idx ) == NULL )
00277                         return tag_idx;
00278         }
00279         return -EADDRINUSE;
00280 }
00281 
00282 /**
00283  * Transmit SRP login request
00284  *
00285  * @v srpdev            SRP device
00286  * @v initiator         Initiator port ID
00287  * @v target            Target port ID
00288  * @v tag               Command tag
00289  * @ret rc              Return status code
00290  */
00291 static int srp_login ( struct srp_device *srpdev, union srp_port_id *initiator,
00292                        union srp_port_id *target, uint32_t tag ) {
00293         struct io_buffer *iobuf;
00294         struct srp_login_req *login_req;
00295         int rc;
00296 
00297         /* Allocate I/O buffer */
00298         iobuf = xfer_alloc_iob ( &srpdev->socket, sizeof ( *login_req ) );
00299         if ( ! iobuf )
00300                 return -ENOMEM;
00301 
00302         /* Construct login request IU */
00303         login_req = iob_put ( iobuf, sizeof ( *login_req ) );
00304         memset ( login_req, 0, sizeof ( *login_req ) );
00305         login_req->type = SRP_LOGIN_REQ;
00306         login_req->tag.dwords[0] = htonl ( SRP_TAG_MAGIC );
00307         login_req->tag.dwords[1] = htonl ( tag );
00308         login_req->max_i_t_iu_len = htonl ( SRP_MAX_I_T_IU_LEN );
00309         login_req->required_buffer_formats = SRP_LOGIN_REQ_FMT_DDBD;
00310         memcpy ( &login_req->initiator, initiator,
00311                  sizeof ( login_req->initiator ) );
00312         memcpy ( &login_req->target, target, sizeof ( login_req->target ) );
00313 
00314         DBGC ( srpdev, "SRP %p tag %08x LOGIN_REQ:\n", srpdev, tag );
00315         DBGC_HDA ( srpdev, 0, iobuf->data, iob_len ( iobuf ) );
00316 
00317         /* Send login request IU */
00318         if ( ( rc = xfer_deliver_iob ( &srpdev->socket, iobuf ) ) != 0 ) {
00319                 DBGC ( srpdev, "SRP %p tag %08x could not send LOGIN_REQ: "
00320                        "%s\n", srpdev, tag, strerror ( rc ) );
00321                 return rc;
00322         }
00323 
00324         return 0;
00325 }
00326 
00327 /**
00328  * Receive SRP login response
00329  *
00330  * @v srpdev            SRP device
00331  * @v data              SRP IU
00332  * @v len               Length of SRP IU
00333  * @ret rc              Return status code
00334  */
00335 static int srp_login_rsp ( struct srp_device *srpdev,
00336                            const void *data, size_t len ) {
00337         const struct srp_login_rsp *login_rsp = data;
00338 
00339         /* Sanity check */
00340         if ( len < sizeof ( *login_rsp ) ) {
00341                 DBGC ( srpdev, "SRP %p LOGIN_RSP too short (%zd bytes)\n",
00342                        srpdev, len );
00343                 return -EINVAL;
00344         }
00345         DBGC ( srpdev, "SRP %p tag %08x LOGIN_RSP:\n",
00346                srpdev, ntohl ( login_rsp->tag.dwords[1] ) );
00347         DBGC_HDA ( srpdev, 0, data, len );
00348 
00349         /* Mark as logged in */
00350         srpdev->logged_in = 1;
00351         DBGC ( srpdev, "SRP %p logged in\n", srpdev );
00352 
00353         /* Notify of window change */
00354         xfer_window_changed ( &srpdev->scsi );
00355 
00356         return 0;
00357 }
00358 
00359 /**
00360  * Receive SRP login rejection
00361  *
00362  * @v srpdev            SRP device
00363  * @v data              SRP IU
00364  * @v len               Length of SRP IU
00365  * @ret rc              Return status code
00366  */
00367 static int srp_login_rej ( struct srp_device *srpdev,
00368                            const void *data, size_t len ) {
00369         const struct srp_login_rej *login_rej = data;
00370         uint32_t reason;
00371 
00372         /* Sanity check */
00373         if ( len < sizeof ( *login_rej ) ) {
00374                 DBGC ( srpdev, "SRP %p LOGIN_REJ too short (%zd bytes)\n",
00375                        srpdev, len );
00376                 return -EINVAL;
00377         }
00378         reason = ntohl ( login_rej->reason );
00379         DBGC ( srpdev, "SRP %p tag %08x LOGIN_REJ reason %08x:\n",
00380                srpdev, ntohl ( login_rej->tag.dwords[1] ), reason );
00381         DBGC_HDA ( srpdev, 0, data, len );
00382 
00383         /* Login rejection always indicates an error */
00384         return ( SRP_LOGIN_REJ_REASON_DEFINED ( reason ) ?
00385                  -EPERM_LOGIN_REJ ( reason ) : -EACCES );
00386 }
00387 
00388 /**
00389  * Transmit SRP SCSI command
00390  *
00391  * @v srpdev            SRP device
00392  * @v command           SCSI command
00393  * @v tag               Command tag
00394  * @ret rc              Return status code
00395  */
00396 static int srp_cmd ( struct srp_device *srpdev,
00397                      struct scsi_cmd *command,
00398                      uint32_t tag ) {
00399         struct io_buffer *iobuf;
00400         struct srp_cmd *cmd;
00401         struct srp_memory_descriptor *data_out;
00402         struct srp_memory_descriptor *data_in;
00403         int rc;
00404 
00405         /* Sanity check */
00406         if ( ! srpdev->logged_in ) {
00407                 DBGC ( srpdev, "SRP %p tag %08x cannot send CMD before "
00408                        "login completes\n", srpdev, tag );
00409                 return -EBUSY;
00410         }
00411 
00412         /* Allocate I/O buffer */
00413         iobuf = xfer_alloc_iob ( &srpdev->socket, SRP_MAX_I_T_IU_LEN );
00414         if ( ! iobuf )
00415                 return -ENOMEM;
00416 
00417         /* Construct base portion */
00418         cmd = iob_put ( iobuf, sizeof ( *cmd ) );
00419         memset ( cmd, 0, sizeof ( *cmd ) );
00420         cmd->type = SRP_CMD;
00421         cmd->tag.dwords[0] = htonl ( SRP_TAG_MAGIC );
00422         cmd->tag.dwords[1] = htonl ( tag );
00423         memcpy ( &cmd->lun, &command->lun, sizeof ( cmd->lun ) );
00424         memcpy ( &cmd->cdb, &command->cdb, sizeof ( cmd->cdb ) );
00425 
00426         /* Construct data-out descriptor, if present */
00427         if ( command->data_out ) {
00428                 cmd->data_buffer_formats |= SRP_CMD_DO_FMT_DIRECT;
00429                 data_out = iob_put ( iobuf, sizeof ( *data_out ) );
00430                 data_out->address =
00431                     cpu_to_be64 ( user_to_phys ( command->data_out, 0 ) );
00432                 data_out->handle = ntohl ( srpdev->memory_handle );
00433                 data_out->len = ntohl ( command->data_out_len );
00434         }
00435 
00436         /* Construct data-in descriptor, if present */
00437         if ( command->data_in ) {
00438                 cmd->data_buffer_formats |= SRP_CMD_DI_FMT_DIRECT;
00439                 data_in = iob_put ( iobuf, sizeof ( *data_in ) );
00440                 data_in->address =
00441                      cpu_to_be64 ( user_to_phys ( command->data_in, 0 ) );
00442                 data_in->handle = ntohl ( srpdev->memory_handle );
00443                 data_in->len = ntohl ( command->data_in_len );
00444         }
00445 
00446         DBGC2 ( srpdev, "SRP %p tag %08x CMD " SCSI_CDB_FORMAT "\n",
00447                 srpdev, tag, SCSI_CDB_DATA ( cmd->cdb ) );
00448 
00449         /* Send IU */
00450         if ( ( rc = xfer_deliver_iob ( &srpdev->socket, iobuf ) ) != 0 ) {
00451                 DBGC ( srpdev, "SRP %p tag %08x could not send CMD: %s\n",
00452                        srpdev, tag, strerror ( rc ) );
00453                 return rc;
00454         }
00455 
00456         return 0;
00457 }
00458 
00459 /**
00460  * Receive SRP SCSI response
00461  *
00462  * @v srpdev            SRP device
00463  * @v data              SRP IU
00464  * @v len               Length of SRP IU
00465  * @ret rc              Returns status code
00466  */
00467 static int srp_rsp ( struct srp_device *srpdev,
00468                      const void *data, size_t len ) {
00469         const struct srp_rsp *rsp = data;
00470         struct srp_command *srpcmd;
00471         struct scsi_rsp response;
00472         ssize_t data_out_residual_count;
00473         ssize_t data_in_residual_count;
00474 
00475         /* Sanity check */
00476         if ( ( len < sizeof ( *rsp ) ) ||
00477              ( len < ( sizeof ( *rsp ) +
00478                        srp_rsp_response_data_len ( rsp ) +
00479                        srp_rsp_sense_data_len ( rsp ) ) ) ) {
00480                 DBGC ( srpdev, "SRP %p RSP too short (%zd bytes)\n",
00481                        srpdev, len );
00482                 return -EINVAL;
00483         }
00484         DBGC2 ( srpdev, "SRP %p tag %08x RSP stat %02x dores %08x dires "
00485                 "%08x valid %02x%s%s%s%s%s%s\n",
00486                 srpdev, ntohl ( rsp->tag.dwords[1] ), rsp->status,
00487                 ntohl ( rsp->data_out_residual_count ),
00488                 ntohl ( rsp->data_in_residual_count ), rsp->valid,
00489                 ( ( rsp->valid & SRP_RSP_VALID_DIUNDER ) ? " diunder" : "" ),
00490                 ( ( rsp->valid & SRP_RSP_VALID_DIOVER ) ? " diover" : "" ),
00491                 ( ( rsp->valid & SRP_RSP_VALID_DOUNDER ) ? " dounder" : "" ),
00492                 ( ( rsp->valid & SRP_RSP_VALID_DOOVER ) ? " doover" : "" ),
00493                 ( ( rsp->valid & SRP_RSP_VALID_SNSVALID ) ? " sns" : "" ),
00494                 ( ( rsp->valid & SRP_RSP_VALID_RSPVALID ) ? " rsp" : "" ) );
00495 
00496         /* Identify command by tag */
00497         srpcmd = srp_find_tag ( srpdev, ntohl ( rsp->tag.dwords[1] ) );
00498         if ( ! srpcmd ) {
00499                 DBGC ( srpdev, "SRP %p tag %08x unrecognised RSP\n",
00500                        srpdev, ntohl ( rsp->tag.dwords[1] ) );
00501                 return -ENOENT;
00502         }
00503 
00504         /* Hold command reference for remainder of function */
00505         srpcmd_get ( srpcmd );
00506 
00507         /* Build SCSI response */
00508         memset ( &response, 0, sizeof ( response ) );
00509         response.status = rsp->status;
00510         data_out_residual_count = ntohl ( rsp->data_out_residual_count );
00511         data_in_residual_count = ntohl ( rsp->data_in_residual_count );
00512         if ( rsp->valid & SRP_RSP_VALID_DOOVER ) {
00513                 response.overrun = data_out_residual_count;
00514         } else if ( rsp->valid & SRP_RSP_VALID_DOUNDER ) {
00515                 response.overrun = -(data_out_residual_count);
00516         } else if ( rsp->valid & SRP_RSP_VALID_DIOVER ) {
00517                 response.overrun = data_in_residual_count;
00518         } else if ( rsp->valid & SRP_RSP_VALID_DIUNDER ) {
00519                 response.overrun = -(data_in_residual_count);
00520         }
00521         scsi_parse_sense ( srp_rsp_sense_data ( rsp ),
00522                            srp_rsp_sense_data_len ( rsp ), &response.sense );
00523 
00524         /* Report SCSI response */
00525         scsi_response ( &srpcmd->scsi, &response );
00526 
00527         /* Close SCSI command */
00528         srpcmd_close ( srpcmd, 0 );
00529 
00530         /* Drop temporary command reference */
00531         srpcmd_put ( srpcmd );
00532 
00533         return 0;
00534 }
00535 
00536 /**
00537  * Receive SRP unrecognised response IU
00538  *
00539  * @v srpdev            SRP device
00540  * @v data              SRP IU
00541  * @v len               Length of SRP IU
00542  * @ret rc              Returns status code
00543  */
00544 static int srp_unrecognised ( struct srp_device *srpdev,
00545                               const void *data, size_t len ) {
00546         const struct srp_common *common = data;
00547 
00548         DBGC ( srpdev, "SRP %p tag %08x unrecognised IU type %02x:\n",
00549                srpdev, ntohl ( common->tag.dwords[1] ), common->type );
00550         DBGC_HDA ( srpdev, 0, data, len );
00551 
00552         return -ENOTSUP;
00553 }
00554 
00555 /** SRP command SCSI interface operations */
00556 static struct interface_operation srpcmd_scsi_op[] = {
00557         INTF_OP ( intf_close, struct srp_command *, srpcmd_close ),
00558 };
00559 
00560 /** SRP command SCSI interface descriptor */
00561 static struct interface_descriptor srpcmd_scsi_desc =
00562         INTF_DESC ( struct srp_command, scsi, srpcmd_scsi_op );
00563 
00564 /**
00565  * Issue SRP SCSI command
00566  *
00567  * @v srpdev            SRP device
00568  * @v parent            Parent interface
00569  * @v command           SCSI command
00570  * @ret tag             Command tag, or negative error
00571  */
00572 static int srpdev_scsi_command ( struct srp_device *srpdev,
00573                                  struct interface *parent,
00574                                  struct scsi_cmd *command ) {
00575         struct srp_command *srpcmd;
00576         int tag;
00577         int rc;
00578 
00579         /* Allocate command tag */
00580         tag = srp_new_tag ( srpdev );
00581         if ( tag < 0 ) {
00582                 rc = tag;
00583                 goto err_tag;
00584         }
00585 
00586         /* Allocate and initialise structure */
00587         srpcmd = zalloc ( sizeof ( *srpcmd ) );
00588         if ( ! srpcmd ) {
00589                 rc = -ENOMEM;
00590                 goto err_zalloc;
00591         }
00592         ref_init ( &srpcmd->refcnt, srpcmd_free );
00593         intf_init ( &srpcmd->scsi, &srpcmd_scsi_desc, &srpcmd->refcnt );
00594         srpcmd->srpdev = srpdev_get ( srpdev );
00595         list_add ( &srpcmd->list, &srpdev->commands );
00596         srpcmd->tag = tag;
00597 
00598         /* Send command IU */
00599         if ( ( rc = srp_cmd ( srpdev, command, srpcmd->tag ) ) != 0 )
00600                 goto err_cmd;
00601 
00602         /* Attach to parent interface, leave reference with command
00603          * list, and return.
00604          */
00605         intf_plug_plug ( &srpcmd->scsi, parent );
00606         return srpcmd->tag;
00607 
00608  err_cmd:
00609         srpcmd_close ( srpcmd, rc );
00610  err_zalloc:
00611  err_tag:
00612         return rc;
00613 }
00614 
00615 /**
00616  * Receive data from SRP socket
00617  *
00618  * @v srpdev            SRP device
00619  * @v iobuf             Datagram I/O buffer
00620  * @v meta              Data transfer metadata
00621  * @ret rc              Return status code
00622  */
00623 static int srpdev_deliver ( struct srp_device *srpdev,
00624                             struct io_buffer *iobuf,
00625                             struct xfer_metadata *meta __unused ) {
00626         struct srp_common *common = iobuf->data;
00627         int ( * type ) ( struct srp_device *srp, const void *data, size_t len );
00628         int rc;
00629 
00630         /* Sanity check */
00631         if ( iob_len ( iobuf ) < sizeof ( *common ) ) {
00632                 DBGC ( srpdev, "SRP %p IU too short (%zd bytes)\n",
00633                        srpdev, iob_len ( iobuf ) );
00634                 rc = -EINVAL;
00635                 goto err;
00636         }
00637 
00638         /* Determine IU type */
00639         switch ( common->type ) {
00640         case SRP_LOGIN_RSP:
00641                 type = srp_login_rsp;
00642                 break;
00643         case SRP_LOGIN_REJ:
00644                 type = srp_login_rej;
00645                 break;
00646         case SRP_RSP:
00647                 type = srp_rsp;
00648                 break;
00649         default:
00650                 type = srp_unrecognised;
00651                 break;
00652         }
00653 
00654         /* Handle IU */
00655         if ( ( rc = type ( srpdev, iobuf->data, iob_len ( iobuf ) ) ) != 0 )
00656                 goto err;
00657 
00658         free_iob ( iobuf );
00659         return 0;
00660 
00661  err:
00662         DBGC ( srpdev, "SRP %p closing due to received IU (%s):\n",
00663                srpdev, strerror ( rc ) );
00664         DBGC_HDA ( srpdev, 0, iobuf->data, iob_len ( iobuf ) );
00665         free_iob ( iobuf );
00666         srpdev_close ( srpdev, rc );
00667         return rc;
00668 }
00669 
00670 /**
00671  * Check SRP device flow-control window
00672  *
00673  * @v srpdev            SRP device
00674  * @ret len             Length of window
00675  */
00676 static size_t srpdev_window ( struct srp_device *srpdev ) {
00677         return ( srpdev->logged_in ? ~( ( size_t ) 0 ) : 0 );
00678 }
00679 
00680 /** SRP device socket interface operations */
00681 static struct interface_operation srpdev_socket_op[] = {
00682         INTF_OP ( xfer_deliver, struct srp_device *, srpdev_deliver ),
00683         INTF_OP ( intf_close, struct srp_device *, srpdev_close ),
00684 };
00685 
00686 /** SRP device socket interface descriptor */
00687 static struct interface_descriptor srpdev_socket_desc =
00688         INTF_DESC_PASSTHRU ( struct srp_device, socket, srpdev_socket_op,
00689                              scsi );
00690 
00691 /** SRP device SCSI interface operations */
00692 static struct interface_operation srpdev_scsi_op[] = {
00693         INTF_OP ( scsi_command, struct srp_device *, srpdev_scsi_command ),
00694         INTF_OP ( xfer_window, struct srp_device *, srpdev_window ),
00695         INTF_OP ( intf_close, struct srp_device *, srpdev_close ),
00696 };
00697 
00698 /** SRP device SCSI interface descriptor */
00699 static struct interface_descriptor srpdev_scsi_desc =
00700         INTF_DESC_PASSTHRU ( struct srp_device, scsi, srpdev_scsi_op, socket  );
00701 
00702 /**
00703  * Open SRP device
00704  *
00705  * @v block             Block control interface
00706  * @v socket            Socket interface
00707  * @v initiator         Initiator port ID
00708  * @v target            Target port ID
00709  * @v memory_handle     RDMA memory handle
00710  * @v lun               SCSI LUN
00711  * @ret rc              Return status code
00712  */
00713 int srp_open ( struct interface *block, struct interface *socket,
00714                union srp_port_id *initiator, union srp_port_id *target,
00715                uint32_t memory_handle, struct scsi_lun *lun ) {
00716         struct srp_device *srpdev;
00717         int tag;
00718         int rc;
00719 
00720         /* Allocate and initialise structure */
00721         srpdev = zalloc ( sizeof ( *srpdev ) );
00722         if ( ! srpdev ) {
00723                 rc = -ENOMEM;
00724                 goto err_zalloc;
00725         }
00726         ref_init ( &srpdev->refcnt, NULL );
00727         intf_init ( &srpdev->scsi, &srpdev_scsi_desc, &srpdev->refcnt );
00728         intf_init ( &srpdev->socket, &srpdev_socket_desc, &srpdev->refcnt );
00729         INIT_LIST_HEAD ( &srpdev->commands );
00730         srpdev->memory_handle = memory_handle;
00731         DBGC ( srpdev, "SRP %p %08x%08x%08x%08x->%08x%08x%08x%08x\n", srpdev,
00732                ntohl ( initiator->dwords[0] ), ntohl ( initiator->dwords[1] ),
00733                ntohl ( initiator->dwords[2] ), ntohl ( initiator->dwords[3] ),
00734                ntohl ( target->dwords[0] ), ntohl ( target->dwords[1] ),
00735                ntohl ( target->dwords[2] ), ntohl ( target->dwords[3] ) );
00736 
00737         /* Attach to socket interface and initiate login */
00738         intf_plug_plug ( &srpdev->socket, socket );
00739         tag = srp_new_tag ( srpdev );
00740         assert ( tag >= 0 ); /* Cannot fail when no commands in progress */
00741         if ( ( rc = srp_login ( srpdev, initiator, target, tag ) ) != 0 )
00742                 goto err_login;
00743 
00744         /* Attach SCSI device to parent interface */
00745         if ( ( rc = scsi_open ( block, &srpdev->scsi, lun ) ) != 0 ) {
00746                 DBGC ( srpdev, "SRP %p could not create SCSI device: %s\n",
00747                        srpdev, strerror ( rc ) );
00748                 goto err_scsi_open;
00749         }
00750 
00751         /* Mortalise self and return */
00752         ref_put ( &srpdev->refcnt );
00753         return 0;
00754 
00755  err_scsi_open:
00756  err_login:
00757         srpdev_close ( srpdev, rc );
00758         ref_put ( &srpdev->refcnt );
00759  err_zalloc:
00760         return rc;
00761 }