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