ib_cm.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020 
00021 #include <stdint.h>
00022 #include <stdlib.h>
00023 #include <string.h>
00024 #include <byteswap.h>
00025 #include <errno.h>
00026 #include <assert.h>
00027 #include <gpxe/infiniband.h>
00028 #include <gpxe/ib_mi.h>
00029 #include <gpxe/ib_pathrec.h>
00030 #include <gpxe/ib_cm.h>
00031 
00032 /**
00033  * @file
00034  *
00035  * Infiniband communication management
00036  *
00037  */
00038 
00039 /** List of connections */
00040 static LIST_HEAD ( ib_cm_conns );
00041 
00042 /**
00043  * Send "ready to use" response
00044  *
00045  * @v ibdev             Infiniband device
00046  * @v mi                Management interface
00047  * @v conn              Connection
00048  * @v av                Address vector
00049  * @ret rc              Return status code
00050  */
00051 static int ib_cm_send_rtu ( struct ib_device *ibdev,
00052                             struct ib_mad_interface *mi,
00053                             struct ib_connection *conn,
00054                             struct ib_address_vector *av ) {
00055         union ib_mad mad;
00056         struct ib_cm_ready_to_use *ready =
00057                 &mad.cm.cm_data.ready_to_use;
00058         int rc;
00059 
00060         /* Construct "ready to use" response */
00061         memset ( &mad, 0, sizeof ( mad ) );
00062         mad.hdr.mgmt_class = IB_MGMT_CLASS_CM;
00063         mad.hdr.class_version = IB_CM_CLASS_VERSION;
00064         mad.hdr.method = IB_MGMT_METHOD_SEND;
00065         mad.hdr.attr_id = htons ( IB_CM_ATTR_READY_TO_USE );
00066         ready->local_id = htonl ( conn->local_id );
00067         ready->remote_id = htonl ( conn->remote_id );
00068         if ( ( rc = ib_mi_send ( ibdev, mi, &mad, av ) ) != 0 ){
00069                 DBGC ( conn, "CM %p could not send RTU: %s\n",
00070                        conn, strerror ( rc ) );
00071                 return rc;
00072         }
00073 
00074         return 0;
00075 }
00076 
00077 /**
00078  * Handle duplicate connection replies
00079  *
00080  * @v ibdev             Infiniband device
00081  * @v mi                Management interface
00082  * @v mad               Received MAD
00083  * @v av                Source address vector
00084  * @ret rc              Return status code
00085  *
00086  * If a "ready to use" MAD is lost, the peer may resend the connection
00087  * reply.  We have to respond to these with duplicate "ready to use"
00088  * MADs, otherwise the peer may time out and drop the connection.
00089  */
00090 static void ib_cm_connect_rep ( struct ib_device *ibdev,
00091                                 struct ib_mad_interface *mi,
00092                                 union ib_mad *mad,
00093                                 struct ib_address_vector *av ) {
00094         struct ib_cm_connect_reply *connect_rep =
00095                 &mad->cm.cm_data.connect_reply;
00096         struct ib_connection *conn;
00097         int rc;
00098 
00099         /* Identify connection */
00100         list_for_each_entry ( conn, &ib_cm_conns, list ) {
00101                 if ( ntohl ( connect_rep->remote_id ) != conn->local_id )
00102                         continue;
00103                 /* Try to send "ready to use" reply */
00104                 if ( ( rc = ib_cm_send_rtu ( ibdev, mi, conn, av ) ) != 0 ) {
00105                         /* Ignore errors */
00106                         return;
00107                 }
00108                 return;
00109         }
00110 
00111         DBG ( "CM unidentified connection %08x\n",
00112               ntohl ( connect_rep->remote_id ) );
00113 }
00114 
00115 /** Communication management agents */
00116 struct ib_mad_agent ib_cm_agent[] __ib_mad_agent = {
00117         {
00118                 .mgmt_class = IB_MGMT_CLASS_CM,
00119                 .class_version = IB_CM_CLASS_VERSION,
00120                 .attr_id = htons ( IB_CM_ATTR_CONNECT_REPLY ),
00121                 .handle = ib_cm_connect_rep,
00122         },
00123 };
00124 
00125 /**
00126  * Convert connection rejection reason to return status code
00127  *
00128  * @v reason            Rejection reason (in network byte order)
00129  * @ret rc              Return status code
00130  */
00131 static int ib_cm_rejection_reason_to_rc ( uint16_t reason ) {
00132         switch ( reason ) {
00133         case htons ( IB_CM_REJECT_BAD_SERVICE_ID ) :
00134                 return -ENODEV;
00135         case htons ( IB_CM_REJECT_STALE_CONN ) :
00136                 return -EALREADY;
00137         case htons ( IB_CM_REJECT_CONSUMER ) :
00138                 return -ENOTTY;
00139         default:
00140                 return -EPERM;
00141         }
00142 }
00143 
00144 /**
00145  * Handle connection request transaction completion
00146  *
00147  * @v ibdev             Infiniband device
00148  * @v mi                Management interface
00149  * @v madx              Management transaction
00150  * @v rc                Status code
00151  * @v mad               Received MAD (or NULL on error)
00152  * @v av                Source address vector (or NULL on error)
00153  */
00154 static void ib_cm_req_complete ( struct ib_device *ibdev,
00155                                  struct ib_mad_interface *mi,
00156                                  struct ib_mad_transaction *madx,
00157                                  int rc, union ib_mad *mad,
00158                                  struct ib_address_vector *av ) {
00159         struct ib_connection *conn = ib_madx_get_ownerdata ( madx );
00160         struct ib_queue_pair *qp = conn->qp;
00161         struct ib_cm_common *common = &mad->cm.cm_data.common;
00162         struct ib_cm_connect_reply *connect_rep =
00163                 &mad->cm.cm_data.connect_reply;
00164         struct ib_cm_connect_reject *connect_rej =
00165                 &mad->cm.cm_data.connect_reject;
00166         void *private_data = NULL;
00167         size_t private_data_len = 0;
00168 
00169         /* Report failures */
00170         if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) ))
00171                 rc = -EIO;
00172         if ( rc != 0 ) {
00173                 DBGC ( conn, "CM %p connection request failed: %s\n",
00174                        conn, strerror ( rc ) );
00175                 goto out;
00176         }
00177 
00178         /* Record remote communication ID */
00179         conn->remote_id = ntohl ( common->local_id );
00180 
00181         /* Handle response */
00182         switch ( mad->hdr.attr_id ) {
00183 
00184         case htons ( IB_CM_ATTR_CONNECT_REPLY ) :
00185                 /* Extract fields */
00186                 qp->av.qpn = ( ntohl ( connect_rep->local_qpn ) >> 8 );
00187                 qp->send.psn = ( ntohl ( connect_rep->starting_psn ) >> 8 );
00188                 private_data = &connect_rep->private_data;
00189                 private_data_len = sizeof ( connect_rep->private_data );
00190                 DBGC ( conn, "CM %p connected to QPN %lx PSN %x\n",
00191                        conn, qp->av.qpn, qp->send.psn );
00192 
00193                 /* Modify queue pair */
00194                 if ( ( rc = ib_modify_qp ( ibdev, qp ) ) != 0 ) {
00195                         DBGC ( conn, "CM %p could not modify queue pair: %s\n",
00196                                conn, strerror ( rc ) );
00197                         goto out;
00198                 }
00199 
00200                 /* Send "ready to use" reply */
00201                 if ( ( rc = ib_cm_send_rtu ( ibdev, mi, conn, av ) ) != 0 ) {
00202                         /* Treat as non-fatal */
00203                         rc = 0;
00204                 }
00205                 break;
00206 
00207         case htons ( IB_CM_ATTR_CONNECT_REJECT ) :
00208                 /* Extract fields */
00209                 DBGC ( conn, "CM %p connection rejected (reason %d)\n",
00210                        conn, ntohs ( connect_rej->reason ) );
00211                 /* Private data is valid only for a Consumer Reject */
00212                 if ( connect_rej->reason == htons ( IB_CM_REJECT_CONSUMER ) ) {
00213                         private_data = &connect_rej->private_data;
00214                         private_data_len = sizeof (connect_rej->private_data);
00215                 }
00216                 rc = ib_cm_rejection_reason_to_rc ( connect_rej->reason );
00217                 break;
00218 
00219         default:
00220                 DBGC ( conn, "CM %p unexpected response (attribute %04x)\n",
00221                        conn, ntohs ( mad->hdr.attr_id ) );
00222                 rc = -ENOTSUP;
00223                 break;
00224         }
00225 
00226  out:
00227         /* Destroy the completed transaction */
00228         ib_destroy_madx ( ibdev, ibdev->gsi, madx );
00229         conn->madx = NULL;
00230 
00231         /* Hand off to the upper completion handler */
00232         conn->op->changed ( ibdev, qp, conn, rc, private_data,
00233                             private_data_len );
00234 }
00235 
00236 /** Connection request operations */
00237 static struct ib_mad_transaction_operations ib_cm_req_op = {
00238         .complete = ib_cm_req_complete,
00239 };
00240 
00241 /**
00242  * Handle connection path transaction completion
00243  *
00244  * @v ibdev             Infiniband device
00245  * @v path              Path
00246  * @v rc                Status code
00247  * @v av                Address vector, or NULL on error
00248  */
00249 static void ib_cm_path_complete ( struct ib_device *ibdev,
00250                                   struct ib_path *path, int rc,
00251                                   struct ib_address_vector *av ) {
00252         struct ib_connection *conn = ib_path_get_ownerdata ( path );
00253         struct ib_queue_pair *qp = conn->qp;
00254         union ib_mad mad;
00255         struct ib_cm_connect_request *connect_req =
00256                 &mad.cm.cm_data.connect_request;
00257         size_t private_data_len;
00258 
00259         /* Report failures */
00260         if ( rc != 0 ) {
00261                 DBGC ( conn, "CM %p path lookup failed: %s\n",
00262                        conn, strerror ( rc ) );
00263                 conn->op->changed ( ibdev, qp, conn, rc, NULL, 0 );
00264                 goto out;
00265         }
00266 
00267         /* Update queue pair peer path */
00268         memcpy ( &qp->av, av, sizeof ( qp->av ) );
00269 
00270         /* Construct connection request */
00271         memset ( &mad, 0, sizeof ( mad ) );
00272         mad.hdr.mgmt_class = IB_MGMT_CLASS_CM;
00273         mad.hdr.class_version = IB_CM_CLASS_VERSION;
00274         mad.hdr.method = IB_MGMT_METHOD_SEND;
00275         mad.hdr.attr_id = htons ( IB_CM_ATTR_CONNECT_REQUEST );
00276         connect_req->local_id = htonl ( conn->local_id );
00277         memcpy ( &connect_req->service_id, &conn->service_id,
00278                  sizeof ( connect_req->service_id ) );
00279         ib_get_hca_info ( ibdev, &connect_req->local_ca );
00280         connect_req->local_qpn__responder_resources =
00281                 htonl ( ( qp->qpn << 8 ) | 1 );
00282         connect_req->local_eecn__initiator_depth = htonl ( ( 0 << 8 ) | 1 );
00283         connect_req->remote_eecn__remote_timeout__service_type__ee_flow_ctrl =
00284                 htonl ( ( 0x14 << 3 ) | ( IB_CM_TRANSPORT_RC << 1 ) |
00285                         ( 0 << 0 ) );
00286         connect_req->starting_psn__local_timeout__retry_count =
00287                 htonl ( ( qp->recv.psn << 8 ) | ( 0x14 << 3 ) |
00288                         ( 0x07 << 0 ) );
00289         connect_req->pkey = htons ( ibdev->pkey );
00290         connect_req->payload_mtu__rdc_exists__rnr_retry =
00291                 ( ( IB_MTU_2048 << 4 ) | ( 1 << 3 ) | ( 0x07 << 0 ) );
00292         connect_req->max_cm_retries__srq =
00293                 ( ( 0x0f << 4 ) | ( 0 << 3 ) );
00294         connect_req->primary.local_lid = htons ( ibdev->lid );
00295         connect_req->primary.remote_lid = htons ( conn->qp->av.lid );
00296         memcpy ( &connect_req->primary.local_gid, &ibdev->gid,
00297                  sizeof ( connect_req->primary.local_gid ) );
00298         memcpy ( &connect_req->primary.remote_gid, &conn->qp->av.gid,
00299                  sizeof ( connect_req->primary.remote_gid ) );
00300         connect_req->primary.flow_label__rate =
00301                 htonl ( ( 0 << 12 ) | ( conn->qp->av.rate << 0 ) );
00302         connect_req->primary.hop_limit = 0;
00303         connect_req->primary.sl__subnet_local =
00304                 ( ( conn->qp->av.sl << 4 ) | ( 1 << 3 ) );
00305         connect_req->primary.local_ack_timeout = ( 0x13 << 3 );
00306         private_data_len = conn->private_data_len;
00307         if ( private_data_len > sizeof ( connect_req->private_data ) )
00308                 private_data_len = sizeof ( connect_req->private_data );
00309         memcpy ( &connect_req->private_data, &conn->private_data,
00310                  private_data_len );
00311 
00312         /* Create connection request */
00313         av->qpn = IB_QPN_GSI;
00314         av->qkey = IB_QKEY_GSI;
00315         conn->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, av,
00316                                       &ib_cm_req_op );
00317         if ( ! conn->madx ) {
00318                 DBGC ( conn, "CM %p could not create connection request\n",
00319                        conn );
00320                 conn->op->changed ( ibdev, qp, conn, rc, NULL, 0 );
00321                 goto out;
00322         }
00323         ib_madx_set_ownerdata ( conn->madx, conn );
00324 
00325  out:
00326         /* Destroy the completed transaction */
00327         ib_destroy_path ( ibdev, path );
00328         conn->path = NULL;
00329 }
00330 
00331 /** Connection path operations */
00332 static struct ib_path_operations ib_cm_path_op = {
00333         .complete = ib_cm_path_complete,
00334 };
00335 
00336 /**
00337  * Create connection to remote QP
00338  *
00339  * @v ibdev             Infiniband device
00340  * @v qp                Queue pair
00341  * @v dgid              Target GID
00342  * @v service_id        Target service ID
00343  * @v private_data      Connection request private data
00344  * @v private_data_len  Length of connection request private data
00345  * @v op                Connection operations
00346  * @ret conn            Connection
00347  */
00348 struct ib_connection *
00349 ib_create_conn ( struct ib_device *ibdev, struct ib_queue_pair *qp,
00350                  struct ib_gid *dgid, struct ib_gid_half *service_id,
00351                  void *private_data, size_t private_data_len,
00352                  struct ib_connection_operations *op ) {
00353         struct ib_connection *conn;
00354 
00355         /* Allocate and initialise request */
00356         conn = zalloc ( sizeof ( *conn ) + private_data_len );
00357         if ( ! conn )
00358                 goto err_alloc_conn;
00359         conn->ibdev = ibdev;
00360         conn->qp = qp;
00361         memset ( &qp->av, 0, sizeof ( qp->av ) );
00362         qp->av.gid_present = 1;
00363         memcpy ( &qp->av.gid, dgid, sizeof ( qp->av.gid ) );
00364         conn->local_id = random();
00365         memcpy ( &conn->service_id, service_id, sizeof ( conn->service_id ) );
00366         conn->op = op;
00367         conn->private_data_len = private_data_len;
00368         memcpy ( &conn->private_data, private_data, private_data_len );
00369 
00370         /* Create path */
00371         conn->path = ib_create_path ( ibdev, &qp->av, &ib_cm_path_op );
00372         if ( ! conn->path )
00373                 goto err_create_path;
00374         ib_path_set_ownerdata ( conn->path, conn );
00375 
00376         /* Add to list of connections */
00377         list_add ( &conn->list, &ib_cm_conns );
00378 
00379         DBGC ( conn, "CM %p created for IBDEV %p QPN %lx\n",
00380                conn, ibdev, qp->qpn );
00381         DBGC ( conn, "CM %p connecting to %08x:%08x:%08x:%08x %08x:%08x\n",
00382                conn, ntohl ( dgid->u.dwords[0] ), ntohl ( dgid->u.dwords[1] ),
00383                ntohl ( dgid->u.dwords[2] ), ntohl ( dgid->u.dwords[3] ),
00384                ntohl ( service_id->u.dwords[0] ),
00385                ntohl ( service_id->u.dwords[1] ) );
00386 
00387         return conn;
00388 
00389         ib_destroy_path ( ibdev, conn->path );
00390  err_create_path:
00391         free ( conn );
00392  err_alloc_conn:
00393         return NULL;
00394 }
00395 
00396 /**
00397  * Destroy connection to remote QP
00398  *
00399  * @v ibdev             Infiniband device
00400  * @v qp                Queue pair
00401  * @v conn              Connection
00402  */
00403 void ib_destroy_conn ( struct ib_device *ibdev,
00404                        struct ib_queue_pair *qp __unused,
00405                        struct ib_connection *conn ) {
00406 
00407         list_del ( &conn->list );
00408         if ( conn->madx )
00409                 ib_destroy_madx ( ibdev, ibdev->gsi, conn->madx );
00410         if ( conn->path )
00411                 ib_destroy_path ( ibdev, conn->path );
00412         free ( conn );
00413 }

Generated on Tue Apr 6 20:01:10 2010 for gPXE by  doxygen 1.5.7.1