qlcnic: Enable Tx queue changes using ethtool for 82xx Series adapter.
o using ethtool {set|get}_channel option, user can change number
  of Tx queues for 82xx Series adapter.
o updated ethtool -S <ethX> option to display stats from each Tx queue.
Signed-off-by: Himanshu Madhani <himanshu.madhani@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
	
	
This commit is contained in:
		
					parent
					
						
							
								012ec81223
							
						
					
				
			
			
				commit
				
					
						aa4a1f7df7
					
				
			
		
					 8 changed files with 145 additions and 35 deletions
				
			
		|  | @ -1531,8 +1531,9 @@ int qlcnic_reset_context(struct qlcnic_adapter *); | |||
| void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings); | ||||
| int qlcnic_diag_alloc_res(struct net_device *netdev, int test); | ||||
| netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev); | ||||
| int qlcnic_set_max_rss(struct qlcnic_adapter *, u8, size_t); | ||||
| int qlcnic_set_max_rss(struct qlcnic_adapter *, u8, int); | ||||
| int qlcnic_validate_max_rss(struct qlcnic_adapter *, __u32); | ||||
| int qlcnic_validate_max_tx_rings(struct qlcnic_adapter *, int); | ||||
| void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter); | ||||
| void qlcnic_82xx_set_mac_filter_count(struct qlcnic_adapter *); | ||||
| int qlcnic_enable_msix(struct qlcnic_adapter *, u32); | ||||
|  | @ -1679,7 +1680,7 @@ struct qlcnic_hardware_ops { | |||
| 	int (*write_reg) (struct qlcnic_adapter *, ulong, u32); | ||||
| 	void (*get_ocm_win) (struct qlcnic_hardware_context *); | ||||
| 	int (*get_mac_address) (struct qlcnic_adapter *, u8 *); | ||||
| 	int (*setup_intr) (struct qlcnic_adapter *, u8); | ||||
| 	int (*setup_intr) (struct qlcnic_adapter *, u8, int); | ||||
| 	int (*alloc_mbx_args)(struct qlcnic_cmd_args *, | ||||
| 			      struct qlcnic_adapter *, u32); | ||||
| 	int (*mbx_cmd) (struct qlcnic_adapter *, struct qlcnic_cmd_args *); | ||||
|  | @ -1745,9 +1746,10 @@ static inline int qlcnic_get_mac_address(struct qlcnic_adapter *adapter, | |||
| 	return adapter->ahw->hw_ops->get_mac_address(adapter, mac); | ||||
| } | ||||
| 
 | ||||
| static inline int qlcnic_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr) | ||||
| static inline int qlcnic_setup_intr(struct qlcnic_adapter *adapter, | ||||
| 				    u8 num_intr, int txq) | ||||
| { | ||||
| 	return adapter->ahw->hw_ops->setup_intr(adapter, num_intr); | ||||
| 	return adapter->ahw->hw_ops->setup_intr(adapter, num_intr, txq); | ||||
| } | ||||
| 
 | ||||
| static inline int qlcnic_alloc_mbx_args(struct qlcnic_cmd_args *mbx, | ||||
|  |  | |||
|  | @ -261,7 +261,7 @@ int qlcnic_83xx_wrt_reg_indirect(struct qlcnic_adapter *adapter, ulong addr, | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| int qlcnic_83xx_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr) | ||||
| int qlcnic_83xx_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr, int txq) | ||||
| { | ||||
| 	int err, i, num_msix; | ||||
| 	struct qlcnic_hardware_context *ahw = adapter->ahw; | ||||
|  |  | |||
|  | @ -523,7 +523,7 @@ enum qlc_83xx_ext_regs { | |||
| /* 83xx funcitons */ | ||||
| int qlcnic_83xx_get_fw_version(struct qlcnic_adapter *); | ||||
| int qlcnic_83xx_issue_cmd(struct qlcnic_adapter *, struct qlcnic_cmd_args *); | ||||
| int qlcnic_83xx_setup_intr(struct qlcnic_adapter *, u8); | ||||
| int qlcnic_83xx_setup_intr(struct qlcnic_adapter *, u8, int); | ||||
| void qlcnic_83xx_get_func_no(struct qlcnic_adapter *); | ||||
| int qlcnic_83xx_cam_lock(struct qlcnic_adapter *); | ||||
| void qlcnic_83xx_cam_unlock(struct qlcnic_adapter *); | ||||
|  |  | |||
|  | @ -2201,7 +2201,7 @@ int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac) | |||
| 	if (err) | ||||
| 		goto detach_mbx; | ||||
| 
 | ||||
| 	err = qlcnic_setup_intr(adapter, 0); | ||||
| 	err = qlcnic_setup_intr(adapter, 0, 0); | ||||
| 	if (err) { | ||||
| 		dev_err(&adapter->pdev->dev, "Failed to setup interrupt\n"); | ||||
| 		goto disable_intr; | ||||
|  |  | |||
|  | @ -125,6 +125,14 @@ static const char qlcnic_83xx_mac_stats_strings[][ETH_GSTRING_LEN] = { | |||
| }; | ||||
| 
 | ||||
| #define QLCNIC_STATS_LEN	ARRAY_SIZE(qlcnic_gstrings_stats) | ||||
| 
 | ||||
| static const char qlcnic_tx_ring_stats_strings[][ETH_GSTRING_LEN] = { | ||||
| 	"xmit_on", | ||||
| 	"xmit_off", | ||||
| 	"xmit_called", | ||||
| 	"xmit_finished", | ||||
| }; | ||||
| 
 | ||||
| static const char qlcnic_83xx_rx_stats_strings[][ETH_GSTRING_LEN] = { | ||||
| 	"ctx_rx_bytes", | ||||
| 	"ctx_rx_pkts", | ||||
|  | @ -630,15 +638,15 @@ qlcnic_set_ringparam(struct net_device *dev, | |||
| static void qlcnic_get_channels(struct net_device *dev, | ||||
| 		struct ethtool_channels *channel) | ||||
| { | ||||
| 	int min; | ||||
| 	struct qlcnic_adapter *adapter = netdev_priv(dev); | ||||
| 	int min; | ||||
| 
 | ||||
| 	min = min_t(int, adapter->ahw->max_rx_ques, num_online_cpus()); | ||||
| 	channel->max_rx = rounddown_pow_of_two(min); | ||||
| 	channel->max_tx = adapter->ahw->max_tx_ques; | ||||
| 	channel->max_tx = min_t(int, QLCNIC_MAX_TX_RINGS, num_online_cpus()); | ||||
| 
 | ||||
| 	channel->rx_count = adapter->max_sds_rings; | ||||
| 	channel->tx_count = adapter->ahw->max_tx_ques; | ||||
| 	channel->tx_count = adapter->max_drv_tx_rings; | ||||
| } | ||||
| 
 | ||||
| static int qlcnic_set_channels(struct net_device *dev, | ||||
|  | @ -646,18 +654,27 @@ static int qlcnic_set_channels(struct net_device *dev, | |||
| { | ||||
| 	struct qlcnic_adapter *adapter = netdev_priv(dev); | ||||
| 	int err; | ||||
| 	int txq = 0; | ||||
| 
 | ||||
| 	if (channel->other_count || channel->combined_count || | ||||
| 	    channel->tx_count != channel->max_tx) | ||||
| 	if (channel->other_count || channel->combined_count) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	err = qlcnic_validate_max_rss(adapter, channel->rx_count); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 	if (channel->rx_count) { | ||||
| 		err = qlcnic_validate_max_rss(adapter, channel->rx_count); | ||||
| 		if (err) | ||||
| 			return err; | ||||
| 	} | ||||
| 
 | ||||
| 	err = qlcnic_set_max_rss(adapter, channel->rx_count, 0); | ||||
| 	netdev_info(dev, "allocated 0x%x sds rings\n", | ||||
| 				 adapter->max_sds_rings); | ||||
| 	if (channel->tx_count) { | ||||
| 		err = qlcnic_validate_max_tx_rings(adapter, channel->tx_count); | ||||
| 		if (err) | ||||
| 			return err; | ||||
| 		txq = channel->tx_count; | ||||
| 	} | ||||
| 
 | ||||
| 	err = qlcnic_set_max_rss(adapter, channel->rx_count, txq); | ||||
| 	netdev_info(dev, "allocated 0x%x sds rings and  0x%x tx rings\n", | ||||
| 		    adapter->max_sds_rings, adapter->max_drv_tx_rings); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
|  | @ -893,6 +910,7 @@ free_diag_res: | |||
| clear_diag_irq: | ||||
| 	adapter->max_sds_rings = max_sds_rings; | ||||
| 	clear_bit(__QLCNIC_RESETTING, &adapter->state); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
|  | @ -1077,11 +1095,21 @@ qlcnic_get_strings(struct net_device *dev, u32 stringset, u8 *data) | |||
| 		       QLCNIC_TEST_LEN * ETH_GSTRING_LEN); | ||||
| 		break; | ||||
| 	case ETH_SS_STATS: | ||||
| 		num_stats = ARRAY_SIZE(qlcnic_tx_ring_stats_strings); | ||||
| 		for (i = 0; i < adapter->max_drv_tx_rings; i++) { | ||||
| 			for (index = 0; index < num_stats; index++) { | ||||
| 				sprintf(data, "tx_ring_%d %s", i, | ||||
| 					qlcnic_tx_ring_stats_strings[index]); | ||||
| 				data += ETH_GSTRING_LEN; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		for (index = 0; index < QLCNIC_STATS_LEN; index++) { | ||||
| 			memcpy(data + index * ETH_GSTRING_LEN, | ||||
| 			       qlcnic_gstrings_stats[index].stat_string, | ||||
| 			       ETH_GSTRING_LEN); | ||||
| 		} | ||||
| 
 | ||||
| 		if (qlcnic_83xx_check(adapter)) { | ||||
| 			num_stats = ARRAY_SIZE(qlcnic_83xx_tx_stats_strings); | ||||
| 			for (i = 0; i < num_stats; i++, index++) | ||||
|  | @ -1173,11 +1201,22 @@ static void qlcnic_get_ethtool_stats(struct net_device *dev, | |||
| 				     struct ethtool_stats *stats, u64 *data) | ||||
| { | ||||
| 	struct qlcnic_adapter *adapter = netdev_priv(dev); | ||||
| 	struct qlcnic_host_tx_ring *tx_ring; | ||||
| 	struct qlcnic_esw_statistics port_stats; | ||||
| 	struct qlcnic_mac_statistics mac_stats; | ||||
| 	int index, ret, length, size; | ||||
| 	int index, ret, length, size, ring; | ||||
| 	char *p; | ||||
| 
 | ||||
| 	memset(data, 0, adapter->max_drv_tx_rings * 4 * sizeof(u64)); | ||||
| 	for (ring = 0, index = 0; ring < adapter->max_drv_tx_rings; ring++) { | ||||
| 		if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) { | ||||
| 			tx_ring = &adapter->tx_ring[ring]; | ||||
| 			*data++ = tx_ring->xmit_on; | ||||
| 			*data++ = tx_ring->xmit_off; | ||||
| 			*data++ = tx_ring->xmit_called; | ||||
| 			*data++ = tx_ring->xmit_finished; | ||||
| 		} | ||||
| 	} | ||||
| 	memset(data, 0, stats->n_stats * sizeof(u64)); | ||||
| 	length = QLCNIC_STATS_LEN; | ||||
| 	for (index = 0; index < length; index++) { | ||||
|  |  | |||
|  | @ -150,7 +150,6 @@ struct ethtool_stats; | |||
| struct pci_device_id; | ||||
| struct qlcnic_host_sds_ring; | ||||
| struct qlcnic_host_tx_ring; | ||||
| struct qlcnic_host_tx_ring; | ||||
| struct qlcnic_hardware_context; | ||||
| struct qlcnic_adapter; | ||||
| 
 | ||||
|  | @ -174,7 +173,7 @@ int qlcnic_82xx_set_lb_mode(struct qlcnic_adapter *, u8); | |||
| void qlcnic_82xx_write_crb(struct qlcnic_adapter *, char *, loff_t, size_t); | ||||
| void qlcnic_82xx_read_crb(struct qlcnic_adapter *, char *, loff_t, size_t); | ||||
| void qlcnic_82xx_dev_request_reset(struct qlcnic_adapter *, u32); | ||||
| int qlcnic_82xx_setup_intr(struct qlcnic_adapter *, u8); | ||||
| int qlcnic_82xx_setup_intr(struct qlcnic_adapter *, u8, int); | ||||
| irqreturn_t qlcnic_82xx_clear_legacy_intr(struct qlcnic_adapter *); | ||||
| int qlcnic_82xx_issue_cmd(struct qlcnic_adapter *adapter, | ||||
| 			  struct qlcnic_cmd_args *); | ||||
|  |  | |||
|  | @ -656,7 +656,7 @@ static int qlcnic_enable_msi_legacy(struct qlcnic_adapter *adapter) | |||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| int qlcnic_82xx_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr) | ||||
| int qlcnic_82xx_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr, int txq) | ||||
| { | ||||
| 	struct qlcnic_hardware_context *ahw = adapter->ahw; | ||||
| 	int num_msix, err = 0; | ||||
|  | @ -667,8 +667,11 @@ int qlcnic_82xx_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr) | |||
| 	if (ahw->msix_supported) { | ||||
| 		num_msix = rounddown_pow_of_two(min_t(int, num_online_cpus(), | ||||
| 						num_intr)); | ||||
| 		if (qlcnic_check_multi_tx(adapter)) | ||||
| 		if (qlcnic_check_multi_tx(adapter)) { | ||||
| 			if (txq) | ||||
| 				adapter->max_drv_tx_rings = txq; | ||||
| 			num_msix += adapter->max_drv_tx_rings; | ||||
| 		} | ||||
| 	} else { | ||||
| 		num_msix = 1; | ||||
| 	} | ||||
|  | @ -1990,11 +1993,9 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev, | |||
| 	netdev->priv_flags |= IFF_UNICAST_FLT; | ||||
| 	netdev->irq = adapter->msix_entries[0].vector; | ||||
| 
 | ||||
| 	if (qlcnic_82xx_check(adapter) && qlcnic_check_multi_tx(adapter)) { | ||||
| 		err = qlcnic_set_real_num_queues(adapter, netdev); | ||||
| 		if (err) | ||||
| 			return err; | ||||
| 	} | ||||
| 	err = qlcnic_set_real_num_queues(adapter, netdev); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	err = register_netdev(netdev); | ||||
| 	if (err) { | ||||
|  | @ -2253,7 +2254,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
| 			 "Device does not support MSI interrupts\n"); | ||||
| 
 | ||||
| 	if (qlcnic_82xx_check(adapter)) { | ||||
| 		err = qlcnic_setup_intr(adapter, 0); | ||||
| 		err = qlcnic_setup_intr(adapter, 0, 0); | ||||
| 		if (err) { | ||||
| 			dev_err(&pdev->dev, "Failed to setup interrupt\n"); | ||||
| 			goto err_out_disable_msi; | ||||
|  | @ -3371,7 +3372,7 @@ static int qlcnic_attach_func(struct pci_dev *pdev) | |||
| 	qlcnic_clr_drv_state(adapter); | ||||
| 	kfree(adapter->msix_entries); | ||||
| 	adapter->msix_entries = NULL; | ||||
| 	err = qlcnic_setup_intr(adapter, 0); | ||||
| 	err = qlcnic_setup_intr(adapter, 0, 0); | ||||
| 
 | ||||
| 	if (err) { | ||||
| 		kfree(adapter->msix_entries); | ||||
|  | @ -3496,6 +3497,49 @@ qlcnicvf_start_firmware(struct qlcnic_adapter *adapter) | |||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| int qlcnic_validate_max_tx_rings(struct qlcnic_adapter *adapter, int txq) | ||||
| { | ||||
| 	struct net_device *netdev = adapter->netdev; | ||||
| 	u8 max_hw = QLCNIC_MAX_TX_RINGS; | ||||
| 	u32 max_allowed; | ||||
| 
 | ||||
| 	if (!qlcnic_82xx_check(adapter)) { | ||||
| 		netdev_err(netdev, "No Multi TX-Q support\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!qlcnic_use_msi_x && !qlcnic_use_msi) { | ||||
| 		netdev_err(netdev, "No Multi TX-Q support in INT-x mode\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!qlcnic_check_multi_tx(adapter)) { | ||||
| 		netdev_err(netdev, "No Multi TX-Q support\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (txq > QLCNIC_MAX_TX_RINGS) { | ||||
| 		netdev_err(netdev, "Invalid ring count\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	max_allowed = rounddown_pow_of_two(min_t(int, max_hw, | ||||
| 						 num_online_cpus())); | ||||
| 	if ((txq > max_allowed) || !is_power_of_2(txq)) { | ||||
| 		if (!is_power_of_2(txq)) | ||||
| 			netdev_err(netdev, | ||||
| 				   "TX queue should be a power of 2\n"); | ||||
| 		if (txq > num_online_cpus()) | ||||
| 			netdev_err(netdev, | ||||
| 				   "Tx queue should not be higher than [%u], number of online CPUs in the system\n", | ||||
| 				   num_online_cpus()); | ||||
| 		netdev_err(netdev, "Unable to configure %u Tx rings\n", txq); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int qlcnic_validate_max_rss(struct qlcnic_adapter *adapter, | ||||
| 				__u32 val) | ||||
| { | ||||
|  | @ -3503,6 +3547,12 @@ int qlcnic_validate_max_rss(struct qlcnic_adapter *adapter, | |||
| 	u8 max_hw = adapter->ahw->max_rx_ques; | ||||
| 	u32 max_allowed; | ||||
| 
 | ||||
| 	if (qlcnic_82xx_check(adapter) && !qlcnic_use_msi_x && | ||||
| 	    !qlcnic_use_msi) { | ||||
| 		netdev_err(netdev, "No RSS support in INT-x mode\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (val > QLCNIC_MAX_SDS_RINGS) { | ||||
| 		netdev_err(netdev, "RSS value should not be higher than %u\n", | ||||
| 			   QLCNIC_MAX_SDS_RINGS); | ||||
|  | @ -3535,27 +3585,48 @@ int qlcnic_validate_max_rss(struct qlcnic_adapter *adapter, | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data, size_t len) | ||||
| int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data, int txq) | ||||
| { | ||||
| 	int err; | ||||
| 	struct net_device *netdev = adapter->netdev; | ||||
| 	int num_msix; | ||||
| 
 | ||||
| 	if (test_bit(__QLCNIC_RESETTING, &adapter->state)) | ||||
| 		return -EBUSY; | ||||
| 
 | ||||
| 	if (qlcnic_82xx_check(adapter) && !qlcnic_use_msi_x && | ||||
| 	    !qlcnic_use_msi) { | ||||
| 		netdev_err(netdev, "No RSS support in INT-x mode\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	netif_device_detach(netdev); | ||||
| 	if (netif_running(netdev)) | ||||
| 		__qlcnic_down(adapter, netdev); | ||||
| 
 | ||||
| 	qlcnic_detach(adapter); | ||||
| 
 | ||||
| 	if (qlcnic_82xx_check(adapter)) { | ||||
| 		if (txq != 0) | ||||
| 			adapter->max_drv_tx_rings = txq; | ||||
| 
 | ||||
| 		if (qlcnic_check_multi_tx(adapter) && | ||||
| 		    (txq > adapter->max_drv_tx_rings)) | ||||
| 			num_msix = adapter->max_drv_tx_rings; | ||||
| 		else | ||||
| 			num_msix = data; | ||||
| 	} | ||||
| 
 | ||||
| 	if (qlcnic_83xx_check(adapter)) { | ||||
| 		qlcnic_83xx_free_mbx_intr(adapter); | ||||
| 		qlcnic_83xx_enable_mbx_poll(adapter); | ||||
| 	} | ||||
| 
 | ||||
| 	netif_set_real_num_tx_queues(netdev, adapter->max_drv_tx_rings); | ||||
| 
 | ||||
| 	qlcnic_teardown_intr(adapter); | ||||
| 	err = qlcnic_setup_intr(adapter, data); | ||||
| 
 | ||||
| 	err = qlcnic_setup_intr(adapter, data, txq); | ||||
| 	if (err) { | ||||
| 		kfree(adapter->msix_entries); | ||||
| 		netdev_err(netdev, "failed to setup interrupt\n"); | ||||
|  | @ -3583,8 +3654,7 @@ int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data, size_t len) | |||
| 			goto done; | ||||
| 		qlcnic_restore_indev_addr(netdev, NETDEV_UP); | ||||
| 	} | ||||
| 	err = len; | ||||
|  done: | ||||
| done: | ||||
| 	netif_device_attach(netdev); | ||||
| 	clear_bit(__QLCNIC_RESETTING, &adapter->state); | ||||
| 	return err; | ||||
|  |  | |||
|  | @ -512,7 +512,7 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter, | |||
| 		dev_warn(&adapter->pdev->dev, | ||||
| 			 "Device does not support MSI interrupts\n"); | ||||
| 
 | ||||
| 	err = qlcnic_setup_intr(adapter, 1); | ||||
| 	err = qlcnic_setup_intr(adapter, 1, 0); | ||||
| 	if (err) { | ||||
| 		dev_err(&adapter->pdev->dev, "Failed to setup interrupt\n"); | ||||
| 		goto err_out_disable_msi; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Himanshu Madhani
				Himanshu Madhani