39d11285a3fa2b5bf2f9778d16e922c9467e5632
[people/mdeck/gpxe.git] / src / net / infiniband.c
1 /*
2  * Copyright (C) 2007 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 #include <stdint.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <byteswap.h>
25 #include <errno.h>
26 #include <assert.h>
27 #include <gpxe/list.h>
28 #include <gpxe/if_arp.h>
29 #include <gpxe/netdevice.h>
30 #include <gpxe/iobuf.h>
31 #include <gpxe/ipoib.h>
32 #include <gpxe/infiniband.h>
33
34 /** @file
35  *
36  * Infiniband protocol
37  *
38  */
39
40 /**
41  * Create completion queue
42  *
43  * @v ibdev             Infiniband device
44  * @v num_cqes          Number of completion queue entries
45  * @ret cq              New completion queue
46  */
47 struct ib_completion_queue * ib_create_cq ( struct ib_device *ibdev,
48                                             unsigned int num_cqes ) {
49         struct ib_completion_queue *cq;
50         int rc;
51
52         DBGC ( ibdev, "IBDEV %p creating completion queue\n", ibdev );
53
54         /* Allocate and initialise data structure */
55         cq = zalloc ( sizeof ( *cq ) );
56         if ( ! cq )
57                 return NULL;
58         cq->num_cqes = num_cqes;
59         INIT_LIST_HEAD ( &cq->work_queues );
60
61         /* Perform device-specific initialisation and get CQN */
62         if ( ( rc = ibdev->op->create_cq ( ibdev, cq ) ) != 0 ) {
63                 DBGC ( ibdev, "IBDEV %p could not initialise completion "
64                        "queue: %s\n", ibdev, strerror ( rc ) );
65                 free ( cq );
66                 return NULL;
67         }
68
69         DBGC ( ibdev, "IBDEV %p created %d-entry completion queue %p (%p) "
70                "with CQN %#lx\n", ibdev, num_cqes, cq,
71                ib_cq_get_drvdata ( cq ), cq->cqn );
72         return cq;
73 }
74
75 /**
76  * Destroy completion queue
77  *
78  * @v ibdev             Infiniband device
79  * @v cq                Completion queue
80  */
81 void ib_destroy_cq ( struct ib_device *ibdev,
82                      struct ib_completion_queue *cq ) {
83         DBGC ( ibdev, "IBDEV %p destroying completion queue %#lx\n",
84                ibdev, cq->cqn );
85         assert ( list_empty ( &cq->work_queues ) );
86         ibdev->op->destroy_cq ( ibdev, cq );
87         free ( cq );
88 }
89
90 /**
91  * Create queue pair
92  *
93  * @v ibdev             Infiniband device
94  * @v num_send_wqes     Number of send work queue entries
95  * @v send_cq           Send completion queue
96  * @v num_recv_wqes     Number of receive work queue entries
97  * @v recv_cq           Receive completion queue
98  * @v qkey              Queue key
99  * @ret qp              Queue pair
100  */
101 struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev,
102                                       unsigned int num_send_wqes,
103                                       struct ib_completion_queue *send_cq,
104                                       unsigned int num_recv_wqes,
105                                       struct ib_completion_queue *recv_cq,
106                                       unsigned long qkey ) {
107         struct ib_queue_pair *qp;
108         size_t total_size;
109         int rc;
110
111         DBGC ( ibdev, "IBDEV %p creating queue pair\n", ibdev );
112
113         /* Allocate and initialise data structure */
114         total_size = ( sizeof ( *qp ) +
115                        ( num_send_wqes * sizeof ( qp->send.iobufs[0] ) ) +
116                        ( num_recv_wqes * sizeof ( qp->recv.iobufs[0] ) ) );
117         qp = zalloc ( total_size );
118         if ( ! qp )
119                 return NULL;
120         qp->qkey = qkey;
121         qp->send.qp = qp;
122         qp->send.is_send = 1;
123         qp->send.cq = send_cq;
124         list_add ( &qp->send.list, &send_cq->work_queues );
125         qp->send.num_wqes = num_send_wqes;
126         qp->send.iobufs = ( ( ( void * ) qp ) + sizeof ( *qp ) );
127         qp->recv.qp = qp;
128         qp->recv.cq = recv_cq;
129         list_add ( &qp->recv.list, &recv_cq->work_queues );
130         qp->recv.num_wqes = num_recv_wqes;
131         qp->recv.iobufs = ( ( ( void * ) qp ) + sizeof ( *qp ) +
132                             ( num_send_wqes * sizeof ( qp->send.iobufs[0] ) ));
133
134         /* Perform device-specific initialisation and get QPN */
135         if ( ( rc = ibdev->op->create_qp ( ibdev, qp ) ) != 0 ) {
136                 DBGC ( ibdev, "IBDEV %p could not initialise queue pair: "
137                        "%s\n", ibdev, strerror ( rc ) );
138                 list_del ( &qp->send.list );
139                 list_del ( &qp->recv.list );
140                 free ( qp );
141                 return NULL;
142         }
143
144         DBGC ( ibdev, "IBDEV %p created queue pair %p (%p) with QPN %#lx\n",
145                ibdev, qp, ib_qp_get_drvdata ( qp ), qp->qpn );
146         DBGC ( ibdev, "IBDEV %p QPN %#lx has %d send entries at [%p,%p)\n",
147                ibdev, qp->qpn, num_send_wqes, qp->send.iobufs,
148                qp->recv.iobufs );
149         DBGC ( ibdev, "IBDEV %p QPN %#lx has %d receive entries at [%p,%p)\n",
150                ibdev, qp->qpn, num_recv_wqes, qp->recv.iobufs,
151                ( ( ( void * ) qp ) + total_size ) );
152         return qp;
153 }
154
155 /**
156  * Destroy queue pair
157  *
158  * @v ibdev             Infiniband device
159  * @v qp                Queue pair
160  */
161 void ib_destroy_qp ( struct ib_device *ibdev,
162                      struct ib_queue_pair *qp ) {
163         DBGC ( ibdev, "IBDEV %p destroying queue pair %#lx\n",
164                ibdev, qp->qpn );
165         ibdev->op->destroy_qp ( ibdev, qp );
166         list_del ( &qp->send.list );
167         list_del ( &qp->recv.list );
168         free ( qp );
169 }
170
171 /**
172  * Find work queue belonging to completion queue
173  *
174  * @v cq                Completion queue
175  * @v qpn               Queue pair number
176  * @v is_send           Find send work queue (rather than receive)
177  * @ret wq              Work queue, or NULL if not found
178  */
179 struct ib_work_queue * ib_find_wq ( struct ib_completion_queue *cq,
180                                     unsigned long qpn, int is_send ) {
181         struct ib_work_queue *wq;
182
183         list_for_each_entry ( wq, &cq->work_queues, list ) {
184                 if ( ( wq->qp->qpn == qpn ) && ( wq->is_send == is_send ) )
185                         return wq;
186         }
187         return NULL;
188 }
189
190 /***************************************************************************
191  *
192  * Management datagram operations
193  *
194  ***************************************************************************
195  */
196
197 /**
198  * Get port information
199  *
200  * @v ibdev             Infiniband device
201  * @v port_info         Port information datagram to fill in
202  * @ret rc              Return status code
203  */
204 static int ib_get_port_info ( struct ib_device *ibdev,
205                               struct ib_mad_port_info *port_info ) {
206         struct ib_mad_hdr *hdr = &port_info->mad_hdr;
207         int rc;
208
209         /* Construct MAD */
210         memset ( port_info, 0, sizeof ( *port_info ) );
211         hdr->base_version = IB_MGMT_BASE_VERSION;
212         hdr->mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
213         hdr->class_version = 1;
214         hdr->method = IB_MGMT_METHOD_GET;
215         hdr->attr_id = htons ( IB_SMP_ATTR_PORT_INFO );
216         hdr->attr_mod = htonl ( ibdev->port );
217
218         if ( ( rc = ib_mad ( ibdev, hdr, sizeof ( *port_info ) ) ) != 0 ) {
219                 DBGC ( ibdev, "IBDEV %p could not get port info: %s\n",
220                        ibdev, strerror ( rc ) );
221                 return rc;
222         }
223         return 0;
224 }
225
226 /**
227  * Get GUID information
228  *
229  * @v ibdev             Infiniband device
230  * @v guid_info         GUID information datagram to fill in
231  * @ret rc              Return status code
232  */
233 static int ib_get_guid_info ( struct ib_device *ibdev,
234                               struct ib_mad_guid_info *guid_info ) {
235         struct ib_mad_hdr *hdr = &guid_info->mad_hdr;
236         int rc;
237
238         /* Construct MAD */
239         memset ( guid_info, 0, sizeof ( *guid_info ) );
240         hdr->base_version = IB_MGMT_BASE_VERSION;
241         hdr->mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
242         hdr->class_version = 1;
243         hdr->method = IB_MGMT_METHOD_GET;
244         hdr->attr_id = htons ( IB_SMP_ATTR_GUID_INFO );
245
246         if ( ( rc = ib_mad ( ibdev, hdr, sizeof ( *guid_info ) ) ) != 0 ) {
247                 DBGC ( ibdev, "IBDEV %p could not get GUID info: %s\n",
248                        ibdev, strerror ( rc ) );
249                 return rc;
250         }
251         return 0;
252 }
253
254 /**
255  * Get partition key table
256  *
257  * @v ibdev             Infiniband device
258  * @v guid_info         Partition key table datagram to fill in
259  * @ret rc              Return status code
260  */
261 static int ib_get_pkey_table ( struct ib_device *ibdev,
262                                struct ib_mad_pkey_table *pkey_table ) {
263         struct ib_mad_hdr *hdr = &pkey_table->mad_hdr;
264         int rc;
265
266         /* Construct MAD */
267         memset ( pkey_table, 0, sizeof ( *pkey_table ) );
268         hdr->base_version = IB_MGMT_BASE_VERSION;
269         hdr->mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
270         hdr->class_version = 1;
271         hdr->method = IB_MGMT_METHOD_GET;
272         hdr->attr_id = htons ( IB_SMP_ATTR_PKEY_TABLE );
273
274         if ( ( rc = ib_mad ( ibdev, hdr, sizeof ( *pkey_table ) ) ) != 0 ) {
275                 DBGC ( ibdev, "IBDEV %p could not get pkey table: %s\n",
276                        ibdev, strerror ( rc ) );
277                 return rc;
278         }
279         return 0;
280 }
281
282 /**
283  * Wait for link up
284  *
285  * @v ibdev             Infiniband device
286  * @ret rc              Return status code
287  *
288  * This function shouldn't really exist.  Unfortunately, IB links take
289  * a long time to come up, and we can't get various key parameters
290  * e.g. our own IPoIB MAC address without information from the subnet
291  * manager).  We should eventually make link-up an asynchronous event.
292  */
293 static int ib_wait_for_link ( struct ib_device *ibdev ) {
294         struct ib_mad_port_info port_info;
295         unsigned int retries;
296         int rc;
297
298         printf ( "Waiting for Infiniband link-up..." );
299         for ( retries = 20 ; retries ; retries-- ) {
300                 if ( ( rc = ib_get_port_info ( ibdev, &port_info ) ) != 0 )
301                         continue;
302                 if ( ( ( port_info.port_state__link_speed_supported ) & 0xf )
303                      == 4 ) {
304                         printf ( "ok\n" );
305                         return 0;
306                 }
307                 printf ( "." );
308                 sleep ( 1 );
309         }
310         printf ( "failed\n" );
311         return -ENODEV;
312 };
313
314 /**
315  * Get MAD parameters
316  *
317  * @v ibdev             Infiniband device
318  * @ret rc              Return status code
319  */
320 static int ib_get_mad_params ( struct ib_device *ibdev ) {
321         union {
322                 /* This union exists just to save stack space */
323                 struct ib_mad_port_info port_info;
324                 struct ib_mad_guid_info guid_info;
325                 struct ib_mad_pkey_table pkey_table;
326         } u;
327         int rc;
328
329         /* Port info gives us the first half of the port GID and the SM LID */
330         if ( ( rc = ib_get_port_info ( ibdev, &u.port_info ) ) != 0 )
331                 return rc;
332         memcpy ( &ibdev->port_gid.u.bytes[0], u.port_info.gid_prefix, 8 );
333         ibdev->sm_lid = ntohs ( u.port_info.mastersm_lid );
334
335         /* GUID info gives us the second half of the port GID */
336         if ( ( rc = ib_get_guid_info ( ibdev, &u.guid_info ) ) != 0 )
337                 return rc;
338         memcpy ( &ibdev->port_gid.u.bytes[8], u.guid_info.gid_local, 8 );
339
340         /* Get partition key */
341         if ( ( rc = ib_get_pkey_table ( ibdev, &u.pkey_table ) ) != 0 )
342                 return rc;
343         ibdev->pkey = ntohs ( u.pkey_table.pkey[0][0] );
344
345         DBGC ( ibdev, "IBDEV %p port GID is %08lx:%08lx:%08lx:%08lx\n",
346                ibdev, htonl ( ibdev->port_gid.u.dwords[0] ),
347                htonl ( ibdev->port_gid.u.dwords[1] ),
348                htonl ( ibdev->port_gid.u.dwords[2] ),
349                htonl ( ibdev->port_gid.u.dwords[3] ) );
350
351         return 0;
352 }
353
354 /***************************************************************************
355  *
356  * Infiniband device creation/destruction
357  *
358  ***************************************************************************
359  */
360
361 /**
362  * Allocate Infiniband device
363  *
364  * @v priv_size         Size of driver private data area
365  * @ret ibdev           Infiniband device, or NULL
366  */
367 struct ib_device * alloc_ibdev ( size_t priv_size ) {
368         struct ib_device *ibdev;
369         void *drv_priv;
370         size_t total_len;
371
372         total_len = ( sizeof ( *ibdev ) + priv_size );
373         ibdev = zalloc ( total_len );
374         if ( ibdev ) {
375                 drv_priv = ( ( ( void * ) ibdev ) + sizeof ( *ibdev ) );
376                 ib_set_drvdata ( ibdev, drv_priv );
377         }
378         return ibdev;
379 }
380
381 /**
382  * Register Infiniband device
383  *
384  * @v ibdev             Infiniband device
385  * @ret rc              Return status code
386  */
387 int register_ibdev ( struct ib_device *ibdev ) {
388         int rc;
389
390         /* Open link */
391         if ( ( rc = ib_open ( ibdev ) ) != 0 )
392                 goto err_open;
393
394         /* Wait for link */
395         if ( ( rc = ib_wait_for_link ( ibdev ) ) != 0 )
396                 goto err_wait_for_link;
397
398         /* Get MAD parameters */
399         if ( ( rc = ib_get_mad_params ( ibdev ) ) != 0 )
400                 goto err_get_mad_params;
401
402         /* Add IPoIB device */
403         if ( ( rc = ipoib_probe ( ibdev ) ) != 0 ) {
404                 DBGC ( ibdev, "IBDEV %p could not add IPoIB device: %s\n",
405                        ibdev, strerror ( rc ) );
406                 goto err_ipoib_probe;
407         }
408
409         return 0;
410
411  err_ipoib_probe:
412  err_get_mad_params:
413  err_wait_for_link:
414         ib_close ( ibdev );
415  err_open:
416         return rc;
417 }
418
419 /**
420  * Unregister Infiniband device
421  *
422  * @v ibdev             Infiniband device
423  */
424 void unregister_ibdev ( struct ib_device *ibdev ) {
425         ipoib_remove ( ibdev );
426         ib_close ( ibdev );
427 }
428
429 /**
430  * Free Infiniband device
431  *
432  * @v ibdev             Infiniband device
433  */
434 void free_ibdev ( struct ib_device *ibdev ) {
435         free ( ibdev );
436 }
437