diff --git a/Documentation/networking/netdevices.rst b/Documentation/networking/netdevices.rst index fde601acd1d2..d2a238f8cc8b 100644 --- a/Documentation/networking/netdevices.rst +++ b/Documentation/networking/netdevices.rst @@ -433,6 +433,8 @@ exceptions) notifiers run under the instance lock. Please extend this documentation whenever you make explicit assumption about lock being held from a notifier. +Drivers **must not** generate nested notifications of the ops-locked types. + NETDEV_INTERNAL symbol namespace ================================ diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 2b74ed56eb16..2d2efb877975 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -77,9 +77,9 @@ static int vlan_group_prealloc_vid(struct vlan_group *vg, return 0; } -static void vlan_stacked_transfer_operstate(const struct net_device *rootdev, - struct net_device *dev, - struct vlan_dev_priv *vlan) +void vlan_stacked_transfer_operstate(const struct net_device *rootdev, + struct net_device *dev, + struct vlan_dev_priv *vlan) { if (!(vlan->flags & VLAN_FLAG_BRIDGE_BINDING)) netif_stacked_transfer_operstate(rootdev, dev); @@ -316,29 +316,6 @@ static void vlan_sync_address(struct net_device *dev, ether_addr_copy(vlan->real_dev_addr, dev->dev_addr); } -static void vlan_transfer_features(struct net_device *dev, - struct net_device *vlandev) -{ - struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev); - - netif_inherit_tso_max(vlandev, dev); - - if (vlan_hw_offload_capable(dev->features, vlan->vlan_proto)) - vlandev->hard_header_len = dev->hard_header_len; - else - vlandev->hard_header_len = dev->hard_header_len + VLAN_HLEN; - -#if IS_ENABLED(CONFIG_FCOE) - vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid; -#endif - - vlandev->priv_flags &= ~IFF_XMIT_DST_RELEASE; - vlandev->priv_flags |= (vlan->real_dev->priv_flags & IFF_XMIT_DST_RELEASE); - vlandev->hw_enc_features = vlan_tnl_features(vlan->real_dev); - - netdev_update_features(vlandev); -} - static int __vlan_device_event(struct net_device *dev, unsigned long event) { int err = 0; @@ -391,13 +368,11 @@ static void vlan_vid0_del(struct net_device *dev) static int vlan_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr); struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct vlan_group *grp; struct vlan_info *vlan_info; int i, flgs; struct net_device *vlandev; - struct vlan_dev_priv *vlan; bool last = false; LIST_HEAD(list); int err; @@ -447,54 +422,19 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, if (vlandev->mtu <= dev->mtu) continue; - dev_set_mtu(vlandev, dev->mtu); + netdev_work_sched(vlandev, VLAN_WORK_MTU); } break; case NETDEV_FEAT_CHANGE: - /* Propagate device features to underlying device */ vlan_group_for_each_dev(grp, i, vlandev) - vlan_transfer_features(dev, vlandev); + netdev_work_sched(vlandev, VLAN_WORK_FEATURES); break; - case NETDEV_DOWN: { - struct net_device *tmp; - LIST_HEAD(close_list); - - /* Put all VLANs for this dev in the down state too. */ - vlan_group_for_each_dev(grp, i, vlandev) { - flgs = vlandev->flags; - if (!(flgs & IFF_UP)) - continue; - - vlan = vlan_dev_priv(vlandev); - if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) - list_add(&vlandev->close_list, &close_list); - } - - netif_close_many(&close_list, false); - - list_for_each_entry_safe(vlandev, tmp, &close_list, close_list) { - vlan_stacked_transfer_operstate(dev, vlandev, - vlan_dev_priv(vlandev)); - list_del_init(&vlandev->close_list); - } - list_del(&close_list); - break; - } + case NETDEV_DOWN: case NETDEV_UP: - /* Put all VLANs for this dev in the up state too. */ - vlan_group_for_each_dev(grp, i, vlandev) { - flgs = netif_get_flags(vlandev); - if (flgs & IFF_UP) - continue; - - vlan = vlan_dev_priv(vlandev); - if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) - dev_change_flags(vlandev, flgs | IFF_UP, - extack); - vlan_stacked_transfer_operstate(dev, vlandev, vlan); - } + vlan_group_for_each_dev(grp, i, vlandev) + netdev_work_sched(vlandev, VLAN_WORK_LINK_STATE); break; case NETDEV_UNREGISTER: diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index c7ffe591d593..c41caaf94095 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -125,6 +125,17 @@ static inline netdev_features_t vlan_tnl_features(struct net_device *real_dev) int vlan_filter_push_vids(struct vlan_info *vlan_info, __be16 proto); void vlan_filter_drop_vids(struct vlan_info *vlan_info, __be16 proto); +/* netdev_work events propagated from the real device, see vlan_dev_work(). */ +enum { + VLAN_WORK_LINK_STATE = BIT(0), /* sync up/down with real_dev */ + VLAN_WORK_MTU = BIT(1), /* clamp mtu to real_dev's */ + VLAN_WORK_FEATURES = BIT(2), /* re-inherit real_dev features */ +}; + +void vlan_stacked_transfer_operstate(const struct net_device *rootdev, + struct net_device *dev, + struct vlan_dev_priv *vlan); + /* found in vlan_dev.c */ void vlan_dev_set_ingress_priority(const struct net_device *dev, u32 skb_prio, u16 vlan_prio); diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 7aa3af8b10ea..ec2569b3f8da 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -270,6 +270,9 @@ static int vlan_dev_open(struct net_device *dev) !(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) return -ENETDOWN; + /* The explicit open supersedes any deferred link-state sync */ + netdev_work_cancel(dev, VLAN_WORK_LINK_STATE); + if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr) && !vlan_dev_inherit_address(dev, real_dev)) { err = dev_uc_add(real_dev, dev->dev_addr); @@ -300,6 +303,9 @@ static int vlan_dev_stop(struct net_device *dev) struct vlan_dev_priv *vlan = vlan_dev_priv(dev); struct net_device *real_dev = vlan->real_dev; + /* The explicit close supersedes any deferred link-state sync */ + netdev_work_cancel(dev, VLAN_WORK_LINK_STATE); + dev_mc_unsync(real_dev, dev); dev_uc_unsync(real_dev, dev); @@ -1016,6 +1022,59 @@ static const struct ethtool_ops vlan_ethtool_ops = { .get_ts_info = vlan_ethtool_get_ts_info, }; +static void vlan_transfer_features(struct net_device *dev, + struct net_device *vlandev) +{ + struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev); + + netif_inherit_tso_max(vlandev, dev); + + if (vlan_hw_offload_capable(dev->features, vlan->vlan_proto)) + vlandev->hard_header_len = dev->hard_header_len; + else + vlandev->hard_header_len = dev->hard_header_len + VLAN_HLEN; + +#if IS_ENABLED(CONFIG_FCOE) + vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid; +#endif + + vlandev->priv_flags &= ~IFF_XMIT_DST_RELEASE; + vlandev->priv_flags |= (vlan->real_dev->priv_flags & IFF_XMIT_DST_RELEASE); + vlandev->hw_enc_features = vlan_tnl_features(vlan->real_dev); + + netdev_update_features(vlandev); +} + +static void vlan_dev_work(struct net_device *vlandev, unsigned long events) +{ + struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev); + struct net_device *real_dev = vlan->real_dev; + bool loose = vlan->flags & VLAN_FLAG_LOOSE_BINDING; + unsigned int flgs; + + if (events & VLAN_WORK_LINK_STATE) { + flgs = netif_get_flags(vlandev); + if (real_dev->flags & IFF_UP) { + if (!(flgs & IFF_UP)) { + if (!loose) + netif_change_flags(vlandev, + flgs | IFF_UP, NULL); + vlan_stacked_transfer_operstate(real_dev, + vlandev, vlan); + } + } else if ((flgs & IFF_UP) && !loose) { + netif_change_flags(vlandev, flgs & ~IFF_UP, NULL); + vlan_stacked_transfer_operstate(real_dev, vlandev, vlan); + } + } + + if ((events & VLAN_WORK_MTU) && vlandev->mtu > real_dev->mtu) + netif_set_mtu(vlandev, real_dev->mtu); + + if (events & VLAN_WORK_FEATURES) + vlan_transfer_features(real_dev, vlandev); +} + static const struct net_device_ops vlan_netdev_ops = { .ndo_change_mtu = vlan_dev_change_mtu, .ndo_init = vlan_dev_init, @@ -1027,6 +1086,7 @@ static const struct net_device_ops vlan_netdev_ops = { .ndo_set_mac_address = vlan_dev_set_mac_address, .ndo_set_rx_mode = vlan_dev_set_rx_mode, .ndo_change_rx_flags = vlan_dev_change_rx_flags, + .ndo_work = vlan_dev_work, .ndo_eth_ioctl = vlan_dev_ioctl, .ndo_neigh_setup = vlan_dev_neigh_setup, .ndo_get_stats64 = vlan_dev_get_stats64, diff --git a/net/core/dev.c b/net/core/dev.c index e1d8af0ef6ab..4b3d5cfdf6e0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -9822,6 +9822,7 @@ int netif_change_flags(struct net_device *dev, unsigned int flags, __dev_notify_flags(dev, old_flags, changes, 0, NULL); return ret; } +EXPORT_SYMBOL(netif_change_flags); int __netif_set_mtu(struct net_device *dev, int new_mtu) {