1219820Sjeff/*
2219820Sjeff * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved.
3219820Sjeff *
4219820Sjeff * This software is available to you under a choice of one of two
5219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
6219820Sjeff * General Public License (GPL) Version 2, available from the file
7219820Sjeff * COPYING in the main directory of this source tree, or the
8219820Sjeff * OpenIB.org BSD license below:
9219820Sjeff *
10219820Sjeff *     Redistribution and use in source and binary forms, with or
11219820Sjeff *     without modification, are permitted provided that the following
12219820Sjeff *     conditions are met:
13219820Sjeff *
14219820Sjeff *      - Redistributions of source code must retain the above
15219820Sjeff *        copyright notice, this list of conditions and the following
16219820Sjeff *        disclaimer.
17219820Sjeff *
18219820Sjeff *      - Redistributions in binary form must reproduce the above
19219820Sjeff *        copyright notice, this list of conditions and the following
20219820Sjeff *        disclaimer in the documentation and/or other materials
21219820Sjeff *        provided with the distribution.
22219820Sjeff *
23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30219820Sjeff * SOFTWARE.
31219820Sjeff */
32219820Sjeff
33219820Sjeff#include "mlx4_ib.h"
34219820Sjeff#include <rdma/ib_addr.h>
35219820Sjeff#include <linux/inet.h>
36219820Sjeff#include <linux/string.h>
37219820Sjeff#include <rdma/ib_cache.h>
38219820Sjeff
39219820Sjeffint mlx4_ib_resolve_grh(struct mlx4_ib_dev *dev, const struct ib_ah_attr *ah_attr,
40219820Sjeff			u8 *mac, int *is_mcast, u8 port)
41219820Sjeff{
42219820Sjeff	struct mlx4_ib_iboe *iboe = &dev->iboe;
43219820Sjeff	struct in6_addr in6;
44219820Sjeff
45219820Sjeff	*is_mcast = 0;
46219820Sjeff	spin_lock(&iboe->lock);
47219820Sjeff	if (!iboe->netdevs[port - 1]) {
48219820Sjeff		spin_unlock(&iboe->lock);
49219820Sjeff		return -EINVAL;
50219820Sjeff	}
51219820Sjeff	spin_unlock(&iboe->lock);
52219820Sjeff
53219820Sjeff	memcpy(&in6, ah_attr->grh.dgid.raw, sizeof in6);
54219820Sjeff	if (rdma_link_local_addr(&in6))
55219820Sjeff		rdma_get_ll_mac(&in6, mac);
56219820Sjeff	else if (rdma_is_multicast_addr(&in6)) {
57219820Sjeff		rdma_get_mcast_mac(&in6, mac);
58219820Sjeff		*is_mcast = 1;
59219820Sjeff	} else
60219820Sjeff		return -EINVAL;
61219820Sjeff
62219820Sjeff	return 0;
63219820Sjeff}
64219820Sjeff
65219820Sjeffstatic struct ib_ah *create_ib_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr,
66219820Sjeff				  struct mlx4_ib_ah *ah)
67219820Sjeff{
68219820Sjeff	struct mlx4_dev *dev = to_mdev(pd->device)->dev;
69219820Sjeff
70219820Sjeff	ah->av.ib.port_pd = cpu_to_be32(to_mpd(pd)->pdn | (ah_attr->port_num << 24));
71219820Sjeff	ah->av.ib.g_slid  = ah_attr->src_path_bits;
72219820Sjeff	if (ah_attr->ah_flags & IB_AH_GRH) {
73219820Sjeff		ah->av.ib.g_slid   |= 0x80;
74219820Sjeff		ah->av.ib.gid_index = ah_attr->grh.sgid_index;
75219820Sjeff		ah->av.ib.hop_limit = ah_attr->grh.hop_limit;
76219820Sjeff		ah->av.ib.sl_tclass_flowlabel |=
77219820Sjeff			cpu_to_be32((ah_attr->grh.traffic_class << 20) |
78219820Sjeff				    ah_attr->grh.flow_label);
79219820Sjeff		memcpy(ah->av.ib.dgid, ah_attr->grh.dgid.raw, 16);
80219820Sjeff	}
81219820Sjeff
82219820Sjeff	ah->av.ib.dlid    = cpu_to_be16(ah_attr->dlid);
83219820Sjeff	if (ah_attr->static_rate) {
84219820Sjeff		ah->av.ib.stat_rate = ah_attr->static_rate + MLX4_STAT_RATE_OFFSET;
85219820Sjeff		while (ah->av.ib.stat_rate > IB_RATE_2_5_GBPS + MLX4_STAT_RATE_OFFSET &&
86219820Sjeff		       !(1 << ah->av.ib.stat_rate & dev->caps.stat_rate_support))
87219820Sjeff			--ah->av.ib.stat_rate;
88219820Sjeff	}
89219820Sjeff	ah->av.ib.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28);
90219820Sjeff
91219820Sjeff	return &ah->ibah;
92219820Sjeff}
93219820Sjeff
94219820Sjeffstatic struct ib_ah *create_iboe_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr,
95219820Sjeff				   struct mlx4_ib_ah *ah)
96219820Sjeff{
97219820Sjeff	struct mlx4_ib_dev *ibdev = to_mdev(pd->device);
98219820Sjeff	struct mlx4_dev *dev = ibdev->dev;
99219820Sjeff	u8 mac[6];
100219820Sjeff	int err;
101219820Sjeff	int is_mcast;
102219820Sjeff	u16 vlan_tag;
103219820Sjeff	union ib_gid sgid;
104219820Sjeff
105219820Sjeff	err = mlx4_ib_resolve_grh(ibdev, ah_attr, mac, &is_mcast, ah_attr->port_num);
106219820Sjeff	if (err)
107219820Sjeff		return ERR_PTR(err);
108219820Sjeff
109219820Sjeff	memcpy(ah->av.eth.mac, mac, 6);
110219820Sjeff	err = ib_get_cached_gid(pd->device, ah_attr->port_num, ah_attr->grh.sgid_index, &sgid);
111219820Sjeff	if (err)
112219820Sjeff		return ERR_PTR(err);
113219820Sjeff	vlan_tag = rdma_get_vlan_id(&sgid);
114219820Sjeff	if (vlan_tag < 0x1000)
115219820Sjeff		vlan_tag |= (ah_attr->sl & 7) << 13;
116219820Sjeff	ah->av.eth.port_pd = cpu_to_be32(to_mpd(pd)->pdn | (ah_attr->port_num << 24));
117219820Sjeff	ah->av.eth.gid_index = ah_attr->grh.sgid_index;
118219820Sjeff	ah->av.eth.vlan = cpu_to_be16(vlan_tag);
119219820Sjeff	if (ah_attr->static_rate) {
120219820Sjeff		ah->av.eth.stat_rate = ah_attr->static_rate + MLX4_STAT_RATE_OFFSET;
121219820Sjeff		while (ah->av.eth.stat_rate > IB_RATE_2_5_GBPS + MLX4_STAT_RATE_OFFSET &&
122219820Sjeff		       !(1 << ah->av.eth.stat_rate & dev->caps.stat_rate_support))
123219820Sjeff			--ah->av.eth.stat_rate;
124219820Sjeff	}
125219820Sjeff
126219820Sjeff	/*
127219820Sjeff	 * HW requires multicast LID so we just choose one.
128219820Sjeff	 */
129219820Sjeff	if (is_mcast)
130219820Sjeff		ah->av.ib.dlid = cpu_to_be16(0xc000);
131219820Sjeff
132219820Sjeff	memcpy(ah->av.eth.dgid, ah_attr->grh.dgid.raw, 16);
133219820Sjeff	ah->av.eth.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28);
134219820Sjeff
135219820Sjeff	return &ah->ibah;
136219820Sjeff}
137219820Sjeff
138219820Sjeffstruct ib_ah *mlx4_ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr)
139219820Sjeff{
140219820Sjeff	struct mlx4_ib_ah *ah;
141219820Sjeff	struct ib_ah *ret;
142219820Sjeff
143219820Sjeff	ah = kzalloc(sizeof *ah, GFP_ATOMIC);
144219820Sjeff	if (!ah)
145219820Sjeff		return ERR_PTR(-ENOMEM);
146219820Sjeff
147219820Sjeff	if (rdma_port_get_link_layer(pd->device, ah_attr->port_num) == IB_LINK_LAYER_ETHERNET) {
148219820Sjeff		if (!(ah_attr->ah_flags & IB_AH_GRH)) {
149219820Sjeff			ret = ERR_PTR(-EINVAL);
150219820Sjeff			goto out;
151219820Sjeff		} else {
152219820Sjeff			/* TBD: need to handle the case when we get called
153219820Sjeff			in an atomic context and there we might sleep. We
154219820Sjeff			don't expect this currently since we're working with
155219820Sjeff			link local addresses which we can translate without
156219820Sjeff			going to sleep */
157219820Sjeff			ret = create_iboe_ah(pd, ah_attr, ah);
158219820Sjeff			if (IS_ERR(ret))
159219820Sjeff				goto out;
160219820Sjeff			else
161219820Sjeff				return ret;
162219820Sjeff		}
163219820Sjeff	} else
164219820Sjeff		return create_ib_ah(pd, ah_attr, ah); /* never fails */
165219820Sjeff
166219820Sjeffout:
167219820Sjeff	kfree(ah);
168219820Sjeff	return ret;
169219820Sjeff}
170219820Sjeff
171219820Sjeffint mlx4_ib_query_ah(struct ib_ah *ibah, struct ib_ah_attr *ah_attr)
172219820Sjeff{
173219820Sjeff	struct mlx4_ib_ah *ah = to_mah(ibah);
174219820Sjeff	enum rdma_link_layer ll;
175219820Sjeff
176219820Sjeff	memset(ah_attr, 0, sizeof *ah_attr);
177219820Sjeff	ah_attr->sl = be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 28;
178219820Sjeff	ah_attr->port_num = be32_to_cpu(ah->av.ib.port_pd) >> 24;
179219820Sjeff	ll = rdma_port_get_link_layer(ibah->device, ah_attr->port_num);
180219820Sjeff	ah_attr->dlid = ll == IB_LINK_LAYER_INFINIBAND ? be16_to_cpu(ah->av.ib.dlid) : 0;
181219820Sjeff	if (ah->av.ib.stat_rate)
182219820Sjeff		ah_attr->static_rate = ah->av.ib.stat_rate - MLX4_STAT_RATE_OFFSET;
183219820Sjeff	ah_attr->src_path_bits = ah->av.ib.g_slid & 0x7F;
184219820Sjeff
185219820Sjeff	if (mlx4_ib_ah_grh_present(ah)) {
186219820Sjeff		ah_attr->ah_flags = IB_AH_GRH;
187219820Sjeff
188219820Sjeff		ah_attr->grh.traffic_class =
189219820Sjeff			be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 20;
190219820Sjeff		ah_attr->grh.flow_label =
191219820Sjeff			be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) & 0xfffff;
192219820Sjeff		ah_attr->grh.hop_limit  = ah->av.ib.hop_limit;
193219820Sjeff		ah_attr->grh.sgid_index = ah->av.ib.gid_index;
194219820Sjeff		memcpy(ah_attr->grh.dgid.raw, ah->av.ib.dgid, 16);
195219820Sjeff	}
196219820Sjeff
197219820Sjeff	return 0;
198219820Sjeff}
199219820Sjeff
200219820Sjeffint mlx4_ib_destroy_ah(struct ib_ah *ah)
201219820Sjeff{
202219820Sjeff	kfree(to_mah(ah));
203219820Sjeff	return 0;
204219820Sjeff}
205219820Sjeff
206