parent
489bd7d028
commit
2e18584af3
|
@ -0,0 +1,169 @@
|
||||||
|
# Makefile for cbd
|
||||||
|
|
||||||
|
TOP := $(shell pwd)
|
||||||
|
|
||||||
|
# Tunables
|
||||||
|
VERSION := 1.0
|
||||||
|
WITH_DEBUG ?= true
|
||||||
|
|
||||||
|
# Default rule
|
||||||
|
.PHONY: all
|
||||||
|
all: all-targets
|
||||||
|
|
||||||
|
# Turn off default suffix rules
|
||||||
|
.SUFFIXES:
|
||||||
|
|
||||||
|
# Turn off RCS/SCCS rules
|
||||||
|
%: RCS/%,v
|
||||||
|
%: RCS/%
|
||||||
|
%: %,v
|
||||||
|
%: s.%
|
||||||
|
%: SCCS/s.%
|
||||||
|
|
||||||
|
# Always delete target when a rule fails
|
||||||
|
.DELETE_ON_ERROR:
|
||||||
|
|
||||||
|
# Kernel stuff
|
||||||
|
KVER ?= $(shell uname -r)
|
||||||
|
KDIR ?= /lib/modules/$(KVER)/build
|
||||||
|
|
||||||
|
# Build tools
|
||||||
|
CPP := $(CROSS_COMPILE)cpp
|
||||||
|
CC := $(CROSS_COMPILE)gcc
|
||||||
|
AR := $(CROSS_COMPILE)ar
|
||||||
|
LD := $(CROSS_COMPILE)gcc
|
||||||
|
STRIP := $(CROSS_COMPILE)strip
|
||||||
|
|
||||||
|
# Paths
|
||||||
|
OUT := out
|
||||||
|
OUT_OBJ := $(OUT)/obj
|
||||||
|
OUT_LIB := $(OUT)/lib
|
||||||
|
OUT_BIN := $(OUT)/bin
|
||||||
|
OUT_KMOD := $(OUT)/kmod
|
||||||
|
|
||||||
|
# Install locations
|
||||||
|
PREFIX ?= /usr/local
|
||||||
|
BINDIR ?= $(PREFIX)/bin
|
||||||
|
LIBDIR ?= $(PREFIX)/lib
|
||||||
|
MANDIR ?= $(PREFIX)/share/man
|
||||||
|
KMODDIR ?= /lib/modules/$(KVER)/misc
|
||||||
|
|
||||||
|
# Platform extensions
|
||||||
|
OBJ_EXT := .o
|
||||||
|
LIB_EXT := .a
|
||||||
|
SHLIB_EXT := .so
|
||||||
|
EXE_EXT :=
|
||||||
|
KMOD_EXT := .ko
|
||||||
|
|
||||||
|
# Build flags
|
||||||
|
ifeq ($(WITH_DEBUG),true)
|
||||||
|
DEBUG_CFLAGS := -g
|
||||||
|
DEBUG_LDFLAGS := -g
|
||||||
|
else
|
||||||
|
DEBUG_CFLAGS := -DNDEBUG
|
||||||
|
DEBUG_LDFLAGS :=
|
||||||
|
endif
|
||||||
|
|
||||||
|
CPPFLAGS :=
|
||||||
|
CFLAGS := $(DEBUG_CFLAGS) -Wall -Werror -Iinclude
|
||||||
|
LDFLAGS := $(DEBUG_LDFLAGS)
|
||||||
|
|
||||||
|
# Targets
|
||||||
|
LIB_NAME := libcbd
|
||||||
|
BIN_NAME := cbd
|
||||||
|
KMOD_NAME := dm-compress
|
||||||
|
|
||||||
|
# Sources
|
||||||
|
LIB_SRCS := \
|
||||||
|
check.c \
|
||||||
|
close.c \
|
||||||
|
format.c \
|
||||||
|
open.c \
|
||||||
|
resize.c \
|
||||||
|
stats.c
|
||||||
|
BIN_SRCS := \
|
||||||
|
cbd.c
|
||||||
|
KMOD_SRCS := \
|
||||||
|
dm-compress.c
|
||||||
|
|
||||||
|
# Intermediates
|
||||||
|
LIB_OBJDIR := $(OUT_OBJ)/$(LIB_NAME)
|
||||||
|
LIB_OBJS := $(addprefix $(LIB_OBJDIR)/,$(LIB_SRCS:.c=$(OBJ_EXT)))
|
||||||
|
BIN_OBJDIR := $(OUT_OBJ)/$(BIN_NAME)
|
||||||
|
BIN_OBJS := $(addprefix $(BIN_OBJDIR)/,$(BIN_SRCS:.c=$(OBJ_EXT)))
|
||||||
|
EXE_LIBS := -ldevmapper
|
||||||
|
KMOD_OBJDIR := $(OUT_OBJ)/$(KMOD_NAME)
|
||||||
|
|
||||||
|
# Transforms
|
||||||
|
define copy-file
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
cp $< $@
|
||||||
|
endef
|
||||||
|
define compile-c
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) -MD -MP -MF $(@D)/.$(@F:$(OBJ_EXT)=.d) -c -o $@ $<
|
||||||
|
endef
|
||||||
|
define make-static-lib
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
@rm -f $@
|
||||||
|
$(AR) r $@ $^
|
||||||
|
endef
|
||||||
|
define make-shared-lib
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
$(LD) -shared -o $@ $^
|
||||||
|
endef
|
||||||
|
define link-executable
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
$(LD) $(LDFLAGS) -o $@ $^ $(EXE_LIBS)
|
||||||
|
endef
|
||||||
|
|
||||||
|
# Version
|
||||||
|
ifeq (,$(wildcard .git))
|
||||||
|
GIT_REV := none
|
||||||
|
else
|
||||||
|
GIT_REV := $(shell git rev-parse --short HEAD)
|
||||||
|
ifeq (,$(shell git diff-index --name-only HEAD))
|
||||||
|
GIT_REV := $(GIT_REV)-clean
|
||||||
|
else
|
||||||
|
GIT_REV := $(GIT_REV)-dirty
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
VERSION := $(VERSION)-$(GIT_REV)
|
||||||
|
|
||||||
|
version.h:
|
||||||
|
@echo "/* Autogenerated file. Do not edit. */" > .$@.tmp && \
|
||||||
|
echo "#ifndef VERSION_H" >> .$@.tmp && \
|
||||||
|
echo "#define VERSION_H" >> .$@.tmp && \
|
||||||
|
echo "#endif" >> .$@.tmp && \
|
||||||
|
rm -f $@ && \
|
||||||
|
mv -f .$@.tmp $@
|
||||||
|
|
||||||
|
# Rules
|
||||||
|
$(LIB_OBJS): $(LIB_OBJDIR)/%.o: $(LIB_NAME)/%.c
|
||||||
|
$(compile-c)
|
||||||
|
|
||||||
|
$(OUT_LIB)/$(LIB_NAME)$(LIB_EXT): $(LIB_OBJS)
|
||||||
|
$(make-static-lib)
|
||||||
|
|
||||||
|
$(BIN_OBJS): $(BIN_OBJDIR)/%.o: $(BIN_NAME)/%.c
|
||||||
|
$(compile-c)
|
||||||
|
|
||||||
|
$(OUT_BIN)/$(BIN_NAME)$(EXE_EXT): $(BIN_OBJS) $(OUT_LIB)/$(LIB_NAME)$(LIB_EXT)
|
||||||
|
$(link-executable)
|
||||||
|
|
||||||
|
$(KMOD_NAME)/$(KMOD_NAME)$(KMOD_EXT): $(KMOD_NAME)/$(KMOD_NAME).c
|
||||||
|
make -C $(KDIR) M=$(TOP)/$(KMOD_NAME) modules
|
||||||
|
|
||||||
|
$(OUT_KMOD)/$(KMOD_NAME)$(KMOD_EXT): $(KMOD_NAME)/$(KMOD_NAME)$(KMOD_EXT)
|
||||||
|
$(copy-file)
|
||||||
|
|
||||||
|
.PHONY: all-targets
|
||||||
|
all-targets: \
|
||||||
|
$(OUT_LIB)/$(LIB_NAME)$(LIB_EXT) \
|
||||||
|
$(OUT_BIN)/$(BIN_NAME)$(EXE_EXT) \
|
||||||
|
$(OUT_KMOD)/$(KMOD_NAME)$(KMOD_EXT)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -rf $(OUT)
|
||||||
|
make -C $(KDIR) M=$(TOP)/$(KMOD_NAME) clean
|
|
@ -0,0 +1,53 @@
|
||||||
|
This is a device mapper block compression driver.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
- Sectors are always 512 bytes (kernel constant).
|
||||||
|
- Physical blocks are always 4kb (internal constant).
|
||||||
|
- Logical blocks are variable 4kb .. 1gb.
|
||||||
|
- All integers are little endian.
|
||||||
|
|
||||||
|
Block device layout:
|
||||||
|
- byte[4096] header
|
||||||
|
- byte[4] magic
|
||||||
|
- u16 version_major
|
||||||
|
- u16 version_minor
|
||||||
|
- u64 nr_physical_sectors
|
||||||
|
- u64 nr_logical_sectors [3 * physical_sectors]
|
||||||
|
- u16 physical_blocks_per_logical_block_shift (0 .. 18) [4 = 64kb]
|
||||||
|
(logical blocks may not exceed 1gb due to LZ4)
|
||||||
|
- byte[] reserved
|
||||||
|
- byte[] zone ... vector of zone
|
||||||
|
|
||||||
|
Number of logical blocks per zone:
|
||||||
|
nr_lblk = 1 << header.logical_blocks_per_group_shift
|
||||||
|
|
||||||
|
Device size limits:
|
||||||
|
- Min: header plus one zone (header, phys_alloc, log_alloc, data)
|
||||||
|
Arbitrarily limit to 1mb (?)
|
||||||
|
- 2^48 = 256t physical blocks = 1eb
|
||||||
|
|
||||||
|
There are six different lblk_alloc sizes:
|
||||||
|
Struct name Size lblk_size device_pblk_size
|
||||||
|
=========== ==== ========= ================
|
||||||
|
lblk_alloc_16_16 2+2*8 <= 64kb <= 64k
|
||||||
|
lblk_alloc_16_32 2+4*8 <= 64kb <= 4g
|
||||||
|
lblk_alloc_16_48 2+6*8 <= 64kb <= 256t
|
||||||
|
lblk_alloc_32_16 4+2*8 > 64kb <= 64k
|
||||||
|
lblk_alloc_32_32 4+4*8 > 64kb <= 4g
|
||||||
|
lblk_alloc_32_48 4+6*8 > 64kb <= 256t
|
||||||
|
|
||||||
|
|
||||||
|
Zone layout:
|
||||||
|
- byte[4k] Physical block alloc bitmap
|
||||||
|
- lblk_alloc_x_y[] Logical block allocation
|
||||||
|
- data[4k*4k*8] Data
|
||||||
|
|
||||||
|
Data size (physical): 4kb*(4k*8) = 128mb
|
||||||
|
Data size (logical) : 128mb*3 = 384mb
|
||||||
|
|
||||||
|
Assume <=4g physical blocks, 64kb logical blocks, 3x compression:
|
||||||
|
- Zone holds 3*128mb = 384mb logical data
|
||||||
|
- 384mb/64kb = 6k logical blocks
|
||||||
|
- 6k * (34b) = 204kb logical block alloc data
|
||||||
|
- 204kb / 4kb = 51 physical blocks for logical block alloc data
|
||||||
|
- Overhead: (1+51)/32k = 0.016%
|
|
@ -0,0 +1,13 @@
|
||||||
|
TODO:
|
||||||
|
- Move back to module based build system.
|
||||||
|
- Make compression algorithm and speed/level selectable.
|
||||||
|
- Create utilities for:
|
||||||
|
- Resizing a compressed device.
|
||||||
|
- Checking and repairing a compressed device.
|
||||||
|
- Check parameters in compress_ctr:
|
||||||
|
- Compressed device must be large enough.
|
||||||
|
- Backing device must be large enough.
|
||||||
|
- Remove workqueue.
|
||||||
|
- (?) Write / flush once per second. What about sync?
|
||||||
|
- (?) Don't cache lblk_alloc.
|
||||||
|
- (?) Function ptrs for reading and writing lblk_alloc.
|
|
@ -0,0 +1,389 @@
|
||||||
|
/*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libcbd.h>
|
||||||
|
|
||||||
|
#include <cbdutil.h>
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse argument into an unsigned 64-bit value.
|
||||||
|
*
|
||||||
|
* The following suffixes are recognized (case insensitive and
|
||||||
|
* always base two, as in LVM):
|
||||||
|
* b bytes
|
||||||
|
* s sectors
|
||||||
|
* k kilo (1024)
|
||||||
|
* m mega (1024^2)
|
||||||
|
* g giga (1024^3)
|
||||||
|
* t tera (1024^4)
|
||||||
|
* p peta (1024^5)
|
||||||
|
*
|
||||||
|
* If no suffix is given, bytes are assumed.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
parse_arg(const char* arg, uint64_t* val)
|
||||||
|
{
|
||||||
|
unsigned long ulval;
|
||||||
|
const char* end;
|
||||||
|
|
||||||
|
ulval = strtoul(arg, (char**)&end, 0);
|
||||||
|
if (ulval == ULONG_MAX || end == arg) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*val = ulval;
|
||||||
|
switch (*end) {
|
||||||
|
case '\0':
|
||||||
|
case 'B':
|
||||||
|
case 'b':
|
||||||
|
/* No conversion */
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
case 's':
|
||||||
|
*val <<= 9;
|
||||||
|
break;
|
||||||
|
case 'K':
|
||||||
|
case 'k':
|
||||||
|
*val <<= 10;
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
case 'm':
|
||||||
|
*val <<= 20;
|
||||||
|
break;
|
||||||
|
case 'G':
|
||||||
|
case 'g':
|
||||||
|
*val <<= 30;
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
case 't':
|
||||||
|
*val <<= 40;
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
case 'p':
|
||||||
|
*val <<= 50;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* progname;
|
||||||
|
|
||||||
|
static void __attribute__((noreturn))
|
||||||
|
usage(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s [-h] [-v] <command> [options] <device>\n"
|
||||||
|
"\n", progname);
|
||||||
|
fprintf(stderr, "Global arguments:\n"
|
||||||
|
" -h --help Show this help message and exit\n"
|
||||||
|
" -v --verbose Increase verbose level\n"
|
||||||
|
"\n");
|
||||||
|
fprintf(stderr, "Commands:\n"
|
||||||
|
" format [opts] <device> Create (format) a compressed device\n"
|
||||||
|
" -S --pysical-size Physical size [device size]\n"
|
||||||
|
" -O --offset Offset in device [0]\n"
|
||||||
|
" -L --logical-blksize Logical block size\n"
|
||||||
|
" -c --compress-factor Compression factor [2.0]\n"
|
||||||
|
" -l --logical-shift Logical block shift [4]\n"
|
||||||
|
" -s --size Logical size\n"
|
||||||
|
" Note:\n"
|
||||||
|
" -c and -s are different ways of specifying the compressed device size.\n"
|
||||||
|
" Only one may be used, not both.\n"
|
||||||
|
" -l and -L are different ways of specifying the logical block size.\n"
|
||||||
|
" Only one may be used, not both.\n"
|
||||||
|
"\n"
|
||||||
|
" open [opts] <device> <name> Open an existing compressed device\n"
|
||||||
|
" create [opts] <device> <name> Alias for open\n"
|
||||||
|
" close [opts] <name> Close an opened compressed device\n"
|
||||||
|
" check [opts] <device> Check and repair a compressed device\n"
|
||||||
|
" -n --assume-no Assume \"no\" no all questions\n"
|
||||||
|
" -y --assume-yes Assume \"yes\" to all questions\n"
|
||||||
|
" resize [opts] <device> Resize an existing compressed device\n"
|
||||||
|
" -S --physical-size New (physical) size [use all]\n"
|
||||||
|
" stats [opts] <name> Show device statistics\n"
|
||||||
|
"\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_format(int argc, char** argv)
|
||||||
|
{
|
||||||
|
static const char short_opts[] = "S:O:L:c:l:s:";
|
||||||
|
static const struct option long_opts[] = {
|
||||||
|
{ "physical-size", 1, NULL, 'S' },
|
||||||
|
{ "offset", 1, NULL, 'O' },
|
||||||
|
{ "logical-blksize", 1, NULL, 'L' },
|
||||||
|
{ "compress-factor", 1, NULL, 'c' },
|
||||||
|
{ "logical-shift", 1, NULL, 'l' },
|
||||||
|
{ "size", 1, NULL, 's' },
|
||||||
|
{ NULL, 0, NULL, 0 }
|
||||||
|
};
|
||||||
|
char opt;
|
||||||
|
uint64_t optval;
|
||||||
|
uint64_t poff = 0;
|
||||||
|
uint64_t psize = 0;
|
||||||
|
uint64_t lsize = 0;
|
||||||
|
uint16_t lshift = 0;
|
||||||
|
|
||||||
|
const char* dev;
|
||||||
|
|
||||||
|
while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'S':
|
||||||
|
if (!parse_arg(optarg, &optval)) {
|
||||||
|
error("Failed to parse \"%s\"\n", optarg);
|
||||||
|
}
|
||||||
|
psize = optval;
|
||||||
|
if (psize % PBLK_SIZE) {
|
||||||
|
error("Size \"%s\" is not a multiple of %u bytes\n", optarg, (uint)PBLK_SIZE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'O':
|
||||||
|
if (!parse_arg(optarg, &optval)) {
|
||||||
|
error("Failed to parse \"%s\"\n", optarg);
|
||||||
|
}
|
||||||
|
poff = optval;
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
if (!parse_arg(optarg, &optval)) {
|
||||||
|
error("Failed to parse \"%s\"\n", optarg);
|
||||||
|
}
|
||||||
|
if ((optval & (optval-1)) ||
|
||||||
|
optval < (1 << (PBLK_SHIFT + LBLK_SHIFT_MIN)) ||
|
||||||
|
optval > (1 << (PBLK_SHIFT + LBLK_SHIFT_MAX))) {
|
||||||
|
error("Size \"%s\" is not a valid logical block size\n", optarg);
|
||||||
|
}
|
||||||
|
lshift = (optval >> PBLK_SHIFT);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
error("Implement me!\n");
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
if (!parse_arg(optarg, &optval)) {
|
||||||
|
error("Failed to parse \"%s\"\n", optarg);
|
||||||
|
}
|
||||||
|
if (optval < LBLK_SHIFT_MIN || optval > LBLK_SHIFT_MAX) {
|
||||||
|
error("Size \"%s\" is not a valid logical block shift\n", optarg);
|
||||||
|
}
|
||||||
|
lshift = optval;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
if (!parse_arg(optarg, &optval)) {
|
||||||
|
error("Failed to parse \"%s\"\n", optarg);
|
||||||
|
}
|
||||||
|
lsize = optval;
|
||||||
|
if (lsize % PBLK_SIZE) {
|
||||||
|
error("Size \"%s\" is not a multiple of %u bytes\n", optarg, (uint)PBLK_SIZE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (argc - optind != 1) {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
dev = argv[optind++];
|
||||||
|
|
||||||
|
cbd_format(dev, poff, psize, lsize, lshift);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_open(int argc, char** argv)
|
||||||
|
{
|
||||||
|
char dev[PATH_MAX];
|
||||||
|
char name[PATH_MAX];
|
||||||
|
|
||||||
|
if (argc != 3) {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
strcpy(dev, argv[1]);
|
||||||
|
strcpy(name, argv[2]);
|
||||||
|
|
||||||
|
if (dev[0] != '/') {
|
||||||
|
sprintf(dev, "/dev/mapper/%s", argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
cbd_open(dev, 0, 0, name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_close(int argc, char** argv)
|
||||||
|
{
|
||||||
|
const char* dev;
|
||||||
|
|
||||||
|
if (argc != 2) {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
dev = argv[1];
|
||||||
|
|
||||||
|
cbd_close(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_check(int argc, char** argv)
|
||||||
|
{
|
||||||
|
static const char short_opts[] = "ny";
|
||||||
|
static const struct option long_opts[] = {
|
||||||
|
{ "assume-no", 0, NULL, 'n' },
|
||||||
|
{ "assume-yes", 0, NULL, 'y' },
|
||||||
|
{ NULL, 0, NULL, 0 }
|
||||||
|
};
|
||||||
|
char opt;
|
||||||
|
tristate_t auto_response = t_none;
|
||||||
|
|
||||||
|
const char* dev;
|
||||||
|
|
||||||
|
while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'y':
|
||||||
|
if (auto_response == t_false) {
|
||||||
|
fprintf(stderr, "Cannot specify both -y and -n\n");
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
auto_response = t_true;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
if (auto_response == t_true) {
|
||||||
|
fprintf(stderr, "Cannot specify both -y and -n\n");
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
auto_response = t_false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (argc - optind != 1) {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
dev = argv[optind++];
|
||||||
|
|
||||||
|
cbd_check(dev, auto_response);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_resize(int argc, char** argv)
|
||||||
|
{
|
||||||
|
static const char short_opts[] = "S:";
|
||||||
|
static const struct option long_opts[] = {
|
||||||
|
{ "physical-size", 1, NULL, 'S' },
|
||||||
|
{ NULL, 0, NULL, 0 }
|
||||||
|
};
|
||||||
|
char opt;
|
||||||
|
uint64_t poff = 0;
|
||||||
|
uint64_t psize = 0;
|
||||||
|
|
||||||
|
const char* dev;
|
||||||
|
|
||||||
|
while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'S':
|
||||||
|
if (!parse_arg(optarg, &psize)) {
|
||||||
|
fprintf(stderr, "Failed to parse \"%s\"\n", optarg);
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
if (psize % PBLK_SIZE) {
|
||||||
|
fprintf(stderr, "Size \"%s\" is not a multiple of %u bytes\n", optarg, (uint)PBLK_SIZE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (argc - optind != 1) {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
dev = argv[optind++];
|
||||||
|
|
||||||
|
cbd_resize(dev, poff, psize / PBLK_SHIFT);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_stats(int argc, char** argv)
|
||||||
|
{
|
||||||
|
const char* dev;
|
||||||
|
|
||||||
|
if (argc != 1) {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
dev = argv[0];
|
||||||
|
|
||||||
|
cbd_stats(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cmd_dispatch
|
||||||
|
{
|
||||||
|
const char* cmd;
|
||||||
|
int (*func)(int, char**);
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct cmd_dispatch dispatch[] =
|
||||||
|
{
|
||||||
|
{ "format", do_format },
|
||||||
|
{ "open", do_open },
|
||||||
|
{ "create", do_open },
|
||||||
|
{ "close", do_close },
|
||||||
|
{ "check", do_check },
|
||||||
|
{ "resize", do_resize },
|
||||||
|
{ "stats", do_stats },
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
static const char short_opts[] = "v";
|
||||||
|
static const struct option long_opts[] = {
|
||||||
|
{ "verbose", 0, NULL, 'v' },
|
||||||
|
{ NULL, 0, NULL, 0 }
|
||||||
|
};
|
||||||
|
char opt;
|
||||||
|
int verbose_level = 0; /* XXX: make global or ...? */
|
||||||
|
|
||||||
|
const char* cmd;
|
||||||
|
uint n;
|
||||||
|
|
||||||
|
progname = argv[0];
|
||||||
|
|
||||||
|
while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'v':
|
||||||
|
++verbose_level;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (argc - optind < 1) {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
cmd = argv[optind];
|
||||||
|
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
optind = 0;
|
||||||
|
for (n = 0; n < ARRAY_SIZE(dispatch); ++n) {
|
||||||
|
if (!strcmp(cmd, dispatch[n].cmd)) {
|
||||||
|
return dispatch[n].func(argc, argv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Unrecognized command \"%s\"\n", cmd);
|
||||||
|
usage();
|
||||||
|
/* NOTREACHED */
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Makefile for dm-compress kernel module
|
||||||
|
|
||||||
|
obj-m += dm-compress.o
|
||||||
|
|
||||||
|
ccflags-y := -I$(M)/../include
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,47 @@
|
||||||
|
#ifndef CBDUTIL_H
|
||||||
|
#define CBDUTIL_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Platform specific macros that affect system headers
|
||||||
|
*/
|
||||||
|
#ifdef linux
|
||||||
|
#ifndef _LARGEFILE64_SOURCE
|
||||||
|
#define _LARGEFILE64_SOURCE
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Include and define all the basic things that would normally be
|
||||||
|
* expected to be available on the current platform.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static inline void __attribute__((noreturn))
|
||||||
|
error(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,66 @@
|
||||||
|
#ifndef CBD_H
|
||||||
|
#define CBD_H
|
||||||
|
|
||||||
|
/* Kernel compatibility */
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <asm/byteorder.h>
|
||||||
|
|
||||||
|
#ifndef BITS_PER_BYTE
|
||||||
|
#define BITS_PER_BYTE 8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef min
|
||||||
|
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef max
|
||||||
|
#define max(a, b) ((a) < (b) ? (b) : (a))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARRAY_SIZE
|
||||||
|
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DIV_ROUND_UP
|
||||||
|
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ROUND_UP
|
||||||
|
#define ROUND_UP(n, d) (DIV_ROUND_UP(n, d) * (n))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef uint8_t u8;
|
||||||
|
typedef uint16_t u16;
|
||||||
|
typedef uint32_t u32;
|
||||||
|
typedef uint64_t u64;
|
||||||
|
|
||||||
|
#include <linux/dm-compress.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define CBD_DEFAULT_COMPRESSION_FACTOR 2.0
|
||||||
|
#define CBD_DEFAULT_LOGICAL_BLOCK_SHIFT 4
|
||||||
|
|
||||||
|
/* XXX: move to types.h */
|
||||||
|
typedef enum {
|
||||||
|
t_false,
|
||||||
|
t_true,
|
||||||
|
t_none
|
||||||
|
} tristate_t;
|
||||||
|
|
||||||
|
int cbd_format(const char* dev,
|
||||||
|
uint64_t poff, uint64_t psize,
|
||||||
|
uint64_t lsize, uint16_t lshift);
|
||||||
|
int cbd_open(const char* dev,
|
||||||
|
uint64_t loff, uint64_t lsize,
|
||||||
|
const char* name);
|
||||||
|
int cbd_close(const char* name);
|
||||||
|
|
||||||
|
int cbd_stats(const char* name);
|
||||||
|
|
||||||
|
int cbd_check(const char* dev, tristate_t auto_response);
|
||||||
|
int cbd_resize(const char* dev,
|
||||||
|
uint64_t poff, uint64_t psize);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,356 @@
|
||||||
|
#ifndef _LINUX_DM_COMPRESS_H
|
||||||
|
#define _LINUX_DM_COMPRESS_H
|
||||||
|
|
||||||
|
#ifndef SECTOR_SHIFT
|
||||||
|
#define SECTOR_SHIFT 9
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PBLK_SHIFT 12
|
||||||
|
#define PBLK_SIZE (1 << PBLK_SHIFT)
|
||||||
|
#define PBLK_SIZE_BITS (PBLK_SIZE * BITS_PER_BYTE)
|
||||||
|
#define PBLK_PER_SECTOR (1 << (PBLK_SHIFT - SECTOR_SHIFT))
|
||||||
|
|
||||||
|
#define LBLK_SHIFT_MIN 1
|
||||||
|
#define LBLK_SHIFT_MAX (30 - PBLK_SHIFT)
|
||||||
|
|
||||||
|
#define CBD_HEADER_BLOCKS 1
|
||||||
|
|
||||||
|
#define CBD_UNCOMPRESSED 1
|
||||||
|
|
||||||
|
static const u8 CBD_MAGIC[] = { 'C', 'B', 'D', '\0' };
|
||||||
|
static const u16 CBD_VERSION_MAJOR = 1;
|
||||||
|
static const u16 CBD_VERSION_MINOR = 1;
|
||||||
|
|
||||||
|
enum cbd_alg {
|
||||||
|
CBD_ALG_NONE,
|
||||||
|
CBD_ALG_LZ4,
|
||||||
|
CBD_ALG_ZLIB,
|
||||||
|
/* lzo, zstd, ... */
|
||||||
|
CBD_ALG_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cbd_params {
|
||||||
|
u8 algorithm; /* cbd_alg */
|
||||||
|
u8 compression; /* 0..9 */
|
||||||
|
u16 lblk_shift;
|
||||||
|
u64 nr_pblk;
|
||||||
|
u32 nr_zones;
|
||||||
|
u32 lblk_per_zone;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cbd_header {
|
||||||
|
u8 magic[4];
|
||||||
|
u16 version_major;
|
||||||
|
u16 version_minor;
|
||||||
|
struct cbd_params params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lblk_alloc
|
||||||
|
{
|
||||||
|
u32 len; /* Compressed length */
|
||||||
|
u64 pblk[1]; /* Vector of physical blocks */
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
get_mem(const u8** raw, u8* buf, size_t len)
|
||||||
|
{
|
||||||
|
memcpy(buf, *raw, len);
|
||||||
|
*raw += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
put_mem(u8** raw, const u8* buf, size_t len)
|
||||||
|
{
|
||||||
|
memcpy(*raw, buf, len);
|
||||||
|
*raw += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8
|
||||||
|
get_byte(const u8** raw)
|
||||||
|
{
|
||||||
|
u8 val = **raw;
|
||||||
|
*raw += sizeof(u8);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
put_byte(u8** raw, u8 val)
|
||||||
|
{
|
||||||
|
**raw = val;
|
||||||
|
*raw += sizeof(u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u16
|
||||||
|
get16_le(const u8** raw)
|
||||||
|
{
|
||||||
|
u16 leval = 0;
|
||||||
|
memcpy(&leval, *raw, sizeof(leval));
|
||||||
|
*raw += sizeof(leval);
|
||||||
|
return __le16_to_cpu(leval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
put16_le(u8** raw, u16 val)
|
||||||
|
{
|
||||||
|
u16 leval = __cpu_to_le16(val);
|
||||||
|
memcpy(*raw, &leval, sizeof(leval));
|
||||||
|
*raw += sizeof(leval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32
|
||||||
|
get32_le(const u8** raw)
|
||||||
|
{
|
||||||
|
u32 leval = 0;
|
||||||
|
memcpy(&leval, *raw, sizeof(leval));
|
||||||
|
*raw += sizeof(leval);
|
||||||
|
return __le32_to_cpu(leval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
put32_le(u8** raw, u32 val)
|
||||||
|
{
|
||||||
|
u32 leval = __cpu_to_le32(val);
|
||||||
|
memcpy(*raw, &leval, sizeof(leval));
|
||||||
|
*raw += sizeof(leval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64
|
||||||
|
get48_le(const u8** raw)
|
||||||
|
{
|
||||||
|
u64 leval = 0;
|
||||||
|
memcpy(&leval, *raw, 6);
|
||||||
|
*raw += 6;
|
||||||
|
return __le64_to_cpu(leval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
put48_le(u8** raw, u64 val)
|
||||||
|
{
|
||||||
|
u64 leval = __cpu_to_le64(val);
|
||||||
|
memcpy(*raw, &leval, 6);
|
||||||
|
*raw += 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64
|
||||||
|
get64_le(const u8** raw)
|
||||||
|
{
|
||||||
|
u64 leval = 0;
|
||||||
|
memcpy(&leval, *raw, sizeof(leval));
|
||||||
|
*raw += sizeof(leval);
|
||||||
|
return __le64_to_cpu(leval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
put64_le(u8** raw, u64 val)
|
||||||
|
{
|
||||||
|
u64 leval = __cpu_to_le64(val);
|
||||||
|
memcpy(*raw, &leval, sizeof(leval));
|
||||||
|
*raw += sizeof(leval);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static inline u32
|
||||||
|
lblk_per_pblk(const struct cbd_params* params)
|
||||||
|
{
|
||||||
|
return (1 << params->lblk_shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32
|
||||||
|
zone_pblk_alloc_len(const struct cbd_params* params)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32
|
||||||
|
zone_lblk_alloc_elem_len(const struct cbd_params* params)
|
||||||
|
{
|
||||||
|
u32 elem_len_bytes = (params->lblk_shift + PBLK_SHIFT > 16) ? 4 : 2;
|
||||||
|
u32 elem_pblk_bytes = (params->nr_pblk <= 0xffff ? 2 :
|
||||||
|
(params->nr_pblk <= 0xffffffff ? 4 : 6));
|
||||||
|
return elem_len_bytes + (1 << params->lblk_shift) * elem_pblk_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32
|
||||||
|
zone_lblk_alloc_len(const struct cbd_params* params)
|
||||||
|
{
|
||||||
|
return DIV_ROUND_UP(params->lblk_per_zone * zone_lblk_alloc_elem_len(params), PBLK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32
|
||||||
|
zone_metadata_len(const struct cbd_params* params)
|
||||||
|
{
|
||||||
|
return zone_pblk_alloc_len(params) +
|
||||||
|
zone_lblk_alloc_len(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32
|
||||||
|
zone_data_len(const struct cbd_params* params)
|
||||||
|
{
|
||||||
|
return zone_pblk_alloc_len(params) * PBLK_SIZE * BITS_PER_BYTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32
|
||||||
|
zone_len(const struct cbd_params* params)
|
||||||
|
{
|
||||||
|
return zone_metadata_len(params) +
|
||||||
|
zone_data_len(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64
|
||||||
|
zone_off(const struct cbd_params* params, u32 idx)
|
||||||
|
{
|
||||||
|
return CBD_HEADER_BLOCKS + idx * zone_len(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64
|
||||||
|
zone_pblk_alloc_off(const struct cbd_params* params, u32 idx)
|
||||||
|
{
|
||||||
|
return zone_off(params, idx) + 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64
|
||||||
|
zone_lblk_alloc_off(const struct cbd_params* params, u32 idx)
|
||||||
|
{
|
||||||
|
return zone_off(params, idx) +
|
||||||
|
zone_pblk_alloc_len(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64
|
||||||
|
zone_data_off(const struct cbd_params* params, u32 idx)
|
||||||
|
{
|
||||||
|
return zone_off(params, idx) +
|
||||||
|
zone_pblk_alloc_len(params) +
|
||||||
|
zone_lblk_alloc_len(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
cbd_header_get(const u8* buf, struct cbd_header* header)
|
||||||
|
{
|
||||||
|
get_mem(&buf, header->magic, sizeof(header->magic));
|
||||||
|
header->version_major = get16_le(&buf);
|
||||||
|
header->version_minor = get16_le(&buf);
|
||||||
|
header->params.algorithm = get_byte(&buf);
|
||||||
|
header->params.compression = get_byte(&buf);
|
||||||
|
header->params.lblk_shift = get16_le(&buf);
|
||||||
|
header->params.nr_pblk = get64_le(&buf);
|
||||||
|
header->params.nr_zones = get32_le(&buf);
|
||||||
|
header->params.lblk_per_zone = get32_le(&buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
cbd_header_put(u8* buf, const struct cbd_header* header)
|
||||||
|
{
|
||||||
|
put_mem(&buf, header->magic, sizeof(header->magic));
|
||||||
|
put16_le(&buf, header->version_major);
|
||||||
|
put16_le(&buf, header->version_minor);
|
||||||
|
put_byte(&buf, header->params.algorithm);
|
||||||
|
put_byte(&buf, header->params.compression);
|
||||||
|
put16_le(&buf, header->params.lblk_shift);
|
||||||
|
put64_le(&buf, header->params.nr_pblk);
|
||||||
|
put32_le(&buf, header->params.nr_zones);
|
||||||
|
put32_le(&buf, header->params.lblk_per_zone);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX:
|
||||||
|
* nr_bits = zone_pblk_alloc_len(params) * PBLK_SIZE;
|
||||||
|
* bit = find_next_zero_bit_le(buf, nr_bits);
|
||||||
|
* if (bit < nr_bits) {
|
||||||
|
* set_bit_le(bit, buf);
|
||||||
|
* }
|
||||||
|
* return bit;
|
||||||
|
*/
|
||||||
|
static inline u32
|
||||||
|
cbd_bitmap_alloc(u8* buf, u32 bitsize)
|
||||||
|
{
|
||||||
|
u32 off = 0;
|
||||||
|
u32 bit = 0;
|
||||||
|
|
||||||
|
for (off = 0; off < bitsize / BITS_PER_BYTE; ++off) {
|
||||||
|
if (buf[off] != 0xff) {
|
||||||
|
while (buf[off] & (1 << bit)) {
|
||||||
|
++bit;
|
||||||
|
}
|
||||||
|
buf[off] |= (1 << bit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return off * BITS_PER_BYTE + bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX:
|
||||||
|
* clear_bit_le(bit, buf);
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
cbd_bitmap_free(u8* buf, u32 idx)
|
||||||
|
{
|
||||||
|
u32 off = idx / BITS_PER_BYTE;
|
||||||
|
u32 bit = idx % BITS_PER_BYTE;
|
||||||
|
|
||||||
|
buf[off] &= ~(1 << bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
cbd_lblk_alloc_get(const struct cbd_params* params,
|
||||||
|
const u8* buf, u32 idx, struct lblk_alloc* alloc)
|
||||||
|
{
|
||||||
|
const u8* raw = buf + idx * zone_lblk_alloc_elem_len(params);
|
||||||
|
u32 n;
|
||||||
|
|
||||||
|
if (params->lblk_shift + PBLK_SHIFT > 16) {
|
||||||
|
alloc->len = get32_le(&raw);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
alloc->len = get16_le(&raw);
|
||||||
|
}
|
||||||
|
if (params->nr_pblk <= 0xffff) {
|
||||||
|
for (n = 0; n < lblk_per_pblk(params); ++n) {
|
||||||
|
alloc->pblk[n] = get16_le(&raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (params->nr_pblk <= 0xffffffff) {
|
||||||
|
for (n = 0; n < lblk_per_pblk(params); ++n) {
|
||||||
|
alloc->pblk[n] = get32_le(&raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (n = 0; n < lblk_per_pblk(params); ++n) {
|
||||||
|
alloc->pblk[n] = get48_le(&raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
cbd_lblk_alloc_put(const struct cbd_params* params,
|
||||||
|
void* buf, u32 idx, const struct lblk_alloc* alloc)
|
||||||
|
{
|
||||||
|
u8* raw = buf + idx * zone_lblk_alloc_elem_len(params);
|
||||||
|
u32 n;
|
||||||
|
|
||||||
|
if (params->lblk_shift + PBLK_SHIFT > 16) {
|
||||||
|
put32_le(&raw, alloc->len);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
put16_le(&raw, alloc->len);
|
||||||
|
}
|
||||||
|
if (params->nr_pblk <= 0xffff) {
|
||||||
|
for (n = 0; n < lblk_per_pblk(params); ++n) {
|
||||||
|
put16_le(&raw, alloc->pblk[n]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (params->nr_pblk <= 0xffffffff) {
|
||||||
|
for (n = 0; n < lblk_per_pblk(params); ++n) {
|
||||||
|
put32_le(&raw, alloc->pblk[n]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (n = 0; n < lblk_per_pblk(params); ++n) {
|
||||||
|
put48_le(&raw, alloc->pblk[n]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _LINUX_DM_COMPRESS_H */
|
|
@ -0,0 +1,186 @@
|
||||||
|
#include <libcbd.h>
|
||||||
|
|
||||||
|
#include <cbdutil.h>
|
||||||
|
|
||||||
|
struct zone_metadata
|
||||||
|
{
|
||||||
|
u8* pblk_alloc;
|
||||||
|
u8* lblk_alloc;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
pblk_read(int fd, size_t pblk, size_t count, u8* data)
|
||||||
|
{
|
||||||
|
off_t off;
|
||||||
|
size_t remain;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
off = lseek(fd, pblk * PBLK_SIZE, SEEK_SET);
|
||||||
|
if (off == (off_t)-1) {
|
||||||
|
error("Failed to seek\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
remain = count * PBLK_SIZE;
|
||||||
|
while (remain) {
|
||||||
|
ret = read(fd, data, remain);
|
||||||
|
if (ret <= 0) {
|
||||||
|
error("Failed to read\n");
|
||||||
|
}
|
||||||
|
remain -= ret;
|
||||||
|
data += ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
check_header(const struct cbd_header* header)
|
||||||
|
{
|
||||||
|
if (memcmp(header->magic, CBD_MAGIC, sizeof(header->magic))) {
|
||||||
|
error("Bad magic\n");
|
||||||
|
}
|
||||||
|
if (header->version_major != CBD_VERSION_MAJOR) {
|
||||||
|
error("Bad major version\n");
|
||||||
|
}
|
||||||
|
if (header->version_minor != CBD_VERSION_MINOR) {
|
||||||
|
error("Bad major version\n");
|
||||||
|
}
|
||||||
|
if (header->params.algorithm == CBD_ALG_NONE ||
|
||||||
|
header->params.algorithm >= CBD_ALG_MAX) {
|
||||||
|
error("Bad algorithm\n");
|
||||||
|
}
|
||||||
|
if (header->params.compression > 9) {
|
||||||
|
error("Bad compression\n");
|
||||||
|
}
|
||||||
|
if (header->params.lblk_shift < LBLK_SHIFT_MIN ||
|
||||||
|
header->params.lblk_shift >= LBLK_SHIFT_MAX) {
|
||||||
|
error("Bad logical block shift\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
check_one_lblk(const struct cbd_params* params,
|
||||||
|
u32 zone,
|
||||||
|
u32 lblk,
|
||||||
|
const struct zone_metadata* zm,
|
||||||
|
u8** pblk_used)
|
||||||
|
{
|
||||||
|
struct lblk_alloc* alloc;
|
||||||
|
u32 n;
|
||||||
|
u64 pblk;
|
||||||
|
u32 rel_pblk;
|
||||||
|
|
||||||
|
alloc = calloc(1, offsetof(struct lblk_alloc, pblk[lblk_per_pblk(params)]));
|
||||||
|
cbd_lblk_alloc_get(params, zm->lblk_alloc, lblk, alloc);
|
||||||
|
printf(" lblk[%u]: len=%u\n", lblk, alloc->len);
|
||||||
|
for (n = 0; n < lblk_per_pblk(params); ++n) {
|
||||||
|
pblk = alloc->pblk[n];
|
||||||
|
if (alloc->len > PBLK_SIZE * n) {
|
||||||
|
/* XXX: allow out-of-zone allocs for v1.1 */
|
||||||
|
if (pblk < zone_data_off(params, zone) || pblk >= zone_off(params, zone + 1)) {
|
||||||
|
printf("Alloc out of bounds for zone %u block %u index %u: %lu\n",
|
||||||
|
(unsigned int)zone, lblk, n,
|
||||||
|
(unsigned long)pblk);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rel_pblk = pblk - zone_data_off(params, zone);
|
||||||
|
printf(" [%u] pblk=%lu rel_pblk=%u\n", n, (unsigned long)pblk, rel_pblk);
|
||||||
|
if (pblk_used[zone][rel_pblk/8] & (1 << (rel_pblk % 8))) {
|
||||||
|
printf("Duplicate allocation for zone %u block %u\n",
|
||||||
|
(unsigned int)zone, (unsigned int)rel_pblk);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pblk_used[zone][rel_pblk/8] |= (1 << (rel_pblk % 8));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (pblk) {
|
||||||
|
printf("Unexpected pblk alloc for zone %u block %u index %u: %lu\n",
|
||||||
|
(unsigned int)zone, lblk, n,
|
||||||
|
(unsigned long)pblk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
check_one_zone(const struct cbd_params* params,
|
||||||
|
u32 zone,
|
||||||
|
const struct zone_metadata* zm,
|
||||||
|
u8** pblk_used)
|
||||||
|
{
|
||||||
|
u32 lblk;
|
||||||
|
|
||||||
|
printf("Zone %u: alloc [%lu .. %lu]\n",
|
||||||
|
(unsigned int)zone,
|
||||||
|
(unsigned long)zone_data_off(params, zone),
|
||||||
|
(unsigned long)zone_off(params, zone + 1));
|
||||||
|
for (lblk = 0; lblk < params->lblk_per_zone; ++lblk) {
|
||||||
|
check_one_lblk(params, zone, lblk, zm, pblk_used);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
check_zone_metadata(const struct cbd_params* params,
|
||||||
|
const struct zone_metadata* zmvec)
|
||||||
|
{
|
||||||
|
u8** pblk_used;
|
||||||
|
u32 zone;
|
||||||
|
|
||||||
|
pblk_used = calloc(params->nr_zones, sizeof(void*));
|
||||||
|
for (zone = 0; zone < params->nr_zones; ++zone) {
|
||||||
|
pblk_used[zone] = calloc(1, zone_pblk_alloc_len(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (zone = 0; zone < params->nr_zones; ++zone) {
|
||||||
|
check_one_zone(params, zone, &zmvec[zone], pblk_used);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (zone = 0; zone < params->nr_zones; ++zone) {
|
||||||
|
free(pblk_used[zone]);
|
||||||
|
}
|
||||||
|
free(pblk_used);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
cbd_check(const char* dev,
|
||||||
|
tristate_t auto_response)
|
||||||
|
{
|
||||||
|
int devfd;
|
||||||
|
struct cbd_header header;
|
||||||
|
uint8_t pblkbuf[PBLK_SIZE];
|
||||||
|
struct zone_metadata* zmvec;
|
||||||
|
u32 zone;
|
||||||
|
|
||||||
|
devfd = open(dev, O_RDONLY);
|
||||||
|
if (devfd < 0) {
|
||||||
|
error("Cannot open device\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
pblk_read(devfd, 0, 1, pblkbuf);
|
||||||
|
cbd_header_get(pblkbuf, &header);
|
||||||
|
check_header(&header);
|
||||||
|
|
||||||
|
zmvec = calloc(header.params.nr_zones, sizeof(struct zone_metadata));
|
||||||
|
for (zone = 0; zone < header.params.nr_zones; ++zone) {
|
||||||
|
zmvec[zone].pblk_alloc = calloc(1, PBLK_SIZE);
|
||||||
|
pblk_read(devfd,
|
||||||
|
zone_pblk_alloc_off(&header.params, zone),
|
||||||
|
zone_pblk_alloc_len(&header.params),
|
||||||
|
zmvec[zone].pblk_alloc);
|
||||||
|
zmvec[zone].lblk_alloc = calloc(zone_lblk_alloc_len(&header.params), PBLK_SIZE);
|
||||||
|
pblk_read(devfd,
|
||||||
|
zone_lblk_alloc_off(&header.params, zone),
|
||||||
|
zone_lblk_alloc_len(&header.params),
|
||||||
|
zmvec[zone].lblk_alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
check_zone_metadata(&header.params, zmvec);
|
||||||
|
|
||||||
|
for (zone = 0; zone < header.params.nr_zones; ++zone) {
|
||||||
|
free(zmvec[zone].lblk_alloc);
|
||||||
|
free(zmvec[zone].pblk_alloc);
|
||||||
|
}
|
||||||
|
free(zmvec);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
#include <libcbd.h>
|
||||||
|
|
||||||
|
#include <libdevmapper.h>
|
||||||
|
|
||||||
|
#include <cbdutil.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
cbd_close(const char* name)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct dm_task* dmt;
|
||||||
|
|
||||||
|
dmt = dm_task_create(DM_DEVICE_REMOVE);
|
||||||
|
ret = dm_task_set_name(dmt, name);
|
||||||
|
if (ret == 0) {
|
||||||
|
error("dm_task_set_name failed\n");
|
||||||
|
}
|
||||||
|
/* ret = dm_task_set_add_node(dmt, DEFAULT_DM_ADD_NODE); */
|
||||||
|
ret = dm_task_run(dmt);
|
||||||
|
dm_task_destroy(dmt);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
#include <libcbd.h>
|
||||||
|
|
||||||
|
#include <cbdutil.h>
|
||||||
|
|
||||||
|
static void
|
||||||
|
pblk_write(int fd, size_t pblk, size_t count, const u8* data)
|
||||||
|
{
|
||||||
|
off_t off;
|
||||||
|
size_t remain;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
off = lseek(fd, pblk * PBLK_SIZE, SEEK_SET);
|
||||||
|
if (off == (off_t)-1) {
|
||||||
|
error("Failed to seek\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
remain = count * PBLK_SIZE;
|
||||||
|
while (remain) {
|
||||||
|
ret = write(fd, data, count * PBLK_SIZE);
|
||||||
|
if (ret <= 0) {
|
||||||
|
error("Failed to write\n");
|
||||||
|
}
|
||||||
|
remain -= ret;
|
||||||
|
data += ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
cbd_format(const char* dev,
|
||||||
|
uint64_t poff, uint64_t psize,
|
||||||
|
uint64_t lsize, uint16_t lshift)
|
||||||
|
{
|
||||||
|
int devfd;
|
||||||
|
struct cbd_header header;
|
||||||
|
uint8_t pblkbuf[PBLK_SIZE];
|
||||||
|
uint64_t pblk;
|
||||||
|
uint64_t zone_idx;
|
||||||
|
|
||||||
|
devfd = open(dev, O_RDWR);
|
||||||
|
if (devfd < 0) {
|
||||||
|
error("Cannot open device\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!psize) {
|
||||||
|
off_t len;
|
||||||
|
len = lseek(devfd, 0, SEEK_END);
|
||||||
|
if (len == (off_t)-1) {
|
||||||
|
error("Cannot seek device\n");
|
||||||
|
}
|
||||||
|
psize = len >> PBLK_SHIFT;
|
||||||
|
}
|
||||||
|
if (!lsize) {
|
||||||
|
lsize = psize * CBD_DEFAULT_COMPRESSION_FACTOR;
|
||||||
|
}
|
||||||
|
if (!lshift) {
|
||||||
|
lshift = CBD_DEFAULT_LOGICAL_BLOCK_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s: paramaters...\n", __func__);
|
||||||
|
printf(" poff=%lu psize=%lu\n", (unsigned long)poff, (unsigned long)psize);
|
||||||
|
printf(" lsize=%lu lshift=%hu\n", (unsigned long)lsize, (unsigned short)lshift);
|
||||||
|
memset(&header, 0, sizeof(header));
|
||||||
|
memcpy(header.magic, CBD_MAGIC, sizeof(header.magic));
|
||||||
|
header.version_major = CBD_VERSION_MAJOR;
|
||||||
|
header.version_minor = CBD_VERSION_MINOR;
|
||||||
|
header.params.algorithm = CBD_ALG_LZ4;
|
||||||
|
header.params.compression = 1;
|
||||||
|
header.params.lblk_shift = lshift;
|
||||||
|
header.params.nr_pblk = psize;
|
||||||
|
/* XXX: Initial estimate */
|
||||||
|
header.params.lblk_per_zone = zone_data_len(&header.params) * (lsize >> lshift) / psize;
|
||||||
|
printf(" initial estimate for lblk_per_zone: %lu\n", (unsigned long)header.params.lblk_per_zone);
|
||||||
|
header.params.nr_zones = (psize - CBD_HEADER_BLOCKS) / zone_len(&header.params);
|
||||||
|
header.params.lblk_per_zone = DIV_ROUND_UP((lsize >> lshift), header.params.nr_zones);
|
||||||
|
printf("%s: parameters...\n", __func__);
|
||||||
|
printf(" lblk_shift=%hu\n", (unsigned short)header.params.lblk_shift);
|
||||||
|
printf(" nr_pblk=%lu\n", (unsigned long)header.params.nr_pblk);
|
||||||
|
printf(" nr_zones=%lu\n", (unsigned long)header.params.nr_zones);
|
||||||
|
printf(" lblk_per_zone=%lu\n", (unsigned long)header.params.lblk_per_zone);
|
||||||
|
|
||||||
|
memset(pblkbuf, 0, sizeof(pblkbuf));
|
||||||
|
cbd_header_put(pblkbuf, &header);
|
||||||
|
|
||||||
|
pblk = poff;
|
||||||
|
pblk_write(devfd, pblk, 1, pblkbuf);
|
||||||
|
pblk += CBD_HEADER_BLOCKS;
|
||||||
|
|
||||||
|
printf("Writing %lu zones starting at %lu\n",
|
||||||
|
(unsigned long)header.params.nr_zones, (unsigned long)pblk);
|
||||||
|
|
||||||
|
memset(pblkbuf, 0, sizeof(pblkbuf));
|
||||||
|
for (zone_idx = 0; zone_idx < header.params.nr_zones; ++zone_idx) {
|
||||||
|
uint32_t count;
|
||||||
|
uint32_t n;
|
||||||
|
pblk = zone_off(&header.params, zone_idx);
|
||||||
|
count = zone_metadata_len(&header.params);
|
||||||
|
printf(" Zone %lu pblk=%lu count=%u\n",
|
||||||
|
(unsigned long)zone_idx, (unsigned long)pblk, (unsigned int)count);
|
||||||
|
for (n = 0; n < count; ++n) {
|
||||||
|
pblk_write(devfd, pblk + n, 1, pblkbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
#include <libcbd.h>
|
||||||
|
|
||||||
|
#include <libdevmapper.h>
|
||||||
|
|
||||||
|
#include <cbdutil.h>
|
||||||
|
|
||||||
|
static uint64_t
|
||||||
|
device_logical_size(const char* dev)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
uint8_t pblkbuf[PBLK_SIZE];
|
||||||
|
struct cbd_header header;
|
||||||
|
|
||||||
|
fd = open(dev, O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
error("Cannot open device\n");
|
||||||
|
}
|
||||||
|
if (read(fd, pblkbuf, sizeof(pblkbuf)) != sizeof(pblkbuf)) {
|
||||||
|
error("Cannot read device\n");
|
||||||
|
}
|
||||||
|
cbd_header_get(pblkbuf, &header);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return (header.params.lblk_per_zone << header.params.lblk_shift) * header.params.nr_zones;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
cbd_open(const char* dev,
|
||||||
|
uint64_t loff, uint64_t lsize,
|
||||||
|
const char* name)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct stat st;
|
||||||
|
struct dm_task* dmt;
|
||||||
|
uint32_t cookie = 0;
|
||||||
|
|
||||||
|
ret = stat(dev, &st);
|
||||||
|
if (ret) {
|
||||||
|
error("Failed to stat device\n");
|
||||||
|
}
|
||||||
|
if (!S_ISBLK(st.st_mode)) {
|
||||||
|
error("Not a block device\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lsize) {
|
||||||
|
lsize = device_logical_size(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
dmt = dm_task_create(DM_DEVICE_CREATE);
|
||||||
|
if (!dmt) {
|
||||||
|
error("dm_task_create failed\n");
|
||||||
|
}
|
||||||
|
ret = dm_task_set_name(dmt, name);
|
||||||
|
if (ret == 0) {
|
||||||
|
error("dm_task_set_name failed\n");
|
||||||
|
}
|
||||||
|
printf("%s: start_sector=%lu num_sectors=%lu\n", __func__,
|
||||||
|
(unsigned long)(loff * PBLK_PER_SECTOR),
|
||||||
|
(unsigned long)(lsize * PBLK_PER_SECTOR));
|
||||||
|
ret = dm_task_add_target(dmt,
|
||||||
|
loff * PBLK_PER_SECTOR,
|
||||||
|
lsize * PBLK_PER_SECTOR,
|
||||||
|
"compress",
|
||||||
|
dev);
|
||||||
|
if (ret == 0) {
|
||||||
|
error("dm_task_add_target failed\n");
|
||||||
|
}
|
||||||
|
ret = dm_task_set_cookie(dmt, &cookie, 0);
|
||||||
|
if (ret == 0) {
|
||||||
|
error("dm_task_set_cookie failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dm_task_run(dmt);
|
||||||
|
if (ret == 0) {
|
||||||
|
error("dm_task_run failed\n");
|
||||||
|
}
|
||||||
|
dm_udev_wait(cookie);
|
||||||
|
|
||||||
|
dm_task_destroy(dmt);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
#include <libcbd.h>
|
||||||
|
|
||||||
|
#include <cbdutil.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
cbd_resize(const char* dev,
|
||||||
|
uint64_t poff, uint64_t psize)
|
||||||
|
{
|
||||||
|
error("Not implemented\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include <libcbd.h>
|
||||||
|
|
||||||
|
#include <cbdutil.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
cbd_stats(const char* name)
|
||||||
|
{
|
||||||
|
error("Not implemented\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue