When creating a xfrmi dev, it always holds the phydev. The xfrmi dev should be deleted when the phydev is being unregistered, so that the phydev can be put on time. Otherwise the phydev's deleting will get stuck:
# ip link add dummy10 type dummy # ip link add xfrmi10 type xfrm dev dummy10 # ip link del dummy10 The last command blocks and dmesg shows: unregister_netdevice: waiting for dummy10 to become free. Usage count = 1 This patch fixes it by adding NETDEV_UNREGISTER event process for xfrmi. Fixes: f203b76d7809 ("xfrm: Add virtual xfrm interfaces") Reported-by: Xiumei Mu <x...@redhat.com> Signed-off-by: Xin Long <lucien....@gmail.com> --- net/xfrm/xfrm_interface.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/net/xfrm/xfrm_interface.c b/net/xfrm/xfrm_interface.c index 74868f9..f3de1f5 100644 --- a/net/xfrm/xfrm_interface.c +++ b/net/xfrm/xfrm_interface.c @@ -877,6 +877,35 @@ static const struct xfrm_if_cb xfrm_if_cb = { .decode_session = xfrmi_decode_session, }; +static int xfrmi_event(struct notifier_block *this, unsigned long e, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct xfrmi_net *xfrmn = net_generic(dev_net(dev), xfrmi_net_id); + struct xfrm_if *xi; + LIST_HEAD(list); + + if (e != NETDEV_UNREGISTER) + return NOTIFY_DONE; + + rcu_read_lock(); + for_each_xfrmi_rcu(xfrmn->xfrmi[0], xi) { + if (xi->phydev != dev) + continue; + unregister_netdevice_queue(xi->dev, &list); + } + rcu_read_unlock(); + + if (list_empty(&list)) + return NOTIFY_DONE; + + unregister_netdevice_many(&list); + return NOTIFY_OK; +} + +static struct notifier_block xfrmi_notifier = { + .notifier_call = xfrmi_event, +}; + static int __init xfrmi_init(void) { const char *msg; @@ -906,6 +935,7 @@ static int __init xfrmi_init(void) goto rtnl_link_failed; xfrm_if_register_cb(&xfrm_if_cb); + register_netdevice_notifier(&xfrmi_notifier); return err; @@ -922,6 +952,7 @@ static int __init xfrmi_init(void) static void __exit xfrmi_fini(void) { + unregister_netdevice_notifier(&xfrmi_notifier); xfrm_if_unregister_cb(); rtnl_link_unregister(&xfrmi_link_ops); xfrmi4_fini(); -- 2.1.0