Merge remote-tracking branches 'regulator/fix/88pm800', 'regulator/fix/max8973',...
[linux-drm-fsl-dcu.git] / drivers / infiniband / core / smi.c
1 /*
2  * Copyright (c) 2004, 2005 Mellanox Technologies Ltd.  All rights reserved.
3  * Copyright (c) 2004, 2005 Infinicon Corporation.  All rights reserved.
4  * Copyright (c) 2004, 2005 Intel Corporation.  All rights reserved.
5  * Copyright (c) 2004, 2005 Topspin Corporation.  All rights reserved.
6  * Copyright (c) 2004-2007 Voltaire Corporation.  All rights reserved.
7  * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
8  * Copyright (c) 2014 Intel Corporation.  All rights reserved.
9  *
10  * This software is available to you under a choice of one of two
11  * licenses.  You may choose to be licensed under the terms of the GNU
12  * General Public License (GPL) Version 2, available from the file
13  * COPYING in the main directory of this source tree, or the
14  * OpenIB.org BSD license below:
15  *
16  *     Redistribution and use in source and binary forms, with or
17  *     without modification, are permitted provided that the following
18  *     conditions are met:
19  *
20  *      - Redistributions of source code must retain the above
21  *        copyright notice, this list of conditions and the following
22  *        disclaimer.
23  *
24  *      - Redistributions in binary form must reproduce the above
25  *        copyright notice, this list of conditions and the following
26  *        disclaimer in the documentation and/or other materials
27  *        provided with the distribution.
28  *
29  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
33  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
34  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
35  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36  * SOFTWARE.
37  *
38  */
39
40 #include <rdma/ib_smi.h>
41 #include "smi.h"
42 #include "opa_smi.h"
43
44 static enum smi_action __smi_handle_dr_smp_send(bool is_switch, int port_num,
45                                                 u8 *hop_ptr, u8 hop_cnt,
46                                                 const u8 *initial_path,
47                                                 const u8 *return_path,
48                                                 u8 direction,
49                                                 bool dr_dlid_is_permissive,
50                                                 bool dr_slid_is_permissive)
51 {
52         /* See section 14.2.2.2, Vol 1 IB spec */
53         /* C14-6 -- valid hop_cnt values are from 0 to 63 */
54         if (hop_cnt >= IB_SMP_MAX_PATH_HOPS)
55                 return IB_SMI_DISCARD;
56
57         if (!direction) {
58                 /* C14-9:1 */
59                 if (hop_cnt && *hop_ptr == 0) {
60                         (*hop_ptr)++;
61                         return (initial_path[*hop_ptr] ==
62                                 port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
63                 }
64
65                 /* C14-9:2 */
66                 if (*hop_ptr && *hop_ptr < hop_cnt) {
67                         if (!is_switch)
68                                 return IB_SMI_DISCARD;
69
70                         /* return_path set when received */
71                         (*hop_ptr)++;
72                         return (initial_path[*hop_ptr] ==
73                                 port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
74                 }
75
76                 /* C14-9:3 -- We're at the end of the DR segment of path */
77                 if (*hop_ptr == hop_cnt) {
78                         /* return_path set when received */
79                         (*hop_ptr)++;
80                         return (is_switch ||
81                                 dr_dlid_is_permissive ?
82                                 IB_SMI_HANDLE : IB_SMI_DISCARD);
83                 }
84
85                 /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
86                 /* C14-9:5 -- Fail unreasonable hop pointer */
87                 return (*hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
88
89         } else {
90                 /* C14-13:1 */
91                 if (hop_cnt && *hop_ptr == hop_cnt + 1) {
92                         (*hop_ptr)--;
93                         return (return_path[*hop_ptr] ==
94                                 port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
95                 }
96
97                 /* C14-13:2 */
98                 if (2 <= *hop_ptr && *hop_ptr <= hop_cnt) {
99                         if (!is_switch)
100                                 return IB_SMI_DISCARD;
101
102                         (*hop_ptr)--;
103                         return (return_path[*hop_ptr] ==
104                                 port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
105                 }
106
107                 /* C14-13:3 -- at the end of the DR segment of path */
108                 if (*hop_ptr == 1) {
109                         (*hop_ptr)--;
110                         /* C14-13:3 -- SMPs destined for SM shouldn't be here */
111                         return (is_switch ||
112                                 dr_slid_is_permissive ?
113                                 IB_SMI_HANDLE : IB_SMI_DISCARD);
114                 }
115
116                 /* C14-13:4 -- hop_ptr = 0 -> should have gone to SM */
117                 if (*hop_ptr == 0)
118                         return IB_SMI_HANDLE;
119
120                 /* C14-13:5 -- Check for unreasonable hop pointer */
121                 return IB_SMI_DISCARD;
122         }
123 }
124
125 /*
126  * Fixup a directed route SMP for sending
127  * Return IB_SMI_DISCARD if the SMP should be discarded
128  */
129 enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp,
130                                        bool is_switch, int port_num)
131 {
132         return __smi_handle_dr_smp_send(is_switch, port_num,
133                                         &smp->hop_ptr, smp->hop_cnt,
134                                         smp->initial_path,
135                                         smp->return_path,
136                                         ib_get_smp_direction(smp),
137                                         smp->dr_dlid == IB_LID_PERMISSIVE,
138                                         smp->dr_slid == IB_LID_PERMISSIVE);
139 }
140
141 enum smi_action opa_smi_handle_dr_smp_send(struct opa_smp *smp,
142                                        bool is_switch, int port_num)
143 {
144         return __smi_handle_dr_smp_send(is_switch, port_num,
145                                         &smp->hop_ptr, smp->hop_cnt,
146                                         smp->route.dr.initial_path,
147                                         smp->route.dr.return_path,
148                                         opa_get_smp_direction(smp),
149                                         smp->route.dr.dr_dlid ==
150                                         OPA_LID_PERMISSIVE,
151                                         smp->route.dr.dr_slid ==
152                                         OPA_LID_PERMISSIVE);
153 }
154
155 static enum smi_action __smi_handle_dr_smp_recv(bool is_switch, int port_num,
156                                                 int phys_port_cnt,
157                                                 u8 *hop_ptr, u8 hop_cnt,
158                                                 const u8 *initial_path,
159                                                 u8 *return_path,
160                                                 u8 direction,
161                                                 bool dr_dlid_is_permissive,
162                                                 bool dr_slid_is_permissive)
163 {
164         /* See section 14.2.2.2, Vol 1 IB spec */
165         /* C14-6 -- valid hop_cnt values are from 0 to 63 */
166         if (hop_cnt >= IB_SMP_MAX_PATH_HOPS)
167                 return IB_SMI_DISCARD;
168
169         if (!direction) {
170                 /* C14-9:1 -- sender should have incremented hop_ptr */
171                 if (hop_cnt && *hop_ptr == 0)
172                         return IB_SMI_DISCARD;
173
174                 /* C14-9:2 -- intermediate hop */
175                 if (*hop_ptr && *hop_ptr < hop_cnt) {
176                         if (!is_switch)
177                                 return IB_SMI_DISCARD;
178
179                         return_path[*hop_ptr] = port_num;
180                         /* hop_ptr updated when sending */
181                         return (initial_path[*hop_ptr+1] <= phys_port_cnt ?
182                                 IB_SMI_HANDLE : IB_SMI_DISCARD);
183                 }
184
185                 /* C14-9:3 -- We're at the end of the DR segment of path */
186                 if (*hop_ptr == hop_cnt) {
187                         if (hop_cnt)
188                                 return_path[*hop_ptr] = port_num;
189                         /* hop_ptr updated when sending */
190
191                         return (is_switch ||
192                                 dr_dlid_is_permissive ?
193                                 IB_SMI_HANDLE : IB_SMI_DISCARD);
194                 }
195
196                 /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
197                 /* C14-9:5 -- fail unreasonable hop pointer */
198                 return (*hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
199
200         } else {
201
202                 /* C14-13:1 */
203                 if (hop_cnt && *hop_ptr == hop_cnt + 1) {
204                         (*hop_ptr)--;
205                         return (return_path[*hop_ptr] ==
206                                 port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
207                 }
208
209                 /* C14-13:2 */
210                 if (2 <= *hop_ptr && *hop_ptr <= hop_cnt) {
211                         if (!is_switch)
212                                 return IB_SMI_DISCARD;
213
214                         /* hop_ptr updated when sending */
215                         return (return_path[*hop_ptr-1] <= phys_port_cnt ?
216                                 IB_SMI_HANDLE : IB_SMI_DISCARD);
217                 }
218
219                 /* C14-13:3 -- We're at the end of the DR segment of path */
220                 if (*hop_ptr == 1) {
221                         if (dr_slid_is_permissive) {
222                                 /* giving SMP to SM - update hop_ptr */
223                                 (*hop_ptr)--;
224                                 return IB_SMI_HANDLE;
225                         }
226                         /* hop_ptr updated when sending */
227                         return (is_switch ? IB_SMI_HANDLE : IB_SMI_DISCARD);
228                 }
229
230                 /* C14-13:4 -- hop_ptr = 0 -> give to SM */
231                 /* C14-13:5 -- Check for unreasonable hop pointer */
232                 return (*hop_ptr == 0 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
233         }
234 }
235
236 /*
237  * Adjust information for a received SMP
238  * Return IB_SMI_DISCARD if the SMP should be dropped
239  */
240 enum smi_action smi_handle_dr_smp_recv(struct ib_smp *smp, bool is_switch,
241                                        int port_num, int phys_port_cnt)
242 {
243         return __smi_handle_dr_smp_recv(is_switch, port_num, phys_port_cnt,
244                                         &smp->hop_ptr, smp->hop_cnt,
245                                         smp->initial_path,
246                                         smp->return_path,
247                                         ib_get_smp_direction(smp),
248                                         smp->dr_dlid == IB_LID_PERMISSIVE,
249                                         smp->dr_slid == IB_LID_PERMISSIVE);
250 }
251
252 /*
253  * Adjust information for a received SMP
254  * Return IB_SMI_DISCARD if the SMP should be dropped
255  */
256 enum smi_action opa_smi_handle_dr_smp_recv(struct opa_smp *smp, bool is_switch,
257                                            int port_num, int phys_port_cnt)
258 {
259         return __smi_handle_dr_smp_recv(is_switch, port_num, phys_port_cnt,
260                                         &smp->hop_ptr, smp->hop_cnt,
261                                         smp->route.dr.initial_path,
262                                         smp->route.dr.return_path,
263                                         opa_get_smp_direction(smp),
264                                         smp->route.dr.dr_dlid ==
265                                         OPA_LID_PERMISSIVE,
266                                         smp->route.dr.dr_slid ==
267                                         OPA_LID_PERMISSIVE);
268 }
269
270 static enum smi_forward_action __smi_check_forward_dr_smp(u8 hop_ptr, u8 hop_cnt,
271                                                           u8 direction,
272                                                           bool dr_dlid_is_permissive,
273                                                           bool dr_slid_is_permissive)
274 {
275         if (!direction) {
276                 /* C14-9:2 -- intermediate hop */
277                 if (hop_ptr && hop_ptr < hop_cnt)
278                         return IB_SMI_FORWARD;
279
280                 /* C14-9:3 -- at the end of the DR segment of path */
281                 if (hop_ptr == hop_cnt)
282                         return (dr_dlid_is_permissive ?
283                                 IB_SMI_SEND : IB_SMI_LOCAL);
284
285                 /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
286                 if (hop_ptr == hop_cnt + 1)
287                         return IB_SMI_SEND;
288         } else {
289                 /* C14-13:2  -- intermediate hop */
290                 if (2 <= hop_ptr && hop_ptr <= hop_cnt)
291                         return IB_SMI_FORWARD;
292
293                 /* C14-13:3 -- at the end of the DR segment of path */
294                 if (hop_ptr == 1)
295                         return (!dr_slid_is_permissive ?
296                                 IB_SMI_SEND : IB_SMI_LOCAL);
297         }
298         return IB_SMI_LOCAL;
299
300 }
301
302 enum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp)
303 {
304         return __smi_check_forward_dr_smp(smp->hop_ptr, smp->hop_cnt,
305                                           ib_get_smp_direction(smp),
306                                           smp->dr_dlid == IB_LID_PERMISSIVE,
307                                           smp->dr_slid == IB_LID_PERMISSIVE);
308 }
309
310 enum smi_forward_action opa_smi_check_forward_dr_smp(struct opa_smp *smp)
311 {
312         return __smi_check_forward_dr_smp(smp->hop_ptr, smp->hop_cnt,
313                                           opa_get_smp_direction(smp),
314                                           smp->route.dr.dr_dlid ==
315                                           OPA_LID_PERMISSIVE,
316                                           smp->route.dr.dr_slid ==
317                                           OPA_LID_PERMISSIVE);
318 }
319
320 /*
321  * Return the forwarding port number from initial_path for outgoing SMP and
322  * from return_path for returning SMP
323  */
324 int smi_get_fwd_port(struct ib_smp *smp)
325 {
326         return (!ib_get_smp_direction(smp) ? smp->initial_path[smp->hop_ptr+1] :
327                 smp->return_path[smp->hop_ptr-1]);
328 }
329
330 /*
331  * Return the forwarding port number from initial_path for outgoing SMP and
332  * from return_path for returning SMP
333  */
334 int opa_smi_get_fwd_port(struct opa_smp *smp)
335 {
336         return !opa_get_smp_direction(smp) ? smp->route.dr.initial_path[smp->hop_ptr+1] :
337                 smp->route.dr.return_path[smp->hop_ptr-1];
338 }