Commit cda8daf1 authored by Erez Shitrit's avatar Erez Shitrit Committed by Jason Gunthorpe

IB/ipoib: Use cancel_delayed_work_sync for neigh-clean task

The neigh_reap_task is self restarting, but so long as we call
cancel_delayed_work_sync() it will be guaranteed to not be running and
never start again. Thus we don't need to have the racy
IPOIB_STOP_NEIGH_GC bit, or the confusing mismatch of places sometimes
calling flush_workqueue after the cancel.

This fixes a situation where the GC work could have been left running
in some rare situations.
Signed-off-by: default avatarErez Shitrit <erezsh@mellanox.com>
Signed-off-by: default avatarLeon Romanovsky <leonro@mellanox.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@mellanox.com>
parent 577e07ff
...@@ -91,7 +91,6 @@ enum { ...@@ -91,7 +91,6 @@ enum {
IPOIB_STOP_REAPER = 7, IPOIB_STOP_REAPER = 7,
IPOIB_FLAG_ADMIN_CM = 9, IPOIB_FLAG_ADMIN_CM = 9,
IPOIB_FLAG_UMCAST = 10, IPOIB_FLAG_UMCAST = 10,
IPOIB_STOP_NEIGH_GC = 11,
IPOIB_NEIGH_TBL_FLUSH = 12, IPOIB_NEIGH_TBL_FLUSH = 12,
IPOIB_FLAG_DEV_ADDR_SET = 13, IPOIB_FLAG_DEV_ADDR_SET = 13,
IPOIB_FLAG_DEV_ADDR_CTRL = 14, IPOIB_FLAG_DEV_ADDR_CTRL = 14,
......
...@@ -1310,9 +1310,6 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv) ...@@ -1310,9 +1310,6 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
int i; int i;
LIST_HEAD(remove_list); LIST_HEAD(remove_list);
if (test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags))
return;
spin_lock_irqsave(&priv->lock, flags); spin_lock_irqsave(&priv->lock, flags);
htbl = rcu_dereference_protected(ntbl->htbl, htbl = rcu_dereference_protected(ntbl->htbl,
...@@ -1324,9 +1321,6 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv) ...@@ -1324,9 +1321,6 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
/* neigh is obsolete if it was idle for two GC periods */ /* neigh is obsolete if it was idle for two GC periods */
dt = 2 * arp_tbl.gc_interval; dt = 2 * arp_tbl.gc_interval;
neigh_obsolete = jiffies - dt; neigh_obsolete = jiffies - dt;
/* handle possible race condition */
if (test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags))
goto out_unlock;
for (i = 0; i < htbl->size; i++) { for (i = 0; i < htbl->size; i++) {
struct ipoib_neigh *neigh; struct ipoib_neigh *neigh;
...@@ -1364,9 +1358,8 @@ static void ipoib_reap_neigh(struct work_struct *work) ...@@ -1364,9 +1358,8 @@ static void ipoib_reap_neigh(struct work_struct *work)
__ipoib_reap_neigh(priv); __ipoib_reap_neigh(priv);
if (!test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags)) queue_delayed_work(priv->wq, &priv->neigh_reap_task,
queue_delayed_work(priv->wq, &priv->neigh_reap_task, arp_tbl.gc_interval);
arp_tbl.gc_interval);
} }
...@@ -1528,7 +1521,6 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv) ...@@ -1528,7 +1521,6 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
htbl = kzalloc(sizeof(*htbl), GFP_KERNEL); htbl = kzalloc(sizeof(*htbl), GFP_KERNEL);
if (!htbl) if (!htbl)
return -ENOMEM; return -ENOMEM;
set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
size = roundup_pow_of_two(arp_tbl.gc_thresh3); size = roundup_pow_of_two(arp_tbl.gc_thresh3);
buckets = kvcalloc(size, sizeof(*buckets), GFP_KERNEL); buckets = kvcalloc(size, sizeof(*buckets), GFP_KERNEL);
if (!buckets) { if (!buckets) {
...@@ -1543,7 +1535,6 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv) ...@@ -1543,7 +1535,6 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
atomic_set(&ntbl->entries, 0); atomic_set(&ntbl->entries, 0);
/* start garbage collection */ /* start garbage collection */
clear_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
queue_delayed_work(priv->wq, &priv->neigh_reap_task, queue_delayed_work(priv->wq, &priv->neigh_reap_task,
arp_tbl.gc_interval); arp_tbl.gc_interval);
...@@ -1653,15 +1644,11 @@ static void ipoib_flush_neighs(struct ipoib_dev_priv *priv) ...@@ -1653,15 +1644,11 @@ static void ipoib_flush_neighs(struct ipoib_dev_priv *priv)
static void ipoib_neigh_hash_uninit(struct net_device *dev) static void ipoib_neigh_hash_uninit(struct net_device *dev)
{ {
struct ipoib_dev_priv *priv = ipoib_priv(dev); struct ipoib_dev_priv *priv = ipoib_priv(dev);
int stopped;
ipoib_dbg(priv, "ipoib_neigh_hash_uninit\n"); ipoib_dbg(priv, "ipoib_neigh_hash_uninit\n");
init_completion(&priv->ntbl.deleted); init_completion(&priv->ntbl.deleted);
/* Stop GC if called at init fail need to cancel work */ cancel_delayed_work_sync(&priv->neigh_reap_task);
stopped = test_and_set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
if (!stopped)
cancel_delayed_work(&priv->neigh_reap_task);
ipoib_flush_neighs(priv); ipoib_flush_neighs(priv);
...@@ -1801,12 +1788,15 @@ int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port) ...@@ -1801,12 +1788,15 @@ int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port)
if (ipoib_ib_dev_open(dev)) { if (ipoib_ib_dev_open(dev)) {
pr_warn("%s failed to open device\n", dev->name); pr_warn("%s failed to open device\n", dev->name);
ret = -ENODEV; ret = -ENODEV;
goto out_dev_uninit; goto out_hash_uninit;
} }
} }
return 0; return 0;
out_hash_uninit:
ipoib_neigh_hash_uninit(dev);
out_dev_uninit: out_dev_uninit:
ipoib_ib_dev_cleanup(dev); ipoib_ib_dev_cleanup(dev);
...@@ -1836,8 +1826,7 @@ void ipoib_dev_cleanup(struct net_device *dev) ...@@ -1836,8 +1826,7 @@ void ipoib_dev_cleanup(struct net_device *dev)
/* Delete any child interfaces first */ /* Delete any child interfaces first */
list_for_each_entry_safe(cpriv, tcpriv, &priv->child_intfs, list) { list_for_each_entry_safe(cpriv, tcpriv, &priv->child_intfs, list) {
/* Stop GC on child */ /* Stop GC on child */
set_bit(IPOIB_STOP_NEIGH_GC, &cpriv->flags); cancel_delayed_work_sync(&cpriv->neigh_reap_task);
cancel_delayed_work(&cpriv->neigh_reap_task);
unregister_netdevice_queue(cpriv->dev, &head); unregister_netdevice_queue(cpriv->dev, &head);
} }
unregister_netdevice_many(&head); unregister_netdevice_many(&head);
...@@ -2345,8 +2334,7 @@ static struct net_device *ipoib_add_port(const char *format, ...@@ -2345,8 +2334,7 @@ static struct net_device *ipoib_add_port(const char *format,
ib_unregister_event_handler(&priv->event_handler); ib_unregister_event_handler(&priv->event_handler);
flush_workqueue(ipoib_workqueue); flush_workqueue(ipoib_workqueue);
/* Stop GC if started before flush */ /* Stop GC if started before flush */
set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags); cancel_delayed_work_sync(&priv->neigh_reap_task);
cancel_delayed_work(&priv->neigh_reap_task);
flush_workqueue(priv->wq); flush_workqueue(priv->wq);
ipoib_dev_cleanup(priv->dev); ipoib_dev_cleanup(priv->dev);
...@@ -2411,8 +2399,7 @@ static void ipoib_remove_one(struct ib_device *device, void *client_data) ...@@ -2411,8 +2399,7 @@ static void ipoib_remove_one(struct ib_device *device, void *client_data)
rtnl_unlock(); rtnl_unlock();
/* Stop GC */ /* Stop GC */
set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags); cancel_delayed_work_sync(&priv->neigh_reap_task);
cancel_delayed_work(&priv->neigh_reap_task);
flush_workqueue(priv->wq); flush_workqueue(priv->wq);
/* Wrap rtnl_lock/unlock with mutex to protect sysfs calls */ /* Wrap rtnl_lock/unlock with mutex to protect sysfs calls */
......
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