ib_mad_rmpp.c revision 331772
1331772Shselasky/*- 2331772Shselasky * SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0 3331772Shselasky * 4320592Shselasky * Copyright (c) 2005 Intel Inc. All rights reserved. 5320592Shselasky * Copyright (c) 2005-2006 Voltaire, Inc. All rights reserved. 6320592Shselasky * Copyright (c) 2014 Intel Corporation. All rights reserved. 7320592Shselasky * 8320592Shselasky * This software is available to you under a choice of one of two 9320592Shselasky * licenses. You may choose to be licensed under the terms of the GNU 10320592Shselasky * General Public License (GPL) Version 2, available from the file 11320592Shselasky * COPYING in the main directory of this source tree, or the 12320592Shselasky * OpenIB.org BSD license below: 13320592Shselasky * 14320592Shselasky * Redistribution and use in source and binary forms, with or 15320592Shselasky * without modification, are permitted provided that the following 16320592Shselasky * conditions are met: 17320592Shselasky * 18320592Shselasky * - Redistributions of source code must retain the above 19320592Shselasky * copyright notice, this list of conditions and the following 20320592Shselasky * disclaimer. 21320592Shselasky * 22320592Shselasky * - Redistributions in binary form must reproduce the above 23320592Shselasky * copyright notice, this list of conditions and the following 24320592Shselasky * disclaimer in the documentation and/or other materials 25320592Shselasky * provided with the distribution. 26320592Shselasky * 27320592Shselasky * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 28320592Shselasky * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 29320592Shselasky * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 30320592Shselasky * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 31320592Shselasky * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 32320592Shselasky * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 33320592Shselasky * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 34320592Shselasky * SOFTWARE. 35331772Shselasky * 36331772Shselasky * $FreeBSD: stable/11/sys/ofed/drivers/infiniband/core/ib_mad_rmpp.c 331772 2018-03-30 18:17:33Z hselasky $ 37320592Shselasky */ 38320592Shselasky 39320592Shselasky#include <linux/slab.h> 40320592Shselasky 41320592Shselasky#include "mad_priv.h" 42320592Shselasky#include "mad_rmpp.h" 43320592Shselasky 44320592Shselaskyenum rmpp_state { 45320592Shselasky RMPP_STATE_ACTIVE, 46320592Shselasky RMPP_STATE_TIMEOUT, 47320592Shselasky RMPP_STATE_COMPLETE, 48320592Shselasky RMPP_STATE_CANCELING 49320592Shselasky}; 50320592Shselasky 51320592Shselaskystruct mad_rmpp_recv { 52320592Shselasky struct ib_mad_agent_private *agent; 53320592Shselasky struct list_head list; 54320592Shselasky struct delayed_work timeout_work; 55320592Shselasky struct delayed_work cleanup_work; 56320592Shselasky struct completion comp; 57320592Shselasky enum rmpp_state state; 58320592Shselasky spinlock_t lock; 59320592Shselasky atomic_t refcount; 60320592Shselasky 61320592Shselasky struct ib_ah *ah; 62320592Shselasky struct ib_mad_recv_wc *rmpp_wc; 63320592Shselasky struct ib_mad_recv_buf *cur_seg_buf; 64320592Shselasky int last_ack; 65320592Shselasky int seg_num; 66320592Shselasky int newwin; 67320592Shselasky int repwin; 68320592Shselasky 69320592Shselasky __be64 tid; 70320592Shselasky u32 src_qp; 71320592Shselasky u16 slid; 72320592Shselasky u8 mgmt_class; 73320592Shselasky u8 class_version; 74320592Shselasky u8 method; 75320592Shselasky u8 base_version; 76320592Shselasky}; 77320592Shselasky 78320592Shselaskystatic inline void deref_rmpp_recv(struct mad_rmpp_recv *rmpp_recv) 79320592Shselasky{ 80320592Shselasky if (atomic_dec_and_test(&rmpp_recv->refcount)) 81320592Shselasky complete(&rmpp_recv->comp); 82320592Shselasky} 83320592Shselasky 84320592Shselaskystatic void destroy_rmpp_recv(struct mad_rmpp_recv *rmpp_recv) 85320592Shselasky{ 86320592Shselasky deref_rmpp_recv(rmpp_recv); 87320592Shselasky wait_for_completion(&rmpp_recv->comp); 88320592Shselasky ib_destroy_ah(rmpp_recv->ah); 89320592Shselasky kfree(rmpp_recv); 90320592Shselasky} 91320592Shselasky 92320592Shselaskyvoid ib_cancel_rmpp_recvs(struct ib_mad_agent_private *agent) 93320592Shselasky{ 94320592Shselasky struct mad_rmpp_recv *rmpp_recv, *temp_rmpp_recv; 95320592Shselasky unsigned long flags; 96320592Shselasky 97320592Shselasky spin_lock_irqsave(&agent->lock, flags); 98320592Shselasky list_for_each_entry(rmpp_recv, &agent->rmpp_list, list) { 99320592Shselasky if (rmpp_recv->state != RMPP_STATE_COMPLETE) 100320592Shselasky ib_free_recv_mad(rmpp_recv->rmpp_wc); 101320592Shselasky rmpp_recv->state = RMPP_STATE_CANCELING; 102320592Shselasky } 103320592Shselasky spin_unlock_irqrestore(&agent->lock, flags); 104320592Shselasky 105320592Shselasky list_for_each_entry(rmpp_recv, &agent->rmpp_list, list) { 106320592Shselasky cancel_delayed_work(&rmpp_recv->timeout_work); 107320592Shselasky cancel_delayed_work(&rmpp_recv->cleanup_work); 108320592Shselasky } 109320592Shselasky 110320592Shselasky flush_workqueue(agent->qp_info->port_priv->wq); 111320592Shselasky 112320592Shselasky list_for_each_entry_safe(rmpp_recv, temp_rmpp_recv, 113320592Shselasky &agent->rmpp_list, list) { 114320592Shselasky list_del(&rmpp_recv->list); 115320592Shselasky destroy_rmpp_recv(rmpp_recv); 116320592Shselasky } 117320592Shselasky} 118320592Shselasky 119320592Shselaskystatic void format_ack(struct ib_mad_send_buf *msg, 120320592Shselasky struct ib_rmpp_mad *data, 121320592Shselasky struct mad_rmpp_recv *rmpp_recv) 122320592Shselasky{ 123320592Shselasky struct ib_rmpp_mad *ack = msg->mad; 124320592Shselasky unsigned long flags; 125320592Shselasky 126320592Shselasky memcpy(ack, &data->mad_hdr, msg->hdr_len); 127320592Shselasky 128320592Shselasky ack->mad_hdr.method ^= IB_MGMT_METHOD_RESP; 129320592Shselasky ack->rmpp_hdr.rmpp_type = IB_MGMT_RMPP_TYPE_ACK; 130320592Shselasky ib_set_rmpp_flags(&ack->rmpp_hdr, IB_MGMT_RMPP_FLAG_ACTIVE); 131320592Shselasky 132320592Shselasky spin_lock_irqsave(&rmpp_recv->lock, flags); 133320592Shselasky rmpp_recv->last_ack = rmpp_recv->seg_num; 134320592Shselasky ack->rmpp_hdr.seg_num = cpu_to_be32(rmpp_recv->seg_num); 135320592Shselasky ack->rmpp_hdr.paylen_newwin = cpu_to_be32(rmpp_recv->newwin); 136320592Shselasky spin_unlock_irqrestore(&rmpp_recv->lock, flags); 137320592Shselasky} 138320592Shselasky 139320592Shselaskystatic void ack_recv(struct mad_rmpp_recv *rmpp_recv, 140320592Shselasky struct ib_mad_recv_wc *recv_wc) 141320592Shselasky{ 142320592Shselasky struct ib_mad_send_buf *msg; 143320592Shselasky int ret, hdr_len; 144320592Shselasky 145320592Shselasky hdr_len = ib_get_mad_data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class); 146320592Shselasky msg = ib_create_send_mad(&rmpp_recv->agent->agent, recv_wc->wc->src_qp, 147320592Shselasky recv_wc->wc->pkey_index, 1, hdr_len, 148320592Shselasky 0, GFP_KERNEL, 149320592Shselasky IB_MGMT_BASE_VERSION); 150320592Shselasky if (IS_ERR(msg)) 151320592Shselasky return; 152320592Shselasky 153320592Shselasky format_ack(msg, (struct ib_rmpp_mad *) recv_wc->recv_buf.mad, rmpp_recv); 154320592Shselasky msg->ah = rmpp_recv->ah; 155320592Shselasky ret = ib_post_send_mad(msg, NULL); 156320592Shselasky if (ret) 157320592Shselasky ib_free_send_mad(msg); 158320592Shselasky} 159320592Shselasky 160320592Shselaskystatic struct ib_mad_send_buf *alloc_response_msg(struct ib_mad_agent *agent, 161320592Shselasky struct ib_mad_recv_wc *recv_wc) 162320592Shselasky{ 163320592Shselasky struct ib_mad_send_buf *msg; 164320592Shselasky struct ib_ah *ah; 165320592Shselasky int hdr_len; 166320592Shselasky 167320592Shselasky ah = ib_create_ah_from_wc(agent->qp->pd, recv_wc->wc, 168320592Shselasky recv_wc->recv_buf.grh, agent->port_num); 169320592Shselasky if (IS_ERR(ah)) 170320592Shselasky return (void *) ah; 171320592Shselasky 172320592Shselasky hdr_len = ib_get_mad_data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class); 173320592Shselasky msg = ib_create_send_mad(agent, recv_wc->wc->src_qp, 174320592Shselasky recv_wc->wc->pkey_index, 1, 175320592Shselasky hdr_len, 0, GFP_KERNEL, 176320592Shselasky IB_MGMT_BASE_VERSION); 177320592Shselasky if (IS_ERR(msg)) 178320592Shselasky ib_destroy_ah(ah); 179320592Shselasky else { 180320592Shselasky msg->ah = ah; 181320592Shselasky msg->context[0] = ah; 182320592Shselasky } 183320592Shselasky 184320592Shselasky return msg; 185320592Shselasky} 186320592Shselasky 187320592Shselaskystatic void ack_ds_ack(struct ib_mad_agent_private *agent, 188320592Shselasky struct ib_mad_recv_wc *recv_wc) 189320592Shselasky{ 190320592Shselasky struct ib_mad_send_buf *msg; 191320592Shselasky struct ib_rmpp_mad *rmpp_mad; 192320592Shselasky int ret; 193320592Shselasky 194320592Shselasky msg = alloc_response_msg(&agent->agent, recv_wc); 195320592Shselasky if (IS_ERR(msg)) 196320592Shselasky return; 197320592Shselasky 198320592Shselasky rmpp_mad = msg->mad; 199320592Shselasky memcpy(rmpp_mad, recv_wc->recv_buf.mad, msg->hdr_len); 200320592Shselasky 201320592Shselasky rmpp_mad->mad_hdr.method ^= IB_MGMT_METHOD_RESP; 202320592Shselasky ib_set_rmpp_flags(&rmpp_mad->rmpp_hdr, IB_MGMT_RMPP_FLAG_ACTIVE); 203320592Shselasky rmpp_mad->rmpp_hdr.seg_num = 0; 204320592Shselasky rmpp_mad->rmpp_hdr.paylen_newwin = cpu_to_be32(1); 205320592Shselasky 206320592Shselasky ret = ib_post_send_mad(msg, NULL); 207320592Shselasky if (ret) { 208320592Shselasky ib_destroy_ah(msg->ah); 209320592Shselasky ib_free_send_mad(msg); 210320592Shselasky } 211320592Shselasky} 212320592Shselasky 213320592Shselaskyvoid ib_rmpp_send_handler(struct ib_mad_send_wc *mad_send_wc) 214320592Shselasky{ 215320592Shselasky if (mad_send_wc->send_buf->context[0] == mad_send_wc->send_buf->ah) 216320592Shselasky ib_destroy_ah(mad_send_wc->send_buf->ah); 217320592Shselasky ib_free_send_mad(mad_send_wc->send_buf); 218320592Shselasky} 219320592Shselasky 220320592Shselaskystatic void nack_recv(struct ib_mad_agent_private *agent, 221320592Shselasky struct ib_mad_recv_wc *recv_wc, u8 rmpp_status) 222320592Shselasky{ 223320592Shselasky struct ib_mad_send_buf *msg; 224320592Shselasky struct ib_rmpp_mad *rmpp_mad; 225320592Shselasky int ret; 226320592Shselasky 227320592Shselasky msg = alloc_response_msg(&agent->agent, recv_wc); 228320592Shselasky if (IS_ERR(msg)) 229320592Shselasky return; 230320592Shselasky 231320592Shselasky rmpp_mad = msg->mad; 232320592Shselasky memcpy(rmpp_mad, recv_wc->recv_buf.mad, msg->hdr_len); 233320592Shselasky 234320592Shselasky rmpp_mad->mad_hdr.method ^= IB_MGMT_METHOD_RESP; 235320592Shselasky rmpp_mad->rmpp_hdr.rmpp_version = IB_MGMT_RMPP_VERSION; 236320592Shselasky rmpp_mad->rmpp_hdr.rmpp_type = IB_MGMT_RMPP_TYPE_ABORT; 237320592Shselasky ib_set_rmpp_flags(&rmpp_mad->rmpp_hdr, IB_MGMT_RMPP_FLAG_ACTIVE); 238320592Shselasky rmpp_mad->rmpp_hdr.rmpp_status = rmpp_status; 239320592Shselasky rmpp_mad->rmpp_hdr.seg_num = 0; 240320592Shselasky rmpp_mad->rmpp_hdr.paylen_newwin = 0; 241320592Shselasky 242320592Shselasky ret = ib_post_send_mad(msg, NULL); 243320592Shselasky if (ret) { 244320592Shselasky ib_destroy_ah(msg->ah); 245320592Shselasky ib_free_send_mad(msg); 246320592Shselasky } 247320592Shselasky} 248320592Shselasky 249320592Shselaskystatic void recv_timeout_handler(struct work_struct *work) 250320592Shselasky{ 251320592Shselasky struct mad_rmpp_recv *rmpp_recv = 252320592Shselasky container_of(work, struct mad_rmpp_recv, timeout_work.work); 253320592Shselasky struct ib_mad_recv_wc *rmpp_wc; 254320592Shselasky unsigned long flags; 255320592Shselasky 256320592Shselasky spin_lock_irqsave(&rmpp_recv->agent->lock, flags); 257320592Shselasky if (rmpp_recv->state != RMPP_STATE_ACTIVE) { 258320592Shselasky spin_unlock_irqrestore(&rmpp_recv->agent->lock, flags); 259320592Shselasky return; 260320592Shselasky } 261320592Shselasky rmpp_recv->state = RMPP_STATE_TIMEOUT; 262320592Shselasky list_del(&rmpp_recv->list); 263320592Shselasky spin_unlock_irqrestore(&rmpp_recv->agent->lock, flags); 264320592Shselasky 265320592Shselasky rmpp_wc = rmpp_recv->rmpp_wc; 266320592Shselasky nack_recv(rmpp_recv->agent, rmpp_wc, IB_MGMT_RMPP_STATUS_T2L); 267320592Shselasky destroy_rmpp_recv(rmpp_recv); 268320592Shselasky ib_free_recv_mad(rmpp_wc); 269320592Shselasky} 270320592Shselasky 271320592Shselaskystatic void recv_cleanup_handler(struct work_struct *work) 272320592Shselasky{ 273320592Shselasky struct mad_rmpp_recv *rmpp_recv = 274320592Shselasky container_of(work, struct mad_rmpp_recv, cleanup_work.work); 275320592Shselasky unsigned long flags; 276320592Shselasky 277320592Shselasky spin_lock_irqsave(&rmpp_recv->agent->lock, flags); 278320592Shselasky if (rmpp_recv->state == RMPP_STATE_CANCELING) { 279320592Shselasky spin_unlock_irqrestore(&rmpp_recv->agent->lock, flags); 280320592Shselasky return; 281320592Shselasky } 282320592Shselasky list_del(&rmpp_recv->list); 283320592Shselasky spin_unlock_irqrestore(&rmpp_recv->agent->lock, flags); 284320592Shselasky destroy_rmpp_recv(rmpp_recv); 285320592Shselasky} 286320592Shselasky 287320592Shselaskystatic struct mad_rmpp_recv * 288320592Shselaskycreate_rmpp_recv(struct ib_mad_agent_private *agent, 289320592Shselasky struct ib_mad_recv_wc *mad_recv_wc) 290320592Shselasky{ 291320592Shselasky struct mad_rmpp_recv *rmpp_recv; 292320592Shselasky struct ib_mad_hdr *mad_hdr; 293320592Shselasky 294320592Shselasky rmpp_recv = kmalloc(sizeof *rmpp_recv, GFP_KERNEL); 295320592Shselasky if (!rmpp_recv) 296320592Shselasky return NULL; 297320592Shselasky 298320592Shselasky rmpp_recv->ah = ib_create_ah_from_wc(agent->agent.qp->pd, 299320592Shselasky mad_recv_wc->wc, 300320592Shselasky mad_recv_wc->recv_buf.grh, 301320592Shselasky agent->agent.port_num); 302320592Shselasky if (IS_ERR(rmpp_recv->ah)) 303320592Shselasky goto error; 304320592Shselasky 305320592Shselasky rmpp_recv->agent = agent; 306320592Shselasky init_completion(&rmpp_recv->comp); 307320592Shselasky INIT_DELAYED_WORK(&rmpp_recv->timeout_work, recv_timeout_handler); 308320592Shselasky INIT_DELAYED_WORK(&rmpp_recv->cleanup_work, recv_cleanup_handler); 309320592Shselasky spin_lock_init(&rmpp_recv->lock); 310320592Shselasky rmpp_recv->state = RMPP_STATE_ACTIVE; 311320592Shselasky atomic_set(&rmpp_recv->refcount, 1); 312320592Shselasky 313320592Shselasky rmpp_recv->rmpp_wc = mad_recv_wc; 314320592Shselasky rmpp_recv->cur_seg_buf = &mad_recv_wc->recv_buf; 315320592Shselasky rmpp_recv->newwin = 1; 316320592Shselasky rmpp_recv->seg_num = 1; 317320592Shselasky rmpp_recv->last_ack = 0; 318320592Shselasky rmpp_recv->repwin = 1; 319320592Shselasky 320320592Shselasky mad_hdr = &mad_recv_wc->recv_buf.mad->mad_hdr; 321320592Shselasky rmpp_recv->tid = mad_hdr->tid; 322320592Shselasky rmpp_recv->src_qp = mad_recv_wc->wc->src_qp; 323320592Shselasky rmpp_recv->slid = mad_recv_wc->wc->slid; 324320592Shselasky rmpp_recv->mgmt_class = mad_hdr->mgmt_class; 325320592Shselasky rmpp_recv->class_version = mad_hdr->class_version; 326320592Shselasky rmpp_recv->method = mad_hdr->method; 327320592Shselasky rmpp_recv->base_version = mad_hdr->base_version; 328320592Shselasky return rmpp_recv; 329320592Shselasky 330320592Shselaskyerror: kfree(rmpp_recv); 331320592Shselasky return NULL; 332320592Shselasky} 333320592Shselasky 334320592Shselaskystatic struct mad_rmpp_recv * 335320592Shselaskyfind_rmpp_recv(struct ib_mad_agent_private *agent, 336320592Shselasky struct ib_mad_recv_wc *mad_recv_wc) 337320592Shselasky{ 338320592Shselasky struct mad_rmpp_recv *rmpp_recv; 339320592Shselasky struct ib_mad_hdr *mad_hdr = &mad_recv_wc->recv_buf.mad->mad_hdr; 340320592Shselasky 341320592Shselasky list_for_each_entry(rmpp_recv, &agent->rmpp_list, list) { 342320592Shselasky if (rmpp_recv->tid == mad_hdr->tid && 343320592Shselasky rmpp_recv->src_qp == mad_recv_wc->wc->src_qp && 344320592Shselasky rmpp_recv->slid == mad_recv_wc->wc->slid && 345320592Shselasky rmpp_recv->mgmt_class == mad_hdr->mgmt_class && 346320592Shselasky rmpp_recv->class_version == mad_hdr->class_version && 347320592Shselasky rmpp_recv->method == mad_hdr->method) 348320592Shselasky return rmpp_recv; 349320592Shselasky } 350320592Shselasky return NULL; 351320592Shselasky} 352320592Shselasky 353320592Shselaskystatic struct mad_rmpp_recv * 354320592Shselaskyacquire_rmpp_recv(struct ib_mad_agent_private *agent, 355320592Shselasky struct ib_mad_recv_wc *mad_recv_wc) 356320592Shselasky{ 357320592Shselasky struct mad_rmpp_recv *rmpp_recv; 358320592Shselasky unsigned long flags; 359320592Shselasky 360320592Shselasky spin_lock_irqsave(&agent->lock, flags); 361320592Shselasky rmpp_recv = find_rmpp_recv(agent, mad_recv_wc); 362320592Shselasky if (rmpp_recv) 363320592Shselasky atomic_inc(&rmpp_recv->refcount); 364320592Shselasky spin_unlock_irqrestore(&agent->lock, flags); 365320592Shselasky return rmpp_recv; 366320592Shselasky} 367320592Shselasky 368320592Shselaskystatic struct mad_rmpp_recv * 369320592Shselaskyinsert_rmpp_recv(struct ib_mad_agent_private *agent, 370320592Shselasky struct mad_rmpp_recv *rmpp_recv) 371320592Shselasky{ 372320592Shselasky struct mad_rmpp_recv *cur_rmpp_recv; 373320592Shselasky 374320592Shselasky cur_rmpp_recv = find_rmpp_recv(agent, rmpp_recv->rmpp_wc); 375320592Shselasky if (!cur_rmpp_recv) 376320592Shselasky list_add_tail(&rmpp_recv->list, &agent->rmpp_list); 377320592Shselasky 378320592Shselasky return cur_rmpp_recv; 379320592Shselasky} 380320592Shselasky 381320592Shselaskystatic inline int get_last_flag(struct ib_mad_recv_buf *seg) 382320592Shselasky{ 383320592Shselasky struct ib_rmpp_mad *rmpp_mad; 384320592Shselasky 385320592Shselasky rmpp_mad = (struct ib_rmpp_mad *) seg->mad; 386320592Shselasky return ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) & IB_MGMT_RMPP_FLAG_LAST; 387320592Shselasky} 388320592Shselasky 389320592Shselaskystatic inline int get_seg_num(struct ib_mad_recv_buf *seg) 390320592Shselasky{ 391320592Shselasky struct ib_rmpp_mad *rmpp_mad; 392320592Shselasky 393320592Shselasky rmpp_mad = (struct ib_rmpp_mad *) seg->mad; 394320592Shselasky return be32_to_cpu(rmpp_mad->rmpp_hdr.seg_num); 395320592Shselasky} 396320592Shselasky 397320592Shselaskystatic inline struct ib_mad_recv_buf * get_next_seg(struct list_head *rmpp_list, 398320592Shselasky struct ib_mad_recv_buf *seg) 399320592Shselasky{ 400320592Shselasky if (seg->list.next == rmpp_list) 401320592Shselasky return NULL; 402320592Shselasky 403320592Shselasky return container_of(seg->list.next, struct ib_mad_recv_buf, list); 404320592Shselasky} 405320592Shselasky 406320592Shselaskystatic inline int window_size(struct ib_mad_agent_private *agent) 407320592Shselasky{ 408320592Shselasky return max(agent->qp_info->recv_queue.max_active >> 3, 1); 409320592Shselasky} 410320592Shselasky 411320592Shselaskystatic struct ib_mad_recv_buf * find_seg_location(struct list_head *rmpp_list, 412320592Shselasky int seg_num) 413320592Shselasky{ 414320592Shselasky struct ib_mad_recv_buf *seg_buf; 415320592Shselasky int cur_seg_num; 416320592Shselasky 417320592Shselasky list_for_each_entry_reverse(seg_buf, rmpp_list, list) { 418320592Shselasky cur_seg_num = get_seg_num(seg_buf); 419320592Shselasky if (seg_num > cur_seg_num) 420320592Shselasky return seg_buf; 421320592Shselasky if (seg_num == cur_seg_num) 422320592Shselasky break; 423320592Shselasky } 424320592Shselasky return NULL; 425320592Shselasky} 426320592Shselasky 427320592Shselaskystatic void update_seg_num(struct mad_rmpp_recv *rmpp_recv, 428320592Shselasky struct ib_mad_recv_buf *new_buf) 429320592Shselasky{ 430320592Shselasky struct list_head *rmpp_list = &rmpp_recv->rmpp_wc->rmpp_list; 431320592Shselasky 432320592Shselasky while (new_buf && (get_seg_num(new_buf) == rmpp_recv->seg_num + 1)) { 433320592Shselasky rmpp_recv->cur_seg_buf = new_buf; 434320592Shselasky rmpp_recv->seg_num++; 435320592Shselasky new_buf = get_next_seg(rmpp_list, new_buf); 436320592Shselasky } 437320592Shselasky} 438320592Shselasky 439320592Shselaskystatic inline int get_mad_len(struct mad_rmpp_recv *rmpp_recv) 440320592Shselasky{ 441320592Shselasky struct ib_rmpp_mad *rmpp_mad; 442320592Shselasky int hdr_size, data_size, pad; 443320592Shselasky bool opa = rdma_cap_opa_mad(rmpp_recv->agent->qp_info->port_priv->device, 444320592Shselasky rmpp_recv->agent->qp_info->port_priv->port_num); 445320592Shselasky 446320592Shselasky rmpp_mad = (struct ib_rmpp_mad *)rmpp_recv->cur_seg_buf->mad; 447320592Shselasky 448320592Shselasky hdr_size = ib_get_mad_data_offset(rmpp_mad->mad_hdr.mgmt_class); 449320592Shselasky if (opa && rmpp_recv->base_version == OPA_MGMT_BASE_VERSION) { 450320592Shselasky data_size = sizeof(struct opa_rmpp_mad) - hdr_size; 451320592Shselasky pad = OPA_MGMT_RMPP_DATA - be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin); 452320592Shselasky if (pad > OPA_MGMT_RMPP_DATA || pad < 0) 453320592Shselasky pad = 0; 454320592Shselasky } else { 455320592Shselasky data_size = sizeof(struct ib_rmpp_mad) - hdr_size; 456320592Shselasky pad = IB_MGMT_RMPP_DATA - be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin); 457320592Shselasky if (pad > IB_MGMT_RMPP_DATA || pad < 0) 458320592Shselasky pad = 0; 459320592Shselasky } 460320592Shselasky 461320592Shselasky return hdr_size + rmpp_recv->seg_num * data_size - pad; 462320592Shselasky} 463320592Shselasky 464320592Shselaskystatic struct ib_mad_recv_wc * complete_rmpp(struct mad_rmpp_recv *rmpp_recv) 465320592Shselasky{ 466320592Shselasky struct ib_mad_recv_wc *rmpp_wc; 467320592Shselasky 468320592Shselasky ack_recv(rmpp_recv, rmpp_recv->rmpp_wc); 469320592Shselasky if (rmpp_recv->seg_num > 1) 470320592Shselasky cancel_delayed_work(&rmpp_recv->timeout_work); 471320592Shselasky 472320592Shselasky rmpp_wc = rmpp_recv->rmpp_wc; 473320592Shselasky rmpp_wc->mad_len = get_mad_len(rmpp_recv); 474320592Shselasky /* 10 seconds until we can find the packet lifetime */ 475320592Shselasky queue_delayed_work(rmpp_recv->agent->qp_info->port_priv->wq, 476320592Shselasky &rmpp_recv->cleanup_work, msecs_to_jiffies(10000)); 477320592Shselasky return rmpp_wc; 478320592Shselasky} 479320592Shselasky 480320592Shselaskystatic struct ib_mad_recv_wc * 481320592Shselaskycontinue_rmpp(struct ib_mad_agent_private *agent, 482320592Shselasky struct ib_mad_recv_wc *mad_recv_wc) 483320592Shselasky{ 484320592Shselasky struct mad_rmpp_recv *rmpp_recv; 485320592Shselasky struct ib_mad_recv_buf *prev_buf; 486320592Shselasky struct ib_mad_recv_wc *done_wc; 487320592Shselasky int seg_num; 488320592Shselasky unsigned long flags; 489320592Shselasky 490320592Shselasky rmpp_recv = acquire_rmpp_recv(agent, mad_recv_wc); 491320592Shselasky if (!rmpp_recv) 492320592Shselasky goto drop1; 493320592Shselasky 494320592Shselasky seg_num = get_seg_num(&mad_recv_wc->recv_buf); 495320592Shselasky 496320592Shselasky spin_lock_irqsave(&rmpp_recv->lock, flags); 497320592Shselasky if ((rmpp_recv->state == RMPP_STATE_TIMEOUT) || 498320592Shselasky (seg_num > rmpp_recv->newwin)) 499320592Shselasky goto drop3; 500320592Shselasky 501320592Shselasky if ((seg_num <= rmpp_recv->last_ack) || 502320592Shselasky (rmpp_recv->state == RMPP_STATE_COMPLETE)) { 503320592Shselasky spin_unlock_irqrestore(&rmpp_recv->lock, flags); 504320592Shselasky ack_recv(rmpp_recv, mad_recv_wc); 505320592Shselasky goto drop2; 506320592Shselasky } 507320592Shselasky 508320592Shselasky prev_buf = find_seg_location(&rmpp_recv->rmpp_wc->rmpp_list, seg_num); 509320592Shselasky if (!prev_buf) 510320592Shselasky goto drop3; 511320592Shselasky 512320592Shselasky done_wc = NULL; 513320592Shselasky list_add(&mad_recv_wc->recv_buf.list, &prev_buf->list); 514320592Shselasky if (rmpp_recv->cur_seg_buf == prev_buf) { 515320592Shselasky update_seg_num(rmpp_recv, &mad_recv_wc->recv_buf); 516320592Shselasky if (get_last_flag(rmpp_recv->cur_seg_buf)) { 517320592Shselasky rmpp_recv->state = RMPP_STATE_COMPLETE; 518320592Shselasky spin_unlock_irqrestore(&rmpp_recv->lock, flags); 519320592Shselasky done_wc = complete_rmpp(rmpp_recv); 520320592Shselasky goto out; 521320592Shselasky } else if (rmpp_recv->seg_num == rmpp_recv->newwin) { 522320592Shselasky rmpp_recv->newwin += window_size(agent); 523320592Shselasky spin_unlock_irqrestore(&rmpp_recv->lock, flags); 524320592Shselasky ack_recv(rmpp_recv, mad_recv_wc); 525320592Shselasky goto out; 526320592Shselasky } 527320592Shselasky } 528320592Shselasky spin_unlock_irqrestore(&rmpp_recv->lock, flags); 529320592Shselaskyout: 530320592Shselasky deref_rmpp_recv(rmpp_recv); 531320592Shselasky return done_wc; 532320592Shselasky 533320592Shselaskydrop3: spin_unlock_irqrestore(&rmpp_recv->lock, flags); 534320592Shselaskydrop2: deref_rmpp_recv(rmpp_recv); 535320592Shselaskydrop1: ib_free_recv_mad(mad_recv_wc); 536320592Shselasky return NULL; 537320592Shselasky} 538320592Shselasky 539320592Shselaskystatic struct ib_mad_recv_wc * 540320592Shselaskystart_rmpp(struct ib_mad_agent_private *agent, 541320592Shselasky struct ib_mad_recv_wc *mad_recv_wc) 542320592Shselasky{ 543320592Shselasky struct mad_rmpp_recv *rmpp_recv; 544320592Shselasky unsigned long flags; 545320592Shselasky 546320592Shselasky rmpp_recv = create_rmpp_recv(agent, mad_recv_wc); 547320592Shselasky if (!rmpp_recv) { 548320592Shselasky ib_free_recv_mad(mad_recv_wc); 549320592Shselasky return NULL; 550320592Shselasky } 551320592Shselasky 552320592Shselasky spin_lock_irqsave(&agent->lock, flags); 553320592Shselasky if (insert_rmpp_recv(agent, rmpp_recv)) { 554320592Shselasky spin_unlock_irqrestore(&agent->lock, flags); 555320592Shselasky /* duplicate first MAD */ 556320592Shselasky destroy_rmpp_recv(rmpp_recv); 557320592Shselasky return continue_rmpp(agent, mad_recv_wc); 558320592Shselasky } 559320592Shselasky atomic_inc(&rmpp_recv->refcount); 560320592Shselasky 561320592Shselasky if (get_last_flag(&mad_recv_wc->recv_buf)) { 562320592Shselasky rmpp_recv->state = RMPP_STATE_COMPLETE; 563320592Shselasky spin_unlock_irqrestore(&agent->lock, flags); 564320592Shselasky complete_rmpp(rmpp_recv); 565320592Shselasky } else { 566320592Shselasky spin_unlock_irqrestore(&agent->lock, flags); 567320592Shselasky /* 40 seconds until we can find the packet lifetimes */ 568320592Shselasky queue_delayed_work(agent->qp_info->port_priv->wq, 569320592Shselasky &rmpp_recv->timeout_work, 570320592Shselasky msecs_to_jiffies(40000)); 571320592Shselasky rmpp_recv->newwin += window_size(agent); 572320592Shselasky ack_recv(rmpp_recv, mad_recv_wc); 573320592Shselasky mad_recv_wc = NULL; 574320592Shselasky } 575320592Shselasky deref_rmpp_recv(rmpp_recv); 576320592Shselasky return mad_recv_wc; 577320592Shselasky} 578320592Shselasky 579320592Shselaskystatic int send_next_seg(struct ib_mad_send_wr_private *mad_send_wr) 580320592Shselasky{ 581320592Shselasky struct ib_rmpp_mad *rmpp_mad; 582320592Shselasky int timeout; 583320592Shselasky u32 paylen = 0; 584320592Shselasky 585320592Shselasky rmpp_mad = mad_send_wr->send_buf.mad; 586320592Shselasky ib_set_rmpp_flags(&rmpp_mad->rmpp_hdr, IB_MGMT_RMPP_FLAG_ACTIVE); 587320592Shselasky rmpp_mad->rmpp_hdr.seg_num = cpu_to_be32(++mad_send_wr->seg_num); 588320592Shselasky 589320592Shselasky if (mad_send_wr->seg_num == 1) { 590320592Shselasky rmpp_mad->rmpp_hdr.rmpp_rtime_flags |= IB_MGMT_RMPP_FLAG_FIRST; 591320592Shselasky paylen = (mad_send_wr->send_buf.seg_count * 592320592Shselasky mad_send_wr->send_buf.seg_rmpp_size) - 593320592Shselasky mad_send_wr->pad; 594320592Shselasky } 595320592Shselasky 596320592Shselasky if (mad_send_wr->seg_num == mad_send_wr->send_buf.seg_count) { 597320592Shselasky rmpp_mad->rmpp_hdr.rmpp_rtime_flags |= IB_MGMT_RMPP_FLAG_LAST; 598320592Shselasky paylen = mad_send_wr->send_buf.seg_rmpp_size - mad_send_wr->pad; 599320592Shselasky } 600320592Shselasky rmpp_mad->rmpp_hdr.paylen_newwin = cpu_to_be32(paylen); 601320592Shselasky 602320592Shselasky /* 2 seconds for an ACK until we can find the packet lifetime */ 603320592Shselasky timeout = mad_send_wr->send_buf.timeout_ms; 604320592Shselasky if (!timeout || timeout > 2000) 605320592Shselasky mad_send_wr->timeout = msecs_to_jiffies(2000); 606320592Shselasky 607320592Shselasky return ib_send_mad(mad_send_wr); 608320592Shselasky} 609320592Shselasky 610320592Shselaskystatic void abort_send(struct ib_mad_agent_private *agent, 611320592Shselasky struct ib_mad_recv_wc *mad_recv_wc, u8 rmpp_status) 612320592Shselasky{ 613320592Shselasky struct ib_mad_send_wr_private *mad_send_wr; 614320592Shselasky struct ib_mad_send_wc wc; 615320592Shselasky unsigned long flags; 616320592Shselasky 617320592Shselasky spin_lock_irqsave(&agent->lock, flags); 618320592Shselasky mad_send_wr = ib_find_send_mad(agent, mad_recv_wc); 619320592Shselasky if (!mad_send_wr) 620320592Shselasky goto out; /* Unmatched send */ 621320592Shselasky 622320592Shselasky if ((mad_send_wr->last_ack == mad_send_wr->send_buf.seg_count) || 623320592Shselasky (!mad_send_wr->timeout) || (mad_send_wr->status != IB_WC_SUCCESS)) 624320592Shselasky goto out; /* Send is already done */ 625320592Shselasky 626320592Shselasky ib_mark_mad_done(mad_send_wr); 627320592Shselasky spin_unlock_irqrestore(&agent->lock, flags); 628320592Shselasky 629320592Shselasky wc.status = IB_WC_REM_ABORT_ERR; 630320592Shselasky wc.vendor_err = rmpp_status; 631320592Shselasky wc.send_buf = &mad_send_wr->send_buf; 632320592Shselasky ib_mad_complete_send_wr(mad_send_wr, &wc); 633320592Shselasky return; 634320592Shselaskyout: 635320592Shselasky spin_unlock_irqrestore(&agent->lock, flags); 636320592Shselasky} 637320592Shselasky 638320592Shselaskystatic inline void adjust_last_ack(struct ib_mad_send_wr_private *wr, 639320592Shselasky int seg_num) 640320592Shselasky{ 641320592Shselasky struct list_head *list; 642320592Shselasky 643320592Shselasky wr->last_ack = seg_num; 644320592Shselasky list = &wr->last_ack_seg->list; 645320592Shselasky list_for_each_entry(wr->last_ack_seg, list, list) 646320592Shselasky if (wr->last_ack_seg->num == seg_num) 647320592Shselasky break; 648320592Shselasky} 649320592Shselasky 650320592Shselaskystatic void process_ds_ack(struct ib_mad_agent_private *agent, 651320592Shselasky struct ib_mad_recv_wc *mad_recv_wc, int newwin) 652320592Shselasky{ 653320592Shselasky struct mad_rmpp_recv *rmpp_recv; 654320592Shselasky 655320592Shselasky rmpp_recv = find_rmpp_recv(agent, mad_recv_wc); 656320592Shselasky if (rmpp_recv && rmpp_recv->state == RMPP_STATE_COMPLETE) 657320592Shselasky rmpp_recv->repwin = newwin; 658320592Shselasky} 659320592Shselasky 660320592Shselaskystatic void process_rmpp_ack(struct ib_mad_agent_private *agent, 661320592Shselasky struct ib_mad_recv_wc *mad_recv_wc) 662320592Shselasky{ 663320592Shselasky struct ib_mad_send_wr_private *mad_send_wr; 664320592Shselasky struct ib_rmpp_mad *rmpp_mad; 665320592Shselasky unsigned long flags; 666320592Shselasky int seg_num, newwin, ret; 667320592Shselasky 668320592Shselasky rmpp_mad = (struct ib_rmpp_mad *)mad_recv_wc->recv_buf.mad; 669320592Shselasky if (rmpp_mad->rmpp_hdr.rmpp_status) { 670320592Shselasky abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS); 671320592Shselasky nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS); 672320592Shselasky return; 673320592Shselasky } 674320592Shselasky 675320592Shselasky seg_num = be32_to_cpu(rmpp_mad->rmpp_hdr.seg_num); 676320592Shselasky newwin = be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin); 677320592Shselasky if (newwin < seg_num) { 678320592Shselasky abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_W2S); 679320592Shselasky nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_W2S); 680320592Shselasky return; 681320592Shselasky } 682320592Shselasky 683320592Shselasky spin_lock_irqsave(&agent->lock, flags); 684320592Shselasky mad_send_wr = ib_find_send_mad(agent, mad_recv_wc); 685320592Shselasky if (!mad_send_wr) { 686320592Shselasky if (!seg_num) 687320592Shselasky process_ds_ack(agent, mad_recv_wc, newwin); 688320592Shselasky goto out; /* Unmatched or DS RMPP ACK */ 689320592Shselasky } 690320592Shselasky 691320592Shselasky if ((mad_send_wr->last_ack == mad_send_wr->send_buf.seg_count) && 692320592Shselasky (mad_send_wr->timeout)) { 693320592Shselasky spin_unlock_irqrestore(&agent->lock, flags); 694320592Shselasky ack_ds_ack(agent, mad_recv_wc); 695320592Shselasky return; /* Repeated ACK for DS RMPP transaction */ 696320592Shselasky } 697320592Shselasky 698320592Shselasky if ((mad_send_wr->last_ack == mad_send_wr->send_buf.seg_count) || 699320592Shselasky (!mad_send_wr->timeout) || (mad_send_wr->status != IB_WC_SUCCESS)) 700320592Shselasky goto out; /* Send is already done */ 701320592Shselasky 702320592Shselasky if (seg_num > mad_send_wr->send_buf.seg_count || 703320592Shselasky seg_num > mad_send_wr->newwin) { 704320592Shselasky spin_unlock_irqrestore(&agent->lock, flags); 705320592Shselasky abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_S2B); 706320592Shselasky nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_S2B); 707320592Shselasky return; 708320592Shselasky } 709320592Shselasky 710320592Shselasky if (newwin < mad_send_wr->newwin || seg_num < mad_send_wr->last_ack) 711320592Shselasky goto out; /* Old ACK */ 712320592Shselasky 713320592Shselasky if (seg_num > mad_send_wr->last_ack) { 714320592Shselasky adjust_last_ack(mad_send_wr, seg_num); 715320592Shselasky mad_send_wr->retries_left = mad_send_wr->max_retries; 716320592Shselasky } 717320592Shselasky mad_send_wr->newwin = newwin; 718320592Shselasky if (mad_send_wr->last_ack == mad_send_wr->send_buf.seg_count) { 719320592Shselasky /* If no response is expected, the ACK completes the send */ 720320592Shselasky if (!mad_send_wr->send_buf.timeout_ms) { 721320592Shselasky struct ib_mad_send_wc wc; 722320592Shselasky 723320592Shselasky ib_mark_mad_done(mad_send_wr); 724320592Shselasky spin_unlock_irqrestore(&agent->lock, flags); 725320592Shselasky 726320592Shselasky wc.status = IB_WC_SUCCESS; 727320592Shselasky wc.vendor_err = 0; 728320592Shselasky wc.send_buf = &mad_send_wr->send_buf; 729320592Shselasky ib_mad_complete_send_wr(mad_send_wr, &wc); 730320592Shselasky return; 731320592Shselasky } 732320592Shselasky if (mad_send_wr->refcount == 1) 733320592Shselasky ib_reset_mad_timeout(mad_send_wr, 734320592Shselasky mad_send_wr->send_buf.timeout_ms); 735320592Shselasky spin_unlock_irqrestore(&agent->lock, flags); 736320592Shselasky ack_ds_ack(agent, mad_recv_wc); 737320592Shselasky return; 738320592Shselasky } else if (mad_send_wr->refcount == 1 && 739320592Shselasky mad_send_wr->seg_num < mad_send_wr->newwin && 740320592Shselasky mad_send_wr->seg_num < mad_send_wr->send_buf.seg_count) { 741320592Shselasky /* Send failure will just result in a timeout/retry */ 742320592Shselasky ret = send_next_seg(mad_send_wr); 743320592Shselasky if (ret) 744320592Shselasky goto out; 745320592Shselasky 746320592Shselasky mad_send_wr->refcount++; 747320592Shselasky list_move_tail(&mad_send_wr->agent_list, 748320592Shselasky &mad_send_wr->mad_agent_priv->send_list); 749320592Shselasky } 750320592Shselaskyout: 751320592Shselasky spin_unlock_irqrestore(&agent->lock, flags); 752320592Shselasky} 753320592Shselasky 754320592Shselaskystatic struct ib_mad_recv_wc * 755320592Shselaskyprocess_rmpp_data(struct ib_mad_agent_private *agent, 756320592Shselasky struct ib_mad_recv_wc *mad_recv_wc) 757320592Shselasky{ 758320592Shselasky struct ib_rmpp_hdr *rmpp_hdr; 759320592Shselasky u8 rmpp_status; 760320592Shselasky 761320592Shselasky rmpp_hdr = &((struct ib_rmpp_mad *)mad_recv_wc->recv_buf.mad)->rmpp_hdr; 762320592Shselasky 763320592Shselasky if (rmpp_hdr->rmpp_status) { 764320592Shselasky rmpp_status = IB_MGMT_RMPP_STATUS_BAD_STATUS; 765320592Shselasky goto bad; 766320592Shselasky } 767320592Shselasky 768320592Shselasky if (rmpp_hdr->seg_num == cpu_to_be32(1)) { 769320592Shselasky if (!(ib_get_rmpp_flags(rmpp_hdr) & IB_MGMT_RMPP_FLAG_FIRST)) { 770320592Shselasky rmpp_status = IB_MGMT_RMPP_STATUS_BAD_SEG; 771320592Shselasky goto bad; 772320592Shselasky } 773320592Shselasky return start_rmpp(agent, mad_recv_wc); 774320592Shselasky } else { 775320592Shselasky if (ib_get_rmpp_flags(rmpp_hdr) & IB_MGMT_RMPP_FLAG_FIRST) { 776320592Shselasky rmpp_status = IB_MGMT_RMPP_STATUS_BAD_SEG; 777320592Shselasky goto bad; 778320592Shselasky } 779320592Shselasky return continue_rmpp(agent, mad_recv_wc); 780320592Shselasky } 781320592Shselaskybad: 782320592Shselasky nack_recv(agent, mad_recv_wc, rmpp_status); 783320592Shselasky ib_free_recv_mad(mad_recv_wc); 784320592Shselasky return NULL; 785320592Shselasky} 786320592Shselasky 787320592Shselaskystatic void process_rmpp_stop(struct ib_mad_agent_private *agent, 788320592Shselasky struct ib_mad_recv_wc *mad_recv_wc) 789320592Shselasky{ 790320592Shselasky struct ib_rmpp_mad *rmpp_mad; 791320592Shselasky 792320592Shselasky rmpp_mad = (struct ib_rmpp_mad *)mad_recv_wc->recv_buf.mad; 793320592Shselasky 794320592Shselasky if (rmpp_mad->rmpp_hdr.rmpp_status != IB_MGMT_RMPP_STATUS_RESX) { 795320592Shselasky abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS); 796320592Shselasky nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS); 797320592Shselasky } else 798320592Shselasky abort_send(agent, mad_recv_wc, rmpp_mad->rmpp_hdr.rmpp_status); 799320592Shselasky} 800320592Shselasky 801320592Shselaskystatic void process_rmpp_abort(struct ib_mad_agent_private *agent, 802320592Shselasky struct ib_mad_recv_wc *mad_recv_wc) 803320592Shselasky{ 804320592Shselasky struct ib_rmpp_mad *rmpp_mad; 805320592Shselasky 806320592Shselasky rmpp_mad = (struct ib_rmpp_mad *)mad_recv_wc->recv_buf.mad; 807320592Shselasky 808320592Shselasky if (rmpp_mad->rmpp_hdr.rmpp_status < IB_MGMT_RMPP_STATUS_ABORT_MIN || 809320592Shselasky rmpp_mad->rmpp_hdr.rmpp_status > IB_MGMT_RMPP_STATUS_ABORT_MAX) { 810320592Shselasky abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS); 811320592Shselasky nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS); 812320592Shselasky } else 813320592Shselasky abort_send(agent, mad_recv_wc, rmpp_mad->rmpp_hdr.rmpp_status); 814320592Shselasky} 815320592Shselasky 816320592Shselaskystruct ib_mad_recv_wc * 817320592Shselaskyib_process_rmpp_recv_wc(struct ib_mad_agent_private *agent, 818320592Shselasky struct ib_mad_recv_wc *mad_recv_wc) 819320592Shselasky{ 820320592Shselasky struct ib_rmpp_mad *rmpp_mad; 821320592Shselasky 822320592Shselasky rmpp_mad = (struct ib_rmpp_mad *)mad_recv_wc->recv_buf.mad; 823320592Shselasky if (!(rmpp_mad->rmpp_hdr.rmpp_rtime_flags & IB_MGMT_RMPP_FLAG_ACTIVE)) 824320592Shselasky return mad_recv_wc; 825320592Shselasky 826320592Shselasky if (rmpp_mad->rmpp_hdr.rmpp_version != IB_MGMT_RMPP_VERSION) { 827320592Shselasky abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_UNV); 828320592Shselasky nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_UNV); 829320592Shselasky goto out; 830320592Shselasky } 831320592Shselasky 832320592Shselasky switch (rmpp_mad->rmpp_hdr.rmpp_type) { 833320592Shselasky case IB_MGMT_RMPP_TYPE_DATA: 834320592Shselasky return process_rmpp_data(agent, mad_recv_wc); 835320592Shselasky case IB_MGMT_RMPP_TYPE_ACK: 836320592Shselasky process_rmpp_ack(agent, mad_recv_wc); 837320592Shselasky break; 838320592Shselasky case IB_MGMT_RMPP_TYPE_STOP: 839320592Shselasky process_rmpp_stop(agent, mad_recv_wc); 840320592Shselasky break; 841320592Shselasky case IB_MGMT_RMPP_TYPE_ABORT: 842320592Shselasky process_rmpp_abort(agent, mad_recv_wc); 843320592Shselasky break; 844320592Shselasky default: 845320592Shselasky abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BADT); 846320592Shselasky nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BADT); 847320592Shselasky break; 848320592Shselasky } 849320592Shselaskyout: 850320592Shselasky ib_free_recv_mad(mad_recv_wc); 851320592Shselasky return NULL; 852320592Shselasky} 853320592Shselasky 854320592Shselaskystatic int init_newwin(struct ib_mad_send_wr_private *mad_send_wr) 855320592Shselasky{ 856320592Shselasky struct ib_mad_agent_private *agent = mad_send_wr->mad_agent_priv; 857320592Shselasky struct ib_mad_hdr *mad_hdr = mad_send_wr->send_buf.mad; 858320592Shselasky struct mad_rmpp_recv *rmpp_recv; 859320592Shselasky struct ib_ah_attr ah_attr; 860320592Shselasky unsigned long flags; 861320592Shselasky int newwin = 1; 862320592Shselasky 863320592Shselasky if (!(mad_hdr->method & IB_MGMT_METHOD_RESP)) 864320592Shselasky goto out; 865320592Shselasky 866320592Shselasky spin_lock_irqsave(&agent->lock, flags); 867320592Shselasky list_for_each_entry(rmpp_recv, &agent->rmpp_list, list) { 868320592Shselasky if (rmpp_recv->tid != mad_hdr->tid || 869320592Shselasky rmpp_recv->mgmt_class != mad_hdr->mgmt_class || 870320592Shselasky rmpp_recv->class_version != mad_hdr->class_version || 871320592Shselasky (rmpp_recv->method & IB_MGMT_METHOD_RESP)) 872320592Shselasky continue; 873320592Shselasky 874320592Shselasky if (ib_query_ah(mad_send_wr->send_buf.ah, &ah_attr)) 875320592Shselasky continue; 876320592Shselasky 877320592Shselasky if (rmpp_recv->slid == ah_attr.dlid) { 878320592Shselasky newwin = rmpp_recv->repwin; 879320592Shselasky break; 880320592Shselasky } 881320592Shselasky } 882320592Shselasky spin_unlock_irqrestore(&agent->lock, flags); 883320592Shselaskyout: 884320592Shselasky return newwin; 885320592Shselasky} 886320592Shselasky 887320592Shselaskyint ib_send_rmpp_mad(struct ib_mad_send_wr_private *mad_send_wr) 888320592Shselasky{ 889320592Shselasky struct ib_rmpp_mad *rmpp_mad; 890320592Shselasky int ret; 891320592Shselasky 892320592Shselasky rmpp_mad = mad_send_wr->send_buf.mad; 893320592Shselasky if (!(ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) & 894320592Shselasky IB_MGMT_RMPP_FLAG_ACTIVE)) 895320592Shselasky return IB_RMPP_RESULT_UNHANDLED; 896320592Shselasky 897320592Shselasky if (rmpp_mad->rmpp_hdr.rmpp_type != IB_MGMT_RMPP_TYPE_DATA) { 898320592Shselasky mad_send_wr->seg_num = 1; 899320592Shselasky return IB_RMPP_RESULT_INTERNAL; 900320592Shselasky } 901320592Shselasky 902320592Shselasky mad_send_wr->newwin = init_newwin(mad_send_wr); 903320592Shselasky 904320592Shselasky /* We need to wait for the final ACK even if there isn't a response */ 905320592Shselasky mad_send_wr->refcount += (mad_send_wr->timeout == 0); 906320592Shselasky ret = send_next_seg(mad_send_wr); 907320592Shselasky if (!ret) 908320592Shselasky return IB_RMPP_RESULT_CONSUMED; 909320592Shselasky return ret; 910320592Shselasky} 911320592Shselasky 912320592Shselaskyint ib_process_rmpp_send_wc(struct ib_mad_send_wr_private *mad_send_wr, 913320592Shselasky struct ib_mad_send_wc *mad_send_wc) 914320592Shselasky{ 915320592Shselasky struct ib_rmpp_mad *rmpp_mad; 916320592Shselasky int ret; 917320592Shselasky 918320592Shselasky rmpp_mad = mad_send_wr->send_buf.mad; 919320592Shselasky if (!(ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) & 920320592Shselasky IB_MGMT_RMPP_FLAG_ACTIVE)) 921320592Shselasky return IB_RMPP_RESULT_UNHANDLED; /* RMPP not active */ 922320592Shselasky 923320592Shselasky if (rmpp_mad->rmpp_hdr.rmpp_type != IB_MGMT_RMPP_TYPE_DATA) 924320592Shselasky return IB_RMPP_RESULT_INTERNAL; /* ACK, STOP, or ABORT */ 925320592Shselasky 926320592Shselasky if (mad_send_wc->status != IB_WC_SUCCESS || 927320592Shselasky mad_send_wr->status != IB_WC_SUCCESS) 928320592Shselasky return IB_RMPP_RESULT_PROCESSED; /* Canceled or send error */ 929320592Shselasky 930320592Shselasky if (!mad_send_wr->timeout) 931320592Shselasky return IB_RMPP_RESULT_PROCESSED; /* Response received */ 932320592Shselasky 933320592Shselasky if (mad_send_wr->last_ack == mad_send_wr->send_buf.seg_count) { 934320592Shselasky mad_send_wr->timeout = 935320592Shselasky msecs_to_jiffies(mad_send_wr->send_buf.timeout_ms); 936320592Shselasky return IB_RMPP_RESULT_PROCESSED; /* Send done */ 937320592Shselasky } 938320592Shselasky 939320592Shselasky if (mad_send_wr->seg_num == mad_send_wr->newwin || 940320592Shselasky mad_send_wr->seg_num == mad_send_wr->send_buf.seg_count) 941320592Shselasky return IB_RMPP_RESULT_PROCESSED; /* Wait for ACK */ 942320592Shselasky 943320592Shselasky ret = send_next_seg(mad_send_wr); 944320592Shselasky if (ret) { 945320592Shselasky mad_send_wc->status = IB_WC_GENERAL_ERR; 946320592Shselasky return IB_RMPP_RESULT_PROCESSED; 947320592Shselasky } 948320592Shselasky return IB_RMPP_RESULT_CONSUMED; 949320592Shselasky} 950320592Shselasky 951320592Shselaskyint ib_retry_rmpp(struct ib_mad_send_wr_private *mad_send_wr) 952320592Shselasky{ 953320592Shselasky struct ib_rmpp_mad *rmpp_mad; 954320592Shselasky int ret; 955320592Shselasky 956320592Shselasky rmpp_mad = mad_send_wr->send_buf.mad; 957320592Shselasky if (!(ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) & 958320592Shselasky IB_MGMT_RMPP_FLAG_ACTIVE)) 959320592Shselasky return IB_RMPP_RESULT_UNHANDLED; /* RMPP not active */ 960320592Shselasky 961320592Shselasky if (mad_send_wr->last_ack == mad_send_wr->send_buf.seg_count) 962320592Shselasky return IB_RMPP_RESULT_PROCESSED; 963320592Shselasky 964320592Shselasky mad_send_wr->seg_num = mad_send_wr->last_ack; 965320592Shselasky mad_send_wr->cur_seg = mad_send_wr->last_ack_seg; 966320592Shselasky 967320592Shselasky ret = send_next_seg(mad_send_wr); 968320592Shselasky if (ret) 969320592Shselasky return IB_RMPP_RESULT_PROCESSED; 970320592Shselasky 971320592Shselasky return IB_RMPP_RESULT_CONSUMED; 972320592Shselasky} 973