1 | // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) |
2 | /* |
3 | * Copyright(c) 2020 Intel Corporation. |
4 | * |
5 | */ |
6 | |
7 | /* |
8 | * This file contains HFI1 support for ipoib functionality |
9 | */ |
10 | |
11 | #include "ipoib.h" |
12 | #include "hfi.h" |
13 | |
14 | static u32 qpn_from_mac(const u8 *mac_arr) |
15 | { |
16 | return (u32)mac_arr[1] << 16 | mac_arr[2] << 8 | mac_arr[3]; |
17 | } |
18 | |
19 | static int hfi1_ipoib_dev_init(struct net_device *dev) |
20 | { |
21 | struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev); |
22 | int ret; |
23 | |
24 | dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); |
25 | if (!dev->tstats) |
26 | return -ENOMEM; |
27 | |
28 | ret = priv->netdev_ops->ndo_init(dev); |
29 | if (ret) |
30 | goto out_ret; |
31 | |
32 | ret = hfi1_netdev_add_data(dd: priv->dd, |
33 | id: qpn_from_mac(mac_arr: priv->netdev->dev_addr), |
34 | data: dev); |
35 | if (ret < 0) { |
36 | priv->netdev_ops->ndo_uninit(dev); |
37 | goto out_ret; |
38 | } |
39 | |
40 | return 0; |
41 | out_ret: |
42 | free_percpu(pdata: dev->tstats); |
43 | dev->tstats = NULL; |
44 | return ret; |
45 | } |
46 | |
47 | static void hfi1_ipoib_dev_uninit(struct net_device *dev) |
48 | { |
49 | struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev); |
50 | |
51 | free_percpu(pdata: dev->tstats); |
52 | dev->tstats = NULL; |
53 | |
54 | hfi1_netdev_remove_data(dd: priv->dd, id: qpn_from_mac(mac_arr: priv->netdev->dev_addr)); |
55 | |
56 | priv->netdev_ops->ndo_uninit(dev); |
57 | } |
58 | |
59 | static int hfi1_ipoib_dev_open(struct net_device *dev) |
60 | { |
61 | struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev); |
62 | int ret; |
63 | |
64 | ret = priv->netdev_ops->ndo_open(dev); |
65 | if (!ret) { |
66 | struct hfi1_ibport *ibp = to_iport(ibdev: priv->device, |
67 | port: priv->port_num); |
68 | struct rvt_qp *qp; |
69 | u32 qpn = qpn_from_mac(mac_arr: priv->netdev->dev_addr); |
70 | |
71 | rcu_read_lock(); |
72 | qp = rvt_lookup_qpn(rdi: ib_to_rvt(ibdev: priv->device), rvp: &ibp->rvp, qpn); |
73 | if (!qp) { |
74 | rcu_read_unlock(); |
75 | priv->netdev_ops->ndo_stop(dev); |
76 | return -EINVAL; |
77 | } |
78 | rvt_get_qp(qp); |
79 | priv->qp = qp; |
80 | rcu_read_unlock(); |
81 | |
82 | hfi1_netdev_enable_queues(dd: priv->dd); |
83 | hfi1_ipoib_napi_tx_enable(dev); |
84 | } |
85 | |
86 | return ret; |
87 | } |
88 | |
89 | static int hfi1_ipoib_dev_stop(struct net_device *dev) |
90 | { |
91 | struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev); |
92 | |
93 | if (!priv->qp) |
94 | return 0; |
95 | |
96 | hfi1_ipoib_napi_tx_disable(dev); |
97 | hfi1_netdev_disable_queues(dd: priv->dd); |
98 | |
99 | rvt_put_qp(qp: priv->qp); |
100 | priv->qp = NULL; |
101 | |
102 | return priv->netdev_ops->ndo_stop(dev); |
103 | } |
104 | |
105 | static const struct net_device_ops hfi1_ipoib_netdev_ops = { |
106 | .ndo_init = hfi1_ipoib_dev_init, |
107 | .ndo_uninit = hfi1_ipoib_dev_uninit, |
108 | .ndo_open = hfi1_ipoib_dev_open, |
109 | .ndo_stop = hfi1_ipoib_dev_stop, |
110 | .ndo_get_stats64 = dev_get_tstats64, |
111 | }; |
112 | |
113 | static int hfi1_ipoib_mcast_attach(struct net_device *dev, |
114 | struct ib_device *device, |
115 | union ib_gid *mgid, |
116 | u16 mlid, |
117 | int set_qkey, |
118 | u32 qkey) |
119 | { |
120 | struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev); |
121 | u32 qpn = (u32)qpn_from_mac(mac_arr: priv->netdev->dev_addr); |
122 | struct hfi1_ibport *ibp = to_iport(ibdev: priv->device, port: priv->port_num); |
123 | struct rvt_qp *qp; |
124 | int ret = -EINVAL; |
125 | |
126 | rcu_read_lock(); |
127 | |
128 | qp = rvt_lookup_qpn(rdi: ib_to_rvt(ibdev: priv->device), rvp: &ibp->rvp, qpn); |
129 | if (qp) { |
130 | rvt_get_qp(qp); |
131 | rcu_read_unlock(); |
132 | if (set_qkey) |
133 | priv->qkey = qkey; |
134 | |
135 | /* attach QP to multicast group */ |
136 | ret = ib_attach_mcast(qp: &qp->ibqp, gid: mgid, lid: mlid); |
137 | rvt_put_qp(qp); |
138 | } else { |
139 | rcu_read_unlock(); |
140 | } |
141 | |
142 | return ret; |
143 | } |
144 | |
145 | static int hfi1_ipoib_mcast_detach(struct net_device *dev, |
146 | struct ib_device *device, |
147 | union ib_gid *mgid, |
148 | u16 mlid) |
149 | { |
150 | struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev); |
151 | u32 qpn = (u32)qpn_from_mac(mac_arr: priv->netdev->dev_addr); |
152 | struct hfi1_ibport *ibp = to_iport(ibdev: priv->device, port: priv->port_num); |
153 | struct rvt_qp *qp; |
154 | int ret = -EINVAL; |
155 | |
156 | rcu_read_lock(); |
157 | |
158 | qp = rvt_lookup_qpn(rdi: ib_to_rvt(ibdev: priv->device), rvp: &ibp->rvp, qpn); |
159 | if (qp) { |
160 | rvt_get_qp(qp); |
161 | rcu_read_unlock(); |
162 | ret = ib_detach_mcast(qp: &qp->ibqp, gid: mgid, lid: mlid); |
163 | rvt_put_qp(qp); |
164 | } else { |
165 | rcu_read_unlock(); |
166 | } |
167 | return ret; |
168 | } |
169 | |
170 | static void hfi1_ipoib_netdev_dtor(struct net_device *dev) |
171 | { |
172 | struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev); |
173 | |
174 | hfi1_ipoib_txreq_deinit(priv); |
175 | hfi1_ipoib_rxq_deinit(dev: priv->netdev); |
176 | |
177 | free_percpu(pdata: dev->tstats); |
178 | dev->tstats = NULL; |
179 | } |
180 | |
181 | static void hfi1_ipoib_set_id(struct net_device *dev, int id) |
182 | { |
183 | struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev); |
184 | |
185 | priv->pkey_index = (u16)id; |
186 | ib_query_pkey(device: priv->device, |
187 | port_num: priv->port_num, |
188 | index: priv->pkey_index, |
189 | pkey: &priv->pkey); |
190 | } |
191 | |
192 | static int hfi1_ipoib_setup_rn(struct ib_device *device, |
193 | u32 port_num, |
194 | struct net_device *netdev, |
195 | void *param) |
196 | { |
197 | struct hfi1_devdata *dd = dd_from_ibdev(ibdev: device); |
198 | struct rdma_netdev *rn = netdev_priv(dev: netdev); |
199 | struct hfi1_ipoib_dev_priv *priv; |
200 | int rc; |
201 | |
202 | rn->send = hfi1_ipoib_send; |
203 | rn->tx_timeout = hfi1_ipoib_tx_timeout; |
204 | rn->attach_mcast = hfi1_ipoib_mcast_attach; |
205 | rn->detach_mcast = hfi1_ipoib_mcast_detach; |
206 | rn->set_id = hfi1_ipoib_set_id; |
207 | rn->hca = device; |
208 | rn->port_num = port_num; |
209 | rn->mtu = netdev->mtu; |
210 | |
211 | priv = hfi1_ipoib_priv(dev: netdev); |
212 | priv->dd = dd; |
213 | priv->netdev = netdev; |
214 | priv->device = device; |
215 | priv->port_num = port_num; |
216 | priv->netdev_ops = netdev->netdev_ops; |
217 | |
218 | ib_query_pkey(device, port_num, index: priv->pkey_index, pkey: &priv->pkey); |
219 | |
220 | rc = hfi1_ipoib_txreq_init(priv); |
221 | if (rc) { |
222 | dd_dev_err(dd, "IPoIB netdev TX init - failed(%d)\n" , rc); |
223 | return rc; |
224 | } |
225 | |
226 | rc = hfi1_ipoib_rxq_init(dev: netdev); |
227 | if (rc) { |
228 | dd_dev_err(dd, "IPoIB netdev RX init - failed(%d)\n" , rc); |
229 | hfi1_ipoib_txreq_deinit(priv); |
230 | return rc; |
231 | } |
232 | |
233 | netdev->netdev_ops = &hfi1_ipoib_netdev_ops; |
234 | |
235 | netdev->priv_destructor = hfi1_ipoib_netdev_dtor; |
236 | netdev->needs_free_netdev = true; |
237 | |
238 | return 0; |
239 | } |
240 | |
241 | int hfi1_ipoib_rn_get_params(struct ib_device *device, |
242 | u32 port_num, |
243 | enum rdma_netdev_t type, |
244 | struct rdma_netdev_alloc_params *params) |
245 | { |
246 | struct hfi1_devdata *dd = dd_from_ibdev(ibdev: device); |
247 | |
248 | if (type != RDMA_NETDEV_IPOIB) |
249 | return -EOPNOTSUPP; |
250 | |
251 | if (!HFI1_CAP_IS_KSET(AIP) || !dd->num_netdev_contexts) |
252 | return -EOPNOTSUPP; |
253 | |
254 | if (!port_num || port_num > dd->num_pports) |
255 | return -EINVAL; |
256 | |
257 | params->sizeof_priv = sizeof(struct hfi1_ipoib_rdma_netdev); |
258 | params->txqs = dd->num_sdma; |
259 | params->rxqs = dd->num_netdev_contexts; |
260 | params->param = NULL; |
261 | params->initialize_rdma_netdev = hfi1_ipoib_setup_rn; |
262 | |
263 | return 0; |
264 | } |
265 | |