static ib_api_status_t __osmv_dispatch_rmpp_rcv(IN osm_bind_handle_t h_bind, IN const ib_mad_t * p_mad, IN osmv_txn_ctx_t * p_txn, IN const osm_mad_addr_t * p_mad_addr) { ib_api_status_t status = IB_SUCCESS; osmv_rmpp_recv_ctx_t *p_recv_ctx = osmv_txn_get_rmpp_recv_ctx(p_txn); osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind; boolean_t is_last1 = FALSE, is_last2 = FALSE; osm_madw_t *p_new_madw = NULL, *p_req_madw = NULL; ib_mad_t *p_mad_buf; uint32_t size = 0; uint64_t key = osmv_txn_get_key(p_txn); uint64_t tid = osmv_txn_get_tid(p_txn); OSM_LOG_ENTER(p_bo->p_vendor->p_log); if (TRUE == osmv_rmpp_is_ack(p_mad)) { osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, "Not supposed to receive ACK's --> dropping the MAD\n"); goto dispatch_rmpp_rcv_done; } if (TRUE == osmv_rmpp_is_abort_stop(p_mad)) { osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, "__osmv_dispatch_rmpp_rcv: ERR 6504: " "The Remote Side stopped sending\n"); status = IB_REMOTE_ERROR; goto dispatch_rmpp_rcv_done; } status = __osmv_dispatch_accept_seg(h_bind, p_txn, p_mad); switch (status) { case IB_SUCCESS: /* Check wheter this is the legal last MAD */ /* Criteria #1: the received MAD is marked last */ is_last1 = osmv_rmpp_is_last(p_mad); /* Criteria #2: the total accumulated length hits the advertised one */ is_last2 = is_last1; size = osmv_rmpp_recv_ctx_get_byte_num_from_first(p_recv_ctx); if (size > 0) { is_last2 = (osmv_rmpp_recv_ctx_get_cur_byte_num(p_recv_ctx) >= size); } if (is_last1 != is_last2) { osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr, IB_RMPP_TYPE_ABORT, IB_RMPP_STATUS_BAD_LEN); status = IB_ERROR; goto dispatch_rmpp_rcv_done; } /* TBD Consider an optimization - sending an ACK * only for the last segment in the window */ __osmv_dispatch_send_ack(h_bind, p_mad, p_txn, p_mad_addr); break; case IB_INSUFFICIENT_RESOURCES: /* An out-of-order segment received. Send the ACK anyway */ __osmv_dispatch_send_ack(h_bind, p_mad, p_txn, p_mad_addr); status = IB_SUCCESS; goto dispatch_rmpp_rcv_done; case IB_INSUFFICIENT_MEMORY: osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr, IB_RMPP_TYPE_STOP, IB_RMPP_STATUS_RESX); goto dispatch_rmpp_rcv_done; default: /* Illegal return code */ CL_ASSERT(FALSE); } if (TRUE != is_last1) { osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, "RMPP MADW assembly continues, TID=0x%" PRIx64 "\n", tid); goto dispatch_rmpp_rcv_done; } /* This is the last packet. */ if (0 == size) { /* The total size was not advertised in the first packet */ size = osmv_rmpp_recv_ctx_get_byte_num_from_last(p_recv_ctx); } /* NOTE: the received mad might not be >= 256 bytes. some MADs might contain several SA records but still be less then a full MAD. We have to use RMPP to send them over since on a regular "simple" MAD there is no way to know how many records were sent */ /* Build the MAD wrapper to be returned to the user. * The actual storage for the MAD is allocated there. */ p_new_madw = osm_mad_pool_get(p_bo->p_osm_pool, h_bind, size, p_mad_addr); if (NULL == p_new_madw) { osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR, "__osmv_dispatch_rmpp_rcv: ERR 6506: " "Out Of Memory - could not allocate %d bytes for the MADW\n", size); status = IB_INSUFFICIENT_MEMORY; goto dispatch_rmpp_rcv_done; } p_req_madw = osmv_txn_get_madw(p_txn); p_mad_buf = osm_madw_get_mad_ptr(p_new_madw); status = osmv_rmpp_recv_ctx_reassemble_arbt_mad(p_recv_ctx, size, (uint8_t *) p_mad_buf); if (IB_SUCCESS != status) { osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR, "__osmv_dispatch_rmpp_rcv: ERR 6507: " "Internal error - could not reassemble the result MAD\n"); goto dispatch_rmpp_rcv_done; /* What can happen here? */ } /* The MAD is assembled, we are about to apply the callback. * Delete the transaction context, unless the transaction is double sided */ if (FALSE == osmv_txn_is_rmpp_init_by_peer(p_txn) || FALSE == osmv_mad_is_multi_resp(p_mad)) { osmv_txn_done(h_bind, key, FALSE); } osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, "RMPP MADW %p assembly complete, TID=0x%" PRIx64 "\n", p_new_madw, tid); p_mad_buf->trans_id = cl_hton64(tid); osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, "Restoring the original TID to 0x%" PRIx64 "\n", cl_ntoh64(p_mad_buf->trans_id)); /* Finally, do the job! */ p_bo->recv_cb(p_new_madw, p_bo->cb_context, p_req_madw); dispatch_rmpp_rcv_done: OSM_LOG_EXIT(p_bo->p_vendor->p_log); return status; }
static uint64_t __osmv_txn_timeout_cb(IN uint64_t key, IN uint32_t num_regs, IN void *cb_context) { osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) cb_context; uint64_t ret = 0; osmv_txn_ctx_t *p_txn; osmv_rmpp_send_ctx_t *p_send_ctx; osm_madw_t *p_madw = NULL; ib_mad_t *p_mad; osm_mad_addr_t *p_mad_addr; boolean_t invoke_err_cb = FALSE; OSM_LOG_ENTER(p_bo->p_vendor->p_log); /* Don't try to acquire a lock on the Bind Object - * it's taken by the mechanism that drives the timeout based events! * (Recall the special constructor that the Event Wheel is applied with) */ if (p_bo->is_closing) { goto txn_done; } ret = osmv_txn_lookup(p_bo, key, &p_txn); if (IB_NOT_FOUND == ret) { /* Prevent a race - the transaction is already destroyed */ goto txn_done; } p_madw = p_txn->p_madw; switch (osmv_txn_get_rmpp_state(p_txn)) { case OSMV_TXN_RMPP_NONE: if (num_regs <= OSMV_MAX_RETRANSMIT) { /* We still did not exceed the limit of retransmissions. * Set the next timeout's value. */ osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, "__osmv_txn_timeout_cb: " "The transaction request (tid=0x%llX) timed out %d times. " "Retrying the send.\n", osmv_txn_get_tid(p_txn), num_regs); /* resend this mad */ ret = osmv_simple_send_madw((osm_bind_handle_t *) p_bo, p_madw, p_txn, TRUE); if (ret != IB_SUCCESS) { osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR, "__osmv_txn_timeout_cb: " "Fail to send retry for transaction request (tid=0x%llX).\n", osmv_txn_get_tid(p_txn)); osmv_txn_done((osm_bind_handle_t) p_bo, key, TRUE /*in timeout callback */ ); /* This is a requester. Always apply the callback */ invoke_err_cb = TRUE; } else { uint64_t next_timeout_ms; next_timeout_ms = p_bo->p_vendor->resp_timeout * (num_regs + 1) * (num_regs + 1); /* when do we need to timeout again */ ret = cl_get_time_stamp() + (uint64_t) (1000 * next_timeout_ms); osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, "__osmv_txn_timeout_cb: " "Retry request timout in : %lu [msec].\n", next_timeout_ms); } } else { osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR, "__osmv_txn_timeout_cb: ERR 6702: " "The transaction request (tid=0x%llX) timed out (after %d retries). " "Invoking the error callback.\n", osmv_txn_get_tid(p_txn), num_regs); osmv_txn_done((osm_bind_handle_t) p_bo, key, TRUE /*in timeout callback */ ); /* This is a requester. Always apply the callback */ invoke_err_cb = TRUE; } break; case OSMV_TXN_RMPP_SENDER: osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, "RMPP sender (tid=0x%llX) did not receive ACK " "on every segment in the current send window.\n", osmv_txn_get_tid(p_txn)); p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn); if (num_regs <= OSMV_MAX_RETRANSMIT) { /* We still did not exceed the limit of retransmissions. * Set the next timeout's value. */ ret = cl_get_time_stamp() + 1000 * p_bo->p_vendor->resp_timeout; } else { p_send_ctx->status = IB_TIMEOUT; p_mad = osm_madw_get_mad_ptr(p_madw); p_mad_addr = osm_madw_get_mad_addr_ptr(p_madw); /* Send an ABORT to the other side */ osmv_rmpp_send_nak((osm_bind_handle_t) p_bo, p_mad, p_mad_addr, IB_RMPP_TYPE_ABORT, IB_RMPP_STATUS_T2L); } /* Wake the RMPP sender thread up */ cl_event_signal(&p_send_ctx->event); break; case OSMV_TXN_RMPP_RECEIVER: osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, "Transaction timeout on an RMPP receiver (tid=0x%llX). " "Dropping the transaction.\n", osmv_txn_get_tid(p_txn)); osmv_txn_done((osm_bind_handle_t) p_bo, key, TRUE /*in timeout callback */ ); if (FALSE == osmv_txn_is_rmpp_init_by_peer(p_txn)) { /* This is a requester, still waiting for the reply. Apply the callback */ invoke_err_cb = TRUE; } break; default: CL_ASSERT(FALSE); } if (TRUE == invoke_err_cb) { CL_ASSERT(NULL != p_madw); /* update the status in the p_madw */ p_madw->status = IB_TIMEOUT; p_bo->send_err_cb(p_bo->cb_context, p_madw); /* no re-registration */ ret = 0; } txn_done: OSM_LOG_EXIT(p_bo->p_vendor->p_log); return ret; }
static void __osmv_dispatch_rmpp_snd(IN osm_bind_handle_t h_bind, IN const ib_mad_t * p_mad, IN osmv_txn_ctx_t * p_txn, IN const osm_mad_addr_t * p_mad_addr) { osmv_rmpp_send_ctx_t *p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn); uint32_t old_wl = p_send_ctx->window_last; uint32_t total_segs = osmv_rmpp_send_ctx_get_num_segs(p_send_ctx); uint32_t seg_num = cl_ntoh32(((ib_rmpp_mad_t *) p_mad)->seg_num); uint32_t new_wl = cl_ntoh32(((ib_rmpp_mad_t *) p_mad)->paylen_newwin); osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind; OSM_LOG_ENTER(p_bo->p_vendor->p_log); if (TRUE == osmv_rmpp_is_abort_stop(p_mad)) { osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR, "__osmv_dispatch_rmpp_snd: ERR 6502: " "The remote side sent an ABORT/STOP indication.\n"); osmv_rmpp_snd_error(p_send_ctx, IB_REMOTE_ERROR); goto dispatch_rmpp_snd_done; } if (FALSE == osmv_rmpp_is_ack(p_mad)) { osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, "Not supposed to receive DATA packets --> dropping the MAD\n"); goto dispatch_rmpp_snd_done; } /* Continue processing the ACK */ if (seg_num > old_wl) { osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR, "__osmv_dispatch_rmpp_snd: ERR 6503: " "ACK received for a non-sent segment %d\n", seg_num); osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr, IB_RMPP_TYPE_ABORT, IB_RMPP_STATUS_S2B); osmv_rmpp_snd_error(p_send_ctx, IB_REMOTE_ERROR); goto dispatch_rmpp_snd_done; } osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, "__osmv_dispatch_rmpp_snd: " "New WL = %u Old WL = %u Total Segs = %u\n", new_wl, old_wl, total_segs); if (new_wl < old_wl) { osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR, "__osmv_dispatch_rmpp_snd: ERR 6508: " "The receiver requests a smaller WL (%d) than before (%d)\n", new_wl, old_wl); osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr, IB_RMPP_TYPE_ABORT, IB_RMPP_STATUS_W2S); osmv_rmpp_snd_error(p_send_ctx, IB_REMOTE_ERROR); goto dispatch_rmpp_snd_done; } /* Update the sender's window, and optionally wake up the sender thread * Note! A single ACK can acknowledge a whole range of segments: [WF..SEG_NUM] */ osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, "ACK for seg_num #%d accepted.\n", seg_num); if (seg_num == old_wl) { osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, "The send window [%d:%d] is totally acknowledged.\n", p_send_ctx->window_first, old_wl); p_send_ctx->window_first = seg_num + 1; p_send_ctx->window_last = (new_wl < total_segs) ? new_wl : total_segs; /* Remove the response timeout event for the window */ osmv_txn_remove_timeout_ev(h_bind, osmv_txn_get_key(p_txn)); /* Wake up the sending thread */ cl_event_signal(&p_send_ctx->event); } dispatch_rmpp_snd_done: OSM_LOG_EXIT(p_bo->p_vendor->p_log); }