]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/ofed/management/opensm/libvendor/osm_vendor_mlx_dispatcher.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / ofed / management / opensm / libvendor / osm_vendor_mlx_dispatcher.c
1 /*
2  * Copyright (c) 2004-2006 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5  *
6  * This software is available to you under a choice of one of two
7  * licenses.  You may choose to be licensed under the terms of the GNU
8  * General Public License (GPL) Version 2, available from the file
9  * COPYING in the main directory of this source tree, or the
10  * OpenIB.org BSD license below:
11  *
12  *     Redistribution and use in source and binary forms, with or
13  *     without modification, are permitted provided that the following
14  *     conditions are met:
15  *
16  *      - Redistributions of source code must retain the above
17  *        copyright notice, this list of conditions and the following
18  *        disclaimer.
19  *
20  *      - Redistributions in binary form must reproduce the above
21  *        copyright notice, this list of conditions and the following
22  *        disclaimer in the documentation and/or other materials
23  *        provided with the distribution.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32  * SOFTWARE.
33  *
34  */
35
36 #if HAVE_CONFIG_H
37 #  include <config.h>
38 #endif                          /* HAVE_CONFIG_H */
39
40 #include <string.h>
41 #include <vendor/osm_vendor_mlx.h>
42 #include <vendor/osm_vendor_mlx_defs.h>
43 #include <vendor/osm_vendor_mlx_svc.h>
44 #include <vendor/osm_vendor_mlx_transport.h>
45 #include <vendor/osm_vendor_mlx_sender.h>
46 #include <vendor/osm_pkt_randomizer.h>
47
48 typedef enum _osmv_disp_route {
49
50         OSMV_ROUTE_DROP,
51         OSMV_ROUTE_SIMPLE,
52         OSMV_ROUTE_RMPP,
53
54 } osmv_disp_route_t;
55
56 /**
57  *   FORWARD REFERENCES TO PRIVATE FUNCTIONS
58  */
59
60 static osmv_disp_route_t
61 __osmv_dispatch_route(IN osm_bind_handle_t h_bind,
62                       IN const ib_mad_t * p_mad, OUT osmv_txn_ctx_t ** pp_txn);
63
64 static void
65 __osmv_dispatch_simple_mad(IN osm_bind_handle_t h_bind,
66                            IN const ib_mad_t * p_mad,
67                            IN osmv_txn_ctx_t * p_txn,
68                            IN const osm_mad_addr_t * p_mad_addr);
69
70 static void
71 __osmv_dispatch_rmpp_mad(IN osm_bind_handle_t h_bind,
72                          IN const ib_mad_t * p_mad,
73                          IN osmv_txn_ctx_t * p_txn,
74                          IN const osm_mad_addr_t * p_mad_addr);
75
76 static void
77 __osmv_dispatch_rmpp_snd(IN osm_bind_handle_t h_bind,
78                          IN const ib_mad_t * p_mad,
79                          IN osmv_txn_ctx_t * p_txn,
80                          IN const osm_mad_addr_t * p_mad_addr);
81
82 static ib_api_status_t
83 __osmv_dispatch_rmpp_rcv(IN osm_bind_handle_t h_bind,
84                          IN const ib_mad_t * p_mad,
85                          IN osmv_txn_ctx_t * p_txn,
86                          IN const osm_mad_addr_t * p_mad_addr);
87
88 static ib_api_status_t
89 __osmv_dispatch_accept_seg(IN osm_bind_handle_t h_bind,
90                            IN osmv_txn_ctx_t * p_txn,
91                            IN const ib_mad_t * p_mad);
92 static void
93 __osmv_dispatch_send_ack(IN osm_bind_handle_t h_bind,
94                          IN const ib_mad_t * p_req_mad,
95                          IN osmv_txn_ctx_t * p_txn,
96                          IN const osm_mad_addr_t * p_mad_addr);
97
98 /*
99  * NAME
100  *   osmv_dispatch_mad
101  *
102  * DESCRIPTION
103  *   Lower-level MAD dispatcher.
104  *   Implements a switch between the following MAD consumers:
105  *   (1) Non-RMPP consumer (DATA)
106  *   (2) RMPP receiver     (DATA/ABORT/STOP)
107  *   (3) RMPP sender       (ACK/ABORT/STOP)
108  *
109  * PARAMETERS
110  *   h_bind                The bind handle
111  *   p_mad_buf             The 256 byte buffer of individual MAD
112  *   p_mad_addr            The MAD originator's address
113  */
114
115 ib_api_status_t
116 osmv_dispatch_mad(IN osm_bind_handle_t h_bind,
117                   IN const void *p_mad_buf,
118                   IN const osm_mad_addr_t * p_mad_addr)
119 {
120         ib_api_status_t ret = IB_SUCCESS;
121         const ib_mad_t *p_mad = (ib_mad_t *) p_mad_buf;
122         osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
123         osmv_txn_ctx_t *p_txn = NULL;
124         osm_log_t *p_log = p_bo->p_vendor->p_log;
125
126         OSM_LOG_ENTER(p_bo->p_vendor->p_log);
127
128         CL_ASSERT(NULL != h_bind && NULL != p_mad && NULL != p_mad_addr);
129
130         osmv_txn_lock(p_bo);
131
132         if (TRUE == p_bo->is_closing) {
133
134                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
135                         "The bind handle %p is being closed. "
136                         "The MAD will not be dispatched.\n", p_bo);
137
138                 ret = IB_INTERRUPTED;
139                 goto dispatch_mad_done;
140         }
141
142         /*
143            Add call for packet drop randomizer.
144            This is a testing feature. If run_randomizer flag is set to TRUE,
145            the randomizer will be called, and randomally will drop
146            a packet. This is used for simulating unstable fabric.
147          */
148         if (p_bo->p_vendor->run_randomizer == TRUE) {
149                 /* Try the randomizer */
150                 if (osm_pkt_randomizer_mad_drop(p_bo->p_vendor->p_log,
151                                                 p_bo->p_vendor->
152                                                 p_pkt_randomizer,
153                                                 p_mad) == TRUE) {
154                         osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
155                                 "The MAD will not be dispatched.\n");
156                         goto dispatch_mad_done;
157                 }
158         }
159
160         switch (__osmv_dispatch_route(h_bind, p_mad, &p_txn)) {
161
162         case OSMV_ROUTE_DROP:
163                 break;          /* Do nothing */
164
165         case OSMV_ROUTE_SIMPLE:
166                 __osmv_dispatch_simple_mad(h_bind, p_mad, p_txn, p_mad_addr);
167                 break;
168
169         case OSMV_ROUTE_RMPP:
170                 __osmv_dispatch_rmpp_mad(h_bind, p_mad, p_txn, p_mad_addr);
171                 break;
172
173         default:
174                 CL_ASSERT(FALSE);
175         }
176
177 dispatch_mad_done:
178         osmv_txn_unlock(p_bo);
179
180         OSM_LOG_EXIT(p_log);
181         return ret;
182 }
183
184 /*
185  *  NAME            __osmv_dispatch_route()
186  *
187  *  DESCRIPTION     Decide which way to handle the received MAD: simple txn/RMPP/drop
188  */
189
190 static osmv_disp_route_t
191 __osmv_dispatch_route(IN osm_bind_handle_t h_bind,
192                       IN const ib_mad_t * p_mad, OUT osmv_txn_ctx_t ** pp_txn)
193 {
194         ib_api_status_t ret;
195         osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
196         boolean_t is_resp = osmv_mad_is_response(p_mad);
197         boolean_t is_txn;
198         uint64_t key = cl_ntoh64(p_mad->trans_id);
199
200         CL_ASSERT(NULL != pp_txn);
201
202         ret = osmv_txn_lookup(h_bind, key, pp_txn);
203         is_txn = (IB_SUCCESS == ret);
204
205         if (FALSE == is_txn && TRUE == is_resp) {
206                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
207                         "Received a response to a non-started/aged-out transaction (tid=0x%llX). "
208                         "Dropping the MAD.\n", key);
209                 return OSMV_ROUTE_DROP;
210         }
211
212         if (TRUE == osmv_mad_is_rmpp(p_mad)) {
213                 /* An RMPP transaction. The filtering is more delicate there */
214                 return OSMV_ROUTE_RMPP;
215         }
216
217         if (TRUE == is_txn && FALSE == is_resp) {
218                 /* Does this MAD try to start a transaction with duplicate tid? */
219                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
220                         "Duplicate TID 0x%llX received (not a response). "
221                         "Dropping the MAD.\n", key);
222
223                 return OSMV_ROUTE_DROP;
224         }
225
226         return OSMV_ROUTE_SIMPLE;
227 }
228
229 /*
230  *  NAME            __osmv_dispatch_simple_mad()
231  *
232  *  DESCRIPTION     Handle a MAD that is part of non-RMPP transfer
233  */
234
235 static void
236 __osmv_dispatch_simple_mad(IN osm_bind_handle_t h_bind,
237                            IN const ib_mad_t * p_mad,
238                            IN osmv_txn_ctx_t * p_txn,
239                            IN const osm_mad_addr_t * p_mad_addr)
240 {
241         osm_madw_t *p_madw;
242         ib_mad_t *p_mad_buf;
243         osm_madw_t *p_req_madw = NULL;
244         osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
245
246         OSM_LOG_ENTER(p_bo->p_vendor->p_log);
247
248         /* Build the MAD wrapper to be returned to the user.
249          * The actual storage for the MAD is allocated there.
250          */
251         p_madw =
252             osm_mad_pool_get(p_bo->p_osm_pool, h_bind, MAD_BLOCK_SIZE,
253                              p_mad_addr);
254
255         if (NULL == p_madw) {
256                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
257                         "__osmv_dispatch_simple_mad: ERR 6501: "
258                         "Out Of Memory - could not allocate a buffer of size %d\n",
259                         MAD_BLOCK_SIZE);
260
261                 goto dispatch_simple_mad_done;
262         }
263
264         p_mad_buf = osm_madw_get_mad_ptr(p_madw);
265         /* Copy the payload to the MAD buffer */
266         memcpy((void *)p_mad_buf, (void *)p_mad, MAD_BLOCK_SIZE);
267
268         if (NULL != p_txn) {
269                 /* This is a RESPONSE MAD. Pair it with the REQUEST MAD, pass upstream */
270                 p_req_madw = p_txn->p_madw;
271                 CL_ASSERT(NULL != p_req_madw);
272
273                 p_mad_buf->trans_id = cl_hton64(osmv_txn_get_tid(p_txn));
274                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
275                         "Restoring the original TID to 0x%llX\n",
276                         cl_ntoh64(p_mad_buf->trans_id));
277
278                 /* Reply matched, transaction complete */
279                 osmv_txn_done(h_bind, osmv_txn_get_key(p_txn), FALSE);
280         } else {
281                 /* This is a REQUEST  MAD. Don't create a context, pass upstream */
282         }
283
284         /* Do the job ! */
285         p_bo->recv_cb(p_madw, p_bo->cb_context, p_req_madw);
286
287 dispatch_simple_mad_done:
288         OSM_LOG_EXIT(p_bo->p_vendor->p_log);
289 }
290
291 /*
292  *  NAME            __osmv_dispatch_rmpp_mad()
293  *
294  *  DESCRIPTION     Handle a MAD that is part of RMPP transfer
295  */
296
297 static void
298 __osmv_dispatch_rmpp_mad(IN osm_bind_handle_t h_bind,
299                          IN const ib_mad_t * p_mad,
300                          IN osmv_txn_ctx_t * p_txn,
301                          IN const osm_mad_addr_t * p_mad_addr)
302 {
303         ib_api_status_t status = IB_SUCCESS;
304         uint64_t key = cl_ntoh64(p_mad->trans_id);
305         boolean_t is_init_by_peer = FALSE;
306         osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
307         osm_madw_t *p_madw;
308
309         OSM_LOG_ENTER(p_bo->p_vendor->p_log);
310
311         if (NULL == p_txn) {
312                 if (FALSE == osmv_rmpp_is_data(p_mad)
313                     || FALSE == osmv_rmpp_is_first(p_mad)) {
314                         osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
315                                 "The MAD does not match any transaction "
316                                 "and does not start a sender-initiated RMPP transfer.\n");
317                         goto dispatch_rmpp_mad_done;
318                 }
319
320                 /* IB Spec 13.6.2.2. This is a Sender Initiated Transfer.
321                    My peer is the requester and RMPP Sender. I am the RMPP Receiver.
322                  */
323                 status = osmv_txn_init(h_bind, /*tid==key */ key, key, &p_txn);
324                 if (IB_SUCCESS != status) {
325                         goto dispatch_rmpp_mad_done;
326                 }
327
328                 is_init_by_peer = TRUE;
329                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
330                         "A new sender-initiated transfer (TID=0x%llX) started\n",
331                         key);
332         }
333
334         if (OSMV_TXN_RMPP_NONE == osmv_txn_get_rmpp_state(p_txn)) {
335                 /* Case 1: Fall through from above.
336                  * Case 2: When the transaction was initiated by me
337                  *         (a single request MAD), there was an uncertainty
338                  *         whether the reply will be RMPP. Now it's resolved,
339                  *         since the reply is RMPP!
340                  */
341                 status =
342                     osmv_txn_init_rmpp_receiver(h_bind, p_txn, is_init_by_peer);
343                 if (IB_SUCCESS != status) {
344                         goto dispatch_rmpp_mad_done;
345                 }
346         }
347
348         switch (osmv_txn_get_rmpp_state(p_txn)) {
349
350         case OSMV_TXN_RMPP_RECEIVER:
351                 status =
352                     __osmv_dispatch_rmpp_rcv(h_bind, p_mad, p_txn, p_mad_addr);
353                 if (IB_SUCCESS != status) {
354                         if (FALSE == osmv_txn_is_rmpp_init_by_peer(p_txn)) {
355                                 /* This is a requester, still waiting for the reply. Apply the callback */
356                                 /* update the status of the p_madw */
357                                 p_madw = osmv_txn_get_madw(p_txn);
358                                 p_madw->status = status;
359                                 p_bo->send_err_cb(p_bo->cb_context, p_madw);
360                         }
361
362                         /* ABORT/STOP/LOCAL ERROR */
363                         osmv_txn_done(h_bind, osmv_txn_get_key(p_txn), FALSE);
364                 }
365                 break;
366
367         case OSMV_TXN_RMPP_SENDER:
368                 __osmv_dispatch_rmpp_snd(h_bind, p_mad, p_txn, p_mad_addr);
369                 /* If an error happens here, it's the sender thread to cleanup the txn */
370                 break;
371
372         default:
373                 CL_ASSERT(FALSE);
374         }
375
376 dispatch_rmpp_mad_done:
377         OSM_LOG_EXIT(p_bo->p_vendor->p_log);
378 }
379
380 /*
381  *  NAME            __osmv_dispatch_rmpp_snd()
382  *
383  *  DESCRIPTION     MAD handling by an RMPP sender (ACK/ABORT/STOP)
384  */
385
386 static void
387 __osmv_dispatch_rmpp_snd(IN osm_bind_handle_t h_bind,
388                          IN const ib_mad_t * p_mad,
389                          IN osmv_txn_ctx_t * p_txn,
390                          IN const osm_mad_addr_t * p_mad_addr)
391 {
392         osmv_rmpp_send_ctx_t *p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn);
393
394         uint32_t old_wl = p_send_ctx->window_last;
395         uint32_t total_segs = osmv_rmpp_send_ctx_get_num_segs(p_send_ctx);
396         uint32_t seg_num = cl_ntoh32(((ib_rmpp_mad_t *) p_mad)->seg_num);
397         uint32_t new_wl = cl_ntoh32(((ib_rmpp_mad_t *) p_mad)->paylen_newwin);
398         osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
399
400         OSM_LOG_ENTER(p_bo->p_vendor->p_log);
401
402         if (TRUE == osmv_rmpp_is_abort_stop(p_mad)) {
403
404                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
405                         "__osmv_dispatch_rmpp_snd: ERR 6502: "
406                         "The remote side sent an ABORT/STOP indication.\n");
407                 osmv_rmpp_snd_error(p_send_ctx, IB_REMOTE_ERROR);
408                 goto dispatch_rmpp_snd_done;
409         }
410
411         if (FALSE == osmv_rmpp_is_ack(p_mad)) {
412
413                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
414                         "Not supposed to receive DATA packets --> dropping the MAD\n");
415                 goto dispatch_rmpp_snd_done;
416         }
417
418         /* Continue processing the ACK */
419         if (seg_num > old_wl) {
420
421                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
422                         "__osmv_dispatch_rmpp_snd: ERR 6503: "
423                         "ACK received for a non-sent segment %d\n", seg_num);
424
425                 osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr,
426                                    IB_RMPP_TYPE_ABORT, IB_RMPP_STATUS_S2B);
427
428                 osmv_rmpp_snd_error(p_send_ctx, IB_REMOTE_ERROR);
429                 goto dispatch_rmpp_snd_done;
430         }
431
432         osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
433                 "__osmv_dispatch_rmpp_snd: "
434                 "New WL = %u Old WL = %u Total Segs = %u\n",
435                 new_wl, old_wl, total_segs);
436
437         if (new_wl < old_wl) {
438                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
439                         "__osmv_dispatch_rmpp_snd: ERR 6508: "
440                         "The receiver requests a smaller WL (%d) than before (%d)\n",
441                         new_wl, old_wl);
442
443                 osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr,
444                                    IB_RMPP_TYPE_ABORT, IB_RMPP_STATUS_W2S);
445
446                 osmv_rmpp_snd_error(p_send_ctx, IB_REMOTE_ERROR);
447                 goto dispatch_rmpp_snd_done;
448         }
449
450         /* Update the sender's window, and optionally wake up the sender thread
451          * Note! A single ACK can acknowledge a whole range of segments: [WF..SEG_NUM]
452          */
453         osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
454                 "ACK for seg_num #%d accepted.\n", seg_num);
455
456         if (seg_num == old_wl) {
457
458                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
459                         "The send window [%d:%d] is totally acknowledged.\n",
460                         p_send_ctx->window_first, old_wl);
461
462                 p_send_ctx->window_first = seg_num + 1;
463                 p_send_ctx->window_last =
464                     (new_wl < total_segs) ? new_wl : total_segs;
465
466                 /* Remove the response timeout event for the window */
467                 osmv_txn_remove_timeout_ev(h_bind, osmv_txn_get_key(p_txn));
468
469                 /* Wake up the sending thread */
470                 cl_event_signal(&p_send_ctx->event);
471         }
472
473 dispatch_rmpp_snd_done:
474         OSM_LOG_EXIT(p_bo->p_vendor->p_log);
475 }
476
477 /*
478  *  NAME           __osmv_dispatch_rmpp_rcv()
479  *
480  *  DESCRIPTION    MAD handling by an RMPP receiver (DATA/ABORT/STOP)
481  */
482
483 static ib_api_status_t
484 __osmv_dispatch_rmpp_rcv(IN osm_bind_handle_t h_bind,
485                          IN const ib_mad_t * p_mad,
486                          IN osmv_txn_ctx_t * p_txn,
487                          IN const osm_mad_addr_t * p_mad_addr)
488 {
489         ib_api_status_t status = IB_SUCCESS;
490         osmv_rmpp_recv_ctx_t *p_recv_ctx = osmv_txn_get_rmpp_recv_ctx(p_txn);
491         osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
492         boolean_t is_last1 = FALSE, is_last2 = FALSE;
493         osm_madw_t *p_new_madw = NULL, *p_req_madw = NULL;
494         ib_mad_t *p_mad_buf;
495         uint32_t size = 0;
496         uint64_t key = osmv_txn_get_key(p_txn);
497         uint64_t tid = osmv_txn_get_tid(p_txn);
498
499         OSM_LOG_ENTER(p_bo->p_vendor->p_log);
500
501         if (TRUE == osmv_rmpp_is_ack(p_mad)) {
502                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
503                         "Not supposed to receive ACK's --> dropping the MAD\n");
504
505                 goto dispatch_rmpp_rcv_done;
506         }
507
508         if (TRUE == osmv_rmpp_is_abort_stop(p_mad)) {
509                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
510                         "__osmv_dispatch_rmpp_rcv: ERR 6504: "
511                         "The Remote Side stopped sending\n");
512
513                 status = IB_REMOTE_ERROR;
514                 goto dispatch_rmpp_rcv_done;
515         }
516
517         status = __osmv_dispatch_accept_seg(h_bind, p_txn, p_mad);
518         switch (status) {
519
520         case IB_SUCCESS:
521
522                 /* Check wheter this is the legal last MAD */
523                 /* Criteria #1: the received MAD is marked last */
524                 is_last1 = osmv_rmpp_is_last(p_mad);
525
526                 /* Criteria #2: the total accumulated length hits the advertised one */
527                 is_last2 = is_last1;
528
529                 size = osmv_rmpp_recv_ctx_get_byte_num_from_first(p_recv_ctx);
530                 if (size > 0) {
531                         is_last2 =
532                             (osmv_rmpp_recv_ctx_get_cur_byte_num(p_recv_ctx) >=
533                              size);
534                 }
535
536                 if (is_last1 != is_last2) {
537
538                         osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr,
539                                            IB_RMPP_TYPE_ABORT,
540                                            IB_RMPP_STATUS_BAD_LEN);
541
542                         status = IB_ERROR;
543                         goto dispatch_rmpp_rcv_done;
544                 }
545
546                 /* TBD Consider an optimization - sending an ACK
547                  * only for the last segment in the window
548                  */
549                 __osmv_dispatch_send_ack(h_bind, p_mad, p_txn, p_mad_addr);
550                 break;
551
552         case IB_INSUFFICIENT_RESOURCES:
553                 /* An out-of-order segment received. Send the ACK anyway */
554                 __osmv_dispatch_send_ack(h_bind, p_mad, p_txn, p_mad_addr);
555                 status = IB_SUCCESS;
556                 goto dispatch_rmpp_rcv_done;
557
558         case IB_INSUFFICIENT_MEMORY:
559                 osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr,
560                                    IB_RMPP_TYPE_STOP, IB_RMPP_STATUS_RESX);
561                 goto dispatch_rmpp_rcv_done;
562
563         default:
564                 /* Illegal return code */
565                 CL_ASSERT(FALSE);
566         }
567
568         if (TRUE != is_last1) {
569                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
570                         "RMPP MADW assembly continues, TID=0x%llX\n", tid);
571                 goto dispatch_rmpp_rcv_done;
572         }
573
574         /* This is the last packet. */
575         if (0 == size) {
576                 /* The total size was not advertised in the first packet */
577                 size = osmv_rmpp_recv_ctx_get_byte_num_from_last(p_recv_ctx);
578         }
579
580         /*
581            NOTE: the received mad might not be >= 256 bytes.
582            some MADs might contain several SA records but still be
583            less then a full MAD.
584            We have to use RMPP to send them over since on a regular
585            "simple" MAD there is no way to know how many records were sent
586          */
587
588         /* Build the MAD wrapper to be returned to the user.
589          * The actual storage for the MAD is allocated there.
590          */
591         p_new_madw =
592             osm_mad_pool_get(p_bo->p_osm_pool, h_bind, size, p_mad_addr);
593         if (NULL == p_new_madw) {
594                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
595                         "__osmv_dispatch_rmpp_rcv: ERR 6506: "
596                         "Out Of Memory - could not allocate %d bytes for the MADW\n",
597                         size);
598
599                 status = IB_INSUFFICIENT_MEMORY;
600                 goto dispatch_rmpp_rcv_done;
601         }
602
603         p_req_madw = osmv_txn_get_madw(p_txn);
604         p_mad_buf = osm_madw_get_mad_ptr(p_new_madw);
605         status = osmv_rmpp_recv_ctx_reassemble_arbt_mad(p_recv_ctx, size,
606                                                         (uint8_t *) p_mad_buf);
607         if (IB_SUCCESS != status) {
608                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
609                         "__osmv_dispatch_rmpp_rcv: ERR 6507: "
610                         "Internal error - could not reassemble the result MAD\n");
611                 goto dispatch_rmpp_rcv_done;    /* What can happen here? */
612         }
613
614         /* The MAD is assembled, we are about to apply the callback.
615          * Delete the transaction context, unless the transaction is double sided */
616         if (FALSE == osmv_txn_is_rmpp_init_by_peer(p_txn)
617             || FALSE == osmv_mad_is_multi_resp(p_mad)) {
618
619                 osmv_txn_done(h_bind, key, FALSE);
620         }
621
622         osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
623                 "RMPP MADW %p assembly complete, TID=0x%llX\n", p_new_madw,
624                 tid);
625
626         p_mad_buf->trans_id = cl_hton64(tid);
627         osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
628                 "Restoring the original TID to 0x%llX\n",
629                 cl_ntoh64(p_mad_buf->trans_id));
630
631         /* Finally, do the job! */
632         p_bo->recv_cb(p_new_madw, p_bo->cb_context, p_req_madw);
633
634 dispatch_rmpp_rcv_done:
635         OSM_LOG_EXIT(p_bo->p_vendor->p_log);
636         return status;
637 }
638
639 /*
640  *  NAME            __osmv_dispatch_accept_seg()
641  *
642  *  DESCRIPTION     Store a DATA segment at the RMPP receiver side,
643  *                  if one is received in order.
644  */
645
646 static ib_api_status_t
647 __osmv_dispatch_accept_seg(IN osm_bind_handle_t h_bind,
648                            IN osmv_txn_ctx_t * p_txn, IN const ib_mad_t * p_mad)
649 {
650         ib_api_status_t ret = IB_SUCCESS;
651         uint32_t seg_num = cl_ntoh32(((ib_rmpp_mad_t *) p_mad)->seg_num);
652         osmv_rmpp_recv_ctx_t *p_recv_ctx = osmv_txn_get_rmpp_recv_ctx(p_txn);
653         osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
654         uint64_t tid = osmv_txn_get_tid(p_txn);
655
656         OSM_LOG_ENTER(p_bo->p_vendor->p_log);
657
658         if (seg_num != p_recv_ctx->expected_seg) {
659                 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
660                         "TID 0x%llX: can't accept this segment (%d) - "
661                         "this is a Go-Back-N implementation\n", tid, seg_num);
662                 return IB_INSUFFICIENT_RESOURCES;
663         }
664
665         /* Store the packet's copy in the reassembly list.
666          * Promote the expected segment counter.
667          */
668         ret = osmv_rmpp_recv_ctx_store_mad_seg(p_recv_ctx, (uint8_t *) p_mad);
669         if (IB_SUCCESS != ret) {
670                 return ret;
671         }
672
673         osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
674                 "TID 0x%llX: segment %d accepted\n", tid, seg_num);
675         p_recv_ctx->expected_seg = seg_num + 1;
676
677         OSM_LOG_EXIT(p_bo->p_vendor->p_log);
678         return IB_SUCCESS;
679 }
680
681 /*
682  *  NAME              __osmv_dispatch_send_ack()
683  *
684  *  DESCRIPTION
685  *
686  *  ISSUES
687  *    Consider sending the ACK from an async thread
688  *    if problems with the receiving side processing arise.
689  */
690
691 static void
692 __osmv_dispatch_send_ack(IN osm_bind_handle_t h_bind,
693                          IN const ib_mad_t * p_req_mad,
694                          IN osmv_txn_ctx_t * p_txn,
695                          IN const osm_mad_addr_t * p_mad_addr)
696 {
697         osmv_rmpp_recv_ctx_t *p_recv_ctx = osmv_txn_get_rmpp_recv_ctx(p_txn);
698
699         /* ACK the segment # that was accepted */
700         uint32_t seg_num = cl_ntoh32(((ib_rmpp_mad_t *) p_req_mad)->seg_num);
701
702         /* NOTE! The receiver can publish the New Window Last (NWL) value
703          * that is greater than the total number of segments to be sent.
704          * It's the sender's responsibility to compute the correct number
705          * of segments to send in the next burst.
706          */
707         uint32_t nwl = p_recv_ctx->expected_seg + OSMV_RMPP_RECV_WIN - 1;
708
709         osmv_rmpp_send_ack(h_bind, p_req_mad, seg_num, nwl, p_mad_addr);
710 }