diff options
| -rw-r--r-- | include/net/netdev_lock.h | 20 | ||||
| -rw-r--r-- | net/core/dev.c | 37 |
2 files changed, 40 insertions, 17 deletions
diff --git a/include/net/netdev_lock.h b/include/net/netdev_lock.h index 9fb3e93857c3..edd8cc2e2b36 100644 --- a/include/net/netdev_lock.h +++ b/include/net/netdev_lock.h @@ -110,34 +110,20 @@ static inline int netdev_is_locked_ops_compat(const struct net_device *dev) return lockdep_rtnl_is_held(); } -static inline int netdev_lock_cmp_fn(const struct lockdep_map *a, - const struct lockdep_map *b) -{ - if (a == b) - return 0; - - /* Allow locking multiple devices only under rtnl_lock, - * the exact order doesn't matter. - * Note that upper devices don't lock their ops, so nesting - * mostly happens in batched device removal for now. - */ - return lockdep_rtnl_is_held() ? -1 : 1; -} +void netdev_set_instance_lock_class(struct net_device *dev); #define netdev_lockdep_set_classes(dev) \ { \ static struct lock_class_key qdisc_tx_busylock_key; \ static struct lock_class_key qdisc_xmit_lock_key; \ static struct lock_class_key dev_addr_list_lock_key; \ - static struct lock_class_key dev_instance_lock_key; \ unsigned int i; \ \ (dev)->qdisc_tx_busylock = &qdisc_tx_busylock_key; \ lockdep_set_class(&(dev)->addr_list_lock, \ &dev_addr_list_lock_key); \ - lockdep_set_class(&(dev)->lock, \ - &dev_instance_lock_key); \ - lock_set_cmp_fn(&dev->lock, netdev_lock_cmp_fn, NULL); \ + if (IS_ENABLED(CONFIG_PROVE_LOCKING)) \ + netdev_set_instance_lock_class(dev); \ for (i = 0; i < (dev)->num_tx_queues; i++) \ lockdep_set_class(&(dev)->_tx[i]._xmit_lock, \ &qdisc_xmit_lock_key); \ diff --git a/net/core/dev.c b/net/core/dev.c index f660fccfc0db..a8eb382f40ca 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -568,6 +568,41 @@ static inline void netdev_set_addr_lockdep_class(struct net_device *dev) } #endif +#ifdef CONFIG_PROVE_LOCKING +static int netdev_lock_cmp_fn(const struct lockdep_map *a, + const struct lockdep_map *b) +{ + if (a == b) + return 0; + + /* @a and @b must be of same class - both virtual or physical. + * cmp_fn won't be called for devices of different classes. + * + * For the same class only allow nesting under the protection + * of rtnl_lock. Note that we can't use lockdep_rtnl_is_held() + * here, it always answers UNKNOWN from within lockdep. + */ + return rtnl_is_locked() ? -1 : 1; +} + +/* A virtual device can be locked before the physical device it leases + * queues from, see netdev_nl_queue_create_doit(). Keep the two kinds + * in separate classes so the dependency graph enforces the order; + * netdev_lock_cmp_fn() then only has to rule on same-class nesting. + */ +void netdev_set_instance_lock_class(struct net_device *dev) +{ + static struct lock_class_key netdev_virt_instance_lock_key; + + if (dev->dev.parent) + return; + + lockdep_set_class(&dev->lock, &netdev_virt_instance_lock_key); + lock_set_cmp_fn(&dev->lock, netdev_lock_cmp_fn, NULL); +} +EXPORT_SYMBOL_GPL(netdev_set_instance_lock_class); +#endif + /******************************************************************************* * * Protocol management and registration routines @@ -12183,6 +12218,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, #endif mutex_init(&dev->lock); + /* see also netdev_set_instance_lock_class() */ + lock_set_cmp_fn(&dev->lock, netdev_lock_cmp_fn, NULL); netif_rx_mode_init(dev); dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM; |
