@@ -2866,6 +2866,7 @@ void device_initialize(struct device *dev)
mutex_init(&dev->mutex);
#ifdef CONFIG_PROVE_LOCKING
mutex_init(&dev->lockdep_mutex);
+ dev->lock_class = -1;
#endif
lockdep_set_novalidate_class(&dev->mutex);
spin_lock_init(&dev->devres_lock);
@@ -402,6 +402,7 @@ struct dev_msi_info {
* @mutex: Mutex to synchronize calls to its driver.
* @lockdep_mutex: An optional debug lock that a subsystem can use as a
* peer lock to gain localized lockdep coverage of the device_lock.
+ * @lock_class: per-subsystem annotated device lock class
* @bus: Type of bus device is on.
* @driver: Which driver has allocated this
* @platform_data: Platform data specific to the device.
@@ -501,6 +502,7 @@ struct device {
dev_set_drvdata/dev_get_drvdata */
#ifdef CONFIG_PROVE_LOCKING
struct mutex lockdep_mutex;
+ int lock_class;
#endif
struct mutex mutex; /* mutex to synchronize calls to
* its driver.
@@ -762,6 +764,12 @@ static inline bool dev_pm_test_driver_flags(struct device *dev, u32 flags)
return !!(dev->power.driver_flags & flags);
}
+static inline void device_lock_assert(struct device *dev)
+{
+ lockdep_assert_held(&dev->mutex);
+}
+
+#ifndef CONFIG_PROVE_LOCKING
static inline void device_lock(struct device *dev)
{
mutex_lock(&dev->mutex);
@@ -782,10 +790,71 @@ static inline void device_unlock(struct device *dev)
mutex_unlock(&dev->mutex);
}
-static inline void device_lock_assert(struct device *dev)
+static inline void device_set_lock_class(struct device *dev, int lock_class)
{
- lockdep_assert_held(&dev->mutex);
}
+#else
+static inline void device_lock(struct device *dev)
+{
+ lockdep_assert_not_held(&dev->lockdep_mutex);
+
+ mutex_lock(&dev->mutex);
+ if (dev->lock_class >= 0)
+ mutex_lock_nested(&dev->lockdep_mutex, dev->lock_class);
+}
+
+static inline int device_lock_interruptible(struct device *dev)
+{
+ int rc = mutex_lock_interruptible(&dev->mutex);
+
+ if (rc || dev->lock_class < 0)
+ return rc;
+
+ return mutex_lock_interruptible_nested(&dev->lockdep_mutex,
+ dev->lock_class);
+}
+
+static inline int device_trylock(struct device *dev)
+{
+ if (mutex_trylock(&dev->mutex)) {
+ if (dev->lock_class >= 0)
+ mutex_lock_nested(&dev->lockdep_mutex, dev->lock_class);
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline void device_unlock(struct device *dev)
+{
+ if (dev->lock_class >= 0)
+ mutex_unlock(&dev->lockdep_mutex);
+ mutex_unlock(&dev->mutex);
+}
+
+static inline void device_set_lock_class(struct device *dev, int lock_class)
+{
+ if (dev->lock_class < 0 && lock_class > 0) {
+ if (mutex_is_locked(&dev->mutex)) {
+ /*
+ * device_unlock() will unlock lockdep_mutex now that
+ * lock_class is set, so take the paired lock now
+ */
+ mutex_lock_nested(&dev->lockdep_mutex, lock_class);
+ }
+ } else if (dev->lock_class >= 0 && lock_class < 0) {
+ if (mutex_is_locked(&dev->mutex)) {
+ /*
+ * device_unlock() will no longer drop lockdep_mutex now
+ * that lock_class is disabled, so drop the paired lock
+ * now.
+ */
+ mutex_unlock(&dev->lockdep_mutex);
+ }
+ }
+ dev->lock_class = lock_class;
+}
+#endif
static inline struct device_node *dev_of_node(struct device *dev)
{
The @lockdep_mutex attribute of 'struct device' was introduced to allow subsystems to wrap their usage of device_lock() with a local definition that adds nested annotations and lockdep validation. However, that approach leaves lockdep blind to the device_lock usage in the device-core. Instead of requiring the subsystem to replace device_lock() teach the core device_lock() to consider a subsystem specified lock class. While this enables increased coverage of the device_lock() it is still limited to one subsystem at a time unless / until a unique "lockdep_mutex" is added for each subsystem that wants a distinct lock class number space. Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: "Rafael J. Wysocki" <rafael@kernel.org> Signed-off-by: Dan Williams <dan.j.williams@intel.com> --- drivers/base/core.c | 1 + include/linux/device.h | 73 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-)