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