iPXE
ib_pathrec.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., 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 <byteswap.h>
00030 #include <errno.h>
00031 #include <ipxe/infiniband.h>
00032 #include <ipxe/ib_mi.h>
00033 #include <ipxe/ib_pathrec.h>
00034 
00035 /** @file
00036  *
00037  * Infiniband path lookups
00038  *
00039  */
00040 
00041 /**
00042  * Handle path transaction completion
00043  *
00044  * @v ibdev             Infiniband device
00045  * @v mi                Management interface
00046  * @v madx              Management transaction
00047  * @v rc                Status code
00048  * @v mad               Received MAD (or NULL on error)
00049  * @v av                Source address vector (or NULL on error)
00050  */
00051 static void ib_path_complete ( struct ib_device *ibdev,
00052                                struct ib_mad_interface *mi,
00053                                struct ib_mad_transaction *madx,
00054                                int rc, union ib_mad *mad,
00055                                struct ib_address_vector *av __unused ) {
00056         struct ib_path *path = ib_madx_get_ownerdata ( madx );
00057         union ib_gid *dgid = &path->av.gid;
00058         struct ib_path_record *pathrec = &mad->sa.sa_data.path_record;
00059 
00060         /* Report failures */
00061         if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) ))
00062                 rc = -ENETUNREACH;
00063         if ( rc != 0 ) {
00064                 DBGC ( ibdev, "IBDEV %s path lookup for " IB_GID_FMT
00065                        " failed: %s\n",
00066                        ibdev->name, IB_GID_ARGS ( dgid ), strerror ( rc ) );
00067                 goto out;
00068         }
00069 
00070         /* Extract values from MAD */
00071         path->av.lid = ntohs ( pathrec->dlid );
00072         path->av.sl = ( pathrec->reserved__sl & 0x0f );
00073         path->av.rate = ( pathrec->rate_selector__rate & 0x3f );
00074         DBGC ( ibdev, "IBDEV %s path to " IB_GID_FMT " lid %d sl %d rate "
00075                "%d\n", ibdev->name, IB_GID_ARGS ( dgid ), path->av.lid,
00076                path->av.sl, path->av.rate );
00077 
00078         /* Use only the LID if no GRH is needed for this path */
00079         if ( memcmp ( &path->av.gid.s.prefix, &ibdev->gid.s.prefix,
00080                       sizeof ( path->av.gid.s.prefix ) ) == 0 ) {
00081                 path->av.gid_present = 0;
00082         }
00083 
00084  out:
00085         /* Destroy the completed transaction */
00086         ib_destroy_madx ( ibdev, mi, madx );
00087         path->madx = NULL;
00088 
00089         /* Hand off to upper completion handler */
00090         path->op->complete ( ibdev, path, rc, &path->av );
00091 }
00092 
00093 /** Path transaction completion operations */
00094 static struct ib_mad_transaction_operations ib_path_op = {
00095         .complete = ib_path_complete,
00096 };
00097 
00098 /**
00099  * Create path
00100  *
00101  * @v ibdev             Infiniband device
00102  * @v av                Address vector to complete
00103  * @v op                Path operations
00104  * @ret path            Path
00105  */
00106 struct ib_path *
00107 ib_create_path ( struct ib_device *ibdev, struct ib_address_vector *av,
00108                  struct ib_path_operations *op ) {
00109         struct ib_path *path;
00110         union ib_mad mad;
00111         struct ib_mad_sa *sa = &mad.sa;
00112 
00113         /* Allocate and initialise structure */
00114         path = zalloc ( sizeof ( *path ) );
00115         if ( ! path )
00116                 goto err_alloc_path;
00117         path->ibdev = ibdev;
00118         memcpy ( &path->av, av, sizeof ( path->av ) );
00119         path->op = op;
00120 
00121         /* Construct path request */
00122         memset ( sa, 0, sizeof ( *sa ) );
00123         sa->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM;
00124         sa->mad_hdr.class_version = IB_SA_CLASS_VERSION;
00125         sa->mad_hdr.method = IB_MGMT_METHOD_GET;
00126         sa->mad_hdr.attr_id = htons ( IB_SA_ATTR_PATH_REC );
00127         sa->sa_hdr.comp_mask[1] =
00128                 htonl ( IB_SA_PATH_REC_DGID | IB_SA_PATH_REC_SGID );
00129         memcpy ( &sa->sa_data.path_record.dgid, &path->av.gid,
00130                  sizeof ( sa->sa_data.path_record.dgid ) );
00131         memcpy ( &sa->sa_data.path_record.sgid, &ibdev->gid,
00132                  sizeof ( sa->sa_data.path_record.sgid ) );
00133 
00134         /* Create management transaction */
00135         path->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, NULL,
00136                                       &ib_path_op );
00137         if ( ! path->madx )
00138                 goto err_create_madx;
00139         ib_madx_set_ownerdata ( path->madx, path );
00140 
00141         return path;
00142 
00143         ib_destroy_madx ( ibdev, ibdev->gsi, path->madx );
00144  err_create_madx:
00145         free ( path );
00146  err_alloc_path:
00147         return NULL;
00148 }
00149 
00150 /**
00151  * Destroy path
00152  *
00153  * @v ibdev             Infiniband device
00154  * @v path              Path
00155  */
00156 void ib_destroy_path ( struct ib_device *ibdev, struct ib_path *path ) {
00157 
00158         if ( path->madx )
00159                 ib_destroy_madx ( ibdev, ibdev->gsi, path->madx );
00160         free ( path );
00161 }
00162 
00163 /** Number of path cache entries
00164  *
00165  * Must be a power of two.
00166  */
00167 #define IB_NUM_CACHED_PATHS 4
00168 
00169 /** A cached path */
00170 struct ib_cached_path {
00171         /** Path */
00172         struct ib_path *path;
00173 };
00174 
00175 /** Path cache */
00176 static struct ib_cached_path ib_path_cache[IB_NUM_CACHED_PATHS];
00177 
00178 /** Oldest path cache entry index */
00179 static unsigned int ib_path_cache_idx;
00180 
00181 /**
00182  * Find path cache entry
00183  *
00184  * @v ibdev             Infiniband device
00185  * @v dgid              Destination GID
00186  * @ret path            Path cache entry, or NULL
00187  */
00188 static struct ib_cached_path *
00189 ib_find_path_cache_entry ( struct ib_device *ibdev, union ib_gid *dgid ) {
00190         struct ib_cached_path *cached;
00191         unsigned int i;
00192 
00193         for ( i = 0 ; i < IB_NUM_CACHED_PATHS ; i++ ) {
00194                 cached = &ib_path_cache[i];
00195                 if ( ! cached->path )
00196                         continue;
00197                 if ( cached->path->ibdev != ibdev )
00198                         continue;
00199                 if ( memcmp ( &cached->path->av.gid, dgid,
00200                               sizeof ( cached->path->av.gid ) ) != 0 )
00201                         continue;
00202                 return cached;
00203         }
00204 
00205         return NULL;
00206 }
00207 
00208 /**
00209  * Handle cached path transaction completion
00210  *
00211  * @v ibdev             Infiniband device
00212  * @v path              Path
00213  * @v rc                Status code
00214  * @v av                Address vector, or NULL on error
00215  */
00216 static void ib_cached_path_complete ( struct ib_device *ibdev,
00217                                       struct ib_path *path, int rc,
00218                                       struct ib_address_vector *av __unused ) {
00219         struct ib_cached_path *cached = ib_path_get_ownerdata ( path );
00220 
00221         /* If the transaction failed, erase the cache entry */
00222         if ( rc != 0 ) {
00223                 /* Destroy the old cache entry */
00224                 ib_destroy_path ( ibdev, path );
00225                 memset ( cached, 0, sizeof ( *cached ) );
00226                 return;
00227         }
00228 
00229         /* Do not destroy the completed transaction; we still need to
00230          * refer to the resolved path.
00231          */
00232 }
00233 
00234 /** Cached path transaction completion operations */
00235 static struct ib_path_operations ib_cached_path_op = {
00236         .complete = ib_cached_path_complete,
00237 };
00238 
00239 /**
00240  * Resolve path
00241  *
00242  * @v ibdev             Infiniband device
00243  * @v av                Address vector to complete
00244  * @ret rc              Return status code
00245  *
00246  * This provides a non-transactional way to resolve a path, via a
00247  * cache similar to ARP.
00248  */
00249 int ib_resolve_path ( struct ib_device *ibdev, struct ib_address_vector *av ) {
00250         union ib_gid *gid = &av->gid;
00251         struct ib_cached_path *cached;
00252         unsigned int cache_idx;
00253 
00254         /* Look in cache for a matching entry */
00255         cached = ib_find_path_cache_entry ( ibdev, gid );
00256         if ( cached && cached->path->av.lid ) {
00257                 /* Populated entry found */
00258                 av->lid = cached->path->av.lid;
00259                 av->rate = cached->path->av.rate;
00260                 av->sl = cached->path->av.sl;
00261                 av->gid_present = cached->path->av.gid_present;
00262                 DBGC2 ( ibdev, "IBDEV %s cache hit for " IB_GID_FMT "\n",
00263                         ibdev->name, IB_GID_ARGS ( gid ) );
00264                 return 0;
00265         }
00266         DBGC ( ibdev, "IBDEV %s cache miss for " IB_GID_FMT "%s\n", ibdev->name,
00267                IB_GID_ARGS ( gid ), ( cached ? " (in progress)" : "" ) );
00268 
00269         /* If lookup is already in progress, do nothing */
00270         if ( cached )
00271                 return -ENOENT;
00272 
00273         /* Locate a new cache entry to use */
00274         cache_idx = ( (ib_path_cache_idx++) % IB_NUM_CACHED_PATHS );
00275         cached = &ib_path_cache[cache_idx];
00276 
00277         /* Destroy the old cache entry */
00278         if ( cached->path )
00279                 ib_destroy_path ( ibdev, cached->path );
00280         memset ( cached, 0, sizeof ( *cached ) );
00281 
00282         /* Create new path */
00283         cached->path = ib_create_path ( ibdev, av, &ib_cached_path_op );
00284         if ( ! cached->path ) {
00285                 DBGC ( ibdev, "IBDEV %s could not create path\n",
00286                        ibdev->name );
00287                 return -ENOMEM;
00288         }
00289         ib_path_set_ownerdata ( cached->path, cached );
00290 
00291         /* Not found yet */
00292         return -ENOENT;
00293 }