Commit 9ad716b9 authored by Jakub Kicinski's avatar Jakub Kicinski Committed by David S. Miller

nfp: wait for posted reconfigs when disabling the device

To avoid leaking a running timer we need to wait for the
posted reconfigs after netdev is unregistered.  In common
case the process of deinitializing the device will perform
synchronous reconfigs which wait for posted requests, but
especially with VXLAN ports being actively added and removed
there can be a race condition leaving a timer running after
adapter structure is freed leading to a crash.

Add an explicit flush after deregistering and for a good
measure a warning to check if timer is running just before
structures are freed.

Fixes: 3d780b92 ("nfp: add async reconfiguration mechanism")
Signed-off-by: default avatarJakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: default avatarDirk van der Merwe <dirk.vandermerwe@netronome.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3a7ad063
...@@ -229,29 +229,16 @@ static void nfp_net_reconfig_post(struct nfp_net *nn, u32 update) ...@@ -229,29 +229,16 @@ static void nfp_net_reconfig_post(struct nfp_net *nn, u32 update)
spin_unlock_bh(&nn->reconfig_lock); spin_unlock_bh(&nn->reconfig_lock);
} }
/** static void nfp_net_reconfig_sync_enter(struct nfp_net *nn)
* nfp_net_reconfig() - Reconfigure the firmware
* @nn: NFP Net device to reconfigure
* @update: The value for the update field in the BAR config
*
* Write the update word to the BAR and ping the reconfig queue. The
* poll until the firmware has acknowledged the update by zeroing the
* update word.
*
* Return: Negative errno on error, 0 on success
*/
int nfp_net_reconfig(struct nfp_net *nn, u32 update)
{ {
bool cancelled_timer = false; bool cancelled_timer = false;
u32 pre_posted_requests; u32 pre_posted_requests;
int ret;
spin_lock_bh(&nn->reconfig_lock); spin_lock_bh(&nn->reconfig_lock);
nn->reconfig_sync_present = true; nn->reconfig_sync_present = true;
if (nn->reconfig_timer_active) { if (nn->reconfig_timer_active) {
del_timer(&nn->reconfig_timer);
nn->reconfig_timer_active = false; nn->reconfig_timer_active = false;
cancelled_timer = true; cancelled_timer = true;
} }
...@@ -260,14 +247,43 @@ int nfp_net_reconfig(struct nfp_net *nn, u32 update) ...@@ -260,14 +247,43 @@ int nfp_net_reconfig(struct nfp_net *nn, u32 update)
spin_unlock_bh(&nn->reconfig_lock); spin_unlock_bh(&nn->reconfig_lock);
if (cancelled_timer) if (cancelled_timer) {
del_timer_sync(&nn->reconfig_timer);
nfp_net_reconfig_wait(nn, nn->reconfig_timer.expires); nfp_net_reconfig_wait(nn, nn->reconfig_timer.expires);
}
/* Run the posted reconfigs which were issued before we started */ /* Run the posted reconfigs which were issued before we started */
if (pre_posted_requests) { if (pre_posted_requests) {
nfp_net_reconfig_start(nn, pre_posted_requests); nfp_net_reconfig_start(nn, pre_posted_requests);
nfp_net_reconfig_wait(nn, jiffies + HZ * NFP_NET_POLL_TIMEOUT); nfp_net_reconfig_wait(nn, jiffies + HZ * NFP_NET_POLL_TIMEOUT);
} }
}
static void nfp_net_reconfig_wait_posted(struct nfp_net *nn)
{
nfp_net_reconfig_sync_enter(nn);
spin_lock_bh(&nn->reconfig_lock);
nn->reconfig_sync_present = false;
spin_unlock_bh(&nn->reconfig_lock);
}
/**
* nfp_net_reconfig() - Reconfigure the firmware
* @nn: NFP Net device to reconfigure
* @update: The value for the update field in the BAR config
*
* Write the update word to the BAR and ping the reconfig queue. The
* poll until the firmware has acknowledged the update by zeroing the
* update word.
*
* Return: Negative errno on error, 0 on success
*/
int nfp_net_reconfig(struct nfp_net *nn, u32 update)
{
int ret;
nfp_net_reconfig_sync_enter(nn);
nfp_net_reconfig_start(nn, update); nfp_net_reconfig_start(nn, update);
ret = nfp_net_reconfig_wait(nn, jiffies + HZ * NFP_NET_POLL_TIMEOUT); ret = nfp_net_reconfig_wait(nn, jiffies + HZ * NFP_NET_POLL_TIMEOUT);
...@@ -3633,6 +3649,7 @@ struct nfp_net *nfp_net_alloc(struct pci_dev *pdev, bool needs_netdev, ...@@ -3633,6 +3649,7 @@ struct nfp_net *nfp_net_alloc(struct pci_dev *pdev, bool needs_netdev,
*/ */
void nfp_net_free(struct nfp_net *nn) void nfp_net_free(struct nfp_net *nn)
{ {
WARN_ON(timer_pending(&nn->reconfig_timer) || nn->reconfig_posted);
if (nn->dp.netdev) if (nn->dp.netdev)
free_netdev(nn->dp.netdev); free_netdev(nn->dp.netdev);
else else
...@@ -3920,4 +3937,5 @@ void nfp_net_clean(struct nfp_net *nn) ...@@ -3920,4 +3937,5 @@ void nfp_net_clean(struct nfp_net *nn)
return; return;
unregister_netdev(nn->dp.netdev); unregister_netdev(nn->dp.netdev);
nfp_net_reconfig_wait_posted(nn);
} }
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