[infiniband] Add the concept of a management interface
[people/peper/gpxe.git] / src / net / infiniband / ib_mi.c
1 /*
2  * Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>.
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 FILE_LICENCE ( GPL2_OR_LATER );
20
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <byteswap.h>
28 #include <gpxe/infiniband.h>
29 #include <gpxe/iobuf.h>
30 #include <gpxe/ib_mi.h>
31
32 /**
33  * @file
34  *
35  * Infiniband management interfaces
36  *
37  */
38
39 /** Management interface number of send WQEs
40  *
41  * This is a policy decision.
42  */
43 #define IB_MI_NUM_SEND_WQES 4
44
45 /** Management interface number of receive WQEs
46  *
47  * This is a policy decision.
48  */
49 #define IB_MI_NUM_RECV_WQES 2
50
51 /** Management interface number of completion queue entries
52  *
53  * This is a policy decision
54  */
55 #define IB_MI_NUM_CQES 8
56
57 /** TID magic signature */
58 #define IB_MI_TID_MAGIC ( ( 'g' << 24 ) | ( 'P' << 16 ) | ( 'X' << 8 ) | 'E' )
59
60 /** TID to use for next MAD */
61 static unsigned int next_tid;
62
63 /**
64  * Handle received MAD
65  *
66  * @v ibdev             Infiniband device
67  * @v mi                Management interface
68  * @v mad               Received MAD
69  * @v av                Source address vector
70  * @ret rc              Return status code
71  */
72 static int ib_mi_handle ( struct ib_device *ibdev,
73                           struct ib_mad_interface *mi,
74                           union ib_mad *mad,
75                           struct ib_address_vector *av ) {
76         struct ib_mad_hdr *hdr = &mad->hdr;
77         struct ib_mad_transaction *madx;
78         struct ib_mad_agent *agent;
79         int rc;
80
81         /* Look for a matching transaction by TID */
82         list_for_each_entry ( madx, &mi->madx, list ) {
83                 if ( memcmp ( &hdr->tid, &madx->mad.hdr.tid,
84                               sizeof ( hdr->tid ) ) != 0 )
85                         continue;
86                 /* Get transaction result status */
87                 rc = ( ( hdr->status == htons ( IB_MGMT_STATUS_OK ) ) ?
88                        0 : -EIO );
89                 /* Found a matching transaction */
90                 madx->op->complete ( ibdev, mi, madx, rc, mad, av );
91                 return 0;
92         }
93
94         /* If there is no matching transaction, look for a listening agent */
95         for_each_table_entry ( agent, IB_MAD_AGENTS ) {
96                 if ( ( ( agent->mgmt_class & IB_MGMT_CLASS_MASK ) !=
97                        ( hdr->mgmt_class & IB_MGMT_CLASS_MASK ) ) ||
98                      ( agent->class_version != hdr->class_version ) ||
99                      ( agent->attr_id != hdr->attr_id ) )
100                         continue;
101                 /* Found a matching agent */
102                 agent->handle ( ibdev, mi, mad, av );
103                 return 0;
104         }
105
106         /* Otherwise, ignore it */
107         DBGC ( mi, "MI %p RX TID %08x%08x ignored\n",
108                mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ) );
109         return -ENOTSUP;
110 }
111
112 /**
113  * Complete receive via management interface
114  *
115  *
116  * @v ibdev             Infiniband device
117  * @v qp                Queue pair
118  * @v av                Address vector
119  * @v iobuf             I/O buffer
120  * @v rc                Completion status code
121  */
122 static void ib_mi_complete_recv ( struct ib_device *ibdev,
123                                   struct ib_queue_pair *qp,
124                                   struct ib_address_vector *av,
125                                   struct io_buffer *iobuf, int rc ) {
126         struct ib_mad_interface *mi = ib_qp_get_ownerdata ( qp );
127         union ib_mad *mad;
128         struct ib_mad_hdr *hdr;
129
130         /* Ignore errors */
131         if ( rc != 0 ) {
132                 DBGC ( mi, "MI %p RX error: %s\n", mi, strerror ( rc ) );
133                 goto out;
134         }
135
136         /* Sanity checks */
137         if ( iob_len ( iobuf ) != sizeof ( *mad ) ) {
138                 DBGC ( mi, "MI %p RX bad size (%zd bytes)\n",
139                        mi, iob_len ( iobuf ) );
140                 DBGC_HDA ( mi, 0, iobuf->data, iob_len ( iobuf ) );
141                 goto out;
142         }
143         mad = iobuf->data;
144         hdr = &mad->hdr;
145         if ( hdr->base_version != IB_MGMT_BASE_VERSION ) {
146                 DBGC ( mi, "MI %p RX unsupported base version %x\n",
147                        mi, hdr->base_version );
148                 DBGC_HDA ( mi, 0, mad, sizeof ( *mad ) );
149                 goto out;
150         }
151         DBGC ( mi, "MI %p RX TID %08x%08x (%02x,%02x,%02x,%04x) status "
152                "%04x\n", mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ),
153                hdr->mgmt_class, hdr->class_version, hdr->method,
154                ntohs ( hdr->attr_id ), ntohs ( hdr->status ) );
155         DBGC2_HDA ( mi, 0, mad, sizeof ( *mad ) );
156
157         /* Handle MAD */
158         if ( ( rc = ib_mi_handle ( ibdev, mi, mad, av ) ) != 0 )
159                 goto out;
160
161  out:
162         free_iob ( iobuf );
163 }
164
165 /** Management interface completion operations */
166 static struct ib_completion_queue_operations ib_mi_completion_ops = {
167         .complete_recv = ib_mi_complete_recv,
168 };
169
170 /**
171  * Transmit MAD
172  *
173  * @v ibdev             Infiniband device
174  * @v mi                Management interface
175  * @v mad               MAD
176  * @v av                Destination address vector
177  * @ret rc              Return status code
178  */
179 int ib_mi_send ( struct ib_device *ibdev, struct ib_mad_interface *mi,
180                  union ib_mad *mad, struct ib_address_vector *av ) {
181         struct ib_mad_hdr *hdr = &mad->hdr;
182         struct io_buffer *iobuf;
183         int rc;
184
185         /* Set common fields */
186         hdr->base_version = IB_MGMT_BASE_VERSION;
187         if ( ( hdr->tid[0] == 0 ) && ( hdr->tid[1] == 0 ) ) {
188                 hdr->tid[0] = htonl ( IB_MI_TID_MAGIC );
189                 hdr->tid[1] = htonl ( ++next_tid );
190         }
191         DBGC ( mi, "MI %p TX TID %08x%08x (%02x,%02x,%02x,%04x) status "
192                "%04x\n", mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ),
193                hdr->mgmt_class, hdr->class_version, hdr->method,
194                ntohs ( hdr->attr_id ), ntohs ( hdr->status ) );
195         DBGC2_HDA ( mi, 0, mad, sizeof ( *mad ) );
196
197         /* Construct directed route portion of response, if necessary */
198         if ( hdr->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE ) {
199                 struct ib_mad_smp *smp = &mad->smp;
200                 unsigned int hop_pointer;
201                 unsigned int hop_count;
202
203                 smp->mad_hdr.status |= htons ( IB_SMP_STATUS_D_INBOUND );
204                 hop_pointer = smp->mad_hdr.class_specific.smp.hop_pointer;
205                 hop_count = smp->mad_hdr.class_specific.smp.hop_count;
206                 assert ( hop_count == hop_pointer );
207                 if ( hop_pointer < ( sizeof ( smp->return_path.hops ) /
208                                      sizeof ( smp->return_path.hops[0] ) ) ) {
209                         smp->return_path.hops[hop_pointer] = ibdev->port;
210                 } else {
211                         DBGC ( mi, "MI %p TX TID %08x%08x invalid hop pointer "
212                                "%d\n", mi, ntohl ( hdr->tid[0] ),
213                                ntohl ( hdr->tid[1] ), hop_pointer );
214                         return -EINVAL;
215                 }
216         }
217
218         /* Construct I/O buffer */
219         iobuf = alloc_iob ( sizeof ( *mad ) );
220         if ( ! iobuf ) {
221                 DBGC ( mi, "MI %p could not allocate buffer for TID "
222                        "%08x%08x\n",
223                        mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ) );
224                 return -ENOMEM;
225         }
226         memcpy ( iob_put ( iobuf, sizeof ( *mad ) ), mad, sizeof ( *mad ) );
227
228         /* Send I/O buffer */
229         if ( ( rc = ib_post_send ( ibdev, mi->qp, av, iobuf ) ) != 0 ) {
230                 DBGC ( mi, "MI %p TX TID %08x%08x failed: %s\n",
231                        mi,  ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ),
232                        strerror ( rc ) );
233                 free_iob ( iobuf );
234                 return rc;
235         }
236
237         return 0;
238 }
239
240 /**
241  * Handle management transaction timer expiry
242  *
243  * @v timer             Retry timer
244  * @v expired           Failure indicator
245  */
246 static void ib_mi_timer_expired ( struct retry_timer *timer, int expired ) {
247         struct ib_mad_transaction *madx =
248                 container_of ( timer, struct ib_mad_transaction, timer );
249         struct ib_mad_interface *mi = madx->mi;
250         struct ib_device *ibdev = mi->ibdev;
251         struct ib_mad_hdr *hdr = &madx->mad.hdr;
252
253         /* Abandon transaction if we have tried too many times */
254         if ( expired ) {
255                 DBGC ( mi, "MI %p abandoning TID %08x%08x\n",
256                        mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ) );
257                 madx->op->complete ( ibdev, mi, madx, -ETIMEDOUT, NULL, NULL );
258                 return;
259         }
260
261         /* Restart retransmission timer */
262         start_timer ( timer );
263
264         /* Resend MAD */
265         ib_mi_send ( ibdev, mi, &madx->mad, &madx->av );
266 }
267
268 /**
269  * Create management transaction
270  *
271  * @v ibdev             Infiniband device
272  * @v mi                Management interface
273  * @v mad               MAD to send
274  * @v av                Destination address, or NULL to use SM's GSI
275  * @v op                Management transaction operations
276  * @ret madx            Management transaction, or NULL
277  */
278 struct ib_mad_transaction *
279 ib_create_madx ( struct ib_device *ibdev, struct ib_mad_interface *mi,
280                  union ib_mad *mad, struct ib_address_vector *av,
281                  struct ib_mad_transaction_operations *op ) {
282         struct ib_mad_transaction *madx;
283
284         /* Allocate and initialise structure */
285         madx = zalloc ( sizeof ( *madx ) );
286         if ( ! madx )
287                 return NULL;
288         madx->mi = mi;
289         madx->timer.expired = ib_mi_timer_expired;
290         madx->op = op;
291
292         /* Determine address vector */
293         if ( av ) {
294                 memcpy ( &madx->av, av, sizeof ( madx->av ) );
295         } else {
296                 madx->av.lid = ibdev->sm_lid;
297                 madx->av.sl = ibdev->sm_sl;
298                 madx->av.qpn = IB_QPN_GSI;
299                 madx->av.qkey = IB_QKEY_GSI;
300         }
301
302         /* Copy MAD */
303         memcpy ( &madx->mad, mad, sizeof ( madx->mad ) );
304
305         /* Add to list and start timer to send initial MAD */
306         list_add ( &madx->list, &mi->madx );
307         start_timer_nodelay ( &madx->timer );
308
309         return madx;
310 }
311
312 /**
313  * Destroy management transaction
314  *
315  * @v ibdev             Infiniband device
316  * @v mi                Management interface
317  * @v madx              Management transaction
318  */
319 void ib_destroy_madx ( struct ib_device *ibdev __unused,
320                        struct ib_mad_interface *mi __unused,
321                        struct ib_mad_transaction *madx ) {
322
323         /* Stop timer and remove from list */
324         stop_timer ( &madx->timer );
325         list_del ( &madx->list );
326
327         /* Free transaction */
328         free ( madx );
329 }
330
331 /**
332  * Create management interface
333  *
334  * @v ibdev             Infiniband device
335  * @v type              Queue pair type
336  * @ret mi              Management agent, or NULL
337  */
338 struct ib_mad_interface * ib_create_mi ( struct ib_device *ibdev,
339                                          enum ib_queue_pair_type type ) {
340         struct ib_mad_interface *mi;
341         int rc;
342
343         /* Allocate and initialise fields */
344         mi = zalloc ( sizeof ( *mi ) );
345         if ( ! mi )
346                 goto err_alloc;
347         mi->ibdev = ibdev;
348         INIT_LIST_HEAD ( &mi->madx );
349
350         /* Create completion queue */
351         mi->cq = ib_create_cq ( ibdev, IB_MI_NUM_CQES, &ib_mi_completion_ops );
352         if ( ! mi->cq ) {
353                 DBGC ( mi, "MI %p could not allocate completion queue\n", mi );
354                 goto err_create_cq;
355         }
356
357         /* Create queue pair */
358         mi->qp = ib_create_qp ( ibdev, type, IB_MI_NUM_SEND_WQES, mi->cq,
359                                 IB_MI_NUM_RECV_WQES, mi->cq );
360         if ( ! mi->qp ) {
361                 DBGC ( mi, "MI %p could not allocate queue pair\n", mi );
362                 goto err_create_qp;
363         }
364         ib_qp_set_ownerdata ( mi->qp, mi );
365         DBGC ( mi, "MI %p (%s) running on QPN %#lx\n",
366                mi, ( ( type == IB_QPT_SMI ) ? "SMI" : "GSI" ), mi->qp->qpn );
367
368         /* Set queue key */
369         mi->qp->qkey = ( ( type == IB_QPT_SMI ) ? IB_QKEY_SMI : IB_QKEY_GSI );
370         if ( ( rc = ib_modify_qp ( ibdev, mi->qp ) ) != 0 ) {
371                 DBGC ( mi, "MI %p could not set queue key: %s\n",
372                        mi, strerror ( rc ) );
373                 goto err_modify_qp;
374         }
375
376         /* Fill receive ring */
377         ib_refill_recv ( ibdev, mi->qp );
378         return mi;
379
380  err_modify_qp:
381         ib_destroy_qp ( ibdev, mi->qp );
382  err_create_qp:
383         ib_destroy_cq ( ibdev, mi->cq );
384  err_create_cq:
385         free ( mi );
386  err_alloc:
387         return NULL;
388 }
389
390 /**
391  * Destroy management interface
392  *
393  * @v mi                Management interface
394  */
395 void ib_destroy_mi ( struct ib_device *ibdev, struct ib_mad_interface *mi ) {
396         struct ib_mad_transaction *madx;
397         struct ib_mad_transaction *tmp;
398
399         /* Flush any outstanding requests */
400         list_for_each_entry_safe ( madx, tmp, &mi->madx, list ) {
401                 DBGC ( mi, "MI %p destroyed while TID %08x%08x in progress\n",
402                        mi, ntohl ( madx->mad.hdr.tid[0] ),
403                        ntohl ( madx->mad.hdr.tid[1] ) );
404                 madx->op->complete ( ibdev, mi, madx, -ECANCELED, NULL, NULL );
405         }
406
407         ib_destroy_qp ( ibdev, mi->qp );
408         ib_destroy_cq ( ibdev, mi->cq );
409         free ( mi );
410 }