AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/06 06:47 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": [
    "DEPT",
    "DEPT_AGGRESSIVE_TIMEOUT_WAIT",
    "DEPT_UNIT_TEST"
  ],
  "FocusSymbols": [
    "__add_dep",
    "__add_idep",
    "check_dl_bfs",
    "bfs",
    "print_circle",
    "dept_free_range",
    "dept_key_destroy",
    "disconnect_class"
  ],
  "Reasoning": "The patch introduces DEPT (DEPendency Tracker), a new runtime debugging tool similar to lockdep but for general synchronization primitives (waits/events). It hooks into core kernel primitives like wait queues, completions, page flags, and IRQ handling. Since it adds significant new runtime logic and graph processing for deadlock detection, it is highly functional and should be fuzzed to ensure the tracker itself does not introduce crashes, memory leaks, or deadlocks. We focus on the slow-path graph processing and cleanup functions to avoid skewing the fuzzer on hot-path lock/wait operations.",
  "WorthFuzzing": true
}

1/1 2026/07/06 06:47 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 52576b571949845328063b58e8f0a9fe0d409ce9\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Jul 6 06:47:32 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/Documentation/dev-tools/dept.rst b/Documentation/dev-tools/dept.rst\nnew file mode 100644\nindex 00000000000000..c0ed1314a5a796\n--- /dev/null\n+++ b/Documentation/dev-tools/dept.rst\n@@ -0,0 +1,905 @@\n+DEPT(DEPendency Tracker)\n+========================\n+\n+Started by Byungchul Park \u003cmax.byungchul.park@sk.com\u003e\n+\n+How lockdep works\n+-----------------\n+\n+Lockdep detects deadlocks by checking lock acquisition order. For\n+example, a graph to track acquisition order built by lockdep might look\n+like:\n+\n+.. code-block::\n+\n+   A -\u003e B -\n+           \\\n+            -\u003e E\n+           /\n+   C -\u003e D -\n+\n+   where 'A -\u003e B' means that acquisition A is prior to acquisition B\n+   with A still held.\n+\n+Lockdep keeps adding each new acquisition order into the graph at\n+runtime. For example, 'E -\u003e C' will be added when the two locks have\n+been acquired in the order E and then C. The graph will look like:\n+\n+.. code-block::\n+\n+       A -\u003e B -\n+               \\\n+                -\u003e E -\n+               /      \\\n+    -\u003e C -\u003e D -        \\\n+   /                   /\n+   \\                  /\n+    ------------------\n+\n+   where 'A -\u003e B' means that acquisition A is prior to acquisition B\n+   with A still held.\n+\n+This graph contains a subgraph that demonstrates a loop like:\n+\n+.. code-block::\n+\n+                -\u003e E -\n+               /      \\\n+    -\u003e C -\u003e D -        \\\n+   /                   /\n+   \\                  /\n+    ------------------\n+\n+   where 'A -\u003e B' means that acquisition A is prior to acquisition B\n+   with A still held.\n+\n+Lockdep reports it as a deadlock on detection of a loop and stops\n+working.\n+\n+CONCLUSION\n+\n+Lockdep detects a deadlock by checking if a loop has been created after\n+adding a new acquisition order into the graph.\n+\n+\n+Limitation of lockdep\n+---------------------\n+\n+Lockdep deals with deadlocks involving typical locks e.g. spinlock and\n+mutex, that are supposed to be released within the acquisition context.\n+However, when it comes to a deadlock involving folio lock that is not\n+supposed to be released within the acquisition context or other general\n+synchronization mechanisms, lockdep doesn't work.\n+\n+NOTE: In this document, 'context' refers to any type of unique context\n+e.g. irq context, normal process context, wq worker context, and so on.\n+\n+Can lockdep detect the following deadlock?\n+\n+.. code-block::\n+\n+   context X\t   context Y\t   context Z\n+\n+\t\t   mutex_lock A\n+   folio_lock B\n+\t\t   folio_lock B \u003c- DEADLOCK\n+\t\t\t\t   mutex_lock A \u003c- DEADLOCK\n+\t\t\t\t   folio_unlock B\n+\t\t   folio_unlock B\n+\t\t   mutex_unlock A\n+\t\t\t\t   mutex_unlock A\n+\n+No. What about the following?\n+\n+.. code-block::\n+\n+   context X\t   context Y\n+\n+\t\t   mutex_lock A\n+   mutex_lock A \u003c- DEADLOCK\n+\t\t   wait_for_completion B \u003c- DEADLOCK\n+   complete B\n+\t\t   mutex_unlock A\n+   mutex_unlock A\n+\n+No.\n+\n+CONCLUSION\n+\n+Lockdep cannot detect a deadlock involving folio lock or other general\n+synchronization mechanisms.\n+\n+\n+What leads to a deadlock\n+------------------------\n+\n+A deadlock occurs when one or more contexts are waiting for events that\n+will never happen. For example:\n+\n+.. code-block::\n+\n+   context X\t   context Y\t   context Z\n+\n+   |\t\t   |\t\t   |\n+   v\t\t   |\t\t   |\n+   1 wait for A    v\t\t   |\n+   .\t\t   2 wait for C    v\n+   event C\t   .\t\t   3 wait for B\n+\t\t   event B\t   .\n+\t\t\t\t   event A\n+\n+Event C cannot be triggered because context X is stuck at 1, event B\n+cannot be triggered because context Y is stuck at 2, and event A cannot\n+be triggered because context Z is stuck at 3. All the contexts are stuck.\n+We call this **deadlock**.\n+\n+If an event occurrence to awaken its wait is a prerequisite to reaching\n+another event, we call it **dependency**. In this example:\n+\n+   * Event A occurrence is a prerequisite to reaching event C.\n+   * Event C occurrence is a prerequisite to reaching event B.\n+   * Event B occurrence is a prerequisite to reaching event A.\n+\n+In terms of dependency:\n+\n+   * Event C depends on event A.\n+   * Event B depends on event C.\n+   * Event A depends on event B.\n+\n+Dependency graph reflecting this example will look like:\n+\n+.. code-block::\n+\n+    -\u003e C -\u003e A -\u003e B -\n+   /                \\\n+   \\                /\n+    ----------------\n+\n+   where 'A -\u003e B' means that event A depends on event B.\n+\n+A circular dependency exists. Such a circular dependency leads to a\n+deadlock since no waiters can have desired events triggered.\n+\n+CONCLUSION\n+\n+A circular dependency of events leads to a deadlock.\n+\n+\n+Introduce DEPT\n+--------------\n+\n+DEPT(DEPendency Tracker) tracks wait and event instead of lock\n+acquisition order so as to recognize the following situation:\n+\n+.. code-block::\n+\n+   context X\t   context Y\t   context Z\n+\n+   |\t\t   |\t\t   |\n+   v\t\t   |\t\t   |\n+   wait for A\t   v\t\t   |\n+   .\t\t   wait for C\t   v\n+   event C\t   .\t\t   wait for B\n+\t\t   event B\t   .\n+\t\t\t\t   event A\n+\n+and builds up a dependency graph at runtime that is similar to lockdep.\n+The graph might look like:\n+\n+.. code-block::\n+\n+    -\u003e C -\u003e A -\u003e B -\n+   /                \\\n+   \\                /\n+    ----------------\n+\n+   where 'A -\u003e B' means that event A depends on event B.\n+\n+DEPT keeps adding each new dependency into the graph at runtime. For\n+example, 'B -\u003e D' will be added when event D occurrence is a\n+prerequisite to reaching event B like:\n+\n+.. code-block::\n+\n+   context W\n+\n+   |\n+   v\n+   wait for D\n+   .\n+   event B\n+\n+After the addition, the graph will look like:\n+\n+.. code-block::\n+\n+                     -\u003e D\n+                    /\n+    -\u003e C -\u003e A -\u003e B -\n+   /                \\\n+   \\                /\n+    ----------------\n+\n+   where 'A -\u003e B' means that event A depends on event B.\n+\n+DEPT is going to report a deadlock on detection of a new loop.\n+\n+CONCLUSION\n+\n+DEPT works on wait and event so as to theoretically detect all potential\n+deadlocks.\n+\n+\n+How DEPT works\n+--------------\n+\n+Let's take a look at how DEPT works with the 1st example in the section\n+'Limitation of lockdep'.\n+\n+.. code-block::\n+\n+   context X\t   context Y\t   context Z\n+\n+\t\t   mutex_lock A\n+   folio_lock B\n+\t\t   folio_lock B \u003c- DEADLOCK\n+\t\t\t\t   mutex_lock A \u003c- DEADLOCK\n+\t\t\t\t   folio_unlock B\n+\t\t   folio_unlock B\n+\t\t   mutex_unlock A\n+\t\t\t\t   mutex_unlock A\n+\n+NOTE: In this document, 'event context' refers to a portion within a\n+context where an interesting event is triggered in, between a point\n+where the context has started progressing toward the event, and the\n+event.\n+\n+Adding comments to describe DEPT's view in detail:\n+\n+.. code-block::\n+\n+   context X\t   context Y\t   context Z\n+\n+\t\t   mutex_lock A\n+\t\t   /* might wait for A */\n+\t\t   /* start to take into account event A's context */\n+\t\t   /* 1 */\n+   folio_lock B\n+   /* might wait for B */\n+   /* start to take into account event B's context */\n+   /* 2 */\n+\n+\t\t   folio_lock B\n+\t\t   /* might wait for B */ \u003c- DEADLOCK\n+\t\t   /* start to take into account event B's context */\n+\t\t   /* 3 */\n+\n+\t\t\t\t   mutex_lock A\n+\t\t\t\t   /* might wait for A */ \u003c- DEADLOCK\n+\t\t\t\t   /* start to take into account\n+\t\t\t\t      event A's context */\n+\t\t\t\t   /* 4 */\n+\n+\t\t\t\t   folio_unlock B\n+\t\t\t\t   /* event B that has been valid since 2 */\n+\t\t   folio_unlock B\n+\t\t   /* event B that has been valid since 3 */\n+\n+\t\t   mutex_unlock A\n+\t\t   /* event A that has been valid since 1 */\n+\n+\t\t\t\t   mutex_unlock A\n+\t\t\t\t   /* event A that has been valid since 4 */\n+\n+Let's build up a dependency graph with this example. Firstly, context X:\n+\n+.. code-block::\n+\n+   context X\n+\n+   folio_lock B\n+   /* might wait for B */\n+   /* start to take into account event B's context */\n+   /* 2 */\n+\n+There are no events to create dependency. Next, context Y:\n+\n+.. code-block::\n+\n+   context Y\n+\n+   mutex_lock A\n+   /* might wait for A */\n+   /* start to take into account event A's context */\n+   /* 1 */\n+\n+   folio_lock B\n+   /* might wait for B */\n+   /* start to take into account event B's context */\n+   /* 3 */\n+\n+   folio_unlock B\n+   /* event B that has been valid since 3 */\n+\n+   mutex_unlock A\n+   /* event A that has been valid since 1 */\n+\n+There are two events, folio_unlock B a.k.a. event B and mutex_unlock A\n+a.k.a. event A. For event B, since there are no waits between 3 and the\n+event, event B does not create any dependency. For event A, there is a\n+wait, folio_lock B a.k.a. wait B, between 1 and the event. Which means\n+event A cannot be triggered if wait B cannot be awakened by event B.\n+Therefore, we can say event A depends on event B, say, 'A -\u003e B'. The\n+graph will look like after adding the dependency:\n+\n+.. code-block::\n+\n+   A -\u003e B\n+\n+   where 'A -\u003e B' means that event A depends on event B.\n+\n+Lastly, context Z:\n+\n+.. code-block::\n+\n+   context Z\n+\n+   mutex_lock A\n+   /* might wait for A */\n+   /* start to take into account event A's context */\n+   /* 4 */\n+\n+   folio_unlock B\n+   /* event B that has been valid since 2 */\n+\n+   mutex_unlock A\n+   /* event A that has been valid since 4 */\n+\n+There are also two events, folio_unlock B a.k.a. event B and\n+mutex_unlock A a.k.a. event A. For event B, there is a wait, mutex_lock\n+A a.k.a. wait A, between 2 and the event. Which means event B cannot be\n+triggered if wait A cannot be awakened by event A. Therefore, we can\n+say event B depends on event A, say, 'B -\u003e A'. The graph will look like\n+after adding the dependency:\n+\n+.. code-block::\n+\n+    -\u003e A -\u003e B -\n+   /           \\\n+   \\           /\n+    -----------\n+\n+   where 'A -\u003e B' means that event A depends on event B.\n+\n+A new loop has been created. So DEPT can report it as a deadlock. For\n+event A, since there are no waits between 4 and the event, event A does\n+not create any dependency. That's it.\n+\n+Let's take a look at how DEPT works with the 2nd example in the section\n+'Limitation of lockdep'.\n+\n+.. code-block::\n+\n+   context X\t   context Y\n+\n+\t\t   mutex_lock A\n+   mutex_lock A \u003c- DEADLOCK\n+\t\t   wait_for_completion B \u003c- DEADLOCK\n+   complete B\n+\t\t   mutex_unlock A\n+   mutex_unlock A\n+\n+Similarly adding comments to describe DEPT's view in detail:\n+\n+.. code-block::\n+\n+   context X\t   context Y\n+\n+\t\t   mutex_lock A\n+                   /* might wait for A */\n+                   /* start to take into account event A's context */\n+                   /* 1 */\n+\n+                   request_something_and_complete_B\n+                   /* request to handle something via e.g. wq, daemon,\n+                      or any its own way, and finally do 'complete B'\n+                      a.k.a. event B */\n+                   /* 2 */\n+   /* notice the request from 2 and handle it running toward event B */\n+   /* start to take into account event B's context */\n+   /* 3 */\n+\n+   mutex_lock A\n+   /* might wait for A */ \u003c- DEADLOCK\n+   /* start to take into account event A's context */\n+   /* 4 */\n+\t\t   wait_for_completion B\n+                   /* wait for B */ \u003c- DEADLOCK\n+                   /* 5 */\n+   complete B\n+   /* event B that has been valid since 3 */\n+\t\t   mutex_unlock A\n+                   /* event A that has been valid since 1 */\n+   mutex_unlock A\n+   /* event A that has been valid since 4 */\n+\n+Let's build up a dependency graph with this example. Firstly, context X:\n+\n+.. code-block::\n+\n+   context X\n+\n+   /* notice the request from 2 and handle it running toward event B */\n+   /* start to take into account event B's context */\n+   /* 3 */\n+\n+   mutex_lock A\n+   /* might wait for A */\n+   /* start to take into account event A's context */\n+   /* 4 */\n+\n+   complete B\n+   /* event B that has been valid since 3 */\n+\n+   mutex_unlock A\n+   /* event A that has been valid since 4 */\n+\n+There are two events, complete B a.k.a. event B and mutex_unlock A a.k.a.\n+event A. For event A, since there are no waits between between 4 and the\n+event, event A does not create any dependency. For event B, there is a\n+wait, mutex_lock A a.k.a. wait A, between 3 and the event. Which means\n+event B cannot be triggered if wait A cannot be awakened by event A.\n+Therefore, we can say event B depends on event A, say, 'B -\u003e A'. The\n+graph will look like after adding the dependency:\n+\n+.. code-block::\n+\n+   B -\u003e A\n+\n+   where 'A -\u003e B' means that event A depends on event B.\n+\n+If context X might notice the request after mutex_lock A, DEPT cannot\n+track this dependency, which results in missing a dependency. However,\n+that can be improved by adding proper DEPT annotations if needed.\n+\n+Next, context Y:\n+\n+.. code-block::\n+\n+   context Y\n+\n+   mutex_lock A\n+   /* might wait for A */\n+   /* start to take into account event A's context */\n+   /* 1 */\n+\n+   request_something_and_complete_B\n+   /* request to handle something via e.g. wq, daemon, or any its own\n+      way, and finally do 'complete B' a.k.a. event B */\n+   /* 2 */\n+\n+   wait_for_completion B\n+   /* wait for B */\n+   /* 5 */\n+\n+   mutex_unlock A\n+   /* event A that has been valid since 1 */\n+\n+There is one event, mutex_unlock A a.k.a. event A. For event A, there is\n+a wait, wait_for_completion B a.k.a. wait B, between 1 and the event.\n+Which means event A cannot be triggered if wait B cannot be awakened by\n+event B. Therefore, we can say event A depends on event B, say, 'A -\u003e B'.\n+The graph will look like after adding the dependency:\n+\n+.. code-block::\n+\n+    -\u003e B -\u003e A -\n+   /           \\\n+   \\           /\n+    -----------\n+\n+   where 'A -\u003e B' means that event A depends on event B.\n+\n+A new loop has been created. So DEPT can report it as a deadlock.\n+\n+CONCLUSION\n+\n+DEPT works well with any general synchronization mechanisms by focusing\n+on wait, event and its context.\n+\n+\n+Interpret DEPT report\n+---------------------\n+\n+The following is the same example in the section 'How DEPT works'.\n+\n+.. code-block::\n+\n+   context X\t   context Y\t   context Z\n+\n+\t\t   mutex_lock A\n+\t\t   /* might wait for A */\n+\t\t   /* start to take into account event A's context */\n+\t\t   /* 1 */\n+   folio_lock B\n+   /* might wait for B */\n+   /* start to take into account event B's context */\n+   /* 2 */\n+\n+\t\t   folio_lock B\n+\t\t   /* might wait for B */ \u003c- DEADLOCK\n+\t\t   /* start to take into account event B's context */\n+\t\t   /* 3 */\n+\n+\t\t\t\t   mutex_lock A\n+\t\t\t\t   /* might wait for A */ \u003c- DEADLOCK\n+\t\t\t\t   /* start to take into account\n+\t\t\t\t      event A's context */\n+\t\t\t\t   /* 4 */\n+\n+\t\t\t\t   folio_unlock B\n+\t\t\t\t   /* event B that has been valid since 2 */\n+\t\t   folio_unlock B\n+\t\t   /* event B that has been valid since 3 */\n+\n+\t\t   mutex_unlock A\n+\t\t   /* event A that has been valid since 1 */\n+\n+\t\t\t\t   mutex_unlock A\n+\t\t\t\t   /* event A that has been valid since 4 */\n+\n+We can simplify this by labeling each waiting point with [W], each point\n+where its event's context starts with [S] and each event with [E]. This\n+example will look like after the labeling:\n+\n+.. code-block::\n+\n+   context X\t   context Y\t   context Z\n+\n+\t\t   [W][S] mutex_lock A\n+   [W][S] folio_lock B\n+\t\t   [W][S] folio_lock B \u003c- DEADLOCK\n+\n+\t\t\t\t   [W][S] mutex_lock A \u003c- DEADLOCK\n+\t\t\t\t   [E] folio_unlock B\n+\t\t   [E] folio_unlock B\n+\t\t   [E] mutex_unlock A\n+\t\t\t\t   [E] mutex_unlock A\n+\n+DEPT uses the symbols [W], [S] and [E] in its report as described above.\n+The following is an example reported by DEPT for a real problem in\n+practice.\n+\n+.. code-block::\n+\n+   Link: https://lore.kernel.org/lkml/6383cde5-cf4b-facf-6e07-1378a485657d@I-love.SAKURA.ne.jp/#t\n+   Link: https://lore.kernel.org/lkml/1674268856-31807-1-git-send-email-byungchul.park@lge.com/\n+\n+   ===================================================\n+   DEPT: Circular dependency has been detected.\n+   6.2.0-rc1-00025-gb0c20ebf51ac-dirty #28 Not tainted\n+   ---------------------------------------------------\n+   summary\n+   ---------------------------------------------------\n+   *** DEADLOCK ***\n+\n+   context A\n+       [S] lock(\u0026ni-\u003eni_lock:0)\n+       [W] folio_wait_bit_common(PG_locked_map:0)\n+       [E] unlock(\u0026ni-\u003eni_lock:0)\n+\n+   context B\n+       [S] (unknown)(PG_locked_map:0)\n+       [W] lock(\u0026ni-\u003eni_lock:0)\n+       [E] folio_unlock(PG_locked_map:0)\n+\n+   [S]: start of the event context\n+   [W]: the wait blocked\n+   [E]: the event not reachable\n+   ---------------------------------------------------\n+   context A's detail\n+   ---------------------------------------------------\n+   context A\n+       [S] lock(\u0026ni-\u003eni_lock:0)\n+       [W] folio_wait_bit_common(PG_locked_map:0)\n+       [E] unlock(\u0026ni-\u003eni_lock:0)\n+\n+   [S] lock(\u0026ni-\u003eni_lock:0):\n+   [\u003cffffffff82b396fb\u003e] ntfs3_setattr+0x54b/0xd40\n+   stacktrace:\n+         ntfs3_setattr+0x54b/0xd40\n+         notify_change+0xcb3/0x1430\n+         do_truncate+0x149/0x210\n+         path_openat+0x21a3/0x2a90\n+         do_filp_open+0x1ba/0x410\n+         do_sys_openat2+0x16d/0x4e0\n+         __x64_sys_creat+0xcd/0x120\n+         do_syscall_64+0x41/0xc0\n+         entry_SYSCALL_64_after_hwframe+0x63/0xcd\n+\n+   [W] folio_wait_bit_common(PG_locked_map:0):\n+   [\u003cffffffff81b228b0\u003e] truncate_inode_pages_range+0x9b0/0xf20\n+   stacktrace:\n+         folio_wait_bit_common+0x5e0/0xaf0\n+         truncate_inode_pages_range+0x9b0/0xf20\n+         truncate_pagecache+0x67/0x90\n+         ntfs3_setattr+0x55a/0xd40\n+         notify_change+0xcb3/0x1430\n+         do_truncate+0x149/0x210\n+         path_openat+0x21a3/0x2a90\n+         do_filp_open+0x1ba/0x410\n+         do_sys_openat2+0x16d/0x4e0\n+         __x64_sys_creat+0xcd/0x120\n+         do_syscall_64+0x41/0xc0\n+         entry_SYSCALL_64_after_hwframe+0x63/0xcd\n+\n+   [E] unlock(\u0026ni-\u003eni_lock:0):\n+   (N/A)\n+   ---------------------------------------------------\n+   context B's detail\n+   ---------------------------------------------------\n+   context B\n+       [S] (unknown)(PG_locked_map:0)\n+       [W] lock(\u0026ni-\u003eni_lock:0)\n+       [E] folio_unlock(PG_locked_map:0)\n+\n+   [S] (unknown)(PG_locked_map:0):\n+   (N/A)\n+\n+   [W] lock(\u0026ni-\u003eni_lock:0):\n+   [\u003cffffffff82b009ec\u003e] attr_data_get_block+0x32c/0x19f0\n+   stacktrace:\n+         attr_data_get_block+0x32c/0x19f0\n+         ntfs_get_block_vbo+0x264/0x1330\n+         __block_write_begin_int+0x3bd/0x14b0\n+         block_write_begin+0xb9/0x4d0\n+         ntfs_write_begin+0x27e/0x480\n+         generic_perform_write+0x256/0x570\n+         __generic_file_write_iter+0x2ae/0x500\n+         ntfs_file_write_iter+0x66d/0x1d70\n+         do_iter_readv_writev+0x20b/0x3c0\n+         do_iter_write+0x188/0x710\n+         vfs_iter_write+0x74/0xa0\n+         iter_file_splice_write+0x745/0xc90\n+         direct_splice_actor+0x114/0x180\n+         splice_direct_to_actor+0x33b/0x8b0\n+         do_splice_direct+0x1b7/0x280\n+         do_sendfile+0xb49/0x1310\n+\n+   [E] folio_unlock(PG_locked_map:0):\n+   [\u003cffffffff81f10222\u003e] generic_write_end+0xf2/0x440\n+   stacktrace:\n+         generic_write_end+0xf2/0x440\n+         ntfs_write_end+0x42e/0x980\n+         generic_perform_write+0x316/0x570\n+         __generic_file_write_iter+0x2ae/0x500\n+         ntfs_file_write_iter+0x66d/0x1d70\n+         do_iter_readv_writev+0x20b/0x3c0\n+         do_iter_write+0x188/0x710\n+         vfs_iter_write+0x74/0xa0\n+         iter_file_splice_write+0x745/0xc90\n+         direct_splice_actor+0x114/0x180\n+         splice_direct_to_actor+0x33b/0x8b0\n+         do_splice_direct+0x1b7/0x280\n+         do_sendfile+0xb49/0x1310\n+         __x64_sys_sendfile64+0x1d0/0x210\n+         do_syscall_64+0x41/0xc0\n+         entry_SYSCALL_64_after_hwframe+0x63/0xcd\n+   ---------------------------------------------------\n+   information that might be helpful\n+   ---------------------------------------------------\n+   CPU: 1 PID: 8060 Comm: a.out Not tainted\n+\t6.2.0-rc1-00025-gb0c20ebf51ac-dirty #28\n+   Hardware name: QEMU Standard PC (i440FX + PIIX, 1996),\n+\tBIOS Bochs 01/01/2011\n+   Call Trace:\n+    \u003cTASK\u003e\n+    dump_stack_lvl+0xf2/0x169\n+    print_circle.cold+0xca4/0xd28\n+    ? lookup_dep+0x240/0x240\n+    ? extend_queue+0x223/0x300\n+    cb_check_dl+0x1e7/0x260\n+    bfs+0x27b/0x610\n+    ? print_circle+0x240/0x240\n+    ? llist_add_batch+0x180/0x180\n+    ? extend_queue_rev+0x300/0x300\n+    ? __add_dep+0x60f/0x810\n+    add_dep+0x221/0x5b0\n+    ? __add_idep+0x310/0x310\n+    ? add_iecxt+0x1bc/0xa60\n+    ? add_iecxt+0x1bc/0xa60\n+    ? add_iecxt+0x1bc/0xa60\n+    ? add_iecxt+0x1bc/0xa60\n+    __dept_wait+0x600/0x1490\n+    ? add_iecxt+0x1bc/0xa60\n+    ? truncate_inode_pages_range+0x9b0/0xf20\n+    ? check_new_class+0x790/0x790\n+    ? dept_enirq_transition+0x519/0x9c0\n+    dept_wait+0x159/0x3b0\n+    ? truncate_inode_pages_range+0x9b0/0xf20\n+    folio_wait_bit_common+0x5e0/0xaf0\n+    ? filemap_get_folios_contig+0xa30/0xa30\n+    ? dept_enirq_transition+0x519/0x9c0\n+    ? lock_is_held_type+0x10e/0x160\n+    ? lock_is_held_type+0x11e/0x160\n+    truncate_inode_pages_range+0x9b0/0xf20\n+    ? truncate_inode_partial_folio+0xba0/0xba0\n+    ? setattr_prepare+0x142/0xc40\n+    truncate_pagecache+0x67/0x90\n+    ntfs3_setattr+0x55a/0xd40\n+    ? ktime_get_coarse_real_ts64+0x1e5/0x2f0\n+    ? ntfs_extend+0x5c0/0x5c0\n+    ? mode_strip_sgid+0x210/0x210\n+    ? ntfs_extend+0x5c0/0x5c0\n+    notify_change+0xcb3/0x1430\n+    ? do_truncate+0x149/0x210\n+    do_truncate+0x149/0x210\n+    ? file_open_root+0x430/0x430\n+    ? process_measurement+0x18c0/0x18c0\n+    ? ntfs_file_release+0x230/0x230\n+    path_openat+0x21a3/0x2a90\n+    ? path_lookupat+0x840/0x840\n+    ? dept_enirq_transition+0x519/0x9c0\n+    ? lock_is_held_type+0x10e/0x160\n+    do_filp_open+0x1ba/0x410\n+    ? may_open_dev+0xf0/0xf0\n+    ? find_held_lock+0x2d/0x110\n+    ? lock_release+0x43c/0x830\n+    ? dept_ecxt_exit+0x31a/0x590\n+    ? _raw_spin_unlock+0x3b/0x50\n+    ? alloc_fd+0x2de/0x6e0\n+    do_sys_openat2+0x16d/0x4e0\n+    ? __ia32_sys_get_robust_list+0x3b0/0x3b0\n+    ? build_open_flags+0x6f0/0x6f0\n+    ? dept_enirq_transition+0x519/0x9c0\n+    ? dept_enirq_transition+0x519/0x9c0\n+    ? lock_is_held_type+0x4e/0x160\n+    ? lock_is_held_type+0x4e/0x160\n+    __x64_sys_creat+0xcd/0x120\n+    ? __x64_compat_sys_openat+0x1f0/0x1f0\n+    do_syscall_64+0x41/0xc0\n+    entry_SYSCALL_64_after_hwframe+0x63/0xcd\n+   RIP: 0033:0x7f8b9e4e4469\n+   Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48\n+   89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 \u003c48\u003e\n+   3d 01 f0 ff ff 73 01 c3 48 8b 0d ff 49 2b 00 f7 d8 64 89 01 48\n+   RSP: 002b:00007f8b9eea4ef8 EFLAGS: 00000202 ORIG_RAX: 0000000000000055\n+   RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f8b9e4e4469\n+   RDX: 0000000000737562 RSI: 0000000000000000 RDI: 0000000020000000\n+   RBP: 00007f8b9eea4f20 R08: 0000000000000000 R09: 0000000000000000\n+   R10: 0000000000000000 R11: 0000000000000202 R12: 00007fffa75511ee\n+   R13: 00007fffa75511ef R14: 00007f8b9ee85000 R15: 0000000000000003\n+    \u003c/TASK\u003e\n+\n+Let's take a look at the summary that is the most important part.\n+\n+.. code-block::\n+\n+   ---------------------------------------------------\n+   summary\n+   ---------------------------------------------------\n+   *** DEADLOCK ***\n+\n+   context A\n+       [S] lock(\u0026ni-\u003eni_lock:0)\n+       [W] folio_wait_bit_common(PG_locked_map:0)\n+       [E] unlock(\u0026ni-\u003eni_lock:0)\n+\n+   context B\n+       [S] (unknown)(PG_locked_map:0)\n+       [W] lock(\u0026ni-\u003eni_lock:0)\n+       [E] folio_unlock(PG_locked_map:0)\n+\n+   [S]: start of the event context\n+   [W]: the wait blocked\n+   [E]: the event not reachable\n+\n+The summary shows the following scenario:\n+\n+.. code-block::\n+\n+   context A\t   context B\t   context ?(unknown)\n+\n+\t\t\t\t   [S] folio_lock(\u0026f1)\n+   [S] lock(\u0026ni-\u003eni_lock:0)\n+   [W] folio_wait_bit_common(PG_locked_map:0)\n+\n+\t\t   [W] lock(\u0026ni-\u003eni_lock:0)\n+\t\t   [E] folio_unlock(\u0026f1)\n+\n+   [E] unlock(\u0026ni-\u003eni_lock:0)\n+\n+Adding comments to describe DEPT's view in detail:\n+\n+.. code-block::\n+\n+   context A\t   context B\t   context ?(unknown)\n+\n+\t\t\t\t   [S] folio_lock(\u0026f1)\n+\t\t\t\t   /* start to take into account context\n+\t\t\t\t      B heading for folio_unlock(\u0026f1) */\n+\t\t\t\t   /* 1 */\n+   [S] lock(\u0026ni-\u003eni_lock:0)\n+   /* start to take into account this context heading for\n+      unlock(\u0026ni-\u003eni_lock:0) */\n+   /* 2 */\n+\n+   [W] folio_wait_bit_common(PG_locked_map:0) (= folio_lock(\u0026f1))\n+   /* might wait for folio_unlock(\u0026f1) */\n+\n+\t\t   [W] lock(\u0026ni-\u003eni_lock:0)\n+\t\t   /* might wait for unlock(\u0026ni-\u003eni_lock:0) */\n+\n+\t\t   [E] folio_unlock(\u0026f1)\n+\t\t   /* event that has been valid since 1 */\n+\n+   [E] unlock(\u0026ni-\u003eni_lock:0)\n+   /* event that has been valid since 2 */\n+\n+Let's build up a dependency graph with this report. Firstly, context A:\n+\n+.. code-block::\n+\n+   context A\n+\n+   [S] lock(\u0026ni-\u003eni_lock:0)\n+   /* start to take into account this context heading for\n+      unlock(\u0026ni-\u003eni_lock:0) */\n+   /* 2 */\n+\n+   [W] folio_wait_bit_common(PG_locked_map:0) (= folio_lock(\u0026f1))\n+   /* might wait for folio_unlock(\u0026f1) */\n+\n+   [E] unlock(\u0026ni-\u003eni_lock:0)\n+   /* event that has been valid since 2 */\n+\n+There is one event, unlock(\u0026ni-\u003eni_lock:0). There is a wait,\n+folio_lock(\u0026f1), between 2 and the event. Which means\n+unlock(\u0026ni-\u003eni_lock:0) is not reachable if folio_lock(\u0026f1) cannot be\n+awakened by the owner's folio_unlock(\u0026f1). Therefore, we can say\n+unlock(\u0026ni-\u003eni_lock:0) depends on folio_unlock(\u0026f1), say,\n+'unlock(\u0026ni-\u003eni_lock:0) -\u003e folio_unlock(\u0026f1)'.\n+\n+The graph will look like after adding the dependency:\n+\n+.. code-block::\n+\n+   unlock(\u0026ni-\u003eni_lock:0) -\u003e folio_unlock(\u0026f1)\n+\n+   where 'A -\u003e B' means that event A depends on event B.\n+\n+Secondly, context B:\n+\n+.. code-block::\n+\n+   context B\n+\n+   [W] lock(\u0026ni-\u003eni_lock:0)\n+   /* might wait for unlock(\u0026ni-\u003eni_lock:0) */\n+\n+   [E] folio_unlock(\u0026f1)\n+   /* event that has been valid since 1 */\n+\n+There is also one event, folio_unlock(\u0026f1). There is a wait,\n+lock(\u0026ni-\u003eni_lock:0), between 1 and the event. Which means\n+folio_unlock(\u0026f1) is not reachable if lock(\u0026ni-\u003eni_lock:0) cannot be\n+awakened by the owner's unlock(\u0026ni-\u003eni_lock:0). Therefore, we can say\n+folio_unlock(\u0026f1) depends on unlock(\u0026ni-\u003eni_lock:0), say,\n+'folio_unlock(\u0026f1) -\u003e unlock(\u0026ni-\u003eni_lock:0)'.\n+\n+The graph will look like after adding the dependency:\n+\n+.. code-block::\n+\n+    -\u003e unlock(\u0026ni-\u003eni_lock:0) -\u003e folio_unlock(\u0026f1) -\n+   /                                                \\\n+   \\                                                /\n+    ------------------------------------------------\n+\n+   where 'A -\u003e B' means that event A depends on event B.\n+\n+A new loop has been created. So DEPT can report it as a deadlock! Cool!\n+\n+CONCLUSION\n+\n+DEPT works awesome!\ndiff --git a/Documentation/dev-tools/dept_api.rst b/Documentation/dev-tools/dept_api.rst\nnew file mode 100644\nindex 00000000000000..6706d206f6bfa8\n--- /dev/null\n+++ b/Documentation/dev-tools/dept_api.rst\n@@ -0,0 +1,124 @@\n+DEPT(DEPendency Tracker) APIs\n+=============================\n+\n+Started by Byungchul Park \u003cmax.byungchul.park@sk.com\u003e\n+\n+SDT(Single-event Dependency Tracker) APIs\n+-----------------------------------------\n+Use these APIs to annotate either wait or event. These have been already\n+applied to the existing synchronization primitives e.g. waitqueue, swait,\n+wait_for_completion(), dma fence and so on. The basic APIs of SDT are:\n+\n+.. code-block:: c\n+\n+   /*\n+    * After defining 'struct dept_map map', initialize the instance.\n+    */\n+   sdt_map_init(map);\n+\n+   /*\n+    * Place just before the interesting wait.\n+    */\n+   sdt_wait(map);\n+\n+   /*\n+    * Place just before the interesting event.\n+    */\n+   sdt_event(map);\n+\n+The advanced APIs of SDT are:\n+\n+.. code-block:: c\n+\n+   /*\n+    * After defining 'struct dept_map map', initialize the instance\n+    * using an external key.\n+    */\n+   sdt_map_init_key(map, key);\n+\n+   /*\n+    * Place just before the interesting timeout wait.\n+    */\n+   sdt_wait_timeout(map, time);\n+\n+   /*\n+    * Use sdt_might_sleep_start() and sdt_might_sleep_end() in pair.\n+    * Place at the start of the interesting section that might enter\n+    * schedule() or its family that needs to be woken up by\n+    * try_to_wake_up().\n+    */\n+   sdt_might_sleep_start(map);\n+\n+   /*\n+    * Use sdt_might_sleep_start_timeout() and sdt_might_sleep_end() in\n+    * pair. Place at the start of the interesting section that might\n+    * enter schedule_timeout() or its family that needs to be woken up\n+    * by try_to_wake_up().\n+    */\n+   sdt_might_sleep_start_timeout(map, time);\n+\n+   /*\n+    * Use sdt_might_sleep_start() and sdt_might_sleep_end() in pair.\n+    * Place at the end of the interesting section that might enter\n+    * schedule(), schedule_timeout() or its family that needs to be\n+    * woken up by try_to_wake_up().\n+    */\n+   sdt_might_sleep_end();\n+\n+   /*\n+    * Use sdt_ecxt_enter() and sdt_ecxt_exit() in pair. Place at the\n+    * start of the interesting section where the interesting event might\n+    * be triggered.\n+    */\n+   sdt_ecxt_enter(map);\n+\n+   /*\n+    * Use sdt_ecxt_enter() and sdt_ecxt_exit() in pair. Place at the\n+    * end of the interesting section where the interesting event might\n+    * be triggered.\n+    */\n+   sdt_ecxt_exit(map);\n+\n+\n+LDT(Lock Dependency Tracker) APIs\n+---------------------------------\n+Do not use these APIs directly. These are wrappers for typical locks\n+that have been already applied to major locks internally e.g. spin lock,\n+mutex, rwlock and so on. The APIs of LDT are:\n+\n+.. code-block:: c\n+\n+   ldt_init(map, key, sub, name);\n+   ldt_lock(map, sub_local, try, nest, ip);\n+   ldt_rlock(map, sub_local, try, nest, ip, queued);\n+   ldt_wlock(map, sub_local, try, nest, ip);\n+   ldt_unlock(map, ip);\n+   ldt_downgrade(map, ip);\n+   ldt_set_class(map, name, key, sub_local, ip);\n+\n+\n+Raw APIs\n+--------\n+Do not use these APIs directly. The raw APIs of dept are:\n+\n+.. code-block:: c\n+\n+   dept_free_range(start, size);\n+   dept_map_init(map, key, sub, name);\n+   dept_map_reinit(map, key, sub, name);\n+   dept_ext_wgen_init(ext_wgen);\n+   dept_map_copy(map_to, map_from);\n+   dept_wait(map, wait_flags, ip, wait_func, sub_local, time);\n+   dept_stage_wait(map, key, ip, wait_func, time);\n+   dept_request_event_wait_commit();\n+   dept_clean_stage();\n+   dept_ttwu_stage_wait(task, ip);\n+   dept_ecxt_enter(map, evt_flags, ip, ecxt_func, evt_func, sub_local);\n+   dept_ecxt_holding(map, evt_flags);\n+   dept_request_event(map, ext_wgen);\n+   dept_event(map, evt_flags, ip, evt_func, ext_wgen);\n+   dept_ecxt_exit(map, evt_flags, ip);\n+   dept_ecxt_enter_nokeep(map);\n+   dept_key_init(key);\n+   dept_key_destroy(key);\n+   dept_map_ecxt_modify(map, cur_evt_flags, key, evt_flags, ip, ecxt_func, evt_func, sub_local);\ndiff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/index.rst\nindex 59cbb77b33ff4d..0f37940e4c6e57 100644\n--- a/Documentation/dev-tools/index.rst\n+++ b/Documentation/dev-tools/index.rst\n@@ -23,6 +23,8 @@ Documentation/process/debugging/index.rst\n    coccinelle\n    context-analysis\n    sparse\n+   dept\n+   dept_api\n    kcov\n    gcov\n    kasan\ndiff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c\nindex 35afcfcac5910e..e56044492166f8 100644\n--- a/drivers/dma-buf/dma-fence.c\n+++ b/drivers/dma-buf/dma-fence.c\n@@ -16,6 +16,7 @@\n #include \u003clinux/dma-fence.h\u003e\n #include \u003clinux/sched/signal.h\u003e\n #include \u003clinux/seq_file.h\u003e\n+#include \u003clinux/dept_sdt.h\u003e\n \n #define CREATE_TRACE_POINTS\n #include \u003ctrace/events/dma_fence.h\u003e\n@@ -502,7 +503,7 @@ void dma_fence_signal(struct dma_fence *fence)\n EXPORT_SYMBOL(dma_fence_signal);\n \n /**\n- * dma_fence_wait_timeout - sleep until the fence gets signaled\n+ * __dma_fence_wait_timeout - sleep until the fence gets signaled\n  * or until timeout elapses\n  * @fence: the fence to wait on\n  * @intr: if true, do an interruptible wait\n@@ -520,7 +521,7 @@ EXPORT_SYMBOL(dma_fence_signal);\n  * See also dma_fence_wait() and dma_fence_wait_any_timeout().\n  */\n signed long\n-dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout)\n+__dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout)\n {\n \tsigned long ret;\n \n@@ -549,7 +550,7 @@ dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout)\n \t}\n \treturn ret;\n }\n-EXPORT_SYMBOL(dma_fence_wait_timeout);\n+EXPORT_SYMBOL(__dma_fence_wait_timeout);\n \n /**\n  * dma_fence_release - default release function for fences\n@@ -785,7 +786,7 @@ dma_fence_default_wait_cb(struct dma_fence *fence, struct dma_fence_cb *cb)\n }\n \n /**\n- * dma_fence_default_wait - default sleep until the fence gets signaled\n+ * __dma_fence_default_wait - default sleep until the fence gets signaled\n  * or until timeout elapses\n  * @fence: the fence to wait on\n  * @intr: if true, do an interruptible wait\n@@ -797,7 +798,7 @@ dma_fence_default_wait_cb(struct dma_fence *fence, struct dma_fence_cb *cb)\n  * functions taking a jiffies timeout.\n  */\n signed long\n-dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)\n+__dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)\n {\n \tstruct default_wait_cb cb;\n \tunsigned long flags;\n@@ -822,6 +823,7 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)\n \tcb.task = current;\n \tlist_add(\u0026cb.base.node, \u0026fence-\u003ecb_list);\n \n+\tsdt_might_sleep_start_timeout(NULL, timeout);\n \twhile (!dma_fence_test_signaled_flag(fence) \u0026\u0026 ret \u003e 0) {\n \t\tif (intr)\n \t\t\t__set_current_state(TASK_INTERRUPTIBLE);\n@@ -835,6 +837,7 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)\n \t\tif (ret \u003e 0 \u0026\u0026 intr \u0026\u0026 signal_pending(current))\n \t\t\tret = -ERESTARTSYS;\n \t}\n+\tsdt_might_sleep_end();\n \n \tif (!list_empty(\u0026cb.base.node))\n \t\tlist_del(\u0026cb.base.node);\n@@ -844,7 +847,7 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)\n \tspin_unlock_irqrestore(fence-\u003elock, flags);\n \treturn ret;\n }\n-EXPORT_SYMBOL(dma_fence_default_wait);\n+EXPORT_SYMBOL(__dma_fence_default_wait);\n \n static bool\n dma_fence_test_signaled_any(struct dma_fence **fences, uint32_t count,\n@@ -864,7 +867,7 @@ dma_fence_test_signaled_any(struct dma_fence **fences, uint32_t count,\n }\n \n /**\n- * dma_fence_wait_any_timeout - sleep until any fence gets signaled\n+ * __dma_fence_wait_any_timeout - sleep until any fence gets signaled\n  * or until timeout elapses\n  * @fences: array of fences to wait on\n  * @count: number of fences to wait on\n@@ -884,7 +887,7 @@ dma_fence_test_signaled_any(struct dma_fence **fences, uint32_t count,\n  * See also dma_fence_wait() and dma_fence_wait_timeout().\n  */\n signed long\n-dma_fence_wait_any_timeout(struct dma_fence **fences, uint32_t count,\n+__dma_fence_wait_any_timeout(struct dma_fence **fences, uint32_t count,\n \t\t\t   bool intr, signed long timeout, uint32_t *idx)\n {\n \tstruct default_wait_cb *cb;\n@@ -924,6 +927,7 @@ dma_fence_wait_any_timeout(struct dma_fence **fences, uint32_t count,\n \t\t}\n \t}\n \n+\tsdt_might_sleep_start_timeout(NULL, timeout);\n \twhile (ret \u003e 0) {\n \t\tif (intr)\n \t\t\tset_current_state(TASK_INTERRUPTIBLE);\n@@ -938,6 +942,7 @@ dma_fence_wait_any_timeout(struct dma_fence **fences, uint32_t count,\n \t\tif (ret \u003e 0 \u0026\u0026 intr \u0026\u0026 signal_pending(current))\n \t\t\tret = -ERESTARTSYS;\n \t}\n+\tsdt_might_sleep_end();\n \n \t__set_current_state(TASK_RUNNING);\n \n@@ -950,7 +955,7 @@ dma_fence_wait_any_timeout(struct dma_fence **fences, uint32_t count,\n \n \treturn ret;\n }\n-EXPORT_SYMBOL(dma_fence_wait_any_timeout);\n+EXPORT_SYMBOL(__dma_fence_wait_any_timeout);\n \n /**\n  * DOC: deadline hints\ndiff --git a/include/linux/completion.h b/include/linux/completion.h\nindex fb291567657432..e50f7d9b4b974f 100644\n--- a/include/linux/completion.h\n+++ b/include/linux/completion.h\n@@ -10,6 +10,7 @@\n  */\n \n #include \u003clinux/swait.h\u003e\n+#include \u003clinux/dept_sdt.h\u003e\n \n /*\n  * struct completion - structure used to maintain state for a \"completion\"\n@@ -26,15 +27,30 @@\n struct completion {\n \tunsigned int done;\n \tstruct swait_queue_head wait;\n+\tstruct dept_map *dmap;\n };\n \n-#define init_completion_map(x, m) init_completion(x)\n-static inline void complete_acquire(struct completion *x) {}\n-static inline void complete_release(struct completion *x) {}\n+#define init_completion(x) init_completion_dmap(x, NULL)\n+\n+/*\n+ * XXX: This usage using lockdep's map should be deprecated.\n+ */\n+#define init_completion_map(x, m) init_completion_dmap(x, NULL)\n+\n+static inline void complete_acquire(struct completion *x, long timeout)\n+{\n+}\n+\n+static inline void complete_release(struct completion *x)\n+{\n+}\n \n #define COMPLETION_INITIALIZER(work) \\\n-\t{ 0, __SWAIT_QUEUE_HEAD_INITIALIZER((work).wait) }\n+\t{ 0, __SWAIT_QUEUE_HEAD_INITIALIZER((work).wait), .dmap = NULL, }\n \n+/*\n+ * XXX: This usage using lockdep's map should be deprecated.\n+ */\n #define COMPLETION_INITIALIZER_ONSTACK_MAP(work, map) \\\n \t(*({ init_completion_map(\u0026(work), \u0026(map)); \u0026(work); }))\n \n@@ -75,15 +91,18 @@ static inline void complete_release(struct completion *x) {}\n #endif\n \n /**\n- * init_completion - Initialize a dynamically allocated completion\n+ * init_completion_dmap - Initialize a dynamically allocated completion\n  * @x:  pointer to completion structure that is to be initialized\n+ * @dmap:  pointer to external dept's map to be used as a separated map\n  *\n  * This inline function will initialize a dynamically created completion\n  * structure.\n  */\n-static inline void init_completion(struct completion *x)\n+static inline void init_completion_dmap(struct completion *x,\n+\t\tstruct dept_map *dmap)\n {\n \tx-\u003edone = 0;\n+\tx-\u003edmap = dmap;\n \tinit_swait_queue_head(\u0026x-\u003ewait);\n }\n \n@@ -99,18 +118,18 @@ static inline void reinit_completion(struct completion *x)\n \tx-\u003edone = 0;\n }\n \n-extern void wait_for_completion(struct completion *);\n-extern void wait_for_completion_io(struct completion *);\n-extern int wait_for_completion_interruptible(struct completion *x);\n-extern int wait_for_completion_killable(struct completion *x);\n-extern int wait_for_completion_state(struct completion *x, unsigned int state);\n-extern unsigned long wait_for_completion_timeout(struct completion *x,\n+extern void __wait_for_completion(struct completion *);\n+extern void __wait_for_completion_io(struct completion *);\n+extern int __wait_for_completion_interruptible(struct completion *x);\n+extern int __wait_for_completion_killable(struct completion *x);\n+extern int __wait_for_completion_state(struct completion *x, unsigned int state);\n+extern unsigned long __wait_for_completion_timeout(struct completion *x,\n \t\t\t\t\t\t   unsigned long timeout);\n-extern unsigned long wait_for_completion_io_timeout(struct completion *x,\n+extern unsigned long __wait_for_completion_io_timeout(struct completion *x,\n \t\t\t\t\t\t    unsigned long timeout);\n-extern long wait_for_completion_interruptible_timeout(\n+extern long __wait_for_completion_interruptible_timeout(\n \tstruct completion *x, unsigned long timeout);\n-extern long wait_for_completion_killable_timeout(\n+extern long __wait_for_completion_killable_timeout(\n \tstruct completion *x, unsigned long timeout);\n extern bool try_wait_for_completion(struct completion *x);\n extern bool completion_done(struct completion *x);\n@@ -119,4 +138,79 @@ extern void complete(struct completion *);\n extern void complete_on_current_cpu(struct completion *x);\n extern void complete_all(struct completion *);\n \n+#define wait_for_completion(x)\t\t\t\t\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tsdt_might_sleep_start_timeout((x)-\u003edmap, -1L);\t\t\t\\\n+\t__wait_for_completion(x);\t\t\t\t\t\\\n+\tsdt_might_sleep_end();\t\t\t\t\t\t\\\n+})\n+#define wait_for_completion_io(x)\t\t\t\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tsdt_might_sleep_start_timeout((x)-\u003edmap, -1L);\t\t\t\\\n+\t__wait_for_completion_io(x);\t\t\t\t\t\\\n+\tsdt_might_sleep_end();\t\t\t\t\t\t\\\n+})\n+#define wait_for_completion_interruptible(x)\t\t\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tint __ret;\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\tsdt_might_sleep_start_timeout((x)-\u003edmap, -1L);\t\t\t\\\n+\t__ret = __wait_for_completion_interruptible(x);\t\t\t\\\n+\tsdt_might_sleep_end();\t\t\t\t\t\t\\\n+\t__ret;\t\t\t\t\t\t\t\t\\\n+})\n+#define wait_for_completion_killable(x)\t\t\t\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tint __ret;\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\tsdt_might_sleep_start_timeout((x)-\u003edmap, -1L);\t\t\t\\\n+\t__ret = __wait_for_completion_killable(x);\t\t\t\\\n+\tsdt_might_sleep_end();\t\t\t\t\t\t\\\n+\t__ret;\t\t\t\t\t\t\t\t\\\n+})\n+#define wait_for_completion_state(x, s)\t\t\t\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tint __ret;\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\tsdt_might_sleep_start_timeout((x)-\u003edmap, -1L);\t\t\t\\\n+\t__ret = __wait_for_completion_state(x, s);\t\t\t\\\n+\tsdt_might_sleep_end();\t\t\t\t\t\t\\\n+\t__ret;\t\t\t\t\t\t\t\t\\\n+})\n+#define wait_for_completion_timeout(x, t)\t\t\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tunsigned long __ret;\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\tsdt_might_sleep_start_timeout((x)-\u003edmap, t);\t\t\t\\\n+\t__ret = __wait_for_completion_timeout(x, t);\t\t\t\\\n+\tsdt_might_sleep_end();\t\t\t\t\t\t\\\n+\t__ret;\t\t\t\t\t\t\t\t\\\n+})\n+#define wait_for_completion_io_timeout(x, t)\t\t\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tunsigned long __ret;\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\tsdt_might_sleep_start_timeout((x)-\u003edmap, t);\t\t\t\\\n+\t__ret = __wait_for_completion_io_timeout(x, t);\t\t\t\\\n+\tsdt_might_sleep_end();\t\t\t\t\t\t\\\n+\t__ret;\t\t\t\t\t\t\t\t\\\n+})\n+#define wait_for_completion_interruptible_timeout(x, t)\t\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tlong __ret;\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\tsdt_might_sleep_start_timeout((x)-\u003edmap, t);\t\t\t\\\n+\t__ret = __wait_for_completion_interruptible_timeout(x, t);\t\\\n+\tsdt_might_sleep_end();\t\t\t\t\t\t\\\n+\t__ret;\t\t\t\t\t\t\t\t\\\n+})\n+#define wait_for_completion_killable_timeout(x, t)\t\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tlong __ret;\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\tsdt_might_sleep_start_timeout((x)-\u003edmap, t);\t\t\t\\\n+\t__ret = __wait_for_completion_killable_timeout(x, t);\t\t\\\n+\tsdt_might_sleep_end();\t\t\t\t\t\t\\\n+\t__ret;\t\t\t\t\t\t\t\t\\\n+})\n #endif\ndiff --git a/include/linux/dept.h b/include/linux/dept.h\nnew file mode 100644\nindex 00000000000000..3b8faf5f04cf92\n--- /dev/null\n+++ b/include/linux/dept.h\n@@ -0,0 +1,267 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * DEPT(DEPendency Tracker) - runtime dependency tracker\n+ *\n+ * Started by Byungchul Park \u003cmax.byungchul.park@gmail.com\u003e:\n+ *\n+ *  Copyright (c) 2020 LG Electronics, Inc., Byungchul Park\n+ *  Copyright (c) 2024 SK hynix, Inc., Byungchul Park\n+ */\n+\n+#ifndef __LINUX_DEPT_H\n+#define __LINUX_DEPT_H\n+\n+#ifdef CONFIG_DEPT\n+\n+struct task_struct;\n+\n+#define DEPT_MAX_STACK_ENTRY\t\t16\n+#define DEPT_MAX_WAIT_HIST\t\t64\n+#define DEPT_MAX_ECXT_HELD\t\t48\n+\n+#define DEPT_MAX_SUBCLASSES\t\t24\n+#define DEPT_MAX_SUBCLASSES_EVT\t\t3\n+#define DEPT_MAX_SUBCLASSES_USR\t\t(DEPT_MAX_SUBCLASSES / DEPT_MAX_SUBCLASSES_EVT)\n+#define DEPT_MAX_SUBCLASSES_CACHE\t2\n+\n+enum {\n+\tDEPT_CXT_SIRQ = 0,\n+\tDEPT_CXT_HIRQ,\n+\tDEPT_CXT_IRQS_NR,\n+\tDEPT_CXT_PROCESS = DEPT_CXT_IRQS_NR,\n+\tDEPT_CXTS_NR\n+};\n+\n+#define DEPT_SIRQF\t\t\t(1UL \u003c\u003c DEPT_CXT_SIRQ)\n+#define DEPT_HIRQF\t\t\t(1UL \u003c\u003c DEPT_CXT_HIRQ)\n+\n+struct dept_key {\n+\tunion {\n+\t\t/*\n+\t\t * Each byte-wise address will be used as its key.\n+\t\t */\n+\t\tchar\t\t\tbase[DEPT_MAX_SUBCLASSES];\n+\n+\t\t/*\n+\t\t * for caching the main class pointer\n+\t\t */\n+\t\tstruct dept_class\t*classes[DEPT_MAX_SUBCLASSES_CACHE];\n+\t};\n+};\n+\n+struct dept_map {\n+\tconst char\t\t\t*name;\n+\tstruct dept_key\t\t\t*keys;\n+\n+\t/*\n+\t * keep lockdep map to handle lockdep_set_lock_cmp_fn().\n+\t */\n+\tvoid\t\t\t\t*lockdep_map;\n+\n+\t/*\n+\t * subclass that can be set from user\n+\t */\n+\tint\t\t\t\tsub_u;\n+\n+\t/*\n+\t * It's local copy for fast access to the associated classes.\n+\t * Also used for dept_key for static maps.\n+\t */\n+\tstruct dept_key\t\t\tmap_key;\n+\n+\t/*\n+\t * wait timestamp associated to this map\n+\t */\n+\tunsigned int\t\t\twgen;\n+\n+\t/*\n+\t * whether this map should be going to be checked or not\n+\t */\n+\tbool\t\t\t\tnocheck;\n+};\n+\n+#define DEPT_MAP_INITIALIZER(n, k)\t\t\t\t\t\\\n+{\t\t\t\t\t\t\t\t\t\\\n+\t.name = #n,\t\t\t\t\t\t\t\\\n+\t.keys = (struct dept_key *)(k),\t\t\t\t\t\\\n+\t.lockdep_map = NULL,\t\t\t\t\t\t\\\n+\t.sub_u = 0,\t\t\t\t\t\t\t\\\n+\t.map_key = { .classes = { NULL, } },\t\t\t\t\\\n+\t.wgen = 0U,\t\t\t\t\t\t\t\\\n+\t.nocheck = false,\t\t\t\t\t\t\\\n+}\n+\n+struct dept_ecxt_held {\n+\t/*\n+\t * associated event context\n+\t */\n+\tstruct dept_ecxt\t\t*ecxt;\n+\n+\t/*\n+\t * unique key for this dept_ecxt_held\n+\t */\n+\tstruct dept_map\t\t\t*map;\n+\n+\t/*\n+\t * class of the ecxt of this dept_ecxt_held\n+\t */\n+\tstruct dept_class\t\t*class;\n+\n+\t/*\n+\t * the wgen when the event context started\n+\t */\n+\tunsigned int\t\t\twgen;\n+\n+\t/*\n+\t * subclass that only works in the local context\n+\t */\n+\tint\t\t\t\tsub_l;\n+};\n+\n+struct dept_wait_hist {\n+\t/*\n+\t * associated wait\n+\t */\n+\tstruct dept_wait\t\t*wait;\n+\n+\t/*\n+\t * unique id of all waits system-wise until wrapped\n+\t */\n+\tunsigned int\t\t\twgen;\n+\n+\t/*\n+\t * local context id to identify IRQ context\n+\t */\n+\tunsigned int\t\t\tctxt_id;\n+};\n+\n+/*\n+ * for subsystems that requires compact use of memory e.g. struct page\n+ */\n+struct dept_ext_wgen {\n+\tunsigned int wgen;\n+};\n+\n+enum {\n+\tDEPT_PAGE_DEFAULT = 0,\n+\tDEPT_PAGE_REGFILE_CACHE,\t/* regular file page cache */\n+\tDEPT_PAGE_BDEV_CACHE,\t\t/* block device cache */\n+\tDEPT_PAGE_USAGE_NR,\t\t/* nr of usages options */\n+};\n+\n+#define DEPT_PAGE_USAGE_SHIFT 16\n+#define DEPT_PAGE_USAGE_MASK ((1U \u003c\u003c DEPT_PAGE_USAGE_SHIFT) - 1)\n+#define DEPT_PAGE_USAGE_PENDING_MASK (DEPT_PAGE_USAGE_MASK \u003c\u003c DEPT_PAGE_USAGE_SHIFT)\n+\n+/*\n+ * Identify each page's usage type\n+ */\n+struct dept_page_usage {\n+\t/*\n+\t * low 16 bits  : the current usage type\n+\t * high 16 bits : usage type requested to be set\n+\t *\n+\t * Do not apply usage type on request immediately but postpone\n+\t * it until the next use of PG flags.  For example, if the page\n+\t * is already within a PG_locked critical section, regard it as\n+\t * DEPT_PAGE_DEFAULT temporarily at least until the section ends\n+\t * e.g. folio_unlock() since it's still unclear which usage type\n+\t * the page acts within the section.\n+\t */\n+\tatomic_t type; /* Update and read atomically */\n+};\n+\n+void dept_stop_emerg(void);\n+void dept_on(void);\n+void dept_off(void);\n+void dept_init(void);\n+void dept_task_init(struct task_struct *t);\n+void dept_task_exit(struct task_struct *t);\n+void dept_free_range(void *start, unsigned int sz);\n+\n+void dept_map_init(struct dept_map *m, struct dept_key *k, int sub_u, const char *n);\n+void dept_map_reinit(struct dept_map *m, struct dept_key *k, int sub_u, const char *n);\n+void dept_ext_wgen_init(struct dept_ext_wgen *ewg);\n+void dept_map_copy(struct dept_map *to, struct dept_map *from);\n+void dept_wait(struct dept_map *m, unsigned long w_f, unsigned long ip, const char *w_fn, int sub_l, long timeout);\n+void dept_stage_wait(struct dept_map *m, struct dept_key *k, unsigned long ip, const char *w_fn, long timeout);\n+void dept_request_event_wait_commit(void);\n+void dept_clean_stage(void);\n+void dept_ttwu_stage_wait(struct task_struct *t, unsigned long ip);\n+void dept_ecxt_enter(struct dept_map *m, unsigned long e_f, unsigned long ip, const char *c_fn, const char *e_fn, int sub_l);\n+bool dept_ecxt_holding(struct dept_map *m, unsigned long e_f);\n+void dept_request_event(struct dept_map *m, struct dept_ext_wgen *ewg);\n+void dept_event(struct dept_map *m, unsigned long e_f, unsigned long ip, const char *e_fn, struct dept_ext_wgen *ewg);\n+void dept_ecxt_exit(struct dept_map *m, unsigned long e_f, unsigned long ip);\n+void dept_sched_enter(void);\n+void dept_sched_exit(void);\n+void dept_update_cxt(void);\n+\n+static inline void dept_ecxt_enter_nokeep(struct dept_map *m)\n+{\n+\tdept_ecxt_enter(m, 0UL, 0UL, NULL, NULL, 0);\n+}\n+\n+/*\n+ * for users who want to manage external keys\n+ */\n+void dept_key_init(struct dept_key *k);\n+void dept_key_destroy(struct dept_key *k);\n+void dept_map_ecxt_modify(struct dept_map *m, unsigned long e_f, struct dept_key *new_k, unsigned long new_e_f, unsigned long new_ip, const char *new_c_fn, const char *new_e_fn, int new_sub_l);\n+\n+void dept_softirq_enter(void);\n+void dept_hardirq_enter(void);\n+void dept_softirqs_on_ip(unsigned long ip);\n+void dept_hardirqs_on(void);\n+void dept_softirqs_off(void);\n+void dept_hardirqs_off(void);\n+\n+#define dept_set_lockdep_map(m, lockdep_m) ({ (m)-\u003elockdep_map = lockdep_m; })\n+#else /* !CONFIG_DEPT */\n+struct dept_key { };\n+struct dept_map { };\n+struct dept_ext_wgen { };\n+struct dept_page_usage { };\n+\n+#define DEPT_MAP_INITIALIZER(n, k) { }\n+\n+#define dept_stop_emerg()\t\t\t\tdo { } while (0)\n+#define dept_on()\t\t\t\t\tdo { } while (0)\n+#define dept_off()\t\t\t\t\tdo { } while (0)\n+#define dept_init()\t\t\t\t\tdo { } while (0)\n+#define dept_task_init(t)\t\t\t\tdo { } while (0)\n+#define dept_task_exit(t)\t\t\t\tdo { } while (0)\n+#define dept_free_range(s, sz)\t\t\t\tdo { } while (0)\n+\n+#define dept_map_init(m, k, su, n)\t\t\tdo { (void)(n); (void)(k); } while (0)\n+#define dept_map_reinit(m, k, su, n)\t\t\tdo { (void)(n); (void)(k); } while (0)\n+#define dept_ext_wgen_init(wg)\t\t\t\tdo { } while (0)\n+#define dept_map_copy(t, f)\t\t\t\tdo { } while (0)\n+#define dept_wait(m, w_f, ip, w_fn, sl, t)\t\tdo { (void)(w_fn); } while (0)\n+#define dept_stage_wait(m, k, ip, w_fn, t)\t\tdo { (void)(k); (void)(w_fn); } while (0)\n+#define dept_request_event_wait_commit()\t\tdo { } while (0)\n+#define dept_clean_stage()\t\t\t\tdo { } while (0)\n+#define dept_ttwu_stage_wait(t, ip)\t\t\tdo { } while (0)\n+#define dept_ecxt_enter(m, e_f, ip, c_fn, e_fn, sl)\tdo { (void)(c_fn); (void)(e_fn); } while (0)\n+#define dept_ecxt_holding(m, e_f)\t\t\tfalse\n+#define dept_request_event(m, wg)\t\t\tdo { } while (0)\n+#define dept_event(m, e_f, ip, e_fn, wg)\t\tdo { (void)(e_fn); } while (0)\n+#define dept_ecxt_exit(m, e_f, ip)\t\t\tdo { } while (0)\n+#define dept_sched_enter()\t\t\t\tdo { } while (0)\n+#define dept_sched_exit()\t\t\t\tdo { } while (0)\n+#define dept_update_cxt()\t\t\t\tdo { } while (0)\n+#define dept_ecxt_enter_nokeep(m)\t\t\tdo { } while (0)\n+#define dept_key_init(k)\t\t\t\tdo { (void)(k); } while (0)\n+#define dept_key_destroy(k)\t\t\t\tdo { (void)(k); } while (0)\n+#define dept_map_ecxt_modify(m, e_f, n_k, n_e_f, n_ip, n_c_fn, n_e_fn, n_sl) do { (void)(n_k); (void)(n_c_fn); (void)(n_e_fn); } while (0)\n+\n+#define dept_softirq_enter()\t\t\t\tdo { } while (0)\n+#define dept_hardirq_enter()\t\t\t\tdo { } while (0)\n+#define dept_softirqs_on_ip(ip)\t\t\t\tdo { } while (0)\n+#define dept_hardirqs_on()\t\t\t\tdo { } while (0)\n+#define dept_softirqs_off()\t\t\t\tdo { } while (0)\n+#define dept_hardirqs_off()\t\t\t\tdo { } while (0)\n+\n+#define dept_set_lockdep_map(m, lockdep_m)\t\tdo { } while (0)\n+#endif\n+#endif /* __LINUX_DEPT_H */\ndiff --git a/include/linux/dept_ldt.h b/include/linux/dept_ldt.h\nnew file mode 100644\nindex 00000000000000..730af2517ecd41\n--- /dev/null\n+++ b/include/linux/dept_ldt.h\n@@ -0,0 +1,78 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * Lock Dependency Tracker\n+ *\n+ * Started by Byungchul Park \u003cmax.byungchul.park@gmail.com\u003e:\n+ *\n+ *  Copyright (c) 2020 LG Electronics, Inc., Byungchul Park\n+ *  Copyright (c) 2024 SK hynix, Inc., Byungchul Park\n+ */\n+\n+#ifndef __LINUX_DEPT_LDT_H\n+#define __LINUX_DEPT_LDT_H\n+\n+#include \u003clinux/dept.h\u003e\n+\n+#ifdef CONFIG_DEPT\n+#define LDT_EVT_L\t\t\t1UL\n+#define LDT_EVT_R\t\t\t2UL\n+#define LDT_EVT_W\t\t\t1UL\n+#define LDT_EVT_RW\t\t\t(LDT_EVT_R | LDT_EVT_W)\n+#define LDT_EVT_ALL\t\t\t(LDT_EVT_L | LDT_EVT_RW)\n+\n+#define ldt_init(m, k, su, n)\t\tdept_map_init(m, k, su, n)\n+#define ldt_lock(m, sl, t, n, i)\t\t\t\t\t\\\n+\tdo {\t\t\t\t\t\t\t\t\\\n+\t\tif (n)\t\t\t\t\t\t\t\\\n+\t\t\tdept_ecxt_enter_nokeep(m);\t\t\t\\\n+\t\telse if (t)\t\t\t\t\t\t\\\n+\t\t\tdept_ecxt_enter(m, LDT_EVT_L, i, \"trylock\", \"unlock\", sl);\\\n+\t\telse {\t\t\t\t\t\t\t\\\n+\t\t\tdept_wait(m, LDT_EVT_L, i, \"lock\", sl, false);\t\\\n+\t\t\tdept_ecxt_enter(m, LDT_EVT_L, i, \"lock\", \"unlock\", sl);\\\n+\t\t}\t\t\t\t\t\t\t\\\n+\t} while (0)\n+\n+#define ldt_rlock(m, sl, t, n, i, q)\t\t\t\t\t\\\n+\tdo {\t\t\t\t\t\t\t\t\\\n+\t\tif (n)\t\t\t\t\t\t\t\\\n+\t\t\tdept_ecxt_enter_nokeep(m);\t\t\t\\\n+\t\telse if (t)\t\t\t\t\t\t\\\n+\t\t\tdept_ecxt_enter(m, LDT_EVT_R, i, \"read_trylock\", \"read_unlock\", sl);\\\n+\t\telse {\t\t\t\t\t\t\t\\\n+\t\t\tdept_wait(m, q ? LDT_EVT_RW : LDT_EVT_W, i, \"read_lock\", sl, false);\\\n+\t\t\tdept_ecxt_enter(m, LDT_EVT_R, i, \"read_lock\", \"read_unlock\", sl);\\\n+\t\t}\t\t\t\t\t\t\t\\\n+\t} while (0)\n+\n+#define ldt_wlock(m, sl, t, n, i)\t\t\t\t\t\\\n+\tdo {\t\t\t\t\t\t\t\t\\\n+\t\tif (n)\t\t\t\t\t\t\t\\\n+\t\t\tdept_ecxt_enter_nokeep(m);\t\t\t\\\n+\t\telse if (t)\t\t\t\t\t\t\\\n+\t\t\tdept_ecxt_enter(m, LDT_EVT_W, i, \"write_trylock\", \"write_unlock\", sl);\\\n+\t\telse {\t\t\t\t\t\t\t\\\n+\t\t\tdept_wait(m, LDT_EVT_RW, i, \"write_lock\", sl, false);\\\n+\t\t\tdept_ecxt_enter(m, LDT_EVT_W, i, \"write_lock\", \"write_unlock\", sl);\\\n+\t\t}\t\t\t\t\t\t\t\\\n+\t} while (0)\n+\n+#define ldt_unlock(m, i)\t\tdept_ecxt_exit(m, LDT_EVT_ALL, i)\n+\n+#define ldt_downgrade(m, i)\t\t\t\t\t\t\\\n+\tdo {\t\t\t\t\t\t\t\t\\\n+\t\tif (dept_ecxt_holding(m, LDT_EVT_W))\t\t\t\\\n+\t\t\tdept_map_ecxt_modify(m, LDT_EVT_W, NULL, LDT_EVT_R, i, \"downgrade\", \"read_unlock\", -1);\\\n+\t} while (0)\n+\n+#define ldt_set_class(m, n, k, sl, i)\tdept_map_ecxt_modify(m, LDT_EVT_ALL, k, 0UL, i, \"lock_set_class\", \"(any)unlock\", sl)\n+#else /* !CONFIG_DEPT */\n+#define ldt_init(m, k, su, n)\t\tdo { (void)(k); } while (0)\n+#define ldt_lock(m, sl, t, n, i)\tdo { } while (0)\n+#define ldt_rlock(m, sl, t, n, i, q)\tdo { } while (0)\n+#define ldt_wlock(m, sl, t, n, i)\tdo { } while (0)\n+#define ldt_unlock(m, i)\t\tdo { } while (0)\n+#define ldt_downgrade(m, i)\t\tdo { } while (0)\n+#define ldt_set_class(m, n, k, sl, i)\tdo { } while (0)\n+#endif\n+#endif /* __LINUX_DEPT_LDT_H */\ndiff --git a/include/linux/dept_sdt.h b/include/linux/dept_sdt.h\nnew file mode 100644\nindex 00000000000000..9cd70affaf35c8\n--- /dev/null\n+++ b/include/linux/dept_sdt.h\n@@ -0,0 +1,68 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * Single-event Dependency Tracker\n+ *\n+ * Started by Byungchul Park \u003cmax.byungchul.park@gmail.com\u003e:\n+ *\n+ *  Copyright (c) 2020 LG Electronics, Inc., Byungchul Park\n+ *  Copyright (c) 2024 SK hynix, Inc., Byungchul Park\n+ */\n+\n+#ifndef __LINUX_DEPT_SDT_H\n+#define __LINUX_DEPT_SDT_H\n+\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/dept.h\u003e\n+\n+#ifdef CONFIG_DEPT\n+#define sdt_map_init(m)\t\t\t\t\t\t\t\\\n+\tdo {\t\t\t\t\t\t\t\t\\\n+\t\tstatic struct dept_key __key;\t\t\t\t\\\n+\t\tdept_map_init(m, \u0026__key, 0, #m);\t\t\t\\\n+\t} while (0)\n+\n+#define sdt_map_init_key(m, k)\t\tdept_map_init(m, k, 0, #m)\n+\n+#define sdt_wait_timeout(m, t)\t\t\t\t\t\t\\\n+\tdo {\t\t\t\t\t\t\t\t\\\n+\t\tdept_request_event(m, NULL);\t\t\t\t\\\n+\t\tdept_wait(m, 1UL, _THIS_IP_, __func__, 0, t);\t\t\\\n+\t} while (0)\n+#define sdt_wait(m) sdt_wait_timeout(m, -1L)\n+\n+/*\n+ * sdt_might_sleep() and its family will be committed in __schedule()\n+ * when it actually gets to __schedule(). Both dept_request_event() and\n+ * dept_wait() will be performed on the commit.\n+ */\n+\n+/*\n+ * Use the code location as the class key if an explicit map is not used.\n+ */\n+#define sdt_might_sleep_start_timeout(m, t)\t\t\t\t\\\n+\tdo {\t\t\t\t\t\t\t\t\\\n+\t\tstruct dept_map *__m = m;\t\t\t\t\\\n+\t\tstatic struct dept_key __key;\t\t\t\t\\\n+\t\tdept_stage_wait(__m, __m ? NULL : \u0026__key, _THIS_IP_, __func__, t);\\\n+\t} while (0)\n+#define sdt_might_sleep_start(m)\tsdt_might_sleep_start_timeout(m, -1L)\n+#define sdt_might_sleep_end()\t\tdept_clean_stage()\n+\n+#define sdt_ecxt_enter(m)\t\tdept_ecxt_enter(m, 1UL, _THIS_IP_, \"start\", \"event\", 0)\n+#define sdt_event(m)\t\t\tdept_event(m, 1UL, _THIS_IP_, __func__, NULL)\n+#define sdt_ecxt_exit(m)\t\tdept_ecxt_exit(m, 1UL, _THIS_IP_)\n+#define sdt_request_event(m)\t\tdept_request_event(m, NULL)\n+#else /* !CONFIG_DEPT */\n+#define sdt_map_init(m)\t\t\tdo { } while (0)\n+#define sdt_map_init_key(m, k)\t\tdo { (void)(k); } while (0)\n+#define sdt_wait_timeout(m, t)\t\tdo { } while (0)\n+#define sdt_wait(m)\t\t\tdo { } while (0)\n+#define sdt_might_sleep_start_timeout(m, t) do { } while (0)\n+#define sdt_might_sleep_start(m)\tdo { } while (0)\n+#define sdt_might_sleep_end()\t\tdo { } while (0)\n+#define sdt_ecxt_enter(m)\t\tdo { } while (0)\n+#define sdt_event(m)\t\t\tdo { } while (0)\n+#define sdt_ecxt_exit(m)\t\tdo { } while (0)\n+#define sdt_request_event(m)\t\tdo { } while (0)\n+#endif\n+#endif /* __LINUX_DEPT_SDT_H */\ndiff --git a/include/linux/dept_unit_test.h b/include/linux/dept_unit_test.h\nnew file mode 100644\nindex 00000000000000..753ac9ac727c65\n--- /dev/null\n+++ b/include/linux/dept_unit_test.h\n@@ -0,0 +1,61 @@\n+// SPDX-License-Identifier: GPL-2.0+\n+/*\n+ * DEPT unit test\n+ *\n+ * Started by Byungchul Park \u003cmax.byungchul.park@gmail.com\u003e:\n+ *\n+ *  Copyright (c) 2025 SK hynix, Inc., Byungchul Park\n+ */\n+\n+#ifndef __LINUX_DEPT_UNIT_TEST_H\n+#define __LINUX_DEPT_UNIT_TEST_H\n+\n+#if defined(CONFIG_DEPT_UNIT_TEST) || defined(CONFIG_DEPT_UNIT_TEST_MODULE)\n+struct dept_ut {\n+\tbool circle_detected;\n+\n+\tint ecxt_stack_total_cnt;\n+\tint wait_stack_total_cnt;\n+\tint evnt_stack_total_cnt;\n+\tint ecxt_stack_valid_cnt;\n+\tint wait_stack_valid_cnt;\n+\tint evnt_stack_valid_cnt;\n+};\n+\n+extern struct dept_ut dept_ut_results;\n+\n+static inline void dept_ut_circle_detect(void)\n+{\n+\tdept_ut_results.circle_detected = true;\n+}\n+static inline void dept_ut_ecxt_stack_account(bool valid)\n+{\n+\tdept_ut_results.ecxt_stack_total_cnt++;\n+\n+\tif (valid)\n+\t\tdept_ut_results.ecxt_stack_valid_cnt++;\n+}\n+static inline void dept_ut_wait_stack_account(bool valid)\n+{\n+\tdept_ut_results.wait_stack_total_cnt++;\n+\n+\tif (valid)\n+\t\tdept_ut_results.wait_stack_valid_cnt++;\n+}\n+static inline void dept_ut_evnt_stack_account(bool valid)\n+{\n+\tdept_ut_results.evnt_stack_total_cnt++;\n+\n+\tif (valid)\n+\t\tdept_ut_results.evnt_stack_valid_cnt++;\n+}\n+#else\n+struct dept_ut {};\n+\n+#define dept_ut_circle_detect() do { } while (0)\n+#define dept_ut_ecxt_stack_account(v) do { } while (0)\n+#define dept_ut_wait_stack_account(v) do { } while (0)\n+#define dept_ut_evnt_stack_account(v) do { } while (0)\n+\n+#endif\n+#endif /* __LINUX_DEPT_UNIT_TEST_H */\ndiff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h\nindex d4c92fd3509247..3732849a30b7ee 100644\n--- a/include/linux/dma-fence.h\n+++ b/include/linux/dma-fence.h\n@@ -370,8 +370,22 @@ bool dma_fence_check_and_signal_locked(struct dma_fence *fence);\n void dma_fence_signal_locked(struct dma_fence *fence);\n void dma_fence_signal_timestamp(struct dma_fence *fence, ktime_t timestamp);\n void dma_fence_signal_timestamp_locked(struct dma_fence *fence, ktime_t timestamp);\n-signed long dma_fence_default_wait(struct dma_fence *fence,\n+signed long __dma_fence_default_wait(struct dma_fence *fence,\n \t\t\t\t   bool intr, signed long timeout);\n+\n+/*\n+ * Associate every caller with its own dept map.\n+ */\n+#define dma_fence_default_wait(f, intr, t)\t\t\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tsigned long __ret;\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\tsdt_might_sleep_start_timeout(NULL, t);\t\t\t\t\\\n+\t__ret = __dma_fence_default_wait(f, intr, t);\t\t\t\\\n+\tsdt_might_sleep_end();\t\t\t\t\t\t\\\n+\t__ret;\t\t\t\t\t\t\t\t\\\n+})\n+\n int dma_fence_add_callback(struct dma_fence *fence,\n \t\t\t   struct dma_fence_cb *cb,\n \t\t\t   dma_fence_func_t func);\n@@ -628,12 +642,37 @@ static inline ktime_t dma_fence_timestamp(struct dma_fence *fence)\n \treturn fence-\u003etimestamp;\n }\n \n-signed long dma_fence_wait_timeout(struct dma_fence *,\n+signed long __dma_fence_wait_timeout(struct dma_fence *,\n \t\t\t\t   bool intr, signed long timeout);\n-signed long dma_fence_wait_any_timeout(struct dma_fence **fences,\n+signed long __dma_fence_wait_any_timeout(struct dma_fence **fences,\n \t\t\t\t       uint32_t count,\n \t\t\t\t       bool intr, signed long timeout,\n \t\t\t\t       uint32_t *idx);\n+/*\n+ * Associate every caller with its own dept map.\n+ */\n+#define dma_fence_wait_timeout(f, intr, t)\t\t\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tsigned long __ret;\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\tsdt_might_sleep_start_timeout(NULL, t);\t\t\t\t\\\n+\t__ret = __dma_fence_wait_timeout(f, intr, t);\t\t\t\\\n+\tsdt_might_sleep_end();\t\t\t\t\t\t\\\n+\t__ret;\t\t\t\t\t\t\t\t\\\n+})\n+\n+/*\n+ * Associate every caller with its own dept map.\n+ */\n+#define dma_fence_wait_any_timeout(fpp, count, intr, t, idx)\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tsigned long __ret;\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\tsdt_might_sleep_start_timeout(NULL, t);\t\t\t\t\\\n+\t__ret = __dma_fence_wait_any_timeout(fpp, count, intr, t, idx);\t\\\n+\tsdt_might_sleep_end();\t\t\t\t\t\t\\\n+\t__ret;\t\t\t\t\t\t\t\t\\\n+})\n \n /**\n  * dma_fence_wait - sleep until the fence gets signaled\n@@ -649,19 +688,24 @@ signed long dma_fence_wait_any_timeout(struct dma_fence **fences,\n  * fence might be freed before return, resulting in undefined behavior.\n  *\n  * See also dma_fence_wait_timeout() and dma_fence_wait_any_timeout().\n+ *\n+ * Associate every caller with its own dept map.\n  */\n-static inline signed long dma_fence_wait(struct dma_fence *fence, bool intr)\n-{\n-\tsigned long ret;\n-\n-\t/* Since dma_fence_wait_timeout cannot timeout with\n-\t * MAX_SCHEDULE_TIMEOUT, only valid return values are\n-\t * -ERESTARTSYS and MAX_SCHEDULE_TIMEOUT.\n-\t */\n-\tret = dma_fence_wait_timeout(fence, intr, MAX_SCHEDULE_TIMEOUT);\n-\n-\treturn ret \u003c 0 ? ret : 0;\n-}\n+#define dma_fence_wait(f, intr)\t\t\t\t\t\t\\\n+({\t\t\t\t\t\t\t\t\t\\\n+\tsigned long __ret;\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\tsdt_might_sleep_start_timeout(NULL, MAX_SCHEDULE_TIMEOUT);\t\\\n+\t__ret = __dma_fence_wait_timeout(f, intr, MAX_SCHEDULE_TIMEOUT);\\\n+\tsdt_might_sleep_end();\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\t/*\t\t\t\t\t\t\t\t\\\n+\t * Since dma_fence_wait_timeout cannot timeout with\t\t\\\n+\t * MAX_SCHEDULE_TIMEOUT, only valid return values are\t\t\\\n+\t * -ERESTARTSYS and MAX_SCHEDULE_TIMEOUT.\t\t\t\\\n+\t */\t\t\t\t\t\t\t\t\\\n+\t__ret \u003c 0 ? __ret : 0;\t\t\t\t\t\t\\\n+})\n \n void dma_fence_set_deadline(struct dma_fence *fence, ktime_t deadline);\n \ndiff --git a/include/linux/hardirq.h b/include/linux/hardirq.h\nindex d57cab4d4c06fd..bb279dbbe74806 100644\n--- a/include/linux/hardirq.h\n+++ b/include/linux/hardirq.h\n@@ -5,6 +5,7 @@\n #include \u003clinux/context_tracking_state.h\u003e\n #include \u003clinux/preempt.h\u003e\n #include \u003clinux/lockdep.h\u003e\n+#include \u003clinux/dept.h\u003e\n #include \u003clinux/ftrace_irq.h\u003e\n #include \u003clinux/sched.h\u003e\n #include \u003clinux/vtime.h\u003e\n@@ -106,6 +107,7 @@ void irq_exit_rcu(void);\n  */\n #define __nmi_enter()\t\t\t\t\t\t\\\n \tdo {\t\t\t\t\t\t\t\\\n+\t\tdept_off();\t\t\t\t\t\\\n \t\tlockdep_off();\t\t\t\t\t\\\n \t\tarch_nmi_enter();\t\t\t\t\\\n \t\tBUG_ON(in_nmi() == NMI_MASK);\t\t\t\\\n@@ -128,6 +130,7 @@ void irq_exit_rcu(void);\n \t\t__preempt_count_sub(NMI_OFFSET + HARDIRQ_OFFSET);\t\\\n \t\tarch_nmi_exit();\t\t\t\t\\\n \t\tlockdep_on();\t\t\t\t\t\\\n+\t\tdept_on();\t\t\t\t\t\\\n \t} while (0)\n \n #define nmi_exit()\t\t\t\t\t\t\\\ndiff --git a/include/linux/irq-entry-common.h b/include/linux/irq-entry-common.h\nindex d26d1b1bcbfb97..37ef4f20bdc4c4 100644\n--- a/include/linux/irq-entry-common.h\n+++ b/include/linux/irq-entry-common.h\n@@ -9,6 +9,7 @@\n #include \u003clinux/syscalls.h\u003e\n #include \u003clinux/tick.h\u003e\n #include \u003clinux/unwind_deferred.h\u003e\n+#include \u003clinux/dept.h\u003e\n \n #include \u003casm/entry-common.h\u003e\n \n@@ -88,6 +89,9 @@ static __always_inline bool arch_in_rcu_eqs(void) { return false; }\n  */\n static __always_inline void enter_from_user_mode(struct pt_regs *regs)\n {\n+\t/* Make dept work with a new context. */\n+\tdept_update_cxt();\n+\n \tarch_enter_from_user_mode(regs);\n \tlockdep_hardirqs_off(CALLER_ADDR0);\n \ndiff --git a/include/linux/irqflags.h b/include/linux/irqflags.h\nindex 57b074e0cfbbb3..586f5bad4da786 100644\n--- a/include/linux/irqflags.h\n+++ b/include/linux/irqflags.h\n@@ -15,6 +15,7 @@\n #include \u003clinux/irqflags_types.h\u003e\n #include \u003clinux/typecheck.h\u003e\n #include \u003clinux/cleanup.h\u003e\n+#include \u003clinux/dept.h\u003e\n #include \u003casm/irqflags.h\u003e\n #include \u003casm/percpu.h\u003e\n \n@@ -55,8 +56,10 @@ extern void trace_hardirqs_off(void);\n # define lockdep_softirqs_enabled(p)\t((p)-\u003esoftirqs_enabled)\n # define lockdep_hardirq_enter()\t\t\t\\\n do {\t\t\t\t\t\t\t\\\n-\tif (__this_cpu_inc_return(hardirq_context) == 1)\\\n+\tif (__this_cpu_inc_return(hardirq_context) == 1) { \\\n \t\tcurrent-\u003ehardirq_threaded = 0;\t\t\\\n+\t\tdept_hardirq_enter();\t\t\t\\\n+\t}\t\t\t\t\t\t\\\n } while (0)\n # define lockdep_hardirq_threaded()\t\t\\\n do {\t\t\t\t\t\t\\\n@@ -131,6 +134,8 @@ do {\t\t\t\t\t\t\\\n # define lockdep_softirq_enter()\t\t\\\n do {\t\t\t\t\t\t\\\n \tcurrent-\u003esoftirq_context++;\t\t\\\n+\tif (current-\u003esoftirq_context == 1)\t\\\n+\t\tdept_softirq_enter();\t\t\\\n } while (0)\n # define lockdep_softirq_exit()\t\t\t\\\n do {\t\t\t\t\t\t\\\n@@ -209,6 +214,13 @@ extern void warn_bogus_irq_restore(void);\n \t\traw_local_irq_disable();\t\t\\\n \t\tif (!was_disabled)\t\t\t\\\n \t\t\ttrace_hardirqs_off();\t\t\\\n+\t\t/*\t\t\t\t\t\\\n+\t\t * Just in case that C code has missed\t\\\n+\t\t * trace_hardirqs_off() at the first\t\\\n+\t\t * place e.g. disabling irq at asm code.\\\n+\t\t */\t\t\t\t\t\\\n+\t\telse\t\t\t\t\t\\\n+\t\t\tdept_hardirqs_off();\t\t\\\n \t} while (0)\n \n #define local_irq_save(flags)\t\t\t\t\\\n@@ -216,6 +228,13 @@ extern void warn_bogus_irq_restore(void);\n \t\traw_local_irq_save(flags);\t\t\\\n \t\tif (!raw_irqs_disabled_flags(flags))\t\\\n \t\t\ttrace_hardirqs_off();\t\t\\\n+\t\t/*\t\t\t\t\t\\\n+\t\t * Just in case that C code has missed\t\\\n+\t\t * trace_hardirqs_off() at the first\t\\\n+\t\t * place e.g. disabling irq at asm code.\\\n+\t\t */\t\t\t\t\t\\\n+\t\telse\t\t\t\t\t\\\n+\t\t\tdept_hardirqs_off();\t\t\\\n \t} while (0)\n \n #define local_irq_restore(flags)\t\t\t\\\ndiff --git a/include/linux/local_lock_internal.h b/include/linux/local_lock_internal.h\nindex 234be7f12c15e5..09255c5a665ffd 100644\n--- a/include/linux/local_lock_internal.h\n+++ b/include/linux/local_lock_internal.h\n@@ -35,6 +35,7 @@ typedef struct local_trylock local_trylock_t;\n \t\t.name = #lockname,\t\t\t\\\n \t\t.wait_type_inner = LD_WAIT_CONFIG,\t\\\n \t\t.lock_type = LD_LOCK_PERCPU,\t\t\\\n+\t\t.dmap = DEPT_MAP_INITIALIZER(lockname, NULL),\\\n \t},\t\t\t\t\t\t\\\n \t.owner = NULL,\n \ndiff --git a/include/linux/lockdep.h b/include/linux/lockdep.h\nindex 621566345406dd..5113b7053b621e 100644\n--- a/include/linux/lockdep.h\n+++ b/include/linux/lockdep.h\n@@ -12,6 +12,7 @@\n \n #include \u003clinux/lockdep_types.h\u003e\n #include \u003clinux/smp.h\u003e\n+#include \u003clinux/dept_ldt.h\u003e\n #include \u003casm/percpu.h\u003e\n \n struct task_struct;\n@@ -39,6 +40,8 @@ static inline void lockdep_copy_map(struct lockdep_map *to,\n \t */\n \tfor (i = 0; i \u003c NR_LOCKDEP_CACHING_CLASSES; i++)\n \t\tto-\u003eclass_cache[i] = NULL;\n+\n+\tdept_map_copy(\u0026to-\u003edmap, \u0026from-\u003edmap);\n }\n \n /*\n@@ -300,6 +303,7 @@ extern void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie);\n \tlockdep_assert_once(!current-\u003elockdep_depth)\n \n #define lockdep_recursing(tsk)\t((tsk)-\u003elockdep_recursion)\n+extern bool lockdep_recursing_current(void);\n \n #define lockdep_pin_lock(l)\tlock_pin_lock(\u0026(l)-\u003edep_map)\n #define lockdep_repin_lock(l,c)\tlock_repin_lock(\u0026(l)-\u003edep_map, (c))\n@@ -428,7 +432,8 @@ enum xhlock_context_t {\n  * Note that _name must not be NULL.\n  */\n #define STATIC_LOCKDEP_MAP_INIT(_name, _key) \\\n-\t{ .name = (_name), .key = (void *)(_key), }\n+\t{ .name = (_name), .key = (void *)(_key), \\\n+\t  .dmap = DEPT_MAP_INITIALIZER(_name, _key) }\n \n static inline void lockdep_invariant_state(bool force) {}\n static inline void lockdep_free_task(struct task_struct *task) {}\n@@ -510,33 +515,89 @@ extern bool read_lock_is_recursive(void);\n #define lock_acquire_shared(l, s, t, n, i)\t\tlock_acquire(l, s, t, 1, 1, n, i)\n #define lock_acquire_shared_recursive(l, s, t, n, i)\tlock_acquire(l, s, t, 2, 1, n, i)\n \n-#define spin_acquire(l, s, t, i)\t\tlock_acquire_exclusive(l, s, t, NULL, i)\n-#define spin_acquire_nest(l, s, t, n, i)\tlock_acquire_exclusive(l, s, t, n, i)\n-#define spin_release(l, i)\t\t\tlock_release(l, i)\n-\n-#define rwlock_acquire(l, s, t, i)\t\tlock_acquire_exclusive(l, s, t, NULL, i)\n+#define spin_acquire(l, s, t, i)\t\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\tldt_lock(\u0026(l)-\u003edmap, s, t, NULL, i);\t\t\t\t\\\n+\tlock_acquire_exclusive(l, s, t, NULL, i);\t\t\t\\\n+} while (0)\n+#define spin_acquire_nest(l, s, t, n, i)\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\tldt_lock(\u0026(l)-\u003edmap, s, t, n, i);\t\t\t\t\\\n+\tlock_acquire_exclusive(l, s, t, n, i);\t\t\t\t\\\n+} while (0)\n+#define spin_release(l, i)\t\t\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\tldt_unlock(\u0026(l)-\u003edmap, i);\t\t\t\t\t\\\n+\tlock_release(l, i);\t\t\t\t\t\t\\\n+} while (0)\n+#define rwlock_acquire(l, s, t, i)\t\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\tldt_wlock(\u0026(l)-\u003edmap, s, t, NULL, i);\t\t\t\t\\\n+\tlock_acquire_exclusive(l, s, t, NULL, i);\t\t\t\\\n+} while (0)\n #define rwlock_acquire_read(l, s, t, i)\t\t\t\t\t\\\n do {\t\t\t\t\t\t\t\t\t\\\n+\tldt_rlock(\u0026(l)-\u003edmap, s, t, NULL, i, !read_lock_is_recursive());\\\n \tif (read_lock_is_recursive())\t\t\t\t\t\\\n \t\tlock_acquire_shared_recursive(l, s, t, NULL, i);\t\\\n \telse\t\t\t\t\t\t\t\t\\\n \t\tlock_acquire_shared(l, s, t, NULL, i);\t\t\t\\\n } while (0)\n-\n-#define rwlock_release(l, i)\t\t\tlock_release(l, i)\n-\n-#define seqcount_acquire(l, s, t, i)\t\tlock_acquire_exclusive(l, s, t, NULL, i)\n-#define seqcount_acquire_read(l, s, t, i)\tlock_acquire_shared_recursive(l, s, t, NULL, i)\n-#define seqcount_release(l, i)\t\t\tlock_release(l, i)\n-\n-#define mutex_acquire(l, s, t, i)\t\tlock_acquire_exclusive(l, s, t, NULL, i)\n-#define mutex_acquire_nest(l, s, t, n, i)\tlock_acquire_exclusive(l, s, t, n, i)\n-#define mutex_release(l, i)\t\t\tlock_release(l, i)\n-\n-#define rwsem_acquire(l, s, t, i)\t\tlock_acquire_exclusive(l, s, t, NULL, i)\n-#define rwsem_acquire_nest(l, s, t, n, i)\tlock_acquire_exclusive(l, s, t, n, i)\n-#define rwsem_acquire_read(l, s, t, i)\t\tlock_acquire_shared(l, s, t, NULL, i)\n-#define rwsem_release(l, i)\t\t\tlock_release(l, i)\n+#define rwlock_release(l, i)\t\t\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\tldt_unlock(\u0026(l)-\u003edmap, i);\t\t\t\t\t\\\n+\tlock_release(l, i);\t\t\t\t\t\t\\\n+} while (0)\n+#define seqcount_acquire(l, s, t, i)\t\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\tldt_wlock(\u0026(l)-\u003edmap, s, t, NULL, i);\t\t\t\t\\\n+\tlock_acquire_exclusive(l, s, t, NULL, i);\t\t\t\\\n+} while (0)\n+#define seqcount_acquire_read(l, s, t, i)\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\tldt_rlock(\u0026(l)-\u003edmap, s, t, NULL, i, false);\t\t\t\\\n+\tlock_acquire_shared_recursive(l, s, t, NULL, i);\t\t\\\n+} while (0)\n+#define seqcount_release(l, i)\t\t\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\tldt_unlock(\u0026(l)-\u003edmap, i);\t\t\t\t\t\\\n+\tlock_release(l, i);\t\t\t\t\t\t\\\n+} while (0)\n+#define mutex_acquire(l, s, t, i)\t\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\tldt_lock(\u0026(l)-\u003edmap, s, t, NULL, i);\t\t\t\t\\\n+\tlock_acquire_exclusive(l, s, t, NULL, i);\t\t\t\\\n+} while (0)\n+#define mutex_acquire_nest(l, s, t, n, i)\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\tldt_lock(\u0026(l)-\u003edmap, s, t, n, i);\t\t\t\t\\\n+\tlock_acquire_exclusive(l, s, t, n, i);\t\t\t\t\\\n+} while (0)\n+#define mutex_release(l, i)\t\t\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\tldt_unlock(\u0026(l)-\u003edmap, i);\t\t\t\t\t\\\n+\tlock_release(l, i);\t\t\t\t\t\t\\\n+} while (0)\n+#define rwsem_acquire(l, s, t, i)\t\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\tldt_lock(\u0026(l)-\u003edmap, s, t, NULL, i);\t\t\t\t\\\n+\tlock_acquire_exclusive(l, s, t, NULL, i);\t\t\t\\\n+} while (0)\n+#define rwsem_acquire_nest(l, s, t, n, i)\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\tldt_lock(\u0026(l)-\u003edmap, s, t, n, i);\t\t\t\t\\\n+\tlock_acquire_exclusive(l, s, t, n, i);\t\t\t\t\\\n+} while (0)\n+#define rwsem_acquire_read(l, s, t, i)\t\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\tldt_lock(\u0026(l)-\u003edmap, s, t, NULL, i);\t\t\t\t\\\n+\tlock_acquire_shared(l, s, t, NULL, i);\t\t\t\t\\\n+} while (0)\n+#define rwsem_release(l, i)\t\t\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\tldt_unlock(\u0026(l)-\u003edmap, i);\t\t\t\t\t\\\n+\tlock_release(l, i);\t\t\t\t\t\t\\\n+} while (0)\n \n #define lock_map_acquire(l)\t\t\tlock_acquire_exclusive(l, 0, 0, NULL, _THIS_IP_)\n #define lock_map_acquire_try(l)\t\t\tlock_acquire_exclusive(l, 0, 1, NULL, _THIS_IP_)\n@@ -570,7 +631,7 @@ DECLARE_PER_CPU(int, hardirqs_enabled);\n DECLARE_PER_CPU(int, hardirq_context);\n DECLARE_PER_CPU(unsigned int, lockdep_recursion);\n \n-#define __lockdep_enabled\t(debug_locks \u0026\u0026 !this_cpu_read(lockdep_recursion))\n+#define __lockdep_enabled\t(debug_locks \u0026\u0026 !this_cpu_read(lockdep_recursion) \u0026\u0026 !lockdep_recursing_current())\n \n #define lockdep_assert_irqs_enabled()\t\t\t\t\t\\\n do {\t\t\t\t\t\t\t\t\t\\\ndiff --git a/include/linux/lockdep_types.h b/include/linux/lockdep_types.h\nindex eae115a2648856..0c3389ed26b6c1 100644\n--- a/include/linux/lockdep_types.h\n+++ b/include/linux/lockdep_types.h\n@@ -11,6 +11,7 @@\n #define __LINUX_LOCKDEP_TYPES_H\n \n #include \u003clinux/types.h\u003e\n+#include \u003clinux/dept.h\u003e\n \n #define MAX_LOCKDEP_SUBCLASSES\t\t8UL\n \n@@ -77,6 +78,7 @@ struct lock_class_key {\n \t\tstruct hlist_node\t\thash_entry;\n \t\tstruct lockdep_subclass_key\tsubkeys[MAX_LOCKDEP_SUBCLASSES];\n \t};\n+\tstruct dept_key\t\t\t\tdkey;\n };\n \n extern struct lock_class_key __lockdep_no_validate__;\n@@ -195,6 +197,7 @@ struct lockdep_map {\n \tint\t\t\t\tcpu;\n \tunsigned long\t\t\tip;\n #endif\n+\tstruct dept_map\t\t\tdmap;\n };\n \n struct pin_cookie { unsigned int val; };\ndiff --git a/include/linux/mm_types.h b/include/linux/mm_types.h\nindex 3cc8ae72288601..81dc9999090a8a 100644\n--- a/include/linux/mm_types.h\n+++ b/include/linux/mm_types.h\n@@ -22,6 +22,7 @@\n #include \u003clinux/types.h\u003e\n #include \u003clinux/rseq_types.h\u003e\n #include \u003clinux/bitmap.h\u003e\n+#include \u003clinux/dept.h\u003e\n \n #include \u003casm/mmu.h\u003e\n \n@@ -219,6 +220,9 @@ struct page {\n \tstruct page *kmsan_shadow;\n \tstruct page *kmsan_origin;\n #endif\n+\tstruct dept_page_usage usage;\n+\tstruct dept_ext_wgen pg_locked_wgen;\n+\tstruct dept_ext_wgen pg_writeback_wgen;\n } _struct_page_alignment;\n \n /*\ndiff --git a/include/linux/mmu_notifier.h b/include/linux/mmu_notifier.h\nindex 8450e18a87c26d..638b1b402d122e 100644\n--- a/include/linux/mmu_notifier.h\n+++ b/include/linux/mmu_notifier.h\n@@ -429,6 +429,14 @@ static inline int mmu_notifier_test_young(struct mm_struct *mm,\n \treturn 0;\n }\n \n+#ifdef CONFIG_DEPT\n+void mmu_notifier_invalidate_dept_ecxt_start(struct mmu_notifier_range *range);\n+void mmu_notifier_invalidate_dept_ecxt_end(struct mmu_notifier_range *range);\n+#else\n+static inline void mmu_notifier_invalidate_dept_ecxt_start(struct mmu_notifier_range *range) {}\n+static inline void mmu_notifier_invalidate_dept_ecxt_end(struct mmu_notifier_range *range) {}\n+#endif\n+\n static inline void\n mmu_notifier_invalidate_range_start(struct mmu_notifier_range *range)\n {\n@@ -440,6 +448,12 @@ mmu_notifier_invalidate_range_start(struct mmu_notifier_range *range)\n \t\t__mmu_notifier_invalidate_range_start(range);\n \t}\n \tlock_map_release(\u0026__mmu_notifier_invalidate_range_start_map);\n+\n+\t/*\n+\t * From now on, waiters could be there by this start until\n+\t * mmu_notifier_invalidate_range_end().\n+\t */\n+\tmmu_notifier_invalidate_dept_ecxt_start(range);\n }\n \n /*\n@@ -460,6 +474,12 @@ mmu_notifier_invalidate_range_start_nonblock(struct mmu_notifier_range *range)\n \t\tret = __mmu_notifier_invalidate_range_start(range);\n \t}\n \tlock_map_release(\u0026__mmu_notifier_invalidate_range_start_map);\n+\n+\t/*\n+\t * From now on, waiters could be there by this start until\n+\t * mmu_notifier_invalidate_range_end().\n+\t */\n+\tmmu_notifier_invalidate_dept_ecxt_start(range);\n \treturn ret;\n }\n \n@@ -471,6 +491,12 @@ mmu_notifier_invalidate_range_end(struct mmu_notifier_range *range)\n \n \tif (mm_has_notifiers(range-\u003emm))\n \t\t__mmu_notifier_invalidate_range_end(range);\n+\n+\t/*\n+\t * The event context that has been started by\n+\t * mmu_notifier_invalidate_range_start() ends.\n+\t */\n+\tmmu_notifier_invalidate_dept_ecxt_end(range);\n }\n \n static inline void mmu_notifier_arch_invalidate_secondary_tlbs(struct mm_struct *mm,\ndiff --git a/include/linux/mutex.h b/include/linux/mutex.h\nindex ecaa0440f6ec48..3d9bc1a28569af 100644\n--- a/include/linux/mutex.h\n+++ b/include/linux/mutex.h\n@@ -29,6 +29,7 @@ struct device;\n \t\t, .dep_map = {\t\t\t\t\t\\\n \t\t\t.name = #lockname,\t\t\t\\\n \t\t\t.wait_type_inner = LD_WAIT_SLEEP,\t\\\n+\t\t\t.dmap = DEPT_MAP_INITIALIZER(lockname, NULL),\\\n \t\t}\n #else\n # define __DEP_MAP_MUTEX_INITIALIZER(lockname)\ndiff --git a/include/linux/page-flags.h b/include/linux/page-flags.h\nindex f7a0e4af0c7344..ec736811a2c66d 100644\n--- a/include/linux/page-flags.h\n+++ b/include/linux/page-flags.h\n@@ -198,6 +198,153 @@ enum pageflags {\n \n #ifndef __GENERATING_BOUNDS_H\n \n+#ifdef CONFIG_DEPT\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/dept.h\u003e\n+\n+extern struct dept_map pg_locked_map;\n+extern struct dept_map pg_writeback_map;\n+\n+static inline void dept_set_page_usage(struct page *p,\n+\t\tunsigned int new_type)\n+{\n+\t/*\n+\t * Consider the page as DEPT_PAGE_DEFAULT until the next use of\n+\t * PG flags e.g. folio_lock().\n+\t */\n+\tunsigned int type = DEPT_PAGE_DEFAULT;\n+\n+\tif (WARN_ON_ONCE(new_type \u003e= DEPT_PAGE_USAGE_NR))\n+\t\treturn;\n+\n+\tnew_type \u003c\u003c= DEPT_PAGE_USAGE_SHIFT;\n+\tnew_type |= type \u0026 DEPT_PAGE_USAGE_MASK;\n+\tatomic_set(\u0026p-\u003eusage.type, new_type);\n+}\n+\n+static inline void dept_set_folio_usage(struct folio *f,\n+\t\tunsigned int new_type)\n+{\n+\tdept_set_page_usage(\u0026f-\u003epage, new_type);\n+}\n+\n+static inline void dept_reset_page_usage(struct page *p)\n+{\n+\tdept_set_page_usage(p, DEPT_PAGE_DEFAULT);\n+}\n+\n+static inline void dept_reset_folio_usage(struct folio *f)\n+{\n+\tdept_reset_page_usage(\u0026f-\u003epage);\n+}\n+\n+static inline void dept_update_page_usage(struct page *p)\n+{\n+\tunsigned int type = atomic_read(\u0026p-\u003eusage.type);\n+\tunsigned int new_type;\n+\n+retry:\n+\tnew_type = type \u0026 DEPT_PAGE_USAGE_PENDING_MASK;\n+\tnew_type \u003e\u003e= DEPT_PAGE_USAGE_SHIFT;\n+\tnew_type |= type \u0026 DEPT_PAGE_USAGE_PENDING_MASK;\n+\n+\t/*\n+\t * Already updated by others.\n+\t */\n+\tif (type == new_type)\n+\t\treturn;\n+\n+\tif (!atomic_try_cmpxchg(\u0026p-\u003eusage.type, \u0026type, new_type))\n+\t\tgoto retry;\n+}\n+\n+static inline unsigned long dept_event_flags(struct page *p, bool wait)\n+{\n+\tunsigned int type;\n+\n+\ttype = atomic_read(\u0026p-\u003eusage.type) \u0026 DEPT_PAGE_USAGE_MASK;\n+\n+\tif (WARN_ON_ONCE(type \u003e= DEPT_PAGE_USAGE_NR))\n+\t\treturn 0;\n+\n+\t/*\n+\t * wait\n+\t */\n+\tif (wait)\n+\t\treturn (1UL \u003c\u003c DEPT_PAGE_DEFAULT) | (1UL \u003c\u003c type);\n+\n+\t/*\n+\t * event\n+\t */\n+\treturn 1UL \u003c\u003c type;\n+}\n+\n+/*\n+ * Place the following annotations in its suitable point in code:\n+ *\n+ *\tAnnotate dept_page_set_bit() around firstly set_bit*()\n+ *\tAnnotate dept_page_clear_bit() around clear_bit*()\n+ *\tAnnotate dept_page_wait_on_bit() around wait_on_bit*()\n+ */\n+\n+static inline void dept_page_set_bit(struct page *p, int bit_nr)\n+{\n+\tdept_update_page_usage(p);\n+\n+\tif (bit_nr == PG_locked)\n+\t\tdept_request_event(\u0026pg_locked_map, \u0026p-\u003epg_locked_wgen);\n+\telse if (bit_nr == PG_writeback)\n+\t\tdept_request_event(\u0026pg_writeback_map, \u0026p-\u003epg_writeback_wgen);\n+}\n+\n+static inline void dept_page_clear_bit(struct page *p, int bit_nr)\n+{\n+\tunsigned long evt_f = dept_event_flags(p, false);\n+\n+\tif (bit_nr == PG_locked)\n+\t\tdept_event(\u0026pg_locked_map, evt_f, _RET_IP_, __func__, \u0026p-\u003epg_locked_wgen);\n+\telse if (bit_nr == PG_writeback)\n+\t\tdept_event(\u0026pg_writeback_map, evt_f, _RET_IP_, __func__, \u0026p-\u003epg_writeback_wgen);\n+}\n+\n+static inline void dept_page_wait_on_bit(struct page *p, int bit_nr)\n+{\n+\tunsigned long evt_f;\n+\n+\tdept_update_page_usage(p);\n+\tevt_f = dept_event_flags(p, true);\n+\n+\tif (bit_nr == PG_locked)\n+\t\tdept_wait(\u0026pg_locked_map, evt_f, _RET_IP_, __func__, 0, -1L);\n+\telse if (bit_nr == PG_writeback)\n+\t\tdept_wait(\u0026pg_writeback_map, evt_f, _RET_IP_, __func__, 0, -1L);\n+}\n+\n+static inline void dept_folio_set_bit(struct folio *f, int bit_nr)\n+{\n+\tdept_page_set_bit(\u0026f-\u003epage, bit_nr);\n+}\n+\n+static inline void dept_folio_clear_bit(struct folio *f, int bit_nr)\n+{\n+\tdept_page_clear_bit(\u0026f-\u003epage, bit_nr);\n+}\n+\n+static inline void dept_folio_wait_on_bit(struct folio *f, int bit_nr)\n+{\n+\tdept_page_wait_on_bit(\u0026f-\u003epage, bit_nr);\n+}\n+#else\n+#define dept_set_page_usage(p, t)\t\tdo { } while (0)\n+#define dept_reset_page_usage(p)\t\tdo { } while (0)\n+#define dept_page_set_bit(p, bit_nr)\t\tdo { } while (0)\n+#define dept_page_clear_bit(p, bit_nr)\t\tdo { } while (0)\n+#define dept_page_wait_on_bit(p, bit_nr)\tdo { } while (0)\n+#define dept_folio_set_bit(f, bit_nr)\t\tdo { } while (0)\n+#define dept_folio_clear_bit(f, bit_nr)\t\tdo { } while (0)\n+#define dept_folio_wait_on_bit(f, bit_nr)\tdo { } while (0)\n+#endif\n+\n #ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP\n DECLARE_STATIC_KEY_FALSE(hugetlb_optimize_vmemmap_key);\n \n@@ -419,27 +566,51 @@ static __always_inline bool folio_test_##name(const struct folio *folio) \\\n \n #define FOLIO_SET_FLAG(name, page)\t\t\t\t\t\\\n static __always_inline void folio_set_##name(struct folio *folio)\t\\\n-{ set_bit(PG_##name, folio_flags(folio, page)); }\n+{\t\t\t\t\t\t\t\t\t\\\n+\tset_bit(PG_##name, folio_flags(folio, page));\t\t\t\\\n+\tdept_folio_set_bit(folio, PG_##name);\t\t\t\t\\\n+}\n \n #define FOLIO_CLEAR_FLAG(name, page)\t\t\t\t\t\\\n static __always_inline void folio_clear_##name(struct folio *folio)\t\\\n-{ clear_bit(PG_##name, folio_flags(folio, page)); }\n+{\t\t\t\t\t\t\t\t\t\\\n+\tclear_bit(PG_##name, folio_flags(folio, page));\t\t\t\\\n+\tdept_folio_clear_bit(folio, PG_##name);\t\t\t\t\\\n+}\n \n #define __FOLIO_SET_FLAG(name, page)\t\t\t\t\t\\\n static __always_inline void __folio_set_##name(struct folio *folio)\t\\\n-{ __set_bit(PG_##name, folio_flags(folio, page)); }\n+{\t\t\t\t\t\t\t\t\t\\\n+\t__set_bit(PG_##name, folio_flags(folio, page));\t\t\t\\\n+\tdept_folio_set_bit(folio, PG_##name);\t\t\t\t\\\n+}\n \n #define __FOLIO_CLEAR_FLAG(name, page)\t\t\t\t\t\\\n static __always_inline void __folio_clear_##name(struct folio *folio)\t\\\n-{ __clear_bit(PG_##name, folio_flags(folio, page)); }\n+{\t\t\t\t\t\t\t\t\t\\\n+\t__clear_bit(PG_##name, folio_flags(folio, page));\t\t\\\n+\tdept_folio_clear_bit(folio, PG_##name);\t\t\t\t\\\n+}\n \n #define FOLIO_TEST_SET_FLAG(name, page)\t\t\t\t\t\\\n static __always_inline bool folio_test_set_##name(struct folio *folio)\t\\\n-{ return test_and_set_bit(PG_##name, folio_flags(folio, page)); }\n+{\t\t\t\t\t\t\t\t\t\\\n+\tbool __ret = test_and_set_bit(PG_##name, folio_flags(folio, page)); \\\n+\t\t\t\t\t\t\t\t\t\\\n+\tif (!__ret)\t\t\t\t\t\t\t\\\n+\t\tdept_folio_set_bit(folio, PG_##name);\t\t\t\\\n+\treturn __ret;\t\t\t\t\t\t\t\\\n+}\n \n #define FOLIO_TEST_CLEAR_FLAG(name, page)\t\t\t\t\\\n static __always_inline bool folio_test_clear_##name(struct folio *folio) \\\n-{ return test_and_clear_bit(PG_##name, folio_flags(folio, page)); }\n+{\t\t\t\t\t\t\t\t\t\\\n+\tbool __ret = test_and_clear_bit(PG_##name, folio_flags(folio, page)); \\\n+\t\t\t\t\t\t\t\t\t\\\n+\tif (__ret)\t\t\t\t\t\t\t\\\n+\t\tdept_folio_clear_bit(folio, PG_##name);\t\t\t\\\n+\treturn __ret;\t\t\t\t\t\t\t\\\n+}\n \n #define FOLIO_FLAG(name, page)\t\t\t\t\t\t\\\n FOLIO_TEST_FLAG(name, page)\t\t\t\t\t\t\\\n@@ -454,32 +625,54 @@ static __always_inline int Page##uname(const struct page *page)\t\t\\\n #define SETPAGEFLAG(uname, lname, policy)\t\t\t\t\\\n FOLIO_SET_FLAG(lname, FOLIO_##policy)\t\t\t\t\t\\\n static __always_inline void SetPage##uname(struct page *page)\t\t\\\n-{ set_bit(PG_##lname, \u0026policy(page, 1)-\u003eflags.f); }\n+{\t\t\t\t\t\t\t\t\t\\\n+\tset_bit(PG_##lname, \u0026policy(page, 1)-\u003eflags.f);\t\t\t\\\n+\tdept_page_set_bit(page, PG_##lname);\t\t\t\t\\\n+}\n \n #define CLEARPAGEFLAG(uname, lname, policy)\t\t\t\t\\\n FOLIO_CLEAR_FLAG(lname, FOLIO_##policy)\t\t\t\t\t\\\n static __always_inline void ClearPage##uname(struct page *page)\t\t\\\n-{ clear_bit(PG_##lname, \u0026policy(page, 1)-\u003eflags.f); }\n+{\t\t\t\t\t\t\t\t\t\\\n+\tclear_bit(PG_##lname, \u0026policy(page, 1)-\u003eflags.f);\t\t\t\\\n+\tdept_page_clear_bit(page, PG_##lname);\t\t\t\t\\\n+}\n \n #define __SETPAGEFLAG(uname, lname, policy)\t\t\t\t\\\n __FOLIO_SET_FLAG(lname, FOLIO_##policy)\t\t\t\t\t\\\n static __always_inline void __SetPage##uname(struct page *page)\t\t\\\n-{ __set_bit(PG_##lname, \u0026policy(page, 1)-\u003eflags.f); }\n+{\t\t\t\t\t\t\t\t\t\\\n+\t__set_bit(PG_##lname, \u0026policy(page, 1)-\u003eflags.f);\t\t\t\\\n+\tdept_page_set_bit(page, PG_##lname);\t\t\t\t\\\n+}\n \n #define __CLEARPAGEFLAG(uname, lname, policy)\t\t\t\t\\\n __FOLIO_CLEAR_FLAG(lname, FOLIO_##policy)\t\t\t\t\\\n static __always_inline void __ClearPage##uname(struct page *page)\t\\\n-{ __clear_bit(PG_##lname, \u0026policy(page, 1)-\u003eflags.f); }\n+{\t\t\t\t\t\t\t\t\t\\\n+\t__clear_bit(PG_##lname, \u0026policy(page, 1)-\u003eflags.f);\t\t\\\n+\tdept_page_clear_bit(page, PG_##lname);\t\t\t\t\\\n+}\n \n #define TESTSETFLAG(uname, lname, policy)\t\t\t\t\\\n FOLIO_TEST_SET_FLAG(lname, FOLIO_##policy)\t\t\t\t\\\n static __always_inline int TestSetPage##uname(struct page *page)\t\\\n-{ return test_and_set_bit(PG_##lname, \u0026policy(page, 1)-\u003eflags.f); }\n+{\t\t\t\t\t\t\t\t\t\\\n+\tbool ret = test_and_set_bit(PG_##lname, \u0026policy(page, 1)-\u003eflags.f);\\\n+\tif (!ret)\t\t\t\t\t\t\t\\\n+\t\tdept_page_set_bit(page, PG_##lname);\t\t\t\\\n+\treturn ret;\t\t\t\t\t\t\t\\\n+}\n \n #define TESTCLEARFLAG(uname, lname, policy)\t\t\t\t\\\n FOLIO_TEST_CLEAR_FLAG(lname, FOLIO_##policy)\t\t\t\t\\\n static __always_inline int TestClearPage##uname(struct page *page)\t\\\n-{ return test_and_clear_bit(PG_##lname, \u0026policy(page, 1)-\u003eflags.f); }\n+{\t\t\t\t\t\t\t\t\t\\\n+\tbool ret = test_and_clear_bit(PG_##lname, \u0026policy(page, 1)-\u003eflags.f);\\\n+\tif (ret)\t\t\t\t\t\t\t\\\n+\t\tdept_page_clear_bit(page, PG_##lname);\t\t\t\\\n+\treturn ret;\t\t\t\t\t\t\t\\\n+}\n \n #define PAGEFLAG(uname, lname, policy)\t\t\t\t\t\\\n \tTESTPAGEFLAG(uname, lname, policy)\t\t\t\t\\\ndiff --git a/include/linux/pagemap.h b/include/linux/pagemap.h\nindex 31a848485ad9d9..6605800ba3ad8b 100644\n--- a/include/linux/pagemap.h\n+++ b/include/linux/pagemap.h\n@@ -1119,7 +1119,12 @@ void folio_unlock(struct folio *folio);\n  */\n static inline bool folio_trylock(struct folio *folio)\n {\n-\treturn likely(!test_and_set_bit_lock(PG_locked, folio_flags(folio, 0)));\n+\tbool ret = !test_and_set_bit_lock(PG_locked, folio_flags(folio, 0));\n+\n+\tif (ret)\n+\t\tdept_page_set_bit(\u0026folio-\u003epage, PG_locked);\n+\n+\treturn likely(ret);\n }\n \n /*\n@@ -1155,6 +1160,16 @@ static inline bool trylock_page(struct page *page)\n static inline void folio_lock(struct folio *folio)\n {\n \tmight_sleep();\n+\n+\t/*\n+\t * dept_page_wait_on_bit() will be called if __folio_lock() goes\n+\t * through a real wait path.  However, for better job to detect\n+\t * *potential* deadlocks, let's assume that folio_lock() always\n+\t * goes through wait so that dept can take into account all the\n+\t * potential cases.\n+\t */\n+\tdept_page_wait_on_bit(\u0026folio-\u003epage, PG_locked);\n+\n \tif (!folio_trylock(folio))\n \t\t__folio_lock(folio);\n }\n@@ -1175,6 +1190,15 @@ static inline void lock_page(struct page *page)\n \tstruct folio *folio;\n \tmight_sleep();\n \n+\t/*\n+\t * dept_page_wait_on_bit() will be called if __folio_lock() goes\n+\t * through a real wait path.  However, for better job to detect\n+\t * *potential* deadlocks, let's assume that lock_page() always\n+\t * goes through wait so that dept can take into account all the\n+\t * potential cases.\n+\t */\n+\tdept_page_wait_on_bit(page, PG_locked);\n+\n \tfolio = page_folio(page);\n \tif (!folio_trylock(folio))\n \t\t__folio_lock(folio);\n@@ -1193,6 +1217,17 @@ static inline void lock_page(struct page *page)\n static inline int folio_lock_killable(struct folio *folio)\n {\n \tmight_sleep();\n+\n+\t/*\n+\t * dept_page_wait_on_bit() will be called if\n+\t * __folio_lock_killable() goes through a real wait path.\n+\t * However, for better job to detect *potential* deadlocks,\n+\t * let's assume that folio_lock_killable() always goes through\n+\t * wait so that dept can take into account all the potential\n+\t * cases.\n+\t */\n+\tdept_page_wait_on_bit(\u0026folio-\u003epage, PG_locked);\n+\n \tif (!folio_trylock(folio))\n \t\treturn __folio_lock_killable(folio);\n \treturn 0;\ndiff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h\nindex c8cb010d655ebe..ca9522f0882bcc 100644\n--- a/include/linux/percpu-rwsem.h\n+++ b/include/linux/percpu-rwsem.h\n@@ -22,7 +22,7 @@ struct percpu_rw_semaphore {\n };\n \n #ifdef CONFIG_DEBUG_LOCK_ALLOC\n-#define __PERCPU_RWSEM_DEP_MAP_INIT(lockname)\t.dep_map = { .name = #lockname },\n+#define __PERCPU_RWSEM_DEP_MAP_INIT(lockname)\t.dep_map = { .name = #lockname, .dmap = DEPT_MAP_INITIALIZER(lockname, NULL) },\n #else\n #define __PERCPU_RWSEM_DEP_MAP_INIT(lockname)\n #endif\ndiff --git a/include/linux/percpu.h b/include/linux/percpu.h\nindex 85bf8dd9f08740..dd74321d4bbd03 100644\n--- a/include/linux/percpu.h\n+++ b/include/linux/percpu.h\n@@ -43,7 +43,11 @@\n # define PERCPU_DYNAMIC_SIZE_SHIFT      12\n #endif /* LOCKDEP and PAGE_SIZE \u003e 4KiB */\n #else\n+#if defined(CONFIG_DEPT) \u0026\u0026 !defined(CONFIG_PAGE_SIZE_4KB)\n+#define PERCPU_DYNAMIC_SIZE_SHIFT      11\n+#else\n #define PERCPU_DYNAMIC_SIZE_SHIFT      10\n+#endif /* DEPT and PAGE_SIZE \u003e 4KiB */\n #endif\n \n /*\ndiff --git a/include/linux/rcupdate_wait.h b/include/linux/rcupdate_wait.h\nindex 4c92d4291cce7a..ee598e70b4bc7c 100644\n--- a/include/linux/rcupdate_wait.h\n+++ b/include/linux/rcupdate_wait.h\n@@ -19,17 +19,20 @@ struct rcu_synchronize {\n \n \t/* This is for debugging. */\n \tstruct rcu_gp_oldstate oldstate;\n+\tstruct dept_map dmap;\n+\tstruct dept_key dkey;\n };\n void wakeme_after_rcu(struct rcu_head *head);\n \n void __wait_rcu_gp(bool checktiny, unsigned int state, int n, call_rcu_func_t *crcu_array,\n-\t\t   struct rcu_synchronize *rs_array);\n+\t\t   struct rcu_synchronize *rs_array, struct dept_key *dkey);\n \n #define _wait_rcu_gp(checktiny, state, ...) \\\n-do {\t\t\t\t\t\t\t\t\t\t\t\t\\\n-\tcall_rcu_func_t __crcu_array[] = { __VA_ARGS__ };\t\t\t\t\t\\\n-\tstruct rcu_synchronize __rs_array[ARRAY_SIZE(__crcu_array)];\t\t\t\t\\\n-\t__wait_rcu_gp(checktiny, state, ARRAY_SIZE(__crcu_array), __crcu_array, __rs_array);\t\\\n+do {\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\tcall_rcu_func_t __crcu_array[] = { __VA_ARGS__ };\t\t\t\t\t\t\\\n+\tstatic struct dept_key __key;\t\t\t\t\t\t\t\t\t\\\n+\tstruct rcu_synchronize __rs_array[ARRAY_SIZE(__crcu_array)];\t\t\t\t\t\\\n+\t__wait_rcu_gp(checktiny, state, ARRAY_SIZE(__crcu_array), __crcu_array, __rs_array, \u0026__key);\t\\\n } while (0)\n \n #define wait_rcu_gp(...) _wait_rcu_gp(false, TASK_UNINTERRUPTIBLE, __VA_ARGS__)\ndiff --git a/include/linux/rtmutex.h b/include/linux/rtmutex.h\nindex ede4c6bf6f2266..ac68c3e5e2ecce 100644\n--- a/include/linux/rtmutex.h\n+++ b/include/linux/rtmutex.h\n@@ -91,6 +91,7 @@ do { \\\n \t.dep_map = {\t\t\t\t\t\\\n \t\t.name = #mutexname,\t\t\t\\\n \t\t.wait_type_inner = LD_WAIT_SLEEP,\t\\\n+\t\t.dmap = DEPT_MAP_INITIALIZER(mutexname, NULL),\\\n \t}\n #else\n #define __DEP_MAP_RT_MUTEX_INITIALIZER(mutexname)\ndiff --git a/include/linux/rwlock_types.h b/include/linux/rwlock_types.h\nindex d5e7316401e75a..f2ff62ef4c3661 100644\n--- a/include/linux/rwlock_types.h\n+++ b/include/linux/rwlock_types.h\n@@ -10,6 +10,7 @@\n \t.dep_map = {\t\t\t\t\t\t\t\\\n \t\t.name = #lockname,\t\t\t\t\t\\\n \t\t.wait_type_inner = LD_WAIT_CONFIG,\t\t\t\\\n+\t\t.dmap = DEPT_MAP_INITIALIZER(lockname, NULL),\t\t\\\n \t}\n #else\n # define RW_DEP_MAP_INIT(lockname)\ndiff --git a/include/linux/rwsem.h b/include/linux/rwsem.h\nindex 9bf1d93d3d7ba3..47ab3fcee48b69 100644\n--- a/include/linux/rwsem.h\n+++ b/include/linux/rwsem.h\n@@ -22,6 +22,7 @@\n \t.dep_map = {\t\t\t\t\t\\\n \t\t.name = #lockname,\t\t\t\\\n \t\t.wait_type_inner = LD_WAIT_SLEEP,\t\\\n+\t\t.dmap = DEPT_MAP_INITIALIZER(lockname, NULL),\\\n \t},\n #else\n # define __RWSEM_DEP_MAP_INIT(lockname)\ndiff --git a/include/linux/sched.h b/include/linux/sched.h\nindex 5a5d3dbc9cdf33..b2fbcf0f00f483 100644\n--- a/include/linux/sched.h\n+++ b/include/linux/sched.h\n@@ -50,6 +50,7 @@\n #include \u003clinux/unwind_deferred_types.h\u003e\n #include \u003casm/kmap_size.h\u003e\n #include \u003clinux/time64.h\u003e\n+#include \u003clinux/dept.h\u003e\n #ifndef COMPILE_OFFSETS\n #include \u003cgenerated/rq-offsets.h\u003e\n #endif\n@@ -817,6 +818,114 @@ struct kmap_ctrl {\n #endif\n };\n \n+#ifdef CONFIG_DEPT\n+struct dept_task {\n+\t/*\n+\t * all event contexts that have entered and before exiting\n+\t */\n+\tstruct dept_ecxt_held\t\tecxt_held[DEPT_MAX_ECXT_HELD];\n+\tint\t\t\t\tecxt_held_pos;\n+\n+\t/*\n+\t * ring buffer holding all waits that have happened\n+\t */\n+\tstruct dept_wait_hist\t\twait_hist[DEPT_MAX_WAIT_HIST];\n+\tint\t\t\t\twait_hist_pos;\n+\n+\t/*\n+\t * sequential id to identify each context\n+\t */\n+\tunsigned int\t\t\tcxt_id[DEPT_CXTS_NR];\n+\n+\t/*\n+\t * for tracking IRQ-enabled points with cross-event\n+\t */\n+\tunsigned int\t\t\twgen_enirq[DEPT_CXT_IRQS_NR];\n+\n+\t/*\n+\t * for keeping up-to-date IRQ-enabled points\n+\t */\n+\tunsigned long\t\t\tenirq_ip[DEPT_CXT_IRQS_NR];\n+\n+\t/*\n+\t * for reserving a current stack instance at each operation\n+\t */\n+\tstruct dept_stack\t\t*stack;\n+\n+\t/*\n+\t * for preventing recursive call into DEPT engine\n+\t */\n+\tint\t\t\t\trecursive;\n+\n+\t/*\n+\t * for preventing reentrance to WARN*() while warning\n+\t */\n+\tint\t\t\t\tin_warning;\n+\n+\t/*\n+\t * for staging data to commit a wait\n+\t */\n+\tstruct dept_map\t\t\tstage_m;\n+\tstruct dept_map\t\t\t*stage_real_m;\n+\tbool\t\t\t\tstage_sched_map;\n+\tconst char\t\t\t*stage_w_fn;\n+\tunsigned long\t\t\tstage_ip;\n+\tbool\t\t\t\tstage_timeout;\n+\tstruct dept_stack\t\t*stage_wait_stack;\n+\tarch_spinlock_t\t\t\tstage_lock;\n+\n+\t/*\n+\t * the number of missing ecxts\n+\t */\n+\tint\t\t\t\tmissing_ecxt;\n+\n+\t/*\n+\t * for tracking IRQ-enable state\n+\t */\n+\tbool\t\t\t\thardirqs_enabled;\n+\tbool\t\t\t\tsoftirqs_enabled;\n+\n+\t/*\n+\t * whether the current is on do_exit()\n+\t */\n+\tbool\t\t\t\ttask_exit;\n+\n+\t/*\n+\t * whether the current is running __schedule()\n+\t */\n+\tbool\t\t\t\tin_sched;\n+};\n+\n+#define DEPT_TASK_INITIALIZER(t)\t\t\t\t\\\n+{\t\t\t\t\t\t\t\t\\\n+\t.wait_hist = { { .wait = NULL, } },\t\t\t\\\n+\t.ecxt_held_pos = 0,\t\t\t\t\t\\\n+\t.wait_hist_pos = 0,\t\t\t\t\t\\\n+\t.cxt_id = { 0U },\t\t\t\t\t\\\n+\t.wgen_enirq = { 0U },\t\t\t\t\t\\\n+\t.enirq_ip = { 0UL },\t\t\t\t\t\\\n+\t.stack = NULL,\t\t\t\t\t\t\\\n+\t.recursive = 0,\t\t\t\t\t\t\\\n+\t.in_warning = 0,\t\t\t\t\t\\\n+\t.stage_m = DEPT_MAP_INITIALIZER((t)-\u003estage_m, NULL),\t\\\n+\t.stage_real_m = NULL,\t\t\t\t\t\\\n+\t.stage_sched_map = false,\t\t\t\t\\\n+\t.stage_w_fn = NULL,\t\t\t\t\t\\\n+\t.stage_ip = 0UL,\t\t\t\t\t\\\n+\t.stage_timeout = false,\t\t\t\t\t\\\n+\t.stage_wait_stack = NULL,\t\t\t\t\\\n+\t.stage_lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED,\\\n+\t.missing_ecxt = 0,\t\t\t\t\t\\\n+\t.hardirqs_enabled = false,\t\t\t\t\\\n+\t.softirqs_enabled = false,\t\t\t\t\\\n+\t.task_exit = false,\t\t\t\t\t\\\n+\t.in_sched = false,\t\t\t\t\t\\\n+}\n+#else\n+struct dept_task { };\n+#define DEPT_TASK_INITIALIZER(t) { }\n+#endif\n+\n struct task_struct {\n #ifdef CONFIG_THREAD_INFO_IN_TASK\n \t/*\n@@ -1271,6 +1380,8 @@ struct task_struct {\n \tstruct held_lock\t\theld_locks[MAX_LOCK_DEPTH];\n #endif\n \n+\tstruct dept_task\t\tdept_task;\n+\n #if defined(CONFIG_UBSAN) \u0026\u0026 !defined(CONFIG_UBSAN_TRAP)\n \tunsigned int\t\t\tin_ubsan;\n #endif\ndiff --git a/include/linux/seqlock.h b/include/linux/seqlock.h\nindex 5a40252b833486..efc93acf161213 100644\n--- a/include/linux/seqlock.h\n+++ b/include/linux/seqlock.h\n@@ -52,7 +52,7 @@ static inline void __seqcount_init(seqcount_t *s, const char *name,\n #ifdef CONFIG_DEBUG_LOCK_ALLOC\n \n # define SEQCOUNT_DEP_MAP_INIT(lockname)\t\t\t\t\\\n-\t\t.dep_map = { .name = #lockname }\n+\t\t.dep_map = { .name = #lockname, .dmap = DEPT_MAP_INITIALIZER(lockname, NULL) }\n \n /**\n  * seqcount_init() - runtime initializer for seqcount_t\ndiff --git a/include/linux/spinlock_types_raw.h b/include/linux/spinlock_types_raw.h\nindex e5644ab2161f8c..5f245afdd77caa 100644\n--- a/include/linux/spinlock_types_raw.h\n+++ b/include/linux/spinlock_types_raw.h\n@@ -32,11 +32,13 @@ typedef struct raw_spinlock raw_spinlock_t;\n \t.dep_map = {\t\t\t\t\t\\\n \t\t.name = #lockname,\t\t\t\\\n \t\t.wait_type_inner = LD_WAIT_SPIN,\t\\\n+\t\t.dmap = DEPT_MAP_INITIALIZER(lockname, NULL),\\\n \t}\n # define SPIN_DEP_MAP_INIT(lockname)\t\t\t\\\n \t.dep_map = {\t\t\t\t\t\\\n \t\t.name = #lockname,\t\t\t\\\n \t\t.wait_type_inner = LD_WAIT_CONFIG,\t\\\n+\t\t.dmap = DEPT_MAP_INITIALIZER(lockname, NULL),\\\n \t}\n \n # define LOCAL_SPIN_DEP_MAP_INIT(lockname)\t\t\\\n@@ -44,6 +46,7 @@ typedef struct raw_spinlock raw_spinlock_t;\n \t\t.name = #lockname,\t\t\t\\\n \t\t.wait_type_inner = LD_WAIT_CONFIG,\t\\\n \t\t.lock_type = LD_LOCK_PERCPU,\t\t\\\n+\t\t.dmap = DEPT_MAP_INITIALIZER(lockname, NULL),\\\n \t}\n #else\n # define RAW_SPIN_DEP_MAP_INIT(lockname)\ndiff --git a/include/linux/srcu.h b/include/linux/srcu.h\nindex bb44a0bd769683..50c78f71ad4328 100644\n--- a/include/linux/srcu.h\n+++ b/include/linux/srcu.h\n@@ -53,7 +53,7 @@ int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,\n \t__init_srcu_struct_fast_updown((ssp), #ssp, \u0026__srcu_key); \\\n })\n \n-#define __SRCU_DEP_MAP_INIT(srcu_name)\t.dep_map = { .name = #srcu_name },\n+#define __SRCU_DEP_MAP_INIT(srcu_name)\t.dep_map = { .name = #srcu_name, .dmap = DEPT_MAP_INITIALIZER(srcu_name, NULL) },\n #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */\n \n int init_srcu_struct(struct srcu_struct *ssp);\ndiff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h\nindex f46d1fb8f71ae2..666e42a17a317c 100644\n--- a/include/linux/sunrpc/xprt.h\n+++ b/include/linux/sunrpc/xprt.h\n@@ -211,6 +211,14 @@ enum xprt_transports {\n \n struct rpc_sysfs_xprt;\n struct rpc_xprt {\n+\t/*\n+\t * Place struct rcu_head within the first 4096 bytes of struct\n+\t * rpc_xprt if sizeof(struct rpc_xprt) \u003e 4096, so that\n+\t * kfree_rcu() can simply work assuming that.  See the comment\n+\t * in kfree_rcu().\n+\t */\n+\tstruct rcu_head\t\trcu;\n+\n \tstruct kref\t\tkref;\t\t/* Reference count */\n \tconst struct rpc_xprt_ops *ops;\t\t/* transport methods */\n \tunsigned int\t\tid;\t\t/* transport id */\n@@ -317,7 +325,6 @@ struct rpc_xprt {\n #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)\n \tstruct dentry\t\t*debugfs;\t\t/* debugfs directory */\n #endif\n-\tstruct rcu_head\t\trcu;\n \tconst struct xprt_class\t*xprt_class;\n \tstruct rpc_sysfs_xprt\t*xprt_sysfs;\n \tbool\t\t\tmain; /*mark if this is the 1st transport */\ndiff --git a/include/linux/swait.h b/include/linux/swait.h\nindex d324419482a0f5..233acdf55e9bcc 100644\n--- a/include/linux/swait.h\n+++ b/include/linux/swait.h\n@@ -6,6 +6,7 @@\n #include \u003clinux/stddef.h\u003e\n #include \u003clinux/spinlock.h\u003e\n #include \u003clinux/wait.h\u003e\n+#include \u003clinux/dept_sdt.h\u003e\n #include \u003casm/current.h\u003e\n \n /*\n@@ -161,6 +162,7 @@ extern void finish_swait(struct swait_queue_head *q, struct swait_queue *wait);\n \tstruct swait_queue __wait;\t\t\t\t\t\\\n \tlong __ret = ret;\t\t\t\t\t\t\\\n \t\t\t\t\t\t\t\t\t\\\n+\tsdt_might_sleep_start_timeout(NULL, __ret);\t\t\t\\\n \tINIT_LIST_HEAD(\u0026__wait.task_list);\t\t\t\t\\\n \tfor (;;) {\t\t\t\t\t\t\t\\\n \t\tlong __int = prepare_to_swait_event(\u0026wq, \u0026__wait, state);\\\n@@ -176,6 +178,7 @@ extern void finish_swait(struct swait_queue_head *q, struct swait_queue *wait);\n \t\tcmd;\t\t\t\t\t\t\t\\\n \t}\t\t\t\t\t\t\t\t\\\n \tfinish_swait(\u0026wq, \u0026__wait);\t\t\t\t\t\\\n+\tsdt_might_sleep_end();\t\t\t\t\t\t\\\n __out:\t__ret;\t\t\t\t\t\t\t\t\\\n })\n \ndiff --git a/include/linux/wait.h b/include/linux/wait.h\nindex dce055e6add390..a9524bc8630b77 100644\n--- a/include/linux/wait.h\n+++ b/include/linux/wait.h\n@@ -7,6 +7,7 @@\n #include \u003clinux/list.h\u003e\n #include \u003clinux/stddef.h\u003e\n #include \u003clinux/spinlock.h\u003e\n+#include \u003clinux/dept_sdt.h\u003e\n \n #include \u003casm/current.h\u003e\n \n@@ -305,6 +306,7 @@ extern void init_wait_entry(struct wait_queue_entry *wq_entry, int flags);\n \tstruct wait_queue_entry __wq_entry;\t\t\t\t\t\\\n \tlong __ret = ret;\t/* explicit shadow */\t\t\t\t\\\n \t\t\t\t\t\t\t\t\t\t\\\n+\tsdt_might_sleep_start_timeout(NULL, __ret);\t\t\t\t\\\n \tinit_wait_entry(\u0026__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0);\t\\\n \tfor (;;) {\t\t\t\t\t\t\t\t\\\n \t\tlong __int = prepare_to_wait_event(\u0026wq_head, \u0026__wq_entry, state);\\\n@@ -323,6 +325,7 @@ extern void init_wait_entry(struct wait_queue_entry *wq_entry, int flags);\n \t\t\tbreak;\t\t\t\t\t\t\t\\\n \t}\t\t\t\t\t\t\t\t\t\\\n \tfinish_wait(\u0026wq_head, \u0026__wq_entry);\t\t\t\t\t\\\n+\tsdt_might_sleep_end();\t\t\t\t\t\t\t\\\n __out:\t__ret;\t\t\t\t\t\t\t\t\t\\\n })\n \ndiff --git a/include/linux/wait_bit.h b/include/linux/wait_bit.h\nindex 9e29d79fc790af..9885ac4e1ded55 100644\n--- a/include/linux/wait_bit.h\n+++ b/include/linux/wait_bit.h\n@@ -6,6 +6,7 @@\n  * Linux wait-bit related types and methods:\n  */\n #include \u003clinux/wait.h\u003e\n+#include \u003clinux/dept_sdt.h\u003e\n \n struct wait_bit_key {\n \tunsigned long\t\t*flags;\n@@ -257,6 +258,7 @@ extern wait_queue_head_t *__var_waitqueue(void *p);\n \tstruct wait_bit_queue_entry __wbq_entry;\t\t\t\\\n \tlong __ret = ret; /* explicit shadow */\t\t\t\t\\\n \t\t\t\t\t\t\t\t\t\\\n+\tsdt_might_sleep_start_timeout(NULL, __ret);\t\t\t\\\n \tinit_wait_var_entry(\u0026__wbq_entry, var,\t\t\t\t\\\n \t\t\t    exclusive ? WQ_FLAG_EXCLUSIVE : 0);\t\t\\\n \tfor (;;) {\t\t\t\t\t\t\t\\\n@@ -274,6 +276,7 @@ extern wait_queue_head_t *__var_waitqueue(void *p);\n \t\tcmd;\t\t\t\t\t\t\t\\\n \t}\t\t\t\t\t\t\t\t\\\n \tfinish_wait(__wq_head, \u0026__wbq_entry.wq_entry);\t\t\t\\\n+\tsdt_might_sleep_end();\t\t\t\t\t\t\\\n __out:\t__ret;\t\t\t\t\t\t\t\t\\\n })\n \ndiff --git a/init/init_task.c b/init/init_task.c\nindex 5c838757fc10eb..79aae8437b10bf 100644\n--- a/init/init_task.c\n+++ b/init/init_task.c\n@@ -14,6 +14,7 @@\n #include \u003clinux/numa.h\u003e\n #include \u003clinux/scs.h\u003e\n #include \u003clinux/plist.h\u003e\n+#include \u003clinux/dept.h\u003e\n \n #include \u003clinux/uaccess.h\u003e\n \n@@ -230,6 +231,7 @@ struct task_struct init_task __aligned(L1_CACHE_BYTES) = {\n \t.curr_chain_key = INITIAL_CHAIN_KEY,\n \t.lockdep_recursion = 0,\n #endif\n+\t.dept_task = DEPT_TASK_INITIALIZER(init_task),\n #ifdef CONFIG_FUNCTION_GRAPH_TRACER\n \t.ret_stack\t\t= NULL,\n \t.tracing_graph_pause\t= ATOMIC_INIT(0),\ndiff --git a/init/main.c b/init/main.c\nindex 1cb395dd94e43f..9c0603cca965a4 100644\n--- a/init/main.c\n+++ b/init/main.c\n@@ -66,6 +66,7 @@\n #include \u003clinux/debug_locks.h\u003e\n #include \u003clinux/debugobjects.h\u003e\n #include \u003clinux/lockdep.h\u003e\n+#include \u003clinux/dept.h\u003e\n #include \u003clinux/kmemleak.h\u003e\n #include \u003clinux/padata.h\u003e\n #include \u003clinux/pid_namespace.h\u003e\n@@ -1150,6 +1151,7 @@ void start_kernel(void)\n \t\t      panic_param);\n \n \tlockdep_init();\n+\tdept_init();\n \n \t/*\n \t * Need to run this when irqs are enabled, because it wants\ndiff --git a/kernel/Makefile b/kernel/Makefile\nindex 6785982013dced..a1856fb9887c29 100644\n--- a/kernel/Makefile\n+++ b/kernel/Makefile\n@@ -59,6 +59,7 @@ obj-y += dma/\n obj-y += entry/\n obj-y += unwind/\n obj-$(CONFIG_MODULES) += module/\n+obj-y += dependency/\n \n obj-$(CONFIG_KCMP) += kcmp.o\n obj-$(CONFIG_FREEZER) += freezer.o\ndiff --git a/kernel/cpu.c b/kernel/cpu.c\nindex bc4f7a9ba64e62..ba9d8961359047 100644\n--- a/kernel/cpu.c\n+++ b/kernel/cpu.c\n@@ -542,7 +542,7 @@ int lockdep_is_cpus_write_held(void)\n \n static void lockdep_acquire_cpus_lock(void)\n {\n-\trwsem_acquire(\u0026cpu_hotplug_lock.dep_map, 0, 0, _THIS_IP_);\n+\trwsem_acquire(\u0026cpu_hotplug_lock.dep_map, 0, 1, _THIS_IP_);\n }\n \n static void lockdep_release_cpus_lock(void)\ndiff --git a/kernel/dependency/Makefile b/kernel/dependency/Makefile\nnew file mode 100644\nindex 00000000000000..fc584ca8712429\n--- /dev/null\n+++ b/kernel/dependency/Makefile\n@@ -0,0 +1,5 @@\n+# SPDX-License-Identifier: GPL-2.0\n+\n+obj-$(CONFIG_DEPT) += dept.o\n+obj-$(CONFIG_DEPT) += dept_proc.o\n+obj-$(CONFIG_DEPT_UNIT_TEST) += dept_unit_test.o\ndiff --git a/kernel/dependency/dept.c b/kernel/dependency/dept.c\nnew file mode 100644\nindex 00000000000000..bcff14f2004662\n--- /dev/null\n+++ b/kernel/dependency/dept.c\n@@ -0,0 +1,3222 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * DEPT(DEPendency Tracker) - Runtime dependency tracker\n+ *\n+ * Started by Byungchul Park \u003cmax.byungchul.park@gmail.com\u003e:\n+ *\n+ *  Copyright (c) 2020 LG Electronics, Inc., Byungchul Park\n+ *  Copyright (c) 2024 SK hynix, Inc., Byungchul Park\n+ *\n+ * DEPT provides a general way to detect potential deadlocks at runtime\n+ * and the interest is not limited to typical lock but to every\n+ * synchronization primitives.\n+ *\n+ * The following ideas were borrowed from LOCKDEP:\n+ *\n+ *    1) Use a graph to track relationship between classes.\n+ *    2) Prevent performance regression using hash.\n+ *\n+ * The following items were enhanced from LOCKDEP:\n+ *\n+ *    1) Cover more deadlock cases.\n+ *    2) Allow multiple reports.\n+ *\n+ * TODO: Both LOCKDEP and DEPT should co-exist until DEPT is considered\n+ * stable. Then the dependency check routine should be replaced with\n+ * DEPT after. It should finally look like:\n+ *\n+ *\n+ *\n+ * As is:\n+ *\n+ *    LOCKDEP\n+ *    +-----------------------------------------+\n+ *    | Lock usage correctness check            | \u003c-\u003e locks\n+ *    |                                         |\n+ *    |                                         |\n+ *    | +-------------------------------------+ |\n+ *    | | Dependency check                    | |\n+ *    | | (by tracking lock acquisition order)| |\n+ *    | +-------------------------------------+ |\n+ *    |                                         |\n+ *    +-----------------------------------------+\n+ *\n+ *    DEPT\n+ *    +-----------------------------------------+\n+ *    | Dependency check                        | \u003c-\u003e waits/events\n+ *    | (by tracking wait and event context)    |\n+ *    +-----------------------------------------+\n+ *\n+ *\n+ *\n+ * To be:\n+ *\n+ *    LOCKDEP\n+ *    +-----------------------------------------+\n+ *    | Lock usage correctness check            | \u003c-\u003e locks\n+ *    |                                         |\n+ *    |                                         |\n+ *    |       (Request dependency check)        |\n+ *    |                    T                    |\n+ *    +--------------------|--------------------+\n+ *                         |\n+ *    DEPT                 V\n+ *    +-----------------------------------------+\n+ *    | Dependency check                        | \u003c-\u003e waits/events\n+ *    | (by tracking wait and event context)    |\n+ *    +-----------------------------------------+\n+ */\n+\n+#include \u003clinux/sched.h\u003e\n+#include \u003clinux/stacktrace.h\u003e\n+#include \u003clinux/spinlock.h\u003e\n+#include \u003clinux/kallsyms.h\u003e\n+#include \u003clinux/hash.h\u003e\n+#include \u003clinux/dept.h\u003e\n+#include \u003clinux/utsname.h\u003e\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/workqueue.h\u003e\n+#include \u003clinux/irq_work.h\u003e\n+#include \u003clinux/vmalloc.h\u003e\n+#include \u003clinux/dept_unit_test.h\u003e\n+#include \"dept_internal.h\"\n+\n+struct dept_ut dept_ut_results;\n+EXPORT_SYMBOL_GPL(dept_ut_results);\n+\n+static int dept_stop;\n+static int dept_per_cpu_ready;\n+\n+static inline struct dept_task *dept_task(void)\n+{\n+\treturn \u0026current-\u003edept_task;\n+}\n+\n+#define DEPT_READY_WARN (!oops_in_progress \u0026\u0026 !dept_task()-\u003ein_warning)\n+\n+/*\n+ * Make all operations using DEPT_WARN_ON() fail on oops_in_progress and\n+ * prevent warning message.\n+ */\n+#define DEPT_WARN_ON_ONCE(c)\t\t\t\t\t\t\\\n+\t({\t\t\t\t\t\t\t\t\\\n+\t\tint __ret = !!(c);\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\t\tif (likely(DEPT_READY_WARN)) {\t\t\t\t\\\n+\t\t\t++dept_task()-\u003ein_warning;\t\t\t\\\n+\t\t\tWARN_ONCE(c, \"DEPT_WARN_ON_ONCE: \" #c);\t\t\\\n+\t\t\t--dept_task()-\u003ein_warning;\t\t\t\\\n+\t\t}\t\t\t\t\t\t\t\\\n+\t\t__ret;\t\t\t\t\t\t\t\\\n+\t})\n+\n+#define DEPT_WARN_ONCE(s...)\t\t\t\t\t\t\\\n+\t({\t\t\t\t\t\t\t\t\\\n+\t\tif (likely(DEPT_READY_WARN)) {\t\t\t\t\\\n+\t\t\t++dept_task()-\u003ein_warning;\t\t\t\\\n+\t\t\tWARN_ONCE(1, \"DEPT_WARN_ONCE: \" s);\t\t\\\n+\t\t\t--dept_task()-\u003ein_warning;\t\t\t\\\n+\t\t}\t\t\t\t\t\t\t\\\n+\t})\n+\n+#define DEPT_WARN_ON(c)\t\t\t\t\t\t\t\\\n+\t({\t\t\t\t\t\t\t\t\\\n+\t\tint __ret = !!(c);\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\t\tif (likely(DEPT_READY_WARN)) {\t\t\t\t\\\n+\t\t\t++dept_task()-\u003ein_warning;\t\t\t\\\n+\t\t\tWARN(c, \"DEPT_WARN_ON: \" #c);\t\t\t\\\n+\t\t\t--dept_task()-\u003ein_warning;\t\t\t\\\n+\t\t}\t\t\t\t\t\t\t\\\n+\t\t__ret;\t\t\t\t\t\t\t\\\n+\t})\n+\n+#define DEPT_WARN(s...)\t\t\t\t\t\t\t\\\n+\t({\t\t\t\t\t\t\t\t\\\n+\t\tif (likely(DEPT_READY_WARN)) {\t\t\t\t\\\n+\t\t\t++dept_task()-\u003ein_warning;\t\t\t\\\n+\t\t\tWARN(1, \"DEPT_WARN: \" s);\t\t\t\\\n+\t\t\t--dept_task()-\u003ein_warning;\t\t\t\\\n+\t\t}\t\t\t\t\t\t\t\\\n+\t})\n+\n+#define DEPT_STOP(s...)\t\t\t\t\t\t\t\\\n+\t({\t\t\t\t\t\t\t\t\\\n+\t\tWRITE_ONCE(dept_stop, 1);\t\t\t\t\\\n+\t\tif (likely(DEPT_READY_WARN)) {\t\t\t\t\\\n+\t\t\t++dept_task()-\u003ein_warning;\t\t\t\\\n+\t\t\tWARN(1, \"DEPT_STOP: \" s);\t\t\t\\\n+\t\t\t--dept_task()-\u003ein_warning;\t\t\t\\\n+\t\t}\t\t\t\t\t\t\t\\\n+\t})\n+\n+#define DEPT_INFO_ONCE(s...)\tpr_warn_once(\"DEPT_INFO_ONCE: \" s)\n+#define DEPT_INFO(s...)\t\tpr_warn(\"DEPT_INFO: \" s)\n+\n+static arch_spinlock_t dept_spin = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;\n+static arch_spinlock_t dept_pool_spin = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;\n+\n+/*\n+ * The DEPT internal engine should be cautious when using external functions\n+ * (e.g., printk) during reporting, as such usage might cause untrackable\n+ * deadlocks.\n+ */\n+static atomic_t dept_outworld = ATOMIC_INIT(0);\n+\n+static void dept_outworld_enter(void)\n+{\n+\tatomic_inc(\u0026dept_outworld);\n+}\n+\n+static void dept_outworld_exit(void)\n+{\n+\tatomic_dec(\u0026dept_outworld);\n+}\n+\n+static bool dept_outworld_entered(void)\n+{\n+\treturn atomic_read(\u0026dept_outworld);\n+}\n+\n+static bool dept_lock(void)\n+{\n+\twhile (!arch_spin_trylock(\u0026dept_spin))\n+\t\tif (unlikely(dept_outworld_entered()))\n+\t\t\treturn false;\n+\treturn true;\n+}\n+\n+static void dept_unlock(void)\n+{\n+\tarch_spin_unlock(\u0026dept_spin);\n+}\n+\n+void dept_stop_emerg(void)\n+{\n+\tWRITE_ONCE(dept_stop, 1);\n+}\n+EXPORT_SYMBOL_GPL(dept_stop_emerg);\n+\n+enum bfs_ret {\n+\tBFS_CONTINUE,\n+\tBFS_DONE,\n+\tBFS_SKIP,\n+};\n+\n+static bool before(unsigned int a, unsigned int b)\n+{\n+\treturn (int)(a - b) \u003c 0;\n+}\n+\n+static bool valid_stack(struct dept_stack *s)\n+{\n+\treturn s \u0026\u0026 s-\u003enr \u003e 0;\n+}\n+\n+static bool valid_class(struct dept_class *c)\n+{\n+\treturn c-\u003ekey;\n+}\n+\n+static void invalidate_class(struct dept_class *c)\n+{\n+\tc-\u003ekey = 0UL;\n+}\n+\n+static struct dept_ecxt *dep_e(struct dept_dep *d)\n+{\n+\treturn d-\u003eecxt;\n+}\n+\n+static struct dept_wait *dep_w(struct dept_dep *d)\n+{\n+\treturn d-\u003ewait;\n+}\n+\n+static struct dept_class *dep_fc(struct dept_dep *d)\n+{\n+\treturn dep_e(d)-\u003eclass;\n+}\n+\n+static struct dept_class *dep_tc(struct dept_dep *d)\n+{\n+\treturn dep_w(d)-\u003eclass;\n+}\n+\n+static const char *irq_str(int irq)\n+{\n+\tif (irq == DEPT_CXT_SIRQ)\n+\t\treturn \"softirq\";\n+\tif (irq == DEPT_CXT_HIRQ)\n+\t\treturn \"hardirq\";\n+\treturn \"(unknown)\";\n+}\n+\n+/*\n+ * DEPT doesn't work when it's stopped by DEPT_STOP() or when running in a\n+ * NMI context.\n+ */\n+static bool dept_working(void)\n+{\n+\treturn !READ_ONCE(dept_stop) \u0026\u0026 !in_nmi();\n+}\n+\n+/*\n+ * Even k == NULL is considered a valid key because it would use\n+ * \u0026-\u003emap_key as the key in that case.\n+ */\n+extern struct lock_class_key __lockdep_no_validate__;\n+static bool valid_key(struct dept_key *k)\n+{\n+\treturn \u0026__lockdep_no_validate__.dkey != k;\n+}\n+\n+/*\n+ * Pool\n+ * =====================================================================\n+ * DEPT maintains pools to provide objects in a safe way.\n+ *\n+ *    1) Static pool is used at the beginning of boot time.\n+ *    2) Local pool is tried first before the static pool. Objects that\n+ *       have been freed will be placed there.\n+ */\n+\n+#define OBJECT(id, nr)\t\t\t\t\t\t\t\\\n+static struct dept_##id spool_##id[nr];\t\t\t\t\t\\\n+static struct dept_##id rpool_##id[nr];\t\t\t\t\t\\\n+static DEFINE_PER_CPU(struct llist_head, lpool_##id);\n+\t#include \"dept_object.h\"\n+#undef OBJECT\n+\n+struct dept_pool dept_pool[OBJECT_NR] = {\n+#define OBJECT(id, nr) {\t\t\t\t\t\t\\\n+\t.name = #id,\t\t\t\t\t\t\t\\\n+\t.obj_sz = sizeof(struct dept_##id),\t\t\t\t\\\n+\t.obj_nr = nr,\t\t\t\t\t\t\t\\\n+\t.tot_nr = nr,\t\t\t\t\t\t\t\\\n+\t.acc_sz = ATOMIC_INIT(sizeof(spool_##id) + sizeof(rpool_##id)), \\\n+\t.node_off = offsetof(struct dept_##id, pool_node),\t\t\\\n+\t.spool = spool_##id,\t\t\t\t\t\t\\\n+\t.rpool = rpool_##id,\t\t\t\t\t\t\\\n+\t.lpool = \u0026lpool_##id, },\n+\t#include \"dept_object.h\"\n+#undef OBJECT\n+};\n+\n+static void dept_wq_work_fn(struct work_struct *work)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i \u003c OBJECT_NR; i++) {\n+\t\tstruct dept_pool *p = dept_pool + i;\n+\t\tint sz = p-\u003etot_nr * p-\u003eobj_sz;\n+\t\tvoid *rpool;\n+\t\tbool need;\n+\n+\t\tlocal_irq_disable();\n+\t\tarch_spin_lock(\u0026dept_pool_spin);\n+\t\tneed = !p-\u003erpool;\n+\t\tarch_spin_unlock(\u0026dept_pool_spin);\n+\t\tlocal_irq_enable();\n+\n+\t\tif (!need)\n+\t\t\tcontinue;\n+\n+\t\trpool = vmalloc(sz);\n+\n+\t\tif (!rpool) {\n+\t\t\tDEPT_STOP(\"Failed to extend internal resources.\\n\");\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tlocal_irq_disable();\n+\t\tarch_spin_lock(\u0026dept_pool_spin);\n+\t\tif (!p-\u003erpool) {\n+\t\t\tp-\u003erpool = rpool;\n+\t\t\trpool = NULL;\n+\t\t\tatomic_add(sz, \u0026p-\u003eacc_sz);\n+\t\t}\n+\t\tarch_spin_unlock(\u0026dept_pool_spin);\n+\t\tlocal_irq_enable();\n+\n+\t\tif (rpool)\n+\t\t\tvfree(rpool);\n+\t\telse\n+\t\t\tDEPT_INFO(\"Dept object(%s) just got refilled successfully.\\n\", p-\u003ename);\n+\t}\n+}\n+\n+static DECLARE_WORK(dept_wq_work, dept_wq_work_fn);\n+\n+static void dept_irq_work_fn(struct irq_work *w)\n+{\n+\tschedule_work(\u0026dept_wq_work);\n+}\n+\n+static DEFINE_IRQ_WORK(dept_irq_work, dept_irq_work_fn);\n+\n+static void request_rpool_refill(void)\n+{\n+\tirq_work_queue(\u0026dept_irq_work);\n+}\n+\n+/*\n+ * We can use llist regardless of whether CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG\n+ * is enabled, because NMI and other contexts on the same CPU never run\n+ * inside DEPT concurrently—reentrance is prevented.\n+ */\n+static void *from_pool(enum object_t t)\n+{\n+\tstruct dept_pool *p;\n+\tstruct llist_head *h;\n+\tstruct llist_node *n;\n+\n+\t/*\n+\t * llist_del_first() doesn't allow concurrent access, e.g.,\n+\t * between process and IRQ context.\n+\t */\n+\tif (DEPT_WARN_ON(!irqs_disabled()))\n+\t\treturn NULL;\n+\n+\tp = \u0026dept_pool[t];\n+\n+\t/*\n+\t * Try local pool first.\n+\t */\n+\tif (likely(dept_per_cpu_ready))\n+\t\th = this_cpu_ptr(p-\u003elpool);\n+\telse\n+\t\th = \u0026p-\u003eboot_pool;\n+\n+\tn = llist_del_first(h);\n+\tif (n)\n+\t\treturn (void *)n - p-\u003enode_off;\n+\n+\t/*\n+\t * Try static pool.\n+\t */\n+\tarch_spin_lock(\u0026dept_pool_spin);\n+\n+\tif (!p-\u003eobj_nr) {\n+\t\tp-\u003espool = p-\u003erpool;\n+\t\tp-\u003eobj_nr = p-\u003erpool ? p-\u003etot_nr : 0;\n+\t\tp-\u003erpool = NULL;\n+\t\trequest_rpool_refill();\n+\t}\n+\n+\tif (p-\u003eobj_nr) {\n+\t\tvoid *ret;\n+\n+\t\tp-\u003eobj_nr--;\n+\t\tret = p-\u003espool + (p-\u003eobj_nr * p-\u003eobj_sz);\n+\t\tarch_spin_unlock(\u0026dept_pool_spin);\n+\n+\t\treturn ret;\n+\t}\n+\tarch_spin_unlock(\u0026dept_pool_spin);\n+\n+\tDEPT_INFO(\"------------------------------------------\\n\"\n+\t\t\"  Dept object(%s) is run out.\\n\"\n+\t\t\"  Dept is trying to refill the object.\\n\"\n+\t\t\"  Nevertheless, if it fails, Dept will stop.\\n\",\n+\t\tp-\u003ename);\n+\treturn NULL;\n+}\n+\n+static void to_pool(void *o, enum object_t t)\n+{\n+\tstruct dept_pool *p = \u0026dept_pool[t];\n+\tstruct llist_head *h;\n+\n+\tpreempt_disable();\n+\tif (likely(dept_per_cpu_ready))\n+\t\th = this_cpu_ptr(p-\u003elpool);\n+\telse\n+\t\th = \u0026p-\u003eboot_pool;\n+\n+\tllist_add(o + p-\u003enode_off, h);\n+\tpreempt_enable();\n+}\n+\n+#define OBJECT(id, nr)\t\t\t\t\t\t\t\\\n+static void (*ctor_##id)(struct dept_##id *a);\t\t\t\t\\\n+static void (*dtor_##id)(struct dept_##id *a);\t\t\t\t\\\n+static struct dept_##id *new_##id(void)\t\t\t\t\t\\\n+{\t\t\t\t\t\t\t\t\t\\\n+\tstruct dept_##id *a;\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\ta = (struct dept_##id *)from_pool(OBJECT_##id);\t\t\t\\\n+\tif (unlikely(!a))\t\t\t\t\t\t\\\n+\t\treturn NULL;\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\tatomic_set(\u0026a-\u003eref, 1);\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\tif (ctor_##id)\t\t\t\t\t\t\t\\\n+\t\tctor_##id(a);\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\treturn a;\t\t\t\t\t\t\t\\\n+}\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+static struct dept_##id *get_##id(struct dept_##id *a)\t\t\t\\\n+{\t\t\t\t\t\t\t\t\t\\\n+\tatomic_inc(\u0026a-\u003eref);\t\t\t\t\t\t\\\n+\treturn a;\t\t\t\t\t\t\t\\\n+}\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+static void put_##id(struct dept_##id *a)\t\t\t\t\\\n+{\t\t\t\t\t\t\t\t\t\\\n+\tif (!atomic_dec_return(\u0026a-\u003eref)) {\t\t\t\t\\\n+\t\tif (dtor_##id)\t\t\t\t\t\t\\\n+\t\t\tdtor_##id(a);\t\t\t\t\t\\\n+\t\tto_pool(a, OBJECT_##id);\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\\\n+}\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+static void del_##id(struct dept_##id *a)\t\t\t\t\\\n+{\t\t\t\t\t\t\t\t\t\\\n+\tput_##id(a);\t\t\t\t\t\t\t\\\n+}\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+static bool __maybe_unused id##_consumed(struct dept_##id *a)\t\t\\\n+{\t\t\t\t\t\t\t\t\t\\\n+\treturn a \u0026\u0026 atomic_read(\u0026a-\u003eref) \u003e 1;\t\t\t\t\\\n+}\n+#include \"dept_object.h\"\n+#undef OBJECT\n+\n+#define SET_CONSTRUCTOR(id, f) \\\n+static void (*ctor_##id)(struct dept_##id *a) = f\n+\n+static void initialize_dep(struct dept_dep *d)\n+{\n+\tINIT_LIST_HEAD(\u0026d-\u003edep_node);\n+\tINIT_LIST_HEAD(\u0026d-\u003edep_rev_node);\n+}\n+SET_CONSTRUCTOR(dep, initialize_dep);\n+\n+static void initialize_class(struct dept_class *c)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i \u003c DEPT_CXT_IRQS_NR; i++) {\n+\t\tstruct dept_iecxt *ie = \u0026c-\u003eiecxt[i];\n+\t\tstruct dept_iwait *iw = \u0026c-\u003eiwait[i];\n+\n+\t\tie-\u003eecxt = NULL;\n+\t\tie-\u003eenirq = i;\n+\t\tie-\u003estaled = false;\n+\n+\t\tiw-\u003ewait = NULL;\n+\t\tiw-\u003eirq = i;\n+\t\tiw-\u003estaled = false;\n+\t\tiw-\u003etouched = false;\n+\t}\n+\tc-\u003ebfs_gen = 0U;\n+\n+\tINIT_LIST_HEAD(\u0026c-\u003eall_node);\n+\tINIT_LIST_HEAD(\u0026c-\u003edep_head);\n+\tINIT_LIST_HEAD(\u0026c-\u003edep_rev_head);\n+\tINIT_LIST_HEAD(\u0026c-\u003ebfs_node);\n+}\n+SET_CONSTRUCTOR(class, initialize_class);\n+\n+static void initialize_ecxt(struct dept_ecxt *e)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i \u003c DEPT_CXT_IRQS_NR; i++) {\n+\t\te-\u003eenirq_stack[i] = NULL;\n+\t\te-\u003eenirq_ip[i] = 0UL;\n+\t}\n+\te-\u003eecxt_ip = 0UL;\n+\te-\u003eecxt_stack = NULL;\n+\te-\u003eenirqf = 0UL;\n+\te-\u003eevent_ip = 0UL;\n+\te-\u003eevent_stack = NULL;\n+\te-\u003eewait_stack = NULL;\n+}\n+SET_CONSTRUCTOR(ecxt, initialize_ecxt);\n+\n+static void initialize_wait(struct dept_wait *w)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i \u003c DEPT_CXT_IRQS_NR; i++) {\n+\t\tw-\u003eirq_stack[i] = NULL;\n+\t\tw-\u003eirq_ip[i] = 0UL;\n+\t}\n+\tw-\u003ewait_ip = 0UL;\n+\tw-\u003ewait_stack = NULL;\n+\tw-\u003eirqf = 0UL;\n+}\n+SET_CONSTRUCTOR(wait, initialize_wait);\n+\n+static void initialize_stack(struct dept_stack *s)\n+{\n+\ts-\u003enr = 0;\n+}\n+SET_CONSTRUCTOR(stack, initialize_stack);\n+\n+#define OBJECT(id, nr) \\\n+static void (*ctor_##id)(struct dept_##id *a);\n+\t#include \"dept_object.h\"\n+#undef OBJECT\n+\n+#undef SET_CONSTRUCTOR\n+\n+#define SET_DESTRUCTOR(id, f) \\\n+static void (*dtor_##id)(struct dept_##id *a) = f\n+\n+static void destroy_dep(struct dept_dep *d)\n+{\n+\tif (dep_e(d))\n+\t\tput_ecxt(dep_e(d));\n+\tif (dep_w(d))\n+\t\tput_wait(dep_w(d));\n+}\n+SET_DESTRUCTOR(dep, destroy_dep);\n+\n+static void destroy_ecxt(struct dept_ecxt *e)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i \u003c DEPT_CXT_IRQS_NR; i++)\n+\t\tif (e-\u003eenirq_stack[i])\n+\t\t\tput_stack(e-\u003eenirq_stack[i]);\n+\tif (e-\u003eclass)\n+\t\tput_class(e-\u003eclass);\n+\tif (e-\u003eecxt_stack)\n+\t\tput_stack(e-\u003eecxt_stack);\n+\tif (e-\u003eevent_stack)\n+\t\tput_stack(e-\u003eevent_stack);\n+\tif (e-\u003eewait_stack)\n+\t\tput_stack(e-\u003eewait_stack);\n+}\n+SET_DESTRUCTOR(ecxt, destroy_ecxt);\n+\n+static void destroy_wait(struct dept_wait *w)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i \u003c DEPT_CXT_IRQS_NR; i++)\n+\t\tif (w-\u003eirq_stack[i])\n+\t\t\tput_stack(w-\u003eirq_stack[i]);\n+\tif (w-\u003eclass)\n+\t\tput_class(w-\u003eclass);\n+\tif (w-\u003ewait_stack)\n+\t\tput_stack(w-\u003ewait_stack);\n+}\n+SET_DESTRUCTOR(wait, destroy_wait);\n+\n+#define OBJECT(id, nr) \\\n+static void (*dtor_##id)(struct dept_##id *a);\n+\t#include \"dept_object.h\"\n+#undef OBJECT\n+\n+#undef SET_DESTRUCTOR\n+\n+/*\n+ * Caching and hashing\n+ * =====================================================================\n+ * DEPT makes use of caching and hashing to improve performance. Each\n+ * object can be obtained in O(1) with its key.\n+ *\n+ * NOTE: Currently we assume all the objects in the hashs will never be\n+ * removed. Implement it when needed.\n+ */\n+\n+/*\n+ * Some information might be lost but it's only for hashing key.\n+ */\n+static unsigned long mix(unsigned long a, unsigned long b)\n+{\n+\tint halfbits = sizeof(unsigned long) * 8 / 2;\n+\tunsigned long halfmask = (1UL \u003c\u003c halfbits) - 1UL;\n+\n+\treturn (a \u003c\u003c halfbits) | (b \u0026 halfmask);\n+}\n+\n+static bool cmp_dep(struct dept_dep *d1, struct dept_dep *d2)\n+{\n+\treturn dep_fc(d1)-\u003ekey == dep_fc(d2)-\u003ekey \u0026\u0026\n+\t       dep_tc(d1)-\u003ekey == dep_tc(d2)-\u003ekey;\n+}\n+\n+static unsigned long key_dep(struct dept_dep *d)\n+{\n+\treturn mix(dep_fc(d)-\u003ekey, dep_tc(d)-\u003ekey);\n+}\n+\n+static bool cmp_class(struct dept_class *c1, struct dept_class *c2)\n+{\n+\treturn c1-\u003ekey == c2-\u003ekey;\n+}\n+\n+static unsigned long key_class(struct dept_class *c)\n+{\n+\treturn c-\u003ekey;\n+}\n+\n+#define HASH(id, bits)\t\t\t\t\t\t\t\\\n+static struct hlist_head table_##id[1 \u003c\u003c (bits)];\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+static struct hlist_head *head_##id(struct dept_##id *a)\t\t\\\n+{\t\t\t\t\t\t\t\t\t\\\n+\treturn table_##id + hash_long(key_##id(a), bits);\t\t\\\n+}\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+static struct dept_##id *hash_lookup_##id(struct dept_##id *a)\t\t\\\n+{\t\t\t\t\t\t\t\t\t\\\n+\tstruct dept_##id *b;\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\thlist_for_each_entry_rcu(b, head_##id(a), hash_node)\t\t\\\n+\t\tif (cmp_##id(a, b))\t\t\t\t\t\\\n+\t\t\treturn b;\t\t\t\t\t\\\n+\treturn NULL;\t\t\t\t\t\t\t\\\n+}\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+static void hash_add_##id(struct dept_##id *a)\t\t\t\t\\\n+{\t\t\t\t\t\t\t\t\t\\\n+\tget_##id(a);\t\t\t\t\t\t\t\\\n+\thlist_add_head_rcu(\u0026a-\u003ehash_node, head_##id(a));\t\t\\\n+}\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+static void hash_del_##id(struct dept_##id *a)\t\t\t\t\\\n+{\t\t\t\t\t\t\t\t\t\\\n+\thlist_del_rcu(\u0026a-\u003ehash_node);\t\t\t\t\t\\\n+\tput_##id(a);\t\t\t\t\t\t\t\\\n+}\n+#include \"dept_hash.h\"\n+#undef HASH\n+\n+static struct dept_dep *lookup_dep(struct dept_class *fc,\n+\t\t\t\t   struct dept_class *tc)\n+{\n+\tstruct dept_ecxt onetime_e = { .class = fc };\n+\tstruct dept_wait onetime_w = { .class = tc };\n+\tstruct dept_dep  onetime_d = { .ecxt = \u0026onetime_e,\n+\t\t\t\t       .wait = \u0026onetime_w };\n+\treturn hash_lookup_dep(\u0026onetime_d);\n+}\n+\n+static struct dept_class *lookup_class(unsigned long key)\n+{\n+\tstruct dept_class onetime_c = { .key = key };\n+\n+\treturn hash_lookup_class(\u0026onetime_c);\n+}\n+\n+/*\n+ * Report\n+ * =====================================================================\n+ * DEPT prints useful information to help debugging on detection of\n+ * problematic dependency.\n+ */\n+\n+static void print_ip_stack(unsigned long ip, struct dept_stack *s)\n+{\n+\tif (ip)\n+\t\tprint_ip_sym(KERN_WARNING, ip);\n+\n+#ifdef CONFIG_DEPT_DEBUG\n+\tif (!s)\n+\t\tpr_warn(\"stack is NULL.\\n\");\n+\telse if (!s-\u003enr)\n+\t\tpr_warn(\"stack-\u003enr is 0.\\n\");\n+\tif (s)\n+\t\tpr_warn(\"stack ref is %d.\\n\", atomic_read(\u0026s-\u003eref));\n+#endif\n+\n+\tif (valid_stack(s)) {\n+\t\tpr_warn(\"stacktrace:\\n\");\n+\t\tstack_trace_print(s-\u003eraw, s-\u003enr, 5);\n+\t}\n+\n+\tif (!ip \u0026\u0026 !valid_stack(s))\n+\t\tpr_warn(\"(N/A)\\n\");\n+}\n+\n+#define print_spc(spc, fmt, ...) \\\n+\tpr_warn(\"%*c\" fmt, (spc) * 3, ' ', ##__VA_ARGS__)\n+\n+static void print_diagram(struct dept_dep *d)\n+{\n+\tstruct dept_ecxt *e = dep_e(d);\n+\tstruct dept_wait *w = dep_w(d);\n+\tstruct dept_class *fc = dep_fc(d);\n+\tstruct dept_class *tc = dep_tc(d);\n+\tunsigned long irqf;\n+\tint irq;\n+\tbool firstline = true;\n+\tint spc = 1;\n+\tconst char *w_fn = w-\u003ewait_fn ?: \"(unknown)\";\n+\tconst char *e_fn = e-\u003eevent_fn ?: \"(unknown)\";\n+\tconst char *c_fn = e-\u003eecxt_fn ?: \"(unknown)\";\n+\tconst char *fc_n = fc-\u003esched_map ? \"\u003csched\u003e\" : (fc-\u003ename ?: \"(unknown)\");\n+\tconst char *tc_n = tc-\u003esched_map ? \"\u003csched\u003e\" : (tc-\u003ename ?: \"(unknown)\");\n+\n+\tirqf = e-\u003eenirqf \u0026 w-\u003eirqf;\n+\tfor_each_set_bit(irq, \u0026irqf, DEPT_CXT_IRQS_NR) {\n+\t\tif (!firstline)\n+\t\t\tpr_warn(\"\\nor\\n\\n\");\n+\t\tfirstline = false;\n+\n+\t\tprint_spc(spc, \"[S] %s(%s:%d)\\n\", c_fn, fc_n, fc-\u003esub_id);\n+\t\tprint_spc(spc, \"    \u003c%s interrupt\u003e\\n\", irq_str(irq));\n+\t\tprint_spc(spc + 1, \"[W] %s(%s:%d)\\n\", w_fn, tc_n, tc-\u003esub_id);\n+\t\tprint_spc(spc, \"[E] %s(%s:%d)\\n\", e_fn, fc_n, fc-\u003esub_id);\n+\t}\n+\n+\tif (!irqf) {\n+\t\tprint_spc(spc, \"[S] %s(%s:%d)\\n\", c_fn, fc_n, fc-\u003esub_id);\n+\t\tprint_spc(spc, \"[W] %s(%s:%d)\\n\", w_fn, tc_n, tc-\u003esub_id);\n+\t\tif (w-\u003etimeout)\n+\t\t\tprint_spc(spc, \"--------------- \u003e8 timeout ---------------\\n\");\n+\t\tprint_spc(spc, \"[E] %s(%s:%d)\\n\", e_fn, fc_n, fc-\u003esub_id);\n+\t}\n+}\n+\n+static void print_dep(struct dept_dep *d)\n+{\n+\tstruct dept_ecxt *e = dep_e(d);\n+\tstruct dept_wait *w = dep_w(d);\n+\tstruct dept_class *fc = dep_fc(d);\n+\tstruct dept_class *tc = dep_tc(d);\n+\tunsigned long irqf;\n+\tint irq;\n+\tconst char *w_fn = w-\u003ewait_fn ?: \"(unknown)\";\n+\tconst char *e_fn = e-\u003eevent_fn ?: \"(unknown)\";\n+\tconst char *c_fn = e-\u003eecxt_fn ?: \"(unknown)\";\n+\tconst char *fc_n = fc-\u003esched_map ? \"\u003csched\u003e\" : (fc-\u003ename ?: \"(unknown)\");\n+\tconst char *tc_n = tc-\u003esched_map ? \"\u003csched\u003e\" : (tc-\u003ename ?: \"(unknown)\");\n+\n+\tirqf = e-\u003eenirqf \u0026 w-\u003eirqf;\n+\tfor_each_set_bit(irq, \u0026irqf, DEPT_CXT_IRQS_NR) {\n+\t\tpr_warn(\"%s has been enabled:\\n\", irq_str(irq));\n+\t\tprint_ip_stack(e-\u003eenirq_ip[irq], e-\u003eenirq_stack[irq]);\n+\t\tpr_warn(\"\\n\");\n+\n+\t\tpr_warn(\"[S] %s(%s:%d):\\n\", c_fn, fc_n, fc-\u003esub_id);\n+\t\tprint_ip_stack(e-\u003eecxt_ip, e-\u003eecxt_stack);\n+\t\tpr_warn(\"\\n\");\n+\n+\t\tpr_warn(\"[W] %s(%s:%d) in %s context:\\n\",\n+\t\t       w_fn, tc_n, tc-\u003esub_id, irq_str(irq));\n+\t\tprint_ip_stack(w-\u003eirq_ip[irq], w-\u003eirq_stack[irq]);\n+\t\tpr_warn(\"\\n\");\n+\n+\t\tpr_warn(\"[E] %s(%s:%d):\\n\", e_fn, fc_n, fc-\u003esub_id);\n+\t\tprint_ip_stack(e-\u003eevent_ip, e-\u003eevent_stack);\n+\n+\t\tif (valid_stack(e-\u003eewait_stack)) {\n+\t\t\tpr_warn(\"(wait to wake up)\\n\");\n+\t\t\tprint_ip_stack(0, e-\u003eewait_stack);\n+\t\t}\n+\t}\n+\n+\tif (!irqf) {\n+\t\tpr_warn(\"[S] %s(%s:%d):\\n\", c_fn, fc_n, fc-\u003esub_id);\n+\t\tprint_ip_stack(e-\u003eecxt_ip, e-\u003eecxt_stack);\n+\t\tpr_warn(\"\\n\");\n+\n+\t\tpr_warn(\"[W] %s(%s:%d):\\n\", w_fn, tc_n, tc-\u003esub_id);\n+\t\tprint_ip_stack(w-\u003ewait_ip, w-\u003ewait_stack);\n+\t\tpr_warn(\"\\n\");\n+\n+\t\tpr_warn(\"[E] %s(%s:%d):\\n\", e_fn, fc_n, fc-\u003esub_id);\n+\t\tprint_ip_stack(e-\u003eevent_ip, e-\u003eevent_stack);\n+\n+\t\tif (valid_stack(e-\u003eewait_stack)) {\n+\t\t\tpr_warn(\"(wait to wake up)\\n\");\n+\t\t\tprint_ip_stack(0, e-\u003eewait_stack);\n+\t\t}\n+\n+\t\tdept_ut_ecxt_stack_account(valid_stack(e-\u003eecxt_stack));\n+\t\tdept_ut_wait_stack_account(valid_stack(w-\u003ewait_stack));\n+\t\tdept_ut_evnt_stack_account(valid_stack(e-\u003eevent_stack));\n+\t}\n+}\n+\n+static void save_current_stack(int skip);\n+\n+static bool is_timeout_wait_circle(struct dept_class *c)\n+{\n+\tstruct dept_class *fc = c-\u003ebfs_parent;\n+\tstruct dept_class *tc = c;\n+\n+\tdo {\n+\t\tstruct dept_dep *d = lookup_dep(fc, tc);\n+\n+\t\tif (d-\u003ewait-\u003etimeout)\n+\t\t\treturn true;\n+\n+\t\ttc = fc;\n+\t\tfc = fc-\u003ebfs_parent;\n+\t} while (tc != c);\n+\n+\treturn false;\n+}\n+\n+/*\n+ * Print all classes in a circle.\n+ */\n+static void print_circle(struct dept_class *c)\n+{\n+\tstruct dept_class *fc = c-\u003ebfs_parent;\n+\tstruct dept_class *tc = c;\n+\tint i;\n+\n+\tdept_outworld_enter();\n+\tsave_current_stack(6);\n+\n+\tpr_warn(\"===================================================\\n\");\n+\tpr_warn(\"DEPT: Circular dependency has been detected.\\n\");\n+\tpr_warn(\"%s %.*s %s\\n\", init_utsname()-\u003erelease,\n+\t\t(int)strcspn(init_utsname()-\u003eversion, \" \"),\n+\t\tinit_utsname()-\u003eversion,\n+\t\tprint_tainted());\n+\tpr_warn(\"---------------------------------------------------\\n\");\n+\tpr_warn(\"summary\\n\");\n+\tpr_warn(\"---------------------------------------------------\\n\");\n+\n+\tif (is_timeout_wait_circle(c)) {\n+\t\tpr_warn(\"NOT A DEADLOCK BUT A CIRCULAR DEPENDENCY\\n\");\n+\t\tpr_warn(\"CHECK IF THE TIMEOUT IS INTENDED\\n\\n\");\n+\t} else if (fc == tc) {\n+\t\tpr_warn(\"*** AA DEADLOCK ***\\n\\n\");\n+\t} else {\n+\t\tpr_warn(\"*** DEADLOCK ***\\n\\n\");\n+\t}\n+\n+\ti = 0;\n+\tdo {\n+\t\tstruct dept_dep *d = lookup_dep(fc, tc);\n+\n+\t\tpr_warn(\"context %c\\n\", 'A' + (i++));\n+\t\tprint_diagram(d);\n+\t\tif (fc != c)\n+\t\t\tpr_warn(\"\\n\");\n+\n+\t\ttc = fc;\n+\t\tfc = fc-\u003ebfs_parent;\n+\t} while (tc != c);\n+\n+\tpr_warn(\"\\n\");\n+\tpr_warn(\"[S]: start of the event context\\n\");\n+\tpr_warn(\"[W]: the wait blocked\\n\");\n+\tpr_warn(\"[E]: the event not reachable\\n\");\n+\n+\ti = 0;\n+\tdo {\n+\t\tstruct dept_dep *d = lookup_dep(fc, tc);\n+\n+\t\tpr_warn(\"---------------------------------------------------\\n\");\n+\t\tpr_warn(\"context %c's detail\\n\", 'A' + i);\n+\t\tpr_warn(\"---------------------------------------------------\\n\");\n+\t\tpr_warn(\"context %c\\n\", 'A' + (i++));\n+\t\tprint_diagram(d);\n+\t\tpr_warn(\"\\n\");\n+\t\tprint_dep(d);\n+\n+\t\ttc = fc;\n+\t\tfc = fc-\u003ebfs_parent;\n+\t} while (tc != c);\n+\n+\tpr_warn(\"---------------------------------------------------\\n\");\n+\tpr_warn(\"information that might be helpful\\n\");\n+\tpr_warn(\"---------------------------------------------------\\n\");\n+\tdump_stack();\n+\n+\tdept_outworld_exit();\n+\n+\tdept_ut_circle_detect();\n+}\n+\n+/*\n+ * BFS(Breadth First Search)\n+ * =====================================================================\n+ * Whenever a new dependency is added into the graph, search the graph\n+ * for a new circular dependency.\n+ */\n+\n+struct bfs_ops {\n+\tvoid (*bfs_init)(void *, void *, void **);\n+\tvoid (*extend)(struct list_head *, void *);\n+\tvoid *(*dequeue)(struct list_head *);\n+\tenum bfs_ret (*callback)(void *, void *, void **);\n+};\n+\n+static unsigned int bfs_gen;\n+\n+/*\n+ * NOTE: Must be called with dept_lock held.\n+ */\n+static void bfs(void *root, struct bfs_ops *ops, void *in, void **out)\n+{\n+\tLIST_HEAD(q);\n+\tenum bfs_ret ret;\n+\n+\tif (DEPT_WARN_ON(!ops || !ops-\u003ebfs_init || !ops-\u003eextend ||\n+\t\t\t\t!ops-\u003edequeue || !ops-\u003ecallback))\n+\t\treturn;\n+\n+\t/*\n+\t * Avoid zero bfs_gen.\n+\t */\n+\tbfs_gen = bfs_gen + 1 ?: 1;\n+\tops-\u003ebfs_init(root, in, out);\n+\n+\tret = ops-\u003ecallback(root, in, out);\n+\tif (ret != BFS_CONTINUE)\n+\t\treturn;\n+\n+\tops-\u003eextend(\u0026q, root);\n+\twhile (!list_empty(\u0026q)) {\n+\t\tvoid *node = ops-\u003edequeue(\u0026q);\n+\n+\t\tif (ret == BFS_DONE)\n+\t\t\tcontinue;\n+\n+\t\tret = ops-\u003ecallback(node, in, out);\n+\t\tif (ret == BFS_CONTINUE)\n+\t\t\tops-\u003eextend(\u0026q, node);\n+\t}\n+}\n+\n+/*\n+ * Main operations\n+ * =====================================================================\n+ * Add dependencies - Each new dependency is added into the graph and\n+ * checked if it forms a circular dependency.\n+ *\n+ * Track waits - Waits are queued into the ring buffer for later use to\n+ * generate appropriate dependencies with cross-event.\n+ *\n+ * Track event contexts(ecxt) - Event contexts are pushed into local\n+ * stack for later use to generate appropriate dependencies with waits.\n+ */\n+\n+static unsigned long cur_enirqf(void);\n+static int cur_cxt(void);\n+static unsigned int cur_ctxt_id(void);\n+\n+static struct dept_iecxt *iecxt(struct dept_class *c, int irq)\n+{\n+\treturn \u0026c-\u003eiecxt[irq];\n+}\n+\n+static struct dept_iwait *iwait(struct dept_class *c, int irq)\n+{\n+\treturn \u0026c-\u003eiwait[irq];\n+}\n+\n+static void stale_iecxt(struct dept_iecxt *ie)\n+{\n+\tif (ie-\u003eecxt)\n+\t\tput_ecxt(ie-\u003eecxt);\n+\n+\tWRITE_ONCE(ie-\u003eecxt, NULL);\n+\tWRITE_ONCE(ie-\u003estaled, true);\n+}\n+\n+static void set_iecxt(struct dept_iecxt *ie, struct dept_ecxt *e)\n+{\n+\t/*\n+\t * -\u003eecxt will never be updated once getting set until the class\n+\t * gets removed.\n+\t */\n+\tif (ie-\u003eecxt)\n+\t\tDEPT_WARN_ON(1);\n+\telse\n+\t\tWRITE_ONCE(ie-\u003eecxt, get_ecxt(e));\n+}\n+\n+static void stale_iwait(struct dept_iwait *iw)\n+{\n+\tif (iw-\u003ewait)\n+\t\tput_wait(iw-\u003ewait);\n+\n+\tWRITE_ONCE(iw-\u003ewait, NULL);\n+\tWRITE_ONCE(iw-\u003estaled, true);\n+}\n+\n+static void set_iwait(struct dept_iwait *iw, struct dept_wait *w)\n+{\n+\t/*\n+\t * -\u003ewait will never be updated once getting set until the class\n+\t * gets removed.\n+\t */\n+\tif (iw-\u003ewait)\n+\t\tDEPT_WARN_ON(1);\n+\telse\n+\t\tWRITE_ONCE(iw-\u003ewait, get_wait(w));\n+\n+\tiw-\u003etouched = true;\n+}\n+\n+static void touch_iwait(struct dept_iwait *iw)\n+{\n+\tiw-\u003etouched = true;\n+}\n+\n+static void untouch_iwait(struct dept_iwait *iw)\n+{\n+\tiw-\u003etouched = false;\n+}\n+\n+static struct dept_stack *get_current_stack(void)\n+{\n+\tstruct dept_stack *s = dept_task()-\u003estack;\n+\n+\treturn s ? get_stack(s) : NULL;\n+}\n+\n+static void prepare_current_stack(void)\n+{\n+\tDEPT_WARN_ON(dept_task()-\u003estack);\n+\n+\tdept_task()-\u003estack = new_stack();\n+}\n+\n+static void save_current_stack(int skip)\n+{\n+\tstruct dept_stack *s = dept_task()-\u003estack;\n+\n+\tif (!s)\n+\t\treturn;\n+\n+\tif (valid_stack(s))\n+\t\treturn;\n+\n+\ts-\u003enr = stack_trace_save(s-\u003eraw, DEPT_MAX_STACK_ENTRY, skip);\n+}\n+\n+static void finish_current_stack(void)\n+{\n+\tstruct dept_stack *s = dept_task()-\u003estack;\n+\n+\t/*\n+\t * Fill the struct dept_stack with a valid stracktrace if it has\n+\t * been referred at least once.\n+\t */\n+\tif (stack_consumed(s))\n+\t\tsave_current_stack(2);\n+\n+\tdept_task()-\u003estack = NULL;\n+\n+\t/*\n+\t * Actual deletion will happen at put_stack() if the stack has\n+\t * been referred.\n+\t */\n+\tif (s)\n+\t\tdel_stack(s);\n+}\n+\n+/*\n+ * FIXME: For now, disable LOCKDEP while DEPT is working.\n+ *\n+ * Both LOCKDEP and DEPT report it on a deadlock detection using\n+ * printk taking the risk of another deadlock that might be caused by\n+ * locks of console or printk between inside and outside of them.\n+ *\n+ * For DEPT, it's no problem since multiple reports are allowed. But it\n+ * would be a bad idea for LOCKDEP since it will stop even on a singe\n+ * report. So we need to prevent LOCKDEP from its reporting the risk\n+ * DEPT would take when reporting something.\n+ */\n+#include \u003clinux/lockdep.h\u003e\n+\n+void noinstr dept_off(void)\n+{\n+\tdept_task()-\u003erecursive++;\n+\tlockdep_off();\n+}\n+\n+void noinstr dept_on(void)\n+{\n+\tlockdep_on();\n+\tdept_task()-\u003erecursive--;\n+}\n+\n+static unsigned long dept_enter(void)\n+{\n+\tunsigned long flags;\n+\n+\tflags = arch_local_irq_save();\n+\tdept_off();\n+\tprepare_current_stack();\n+\treturn flags;\n+}\n+\n+static void dept_exit(unsigned long flags)\n+{\n+\tfinish_current_stack();\n+\tdept_on();\n+\tarch_local_irq_restore(flags);\n+}\n+\n+static unsigned long dept_enter_recursive(void)\n+{\n+\tunsigned long flags;\n+\n+\tflags = arch_local_irq_save();\n+\treturn flags;\n+}\n+\n+static void dept_exit_recursive(unsigned long flags)\n+{\n+\tarch_local_irq_restore(flags);\n+}\n+\n+/*\n+ * NOTE: Must be called with dept_lock held.\n+ */\n+static struct dept_dep *__add_dep(struct dept_ecxt *e,\n+\t\t\t\t  struct dept_wait *w)\n+{\n+\tstruct dept_dep *d;\n+\n+\tif (DEPT_WARN_ON(!valid_class(e-\u003eclass)))\n+\t\treturn NULL;\n+\n+\tif (DEPT_WARN_ON(!valid_class(w-\u003eclass)))\n+\t\treturn NULL;\n+\n+\tif (lookup_dep(e-\u003eclass, w-\u003eclass))\n+\t\treturn NULL;\n+\n+\td = new_dep();\n+\tif (unlikely(!d))\n+\t\treturn NULL;\n+\n+\td-\u003eecxt = get_ecxt(e);\n+\td-\u003ewait = get_wait(w);\n+\n+\t/*\n+\t * Add the dependency into hash and graph.\n+\t */\n+\thash_add_dep(d);\n+\tlist_add(\u0026d-\u003edep_node, \u0026dep_fc(d)-\u003edep_head);\n+\tlist_add(\u0026d-\u003edep_rev_node, \u0026dep_tc(d)-\u003edep_rev_head);\n+\treturn d;\n+}\n+\n+static void bfs_init_check_dl(void *node, void *in, void **out)\n+{\n+\tstruct dept_class *root = (struct dept_class *)node;\n+\tstruct dept_dep *new = (struct dept_dep *)in;\n+\n+\troot-\u003ebfs_gen = bfs_gen;\n+\tdep_tc(new)-\u003ebfs_parent = dep_fc(new);\n+}\n+\n+static void bfs_extend_dep(struct list_head *h, void *node)\n+{\n+\tstruct dept_class *cur = (struct dept_class *)node;\n+\tstruct dept_dep *d;\n+\n+\tlist_for_each_entry(d, \u0026cur-\u003edep_head, dep_node) {\n+\t\tstruct dept_class *next = dep_tc(d);\n+\n+\t\tif (bfs_gen == next-\u003ebfs_gen)\n+\t\t\tcontinue;\n+\t\tnext-\u003ebfs_parent = cur;\n+\t\tnext-\u003ebfs_gen = bfs_gen;\n+\t\tlist_add_tail(\u0026next-\u003ebfs_node, h);\n+\t}\n+}\n+\n+static void *bfs_dequeue_dep(struct list_head *h)\n+{\n+\tstruct dept_class *c;\n+\n+\tDEPT_WARN_ON(list_empty(h));\n+\n+\tc = list_first_entry(h, struct dept_class, bfs_node);\n+\tlist_del(\u0026c-\u003ebfs_node);\n+\treturn c;\n+}\n+\n+static enum bfs_ret cb_check_dl(void *node, void *in, void **out)\n+{\n+\tstruct dept_class *cur = (struct dept_class *)node;\n+\tstruct dept_dep *new = (struct dept_dep *)in;\n+\n+\tif (cur == dep_fc(new)) {\n+\t\tprint_circle(dep_tc(new));\n+\t\treturn BFS_DONE;\n+\t}\n+\n+\treturn BFS_CONTINUE;\n+}\n+\n+/*\n+ * This function is actually in charge of reporting.\n+ */\n+static void check_dl_bfs(struct dept_dep *d)\n+{\n+\tstruct bfs_ops ops = {\n+\t\t.bfs_init = bfs_init_check_dl,\n+\t\t.extend = bfs_extend_dep,\n+\t\t.dequeue = bfs_dequeue_dep,\n+\t\t.callback = cb_check_dl,\n+\t};\n+\n+\tbfs((void *)dep_tc(d), \u0026ops, (void *)d, NULL);\n+}\n+\n+static void bfs_init_dep(void *node, void *in, void **out)\n+{\n+\tstruct dept_class *root = (struct dept_class *)node;\n+\n+\troot-\u003ebfs_gen = bfs_gen;\n+}\n+\n+static void bfs_extend_dep_rev(struct list_head *h, void *node)\n+{\n+\tstruct dept_class *cur = (struct dept_class *)node;\n+\tstruct dept_dep *d;\n+\n+\tlist_for_each_entry(d, \u0026cur-\u003edep_rev_head, dep_rev_node) {\n+\t\tstruct dept_class *next = dep_fc(d);\n+\n+\t\tif (bfs_gen == next-\u003ebfs_gen)\n+\t\t\tcontinue;\n+\t\tnext-\u003ebfs_parent = cur;\n+\t\tnext-\u003ebfs_gen = bfs_gen;\n+\t\tlist_add_tail(\u0026next-\u003ebfs_node, h);\n+\t}\n+}\n+\n+static enum bfs_ret cb_find_iw(void *node, void *in, void **out)\n+{\n+\tstruct dept_class *cur = (struct dept_class *)node;\n+\tint irq = *(int *)in;\n+\tstruct dept_iwait *iw;\n+\n+\tif (DEPT_WARN_ON(!out))\n+\t\treturn BFS_DONE;\n+\n+\tiw = iwait(cur, irq);\n+\n+\t/*\n+\t * If any parent's -\u003ewait was set, then the children would've\n+\t * been touched.\n+\t */\n+\tif (!iw-\u003etouched)\n+\t\treturn BFS_SKIP;\n+\n+\tif (!iw-\u003ewait)\n+\t\treturn BFS_CONTINUE;\n+\n+\t*out = iw;\n+\treturn BFS_DONE;\n+}\n+\n+static struct dept_iwait *find_iw_bfs(struct dept_class *c, int irq)\n+{\n+\tstruct dept_iwait *iw = iwait(c, irq);\n+\tstruct dept_iwait *found = NULL;\n+\tstruct bfs_ops ops = {\n+\t\t.bfs_init = bfs_init_dep,\n+\t\t.extend = bfs_extend_dep_rev,\n+\t\t.dequeue = bfs_dequeue_dep,\n+\t\t.callback = cb_find_iw,\n+\t};\n+\n+\tbfs((void *)c, \u0026ops, (void *)\u0026irq, (void **)\u0026found);\n+\n+\tif (found)\n+\t\treturn found;\n+\n+\tuntouch_iwait(iw);\n+\treturn NULL;\n+}\n+\n+static enum bfs_ret cb_touch_iw_find_ie(void *node, void *in, void **out)\n+{\n+\tstruct dept_class *cur = (struct dept_class *)node;\n+\tint irq = *(int *)in;\n+\tstruct dept_iecxt *ie = iecxt(cur, irq);\n+\tstruct dept_iwait *iw = iwait(cur, irq);\n+\n+\tif (DEPT_WARN_ON(!out))\n+\t\treturn BFS_DONE;\n+\n+\ttouch_iwait(iw);\n+\n+\tif (!ie-\u003eecxt)\n+\t\treturn BFS_CONTINUE;\n+\tif (!*out)\n+\t\t*out = ie;\n+\n+\t/*\n+\t * Do touch_iwait() all the way.\n+\t */\n+\treturn BFS_CONTINUE;\n+}\n+\n+static struct dept_iecxt *touch_iw_find_ie_bfs(struct dept_class *c,\n+\t\t\t\t\t       int irq)\n+{\n+\tstruct dept_iecxt *found = NULL;\n+\tstruct bfs_ops ops = {\n+\t\t.bfs_init = bfs_init_dep,\n+\t\t.extend = bfs_extend_dep,\n+\t\t.dequeue = bfs_dequeue_dep,\n+\t\t.callback = cb_touch_iw_find_ie,\n+\t};\n+\n+\tbfs((void *)c, \u0026ops, (void *)\u0026irq, (void **)\u0026found);\n+\treturn found;\n+}\n+\n+/*\n+ * Should be called with dept_lock held.\n+ */\n+static void __add_idep(struct dept_iecxt *ie, struct dept_iwait *iw)\n+{\n+\tstruct dept_dep *new;\n+\n+\t/*\n+\t * There's nothing to do.\n+\t */\n+\tif (!ie || !iw || !ie-\u003eecxt || !iw-\u003ewait)\n+\t\treturn;\n+\n+\tnew = __add_dep(ie-\u003eecxt, iw-\u003ewait);\n+\n+\t/*\n+\t * Deadlock detected. Let check_dl_bfs() report it.\n+\t */\n+\tif (new) {\n+\t\tcheck_dl_bfs(new);\n+\t\tstale_iecxt(ie);\n+\t\tstale_iwait(iw);\n+\t}\n+\n+\t/*\n+\t * If !new, it would be the case of lack of object resource.\n+\t * Just let it go and get checked by other chances. Retrying is\n+\t * meaningless in that case.\n+\t */\n+}\n+\n+static void set_check_iecxt(struct dept_class *c, int irq,\n+\t\t\t    struct dept_ecxt *e)\n+{\n+\tstruct dept_iecxt *ie = iecxt(c, irq);\n+\n+\tset_iecxt(ie, e);\n+\t__add_idep(ie, find_iw_bfs(c, irq));\n+}\n+\n+static void set_check_iwait(struct dept_class *c, int irq,\n+\t\t\t    struct dept_wait *w)\n+{\n+\tstruct dept_iwait *iw = iwait(c, irq);\n+\n+\tset_iwait(iw, w);\n+\t__add_idep(touch_iw_find_ie_bfs(c, irq), iw);\n+}\n+\n+static void add_iecxt(struct dept_class *c, int irq, struct dept_ecxt *e,\n+\t\t      bool stack)\n+{\n+\t/*\n+\t * This access is safe since we ensure e-\u003eclass has set locally.\n+\t */\n+\tstruct dept_task *dt = dept_task();\n+\tstruct dept_iecxt *ie = iecxt(c, irq);\n+\n+\tif (DEPT_WARN_ON(!valid_class(c)))\n+\t\treturn;\n+\n+\tif (unlikely(READ_ONCE(ie-\u003estaled)))\n+\t\treturn;\n+\n+\t/*\n+\t * Skip add_iecxt() if ie-\u003eecxt has ever been set at least once.\n+\t * Which means it has a valid -\u003eecxt or been staled.\n+\t */\n+\tif (READ_ONCE(ie-\u003eecxt))\n+\t\treturn;\n+\n+\tif (unlikely(!dept_lock()))\n+\t\treturn;\n+\n+\tif (unlikely(ie-\u003estaled))\n+\t\tgoto unlock;\n+\tif (ie-\u003eecxt)\n+\t\tgoto unlock;\n+\n+\te-\u003eenirqf |= (1UL \u003c\u003c irq);\n+\n+\t/*\n+\t * Should be NULL since it's the first time that these\n+\t * enirq_{ip,stack}[irq] have ever set.\n+\t */\n+\tDEPT_WARN_ON(e-\u003eenirq_ip[irq]);\n+\tDEPT_WARN_ON(e-\u003eenirq_stack[irq]);\n+\n+\te-\u003eenirq_ip[irq] = dt-\u003eenirq_ip[irq];\n+\te-\u003eenirq_stack[irq] = stack ? get_current_stack() : NULL;\n+\n+\tset_check_iecxt(c, irq, e);\n+unlock:\n+\tdept_unlock();\n+}\n+\n+static void add_iwait(struct dept_class *c, int irq, struct dept_wait *w)\n+{\n+\tstruct dept_iwait *iw = iwait(c, irq);\n+\n+\tif (DEPT_WARN_ON(!valid_class(c)))\n+\t\treturn;\n+\n+\tif (unlikely(READ_ONCE(iw-\u003estaled)))\n+\t\treturn;\n+\n+\t/*\n+\t * Skip add_iwait() if iw-\u003ewait has ever been set at least once.\n+\t * Which means it has a valid -\u003ewait or been staled.\n+\t */\n+\tif (READ_ONCE(iw-\u003ewait))\n+\t\treturn;\n+\n+\tif (unlikely(!dept_lock()))\n+\t\treturn;\n+\n+\tif (unlikely(iw-\u003estaled))\n+\t\tgoto unlock;\n+\tif (iw-\u003ewait)\n+\t\tgoto unlock;\n+\n+\tw-\u003eirqf |= (1UL \u003c\u003c irq);\n+\n+\t/*\n+\t * Should be NULL since it's the first time that these\n+\t * irq_{ip,stack}[irq] have ever set.\n+\t */\n+\tDEPT_WARN_ON(w-\u003eirq_ip[irq]);\n+\tDEPT_WARN_ON(w-\u003eirq_stack[irq]);\n+\n+\tw-\u003eirq_ip[irq] = w-\u003ewait_ip;\n+\tw-\u003eirq_stack[irq] = get_current_stack();\n+\n+\tset_check_iwait(c, irq, w);\n+unlock:\n+\tdept_unlock();\n+}\n+\n+static struct dept_wait_hist *hist(int pos)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\n+\treturn dt-\u003ewait_hist + (pos % DEPT_MAX_WAIT_HIST);\n+}\n+\n+static int hist_pos_next(void)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\n+\treturn dt-\u003ewait_hist_pos % DEPT_MAX_WAIT_HIST;\n+}\n+\n+static void hist_advance(void)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\n+\tdt-\u003ewait_hist_pos++;\n+\tdt-\u003ewait_hist_pos %= DEPT_MAX_WAIT_HIST;\n+}\n+\n+static struct dept_wait_hist *new_hist(void)\n+{\n+\tstruct dept_wait_hist *wh = hist(hist_pos_next());\n+\n+\thist_advance();\n+\treturn wh;\n+}\n+\n+static struct dept_wait_hist *last_hist(void)\n+{\n+\tint pos_n = hist_pos_next();\n+\tstruct dept_wait_hist *wh_n = hist(pos_n);\n+\n+\t/*\n+\t * This is the first try.\n+\t */\n+\tif (!pos_n \u0026\u0026 !wh_n-\u003ewait)\n+\t\treturn NULL;\n+\n+\treturn hist(pos_n + DEPT_MAX_WAIT_HIST - 1);\n+}\n+\n+static void add_hist(struct dept_wait *w, unsigned int wg, unsigned int ctxt_id)\n+{\n+\tstruct dept_wait_hist *wh;\n+\n+\twh = last_hist();\n+\n+\tif (!wh || wh-\u003ewait-\u003eclass != w-\u003eclass || wh-\u003ectxt_id != ctxt_id)\n+\t\twh = new_hist();\n+\n+\tif (likely(wh-\u003ewait))\n+\t\tput_wait(wh-\u003ewait);\n+\n+\twh-\u003ewait = get_wait(w);\n+\twh-\u003ewgen = wg;\n+\twh-\u003ectxt_id = ctxt_id;\n+}\n+\n+/*\n+ * Should be called after setting up e's iecxt and w's iwait.\n+ */\n+static void add_dep(struct dept_ecxt *e, struct dept_wait *w)\n+{\n+\tstruct dept_class *fc = e-\u003eclass;\n+\tstruct dept_class *tc = w-\u003eclass;\n+\tstruct dept_dep *d;\n+\tint i;\n+\n+\tif (lookup_dep(fc, tc))\n+\t\treturn;\n+\n+\tif (unlikely(!dept_lock()))\n+\t\treturn;\n+\n+\t/*\n+\t * __add_dep() will lookup_dep() again with lock held.\n+\t */\n+\td = __add_dep(e, w);\n+\tif (d) {\n+\t\tcheck_dl_bfs(d);\n+\n+\t\tfor (i = 0; i \u003c DEPT_CXT_IRQS_NR; i++) {\n+\t\t\tstruct dept_iwait *fiw = iwait(fc, i);\n+\t\t\tstruct dept_iecxt *found_ie;\n+\t\t\tstruct dept_iwait *found_iw;\n+\n+\t\t\t/*\n+\t\t\t * '-\u003etouched == false' guarantees there's no\n+\t\t\t * parent that has been set -\u003ewait.\n+\t\t\t */\n+\t\t\tif (!fiw-\u003etouched)\n+\t\t\t\tcontinue;\n+\n+\t\t\t/*\n+\t\t\t * find_iw_bfs() will untouch the iwait if\n+\t\t\t * not found.\n+\t\t\t */\n+\t\t\tfound_iw = find_iw_bfs(fc, i);\n+\n+\t\t\tif (!found_iw)\n+\t\t\t\tcontinue;\n+\n+\t\t\tfound_ie = touch_iw_find_ie_bfs(tc, i);\n+\t\t\t__add_idep(found_ie, found_iw);\n+\t\t}\n+\t}\n+\tdept_unlock();\n+}\n+\n+static atomic_t wgen = ATOMIC_INIT(1);\n+\n+static int next_wgen(void)\n+{\n+\t/*\n+\t * Avoid zero wgen.\n+\t */\n+\treturn atomic_inc_return(\u0026wgen) ?: atomic_inc_return(\u0026wgen);\n+}\n+\n+/*\n+ * XXX: This is a temporary patch needed until lockdep stops tracking\n+ * dependency in wrong way.  lockdep has added an annotation to specify\n+ * a callback to determin whether the given lock aquisition order is\n+ * okay or not in its own way.  Even though dept is already working\n+ * correctly with sub class on that issue, it needs to be aware of the\n+ * annotation anyway.\n+ */\n+static bool lockdep_cmp_fn(struct dept_map *prev, struct dept_map *next)\n+{\n+\t/*\n+\t * Assumes the cmp_fn thing comes from struct lockdep_map.\n+\t */\n+\tstruct lockdep_map *p_lock = (struct lockdep_map *)prev-\u003elockdep_map;\n+\tstruct lockdep_map *n_lock = (struct lockdep_map *)next-\u003elockdep_map;\n+\tstruct lock_class *p_class = p_lock ? p_lock-\u003eclass_cache[0] : NULL;\n+\tstruct lock_class *n_class = n_lock ? n_lock-\u003eclass_cache[0] : NULL;\n+\n+\tif (!p_class || !n_class)\n+\t\treturn false;\n+\n+\tif (p_class != n_class)\n+\t\treturn false;\n+\n+\tif (!p_class-\u003ecmp_fn)\n+\t\treturn false;\n+\n+\treturn p_class-\u003ecmp_fn(p_lock, n_lock) \u003c 0;\n+}\n+\n+static void add_wait(struct dept_map *m, struct dept_class *c,\n+\t\tunsigned long ip, const char *w_fn, int sub_l,\n+\t\tbool sched_sleep, bool timeout)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tstruct dept_wait *w;\n+\tunsigned int wg;\n+\tint cxt;\n+\tint i;\n+\n+\tif (DEPT_WARN_ON(!valid_class(c)))\n+\t\treturn;\n+\n+\tw = new_wait();\n+\tif (unlikely(!w))\n+\t\treturn;\n+\n+\tWRITE_ONCE(w-\u003eclass, get_class(c));\n+\tw-\u003ewait_ip = ip;\n+\tw-\u003ewait_fn = w_fn;\n+\tw-\u003ewait_stack = get_current_stack();\n+\tw-\u003esched_sleep = sched_sleep;\n+\tw-\u003etimeout = timeout;\n+\n+\tcxt = cur_cxt();\n+\tif (cxt == DEPT_CXT_HIRQ || cxt == DEPT_CXT_SIRQ)\n+\t\tadd_iwait(c, cxt, w);\n+\n+\t/*\n+\t * Avoid adding dependency between user aware nested ecxt and\n+\t * wait.\n+\t */\n+\tfor (i = dt-\u003eecxt_held_pos - 1; i \u003e= 0; i--) {\n+\t\tstruct dept_ecxt_held *eh;\n+\n+\t\teh = dt-\u003eecxt_held + i;\n+\n+\t\t/*\n+\t\t * the case of invalid key'ed one\n+\t\t */\n+\t\tif (!eh-\u003eecxt)\n+\t\t\tcontinue;\n+\n+\t\tif (eh-\u003eecxt-\u003eclass == c \u0026\u0026 eh-\u003esub_l != sub_l)\n+\t\t\tcontinue;\n+\n+\t\tif (i == dt-\u003eecxt_held_pos - 1 \u0026\u0026 lockdep_cmp_fn(eh-\u003emap, m))\n+\t\t\tcontinue;\n+\n+\t\tadd_dep(eh-\u003eecxt, w);\n+\t}\n+\n+\twg = next_wgen();\n+\tadd_hist(w, wg, cur_ctxt_id());\n+\n+\tdel_wait(w);\n+}\n+\n+static struct dept_ecxt_held *add_ecxt(struct dept_map *m,\n+\t\tstruct dept_class *c, unsigned long ip, const char *c_fn,\n+\t\tconst char *e_fn, int sub_l,\n+\t\tstruct dept_stack *ewait_stack)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tstruct dept_ecxt_held *eh;\n+\tstruct dept_ecxt *e;\n+\tunsigned long irqf;\n+\tunsigned int wg;\n+\tint irq;\n+\n+\tif (DEPT_WARN_ON(!valid_class(c)))\n+\t\treturn NULL;\n+\n+\tif (DEPT_WARN_ON_ONCE(dt-\u003eecxt_held_pos \u003e= DEPT_MAX_ECXT_HELD))\n+\t\treturn NULL;\n+\n+\twg = next_wgen();\n+\tif (m-\u003enocheck) {\n+\t\teh = dt-\u003eecxt_held + (dt-\u003eecxt_held_pos++);\n+\t\teh-\u003eecxt = NULL;\n+\t\teh-\u003emap = m;\n+\t\teh-\u003eclass = get_class(c);\n+\t\teh-\u003ewgen = wg;\n+\t\teh-\u003esub_l = sub_l;\n+\n+\t\treturn eh;\n+\t}\n+\n+\te = new_ecxt();\n+\tif (unlikely(!e))\n+\t\treturn NULL;\n+\n+\te-\u003eclass = get_class(c);\n+\te-\u003eecxt_ip = ip;\n+\te-\u003eecxt_stack = ip ? get_current_stack() : NULL;\n+\te-\u003eewait_stack = ewait_stack ? get_stack(ewait_stack) : NULL;\n+\te-\u003eevent_fn = e_fn;\n+\te-\u003eecxt_fn = c_fn;\n+\n+\teh = dt-\u003eecxt_held + (dt-\u003eecxt_held_pos++);\n+\teh-\u003eecxt = get_ecxt(e);\n+\teh-\u003emap = m;\n+\teh-\u003eclass = get_class(c);\n+\teh-\u003ewgen = wg;\n+\teh-\u003esub_l = sub_l;\n+\n+\tirqf = cur_enirqf();\n+\tfor_each_set_bit(irq, \u0026irqf, DEPT_CXT_IRQS_NR)\n+\t\tadd_iecxt(c, irq, e, false);\n+\n+\tdel_ecxt(e);\n+\treturn eh;\n+}\n+\n+static int find_ecxt_pos(struct dept_map *m, struct dept_class *c,\n+\t\t\t bool newfirst)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tint i;\n+\n+\tif (newfirst) {\n+\t\tfor (i = dt-\u003eecxt_held_pos - 1; i \u003e= 0; i--) {\n+\t\t\tstruct dept_ecxt_held *eh;\n+\n+\t\t\teh = dt-\u003eecxt_held + i;\n+\t\t\tif (eh-\u003emap == m \u0026\u0026 eh-\u003eclass == c)\n+\t\t\t\treturn i;\n+\t\t}\n+\t} else {\n+\t\tfor (i = 0; i \u003c dt-\u003eecxt_held_pos; i++) {\n+\t\t\tstruct dept_ecxt_held *eh;\n+\n+\t\t\teh = dt-\u003eecxt_held + i;\n+\t\t\tif (eh-\u003emap == m \u0026\u0026 eh-\u003eclass == c)\n+\t\t\t\treturn i;\n+\t\t}\n+\t}\n+\treturn -1;\n+}\n+\n+static bool pop_ecxt(struct dept_map *m, struct dept_class *c)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tint pos;\n+\tint i;\n+\n+\tpos = find_ecxt_pos(m, c, true);\n+\tif (pos == -1)\n+\t\treturn false;\n+\n+\tif (dt-\u003eecxt_held[pos].class)\n+\t\tput_class(dt-\u003eecxt_held[pos].class);\n+\n+\tif (dt-\u003eecxt_held[pos].ecxt)\n+\t\tput_ecxt(dt-\u003eecxt_held[pos].ecxt);\n+\n+\tdt-\u003eecxt_held_pos--;\n+\n+\tfor (i = pos; i \u003c dt-\u003eecxt_held_pos; i++)\n+\t\tdt-\u003eecxt_held[i] = dt-\u003eecxt_held[i + 1];\n+\treturn true;\n+}\n+\n+static bool good_hist(struct dept_wait_hist *wh, unsigned int wg)\n+{\n+\treturn wh-\u003ewait != NULL \u0026\u0026 before(wg, wh-\u003ewgen);\n+}\n+\n+/*\n+ * Binary-search the ring buffer for the earliest valid wait.\n+ */\n+static int find_hist_pos(unsigned int wg)\n+{\n+\tint oldest;\n+\tint l;\n+\tint r;\n+\tint pos;\n+\n+\toldest = hist_pos_next();\n+\tif (unlikely(good_hist(hist(oldest), wg))) {\n+\t\tDEPT_INFO_ONCE(\"Need to expand the ring buffer.\\n\");\n+\t\treturn oldest;\n+\t}\n+\n+\tl = oldest + 1;\n+\tr = oldest + DEPT_MAX_WAIT_HIST - 1;\n+\tfor (pos = (l + r) / 2; l \u003c= r; pos = (l + r) / 2) {\n+\t\tstruct dept_wait_hist *p = hist(pos - 1);\n+\t\tstruct dept_wait_hist *wh = hist(pos);\n+\n+\t\tif (!good_hist(p, wg) \u0026\u0026 good_hist(wh, wg))\n+\t\t\treturn pos % DEPT_MAX_WAIT_HIST;\n+\t\tif (good_hist(wh, wg))\n+\t\t\tr = pos - 1;\n+\t\telse\n+\t\t\tl = pos + 1;\n+\t}\n+\treturn -1;\n+}\n+\n+static void do_event(struct dept_map *m, struct dept_map *real_m,\n+\t\tstruct dept_class *c, unsigned int wg, unsigned long ip,\n+\t\tconst char *e_fn, struct dept_stack *ewait_stack)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tstruct dept_wait_hist *wh;\n+\tstruct dept_ecxt_held *eh;\n+\tunsigned int ctxt_id;\n+\tint end;\n+\tint pos;\n+\tint i;\n+\n+\tif (DEPT_WARN_ON(!valid_class(c)))\n+\t\treturn;\n+\n+\tif (m-\u003enocheck)\n+\t\treturn;\n+\n+\t/*\n+\t * The event was triggered before wait.\n+\t */\n+\tif (!wg)\n+\t\treturn;\n+\n+\t/*\n+\t * If an ecxt for this map exists, let the ecxt work for this\n+\t * event and do not proceed it in do_event().\n+\t */\n+\tif (find_ecxt_pos(real_m, c, false) != -1)\n+\t\treturn;\n+\teh = add_ecxt(m, c, 0UL, NULL, e_fn, 0, ewait_stack);\n+\n+\tif (!eh)\n+\t\treturn;\n+\n+\tif (DEPT_WARN_ON(!eh-\u003eecxt))\n+\t\tgoto out;\n+\n+\teh-\u003eecxt-\u003eevent_ip = ip;\n+\teh-\u003eecxt-\u003eevent_stack = get_current_stack();\n+\n+\tpos = find_hist_pos(wg);\n+\tif (pos == -1)\n+\t\tgoto out;\n+\n+\tctxt_id = cur_ctxt_id();\n+\tend = hist_pos_next();\n+\tend = end \u003e pos ? end : end + DEPT_MAX_WAIT_HIST;\n+\tfor (wh = hist(pos); pos \u003c end; wh = hist(++pos)) {\n+\t\tif (dt-\u003ein_sched \u0026\u0026 wh-\u003ewait-\u003esched_sleep)\n+\t\t\tcontinue;\n+\n+\t\tif (wh-\u003ectxt_id == ctxt_id)\n+\t\t\tadd_dep(eh-\u003eecxt, wh-\u003ewait);\n+\t}\n+\n+\tfor (i = 0; i \u003c DEPT_CXT_IRQS_NR; i++) {\n+\t\tstruct dept_ecxt *e;\n+\n+\t\tif (before(dt-\u003ewgen_enirq[i], wg))\n+\t\t\tcontinue;\n+\n+\t\te = eh-\u003eecxt;\n+\t\tadd_iecxt(e-\u003eclass, i, e, false);\n+\t}\n+out:\n+\t/*\n+\t * Pop ecxt that temporarily has been added to handle this event.\n+\t */\n+\tpop_ecxt(m, c);\n+}\n+\n+static void del_dep_rcu(struct rcu_head *rh)\n+{\n+\tstruct dept_dep *d = container_of(rh, struct dept_dep, rh);\n+\n+\tpreempt_disable();\n+\tdel_dep(d);\n+\tpreempt_enable();\n+}\n+\n+/*\n+ * NOTE: Must be called with dept_lock held.\n+ */\n+static void disconnect_class(struct dept_class *c)\n+{\n+\tstruct dept_dep *d, *n;\n+\tint i;\n+\n+\tlist_for_each_entry_safe(d, n, \u0026c-\u003edep_head, dep_node) {\n+\t\tlist_del_rcu(\u0026d-\u003edep_node);\n+\t\tlist_del_rcu(\u0026d-\u003edep_rev_node);\n+\t\thash_del_dep(d);\n+\t\tcall_rcu(\u0026d-\u003erh, del_dep_rcu);\n+\t}\n+\n+\tlist_for_each_entry_safe(d, n, \u0026c-\u003edep_rev_head, dep_rev_node) {\n+\t\tlist_del_rcu(\u0026d-\u003edep_node);\n+\t\tlist_del_rcu(\u0026d-\u003edep_rev_node);\n+\t\thash_del_dep(d);\n+\t\tcall_rcu(\u0026d-\u003erh, del_dep_rcu);\n+\t}\n+\n+\tfor (i = 0; i \u003c DEPT_CXT_IRQS_NR; i++) {\n+\t\tstale_iecxt(iecxt(c, i));\n+\t\tstale_iwait(iwait(c, i));\n+\t}\n+}\n+\n+/*\n+ * Context control\n+ * =====================================================================\n+ * Whether a wait is in {hard,soft}-IRQ context or whether\n+ * {hard,soft}-IRQ has been enabled on the way to an event is very\n+ * important to check dependency. All those things should be tracked.\n+ */\n+\n+static unsigned long cur_enirqf(void)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tint he = dt-\u003ehardirqs_enabled;\n+\tint se = dt-\u003esoftirqs_enabled;\n+\n+\tif (he)\n+\t\treturn DEPT_HIRQF | (se ? DEPT_SIRQF : 0UL);\n+\treturn 0UL;\n+}\n+\n+static int cur_cxt(void)\n+{\n+\tif (lockdep_softirq_context(current))\n+\t\treturn DEPT_CXT_SIRQ;\n+\tif (lockdep_hardirq_context())\n+\t\treturn DEPT_CXT_HIRQ;\n+\treturn DEPT_CXT_PROCESS;\n+}\n+\n+static unsigned int cur_ctxt_id(void)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tint cxt = cur_cxt();\n+\n+\treturn dt-\u003ecxt_id[cxt] | (1UL \u003c\u003c cxt);\n+}\n+\n+static void enirq_transition(int irq)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tint i;\n+\n+\t/*\n+\t * IRQ can cut in on the way to the event. Used for cross-event\n+\t * detection.\n+\t *\n+\t *    wait context\tevent context(ecxt)\n+\t *    ------------\t-------------------\n+\t *    wait event\n+\t *       UPDATE wgen\n+\t *\t\t\tobserve IRQ enabled\n+\t *\t\t\t   UPDATE wgen\n+\t *\t\t\t   keep the wgen locally\n+\t *\n+\t *\t\t\ton the event\n+\t *\t\t\t   check the wgen kept\n+\t */\n+\n+\tdt-\u003ewgen_enirq[irq] = next_wgen();\n+\n+\tfor (i = dt-\u003eecxt_held_pos - 1; i \u003e= 0; i--) {\n+\t\tstruct dept_ecxt_held *eh;\n+\t\tstruct dept_ecxt *e;\n+\n+\t\teh = dt-\u003eecxt_held + i;\n+\t\te = eh-\u003eecxt;\n+\t\tif (e)\n+\t\t\tadd_iecxt(e-\u003eclass, irq, e, true);\n+\t}\n+}\n+\n+static void dept_enirq(unsigned long ip)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tunsigned long irqf = cur_enirqf();\n+\tint irq;\n+\tunsigned long flags;\n+\n+\tif (unlikely(!dept_working()))\n+\t\treturn;\n+\n+\t/*\n+\t * IRQ ON/OFF transition might happen while Dept is working.\n+\t * We cannot handle recursive entrance. Just ignore it.\n+\t * Only transitions outside of Dept will be considered.\n+\t */\n+\tif (dt-\u003erecursive)\n+\t\treturn;\n+\n+\tflags = dept_enter();\n+\n+\tfor_each_set_bit(irq, \u0026irqf, DEPT_CXT_IRQS_NR) {\n+\t\tdt-\u003eenirq_ip[irq] = ip;\n+\t\tenirq_transition(irq);\n+\t}\n+\n+\tdept_exit(flags);\n+}\n+\n+void dept_softirqs_on_ip(unsigned long ip)\n+{\n+\t/*\n+\t * Assumes that it's called with IRQ disabled so that accessing\n+\t * current's fields is not racy.\n+\t */\n+\tdept_task()-\u003esoftirqs_enabled = true;\n+\tdept_enirq(ip);\n+}\n+\n+void dept_hardirqs_on(void)\n+{\n+\t/*\n+\t * Assumes that it's called with IRQ disabled so that accessing\n+\t * current's fields is not racy.\n+\t */\n+\tdept_task()-\u003ehardirqs_enabled = true;\n+\tdept_enirq(_RET_IP_);\n+}\n+\n+void dept_softirqs_off(void)\n+{\n+\t/*\n+\t * Assumes that it's called with IRQ disabled so that accessing\n+\t * current's fields is not racy.\n+\t */\n+\tdept_task()-\u003esoftirqs_enabled = false;\n+}\n+\n+void noinstr dept_hardirqs_off(void)\n+{\n+\t/*\n+\t * Assumes that it's called with IRQ disabled so that accessing\n+\t * current's fields is not racy.\n+\t */\n+\tdept_task()-\u003ehardirqs_enabled = false;\n+}\n+EXPORT_SYMBOL_GPL(dept_hardirqs_off);\n+\n+void noinstr dept_update_cxt(void)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\n+\tdt-\u003ecxt_id[DEPT_CXT_PROCESS] += 1UL \u003c\u003c DEPT_CXTS_NR;\n+}\n+\n+/*\n+ * Ensure it's the outmost softirq context.\n+ */\n+void dept_softirq_enter(void)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\n+\tdt-\u003ecxt_id[DEPT_CXT_SIRQ] += 1UL \u003c\u003c DEPT_CXTS_NR;\n+}\n+\n+/*\n+ * Ensure it's the outmost hardirq context.\n+ */\n+void noinstr dept_hardirq_enter(void)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\n+\tdt-\u003ecxt_id[DEPT_CXT_HIRQ] += 1UL \u003c\u003c DEPT_CXTS_NR;\n+}\n+\n+void dept_sched_enter(void)\n+{\n+\tdept_task()-\u003ein_sched = true;\n+}\n+\n+void dept_sched_exit(void)\n+{\n+\tdept_task()-\u003ein_sched = false;\n+}\n+\n+/*\n+ * Exposed APIs\n+ * =====================================================================\n+ */\n+\n+static void clean_classes_cache(struct dept_key *k)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i \u003c DEPT_MAX_SUBCLASSES_CACHE; i++) {\n+\t\tif (!READ_ONCE(k-\u003eclasses[i]))\n+\t\t\tcontinue;\n+\n+\t\tWRITE_ONCE(k-\u003eclasses[i], NULL);\n+\t}\n+}\n+\n+/*\n+ * Assume we don't have to consider race with the map when\n+ * dept_map_init() is called.\n+ */\n+void dept_map_init(struct dept_map *m, struct dept_key *k, int sub_u,\n+\t\t   const char *n)\n+{\n+\tunsigned long flags;\n+\n+\tif (unlikely(!dept_working())) {\n+\t\tm-\u003enocheck = true;\n+\t\treturn;\n+\t}\n+\n+\tif (DEPT_WARN_ON(sub_u \u003c 0)) {\n+\t\tm-\u003enocheck = true;\n+\t\treturn;\n+\t}\n+\n+\tif (DEPT_WARN_ON(sub_u \u003e= DEPT_MAX_SUBCLASSES_USR)) {\n+\t\tm-\u003enocheck = true;\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * Allow recursive entrance.\n+\t */\n+\tflags = dept_enter_recursive();\n+\n+\tclean_classes_cache(\u0026m-\u003emap_key);\n+\n+\tm-\u003ekeys = k;\n+\tm-\u003esub_u = sub_u;\n+\tm-\u003ename = n;\n+\tm-\u003ewgen = 0U;\n+\tm-\u003enocheck = !valid_key(k);\n+\tm-\u003elockdep_map = NULL;\n+\n+\tdept_exit_recursive(flags);\n+}\n+EXPORT_SYMBOL_GPL(dept_map_init);\n+\n+/*\n+ * Assume we don't have to consider race with the map when\n+ * dept_map_reinit() is called.\n+ */\n+void dept_map_reinit(struct dept_map *m, struct dept_key *k, int sub_u,\n+\t\t     const char *n)\n+{\n+\tunsigned long flags;\n+\n+\tif (unlikely(!dept_working())) {\n+\t\tm-\u003enocheck = true;\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * Allow recursive entrance.\n+\t */\n+\tflags = dept_enter_recursive();\n+\n+\tif (k) {\n+\t\tclean_classes_cache(\u0026m-\u003emap_key);\n+\t\tm-\u003ekeys = k;\n+\t\tm-\u003enocheck = !valid_key(k);\n+\t}\n+\n+\tif (sub_u \u003e= 0 \u0026\u0026 sub_u \u003c DEPT_MAX_SUBCLASSES_USR)\n+\t\tm-\u003esub_u = sub_u;\n+\n+\tif (n)\n+\t\tm-\u003ename = n;\n+\n+\tm-\u003ewgen = 0U;\n+\n+\tdept_exit_recursive(flags);\n+}\n+EXPORT_SYMBOL_GPL(dept_map_reinit);\n+\n+void dept_ext_wgen_init(struct dept_ext_wgen *ewg)\n+{\n+\tewg-\u003ewgen = 0U;\n+}\n+\n+void dept_map_copy(struct dept_map *to, struct dept_map *from)\n+{\n+\tif (unlikely(!dept_working())) {\n+\t\tto-\u003enocheck = true;\n+\t\treturn;\n+\t}\n+\n+\t*to = *from;\n+\n+\t/*\n+\t * XXX: 'to' might be in a stack or something. Using the address\n+\t * in a stack segment as a key is meaningless. Just ignore the\n+\t * case for now.\n+\t */\n+\tif (!to-\u003ekeys) {\n+\t\tto-\u003enocheck = true;\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * Since the class cache can be modified concurrently we could\n+\t * observe half pointers (64bit arch using 32bit copy\n+\t * instructions).  Therefore clear the caches and take the\n+\t * performance hit.\n+\t */\n+\tclean_classes_cache(\u0026to-\u003emap_key);\n+}\n+\n+LIST_HEAD(dept_classes);\n+\n+static bool within(const void *addr, void *start, unsigned long size)\n+{\n+\treturn addr \u003e= start \u0026\u0026 addr \u003c start + size;\n+}\n+\n+void dept_free_range(void *start, unsigned int sz)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tstruct dept_class *c, *n;\n+\tunsigned long flags;\n+\n+\tif (unlikely(!dept_working()))\n+\t\treturn;\n+\n+\tif (dt-\u003erecursive) {\n+\t\tDEPT_STOP(\"Failed to successfully free Dept objects.\\n\");\n+\t\treturn;\n+\t}\n+\n+\tflags = dept_enter();\n+\n+\t/*\n+\t * dept_free_range() should not fail.\n+\t *\n+\t * FIXME: Should be fixed if dept_free_range() causes deadlock\n+\t * with dept_lock().\n+\t */\n+\twhile (unlikely(!dept_lock()))\n+\t\tcpu_relax();\n+\n+\tlist_for_each_entry_safe(c, n, \u0026dept_classes, all_node) {\n+\t\tif (!within((void *)c-\u003ekey, start, sz) \u0026\u0026\n+\t\t    !within(c-\u003ename, start, sz))\n+\t\t\tcontinue;\n+\n+\t\thash_del_class(c);\n+\t\tdisconnect_class(c);\n+\t\tlist_del(\u0026c-\u003eall_node);\n+\t\tinvalidate_class(c);\n+\n+\t\t/*\n+\t\t * Actual deletion will happen on the rcu callback\n+\t\t * that has been added in disconnect_class().\n+\t\t */\n+\t\tdel_class(c);\n+\t}\n+\tdept_unlock();\n+\tdept_exit(flags);\n+\n+\t/*\n+\t * Wait until even lockless hash_lookup_class() for the class\n+\t * returns NULL.\n+\t */\n+\tmight_sleep();\n+\tsynchronize_rcu();\n+}\n+\n+static int sub_id(struct dept_map *m, int e)\n+{\n+\treturn (m ? m-\u003esub_u : 0) + e * DEPT_MAX_SUBCLASSES_USR;\n+}\n+\n+static struct dept_class *check_new_class(struct dept_key *local,\n+\t\t\t\t\t  struct dept_key *k, int sub_id,\n+\t\t\t\t\t  const char *n, bool sched_map)\n+{\n+\tstruct dept_class *c = NULL;\n+\n+\tif (DEPT_WARN_ON(sub_id \u003e= DEPT_MAX_SUBCLASSES))\n+\t\treturn NULL;\n+\n+\tif (DEPT_WARN_ON(!k))\n+\t\treturn NULL;\n+\n+\t/*\n+\t * XXX: Assume that users prevent the map from using if any of\n+\t * the cached keys has been invalidated. If not, the cache,\n+\t * local-\u003eclasses should not be used because it would be racy\n+\t * with class deletion.\n+\t */\n+\tif (local \u0026\u0026 sub_id \u003c DEPT_MAX_SUBCLASSES_CACHE)\n+\t\tc = READ_ONCE(local-\u003eclasses[sub_id]);\n+\n+\tif (c)\n+\t\treturn c;\n+\n+\tc = lookup_class((unsigned long)k-\u003ebase + sub_id);\n+\tif (c)\n+\t\tgoto caching;\n+\n+\tif (unlikely(!dept_lock()))\n+\t\treturn NULL;\n+\n+\tc = lookup_class((unsigned long)k-\u003ebase + sub_id);\n+\tif (unlikely(c))\n+\t\tgoto unlock;\n+\n+\tc = new_class();\n+\tif (unlikely(!c))\n+\t\tgoto unlock;\n+\n+\tc-\u003ename = n;\n+\tc-\u003esched_map = sched_map;\n+\tc-\u003esub_id = sub_id;\n+\tc-\u003ekey = (unsigned long)(k-\u003ebase + sub_id);\n+\thash_add_class(c);\n+\tlist_add(\u0026c-\u003eall_node, \u0026dept_classes);\n+unlock:\n+\tdept_unlock();\n+caching:\n+\tif (local \u0026\u0026 sub_id \u003c DEPT_MAX_SUBCLASSES_CACHE)\n+\t\tWRITE_ONCE(local-\u003eclasses[sub_id], c);\n+\n+\treturn c;\n+}\n+\n+/*\n+ * Called between dept_enter() and dept_exit().\n+ */\n+static void __dept_wait(struct dept_map *m, unsigned long w_f,\n+\t\t\tunsigned long ip, const char *w_fn, int sub_l,\n+\t\t\tbool sched_sleep, bool sched_map, bool timeout)\n+{\n+\tint e;\n+\n+\t/*\n+\t * Be as conservative as possible. In case of multiple waits for\n+\t * a single dept_map, we are going to keep only the last wait's\n+\t * wgen for simplicity - keeping all wgens seems overengineering.\n+\t *\n+\t * Of course, it might cause missing some dependencies that\n+\t * would rarely, probably never, happen but it helps avoid\n+\t * false positive reports.\n+\t */\n+\tfor_each_set_bit(e, \u0026w_f, DEPT_MAX_SUBCLASSES_EVT) {\n+\t\tstruct dept_class *c;\n+\t\tstruct dept_key *k;\n+\n+\t\tk = m-\u003ekeys ?: \u0026m-\u003emap_key;\n+\t\tc = check_new_class(\u0026m-\u003emap_key, k,\n+\t\t\t\t    sub_id(m, e), m-\u003ename, sched_map);\n+\t\tif (!c)\n+\t\t\tcontinue;\n+\n+\t\tadd_wait(m, c, ip, w_fn, sub_l, sched_sleep, timeout);\n+\t}\n+}\n+\n+/*\n+ * Called between dept_enter() and dept_exit().\n+ */\n+static void __dept_event(struct dept_map *m, struct dept_map *real_m,\n+\t\tunsigned long e_f, unsigned long ip, const char *e_fn,\n+\t\tbool sched_map, unsigned int wg,\n+\t\tstruct dept_stack *ewait_stack)\n+{\n+\tstruct dept_class *c;\n+\tstruct dept_key *k;\n+\tint e;\n+\n+\te = find_first_bit(\u0026e_f, DEPT_MAX_SUBCLASSES_EVT);\n+\n+\tif (DEPT_WARN_ON(e \u003e= DEPT_MAX_SUBCLASSES_EVT))\n+\t\treturn;\n+\n+\t/*\n+\t * An event is an event. If the caller passed more than single\n+\t * event, then warn it and handle the event corresponding to\n+\t * the first bit anyway.\n+\t */\n+\tDEPT_WARN_ON(1UL \u003c\u003c e != e_f);\n+\n+\tk = m-\u003ekeys ?: \u0026m-\u003emap_key;\n+\tc = check_new_class(\u0026m-\u003emap_key, k, sub_id(m, e), m-\u003ename, sched_map);\n+\n+\tif (c)\n+\t\tdo_event(m, real_m, c, wg, ip, e_fn, ewait_stack);\n+}\n+\n+void dept_wait(struct dept_map *m, unsigned long w_f,\n+\t       unsigned long ip, const char *w_fn, int sub_l,\n+\t       long timeoutval)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tunsigned long flags;\n+\tbool timeout;\n+\n+\tif (unlikely(!dept_working()))\n+\t\treturn;\n+\n+\ttimeout = timeoutval \u003e 0 \u0026\u0026 timeoutval \u003c MAX_SCHEDULE_TIMEOUT;\n+\n+#if !defined(CONFIG_DEPT_AGGRESSIVE_TIMEOUT_WAIT)\n+\tif (timeout)\n+\t\treturn;\n+#endif\n+\n+\tif (dt-\u003erecursive)\n+\t\treturn;\n+\n+\tif (m-\u003enocheck)\n+\t\treturn;\n+\n+\tflags = dept_enter();\n+\n+\t__dept_wait(m, w_f, ip, w_fn, sub_l, false, false, timeout);\n+\n+\tdept_exit(flags);\n+}\n+EXPORT_SYMBOL_GPL(dept_wait);\n+\n+void dept_stage_wait(struct dept_map *m, struct dept_key *k,\n+\t\t     unsigned long ip, const char *w_fn,\n+\t\t     long timeoutval)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tunsigned long flags;\n+\tbool timeout;\n+\n+\tif (unlikely(!dept_working()))\n+\t\treturn;\n+\n+\ttimeout = timeoutval \u003e 0 \u0026\u0026 timeoutval \u003c MAX_SCHEDULE_TIMEOUT;\n+\n+#if !defined(CONFIG_DEPT_AGGRESSIVE_TIMEOUT_WAIT)\n+\tif (timeout)\n+\t\treturn;\n+#endif\n+\n+\tif (m \u0026\u0026 m-\u003enocheck)\n+\t\treturn;\n+\n+\t/*\n+\t * Either m or k should be passed. Which means Dept relies on\n+\t * either its own map or the caller's position in the code when\n+\t * determining its class.\n+\t */\n+\tif (DEPT_WARN_ON(!m \u0026\u0026 !k))\n+\t\treturn;\n+\n+\t/*\n+\t * Allow recursive entrance.\n+\t */\n+\tflags = dept_enter_recursive();\n+\n+\t/*\n+\t * Ensure the outmost dept_stage_wait() works.\n+\t */\n+\tif (dt-\u003estage_m.keys)\n+\t\tgoto exit;\n+\n+\tarch_spin_lock(\u0026dt-\u003estage_lock);\n+\tif (m) {\n+\t\tdt-\u003estage_m = *m;\n+\t\tdt-\u003estage_real_m = m;\n+\n+\t\t/*\n+\t\t * Ensure dt-\u003estage_m.keys != NULL and it works with the\n+\t\t * map's map_key, not stage_m's one when -\u003ekeys == NULL.\n+\t\t */\n+\t\tif (!m-\u003ekeys)\n+\t\t\tdt-\u003estage_m.keys = \u0026m-\u003emap_key;\n+\t} else {\n+\t\tdt-\u003estage_m.name = w_fn;\n+\t\tdt-\u003estage_sched_map = true;\n+\t\tdt-\u003estage_real_m = \u0026dt-\u003estage_m;\n+\t}\n+\n+\t/*\n+\t * dept_map_reinit() includes WRITE_ONCE(-\u003ewgen, 0U) that\n+\t * effectively disables the map just in case real sleep won't\n+\t * happen. dept_request_event_wait_commit() will enable it.\n+\t */\n+\tdept_map_reinit(\u0026dt-\u003estage_m, k, -1, NULL);\n+\n+\tdt-\u003estage_w_fn = w_fn;\n+\tdt-\u003estage_ip = ip;\n+\tdt-\u003estage_timeout = timeout;\n+\tarch_spin_unlock(\u0026dt-\u003estage_lock);\n+exit:\n+\tdept_exit_recursive(flags);\n+}\n+EXPORT_SYMBOL_GPL(dept_stage_wait);\n+\n+static void __dept_clean_stage(struct dept_task *dt)\n+{\n+\tmemset(\u0026dt-\u003estage_m, 0x0, sizeof(struct dept_map));\n+\tdt-\u003estage_real_m = NULL;\n+\tdt-\u003estage_sched_map = false;\n+\tdt-\u003estage_w_fn = NULL;\n+\tdt-\u003estage_ip = 0UL;\n+\tdt-\u003estage_timeout = false;\n+\tif (dt-\u003estage_wait_stack)\n+\t\tput_stack(dt-\u003estage_wait_stack);\n+\tdt-\u003estage_wait_stack = NULL;\n+}\n+\n+void dept_clean_stage(void)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tunsigned long flags;\n+\n+\tif (unlikely(!dept_working()))\n+\t\treturn;\n+\n+\t/*\n+\t * Allow recursive entrance.\n+\t */\n+\tflags = dept_enter_recursive();\n+\tarch_spin_lock(\u0026dt-\u003estage_lock);\n+\t__dept_clean_stage(dt);\n+\tarch_spin_unlock(\u0026dt-\u003estage_lock);\n+\tdept_exit_recursive(flags);\n+}\n+EXPORT_SYMBOL_GPL(dept_clean_stage);\n+\n+/*\n+ * Always called from __schedule().\n+ */\n+void dept_request_event_wait_commit(void)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tunsigned long flags;\n+\tunsigned int wg;\n+\tunsigned long ip;\n+\tconst char *w_fn;\n+\tbool sched_map;\n+\tbool timeout;\n+\n+\tif (unlikely(!dept_working()))\n+\t\treturn;\n+\n+\t/*\n+\t * It's impossible that __schedule() is called while Dept is\n+\t * working that already disabled IRQ at the entrance.\n+\t */\n+\tif (DEPT_WARN_ON(dt-\u003erecursive))\n+\t\treturn;\n+\n+\tflags = dept_enter();\n+\n+\tarch_spin_lock(\u0026dt-\u003estage_lock);\n+\n+\t/*\n+\t * Checks if current has staged a wait.\n+\t */\n+\tif (!dt-\u003estage_m.keys) {\n+\t\tarch_spin_unlock(\u0026dt-\u003estage_lock);\n+\t\tgoto exit;\n+\t}\n+\n+\tw_fn = dt-\u003estage_w_fn;\n+\tip = dt-\u003estage_ip;\n+\tsched_map = dt-\u003estage_sched_map;\n+\ttimeout = dt-\u003estage_timeout;\n+\n+\twg = next_wgen();\n+\tWRITE_ONCE(dt-\u003estage_m.wgen, wg);\n+\n+\t/*\n+\t * __schedule() can be hit multiple times between\n+\t * dept_stage_wait() and dept_clean_stage().  In that case,\n+\t * keep the first stacktrace only.  That's enough.\n+\t */\n+\tif (!dt-\u003estage_wait_stack)\n+\t\tdt-\u003estage_wait_stack = get_current_stack();\n+\tarch_spin_unlock(\u0026dt-\u003estage_lock);\n+\n+\t__dept_wait(\u0026dt-\u003estage_m, 1UL, ip, w_fn, 0, true, sched_map, timeout);\n+exit:\n+\tdept_exit(flags);\n+}\n+\n+/*\n+ * Always called from try_to_wake_up().\n+ */\n+void dept_ttwu_stage_wait(struct task_struct *requestor, unsigned long ip)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tstruct dept_task *dt_req = \u0026requestor-\u003edept_task;\n+\tunsigned long flags;\n+\tstruct dept_map m;\n+\tstruct dept_map *real_m;\n+\tbool sched_map;\n+\tstruct dept_stack *ewait_stack;\n+\n+\tif (unlikely(!dept_working()))\n+\t\treturn;\n+\n+\tif (dt-\u003erecursive)\n+\t\treturn;\n+\n+\tflags = dept_enter();\n+\n+\tarch_spin_lock(\u0026dt_req-\u003estage_lock);\n+\n+\t/*\n+\t * Serializing is unnecessary as long as it always comes from\n+\t * try_to_wake_up().\n+\t */\n+\tm = dt_req-\u003estage_m;\n+\tsched_map = dt_req-\u003estage_sched_map;\n+\treal_m = dt_req-\u003estage_real_m;\n+\tewait_stack = dt_req-\u003estage_wait_stack;\n+\tif (ewait_stack)\n+\t\tget_stack(ewait_stack);\n+\n+\t__dept_clean_stage(dt_req);\n+\tarch_spin_unlock(\u0026dt_req-\u003estage_lock);\n+\n+\t/*\n+\t * -\u003estage_m.keys should not be NULL if it's in use. Should\n+\t * make sure that it's not NULL when staging a valid map.\n+\t */\n+\tif (!m.keys)\n+\t\tgoto exit;\n+\n+\t__dept_event(\u0026m, real_m, 1UL, ip, \"try_to_wake_up\", sched_map,\n+\t\t\tm.wgen, ewait_stack);\n+exit:\n+\tif (ewait_stack)\n+\t\tput_stack(ewait_stack);\n+\n+\tdept_exit(flags);\n+}\n+\n+/*\n+ * Modifies the latest ecxt corresponding to m and e_f.\n+ */\n+void dept_map_ecxt_modify(struct dept_map *m, unsigned long e_f,\n+\t\t\t  struct dept_key *new_k, unsigned long new_e_f,\n+\t\t\t  unsigned long new_ip, const char *new_c_fn,\n+\t\t\t  const char *new_e_fn, int new_sub_l)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tstruct dept_ecxt_held *eh;\n+\tstruct dept_class *c;\n+\tstruct dept_key *k;\n+\tunsigned long flags;\n+\tint pos = -1;\n+\tint new_e;\n+\tint e;\n+\n+\tif (unlikely(!dept_working()))\n+\t\treturn;\n+\n+\t/*\n+\t * XXX: Couldn't handle re-enterance cases. Ignore it for now.\n+\t */\n+\tif (dt-\u003erecursive)\n+\t\treturn;\n+\n+\t/*\n+\t * Should go ahead no matter whether -\u003enocheck == true or not\n+\t * because -\u003enocheck value can be changed within the ecxt area\n+\t * delimitated by dept_ecxt_enter() and dept_ecxt_exit().\n+\t */\n+\n+\tflags = dept_enter();\n+\n+\tfor_each_set_bit(e, \u0026e_f, DEPT_MAX_SUBCLASSES_EVT) {\n+\t\tk = m-\u003ekeys ?: \u0026m-\u003emap_key;\n+\t\tc = check_new_class(\u0026m-\u003emap_key, k,\n+\t\t\t\t    sub_id(m, e), m-\u003ename, false);\n+\t\tif (!c)\n+\t\t\tcontinue;\n+\n+\t\t/*\n+\t\t * When it found an ecxt for any event in e_f, done.\n+\t\t */\n+\t\tpos = find_ecxt_pos(m, c, true);\n+\t\tif (pos != -1)\n+\t\t\tbreak;\n+\t}\n+\n+\tif (unlikely(pos == -1))\n+\t\tgoto exit;\n+\n+\teh = dt-\u003eecxt_held + pos;\n+\tnew_sub_l = new_sub_l \u003e= 0 ? new_sub_l : eh-\u003esub_l;\n+\n+\tnew_e = find_first_bit(\u0026new_e_f, DEPT_MAX_SUBCLASSES_EVT);\n+\n+\tif (new_e \u003c DEPT_MAX_SUBCLASSES_EVT)\n+\t\t/*\n+\t\t * Let it work with the first bit anyway.\n+\t\t */\n+\t\tDEPT_WARN_ON(1UL \u003c\u003c new_e != new_e_f);\n+\telse\n+\t\tnew_e = e;\n+\n+\tpop_ecxt(m, c);\n+\n+\t/*\n+\t * Apply the key to the map.\n+\t */\n+\tif (new_k)\n+\t\tdept_map_reinit(m, new_k, -1, NULL);\n+\n+\tk = m-\u003ekeys ?: \u0026m-\u003emap_key;\n+\tc = check_new_class(\u0026m-\u003emap_key, k, sub_id(m, new_e), m-\u003ename, false);\n+\n+\tif (c \u0026\u0026 add_ecxt(m, c, new_ip, new_c_fn, new_e_fn, new_sub_l, NULL))\n+\t\tgoto exit;\n+\n+\t/*\n+\t * Successfully pop_ecxt()ed but failed to add_ecxt().\n+\t */\n+\tdt-\u003emissing_ecxt++;\n+exit:\n+\tdept_exit(flags);\n+}\n+EXPORT_SYMBOL_GPL(dept_map_ecxt_modify);\n+\n+void dept_ecxt_enter(struct dept_map *m, unsigned long e_f, unsigned long ip,\n+\t\t     const char *c_fn, const char *e_fn, int sub_l)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tunsigned long flags;\n+\tstruct dept_class *c;\n+\tstruct dept_key *k;\n+\tint e;\n+\n+\tif (unlikely(!dept_working()))\n+\t\treturn;\n+\n+\tif (dt-\u003erecursive) {\n+\t\tdt-\u003emissing_ecxt++;\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * Should go ahead no matter whether -\u003enocheck == true or not\n+\t * because -\u003enocheck value can be changed within the ecxt area\n+\t * delimitated by dept_ecxt_enter() and dept_ecxt_exit().\n+\t */\n+\n+\tflags = dept_enter();\n+\n+\te = find_first_bit(\u0026e_f, DEPT_MAX_SUBCLASSES_EVT);\n+\n+\tif (e \u003e= DEPT_MAX_SUBCLASSES_EVT)\n+\t\tgoto missing_ecxt;\n+\n+\t/*\n+\t * An event is an event. If the caller passed more than single\n+\t * event, then warn it and handle the event corresponding to\n+\t * the first bit anyway.\n+\t */\n+\tDEPT_WARN_ON(1UL \u003c\u003c e != e_f);\n+\n+\tk = m-\u003ekeys ?: \u0026m-\u003emap_key;\n+\tc = check_new_class(\u0026m-\u003emap_key, k, sub_id(m, e), m-\u003ename, false);\n+\n+\tif (c \u0026\u0026 add_ecxt(m, c, ip, c_fn, e_fn, sub_l, NULL))\n+\t\tgoto exit;\n+missing_ecxt:\n+\tdt-\u003emissing_ecxt++;\n+exit:\n+\tdept_exit(flags);\n+}\n+EXPORT_SYMBOL_GPL(dept_ecxt_enter);\n+\n+bool dept_ecxt_holding(struct dept_map *m, unsigned long e_f)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tunsigned long flags;\n+\tbool ret = false;\n+\tint e;\n+\n+\tif (unlikely(!dept_working()))\n+\t\treturn false;\n+\n+\tif (dt-\u003erecursive)\n+\t\treturn false;\n+\n+\tflags = dept_enter();\n+\n+\tfor_each_set_bit(e, \u0026e_f, DEPT_MAX_SUBCLASSES_EVT) {\n+\t\tstruct dept_class *c;\n+\t\tstruct dept_key *k;\n+\n+\t\tk = m-\u003ekeys ?: \u0026m-\u003emap_key;\n+\t\tc = check_new_class(\u0026m-\u003emap_key, k,\n+\t\t\t\t    sub_id(m, e), m-\u003ename, false);\n+\t\tif (!c)\n+\t\t\tcontinue;\n+\n+\t\tif (find_ecxt_pos(m, c, true) != -1) {\n+\t\t\tret = true;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tdept_exit(flags);\n+\n+\treturn ret;\n+}\n+EXPORT_SYMBOL_GPL(dept_ecxt_holding);\n+\n+void dept_request_event(struct dept_map *m, struct dept_ext_wgen *ewg)\n+{\n+\tunsigned long flags;\n+\tunsigned int wg;\n+\tunsigned int *wg_p;\n+\n+\tif (unlikely(!dept_working()))\n+\t\treturn;\n+\n+\tif (m-\u003enocheck)\n+\t\treturn;\n+\n+\t/*\n+\t * Allow recursive entrance.\n+\t */\n+\tflags = dept_enter_recursive();\n+\n+\twg_p = ewg ? \u0026ewg-\u003ewgen : \u0026m-\u003ewgen;\n+\n+\twg = next_wgen();\n+\tWRITE_ONCE(*wg_p, wg);\n+\n+\tdept_exit_recursive(flags);\n+}\n+EXPORT_SYMBOL_GPL(dept_request_event);\n+\n+void dept_event(struct dept_map *m, unsigned long e_f,\n+\t\tunsigned long ip, const char *e_fn,\n+\t\tstruct dept_ext_wgen *ewg)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tunsigned long flags;\n+\tunsigned int *wg_p;\n+\n+\tif (unlikely(!dept_working()))\n+\t\treturn;\n+\n+\tif (m-\u003enocheck)\n+\t\treturn;\n+\n+\twg_p = ewg ? \u0026ewg-\u003ewgen : \u0026m-\u003ewgen;\n+\n+\tif (dt-\u003erecursive) {\n+\t\t/*\n+\t\t * Dept won't work with this even though an event\n+\t\t * context has been asked. Don't make it confused at\n+\t\t * handling the event. Disable it until the next.\n+\t\t */\n+\t\tWRITE_ONCE(*wg_p, 0U);\n+\t\treturn;\n+\t}\n+\n+\tflags = dept_enter();\n+\n+\t__dept_event(m, m, e_f, ip, e_fn, false, READ_ONCE(*wg_p), NULL);\n+\n+\t/*\n+\t * Keep the map diabled until the next sleep.\n+\t */\n+\tWRITE_ONCE(*wg_p, 0U);\n+\n+\tdept_exit(flags);\n+}\n+EXPORT_SYMBOL_GPL(dept_event);\n+\n+void dept_ecxt_exit(struct dept_map *m, unsigned long e_f,\n+\t\t    unsigned long ip)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tunsigned long flags;\n+\tint e;\n+\n+\tif (unlikely(!dept_working()))\n+\t\treturn;\n+\n+\tif (dt-\u003erecursive) {\n+\t\tdt-\u003emissing_ecxt--;\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * Should go ahead no matter whether -\u003enocheck == true or not\n+\t * because -\u003enocheck value can be changed within the ecxt area\n+\t * delimitated by dept_ecxt_enter() and dept_ecxt_exit().\n+\t */\n+\n+\tflags = dept_enter();\n+\n+\tfor_each_set_bit(e, \u0026e_f, DEPT_MAX_SUBCLASSES_EVT) {\n+\t\tstruct dept_class *c;\n+\t\tstruct dept_key *k;\n+\n+\t\tk = m-\u003ekeys ?: \u0026m-\u003emap_key;\n+\t\tc = check_new_class(\u0026m-\u003emap_key, k,\n+\t\t\t\t    sub_id(m, e), m-\u003ename, false);\n+\t\tif (!c)\n+\t\t\tcontinue;\n+\n+\t\t/*\n+\t\t * When it found an ecxt for any event in e_f, done.\n+\t\t */\n+\t\tif (pop_ecxt(m, c))\n+\t\t\tgoto exit;\n+\t}\n+\n+\tdt-\u003emissing_ecxt--;\n+exit:\n+\tdept_exit(flags);\n+}\n+EXPORT_SYMBOL_GPL(dept_ecxt_exit);\n+\n+void dept_task_exit(struct task_struct *t)\n+{\n+\tstruct dept_task *dt = \u0026t-\u003edept_task;\n+\tint i;\n+\n+\tif (unlikely(!dept_working()))\n+\t\treturn;\n+\n+\traw_local_irq_disable();\n+\n+\tif (dt-\u003estack) {\n+\t\tput_stack(dt-\u003estack);\n+\t\tdt-\u003estack = NULL;\n+\t}\n+\n+\tif (dt-\u003estage_wait_stack) {\n+\t\tput_stack(dt-\u003estage_wait_stack);\n+\t\tdt-\u003estage_wait_stack = NULL;\n+\t}\n+\n+\tfor (i = 0; i \u003c dt-\u003eecxt_held_pos; i++) {\n+\t\tif (dt-\u003eecxt_held[i].class) {\n+\t\t\tput_class(dt-\u003eecxt_held[i].class);\n+\t\t\tdt-\u003eecxt_held[i].class = NULL;\n+\t\t}\n+\t\tif (dt-\u003eecxt_held[i].ecxt) {\n+\t\t\tput_ecxt(dt-\u003eecxt_held[i].ecxt);\n+\t\t\tdt-\u003eecxt_held[i].ecxt = NULL;\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i \u003c DEPT_MAX_WAIT_HIST; i++) {\n+\t\tif (dt-\u003ewait_hist[i].wait) {\n+\t\t\tput_wait(dt-\u003ewait_hist[i].wait);\n+\t\t\tdt-\u003ewait_hist[i].wait = NULL;\n+\t\t}\n+\t}\n+\n+\tdt-\u003etask_exit = true;\n+\tdept_off();\n+\n+\traw_local_irq_enable();\n+}\n+\n+void dept_task_init(struct task_struct *t)\n+{\n+\tmemset(\u0026t-\u003edept_task, 0x0, sizeof(struct dept_task));\n+\tt-\u003edept_task.stage_lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;\n+}\n+\n+void dept_key_init(struct dept_key *k)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tunsigned long flags;\n+\tint sub_id;\n+\n+\tif (unlikely(!dept_working()))\n+\t\treturn;\n+\n+\tif (dt-\u003erecursive) {\n+\t\tDEPT_STOP(\"Key initialization fails.\\n\");\n+\t\treturn;\n+\t}\n+\n+\tflags = dept_enter();\n+\n+\tclean_classes_cache(k);\n+\n+\t/*\n+\t * dept_key_init() should not fail.\n+\t *\n+\t * FIXME: Should be fixed if dept_key_init() causes deadlock\n+\t * with dept_lock().\n+\t */\n+\twhile (unlikely(!dept_lock()))\n+\t\tcpu_relax();\n+\n+\tfor (sub_id = 0; sub_id \u003c DEPT_MAX_SUBCLASSES; sub_id++) {\n+\t\tstruct dept_class *c;\n+\n+\t\tc = lookup_class((unsigned long)k-\u003ebase + sub_id);\n+\t\tif (!c)\n+\t\t\tcontinue;\n+\n+\t\tDEPT_STOP(\"The class(%s/%d) has not been removed.\\n\",\n+\t\t\t  c-\u003ename, sub_id);\n+\t\tbreak;\n+\t}\n+\n+\tdept_unlock();\n+\tdept_exit(flags);\n+}\n+EXPORT_SYMBOL_GPL(dept_key_init);\n+\n+void dept_key_destroy(struct dept_key *k)\n+{\n+\tstruct dept_task *dt = dept_task();\n+\tunsigned long flags;\n+\tint sub_id;\n+\n+\tif (unlikely(!dept_working()))\n+\t\treturn;\n+\n+\tif (dt-\u003erecursive == 1 \u0026\u0026 dt-\u003etask_exit) {\n+\t\t/*\n+\t\t * Need to allow to go ahead in this case where\n+\t\t * -\u003erecursive has been set to 1 by dept_off() in\n+\t\t * dept_task_exit() and -\u003etask_exit has been set to\n+\t\t * true in dept_task_exit().\n+\t\t */\n+\t} else if (dt-\u003erecursive) {\n+\t\tDEPT_STOP(\"Key destroying fails.\\n\");\n+\t\treturn;\n+\t}\n+\n+\tflags = dept_enter();\n+\n+\t/*\n+\t * dept_key_destroy() should not fail.\n+\t *\n+\t * FIXME: Should be fixed if dept_key_destroy() causes deadlock\n+\t * with dept_lock().\n+\t */\n+\twhile (unlikely(!dept_lock()))\n+\t\tcpu_relax();\n+\n+\tfor (sub_id = 0; sub_id \u003c DEPT_MAX_SUBCLASSES; sub_id++) {\n+\t\tstruct dept_class *c;\n+\n+\t\tc = lookup_class((unsigned long)k-\u003ebase + sub_id);\n+\t\tif (!c)\n+\t\t\tcontinue;\n+\n+\t\thash_del_class(c);\n+\t\tdisconnect_class(c);\n+\t\tlist_del(\u0026c-\u003eall_node);\n+\t\tinvalidate_class(c);\n+\n+\t\t/*\n+\t\t * Actual deletion will happen on the rcu callback\n+\t\t * that has been added in disconnect_class().\n+\t\t */\n+\t\tdel_class(c);\n+\t}\n+\n+\tdept_unlock();\n+\tdept_exit(flags);\n+\n+\t/*\n+\t * Wait until even lockless hash_lookup_class() for the class\n+\t * returns NULL.\n+\t */\n+\tmight_sleep();\n+\tsynchronize_rcu();\n+}\n+EXPORT_SYMBOL_GPL(dept_key_destroy);\n+\n+static void move_llist(struct llist_head *to, struct llist_head *from)\n+{\n+\tstruct llist_node *first = llist_del_all(from);\n+\tstruct llist_node *last = first;\n+\n+\tif (!first)\n+\t\treturn;\n+\n+\twhile (llist_next(last))\n+\t\tlast = llist_next(last);\n+\tllist_add_batch(first, last, to);\n+}\n+\n+static void migrate_per_cpu_pool(void)\n+{\n+\tconst int boot_cpu = 0;\n+\tint i;\n+\n+\t/*\n+\t * The boot CPU has been using the temporal local pool so far.\n+\t * From now on that per_cpu areas have been ready, use the\n+\t * per_cpu local pool instead.\n+\t */\n+\tDEPT_WARN_ON(smp_processor_id() != boot_cpu);\n+\tfor (i = 0; i \u003c OBJECT_NR; i++) {\n+\t\tstruct llist_head *from;\n+\t\tstruct llist_head *to;\n+\n+\t\tfrom = \u0026dept_pool[i].boot_pool;\n+\t\tto = per_cpu_ptr(dept_pool[i].lpool, boot_cpu);\n+\t\tmove_llist(to, from);\n+\t}\n+}\n+\n+#define B2KB(B) ((B) / 1024)\n+\n+/*\n+ * Should be called after setup_per_cpu_areas() and before no non-boot\n+ * CPUs have been on.\n+ */\n+void __init dept_init(void)\n+{\n+\tsize_t mem_total = 0;\n+\n+\tlocal_irq_disable();\n+\tdept_per_cpu_ready = 1;\n+\tmigrate_per_cpu_pool();\n+\tlocal_irq_enable();\n+\n+#define HASH(id, bits) BUILD_BUG_ON(1 \u003c\u003c (bits) \u003c= 0);\n+\t#include \"dept_hash.h\"\n+#undef HASH\n+#define OBJECT(id, nr) mem_total += sizeof(struct dept_##id) * nr;\n+\t#include \"dept_object.h\"\n+#undef OBJECT\n+#define HASH(id, bits) mem_total += sizeof(struct hlist_head) * (1 \u003c\u003c (bits));\n+\t#include \"dept_hash.h\"\n+#undef HASH\n+\n+\tpr_info(\"DEPendency Tracker: Copyright (c) 2020 LG Electronics, Inc., Byungchul Park\\n\");\n+\tpr_info(\"... DEPT_MAX_STACK_ENTRY: %d\\n\", DEPT_MAX_STACK_ENTRY);\n+\tpr_info(\"... DEPT_MAX_WAIT_HIST  : %d\\n\", DEPT_MAX_WAIT_HIST);\n+\tpr_info(\"... DEPT_MAX_ECXT_HELD  : %d\\n\", DEPT_MAX_ECXT_HELD);\n+\tpr_info(\"... DEPT_MAX_SUBCLASSES : %d\\n\", DEPT_MAX_SUBCLASSES);\n+#define OBJECT(id, nr)\t\t\t\t\t\t\t\\\n+\tpr_info(\"... memory initially used by %s: %zu KB\\n\",\t\t\\\n+\t       #id, B2KB(sizeof(spool_##id) + sizeof(rpool_##id)));\n+\t#include \"dept_object.h\"\n+#undef OBJECT\n+#define HASH(id, bits)\t\t\t\t\t\t\t\\\n+\tpr_info(\"... hash list head used by %s: %zu KB\\n\",\t\t\\\n+\t       #id, B2KB(sizeof(struct hlist_head) * (1 \u003c\u003c (bits))));\n+\t#include \"dept_hash.h\"\n+#undef HASH\n+\tpr_info(\"... total memory initially used by objects and hashs: %zu KB\\n\", B2KB(mem_total));\n+\tpr_info(\"... per task memory footprint: %zu bytes\\n\", sizeof(struct dept_task));\n+}\ndiff --git a/kernel/dependency/dept_hash.h b/kernel/dependency/dept_hash.h\nnew file mode 100644\nindex 00000000000000..fd85aab1fdfbe0\n--- /dev/null\n+++ b/kernel/dependency/dept_hash.h\n@@ -0,0 +1,10 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * HASH(id, bits)\n+ *\n+ * id  : Id for the object of struct dept_##id.\n+ * bits: 1UL \u003c\u003c bits is the hash table size.\n+ */\n+\n+HASH(dep, 12)\n+HASH(class, 12)\ndiff --git a/kernel/dependency/dept_internal.h b/kernel/dependency/dept_internal.h\nnew file mode 100644\nindex 00000000000000..c02783ecf0c4d5\n--- /dev/null\n+++ b/kernel/dependency/dept_internal.h\n@@ -0,0 +1,314 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * DEPT(DEPendency Tracker) - runtime dependency tracker internal header\n+ *\n+ * Started by Byungchul Park \u003cmax.byungchul.park@gmail.com\u003e:\n+ *\n+ *  Copyright (c) 2020 LG Electronics, Inc., Byungchul Park\n+ *  Copyright (c) 2024 SK hynix, Inc., Byungchul Park\n+ */\n+\n+#ifndef __DEPT_INTERNAL_H\n+#define __DEPT_INTERNAL_H\n+\n+#ifdef CONFIG_DEPT\n+#include \u003clinux/dept.h\u003e\n+#include \u003clinux/percpu.h\u003e\n+#include \u003clinux/llist.h\u003e\n+#include \u003clinux/types.h\u003e\n+\n+struct dept_pool {\n+\tconst char\t\t\t*name;\n+\n+\t/*\n+\t * object size\n+\t */\n+\tsize_t\t\t\t\tobj_sz;\n+\n+\t/*\n+\t * the remaining number of the object in spool\n+\t */\n+\tint\t\t\t\tobj_nr;\n+\n+\t/*\n+\t * the number of the object in spool\n+\t */\n+\tint\t\t\t\ttot_nr;\n+\n+\t/*\n+\t * accumulated amount of memory used by the object in byte\n+\t */\n+\tatomic_t\t\t\tacc_sz;\n+\n+\t/*\n+\t * offset of -\u003epool_node\n+\t */\n+\tsize_t\t\t\t\tnode_off;\n+\n+\t/*\n+\t * pointer to the pool\n+\t */\n+\tvoid\t\t\t\t*spool; /* static pool */\n+\tvoid\t\t\t\t*rpool; /* reserved pool */\n+\tstruct llist_head\t\tboot_pool;\n+\tstruct llist_head __percpu\t*lpool; /* local pool */\n+};\n+\n+struct dept_ecxt;\n+struct dept_iecxt {\n+\tstruct dept_ecxt\t\t*ecxt;\n+\tint\t\t\t\tenirq;\n+\t/*\n+\t * flag to prevent adding a new ecxt\n+\t */\n+\tbool\t\t\t\tstaled;\n+};\n+\n+struct dept_wait;\n+struct dept_iwait {\n+\tstruct dept_wait\t\t*wait;\n+\tint\t\t\t\tirq;\n+\t/*\n+\t * flag to prevent adding a new wait\n+\t */\n+\tbool\t\t\t\tstaled;\n+\tbool\t\t\t\ttouched;\n+};\n+\n+struct dept_class {\n+\tunion {\n+\t\tstruct llist_node\tpool_node;\n+\t\tstruct {\n+\t\t\t/*\n+\t\t\t * reference counter for object management\n+\t\t\t */\n+\t\t\tatomic_t\tref;\n+\n+\t\t\t/*\n+\t\t\t * unique information about the class\n+\t\t\t */\n+\t\t\tconst char\t*name;\n+\t\t\tunsigned long\tkey;\n+\t\t\tint\t\tsub_id;\n+\n+\t\t\t/*\n+\t\t\t * for BFS\n+\t\t\t */\n+\t\t\tunsigned int\tbfs_gen;\n+\t\t\tstruct dept_class *bfs_parent;\n+\t\t\tstruct list_head bfs_node;\n+\n+\t\t\t/*\n+\t\t\t * for hashing this object\n+\t\t\t */\n+\t\t\tstruct hlist_node hash_node;\n+\n+\t\t\t/*\n+\t\t\t * for linking all classes\n+\t\t\t */\n+\t\t\tstruct list_head all_node;\n+\n+\t\t\t/*\n+\t\t\t * for associating its dependencies\n+\t\t\t */\n+\t\t\tstruct list_head dep_head;\n+\t\t\tstruct list_head dep_rev_head;\n+\n+\t\t\t/*\n+\t\t\t * for tracking IRQ dependencies\n+\t\t\t */\n+\t\t\tstruct dept_iecxt iecxt[DEPT_CXT_IRQS_NR];\n+\t\t\tstruct dept_iwait iwait[DEPT_CXT_IRQS_NR];\n+\n+\t\t\t/*\n+\t\t\t * classified by a map embedded in task_struct,\n+\t\t\t * not an explicit map\n+\t\t\t */\n+\t\t\tbool\t\tsched_map;\n+\t\t};\n+\t};\n+};\n+\n+struct dept_stack {\n+\tunion {\n+\t\tstruct llist_node\tpool_node;\n+\t\tstruct {\n+\t\t\t/*\n+\t\t\t * reference counter for object management\n+\t\t\t */\n+\t\t\tatomic_t\tref;\n+\n+\t\t\t/*\n+\t\t\t * backtrace entries\n+\t\t\t */\n+\t\t\tunsigned long\traw[DEPT_MAX_STACK_ENTRY];\n+\t\t\tint nr;\n+\t\t};\n+\t};\n+};\n+\n+struct dept_ecxt {\n+\tunion {\n+\t\tstruct llist_node\tpool_node;\n+\t\tstruct {\n+\t\t\t/*\n+\t\t\t * reference counter for object management\n+\t\t\t */\n+\t\t\tatomic_t\tref;\n+\n+\t\t\t/*\n+\t\t\t * function that entered to this ecxt\n+\t\t\t */\n+\t\t\tconst char\t*ecxt_fn;\n+\n+\t\t\t/*\n+\t\t\t * event function\n+\t\t\t */\n+\t\t\tconst char\t*event_fn;\n+\n+\t\t\t/*\n+\t\t\t * associated class\n+\t\t\t */\n+\t\t\tstruct dept_class *class;\n+\n+\t\t\t/*\n+\t\t\t * flag indicating which IRQ has been\n+\t\t\t * enabled within the event context\n+\t\t\t */\n+\t\t\tunsigned long\tenirqf;\n+\n+\t\t\t/*\n+\t\t\t * where the IRQ-enabled happened\n+\t\t\t */\n+\t\t\tunsigned long\tenirq_ip[DEPT_CXT_IRQS_NR];\n+\t\t\tstruct dept_stack *enirq_stack[DEPT_CXT_IRQS_NR];\n+\n+\t\t\t/*\n+\t\t\t * where the event context started\n+\t\t\t */\n+\t\t\tunsigned long\tecxt_ip;\n+\t\t\tstruct dept_stack *ecxt_stack;\n+\n+\t\t\t/*\n+\t\t\t * where the event triggered\n+\t\t\t */\n+\t\t\tunsigned long\tevent_ip;\n+\t\t\tstruct dept_stack *event_stack;\n+\n+\t\t\t/*\n+\t\t\t * wait that this event ttwu\n+\t\t\t */\n+\t\t\tstruct dept_stack *ewait_stack;\n+\t\t};\n+\t};\n+};\n+\n+struct dept_wait {\n+\tunion {\n+\t\tstruct llist_node\tpool_node;\n+\t\tstruct {\n+\t\t\t/*\n+\t\t\t * reference counter for object management\n+\t\t\t */\n+\t\t\tatomic_t\tref;\n+\n+\t\t\t/*\n+\t\t\t * function causing this wait\n+\t\t\t */\n+\t\t\tconst char\t*wait_fn;\n+\n+\t\t\t/*\n+\t\t\t * the associated class\n+\t\t\t */\n+\t\t\tstruct dept_class *class;\n+\n+\t\t\t/*\n+\t\t\t * which IRQ the wait was placed in\n+\t\t\t */\n+\t\t\tunsigned long\tirqf;\n+\n+\t\t\t/*\n+\t\t\t * where the IRQ wait happened\n+\t\t\t */\n+\t\t\tunsigned long\tirq_ip[DEPT_CXT_IRQS_NR];\n+\t\t\tstruct dept_stack *irq_stack[DEPT_CXT_IRQS_NR];\n+\n+\t\t\t/*\n+\t\t\t * where the wait happened\n+\t\t\t */\n+\t\t\tunsigned long\twait_ip;\n+\t\t\tstruct dept_stack *wait_stack;\n+\n+\t\t\t/*\n+\t\t\t * whether this wait is for commit in scheduler\n+\t\t\t */\n+\t\t\tbool\t\tsched_sleep;\n+\n+\t\t\t/*\n+\t\t\t * whether a timeout is set\n+\t\t\t */\n+\t\t\tbool\t\ttimeout;\n+\t\t};\n+\t};\n+};\n+\n+struct dept_dep {\n+\tunion {\n+\t\tstruct llist_node\tpool_node;\n+\t\tstruct {\n+\t\t\t/*\n+\t\t\t * reference counter for object management\n+\t\t\t */\n+\t\t\tatomic_t\tref;\n+\n+\t\t\t/*\n+\t\t\t * key data of dependency\n+\t\t\t */\n+\t\t\tstruct dept_ecxt *ecxt;\n+\t\t\tstruct dept_wait *wait;\n+\n+\t\t\t/*\n+\t\t\t * This object can be referred without dept_lock\n+\t\t\t * held but with IRQ disabled, e.g. for hash\n+\t\t\t * lookup. So deferred deletion is needed.\n+\t\t\t */\n+\t\t\tstruct rcu_head rh;\n+\n+\t\t\t/*\n+\t\t\t * for hashing this object\n+\t\t\t */\n+\t\t\tstruct hlist_node hash_node;\n+\n+\t\t\t/*\n+\t\t\t * for linking to a class object\n+\t\t\t */\n+\t\t\tstruct list_head dep_node;\n+\t\t\tstruct list_head dep_rev_node;\n+\t\t};\n+\t};\n+};\n+\n+struct dept_hash {\n+\t/*\n+\t * hash table\n+\t */\n+\tstruct hlist_head\t\t*table;\n+\n+\t/*\n+\t * size of the table e.i. 2^bits\n+\t */\n+\tint\t\t\t\tbits;\n+};\n+\n+enum object_t {\n+#define OBJECT(id, nr) OBJECT_##id,\n+\t#include \"dept_object.h\"\n+#undef OBJECT\n+\tOBJECT_NR,\n+};\n+\n+extern struct list_head dept_classes;\n+extern struct dept_pool dept_pool[];\n+\n+#endif\n+#endif /* __DEPT_INTERNAL_H */\ndiff --git a/kernel/dependency/dept_object.h b/kernel/dependency/dept_object.h\nnew file mode 100644\nindex 00000000000000..4f936adfa8eef8\n--- /dev/null\n+++ b/kernel/dependency/dept_object.h\n@@ -0,0 +1,13 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * OBJECT(id, nr)\n+ *\n+ * id: Id for the object of struct dept_##id.\n+ * nr: # of the object that should be kept in the pool.\n+ */\n+\n+OBJECT(dep, 1024 * 4 * 2)\n+OBJECT(class, 1024 * 4)\n+OBJECT(stack, 1024 * 4 * 8)\n+OBJECT(ecxt, 1024 * 4 * 2)\n+OBJECT(wait, 1024 * 4 * 4)\ndiff --git a/kernel/dependency/dept_proc.c b/kernel/dependency/dept_proc.c\nnew file mode 100644\nindex 00000000000000..f28992834588a8\n--- /dev/null\n+++ b/kernel/dependency/dept_proc.c\n@@ -0,0 +1,94 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * Procfs knobs for Dept(DEPendency Tracker)\n+ *\n+ * Started by Byungchul Park \u003cmax.byungchul.park@gmail.com\u003e:\n+ *\n+ *  Copyright (C) 2021 LG Electronics, Inc. , Byungchul Park\n+ *  Copyright (C) 2024 SK hynix, Inc. , Byungchul Park\n+ */\n+#include \u003clinux/proc_fs.h\u003e\n+#include \u003clinux/seq_file.h\u003e\n+#include \u003clinux/dept.h\u003e\n+#include \"dept_internal.h\"\n+\n+static void *l_next(struct seq_file *m, void *v, loff_t *pos)\n+{\n+\t/*\n+\t * XXX: Serialize list traversal if needed. The following might\n+\t * give a wrong information on contention.\n+\t */\n+\treturn seq_list_next(v, \u0026dept_classes, pos);\n+}\n+\n+static void *l_start(struct seq_file *m, loff_t *pos)\n+{\n+\t/*\n+\t * XXX: Serialize list traversal if needed. The following might\n+\t * give a wrong information on contention.\n+\t */\n+\treturn seq_list_start_head(\u0026dept_classes, *pos);\n+}\n+\n+static void l_stop(struct seq_file *m, void *v)\n+{\n+}\n+\n+static int l_show(struct seq_file *m, void *v)\n+{\n+\tstruct dept_class *fc = list_entry(v, struct dept_class, all_node);\n+\tstruct dept_dep *d;\n+\tconst char *prefix;\n+\n+\tif (v == \u0026dept_classes) {\n+\t\tseq_puts(m, \"All classes:\\n\\n\");\n+\t\treturn 0;\n+\t}\n+\n+\tprefix = fc-\u003esched_map ? \"\u003csched\u003e \" : \"\";\n+\tseq_printf(m, \"[%p] %s%s\\n\", (void *)fc-\u003ekey, prefix, fc-\u003ename);\n+\n+\t/*\n+\t * XXX: Serialize list traversal if needed. The following might\n+\t * give a wrong information on contention.\n+\t */\n+\tlist_for_each_entry(d, \u0026fc-\u003edep_head, dep_node) {\n+\t\tstruct dept_class *tc = d-\u003ewait-\u003eclass;\n+\n+\t\tprefix = tc-\u003esched_map ? \"\u003csched\u003e \" : \"\";\n+\t\tseq_printf(m, \" -\u003e [%p] %s%s\\n\", (void *)tc-\u003ekey, prefix, tc-\u003ename);\n+\t}\n+\tseq_puts(m, \"\\n\");\n+\n+\treturn 0;\n+}\n+\n+static const struct seq_operations dept_deps_ops = {\n+\t.start\t= l_start,\n+\t.next\t= l_next,\n+\t.stop\t= l_stop,\n+\t.show\t= l_show,\n+};\n+\n+static int dept_stats_show(struct seq_file *m, void *v)\n+{\n+\tint r;\n+\n+\tseq_puts(m, \"Accumulated amount of memory used by pools:\\n\\n\");\n+#define OBJECT(id, nr)\t\t\t\t\t\t\t\\\n+\tr = atomic_read(\u0026dept_pool[OBJECT_##id].acc_sz);\t\t\\\n+\tseq_printf(m, \"%s\\t%d KB\\n\", #id, r / 1024);\n+\t#include \"dept_object.h\"\n+#undef  OBJECT\n+\n+\treturn 0;\n+}\n+\n+static int __init dept_proc_init(void)\n+{\n+\tproc_create_seq(\"dept_deps\", S_IRUSR, NULL, \u0026dept_deps_ops);\n+\tproc_create_single(\"dept_stats\", S_IRUSR, NULL, dept_stats_show);\n+\treturn 0;\n+}\n+\n+__initcall(dept_proc_init);\ndiff --git a/kernel/dependency/dept_unit_test.c b/kernel/dependency/dept_unit_test.c\nnew file mode 100644\nindex 00000000000000..e8dada2e3dfbaf\n--- /dev/null\n+++ b/kernel/dependency/dept_unit_test.c\n@@ -0,0 +1,149 @@\n+// SPDX-License-Identifier: GPL-2.0+\n+/*\n+ * DEPT unit test\n+ *\n+ * Started by Byungchul Park \u003cmax.byungchul.park@gmail.com\u003e:\n+ *\n+ *  Copyright (c) 2025 SK hynix, Inc., Byungchul Park\n+ */\n+\n+#include \u003clinux/module.h\u003e\n+#include \u003clinux/spinlock.h\u003e\n+#include \u003clinux/mutex.h\u003e\n+#include \u003clinux/dept.h\u003e\n+#include \u003clinux/dept_unit_test.h\u003e\n+\n+MODULE_DESCRIPTION(\"DEPT unit test\");\n+MODULE_LICENSE(\"GPL\");\n+MODULE_AUTHOR(\"Byungchul Park \u003cmax.byungchul.park@sk.com\u003e\");\n+\n+struct unit {\n+\tconst char *name;\n+\tbool (*func)(void);\n+\tbool result;\n+};\n+\n+static DEFINE_SPINLOCK(s1);\n+static DEFINE_SPINLOCK(s2);\n+static bool test_spin_lock_deadlock(void)\n+{\n+\tdept_ut_results.circle_detected = false;\n+\n+\tspin_lock(\u0026s1);\n+\tspin_lock(\u0026s2);\n+\tspin_unlock(\u0026s2);\n+\tspin_unlock(\u0026s1);\n+\n+\tspin_lock(\u0026s2);\n+\tspin_lock(\u0026s1);\n+\tspin_unlock(\u0026s1);\n+\tspin_unlock(\u0026s2);\n+\n+\treturn dept_ut_results.circle_detected;\n+}\n+\n+static DEFINE_MUTEX(m1);\n+static DEFINE_MUTEX(m2);\n+static bool test_mutex_lock_deadlock(void)\n+{\n+\tdept_ut_results.circle_detected = false;\n+\n+\tmutex_lock(\u0026m1);\n+\tmutex_lock(\u0026m2);\n+\tmutex_unlock(\u0026m2);\n+\tmutex_unlock(\u0026m1);\n+\n+\tmutex_lock(\u0026m2);\n+\tmutex_lock(\u0026m1);\n+\tmutex_unlock(\u0026m1);\n+\tmutex_unlock(\u0026m2);\n+\n+\treturn dept_ut_results.circle_detected;\n+}\n+\n+static bool test_wait_event_deadlock(void)\n+{\n+\tstruct dept_map dmap1;\n+\tstruct dept_map dmap2;\n+\n+\tsdt_map_init(\u0026dmap1);\n+\tsdt_map_init(\u0026dmap2);\n+\n+\tdept_ut_results.circle_detected = false;\n+\n+\tsdt_request_event(\u0026dmap1); /* [S] */\n+\tsdt_wait(\u0026dmap2); /* [W] */\n+\tsdt_event(\u0026dmap1); /* [E] */\n+\n+\tsdt_request_event(\u0026dmap2); /* [S] */\n+\tsdt_wait(\u0026dmap1); /* [W] */\n+\tsdt_event(\u0026dmap2); /* [E] */\n+\n+\treturn dept_ut_results.circle_detected;\n+}\n+\n+static struct unit units[] = {\n+\t{\n+\t\t.name = \"spin lock deadlock test\",\n+\t\t.func = test_spin_lock_deadlock,\n+\t},\n+\t{\n+\t\t.name = \"mutex lock deadlock test\",\n+\t\t.func = test_mutex_lock_deadlock,\n+\t},\n+\t{\n+\t\t.name = \"wait event deadlock test\",\n+\t\t.func = test_wait_event_deadlock,\n+\t},\n+};\n+\n+static int __init dept_ut_init(void)\n+{\n+\tint i;\n+\n+\tlockdep_off();\n+\n+\tdept_ut_results.ecxt_stack_valid_cnt = 0;\n+\tdept_ut_results.ecxt_stack_total_cnt = 0;\n+\tdept_ut_results.wait_stack_valid_cnt = 0;\n+\tdept_ut_results.wait_stack_total_cnt = 0;\n+\tdept_ut_results.evnt_stack_valid_cnt = 0;\n+\tdept_ut_results.evnt_stack_total_cnt = 0;\n+\n+\tfor (i = 0; i \u003c ARRAY_SIZE(units); i++)\n+\t\tunits[i].result = units[i].func();\n+\n+\tpr_info(\"\\n\");\n+\tpr_info(\"******************************************\\n\");\n+\tpr_info(\"DEPT unit test results\\n\");\n+\tpr_info(\"******************************************\\n\");\n+\tfor (i = 0; i \u003c ARRAY_SIZE(units); i++) {\n+\t\tpr_info(\"(%s) %s\\n\", units[i].result ? \"pass\" : \"fail\",\n+\t\t\t\tunits[i].name);\n+\t}\n+\tpr_info(\"ecxt stack valid count = %d/%d\\n\",\n+\t\t\tdept_ut_results.ecxt_stack_valid_cnt,\n+\t\t\tdept_ut_results.ecxt_stack_total_cnt);\n+\tpr_info(\"wait stack valid count = %d/%d\\n\",\n+\t\t\tdept_ut_results.wait_stack_valid_cnt,\n+\t\t\tdept_ut_results.wait_stack_total_cnt);\n+\tpr_info(\"event stack valid count = %d/%d\\n\",\n+\t\t\tdept_ut_results.evnt_stack_valid_cnt,\n+\t\t\tdept_ut_results.evnt_stack_total_cnt);\n+\tpr_info(\"******************************************\\n\");\n+\tpr_info(\"\\n\");\n+\n+\tlockdep_on();\n+\n+\treturn 0;\n+}\n+\n+static void dept_ut_cleanup(void)\n+{\n+\t/*\n+\t * Do nothing for now.\n+\t */\n+}\n+\n+module_init(dept_ut_init);\n+module_exit(dept_ut_cleanup);\ndiff --git a/kernel/exit.c b/kernel/exit.c\nindex ede3117fa7d413..25297ef0421edb 100644\n--- a/kernel/exit.c\n+++ b/kernel/exit.c\n@@ -1016,6 +1016,7 @@ void __noreturn do_exit(long code)\n \texit_tasks_rcu_finish();\n \n \tlockdep_free_task(tsk);\n+\tdept_task_exit(tsk);\n \tdo_task_dead();\n }\n EXPORT_SYMBOL(do_exit);\ndiff --git a/kernel/fork.c b/kernel/fork.c\nindex bc2bf58b93b652..1f94bfd1a46b94 100644\n--- a/kernel/fork.c\n+++ b/kernel/fork.c\n@@ -108,6 +108,7 @@\n #include \u003clinux/tick.h\u003e\n #include \u003clinux/unwind_deferred.h\u003e\n #include \u003clinux/pgalloc.h\u003e\n+#include \u003clinux/dept.h\u003e\n #include \u003clinux/uaccess.h\u003e\n \n #include \u003casm/mmu_context.h\u003e\n@@ -2175,6 +2176,7 @@ __latent_entropy struct task_struct *copy_process(\n \tp-\u003epagefault_disabled = 0;\n \n \tlockdep_init_task(p);\n+\tdept_task_init(p);\n \n \tp-\u003eblocked_on = NULL; /* not blocked yet */\n \ndiff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c\nindex 2d4c5bab5af887..c99f91f7a54db9 100644\n--- a/kernel/locking/lockdep.c\n+++ b/kernel/locking/lockdep.c\n@@ -1224,6 +1224,8 @@ void lockdep_register_key(struct lock_class_key *key)\n \tstruct lock_class_key *k;\n \tunsigned long flags;\n \n+\tdept_key_init(\u0026key-\u003edkey);\n+\n \tif (WARN_ON_ONCE(static_obj(key)))\n \t\treturn;\n \thash_head = keyhashentry(key);\n@@ -4361,6 +4363,8 @@ static void __trace_hardirqs_on_caller(void)\n  */\n void lockdep_hardirqs_on_prepare(void)\n {\n+\tdept_hardirqs_on();\n+\n \tif (unlikely(!debug_locks))\n \t\treturn;\n \n@@ -4481,6 +4485,8 @@ EXPORT_SYMBOL_GPL(lockdep_hardirqs_on);\n  */\n void noinstr lockdep_hardirqs_off(unsigned long ip)\n {\n+\tdept_hardirqs_off();\n+\n \tif (unlikely(!debug_locks))\n \t\treturn;\n \n@@ -4525,6 +4531,8 @@ void lockdep_softirqs_on(unsigned long ip)\n {\n \tstruct irqtrace_events *trace = \u0026current-\u003eirqtrace;\n \n+\tdept_softirqs_on_ip(ip);\n+\n \tif (unlikely(!lockdep_enabled()))\n \t\treturn;\n \n@@ -4563,6 +4571,8 @@ void lockdep_softirqs_on(unsigned long ip)\n  */\n void lockdep_softirqs_off(unsigned long ip)\n {\n+\tdept_softirqs_off();\n+\n \tif (unlikely(!lockdep_enabled()))\n \t\treturn;\n \n@@ -4940,6 +4950,8 @@ void lockdep_init_map_type(struct lockdep_map *lock, const char *name,\n {\n \tint i;\n \n+\tldt_init(\u0026lock-\u003edmap, \u0026key-\u003edkey, subclass, name);\n+\n \tfor (i = 0; i \u003c NR_LOCKDEP_CACHING_CLASSES; i++)\n \t\tlock-\u003eclass_cache[i] = NULL;\n \n@@ -5023,6 +5035,7 @@ void lockdep_set_lock_cmp_fn(struct lockdep_map *lock, lock_cmp_fn cmp_fn,\n \t\tclass-\u003eprint_fn = print_fn;\n \t}\n \n+\tdept_set_lockdep_map(\u0026lock-\u003edmap, lock);\n \tlockdep_recursion_finish();\n \traw_local_irq_restore(flags);\n }\n@@ -5736,6 +5749,12 @@ void lock_set_class(struct lockdep_map *lock, const char *name,\n {\n \tunsigned long flags;\n \n+\t/*\n+\t * dept_map_(re)init() might be called twice redundantly. But\n+\t * there's no choice as long as Dept relies on Lockdep.\n+\t */\n+\tldt_set_class(\u0026lock-\u003edmap, name, \u0026key-\u003edkey, subclass, ip);\n+\n \tif (unlikely(!lockdep_enabled()))\n \t\treturn;\n \n@@ -5753,6 +5772,8 @@ void lock_downgrade(struct lockdep_map *lock, unsigned long ip)\n {\n \tunsigned long flags;\n \n+\tldt_downgrade(\u0026lock-\u003edmap, ip);\n+\n \tif (unlikely(!lockdep_enabled()))\n \t\treturn;\n \n@@ -6588,6 +6609,8 @@ void lockdep_unregister_key(struct lock_class_key *key)\n \tbool found = false;\n \tbool need_callback = false;\n \n+\tdept_key_destroy(\u0026key-\u003edkey);\n+\n \tmight_sleep();\n \n \tif (WARN_ON_ONCE(static_obj(key)))\n@@ -6878,3 +6901,13 @@ void lockdep_rcu_suspicious(const char *file, const int line, const char *s)\n \twarn_rcu_exit(rcu);\n }\n EXPORT_SYMBOL_GPL(lockdep_rcu_suspicious);\n+\n+/*\n+ * For avoiding header dependency when using (struct task_struct *)current\n+ * and lockdep_recursing() at the same time.\n+ */\n+noinstr bool lockdep_recursing_current(void)\n+{\n+\treturn lockdep_recursing(current);\n+}\n+EXPORT_SYMBOL_GPL(lockdep_recursing_current);\ndiff --git a/kernel/module/main.c b/kernel/module/main.c\nindex c3ce106c70af16..5bf3b3d1e3ecde 100644\n--- a/kernel/module/main.c\n+++ b/kernel/module/main.c\n@@ -1375,12 +1375,14 @@ static void free_mod_mem(struct module *mod)\n \n \t\t/* Free lock-classes; relies on the preceding sync_rcu(). */\n \t\tlockdep_free_key_range(mod_mem-\u003ebase, mod_mem-\u003esize);\n+\t\tdept_free_range(mod_mem-\u003ebase, mod_mem-\u003esize);\n \t\tif (mod_mem-\u003esize)\n \t\t\tmodule_memory_free(mod, type);\n \t}\n \n \t/* MOD_DATA hosts mod, so free it at last */\n \tlockdep_free_key_range(mod-\u003emem[MOD_DATA].base, mod-\u003emem[MOD_DATA].size);\n+\tdept_free_range(mod-\u003emem[MOD_DATA].base, mod-\u003emem[MOD_DATA].size);\n \tmodule_memory_free(mod, MOD_DATA);\n }\n \ndiff --git a/kernel/rcu/rcu.h b/kernel/rcu/rcu.h\nindex 9b10b57b79ada7..d30dfc34553278 100644\n--- a/kernel/rcu/rcu.h\n+++ b/kernel/rcu/rcu.h\n@@ -12,6 +12,7 @@\n \n #include \u003clinux/slab.h\u003e\n #include \u003ctrace/events/rcu.h\u003e\n+#include \u003clinux/dept_sdt.h\u003e\n \n /*\n  * Grace-period counter management.\ndiff --git a/kernel/rcu/update.c b/kernel/rcu/update.c\nindex d98a5c38e19c51..c2858650ccf52f 100644\n--- a/kernel/rcu/update.c\n+++ b/kernel/rcu/update.c\n@@ -409,7 +409,7 @@ void wakeme_after_rcu(struct rcu_head *head)\n EXPORT_SYMBOL_GPL(wakeme_after_rcu);\n \n void __wait_rcu_gp(bool checktiny, unsigned int state, int n, call_rcu_func_t *crcu_array,\n-\t\t   struct rcu_synchronize *rs_array)\n+\t\t   struct rcu_synchronize *rs_array, struct dept_key *dkey)\n {\n \tint i;\n \tint j;\n@@ -426,7 +426,8 @@ void __wait_rcu_gp(bool checktiny, unsigned int state, int n, call_rcu_func_t *c\n \t\t\t\tbreak;\n \t\tif (j == i) {\n \t\t\tinit_rcu_head_on_stack(\u0026rs_array[i].head);\n-\t\t\tinit_completion(\u0026rs_array[i].completion);\n+\t\t\tsdt_map_init_key(\u0026rs_array[i].dmap, dkey);\n+\t\t\tinit_completion_dmap(\u0026rs_array[i].completion, \u0026rs_array[i].dmap);\n \t\t\t(crcu_array[i])(\u0026rs_array[i].head, wakeme_after_rcu);\n \t\t}\n \t}\ndiff --git a/kernel/sched/completion.c b/kernel/sched/completion.c\nindex 19ee702273c0fa..7262000db1146e 100644\n--- a/kernel/sched/completion.c\n+++ b/kernel/sched/completion.c\n@@ -4,7 +4,7 @@\n  * Generic wait-for-completion handler;\n  *\n  * It differs from semaphores in that their default case is the opposite,\n- * wait_for_completion default blocks whereas semaphore default non-block. The\n+ * __wait_for_completion default blocks whereas semaphore default non-block. The\n  * interface also makes it easy to 'complete' multiple waiting threads,\n  * something which isn't entirely natural for semaphores.\n  *\n@@ -42,7 +42,7 @@ void complete_on_current_cpu(struct completion *x)\n  * This will wake up a single thread waiting on this completion. Threads will be\n  * awakened in the same order in which they were queued.\n  *\n- * See also complete_all(), wait_for_completion() and related routines.\n+ * See also complete_all(), __wait_for_completion() and related routines.\n  *\n  * If this function wakes up a task, it executes a full memory barrier before\n  * accessing the task state.\n@@ -115,7 +115,7 @@ __wait_for_common(struct completion *x,\n {\n \tmight_sleep();\n \n-\tcomplete_acquire(x);\n+\tcomplete_acquire(x, timeout);\n \n \traw_spin_lock_irq(\u0026x-\u003ewait.lock);\n \ttimeout = do_wait_for_common(x, action, timeout, state);\n@@ -139,23 +139,23 @@ wait_for_common_io(struct completion *x, long timeout, int state)\n }\n \n /**\n- * wait_for_completion: - waits for completion of a task\n+ * __wait_for_completion: - waits for completion of a task\n  * @x:  holds the state of this particular completion\n  *\n  * This waits to be signaled for completion of a specific task. It is NOT\n  * interruptible and there is no timeout.\n  *\n- * See also similar routines (i.e. wait_for_completion_timeout()) with timeout\n+ * See also similar routines (i.e. __wait_for_completion_timeout()) with timeout\n  * and interrupt capability. Also see complete().\n  */\n-void __sched wait_for_completion(struct completion *x)\n+void __sched __wait_for_completion(struct completion *x)\n {\n \twait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);\n }\n-EXPORT_SYMBOL(wait_for_completion);\n+EXPORT_SYMBOL(__wait_for_completion);\n \n /**\n- * wait_for_completion_timeout: - waits for completion of a task (w/timeout)\n+ * __wait_for_completion_timeout: - waits for completion of a task (w/timeout)\n  * @x:  holds the state of this particular completion\n  * @timeout:  timeout value in jiffies\n  *\n@@ -167,28 +167,28 @@ EXPORT_SYMBOL(wait_for_completion);\n  * till timeout) if completed.\n  */\n unsigned long __sched\n-wait_for_completion_timeout(struct completion *x, unsigned long timeout)\n+__wait_for_completion_timeout(struct completion *x, unsigned long timeout)\n {\n \treturn wait_for_common(x, timeout, TASK_UNINTERRUPTIBLE);\n }\n-EXPORT_SYMBOL(wait_for_completion_timeout);\n+EXPORT_SYMBOL(__wait_for_completion_timeout);\n \n /**\n- * wait_for_completion_io: - waits for completion of a task\n+ * __wait_for_completion_io: - waits for completion of a task\n  * @x:  holds the state of this particular completion\n  *\n  * This waits to be signaled for completion of a specific task. It is NOT\n  * interruptible and there is no timeout. The caller is accounted as waiting\n  * for IO (which traditionally means blkio only).\n  */\n-void __sched wait_for_completion_io(struct completion *x)\n+void __sched __wait_for_completion_io(struct completion *x)\n {\n \twait_for_common_io(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);\n }\n-EXPORT_SYMBOL(wait_for_completion_io);\n+EXPORT_SYMBOL(__wait_for_completion_io);\n \n /**\n- * wait_for_completion_io_timeout: - waits for completion of a task (w/timeout)\n+ * __wait_for_completion_io_timeout: - waits for completion of a task (w/timeout)\n  * @x:  holds the state of this particular completion\n  * @timeout:  timeout value in jiffies\n  *\n@@ -201,14 +201,14 @@ EXPORT_SYMBOL(wait_for_completion_io);\n  * till timeout) if completed.\n  */\n unsigned long __sched\n-wait_for_completion_io_timeout(struct completion *x, unsigned long timeout)\n+__wait_for_completion_io_timeout(struct completion *x, unsigned long timeout)\n {\n \treturn wait_for_common_io(x, timeout, TASK_UNINTERRUPTIBLE);\n }\n-EXPORT_SYMBOL(wait_for_completion_io_timeout);\n+EXPORT_SYMBOL(__wait_for_completion_io_timeout);\n \n /**\n- * wait_for_completion_interruptible: - waits for completion of a task (w/intr)\n+ * __wait_for_completion_interruptible: - waits for completion of a task (w/intr)\n  * @x:  holds the state of this particular completion\n  *\n  * This waits for completion of a specific task to be signaled. It is\n@@ -216,7 +216,7 @@ EXPORT_SYMBOL(wait_for_completion_io_timeout);\n  *\n  * Return: -ERESTARTSYS if interrupted, 0 if completed.\n  */\n-int __sched wait_for_completion_interruptible(struct completion *x)\n+int __sched __wait_for_completion_interruptible(struct completion *x)\n {\n \tlong t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_INTERRUPTIBLE);\n \n@@ -224,10 +224,10 @@ int __sched wait_for_completion_interruptible(struct completion *x)\n \t\treturn t;\n \treturn 0;\n }\n-EXPORT_SYMBOL(wait_for_completion_interruptible);\n+EXPORT_SYMBOL(__wait_for_completion_interruptible);\n \n /**\n- * wait_for_completion_interruptible_timeout: - waits for completion (w/(to,intr))\n+ * __wait_for_completion_interruptible_timeout: - waits for completion (w/(to,intr))\n  * @x:  holds the state of this particular completion\n  * @timeout:  timeout value in jiffies\n  *\n@@ -238,15 +238,15 @@ EXPORT_SYMBOL(wait_for_completion_interruptible);\n  * or number of jiffies left till timeout) if completed.\n  */\n long __sched\n-wait_for_completion_interruptible_timeout(struct completion *x,\n+__wait_for_completion_interruptible_timeout(struct completion *x,\n \t\t\t\t\t  unsigned long timeout)\n {\n \treturn wait_for_common(x, timeout, TASK_INTERRUPTIBLE);\n }\n-EXPORT_SYMBOL(wait_for_completion_interruptible_timeout);\n+EXPORT_SYMBOL(__wait_for_completion_interruptible_timeout);\n \n /**\n- * wait_for_completion_killable: - waits for completion of a task (killable)\n+ * __wait_for_completion_killable: - waits for completion of a task (killable)\n  * @x:  holds the state of this particular completion\n  *\n  * This waits to be signaled for completion of a specific task. It can be\n@@ -254,7 +254,7 @@ EXPORT_SYMBOL(wait_for_completion_interruptible_timeout);\n  *\n  * Return: -ERESTARTSYS if interrupted, 0 if completed.\n  */\n-int __sched wait_for_completion_killable(struct completion *x)\n+int __sched __wait_for_completion_killable(struct completion *x)\n {\n \tlong t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_KILLABLE);\n \n@@ -262,9 +262,9 @@ int __sched wait_for_completion_killable(struct completion *x)\n \t\treturn t;\n \treturn 0;\n }\n-EXPORT_SYMBOL(wait_for_completion_killable);\n+EXPORT_SYMBOL(__wait_for_completion_killable);\n \n-int __sched wait_for_completion_state(struct completion *x, unsigned int state)\n+int __sched __wait_for_completion_state(struct completion *x, unsigned int state)\n {\n \tlong t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, state);\n \n@@ -272,10 +272,10 @@ int __sched wait_for_completion_state(struct completion *x, unsigned int state)\n \t\treturn t;\n \treturn 0;\n }\n-EXPORT_SYMBOL(wait_for_completion_state);\n+EXPORT_SYMBOL(__wait_for_completion_state);\n \n /**\n- * wait_for_completion_killable_timeout: - waits for completion of a task (w/(to,killable))\n+ * __wait_for_completion_killable_timeout: - waits for completion of a task (w/(to,killable))\n  * @x:  holds the state of this particular completion\n  * @timeout:  timeout value in jiffies\n  *\n@@ -287,12 +287,12 @@ EXPORT_SYMBOL(wait_for_completion_state);\n  * or number of jiffies left till timeout) if completed.\n  */\n long __sched\n-wait_for_completion_killable_timeout(struct completion *x,\n+__wait_for_completion_killable_timeout(struct completion *x,\n \t\t\t\t     unsigned long timeout)\n {\n \treturn wait_for_common(x, timeout, TASK_KILLABLE);\n }\n-EXPORT_SYMBOL(wait_for_completion_killable_timeout);\n+EXPORT_SYMBOL(__wait_for_completion_killable_timeout);\n \n /**\n  *\ttry_wait_for_completion - try to decrement a completion without blocking\n@@ -334,7 +334,7 @@ EXPORT_SYMBOL(try_wait_for_completion);\n  *\tcompletion_done - Test to see if a completion has any waiters\n  *\t@x:\tcompletion structure\n  *\n- *\tReturn: 0 if there are waiters (wait_for_completion() in progress)\n+ *\tReturn: 0 if there are waiters (__wait_for_completion() in progress)\n  *\t\t 1 if there are no waiters.\n  *\n  *\tNote, this will always return true if complete_all() was called on @X.\ndiff --git a/kernel/sched/core.c b/kernel/sched/core.c\nindex 496dff740dcafe..c01597d645ae05 100644\n--- a/kernel/sched/core.c\n+++ b/kernel/sched/core.c\n@@ -69,6 +69,7 @@\n #include \u003clinux/wait_api.h\u003e\n #include \u003clinux/workqueue_api.h\u003e\n #include \u003clinux/livepatch_sched.h\u003e\n+#include \u003clinux/dept.h\u003e\n \n #ifdef CONFIG_PREEMPT_DYNAMIC\n # ifdef CONFIG_GENERIC_IRQ_ENTRY\n@@ -4160,6 +4161,8 @@ int try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)\n \t\tif (READ_ONCE(p-\u003eon_rq) \u0026\u0026 ttwu_runnable(p, wake_flags))\n \t\t\tbreak;\n \n+\t\tdept_ttwu_stage_wait(p, _RET_IP_);\n+\n \t\t/*\n \t\t * Ensure we load p-\u003eon_cpu _after_ p-\u003eon_rq, otherwise it would be\n \t\t * possible to, falsely, observe p-\u003eon_cpu == 0.\n@@ -6783,6 +6786,11 @@ static void __sched notrace __schedule(int sched_mode)\n \trq = cpu_rq(cpu);\n \tprev = rq-\u003ecurr;\n \n+\tprev_state = READ_ONCE(prev-\u003e__state);\n+\tif (sched_mode != SM_PREEMPT \u0026\u0026 prev_state \u0026 TASK_NORMAL)\n+\t\tdept_request_event_wait_commit();\n+\n+\tdept_sched_enter();\n \tschedule_debug(prev, preempt);\n \n \tif (sched_feat(HRTICK) || sched_feat(HRTICK_DL))\n@@ -6919,6 +6927,7 @@ static void __sched notrace __schedule(int sched_mode)\n \t\traw_spin_rq_unlock_irq(rq);\n \t}\n \ttrace_sched_exit_tp(is_switch);\n+\tdept_sched_exit();\n }\n \n void __noreturn do_task_dead(void)\ndiff --git a/kernel/workqueue.c b/kernel/workqueue.c\nindex c6ea96d5b71672..4a4075d0697c74 100644\n--- a/kernel/workqueue.c\n+++ b/kernel/workqueue.c\n@@ -55,6 +55,7 @@\n #include \u003clinux/kvm_para.h\u003e\n #include \u003clinux/delay.h\u003e\n #include \u003clinux/irq_work.h\u003e\n+#include \u003clinux/dept.h\u003e\n \n #include \"workqueue_internal.h\"\n \n@@ -3204,6 +3205,8 @@ __acquires(\u0026pool-\u003elock)\n \n \tlockdep_copy_map(\u0026lockdep_map, \u0026work-\u003elockdep_map);\n #endif\n+\tdept_update_cxt();\n+\n \t/* ensure we're on the correct CPU */\n \tWARN_ON_ONCE(!(pool-\u003eflags \u0026 POOL_DISASSOCIATED) \u0026\u0026\n \t\t     raw_smp_processor_id() != pool-\u003ecpu);\ndiff --git a/lib/Kconfig.debug b/lib/Kconfig.debug\nindex 93f356d2b3d955..41c822f7b75a23 100644\n--- a/lib/Kconfig.debug\n+++ b/lib/Kconfig.debug\n@@ -1441,6 +1441,54 @@ config DEBUG_ATOMIC_LARGEST_ALIGN\n \n menu \"Lock Debugging (spinlocks, mutexes, etc...)\"\n \n+config DEPT\n+\tbool \"Dependency tracking (EXPERIMENTAL)\"\n+\tdepends on DEBUG_KERNEL \u0026\u0026 LOCK_DEBUGGING_SUPPORT\n+\tselect DEBUG_SPINLOCK\n+\tselect DEBUG_MUTEXES if !PREEMPT_RT\n+\tselect DEBUG_RT_MUTEXES if RT_MUTEXES\n+\tselect DEBUG_RWSEMS if !PREEMPT_RT\n+\tselect DEBUG_WW_MUTEX_SLOWPATH\n+\tselect DEBUG_LOCK_ALLOC\n+\tselect TRACE_IRQFLAGS\n+\tselect STACKTRACE\n+\tselect KALLSYMS\n+\tselect KALLSYMS_ALL\n+\tselect PROVE_LOCKING\n+\tdefault n\n+\thelp\n+\t  Check dependencies between wait and event and report it if\n+\t  deadlock possibility has been detected. Multiple reports are\n+\t  allowed if there are more than a single problem.\n+\n+\t  This feature is considered EXPERIMENTAL that might produce\n+\t  false positive reports because new dependencies start to be\n+\t  tracked, that have never been tracked before. It's worth\n+\t  noting, to mitigate the impact by the false positives, multi\n+\t  reporting has been supported.\n+\n+config DEPT_AGGRESSIVE_TIMEOUT_WAIT\n+\tbool \"Aggressively track even timeout waits\"\n+\tdepends on DEPT\n+\tdefault n\n+\thelp\n+\t  Timeout wait doesn't contribute to a deadlock. However,\n+\t  informing a circular dependency might be helpful for cases\n+\t  that timeout is used to avoid a deadlock. Say N if you'd like\n+\t  to avoid verbose reports.\n+\n+config DEPT_UNIT_TEST\n+\ttristate \"unit test for DEPT\"\n+\tdepends on DEBUG_KERNEL \u0026\u0026 DEPT\n+\tdefault n\n+\thelp\n+\t  This option provides a kernel module that runs unit test for\n+\t  DEPT.\n+\n+\t  Say Y if you want DEPT unit test to be built into the kernel.\n+\t  Say M if you want DEPT unit test to build as a module.\n+\t  Say N if you are unsure.\n+\n config LOCK_DEBUGGING_SUPPORT\n \tbool\n \tdepends on TRACE_IRQFLAGS_SUPPORT \u0026\u0026 STACKTRACE_SUPPORT \u0026\u0026 LOCKDEP_SUPPORT\ndiff --git a/lib/debug_locks.c b/lib/debug_locks.c\nindex a75ee30b77cb8d..14a965914a8fb4 100644\n--- a/lib/debug_locks.c\n+++ b/lib/debug_locks.c\n@@ -38,6 +38,8 @@ EXPORT_SYMBOL_GPL(debug_locks_silent);\n  */\n int debug_locks_off(void)\n {\n+\tdept_stop_emerg();\n+\n \tif (debug_locks \u0026\u0026 __debug_locks_off()) {\n \t\tif (!debug_locks_silent) {\n \t\t\tconsole_verbose();\ndiff --git a/lib/locking-selftest.c b/lib/locking-selftest.c\nindex d939403331b5a6..a7f8e59d0092da 100644\n--- a/lib/locking-selftest.c\n+++ b/lib/locking-selftest.c\n@@ -1398,6 +1398,8 @@ static void reset_locks(void)\n \tlocal_irq_disable();\n \tlockdep_free_key_range(\u0026ww_lockdep.acquire_key, 1);\n \tlockdep_free_key_range(\u0026ww_lockdep.mutex_key, 1);\n+\tdept_free_range(\u0026ww_lockdep.acquire_key, 1);\n+\tdept_free_range(\u0026ww_lockdep.mutex_key, 1);\n \n \tI1(A); I1(B); I1(C); I1(D);\n \tI1(X1); I1(X2); I1(Y1); I1(Y2); I1(Z1); I1(Z2);\ndiff --git a/mm/filemap.c b/mm/filemap.c\nindex 3c1e785542dde0..e3aa2754da3fa9 100644\n--- a/mm/filemap.c\n+++ b/mm/filemap.c\n@@ -49,6 +49,7 @@\n #include \u003clinux/sched/mm.h\u003e\n #include \u003clinux/sysctl.h\u003e\n #include \u003clinux/pgalloc.h\u003e\n+#include \u003clinux/dept.h\u003e\n \n #include \u003casm/tlbflush.h\u003e\n #include \"internal.h\"\n@@ -1151,6 +1152,7 @@ static int wake_page_function(wait_queue_entry_t *wait, unsigned mode, int sync,\n \t\tif (flags \u0026 WQ_FLAG_CUSTOM) {\n \t\t\tif (test_and_set_bit(key-\u003ebit_nr, \u0026key-\u003efolio-\u003eflags.f))\n \t\t\t\treturn -1;\n+\t\t\tdept_page_set_bit(\u0026key-\u003efolio-\u003epage, key-\u003ebit_nr);\n \t\t\tflags |= WQ_FLAG_DONE;\n \t\t}\n \t}\n@@ -1191,6 +1193,13 @@ static void folio_wake_bit(struct folio *folio, int bit_nr)\n \tkey.bit_nr = bit_nr;\n \tkey.page_match = 0;\n \n+\t/*\n+\t * dept_page_clear_bit() being called multiple times is harmless.\n+\t * The worst case is to miss some dependencies but it's okay.\n+\t */\n+\tif (bit_nr == PG_locked || bit_nr == PG_writeback)\n+\t\tdept_page_clear_bit(\u0026folio-\u003epage, bit_nr);\n+\n \tspin_lock_irqsave(\u0026q-\u003elock, flags);\n \t__wake_up_locked_key(q, TASK_NORMAL, \u0026key);\n \n@@ -1234,6 +1243,7 @@ static inline bool folio_trylock_flag(struct folio *folio, int bit_nr,\n \tif (wait-\u003eflags \u0026 WQ_FLAG_EXCLUSIVE) {\n \t\tif (test_and_set_bit(bit_nr, \u0026folio-\u003eflags.f))\n \t\t\treturn false;\n+\t\tdept_page_set_bit(\u0026folio-\u003epage, bit_nr);\n \t} else if (test_bit(bit_nr, \u0026folio-\u003eflags.f))\n \t\treturn false;\n \n@@ -1241,6 +1251,12 @@ static inline bool folio_trylock_flag(struct folio *folio, int bit_nr,\n \treturn true;\n }\n \n+struct dept_map __maybe_unused pg_locked_map = DEPT_MAP_INITIALIZER(pg_locked_map, NULL);\n+EXPORT_SYMBOL(pg_locked_map);\n+\n+struct dept_map __maybe_unused pg_writeback_map = DEPT_MAP_INITIALIZER(pg_writeback_map, NULL);\n+EXPORT_SYMBOL(pg_writeback_map);\n+\n static inline int folio_wait_bit_common(struct folio *folio, int bit_nr,\n \t\tint state, enum behavior behavior)\n {\n@@ -1252,6 +1268,8 @@ static inline int folio_wait_bit_common(struct folio *folio, int bit_nr,\n \tunsigned long pflags;\n \tbool in_thrashing;\n \n+\tdept_page_wait_on_bit(\u0026folio-\u003epage, bit_nr);\n+\n \tif (bit_nr == PG_locked \u0026\u0026\n \t    !folio_test_uptodate(folio) \u0026\u0026 folio_test_workingset(folio)) {\n \t\tdelayacct_thrashing_start(\u0026in_thrashing);\n@@ -1345,6 +1363,23 @@ static inline int folio_wait_bit_common(struct folio *folio, int bit_nr,\n \t\tbreak;\n \t}\n \n+\t/*\n+\t * dept_page_set_bit() might have been called already in\n+\t * folio_trylock_flag(), wake_page_function() or somewhere.\n+\t * However, call it again to reset the wgen of dept to ensure\n+\t * dept_page_wait_on_bit() is called prior to\n+\t * dept_page_set_bit().\n+\t *\n+\t * Remind dept considers all the waits between\n+\t * dept_page_set_bit() and dept_page_clear_bit() as potential\n+\t * event disturbers. Ensure the correct sequence so that dept\n+\t * can make correct decisions:\n+\t *\n+\t *\twait -\u003e acquire(set bit) -\u003e release(clear bit)\n+\t */\n+\tif (wait-\u003eflags \u0026 WQ_FLAG_DONE)\n+\t\tdept_page_set_bit(\u0026folio-\u003epage, bit_nr);\n+\n \t/*\n \t * If a signal happened, this 'finish_wait()' may remove the last\n \t * waiter from the wait-queues, but the folio waiters bit will remain\n@@ -1507,6 +1542,7 @@ void folio_unlock(struct folio *folio)\n \tBUILD_BUG_ON(PG_waiters != 7);\n \tBUILD_BUG_ON(PG_locked \u003e 7);\n \tVM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);\n+\tdept_page_clear_bit(\u0026folio-\u003epage, PG_locked);\n \tif (folio_xor_flags_has_waiters(folio, 1 \u003c\u003c PG_locked))\n \t\tfolio_wake_bit(folio, PG_locked);\n }\n@@ -1537,6 +1573,7 @@ void folio_end_read(struct folio *folio, bool success)\n \n \tif (likely(success))\n \t\tmask |= 1 \u003c\u003c PG_uptodate;\n+\tdept_page_clear_bit(\u0026folio-\u003epage, PG_locked);\n \tif (folio_xor_flags_has_waiters(folio, mask))\n \t\tfolio_wake_bit(folio, PG_locked);\n }\n@@ -1663,6 +1700,7 @@ void folio_end_writeback_no_dropbehind(struct folio *folio)\n \t\tfolio_rotate_reclaimable(folio);\n \t}\n \n+\tdept_page_clear_bit(\u0026folio-\u003epage, PG_writeback);\n \tif (__folio_end_writeback(folio))\n \t\tfolio_wake_bit(folio, PG_writeback);\n \ndiff --git a/mm/mm_init.c b/mm/mm_init.c\nindex df34797691bda2..2695d7b3b0898b 100644\n--- a/mm/mm_init.c\n+++ b/mm/mm_init.c\n@@ -32,6 +32,7 @@\n #include \u003clinux/vmstat.h\u003e\n #include \u003clinux/kexec_handover.h\u003e\n #include \u003clinux/hugetlb.h\u003e\n+#include \u003clinux/dept.h\u003e\n #include \"internal.h\"\n #include \"slab.h\"\n #include \"shuffle.h\"\n@@ -587,6 +588,8 @@ void __meminit __init_single_page(struct page *page, unsigned long pfn,\n \tatomic_set(\u0026page-\u003e_mapcount, -1);\n \tpage_cpupid_reset_last(page);\n \tpage_kasan_tag_reset(page);\n+\tdept_ext_wgen_init(\u0026page-\u003epg_locked_wgen);\n+\tdept_ext_wgen_init(\u0026page-\u003epg_writeback_wgen);\n \n \tINIT_LIST_HEAD(\u0026page-\u003elru);\n #ifdef WANT_PAGE_VIRTUAL\ndiff --git a/mm/mmu_notifier.c b/mm/mmu_notifier.c\nindex a6cdf3674bdc52..10c3420b3901ad 100644\n--- a/mm/mmu_notifier.c\n+++ b/mm/mmu_notifier.c\n@@ -46,6 +46,7 @@ struct mmu_notifier_subscriptions {\n \tunsigned long active_invalidate_ranges;\n \tstruct rb_root_cached itree;\n \twait_queue_head_t wq;\n+\tstruct dept_map dmap;\n \tstruct hlist_head deferred_list;\n };\n \n@@ -165,6 +166,25 @@ static void mn_itree_inv_end(struct mmu_notifier_subscriptions *subscriptions)\n \twake_up_all(\u0026subscriptions-\u003ewq);\n }\n \n+#ifdef CONFIG_DEPT\n+void mmu_notifier_invalidate_dept_ecxt_start(struct mmu_notifier_range *range)\n+{\n+\tstruct mmu_notifier_subscriptions *subscriptions =\n+\t\trange-\u003emm-\u003enotifier_subscriptions;\n+\n+\tif (subscriptions)\n+\t\tsdt_ecxt_enter(\u0026subscriptions-\u003edmap);\n+}\n+void mmu_notifier_invalidate_dept_ecxt_end(struct mmu_notifier_range *range)\n+{\n+\tstruct mmu_notifier_subscriptions *subscriptions =\n+\t\trange-\u003emm-\u003enotifier_subscriptions;\n+\n+\tif (subscriptions)\n+\t\tsdt_ecxt_exit(\u0026subscriptions-\u003edmap);\n+}\n+#endif\n+\n /**\n  * mmu_interval_read_begin - Begin a read side critical section against a VA\n  *                           range\n@@ -246,9 +266,12 @@ mmu_interval_read_begin(struct mmu_interval_notifier *interval_sub)\n \t */\n \tlock_map_acquire(\u0026__mmu_notifier_invalidate_range_start_map);\n \tlock_map_release(\u0026__mmu_notifier_invalidate_range_start_map);\n-\tif (is_invalidating)\n+\tif (is_invalidating) {\n+\t\tsdt_might_sleep_start(\u0026subscriptions-\u003edmap);\n \t\twait_event(subscriptions-\u003ewq,\n \t\t\t   READ_ONCE(subscriptions-\u003einvalidate_seq) != seq);\n+\t\tsdt_might_sleep_end();\n+\t}\n \n \t/*\n \t * Notice that mmu_interval_read_retry() can already be true at this\n@@ -624,6 +647,7 @@ int __mmu_notifier_register(struct mmu_notifier *subscription,\n \n \t\tINIT_HLIST_HEAD(\u0026subscriptions-\u003elist);\n \t\tspin_lock_init(\u0026subscriptions-\u003elock);\n+\t\tsdt_map_init(\u0026subscriptions-\u003edmap);\n \t\tsubscriptions-\u003einvalidate_seq = 2;\n \t\tsubscriptions-\u003eitree = RB_ROOT_CACHED;\n \t\tinit_waitqueue_head(\u0026subscriptions-\u003ewq);\n@@ -1069,9 +1093,12 @@ void mmu_interval_notifier_remove(struct mmu_interval_notifier *interval_sub)\n \t */\n \tlock_map_acquire(\u0026__mmu_notifier_invalidate_range_start_map);\n \tlock_map_release(\u0026__mmu_notifier_invalidate_range_start_map);\n-\tif (seq)\n+\tif (seq) {\n+\t\tsdt_might_sleep_start(\u0026subscriptions-\u003edmap);\n \t\twait_event(subscriptions-\u003ewq,\n \t\t\t   mmu_interval_seq_released(subscriptions, seq));\n+\t\tsdt_might_sleep_end();\n+\t}\n \n \t/* pairs with mmgrab in mmu_interval_notifier_insert() */\n \tmmdrop(mm);\ndiff --git a/rust/helpers/completion.c b/rust/helpers/completion.c\nindex 0126767cc3bebd..7b55c960fe22ae 100644\n--- a/rust/helpers/completion.c\n+++ b/rust/helpers/completion.c\n@@ -6,3 +6,8 @@ __rust_helper void rust_helper_init_completion(struct completion *x)\n {\n \tinit_completion(x);\n }\n+\n+__rust_helper void rust_helper_wait_for_completion(struct completion *x)\n+{\n+\twait_for_completion(x);\n+}\n"
}

2/1 2026/07/06 06:47 agent patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": [
    "DEPT",
    "DEPT_AGGRESSIVE_TIMEOUT_WAIT",
    "DEPT_UNIT_TEST"
  ],
  "FocusSymbols": [
    "__add_dep",
    "__add_idep",
    "check_dl_bfs",
    "bfs",
    "print_circle",
    "dept_free_range",
    "dept_key_destroy",
    "disconnect_class"
  ],
  "Reasoning": "The patch introduces DEPT (DEPendency Tracker), a new runtime debugging tool similar to lockdep but for general synchronization primitives (waits/events). It hooks into core kernel primitives like wait queues, completions, page flags, and IRQ handling. Since it adds significant new runtime logic and graph processing for deadlock detection, it is highly functional and should be fuzzed to ensure the tracker itself does not introduce crashes, memory leaks, or deadlocks. We focus on the slow-path graph processing and cleanup functions to avoid skewing the fuzzer on hot-path lock/wait operations.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. List them in the EnableConfigs output array,
   and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 52576b571949845328063b58e8f0a9fe0d409ce9
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Jul 6 06:47:32 2026 +0000

    syz-cluster: applied patch under review

diff --git a/Documentation/dev-tools/dept.rst b/Documentation/dev-tools/dept.rst
new file mode 100644
index 00000000000000..c0ed1314a5a796
--- /dev/null
+++ b/Documentation/dev-tools/dept.rst
@@ -0,0 +1,905 @@
+DEPT(DEPendency Tracker)
+========================
+
+Started by Byungchul Park <max.byungchul.park@sk.com>
+
+How lockdep works
+-----------------
+
+Lockdep detects deadlocks by checking lock acquisition order. For
+example, a graph to track acquisition order built by lockdep might look
+like:
+
+.. code-block::
+
+   A -> B -
+           \
+            -> E
+           /
+   C -> D -
+
+   where 'A -> B' means that acquisition A is prior to acquisition B
+   with A still held.
+
+Lockdep keeps adding each new acquisition order into the graph at
+runtime. For example, 'E -> C' will be added when the two locks have
+been acquired in the order E and then C. The graph will look like:
+
+.. code-block::
+
+       A -> B -
+               \
+                -> E -
+               /      \
+    -> C -> D -        \
+   /                   /
+   \                  /
+    ------------------
+
+   where 'A -> B' means that acquisition A is prior to acquisition B
+   with A still held.
+
+This graph contains a subgraph that demonstrates a loop like:
+
+.. code-block::
+
+                -> E -
+               /      \
+    -> C -> D -        \
+   /                   /
+   \                  /
+    ------------------
+
+   where 'A -> B' means that acquisition A is prior to acquisition B
+   with A still held.
+
+Lockdep reports it as a deadlock on detection of a loop and stops
+working.
+
+CONCLUSION
+
+Lockdep detects a deadlock by checking if a loop has been created after
+adding a new acquisition order into the graph.
+
+
+Limitation of lockdep
+---------------------
+
+Lockdep deals with deadlocks involving typical locks e.g. spinlock and
+mutex, that are supposed to be released within the acquisition context.
+However, when it comes to a deadlock involving folio lock that is not
+supposed to be released within the acquisition context or other general
+synchronization mechanisms, lockdep doesn't work.
+
+NOTE: In this document, 'context' refers to any type of unique context
+e.g. irq context, normal process context, wq worker context, and so on.
+
+Can lockdep detect the following deadlock?
+
+.. code-block::
+
+   context X	   context Y	   context Z
+
+		   mutex_lock A
+   folio_lock B
+		   folio_lock B <- DEADLOCK
+				   mutex_lock A <- DEADLOCK
+				   folio_unlock B
+		   folio_unlock B
+		   mutex_unlock A
+				   mutex_unlock A
+
+No. What about the following?
+
+.. code-block::
+
+   context X	   context Y
+
+		   mutex_lock A
+   mutex_lock A <- DEADLOCK
+		   wait_for_completion B <- DEADLOCK
+   complete B
+		   mutex_unlock A
+   mutex_unlock A
+
+No.
+
+CONCLUSION
+
+Lockdep cannot detect a deadlock involving folio lock or other general
+synchronization mechanisms.
+
+
+What leads to a deadlock
+------------------------
+
+A deadlock occurs when one or more contexts are waiting for events that
+will never happen. For example:
+
+.. code-block::
+
+   context X	   context Y	   context Z
+
+   |		   |		   |
+   v		   |		   |
+   1 wait for A    v		   |
+   .		   2 wait for C    v
+   event C	   .		   3 wait for B
+		   event B	   .
+				   event A
+
+Event C cannot be triggered because context X is stuck at 1, event B
+cannot be triggered because context Y is stuck at 2, and event A cannot
+be triggered because context Z is stuck at 3. All the contexts are stuck.
+We call this **deadlock**.
+
+If an event occurrence to awaken its wait is a prerequisite to reaching
+another event, we call it **dependency**. In this example:
+
+   * Event A occurrence is a prerequisite to reaching event C.
+   * Event C occurrence is a prerequisite to reaching event B.
+   * Event B occurrence is a prerequisite to reaching event A.
+
+In terms of dependency:
+
+   * Event C depends on event A.
+   * Event B depends on event C.
+   * Event A depends on event B.
+
+Dependency graph reflecting this example will look like:
+
+.. code-block::
+
+    -> C -> A -> B -
+   /                \
+   \                /
+    ----------------
+
+   where 'A -> B' means that event A depends on event B.
+
+A circular dependency exists. Such a circular dependency leads to a
+deadlock since no waiters can have desired events triggered.
+
+CONCLUSION
+
+A circular dependency of events leads to a deadlock.
+
+
+Introduce DEPT
+--------------
+
+DEPT(DEPendency Tracker) tracks wait and event instead of lock
+acquisition order so as to recognize the following situation:
+
+.. code-block::
+
+   context X	   context Y	   context Z
+
+   |		   |		   |
+   v		   |		   |
+   wait for A	   v		   |
+   .		   wait for C	   v
+   event C	   .		   wait for B
+		   event B	   .
+				   event A
+
+and builds up a dependency graph at runtime that is similar to lockdep.
+The graph might look like:
+
+.. code-block::
+
+    -> C -> A -> B -
+   /                \
+   \                /
+    ----------------
+
+   where 'A -> B' means that event A depends on event B.
+
+DEPT keeps adding each new dependency into the graph at runtime. For
+example, 'B -> D' will be added when event D occurrence is a
+prerequisite to reaching event B like:
+
+.. code-block::
+
+   context W
+
+   |
+   v
+   wait for D
+   .
+   event B
+
+After the addition, the graph will look like:
+
+.. code-block::
+
+                     -> D
+                    /
+    -> C -> A -> B -
+   /                \
+   \                /
+    ----------------
+
+   where 'A -> B' means that event A depends on event B.
+
+DEPT is going to report a deadlock on detection of a new loop.
+
+CONCLUSION
+
+DEPT works on wait and event so as to theoretically detect all potential
+deadlocks.
+
+
+How DEPT works
+--------------
+
+Let's take a look at how DEPT works with the 1st example in the section
+'Limitation of lockdep'.
+
+.. code-block::
+
+   context X	   context Y	   context Z
+
+		   mutex_lock A
+   folio_lock B
+		   folio_lock B <- DEADLOCK
+				   mutex_lock A <- DEADLOCK
+				   folio_unlock B
+		   folio_unlock B
+		   mutex_unlock A
+				   mutex_unlock A
+
+NOTE: In this document, 'event context' refers to a portion within a
+context where an interesting event is triggered in, between a point
+where the context has started progressing toward the event, and the
+event.
+
+Adding comments to describe DEPT's view in detail:
+
+.. code-block::
+
+   context X	   context Y	   context Z
+
+		   mutex_lock A
+		   /* might wait for A */
+		   /* start to take into account event A's context */
+		   /* 1 */
+   folio_lock B
+   /* might wait for B */
+   /* start to take into account event B's context */
+   /* 2 */
+
+		   folio_lock B
+		   /* might wait for B */ <- DEADLOCK
+		   /* start to take into account event B's context */
+		   /* 3 */
+
+				   mutex_lock A
+				   /* might wait for A */ <- DEADLOCK
+				   /* start to take into account
+				      event A's context */
+				   /* 4 */
+
+				   folio_unlock B
+				   /* event B that has been valid since 2 */
+		   folio_unlock B
+		   /* event B that has been valid since 3 */
+
+		   mutex_unlock A
+		   /* event A that has been valid since 1 */
+
+				   mutex_unlock A
+				   /* event A that has been valid since 4 */
+
+Let's build up a dependency graph with this example. Firstly, context X:
+
+.. code-block::
+
+   context X
+
+   folio_lock B
+   /* might wait for B */
+   /* start to take into account event B's context */
+   /* 2 */
+
+There are no events to create dependency. Next, context Y:
+
+.. code-block::
+
+   context Y
+
+   mutex_lock A
+   /* might wait for A */
+   /* start to take into account event A's context */
+   /* 1 */
+
+   folio_lock B
+   /* might wait for B */
+   /* start to take into account event B's context */
+   /* 3 */
+
+   folio_unlock B
+   /* event B that has been valid since 3 */
+
+   mutex_unlock A
+   /* event A that has been valid since 1 */
+
+There are two events, folio_unlock B a.k.a. event B and mutex_unlock A
+a.k.a. event A. For event B, since there are no waits between 3 and the
+event, event B does not create any dependency. For event A, there is a
+wait, folio_lock B a.k.a. wait B, between 1 and the event. Which means
+event A cannot be triggered if wait B cannot be awakened by event B.
+Therefore, we can say event A depends on event B, say, 'A -> B'. The
+graph will look like after adding the dependency:
+
+.. code-block::
+
+   A -> B
+
+   where 'A -> B' means that event A depends on event B.
+
+Lastly, context Z:
+
+.. code-block::
+
+   context Z
+
+   mutex_lock A
+   /* might wait for A */
+   /* start to take into account event A's context */
+   /* 4 */
+
+   folio_unlock B
+   /* event B that has been valid since 2 */
+
+   mutex_unlock A
+   /* event A that has been valid since 4 */
+
+There are also two events, folio_unlock B a.k.a. event B and
+mutex_unlock A a.k.a. event A. For event B, there is a wait, mutex_lock
+A a.k.a. wait A, between 2 and the event. Which means event B cannot be
+triggered if wait A cannot be awakened by event A. Therefore, we can
+say event B depends on event A, say, 'B -> A'. The graph will look like
+after adding the dependency:
+
+.. code-block::
+
+    -> A -> B -
+   /           \
+   \           /
+    -----------
+
+   where 'A -> B' means that event A depends on event B.
+
+A new loop has been created. So DEPT can report it as a deadlock. For
+event A, since there are no waits between 4 and the event, event A does
+not create any dependency. That's it.
+
+Let's take a look at how DEPT works with the 2nd example in the section
+'Limitation of lockdep'.
+
+.. code-block::
+
+   context X	   context Y
+
+		   mutex_lock A
+   mutex_lock A <- DEADLOCK
+		   wait_for_completion B <- DEADLOCK
+   complete B
+		   mutex_unlock A
+   mutex_unlock A
+
+Similarly adding comments to describe DEPT's view in detail:
+
+.. code-block::
+
+   context X	   context Y
+
+		   mutex_lock A
+                   /* might wait for A */
+                   /* start to take into account event A's context */
+                   /* 1 */
+
+                   request_something_and_complete_B
+                   /* request to handle something via e.g. wq, daemon,
+                      or any its own way, and finally do 'complete B'
+                      a.k.a. event B */
+                   /* 2 */
+   /* notice the request from 2 and handle it running toward event B */
+   /* start to take into account event B's context */
+   /* 3 */
+
+   mutex_lock A
+   /* might wait for A */ <- DEADLOCK
+   /* start to take into account event A's context */
+   /* 4 */
+		   wait_for_completion B
+                   /* wait for B */ <- DEADLOCK
+                   /* 5 */
+   complete B
+   /* event B that has been valid since 3 */
+		   mutex_unlock A
+                   /* event A that has been valid since 1 */
+   mutex_unlock A
+   /* event A that has been valid since 4 */
+
+Let's build up a dependency graph with this example. Firstly, context X:
+
+.. code-block::
+
+   context X
+
+   /* notice the request from 2 and handle it running toward event B */
+   /* start to take into account event B's context */
+   /* 3 */
+
+   mutex_lock A
+   /* might wait for A */
+   /* start to take into account event A's context */
+   /* 4 */
+
+   complete B
+   /* event B that has been valid since 3 */
+
+   mutex_unlock A
+   /* event A that has been valid since 4 */
+
+There are two events, complete B a.k.a. event B and mutex_unlock A a.k.a.
+event A. For event A, since there are no waits between between 4 and the
+event, event A does not create any dependency. For event B, there is a
+wait, mutex_lock A a.k.a. wait A, between 3 and the event. Which means
+event B cannot be triggered if wait A cannot be awakened by event A.
+Therefore, we can say event B depends on event A, say, 'B -> A'. The
+graph will look like after adding the dependency:
+
+.. code-block::
+
+   B -> A
+
+   where 'A -> B' means that event A depends on event B.
+
+If context X might notice the request after mutex_lock A, DEPT cannot
+track this dependency, which results in missing a dependency. However,
+that can be improved by adding proper DEPT annotations if needed.
+
+Next, context Y:
+
+.. code-block::
+
+   context Y
+
+   mutex_lock A
+   /* might wait for A */
+   /* start to take into account event A's context */
+   /* 1 */
+
+   request_something_and_complete_B
+   /* request to handle something via e.g. wq, daemon, or any its own
+      way, and finally do 'complete B' a.k.a. event B */
+   /* 2 */
+
+   wait_for_completion B
+   /* wait for B */
+   /* 5 */
+
+   mutex_unlock A
+   /* event A that has been valid since 1 */
+
+There is one event, mutex_unlock A a.k.a. event A. For event A, there is
+a wait, wait_for_completion B a.k.a. wait B, between 1 and the event.
+Which means event A cannot be triggered if wait B cannot be awakened by
+event B. Therefore, we can say event A depends on event B, say, 'A -> B'.
+The graph will look like after adding the dependency:
+
+.. code-block::
+
+    -> B -> A -
+   /           \
+   \           /
+    -----------
+
+   where 'A -> B' means that event A depends on event B.
+
+A new loop has been created. So DEPT can report it as a deadlock.
+
+CONCLUSION
+
+DEPT works well with any general synchronization mechanisms by focusing
+on wait, event and its context.
+
+
+Interpret DEPT report
+---------------------
+
+The following is the same example in the section 'How DEPT works'.
+
+.. code-block::
+
+   context X	   context Y	   context Z
+
+		   mutex_lock A
+		   /* might wait for A */
+		   /* start to take into account event A's context */
+		   /* 1 */
+   folio_lock B
+   /* might wait for B */
+   /* start to take into account event B's context */
+   /* 2 */
+
+		   folio_lock B
+		   /* might wait for B */ <- DEADLOCK
+		   /* start to take into account event B's context */
+		   /* 3 */
+
+				   mutex_lock A
+				   /* might wait for A */ <- DEADLOCK
+				   /* start to take into account
+				      event A's context */
+				   /* 4 */
+
+				   folio_unlock B
+				   /* event B that has been valid since 2 */
+		   folio_unlock B
+		   /* event B that has been valid since 3 */
+
+		   mutex_unlock A
+		   /* event A that has been valid since 1 */
+
+				   mutex_unlock A
+				   /* event A that has been valid since 4 */
+
+We can simplify this by labeling each waiting point with [W], each point
+where its event's context starts with [S] and each event with [E]. This
+example will look like after the labeling:
+
+.. code-block::
+
+   context X	   context Y	   context Z
+
+		   [W][S] mutex_lock A
+   [W][S] folio_lock B
+		   [W][S] folio_lock B <- DEADLOCK
+
+				   [W][S] mutex_lock A <- DEADLOCK
+				   [E] folio_unlock B
+		   [E] folio_unlock B
+		   [E] mutex_unlock A
+				   [E] mutex_unlock A
+
+DEPT uses the symbols [W], [S] and [E] in its report as described above.
+The following is an example reported by DEPT for a real problem in
+practice.
+
+.. code-block::
+
+   Link: https://lore.kernel.org/lkml/6383cde5-cf4b-facf-6e07-1378a485657d@I-love.SAKURA.ne.jp/#t
+   Link: https://lore.kernel.org/lkml/1674268856-31807-1-git-send-email-byungchul.park@lge.com/
+
+   ===================================================
+   DEPT: Circular dependency has been detected.
+   6.2.0-rc1-00025-gb0c20ebf51ac-dirty #28 Not tainted
+   ---------------------------------------------------
+   summary
+   ---------------------------------------------------
+   *** DEADLOCK ***
+
+   context A
+       [S] lock(&ni->ni_lock:0)
+       [W] folio_wait_bit_common(PG_locked_map:0)
+       [E] unlock(&ni->ni_lock:0)
+
+   context B
+       [S] (unknown)(PG_locked_map:0)
+       [W] lock(&ni->ni_lock:0)
+       [E] folio_unlock(PG_locked_map:0)
+
+   [S]: start of the event context
+   [W]: the wait blocked
+   [E]: the event not reachable
+   ---------------------------------------------------
+   context A's detail
+   ---------------------------------------------------
+   context A
+       [S] lock(&ni->ni_lock:0)
+       [W] folio_wait_bit_common(PG_locked_map:0)
+       [E] unlock(&ni->ni_lock:0)
+
+   [S] lock(&ni->ni_lock:0):
+   [<ffffffff82b396fb>] ntfs3_setattr+0x54b/0xd40
+   stacktrace:
+         ntfs3_setattr+0x54b/0xd40
+         notify_change+0xcb3/0x1430
+         do_truncate+0x149/0x210
+         path_openat+0x21a3/0x2a90
+         do_filp_open+0x1ba/0x410
+         do_sys_openat2+0x16d/0x4e0
+         __x64_sys_creat+0xcd/0x120
+         do_syscall_64+0x41/0xc0
+         entry_SYSCALL_64_after_hwframe+0x63/0xcd
+
+   [W] folio_wait_bit_common(PG_locked_map:0):
+   [<ffffffff81b228b0>] truncate_inode_pages_range+0x9b0/0xf20
+   stacktrace:
+         folio_wait_bit_common+0x5e0/0xaf0
+         truncate_inode_pages_range+0x9b0/0xf20
+         truncate_pagecache+0x67/0x90
+         ntfs3_setattr+0x55a/0xd40
+         notify_change+0xcb3/0x1430
+         do_truncate+0x149/0x210
+         path_openat+0x21a3/0x2a90
+         do_filp_open+0x1ba/0x410
+         do_sys_openat2+0x16d/0x4e0
+         __x64_sys_creat+0xcd/0x120
+         do_syscall_64+0x41/0xc0
+         entry_SYSCALL_64_after_hwframe+0x63/0xcd
+
+   [E] unlock(&ni->ni_lock:0):
+   (N/A)
+   ---------------------------------------------------
+   context B's detail
+   ---------------------------------------------------
+   context B
+       [S] (unknown)(PG_locked_map:0)
+       [W] lock(&ni->ni_lock:0)
+       [E] folio_unlock(PG_locked_map:0)
+
+   [S] (unknown)(PG_locked_map:0):
+   (N/A)
+
+   [W] lock(&ni->ni_lock:0):
+   [<ffffffff82b009ec>] attr_data_get_block+0x32c/0x19f0
+   stacktrace:
+         attr_data_get_block+0x32c/0x19f0
+         ntfs_get_block_vbo+0x264/0x1330
+         __block_write_begin_int+0x3bd/0x14b0
+         block_write_begin+0xb9/0x4d0
+         ntfs_write_begin+0x27e/0x480
+         generic_perform_write+0x256/0x570
+         __generic_file_write_iter+0x2ae/0x500
+         ntfs_file_write_iter+0x66d/0x1d70
+         do_iter_readv_writev+0x20b/0x3c0
+         do_iter_write+0x188/0x710
+         vfs_iter_write+0x74/0xa0
+         iter_file_splice_write+0x745/0xc90
+         direct_splice_actor+0x114/0x180
+         splice_direct_to_actor+0x33b/0x8b0
+         do_splice_direct+0x1b7/0x280
+         do_sendfile+0xb49/0x1310
+
+   [E] folio_unlock(PG_locked_map:0):
+   [<ffffffff81f10222>] generic_write_end+0xf2/0x440
+   stacktrace:
+         generic_write_end+0xf2/0x440
+         ntfs_write_end+0x42e/0x980
+         generic_perform_write+0x316/0x570
+         __generic_file_write_iter+0x2ae/0x500
+         ntfs_file_write_iter+0x66d/0x1d70
+         do_iter_readv_writev+0x20b/0x3c0
+         do_iter_write+0x188/0x710
+         vfs_iter_write+0x74/0xa0
+         iter_file_splice_write+0x745/0xc90
+         direct_splice_actor+0x114/0x180
+         splice_direct_to_actor+0x33b/0x8b0
+         do_splice_direct+0x1b7/0x280
+         do_sendfile+0xb49/0x1310
+         __x64_sys_sendfile64+0x1d0/0x210
+         do_syscall_64+0x41/0xc0
+         entry_SYSCALL_64_after_hwframe+0x63/0xcd
+   ---------------------------------------------------
+   information that might be helpful
+   ---------------------------------------------------
+   CPU: 1 PID: 8060 Comm: a.out Not tainted
+	6.2.0-rc1-00025-gb0c20ebf51ac-dirty #28
+   Hardware name: QEMU Standard PC (i440FX + PIIX, 1996),
+	BIOS Bochs 01/01/2011
+   Call Trace:
+    <TASK>
+    dump_stack_lvl+0xf2/0x169
+    print_circle.cold+0xca4/0xd28
+    ? lookup_dep+0x240/0x240
+    ? extend_queue+0x223/0x300
+    cb_check_dl+0x1e7/0x260
+    bfs+0x27b/0x610
+    ? print_circle+0x240/0x240
+    ? llist_add_batch+0x180/0x180
+    ? extend_queue_rev+0x300/0x300
+    ? __add_dep+0x60f/0x810
+    add_dep+0x221/0x5b0
+    ? __add_idep+0x310/0x310
+    ? add_iecxt+0x1bc/0xa60
+    ? add_iecxt+0x1bc/0xa60
+    ? add_iecxt+0x1bc/0xa60
+    ? add_iecxt+0x1bc/0xa60
+    __dept_wait+0x600/0x1490
+    ? add_iecxt+0x1bc/0xa60
+    ? truncate_inode_pages_range+0x9b0/0xf20
+    ? check_new_class+0x790/0x790
+    ? dept_enirq_transition+0x519/0x9c0
+    dept_wait+0x159/0x3b0
+    ? truncate_inode_pages_range+0x9b0/0xf20
+    folio_wait_bit_common+0x5e0/0xaf0
+    ? filemap_get_folios_contig+0xa30/0xa30
+    ? dept_enirq_transition+0x519/0x9c0
+    ? lock_is_held_type+0x10e/0x160
+    ? lock_is_held_type+0x11e/0x160
+    truncate_inode_pages_range+0x9b0/0xf20
+    ? truncate_inode_partial_folio+0xba0/0xba0
+    ? setattr_prepare+0x142/0xc40
+    truncate_pagecache+0x67/0x90
+    ntfs3_setattr+0x55a/0xd40
+    ? ktime_get_coarse_real_ts64+0x1e5/0x2f0
+    ? ntfs_extend+0x5c0/0x5c0
+    ? mode_strip_sgid+0x210/0x210
+    ? ntfs_extend+0x5c0/0x5c0
+    notify_change+0xcb3/0x1430
+    ? do_truncate+0x149/0x210
+    do_truncate+0x149/0x210
+    ? file_open_root+0x430/0x430
+    ? process_measurement+0x18c0/0x18c0
+    ? ntfs_file_release+0x230/0x230
+    path_openat+0x21a3/0x2a90
+    ? path_lookupat+0x840/0x840
+    ? dept_enirq_transition+0x519/0x9c0
+    ? lock_is_held_type+0x10e/0x160
+    do_filp_open+0x1ba/0x410
+    ? may_open_dev+0xf0/0xf0
+    ? find_held_lock+0x2d/0x110
+    ? lock_release+0x43c/0x830
+    ? dept_ecxt_exit+0x31a/0x590
+    ? _raw_spin_unlock+0x3b/0x50
+    ? alloc_fd+0x2de/0x6e0
+    do_sys_openat2+0x16d/0x4e0
+    ? __ia32_sys_get_robust_list+0x3b0/0x3b0
+    ? build_open_flags+0x6f0/0x6f0
+    ? dept_enirq_transition+0x519/0x9c0
+    ? dept_enirq_transition+0x519/0x9c0
+    ? lock_is_held_type+0x4e/0x160
+    ? lock_is_held_type+0x4e/0x160
+    __x64_sys_creat+0xcd/0x120
+    ? __x64_compat_sys_openat+0x1f0/0x1f0
+    do_syscall_64+0x41/0xc0
+    entry_SYSCALL_64_after_hwframe+0x63/0xcd
+   RIP: 0033:0x7f8b9e4e4469
+   Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48
+   89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48>
+   3d 01 f0 ff ff 73 01 c3 48 8b 0d ff 49 2b 00 f7 d8 64 89 01 48
+   RSP: 002b:00007f8b9eea4ef8 EFLAGS: 00000202 ORIG_RAX: 0000000000000055
+   RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f8b9e4e4469
+   RDX: 0000000000737562 RSI: 0000000000000000 RDI: 0000000020000000
+   RBP: 00007f8b9eea4f20 R08: 0000000000000000 R09: 0000000000000000
+   R10: 0000000000000000 R11: 0000000000000202 R12: 00007fffa75511ee
+   R13: 00007fffa75511ef R14: 00007f8b9ee85000 R15: 0000000000000003
+    </TASK>
+
+Let's take a look at the summary that is the most important part.
+
+.. code-block::
+
+   ---------------------------------------------------
+   summary
+   ---------------------------------------------------
+   *** DEADLOCK ***
+
+   context A
+       [S] lock(&ni->ni_lock:0)
+       [W] folio_wait_bit_common(PG_locked_map:0)
+       [E] unlock(&ni->ni_lock:0)
+
+   context B
+       [S] (unknown)(PG_locked_map:0)
+       [W] lock(&ni->ni_lock:0)
+       [E] folio_unlock(PG_locked_map:0)
+
+   [S]: start of the event context
+   [W]: the wait blocked
+   [E]: the event not reachable
+
+The summary shows the following scenario:
+
+.. code-block::
+
+   context A	   context B	   context ?(unknown)
+
+				   [S] folio_lock(&f1)
+   [S] lock(&ni->ni_lock:0)
+   [W] folio_wait_bit_common(PG_locked_map:0)
+
+		   [W] lock(&ni->ni_lock:0)
+		   [E] folio_unlock(&f1)
+
+   [E] unlock(&ni->ni_lock:0)
+
+Adding comments to describe DEPT's view in detail:
+
+.. code-block::
+
+   context A	   context B	   context ?(unknown)
+
+				   [S] folio_lock(&f1)
+				   /* start to take into account context
+				      B heading for folio_unlock(&f1) */
+				   /* 1 */
+   [S] lock(&ni->ni_lock:0)
+   /* start to take into account this context heading for
+      unlock(&ni->ni_lock:0) */
+   /* 2 */
+
+   [W] folio_wait_bit_common(PG_locked_map:0) (= folio_lock(&f1))
+   /* might wait for folio_unlock(&f1) */
+
+		   [W] lock(&ni->ni_lock:0)
+		   /* might wait for unlock(&ni->ni_lock:0) */
+
+		   [E] folio_unlock(&f1)
+		   /* event that has been valid since 1 */
+
+   [E] unlock(&ni->ni_lock:0)
+   /* event that has been valid since 2 */
+
+Let's build up a dependency graph with this report. Firstly, context A:
+
+.. code-block::
+
+   context A
+
+   [S] lock(&ni->ni_lock:0)
+   /* start to take into account this context heading for
+      unlock(&ni->ni_lock:0) */
+   /* 2 */
+
+   [W] folio_wait_bit_common(PG_locked_map:0) (= folio_lock(&f1))
+   /* might wait for folio_unlock(&f1) */
+
+   [E] unlock(&ni->ni_lock:0)
+   /* event that has been valid since 2 */
+
+There is one event, unlock(&ni->ni_lock:0). There is a wait,
+folio_lock(&f1), between 2 and the event. Which means
+unlock(&ni->ni_lock:0) is not reachable if folio_lock(&f1) cannot be
+awakened by the owner's folio_unlock(&f1). Therefore, we can say
+unlock(&ni->ni_lock:0) depends on folio_unlock(&f1), say,
+'unlock(&ni->ni_lock:0) -> folio_unlock(&f1)'.
+
+The graph will look like after adding the dependency:
+
+.. code-block::
+
+   unlock(&ni->ni_lock:0) -> folio_unlock(&f1)
+
+   where 'A -> B' means that event A depends on event B.
+
+Secondly, context B:
+
+.. code-block::
+
+   context B
+
+   [W] lock(&ni->ni_lock:0)
+   /* might wait for unlock(&ni->ni_lock:0) */
+
+   [E] folio_unlock(&f1)
+   /* event that has been valid since 1 */
+
+There is also one event, folio_unlock(&f1). There is a wait,
+lock(&ni->ni_lock:0), between 1 and the event. Which means
+folio_unlock(&f1) is not reachable if lock(&ni->ni_lock:0) cannot be
+awakened by the owner's unlock(&ni->ni_lock:0). Therefore, we can say
+folio_unlock(&f1) depends on unlock(&ni->ni_lock:0), say,
+'folio_unlock(&f1) -> unlock(&ni->ni_lock:0)'.
+
+The graph will look like after adding the dependency:
+
+.. code-block::
+
+    -> unlock(&ni->ni_lock:0) -> folio_unlock(&f1) -
+   /                                                \
+   \                                                /
+    ------------------------------------------------
+
+   where 'A -> B' means that event A depends on event B.
+
+A new loop has been created. So DEPT can report it as a deadlock! Cool!
+
+CONCLUSION
+
+DEPT works awesome!
diff --git a/Documentation/dev-tools/dept_api.rst b/Documentation/dev-tools/dept_api.rst
new file mode 100644
index 00000000000000..6706d206f6bfa8
--- /dev/null
+++ b/Documentation/dev-tools/dept_api.rst
@@ -0,0 +1,124 @@
+DEPT(DEPendency Tracker) APIs
+=============================
+
+Started by Byungchul Park <max.byungchul.park@sk.com>
+
+SDT(Single-event Dependency Tracker) APIs
+-----------------------------------------
+Use these APIs to annotate either wait or event. These have been already
+applied to the existing synchronization primitives e.g. waitqueue, swait,
+wait_for_completion(), dma fence and so on. The basic APIs of SDT are:
+
+.. code-block:: c
+
+   /*
+    * After defining 'struct dept_map map', initialize the instance.
+    */
+   sdt_map_init(map);
+
+   /*
+    * Place just before the interesting wait.
+    */
+   sdt_wait(map);
+
+   /*
+    * Place just before the interesting event.
+    */
+   sdt_event(map);
+
+The advanced APIs of SDT are:
+
+.. code-block:: c
+
+   /*
+    * After defining 'struct dept_map map', initialize the instance
+    * using an external key.
+    */
+   sdt_map_init_key(map, key);
+
+   /*
+    * Place just before the interesting timeout wait.
+    */
+   sdt_wait_timeout(map, time);
+
+   /*
+    * Use sdt_might_sleep_start() and sdt_might_sleep_end() in pair.
+    * Place at the start of the interesting section that might enter
+    * schedule() or its family that needs to be woken up by
+    * try_to_wake_up().
+    */
+   sdt_might_sleep_start(map);
+
+   /*
+    * Use sdt_might_sleep_start_timeout() and sdt_might_sleep_end() in
+    * pair. Place at the start of the interesting section that might
+    * enter schedule_timeout() or its family that needs to be woken up
+    * by try_to_wake_up().
+    */
+   sdt_might_sleep_start_timeout(map, time);
+
+   /*
+    * Use sdt_might_sleep_start() and sdt_might_sleep_end() in pair.
+    * Place at the end of the interesting section that might enter
+    * schedule(), schedule_timeout() or its family that needs to be
+    * woken up by try_to_wake_up().
+    */
+   sdt_might_sleep_end();
+
+   /*
+    * Use sdt_ecxt_enter() and sdt_ecxt_exit() in pair. Place at the
+    * start of the interesting section where the interesting event might
+    * be triggered.
+    */
+   sdt_ecxt_enter(map);
+
+   /*
+    * Use sdt_ecxt_enter() and sdt_ecxt_exit() in pair. Place at the
+    * end of the interesting section where the interesting event might
+    * be triggered.
+    */
+   sdt_ecxt_exit(map);
+
+
+LDT(Lock Dependency Tracker) APIs
+---------------------------------
+Do not use these APIs directly. These are wrappers for typical locks
+that have been already applied to major locks internally e.g. spin lock,
+mutex, rwlock and so on. The APIs of LDT are:
+
+.. code-block:: c
+
+   ldt_init(map, key, sub, name);
+   ldt_lock(map, sub_local, try, nest, ip);
+   ldt_rlock(map, sub_local, try, nest, ip, queued);
+   ldt_wlock(map, sub_local, try, nest, ip);
+   ldt_unlock(map, ip);
+   ldt_downgrade(map, ip);
+   ldt_set_class(map, name, key, sub_local, ip);
+
+
+Raw APIs
+--------
+Do not use these APIs directly. The raw APIs of dept are:
+
+.. code-block:: c
+
+   dept_free_range(start, size);
+   dept_map_init(map, key, sub, name);
+   dept_map_reinit(map, key, sub, name);
+   dept_ext_wgen_init(ext_wgen);
+   dept_map_copy(map_to, map_from);
+   dept_wait(map, wait_flags, ip, wait_func, sub_local, time);
+   dept_stage_wait(map, key, ip, wait_func, time);
+   dept_request_event_wait_commit();
+   dept_clean_stage();
+   dept_ttwu_stage_wait(task, ip);
+   dept_ecxt_enter(map, evt_flags, ip, ecxt_func, evt_func, sub_local);
+   dept_ecxt_holding(map, evt_flags);
+   dept_request_event(map, ext_wgen);
+   dept_event(map, evt_flags, ip, evt_func, ext_wgen);
+   dept_ecxt_exit(map, evt_flags, ip);
+   dept_ecxt_enter_nokeep(map);
+   dept_key_init(key);
+   dept_key_destroy(key);
+   dept_map_ecxt_modify(map, cur_evt_flags, key, evt_flags, ip, ecxt_func, evt_func, sub_local);
diff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/index.rst
index 59cbb77b33ff4d..0f37940e4c6e57 100644
--- a/Documentation/dev-tools/index.rst
+++ b/Documentation/dev-tools/index.rst
@@ -23,6 +23,8 @@ Documentation/process/debugging/index.rst
    coccinelle
    context-analysis
    sparse
+   dept
+   dept_api
    kcov
    gcov
    kasan
diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c
index 35afcfcac5910e..e56044492166f8 100644
--- a/drivers/dma-buf/dma-fence.c
+++ b/drivers/dma-buf/dma-fence.c
@@ -16,6 +16,7 @@
 #include <linux/dma-fence.h>
 #include <linux/sched/signal.h>
 #include <linux/seq_file.h>
+#include <linux/dept_sdt.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/dma_fence.h>
@@ -502,7 +503,7 @@ void dma_fence_signal(struct dma_fence *fence)
 EXPORT_SYMBOL(dma_fence_signal);
 
 /**
- * dma_fence_wait_timeout - sleep until the fence gets signaled
+ * __dma_fence_wait_timeout - sleep until the fence gets signaled
  * or until timeout elapses
  * @fence: the fence to wait on
  * @intr: if true, do an interruptible wait
@@ -520,7 +521,7 @@ EXPORT_SYMBOL(dma_fence_signal);
  * See also dma_fence_wait() and dma_fence_wait_any_timeout().
  */
 signed long
-dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout)
+__dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout)
 {
 	signed long ret;
 
@@ -549,7 +550,7 @@ dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout)
 	}
 	return ret;
 }
-EXPORT_SYMBOL(dma_fence_wait_timeout);
+EXPORT_SYMBOL(__dma_fence_wait_timeout);
 
 /**
  * dma_fence_release - default release function for fences
@@ -785,7 +786,7 @@ dma_fence_default_wait_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
 }
 
 /**
- * dma_fence_default_wait - default sleep until the fence gets signaled
+ * __dma_fence_default_wait - default sleep until the fence gets signaled
  * or until timeout elapses
  * @fence: the fence to wait on
  * @intr: if true, do an interruptible wait
@@ -797,7 +798,7 @@ dma_fence_default_wait_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
  * functions taking a jiffies timeout.
  */
 signed long
-dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)
+__dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)
 {
 	struct default_wait_cb cb;
 	unsigned long flags;
@@ -822,6 +823,7 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)
 	cb.task = current;
 	list_add(&cb.base.node, &fence->cb_list);
 
+	sdt_might_sleep_start_timeout(NULL, timeout);
 	while (!dma_fence_test_signaled_flag(fence) && ret > 0) {
 		if (intr)
 			__set_current_state(TASK_INTERRUPTIBLE);
@@ -835,6 +837,7 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)
 		if (ret > 0 && intr && signal_pending(current))
 			ret = -ERESTARTSYS;
 	}
+	sdt_might_sleep_end();
 
 	if (!list_empty(&cb.base.node))
 		list_del(&cb.base.node);
@@ -844,7 +847,7 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)
 	spin_unlock_irqrestore(fence->lock, flags);
 	return ret;
 }
-EXPORT_SYMBOL(dma_fence_default_wait);
+EXPORT_SYMBOL(__dma_fence_default_wait);
 
 static bool
 dma_fence_test_signaled_any(struct dma_fence **fences, uint32_t count,
@@ -864,7 +867,7 @@ dma_fence_test_signaled_any(struct dma_fence **fences, uint32_t count,
 }
 
 /**
- * dma_fence_wait_any_timeout - sleep until any fence gets signaled
+ * __dma_fence_wait_any_timeout - sleep until any fence gets signaled
  * or until timeout elapses
  * @fences: array of fences to wait on
  * @count: number of fences to wait on
@@ -884,7 +887,7 @@ dma_fence_test_signaled_any(struct dma_fence **fences, uint32_t count,
  * See also dma_fence_wait() and dma_fence_wait_timeout().
  */
 signed long
-dma_fence_wait_any_timeout(struct dma_fence **fences, uint32_t count,
+__dma_fence_wait_any_timeout(struct dma_fence **fences, uint32_t count,
 			   bool intr, signed long timeout, uint32_t *idx)
 {
 	struct default_wait_cb *cb;
@@ -924,6 +927,7 @@ dma_fence_wait_any_timeout(struct dma_fence **fences, uint32_t count,
 		}
 	}
 
+	sdt_might_sleep_start_timeout(NULL, timeout);
 	while (ret > 0) {
 		if (intr)
 			set_current_state(TASK_INTERRUPTIBLE);
@@ -938,6 +942,7 @@ dma_fence_wait_any_timeout(struct dma_fence **fences, uint32_t count,
 		if (ret > 0 && intr && signal_pending(current))
 			ret = -ERESTARTSYS;
 	}
+	sdt_might_sleep_end();
 
 	__set_current_state(TASK_RUNNING);
 
@@ -950,7 +955,7 @@ dma_fence_wait_any_timeout(struct dma_fence **fences, uint32_t count,
 
 	return ret;
 }
-EXPORT_SYMBOL(dma_fence_wait_any_timeout);
+EXPORT_SYMBOL(__dma_fence_wait_any_timeout);
 
 /**
  * DOC: deadline hints
diff --git a/include/linux/completion.h b/include/linux/completion.h
index fb291567657432..e50f7d9b4b974f 100644
--- a/include/linux/completion.h
+++ b/include/linux/completion.h
@@ -10,6 +10,7 @@
  */
 
 #include <linux/swait.h>
+#include <linux/dept_sdt.h>
 
 /*
  * struct completion - structure used to maintain state for a "completion"
@@ -26,15 +27,30 @@
 struct completion {
 	unsigned int done;
 	struct swait_queue_head wait;
+	struct dept_map *dmap;
 };
 
-#define init_completion_map(x, m) init_completion(x)
-static inline void complete_acquire(struct completion *x) {}
-static inline void complete_release(struct completion *x) {}
+#define init_completion(x) init_completion_dmap(x, NULL)
+
+/*
+ * XXX: This usage using lockdep's map should be deprecated.
+ */
+#define init_completion_map(x, m) init_completion_dmap(x, NULL)
+
+static inline void complete_acquire(struct completion *x, long timeout)
+{
+}
+
+static inline void complete_release(struct completion *x)
+{
+}
 
 #define COMPLETION_INITIALIZER(work) \
-	{ 0, __SWAIT_QUEUE_HEAD_INITIALIZER((work).wait) }
+	{ 0, __SWAIT_QUEUE_HEAD_INITIALIZER((work).wait), .dmap = NULL, }
 
+/*
+ * XXX: This usage using lockdep's map should be deprecated.
+ */
 #define COMPLETION_INITIALIZER_ONSTACK_MAP(work, map) \
 	(*({ init_completion_map(&(work), &(map)); &(work); }))
 
@@ -75,15 +91,18 @@ static inline void complete_release(struct completion *x) {}
 #endif
 
 /**
- * init_completion - Initialize a dynamically allocated completion
+ * init_completion_dmap - Initialize a dynamically allocated completion
  * @x:  pointer to completion structure that is to be initialized
+ * @dmap:  pointer to external dept's map to be used as a separated map
  *
  * This inline function will initialize a dynamically created completion
  * structure.
  */
-static inline void init_completion(struct completion *x)
+static inline void init_completion_dmap(struct completion *x,
+		struct dept_map *dmap)
 {
 	x->done = 0;
+	x->dmap = dmap;
 	init_swait_queue_head(&x->wait);
 }
 
@@ -99,18 +118,18 @@ static inline void reinit_completion(struct completion *x)
 	x->done = 0;
 }
 
-extern void wait_for_completion(struct completion *);
-extern void wait_for_completion_io(struct completion *);
-extern int wait_for_completion_interruptible(struct completion *x);
-extern int wait_for_completion_killable(struct completion *x);
-extern int wait_for_completion_state(struct completion *x, unsigned int state);
-extern unsigned long wait_for_completion_timeout(struct completion *x,
+extern void __wait_for_completion(struct completion *);
+extern void __wait_for_completion_io(struct completion *);
+extern int __wait_for_completion_interruptible(struct completion *x);
+extern int __wait_for_completion_killable(struct completion *x);
+extern int __wait_for_completion_state(struct completion *x, unsigned int state);
+extern unsigned long __wait_for_completion_timeout(struct completion *x,
 						   unsigned long timeout);
-extern unsigned long wait_for_completion_io_timeout(struct completion *x,
+extern unsigned long __wait_for_completion_io_timeout(struct completion *x,
 						    unsigned long timeout);
-extern long wait_for_completion_interruptible_timeout(
+extern long __wait_for_completion_interruptible_timeout(
 	struct completion *x, unsigned long timeout);
-extern long wait_for_completion_killable_timeout(
+extern long __wait_for_completion_killable_timeout(
 	struct completion *x, unsigned long timeout);
 extern bool try_wait_for_completion(struct completion *x);
 extern bool completion_done(struct completion *x);
@@ -119,4 +138,79 @@ extern void complete(struct completion *);
 extern void complete_on_current_cpu(struct completion *x);
 extern void complete_all(struct completion *);
 
+#define wait_for_completion(x)						\
+({									\
+	sdt_might_sleep_start_timeout((x)->dmap, -1L);			\
+	__wait_for_completion(x);					\
+	sdt_might_sleep_end();						\
+})
+#define wait_for_completion_io(x)					\
+({									\
+	sdt_might_sleep_start_timeout((x)->dmap, -1L);			\
+	__wait_for_completion_io(x);					\
+	sdt_might_sleep_end();						\
+})
+#define wait_for_completion_interruptible(x)				\
+({									\
+	int __ret;							\
+									\
+	sdt_might_sleep_start_timeout((x)->dmap, -1L);			\
+	__ret = __wait_for_completion_interruptible(x);			\
+	sdt_might_sleep_end();						\
+	__ret;								\
+})
+#define wait_for_completion_killable(x)					\
+({									\
+	int __ret;							\
+									\
+	sdt_might_sleep_start_timeout((x)->dmap, -1L);			\
+	__ret = __wait_for_completion_killable(x);			\
+	sdt_might_sleep_end();						\
+	__ret;								\
+})
+#define wait_for_completion_state(x, s)					\
+({									\
+	int __ret;							\
+									\
+	sdt_might_sleep_start_timeout((x)->dmap, -1L);			\
+	__ret = __wait_for_completion_state(x, s);			\
+	sdt_might_sleep_end();						\
+	__ret;								\
+})
+#define wait_for_completion_timeout(x, t)				\
+({									\
+	unsigned long __ret;						\
+									\
+	sdt_might_sleep_start_timeout((x)->dmap, t);			\
+	__ret = __wait_for_completion_timeout(x, t);			\
+	sdt_might_sleep_end();						\
+	__ret;								\
+})
+#define wait_for_completion_io_timeout(x, t)				\
+({									\
+	unsigned long __ret;						\
+									\
+	sdt_might_sleep_start_timeout((x)->dmap, t);			\
+	__ret = __wait_for_completion_io_timeout(x, t);			\
+	sdt_might_sleep_end();						\
+	__ret;								\
+})
+#define wait_for_completion_interruptible_timeout(x, t)			\
+({									\
+	long __ret;							\
+									\
+	sdt_might_sleep_start_timeout((x)->dmap, t);			\
+	__ret = __wait_for_completion_interruptible_timeout(x, t);	\
+	sdt_might_sleep_end();						\
+	__ret;								\
+})
+#define wait_for_completion_killable_timeout(x, t)			\
+({									\
+	long __ret;							\
+									\
+	sdt_might_sleep_start_timeout((x)->dmap, t);			\
+	__ret = __wait_for_completion_killable_timeout(x, t);		\
+	sdt_might_sleep_end();						\
+	__ret;								\
+})
 #endif
diff --git a/include/linux/dept.h b/include/linux/dept.h
new file mode 100644
index 00000000000000..3b8faf5f04cf92
--- /dev/null
+++ b/include/linux/dept.h
@@ -0,0 +1,267 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * DEPT(DEPendency Tracker) - runtime dependency tracker
+ *
+ * Started by Byungchul Park <max.byungchul.park@gmail.com>:
+ *
+ *  Copyright (c) 2020 LG Electronics, Inc., Byungchul Park
+ *  Copyright (c) 2024 SK hynix, Inc., Byungchul Park
+ */
+
+#ifndef __LINUX_DEPT_H
+#define __LINUX_DEPT_H
+
+#ifdef CONFIG_DEPT
+
+struct task_struct;
+
+#define DEPT_MAX_STACK_ENTRY		16
+#define DEPT_MAX_WAIT_HIST		64
+#define DEPT_MAX_ECXT_HELD		48
+
+#define DEPT_MAX_SUBCLASSES		24
+#define DEPT_MAX_SUBCLASSES_EVT		3
+#define DEPT_MAX_SUBCLASSES_USR		(DEPT_MAX_SUBCLASSES / DEPT_MAX_SUBCLASSES_EVT)
+#define DEPT_MAX_SUBCLASSES_CACHE	2
+
+enum {
+	DEPT_CXT_SIRQ = 0,
+	DEPT_CXT_HIRQ,
+	DEPT_CXT_IRQS_NR,
+	DEPT_CXT_PROCESS = DEPT_CXT_IRQS_NR,
+	DEPT_CXTS_NR
+};
+
+#define DEPT_SIRQF			(1UL << DEPT_CXT_SIRQ)
+#define DEPT_HIRQF			(1UL << DEPT_CXT_HIRQ)
+
+struct dept_key {
+	union {
+		/*
+		 * Each byte-wise address will be used as its key.
+		 */
+		char			base[DEPT_MAX_SUBCLASSES];
+
+		/*
+		 * for caching the main class pointer
+		 */
+		struct dept_class	*classes[DEPT_MAX_SUBCLASSES_CACHE];
+	};
+};
+
+struct dept_map {
+	const char			*name;
+	struct dept_key			*keys;
+
+	/*
+	 * keep lockdep map to handle lockdep_set_lock_cmp_fn().
+	 */
+	void				*lockdep_map;
+
+	/*
+	 * subclass that can be set from user
+	 */
+	int				sub_u;
+
+	/*
+	 * It's local copy for fast access to the associated classes.
+	 * Also used for dept_key for static maps.
+	 */
+	struct dept_key			map_key;
+
+	/*
+	 * wait timestamp associated to this map
+	 */
+	unsigned int			wgen;
+
+	/*
+	 * whether this map should be going to be checked or not
+	 */
+	bool				nocheck;
+};
+
+#define DEPT_MAP_INITIALIZER(n, k)					\
+{									\
+	.name = #n,							\
+	.keys = (struct dept_key *)(k),					\
+	.lockdep_map = NULL,						\
+	.sub_u = 0,							\
+	.map_key = { .classes = { NULL, } },				\
+	.wgen = 0U,							\
+	.nocheck = false,						\
+}
+
+struct dept_ecxt_held {
+	/*
+	 * associated event context
+	 */
+	struct dept_ecxt		*ecxt;
+
+	/*
+	 * unique key for this dept_ecxt_held
+	 */
+	struct dept_map			*map;
+
+	/*
+	 * class of the ecxt of this dept_ecxt_held
+	 */
+	struct dept_class		*class;
+
+	/*
+	 * the wgen when the event context started
+	 */
+	unsigned int			wgen;
+
+	/*
+	 * subclass that only works in the local context
+	 */
+	int				sub_l;
+};
+
+struct dept_wait_hist {
+	/*
+	 * associated wait
+	 */
+	struct dept_wait		*wait;
+
+	/*
+	 * unique id of all waits system-wise until wrapped
+	 */
+	unsigned int			wgen;
+
+	/*
+	 * local context id to identify IRQ context
+	 */
+	unsigned int			ctxt_id;
+};
+
+/*
+ * for subsystems that requires compact use of memory e.g. struct page
+ */
+struct dept_ext_wgen {
+	unsigned int wgen;
+};
+
+enum {
+	DEPT_PAGE_DEFAULT = 0,
+	DEPT_PAGE_REGFILE_CACHE,	/* regular file page cache */
+	DEPT_PAGE_BDEV_CACHE,		/* block device cache */
+	DEPT_PAGE_USAGE_NR,		/* nr of usages options */
+};
+
+#define DEPT_PAGE_USAGE_SHIFT 16
+#define DEPT_PAGE_USAGE_MASK ((1U << DEPT_PAGE_USAGE_SHIFT) - 1)
+#define DEPT_PAGE_USAGE_PENDING_MASK (DEPT_PAGE_USAGE_MASK << DEPT_PAGE_USAGE_SHIFT)
+
+/*
+ * Identify each page's usage type
+ */
+struct dept_page_usage {
+	/*
+	 * low 16 bits  : the current usage type
+	 * high 16 bits : usage type requested to be set
+	 *
+	 * Do not apply usage type on request immediately but postpone
+	 * it until the next use of PG flags.  For example, if the page
+	 * is already within a PG_locked critical section, regard it as
+	 * DEPT_PAGE_DEFAULT temporarily at least until the section ends
+	 * e.g. folio_unlock() since it's still unclear which usage type
+	 * the page acts within the section.
+	 */
+	atomic_t type; /* Update and read atomically */
+};
+
+void dept_stop_emerg(void);
+void dept_on(void);
+void dept_off(void);
+void dept_init(void);
+void dept_task_init(struct task_struct *t);
+void dept_task_exit(struct task_struct *t);
+void dept_free_range(void *start, unsigned int sz);
+
+void dept_map_init(struct dept_map *m, struct dept_key *k, int sub_u, const char *n);
+void dept_map_reinit(struct dept_map *m, struct dept_key *k, int sub_u, const char *n);
+void dept_ext_wgen_init(struct dept_ext_wgen *ewg);
+void dept_map_copy(struct dept_map *to, struct dept_map *from);
+void dept_wait(struct dept_map *m, unsigned long w_f, unsigned long ip, const char *w_fn, int sub_l, long timeout);
+void dept_stage_wait(struct dept_map *m, struct dept_key *k, unsigned long ip, const char *w_fn, long timeout);
+void dept_request_event_wait_commit(void);
+void dept_clean_stage(void);
+void dept_ttwu_stage_wait(struct task_struct *t, unsigned long ip);
+void dept_ecxt_enter(struct dept_map *m, unsigned long e_f, unsigned long ip, const char *c_fn, const char *e_fn, int sub_l);
+bool dept_ecxt_holding(struct dept_map *m, unsigned long e_f);
+void dept_request_event(struct dept_map *m, struct dept_ext_wgen *ewg);
+void dept_event(struct dept_map *m, unsigned long e_f, unsigned long ip, const char *e_fn, struct dept_ext_wgen *ewg);
+void dept_ecxt_exit(struct dept_map *m, unsigned long e_f, unsigned long ip);
+void dept_sched_enter(void);
+void dept_sched_exit(void);
+void dept_update_cxt(void);
+
+static inline void dept_ecxt_enter_nokeep(struct dept_map *m)
+{
+	dept_ecxt_enter(m, 0UL, 0UL, NULL, NULL, 0);
+}
+
+/*
+ * for users who want to manage external keys
+ */
+void dept_key_init(struct dept_key *k);
+void dept_key_destroy(struct dept_key *k);
+void dept_map_ecxt_modify(struct dept_map *m, unsigned long e_f, struct dept_key *new_k, unsigned long new_e_f, unsigned long new_ip, const char *new_c_fn, const char *new_e_fn, int new_sub_l);
+
+void dept_softirq_enter(void);
+void dept_hardirq_enter(void);
+void dept_softirqs_on_ip(unsigned long ip);
+void dept_hardirqs_on(void);
+void dept_softirqs_off(void);
+void dept_hardirqs_off(void);
+
+#define dept_set_lockdep_map(m, lockdep_m) ({ (m)->lockdep_map = lockdep_m; })
+#else /* !CONFIG_DEPT */
+struct dept_key { };
+struct dept_map { };
+struct dept_ext_wgen { };
+struct dept_page_usage { };
+
+#define DEPT_MAP_INITIALIZER(n, k) { }
+
+#define dept_stop_emerg()				do { } while (0)
+#define dept_on()					do { } while (0)
+#define dept_off()					do { } while (0)
+#define dept_init()					do { } while (0)
+#define dept_task_init(t)				do { } while (0)
+#define dept_task_exit(t)				do { } while (0)
+#define dept_free_range(s, sz)				do { } while (0)
+
+#define dept_map_init(m, k, su, n)			do { (void)(n); (void)(k); } while (0)
+#define dept_map_reinit(m, k, su, n)			do { (void)(n); (void)(k); } while (0)
+#define dept_ext_wgen_init(wg)				do { } while (0)
+#define dept_map_copy(t, f)				do { } while (0)
+#define dept_wait(m, w_f, ip, w_fn, sl, t)		do { (void)(w_fn); } while (0)
+#define dept_stage_wait(m, k, ip, w_fn, t)		do { (void)(k); (void)(w_fn); } while (0)
+#define dept_request_event_wait_commit()		do { } while (0)
+#define dept_clean_stage()				do { } while (0)
+#define dept_ttwu_stage_wait(t, ip)			do { } while (0)
+#define dept_ecxt_enter(m, e_f, ip, c_fn, e_fn, sl)	do { (void)(c_fn); (void)(e_fn); } while (0)
+#define dept_ecxt_holding(m, e_f)			false
+#define dept_request_event(m, wg)			do { } while (0)
+#define dept_event(m, e_f, ip, e_fn, wg)		do { (void)(e_fn); } while (0)
+#define dept_ecxt_exit(m, e_f, ip)			do { } while (0)
+#define dept_sched_enter()				do { } while (0)
+#define dept_sched_exit()				do { } while (0)
+#define dept_update_cxt()				do { } while (0)
+#define dept_ecxt_enter_nokeep(m)			do { } while (0)
+#define dept_key_init(k)				do { (void)(k); } while (0)
+#define dept_key_destroy(k)				do { (void)(k); } while (0)
+#define dept_map_ecxt_modify(m, e_f, n_k, n_e_f, n_ip, n_c_fn, n_e_fn, n_sl) do { (void)(n_k); (void)(n_c_fn); (void)(n_e_fn); } while (0)
+
+#define dept_softirq_enter()				do { } while (0)
+#define dept_hardirq_enter()				do { } while (0)
+#define dept_softirqs_on_ip(ip)				do { } while (0)
+#define dept_hardirqs_on()				do { } while (0)
+#define dept_softirqs_off()				do { } while (0)
+#define dept_hardirqs_off()				do { } while (0)
+
+#define dept_set_lockdep_map(m, lockdep_m)		do { } while (0)
+#endif
+#endif /* __LINUX_DEPT_H */
diff --git a/include/linux/dept_ldt.h b/include/linux/dept_ldt.h
new file mode 100644
index 00000000000000..730af2517ecd41
--- /dev/null
+++ b/include/linux/dept_ldt.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Lock Dependency Tracker
+ *
+ * Started by Byungchul Park <max.byungchul.park@gmail.com>:
+ *
+ *  Copyright (c) 2020 LG Electronics, Inc., Byungchul Park
+ *  Copyright (c) 2024 SK hynix, Inc., Byungchul Park
+ */
+
+#ifndef __LINUX_DEPT_LDT_H
+#define __LINUX_DEPT_LDT_H
+
+#include <linux/dept.h>
+
+#ifdef CONFIG_DEPT
+#define LDT_EVT_L			1UL
+#define LDT_EVT_R			2UL
+#define LDT_EVT_W			1UL
+#define LDT_EVT_RW			(LDT_EVT_R | LDT_EVT_W)
+#define LDT_EVT_ALL			(LDT_EVT_L | LDT_EVT_RW)
+
+#define ldt_init(m, k, su, n)		dept_map_init(m, k, su, n)
+#define ldt_lock(m, sl, t, n, i)					\
+	do {								\
+		if (n)							\
+			dept_ecxt_enter_nokeep(m);			\
+		else if (t)						\
+			dept_ecxt_enter(m, LDT_EVT_L, i, "trylock", "unlock", sl);\
+		else {							\
+			dept_wait(m, LDT_EVT_L, i, "lock", sl, false);	\
+			dept_ecxt_enter(m, LDT_EVT_L, i, "lock", "unlock", sl);\
+		}							\
+	} while (0)
+
+#define ldt_rlock(m, sl, t, n, i, q)					\
+	do {								\
+		if (n)							\
+			dept_ecxt_enter_nokeep(m);			\
+		else if (t)						\
+			dept_ecxt_enter(m, LDT_EVT_R, i, "read_trylock", "read_unlock", sl);\
+		else {							\
+			dept_wait(m, q ? LDT_EVT_RW : LDT_EVT_W, i, "read_lock", sl, false);\
+			dept_ecxt_enter(m, LDT_EVT_R, i, "read_lock", "read_unlock", sl);\
+		}							\
+	} while (0)
+
+#define ldt_wlock(m, sl, t, n, i)					\
+	do {								\
+		if (n)							\
+			dept_ecxt_enter_nokeep(m);			\
+		else if (t)						\
+			dept_ecxt_enter(m, LDT_EVT_W, i, "write_trylock", "write_unlock", sl);\
+		else {							\
+			dept_wait(m, LDT_EVT_RW, i, "write_lock", sl, false);\
+			dept_ecxt_enter(m, LDT_EVT_W, i, "write_lock", "write_unlock", sl);\
+		}							\
+	} while (0)
+
+#define ldt_unlock(m, i)		dept_ecxt_exit(m, LDT_EVT_ALL, i)
+
+#define ldt_downgrade(m, i)						\
+	do {								\
+		if (dept_ecxt_holding(m, LDT_EVT_W))			\
+			dept_map_ecxt_modify(m, LDT_EVT_W, NULL, LDT_EVT_R, i, "downgrade", "read_unlock", -1);\
+	} while (0)
+
+#define ldt_set_class(m, n, k, sl, i)	dept_map_ecxt_modify(m, LDT_EVT_ALL, k, 0UL, i, "lock_set_class", "(any)unlock", sl)
+#else /* !CONFIG_DEPT */
+#define ldt_init(m, k, su, n)		do { (void)(k); } while (0)
+#define ldt_lock(m, sl, t, n, i)	do { } while (0)
+#define ldt_rlock(m, sl, t, n, i, q)	do { } while (0)
+#define ldt_wlock(m, sl, t, n, i)	do { } while (0)
+#define ldt_unlock(m, i)		do { } while (0)
+#define ldt_downgrade(m, i)		do { } while (0)
+#define ldt_set_class(m, n, k, sl, i)	do { } while (0)
+#endif
+#endif /* __LINUX_DEPT_LDT_H */
diff --git a/include/linux/dept_sdt.h b/include/linux/dept_sdt.h
new file mode 100644
index 00000000000000..9cd70affaf35c8
--- /dev/null
+++ b/include/linux/dept_sdt.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Single-event Dependency Tracker
+ *
+ * Started by Byungchul Park <max.byungchul.park@gmail.com>:
+ *
+ *  Copyright (c) 2020 LG Electronics, Inc., Byungchul Park
+ *  Copyright (c) 2024 SK hynix, Inc., Byungchul Park
+ */
+
+#ifndef __LINUX_DEPT_SDT_H
+#define __LINUX_DEPT_SDT_H
+
+#include <linux/kernel.h>
+#include <linux/dept.h>
+
+#ifdef CONFIG_DEPT
+#define sdt_map_init(m)							\
+	do {								\
+		static struct dept_key __key;				\
+		dept_map_init(m, &__key, 0, #m);			\
+	} while (0)
+
+#define sdt_map_init_key(m, k)		dept_map_init(m, k, 0, #m)
+
+#define sdt_wait_timeout(m, t)						\
+	do {								\
+		dept_request_event(m, NULL);				\
+		dept_wait(m, 1UL, _THIS_IP_, __func__, 0, t);		\
+	} while (0)
+#define sdt_wait(m) sdt_wait_timeout(m, -1L)
+
+/*
+ * sdt_might_sleep() and its family will be committed in __schedule()
+ * when it actually gets to __schedule(). Both dept_request_event() and
+ * dept_wait() will be performed on the commit.
+ */
+
+/*
+ * Use the code location as the class key if an explicit map is not used.
+ */
+#define sdt_might_sleep_start_timeout(m, t)				\
+	do {								\
+		struct dept_map *__m = m;				\
+		static struct dept_key __key;				\
+		dept_stage_wait(__m, __m ? NULL : &__key, _THIS_IP_, __func__, t);\
+	} while (0)
+#define sdt_might_sleep_start(m)	sdt_might_sleep_start_timeout(m, -1L)
+#define sdt_might_sleep_end()		dept_clean_stage()
+
+#define sdt_ecxt_enter(m)		dept_ecxt_enter(m, 1UL, _THIS_IP_, "start", "event", 0)
+#define sdt_event(m)			dept_event(m, 1UL, _THIS_IP_, __func__, NULL)
+#define sdt_ecxt_exit(m)		dept_ecxt_exit(m, 1UL, _THIS_IP_)
+#define sdt_request_event(m)		dept_request_event(m, NULL)
+#else /* !CONFIG_DEPT */
+#define sdt_map_init(m)			do { } while (0)
+#define sdt_map_init_key(m, k)		do { (void)(k); } while (0)
+#define sdt_wait_timeout(m, t)		do { } while (0)
+#define sdt_wait(m)			do { } while (0)
+#define sdt_might_sleep_start_timeout(m, t) do { } while (0)
+#define sdt_might_sleep_start(m)	do { } while (0)
+#define sdt_might_sleep_end()		do { } while (0)
+#define sdt_ecxt_enter(m)		do { } while (0)
+#define sdt_event(m)			do { } while (0)
+#define sdt_ecxt_exit(m)		do { } while (0)
+#define sdt_request_event(m)		do { } while (0)
+#endif
+#endif /* __LINUX_DEPT_SDT_H */
diff --git a/include/linux/dept_unit_test.h b/include/linux/dept_unit_test.h
new file mode 100644
index 00000000000000..753ac9ac727c65
--- /dev/null
+++ b/include/linux/dept_unit_test.h
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * DEPT unit test
+ *
+ * Started by Byungchul Park <max.byungchul.park@gmail.com>:
+ *
+ *  Copyright (c) 2025 SK hynix, Inc., Byungchul Park
+ */
+
+#ifndef __LINUX_DEPT_UNIT_TEST_H
+#define __LINUX_DEPT_UNIT_TEST_H
+
+#if defined(CONFIG_DEPT_UNIT_TEST) || defined(CONFIG_DEPT_UNIT_TEST_MODULE)
+struct dept_ut {
+	bool circle_detected;
+
+	int ecxt_stack_total_cnt;
+	int wait_stack_total_cnt;
+	int evnt_stack_total_cnt;
+	int ecxt_stack_valid_cnt;
+	int wait_stack_valid_cnt;
+	int evnt_stack_valid_cnt;
+};
+
+extern struct dept_ut dept_ut_results;
+
+static inline void dept_ut_circle_detect(void)
+{
+	dept_ut_results.circle_detected = true;
+}
+static inline void dept_ut_ecxt_stack_account(bool valid)
+{
+	dept_ut_results.ecxt_stack_total_cnt++;
+
+	if (valid)
+		dept_ut_results.ecxt_stack_valid_cnt++;
+}
+static inline void dept_ut_wait_stack_account(bool valid)
+{
+	dept_ut_results.wait_stack_total_cnt++;
+
+	if (valid)
+		dept_ut_results.wait_stack_valid_cnt++;
+}
+static inline void dept_ut_evnt_stack_account(bool valid)
+{
+	dept_ut_results.evnt_stack_total_cnt++;
+
+	if (valid)
+		dept_ut_results.evnt_stack_valid_cnt++;
+}
+#else
+struct dept_ut {};
+
+#define dept_ut_circle_detect() do { } while (0)
+#define dept_ut_ecxt_stack_account(v) do { } while (0)
+#define dept_ut_wait_stack_account(v) do { } while (0)
+#define dept_ut_evnt_stack_account(v) do { } while (0)
+
+#endif
+#endif /* __LINUX_DEPT_UNIT_TEST_H */
diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h
index d4c92fd3509247..3732849a30b7ee 100644
--- a/include/linux/dma-fence.h
+++ b/include/linux/dma-fence.h
@@ -370,8 +370,22 @@ bool dma_fence_check_and_signal_locked(struct dma_fence *fence);
 void dma_fence_signal_locked(struct dma_fence *fence);
 void dma_fence_signal_timestamp(struct dma_fence *fence, ktime_t timestamp);
 void dma_fence_signal_timestamp_locked(struct dma_fence *fence, ktime_t timestamp);
-signed long dma_fence_default_wait(struct dma_fence *fence,
+signed long __dma_fence_default_wait(struct dma_fence *fence,
 				   bool intr, signed long timeout);
+
+/*
+ * Associate every caller with its own dept map.
+ */
+#define dma_fence_default_wait(f, intr, t)				\
+({									\
+	signed long __ret;						\
+									\
+	sdt_might_sleep_start_timeout(NULL, t);				\
+	__ret = __dma_fence_default_wait(f, intr, t);			\
+	sdt_might_sleep_end();						\
+	__ret;								\
+})
+
 int dma_fence_add_callback(struct dma_fence *fence,
 			   struct dma_fence_cb *cb,
 			   dma_fence_func_t func);
@@ -628,12 +642,37 @@ static inline ktime_t dma_fence_timestamp(struct dma_fence *fence)
 	return fence->timestamp;
 }
 
-signed long dma_fence_wait_timeout(struct dma_fence *,
+signed long __dma_fence_wait_timeout(struct dma_fence *,
 				   bool intr, signed long timeout);
-signed long dma_fence_wait_any_timeout(struct dma_fence **fences,
+signed long __dma_fence_wait_any_timeout(struct dma_fence **fences,
 				       uint32_t count,
 				       bool intr, signed long timeout,
 				       uint32_t *idx);
+/*
+ * Associate every caller with its own dept map.
+ */
+#define dma_fence_wait_timeout(f, intr, t)				\
+({									\
+	signed long __ret;						\
+									\
+	sdt_might_sleep_start_timeout(NULL, t);				\
+	__ret = __dma_fence_wait_timeout(f, intr, t);			\
+	sdt_might_sleep_end();						\
+	__ret;								\
+})
+
+/*
+ * Associate every caller with its own dept map.
+ */
+#define dma_fence_wait_any_timeout(fpp, count, intr, t, idx)		\
+({									\
+	signed long __ret;						\
+									\
+	sdt_might_sleep_start_timeout(NULL, t);				\
+	__ret = __dma_fence_wait_any_timeout(fpp, count, intr, t, idx);	\
+	sdt_might_sleep_end();						\
+	__ret;								\
+})
 
 /**
  * dma_fence_wait - sleep until the fence gets signaled
@@ -649,19 +688,24 @@ signed long dma_fence_wait_any_timeout(struct dma_fence **fences,
  * fence might be freed before return, resulting in undefined behavior.
  *
  * See also dma_fence_wait_timeout() and dma_fence_wait_any_timeout().
+ *
+ * Associate every caller with its own dept map.
  */
-static inline signed long dma_fence_wait(struct dma_fence *fence, bool intr)
-{
-	signed long ret;
-
-	/* Since dma_fence_wait_timeout cannot timeout with
-	 * MAX_SCHEDULE_TIMEOUT, only valid return values are
-	 * -ERESTARTSYS and MAX_SCHEDULE_TIMEOUT.
-	 */
-	ret = dma_fence_wait_timeout(fence, intr, MAX_SCHEDULE_TIMEOUT);
-
-	return ret < 0 ? ret : 0;
-}
+#define dma_fence_wait(f, intr)						\
+({									\
+	signed long __ret;						\
+									\
+	sdt_might_sleep_start_timeout(NULL, MAX_SCHEDULE_TIMEOUT);	\
+	__ret = __dma_fence_wait_timeout(f, intr, MAX_SCHEDULE_TIMEOUT);\
+	sdt_might_sleep_end();						\
+									\
+	/*								\
+	 * Since dma_fence_wait_timeout cannot timeout with		\
+	 * MAX_SCHEDULE_TIMEOUT, only valid return values are		\
+	 * -ERESTARTSYS and MAX_SCHEDULE_TIMEOUT.			\
+	 */								\
+	__ret < 0 ? __ret : 0;						\
+})
 
 void dma_fence_set_deadline(struct dma_fence *fence, ktime_t deadline);
 
diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h
index d57cab4d4c06fd..bb279dbbe74806 100644
--- a/include/linux/hardirq.h
+++ b/include/linux/hardirq.h
@@ -5,6 +5,7 @@
 #include <linux/context_tracking_state.h>
 #include <linux/preempt.h>
 #include <linux/lockdep.h>
+#include <linux/dept.h>
 #include <linux/ftrace_irq.h>
 #include <linux/sched.h>
 #include <linux/vtime.h>
@@ -106,6 +107,7 @@ void irq_exit_rcu(void);
  */
 #define __nmi_enter()						\
 	do {							\
+		dept_off();					\
 		lockdep_off();					\
 		arch_nmi_enter();				\
 		BUG_ON(in_nmi() == NMI_MASK);			\
@@ -128,6 +130,7 @@ void irq_exit_rcu(void);
 		__preempt_count_sub(NMI_OFFSET + HARDIRQ_OFFSET);	\
 		arch_nmi_exit();				\
 		lockdep_on();					\
+		dept_on();					\
 	} while (0)
 
 #define nmi_exit()						\
diff --git a/include/linux/irq-entry-common.h b/include/linux/irq-entry-common.h
index d26d1b1bcbfb97..37ef4f20bdc4c4 100644
--- a/include/linux/irq-entry-common.h
+++ b/include/linux/irq-entry-common.h
@@ -9,6 +9,7 @@
 #include <linux/syscalls.h>
 #include <linux/tick.h>
 #include <linux/unwind_deferred.h>
+#include <linux/dept.h>
 
 #include <asm/entry-common.h>
 
@@ -88,6 +89,9 @@ static __always_inline bool arch_in_rcu_eqs(void) { return false; }
  */
 static __always_inline void enter_from_user_mode(struct pt_regs *regs)
 {
+	/* Make dept work with a new context. */
+	dept_update_cxt();
+
 	arch_enter_from_user_mode(regs);
 	lockdep_hardirqs_off(CALLER_ADDR0);
 
diff --git a/include/linux/irqflags.h b/include/linux/irqflags.h
index 57b074e0cfbbb3..586f5bad4da786 100644
--- a/include/linux/irqflags.h
+++ b/include/linux/irqflags.h
@@ -15,6 +15,7 @@
 #include <linux/irqflags_types.h>
 #include <linux/typecheck.h>
 #include <linux/cleanup.h>
+#include <linux/dept.h>
 #include <asm/irqflags.h>
 #include <asm/percpu.h>
 
@@ -55,8 +56,10 @@ extern void trace_hardirqs_off(void);
 # define lockdep_softirqs_enabled(p)	((p)->softirqs_enabled)
 # define lockdep_hardirq_enter()			\
 do {							\
-	if (__this_cpu_inc_return(hardirq_context) == 1)\
+	if (__this_cpu_inc_return(hardirq_context) == 1) { \
 		current->hardirq_threaded = 0;		\
+		dept_hardirq_enter();			\
+	}						\
 } while (0)
 # define lockdep_hardirq_threaded()		\
 do {						\
@@ -131,6 +134,8 @@ do {						\
 # define lockdep_softirq_enter()		\
 do {						\
 	current->softirq_context++;		\
+	if (current->softirq_context == 1)	\
+		dept_softirq_enter();		\
 } while (0)
 # define lockdep_softirq_exit()			\
 do {						\
@@ -209,6 +214,13 @@ extern void warn_bogus_irq_restore(void);
 		raw_local_irq_disable();		\
 		if (!was_disabled)			\
 			trace_hardirqs_off();		\
+		/*					\
+		 * Just in case that C code has missed	\
+		 * trace_hardirqs_off() at the first	\
+		 * place e.g. disabling irq at asm code.\
+		 */					\
+		else					\
+			dept_hardirqs_off();		\
 	} while (0)
 
 #define local_irq_save(flags)				\
@@ -216,6 +228,13 @@ extern void warn_bogus_irq_restore(void);
 		raw_local_irq_save(flags);		\
 		if (!raw_irqs_disabled_flags(flags))	\
 			trace_hardirqs_off();		\
+		/*					\
+		 * Just in case that C code has missed	\
+		 * trace_hardirqs_off() at the first	\
+		 * place e.g. disabling irq at asm code.\
+		 */					\
+		else					\
+			dept_hardirqs_off();		\
 	} while (0)
 
 #define local_irq_restore(flags)			\
diff --git a/include/linux/local_lock_internal.h b/include/linux/local_lock_internal.h
index 234be7f12c15e5..09255c5a665ffd 100644
--- a/include/linux/local_lock_internal.h
+++ b/include/linux/local_lock_internal.h
@@ -35,6 +35,7 @@ typedef struct local_trylock local_trylock_t;
 		.name = #lockname,			\
 		.wait_type_inner = LD_WAIT_CONFIG,	\
 		.lock_type = LD_LOCK_PERCPU,		\
+		.dmap = DEPT_MAP_INITIALIZER(lockname, NULL),\
 	},						\
 	.owner = NULL,
 
diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h
index 621566345406dd..5113b7053b621e 100644
--- a/include/linux/lockdep.h
+++ b/include/linux/lockdep.h
@@ -12,6 +12,7 @@
 
 #include <linux/lockdep_types.h>
 #include <linux/smp.h>
+#include <linux/dept_ldt.h>
 #include <asm/percpu.h>
 
 struct task_struct;
@@ -39,6 +40,8 @@ static inline void lockdep_copy_map(struct lockdep_map *to,
 	 */
 	for (i = 0; i < NR_LOCKDEP_CACHING_CLASSES; i++)
 		to->class_cache[i] = NULL;
+
+	dept_map_copy(&to->dmap, &from->dmap);
 }
 
 /*
@@ -300,6 +303,7 @@ extern void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie);
 	lockdep_assert_once(!current->lockdep_depth)
 
 #define lockdep_recursing(tsk)	((tsk)->lockdep_recursion)
+extern bool lockdep_recursing_current(void);
 
 #define lockdep_pin_lock(l)	lock_pin_lock(&(l)->dep_map)
 #define lockdep_repin_lock(l,c)	lock_repin_lock(&(l)->dep_map, (c))
@@ -428,7 +432,8 @@ enum xhlock_context_t {
  * Note that _name must not be NULL.
  */
 #define STATIC_LOCKDEP_MAP_INIT(_name, _key) \
-	{ .name = (_name), .key = (void *)(_key), }
+	{ .name = (_name), .key = (void *)(_key), \
+	  .dmap = DEPT_MAP_INITIALIZER(_name, _key) }
 
 static inline void lockdep_invariant_state(bool force) {}
 static inline void lockdep_free_task(struct task_struct *task) {}
@@ -510,33 +515,89 @@ extern bool read_lock_is_recursive(void);
 #define lock_acquire_shared(l, s, t, n, i)		lock_acquire(l, s, t, 1, 1, n, i)
 #define lock_acquire_shared_recursive(l, s, t, n, i)	lock_acquire(l, s, t, 2, 1, n, i)
 
-#define spin_acquire(l, s, t, i)		lock_acquire_exclusive(l, s, t, NULL, i)
-#define spin_acquire_nest(l, s, t, n, i)	lock_acquire_exclusive(l, s, t, n, i)
-#define spin_release(l, i)			lock_release(l, i)
-
-#define rwlock_acquire(l, s, t, i)		lock_acquire_exclusive(l, s, t, NULL, i)
+#define spin_acquire(l, s, t, i)					\
+do {									\
+	ldt_lock(&(l)->dmap, s, t, NULL, i);				\
+	lock_acquire_exclusive(l, s, t, NULL, i);			\
+} while (0)
+#define spin_acquire_nest(l, s, t, n, i)				\
+do {									\
+	ldt_lock(&(l)->dmap, s, t, n, i);				\
+	lock_acquire_exclusive(l, s, t, n, i);				\
+} while (0)
+#define spin_release(l, i)						\
+do {									\
+	ldt_unlock(&(l)->dmap, i);					\
+	lock_release(l, i);						\
+} while (0)
+#define rwlock_acquire(l, s, t, i)					\
+do {									\
+	ldt_wlock(&(l)->dmap, s, t, NULL, i);				\
+	lock_acquire_exclusive(l, s, t, NULL, i);			\
+} while (0)
 #define rwlock_acquire_read(l, s, t, i)					\
 do {									\
+	ldt_rlock(&(l)->dmap, s, t, NULL, i, !read_lock_is_recursive());\
 	if (read_lock_is_recursive())					\
 		lock_acquire_shared_recursive(l, s, t, NULL, i);	\
 	else								\
 		lock_acquire_shared(l, s, t, NULL, i);			\
 } while (0)
-
-#define rwlock_release(l, i)			lock_release(l, i)
-
-#define seqcount_acquire(l, s, t, i)		lock_acquire_exclusive(l, s, t, NULL, i)
-#define seqcount_acquire_read(l, s, t, i)	lock_acquire_shared_recursive(l, s, t, NULL, i)
-#define seqcount_release(l, i)			lock_release(l, i)
-
-#define mutex_acquire(l, s, t, i)		lock_acquire_exclusive(l, s, t, NULL, i)
-#define mutex_acquire_nest(l, s, t, n, i)	lock_acquire_exclusive(l, s, t, n, i)
-#define mutex_release(l, i)			lock_release(l, i)
-
-#define rwsem_acquire(l, s, t, i)		lock_acquire_exclusive(l, s, t, NULL, i)
-#define rwsem_acquire_nest(l, s, t, n, i)	lock_acquire_exclusive(l, s, t, n, i)
-#define rwsem_acquire_read(l, s, t, i)		lock_acquire_shared(l, s, t, NULL, i)
-#define rwsem_release(l, i)			lock_release(l, i)
+#define rwlock_release(l, i)						\
+do {									\
+	ldt_unlock(&(l)->dmap, i);					\
+	lock_release(l, i);						\
+} while (0)
+#define seqcount_acquire(l, s, t, i)					\
+do {									\
+	ldt_wlock(&(l)->dmap, s, t, NULL, i);				\
+	lock_acquire_exclusive(l, s, t, NULL, i);			\
+} while (0)
+#define seqcount_acquire_read(l, s, t, i)				\
+do {									\
+	ldt_rlock(&(l)->dmap, s, t, NULL, i, false);			\
+	lock_acquire_shared_recursive(l, s, t, NULL, i);		\
+} while (0)
+#define seqcount_release(l, i)						\
+do {									\
+	ldt_unlock(&(l)->dmap, i);					\
+	lock_release(l, i);						\
+} while (0)
+#define mutex_acquire(l, s, t, i)					\
+do {									\
+	ldt_lock(&(l)->dmap, s, t, NULL, i);				\
+	lock_acquire_exclusive(l, s, t, NULL, i);			\
+} while (0)
+#define mutex_acquire_nest(l, s, t, n, i)				\
+do {									\
+	ldt_lock(&(l)->dmap, s, t, n, i);				\
+	lock_acquire_exclusive(l, s, t, n, i);				\
+} while (0)
+#define mutex_release(l, i)						\
+do {									\
+	ldt_unlock(&(l)->dmap, i);					\
+	lock_release(l, i);						\
+} while (0)
+#define rwsem_acquire(l, s, t, i)					\
+do {									\
+	ldt_lock(&(l)->dmap, s, t, NULL, i);				\
+	lock_acquire_exclusive(l, s, t, NULL, i);			\
+} while (0)
+#define rwsem_acquire_nest(l, s, t, n, i)				\
+do {									\
+	ldt_lock(&(l)->dmap, s, t, n, i);				\
+	lock_acquire_exclusive(l, s, t, n, i);				\
+} while (0)
+#define rwsem_acquire_read(l, s, t, i)					\
+do {									\
+	ldt_lock(&(l)->dmap, s, t, NULL, i);				\
+	lock_acquire_shared(l, s, t, NULL, i);				\
+} while (0)
+#define rwsem_release(l, i)						\
+do {									\
+	ldt_unlock(&(l)->dmap, i);					\
+	lock_release(l, i);						\
+} while (0)
 
 #define lock_map_acquire(l)			lock_acquire_exclusive(l, 0, 0, NULL, _THIS_IP_)
 #define lock_map_acquire_try(l)			lock_acquire_exclusive(l, 0, 1, NULL, _THIS_IP_)
@@ -570,7 +631,7 @@ DECLARE_PER_CPU(int, hardirqs_enabled);
 DECLARE_PER_CPU(int, hardirq_context);
 DECLARE_PER_CPU(unsigned int, lockdep_recursion);
 
-#define __lockdep_enabled	(debug_locks && !this_cpu_read(lockdep_recursion))
+#define __lockdep_enabled	(debug_locks && !this_cpu_read(lockdep_recursion) && !lockdep_recursing_current())
 
 #define lockdep_assert_irqs_enabled()					\
 do {									\
diff --git a/include/linux/lockdep_types.h b/include/linux/lockdep_types.h
index eae115a2648856..0c3389ed26b6c1 100644
--- a/include/linux/lockdep_types.h
+++ b/include/linux/lockdep_types.h
@@ -11,6 +11,7 @@
 #define __LINUX_LOCKDEP_TYPES_H
 
 #include <linux/types.h>
+#include <linux/dept.h>
 
 #define MAX_LOCKDEP_SUBCLASSES		8UL
 
@@ -77,6 +78,7 @@ struct lock_class_key {
 		struct hlist_node		hash_entry;
 		struct lockdep_subclass_key	subkeys[MAX_LOCKDEP_SUBCLASSES];
 	};
+	struct dept_key				dkey;
 };
 
 extern struct lock_class_key __lockdep_no_validate__;
@@ -195,6 +197,7 @@ struct lockdep_map {
 	int				cpu;
 	unsigned long			ip;
 #endif
+	struct dept_map			dmap;
 };
 
 struct pin_cookie { unsigned int val; };
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 3cc8ae72288601..81dc9999090a8a 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -22,6 +22,7 @@
 #include <linux/types.h>
 #include <linux/rseq_types.h>
 #include <linux/bitmap.h>
+#include <linux/dept.h>
 
 #include <asm/mmu.h>
 
@@ -219,6 +220,9 @@ struct page {
 	struct page *kmsan_shadow;
 	struct page *kmsan_origin;
 #endif
+	struct dept_page_usage usage;
+	struct dept_ext_wgen pg_locked_wgen;
+	struct dept_ext_wgen pg_writeback_wgen;
 } _struct_page_alignment;
 
 /*
diff --git a/include/linux/mmu_notifier.h b/include/linux/mmu_notifier.h
index 8450e18a87c26d..638b1b402d122e 100644
--- a/include/linux/mmu_notifier.h
+++ b/include/linux/mmu_notifier.h
@@ -429,6 +429,14 @@ static inline int mmu_notifier_test_young(struct mm_struct *mm,
 	return 0;
 }
 
+#ifdef CONFIG_DEPT
+void mmu_notifier_invalidate_dept_ecxt_start(struct mmu_notifier_range *range);
+void mmu_notifier_invalidate_dept_ecxt_end(struct mmu_notifier_range *range);
+#else
+static inline void mmu_notifier_invalidate_dept_ecxt_start(struct mmu_notifier_range *range) {}
+static inline void mmu_notifier_invalidate_dept_ecxt_end(struct mmu_notifier_range *range) {}
+#endif
+
 static inline void
 mmu_notifier_invalidate_range_start(struct mmu_notifier_range *range)
 {
@@ -440,6 +448,12 @@ mmu_notifier_invalidate_range_start(struct mmu_notifier_range *range)
 		__mmu_notifier_invalidate_range_start(range);
 	}
 	lock_map_release(&__mmu_notifier_invalidate_range_start_map);
+
+	/*
+	 * From now on, waiters could be there by this start until
+	 * mmu_notifier_invalidate_range_end().
+	 */
+	mmu_notifier_invalidate_dept_ecxt_start(range);
 }
 
 /*
@@ -460,6 +474,12 @@ mmu_notifier_invalidate_range_start_nonblock(struct mmu_notifier_range *range)
 		ret = __mmu_notifier_invalidate_range_start(range);
 	}
 	lock_map_release(&__mmu_notifier_invalidate_range_start_map);
+
+	/*
+	 * From now on, waiters could be there by this start until
+	 * mmu_notifier_invalidate_range_end().
+	 */
+	mmu_notifier_invalidate_dept_ecxt_start(range);
 	return ret;
 }
 
@@ -471,6 +491,12 @@ mmu_notifier_invalidate_range_end(struct mmu_notifier_range *range)
 
 	if (mm_has_notifiers(range->mm))
 		__mmu_notifier_invalidate_range_end(range);
+
+	/*
+	 * The event context that has been started by
+	 * mmu_notifier_invalidate_range_start() ends.
+	 */
+	mmu_notifier_invalidate_dept_ecxt_end(range);
 }
 
 static inline void mmu_notifier_arch_invalidate_secondary_tlbs(struct mm_struct *mm,
diff --git a/include/linux/mutex.h b/include/linux/mutex.h
index ecaa0440f6ec48..3d9bc1a28569af 100644
--- a/include/linux/mutex.h
+++ b/include/linux/mutex.h
@@ -29,6 +29,7 @@ struct device;
 		, .dep_map = {					\
 			.name = #lockname,			\
 			.wait_type_inner = LD_WAIT_SLEEP,	\
+			.dmap = DEPT_MAP_INITIALIZER(lockname, NULL),\
 		}
 #else
 # define __DEP_MAP_MUTEX_INITIALIZER(lockname)
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index f7a0e4af0c7344..ec736811a2c66d 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -198,6 +198,153 @@ enum pageflags {
 
 #ifndef __GENERATING_BOUNDS_H
 
+#ifdef CONFIG_DEPT
+#include <linux/kernel.h>
+#include <linux/dept.h>
+
+extern struct dept_map pg_locked_map;
+extern struct dept_map pg_writeback_map;
+
+static inline void dept_set_page_usage(struct page *p,
+		unsigned int new_type)
+{
+	/*
+	 * Consider the page as DEPT_PAGE_DEFAULT until the next use of
+	 * PG flags e.g. folio_lock().
+	 */
+	unsigned int type = DEPT_PAGE_DEFAULT;
+
+	if (WARN_ON_ONCE(new_type >= DEPT_PAGE_USAGE_NR))
+		return;
+
+	new_type <<= DEPT_PAGE_USAGE_SHIFT;
+	new_type |= type & DEPT_PAGE_USAGE_MASK;
+	atomic_set(&p->usage.type, new_type);
+}
+
+static inline void dept_set_folio_usage(struct folio *f,
+		unsigned int new_type)
+{
+	dept_set_page_usage(&f->page, new_type);
+}
+
+static inline void dept_reset_page_usage(struct page *p)
+{
+	dept_set_page_usage(p, DEPT_PAGE_DEFAULT);
+}
+
+static inline void dept_reset_folio_usage(struct folio *f)
+{
+	dept_reset_page_usage(&f->page);
+}
+
+static inline void dept_update_page_usage(struct page *p)
+{
+	unsigned int type = atomic_read(&p->usage.type);
+	unsigned int new_type;
+
+retry:
+	new_type = type & DEPT_PAGE_USAGE_PENDING_MASK;
+	new_type >>= DEPT_PAGE_USAGE_SHIFT;
+	new_type |= type & DEPT_PAGE_USAGE_PENDING_MASK;
+
+	/*
+	 * Already updated by others.
+	 */
+	if (type == new_type)
+		return;
+
+	if (!atomic_try_cmpxchg(&p->usage.type, &type, new_type))
+		goto retry;
+}
+
+static inline unsigned long dept_event_flags(struct page *p, bool wait)
+{
+	unsigned int type;
+
+	type = atomic_read(&p->usage.type) & DEPT_PAGE_USAGE_MASK;
+
+	if (WARN_ON_ONCE(type >= DEPT_PAGE_USAGE_NR))
+		return 0;
+
+	/*
+	 * wait
+	 */
+	if (wait)
+		return (1UL << DEPT_PAGE_DEFAULT) | (1UL << type);
+
+	/*
+	 * event
+	 */
+	return 1UL << type;
+}
+
+/*
+ * Place the following annotations in its suitable point in code:
+ *
+ *	Annotate dept_page_set_bit() around firstly set_bit*()
+ *	Annotate dept_page_clear_bit() around clear_bit*()
+ *	Annotate dept_page_wait_on_bit() around wait_on_bit*()
+ */
+
+static inline void dept_page_set_bit(struct page *p, int bit_nr)
+{
+	dept_update_page_usage(p);
+
+	if (bit_nr == PG_locked)
+		dept_request_event(&pg_locked_map, &p->pg_locked_wgen);
+	else if (bit_nr == PG_writeback)
+		dept_request_event(&pg_writeback_map, &p->pg_writeback_wgen);
+}
+
+static inline void dept_page_clear_bit(struct page *p, int bit_nr)
+{
+	unsigned long evt_f = dept_event_flags(p, false);
+
+	if (bit_nr == PG_locked)
+		dept_event(&pg_locked_map, evt_f, _RET_IP_, __func__, &p->pg_locked_wgen);
+	else if (bit_nr == PG_writeback)
+		dept_event(&pg_writeback_map, evt_f, _RET_IP_, __func__, &p->pg_writeback_wgen);
+}
+
+static inline void dept_page_wait_on_bit(struct page *p, int bit_nr)
+{
+	unsigned long evt_f;
+
+	dept_update_page_usage(p);
+	evt_f = dept_event_flags(p, true);
+
+	if (bit_nr == PG_locked)
+		dept_wait(&pg_locked_map, evt_f, _RET_IP_, __func__, 0, -1L);
+	else if (bit_nr == PG_writeback)
+		dept_wait(&pg_writeback_map, evt_f, _RET_IP_, __func__, 0, -1L);
+}
+
+static inline void dept_folio_set_bit(struct folio *f, int bit_nr)
+{
+	dept_page_set_bit(&f->page, bit_nr);
+}
+
+static inline void dept_folio_clear_bit(struct folio *f, int bit_nr)
+{
+	dept_page_clear_bit(&f->page, bit_nr);
+}
+
+static inline void dept_folio_wait_on_bit(struct folio *f, int bit_nr)
+{
+	dept_page_wait_on_bit(&f->page, bit_nr);
+}
+#else
+#define dept_set_page_usage(p, t)		do { } while (0)
+#define dept_reset_page_usage(p)		do { } while (0)
+#define dept_page_set_bit(p, bit_nr)		do { } while (0)
+#define dept_page_clear_bit(p, bit_nr)		do { } while (0)
+#define dept_page_wait_on_bit(p, bit_nr)	do { } while (0)
+#define dept_folio_set_bit(f, bit_nr)		do { } while (0)
+#define dept_folio_clear_bit(f, bit_nr)		do { } while (0)
+#define dept_folio_wait_on_bit(f, bit_nr)	do { } while (0)
+#endif
+
 #ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
 DECLARE_STATIC_KEY_FALSE(hugetlb_optimize_vmemmap_key);
 
@@ -419,27 +566,51 @@ static __always_inline bool folio_test_##name(const struct folio *folio) \
 
 #define FOLIO_SET_FLAG(name, page)					\
 static __always_inline void folio_set_##name(struct folio *folio)	\
-{ set_bit(PG_##name, folio_flags(folio, page)); }
+{									\
+	set_bit(PG_##name, folio_flags(folio, page));			\
+	dept_folio_set_bit(folio, PG_##name);				\
+}
 
 #define FOLIO_CLEAR_FLAG(name, page)					\
 static __always_inline void folio_clear_##name(struct folio *folio)	\
-{ clear_bit(PG_##name, folio_flags(folio, page)); }
+{									\
+	clear_bit(PG_##name, folio_flags(folio, page));			\
+	dept_folio_clear_bit(folio, PG_##name);				\
+}
 
 #define __FOLIO_SET_FLAG(name, page)					\
 static __always_inline void __folio_set_##name(struct folio *folio)	\
-{ __set_bit(PG_##name, folio_flags(folio, page)); }
+{									\
+	__set_bit(PG_##name, folio_flags(folio, page));			\
+	dept_folio_set_bit(folio, PG_##name);				\
+}
 
 #define __FOLIO_CLEAR_FLAG(name, page)					\
 static __always_inline void __folio_clear_##name(struct folio *folio)	\
-{ __clear_bit(PG_##name, folio_flags(folio, page)); }
+{									\
+	__clear_bit(PG_##name, folio_flags(folio, page));		\
+	dept_folio_clear_bit(folio, PG_##name);				\
+}
 
 #define FOLIO_TEST_SET_FLAG(name, page)					\
 static __always_inline bool folio_test_set_##name(struct folio *folio)	\
-{ return test_and_set_bit(PG_##name, folio_flags(folio, page)); }
+{									\
+	bool __ret = test_and_set_bit(PG_##name, folio_flags(folio, page)); \
+									\
+	if (!__ret)							\
+		dept_folio_set_bit(folio, PG_##name);			\
+	return __ret;							\
+}
 
 #define FOLIO_TEST_CLEAR_FLAG(name, page)				\
 static __always_inline bool folio_test_clear_##name(struct folio *folio) \
-{ return test_and_clear_bit(PG_##name, folio_flags(folio, page)); }
+{									\
+	bool __ret = test_and_clear_bit(PG_##name, folio_flags(folio, page)); \
+									\
+	if (__ret)							\
+		dept_folio_clear_bit(folio, PG_##name);			\
+	return __ret;							\
+}
 
 #define FOLIO_FLAG(name, page)						\
 FOLIO_TEST_FLAG(name, page)						\
@@ -454,32 +625,54 @@ static __always_inline int Page##uname(const struct page *page)		\
 #define SETPAGEFLAG(uname, lname, policy)				\
 FOLIO_SET_FLAG(lname, FOLIO_##policy)					\
 static __always_inline void SetPage##uname(struct page *page)		\
-{ set_bit(PG_##lname, &policy(page, 1)->flags.f); }
+{									\
+	set_bit(PG_##lname, &policy(page, 1)->flags.f);			\
+	dept_page_set_bit(page, PG_##lname);				\
+}
 
 #define CLEARPAGEFLAG(uname, lname, policy)				\
 FOLIO_CLEAR_FLAG(lname, FOLIO_##policy)					\
 static __always_inline void ClearPage##uname(struct page *page)		\
-{ clear_bit(PG_##lname, &policy(page, 1)->flags.f); }
+{									\
+	clear_bit(PG_##lname, &policy(page, 1)->flags.f);			\
+	dept_page_clear_bit(page, PG_##lname);				\
+}
 
 #define __SETPAGEFLAG(uname, lname, policy)				\
 __FOLIO_SET_FLAG(lname, FOLIO_##policy)					\
 static __always_inline void __SetPage##uname(struct page *page)		\
-{ __set_bit(PG_##lname, &policy(page, 1)->flags.f); }
+{									\
+	__set_bit(PG_##lname, &policy(page, 1)->flags.f);			\
+	dept_page_set_bit(page, PG_##lname);				\
+}
 
 #define __CLEARPAGEFLAG(uname, lname, policy)				\
 __FOLIO_CLEAR_FLAG(lname, FOLIO_##policy)				\
 static __always_inline void __ClearPage##uname(struct page *page)	\
-{ __clear_bit(PG_##lname, &policy(page, 1)->flags.f); }
+{									\
+	__clear_bit(PG_##lname, &policy(page, 1)->flags.f);		\
+	dept_page_clear_bit(page, PG_##lname);				\
+}
 
 #define TESTSETFLAG(uname, lname, policy)				\
 FOLIO_TEST_SET_FLAG(lname, FOLIO_##policy)				\
 static __always_inline int TestSetPage##uname(struct page *page)	\
-{ return test_and_set_bit(PG_##lname, &policy(page, 1)->flags.f); }
+{									\
+	bool ret = test_and_set_bit(PG_##lname, &policy(page, 1)->flags.f);\
+	if (!ret)							\
+		dept_page_set_bit(page, PG_##lname);			\
+	return ret;							\
+}
 
 #define TESTCLEARFLAG(uname, lname, policy)				\
 FOLIO_TEST_CLEAR_FLAG(lname, FOLIO_##policy)				\
 static __always_inline int TestClearPage##uname(struct page *page)	\
-{ return test_and_clear_bit(PG_##lname, &policy(page, 1)->flags.f); }
+{									\
+	bool ret = test_and_clear_bit(PG_##lname, &policy(page, 1)->flags.f);\
+	if (ret)							\
+		dept_page_clear_bit(page, PG_##lname);			\
+	return ret;							\
+}
 
 #define PAGEFLAG(uname, lname, policy)					\
 	TESTPAGEFLAG(uname, lname, policy)				\
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 31a848485ad9d9..6605800ba3ad8b 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -1119,7 +1119,12 @@ void folio_unlock(struct folio *folio);
  */
 static inline bool folio_trylock(struct folio *folio)
 {
-	return likely(!test_and_set_bit_lock(PG_locked, folio_flags(folio, 0)));
+	bool ret = !test_and_set_bit_lock(PG_locked, folio_flags(folio, 0));
+
+	if (ret)
+		dept_page_set_bit(&folio->page, PG_locked);
+
+	return likely(ret);
 }
 
 /*
@@ -1155,6 +1160,16 @@ static inline bool trylock_page(struct page *page)
 static inline void folio_lock(struct folio *folio)
 {
 	might_sleep();
+
+	/*
+	 * dept_page_wait_on_bit() will be called if __folio_lock() goes
+	 * through a real wait path.  However, for better job to detect
+	 * *potential* deadlocks, let's assume that folio_lock() always
+	 * goes through wait so that dept can take into account all the
+	 * potential cases.
+	 */
+	dept_page_wait_on_bit(&folio->page, PG_locked);
+
 	if (!folio_trylock(folio))
 		__folio_lock(folio);
 }
@@ -1175,6 +1190,15 @@ static inline void lock_page(struct page *page)
 	struct folio *folio;
 	might_sleep();
 
+	/*
+	 * dept_page_wait_on_bit() will be called if __folio_lock() goes
+	 * through a real wait path.  However, for better job to detect
+	 * *potential* deadlocks, let's assume that lock_page() always
+	 * goes through wait so that dept can take into account all the
+	 * potential cases.
+	 */
+	dept_page_wait_on_bit(page, PG_locked);
+
 	folio = page_folio(page);
 	if (!folio_trylock(folio))
 		__folio_lock(folio);
@@ -1193,6 +1217,17 @@ static inline void lock_page(struct page *page)
 static inline int folio_lock_killable(struct folio *folio)
 {
 	might_sleep();
+
+	/*
+	 * dept_page_wait_on_bit() will be called if
+	 * __folio_lock_killable() goes through a real wait path.
+	 * However, for better job to detect *potential* deadlocks,
+	 * let's assume that folio_lock_killable() always goes through
+	 * wait so that dept can take into account all the potential
+	 * cases.
+	 */
+	dept_page_wait_on_bit(&folio->page, PG_locked);
+
 	if (!folio_trylock(folio))
 		return __folio_lock_killable(folio);
 	return 0;
diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h
index c8cb010d655ebe..ca9522f0882bcc 100644
--- a/include/linux/percpu-rwsem.h
+++ b/include/linux/percpu-rwsem.h
@@ -22,7 +22,7 @@ struct percpu_rw_semaphore {
 };
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
-#define __PERCPU_RWSEM_DEP_MAP_INIT(lockname)	.dep_map = { .name = #lockname },
+#define __PERCPU_RWSEM_DEP_MAP_INIT(lockname)	.dep_map = { .name = #lockname, .dmap = DEPT_MAP_INITIALIZER(lockname, NULL) },
 #else
 #define __PERCPU_RWSEM_DEP_MAP_INIT(lockname)
 #endif
diff --git a/include/linux/percpu.h b/include/linux/percpu.h
index 85bf8dd9f08740..dd74321d4bbd03 100644
--- a/include/linux/percpu.h
+++ b/include/linux/percpu.h
@@ -43,7 +43,11 @@
 # define PERCPU_DYNAMIC_SIZE_SHIFT      12
 #endif /* LOCKDEP and PAGE_SIZE > 4KiB */
 #else
+#if defined(CONFIG_DEPT) && !defined(CONFIG_PAGE_SIZE_4KB)
+#define PERCPU_DYNAMIC_SIZE_SHIFT      11
+#else
 #define PERCPU_DYNAMIC_SIZE_SHIFT      10
+#endif /* DEPT and PAGE_SIZE > 4KiB */
 #endif
 
 /*
diff --git a/include/linux/rcupdate_wait.h b/include/linux/rcupdate_wait.h
index 4c92d4291cce7a..ee598e70b4bc7c 100644
--- a/include/linux/rcupdate_wait.h
+++ b/include/linux/rcupdate_wait.h
@@ -19,17 +19,20 @@ struct rcu_synchronize {
 
 	/* This is for debugging. */
 	struct rcu_gp_oldstate oldstate;
+	struct dept_map dmap;
+	struct dept_key dkey;
 };
 void wakeme_after_rcu(struct rcu_head *head);
 
 void __wait_rcu_gp(bool checktiny, unsigned int state, int n, call_rcu_func_t *crcu_array,
-		   struct rcu_synchronize *rs_array);
+		   struct rcu_synchronize *rs_array, struct dept_key *dkey);
 
 #define _wait_rcu_gp(checktiny, state, ...) \
-do {												\
-	call_rcu_func_t __crcu_array[] = { __VA_ARGS__ };					\
-	struct rcu_synchronize __rs_array[ARRAY_SIZE(__crcu_array)];				\
-	__wait_rcu_gp(checktiny, state, ARRAY_SIZE(__crcu_array), __crcu_array, __rs_array);	\
+do {													\
+	call_rcu_func_t __crcu_array[] = { __VA_ARGS__ };						\
+	static struct dept_key __key;									\
+	struct rcu_synchronize __rs_array[ARRAY_SIZE(__crcu_array)];					\
+	__wait_rcu_gp(checktiny, state, ARRAY_SIZE(__crcu_array), __crcu_array, __rs_array, &__key);	\
 } while (0)
 
 #define wait_rcu_gp(...) _wait_rcu_gp(false, TASK_UNINTERRUPTIBLE, __VA_ARGS__)
diff --git a/include/linux/rtmutex.h b/include/linux/rtmutex.h
index ede4c6bf6f2266..ac68c3e5e2ecce 100644
--- a/include/linux/rtmutex.h
+++ b/include/linux/rtmutex.h
@@ -91,6 +91,7 @@ do { \
 	.dep_map = {					\
 		.name = #mutexname,			\
 		.wait_type_inner = LD_WAIT_SLEEP,	\
+		.dmap = DEPT_MAP_INITIALIZER(mutexname, NULL),\
 	}
 #else
 #define __DEP_MAP_RT_MUTEX_INITIALIZER(mutexname)
diff --git a/include/linux/rwlock_types.h b/include/linux/rwlock_types.h
index d5e7316401e75a..f2ff62ef4c3661 100644
--- a/include/linux/rwlock_types.h
+++ b/include/linux/rwlock_types.h
@@ -10,6 +10,7 @@
 	.dep_map = {							\
 		.name = #lockname,					\
 		.wait_type_inner = LD_WAIT_CONFIG,			\
+		.dmap = DEPT_MAP_INITIALIZER(lockname, NULL),		\
 	}
 #else
 # define RW_DEP_MAP_INIT(lockname)
diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h
index 9bf1d93d3d7ba3..47ab3fcee48b69 100644
--- a/include/linux/rwsem.h
+++ b/include/linux/rwsem.h
@@ -22,6 +22,7 @@
 	.dep_map = {					\
 		.name = #lockname,			\
 		.wait_type_inner = LD_WAIT_SLEEP,	\
+		.dmap = DEPT_MAP_INITIALIZER(lockname, NULL),\
 	},
 #else
 # define __RWSEM_DEP_MAP_INIT(lockname)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 5a5d3dbc9cdf33..b2fbcf0f00f483 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -50,6 +50,7 @@
 #include <linux/unwind_deferred_types.h>
 #include <asm/kmap_size.h>
 #include <linux/time64.h>
+#include <linux/dept.h>
 #ifndef COMPILE_OFFSETS
 #include <generated/rq-offsets.h>
 #endif
@@ -817,6 +818,114 @@ struct kmap_ctrl {
 #endif
 };
 
+#ifdef CONFIG_DEPT
+struct dept_task {
+	/*
+	 * all event contexts that have entered and before exiting
+	 */
+	struct dept_ecxt_held		ecxt_held[DEPT_MAX_ECXT_HELD];
+	int				ecxt_held_pos;
+
+	/*
+	 * ring buffer holding all waits that have happened
+	 */
+	struct dept_wait_hist		wait_hist[DEPT_MAX_WAIT_HIST];
+	int				wait_hist_pos;
+
+	/*
+	 * sequential id to identify each context
+	 */
+	unsigned int			cxt_id[DEPT_CXTS_NR];
+
+	/*
+	 * for tracking IRQ-enabled points with cross-event
+	 */
+	unsigned int			wgen_enirq[DEPT_CXT_IRQS_NR];
+
+	/*
+	 * for keeping up-to-date IRQ-enabled points
+	 */
+	unsigned long			enirq_ip[DEPT_CXT_IRQS_NR];
+
+	/*
+	 * for reserving a current stack instance at each operation
+	 */
+	struct dept_stack		*stack;
+
+	/*
+	 * for preventing recursive call into DEPT engine
+	 */
+	int				recursive;
+
+	/*
+	 * for preventing reentrance to WARN*() while warning
+	 */
+	int				in_warning;
+
+	/*
+	 * for staging data to commit a wait
+	 */
+	struct dept_map			stage_m;
+	struct dept_map			*stage_real_m;
+	bool				stage_sched_map;
+	const char			*stage_w_fn;
+	unsigned long			stage_ip;
+	bool				stage_timeout;
+	struct dept_stack		*stage_wait_stack;
+	arch_spinlock_t			stage_lock;
+
+	/*
+	 * the number of missing ecxts
+	 */
+	int				missing_ecxt;
+
+	/*
+	 * for tracking IRQ-enable state
+	 */
+	bool				hardirqs_enabled;
+	bool				softirqs_enabled;
+
+	/*
+	 * whether the current is on do_exit()
+	 */
+	bool				task_exit;
+
+	/*
+	 * whether the current is running __schedule()
+	 */
+	bool				in_sched;
+};
+
+#define DEPT_TASK_INITIALIZER(t)				\
+{								\
+	.wait_hist = { { .wait = NULL, } },			\
+	.ecxt_held_pos = 0,					\
+	.wait_hist_pos = 0,					\
+	.cxt_id = { 0U },					\
+	.wgen_enirq = { 0U },					\
+	.enirq_ip = { 0UL },					\
+	.stack = NULL,						\
+	.recursive = 0,						\
+	.in_warning = 0,					\
+	.stage_m = DEPT_MAP_INITIALIZER((t)->stage_m, NULL),	\
+	.stage_real_m = NULL,					\
+	.stage_sched_map = false,				\
+	.stage_w_fn = NULL,					\
+	.stage_ip = 0UL,					\
+	.stage_timeout = false,					\
+	.stage_wait_stack = NULL,				\
+	.stage_lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED,\
+	.missing_ecxt = 0,					\
+	.hardirqs_enabled = false,				\
+	.softirqs_enabled = false,				\
+	.task_exit = false,					\
+	.in_sched = false,					\
+}
+#else
+struct dept_task { };
+#define DEPT_TASK_INITIALIZER(t) { }
+#endif
+
 struct task_struct {
 #ifdef CONFIG_THREAD_INFO_IN_TASK
 	/*
@@ -1271,6 +1380,8 @@ struct task_struct {
 	struct held_lock		held_locks[MAX_LOCK_DEPTH];
 #endif
 
+	struct dept_task		dept_task;
+
 #if defined(CONFIG_UBSAN) && !defined(CONFIG_UBSAN_TRAP)
 	unsigned int			in_ubsan;
 #endif
diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h
index 5a40252b833486..efc93acf161213 100644
--- a/include/linux/seqlock.h
+++ b/include/linux/seqlock.h
@@ -52,7 +52,7 @@ static inline void __seqcount_init(seqcount_t *s, const char *name,
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 
 # define SEQCOUNT_DEP_MAP_INIT(lockname)				\
-		.dep_map = { .name = #lockname }
+		.dep_map = { .name = #lockname, .dmap = DEPT_MAP_INITIALIZER(lockname, NULL) }
 
 /**
  * seqcount_init() - runtime initializer for seqcount_t
diff --git a/include/linux/spinlock_types_raw.h b/include/linux/spinlock_types_raw.h
index e5644ab2161f8c..5f245afdd77caa 100644
--- a/include/linux/spinlock_types_raw.h
+++ b/include/linux/spinlock_types_raw.h
@@ -32,11 +32,13 @@ typedef struct raw_spinlock raw_spinlock_t;
 	.dep_map = {					\
 		.name = #lockname,			\
 		.wait_type_inner = LD_WAIT_SPIN,	\
+		.dmap = DEPT_MAP_INITIALIZER(lockname, NULL),\
 	}
 # define SPIN_DEP_MAP_INIT(lockname)			\
 	.dep_map = {					\
 		.name = #lockname,			\
 		.wait_type_inner = LD_WAIT_CONFIG,	\
+		.dmap = DEPT_MAP_INITIALIZER(lockname, NULL),\
 	}
 
 # define LOCAL_SPIN_DEP_MAP_INIT(lockname)		\
@@ -44,6 +46,7 @@ typedef struct raw_spinlock raw_spinlock_t;
 		.name = #lockname,			\
 		.wait_type_inner = LD_WAIT_CONFIG,	\
 		.lock_type = LD_LOCK_PERCPU,		\
+		.dmap = DEPT_MAP_INITIALIZER(lockname, NULL),\
 	}
 #else
 # define RAW_SPIN_DEP_MAP_INIT(lockname)
diff --git a/include/linux/srcu.h b/include/linux/srcu.h
index bb44a0bd769683..50c78f71ad4328 100644
--- a/include/linux/srcu.h
+++ b/include/linux/srcu.h
@@ -53,7 +53,7 @@ int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,
 	__init_srcu_struct_fast_updown((ssp), #ssp, &__srcu_key); \
 })
 
-#define __SRCU_DEP_MAP_INIT(srcu_name)	.dep_map = { .name = #srcu_name },
+#define __SRCU_DEP_MAP_INIT(srcu_name)	.dep_map = { .name = #srcu_name, .dmap = DEPT_MAP_INITIALIZER(srcu_name, NULL) },
 #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
 
 int init_srcu_struct(struct srcu_struct *ssp);
diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h
index f46d1fb8f71ae2..666e42a17a317c 100644
--- a/include/linux/sunrpc/xprt.h
+++ b/include/linux/sunrpc/xprt.h
@@ -211,6 +211,14 @@ enum xprt_transports {
 
 struct rpc_sysfs_xprt;
 struct rpc_xprt {
+	/*
+	 * Place struct rcu_head within the first 4096 bytes of struct
+	 * rpc_xprt if sizeof(struct rpc_xprt) > 4096, so that
+	 * kfree_rcu() can simply work assuming that.  See the comment
+	 * in kfree_rcu().
+	 */
+	struct rcu_head		rcu;
+
 	struct kref		kref;		/* Reference count */
 	const struct rpc_xprt_ops *ops;		/* transport methods */
 	unsigned int		id;		/* transport id */
@@ -317,7 +325,6 @@ struct rpc_xprt {
 #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
 	struct dentry		*debugfs;		/* debugfs directory */
 #endif
-	struct rcu_head		rcu;
 	const struct xprt_class	*xprt_class;
 	struct rpc_sysfs_xprt	*xprt_sysfs;
 	bool			main; /*mark if this is the 1st transport */
diff --git a/include/linux/swait.h b/include/linux/swait.h
index d324419482a0f5..233acdf55e9bcc 100644
--- a/include/linux/swait.h
+++ b/include/linux/swait.h
@@ -6,6 +6,7 @@
 #include <linux/stddef.h>
 #include <linux/spinlock.h>
 #include <linux/wait.h>
+#include <linux/dept_sdt.h>
 #include <asm/current.h>
 
 /*
@@ -161,6 +162,7 @@ extern void finish_swait(struct swait_queue_head *q, struct swait_queue *wait);
 	struct swait_queue __wait;					\
 	long __ret = ret;						\
 									\
+	sdt_might_sleep_start_timeout(NULL, __ret);			\
 	INIT_LIST_HEAD(&__wait.task_list);				\
 	for (;;) {							\
 		long __int = prepare_to_swait_event(&wq, &__wait, state);\
@@ -176,6 +178,7 @@ extern void finish_swait(struct swait_queue_head *q, struct swait_queue *wait);
 		cmd;							\
 	}								\
 	finish_swait(&wq, &__wait);					\
+	sdt_might_sleep_end();						\
 __out:	__ret;								\
 })
 
diff --git a/include/linux/wait.h b/include/linux/wait.h
index dce055e6add390..a9524bc8630b77 100644
--- a/include/linux/wait.h
+++ b/include/linux/wait.h
@@ -7,6 +7,7 @@
 #include <linux/list.h>
 #include <linux/stddef.h>
 #include <linux/spinlock.h>
+#include <linux/dept_sdt.h>
 
 #include <asm/current.h>
 
@@ -305,6 +306,7 @@ extern void init_wait_entry(struct wait_queue_entry *wq_entry, int flags);
 	struct wait_queue_entry __wq_entry;					\
 	long __ret = ret;	/* explicit shadow */				\
 										\
+	sdt_might_sleep_start_timeout(NULL, __ret);				\
 	init_wait_entry(&__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0);	\
 	for (;;) {								\
 		long __int = prepare_to_wait_event(&wq_head, &__wq_entry, state);\
@@ -323,6 +325,7 @@ extern void init_wait_entry(struct wait_queue_entry *wq_entry, int flags);
 			break;							\
 	}									\
 	finish_wait(&wq_head, &__wq_entry);					\
+	sdt_might_sleep_end();							\
 __out:	__ret;									\
 })
 
diff --git a/include/linux/wait_bit.h b/include/linux/wait_bit.h
index 9e29d79fc790af..9885ac4e1ded55 100644
--- a/include/linux/wait_bit.h
+++ b/include/linux/wait_bit.h
@@ -6,6 +6,7 @@
  * Linux wait-bit related types and methods:
  */
 #include <linux/wait.h>
+#include <linux/dept_sdt.h>
 
 struct wait_bit_key {
 	unsigned long		*flags;
@@ -257,6 +258,7 @@ extern wait_queue_head_t *__var_waitqueue(void *p);
 	struct wait_bit_queue_entry __wbq_entry;			\
 	long __ret = ret; /* explicit shadow */				\
 									\
+	sdt_might_sleep_start_timeout(NULL, __ret);			\
 	init_wait_var_entry(&__wbq_entry, var,				\
 			    exclusive ? WQ_FLAG_EXCLUSIVE : 0);		\
 	for (;;) {							\
@@ -274,6 +276,7 @@ extern wait_queue_head_t *__var_waitqueue(void *p);
 		cmd;							\
 	}								\
 	finish_wait(__wq_head, &__wbq_entry.wq_entry);			\
+	sdt_might_sleep_end();						\
 __out:	__ret;								\
 })
 
diff --git a/init/init_task.c b/init/init_task.c
index 5c838757fc10eb..79aae8437b10bf 100644
--- a/init/init_task.c
+++ b/init/init_task.c
@@ -14,6 +14,7 @@
 #include <linux/numa.h>
 #include <linux/scs.h>
 #include <linux/plist.h>
+#include <linux/dept.h>
 
 #include <linux/uaccess.h>
 
@@ -230,6 +231,7 @@ struct task_struct init_task __aligned(L1_CACHE_BYTES) = {
 	.curr_chain_key = INITIAL_CHAIN_KEY,
 	.lockdep_recursion = 0,
 #endif
+	.dept_task = DEPT_TASK_INITIALIZER(init_task),
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 	.ret_stack		= NULL,
 	.tracing_graph_pause	= ATOMIC_INIT(0),
diff --git a/init/main.c b/init/main.c
index 1cb395dd94e43f..9c0603cca965a4 100644
--- a/init/main.c
+++ b/init/main.c
@@ -66,6 +66,7 @@
 #include <linux/debug_locks.h>
 #include <linux/debugobjects.h>
 #include <linux/lockdep.h>
+#include <linux/dept.h>
 #include <linux/kmemleak.h>
 #include <linux/padata.h>
 #include <linux/pid_namespace.h>
@@ -1150,6 +1151,7 @@ void start_kernel(void)
 		      panic_param);
 
 	lockdep_init();
+	dept_init();
 
 	/*
 	 * Need to run this when irqs are enabled, because it wants
diff --git a/kernel/Makefile b/kernel/Makefile
index 6785982013dced..a1856fb9887c29 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -59,6 +59,7 @@ obj-y += dma/
 obj-y += entry/
 obj-y += unwind/
 obj-$(CONFIG_MODULES) += module/
+obj-y += dependency/
 
 obj-$(CONFIG_KCMP) += kcmp.o
 obj-$(CONFIG_FREEZER) += freezer.o
diff --git a/kernel/cpu.c b/kernel/cpu.c
index bc4f7a9ba64e62..ba9d8961359047 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -542,7 +542,7 @@ int lockdep_is_cpus_write_held(void)
 
 static void lockdep_acquire_cpus_lock(void)
 {
-	rwsem_acquire(&cpu_hotplug_lock.dep_map, 0, 0, _THIS_IP_);
+	rwsem_acquire(&cpu_hotplug_lock.dep_map, 0, 1, _THIS_IP_);
 }
 
 static void lockdep_release_cpus_lock(void)
diff --git a/kernel/dependency/Makefile b/kernel/dependency/Makefile
new file mode 100644
index 00000000000000..fc584ca8712429
--- /dev/null
+++ b/kernel/dependency/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_DEPT) += dept.o
+obj-$(CONFIG_DEPT) += dept_proc.o
+obj-$(CONFIG_DEPT_UNIT_TEST) += dept_unit_test.o
diff --git a/kernel/dependency/dept.c b/kernel/dependency/dept.c
new file mode 100644
index 00000000000000..bcff14f2004662
--- /dev/null
+++ b/kernel/dependency/dept.c
@@ -0,0 +1,3222 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DEPT(DEPendency Tracker) - Runtime dependency tracker
+ *
+ * Started by Byungchul Park <max.byungchul.park@gmail.com>:
+ *
+ *  Copyright (c) 2020 LG Electronics, Inc., Byungchul Park
+ *  Copyright (c) 2024 SK hynix, Inc., Byungchul Park
+ *
+ * DEPT provides a general way to detect potential deadlocks at runtime
+ * and the interest is not limited to typical lock but to every
+ * synchronization primitives.
+ *
+ * The following ideas were borrowed from LOCKDEP:
+ *
+ *    1) Use a graph to track relationship between classes.
+ *    2) Prevent performance regression using hash.
+ *
+ * The following items were enhanced from LOCKDEP:
+ *
+ *    1) Cover more deadlock cases.
+ *    2) Allow multiple reports.
+ *
+ * TODO: Both LOCKDEP and DEPT should co-exist until DEPT is considered
+ * stable. Then the dependency check routine should be replaced with
+ * DEPT after. It should finally look like:
+ *
+ *
+ *
+ * As is:
+ *
+ *    LOCKDEP
+ *    +-----------------------------------------+
+ *    | Lock usage correctness check            | <-> locks
+ *    |                                         |
+ *    |                                         |
+ *    | +-------------------------------------+ |
+ *    | | Dependency check                    | |
+ *    | | (by tracking lock acquisition order)| |
+ *    | +-------------------------------------+ |
+ *    |                                         |
+ *    +-----------------------------------------+
+ *
+ *    DEPT
+ *    +-----------------------------------------+
+ *    | Dependency check                        | <-> waits/events
+ *    | (by tracking wait and event context)    |
+ *    +-----------------------------------------+
+ *
+ *
+ *
+ * To be:
+ *
+ *    LOCKDEP
+ *    +-----------------------------------------+
+ *    | Lock usage correctness check            | <-> locks
+ *    |                                         |
+ *    |                                         |
+ *    |       (Request dependency check)        |
+ *    |                    T                    |
+ *    +--------------------|--------------------+
+ *                         |
+ *    DEPT                 V
+ *    +-----------------------------------------+
+ *    | Dependency check                        | <-> waits/events
+ *    | (by tracking wait and event context)    |
+ *    +-----------------------------------------+
+ */
+
+#include <linux/sched.h>
+#include <linux/stacktrace.h>
+#include <linux/spinlock.h>
+#include <linux/kallsyms.h>
+#include <linux/hash.h>
+#include <linux/dept.h>
+#include <linux/utsname.h>
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
+#include <linux/irq_work.h>
+#include <linux/vmalloc.h>
+#include <linux/dept_unit_test.h>
+#include "dept_internal.h"
+
+struct dept_ut dept_ut_results;
+EXPORT_SYMBOL_GPL(dept_ut_results);
+
+static int dept_stop;
+static int dept_per_cpu_ready;
+
+static inline struct dept_task *dept_task(void)
+{
+	return &current->dept_task;
+}
+
+#define DEPT_READY_WARN (!oops_in_progress && !dept_task()->in_warning)
+
+/*
+ * Make all operations using DEPT_WARN_ON() fail on oops_in_progress and
+ * prevent warning message.
+ */
+#define DEPT_WARN_ON_ONCE(c)						\
+	({								\
+		int __ret = !!(c);					\
+									\
+		if (likely(DEPT_READY_WARN)) {				\
+			++dept_task()->in_warning;			\
+			WARN_ONCE(c, "DEPT_WARN_ON_ONCE: " #c);		\
+			--dept_task()->in_warning;			\
+		}							\
+		__ret;							\
+	})
+
+#define DEPT_WARN_ONCE(s...)						\
+	({								\
+		if (likely(DEPT_READY_WARN)) {				\
+			++dept_task()->in_warning;			\
+			WARN_ONCE(1, "DEPT_WARN_ONCE: " s);		\
+			--dept_task()->in_warning;			\
+		}							\
+	})
+
+#define DEPT_WARN_ON(c)							\
+	({								\
+		int __ret = !!(c);					\
+									\
+		if (likely(DEPT_READY_WARN)) {				\
+			++dept_task()->in_warning;			\
+			WARN(c, "DEPT_WARN_ON: " #c);			\
+			--dept_task()->in_warning;			\
+		}							\
+		__ret;							\
+	})
+
+#define DEPT_WARN(s...)							\
+	({								\
+		if (likely(DEPT_READY_WARN)) {				\
+			++dept_task()->in_warning;			\
+			WARN(1, "DEPT_WARN: " s);			\
+			--dept_task()->in_warning;			\
+		}							\
+	})
+
+#define DEPT_STOP(s...)							\
+	({								\
+		WRITE_ONCE(dept_stop, 1);				\
+		if (likely(DEPT_READY_WARN)) {				\
+			++dept_task()->in_warning;			\
+			WARN(1, "DEPT_STOP: " s);			\
+			--dept_task()->in_warning;			\
+		}							\
+	})
+
+#define DEPT_INFO_ONCE(s...)	pr_warn_once("DEPT_INFO_ONCE: " s)
+#define DEPT_INFO(s...)		pr_warn("DEPT_INFO: " s)
+
+static arch_spinlock_t dept_spin = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
+static arch_spinlock_t dept_pool_spin = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
+
+/*
+ * The DEPT internal engine should be cautious when using external functions
+ * (e.g., printk) during reporting, as such usage might cause untrackable
+ * deadlocks.
+ */
+static atomic_t dept_outworld = ATOMIC_INIT(0);
+
+static void dept_outworld_enter(void)
+{
+	atomic_inc(&dept_outworld);
+}
+
+static void dept_outworld_exit(void)
+{
+	atomic_dec(&dept_outworld);
+}
+
+static bool dept_outworld_entered(void)
+{
+	return atomic_read(&dept_outworld);
+}
+
+static bool dept_lock(void)
+{
+	while (!arch_spin_trylock(&dept_spin))
+		if (unlikely(dept_outworld_entered()))
+			return false;
+	return true;
+}
+
+static void dept_unlock(void)
+{
+	arch_spin_unlock(&dept_spin);
+}
+
+void dept_stop_emerg(void)
+{
+	WRITE_ONCE(dept_stop, 1);
+}
+EXPORT_SYMBOL_GPL(dept_stop_emerg);
+
+enum bfs_ret {
+	BFS_CONTINUE,
+	BFS_DONE,
+	BFS_SKIP,
+};
+
+static bool before(unsigned int a, unsigned int b)
+{
+	return (int)(a - b) < 0;
+}
+
+static bool valid_stack(struct dept_stack *s)
+{
+	return s && s->nr > 0;
+}
+
+static bool valid_class(struct dept_class *c)
+{
+	return c->key;
+}
+
+static void invalidate_class(struct dept_class *c)
+{
+	c->key = 0UL;
+}
+
+static struct dept_ecxt *dep_e(struct dept_dep *d)
+{
+	return d->ecxt;
+}
+
+static struct dept_wait *dep_w(struct dept_dep *d)
+{
+	return d->wait;
+}
+
+static struct dept_class *dep_fc(struct dept_dep *d)
+{
+	return dep_e(d)->class;
+}
+
+static struct dept_class *dep_tc(struct dept_dep *d)
+{
+	return dep_w(d)->class;
+}
+
+static const char *irq_str(int irq)
+{
+	if (irq == DEPT_CXT_SIRQ)
+		return "softirq";
+	if (irq == DEPT_CXT_HIRQ)
+		return "hardirq";
+	return "(unknown)";
+}
+
+/*
+ * DEPT doesn't work when it's stopped by DEPT_STOP() or when running in a
+ * NMI context.
+ */
+static bool dept_working(void)
+{
+	return !READ_ONCE(dept_stop) && !in_nmi();
+}
+
+/*
+ * Even k == NULL is considered a valid key because it would use
+ * &->map_key as the key in that case.
+ */
+extern struct lock_class_key __lockdep_no_validate__;
+static bool valid_key(struct dept_key *k)
+{
+	return &__lockdep_no_validate__.dkey != k;
+}
+
+/*
+ * Pool
+ * =====================================================================
+ * DEPT maintains pools to provide objects in a safe way.
+ *
+ *    1) Static pool is used at the beginning of boot time.
+ *    2) Local pool is tried first before the static pool. Objects that
+ *       have been freed will be placed there.
+ */
+
+#define OBJECT(id, nr)							\
+static struct dept_##id spool_##id[nr];					\
+static struct dept_##id rpool_##id[nr];					\
+static DEFINE_PER_CPU(struct llist_head, lpool_##id);
+	#include "dept_object.h"
+#undef OBJECT
+
+struct dept_pool dept_pool[OBJECT_NR] = {
+#define OBJECT(id, nr) {						\
+	.name = #id,							\
+	.obj_sz = sizeof(struct dept_##id),				\
+	.obj_nr = nr,							\
+	.tot_nr = nr,							\
+	.acc_sz = ATOMIC_INIT(sizeof(spool_##id) + sizeof(rpool_##id)), \
+	.node_off = offsetof(struct dept_##id, pool_node),		\
+	.spool = spool_##id,						\
+	.rpool = rpool_##id,						\
+	.lpool = &lpool_##id, },
+	#include "dept_object.h"
+#undef OBJECT
+};
+
+static void dept_wq_work_fn(struct work_struct *work)
+{
+	int i;
+
+	for (i = 0; i < OBJECT_NR; i++) {
+		struct dept_pool *p = dept_pool + i;
+		int sz = p->tot_nr * p->obj_sz;
+		void *rpool;
+		bool need;
+
+		local_irq_disable();
+		arch_spin_lock(&dept_pool_spin);
+		need = !p->rpool;
+		arch_spin_unlock(&dept_pool_spin);
+		local_irq_enable();
+
+		if (!need)
+			continue;
+
+		rpool = vmalloc(sz);
+
+		if (!rpool) {
+			DEPT_STOP("Failed to extend internal resources.\n");
+			break;
+		}
+
+		local_irq_disable();
+		arch_spin_lock(&dept_pool_spin);
+		if (!p->rpool) {
+			p->rpool = rpool;
+			rpool = NULL;
+			atomic_add(sz, &p->acc_sz);
+		}
+		arch_spin_unlock(&dept_pool_spin);
+		local_irq_enable();
+
+		if (rpool)
+			vfree(rpool);
+		else
+			DEPT_INFO("Dept object(%s) just got refilled successfully.\n", p->name);
+	}
+}
+
+static DECLARE_WORK(dept_wq_work, dept_wq_work_fn);
+
+static void dept_irq_work_fn(struct irq_work *w)
+{
+	schedule_work(&dept_wq_work);
+}
+
+static DEFINE_IRQ_WORK(dept_irq_work, dept_irq_work_fn);
+
+static void request_rpool_refill(void)
+{
+	irq_work_queue(&dept_irq_work);
+}
+
+/*
+ * We can use llist regardless of whether CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+ * is enabled, because NMI and other contexts on the same CPU never run
+ * inside DEPT concurrently—reentrance is prevented.
+ */
+static void *from_pool(enum object_t t)
+{
+	struct dept_pool *p;
+	struct llist_head *h;
+	struct llist_node *n;
+
+	/*
+	 * llist_del_first() doesn't allow concurrent access, e.g.,
+	 * between process and IRQ context.
+	 */
+	if (DEPT_WARN_ON(!irqs_disabled()))
+		return NULL;
+
+	p = &dept_pool[t];
+
+	/*
+	 * Try local pool first.
+	 */
+	if (likely(dept_per_cpu_ready))
+		h = this_cpu_ptr(p->lpool);
+	else
+		h = &p->boot_pool;
+
+	n = llist_del_first(h);
+	if (n)
+		return (void *)n - p->node_off;
+
+	/*
+	 * Try static pool.
+	 */
+	arch_spin_lock(&dept_pool_spin);
+
+	if (!p->obj_nr) {
+		p->spool = p->rpool;
+		p->obj_nr = p->rpool ? p->tot_nr : 0;
+		p->rpool = NULL;
+		request_rpool_refill();
+	}
+
+	if (p->obj_nr) {
+		void *ret;
+
+		p->obj_nr--;
+		ret = p->spool + (p->obj_nr * p->obj_sz);
+		arch_spin_unlock(&dept_pool_spin);
+
+		return ret;
+	}
+	arch_spin_unlock(&dept_pool_spin);
+
+	DEPT_INFO("------------------------------------------\n"
+		"  Dept object(%s) is run out.\n"
+		"  Dept is trying to refill the object.\n"
+		"  Nevertheless, if it fails, Dept will stop.\n",
+		p->name);
+	return NULL;
+}
+
+static void to_pool(void *o, enum object_t t)
+{
+	struct dept_pool *p = &dept_pool[t];
+	struct llist_head *h;
+
+	preempt_disable();
+	if (likely(dept_per_cpu_ready))
+		h = this_cpu_ptr(p->lpool);
+	else
+		h = &p->boot_pool;
+
+	llist_add(o + p->node_off, h);
+	preempt_enable();
+}
+
+#define OBJECT(id, nr)							\
+static void (*ctor_##id)(struct dept_##id *a);				\
+static void (*dtor_##id)(struct dept_##id *a);				\
+static struct dept_##id *new_##id(void)					\
+{									\
+	struct dept_##id *a;						\
+									\
+	a = (struct dept_##id *)from_pool(OBJECT_##id);			\
+	if (unlikely(!a))						\
+		return NULL;						\
+									\
+	atomic_set(&a->ref, 1);						\
+									\
+	if (ctor_##id)							\
+		ctor_##id(a);						\
+									\
+	return a;							\
+}									\
+									\
+static struct dept_##id *get_##id(struct dept_##id *a)			\
+{									\
+	atomic_inc(&a->ref);						\
+	return a;							\
+}									\
+									\
+static void put_##id(struct dept_##id *a)				\
+{									\
+	if (!atomic_dec_return(&a->ref)) {				\
+		if (dtor_##id)						\
+			dtor_##id(a);					\
+		to_pool(a, OBJECT_##id);				\
+	}								\
+}									\
+									\
+static void del_##id(struct dept_##id *a)				\
+{									\
+	put_##id(a);							\
+}									\
+									\
+static bool __maybe_unused id##_consumed(struct dept_##id *a)		\
+{									\
+	return a && atomic_read(&a->ref) > 1;				\
+}
+#include "dept_object.h"
+#undef OBJECT
+
+#define SET_CONSTRUCTOR(id, f) \
+static void (*ctor_##id)(struct dept_##id *a) = f
+
+static void initialize_dep(struct dept_dep *d)
+{
+	INIT_LIST_HEAD(&d->dep_node);
+	INIT_LIST_HEAD(&d->dep_rev_node);
+}
+SET_CONSTRUCTOR(dep, initialize_dep);
+
+static void initialize_class(struct dept_class *c)
+{
+	int i;
+
+	for (i = 0; i < DEPT_CXT_IRQS_NR; i++) {
+		struct dept_iecxt *ie = &c->iecxt[i];
+		struct dept_iwait *iw = &c->iwait[i];
+
+		ie->ecxt = NULL;
+		ie->enirq = i;
+		ie->staled = false;
+
+		iw->wait = NULL;
+		iw->irq = i;
+		iw->staled = false;
+		iw->touched = false;
+	}
+	c->bfs_gen = 0U;
+
+	INIT_LIST_HEAD(&c->all_node);
+	INIT_LIST_HEAD(&c->dep_head);
+	INIT_LIST_HEAD(&c->dep_rev_head);
+	INIT_LIST_HEAD(&c->bfs_node);
+}
+SET_CONSTRUCTOR(class, initialize_class);
+
+static void initialize_ecxt(struct dept_ecxt *e)
+{
+	int i;
+
+	for (i = 0; i < DEPT_CXT_IRQS_NR; i++) {
+		e->enirq_stack[i] = NULL;
+		e->enirq_ip[i] = 0UL;
+	}
+	e->ecxt_ip = 0UL;
+	e->ecxt_stack = NULL;
+	e->enirqf = 0UL;
+	e->event_ip = 0UL;
+	e->event_stack = NULL;
+	e->ewait_stack = NULL;
+}
+SET_CONSTRUCTOR(ecxt, initialize_ecxt);
+
+static void initialize_wait(struct dept_wait *w)
+{
+	int i;
+
+	for (i = 0; i < DEPT_CXT_IRQS_NR; i++) {
+		w->irq_stack[i] = NULL;
+		w->irq_ip[i] = 0UL;
+	}
+	w->wait_ip = 0UL;
+	w->wait_stack = NULL;
+	w->irqf = 0UL;
+}
+SET_CONSTRUCTOR(wait, initialize_wait);
+
+static void initialize_stack(struct dept_stack *s)
+{
+	s->nr = 0;
+}
+SET_CONSTRUCTOR(stack, initialize_stack);
+
+#define OBJECT(id, nr) \
+static void (*ctor_##id)(struct dept_##id *a);
+	#include "dept_object.h"
+#undef OBJECT
+
+#undef SET_CONSTRUCTOR
+
+#define SET_DESTRUCTOR(id, f) \
+static void (*dtor_##id)(struct dept_##id *a) = f
+
+static void destroy_dep(struct dept_dep *d)
+{
+	if (dep_e(d))
+		put_ecxt(dep_e(d));
+	if (dep_w(d))
+		put_wait(dep_w(d));
+}
+SET_DESTRUCTOR(dep, destroy_dep);
+
+static void destroy_ecxt(struct dept_ecxt *e)
+{
+	int i;
+
+	for (i = 0; i < DEPT_CXT_IRQS_NR; i++)
+		if (e->enirq_stack[i])
+			put_stack(e->enirq_stack[i]);
+	if (e->class)
+		put_class(e->class);
+	if (e->ecxt_stack)
+		put_stack(e->ecxt_stack);
+	if (e->event_stack)
+		put_stack(e->event_stack);
+	if (e->ewait_stack)
+		put_stack(e->ewait_stack);
+}
+SET_DESTRUCTOR(ecxt, destroy_ecxt);
+
+static void destroy_wait(struct dept_wait *w)
+{
+	int i;
+
+	for (i = 0; i < DEPT_CXT_IRQS_NR; i++)
+		if (w->irq_stack[i])
+			put_stack(w->irq_stack[i]);
+	if (w->class)
+		put_class(w->class);
+	if (w->wait_stack)
+		put_stack(w->wait_stack);
+}
+SET_DESTRUCTOR(wait, destroy_wait);
+
+#define OBJECT(id, nr) \
+static void (*dtor_##id)(struct dept_##id *a);
+	#include "dept_object.h"
+#undef OBJECT
+
+#undef SET_DESTRUCTOR
+
+/*
+ * Caching and hashing
+ * =====================================================================
+ * DEPT makes use of caching and hashing to improve performance. Each
+ * object can be obtained in O(1) with its key.
+ *
+ * NOTE: Currently we assume all the objects in the hashs will never be
+ * removed. Implement it when needed.
+ */
+
+/*
+ * Some information might be lost but it's only for hashing key.
+ */
+static unsigned long mix(unsigned long a, unsigned long b)
+{
+	int halfbits = sizeof(unsigned long) * 8 / 2;
+	unsigned long halfmask = (1UL << halfbits) - 1UL;
+
+	return (a << halfbits) | (b & halfmask);
+}
+
+static bool cmp_dep(struct dept_dep *d1, struct dept_dep *d2)
+{
+	return dep_fc(d1)->key == dep_fc(d2)->key &&
+	       dep_tc(d1)->key == dep_tc(d2)->key;
+}
+
+static unsigned long key_dep(struct dept_dep *d)
+{
+	return mix(dep_fc(d)->key, dep_tc(d)->key);
+}
+
+static bool cmp_class(struct dept_class *c1, struct dept_class *c2)
+{
+	return c1->key == c2->key;
+}
+
+static unsigned long key_class(struct dept_class *c)
+{
+	return c->key;
+}
+
+#define HASH(id, bits)							\
+static struct hlist_head table_##id[1 << (bits)];			\
+									\
+static struct hlist_head *head_##id(struct dept_##id *a)		\
+{									\
+	return table_##id + hash_long(key_##id(a), bits);		\
+}									\
+									\
+static struct dept_##id *hash_lookup_##id(struct dept_##id *a)		\
+{									\
+	struct dept_##id *b;						\
+									\
+	hlist_for_each_entry_rcu(b, head_##id(a), hash_node)		\
+		if (cmp_##id(a, b))					\
+			return b;					\
+	return NULL;							\
+}									\
+									\
+static void hash_add_##id(struct dept_##id *a)				\
+{									\
+	get_##id(a);							\
+	hlist_add_head_rcu(&a->hash_node, head_##id(a));		\
+}									\
+									\
+static void hash_del_##id(struct dept_##id *a)				\
+{									\
+	hlist_del_rcu(&a->hash_node);					\
+	put_##id(a);							\
+}
+#include "dept_hash.h"
+#undef HASH
+
+static struct dept_dep *lookup_dep(struct dept_class *fc,
+				   struct dept_class *tc)
+{
+	struct dept_ecxt onetime_e = { .class = fc };
+	struct dept_wait onetime_w = { .class = tc };
+	struct dept_dep  onetime_d = { .ecxt = &onetime_e,
+				       .wait = &onetime_w };
+	return hash_lookup_dep(&onetime_d);
+}
+
+static struct dept_class *lookup_class(unsigned long key)
+{
+	struct dept_class onetime_c = { .key = key };
+
+	return hash_lookup_class(&onetime_c);
+}
+
+/*
+ * Report
+ * =====================================================================
+ * DEPT prints useful information to help debugging on detection of
+ * problematic dependency.
+ */
+
+static void print_ip_stack(unsigned long ip, struct dept_stack *s)
+{
+	if (ip)
+		print_ip_sym(KERN_WARNING, ip);
+
+#ifdef CONFIG_DEPT_DEBUG
+	if (!s)
+		pr_warn("stack is NULL.\n");
+	else if (!s->nr)
+		pr_warn("stack->nr is 0.\n");
+	if (s)
+		pr_warn("stack ref is %d.\n", atomic_read(&s->ref));
+#endif
+
+	if (valid_stack(s)) {
+		pr_warn("stacktrace:\n");
+		stack_trace_print(s->raw, s->nr, 5);
+	}
+
+	if (!ip && !valid_stack(s))
+		pr_warn("(N/A)\n");
+}
+
+#define print_spc(spc, fmt, ...) \
+	pr_warn("%*c" fmt, (spc) * 3, ' ', ##__VA_ARGS__)
+
+static void print_diagram(struct dept_dep *d)
+{
+	struct dept_ecxt *e = dep_e(d);
+	struct dept_wait *w = dep_w(d);
+	struct dept_class *fc = dep_fc(d);
+	struct dept_class *tc = dep_tc(d);
+	unsigned long irqf;
+	int irq;
+	bool firstline = true;
+	int spc = 1;
+	const char *w_fn = w->wait_fn ?: "(unknown)";
+	const char *e_fn = e->event_fn ?: "(unknown)";
+	const char *c_fn = e->ecxt_fn ?: "(unknown)";
+	const char *fc_n = fc->sched_map ? "<sched>" : (fc->name ?: "(unknown)");
+	const char *tc_n = tc->sched_map ? "<sched>" : (tc->name ?: "(unknown)");
+
+	irqf = e->enirqf & w->irqf;
+	for_each_set_bit(irq, &irqf, DEPT_CXT_IRQS_NR) {
+		if (!firstline)
+			pr_warn("\nor\n\n");
+		firstline = false;
+
+		print_spc(spc, "[S] %s(%s:%d)\n", c_fn, fc_n, fc->sub_id);
+		print_spc(spc, "    <%s interrupt>\n", irq_str(irq));
+		print_spc(spc + 1, "[W] %s(%s:%d)\n", w_fn, tc_n, tc->sub_id);
+		print_spc(spc, "[E] %s(%s:%d)\n", e_fn, fc_n, fc->sub_id);
+	}
+
+	if (!irqf) {
+		print_spc(spc, "[S] %s(%s:%d)\n", c_fn, fc_n, fc->sub_id);
+		print_spc(spc, "[W] %s(%s:%d)\n", w_fn, tc_n, tc->sub_id);
+		if (w->timeout)
+			print_spc(spc, "--------------- >8 timeout ---------------\n");
+		print_spc(spc, "[E] %s(%s:%d)\n", e_fn, fc_n, fc->sub_id);
+	}
+}
+
+static void print_dep(struct dept_dep *d)
+{
+	struct dept_ecxt *e = dep_e(d);
+	struct dept_wait *w = dep_w(d);
+	struct dept_class *fc = dep_fc(d);
+	struct dept_class *tc = dep_tc(d);
+	unsigned long irqf;
+	int irq;
+	const char *w_fn = w->wait_fn ?: "(unknown)";
+	const char *e_fn = e->event_fn ?: "(unknown)";
+	const char *c_fn = e->ecxt_fn ?: "(unknown)";
+	const char *fc_n = fc->sched_map ? "<sched>" : (fc->name ?: "(unknown)");
+	const char *tc_n = tc->sched_map ? "<sched>" : (tc->name ?: "(unknown)");
+
+	irqf = e->enirqf & w->irqf;
+	for_each_set_bit(irq, &irqf, DEPT_CXT_IRQS_NR) {
+		pr_warn("%s has been enabled:\n", irq_str(irq));
+		print_ip_stack(e->enirq_ip[irq], e->enirq_stack[irq]);
+		pr_warn("\n");
+
+		pr_warn("[S] %s(%s:%d):\n", c_fn, fc_n, fc->sub_id);
+		print_ip_stack(e->ecxt_ip, e->ecxt_stack);
+		pr_warn("\n");
+
+		pr_warn("[W] %s(%s:%d) in %s context:\n",
+		       w_fn, tc_n, tc->sub_id, irq_str(irq));
+		print_ip_stack(w->irq_ip[irq], w->irq_stack[irq]);
+		pr_warn("\n");
+
+		pr_warn("[E] %s(%s:%d):\n", e_fn, fc_n, fc->sub_id);
+		print_ip_stack(e->event_ip, e->event_stack);
+
+		if (valid_stack(e->ewait_stack)) {
+			pr_warn("(wait to wake up)\n");
+			print_ip_stack(0, e->ewait_stack);
+		}
+	}
+
+	if (!irqf) {
+		pr_warn("[S] %s(%s:%d):\n", c_fn, fc_n, fc->sub_id);
+		print_ip_stack(e->ecxt_ip, e->ecxt_stack);
+		pr_warn("\n");
+
+		pr_warn("[W] %s(%s:%d):\n", w_fn, tc_n, tc->sub_id);
+		print_ip_stack(w->wait_ip, w->wait_stack);
+		pr_warn("\n");
+
+		pr_warn("[E] %s(%s:%d):\n", e_fn, fc_n, fc->sub_id);
+		print_ip_stack(e->event_ip, e->event_stack);
+
+		if (valid_stack(e->ewait_stack)) {
+			pr_warn("(wait to wake up)\n");
+			print_ip_stack(0, e->ewait_stack);
+		}
+
+		dept_ut_ecxt_stack_account(valid_stack(e->ecxt_stack));
+		dept_ut_wait_stack_account(valid_stack(w->wait_stack));
+		dept_ut_evnt_stack_account(valid_stack(e->event_stack));
+	}
+}
+
+static void save_current_stack(int skip);
+
+static bool is_timeout_wait_circle(struct dept_class *c)
+{
+	struct dept_class *fc = c->bfs_parent;
+	struct dept_class *tc = c;
+
+	do {
+		struct dept_dep *d = lookup_dep(fc, tc);
+
+		if (d->wait->timeout)
+			return true;
+
+		tc = fc;
+		fc = fc->bfs_parent;
+	} while (tc != c);
+
+	return false;
+}
+
+/*
+ * Print all classes in a circle.
+ */
+static void print_circle(struct dept_class *c)
+{
+	struct dept_class *fc = c->bfs_parent;
+	struct dept_class *tc = c;
+	int i;
+
+	dept_outworld_enter();
+	save_current_stack(6);
+
+	pr_warn("===================================================\n");
+	pr_warn("DEPT: Circular dependency has been detected.\n");
+	pr_warn("%s %.*s %s\n", init_utsname()->release,
+		(int)strcspn(init_utsname()->version, " "),
+		init_utsname()->version,
+		print_tainted());
+	pr_warn("---------------------------------------------------\n");
+	pr_warn("summary\n");
+	pr_warn("---------------------------------------------------\n");
+
+	if (is_timeout_wait_circle(c)) {
+		pr_warn("NOT A DEADLOCK BUT A CIRCULAR DEPENDENCY\n");
+		pr_warn("CHECK IF THE TIMEOUT IS INTENDED\n\n");
+	} else if (fc == tc) {
+		pr_warn("*** AA DEADLOCK ***\n\n");
+	} else {
+		pr_warn("*** DEADLOCK ***\n\n");
+	}
+
+	i = 0;
+	do {
+		struct dept_dep *d = lookup_dep(fc, tc);
+
+		pr_warn("context %c\n", 'A' + (i++));
+		print_diagram(d);
+		if (fc != c)
+			pr_warn("\n");
+
+		tc = fc;
+		fc = fc->bfs_parent;
+	} while (tc != c);
+
+	pr_warn("\n");
+	pr_warn("[S]: start of the event context\n");
+	pr_warn("[W]: the wait blocked\n");
+	pr_warn("[E]: the event not reachable\n");
+
+	i = 0;
+	do {
+		struct dept_dep *d = lookup_dep(fc, tc);
+
+		pr_warn("---------------------------------------------------\n");
+		pr_warn("context %c's detail\n", 'A' + i);
+		pr_warn("---------------------------------------------------\n");
+		pr_warn("context %c\n", 'A' + (i++));
+		print_diagram(d);
+		pr_warn("\n");
+		print_dep(d);
+
+		tc = fc;
+		fc = fc->bfs_parent;
+	} while (tc != c);
+
+	pr_warn("---------------------------------------------------\n");
+	pr_warn("information that might be helpful\n");
+	pr_warn("---------------------------------------------------\n");
+	dump_stack();
+
+	dept_outworld_exit();
+
+	dept_ut_circle_detect();
+}
+
+/*
+ * BFS(Breadth First Search)
+ * =====================================================================
+ * Whenever a new dependency is added into the graph, search the graph
+ * for a new circular dependency.
+ */
+
+struct bfs_ops {
+	void (*bfs_init)(void *, void *, void **);
+	void (*extend)(struct list_head *, void *);
+	void *(*dequeue)(struct list_head *);
+	enum bfs_ret (*callback)(void *, void *, void **);
+};
+
+static unsigned int bfs_gen;
+
+/*
+ * NOTE: Must be called with dept_lock held.
+ */
+static void bfs(void *root, struct bfs_ops *ops, void *in, void **out)
+{
+	LIST_HEAD(q);
+	enum bfs_ret ret;
+
+	if (DEPT_WARN_ON(!ops || !ops->bfs_init || !ops->extend ||
+				!ops->dequeue || !ops->callback))
+		return;
+
+	/*
+	 * Avoid zero bfs_gen.
+	 */
+	bfs_gen = bfs_gen + 1 ?: 1;
+	ops->bfs_init(root, in, out);
+
+	ret = ops->callback(root, in, out);
+	if (ret != BFS_CONTINUE)
+		return;
+
+	ops->extend(&q, root);
+	while (!list_empty(&q)) {
+		void *node = ops->dequeue(&q);
+
+		if (ret == BFS_DONE)
+			continue;
+
+		ret = ops->callback(node, in, out);
+		if (ret == BFS_CONTINUE)
+			ops->extend(&q, node);
+	}
+}
+
+/*
+ * Main operations
+ * =====================================================================
+ * Add dependencies - Each new dependency is added into the graph and
+ * checked if it forms a circular dependency.
+ *
+ * Track waits - Waits are queued into the ring buffer for later use to
+ * generate appropriate dependencies with cross-event.
+ *
+ * Track event contexts(ecxt) - Event contexts are pushed into local
+ * stack for later use to generate appropriate dependencies with waits.
+ */
+
+static unsigned long cur_enirqf(void);
+static int cur_cxt(void);
+static unsigned int cur_ctxt_id(void);
+
+static struct dept_iecxt *iecxt(struct dept_class *c, int irq)
+{
+	return &c->iecxt[irq];
+}
+
+static struct dept_iwait *iwait(struct dept_class *c, int irq)
+{
+	return &c->iwait[irq];
+}
+
+static void stale_iecxt(struct dept_iecxt *ie)
+{
+	if (ie->ecxt)
+		put_ecxt(ie->ecxt);
+
+	WRITE_ONCE(ie->ecxt, NULL);
+	WRITE_ONCE(ie->staled, true);
+}
+
+static void set_iecxt(struct dept_iecxt *ie, struct dept_ecxt *e)
+{
+	/*
+	 * ->ecxt will never be updated once getting set until the class
+	 * gets removed.
+	 */
+	if (ie->ecxt)
+		DEPT_WARN_ON(1);
+	else
+		WRITE_ONCE(ie->ecxt, get_ecxt(e));
+}
+
+static void stale_iwait(struct dept_iwait *iw)
+{
+	if (iw->wait)
+		put_wait(iw->wait);
+
+	WRITE_ONCE(iw->wait, NULL);
+	WRITE_ONCE(iw->staled, true);
+}
+
+static void set_iwait(struct dept_iwait *iw, struct dept_wait *w)
+{
+	/*
+	 * ->wait will never be updated once getting set until the class
+	 * gets removed.
+	 */
+	if (iw->wait)
+		DEPT_WARN_ON(1);
+	else
+		WRITE_ONCE(iw->wait, get_wait(w));
+
+	iw->touched = true;
+}
+
+static void touch_iwait(struct dept_iwait *iw)
+{
+	iw->touched = true;
+}
+
+static void untouch_iwait(struct dept_iwait *iw)
+{
+	iw->touched = false;
+}
+
+static struct dept_stack *get_current_stack(void)
+{
+	struct dept_stack *s = dept_task()->stack;
+
+	return s ? get_stack(s) : NULL;
+}
+
+static void prepare_current_stack(void)
+{
+	DEPT_WARN_ON(dept_task()->stack);
+
+	dept_task()->stack = new_stack();
+}
+
+static void save_current_stack(int skip)
+{
+	struct dept_stack *s = dept_task()->stack;
+
+	if (!s)
+		return;
+
+	if (valid_stack(s))
+		return;
+
+	s->nr = stack_trace_save(s->raw, DEPT_MAX_STACK_ENTRY, skip);
+}
+
+static void finish_current_stack(void)
+{
+	struct dept_stack *s = dept_task()->stack;
+
+	/*
+	 * Fill the struct dept_stack with a valid stracktrace if it has
+	 * been referred at least once.
+	 */
+	if (stack_consumed(s))
+		save_current_stack(2);
+
+	dept_task()->stack = NULL;
+
+	/*
+	 * Actual deletion will happen at put_stack() if the stack has
+	 * been referred.
+	 */
+	if (s)
+		del_stack(s);
+}
+
+/*
+ * FIXME: For now, disable LOCKDEP while DEPT is working.
+ *
+ * Both LOCKDEP and DEPT report it on a deadlock detection using
+ * printk taking the risk of another deadlock that might be caused by
+ * locks of console or printk between inside and outside of them.
+ *
+ * For DEPT, it's no problem since multiple reports are allowed. But it
+ * would be a bad idea for LOCKDEP since it will stop even on a singe
+ * report. So we need to prevent LOCKDEP from its reporting the risk
+ * DEPT would take when reporting something.
+ */
+#include <linux/lockdep.h>
+
+void noinstr dept_off(void)
+{
+	dept_task()->recursive++;
+	lockdep_off();
+}
+
+void noinstr dept_on(void)
+{
+	lockdep_on();
+	dept_task()->recursive--;
+}
+
+static unsigned long dept_enter(void)
+{
+	unsigned long flags;
+
+	flags = arch_local_irq_save();
+	dept_off();
+	prepare_current_stack();
+	return flags;
+}
+
+static void dept_exit(unsigned long flags)
+{
+	finish_current_stack();
+	dept_on();
+	arch_local_irq_restore(flags);
+}
+
+static unsigned long dept_enter_recursive(void)
+{
+	unsigned long flags;
+
+	flags = arch_local_irq_save();
+	return flags;
+}
+
+static void dept_exit_recursive(unsigned long flags)
+{
+	arch_local_irq_restore(flags);
+}
+
+/*
+ * NOTE: Must be called with dept_lock held.
+ */
+static struct dept_dep *__add_dep(struct dept_ecxt *e,
+				  struct dept_wait *w)
+{
+	struct dept_dep *d;
+
+	if (DEPT_WARN_ON(!valid_class(e->class)))
+		return NULL;
+
+	if (DEPT_WARN_ON(!valid_class(w->class)))
+		return NULL;
+
+	if (lookup_dep(e->class, w->class))
+		return NULL;
+
+	d = new_dep();
+	if (unlikely(!d))
+		return NULL;
+
+	d->ecxt = get_ecxt(e);
+	d->wait = get_wait(w);
+
+	/*
+	 * Add the dependency into hash and graph.
+	 */
+	hash_add_dep(d);
+	list_add(&d->dep_node, &dep_fc(d)->dep_head);
+	list_add(&d->dep_rev_node, &dep_tc(d)->dep_rev_head);
+	return d;
+}
+
+static void bfs_init_check_dl(void *node, void *in, void **out)
+{
+	struct dept_class *root = (struct dept_class *)node;
+	struct dept_dep *new = (struct dept_dep *)in;
+
+	root->bfs_gen = bfs_gen;
+	dep_tc(new)->bfs_parent = dep_fc(new);
+}
+
+static void bfs_extend_dep(struct list_head *h, void *node)
+{
+	struct dept_class *cur = (struct dept_class *)node;
+	struct dept_dep *d;
+
+	list_for_each_entry(d, &cur->dep_head, dep_node) {
+		struct dept_class *next = dep_tc(d);
+
+		if (bfs_gen == next->bfs_gen)
+			continue;
+		next->bfs_parent = cur;
+		next->bfs_gen = bfs_gen;
+		list_add_tail(&next->bfs_node, h);
+	}
+}
+
+static void *bfs_dequeue_dep(struct list_head *h)
+{
+	struct dept_class *c;
+
+	DEPT_WARN_ON(list_empty(h));
+
+	c = list_first_entry(h, struct dept_class, bfs_node);
+	list_del(&c->bfs_node);
+	return c;
+}
+
+static enum bfs_ret cb_check_dl(void *node, void *in, void **out)
+{
+	struct dept_class *cur = (struct dept_class *)node;
+	struct dept_dep *new = (struct dept_dep *)in;
+
+	if (cur == dep_fc(new)) {
+		print_circle(dep_tc(new));
+		return BFS_DONE;
+	}
+
+	return BFS_CONTINUE;
+}
+
+/*
+ * This function is actually in charge of reporting.
+ */
+static void check_dl_bfs(struct dept_dep *d)
+{
+	struct bfs_ops ops = {
+		.bfs_init = bfs_init_check_dl,
+		.extend = bfs_extend_dep,
+		.dequeue = bfs_dequeue_dep,
+		.callback = cb_check_dl,
+	};
+
+	bfs((void *)dep_tc(d), &ops, (void *)d, NULL);
+}
+
+static void bfs_init_dep(void *node, void *in, void **out)
+{
+	struct dept_class *root = (struct dept_class *)node;
+
+	root->bfs_gen = bfs_gen;
+}
+
+static void bfs_extend_dep_rev(struct list_head *h, void *node)
+{
+	struct dept_class *cur = (struct dept_class *)node;
+	struct dept_dep *d;
+
+	list_for_each_entry(d, &cur->dep_rev_head, dep_rev_node) {
+		struct dept_class *next = dep_fc(d);
+
+		if (bfs_gen == next->bfs_gen)
+			continue;
+		next->bfs_parent = cur;
+		next->bfs_gen = bfs_gen;
+		list_add_tail(&next->bfs_node, h);
+	}
+}
+
+static enum bfs_ret cb_find_iw(void *node, void *in, void **out)
+{
+	struct dept_class *cur = (struct dept_class *)node;
+	int irq = *(int *)in;
+	struct dept_iwait *iw;
+
+	if (DEPT_WARN_ON(!out))
+		return BFS_DONE;
+
+	iw = iwait(cur, irq);
+
+	/*
+	 * If any parent's ->wait was set, then the children would've
+	 * been touched.
+	 */
+	if (!iw->touched)
+		return BFS_SKIP;
+
+	if (!iw->wait)
+		return BFS_CONTINUE;
+
+	*out = iw;
+	return BFS_DONE;
+}
+
+static struct dept_iwait *find_iw_bfs(struct dept_class *c, int irq)
+{
+	struct dept_iwait *iw = iwait(c, irq);
+	struct dept_iwait *found = NULL;
+	struct bfs_ops ops = {
+		.bfs_init = bfs_init_dep,
+		.extend = bfs_extend_dep_rev,
+		.dequeue = bfs_dequeue_dep,
+		.callback = cb_find_iw,
+	};
+
+	bfs((void *)c, &ops, (void *)&irq, (void **)&found);
+
+	if (found)
+		return found;
+
+	untouch_iwait(iw);
+	return NULL;
+}
+
+static enum bfs_ret cb_touch_iw_find_ie(void *node, void *in, void **out)
+{
+	struct dept_class *cur = (struct dept_class *)node;
+	int irq = *(int *)in;
+	struct dept_iecxt *ie = iecxt(cur, irq);
+	struct dept_iwait *iw = iwait(cur, irq);
+
+	if (DEPT_WARN_ON(!out))
+		return BFS_DONE;
+
+	touch_iwait(iw);
+
+	if (!ie->ecxt)
+		return BFS_CONTINUE;
+	if (!*out)
+		*out = ie;
+
+	/*
+	 * Do touch_iwait() all the way.
+	 */
+	return BFS_CONTINUE;
+}
+
+static struct dept_iecxt *touch_iw_find_ie_bfs(struct dept_class *c,
+					       int irq)
+{
+	struct dept_iecxt *found = NULL;
+	struct bfs_ops ops = {
+		.bfs_init = bfs_init_dep,
+		.extend = bfs_extend_dep,
+		.dequeue = bfs_dequeue_dep,
+		.callback = cb_touch_iw_find_ie,
+	};
+
+	bfs((void *)c, &ops, (void *)&irq, (void **)&found);
+	return found;
+}
+
+/*
+ * Should be called with dept_lock held.
+ */
+static void __add_idep(struct dept_iecxt *ie, struct dept_iwait *iw)
+{
+	struct dept_dep *new;
+
+	/*
+	 * There's nothing to do.
+	 */
+	if (!ie || !iw || !ie->ecxt || !iw->wait)
+		return;
+
+	new = __add_dep(ie->ecxt, iw->wait);
+
+	/*
+	 * Deadlock detected. Let check_dl_bfs() report it.
+	 */
+	if (new) {
+		check_dl_bfs(new);
+		stale_iecxt(ie);
+		stale_iwait(iw);
+	}
+
+	/*
+	 * If !new, it would be the case of lack of object resource.
+	 * Just let it go and get checked by other chances. Retrying is
+	 * meaningless in that case.
+	 */
+}
+
+static void set_check_iecxt(struct dept_class *c, int irq,
+			    struct dept_ecxt *e)
+{
+	struct dept_iecxt *ie = iecxt(c, irq);
+
+	set_iecxt(ie, e);
+	__add_idep(ie, find_iw_bfs(c, irq));
+}
+
+static void set_check_iwait(struct dept_class *c, int irq,
+			    struct dept_wait *w)
+{
+	struct dept_iwait *iw = iwait(c, irq);
+
+	set_iwait(iw, w);
+	__add_idep(touch_iw_find_ie_bfs(c, irq), iw);
+}
+
+static void add_iecxt(struct dept_class *c, int irq, struct dept_ecxt *e,
+		      bool stack)
+{
+	/*
+	 * This access is safe since we ensure e->class has set locally.
+	 */
+	struct dept_task *dt = dept_task();
+	struct dept_iecxt *ie = iecxt(c, irq);
+
+	if (DEPT_WARN_ON(!valid_class(c)))
+		return;
+
+	if (unlikely(READ_ONCE(ie->staled)))
+		return;
+
+	/*
+	 * Skip add_iecxt() if ie->ecxt has ever been set at least once.
+	 * Which means it has a valid ->ecxt or been staled.
+	 */
+	if (READ_ONCE(ie->ecxt))
+		return;
+
+	if (unlikely(!dept_lock()))
+		return;
+
+	if (unlikely(ie->staled))
+		goto unlock;
+	if (ie->ecxt)
+		goto unlock;
+
+	e->enirqf |= (1UL << irq);
+
+	/*
+	 * Should be NULL since it's the first time that these
+	 * enirq_{ip,stack}[irq] have ever set.
+	 */
+	DEPT_WARN_ON(e->enirq_ip[irq]);
+	DEPT_WARN_ON(e->enirq_stack[irq]);
+
+	e->enirq_ip[irq] = dt->enirq_ip[irq];
+	e->enirq_stack[irq] = stack ? get_current_stack() : NULL;
+
+	set_check_iecxt(c, irq, e);
+unlock:
+	dept_unlock();
+}
+
+static void add_iwait(struct dept_class *c, int irq, struct dept_wait *w)
+{
+	struct dept_iwait *iw = iwait(c, irq);
+
+	if (DEPT_WARN_ON(!valid_class(c)))
+		return;
+
+	if (unlikely(READ_ONCE(iw->staled)))
+		return;
+
+	/*
+	 * Skip add_iwait() if iw->wait has ever been set at least once.
+	 * Which means it has a valid ->wait or been staled.
+	 */
+	if (READ_ONCE(iw->wait))
+		return;
+
+	if (unlikely(!dept_lock()))
+		return;
+
+	if (unlikely(iw->staled))
+		goto unlock;
+	if (iw->wait)
+		goto unlock;
+
+	w->irqf |= (1UL << irq);
+
+	/*
+	 * Should be NULL since it's the first time that these
+	 * irq_{ip,stack}[irq] have ever set.
+	 */
+	DEPT_WARN_ON(w->irq_ip[irq]);
+	DEPT_WARN_ON(w->irq_stack[irq]);
+
+	w->irq_ip[irq] = w->wait_ip;
+	w->irq_stack[irq] = get_current_stack();
+
+	set_check_iwait(c, irq, w);
+unlock:
+	dept_unlock();
+}
+
+static struct dept_wait_hist *hist(int pos)
+{
+	struct dept_task *dt = dept_task();
+
+	return dt->wait_hist + (pos % DEPT_MAX_WAIT_HIST);
+}
+
+static int hist_pos_next(void)
+{
+	struct dept_task *dt = dept_task();
+
+	return dt->wait_hist_pos % DEPT_MAX_WAIT_HIST;
+}
+
+static void hist_advance(void)
+{
+	struct dept_task *dt = dept_task();
+
+	dt->wait_hist_pos++;
+	dt->wait_hist_pos %= DEPT_MAX_WAIT_HIST;
+}
+
+static struct dept_wait_hist *new_hist(void)
+{
+	struct dept_wait_hist *wh = hist(hist_pos_next());
+
+	hist_advance();
+	return wh;
+}
+
+static struct dept_wait_hist *last_hist(void)
+{
+	int pos_n = hist_pos_next();
+	struct dept_wait_hist *wh_n = hist(pos_n);
+
+	/*
+	 * This is the first try.
+	 */
+	if (!pos_n && !wh_n->wait)
+		return NULL;
+
+	return hist(pos_n + DEPT_MAX_WAIT_HIST - 1);
+}
+
+static void add_hist(struct dept_wait *w, unsigned int wg, unsigned int ctxt_id)
+{
+	struct dept_wait_hist *wh;
+
+	wh = last_hist();
+
+	if (!wh || wh->wait->class != w->class || wh->ctxt_id != ctxt_id)
+		wh = new_hist();
+
+	if (likely(wh->wait))
+		put_wait(wh->wait);
+
+	wh->wait = get_wait(w);
+	wh->wgen = wg;
+	wh->ctxt_id = ctxt_id;
+}
+
+/*
+ * Should be called after setting up e's iecxt and w's iwait.
+ */
+static void add_dep(struct dept_ecxt *e, struct dept_wait *w)
+{
+	struct dept_class *fc = e->class;
+	struct dept_class *tc = w->class;
+	struct dept_dep *d;
+	int i;
+
+	if (lookup_dep(fc, tc))
+		return;
+
+	if (unlikely(!dept_lock()))
+		return;
+
+	/*
+	 * __add_dep() will lookup_dep() again with lock held.
+	 */
+	d = __add_dep(e, w);
+	if (d) {
+		check_dl_bfs(d);
+
+		for (i = 0; i < DEPT_CXT_IRQS_NR; i++) {
+			struct dept_iwait *fiw = iwait(fc, i);
+			struct dept_iecxt *found_ie;
+			struct dept_iwait *found_iw;
+
+			/*
+			 * '->touched == false' guarantees there's no
+			 * parent that has been set ->wait.
+			 */
+			if (!fiw->touched)
+				continue;
+
+			/*
+			 * find_iw_bfs() will untouch the iwait if
+			 * not found.
+			 */
+			found_iw = find_iw_bfs(fc, i);
+
+			if (!found_iw)
+				continue;
+
+			found_ie = touch_iw_find_ie_bfs(tc, i);
+			__add_idep(found_ie, found_iw);
+		}
+	}
+	dept_unlock();
+}
+
+static atomic_t wgen = ATOMIC_INIT(1);
+
+static int next_wgen(void)
+{
+	/*
+	 * Avoid zero wgen.
+	 */
+	return atomic_inc_return(&wgen) ?: atomic_inc_return(&wgen);
+}
+
+/*
+ * XXX: This is a temporary patch needed until lockdep stops tracking
+ * dependency in wrong way.  lockdep has added an annotation to specify
+ * a callback to determin whether the given lock aquisition order is
+ * okay or not in its own way.  Even though dept is already working
+ * correctly with sub class on that issue, it needs to be aware of the
+ * annotation anyway.
+ */
+static bool lockdep_cmp_fn(struct dept_map *prev, struct dept_map *next)
+{
+	/*
+	 * Assumes the cmp_fn thing comes from struct lockdep_map.
+	 */
+	struct lockdep_map *p_lock = (struct lockdep_map *)prev->lockdep_map;
+	struct lockdep_map *n_lock = (struct lockdep_map *)next->lockdep_map;
+	struct lock_class *p_class = p_lock ? p_lock->class_cache[0] : NULL;
+	struct lock_class *n_class = n_lock ? n_lock->class_cache[0] : NULL;
+
+	if (!p_class || !n_class)
+		return false;
+
+	if (p_class != n_class)
+		return false;
+
+	if (!p_class->cmp_fn)
+		return false;
+
+	return p_class->cmp_fn(p_lock, n_lock) < 0;
+}
+
+static void add_wait(struct dept_map *m, struct dept_class *c,
+		unsigned long ip, const char *w_fn, int sub_l,
+		bool sched_sleep, bool timeout)
+{
+	struct dept_task *dt = dept_task();
+	struct dept_wait *w;
+	unsigned int wg;
+	int cxt;
+	int i;
+
+	if (DEPT_WARN_ON(!valid_class(c)))
+		return;
+
+	w = new_wait();
+	if (unlikely(!w))
+		return;
+
+	WRITE_ONCE(w->class, get_class(c));
+	w->wait_ip = ip;
+	w->wait_fn = w_fn;
+	w->wait_stack = get_current_stack();
+	w->sched_sleep = sched_sleep;
+	w->timeout = timeout;
+
+	cxt = cur_cxt();
+	if (cxt == DEPT_CXT_HIRQ || cxt == DEPT_CXT_SIRQ)
+		add_iwait(c, cxt, w);
+
+	/*
+	 * Avoid adding dependency between user aware nested ecxt and
+	 * wait.
+	 */
+	for (i = dt->ecxt_held_pos - 1; i >= 0; i--) {
+		struct dept_ecxt_held *eh;
+
+		eh = dt->ecxt_held + i;
+
+		/*
+		 * the case of invalid key'ed one
+		 */
+		if (!eh->ecxt)
+			continue;
+
+		if (eh->ecxt->class == c && eh->sub_l != sub_l)
+			continue;
+
+		if (i == dt->ecxt_held_pos - 1 && lockdep_cmp_fn(eh->map, m))
+			continue;
+
+		add_dep(eh->ecxt, w);
+	}
+
+	wg = next_wgen();
+	add_hist(w, wg, cur_ctxt_id());
+
+	del_wait(w);
+}
+
+static struct dept_ecxt_held *add_ecxt(struct dept_map *m,
+		struct dept_class *c, unsigned long ip, const char *c_fn,
+		const char *e_fn, int sub_l,
+		struct dept_stack *ewait_stack)
+{
+	struct dept_task *dt = dept_task();
+	struct dept_ecxt_held *eh;
+	struct dept_ecxt *e;
+	unsigned long irqf;
+	unsigned int wg;
+	int irq;
+
+	if (DEPT_WARN_ON(!valid_class(c)))
+		return NULL;
+
+	if (DEPT_WARN_ON_ONCE(dt->ecxt_held_pos >= DEPT_MAX_ECXT_HELD))
+		return NULL;
+
+	wg = next_wgen();
+	if (m->nocheck) {
+		eh = dt->ecxt_held + (dt->ecxt_held_pos++);
+		eh->ecxt = NULL;
+		eh->map = m;
+		eh->class = get_class(c);
+		eh->wgen = wg;
+		eh->sub_l = sub_l;
+
+		return eh;
+	}
+
+	e = new_ecxt();
+	if (unlikely(!e))
+		return NULL;
+
+	e->class = get_class(c);
+	e->ecxt_ip = ip;
+	e->ecxt_stack = ip ? get_current_stack() : NULL;
+	e->ewait_stack = ewait_stack ? get_stack(ewait_stack) : NULL;
+	e->event_fn = e_fn;
+	e->ecxt_fn = c_fn;
+
+	eh = dt->ecxt_held + (dt->ecxt_held_pos++);
+	eh->ecxt = get_ecxt(e);
+	eh->map = m;
+	eh->class = get_class(c);
+	eh->wgen = wg;
+	eh->sub_l = sub_l;
+
+	irqf = cur_enirqf();
+	for_each_set_bit(irq, &irqf, DEPT_CXT_IRQS_NR)
+		add_iecxt(c, irq, e, false);
+
+	del_ecxt(e);
+	return eh;
+}
+
+static int find_ecxt_pos(struct dept_map *m, struct dept_class *c,
+			 bool newfirst)
+{
+	struct dept_task *dt = dept_task();
+	int i;
+
+	if (newfirst) {
+		for (i = dt->ecxt_held_pos - 1; i >= 0; i--) {
+			struct dept_ecxt_held *eh;
+
+			eh = dt->ecxt_held + i;
+			if (eh->map == m && eh->class == c)
+				return i;
+		}
+	} else {
+		for (i = 0; i < dt->ecxt_held_pos; i++) {
+			struct dept_ecxt_held *eh;
+
+			eh = dt->ecxt_held + i;
+			if (eh->map == m && eh->class == c)
+				return i;
+		}
+	}
+	return -1;
+}
+
+static bool pop_ecxt(struct dept_map *m, struct dept_class *c)
+{
+	struct dept_task *dt = dept_task();
+	int pos;
+	int i;
+
+	pos = find_ecxt_pos(m, c, true);
+	if (pos == -1)
+		return false;
+
+	if (dt->ecxt_held[pos].class)
+		put_class(dt->ecxt_held[pos].class);
+
+	if (dt->ecxt_held[pos].ecxt)
+		put_ecxt(dt->ecxt_held[pos].ecxt);
+
+	dt->ecxt_held_pos--;
+
+	for (i = pos; i < dt->ecxt_held_pos; i++)
+		dt->ecxt_held[i] = dt->ecxt_held[i + 1];
+	return true;
+}
+
+static bool good_hist(struct dept_wait_hist *wh, unsigned int wg)
+{
+	return wh->wait != NULL && before(wg, wh->wgen);
+}
+
+/*
+ * Binary-search the ring buffer for the earliest valid wait.
+ */
+static int find_hist_pos(unsigned int wg)
+{
+	int oldest;
+	int l;
+	int r;
+	int pos;
+
+	oldest = hist_pos_next();
+	if (unlikely(good_hist(hist(oldest), wg))) {
+		DEPT_INFO_ONCE("Need to expand the ring buffer.\n");
+		return oldest;
+	}
+
+	l = oldest + 1;
+	r = oldest + DEPT_MAX_WAIT_HIST - 1;
+	for (pos = (l + r) / 2; l <= r; pos = (l + r) / 2) {
+		struct dept_wait_hist *p = hist(pos - 1);
+		struct dept_wait_hist *wh = hist(pos);
+
+		if (!good_hist(p, wg) && good_hist(wh, wg))
+			return pos % DEPT_MAX_WAIT_HIST;
+		if (good_hist(wh, wg))
+			r = pos - 1;
+		else
+			l = pos + 1;
+	}
+	return -1;
+}
+
+static void do_event(struct dept_map *m, struct dept_map *real_m,
+		struct dept_class *c, unsigned int wg, unsigned long ip,
+		const char *e_fn, struct dept_stack *ewait_stack)
+{
+	struct dept_task *dt = dept_task();
+	struct dept_wait_hist *wh;
+	struct dept_ecxt_held *eh;
+	unsigned int ctxt_id;
+	int end;
+	int pos;
+	int i;
+
+	if (DEPT_WARN_ON(!valid_class(c)))
+		return;
+
+	if (m->nocheck)
+		return;
+
+	/*
+	 * The event was triggered before wait.
+	 */
+	if (!wg)
+		return;
+
+	/*
+	 * If an ecxt for this map exists, let the ecxt work for this
+	 * event and do not proceed it in do_event().
+	 */
+	if (find_ecxt_pos(real_m, c, false) != -1)
+		return;
+	eh = add_ecxt(m, c, 0UL, NULL, e_fn, 0, ewait_stack);
+
+	if (!eh)
+		return;
+
+	if (DEPT_WARN_ON(!eh->ecxt))
+		goto out;
+
+	eh->ecxt->event_ip = ip;
+	eh->ecxt->event_stack = get_current_stack();
+
+	pos = find_hist_pos(wg);
+	if (pos == -1)
+		goto out;
+
+	ctxt_id = cur_ctxt_id();
+	end = hist_pos_next();
+	end = end > pos ? end : end + DEPT_MAX_WAIT_HIST;
+	for (wh = hist(pos); pos < end; wh = hist(++pos)) {
+		if (dt->in_sched && wh->wait->sched_sleep)
+			continue;
+
+		if (wh->ctxt_id == ctxt_id)
+			add_dep(eh->ecxt, wh->wait);
+	}
+
+	for (i = 0; i < DEPT_CXT_IRQS_NR; i++) {
+		struct dept_ecxt *e;
+
+		if (before(dt->wgen_enirq[i], wg))
+			continue;
+
+		e = eh->ecxt;
+		add_iecxt(e->class, i, e, false);
+	}
+out:
+	/*
+	 * Pop ecxt that temporarily has been added to handle this event.
+	 */
+	pop_ecxt(m, c);
+}
+
+static void del_dep_rcu(struct rcu_head *rh)
+{
+	struct dept_dep *d = container_of(rh, struct dept_dep, rh);
+
+	preempt_disable();
+	del_dep(d);
+	preempt_enable();
+}
+
+/*
+ * NOTE: Must be called with dept_lock held.
+ */
+static void disconnect_class(struct dept_class *c)
+{
+	struct dept_dep *d, *n;
+	int i;
+
+	list_for_each_entry_safe(d, n, &c->dep_head, dep_node) {
+		list_del_rcu(&d->dep_node);
+		list_del_rcu(&d->dep_rev_node);
+		hash_del_dep(d);
+		call_rcu(&d->rh, del_dep_rcu);
+	}
+
+	list_for_each_entry_safe(d, n, &c->dep_rev_head, dep_rev_node) {
+		list_del_rcu(&d->dep_node);
+		list_del_rcu(&d->dep_rev_node);
+		hash_del_dep(d);
+		call_rcu(&d->rh, del_dep_rcu);
+	}
+
+	for (i = 0; i < DEPT_CXT_IRQS_NR; i++) {
+		stale_iecxt(iecxt(c, i));
+		stale_iwait(iwait(c, i));
+	}
+}
+
+/*
+ * Context control
+ * =====================================================================
+ * Whether a wait is in {hard,soft}-IRQ context or whether
+ * {hard,soft}-IRQ has been enabled on the way to an event is very
+ * important to check dependency. All those things should be tracked.
+ */
+
+static unsigned long cur_enirqf(void)
+{
+	struct dept_task *dt = dept_task();
+	int he = dt->hardirqs_enabled;
+	int se = dt->softirqs_enabled;
+
+	if (he)
+		return DEPT_HIRQF | (se ? DEPT_SIRQF : 0UL);
+	return 0UL;
+}
+
+static int cur_cxt(void)
+{
+	if (lockdep_softirq_context(current))
+		return DEPT_CXT_SIRQ;
+	if (lockdep_hardirq_context())
+		return DEPT_CXT_HIRQ;
+	return DEPT_CXT_PROCESS;
+}
+
+static unsigned int cur_ctxt_id(void)
+{
+	struct dept_task *dt = dept_task();
+	int cxt = cur_cxt();
+
+	return dt->cxt_id[cxt] | (1UL << cxt);
+}
+
+static void enirq_transition(int irq)
+{
+	struct dept_task *dt = dept_task();
+	int i;
+
+	/*
+	 * IRQ can cut in on the way to the event. Used for cross-event
+	 * detection.
+	 *
+	 *    wait context	event context(ecxt)
+	 *    ------------	-------------------
+	 *    wait event
+	 *       UPDATE wgen
+	 *			observe IRQ enabled
+	 *			   UPDATE wgen
+	 *			   keep the wgen locally
+	 *
+	 *			on the event
+	 *			   check the wgen kept
+	 */
+
+	dt->wgen_enirq[irq] = next_wgen();
+
+	for (i = dt->ecxt_held_pos - 1; i >= 0; i--) {
+		struct dept_ecxt_held *eh;
+		struct dept_ecxt *e;
+
+		eh = dt->ecxt_held + i;
+		e = eh->ecxt;
+		if (e)
+			add_iecxt(e->class, irq, e, true);
+	}
+}
+
+static void dept_enirq(unsigned long ip)
+{
+	struct dept_task *dt = dept_task();
+	unsigned long irqf = cur_enirqf();
+	int irq;
+	unsigned long flags;
+
+	if (unlikely(!dept_working()))
+		return;
+
+	/*
+	 * IRQ ON/OFF transition might happen while Dept is working.
+	 * We cannot handle recursive entrance. Just ignore it.
+	 * Only transitions outside of Dept will be considered.
+	 */
+	if (dt->recursive)
+		return;
+
+	flags = dept_enter();
+
+	for_each_set_bit(irq, &irqf, DEPT_CXT_IRQS_NR) {
+		dt->enirq_ip[irq] = ip;
+		enirq_transition(irq);
+	}
+
+	dept_exit(flags);
+}
+
+void dept_softirqs_on_ip(unsigned long ip)
+{
+	/*
+	 * Assumes that it's called with IRQ disabled so that accessing
+	 * current's fields is not racy.
+	 */
+	dept_task()->softirqs_enabled = true;
+	dept_enirq(ip);
+}
+
+void dept_hardirqs_on(void)
+{
+	/*
+	 * Assumes that it's called with IRQ disabled so that accessing
+	 * current's fields is not racy.
+	 */
+	dept_task()->hardirqs_enabled = true;
+	dept_enirq(_RET_IP_);
+}
+
+void dept_softirqs_off(void)
+{
+	/*
+	 * Assumes that it's called with IRQ disabled so that accessing
+	 * current's fields is not racy.
+	 */
+	dept_task()->softirqs_enabled = false;
+}
+
+void noinstr dept_hardirqs_off(void)
+{
+	/*
+	 * Assumes that it's called with IRQ disabled so that accessing
+	 * current's fields is not racy.
+	 */
+	dept_task()->hardirqs_enabled = false;
+}
+EXPORT_SYMBOL_GPL(dept_hardirqs_off);
+
+void noinstr dept_update_cxt(void)
+{
+	struct dept_task *dt = dept_task();
+
+	dt->cxt_id[DEPT_CXT_PROCESS] += 1UL << DEPT_CXTS_NR;
+}
+
+/*
+ * Ensure it's the outmost softirq context.
+ */
+void dept_softirq_enter(void)
+{
+	struct dept_task *dt = dept_task();
+
+	dt->cxt_id[DEPT_CXT_SIRQ] += 1UL << DEPT_CXTS_NR;
+}
+
+/*
+ * Ensure it's the outmost hardirq context.
+ */
+void noinstr dept_hardirq_enter(void)
+{
+	struct dept_task *dt = dept_task();
+
+	dt->cxt_id[DEPT_CXT_HIRQ] += 1UL << DEPT_CXTS_NR;
+}
+
+void dept_sched_enter(void)
+{
+	dept_task()->in_sched = true;
+}
+
+void dept_sched_exit(void)
+{
+	dept_task()->in_sched = false;
+}
+
+/*
+ * Exposed APIs
+ * =====================================================================
+ */
+
+static void clean_classes_cache(struct dept_key *k)
+{
+	int i;
+
+	for (i = 0; i < DEPT_MAX_SUBCLASSES_CACHE; i++) {
+		if (!READ_ONCE(k->classes[i]))
+			continue;
+
+		WRITE_ONCE(k->classes[i], NULL);
+	}
+}
+
+/*
+ * Assume we don't have to consider race with the map when
+ * dept_map_init() is called.
+ */
+void dept_map_init(struct dept_map *m, struct dept_key *k, int sub_u,
+		   const char *n)
+{
+	unsigned long flags;
+
+	if (unlikely(!dept_working())) {
+		m->nocheck = true;
+		return;
+	}
+
+	if (DEPT_WARN_ON(sub_u < 0)) {
+		m->nocheck = true;
+		return;
+	}
+
+	if (DEPT_WARN_ON(sub_u >= DEPT_MAX_SUBCLASSES_USR)) {
+		m->nocheck = true;
+		return;
+	}
+
+	/*
+	 * Allow recursive entrance.
+	 */
+	flags = dept_enter_recursive();
+
+	clean_classes_cache(&m->map_key);
+
+	m->keys = k;
+	m->sub_u = sub_u;
+	m->name = n;
+	m->wgen = 0U;
+	m->nocheck = !valid_key(k);
+	m->lockdep_map = NULL;
+
+	dept_exit_recursive(flags);
+}
+EXPORT_SYMBOL_GPL(dept_map_init);
+
+/*
+ * Assume we don't have to consider race with the map when
+ * dept_map_reinit() is called.
+ */
+void dept_map_reinit(struct dept_map *m, struct dept_key *k, int sub_u,
+		     const char *n)
+{
+	unsigned long flags;
+
+	if (unlikely(!dept_working())) {
+		m->nocheck = true;
+		return;
+	}
+
+	/*
+	 * Allow recursive entrance.
+	 */
+	flags = dept_enter_recursive();
+
+	if (k) {
+		clean_classes_cache(&m->map_key);
+		m->keys = k;
+		m->nocheck = !valid_key(k);
+	}
+
+	if (sub_u >= 0 && sub_u < DEPT_MAX_SUBCLASSES_USR)
+		m->sub_u = sub_u;
+
+	if (n)
+		m->name = n;
+
+	m->wgen = 0U;
+
+	dept_exit_recursive(flags);
+}
+EXPORT_SYMBOL_GPL(dept_map_reinit);
+
+void dept_ext_wgen_init(struct dept_ext_wgen *ewg)
+{
+	ewg->wgen = 0U;
+}
+
+void dept_map_copy(struct dept_map *to, struct dept_map *from)
+{
+	if (unlikely(!dept_working())) {
+		to->nocheck = true;
+		return;
+	}
+
+	*to = *from;
+
+	/*
+	 * XXX: 'to' might be in a stack or something. Using the address
+	 * in a stack segment as a key is meaningless. Just ignore the
+	 * case for now.
+	 */
+	if (!to->keys) {
+		to->nocheck = true;
+		return;
+	}
+
+	/*
+	 * Since the class cache can be modified concurrently we could
+	 * observe half pointers (64bit arch using 32bit copy
+	 * instructions).  Therefore clear the caches and take the
+	 * performance hit.
+	 */
+	clean_classes_cache(&to->map_key);
+}
+
+LIST_HEAD(dept_classes);
+
+static bool within(const void *addr, void *start, unsigned long size)
+{
+	return addr >= start && addr < start + size;
+}
+
+void dept_free_range(void *start, unsigned int sz)
+{
+	struct dept_task *dt = dept_task();
+	struct dept_class *c, *n;
+	unsigned long flags;
+
+	if (unlikely(!dept_working()))
+		return;
+
+	if (dt->recursive) {
+		DEPT_STOP("Failed to successfully free Dept objects.\n");
+		return;
+	}
+
+	flags = dept_enter();
+
+	/*
+	 * dept_free_range() should not fail.
+	 *
+	 * FIXME: Should be fixed if dept_free_range() causes deadlock
+	 * with dept_lock().
+	 */
+	while (unlikely(!dept_lock()))
+		cpu_relax();
+
+	list_for_each_entry_safe(c, n, &dept_classes, all_node) {
+		if (!within((void *)c->key, start, sz) &&
+		    !within(c->name, start, sz))
+			continue;
+
+		hash_del_class(c);
+		disconnect_class(c);
+		list_del(&c->all_node);
+		invalidate_class(c);
+
+		/*
+		 * Actual deletion will happen on the rcu callback
+		 * that has been added in disconnect_class().
+		 */
+		del_class(c);
+	}
+	dept_unlock();
+	dept_exit(flags);
+
+	/*
+	 * Wait until even lockless hash_lookup_class() for the class
+	 * returns NULL.
+	 */
+	might_sleep();
+	synchronize_rcu();
+}
+
+static int sub_id(struct dept_map *m, int e)
+{
+	return (m ? m->sub_u : 0) + e * DEPT_MAX_SUBCLASSES_USR;
+}
+
+static struct dept_class *check_new_class(struct dept_key *local,
+					  struct dept_key *k, int sub_id,
+					  const char *n, bool sched_map)
+{
+	struct dept_class *c = NULL;
+
+	if (DEPT_WARN_ON(sub_id >= DEPT_MAX_SUBCLASSES))
+		return NULL;
+
+	if (DEPT_WARN_ON(!k))
+		return NULL;
+
+	/*
+	 * XXX: Assume that users prevent the map from using if any of
+	 * the cached keys has been invalidated. If not, the cache,
+	 * local->classes should not be used because it would be racy
+	 * with class deletion.
+	 */
+	if (local && sub_id < DEPT_MAX_SUBCLASSES_CACHE)
+		c = READ_ONCE(local->classes[sub_id]);
+
+	if (c)
+		return c;
+
+	c = lookup_class((unsigned long)k->base + sub_id);
+	if (c)
+		goto caching;
+
+	if (unlikely(!dept_lock()))
+		return NULL;
+
+	c = lookup_class((unsigned long)k->base + sub_id);
+	if (unlikely(c))
+		goto unlock;
+
+	c = new_class();
+	if (unlikely(!c))
+		goto unlock;
+
+	c->name = n;
+	c->sched_map = sched_map;
+	c->sub_id = sub_id;
+	c->key = (unsigned long)(k->base + sub_id);
+	hash_add_class(c);
+	list_add(&c->all_node, &dept_classes);
+unlock:
+	dept_unlock();
+caching:
+	if (local && sub_id < DEPT_MAX_SUBCLASSES_CACHE)
+		WRITE_ONCE(local->classes[sub_id], c);
+
+	return c;
+}
+
+/*
+ * Called between dept_enter() and dept_exit().
+ */
+static void __dept_wait(struct dept_map *m, unsigned long w_f,
+			unsigned long ip, const char *w_fn, int sub_l,
+			bool sched_sleep, bool sched_map, bool timeout)
+{
+	int e;
+
+	/*
+	 * Be as conservative as possible. In case of multiple waits for
+	 * a single dept_map, we are going to keep only the last wait's
+	 * wgen for simplicity - keeping all wgens seems overengineering.
+	 *
+	 * Of course, it might cause missing some dependencies that
+	 * would rarely, probably never, happen but it helps avoid
+	 * false positive reports.
+	 */
+	for_each_set_bit(e, &w_f, DEPT_MAX_SUBCLASSES_EVT) {
+		struct dept_class *c;
+		struct dept_key *k;
+
+		k = m->keys ?: &m->map_key;
+		c = check_new_class(&m->map_key, k,
+				    sub_id(m, e), m->name, sched_map);
+		if (!c)
+			continue;
+
+		add_wait(m, c, ip, w_fn, sub_l, sched_sleep, timeout);
+	}
+}
+
+/*
+ * Called between dept_enter() and dept_exit().
+ */
+static void __dept_event(struct dept_map *m, struct dept_map *real_m,
+		unsigned long e_f, unsigned long ip, const char *e_fn,
+		bool sched_map, unsigned int wg,
+		struct dept_stack *ewait_stack)
+{
+	struct dept_class *c;
+	struct dept_key *k;
+	int e;
+
+	e = find_first_bit(&e_f, DEPT_MAX_SUBCLASSES_EVT);
+
+	if (DEPT_WARN_ON(e >= DEPT_MAX_SUBCLASSES_EVT))
+		return;
+
+	/*
+	 * An event is an event. If the caller passed more than single
+	 * event, then warn it and handle the event corresponding to
+	 * the first bit anyway.
+	 */
+	DEPT_WARN_ON(1UL << e != e_f);
+
+	k = m->keys ?: &m->map_key;
+	c = check_new_class(&m->map_key, k, sub_id(m, e), m->name, sched_map);
+
+	if (c)
+		do_event(m, real_m, c, wg, ip, e_fn, ewait_stack);
+}
+
+void dept_wait(struct dept_map *m, unsigned long w_f,
+	       unsigned long ip, const char *w_fn, int sub_l,
+	       long timeoutval)
+{
+	struct dept_task *dt = dept_task();
+	unsigned long flags;
+	bool timeout;
+
+	if (unlikely(!dept_working()))
+		return;
+
+	timeout = timeoutval > 0 && timeoutval < MAX_SCHEDULE_TIMEOUT;
+
+#if !defined(CONFIG_DEPT_AGGRESSIVE_TIMEOUT_WAIT)
+	if (timeout)
+		return;
+#endif
+
+	if (dt->recursive)
+		return;
+
+	if (m->nocheck)
+		return;
+
+	flags = dept_enter();
+
+	__dept_wait(m, w_f, ip, w_fn, sub_l, false, false, timeout);
+
+	dept_exit(flags);
+}
+EXPORT_SYMBOL_GPL(dept_wait);
+
+void dept_stage_wait(struct dept_map *m, struct dept_key *k,
+		     unsigned long ip, const char *w_fn,
+		     long timeoutval)
+{
+	struct dept_task *dt = dept_task();
+	unsigned long flags;
+	bool timeout;
+
+	if (unlikely(!dept_working()))
+		return;
+
+	timeout = timeoutval > 0 && timeoutval < MAX_SCHEDULE_TIMEOUT;
+
+#if !defined(CONFIG_DEPT_AGGRESSIVE_TIMEOUT_WAIT)
+	if (timeout)
+		return;
+#endif
+
+	if (m && m->nocheck)
+		return;
+
+	/*
+	 * Either m or k should be passed. Which means Dept relies on
+	 * either its own map or the caller's position in the code when
+	 * determining its class.
+	 */
+	if (DEPT_WARN_ON(!m && !k))
+		return;
+
+	/*
+	 * Allow recursive entrance.
+	 */
+	flags = dept_enter_recursive();
+
+	/*
+	 * Ensure the outmost dept_stage_wait() works.
+	 */
+	if (dt->stage_m.keys)
+		goto exit;
+
+	arch_spin_lock(&dt->stage_lock);
+	if (m) {
+		dt->stage_m = *m;
+		dt->stage_real_m = m;
+
+		/*
+		 * Ensure dt->stage_m.keys != NULL and it works with the
+		 * map's map_key, not stage_m's one when ->keys == NULL.
+		 */
+		if (!m->keys)
+			dt->stage_m.keys = &m->map_key;
+	} else {
+		dt->stage_m.name = w_fn;
+		dt->stage_sched_map = true;
+		dt->stage_real_m = &dt->stage_m;
+	}
+
+	/*
+	 * dept_map_reinit() includes WRITE_ONCE(->wgen, 0U) that
+	 * effectively disables the map just in case real sleep won't
+	 * happen. dept_request_event_wait_commit() will enable it.
+	 */
+	dept_map_reinit(&dt->stage_m, k, -1, NULL);
+
+	dt->stage_w_fn = w_fn;
+	dt->stage_ip = ip;
+	dt->stage_timeout = timeout;
+	arch_spin_unlock(&dt->stage_lock);
+exit:
+	dept_exit_recursive(flags);
+}
+EXPORT_SYMBOL_GPL(dept_stage_wait);
+
+static void __dept_clean_stage(struct dept_task *dt)
+{
+	memset(&dt->stage_m, 0x0, sizeof(struct dept_map));
+	dt->stage_real_m = NULL;
+	dt->stage_sched_map = false;
+	dt->stage_w_fn = NULL;
+	dt->stage_ip = 0UL;
+	dt->stage_timeout = false;
+	if (dt->stage_wait_stack)
+		put_stack(dt->stage_wait_stack);
+	dt->stage_wait_stack = NULL;
+}
+
+void dept_clean_stage(void)
+{
+	struct dept_task *dt = dept_task();
+	unsigned long flags;
+
+	if (unlikely(!dept_working()))
+		return;
+
+	/*
+	 * Allow recursive entrance.
+	 */
+	flags = dept_enter_recursive();
+	arch_spin_lock(&dt->stage_lock);
+	__dept_clean_stage(dt);
+	arch_spin_unlock(&dt->stage_lock);
+	dept_exit_recursive(flags);
+}
+EXPORT_SYMBOL_GPL(dept_clean_stage);
+
+/*
+ * Always called from __schedule().
+ */
+void dept_request_event_wait_commit(void)
+{
+	struct dept_task *dt = dept_task();
+	unsigned long flags;
+	unsigned int wg;
+	unsigned long ip;
+	const char *w_fn;
+	bool sched_map;
+	bool timeout;
+
+	if (unlikely(!dept_working()))
+		return;
+
+	/*
+	 * It's impossible that __schedule() is called while Dept is
+	 * working that already disabled IRQ at the entrance.
+	 */
+	if (DEPT_WARN_ON(dt->recursive))
+		return;
+
+	flags = dept_enter();
+
+	arch_spin_lock(&dt->stage_lock);
+
+	/*
+	 * Checks if current has staged a wait.
+	 */
+	if (!dt->stage_m.keys) {
+		arch_spin_unlock(&dt->stage_lock);
+		goto exit;
+	}
+
+	w_fn = dt->stage_w_fn;
+	ip = dt->stage_ip;
+	sched_map = dt->stage_sched_map;
+	timeout = dt->stage_timeout;
+
+	wg = next_wgen();
+	WRITE_ONCE(dt->stage_m.wgen, wg);
+
+	/*
+	 * __schedule() can be hit multiple times between
+	 * dept_stage_wait() and dept_clean_stage().  In that case,
+	 * keep the first stacktrace only.  That's enough.
+	 */
+	if (!dt->stage_wait_stack)
+		dt->stage_wait_stack = get_current_stack();
+	arch_spin_unlock(&dt->stage_lock);
+
+	__dept_wait(&dt->stage_m, 1UL, ip, w_fn, 0, true, sched_map, timeout);
+exit:
+	dept_exit(flags);
+}
+
+/*
+ * Always called from try_to_wake_up().
+ */
+void dept_ttwu_stage_wait(struct task_struct *requestor, unsigned long ip)
+{
+	struct dept_task *dt = dept_task();
+	struct dept_task *dt_req = &requestor->dept_task;
+	unsigned long flags;
+	struct dept_map m;
+	struct dept_map *real_m;
+	bool sched_map;
+	struct dept_stack *ewait_stack;
+
+	if (unlikely(!dept_working()))
+		return;
+
+	if (dt->recursive)
+		return;
+
+	flags = dept_enter();
+
+	arch_spin_lock(&dt_req->stage_lock);
+
+	/*
+	 * Serializing is unnecessary as long as it always comes from
+	 * try_to_wake_up().
+	 */
+	m = dt_req->stage_m;
+	sched_map = dt_req->stage_sched_map;
+	real_m = dt_req->stage_real_m;
+	ewait_stack = dt_req->stage_wait_stack;
+	if (ewait_stack)
+		get_stack(ewait_stack);
+
+	__dept_clean_stage(dt_req);
+	arch_spin_unlock(&dt_req->stage_lock);
+
+	/*
+	 * ->stage_m.keys should not be NULL if it's in use. Should
+	 * make sure that it's not NULL when staging a valid map.
+	 */
+	if (!m.keys)
+		goto exit;
+
+	__dept_event(&m, real_m, 1UL, ip, "try_to_wake_up", sched_map,
+			m.wgen, ewait_stack);
+exit:
+	if (ewait_stack)
+		put_stack(ewait_stack);
+
+	dept_exit(flags);
+}
+
+/*
+ * Modifies the latest ecxt corresponding to m and e_f.
+ */
+void dept_map_ecxt_modify(struct dept_map *m, unsigned long e_f,
+			  struct dept_key *new_k, unsigned long new_e_f,
+			  unsigned long new_ip, const char *new_c_fn,
+			  const char *new_e_fn, int new_sub_l)
+{
+	struct dept_task *dt = dept_task();
+	struct dept_ecxt_held *eh;
+	struct dept_class *c;
+	struct dept_key *k;
+	unsigned long flags;
+	int pos = -1;
+	int new_e;
+	int e;
+
+	if (unlikely(!dept_working()))
+		return;
+
+	/*
+	 * XXX: Couldn't handle re-enterance cases. Ignore it for now.
+	 */
+	if (dt->recursive)
+		return;
+
+	/*
+	 * Should go ahead no matter whether ->nocheck == true or not
+	 * because ->nocheck value can be changed within the ecxt area
+	 * delimitated by dept_ecxt_enter() and dept_ecxt_exit().
+	 */
+
+	flags = dept_enter();
+
+	for_each_set_bit(e, &e_f, DEPT_MAX_SUBCLASSES_EVT) {
+		k = m->keys ?: &m->map_key;
+		c = check_new_class(&m->map_key, k,
+				    sub_id(m, e), m->name, false);
+		if (!c)
+			continue;
+
+		/*
+		 * When it found an ecxt for any event in e_f, done.
+		 */
+		pos = find_ecxt_pos(m, c, true);
+		if (pos != -1)
+			break;
+	}
+
+	if (unlikely(pos == -1))
+		goto exit;
+
+	eh = dt->ecxt_held + pos;
+	new_sub_l = new_sub_l >= 0 ? new_sub_l : eh->sub_l;
+
+	new_e = find_first_bit(&new_e_f, DEPT_MAX_SUBCLASSES_EVT);
+
+	if (new_e < DEPT_MAX_SUBCLASSES_EVT)
+		/*
+		 * Let it work with the first bit anyway.
+		 */
+		DEPT_WARN_ON(1UL << new_e != new_e_f);
+	else
+		new_e = e;
+
+	pop_ecxt(m, c);
+
+	/*
+	 * Apply the key to the map.
+	 */
+	if (new_k)
+		dept_map_reinit(m, new_k, -1, NULL);
+
+	k = m->keys ?: &m->map_key;
+	c = check_new_class(&m->map_key, k, sub_id(m, new_e), m->name, false);
+
+	if (c && add_ecxt(m, c, new_ip, new_c_fn, new_e_fn, new_sub_l, NULL))
+		goto exit;
+
+	/*
+	 * Successfully pop_ecxt()ed but failed to add_ecxt().
+	 */
+	dt->missing_ecxt++;
+exit:
+	dept_exit(flags);
+}
+EXPORT_SYMBOL_GPL(dept_map_ecxt_modify);
+
+void dept_ecxt_enter(struct dept_map *m, unsigned long e_f, unsigned long ip,
+		     const char *c_fn, const char *e_fn, int sub_l)
+{
+	struct dept_task *dt = dept_task();
+	unsigned long flags;
+	struct dept_class *c;
+	struct dept_key *k;
+	int e;
+
+	if (unlikely(!dept_working()))
+		return;
+
+	if (dt->recursive) {
+		dt->missing_ecxt++;
+		return;
+	}
+
+	/*
+	 * Should go ahead no matter whether ->nocheck == true or not
+	 * because ->nocheck value can be changed within the ecxt area
+	 * delimitated by dept_ecxt_enter() and dept_ecxt_exit().
+	 */
+
+	flags = dept_enter();
+
+	e = find_first_bit(&e_f, DEPT_MAX_SUBCLASSES_EVT);
+
+	if (e >= DEPT_MAX_SUBCLASSES_EVT)
+		goto missing_ecxt;
+
+	/*
+	 * An event is an event. If the caller passed more than single
+	 * event, then warn it and handle the event corresponding to
+	 * the first bit anyway.
+	 */
+	DEPT_WARN_ON(1UL << e != e_f);
+
+	k = m->keys ?: &m->map_key;
+	c = check_new_class(&m->map_key, k, sub_id(m, e), m->name, false);
+
+	if (c && add_ecxt(m, c, ip, c_fn, e_fn, sub_l, NULL))
+		goto exit;
+missing_ecxt:
+	dt->missing_ecxt++;
+exit:
+	dept_exit(flags);
+}
+EXPORT_SYMBOL_GPL(dept_ecxt_enter);
+
+bool dept_ecxt_holding(struct dept_map *m, unsigned long e_f)
+{
+	struct dept_task *dt = dept_task();
+	unsigned long flags;
+	bool ret = false;
+	int e;
+
+	if (unlikely(!dept_working()))
+		return false;
+
+	if (dt->recursive)
+		return false;
+
+	flags = dept_enter();
+
+	for_each_set_bit(e, &e_f, DEPT_MAX_SUBCLASSES_EVT) {
+		struct dept_class *c;
+		struct dept_key *k;
+
+		k = m->keys ?: &m->map_key;
+		c = check_new_class(&m->map_key, k,
+				    sub_id(m, e), m->name, false);
+		if (!c)
+			continue;
+
+		if (find_ecxt_pos(m, c, true) != -1) {
+			ret = true;
+			break;
+		}
+	}
+
+	dept_exit(flags);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dept_ecxt_holding);
+
+void dept_request_event(struct dept_map *m, struct dept_ext_wgen *ewg)
+{
+	unsigned long flags;
+	unsigned int wg;
+	unsigned int *wg_p;
+
+	if (unlikely(!dept_working()))
+		return;
+
+	if (m->nocheck)
+		return;
+
+	/*
+	 * Allow recursive entrance.
+	 */
+	flags = dept_enter_recursive();
+
+	wg_p = ewg ? &ewg->wgen : &m->wgen;
+
+	wg = next_wgen();
+	WRITE_ONCE(*wg_p, wg);
+
+	dept_exit_recursive(flags);
+}
+EXPORT_SYMBOL_GPL(dept_request_event);
+
+void dept_event(struct dept_map *m, unsigned long e_f,
+		unsigned long ip, const char *e_fn,
+		struct dept_ext_wgen *ewg)
+{
+	struct dept_task *dt = dept_task();
+	unsigned long flags;
+	unsigned int *wg_p;
+
+	if (unlikely(!dept_working()))
+		return;
+
+	if (m->nocheck)
+		return;
+
+	wg_p = ewg ? &ewg->wgen : &m->wgen;
+
+	if (dt->recursive) {
+		/*
+		 * Dept won't work with this even though an event
+		 * context has been asked. Don't make it confused at
+		 * handling the event. Disable it until the next.
+		 */
+		WRITE_ONCE(*wg_p, 0U);
+		return;
+	}
+
+	flags = dept_enter();
+
+	__dept_event(m, m, e_f, ip, e_fn, false, READ_ONCE(*wg_p), NULL);
+
+	/*
+	 * Keep the map diabled until the next sleep.
+	 */
+	WRITE_ONCE(*wg_p, 0U);
+
+	dept_exit(flags);
+}
+EXPORT_SYMBOL_GPL(dept_event);
+
+void dept_ecxt_exit(struct dept_map *m, unsigned long e_f,
+		    unsigned long ip)
+{
+	struct dept_task *dt = dept_task();
+	unsigned long flags;
+	int e;
+
+	if (unlikely(!dept_working()))
+		return;
+
+	if (dt->recursive) {
+		dt->missing_ecxt--;
+		return;
+	}
+
+	/*
+	 * Should go ahead no matter whether ->nocheck == true or not
+	 * because ->nocheck value can be changed within the ecxt area
+	 * delimitated by dept_ecxt_enter() and dept_ecxt_exit().
+	 */
+
+	flags = dept_enter();
+
+	for_each_set_bit(e, &e_f, DEPT_MAX_SUBCLASSES_EVT) {
+		struct dept_class *c;
+		struct dept_key *k;
+
+		k = m->keys ?: &m->map_key;
+		c = check_new_class(&m->map_key, k,
+				    sub_id(m, e), m->name, false);
+		if (!c)
+			continue;
+
+		/*
+		 * When it found an ecxt for any event in e_f, done.
+		 */
+		if (pop_ecxt(m, c))
+			goto exit;
+	}
+
+	dt->missing_ecxt--;
+exit:
+	dept_exit(flags);
+}
+EXPORT_SYMBOL_GPL(dept_ecxt_exit);
+
+void dept_task_exit(struct task_struct *t)
+{
+	struct dept_task *dt = &t->dept_task;
+	int i;
+
+	if (unlikely(!dept_working()))
+		return;
+
+	raw_local_irq_disable();
+
+	if (dt->stack) {
+		put_stack(dt->stack);
+		dt->stack = NULL;
+	}
+
+	if (dt->stage_wait_stack) {
+		put_stack(dt->stage_wait_stack);
+		dt->stage_wait_stack = NULL;
+	}
+
+	for (i = 0; i < dt->ecxt_held_pos; i++) {
+		if (dt->ecxt_held[i].class) {
+			put_class(dt->ecxt_held[i].class);
+			dt->ecxt_held[i].class = NULL;
+		}
+		if (dt->ecxt_held[i].ecxt) {
+			put_ecxt(dt->ecxt_held[i].ecxt);
+			dt->ecxt_held[i].ecxt = NULL;
+		}
+	}
+
+	for (i = 0; i < DEPT_MAX_WAIT_HIST; i++) {
+		if (dt->wait_hist[i].wait) {
+			put_wait(dt->wait_hist[i].wait);
+			dt->wait_hist[i].wait = NULL;
+		}
+	}
+
+	dt->task_exit = true;
+	dept_off();
+
+	raw_local_irq_enable();
+}
+
+void dept_task_init(struct task_struct *t)
+{
+	memset(&t->dept_task, 0x0, sizeof(struct dept_task));
+	t->dept_task.stage_lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
+}
+
+void dept_key_init(struct dept_key *k)
+{
+	struct dept_task *dt = dept_task();
+	unsigned long flags;
+	int sub_id;
+
+	if (unlikely(!dept_working()))
+		return;
+
+	if (dt->recursive) {
+		DEPT_STOP("Key initialization fails.\n");
+		return;
+	}
+
+	flags = dept_enter();
+
+	clean_classes_cache(k);
+
+	/*
+	 * dept_key_init() should not fail.
+	 *
+	 * FIXME: Should be fixed if dept_key_init() causes deadlock
+	 * with dept_lock().
+	 */
+	while (unlikely(!dept_lock()))
+		cpu_relax();
+
+	for (sub_id = 0; sub_id < DEPT_MAX_SUBCLASSES; sub_id++) {
+		struct dept_class *c;
+
+		c = lookup_class((unsigned long)k->base + sub_id);
+		if (!c)
+			continue;
+
+		DEPT_STOP("The class(%s/%d) has not been removed.\n",
+			  c->name, sub_id);
+		break;
+	}
+
+	dept_unlock();
+	dept_exit(flags);
+}
+EXPORT_SYMBOL_GPL(dept_key_init);
+
+void dept_key_destroy(struct dept_key *k)
+{
+	struct dept_task *dt = dept_task();
+	unsigned long flags;
+	int sub_id;
+
+	if (unlikely(!dept_working()))
+		return;
+
+	if (dt->recursive == 1 && dt->task_exit) {
+		/*
+		 * Need to allow to go ahead in this case where
+		 * ->recursive has been set to 1 by dept_off() in
+		 * dept_task_exit() and ->task_exit has been set to
+		 * true in dept_task_exit().
+		 */
+	} else if (dt->recursive) {
+		DEPT_STOP("Key destroying fails.\n");
+		return;
+	}
+
+	flags = dept_enter();
+
+	/*
+	 * dept_key_destroy() should not fail.
+	 *
+	 * FIXME: Should be fixed if dept_key_destroy() causes deadlock
+	 * with dept_lock().
+	 */
+	while (unlikely(!dept_lock()))
+		cpu_relax();
+
+	for (sub_id = 0; sub_id < DEPT_MAX_SUBCLASSES; sub_id++) {
+		struct dept_class *c;
+
+		c = lookup_class((unsigned long)k->base + sub_id);
+		if (!c)
+			continue;
+
+		hash_del_class(c);
+		disconnect_class(c);
+		list_del(&c->all_node);
+		invalidate_class(c);
+
+		/*
+		 * Actual deletion will happen on the rcu callback
+		 * that has been added in disconnect_class().
+		 */
+		del_class(c);
+	}
+
+	dept_unlock();
+	dept_exit(flags);
+
+	/*
+	 * Wait until even lockless hash_lookup_class() for the class
+	 * returns NULL.
+	 */
+	might_sleep();
+	synchronize_rcu();
+}
+EXPORT_SYMBOL_GPL(dept_key_destroy);
+
+static void move_llist(struct llist_head *to, struct llist_head *from)
+{
+	struct llist_node *first = llist_del_all(from);
+	struct llist_node *last = first;
+
+	if (!first)
+		return;
+
+	while (llist_next(last))
+		last = llist_next(last);
+	llist_add_batch(first, last, to);
+}
+
+static void migrate_per_cpu_pool(void)
+{
+	const int boot_cpu = 0;
+	int i;
+
+	/*
+	 * The boot CPU has been using the temporal local pool so far.
+	 * From now on that per_cpu areas have been ready, use the
+	 * per_cpu local pool instead.
+	 */
+	DEPT_WARN_ON(smp_processor_id() != boot_cpu);
+	for (i = 0; i < OBJECT_NR; i++) {
+		struct llist_head *from;
+		struct llist_head *to;
+
+		from = &dept_pool[i].boot_pool;
+		to = per_cpu_ptr(dept_pool[i].lpool, boot_cpu);
+		move_llist(to, from);
+	}
+}
+
+#define B2KB(B) ((B) / 1024)
+
+/*
+ * Should be called after setup_per_cpu_areas() and before no non-boot
+ * CPUs have been on.
+ */
+void __init dept_init(void)
+{
+	size_t mem_total = 0;
+
+	local_irq_disable();
+	dept_per_cpu_ready = 1;
+	migrate_per_cpu_pool();
+	local_irq_enable();
+
+#define HASH(id, bits) BUILD_BUG_ON(1 << (bits) <= 0);
+	#include "dept_hash.h"
+#undef HASH
+#define OBJECT(id, nr) mem_total += sizeof(struct dept_##id) * nr;
+	#include "dept_object.h"
+#undef OBJECT
+#define HASH(id, bits) mem_total += sizeof(struct hlist_head) * (1 << (bits));
+	#include "dept_hash.h"
+#undef HASH
+
+	pr_info("DEPendency Tracker: Copyright (c) 2020 LG Electronics, Inc., Byungchul Park\n");
+	pr_info("... DEPT_MAX_STACK_ENTRY: %d\n", DEPT_MAX_STACK_ENTRY);
+	pr_info("... DEPT_MAX_WAIT_HIST  : %d\n", DEPT_MAX_WAIT_HIST);
+	pr_info("... DEPT_MAX_ECXT_HELD  : %d\n", DEPT_MAX_ECXT_HELD);
+	pr_info("... DEPT_MAX_SUBCLASSES : %d\n", DEPT_MAX_SUBCLASSES);
+#define OBJECT(id, nr)							\
+	pr_info("... memory initially used by %s: %zu KB\n",		\
+	       #id, B2KB(sizeof(spool_##id) + sizeof(rpool_##id)));
+	#include "dept_object.h"
+#undef OBJECT
+#define HASH(id, bits)							\
+	pr_info("... hash list head used by %s: %zu KB\n",		\
+	       #id, B2KB(sizeof(struct hlist_head) * (1 << (bits))));
+	#include "dept_hash.h"
+#undef HASH
+	pr_info("... total memory initially used by objects and hashs: %zu KB\n", B2KB(mem_total));
+	pr_info("... per task memory footprint: %zu bytes\n", sizeof(struct dept_task));
+}
diff --git a/kernel/dependency/dept_hash.h b/kernel/dependency/dept_hash.h
new file mode 100644
index 00000000000000..fd85aab1fdfbe0
--- /dev/null
+++ b/kernel/dependency/dept_hash.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * HASH(id, bits)
+ *
+ * id  : Id for the object of struct dept_##id.
+ * bits: 1UL << bits is the hash table size.
+ */
+
+HASH(dep, 12)
+HASH(class, 12)
diff --git a/kernel/dependency/dept_internal.h b/kernel/dependency/dept_internal.h
new file mode 100644
index 00000000000000..c02783ecf0c4d5
--- /dev/null
+++ b/kernel/dependency/dept_internal.h
@@ -0,0 +1,314 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * DEPT(DEPendency Tracker) - runtime dependency tracker internal header
+ *
+ * Started by Byungchul Park <max.byungchul.park@gmail.com>:
+ *
+ *  Copyright (c) 2020 LG Electronics, Inc., Byungchul Park
+ *  Copyright (c) 2024 SK hynix, Inc., Byungchul Park
+ */
+
+#ifndef __DEPT_INTERNAL_H
+#define __DEPT_INTERNAL_H
+
+#ifdef CONFIG_DEPT
+#include <linux/dept.h>
+#include <linux/percpu.h>
+#include <linux/llist.h>
+#include <linux/types.h>
+
+struct dept_pool {
+	const char			*name;
+
+	/*
+	 * object size
+	 */
+	size_t				obj_sz;
+
+	/*
+	 * the remaining number of the object in spool
+	 */
+	int				obj_nr;
+
+	/*
+	 * the number of the object in spool
+	 */
+	int				tot_nr;
+
+	/*
+	 * accumulated amount of memory used by the object in byte
+	 */
+	atomic_t			acc_sz;
+
+	/*
+	 * offset of ->pool_node
+	 */
+	size_t				node_off;
+
+	/*
+	 * pointer to the pool
+	 */
+	void				*spool; /* static pool */
+	void				*rpool; /* reserved pool */
+	struct llist_head		boot_pool;
+	struct llist_head __percpu	*lpool; /* local pool */
+};
+
+struct dept_ecxt;
+struct dept_iecxt {
+	struct dept_ecxt		*ecxt;
+	int				enirq;
+	/*
+	 * flag to prevent adding a new ecxt
+	 */
+	bool				staled;
+};
+
+struct dept_wait;
+struct dept_iwait {
+	struct dept_wait		*wait;
+	int				irq;
+	/*
+	 * flag to prevent adding a new wait
+	 */
+	bool				staled;
+	bool				touched;
+};
+
+struct dept_class {
+	union {
+		struct llist_node	pool_node;
+		struct {
+			/*
+			 * reference counter for object management
+			 */
+			atomic_t	ref;
+
+			/*
+			 * unique information about the class
+			 */
+			const char	*name;
+			unsigned long	key;
+			int		sub_id;
+
+			/*
+			 * for BFS
+			 */
+			unsigned int	bfs_gen;
+			struct dept_class *bfs_parent;
+			struct list_head bfs_node;
+
+			/*
+			 * for hashing this object
+			 */
+			struct hlist_node hash_node;
+
+			/*
+			 * for linking all classes
+			 */
+			struct list_head all_node;
+
+			/*
+			 * for associating its dependencies
+			 */
+			struct list_head dep_head;
+			struct list_head dep_rev_head;
+
+			/*
+			 * for tracking IRQ dependencies
+			 */
+			struct dept_iecxt iecxt[DEPT_CXT_IRQS_NR];
+			struct dept_iwait iwait[DEPT_CXT_IRQS_NR];
+
+			/*
+			 * classified by a map embedded in task_struct,
+			 * not an explicit map
+			 */
+			bool		sched_map;
+		};
+	};
+};
+
+struct dept_stack {
+	union {
+		struct llist_node	pool_node;
+		struct {
+			/*
+			 * reference counter for object management
+			 */
+			atomic_t	ref;
+
+			/*
+			 * backtrace entries
+			 */
+			unsigned long	raw[DEPT_MAX_STACK_ENTRY];
+			int nr;
+		};
+	};
+};
+
+struct dept_ecxt {
+	union {
+		struct llist_node	pool_node;
+		struct {
+			/*
+			 * reference counter for object management
+			 */
+			atomic_t	ref;
+
+			/*
+			 * function that entered to this ecxt
+			 */
+			const char	*ecxt_fn;
+
+			/*
+			 * event function
+			 */
+			const char	*event_fn;
+
+			/*
+			 * associated class
+			 */
+			struct dept_class *class;
+
+			/*
+			 * flag indicating which IRQ has been
+			 * enabled within the event context
+			 */
+			unsigned long	enirqf;
+
+			/*
+			 * where the IRQ-enabled happened
+			 */
+			unsigned long	enirq_ip[DEPT_CXT_IRQS_NR];
+			struct dept_stack *enirq_stack[DEPT_CXT_IRQS_NR];
+
+			/*
+			 * where the event context started
+			 */
+			unsigned long	ecxt_ip;
+			struct dept_stack *ecxt_stack;
+
+			/*
+			 * where the event triggered
+			 */
+			unsigned long	event_ip;
+			struct dept_stack *event_stack;
+
+			/*
+			 * wait that this event ttwu
+			 */
+			struct dept_stack *ewait_stack;
+		};
+	};
+};
+
+struct dept_wait {
+	union {
+		struct llist_node	pool_node;
+		struct {
+			/*
+			 * reference counter for object management
+			 */
+			atomic_t	ref;
+
+			/*
+			 * function causing this wait
+			 */
+			const char	*wait_fn;
+
+			/*
+			 * the associated class
+			 */
+			struct dept_class *class;
+
+			/*
+			 * which IRQ the wait was placed in
+			 */
+			unsigned long	irqf;
+
+			/*
+			 * where the IRQ wait happened
+			 */
+			unsigned long	irq_ip[DEPT_CXT_IRQS_NR];
+			struct dept_stack *irq_stack[DEPT_CXT_IRQS_NR];
+
+			/*
+			 * where the wait happened
+			 */
+			unsigned long	wait_ip;
+			struct dept_stack *wait_stack;
+
+			/*
+			 * whether this wait is for commit in scheduler
+			 */
+			bool		sched_sleep;
+
+			/*
+			 * whether a timeout is set
+			 */
+			bool		timeout;
+		};
+	};
+};
+
+struct dept_dep {
+	union {
+		struct llist_node	pool_node;
+		struct {
+			/*
+			 * reference counter for object management
+			 */
+			atomic_t	ref;
+
+			/*
+			 * key data of dependency
+			 */
+			struct dept_ecxt *ecxt;
+			struct dept_wait *wait;
+
+			/*
+			 * This object can be referred without dept_lock
+			 * held but with IRQ disabled, e.g. for hash
+			 * lookup. So deferred deletion is needed.
+			 */
+			struct rcu_head rh;
+
+			/*
+			 * for hashing this object
+			 */
+			struct hlist_node hash_node;
+
+			/*
+			 * for linking to a class object
+			 */
+			struct list_head dep_node;
+			struct list_head dep_rev_node;
+		};
+	};
+};
+
+struct dept_hash {
+	/*
+	 * hash table
+	 */
+	struct hlist_head		*table;
+
+	/*
+	 * size of the table e.i. 2^bits
+	 */
+	int				bits;
+};
+
+enum object_t {
+#define OBJECT(id, nr) OBJECT_##id,
+	#include "dept_object.h"
+#undef OBJECT
+	OBJECT_NR,
+};
+
+extern struct list_head dept_classes;
+extern struct dept_pool dept_pool[];
+
+#endif
+#endif /* __DEPT_INTERNAL_H */
diff --git a/kernel/dependency/dept_object.h b/kernel/dependency/dept_object.h
new file mode 100644
index 00000000000000..4f936adfa8eef8
--- /dev/null
+++ b/kernel/dependency/dept_object.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * OBJECT(id, nr)
+ *
+ * id: Id for the object of struct dept_##id.
+ * nr: # of the object that should be kept in the pool.
+ */
+
+OBJECT(dep, 1024 * 4 * 2)
+OBJECT(class, 1024 * 4)
+OBJECT(stack, 1024 * 4 * 8)
+OBJECT(ecxt, 1024 * 4 * 2)
+OBJECT(wait, 1024 * 4 * 4)
diff --git a/kernel/dependency/dept_proc.c b/kernel/dependency/dept_proc.c
new file mode 100644
index 00000000000000..f28992834588a8
--- /dev/null
+++ b/kernel/dependency/dept_proc.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Procfs knobs for Dept(DEPendency Tracker)
+ *
+ * Started by Byungchul Park <max.byungchul.park@gmail.com>:
+ *
+ *  Copyright (C) 2021 LG Electronics, Inc. , Byungchul Park
+ *  Copyright (C) 2024 SK hynix, Inc. , Byungchul Park
+ */
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/dept.h>
+#include "dept_internal.h"
+
+static void *l_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	/*
+	 * XXX: Serialize list traversal if needed. The following might
+	 * give a wrong information on contention.
+	 */
+	return seq_list_next(v, &dept_classes, pos);
+}
+
+static void *l_start(struct seq_file *m, loff_t *pos)
+{
+	/*
+	 * XXX: Serialize list traversal if needed. The following might
+	 * give a wrong information on contention.
+	 */
+	return seq_list_start_head(&dept_classes, *pos);
+}
+
+static void l_stop(struct seq_file *m, void *v)
+{
+}
+
+static int l_show(struct seq_file *m, void *v)
+{
+	struct dept_class *fc = list_entry(v, struct dept_class, all_node);
+	struct dept_dep *d;
+	const char *prefix;
+
+	if (v == &dept_classes) {
+		seq_puts(m, "All classes:\n\n");
+		return 0;
+	}
+
+	prefix = fc->sched_map ? "<sched> " : "";
+	seq_printf(m, "[%p] %s%s\n", (void *)fc->key, prefix, fc->name);
+
+	/*
+	 * XXX: Serialize list traversal if needed. The following might
+	 * give a wrong information on contention.
+	 */
+	list_for_each_entry(d, &fc->dep_head, dep_node) {
+		struct dept_class *tc = d->wait->class;
+
+		prefix = tc->sched_map ? "<sched> " : "";
+		seq_printf(m, " -> [%p] %s%s\n", (void *)tc->key, prefix, tc->name);
+	}
+	seq_puts(m, "\n");
+
+	return 0;
+}
+
+static const struct seq_operations dept_deps_ops = {
+	.start	= l_start,
+	.next	= l_next,
+	.stop	= l_stop,
+	.show	= l_show,
+};
+
+static int dept_stats_show(struct seq_file *m, void *v)
+{
+	int r;
+
+	seq_puts(m, "Accumulated amount of memory used by pools:\n\n");
+#define OBJECT(id, nr)							\
+	r = atomic_read(&dept_pool[OBJECT_##id].acc_sz);		\
+	seq_printf(m, "%s\t%d KB\n", #id, r / 1024);
+	#include "dept_object.h"
+#undef  OBJECT
+
+	return 0;
+}
+
+static int __init dept_proc_init(void)
+{
+	proc_create_seq("dept_deps", S_IRUSR, NULL, &dept_deps_ops);
+	proc_create_single("dept_stats", S_IRUSR, NULL, dept_stats_show);
+	return 0;
+}
+
+__initcall(dept_proc_init);
diff --git a/kernel/dependency/dept_unit_test.c b/kernel/dependency/dept_unit_test.c
new file mode 100644
index 00000000000000..e8dada2e3dfbaf
--- /dev/null
+++ b/kernel/dependency/dept_unit_test.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * DEPT unit test
+ *
+ * Started by Byungchul Park <max.byungchul.park@gmail.com>:
+ *
+ *  Copyright (c) 2025 SK hynix, Inc., Byungchul Park
+ */
+
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/dept.h>
+#include <linux/dept_unit_test.h>
+
+MODULE_DESCRIPTION("DEPT unit test");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Byungchul Park <max.byungchul.park@sk.com>");
+
+struct unit {
+	const char *name;
+	bool (*func)(void);
+	bool result;
+};
+
+static DEFINE_SPINLOCK(s1);
+static DEFINE_SPINLOCK(s2);
+static bool test_spin_lock_deadlock(void)
+{
+	dept_ut_results.circle_detected = false;
+
+	spin_lock(&s1);
+	spin_lock(&s2);
+	spin_unlock(&s2);
+	spin_unlock(&s1);
+
+	spin_lock(&s2);
+	spin_lock(&s1);
+	spin_unlock(&s1);
+	spin_unlock(&s2);
+
+	return dept_ut_results.circle_detected;
+}
+
+static DEFINE_MUTEX(m1);
+static DEFINE_MUTEX(m2);
+static bool test_mutex_lock_deadlock(void)
+{
+	dept_ut_results.circle_detected = false;
+
+	mutex_lock(&m1);
+	mutex_lock(&m2);
+	mutex_unlock(&m2);
+	mutex_unlock(&m1);
+
+	mutex_lock(&m2);
+	mutex_lock(&m1);
+	mutex_unlock(&m1);
+	mutex_unlock(&m2);
+
+	return dept_ut_results.circle_detected;
+}
+
+static bool test_wait_event_deadlock(void)
+{
+	struct dept_map dmap1;
+	struct dept_map dmap2;
+
+	sdt_map_init(&dmap1);
+	sdt_map_init(&dmap2);
+
+	dept_ut_results.circle_detected = false;
+
+	sdt_request_event(&dmap1); /* [S] */
+	sdt_wait(&dmap2); /* [W] */
+	sdt_event(&dmap1); /* [E] */
+
+	sdt_request_event(&dmap2); /* [S] */
+	sdt_wait(&dmap1); /* [W] */
+	sdt_event(&dmap2); /* [E] */
+
+	return dept_ut_results.circle_detected;
+}
+
+static struct unit units[] = {
+	{
+		.name = "spin lock deadlock test",
+		.func = test_spin_lock_deadlock,
+	},
+	{
+		.name = "mutex lock deadlock test",
+		.func = test_mutex_lock_deadlock,
+	},
+	{
+		.name = "wait event deadlock test",
+		.func = test_wait_event_deadlock,
+	},
+};
+
+static int __init dept_ut_init(void)
+{
+	int i;
+
+	lockdep_off();
+
+	dept_ut_results.ecxt_stack_valid_cnt = 0;
+	dept_ut_results.ecxt_stack_total_cnt = 0;
+	dept_ut_results.wait_stack_valid_cnt = 0;
+	dept_ut_results.wait_stack_total_cnt = 0;
+	dept_ut_results.evnt_stack_valid_cnt = 0;
+	dept_ut_results.evnt_stack_total_cnt = 0;
+
+	for (i = 0; i < ARRAY_SIZE(units); i++)
+		units[i].result = units[i].func();
+
+	pr_info("\n");
+	pr_info("******************************************\n");
+	pr_info("DEPT unit test results\n");
+	pr_info("******************************************\n");
+	for (i = 0; i < ARRAY_SIZE(units); i++) {
+		pr_info("(%s) %s\n", units[i].result ? "pass" : "fail",
+				units[i].name);
+	}
+	pr_info("ecxt stack valid count = %d/%d\n",
+			dept_ut_results.ecxt_stack_valid_cnt,
+			dept_ut_results.ecxt_stack_total_cnt);
+	pr_info("wait stack valid count = %d/%d\n",
+			dept_ut_results.wait_stack_valid_cnt,
+			dept_ut_results.wait_stack_total_cnt);
+	pr_info("event stack valid count = %d/%d\n",
+			dept_ut_results.evnt_stack_valid_cnt,
+			dept_ut_results.evnt_stack_total_cnt);
+	pr_info("******************************************\n");
+	pr_info("\n");
+
+	lockdep_on();
+
+	return 0;
+}
+
+static void dept_ut_cleanup(void)
+{
+	/*
+	 * Do nothing for now.
+	 */
+}
+
+module_init(dept_ut_init);
+module_exit(dept_ut_cleanup);
diff --git a/kernel/exit.c b/kernel/exit.c
index ede3117fa7d413..25297ef0421edb 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -1016,6 +1016,7 @@ void __noreturn do_exit(long code)
 	exit_tasks_rcu_finish();
 
 	lockdep_free_task(tsk);
+	dept_task_exit(tsk);
 	do_task_dead();
 }
 EXPORT_SYMBOL(do_exit);
diff --git a/kernel/fork.c b/kernel/fork.c
index bc2bf58b93b652..1f94bfd1a46b94 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -108,6 +108,7 @@
 #include <linux/tick.h>
 #include <linux/unwind_deferred.h>
 #include <linux/pgalloc.h>
+#include <linux/dept.h>
 #include <linux/uaccess.h>
 
 #include <asm/mmu_context.h>
@@ -2175,6 +2176,7 @@ __latent_entropy struct task_struct *copy_process(
 	p->pagefault_disabled = 0;
 
 	lockdep_init_task(p);
+	dept_task_init(p);
 
 	p->blocked_on = NULL; /* not blocked yet */
 
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 2d4c5bab5af887..c99f91f7a54db9 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -1224,6 +1224,8 @@ void lockdep_register_key(struct lock_class_key *key)
 	struct lock_class_key *k;
 	unsigned long flags;
 
+	dept_key_init(&key->dkey);
+
 	if (WARN_ON_ONCE(static_obj(key)))
 		return;
 	hash_head = keyhashentry(key);
@@ -4361,6 +4363,8 @@ static void __trace_hardirqs_on_caller(void)
  */
 void lockdep_hardirqs_on_prepare(void)
 {
+	dept_hardirqs_on();
+
 	if (unlikely(!debug_locks))
 		return;
 
@@ -4481,6 +4485,8 @@ EXPORT_SYMBOL_GPL(lockdep_hardirqs_on);
  */
 void noinstr lockdep_hardirqs_off(unsigned long ip)
 {
+	dept_hardirqs_off();
+
 	if (unlikely(!debug_locks))
 		return;
 
@@ -4525,6 +4531,8 @@ void lockdep_softirqs_on(unsigned long ip)
 {
 	struct irqtrace_events *trace = &current->irqtrace;
 
+	dept_softirqs_on_ip(ip);
+
 	if (unlikely(!lockdep_enabled()))
 		return;
 
@@ -4563,6 +4571,8 @@ void lockdep_softirqs_on(unsigned long ip)
  */
 void lockdep_softirqs_off(unsigned long ip)
 {
+	dept_softirqs_off();
+
 	if (unlikely(!lockdep_enabled()))
 		return;
 
@@ -4940,6 +4950,8 @@ void lockdep_init_map_type(struct lockdep_map *lock, const char *name,
 {
 	int i;
 
+	ldt_init(&lock->dmap, &key->dkey, subclass, name);
+
 	for (i = 0; i < NR_LOCKDEP_CACHING_CLASSES; i++)
 		lock->class_cache[i] = NULL;
 
@@ -5023,6 +5035,7 @@ void lockdep_set_lock_cmp_fn(struct lockdep_map *lock, lock_cmp_fn cmp_fn,
 		class->print_fn = print_fn;
 	}
 
+	dept_set_lockdep_map(&lock->dmap, lock);
 	lockdep_recursion_finish();
 	raw_local_irq_restore(flags);
 }
@@ -5736,6 +5749,12 @@ void lock_set_class(struct lockdep_map *lock, const char *name,
 {
 	unsigned long flags;
 
+	/*
+	 * dept_map_(re)init() might be called twice redundantly. But
+	 * there's no choice as long as Dept relies on Lockdep.
+	 */
+	ldt_set_class(&lock->dmap, name, &key->dkey, subclass, ip);
+
 	if (unlikely(!lockdep_enabled()))
 		return;
 
@@ -5753,6 +5772,8 @@ void lock_downgrade(struct lockdep_map *lock, unsigned long ip)
 {
 	unsigned long flags;
 
+	ldt_downgrade(&lock->dmap, ip);
+
 	if (unlikely(!lockdep_enabled()))
 		return;
 
@@ -6588,6 +6609,8 @@ void lockdep_unregister_key(struct lock_class_key *key)
 	bool found = false;
 	bool need_callback = false;
 
+	dept_key_destroy(&key->dkey);
+
 	might_sleep();
 
 	if (WARN_ON_ONCE(static_obj(key)))
@@ -6878,3 +6901,13 @@ void lockdep_rcu_suspicious(const char *file, const int line, const char *s)
 	warn_rcu_exit(rcu);
 }
 EXPORT_SYMBOL_GPL(lockdep_rcu_suspicious);
+
+/*
+ * For avoiding header dependency when using (struct task_struct *)current
+ * and lockdep_recursing() at the same time.
+ */
+noinstr bool lockdep_recursing_current(void)
+{
+	return lockdep_recursing(current);
+}
+EXPORT_SYMBOL_GPL(lockdep_recursing_current);
diff --git a/kernel/module/main.c b/kernel/module/main.c
index c3ce106c70af16..5bf3b3d1e3ecde 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -1375,12 +1375,14 @@ static void free_mod_mem(struct module *mod)
 
 		/* Free lock-classes; relies on the preceding sync_rcu(). */
 		lockdep_free_key_range(mod_mem->base, mod_mem->size);
+		dept_free_range(mod_mem->base, mod_mem->size);
 		if (mod_mem->size)
 			module_memory_free(mod, type);
 	}
 
 	/* MOD_DATA hosts mod, so free it at last */
 	lockdep_free_key_range(mod->mem[MOD_DATA].base, mod->mem[MOD_DATA].size);
+	dept_free_range(mod->mem[MOD_DATA].base, mod->mem[MOD_DATA].size);
 	module_memory_free(mod, MOD_DATA);
 }
 
diff --git a/kernel/rcu/rcu.h b/kernel/rcu/rcu.h
index 9b10b57b79ada7..d30dfc34553278 100644
--- a/kernel/rcu/rcu.h
+++ b/kernel/rcu/rcu.h
@@ -12,6 +12,7 @@
 
 #include <linux/slab.h>
 #include <trace/events/rcu.h>
+#include <linux/dept_sdt.h>
 
 /*
  * Grace-period counter management.
diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c
index d98a5c38e19c51..c2858650ccf52f 100644
--- a/kernel/rcu/update.c
+++ b/kernel/rcu/update.c
@@ -409,7 +409,7 @@ void wakeme_after_rcu(struct rcu_head *head)
 EXPORT_SYMBOL_GPL(wakeme_after_rcu);
 
 void __wait_rcu_gp(bool checktiny, unsigned int state, int n, call_rcu_func_t *crcu_array,
-		   struct rcu_synchronize *rs_array)
+		   struct rcu_synchronize *rs_array, struct dept_key *dkey)
 {
 	int i;
 	int j;
@@ -426,7 +426,8 @@ void __wait_rcu_gp(bool checktiny, unsigned int state, int n, call_rcu_func_t *c
 				break;
 		if (j == i) {
 			init_rcu_head_on_stack(&rs_array[i].head);
-			init_completion(&rs_array[i].completion);
+			sdt_map_init_key(&rs_array[i].dmap, dkey);
+			init_completion_dmap(&rs_array[i].completion, &rs_array[i].dmap);
 			(crcu_array[i])(&rs_array[i].head, wakeme_after_rcu);
 		}
 	}
diff --git a/kernel/sched/completion.c b/kernel/sched/completion.c
index 19ee702273c0fa..7262000db1146e 100644
--- a/kernel/sched/completion.c
+++ b/kernel/sched/completion.c
@@ -4,7 +4,7 @@
  * Generic wait-for-completion handler;
  *
  * It differs from semaphores in that their default case is the opposite,
- * wait_for_completion default blocks whereas semaphore default non-block. The
+ * __wait_for_completion default blocks whereas semaphore default non-block. The
  * interface also makes it easy to 'complete' multiple waiting threads,
  * something which isn't entirely natural for semaphores.
  *
@@ -42,7 +42,7 @@ void complete_on_current_cpu(struct completion *x)
  * This will wake up a single thread waiting on this completion. Threads will be
  * awakened in the same order in which they were queued.
  *
- * See also complete_all(), wait_for_completion() and related routines.
+ * See also complete_all(), __wait_for_completion() and related routines.
  *
  * If this function wakes up a task, it executes a full memory barrier before
  * accessing the task state.
@@ -115,7 +115,7 @@ __wait_for_common(struct completion *x,
 {
 	might_sleep();
 
-	complete_acquire(x);
+	complete_acquire(x, timeout);
 
 	raw_spin_lock_irq(&x->wait.lock);
 	timeout = do_wait_for_common(x, action, timeout, state);
@@ -139,23 +139,23 @@ wait_for_common_io(struct completion *x, long timeout, int state)
 }
 
 /**
- * wait_for_completion: - waits for completion of a task
+ * __wait_for_completion: - waits for completion of a task
  * @x:  holds the state of this particular completion
  *
  * This waits to be signaled for completion of a specific task. It is NOT
  * interruptible and there is no timeout.
  *
- * See also similar routines (i.e. wait_for_completion_timeout()) with timeout
+ * See also similar routines (i.e. __wait_for_completion_timeout()) with timeout
  * and interrupt capability. Also see complete().
  */
-void __sched wait_for_completion(struct completion *x)
+void __sched __wait_for_completion(struct completion *x)
 {
 	wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);
 }
-EXPORT_SYMBOL(wait_for_completion);
+EXPORT_SYMBOL(__wait_for_completion);
 
 /**
- * wait_for_completion_timeout: - waits for completion of a task (w/timeout)
+ * __wait_for_completion_timeout: - waits for completion of a task (w/timeout)
  * @x:  holds the state of this particular completion
  * @timeout:  timeout value in jiffies
  *
@@ -167,28 +167,28 @@ EXPORT_SYMBOL(wait_for_completion);
  * till timeout) if completed.
  */
 unsigned long __sched
-wait_for_completion_timeout(struct completion *x, unsigned long timeout)
+__wait_for_completion_timeout(struct completion *x, unsigned long timeout)
 {
 	return wait_for_common(x, timeout, TASK_UNINTERRUPTIBLE);
 }
-EXPORT_SYMBOL(wait_for_completion_timeout);
+EXPORT_SYMBOL(__wait_for_completion_timeout);
 
 /**
- * wait_for_completion_io: - waits for completion of a task
+ * __wait_for_completion_io: - waits for completion of a task
  * @x:  holds the state of this particular completion
  *
  * This waits to be signaled for completion of a specific task. It is NOT
  * interruptible and there is no timeout. The caller is accounted as waiting
  * for IO (which traditionally means blkio only).
  */
-void __sched wait_for_completion_io(struct completion *x)
+void __sched __wait_for_completion_io(struct completion *x)
 {
 	wait_for_common_io(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);
 }
-EXPORT_SYMBOL(wait_for_completion_io);
+EXPORT_SYMBOL(__wait_for_completion_io);
 
 /**
- * wait_for_completion_io_timeout: - waits for completion of a task (w/timeout)
+ * __wait_for_completion_io_timeout: - waits for completion of a task (w/timeout)
  * @x:  holds the state of this particular completion
  * @timeout:  timeout value in jiffies
  *
@@ -201,14 +201,14 @@ EXPORT_SYMBOL(wait_for_completion_io);
  * till timeout) if completed.
  */
 unsigned long __sched
-wait_for_completion_io_timeout(struct completion *x, unsigned long timeout)
+__wait_for_completion_io_timeout(struct completion *x, unsigned long timeout)
 {
 	return wait_for_common_io(x, timeout, TASK_UNINTERRUPTIBLE);
 }
-EXPORT_SYMBOL(wait_for_completion_io_timeout);
+EXPORT_SYMBOL(__wait_for_completion_io_timeout);
 
 /**
- * wait_for_completion_interruptible: - waits for completion of a task (w/intr)
+ * __wait_for_completion_interruptible: - waits for completion of a task (w/intr)
  * @x:  holds the state of this particular completion
  *
  * This waits for completion of a specific task to be signaled. It is
@@ -216,7 +216,7 @@ EXPORT_SYMBOL(wait_for_completion_io_timeout);
  *
  * Return: -ERESTARTSYS if interrupted, 0 if completed.
  */
-int __sched wait_for_completion_interruptible(struct completion *x)
+int __sched __wait_for_completion_interruptible(struct completion *x)
 {
 	long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_INTERRUPTIBLE);
 
@@ -224,10 +224,10 @@ int __sched wait_for_completion_interruptible(struct completion *x)
 		return t;
 	return 0;
 }
-EXPORT_SYMBOL(wait_for_completion_interruptible);
+EXPORT_SYMBOL(__wait_for_completion_interruptible);
 
 /**
- * wait_for_completion_interruptible_timeout: - waits for completion (w/(to,intr))
+ * __wait_for_completion_interruptible_timeout: - waits for completion (w/(to,intr))
  * @x:  holds the state of this particular completion
  * @timeout:  timeout value in jiffies
  *
@@ -238,15 +238,15 @@ EXPORT_SYMBOL(wait_for_completion_interruptible);
  * or number of jiffies left till timeout) if completed.
  */
 long __sched
-wait_for_completion_interruptible_timeout(struct completion *x,
+__wait_for_completion_interruptible_timeout(struct completion *x,
 					  unsigned long timeout)
 {
 	return wait_for_common(x, timeout, TASK_INTERRUPTIBLE);
 }
-EXPORT_SYMBOL(wait_for_completion_interruptible_timeout);
+EXPORT_SYMBOL(__wait_for_completion_interruptible_timeout);
 
 /**
- * wait_for_completion_killable: - waits for completion of a task (killable)
+ * __wait_for_completion_killable: - waits for completion of a task (killable)
  * @x:  holds the state of this particular completion
  *
  * This waits to be signaled for completion of a specific task. It can be
@@ -254,7 +254,7 @@ EXPORT_SYMBOL(wait_for_completion_interruptible_timeout);
  *
  * Return: -ERESTARTSYS if interrupted, 0 if completed.
  */
-int __sched wait_for_completion_killable(struct completion *x)
+int __sched __wait_for_completion_killable(struct completion *x)
 {
 	long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_KILLABLE);
 
@@ -262,9 +262,9 @@ int __sched wait_for_completion_killable(struct completion *x)
 		return t;
 	return 0;
 }
-EXPORT_SYMBOL(wait_for_completion_killable);
+EXPORT_SYMBOL(__wait_for_completion_killable);
 
-int __sched wait_for_completion_state(struct completion *x, unsigned int state)
+int __sched __wait_for_completion_state(struct completion *x, unsigned int state)
 {
 	long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, state);
 
@@ -272,10 +272,10 @@ int __sched wait_for_completion_state(struct completion *x, unsigned int state)
 		return t;
 	return 0;
 }
-EXPORT_SYMBOL(wait_for_completion_state);
+EXPORT_SYMBOL(__wait_for_completion_state);
 
 /**
- * wait_for_completion_killable_timeout: - waits for completion of a task (w/(to,killable))
+ * __wait_for_completion_killable_timeout: - waits for completion of a task (w/(to,killable))
  * @x:  holds the state of this particular completion
  * @timeout:  timeout value in jiffies
  *
@@ -287,12 +287,12 @@ EXPORT_SYMBOL(wait_for_completion_state);
  * or number of jiffies left till timeout) if completed.
  */
 long __sched
-wait_for_completion_killable_timeout(struct completion *x,
+__wait_for_completion_killable_timeout(struct completion *x,
 				     unsigned long timeout)
 {
 	return wait_for_common(x, timeout, TASK_KILLABLE);
 }
-EXPORT_SYMBOL(wait_for_completion_killable_timeout);
+EXPORT_SYMBOL(__wait_for_completion_killable_timeout);
 
 /**
  *	try_wait_for_completion - try to decrement a completion without blocking
@@ -334,7 +334,7 @@ EXPORT_SYMBOL(try_wait_for_completion);
  *	completion_done - Test to see if a completion has any waiters
  *	@x:	completion structure
  *
- *	Return: 0 if there are waiters (wait_for_completion() in progress)
+ *	Return: 0 if there are waiters (__wait_for_completion() in progress)
  *		 1 if there are no waiters.
  *
  *	Note, this will always return true if complete_all() was called on @X.
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 496dff740dcafe..c01597d645ae05 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -69,6 +69,7 @@
 #include <linux/wait_api.h>
 #include <linux/workqueue_api.h>
 #include <linux/livepatch_sched.h>
+#include <linux/dept.h>
 
 #ifdef CONFIG_PREEMPT_DYNAMIC
 # ifdef CONFIG_GENERIC_IRQ_ENTRY
@@ -4160,6 +4161,8 @@ int try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
 		if (READ_ONCE(p->on_rq) && ttwu_runnable(p, wake_flags))
 			break;
 
+		dept_ttwu_stage_wait(p, _RET_IP_);
+
 		/*
 		 * Ensure we load p->on_cpu _after_ p->on_rq, otherwise it would be
 		 * possible to, falsely, observe p->on_cpu == 0.
@@ -6783,6 +6786,11 @@ static void __sched notrace __schedule(int sched_mode)
 	rq = cpu_rq(cpu);
 	prev = rq->curr;
 
+	prev_state = READ_ONCE(prev->__state);
+	if (sched_mode != SM_PREEMPT && prev_state & TASK_NORMAL)
+		dept_request_event_wait_commit();
+
+	dept_sched_enter();
 	schedule_debug(prev, preempt);
 
 	if (sched_feat(HRTICK) || sched_feat(HRTICK_DL))
@@ -6919,6 +6927,7 @@ static void __sched notrace __schedule(int sched_mode)
 		raw_spin_rq_unlock_irq(rq);
 	}
 	trace_sched_exit_tp(is_switch);
+	dept_sched_exit();
 }
 
 void __noreturn do_task_dead(void)
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index c6ea96d5b71672..4a4075d0697c74 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -55,6 +55,7 @@
 #include <linux/kvm_para.h>
 #include <linux/delay.h>
 #include <linux/irq_work.h>
+#include <linux/dept.h>
 
 #include "workqueue_internal.h"
 
@@ -3204,6 +3205,8 @@ __acquires(&pool->lock)
 
 	lockdep_copy_map(&lockdep_map, &work->lockdep_map);
 #endif
+	dept_update_cxt();
+
 	/* ensure we're on the correct CPU */
 	WARN_ON_ONCE(!(pool->flags & POOL_DISASSOCIATED) &&
 		     raw_smp_processor_id() != pool->cpu);
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 93f356d2b3d955..41c822f7b75a23 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1441,6 +1441,54 @@ config DEBUG_ATOMIC_LARGEST_ALIGN
 
 menu "Lock Debugging (spinlocks, mutexes, etc...)"
 
+config DEPT
+	bool "Dependency tracking (EXPERIMENTAL)"
+	depends on DEBUG_KERNEL && LOCK_DEBUGGING_SUPPORT
+	select DEBUG_SPINLOCK
+	select DEBUG_MUTEXES if !PREEMPT_RT
+	select DEBUG_RT_MUTEXES if RT_MUTEXES
+	select DEBUG_RWSEMS if !PREEMPT_RT
+	select DEBUG_WW_MUTEX_SLOWPATH
+	select DEBUG_LOCK_ALLOC
+	select TRACE_IRQFLAGS
+	select STACKTRACE
+	select KALLSYMS
+	select KALLSYMS_ALL
+	select PROVE_LOCKING
+	default n
+	help
+	  Check dependencies between wait and event and report it if
+	  deadlock possibility has been detected. Multiple reports are
+	  allowed if there are more than a single problem.
+
+	  This feature is considered EXPERIMENTAL that might produce
+	  false positive reports because new dependencies start to be
+	  tracked, that have never been tracked before. It's worth
+	  noting, to mitigate the impact by the false positives, multi
+	  reporting has been supported.
+
+config DEPT_AGGRESSIVE_TIMEOUT_WAIT
+	bool "Aggressively track even timeout waits"
+	depends on DEPT
+	default n
+	help
+	  Timeout wait doesn't contribute to a deadlock. However,
+	  informing a circular dependency might be helpful for cases
+	  that timeout is used to avoid a deadlock. Say N if you'd like
+	  to avoid verbose reports.
+
+config DEPT_UNIT_TEST
+	tristate "unit test for DEPT"
+	depends on DEBUG_KERNEL && DEPT
+	default n
+	help
+	  This option provides a kernel module that runs unit test for
+	  DEPT.
+
+	  Say Y if you want DEPT unit test to be built into the kernel.
+	  Say M if you want DEPT unit test to build as a module.
+	  Say N if you are unsure.
+
 config LOCK_DEBUGGING_SUPPORT
 	bool
 	depends on TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT
diff --git a/lib/debug_locks.c b/lib/debug_locks.c
index a75ee30b77cb8d..14a965914a8fb4 100644
--- a/lib/debug_locks.c
+++ b/lib/debug_locks.c
@@ -38,6 +38,8 @@ EXPORT_SYMBOL_GPL(debug_locks_silent);
  */
 int debug_locks_off(void)
 {
+	dept_stop_emerg();
+
 	if (debug_locks && __debug_locks_off()) {
 		if (!debug_locks_silent) {
 			console_verbose();
diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c
index d939403331b5a6..a7f8e59d0092da 100644
--- a/lib/locking-selftest.c
+++ b/lib/locking-selftest.c
@@ -1398,6 +1398,8 @@ static void reset_locks(void)
 	local_irq_disable();
 	lockdep_free_key_range(&ww_lockdep.acquire_key, 1);
 	lockdep_free_key_range(&ww_lockdep.mutex_key, 1);
+	dept_free_range(&ww_lockdep.acquire_key, 1);
+	dept_free_range(&ww_lockdep.mutex_key, 1);
 
 	I1(A); I1(B); I1(C); I1(D);
 	I1(X1); I1(X2); I1(Y1); I1(Y2); I1(Z1); I1(Z2);
diff --git a/mm/filemap.c b/mm/filemap.c
index 3c1e785542dde0..e3aa2754da3fa9 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -49,6 +49,7 @@
 #include <linux/sched/mm.h>
 #include <linux/sysctl.h>
 #include <linux/pgalloc.h>
+#include <linux/dept.h>
 
 #include <asm/tlbflush.h>
 #include "internal.h"
@@ -1151,6 +1152,7 @@ static int wake_page_function(wait_queue_entry_t *wait, unsigned mode, int sync,
 		if (flags & WQ_FLAG_CUSTOM) {
 			if (test_and_set_bit(key->bit_nr, &key->folio->flags.f))
 				return -1;
+			dept_page_set_bit(&key->folio->page, key->bit_nr);
 			flags |= WQ_FLAG_DONE;
 		}
 	}
@@ -1191,6 +1193,13 @@ static void folio_wake_bit(struct folio *folio, int bit_nr)
 	key.bit_nr = bit_nr;
 	key.page_match = 0;
 
+	/*
+	 * dept_page_clear_bit() being called multiple times is harmless.
+	 * The worst case is to miss some dependencies but it's okay.
+	 */
+	if (bit_nr == PG_locked || bit_nr == PG_writeback)
+		dept_page_clear_bit(&folio->page, bit_nr);
+
 	spin_lock_irqsave(&q->lock, flags);
 	__wake_up_locked_key(q, TASK_NORMAL, &key);
 
@@ -1234,6 +1243,7 @@ static inline bool folio_trylock_flag(struct folio *folio, int bit_nr,
 	if (wait->flags & WQ_FLAG_EXCLUSIVE) {
 		if (test_and_set_bit(bit_nr, &folio->flags.f))
 			return false;
+		dept_page_set_bit(&folio->page, bit_nr);
 	} else if (test_bit(bit_nr, &folio->flags.f))
 		return false;
 
@@ -1241,6 +1251,12 @@ static inline bool folio_trylock_flag(struct folio *folio, int bit_nr,
 	return true;
 }
 
+struct dept_map __maybe_unused pg_locked_map = DEPT_MAP_INITIALIZER(pg_locked_map, NULL);
+EXPORT_SYMBOL(pg_locked_map);
+
+struct dept_map __maybe_unused pg_writeback_map = DEPT_MAP_INITIALIZER(pg_writeback_map, NULL);
+EXPORT_SYMBOL(pg_writeback_map);
+
 static inline int folio_wait_bit_common(struct folio *folio, int bit_nr,
 		int state, enum behavior behavior)
 {
@@ -1252,6 +1268,8 @@ static inline int folio_wait_bit_common(struct folio *folio, int bit_nr,
 	unsigned long pflags;
 	bool in_thrashing;
 
+	dept_page_wait_on_bit(&folio->page, bit_nr);
+
 	if (bit_nr == PG_locked &&
 	    !folio_test_uptodate(folio) && folio_test_workingset(folio)) {
 		delayacct_thrashing_start(&in_thrashing);
@@ -1345,6 +1363,23 @@ static inline int folio_wait_bit_common(struct folio *folio, int bit_nr,
 		break;
 	}
 
+	/*
+	 * dept_page_set_bit() might have been called already in
+	 * folio_trylock_flag(), wake_page_function() or somewhere.
+	 * However, call it again to reset the wgen of dept to ensure
+	 * dept_page_wait_on_bit() is called prior to
+	 * dept_page_set_bit().
+	 *
+	 * Remind dept considers all the waits between
+	 * dept_page_set_bit() and dept_page_clear_bit() as potential
+	 * event disturbers. Ensure the correct sequence so that dept
+	 * can make correct decisions:
+	 *
+	 *	wait -> acquire(set bit) -> release(clear bit)
+	 */
+	if (wait->flags & WQ_FLAG_DONE)
+		dept_page_set_bit(&folio->page, bit_nr);
+
 	/*
 	 * If a signal happened, this 'finish_wait()' may remove the last
 	 * waiter from the wait-queues, but the folio waiters bit will remain
@@ -1507,6 +1542,7 @@ void folio_unlock(struct folio *folio)
 	BUILD_BUG_ON(PG_waiters != 7);
 	BUILD_BUG_ON(PG_locked > 7);
 	VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
+	dept_page_clear_bit(&folio->page, PG_locked);
 	if (folio_xor_flags_has_waiters(folio, 1 << PG_locked))
 		folio_wake_bit(folio, PG_locked);
 }
@@ -1537,6 +1573,7 @@ void folio_end_read(struct folio *folio, bool success)
 
 	if (likely(success))
 		mask |= 1 << PG_uptodate;
+	dept_page_clear_bit(&folio->page, PG_locked);
 	if (folio_xor_flags_has_waiters(folio, mask))
 		folio_wake_bit(folio, PG_locked);
 }
@@ -1663,6 +1700,7 @@ void folio_end_writeback_no_dropbehind(struct folio *folio)
 		folio_rotate_reclaimable(folio);
 	}
 
+	dept_page_clear_bit(&folio->page, PG_writeback);
 	if (__folio_end_writeback(folio))
 		folio_wake_bit(folio, PG_writeback);
 
diff --git a/mm/mm_init.c b/mm/mm_init.c
index df34797691bda2..2695d7b3b0898b 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -32,6 +32,7 @@
 #include <linux/vmstat.h>
 #include <linux/kexec_handover.h>
 #include <linux/hugetlb.h>
+#include <linux/dept.h>
 #include "internal.h"
 #include "slab.h"
 #include "shuffle.h"
@@ -587,6 +588,8 @@ void __meminit __init_single_page(struct page *page, unsigned long pfn,
 	atomic_set(&page->_mapcount, -1);
 	page_cpupid_reset_last(page);
 	page_kasan_tag_reset(page);
+	dept_ext_wgen_init(&page->pg_locked_wgen);
+	dept_ext_wgen_init(&page->pg_writeback_wgen);
 
 	INIT_LIST_HEAD(&page->lru);
 #ifdef WANT_PAGE_VIRTUAL
diff --git a/mm/mmu_notifier.c b/mm/mmu_notifier.c
index a6cdf3674bdc52..10c3420b3901ad 100644
--- a/mm/mmu_notifier.c
+++ b/mm/mmu_notifier.c
@@ -46,6 +46,7 @@ struct mmu_notifier_subscriptions {
 	unsigned long active_invalidate_ranges;
 	struct rb_root_cached itree;
 	wait_queue_head_t wq;
+	struct dept_map dmap;
 	struct hlist_head deferred_list;
 };
 
@@ -165,6 +166,25 @@ static void mn_itree_inv_end(struct mmu_notifier_subscriptions *subscriptions)
 	wake_up_all(&subscriptions->wq);
 }
 
+#ifdef CONFIG_DEPT
+void mmu_notifier_invalidate_dept_ecxt_start(struct mmu_notifier_range *range)
+{
+	struct mmu_notifier_subscriptions *subscriptions =
+		range->mm->notifier_subscriptions;
+
+	if (subscriptions)
+		sdt_ecxt_enter(&subscriptions->dmap);
+}
+void mmu_notifier_invalidate_dept_ecxt_end(struct mmu_notifier_range *range)
+{
+	struct mmu_notifier_subscriptions *subscriptions =
+		range->mm->notifier_subscriptions;
+
+	if (subscriptions)
+		sdt_ecxt_exit(&subscriptions->dmap);
+}
+#endif
+
 /**
  * mmu_interval_read_begin - Begin a read side critical section against a VA
  *                           range
@@ -246,9 +266,12 @@ mmu_interval_read_begin(struct mmu_interval_notifier *interval_sub)
 	 */
 	lock_map_acquire(&__mmu_notifier_invalidate_range_start_map);
 	lock_map_release(&__mmu_notifier_invalidate_range_start_map);
-	if (is_invalidating)
+	if (is_invalidating) {
+		sdt_might_sleep_start(&subscriptions->dmap);
 		wait_event(subscriptions->wq,
 			   READ_ONCE(subscriptions->invalidate_seq) != seq);
+		sdt_might_sleep_end();
+	}
 
 	/*
 	 * Notice that mmu_interval_read_retry() can already be true at this
@@ -624,6 +647,7 @@ int __mmu_notifier_register(struct mmu_notifier *subscription,
 
 		INIT_HLIST_HEAD(&subscriptions->list);
 		spin_lock_init(&subscriptions->lock);
+		sdt_map_init(&subscriptions->dmap);
 		subscriptions->invalidate_seq = 2;
 		subscriptions->itree = RB_ROOT_CACHED;
 		init_waitqueue_head(&subscriptions->wq);
@@ -1069,9 +1093,12 @@ void mmu_interval_notifier_remove(struct mmu_interval_notifier *interval_sub)
 	 */
 	lock_map_acquire(&__mmu_notifier_invalidate_range_start_map);
 	lock_map_release(&__mmu_notifier_invalidate_range_start_map);
-	if (seq)
+	if (seq) {
+		sdt_might_sleep_start(&subscriptions->dmap);
 		wait_event(subscriptions->wq,
 			   mmu_interval_seq_released(subscriptions, seq));
+		sdt_might_sleep_end();
+	}
 
 	/* pairs with mmgrab in mmu_interval_notifier_insert() */
 	mmdrop(mm);
diff --git a/rust/helpers/completion.c b/rust/helpers/completion.c
index 0126767cc3bebd..7b55c960fe22ae 100644
--- a/rust/helpers/completion.c
+++ b/rust/helpers/completion.c
@@ -6,3 +6,8 @@ __rust_helper void rust_helper_init_completion(struct completion *x)
 {
 	init_completion(x);
 }
+
+__rust_helper void rust_helper_wait_for_completion(struct completion *x)
+{
+	wait_for_completion(x);
+}

3/2 2026/07/06 06:47 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 87835
							output: 53
							thoughts: 1455
						

Thoughts:
**DEPT Patch Series Evaluation for Fuzzing**

Alright, let's break this down. We have a patch series introducing DEPT, the Dependency Tracker. My initial assessment is that this is a *substantial* addition to the kernel, aiming to be a runtime dependency tracker, similar to, but more general than, lockdep. Given my expertise, I immediately recognize the implications. This isn't just a minor tweak; it's weaving itself throughout the core of the kernel: the scheduler, locking mechanisms, wait queues, memory management, interrupt handling, and even task management.

Looking at the diff, it's pretty extensive. We've got documentation, numerous new headers, and modifications scattered across a huge number of files. The presence of annotations in `dma-fence.c` and completion headers, alongside hooks into IRQ handling and lockdep itself, strongly suggests it's designed to monitor synchronization primitives, which is *precisely* the kind of thing that could expose interesting concurrency bugs, making this an ideal fuzzing target.

The critical question is whether it introduces enough new code and functional changes to warrant fuzzing. Absolutely. The introduction of `kernel/dependency/dept.c` is the most significant indicator. The presence of runtime dependencies means DEPT touches hot paths when it is enabled and adds complexity.

My recommendation is to enable `CONFIG_DEPT` and `CONFIG_DEPT_AGGRESSIVE_TIMEOUT_WAIT`. From a quick check of `lib/Kconfig.debug`, it seems those are the primary configuration options we need. `CONFIG_DEPT_UNIT_TEST` might be useful, but I'm thinking we can focus on DEPT itself for fuzzing. We can just configure DEPT and the aggressive timeouts and fuzz.

The core implementation looks to reside within `kernel/dependency/dept.c`. Given that `dept_wait` and `dept_event` will be called *frequently*, touching all lock and wait calls with DEPT enabled, maybe we should first prioritize the graph processing functions and other less hot-path functions, to avoid fuzzing the hot path. Based on my quick glance at the code, functions such as `check_dl_bfs`, `bfs`, `__add_dep`, `__add_idep`, `print_circle`, `add_ecxt`, `do_event`, `add_wait`, `add_dep`, `add_iecxt`, `add_iwait`, and `dept_map_ecxt_modify` appear to be likely candidates.

We need to dive deeper into `kernel/dependency/dept.c`, and understand the data structures and how dependencies are tracked. It's a significant undertaking, but this is clearly worth investigating from a fuzzing perspective.



4/2 2026/07/06 06:47 tool grepper
0m Args:
{
  "Expression": "^(static )?(void|int|bool|struct [a-z_]+ \\*) [a-z_]+\\(",
  "PathPrefix": "kernel/dependency/dept.c"
}

Results:
{
  "Output": "kernel/dependency/dept.c=164=static atomic_t dept_outworld = ATOMIC_INIT(0);\nkernel/dependency/dept.c-165-\nkernel/dependency/dept.c:166:static void dept_outworld_enter(void)\nkernel/dependency/dept.c-167-{\n--\nkernel/dependency/dept.c-170-\nkernel/dependency/dept.c:171:static void dept_outworld_exit(void)\nkernel/dependency/dept.c-172-{\n--\nkernel/dependency/dept.c-175-\nkernel/dependency/dept.c:176:static bool dept_outworld_entered(void)\nkernel/dependency/dept.c-177-{\n--\nkernel/dependency/dept.c-180-\nkernel/dependency/dept.c:181:static bool dept_lock(void)\nkernel/dependency/dept.c-182-{\n--\nkernel/dependency/dept.c-188-\nkernel/dependency/dept.c:189:static void dept_unlock(void)\nkernel/dependency/dept.c-190-{\n--\nkernel/dependency/dept.c-193-\nkernel/dependency/dept.c:194:void dept_stop_emerg(void)\nkernel/dependency/dept.c-195-{\n--\nkernel/dependency/dept.c=200=enum bfs_ret {\n--\nkernel/dependency/dept.c-205-\nkernel/dependency/dept.c:206:static bool before(unsigned int a, unsigned int b)\nkernel/dependency/dept.c-207-{\n--\nkernel/dependency/dept.c-210-\nkernel/dependency/dept.c:211:static bool valid_stack(struct dept_stack *s)\nkernel/dependency/dept.c-212-{\n--\nkernel/dependency/dept.c-215-\nkernel/dependency/dept.c:216:static bool valid_class(struct dept_class *c)\nkernel/dependency/dept.c-217-{\n--\nkernel/dependency/dept.c-220-\nkernel/dependency/dept.c:221:static void invalidate_class(struct dept_class *c)\nkernel/dependency/dept.c-222-{\n--\nkernel/dependency/dept.c=246=static const char *irq_str(int irq)\n--\nkernel/dependency/dept.c-258- */\nkernel/dependency/dept.c:259:static bool dept_working(void)\nkernel/dependency/dept.c-260-{\n--\nkernel/dependency/dept.c=268=extern struct lock_class_key __lockdep_no_validate__;\nkernel/dependency/dept.c:269:static bool valid_key(struct dept_key *k)\nkernel/dependency/dept.c-270-{\n--\nkernel/dependency/dept.c=291=struct dept_pool dept_pool[OBJECT_NR] = {\n--\nkernel/dependency/dept.c-305-\nkernel/dependency/dept.c:306:static void dept_wq_work_fn(struct work_struct *work)\nkernel/dependency/dept.c-307-{\n--\nkernel/dependency/dept.c=349=static DECLARE_WORK(dept_wq_work, dept_wq_work_fn);\nkernel/dependency/dept.c-350-\nkernel/dependency/dept.c:351:static void dept_irq_work_fn(struct irq_work *w)\nkernel/dependency/dept.c-352-{\n--\nkernel/dependency/dept.c=356=static DEFINE_IRQ_WORK(dept_irq_work, dept_irq_work_fn);\nkernel/dependency/dept.c-357-\nkernel/dependency/dept.c:358:static void request_rpool_refill(void)\nkernel/dependency/dept.c-359-{\n--\nkernel/dependency/dept.c=368=static void *from_pool(enum object_t t)\n--\nkernel/dependency/dept.c-425-\nkernel/dependency/dept.c:426:static void to_pool(void *o, enum object_t t)\nkernel/dependency/dept.c-427-{\n--\nkernel/dependency/dept.c=488=static void (*ctor_##id)(struct dept_##id *a) = f\nkernel/dependency/dept.c-489-\nkernel/dependency/dept.c:490:static void initialize_dep(struct dept_dep *d)\nkernel/dependency/dept.c-491-{\n--\nkernel/dependency/dept.c=495=SET_CONSTRUCTOR(dep, initialize_dep);\nkernel/dependency/dept.c-496-\nkernel/dependency/dept.c:497:static void initialize_class(struct dept_class *c)\nkernel/dependency/dept.c-498-{\n--\nkernel/dependency/dept.c=521=SET_CONSTRUCTOR(class, initialize_class);\nkernel/dependency/dept.c-522-\nkernel/dependency/dept.c:523:static void initialize_ecxt(struct dept_ecxt *e)\nkernel/dependency/dept.c-524-{\n--\nkernel/dependency/dept.c=538=SET_CONSTRUCTOR(ecxt, initialize_ecxt);\nkernel/dependency/dept.c-539-\nkernel/dependency/dept.c:540:static void initialize_wait(struct dept_wait *w)\nkernel/dependency/dept.c-541-{\n--\nkernel/dependency/dept.c=552=SET_CONSTRUCTOR(wait, initialize_wait);\nkernel/dependency/dept.c-553-\nkernel/dependency/dept.c:554:static void initialize_stack(struct dept_stack *s)\nkernel/dependency/dept.c-555-{\n--\nkernel/dependency/dept.c=568=static void (*dtor_##id)(struct dept_##id *a) = f\nkernel/dependency/dept.c-569-\nkernel/dependency/dept.c:570:static void destroy_dep(struct dept_dep *d)\nkernel/dependency/dept.c-571-{\n--\nkernel/dependency/dept.c=577=SET_DESTRUCTOR(dep, destroy_dep);\nkernel/dependency/dept.c-578-\nkernel/dependency/dept.c:579:static void destroy_ecxt(struct dept_ecxt *e)\nkernel/dependency/dept.c-580-{\n--\nkernel/dependency/dept.c=595=SET_DESTRUCTOR(ecxt, destroy_ecxt);\nkernel/dependency/dept.c-596-\nkernel/dependency/dept.c:597:static void destroy_wait(struct dept_wait *w)\nkernel/dependency/dept.c-598-{\n--\nkernel/dependency/dept.c=631=static unsigned long mix(unsigned long a, unsigned long b)\n--\nkernel/dependency/dept.c-638-\nkernel/dependency/dept.c:639:static bool cmp_dep(struct dept_dep *d1, struct dept_dep *d2)\nkernel/dependency/dept.c-640-{\n--\nkernel/dependency/dept.c=645=static unsigned long key_dep(struct dept_dep *d)\n--\nkernel/dependency/dept.c-649-\nkernel/dependency/dept.c:650:static bool cmp_class(struct dept_class *c1, struct dept_class *c2)\nkernel/dependency/dept.c-651-{\n--\nkernel/dependency/dept.c=702=static struct dept_class *lookup_class(unsigned long key)\n--\nkernel/dependency/dept.c-715-\nkernel/dependency/dept.c:716:static void print_ip_stack(unsigned long ip, struct dept_stack *s)\nkernel/dependency/dept.c-717-{\n--\nkernel/dependency/dept.c-741-\nkernel/dependency/dept.c:742:static void print_diagram(struct dept_dep *d)\nkernel/dependency/dept.c-743-{\n--\nkernel/dependency/dept.c-778-\nkernel/dependency/dept.c:779:static void print_dep(struct dept_dep *d)\nkernel/dependency/dept.c-780-{\n--\nkernel/dependency/dept.c-839-\nkernel/dependency/dept.c:840:static void save_current_stack(int skip);\nkernel/dependency/dept.c-841-\nkernel/dependency/dept.c:842:static bool is_timeout_wait_circle(struct dept_class *c)\nkernel/dependency/dept.c-843-{\n--\nkernel/dependency/dept.c-862- */\nkernel/dependency/dept.c:863:static void print_circle(struct dept_class *c)\nkernel/dependency/dept.c-864-{\n--\nkernel/dependency/dept.c=949=static unsigned int bfs_gen;\n--\nkernel/dependency/dept.c-953- */\nkernel/dependency/dept.c:954:static void bfs(void *root, struct bfs_ops *ops, void *in, void **out)\nkernel/dependency/dept.c-955-{\n--\nkernel/dependency/dept.c=999=static unsigned long cur_enirqf(void);\nkernel/dependency/dept.c:1000:static int cur_cxt(void);\nkernel/dependency/dept.c-1001-static unsigned int cur_ctxt_id(void);\n--\nkernel/dependency/dept.c=1008=static struct dept_iwait *iwait(struct dept_class *c, int irq)\n--\nkernel/dependency/dept.c-1012-\nkernel/dependency/dept.c:1013:static void stale_iecxt(struct dept_iecxt *ie)\nkernel/dependency/dept.c-1014-{\n--\nkernel/dependency/dept.c-1021-\nkernel/dependency/dept.c:1022:static void set_iecxt(struct dept_iecxt *ie, struct dept_ecxt *e)\nkernel/dependency/dept.c-1023-{\n--\nkernel/dependency/dept.c-1033-\nkernel/dependency/dept.c:1034:static void stale_iwait(struct dept_iwait *iw)\nkernel/dependency/dept.c-1035-{\n--\nkernel/dependency/dept.c-1042-\nkernel/dependency/dept.c:1043:static void set_iwait(struct dept_iwait *iw, struct dept_wait *w)\nkernel/dependency/dept.c-1044-{\n--\nkernel/dependency/dept.c-1056-\nkernel/dependency/dept.c:1057:static void touch_iwait(struct dept_iwait *iw)\nkernel/dependency/dept.c-1058-{\n--\nkernel/dependency/dept.c-1061-\nkernel/dependency/dept.c:1062:static void untouch_iwait(struct dept_iwait *iw)\nkernel/dependency/dept.c-1063-{\n--\nkernel/dependency/dept.c=1067=static struct dept_stack *get_current_stack(void)\n--\nkernel/dependency/dept.c-1073-\nkernel/dependency/dept.c:1074:static void prepare_current_stack(void)\nkernel/dependency/dept.c-1075-{\n--\nkernel/dependency/dept.c-1080-\nkernel/dependency/dept.c:1081:static void save_current_stack(int skip)\nkernel/dependency/dept.c-1082-{\n--\nkernel/dependency/dept.c-1093-\nkernel/dependency/dept.c:1094:static void finish_current_stack(void)\nkernel/dependency/dept.c-1095-{\n--\nkernel/dependency/dept.c=1141=static unsigned long dept_enter(void)\n--\nkernel/dependency/dept.c-1150-\nkernel/dependency/dept.c:1151:static void dept_exit(unsigned long flags)\nkernel/dependency/dept.c-1152-{\n--\nkernel/dependency/dept.c=1158=static unsigned long dept_enter_recursive(void)\n--\nkernel/dependency/dept.c-1165-\nkernel/dependency/dept.c:1166:static void dept_exit_recursive(unsigned long flags)\nkernel/dependency/dept.c-1167-{\n--\nkernel/dependency/dept.c=1174=static struct dept_dep *__add_dep(struct dept_ecxt *e,\n--\nkernel/dependency/dept.c-1203-\nkernel/dependency/dept.c:1204:static void bfs_init_check_dl(void *node, void *in, void **out)\nkernel/dependency/dept.c-1205-{\n--\nkernel/dependency/dept.c-1212-\nkernel/dependency/dept.c:1213:static void bfs_extend_dep(struct list_head *h, void *node)\nkernel/dependency/dept.c-1214-{\n--\nkernel/dependency/dept.c=1240=static enum bfs_ret cb_check_dl(void *node, void *in, void **out)\n--\nkernel/dependency/dept.c-1255- */\nkernel/dependency/dept.c:1256:static void check_dl_bfs(struct dept_dep *d)\nkernel/dependency/dept.c-1257-{\n--\nkernel/dependency/dept.c-1267-\nkernel/dependency/dept.c:1268:static void bfs_init_dep(void *node, void *in, void **out)\nkernel/dependency/dept.c-1269-{\n--\nkernel/dependency/dept.c-1274-\nkernel/dependency/dept.c:1275:static void bfs_extend_dep_rev(struct list_head *h, void *node)\nkernel/dependency/dept.c-1276-{\n--\nkernel/dependency/dept.c=1359=static struct dept_iecxt *touch_iw_find_ie_bfs(struct dept_class *c,\n--\nkernel/dependency/dept.c-1376- */\nkernel/dependency/dept.c:1377:static void __add_idep(struct dept_iecxt *ie, struct dept_iwait *iw)\nkernel/dependency/dept.c-1378-{\n--\nkernel/dependency/dept.c-1404-\nkernel/dependency/dept.c:1405:static void set_check_iecxt(struct dept_class *c, int irq,\nkernel/dependency/dept.c-1406-\t\t\t    struct dept_ecxt *e)\n--\nkernel/dependency/dept.c-1413-\nkernel/dependency/dept.c:1414:static void set_check_iwait(struct dept_class *c, int irq,\nkernel/dependency/dept.c-1415-\t\t\t    struct dept_wait *w)\n--\nkernel/dependency/dept.c-1422-\nkernel/dependency/dept.c:1423:static void add_iecxt(struct dept_class *c, int irq, struct dept_ecxt *e,\nkernel/dependency/dept.c-1424-\t\t      bool stack)\n--\nkernel/dependency/dept.c-1469-\nkernel/dependency/dept.c:1470:static void add_iwait(struct dept_class *c, int irq, struct dept_wait *w)\nkernel/dependency/dept.c-1471-{\n--\nkernel/dependency/dept.c=1512=static struct dept_wait_hist *hist(int pos)\n--\nkernel/dependency/dept.c-1518-\nkernel/dependency/dept.c:1519:static int hist_pos_next(void)\nkernel/dependency/dept.c-1520-{\n--\nkernel/dependency/dept.c-1525-\nkernel/dependency/dept.c:1526:static void hist_advance(void)\nkernel/dependency/dept.c-1527-{\n--\nkernel/dependency/dept.c=1542=static struct dept_wait_hist *last_hist(void)\n--\nkernel/dependency/dept.c-1555-\nkernel/dependency/dept.c:1556:static void add_hist(struct dept_wait *w, unsigned int wg, unsigned int ctxt_id)\nkernel/dependency/dept.c-1557-{\n--\nkernel/dependency/dept.c-1575- */\nkernel/dependency/dept.c:1576:static void add_dep(struct dept_ecxt *e, struct dept_wait *w)\nkernel/dependency/dept.c-1577-{\n--\nkernel/dependency/dept.c=1624=static atomic_t wgen = ATOMIC_INIT(1);\nkernel/dependency/dept.c-1625-\nkernel/dependency/dept.c:1626:static int next_wgen(void)\nkernel/dependency/dept.c-1627-{\n--\nkernel/dependency/dept.c-1641- */\nkernel/dependency/dept.c:1642:static bool lockdep_cmp_fn(struct dept_map *prev, struct dept_map *next)\nkernel/dependency/dept.c-1643-{\n--\nkernel/dependency/dept.c-1663-\nkernel/dependency/dept.c:1664:static void add_wait(struct dept_map *m, struct dept_class *c,\nkernel/dependency/dept.c-1665-\t\tunsigned long ip, const char *w_fn, int sub_l,\n--\nkernel/dependency/dept.c=1722=static struct dept_ecxt_held *add_ecxt(struct dept_map *m,\n--\nkernel/dependency/dept.c-1777-\nkernel/dependency/dept.c:1778:static int find_ecxt_pos(struct dept_map *m, struct dept_class *c,\nkernel/dependency/dept.c-1779-\t\t\t bool newfirst)\n--\nkernel/dependency/dept.c-1803-\nkernel/dependency/dept.c:1804:static bool pop_ecxt(struct dept_map *m, struct dept_class *c)\nkernel/dependency/dept.c-1805-{\n--\nkernel/dependency/dept.c-1826-\nkernel/dependency/dept.c:1827:static bool good_hist(struct dept_wait_hist *wh, unsigned int wg)\nkernel/dependency/dept.c-1828-{\n--\nkernel/dependency/dept.c-1834- */\nkernel/dependency/dept.c:1835:static int find_hist_pos(unsigned int wg)\nkernel/dependency/dept.c-1836-{\n--\nkernel/dependency/dept.c-1863-\nkernel/dependency/dept.c:1864:static void do_event(struct dept_map *m, struct dept_map *real_m,\nkernel/dependency/dept.c-1865-\t\tstruct dept_class *c, unsigned int wg, unsigned long ip,\n--\nkernel/dependency/dept.c-1935-\nkernel/dependency/dept.c:1936:static void del_dep_rcu(struct rcu_head *rh)\nkernel/dependency/dept.c-1937-{\n--\nkernel/dependency/dept.c-1947- */\nkernel/dependency/dept.c:1948:static void disconnect_class(struct dept_class *c)\nkernel/dependency/dept.c-1949-{\n--\nkernel/dependency/dept.c=1981=static unsigned long cur_enirqf(void)\n--\nkernel/dependency/dept.c-1991-\nkernel/dependency/dept.c:1992:static int cur_cxt(void)\nkernel/dependency/dept.c-1993-{\n--\nkernel/dependency/dept.c=2001=static unsigned int cur_ctxt_id(void)\n--\nkernel/dependency/dept.c-2008-\nkernel/dependency/dept.c:2009:static void enirq_transition(int irq)\nkernel/dependency/dept.c-2010-{\n--\nkernel/dependency/dept.c-2042-\nkernel/dependency/dept.c:2043:static void dept_enirq(unsigned long ip)\nkernel/dependency/dept.c-2044-{\n--\nkernel/dependency/dept.c-2070-\nkernel/dependency/dept.c:2071:void dept_softirqs_on_ip(unsigned long ip)\nkernel/dependency/dept.c-2072-{\n--\nkernel/dependency/dept.c-2080-\nkernel/dependency/dept.c:2081:void dept_hardirqs_on(void)\nkernel/dependency/dept.c-2082-{\n--\nkernel/dependency/dept.c-2090-\nkernel/dependency/dept.c:2091:void dept_softirqs_off(void)\nkernel/dependency/dept.c-2092-{\n--\nkernel/dependency/dept.c=2110=void noinstr dept_update_cxt(void)\n--\nkernel/dependency/dept.c-2119- */\nkernel/dependency/dept.c:2120:void dept_softirq_enter(void)\nkernel/dependency/dept.c-2121-{\n--\nkernel/dependency/dept.c=2130=void noinstr dept_hardirq_enter(void)\n--\nkernel/dependency/dept.c-2136-\nkernel/dependency/dept.c:2137:void dept_sched_enter(void)\nkernel/dependency/dept.c-2138-{\n--\nkernel/dependency/dept.c-2141-\nkernel/dependency/dept.c:2142:void dept_sched_exit(void)\nkernel/dependency/dept.c-2143-{\n--\nkernel/dependency/dept.c-2151-\nkernel/dependency/dept.c:2152:static void clean_classes_cache(struct dept_key *k)\nkernel/dependency/dept.c-2153-{\n--\nkernel/dependency/dept.c-2167- */\nkernel/dependency/dept.c:2168:void dept_map_init(struct dept_map *m, struct dept_key *k, int sub_u,\nkernel/dependency/dept.c-2169-\t\t   const char *n)\n--\nkernel/dependency/dept.c=2204=EXPORT_SYMBOL_GPL(dept_map_init);\n--\nkernel/dependency/dept.c-2209- */\nkernel/dependency/dept.c:2210:void dept_map_reinit(struct dept_map *m, struct dept_key *k, int sub_u,\nkernel/dependency/dept.c-2211-\t\t     const char *n)\n--\nkernel/dependency/dept.c=2241=EXPORT_SYMBOL_GPL(dept_map_reinit);\nkernel/dependency/dept.c-2242-\nkernel/dependency/dept.c:2243:void dept_ext_wgen_init(struct dept_ext_wgen *ewg)\nkernel/dependency/dept.c-2244-{\n--\nkernel/dependency/dept.c-2247-\nkernel/dependency/dept.c:2248:void dept_map_copy(struct dept_map *to, struct dept_map *from)\nkernel/dependency/dept.c-2249-{\n--\nkernel/dependency/dept.c=2276=LIST_HEAD(dept_classes);\nkernel/dependency/dept.c-2277-\nkernel/dependency/dept.c:2278:static bool within(const void *addr, void *start, unsigned long size)\nkernel/dependency/dept.c-2279-{\n--\nkernel/dependency/dept.c-2282-\nkernel/dependency/dept.c:2283:void dept_free_range(void *start, unsigned int sz)\nkernel/dependency/dept.c-2284-{\n--\nkernel/dependency/dept.c-2334-\nkernel/dependency/dept.c:2335:static int sub_id(struct dept_map *m, int e)\nkernel/dependency/dept.c-2336-{\n--\nkernel/dependency/dept.c=2340=static struct dept_class *check_new_class(struct dept_key *local,\n--\nkernel/dependency/dept.c-2396- */\nkernel/dependency/dept.c:2397:static void __dept_wait(struct dept_map *m, unsigned long w_f,\nkernel/dependency/dept.c-2398-\t\t\tunsigned long ip, const char *w_fn, int sub_l,\n--\nkernel/dependency/dept.c-2428- */\nkernel/dependency/dept.c:2429:static void __dept_event(struct dept_map *m, struct dept_map *real_m,\nkernel/dependency/dept.c-2430-\t\tunsigned long e_f, unsigned long ip, const char *e_fn,\n--\nkernel/dependency/dept.c-2456-\nkernel/dependency/dept.c:2457:void dept_wait(struct dept_map *m, unsigned long w_f,\nkernel/dependency/dept.c-2458-\t       unsigned long ip, const char *w_fn, int sub_l,\n--\nkernel/dependency/dept.c=2487=EXPORT_SYMBOL_GPL(dept_wait);\nkernel/dependency/dept.c-2488-\nkernel/dependency/dept.c:2489:void dept_stage_wait(struct dept_map *m, struct dept_key *k,\nkernel/dependency/dept.c-2490-\t\t     unsigned long ip, const char *w_fn,\n--\nkernel/dependency/dept.c=2560=EXPORT_SYMBOL_GPL(dept_stage_wait);\nkernel/dependency/dept.c-2561-\nkernel/dependency/dept.c:2562:static void __dept_clean_stage(struct dept_task *dt)\nkernel/dependency/dept.c-2563-{\n--\nkernel/dependency/dept.c-2574-\nkernel/dependency/dept.c:2575:void dept_clean_stage(void)\nkernel/dependency/dept.c-2576-{\n--\nkernel/dependency/dept.c=2592=EXPORT_SYMBOL_GPL(dept_clean_stage);\n--\nkernel/dependency/dept.c-2596- */\nkernel/dependency/dept.c:2597:void dept_request_event_wait_commit(void)\nkernel/dependency/dept.c-2598-{\n--\nkernel/dependency/dept.c-2653- */\nkernel/dependency/dept.c:2654:void dept_ttwu_stage_wait(struct task_struct *requestor, unsigned long ip)\nkernel/dependency/dept.c-2655-{\n--\nkernel/dependency/dept.c-2706- */\nkernel/dependency/dept.c:2707:void dept_map_ecxt_modify(struct dept_map *m, unsigned long e_f,\nkernel/dependency/dept.c-2708-\t\t\t  struct dept_key *new_k, unsigned long new_e_f,\n--\nkernel/dependency/dept.c=2790=EXPORT_SYMBOL_GPL(dept_map_ecxt_modify);\nkernel/dependency/dept.c-2791-\nkernel/dependency/dept.c:2792:void dept_ecxt_enter(struct dept_map *m, unsigned long e_f, unsigned long ip,\nkernel/dependency/dept.c-2793-\t\t     const char *c_fn, const char *e_fn, int sub_l)\n--\nkernel/dependency/dept.c=2839=EXPORT_SYMBOL_GPL(dept_ecxt_enter);\nkernel/dependency/dept.c-2840-\nkernel/dependency/dept.c:2841:bool dept_ecxt_holding(struct dept_map *m, unsigned long e_f)\nkernel/dependency/dept.c-2842-{\n--\nkernel/dependency/dept.c=2876=EXPORT_SYMBOL_GPL(dept_ecxt_holding);\nkernel/dependency/dept.c-2877-\nkernel/dependency/dept.c:2878:void dept_request_event(struct dept_map *m, struct dept_ext_wgen *ewg)\nkernel/dependency/dept.c-2879-{\n--\nkernel/dependency/dept.c=2902=EXPORT_SYMBOL_GPL(dept_request_event);\nkernel/dependency/dept.c-2903-\nkernel/dependency/dept.c:2904:void dept_event(struct dept_map *m, unsigned long e_f,\nkernel/dependency/dept.c-2905-\t\tunsigned long ip, const char *e_fn,\n--\nkernel/dependency/dept.c=2941=EXPORT_SYMBOL_GPL(dept_event);\nkernel/dependency/dept.c-2942-\nkernel/dependency/dept.c:2943:void dept_ecxt_exit(struct dept_map *m, unsigned long e_f,\nkernel/dependency/dept.c-2944-\t\t    unsigned long ip)\n--\nkernel/dependency/dept.c=2987=EXPORT_SYMBOL_GPL(dept_ecxt_exit);\nkernel/dependency/dept.c-2988-\nkernel/dependency/dept.c:2989:void dept_task_exit(struct task_struct *t)\nkernel/dependency/dept.c-2990-{\n--\nkernel/dependency/dept.c-3032-\nkernel/dependency/dept.c:3033:void dept_task_init(struct task_struct *t)\nkernel/dependency/dept.c-3034-{\n--\nkernel/dependency/dept.c-3038-\nkernel/dependency/dept.c:3039:void dept_key_init(struct dept_key *k)\nkernel/dependency/dept.c-3040-{\n--\nkernel/dependency/dept.c=3081=EXPORT_SYMBOL_GPL(dept_key_init);\nkernel/dependency/dept.c-3082-\nkernel/dependency/dept.c:3083:void dept_key_destroy(struct dept_key *k)\nkernel/dependency/dept.c-3084-{\n--\nkernel/dependency/dept.c=3144=EXPORT_SYMBOL_GPL(dept_key_destroy);\nkernel/dependency/dept.c-3145-\nkernel/dependency/dept.c:3146:static void move_llist(struct llist_head *to, struct llist_head *from)\nkernel/dependency/dept.c-3147-{\n--\nkernel/dependency/dept.c-3158-\nkernel/dependency/dept.c:3159:static void migrate_per_cpu_pool(void)\nkernel/dependency/dept.c-3160-{\n"
}

5/2 2026/07/06 06:47 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 97842
							output: 232
							thoughts: 2010
						

Thoughts:
**Fuzzing DEPT: A Focus on Dependency Tracking Logic**

Okay, so I'm looking at this DEPT patch, which introduces a dependency tracking tool in the kernel, similar to lockdep. My goal here is to figure out the best way to fuzz this new functionality effectively. First, the `dept_*` functions listed are all over the place in the kernel.  `dept_wait` and `dept_event` seem like hot paths, called for *every* lock or unlock. But wait, DEPT is a debugging tool, not directly exposed to userspace.  Fuzzing it directly via syscalls isn't feasible. Instead, we fuzz the kernel *with* DEPT enabled, which will exercise it.

So, I need to enable `CONFIG_DEPT` and `CONFIG_DEPT_AGGRESSIVE_TIMEOUT_WAIT`.  And there's `CONFIG_DEPT_UNIT_TEST`?  Might as well enable it; it'll run at boot and catch any immediate issues. That seems like a no-brainer.

The critical part is choosing *what* to focus the fuzzer on. Because DEPT is a debugging tool, I want to hone in on its core logic to prevent crashes or other bugs. The initial thought was to include all functions related to DEPT:

- `__add_dep`
- `__add_idep`
- `check_dl_bfs`
- `bfs`
- `add_ecxt`
- `do_event`
- `add_wait`
- `add_dep`
- `add_iecxt`
- `add_iwait`
- `dept_map_ecxt_modify`
- `dept_map_init`
- `dept_map_reinit`
- `dept_free_range`
- `dept_key_init`
- `dept_key_destroy`

But, `add_wait`, `add_ecxt`, and `do_event` seem like hot paths from DEPT's dependency, but the patch says to avoid hot paths, to give a more balanced coverage. So these are out. The challenge is to make sure we don't skew the fuzzer towards functions called all the time. That means focusing on the *unique* logic, that isn't called for *every* lock or event.

Now, thinking like lockdep,  `__lock_acquire` is hot, but adding a new dependency, is less hot. The core graph-processing functions of DEPT are called when a *new* dependency is added.  So `__add_dep`, `__add_idep`, `check_dl_bfs`, and `bfs` are key, like lockdep's `__lock_acquire`. These functions handle the graph processing when a *new* dependency is added, so that would include:

- `__add_dep`
- `__add_idep`
- `check_dl_bfs`
- `bfs`
- `print_circle`
- `print_dep`
- `print_diagram`
- `dept_free_range`
- `dept_key_destroy`
- `disconnect_class`

I checked `dept_free_range` and `dept_key_destroy` since those are called when modules are unloaded or locks destroyed. Those are not extremely hot so it would also be good to include them.

So, the plan is: enable `DEPT`, `DEPT_AGGRESSIVE_TIMEOUT_WAIT`, and `DEPT_UNIT_TEST`, and focus the fuzzer on the logic in `__add_dep`, `__add_idep`, `check_dl_bfs`, `bfs`, `print_circle`, `dept_free_range`, `dept_key_destroy`, and `disconnect_class`. That will make sure we are focused on the key graph-processing aspects of this new debugging tool. Then we're good to go.



6/2 2026/07/06 06:48 tool set-results
0m Args:
{
  "EnableConfigs": [
    "DEPT",
    "DEPT_AGGRESSIVE_TIMEOUT_WAIT",
    "DEPT_UNIT_TEST"
  ],
  "FocusSymbols": [
    "__add_dep",
    "__add_idep",
    "check_dl_bfs",
    "bfs",
    "print_circle",
    "dept_free_range",
    "dept_key_destroy",
    "disconnect_class"
  ],
  "Reasoning": "The patch introduces DEPT (DEPendency Tracker), a new runtime debugging tool similar to lockdep but for general synchronization primitives (waits/events). It hooks into core kernel primitives like wait queues, completions, page flags, and IRQ handling. Since it adds significant new runtime logic and graph processing for deadlock detection, it is highly functional and should be fuzzed to ensure the tracker itself does not introduce crashes, memory leaks, or deadlocks. We focus on the slow-path graph processing and cleanup functions to avoid skewing the fuzzer on hot-path lock/wait operations.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "DEPT",
    "DEPT_AGGRESSIVE_TIMEOUT_WAIT",
    "DEPT_UNIT_TEST"
  ],
  "FocusSymbols": [
    "__add_dep",
    "__add_idep",
    "check_dl_bfs",
    "bfs",
    "print_circle",
    "dept_free_range",
    "dept_key_destroy",
    "disconnect_class"
  ],
  "Reasoning": "The patch introduces DEPT (DEPendency Tracker), a new runtime debugging tool similar to lockdep but for general synchronization primitives (waits/events). It hooks into core kernel primitives like wait queues, completions, page flags, and IRQ handling. Since it adds significant new runtime logic and graph processing for deadlock detection, it is highly functional and should be fuzzed to ensure the tracker itself does not introduce crashes, memory leaks, or deadlocks. We focus on the slow-path graph processing and cleanup functions to avoid skewing the fuzzer on hot-path lock/wait operations.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)