diff options
Diffstat (limited to 'drivers/net/ethernet/cisco')
| -rw-r--r-- | drivers/net/ethernet/cisco/enic/enic.h | 33 | ||||
| -rw-r--r-- | drivers/net/ethernet/cisco/enic/enic_dev.c | 4 | ||||
| -rw-r--r-- | drivers/net/ethernet/cisco/enic/enic_dev.h | 4 | ||||
| -rw-r--r-- | drivers/net/ethernet/cisco/enic/enic_ethtool.c | 67 | ||||
| -rw-r--r-- | drivers/net/ethernet/cisco/enic/enic_main.c | 360 | ||||
| -rw-r--r-- | drivers/net/ethernet/cisco/enic/enic_pp.c | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/cisco/enic/vnic_cq.h | 9 | ||||
| -rw-r--r-- | drivers/net/ethernet/cisco/enic/vnic_dev.c | 4 | ||||
| -rw-r--r-- | drivers/net/ethernet/cisco/enic/vnic_dev.h | 4 | 
9 files changed, 342 insertions, 145 deletions
diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h index e9f7c656ddd..14f465f239d 100644 --- a/drivers/net/ethernet/cisco/enic/enic.h +++ b/drivers/net/ethernet/cisco/enic/enic.h @@ -29,6 +29,7 @@  #include "vnic_stats.h"  #include "vnic_nic.h"  #include "vnic_rss.h" +#include <linux/irq.h>  #define DRV_NAME		"enic"  #define DRV_DESCRIPTION		"Cisco VIC Ethernet NIC Driver" @@ -42,6 +43,8 @@  #define ENIC_CQ_MAX		(ENIC_WQ_MAX + ENIC_RQ_MAX)  #define ENIC_INTR_MAX		(ENIC_CQ_MAX + 2) +#define ENIC_AIC_LARGE_PKT_DIFF	3 +  struct enic_msix_entry {  	int requested;  	char devname[IFNAMSIZ]; @@ -49,6 +52,33 @@ struct enic_msix_entry {  	void *devid;  }; +/* Store only the lower range.  Higher range is given by fw. */ +struct enic_intr_mod_range { +	u32 small_pkt_range_start; +	u32 large_pkt_range_start; +}; + +struct enic_intr_mod_table { +	u32 rx_rate; +	u32 range_percent; +}; + +#define ENIC_MAX_LINK_SPEEDS		3 +#define ENIC_LINK_SPEED_10G		10000 +#define ENIC_LINK_SPEED_4G		4000 +#define ENIC_LINK_40G_INDEX		2 +#define ENIC_LINK_10G_INDEX		1 +#define ENIC_LINK_4G_INDEX		0 +#define ENIC_RX_COALESCE_RANGE_END	125 +#define ENIC_AIC_TS_BREAK		100 + +struct enic_rx_coal { +	u32 small_pkt_range_start; +	u32 large_pkt_range_start; +	u32 range_end; +	u32 use_adaptive_rx_coalesce; +}; +  /* priv_flags */  #define ENIC_SRIOV_ENABLED		(1 << 0) @@ -84,13 +114,12 @@ struct enic {  	u32 msg_enable;  	spinlock_t devcmd_lock;  	u8 mac_addr[ETH_ALEN]; -	u8 mc_addr[ENIC_MULTICAST_PERFECT_FILTERS][ETH_ALEN]; -	u8 uc_addr[ENIC_UNICAST_PERFECT_FILTERS][ETH_ALEN];  	unsigned int flags;  	unsigned int priv_flags;  	unsigned int mc_count;  	unsigned int uc_count;  	u32 port_mtu; +	struct enic_rx_coal rx_coalesce_setting;  	u32 rx_coalesce_usecs;  	u32 tx_coalesce_usecs;  #ifdef CONFIG_PCI_IOV diff --git a/drivers/net/ethernet/cisco/enic/enic_dev.c b/drivers/net/ethernet/cisco/enic/enic_dev.c index 4b6e5695b26..3e27df52284 100644 --- a/drivers/net/ethernet/cisco/enic/enic_dev.c +++ b/drivers/net/ethernet/cisco/enic/enic_dev.c @@ -88,7 +88,7 @@ int enic_dev_packet_filter(struct enic *enic, int directed, int multicast,  	return err;  } -int enic_dev_add_addr(struct enic *enic, u8 *addr) +int enic_dev_add_addr(struct enic *enic, const u8 *addr)  {  	int err; @@ -99,7 +99,7 @@ int enic_dev_add_addr(struct enic *enic, u8 *addr)  	return err;  } -int enic_dev_del_addr(struct enic *enic, u8 *addr) +int enic_dev_del_addr(struct enic *enic, const u8 *addr)  {  	int err; diff --git a/drivers/net/ethernet/cisco/enic/enic_dev.h b/drivers/net/ethernet/cisco/enic/enic_dev.h index 129b14a4efb..36ea1ab25f6 100644 --- a/drivers/net/ethernet/cisco/enic/enic_dev.h +++ b/drivers/net/ethernet/cisco/enic/enic_dev.h @@ -45,8 +45,8 @@ int enic_dev_add_station_addr(struct enic *enic);  int enic_dev_del_station_addr(struct enic *enic);  int enic_dev_packet_filter(struct enic *enic, int directed, int multicast,  	int broadcast, int promisc, int allmulti); -int enic_dev_add_addr(struct enic *enic, u8 *addr); -int enic_dev_del_addr(struct enic *enic, u8 *addr); +int enic_dev_add_addr(struct enic *enic, const u8 *addr); +int enic_dev_del_addr(struct enic *enic, const u8 *addr);  int enic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid);  int enic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid);  int enic_dev_notify_unset(struct enic *enic); diff --git a/drivers/net/ethernet/cisco/enic/enic_ethtool.c b/drivers/net/ethernet/cisco/enic/enic_ethtool.c index 47e3562f486..2e50b5489d2 100644 --- a/drivers/net/ethernet/cisco/enic/enic_ethtool.c +++ b/drivers/net/ethernet/cisco/enic/enic_ethtool.c @@ -79,6 +79,17 @@ static const struct enic_stat enic_rx_stats[] = {  static const unsigned int enic_n_tx_stats = ARRAY_SIZE(enic_tx_stats);  static const unsigned int enic_n_rx_stats = ARRAY_SIZE(enic_rx_stats); +void enic_intr_coal_set_rx(struct enic *enic, u32 timer) +{ +	int i; +	int intr; + +	for (i = 0; i < enic->rq_count; i++) { +		intr = enic_msix_rq_intr(enic, i); +		vnic_intr_coalescing_timer_set(&enic->intr[intr], timer); +	} +} +  static int enic_get_settings(struct net_device *netdev,  	struct ethtool_cmd *ecmd)  { @@ -93,8 +104,8 @@ static int enic_get_settings(struct net_device *netdev,  		ethtool_cmd_speed_set(ecmd, vnic_dev_port_speed(enic->vdev));  		ecmd->duplex = DUPLEX_FULL;  	} else { -		ethtool_cmd_speed_set(ecmd, -1); -		ecmd->duplex = -1; +		ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); +		ecmd->duplex = DUPLEX_UNKNOWN;  	}  	ecmd->autoneg = AUTONEG_DISABLE; @@ -178,9 +189,14 @@ static int enic_get_coalesce(struct net_device *netdev,  	struct ethtool_coalesce *ecmd)  {  	struct enic *enic = netdev_priv(netdev); +	struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting;  	ecmd->tx_coalesce_usecs = enic->tx_coalesce_usecs;  	ecmd->rx_coalesce_usecs = enic->rx_coalesce_usecs; +	if (rxcoal->use_adaptive_rx_coalesce) +		ecmd->use_adaptive_rx_coalesce = 1; +	ecmd->rx_coalesce_usecs_low = rxcoal->small_pkt_range_start; +	ecmd->rx_coalesce_usecs_high = rxcoal->range_end;  	return 0;  } @@ -191,17 +207,31 @@ static int enic_set_coalesce(struct net_device *netdev,  	struct enic *enic = netdev_priv(netdev);  	u32 tx_coalesce_usecs;  	u32 rx_coalesce_usecs; +	u32 rx_coalesce_usecs_low; +	u32 rx_coalesce_usecs_high; +	u32 coalesce_usecs_max;  	unsigned int i, intr; +	struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting; +	coalesce_usecs_max = vnic_dev_get_intr_coal_timer_max(enic->vdev);  	tx_coalesce_usecs = min_t(u32, ecmd->tx_coalesce_usecs, -		vnic_dev_get_intr_coal_timer_max(enic->vdev)); +				  coalesce_usecs_max);  	rx_coalesce_usecs = min_t(u32, ecmd->rx_coalesce_usecs, -		vnic_dev_get_intr_coal_timer_max(enic->vdev)); +				  coalesce_usecs_max); + +	rx_coalesce_usecs_low = min_t(u32, ecmd->rx_coalesce_usecs_low, +				      coalesce_usecs_max); +	rx_coalesce_usecs_high = min_t(u32, ecmd->rx_coalesce_usecs_high, +				       coalesce_usecs_max);  	switch (vnic_dev_get_intr_mode(enic->vdev)) {  	case VNIC_DEV_INTR_MODE_INTX:  		if (tx_coalesce_usecs != rx_coalesce_usecs)  			return -EINVAL; +		if (ecmd->use_adaptive_rx_coalesce	|| +		    ecmd->rx_coalesce_usecs_low		|| +		    ecmd->rx_coalesce_usecs_high) +			return -EOPNOTSUPP;  		intr = enic_legacy_io_intr();  		vnic_intr_coalescing_timer_set(&enic->intr[intr], @@ -210,6 +240,10 @@ static int enic_set_coalesce(struct net_device *netdev,  	case VNIC_DEV_INTR_MODE_MSI:  		if (tx_coalesce_usecs != rx_coalesce_usecs)  			return -EINVAL; +		if (ecmd->use_adaptive_rx_coalesce	|| +		    ecmd->rx_coalesce_usecs_low		|| +		    ecmd->rx_coalesce_usecs_high) +			return -EOPNOTSUPP;  		vnic_intr_coalescing_timer_set(&enic->intr[0],  			tx_coalesce_usecs); @@ -221,12 +255,27 @@ static int enic_set_coalesce(struct net_device *netdev,  				tx_coalesce_usecs);  		} -		for (i = 0; i < enic->rq_count; i++) { -			intr = enic_msix_rq_intr(enic, i); -			vnic_intr_coalescing_timer_set(&enic->intr[intr], -				rx_coalesce_usecs); +		if (rxcoal->use_adaptive_rx_coalesce) { +			if (!ecmd->use_adaptive_rx_coalesce) { +				rxcoal->use_adaptive_rx_coalesce = 0; +				enic_intr_coal_set_rx(enic, rx_coalesce_usecs); +			} +		} else { +			if (ecmd->use_adaptive_rx_coalesce) +				rxcoal->use_adaptive_rx_coalesce = 1; +			else +				enic_intr_coal_set_rx(enic, rx_coalesce_usecs);  		} +		if (ecmd->rx_coalesce_usecs_high) { +			if (rx_coalesce_usecs_high < +			    (rx_coalesce_usecs_low + ENIC_AIC_LARGE_PKT_DIFF)) +				return -EINVAL; +			rxcoal->range_end = rx_coalesce_usecs_high; +			rxcoal->small_pkt_range_start = rx_coalesce_usecs_low; +			rxcoal->large_pkt_range_start = rx_coalesce_usecs_low + +							ENIC_AIC_LARGE_PKT_DIFF; +		}  		break;  	default:  		break; @@ -253,5 +302,5 @@ static const struct ethtool_ops enic_ethtool_ops = {  void enic_set_ethtool_ops(struct net_device *netdev)  { -	SET_ETHTOOL_OPS(netdev, &enic_ethtool_ops); +	netdev->ethtool_ops = &enic_ethtool_ops;  } diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 7b756cf9474..f32f828b7f3 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -38,6 +38,7 @@  #include <linux/rtnetlink.h>  #include <linux/prefetch.h>  #include <net/ip6_checksum.h> +#include <linux/ktime.h>  #include "cq_enet_desc.h"  #include "vnic_dev.h" @@ -72,6 +73,35 @@ MODULE_LICENSE("GPL");  MODULE_VERSION(DRV_VERSION);  MODULE_DEVICE_TABLE(pci, enic_id_table); +#define ENIC_LARGE_PKT_THRESHOLD		1000 +#define ENIC_MAX_COALESCE_TIMERS		10 +/*  Interrupt moderation table, which will be used to decide the + *  coalescing timer values + *  {rx_rate in Mbps, mapping percentage of the range} + */ +struct enic_intr_mod_table mod_table[ENIC_MAX_COALESCE_TIMERS + 1] = { +	{4000,  0}, +	{4400, 10}, +	{5060, 20}, +	{5230, 30}, +	{5540, 40}, +	{5820, 50}, +	{6120, 60}, +	{6435, 70}, +	{6745, 80}, +	{7000, 90}, +	{0xFFFFFFFF, 100} +}; + +/* This table helps the driver to pick different ranges for rx coalescing + * timer depending on the link speed. + */ +struct enic_intr_mod_range mod_range[ENIC_MAX_LINK_SPEEDS] = { +	{0,  0}, /* 0  - 4  Gbps */ +	{0,  3}, /* 4  - 10 Gbps */ +	{3,  6}, /* 10 - 40 Gbps */ +}; +  int enic_is_dynamic(struct enic *enic)  {  	return enic->pdev->device == PCI_DEVICE_ID_CISCO_VIC_ENET_DYN; @@ -521,7 +551,7 @@ static netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb,  	unsigned int txq_map;  	if (skb->len <= 0) { -		dev_kfree_skb(skb); +		dev_kfree_skb_any(skb);  		return NETDEV_TX_OK;  	} @@ -536,7 +566,7 @@ static netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb,  	if (skb_shinfo(skb)->gso_size == 0 &&  	    skb_shinfo(skb)->nr_frags + 1 > ENIC_NON_TSO_MAX_DESC &&  	    skb_linearize(skb)) { -		dev_kfree_skb(skb); +		dev_kfree_skb_any(skb);  		return NETDEV_TX_OK;  	} @@ -586,8 +616,71 @@ static struct rtnl_link_stats64 *enic_get_stats(struct net_device *netdev,  	return net_stats;  } +static int enic_mc_sync(struct net_device *netdev, const u8 *mc_addr) +{ +	struct enic *enic = netdev_priv(netdev); + +	if (enic->mc_count == ENIC_MULTICAST_PERFECT_FILTERS) { +		unsigned int mc_count = netdev_mc_count(netdev); + +		netdev_warn(netdev, "Registering only %d out of %d multicast addresses\n", +			    ENIC_MULTICAST_PERFECT_FILTERS, mc_count); + +		return -ENOSPC; +	} + +	enic_dev_add_addr(enic, mc_addr); +	enic->mc_count++; + +	return 0; +} + +static int enic_mc_unsync(struct net_device *netdev, const u8 *mc_addr) +{ +	struct enic *enic = netdev_priv(netdev); + +	enic_dev_del_addr(enic, mc_addr); +	enic->mc_count--; + +	return 0; +} + +static int enic_uc_sync(struct net_device *netdev, const u8 *uc_addr) +{ +	struct enic *enic = netdev_priv(netdev); + +	if (enic->uc_count == ENIC_UNICAST_PERFECT_FILTERS) { +		unsigned int uc_count = netdev_uc_count(netdev); + +		netdev_warn(netdev, "Registering only %d out of %d unicast addresses\n", +			    ENIC_UNICAST_PERFECT_FILTERS, uc_count); + +		return -ENOSPC; +	} + +	enic_dev_add_addr(enic, uc_addr); +	enic->uc_count++; + +	return 0; +} + +static int enic_uc_unsync(struct net_device *netdev, const u8 *uc_addr) +{ +	struct enic *enic = netdev_priv(netdev); + +	enic_dev_del_addr(enic, uc_addr); +	enic->uc_count--; + +	return 0; +} +  void enic_reset_addr_lists(struct enic *enic)  { +	struct net_device *netdev = enic->netdev; + +	__dev_uc_unsync(netdev, NULL); +	__dev_mc_unsync(netdev, NULL); +  	enic->mc_count = 0;  	enic->uc_count = 0;  	enic->flags = 0; @@ -654,112 +747,6 @@ static int enic_set_mac_address(struct net_device *netdev, void *p)  	return enic_dev_add_station_addr(enic);  } -static void enic_update_multicast_addr_list(struct enic *enic) -{ -	struct net_device *netdev = enic->netdev; -	struct netdev_hw_addr *ha; -	unsigned int mc_count = netdev_mc_count(netdev); -	u8 mc_addr[ENIC_MULTICAST_PERFECT_FILTERS][ETH_ALEN]; -	unsigned int i, j; - -	if (mc_count > ENIC_MULTICAST_PERFECT_FILTERS) { -		netdev_warn(netdev, "Registering only %d out of %d " -			"multicast addresses\n", -			ENIC_MULTICAST_PERFECT_FILTERS, mc_count); -		mc_count = ENIC_MULTICAST_PERFECT_FILTERS; -	} - -	/* Is there an easier way?  Trying to minimize to -	 * calls to add/del multicast addrs.  We keep the -	 * addrs from the last call in enic->mc_addr and -	 * look for changes to add/del. -	 */ - -	i = 0; -	netdev_for_each_mc_addr(ha, netdev) { -		if (i == mc_count) -			break; -		memcpy(mc_addr[i++], ha->addr, ETH_ALEN); -	} - -	for (i = 0; i < enic->mc_count; i++) { -		for (j = 0; j < mc_count; j++) -			if (ether_addr_equal(enic->mc_addr[i], mc_addr[j])) -				break; -		if (j == mc_count) -			enic_dev_del_addr(enic, enic->mc_addr[i]); -	} - -	for (i = 0; i < mc_count; i++) { -		for (j = 0; j < enic->mc_count; j++) -			if (ether_addr_equal(mc_addr[i], enic->mc_addr[j])) -				break; -		if (j == enic->mc_count) -			enic_dev_add_addr(enic, mc_addr[i]); -	} - -	/* Save the list to compare against next time -	 */ - -	for (i = 0; i < mc_count; i++) -		memcpy(enic->mc_addr[i], mc_addr[i], ETH_ALEN); - -	enic->mc_count = mc_count; -} - -static void enic_update_unicast_addr_list(struct enic *enic) -{ -	struct net_device *netdev = enic->netdev; -	struct netdev_hw_addr *ha; -	unsigned int uc_count = netdev_uc_count(netdev); -	u8 uc_addr[ENIC_UNICAST_PERFECT_FILTERS][ETH_ALEN]; -	unsigned int i, j; - -	if (uc_count > ENIC_UNICAST_PERFECT_FILTERS) { -		netdev_warn(netdev, "Registering only %d out of %d " -			"unicast addresses\n", -			ENIC_UNICAST_PERFECT_FILTERS, uc_count); -		uc_count = ENIC_UNICAST_PERFECT_FILTERS; -	} - -	/* Is there an easier way?  Trying to minimize to -	 * calls to add/del unicast addrs.  We keep the -	 * addrs from the last call in enic->uc_addr and -	 * look for changes to add/del. -	 */ - -	i = 0; -	netdev_for_each_uc_addr(ha, netdev) { -		if (i == uc_count) -			break; -		memcpy(uc_addr[i++], ha->addr, ETH_ALEN); -	} - -	for (i = 0; i < enic->uc_count; i++) { -		for (j = 0; j < uc_count; j++) -			if (ether_addr_equal(enic->uc_addr[i], uc_addr[j])) -				break; -		if (j == uc_count) -			enic_dev_del_addr(enic, enic->uc_addr[i]); -	} - -	for (i = 0; i < uc_count; i++) { -		for (j = 0; j < enic->uc_count; j++) -			if (ether_addr_equal(uc_addr[i], enic->uc_addr[j])) -				break; -		if (j == enic->uc_count) -			enic_dev_add_addr(enic, uc_addr[i]); -	} - -	/* Save the list to compare against next time -	 */ - -	for (i = 0; i < uc_count; i++) -		memcpy(enic->uc_addr[i], uc_addr[i], ETH_ALEN); - -	enic->uc_count = uc_count; -} -  /* netif_tx_lock held, BHs disabled */  static void enic_set_rx_mode(struct net_device *netdev)  { @@ -782,9 +769,9 @@ static void enic_set_rx_mode(struct net_device *netdev)  	}  	if (!promisc) { -		enic_update_unicast_addr_list(enic); +		__dev_uc_sync(netdev, enic_uc_sync, enic_uc_unsync);  		if (!allmulti) -			enic_update_multicast_addr_list(enic); +			__dev_mc_sync(netdev, enic_mc_sync, enic_mc_unsync);  	}  } @@ -979,6 +966,15 @@ static int enic_rq_alloc_buf(struct vnic_rq *rq)  	return 0;  } +static void enic_intr_update_pkt_size(struct vnic_rx_bytes_counter *pkt_size, +				      u32 pkt_len) +{ +	if (ENIC_LARGE_PKT_THRESHOLD <= pkt_len) +		pkt_size->large_pkt_bytes_cnt += pkt_len; +	else +		pkt_size->small_pkt_bytes_cnt += pkt_len; +} +  static void enic_rq_indicate_buf(struct vnic_rq *rq,  	struct cq_desc *cq_desc, struct vnic_rq_buf *buf,  	int skipped, void *opaque) @@ -986,6 +982,7 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,  	struct enic *enic = vnic_dev_priv(rq->vdev);  	struct net_device *netdev = enic->netdev;  	struct sk_buff *skb; +	struct vnic_cq *cq = &enic->cq[enic_cq_rq(enic, rq->index)];  	u8 type, color, eop, sop, ingress_port, vlan_stripped;  	u8 fcoe, fcoe_sof, fcoe_fc_crc_ok, fcoe_enc_error, fcoe_eof; @@ -1036,11 +1033,12 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,  		skb->protocol = eth_type_trans(skb, netdev);  		skb_record_rx_queue(skb, q_number);  		if (netdev->features & NETIF_F_RXHASH) { -			skb->rxhash = rss_hash; -			if (rss_type & (NIC_CFG_RSS_HASH_TYPE_TCP_IPV6_EX | -					NIC_CFG_RSS_HASH_TYPE_TCP_IPV6 | -					NIC_CFG_RSS_HASH_TYPE_TCP_IPV4)) -				skb->l4_rxhash = true; +			skb_set_hash(skb, rss_hash, +				     (rss_type & +				      (NIC_CFG_RSS_HASH_TYPE_TCP_IPV6_EX | +				       NIC_CFG_RSS_HASH_TYPE_TCP_IPV6 | +				       NIC_CFG_RSS_HASH_TYPE_TCP_IPV4)) ? +				     PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3);  		}  		if ((netdev->features & NETIF_F_RXCSUM) && !csum_not_calc) { @@ -1055,6 +1053,9 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,  			napi_gro_receive(&enic->napi[q_number], skb);  		else  			netif_receive_skb(skb); +		if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce) +			enic_intr_update_pkt_size(&cq->pkt_size_counter, +						  bytes_written);  	} else {  		/* Buffer overflow @@ -1085,14 +1086,15 @@ static int enic_poll(struct napi_struct *napi, int budget)  	unsigned int intr = enic_legacy_io_intr();  	unsigned int rq_work_to_do = budget;  	unsigned int wq_work_to_do = -1; /* no limit */ -	unsigned int  work_done, rq_work_done, wq_work_done; +	unsigned int  work_done, rq_work_done = 0, wq_work_done;  	int err;  	/* Service RQ (first) and WQ  	 */ -	rq_work_done = vnic_cq_service(&enic->cq[cq_rq], -		rq_work_to_do, enic_rq_service, NULL); +	if (budget > 0) +		rq_work_done = vnic_cq_service(&enic->cq[cq_rq], +			rq_work_to_do, enic_rq_service, NULL);  	wq_work_done = vnic_cq_service(&enic->cq[cq_wq],  		wq_work_to_do, enic_wq_service, NULL); @@ -1132,6 +1134,64 @@ static int enic_poll(struct napi_struct *napi, int budget)  	return rq_work_done;  } +static void enic_set_int_moderation(struct enic *enic, struct vnic_rq *rq) +{ +	unsigned int intr = enic_msix_rq_intr(enic, rq->index); +	struct vnic_cq *cq = &enic->cq[enic_cq_rq(enic, rq->index)]; +	u32 timer = cq->tobe_rx_coal_timeval; + +	if (cq->tobe_rx_coal_timeval != cq->cur_rx_coal_timeval) { +		vnic_intr_coalescing_timer_set(&enic->intr[intr], timer); +		cq->cur_rx_coal_timeval = cq->tobe_rx_coal_timeval; +	} +} + +static void enic_calc_int_moderation(struct enic *enic, struct vnic_rq *rq) +{ +	struct enic_rx_coal *rx_coal = &enic->rx_coalesce_setting; +	struct vnic_cq *cq = &enic->cq[enic_cq_rq(enic, rq->index)]; +	struct vnic_rx_bytes_counter *pkt_size_counter = &cq->pkt_size_counter; +	int index; +	u32 timer; +	u32 range_start; +	u32 traffic; +	u64 delta; +	ktime_t now = ktime_get(); + +	delta = ktime_us_delta(now, cq->prev_ts); +	if (delta < ENIC_AIC_TS_BREAK) +		return; +	cq->prev_ts = now; + +	traffic = pkt_size_counter->large_pkt_bytes_cnt + +		  pkt_size_counter->small_pkt_bytes_cnt; +	/* The table takes Mbps +	 * traffic *= 8    => bits +	 * traffic *= (10^6 / delta)    => bps +	 * traffic /= 10^6     => Mbps +	 * +	 * Combining, traffic *= (8 / delta) +	 */ + +	traffic <<= 3; +	traffic = delta > UINT_MAX ? 0 : traffic / (u32)delta; + +	for (index = 0; index < ENIC_MAX_COALESCE_TIMERS; index++) +		if (traffic < mod_table[index].rx_rate) +			break; +	range_start = (pkt_size_counter->small_pkt_bytes_cnt > +		       pkt_size_counter->large_pkt_bytes_cnt << 1) ? +		      rx_coal->small_pkt_range_start : +		      rx_coal->large_pkt_range_start; +	timer = range_start + ((rx_coal->range_end - range_start) * +			       mod_table[index].range_percent / 100); +	/* Damping */ +	cq->tobe_rx_coal_timeval = (timer + cq->tobe_rx_coal_timeval) >> 1; + +	pkt_size_counter->large_pkt_bytes_cnt = 0; +	pkt_size_counter->small_pkt_bytes_cnt = 0; +} +  static int enic_poll_msix(struct napi_struct *napi, int budget)  {  	struct net_device *netdev = napi->dev; @@ -1140,14 +1200,15 @@ static int enic_poll_msix(struct napi_struct *napi, int budget)  	unsigned int cq = enic_cq_rq(enic, rq);  	unsigned int intr = enic_msix_rq_intr(enic, rq);  	unsigned int work_to_do = budget; -	unsigned int work_done; +	unsigned int work_done = 0;  	int err;  	/* Service RQ  	 */ -	work_done = vnic_cq_service(&enic->cq[cq], -		work_to_do, enic_rq_service, NULL); +	if (budget > 0) +		work_done = vnic_cq_service(&enic->cq[cq], +			work_to_do, enic_rq_service, NULL);  	/* Return intr event credits for this polling  	 * cycle.  An intr event is the completion of a @@ -1168,6 +1229,13 @@ static int enic_poll_msix(struct napi_struct *napi, int budget)  	if (err)  		work_done = work_to_do; +	if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce) +		/* Call the function which refreshes +		 * the intr coalescing timer value based on +		 * the traffic.  This is supported only in +		 * the case of MSI-x mode +		 */ +		enic_calc_int_moderation(enic, &enic->rq[rq]);  	if (work_done < work_to_do) { @@ -1176,6 +1244,8 @@ static int enic_poll_msix(struct napi_struct *napi, int budget)  		 */  		napi_complete(napi); +		if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce) +			enic_set_int_moderation(enic, &enic->rq[rq]);  		vnic_intr_unmask(&enic->intr[intr]);  	} @@ -1311,6 +1381,42 @@ static void enic_synchronize_irqs(struct enic *enic)  	}  } +static void enic_set_rx_coal_setting(struct enic *enic) +{ +	unsigned int speed; +	int index = -1; +	struct enic_rx_coal *rx_coal = &enic->rx_coalesce_setting; + +	/* If intr mode is not MSIX, do not do adaptive coalescing */ +	if (VNIC_DEV_INTR_MODE_MSIX != vnic_dev_get_intr_mode(enic->vdev)) { +		netdev_info(enic->netdev, "INTR mode is not MSIX, Not initializing adaptive coalescing"); +		return; +	} + +	/* 1. Read the link speed from fw +	 * 2. Pick the default range for the speed +	 * 3. Update it in enic->rx_coalesce_setting +	 */ +	speed = vnic_dev_port_speed(enic->vdev); +	if (ENIC_LINK_SPEED_10G < speed) +		index = ENIC_LINK_40G_INDEX; +	else if (ENIC_LINK_SPEED_4G < speed) +		index = ENIC_LINK_10G_INDEX; +	else +		index = ENIC_LINK_4G_INDEX; + +	rx_coal->small_pkt_range_start = mod_range[index].small_pkt_range_start; +	rx_coal->large_pkt_range_start = mod_range[index].large_pkt_range_start; +	rx_coal->range_end = ENIC_RX_COALESCE_RANGE_END; + +	/* Start with the value provided by UCSM */ +	for (index = 0; index < enic->rq_count; index++) +		enic->cq[index].cur_rx_coal_timeval = +				enic->config.intr_timer_usec; + +	rx_coal->use_adaptive_rx_coalesce = 1; +} +  static int enic_dev_notify_set(struct enic *enic)  {  	int err; @@ -1795,7 +1901,8 @@ static int enic_set_intr_mode(struct enic *enic)  	    enic->cq_count >= n + m &&  	    enic->intr_count >= n + m + 2) { -		if (!pci_enable_msix(enic->pdev, enic->msix_entry, n + m + 2)) { +		if (pci_enable_msix_range(enic->pdev, enic->msix_entry, +					  n + m + 2, n + m + 2) > 0) {  			enic->rq_count = n;  			enic->wq_count = m; @@ -1814,7 +1921,8 @@ static int enic_set_intr_mode(struct enic *enic)  	    enic->wq_count >= m &&  	    enic->cq_count >= 1 + m &&  	    enic->intr_count >= 1 + m + 2) { -		if (!pci_enable_msix(enic->pdev, enic->msix_entry, 1 + m + 2)) { +		if (pci_enable_msix_range(enic->pdev, enic->msix_entry, +					  1 + m + 2, 1 + m + 2) > 0) {  			enic->rq_count = 1;  			enic->wq_count = m; @@ -2226,6 +2334,7 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  	enic->notify_timer.function = enic_notify_timer;  	enic->notify_timer.data = (unsigned long)enic; +	enic_set_rx_coal_setting(enic);  	INIT_WORK(&enic->reset, enic_reset);  	INIT_WORK(&enic->change_mtu_work, enic_change_mtu_work); @@ -2245,6 +2354,9 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  	}  	enic->tx_coalesce_usecs = enic->config.intr_timer_usec; +	/* rx coalesce time already got initialized. This gets used +	 * if adaptive coal is turned off +	 */  	enic->rx_coalesce_usecs = enic->tx_coalesce_usecs;  	if (enic_is_dynamic(enic) || enic_is_sriov_vf(enic)) @@ -2309,7 +2421,6 @@ err_out_release_regions:  err_out_disable_device:  	pci_disable_device(pdev);  err_out_free_netdev: -	pci_set_drvdata(pdev, NULL);  	free_netdev(netdev);  	return err; @@ -2338,7 +2449,6 @@ static void enic_remove(struct pci_dev *pdev)  		enic_iounmap(enic);  		pci_release_regions(pdev);  		pci_disable_device(pdev); -		pci_set_drvdata(pdev, NULL);  		free_netdev(netdev);  	}  } diff --git a/drivers/net/ethernet/cisco/enic/enic_pp.c b/drivers/net/ethernet/cisco/enic/enic_pp.c index 43464f0a4f9..e6a83198c3d 100644 --- a/drivers/net/ethernet/cisco/enic/enic_pp.c +++ b/drivers/net/ethernet/cisco/enic/enic_pp.c @@ -162,7 +162,7 @@ static int enic_are_pp_different(struct enic_port_profile *pp1,  	return strcmp(pp1->name, pp2->name) | !!memcmp(pp1->instance_uuid,  		pp2->instance_uuid, PORT_UUID_MAX) |  		!!memcmp(pp1->host_uuid, pp2->host_uuid, PORT_UUID_MAX) | -		!!memcmp(pp1->mac_addr, pp2->mac_addr, ETH_ALEN); +		!ether_addr_equal(pp1->mac_addr, pp2->mac_addr);  }  static int enic_pp_preassociate(struct enic *enic, int vf, diff --git a/drivers/net/ethernet/cisco/enic/vnic_cq.h b/drivers/net/ethernet/cisco/enic/vnic_cq.h index 579315cbe80..4e6aa65857f 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_cq.h +++ b/drivers/net/ethernet/cisco/enic/vnic_cq.h @@ -50,6 +50,11 @@ struct vnic_cq_ctrl {  	u32 pad10;  }; +struct vnic_rx_bytes_counter { +	unsigned int small_pkt_bytes_cnt; +	unsigned int large_pkt_bytes_cnt; +}; +  struct vnic_cq {  	unsigned int index;  	struct vnic_dev *vdev; @@ -58,6 +63,10 @@ struct vnic_cq {  	unsigned int to_clean;  	unsigned int last_color;  	unsigned int interrupt_offset; +	struct vnic_rx_bytes_counter pkt_size_counter; +	unsigned int cur_rx_coal_timeval; +	unsigned int tobe_rx_coal_timeval; +	ktime_t prev_ts;  };  static inline unsigned int vnic_cq_service(struct vnic_cq *cq, diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.c b/drivers/net/ethernet/cisco/enic/vnic_dev.c index 69dd92598b7..e86a45cb9e6 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_dev.c +++ b/drivers/net/ethernet/cisco/enic/vnic_dev.c @@ -657,7 +657,7 @@ int vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast,  	return err;  } -int vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr) +int vnic_dev_add_addr(struct vnic_dev *vdev, const u8 *addr)  {  	u64 a0 = 0, a1 = 0;  	int wait = 1000; @@ -674,7 +674,7 @@ int vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr)  	return err;  } -int vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr) +int vnic_dev_del_addr(struct vnic_dev *vdev, const u8 *addr)  {  	u64 a0 = 0, a1 = 0;  	int wait = 1000; diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.h b/drivers/net/ethernet/cisco/enic/vnic_dev.h index e670029862a..1f3b301f822 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_dev.h +++ b/drivers/net/ethernet/cisco/enic/vnic_dev.h @@ -95,8 +95,8 @@ int vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats);  int vnic_dev_hang_notify(struct vnic_dev *vdev);  int vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast,  	int broadcast, int promisc, int allmulti); -int vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr); -int vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr); +int vnic_dev_add_addr(struct vnic_dev *vdev, const u8 *addr); +int vnic_dev_del_addr(struct vnic_dev *vdev, const u8 *addr);  int vnic_dev_get_mac_addr(struct vnic_dev *vdev, u8 *mac_addr);  int vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr);  int vnic_dev_notify_unset(struct vnic_dev *vdev);  | 
