linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Yuyang Du <duyuyang@gmail.com>
To: peterz@infradead.org, will.deacon@arm.com, mingo@kernel.org
Cc: bvanassche@acm.org, ming.lei@redhat.com, frederic@kernel.org,
	tglx@linutronix.de, linux-kernel@vger.kernel.org,
	Yuyang Du <duyuyang@gmail.com>
Subject: [PATCH 19/28] locking/lockdep: Optimize irq usage check when marking lock usage bit
Date: Wed, 24 Apr 2019 18:19:25 +0800	[thread overview]
Message-ID: <20190424101934.51535-20-duyuyang@gmail.com> (raw)
In-Reply-To: <20190424101934.51535-1-duyuyang@gmail.com>

The basic rule concerning IRQ usage related deadlock is to find whether
there is a direct or indirect dependency from an IRQ safe lock to an IRQ
unsafe lock, where an IRQ safe lock means the lock is ever used in IRQ
context (i.e., LOCK_USED_IN_*) while an IRQ unsafe lock means the lock
is ever acquired with IRQ enabled (i.e., LOCK_ENABLED_IN_*).

This rule is checked to find violation at two points:

 - When marking a new IRQ usage bit
 - When adding a new dependency

At each point, a check may take two searches through the dependency
graph:

 - Forward search: walk forwards the dependency graph, try to find an unsafe lock.
 - Backward search: walk backwards the dependency graph, try to find a safe lock.

If graph search/searches are successful, then there is a rule violation.
The current checks are effective albeit they can be significantly
optimized.

----------
Lock usage
----------

Any lock class can be marked as in three exclusive usage states:

 - IRQ safe or IRQ unsafe
 - Lock used (with the LOCK_USED bit marked only), which is neither safe
   nor unsafe, but can transition to either one above state

A lock can not be in both IRQ safe and unsafe states, if so it is a
simple violation of the rule by the node itself, which can be easily
found when marking IRQ usage. Therefore, this violation quest is left
out in the following.

Note that with read-write locks considered, the exclusiveness is a bit
more complex, which will be discussed in later patches.

----------------
Dependency graph
----------------

When checking the rule, the current dependency graph is composed of
nodes of the three exclusive types, and of directed edges as follows,
presumably it has no violation:

 - Edges from safe node to safe node
 - Edges from unsafe node to safe node
 - Edges from unsafe node to unsafe node

 - Edges from used node to safe node
 - Edges from used node to unsafe node
 - Edges from used node to used node
 - Edges from safe node to used node
 - Edges from unsafe node to used node

The first three types are fixed; edges will not change the type over
time. If the graph has only fixed-type edges, checking the rule would be
so easy: just check the direct dependencies involved at the two checking
points, there is even no need to search the entire graph, because a
violation could only be a direct dependency from a safe lock to an
unsafe lock.

The other five types are uncertain. If a used node transitions to other
states, the involved edge types will be altered.

----------------------
New checking algorithm
----------------------

First, two exclusive substates are added to the used state (they will be
discussed later):

 - Reachable from IRQ safe nodes
 - Unreachable from IRQ safe nodes

The new checking algorithm at the two checking points as follows:

1. When adding a new dependency from prev lock to next lock:

 - If prev is in unsafe then done. There is no worry to have any
   new violation from prev forwards so a forward search is not needed. A
   backward search for safe node is not needed either because prev is like
   a shield for next since there has been no safe node to prev.

 - If the prev is in safe state:
   * If next is safe, do a forward search for unsafe node from next
   * If next is unsafe, one violation is found
   * If next is used, do a forward search for unsafe node from next

 - If prev is in used state:
   * If prev is reachable from safe state, do a forward search for
     unsafe states from next
   * If prev is unreachable from safe state, done

A forward search for unsafe node traverses the graph as the current
forward search does. In addition, after a safe-state node or denpendency
is newly added to the graph, if a used node is encountered not marked as
reachable from safe state, then mark it reachable.

2. When marking a new usage bit for a lock

 - Used: this happens only when initializing the lock's usage bit. At
   this time, this node should have no any dependency so nothing needs
   to be done

 - Safe from used:
   * if the node is reachable from safe node, done
   * if the node is unreachable from safe node, do a forward search for unsafe node

 - Unsafe from used:
   * If the node is reachable from safe node, then a violation is found
   * If the node is unreachable from safe node, done

With this new algorithm, the checking efficiency can be improved
significantly. Backward searches are avoided; some forward search cases
are saved as well. As a result, backward dependencies are not needed any
more.

The following patches implement this new IRQ usage checking algorithm.

This starter patch defines and initializes irqsafe_lock and
irqsafe_distance fields in lock_class, which keep track of the irqsafe
locks that reach this lock class. In addition, four bitmaps are declared
to keep track of the irqsafe locks.

Signed-off-by: Yuyang Du <duyuyang@gmail.com>
---
 include/linux/lockdep.h  | 14 ++++++++++++++
 kernel/locking/lockdep.c | 14 ++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h
index 7c2fefa..0e209b8 100644
--- a/include/linux/lockdep.h
+++ b/include/linux/lockdep.h
@@ -72,6 +72,11 @@ struct lock_trace {
 };
 
 #define LOCKSTAT_POINTS		4
+/*
+ * For hardirq and softirq, each has one for irqsafe lock that reaches
+ * this lock and one for irqsafe-read lock that reaches this lock.
+ */
+#define LOCK_IRQ_SAFE_LOCKS	4
 
 /*
  * The lock-class itself. The order of the structure members matters.
@@ -114,6 +119,15 @@ struct lock_class {
 	int				name_version;
 	const char			*name;
 
+	/*
+	 * irqsafe_lock indicates whether there is an IRQ safe lock
+	 * reaches this lock_class in the dependency graph, and if so
+	 * points to it; irqsafe_distance measures the distance from the
+	 * IRQ safe locks, used for keeping the shortest.
+	 */
+	struct lock_class		*irqsafe_lock[LOCK_IRQ_SAFE_LOCKS];
+	int				irqsafe_distance[LOCK_IRQ_SAFE_LOCKS];
+
 #ifdef CONFIG_LOCK_STAT
 	unsigned long			contention_point[LOCKSTAT_POINTS];
 	unsigned long			contending_point[LOCKSTAT_POINTS];
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 6e79bd6..a3138c9 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -1101,6 +1101,7 @@ static bool is_dynamic_key(const struct lock_class_key *key)
 	struct lockdep_subclass_key *key;
 	struct hlist_head *hash_head;
 	struct lock_class *class;
+	int i;
 
 	DEBUG_LOCKS_WARN_ON(!irqs_disabled());
 
@@ -1153,6 +1154,9 @@ static bool is_dynamic_key(const struct lock_class_key *key)
 	WARN_ON_ONCE(!list_empty(&class->locks_before));
 	WARN_ON_ONCE(!list_empty(&class->locks_after));
 	class->name_version = count_matching_names(class);
+	for (i = 0; i < ARRAY_SIZE(class->irqsafe_distance); i++)
+		class->irqsafe_distance[i] = INT_MAX;
+
 	/*
 	 * We use RCU's safe list-add method to make
 	 * parallel walking of the hash-list safe:
@@ -2896,6 +2900,10 @@ static void print_usage_bug_scenario(struct held_lock *lock)
 	return 1;
 }
 
+static DECLARE_BITMAP(lock_classes_hardirq_safe, MAX_LOCKDEP_KEYS);
+static DECLARE_BITMAP(lock_classes_hardirq_safe_read, MAX_LOCKDEP_KEYS);
+static DECLARE_BITMAP(lock_classes_softirq_safe, MAX_LOCKDEP_KEYS);
+static DECLARE_BITMAP(lock_classes_softirq_safe_read, MAX_LOCKDEP_KEYS);
 
 /*
  * print irq inversion bug:
@@ -5001,6 +5009,12 @@ void __init lockdep_init(void)
 		+ sizeof(lock_chains)
 		+ sizeof(lock_chains_in_use)
 		+ sizeof(chain_hlocks)
+#ifdef CONFIG_TRACE_IRQFLAGS
+		+ sizeof(lock_classes_hardirq_safe)
+		+ sizeof(lock_classes_hardirq_safe_read)
+		+ sizeof(lock_classes_softirq_safe)
+		+ sizeof(lock_classes_softirq_safe_read)
+#endif
 #endif
 		) / 1024
 		);
-- 
1.8.3.1


  parent reply	other threads:[~2019-04-24 10:21 UTC|newest]

Thread overview: 52+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-04-24 10:19 [PATCH 00/28] Optimize IRQ usage checks and other small bits Yuyang Du
2019-04-24 10:19 ` [PATCH 01/28] locking/lockdep: Change all print_*() return type to void Yuyang Du
2019-04-24 10:19 ` [PATCH 02/28] locking/lockdep: Add description and explanation in lockdep design doc Yuyang Du
2019-04-25 14:01   ` Peter Zijlstra
2019-04-26  5:41     ` Yuyang Du
2019-04-24 10:19 ` [PATCH 03/28] locking/lockdep: Adjust lock usage bit character checks Yuyang Du
2019-04-24 10:19 ` [PATCH 04/28] locking/lockdep: Remove useless conditional macro Yuyang Du
2019-04-24 10:19 ` [PATCH 05/28] locking/lockdep: Print the right depth for chain key colission Yuyang Du
2019-04-24 10:19 ` [PATCH 06/28] locking/lockdep: Update obsolete struct field description Yuyang Du
2019-04-24 10:19 ` [PATCH 07/28] locking/lockdep: Use lockdep_init_task for task initiation consistently Yuyang Du
2019-04-24 10:19 ` [PATCH 08/28] locking/lockdep: Define INITIAL_CHAIN_KEY for chain keys to start with Yuyang Du
2019-04-24 10:19 ` [PATCH 09/28] locking/lockdep: Change the range of class_idx in held_lock struct Yuyang Du
2019-04-24 10:19 ` [PATCH 10/28] locking/lockdep: Remove unused argument in validate_chain() and check_deadlock() Yuyang Du
2019-04-24 10:19 ` [PATCH 11/28] locking/lockdep: Update comment Yuyang Du
2019-04-24 10:19 ` [PATCH 12/28] locking/lockdep: Change type of the element field in circular_queue Yuyang Du
2019-04-24 10:19 ` [PATCH 13/28] locking/lockdep: Change the return type of __cq_dequeue() Yuyang Du
2019-04-24 10:19 ` [PATCH 14/28] locking/lockdep: Avoid constant checks in __bfs by using offset reference Yuyang Du
2019-04-24 10:19 ` [PATCH 15/28] locking/lockdep: Update comments on dependency search Yuyang Du
2019-04-24 10:19 ` [PATCH 16/28] locking/lockdep: Add explanation to lock usage rules in lockdep design doc Yuyang Du
2019-04-24 10:19 ` [PATCH 17/28] locking/lockdep: Remove redundant argument in check_deadlock Yuyang Du
2019-04-24 10:19 ` [PATCH 18/28] locking/lockdep: Remove unused argument in __lock_release Yuyang Du
2019-04-24 10:19 ` Yuyang Du [this message]
2019-04-25 19:32   ` [PATCH 19/28] locking/lockdep: Optimize irq usage check when marking lock usage bit Peter Zijlstra
2019-04-26  6:57     ` Yuyang Du
2019-04-30 12:11       ` Peter Zijlstra
2019-05-06  3:05         ` Yuyang Du
2019-05-06  3:42           ` Yuyang Du
2019-05-07  1:47         ` Frederic Weisbecker
2019-05-07  2:21           ` Yuyang Du
2019-04-24 10:19 ` [PATCH 20/28] locking/lockdep: Refactorize check_noncircular and check_redundant Yuyang Du
2019-04-25 19:48   ` Peter Zijlstra
2019-04-26  6:48     ` Yuyang Du
2019-04-24 10:19 ` [PATCH 21/28] locking/lockdep: Consolidate lock usage bit initialization Yuyang Du
2019-04-24 10:19 ` [PATCH 22/28] locking/lockdep: Adjust new bit cases in mark_lock Yuyang Du
2019-04-25 19:52   ` Peter Zijlstra
2019-04-26  6:47     ` Yuyang Du
2019-04-24 10:19 ` [PATCH 23/28] locking/lockdep: Update irqsafe lock bitmaps Yuyang Du
2019-04-25 19:55   ` Peter Zijlstra
2019-04-26  6:45     ` Yuyang Du
2019-04-24 10:19 ` [PATCH 24/28] locking/lockdep: Remove !dir in lock irq usage check Yuyang Du
2019-04-25 20:03   ` Peter Zijlstra
2019-04-26  7:06     ` Yuyang Du
2019-04-26  7:25       ` Boqun Feng
2019-04-30 15:35     ` Peter Zijlstra
2019-04-24 10:19 ` [PATCH 25/28] locking/lockdep: Implement new IRQ usage checking algorithm Yuyang Du
2019-04-24 10:19 ` [PATCH 26/28] locking/lockdep: Remove __bfs Yuyang Du
2019-04-25 20:06   ` Peter Zijlstra
2019-04-26  6:35     ` Yuyang Du
2019-04-24 10:19 ` [PATCH 27/28] locking/lockdep: Remove locks_before Yuyang Du
2019-04-24 10:19 ` [PATCH 28/28] locking/lockdep: Reduce lock_list_entries by half Yuyang Du
2019-04-25 18:56 ` [PATCH 00/28] Optimize IRQ usage checks and other small bits Peter Zijlstra
2019-04-26  6:59   ` Yuyang Du

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190424101934.51535-20-duyuyang@gmail.com \
    --to=duyuyang@gmail.com \
    --cc=bvanassche@acm.org \
    --cc=frederic@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ming.lei@redhat.com \
    --cc=mingo@kernel.org \
    --cc=peterz@infradead.org \
    --cc=tglx@linutronix.de \
    --cc=will.deacon@arm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).