1/*
2 * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved.
3 *
4 * This software is available to you under a choice of one of two
5 * licenses.  You may choose to be licensed under the terms of the GNU
6 * General Public License (GPL) Version 2, available from the file
7 * COPYING in the main directory of this source tree, or the
8 * OpenIB.org BSD license below:
9 *
10 *     Redistribution and use in source and binary forms, with or
11 *     without modification, are permitted provided that the following
12 *     conditions are met:
13 *
14 *      - Redistributions of source code must retain the above
15 *        copyright notice, this list of conditions and the following
16 *        disclaimer.
17 *
18 *      - Redistributions in binary form must reproduce the above
19 *        copyright notice, this list of conditions and the following
20 *        disclaimer in the documentation and/or other materials
21 *        provided with the distribution.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * SOFTWARE.
31 */
32
33#include "mlx4_ib.h"
34#include <rdma/ib_addr.h>
35#include <linux/inet.h>
36#include <linux/string.h>
37#include <rdma/ib_cache.h>
38
39int mlx4_ib_resolve_grh(struct mlx4_ib_dev *dev, const struct ib_ah_attr *ah_attr,
40			u8 *mac, int *is_mcast, u8 port)
41{
42	struct mlx4_ib_iboe *iboe = &dev->iboe;
43	struct in6_addr in6;
44
45	*is_mcast = 0;
46	spin_lock(&iboe->lock);
47	if (!iboe->netdevs[port - 1]) {
48		spin_unlock(&iboe->lock);
49		return -EINVAL;
50	}
51	spin_unlock(&iboe->lock);
52
53	memcpy(&in6, ah_attr->grh.dgid.raw, sizeof in6);
54	if (rdma_link_local_addr(&in6))
55		rdma_get_ll_mac(&in6, mac);
56	else if (rdma_is_multicast_addr(&in6)) {
57		rdma_get_mcast_mac(&in6, mac);
58		*is_mcast = 1;
59	} else
60		return -EINVAL;
61
62	return 0;
63}
64
65static struct ib_ah *create_ib_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr,
66				  struct mlx4_ib_ah *ah)
67{
68	struct mlx4_dev *dev = to_mdev(pd->device)->dev;
69
70	ah->av.ib.port_pd = cpu_to_be32(to_mpd(pd)->pdn | (ah_attr->port_num << 24));
71	ah->av.ib.g_slid  = ah_attr->src_path_bits;
72	if (ah_attr->ah_flags & IB_AH_GRH) {
73		ah->av.ib.g_slid   |= 0x80;
74		ah->av.ib.gid_index = ah_attr->grh.sgid_index;
75		ah->av.ib.hop_limit = ah_attr->grh.hop_limit;
76		ah->av.ib.sl_tclass_flowlabel |=
77			cpu_to_be32((ah_attr->grh.traffic_class << 20) |
78				    ah_attr->grh.flow_label);
79		memcpy(ah->av.ib.dgid, ah_attr->grh.dgid.raw, 16);
80	}
81
82	ah->av.ib.dlid    = cpu_to_be16(ah_attr->dlid);
83	if (ah_attr->static_rate) {
84		ah->av.ib.stat_rate = ah_attr->static_rate + MLX4_STAT_RATE_OFFSET;
85		while (ah->av.ib.stat_rate > IB_RATE_2_5_GBPS + MLX4_STAT_RATE_OFFSET &&
86		       !(1 << ah->av.ib.stat_rate & dev->caps.stat_rate_support))
87			--ah->av.ib.stat_rate;
88	}
89	ah->av.ib.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28);
90
91	return &ah->ibah;
92}
93
94static struct ib_ah *create_iboe_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr,
95				   struct mlx4_ib_ah *ah)
96{
97	struct mlx4_ib_dev *ibdev = to_mdev(pd->device);
98	struct mlx4_dev *dev = ibdev->dev;
99	u8 mac[6];
100	int err;
101	int is_mcast;
102	u16 vlan_tag;
103	union ib_gid sgid;
104
105	err = mlx4_ib_resolve_grh(ibdev, ah_attr, mac, &is_mcast, ah_attr->port_num);
106	if (err)
107		return ERR_PTR(err);
108
109	memcpy(ah->av.eth.mac, mac, 6);
110	err = ib_get_cached_gid(pd->device, ah_attr->port_num, ah_attr->grh.sgid_index, &sgid);
111	if (err)
112		return ERR_PTR(err);
113	vlan_tag = rdma_get_vlan_id(&sgid);
114	if (vlan_tag < 0x1000)
115		vlan_tag |= (ah_attr->sl & 7) << 13;
116	ah->av.eth.port_pd = cpu_to_be32(to_mpd(pd)->pdn | (ah_attr->port_num << 24));
117	ah->av.eth.gid_index = ah_attr->grh.sgid_index;
118	ah->av.eth.vlan = cpu_to_be16(vlan_tag);
119	if (ah_attr->static_rate) {
120		ah->av.eth.stat_rate = ah_attr->static_rate + MLX4_STAT_RATE_OFFSET;
121		while (ah->av.eth.stat_rate > IB_RATE_2_5_GBPS + MLX4_STAT_RATE_OFFSET &&
122		       !(1 << ah->av.eth.stat_rate & dev->caps.stat_rate_support))
123			--ah->av.eth.stat_rate;
124	}
125
126	/*
127	 * HW requires multicast LID so we just choose one.
128	 */
129	if (is_mcast)
130		ah->av.ib.dlid = cpu_to_be16(0xc000);
131
132	memcpy(ah->av.eth.dgid, ah_attr->grh.dgid.raw, 16);
133	ah->av.eth.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28);
134
135	return &ah->ibah;
136}
137
138struct ib_ah *mlx4_ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr)
139{
140	struct mlx4_ib_ah *ah;
141	struct ib_ah *ret;
142
143	ah = kzalloc(sizeof *ah, GFP_ATOMIC);
144	if (!ah)
145		return ERR_PTR(-ENOMEM);
146
147	if (rdma_port_get_link_layer(pd->device, ah_attr->port_num) == IB_LINK_LAYER_ETHERNET) {
148		if (!(ah_attr->ah_flags & IB_AH_GRH)) {
149			ret = ERR_PTR(-EINVAL);
150			goto out;
151		} else {
152			/* TBD: need to handle the case when we get called
153			in an atomic context and there we might sleep. We
154			don't expect this currently since we're working with
155			link local addresses which we can translate without
156			going to sleep */
157			ret = create_iboe_ah(pd, ah_attr, ah);
158			if (IS_ERR(ret))
159				goto out;
160			else
161				return ret;
162		}
163	} else
164		return create_ib_ah(pd, ah_attr, ah); /* never fails */
165
166out:
167	kfree(ah);
168	return ret;
169}
170
171int mlx4_ib_query_ah(struct ib_ah *ibah, struct ib_ah_attr *ah_attr)
172{
173	struct mlx4_ib_ah *ah = to_mah(ibah);
174	enum rdma_link_layer ll;
175
176	memset(ah_attr, 0, sizeof *ah_attr);
177	ah_attr->sl = be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 28;
178	ah_attr->port_num = be32_to_cpu(ah->av.ib.port_pd) >> 24;
179	ll = rdma_port_get_link_layer(ibah->device, ah_attr->port_num);
180	ah_attr->dlid = ll == IB_LINK_LAYER_INFINIBAND ? be16_to_cpu(ah->av.ib.dlid) : 0;
181	if (ah->av.ib.stat_rate)
182		ah_attr->static_rate = ah->av.ib.stat_rate - MLX4_STAT_RATE_OFFSET;
183	ah_attr->src_path_bits = ah->av.ib.g_slid & 0x7F;
184
185	if (mlx4_ib_ah_grh_present(ah)) {
186		ah_attr->ah_flags = IB_AH_GRH;
187
188		ah_attr->grh.traffic_class =
189			be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 20;
190		ah_attr->grh.flow_label =
191			be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) & 0xfffff;
192		ah_attr->grh.hop_limit  = ah->av.ib.hop_limit;
193		ah_attr->grh.sgid_index = ah->av.ib.gid_index;
194		memcpy(ah_attr->grh.dgid.raw, ah->av.ib.dgid, 16);
195	}
196
197	return 0;
198}
199
200int mlx4_ib_destroy_ah(struct ib_ah *ah)
201{
202	kfree(to_mah(ah));
203	return 0;
204}
205
206