* Lockdep circular dependency: btrfs_swap_activate() calls sysfs_notify()
@ 2026-07-31 10:57 Christian Borntraeger
2026-07-31 11:12 ` Qu Wenruo
0 siblings, 1 reply; 5+ messages in thread
From: Christian Borntraeger @ 2026-07-31 10:57 UTC (permalink / raw)
To: Chris Mason, David Sterba, Qu Wenruo; +Cc: linux-btrfs, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 6384 bytes --]
One or more of the following files ( btrfs-swapon-kernfs-repro.sh ) violates IBM policy and all attachment(s) have been removed from the message.
**********************************************************************
We had the calltrace at the bottom of this mail in our CI logs
I also attached an AI generated reproducer that triggers this easily.
For convenience, here is what AI came up with analysing the log, but
I would like your take on it. Let me know if you want to see the
AI proposed fix.
----
btrfs_swap_activate() takes the inode's i_mmap_lock for write very
early (inode.c:10120) and holds it across the entire function; the
comment there explains the intent, which is to keep mmap writes from
racing with the delalloc flush and the extent range lock.
There are three btrfs_exclop_finish() calls inside that window:
fs/btrfs/inode.c:10181 error path, swapfile on a rw subvolume
with an active snapshot
fs/btrfs/inode.c:10202 error path, could not lock the snapshot drew
lock
fs/btrfs/inode.c:10399 the common "out:" path -- taken on both
success and failure
and btrfs_exclop_finish() (fs/btrfs/fs.c:224) ends with an unconditional
sysfs_notify(). So the offending edge is taken on *every* successful
swapon of a btrfs swap file, not only on an error path.
The sysfs_notify() itself does nothing but a kernfs lookup plus a poll
wakeup for userspace watching the "exclusive_operation" attribute. It
has no dependency whatsoever on i_mmap_lock, or on the inode at all.
Both kernfs_rwsem acquisitions in the cycle are read acquisitions
({++++}), so the two of them alone cannot deadlock. A real hang needs a
third task waiting to take kernfs_rwsem for write, because rwsem
write-fairness makes a later down_read() block behind a queued writer:
T_swapon: holds i_mmap_lock(write)
blocks in down_read(kernfs_rwsem) [queued behind T_w]
T_w: blocks in down_write(kernfs_rwsem) [waiting for T_dir]
e.g. any sysfs node create/remove -- device hotplug,
module load, cgroup or block-device attribute changes
T_dir: holds kernfs_rwsem(read) in kernfs_fop_readdir
faults on the user dirent buffer -> mmap_lock
-> btrfs_page_mkwrite -> down_read(i_mmap_lock)
blocks behind T_swapon's write holder
-> three-way deadlock.
That is a narrow race, which is consistent with this having gone
unnoticed for years, but every step of it is ordinary system activity.
The dependency is genuine and worth fixing rather than annotating away.
Suggested fix is to get the sysfs_notify() out from under i_mmap_lock
real life log found in our CI:
----------------------------
LOCKDEP_CIRCULAR (suite: tela-distro, case: tests/test_mempig/test_mempig)
WARNING: possible circular locking dependency detected
7.2.0-20260730.rc5.git10.af7a8a7752eb.300.fc44.s390x+debug #1 Not tainted
------------------------------------------------------
swapon/172010 is trying to acquire lock:
000002ea80a485a0 (&root->kernfs_rwsem){++++}-{3:3}, at: kernfs_find_and_get_ns+0x3c/0x80
but task is already holding lock:
000002ebc465d270 (&ei->i_mmap_lock){++++}-{3:3}, at: btrfs_swap_activate+0x9a/0x1240
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #3 (&ei->i_mmap_lock){++++}-{3:3}:
lock_acquire+0x150/0x3f0
down_read+0x5a/0x280
btrfs_page_mkwrite+0x258/0x870
do_page_mkwrite+0x60/0x160
do_wp_page+0x128/0x750
__handle_mm_fault+0x1be/0x590
handle_mm_fault+0xa2/0x370
do_exception+0x292/0x590
__do_pgm_check+0x168/0x430
pgm_check_handler+0x114/0x160
-> #2 (sb_pagefaults){.+.+}-{0:0}:
lock_acquire+0x150/0x3f0
percpu_down_read_internal.constprop.0+0x54/0x120
btrfs_page_mkwrite+0xa6/0x870
do_page_mkwrite+0x60/0x160
do_fault+0x132/0x4a0
__handle_mm_fault+0x1be/0x590
handle_mm_fault+0xa2/0x370
do_exception+0x1a0/0x590
__do_pgm_check+0x168/0x430
pgm_check_handler+0x114/0x160
-> #1 (&mm->mmap_lock){++++}-{3:3}:
lock_acquire+0x150/0x3f0
__might_fault+0x7a/0xa0
filldir64+0x11c/0x210
kernfs_fop_readdir+0x150/0x4c0
iterate_dir+0xcc/0x2d0
__do_sys_getdents64+0x7a/0x130
__do_syscall+0x172/0x750
system_call+0x72/0x90
-> #0 (&root->kernfs_rwsem){++++}-{3:3}:
check_prev_add+0x160/0xf40
__lock_acquire+0x12aa/0x15a0
lock_acquire+0x150/0x3f0
down_read+0x5a/0x280
kernfs_find_and_get_ns+0x3c/0x80
sysfs_notify+0x60/0xc0
btrfs_swap_activate+0x83c/0x1240
__do_sys_swapon+0x278/0x9c0
__do_syscall+0x172/0x750
system_call+0x72/0x90
other info that might help us debug this:
Chain exists of:
&root->kernfs_rwsem --> sb_pagefaults --> &ei->i_mmap_lock
Possible unsafe locking scenario:
CPU0 CPU1
---- ----
lock(&ei->i_mmap_lock);
lock(sb_pagefaults);
lock(&ei->i_mmap_lock);
rlock(&root->kernfs_rwsem);
*** DEADLOCK ***
2 locks held by swapon/172010:
#0: 000002ebc465d3f0 (&sb->s_type->i_mutex_key#20){++++}-{3:3}, at: __do_sys_swapon+0x5be/0x9c0
#1: 000002ebc465d270 (&ei->i_mmap_lock){++++}-{3:3}, at: btrfs_swap_activate+0x9a/0x1240
stack backtrace:
CPU: 6 UID: 0 PID: 172010 Comm: swapon Not tainted 7.2.0-20260730.rc5.git10.af7a8a7752eb.300.fc44.s390x+debug #1 PREEMPT
Hardware name: IBM 8561 T01 701 (z/VM 7.4.0)
Call Trace:
[<000003f7d5ab4e3e>] dump_stack_lvl+0xae/0x108
[<000003f7d5bbef24>] print_circular_bug+0x1a4/0x230
[<000003f7d5bbf13c>] check_noncircular+0x18c/0x1b0
[<000003f7d5bc0510>] check_prev_add+0x160/0xf40
[<000003f7d5bc408a>] __lock_acquire+0x12aa/0x15a0
[<000003f7d5bc44d0>] lock_acquire+0x150/0x3f0
[<000003f7d6c447ca>] down_read+0x5a/0x280
[<000003f7d60ad61c>] kernfs_find_and_get_ns+0x3c/0x80
[<000003f7d60b3b70>] sysfs_notify+0x60/0xc0
[<000003f7d635653c>] btrfs_swap_activate+0x83c/0x1240
[<000003f7d5f1d268>] __do_sys_swapon+0x278/0x9c0
[<000003f7d6c369a2>] __do_syscall+0x172/0x750
[<000003f7d6c4baa2>] system_call+0x72/0x90
INFO: lockdep is turned off.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: Lockdep circular dependency: btrfs_swap_activate() calls sysfs_notify()
2026-07-31 10:57 Lockdep circular dependency: btrfs_swap_activate() calls sysfs_notify() Christian Borntraeger
@ 2026-07-31 11:12 ` Qu Wenruo
2026-07-31 11:14 ` Christian Borntraeger
0 siblings, 1 reply; 5+ messages in thread
From: Qu Wenruo @ 2026-07-31 11:12 UTC (permalink / raw)
To: Christian Borntraeger, Chris Mason, David Sterba, Qu Wenruo
Cc: linux-btrfs, linux-kernel
在 2026/7/31 20:27, Christian Borntraeger 写道:
> One or more of the following files ( btrfs-swapon-kernfs-repro.sh )
> violates IBM policy and all attachment(s) have been removed from the
> message.
If the bash script, as an attachment, violates the policy, can you just
paste the content as a reply?
As I still have some uncertainty on the LLM explanation.
A concrete reproducer would allow us to do our own experiments and get
everything more clear.
Otherwise really appreciated all your reports, no matter if it's
generated by LLM or not, as so far your reports are 100% real bugs.
Thanks,
Qu
>
> **********************************************************************
> We had the calltrace at the bottom of this mail in our CI logs
> I also attached an AI generated reproducer that triggers this easily.
>
> For convenience, here is what AI came up with analysing the log, but
> I would like your take on it. Let me know if you want to see the
> AI proposed fix.
>
> ----
>
> btrfs_swap_activate() takes the inode's i_mmap_lock for write very
> early (inode.c:10120) and holds it across the entire function; the
> comment there explains the intent, which is to keep mmap writes from
> racing with the delalloc flush and the extent range lock.
>
> There are three btrfs_exclop_finish() calls inside that window:
>
> fs/btrfs/inode.c:10181 error path, swapfile on a rw subvolume
> with an active snapshot
> fs/btrfs/inode.c:10202 error path, could not lock the snapshot drew
> lock
> fs/btrfs/inode.c:10399 the common "out:" path -- taken on both
> success and failure
>
> and btrfs_exclop_finish() (fs/btrfs/fs.c:224) ends with an unconditional
> sysfs_notify(). So the offending edge is taken on *every* successful
> swapon of a btrfs swap file, not only on an error path.
>
> The sysfs_notify() itself does nothing but a kernfs lookup plus a poll
> wakeup for userspace watching the "exclusive_operation" attribute. It
> has no dependency whatsoever on i_mmap_lock, or on the inode at all.
>
>
> Both kernfs_rwsem acquisitions in the cycle are read acquisitions
> ({++++}), so the two of them alone cannot deadlock. A real hang needs a
> third task waiting to take kernfs_rwsem for write, because rwsem
> write-fairness makes a later down_read() block behind a queued writer:
>
> T_swapon: holds i_mmap_lock(write)
> blocks in down_read(kernfs_rwsem) [queued behind T_w]
> T_w: blocks in down_write(kernfs_rwsem) [waiting for T_dir]
> e.g. any sysfs node create/remove -- device hotplug,
> module load, cgroup or block-device attribute changes
> T_dir: holds kernfs_rwsem(read) in kernfs_fop_readdir
> faults on the user dirent buffer -> mmap_lock
> -> btrfs_page_mkwrite -> down_read(i_mmap_lock)
> blocks behind T_swapon's write holder
>
> -> three-way deadlock.
>
> That is a narrow race, which is consistent with this having gone
> unnoticed for years, but every step of it is ordinary system activity.
> The dependency is genuine and worth fixing rather than annotating away.
>
> Suggested fix is to get the sysfs_notify() out from under i_mmap_lock
>
>
> real life log found in our CI:
> ----------------------------
> LOCKDEP_CIRCULAR (suite: tela-distro, case: tests/test_mempig/test_mempig)
> WARNING: possible circular locking dependency detected
> 7.2.0-20260730.rc5.git10.af7a8a7752eb.300.fc44.s390x+debug #1 Not tainted
> ------------------------------------------------------
> swapon/172010 is trying to acquire lock:
> 000002ea80a485a0 (&root->kernfs_rwsem){++++}-{3:3}, at:
> kernfs_find_and_get_ns+0x3c/0x80
> but task is already holding lock:
> 000002ebc465d270 (&ei->i_mmap_lock){++++}-{3:3}, at:
> btrfs_swap_activate+0x9a/0x1240
> which lock already depends on the new lock.
> the existing dependency chain (in reverse order) is:
> -> #3 (&ei->i_mmap_lock){++++}-{3:3}:
> lock_acquire+0x150/0x3f0
> down_read+0x5a/0x280
> btrfs_page_mkwrite+0x258/0x870
> do_page_mkwrite+0x60/0x160
> do_wp_page+0x128/0x750
> __handle_mm_fault+0x1be/0x590
> handle_mm_fault+0xa2/0x370
> do_exception+0x292/0x590
> __do_pgm_check+0x168/0x430
> pgm_check_handler+0x114/0x160
> -> #2 (sb_pagefaults){.+.+}-{0:0}:
> lock_acquire+0x150/0x3f0
> percpu_down_read_internal.constprop.0+0x54/0x120
> btrfs_page_mkwrite+0xa6/0x870
> do_page_mkwrite+0x60/0x160
> do_fault+0x132/0x4a0
> __handle_mm_fault+0x1be/0x590
> handle_mm_fault+0xa2/0x370
> do_exception+0x1a0/0x590
> __do_pgm_check+0x168/0x430
> pgm_check_handler+0x114/0x160
> -> #1 (&mm->mmap_lock){++++}-{3:3}:
> lock_acquire+0x150/0x3f0
> __might_fault+0x7a/0xa0
> filldir64+0x11c/0x210
> kernfs_fop_readdir+0x150/0x4c0
> iterate_dir+0xcc/0x2d0
> __do_sys_getdents64+0x7a/0x130
> __do_syscall+0x172/0x750
> system_call+0x72/0x90
> -> #0 (&root->kernfs_rwsem){++++}-{3:3}:
> check_prev_add+0x160/0xf40
> __lock_acquire+0x12aa/0x15a0
> lock_acquire+0x150/0x3f0
> down_read+0x5a/0x280
> kernfs_find_and_get_ns+0x3c/0x80
> sysfs_notify+0x60/0xc0
> btrfs_swap_activate+0x83c/0x1240
> __do_sys_swapon+0x278/0x9c0
> __do_syscall+0x172/0x750
> system_call+0x72/0x90
> other info that might help us debug this:
> Chain exists of:
> &root->kernfs_rwsem --> sb_pagefaults --> &ei->i_mmap_lock
> Possible unsafe locking scenario:
> CPU0 CPU1
> ---- ----
> lock(&ei->i_mmap_lock);
> lock(sb_pagefaults);
> lock(&ei->i_mmap_lock);
> rlock(&root->kernfs_rwsem);
> *** DEADLOCK ***
> 2 locks held by swapon/172010:
> #0: 000002ebc465d3f0 (&sb->s_type->i_mutex_key#20){++++}-{3:3}, at:
> __do_sys_swapon+0x5be/0x9c0
> #1: 000002ebc465d270 (&ei->i_mmap_lock){++++}-{3:3}, at:
> btrfs_swap_activate+0x9a/0x1240
> stack backtrace:
> CPU: 6 UID: 0 PID: 172010 Comm: swapon Not tainted
> 7.2.0-20260730.rc5.git10.af7a8a7752eb.300.fc44.s390x+debug #1 PREEMPT
> Hardware name: IBM 8561 T01 701 (z/VM 7.4.0)
> Call Trace:
> [<000003f7d5ab4e3e>] dump_stack_lvl+0xae/0x108
> [<000003f7d5bbef24>] print_circular_bug+0x1a4/0x230
> [<000003f7d5bbf13c>] check_noncircular+0x18c/0x1b0
> [<000003f7d5bc0510>] check_prev_add+0x160/0xf40
> [<000003f7d5bc408a>] __lock_acquire+0x12aa/0x15a0
> [<000003f7d5bc44d0>] lock_acquire+0x150/0x3f0
> [<000003f7d6c447ca>] down_read+0x5a/0x280
> [<000003f7d60ad61c>] kernfs_find_and_get_ns+0x3c/0x80
> [<000003f7d60b3b70>] sysfs_notify+0x60/0xc0
> [<000003f7d635653c>] btrfs_swap_activate+0x83c/0x1240
> [<000003f7d5f1d268>] __do_sys_swapon+0x278/0x9c0
> [<000003f7d6c369a2>] __do_syscall+0x172/0x750
> [<000003f7d6c4baa2>] system_call+0x72/0x90
> INFO: lockdep is turned off.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: Lockdep circular dependency: btrfs_swap_activate() calls sysfs_notify()
2026-07-31 11:12 ` Qu Wenruo
@ 2026-07-31 11:14 ` Christian Borntraeger
2026-08-03 9:07 ` Christian Borntraeger
0 siblings, 1 reply; 5+ messages in thread
From: Christian Borntraeger @ 2026-07-31 11:14 UTC (permalink / raw)
To: Qu Wenruo, Chris Mason, David Sterba, Qu Wenruo; +Cc: linux-btrfs, linux-kernel
Am 31.07.26 um 13:12 schrieb Qu Wenruo:
>
>
> 在 2026/7/31 20:27, Christian Borntraeger 写道:
>> One or more of the following files ( btrfs-swapon-kernfs-repro.sh ) violates IBM policy and all attachment(s) have been removed from the message.
Oh.
Lets try this:
#!/bin/bash
# Reproducer for the lockdep circular dependency
#
# &root->kernfs_rwsem --> sb_pagefaults --> &ei->i_mmap_lock
#
# btrfs_swap_activate() calls btrfs_exclop_finish() -> sysfs_notify() ->
# kernfs_rwsem while holding the inode's i_mmap_lock for write, while
# kernfs_fop_readdir() holds kernfs_rwsem across filldir64() (-> mmap_lock)
# and a btrfs write fault goes mmap_lock -> sb_pagefaults -> i_mmap_lock.
#
#
# Must be run as root, on a kernel with CONFIG_PROVE_LOCKING and
# CONFIG_DEBUG_ATOMIC_SLEEP. Run it on an unpatched kernel to see the splat,
# and on a patched one to confirm it is gone.
#
# Usage: ./btrfs-swapon-kernfs-repro.sh [existing-btrfs-mountpoint]
#
# With no argument a 512M loopback btrfs image is created under /var/tmp and
# removed again at the end. With an argument, that existing single-device
# btrfs mount is used and only the test files inside it are removed.
#
# Exit codes: 0 = no splat (expected with the fix), 1 = reproduced,
# 2 = inconclusive (setup problem, or lockdep already off).
set -u
IMG=""
LOOP=""
MNT=""
OWN_FS=0
SWAPFILE=""
SWAP_ON=0
SWAPON_OK=0
msg() { printf '[repro] %s\n' "$*"; }
fail() { printf '[repro] ERROR: %s\n' "$*" >&2; exit 2; }
cleanup() {
set +e
[ "$SWAP_ON" = 1 ] && { msg "swapoff $SWAPFILE"; swapoff "$SWAPFILE"; }
if [ "$OWN_FS" = 1 ]; then
[ -n "$MNT" ] && umount "$MNT" 2>/dev/null && rmdir "$MNT"
[ -n "$LOOP" ] && losetup -d "$LOOP" 2>/dev/null
[ -n "$IMG" ] && rm -f "$IMG"
elif [ -n "$MNT" ]; then
# Only ever the two files this script created, never a bare path.
[ -n "$SWAPFILE" ] && rm -f "$SWAPFILE"
rm -f "$MNT/lockdep-mmap.dat"
fi
}
trap cleanup EXIT
# ---------------------------------------------------------------- checks ---
[ "$(id -u)" = 0 ] || fail "must be run as root"
for t in mkfs.btrfs losetup mkswap swapon swapoff chattr dmesg \
truncate stat find dd sync mount umount; do
command -v "$t" >/dev/null || fail "missing tool: $t"
done
[ -e /proc/lockdep_stats ] || fail \
"no /proc/lockdep_stats -- kernel lacks CONFIG_PROVE_LOCKING, nothing to see"
# A previous splat disables lockdep for the rest of the boot ("INFO: lockdep
# is turned off"). Nothing will be reported after that, so bail out early
# rather than report a bogus PASS.
if grep -qE '^ *debug_locks: *0' /proc/lockdep_stats; then
fail "lockdep is already turned off (earlier splat this boot) -- reboot first"
fi
# Edge #1 of the cycle needs the might_fault() annotation in filldir64().
KCONF=""
for c in /proc/config.gz "/boot/config-$(uname -r)"; do
[ -e "$c" ] && { KCONF="$c"; break; }
done
if [ -n "$KCONF" ]; then
if [ "${KCONF##*.}" = gz ]; then RDCONF="zcat"; else RDCONF="cat"; fi
$RDCONF "$KCONF" | grep -q '^CONFIG_DEBUG_ATOMIC_SLEEP=y' || msg \
"WARNING: CONFIG_DEBUG_ATOMIC_SLEEP is not set -- filldir64() will not
annotate mmap_lock and the cycle cannot be detected"
else
msg "note: could not find the kernel config, not checking DEBUG_ATOMIC_SLEEP"
fi
MMAP_HELPER=""
if command -v python3 >/dev/null; then
MMAP_HELPER=python3
elif command -v cc >/dev/null; then
MMAP_HELPER=cc
else
fail "need python3 or cc to do the mmap write fault"
fi
msg "kernel $(uname -r), lockdep active"
# Cursor for the verdict. Taken before the first step, not just before
# swapon: if some earlier boot activity already registered the
# i_mmap_lock -> kernfs_rwsem edge, the cycle is completed by the mmap or
# the readdir step below instead, and the splat has to be caught there too.
DMESG_LINES=$(dmesg | wc -l)
# ------------------------------------------------------------ test btrfs ---
if [ $# -ge 1 ]; then
MNT="$1"
[ -d "$MNT" ] || fail "$MNT is not a directory"
[ "$(stat -f -c %T "$MNT")" = btrfs ] || fail "$MNT is not btrfs"
msg "using existing btrfs at $MNT"
else
OWN_FS=1
IMG=$(mktemp /var/tmp/btrfs-swapon-repro.XXXXXX.img) || fail "mktemp failed"
MNT=$(mktemp -d /var/tmp/btrfs-swapon-repro.XXXXXX.mnt) || fail "mktemp failed"
msg "creating 512M btrfs image $IMG"
truncate -s 512M "$IMG" || fail "truncate failed"
LOOP=$(losetup --find --show "$IMG") || fail "losetup failed"
mkfs.btrfs -q -f "$LOOP" || fail "mkfs.btrfs failed"
# No compression: a compressed swap file is rejected by btrfs.
mount -o compress=no "$LOOP" "$MNT" || fail "mount failed"
msg "mounted $LOOP on $MNT"
fi
# ------------------------------------ edges #2/#3: mmap write fault on btrfs --
# handle_mm_fault -> do_page_mkwrite -> btrfs_page_mkwrite
# -> sb_start_pagefault() [sb_pagefaults]
# -> down_read(&BTRFS_I(inode)->i_mmap_lock)
# Both the do_fault (first touch is a write) and the do_wp_page (read first,
# then write) variants are exercised.
MMAPFILE="$MNT/lockdep-mmap.dat"
dd if=/dev/zero of="$MMAPFILE" bs=4096 count=64 status=none || fail "dd failed"
sync
msg "dirtying a btrfs file mapping (mmap_lock -> sb_pagefaults -> i_mmap_lock)"
if [ "$MMAP_HELPER" = python3 ]; then
python3 - "$MMAPFILE" <<-'EOF' || fail "mmap helper failed"
import mmap, os, sys
fd = os.open(sys.argv[1], os.O_RDWR)
m = mmap.mmap(fd, 4096 * 64)
m[0:8] = b"lockdep!" # write fault on an absent page (do_fault)
_ = m[8192] # populate clean ...
m[8192:8200] = b"lockdep!" # ... then dirty it (do_wp_page)
m.flush()
m.close()
os.close(fd)
EOF
else
HELPER=$(mktemp -d)
cat > "$HELPER/mw.c" <<-'EOF'
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
int main(int argc, char **argv)
{
size_t len = 4096 * 64;
char *p;
int fd = open(argv[1], O_RDWR);
if (fd < 0)
return perror("open"), 1;
p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (p == MAP_FAILED)
return perror("mmap"), 1;
p[0] = 'x'; /* do_fault */
(void)*(volatile char *)(p + 8192);
p[8192] = 'x'; /* do_wp_page */
msync(p, len, MS_SYNC);
munmap(p, len);
close(fd);
return 0;
}
EOF
cc -O2 -o "$HELPER/mw" "$HELPER/mw.c" || fail "compiling the mmap helper failed"
"$HELPER/mw" "$MMAPFILE" || fail "mmap helper failed"
rm -rf "$HELPER"
fi
# ------------------------------------------- edge #1: kernfs readdir + fault --
# kernfs_fop_readdir() holds kernfs_rwsem for read across the whole emit loop
# and calls filldir64() -> __might_fault() -> mmap_lock. Any getdents on a
# sysfs directory registers it.
msg "reading sysfs directories (kernfs_rwsem -> mmap_lock)"
for d in /sys/fs/btrfs /sys/class/net /sys/block /sys/devices/system/cpu; do
[ -d "$d" ] && ls -fU "$d" >/dev/null 2>&1
done
find /sys/fs/btrfs -maxdepth 3 >/dev/null 2>&1
# ------------------------------------------- edge #0: swapon on btrfs (bang) --
# btrfs_swap_activate() holds i_mmap_lock for write across
# btrfs_exclop_finish() -> sysfs_notify() -> kernfs_find_and_get_ns()
# -> down_read(&root->kernfs_rwsem)
#
# The swap file has to satisfy every btrfs_swap_activate() precondition:
# NODATACOW and NODATASUM (both from chattr +C on a still empty file), not
# compressed, no holes, no inline extent, not shared. fallocate() is not
# used on purpose -- the extents are written out for real.
SWAPFILE="$MNT/lockdep-swapfile"
msg "creating swap file $SWAPFILE"
rm -f "$SWAPFILE"
touch "$SWAPFILE" || fail "touch failed"
chattr +C "$SWAPFILE" || fail "chattr +C failed (needs NODATACOW)"
chmod 600 "$SWAPFILE"
dd if=/dev/zero of="$SWAPFILE" bs=1M count=64 status=none || fail "dd failed"
sync
mkswap "$SWAPFILE" >/dev/null || fail "mkswap failed"
msg "swapon -- this is the call that closes the cycle"
if swapon -p 0 "$SWAPFILE"; then
SWAP_ON=1
SWAPON_OK=1
msg "swapon succeeded"
else
msg "swapon FAILED -- check the btrfs_warn above for which precondition"
msg "(the lockdep edge is taken on the error paths too, so keep reading)"
fi
sleep 1
# ------------------------------------------------------------------ verdict --
NEW=$(dmesg | tail -n +$((DMESG_LINES + 1)))
if printf '%s\n' "$NEW" | grep -q "possible circular locking dependency"; then
printf '%s\n' "$NEW" | sed -n '/possible circular locking dependency/,$p'
if printf '%s\n' "$NEW" | grep -q "kernfs_rwsem"; then
msg "REPRODUCED: the expected cycle, kernfs_rwsem is in the chain"
exit 1
fi
msg "INCONCLUSIVE: a circular dependency was reported, but without"
msg "kernfs_rwsem -- that is a different bug, compare against more.txt"
exit 2
fi
msg "no lockdep splat"
if grep -qE '^ *debug_locks: *0' /proc/lockdep_stats; then
msg "INCONCLUSIVE: lockdep turned itself off during the run"
exit 2
fi
if [ "$SWAPON_OK" = 0 ]; then
msg "INCONCLUSIVE: swapon did not get far enough, fix that first"
exit 2
fi
msg "PASS: the i_mmap_lock -> kernfs_rwsem edge was not taken"
msg "(expected on a kernel carrying btrfs-swapon-kernfs-fix.patch)"
exit 0
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: Lockdep circular dependency: btrfs_swap_activate() calls sysfs_notify()
2026-07-31 11:14 ` Christian Borntraeger
@ 2026-08-03 9:07 ` Christian Borntraeger
2026-08-03 9:44 ` Qu Wenruo
0 siblings, 1 reply; 5+ messages in thread
From: Christian Borntraeger @ 2026-08-03 9:07 UTC (permalink / raw)
To: Qu Wenruo, Chris Mason, David Sterba, Qu Wenruo; +Cc: linux-btrfs, linux-kernel
Am 31.07.26 um 13:14 schrieb Christian Borntraeger:
>
>
> Am 31.07.26 um 13:12 schrieb Qu Wenruo:
>>
>>
>> 在 2026/7/31 20:27, Christian Borntraeger 写道:
>>> One or more of the following files ( btrfs-swapon-kernfs-repro.sh ) violates IBM policy and all attachment(s) have been removed from the message.
>
> Oh.
>
>
> Lets try this:
> #!/bin/bash
> # Reproducer for the lockdep circular dependency
> #
Can you reproduce the lockdep message?
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: Lockdep circular dependency: btrfs_swap_activate() calls sysfs_notify()
2026-08-03 9:07 ` Christian Borntraeger
@ 2026-08-03 9:44 ` Qu Wenruo
0 siblings, 0 replies; 5+ messages in thread
From: Qu Wenruo @ 2026-08-03 9:44 UTC (permalink / raw)
To: Christian Borntraeger, Chris Mason, David Sterba, Qu Wenruo
Cc: linux-btrfs, linux-kernel
在 2026/8/3 18:37, Christian Borntraeger 写道:
>
>
> Am 31.07.26 um 13:14 schrieb Christian Borntraeger:
>>
>>
>> Am 31.07.26 um 13:12 schrieb Qu Wenruo:
>>>
>>>
>>> 在 2026/7/31 20:27, Christian Borntraeger 写道:
>>>> One or more of the following files ( btrfs-swapon-kernfs-repro.sh )
>>>> violates IBM policy and all attachment(s) have been removed from the
>>>> message.
>>
>> Oh.
>>
>>
>> Lets try this:
>> #!/bin/bash
>> # Reproducer for the lockdep circular dependency
>> #
> Can you reproduce the lockdep message?
>
Sorry, I am on vacation since the last reply, until Wednesday.
However the script seems to have several minor problems that got
rejected by my VM:
- EOF not matching
The involved python scripts expects "EOF" without any other prefix,
but the script is using "\tEOF", which failed to end the file.
My bash version is 5.3.3, not sure if this is version specific.
- Bad indent rejected by python3
The python version is 3.13.7, which rejects any weird indent, so
it fails like:
File "<stdin>", line 1
import mmap, os, sys
IndentationError: unexpected indent
Even with all those minor problems fixed, it still didn't trigger on my VM.
I tried 128 runs, the script is triggered for a newly created btrfs in a
loop, no lockdep warning triggered, all the same successful runs:
[repro] kernel 7.2.0-rc5-custom+, lockdep active
[repro] using existing btrfs at /mnt/btrfs/
[repro] dirtying a btrfs file mapping (mmap_lock -> sb_pagefaults ->
i_mmap_lock)
[repro] reading sysfs directories (kernfs_rwsem -> mmap_lock)
[repro] creating swap file /mnt/btrfs//lockdep-swapfile
[repro] swapon -- this is the call that closes the cycle
[repro] swapon succeeded
[repro] no lockdep splat
[repro] PASS: the i_mmap_lock -> kernfs_rwsem edge was not taken
[repro] (expected on a kernel carrying btrfs-swapon-kernfs-fix.patch)
[repro] swapoff /mnt/btrfs//lockdep-swapfile
The kernel has LOCKDEP and DEBUG_ATOMIC_SLEEP set.
Although this is on a newer branch, based on btrfs' for-next.
Thus not sure if there is anything new, or if it's really too timing
specific.
You may want to send out your fix for extra reviews, other developers
may provide extra review on the fix.
Thanks,
Qu
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-08-03 9:44 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-31 10:57 Lockdep circular dependency: btrfs_swap_activate() calls sysfs_notify() Christian Borntraeger
2026-07-31 11:12 ` Qu Wenruo
2026-07-31 11:14 ` Christian Borntraeger
2026-08-03 9:07 ` Christian Borntraeger
2026-08-03 9:44 ` Qu Wenruo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®