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 */
46enum 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 */
127enum 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
204enum 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 */
241int 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}
246