| /* | 
 |  * Copyright (c) 2007 Mellanox Technologies. All rights reserved. | 
 |  * | 
 |  * This software is available to you under a choice of one of two | 
 |  * licenses.  You may choose to be licensed under the terms of the GNU | 
 |  * General Public License (GPL) Version 2, available from the file | 
 |  * COPYING in the main directory of this source tree, or the | 
 |  * OpenIB.org BSD license below: | 
 |  * | 
 |  *     Redistribution and use in source and binary forms, with or | 
 |  *     without modification, are permitted provided that the following | 
 |  *     conditions are met: | 
 |  * | 
 |  *      - Redistributions of source code must retain the above | 
 |  *        copyright notice, this list of conditions and the following | 
 |  *        disclaimer. | 
 |  * | 
 |  *      - Redistributions in binary form must reproduce the above | 
 |  *        copyright notice, this list of conditions and the following | 
 |  *        disclaimer in the documentation and/or other materials | 
 |  *        provided with the distribution. | 
 |  * | 
 |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
 |  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
 |  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 
 |  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | 
 |  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | 
 |  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 
 |  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
 |  * SOFTWARE. | 
 |  * | 
 |  */ | 
 |  | 
 | #include <linux_compat.h> | 
 | #include <linux/mlx4/driver.h> | 
 | #include <linux/mlx4/device.h> | 
 | #include <linux/mlx4/cmd.h> | 
 | #include <linux/mlx4/cq.h> | 
 |  | 
 | #include "mlx4_en.h" | 
 | #include "en_port.h" | 
 |  | 
 | int mlx4_en_setup_tc(struct ether *dev, uint8_t up) | 
 | { | 
 | 	panic("Disabled"); | 
 | #if 0 // AKAROS_PORT | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	int i; | 
 | 	unsigned int offset = 0; | 
 |  | 
 | 	if (up && up != MLX4_EN_NUM_UP) | 
 | 		return -EINVAL; | 
 |  | 
 | 	netdev_set_num_tc(dev, up); | 
 |  | 
 | 	/* Partition Tx queues evenly amongst UP's */ | 
 | 	for (i = 0; i < up; i++) { | 
 | 		netdev_set_tc_queue(dev, i, priv->num_tx_rings_p_up, offset); | 
 | 		offset += priv->num_tx_rings_p_up; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | #endif | 
 | } | 
 |  | 
 | #ifdef CONFIG_NET_RX_BUSY_POLL | 
 | /* must be called with local_bh_disable()d */ | 
 | static int mlx4_en_low_latency_recv(struct napi_struct *napi) | 
 | { | 
 | 	struct mlx4_en_cq *cq = container_of(napi, struct mlx4_en_cq, napi); | 
 | 	struct ether *dev = cq->dev; | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_rx_ring *rx_ring = priv->rx_ring[cq->ring]; | 
 | 	int done; | 
 |  | 
 | 	if (!priv->port_up) | 
 | 		return LL_FLUSH_FAILED; | 
 |  | 
 | 	if (!mlx4_en_cq_lock_poll(cq)) | 
 | 		return LL_FLUSH_BUSY; | 
 |  | 
 | 	done = mlx4_en_process_rx_cq(dev, cq, 4); | 
 | 	if (likely(done)) | 
 | 		rx_ring->cleaned += done; | 
 | 	else | 
 | 		rx_ring->misses++; | 
 |  | 
 | 	mlx4_en_cq_unlock_poll(cq); | 
 |  | 
 | 	return done; | 
 | } | 
 | #endif	/* CONFIG_NET_RX_BUSY_POLL */ | 
 |  | 
 | #ifdef CONFIG_RFS_ACCEL | 
 |  | 
 | struct mlx4_en_filter { | 
 | 	struct list_head next; | 
 | 	struct work_struct work; | 
 |  | 
 | 	uint8_t     ip_proto; | 
 | 	__be32 src_ip; | 
 | 	__be32 dst_ip; | 
 | 	__be16 src_port; | 
 | 	__be16 dst_port; | 
 |  | 
 | 	int rxq_index; | 
 | 	struct mlx4_en_priv *priv; | 
 | 	uint32_t flow_id;		/* RFS infrastructure id */ | 
 | 	int id;				/* mlx4_en driver id */ | 
 | 	uint64_t reg_id;		/* Flow steering API id */ | 
 | 	uint8_t activated;		/* Used to prevent expiry before filter | 
 | 					 * is attached | 
 | 					 */ | 
 | 	struct hlist_node filter_chain; | 
 | }; | 
 |  | 
 | static void mlx4_en_filter_rfs_expire(struct mlx4_en_priv *priv); | 
 |  | 
 | static enum mlx4_net_trans_rule_id mlx4_ip_proto_to_trans_rule_id(uint8_t ip_proto) | 
 | { | 
 | 	switch (ip_proto) { | 
 | 	case IPPROTO_UDP: | 
 | 		return MLX4_NET_TRANS_RULE_ID_UDP; | 
 | 	case IPPROTO_TCP: | 
 | 		return MLX4_NET_TRANS_RULE_ID_TCP; | 
 | 	default: | 
 | 		return MLX4_NET_TRANS_RULE_NUM; | 
 | 	} | 
 | }; | 
 |  | 
 | static void mlx4_en_filter_work(struct work_struct *work) | 
 | { | 
 | 	struct mlx4_en_filter *filter = container_of(work, | 
 | 						     struct mlx4_en_filter, | 
 | 						     work); | 
 | 	struct mlx4_en_priv *priv = filter->priv; | 
 | 	struct mlx4_spec_list spec_tcp_udp = { | 
 | 		.id = mlx4_ip_proto_to_trans_rule_id(filter->ip_proto), | 
 | 		{ | 
 | 			.tcp_udp = { | 
 | 				.dst_port = filter->dst_port, | 
 | 				.dst_port_msk = (__force __be16)-1, | 
 | 				.src_port = filter->src_port, | 
 | 				.src_port_msk = (__force __be16)-1, | 
 | 			}, | 
 | 		}, | 
 | 	}; | 
 | 	struct mlx4_spec_list spec_ip = { | 
 | 		.id = MLX4_NET_TRANS_RULE_ID_IPV4, | 
 | 		{ | 
 | 			.ipv4 = { | 
 | 				.dst_ip = filter->dst_ip, | 
 | 				.dst_ip_msk = (__force __be32)-1, | 
 | 				.src_ip = filter->src_ip, | 
 | 				.src_ip_msk = (__force __be32)-1, | 
 | 			}, | 
 | 		}, | 
 | 	}; | 
 | 	struct mlx4_spec_list spec_eth = { | 
 | 		.id = MLX4_NET_TRANS_RULE_ID_ETH, | 
 | 	}; | 
 | 	struct mlx4_net_trans_rule rule = { | 
 | 		.list = LIST_HEAD_INIT(rule.list), | 
 | 		.queue_mode = MLX4_NET_TRANS_Q_LIFO, | 
 | 		.exclusive = 1, | 
 | 		.allow_loopback = 1, | 
 | 		.promisc_mode = MLX4_FS_REGULAR, | 
 | 		.port = priv->port, | 
 | 		.priority = MLX4_DOMAIN_RFS, | 
 | 	}; | 
 | 	int rc; | 
 | 	__be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16); | 
 |  | 
 | 	if (spec_tcp_udp.id >= MLX4_NET_TRANS_RULE_NUM) { | 
 | 		en_warn(priv, "RFS: ignoring unsupported ip protocol (%d)\n", | 
 | 			filter->ip_proto); | 
 | 		goto ignore; | 
 | 	} | 
 | 	list_add_tail(&spec_eth.list, &rule.list); | 
 | 	list_add_tail(&spec_ip.list, &rule.list); | 
 | 	list_add_tail(&spec_tcp_udp.list, &rule.list); | 
 |  | 
 | 	rule.qpn = priv->rss_map.qps[filter->rxq_index].qpn; | 
 | 	memcpy(spec_eth.eth.dst_mac, priv->dev->ea, Eaddrlen); | 
 | 	memcpy(spec_eth.eth.dst_mac_msk, &mac_mask, Eaddrlen); | 
 |  | 
 | 	filter->activated = 0; | 
 |  | 
 | 	if (filter->reg_id) { | 
 | 		rc = mlx4_flow_detach(priv->mdev->dev, filter->reg_id); | 
 | 		if (rc && rc != -ENOENT) | 
 | 			en_err(priv, "Error detaching flow. rc = %d\n", rc); | 
 | 	} | 
 |  | 
 | 	rc = mlx4_flow_attach(priv->mdev->dev, &rule, &filter->reg_id); | 
 | 	if (rc) | 
 | 		en_err(priv, "Error attaching flow. err = %d\n", rc); | 
 |  | 
 | ignore: | 
 | 	mlx4_en_filter_rfs_expire(priv); | 
 |  | 
 | 	filter->activated = 1; | 
 | } | 
 |  | 
 | static inline struct hlist_head * | 
 | filter_hash_bucket(struct mlx4_en_priv *priv, __be32 src_ip, __be32 dst_ip, | 
 | 		   __be16 src_port, __be16 dst_port) | 
 | { | 
 | 	unsigned long l; | 
 | 	int bucket_idx; | 
 |  | 
 | 	l = (__force unsigned long)src_port | | 
 | 	    ((__force unsigned long)dst_port << 2); | 
 | 	l ^= (__force unsigned long)(src_ip ^ dst_ip); | 
 |  | 
 | 	bucket_idx = hash_long(l, MLX4_EN_FILTER_HASH_SHIFT); | 
 |  | 
 | 	return &priv->filter_hash[bucket_idx]; | 
 | } | 
 |  | 
 | static struct mlx4_en_filter * | 
 | mlx4_en_filter_alloc(struct mlx4_en_priv *priv, int rxq_index, __be32 src_ip, | 
 | 		     __be32 dst_ip, uint8_t ip_proto, __be16 src_port, | 
 | 		     __be16 dst_port, uint32_t flow_id) | 
 | { | 
 | 	struct mlx4_en_filter *filter = NULL; | 
 |  | 
 | 	filter = kzmalloc(sizeof(struct mlx4_en_filter), 0); | 
 | 	if (!filter) | 
 | 		return NULL; | 
 |  | 
 | 	filter->priv = priv; | 
 | 	filter->rxq_index = rxq_index; | 
 | 	INIT_WORK(&filter->work, mlx4_en_filter_work); | 
 |  | 
 | 	filter->src_ip = src_ip; | 
 | 	filter->dst_ip = dst_ip; | 
 | 	filter->ip_proto = ip_proto; | 
 | 	filter->src_port = src_port; | 
 | 	filter->dst_port = dst_port; | 
 |  | 
 | 	filter->flow_id = flow_id; | 
 |  | 
 | 	filter->id = priv->last_filter_id++ % RPS_NO_FILTER; | 
 |  | 
 | 	list_add_tail(&filter->next, &priv->filters); | 
 | 	hlist_add_head(&filter->filter_chain, | 
 | 		       filter_hash_bucket(priv, src_ip, dst_ip, src_port, | 
 | 					  dst_port)); | 
 |  | 
 | 	return filter; | 
 | } | 
 |  | 
 | static void mlx4_en_filter_free(struct mlx4_en_filter *filter) | 
 | { | 
 | 	struct mlx4_en_priv *priv = filter->priv; | 
 | 	int rc; | 
 |  | 
 | 	list_del(&filter->next); | 
 |  | 
 | 	rc = mlx4_flow_detach(priv->mdev->dev, filter->reg_id); | 
 | 	if (rc && rc != -ENOENT) | 
 | 		en_err(priv, "Error detaching flow. rc = %d\n", rc); | 
 |  | 
 | 	kfree(filter); | 
 | } | 
 |  | 
 | static inline struct mlx4_en_filter * | 
 | mlx4_en_filter_find(struct mlx4_en_priv *priv, __be32 src_ip, __be32 dst_ip, | 
 | 		    uint8_t ip_proto, __be16 src_port, __be16 dst_port) | 
 | { | 
 | 	struct mlx4_en_filter *filter; | 
 | 	struct mlx4_en_filter *ret = NULL; | 
 |  | 
 | 	hlist_for_each_entry(filter, | 
 | 			     filter_hash_bucket(priv, src_ip, dst_ip, | 
 | 						src_port, dst_port), | 
 | 			     filter_chain) { | 
 | 		if (filter->src_ip == src_ip && | 
 | 		    filter->dst_ip == dst_ip && | 
 | 		    filter->ip_proto == ip_proto && | 
 | 		    filter->src_port == src_port && | 
 | 		    filter->dst_port == dst_port) { | 
 | 			ret = filter; | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int | 
 | mlx4_en_filter_rfs(struct ether *net_dev, const struct sk_buff *skb, | 
 | 		   uint16_t rxq_index, uint32_t flow_id) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(net_dev); | 
 | 	struct mlx4_en_filter *filter; | 
 | 	const struct iphdr *ip; | 
 | 	const __be16 *ports; | 
 | 	uint8_t ip_proto; | 
 | 	__be32 src_ip; | 
 | 	__be32 dst_ip; | 
 | 	__be16 src_port; | 
 | 	__be16 dst_port; | 
 | 	int nhoff = skb_network_offset(skb); | 
 | 	int ret = 0; | 
 |  | 
 | 	if (skb->protocol != cpu_to_be16(ETH_P_IP)) | 
 | 		return -EPROTONOSUPPORT; | 
 |  | 
 | 	ip = (const struct iphdr *)(skb->data + nhoff); | 
 | 	if (ip_is_fragment(ip)) | 
 | 		return -EPROTONOSUPPORT; | 
 |  | 
 | 	if ((ip->protocol != IPPROTO_TCP) && (ip->protocol != IPPROTO_UDP)) | 
 | 		return -EPROTONOSUPPORT; | 
 | 	ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl); | 
 |  | 
 | 	ip_proto = ip->protocol; | 
 | 	src_ip = ip->saddr; | 
 | 	dst_ip = ip->daddr; | 
 | 	src_port = ports[0]; | 
 | 	dst_port = ports[1]; | 
 |  | 
 | 	spin_lock(&priv->filters_lock); | 
 | 	filter = mlx4_en_filter_find(priv, src_ip, dst_ip, ip_proto, | 
 | 				     src_port, dst_port); | 
 | 	if (filter) { | 
 | 		if (filter->rxq_index == rxq_index) | 
 | 			goto out; | 
 |  | 
 | 		filter->rxq_index = rxq_index; | 
 | 	} else { | 
 | 		filter = mlx4_en_filter_alloc(priv, rxq_index, | 
 | 					      src_ip, dst_ip, ip_proto, | 
 | 					      src_port, dst_port, flow_id); | 
 | 		if (!filter) { | 
 | 			ret = -ENOMEM; | 
 | 			goto err; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	queue_work(priv->mdev->workqueue, &filter->work); | 
 |  | 
 | out: | 
 | 	ret = filter->id; | 
 | err: | 
 | 	spin_unlock(&priv->filters_lock); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | void mlx4_en_cleanup_filters(struct mlx4_en_priv *priv) | 
 | { | 
 | 	struct mlx4_en_filter *filter, *tmp; | 
 | 	LIST_HEAD(del_list); | 
 |  | 
 | 	spin_lock(&priv->filters_lock); | 
 | 	list_for_each_entry_safe(filter, tmp, &priv->filters, next) { | 
 | 		list_move(&filter->next, &del_list); | 
 | 		hlist_del(&filter->filter_chain); | 
 | 	} | 
 | 	spin_unlock(&priv->filters_lock); | 
 |  | 
 | 	list_for_each_entry_safe(filter, tmp, &del_list, next) { | 
 | 		cancel_work_sync(&filter->work); | 
 | 		mlx4_en_filter_free(filter); | 
 | 	} | 
 | } | 
 |  | 
 | static void mlx4_en_filter_rfs_expire(struct mlx4_en_priv *priv) | 
 | { | 
 | 	struct mlx4_en_filter *filter = NULL, *tmp, *last_filter = NULL; | 
 | 	LIST_HEAD(del_list); | 
 | 	int i = 0; | 
 |  | 
 | 	spin_lock(&priv->filters_lock); | 
 | 	list_for_each_entry_safe(filter, tmp, &priv->filters, next) { | 
 | 		if (i > MLX4_EN_FILTER_EXPIRY_QUOTA) | 
 | 			break; | 
 |  | 
 | 		if (filter->activated && | 
 | 		    !work_pending(&filter->work) && | 
 | 		    rps_may_expire_flow(priv->dev, | 
 | 					filter->rxq_index, filter->flow_id, | 
 | 					filter->id)) { | 
 | 			list_move(&filter->next, &del_list); | 
 | 			hlist_del(&filter->filter_chain); | 
 | 		} else | 
 | 			last_filter = filter; | 
 |  | 
 | 		i++; | 
 | 	} | 
 |  | 
 | 	if (last_filter && (&last_filter->next != priv->filters.next)) | 
 | 		list_move(&priv->filters, &last_filter->next); | 
 |  | 
 | 	spin_unlock(&priv->filters_lock); | 
 |  | 
 | 	list_for_each_entry_safe(filter, tmp, &del_list, next) | 
 | 		mlx4_en_filter_free(filter); | 
 | } | 
 | #endif | 
 |  | 
 | static int mlx4_en_vlan_rx_add_vid(struct ether *dev, | 
 | 				   __be16 proto, uint16_t vid) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	int err; | 
 | 	int idx; | 
 |  | 
 | 	en_dbg(HW, priv, "adding VLAN:%d\n", vid); | 
 |  | 
 | 	set_bit(vid, priv->active_vlans); | 
 |  | 
 | 	/* Add VID to port VLAN filter */ | 
 | 	qlock(&mdev->state_lock); | 
 | 	if (mdev->device_up && priv->port_up) { | 
 | 		err = mlx4_SET_VLAN_FLTR(mdev->dev, priv); | 
 | 		if (err) | 
 | 			en_err(priv, "Failed configuring VLAN filter\n"); | 
 | 	} | 
 | 	if (mlx4_register_vlan(mdev->dev, priv->port, vid, &idx)) | 
 | 		en_dbg(HW, priv, "failed adding vlan %d\n", vid); | 
 | 	qunlock(&mdev->state_lock); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int mlx4_en_vlan_rx_kill_vid(struct ether *dev, | 
 | 				    __be16 proto, uint16_t vid) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	int err; | 
 |  | 
 | 	en_dbg(HW, priv, "Killing VID:%d\n", vid); | 
 |  | 
 | 	clear_bit(vid, priv->active_vlans); | 
 |  | 
 | 	/* Remove VID from port VLAN filter */ | 
 | 	qlock(&mdev->state_lock); | 
 | 	mlx4_unregister_vlan(mdev->dev, priv->port, vid); | 
 |  | 
 | 	if (mdev->device_up && priv->port_up) { | 
 | 		err = mlx4_SET_VLAN_FLTR(mdev->dev, priv); | 
 | 		if (err) | 
 | 			en_err(priv, "Failed configuring VLAN filter\n"); | 
 | 	} | 
 | 	qunlock(&mdev->state_lock); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void mlx4_en_u64_to_mac(unsigned char dst_mac[Eaddrlen + 2], | 
 | 			       uint64_t src_mac) | 
 | { | 
 | 	int i; | 
 | 	for (i = Eaddrlen - 1; i >= 0; --i) { | 
 | 		dst_mac[i] = src_mac & 0xff; | 
 | 		src_mac >>= 8; | 
 | 	} | 
 | 	memset(&dst_mac[Eaddrlen], 0, 2); | 
 | } | 
 |  | 
 |  | 
 | static int mlx4_en_tunnel_steer_add(struct mlx4_en_priv *priv, unsigned char *addr, | 
 | 				    int qpn, uint64_t *reg_id) | 
 | { | 
 | 	int err; | 
 |  | 
 | 	if (priv->mdev->dev->caps.tunnel_offload_mode != MLX4_TUNNEL_OFFLOAD_MODE_VXLAN || | 
 | 	    priv->mdev->dev->caps.dmfs_high_steer_mode == MLX4_STEERING_DMFS_A0_STATIC) | 
 | 		return 0; /* do nothing */ | 
 |  | 
 | 	err = mlx4_tunnel_steer_add(priv->mdev->dev, addr, priv->port, qpn, | 
 | 				    MLX4_DOMAIN_NIC, reg_id); | 
 | 	if (err) { | 
 | 		en_err(priv, "failed to add vxlan steering rule, err %d\n", err); | 
 | 		return err; | 
 | 	} | 
 | 	en_dbg(DRV, priv, "added vxlan steering rule, mac %pM reg_id %llx\n", addr, *reg_id); | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | static int mlx4_en_uc_steer_add(struct mlx4_en_priv *priv, | 
 | 				unsigned char *mac, int *qpn, | 
 | 				uint64_t *reg_id) | 
 | { | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	struct mlx4_dev *dev = mdev->dev; | 
 | 	int err; | 
 |  | 
 | 	switch (dev->caps.steering_mode) { | 
 | 	case MLX4_STEERING_MODE_B0: { | 
 | 		struct mlx4_qp qp; | 
 | 		uint8_t gid[16] = {0}; | 
 |  | 
 | 		qp.qpn = *qpn; | 
 | 		memcpy(&gid[10], mac, Eaddrlen); | 
 | 		gid[5] = priv->port; | 
 |  | 
 | 		err = mlx4_unicast_attach(dev, &qp, gid, 0, MLX4_PROT_ETH); | 
 | 		break; | 
 | 	} | 
 | 	case MLX4_STEERING_MODE_DEVICE_MANAGED: { | 
 | 		struct mlx4_spec_list spec_eth = { {NULL} }; | 
 | 		__be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16); | 
 |  | 
 | 		struct mlx4_net_trans_rule rule = { | 
 | 			.queue_mode = MLX4_NET_TRANS_Q_FIFO, | 
 | 			.exclusive = 0, | 
 | 			.allow_loopback = 1, | 
 | 			.promisc_mode = MLX4_FS_REGULAR, | 
 | 			.priority = MLX4_DOMAIN_NIC, | 
 | 		}; | 
 |  | 
 | 		rule.port = priv->port; | 
 | 		rule.qpn = *qpn; | 
 | 		INIT_LIST_HEAD(&rule.list); | 
 |  | 
 | 		spec_eth.id = MLX4_NET_TRANS_RULE_ID_ETH; | 
 | 		memcpy(spec_eth.eth.dst_mac, mac, Eaddrlen); | 
 | 		memcpy(spec_eth.eth.dst_mac_msk, &mac_mask, Eaddrlen); | 
 | 		list_add_tail(&spec_eth.list, &rule.list); | 
 |  | 
 | 		err = mlx4_flow_attach(dev, &rule, reg_id); | 
 | 		break; | 
 | 	} | 
 | 	default: | 
 | 		return -EINVAL; | 
 | 	} | 
 | 	if (err) | 
 | 		en_warn(priv, "Failed Attaching Unicast\n"); | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | static void mlx4_en_uc_steer_release(struct mlx4_en_priv *priv, | 
 | 				     unsigned char *mac, int qpn, | 
 | 				     uint64_t reg_id) | 
 | { | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	struct mlx4_dev *dev = mdev->dev; | 
 |  | 
 | 	switch (dev->caps.steering_mode) { | 
 | 	case MLX4_STEERING_MODE_B0: { | 
 | 		struct mlx4_qp qp; | 
 | 		uint8_t gid[16] = {0}; | 
 |  | 
 | 		qp.qpn = qpn; | 
 | 		memcpy(&gid[10], mac, Eaddrlen); | 
 | 		gid[5] = priv->port; | 
 |  | 
 | 		mlx4_unicast_detach(dev, &qp, gid, MLX4_PROT_ETH); | 
 | 		break; | 
 | 	} | 
 | 	case MLX4_STEERING_MODE_DEVICE_MANAGED: { | 
 | 		mlx4_flow_detach(dev, reg_id); | 
 | 		break; | 
 | 	} | 
 | 	default: | 
 | 		en_err(priv, "Invalid steering mode.\n"); | 
 | 	} | 
 | } | 
 |  | 
 | static int mlx4_en_get_qp(struct mlx4_en_priv *priv) | 
 | { | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	struct mlx4_dev *dev = mdev->dev; | 
 | 	struct mlx4_mac_entry *entry; | 
 | 	int index = 0; | 
 | 	int err = 0; | 
 | 	uint64_t reg_id = 0; | 
 | 	int *qpn = &priv->base_qpn; | 
 | 	uint64_t mac = mlx4_mac_to_u64(priv->dev->ea); | 
 |  | 
 | #if 0 // AKAROS_PORT | 
 | 	en_dbg(DRV, priv, "Registering MAC: %pM for adding\n", | 
 | 	       priv->dev->ea); | 
 | #else | 
 | 	en_dbg(DRV, priv, "Registering MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", | 
 | 	       priv->dev->ea[0], priv->dev->ea[1], priv->dev->ea[2], | 
 | 	       priv->dev->ea[3], priv->dev->ea[4], priv->dev->ea[5]); | 
 | #endif | 
 | 	index = mlx4_register_mac(dev, priv->port, mac); | 
 | 	if (index < 0) { | 
 | 		err = index; | 
 | 		en_err(priv, "Failed adding MAC: %pM\n", | 
 | 		       priv->dev->ea); | 
 | 		return err; | 
 | 	} | 
 |  | 
 | 	if (dev->caps.steering_mode == MLX4_STEERING_MODE_A0) { | 
 | 		int base_qpn = mlx4_get_base_qpn(dev, priv->port); | 
 | 		*qpn = base_qpn + index; | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	err = mlx4_qp_reserve_range(dev, 1, 1, qpn, MLX4_RESERVE_A0_QP); | 
 | 	en_dbg(DRV, priv, "Reserved qp %d\n", *qpn); | 
 | 	if (err) { | 
 | 		en_err(priv, "Failed to reserve qp for mac registration\n"); | 
 | 		goto qp_err; | 
 | 	} | 
 |  | 
 | 	err = mlx4_en_uc_steer_add(priv, priv->dev->ea, qpn, ®_id); | 
 | 	if (err) | 
 | 		goto steer_err; | 
 |  | 
 | #if 0 // AKAROS_PORT | 
 | 	err = mlx4_en_tunnel_steer_add(priv, priv->dev->ea, *qpn, | 
 | 				       &priv->tunnel_reg_id); | 
 | 	if (err) | 
 | 		goto tunnel_err; | 
 | #endif | 
 |  | 
 | 	entry = kmalloc(sizeof(*entry), MEM_WAIT); | 
 | 	if (!entry) { | 
 | 		err = -ENOMEM; | 
 | 		goto alloc_err; | 
 | 	} | 
 | 	memcpy(entry->mac, priv->dev->ea, sizeof(entry->mac)); | 
 | 	memcpy(priv->current_mac, entry->mac, sizeof(priv->current_mac)); | 
 | 	entry->reg_id = reg_id; | 
 |  | 
 | #if 0 // AKAROS_PORT | 
 | 	hlist_add_head_rcu(&entry->hlist, | 
 | 			   &priv->mac_hash[entry->mac[MLX4_EN_MAC_HASH_IDX]]); | 
 | #else | 
 | 	hlist_add_head(&entry->hlist, | 
 | 		       &priv->mac_hash[entry->mac[MLX4_EN_MAC_HASH_IDX]]); | 
 | 	/* FIXME why is rcu significant? */ | 
 | #endif | 
 |  | 
 | 	return 0; | 
 |  | 
 | alloc_err: | 
 | 	if (priv->tunnel_reg_id) | 
 | 		mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id); | 
 | #if 0 // AKAROS_PORT | 
 | tunnel_err: | 
 | 	mlx4_en_uc_steer_release(priv, priv->dev->ea, *qpn, reg_id); | 
 | #endif | 
 |  | 
 | steer_err: | 
 | 	mlx4_qp_release_range(dev, *qpn, 1); | 
 |  | 
 | qp_err: | 
 | 	mlx4_unregister_mac(dev, priv->port, mac); | 
 | 	return err; | 
 | } | 
 |  | 
 | static void mlx4_en_put_qp(struct mlx4_en_priv *priv) | 
 | { | 
 | 	panic("Disabled"); | 
 | #if 0 // AKAROS_PORT | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	struct mlx4_dev *dev = mdev->dev; | 
 | 	int qpn = priv->base_qpn; | 
 | 	uint64_t mac; | 
 |  | 
 | 	if (dev->caps.steering_mode == MLX4_STEERING_MODE_A0) { | 
 | 		mac = mlx4_mac_to_u64(priv->dev->ea); | 
 | 		en_dbg(DRV, priv, "Registering MAC: %pM for deleting\n", | 
 | 		       priv->dev->ea); | 
 | 		mlx4_unregister_mac(dev, priv->port, mac); | 
 | 	} else { | 
 | 		struct mlx4_mac_entry *entry; | 
 | 		struct hlist_node *tmp; | 
 | 		struct hlist_head *bucket; | 
 | 		unsigned int i; | 
 |  | 
 | 		for (i = 0; i < MLX4_EN_MAC_HASH_SIZE; ++i) { | 
 | 			bucket = &priv->mac_hash[i]; | 
 | 			hlist_for_each_entry_safe(entry, tmp, bucket, hlist) { | 
 | 				mac = mlx4_mac_to_u64(entry->mac); | 
 | 				en_dbg(DRV, priv, "Registering MAC: %pM for deleting\n", | 
 | 				       entry->mac); | 
 | 				mlx4_en_uc_steer_release(priv, entry->mac, | 
 | 							 qpn, entry->reg_id); | 
 |  | 
 | 				mlx4_unregister_mac(dev, priv->port, mac); | 
 | 				hlist_del_rcu(&entry->hlist); | 
 | 				kfree_rcu(entry, rcu); | 
 | 			} | 
 | 		} | 
 |  | 
 | 		if (priv->tunnel_reg_id) { | 
 | 			mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id); | 
 | 			priv->tunnel_reg_id = 0; | 
 | 		} | 
 |  | 
 | 		en_dbg(DRV, priv, "Releasing qp: port %d, qpn %d\n", | 
 | 		       priv->port, qpn); | 
 | 		mlx4_qp_release_range(dev, qpn, 1); | 
 | 		priv->flags &= ~MLX4_EN_FLAG_FORCE_PROMISC; | 
 | 	} | 
 | #endif | 
 | } | 
 |  | 
 | #if 0 // AKAROS_PORT | 
 | static int mlx4_en_replace_mac(struct mlx4_en_priv *priv, int qpn, | 
 | 			       unsigned char *new_mac, unsigned char *prev_mac) | 
 | { | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	struct mlx4_dev *dev = mdev->dev; | 
 | 	int err = 0; | 
 | 	uint64_t new_mac_u64 = mlx4_mac_to_u64(new_mac); | 
 |  | 
 | 	if (dev->caps.steering_mode != MLX4_STEERING_MODE_A0) { | 
 | 		struct hlist_head *bucket; | 
 | 		unsigned int mac_hash; | 
 | 		struct mlx4_mac_entry *entry; | 
 | 		struct hlist_node *tmp; | 
 | 		uint64_t prev_mac_u64 = mlx4_mac_to_u64(prev_mac); | 
 |  | 
 | 		bucket = &priv->mac_hash[prev_mac[MLX4_EN_MAC_HASH_IDX]]; | 
 | 		hlist_for_each_entry_safe(entry, tmp, bucket, hlist) { | 
 | 			if (ether_addr_equal_64bits(entry->mac, prev_mac)) { | 
 | 				mlx4_en_uc_steer_release(priv, entry->mac, | 
 | 							 qpn, entry->reg_id); | 
 | 				mlx4_unregister_mac(dev, priv->port, | 
 | 						    prev_mac_u64); | 
 | 				hlist_del_rcu(&entry->hlist); | 
 | 				synchronize_rcu(); | 
 | 				memcpy(entry->mac, new_mac, Eaddrlen); | 
 | 				entry->reg_id = 0; | 
 | 				mac_hash = new_mac[MLX4_EN_MAC_HASH_IDX]; | 
 | 				hlist_add_head_rcu(&entry->hlist, | 
 | 						   &priv->mac_hash[mac_hash]); | 
 | 				mlx4_register_mac(dev, priv->port, new_mac_u64); | 
 | 				err = mlx4_en_uc_steer_add(priv, new_mac, | 
 | 							   &qpn, | 
 | 							   &entry->reg_id); | 
 | 				if (err) | 
 | 					return err; | 
 | 				if (priv->tunnel_reg_id) { | 
 | 					mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id); | 
 | 					priv->tunnel_reg_id = 0; | 
 | 				} | 
 | 				err = mlx4_en_tunnel_steer_add(priv, new_mac, qpn, | 
 | 							       &priv->tunnel_reg_id); | 
 | 				return err; | 
 | 			} | 
 | 		} | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	return __mlx4_replace_mac(dev, priv->port, qpn, new_mac_u64); | 
 | } | 
 |  | 
 | static int mlx4_en_do_set_mac(struct mlx4_en_priv *priv, | 
 | 			      unsigned char new_mac[Eaddrlen + 2]) | 
 | { | 
 | 	int err = 0; | 
 |  | 
 | 	if (priv->port_up) { | 
 | 		/* Remove old MAC and insert the new one */ | 
 | 		err = mlx4_en_replace_mac(priv, priv->base_qpn, | 
 | 					  new_mac, priv->current_mac); | 
 | 		if (err) | 
 | 			en_err(priv, "Failed changing HW MAC address\n"); | 
 | 	} else | 
 | 		en_dbg(HW, priv, "Port is down while registering mac, exiting...\n"); | 
 |  | 
 | 	if (!err) | 
 | 		memcpy(priv->current_mac, new_mac, sizeof(priv->current_mac)); | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | static int mlx4_en_set_mac(struct ether *dev, void *addr) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	struct sockaddr *saddr = addr; | 
 | 	unsigned char new_mac[Eaddrlen + 2]; | 
 | 	int err; | 
 |  | 
 | 	if (!is_valid_ether_addr(saddr->sa_data)) | 
 | 		return -EADDRNOTAVAIL; | 
 |  | 
 | 	qlock(&mdev->state_lock); | 
 | 	memcpy(new_mac, saddr->sa_data, Eaddrlen); | 
 | 	err = mlx4_en_do_set_mac(priv, new_mac); | 
 | 	if (!err) | 
 | 		memcpy(dev->ea, saddr->sa_data, Eaddrlen); | 
 | 	qunlock(&mdev->state_lock); | 
 |  | 
 | 	return err; | 
 | } | 
 | #endif | 
 |  | 
 | static void mlx4_en_clear_list(struct ether *dev) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_mc_list *tmp, *mc_to_del; | 
 |  | 
 | 	list_for_each_entry_safe(mc_to_del, tmp, &priv->mc_list, list) { | 
 | 		list_del(&mc_to_del->list); | 
 | 		kfree(mc_to_del); | 
 | 	} | 
 | } | 
 |  | 
 | #if 0 // AKAROS_PORT | 
 | static void mlx4_en_cache_mclist(struct ether *dev) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct netdev_hw_addr *ha; | 
 | 	struct mlx4_en_mc_list *tmp; | 
 |  | 
 | 	mlx4_en_clear_list(dev); | 
 | 	netdev_for_each_mc_addr(ha, dev) { | 
 | 		tmp = kzmalloc(sizeof(struct mlx4_en_mc_list), 0); | 
 | 		if (!tmp) { | 
 | 			mlx4_en_clear_list(dev); | 
 | 			return; | 
 | 		} | 
 | 		memcpy(tmp->addr, ha->addr, Eaddrlen); | 
 | 		list_add_tail(&tmp->list, &priv->mc_list); | 
 | 	} | 
 | } | 
 |  | 
 | static void update_mclist_flags(struct mlx4_en_priv *priv, | 
 | 				struct list_head *dst, | 
 | 				struct list_head *src) | 
 | { | 
 | 	struct mlx4_en_mc_list *dst_tmp, *src_tmp, *new_mc; | 
 | 	bool found; | 
 |  | 
 | 	/* Find all the entries that should be removed from dst, | 
 | 	 * These are the entries that are not found in src | 
 | 	 */ | 
 | 	list_for_each_entry(dst_tmp, dst, list) { | 
 | 		found = false; | 
 | 		list_for_each_entry(src_tmp, src, list) { | 
 | 			if (ether_addr_equal(dst_tmp->addr, src_tmp->addr)) { | 
 | 				found = true; | 
 | 				break; | 
 | 			} | 
 | 		} | 
 | 		if (!found) | 
 | 			dst_tmp->action = MCLIST_REM; | 
 | 	} | 
 |  | 
 | 	/* Add entries that exist in src but not in dst | 
 | 	 * mark them as need to add | 
 | 	 */ | 
 | 	list_for_each_entry(src_tmp, src, list) { | 
 | 		found = false; | 
 | 		list_for_each_entry(dst_tmp, dst, list) { | 
 | 			if (ether_addr_equal(dst_tmp->addr, src_tmp->addr)) { | 
 | 				dst_tmp->action = MCLIST_NONE; | 
 | 				found = true; | 
 | 				break; | 
 | 			} | 
 | 		} | 
 | 		if (!found) { | 
 | 			new_mc = kmemdup(src_tmp, | 
 | 					 sizeof(struct mlx4_en_mc_list), | 
 | 					 MEM_WAIT); | 
 | 			if (!new_mc) | 
 | 				return; | 
 |  | 
 | 			new_mc->action = MCLIST_ADD; | 
 | 			list_add_tail(&new_mc->list, dst); | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | static void mlx4_en_set_rx_mode(struct ether *dev) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 |  | 
 | 	if (!priv->port_up) | 
 | 		return; | 
 |  | 
 | 	queue_work(priv->mdev->workqueue, &priv->rx_mode_task); | 
 | } | 
 |  | 
 | static void mlx4_en_set_promisc_mode(struct mlx4_en_priv *priv, | 
 | 				     struct mlx4_en_dev *mdev) | 
 | { | 
 | 	int err = 0; | 
 |  | 
 | 	if (!(priv->flags & MLX4_EN_FLAG_PROMISC)) { | 
 | 		if (netif_msg_rx_status(priv)) | 
 | 			en_warn(priv, "Entering promiscuous mode\n"); | 
 | 		priv->flags |= MLX4_EN_FLAG_PROMISC; | 
 |  | 
 | 		/* Enable promiscouos mode */ | 
 | 		switch (mdev->dev->caps.steering_mode) { | 
 | 		case MLX4_STEERING_MODE_DEVICE_MANAGED: | 
 | 			err = mlx4_flow_steer_promisc_add(mdev->dev, | 
 | 							  priv->port, | 
 | 							  priv->base_qpn, | 
 | 							  MLX4_FS_ALL_DEFAULT); | 
 | 			if (err) | 
 | 				en_err(priv, "Failed enabling promiscuous mode\n"); | 
 | 			priv->flags |= MLX4_EN_FLAG_MC_PROMISC; | 
 | 			break; | 
 |  | 
 | 		case MLX4_STEERING_MODE_B0: | 
 | 			err = mlx4_unicast_promisc_add(mdev->dev, | 
 | 						       priv->base_qpn, | 
 | 						       priv->port); | 
 | 			if (err) | 
 | 				en_err(priv, "Failed enabling unicast promiscuous mode\n"); | 
 |  | 
 | 			/* Add the default qp number as multicast | 
 | 			 * promisc | 
 | 			 */ | 
 | 			if (!(priv->flags & MLX4_EN_FLAG_MC_PROMISC)) { | 
 | 				err = mlx4_multicast_promisc_add(mdev->dev, | 
 | 								 priv->base_qpn, | 
 | 								 priv->port); | 
 | 				if (err) | 
 | 					en_err(priv, "Failed enabling multicast promiscuous mode\n"); | 
 | 				priv->flags |= MLX4_EN_FLAG_MC_PROMISC; | 
 | 			} | 
 | 			break; | 
 |  | 
 | 		case MLX4_STEERING_MODE_A0: | 
 | 			err = mlx4_SET_PORT_qpn_calc(mdev->dev, | 
 | 						     priv->port, | 
 | 						     priv->base_qpn, | 
 | 						     1); | 
 | 			if (err) | 
 | 				en_err(priv, "Failed enabling promiscuous mode\n"); | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		/* Disable port multicast filter (unconditionally) */ | 
 | 		err = mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 0, | 
 | 					  0, MLX4_MCAST_DISABLE); | 
 | 		if (err) | 
 | 			en_err(priv, "Failed disabling multicast filter\n"); | 
 | 	} | 
 | } | 
 |  | 
 | static void mlx4_en_clear_promisc_mode(struct mlx4_en_priv *priv, | 
 | 				       struct mlx4_en_dev *mdev) | 
 | { | 
 | 	int err = 0; | 
 |  | 
 | 	if (netif_msg_rx_status(priv)) | 
 | 		en_warn(priv, "Leaving promiscuous mode\n"); | 
 | 	priv->flags &= ~MLX4_EN_FLAG_PROMISC; | 
 |  | 
 | 	/* Disable promiscouos mode */ | 
 | 	switch (mdev->dev->caps.steering_mode) { | 
 | 	case MLX4_STEERING_MODE_DEVICE_MANAGED: | 
 | 		err = mlx4_flow_steer_promisc_remove(mdev->dev, | 
 | 						     priv->port, | 
 | 						     MLX4_FS_ALL_DEFAULT); | 
 | 		if (err) | 
 | 			en_err(priv, "Failed disabling promiscuous mode\n"); | 
 | 		priv->flags &= ~MLX4_EN_FLAG_MC_PROMISC; | 
 | 		break; | 
 |  | 
 | 	case MLX4_STEERING_MODE_B0: | 
 | 		err = mlx4_unicast_promisc_remove(mdev->dev, | 
 | 						  priv->base_qpn, | 
 | 						  priv->port); | 
 | 		if (err) | 
 | 			en_err(priv, "Failed disabling unicast promiscuous mode\n"); | 
 | 		/* Disable Multicast promisc */ | 
 | 		if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) { | 
 | 			err = mlx4_multicast_promisc_remove(mdev->dev, | 
 | 							    priv->base_qpn, | 
 | 							    priv->port); | 
 | 			if (err) | 
 | 				en_err(priv, "Failed disabling multicast promiscuous mode\n"); | 
 | 			priv->flags &= ~MLX4_EN_FLAG_MC_PROMISC; | 
 | 		} | 
 | 		break; | 
 |  | 
 | 	case MLX4_STEERING_MODE_A0: | 
 | 		err = mlx4_SET_PORT_qpn_calc(mdev->dev, | 
 | 					     priv->port, | 
 | 					     priv->base_qpn, 0); | 
 | 		if (err) | 
 | 			en_err(priv, "Failed disabling promiscuous mode\n"); | 
 | 		break; | 
 | 	} | 
 | } | 
 |  | 
 | static void mlx4_en_do_multicast(struct mlx4_en_priv *priv, | 
 | 				 struct ether *dev, | 
 | 				 struct mlx4_en_dev *mdev) | 
 | { | 
 | 	struct mlx4_en_mc_list *mclist, *tmp; | 
 | 	uint64_t mcast_addr = 0; | 
 | 	uint8_t mc_list[16] = {0}; | 
 | 	int err = 0; | 
 |  | 
 | 	/* Enable/disable the multicast filter according to IFF_ALLMULTI */ | 
 | 	if (dev->flags & IFF_ALLMULTI) { | 
 | 		err = mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 0, | 
 | 					  0, MLX4_MCAST_DISABLE); | 
 | 		if (err) | 
 | 			en_err(priv, "Failed disabling multicast filter\n"); | 
 |  | 
 | 		/* Add the default qp number as multicast promisc */ | 
 | 		if (!(priv->flags & MLX4_EN_FLAG_MC_PROMISC)) { | 
 | 			switch (mdev->dev->caps.steering_mode) { | 
 | 			case MLX4_STEERING_MODE_DEVICE_MANAGED: | 
 | 				err = mlx4_flow_steer_promisc_add(mdev->dev, | 
 | 								  priv->port, | 
 | 								  priv->base_qpn, | 
 | 								  MLX4_FS_MC_DEFAULT); | 
 | 				break; | 
 |  | 
 | 			case MLX4_STEERING_MODE_B0: | 
 | 				err = mlx4_multicast_promisc_add(mdev->dev, | 
 | 								 priv->base_qpn, | 
 | 								 priv->port); | 
 | 				break; | 
 |  | 
 | 			case MLX4_STEERING_MODE_A0: | 
 | 				break; | 
 | 			} | 
 | 			if (err) | 
 | 				en_err(priv, "Failed entering multicast promisc mode\n"); | 
 | 			priv->flags |= MLX4_EN_FLAG_MC_PROMISC; | 
 | 		} | 
 | 	} else { | 
 | 		/* Disable Multicast promisc */ | 
 | 		if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) { | 
 | 			switch (mdev->dev->caps.steering_mode) { | 
 | 			case MLX4_STEERING_MODE_DEVICE_MANAGED: | 
 | 				err = mlx4_flow_steer_promisc_remove(mdev->dev, | 
 | 								     priv->port, | 
 | 								     MLX4_FS_MC_DEFAULT); | 
 | 				break; | 
 |  | 
 | 			case MLX4_STEERING_MODE_B0: | 
 | 				err = mlx4_multicast_promisc_remove(mdev->dev, | 
 | 								    priv->base_qpn, | 
 | 								    priv->port); | 
 | 				break; | 
 |  | 
 | 			case MLX4_STEERING_MODE_A0: | 
 | 				break; | 
 | 			} | 
 | 			if (err) | 
 | 				en_err(priv, "Failed disabling multicast promiscuous mode\n"); | 
 | 			priv->flags &= ~MLX4_EN_FLAG_MC_PROMISC; | 
 | 		} | 
 |  | 
 | 		err = mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 0, | 
 | 					  0, MLX4_MCAST_DISABLE); | 
 | 		if (err) | 
 | 			en_err(priv, "Failed disabling multicast filter\n"); | 
 |  | 
 | 		/* Flush mcast filter and init it with broadcast address */ | 
 | 		mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, ETH_BCAST, | 
 | 				    1, MLX4_MCAST_CONFIG); | 
 |  | 
 | 		/* Update multicast list - we cache all addresses so they won't | 
 | 		 * change while HW is updated holding the command semaphor */ | 
 | 		qlock(&dev->qlock); | 
 | 		mlx4_en_cache_mclist(dev); | 
 | 		qunlock(&dev->qlock); | 
 | 		list_for_each_entry(mclist, &priv->mc_list, list) { | 
 | 			mcast_addr = mlx4_mac_to_u64(mclist->addr); | 
 | 			mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, | 
 | 					    mcast_addr, 0, MLX4_MCAST_CONFIG); | 
 | 		} | 
 | 		err = mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 0, | 
 | 					  0, MLX4_MCAST_ENABLE); | 
 | 		if (err) | 
 | 			en_err(priv, "Failed enabling multicast filter\n"); | 
 |  | 
 | 		update_mclist_flags(priv, &priv->curr_list, &priv->mc_list); | 
 | 		list_for_each_entry_safe(mclist, tmp, &priv->curr_list, list) { | 
 | 			if (mclist->action == MCLIST_REM) { | 
 | 				/* detach this address and delete from list */ | 
 | 				memcpy(&mc_list[10], mclist->addr, Eaddrlen); | 
 | 				mc_list[5] = priv->port; | 
 | 				err = mlx4_multicast_detach(mdev->dev, | 
 | 							    &priv->rss_map.indir_qp, | 
 | 							    mc_list, | 
 | 							    MLX4_PROT_ETH, | 
 | 							    mclist->reg_id); | 
 | 				if (err) | 
 | 					en_err(priv, "Fail to detach multicast address\n"); | 
 |  | 
 | 				if (mclist->tunnel_reg_id) { | 
 | 					err = mlx4_flow_detach(priv->mdev->dev, mclist->tunnel_reg_id); | 
 | 					if (err) | 
 | 						en_err(priv, "Failed to detach multicast address\n"); | 
 | 				} | 
 |  | 
 | 				/* remove from list */ | 
 | 				list_del(&mclist->list); | 
 | 				kfree(mclist); | 
 | 			} else if (mclist->action == MCLIST_ADD) { | 
 | 				/* attach the address */ | 
 | 				memcpy(&mc_list[10], mclist->addr, Eaddrlen); | 
 | 				/* needed for B0 steering support */ | 
 | 				mc_list[5] = priv->port; | 
 | 				err = mlx4_multicast_attach(mdev->dev, | 
 | 							    &priv->rss_map.indir_qp, | 
 | 							    mc_list, | 
 | 							    priv->port, 0, | 
 | 							    MLX4_PROT_ETH, | 
 | 							    &mclist->reg_id); | 
 | 				if (err) | 
 | 					en_err(priv, "Fail to attach multicast address\n"); | 
 |  | 
 | 				err = mlx4_en_tunnel_steer_add(priv, &mc_list[10], priv->base_qpn, | 
 | 							       &mclist->tunnel_reg_id); | 
 | 				if (err) | 
 | 					en_err(priv, "Failed to attach multicast address\n"); | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | static void mlx4_en_do_uc_filter(struct mlx4_en_priv *priv, | 
 | 				 struct ether *dev, | 
 | 				 struct mlx4_en_dev *mdev) | 
 | { | 
 | 	struct netdev_hw_addr *ha; | 
 | 	struct mlx4_mac_entry *entry; | 
 | 	struct hlist_node *tmp; | 
 | 	bool found; | 
 | 	uint64_t mac; | 
 | 	int err = 0; | 
 | 	struct hlist_head *bucket; | 
 | 	unsigned int i; | 
 | 	int removed = 0; | 
 | 	uint32_t prev_flags; | 
 |  | 
 | 	/* Note that we do not need to protect our mac_hash traversal with rcu, | 
 | 	 * since all modification code is protected by mdev->state_lock | 
 | 	 */ | 
 |  | 
 | 	/* find what to remove */ | 
 | 	for (i = 0; i < MLX4_EN_MAC_HASH_SIZE; ++i) { | 
 | 		bucket = &priv->mac_hash[i]; | 
 | 		hlist_for_each_entry_safe(entry, tmp, bucket, hlist) { | 
 | 			found = false; | 
 | 			netdev_for_each_uc_addr(ha, dev) { | 
 | 				if (ether_addr_equal_64bits(entry->mac, | 
 | 							    ha->addr)) { | 
 | 					found = true; | 
 | 					break; | 
 | 				} | 
 | 			} | 
 |  | 
 | 			/* MAC address of the port is not in uc list */ | 
 | 			if (ether_addr_equal_64bits(entry->mac, | 
 | 						    priv->current_mac)) | 
 | 				found = true; | 
 |  | 
 | 			if (!found) { | 
 | 				mac = mlx4_mac_to_u64(entry->mac); | 
 | 				mlx4_en_uc_steer_release(priv, entry->mac, | 
 | 							 priv->base_qpn, | 
 | 							 entry->reg_id); | 
 | 				mlx4_unregister_mac(mdev->dev, priv->port, mac); | 
 |  | 
 | 				hlist_del_rcu(&entry->hlist); | 
 | 				kfree_rcu(entry, rcu); | 
 | 				en_dbg(DRV, priv, "Removed MAC %pM on port:%d\n", | 
 | 				       entry->mac, priv->port); | 
 | 				++removed; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* if we didn't remove anything, there is no use in trying to add | 
 | 	 * again once we are in a forced promisc mode state | 
 | 	 */ | 
 | 	if ((priv->flags & MLX4_EN_FLAG_FORCE_PROMISC) && 0 == removed) | 
 | 		return; | 
 |  | 
 | 	prev_flags = priv->flags; | 
 | 	priv->flags &= ~MLX4_EN_FLAG_FORCE_PROMISC; | 
 |  | 
 | 	/* find what to add */ | 
 | 	netdev_for_each_uc_addr(ha, dev) { | 
 | 		found = false; | 
 | 		bucket = &priv->mac_hash[ha->addr[MLX4_EN_MAC_HASH_IDX]]; | 
 | 		hlist_for_each_entry(entry, bucket, hlist) { | 
 | 			if (ether_addr_equal_64bits(entry->mac, ha->addr)) { | 
 | 				found = true; | 
 | 				break; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		if (!found) { | 
 | 			entry = kmalloc(sizeof(*entry), MEM_WAIT); | 
 | 			if (!entry) { | 
 | 				en_err(priv, "Failed adding MAC %pM on port:%d (out of memory)\n", | 
 | 				       ha->addr, priv->port); | 
 | 				priv->flags |= MLX4_EN_FLAG_FORCE_PROMISC; | 
 | 				break; | 
 | 			} | 
 | 			mac = mlx4_mac_to_u64(ha->addr); | 
 | 			memcpy(entry->mac, ha->addr, Eaddrlen); | 
 | 			err = mlx4_register_mac(mdev->dev, priv->port, mac); | 
 | 			if (err < 0) { | 
 | 				en_err(priv, "Failed registering MAC %pM on port %d: %d\n", | 
 | 				       ha->addr, priv->port, err); | 
 | 				kfree(entry); | 
 | 				priv->flags |= MLX4_EN_FLAG_FORCE_PROMISC; | 
 | 				break; | 
 | 			} | 
 | 			err = mlx4_en_uc_steer_add(priv, ha->addr, | 
 | 						   &priv->base_qpn, | 
 | 						   &entry->reg_id); | 
 | 			if (err) { | 
 | 				en_err(priv, "Failed adding MAC %pM on port %d: %d\n", | 
 | 				       ha->addr, priv->port, err); | 
 | 				mlx4_unregister_mac(mdev->dev, priv->port, mac); | 
 | 				kfree(entry); | 
 | 				priv->flags |= MLX4_EN_FLAG_FORCE_PROMISC; | 
 | 				break; | 
 | 			} else { | 
 | 				unsigned int mac_hash; | 
 | 				en_dbg(DRV, priv, "Added MAC %pM on port:%d\n", | 
 | 				       ha->addr, priv->port); | 
 | 				mac_hash = ha->addr[MLX4_EN_MAC_HASH_IDX]; | 
 | 				bucket = &priv->mac_hash[mac_hash]; | 
 | 				hlist_add_head_rcu(&entry->hlist, bucket); | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (priv->flags & MLX4_EN_FLAG_FORCE_PROMISC) { | 
 | 		en_warn(priv, "Forcing promiscuous mode on port:%d\n", | 
 | 			priv->port); | 
 | 	} else if (prev_flags & MLX4_EN_FLAG_FORCE_PROMISC) { | 
 | 		en_warn(priv, "Stop forcing promiscuous mode on port:%d\n", | 
 | 			priv->port); | 
 | 	} | 
 | } | 
 | #endif | 
 |  | 
 | static void mlx4_en_do_set_rx_mode(struct work_struct *work) | 
 | { | 
 | 	struct mlx4_en_priv *priv = container_of(work, struct mlx4_en_priv, | 
 | 						 rx_mode_task); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	struct ether *dev = priv->dev; | 
 |  | 
 | 	qlock(&mdev->state_lock); | 
 | 	if (!mdev->device_up) { | 
 | 		en_dbg(HW, priv, "Card is not up, ignoring rx mode change.\n"); | 
 | 		goto out; | 
 | 	} | 
 | 	if (!priv->port_up) { | 
 | 		en_dbg(HW, priv, "Port is down, ignoring rx mode change.\n"); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	if (!netif_carrier_ok(dev)) { | 
 | 		if (!mlx4_en_QUERY_PORT(mdev, priv->port)) { | 
 | 			if (priv->port_state.link_state) { | 
 | 				priv->last_link_state = MLX4_DEV_EVENT_PORT_UP; | 
 | 				netif_carrier_on(dev); | 
 | 				en_dbg(LINK, priv, "Link Up\n"); | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | #if 0 // AKAROS_PORT | 
 | 	if (dev->priv_flags & IFF_UNICAST_FLT) | 
 | 		mlx4_en_do_uc_filter(priv, dev, mdev); | 
 |  | 
 | 	/* Promsicuous mode: disable all filters */ | 
 | 	if ((dev->flags & IFF_PROMISC) || | 
 | 	    (priv->flags & MLX4_EN_FLAG_FORCE_PROMISC)) { | 
 | 		mlx4_en_set_promisc_mode(priv, mdev); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	/* Not in promiscuous mode */ | 
 | 	if (priv->flags & MLX4_EN_FLAG_PROMISC) | 
 | 		mlx4_en_clear_promisc_mode(priv, mdev); | 
 |  | 
 | 	mlx4_en_do_multicast(priv, dev, mdev); | 
 | #endif | 
 | out: | 
 | 	qunlock(&mdev->state_lock); | 
 | } | 
 |  | 
 | #ifdef CONFIG_NET_POLL_CONTROLLER | 
 | static void mlx4_en_netpoll(struct ether *dev) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_cq *cq; | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < priv->rx_ring_num; i++) { | 
 | 		cq = priv->rx_cq[i]; | 
 | 		napi_schedule(&cq->napi); | 
 | 	} | 
 | } | 
 | #endif | 
 |  | 
 | static void mlx4_en_tx_timeout(struct ether *dev) | 
 | { | 
 | 	panic("Disabled"); | 
 | #if 0 // AKAROS_PORT | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	int i; | 
 |  | 
 | 	if (netif_msg_timer(priv)) | 
 | 		en_warn(priv, "Tx timeout called on port:%d\n", priv->port); | 
 |  | 
 | 	for (i = 0; i < priv->tx_ring_num; i++) { | 
 | 		if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, i))) | 
 | 			continue; | 
 | 		en_warn(priv, "TX timeout on queue: %d, QP: 0x%x, CQ: 0x%x, Cons: 0x%x, Prod: 0x%x\n", | 
 | 			i, priv->tx_ring[i]->qpn, priv->tx_ring[i]->cqn, | 
 | 			priv->tx_ring[i]->cons, priv->tx_ring[i]->prod); | 
 | 	} | 
 |  | 
 | 	priv->port_stats.tx_timeout++; | 
 | 	en_dbg(DRV, priv, "Scheduling watchdog\n"); | 
 | 	queue_work(mdev->workqueue, &priv->watchdog_task); | 
 | #endif | 
 | } | 
 |  | 
 |  | 
 | struct netif_stats *mlx4_en_get_stats(struct ether *dev) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 |  | 
 | 	spin_lock(&priv->stats_lock); | 
 | 	memcpy(&priv->ret_stats, &priv->stats, sizeof(priv->stats)); | 
 | 	spin_unlock(&priv->stats_lock); | 
 |  | 
 | 	return &priv->ret_stats; | 
 | } | 
 |  | 
 | static void mlx4_en_set_default_moderation(struct mlx4_en_priv *priv) | 
 | { | 
 | 	struct mlx4_en_cq *cq; | 
 | 	int i; | 
 |  | 
 | 	/* If we haven't received a specific coalescing setting | 
 | 	 * (module param), we set the moderation parameters as follows: | 
 | 	 * - moder_cnt is set to the number of mtu sized packets to | 
 | 	 *   satisfy our coalescing target. | 
 | 	 * - moder_time is set to a fixed value. | 
 | 	 */ | 
 | 	priv->rx_frames = MLX4_EN_RX_COAL_TARGET; | 
 | 	priv->rx_usecs = MLX4_EN_RX_COAL_TIME; | 
 | 	priv->tx_frames = MLX4_EN_TX_COAL_PKTS; | 
 | 	priv->tx_usecs = MLX4_EN_TX_COAL_TIME; | 
 | 	en_dbg(INTR, priv, "Default coalesing params for mtu:%d - rx_frames:%d rx_usecs:%d\n", | 
 | 	       priv->dev->mtu, priv->rx_frames, priv->rx_usecs); | 
 |  | 
 | 	/* Setup cq moderation params */ | 
 | 	for (i = 0; i < priv->rx_ring_num; i++) { | 
 | 		cq = priv->rx_cq[i]; | 
 | 		cq->moder_cnt = priv->rx_frames; | 
 | 		cq->moder_time = priv->rx_usecs; | 
 | 		priv->last_moder_time[i] = MLX4_EN_AUTO_CONF; | 
 | 		priv->last_moder_packets[i] = 0; | 
 | 		priv->last_moder_bytes[i] = 0; | 
 | 	} | 
 |  | 
 | 	for (i = 0; i < priv->tx_ring_num; i++) { | 
 | 		cq = priv->tx_cq[i]; | 
 | 		cq->moder_cnt = priv->tx_frames; | 
 | 		cq->moder_time = priv->tx_usecs; | 
 | 	} | 
 |  | 
 | 	/* Reset auto-moderation params */ | 
 | 	priv->pkt_rate_low = MLX4_EN_RX_RATE_LOW; | 
 | 	priv->rx_usecs_low = MLX4_EN_RX_COAL_TIME_LOW; | 
 | 	priv->pkt_rate_high = MLX4_EN_RX_RATE_HIGH; | 
 | 	priv->rx_usecs_high = MLX4_EN_RX_COAL_TIME_HIGH; | 
 | 	priv->sample_interval = MLX4_EN_SAMPLE_INTERVAL; | 
 | 	priv->adaptive_rx_coal = 1; | 
 | 	priv->last_moder_jiffies = 0; | 
 | 	priv->last_moder_tx_packets = 0; | 
 | } | 
 |  | 
 | static void mlx4_en_auto_moderation(struct mlx4_en_priv *priv) | 
 | { | 
 | 	unsigned long period = (unsigned long) (jiffies - priv->last_moder_jiffies); | 
 | 	struct mlx4_en_cq *cq; | 
 | 	unsigned long packets; | 
 | 	unsigned long rate; | 
 | 	unsigned long avg_pkt_size; | 
 | 	unsigned long rx_packets; | 
 | 	unsigned long rx_bytes; | 
 | 	unsigned long rx_pkt_diff; | 
 | 	int moder_time; | 
 | 	int ring, err; | 
 |  | 
 | 	if (!priv->adaptive_rx_coal || period < priv->sample_interval * HZ) | 
 | 		return; | 
 |  | 
 | 	for (ring = 0; ring < priv->rx_ring_num; ring++) { | 
 | 		spin_lock(&priv->stats_lock); | 
 | 		rx_packets = priv->rx_ring[ring]->packets; | 
 | 		rx_bytes = priv->rx_ring[ring]->bytes; | 
 | 		spin_unlock(&priv->stats_lock); | 
 |  | 
 | 		rx_pkt_diff = ((unsigned long) (rx_packets - | 
 | 				priv->last_moder_packets[ring])); | 
 | 		packets = rx_pkt_diff; | 
 | 		rate = packets * HZ / period; | 
 | 		avg_pkt_size = packets ? ((unsigned long) (rx_bytes - | 
 | 				priv->last_moder_bytes[ring])) / packets : 0; | 
 |  | 
 | 		/* Apply auto-moderation only when packet rate | 
 | 		 * exceeds a rate that it matters */ | 
 | 		if (rate > (MLX4_EN_RX_RATE_THRESH / priv->rx_ring_num) && | 
 | 		    avg_pkt_size > MLX4_EN_AVG_PKT_SMALL) { | 
 | 			if (rate < priv->pkt_rate_low) | 
 | 				moder_time = priv->rx_usecs_low; | 
 | 			else if (rate > priv->pkt_rate_high) | 
 | 				moder_time = priv->rx_usecs_high; | 
 | 			else | 
 | 				moder_time = (rate - priv->pkt_rate_low) * | 
 | 					(priv->rx_usecs_high - priv->rx_usecs_low) / | 
 | 					(priv->pkt_rate_high - priv->pkt_rate_low) + | 
 | 					priv->rx_usecs_low; | 
 | 		} else { | 
 | 			moder_time = priv->rx_usecs_low; | 
 | 		} | 
 |  | 
 | 		if (moder_time != priv->last_moder_time[ring]) { | 
 | 			priv->last_moder_time[ring] = moder_time; | 
 | 			cq = priv->rx_cq[ring]; | 
 | 			cq->moder_time = moder_time; | 
 | 			cq->moder_cnt = priv->rx_frames; | 
 | 			err = mlx4_en_set_cq_moder(priv, cq); | 
 | 			if (err) | 
 | 				en_err(priv, "Failed modifying moderation for cq:%d\n", | 
 | 				       ring); | 
 | 		} | 
 | 		priv->last_moder_packets[ring] = rx_packets; | 
 | 		priv->last_moder_bytes[ring] = rx_bytes; | 
 | 	} | 
 |  | 
 | 	priv->last_moder_jiffies = jiffies; | 
 | } | 
 |  | 
 | static void mlx4_en_do_get_stats(struct work_struct *work) | 
 | { | 
 | 	struct delayed_work *delay = to_delayed_work(work); | 
 | 	struct mlx4_en_priv *priv = container_of(delay, struct mlx4_en_priv, | 
 | 						 stats_task); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	int err; | 
 |  | 
 | 	qlock(&mdev->state_lock); | 
 | 	if (mdev->device_up) { | 
 | 		if (priv->port_up) { | 
 | 			err = mlx4_en_DUMP_ETH_STATS(mdev, priv->port, 0); | 
 | 			if (err) | 
 | 				en_dbg(HW, priv, "Could not update stats\n"); | 
 |  | 
 | 			mlx4_en_auto_moderation(priv); | 
 | 		} | 
 |  | 
 | 		queue_delayed_work(mdev->workqueue, &priv->stats_task, STATS_DELAY); | 
 | 	} | 
 | #if 0 // AKAROS_PORT | 
 | 	if (mdev->mac_removed[MLX4_MAX_PORTS + 1 - priv->port]) { | 
 | 		mlx4_en_do_set_mac(priv, priv->current_mac); | 
 | 		mdev->mac_removed[MLX4_MAX_PORTS + 1 - priv->port] = 0; | 
 | 	} | 
 | #endif | 
 | 	qunlock(&mdev->state_lock); | 
 | } | 
 |  | 
 | /* mlx4_en_service_task - Run service task for tasks that needed to be done | 
 |  * periodically | 
 |  */ | 
 | static void mlx4_en_service_task(struct work_struct *work) | 
 | { | 
 | 	struct delayed_work *delay = to_delayed_work(work); | 
 | 	struct mlx4_en_priv *priv = container_of(delay, struct mlx4_en_priv, | 
 | 						 service_task); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 |  | 
 | 	qlock(&mdev->state_lock); | 
 | 	if (mdev->device_up) { | 
 | #if 0 // AKAROS_PORT | 
 | 		if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS) | 
 | 			mlx4_en_ptp_overflow_check(mdev); | 
 | #endif | 
 |  | 
 | 		mlx4_en_recover_from_oom(priv); | 
 | 		queue_delayed_work(mdev->workqueue, &priv->service_task, | 
 | 				   SERVICE_TASK_DELAY); | 
 | 	} | 
 | 	qunlock(&mdev->state_lock); | 
 | } | 
 |  | 
 | static void mlx4_en_linkstate(struct work_struct *work) | 
 | { | 
 | 	struct mlx4_en_priv *priv = container_of(work, struct mlx4_en_priv, | 
 | 						 linkstate_task); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	int linkstate = priv->link_state; | 
 |  | 
 | 	qlock(&mdev->state_lock); | 
 | 	/* If observable port state changed set carrier state and | 
 | 	 * report to system log */ | 
 | 	if (priv->last_link_state != linkstate) { | 
 | 		if (linkstate == MLX4_DEV_EVENT_PORT_DOWN) { | 
 | 			en_info(priv, "Link Down\n"); | 
 | 			netif_carrier_off(priv->dev); | 
 | 		} else { | 
 | 			en_info(priv, "Link Up\n"); | 
 | 			netif_carrier_on(priv->dev); | 
 | 		} | 
 | 	} | 
 | 	priv->last_link_state = linkstate; | 
 | 	qunlock(&mdev->state_lock); | 
 | } | 
 |  | 
 | static int mlx4_en_init_affinity_hint(struct mlx4_en_priv *priv, int ring_idx) | 
 | { | 
 | 	struct mlx4_en_rx_ring *ring = priv->rx_ring[ring_idx]; | 
 | 	int numa_node = priv->mdev->dev->numa_node; | 
 |  | 
 | 	if (!zalloc_cpumask_var(&ring->affinity_mask, MEM_WAIT)) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	cpumask_set_cpu(cpumask_local_spread(ring_idx, numa_node), | 
 | 			ring->affinity_mask); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void mlx4_en_free_affinity_hint(struct mlx4_en_priv *priv, int ring_idx) | 
 | { | 
 | 	panic("Disabled"); | 
 | #if 0 // AKAROS_PORT | 
 | 	free_cpumask_var(priv->rx_ring[ring_idx]->affinity_mask); | 
 | #endif | 
 | } | 
 |  | 
 | int mlx4_en_start_port(struct ether *dev) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	struct mlx4_en_cq *cq; | 
 | 	struct mlx4_en_tx_ring *tx_ring; | 
 | 	int rx_index = 0; | 
 | 	int tx_index = 0; | 
 | 	int err = 0; | 
 | 	int i; | 
 | 	int j; | 
 | 	uint8_t mc_list[16] = {0}; | 
 |  | 
 | 	if (priv->port_up) { | 
 | 		en_dbg(DRV, priv, "start port called while port already up\n"); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	INIT_LIST_HEAD(&priv->mc_list); | 
 | 	INIT_LIST_HEAD(&priv->curr_list); | 
 | #if 0 // AKAROS_PORT | 
 | 	INIT_LIST_HEAD(&priv->ethtool_list); | 
 | 	memset(&priv->ethtool_rules[0], 0, | 
 | 	       sizeof(struct ethtool_flow_id) * MAX_NUM_OF_FS_RULES); | 
 | #endif | 
 |  | 
 | 	/* Calculate Rx buf size */ | 
 | 	dev->mtu = MIN(dev->mtu, priv->max_mtu); | 
 | 	mlx4_en_calc_rx_buf(dev); | 
 | 	en_dbg(DRV, priv, "Rx buf size:%d\n", priv->rx_skb_size); | 
 |  | 
 | 	/* Configure rx cq's and rings */ | 
 | 	err = mlx4_en_activate_rx_rings(priv); | 
 | 	if (err) { | 
 | 		en_err(priv, "Failed to activate RX rings\n"); | 
 | 		return err; | 
 | 	} | 
 | 	for (i = 0; i < priv->rx_ring_num; i++) { | 
 | 		cq = priv->rx_cq[i]; | 
 |  | 
 | 		mlx4_en_cq_init_lock(cq); | 
 |  | 
 | 		err = mlx4_en_init_affinity_hint(priv, i); | 
 | 		if (err) { | 
 | 			en_err(priv, "Failed preparing IRQ affinity hint\n"); | 
 | 			goto cq_err; | 
 | 		} | 
 |  | 
 | 		err = mlx4_en_activate_cq(priv, cq, i); | 
 | 		if (err) { | 
 | 			en_err(priv, "Failed activating Rx CQ\n"); | 
 | 			mlx4_en_free_affinity_hint(priv, i); | 
 | 			goto cq_err; | 
 | 		} | 
 |  | 
 | 		for (j = 0; j < cq->size; j++) { | 
 | 			struct mlx4_cqe *cqe = NULL; | 
 |  | 
 | 			cqe = mlx4_en_get_cqe(cq->buf, j, priv->cqe_size) + | 
 | 			      priv->cqe_factor; | 
 | 			cqe->owner_sr_opcode = MLX4_CQE_OWNER_MASK; | 
 | 		} | 
 |  | 
 | 		err = mlx4_en_set_cq_moder(priv, cq); | 
 | 		if (err) { | 
 | 			en_err(priv, "Failed setting cq moderation parameters\n"); | 
 | 			mlx4_en_deactivate_cq(priv, cq); | 
 | 			mlx4_en_free_affinity_hint(priv, i); | 
 | 			goto cq_err; | 
 | 		} | 
 | 		mlx4_en_arm_cq(priv, cq); | 
 | 		priv->rx_ring[i]->cqn = cq->mcq.cqn; | 
 | 		++rx_index; | 
 | 	} | 
 |  | 
 | 	/* Set qp number */ | 
 | 	en_dbg(DRV, priv, "Getting qp number for port %d\n", priv->port); | 
 | 	err = mlx4_en_get_qp(priv); | 
 | 	if (err) { | 
 | 		en_err(priv, "Failed getting eth qp\n"); | 
 | 		goto cq_err; | 
 | 	} | 
 | 	mdev->mac_removed[priv->port] = 0; | 
 |  | 
 | 	err = mlx4_en_config_rss_steer(priv); | 
 | 	if (err) { | 
 | 		en_err(priv, "Failed configuring rss steering\n"); | 
 | 		goto mac_err; | 
 | 	} | 
 |  | 
 | 	err = mlx4_en_create_drop_qp(priv); | 
 | 	if (err) | 
 | 		goto rss_err; | 
 |  | 
 | 	/* Configure tx cq's and rings */ | 
 | 	for (i = 0; i < priv->tx_ring_num; i++) { | 
 | 		/* Configure cq */ | 
 | 		cq = priv->tx_cq[i]; | 
 | 		err = mlx4_en_activate_cq(priv, cq, i); | 
 | 		if (err) { | 
 | 			en_err(priv, "Failed allocating Tx CQ\n"); | 
 | 			goto tx_err; | 
 | 		} | 
 | 		err = mlx4_en_set_cq_moder(priv, cq); | 
 | 		if (err) { | 
 | 			en_err(priv, "Failed setting cq moderation parameters\n"); | 
 | 			mlx4_en_deactivate_cq(priv, cq); | 
 | 			goto tx_err; | 
 | 		} | 
 | 		en_dbg(DRV, priv, "Resetting index of collapsed CQ:%d to -1\n", i); | 
 | 		cq->buf->wqe_index = cpu_to_be16(0xffff); | 
 |  | 
 | 		/* Configure ring */ | 
 | 		tx_ring = priv->tx_ring[i]; | 
 | 		err = mlx4_en_activate_tx_ring(priv, tx_ring, cq->mcq.cqn, | 
 | 			i / priv->num_tx_rings_p_up); | 
 | 		if (err) { | 
 | 			en_err(priv, "Failed allocating Tx ring\n"); | 
 | 			mlx4_en_deactivate_cq(priv, cq); | 
 | 			goto tx_err; | 
 | 		} | 
 | #if 0 // AKAROS_PORT | 
 | 		tx_ring->tx_queue = netdev_get_tx_queue(dev, i); | 
 | #else | 
 | 		tx_ring->tx_queue = 0; /* TODO multi-queue support. */ | 
 | #endif | 
 |  | 
 | 		/* Arm CQ for TX completions */ | 
 | 		mlx4_en_arm_cq(priv, cq); | 
 |  | 
 | 		/* Set initial ownership of all Tx TXBBs to SW (1) */ | 
 | 		for (j = 0; j < tx_ring->buf_size; j += STAMP_STRIDE) | 
 | 			*((uint32_t *) (tx_ring->buf + j)) = 0xffffffff; | 
 | 		++tx_index; | 
 | 	} | 
 |  | 
 | 	/* Configure port */ | 
 | 	err = mlx4_SET_PORT_general(mdev->dev, priv->port, | 
 | 				    priv->rx_skb_size + ETH_FCS_LEN, | 
 | 				    priv->prof->tx_pause, | 
 | 				    priv->prof->tx_ppp, | 
 | 				    priv->prof->rx_pause, | 
 | 				    priv->prof->rx_ppp); | 
 | 	if (err) { | 
 | 		en_err(priv, "Failed setting port general configurations for port %d, with error %d\n", | 
 | 		       priv->port, err); | 
 | 		goto tx_err; | 
 | 	} | 
 | 	/* Set default qp number */ | 
 | 	err = mlx4_SET_PORT_qpn_calc(mdev->dev, priv->port, priv->base_qpn, 0); | 
 | 	if (err) { | 
 | 		en_err(priv, "Failed setting default qp numbers\n"); | 
 | 		goto tx_err; | 
 | 	} | 
 |  | 
 | #if 0 // AKAROS_PORT | 
 | 	if (mdev->dev->caps.tunnel_offload_mode == MLX4_TUNNEL_OFFLOAD_MODE_VXLAN) { | 
 | 		err = mlx4_SET_PORT_VXLAN(mdev->dev, priv->port, VXLAN_STEER_BY_OUTER_MAC, 1); | 
 | 		if (err) { | 
 | 			en_err(priv, "Failed setting port L2 tunnel configuration, err %d\n", | 
 | 			       err); | 
 | 			goto tx_err; | 
 | 		} | 
 | 	} | 
 | #endif | 
 |  | 
 | 	/* Init port */ | 
 | 	en_dbg(HW, priv, "Initializing port\n"); | 
 | 	err = mlx4_INIT_PORT(mdev->dev, priv->port); | 
 | 	if (err) { | 
 | 		en_err(priv, "Failed Initializing port\n"); | 
 | 		goto tx_err; | 
 | 	} | 
 |  | 
 | 	/* Attach rx QP to bradcast address */ | 
 | 	eth_broadcast_addr(&mc_list[10]); | 
 | 	mc_list[5] = priv->port; /* needed for B0 steering support */ | 
 | 	if (mlx4_multicast_attach(mdev->dev, &priv->rss_map.indir_qp, mc_list, | 
 | 				  priv->port, 0, MLX4_PROT_ETH, | 
 | 				  &priv->broadcast_id)) | 
 | 		mlx4_warn(mdev, "Failed Attaching Broadcast\n"); | 
 |  | 
 | 	/* Must redo promiscuous mode setup. */ | 
 | 	priv->flags &= ~(MLX4_EN_FLAG_PROMISC | MLX4_EN_FLAG_MC_PROMISC); | 
 |  | 
 | 	/* Schedule multicast task to populate multicast list */ | 
 | 	queue_work(mdev->workqueue, &priv->rx_mode_task); | 
 |  | 
 | #ifdef CONFIG_MLX4_EN_VXLAN | 
 | 	if (priv->mdev->dev->caps.tunnel_offload_mode == MLX4_TUNNEL_OFFLOAD_MODE_VXLAN) | 
 | 		vxlan_get_rx_port(dev); | 
 | #endif | 
 | 	priv->port_up = true; | 
 | #if 0 // AKAROS_PORT | 
 | 	netif_tx_start_all_queues(dev); | 
 | 	netif_device_attach(dev); | 
 | #endif | 
 |  | 
 | 	return 0; | 
 |  | 
 | tx_err: | 
 | 	while (tx_index--) { | 
 | 		mlx4_en_deactivate_tx_ring(priv, priv->tx_ring[tx_index]); | 
 | 		mlx4_en_deactivate_cq(priv, priv->tx_cq[tx_index]); | 
 | 	} | 
 | 	mlx4_en_destroy_drop_qp(priv); | 
 | rss_err: | 
 | 	mlx4_en_release_rss_steer(priv); | 
 | mac_err: | 
 | 	mlx4_en_put_qp(priv); | 
 | cq_err: | 
 | 	while (rx_index--) { | 
 | 		mlx4_en_deactivate_cq(priv, priv->rx_cq[rx_index]); | 
 | 		mlx4_en_free_affinity_hint(priv, rx_index); | 
 | 	} | 
 | 	for (i = 0; i < priv->rx_ring_num; i++) | 
 | 		mlx4_en_deactivate_rx_ring(priv, priv->rx_ring[i]); | 
 |  | 
 | 	return err; /* need to close devices */ | 
 | } | 
 |  | 
 |  | 
 | void mlx4_en_stop_port(struct ether *dev, int detach) | 
 | { | 
 | 	panic("Disabled"); | 
 | #if 0 // AKAROS_PORT | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	struct mlx4_en_mc_list *mclist, *tmp; | 
 | #if 0 // AKAROS_PORT | 
 | 	struct ethtool_flow_id *flow, *tmp_flow; | 
 | #endif | 
 | 	int i; | 
 | 	uint8_t mc_list[16] = {0}; | 
 |  | 
 | 	if (!priv->port_up) { | 
 | 		en_dbg(DRV, priv, "stop port called while port already down\n"); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* close port*/ | 
 | 	mlx4_CLOSE_PORT(mdev->dev, priv->port); | 
 |  | 
 | 	/* Synchronize with tx routine */ | 
 | 	netif_tx_lock_bh(dev); | 
 | 	if (detach) | 
 | 		netif_device_detach(dev); | 
 | 	netif_tx_stop_all_queues(dev); | 
 | 	netif_tx_unlock_bh(dev); | 
 |  | 
 | 	netif_tx_disable(dev); | 
 |  | 
 | 	/* Set port as not active */ | 
 | 	priv->port_up = false; | 
 |  | 
 | 	/* Promsicuous mode */ | 
 | 	if (mdev->dev->caps.steering_mode == | 
 | 	    MLX4_STEERING_MODE_DEVICE_MANAGED) { | 
 | 		priv->flags &= ~(MLX4_EN_FLAG_PROMISC | | 
 | 				 MLX4_EN_FLAG_MC_PROMISC); | 
 | 		mlx4_flow_steer_promisc_remove(mdev->dev, | 
 | 					       priv->port, | 
 | 					       MLX4_FS_ALL_DEFAULT); | 
 | 		mlx4_flow_steer_promisc_remove(mdev->dev, | 
 | 					       priv->port, | 
 | 					       MLX4_FS_MC_DEFAULT); | 
 | 	} else if (priv->flags & MLX4_EN_FLAG_PROMISC) { | 
 | 		priv->flags &= ~MLX4_EN_FLAG_PROMISC; | 
 |  | 
 | 		/* Disable promiscouos mode */ | 
 | 		mlx4_unicast_promisc_remove(mdev->dev, priv->base_qpn, | 
 | 					    priv->port); | 
 |  | 
 | 		/* Disable Multicast promisc */ | 
 | 		if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) { | 
 | 			mlx4_multicast_promisc_remove(mdev->dev, priv->base_qpn, | 
 | 						      priv->port); | 
 | 			priv->flags &= ~MLX4_EN_FLAG_MC_PROMISC; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* Detach All multicasts */ | 
 | 	eth_broadcast_addr(&mc_list[10]); | 
 | 	mc_list[5] = priv->port; /* needed for B0 steering support */ | 
 | 	mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, mc_list, | 
 | 			      MLX4_PROT_ETH, priv->broadcast_id); | 
 | 	list_for_each_entry(mclist, &priv->curr_list, list) { | 
 | 		memcpy(&mc_list[10], mclist->addr, Eaddrlen); | 
 | 		mc_list[5] = priv->port; | 
 | 		mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, | 
 | 				      mc_list, MLX4_PROT_ETH, mclist->reg_id); | 
 | 		if (mclist->tunnel_reg_id) | 
 | 			mlx4_flow_detach(mdev->dev, mclist->tunnel_reg_id); | 
 | 	} | 
 | 	mlx4_en_clear_list(dev); | 
 | 	list_for_each_entry_safe(mclist, tmp, &priv->curr_list, list) { | 
 | 		list_del(&mclist->list); | 
 | 		kfree(mclist); | 
 | 	} | 
 |  | 
 | 	/* Flush multicast filter */ | 
 | 	mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 0, 1, MLX4_MCAST_CONFIG); | 
 |  | 
 | #if 0 // AKAROS_PORT | 
 | 	/* Remove flow steering rules for the port*/ | 
 | 	if (mdev->dev->caps.steering_mode == | 
 | 	    MLX4_STEERING_MODE_DEVICE_MANAGED) { | 
 | 		ASSERT_RTNL(); | 
 | 		list_for_each_entry_safe(flow, tmp_flow, | 
 | 					 &priv->ethtool_list, list) { | 
 | 			mlx4_flow_detach(mdev->dev, flow->id); | 
 | 			list_del(&flow->list); | 
 | 		} | 
 | 	} | 
 | #endif | 
 |  | 
 | 	mlx4_en_destroy_drop_qp(priv); | 
 |  | 
 | 	/* Free TX Rings */ | 
 | 	for (i = 0; i < priv->tx_ring_num; i++) { | 
 | 		mlx4_en_deactivate_tx_ring(priv, priv->tx_ring[i]); | 
 | 		mlx4_en_deactivate_cq(priv, priv->tx_cq[i]); | 
 | 	} | 
 | 	kthread_usleep(1000 * 10); | 
 |  | 
 | 	for (i = 0; i < priv->tx_ring_num; i++) | 
 | 		mlx4_en_free_tx_buf(dev, priv->tx_ring[i]); | 
 |  | 
 | 	/* Free RSS qps */ | 
 | 	mlx4_en_release_rss_steer(priv); | 
 |  | 
 | 	/* Unregister Mac address for the port */ | 
 | 	mlx4_en_put_qp(priv); | 
 | 	if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_REASSIGN_MAC_EN)) | 
 | 		mdev->mac_removed[priv->port] = 1; | 
 |  | 
 | 	/* Free RX Rings */ | 
 | 	for (i = 0; i < priv->rx_ring_num; i++) { | 
 | 		struct mlx4_en_cq *cq = priv->rx_cq[i]; | 
 |  | 
 | 		local_bh_disable(); | 
 | 		while (!mlx4_en_cq_lock_napi(cq)) { | 
 | 			pr_info("CQ %d locked\n", i); | 
 | 			mdelay(1); | 
 | 		} | 
 | 		local_bh_enable(); | 
 |  | 
 | 		napi_synchronize(&cq->napi); | 
 | 		mlx4_en_deactivate_rx_ring(priv, priv->rx_ring[i]); | 
 | 		mlx4_en_deactivate_cq(priv, cq); | 
 |  | 
 | 		mlx4_en_free_affinity_hint(priv, i); | 
 | 	} | 
 | #endif | 
 | } | 
 |  | 
 | static void mlx4_en_restart(struct work_struct *work) | 
 | { | 
 | 	struct mlx4_en_priv *priv = container_of(work, struct mlx4_en_priv, | 
 | 						 watchdog_task); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	struct ether *dev = priv->dev; | 
 |  | 
 | 	en_dbg(DRV, priv, "Watchdog task called for port %d\n", priv->port); | 
 |  | 
 | 	qlock(&mdev->state_lock); | 
 | 	if (priv->port_up) { | 
 | 		mlx4_en_stop_port(dev, 1); | 
 | 		if (mlx4_en_start_port(dev)) | 
 | 			en_err(priv, "Failed restarting port %d\n", priv->port); | 
 | 	} | 
 | 	qunlock(&mdev->state_lock); | 
 | } | 
 |  | 
 | static void mlx4_en_clear_stats(struct ether *dev) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	int i; | 
 |  | 
 | 	if (mlx4_en_DUMP_ETH_STATS(mdev, priv->port, 1)) | 
 | 		en_dbg(HW, priv, "Failed dumping statistics\n"); | 
 |  | 
 | 	memset(&priv->stats, 0, sizeof(priv->stats)); | 
 | 	memset(&priv->pstats, 0, sizeof(priv->pstats)); | 
 | 	memset(&priv->pkstats, 0, sizeof(priv->pkstats)); | 
 | 	memset(&priv->port_stats, 0, sizeof(priv->port_stats)); | 
 | 	memset(&priv->rx_flowstats, 0, sizeof(priv->rx_flowstats)); | 
 | 	memset(&priv->tx_flowstats, 0, sizeof(priv->tx_flowstats)); | 
 | 	memset(&priv->rx_priority_flowstats, 0, | 
 | 	       sizeof(priv->rx_priority_flowstats)); | 
 | 	memset(&priv->tx_priority_flowstats, 0, | 
 | 	       sizeof(priv->tx_priority_flowstats)); | 
 |  | 
 | 	for (i = 0; i < priv->tx_ring_num; i++) { | 
 | 		priv->tx_ring[i]->bytes = 0; | 
 | 		priv->tx_ring[i]->packets = 0; | 
 | 		priv->tx_ring[i]->tx_csum = 0; | 
 | 	} | 
 | 	for (i = 0; i < priv->rx_ring_num; i++) { | 
 | 		priv->rx_ring[i]->bytes = 0; | 
 | 		priv->rx_ring[i]->packets = 0; | 
 | 		priv->rx_ring[i]->csum_ok = 0; | 
 | 		priv->rx_ring[i]->csum_none = 0; | 
 | 		priv->rx_ring[i]->csum_complete = 0; | 
 | 	} | 
 | } | 
 |  | 
 | int mlx4_en_open(struct ether *dev) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	int err = 0; | 
 |  | 
 | 	qlock(&mdev->state_lock); | 
 |  | 
 | 	if (!mdev->device_up) { | 
 | 		en_err(priv, "Cannot open - device down/disabled\n"); | 
 | 		err = -EBUSY; | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	/* Reset HW statistics and SW counters */ | 
 | 	mlx4_en_clear_stats(dev); | 
 |  | 
 | 	err = mlx4_en_start_port(dev); | 
 | 	if (err) | 
 | 		en_err(priv, "Failed starting port:%d\n", priv->port); | 
 |  | 
 | out: | 
 | 	qunlock(&mdev->state_lock); | 
 | 	return err; | 
 | } | 
 |  | 
 |  | 
 | static int mlx4_en_close(struct ether *dev) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 |  | 
 | 	en_dbg(IFDOWN, priv, "Close port called\n"); | 
 |  | 
 | 	qlock(&mdev->state_lock); | 
 |  | 
 | 	mlx4_en_stop_port(dev, 0); | 
 | 	netif_carrier_off(dev); | 
 |  | 
 | 	qunlock(&mdev->state_lock); | 
 | 	return 0; | 
 | } | 
 |  | 
 | void mlx4_en_free_resources(struct mlx4_en_priv *priv) | 
 | { | 
 | 	int i; | 
 |  | 
 | #ifdef CONFIG_RFS_ACCEL | 
 | 	free_irq_cpu_rmap(priv->dev->rx_cpu_rmap); | 
 | 	priv->dev->rx_cpu_rmap = NULL; | 
 | #endif | 
 |  | 
 | 	for (i = 0; i < priv->tx_ring_num; i++) { | 
 | 		if (priv->tx_ring && priv->tx_ring[i]) | 
 | 			mlx4_en_destroy_tx_ring(priv, &priv->tx_ring[i]); | 
 | 		if (priv->tx_cq && priv->tx_cq[i]) | 
 | 			mlx4_en_destroy_cq(priv, &priv->tx_cq[i]); | 
 | 	} | 
 |  | 
 | 	for (i = 0; i < priv->rx_ring_num; i++) { | 
 | 		if (priv->rx_ring[i]) | 
 | 			mlx4_en_destroy_rx_ring(priv, &priv->rx_ring[i], | 
 | 				priv->prof->rx_ring_size, priv->stride); | 
 | 		if (priv->rx_cq[i]) | 
 | 			mlx4_en_destroy_cq(priv, &priv->rx_cq[i]); | 
 | 	} | 
 |  | 
 | 	if (priv->base_tx_qpn) { | 
 | 		mlx4_qp_release_range(priv->mdev->dev, priv->base_tx_qpn, priv->tx_ring_num); | 
 | 		priv->base_tx_qpn = 0; | 
 | 	} | 
 | } | 
 |  | 
 | int mlx4_en_alloc_resources(struct mlx4_en_priv *priv) | 
 | { | 
 | 	struct mlx4_en_port_profile *prof = priv->prof; | 
 | 	int i; | 
 | 	int node; | 
 |  | 
 | 	/* Create tx Rings */ | 
 | 	for (i = 0; i < priv->tx_ring_num; i++) { | 
 | 		node = cpu_to_node(i % num_online_cpus()); | 
 | 		if (mlx4_en_create_cq(priv, &priv->tx_cq[i], | 
 | 				      prof->tx_ring_size, i, TX, node)) | 
 | 			goto err; | 
 |  | 
 | 		if (mlx4_en_create_tx_ring(priv, &priv->tx_ring[i], | 
 | 					   prof->tx_ring_size, TXBB_SIZE, | 
 | 					   node, i)) | 
 | 			goto err; | 
 | 	} | 
 |  | 
 | 	/* Create rx Rings */ | 
 | 	for (i = 0; i < priv->rx_ring_num; i++) { | 
 | 		node = cpu_to_node(i % num_online_cpus()); | 
 | 		if (mlx4_en_create_cq(priv, &priv->rx_cq[i], | 
 | 				      prof->rx_ring_size, i, RX, node)) | 
 | 			goto err; | 
 |  | 
 | 		if (mlx4_en_create_rx_ring(priv, &priv->rx_ring[i], | 
 | 					   prof->rx_ring_size, priv->stride, | 
 | 					   node)) | 
 | 			goto err; | 
 | 	} | 
 |  | 
 | #ifdef CONFIG_RFS_ACCEL | 
 | 	if (priv->mdev->dev->caps.comp_pool) { | 
 | 		priv->dev->rx_cpu_rmap = alloc_irq_cpu_rmap(priv->mdev->dev->caps.comp_pool); | 
 | 		if (!priv->dev->rx_cpu_rmap) | 
 | 			goto err; | 
 | 	} | 
 | #endif | 
 |  | 
 | 	return 0; | 
 |  | 
 | err: | 
 | 	en_err(priv, "Failed to allocate NIC resources\n"); | 
 | 	for (i = 0; i < priv->rx_ring_num; i++) { | 
 | 		if (priv->rx_ring[i]) | 
 | 			mlx4_en_destroy_rx_ring(priv, &priv->rx_ring[i], | 
 | 						prof->rx_ring_size, | 
 | 						priv->stride); | 
 | 		if (priv->rx_cq[i]) | 
 | 			mlx4_en_destroy_cq(priv, &priv->rx_cq[i]); | 
 | 	} | 
 | 	for (i = 0; i < priv->tx_ring_num; i++) { | 
 | 		if (priv->tx_ring[i]) | 
 | 			mlx4_en_destroy_tx_ring(priv, &priv->tx_ring[i]); | 
 | 		if (priv->tx_cq[i]) | 
 | 			mlx4_en_destroy_cq(priv, &priv->tx_cq[i]); | 
 | 	} | 
 | 	return -ENOMEM; | 
 | } | 
 |  | 
 |  | 
 | void mlx4_en_destroy_netdev(struct ether *dev) | 
 | { | 
 | 	panic("Disabled"); | 
 | #if 0 // AKAROS_PORT | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 |  | 
 | 	en_dbg(DRV, priv, "Destroying netdev on port:%d\n", priv->port); | 
 |  | 
 | 	/* Unregister device - this will close the port if it was up */ | 
 | 	if (priv->registered) | 
 | 		unregister_netdev(dev); | 
 |  | 
 | 	if (priv->allocated) | 
 | 		mlx4_free_hwq_res(mdev->dev, &priv->res, MLX4_EN_PAGE_SIZE); | 
 |  | 
 | 	cancel_delayed_work(&priv->stats_task); | 
 | 	cancel_delayed_work(&priv->service_task); | 
 | 	/* flush any pending task for this netdev */ | 
 | 	flush_workqueue(mdev->workqueue); | 
 |  | 
 | 	/* Detach the netdev so tasks would not attempt to access it */ | 
 | 	qlock(&mdev->state_lock); | 
 | 	mdev->pndev[priv->port] = NULL; | 
 | 	mdev->upper[priv->port] = NULL; | 
 | 	qunlock(&mdev->state_lock); | 
 |  | 
 | 	mlx4_en_free_resources(priv); | 
 |  | 
 | 	kfree(priv->tx_ring); | 
 | 	kfree(priv->tx_cq); | 
 |  | 
 | 	free_netdev(dev); | 
 | #endif | 
 | } | 
 |  | 
 | #if 0 // AKAROS_PORT | 
 | static int mlx4_en_change_mtu(struct ether *dev, int new_mtu) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	int err = 0; | 
 |  | 
 | 	en_dbg(DRV, priv, "Change MTU called - current:%d new:%d\n", | 
 | 		 dev->mtu, new_mtu); | 
 |  | 
 | 	if ((new_mtu < MLX4_EN_MIN_MTU) || (new_mtu > priv->max_mtu)) { | 
 | 		en_err(priv, "Bad MTU size:%d.\n", new_mtu); | 
 | 		return -EPERM; | 
 | 	} | 
 | 	dev->mtu = new_mtu; | 
 |  | 
 | 	if (netif_running(dev)) { | 
 | 		qlock(&mdev->state_lock); | 
 | 		if (!mdev->device_up) { | 
 | 			/* NIC is probably restarting - let watchdog task reset | 
 | 			 * the port */ | 
 | 			en_dbg(DRV, priv, "Change MTU called with card down!?\n"); | 
 | 		} else { | 
 | 			mlx4_en_stop_port(dev, 1); | 
 | 			err = mlx4_en_start_port(dev); | 
 | 			if (err) { | 
 | 				en_err(priv, "Failed restarting port:%d\n", | 
 | 					 priv->port); | 
 | 				queue_work(mdev->workqueue, &priv->watchdog_task); | 
 | 			} | 
 | 		} | 
 | 		qunlock(&mdev->state_lock); | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int mlx4_en_hwtstamp_set(struct ether *dev, struct ifreq *ifr) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	struct hwtstamp_config config; | 
 |  | 
 | 	if (memcpy_from_user(current, &config, ifr->ifr_data, sizeof(config))) | 
 | 		return -EFAULT; | 
 |  | 
 | 	/* reserved for future extensions */ | 
 | 	if (config.flags) | 
 | 		return -EINVAL; | 
 |  | 
 | 	/* device doesn't support time stamping */ | 
 | 	if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS)) | 
 | 		return -EINVAL; | 
 |  | 
 | 	/* TX HW timestamp */ | 
 | 	switch (config.tx_type) { | 
 | 	case HWTSTAMP_TX_OFF: | 
 | 	case HWTSTAMP_TX_ON: | 
 | 		break; | 
 | 	default: | 
 | 		return -ERANGE; | 
 | 	} | 
 |  | 
 | 	/* RX HW timestamp */ | 
 | 	switch (config.rx_filter) { | 
 | 	case HWTSTAMP_FILTER_NONE: | 
 | 		break; | 
 | 	case HWTSTAMP_FILTER_ALL: | 
 | 	case HWTSTAMP_FILTER_SOME: | 
 | 	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: | 
 | 	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: | 
 | 	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: | 
 | 	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: | 
 | 	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: | 
 | 	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: | 
 | 	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: | 
 | 	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: | 
 | 	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: | 
 | 	case HWTSTAMP_FILTER_PTP_V2_EVENT: | 
 | 	case HWTSTAMP_FILTER_PTP_V2_SYNC: | 
 | 	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: | 
 | 		config.rx_filter = HWTSTAMP_FILTER_ALL; | 
 | 		break; | 
 | 	default: | 
 | 		return -ERANGE; | 
 | 	} | 
 |  | 
 | 	if (mlx4_en_reset_config(dev, config, dev->feat)) { | 
 | 		config.tx_type = HWTSTAMP_TX_OFF; | 
 | 		config.rx_filter = HWTSTAMP_FILTER_NONE; | 
 | 	} | 
 |  | 
 | 	return memcpy_to_user(current, ifr->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; | 
 | } | 
 |  | 
 | static int mlx4_en_hwtstamp_get(struct ether *dev, struct ifreq *ifr) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 |  | 
 | 	return memcpy_to_user(current, ifr->ifr_data, &priv->hwtstamp_config, | 
 | 			      sizeof(priv->hwtstamp_config)) ? -EFAULT : 0; | 
 | } | 
 |  | 
 | static int mlx4_en_ioctl(struct ether *dev, struct ifreq *ifr, int cmd) | 
 | { | 
 | 	switch (cmd) { | 
 | 	case SIOCSHWTSTAMP: | 
 | 		return mlx4_en_hwtstamp_set(dev, ifr); | 
 | 	case SIOCGHWTSTAMP: | 
 | 		return mlx4_en_hwtstamp_get(dev, ifr); | 
 | 	default: | 
 | 		return -EOPNOTSUPP; | 
 | 	} | 
 | } | 
 |  | 
 | static int mlx4_en_set_features(struct ether *netdev, | 
 | 				netdev_features_t features) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(netdev); | 
 | 	bool reset = false; | 
 | 	int ret = 0; | 
 |  | 
 | 	if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_RXFCS)) { | 
 | 		en_info(priv, "Turn %s RX-FCS\n", | 
 | 			(features & NETIF_F_RXFCS) ? "ON" : "OFF"); | 
 | 		reset = true; | 
 | 	} | 
 |  | 
 | 	if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_RXALL)) { | 
 | 		uint8_t ignore_fcs_value = (features & NETIF_F_RXALL) ? 1 : 0; | 
 |  | 
 | 		en_info(priv, "Turn %s RX-ALL\n", | 
 | 			ignore_fcs_value ? "ON" : "OFF"); | 
 | 		ret = mlx4_SET_PORT_fcs_check(priv->mdev->dev, | 
 | 					      priv->port, ignore_fcs_value); | 
 | 		if (ret) | 
 | 			return ret; | 
 | 	} | 
 |  | 
 | 	if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_HW_VLAN_CTAG_RX)) { | 
 | 		en_info(priv, "Turn %s RX vlan strip offload\n", | 
 | 			(features & NETIF_F_HW_VLAN_CTAG_RX) ? "ON" : "OFF"); | 
 | 		reset = true; | 
 | 	} | 
 |  | 
 | 	if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_HW_VLAN_CTAG_TX)) | 
 | 		en_info(priv, "Turn %s TX vlan strip offload\n", | 
 | 			(features & NETIF_F_HW_VLAN_CTAG_TX) ? "ON" : "OFF"); | 
 |  | 
 | 	if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_LOOPBACK)) { | 
 | 		en_info(priv, "Turn %s loopback\n", | 
 | 			(features & NETIF_F_LOOPBACK) ? "ON" : "OFF"); | 
 | 		mlx4_en_update_loopback_state(netdev, features); | 
 | 	} | 
 |  | 
 | 	if (reset) { | 
 | 		ret = mlx4_en_reset_config(netdev, priv->hwtstamp_config, | 
 | 					   features); | 
 | 		if (ret) | 
 | 			return ret; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int mlx4_en_set_vf_mac(struct ether *dev, int queue, uint8_t *mac) | 
 | { | 
 | 	struct mlx4_en_priv *en_priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = en_priv->mdev; | 
 | 	uint64_t mac_u64 = mlx4_mac_to_u64(mac); | 
 |  | 
 | 	if (!is_valid_ether_addr(mac)) | 
 | 		return -EINVAL; | 
 |  | 
 | 	return mlx4_set_vf_mac(mdev->dev, en_priv->port, queue, mac_u64); | 
 | } | 
 |  | 
 | static int mlx4_en_set_vf_vlan(struct ether *dev, int vf, uint16_t vlan, | 
 | 			       uint8_t qos) | 
 | { | 
 | 	struct mlx4_en_priv *en_priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = en_priv->mdev; | 
 |  | 
 | 	return mlx4_set_vf_vlan(mdev->dev, en_priv->port, vf, vlan, qos); | 
 | } | 
 |  | 
 | static int mlx4_en_set_vf_rate(struct ether *dev, int vf, int min_tx_rate, | 
 | 			       int max_tx_rate) | 
 | { | 
 | 	struct mlx4_en_priv *en_priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = en_priv->mdev; | 
 |  | 
 | 	return mlx4_set_vf_rate(mdev->dev, en_priv->port, vf, min_tx_rate, | 
 | 				max_tx_rate); | 
 | } | 
 |  | 
 | static int mlx4_en_set_vf_spoofchk(struct ether *dev, int vf, bool setting) | 
 | { | 
 | 	struct mlx4_en_priv *en_priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = en_priv->mdev; | 
 |  | 
 | 	return mlx4_set_vf_spoofchk(mdev->dev, en_priv->port, vf, setting); | 
 | } | 
 |  | 
 | static int mlx4_en_get_vf_config(struct ether *dev, int vf, | 
 | 				 struct ifla_vf_info *ivf) | 
 | { | 
 | 	struct mlx4_en_priv *en_priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = en_priv->mdev; | 
 |  | 
 | 	return mlx4_get_vf_config(mdev->dev, en_priv->port, vf, ivf); | 
 | } | 
 |  | 
 | static int mlx4_en_set_vf_link_state(struct ether *dev, int vf, | 
 | 				     int link_state) | 
 | { | 
 | 	struct mlx4_en_priv *en_priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = en_priv->mdev; | 
 |  | 
 | 	return mlx4_set_vf_link_state(mdev->dev, en_priv->port, vf, link_state); | 
 | } | 
 |  | 
 | #define PORT_ID_BYTE_LEN 8 | 
 | static int mlx4_en_get_phys_port_id(struct ether *dev, | 
 | 				    struct netdev_phys_item_id *ppid) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_dev *mdev = priv->mdev->dev; | 
 | 	int i; | 
 | 	uint64_t phys_port_id = mdev->caps.phys_port_id[priv->port]; | 
 |  | 
 | 	if (!phys_port_id) | 
 | 		return -EOPNOTSUPP; | 
 |  | 
 | 	ppid->id_len = sizeof(phys_port_id); | 
 | 	for (i = PORT_ID_BYTE_LEN - 1; i >= 0; --i) { | 
 | 		ppid->id[i] =  phys_port_id & 0xff; | 
 | 		phys_port_id >>= 8; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | #ifdef CONFIG_MLX4_EN_VXLAN | 
 | static void mlx4_en_add_vxlan_offloads(struct work_struct *work) | 
 | { | 
 | 	int ret; | 
 | 	struct mlx4_en_priv *priv = container_of(work, struct mlx4_en_priv, | 
 | 						 vxlan_add_task); | 
 |  | 
 | 	ret = mlx4_config_vxlan_port(priv->mdev->dev, priv->vxlan_port); | 
 | 	if (ret) | 
 | 		goto out; | 
 |  | 
 | 	ret = mlx4_SET_PORT_VXLAN(priv->mdev->dev, priv->port, | 
 | 				  VXLAN_STEER_BY_OUTER_MAC, 1); | 
 | out: | 
 | 	if (ret) { | 
 | 		en_err(priv, "failed setting L2 tunnel configuration ret %d\n", ret); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* set offloads */ | 
 | 	priv->dev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM | | 
 | 				      NETIF_F_TSO | NETIF_F_GSO_UDP_TUNNEL; | 
 | 	priv->dev->hw_features |= NETIF_F_GSO_UDP_TUNNEL; | 
 | 	priv->dev->feat    |= NETIF_F_GSO_UDP_TUNNEL; | 
 | } | 
 |  | 
 | static void mlx4_en_del_vxlan_offloads(struct work_struct *work) | 
 | { | 
 | 	int ret; | 
 | 	struct mlx4_en_priv *priv = container_of(work, struct mlx4_en_priv, | 
 | 						 vxlan_del_task); | 
 | 	/* unset offloads */ | 
 | 	priv->dev->hw_enc_features &= ~(NETIF_F_IP_CSUM | NETIF_F_RXCSUM | | 
 | 				      NETIF_F_TSO | NETIF_F_GSO_UDP_TUNNEL); | 
 | 	priv->dev->hw_features &= ~NETIF_F_GSO_UDP_TUNNEL; | 
 | 	priv->dev->feat    &= ~NETIF_F_GSO_UDP_TUNNEL; | 
 |  | 
 | 	ret = mlx4_SET_PORT_VXLAN(priv->mdev->dev, priv->port, | 
 | 				  VXLAN_STEER_BY_OUTER_MAC, 0); | 
 | 	if (ret) | 
 | 		en_err(priv, "failed setting L2 tunnel configuration ret %d\n", ret); | 
 |  | 
 | 	priv->vxlan_port = 0; | 
 | } | 
 |  | 
 | static void mlx4_en_add_vxlan_port(struct  ether *dev, | 
 | 				   sa_family_t sa_family, __be16 port) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	__be16 current_port; | 
 |  | 
 | 	if (priv->mdev->dev->caps.tunnel_offload_mode != MLX4_TUNNEL_OFFLOAD_MODE_VXLAN) | 
 | 		return; | 
 |  | 
 | 	if (sa_family == AF_INET6) | 
 | 		return; | 
 |  | 
 | 	current_port = priv->vxlan_port; | 
 | 	if (current_port && current_port != port) { | 
 | 		en_warn(priv, "vxlan port %d configured, can't add port %d\n", | 
 | 			be16_to_cpu(current_port), be16_to_cpu(port)); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	priv->vxlan_port = port; | 
 | 	queue_work(priv->mdev->workqueue, &priv->vxlan_add_task); | 
 | } | 
 |  | 
 | static void mlx4_en_del_vxlan_port(struct  ether *dev, | 
 | 				   sa_family_t sa_family, __be16 port) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	__be16 current_port; | 
 |  | 
 | 	if (priv->mdev->dev->caps.tunnel_offload_mode != MLX4_TUNNEL_OFFLOAD_MODE_VXLAN) | 
 | 		return; | 
 |  | 
 | 	if (sa_family == AF_INET6) | 
 | 		return; | 
 |  | 
 | 	current_port = priv->vxlan_port; | 
 | 	if (current_port != port) { | 
 | 		en_dbg(DRV, priv, "vxlan port %d isn't configured, ignoring\n", | 
 | 		       be16_to_cpu(port)); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	queue_work(priv->mdev->workqueue, &priv->vxlan_del_task); | 
 | } | 
 |  | 
 | static netdev_features_t mlx4_en_features_check(struct sk_buff *skb, | 
 | 						struct ether *dev, | 
 | 						netdev_features_t features) | 
 | { | 
 | 	features = vlan_features_check(skb, features); | 
 | 	return vxlan_features_check(skb, features); | 
 | } | 
 | #endif | 
 |  | 
 | static int mlx4_en_set_tx_maxrate(struct ether *dev, int queue_index, | 
 | 				  uint32_t maxrate) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_tx_ring *tx_ring = priv->tx_ring[queue_index]; | 
 | 	struct mlx4_update_qp_params params; | 
 | 	int err; | 
 |  | 
 | 	if (!(priv->mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_QP_RATE_LIMIT)) | 
 | 		return -EOPNOTSUPP; | 
 |  | 
 | 	/* rate provided to us in Mbs, check if it fits into 12 bits, if not use Gbs */ | 
 | 	if (maxrate >> 12) { | 
 | 		params.rate_unit = MLX4_QP_RATE_LIMIT_GBS; | 
 | 		params.rate_val  = maxrate / 1000; | 
 | 	} else if (maxrate) { | 
 | 		params.rate_unit = MLX4_QP_RATE_LIMIT_MBS; | 
 | 		params.rate_val  = maxrate; | 
 | 	} else { /* zero serves to revoke the QP rate-limitation */ | 
 | 		params.rate_unit = 0; | 
 | 		params.rate_val  = 0; | 
 | 	} | 
 |  | 
 | 	err = mlx4_update_qp(priv->mdev->dev, tx_ring->qpn, MLX4_UPDATE_QP_RATE_LIMIT, | 
 | 			     ¶ms); | 
 | 	return err; | 
 | } | 
 |  | 
 | static const struct net_device_ops mlx4_netdev_ops = { | 
 | 	.ndo_open		= mlx4_en_open, | 
 | 	.ndo_stop		= mlx4_en_close, | 
 | 	.ndo_start_xmit		= mlx4_en_xmit, | 
 | 	.ndo_select_queue	= mlx4_en_select_queue, | 
 | 	.ndo_get_stats		= mlx4_en_get_stats, | 
 | 	.ndo_set_rx_mode	= mlx4_en_set_rx_mode, | 
 | 	.ndo_set_mac_address	= mlx4_en_set_mac, | 
 | 	.ndo_validate_addr	= eth_validate_addr, | 
 | 	.ndo_change_mtu		= mlx4_en_change_mtu, | 
 | 	.ndo_do_ioctl		= mlx4_en_ioctl, | 
 | 	.ndo_tx_timeout		= mlx4_en_tx_timeout, | 
 | 	.ndo_vlan_rx_add_vid	= mlx4_en_vlan_rx_add_vid, | 
 | 	.ndo_vlan_rx_kill_vid	= mlx4_en_vlan_rx_kill_vid, | 
 | #ifdef CONFIG_NET_POLL_CONTROLLER | 
 | 	.ndo_poll_controller	= mlx4_en_netpoll, | 
 | #endif | 
 | 	.ndo_set_features	= mlx4_en_set_features, | 
 | 	.ndo_setup_tc		= mlx4_en_setup_tc, | 
 | #ifdef CONFIG_RFS_ACCEL | 
 | 	.ndo_rx_flow_steer	= mlx4_en_filter_rfs, | 
 | #endif | 
 | #ifdef CONFIG_NET_RX_BUSY_POLL | 
 | 	.ndo_busy_poll		= mlx4_en_low_latency_recv, | 
 | #endif | 
 | 	.ndo_get_phys_port_id	= mlx4_en_get_phys_port_id, | 
 | #ifdef CONFIG_MLX4_EN_VXLAN | 
 | 	.ndo_add_vxlan_port	= mlx4_en_add_vxlan_port, | 
 | 	.ndo_del_vxlan_port	= mlx4_en_del_vxlan_port, | 
 | 	.ndo_features_check	= mlx4_en_features_check, | 
 | #endif | 
 | 	.ndo_set_tx_maxrate	= mlx4_en_set_tx_maxrate, | 
 | }; | 
 |  | 
 | static const struct net_device_ops mlx4_netdev_ops_master = { | 
 | 	.ndo_open		= mlx4_en_open, | 
 | 	.ndo_stop		= mlx4_en_close, | 
 | 	.ndo_start_xmit		= mlx4_en_xmit, | 
 | 	.ndo_select_queue	= mlx4_en_select_queue, | 
 | 	.ndo_get_stats		= mlx4_en_get_stats, | 
 | 	.ndo_set_rx_mode	= mlx4_en_set_rx_mode, | 
 | 	.ndo_set_mac_address	= mlx4_en_set_mac, | 
 | 	.ndo_validate_addr	= eth_validate_addr, | 
 | 	.ndo_change_mtu		= mlx4_en_change_mtu, | 
 | 	.ndo_tx_timeout		= mlx4_en_tx_timeout, | 
 | 	.ndo_vlan_rx_add_vid	= mlx4_en_vlan_rx_add_vid, | 
 | 	.ndo_vlan_rx_kill_vid	= mlx4_en_vlan_rx_kill_vid, | 
 | 	.ndo_set_vf_mac		= mlx4_en_set_vf_mac, | 
 | 	.ndo_set_vf_vlan	= mlx4_en_set_vf_vlan, | 
 | 	.ndo_set_vf_rate	= mlx4_en_set_vf_rate, | 
 | 	.ndo_set_vf_spoofchk	= mlx4_en_set_vf_spoofchk, | 
 | 	.ndo_set_vf_link_state	= mlx4_en_set_vf_link_state, | 
 | 	.ndo_get_vf_config	= mlx4_en_get_vf_config, | 
 | #ifdef CONFIG_NET_POLL_CONTROLLER | 
 | 	.ndo_poll_controller	= mlx4_en_netpoll, | 
 | #endif | 
 | 	.ndo_set_features	= mlx4_en_set_features, | 
 | 	.ndo_setup_tc		= mlx4_en_setup_tc, | 
 | #ifdef CONFIG_RFS_ACCEL | 
 | 	.ndo_rx_flow_steer	= mlx4_en_filter_rfs, | 
 | #endif | 
 | 	.ndo_get_phys_port_id	= mlx4_en_get_phys_port_id, | 
 | #ifdef CONFIG_MLX4_EN_VXLAN | 
 | 	.ndo_add_vxlan_port	= mlx4_en_add_vxlan_port, | 
 | 	.ndo_del_vxlan_port	= mlx4_en_del_vxlan_port, | 
 | 	.ndo_features_check	= mlx4_en_features_check, | 
 | #endif | 
 | 	.ndo_set_tx_maxrate	= mlx4_en_set_tx_maxrate, | 
 | }; | 
 | #endif | 
 |  | 
 | struct mlx4_en_bond { | 
 | 	struct work_struct work; | 
 | 	struct mlx4_en_priv *priv; | 
 | 	int is_bonded; | 
 | 	struct mlx4_port_map port_map; | 
 | }; | 
 |  | 
 | static void mlx4_en_bond_work(struct work_struct *work) | 
 | { | 
 | 	panic("Disabled"); | 
 | #if 0 // AKAROS_PORT | 
 | 	struct mlx4_en_bond *bond = container_of(work, | 
 | 						     struct mlx4_en_bond, | 
 | 						     work); | 
 | 	int err = 0; | 
 | 	struct mlx4_dev *dev = bond->priv->mdev->dev; | 
 |  | 
 | 	if (bond->is_bonded) { | 
 | 		if (!mlx4_is_bonded(dev)) { | 
 | 			err = mlx4_bond(dev); | 
 | 			if (err) | 
 | 				en_err(bond->priv, "Fail to bond device\n"); | 
 | 		} | 
 | 		if (!err) { | 
 | 			err = mlx4_port_map_set(dev, &bond->port_map); | 
 | 			if (err) | 
 | 				en_err(bond->priv, "Fail to set port map [%d][%d]: %d\n", | 
 | 				       bond->port_map.port1, | 
 | 				       bond->port_map.port2, | 
 | 				       err); | 
 | 		} | 
 | 	} else if (mlx4_is_bonded(dev)) { | 
 | 		err = mlx4_unbond(dev); | 
 | 		if (err) | 
 | 			en_err(bond->priv, "Fail to unbond device\n"); | 
 | 	} | 
 | 	dev_put(bond->priv->dev); | 
 | 	kfree(bond); | 
 | #endif | 
 | } | 
 |  | 
 | static int mlx4_en_queue_bond_work(struct mlx4_en_priv *priv, int is_bonded, | 
 | 				   uint8_t v2p_p1, uint8_t v2p_p2) | 
 | { | 
 | 	panic("Disabled"); | 
 | #if 0 // AKAROS_PORT | 
 | 	struct mlx4_en_bond *bond = NULL; | 
 |  | 
 | 	bond = kzmalloc(sizeof(*bond), 0); | 
 | 	if (!bond) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	INIT_WORK(&bond->work, mlx4_en_bond_work); | 
 | 	bond->priv = priv; | 
 | 	bond->is_bonded = is_bonded; | 
 | 	bond->port_map.port1 = v2p_p1; | 
 | 	bond->port_map.port2 = v2p_p2; | 
 | 	dev_hold(priv->dev); | 
 | 	queue_work(priv->mdev->workqueue, &bond->work); | 
 | 	return 0; | 
 | #endif | 
 | } | 
 |  | 
 | int mlx4_en_netdev_event(struct notifier_block *this, | 
 | 			 unsigned long event, void *ptr) | 
 | { | 
 | 	panic("Disabled"); | 
 | #if 0 // AKAROS_PORT | 
 | 	struct ether *ndev = netdev_notifier_info_to_dev(ptr); | 
 | 	uint8_t port = 0; | 
 | 	struct mlx4_en_dev *mdev; | 
 | 	struct mlx4_dev *dev; | 
 | 	int i, num_eth_ports = 0; | 
 | 	bool do_bond = true; | 
 | 	struct mlx4_en_priv *priv; | 
 | 	uint8_t v2p_port1 = 0; | 
 | 	uint8_t v2p_port2 = 0; | 
 |  | 
 | 	if (!net_eq(dev_net(ndev), &init_net)) | 
 | 		return NOTIFY_DONE; | 
 |  | 
 | 	mdev = container_of(this, struct mlx4_en_dev, nb); | 
 | 	dev = mdev->dev; | 
 |  | 
 | 	/* Go into this mode only when two network devices set on two ports | 
 | 	 * of the same mlx4 device are slaves of the same bonding master | 
 | 	 */ | 
 | 	mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH) { | 
 | 		++num_eth_ports; | 
 | 		if (!port && (mdev->pndev[i] == ndev)) | 
 | 			port = i; | 
 | 		mdev->upper[i] = mdev->pndev[i] ? | 
 | 			netdev_master_upper_dev_get(mdev->pndev[i]) : NULL; | 
 | 		/* condition not met: network device is a slave */ | 
 | 		if (!mdev->upper[i]) | 
 | 			do_bond = false; | 
 | 		if (num_eth_ports < 2) | 
 | 			continue; | 
 | 		/* condition not met: same master */ | 
 | 		if (mdev->upper[i] != mdev->upper[i-1]) | 
 | 			do_bond = false; | 
 | 	} | 
 | 	/* condition not met: 2 salves */ | 
 | 	do_bond = (num_eth_ports ==  2) ? do_bond : false; | 
 |  | 
 | 	/* handle only events that come with enough info */ | 
 | 	if ((do_bond && (event != NETDEV_BONDING_INFO)) || !port) | 
 | 		return NOTIFY_DONE; | 
 |  | 
 | 	priv = netdev_priv(ndev); | 
 | 	if (do_bond) { | 
 | 		struct netdev_notifier_bonding_info *notifier_info = ptr; | 
 | 		struct netdev_bonding_info *bonding_info = | 
 | 			¬ifier_info->bonding_info; | 
 |  | 
 | 		/* required mode 1, 2 or 4 */ | 
 | 		if ((bonding_info->master.bond_mode != BOND_MODE_ACTIVEBACKUP) && | 
 | 		    (bonding_info->master.bond_mode != BOND_MODE_XOR) && | 
 | 		    (bonding_info->master.bond_mode != BOND_MODE_8023AD)) | 
 | 			do_bond = false; | 
 |  | 
 | 		/* require exactly 2 slaves */ | 
 | 		if (bonding_info->master.num_slaves != 2) | 
 | 			do_bond = false; | 
 |  | 
 | 		/* calc v2p */ | 
 | 		if (do_bond) { | 
 | 			if (bonding_info->master.bond_mode == | 
 | 			    BOND_MODE_ACTIVEBACKUP) { | 
 | 				/* in active-backup mode virtual ports are | 
 | 				 * mapped to the physical port of the active | 
 | 				 * slave */ | 
 | 				if (bonding_info->slave.state == | 
 | 				    BOND_STATE_BACKUP) { | 
 | 					if (port == 1) { | 
 | 						v2p_port1 = 2; | 
 | 						v2p_port2 = 2; | 
 | 					} else { | 
 | 						v2p_port1 = 1; | 
 | 						v2p_port2 = 1; | 
 | 					} | 
 | 				} else { /* BOND_STATE_ACTIVE */ | 
 | 					if (port == 1) { | 
 | 						v2p_port1 = 1; | 
 | 						v2p_port2 = 1; | 
 | 					} else { | 
 | 						v2p_port1 = 2; | 
 | 						v2p_port2 = 2; | 
 | 					} | 
 | 				} | 
 | 			} else { /* Active-Active */ | 
 | 				/* in active-active mode a virtual port is | 
 | 				 * mapped to the native physical port if and only | 
 | 				 * if the physical port is up */ | 
 | 				__s8 link = bonding_info->slave.link; | 
 |  | 
 | 				if (port == 1) | 
 | 					v2p_port2 = 2; | 
 | 				else | 
 | 					v2p_port1 = 1; | 
 | 				if ((link == BOND_LINK_UP) || | 
 | 				    (link == BOND_LINK_FAIL)) { | 
 | 					if (port == 1) | 
 | 						v2p_port1 = 1; | 
 | 					else | 
 | 						v2p_port2 = 2; | 
 | 				} else { /* BOND_LINK_DOWN || BOND_LINK_BACK */ | 
 | 					if (port == 1) | 
 | 						v2p_port1 = 2; | 
 | 					else | 
 | 						v2p_port2 = 1; | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	mlx4_en_queue_bond_work(priv, do_bond, | 
 | 				v2p_port1, v2p_port2); | 
 | #endif | 
 |  | 
 | 	return NOTIFY_DONE; | 
 | } | 
 |  | 
 | void mlx4_en_update_pfc_stats_bitmap(struct mlx4_dev *dev, | 
 | 				     struct mlx4_en_stats_bitmap *stats_bitmap, | 
 | 				     uint8_t rx_ppp, uint8_t rx_pause, | 
 | 				     uint8_t tx_ppp, uint8_t tx_pause) | 
 | { | 
 | 	int last_i = NUM_MAIN_STATS + NUM_PORT_STATS; | 
 |  | 
 | 	if (!mlx4_is_slave(dev) && | 
 | 	    (dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN)) { | 
 | 		qlock(&stats_bitmap->mutex); | 
 | 		bitmap_clear(stats_bitmap->bitmap, last_i, NUM_FLOW_STATS); | 
 |  | 
 | 		if (rx_ppp) | 
 | 			bitmap_set(stats_bitmap->bitmap, last_i, | 
 | 				   NUM_FLOW_PRIORITY_STATS_RX); | 
 | 		last_i += NUM_FLOW_PRIORITY_STATS_RX; | 
 |  | 
 | 		if (rx_pause && !(rx_ppp)) | 
 | 			bitmap_set(stats_bitmap->bitmap, last_i, | 
 | 				   NUM_FLOW_STATS_RX); | 
 | 		last_i += NUM_FLOW_STATS_RX; | 
 |  | 
 | 		if (tx_ppp) | 
 | 			bitmap_set(stats_bitmap->bitmap, last_i, | 
 | 				   NUM_FLOW_PRIORITY_STATS_TX); | 
 | 		last_i += NUM_FLOW_PRIORITY_STATS_TX; | 
 |  | 
 | 		if (tx_pause && !(tx_ppp)) | 
 | 			bitmap_set(stats_bitmap->bitmap, last_i, | 
 | 				   NUM_FLOW_STATS_TX); | 
 | 		last_i += NUM_FLOW_STATS_TX; | 
 |  | 
 | 		qunlock(&stats_bitmap->mutex); | 
 | 	} | 
 | } | 
 |  | 
 | void mlx4_en_set_stats_bitmap(struct mlx4_dev *dev, | 
 | 			      struct mlx4_en_stats_bitmap *stats_bitmap, | 
 | 			      uint8_t rx_ppp, uint8_t rx_pause, | 
 | 			      uint8_t tx_ppp, uint8_t tx_pause) | 
 | { | 
 | 	panic("Disabled"); | 
 | #if 0 // AKAROS_PORT | 
 | 	int last_i = 0; | 
 |  | 
 | 	qlock_init(&stats_bitmap->mutex); | 
 | 	bitmap_zero(stats_bitmap->bitmap, NUM_ALL_STATS); | 
 |  | 
 | 	if (mlx4_is_slave(dev)) { | 
 | 		bitmap_set(stats_bitmap->bitmap, last_i + | 
 | 					 MLX4_FIND_NETDEV_STAT(rx_packets), 1); | 
 | 		bitmap_set(stats_bitmap->bitmap, last_i + | 
 | 					 MLX4_FIND_NETDEV_STAT(tx_packets), 1); | 
 | 		bitmap_set(stats_bitmap->bitmap, last_i + | 
 | 					 MLX4_FIND_NETDEV_STAT(rx_bytes), 1); | 
 | 		bitmap_set(stats_bitmap->bitmap, last_i + | 
 | 					 MLX4_FIND_NETDEV_STAT(tx_bytes), 1); | 
 | 		bitmap_set(stats_bitmap->bitmap, last_i + | 
 | 					 MLX4_FIND_NETDEV_STAT(rx_dropped), 1); | 
 | 		bitmap_set(stats_bitmap->bitmap, last_i + | 
 | 					 MLX4_FIND_NETDEV_STAT(tx_dropped), 1); | 
 | 	} else { | 
 | 		bitmap_set(stats_bitmap->bitmap, last_i, NUM_MAIN_STATS); | 
 | 	} | 
 | 	last_i += NUM_MAIN_STATS; | 
 |  | 
 | 	bitmap_set(stats_bitmap->bitmap, last_i, NUM_PORT_STATS); | 
 | 	last_i += NUM_PORT_STATS; | 
 |  | 
 | 	mlx4_en_update_pfc_stats_bitmap(dev, stats_bitmap, | 
 | 					rx_ppp, rx_pause, | 
 | 					tx_ppp, tx_pause); | 
 | 	last_i += NUM_FLOW_STATS; | 
 |  | 
 | 	if (!mlx4_is_slave(dev)) | 
 | 		bitmap_set(stats_bitmap->bitmap, last_i, NUM_PKT_STATS); | 
 | #endif | 
 | } | 
 |  | 
 | int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, | 
 | 			struct ether *dev, | 
 | 			struct mlx4_en_port_profile *prof) | 
 | { | 
 | 	struct mlx4_en_priv *priv; | 
 | 	int i; | 
 | 	int err; | 
 | 	uint64_t mac_u64; | 
 |  | 
 | #if 0 // AKAROS_PORT | 
 | 	dev = alloc_etherdev_mqs(sizeof(struct mlx4_en_priv), | 
 | 				 MAX_TX_RINGS, MAX_RX_RINGS); | 
 | 	if (dev == NULL) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	netif_set_real_num_tx_queues(dev, prof->tx_ring_num); | 
 | 	netif_set_real_num_rx_queues(dev, prof->rx_ring_num); | 
 |  | 
 | 	SET_NETDEV_DEV(dev, &mdev->dev->persist->pdev->linux_dev); | 
 | #else | 
 | 	dev->ctlr = kzmalloc(sizeof(struct mlx4_en_priv), MEM_WAIT); | 
 | #endif | 
 |  | 
 | 	/* | 
 | 	 * Initialize driver private data | 
 | 	 */ | 
 |  | 
 | 	priv = netdev_priv(dev); | 
 | 	memset(priv, 0, sizeof(struct mlx4_en_priv)); | 
 | 	spinlock_init(&priv->stats_lock); | 
 | 	INIT_WORK(&priv->rx_mode_task, mlx4_en_do_set_rx_mode); | 
 | 	INIT_WORK(&priv->watchdog_task, mlx4_en_restart); | 
 | 	INIT_WORK(&priv->linkstate_task, mlx4_en_linkstate); | 
 | 	INIT_DELAYED_WORK(&priv->stats_task, mlx4_en_do_get_stats); | 
 | 	INIT_DELAYED_WORK(&priv->service_task, mlx4_en_service_task); | 
 | #ifdef CONFIG_MLX4_EN_VXLAN | 
 | 	INIT_WORK(&priv->vxlan_add_task, mlx4_en_add_vxlan_offloads); | 
 | 	INIT_WORK(&priv->vxlan_del_task, mlx4_en_del_vxlan_offloads); | 
 | #endif | 
 | #ifdef CONFIG_RFS_ACCEL | 
 | 	INIT_LIST_HEAD(&priv->filters); | 
 | 	spinlock_init(&priv->filters_lock); | 
 | #endif | 
 |  | 
 | 	priv->dev = dev; | 
 | 	priv->mdev = mdev; | 
 | 	priv->ddev = &mdev->pdev->linux_dev; | 
 | 	priv->prof = prof; | 
 | 	priv->port = port; | 
 | 	priv->port_up = false; | 
 | 	priv->flags = prof->flags; | 
 | 	priv->pflags = MLX4_EN_PRIV_FLAGS_BLUEFLAME; | 
 | 	priv->ctrl_flags = cpu_to_be32(MLX4_WQE_CTRL_CQ_UPDATE | | 
 | 			MLX4_WQE_CTRL_SOLICITED); | 
 | 	priv->num_tx_rings_p_up = mdev->profile.num_tx_rings_p_up; | 
 | 	priv->tx_ring_num = prof->tx_ring_num; | 
 | 	priv->tx_work_limit = MLX4_EN_DEFAULT_TX_WORK; | 
 | 	netdev_rss_key_fill(priv->rss_key, sizeof(priv->rss_key)); | 
 |  | 
 | 	priv->tx_ring = kzmalloc(sizeof(struct mlx4_en_tx_ring *) * MAX_TX_RINGS, | 
 | 				 MEM_WAIT); | 
 | 	if (!priv->tx_ring) { | 
 | 		err = -ENOMEM; | 
 | 		goto out; | 
 | 	} | 
 | 	priv->tx_cq = kzmalloc(sizeof(struct mlx4_en_cq *) * MAX_TX_RINGS, | 
 | 			       MEM_WAIT); | 
 | 	if (!priv->tx_cq) { | 
 | 		err = -ENOMEM; | 
 | 		goto out; | 
 | 	} | 
 | 	priv->rx_ring_num = prof->rx_ring_num; | 
 | 	priv->cqe_factor = (mdev->dev->caps.cqe_size == 64) ? 1 : 0; | 
 | 	priv->cqe_size = mdev->dev->caps.cqe_size; | 
 | 	priv->mac_index = -1; | 
 | 	priv->msg_enable = MLX4_EN_MSG_LEVEL; | 
 | #ifdef CONFIG_MLX4_EN_DCB | 
 | 	if (!mlx4_is_slave(priv->mdev->dev)) { | 
 | 		if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETS_CFG) { | 
 | 			dev->dcbnl_ops = &mlx4_en_dcbnl_ops; | 
 | 		} else { | 
 | 			en_info(priv, "enabling only PFC DCB ops\n"); | 
 | 			dev->dcbnl_ops = &mlx4_en_dcbnl_pfc_ops; | 
 | 		} | 
 | 	} | 
 | #endif | 
 |  | 
 | 	for (i = 0; i < MLX4_EN_MAC_HASH_SIZE; ++i) | 
 | 		INIT_HLIST_HEAD(&priv->mac_hash[i]); | 
 |  | 
 | 	/* Query for default mac and max mtu */ | 
 | 	priv->max_mtu = mdev->dev->caps.eth_mtu_cap[priv->port]; | 
 |  | 
 | 	if (mdev->dev->caps.rx_checksum_flags_port[priv->port] & | 
 | 	    MLX4_RX_CSUM_MODE_VAL_NON_TCP_UDP) | 
 | 		priv->flags |= MLX4_EN_FLAG_RX_CSUM_NON_TCP_UDP; | 
 |  | 
 | 	/* Set default MAC */ | 
 | 	mlx4_en_u64_to_mac(dev->ea, mdev->dev->caps.def_mac[priv->port]); | 
 | 	if (!is_valid_ether_addr(dev->ea)) { | 
 | 		if (mlx4_is_slave(priv->mdev->dev)) { | 
 | 			eth_hw_addr_random(dev); | 
 | 			en_warn(priv, "Assigned random MAC address %pM\n", | 
 | 				dev->ea); | 
 | 			mac_u64 = mlx4_mac_to_u64(dev->ea); | 
 | 			mdev->dev->caps.def_mac[priv->port] = mac_u64; | 
 | 		} else { | 
 | 			en_err(priv, "Port: %d, invalid mac burned: %pM, quiting\n", | 
 | 			       priv->port, dev->ea); | 
 | 			err = -EINVAL; | 
 | 			goto out; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	memcpy(priv->current_mac, dev->ea, sizeof(priv->current_mac)); | 
 |  | 
 | 	priv->stride = ROUNDUPPWR2(sizeof(struct mlx4_en_rx_desc) + DS_SIZE * MLX4_EN_MAX_RX_FRAGS); | 
 | 	err = mlx4_en_alloc_resources(priv); | 
 | 	if (err) | 
 | 		goto out; | 
 |  | 
 | 	/* Initialize time stamping config */ | 
 | 	priv->hwtstamp_config.flags = 0; | 
 | 	priv->hwtstamp_config.tx_type = HWTSTAMP_TX_OFF; | 
 | 	priv->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; | 
 |  | 
 | 	/* Allocate page for receive rings */ | 
 | 	err = mlx4_alloc_hwq_res(mdev->dev, &priv->res, | 
 | 				MLX4_EN_PAGE_SIZE, MLX4_EN_PAGE_SIZE); | 
 | 	if (err) { | 
 | 		en_err(priv, "Failed to allocate page for rx qps\n"); | 
 | 		goto out; | 
 | 	} | 
 | 	priv->allocated = 1; | 
 |  | 
 | #if 0 // AKAROS_PORT | 
 | 	/* | 
 | 	 * Initialize netdev entry points | 
 | 	 */ | 
 | 	if (mlx4_is_master(priv->mdev->dev)) | 
 | 		{} | 
 | 		else | 
 | 			{} | 
 | 	netif_set_real_num_tx_queues(dev, priv->tx_ring_num); | 
 | 	netif_set_real_num_rx_queues(dev, priv->rx_ring_num); | 
 | #endif | 
 |  | 
 | 	/* | 
 | 	 * Set driver features | 
 | 	 */ | 
 | 	dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; | 
 | 	if (mdev->LSO_support) | 
 | 		dev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6; | 
 |  | 
 | 	dev->hw_features |= NETIF_F_RXCSUM | NETIF_F_RXHASH; | 
 | #if 0 // AKAROS_PORT | 
 | 	dev->feat = dev->hw_features | NETIF_F_HIGHDMA | | 
 | 			NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | | 
 | 			NETIF_F_HW_VLAN_CTAG_FILTER; | 
 | #else | 
 | 	dev->feat = dev->hw_features | NETIF_F_SG | NETIF_F_IP_CSUM | NETF_PADMIN; | 
 | #endif | 
 | 	dev->hw_features |= NETIF_F_LOOPBACK | | 
 | 			NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; | 
 |  | 
 | 	if (mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_FCS_KEEP) | 
 | 		dev->hw_features |= NETIF_F_RXFCS; | 
 |  | 
 | 	if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_IGNORE_FCS) | 
 | 		dev->hw_features |= NETIF_F_RXALL; | 
 |  | 
 | 	if (mdev->dev->caps.steering_mode == | 
 | 	    MLX4_STEERING_MODE_DEVICE_MANAGED && | 
 | 	    mdev->dev->caps.dmfs_high_steer_mode != MLX4_STEERING_DMFS_A0_STATIC) | 
 | 		dev->hw_features |= NETIF_F_NTUPLE; | 
 |  | 
 | #if 0 // AKAROS_PORT | 
 | 	if (mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_A0) | 
 | 		dev->priv_flags |= IFF_UNICAST_FLT; | 
 | #endif | 
 |  | 
 | 	/* Setting a default hash function value */ | 
 | 	if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_RSS_TOP) { | 
 | 		priv->rss_hash_fn = ETH_RSS_HASH_TOP; | 
 | 	} else if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_RSS_XOR) { | 
 | 		priv->rss_hash_fn = ETH_RSS_HASH_XOR; | 
 | 	} else { | 
 | 		en_warn(priv, | 
 | 			"No RSS hash capabilities exposed, using Toeplitz\n"); | 
 | 		priv->rss_hash_fn = ETH_RSS_HASH_TOP; | 
 | 	} | 
 |  | 
 | 	mdev->pndev[port] = dev; | 
 | 	mdev->upper[port] = NULL; | 
 |  | 
 | 	netif_carrier_off(dev); | 
 | 	mlx4_en_set_default_moderation(priv); | 
 |  | 
 | 	en_warn(priv, "Using %d TX rings\n", prof->tx_ring_num); | 
 | 	en_warn(priv, "Using %d RX rings\n", prof->rx_ring_num); | 
 |  | 
 | 	mlx4_en_update_loopback_state(priv->dev, priv->dev->feat); | 
 |  | 
 | 	/* Configure port */ | 
 | 	mlx4_en_calc_rx_buf(dev); | 
 | 	err = mlx4_SET_PORT_general(mdev->dev, priv->port, | 
 | 				    priv->rx_skb_size + ETH_FCS_LEN, | 
 | 				    prof->tx_pause, prof->tx_ppp, | 
 | 				    prof->rx_pause, prof->rx_ppp); | 
 | 	if (err) { | 
 | 		en_err(priv, "Failed setting port general configurations for port %d, with error %d\n", | 
 | 		       priv->port, err); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	if (mdev->dev->caps.tunnel_offload_mode == MLX4_TUNNEL_OFFLOAD_MODE_VXLAN) { | 
 | 		err = mlx4_SET_PORT_VXLAN(mdev->dev, priv->port, VXLAN_STEER_BY_OUTER_MAC, 1); | 
 | 		if (err) { | 
 | 			en_err(priv, "Failed setting port L2 tunnel configuration, err %d\n", | 
 | 			       err); | 
 | 			goto out; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* Init port */ | 
 | 	en_warn(priv, "Initializing port\n"); | 
 | 	err = mlx4_INIT_PORT(mdev->dev, priv->port); | 
 | 	if (err) { | 
 | 		en_err(priv, "Failed Initializing port\n"); | 
 | 		goto out; | 
 | 	} | 
 | 	queue_delayed_work(mdev->workqueue, &priv->stats_task, STATS_DELAY); | 
 |  | 
 | 	if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS) | 
 | 		queue_delayed_work(mdev->workqueue, &priv->service_task, | 
 | 				   SERVICE_TASK_DELAY); | 
 |  | 
 | #if 0 // AKAROS_PORT | 
 | 	mlx4_en_set_stats_bitmap(mdev->dev, &priv->stats_bitmap, | 
 | 				 mdev->profile.prof[priv->port].rx_ppp, | 
 | 				 mdev->profile.prof[priv->port].rx_pause, | 
 | 				 mdev->profile.prof[priv->port].tx_ppp, | 
 | 				 mdev->profile.prof[priv->port].tx_pause); | 
 |  | 
 | 	err = register_netdev(dev); | 
 | 	if (err) { | 
 | 		en_err(priv, "Netdev registration failed for port %d\n", port); | 
 | 		goto out; | 
 | 	} | 
 | #endif | 
 |  | 
 | 	priv->registered = 1; | 
 |  | 
 | 	return 0; | 
 |  | 
 | out: | 
 | 	mlx4_en_destroy_netdev(dev); | 
 | 	return err; | 
 | } | 
 |  | 
 | int mlx4_en_reset_config(struct ether *dev, | 
 | 			 struct hwtstamp_config ts_config, | 
 | 			 netdev_features_t features) | 
 | { | 
 | 	struct mlx4_en_priv *priv = netdev_priv(dev); | 
 | 	struct mlx4_en_dev *mdev = priv->mdev; | 
 | 	int port_up = 0; | 
 | 	int err = 0; | 
 |  | 
 | 	if (priv->hwtstamp_config.tx_type == ts_config.tx_type && | 
 | 	    priv->hwtstamp_config.rx_filter == ts_config.rx_filter && | 
 | 	    !DEV_FEATURE_CHANGED(dev, features, NETIF_F_HW_VLAN_CTAG_RX) && | 
 | 	    !DEV_FEATURE_CHANGED(dev, features, NETIF_F_RXFCS)) | 
 | 		return 0; /* Nothing to change */ | 
 |  | 
 | 	if (DEV_FEATURE_CHANGED(dev, features, NETIF_F_HW_VLAN_CTAG_RX) && | 
 | 	    (features & NETIF_F_HW_VLAN_CTAG_RX) && | 
 | 	    (priv->hwtstamp_config.rx_filter != HWTSTAMP_FILTER_NONE)) { | 
 | 		en_warn(priv, "Can't turn ON rx vlan offload while time-stamping rx filter is ON\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	qlock(&mdev->state_lock); | 
 | 	if (priv->port_up) { | 
 | 		port_up = 1; | 
 | 		mlx4_en_stop_port(dev, 1); | 
 | 	} | 
 |  | 
 | 	mlx4_en_free_resources(priv); | 
 |  | 
 | 	en_warn(priv, "Changing device configuration rx filter(%x) rx vlan(%x)\n", | 
 | 		ts_config.rx_filter, !!(features & NETIF_F_HW_VLAN_CTAG_RX)); | 
 |  | 
 | 	priv->hwtstamp_config.tx_type = ts_config.tx_type; | 
 | 	priv->hwtstamp_config.rx_filter = ts_config.rx_filter; | 
 |  | 
 | 	if (DEV_FEATURE_CHANGED(dev, features, NETIF_F_HW_VLAN_CTAG_RX)) { | 
 | 		if (features & NETIF_F_HW_VLAN_CTAG_RX) | 
 | 			dev->feat |= NETIF_F_HW_VLAN_CTAG_RX; | 
 | 		else | 
 | 			dev->feat &= ~NETIF_F_HW_VLAN_CTAG_RX; | 
 | 	} else if (ts_config.rx_filter == HWTSTAMP_FILTER_NONE) { | 
 | 		/* RX time-stamping is OFF, update the RX vlan offload | 
 | 		 * to the latest wanted state | 
 | 		 */ | 
 | #if 0 // AKAROS_PORT | 
 | 		if (dev->wanted_features & NETIF_F_HW_VLAN_CTAG_RX) | 
 | 			dev->feat |= NETIF_F_HW_VLAN_CTAG_RX; | 
 | 		else | 
 | #endif | 
 | 			dev->feat &= ~NETIF_F_HW_VLAN_CTAG_RX; | 
 | 	} | 
 |  | 
 | 	if (DEV_FEATURE_CHANGED(dev, features, NETIF_F_RXFCS)) { | 
 | 		if (features & NETIF_F_RXFCS) | 
 | 			dev->feat |= NETIF_F_RXFCS; | 
 | 		else | 
 | 			dev->feat &= ~NETIF_F_RXFCS; | 
 | 	} | 
 |  | 
 | 	/* RX vlan offload and RX time-stamping can't co-exist ! | 
 | 	 * Regardless of the caller's choice, | 
 | 	 * Turn Off RX vlan offload in case of time-stamping is ON | 
 | 	 */ | 
 | 	if (ts_config.rx_filter != HWTSTAMP_FILTER_NONE) { | 
 | 		if (dev->feat & NETIF_F_HW_VLAN_CTAG_RX) | 
 | 			en_warn(priv, "Turning off RX vlan offload since RX time-stamping is ON\n"); | 
 | 		dev->feat &= ~NETIF_F_HW_VLAN_CTAG_RX; | 
 | 	} | 
 |  | 
 | 	err = mlx4_en_alloc_resources(priv); | 
 | 	if (err) { | 
 | 		en_err(priv, "Failed reallocating port resources\n"); | 
 | 		goto out; | 
 | 	} | 
 | 	if (port_up) { | 
 | 		err = mlx4_en_start_port(dev); | 
 | 		if (err) | 
 | 			en_err(priv, "Failed starting port\n"); | 
 | 	} | 
 |  | 
 | out: | 
 | 	qunlock(&mdev->state_lock); | 
 | #if 0 // AKAROS_PORT | 
 | 	netdev_features_change(dev); | 
 | #endif | 
 | 	return err; | 
 | } |