net: devinet: Reduce refcount before grace period
Currently, the inetdev_destroy() function waits for an RCU grace period before decrementing the refcount and freeing memory. This causes a delay with a new RCU configuration that tries to save power, which results in the network interface disappearing later than expected. The resulting delay causes test failures on ChromeOS. Refactor the code such that the refcount is freed before the grace period and memory is freed after. With this a ChromeOS network test passes that does 'ip netns del' and polls for an interface disappearing, now passes. Reported-by: Joel Fernandes (Google) <joel@joelfernandes.org> Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: Joel Fernandes (Google) <joel@joelfernandes.org> Cc: David Ahern <dsahern@kernel.org> Cc: "David S. Miller" <davem@davemloft.net> Cc: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Paolo Abeni <pabeni@redhat.com> Cc: <netdev@vger.kernel.org> Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
This commit is contained in:
parent
483c26ff63
commit
9d40c84cf5
@ -234,13 +234,20 @@ static void inet_free_ifa(struct in_ifaddr *ifa)
|
||||
call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
|
||||
}
|
||||
|
||||
static void in_dev_free_rcu(struct rcu_head *head)
|
||||
{
|
||||
struct in_device *idev = container_of(head, struct in_device, rcu_head);
|
||||
|
||||
kfree(rcu_dereference_protected(idev->mc_hash, 1));
|
||||
kfree(idev);
|
||||
}
|
||||
|
||||
void in_dev_finish_destroy(struct in_device *idev)
|
||||
{
|
||||
struct net_device *dev = idev->dev;
|
||||
|
||||
WARN_ON(idev->ifa_list);
|
||||
WARN_ON(idev->mc_list);
|
||||
kfree(rcu_dereference_protected(idev->mc_hash, 1));
|
||||
#ifdef NET_REFCNT_DEBUG
|
||||
pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
|
||||
#endif
|
||||
@ -248,7 +255,7 @@ void in_dev_finish_destroy(struct in_device *idev)
|
||||
if (!idev->dead)
|
||||
pr_err("Freeing alive in_device %p\n", idev);
|
||||
else
|
||||
kfree(idev);
|
||||
call_rcu(&idev->rcu_head, in_dev_free_rcu);
|
||||
}
|
||||
EXPORT_SYMBOL(in_dev_finish_destroy);
|
||||
|
||||
@ -298,12 +305,6 @@ out_kfree:
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void in_dev_rcu_put(struct rcu_head *head)
|
||||
{
|
||||
struct in_device *idev = container_of(head, struct in_device, rcu_head);
|
||||
in_dev_put(idev);
|
||||
}
|
||||
|
||||
static void inetdev_destroy(struct in_device *in_dev)
|
||||
{
|
||||
struct net_device *dev;
|
||||
@ -328,7 +329,7 @@ static void inetdev_destroy(struct in_device *in_dev)
|
||||
neigh_parms_release(&arp_tbl, in_dev->arp_parms);
|
||||
arp_ifdown(dev);
|
||||
|
||||
call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
|
||||
in_dev_put(in_dev);
|
||||
}
|
||||
|
||||
int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
|
||||
|
Loading…
Reference in New Issue
Block a user