Commit 9fac865d authored by David S. Miller's avatar David S. Miller

Merge branch 'qlcnic'

Himanshu Madhani says:

====================
qlcnic: Refactoring and enhancements for all adapters.

This patch series contains following patches.

o Enhanced debug data collection when we are in Tx-timeout situation.
o Enhanced MSI-x vector calculation for defualt load path as well
  as for TSS/RSS ring change path.
o Refactored interrupt coalescing code for all adapters.
o Refactored interrupt handling as well as cleanup of poll controller
  code patch for all adapters.
o changed rx_mac_learn type to boolean.

Please apply to net-next.
====================
Signed-off-by: default avatarZoltan Kiss <zoltan.kiss@citrix.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 0681a282 18cae184
...@@ -38,8 +38,8 @@ ...@@ -38,8 +38,8 @@
#define _QLCNIC_LINUX_MAJOR 5 #define _QLCNIC_LINUX_MAJOR 5
#define _QLCNIC_LINUX_MINOR 3 #define _QLCNIC_LINUX_MINOR 3
#define _QLCNIC_LINUX_SUBVERSION 54 #define _QLCNIC_LINUX_SUBVERSION 55
#define QLCNIC_LINUX_VERSIONID "5.3.54" #define QLCNIC_LINUX_VERSIONID "5.3.55"
#define QLCNIC_DRV_IDC_VER 0x01 #define QLCNIC_DRV_IDC_VER 0x01
#define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\ #define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\
(_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION)) (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION))
...@@ -105,6 +105,8 @@ ...@@ -105,6 +105,8 @@
#define QLCNIC_DEF_TX_RINGS 4 #define QLCNIC_DEF_TX_RINGS 4
#define QLCNIC_MAX_VNIC_TX_RINGS 4 #define QLCNIC_MAX_VNIC_TX_RINGS 4
#define QLCNIC_MAX_VNIC_SDS_RINGS 4 #define QLCNIC_MAX_VNIC_SDS_RINGS 4
#define QLCNIC_83XX_MINIMUM_VECTOR 3
#define QLCNIC_82XX_MINIMUM_VECTOR 2
enum qlcnic_queue_type { enum qlcnic_queue_type {
QLCNIC_TX_QUEUE = 1, QLCNIC_TX_QUEUE = 1,
...@@ -369,6 +371,7 @@ struct qlcnic_rx_buffer { ...@@ -369,6 +371,7 @@ struct qlcnic_rx_buffer {
*/ */
#define QLCNIC_INTR_COAL_TYPE_RX 1 #define QLCNIC_INTR_COAL_TYPE_RX 1
#define QLCNIC_INTR_COAL_TYPE_TX 2 #define QLCNIC_INTR_COAL_TYPE_TX 2
#define QLCNIC_INTR_COAL_TYPE_RX_TX 3
#define QLCNIC_DEF_INTR_COALESCE_RX_TIME_US 3 #define QLCNIC_DEF_INTR_COALESCE_RX_TIME_US 3
#define QLCNIC_DEF_INTR_COALESCE_RX_PACKETS 256 #define QLCNIC_DEF_INTR_COALESCE_RX_PACKETS 256
...@@ -961,6 +964,7 @@ struct qlcnic_ipaddr { ...@@ -961,6 +964,7 @@ struct qlcnic_ipaddr {
#define QLCNIC_TX_INTR_SHARED 0x10000 #define QLCNIC_TX_INTR_SHARED 0x10000
#define QLCNIC_APP_CHANGED_FLAGS 0x20000 #define QLCNIC_APP_CHANGED_FLAGS 0x20000
#define QLCNIC_HAS_PHYS_PORT_ID 0x40000 #define QLCNIC_HAS_PHYS_PORT_ID 0x40000
#define QLCNIC_TSS_RSS 0x80000
#define QLCNIC_IS_MSI_FAMILY(adapter) \ #define QLCNIC_IS_MSI_FAMILY(adapter) \
((adapter)->flags & (QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED)) ((adapter)->flags & (QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED))
...@@ -1057,6 +1061,9 @@ struct qlcnic_adapter { ...@@ -1057,6 +1061,9 @@ struct qlcnic_adapter {
u8 drv_tx_rings; /* max tx rings supported by driver */ u8 drv_tx_rings; /* max tx rings supported by driver */
u8 drv_sds_rings; /* max sds rings supported by driver */ u8 drv_sds_rings; /* max sds rings supported by driver */
u8 drv_tss_rings; /* tss ring input */
u8 drv_rss_rings; /* rss ring input */
u8 rx_csum; u8 rx_csum;
u8 portnum; u8 portnum;
...@@ -1082,7 +1089,7 @@ struct qlcnic_adapter { ...@@ -1082,7 +1089,7 @@ struct qlcnic_adapter {
u64 dev_rst_time; u64 dev_rst_time;
bool drv_mac_learn; bool drv_mac_learn;
bool fdb_mac_learn; bool fdb_mac_learn;
u8 rx_mac_learn; bool rx_mac_learn;
unsigned long vlans[BITS_TO_LONGS(VLAN_N_VID)]; unsigned long vlans[BITS_TO_LONGS(VLAN_N_VID)];
u8 flash_mfg_id; u8 flash_mfg_id;
struct qlcnic_npar_info *npars; struct qlcnic_npar_info *npars;
...@@ -1573,7 +1580,7 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int); ...@@ -1573,7 +1580,7 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int);
netdev_tx_t qlcnic_xmit_frame(struct sk_buff *, struct net_device *); netdev_tx_t qlcnic_xmit_frame(struct sk_buff *, struct net_device *);
void qlcnic_set_tx_ring_count(struct qlcnic_adapter *, u8); void qlcnic_set_tx_ring_count(struct qlcnic_adapter *, u8);
void qlcnic_set_sds_ring_count(struct qlcnic_adapter *, u8); void qlcnic_set_sds_ring_count(struct qlcnic_adapter *, u8);
int qlcnic_setup_rings(struct qlcnic_adapter *, u8, u8); int qlcnic_setup_rings(struct qlcnic_adapter *);
int qlcnic_validate_rings(struct qlcnic_adapter *, __u32, int); int qlcnic_validate_rings(struct qlcnic_adapter *, __u32, int);
void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter); void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter);
int qlcnic_enable_msix(struct qlcnic_adapter *, u32); int qlcnic_enable_msix(struct qlcnic_adapter *, u32);
...@@ -1613,7 +1620,7 @@ void qlcnic_set_vlan_config(struct qlcnic_adapter *, ...@@ -1613,7 +1620,7 @@ void qlcnic_set_vlan_config(struct qlcnic_adapter *,
struct qlcnic_esw_func_cfg *); struct qlcnic_esw_func_cfg *);
void qlcnic_set_eswitch_port_features(struct qlcnic_adapter *, void qlcnic_set_eswitch_port_features(struct qlcnic_adapter *,
struct qlcnic_esw_func_cfg *); struct qlcnic_esw_func_cfg *);
int qlcnic_setup_tss_rss_intr(struct qlcnic_adapter *);
void qlcnic_down(struct qlcnic_adapter *, struct net_device *); void qlcnic_down(struct qlcnic_adapter *, struct net_device *);
int qlcnic_up(struct qlcnic_adapter *, struct net_device *); int qlcnic_up(struct qlcnic_adapter *, struct net_device *);
void __qlcnic_down(struct qlcnic_adapter *, struct net_device *); void __qlcnic_down(struct qlcnic_adapter *, struct net_device *);
...@@ -1670,11 +1677,8 @@ static inline int qlcnic_set_real_num_queues(struct qlcnic_adapter *adapter, ...@@ -1670,11 +1677,8 @@ static inline int qlcnic_set_real_num_queues(struct qlcnic_adapter *adapter,
err = netif_set_real_num_tx_queues(netdev, adapter->drv_tx_rings); err = netif_set_real_num_tx_queues(netdev, adapter->drv_tx_rings);
if (err) if (err)
dev_err(&adapter->pdev->dev, "failed to set %d Tx queues\n", netdev_err(netdev, "failed to set %d Tx queues\n",
adapter->drv_tx_rings); adapter->drv_tx_rings);
else
dev_info(&adapter->pdev->dev, "Set %d Tx queues\n",
adapter->drv_tx_rings);
return err; return err;
} }
...@@ -1740,7 +1744,8 @@ struct qlcnic_hardware_ops { ...@@ -1740,7 +1744,8 @@ struct qlcnic_hardware_ops {
int (*change_macvlan) (struct qlcnic_adapter *, u8*, u16, u8); int (*change_macvlan) (struct qlcnic_adapter *, u8*, u16, u8);
void (*napi_enable) (struct qlcnic_adapter *); void (*napi_enable) (struct qlcnic_adapter *);
void (*napi_disable) (struct qlcnic_adapter *); void (*napi_disable) (struct qlcnic_adapter *);
void (*config_intr_coal) (struct qlcnic_adapter *); int (*config_intr_coal) (struct qlcnic_adapter *,
struct ethtool_coalesce *);
int (*config_rss) (struct qlcnic_adapter *, int); int (*config_rss) (struct qlcnic_adapter *, int);
int (*config_hw_lro) (struct qlcnic_adapter *, int); int (*config_hw_lro) (struct qlcnic_adapter *, int);
int (*config_loopback) (struct qlcnic_adapter *, u8); int (*config_loopback) (struct qlcnic_adapter *, u8);
...@@ -1756,6 +1761,14 @@ struct qlcnic_hardware_ops { ...@@ -1756,6 +1761,14 @@ struct qlcnic_hardware_ops {
pci_ers_result_t (*io_slot_reset) (struct pci_dev *); pci_ers_result_t (*io_slot_reset) (struct pci_dev *);
void (*io_resume) (struct pci_dev *); void (*io_resume) (struct pci_dev *);
void (*get_beacon_state)(struct qlcnic_adapter *); void (*get_beacon_state)(struct qlcnic_adapter *);
void (*enable_sds_intr) (struct qlcnic_adapter *,
struct qlcnic_host_sds_ring *);
void (*disable_sds_intr) (struct qlcnic_adapter *,
struct qlcnic_host_sds_ring *);
void (*enable_tx_intr) (struct qlcnic_adapter *,
struct qlcnic_host_tx_ring *);
void (*disable_tx_intr) (struct qlcnic_adapter *,
struct qlcnic_host_tx_ring *);
}; };
extern struct qlcnic_nic_template qlcnic_vf_ops; extern struct qlcnic_nic_template qlcnic_vf_ops;
...@@ -1928,9 +1941,10 @@ static inline void qlcnic_napi_disable(struct qlcnic_adapter *adapter) ...@@ -1928,9 +1941,10 @@ static inline void qlcnic_napi_disable(struct qlcnic_adapter *adapter)
adapter->ahw->hw_ops->napi_disable(adapter); adapter->ahw->hw_ops->napi_disable(adapter);
} }
static inline void qlcnic_config_intr_coalesce(struct qlcnic_adapter *adapter) static inline int qlcnic_config_intr_coalesce(struct qlcnic_adapter *adapter,
struct ethtool_coalesce *ethcoal)
{ {
adapter->ahw->hw_ops->config_intr_coal(adapter); return adapter->ahw->hw_ops->config_intr_coal(adapter, ethcoal);
} }
static inline int qlcnic_config_rss(struct qlcnic_adapter *adapter, int enable) static inline int qlcnic_config_rss(struct qlcnic_adapter *adapter, int enable)
...@@ -2029,6 +2043,54 @@ static inline bool qlcnic_check_multi_tx(struct qlcnic_adapter *adapter) ...@@ -2029,6 +2043,54 @@ static inline bool qlcnic_check_multi_tx(struct qlcnic_adapter *adapter)
return test_bit(__QLCNIC_MULTI_TX_UNIQUE, &adapter->state); return test_bit(__QLCNIC_MULTI_TX_UNIQUE, &adapter->state);
} }
static inline void
qlcnic_82xx_enable_tx_intr(struct qlcnic_adapter *adapter,
struct qlcnic_host_tx_ring *tx_ring)
{
if (qlcnic_check_multi_tx(adapter) &&
!adapter->ahw->diag_test)
writel(0x0, tx_ring->crb_intr_mask);
}
static inline void
qlcnic_82xx_disable_tx_intr(struct qlcnic_adapter *adapter,
struct qlcnic_host_tx_ring *tx_ring)
{
if (qlcnic_check_multi_tx(adapter) &&
!adapter->ahw->diag_test)
writel(1, tx_ring->crb_intr_mask);
}
static inline void
qlcnic_83xx_enable_tx_intr(struct qlcnic_adapter *adapter,
struct qlcnic_host_tx_ring *tx_ring)
{
writel(0, tx_ring->crb_intr_mask);
}
static inline void
qlcnic_83xx_disable_tx_intr(struct qlcnic_adapter *adapter,
struct qlcnic_host_tx_ring *tx_ring)
{
writel(1, tx_ring->crb_intr_mask);
}
/* Enable MSI-x and INT-x interrupts */
static inline void
qlcnic_83xx_enable_sds_intr(struct qlcnic_adapter *adapter,
struct qlcnic_host_sds_ring *sds_ring)
{
writel(0, sds_ring->crb_intr_mask);
}
/* Disable MSI-x and INT-x interrupts */
static inline void
qlcnic_83xx_disable_sds_intr(struct qlcnic_adapter *adapter,
struct qlcnic_host_sds_ring *sds_ring)
{
writel(1, sds_ring->crb_intr_mask);
}
static inline void qlcnic_disable_multi_tx(struct qlcnic_adapter *adapter) static inline void qlcnic_disable_multi_tx(struct qlcnic_adapter *adapter)
{ {
test_and_clear_bit(__QLCNIC_MULTI_TX_UNIQUE, &adapter->state); test_and_clear_bit(__QLCNIC_MULTI_TX_UNIQUE, &adapter->state);
...@@ -2038,10 +2100,10 @@ static inline void qlcnic_disable_multi_tx(struct qlcnic_adapter *adapter) ...@@ -2038,10 +2100,10 @@ static inline void qlcnic_disable_multi_tx(struct qlcnic_adapter *adapter)
/* When operating in a muti tx mode, driver needs to write 0x1 /* When operating in a muti tx mode, driver needs to write 0x1
* to src register, instead of 0x0 to disable receiving interrupt. * to src register, instead of 0x0 to disable receiving interrupt.
*/ */
static inline void qlcnic_disable_int(struct qlcnic_host_sds_ring *sds_ring) static inline void
qlcnic_82xx_disable_sds_intr(struct qlcnic_adapter *adapter,
struct qlcnic_host_sds_ring *sds_ring)
{ {
struct qlcnic_adapter *adapter = sds_ring->adapter;
if (qlcnic_check_multi_tx(adapter) && if (qlcnic_check_multi_tx(adapter) &&
!adapter->ahw->diag_test && !adapter->ahw->diag_test &&
(adapter->flags & QLCNIC_MSIX_ENABLED)) (adapter->flags & QLCNIC_MSIX_ENABLED))
...@@ -2050,13 +2112,42 @@ static inline void qlcnic_disable_int(struct qlcnic_host_sds_ring *sds_ring) ...@@ -2050,13 +2112,42 @@ static inline void qlcnic_disable_int(struct qlcnic_host_sds_ring *sds_ring)
writel(0, sds_ring->crb_intr_mask); writel(0, sds_ring->crb_intr_mask);
} }
static inline void qlcnic_enable_sds_intr(struct qlcnic_adapter *adapter,
struct qlcnic_host_sds_ring *sds_ring)
{
if (adapter->ahw->hw_ops->enable_sds_intr)
adapter->ahw->hw_ops->enable_sds_intr(adapter, sds_ring);
}
static inline void
qlcnic_disable_sds_intr(struct qlcnic_adapter *adapter,
struct qlcnic_host_sds_ring *sds_ring)
{
if (adapter->ahw->hw_ops->disable_sds_intr)
adapter->ahw->hw_ops->disable_sds_intr(adapter, sds_ring);
}
static inline void qlcnic_enable_tx_intr(struct qlcnic_adapter *adapter,
struct qlcnic_host_tx_ring *tx_ring)
{
if (adapter->ahw->hw_ops->enable_tx_intr)
adapter->ahw->hw_ops->enable_tx_intr(adapter, tx_ring);
}
static inline void qlcnic_disable_tx_intr(struct qlcnic_adapter *adapter,
struct qlcnic_host_tx_ring *tx_ring)
{
if (adapter->ahw->hw_ops->disable_tx_intr)
adapter->ahw->hw_ops->disable_tx_intr(adapter, tx_ring);
}
/* When operating in a muti tx mode, driver needs to write 0x0 /* When operating in a muti tx mode, driver needs to write 0x0
* to src register, instead of 0x1 to enable receiving interrupts. * to src register, instead of 0x1 to enable receiving interrupts.
*/ */
static inline void qlcnic_enable_int(struct qlcnic_host_sds_ring *sds_ring) static inline void
qlcnic_82xx_enable_sds_intr(struct qlcnic_adapter *adapter,
struct qlcnic_host_sds_ring *sds_ring)
{ {
struct qlcnic_adapter *adapter = sds_ring->adapter;
if (qlcnic_check_multi_tx(adapter) && if (qlcnic_check_multi_tx(adapter) &&
!adapter->ahw->diag_test && !adapter->ahw->diag_test &&
(adapter->flags & QLCNIC_MSIX_ENABLED)) (adapter->flags & QLCNIC_MSIX_ENABLED))
......
...@@ -547,7 +547,6 @@ int qlcnic_83xx_wrt_reg_indirect(struct qlcnic_adapter *, ulong, u32); ...@@ -547,7 +547,6 @@ int qlcnic_83xx_wrt_reg_indirect(struct qlcnic_adapter *, ulong, u32);
int qlcnic_83xx_nic_set_promisc(struct qlcnic_adapter *, u32); int qlcnic_83xx_nic_set_promisc(struct qlcnic_adapter *, u32);
int qlcnic_83xx_config_hw_lro(struct qlcnic_adapter *, int); int qlcnic_83xx_config_hw_lro(struct qlcnic_adapter *, int);
int qlcnic_83xx_config_rss(struct qlcnic_adapter *, int); int qlcnic_83xx_config_rss(struct qlcnic_adapter *, int);
int qlcnic_83xx_config_intr_coalesce(struct qlcnic_adapter *);
void qlcnic_83xx_change_l2_filter(struct qlcnic_adapter *, u64 *, u16); void qlcnic_83xx_change_l2_filter(struct qlcnic_adapter *, u64 *, u16);
int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *, struct qlcnic_pci_info *); int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *, struct qlcnic_pci_info *);
int qlcnic_83xx_set_nic_info(struct qlcnic_adapter *, struct qlcnic_info *); int qlcnic_83xx_set_nic_info(struct qlcnic_adapter *, struct qlcnic_info *);
...@@ -577,17 +576,15 @@ int qlcnic_83xx_alloc_mbx_args(struct qlcnic_cmd_args *, ...@@ -577,17 +576,15 @@ int qlcnic_83xx_alloc_mbx_args(struct qlcnic_cmd_args *,
void qlcnic_free_mbx_args(struct qlcnic_cmd_args *); void qlcnic_free_mbx_args(struct qlcnic_cmd_args *);
void qlcnic_set_npar_data(struct qlcnic_adapter *, const struct qlcnic_info *, void qlcnic_set_npar_data(struct qlcnic_adapter *, const struct qlcnic_info *,
struct qlcnic_info *); struct qlcnic_info *);
void qlcnic_83xx_config_intr_coal(struct qlcnic_adapter *); int qlcnic_83xx_config_intr_coal(struct qlcnic_adapter *,
struct ethtool_coalesce *);
int qlcnic_83xx_set_rx_tx_intr_coal(struct qlcnic_adapter *);
int qlcnic_83xx_get_port_info(struct qlcnic_adapter *); int qlcnic_83xx_get_port_info(struct qlcnic_adapter *);
void qlcnic_83xx_enable_mbx_interrupt(struct qlcnic_adapter *); void qlcnic_83xx_enable_mbx_interrupt(struct qlcnic_adapter *);
void qlcnic_83xx_disable_mbx_intr(struct qlcnic_adapter *); void qlcnic_83xx_disable_mbx_intr(struct qlcnic_adapter *);
irqreturn_t qlcnic_83xx_clear_legacy_intr(struct qlcnic_adapter *); irqreturn_t qlcnic_83xx_clear_legacy_intr(struct qlcnic_adapter *);
irqreturn_t qlcnic_83xx_intr(int, void *); irqreturn_t qlcnic_83xx_intr(int, void *);
irqreturn_t qlcnic_83xx_tmp_intr(int, void *); irqreturn_t qlcnic_83xx_tmp_intr(int, void *);
void qlcnic_83xx_enable_intr(struct qlcnic_adapter *,
struct qlcnic_host_sds_ring *);
void qlcnic_83xx_disable_intr(struct qlcnic_adapter *,
struct qlcnic_host_sds_ring *);
void qlcnic_83xx_check_vf(struct qlcnic_adapter *, void qlcnic_83xx_check_vf(struct qlcnic_adapter *,
const struct pci_device_id *); const struct pci_device_id *);
int qlcnic_83xx_config_default_opmode(struct qlcnic_adapter *); int qlcnic_83xx_config_default_opmode(struct qlcnic_adapter *);
......
...@@ -383,7 +383,7 @@ static int qlcnic_83xx_idc_tx_soft_reset(struct qlcnic_adapter *adapter) ...@@ -383,7 +383,7 @@ static int qlcnic_83xx_idc_tx_soft_reset(struct qlcnic_adapter *adapter)
qlcnic_up(adapter, netdev); qlcnic_up(adapter, netdev);
netif_device_attach(netdev); netif_device_attach(netdev);
clear_bit(__QLCNIC_RESETTING, &adapter->state); clear_bit(__QLCNIC_RESETTING, &adapter->state);
dev_err(&adapter->pdev->dev, "%s:\n", __func__); netdev_info(adapter->netdev, "%s: soft reset complete.\n", __func__);
return 0; return 0;
} }
...@@ -2217,7 +2217,7 @@ int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac) ...@@ -2217,7 +2217,7 @@ int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac)
struct qlcnic_hardware_context *ahw = adapter->ahw; struct qlcnic_hardware_context *ahw = adapter->ahw;
int err = 0; int err = 0;
adapter->rx_mac_learn = 0; adapter->rx_mac_learn = false;
ahw->msix_supported = !!qlcnic_use_msi_x; ahw->msix_supported = !!qlcnic_use_msi_x;
qlcnic_83xx_init_rings(adapter); qlcnic_83xx_init_rings(adapter);
......
...@@ -227,10 +227,10 @@ int qlcnic_83xx_config_vnic_opmode(struct qlcnic_adapter *adapter) ...@@ -227,10 +227,10 @@ int qlcnic_83xx_config_vnic_opmode(struct qlcnic_adapter *adapter)
if (ahw->capabilities & QLC_83XX_ESWITCH_CAPABILITY) { if (ahw->capabilities & QLC_83XX_ESWITCH_CAPABILITY) {
adapter->flags |= QLCNIC_ESWITCH_ENABLED; adapter->flags |= QLCNIC_ESWITCH_ENABLED;
if (adapter->drv_mac_learn) if (adapter->drv_mac_learn)
adapter->rx_mac_learn = 1; adapter->rx_mac_learn = true;
} else { } else {
adapter->flags &= ~QLCNIC_ESWITCH_ENABLED; adapter->flags &= ~QLCNIC_ESWITCH_ENABLED;
adapter->rx_mac_learn = 0; adapter->rx_mac_learn = false;
} }
ahw->idc.vnic_state = QLCNIC_DEV_NPAR_NON_OPER; ahw->idc.vnic_state = QLCNIC_DEV_NPAR_NON_OPER;
......
...@@ -736,6 +736,7 @@ static int qlcnic_set_channels(struct net_device *dev, ...@@ -736,6 +736,7 @@ static int qlcnic_set_channels(struct net_device *dev,
channel->rx_count); channel->rx_count);
return err; return err;
} }
adapter->drv_rss_rings = channel->rx_count;
} }
if (channel->tx_count) { if (channel->tx_count) {
...@@ -746,10 +747,12 @@ static int qlcnic_set_channels(struct net_device *dev, ...@@ -746,10 +747,12 @@ static int qlcnic_set_channels(struct net_device *dev,
channel->tx_count); channel->tx_count);
return err; return err;
} }
adapter->drv_tss_rings = channel->tx_count;
} }
err = qlcnic_setup_rings(adapter, channel->rx_count, adapter->flags |= QLCNIC_TSS_RSS;
channel->tx_count);
err = qlcnic_setup_rings(adapter);
netdev_info(dev, "Allocated %d SDS rings and %d Tx rings\n", netdev_info(dev, "Allocated %d SDS rings and %d Tx rings\n",
adapter->drv_sds_rings, adapter->drv_tx_rings); adapter->drv_sds_rings, adapter->drv_tx_rings);
...@@ -1495,9 +1498,7 @@ static int qlcnic_set_intr_coalesce(struct net_device *netdev, ...@@ -1495,9 +1498,7 @@ static int qlcnic_set_intr_coalesce(struct net_device *netdev,
struct ethtool_coalesce *ethcoal) struct ethtool_coalesce *ethcoal)
{ {
struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_adapter *adapter = netdev_priv(netdev);
struct qlcnic_nic_intr_coalesce *coal; int err;
u32 rx_coalesce_usecs, rx_max_frames;
u32 tx_coalesce_usecs, tx_max_frames;
if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) if (!test_bit(__QLCNIC_DEV_UP, &adapter->state))
return -EINVAL; return -EINVAL;
...@@ -1507,82 +1508,31 @@ static int qlcnic_set_intr_coalesce(struct net_device *netdev, ...@@ -1507,82 +1508,31 @@ static int qlcnic_set_intr_coalesce(struct net_device *netdev,
* unsupported parameters are set. * unsupported parameters are set.
*/ */
if (ethcoal->rx_coalesce_usecs > 0xffff || if (ethcoal->rx_coalesce_usecs > 0xffff ||
ethcoal->rx_max_coalesced_frames > 0xffff || ethcoal->rx_max_coalesced_frames > 0xffff ||
ethcoal->tx_coalesce_usecs > 0xffff || ethcoal->tx_coalesce_usecs > 0xffff ||
ethcoal->tx_max_coalesced_frames > 0xffff || ethcoal->tx_max_coalesced_frames > 0xffff ||
ethcoal->rx_coalesce_usecs_irq || ethcoal->rx_coalesce_usecs_irq ||
ethcoal->rx_max_coalesced_frames_irq || ethcoal->rx_max_coalesced_frames_irq ||
ethcoal->tx_coalesce_usecs_irq || ethcoal->tx_coalesce_usecs_irq ||
ethcoal->tx_max_coalesced_frames_irq || ethcoal->tx_max_coalesced_frames_irq ||
ethcoal->stats_block_coalesce_usecs || ethcoal->stats_block_coalesce_usecs ||
ethcoal->use_adaptive_rx_coalesce || ethcoal->use_adaptive_rx_coalesce ||
ethcoal->use_adaptive_tx_coalesce || ethcoal->use_adaptive_tx_coalesce ||
ethcoal->pkt_rate_low || ethcoal->pkt_rate_low ||
ethcoal->rx_coalesce_usecs_low || ethcoal->rx_coalesce_usecs_low ||
ethcoal->rx_max_coalesced_frames_low || ethcoal->rx_max_coalesced_frames_low ||
ethcoal->tx_coalesce_usecs_low || ethcoal->tx_coalesce_usecs_low ||
ethcoal->tx_max_coalesced_frames_low || ethcoal->tx_max_coalesced_frames_low ||
ethcoal->pkt_rate_high || ethcoal->pkt_rate_high ||
ethcoal->rx_coalesce_usecs_high || ethcoal->rx_coalesce_usecs_high ||
ethcoal->rx_max_coalesced_frames_high || ethcoal->rx_max_coalesced_frames_high ||
ethcoal->tx_coalesce_usecs_high || ethcoal->tx_coalesce_usecs_high ||
ethcoal->tx_max_coalesced_frames_high) ethcoal->tx_max_coalesced_frames_high)
return -EINVAL; return -EINVAL;
coal = &adapter->ahw->coal; err = qlcnic_config_intr_coalesce(adapter, ethcoal);
if (qlcnic_83xx_check(adapter)) {
if (!ethcoal->tx_coalesce_usecs ||
!ethcoal->tx_max_coalesced_frames ||
!ethcoal->rx_coalesce_usecs ||
!ethcoal->rx_max_coalesced_frames) {
coal->flag = QLCNIC_INTR_DEFAULT;
coal->type = QLCNIC_INTR_COAL_TYPE_RX;
coal->rx_time_us = QLCNIC_DEF_INTR_COALESCE_RX_TIME_US;
coal->rx_packets = QLCNIC_DEF_INTR_COALESCE_RX_PACKETS;
coal->tx_time_us = QLCNIC_DEF_INTR_COALESCE_TX_TIME_US;
coal->tx_packets = QLCNIC_DEF_INTR_COALESCE_TX_PACKETS;
} else {
tx_coalesce_usecs = ethcoal->tx_coalesce_usecs;
tx_max_frames = ethcoal->tx_max_coalesced_frames;
rx_coalesce_usecs = ethcoal->rx_coalesce_usecs;
rx_max_frames = ethcoal->rx_max_coalesced_frames;
coal->flag = 0;
if ((coal->rx_time_us == rx_coalesce_usecs) &&
(coal->rx_packets == rx_max_frames)) {
coal->type = QLCNIC_INTR_COAL_TYPE_TX;
coal->tx_time_us = tx_coalesce_usecs;
coal->tx_packets = tx_max_frames;
} else if ((coal->tx_time_us == tx_coalesce_usecs) &&
(coal->tx_packets == tx_max_frames)) {
coal->type = QLCNIC_INTR_COAL_TYPE_RX;
coal->rx_time_us = rx_coalesce_usecs;
coal->rx_packets = rx_max_frames;
} else {
coal->type = QLCNIC_INTR_COAL_TYPE_RX;
coal->rx_time_us = rx_coalesce_usecs;
coal->rx_packets = rx_max_frames;
coal->tx_time_us = tx_coalesce_usecs;
coal->tx_packets = tx_max_frames;
}
}
} else {
if (!ethcoal->rx_coalesce_usecs ||
!ethcoal->rx_max_coalesced_frames) {
coal->flag = QLCNIC_INTR_DEFAULT;
coal->rx_time_us = QLCNIC_DEF_INTR_COALESCE_RX_TIME_US;
coal->rx_packets = QLCNIC_DEF_INTR_COALESCE_RX_PACKETS;
} else {
coal->flag = 0;
coal->rx_time_us = ethcoal->rx_coalesce_usecs;
coal->rx_packets = ethcoal->rx_max_coalesced_frames;
}
}
qlcnic_config_intr_coalesce(adapter);
return 0; return err;
} }
static int qlcnic_get_intr_coalesce(struct net_device *netdev, static int qlcnic_get_intr_coalesce(struct net_device *netdev,
......
...@@ -547,10 +547,10 @@ static void __qlcnic_set_multi(struct net_device *netdev, u16 vlan) ...@@ -547,10 +547,10 @@ static void __qlcnic_set_multi(struct net_device *netdev, u16 vlan)
qlcnic_alloc_lb_filters_mem(adapter); qlcnic_alloc_lb_filters_mem(adapter);
adapter->drv_mac_learn = 1; adapter->drv_mac_learn = 1;
if (adapter->flags & QLCNIC_ESWITCH_ENABLED) if (adapter->flags & QLCNIC_ESWITCH_ENABLED)
adapter->rx_mac_learn = 1; adapter->rx_mac_learn = true;
} else { } else {
adapter->drv_mac_learn = 0; adapter->drv_mac_learn = 0;
adapter->rx_mac_learn = 0; adapter->rx_mac_learn = false;
} }
qlcnic_nic_set_promisc(adapter, mode); qlcnic_nic_set_promisc(adapter, mode);
...@@ -755,10 +755,7 @@ int qlcnic_82xx_read_phys_port_id(struct qlcnic_adapter *adapter) ...@@ -755,10 +755,7 @@ int qlcnic_82xx_read_phys_port_id(struct qlcnic_adapter *adapter)
return 0; return 0;
} }
/* int qlcnic_82xx_set_rx_coalesce(struct qlcnic_adapter *adapter)
* Send the interrupt coalescing parameter set by ethtool to the card.
*/
void qlcnic_82xx_config_intr_coalesce(struct qlcnic_adapter *adapter)
{ {
struct qlcnic_nic_req req; struct qlcnic_nic_req req;
int rv; int rv;
...@@ -780,6 +777,28 @@ void qlcnic_82xx_config_intr_coalesce(struct qlcnic_adapter *adapter) ...@@ -780,6 +777,28 @@ void qlcnic_82xx_config_intr_coalesce(struct qlcnic_adapter *adapter)
if (rv != 0) if (rv != 0)
dev_err(&adapter->netdev->dev, dev_err(&adapter->netdev->dev,
"Could not send interrupt coalescing parameters\n"); "Could not send interrupt coalescing parameters\n");
return rv;
}
/* Send the interrupt coalescing parameter set by ethtool to the card. */
int qlcnic_82xx_config_intr_coalesce(struct qlcnic_adapter *adapter,
struct ethtool_coalesce *ethcoal)
{
struct qlcnic_nic_intr_coalesce *coal = &adapter->ahw->coal;
int rv;
coal->flag = QLCNIC_INTR_DEFAULT;
coal->rx_time_us = ethcoal->rx_coalesce_usecs;
coal->rx_packets = ethcoal->rx_max_coalesced_frames;
rv = qlcnic_82xx_set_rx_coalesce(adapter);
if (rv)
netdev_err(adapter->netdev,
"Failed to set Rx coalescing parametrs\n");
return rv;
} }
#define QLCNIC_ENABLE_IPV4_LRO BIT_0 #define QLCNIC_ENABLE_IPV4_LRO BIT_0
......
...@@ -171,7 +171,9 @@ int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter, ...@@ -171,7 +171,9 @@ int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter,
void qlcnic_82xx_get_beacon_state(struct qlcnic_adapter *); void qlcnic_82xx_get_beacon_state(struct qlcnic_adapter *);
void qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter, void qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter,
u64 *uaddr, u16 vlan_id); u64 *uaddr, u16 vlan_id);
void qlcnic_82xx_config_intr_coalesce(struct qlcnic_adapter *adapter); int qlcnic_82xx_config_intr_coalesce(struct qlcnic_adapter *,
struct ethtool_coalesce *);
int qlcnic_82xx_set_rx_coalesce(struct qlcnic_adapter *);
int qlcnic_82xx_config_rss(struct qlcnic_adapter *adapter, int); int qlcnic_82xx_config_rss(struct qlcnic_adapter *adapter, int);
void qlcnic_82xx_config_ipaddr(struct qlcnic_adapter *adapter, void qlcnic_82xx_config_ipaddr(struct qlcnic_adapter *adapter,
__be32, int); __be32, int);
......
...@@ -131,35 +131,6 @@ static struct sk_buff *qlcnic_process_rxbuf(struct qlcnic_adapter *, ...@@ -131,35 +131,6 @@ static struct sk_buff *qlcnic_process_rxbuf(struct qlcnic_adapter *,
struct qlcnic_host_rds_ring *, struct qlcnic_host_rds_ring *,
u16, u16); u16, u16);
static inline void qlcnic_enable_tx_intr(struct qlcnic_adapter *adapter,
struct qlcnic_host_tx_ring *tx_ring)
{
if (qlcnic_check_multi_tx(adapter) &&
!adapter->ahw->diag_test)
writel(0x0, tx_ring->crb_intr_mask);
}
static inline void qlcnic_disable_tx_int(struct qlcnic_adapter *adapter,
struct qlcnic_host_tx_ring *tx_ring)
{
if (qlcnic_check_multi_tx(adapter) &&
!adapter->ahw->diag_test)
writel(1, tx_ring->crb_intr_mask);
}
static inline void qlcnic_83xx_enable_tx_intr(struct qlcnic_adapter *adapter,
struct qlcnic_host_tx_ring *tx_ring)
{
writel(0, tx_ring->crb_intr_mask);
}
static inline void qlcnic_83xx_disable_tx_intr(struct qlcnic_adapter *adapter,
struct qlcnic_host_tx_ring *tx_ring)
{
writel(1, tx_ring->crb_intr_mask);
}
static inline u8 qlcnic_mac_hash(u64 mac, u16 vlan) static inline u8 qlcnic_mac_hash(u64 mac, u16 vlan)
{ {
return (u8)((mac & 0xff) ^ ((mac >> 40) & 0xff) ^ (vlan & 0xff)); return (u8)((mac & 0xff) ^ ((mac >> 40) & 0xff) ^ (vlan & 0xff));
...@@ -885,7 +856,7 @@ static int qlcnic_poll(struct napi_struct *napi, int budget) ...@@ -885,7 +856,7 @@ static int qlcnic_poll(struct napi_struct *napi, int budget)
if ((work_done < budget) && tx_complete) { if ((work_done < budget) && tx_complete) {
napi_complete(&sds_ring->napi); napi_complete(&sds_ring->napi);
if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) { if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
qlcnic_enable_int(sds_ring); qlcnic_enable_sds_intr(adapter, sds_ring);
qlcnic_enable_tx_intr(adapter, tx_ring); qlcnic_enable_tx_intr(adapter, tx_ring);
} }
} }
...@@ -926,7 +897,7 @@ static int qlcnic_rx_poll(struct napi_struct *napi, int budget) ...@@ -926,7 +897,7 @@ static int qlcnic_rx_poll(struct napi_struct *napi, int budget)
if (work_done < budget) { if (work_done < budget) {
napi_complete(&sds_ring->napi); napi_complete(&sds_ring->napi);
if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) if (test_bit(__QLCNIC_DEV_UP, &adapter->state))
qlcnic_enable_int(sds_ring); qlcnic_enable_sds_intr(adapter, sds_ring);
} }
return work_done; return work_done;
...@@ -1557,7 +1528,7 @@ void qlcnic_82xx_napi_enable(struct qlcnic_adapter *adapter) ...@@ -1557,7 +1528,7 @@ void qlcnic_82xx_napi_enable(struct qlcnic_adapter *adapter)
for (ring = 0; ring < adapter->drv_sds_rings; ring++) { for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring]; sds_ring = &recv_ctx->sds_rings[ring];
napi_enable(&sds_ring->napi); napi_enable(&sds_ring->napi);
qlcnic_enable_int(sds_ring); qlcnic_enable_sds_intr(adapter, sds_ring);
} }
if (qlcnic_check_multi_tx(adapter) && if (qlcnic_check_multi_tx(adapter) &&
...@@ -1583,7 +1554,7 @@ void qlcnic_82xx_napi_disable(struct qlcnic_adapter *adapter) ...@@ -1583,7 +1554,7 @@ void qlcnic_82xx_napi_disable(struct qlcnic_adapter *adapter)
for (ring = 0; ring < adapter->drv_sds_rings; ring++) { for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring]; sds_ring = &recv_ctx->sds_rings[ring];
qlcnic_disable_int(sds_ring); qlcnic_disable_sds_intr(adapter, sds_ring);
napi_synchronize(&sds_ring->napi); napi_synchronize(&sds_ring->napi);
napi_disable(&sds_ring->napi); napi_disable(&sds_ring->napi);
} }
...@@ -1593,7 +1564,7 @@ void qlcnic_82xx_napi_disable(struct qlcnic_adapter *adapter) ...@@ -1593,7 +1564,7 @@ void qlcnic_82xx_napi_disable(struct qlcnic_adapter *adapter)
qlcnic_check_multi_tx(adapter)) { qlcnic_check_multi_tx(adapter)) {
for (ring = 0; ring < adapter->drv_tx_rings; ring++) { for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring]; tx_ring = &adapter->tx_ring[ring];
qlcnic_disable_tx_int(adapter, tx_ring); qlcnic_disable_tx_intr(adapter, tx_ring);
napi_synchronize(&tx_ring->napi); napi_synchronize(&tx_ring->napi);
napi_disable(&tx_ring->napi); napi_disable(&tx_ring->napi);
} }
...@@ -1856,7 +1827,7 @@ static int qlcnic_83xx_msix_sriov_vf_poll(struct napi_struct *napi, int budget) ...@@ -1856,7 +1827,7 @@ static int qlcnic_83xx_msix_sriov_vf_poll(struct napi_struct *napi, int budget)
work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget); work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget);
if ((work_done < budget) && tx_complete) { if ((work_done < budget) && tx_complete) {
napi_complete(&sds_ring->napi); napi_complete(&sds_ring->napi);
qlcnic_83xx_enable_intr(adapter, sds_ring); qlcnic_enable_sds_intr(adapter, sds_ring);
} }
return work_done; return work_done;
...@@ -1879,7 +1850,7 @@ static int qlcnic_83xx_poll(struct napi_struct *napi, int budget) ...@@ -1879,7 +1850,7 @@ static int qlcnic_83xx_poll(struct napi_struct *napi, int budget)
work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget); work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget);
if ((work_done < budget) && tx_complete) { if ((work_done < budget) && tx_complete) {
napi_complete(&sds_ring->napi); napi_complete(&sds_ring->napi);
qlcnic_83xx_enable_intr(adapter, sds_ring); qlcnic_enable_sds_intr(adapter, sds_ring);
} }
return work_done; return work_done;
...@@ -1898,7 +1869,7 @@ static int qlcnic_83xx_msix_tx_poll(struct napi_struct *napi, int budget) ...@@ -1898,7 +1869,7 @@ static int qlcnic_83xx_msix_tx_poll(struct napi_struct *napi, int budget)
if (work_done) { if (work_done) {
napi_complete(&tx_ring->napi); napi_complete(&tx_ring->napi);
if (test_bit(__QLCNIC_DEV_UP , &adapter->state)) if (test_bit(__QLCNIC_DEV_UP , &adapter->state))
qlcnic_83xx_enable_tx_intr(adapter, tx_ring); qlcnic_enable_tx_intr(adapter, tx_ring);
} }
return work_done; return work_done;
...@@ -1916,7 +1887,7 @@ static int qlcnic_83xx_rx_poll(struct napi_struct *napi, int budget) ...@@ -1916,7 +1887,7 @@ static int qlcnic_83xx_rx_poll(struct napi_struct *napi, int budget)
if (work_done < budget) { if (work_done < budget) {
napi_complete(&sds_ring->napi); napi_complete(&sds_ring->napi);
if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) if (test_bit(__QLCNIC_DEV_UP, &adapter->state))
qlcnic_83xx_enable_intr(adapter, sds_ring); qlcnic_enable_sds_intr(adapter, sds_ring);
} }
return work_done; return work_done;
...@@ -1936,7 +1907,7 @@ void qlcnic_83xx_napi_enable(struct qlcnic_adapter *adapter) ...@@ -1936,7 +1907,7 @@ void qlcnic_83xx_napi_enable(struct qlcnic_adapter *adapter)
sds_ring = &recv_ctx->sds_rings[ring]; sds_ring = &recv_ctx->sds_rings[ring];
napi_enable(&sds_ring->napi); napi_enable(&sds_ring->napi);
if (adapter->flags & QLCNIC_MSIX_ENABLED) if (adapter->flags & QLCNIC_MSIX_ENABLED)
qlcnic_83xx_enable_intr(adapter, sds_ring); qlcnic_enable_sds_intr(adapter, sds_ring);
} }
if ((adapter->flags & QLCNIC_MSIX_ENABLED) && if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
...@@ -1944,7 +1915,7 @@ void qlcnic_83xx_napi_enable(struct qlcnic_adapter *adapter) ...@@ -1944,7 +1915,7 @@ void qlcnic_83xx_napi_enable(struct qlcnic_adapter *adapter)
for (ring = 0; ring < adapter->drv_tx_rings; ring++) { for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring]; tx_ring = &adapter->tx_ring[ring];
napi_enable(&tx_ring->napi); napi_enable(&tx_ring->napi);
qlcnic_83xx_enable_tx_intr(adapter, tx_ring); qlcnic_enable_tx_intr(adapter, tx_ring);
} }
} }
} }
...@@ -1962,7 +1933,7 @@ void qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter) ...@@ -1962,7 +1933,7 @@ void qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter)
for (ring = 0; ring < adapter->drv_sds_rings; ring++) { for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring]; sds_ring = &recv_ctx->sds_rings[ring];
if (adapter->flags & QLCNIC_MSIX_ENABLED) if (adapter->flags & QLCNIC_MSIX_ENABLED)
qlcnic_83xx_disable_intr(adapter, sds_ring); qlcnic_disable_sds_intr(adapter, sds_ring);
napi_synchronize(&sds_ring->napi); napi_synchronize(&sds_ring->napi);
napi_disable(&sds_ring->napi); napi_disable(&sds_ring->napi);
} }
...@@ -1971,7 +1942,7 @@ void qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter) ...@@ -1971,7 +1942,7 @@ void qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter)
!(adapter->flags & QLCNIC_TX_INTR_SHARED)) { !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
for (ring = 0; ring < adapter->drv_tx_rings; ring++) { for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring]; tx_ring = &adapter->tx_ring[ring];
qlcnic_83xx_disable_tx_intr(adapter, tx_ring); qlcnic_disable_tx_intr(adapter, tx_ring);
napi_synchronize(&tx_ring->napi); napi_synchronize(&tx_ring->napi);
napi_disable(&tx_ring->napi); napi_disable(&tx_ring->napi);
} }
......
...@@ -71,6 +71,8 @@ static struct qlcnic_hardware_ops qlcnic_sriov_vf_hw_ops = { ...@@ -71,6 +71,8 @@ static struct qlcnic_hardware_ops qlcnic_sriov_vf_hw_ops = {
.change_l2_filter = qlcnic_83xx_change_l2_filter, .change_l2_filter = qlcnic_83xx_change_l2_filter,
.get_board_info = qlcnic_83xx_get_port_info, .get_board_info = qlcnic_83xx_get_port_info,
.free_mac_list = qlcnic_sriov_vf_free_mac_list, .free_mac_list = qlcnic_sriov_vf_free_mac_list,
.enable_sds_intr = qlcnic_83xx_enable_sds_intr,
.disable_sds_intr = qlcnic_83xx_disable_sds_intr,
}; };
static struct qlcnic_nic_template qlcnic_sriov_vf_ops = { static struct qlcnic_nic_template qlcnic_sriov_vf_ops = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment