1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004 Topspin Communications.  All rights reserved.
3219820Sjeff * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
4219820Sjeff * Copyright (c) 2004 Voltaire, Inc. All rights reserved.
5219820Sjeff *
6219820Sjeff * This software is available to you under a choice of one of two
7219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
8219820Sjeff * General Public License (GPL) Version 2, available from the file
9219820Sjeff * COPYING in the main directory of this source tree, or the
10219820Sjeff * OpenIB.org BSD license below:
11219820Sjeff *
12219820Sjeff *     Redistribution and use in source and binary forms, with or
13219820Sjeff *     without modification, are permitted provided that the following
14219820Sjeff *     conditions are met:
15219820Sjeff *
16219820Sjeff *      - Redistributions of source code must retain the above
17219820Sjeff *        copyright notice, this list of conditions and the following
18219820Sjeff *        disclaimer.
19219820Sjeff *
20219820Sjeff *      - Redistributions in binary form must reproduce the above
21219820Sjeff *        copyright notice, this list of conditions and the following
22219820Sjeff *        disclaimer in the documentation and/or other materials
23219820Sjeff *        provided with the distribution.
24219820Sjeff *
25219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32219820Sjeff * SOFTWARE.
33219820Sjeff */
34219820Sjeff
35219820Sjeff#include <linux/string.h>
36219820Sjeff#include <linux/slab.h>
37219820Sjeff
38219820Sjeff#include <rdma/ib_verbs.h>
39219820Sjeff#include <rdma/ib_mad.h>
40219820Sjeff#include <rdma/ib_smi.h>
41219820Sjeff
42219820Sjeff#include "mthca_dev.h"
43219820Sjeff#include "mthca_cmd.h"
44219820Sjeff
45219820Sjeffenum {
46219820Sjeff	MTHCA_VENDOR_CLASS1 = 0x9,
47219820Sjeff	MTHCA_VENDOR_CLASS2 = 0xa
48219820Sjeff};
49219820Sjeff
50219820Sjeffstatic int mthca_update_rate(struct mthca_dev *dev, u8 port_num)
51219820Sjeff{
52219820Sjeff	struct ib_port_attr *tprops = NULL;
53219820Sjeff	int                  ret;
54219820Sjeff
55219820Sjeff	tprops = kmalloc(sizeof *tprops, GFP_KERNEL);
56219820Sjeff	if (!tprops)
57219820Sjeff		return -ENOMEM;
58219820Sjeff
59219820Sjeff	ret = ib_query_port(&dev->ib_dev, port_num, tprops);
60219820Sjeff	if (ret) {
61219820Sjeff		printk(KERN_WARNING "ib_query_port failed (%d) for %s port %d\n",
62219820Sjeff		       ret, dev->ib_dev.name, port_num);
63219820Sjeff		goto out;
64219820Sjeff	}
65219820Sjeff
66219820Sjeff	dev->rate[port_num - 1] = tprops->active_speed *
67219820Sjeff				  ib_width_enum_to_int(tprops->active_width);
68219820Sjeff
69219820Sjeffout:
70219820Sjeff	kfree(tprops);
71219820Sjeff	return ret;
72219820Sjeff}
73219820Sjeff
74219820Sjeffstatic void update_sm_ah(struct mthca_dev *dev,
75219820Sjeff			 u8 port_num, u16 lid, u8 sl)
76219820Sjeff{
77219820Sjeff	struct ib_ah *new_ah;
78219820Sjeff	struct ib_ah_attr ah_attr;
79219820Sjeff	unsigned long flags;
80219820Sjeff
81219820Sjeff	if (!dev->send_agent[port_num - 1][0])
82219820Sjeff		return;
83219820Sjeff
84219820Sjeff	memset(&ah_attr, 0, sizeof ah_attr);
85219820Sjeff	ah_attr.dlid     = lid;
86219820Sjeff	ah_attr.sl       = sl;
87219820Sjeff	ah_attr.port_num = port_num;
88219820Sjeff
89219820Sjeff	new_ah = ib_create_ah(dev->send_agent[port_num - 1][0]->qp->pd,
90219820Sjeff			      &ah_attr);
91219820Sjeff	if (IS_ERR(new_ah))
92219820Sjeff		return;
93219820Sjeff
94219820Sjeff	spin_lock_irqsave(&dev->sm_lock, flags);
95219820Sjeff	if (dev->sm_ah[port_num - 1])
96219820Sjeff		ib_destroy_ah(dev->sm_ah[port_num - 1]);
97219820Sjeff	dev->sm_ah[port_num - 1] = new_ah;
98219820Sjeff	spin_unlock_irqrestore(&dev->sm_lock, flags);
99219820Sjeff}
100219820Sjeff
101219820Sjeff/*
102219820Sjeff * Snoop SM MADs for port info and P_Key table sets, so we can
103219820Sjeff * synthesize LID change and P_Key change events.
104219820Sjeff */
105219820Sjeffstatic void smp_snoop(struct ib_device *ibdev,
106219820Sjeff		      u8 port_num,
107219820Sjeff		      struct ib_mad *mad,
108219820Sjeff		      u16 prev_lid)
109219820Sjeff{
110219820Sjeff	struct ib_event event;
111219820Sjeff
112219820Sjeff	if ((mad->mad_hdr.mgmt_class  == IB_MGMT_CLASS_SUBN_LID_ROUTED ||
113219820Sjeff	     mad->mad_hdr.mgmt_class  == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) &&
114219820Sjeff	    mad->mad_hdr.method     == IB_MGMT_METHOD_SET) {
115219820Sjeff		if (mad->mad_hdr.attr_id == IB_SMP_ATTR_PORT_INFO) {
116219820Sjeff			struct ib_port_info *pinfo =
117219820Sjeff				(struct ib_port_info *) ((struct ib_smp *) mad)->data;
118219820Sjeff			u16 lid = be16_to_cpu(pinfo->lid);
119219820Sjeff
120219820Sjeff			mthca_update_rate(to_mdev(ibdev), port_num);
121219820Sjeff			update_sm_ah(to_mdev(ibdev), port_num,
122219820Sjeff				     be16_to_cpu(pinfo->sm_lid),
123219820Sjeff				     pinfo->neighbormtu_mastersmsl & 0xf);
124219820Sjeff
125219820Sjeff			event.device           = ibdev;
126219820Sjeff			event.element.port_num = port_num;
127219820Sjeff
128219820Sjeff			if (pinfo->clientrereg_resv_subnetto & 0x80) {
129219820Sjeff				event.event    = IB_EVENT_CLIENT_REREGISTER;
130219820Sjeff				ib_dispatch_event(&event);
131219820Sjeff			}
132219820Sjeff
133219820Sjeff			if (prev_lid != lid) {
134219820Sjeff				event.event    = IB_EVENT_LID_CHANGE;
135219820Sjeff				ib_dispatch_event(&event);
136219820Sjeff			}
137219820Sjeff		}
138219820Sjeff
139219820Sjeff		if (mad->mad_hdr.attr_id == IB_SMP_ATTR_PKEY_TABLE) {
140219820Sjeff			event.device           = ibdev;
141219820Sjeff			event.event            = IB_EVENT_PKEY_CHANGE;
142219820Sjeff			event.element.port_num = port_num;
143219820Sjeff			ib_dispatch_event(&event);
144219820Sjeff		}
145219820Sjeff	}
146219820Sjeff}
147219820Sjeff
148219820Sjeffstatic void node_desc_override(struct ib_device *dev,
149219820Sjeff			       struct ib_mad *mad)
150219820Sjeff{
151219820Sjeff	if ((mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED ||
152219820Sjeff	     mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) &&
153219820Sjeff	    mad->mad_hdr.method == IB_MGMT_METHOD_GET_RESP &&
154219820Sjeff	    mad->mad_hdr.attr_id == IB_SMP_ATTR_NODE_DESC) {
155219820Sjeff		mutex_lock(&to_mdev(dev)->cap_mask_mutex);
156219820Sjeff		memcpy(((struct ib_smp *) mad)->data, dev->node_desc, 64);
157219820Sjeff		mutex_unlock(&to_mdev(dev)->cap_mask_mutex);
158219820Sjeff	}
159219820Sjeff}
160219820Sjeff
161219820Sjeffstatic void forward_trap(struct mthca_dev *dev,
162219820Sjeff			 u8 port_num,
163219820Sjeff			 struct ib_mad *mad)
164219820Sjeff{
165219820Sjeff	int qpn = mad->mad_hdr.mgmt_class != IB_MGMT_CLASS_SUBN_LID_ROUTED;
166219820Sjeff	struct ib_mad_send_buf *send_buf;
167219820Sjeff	struct ib_mad_agent *agent = dev->send_agent[port_num - 1][qpn];
168219820Sjeff	int ret;
169219820Sjeff	unsigned long flags;
170219820Sjeff
171219820Sjeff	if (agent) {
172219820Sjeff		send_buf = ib_create_send_mad(agent, qpn, 0, 0, IB_MGMT_MAD_HDR,
173219820Sjeff					      IB_MGMT_MAD_DATA, GFP_ATOMIC);
174219820Sjeff		/*
175219820Sjeff		 * We rely here on the fact that MLX QPs don't use the
176219820Sjeff		 * address handle after the send is posted (this is
177219820Sjeff		 * wrong following the IB spec strictly, but we know
178219820Sjeff		 * it's OK for our devices).
179219820Sjeff		 */
180219820Sjeff		spin_lock_irqsave(&dev->sm_lock, flags);
181219820Sjeff		memcpy(send_buf->mad, mad, sizeof *mad);
182219820Sjeff		if ((send_buf->ah = dev->sm_ah[port_num - 1]))
183219820Sjeff			ret = ib_post_send_mad(send_buf, NULL);
184219820Sjeff		else
185219820Sjeff			ret = -EINVAL;
186219820Sjeff		spin_unlock_irqrestore(&dev->sm_lock, flags);
187219820Sjeff
188219820Sjeff		if (ret)
189219820Sjeff			ib_free_send_mad(send_buf);
190219820Sjeff	}
191219820Sjeff}
192219820Sjeff
193219820Sjeffint mthca_process_mad(struct ib_device *ibdev,
194219820Sjeff		      int mad_flags,
195219820Sjeff		      u8 port_num,
196219820Sjeff		      struct ib_wc *in_wc,
197219820Sjeff		      struct ib_grh *in_grh,
198219820Sjeff		      struct ib_mad *in_mad,
199219820Sjeff		      struct ib_mad *out_mad)
200219820Sjeff{
201219820Sjeff	int err;
202219820Sjeff	u8 status;
203219820Sjeff	u16 slid = in_wc ? in_wc->slid : be16_to_cpu(IB_LID_PERMISSIVE);
204219820Sjeff	u16 prev_lid = 0;
205219820Sjeff	struct ib_port_attr pattr;
206219820Sjeff
207219820Sjeff	/* Forward locally generated traps to the SM */
208219820Sjeff	if (in_mad->mad_hdr.method == IB_MGMT_METHOD_TRAP &&
209219820Sjeff	    slid == 0) {
210219820Sjeff		forward_trap(to_mdev(ibdev), port_num, in_mad);
211219820Sjeff		return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED;
212219820Sjeff	}
213219820Sjeff
214219820Sjeff	/*
215219820Sjeff	 * Only handle SM gets, sets and trap represses for SM class
216219820Sjeff	 *
217219820Sjeff	 * Only handle PMA and Mellanox vendor-specific class gets and
218219820Sjeff	 * sets for other classes.
219219820Sjeff	 */
220219820Sjeff	if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED ||
221219820Sjeff	    in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
222219820Sjeff		if (in_mad->mad_hdr.method   != IB_MGMT_METHOD_GET &&
223219820Sjeff		    in_mad->mad_hdr.method   != IB_MGMT_METHOD_SET &&
224219820Sjeff		    in_mad->mad_hdr.method   != IB_MGMT_METHOD_TRAP_REPRESS)
225219820Sjeff			return IB_MAD_RESULT_SUCCESS;
226219820Sjeff
227219820Sjeff		/*
228219820Sjeff		 * Don't process SMInfo queries or vendor-specific
229219820Sjeff		 * MADs -- the SMA can't handle them.
230219820Sjeff		 */
231219820Sjeff		if (in_mad->mad_hdr.attr_id == IB_SMP_ATTR_SM_INFO ||
232219820Sjeff		    ((in_mad->mad_hdr.attr_id & IB_SMP_ATTR_VENDOR_MASK) ==
233219820Sjeff		     IB_SMP_ATTR_VENDOR_MASK))
234219820Sjeff			return IB_MAD_RESULT_SUCCESS;
235219820Sjeff	} else if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_PERF_MGMT ||
236219820Sjeff		   in_mad->mad_hdr.mgmt_class == MTHCA_VENDOR_CLASS1     ||
237219820Sjeff		   in_mad->mad_hdr.mgmt_class == MTHCA_VENDOR_CLASS2) {
238219820Sjeff		if (in_mad->mad_hdr.method  != IB_MGMT_METHOD_GET &&
239219820Sjeff		    in_mad->mad_hdr.method  != IB_MGMT_METHOD_SET)
240219820Sjeff			return IB_MAD_RESULT_SUCCESS;
241219820Sjeff	} else
242219820Sjeff		return IB_MAD_RESULT_SUCCESS;
243219820Sjeff	if ((in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED ||
244219820Sjeff	     in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) &&
245219820Sjeff	    in_mad->mad_hdr.method == IB_MGMT_METHOD_SET &&
246219820Sjeff	    in_mad->mad_hdr.attr_id == IB_SMP_ATTR_PORT_INFO &&
247219820Sjeff	    !ib_query_port(ibdev, port_num, &pattr))
248219820Sjeff		prev_lid = pattr.lid;
249219820Sjeff
250219820Sjeff	err = mthca_MAD_IFC(to_mdev(ibdev),
251219820Sjeff			    mad_flags & IB_MAD_IGNORE_MKEY,
252219820Sjeff			    mad_flags & IB_MAD_IGNORE_BKEY,
253219820Sjeff			    port_num, in_wc, in_grh, in_mad, out_mad,
254219820Sjeff			    &status);
255219820Sjeff	if (err) {
256219820Sjeff		mthca_err(to_mdev(ibdev), "MAD_IFC failed\n");
257219820Sjeff		return IB_MAD_RESULT_FAILURE;
258219820Sjeff	}
259219820Sjeff	if (status == MTHCA_CMD_STAT_BAD_PKT)
260219820Sjeff		return IB_MAD_RESULT_SUCCESS;
261219820Sjeff	if (status) {
262219820Sjeff		mthca_err(to_mdev(ibdev), "MAD_IFC returned status %02x\n",
263219820Sjeff			  status);
264219820Sjeff		return IB_MAD_RESULT_FAILURE;
265219820Sjeff	}
266219820Sjeff
267219820Sjeff	if (!out_mad->mad_hdr.status) {
268219820Sjeff		smp_snoop(ibdev, port_num, in_mad, prev_lid);
269219820Sjeff		node_desc_override(ibdev, out_mad);
270219820Sjeff	}
271219820Sjeff
272219820Sjeff	/* set return bit in status of directed route responses */
273219820Sjeff	if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
274219820Sjeff		out_mad->mad_hdr.status |= cpu_to_be16(1 << 15);
275219820Sjeff
276219820Sjeff	if (in_mad->mad_hdr.method == IB_MGMT_METHOD_TRAP_REPRESS)
277219820Sjeff		/* no response for trap repress */
278219820Sjeff		return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED;
279219820Sjeff
280219820Sjeff	return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY;
281219820Sjeff}
282219820Sjeff
283219820Sjeffstatic void send_handler(struct ib_mad_agent *agent,
284219820Sjeff			 struct ib_mad_send_wc *mad_send_wc)
285219820Sjeff{
286219820Sjeff	ib_free_send_mad(mad_send_wc->send_buf);
287219820Sjeff}
288219820Sjeff
289219820Sjeffint mthca_create_agents(struct mthca_dev *dev)
290219820Sjeff{
291219820Sjeff	struct ib_mad_agent *agent;
292219820Sjeff	int p, q;
293219820Sjeff	int ret;
294219820Sjeff
295219820Sjeff	spin_lock_init(&dev->sm_lock);
296219820Sjeff
297219820Sjeff	for (p = 0; p < dev->limits.num_ports; ++p)
298219820Sjeff		for (q = 0; q <= 1; ++q) {
299219820Sjeff			agent = ib_register_mad_agent(&dev->ib_dev, p + 1,
300219820Sjeff						      q ? IB_QPT_GSI : IB_QPT_SMI,
301219820Sjeff						      NULL, 0, send_handler,
302219820Sjeff						      NULL, NULL);
303219820Sjeff			if (IS_ERR(agent)) {
304219820Sjeff				ret = PTR_ERR(agent);
305219820Sjeff				goto err;
306219820Sjeff			}
307219820Sjeff			dev->send_agent[p][q] = agent;
308219820Sjeff		}
309219820Sjeff
310219820Sjeff
311219820Sjeff	for (p = 1; p <= dev->limits.num_ports; ++p) {
312219820Sjeff		ret = mthca_update_rate(dev, p);
313219820Sjeff		if (ret) {
314219820Sjeff			mthca_err(dev, "Failed to obtain port %d rate."
315219820Sjeff				  " aborting.\n", p);
316219820Sjeff			goto err;
317219820Sjeff		}
318219820Sjeff	}
319219820Sjeff
320219820Sjeff	return 0;
321219820Sjeff
322219820Sjefferr:
323219820Sjeff	for (p = 0; p < dev->limits.num_ports; ++p)
324219820Sjeff		for (q = 0; q <= 1; ++q)
325219820Sjeff			if (dev->send_agent[p][q])
326219820Sjeff				ib_unregister_mad_agent(dev->send_agent[p][q]);
327219820Sjeff
328219820Sjeff	return ret;
329219820Sjeff}
330219820Sjeff
331219820Sjeffvoid mthca_free_agents(struct mthca_dev *dev)
332219820Sjeff{
333219820Sjeff	struct ib_mad_agent *agent;
334219820Sjeff	int p, q;
335219820Sjeff
336219820Sjeff	for (p = 0; p < dev->limits.num_ports; ++p) {
337219820Sjeff		for (q = 0; q <= 1; ++q) {
338219820Sjeff			agent = dev->send_agent[p][q];
339219820Sjeff			dev->send_agent[p][q] = NULL;
340219820Sjeff			ib_unregister_mad_agent(agent);
341219820Sjeff		}
342219820Sjeff
343219820Sjeff		if (dev->sm_ah[p])
344219820Sjeff			ib_destroy_ah(dev->sm_ah[p]);
345219820Sjeff	}
346219820Sjeff}
347