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