From: Valentine Fatiev <valentinef@nvidia.com>
Subject: [PATCH] BACKPORT: drivers/infiniband/ulp/ipoib/ipoib_main.c

Change-Id: I6713db8c0a3c5ab46821e92215b48772574c0a9a
---
 drivers/infiniband/ulp/ipoib/ipoib_main.c | 239 +++++++++++++++++++---
 1 file changed, 210 insertions(+), 29 deletions(-)

--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -56,6 +56,9 @@
 MODULE_AUTHOR("Roland Dreier");
 MODULE_DESCRIPTION("IP-over-InfiniBand net driver");
 MODULE_LICENSE("Dual BSD/GPL");
+#ifdef RETPOLINE_MLNX
+MODULE_INFO(retpoline, "Y");
+#endif
 
 int ipoib_sendq_size __read_mostly = IPOIB_TX_RING_SIZE;
 int ipoib_recvq_size __read_mostly = IPOIB_RX_RING_SIZE;
@@ -168,7 +171,11 @@ int ipoib_open(struct net_device *dev)
 			if (flags & IFF_UP)
 				continue;
 
+#ifdef HAVE_DEV_CHANGE_FLAGS_HAS_3_PARAMS
 			dev_change_flags(cpriv->dev, flags | IFF_UP, NULL);
+#else
+			dev_change_flags(cpriv->dev, flags | IFF_UP);
+#endif
 		}
 		up_read(&priv->vlan_rwsem);
 	} else if (priv->parent) {
@@ -213,7 +220,11 @@ static int ipoib_stop(struct net_device
 			if (!(flags & IFF_UP))
 				continue;
 
+#ifdef HAVE_DEV_CHANGE_FLAGS_HAS_3_PARAMS
 			dev_change_flags(cpriv->dev, flags & ~IFF_UP, NULL);
+#else
+			dev_change_flags(cpriv->dev, flags & ~IFF_UP);
+#endif
 		}
 		up_read(&priv->vlan_rwsem);
 	}
@@ -260,7 +271,6 @@ static int ipoib_change_mtu(struct net_d
 				"link layer MTU - 4 (%u)\n", priv->mcast_mtu);
 
 	new_mtu = min(priv->mcast_mtu, priv->admin_mtu);
-
 	if (priv->rn_ops->ndo_change_mtu) {
 		bool carrier_status = netif_carrier_ok(dev);
 
@@ -268,7 +278,6 @@ static int ipoib_change_mtu(struct net_d
 
 		/* notify lower level on the real mtu */
 		ret = priv->rn_ops->ndo_change_mtu(dev, new_mtu);
-
 		if (carrier_status)
 			netif_carrier_on(dev);
 	} else {
@@ -305,9 +314,15 @@ static bool ipoib_is_dev_match_addr_rcu(
 		if (!in_dev)
 			return false;
 
+#ifdef HAVE_INET_CONFIRM_ADDR_EXPORTED
 		ret_addr = inet_confirm_addr(net, in_dev, 0,
 					     addr_in->sin_addr.s_addr,
 					     RT_SCOPE_HOST);
+#else
+		ret_addr = confirm_addr_indev(in_dev, 0,
+					      addr_in->sin_addr.s_addr,
+					      RT_SCOPE_HOST);
+#endif
 		in_dev_put(in_dev);
 		if (ret_addr)
 			return true;
@@ -353,9 +368,17 @@ struct ipoib_walk_data {
 };
 
 static int ipoib_upper_walk(struct net_device *upper,
-			    struct netdev_nested_priv *priv)
+#ifdef HAVE_NETDEV_NESTED_PRIV_STRUCT
+				struct netdev_nested_priv *priv)
+#else
+				void *_data)
+#endif
 {
+#ifdef HAVE_NETDEV_NESTED_PRIV_STRUCT
 	struct ipoib_walk_data *data = (struct ipoib_walk_data *)priv->data;
+#else
+	struct ipoib_walk_data *data = _data;
+#endif
 	int ret = 0;
 
 	if (ipoib_is_dev_match_addr_rcu(data->addr, upper)) {
@@ -380,12 +403,16 @@ static int ipoib_upper_walk(struct net_d
 static struct net_device *ipoib_get_net_dev_match_addr(
 		const struct sockaddr *addr, struct net_device *dev)
 {
+#ifdef HAVE_NETDEV_NESTED_PRIV_STRUCT
 	struct netdev_nested_priv priv;
+#endif
 	struct ipoib_walk_data data = {
 		.addr = addr,
 	};
 
+#ifdef HAVE_NETDEV_NESTED_PRIV_STRUCT
 	priv.data = (void *)&data;
+#endif
 	rcu_read_lock();
 	if (ipoib_is_dev_match_addr_rcu(addr, dev)) {
 		dev_hold(dev);
@@ -393,7 +420,13 @@ static struct net_device *ipoib_get_net_
 		goto out;
 	}
 
-	netdev_walk_all_upper_dev_rcu(dev, ipoib_upper_walk, &priv);
+	netdev_walk_all_upper_dev_rcu(dev, ipoib_upper_walk,
+#ifdef HAVE_NETDEV_NESTED_PRIV_STRUCT
+					&priv);
+#else
+					&data);
+#endif
+
 out:
 	rcu_read_unlock();
 	return data.result;
@@ -707,7 +740,11 @@ static void push_pseudo_header(struct sk
 {
 	struct ipoib_pseudo_header *phdr;
 
-	phdr = skb_push(skb, sizeof(*phdr));
+	phdr =
+#ifdef HAVE_BASECODE_EXTRAS
+		(struct ipoib_pseudo_header *)
+#endif
+		skb_push(skb, sizeof(*phdr));
 	memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN);
 }
 
@@ -1264,15 +1301,21 @@ unref:
 	return NETDEV_TX_OK;
 }
 
-static void ipoib_timeout(struct net_device *dev, unsigned int txqueue)
+#ifdef HAVE_NDO_TX_TIMEOUT_GET_2_PARAMS
+	static void ipoib_timeout(struct net_device *dev, unsigned int txqueue)
+#else
+	static void ipoib_timeout(struct net_device *dev)
+#endif
 {
 	struct ipoib_dev_priv *priv = ipoib_priv(dev);
+#ifdef HAVE_NDO_TX_TIMEOUT_GET_2_PARAMS
 	struct rdma_netdev *rn = netdev_priv(dev);
 
 	if (rn->tx_timeout) {
 		rn->tx_timeout(dev, txqueue);
 		return;
 	}
+#endif
 	ipoib_warn(priv, "transmit timeout: latency %d msecs\n",
 		   jiffies_to_msecs(jiffies - dev_trans_start(dev)));
 	ipoib_warn(priv,
@@ -1319,7 +1362,19 @@ static int ipoib_hard_header(struct sk_b
 {
 	struct ipoib_header *header;
 
-	header = skb_push(skb, sizeof(*header));
+#ifdef HAVE_BASECODE_EXTRAS
+	if (unlikely(dev->hard_header_len != IPOIB_HARD_LEN)) {
+		printk_once(KERN_DEBUG "%s: dropping packet, incorrect header length %u\n",
+			    dev->name, dev->hard_header_len);
+		return -EINVAL;
+	}
+#endif
+
+	header =
+#ifdef HAVE_BASECODE_EXTRAS
+		(struct ipoib_header *)
+#endif
+		skb_push(skb, sizeof *header);
 
 	header->proto = htons(type);
 	header->reserved = 0;
@@ -1358,6 +1413,69 @@ static int ipoib_get_iflink(const struct
 	return READ_ONCE(priv->parent->ifindex);
 }
 
+#ifdef CONFIG_COMPAT_LRO_ENABLED_IPOIB
+static int get_skb_hdr(struct sk_buff *skb, void **iphdr,
+		       void **tcph, u64 *hdr_flags, void *priv)
+{
+	unsigned int ip_len;
+	struct iphdr *iph;
+
+	if (unlikely(skb->protocol != htons(ETH_P_IP)))
+		return -1;
+
+	/*
+	* In the future we may add an else clause that verifies the
+	* checksum and allows devices which do not calculate checksum
+	* to use LRO.
+	*/
+	if (unlikely(skb->ip_summed != CHECKSUM_UNNECESSARY))
+		return -1;
+
+	/* Check for non-TCP packet */
+	skb_reset_network_header(skb);
+	iph = ip_hdr(skb);
+	if (iph->protocol != IPPROTO_TCP)
+		return -1;
+
+	ip_len = ip_hdrlen(skb);
+	skb_set_transport_header(skb, ip_len);
+	*tcph = tcp_hdr(skb);
+
+	/* check if IP header and TCP header are complete */
+	if (ntohs(iph->tot_len) < ip_len + tcp_hdrlen(skb))
+		return -1;
+
+	*hdr_flags = LRO_IPV4 | LRO_TCP;
+	*iphdr = iph;
+
+	return 0;
+}
+
+#ifdef CONFIG_COMPAT_LRO_ENABLED_IPOIB
+static void ipoib_lro_setup(struct ipoib_dev_priv *priv)
+{
+	priv->lro.lro_mgr.max_aggr		= IPOIB_LRO_MAX_AGGR;
+	priv->lro.lro_mgr.max_desc		= IPOIB_MAX_LRO_DESCRIPTORS;
+	priv->lro.lro_mgr.lro_arr		= priv->lro.lro_desc;
+	priv->lro.lro_mgr.get_skb_header	= get_skb_hdr;
+	priv->lro.lro_mgr.features		= LRO_F_NAPI;
+	priv->lro.lro_mgr.dev			= priv->dev;
+	priv->lro.lro_mgr.ip_summed_aggr	= CHECKSUM_UNNECESSARY;
+}
+#endif
+
+void set_lro_features_bit(struct ipoib_dev_priv *priv)
+{
+	u64 hw_support_lro = 0;
+	hw_support_lro = priv->dev->hw_features & NETIF_F_RXCSUM;
+	if (hw_support_lro) {
+		priv->dev->features |= NETIF_F_LRO;
+		priv->dev->hw_features |= NETIF_F_LRO;
+		priv->dev->wanted_features |= NETIF_F_LRO;
+	}
+}
+#endif
+
 static u32 ipoib_addr_hash(struct ipoib_neigh_hash *htbl, u8 *daddr)
 {
 	/*
@@ -1474,7 +1592,7 @@ static void ipoib_reap_neigh(struct work
 	gc_time = arp_tbl.gc_interval;
 	gc_time = min(gc_time, IPOIB_MAX_NEIGH_TIME);
 	gc_time = max(gc_time, IPOIB_MIN_NEIGH_TIME);
-	
+
 	__ipoib_reap_neigh(priv);
 
 	queue_delayed_work(priv->wq, &priv->neigh_reap_task,
@@ -1777,11 +1895,15 @@ static void ipoib_neigh_hash_uninit(stru
 static void ipoib_napi_add(struct net_device *dev)
 {
 	struct ipoib_dev_priv *priv = ipoib_priv(dev);
-
+#ifdef HAVE_NETIF_NAPI_ADD_WEIGHT
 	netif_napi_add_weight(dev, &priv->recv_napi, ipoib_rx_poll,
 			      IPOIB_NUM_WC);
 	netif_napi_add_weight(dev, &priv->send_napi, ipoib_tx_poll,
 			      MAX_SEND_CQE);
+#else
+	 netif_napi_add(dev, &priv->recv_napi, ipoib_rx_poll, IPOIB_NUM_WC);
+	 netif_napi_add(dev, &priv->send_napi, ipoib_tx_poll, MAX_SEND_CQE);
+#endif
 }
 
 static void ipoib_napi_del(struct net_device *dev)
@@ -1812,7 +1934,9 @@ static void ipoib_dev_uninit_default(str
 static int ipoib_dev_init_default(struct net_device *dev)
 {
 	struct ipoib_dev_priv *priv = ipoib_priv(dev);
+#ifdef HAVE_DEV_ADDR_MOD
 	u8 addr_mod[3];
+#endif
 
 	ipoib_napi_add(dev);
 
@@ -1823,6 +1947,10 @@ static int ipoib_dev_init_default(struct
 	if (!priv->rx_ring)
 		goto out;
 
+#ifdef CONFIG_COMPAT_LRO_ENABLED_IPOIB
+	ipoib_lro_setup(priv);
+#endif
+
 	priv->tx_ring = vzalloc(array_size(ipoib_sendq_size,
 					   sizeof(*priv->tx_ring)));
 	if (!priv->tx_ring) {
@@ -1840,10 +1968,16 @@ static int ipoib_dev_init_default(struct
 	}
 
 	/* after qp created set dev address */
+#ifdef HAVE_DEV_ADDR_MOD
 	addr_mod[0] = (priv->qp->qp_num >> 16) & 0xff;
 	addr_mod[1] = (priv->qp->qp_num >>  8) & 0xff;
 	addr_mod[2] = (priv->qp->qp_num) & 0xff;
 	dev_addr_mod(priv->dev, 1, addr_mod, sizeof(addr_mod));
+#else
+	priv->dev->dev_addr[1] = (priv->qp->qp_num >> 16) & 0xff;
+	priv->dev->dev_addr[2] = (priv->qp->qp_num >>  8) & 0xff;
+	priv->dev->dev_addr[3] = (priv->qp->qp_num) & 0xff;
+#endif
 
 	return 0;
 
@@ -1863,10 +1997,17 @@ static int ipoib_ioctl(struct net_device
 {
 	struct ipoib_dev_priv *priv = ipoib_priv(dev);
 
+#ifdef HAVE_NDO_ETH_IOCTL
 	if (!priv->rn_ops->ndo_eth_ioctl)
 		return -EOPNOTSUPP;
 
 	return priv->rn_ops->ndo_eth_ioctl(dev, ifr, cmd);
+#else
+	if (!priv->rn_ops->ndo_do_ioctl)
+		return -EOPNOTSUPP;
+
+	return priv->rn_ops->ndo_do_ioctl(dev, ifr, cmd);
+#endif
 }
 
 static int ipoib_dev_init(struct net_device *dev)
@@ -1950,7 +2091,11 @@ static void ipoib_parent_unregister_pre(
 	 * running ensures the it will not add more work.
 	 */
 	rtnl_lock();
+#ifdef HAVE_DEV_CHANGE_FLAGS_HAS_3_PARAMS
 	dev_change_flags(priv->dev, priv->dev->flags & ~IFF_UP, NULL);
+#else
+	dev_change_flags(priv->dev, priv->dev->flags & ~IFF_UP);
+#endif
 	rtnl_unlock();
 
 	/* ipoib_event() cannot be running once this returns */
@@ -2005,13 +2150,19 @@ static int ipoib_parent_init(struct net_
 			priv->ca->name, priv->port, result);
 		return result;
 	}
+#ifdef HAVE_DEV_ADDR_MOD
 	dev_addr_mod(priv->dev, 4, priv->local_gid.raw, sizeof(union ib_gid));
+#else
+	memcpy(priv->dev->dev_addr + 4, priv->local_gid.raw,
+	       sizeof(union ib_gid));
+#endif
 
 	SET_NETDEV_DEV(priv->dev, priv->ca->dev.parent);
 	priv->dev->dev_port = priv->port - 1;
 	/* Let's set this one too for backwards compatibility. */
 	priv->dev->dev_id = priv->port - 1;
 
+
 	return 0;
 }
 
@@ -2026,8 +2177,13 @@ static void ipoib_child_init(struct net_
 		memcpy(&priv->local_gid, priv->dev->dev_addr + 4,
 		       sizeof(priv->local_gid));
 	else {
+#ifdef HAVE_DEV_ADDR_MOD
 		__dev_addr_set(priv->dev, ppriv->dev->dev_addr,
 			       INFINIBAND_ALEN);
+#else
+		memcpy(priv->dev->dev_addr, ppriv->dev->dev_addr,
+		       INFINIBAND_ALEN);
+#endif
 		memcpy(&priv->local_gid, &ppriv->local_gid,
 		       sizeof(priv->local_gid));
 	}
@@ -2051,7 +2207,7 @@ static int ipoib_ndo_init(struct net_dev
 	ndev->mtu = IPOIB_UD_MTU(priv->max_ib_mtu);
 	priv->mcast_mtu = priv->admin_mtu = ndev->mtu;
 	rn->mtu = priv->mcast_mtu;
-	ndev->max_mtu = IPOIB_CM_MTU;
+       ndev->max_mtu = IPOIB_CM_MTU;
 
 	ndev->neigh_priv_len = sizeof(struct ipoib_neigh);
 
@@ -2119,6 +2275,9 @@ static void ipoib_ndo_uninit(struct net_
 		priv->wq = NULL;
 	}
 
+#ifndef HAVE_NETDEV_PUT
+	if (priv->parent)
+#endif
 	dev_put(priv->parent);
 }
 
@@ -2155,6 +2314,7 @@ static int ipoib_set_vf_guid(struct net_
 	return ib_set_vf_guid(priv->ca, vf, priv->port, guid, type);
 }
 
+#ifdef HAVE_NDO_GET_VF_GUID
 static int ipoib_get_vf_guid(struct net_device *dev, int vf,
 			     struct ifla_vf_guid *node_guid,
 			     struct ifla_vf_guid *port_guid)
@@ -2163,6 +2323,7 @@ static int ipoib_get_vf_guid(struct net_
 
 	return ib_get_vf_guid(priv->ca, vf, priv->port, node_guid, port_guid);
 }
+#endif
 
 static int ipoib_get_vf_stats(struct net_device *dev, int vf,
 			      struct ifla_vf_stats *vf_stats)
@@ -2196,20 +2357,28 @@ static const struct net_device_ops ipoib
 	.ndo_uninit		 = ipoib_ndo_uninit,
 	.ndo_open		 = ipoib_open,
 	.ndo_stop		 = ipoib_stop,
-	.ndo_change_mtu		 = ipoib_change_mtu,
+       .ndo_change_mtu		 = ipoib_change_mtu,
 	.ndo_fix_features	 = ipoib_fix_features,
 	.ndo_start_xmit		 = ipoib_start_xmit,
 	.ndo_tx_timeout		 = ipoib_timeout,
 	.ndo_set_rx_mode	 = ipoib_set_mcast_list,
-	.ndo_get_iflink		 = ipoib_get_iflink,
-	.ndo_set_vf_link_state	 = ipoib_set_vf_link_state,
-	.ndo_get_vf_config	 = ipoib_get_vf_config,
-	.ndo_get_vf_stats	 = ipoib_get_vf_stats,
-	.ndo_get_vf_guid	 = ipoib_get_vf_guid,
-	.ndo_set_vf_guid	 = ipoib_set_vf_guid,
-	.ndo_set_mac_address	 = ipoib_set_mac,
-	.ndo_get_stats64	 = ipoib_get_stats,
-	.ndo_eth_ioctl		 = ipoib_ioctl,
+       .ndo_get_iflink		 = ipoib_get_iflink,
+       .ndo_set_vf_link_state	 = ipoib_set_vf_link_state,
+       .ndo_get_vf_config	 = ipoib_get_vf_config,
+       .ndo_get_vf_stats	 = ipoib_get_vf_stats,
+#ifdef HAVE_NDO_GET_VF_GUID
+       .ndo_get_vf_guid         = ipoib_get_vf_guid,
+#endif
+       .ndo_set_vf_guid	 = ipoib_set_vf_guid,
+       .ndo_set_mac_address	 = ipoib_set_mac,
+       .ndo_get_stats64	 = ipoib_get_stats,
+
+#ifdef HAVE_NDO_ETH_IOCTL
+	.ndo_eth_ioctl            = ipoib_ioctl,
+#else
+	.ndo_do_ioctl		  = ipoib_ioctl,
+#endif
+
 };
 
 static const struct net_device_ops ipoib_netdev_ops_vf = {
@@ -2225,7 +2394,13 @@ static const struct net_device_ops ipoib
 	.ndo_get_iflink		 = ipoib_get_iflink,
 	.ndo_set_mac_address	 = ipoib_set_vf_local_mac,
 	.ndo_get_stats64	 = ipoib_get_stats,
-	.ndo_eth_ioctl		 = ipoib_ioctl,
+
+#ifdef HAVE_NDO_ETH_IOCTL
+	.ndo_eth_ioctl            = ipoib_ioctl,
+#else
+	.ndo_do_ioctl		  = ipoib_ioctl,
+#endif
+
 };
 
 static const struct net_device_ops ipoib_netdev_default_pf = {
@@ -2252,7 +2427,7 @@ void ipoib_setup_common(struct net_devic
 	dev->tx_queue_len	 = ipoib_sendq_size * 2;
 	dev->features		 = (NETIF_F_VLAN_CHALLENGED	|
 				    NETIF_F_HIGHDMA);
-	netif_keep_dst(dev);
+       netif_keep_dst(dev);
 
 	memcpy(dev->broadcast, ipv4_bcast_addr, INFINIBAND_ALEN);
 
@@ -2359,7 +2534,6 @@ int ipoib_intf_init(struct ib_device *hc
 	 */
 	priv->next_priv_destructor = dev->priv_destructor;
 	dev->priv_destructor = NULL;
-
 	ipoib_build_priv(dev);
 
 	return 0;
@@ -2397,7 +2571,6 @@ void ipoib_intf_free(struct net_device *
 {
 	struct ipoib_dev_priv *priv = ipoib_priv(dev);
 	struct rdma_netdev *rn = netdev_priv(dev);
-
 	dev->priv_destructor = priv->next_priv_destructor;
 	if (dev->priv_destructor)
 		dev->priv_destructor(dev);
@@ -2407,7 +2580,6 @@ void ipoib_intf_free(struct net_device *
 	 * attempt to call priv_destructor twice, prevent that from happening.
 	 */
 	dev->priv_destructor = NULL;
-
 	/* unregister/destroy is very complicated. Make bugs more obvious. */
 	rn->clnt_priv = NULL;
 
@@ -2472,7 +2644,11 @@ static void set_base_guid(struct ipoib_d
 	memcpy(&priv->local_gid.global.interface_id,
 	       &gid->global.interface_id,
 	       sizeof(gid->global.interface_id));
+#ifdef HAVE_DEV_ADDR_MOD
 	dev_addr_mod(netdev, 4, (u8 *)&priv->local_gid, sizeof(priv->local_gid));
+#else
+	memcpy(netdev->dev_addr + 4, &priv->local_gid, sizeof(priv->local_gid));
+#endif
 	clear_bit(IPOIB_FLAG_DEV_ADDR_SET, &priv->flags);
 
 	netif_addr_unlock_bh(netdev);
@@ -2510,7 +2686,7 @@ static int ipoib_set_mac(struct net_devi
 {
 	struct ipoib_dev_priv *priv = ipoib_priv(dev);
 	struct sockaddr_storage *ss = addr;
-	int ret;
+	int ret = 0;
 
 	if (!(dev->priv_flags & IFF_LIVE_ADDR_CHANGE) && netif_running(dev))
 		return -EBUSY;
@@ -2673,6 +2849,13 @@ static struct net_device *ipoib_add_port
 		if (!rc && ops->priv_size < params.sizeof_priv)
 			ops->priv_size = params.sizeof_priv;
 	}
+#ifdef CONFIG_COMPAT_LRO_ENABLED_IPOIB
+	/* force lro on the dev->features, because the function
+	 * register_netdev disable it according to our private lro
+	 */
+	set_lro_features_bit(priv);
+#endif
+
 	/*
 	 * We cannot set priv_destructor before register_netdev because we
 	 * need priv to be always valid during the error flow to execute
@@ -2680,7 +2863,6 @@ static struct net_device *ipoib_add_port
 	 * enter priv_destructor mode once we are completely registered.
 	 */
 	ndev->priv_destructor = ipoib_intf_free;
-
 	if (ipoib_intercept_dev_id_attr(ndev))
 		goto sysfs_failed;
 	if (ipoib_cm_add_mode_attr(ndev))
@@ -2751,10 +2933,9 @@ static void ipoib_remove_one(struct ib_d
 
 		list_for_each_entry_safe(cpriv, tcpriv, &priv->child_intfs,
 					 list)
-			unregister_netdevice_queue(cpriv->dev, &head);
+				unregister_netdevice_queue(cpriv->dev, &head);
 		unregister_netdevice_queue(priv->dev, &head);
 		unregister_netdevice_many(&head);
-
 		rtnl_unlock();
 	}
 
