diff --git a/net/psp/psp.h b/net/psp/psp.h index bbb39e2f5b0a..b123c2427905 100644 --- a/net/psp/psp.h +++ b/net/psp/psp.h @@ -19,6 +19,7 @@ bool psp_has_assoc_dev_in_ns(struct psp_dev *psd, struct net *net); int psp_attach_netdev_notifier(void); void psp_nl_notify_dev(struct psp_dev *psd, u32 cmd); +void psp_nl_notify_disassoc(struct psp_dev *psd, struct net *net); struct psp_assoc *psp_assoc_create(struct psp_dev *psd); struct psp_dev *psp_dev_get_for_sock(struct sock *sk); diff --git a/net/psp/psp_main.c b/net/psp/psp_main.c index 91473f96ad21..273b010d2355 100644 --- a/net/psp/psp_main.c +++ b/net/psp/psp_main.c @@ -408,7 +408,7 @@ int psp_dev_rcv(struct sk_buff *skb, u16 dev_id, u8 generation, bool strip_icv) } EXPORT_SYMBOL(psp_dev_rcv); -static void psp_dev_disassoc_one(struct psp_dev *psd, struct net_device *dev) +static bool psp_dev_disassoc_one(struct psp_dev *psd, struct net_device *dev) { struct psp_assoc_dev *entry; @@ -419,9 +419,11 @@ static void psp_dev_disassoc_one(struct psp_dev *psd, struct net_device *dev) rcu_assign_pointer(entry->assoc_dev->psp_dev, NULL); netdev_put(entry->assoc_dev, &entry->dev_tracker); kfree(entry); - return; + return true; } } + + return false; } static int psp_netdev_event(struct notifier_block *nb, unsigned long event, @@ -438,9 +440,13 @@ static int psp_netdev_event(struct notifier_block *nb, unsigned long event, if (psd && psp_dev_tryget(psd)) { rcu_read_unlock(); mutex_lock(&psd->lock); - if (psp_dev_is_registered(psd)) - psp_nl_notify_dev(psd, PSP_CMD_DEV_CHANGE_NTF); - psp_dev_disassoc_one(psd, dev); + /* Nothing to report if the device was never on the list, + * dev-assoc may have failed after publishing dev->psp_dev, + * and this is also the main netdevice's path. + */ + if (psp_dev_disassoc_one(psd, dev) && + psp_dev_is_registered(psd)) + psp_nl_notify_disassoc(psd, dev_net(dev)); mutex_unlock(&psd->lock); psp_dev_put(psd); } else { diff --git a/net/psp/psp_nl.c b/net/psp/psp_nl.c index b57366b5e032..cdfc2d72fb39 100644 --- a/net/psp/psp_nl.c +++ b/net/psp/psp_nl.c @@ -356,6 +356,40 @@ void psp_nl_notify_dev(struct psp_dev *psd, u32 cmd) psp_nl_build_dev_ntf, &cmd); } +/** + * psp_nl_notify_disassoc() - notify about a device losing an association + * @psd: PSP device (must be locked) + * @net: netns of the netdevice which got disassociated + * + * Must be called once @psd no longer has the association, so that the + * notifications carry the state after the change. + */ +void psp_nl_notify_disassoc(struct psp_dev *psd, struct net *net) +{ + struct sk_buff *ntf; + bool still_visible; + u32 cmd; + + lockdep_assert_held(&psd->lock); + + psp_nl_notify_dev(psd, PSP_CMD_DEV_CHANGE_NTF); + + /* psp_nl_notify_dev() reaches the main netdevice's netns and every + * netns which still has an associated device. If @net is neither, + * the device is gone from @net and we should send a delete ntf. + */ + still_visible = !psp_dev_check_access(psd, net, false); + if (still_visible || !maybe_get_net(net)) + return; + + cmd = PSP_CMD_DEV_DEL_NTF; + ntf = psp_nl_build_dev_ntf(psd, net, &cmd); + if (ntf) + genlmsg_multicast_netns(&psp_nl_family, net, ntf, 0, + PSP_NLGRP_MGMT, GFP_KERNEL); + put_net(net); +} + int psp_nl_dev_get_doit(struct sk_buff *req, struct genl_info *info) { struct psp_dev *psd = info->user_ptr[0]; @@ -620,13 +654,6 @@ int psp_nl_dev_disassoc_doit(struct sk_buff *skb, struct genl_info *info) return -ENOMEM; } - put_net(net); - - /* Notify before removal so listeners in the disassociated namespace - * still receive the notification. - */ - psp_nl_notify_dev(psd, PSP_CMD_DEV_CHANGE_NTF); - /* Remove from the association list */ list_del(&found->dev_list); psd->assoc_dev_cnt--; @@ -634,6 +661,9 @@ int psp_nl_dev_disassoc_doit(struct sk_buff *skb, struct genl_info *info) netdev_put(found->assoc_dev, &found->dev_tracker); kfree(found); + psp_nl_notify_disassoc(psd, net); + put_net(net); + return psp_nl_reply_send(rsp, info); }