Skip to content

Commit

Permalink
final commit for bachelor's thesis
Browse files Browse the repository at this point in the history
  • Loading branch information
luwae committed Sep 22, 2023
1 parent 302907b commit 63dc6fe
Show file tree
Hide file tree
Showing 63 changed files with 3,899 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/debug/
/target/
/qemu/

*.lock
15 changes: 15 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[workspace]
members = [
"permanent_common",
"permanent_plugin",
"permanent_trace",
"permanent_cig",
"permanent_tester",
]
exclude = [
# Needs to be built with musl.
"fs-testing/fs-dump",
]

[profile.release]
debug = true
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Permanent: Persistent Memory and NVMe Non-Deterministic Tester

This is the source code of Permanent, a tool for automated crash consistency testing for file systems using persistent memory and NVMe.
It is a combination of [Vinter](https://os.itec.kit.edu/65_3814.php) [(source)](https://github.com/KIT-OSGroup/vinter) and [Revin](https://os.itec.kit.edu/97_3853.php).

## Setup

```sh
# install dependencies
# TODO

# Rust via rustup (see https://rustup.rs)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add x86_64-unknown-linux-musl

# get QEMU
wget https://download.qemu.org/qemu-8.0.4.tar.xz
tar xvJf qemu-8.0.4.tar.xz && mv qemu-8.0.4 qemu
# apply patch
pushd qemu && patch -p1 < ../qemu.patch && popd
# configure QEMU
mkdir qemu/build && pushd qemu/build && ../configure --target-list=x86_64-softmmu --enable-debug --enable-plugins && popd
# build QEMU
make -C qemu/build -j$(nproc)

# build permanent
./build.sh

```

## License

Permanent is released under the MIT license, see `LICENSE` for details.
14 changes: 14 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash

set -eux

cargo build --release

# build fs-dump
(cd fs-testing/fs-dump && cargo build --release --target=x86_64-unknown-linux-musl)

# build checkpoint
(cd permanent_plugin && ./build_checkpoint.sh)

# build initramfs
make -C fs-testing/initramfs
1 change: 1 addition & 0 deletions fs-testing/fs-dump/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
9 changes: 9 additions & 0 deletions fs-testing/fs-dump/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "fs-dump"
version = "0.1.0"
edition = "2018"

[dependencies]
walkdir = "2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
85 changes: 85 additions & 0 deletions fs-testing/fs-dump/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use serde::Serialize;
use std::collections::BTreeMap;
use std::os::unix::fs::MetadataExt;
use std::fs::File;
use std::io::Read;
use walkdir::WalkDir;

#[derive(Serialize)]
struct FileAttrs {
typeflag: String,
#[serde(skip_serializing_if = "Option::is_none")]
content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
target: Option<String>,
st_ino: u64,
st_mode: u32,
st_nlink: u64,
st_uid: u32,
st_gid: u32,
st_size: u64,
st_blocks: u64,
st_atim_sec: i64,
st_atim_nsec: i64,
st_mtim_sec: i64,
st_mtim_nsec: i64,
st_ctim_sec: i64,
st_ctim_nsec: i64,
}

fn main() {
let args: Vec<String> = std::env::args().collect();
let (path, dump_contents) = match args.len() {
2 => (&args[1], false),
3 if args[1] == "--contents" => (&args[2], true),
_ => {
println!("usage: {} [--contents] <path>", args[0]);
std::process::exit(1);
}
};
let mut result = BTreeMap::new();
for entry in WalkDir::new(path) {
let entry = entry.expect("could not read dir entry");
let metadata = entry.metadata().expect("could not retrieve file metadata");
result.insert(
entry.path().to_string_lossy().into_owned(),
FileAttrs {
typeflag: match entry.file_type() {
t if t.is_file() => "F",
t if t.is_dir() => "D",
t if t.is_symlink() => "SL",
_ => panic!("unexpected file type at {}", entry.path().display())
}.to_string(),
content: if dump_contents && entry.file_type().is_file() {
let mut file = File::open(entry.path()).expect("could not open file");
let mut contents = String::new();
file.read_to_string(&mut contents).expect("could not read file contents");
Some(contents)
} else {
None
},
target: if dump_contents && entry.file_type().is_symlink() {
Some(std::fs::read_link(entry.path()).expect("could not read symlink").to_string_lossy().into_owned())
} else {
None}
,
st_ino: metadata.ino(),
st_mode: metadata.mode(),
st_nlink: metadata.nlink(),
st_uid: metadata.uid(),
st_gid: metadata.gid(),
st_size: metadata.size(),
st_blocks: metadata.blocks(),
st_atim_sec: metadata.atime(),
st_atim_nsec: metadata.atime_nsec(),
st_mtim_sec: metadata.mtime(),
st_mtim_nsec: metadata.mtime_nsec(),
st_ctim_sec: metadata.ctime(),
st_ctim_nsec: metadata.ctime_nsec(),
},
);
}
print!("PERMANENT START");
serde_json::to_writer_pretty(std::io::stdout(), &result).expect("could not serialize JSON");
print!("PERMANENT END");
}
4 changes: 4 additions & 0 deletions fs-testing/initramfs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/initramfs
/busybox
/busybox_pmfs
*.cpio.gz
29 changes: 29 additions & 0 deletions fs-testing/initramfs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

INITRAMFS_DEPS := \
create-initramfs.sh \
busybox \
../../permanent_plugin/checkpoint \
../fs-dump/target/x86_64-unknown-linux-musl/release/fs-dump

default: initramfs.cpio.gz
.PHONY: default

initramfs.cpio.gz: $(INITRAMFS_DEPS)
./create-initramfs.sh > $@

initramfs_mainline.cpio.gz: $(INITRAMFS_DEPS)
rm -rf initramfs_mainline
mkdir -p initramfs_mainline/{bin,lib}
ln -s lib initramfs_mainline/lib64
./copy-binary.sh `which mkfs.ext4` initramfs_mainline
./copy-binary.sh `which fsck.ext4` initramfs_mainline
./copy-binary.sh `which mkfs.xfs` initramfs_mainline
./copy-binary.sh `which xfs_repair` initramfs_mainline
./create-initramfs.sh initramfs_mainline > $@

initramfs_zilpmem.cpio.gz: $(INITRAMFS_DEPS) ../zil-pmem/initramfs_zfs
./create-initramfs.sh ../zil-pmem/initramfs_zfs > $@

busybox:
curl -o $@ https://busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox
chmod +x $@
16 changes: 16 additions & 0 deletions fs-testing/initramfs/copy-binary.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env bash

set -eu

# Usage: copy-binary.sh <binary> <initramfs root>

binary=${1:?need binary}
initramfs=${2:?need initramfs root}

cp "$binary" "$initramfs/bin/"

# Copy libraries from host system.
ldd "$binary" | \
awk '/ => \// { print $3 } /ld-linux/ { print $1 }' | \
xargs cp -t "$initramfs/lib"

22 changes: 22 additions & 0 deletions fs-testing/initramfs/create-initramfs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -eu

# Usage: create-initramfs.sh [folder with extra files] > initramfs.cpio.gz

cd "$( dirname "${BASH_SOURCE[0]}" )"

rm -rf initramfs

# put extra stuff in $1
if [[ -n "${1:-}" ]]; then
cp -rH "$1" initramfs
fi

mkdir -p initramfs/{bin,mnt,proc,sys}

cp init initramfs
"${BUSYBOX:-./busybox}" --install initramfs/bin
cp ../../permanent_plugin/checkpoint initramfs/bin
cp ../fs-dump/target/x86_64-unknown-linux-musl/release/fs-dump initramfs/bin

(cd initramfs && find . -print0 | cpio --owner root:root --null -ov --format=newc | gzip -9)
13 changes: 13 additions & 0 deletions fs-testing/initramfs/init
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/sh

mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev

if [ -d /init.d ]; then
for f in /init.d/*; do
. "$f"
done
fi

exec /bin/sh
3 changes: 3 additions & 0 deletions fs-testing/tests/test_append.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trace_cmd_suffix: "checkpoint 0 && echo -n test > /mnt/myfile && sync && checkpoint 1 && sleep 2 && echo -n appendedalignedtext01234 >> /mnt/myfile && sync && checkpoint 2"
checkpoint_range: [0, 2]
dump_cmd_suffix: "echo test >> /mnt/myfile && rm -r /mnt/*"
5 changes: 5 additions & 0 deletions fs-testing/tests/test_atime.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# atomically update access time
# touch with old timestamp first to trick relatime feature
trace_cmd_suffix: "checkpoint 0 && echo -n test > /mnt/myfile && touch -d '2020-01-01 00:00:00' /mnt/myfile && sync && checkpoint 1 && sleep 2 && cat /mnt/myfile > /dev/null && sync && checkpoint 2"
checkpoint_range: [0, 2]
dump_cmd_suffix: "echo test >> /mnt/myfile && rm -r /mnt/*"
3 changes: 3 additions & 0 deletions fs-testing/tests/test_chmod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trace_cmd_suffix: "checkpoint 0 && echo -n test > /mnt/myfile && sync && checkpoint 1 && sleep 2 && chmod 666 /mnt/myfile && sync && checkpoint 2"
checkpoint_range: [0, 2]
dump_cmd_suffix: "echo test >> /mnt/myfile && rm -r /mnt/*"
3 changes: 3 additions & 0 deletions fs-testing/tests/test_chown.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trace_cmd_suffix: "checkpoint 0 && echo -n test > /mnt/myfile && sync && checkpoint 1 && sleep 2 && chown 321:789 /mnt/myfile && sync && checkpoint 2"
checkpoint_range: [0, 2]
dump_cmd_suffix: "echo test >> /mnt/myfile && rm -r /mnt/*"
4 changes: 4 additions & 0 deletions fs-testing/tests/test_ctime-mtime.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# touch with old timestamp first to trick relatime feature
trace_cmd_suffix: "checkpoint 0 && mkdir /mnt/mydir && touch /mnt/mydir/myfile && touch -d '2020-01-01 00:00:00' /mnt/mydir && sync && checkpoint 1 && sleep 2 && rm /mnt/mydir/myfile && sync && checkpoint 2"
checkpoint_range: [0, 2]
dump_cmd_suffix: "echo test >> /mnt/myfile && if [ -e /mnt/mydir ] ; then echo test >> /mnt/mydir/myfile ; else true ; fi && rm -r /mnt/*"
3 changes: 3 additions & 0 deletions fs-testing/tests/test_hello-world.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trace_cmd_suffix: "checkpoint 0 && sync && checkpoint 1 && echo HelloWorld > /mnt/myfile && checkpoint 2 && sync && checkpoint 3"
checkpoint_range: [0, 3]
dump_cmd_suffix: "echo test >> /mnt/myfile && rm /mnt/myfile"
3 changes: 3 additions & 0 deletions fs-testing/tests/test_link-hard.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trace_cmd_suffix: "checkpoint 0 && echo -n test > /mnt/myfile && sync && checkpoint 1 && ln /mnt/myfile /mnt/hardlink && sync && checkpoint 2 && sleep 2 && rm /mnt/myfile && sync && checkpoint 3"
checkpoint_range: [0, 3]
dump_cmd_suffix: "echo test > /mnt/myfile && rm -r /mnt/*"
3 changes: 3 additions & 0 deletions fs-testing/tests/test_link-sym.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trace_cmd_suffix: "checkpoint 0 && echo -n test > /mnt/myfile && sync && checkpoint 1 && ln -s /mnt/myfile /mnt/symlink && sync && checkpoint 2"
checkpoint_range: [0, 2]
dump_cmd_suffix: "echo test >> /mnt/myfile && rm -r /mnt/*"
3 changes: 3 additions & 0 deletions fs-testing/tests/test_mkdir-rmdir.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trace_cmd_suffix: "checkpoint 0 && mkdir /mnt/mydir && sync && checkpoint 1 && sleep 2 && rmdir /mnt/mydir && sync && checkpoint 2"
checkpoint_range: [0, 2]
dump_cmd_suffix: "if [ -e /mnt/mydir ] ; then echo -n hoho > /mnt/mydir/file ; else echo -n test > /mnt/mydir ; fi && echo -n test > /mnt/myfile && rm -r /mnt/*"
3 changes: 3 additions & 0 deletions fs-testing/tests/test_rename-dir.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trace_cmd_suffix: "checkpoint 0 && mkdir /mnt/newdir && sync && checkpoint 1 && mkdir /mnt/newdir2 && sync && checkpoint 2 && echo -n test > /mnt/newdir/testfile && sync && checkpoint 3 && mv /mnt/newdir /mnt/newdir2 && sync && checkpoint 4"
checkpoint_range: [0, 4]
dump_cmd_suffix: "echo test >> /mnt/myfile && if [ -f /mnt/newdir2/newdir/testfile ] ; then echo test >> /mnt/newdir2/newdir/testfile ; else true ; fi && rm -r /mnt/*"
3 changes: 3 additions & 0 deletions fs-testing/tests/test_rename-long-name.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trace_cmd_suffix: "checkpoint 0 && echo -n test > /mnt/myfile && sync && checkpoint 1 && mv /mnt/myfile /mnt/testfile_renamed_to_a_long_filename2222222222222222222222222222222222222222222222222223200 && sync && checkpoint 2"
checkpoint_range: [0, 2]
dump_cmd_suffix: "echo test >> /mnt/testfile_renamed_to_a_long_filename2222222222222222222222222222222222222222222222222223200 && rm -r /mnt/*"
3 changes: 3 additions & 0 deletions fs-testing/tests/test_rename.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trace_cmd_suffix: "checkpoint 0 && echo -n tes1 > /mnt/myfile && echo -n tes2 > /mnt/myfile2 && sync && checkpoint 1 && mv /mnt/myfile2 /mnt/myfile && sync && checkpoint 2"
checkpoint_range: [0, 2]
dump_cmd_suffix: "echo test >> /mnt/myfile && rm /mnt/myfile"
3 changes: 3 additions & 0 deletions fs-testing/tests/test_touch-long-name.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trace_cmd_suffix: "checkpoint 0 && touch /mnt/eizAKifFfyOn72ieKYxbCraXxNonCfH8CargS4xDIbOGGW6BPBCPEc1RYyNyZWZgXXX && sync && checkpoint 1 && sleep 2 && echo -n helo > /mnt/eizAKifFfyOn72ieKYxbCraXxNonCfH8CargS4xDIbOGGW6BPBCPEc1RYyNyZWZgXXX && sync && checkpoint 2"
checkpoint_range: [0, 2]
dump_cmd_suffix: "echo test >> /mnt/eizAKifFfyOn72ieKYxbCraXxNonCfH8CargS4xDIbOGGW6BPBCPEc1RYyNyZWZgXXX && rm -r /mnt/*"
3 changes: 3 additions & 0 deletions fs-testing/tests/test_touch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trace_cmd_suffix: "checkpoint 0 && touch /mnt/myfile && sync && checkpoint 1 && sleep 2 && touch /mnt/myfile && sync && checkpoint 2"
checkpoint_range: [0, 2]
dump_cmd_suffix: "echo test >> /mnt/myfile && rm -r /mnt/*"
3 changes: 3 additions & 0 deletions fs-testing/tests/test_unlink.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trace_cmd_suffix: "checkpoint 0 && echo -n test > /mnt/myfile && sync && checkpoint 1 && rm /mnt/myfile && sync && checkpoint 2"
checkpoint_range: [0, 2]
dump_cmd_suffix: "echo test > /mnt/myfile && rm -r /mnt/*"
4 changes: 4 additions & 0 deletions fs-testing/tests/test_update-middle.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# update part of file
trace_cmd_suffix: 'checkpoint 0 && for i in `seq 71` ; do printf MjOf1E3x18E3R5EP6hq7WjzALMtjsAXY ; done > /mnt/myfile && sync && checkpoint 1 && echo -n hohoho | dd of=/mnt/myfile seek=171 bs=6 conv=notrunc && checkpoint 2 && sync && checkpoint 3'
checkpoint_range: [0, 3]
dump_cmd_suffix: "echo test >> /mnt/myfile && rm -r /mnt/*"
12 changes: 12 additions & 0 deletions fs-testing/vms/vm_zilpmem.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
fs_type: "hybrid"
pmem_start: 536870912 # 512 * 2**20
pmem_len: 134217728 # 128 * 2**20
qemu_path: "qemu/build/qemu-system-x86_64"
kernel_path: "fs-testing/zil-pmem/linux_build/arch/x86/boot/bzImage"
initrd_path: "fs-testing/initramfs/initramfs_zilpmem.cpio.gz"
qemu_args: [ "-m", "1G", "-append", "console=ttyS0,115200n8 memmap=128M!512M" ]
trace_cmd_prefix: 'echo 1 > /proc/sys/kernel/printk && echo 1 > /sys/module/zfs/parameters/zfs_zil_pmem_prb_ncommitters && echo 2 > /sys/module/zfs/parameters/zil_default_kind && zpool create -O mountpoint=legacy testpool /dev/nvme0n1 log dax:/dev/pmem0 && mount -t zfs -o sync=always testpool /mnt'
# Recovery: Import pool read-write to allow replay, then mount dataset read-only.
recovery_cmd: 'echo 1 > /proc/sys/kernel/printk && echo 1 > /sys/module/zfs/parameters/zfs_zil_pmem_prb_ncommitters && zpool import testpool && mount -t zfs -oro testpool /mnt && ls -lah /mnt && fs-dump --contents /mnt > /dev/null'
# reduce linux console level to prevent garbage in the output
dump_cmd_prefix: 'echo 1 > /proc/sys/kernel/printk && echo 1 > /sys/module/zfs/parameters/zfs_zil_pmem_prb_ncommitters && zpool import testpool && mount -t zfs -oro testpool /mnt && fs-dump --contents /mnt && umount /mnt && mount -t zfs testpool /mnt'
34 changes: 34 additions & 0 deletions fs-testing/zil-pmem/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Analyzing ZIL-PMEM

https://github.com/openzfs/zfs/pull/12731

## Building

Clone ZFS (with ZIL-PMEM) into openzfs/ and a suitable Linux version into linux/ (e.g., 5.11).

```sh
# upstream
git clone -b zil-pmem/upstreaming https://github.com/problame/zfs/ openzfs
# with some patches (e.g., smaller chunk size) to ease analysis
git clone -b zil-pmem/vinter https://github.com/lluchs/zfs/ openzfs
```

Build the kernel:

```sh
./build-zfs-builtin.sh
# builds linux_build/arch/x86/boot/bzImage
```

Build the ZFS tools:

```sh
./build-zfs-tools.sh
# builds initramfs_zfs/
```

Build the initramfs:

```sh
make -C../initramfs initramfs_zilpmem.cpio.gz
```
Loading

0 comments on commit 63dc6fe

Please sign in to comment.