Merge branch 'next-i2c' of git://aeryn.fluff.org.uk/bjdooks/linux
[linux-2.6] / 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 /*
43  * Fixup a directed route SMP for sending
44  * Return 0 if the SMP should be discarded
45  */
46 enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp,
47                                        u8 node_type, int port_num)
48 {
49         u8 hop_ptr, hop_cnt;
50
51         hop_ptr = smp->hop_ptr;
52         hop_cnt = smp->hop_cnt;
53
54         /* See section 14.2.2.2, Vol 1 IB spec */
55         if (!ib_get_smp_direction(smp)) {
56                 /* C14-9:1 */
57                 if (hop_cnt && hop_ptr == 0) {
58                         smp->hop_ptr++;
59                         return (smp->initial_path[smp->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                         /* smp->return_path set when received */
69                         smp->hop_ptr++;
70                         return (smp->initial_path[smp->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                         /* smp->return_path set when received */
77                         smp->hop_ptr++;
78                         return (node_type == RDMA_NODE_IB_SWITCH ||
79                                 smp->dr_dlid == IB_LID_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                         smp->hop_ptr--;
91                         return (smp->return_path[smp->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                         smp->hop_ptr--;
101                         return (smp->return_path[smp->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                         smp->hop_ptr--;
108                         /* C14-13:3 -- SMPs destined for SM shouldn't be here */
109                         return (node_type == RDMA_NODE_IB_SWITCH ||
110                                 smp->dr_slid == IB_LID_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  * Adjust information for a received SMP
125  * Return 0 if the SMP should be dropped
126  */
127 enum smi_action smi_handle_dr_smp_recv(struct ib_smp *smp, u8 node_type,
128                                        int port_num, int phys_port_cnt)
129 {
130         u8 hop_ptr, hop_cnt;
131
132         hop_ptr = smp->hop_ptr;
133         hop_cnt = smp->hop_cnt;
134
135         /* See section 14.2.2.2, Vol 1 IB spec */
136         if (!ib_get_smp_direction(smp)) {
137                 /* C14-9:1 -- sender should have incremented hop_ptr */
138                 if (hop_cnt && hop_ptr == 0)
139                         return IB_SMI_DISCARD;
140
141                 /* C14-9:2 -- intermediate hop */
142                 if (hop_ptr && hop_ptr < hop_cnt) {
143                         if (node_type != RDMA_NODE_IB_SWITCH)
144                                 return IB_SMI_DISCARD;
145
146                         smp->return_path[hop_ptr] = port_num;
147                         /* smp->hop_ptr updated when sending */
148                         return (smp->initial_path[hop_ptr+1] <= phys_port_cnt ?
149                                 IB_SMI_HANDLE : IB_SMI_DISCARD);
150                 }
151
152                 /* C14-9:3 -- We're at the end of the DR segment of path */
153                 if (hop_ptr == hop_cnt) {
154                         if (hop_cnt)
155                                 smp->return_path[hop_ptr] = port_num;
156                         /* smp->hop_ptr updated when sending */
157
158                         return (node_type == RDMA_NODE_IB_SWITCH ||
159                                 smp->dr_dlid == IB_LID_PERMISSIVE ?
160                                 IB_SMI_HANDLE : IB_SMI_DISCARD);
161                 }
162
163                 /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
164                 /* C14-9:5 -- fail unreasonable hop pointer */
165                 return (hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
166
167         } else {
168
169                 /* C14-13:1 */
170                 if (hop_cnt && hop_ptr == hop_cnt + 1) {
171                         smp->hop_ptr--;
172                         return (smp->return_path[smp->hop_ptr] ==
173                                 port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
174                 }
175
176                 /* C14-13:2 */
177                 if (2 <= hop_ptr && hop_ptr <= hop_cnt) {
178                         if (node_type != RDMA_NODE_IB_SWITCH)
179                                 return IB_SMI_DISCARD;
180
181                         /* smp->hop_ptr updated when sending */
182                         return (smp->return_path[hop_ptr-1] <= phys_port_cnt ?
183                                 IB_SMI_HANDLE : IB_SMI_DISCARD);
184                 }
185
186                 /* C14-13:3 -- We're at the end of the DR segment of path */
187                 if (hop_ptr == 1) {
188                         if (smp->dr_slid == IB_LID_PERMISSIVE) {
189                                 /* giving SMP to SM - update hop_ptr */
190                                 smp->hop_ptr--;
191                                 return IB_SMI_HANDLE;
192                         }
193                         /* smp->hop_ptr updated when sending */
194                         return (node_type == RDMA_NODE_IB_SWITCH ?
195                                 IB_SMI_HANDLE : IB_SMI_DISCARD);
196                 }
197
198                 /* C14-13:4 -- hop_ptr = 0 -> give to SM */
199                 /* C14-13:5 -- Check for unreasonable hop pointer */
200                 return (hop_ptr == 0 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
201         }
202 }
203
204 enum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp)
205 {
206         u8 hop_ptr, hop_cnt;
207
208         hop_ptr = smp->hop_ptr;
209         hop_cnt = smp->hop_cnt;
210
211         if (!ib_get_smp_direction(smp)) {
212                 /* C14-9:2 -- intermediate hop */
213                 if (hop_ptr && hop_ptr < hop_cnt)
214                         return IB_SMI_FORWARD;
215
216                 /* C14-9:3 -- at the end of the DR segment of path */
217                 if (hop_ptr == hop_cnt)
218                         return (smp->dr_dlid == IB_LID_PERMISSIVE ?
219                                 IB_SMI_SEND : IB_SMI_LOCAL);
220
221                 /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
222                 if (hop_ptr == hop_cnt + 1)
223                         return IB_SMI_SEND;
224         } else {
225                 /* C14-13:2  -- intermediate hop */
226                 if (2 <= hop_ptr && hop_ptr <= hop_cnt)
227                         return IB_SMI_FORWARD;
228
229                 /* C14-13:3 -- at the end of the DR segment of path */
230                 if (hop_ptr == 1)
231                         return (smp->dr_slid != IB_LID_PERMISSIVE ?
232                                 IB_SMI_SEND : IB_SMI_LOCAL);
233         }
234         return IB_SMI_LOCAL;
235 }
236
237 /*
238  * Return the forwarding port number from initial_path for outgoing SMP and
239  * from return_path for returning SMP
240  */
241 int smi_get_fwd_port(struct ib_smp *smp)
242 {
243         return (!ib_get_smp_direction(smp) ? smp->initial_path[smp->hop_ptr+1] :
244                 smp->return_path[smp->hop_ptr-1]);
245 }