Skip to content

Commit

Permalink
add generic/585
Browse files Browse the repository at this point in the history
Quotasync may livelock if others tasks generate enough dirty dquots in parallel
This test case pefrorm fchown to produce dirty quotas

This test known to detect livelock non-journaled quota for kernels prior to v5.4
  • Loading branch information
Dmitry Monakhov committed Oct 31, 2019
1 parent b2b1f60 commit b10ad23
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/Makefile
Expand Up @@ -28,7 +28,7 @@ LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
attr-list-by-handle-cursor-test listxattr dio-interleaved t_dir_type \
dio-invalidate-cache stat_test t_encrypted_d_revalidate \
attr_replace_test swapon mkswap t_attr_corruption t_open_tmpfiles \
fscrypt-crypt-util bulkstat_null_ocount
fscrypt-crypt-util bulkstat_null_ocount chowner

SUBDIRS = log-writes perf

Expand Down
107 changes: 107 additions & 0 deletions src/chowner.c
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2019 YANDEX LLC. All Rights Reserved.
// Author: Dmitry Monakhov <dmtrmonakhov@yandex-team.ru>

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <fcntl.h>
#include <limits.h>

char *progname;

void usage(void)
{
printf("Usage %s: [-b uid] [-e uid] [-g] [-P] [-t timeout] FNAME\n", progname);
printf("\t\t -b: begin of uid range\n");
printf("\t\t -e: length of uid range\n");
printf("\t\t -g: change group uid\n");
printf("\t\t -t: loop timeout\n");
printf("\t\t -P: Do not run chowner loop, only prepare files\n");
exit(1);
}
int main(int argc, char *const *argv)
{
char *filename;
int fd;
int c;
uid_t begin = 2000;
uid_t end = 12000;
unsigned int timeout = 10;
struct timeval start, now, delta = { 0, 0 };
uid_t uid;
gid_t gid = getegid();
int do_group = 0;
int do_prepare = 0;

progname = argv[0];
while ((c = getopt(argc, argv, "b:e:gPt:")) != -1) {
switch (c) {
case 'b':
begin = atoi(optarg);
break;
case 'e':
end = atoi(optarg);
break;
case 'g':
do_group = 1;
break;
case 'P':
do_prepare = 1;
break;
case 't':
timeout = atoi(optarg);
break;
default:
usage();
}
}
if (optind == argc-1)
filename = argv[optind];
else
usage();
if (do_prepare) {
char path[PATH_MAX];
for (uid = begin; uid < end;uid++) {
sprintf(path, "%s.%d", filename, uid);
fd = open(path, O_RDWR|O_CREAT, 0666);
if (fd < 0) {
perror("open");
exit(1);
}
if (do_group)
gid = uid;
if (fchown(fd, uid, gid)) {
perror("chown");
exit(1);
}
close(fd);
}
return 0;
}
fd = open(filename, O_RDWR|O_CREAT, 0666);
if (fd < 0) {
perror("open");
exit(1);
}
gettimeofday(&start, NULL);

while (1) {
for (uid = begin; uid < end;uid++) {
if (do_group)
gid = uid;
if (fchown(fd, uid, gid)) {
perror("chown");
exit(1);
}
}
gettimeofday(&now, NULL);
timersub(&now, &start, &delta);
if (delta.tv_sec >= timeout)
break;
}
return 0;
}
94 changes: 94 additions & 0 deletions tests/generic/585
@@ -0,0 +1,94 @@
#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2019 YANDEX LLC. All Rights Reserved.
#
# FS QA Test 585
#
# Check livelock during quota-sync while other tasks dirty quotas in parallel.
# Run fchown(2) in a loop is the fastest way to produce dirty quotas
#
seq=`basename $0`
seqres=$RESULT_DIR/$seq
echo "QA output created by $seq"

here=`pwd`
tmp=/tmp/$$
status=1 # failure is the default!
trap "_cleanup; exit \$status" 0 1 2 3 15

_cleanup()
{
cd /
rm -f $tmp.*
}
# get standard environment, filters and checks
. ./common/rc
. ./common/filter
. ./common/quota


begin=2000
end=10000
nr_proc=$((16 * LOAD_FACTOR))
deadline=$((100 * TIME_FACTOR))

_workout()
{
for ((i=0; i < nr_proc; i++))
do
# Spread files to isolated dirs to minimize locking contention
mkdir -p $SCRATCH_MNT/chowner/$i
$here/src/chowner $SCRATCH_MNT/chowner/$i/test -b $begin -e $end \
-t $((deadline + 10)) &
pids="$pids $!"
done
# Let chowners warm up ...
sleep 5
start=$(date +%s)
for ((i=0;i<3;i++))
do
s=$(date +%s)
# In normal situation command should finish in ~1sec,
# but in case of livelock it will spin until chowners exits
$*
e=$(date +%s)
echo "loop $i: $* runtime: $((e-s))" >> $seqres.full
sleep 2
done
end=$(date +%s)
runtime=$((end-start))
echo "DONE: $* total runtime: $runtime" >> $seqres.full
kill -TERM $pids 2> /dev/null
wait $pids

[ $runtime -le $deadline ] || \
_fail "Live lock detected, $* runtime: $runtime, deadline: $deadline"
}

# real QA test starts here
_supported_fs generic
_supported_os Linux
_require_quota
#_require_user
_require_scratch
_require_command "$KILLALL_PROG" killall

rm -f $seqres.full
_scratch_mkfs_sized $((512 * 1024 * 1024)) >> $seqres.full 2>&1
_scratch_mount "-o quota,user"
chmod 777 $SCRATCH_MNT
quotacheck -u $SCRATCH_MNT 2>/dev/null
quotaon -v -u $SCRATCH_MNT >> $seqres.full 2>&1

# Preparation step: Create all files with uid in range
# to cache quota in kernel memory
mkdir -p $SCRATCH_MNT/q
$here/src/chowner $SCRATCH_MNT/q -b $begin -e $end -P

_workout quotasync $SCRATCH_MNT
_workout sync

echo "Silence is golden"
# success, all done
status=0
exit
2 changes: 2 additions & 0 deletions tests/generic/585.out
@@ -0,0 +1,2 @@
QA output created by 585
Silence is golden
1 change: 1 addition & 0 deletions tests/generic/group
Expand Up @@ -587,3 +587,4 @@
582 auto quick encrypt
583 auto quick encrypt
584 auto quick encrypt
585 auto quota rw stress

0 comments on commit b10ad23

Please sign in to comment.