1/* 2 * Copyright (c) 2004-2008 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_sender.h> 42#include <vendor/osm_vendor_mlx_transport.h> 43#include <vendor/osm_vendor_mlx_svc.h> 44#include <vendor/osm_pkt_randomizer.h> 45 46static ib_api_status_t 47__osmv_rmpp_send_segment(IN osm_bind_handle_t h_bind, 48 IN osmv_txn_ctx_t * p_txn, IN uint32_t seg_num); 49 50/****d* OSM Vendor/osmv_simple_send_madw 51 * NAME 52 * osmv_simple_send_madw 53 * 54 * DESCRIPTION 55 * Send a single MAD (256 bytes). 56 * 57 * If this MAD requires a response, set the timeout event. 58 * The function call returns when the MAD's send completion is received. 59 * 60 */ 61 62ib_api_status_t 63osmv_simple_send_madw(IN osm_bind_handle_t h_bind, 64 IN osm_madw_t * const p_madw, 65 IN osmv_txn_ctx_t * p_txn, IN boolean_t is_retry) 66{ 67 ib_api_status_t ret; 68 osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind; 69 osm_mad_addr_t *p_mad_addr = osm_madw_get_mad_addr_ptr(p_madw); 70 uint8_t mad_buf[MAD_BLOCK_SIZE]; 71 ib_mad_t *p_mad = (ib_mad_t *) mad_buf; 72 uint64_t key = 0; 73 74 OSM_LOG_ENTER(p_bo->p_vendor->p_log); 75 76 CL_ASSERT(p_madw->mad_size <= MAD_BLOCK_SIZE); 77 78 memset(p_mad, 0, MAD_BLOCK_SIZE); 79 memcpy(p_mad, osm_madw_get_mad_ptr(p_madw), p_madw->mad_size); 80 81 if (NULL != p_txn) { 82 /* Push a fake txn id to the MAD */ 83 key = osmv_txn_get_key(p_txn); 84 p_mad->trans_id = cl_hton64(key); 85 } 86 87 /* 88 Add call for packet drop randomizer. 89 This is a testing feature. If run_randomizer flag is set to TRUE, 90 the randomizer will be called, and randomally will drop 91 a packet. This is used for simulating unstable fabric. 92 */ 93 if (p_bo->p_vendor->run_randomizer == TRUE) { 94 /* Try the randomizer */ 95 if (osm_pkt_randomizer_mad_drop(p_bo->p_vendor->p_log, 96 p_bo->p_vendor-> 97 p_pkt_randomizer, 98 p_mad) == TRUE) { 99 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, 100 "The MAD will not be sent. \n"); 101 ret = IB_SUCCESS; 102 } else { 103 ret = 104 osmv_transport_mad_send(h_bind, p_mad, p_mad_addr); 105 } 106 } else { 107 ret = osmv_transport_mad_send(h_bind, p_mad, p_mad_addr); 108 } 109 110 if ((IB_SUCCESS == ret) && (NULL != p_txn) && (!is_retry)) { 111 /* Set the timeout for receiving the response MAD */ 112 ret = osmv_txn_set_timeout_ev(h_bind, key, 113 p_bo->p_vendor->resp_timeout); 114 } 115 116 OSM_LOG_EXIT(p_bo->p_vendor->p_log); 117 return ret; 118} 119 120/***** OSM Vendor/osmv_rmpp_send_madw 121 * NAME 122 * osmv_rmpp_send_madw 123 * 124 * DESCRIPTION 125 * Send a single message (MAD wrapper of arbitrary length). 126 * Follow the RMPP semantics 127 * (segmentation, send window, timeouts etc). 128 * 129 * The function call returns either when the whole message 130 * has been acknowledged, or upon error. 131 * 132 * ASSUMPTIONS 133 * The RMPP sender context is set up 134 */ 135 136ib_api_status_t 137osmv_rmpp_send_madw(IN osm_bind_handle_t h_bind, 138 IN osm_madw_t * const p_madw, 139 IN osmv_txn_ctx_t * p_txn, IN boolean_t is_rmpp_ds) 140{ 141 ib_api_status_t ret = IB_SUCCESS; 142 uint32_t i, total_segs; 143 144 osmv_rmpp_send_ctx_t *p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn); 145 osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind; 146 147 OSM_LOG_ENTER(p_bo->p_vendor->p_log); 148 149 total_segs = osmv_rmpp_send_ctx_get_num_segs(p_send_ctx); 150 CL_ASSERT(total_segs >= 1); 151 152 /* In the double-sided transfer, wait for ACK 0 */ 153 154 for (;;) { 155 156 if (p_send_ctx->window_first > total_segs) { 157 158 /* Every segment is acknowledged */ 159 break; 160 } 161 162 /* Send the next burst. */ 163 for (i = p_send_ctx->window_first; i <= p_send_ctx->window_last; 164 i++) { 165 166 /* Send a segment and setup a timeout timer */ 167 ret = __osmv_rmpp_send_segment(h_bind, p_txn, i); 168 if (IB_SUCCESS != ret) { 169 goto send_done; 170 } 171 } 172 173 /* Set the Response Timeout for the ACK on the last DATA segment */ 174 ret = osmv_txn_set_timeout_ev(h_bind, osmv_txn_get_key(p_txn), 175 p_bo->p_vendor->resp_timeout); 176 if (IB_SUCCESS != ret) { 177 goto send_done; 178 } 179 180 /* Going to sleep. Let the others access the transaction DB */ 181 osmv_txn_unlock(p_bo); 182 183 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, 184 "RMPP Sender thread (madw=%p) going to sleep ...\n", 185 p_madw); 186 187 /* Await the next event to happen */ 188 cl_event_wait_on(&p_send_ctx->event, 189 EVENT_NO_TIMEOUT, TRUE /* interruptible */ ); 190 191 /* Got a signal from the MAD dispatcher/timeout handler */ 192 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, 193 "RMPP Sender thread (madw=%p) waking up on a signal ...\n", 194 p_madw); 195 196 /* Let's see what changed... Make this atomic - re-acquire the lock. */ 197 osmv_txn_lock(p_bo); 198 199 if (TRUE == p_bo->is_closing) { 200 osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR, 201 "osmv_rmpp_send_madw: ERR 6601: " 202 "The bind handle %p is being closed. " 203 "Stopping the RMPP Send of MADW %p\n", 204 h_bind, p_madw); 205 206 ret = IB_TIMEOUT; 207 return IB_INTERRUPTED; 208 } 209 210 /* STOP? ABORT? TIMEOUT? */ 211 if (IB_SUCCESS != p_send_ctx->status) { 212 osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR, 213 "osmv_rmpp_send_madw: ERR 6602: " 214 "An error (%s) happened during the RMPP send of %p. Bailing out.\n", 215 ib_get_err_str(p_send_ctx->status), p_madw); 216 ret = p_send_ctx->status; 217 goto send_done; 218 } 219 } 220 221 if (TRUE == is_rmpp_ds) { 222 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, 223 "Double-sided RMPP - switching to be the receiver.\n"); 224 225 ret = osmv_txn_init_rmpp_receiver(h_bind, p_txn, FALSE 226 /*Send was initiated by me */ 227 ); 228 229 if (IB_SUCCESS == ret) { 230 /* Send ACK on the 0 segment */ 231 ret = __osmv_rmpp_send_segment(h_bind, p_txn, 0); 232 } 233 } 234 235send_done: 236 OSM_LOG_EXIT(p_bo->p_vendor->p_log); 237 return ret; 238} 239 240/* 241 * NAME osmv_rmpp_send_ack 242 * 243 * DESCRIPTION 244 * 245 */ 246 247ib_api_status_t 248osmv_rmpp_send_ack(IN osm_bind_handle_t h_bind, 249 IN const ib_mad_t * p_req_mad, 250 IN uint32_t seg_num, 251 IN uint32_t nwl, IN const osm_mad_addr_t * p_mad_addr) 252{ 253 uint8_t resp_mad[MAD_BLOCK_SIZE]; 254 ib_rmpp_mad_t *p_resp_mad = (ib_rmpp_mad_t *) resp_mad; 255 256#ifdef OSMV_RANDOM_DROP 257 if (TRUE == osmv_random_drop()) { 258 osm_log(((osmv_bind_obj_t *) h_bind)->p_vendor->p_log, 259 OSM_LOG_DEBUG, 260 "Error injection - dropping the RMPP ACK\n"); 261 return IB_SUCCESS; 262 } 263#endif 264 265 memcpy(p_resp_mad, p_req_mad, MAD_BLOCK_SIZE); 266 267 p_resp_mad->common_hdr.method = osmv_invert_method(p_req_mad->method); 268 p_resp_mad->rmpp_type = IB_RMPP_TYPE_ACK; 269 p_resp_mad->seg_num = cl_hton32(seg_num); 270 p_resp_mad->paylen_newwin = cl_hton32(nwl); 271 p_resp_mad->rmpp_flags = IB_RMPP_FLAG_ACTIVE; 272 273 return osmv_transport_mad_send(h_bind, p_resp_mad, p_mad_addr); 274} 275 276/* 277 * NAME osmv_rmpp_send_nak 278 * 279 * DESCRIPTION Send the RMPP ABORT or STOP packet 280 */ 281 282ib_api_status_t 283osmv_rmpp_send_nak(IN osm_bind_handle_t h_bind, 284 IN const ib_mad_t * p_req_mad, 285 IN const osm_mad_addr_t * p_mad_addr, 286 IN uint8_t nak_type, IN uint8_t status) 287{ 288 uint8_t resp_mad[MAD_BLOCK_SIZE]; 289 ib_rmpp_mad_t *p_resp_mad = (ib_rmpp_mad_t *) resp_mad; 290 291 memcpy(p_resp_mad, p_req_mad, MAD_BLOCK_SIZE); 292 293 p_resp_mad->common_hdr.method = osmv_invert_method(p_req_mad->method); 294 p_resp_mad->rmpp_type = nak_type; 295 p_resp_mad->rmpp_status = status; 296 297 return osmv_transport_mad_send(h_bind, p_resp_mad, p_mad_addr); 298} 299 300/* 301 * NAME __osmv_rmpp_send_segment 302 * 303 * DESCRIPTION Build a MAD for a specific segment and send it 304 */ 305 306static ib_api_status_t 307__osmv_rmpp_send_segment(IN osm_bind_handle_t h_bind, 308 IN osmv_txn_ctx_t * p_txn, IN uint32_t seg_num) 309{ 310 ib_api_status_t ret; 311 osmv_rmpp_send_ctx_t *p_send_ctx; 312 uint8_t mad_buf[MAD_BLOCK_SIZE]; 313 ib_mad_t *p_mad = (ib_mad_t *) mad_buf; 314 osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind; 315 osm_mad_addr_t *p_mad_addr = 316 osm_madw_get_mad_addr_ptr(osmv_txn_get_madw(p_txn)); 317 uint32_t timeout = p_bo->p_vendor->resp_timeout; 318 uint64_t key; 319 320 OSM_LOG_ENTER(p_bo->p_vendor->p_log); 321 322#ifdef OSMV_RANDOM_DROP 323 if (TRUE == osmv_random_drop()) { 324 325 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, 326 "Error injection - simulating the RMPP segment drop\n"); 327 return IB_SUCCESS; 328 } 329#endif 330 331 p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn); 332 key = osmv_txn_get_key(p_txn); 333 334 if (0 != seg_num) { 335 ret = 336 osmv_rmpp_send_ctx_get_seg(p_send_ctx, seg_num, timeout, 337 p_mad); 338 CL_ASSERT(IB_SUCCESS == ret); 339 340 /* Put the segment to the wire ! */ 341 p_mad->trans_id = cl_hton64(key); 342 343 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, 344 "Sending RMPP segment #%d, on-wire TID=0x%" PRIx64 "\n", 345 seg_num, p_mad->trans_id); 346 347 /* 348 Add call for packet drop randomizer. 349 This is a testing feature. If run_randomizer flag is set to TRUE, 350 the randomizer will be called, and randomally will drop 351 a packet. This is used for simulating unstable fabric. 352 */ 353 if (p_bo->p_vendor->run_randomizer == TRUE) { 354 /* Try the randomizer */ 355 if (osm_pkt_randomizer_mad_drop(p_bo->p_vendor->p_log, 356 p_bo->p_vendor-> 357 p_pkt_randomizer, 358 p_mad) == TRUE) { 359 osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG, 360 "The MAD will not be sent. \n"); 361 ret = IB_SUCCESS; 362 } else { 363 ret = 364 osmv_transport_mad_send((osm_bind_handle_t) 365 p_bo, p_mad, 366 p_mad_addr); 367 } 368 } else { 369 ret = 370 osmv_transport_mad_send((osm_bind_handle_t) p_bo, 371 p_mad, p_mad_addr); 372 } 373 } else { 374 /* This is an ACK for double-sided handshake. Give it a special treatment. */ 375 376 /* It doesn't really matter which data to put. Only the header matters. */ 377 ret = osmv_rmpp_send_ctx_get_seg(p_send_ctx, 1, timeout, p_mad); 378 CL_ASSERT(IB_SUCCESS == ret); 379 380 p_mad->trans_id = cl_hton64(key); 381 ret = 382 osmv_rmpp_send_ack((osm_bind_handle_t) p_bo, p_mad, 383 0 /* segnum */ , 384 OSMV_RMPP_RECV_WIN /* NWL */ , 385 p_mad_addr); 386 } 387 388 OSM_LOG_EXIT(p_bo->p_vendor->p_log); 389 return ret; 390} 391