f001805ddd8e00de88dea3b528a0aab398614978
[mirror/winof/.git] / hw / mthca / kernel / mthca_mad.c
1 /*\r
2  * Copyright (c) 2004 Topspin Communications.  All rights reserved.\r
3  * Copyright (c) 2005 Mellanox Technologies. All rights reserved.\r
4  * Copyright (c) 2004 Voltaire, Inc. All rights reserved.\r
5  * Portions Copyright (c) 2008 Microsoft Corporation.  All rights reserved.\r
6  *\r
7  * This software is available to you under the OpenIB.org BSD license\r
8  * below:\r
9  *\r
10  *     Redistribution and use in source and binary forms, with or\r
11  *     without modification, are permitted provided that the following\r
12  *     conditions are met:\r
13  *\r
14  *      - Redistributions of source code must retain the above\r
15  *        copyright notice, this list of conditions and the following\r
16  *        disclaimer.\r
17  *\r
18  *      - Redistributions in binary form must reproduce the above\r
19  *        copyright notice, this list of conditions and the following\r
20  *        disclaimer in the documentation and/or other materials\r
21  *        provided with the distribution.\r
22  *\r
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
30  * SOFTWARE.\r
31  *\r
32  * $Id$\r
33  */\r
34 \r
35 #include <ib_verbs.h>\r
36 #include <ib_mad.h>\r
37 #include <ib_smi.h>\r
38 \r
39 #include "mthca_dev.h"\r
40 #if defined(EVENT_TRACING)\r
41 #ifdef offsetof\r
42 #undef offsetof\r
43 #endif\r
44 #include "mthca_mad.tmh"\r
45 #endif\r
46 #include "mthca_cmd.h"\r
47 \r
48 enum {\r
49         MTHCA_VENDOR_CLASS1 = 0x9,\r
50         MTHCA_VENDOR_CLASS2 = 0xa\r
51 };\r
52 \r
53 struct mthca_trap_mad {\r
54         struct scatterlist sg;\r
55 };\r
56 \r
57 static void update_sm_ah(struct mthca_dev *dev,\r
58                          u8 port_num, u16 lid, u8 sl)\r
59 {\r
60         struct ib_ah *new_ah;\r
61         struct ib_ah_attr ah_attr;\r
62         SPIN_LOCK_PREP(lh);\r
63 \r
64         if (!dev->send_agent[port_num - 1][0])\r
65                 return;\r
66 \r
67         RtlZeroMemory(&ah_attr, sizeof ah_attr);\r
68         ah_attr.dlid     = lid;\r
69         ah_attr.sl       = sl;\r
70         ah_attr.port_num = port_num;\r
71 \r
72         new_ah = ibv_create_ah(dev->send_agent[port_num - 1][0]->qp->pd,\r
73                               &ah_attr, NULL, NULL);\r
74         if (IS_ERR(new_ah))\r
75                 return;\r
76 \r
77         spin_lock_irqsave(&dev->sm_lock, &lh);\r
78         if (dev->sm_ah[port_num - 1]) {\r
79                 ibv_destroy_ah(dev->sm_ah[port_num - 1]);\r
80         }\r
81         dev->sm_ah[port_num - 1] = new_ah;\r
82         spin_unlock_irqrestore(&lh);\r
83 }\r
84 \r
85 /*\r
86  * Snoop SM MADs for port info and P_Key table sets, so we can\r
87  * synthesize LID change and P_Key change events.\r
88  */\r
89 static void smp_snoop(struct ib_device *ibdev,\r
90                       u8 port_num,\r
91                       struct ib_mad *mad)\r
92 {\r
93         struct ib_event event;\r
94 \r
95         if ((mad->mad_hdr.mgmt_class  == IB_MGMT_CLASS_SUBN_LID_ROUTED ||\r
96              mad->mad_hdr.mgmt_class  == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) &&\r
97             mad->mad_hdr.method     == IB_MGMT_METHOD_SET) {\r
98                 if (mad->mad_hdr.attr_id == IB_SMP_ATTR_PORT_INFO) {\r
99                         update_sm_ah(to_mdev(ibdev), port_num,\r
100                                      cl_ntoh16(*(__be16 *) (mad->data + 58)),\r
101                                      (*(u8 *) (mad->data + 76)) & 0xf);\r
102 \r
103                         event.device           = ibdev;\r
104                         event.event            = IB_EVENT_LID_CHANGE;\r
105                         event.element.port_num = port_num;\r
106                         ib_dispatch_event(&event);\r
107                 }\r
108 \r
109                 if (mad->mad_hdr.attr_id == IB_SMP_ATTR_PKEY_TABLE) {\r
110                         event.device           = ibdev;\r
111                         event.event            = IB_EVENT_PKEY_CHANGE;\r
112                         event.element.port_num = port_num;\r
113                         ib_dispatch_event(&event);\r
114                 }\r
115         }\r
116 }\r
117 \r
118 static void forward_trap(struct mthca_dev *dev,\r
119                          u8 port_num,\r
120                          struct ib_mad *mad)\r
121 {\r
122         int qpn = mad->mad_hdr.mgmt_class != IB_MGMT_CLASS_SUBN_LID_ROUTED;\r
123         struct mthca_trap_mad *tmad;\r
124         struct ib_sge      gather_list;\r
125         struct _ib_send_wr wr;\r
126         struct ib_mad_agent *agent = dev->send_agent[port_num - 1][qpn];\r
127         int ret;\r
128         SPIN_LOCK_PREP(lh);\r
129 \r
130         /* fill the template */\r
131         wr.ds_array = (ib_local_ds_t*)(void*)&gather_list;\r
132         wr.num_ds = 1;\r
133         wr.wr_type = WR_SEND;\r
134         wr.send_opt = IB_SEND_OPT_SIGNALED;\r
135         wr.dgrm.ud.remote_qp = cl_hton32(qpn);\r
136         wr.dgrm.ud.remote_qkey = qpn ? IB_QP1_QKEY : 0;\r
137         \r
138         if (agent) {\r
139                 tmad = kmalloc(sizeof *tmad, GFP_KERNEL);\r
140                 if (!tmad)\r
141                         return;\r
142 \r
143                 alloc_dma_zmem(dev, sizeof *mad, &tmad->sg);\r
144                 if (!tmad->sg.page) {\r
145                         kfree(tmad);\r
146                         return;\r
147                 }\r
148 \r
149                 memcpy(tmad->sg.page, mad, sizeof *mad);\r
150 \r
151                 wr.dgrm.ud.rsvd = (void*)&((struct ib_mad *)tmad->sg.page)->mad_hdr;\r
152                 wr.wr_id         = (u64)(ULONG_PTR)tmad;\r
153                 gather_list.addr   = tmad->sg.dma_address;\r
154                 gather_list.length = tmad->sg.length;\r
155                 gather_list.lkey   = to_mpd(agent->qp->pd)->ntmr.ibmr.lkey;\r
156 \r
157                 /*\r
158                  * We rely here on the fact that MLX QPs don't use the\r
159                  * address handle after the send is posted (this is\r
160                  * wrong following the IB spec strictly, but we know\r
161                  * it's OK for our devices).\r
162                  */\r
163                 spin_lock_irqsave(&dev->sm_lock, &lh);\r
164                 wr.dgrm.ud.h_av = (ib_av_handle_t)dev->sm_ah[port_num - 1];\r
165                 if (wr.dgrm.ud.h_av) {\r
166                                 HCA_PRINT( TRACE_LEVEL_ERROR ,HCA_DBG_MAD ,(" ib_post_send_mad not ported \n" ));\r
167                                 ret = -EINVAL;\r
168                 }\r
169                 else\r
170                         ret = -EINVAL;\r
171                 spin_unlock_irqrestore(&lh);\r
172 \r
173                 if (ret) {\r
174                         free_dma_mem_map(dev, &tmad->sg, PCI_DMA_BIDIRECTIONAL );\r
175                         kfree(tmad);\r
176                 }\r
177         }\r
178 }\r
179 \r
180 int mthca_process_mad(struct ib_device *ibdev,\r
181                       int mad_flags,\r
182                       u8 port_num,\r
183                       struct _ib_wc *in_wc,\r
184                       struct _ib_grh *in_grh,\r
185                       struct ib_mad *in_mad,\r
186                       struct ib_mad *out_mad)\r
187 {\r
188         int err;\r
189         u8 status;\r
190         u16 slid = in_wc ? in_wc->recv.ud.remote_lid : cl_ntoh16(IB_LID_PERMISSIVE);\r
191 \r
192         HCA_PRINT( TRACE_LEVEL_VERBOSE ,HCA_DBG_MAD ,("in: Class %02x, Method %02x, AttrId %x, AttrMod %x, ClSpec %x, Tid %I64x\n",\r
193                 (u32)in_mad->mad_hdr.mgmt_class, (u32)in_mad->mad_hdr.method, \r
194                 (u32)in_mad->mad_hdr.attr_id, in_mad->mad_hdr.attr_mod, \r
195                 (u32)in_mad->mad_hdr.class_specific, in_mad->mad_hdr.tid ));\r
196 \r
197         /* Forward locally generated traps to the SM */\r
198         if (in_mad->mad_hdr.method == IB_MGMT_METHOD_TRAP &&\r
199             slid == 0) {\r
200                 forward_trap(to_mdev(ibdev), port_num, in_mad);\r
201                 HCA_PRINT( TRACE_LEVEL_VERBOSE ,HCA_DBG_MAD ,("Not sent, but locally forwarded\n"));\r
202                 return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED;\r
203         }\r
204 \r
205         /*\r
206          * Only handle SM gets, sets and trap represses for SM class\r
207          *\r
208          * Only handle PMA and Mellanox vendor-specific class gets and\r
209          * sets for other classes.\r
210          */\r
211         if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED ||\r
212             in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {\r
213 \r
214                 if (in_mad->mad_hdr.method   != IB_MGMT_METHOD_GET &&\r
215                     in_mad->mad_hdr.method   != IB_MGMT_METHOD_SET &&\r
216                     in_mad->mad_hdr.method   != IB_MGMT_METHOD_TRAP_REPRESS) {\r
217                         HCA_PRINT( TRACE_LEVEL_VERBOSE,HCA_DBG_MAD,(" Skip some methods. Nothing done !\n"));\r
218                         return IB_MAD_RESULT_SUCCESS;\r
219                 }\r
220 \r
221                 /*\r
222                  * Don't process SMInfo queries or vendor-specific\r
223                  * MADs -- the SMA can't handle them.\r
224                  */\r
225                 if (in_mad->mad_hdr.attr_id == IB_SMP_ATTR_SM_INFO ||\r
226                     ((in_mad->mad_hdr.attr_id & IB_SMP_ATTR_VENDOR_MASK) ==\r
227                      IB_SMP_ATTR_VENDOR_MASK)) {\r
228                         HCA_PRINT( TRACE_LEVEL_VERBOSE ,HCA_DBG_MAD ,("Skip SMInfo queries or vendor-specific MADs. Nothing done !\n"));\r
229                         return IB_MAD_RESULT_SUCCESS;\r
230                 }\r
231         } \r
232         else {\r
233                 if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_PERF_MGMT ||\r
234                    in_mad->mad_hdr.mgmt_class == MTHCA_VENDOR_CLASS1     ||\r
235                    in_mad->mad_hdr.mgmt_class == MTHCA_VENDOR_CLASS2) {\r
236 \r
237                         if (in_mad->mad_hdr.method  != IB_MGMT_METHOD_GET &&\r
238                             in_mad->mad_hdr.method  != IB_MGMT_METHOD_SET) {\r
239                                 HCA_PRINT( TRACE_LEVEL_VERBOSE ,HCA_DBG_MAD ,("Skip some management methods. Nothing done !\n"));\r
240                                 return IB_MAD_RESULT_SUCCESS;\r
241                         }\r
242                 } \r
243                 else {\r
244                         HCA_PRINT( TRACE_LEVEL_VERBOSE ,HCA_DBG_MAD ,("Skip IB_MGMT_CLASS_PERF_MGMT et al. Nothing done !\n"));\r
245                         return IB_MAD_RESULT_SUCCESS;\r
246                 }       \r
247         }\r
248 \r
249         // send MAD\r
250         err = mthca_MAD_IFC(to_mdev(ibdev),\r
251                             mad_flags & IB_MAD_IGNORE_MKEY,\r
252                             mad_flags & IB_MAD_IGNORE_BKEY,\r
253                             port_num, in_wc, in_grh, in_mad, out_mad,\r
254                             &status);\r
255         if (err) {\r
256                 HCA_PRINT(TRACE_LEVEL_ERROR ,HCA_DBG_MAD ,("MAD_IFC failed\n"));\r
257                 return IB_MAD_RESULT_FAILURE;\r
258         }\r
259         if (status == MTHCA_CMD_STAT_BAD_PKT)\r
260                 return IB_MAD_RESULT_SUCCESS;\r
261         if (status) {\r
262                 HCA_PRINT(TRACE_LEVEL_ERROR ,HCA_DBG_MAD ,("MAD_IFC returned status %02x\n", status));\r
263                 return IB_MAD_RESULT_FAILURE;\r
264         }\r
265 \r
266         if (!out_mad->mad_hdr.status)\r
267                 smp_snoop(ibdev, port_num, in_mad);\r
268 \r
269         HCA_PRINT( TRACE_LEVEL_VERBOSE ,HCA_DBG_MAD,("out: Class %02x, Method %02x, AttrId %x, AttrMod %x, ClSpec %x, Tid %I64x, Status %x\n",\r
270                 (u32)out_mad->mad_hdr.mgmt_class, (u32)out_mad->mad_hdr.method, \r
271                 (u32)out_mad->mad_hdr.attr_id, out_mad->mad_hdr.attr_mod, \r
272                 (u32)out_mad->mad_hdr.class_specific, out_mad->mad_hdr.tid,\r
273                 (u32)out_mad->mad_hdr.status ));\r
274 \r
275         if (in_mad->mad_hdr.method == IB_MGMT_METHOD_TRAP_REPRESS) {\r
276                 /* no response for trap repress */\r
277                 return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED;\r
278         }\r
279 \r
280         return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY;\r
281 }\r
282 \r
283 static void send_handler(struct ib_mad_agent *agent,\r
284                          struct ib_mad_send_wc *mad_send_wc)\r
285 {\r
286         struct mthca_trap_mad *tmad =\r
287                 (void *) (ULONG_PTR) mad_send_wc->wr_id;\r
288 \r
289         free_dma_mem_map(agent->device->mdev, &tmad->sg, PCI_DMA_BIDIRECTIONAL );\r
290         kfree(tmad);\r
291 }\r