summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/net/netdev_lock.h20
-rw-r--r--net/core/dev.c37
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;