[PATCH v2 04/17] locking/lockdep: Update direct dependency's read-write type if it exists

Yuyang Du
2019-05-16T08:00:40Z

When adding a dependency, if the dependency exists - be it direct or
indirect - the dependency's read-write type may be updated.

We can keep all different-typed dependencies, but for simplicity we just
"upgrade" lock types if possible, making them more exlusive (in the order:
recursive read -> read -> write.

For indirect dependency, we do the redundancy check only when it has no
read locks (i.e., write-lock ended dependency); this makes the redundancy
check less complicated, which matters only when CONFIG_LOCKDEP_SMALL.

Signed-off-by: Yuyang Du <duyuyang@gmail.com>
---
kernel/locking/lockdep.c | 61 ++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 57 insertions(+), 4 deletions(-)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 595dc94..1f1cb21 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -1734,6 +1734,16 @@ static inline int get_lock_type2(struct lock_list *lock)
return lock->lock_type[1];
}

+static inline void set_lock_type1(struct lock_list *lock, int read)
+{
+ lock->lock_type[0] = (u16)read;
+}
+
+static inline void set_lock_type2(struct lock_list *lock, int read)
+{
+ lock->lock_type[1] = (u16)read;
+}
+
/*
* Check that the dependency graph starting at <src> can lead to
* <target> or not. Print an error and return 0 if it does.
@@ -1814,6 +1824,32 @@ static inline int get_lock_type2(struct lock_list *lock)
ret = check_path(hlock_class(target), &src_entry, &target_entry);

if (!ret) {
+ struct lock_list *parent;
+
+ /*
+ * Do this indirect dependency has the same type as the
+ * direct dependency?
+ *
+ * Actually, we keep the more exclusive lock type
+ * between the indirect and direct dependencies by
+ * "upgrading" the indirect dependency's lock type if
+ * needed, and then consider this redundant.
+ */
+
+ /* Target end lock type: */
+ if (target->read < get_lock_type2(target_entry))
+ set_lock_type2(target_entry, target->read)
+
+ /* Source end lock type: */
+ parent = find_next_dep_in_path(&src_entry, target_entry);
+ if (!parent) {
+ DEBUG_LOCKS_WARN_ON(1);
+ return 0;
+ }
+
+ if (src->read < get_lock_type1(parent))
+ set_lock_type1(parent, src->read)
+
debug_atomic_inc(nr_redundant);
ret = 2;
} else if (ret < 0)
@@ -2479,6 +2515,17 @@ static inline void inc_chains(void)
*/
list_for_each_entry(entry, &hlock_class(prev)->locks_after, entry) {
if (entry->class == hlock_class(next)) {
+ /*
+ * For direct dependency, smaller type value
+ * generally means more exlusive; we keep the
+ * more exlusive ones, in other words, we
+ * "upgrade" the dependency if we can.
+ */
+ if (prev->read < get_lock_type1(entry))
+ set_lock_type1(entry, prev->read);
+ if (next->read < get_lock_type2(entry))
+ set_lock_type2(entry, next->read);
+
if (distance == 1)
entry->distance = 1;
return 1;
@@ -2487,11 +2534,17 @@ static inline void inc_chains(void)

#ifdef CONFIG_LOCKDEP_SMALL
/*
- * Is the <prev> -> <next> link redundant?
+ * Only when this dependency has no read lock/locks (i.e.,
+ * write-write dependency), we do redundancy check.
*/
- ret = check_redundant(prev, next);
- if (ret != 1)
- return ret;
+ if (!get_dep_type(prev, next)) {
+ /*
+ * Is the <prev> -> <next> link redundant?
+ */
+ ret = check_redundant(prev, next);
+ if (ret != 1)
+ return ret;
+ }
#endif

if (!trace->nr_entries && !save_trace(trace))
--
1.8.3.1