2 * Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 FILE_LICENCE ( GPL2_OR_LATER );
25 #include <gpxe/infiniband.h>
26 #include <gpxe/ib_gma.h>
27 #include <gpxe/ib_pathrec.h>
31 * Infiniband path lookups
35 /** Number of path record cache entries
37 * Must be a power of two.
39 #define IB_NUM_CACHED_PATHS 4
41 /** A path record cache entry */
42 struct ib_cached_path_record {
43 /** Infiniband device's port GID
45 * Used to disambiguate cache entries when we have multiple
46 * Infiniband devices, without having to maintain a pointer to
47 * the Infiniband device.
50 /** Destination GID */
52 /** Destination LID */
60 /** Path record cache */
61 static struct ib_cached_path_record ib_path_cache[IB_NUM_CACHED_PATHS];
63 /** Oldest path record cache entry index */
64 static unsigned int ib_path_cache_idx;
67 * Find path record cache entry
69 * @v ibdev Infiniband device
70 * @v dgid Destination GID
71 * @ret cached Path record cache entry, or NULL
73 static struct ib_cached_path_record *
74 ib_find_path_cache_entry ( struct ib_device *ibdev, struct ib_gid *dgid ) {
75 struct ib_cached_path_record *cached;
78 for ( i = 0 ; i < IB_NUM_CACHED_PATHS ; i++ ) {
79 cached = &ib_path_cache[i];
80 if ( memcmp ( &cached->sgid, &ibdev->gid,
81 sizeof ( cached->sgid ) ) != 0 )
83 if ( memcmp ( &cached->dgid, dgid,
84 sizeof ( cached->dgid ) ) != 0 )
95 * @v ibdev Infiniband device
96 * @v av Address vector to complete
97 * @ret rc Return status code
99 int ib_resolve_path ( struct ib_device *ibdev,
100 struct ib_address_vector *av ) {
101 struct ib_gid *gid = &av->gid;
102 struct ib_cached_path_record *cached;
104 struct ib_mad_sa *sa = &mad.sa;
105 unsigned int cache_idx;
109 if ( ! av->gid_present ) {
110 DBGC ( ibdev, "IBDEV %p attempt to look up path record "
111 "without GID\n", ibdev );
115 /* Look in cache for a matching entry */
116 cached = ib_find_path_cache_entry ( ibdev, gid );
117 if ( cached && cached->dlid ) {
118 /* Populated entry found */
119 av->lid = cached->dlid;
120 av->rate = cached->rate;
122 DBGC2 ( ibdev, "IBDEV %p cache hit for %08x:%08x:%08x:%08x\n",
123 ibdev, htonl ( gid->u.dwords[0] ),
124 htonl ( gid->u.dwords[1] ), htonl ( gid->u.dwords[2] ),
125 htonl ( gid->u.dwords[3] ) );
128 DBGC ( ibdev, "IBDEV %p cache miss for %08x:%08x:%08x:%08x%s\n", ibdev,
129 htonl ( gid->u.dwords[0] ), htonl ( gid->u.dwords[1] ),
130 htonl ( gid->u.dwords[2] ), htonl ( gid->u.dwords[3] ),
131 ( cached ? " (in progress)" : "" ) );
133 /* If no unresolved entry was found, then create a new one */
135 cache_idx = ( (ib_path_cache_idx++) % IB_NUM_CACHED_PATHS );
136 cached = &ib_path_cache[cache_idx];
137 memset ( cached, 0, sizeof ( *cached ) );
138 memcpy ( &cached->sgid, &ibdev->gid, sizeof ( cached->sgid ) );
139 memcpy ( &cached->dgid, gid, sizeof ( cached->dgid ) );
142 /* Construct path record request */
143 memset ( sa, 0, sizeof ( *sa ) );
144 sa->mad_hdr.base_version = IB_MGMT_BASE_VERSION;
145 sa->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM;
146 sa->mad_hdr.class_version = IB_SA_CLASS_VERSION;
147 sa->mad_hdr.method = IB_MGMT_METHOD_GET;
148 sa->mad_hdr.attr_id = htons ( IB_SA_ATTR_PATH_REC );
149 sa->sa_hdr.comp_mask[1] =
150 htonl ( IB_SA_PATH_REC_DGID | IB_SA_PATH_REC_SGID );
151 memcpy ( &sa->sa_data.path_record.dgid, &cached->dgid,
152 sizeof ( sa->sa_data.path_record.dgid ) );
153 memcpy ( &sa->sa_data.path_record.sgid, &cached->sgid,
154 sizeof ( sa->sa_data.path_record.sgid ) );
156 /* Issue path record request */
157 if ( ( rc = ib_gma_request ( &ibdev->gma, &mad, NULL ) ) != 0 ) {
158 DBGC ( ibdev, "IBDEV %p could not get path record: %s\n",
159 ibdev, strerror ( rc ) );
168 * Handle path record response
170 * @v ibdev Infiniband device
172 * @ret rc Return status code
174 static int ib_handle_path_record ( struct ib_device *ibdev,
175 union ib_mad *mad ) {
176 struct ib_path_record *path_record = &mad->sa.sa_data.path_record;
177 struct ib_gid *dgid = &path_record->dgid;
178 struct ib_cached_path_record *cached;
183 /* Ignore if not a success */
184 if ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) ) {
185 DBGC ( ibdev, "IBDEV %p path record lookup failed with status "
186 "%04x\n", ibdev, ntohs ( mad->hdr.status ) );
190 /* Extract values from MAD */
191 dlid = ntohs ( path_record->dlid );
192 sl = ( path_record->reserved__sl & 0x0f );
193 rate = ( path_record->rate_selector__rate & 0x3f );
194 DBGC ( ibdev, "IBDEV %p path to %08x:%08x:%08x:%08x is %04x sl %d "
195 "rate %d\n", ibdev, htonl ( dgid->u.dwords[0] ),
196 htonl ( dgid->u.dwords[1] ), htonl ( dgid->u.dwords[2] ),
197 htonl ( dgid->u.dwords[3] ), dlid, sl, rate );
199 /* Look for a matching cache entry to fill in */
200 if ( ( cached = ib_find_path_cache_entry ( ibdev, dgid ) ) != NULL ) {
201 DBGC ( ibdev, "IBDEV %p cache add for %08x:%08x:%08x:%08x\n",
202 ibdev, htonl ( dgid->u.dwords[0] ),
203 htonl ( dgid->u.dwords[1] ),
204 htonl ( dgid->u.dwords[2] ),
205 htonl ( dgid->u.dwords[3] ) );
214 /** Path record response handler */
215 struct ib_mad_handler ib_path_record_handler __ib_mad_handler = {
216 .mgmt_class = IB_MGMT_CLASS_SUBN_ADM,
217 .class_version = IB_SA_CLASS_VERSION,
218 .method = IB_MGMT_METHOD_GET_RESP,
219 .attr_id = htons ( IB_SA_ATTR_PATH_REC ),
220 .handle = ib_handle_path_record,