162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2004-2007 Voltaire, Inc. All rights reserved.
362306a36Sopenharmony_ci * Copyright (c) 2005 Intel Corporation.  All rights reserved.
462306a36Sopenharmony_ci * Copyright (c) 2005 Mellanox Technologies Ltd.  All rights reserved.
562306a36Sopenharmony_ci * Copyright (c) 2009 HNR Consulting. All rights reserved.
662306a36Sopenharmony_ci * Copyright (c) 2014,2018 Intel Corporation.  All rights reserved.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This software is available to you under a choice of one of two
962306a36Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
1062306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
1162306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the
1262306a36Sopenharmony_ci * OpenIB.org BSD license below:
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
1562306a36Sopenharmony_ci *     without modification, are permitted provided that the following
1662306a36Sopenharmony_ci *     conditions are met:
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci *      - Redistributions of source code must retain the above
1962306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2062306a36Sopenharmony_ci *        disclaimer.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
2362306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2462306a36Sopenharmony_ci *        disclaimer in the documentation and/or other materials
2562306a36Sopenharmony_ci *        provided with the distribution.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2862306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2962306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
3062306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
3162306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
3262306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
3362306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3462306a36Sopenharmony_ci * SOFTWARE.
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
4162306a36Sopenharmony_ci#include <linux/slab.h>
4262306a36Sopenharmony_ci#include <linux/module.h>
4362306a36Sopenharmony_ci#include <linux/security.h>
4462306a36Sopenharmony_ci#include <linux/xarray.h>
4562306a36Sopenharmony_ci#include <rdma/ib_cache.h>
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#include "mad_priv.h"
4862306a36Sopenharmony_ci#include "core_priv.h"
4962306a36Sopenharmony_ci#include "mad_rmpp.h"
5062306a36Sopenharmony_ci#include "smi.h"
5162306a36Sopenharmony_ci#include "opa_smi.h"
5262306a36Sopenharmony_ci#include "agent.h"
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
5562306a36Sopenharmony_ci#include <trace/events/ib_mad.h>
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#ifdef CONFIG_TRACEPOINTS
5862306a36Sopenharmony_cistatic void create_mad_addr_info(struct ib_mad_send_wr_private *mad_send_wr,
5962306a36Sopenharmony_ci			  struct ib_mad_qp_info *qp_info,
6062306a36Sopenharmony_ci			  struct trace_event_raw_ib_mad_send_template *entry)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct ib_ud_wr *wr = &mad_send_wr->send_wr;
6362306a36Sopenharmony_ci	struct rdma_ah_attr attr = {};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	rdma_query_ah(wr->ah, &attr);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	/* These are common */
6862306a36Sopenharmony_ci	entry->sl = attr.sl;
6962306a36Sopenharmony_ci	entry->rqpn = wr->remote_qpn;
7062306a36Sopenharmony_ci	entry->rqkey = wr->remote_qkey;
7162306a36Sopenharmony_ci	entry->dlid = rdma_ah_get_dlid(&attr);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci#endif
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int mad_sendq_size = IB_MAD_QP_SEND_SIZE;
7662306a36Sopenharmony_cistatic int mad_recvq_size = IB_MAD_QP_RECV_SIZE;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cimodule_param_named(send_queue_size, mad_sendq_size, int, 0444);
7962306a36Sopenharmony_ciMODULE_PARM_DESC(send_queue_size, "Size of send queue in number of work requests");
8062306a36Sopenharmony_cimodule_param_named(recv_queue_size, mad_recvq_size, int, 0444);
8162306a36Sopenharmony_ciMODULE_PARM_DESC(recv_queue_size, "Size of receive queue in number of work requests");
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic DEFINE_XARRAY_ALLOC1(ib_mad_clients);
8462306a36Sopenharmony_cistatic u32 ib_mad_client_next;
8562306a36Sopenharmony_cistatic struct list_head ib_mad_port_list;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/* Port list lock */
8862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(ib_mad_port_list_lock);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/* Forward declarations */
9162306a36Sopenharmony_cistatic int method_in_use(struct ib_mad_mgmt_method_table **method,
9262306a36Sopenharmony_ci			 struct ib_mad_reg_req *mad_reg_req);
9362306a36Sopenharmony_cistatic void remove_mad_reg_req(struct ib_mad_agent_private *priv);
9462306a36Sopenharmony_cistatic struct ib_mad_agent_private *find_mad_agent(
9562306a36Sopenharmony_ci					struct ib_mad_port_private *port_priv,
9662306a36Sopenharmony_ci					const struct ib_mad_hdr *mad);
9762306a36Sopenharmony_cistatic int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
9862306a36Sopenharmony_ci				    struct ib_mad_private *mad);
9962306a36Sopenharmony_cistatic void cancel_mads(struct ib_mad_agent_private *mad_agent_priv);
10062306a36Sopenharmony_cistatic void timeout_sends(struct work_struct *work);
10162306a36Sopenharmony_cistatic void local_completions(struct work_struct *work);
10262306a36Sopenharmony_cistatic int add_nonoui_reg_req(struct ib_mad_reg_req *mad_reg_req,
10362306a36Sopenharmony_ci			      struct ib_mad_agent_private *agent_priv,
10462306a36Sopenharmony_ci			      u8 mgmt_class);
10562306a36Sopenharmony_cistatic int add_oui_reg_req(struct ib_mad_reg_req *mad_reg_req,
10662306a36Sopenharmony_ci			   struct ib_mad_agent_private *agent_priv);
10762306a36Sopenharmony_cistatic bool ib_mad_send_error(struct ib_mad_port_private *port_priv,
10862306a36Sopenharmony_ci			      struct ib_wc *wc);
10962306a36Sopenharmony_cistatic void ib_mad_send_done(struct ib_cq *cq, struct ib_wc *wc);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/*
11262306a36Sopenharmony_ci * Returns a ib_mad_port_private structure or NULL for a device/port
11362306a36Sopenharmony_ci * Assumes ib_mad_port_list_lock is being held
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_cistatic inline struct ib_mad_port_private *
11662306a36Sopenharmony_ci__ib_get_mad_port(struct ib_device *device, u32 port_num)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct ib_mad_port_private *entry;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	list_for_each_entry(entry, &ib_mad_port_list, port_list) {
12162306a36Sopenharmony_ci		if (entry->device == device && entry->port_num == port_num)
12262306a36Sopenharmony_ci			return entry;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci	return NULL;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/*
12862306a36Sopenharmony_ci * Wrapper function to return a ib_mad_port_private structure or NULL
12962306a36Sopenharmony_ci * for a device/port
13062306a36Sopenharmony_ci */
13162306a36Sopenharmony_cistatic inline struct ib_mad_port_private *
13262306a36Sopenharmony_ciib_get_mad_port(struct ib_device *device, u32 port_num)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct ib_mad_port_private *entry;
13562306a36Sopenharmony_ci	unsigned long flags;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	spin_lock_irqsave(&ib_mad_port_list_lock, flags);
13862306a36Sopenharmony_ci	entry = __ib_get_mad_port(device, port_num);
13962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ib_mad_port_list_lock, flags);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	return entry;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic inline u8 convert_mgmt_class(u8 mgmt_class)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	/* Alias IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE to 0 */
14762306a36Sopenharmony_ci	return mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE ?
14862306a36Sopenharmony_ci		0 : mgmt_class;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int get_spl_qp_index(enum ib_qp_type qp_type)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	switch (qp_type) {
15462306a36Sopenharmony_ci	case IB_QPT_SMI:
15562306a36Sopenharmony_ci		return 0;
15662306a36Sopenharmony_ci	case IB_QPT_GSI:
15762306a36Sopenharmony_ci		return 1;
15862306a36Sopenharmony_ci	default:
15962306a36Sopenharmony_ci		return -1;
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic int vendor_class_index(u8 mgmt_class)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	return mgmt_class - IB_MGMT_CLASS_VENDOR_RANGE2_START;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic int is_vendor_class(u8 mgmt_class)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	if ((mgmt_class < IB_MGMT_CLASS_VENDOR_RANGE2_START) ||
17162306a36Sopenharmony_ci	    (mgmt_class > IB_MGMT_CLASS_VENDOR_RANGE2_END))
17262306a36Sopenharmony_ci		return 0;
17362306a36Sopenharmony_ci	return 1;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int is_vendor_oui(char *oui)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	if (oui[0] || oui[1] || oui[2])
17962306a36Sopenharmony_ci		return 1;
18062306a36Sopenharmony_ci	return 0;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic int is_vendor_method_in_use(
18462306a36Sopenharmony_ci		struct ib_mad_mgmt_vendor_class *vendor_class,
18562306a36Sopenharmony_ci		struct ib_mad_reg_req *mad_reg_req)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	struct ib_mad_mgmt_method_table *method;
18862306a36Sopenharmony_ci	int i;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	for (i = 0; i < MAX_MGMT_OUI; i++) {
19162306a36Sopenharmony_ci		if (!memcmp(vendor_class->oui[i], mad_reg_req->oui, 3)) {
19262306a36Sopenharmony_ci			method = vendor_class->method_table[i];
19362306a36Sopenharmony_ci			if (method) {
19462306a36Sopenharmony_ci				if (method_in_use(&method, mad_reg_req))
19562306a36Sopenharmony_ci					return 1;
19662306a36Sopenharmony_ci				else
19762306a36Sopenharmony_ci					break;
19862306a36Sopenharmony_ci			}
19962306a36Sopenharmony_ci		}
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci	return 0;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ciint ib_response_mad(const struct ib_mad_hdr *hdr)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	return ((hdr->method & IB_MGMT_METHOD_RESP) ||
20762306a36Sopenharmony_ci		(hdr->method == IB_MGMT_METHOD_TRAP_REPRESS) ||
20862306a36Sopenharmony_ci		((hdr->mgmt_class == IB_MGMT_CLASS_BM) &&
20962306a36Sopenharmony_ci		 (hdr->attr_mod & IB_BM_ATTR_MOD_RESP)));
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ciEXPORT_SYMBOL(ib_response_mad);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/*
21462306a36Sopenharmony_ci * ib_register_mad_agent - Register to send/receive MADs
21562306a36Sopenharmony_ci *
21662306a36Sopenharmony_ci * Context: Process context.
21762306a36Sopenharmony_ci */
21862306a36Sopenharmony_cistruct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
21962306a36Sopenharmony_ci					   u32 port_num,
22062306a36Sopenharmony_ci					   enum ib_qp_type qp_type,
22162306a36Sopenharmony_ci					   struct ib_mad_reg_req *mad_reg_req,
22262306a36Sopenharmony_ci					   u8 rmpp_version,
22362306a36Sopenharmony_ci					   ib_mad_send_handler send_handler,
22462306a36Sopenharmony_ci					   ib_mad_recv_handler recv_handler,
22562306a36Sopenharmony_ci					   void *context,
22662306a36Sopenharmony_ci					   u32 registration_flags)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct ib_mad_port_private *port_priv;
22962306a36Sopenharmony_ci	struct ib_mad_agent *ret = ERR_PTR(-EINVAL);
23062306a36Sopenharmony_ci	struct ib_mad_agent_private *mad_agent_priv;
23162306a36Sopenharmony_ci	struct ib_mad_reg_req *reg_req = NULL;
23262306a36Sopenharmony_ci	struct ib_mad_mgmt_class_table *class;
23362306a36Sopenharmony_ci	struct ib_mad_mgmt_vendor_class_table *vendor;
23462306a36Sopenharmony_ci	struct ib_mad_mgmt_vendor_class *vendor_class;
23562306a36Sopenharmony_ci	struct ib_mad_mgmt_method_table *method;
23662306a36Sopenharmony_ci	int ret2, qpn;
23762306a36Sopenharmony_ci	u8 mgmt_class, vclass;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if ((qp_type == IB_QPT_SMI && !rdma_cap_ib_smi(device, port_num)) ||
24062306a36Sopenharmony_ci	    (qp_type == IB_QPT_GSI && !rdma_cap_ib_cm(device, port_num)))
24162306a36Sopenharmony_ci		return ERR_PTR(-EPROTONOSUPPORT);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	/* Validate parameters */
24462306a36Sopenharmony_ci	qpn = get_spl_qp_index(qp_type);
24562306a36Sopenharmony_ci	if (qpn == -1) {
24662306a36Sopenharmony_ci		dev_dbg_ratelimited(&device->dev, "%s: invalid QP Type %d\n",
24762306a36Sopenharmony_ci				    __func__, qp_type);
24862306a36Sopenharmony_ci		goto error1;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (rmpp_version && rmpp_version != IB_MGMT_RMPP_VERSION) {
25262306a36Sopenharmony_ci		dev_dbg_ratelimited(&device->dev,
25362306a36Sopenharmony_ci				    "%s: invalid RMPP Version %u\n",
25462306a36Sopenharmony_ci				    __func__, rmpp_version);
25562306a36Sopenharmony_ci		goto error1;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* Validate MAD registration request if supplied */
25962306a36Sopenharmony_ci	if (mad_reg_req) {
26062306a36Sopenharmony_ci		if (mad_reg_req->mgmt_class_version >= MAX_MGMT_VERSION) {
26162306a36Sopenharmony_ci			dev_dbg_ratelimited(&device->dev,
26262306a36Sopenharmony_ci					    "%s: invalid Class Version %u\n",
26362306a36Sopenharmony_ci					    __func__,
26462306a36Sopenharmony_ci					    mad_reg_req->mgmt_class_version);
26562306a36Sopenharmony_ci			goto error1;
26662306a36Sopenharmony_ci		}
26762306a36Sopenharmony_ci		if (!recv_handler) {
26862306a36Sopenharmony_ci			dev_dbg_ratelimited(&device->dev,
26962306a36Sopenharmony_ci					    "%s: no recv_handler\n", __func__);
27062306a36Sopenharmony_ci			goto error1;
27162306a36Sopenharmony_ci		}
27262306a36Sopenharmony_ci		if (mad_reg_req->mgmt_class >= MAX_MGMT_CLASS) {
27362306a36Sopenharmony_ci			/*
27462306a36Sopenharmony_ci			 * IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE is the only
27562306a36Sopenharmony_ci			 * one in this range currently allowed
27662306a36Sopenharmony_ci			 */
27762306a36Sopenharmony_ci			if (mad_reg_req->mgmt_class !=
27862306a36Sopenharmony_ci			    IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
27962306a36Sopenharmony_ci				dev_dbg_ratelimited(&device->dev,
28062306a36Sopenharmony_ci					"%s: Invalid Mgmt Class 0x%x\n",
28162306a36Sopenharmony_ci					__func__, mad_reg_req->mgmt_class);
28262306a36Sopenharmony_ci				goto error1;
28362306a36Sopenharmony_ci			}
28462306a36Sopenharmony_ci		} else if (mad_reg_req->mgmt_class == 0) {
28562306a36Sopenharmony_ci			/*
28662306a36Sopenharmony_ci			 * Class 0 is reserved in IBA and is used for
28762306a36Sopenharmony_ci			 * aliasing of IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE
28862306a36Sopenharmony_ci			 */
28962306a36Sopenharmony_ci			dev_dbg_ratelimited(&device->dev,
29062306a36Sopenharmony_ci					    "%s: Invalid Mgmt Class 0\n",
29162306a36Sopenharmony_ci					    __func__);
29262306a36Sopenharmony_ci			goto error1;
29362306a36Sopenharmony_ci		} else if (is_vendor_class(mad_reg_req->mgmt_class)) {
29462306a36Sopenharmony_ci			/*
29562306a36Sopenharmony_ci			 * If class is in "new" vendor range,
29662306a36Sopenharmony_ci			 * ensure supplied OUI is not zero
29762306a36Sopenharmony_ci			 */
29862306a36Sopenharmony_ci			if (!is_vendor_oui(mad_reg_req->oui)) {
29962306a36Sopenharmony_ci				dev_dbg_ratelimited(&device->dev,
30062306a36Sopenharmony_ci					"%s: No OUI specified for class 0x%x\n",
30162306a36Sopenharmony_ci					__func__,
30262306a36Sopenharmony_ci					mad_reg_req->mgmt_class);
30362306a36Sopenharmony_ci				goto error1;
30462306a36Sopenharmony_ci			}
30562306a36Sopenharmony_ci		}
30662306a36Sopenharmony_ci		/* Make sure class supplied is consistent with RMPP */
30762306a36Sopenharmony_ci		if (!ib_is_mad_class_rmpp(mad_reg_req->mgmt_class)) {
30862306a36Sopenharmony_ci			if (rmpp_version) {
30962306a36Sopenharmony_ci				dev_dbg_ratelimited(&device->dev,
31062306a36Sopenharmony_ci					"%s: RMPP version for non-RMPP class 0x%x\n",
31162306a36Sopenharmony_ci					__func__, mad_reg_req->mgmt_class);
31262306a36Sopenharmony_ci				goto error1;
31362306a36Sopenharmony_ci			}
31462306a36Sopenharmony_ci		}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		/* Make sure class supplied is consistent with QP type */
31762306a36Sopenharmony_ci		if (qp_type == IB_QPT_SMI) {
31862306a36Sopenharmony_ci			if ((mad_reg_req->mgmt_class !=
31962306a36Sopenharmony_ci					IB_MGMT_CLASS_SUBN_LID_ROUTED) &&
32062306a36Sopenharmony_ci			    (mad_reg_req->mgmt_class !=
32162306a36Sopenharmony_ci					IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)) {
32262306a36Sopenharmony_ci				dev_dbg_ratelimited(&device->dev,
32362306a36Sopenharmony_ci					"%s: Invalid SM QP type: class 0x%x\n",
32462306a36Sopenharmony_ci					__func__, mad_reg_req->mgmt_class);
32562306a36Sopenharmony_ci				goto error1;
32662306a36Sopenharmony_ci			}
32762306a36Sopenharmony_ci		} else {
32862306a36Sopenharmony_ci			if ((mad_reg_req->mgmt_class ==
32962306a36Sopenharmony_ci					IB_MGMT_CLASS_SUBN_LID_ROUTED) ||
33062306a36Sopenharmony_ci			    (mad_reg_req->mgmt_class ==
33162306a36Sopenharmony_ci					IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)) {
33262306a36Sopenharmony_ci				dev_dbg_ratelimited(&device->dev,
33362306a36Sopenharmony_ci					"%s: Invalid GS QP type: class 0x%x\n",
33462306a36Sopenharmony_ci					__func__, mad_reg_req->mgmt_class);
33562306a36Sopenharmony_ci				goto error1;
33662306a36Sopenharmony_ci			}
33762306a36Sopenharmony_ci		}
33862306a36Sopenharmony_ci	} else {
33962306a36Sopenharmony_ci		/* No registration request supplied */
34062306a36Sopenharmony_ci		if (!send_handler)
34162306a36Sopenharmony_ci			goto error1;
34262306a36Sopenharmony_ci		if (registration_flags & IB_MAD_USER_RMPP)
34362306a36Sopenharmony_ci			goto error1;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	/* Validate device and port */
34762306a36Sopenharmony_ci	port_priv = ib_get_mad_port(device, port_num);
34862306a36Sopenharmony_ci	if (!port_priv) {
34962306a36Sopenharmony_ci		dev_dbg_ratelimited(&device->dev, "%s: Invalid port %u\n",
35062306a36Sopenharmony_ci				    __func__, port_num);
35162306a36Sopenharmony_ci		ret = ERR_PTR(-ENODEV);
35262306a36Sopenharmony_ci		goto error1;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/* Verify the QP requested is supported. For example, Ethernet devices
35662306a36Sopenharmony_ci	 * will not have QP0.
35762306a36Sopenharmony_ci	 */
35862306a36Sopenharmony_ci	if (!port_priv->qp_info[qpn].qp) {
35962306a36Sopenharmony_ci		dev_dbg_ratelimited(&device->dev, "%s: QP %d not supported\n",
36062306a36Sopenharmony_ci				    __func__, qpn);
36162306a36Sopenharmony_ci		ret = ERR_PTR(-EPROTONOSUPPORT);
36262306a36Sopenharmony_ci		goto error1;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	/* Allocate structures */
36662306a36Sopenharmony_ci	mad_agent_priv = kzalloc(sizeof *mad_agent_priv, GFP_KERNEL);
36762306a36Sopenharmony_ci	if (!mad_agent_priv) {
36862306a36Sopenharmony_ci		ret = ERR_PTR(-ENOMEM);
36962306a36Sopenharmony_ci		goto error1;
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (mad_reg_req) {
37362306a36Sopenharmony_ci		reg_req = kmemdup(mad_reg_req, sizeof *reg_req, GFP_KERNEL);
37462306a36Sopenharmony_ci		if (!reg_req) {
37562306a36Sopenharmony_ci			ret = ERR_PTR(-ENOMEM);
37662306a36Sopenharmony_ci			goto error3;
37762306a36Sopenharmony_ci		}
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/* Now, fill in the various structures */
38162306a36Sopenharmony_ci	mad_agent_priv->qp_info = &port_priv->qp_info[qpn];
38262306a36Sopenharmony_ci	mad_agent_priv->reg_req = reg_req;
38362306a36Sopenharmony_ci	mad_agent_priv->agent.rmpp_version = rmpp_version;
38462306a36Sopenharmony_ci	mad_agent_priv->agent.device = device;
38562306a36Sopenharmony_ci	mad_agent_priv->agent.recv_handler = recv_handler;
38662306a36Sopenharmony_ci	mad_agent_priv->agent.send_handler = send_handler;
38762306a36Sopenharmony_ci	mad_agent_priv->agent.context = context;
38862306a36Sopenharmony_ci	mad_agent_priv->agent.qp = port_priv->qp_info[qpn].qp;
38962306a36Sopenharmony_ci	mad_agent_priv->agent.port_num = port_num;
39062306a36Sopenharmony_ci	mad_agent_priv->agent.flags = registration_flags;
39162306a36Sopenharmony_ci	spin_lock_init(&mad_agent_priv->lock);
39262306a36Sopenharmony_ci	INIT_LIST_HEAD(&mad_agent_priv->send_list);
39362306a36Sopenharmony_ci	INIT_LIST_HEAD(&mad_agent_priv->wait_list);
39462306a36Sopenharmony_ci	INIT_LIST_HEAD(&mad_agent_priv->done_list);
39562306a36Sopenharmony_ci	INIT_LIST_HEAD(&mad_agent_priv->rmpp_list);
39662306a36Sopenharmony_ci	INIT_DELAYED_WORK(&mad_agent_priv->timed_work, timeout_sends);
39762306a36Sopenharmony_ci	INIT_LIST_HEAD(&mad_agent_priv->local_list);
39862306a36Sopenharmony_ci	INIT_WORK(&mad_agent_priv->local_work, local_completions);
39962306a36Sopenharmony_ci	refcount_set(&mad_agent_priv->refcount, 1);
40062306a36Sopenharmony_ci	init_completion(&mad_agent_priv->comp);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	ret2 = ib_mad_agent_security_setup(&mad_agent_priv->agent, qp_type);
40362306a36Sopenharmony_ci	if (ret2) {
40462306a36Sopenharmony_ci		ret = ERR_PTR(ret2);
40562306a36Sopenharmony_ci		goto error4;
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	/*
40962306a36Sopenharmony_ci	 * The mlx4 driver uses the top byte to distinguish which virtual
41062306a36Sopenharmony_ci	 * function generated the MAD, so we must avoid using it.
41162306a36Sopenharmony_ci	 */
41262306a36Sopenharmony_ci	ret2 = xa_alloc_cyclic(&ib_mad_clients, &mad_agent_priv->agent.hi_tid,
41362306a36Sopenharmony_ci			mad_agent_priv, XA_LIMIT(0, (1 << 24) - 1),
41462306a36Sopenharmony_ci			&ib_mad_client_next, GFP_KERNEL);
41562306a36Sopenharmony_ci	if (ret2 < 0) {
41662306a36Sopenharmony_ci		ret = ERR_PTR(ret2);
41762306a36Sopenharmony_ci		goto error5;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/*
42162306a36Sopenharmony_ci	 * Make sure MAD registration (if supplied)
42262306a36Sopenharmony_ci	 * is non overlapping with any existing ones
42362306a36Sopenharmony_ci	 */
42462306a36Sopenharmony_ci	spin_lock_irq(&port_priv->reg_lock);
42562306a36Sopenharmony_ci	if (mad_reg_req) {
42662306a36Sopenharmony_ci		mgmt_class = convert_mgmt_class(mad_reg_req->mgmt_class);
42762306a36Sopenharmony_ci		if (!is_vendor_class(mgmt_class)) {
42862306a36Sopenharmony_ci			class = port_priv->version[mad_reg_req->
42962306a36Sopenharmony_ci						   mgmt_class_version].class;
43062306a36Sopenharmony_ci			if (class) {
43162306a36Sopenharmony_ci				method = class->method_table[mgmt_class];
43262306a36Sopenharmony_ci				if (method) {
43362306a36Sopenharmony_ci					if (method_in_use(&method,
43462306a36Sopenharmony_ci							   mad_reg_req))
43562306a36Sopenharmony_ci						goto error6;
43662306a36Sopenharmony_ci				}
43762306a36Sopenharmony_ci			}
43862306a36Sopenharmony_ci			ret2 = add_nonoui_reg_req(mad_reg_req, mad_agent_priv,
43962306a36Sopenharmony_ci						  mgmt_class);
44062306a36Sopenharmony_ci		} else {
44162306a36Sopenharmony_ci			/* "New" vendor class range */
44262306a36Sopenharmony_ci			vendor = port_priv->version[mad_reg_req->
44362306a36Sopenharmony_ci						    mgmt_class_version].vendor;
44462306a36Sopenharmony_ci			if (vendor) {
44562306a36Sopenharmony_ci				vclass = vendor_class_index(mgmt_class);
44662306a36Sopenharmony_ci				vendor_class = vendor->vendor_class[vclass];
44762306a36Sopenharmony_ci				if (vendor_class) {
44862306a36Sopenharmony_ci					if (is_vendor_method_in_use(
44962306a36Sopenharmony_ci							vendor_class,
45062306a36Sopenharmony_ci							mad_reg_req))
45162306a36Sopenharmony_ci						goto error6;
45262306a36Sopenharmony_ci				}
45362306a36Sopenharmony_ci			}
45462306a36Sopenharmony_ci			ret2 = add_oui_reg_req(mad_reg_req, mad_agent_priv);
45562306a36Sopenharmony_ci		}
45662306a36Sopenharmony_ci		if (ret2) {
45762306a36Sopenharmony_ci			ret = ERR_PTR(ret2);
45862306a36Sopenharmony_ci			goto error6;
45962306a36Sopenharmony_ci		}
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci	spin_unlock_irq(&port_priv->reg_lock);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	trace_ib_mad_create_agent(mad_agent_priv);
46462306a36Sopenharmony_ci	return &mad_agent_priv->agent;
46562306a36Sopenharmony_cierror6:
46662306a36Sopenharmony_ci	spin_unlock_irq(&port_priv->reg_lock);
46762306a36Sopenharmony_ci	xa_erase(&ib_mad_clients, mad_agent_priv->agent.hi_tid);
46862306a36Sopenharmony_cierror5:
46962306a36Sopenharmony_ci	ib_mad_agent_security_cleanup(&mad_agent_priv->agent);
47062306a36Sopenharmony_cierror4:
47162306a36Sopenharmony_ci	kfree(reg_req);
47262306a36Sopenharmony_cierror3:
47362306a36Sopenharmony_ci	kfree(mad_agent_priv);
47462306a36Sopenharmony_cierror1:
47562306a36Sopenharmony_ci	return ret;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ciEXPORT_SYMBOL(ib_register_mad_agent);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic inline void deref_mad_agent(struct ib_mad_agent_private *mad_agent_priv)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	if (refcount_dec_and_test(&mad_agent_priv->refcount))
48262306a36Sopenharmony_ci		complete(&mad_agent_priv->comp);
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic void unregister_mad_agent(struct ib_mad_agent_private *mad_agent_priv)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	struct ib_mad_port_private *port_priv;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	/* Note that we could still be handling received MADs */
49062306a36Sopenharmony_ci	trace_ib_mad_unregister_agent(mad_agent_priv);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	/*
49362306a36Sopenharmony_ci	 * Canceling all sends results in dropping received response
49462306a36Sopenharmony_ci	 * MADs, preventing us from queuing additional work
49562306a36Sopenharmony_ci	 */
49662306a36Sopenharmony_ci	cancel_mads(mad_agent_priv);
49762306a36Sopenharmony_ci	port_priv = mad_agent_priv->qp_info->port_priv;
49862306a36Sopenharmony_ci	cancel_delayed_work(&mad_agent_priv->timed_work);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	spin_lock_irq(&port_priv->reg_lock);
50162306a36Sopenharmony_ci	remove_mad_reg_req(mad_agent_priv);
50262306a36Sopenharmony_ci	spin_unlock_irq(&port_priv->reg_lock);
50362306a36Sopenharmony_ci	xa_erase(&ib_mad_clients, mad_agent_priv->agent.hi_tid);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	flush_workqueue(port_priv->wq);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	deref_mad_agent(mad_agent_priv);
50862306a36Sopenharmony_ci	wait_for_completion(&mad_agent_priv->comp);
50962306a36Sopenharmony_ci	ib_cancel_rmpp_recvs(mad_agent_priv);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	ib_mad_agent_security_cleanup(&mad_agent_priv->agent);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	kfree(mad_agent_priv->reg_req);
51462306a36Sopenharmony_ci	kfree_rcu(mad_agent_priv, rcu);
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci/*
51862306a36Sopenharmony_ci * ib_unregister_mad_agent - Unregisters a client from using MAD services
51962306a36Sopenharmony_ci *
52062306a36Sopenharmony_ci * Context: Process context.
52162306a36Sopenharmony_ci */
52262306a36Sopenharmony_civoid ib_unregister_mad_agent(struct ib_mad_agent *mad_agent)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	struct ib_mad_agent_private *mad_agent_priv;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	mad_agent_priv = container_of(mad_agent,
52762306a36Sopenharmony_ci				      struct ib_mad_agent_private,
52862306a36Sopenharmony_ci				      agent);
52962306a36Sopenharmony_ci	unregister_mad_agent(mad_agent_priv);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ciEXPORT_SYMBOL(ib_unregister_mad_agent);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic void dequeue_mad(struct ib_mad_list_head *mad_list)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	struct ib_mad_queue *mad_queue;
53662306a36Sopenharmony_ci	unsigned long flags;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	mad_queue = mad_list->mad_queue;
53962306a36Sopenharmony_ci	spin_lock_irqsave(&mad_queue->lock, flags);
54062306a36Sopenharmony_ci	list_del(&mad_list->list);
54162306a36Sopenharmony_ci	mad_queue->count--;
54262306a36Sopenharmony_ci	spin_unlock_irqrestore(&mad_queue->lock, flags);
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic void build_smp_wc(struct ib_qp *qp, struct ib_cqe *cqe, u16 slid,
54662306a36Sopenharmony_ci		u16 pkey_index, u32 port_num, struct ib_wc *wc)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	memset(wc, 0, sizeof *wc);
54962306a36Sopenharmony_ci	wc->wr_cqe = cqe;
55062306a36Sopenharmony_ci	wc->status = IB_WC_SUCCESS;
55162306a36Sopenharmony_ci	wc->opcode = IB_WC_RECV;
55262306a36Sopenharmony_ci	wc->pkey_index = pkey_index;
55362306a36Sopenharmony_ci	wc->byte_len = sizeof(struct ib_mad) + sizeof(struct ib_grh);
55462306a36Sopenharmony_ci	wc->src_qp = IB_QP0;
55562306a36Sopenharmony_ci	wc->qp = qp;
55662306a36Sopenharmony_ci	wc->slid = slid;
55762306a36Sopenharmony_ci	wc->sl = 0;
55862306a36Sopenharmony_ci	wc->dlid_path_bits = 0;
55962306a36Sopenharmony_ci	wc->port_num = port_num;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic size_t mad_priv_size(const struct ib_mad_private *mp)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	return sizeof(struct ib_mad_private) + mp->mad_size;
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic struct ib_mad_private *alloc_mad_private(size_t mad_size, gfp_t flags)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	size_t size = sizeof(struct ib_mad_private) + mad_size;
57062306a36Sopenharmony_ci	struct ib_mad_private *ret = kzalloc(size, flags);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (ret)
57362306a36Sopenharmony_ci		ret->mad_size = mad_size;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	return ret;
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic size_t port_mad_size(const struct ib_mad_port_private *port_priv)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	return rdma_max_mad_size(port_priv->device, port_priv->port_num);
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic size_t mad_priv_dma_size(const struct ib_mad_private *mp)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	return sizeof(struct ib_grh) + mp->mad_size;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci/*
58962306a36Sopenharmony_ci * Return 0 if SMP is to be sent
59062306a36Sopenharmony_ci * Return 1 if SMP was consumed locally (whether or not solicited)
59162306a36Sopenharmony_ci * Return < 0 if error
59262306a36Sopenharmony_ci */
59362306a36Sopenharmony_cistatic int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
59462306a36Sopenharmony_ci				  struct ib_mad_send_wr_private *mad_send_wr)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	int ret = 0;
59762306a36Sopenharmony_ci	struct ib_smp *smp = mad_send_wr->send_buf.mad;
59862306a36Sopenharmony_ci	struct opa_smp *opa_smp = (struct opa_smp *)smp;
59962306a36Sopenharmony_ci	unsigned long flags;
60062306a36Sopenharmony_ci	struct ib_mad_local_private *local;
60162306a36Sopenharmony_ci	struct ib_mad_private *mad_priv;
60262306a36Sopenharmony_ci	struct ib_mad_port_private *port_priv;
60362306a36Sopenharmony_ci	struct ib_mad_agent_private *recv_mad_agent = NULL;
60462306a36Sopenharmony_ci	struct ib_device *device = mad_agent_priv->agent.device;
60562306a36Sopenharmony_ci	u32 port_num;
60662306a36Sopenharmony_ci	struct ib_wc mad_wc;
60762306a36Sopenharmony_ci	struct ib_ud_wr *send_wr = &mad_send_wr->send_wr;
60862306a36Sopenharmony_ci	size_t mad_size = port_mad_size(mad_agent_priv->qp_info->port_priv);
60962306a36Sopenharmony_ci	u16 out_mad_pkey_index = 0;
61062306a36Sopenharmony_ci	u16 drslid;
61162306a36Sopenharmony_ci	bool opa = rdma_cap_opa_mad(mad_agent_priv->qp_info->port_priv->device,
61262306a36Sopenharmony_ci				    mad_agent_priv->qp_info->port_priv->port_num);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (rdma_cap_ib_switch(device) &&
61562306a36Sopenharmony_ci	    smp->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
61662306a36Sopenharmony_ci		port_num = send_wr->port_num;
61762306a36Sopenharmony_ci	else
61862306a36Sopenharmony_ci		port_num = mad_agent_priv->agent.port_num;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	/*
62162306a36Sopenharmony_ci	 * Directed route handling starts if the initial LID routed part of
62262306a36Sopenharmony_ci	 * a request or the ending LID routed part of a response is empty.
62362306a36Sopenharmony_ci	 * If we are at the start of the LID routed part, don't update the
62462306a36Sopenharmony_ci	 * hop_ptr or hop_cnt.  See section 14.2.2, Vol 1 IB spec.
62562306a36Sopenharmony_ci	 */
62662306a36Sopenharmony_ci	if (opa && smp->class_version == OPA_SM_CLASS_VERSION) {
62762306a36Sopenharmony_ci		u32 opa_drslid;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci		trace_ib_mad_handle_out_opa_smi(opa_smp);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci		if ((opa_get_smp_direction(opa_smp)
63262306a36Sopenharmony_ci		     ? opa_smp->route.dr.dr_dlid : opa_smp->route.dr.dr_slid) ==
63362306a36Sopenharmony_ci		     OPA_LID_PERMISSIVE &&
63462306a36Sopenharmony_ci		     opa_smi_handle_dr_smp_send(opa_smp,
63562306a36Sopenharmony_ci						rdma_cap_ib_switch(device),
63662306a36Sopenharmony_ci						port_num) == IB_SMI_DISCARD) {
63762306a36Sopenharmony_ci			ret = -EINVAL;
63862306a36Sopenharmony_ci			dev_err(&device->dev, "OPA Invalid directed route\n");
63962306a36Sopenharmony_ci			goto out;
64062306a36Sopenharmony_ci		}
64162306a36Sopenharmony_ci		opa_drslid = be32_to_cpu(opa_smp->route.dr.dr_slid);
64262306a36Sopenharmony_ci		if (opa_drslid != be32_to_cpu(OPA_LID_PERMISSIVE) &&
64362306a36Sopenharmony_ci		    opa_drslid & 0xffff0000) {
64462306a36Sopenharmony_ci			ret = -EINVAL;
64562306a36Sopenharmony_ci			dev_err(&device->dev, "OPA Invalid dr_slid 0x%x\n",
64662306a36Sopenharmony_ci			       opa_drslid);
64762306a36Sopenharmony_ci			goto out;
64862306a36Sopenharmony_ci		}
64962306a36Sopenharmony_ci		drslid = (u16)(opa_drslid & 0x0000ffff);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci		/* Check to post send on QP or process locally */
65262306a36Sopenharmony_ci		if (opa_smi_check_local_smp(opa_smp, device) == IB_SMI_DISCARD &&
65362306a36Sopenharmony_ci		    opa_smi_check_local_returning_smp(opa_smp, device) == IB_SMI_DISCARD)
65462306a36Sopenharmony_ci			goto out;
65562306a36Sopenharmony_ci	} else {
65662306a36Sopenharmony_ci		trace_ib_mad_handle_out_ib_smi(smp);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci		if ((ib_get_smp_direction(smp) ? smp->dr_dlid : smp->dr_slid) ==
65962306a36Sopenharmony_ci		     IB_LID_PERMISSIVE &&
66062306a36Sopenharmony_ci		     smi_handle_dr_smp_send(smp, rdma_cap_ib_switch(device), port_num) ==
66162306a36Sopenharmony_ci		     IB_SMI_DISCARD) {
66262306a36Sopenharmony_ci			ret = -EINVAL;
66362306a36Sopenharmony_ci			dev_err(&device->dev, "Invalid directed route\n");
66462306a36Sopenharmony_ci			goto out;
66562306a36Sopenharmony_ci		}
66662306a36Sopenharmony_ci		drslid = be16_to_cpu(smp->dr_slid);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci		/* Check to post send on QP or process locally */
66962306a36Sopenharmony_ci		if (smi_check_local_smp(smp, device) == IB_SMI_DISCARD &&
67062306a36Sopenharmony_ci		    smi_check_local_returning_smp(smp, device) == IB_SMI_DISCARD)
67162306a36Sopenharmony_ci			goto out;
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	local = kmalloc(sizeof *local, GFP_ATOMIC);
67562306a36Sopenharmony_ci	if (!local) {
67662306a36Sopenharmony_ci		ret = -ENOMEM;
67762306a36Sopenharmony_ci		goto out;
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci	local->mad_priv = NULL;
68062306a36Sopenharmony_ci	local->recv_mad_agent = NULL;
68162306a36Sopenharmony_ci	mad_priv = alloc_mad_private(mad_size, GFP_ATOMIC);
68262306a36Sopenharmony_ci	if (!mad_priv) {
68362306a36Sopenharmony_ci		ret = -ENOMEM;
68462306a36Sopenharmony_ci		kfree(local);
68562306a36Sopenharmony_ci		goto out;
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	build_smp_wc(mad_agent_priv->agent.qp,
68962306a36Sopenharmony_ci		     send_wr->wr.wr_cqe, drslid,
69062306a36Sopenharmony_ci		     send_wr->pkey_index,
69162306a36Sopenharmony_ci		     send_wr->port_num, &mad_wc);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (opa && smp->base_version == OPA_MGMT_BASE_VERSION) {
69462306a36Sopenharmony_ci		mad_wc.byte_len = mad_send_wr->send_buf.hdr_len
69562306a36Sopenharmony_ci					+ mad_send_wr->send_buf.data_len
69662306a36Sopenharmony_ci					+ sizeof(struct ib_grh);
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/* No GRH for DR SMP */
70062306a36Sopenharmony_ci	ret = device->ops.process_mad(device, 0, port_num, &mad_wc, NULL,
70162306a36Sopenharmony_ci				      (const struct ib_mad *)smp,
70262306a36Sopenharmony_ci				      (struct ib_mad *)mad_priv->mad, &mad_size,
70362306a36Sopenharmony_ci				      &out_mad_pkey_index);
70462306a36Sopenharmony_ci	switch (ret) {
70562306a36Sopenharmony_ci	case IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY:
70662306a36Sopenharmony_ci		if (ib_response_mad((const struct ib_mad_hdr *)mad_priv->mad) &&
70762306a36Sopenharmony_ci		    mad_agent_priv->agent.recv_handler) {
70862306a36Sopenharmony_ci			local->mad_priv = mad_priv;
70962306a36Sopenharmony_ci			local->recv_mad_agent = mad_agent_priv;
71062306a36Sopenharmony_ci			/*
71162306a36Sopenharmony_ci			 * Reference MAD agent until receive
71262306a36Sopenharmony_ci			 * side of local completion handled
71362306a36Sopenharmony_ci			 */
71462306a36Sopenharmony_ci			refcount_inc(&mad_agent_priv->refcount);
71562306a36Sopenharmony_ci		} else
71662306a36Sopenharmony_ci			kfree(mad_priv);
71762306a36Sopenharmony_ci		break;
71862306a36Sopenharmony_ci	case IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED:
71962306a36Sopenharmony_ci		kfree(mad_priv);
72062306a36Sopenharmony_ci		break;
72162306a36Sopenharmony_ci	case IB_MAD_RESULT_SUCCESS:
72262306a36Sopenharmony_ci		/* Treat like an incoming receive MAD */
72362306a36Sopenharmony_ci		port_priv = ib_get_mad_port(mad_agent_priv->agent.device,
72462306a36Sopenharmony_ci					    mad_agent_priv->agent.port_num);
72562306a36Sopenharmony_ci		if (port_priv) {
72662306a36Sopenharmony_ci			memcpy(mad_priv->mad, smp, mad_priv->mad_size);
72762306a36Sopenharmony_ci			recv_mad_agent = find_mad_agent(port_priv,
72862306a36Sopenharmony_ci						        (const struct ib_mad_hdr *)mad_priv->mad);
72962306a36Sopenharmony_ci		}
73062306a36Sopenharmony_ci		if (!port_priv || !recv_mad_agent) {
73162306a36Sopenharmony_ci			/*
73262306a36Sopenharmony_ci			 * No receiving agent so drop packet and
73362306a36Sopenharmony_ci			 * generate send completion.
73462306a36Sopenharmony_ci			 */
73562306a36Sopenharmony_ci			kfree(mad_priv);
73662306a36Sopenharmony_ci			break;
73762306a36Sopenharmony_ci		}
73862306a36Sopenharmony_ci		local->mad_priv = mad_priv;
73962306a36Sopenharmony_ci		local->recv_mad_agent = recv_mad_agent;
74062306a36Sopenharmony_ci		break;
74162306a36Sopenharmony_ci	default:
74262306a36Sopenharmony_ci		kfree(mad_priv);
74362306a36Sopenharmony_ci		kfree(local);
74462306a36Sopenharmony_ci		ret = -EINVAL;
74562306a36Sopenharmony_ci		goto out;
74662306a36Sopenharmony_ci	}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	local->mad_send_wr = mad_send_wr;
74962306a36Sopenharmony_ci	if (opa) {
75062306a36Sopenharmony_ci		local->mad_send_wr->send_wr.pkey_index = out_mad_pkey_index;
75162306a36Sopenharmony_ci		local->return_wc_byte_len = mad_size;
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci	/* Reference MAD agent until send side of local completion handled */
75462306a36Sopenharmony_ci	refcount_inc(&mad_agent_priv->refcount);
75562306a36Sopenharmony_ci	/* Queue local completion to local list */
75662306a36Sopenharmony_ci	spin_lock_irqsave(&mad_agent_priv->lock, flags);
75762306a36Sopenharmony_ci	list_add_tail(&local->completion_list, &mad_agent_priv->local_list);
75862306a36Sopenharmony_ci	spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
75962306a36Sopenharmony_ci	queue_work(mad_agent_priv->qp_info->port_priv->wq,
76062306a36Sopenharmony_ci		   &mad_agent_priv->local_work);
76162306a36Sopenharmony_ci	ret = 1;
76262306a36Sopenharmony_ciout:
76362306a36Sopenharmony_ci	return ret;
76462306a36Sopenharmony_ci}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_cistatic int get_pad_size(int hdr_len, int data_len, size_t mad_size)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	int seg_size, pad;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	seg_size = mad_size - hdr_len;
77162306a36Sopenharmony_ci	if (data_len && seg_size) {
77262306a36Sopenharmony_ci		pad = seg_size - data_len % seg_size;
77362306a36Sopenharmony_ci		return pad == seg_size ? 0 : pad;
77462306a36Sopenharmony_ci	} else
77562306a36Sopenharmony_ci		return seg_size;
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_cistatic void free_send_rmpp_list(struct ib_mad_send_wr_private *mad_send_wr)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	struct ib_rmpp_segment *s, *t;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	list_for_each_entry_safe(s, t, &mad_send_wr->rmpp_list, list) {
78362306a36Sopenharmony_ci		list_del(&s->list);
78462306a36Sopenharmony_ci		kfree(s);
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic int alloc_send_rmpp_list(struct ib_mad_send_wr_private *send_wr,
78962306a36Sopenharmony_ci				size_t mad_size, gfp_t gfp_mask)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	struct ib_mad_send_buf *send_buf = &send_wr->send_buf;
79262306a36Sopenharmony_ci	struct ib_rmpp_mad *rmpp_mad = send_buf->mad;
79362306a36Sopenharmony_ci	struct ib_rmpp_segment *seg = NULL;
79462306a36Sopenharmony_ci	int left, seg_size, pad;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	send_buf->seg_size = mad_size - send_buf->hdr_len;
79762306a36Sopenharmony_ci	send_buf->seg_rmpp_size = mad_size - IB_MGMT_RMPP_HDR;
79862306a36Sopenharmony_ci	seg_size = send_buf->seg_size;
79962306a36Sopenharmony_ci	pad = send_wr->pad;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	/* Allocate data segments. */
80262306a36Sopenharmony_ci	for (left = send_buf->data_len + pad; left > 0; left -= seg_size) {
80362306a36Sopenharmony_ci		seg = kmalloc(sizeof(*seg) + seg_size, gfp_mask);
80462306a36Sopenharmony_ci		if (!seg) {
80562306a36Sopenharmony_ci			free_send_rmpp_list(send_wr);
80662306a36Sopenharmony_ci			return -ENOMEM;
80762306a36Sopenharmony_ci		}
80862306a36Sopenharmony_ci		seg->num = ++send_buf->seg_count;
80962306a36Sopenharmony_ci		list_add_tail(&seg->list, &send_wr->rmpp_list);
81062306a36Sopenharmony_ci	}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	/* Zero any padding */
81362306a36Sopenharmony_ci	if (pad)
81462306a36Sopenharmony_ci		memset(seg->data + seg_size - pad, 0, pad);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	rmpp_mad->rmpp_hdr.rmpp_version = send_wr->mad_agent_priv->
81762306a36Sopenharmony_ci					  agent.rmpp_version;
81862306a36Sopenharmony_ci	rmpp_mad->rmpp_hdr.rmpp_type = IB_MGMT_RMPP_TYPE_DATA;
81962306a36Sopenharmony_ci	ib_set_rmpp_flags(&rmpp_mad->rmpp_hdr, IB_MGMT_RMPP_FLAG_ACTIVE);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	send_wr->cur_seg = container_of(send_wr->rmpp_list.next,
82262306a36Sopenharmony_ci					struct ib_rmpp_segment, list);
82362306a36Sopenharmony_ci	send_wr->last_ack_seg = send_wr->cur_seg;
82462306a36Sopenharmony_ci	return 0;
82562306a36Sopenharmony_ci}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ciint ib_mad_kernel_rmpp_agent(const struct ib_mad_agent *agent)
82862306a36Sopenharmony_ci{
82962306a36Sopenharmony_ci	return agent->rmpp_version && !(agent->flags & IB_MAD_USER_RMPP);
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ciEXPORT_SYMBOL(ib_mad_kernel_rmpp_agent);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_cistruct ib_mad_send_buf *ib_create_send_mad(struct ib_mad_agent *mad_agent,
83462306a36Sopenharmony_ci					   u32 remote_qpn, u16 pkey_index,
83562306a36Sopenharmony_ci					   int rmpp_active, int hdr_len,
83662306a36Sopenharmony_ci					   int data_len, gfp_t gfp_mask,
83762306a36Sopenharmony_ci					   u8 base_version)
83862306a36Sopenharmony_ci{
83962306a36Sopenharmony_ci	struct ib_mad_agent_private *mad_agent_priv;
84062306a36Sopenharmony_ci	struct ib_mad_send_wr_private *mad_send_wr;
84162306a36Sopenharmony_ci	int pad, message_size, ret, size;
84262306a36Sopenharmony_ci	void *buf;
84362306a36Sopenharmony_ci	size_t mad_size;
84462306a36Sopenharmony_ci	bool opa;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	mad_agent_priv = container_of(mad_agent, struct ib_mad_agent_private,
84762306a36Sopenharmony_ci				      agent);
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	opa = rdma_cap_opa_mad(mad_agent->device, mad_agent->port_num);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	if (opa && base_version == OPA_MGMT_BASE_VERSION)
85262306a36Sopenharmony_ci		mad_size = sizeof(struct opa_mad);
85362306a36Sopenharmony_ci	else
85462306a36Sopenharmony_ci		mad_size = sizeof(struct ib_mad);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	pad = get_pad_size(hdr_len, data_len, mad_size);
85762306a36Sopenharmony_ci	message_size = hdr_len + data_len + pad;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (ib_mad_kernel_rmpp_agent(mad_agent)) {
86062306a36Sopenharmony_ci		if (!rmpp_active && message_size > mad_size)
86162306a36Sopenharmony_ci			return ERR_PTR(-EINVAL);
86262306a36Sopenharmony_ci	} else
86362306a36Sopenharmony_ci		if (rmpp_active || message_size > mad_size)
86462306a36Sopenharmony_ci			return ERR_PTR(-EINVAL);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	size = rmpp_active ? hdr_len : mad_size;
86762306a36Sopenharmony_ci	buf = kzalloc(sizeof *mad_send_wr + size, gfp_mask);
86862306a36Sopenharmony_ci	if (!buf)
86962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	mad_send_wr = buf + size;
87262306a36Sopenharmony_ci	INIT_LIST_HEAD(&mad_send_wr->rmpp_list);
87362306a36Sopenharmony_ci	mad_send_wr->send_buf.mad = buf;
87462306a36Sopenharmony_ci	mad_send_wr->send_buf.hdr_len = hdr_len;
87562306a36Sopenharmony_ci	mad_send_wr->send_buf.data_len = data_len;
87662306a36Sopenharmony_ci	mad_send_wr->pad = pad;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	mad_send_wr->mad_agent_priv = mad_agent_priv;
87962306a36Sopenharmony_ci	mad_send_wr->sg_list[0].length = hdr_len;
88062306a36Sopenharmony_ci	mad_send_wr->sg_list[0].lkey = mad_agent->qp->pd->local_dma_lkey;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	/* OPA MADs don't have to be the full 2048 bytes */
88362306a36Sopenharmony_ci	if (opa && base_version == OPA_MGMT_BASE_VERSION &&
88462306a36Sopenharmony_ci	    data_len < mad_size - hdr_len)
88562306a36Sopenharmony_ci		mad_send_wr->sg_list[1].length = data_len;
88662306a36Sopenharmony_ci	else
88762306a36Sopenharmony_ci		mad_send_wr->sg_list[1].length = mad_size - hdr_len;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	mad_send_wr->sg_list[1].lkey = mad_agent->qp->pd->local_dma_lkey;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	mad_send_wr->mad_list.cqe.done = ib_mad_send_done;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	mad_send_wr->send_wr.wr.wr_cqe = &mad_send_wr->mad_list.cqe;
89462306a36Sopenharmony_ci	mad_send_wr->send_wr.wr.sg_list = mad_send_wr->sg_list;
89562306a36Sopenharmony_ci	mad_send_wr->send_wr.wr.num_sge = 2;
89662306a36Sopenharmony_ci	mad_send_wr->send_wr.wr.opcode = IB_WR_SEND;
89762306a36Sopenharmony_ci	mad_send_wr->send_wr.wr.send_flags = IB_SEND_SIGNALED;
89862306a36Sopenharmony_ci	mad_send_wr->send_wr.remote_qpn = remote_qpn;
89962306a36Sopenharmony_ci	mad_send_wr->send_wr.remote_qkey = IB_QP_SET_QKEY;
90062306a36Sopenharmony_ci	mad_send_wr->send_wr.pkey_index = pkey_index;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	if (rmpp_active) {
90362306a36Sopenharmony_ci		ret = alloc_send_rmpp_list(mad_send_wr, mad_size, gfp_mask);
90462306a36Sopenharmony_ci		if (ret) {
90562306a36Sopenharmony_ci			kfree(buf);
90662306a36Sopenharmony_ci			return ERR_PTR(ret);
90762306a36Sopenharmony_ci		}
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	mad_send_wr->send_buf.mad_agent = mad_agent;
91162306a36Sopenharmony_ci	refcount_inc(&mad_agent_priv->refcount);
91262306a36Sopenharmony_ci	return &mad_send_wr->send_buf;
91362306a36Sopenharmony_ci}
91462306a36Sopenharmony_ciEXPORT_SYMBOL(ib_create_send_mad);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ciint ib_get_mad_data_offset(u8 mgmt_class)
91762306a36Sopenharmony_ci{
91862306a36Sopenharmony_ci	if (mgmt_class == IB_MGMT_CLASS_SUBN_ADM)
91962306a36Sopenharmony_ci		return IB_MGMT_SA_HDR;
92062306a36Sopenharmony_ci	else if ((mgmt_class == IB_MGMT_CLASS_DEVICE_MGMT) ||
92162306a36Sopenharmony_ci		 (mgmt_class == IB_MGMT_CLASS_DEVICE_ADM) ||
92262306a36Sopenharmony_ci		 (mgmt_class == IB_MGMT_CLASS_BIS))
92362306a36Sopenharmony_ci		return IB_MGMT_DEVICE_HDR;
92462306a36Sopenharmony_ci	else if ((mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START) &&
92562306a36Sopenharmony_ci		 (mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END))
92662306a36Sopenharmony_ci		return IB_MGMT_VENDOR_HDR;
92762306a36Sopenharmony_ci	else
92862306a36Sopenharmony_ci		return IB_MGMT_MAD_HDR;
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ciEXPORT_SYMBOL(ib_get_mad_data_offset);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ciint ib_is_mad_class_rmpp(u8 mgmt_class)
93362306a36Sopenharmony_ci{
93462306a36Sopenharmony_ci	if ((mgmt_class == IB_MGMT_CLASS_SUBN_ADM) ||
93562306a36Sopenharmony_ci	    (mgmt_class == IB_MGMT_CLASS_DEVICE_MGMT) ||
93662306a36Sopenharmony_ci	    (mgmt_class == IB_MGMT_CLASS_DEVICE_ADM) ||
93762306a36Sopenharmony_ci	    (mgmt_class == IB_MGMT_CLASS_BIS) ||
93862306a36Sopenharmony_ci	    ((mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START) &&
93962306a36Sopenharmony_ci	     (mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END)))
94062306a36Sopenharmony_ci		return 1;
94162306a36Sopenharmony_ci	return 0;
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ciEXPORT_SYMBOL(ib_is_mad_class_rmpp);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_civoid *ib_get_rmpp_segment(struct ib_mad_send_buf *send_buf, int seg_num)
94662306a36Sopenharmony_ci{
94762306a36Sopenharmony_ci	struct ib_mad_send_wr_private *mad_send_wr;
94862306a36Sopenharmony_ci	struct list_head *list;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	mad_send_wr = container_of(send_buf, struct ib_mad_send_wr_private,
95162306a36Sopenharmony_ci				   send_buf);
95262306a36Sopenharmony_ci	list = &mad_send_wr->cur_seg->list;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	if (mad_send_wr->cur_seg->num < seg_num) {
95562306a36Sopenharmony_ci		list_for_each_entry(mad_send_wr->cur_seg, list, list)
95662306a36Sopenharmony_ci			if (mad_send_wr->cur_seg->num == seg_num)
95762306a36Sopenharmony_ci				break;
95862306a36Sopenharmony_ci	} else if (mad_send_wr->cur_seg->num > seg_num) {
95962306a36Sopenharmony_ci		list_for_each_entry_reverse(mad_send_wr->cur_seg, list, list)
96062306a36Sopenharmony_ci			if (mad_send_wr->cur_seg->num == seg_num)
96162306a36Sopenharmony_ci				break;
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci	return mad_send_wr->cur_seg->data;
96462306a36Sopenharmony_ci}
96562306a36Sopenharmony_ciEXPORT_SYMBOL(ib_get_rmpp_segment);
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_cistatic inline void *ib_get_payload(struct ib_mad_send_wr_private *mad_send_wr)
96862306a36Sopenharmony_ci{
96962306a36Sopenharmony_ci	if (mad_send_wr->send_buf.seg_count)
97062306a36Sopenharmony_ci		return ib_get_rmpp_segment(&mad_send_wr->send_buf,
97162306a36Sopenharmony_ci					   mad_send_wr->seg_num);
97262306a36Sopenharmony_ci	else
97362306a36Sopenharmony_ci		return mad_send_wr->send_buf.mad +
97462306a36Sopenharmony_ci		       mad_send_wr->send_buf.hdr_len;
97562306a36Sopenharmony_ci}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_civoid ib_free_send_mad(struct ib_mad_send_buf *send_buf)
97862306a36Sopenharmony_ci{
97962306a36Sopenharmony_ci	struct ib_mad_agent_private *mad_agent_priv;
98062306a36Sopenharmony_ci	struct ib_mad_send_wr_private *mad_send_wr;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	mad_agent_priv = container_of(send_buf->mad_agent,
98362306a36Sopenharmony_ci				      struct ib_mad_agent_private, agent);
98462306a36Sopenharmony_ci	mad_send_wr = container_of(send_buf, struct ib_mad_send_wr_private,
98562306a36Sopenharmony_ci				   send_buf);
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	free_send_rmpp_list(mad_send_wr);
98862306a36Sopenharmony_ci	kfree(send_buf->mad);
98962306a36Sopenharmony_ci	deref_mad_agent(mad_agent_priv);
99062306a36Sopenharmony_ci}
99162306a36Sopenharmony_ciEXPORT_SYMBOL(ib_free_send_mad);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ciint ib_send_mad(struct ib_mad_send_wr_private *mad_send_wr)
99462306a36Sopenharmony_ci{
99562306a36Sopenharmony_ci	struct ib_mad_qp_info *qp_info;
99662306a36Sopenharmony_ci	struct list_head *list;
99762306a36Sopenharmony_ci	struct ib_mad_agent *mad_agent;
99862306a36Sopenharmony_ci	struct ib_sge *sge;
99962306a36Sopenharmony_ci	unsigned long flags;
100062306a36Sopenharmony_ci	int ret;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	/* Set WR ID to find mad_send_wr upon completion */
100362306a36Sopenharmony_ci	qp_info = mad_send_wr->mad_agent_priv->qp_info;
100462306a36Sopenharmony_ci	mad_send_wr->mad_list.mad_queue = &qp_info->send_queue;
100562306a36Sopenharmony_ci	mad_send_wr->mad_list.cqe.done = ib_mad_send_done;
100662306a36Sopenharmony_ci	mad_send_wr->send_wr.wr.wr_cqe = &mad_send_wr->mad_list.cqe;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	mad_agent = mad_send_wr->send_buf.mad_agent;
100962306a36Sopenharmony_ci	sge = mad_send_wr->sg_list;
101062306a36Sopenharmony_ci	sge[0].addr = ib_dma_map_single(mad_agent->device,
101162306a36Sopenharmony_ci					mad_send_wr->send_buf.mad,
101262306a36Sopenharmony_ci					sge[0].length,
101362306a36Sopenharmony_ci					DMA_TO_DEVICE);
101462306a36Sopenharmony_ci	if (unlikely(ib_dma_mapping_error(mad_agent->device, sge[0].addr)))
101562306a36Sopenharmony_ci		return -ENOMEM;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	mad_send_wr->header_mapping = sge[0].addr;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	sge[1].addr = ib_dma_map_single(mad_agent->device,
102062306a36Sopenharmony_ci					ib_get_payload(mad_send_wr),
102162306a36Sopenharmony_ci					sge[1].length,
102262306a36Sopenharmony_ci					DMA_TO_DEVICE);
102362306a36Sopenharmony_ci	if (unlikely(ib_dma_mapping_error(mad_agent->device, sge[1].addr))) {
102462306a36Sopenharmony_ci		ib_dma_unmap_single(mad_agent->device,
102562306a36Sopenharmony_ci				    mad_send_wr->header_mapping,
102662306a36Sopenharmony_ci				    sge[0].length, DMA_TO_DEVICE);
102762306a36Sopenharmony_ci		return -ENOMEM;
102862306a36Sopenharmony_ci	}
102962306a36Sopenharmony_ci	mad_send_wr->payload_mapping = sge[1].addr;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	spin_lock_irqsave(&qp_info->send_queue.lock, flags);
103262306a36Sopenharmony_ci	if (qp_info->send_queue.count < qp_info->send_queue.max_active) {
103362306a36Sopenharmony_ci		trace_ib_mad_ib_send_mad(mad_send_wr, qp_info);
103462306a36Sopenharmony_ci		ret = ib_post_send(mad_agent->qp, &mad_send_wr->send_wr.wr,
103562306a36Sopenharmony_ci				   NULL);
103662306a36Sopenharmony_ci		list = &qp_info->send_queue.list;
103762306a36Sopenharmony_ci	} else {
103862306a36Sopenharmony_ci		ret = 0;
103962306a36Sopenharmony_ci		list = &qp_info->overflow_list;
104062306a36Sopenharmony_ci	}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	if (!ret) {
104362306a36Sopenharmony_ci		qp_info->send_queue.count++;
104462306a36Sopenharmony_ci		list_add_tail(&mad_send_wr->mad_list.list, list);
104562306a36Sopenharmony_ci	}
104662306a36Sopenharmony_ci	spin_unlock_irqrestore(&qp_info->send_queue.lock, flags);
104762306a36Sopenharmony_ci	if (ret) {
104862306a36Sopenharmony_ci		ib_dma_unmap_single(mad_agent->device,
104962306a36Sopenharmony_ci				    mad_send_wr->header_mapping,
105062306a36Sopenharmony_ci				    sge[0].length, DMA_TO_DEVICE);
105162306a36Sopenharmony_ci		ib_dma_unmap_single(mad_agent->device,
105262306a36Sopenharmony_ci				    mad_send_wr->payload_mapping,
105362306a36Sopenharmony_ci				    sge[1].length, DMA_TO_DEVICE);
105462306a36Sopenharmony_ci	}
105562306a36Sopenharmony_ci	return ret;
105662306a36Sopenharmony_ci}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci/*
105962306a36Sopenharmony_ci * ib_post_send_mad - Posts MAD(s) to the send queue of the QP associated
106062306a36Sopenharmony_ci *  with the registered client
106162306a36Sopenharmony_ci */
106262306a36Sopenharmony_ciint ib_post_send_mad(struct ib_mad_send_buf *send_buf,
106362306a36Sopenharmony_ci		     struct ib_mad_send_buf **bad_send_buf)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	struct ib_mad_agent_private *mad_agent_priv;
106662306a36Sopenharmony_ci	struct ib_mad_send_buf *next_send_buf;
106762306a36Sopenharmony_ci	struct ib_mad_send_wr_private *mad_send_wr;
106862306a36Sopenharmony_ci	unsigned long flags;
106962306a36Sopenharmony_ci	int ret = -EINVAL;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	/* Walk list of send WRs and post each on send list */
107262306a36Sopenharmony_ci	for (; send_buf; send_buf = next_send_buf) {
107362306a36Sopenharmony_ci		mad_send_wr = container_of(send_buf,
107462306a36Sopenharmony_ci					   struct ib_mad_send_wr_private,
107562306a36Sopenharmony_ci					   send_buf);
107662306a36Sopenharmony_ci		mad_agent_priv = mad_send_wr->mad_agent_priv;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci		ret = ib_mad_enforce_security(mad_agent_priv,
107962306a36Sopenharmony_ci					      mad_send_wr->send_wr.pkey_index);
108062306a36Sopenharmony_ci		if (ret)
108162306a36Sopenharmony_ci			goto error;
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci		if (!send_buf->mad_agent->send_handler ||
108462306a36Sopenharmony_ci		    (send_buf->timeout_ms &&
108562306a36Sopenharmony_ci		     !send_buf->mad_agent->recv_handler)) {
108662306a36Sopenharmony_ci			ret = -EINVAL;
108762306a36Sopenharmony_ci			goto error;
108862306a36Sopenharmony_ci		}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci		if (!ib_is_mad_class_rmpp(((struct ib_mad_hdr *) send_buf->mad)->mgmt_class)) {
109162306a36Sopenharmony_ci			if (mad_agent_priv->agent.rmpp_version) {
109262306a36Sopenharmony_ci				ret = -EINVAL;
109362306a36Sopenharmony_ci				goto error;
109462306a36Sopenharmony_ci			}
109562306a36Sopenharmony_ci		}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci		/*
109862306a36Sopenharmony_ci		 * Save pointer to next work request to post in case the
109962306a36Sopenharmony_ci		 * current one completes, and the user modifies the work
110062306a36Sopenharmony_ci		 * request associated with the completion
110162306a36Sopenharmony_ci		 */
110262306a36Sopenharmony_ci		next_send_buf = send_buf->next;
110362306a36Sopenharmony_ci		mad_send_wr->send_wr.ah = send_buf->ah;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci		if (((struct ib_mad_hdr *) send_buf->mad)->mgmt_class ==
110662306a36Sopenharmony_ci		    IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
110762306a36Sopenharmony_ci			ret = handle_outgoing_dr_smp(mad_agent_priv,
110862306a36Sopenharmony_ci						     mad_send_wr);
110962306a36Sopenharmony_ci			if (ret < 0)		/* error */
111062306a36Sopenharmony_ci				goto error;
111162306a36Sopenharmony_ci			else if (ret == 1)	/* locally consumed */
111262306a36Sopenharmony_ci				continue;
111362306a36Sopenharmony_ci		}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci		mad_send_wr->tid = ((struct ib_mad_hdr *) send_buf->mad)->tid;
111662306a36Sopenharmony_ci		/* Timeout will be updated after send completes */
111762306a36Sopenharmony_ci		mad_send_wr->timeout = msecs_to_jiffies(send_buf->timeout_ms);
111862306a36Sopenharmony_ci		mad_send_wr->max_retries = send_buf->retries;
111962306a36Sopenharmony_ci		mad_send_wr->retries_left = send_buf->retries;
112062306a36Sopenharmony_ci		send_buf->retries = 0;
112162306a36Sopenharmony_ci		/* Reference for work request to QP + response */
112262306a36Sopenharmony_ci		mad_send_wr->refcount = 1 + (mad_send_wr->timeout > 0);
112362306a36Sopenharmony_ci		mad_send_wr->status = IB_WC_SUCCESS;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci		/* Reference MAD agent until send completes */
112662306a36Sopenharmony_ci		refcount_inc(&mad_agent_priv->refcount);
112762306a36Sopenharmony_ci		spin_lock_irqsave(&mad_agent_priv->lock, flags);
112862306a36Sopenharmony_ci		list_add_tail(&mad_send_wr->agent_list,
112962306a36Sopenharmony_ci			      &mad_agent_priv->send_list);
113062306a36Sopenharmony_ci		spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci		if (ib_mad_kernel_rmpp_agent(&mad_agent_priv->agent)) {
113362306a36Sopenharmony_ci			ret = ib_send_rmpp_mad(mad_send_wr);
113462306a36Sopenharmony_ci			if (ret >= 0 && ret != IB_RMPP_RESULT_CONSUMED)
113562306a36Sopenharmony_ci				ret = ib_send_mad(mad_send_wr);
113662306a36Sopenharmony_ci		} else
113762306a36Sopenharmony_ci			ret = ib_send_mad(mad_send_wr);
113862306a36Sopenharmony_ci		if (ret < 0) {
113962306a36Sopenharmony_ci			/* Fail send request */
114062306a36Sopenharmony_ci			spin_lock_irqsave(&mad_agent_priv->lock, flags);
114162306a36Sopenharmony_ci			list_del(&mad_send_wr->agent_list);
114262306a36Sopenharmony_ci			spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
114362306a36Sopenharmony_ci			deref_mad_agent(mad_agent_priv);
114462306a36Sopenharmony_ci			goto error;
114562306a36Sopenharmony_ci		}
114662306a36Sopenharmony_ci	}
114762306a36Sopenharmony_ci	return 0;
114862306a36Sopenharmony_cierror:
114962306a36Sopenharmony_ci	if (bad_send_buf)
115062306a36Sopenharmony_ci		*bad_send_buf = send_buf;
115162306a36Sopenharmony_ci	return ret;
115262306a36Sopenharmony_ci}
115362306a36Sopenharmony_ciEXPORT_SYMBOL(ib_post_send_mad);
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci/*
115662306a36Sopenharmony_ci * ib_free_recv_mad - Returns data buffers used to receive
115762306a36Sopenharmony_ci *  a MAD to the access layer
115862306a36Sopenharmony_ci */
115962306a36Sopenharmony_civoid ib_free_recv_mad(struct ib_mad_recv_wc *mad_recv_wc)
116062306a36Sopenharmony_ci{
116162306a36Sopenharmony_ci	struct ib_mad_recv_buf *mad_recv_buf, *temp_recv_buf;
116262306a36Sopenharmony_ci	struct ib_mad_private_header *mad_priv_hdr;
116362306a36Sopenharmony_ci	struct ib_mad_private *priv;
116462306a36Sopenharmony_ci	struct list_head free_list;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	INIT_LIST_HEAD(&free_list);
116762306a36Sopenharmony_ci	list_splice_init(&mad_recv_wc->rmpp_list, &free_list);
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	list_for_each_entry_safe(mad_recv_buf, temp_recv_buf,
117062306a36Sopenharmony_ci					&free_list, list) {
117162306a36Sopenharmony_ci		mad_recv_wc = container_of(mad_recv_buf, struct ib_mad_recv_wc,
117262306a36Sopenharmony_ci					   recv_buf);
117362306a36Sopenharmony_ci		mad_priv_hdr = container_of(mad_recv_wc,
117462306a36Sopenharmony_ci					    struct ib_mad_private_header,
117562306a36Sopenharmony_ci					    recv_wc);
117662306a36Sopenharmony_ci		priv = container_of(mad_priv_hdr, struct ib_mad_private,
117762306a36Sopenharmony_ci				    header);
117862306a36Sopenharmony_ci		kfree(priv);
117962306a36Sopenharmony_ci	}
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ciEXPORT_SYMBOL(ib_free_recv_mad);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cistatic int method_in_use(struct ib_mad_mgmt_method_table **method,
118462306a36Sopenharmony_ci			 struct ib_mad_reg_req *mad_reg_req)
118562306a36Sopenharmony_ci{
118662306a36Sopenharmony_ci	int i;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	for_each_set_bit(i, mad_reg_req->method_mask, IB_MGMT_MAX_METHODS) {
118962306a36Sopenharmony_ci		if ((*method)->agent[i]) {
119062306a36Sopenharmony_ci			pr_err("Method %d already in use\n", i);
119162306a36Sopenharmony_ci			return -EINVAL;
119262306a36Sopenharmony_ci		}
119362306a36Sopenharmony_ci	}
119462306a36Sopenharmony_ci	return 0;
119562306a36Sopenharmony_ci}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_cistatic int allocate_method_table(struct ib_mad_mgmt_method_table **method)
119862306a36Sopenharmony_ci{
119962306a36Sopenharmony_ci	/* Allocate management method table */
120062306a36Sopenharmony_ci	*method = kzalloc(sizeof **method, GFP_ATOMIC);
120162306a36Sopenharmony_ci	return (*method) ? 0 : (-ENOMEM);
120262306a36Sopenharmony_ci}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci/*
120562306a36Sopenharmony_ci * Check to see if there are any methods still in use
120662306a36Sopenharmony_ci */
120762306a36Sopenharmony_cistatic int check_method_table(struct ib_mad_mgmt_method_table *method)
120862306a36Sopenharmony_ci{
120962306a36Sopenharmony_ci	int i;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	for (i = 0; i < IB_MGMT_MAX_METHODS; i++)
121262306a36Sopenharmony_ci		if (method->agent[i])
121362306a36Sopenharmony_ci			return 1;
121462306a36Sopenharmony_ci	return 0;
121562306a36Sopenharmony_ci}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci/*
121862306a36Sopenharmony_ci * Check to see if there are any method tables for this class still in use
121962306a36Sopenharmony_ci */
122062306a36Sopenharmony_cistatic int check_class_table(struct ib_mad_mgmt_class_table *class)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	int i;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	for (i = 0; i < MAX_MGMT_CLASS; i++)
122562306a36Sopenharmony_ci		if (class->method_table[i])
122662306a36Sopenharmony_ci			return 1;
122762306a36Sopenharmony_ci	return 0;
122862306a36Sopenharmony_ci}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_cistatic int check_vendor_class(struct ib_mad_mgmt_vendor_class *vendor_class)
123162306a36Sopenharmony_ci{
123262306a36Sopenharmony_ci	int i;
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	for (i = 0; i < MAX_MGMT_OUI; i++)
123562306a36Sopenharmony_ci		if (vendor_class->method_table[i])
123662306a36Sopenharmony_ci			return 1;
123762306a36Sopenharmony_ci	return 0;
123862306a36Sopenharmony_ci}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cistatic int find_vendor_oui(struct ib_mad_mgmt_vendor_class *vendor_class,
124162306a36Sopenharmony_ci			   const char *oui)
124262306a36Sopenharmony_ci{
124362306a36Sopenharmony_ci	int i;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	for (i = 0; i < MAX_MGMT_OUI; i++)
124662306a36Sopenharmony_ci		/* Is there matching OUI for this vendor class ? */
124762306a36Sopenharmony_ci		if (!memcmp(vendor_class->oui[i], oui, 3))
124862306a36Sopenharmony_ci			return i;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	return -1;
125162306a36Sopenharmony_ci}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_cistatic int check_vendor_table(struct ib_mad_mgmt_vendor_class_table *vendor)
125462306a36Sopenharmony_ci{
125562306a36Sopenharmony_ci	int i;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	for (i = 0; i < MAX_MGMT_VENDOR_RANGE2; i++)
125862306a36Sopenharmony_ci		if (vendor->vendor_class[i])
125962306a36Sopenharmony_ci			return 1;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	return 0;
126262306a36Sopenharmony_ci}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_cistatic void remove_methods_mad_agent(struct ib_mad_mgmt_method_table *method,
126562306a36Sopenharmony_ci				     struct ib_mad_agent_private *agent)
126662306a36Sopenharmony_ci{
126762306a36Sopenharmony_ci	int i;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	/* Remove any methods for this mad agent */
127062306a36Sopenharmony_ci	for (i = 0; i < IB_MGMT_MAX_METHODS; i++)
127162306a36Sopenharmony_ci		if (method->agent[i] == agent)
127262306a36Sopenharmony_ci			method->agent[i] = NULL;
127362306a36Sopenharmony_ci}
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_cistatic int add_nonoui_reg_req(struct ib_mad_reg_req *mad_reg_req,
127662306a36Sopenharmony_ci			      struct ib_mad_agent_private *agent_priv,
127762306a36Sopenharmony_ci			      u8 mgmt_class)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	struct ib_mad_port_private *port_priv;
128062306a36Sopenharmony_ci	struct ib_mad_mgmt_class_table **class;
128162306a36Sopenharmony_ci	struct ib_mad_mgmt_method_table **method;
128262306a36Sopenharmony_ci	int i, ret;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	port_priv = agent_priv->qp_info->port_priv;
128562306a36Sopenharmony_ci	class = &port_priv->version[mad_reg_req->mgmt_class_version].class;
128662306a36Sopenharmony_ci	if (!*class) {
128762306a36Sopenharmony_ci		/* Allocate management class table for "new" class version */
128862306a36Sopenharmony_ci		*class = kzalloc(sizeof **class, GFP_ATOMIC);
128962306a36Sopenharmony_ci		if (!*class) {
129062306a36Sopenharmony_ci			ret = -ENOMEM;
129162306a36Sopenharmony_ci			goto error1;
129262306a36Sopenharmony_ci		}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci		/* Allocate method table for this management class */
129562306a36Sopenharmony_ci		method = &(*class)->method_table[mgmt_class];
129662306a36Sopenharmony_ci		if ((ret = allocate_method_table(method)))
129762306a36Sopenharmony_ci			goto error2;
129862306a36Sopenharmony_ci	} else {
129962306a36Sopenharmony_ci		method = &(*class)->method_table[mgmt_class];
130062306a36Sopenharmony_ci		if (!*method) {
130162306a36Sopenharmony_ci			/* Allocate method table for this management class */
130262306a36Sopenharmony_ci			if ((ret = allocate_method_table(method)))
130362306a36Sopenharmony_ci				goto error1;
130462306a36Sopenharmony_ci		}
130562306a36Sopenharmony_ci	}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	/* Now, make sure methods are not already in use */
130862306a36Sopenharmony_ci	if (method_in_use(method, mad_reg_req))
130962306a36Sopenharmony_ci		goto error3;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	/* Finally, add in methods being registered */
131262306a36Sopenharmony_ci	for_each_set_bit(i, mad_reg_req->method_mask, IB_MGMT_MAX_METHODS)
131362306a36Sopenharmony_ci		(*method)->agent[i] = agent_priv;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	return 0;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_cierror3:
131862306a36Sopenharmony_ci	/* Remove any methods for this mad agent */
131962306a36Sopenharmony_ci	remove_methods_mad_agent(*method, agent_priv);
132062306a36Sopenharmony_ci	/* Now, check to see if there are any methods in use */
132162306a36Sopenharmony_ci	if (!check_method_table(*method)) {
132262306a36Sopenharmony_ci		/* If not, release management method table */
132362306a36Sopenharmony_ci		kfree(*method);
132462306a36Sopenharmony_ci		*method = NULL;
132562306a36Sopenharmony_ci	}
132662306a36Sopenharmony_ci	ret = -EINVAL;
132762306a36Sopenharmony_ci	goto error1;
132862306a36Sopenharmony_cierror2:
132962306a36Sopenharmony_ci	kfree(*class);
133062306a36Sopenharmony_ci	*class = NULL;
133162306a36Sopenharmony_cierror1:
133262306a36Sopenharmony_ci	return ret;
133362306a36Sopenharmony_ci}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_cistatic int add_oui_reg_req(struct ib_mad_reg_req *mad_reg_req,
133662306a36Sopenharmony_ci			   struct ib_mad_agent_private *agent_priv)
133762306a36Sopenharmony_ci{
133862306a36Sopenharmony_ci	struct ib_mad_port_private *port_priv;
133962306a36Sopenharmony_ci	struct ib_mad_mgmt_vendor_class_table **vendor_table;
134062306a36Sopenharmony_ci	struct ib_mad_mgmt_vendor_class_table *vendor = NULL;
134162306a36Sopenharmony_ci	struct ib_mad_mgmt_vendor_class *vendor_class = NULL;
134262306a36Sopenharmony_ci	struct ib_mad_mgmt_method_table **method;
134362306a36Sopenharmony_ci	int i, ret = -ENOMEM;
134462306a36Sopenharmony_ci	u8 vclass;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	/* "New" vendor (with OUI) class */
134762306a36Sopenharmony_ci	vclass = vendor_class_index(mad_reg_req->mgmt_class);
134862306a36Sopenharmony_ci	port_priv = agent_priv->qp_info->port_priv;
134962306a36Sopenharmony_ci	vendor_table = &port_priv->version[
135062306a36Sopenharmony_ci				mad_reg_req->mgmt_class_version].vendor;
135162306a36Sopenharmony_ci	if (!*vendor_table) {
135262306a36Sopenharmony_ci		/* Allocate mgmt vendor class table for "new" class version */
135362306a36Sopenharmony_ci		vendor = kzalloc(sizeof *vendor, GFP_ATOMIC);
135462306a36Sopenharmony_ci		if (!vendor)
135562306a36Sopenharmony_ci			goto error1;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci		*vendor_table = vendor;
135862306a36Sopenharmony_ci	}
135962306a36Sopenharmony_ci	if (!(*vendor_table)->vendor_class[vclass]) {
136062306a36Sopenharmony_ci		/* Allocate table for this management vendor class */
136162306a36Sopenharmony_ci		vendor_class = kzalloc(sizeof *vendor_class, GFP_ATOMIC);
136262306a36Sopenharmony_ci		if (!vendor_class)
136362306a36Sopenharmony_ci			goto error2;
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci		(*vendor_table)->vendor_class[vclass] = vendor_class;
136662306a36Sopenharmony_ci	}
136762306a36Sopenharmony_ci	for (i = 0; i < MAX_MGMT_OUI; i++) {
136862306a36Sopenharmony_ci		/* Is there matching OUI for this vendor class ? */
136962306a36Sopenharmony_ci		if (!memcmp((*vendor_table)->vendor_class[vclass]->oui[i],
137062306a36Sopenharmony_ci			    mad_reg_req->oui, 3)) {
137162306a36Sopenharmony_ci			method = &(*vendor_table)->vendor_class[
137262306a36Sopenharmony_ci						vclass]->method_table[i];
137362306a36Sopenharmony_ci			if (!*method)
137462306a36Sopenharmony_ci				goto error3;
137562306a36Sopenharmony_ci			goto check_in_use;
137662306a36Sopenharmony_ci		}
137762306a36Sopenharmony_ci	}
137862306a36Sopenharmony_ci	for (i = 0; i < MAX_MGMT_OUI; i++) {
137962306a36Sopenharmony_ci		/* OUI slot available ? */
138062306a36Sopenharmony_ci		if (!is_vendor_oui((*vendor_table)->vendor_class[
138162306a36Sopenharmony_ci				vclass]->oui[i])) {
138262306a36Sopenharmony_ci			method = &(*vendor_table)->vendor_class[
138362306a36Sopenharmony_ci				vclass]->method_table[i];
138462306a36Sopenharmony_ci			/* Allocate method table for this OUI */
138562306a36Sopenharmony_ci			if (!*method) {
138662306a36Sopenharmony_ci				ret = allocate_method_table(method);
138762306a36Sopenharmony_ci				if (ret)
138862306a36Sopenharmony_ci					goto error3;
138962306a36Sopenharmony_ci			}
139062306a36Sopenharmony_ci			memcpy((*vendor_table)->vendor_class[vclass]->oui[i],
139162306a36Sopenharmony_ci			       mad_reg_req->oui, 3);
139262306a36Sopenharmony_ci			goto check_in_use;
139362306a36Sopenharmony_ci		}
139462306a36Sopenharmony_ci	}
139562306a36Sopenharmony_ci	dev_err(&agent_priv->agent.device->dev, "All OUI slots in use\n");
139662306a36Sopenharmony_ci	goto error3;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_cicheck_in_use:
139962306a36Sopenharmony_ci	/* Now, make sure methods are not already in use */
140062306a36Sopenharmony_ci	if (method_in_use(method, mad_reg_req))
140162306a36Sopenharmony_ci		goto error4;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	/* Finally, add in methods being registered */
140462306a36Sopenharmony_ci	for_each_set_bit(i, mad_reg_req->method_mask, IB_MGMT_MAX_METHODS)
140562306a36Sopenharmony_ci		(*method)->agent[i] = agent_priv;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	return 0;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_cierror4:
141062306a36Sopenharmony_ci	/* Remove any methods for this mad agent */
141162306a36Sopenharmony_ci	remove_methods_mad_agent(*method, agent_priv);
141262306a36Sopenharmony_ci	/* Now, check to see if there are any methods in use */
141362306a36Sopenharmony_ci	if (!check_method_table(*method)) {
141462306a36Sopenharmony_ci		/* If not, release management method table */
141562306a36Sopenharmony_ci		kfree(*method);
141662306a36Sopenharmony_ci		*method = NULL;
141762306a36Sopenharmony_ci	}
141862306a36Sopenharmony_ci	ret = -EINVAL;
141962306a36Sopenharmony_cierror3:
142062306a36Sopenharmony_ci	if (vendor_class) {
142162306a36Sopenharmony_ci		(*vendor_table)->vendor_class[vclass] = NULL;
142262306a36Sopenharmony_ci		kfree(vendor_class);
142362306a36Sopenharmony_ci	}
142462306a36Sopenharmony_cierror2:
142562306a36Sopenharmony_ci	if (vendor) {
142662306a36Sopenharmony_ci		*vendor_table = NULL;
142762306a36Sopenharmony_ci		kfree(vendor);
142862306a36Sopenharmony_ci	}
142962306a36Sopenharmony_cierror1:
143062306a36Sopenharmony_ci	return ret;
143162306a36Sopenharmony_ci}
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_cistatic void remove_mad_reg_req(struct ib_mad_agent_private *agent_priv)
143462306a36Sopenharmony_ci{
143562306a36Sopenharmony_ci	struct ib_mad_port_private *port_priv;
143662306a36Sopenharmony_ci	struct ib_mad_mgmt_class_table *class;
143762306a36Sopenharmony_ci	struct ib_mad_mgmt_method_table *method;
143862306a36Sopenharmony_ci	struct ib_mad_mgmt_vendor_class_table *vendor;
143962306a36Sopenharmony_ci	struct ib_mad_mgmt_vendor_class *vendor_class;
144062306a36Sopenharmony_ci	int index;
144162306a36Sopenharmony_ci	u8 mgmt_class;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	/*
144462306a36Sopenharmony_ci	 * Was MAD registration request supplied
144562306a36Sopenharmony_ci	 * with original registration ?
144662306a36Sopenharmony_ci	 */
144762306a36Sopenharmony_ci	if (!agent_priv->reg_req)
144862306a36Sopenharmony_ci		goto out;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	port_priv = agent_priv->qp_info->port_priv;
145162306a36Sopenharmony_ci	mgmt_class = convert_mgmt_class(agent_priv->reg_req->mgmt_class);
145262306a36Sopenharmony_ci	class = port_priv->version[
145362306a36Sopenharmony_ci			agent_priv->reg_req->mgmt_class_version].class;
145462306a36Sopenharmony_ci	if (!class)
145562306a36Sopenharmony_ci		goto vendor_check;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	method = class->method_table[mgmt_class];
145862306a36Sopenharmony_ci	if (method) {
145962306a36Sopenharmony_ci		/* Remove any methods for this mad agent */
146062306a36Sopenharmony_ci		remove_methods_mad_agent(method, agent_priv);
146162306a36Sopenharmony_ci		/* Now, check to see if there are any methods still in use */
146262306a36Sopenharmony_ci		if (!check_method_table(method)) {
146362306a36Sopenharmony_ci			/* If not, release management method table */
146462306a36Sopenharmony_ci			kfree(method);
146562306a36Sopenharmony_ci			class->method_table[mgmt_class] = NULL;
146662306a36Sopenharmony_ci			/* Any management classes left ? */
146762306a36Sopenharmony_ci			if (!check_class_table(class)) {
146862306a36Sopenharmony_ci				/* If not, release management class table */
146962306a36Sopenharmony_ci				kfree(class);
147062306a36Sopenharmony_ci				port_priv->version[
147162306a36Sopenharmony_ci					agent_priv->reg_req->
147262306a36Sopenharmony_ci					mgmt_class_version].class = NULL;
147362306a36Sopenharmony_ci			}
147462306a36Sopenharmony_ci		}
147562306a36Sopenharmony_ci	}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_civendor_check:
147862306a36Sopenharmony_ci	if (!is_vendor_class(mgmt_class))
147962306a36Sopenharmony_ci		goto out;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	/* normalize mgmt_class to vendor range 2 */
148262306a36Sopenharmony_ci	mgmt_class = vendor_class_index(agent_priv->reg_req->mgmt_class);
148362306a36Sopenharmony_ci	vendor = port_priv->version[
148462306a36Sopenharmony_ci			agent_priv->reg_req->mgmt_class_version].vendor;
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	if (!vendor)
148762306a36Sopenharmony_ci		goto out;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	vendor_class = vendor->vendor_class[mgmt_class];
149062306a36Sopenharmony_ci	if (vendor_class) {
149162306a36Sopenharmony_ci		index = find_vendor_oui(vendor_class, agent_priv->reg_req->oui);
149262306a36Sopenharmony_ci		if (index < 0)
149362306a36Sopenharmony_ci			goto out;
149462306a36Sopenharmony_ci		method = vendor_class->method_table[index];
149562306a36Sopenharmony_ci		if (method) {
149662306a36Sopenharmony_ci			/* Remove any methods for this mad agent */
149762306a36Sopenharmony_ci			remove_methods_mad_agent(method, agent_priv);
149862306a36Sopenharmony_ci			/*
149962306a36Sopenharmony_ci			 * Now, check to see if there are
150062306a36Sopenharmony_ci			 * any methods still in use
150162306a36Sopenharmony_ci			 */
150262306a36Sopenharmony_ci			if (!check_method_table(method)) {
150362306a36Sopenharmony_ci				/* If not, release management method table */
150462306a36Sopenharmony_ci				kfree(method);
150562306a36Sopenharmony_ci				vendor_class->method_table[index] = NULL;
150662306a36Sopenharmony_ci				memset(vendor_class->oui[index], 0, 3);
150762306a36Sopenharmony_ci				/* Any OUIs left ? */
150862306a36Sopenharmony_ci				if (!check_vendor_class(vendor_class)) {
150962306a36Sopenharmony_ci					/* If not, release vendor class table */
151062306a36Sopenharmony_ci					kfree(vendor_class);
151162306a36Sopenharmony_ci					vendor->vendor_class[mgmt_class] = NULL;
151262306a36Sopenharmony_ci					/* Any other vendor classes left ? */
151362306a36Sopenharmony_ci					if (!check_vendor_table(vendor)) {
151462306a36Sopenharmony_ci						kfree(vendor);
151562306a36Sopenharmony_ci						port_priv->version[
151662306a36Sopenharmony_ci							agent_priv->reg_req->
151762306a36Sopenharmony_ci							mgmt_class_version].
151862306a36Sopenharmony_ci							vendor = NULL;
151962306a36Sopenharmony_ci					}
152062306a36Sopenharmony_ci				}
152162306a36Sopenharmony_ci			}
152262306a36Sopenharmony_ci		}
152362306a36Sopenharmony_ci	}
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ciout:
152662306a36Sopenharmony_ci	return;
152762306a36Sopenharmony_ci}
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_cistatic struct ib_mad_agent_private *
153062306a36Sopenharmony_cifind_mad_agent(struct ib_mad_port_private *port_priv,
153162306a36Sopenharmony_ci	       const struct ib_mad_hdr *mad_hdr)
153262306a36Sopenharmony_ci{
153362306a36Sopenharmony_ci	struct ib_mad_agent_private *mad_agent = NULL;
153462306a36Sopenharmony_ci	unsigned long flags;
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	if (ib_response_mad(mad_hdr)) {
153762306a36Sopenharmony_ci		u32 hi_tid;
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci		/*
154062306a36Sopenharmony_ci		 * Routing is based on high 32 bits of transaction ID
154162306a36Sopenharmony_ci		 * of MAD.
154262306a36Sopenharmony_ci		 */
154362306a36Sopenharmony_ci		hi_tid = be64_to_cpu(mad_hdr->tid) >> 32;
154462306a36Sopenharmony_ci		rcu_read_lock();
154562306a36Sopenharmony_ci		mad_agent = xa_load(&ib_mad_clients, hi_tid);
154662306a36Sopenharmony_ci		if (mad_agent && !refcount_inc_not_zero(&mad_agent->refcount))
154762306a36Sopenharmony_ci			mad_agent = NULL;
154862306a36Sopenharmony_ci		rcu_read_unlock();
154962306a36Sopenharmony_ci	} else {
155062306a36Sopenharmony_ci		struct ib_mad_mgmt_class_table *class;
155162306a36Sopenharmony_ci		struct ib_mad_mgmt_method_table *method;
155262306a36Sopenharmony_ci		struct ib_mad_mgmt_vendor_class_table *vendor;
155362306a36Sopenharmony_ci		struct ib_mad_mgmt_vendor_class *vendor_class;
155462306a36Sopenharmony_ci		const struct ib_vendor_mad *vendor_mad;
155562306a36Sopenharmony_ci		int index;
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci		spin_lock_irqsave(&port_priv->reg_lock, flags);
155862306a36Sopenharmony_ci		/*
155962306a36Sopenharmony_ci		 * Routing is based on version, class, and method
156062306a36Sopenharmony_ci		 * For "newer" vendor MADs, also based on OUI
156162306a36Sopenharmony_ci		 */
156262306a36Sopenharmony_ci		if (mad_hdr->class_version >= MAX_MGMT_VERSION)
156362306a36Sopenharmony_ci			goto out;
156462306a36Sopenharmony_ci		if (!is_vendor_class(mad_hdr->mgmt_class)) {
156562306a36Sopenharmony_ci			class = port_priv->version[
156662306a36Sopenharmony_ci					mad_hdr->class_version].class;
156762306a36Sopenharmony_ci			if (!class)
156862306a36Sopenharmony_ci				goto out;
156962306a36Sopenharmony_ci			if (convert_mgmt_class(mad_hdr->mgmt_class) >=
157062306a36Sopenharmony_ci			    ARRAY_SIZE(class->method_table))
157162306a36Sopenharmony_ci				goto out;
157262306a36Sopenharmony_ci			method = class->method_table[convert_mgmt_class(
157362306a36Sopenharmony_ci							mad_hdr->mgmt_class)];
157462306a36Sopenharmony_ci			if (method)
157562306a36Sopenharmony_ci				mad_agent = method->agent[mad_hdr->method &
157662306a36Sopenharmony_ci							  ~IB_MGMT_METHOD_RESP];
157762306a36Sopenharmony_ci		} else {
157862306a36Sopenharmony_ci			vendor = port_priv->version[
157962306a36Sopenharmony_ci					mad_hdr->class_version].vendor;
158062306a36Sopenharmony_ci			if (!vendor)
158162306a36Sopenharmony_ci				goto out;
158262306a36Sopenharmony_ci			vendor_class = vendor->vendor_class[vendor_class_index(
158362306a36Sopenharmony_ci						mad_hdr->mgmt_class)];
158462306a36Sopenharmony_ci			if (!vendor_class)
158562306a36Sopenharmony_ci				goto out;
158662306a36Sopenharmony_ci			/* Find matching OUI */
158762306a36Sopenharmony_ci			vendor_mad = (const struct ib_vendor_mad *)mad_hdr;
158862306a36Sopenharmony_ci			index = find_vendor_oui(vendor_class, vendor_mad->oui);
158962306a36Sopenharmony_ci			if (index == -1)
159062306a36Sopenharmony_ci				goto out;
159162306a36Sopenharmony_ci			method = vendor_class->method_table[index];
159262306a36Sopenharmony_ci			if (method) {
159362306a36Sopenharmony_ci				mad_agent = method->agent[mad_hdr->method &
159462306a36Sopenharmony_ci							  ~IB_MGMT_METHOD_RESP];
159562306a36Sopenharmony_ci			}
159662306a36Sopenharmony_ci		}
159762306a36Sopenharmony_ci		if (mad_agent)
159862306a36Sopenharmony_ci			refcount_inc(&mad_agent->refcount);
159962306a36Sopenharmony_ciout:
160062306a36Sopenharmony_ci		spin_unlock_irqrestore(&port_priv->reg_lock, flags);
160162306a36Sopenharmony_ci	}
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	if (mad_agent && !mad_agent->agent.recv_handler) {
160462306a36Sopenharmony_ci		dev_notice(&port_priv->device->dev,
160562306a36Sopenharmony_ci			   "No receive handler for client %p on port %u\n",
160662306a36Sopenharmony_ci			   &mad_agent->agent, port_priv->port_num);
160762306a36Sopenharmony_ci		deref_mad_agent(mad_agent);
160862306a36Sopenharmony_ci		mad_agent = NULL;
160962306a36Sopenharmony_ci	}
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	return mad_agent;
161262306a36Sopenharmony_ci}
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_cistatic int validate_mad(const struct ib_mad_hdr *mad_hdr,
161562306a36Sopenharmony_ci			const struct ib_mad_qp_info *qp_info,
161662306a36Sopenharmony_ci			bool opa)
161762306a36Sopenharmony_ci{
161862306a36Sopenharmony_ci	int valid = 0;
161962306a36Sopenharmony_ci	u32 qp_num = qp_info->qp->qp_num;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	/* Make sure MAD base version is understood */
162262306a36Sopenharmony_ci	if (mad_hdr->base_version != IB_MGMT_BASE_VERSION &&
162362306a36Sopenharmony_ci	    (!opa || mad_hdr->base_version != OPA_MGMT_BASE_VERSION)) {
162462306a36Sopenharmony_ci		pr_err("MAD received with unsupported base version %u %s\n",
162562306a36Sopenharmony_ci		       mad_hdr->base_version, opa ? "(opa)" : "");
162662306a36Sopenharmony_ci		goto out;
162762306a36Sopenharmony_ci	}
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	/* Filter SMI packets sent to other than QP0 */
163062306a36Sopenharmony_ci	if ((mad_hdr->mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED) ||
163162306a36Sopenharmony_ci	    (mad_hdr->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)) {
163262306a36Sopenharmony_ci		if (qp_num == 0)
163362306a36Sopenharmony_ci			valid = 1;
163462306a36Sopenharmony_ci	} else {
163562306a36Sopenharmony_ci		/* CM attributes other than ClassPortInfo only use Send method */
163662306a36Sopenharmony_ci		if ((mad_hdr->mgmt_class == IB_MGMT_CLASS_CM) &&
163762306a36Sopenharmony_ci		    (mad_hdr->attr_id != IB_MGMT_CLASSPORTINFO_ATTR_ID) &&
163862306a36Sopenharmony_ci		    (mad_hdr->method != IB_MGMT_METHOD_SEND))
163962306a36Sopenharmony_ci			goto out;
164062306a36Sopenharmony_ci		/* Filter GSI packets sent to QP0 */
164162306a36Sopenharmony_ci		if (qp_num != 0)
164262306a36Sopenharmony_ci			valid = 1;
164362306a36Sopenharmony_ci	}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ciout:
164662306a36Sopenharmony_ci	return valid;
164762306a36Sopenharmony_ci}
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_cistatic int is_rmpp_data_mad(const struct ib_mad_agent_private *mad_agent_priv,
165062306a36Sopenharmony_ci			    const struct ib_mad_hdr *mad_hdr)
165162306a36Sopenharmony_ci{
165262306a36Sopenharmony_ci	struct ib_rmpp_mad *rmpp_mad;
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	rmpp_mad = (struct ib_rmpp_mad *)mad_hdr;
165562306a36Sopenharmony_ci	return !mad_agent_priv->agent.rmpp_version ||
165662306a36Sopenharmony_ci		!ib_mad_kernel_rmpp_agent(&mad_agent_priv->agent) ||
165762306a36Sopenharmony_ci		!(ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) &
165862306a36Sopenharmony_ci				    IB_MGMT_RMPP_FLAG_ACTIVE) ||
165962306a36Sopenharmony_ci		(rmpp_mad->rmpp_hdr.rmpp_type == IB_MGMT_RMPP_TYPE_DATA);
166062306a36Sopenharmony_ci}
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_cistatic inline int rcv_has_same_class(const struct ib_mad_send_wr_private *wr,
166362306a36Sopenharmony_ci				     const struct ib_mad_recv_wc *rwc)
166462306a36Sopenharmony_ci{
166562306a36Sopenharmony_ci	return ((struct ib_mad_hdr *)(wr->send_buf.mad))->mgmt_class ==
166662306a36Sopenharmony_ci		rwc->recv_buf.mad->mad_hdr.mgmt_class;
166762306a36Sopenharmony_ci}
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_cistatic inline int
167062306a36Sopenharmony_circv_has_same_gid(const struct ib_mad_agent_private *mad_agent_priv,
167162306a36Sopenharmony_ci		 const struct ib_mad_send_wr_private *wr,
167262306a36Sopenharmony_ci		 const struct ib_mad_recv_wc *rwc)
167362306a36Sopenharmony_ci{
167462306a36Sopenharmony_ci	struct rdma_ah_attr attr;
167562306a36Sopenharmony_ci	u8 send_resp, rcv_resp;
167662306a36Sopenharmony_ci	union ib_gid sgid;
167762306a36Sopenharmony_ci	struct ib_device *device = mad_agent_priv->agent.device;
167862306a36Sopenharmony_ci	u32 port_num = mad_agent_priv->agent.port_num;
167962306a36Sopenharmony_ci	u8 lmc;
168062306a36Sopenharmony_ci	bool has_grh;
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	send_resp = ib_response_mad((struct ib_mad_hdr *)wr->send_buf.mad);
168362306a36Sopenharmony_ci	rcv_resp = ib_response_mad(&rwc->recv_buf.mad->mad_hdr);
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	if (send_resp == rcv_resp)
168662306a36Sopenharmony_ci		/* both requests, or both responses. GIDs different */
168762306a36Sopenharmony_ci		return 0;
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	if (rdma_query_ah(wr->send_buf.ah, &attr))
169062306a36Sopenharmony_ci		/* Assume not equal, to avoid false positives. */
169162306a36Sopenharmony_ci		return 0;
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	has_grh = !!(rdma_ah_get_ah_flags(&attr) & IB_AH_GRH);
169462306a36Sopenharmony_ci	if (has_grh != !!(rwc->wc->wc_flags & IB_WC_GRH))
169562306a36Sopenharmony_ci		/* one has GID, other does not.  Assume different */
169662306a36Sopenharmony_ci		return 0;
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	if (!send_resp && rcv_resp) {
169962306a36Sopenharmony_ci		/* is request/response. */
170062306a36Sopenharmony_ci		if (!has_grh) {
170162306a36Sopenharmony_ci			if (ib_get_cached_lmc(device, port_num, &lmc))
170262306a36Sopenharmony_ci				return 0;
170362306a36Sopenharmony_ci			return (!lmc || !((rdma_ah_get_path_bits(&attr) ^
170462306a36Sopenharmony_ci					   rwc->wc->dlid_path_bits) &
170562306a36Sopenharmony_ci					  ((1 << lmc) - 1)));
170662306a36Sopenharmony_ci		} else {
170762306a36Sopenharmony_ci			const struct ib_global_route *grh =
170862306a36Sopenharmony_ci					rdma_ah_read_grh(&attr);
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci			if (rdma_query_gid(device, port_num,
171162306a36Sopenharmony_ci					   grh->sgid_index, &sgid))
171262306a36Sopenharmony_ci				return 0;
171362306a36Sopenharmony_ci			return !memcmp(sgid.raw, rwc->recv_buf.grh->dgid.raw,
171462306a36Sopenharmony_ci				       16);
171562306a36Sopenharmony_ci		}
171662306a36Sopenharmony_ci	}
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	if (!has_grh)
171962306a36Sopenharmony_ci		return rdma_ah_get_dlid(&attr) == rwc->wc->slid;
172062306a36Sopenharmony_ci	else
172162306a36Sopenharmony_ci		return !memcmp(rdma_ah_read_grh(&attr)->dgid.raw,
172262306a36Sopenharmony_ci			       rwc->recv_buf.grh->sgid.raw,
172362306a36Sopenharmony_ci			       16);
172462306a36Sopenharmony_ci}
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_cistatic inline int is_direct(u8 class)
172762306a36Sopenharmony_ci{
172862306a36Sopenharmony_ci	return (class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE);
172962306a36Sopenharmony_ci}
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_cistruct ib_mad_send_wr_private*
173262306a36Sopenharmony_ciib_find_send_mad(const struct ib_mad_agent_private *mad_agent_priv,
173362306a36Sopenharmony_ci		 const struct ib_mad_recv_wc *wc)
173462306a36Sopenharmony_ci{
173562306a36Sopenharmony_ci	struct ib_mad_send_wr_private *wr;
173662306a36Sopenharmony_ci	const struct ib_mad_hdr *mad_hdr;
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	mad_hdr = &wc->recv_buf.mad->mad_hdr;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	list_for_each_entry(wr, &mad_agent_priv->wait_list, agent_list) {
174162306a36Sopenharmony_ci		if ((wr->tid == mad_hdr->tid) &&
174262306a36Sopenharmony_ci		    rcv_has_same_class(wr, wc) &&
174362306a36Sopenharmony_ci		    /*
174462306a36Sopenharmony_ci		     * Don't check GID for direct routed MADs.
174562306a36Sopenharmony_ci		     * These might have permissive LIDs.
174662306a36Sopenharmony_ci		     */
174762306a36Sopenharmony_ci		    (is_direct(mad_hdr->mgmt_class) ||
174862306a36Sopenharmony_ci		     rcv_has_same_gid(mad_agent_priv, wr, wc)))
174962306a36Sopenharmony_ci			return (wr->status == IB_WC_SUCCESS) ? wr : NULL;
175062306a36Sopenharmony_ci	}
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	/*
175362306a36Sopenharmony_ci	 * It's possible to receive the response before we've
175462306a36Sopenharmony_ci	 * been notified that the send has completed
175562306a36Sopenharmony_ci	 */
175662306a36Sopenharmony_ci	list_for_each_entry(wr, &mad_agent_priv->send_list, agent_list) {
175762306a36Sopenharmony_ci		if (is_rmpp_data_mad(mad_agent_priv, wr->send_buf.mad) &&
175862306a36Sopenharmony_ci		    wr->tid == mad_hdr->tid &&
175962306a36Sopenharmony_ci		    wr->timeout &&
176062306a36Sopenharmony_ci		    rcv_has_same_class(wr, wc) &&
176162306a36Sopenharmony_ci		    /*
176262306a36Sopenharmony_ci		     * Don't check GID for direct routed MADs.
176362306a36Sopenharmony_ci		     * These might have permissive LIDs.
176462306a36Sopenharmony_ci		     */
176562306a36Sopenharmony_ci		    (is_direct(mad_hdr->mgmt_class) ||
176662306a36Sopenharmony_ci		     rcv_has_same_gid(mad_agent_priv, wr, wc)))
176762306a36Sopenharmony_ci			/* Verify request has not been canceled */
176862306a36Sopenharmony_ci			return (wr->status == IB_WC_SUCCESS) ? wr : NULL;
176962306a36Sopenharmony_ci	}
177062306a36Sopenharmony_ci	return NULL;
177162306a36Sopenharmony_ci}
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_civoid ib_mark_mad_done(struct ib_mad_send_wr_private *mad_send_wr)
177462306a36Sopenharmony_ci{
177562306a36Sopenharmony_ci	mad_send_wr->timeout = 0;
177662306a36Sopenharmony_ci	if (mad_send_wr->refcount == 1)
177762306a36Sopenharmony_ci		list_move_tail(&mad_send_wr->agent_list,
177862306a36Sopenharmony_ci			      &mad_send_wr->mad_agent_priv->done_list);
177962306a36Sopenharmony_ci}
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_cistatic void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
178262306a36Sopenharmony_ci				 struct ib_mad_recv_wc *mad_recv_wc)
178362306a36Sopenharmony_ci{
178462306a36Sopenharmony_ci	struct ib_mad_send_wr_private *mad_send_wr;
178562306a36Sopenharmony_ci	struct ib_mad_send_wc mad_send_wc;
178662306a36Sopenharmony_ci	unsigned long flags;
178762306a36Sopenharmony_ci	int ret;
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	INIT_LIST_HEAD(&mad_recv_wc->rmpp_list);
179062306a36Sopenharmony_ci	ret = ib_mad_enforce_security(mad_agent_priv,
179162306a36Sopenharmony_ci				      mad_recv_wc->wc->pkey_index);
179262306a36Sopenharmony_ci	if (ret) {
179362306a36Sopenharmony_ci		ib_free_recv_mad(mad_recv_wc);
179462306a36Sopenharmony_ci		deref_mad_agent(mad_agent_priv);
179562306a36Sopenharmony_ci		return;
179662306a36Sopenharmony_ci	}
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	list_add(&mad_recv_wc->recv_buf.list, &mad_recv_wc->rmpp_list);
179962306a36Sopenharmony_ci	if (ib_mad_kernel_rmpp_agent(&mad_agent_priv->agent)) {
180062306a36Sopenharmony_ci		mad_recv_wc = ib_process_rmpp_recv_wc(mad_agent_priv,
180162306a36Sopenharmony_ci						      mad_recv_wc);
180262306a36Sopenharmony_ci		if (!mad_recv_wc) {
180362306a36Sopenharmony_ci			deref_mad_agent(mad_agent_priv);
180462306a36Sopenharmony_ci			return;
180562306a36Sopenharmony_ci		}
180662306a36Sopenharmony_ci	}
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	/* Complete corresponding request */
180962306a36Sopenharmony_ci	if (ib_response_mad(&mad_recv_wc->recv_buf.mad->mad_hdr)) {
181062306a36Sopenharmony_ci		spin_lock_irqsave(&mad_agent_priv->lock, flags);
181162306a36Sopenharmony_ci		mad_send_wr = ib_find_send_mad(mad_agent_priv, mad_recv_wc);
181262306a36Sopenharmony_ci		if (!mad_send_wr) {
181362306a36Sopenharmony_ci			spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
181462306a36Sopenharmony_ci			if (!ib_mad_kernel_rmpp_agent(&mad_agent_priv->agent)
181562306a36Sopenharmony_ci			   && ib_is_mad_class_rmpp(mad_recv_wc->recv_buf.mad->mad_hdr.mgmt_class)
181662306a36Sopenharmony_ci			   && (ib_get_rmpp_flags(&((struct ib_rmpp_mad *)mad_recv_wc->recv_buf.mad)->rmpp_hdr)
181762306a36Sopenharmony_ci					& IB_MGMT_RMPP_FLAG_ACTIVE)) {
181862306a36Sopenharmony_ci				/* user rmpp is in effect
181962306a36Sopenharmony_ci				 * and this is an active RMPP MAD
182062306a36Sopenharmony_ci				 */
182162306a36Sopenharmony_ci				mad_agent_priv->agent.recv_handler(
182262306a36Sopenharmony_ci						&mad_agent_priv->agent, NULL,
182362306a36Sopenharmony_ci						mad_recv_wc);
182462306a36Sopenharmony_ci				deref_mad_agent(mad_agent_priv);
182562306a36Sopenharmony_ci			} else {
182662306a36Sopenharmony_ci				/* not user rmpp, revert to normal behavior and
182762306a36Sopenharmony_ci				 * drop the mad
182862306a36Sopenharmony_ci				 */
182962306a36Sopenharmony_ci				ib_free_recv_mad(mad_recv_wc);
183062306a36Sopenharmony_ci				deref_mad_agent(mad_agent_priv);
183162306a36Sopenharmony_ci				return;
183262306a36Sopenharmony_ci			}
183362306a36Sopenharmony_ci		} else {
183462306a36Sopenharmony_ci			ib_mark_mad_done(mad_send_wr);
183562306a36Sopenharmony_ci			spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci			/* Defined behavior is to complete response before request */
183862306a36Sopenharmony_ci			mad_agent_priv->agent.recv_handler(
183962306a36Sopenharmony_ci					&mad_agent_priv->agent,
184062306a36Sopenharmony_ci					&mad_send_wr->send_buf,
184162306a36Sopenharmony_ci					mad_recv_wc);
184262306a36Sopenharmony_ci			deref_mad_agent(mad_agent_priv);
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci			mad_send_wc.status = IB_WC_SUCCESS;
184562306a36Sopenharmony_ci			mad_send_wc.vendor_err = 0;
184662306a36Sopenharmony_ci			mad_send_wc.send_buf = &mad_send_wr->send_buf;
184762306a36Sopenharmony_ci			ib_mad_complete_send_wr(mad_send_wr, &mad_send_wc);
184862306a36Sopenharmony_ci		}
184962306a36Sopenharmony_ci	} else {
185062306a36Sopenharmony_ci		mad_agent_priv->agent.recv_handler(&mad_agent_priv->agent, NULL,
185162306a36Sopenharmony_ci						   mad_recv_wc);
185262306a36Sopenharmony_ci		deref_mad_agent(mad_agent_priv);
185362306a36Sopenharmony_ci	}
185462306a36Sopenharmony_ci}
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_cistatic enum smi_action handle_ib_smi(const struct ib_mad_port_private *port_priv,
185762306a36Sopenharmony_ci				     const struct ib_mad_qp_info *qp_info,
185862306a36Sopenharmony_ci				     const struct ib_wc *wc,
185962306a36Sopenharmony_ci				     u32 port_num,
186062306a36Sopenharmony_ci				     struct ib_mad_private *recv,
186162306a36Sopenharmony_ci				     struct ib_mad_private *response)
186262306a36Sopenharmony_ci{
186362306a36Sopenharmony_ci	enum smi_forward_action retsmi;
186462306a36Sopenharmony_ci	struct ib_smp *smp = (struct ib_smp *)recv->mad;
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	trace_ib_mad_handle_ib_smi(smp);
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	if (smi_handle_dr_smp_recv(smp,
186962306a36Sopenharmony_ci				   rdma_cap_ib_switch(port_priv->device),
187062306a36Sopenharmony_ci				   port_num,
187162306a36Sopenharmony_ci				   port_priv->device->phys_port_cnt) ==
187262306a36Sopenharmony_ci				   IB_SMI_DISCARD)
187362306a36Sopenharmony_ci		return IB_SMI_DISCARD;
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	retsmi = smi_check_forward_dr_smp(smp);
187662306a36Sopenharmony_ci	if (retsmi == IB_SMI_LOCAL)
187762306a36Sopenharmony_ci		return IB_SMI_HANDLE;
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	if (retsmi == IB_SMI_SEND) { /* don't forward */
188062306a36Sopenharmony_ci		if (smi_handle_dr_smp_send(smp,
188162306a36Sopenharmony_ci					   rdma_cap_ib_switch(port_priv->device),
188262306a36Sopenharmony_ci					   port_num) == IB_SMI_DISCARD)
188362306a36Sopenharmony_ci			return IB_SMI_DISCARD;
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci		if (smi_check_local_smp(smp, port_priv->device) == IB_SMI_DISCARD)
188662306a36Sopenharmony_ci			return IB_SMI_DISCARD;
188762306a36Sopenharmony_ci	} else if (rdma_cap_ib_switch(port_priv->device)) {
188862306a36Sopenharmony_ci		/* forward case for switches */
188962306a36Sopenharmony_ci		memcpy(response, recv, mad_priv_size(response));
189062306a36Sopenharmony_ci		response->header.recv_wc.wc = &response->header.wc;
189162306a36Sopenharmony_ci		response->header.recv_wc.recv_buf.mad = (struct ib_mad *)response->mad;
189262306a36Sopenharmony_ci		response->header.recv_wc.recv_buf.grh = &response->grh;
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci		agent_send_response((const struct ib_mad_hdr *)response->mad,
189562306a36Sopenharmony_ci				    &response->grh, wc,
189662306a36Sopenharmony_ci				    port_priv->device,
189762306a36Sopenharmony_ci				    smi_get_fwd_port(smp),
189862306a36Sopenharmony_ci				    qp_info->qp->qp_num,
189962306a36Sopenharmony_ci				    response->mad_size,
190062306a36Sopenharmony_ci				    false);
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci		return IB_SMI_DISCARD;
190362306a36Sopenharmony_ci	}
190462306a36Sopenharmony_ci	return IB_SMI_HANDLE;
190562306a36Sopenharmony_ci}
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_cistatic bool generate_unmatched_resp(const struct ib_mad_private *recv,
190862306a36Sopenharmony_ci				    struct ib_mad_private *response,
190962306a36Sopenharmony_ci				    size_t *resp_len, bool opa)
191062306a36Sopenharmony_ci{
191162306a36Sopenharmony_ci	const struct ib_mad_hdr *recv_hdr = (const struct ib_mad_hdr *)recv->mad;
191262306a36Sopenharmony_ci	struct ib_mad_hdr *resp_hdr = (struct ib_mad_hdr *)response->mad;
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	if (recv_hdr->method == IB_MGMT_METHOD_GET ||
191562306a36Sopenharmony_ci	    recv_hdr->method == IB_MGMT_METHOD_SET) {
191662306a36Sopenharmony_ci		memcpy(response, recv, mad_priv_size(response));
191762306a36Sopenharmony_ci		response->header.recv_wc.wc = &response->header.wc;
191862306a36Sopenharmony_ci		response->header.recv_wc.recv_buf.mad = (struct ib_mad *)response->mad;
191962306a36Sopenharmony_ci		response->header.recv_wc.recv_buf.grh = &response->grh;
192062306a36Sopenharmony_ci		resp_hdr->method = IB_MGMT_METHOD_GET_RESP;
192162306a36Sopenharmony_ci		resp_hdr->status = cpu_to_be16(IB_MGMT_MAD_STATUS_UNSUPPORTED_METHOD_ATTRIB);
192262306a36Sopenharmony_ci		if (recv_hdr->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
192362306a36Sopenharmony_ci			resp_hdr->status |= IB_SMP_DIRECTION;
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci		if (opa && recv_hdr->base_version == OPA_MGMT_BASE_VERSION) {
192662306a36Sopenharmony_ci			if (recv_hdr->mgmt_class ==
192762306a36Sopenharmony_ci			    IB_MGMT_CLASS_SUBN_LID_ROUTED ||
192862306a36Sopenharmony_ci			    recv_hdr->mgmt_class ==
192962306a36Sopenharmony_ci			    IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
193062306a36Sopenharmony_ci				*resp_len = opa_get_smp_header_size(
193162306a36Sopenharmony_ci							(struct opa_smp *)recv->mad);
193262306a36Sopenharmony_ci			else
193362306a36Sopenharmony_ci				*resp_len = sizeof(struct ib_mad_hdr);
193462306a36Sopenharmony_ci		}
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci		return true;
193762306a36Sopenharmony_ci	} else {
193862306a36Sopenharmony_ci		return false;
193962306a36Sopenharmony_ci	}
194062306a36Sopenharmony_ci}
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_cistatic enum smi_action
194362306a36Sopenharmony_cihandle_opa_smi(struct ib_mad_port_private *port_priv,
194462306a36Sopenharmony_ci	       struct ib_mad_qp_info *qp_info,
194562306a36Sopenharmony_ci	       struct ib_wc *wc,
194662306a36Sopenharmony_ci	       u32 port_num,
194762306a36Sopenharmony_ci	       struct ib_mad_private *recv,
194862306a36Sopenharmony_ci	       struct ib_mad_private *response)
194962306a36Sopenharmony_ci{
195062306a36Sopenharmony_ci	enum smi_forward_action retsmi;
195162306a36Sopenharmony_ci	struct opa_smp *smp = (struct opa_smp *)recv->mad;
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	trace_ib_mad_handle_opa_smi(smp);
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci	if (opa_smi_handle_dr_smp_recv(smp,
195662306a36Sopenharmony_ci				   rdma_cap_ib_switch(port_priv->device),
195762306a36Sopenharmony_ci				   port_num,
195862306a36Sopenharmony_ci				   port_priv->device->phys_port_cnt) ==
195962306a36Sopenharmony_ci				   IB_SMI_DISCARD)
196062306a36Sopenharmony_ci		return IB_SMI_DISCARD;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	retsmi = opa_smi_check_forward_dr_smp(smp);
196362306a36Sopenharmony_ci	if (retsmi == IB_SMI_LOCAL)
196462306a36Sopenharmony_ci		return IB_SMI_HANDLE;
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci	if (retsmi == IB_SMI_SEND) { /* don't forward */
196762306a36Sopenharmony_ci		if (opa_smi_handle_dr_smp_send(smp,
196862306a36Sopenharmony_ci					   rdma_cap_ib_switch(port_priv->device),
196962306a36Sopenharmony_ci					   port_num) == IB_SMI_DISCARD)
197062306a36Sopenharmony_ci			return IB_SMI_DISCARD;
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci		if (opa_smi_check_local_smp(smp, port_priv->device) ==
197362306a36Sopenharmony_ci		    IB_SMI_DISCARD)
197462306a36Sopenharmony_ci			return IB_SMI_DISCARD;
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci	} else if (rdma_cap_ib_switch(port_priv->device)) {
197762306a36Sopenharmony_ci		/* forward case for switches */
197862306a36Sopenharmony_ci		memcpy(response, recv, mad_priv_size(response));
197962306a36Sopenharmony_ci		response->header.recv_wc.wc = &response->header.wc;
198062306a36Sopenharmony_ci		response->header.recv_wc.recv_buf.opa_mad =
198162306a36Sopenharmony_ci				(struct opa_mad *)response->mad;
198262306a36Sopenharmony_ci		response->header.recv_wc.recv_buf.grh = &response->grh;
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci		agent_send_response((const struct ib_mad_hdr *)response->mad,
198562306a36Sopenharmony_ci				    &response->grh, wc,
198662306a36Sopenharmony_ci				    port_priv->device,
198762306a36Sopenharmony_ci				    opa_smi_get_fwd_port(smp),
198862306a36Sopenharmony_ci				    qp_info->qp->qp_num,
198962306a36Sopenharmony_ci				    recv->header.wc.byte_len,
199062306a36Sopenharmony_ci				    true);
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci		return IB_SMI_DISCARD;
199362306a36Sopenharmony_ci	}
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci	return IB_SMI_HANDLE;
199662306a36Sopenharmony_ci}
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_cistatic enum smi_action
199962306a36Sopenharmony_cihandle_smi(struct ib_mad_port_private *port_priv,
200062306a36Sopenharmony_ci	   struct ib_mad_qp_info *qp_info,
200162306a36Sopenharmony_ci	   struct ib_wc *wc,
200262306a36Sopenharmony_ci	   u32 port_num,
200362306a36Sopenharmony_ci	   struct ib_mad_private *recv,
200462306a36Sopenharmony_ci	   struct ib_mad_private *response,
200562306a36Sopenharmony_ci	   bool opa)
200662306a36Sopenharmony_ci{
200762306a36Sopenharmony_ci	struct ib_mad_hdr *mad_hdr = (struct ib_mad_hdr *)recv->mad;
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci	if (opa && mad_hdr->base_version == OPA_MGMT_BASE_VERSION &&
201062306a36Sopenharmony_ci	    mad_hdr->class_version == OPA_SM_CLASS_VERSION)
201162306a36Sopenharmony_ci		return handle_opa_smi(port_priv, qp_info, wc, port_num, recv,
201262306a36Sopenharmony_ci				      response);
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	return handle_ib_smi(port_priv, qp_info, wc, port_num, recv, response);
201562306a36Sopenharmony_ci}
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_cistatic void ib_mad_recv_done(struct ib_cq *cq, struct ib_wc *wc)
201862306a36Sopenharmony_ci{
201962306a36Sopenharmony_ci	struct ib_mad_port_private *port_priv = cq->cq_context;
202062306a36Sopenharmony_ci	struct ib_mad_list_head *mad_list =
202162306a36Sopenharmony_ci		container_of(wc->wr_cqe, struct ib_mad_list_head, cqe);
202262306a36Sopenharmony_ci	struct ib_mad_qp_info *qp_info;
202362306a36Sopenharmony_ci	struct ib_mad_private_header *mad_priv_hdr;
202462306a36Sopenharmony_ci	struct ib_mad_private *recv, *response = NULL;
202562306a36Sopenharmony_ci	struct ib_mad_agent_private *mad_agent;
202662306a36Sopenharmony_ci	u32 port_num;
202762306a36Sopenharmony_ci	int ret = IB_MAD_RESULT_SUCCESS;
202862306a36Sopenharmony_ci	size_t mad_size;
202962306a36Sopenharmony_ci	u16 resp_mad_pkey_index = 0;
203062306a36Sopenharmony_ci	bool opa;
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	if (list_empty_careful(&port_priv->port_list))
203362306a36Sopenharmony_ci		return;
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci	if (wc->status != IB_WC_SUCCESS) {
203662306a36Sopenharmony_ci		/*
203762306a36Sopenharmony_ci		 * Receive errors indicate that the QP has entered the error
203862306a36Sopenharmony_ci		 * state - error handling/shutdown code will cleanup
203962306a36Sopenharmony_ci		 */
204062306a36Sopenharmony_ci		return;
204162306a36Sopenharmony_ci	}
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci	qp_info = mad_list->mad_queue->qp_info;
204462306a36Sopenharmony_ci	dequeue_mad(mad_list);
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci	opa = rdma_cap_opa_mad(qp_info->port_priv->device,
204762306a36Sopenharmony_ci			       qp_info->port_priv->port_num);
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_ci	mad_priv_hdr = container_of(mad_list, struct ib_mad_private_header,
205062306a36Sopenharmony_ci				    mad_list);
205162306a36Sopenharmony_ci	recv = container_of(mad_priv_hdr, struct ib_mad_private, header);
205262306a36Sopenharmony_ci	ib_dma_unmap_single(port_priv->device,
205362306a36Sopenharmony_ci			    recv->header.mapping,
205462306a36Sopenharmony_ci			    mad_priv_dma_size(recv),
205562306a36Sopenharmony_ci			    DMA_FROM_DEVICE);
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	/* Setup MAD receive work completion from "normal" work completion */
205862306a36Sopenharmony_ci	recv->header.wc = *wc;
205962306a36Sopenharmony_ci	recv->header.recv_wc.wc = &recv->header.wc;
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci	if (opa && ((struct ib_mad_hdr *)(recv->mad))->base_version == OPA_MGMT_BASE_VERSION) {
206262306a36Sopenharmony_ci		recv->header.recv_wc.mad_len = wc->byte_len - sizeof(struct ib_grh);
206362306a36Sopenharmony_ci		recv->header.recv_wc.mad_seg_size = sizeof(struct opa_mad);
206462306a36Sopenharmony_ci	} else {
206562306a36Sopenharmony_ci		recv->header.recv_wc.mad_len = sizeof(struct ib_mad);
206662306a36Sopenharmony_ci		recv->header.recv_wc.mad_seg_size = sizeof(struct ib_mad);
206762306a36Sopenharmony_ci	}
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	recv->header.recv_wc.recv_buf.mad = (struct ib_mad *)recv->mad;
207062306a36Sopenharmony_ci	recv->header.recv_wc.recv_buf.grh = &recv->grh;
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	/* Validate MAD */
207362306a36Sopenharmony_ci	if (!validate_mad((const struct ib_mad_hdr *)recv->mad, qp_info, opa))
207462306a36Sopenharmony_ci		goto out;
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	trace_ib_mad_recv_done_handler(qp_info, wc,
207762306a36Sopenharmony_ci				       (struct ib_mad_hdr *)recv->mad);
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci	mad_size = recv->mad_size;
208062306a36Sopenharmony_ci	response = alloc_mad_private(mad_size, GFP_KERNEL);
208162306a36Sopenharmony_ci	if (!response)
208262306a36Sopenharmony_ci		goto out;
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	if (rdma_cap_ib_switch(port_priv->device))
208562306a36Sopenharmony_ci		port_num = wc->port_num;
208662306a36Sopenharmony_ci	else
208762306a36Sopenharmony_ci		port_num = port_priv->port_num;
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	if (((struct ib_mad_hdr *)recv->mad)->mgmt_class ==
209062306a36Sopenharmony_ci	    IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
209162306a36Sopenharmony_ci		if (handle_smi(port_priv, qp_info, wc, port_num, recv,
209262306a36Sopenharmony_ci			       response, opa)
209362306a36Sopenharmony_ci		    == IB_SMI_DISCARD)
209462306a36Sopenharmony_ci			goto out;
209562306a36Sopenharmony_ci	}
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci	/* Give driver "right of first refusal" on incoming MAD */
209862306a36Sopenharmony_ci	if (port_priv->device->ops.process_mad) {
209962306a36Sopenharmony_ci		ret = port_priv->device->ops.process_mad(
210062306a36Sopenharmony_ci			port_priv->device, 0, port_priv->port_num, wc,
210162306a36Sopenharmony_ci			&recv->grh, (const struct ib_mad *)recv->mad,
210262306a36Sopenharmony_ci			(struct ib_mad *)response->mad, &mad_size,
210362306a36Sopenharmony_ci			&resp_mad_pkey_index);
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci		if (opa)
210662306a36Sopenharmony_ci			wc->pkey_index = resp_mad_pkey_index;
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci		if (ret & IB_MAD_RESULT_SUCCESS) {
210962306a36Sopenharmony_ci			if (ret & IB_MAD_RESULT_CONSUMED)
211062306a36Sopenharmony_ci				goto out;
211162306a36Sopenharmony_ci			if (ret & IB_MAD_RESULT_REPLY) {
211262306a36Sopenharmony_ci				agent_send_response((const struct ib_mad_hdr *)response->mad,
211362306a36Sopenharmony_ci						    &recv->grh, wc,
211462306a36Sopenharmony_ci						    port_priv->device,
211562306a36Sopenharmony_ci						    port_num,
211662306a36Sopenharmony_ci						    qp_info->qp->qp_num,
211762306a36Sopenharmony_ci						    mad_size, opa);
211862306a36Sopenharmony_ci				goto out;
211962306a36Sopenharmony_ci			}
212062306a36Sopenharmony_ci		}
212162306a36Sopenharmony_ci	}
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci	mad_agent = find_mad_agent(port_priv, (const struct ib_mad_hdr *)recv->mad);
212462306a36Sopenharmony_ci	if (mad_agent) {
212562306a36Sopenharmony_ci		trace_ib_mad_recv_done_agent(mad_agent);
212662306a36Sopenharmony_ci		ib_mad_complete_recv(mad_agent, &recv->header.recv_wc);
212762306a36Sopenharmony_ci		/*
212862306a36Sopenharmony_ci		 * recv is freed up in error cases in ib_mad_complete_recv
212962306a36Sopenharmony_ci		 * or via recv_handler in ib_mad_complete_recv()
213062306a36Sopenharmony_ci		 */
213162306a36Sopenharmony_ci		recv = NULL;
213262306a36Sopenharmony_ci	} else if ((ret & IB_MAD_RESULT_SUCCESS) &&
213362306a36Sopenharmony_ci		   generate_unmatched_resp(recv, response, &mad_size, opa)) {
213462306a36Sopenharmony_ci		agent_send_response((const struct ib_mad_hdr *)response->mad, &recv->grh, wc,
213562306a36Sopenharmony_ci				    port_priv->device, port_num,
213662306a36Sopenharmony_ci				    qp_info->qp->qp_num, mad_size, opa);
213762306a36Sopenharmony_ci	}
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ciout:
214062306a36Sopenharmony_ci	/* Post another receive request for this QP */
214162306a36Sopenharmony_ci	if (response) {
214262306a36Sopenharmony_ci		ib_mad_post_receive_mads(qp_info, response);
214362306a36Sopenharmony_ci		kfree(recv);
214462306a36Sopenharmony_ci	} else
214562306a36Sopenharmony_ci		ib_mad_post_receive_mads(qp_info, recv);
214662306a36Sopenharmony_ci}
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_cistatic void adjust_timeout(struct ib_mad_agent_private *mad_agent_priv)
214962306a36Sopenharmony_ci{
215062306a36Sopenharmony_ci	struct ib_mad_send_wr_private *mad_send_wr;
215162306a36Sopenharmony_ci	unsigned long delay;
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_ci	if (list_empty(&mad_agent_priv->wait_list)) {
215462306a36Sopenharmony_ci		cancel_delayed_work(&mad_agent_priv->timed_work);
215562306a36Sopenharmony_ci	} else {
215662306a36Sopenharmony_ci		mad_send_wr = list_entry(mad_agent_priv->wait_list.next,
215762306a36Sopenharmony_ci					 struct ib_mad_send_wr_private,
215862306a36Sopenharmony_ci					 agent_list);
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci		if (time_after(mad_agent_priv->timeout,
216162306a36Sopenharmony_ci			       mad_send_wr->timeout)) {
216262306a36Sopenharmony_ci			mad_agent_priv->timeout = mad_send_wr->timeout;
216362306a36Sopenharmony_ci			delay = mad_send_wr->timeout - jiffies;
216462306a36Sopenharmony_ci			if ((long)delay <= 0)
216562306a36Sopenharmony_ci				delay = 1;
216662306a36Sopenharmony_ci			mod_delayed_work(mad_agent_priv->qp_info->port_priv->wq,
216762306a36Sopenharmony_ci					 &mad_agent_priv->timed_work, delay);
216862306a36Sopenharmony_ci		}
216962306a36Sopenharmony_ci	}
217062306a36Sopenharmony_ci}
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_cistatic void wait_for_response(struct ib_mad_send_wr_private *mad_send_wr)
217362306a36Sopenharmony_ci{
217462306a36Sopenharmony_ci	struct ib_mad_agent_private *mad_agent_priv;
217562306a36Sopenharmony_ci	struct ib_mad_send_wr_private *temp_mad_send_wr;
217662306a36Sopenharmony_ci	struct list_head *list_item;
217762306a36Sopenharmony_ci	unsigned long delay;
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	mad_agent_priv = mad_send_wr->mad_agent_priv;
218062306a36Sopenharmony_ci	list_del(&mad_send_wr->agent_list);
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci	delay = mad_send_wr->timeout;
218362306a36Sopenharmony_ci	mad_send_wr->timeout += jiffies;
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ci	if (delay) {
218662306a36Sopenharmony_ci		list_for_each_prev(list_item, &mad_agent_priv->wait_list) {
218762306a36Sopenharmony_ci			temp_mad_send_wr = list_entry(list_item,
218862306a36Sopenharmony_ci						struct ib_mad_send_wr_private,
218962306a36Sopenharmony_ci						agent_list);
219062306a36Sopenharmony_ci			if (time_after(mad_send_wr->timeout,
219162306a36Sopenharmony_ci				       temp_mad_send_wr->timeout))
219262306a36Sopenharmony_ci				break;
219362306a36Sopenharmony_ci		}
219462306a36Sopenharmony_ci	} else {
219562306a36Sopenharmony_ci		list_item = &mad_agent_priv->wait_list;
219662306a36Sopenharmony_ci	}
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	list_add(&mad_send_wr->agent_list, list_item);
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	/* Reschedule a work item if we have a shorter timeout */
220162306a36Sopenharmony_ci	if (mad_agent_priv->wait_list.next == &mad_send_wr->agent_list)
220262306a36Sopenharmony_ci		mod_delayed_work(mad_agent_priv->qp_info->port_priv->wq,
220362306a36Sopenharmony_ci				 &mad_agent_priv->timed_work, delay);
220462306a36Sopenharmony_ci}
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_civoid ib_reset_mad_timeout(struct ib_mad_send_wr_private *mad_send_wr,
220762306a36Sopenharmony_ci			  unsigned long timeout_ms)
220862306a36Sopenharmony_ci{
220962306a36Sopenharmony_ci	mad_send_wr->timeout = msecs_to_jiffies(timeout_ms);
221062306a36Sopenharmony_ci	wait_for_response(mad_send_wr);
221162306a36Sopenharmony_ci}
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci/*
221462306a36Sopenharmony_ci * Process a send work completion
221562306a36Sopenharmony_ci */
221662306a36Sopenharmony_civoid ib_mad_complete_send_wr(struct ib_mad_send_wr_private *mad_send_wr,
221762306a36Sopenharmony_ci			     struct ib_mad_send_wc *mad_send_wc)
221862306a36Sopenharmony_ci{
221962306a36Sopenharmony_ci	struct ib_mad_agent_private	*mad_agent_priv;
222062306a36Sopenharmony_ci	unsigned long			flags;
222162306a36Sopenharmony_ci	int				ret;
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	mad_agent_priv = mad_send_wr->mad_agent_priv;
222462306a36Sopenharmony_ci	spin_lock_irqsave(&mad_agent_priv->lock, flags);
222562306a36Sopenharmony_ci	if (ib_mad_kernel_rmpp_agent(&mad_agent_priv->agent)) {
222662306a36Sopenharmony_ci		ret = ib_process_rmpp_send_wc(mad_send_wr, mad_send_wc);
222762306a36Sopenharmony_ci		if (ret == IB_RMPP_RESULT_CONSUMED)
222862306a36Sopenharmony_ci			goto done;
222962306a36Sopenharmony_ci	} else
223062306a36Sopenharmony_ci		ret = IB_RMPP_RESULT_UNHANDLED;
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_ci	if (mad_send_wc->status != IB_WC_SUCCESS &&
223362306a36Sopenharmony_ci	    mad_send_wr->status == IB_WC_SUCCESS) {
223462306a36Sopenharmony_ci		mad_send_wr->status = mad_send_wc->status;
223562306a36Sopenharmony_ci		mad_send_wr->refcount -= (mad_send_wr->timeout > 0);
223662306a36Sopenharmony_ci	}
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci	if (--mad_send_wr->refcount > 0) {
223962306a36Sopenharmony_ci		if (mad_send_wr->refcount == 1 && mad_send_wr->timeout &&
224062306a36Sopenharmony_ci		    mad_send_wr->status == IB_WC_SUCCESS) {
224162306a36Sopenharmony_ci			wait_for_response(mad_send_wr);
224262306a36Sopenharmony_ci		}
224362306a36Sopenharmony_ci		goto done;
224462306a36Sopenharmony_ci	}
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci	/* Remove send from MAD agent and notify client of completion */
224762306a36Sopenharmony_ci	list_del(&mad_send_wr->agent_list);
224862306a36Sopenharmony_ci	adjust_timeout(mad_agent_priv);
224962306a36Sopenharmony_ci	spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	if (mad_send_wr->status != IB_WC_SUCCESS)
225262306a36Sopenharmony_ci		mad_send_wc->status = mad_send_wr->status;
225362306a36Sopenharmony_ci	if (ret == IB_RMPP_RESULT_INTERNAL)
225462306a36Sopenharmony_ci		ib_rmpp_send_handler(mad_send_wc);
225562306a36Sopenharmony_ci	else
225662306a36Sopenharmony_ci		mad_agent_priv->agent.send_handler(&mad_agent_priv->agent,
225762306a36Sopenharmony_ci						   mad_send_wc);
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	/* Release reference on agent taken when sending */
226062306a36Sopenharmony_ci	deref_mad_agent(mad_agent_priv);
226162306a36Sopenharmony_ci	return;
226262306a36Sopenharmony_cidone:
226362306a36Sopenharmony_ci	spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
226462306a36Sopenharmony_ci}
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_cistatic void ib_mad_send_done(struct ib_cq *cq, struct ib_wc *wc)
226762306a36Sopenharmony_ci{
226862306a36Sopenharmony_ci	struct ib_mad_port_private *port_priv = cq->cq_context;
226962306a36Sopenharmony_ci	struct ib_mad_list_head *mad_list =
227062306a36Sopenharmony_ci		container_of(wc->wr_cqe, struct ib_mad_list_head, cqe);
227162306a36Sopenharmony_ci	struct ib_mad_send_wr_private	*mad_send_wr, *queued_send_wr;
227262306a36Sopenharmony_ci	struct ib_mad_qp_info		*qp_info;
227362306a36Sopenharmony_ci	struct ib_mad_queue		*send_queue;
227462306a36Sopenharmony_ci	struct ib_mad_send_wc		mad_send_wc;
227562306a36Sopenharmony_ci	unsigned long flags;
227662306a36Sopenharmony_ci	int ret;
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci	if (list_empty_careful(&port_priv->port_list))
227962306a36Sopenharmony_ci		return;
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci	if (wc->status != IB_WC_SUCCESS) {
228262306a36Sopenharmony_ci		if (!ib_mad_send_error(port_priv, wc))
228362306a36Sopenharmony_ci			return;
228462306a36Sopenharmony_ci	}
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	mad_send_wr = container_of(mad_list, struct ib_mad_send_wr_private,
228762306a36Sopenharmony_ci				   mad_list);
228862306a36Sopenharmony_ci	send_queue = mad_list->mad_queue;
228962306a36Sopenharmony_ci	qp_info = send_queue->qp_info;
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_ci	trace_ib_mad_send_done_agent(mad_send_wr->mad_agent_priv);
229262306a36Sopenharmony_ci	trace_ib_mad_send_done_handler(mad_send_wr, wc);
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ciretry:
229562306a36Sopenharmony_ci	ib_dma_unmap_single(mad_send_wr->send_buf.mad_agent->device,
229662306a36Sopenharmony_ci			    mad_send_wr->header_mapping,
229762306a36Sopenharmony_ci			    mad_send_wr->sg_list[0].length, DMA_TO_DEVICE);
229862306a36Sopenharmony_ci	ib_dma_unmap_single(mad_send_wr->send_buf.mad_agent->device,
229962306a36Sopenharmony_ci			    mad_send_wr->payload_mapping,
230062306a36Sopenharmony_ci			    mad_send_wr->sg_list[1].length, DMA_TO_DEVICE);
230162306a36Sopenharmony_ci	queued_send_wr = NULL;
230262306a36Sopenharmony_ci	spin_lock_irqsave(&send_queue->lock, flags);
230362306a36Sopenharmony_ci	list_del(&mad_list->list);
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci	/* Move queued send to the send queue */
230662306a36Sopenharmony_ci	if (send_queue->count-- > send_queue->max_active) {
230762306a36Sopenharmony_ci		mad_list = container_of(qp_info->overflow_list.next,
230862306a36Sopenharmony_ci					struct ib_mad_list_head, list);
230962306a36Sopenharmony_ci		queued_send_wr = container_of(mad_list,
231062306a36Sopenharmony_ci					struct ib_mad_send_wr_private,
231162306a36Sopenharmony_ci					mad_list);
231262306a36Sopenharmony_ci		list_move_tail(&mad_list->list, &send_queue->list);
231362306a36Sopenharmony_ci	}
231462306a36Sopenharmony_ci	spin_unlock_irqrestore(&send_queue->lock, flags);
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	mad_send_wc.send_buf = &mad_send_wr->send_buf;
231762306a36Sopenharmony_ci	mad_send_wc.status = wc->status;
231862306a36Sopenharmony_ci	mad_send_wc.vendor_err = wc->vendor_err;
231962306a36Sopenharmony_ci	ib_mad_complete_send_wr(mad_send_wr, &mad_send_wc);
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	if (queued_send_wr) {
232262306a36Sopenharmony_ci		trace_ib_mad_send_done_resend(queued_send_wr, qp_info);
232362306a36Sopenharmony_ci		ret = ib_post_send(qp_info->qp, &queued_send_wr->send_wr.wr,
232462306a36Sopenharmony_ci				   NULL);
232562306a36Sopenharmony_ci		if (ret) {
232662306a36Sopenharmony_ci			dev_err(&port_priv->device->dev,
232762306a36Sopenharmony_ci				"ib_post_send failed: %d\n", ret);
232862306a36Sopenharmony_ci			mad_send_wr = queued_send_wr;
232962306a36Sopenharmony_ci			wc->status = IB_WC_LOC_QP_OP_ERR;
233062306a36Sopenharmony_ci			goto retry;
233162306a36Sopenharmony_ci		}
233262306a36Sopenharmony_ci	}
233362306a36Sopenharmony_ci}
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_cistatic void mark_sends_for_retry(struct ib_mad_qp_info *qp_info)
233662306a36Sopenharmony_ci{
233762306a36Sopenharmony_ci	struct ib_mad_send_wr_private *mad_send_wr;
233862306a36Sopenharmony_ci	struct ib_mad_list_head *mad_list;
233962306a36Sopenharmony_ci	unsigned long flags;
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci	spin_lock_irqsave(&qp_info->send_queue.lock, flags);
234262306a36Sopenharmony_ci	list_for_each_entry(mad_list, &qp_info->send_queue.list, list) {
234362306a36Sopenharmony_ci		mad_send_wr = container_of(mad_list,
234462306a36Sopenharmony_ci					   struct ib_mad_send_wr_private,
234562306a36Sopenharmony_ci					   mad_list);
234662306a36Sopenharmony_ci		mad_send_wr->retry = 1;
234762306a36Sopenharmony_ci	}
234862306a36Sopenharmony_ci	spin_unlock_irqrestore(&qp_info->send_queue.lock, flags);
234962306a36Sopenharmony_ci}
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_cistatic bool ib_mad_send_error(struct ib_mad_port_private *port_priv,
235262306a36Sopenharmony_ci		struct ib_wc *wc)
235362306a36Sopenharmony_ci{
235462306a36Sopenharmony_ci	struct ib_mad_list_head *mad_list =
235562306a36Sopenharmony_ci		container_of(wc->wr_cqe, struct ib_mad_list_head, cqe);
235662306a36Sopenharmony_ci	struct ib_mad_qp_info *qp_info = mad_list->mad_queue->qp_info;
235762306a36Sopenharmony_ci	struct ib_mad_send_wr_private *mad_send_wr;
235862306a36Sopenharmony_ci	int ret;
235962306a36Sopenharmony_ci
236062306a36Sopenharmony_ci	/*
236162306a36Sopenharmony_ci	 * Send errors will transition the QP to SQE - move
236262306a36Sopenharmony_ci	 * QP to RTS and repost flushed work requests
236362306a36Sopenharmony_ci	 */
236462306a36Sopenharmony_ci	mad_send_wr = container_of(mad_list, struct ib_mad_send_wr_private,
236562306a36Sopenharmony_ci				   mad_list);
236662306a36Sopenharmony_ci	if (wc->status == IB_WC_WR_FLUSH_ERR) {
236762306a36Sopenharmony_ci		if (mad_send_wr->retry) {
236862306a36Sopenharmony_ci			/* Repost send */
236962306a36Sopenharmony_ci			mad_send_wr->retry = 0;
237062306a36Sopenharmony_ci			trace_ib_mad_error_handler(mad_send_wr, qp_info);
237162306a36Sopenharmony_ci			ret = ib_post_send(qp_info->qp, &mad_send_wr->send_wr.wr,
237262306a36Sopenharmony_ci					   NULL);
237362306a36Sopenharmony_ci			if (!ret)
237462306a36Sopenharmony_ci				return false;
237562306a36Sopenharmony_ci		}
237662306a36Sopenharmony_ci	} else {
237762306a36Sopenharmony_ci		struct ib_qp_attr *attr;
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci		/* Transition QP to RTS and fail offending send */
238062306a36Sopenharmony_ci		attr = kmalloc(sizeof *attr, GFP_KERNEL);
238162306a36Sopenharmony_ci		if (attr) {
238262306a36Sopenharmony_ci			attr->qp_state = IB_QPS_RTS;
238362306a36Sopenharmony_ci			attr->cur_qp_state = IB_QPS_SQE;
238462306a36Sopenharmony_ci			ret = ib_modify_qp(qp_info->qp, attr,
238562306a36Sopenharmony_ci					   IB_QP_STATE | IB_QP_CUR_STATE);
238662306a36Sopenharmony_ci			kfree(attr);
238762306a36Sopenharmony_ci			if (ret)
238862306a36Sopenharmony_ci				dev_err(&port_priv->device->dev,
238962306a36Sopenharmony_ci					"%s - ib_modify_qp to RTS: %d\n",
239062306a36Sopenharmony_ci					__func__, ret);
239162306a36Sopenharmony_ci			else
239262306a36Sopenharmony_ci				mark_sends_for_retry(qp_info);
239362306a36Sopenharmony_ci		}
239462306a36Sopenharmony_ci	}
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_ci	return true;
239762306a36Sopenharmony_ci}
239862306a36Sopenharmony_ci
239962306a36Sopenharmony_cistatic void cancel_mads(struct ib_mad_agent_private *mad_agent_priv)
240062306a36Sopenharmony_ci{
240162306a36Sopenharmony_ci	unsigned long flags;
240262306a36Sopenharmony_ci	struct ib_mad_send_wr_private *mad_send_wr, *temp_mad_send_wr;
240362306a36Sopenharmony_ci	struct ib_mad_send_wc mad_send_wc;
240462306a36Sopenharmony_ci	struct list_head cancel_list;
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ci	INIT_LIST_HEAD(&cancel_list);
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_ci	spin_lock_irqsave(&mad_agent_priv->lock, flags);
240962306a36Sopenharmony_ci	list_for_each_entry_safe(mad_send_wr, temp_mad_send_wr,
241062306a36Sopenharmony_ci				 &mad_agent_priv->send_list, agent_list) {
241162306a36Sopenharmony_ci		if (mad_send_wr->status == IB_WC_SUCCESS) {
241262306a36Sopenharmony_ci			mad_send_wr->status = IB_WC_WR_FLUSH_ERR;
241362306a36Sopenharmony_ci			mad_send_wr->refcount -= (mad_send_wr->timeout > 0);
241462306a36Sopenharmony_ci		}
241562306a36Sopenharmony_ci	}
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci	/* Empty wait list to prevent receives from finding a request */
241862306a36Sopenharmony_ci	list_splice_init(&mad_agent_priv->wait_list, &cancel_list);
241962306a36Sopenharmony_ci	spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_ci	/* Report all cancelled requests */
242262306a36Sopenharmony_ci	mad_send_wc.status = IB_WC_WR_FLUSH_ERR;
242362306a36Sopenharmony_ci	mad_send_wc.vendor_err = 0;
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_ci	list_for_each_entry_safe(mad_send_wr, temp_mad_send_wr,
242662306a36Sopenharmony_ci				 &cancel_list, agent_list) {
242762306a36Sopenharmony_ci		mad_send_wc.send_buf = &mad_send_wr->send_buf;
242862306a36Sopenharmony_ci		list_del(&mad_send_wr->agent_list);
242962306a36Sopenharmony_ci		mad_agent_priv->agent.send_handler(&mad_agent_priv->agent,
243062306a36Sopenharmony_ci						   &mad_send_wc);
243162306a36Sopenharmony_ci		deref_mad_agent(mad_agent_priv);
243262306a36Sopenharmony_ci	}
243362306a36Sopenharmony_ci}
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_cistatic struct ib_mad_send_wr_private*
243662306a36Sopenharmony_cifind_send_wr(struct ib_mad_agent_private *mad_agent_priv,
243762306a36Sopenharmony_ci	     struct ib_mad_send_buf *send_buf)
243862306a36Sopenharmony_ci{
243962306a36Sopenharmony_ci	struct ib_mad_send_wr_private *mad_send_wr;
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	list_for_each_entry(mad_send_wr, &mad_agent_priv->wait_list,
244262306a36Sopenharmony_ci			    agent_list) {
244362306a36Sopenharmony_ci		if (&mad_send_wr->send_buf == send_buf)
244462306a36Sopenharmony_ci			return mad_send_wr;
244562306a36Sopenharmony_ci	}
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_ci	list_for_each_entry(mad_send_wr, &mad_agent_priv->send_list,
244862306a36Sopenharmony_ci			    agent_list) {
244962306a36Sopenharmony_ci		if (is_rmpp_data_mad(mad_agent_priv,
245062306a36Sopenharmony_ci				     mad_send_wr->send_buf.mad) &&
245162306a36Sopenharmony_ci		    &mad_send_wr->send_buf == send_buf)
245262306a36Sopenharmony_ci			return mad_send_wr;
245362306a36Sopenharmony_ci	}
245462306a36Sopenharmony_ci	return NULL;
245562306a36Sopenharmony_ci}
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_ciint ib_modify_mad(struct ib_mad_send_buf *send_buf, u32 timeout_ms)
245862306a36Sopenharmony_ci{
245962306a36Sopenharmony_ci	struct ib_mad_agent_private *mad_agent_priv;
246062306a36Sopenharmony_ci	struct ib_mad_send_wr_private *mad_send_wr;
246162306a36Sopenharmony_ci	unsigned long flags;
246262306a36Sopenharmony_ci	int active;
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_ci	if (!send_buf)
246562306a36Sopenharmony_ci		return -EINVAL;
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_ci	mad_agent_priv = container_of(send_buf->mad_agent,
246862306a36Sopenharmony_ci				      struct ib_mad_agent_private, agent);
246962306a36Sopenharmony_ci	spin_lock_irqsave(&mad_agent_priv->lock, flags);
247062306a36Sopenharmony_ci	mad_send_wr = find_send_wr(mad_agent_priv, send_buf);
247162306a36Sopenharmony_ci	if (!mad_send_wr || mad_send_wr->status != IB_WC_SUCCESS) {
247262306a36Sopenharmony_ci		spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
247362306a36Sopenharmony_ci		return -EINVAL;
247462306a36Sopenharmony_ci	}
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci	active = (!mad_send_wr->timeout || mad_send_wr->refcount > 1);
247762306a36Sopenharmony_ci	if (!timeout_ms) {
247862306a36Sopenharmony_ci		mad_send_wr->status = IB_WC_WR_FLUSH_ERR;
247962306a36Sopenharmony_ci		mad_send_wr->refcount -= (mad_send_wr->timeout > 0);
248062306a36Sopenharmony_ci	}
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	mad_send_wr->send_buf.timeout_ms = timeout_ms;
248362306a36Sopenharmony_ci	if (active)
248462306a36Sopenharmony_ci		mad_send_wr->timeout = msecs_to_jiffies(timeout_ms);
248562306a36Sopenharmony_ci	else
248662306a36Sopenharmony_ci		ib_reset_mad_timeout(mad_send_wr, timeout_ms);
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci	spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
248962306a36Sopenharmony_ci	return 0;
249062306a36Sopenharmony_ci}
249162306a36Sopenharmony_ciEXPORT_SYMBOL(ib_modify_mad);
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_cistatic void local_completions(struct work_struct *work)
249462306a36Sopenharmony_ci{
249562306a36Sopenharmony_ci	struct ib_mad_agent_private *mad_agent_priv;
249662306a36Sopenharmony_ci	struct ib_mad_local_private *local;
249762306a36Sopenharmony_ci	struct ib_mad_agent_private *recv_mad_agent;
249862306a36Sopenharmony_ci	unsigned long flags;
249962306a36Sopenharmony_ci	int free_mad;
250062306a36Sopenharmony_ci	struct ib_wc wc;
250162306a36Sopenharmony_ci	struct ib_mad_send_wc mad_send_wc;
250262306a36Sopenharmony_ci	bool opa;
250362306a36Sopenharmony_ci
250462306a36Sopenharmony_ci	mad_agent_priv =
250562306a36Sopenharmony_ci		container_of(work, struct ib_mad_agent_private, local_work);
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_ci	opa = rdma_cap_opa_mad(mad_agent_priv->qp_info->port_priv->device,
250862306a36Sopenharmony_ci			       mad_agent_priv->qp_info->port_priv->port_num);
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ci	spin_lock_irqsave(&mad_agent_priv->lock, flags);
251162306a36Sopenharmony_ci	while (!list_empty(&mad_agent_priv->local_list)) {
251262306a36Sopenharmony_ci		local = list_entry(mad_agent_priv->local_list.next,
251362306a36Sopenharmony_ci				   struct ib_mad_local_private,
251462306a36Sopenharmony_ci				   completion_list);
251562306a36Sopenharmony_ci		list_del(&local->completion_list);
251662306a36Sopenharmony_ci		spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
251762306a36Sopenharmony_ci		free_mad = 0;
251862306a36Sopenharmony_ci		if (local->mad_priv) {
251962306a36Sopenharmony_ci			u8 base_version;
252062306a36Sopenharmony_ci			recv_mad_agent = local->recv_mad_agent;
252162306a36Sopenharmony_ci			if (!recv_mad_agent) {
252262306a36Sopenharmony_ci				dev_err(&mad_agent_priv->agent.device->dev,
252362306a36Sopenharmony_ci					"No receive MAD agent for local completion\n");
252462306a36Sopenharmony_ci				free_mad = 1;
252562306a36Sopenharmony_ci				goto local_send_completion;
252662306a36Sopenharmony_ci			}
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci			/*
252962306a36Sopenharmony_ci			 * Defined behavior is to complete response
253062306a36Sopenharmony_ci			 * before request
253162306a36Sopenharmony_ci			 */
253262306a36Sopenharmony_ci			build_smp_wc(recv_mad_agent->agent.qp,
253362306a36Sopenharmony_ci				     local->mad_send_wr->send_wr.wr.wr_cqe,
253462306a36Sopenharmony_ci				     be16_to_cpu(IB_LID_PERMISSIVE),
253562306a36Sopenharmony_ci				     local->mad_send_wr->send_wr.pkey_index,
253662306a36Sopenharmony_ci				     recv_mad_agent->agent.port_num, &wc);
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci			local->mad_priv->header.recv_wc.wc = &wc;
253962306a36Sopenharmony_ci
254062306a36Sopenharmony_ci			base_version = ((struct ib_mad_hdr *)(local->mad_priv->mad))->base_version;
254162306a36Sopenharmony_ci			if (opa && base_version == OPA_MGMT_BASE_VERSION) {
254262306a36Sopenharmony_ci				local->mad_priv->header.recv_wc.mad_len = local->return_wc_byte_len;
254362306a36Sopenharmony_ci				local->mad_priv->header.recv_wc.mad_seg_size = sizeof(struct opa_mad);
254462306a36Sopenharmony_ci			} else {
254562306a36Sopenharmony_ci				local->mad_priv->header.recv_wc.mad_len = sizeof(struct ib_mad);
254662306a36Sopenharmony_ci				local->mad_priv->header.recv_wc.mad_seg_size = sizeof(struct ib_mad);
254762306a36Sopenharmony_ci			}
254862306a36Sopenharmony_ci
254962306a36Sopenharmony_ci			INIT_LIST_HEAD(&local->mad_priv->header.recv_wc.rmpp_list);
255062306a36Sopenharmony_ci			list_add(&local->mad_priv->header.recv_wc.recv_buf.list,
255162306a36Sopenharmony_ci				 &local->mad_priv->header.recv_wc.rmpp_list);
255262306a36Sopenharmony_ci			local->mad_priv->header.recv_wc.recv_buf.grh = NULL;
255362306a36Sopenharmony_ci			local->mad_priv->header.recv_wc.recv_buf.mad =
255462306a36Sopenharmony_ci						(struct ib_mad *)local->mad_priv->mad;
255562306a36Sopenharmony_ci			recv_mad_agent->agent.recv_handler(
255662306a36Sopenharmony_ci						&recv_mad_agent->agent,
255762306a36Sopenharmony_ci						&local->mad_send_wr->send_buf,
255862306a36Sopenharmony_ci						&local->mad_priv->header.recv_wc);
255962306a36Sopenharmony_ci			spin_lock_irqsave(&recv_mad_agent->lock, flags);
256062306a36Sopenharmony_ci			deref_mad_agent(recv_mad_agent);
256162306a36Sopenharmony_ci			spin_unlock_irqrestore(&recv_mad_agent->lock, flags);
256262306a36Sopenharmony_ci		}
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_cilocal_send_completion:
256562306a36Sopenharmony_ci		/* Complete send */
256662306a36Sopenharmony_ci		mad_send_wc.status = IB_WC_SUCCESS;
256762306a36Sopenharmony_ci		mad_send_wc.vendor_err = 0;
256862306a36Sopenharmony_ci		mad_send_wc.send_buf = &local->mad_send_wr->send_buf;
256962306a36Sopenharmony_ci		mad_agent_priv->agent.send_handler(&mad_agent_priv->agent,
257062306a36Sopenharmony_ci						   &mad_send_wc);
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci		spin_lock_irqsave(&mad_agent_priv->lock, flags);
257362306a36Sopenharmony_ci		deref_mad_agent(mad_agent_priv);
257462306a36Sopenharmony_ci		if (free_mad)
257562306a36Sopenharmony_ci			kfree(local->mad_priv);
257662306a36Sopenharmony_ci		kfree(local);
257762306a36Sopenharmony_ci	}
257862306a36Sopenharmony_ci	spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
257962306a36Sopenharmony_ci}
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_cistatic int retry_send(struct ib_mad_send_wr_private *mad_send_wr)
258262306a36Sopenharmony_ci{
258362306a36Sopenharmony_ci	int ret;
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci	if (!mad_send_wr->retries_left)
258662306a36Sopenharmony_ci		return -ETIMEDOUT;
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	mad_send_wr->retries_left--;
258962306a36Sopenharmony_ci	mad_send_wr->send_buf.retries++;
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_ci	mad_send_wr->timeout = msecs_to_jiffies(mad_send_wr->send_buf.timeout_ms);
259262306a36Sopenharmony_ci
259362306a36Sopenharmony_ci	if (ib_mad_kernel_rmpp_agent(&mad_send_wr->mad_agent_priv->agent)) {
259462306a36Sopenharmony_ci		ret = ib_retry_rmpp(mad_send_wr);
259562306a36Sopenharmony_ci		switch (ret) {
259662306a36Sopenharmony_ci		case IB_RMPP_RESULT_UNHANDLED:
259762306a36Sopenharmony_ci			ret = ib_send_mad(mad_send_wr);
259862306a36Sopenharmony_ci			break;
259962306a36Sopenharmony_ci		case IB_RMPP_RESULT_CONSUMED:
260062306a36Sopenharmony_ci			ret = 0;
260162306a36Sopenharmony_ci			break;
260262306a36Sopenharmony_ci		default:
260362306a36Sopenharmony_ci			ret = -ECOMM;
260462306a36Sopenharmony_ci			break;
260562306a36Sopenharmony_ci		}
260662306a36Sopenharmony_ci	} else
260762306a36Sopenharmony_ci		ret = ib_send_mad(mad_send_wr);
260862306a36Sopenharmony_ci
260962306a36Sopenharmony_ci	if (!ret) {
261062306a36Sopenharmony_ci		mad_send_wr->refcount++;
261162306a36Sopenharmony_ci		list_add_tail(&mad_send_wr->agent_list,
261262306a36Sopenharmony_ci			      &mad_send_wr->mad_agent_priv->send_list);
261362306a36Sopenharmony_ci	}
261462306a36Sopenharmony_ci	return ret;
261562306a36Sopenharmony_ci}
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_cistatic void timeout_sends(struct work_struct *work)
261862306a36Sopenharmony_ci{
261962306a36Sopenharmony_ci	struct ib_mad_agent_private *mad_agent_priv;
262062306a36Sopenharmony_ci	struct ib_mad_send_wr_private *mad_send_wr;
262162306a36Sopenharmony_ci	struct ib_mad_send_wc mad_send_wc;
262262306a36Sopenharmony_ci	unsigned long flags, delay;
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_ci	mad_agent_priv = container_of(work, struct ib_mad_agent_private,
262562306a36Sopenharmony_ci				      timed_work.work);
262662306a36Sopenharmony_ci	mad_send_wc.vendor_err = 0;
262762306a36Sopenharmony_ci
262862306a36Sopenharmony_ci	spin_lock_irqsave(&mad_agent_priv->lock, flags);
262962306a36Sopenharmony_ci	while (!list_empty(&mad_agent_priv->wait_list)) {
263062306a36Sopenharmony_ci		mad_send_wr = list_entry(mad_agent_priv->wait_list.next,
263162306a36Sopenharmony_ci					 struct ib_mad_send_wr_private,
263262306a36Sopenharmony_ci					 agent_list);
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci		if (time_after(mad_send_wr->timeout, jiffies)) {
263562306a36Sopenharmony_ci			delay = mad_send_wr->timeout - jiffies;
263662306a36Sopenharmony_ci			if ((long)delay <= 0)
263762306a36Sopenharmony_ci				delay = 1;
263862306a36Sopenharmony_ci			queue_delayed_work(mad_agent_priv->qp_info->
263962306a36Sopenharmony_ci					   port_priv->wq,
264062306a36Sopenharmony_ci					   &mad_agent_priv->timed_work, delay);
264162306a36Sopenharmony_ci			break;
264262306a36Sopenharmony_ci		}
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci		list_del(&mad_send_wr->agent_list);
264562306a36Sopenharmony_ci		if (mad_send_wr->status == IB_WC_SUCCESS &&
264662306a36Sopenharmony_ci		    !retry_send(mad_send_wr))
264762306a36Sopenharmony_ci			continue;
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci		spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_ci		if (mad_send_wr->status == IB_WC_SUCCESS)
265262306a36Sopenharmony_ci			mad_send_wc.status = IB_WC_RESP_TIMEOUT_ERR;
265362306a36Sopenharmony_ci		else
265462306a36Sopenharmony_ci			mad_send_wc.status = mad_send_wr->status;
265562306a36Sopenharmony_ci		mad_send_wc.send_buf = &mad_send_wr->send_buf;
265662306a36Sopenharmony_ci		mad_agent_priv->agent.send_handler(&mad_agent_priv->agent,
265762306a36Sopenharmony_ci						   &mad_send_wc);
265862306a36Sopenharmony_ci
265962306a36Sopenharmony_ci		deref_mad_agent(mad_agent_priv);
266062306a36Sopenharmony_ci		spin_lock_irqsave(&mad_agent_priv->lock, flags);
266162306a36Sopenharmony_ci	}
266262306a36Sopenharmony_ci	spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
266362306a36Sopenharmony_ci}
266462306a36Sopenharmony_ci
266562306a36Sopenharmony_ci/*
266662306a36Sopenharmony_ci * Allocate receive MADs and post receive WRs for them
266762306a36Sopenharmony_ci */
266862306a36Sopenharmony_cistatic int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
266962306a36Sopenharmony_ci				    struct ib_mad_private *mad)
267062306a36Sopenharmony_ci{
267162306a36Sopenharmony_ci	unsigned long flags;
267262306a36Sopenharmony_ci	int post, ret;
267362306a36Sopenharmony_ci	struct ib_mad_private *mad_priv;
267462306a36Sopenharmony_ci	struct ib_sge sg_list;
267562306a36Sopenharmony_ci	struct ib_recv_wr recv_wr;
267662306a36Sopenharmony_ci	struct ib_mad_queue *recv_queue = &qp_info->recv_queue;
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	/* Initialize common scatter list fields */
267962306a36Sopenharmony_ci	sg_list.lkey = qp_info->port_priv->pd->local_dma_lkey;
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci	/* Initialize common receive WR fields */
268262306a36Sopenharmony_ci	recv_wr.next = NULL;
268362306a36Sopenharmony_ci	recv_wr.sg_list = &sg_list;
268462306a36Sopenharmony_ci	recv_wr.num_sge = 1;
268562306a36Sopenharmony_ci
268662306a36Sopenharmony_ci	do {
268762306a36Sopenharmony_ci		/* Allocate and map receive buffer */
268862306a36Sopenharmony_ci		if (mad) {
268962306a36Sopenharmony_ci			mad_priv = mad;
269062306a36Sopenharmony_ci			mad = NULL;
269162306a36Sopenharmony_ci		} else {
269262306a36Sopenharmony_ci			mad_priv = alloc_mad_private(port_mad_size(qp_info->port_priv),
269362306a36Sopenharmony_ci						     GFP_ATOMIC);
269462306a36Sopenharmony_ci			if (!mad_priv) {
269562306a36Sopenharmony_ci				ret = -ENOMEM;
269662306a36Sopenharmony_ci				break;
269762306a36Sopenharmony_ci			}
269862306a36Sopenharmony_ci		}
269962306a36Sopenharmony_ci		sg_list.length = mad_priv_dma_size(mad_priv);
270062306a36Sopenharmony_ci		sg_list.addr = ib_dma_map_single(qp_info->port_priv->device,
270162306a36Sopenharmony_ci						 &mad_priv->grh,
270262306a36Sopenharmony_ci						 mad_priv_dma_size(mad_priv),
270362306a36Sopenharmony_ci						 DMA_FROM_DEVICE);
270462306a36Sopenharmony_ci		if (unlikely(ib_dma_mapping_error(qp_info->port_priv->device,
270562306a36Sopenharmony_ci						  sg_list.addr))) {
270662306a36Sopenharmony_ci			kfree(mad_priv);
270762306a36Sopenharmony_ci			ret = -ENOMEM;
270862306a36Sopenharmony_ci			break;
270962306a36Sopenharmony_ci		}
271062306a36Sopenharmony_ci		mad_priv->header.mapping = sg_list.addr;
271162306a36Sopenharmony_ci		mad_priv->header.mad_list.mad_queue = recv_queue;
271262306a36Sopenharmony_ci		mad_priv->header.mad_list.cqe.done = ib_mad_recv_done;
271362306a36Sopenharmony_ci		recv_wr.wr_cqe = &mad_priv->header.mad_list.cqe;
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_ci		/* Post receive WR */
271662306a36Sopenharmony_ci		spin_lock_irqsave(&recv_queue->lock, flags);
271762306a36Sopenharmony_ci		post = (++recv_queue->count < recv_queue->max_active);
271862306a36Sopenharmony_ci		list_add_tail(&mad_priv->header.mad_list.list, &recv_queue->list);
271962306a36Sopenharmony_ci		spin_unlock_irqrestore(&recv_queue->lock, flags);
272062306a36Sopenharmony_ci		ret = ib_post_recv(qp_info->qp, &recv_wr, NULL);
272162306a36Sopenharmony_ci		if (ret) {
272262306a36Sopenharmony_ci			spin_lock_irqsave(&recv_queue->lock, flags);
272362306a36Sopenharmony_ci			list_del(&mad_priv->header.mad_list.list);
272462306a36Sopenharmony_ci			recv_queue->count--;
272562306a36Sopenharmony_ci			spin_unlock_irqrestore(&recv_queue->lock, flags);
272662306a36Sopenharmony_ci			ib_dma_unmap_single(qp_info->port_priv->device,
272762306a36Sopenharmony_ci					    mad_priv->header.mapping,
272862306a36Sopenharmony_ci					    mad_priv_dma_size(mad_priv),
272962306a36Sopenharmony_ci					    DMA_FROM_DEVICE);
273062306a36Sopenharmony_ci			kfree(mad_priv);
273162306a36Sopenharmony_ci			dev_err(&qp_info->port_priv->device->dev,
273262306a36Sopenharmony_ci				"ib_post_recv failed: %d\n", ret);
273362306a36Sopenharmony_ci			break;
273462306a36Sopenharmony_ci		}
273562306a36Sopenharmony_ci	} while (post);
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_ci	return ret;
273862306a36Sopenharmony_ci}
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci/*
274162306a36Sopenharmony_ci * Return all the posted receive MADs
274262306a36Sopenharmony_ci */
274362306a36Sopenharmony_cistatic void cleanup_recv_queue(struct ib_mad_qp_info *qp_info)
274462306a36Sopenharmony_ci{
274562306a36Sopenharmony_ci	struct ib_mad_private_header *mad_priv_hdr;
274662306a36Sopenharmony_ci	struct ib_mad_private *recv;
274762306a36Sopenharmony_ci	struct ib_mad_list_head *mad_list;
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_ci	if (!qp_info->qp)
275062306a36Sopenharmony_ci		return;
275162306a36Sopenharmony_ci
275262306a36Sopenharmony_ci	while (!list_empty(&qp_info->recv_queue.list)) {
275362306a36Sopenharmony_ci
275462306a36Sopenharmony_ci		mad_list = list_entry(qp_info->recv_queue.list.next,
275562306a36Sopenharmony_ci				      struct ib_mad_list_head, list);
275662306a36Sopenharmony_ci		mad_priv_hdr = container_of(mad_list,
275762306a36Sopenharmony_ci					    struct ib_mad_private_header,
275862306a36Sopenharmony_ci					    mad_list);
275962306a36Sopenharmony_ci		recv = container_of(mad_priv_hdr, struct ib_mad_private,
276062306a36Sopenharmony_ci				    header);
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci		/* Remove from posted receive MAD list */
276362306a36Sopenharmony_ci		list_del(&mad_list->list);
276462306a36Sopenharmony_ci
276562306a36Sopenharmony_ci		ib_dma_unmap_single(qp_info->port_priv->device,
276662306a36Sopenharmony_ci				    recv->header.mapping,
276762306a36Sopenharmony_ci				    mad_priv_dma_size(recv),
276862306a36Sopenharmony_ci				    DMA_FROM_DEVICE);
276962306a36Sopenharmony_ci		kfree(recv);
277062306a36Sopenharmony_ci	}
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci	qp_info->recv_queue.count = 0;
277362306a36Sopenharmony_ci}
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_ci/*
277662306a36Sopenharmony_ci * Start the port
277762306a36Sopenharmony_ci */
277862306a36Sopenharmony_cistatic int ib_mad_port_start(struct ib_mad_port_private *port_priv)
277962306a36Sopenharmony_ci{
278062306a36Sopenharmony_ci	int ret, i;
278162306a36Sopenharmony_ci	struct ib_qp_attr *attr;
278262306a36Sopenharmony_ci	struct ib_qp *qp;
278362306a36Sopenharmony_ci	u16 pkey_index;
278462306a36Sopenharmony_ci
278562306a36Sopenharmony_ci	attr = kmalloc(sizeof *attr, GFP_KERNEL);
278662306a36Sopenharmony_ci	if (!attr)
278762306a36Sopenharmony_ci		return -ENOMEM;
278862306a36Sopenharmony_ci
278962306a36Sopenharmony_ci	ret = ib_find_pkey(port_priv->device, port_priv->port_num,
279062306a36Sopenharmony_ci			   IB_DEFAULT_PKEY_FULL, &pkey_index);
279162306a36Sopenharmony_ci	if (ret)
279262306a36Sopenharmony_ci		pkey_index = 0;
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci	for (i = 0; i < IB_MAD_QPS_CORE; i++) {
279562306a36Sopenharmony_ci		qp = port_priv->qp_info[i].qp;
279662306a36Sopenharmony_ci		if (!qp)
279762306a36Sopenharmony_ci			continue;
279862306a36Sopenharmony_ci
279962306a36Sopenharmony_ci		/*
280062306a36Sopenharmony_ci		 * PKey index for QP1 is irrelevant but
280162306a36Sopenharmony_ci		 * one is needed for the Reset to Init transition
280262306a36Sopenharmony_ci		 */
280362306a36Sopenharmony_ci		attr->qp_state = IB_QPS_INIT;
280462306a36Sopenharmony_ci		attr->pkey_index = pkey_index;
280562306a36Sopenharmony_ci		attr->qkey = (qp->qp_num == 0) ? 0 : IB_QP1_QKEY;
280662306a36Sopenharmony_ci		ret = ib_modify_qp(qp, attr, IB_QP_STATE |
280762306a36Sopenharmony_ci					     IB_QP_PKEY_INDEX | IB_QP_QKEY);
280862306a36Sopenharmony_ci		if (ret) {
280962306a36Sopenharmony_ci			dev_err(&port_priv->device->dev,
281062306a36Sopenharmony_ci				"Couldn't change QP%d state to INIT: %d\n",
281162306a36Sopenharmony_ci				i, ret);
281262306a36Sopenharmony_ci			goto out;
281362306a36Sopenharmony_ci		}
281462306a36Sopenharmony_ci
281562306a36Sopenharmony_ci		attr->qp_state = IB_QPS_RTR;
281662306a36Sopenharmony_ci		ret = ib_modify_qp(qp, attr, IB_QP_STATE);
281762306a36Sopenharmony_ci		if (ret) {
281862306a36Sopenharmony_ci			dev_err(&port_priv->device->dev,
281962306a36Sopenharmony_ci				"Couldn't change QP%d state to RTR: %d\n",
282062306a36Sopenharmony_ci				i, ret);
282162306a36Sopenharmony_ci			goto out;
282262306a36Sopenharmony_ci		}
282362306a36Sopenharmony_ci
282462306a36Sopenharmony_ci		attr->qp_state = IB_QPS_RTS;
282562306a36Sopenharmony_ci		attr->sq_psn = IB_MAD_SEND_Q_PSN;
282662306a36Sopenharmony_ci		ret = ib_modify_qp(qp, attr, IB_QP_STATE | IB_QP_SQ_PSN);
282762306a36Sopenharmony_ci		if (ret) {
282862306a36Sopenharmony_ci			dev_err(&port_priv->device->dev,
282962306a36Sopenharmony_ci				"Couldn't change QP%d state to RTS: %d\n",
283062306a36Sopenharmony_ci				i, ret);
283162306a36Sopenharmony_ci			goto out;
283262306a36Sopenharmony_ci		}
283362306a36Sopenharmony_ci	}
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci	ret = ib_req_notify_cq(port_priv->cq, IB_CQ_NEXT_COMP);
283662306a36Sopenharmony_ci	if (ret) {
283762306a36Sopenharmony_ci		dev_err(&port_priv->device->dev,
283862306a36Sopenharmony_ci			"Failed to request completion notification: %d\n",
283962306a36Sopenharmony_ci			ret);
284062306a36Sopenharmony_ci		goto out;
284162306a36Sopenharmony_ci	}
284262306a36Sopenharmony_ci
284362306a36Sopenharmony_ci	for (i = 0; i < IB_MAD_QPS_CORE; i++) {
284462306a36Sopenharmony_ci		if (!port_priv->qp_info[i].qp)
284562306a36Sopenharmony_ci			continue;
284662306a36Sopenharmony_ci
284762306a36Sopenharmony_ci		ret = ib_mad_post_receive_mads(&port_priv->qp_info[i], NULL);
284862306a36Sopenharmony_ci		if (ret) {
284962306a36Sopenharmony_ci			dev_err(&port_priv->device->dev,
285062306a36Sopenharmony_ci				"Couldn't post receive WRs\n");
285162306a36Sopenharmony_ci			goto out;
285262306a36Sopenharmony_ci		}
285362306a36Sopenharmony_ci	}
285462306a36Sopenharmony_ciout:
285562306a36Sopenharmony_ci	kfree(attr);
285662306a36Sopenharmony_ci	return ret;
285762306a36Sopenharmony_ci}
285862306a36Sopenharmony_ci
285962306a36Sopenharmony_cistatic void qp_event_handler(struct ib_event *event, void *qp_context)
286062306a36Sopenharmony_ci{
286162306a36Sopenharmony_ci	struct ib_mad_qp_info	*qp_info = qp_context;
286262306a36Sopenharmony_ci
286362306a36Sopenharmony_ci	/* It's worse than that! He's dead, Jim! */
286462306a36Sopenharmony_ci	dev_err(&qp_info->port_priv->device->dev,
286562306a36Sopenharmony_ci		"Fatal error (%d) on MAD QP (%u)\n",
286662306a36Sopenharmony_ci		event->event, qp_info->qp->qp_num);
286762306a36Sopenharmony_ci}
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_cistatic void init_mad_queue(struct ib_mad_qp_info *qp_info,
287062306a36Sopenharmony_ci			   struct ib_mad_queue *mad_queue)
287162306a36Sopenharmony_ci{
287262306a36Sopenharmony_ci	mad_queue->qp_info = qp_info;
287362306a36Sopenharmony_ci	mad_queue->count = 0;
287462306a36Sopenharmony_ci	spin_lock_init(&mad_queue->lock);
287562306a36Sopenharmony_ci	INIT_LIST_HEAD(&mad_queue->list);
287662306a36Sopenharmony_ci}
287762306a36Sopenharmony_ci
287862306a36Sopenharmony_cistatic void init_mad_qp(struct ib_mad_port_private *port_priv,
287962306a36Sopenharmony_ci			struct ib_mad_qp_info *qp_info)
288062306a36Sopenharmony_ci{
288162306a36Sopenharmony_ci	qp_info->port_priv = port_priv;
288262306a36Sopenharmony_ci	init_mad_queue(qp_info, &qp_info->send_queue);
288362306a36Sopenharmony_ci	init_mad_queue(qp_info, &qp_info->recv_queue);
288462306a36Sopenharmony_ci	INIT_LIST_HEAD(&qp_info->overflow_list);
288562306a36Sopenharmony_ci}
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_cistatic int create_mad_qp(struct ib_mad_qp_info *qp_info,
288862306a36Sopenharmony_ci			 enum ib_qp_type qp_type)
288962306a36Sopenharmony_ci{
289062306a36Sopenharmony_ci	struct ib_qp_init_attr	qp_init_attr;
289162306a36Sopenharmony_ci	int ret;
289262306a36Sopenharmony_ci
289362306a36Sopenharmony_ci	memset(&qp_init_attr, 0, sizeof qp_init_attr);
289462306a36Sopenharmony_ci	qp_init_attr.send_cq = qp_info->port_priv->cq;
289562306a36Sopenharmony_ci	qp_init_attr.recv_cq = qp_info->port_priv->cq;
289662306a36Sopenharmony_ci	qp_init_attr.sq_sig_type = IB_SIGNAL_ALL_WR;
289762306a36Sopenharmony_ci	qp_init_attr.cap.max_send_wr = mad_sendq_size;
289862306a36Sopenharmony_ci	qp_init_attr.cap.max_recv_wr = mad_recvq_size;
289962306a36Sopenharmony_ci	qp_init_attr.cap.max_send_sge = IB_MAD_SEND_REQ_MAX_SG;
290062306a36Sopenharmony_ci	qp_init_attr.cap.max_recv_sge = IB_MAD_RECV_REQ_MAX_SG;
290162306a36Sopenharmony_ci	qp_init_attr.qp_type = qp_type;
290262306a36Sopenharmony_ci	qp_init_attr.port_num = qp_info->port_priv->port_num;
290362306a36Sopenharmony_ci	qp_init_attr.qp_context = qp_info;
290462306a36Sopenharmony_ci	qp_init_attr.event_handler = qp_event_handler;
290562306a36Sopenharmony_ci	qp_info->qp = ib_create_qp(qp_info->port_priv->pd, &qp_init_attr);
290662306a36Sopenharmony_ci	if (IS_ERR(qp_info->qp)) {
290762306a36Sopenharmony_ci		dev_err(&qp_info->port_priv->device->dev,
290862306a36Sopenharmony_ci			"Couldn't create ib_mad QP%d\n",
290962306a36Sopenharmony_ci			get_spl_qp_index(qp_type));
291062306a36Sopenharmony_ci		ret = PTR_ERR(qp_info->qp);
291162306a36Sopenharmony_ci		goto error;
291262306a36Sopenharmony_ci	}
291362306a36Sopenharmony_ci	/* Use minimum queue sizes unless the CQ is resized */
291462306a36Sopenharmony_ci	qp_info->send_queue.max_active = mad_sendq_size;
291562306a36Sopenharmony_ci	qp_info->recv_queue.max_active = mad_recvq_size;
291662306a36Sopenharmony_ci	return 0;
291762306a36Sopenharmony_ci
291862306a36Sopenharmony_cierror:
291962306a36Sopenharmony_ci	return ret;
292062306a36Sopenharmony_ci}
292162306a36Sopenharmony_ci
292262306a36Sopenharmony_cistatic void destroy_mad_qp(struct ib_mad_qp_info *qp_info)
292362306a36Sopenharmony_ci{
292462306a36Sopenharmony_ci	if (!qp_info->qp)
292562306a36Sopenharmony_ci		return;
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_ci	ib_destroy_qp(qp_info->qp);
292862306a36Sopenharmony_ci}
292962306a36Sopenharmony_ci
293062306a36Sopenharmony_ci/*
293162306a36Sopenharmony_ci * Open the port
293262306a36Sopenharmony_ci * Create the QP, PD, MR, and CQ if needed
293362306a36Sopenharmony_ci */
293462306a36Sopenharmony_cistatic int ib_mad_port_open(struct ib_device *device,
293562306a36Sopenharmony_ci			    u32 port_num)
293662306a36Sopenharmony_ci{
293762306a36Sopenharmony_ci	int ret, cq_size;
293862306a36Sopenharmony_ci	struct ib_mad_port_private *port_priv;
293962306a36Sopenharmony_ci	unsigned long flags;
294062306a36Sopenharmony_ci	char name[sizeof "ib_mad123"];
294162306a36Sopenharmony_ci	int has_smi;
294262306a36Sopenharmony_ci
294362306a36Sopenharmony_ci	if (WARN_ON(rdma_max_mad_size(device, port_num) < IB_MGMT_MAD_SIZE))
294462306a36Sopenharmony_ci		return -EFAULT;
294562306a36Sopenharmony_ci
294662306a36Sopenharmony_ci	if (WARN_ON(rdma_cap_opa_mad(device, port_num) &&
294762306a36Sopenharmony_ci		    rdma_max_mad_size(device, port_num) < OPA_MGMT_MAD_SIZE))
294862306a36Sopenharmony_ci		return -EFAULT;
294962306a36Sopenharmony_ci
295062306a36Sopenharmony_ci	/* Create new device info */
295162306a36Sopenharmony_ci	port_priv = kzalloc(sizeof *port_priv, GFP_KERNEL);
295262306a36Sopenharmony_ci	if (!port_priv)
295362306a36Sopenharmony_ci		return -ENOMEM;
295462306a36Sopenharmony_ci
295562306a36Sopenharmony_ci	port_priv->device = device;
295662306a36Sopenharmony_ci	port_priv->port_num = port_num;
295762306a36Sopenharmony_ci	spin_lock_init(&port_priv->reg_lock);
295862306a36Sopenharmony_ci	init_mad_qp(port_priv, &port_priv->qp_info[0]);
295962306a36Sopenharmony_ci	init_mad_qp(port_priv, &port_priv->qp_info[1]);
296062306a36Sopenharmony_ci
296162306a36Sopenharmony_ci	cq_size = mad_sendq_size + mad_recvq_size;
296262306a36Sopenharmony_ci	has_smi = rdma_cap_ib_smi(device, port_num);
296362306a36Sopenharmony_ci	if (has_smi)
296462306a36Sopenharmony_ci		cq_size *= 2;
296562306a36Sopenharmony_ci
296662306a36Sopenharmony_ci	port_priv->pd = ib_alloc_pd(device, 0);
296762306a36Sopenharmony_ci	if (IS_ERR(port_priv->pd)) {
296862306a36Sopenharmony_ci		dev_err(&device->dev, "Couldn't create ib_mad PD\n");
296962306a36Sopenharmony_ci		ret = PTR_ERR(port_priv->pd);
297062306a36Sopenharmony_ci		goto error3;
297162306a36Sopenharmony_ci	}
297262306a36Sopenharmony_ci
297362306a36Sopenharmony_ci	port_priv->cq = ib_alloc_cq(port_priv->device, port_priv, cq_size, 0,
297462306a36Sopenharmony_ci			IB_POLL_UNBOUND_WORKQUEUE);
297562306a36Sopenharmony_ci	if (IS_ERR(port_priv->cq)) {
297662306a36Sopenharmony_ci		dev_err(&device->dev, "Couldn't create ib_mad CQ\n");
297762306a36Sopenharmony_ci		ret = PTR_ERR(port_priv->cq);
297862306a36Sopenharmony_ci		goto error4;
297962306a36Sopenharmony_ci	}
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_ci	if (has_smi) {
298262306a36Sopenharmony_ci		ret = create_mad_qp(&port_priv->qp_info[0], IB_QPT_SMI);
298362306a36Sopenharmony_ci		if (ret)
298462306a36Sopenharmony_ci			goto error6;
298562306a36Sopenharmony_ci	}
298662306a36Sopenharmony_ci	ret = create_mad_qp(&port_priv->qp_info[1], IB_QPT_GSI);
298762306a36Sopenharmony_ci	if (ret)
298862306a36Sopenharmony_ci		goto error7;
298962306a36Sopenharmony_ci
299062306a36Sopenharmony_ci	snprintf(name, sizeof(name), "ib_mad%u", port_num);
299162306a36Sopenharmony_ci	port_priv->wq = alloc_ordered_workqueue(name, WQ_MEM_RECLAIM);
299262306a36Sopenharmony_ci	if (!port_priv->wq) {
299362306a36Sopenharmony_ci		ret = -ENOMEM;
299462306a36Sopenharmony_ci		goto error8;
299562306a36Sopenharmony_ci	}
299662306a36Sopenharmony_ci
299762306a36Sopenharmony_ci	spin_lock_irqsave(&ib_mad_port_list_lock, flags);
299862306a36Sopenharmony_ci	list_add_tail(&port_priv->port_list, &ib_mad_port_list);
299962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ib_mad_port_list_lock, flags);
300062306a36Sopenharmony_ci
300162306a36Sopenharmony_ci	ret = ib_mad_port_start(port_priv);
300262306a36Sopenharmony_ci	if (ret) {
300362306a36Sopenharmony_ci		dev_err(&device->dev, "Couldn't start port\n");
300462306a36Sopenharmony_ci		goto error9;
300562306a36Sopenharmony_ci	}
300662306a36Sopenharmony_ci
300762306a36Sopenharmony_ci	return 0;
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_cierror9:
301062306a36Sopenharmony_ci	spin_lock_irqsave(&ib_mad_port_list_lock, flags);
301162306a36Sopenharmony_ci	list_del_init(&port_priv->port_list);
301262306a36Sopenharmony_ci	spin_unlock_irqrestore(&ib_mad_port_list_lock, flags);
301362306a36Sopenharmony_ci
301462306a36Sopenharmony_ci	destroy_workqueue(port_priv->wq);
301562306a36Sopenharmony_cierror8:
301662306a36Sopenharmony_ci	destroy_mad_qp(&port_priv->qp_info[1]);
301762306a36Sopenharmony_cierror7:
301862306a36Sopenharmony_ci	destroy_mad_qp(&port_priv->qp_info[0]);
301962306a36Sopenharmony_cierror6:
302062306a36Sopenharmony_ci	ib_free_cq(port_priv->cq);
302162306a36Sopenharmony_ci	cleanup_recv_queue(&port_priv->qp_info[1]);
302262306a36Sopenharmony_ci	cleanup_recv_queue(&port_priv->qp_info[0]);
302362306a36Sopenharmony_cierror4:
302462306a36Sopenharmony_ci	ib_dealloc_pd(port_priv->pd);
302562306a36Sopenharmony_cierror3:
302662306a36Sopenharmony_ci	kfree(port_priv);
302762306a36Sopenharmony_ci
302862306a36Sopenharmony_ci	return ret;
302962306a36Sopenharmony_ci}
303062306a36Sopenharmony_ci
303162306a36Sopenharmony_ci/*
303262306a36Sopenharmony_ci * Close the port
303362306a36Sopenharmony_ci * If there are no classes using the port, free the port
303462306a36Sopenharmony_ci * resources (CQ, MR, PD, QP) and remove the port's info structure
303562306a36Sopenharmony_ci */
303662306a36Sopenharmony_cistatic int ib_mad_port_close(struct ib_device *device, u32 port_num)
303762306a36Sopenharmony_ci{
303862306a36Sopenharmony_ci	struct ib_mad_port_private *port_priv;
303962306a36Sopenharmony_ci	unsigned long flags;
304062306a36Sopenharmony_ci
304162306a36Sopenharmony_ci	spin_lock_irqsave(&ib_mad_port_list_lock, flags);
304262306a36Sopenharmony_ci	port_priv = __ib_get_mad_port(device, port_num);
304362306a36Sopenharmony_ci	if (port_priv == NULL) {
304462306a36Sopenharmony_ci		spin_unlock_irqrestore(&ib_mad_port_list_lock, flags);
304562306a36Sopenharmony_ci		dev_err(&device->dev, "Port %u not found\n", port_num);
304662306a36Sopenharmony_ci		return -ENODEV;
304762306a36Sopenharmony_ci	}
304862306a36Sopenharmony_ci	list_del_init(&port_priv->port_list);
304962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ib_mad_port_list_lock, flags);
305062306a36Sopenharmony_ci
305162306a36Sopenharmony_ci	destroy_workqueue(port_priv->wq);
305262306a36Sopenharmony_ci	destroy_mad_qp(&port_priv->qp_info[1]);
305362306a36Sopenharmony_ci	destroy_mad_qp(&port_priv->qp_info[0]);
305462306a36Sopenharmony_ci	ib_free_cq(port_priv->cq);
305562306a36Sopenharmony_ci	ib_dealloc_pd(port_priv->pd);
305662306a36Sopenharmony_ci	cleanup_recv_queue(&port_priv->qp_info[1]);
305762306a36Sopenharmony_ci	cleanup_recv_queue(&port_priv->qp_info[0]);
305862306a36Sopenharmony_ci	/* XXX: Handle deallocation of MAD registration tables */
305962306a36Sopenharmony_ci
306062306a36Sopenharmony_ci	kfree(port_priv);
306162306a36Sopenharmony_ci
306262306a36Sopenharmony_ci	return 0;
306362306a36Sopenharmony_ci}
306462306a36Sopenharmony_ci
306562306a36Sopenharmony_cistatic int ib_mad_init_device(struct ib_device *device)
306662306a36Sopenharmony_ci{
306762306a36Sopenharmony_ci	int start, i;
306862306a36Sopenharmony_ci	unsigned int count = 0;
306962306a36Sopenharmony_ci	int ret;
307062306a36Sopenharmony_ci
307162306a36Sopenharmony_ci	start = rdma_start_port(device);
307262306a36Sopenharmony_ci
307362306a36Sopenharmony_ci	for (i = start; i <= rdma_end_port(device); i++) {
307462306a36Sopenharmony_ci		if (!rdma_cap_ib_mad(device, i))
307562306a36Sopenharmony_ci			continue;
307662306a36Sopenharmony_ci
307762306a36Sopenharmony_ci		ret = ib_mad_port_open(device, i);
307862306a36Sopenharmony_ci		if (ret) {
307962306a36Sopenharmony_ci			dev_err(&device->dev, "Couldn't open port %d\n", i);
308062306a36Sopenharmony_ci			goto error;
308162306a36Sopenharmony_ci		}
308262306a36Sopenharmony_ci		ret = ib_agent_port_open(device, i);
308362306a36Sopenharmony_ci		if (ret) {
308462306a36Sopenharmony_ci			dev_err(&device->dev,
308562306a36Sopenharmony_ci				"Couldn't open port %d for agents\n", i);
308662306a36Sopenharmony_ci			goto error_agent;
308762306a36Sopenharmony_ci		}
308862306a36Sopenharmony_ci		count++;
308962306a36Sopenharmony_ci	}
309062306a36Sopenharmony_ci	if (!count)
309162306a36Sopenharmony_ci		return -EOPNOTSUPP;
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci	return 0;
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_cierror_agent:
309662306a36Sopenharmony_ci	if (ib_mad_port_close(device, i))
309762306a36Sopenharmony_ci		dev_err(&device->dev, "Couldn't close port %d\n", i);
309862306a36Sopenharmony_ci
309962306a36Sopenharmony_cierror:
310062306a36Sopenharmony_ci	while (--i >= start) {
310162306a36Sopenharmony_ci		if (!rdma_cap_ib_mad(device, i))
310262306a36Sopenharmony_ci			continue;
310362306a36Sopenharmony_ci
310462306a36Sopenharmony_ci		if (ib_agent_port_close(device, i))
310562306a36Sopenharmony_ci			dev_err(&device->dev,
310662306a36Sopenharmony_ci				"Couldn't close port %d for agents\n", i);
310762306a36Sopenharmony_ci		if (ib_mad_port_close(device, i))
310862306a36Sopenharmony_ci			dev_err(&device->dev, "Couldn't close port %d\n", i);
310962306a36Sopenharmony_ci	}
311062306a36Sopenharmony_ci	return ret;
311162306a36Sopenharmony_ci}
311262306a36Sopenharmony_ci
311362306a36Sopenharmony_cistatic void ib_mad_remove_device(struct ib_device *device, void *client_data)
311462306a36Sopenharmony_ci{
311562306a36Sopenharmony_ci	unsigned int i;
311662306a36Sopenharmony_ci
311762306a36Sopenharmony_ci	rdma_for_each_port (device, i) {
311862306a36Sopenharmony_ci		if (!rdma_cap_ib_mad(device, i))
311962306a36Sopenharmony_ci			continue;
312062306a36Sopenharmony_ci
312162306a36Sopenharmony_ci		if (ib_agent_port_close(device, i))
312262306a36Sopenharmony_ci			dev_err(&device->dev,
312362306a36Sopenharmony_ci				"Couldn't close port %u for agents\n", i);
312462306a36Sopenharmony_ci		if (ib_mad_port_close(device, i))
312562306a36Sopenharmony_ci			dev_err(&device->dev, "Couldn't close port %u\n", i);
312662306a36Sopenharmony_ci	}
312762306a36Sopenharmony_ci}
312862306a36Sopenharmony_ci
312962306a36Sopenharmony_cistatic struct ib_client mad_client = {
313062306a36Sopenharmony_ci	.name   = "mad",
313162306a36Sopenharmony_ci	.add = ib_mad_init_device,
313262306a36Sopenharmony_ci	.remove = ib_mad_remove_device
313362306a36Sopenharmony_ci};
313462306a36Sopenharmony_ci
313562306a36Sopenharmony_ciint ib_mad_init(void)
313662306a36Sopenharmony_ci{
313762306a36Sopenharmony_ci	mad_recvq_size = min(mad_recvq_size, IB_MAD_QP_MAX_SIZE);
313862306a36Sopenharmony_ci	mad_recvq_size = max(mad_recvq_size, IB_MAD_QP_MIN_SIZE);
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_ci	mad_sendq_size = min(mad_sendq_size, IB_MAD_QP_MAX_SIZE);
314162306a36Sopenharmony_ci	mad_sendq_size = max(mad_sendq_size, IB_MAD_QP_MIN_SIZE);
314262306a36Sopenharmony_ci
314362306a36Sopenharmony_ci	INIT_LIST_HEAD(&ib_mad_port_list);
314462306a36Sopenharmony_ci
314562306a36Sopenharmony_ci	if (ib_register_client(&mad_client)) {
314662306a36Sopenharmony_ci		pr_err("Couldn't register ib_mad client\n");
314762306a36Sopenharmony_ci		return -EINVAL;
314862306a36Sopenharmony_ci	}
314962306a36Sopenharmony_ci
315062306a36Sopenharmony_ci	return 0;
315162306a36Sopenharmony_ci}
315262306a36Sopenharmony_ci
315362306a36Sopenharmony_civoid ib_mad_cleanup(void)
315462306a36Sopenharmony_ci{
315562306a36Sopenharmony_ci	ib_unregister_client(&mad_client);
315662306a36Sopenharmony_ci}
3157