Skip to content
Merged

V2 #30

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 6 additions & 38 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,38 +1,6 @@
[package]
name = "wbtools"
version = "1.1.0"
authors = ["Akira Hayakawa <ruby.wktk@gmail.com>"]

[dependencies]
crc = "3"
byteorder = "1"
clap = { version = "4", features = ["derive"] }
anyhow = "1"

[[bin]]
name = "wbcheck"
path = "src/wbcheck.rs"

[[bin]]
name = "wbcreate"
path = "src/wbcreate.rs"

[[bin]]
name = "wbdump"
path = "src/wbdump.rs"

[[bin]]
name = "wbmeta"
path = "src/wbmeta.rs"

[[bin]]
name = "wbremove"
path = "src/wbremove.rs"

[[bin]]
name = "wbstatus"
path = "src/wbstatus.rs"

[lib]
name = "lib"
path = "src/lib.rs"
[workspace]
resolver = "2"
members = [
"wbtools",
"writeboost-cli"
]
29 changes: 15 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,54 +11,55 @@ Rustup is the best way to install Rust toolset.

https://github.com/rust-lang-nursery/rustup.rs

Then `cargo install` to install the commands.
Then `cargo install --path writeboost-cli` to install **writeboost-cli**.
(For older version, `cargo install --path wbtools`)

## Video

https://www.youtube.com/watch?v=QAXbmr2Rf30

## Commands (Lexicographical order)

### wbcheck
### check

Check if the log was written successfully.

* `wbcheck /dev/cache 1` checks the segment ID 1. Returns 0 on success.
* `writeboost-cli check /dev/cache 1` checks the segment ID 1. Returns 0 on success.

### wbcreate
### create

Create a WB device (wrapping dmsetup create)

* `wbcreate wbdev /dev/backing /dev/cache --reformat` creates a WB device with reformatting
* `wbcreate wbdev /dev/backing /dev/cache --reformat --read_cache_threshold=4 --writeback_threshold=70`
* `writeboost-cli create wbdev /dev/backing /dev/cache --reformat` creates a WB device with reformatting
* `writeboost-cli create wbdev /dev/backing /dev/cache --reformat --read_cache_threshold=4 --writeback_threshold=70`
creates a WB device with some settings

### wbdump
### dump

Dump the data blocks corresponding to the specified metablock ID.

* `wbdump /dev/cache 126` dumps the data block of metablock index 126
* `writeboost-cli dump /dev/cache 126` dumps the data block of metablock index 126

### wbmeta
### meta

Look into the metadata in the cache device.

* `wbmeta /dev/cache 0` dumps the superblock metadata (ID 0 is special)
* `wbmeta /dev/cache 1` dumps the metadata of segment ID 1
* `writeboost-cli meta /dev/cache 0` dumps the superblock metadata (ID 0 is special)
* `writeboost-cli meta /dev/cache 1` dumps the metadata of segment ID 1

### wbremove

Remove a WB device

* `wbremove wbdev` removes a WB device after flushing data in RAM buffer and then writing back
* `writeboost-cli remove wbdev` removes a WB device after flushing data in RAM buffer and then writing back
all cache blocks. This is the way Dmirty Smirnov's writeboost script suggests. (Recommended)
* `wbremove wbdev --nowriteback` remove a WB device without writing back all cache blocks.
* `writeboost-cli remove wbdev --nowriteback` remove a WB device without writing back all cache blocks.

### wbstatus

Pretty-print the status line.

* `dmsetup status wbdev | wbstatus`
* `dmsetup status wbdev | writeboost-cli status`

## Author

Expand Down
38 changes: 38 additions & 0 deletions wbtools/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[package]
name = "wbtools"
version = "1.1.0"
authors = ["Akira Hayakawa <ruby.wktk@gmail.com>"]

[dependencies]
crc = "3"
byteorder = "1"
clap = { version = "4", features = ["derive"] }
anyhow = "1"

[[bin]]
name = "wbcheck"
path = "src/wbcheck.rs"

[[bin]]
name = "wbcreate"
path = "src/wbcreate.rs"

[[bin]]
name = "wbdump"
path = "src/wbdump.rs"

[[bin]]
name = "wbmeta"
path = "src/wbmeta.rs"

[[bin]]
name = "wbremove"
path = "src/wbremove.rs"

[[bin]]
name = "wbstatus"
path = "src/wbstatus.rs"

[lib]
name = "lib"
path = "src/lib.rs"
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
11 changes: 11 additions & 0 deletions writeboost-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "writeboost-cli"
version = "2.0.0"
authors = ["Akira Hayakawa <ruby.wktk@gmail.com>"]
edition = "2024"

[dependencies]
anyhow = "1"
byteorder = "1"
clap = { version = "4", features = ["derive"] }
crc = "3"
Binary file added writeboost-cli/data/sample.cache.226
Binary file not shown.
1 change: 1 addition & 0 deletions writeboost-cli/data/sample.status.226
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0 204800 writeboost 2 762 6 73 73 67 391 12 224 0 0 15 9 1 0 15 6567 0 0 1 0 15 0 11 10 writeback_threshold 0 nr_cur_batched_writeback 1 sync_data_interval 0 update_sb_record_interval 0 read_cache_threshold 0
1 change: 1 addition & 0 deletions writeboost-cli/data/sample.table.226
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0 204800 writeboost 251:0 251:3
5 changes: 5 additions & 0 deletions writeboost-cli/data/sys_dev.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
MAJOR=252
MINOR=1
DEVNAME=vda1
DEVTYPE=partition
PARTN=1
35 changes: 35 additions & 0 deletions writeboost-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use clap::{Args, Parser, Subcommand};

mod sub;

mod utils;
use utils::*;

#[derive(Subcommand)]
enum Sub {
Check(sub::check::CommandArgs),
Create(sub::create::CommandArgs),
Remove(sub::remove::CommandArgs),
Dump(sub::dump::CommandArgs),
Meta(sub::meta::CommandArgs),
Status(sub::status::CommandArgs),
}

#[derive(Parser)]
struct CommandArgs {
#[clap(subcommand)]
sub: Sub,
}

fn main() {
let args = CommandArgs::parse();

match args.sub {
Sub::Check(args) => sub::check::run(args),
Sub::Create(args) => sub::create::run(args),
Sub::Remove(args) => sub::remove::run(args),
Sub::Dump(args) => sub::dump::run(args),
Sub::Meta(args) => sub::meta::run(args),
Sub::Status(args) => sub::status::run(args),
}
}
67 changes: 67 additions & 0 deletions writeboost-cli/src/sub/check/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use super::*;

use std::fs::File;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;

use crc::{CRC_32_ISCSI, Crc};
pub const CASTAGNOLI: Crc<u32> = Crc::<u32>::new(&CRC_32_ISCSI);
fn checksum(data: &[u8]) -> u32 {
CASTAGNOLI.checksum(data)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_checksum() {
let buf = vec![0; 4096 - 512];
assert_eq!(checksum(&buf), 143703573);
}
}

#[derive(Args)]
#[command(about = "Check if the segment is broken")]
pub struct CommandArgs {
#[arg(help = "Path to the cache device")]
cachedev: String,
#[arg(help = "Segment id")]
segid: i32,
}

pub fn run(args: CommandArgs) {
let devname: String = args.cachedev;
let id = args.segid;
let cache_dev = CacheDevice::new(devname.to_owned());

let mut f = File::open(&devname).expect(&format!("Device {} not found", &devname));

let start_byte: u64 = (cache_dev.calc_segment_start(id) as u64) << 9;
f.seek(SeekFrom::Start(start_byte)).unwrap();

let header = {
let mut buf = vec![0; 512];
f.read(&mut buf).unwrap();
SegmentHeader::from_buf(&buf)
};

if header.uninitialized() {
std::process::exit(0);
}

let computed = {
let size: usize = (4096 - 512) + ((header.length as usize) << 12);
let mut buf = vec![0; size];
f.read(&mut buf).unwrap();
checksum(&buf)
};

if computed != header.checksum {
panic!(
"Checksum is broken. computed={}, expected={}",
computed, header.checksum
);
}
}
104 changes: 104 additions & 0 deletions writeboost-cli/src/sub/create/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use super::*;

use std::process::Command;

#[derive(Args)]
#[command(about = "Create a writeboost device")]
#[command(author, version)]
#[clap(rename_all = "snake_case")]
pub struct CommandArgs {
#[arg(help = "Name of the writeboost device")]
lvname: String,
#[arg(help = "Path to the backing device")]
backingdev: String,
#[arg(help = "Path to the cache device")]
cachedev: String,
#[arg(
help = "Reformat the cache device. This cleans up all existing cache blocks",
long
)]
reformat: bool,
#[arg(long)]
write_around_mode: bool,
#[arg(long, value_name = "INT")]
writeback_threshold: Option<u32>,
#[arg(long, value_name = "INT")]
nr_max_batched_writeback: Option<u32>,
#[arg(long, value_name = "INT")]
update_sb_record_interval: Option<u32>,
#[arg(long, value_name = "INT")]
sync_data_interval: Option<u32>,
#[arg(long, value_name = "INT")]
read_cache_threshold: Option<u32>,
}

pub fn run(args: CommandArgs) {
let wbname = args.lvname;
let backing_dev = {
let name = args.backingdev;
BlockDevice::new(name)
};
let cache_dev_name = args.cachedev;

if args.reformat {
let status = Command::new("dd")
.arg("if=/dev/zero")
.arg(format!("of={}", cache_dev_name))
.arg("bs=512")
.arg("count=1")
.status()
.expect("Failed to zero out the cache device");
assert!(status.success());
}

let mut optionals: Vec<String> = Vec::new();
if args.write_around_mode {
optionals.push("write_around_mode".to_string());
optionals.push("1".to_string());
}
if let Some(v) = args.writeback_threshold {
optionals.push("writeback_threshold".to_string());
optionals.push(v.to_string());
}
if let Some(v) = args.nr_max_batched_writeback {
optionals.push("nr_max_batched_writeback".to_string());
optionals.push(v.to_string());
}
if let Some(v) = args.update_sb_record_interval {
optionals.push("update_sb_record_interval".to_string());
optionals.push(v.to_string());
}
if let Some(v) = args.sync_data_interval {
optionals.push("sync_data_interval".to_string());
optionals.push(v.to_string());
}
if let Some(v) = args.read_cache_threshold {
optionals.push("read_cache_threshold".to_string());
optionals.push(v.to_string());
}

let n = optionals.len();

let optionals_table = if n == 0 {
"".to_string()
} else {
format!(" {} {}", n, optionals.join(" "))
};

let table = format!(
"0 {} writeboost {} {}{}",
backing_dev.size(),
backing_dev.name(),
cache_dev_name,
optionals_table
);

let status = Command::new("dmsetup")
.arg("create")
.arg(wbname)
.arg("--table")
.arg(table)
.status()
.expect("Failed to execute dmsetup create");
assert!(status.success());
}
Loading
Loading