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 <duyuy...@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 0617375..27ca55f 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

Reply via email to