162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright(c) 2020 Intel Corporation.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/*
862306a36Sopenharmony_ci * This file contains HFI1 support for ipoib functionality
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "ipoib.h"
1262306a36Sopenharmony_ci#include "hfi.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic u32 qpn_from_mac(const u8 *mac_arr)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	return (u32)mac_arr[1] << 16 | mac_arr[2] << 8 | mac_arr[3];
1762306a36Sopenharmony_ci}
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic int hfi1_ipoib_dev_init(struct net_device *dev)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev);
2262306a36Sopenharmony_ci	int ret;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
2562306a36Sopenharmony_ci	if (!dev->tstats)
2662306a36Sopenharmony_ci		return -ENOMEM;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	ret = priv->netdev_ops->ndo_init(dev);
2962306a36Sopenharmony_ci	if (ret)
3062306a36Sopenharmony_ci		goto out_ret;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	ret = hfi1_netdev_add_data(priv->dd,
3362306a36Sopenharmony_ci				   qpn_from_mac(priv->netdev->dev_addr),
3462306a36Sopenharmony_ci				   dev);
3562306a36Sopenharmony_ci	if (ret < 0) {
3662306a36Sopenharmony_ci		priv->netdev_ops->ndo_uninit(dev);
3762306a36Sopenharmony_ci		goto out_ret;
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	return 0;
4162306a36Sopenharmony_ciout_ret:
4262306a36Sopenharmony_ci	free_percpu(dev->tstats);
4362306a36Sopenharmony_ci	dev->tstats = NULL;
4462306a36Sopenharmony_ci	return ret;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic void hfi1_ipoib_dev_uninit(struct net_device *dev)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	free_percpu(dev->tstats);
5262306a36Sopenharmony_ci	dev->tstats = NULL;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	hfi1_netdev_remove_data(priv->dd, qpn_from_mac(priv->netdev->dev_addr));
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	priv->netdev_ops->ndo_uninit(dev);
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic int hfi1_ipoib_dev_open(struct net_device *dev)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev);
6262306a36Sopenharmony_ci	int ret;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	ret = priv->netdev_ops->ndo_open(dev);
6562306a36Sopenharmony_ci	if (!ret) {
6662306a36Sopenharmony_ci		struct hfi1_ibport *ibp = to_iport(priv->device,
6762306a36Sopenharmony_ci						   priv->port_num);
6862306a36Sopenharmony_ci		struct rvt_qp *qp;
6962306a36Sopenharmony_ci		u32 qpn = qpn_from_mac(priv->netdev->dev_addr);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		rcu_read_lock();
7262306a36Sopenharmony_ci		qp = rvt_lookup_qpn(ib_to_rvt(priv->device), &ibp->rvp, qpn);
7362306a36Sopenharmony_ci		if (!qp) {
7462306a36Sopenharmony_ci			rcu_read_unlock();
7562306a36Sopenharmony_ci			priv->netdev_ops->ndo_stop(dev);
7662306a36Sopenharmony_ci			return -EINVAL;
7762306a36Sopenharmony_ci		}
7862306a36Sopenharmony_ci		rvt_get_qp(qp);
7962306a36Sopenharmony_ci		priv->qp = qp;
8062306a36Sopenharmony_ci		rcu_read_unlock();
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		hfi1_netdev_enable_queues(priv->dd);
8362306a36Sopenharmony_ci		hfi1_ipoib_napi_tx_enable(dev);
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return ret;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int hfi1_ipoib_dev_stop(struct net_device *dev)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (!priv->qp)
9462306a36Sopenharmony_ci		return 0;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	hfi1_ipoib_napi_tx_disable(dev);
9762306a36Sopenharmony_ci	hfi1_netdev_disable_queues(priv->dd);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	rvt_put_qp(priv->qp);
10062306a36Sopenharmony_ci	priv->qp = NULL;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return priv->netdev_ops->ndo_stop(dev);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic const struct net_device_ops hfi1_ipoib_netdev_ops = {
10662306a36Sopenharmony_ci	.ndo_init         = hfi1_ipoib_dev_init,
10762306a36Sopenharmony_ci	.ndo_uninit       = hfi1_ipoib_dev_uninit,
10862306a36Sopenharmony_ci	.ndo_open         = hfi1_ipoib_dev_open,
10962306a36Sopenharmony_ci	.ndo_stop         = hfi1_ipoib_dev_stop,
11062306a36Sopenharmony_ci	.ndo_get_stats64  = dev_get_tstats64,
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int hfi1_ipoib_mcast_attach(struct net_device *dev,
11462306a36Sopenharmony_ci				   struct ib_device *device,
11562306a36Sopenharmony_ci				   union ib_gid *mgid,
11662306a36Sopenharmony_ci				   u16 mlid,
11762306a36Sopenharmony_ci				   int set_qkey,
11862306a36Sopenharmony_ci				   u32 qkey)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev);
12162306a36Sopenharmony_ci	u32 qpn = (u32)qpn_from_mac(priv->netdev->dev_addr);
12262306a36Sopenharmony_ci	struct hfi1_ibport *ibp = to_iport(priv->device, priv->port_num);
12362306a36Sopenharmony_ci	struct rvt_qp *qp;
12462306a36Sopenharmony_ci	int ret = -EINVAL;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	rcu_read_lock();
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	qp = rvt_lookup_qpn(ib_to_rvt(priv->device), &ibp->rvp, qpn);
12962306a36Sopenharmony_ci	if (qp) {
13062306a36Sopenharmony_ci		rvt_get_qp(qp);
13162306a36Sopenharmony_ci		rcu_read_unlock();
13262306a36Sopenharmony_ci		if (set_qkey)
13362306a36Sopenharmony_ci			priv->qkey = qkey;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci		/* attach QP to multicast group */
13662306a36Sopenharmony_ci		ret = ib_attach_mcast(&qp->ibqp, mgid, mlid);
13762306a36Sopenharmony_ci		rvt_put_qp(qp);
13862306a36Sopenharmony_ci	} else {
13962306a36Sopenharmony_ci		rcu_read_unlock();
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return ret;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic int hfi1_ipoib_mcast_detach(struct net_device *dev,
14662306a36Sopenharmony_ci				   struct ib_device *device,
14762306a36Sopenharmony_ci				   union ib_gid *mgid,
14862306a36Sopenharmony_ci				   u16 mlid)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev);
15162306a36Sopenharmony_ci	u32 qpn = (u32)qpn_from_mac(priv->netdev->dev_addr);
15262306a36Sopenharmony_ci	struct hfi1_ibport *ibp = to_iport(priv->device, priv->port_num);
15362306a36Sopenharmony_ci	struct rvt_qp *qp;
15462306a36Sopenharmony_ci	int ret = -EINVAL;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	rcu_read_lock();
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	qp = rvt_lookup_qpn(ib_to_rvt(priv->device), &ibp->rvp, qpn);
15962306a36Sopenharmony_ci	if (qp) {
16062306a36Sopenharmony_ci		rvt_get_qp(qp);
16162306a36Sopenharmony_ci		rcu_read_unlock();
16262306a36Sopenharmony_ci		ret = ib_detach_mcast(&qp->ibqp, mgid, mlid);
16362306a36Sopenharmony_ci		rvt_put_qp(qp);
16462306a36Sopenharmony_ci	} else {
16562306a36Sopenharmony_ci		rcu_read_unlock();
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci	return ret;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void hfi1_ipoib_netdev_dtor(struct net_device *dev)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	hfi1_ipoib_txreq_deinit(priv);
17562306a36Sopenharmony_ci	hfi1_ipoib_rxq_deinit(priv->netdev);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	free_percpu(dev->tstats);
17862306a36Sopenharmony_ci	dev->tstats = NULL;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void hfi1_ipoib_set_id(struct net_device *dev, int id)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct hfi1_ipoib_dev_priv *priv = hfi1_ipoib_priv(dev);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	priv->pkey_index = (u16)id;
18662306a36Sopenharmony_ci	ib_query_pkey(priv->device,
18762306a36Sopenharmony_ci		      priv->port_num,
18862306a36Sopenharmony_ci		      priv->pkey_index,
18962306a36Sopenharmony_ci		      &priv->pkey);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int hfi1_ipoib_setup_rn(struct ib_device *device,
19362306a36Sopenharmony_ci			       u32 port_num,
19462306a36Sopenharmony_ci			       struct net_device *netdev,
19562306a36Sopenharmony_ci			       void *param)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct hfi1_devdata *dd = dd_from_ibdev(device);
19862306a36Sopenharmony_ci	struct rdma_netdev *rn = netdev_priv(netdev);
19962306a36Sopenharmony_ci	struct hfi1_ipoib_dev_priv *priv;
20062306a36Sopenharmony_ci	int rc;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	rn->send = hfi1_ipoib_send;
20362306a36Sopenharmony_ci	rn->tx_timeout = hfi1_ipoib_tx_timeout;
20462306a36Sopenharmony_ci	rn->attach_mcast = hfi1_ipoib_mcast_attach;
20562306a36Sopenharmony_ci	rn->detach_mcast = hfi1_ipoib_mcast_detach;
20662306a36Sopenharmony_ci	rn->set_id = hfi1_ipoib_set_id;
20762306a36Sopenharmony_ci	rn->hca = device;
20862306a36Sopenharmony_ci	rn->port_num = port_num;
20962306a36Sopenharmony_ci	rn->mtu = netdev->mtu;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	priv = hfi1_ipoib_priv(netdev);
21262306a36Sopenharmony_ci	priv->dd = dd;
21362306a36Sopenharmony_ci	priv->netdev = netdev;
21462306a36Sopenharmony_ci	priv->device = device;
21562306a36Sopenharmony_ci	priv->port_num = port_num;
21662306a36Sopenharmony_ci	priv->netdev_ops = netdev->netdev_ops;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	ib_query_pkey(device, port_num, priv->pkey_index, &priv->pkey);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	rc = hfi1_ipoib_txreq_init(priv);
22162306a36Sopenharmony_ci	if (rc) {
22262306a36Sopenharmony_ci		dd_dev_err(dd, "IPoIB netdev TX init - failed(%d)\n", rc);
22362306a36Sopenharmony_ci		return rc;
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	rc = hfi1_ipoib_rxq_init(netdev);
22762306a36Sopenharmony_ci	if (rc) {
22862306a36Sopenharmony_ci		dd_dev_err(dd, "IPoIB netdev RX init - failed(%d)\n", rc);
22962306a36Sopenharmony_ci		hfi1_ipoib_txreq_deinit(priv);
23062306a36Sopenharmony_ci		return rc;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	netdev->netdev_ops = &hfi1_ipoib_netdev_ops;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	netdev->priv_destructor = hfi1_ipoib_netdev_dtor;
23662306a36Sopenharmony_ci	netdev->needs_free_netdev = true;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return 0;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ciint hfi1_ipoib_rn_get_params(struct ib_device *device,
24262306a36Sopenharmony_ci			     u32 port_num,
24362306a36Sopenharmony_ci			     enum rdma_netdev_t type,
24462306a36Sopenharmony_ci			     struct rdma_netdev_alloc_params *params)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct hfi1_devdata *dd = dd_from_ibdev(device);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (type != RDMA_NETDEV_IPOIB)
24962306a36Sopenharmony_ci		return -EOPNOTSUPP;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (!HFI1_CAP_IS_KSET(AIP) || !dd->num_netdev_contexts)
25262306a36Sopenharmony_ci		return -EOPNOTSUPP;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (!port_num || port_num > dd->num_pports)
25562306a36Sopenharmony_ci		return -EINVAL;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	params->sizeof_priv = sizeof(struct hfi1_ipoib_rdma_netdev);
25862306a36Sopenharmony_ci	params->txqs = dd->num_sdma;
25962306a36Sopenharmony_ci	params->rxqs = dd->num_netdev_contexts;
26062306a36Sopenharmony_ci	params->param = NULL;
26162306a36Sopenharmony_ci	params->initialize_rdma_netdev = hfi1_ipoib_setup_rn;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	return 0;
26462306a36Sopenharmony_ci}
265