7da15fed6a43ff6935e24572783cc2e6ee4d5cd9
[mirror/winof/.git] / core / al / al_cm_shared.c
1 /*\r
2  * Copyright (c) 2005 SilverStorm Technologies.  All rights reserved.\r
3  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved. \r
4  *\r
5  * This software is available to you under the OpenIB.org BSD license\r
6  * below:\r
7  *\r
8  *     Redistribution and use in source and binary forms, with or\r
9  *     without modification, are permitted provided that the following\r
10  *     conditions are met:\r
11  *\r
12  *      - Redistributions of source code must retain the above\r
13  *        copyright notice, this list of conditions and the following\r
14  *        disclaimer.\r
15  *\r
16  *      - Redistributions in binary form must reproduce the above\r
17  *        copyright notice, this list of conditions and the following\r
18  *        disclaimer in the documentation and/or other materials\r
19  *        provided with the distribution.\r
20  *\r
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
22  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
23  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
24  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
25  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
26  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
27  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
28  * SOFTWARE.\r
29  *\r
30  * $Id$\r
31  */\r
32 \r
33 #include "al_ci_ca.h"\r
34 #include "al_cm_shared.h"\r
35 #include "al_debug.h"\r
36 #include "al_qp.h"\r
37 \r
38 \r
39 \r
40 static ib_api_status_t\r
41 __reset_qp(\r
42         IN              const   ib_qp_handle_t                          h_qp )\r
43 {\r
44         ib_qp_mod_t                     qp_mod;\r
45         ib_api_status_t         status;\r
46 \r
47         AL_ENTER( AL_DBG_CM );\r
48 \r
49         cl_memclr( &qp_mod, sizeof(ib_qp_mod_t) );\r
50         qp_mod.req_state = IB_QPS_RESET;\r
51 \r
52         status = ib_modify_qp( h_qp, &qp_mod );\r
53         if( status != IB_SUCCESS )\r
54         {\r
55                 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
56                         ("ib_modify_qp to IB_QPS_RESET returned %s\n",\r
57                         ib_get_err_str(status) ) );\r
58         }\r
59 \r
60         AL_EXIT( AL_DBG_CM );\r
61         return status;\r
62 }\r
63 \r
64 \r
65 \r
66 static ib_api_status_t\r
67 __init_qp(\r
68         IN              const   ib_qp_handle_t                          h_qp,\r
69         IN                              ib_gid_t * const                        p_gid,\r
70         IN              const   uint16_t                                        pkey,\r
71         IN              const   ib_access_t                                     access_ctrl )\r
72 {\r
73         ib_port_attr_t          *p_port_attr;\r
74         ib_qp_mod_t                     qp_mod;\r
75         ib_api_status_t         status;\r
76 \r
77         AL_ENTER( AL_DBG_CM );\r
78 \r
79         cl_memclr( &qp_mod, sizeof(ib_qp_mod_t) );\r
80         qp_mod.req_state = IB_QPS_INIT;\r
81         qp_mod.state.init.access_ctrl = access_ctrl;\r
82 \r
83         /* Get the port and PKEY information. */\r
84         ci_ca_lock_attr( h_qp->obj.p_ci_ca );\r
85         p_port_attr = get_port_attr( h_qp->obj.p_ci_ca->p_pnp_attr, p_gid );\r
86         if( !p_port_attr )\r
87         {\r
88                 ci_ca_unlock_attr( h_qp->obj.p_ci_ca );\r
89                 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("invalid p_gid\n" ) );\r
90                 return IB_INVALID_GID;\r
91         }\r
92 \r
93         qp_mod.state.init.primary_port = p_port_attr->port_num;\r
94         qp_mod.state.init.pkey_index = get_pkey_index( p_port_attr, pkey );\r
95         ci_ca_unlock_attr( h_qp->obj.p_ci_ca );\r
96 \r
97         if( qp_mod.state.init.pkey_index == BAD_PKEY_INDEX )\r
98         {\r
99                 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("invalid PKEY\n" ) );\r
100                 return IB_INVALID_PKEY;\r
101         }\r
102 \r
103         status = ib_modify_qp( h_qp, &qp_mod );\r
104         if( status != IB_SUCCESS )\r
105         {\r
106                 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
107                         ("ib_modify_qp to IB_QPS_INIT returned %s\n",\r
108                         ib_get_err_str(status) ) );\r
109         }\r
110 \r
111         AL_EXIT( AL_DBG_CM );\r
112         return status;\r
113 }\r
114 \r
115 \r
116 \r
117 /*\r
118  * Transition the QP to the INIT state, if it is not already in the\r
119  * INIT state.\r
120  */\r
121 ib_api_status_t\r
122 cm_init_qp(\r
123         IN              const   ib_qp_handle_t                          h_qp,\r
124         IN                              ib_gid_t * const                        p_gid,\r
125         IN              const   uint16_t                                        pkey,\r
126         IN              const   ib_access_t                                     access_ctrl )\r
127 {\r
128         ib_api_status_t                 status;\r
129 \r
130         /*\r
131          * Move to the init state to allow posting of receive buffers.\r
132          * Chech the current state of the QP.  The user may have already\r
133          * transitioned it and posted some receives to the QP, so we\r
134          * should not reset the QP if it is already in the INIT state.\r
135          */\r
136         if( h_qp->state != IB_QPS_INIT )\r
137         {\r
138                 /* Reset the QP. */\r
139                 status = __reset_qp( h_qp );\r
140                 if( status != IB_SUCCESS )\r
141                 {\r
142                         AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
143                                 ("__reset_qp returned %s\n", ib_get_err_str(status) ) );\r
144                         return status;\r
145                 }\r
146 \r
147                 /* Initialize the QP. */\r
148                 status = __init_qp( h_qp, p_gid, pkey, access_ctrl );\r
149                 if( status != IB_SUCCESS )\r
150                 {\r
151                         AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
152                                 ("__init_qp returned %s.\n", ib_get_err_str(status) ) );\r
153                         return status;\r
154                 }\r
155         }\r
156 \r
157         return IB_SUCCESS;\r
158 }\r
159 \r
160 \r
161 \r
162 ib_api_status_t\r
163 cm_rts_qp(\r
164         IN              const   ib_qp_handle_t                          h_qp,\r
165         IN                              ib_qp_mod_t     * const                 p_qp_mod_rtr,\r
166         IN                              ib_qp_mod_t * const                     p_qp_mod_rts )\r
167 {\r
168         ib_api_status_t status;\r
169 \r
170         AL_ENTER( AL_DBG_CM );\r
171 \r
172         /* Set the QP to RTR. */\r
173         status = ib_modify_qp( h_qp, p_qp_mod_rtr );\r
174         if( status != IB_SUCCESS )\r
175         {\r
176                 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
177                         ("ib_modify_qp to RTR returned %s.\n", ib_get_err_str(status) ) );\r
178                 return status;\r
179         }\r
180 \r
181         /* Set the QP to RTS. */\r
182         status = ib_modify_qp( h_qp, p_qp_mod_rts );\r
183         if( status != IB_SUCCESS )\r
184         {\r
185                 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
186                         ("ib_modify_qp to RTS returned %s.\n", ib_get_err_str(status) ) );\r
187                 return status;\r
188         }\r
189 \r
190         AL_EXIT( AL_DBG_CM );\r
191         return IB_SUCCESS;\r
192 }\r
193 \r
194 \r
195 \r
196 /*\r
197  * Transition the QP to the error state to flush all oustanding work\r
198  * requests, then transition it back into the reset state.  This function\r
199  * may be called when destroying the QP in order to flush all work requests,\r
200  * so we cannot call through the main API, or the call will fail since the\r
201  * QP is no longer in the initialize state.\r
202  */\r
203 ib_api_status_t\r
204 cm_reset_qp(\r
205         IN              const   ib_qp_handle_t                          h_qp,\r
206         IN              const   uint32_t                                        timewait )\r
207 {\r
208         ib_qp_mod_t                     qp_mod;\r
209         ib_api_status_t         status;\r
210 \r
211         AL_ENTER( AL_DBG_CM );\r
212 \r
213         /* Special checks on the QP state for error handling - see above. */\r
214         if( !h_qp || !AL_OBJ_IS_TYPE( h_qp, AL_OBJ_TYPE_H_QP ) ||\r
215                 ( (h_qp->obj.state != CL_INITIALIZED) && \r
216                   (h_qp->obj.state != CL_DESTROYING) ) )\r
217         {\r
218                 AL_PRINT_EXIT( TRACE_LEVEL_WARNING, AL_DBG_CM, ("IB_INVALID_QP_HANDLE\n") );\r
219                 return IB_INVALID_QP_HANDLE;\r
220         }\r
221 \r
222         /*\r
223          * Only modify the QP if it is not exported to UM.  All UM QP modifications\r
224          * are initiated in UM.\r
225          */\r
226         if( !AL_OBJ_IS_SUBTYPE( h_qp, AL_OBJ_SUBTYPE_UM_EXPORT ) )\r
227         {\r
228                 cl_memclr( &qp_mod, sizeof(ib_qp_mod_t) );\r
229                 qp_mod.req_state = IB_QPS_ERROR;\r
230 \r
231                 /* Modify to error state using function pointers - see above. */\r
232                 status = h_qp->pfn_modify_qp( h_qp, &qp_mod, NULL );\r
233                 if( status != IB_SUCCESS )\r
234                 {\r
235                         AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
236                                 ("pfn_modify_qp to IB_QPS_ERROR returned %s\n",\r
237                                 ib_get_err_str(status) ) );\r
238                         return status;\r
239                 }\r
240         }\r
241 \r
242 #ifdef CL_KERNEL\r
243         /* Store the timestamp after which the QP exits timewait. */\r
244         h_qp->timewait = cl_get_time_stamp() + (((uint64_t)timewait) * 1000);\r
245 #else\r
246         UNUSED_PARAM( timewait );\r
247 #endif  /* CL_KERNEL */\r
248 \r
249         AL_EXIT( AL_DBG_CM );\r
250         return IB_SUCCESS;\r
251 }\r
252 \r
253 \r
254 \r
255 /*\r
256  * Save the information from a user's CM reply needed to transition the QP\r
257  * to the RTR and RTS states.\r
258  */\r
259 void\r
260 cm_save_rep_qp_attr(\r
261         IN              const   ib_cm_rep_t * const                     p_cm_rep,\r
262         IN              const   ib_port_attr_t * const          p_port_attr,\r
263         IN              const   ib_path_rec_t * const           p_path_rec,\r
264         IN              const   ib_path_rec_t * const           p_alt_path_rec,\r
265         IN OUT                  ib_qp_mod_t * const                     p_qp_mod_rtr,\r
266         IN OUT                  ib_qp_mod_t * const                     p_qp_mod_rts )\r
267 {\r
268         AL_ENTER( AL_DBG_CM );\r
269 \r
270         /* Update the QP RTR transition information. */\r
271         p_qp_mod_rtr->state.rtr.rq_psn = p_cm_rep->h_qp->num;\r
272 \r
273         if( p_cm_rep->access_ctrl )\r
274         {\r
275                 p_qp_mod_rtr->state.rtr.opts |= IB_MOD_QP_ACCESS_CTRL;\r
276                 p_qp_mod_rtr->state.rtr.access_ctrl = p_cm_rep->access_ctrl;\r
277         }\r
278 \r
279         if( p_cm_rep->sq_depth )\r
280         {\r
281                 p_qp_mod_rtr->state.rtr.opts |= IB_MOD_QP_SQ_DEPTH;\r
282                 p_qp_mod_rtr->state.rtr.sq_depth = p_cm_rep->sq_depth;\r
283         }\r
284 \r
285         if( p_cm_rep->rq_depth )\r
286         {\r
287                 p_qp_mod_rtr->state.rtr.opts |= IB_MOD_QP_RQ_DEPTH;\r
288                 p_qp_mod_rtr->state.rtr.rq_depth = p_cm_rep->rq_depth;\r
289         }\r
290 \r
291         if( p_cm_rep->rnr_nak_timeout )\r
292         {\r
293                 p_qp_mod_rtr->state.rtr.opts |= IB_MOD_QP_RNR_NAK_TIMEOUT;\r
294                 p_qp_mod_rtr->state.rtr.rnr_nak_timeout = p_cm_rep->rnr_nak_timeout;\r
295         }\r
296 \r
297         /* Some common checks for QP RTR and RTS transition information. */\r
298         if( p_cm_rep->h_qp && p_cm_rep->h_qp->type == IB_QPT_RELIABLE_CONN )\r
299         {\r
300                 p_qp_mod_rts->state.rts.init_depth = p_cm_rep->init_depth;\r
301         }\r
302 \r
303         /* Update the QP RTS transition information starting here. */\r
304         p_qp_mod_rts->state.rts.retry_cnt = p_cm_rep->rnr_retry_cnt;\r
305         p_qp_mod_rts->state.rts.rnr_retry_cnt = p_cm_rep->rnr_retry_cnt;\r
306         p_qp_mod_rts->state.rts.rnr_nak_timeout = p_cm_rep->rnr_nak_timeout;\r
307 \r
308         if( p_cm_rep->failover_accepted == IB_FAILOVER_ACCEPT_SUCCESS )\r
309         {\r
310                 p_qp_mod_rts->state.rts.opts |= IB_MOD_QP_APM_STATE;\r
311                 p_qp_mod_rts->state.rts.apm_state = IB_APM_REARM;\r
312         }\r
313 \r
314         /* Setup the primary address vector. */\r
315         p_qp_mod_rtr->state.rtr.opts |= IB_MOD_QP_PRIMARY_AV;\r
316         cm_save_path_av( p_port_attr, p_path_rec, p_cm_rep->rnr_retry_cnt,\r
317                 p_cm_rep->rnr_retry_cnt, &p_qp_mod_rtr->state.rtr.primary_av );\r
318 \r
319         /* Setup alternate address vector. */\r
320         if( p_alt_path_rec->slid )\r
321         {\r
322                 p_qp_mod_rtr->state.rtr.opts |= IB_MOD_QP_ALTERNATE_AV;\r
323                 cm_save_path_av( p_port_attr, p_alt_path_rec, p_cm_rep->rnr_retry_cnt,\r
324                         p_cm_rep->rnr_retry_cnt, &p_qp_mod_rtr->state.rtr.alternate_av );\r
325         }\r
326 \r
327         p_qp_mod_rts->state.rts.local_ack_timeout =\r
328                 cm_local_ack_timeout( ib_path_rec_pkt_life( p_path_rec ),\r
329                 p_cm_rep->target_ack_delay );\r
330 \r
331         AL_EXIT( AL_DBG_CM );\r
332 }\r
333 \r
334 \r
335 \r
336 /*\r
337  * Save the information from a user's CM RTU needed to transition the QP\r
338  * to the RTR and RTS states.\r
339  */\r
340 void\r
341 cm_save_rtu_qp_attr(\r
342         IN              const   ib_cm_rtu_t * const                     p_cm_rtu,\r
343         IN OUT                  ib_qp_mod_t * const                     p_qp_mod_rtr )\r
344 {\r
345         AL_ENTER( AL_DBG_CM );\r
346 \r
347         /* Update the QP RTR transition information. */\r
348         if( p_cm_rtu->access_ctrl )\r
349         {\r
350                 p_qp_mod_rtr->state.rtr.opts |= IB_MOD_QP_ACCESS_CTRL;\r
351                 p_qp_mod_rtr->state.rtr.access_ctrl = p_cm_rtu->access_ctrl;\r
352         }\r
353 \r
354         if( p_cm_rtu->sq_depth )\r
355         {\r
356                 p_qp_mod_rtr->state.rtr.opts |= IB_MOD_QP_SQ_DEPTH;\r
357                 p_qp_mod_rtr->state.rtr.sq_depth = p_cm_rtu->sq_depth;\r
358         }\r
359 \r
360         if( p_cm_rtu->rq_depth )\r
361         {\r
362                 p_qp_mod_rtr->state.rtr.opts |= IB_MOD_QP_RQ_DEPTH;\r
363                 p_qp_mod_rtr->state.rtr.rq_depth = p_cm_rtu->rq_depth;\r
364         }\r
365 \r
366         AL_EXIT( AL_DBG_CM );\r
367 }\r
368 \r
369 \r
370 \r
371 /*\r
372  * Save the information about a path record needed to specify an\r
373  * address vector for a connected QP.\r
374  */\r
375 void\r
376 cm_save_path_av(\r
377         IN              const   ib_port_attr_t * const          p_port_attr,\r
378         IN              const   ib_path_rec_t * const           p_path_rec,\r
379         IN              const   uint8_t                                         retry_cnt,\r
380         IN              const   uint8_t                                         rnr_retry_cnt,\r
381                 OUT                     ib_av_attr_t * const            p_av )\r
382 {\r
383         p_av->port_num = p_port_attr->port_num;\r
384         p_av->sl = ib_path_rec_sl( p_path_rec );\r
385         p_av->dlid = p_path_rec->dlid;\r
386 \r
387         if( p_path_rec->hop_flow_raw.val )\r
388         {\r
389                 p_av->grh_valid = TRUE;\r
390                 p_av->grh.ver_class_flow = ib_grh_set_ver_class_flow( 1,\r
391                         p_path_rec->tclass, ib_path_rec_flow_lbl( p_path_rec ) );\r
392                 p_av->grh.resv1 = 0;\r
393                 p_av->grh.resv2 = 0;\r
394                 p_av->grh.hop_limit = ib_path_rec_hop_limit( p_path_rec );\r
395                 p_av->grh.src_gid = p_path_rec->sgid;\r
396                 p_av->grh.dest_gid = p_path_rec->dgid;\r
397         }\r
398         else\r
399         {\r
400                 p_av->grh_valid = FALSE;\r
401         }\r
402 \r
403         p_av->static_rate = ib_path_rec_rate( p_path_rec );\r
404         p_av->path_bits = (uint8_t)(p_path_rec->slid - p_port_attr->lid);\r
405         p_av->conn.path_mtu = ib_path_rec_mtu( p_path_rec );\r
406         p_av->conn.local_ack_timeout = cm_local_ack_timeout(\r
407                 ib_path_rec_pkt_life( p_path_rec ), 0 );\r
408 \r
409         p_av->conn.seq_err_retry_cnt = retry_cnt;\r
410         p_av->conn.rnr_retry_cnt = rnr_retry_cnt;\r
411 \r
412 }\r
413 \r
414 \r
415 \r
416 /*\r
417  * Return the local ACK timeout value based on the given packet lifetime\r
418  * and target ACK delay.  Both input values are assumed to be in the form\r
419  * 4.096 x 2 ^ input.\r
420  */\r
421 uint8_t\r
422 cm_local_ack_timeout(\r
423         IN              const   uint8_t                                         pkt_lifetime,\r
424         IN              const   uint8_t                                         target_ack_delay )\r
425 {\r
426         uint64_t        timeout;\r
427         uint8_t         local_ack_timeout;\r
428 \r
429         /*\r
430          * Since both input and the output values are in the same form, we\r
431          * can ignore the 4.096 portion by dividing it out.\r
432          */\r
433 \r
434         /* Double the packet lifetime to get the round trip time. */\r
435         timeout = (uint64_t)2 << pkt_lifetime;\r
436 \r
437         /* Add in the target ack delay. */\r
438         if( target_ack_delay )\r
439                 timeout += (uint64_t)1 << target_ack_delay;\r
440 \r
441         /* Calculate the local ACK timeout. */\r
442         local_ack_timeout = 1;\r
443         while( (1 << local_ack_timeout) <= timeout )\r
444         {\r
445                 local_ack_timeout++;\r
446 \r
447                 /* Only 5-bits are valid. */\r
448                 if( local_ack_timeout > MAX_LOCAL_ACK_TIMEOUT )\r
449                         return MAX_LOCAL_ACK_TIMEOUT;\r
450         }\r
451 \r
452         return local_ack_timeout;\r
453 }\r
454 \r
455 \r
456 /*\r
457  * Remove the link between the connection object and QP, but continue\r
458  * holding any references\r
459  */\r
460 void\r
461 cm_unbind_qp(\r
462         IN              const   ib_cm_handle_t                  h_conn )\r
463 {\r
464         /* If the connection isn't bound to a QP, just return. */\r
465         if( h_conn->h_qp )\r
466         {\r
467                 ((al_conn_qp_t*)h_conn->h_qp)->p_conn = NULL;\r
468                 h_conn->h_qp = NULL;\r
469         }\r
470 }\r