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