Compare commits

..

1 Commits

Author SHA1 Message Date
Auke Kok
f604bb4b77 Show last holder PID, inode in client_locks
Add last_user_pid[mode] and last_user_ino[mode] arrays to scoutfs_lock,
filled in at the granted-mode path alongside the existing counts.
The inode is passed by callers for all per-inode cases, and set to 0
for others. PID is from the current task.

The client_locks line is expanded with "ino: rd I wr I wo I pid: rd P
wr P wo P".  Existing users:/waiters: field positions are unchanged.

A simple test case demonstrates the functionality for the two simple
inode/non-inode case, and for a contended lock case (multiple rd/wr
lock holders).

Signed-off-by: Auke Kok <auke.kok@versity.com>
2026-05-08 15:08:23 -07:00
6 changed files with 95 additions and 36 deletions

View File

@@ -4,15 +4,9 @@
%define kmod_git_describe @@GITDESCRIBE@@
%define pkg_date %(date +%%Y%%m%%d)
# Package type: set --define 'per_kver 1' to tie the package to a specific kernel
# version (per-kver mode); leave unset for the default per-minor-release behavior.
# take kernel version or default to uname -r
%{!?kversion: %global kversion %(uname -r)}
%global kernel_version %{kversion}
%if 0%{?per_kver}
%define sanitized_kernel_version %(echo %{kernel_version} | tr - _ |sed -e 's/.x86_64//')
%endif
%if 0%{?el7}
%global kernel_source() /usr/src/kernels/%{kernel_version}.$(arch)
@@ -23,34 +17,16 @@
%{!?_release: %global _release 0.%{pkg_date}git%{kmod_git_hash}}
%if 0%{?el7}
%if 0%{?per_kver}
Name: %{kmod_name}-%{sanitized_kernel_version}
Provides: %{kmod_name} = %{kmod_version}
%else
Name: %{kmod_name}
%endif
%else
%if 0%{?per_kver}
Name: kmod-%{kmod_name}-%{sanitized_kernel_version}
Provides: kmod-%{kmod_name} = %{kmod_version}
%else
Name: kmod-%{kmod_name}
%endif
%endif
Summary: %{kmod_name} kernel module
Version: %{kmod_version}
%if 0%{?per_kver}
Release: %{_release}
%else
Release: %{_release}%{?dist}
%endif
License: GPLv2
Group: System/Kernel
URL: http://scoutfs.org/
%if 0%{?per_kver}
Requires: kernel-core-uname-r = %{kernel_version}
Requires: kernel-modules-uname-r = %{kernel_version}
%endif
%if 0%{?el7}
BuildRequires: %{kernel_module_package_buildreqs}
@@ -129,9 +105,7 @@ find %{buildroot} -type f -name \*.ko -exec %{__chmod} u+x \{\} \;
/lib/modules
%post
%if ! 0%{?per_kver}
echo /lib/modules/%{kversion}/%{install_mod_dir}/scoutfs.ko | weak-modules --add-modules --no-initramfs
%endif
depmod -a
%endif

View File

@@ -980,7 +980,7 @@ static bool lock_flags_invalid(int flags)
*/
static int lock_key_range(struct super_block *sb, enum scoutfs_lock_mode mode, int flags,
struct scoutfs_key *start, struct scoutfs_key *end,
struct scoutfs_lock **ret_lock)
u64 ino, struct scoutfs_lock **ret_lock)
{
DECLARE_LOCK_INFO(sb, linfo);
struct scoutfs_lock *lock;
@@ -1028,6 +1028,8 @@ static int lock_key_range(struct super_block *sb, enum scoutfs_lock_mode mode, i
/* the fast path where we can use the granted mode */
if (lock_modes_match(lock->mode, mode)) {
lock_inc_count(lock->users, mode);
lock->last_user_pid[mode] = task_pid_nr(current);
lock->last_user_ino[mode] = ino;
*ret_lock = lock;
ret = 0;
break;
@@ -1108,7 +1110,7 @@ int scoutfs_lock_ino(struct super_block *sb, enum scoutfs_lock_mode mode, int fl
end.sk_zone = SCOUTFS_FS_ZONE;
end.ski_ino = cpu_to_le64(ino | SCOUTFS_LOCK_INODE_GROUP_MASK);
return lock_key_range(sb, mode, flags, &start, &end, ret_lock);
return lock_key_range(sb, mode, flags, &start, &end, ino, ret_lock);
}
/*
@@ -1238,7 +1240,7 @@ int scoutfs_lock_rename(struct super_block *sb, enum scoutfs_lock_mode mode, int
.sk_type = SCOUTFS_RENAME_TYPE,
};
return lock_key_range(sb, mode, flags, &key, &key, lock);
return lock_key_range(sb, mode, flags, &key, &key, 0, lock);
}
/*
@@ -1286,7 +1288,7 @@ int scoutfs_lock_inode_index(struct super_block *sb, enum scoutfs_lock_mode mode
scoutfs_lock_get_index_item_range(type, major, ino, &start, &end);
return lock_key_range(sb, mode, 0, &start, &end, ret_lock);
return lock_key_range(sb, mode, 0, &start, &end, ino, ret_lock);
}
/*
@@ -1313,7 +1315,7 @@ int scoutfs_lock_orphan(struct super_block *sb, enum scoutfs_lock_mode mode, int
end.sko_ino = cpu_to_le64(U64_MAX);
end.sk_type = SCOUTFS_ORPHAN_TYPE;
return lock_key_range(sb, mode, flags, &start, &end, lock);
return lock_key_range(sb, mode, flags, &start, &end, ino, lock);
}
int scoutfs_lock_xattr_totl(struct super_block *sb, enum scoutfs_lock_mode mode, int flags,
@@ -1324,7 +1326,7 @@ int scoutfs_lock_xattr_totl(struct super_block *sb, enum scoutfs_lock_mode mode,
scoutfs_totl_set_range(&start, &end);
return lock_key_range(sb, mode, flags, &start, &end, lock);
return lock_key_range(sb, mode, flags, &start, &end, 0, lock);
}
int scoutfs_lock_xattr_indx(struct super_block *sb, enum scoutfs_lock_mode mode, int flags,
@@ -1335,7 +1337,7 @@ int scoutfs_lock_xattr_indx(struct super_block *sb, enum scoutfs_lock_mode mode,
scoutfs_xattr_indx_get_range(&start, &end);
return lock_key_range(sb, mode, flags, &start, &end, lock);
return lock_key_range(sb, mode, flags, &start, &end, 0, lock);
}
int scoutfs_lock_quota(struct super_block *sb, enum scoutfs_lock_mode mode, int flags,
@@ -1346,7 +1348,7 @@ int scoutfs_lock_quota(struct super_block *sb, enum scoutfs_lock_mode mode, int
scoutfs_quota_get_lock_range(&start, &end);
return lock_key_range(sb, mode, flags, &start, &end, lock);
return lock_key_range(sb, mode, flags, &start, &end, 0, lock);
}
void scoutfs_unlock(struct super_block *sb, struct scoutfs_lock *lock, enum scoutfs_lock_mode mode)
@@ -1463,7 +1465,7 @@ static void lock_tseq_show(struct seq_file *m, struct scoutfs_tseq_entry *ent)
struct scoutfs_lock *lock =
container_of(ent, struct scoutfs_lock, tseq_entry);
seq_printf(m, "start "SK_FMT" end "SK_FMT" refresh_gen %llu mode %d waiters: rd %u wr %u wo %u users: rd %u wr %u wo %u\n",
seq_printf(m, "start "SK_FMT" end "SK_FMT" refresh_gen %llu mode %d waiters: rd %u wr %u wo %u users: rd %u wr %u wo %u ino: rd %llu wr %llu wo %llu pid: rd %d wr %d wo %d\n",
SK_ARG(&lock->start), SK_ARG(&lock->end),
lock->refresh_gen, lock->mode,
lock->waiters[SCOUTFS_LOCK_READ],
@@ -1471,7 +1473,13 @@ static void lock_tseq_show(struct seq_file *m, struct scoutfs_tseq_entry *ent)
lock->waiters[SCOUTFS_LOCK_WRITE_ONLY],
lock->users[SCOUTFS_LOCK_READ],
lock->users[SCOUTFS_LOCK_WRITE],
lock->users[SCOUTFS_LOCK_WRITE_ONLY]);
lock->users[SCOUTFS_LOCK_WRITE_ONLY],
lock->last_user_ino[SCOUTFS_LOCK_READ],
lock->last_user_ino[SCOUTFS_LOCK_WRITE],
lock->last_user_ino[SCOUTFS_LOCK_WRITE_ONLY],
lock->last_user_pid[SCOUTFS_LOCK_READ],
lock->last_user_pid[SCOUTFS_LOCK_WRITE],
lock->last_user_pid[SCOUTFS_LOCK_WRITE_ONLY]);
}
/*

View File

@@ -42,6 +42,8 @@ struct scoutfs_lock {
enum scoutfs_lock_mode invalidating_mode;
unsigned int waiters[SCOUTFS_LOCK_NR_MODES];
unsigned int users[SCOUTFS_LOCK_NR_MODES];
pid_t last_user_pid[SCOUTFS_LOCK_NR_MODES];
u64 last_user_ino[SCOUTFS_LOCK_NR_MODES];
struct scoutfs_tseq_entry tseq_entry;

View File

@@ -0,0 +1,6 @@
== set up file
== exercise read, write, and write-only modes
== verify FS-zone lock recorded read and write ino+pid
== verify orphan-zone lock recorded write-only ino+pid
== contend on a single inode with concurrent read and write loops
== verify both rd and wr slots populated by concurrent contention

View File

@@ -32,6 +32,7 @@ totl-merge-read.sh
quota-invalidate-race.sh
totl-delta-inject.sh
lock-refleak.sh
lock-pid-ino.sh
lock-shrink-consistency.sh
lock-shrink-read-race.sh
lock-pr-cw-conflict.sh

View File

@@ -0,0 +1,68 @@
#
# verify debugfs client_locks reports per-mode last-user PID and inode.
#
t_require_commands stat touch awk rm
FILE="$T_D0/file"
echo "== set up file"
touch "$FILE"
INO=$(stat -c %i "$FILE")
GROUP_START=$(( INO & ~1023 ))
echo "== exercise read, write, and write-only modes"
t_quiet stat "$FILE"
echo data > "$FILE"
rm -f "$FILE"
echo "== verify FS-zone lock recorded read and write ino+pid"
ERR=$(awk -v group="$GROUP_START" -v ino="$INO" '
$2 == "16." group ".0.0.0.0" {
if ($25 != ino || $32 <= 0)
print "read mode: ino=" $25 " pid=" $32 " want ino=" ino " pid>0"
if ($27 != ino || $34 <= 0)
print "write mode: ino=" $27 " pid=" $34 " want ino=" ino " pid>0"
found = 1
}
END { if (!found) print "no FS-zone client_locks line for group " group }
' < "$(t_debugfs_path)/client_locks")
[ -n "$ERR" ] && t_fail "$ERR"
echo "== verify orphan-zone lock recorded write-only ino+pid"
ERR=$(awk -v ino="$INO" '
$2 == "8.0.4.0.0.0" {
if ($29 != ino || $36 <= 0)
print "write-only mode: ino=" $29 " pid=" $36 " want ino=" ino " pid>0"
found = 1
}
END { if (!found) print "no orphan-zone client_locks line" }
' < "$(t_debugfs_path)/client_locks")
[ -n "$ERR" ] && t_fail "$ERR"
echo "== contend on a single inode with concurrent read and write loops"
FILE2="$T_D0/file2"
touch "$FILE2"
INO2=$(stat -c %i "$FILE2")
GROUP2=$(( INO2 & ~1023 ))
for i in $(seq 1 5); do t_quiet stat "$FILE2"; done &
RPID=$!
for i in $(seq 1 5); do echo $i > "$FILE2"; done &
WPID=$!
wait $RPID $WPID
echo "== verify both rd and wr slots populated by concurrent contention"
ERR=$(awk -v group="$GROUP2" -v ino="$INO2" '
$2 == "16." group ".0.0.0.0" {
if ($25 != ino || $32 <= 0)
print "concurrent read: ino=" $25 " pid=" $32 " want ino=" ino " pid>0"
if ($27 != ino || $34 <= 0)
print "concurrent write: ino=" $27 " pid=" $34 " want ino=" ino " pid>0"
found = 1
}
END { if (!found) print "no FS-zone client_locks line for group " group }
' < "$(t_debugfs_path)/client_locks")
[ -n "$ERR" ] && t_fail "$ERR"
t_pass