Commit e43d15c8 authored by David S. Miller's avatar David S. Miller

Merge branch '40GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/next-queue

Jeff Kirsher says:

====================
40GbE Intel Wired LAN Driver Updates 2016-04-05

This series contains updates to i40e and i40evf only.

Stefan converts dev_close() to ndo_stop() for ethtool offline self test,
since dev_close() causes IFF_UP to be cleared which will remove the
interface routes and addresses.

Alex bumps up the size of the transmit data buffer to 12K rather than 8K,
which provides a gain in throughput and a reduction in overhead for
putting together the frame.  Fixed an issue in the polling routines where
we were using bitwise operators to avoid the side effects of the
logical operators.  Then added support for bulk transmit clean for skbs.

Jesse fixed a sparse issue in the type casting in the transmit code and
fixed i40e_aq_set_phy_debug() to use i40e_status as a return code.

Catherine cleans up duplicated code.

Shannon fixed the cleaning up of the interrupt handling to clean up the
IRQs only if we actually got them set up.  Also fixed up the error
scenarios where we were trying to remove a non-existent timer or
worktask, which causes the kernel heartburn.

Mitch changes the notification of resets to the reset interrupt handler,
instead of the actual reset initiation code.  This allows the VFs to get
properly notified for all resets, including resets initiated by different
PFs on the same physical device.  Also moved the clearing of VFLR bit
after reset processing, instead of before which could lead to double
resets on VF init.  Fixed code comment to match the actual function name.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents f1cc8094 50f26a50
...@@ -244,7 +244,6 @@ struct i40e_fdir_filter { ...@@ -244,7 +244,6 @@ struct i40e_fdir_filter {
#define I40E_DCB_PRIO_TYPE_STRICT 0 #define I40E_DCB_PRIO_TYPE_STRICT 0
#define I40E_DCB_PRIO_TYPE_ETS 1 #define I40E_DCB_PRIO_TYPE_ETS 1
#define I40E_DCB_STRICT_PRIO_CREDITS 127 #define I40E_DCB_STRICT_PRIO_CREDITS 127
#define I40E_MAX_USER_PRIORITY 8
/* DCB per TC information data structure */ /* DCB per TC information data structure */
struct i40e_tc_info { struct i40e_tc_info {
u16 qoffset; /* Queue offset from base queue */ u16 qoffset; /* Queue offset from base queue */
...@@ -811,6 +810,7 @@ int i40e_vlan_rx_kill_vid(struct net_device *netdev, ...@@ -811,6 +810,7 @@ int i40e_vlan_rx_kill_vid(struct net_device *netdev,
__always_unused __be16 proto, u16 vid); __always_unused __be16 proto, u16 vid);
#endif #endif
int i40e_open(struct net_device *netdev); int i40e_open(struct net_device *netdev);
int i40e_close(struct net_device *netdev);
int i40e_vsi_open(struct i40e_vsi *vsi); int i40e_vsi_open(struct i40e_vsi *vsi);
void i40e_vlan_stripping_disable(struct i40e_vsi *vsi); void i40e_vlan_stripping_disable(struct i40e_vsi *vsi);
int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid); int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid);
...@@ -823,7 +823,6 @@ bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi); ...@@ -823,7 +823,6 @@ bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi);
struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, u8 *macaddr, struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, u8 *macaddr,
bool is_vf, bool is_netdev); bool is_vf, bool is_netdev);
#ifdef I40E_FCOE #ifdef I40E_FCOE
int i40e_close(struct net_device *netdev);
int __i40e_setup_tc(struct net_device *netdev, u32 handle, __be16 proto, int __i40e_setup_tc(struct net_device *netdev, u32 handle, __be16 proto,
struct tc_to_netdev *tc); struct tc_to_netdev *tc);
void i40e_netpoll(struct net_device *netdev); void i40e_netpoll(struct net_device *netdev);
......
...@@ -1901,13 +1901,13 @@ i40e_status i40e_aq_set_phy_int_mask(struct i40e_hw *hw, ...@@ -1901,13 +1901,13 @@ i40e_status i40e_aq_set_phy_int_mask(struct i40e_hw *hw,
* *
* Reset the external PHY. * Reset the external PHY.
**/ **/
enum i40e_status_code i40e_aq_set_phy_debug(struct i40e_hw *hw, u8 cmd_flags, i40e_status i40e_aq_set_phy_debug(struct i40e_hw *hw, u8 cmd_flags,
struct i40e_asq_cmd_details *cmd_details) struct i40e_asq_cmd_details *cmd_details)
{ {
struct i40e_aq_desc desc; struct i40e_aq_desc desc;
struct i40e_aqc_set_phy_debug *cmd = struct i40e_aqc_set_phy_debug *cmd =
(struct i40e_aqc_set_phy_debug *)&desc.params.raw; (struct i40e_aqc_set_phy_debug *)&desc.params.raw;
enum i40e_status_code status; i40e_status status;
i40e_fill_default_direct_cmd_desc(&desc, i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_phy_debug); i40e_aqc_opc_set_phy_debug);
...@@ -2157,6 +2157,9 @@ i40e_status i40e_aq_update_vsi_params(struct i40e_hw *hw, ...@@ -2157,6 +2157,9 @@ i40e_status i40e_aq_update_vsi_params(struct i40e_hw *hw,
struct i40e_aq_desc desc; struct i40e_aq_desc desc;
struct i40e_aqc_add_get_update_vsi *cmd = struct i40e_aqc_add_get_update_vsi *cmd =
(struct i40e_aqc_add_get_update_vsi *)&desc.params.raw; (struct i40e_aqc_add_get_update_vsi *)&desc.params.raw;
struct i40e_aqc_add_get_update_vsi_completion *resp =
(struct i40e_aqc_add_get_update_vsi_completion *)
&desc.params.raw;
i40e_status status; i40e_status status;
i40e_fill_default_direct_cmd_desc(&desc, i40e_fill_default_direct_cmd_desc(&desc,
...@@ -2168,6 +2171,9 @@ i40e_status i40e_aq_update_vsi_params(struct i40e_hw *hw, ...@@ -2168,6 +2171,9 @@ i40e_status i40e_aq_update_vsi_params(struct i40e_hw *hw,
status = i40e_asq_send_command(hw, &desc, &vsi_ctx->info, status = i40e_asq_send_command(hw, &desc, &vsi_ctx->info,
sizeof(vsi_ctx->info), cmd_details); sizeof(vsi_ctx->info), cmd_details);
vsi_ctx->vsis_allocated = le16_to_cpu(resp->vsi_used);
vsi_ctx->vsis_unallocated = le16_to_cpu(resp->vsi_free);
return status; return status;
} }
......
...@@ -1714,7 +1714,7 @@ static void i40e_diag_test(struct net_device *netdev, ...@@ -1714,7 +1714,7 @@ static void i40e_diag_test(struct net_device *netdev,
/* If the device is online then take it offline */ /* If the device is online then take it offline */
if (if_running) if (if_running)
/* indicate we're in test mode */ /* indicate we're in test mode */
dev_close(netdev); i40e_close(netdev);
else else
/* This reset does not affect link - if it is /* This reset does not affect link - if it is
* changed to a type of reset that does affect * changed to a type of reset that does affect
...@@ -1743,7 +1743,7 @@ static void i40e_diag_test(struct net_device *netdev, ...@@ -1743,7 +1743,7 @@ static void i40e_diag_test(struct net_device *netdev,
i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED)); i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED));
if (if_running) if (if_running)
dev_open(netdev); i40e_open(netdev);
} else { } else {
/* Online tests */ /* Online tests */
netif_info(pf, drv, netdev, "online testing starting\n"); netif_info(pf, drv, netdev, "online testing starting\n");
......
...@@ -1371,7 +1371,7 @@ static netdev_tx_t i40e_fcoe_xmit_frame(struct sk_buff *skb, ...@@ -1371,7 +1371,7 @@ static netdev_tx_t i40e_fcoe_xmit_frame(struct sk_buff *skb,
if (i40e_chk_linearize(skb, count)) { if (i40e_chk_linearize(skb, count)) {
if (__skb_linearize(skb)) if (__skb_linearize(skb))
goto out_drop; goto out_drop;
count = TXD_USE_COUNT(skb->len); count = i40e_txd_use_count(skb->len);
tx_ring->tx_stats.tx_linearize++; tx_ring->tx_stats.tx_linearize++;
} }
......
...@@ -45,8 +45,8 @@ static const char i40e_driver_string[] = ...@@ -45,8 +45,8 @@ static const char i40e_driver_string[] =
#define DRV_KERN "-k" #define DRV_KERN "-k"
#define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MAJOR 1
#define DRV_VERSION_MINOR 4 #define DRV_VERSION_MINOR 5
#define DRV_VERSION_BUILD 25 #define DRV_VERSION_BUILD 1
#define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
__stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_MINOR) "." \
__stringify(DRV_VERSION_BUILD) DRV_KERN __stringify(DRV_VERSION_BUILD) DRV_KERN
...@@ -4164,7 +4164,7 @@ static void i40e_clear_interrupt_scheme(struct i40e_pf *pf) ...@@ -4164,7 +4164,7 @@ static void i40e_clear_interrupt_scheme(struct i40e_pf *pf)
int i; int i;
i40e_stop_misc_vector(pf); i40e_stop_misc_vector(pf);
if (pf->flags & I40E_FLAG_MSIX_ENABLED) { if (pf->flags & I40E_FLAG_MSIX_ENABLED && pf->msix_entries) {
synchronize_irq(pf->msix_entries[0].vector); synchronize_irq(pf->msix_entries[0].vector);
free_irq(pf->msix_entries[0].vector, pf); free_irq(pf->msix_entries[0].vector, pf);
} }
...@@ -5509,11 +5509,7 @@ static void i40e_fdir_filter_exit(struct i40e_pf *pf) ...@@ -5509,11 +5509,7 @@ static void i40e_fdir_filter_exit(struct i40e_pf *pf)
* *
* Returns 0, this is not allowed to fail * Returns 0, this is not allowed to fail
**/ **/
#ifdef I40E_FCOE
int i40e_close(struct net_device *netdev) int i40e_close(struct net_device *netdev)
#else
static int i40e_close(struct net_device *netdev)
#endif
{ {
struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi; struct i40e_vsi *vsi = np->vsi;
...@@ -5538,8 +5534,6 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags) ...@@ -5538,8 +5534,6 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags)
WARN_ON(in_interrupt()); WARN_ON(in_interrupt());
if (i40e_check_asq_alive(&pf->hw))
i40e_vc_notify_reset(pf);
/* do the biggest reset indicated */ /* do the biggest reset indicated */
if (reset_flags & BIT_ULL(__I40E_GLOBAL_RESET_REQUESTED)) { if (reset_flags & BIT_ULL(__I40E_GLOBAL_RESET_REQUESTED)) {
...@@ -6377,7 +6371,7 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf) ...@@ -6377,7 +6371,7 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf)
break; break;
default: default:
dev_info(&pf->pdev->dev, dev_info(&pf->pdev->dev,
"ARQ Error: Unknown event 0x%04x received\n", "ARQ: Unknown event 0x%04x ignored\n",
opcode); opcode);
break; break;
} }
...@@ -6742,6 +6736,8 @@ static void i40e_prep_for_reset(struct i40e_pf *pf) ...@@ -6742,6 +6736,8 @@ static void i40e_prep_for_reset(struct i40e_pf *pf)
clear_bit(__I40E_RESET_INTR_RECEIVED, &pf->state); clear_bit(__I40E_RESET_INTR_RECEIVED, &pf->state);
if (test_and_set_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state)) if (test_and_set_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state))
return; return;
if (i40e_check_asq_alive(&pf->hw))
i40e_vc_notify_reset(pf);
dev_dbg(&pf->pdev->dev, "Tearing down internal switch for reset\n"); dev_dbg(&pf->pdev->dev, "Tearing down internal switch for reset\n");
...@@ -10826,6 +10822,12 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -10826,6 +10822,12 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
hw->bus.func = PCI_FUNC(pdev->devfn); hw->bus.func = PCI_FUNC(pdev->devfn);
pf->instance = pfs_found; pf->instance = pfs_found;
/* set up the locks for the AQ, do this only once in probe
* and destroy them only once in remove
*/
mutex_init(&hw->aq.asq_mutex);
mutex_init(&hw->aq.arq_mutex);
if (debug != -1) { if (debug != -1) {
pf->msg_enable = pf->hw.debug_mask; pf->msg_enable = pf->hw.debug_mask;
pf->msg_enable = debug; pf->msg_enable = debug;
...@@ -10871,12 +10873,6 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -10871,12 +10873,6 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* set up a default setting for link flow control */ /* set up a default setting for link flow control */
pf->hw.fc.requested_mode = I40E_FC_NONE; pf->hw.fc.requested_mode = I40E_FC_NONE;
/* set up the locks for the AQ, do this only once in probe
* and destroy them only once in remove
*/
mutex_init(&hw->aq.asq_mutex);
mutex_init(&hw->aq.arq_mutex);
err = i40e_init_adminq(hw); err = i40e_init_adminq(hw);
if (err) { if (err) {
if (err == I40E_ERR_FIRMWARE_API_VERSION) if (err == I40E_ERR_FIRMWARE_API_VERSION)
...@@ -11269,7 +11265,6 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -11269,7 +11265,6 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
kfree(pf->qp_pile); kfree(pf->qp_pile);
err_sw_init: err_sw_init:
err_adminq_setup: err_adminq_setup:
(void)i40e_shutdown_adminq(hw);
err_pf_reset: err_pf_reset:
iounmap(hw->hw_addr); iounmap(hw->hw_addr);
err_ioremap: err_ioremap:
...@@ -11311,8 +11306,10 @@ static void i40e_remove(struct pci_dev *pdev) ...@@ -11311,8 +11306,10 @@ static void i40e_remove(struct pci_dev *pdev)
/* no more scheduling of any task */ /* no more scheduling of any task */
set_bit(__I40E_SUSPENDED, &pf->state); set_bit(__I40E_SUSPENDED, &pf->state);
set_bit(__I40E_DOWN, &pf->state); set_bit(__I40E_DOWN, &pf->state);
del_timer_sync(&pf->service_timer); if (pf->service_timer.data)
cancel_work_sync(&pf->service_task); del_timer_sync(&pf->service_timer);
if (pf->service_task.func)
cancel_work_sync(&pf->service_task);
if (pf->flags & I40E_FLAG_SRIOV_ENABLED) { if (pf->flags & I40E_FLAG_SRIOV_ENABLED) {
i40e_free_vfs(pf); i40e_free_vfs(pf);
......
...@@ -636,19 +636,21 @@ u32 i40e_get_tx_pending(struct i40e_ring *ring, bool in_sw) ...@@ -636,19 +636,21 @@ u32 i40e_get_tx_pending(struct i40e_ring *ring, bool in_sw)
/** /**
* i40e_clean_tx_irq - Reclaim resources after transmit completes * i40e_clean_tx_irq - Reclaim resources after transmit completes
* @tx_ring: tx ring to clean * @vsi: the VSI we care about
* @budget: how many cleans we're allowed * @tx_ring: Tx ring to clean
* @napi_budget: Used to determine if we are in netpoll
* *
* Returns true if there's any budget left (e.g. the clean is finished) * Returns true if there's any budget left (e.g. the clean is finished)
**/ **/
static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) static bool i40e_clean_tx_irq(struct i40e_vsi *vsi,
struct i40e_ring *tx_ring, int napi_budget)
{ {
u16 i = tx_ring->next_to_clean; u16 i = tx_ring->next_to_clean;
struct i40e_tx_buffer *tx_buf; struct i40e_tx_buffer *tx_buf;
struct i40e_tx_desc *tx_head; struct i40e_tx_desc *tx_head;
struct i40e_tx_desc *tx_desc; struct i40e_tx_desc *tx_desc;
unsigned int total_packets = 0; unsigned int total_bytes = 0, total_packets = 0;
unsigned int total_bytes = 0; unsigned int budget = vsi->work_limit;
tx_buf = &tx_ring->tx_bi[i]; tx_buf = &tx_ring->tx_bi[i];
tx_desc = I40E_TX_DESC(tx_ring, i); tx_desc = I40E_TX_DESC(tx_ring, i);
...@@ -678,7 +680,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) ...@@ -678,7 +680,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
total_packets += tx_buf->gso_segs; total_packets += tx_buf->gso_segs;
/* free the skb */ /* free the skb */
dev_consume_skb_any(tx_buf->skb); napi_consume_skb(tx_buf->skb, napi_budget);
/* unmap skb header data */ /* unmap skb header data */
dma_unmap_single(tx_ring->dev, dma_unmap_single(tx_ring->dev,
...@@ -749,7 +751,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) ...@@ -749,7 +751,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
if (budget && if (budget &&
((j / (WB_STRIDE + 1)) == 0) && (j != 0) && ((j / (WB_STRIDE + 1)) == 0) && (j != 0) &&
!test_bit(__I40E_DOWN, &tx_ring->vsi->state) && !test_bit(__I40E_DOWN, &vsi->state) &&
(I40E_DESC_UNUSED(tx_ring) != tx_ring->count)) (I40E_DESC_UNUSED(tx_ring) != tx_ring->count))
tx_ring->arm_wb = true; tx_ring->arm_wb = true;
} }
...@@ -767,7 +769,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) ...@@ -767,7 +769,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
smp_mb(); smp_mb();
if (__netif_subqueue_stopped(tx_ring->netdev, if (__netif_subqueue_stopped(tx_ring->netdev,
tx_ring->queue_index) && tx_ring->queue_index) &&
!test_bit(__I40E_DOWN, &tx_ring->vsi->state)) { !test_bit(__I40E_DOWN, &vsi->state)) {
netif_wake_subqueue(tx_ring->netdev, netif_wake_subqueue(tx_ring->netdev,
tx_ring->queue_index); tx_ring->queue_index);
++tx_ring->tx_stats.restart_queue; ++tx_ring->tx_stats.restart_queue;
...@@ -1975,9 +1977,11 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) ...@@ -1975,9 +1977,11 @@ int i40e_napi_poll(struct napi_struct *napi, int budget)
* budget and be more aggressive about cleaning up the Tx descriptors. * budget and be more aggressive about cleaning up the Tx descriptors.
*/ */
i40e_for_each_ring(ring, q_vector->tx) { i40e_for_each_ring(ring, q_vector->tx) {
clean_complete = clean_complete && if (!i40e_clean_tx_irq(vsi, ring, budget)) {
i40e_clean_tx_irq(ring, vsi->work_limit); clean_complete = false;
arm_wb = arm_wb || ring->arm_wb; continue;
}
arm_wb |= ring->arm_wb;
ring->arm_wb = false; ring->arm_wb = false;
} }
...@@ -1999,8 +2003,9 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) ...@@ -1999,8 +2003,9 @@ int i40e_napi_poll(struct napi_struct *napi, int budget)
cleaned = i40e_clean_rx_irq_1buf(ring, budget_per_ring); cleaned = i40e_clean_rx_irq_1buf(ring, budget_per_ring);
work_done += cleaned; work_done += cleaned;
/* if we didn't clean as many as budgeted, we must be done */ /* if we clean as many as budgeted, we must not be done */
clean_complete = clean_complete && (budget_per_ring > cleaned); if (cleaned >= budget_per_ring)
clean_complete = false;
} }
/* If work not completed, return budget and polling will return */ /* If work not completed, return budget and polling will return */
...@@ -2300,7 +2305,8 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -2300,7 +2305,8 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
/* remove payload length from outer checksum */ /* remove payload length from outer checksum */
paylen = (__force u16)l4.udp->check; paylen = (__force u16)l4.udp->check;
paylen += ntohs(1) * (u16)~(skb->len - l4_offset); paylen += ntohs((__force __be16)1) *
(u16)~(skb->len - l4_offset);
l4.udp->check = ~csum_fold((__force __wsum)paylen); l4.udp->check = ~csum_fold((__force __wsum)paylen);
} }
...@@ -2322,7 +2328,7 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -2322,7 +2328,7 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
/* remove payload length from inner checksum */ /* remove payload length from inner checksum */
paylen = (__force u16)l4.tcp->check; paylen = (__force u16)l4.tcp->check;
paylen += ntohs(1) * (u16)~(skb->len - l4_offset); paylen += ntohs((__force __be16)1) * (u16)~(skb->len - l4_offset);
l4.tcp->check = ~csum_fold((__force __wsum)paylen); l4.tcp->check = ~csum_fold((__force __wsum)paylen);
/* compute length of segmentation header */ /* compute length of segmentation header */
...@@ -2717,6 +2723,8 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -2717,6 +2723,8 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
tx_bi = first; tx_bi = first;
for (frag = &skb_shinfo(skb)->frags[0];; frag++) { for (frag = &skb_shinfo(skb)->frags[0];; frag++) {
unsigned int max_data = I40E_MAX_DATA_PER_TXD_ALIGNED;
if (dma_mapping_error(tx_ring->dev, dma)) if (dma_mapping_error(tx_ring->dev, dma))
goto dma_error; goto dma_error;
...@@ -2724,12 +2732,14 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -2724,12 +2732,14 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
dma_unmap_len_set(tx_bi, len, size); dma_unmap_len_set(tx_bi, len, size);
dma_unmap_addr_set(tx_bi, dma, dma); dma_unmap_addr_set(tx_bi, dma, dma);
/* align size to end of page */
max_data += -dma & (I40E_MAX_READ_REQ_SIZE - 1);
tx_desc->buffer_addr = cpu_to_le64(dma); tx_desc->buffer_addr = cpu_to_le64(dma);
while (unlikely(size > I40E_MAX_DATA_PER_TXD)) { while (unlikely(size > I40E_MAX_DATA_PER_TXD)) {
tx_desc->cmd_type_offset_bsz = tx_desc->cmd_type_offset_bsz =
build_ctob(td_cmd, td_offset, build_ctob(td_cmd, td_offset,
I40E_MAX_DATA_PER_TXD, td_tag); max_data, td_tag);
tx_desc++; tx_desc++;
i++; i++;
...@@ -2740,9 +2750,10 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -2740,9 +2750,10 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
i = 0; i = 0;
} }
dma += I40E_MAX_DATA_PER_TXD; dma += max_data;
size -= I40E_MAX_DATA_PER_TXD; size -= max_data;
max_data = I40E_MAX_DATA_PER_TXD_ALIGNED;
tx_desc->buffer_addr = cpu_to_le64(dma); tx_desc->buffer_addr = cpu_to_le64(dma);
} }
...@@ -2892,7 +2903,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, ...@@ -2892,7 +2903,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
if (i40e_chk_linearize(skb, count)) { if (i40e_chk_linearize(skb, count)) {
if (__skb_linearize(skb)) if (__skb_linearize(skb))
goto out_drop; goto out_drop;
count = TXD_USE_COUNT(skb->len); count = i40e_txd_use_count(skb->len);
tx_ring->tx_stats.tx_linearize++; tx_ring->tx_stats.tx_linearize++;
} }
......
...@@ -146,10 +146,39 @@ enum i40e_dyn_idx_t { ...@@ -146,10 +146,39 @@ enum i40e_dyn_idx_t {
#define I40E_MAX_BUFFER_TXD 8 #define I40E_MAX_BUFFER_TXD 8
#define I40E_MIN_TX_LEN 17 #define I40E_MIN_TX_LEN 17
#define I40E_MAX_DATA_PER_TXD 8192
/* The size limit for a transmit buffer in a descriptor is (16K - 1).
* In order to align with the read requests we will align the value to
* the nearest 4K which represents our maximum read request size.
*/
#define I40E_MAX_READ_REQ_SIZE 4096
#define I40E_MAX_DATA_PER_TXD (16 * 1024 - 1)
#define I40E_MAX_DATA_PER_TXD_ALIGNED \
(I40E_MAX_DATA_PER_TXD & ~(I40E_MAX_READ_REQ_SIZE - 1))
/* This ugly bit of math is equivalent to DIV_ROUNDUP(size, X) where X is
* the value I40E_MAX_DATA_PER_TXD_ALIGNED. It is needed due to the fact
* that 12K is not a power of 2 and division is expensive. It is used to
* approximate the number of descriptors used per linear buffer. Note
* that this will overestimate in some cases as it doesn't account for the
* fact that we will add up to 4K - 1 in aligning the 12K buffer, however
* the error should not impact things much as large buffers usually mean
* we will use fewer descriptors then there are frags in an skb.
*/
static inline unsigned int i40e_txd_use_count(unsigned int size)
{
const unsigned int max = I40E_MAX_DATA_PER_TXD_ALIGNED;
const unsigned int reciprocal = ((1ull << 32) - 1 + (max / 2)) / max;
unsigned int adjust = ~(u32)0;
/* if we rounded up on the reciprocal pull down the adjustment */
if ((max * reciprocal) > adjust)
adjust = ~(u32)(reciprocal - 1);
return (u32)((((u64)size * reciprocal) + adjust) >> 32);
}
/* Tx Descriptors needed, worst case */ /* Tx Descriptors needed, worst case */
#define TXD_USE_COUNT(S) DIV_ROUND_UP((S), I40E_MAX_DATA_PER_TXD)
#define DESC_NEEDED (MAX_SKB_FRAGS + 4) #define DESC_NEEDED (MAX_SKB_FRAGS + 4)
#define I40E_MIN_DESC_PENDING 4 #define I40E_MIN_DESC_PENDING 4
...@@ -377,7 +406,7 @@ static inline int i40e_xmit_descriptor_count(struct sk_buff *skb) ...@@ -377,7 +406,7 @@ static inline int i40e_xmit_descriptor_count(struct sk_buff *skb)
int count = 0, size = skb_headlen(skb); int count = 0, size = skb_headlen(skb);
for (;;) { for (;;) {
count += TXD_USE_COUNT(size); count += i40e_txd_use_count(size);
if (!nr_frags--) if (!nr_frags--)
break; break;
......
...@@ -63,7 +63,7 @@ static void i40e_vc_vf_broadcast(struct i40e_pf *pf, ...@@ -63,7 +63,7 @@ static void i40e_vc_vf_broadcast(struct i40e_pf *pf,
} }
/** /**
* i40e_vc_notify_link_state * i40e_vc_notify_vf_link_state
* @vf: pointer to the VF structure * @vf: pointer to the VF structure
* *
* send a link status message to a single VF * send a link status message to a single VF
...@@ -917,9 +917,9 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr) ...@@ -917,9 +917,9 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr)
{ {
struct i40e_pf *pf = vf->pf; struct i40e_pf *pf = vf->pf;
struct i40e_hw *hw = &pf->hw; struct i40e_hw *hw = &pf->hw;
u32 reg, reg_idx, bit_idx;
bool rsd = false; bool rsd = false;
int i; int i;
u32 reg;
if (test_and_set_bit(__I40E_VF_DISABLE, &pf->state)) if (test_and_set_bit(__I40E_VF_DISABLE, &pf->state))
return; return;
...@@ -988,6 +988,11 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr) ...@@ -988,6 +988,11 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr)
} }
/* tell the VF the reset is done */ /* tell the VF the reset is done */
wr32(hw, I40E_VFGEN_RSTAT1(vf->vf_id), I40E_VFR_VFACTIVE); wr32(hw, I40E_VFGEN_RSTAT1(vf->vf_id), I40E_VFR_VFACTIVE);
/* clear the VFLR bit in GLGEN_VFLRSTAT */
reg_idx = (hw->func_caps.vf_base_id + vf->vf_id) / 32;
bit_idx = (hw->func_caps.vf_base_id + vf->vf_id) % 32;
wr32(hw, I40E_GLGEN_VFLRSTAT(reg_idx), BIT(bit_idx));
i40e_flush(hw); i40e_flush(hw);
clear_bit(__I40E_VF_DISABLE, &pf->state); clear_bit(__I40E_VF_DISABLE, &pf->state);
} }
...@@ -2293,9 +2298,7 @@ int i40e_vc_process_vflr_event(struct i40e_pf *pf) ...@@ -2293,9 +2298,7 @@ int i40e_vc_process_vflr_event(struct i40e_pf *pf)
vf = &pf->vf[vf_id]; vf = &pf->vf[vf_id];
reg = rd32(hw, I40E_GLGEN_VFLRSTAT(reg_idx)); reg = rd32(hw, I40E_GLGEN_VFLRSTAT(reg_idx));
if (reg & BIT(bit_idx)) { if (reg & BIT(bit_idx)) {
/* clear the bit in GLGEN_VFLRSTAT */ /* i40e_reset_vf will clear the bit in GLGEN_VFLRSTAT */
wr32(hw, I40E_GLGEN_VFLRSTAT(reg_idx), BIT(bit_idx));
if (!test_bit(__I40E_DOWN, &pf->state)) if (!test_bit(__I40E_DOWN, &pf->state))
i40e_reset_vf(vf, true); i40e_reset_vf(vf, true);
} }
......
...@@ -155,19 +155,21 @@ u32 i40evf_get_tx_pending(struct i40e_ring *ring, bool in_sw) ...@@ -155,19 +155,21 @@ u32 i40evf_get_tx_pending(struct i40e_ring *ring, bool in_sw)
/** /**
* i40e_clean_tx_irq - Reclaim resources after transmit completes * i40e_clean_tx_irq - Reclaim resources after transmit completes
* @tx_ring: tx ring to clean * @vsi: the VSI we care about
* @budget: how many cleans we're allowed * @tx_ring: Tx ring to clean
* @napi_budget: Used to determine if we are in netpoll
* *
* Returns true if there's any budget left (e.g. the clean is finished) * Returns true if there's any budget left (e.g. the clean is finished)
**/ **/
static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) static bool i40e_clean_tx_irq(struct i40e_vsi *vsi,
struct i40e_ring *tx_ring, int napi_budget)
{ {
u16 i = tx_ring->next_to_clean; u16 i = tx_ring->next_to_clean;
struct i40e_tx_buffer *tx_buf; struct i40e_tx_buffer *tx_buf;
struct i40e_tx_desc *tx_head; struct i40e_tx_desc *tx_head;
struct i40e_tx_desc *tx_desc; struct i40e_tx_desc *tx_desc;
unsigned int total_packets = 0; unsigned int total_bytes = 0, total_packets = 0;
unsigned int total_bytes = 0; unsigned int budget = vsi->work_limit;
tx_buf = &tx_ring->tx_bi[i]; tx_buf = &tx_ring->tx_bi[i];
tx_desc = I40E_TX_DESC(tx_ring, i); tx_desc = I40E_TX_DESC(tx_ring, i);
...@@ -197,7 +199,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) ...@@ -197,7 +199,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
total_packets += tx_buf->gso_segs; total_packets += tx_buf->gso_segs;
/* free the skb */ /* free the skb */
dev_kfree_skb_any(tx_buf->skb); napi_consume_skb(tx_buf->skb, napi_budget);
/* unmap skb header data */ /* unmap skb header data */
dma_unmap_single(tx_ring->dev, dma_unmap_single(tx_ring->dev,
...@@ -267,7 +269,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) ...@@ -267,7 +269,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
if (budget && if (budget &&
((j / (WB_STRIDE + 1)) == 0) && (j > 0) && ((j / (WB_STRIDE + 1)) == 0) && (j > 0) &&
!test_bit(__I40E_DOWN, &tx_ring->vsi->state) && !test_bit(__I40E_DOWN, &vsi->state) &&
(I40E_DESC_UNUSED(tx_ring) != tx_ring->count)) (I40E_DESC_UNUSED(tx_ring) != tx_ring->count))
tx_ring->arm_wb = true; tx_ring->arm_wb = true;
} }
...@@ -285,7 +287,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) ...@@ -285,7 +287,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
smp_mb(); smp_mb();
if (__netif_subqueue_stopped(tx_ring->netdev, if (__netif_subqueue_stopped(tx_ring->netdev,
tx_ring->queue_index) && tx_ring->queue_index) &&
!test_bit(__I40E_DOWN, &tx_ring->vsi->state)) { !test_bit(__I40E_DOWN, &vsi->state)) {
netif_wake_subqueue(tx_ring->netdev, netif_wake_subqueue(tx_ring->netdev,
tx_ring->queue_index); tx_ring->queue_index);
++tx_ring->tx_stats.restart_queue; ++tx_ring->tx_stats.restart_queue;
...@@ -1411,9 +1413,11 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) ...@@ -1411,9 +1413,11 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget)
* budget and be more aggressive about cleaning up the Tx descriptors. * budget and be more aggressive about cleaning up the Tx descriptors.
*/ */
i40e_for_each_ring(ring, q_vector->tx) { i40e_for_each_ring(ring, q_vector->tx) {
clean_complete = clean_complete && if (!i40e_clean_tx_irq(vsi, ring, budget)) {
i40e_clean_tx_irq(ring, vsi->work_limit); clean_complete = false;
arm_wb = arm_wb || ring->arm_wb; continue;
}
arm_wb |= ring->arm_wb;
ring->arm_wb = false; ring->arm_wb = false;
} }
...@@ -1435,8 +1439,9 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) ...@@ -1435,8 +1439,9 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget)
cleaned = i40e_clean_rx_irq_1buf(ring, budget_per_ring); cleaned = i40e_clean_rx_irq_1buf(ring, budget_per_ring);
work_done += cleaned; work_done += cleaned;
/* if we didn't clean as many as budgeted, we must be done */ /* if we clean as many as budgeted, we must not be done */
clean_complete = clean_complete && (budget_per_ring > cleaned); if (cleaned >= budget_per_ring)
clean_complete = false;
} }
/* If work not completed, return budget and polling will return */ /* If work not completed, return budget and polling will return */
...@@ -1567,7 +1572,8 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -1567,7 +1572,8 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
/* remove payload length from outer checksum */ /* remove payload length from outer checksum */
paylen = (__force u16)l4.udp->check; paylen = (__force u16)l4.udp->check;
paylen += ntohs(1) * (u16)~(skb->len - l4_offset); paylen += ntohs((__force __be16)1) *
(u16)~(skb->len - l4_offset);
l4.udp->check = ~csum_fold((__force __wsum)paylen); l4.udp->check = ~csum_fold((__force __wsum)paylen);
} }
...@@ -1589,7 +1595,7 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -1589,7 +1595,7 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
/* remove payload length from inner checksum */ /* remove payload length from inner checksum */
paylen = (__force u16)l4.tcp->check; paylen = (__force u16)l4.tcp->check;
paylen += ntohs(1) * (u16)~(skb->len - l4_offset); paylen += ntohs((__force __be16)1) * (u16)~(skb->len - l4_offset);
l4.tcp->check = ~csum_fold((__force __wsum)paylen); l4.tcp->check = ~csum_fold((__force __wsum)paylen);
/* compute length of segmentation header */ /* compute length of segmentation header */
...@@ -1936,6 +1942,8 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -1936,6 +1942,8 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
tx_bi = first; tx_bi = first;
for (frag = &skb_shinfo(skb)->frags[0];; frag++) { for (frag = &skb_shinfo(skb)->frags[0];; frag++) {
unsigned int max_data = I40E_MAX_DATA_PER_TXD_ALIGNED;
if (dma_mapping_error(tx_ring->dev, dma)) if (dma_mapping_error(tx_ring->dev, dma))
goto dma_error; goto dma_error;
...@@ -1943,12 +1951,14 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -1943,12 +1951,14 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
dma_unmap_len_set(tx_bi, len, size); dma_unmap_len_set(tx_bi, len, size);
dma_unmap_addr_set(tx_bi, dma, dma); dma_unmap_addr_set(tx_bi, dma, dma);
/* align size to end of page */
max_data += -dma & (I40E_MAX_READ_REQ_SIZE - 1);
tx_desc->buffer_addr = cpu_to_le64(dma); tx_desc->buffer_addr = cpu_to_le64(dma);
while (unlikely(size > I40E_MAX_DATA_PER_TXD)) { while (unlikely(size > I40E_MAX_DATA_PER_TXD)) {
tx_desc->cmd_type_offset_bsz = tx_desc->cmd_type_offset_bsz =
build_ctob(td_cmd, td_offset, build_ctob(td_cmd, td_offset,
I40E_MAX_DATA_PER_TXD, td_tag); max_data, td_tag);
tx_desc++; tx_desc++;
i++; i++;
...@@ -1959,9 +1969,10 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -1959,9 +1969,10 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
i = 0; i = 0;
} }
dma += I40E_MAX_DATA_PER_TXD; dma += max_data;
size -= I40E_MAX_DATA_PER_TXD; size -= max_data;
max_data = I40E_MAX_DATA_PER_TXD_ALIGNED;
tx_desc->buffer_addr = cpu_to_le64(dma); tx_desc->buffer_addr = cpu_to_le64(dma);
} }
...@@ -2110,7 +2121,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, ...@@ -2110,7 +2121,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
if (i40e_chk_linearize(skb, count)) { if (i40e_chk_linearize(skb, count)) {
if (__skb_linearize(skb)) if (__skb_linearize(skb))
goto out_drop; goto out_drop;
count = TXD_USE_COUNT(skb->len); count = i40e_txd_use_count(skb->len);
tx_ring->tx_stats.tx_linearize++; tx_ring->tx_stats.tx_linearize++;
} }
......
...@@ -146,10 +146,39 @@ enum i40e_dyn_idx_t { ...@@ -146,10 +146,39 @@ enum i40e_dyn_idx_t {
#define I40E_MAX_BUFFER_TXD 8 #define I40E_MAX_BUFFER_TXD 8
#define I40E_MIN_TX_LEN 17 #define I40E_MIN_TX_LEN 17
#define I40E_MAX_DATA_PER_TXD 8192
/* The size limit for a transmit buffer in a descriptor is (16K - 1).
* In order to align with the read requests we will align the value to
* the nearest 4K which represents our maximum read request size.
*/
#define I40E_MAX_READ_REQ_SIZE 4096
#define I40E_MAX_DATA_PER_TXD (16 * 1024 - 1)
#define I40E_MAX_DATA_PER_TXD_ALIGNED \
(I40E_MAX_DATA_PER_TXD & ~(I40E_MAX_READ_REQ_SIZE - 1))
/* This ugly bit of math is equivalent to DIV_ROUNDUP(size, X) where X is
* the value I40E_MAX_DATA_PER_TXD_ALIGNED. It is needed due to the fact
* that 12K is not a power of 2 and division is expensive. It is used to
* approximate the number of descriptors used per linear buffer. Note
* that this will overestimate in some cases as it doesn't account for the
* fact that we will add up to 4K - 1 in aligning the 12K buffer, however
* the error should not impact things much as large buffers usually mean
* we will use fewer descriptors then there are frags in an skb.
*/
static inline unsigned int i40e_txd_use_count(unsigned int size)
{
const unsigned int max = I40E_MAX_DATA_PER_TXD_ALIGNED;
const unsigned int reciprocal = ((1ull << 32) - 1 + (max / 2)) / max;
unsigned int adjust = ~(u32)0;
/* if we rounded up on the reciprocal pull down the adjustment */
if ((max * reciprocal) > adjust)
adjust = ~(u32)(reciprocal - 1);
return (u32)((((u64)size * reciprocal) + adjust) >> 32);
}
/* Tx Descriptors needed, worst case */ /* Tx Descriptors needed, worst case */
#define TXD_USE_COUNT(S) DIV_ROUND_UP((S), I40E_MAX_DATA_PER_TXD)
#define DESC_NEEDED (MAX_SKB_FRAGS + 4) #define DESC_NEEDED (MAX_SKB_FRAGS + 4)
#define I40E_MIN_DESC_PENDING 4 #define I40E_MIN_DESC_PENDING 4
...@@ -359,7 +388,7 @@ static inline int i40e_xmit_descriptor_count(struct sk_buff *skb) ...@@ -359,7 +388,7 @@ static inline int i40e_xmit_descriptor_count(struct sk_buff *skb)
int count = 0, size = skb_headlen(skb); int count = 0, size = skb_headlen(skb);
for (;;) { for (;;) {
count += TXD_USE_COUNT(size); count += i40e_txd_use_count(size);
if (!nr_frags--) if (!nr_frags--)
break; break;
......
...@@ -37,8 +37,8 @@ static const char i40evf_driver_string[] = ...@@ -37,8 +37,8 @@ static const char i40evf_driver_string[] =
#define DRV_KERN "-k" #define DRV_KERN "-k"
#define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MAJOR 1
#define DRV_VERSION_MINOR 4 #define DRV_VERSION_MINOR 5
#define DRV_VERSION_BUILD 15 #define DRV_VERSION_BUILD 1
#define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
__stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_MINOR) "." \
__stringify(DRV_VERSION_BUILD) \ __stringify(DRV_VERSION_BUILD) \
...@@ -1507,7 +1507,7 @@ static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter) ...@@ -1507,7 +1507,7 @@ static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter)
adapter->q_vectors = kcalloc(num_q_vectors, sizeof(*q_vector), adapter->q_vectors = kcalloc(num_q_vectors, sizeof(*q_vector),
GFP_KERNEL); GFP_KERNEL);
if (!adapter->q_vectors) if (!adapter->q_vectors)
goto err_out; return -ENOMEM;
for (q_idx = 0; q_idx < num_q_vectors; q_idx++) { for (q_idx = 0; q_idx < num_q_vectors; q_idx++) {
q_vector = &adapter->q_vectors[q_idx]; q_vector = &adapter->q_vectors[q_idx];
...@@ -1519,15 +1519,6 @@ static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter) ...@@ -1519,15 +1519,6 @@ static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter)
} }
return 0; return 0;
err_out:
while (q_idx) {
q_idx--;
q_vector = &adapter->q_vectors[q_idx];
netif_napi_del(&q_vector->napi);
}
kfree(adapter->q_vectors);
return -ENOMEM;
} }
/** /**
...@@ -2003,6 +1994,8 @@ static void i40evf_adminq_task(struct work_struct *work) ...@@ -2003,6 +1994,8 @@ static void i40evf_adminq_task(struct work_struct *work)
/* check for error indications */ /* check for error indications */
val = rd32(hw, hw->aq.arq.len); val = rd32(hw, hw->aq.arq.len);
if (val == 0xdeadbeef) /* indicates device in reset */
goto freedom;
oldval = val; oldval = val;
if (val & I40E_VF_ARQLEN1_ARQVFE_MASK) { if (val & I40E_VF_ARQLEN1_ARQVFE_MASK) {
dev_info(&adapter->pdev->dev, "ARQ VF Error detected\n"); dev_info(&adapter->pdev->dev, "ARQ VF Error detected\n");
......
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