1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004, 2005 Mellanox Technologies Ltd.  All rights reserved.
3219820Sjeff * Copyright (c) 2004, 2005 Infinicon Corporation.  All rights reserved.
4219820Sjeff * Copyright (c) 2004, 2005 Intel Corporation.  All rights reserved.
5219820Sjeff * Copyright (c) 2004, 2005 Topspin Corporation.  All rights reserved.
6219820Sjeff * Copyright (c) 2004-2007 Voltaire Corporation.  All rights reserved.
7219820Sjeff * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
8219820Sjeff *
9219820Sjeff * This software is available to you under a choice of one of two
10219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
11219820Sjeff * General Public License (GPL) Version 2, available from the file
12219820Sjeff * COPYING in the main directory of this source tree, or the
13219820Sjeff * OpenIB.org BSD license below:
14219820Sjeff *
15219820Sjeff *     Redistribution and use in source and binary forms, with or
16219820Sjeff *     without modification, are permitted provided that the following
17219820Sjeff *     conditions are met:
18219820Sjeff *
19219820Sjeff *      - Redistributions of source code must retain the above
20219820Sjeff *        copyright notice, this list of conditions and the following
21219820Sjeff *        disclaimer.
22219820Sjeff *
23219820Sjeff *      - Redistributions in binary form must reproduce the above
24219820Sjeff *        copyright notice, this list of conditions and the following
25219820Sjeff *        disclaimer in the documentation and/or other materials
26219820Sjeff *        provided with the distribution.
27219820Sjeff *
28219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
32219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
33219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35219820Sjeff * SOFTWARE.
36219820Sjeff *
37219820Sjeff */
38219820Sjeff
39219820Sjeff#include <rdma/ib_smi.h>
40219820Sjeff#include "smi.h"
41219820Sjeff
42219820Sjeff/*
43219820Sjeff * Fixup a directed route SMP for sending
44219820Sjeff * Return 0 if the SMP should be discarded
45219820Sjeff */
46219820Sjeffenum smi_action smi_handle_dr_smp_send(struct ib_smp *smp,
47219820Sjeff				       u8 node_type, int port_num)
48219820Sjeff{
49219820Sjeff	u8 hop_ptr, hop_cnt;
50219820Sjeff
51219820Sjeff	hop_ptr = smp->hop_ptr;
52219820Sjeff	hop_cnt = smp->hop_cnt;
53219820Sjeff
54219820Sjeff	/* See section 14.2.2.2, Vol 1 IB spec */
55219820Sjeff	if (!ib_get_smp_direction(smp)) {
56219820Sjeff		/* C14-9:1 */
57219820Sjeff		if (hop_cnt && hop_ptr == 0) {
58219820Sjeff			smp->hop_ptr++;
59219820Sjeff			return (smp->initial_path[smp->hop_ptr] ==
60219820Sjeff				port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
61219820Sjeff		}
62219820Sjeff
63219820Sjeff		/* C14-9:2 */
64219820Sjeff		if (hop_ptr && hop_ptr < hop_cnt) {
65219820Sjeff			if (node_type != RDMA_NODE_IB_SWITCH)
66219820Sjeff				return IB_SMI_DISCARD;
67219820Sjeff
68219820Sjeff			/* smp->return_path set when received */
69219820Sjeff			smp->hop_ptr++;
70219820Sjeff			return (smp->initial_path[smp->hop_ptr] ==
71219820Sjeff				port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
72219820Sjeff		}
73219820Sjeff
74219820Sjeff		/* C14-9:3 -- We're at the end of the DR segment of path */
75219820Sjeff		if (hop_ptr == hop_cnt) {
76219820Sjeff			/* smp->return_path set when received */
77219820Sjeff			smp->hop_ptr++;
78219820Sjeff			return (node_type == RDMA_NODE_IB_SWITCH ||
79219820Sjeff				smp->dr_dlid == IB_LID_PERMISSIVE ?
80219820Sjeff				IB_SMI_HANDLE : IB_SMI_DISCARD);
81219820Sjeff		}
82219820Sjeff
83219820Sjeff		/* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
84219820Sjeff		/* C14-9:5 -- Fail unreasonable hop pointer */
85219820Sjeff		return (hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
86219820Sjeff
87219820Sjeff	} else {
88219820Sjeff		/* C14-13:1 */
89219820Sjeff		if (hop_cnt && hop_ptr == hop_cnt + 1) {
90219820Sjeff			smp->hop_ptr--;
91219820Sjeff			return (smp->return_path[smp->hop_ptr] ==
92219820Sjeff				port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
93219820Sjeff		}
94219820Sjeff
95219820Sjeff		/* C14-13:2 */
96219820Sjeff		if (2 <= hop_ptr && hop_ptr <= hop_cnt) {
97219820Sjeff			if (node_type != RDMA_NODE_IB_SWITCH)
98219820Sjeff				return IB_SMI_DISCARD;
99219820Sjeff
100219820Sjeff			smp->hop_ptr--;
101219820Sjeff			return (smp->return_path[smp->hop_ptr] ==
102219820Sjeff				port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
103219820Sjeff		}
104219820Sjeff
105219820Sjeff		/* C14-13:3 -- at the end of the DR segment of path */
106219820Sjeff		if (hop_ptr == 1) {
107219820Sjeff			smp->hop_ptr--;
108219820Sjeff			/* C14-13:3 -- SMPs destined for SM shouldn't be here */
109219820Sjeff			return (node_type == RDMA_NODE_IB_SWITCH ||
110219820Sjeff				smp->dr_slid == IB_LID_PERMISSIVE ?
111219820Sjeff				IB_SMI_HANDLE : IB_SMI_DISCARD);
112219820Sjeff		}
113219820Sjeff
114219820Sjeff		/* C14-13:4 -- hop_ptr = 0 -> should have gone to SM */
115219820Sjeff		if (hop_ptr == 0)
116219820Sjeff			return IB_SMI_HANDLE;
117219820Sjeff
118219820Sjeff		/* C14-13:5 -- Check for unreasonable hop pointer */
119219820Sjeff		return IB_SMI_DISCARD;
120219820Sjeff	}
121219820Sjeff}
122219820Sjeff
123219820Sjeff/*
124219820Sjeff * Adjust information for a received SMP
125219820Sjeff * Return 0 if the SMP should be dropped
126219820Sjeff */
127219820Sjeffenum smi_action smi_handle_dr_smp_recv(struct ib_smp *smp, u8 node_type,
128219820Sjeff				       int port_num, int phys_port_cnt)
129219820Sjeff{
130219820Sjeff	u8 hop_ptr, hop_cnt;
131219820Sjeff
132219820Sjeff	hop_ptr = smp->hop_ptr;
133219820Sjeff	hop_cnt = smp->hop_cnt;
134219820Sjeff
135219820Sjeff	/* See section 14.2.2.2, Vol 1 IB spec */
136219820Sjeff	if (!ib_get_smp_direction(smp)) {
137219820Sjeff		/* C14-9:1 -- sender should have incremented hop_ptr */
138219820Sjeff		if (hop_cnt && hop_ptr == 0)
139219820Sjeff			return IB_SMI_DISCARD;
140219820Sjeff
141219820Sjeff		/* C14-9:2 -- intermediate hop */
142219820Sjeff		if (hop_ptr && hop_ptr < hop_cnt) {
143219820Sjeff			if (node_type != RDMA_NODE_IB_SWITCH)
144219820Sjeff				return IB_SMI_DISCARD;
145219820Sjeff
146219820Sjeff			smp->return_path[hop_ptr] = port_num;
147219820Sjeff			/* smp->hop_ptr updated when sending */
148219820Sjeff			return (smp->initial_path[hop_ptr+1] <= phys_port_cnt ?
149219820Sjeff				IB_SMI_HANDLE : IB_SMI_DISCARD);
150219820Sjeff		}
151219820Sjeff
152219820Sjeff		/* C14-9:3 -- We're at the end of the DR segment of path */
153219820Sjeff		if (hop_ptr == hop_cnt) {
154219820Sjeff			if (hop_cnt)
155219820Sjeff				smp->return_path[hop_ptr] = port_num;
156219820Sjeff			/* smp->hop_ptr updated when sending */
157219820Sjeff
158219820Sjeff			return (node_type == RDMA_NODE_IB_SWITCH ||
159219820Sjeff				smp->dr_dlid == IB_LID_PERMISSIVE ?
160219820Sjeff				IB_SMI_HANDLE : IB_SMI_DISCARD);
161219820Sjeff		}
162219820Sjeff
163219820Sjeff		/* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
164219820Sjeff		/* C14-9:5 -- fail unreasonable hop pointer */
165219820Sjeff		return (hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
166219820Sjeff
167219820Sjeff	} else {
168219820Sjeff
169219820Sjeff		/* C14-13:1 */
170219820Sjeff		if (hop_cnt && hop_ptr == hop_cnt + 1) {
171219820Sjeff			smp->hop_ptr--;
172219820Sjeff			return (smp->return_path[smp->hop_ptr] ==
173219820Sjeff				port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
174219820Sjeff		}
175219820Sjeff
176219820Sjeff		/* C14-13:2 */
177219820Sjeff		if (2 <= hop_ptr && hop_ptr <= hop_cnt) {
178219820Sjeff			if (node_type != RDMA_NODE_IB_SWITCH)
179219820Sjeff				return IB_SMI_DISCARD;
180219820Sjeff
181219820Sjeff			/* smp->hop_ptr updated when sending */
182219820Sjeff			return (smp->return_path[hop_ptr-1] <= phys_port_cnt ?
183219820Sjeff				IB_SMI_HANDLE : IB_SMI_DISCARD);
184219820Sjeff		}
185219820Sjeff
186219820Sjeff		/* C14-13:3 -- We're at the end of the DR segment of path */
187219820Sjeff		if (hop_ptr == 1) {
188219820Sjeff			if (smp->dr_slid == IB_LID_PERMISSIVE) {
189219820Sjeff				/* giving SMP to SM - update hop_ptr */
190219820Sjeff				smp->hop_ptr--;
191219820Sjeff				return IB_SMI_HANDLE;
192219820Sjeff			}
193219820Sjeff			/* smp->hop_ptr updated when sending */
194219820Sjeff			return (node_type == RDMA_NODE_IB_SWITCH ?
195219820Sjeff				IB_SMI_HANDLE : IB_SMI_DISCARD);
196219820Sjeff		}
197219820Sjeff
198219820Sjeff		/* C14-13:4 -- hop_ptr = 0 -> give to SM */
199219820Sjeff		/* C14-13:5 -- Check for unreasonable hop pointer */
200219820Sjeff		return (hop_ptr == 0 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
201219820Sjeff	}
202219820Sjeff}
203219820Sjeff
204219820Sjeffenum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp)
205219820Sjeff{
206219820Sjeff	u8 hop_ptr, hop_cnt;
207219820Sjeff
208219820Sjeff	hop_ptr = smp->hop_ptr;
209219820Sjeff	hop_cnt = smp->hop_cnt;
210219820Sjeff
211219820Sjeff	if (!ib_get_smp_direction(smp)) {
212219820Sjeff		/* C14-9:2 -- intermediate hop */
213219820Sjeff		if (hop_ptr && hop_ptr < hop_cnt)
214219820Sjeff			return IB_SMI_FORWARD;
215219820Sjeff
216219820Sjeff		/* C14-9:3 -- at the end of the DR segment of path */
217219820Sjeff		if (hop_ptr == hop_cnt)
218219820Sjeff			return (smp->dr_dlid == IB_LID_PERMISSIVE ?
219219820Sjeff				IB_SMI_SEND : IB_SMI_LOCAL);
220219820Sjeff
221219820Sjeff		/* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
222219820Sjeff		if (hop_ptr == hop_cnt + 1)
223219820Sjeff			return IB_SMI_SEND;
224219820Sjeff	} else {
225219820Sjeff		/* C14-13:2  -- intermediate hop */
226219820Sjeff		if (2 <= hop_ptr && hop_ptr <= hop_cnt)
227219820Sjeff			return IB_SMI_FORWARD;
228219820Sjeff
229219820Sjeff		/* C14-13:3 -- at the end of the DR segment of path */
230219820Sjeff		if (hop_ptr == 1)
231219820Sjeff			return (smp->dr_slid != IB_LID_PERMISSIVE ?
232219820Sjeff				IB_SMI_SEND : IB_SMI_LOCAL);
233219820Sjeff	}
234219820Sjeff	return IB_SMI_LOCAL;
235219820Sjeff}
236219820Sjeff
237219820Sjeff/*
238219820Sjeff * Return the forwarding port number from initial_path for outgoing SMP and
239219820Sjeff * from return_path for returning SMP
240219820Sjeff */
241219820Sjeffint smi_get_fwd_port(struct ib_smp *smp)
242219820Sjeff{
243219820Sjeff	return (!ib_get_smp_direction(smp) ? smp->initial_path[smp->hop_ptr+1] :
244219820Sjeff		smp->return_path[smp->hop_ptr-1]);
245219820Sjeff}
246