iPXE
ib_packet.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008 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., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stdint.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <errno.h>
00030 #include <byteswap.h>
00031 #include <ipxe/iobuf.h>
00032 #include <ipxe/infiniband.h>
00033 #include <ipxe/ib_packet.h>
00034 
00035 /**
00036  * @file
00037  *
00038  * Infiniband Packet Formats
00039  *
00040  */
00041 
00042 /**
00043  * Add IB headers
00044  *
00045  * @v ibdev             Infiniband device
00046  * @v iobuf             I/O buffer to contain headers
00047  * @v qp                Queue pair
00048  * @v payload_len       Payload length
00049  * @v dest              Destination address vector
00050  * @ret rc              Return status code
00051  */
00052 int ib_push ( struct ib_device *ibdev, struct io_buffer *iobuf,
00053               struct ib_queue_pair *qp, size_t payload_len,
00054               const struct ib_address_vector *dest ) {
00055         struct ib_local_route_header *lrh;
00056         struct ib_global_route_header *grh;
00057         struct ib_base_transport_header *bth;
00058         struct ib_datagram_extended_transport_header *deth;
00059         size_t orig_iob_len = iob_len ( iobuf );
00060         size_t pad_len;
00061         size_t lrh_len;
00062         size_t grh_len;
00063         unsigned int vl;
00064         unsigned int lnh;
00065 
00066         DBGC2 ( ibdev, "IBDEV %s TX %04x:%08lx => %04x:%08lx (key %08lx)\n",
00067                 ibdev->name, ibdev->lid, qp->ext_qpn, dest->lid, dest->qpn,
00068                 dest->qkey );
00069 
00070         /* Calculate packet length */
00071         pad_len = ( (-payload_len) & 0x3 );
00072         payload_len += pad_len;
00073         payload_len += 4; /* ICRC */
00074 
00075         /* Reserve space for headers */
00076         orig_iob_len = iob_len ( iobuf );
00077         deth = iob_push ( iobuf, sizeof ( *deth ) );
00078         bth = iob_push ( iobuf, sizeof ( *bth ) );
00079         grh_len = ( payload_len + iob_len ( iobuf ) - orig_iob_len );
00080         grh = ( dest->gid_present ?
00081                 iob_push ( iobuf, sizeof ( *grh ) ) : NULL );
00082         lrh = iob_push ( iobuf, sizeof ( *lrh ) );
00083         lrh_len = ( payload_len + iob_len ( iobuf ) - orig_iob_len );
00084 
00085         /* Construct LRH */
00086         vl = ( ( qp->ext_qpn == IB_QPN_SMI ) ? IB_VL_SMP : IB_VL_DEFAULT );
00087         lrh->vl__lver = ( vl << 4 );
00088         lnh = ( grh ? IB_LNH_GRH : IB_LNH_BTH );
00089         lrh->sl__lnh = ( ( dest->sl << 4 ) | lnh );
00090         lrh->dlid = htons ( dest->lid );
00091         lrh->length = htons ( lrh_len >> 2 );
00092         lrh->slid = htons ( ibdev->lid );
00093 
00094         /* Construct GRH, if required */
00095         if ( grh ) {
00096                 grh->ipver__tclass__flowlabel =
00097                         htonl ( IB_GRH_IPVER_IPv6 << 28 );
00098                 grh->paylen = htons ( grh_len );
00099                 grh->nxthdr = IB_GRH_NXTHDR_IBA;
00100                 grh->hoplmt = 0;
00101                 memcpy ( &grh->sgid, &ibdev->gid, sizeof ( grh->sgid ) );
00102                 memcpy ( &grh->dgid, &dest->gid, sizeof ( grh->dgid ) );
00103         }
00104 
00105         /* Construct BTH */
00106         bth->opcode = BTH_OPCODE_UD_SEND;
00107         bth->se__m__padcnt__tver = ( pad_len << 4 );
00108         bth->pkey = htons ( ibdev->pkey );
00109         bth->dest_qp = htonl ( dest->qpn );
00110         bth->ack__psn = htonl ( ( qp->send.psn++ ) & 0xffffffUL );
00111 
00112         /* Construct DETH */
00113         deth->qkey = htonl ( dest->qkey );
00114         deth->src_qp = htonl ( qp->ext_qpn );
00115 
00116         DBGCP_HDA ( ibdev, 0, iobuf->data,
00117                     ( iob_len ( iobuf ) - orig_iob_len ) );
00118 
00119         return 0;
00120 }
00121 
00122 /**
00123  * Remove IB headers
00124  *
00125  * @v ibdev             Infiniband device
00126  * @v iobuf             I/O buffer containing headers
00127  * @v qp                Queue pair to fill in, or NULL
00128  * @v payload_len       Payload length to fill in, or NULL
00129  * @v dest              Destination address vector to fill in
00130  * @v source            Source address vector to fill in
00131  * @ret rc              Return status code
00132  */
00133 int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf,
00134               struct ib_queue_pair **qp, size_t *payload_len,
00135               struct ib_address_vector *dest,
00136               struct ib_address_vector *source ) {
00137         struct ib_local_route_header *lrh;
00138         struct ib_global_route_header *grh;
00139         struct ib_base_transport_header *bth;
00140         struct ib_datagram_extended_transport_header *deth;
00141         size_t orig_iob_len = iob_len ( iobuf );
00142         unsigned int lnh;
00143         size_t pad_len;
00144 
00145         /* Clear return values */
00146         if ( qp )
00147                 *qp = NULL;
00148         if ( payload_len )
00149                 *payload_len = 0;
00150         memset ( dest, 0, sizeof ( *dest ) );
00151         memset ( source, 0, sizeof ( *source ) );
00152 
00153         /* Extract LRH */
00154         if ( iob_len ( iobuf ) < sizeof ( *lrh ) ) {
00155                 DBGC ( ibdev, "IBDEV %s RX too short (%zd bytes) for LRH\n",
00156                        ibdev->name, iob_len ( iobuf ) );
00157                 return -EINVAL;
00158         }
00159         lrh = iobuf->data;
00160         iob_pull ( iobuf, sizeof ( *lrh ) );
00161         dest->lid = ntohs ( lrh->dlid );
00162         dest->sl = ( lrh->sl__lnh >> 4 );
00163         source->lid = ntohs ( lrh->slid );
00164         source->sl = ( lrh->sl__lnh >> 4 );
00165         lnh = ( lrh->sl__lnh & 0x3 );
00166 
00167         /* Reject unsupported packets */
00168         if ( ! ( ( lnh == IB_LNH_BTH ) || ( lnh == IB_LNH_GRH ) ) ) {
00169                 DBGC ( ibdev, "IBDEV %s RX unsupported LNH %x\n",
00170                        ibdev->name, lnh );
00171                 return -ENOTSUP;
00172         }
00173 
00174         /* Extract GRH, if present */
00175         if ( lnh == IB_LNH_GRH ) {
00176                 if ( iob_len ( iobuf ) < sizeof ( *grh ) ) {
00177                         DBGC ( ibdev, "IBDEV %s RX too short (%zd bytes) "
00178                                "for GRH\n", ibdev->name, iob_len ( iobuf ) );
00179                         return -EINVAL;
00180                 }
00181                 grh = iobuf->data;
00182                 iob_pull ( iobuf, sizeof ( *grh ) );
00183                 dest->gid_present = 1;
00184                 memcpy ( &dest->gid, &grh->dgid, sizeof ( dest->gid ) );
00185                 source->gid_present = 1;
00186                 memcpy ( &source->gid, &grh->sgid, sizeof ( source->gid ) );
00187         } else {
00188                 grh = NULL;
00189         }
00190 
00191         /* Extract BTH */
00192         if ( iob_len ( iobuf ) < sizeof ( *bth ) ) {
00193                 DBGC ( ibdev, "IBDEV %s RX too short (%zd bytes) for BTH\n",
00194                        ibdev->name, iob_len ( iobuf ) );
00195                 return -EINVAL;
00196         }
00197         bth = iobuf->data;
00198         iob_pull ( iobuf, sizeof ( *bth ) );
00199         if ( bth->opcode != BTH_OPCODE_UD_SEND ) {
00200                 DBGC ( ibdev, "IBDEV %s unsupported BTH opcode %x\n",
00201                        ibdev->name, bth->opcode );
00202                 return -ENOTSUP;
00203         }
00204         dest->qpn = ntohl ( bth->dest_qp );
00205 
00206         /* Extract DETH */
00207         if ( iob_len ( iobuf ) < sizeof ( *deth ) ) {
00208                 DBGC ( ibdev, "IBDEV %s RX too short (%zd bytes) for DETH\n",
00209                        ibdev->name, iob_len ( iobuf ) );
00210                 return -EINVAL;
00211         }
00212         deth = iobuf->data;
00213         iob_pull ( iobuf, sizeof ( *deth ) );
00214         source->qpn = ntohl ( deth->src_qp );
00215         source->qkey = ntohl ( deth->qkey );
00216 
00217         /* Calculate payload length, if applicable */
00218         if ( payload_len ) {
00219                 pad_len = ( ( bth->se__m__padcnt__tver >> 4 ) & 0x3 );
00220                 *payload_len = ( ( ntohs ( lrh->length ) << 2 )
00221                                  - ( orig_iob_len - iob_len ( iobuf ) )
00222                                  - pad_len - 4 /* ICRC */ );
00223         }
00224 
00225         /* Determine destination QP, if applicable */
00226         if ( qp ) {
00227                 if ( IB_LID_MULTICAST ( dest->lid ) && grh ) {
00228                         if ( ! ( *qp = ib_find_qp_mgid ( ibdev, &grh->dgid ))){
00229                                 DBGC ( ibdev, "IBDEV %s RX for unknown MGID "
00230                                        IB_GID_FMT "\n", ibdev->name,
00231                                        IB_GID_ARGS ( &grh->dgid ) );
00232                                 return -ENODEV;
00233                         }
00234                 } else {
00235                         if ( ! ( *qp = ib_find_qp_qpn ( ibdev, dest->qpn ) ) ) {
00236                                 DBGC ( ibdev, "IBDEV %s RX for nonexistent "
00237                                        "QPN %#lx\n", ibdev->name, dest->qpn );
00238                                 return -ENODEV;
00239                         }
00240                 }
00241                 assert ( *qp );
00242         }
00243 
00244         DBGC2 ( ibdev, "IBDEV %s RX %04x:%08lx <= %04x:%08lx (key %08x)\n",
00245                 ibdev->name, dest->lid,
00246                 ( IB_LID_MULTICAST ( dest->lid ) ?
00247                   ( qp ? (*qp)->ext_qpn : -1UL ) : dest->qpn ),
00248                 source->lid, source->qpn, ntohl ( deth->qkey ) );
00249         DBGCP_HDA ( ibdev, 0,
00250                     ( iobuf->data - ( orig_iob_len - iob_len ( iobuf ) ) ),
00251                     ( orig_iob_len - iob_len ( iobuf ) ) );
00252 
00253         return 0;
00254 }