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