From b4dd3378f10b7ba83077826129f59805176ef728 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 11 Apr 2017 11:35:36 -0700 Subject: [PATCH] Add BtrFS patch for 4.10 kernel --- contrib/linux-kernel/README.md | 10 +- contrib/linux-kernel/btrfs.diff | 633 +++++++++++++++++++++++++++ contrib/linux-kernel/fs/btrfs/zstd.c | 129 +++--- 3 files changed, 703 insertions(+), 69 deletions(-) create mode 100644 contrib/linux-kernel/btrfs.diff diff --git a/contrib/linux-kernel/README.md b/contrib/linux-kernel/README.md index 11938ad62..1cc74cca6 100644 --- a/contrib/linux-kernel/README.md +++ b/contrib/linux-kernel/README.md @@ -1,8 +1,7 @@ # Linux Kernel Patch There are two pieces, the `zstd_compress` and `zstd_decompress` kernel modules, and the BtrFS patch. -The patches are based off of the linux kernel version 4.9. -The BtrFS patch is not present in its entirety yet. +The patches are based off of the linux kernel master branch (version 4.10). ## Zstd Kernel modules @@ -21,6 +20,7 @@ The BtrFS patch is not present in its entirety yet. ## BtrFS -* `fs/btrfs/zstd.c` is provided. -* Some more glue is required to integrate it with BtrFS, but I haven't included the patches yet. - In the meantime see https://github.com/terrelln/linux/commit/1914f7d4ca6c539369c84853eafa4ac104883047 if you're interested. +* The patch is located in `btrfs.diff`. +* Additionally `fs/btrfs/zstd.c` is provided as a source for convenience. +* The patch seems to be working, it doesn't crash the kernel, and compresses at speeds and ratios athat are expected. + It can still use some more testing for fringe features, like printing options. diff --git a/contrib/linux-kernel/btrfs.diff b/contrib/linux-kernel/btrfs.diff new file mode 100644 index 000000000..b0f8b924b --- /dev/null +++ b/contrib/linux-kernel/btrfs.diff @@ -0,0 +1,633 @@ +diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig +index 80e9c18..a26c63b 100644 +--- a/fs/btrfs/Kconfig ++++ b/fs/btrfs/Kconfig +@@ -6,6 +6,8 @@ config BTRFS_FS + select ZLIB_DEFLATE + select LZO_COMPRESS + select LZO_DECOMPRESS ++ select ZSTD_COMPRESS ++ select ZSTD_DECOMPRESS + select RAID6_PQ + select XOR_BLOCKS + select SRCU +diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile +index 128ce17..962a95a 100644 +--- a/fs/btrfs/Makefile ++++ b/fs/btrfs/Makefile +@@ -6,7 +6,7 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \ + transaction.o inode.o file.o tree-defrag.o \ + extent_map.o sysfs.o struct-funcs.o xattr.o ordered-data.o \ + extent_io.o volumes.o async-thread.o ioctl.o locking.o orphan.o \ +- export.o tree-log.o free-space-cache.o zlib.o lzo.o \ ++ export.o tree-log.o free-space-cache.o zlib.o lzo.o zstd.o \ + compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \ + reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \ + uuid-tree.o props.o hash.o free-space-tree.o +diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c +index c7721a6..66d4ced 100644 +--- a/fs/btrfs/compression.c ++++ b/fs/btrfs/compression.c +@@ -761,6 +761,7 @@ static struct { + static const struct btrfs_compress_op * const btrfs_compress_op[] = { + &btrfs_zlib_compress, + &btrfs_lzo_compress, ++ &btrfs_zstd_compress, + }; + + void __init btrfs_init_compress(void) +diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h +index 39ec43a..d99fc21 100644 +--- a/fs/btrfs/compression.h ++++ b/fs/btrfs/compression.h +@@ -60,8 +60,9 @@ enum btrfs_compression_type { + BTRFS_COMPRESS_NONE = 0, + BTRFS_COMPRESS_ZLIB = 1, + BTRFS_COMPRESS_LZO = 2, +- BTRFS_COMPRESS_TYPES = 2, +- BTRFS_COMPRESS_LAST = 3, ++ BTRFS_COMPRESS_ZSTD = 3, ++ BTRFS_COMPRESS_TYPES = 3, ++ BTRFS_COMPRESS_LAST = 4, + }; + + struct btrfs_compress_op { +@@ -92,5 +93,6 @@ struct btrfs_compress_op { + + extern const struct btrfs_compress_op btrfs_zlib_compress; + extern const struct btrfs_compress_op btrfs_lzo_compress; ++extern const struct btrfs_compress_op btrfs_zstd_compress; + + #endif +diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h +index 29b7fc2..878b23b9 100644 +--- a/fs/btrfs/ctree.h ++++ b/fs/btrfs/ctree.h +@@ -270,6 +270,7 @@ struct btrfs_super_block { + BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS | \ + BTRFS_FEATURE_INCOMPAT_BIG_METADATA | \ + BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO | \ ++ BTRFS_FEATURE_INCOMPAT_COMPRESS_ZSTD | \ + BTRFS_FEATURE_INCOMPAT_RAID56 | \ + BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF | \ + BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA | \ +diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c +index 08b74da..0c43e4e 100644 +--- a/fs/btrfs/disk-io.c ++++ b/fs/btrfs/disk-io.c +@@ -2853,6 +2853,8 @@ int open_ctree(struct super_block *sb, + features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF; + if (fs_info->compress_type == BTRFS_COMPRESS_LZO) + features |= BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO; ++ else if (tree_root->fs_info->compress_type == BTRFS_COMPRESS_ZSTD) ++ features |= BTRFS_FEATURE_INCOMPAT_COMPRESS_ZSTD; + + if (features & BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA) + btrfs_info(fs_info, "has skinny extents"); +diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c +index dabfc7a..d8ea727 100644 +--- a/fs/btrfs/ioctl.c ++++ b/fs/btrfs/ioctl.c +@@ -327,8 +327,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) + + if (fs_info->compress_type == BTRFS_COMPRESS_LZO) + comp = "lzo"; +- else ++ else if (fs_info->compress_type == BTRFS_COMPRESS_ZLIB) + comp = "zlib"; ++ else ++ comp = "zstd"; + ret = btrfs_set_prop(inode, "btrfs.compression", + comp, strlen(comp), 0); + if (ret) +@@ -1463,6 +1465,8 @@ int btrfs_defrag_file(struct inode *inode, struct file *file, + + if (range->compress_type == BTRFS_COMPRESS_LZO) { + btrfs_set_fs_incompat(fs_info, COMPRESS_LZO); ++ } else if (range->compress_type == BTRFS_COMPRESS_ZSTD) { ++ btrfs_set_fs_incompat(fs_info, COMPRESS_ZSTD); + } + + ret = defrag_count; +diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c +index d6cb155..162105f 100644 +--- a/fs/btrfs/props.c ++++ b/fs/btrfs/props.c +@@ -383,6 +383,8 @@ static int prop_compression_validate(const char *value, size_t len) + return 0; + else if (!strncmp("zlib", value, len)) + return 0; ++ else if (!strncmp("zstd", value, len)) ++ return 0; + + return -EINVAL; + } +@@ -405,6 +407,8 @@ static int prop_compression_apply(struct inode *inode, + type = BTRFS_COMPRESS_LZO; + else if (!strncmp("zlib", value, len)) + type = BTRFS_COMPRESS_ZLIB; ++ else if (!strncmp("zstd", value, len)) ++ type = BTRFS_COMPRESS_ZSTD; + else + return -EINVAL; + +@@ -422,6 +426,8 @@ static const char *prop_compression_extract(struct inode *inode) + return "zlib"; + case BTRFS_COMPRESS_LZO: + return "lzo"; ++ case BTRFS_COMPRESS_ZSTD: ++ return "zstd"; + } + + return NULL; +diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c +index da687dc..b064456 100644 +--- a/fs/btrfs/super.c ++++ b/fs/btrfs/super.c +@@ -513,6 +513,14 @@ int btrfs_parse_options(struct btrfs_fs_info *info, char *options, + btrfs_clear_opt(info->mount_opt, NODATASUM); + btrfs_set_fs_incompat(info, COMPRESS_LZO); + no_compress = 0; ++ } else if (strcmp(args[0].from, "zstd") == 0) { ++ compress_type = "zstd"; ++ info->compress_type = BTRFS_COMPRESS_ZSTD; ++ btrfs_set_opt(info->mount_opt, COMPRESS); ++ btrfs_clear_opt(info->mount_opt, NODATACOW); ++ btrfs_clear_opt(info->mount_opt, NODATASUM); ++ btrfs_set_fs_incompat(info, COMPRESS_ZSTD); ++ no_compress = 0; + } else if (strncmp(args[0].from, "no", 2) == 0) { + compress_type = "no"; + btrfs_clear_opt(info->mount_opt, COMPRESS); +@@ -1230,8 +1238,10 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry) + if (btrfs_test_opt(info, COMPRESS)) { + if (info->compress_type == BTRFS_COMPRESS_ZLIB) + compress_type = "zlib"; +- else ++ else if (info->compress_type == BTRFS_COMPRESS_LZO) + compress_type = "lzo"; ++ else ++ compress_type = "zstd"; + if (btrfs_test_opt(info, FORCE_COMPRESS)) + seq_printf(seq, ",compress-force=%s", compress_type); + else +diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c +index 1f157fb..b0dec90 100644 +--- a/fs/btrfs/sysfs.c ++++ b/fs/btrfs/sysfs.c +@@ -200,6 +200,7 @@ BTRFS_FEAT_ATTR_INCOMPAT(mixed_backref, MIXED_BACKREF); + BTRFS_FEAT_ATTR_INCOMPAT(default_subvol, DEFAULT_SUBVOL); + BTRFS_FEAT_ATTR_INCOMPAT(mixed_groups, MIXED_GROUPS); + BTRFS_FEAT_ATTR_INCOMPAT(compress_lzo, COMPRESS_LZO); ++BTRFS_FEAT_ATTR_INCOMPAT(compress_zstd, COMPRESS_ZSTD); + BTRFS_FEAT_ATTR_INCOMPAT(big_metadata, BIG_METADATA); + BTRFS_FEAT_ATTR_INCOMPAT(extended_iref, EXTENDED_IREF); + BTRFS_FEAT_ATTR_INCOMPAT(raid56, RAID56); +@@ -212,6 +213,7 @@ static struct attribute *btrfs_supported_feature_attrs[] = { + BTRFS_FEAT_ATTR_PTR(default_subvol), + BTRFS_FEAT_ATTR_PTR(mixed_groups), + BTRFS_FEAT_ATTR_PTR(compress_lzo), ++ BTRFS_FEAT_ATTR_PTR(compress_zstd), + BTRFS_FEAT_ATTR_PTR(big_metadata), + BTRFS_FEAT_ATTR_PTR(extended_iref), + BTRFS_FEAT_ATTR_PTR(raid56), +diff --git a/fs/btrfs/zstd.c b/fs/btrfs/zstd.c +new file mode 100644 +index 0000000..b7f319e +--- /dev/null ++++ b/fs/btrfs/zstd.c +@@ -0,0 +1,415 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "compression.h" ++ ++#define ZSTD_BTRFS_MAX_WINDOWLOG 17 ++#define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG) ++ ++static ZSTD_parameters zstd_get_btrfs_parameters(size_t src_len) ++{ ++ ZSTD_parameters params = ZSTD_getParams(3, src_len, 0); ++ ++ if (params.cParams.windowLog > ZSTD_BTRFS_MAX_WINDOWLOG) ++ params.cParams.windowLog = ZSTD_BTRFS_MAX_WINDOWLOG; ++ WARN_ON(src_len > ZSTD_BTRFS_MAX_INPUT); ++ return params; ++} ++ ++struct workspace { ++ void *mem; ++ size_t size; ++ char *buf; ++ struct list_head list; ++}; ++ ++static void zstd_free_workspace(struct list_head *ws) ++{ ++ struct workspace *workspace = list_entry(ws, struct workspace, list); ++ ++ vfree(workspace->mem); ++ kfree(workspace->buf); ++ kfree(workspace); ++} ++ ++static struct list_head *zstd_alloc_workspace(void) ++{ ++ ZSTD_parameters params = ++ zstd_get_btrfs_parameters(ZSTD_BTRFS_MAX_INPUT); ++ struct workspace *workspace; ++ ++ workspace = kzalloc(sizeof(*workspace), GFP_NOFS); ++ if (!workspace) ++ return ERR_PTR(-ENOMEM); ++ ++ workspace->size = max_t(size_t, ++ ZSTD_CStreamWorkspaceBound(params.cParams), ++ ZSTD_DStreamWorkspaceBound(ZSTD_BTRFS_MAX_INPUT)); ++ workspace->mem = vmalloc(workspace->size); ++ workspace->buf = kmalloc(PAGE_SIZE, GFP_NOFS); ++ if (!workspace->mem || !workspace->buf) ++ goto fail; ++ ++ INIT_LIST_HEAD(&workspace->list); ++ ++ return &workspace->list; ++fail: ++ zstd_free_workspace(&workspace->list); ++ return ERR_PTR(-ENOMEM); ++} ++ ++static int zstd_compress_pages(struct list_head *ws, ++ struct address_space *mapping, ++ u64 start, ++ struct page **pages, ++ unsigned long *out_pages, ++ unsigned long *total_in, ++ unsigned long *total_out) ++{ ++ struct workspace *workspace = list_entry(ws, struct workspace, list); ++ ZSTD_CStream *stream; ++ int ret = 0; ++ int nr_pages = 0; ++ struct page *in_page = NULL; /* The current page to read */ ++ struct page *out_page = NULL; /* The current page to write to */ ++ ZSTD_inBuffer in_buf = { NULL, 0, 0 }; ++ ZSTD_outBuffer out_buf = { NULL, 0, 0 }; ++ unsigned long tot_in = 0; ++ unsigned long tot_out = 0; ++ unsigned long len = *total_out; ++ const unsigned long nr_dest_pages = *out_pages; ++ unsigned long max_out = nr_dest_pages * PAGE_SIZE; ++ ZSTD_parameters params = zstd_get_btrfs_parameters(len); ++ ++ *out_pages = 0; ++ *total_out = 0; ++ *total_in = 0; ++ ++ /* Initialize the stream */ ++ stream = ZSTD_createCStream(params, len, workspace->mem, ++ workspace->size); ++ if (!stream) { ++ pr_warn("BTRFS: ZSTD_createStream failed\n"); ++ ret = -EIO; ++ goto out; ++ } ++ ++ /* map in the first page of input data */ ++ in_page = find_get_page(mapping, start >> PAGE_SHIFT); ++ in_buf.src = kmap(in_page); ++ in_buf.pos = 0; ++ in_buf.size = min_t(size_t, len, PAGE_SIZE); ++ ++ ++ /* Allocate and map in the output buffer */ ++ out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); ++ if (out_page == NULL) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ pages[nr_pages++] = out_page; ++ out_buf.dst = kmap(out_page); ++ out_buf.pos = 0; ++ out_buf.size = min_t(size_t, max_out, PAGE_SIZE); ++ ++ while (1) { ++ size_t ret2; ++ ++ ret2 = ZSTD_compressStream(stream, &out_buf, &in_buf); ++ if (ZSTD_isError(ret2)) { ++ pr_debug("BTRFS: ZSTD_compressStream returned %d\n", ++ ZSTD_getErrorCode(ret2)); ++ ret = -EIO; ++ goto out; ++ } ++ ++ /* Check to see if we are making it bigger */ ++ if (tot_in + in_buf.pos > 8192 && ++ tot_in + in_buf.pos < ++ tot_out + out_buf.pos) { ++ ret = -E2BIG; ++ goto out; ++ } ++ ++ /* We've reached the end of our output range */ ++ if (out_buf.pos >= max_out) { ++ tot_out += out_buf.pos; ++ ret = -E2BIG; ++ goto out; ++ } ++ ++ /* Check if we need more output space */ ++ if (out_buf.pos == out_buf.size) { ++ tot_out += PAGE_SIZE; ++ max_out -= PAGE_SIZE; ++ kunmap(out_page); ++ if (nr_pages == nr_dest_pages) { ++ out_page = NULL; ++ ret = -E2BIG; ++ goto out; ++ } ++ out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); ++ if (out_page == NULL) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ pages[nr_pages++] = out_page; ++ out_buf.dst = kmap(out_page); ++ out_buf.pos = 0; ++ out_buf.size = min_t(size_t, max_out, PAGE_SIZE); ++ } ++ ++ /* We've reached the end of the input */ ++ if (in_buf.pos >= len) { ++ tot_in += in_buf.pos; ++ break; ++ } ++ ++ /* Check if we need more input */ ++ if (in_buf.pos == in_buf.size) { ++ tot_in += PAGE_SIZE; ++ kunmap(in_page); ++ put_page(in_page); ++ ++ start += PAGE_SIZE; ++ len -= PAGE_SIZE; ++ in_page = find_get_page(mapping, start >> PAGE_SHIFT); ++ in_buf.src = kmap(in_page); ++ in_buf.pos = 0; ++ in_buf.size = min_t(size_t, len, PAGE_SIZE); ++ } ++ } ++ while (1) { ++ size_t ret2; ++ ++ ret2 = ZSTD_endStream(stream, &out_buf); ++ if (ZSTD_isError(ret2)) { ++ pr_debug("BTRFS: ZSTD_endStream returned %d\n", ++ ZSTD_getErrorCode(ret2)); ++ ret = -EIO; ++ goto out; ++ } ++ if (ret2 == 0) { ++ tot_out += out_buf.pos; ++ break; ++ } ++ if (out_buf.pos >= max_out) { ++ tot_out += out_buf.pos; ++ ret = -E2BIG; ++ goto out; ++ } ++ ++ tot_out += PAGE_SIZE; ++ max_out -= PAGE_SIZE; ++ kunmap(out_page); ++ if (nr_pages == nr_dest_pages) { ++ out_page = NULL; ++ ret = -E2BIG; ++ goto out; ++ } ++ out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); ++ if (out_page == NULL) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ pages[nr_pages++] = out_page; ++ out_buf.dst = kmap(out_page); ++ out_buf.pos = 0; ++ out_buf.size = min_t(size_t, max_out, PAGE_SIZE); ++ } ++ ++ if (tot_out >= tot_in) { ++ ret = -E2BIG; ++ goto out; ++ } ++ ++ ret = 0; ++ *total_in = tot_in; ++ *total_out = tot_out; ++out: ++ *out_pages = nr_pages; ++ /* Cleanup */ ++ if (in_page) { ++ kunmap(in_page); ++ put_page(in_page); ++ } ++ if (out_page) ++ kunmap(out_page); ++ return ret; ++} ++ ++static int zstd_decompress_bio(struct list_head *ws, struct page **pages_in, ++ u64 disk_start, ++ struct bio *orig_bio, ++ size_t srclen) ++{ ++ struct workspace *workspace = list_entry(ws, struct workspace, list); ++ ZSTD_DStream *stream; ++ int ret = 0; ++ unsigned long page_in_index = 0; ++ unsigned long total_pages_in = DIV_ROUND_UP(srclen, PAGE_SIZE); ++ unsigned long buf_start; ++ unsigned long total_out = 0; ++ ZSTD_inBuffer in_buf = { NULL, 0, 0 }; ++ ZSTD_outBuffer out_buf = { NULL, 0, 0 }; ++ ++ stream = ZSTD_createDStream( ++ ZSTD_BTRFS_MAX_INPUT, workspace->mem, workspace->size); ++ if (!stream) { ++ pr_debug("BTRFS: ZSTD_createDStream failed\n"); ++ ret = -EIO; ++ goto done; ++ } ++ ++ in_buf.src = kmap(pages_in[page_in_index]); ++ in_buf.pos = 0; ++ in_buf.size = min_t(size_t, srclen, PAGE_SIZE); ++ ++ out_buf.dst = workspace->buf; ++ out_buf.pos = 0; ++ out_buf.size = PAGE_SIZE; ++ ++ while (1) { ++ size_t ret2; ++ ++ ret2 = ZSTD_decompressStream(stream, &out_buf, &in_buf); ++ if (ZSTD_isError(ret2)) { ++ pr_debug("BTRFS: ZSTD_decompressStream returned %d\n", ++ ZSTD_getErrorCode(ret2)); ++ ret = -EIO; ++ goto done; ++ } ++ buf_start = total_out; ++ total_out += out_buf.pos; ++ out_buf.pos = 0; ++ ++ ret = btrfs_decompress_buf2page(out_buf.dst, buf_start, ++ total_out, disk_start, orig_bio); ++ if (ret == 0) ++ break; ++ ++ if (in_buf.pos >= srclen) ++ break; ++ ++ /* Check if we've hit the end of a frame */ ++ if (ret2 == 0) ++ break; ++ ++ if (in_buf.pos == in_buf.size) { ++ kunmap(pages_in[page_in_index++]); ++ if (page_in_index >= total_pages_in) { ++ in_buf.src = NULL; ++ ret = -EIO; ++ goto done; ++ } ++ srclen -= PAGE_SIZE; ++ in_buf.src = kmap(pages_in[page_in_index]); ++ in_buf.pos = 0; ++ in_buf.size = min_t(size_t, srclen, PAGE_SIZE); ++ } ++ } ++ ret = 0; ++ zero_fill_bio(orig_bio); ++done: ++ if (in_buf.src) ++ kunmap(pages_in[page_in_index]); ++ return ret; ++} ++ ++static int zstd_decompress(struct list_head *ws, unsigned char *data_in, ++ struct page *dest_page, ++ unsigned long start_byte, ++ size_t srclen, size_t destlen) ++{ ++ struct workspace *workspace = list_entry(ws, struct workspace, list); ++ ZSTD_DStream *stream; ++ int ret = 0; ++ size_t ret2; ++ ZSTD_inBuffer in_buf = { NULL, 0, 0 }; ++ ZSTD_outBuffer out_buf = { NULL, 0, 0 }; ++ unsigned long total_out = 0; ++ unsigned long pg_offset = 0; ++ char *kaddr; ++ ++ stream = ZSTD_createDStream( ++ ZSTD_BTRFS_MAX_INPUT, workspace->mem, workspace->size); ++ if (!stream) { ++ pr_warn("BTRFS: ZSTD_createDStream failed\n"); ++ ret = -EIO; ++ goto finish; ++ } ++ ++ destlen = min_t(size_t, destlen, PAGE_SIZE); ++ ++ in_buf.src = data_in; ++ in_buf.pos = 0; ++ in_buf.size = srclen; ++ ++ out_buf.dst = workspace->buf; ++ out_buf.pos = 0; ++ out_buf.size = PAGE_SIZE; ++ ++ ret2 = 1; ++ while (pg_offset < destlen && in_buf.pos < in_buf.size) { ++ unsigned long buf_start; ++ unsigned long buf_offset; ++ unsigned long bytes; ++ ++ /* Check if the frame is over and we still need more input */ ++ if (ret2 == 0) { ++ pr_debug("BTRFS: ZSTD_decompressStream ended early\n"); ++ ret = -EIO; ++ goto finish; ++ } ++ ret2 = ZSTD_decompressStream(stream, &out_buf, &in_buf); ++ if (ZSTD_isError(ret2)) { ++ pr_debug("BTRFS: ZSTD_decompressStream returned %d\n", ++ ZSTD_getErrorCode(ret2)); ++ ret = -EIO; ++ goto finish; ++ } ++ ++ buf_start = total_out; ++ total_out += out_buf.pos; ++ out_buf.pos = 0; ++ ++ if (total_out <= start_byte) ++ continue; ++ ++ if (total_out > start_byte && buf_start < start_byte) ++ buf_offset = start_byte - buf_start; ++ else ++ buf_offset = 0; ++ ++ bytes = min_t(unsigned long, destlen - pg_offset, ++ out_buf.size - buf_offset); ++ ++ kaddr = kmap_atomic(dest_page); ++ memcpy(kaddr + pg_offset, out_buf.dst + buf_offset, bytes); ++ kunmap_atomic(kaddr); ++ ++ pg_offset += bytes; ++ } ++ ret = 0; ++finish: ++ if (pg_offset < destlen) { ++ kaddr = kmap_atomic(dest_page); ++ memset(kaddr + pg_offset, 0, destlen - pg_offset); ++ kunmap_atomic(kaddr); ++ } ++ return ret; ++} ++ ++const struct btrfs_compress_op btrfs_zstd_compress = { ++ .alloc_workspace = zstd_alloc_workspace, ++ .free_workspace = zstd_free_workspace, ++ .compress_pages = zstd_compress_pages, ++ .decompress_bio = zstd_decompress_bio, ++ .decompress = zstd_decompress, ++}; +diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h +index db4c253..f26c34f 100644 +--- a/include/uapi/linux/btrfs.h ++++ b/include/uapi/linux/btrfs.h +@@ -255,13 +255,7 @@ struct btrfs_ioctl_fs_info_args { + #define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1) + #define BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS (1ULL << 2) + #define BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO (1ULL << 3) +-/* +- * some patches floated around with a second compression method +- * lets save that incompat here for when they do get in +- * Note we don't actually support it, we're just reserving the +- * number +- */ +-#define BTRFS_FEATURE_INCOMPAT_COMPRESS_LZOv2 (1ULL << 4) ++#define BTRFS_FEATURE_INCOMPAT_COMPRESS_ZSTD (1ULL << 4) + + /* + * older kernels tried to do bigger metadata blocks, but the diff --git a/contrib/linux-kernel/fs/btrfs/zstd.c b/contrib/linux-kernel/fs/btrfs/zstd.c index 23a3692ad..b7f319e7a 100644 --- a/contrib/linux-kernel/fs/btrfs/zstd.c +++ b/contrib/linux-kernel/fs/btrfs/zstd.c @@ -15,9 +15,10 @@ static ZSTD_parameters zstd_get_btrfs_parameters(size_t src_len) { ZSTD_parameters params = ZSTD_getParams(3, src_len, 0); - BUG_ON(src_len > ZSTD_BTRFS_MAX_INPUT); - BUG_ON(params.cParams.windowLog > ZSTD_BTRFS_MAX_WINDOWLOG); - params.fParams.checksumFlag = 1; + + if (params.cParams.windowLog > ZSTD_BTRFS_MAX_WINDOWLOG) + params.cParams.windowLog = ZSTD_BTRFS_MAX_WINDOWLOG; + WARN_ON(src_len > ZSTD_BTRFS_MAX_INPUT); return params; } @@ -39,17 +40,21 @@ static void zstd_free_workspace(struct list_head *ws) static struct list_head *zstd_alloc_workspace(void) { - ZSTD_parameters params = zstd_get_btrfs_parameters(ZSTD_BTRFS_MAX_INPUT); + ZSTD_parameters params = + zstd_get_btrfs_parameters(ZSTD_BTRFS_MAX_INPUT); struct workspace *workspace; workspace = kzalloc(sizeof(*workspace), GFP_NOFS); - if (!workspace) return ERR_PTR(-ENOMEM); + if (!workspace) + return ERR_PTR(-ENOMEM); - workspace->size = max_t(size_t, ZSTD_CStreamWorkspaceBound(params.cParams), + workspace->size = max_t(size_t, + ZSTD_CStreamWorkspaceBound(params.cParams), ZSTD_DStreamWorkspaceBound(ZSTD_BTRFS_MAX_INPUT)); workspace->mem = vmalloc(workspace->size); workspace->buf = kmalloc(PAGE_SIZE, GFP_NOFS); - if (!workspace->mem || !workspace->buf) goto fail; + if (!workspace->mem || !workspace->buf) + goto fail; INIT_LIST_HEAD(&workspace->list); @@ -61,16 +66,13 @@ fail: static int zstd_compress_pages(struct list_head *ws, struct address_space *mapping, - u64 start, unsigned long len, + u64 start, struct page **pages, - unsigned long nr_dest_pages, unsigned long *out_pages, unsigned long *total_in, - unsigned long *total_out, - unsigned long max_out) + unsigned long *total_out) { struct workspace *workspace = list_entry(ws, struct workspace, list); - ZSTD_parameters params = zstd_get_btrfs_parameters(len); ZSTD_CStream *stream; int ret = 0; int nr_pages = 0; @@ -80,13 +82,18 @@ static int zstd_compress_pages(struct list_head *ws, ZSTD_outBuffer out_buf = { NULL, 0, 0 }; unsigned long tot_in = 0; unsigned long tot_out = 0; + unsigned long len = *total_out; + const unsigned long nr_dest_pages = *out_pages; + unsigned long max_out = nr_dest_pages * PAGE_SIZE; + ZSTD_parameters params = zstd_get_btrfs_parameters(len); *out_pages = 0; *total_out = 0; *total_in = 0; /* Initialize the stream */ - stream = ZSTD_createCStream(params, len, workspace->mem, workspace->size); + stream = ZSTD_createCStream(params, len, workspace->mem, + workspace->size); if (!stream) { pr_warn("BTRFS: ZSTD_createStream failed\n"); ret = -EIO; @@ -112,10 +119,12 @@ static int zstd_compress_pages(struct list_head *ws, out_buf.size = min_t(size_t, max_out, PAGE_SIZE); while (1) { - const size_t rc = ZSTD_compressStream(stream, &out_buf, &in_buf); - if (ZSTD_isError(rc)) { + size_t ret2; + + ret2 = ZSTD_compressStream(stream, &out_buf, &in_buf); + if (ZSTD_isError(ret2)) { pr_debug("BTRFS: ZSTD_compressStream returned %d\n", - ZSTD_getErrorCode(rc)); + ZSTD_getErrorCode(ret2)); ret = -EIO; goto out; } @@ -177,14 +186,16 @@ static int zstd_compress_pages(struct list_head *ws, } } while (1) { - const size_t rc = ZSTD_endStream(stream, &out_buf); - if (ZSTD_isError(rc)) { + size_t ret2; + + ret2 = ZSTD_endStream(stream, &out_buf); + if (ZSTD_isError(ret2)) { pr_debug("BTRFS: ZSTD_endStream returned %d\n", - ZSTD_getErrorCode(rc)); + ZSTD_getErrorCode(ret2)); ret = -EIO; goto out; } - if (rc == 0) { + if (ret2 == 0) { tot_out += out_buf.pos; break; } @@ -228,24 +239,22 @@ out: kunmap(in_page); put_page(in_page); } - if (out_page) { kunmap(out_page); } + if (out_page) + kunmap(out_page); return ret; } -static int zstd_decompress_biovec(struct list_head *ws, struct page **pages_in, +static int zstd_decompress_bio(struct list_head *ws, struct page **pages_in, u64 disk_start, - struct bio_vec *bvec, - int vcnt, + struct bio *orig_bio, size_t srclen) { struct workspace *workspace = list_entry(ws, struct workspace, list); ZSTD_DStream *stream; int ret = 0; unsigned long page_in_index = 0; - unsigned long page_out_index = 0; unsigned long total_pages_in = DIV_ROUND_UP(srclen, PAGE_SIZE); unsigned long buf_start; - unsigned long pg_offset; unsigned long total_out = 0; ZSTD_inBuffer in_buf = { NULL, 0, 0 }; ZSTD_outBuffer out_buf = { NULL, 0, 0 }; @@ -266,13 +275,13 @@ static int zstd_decompress_biovec(struct list_head *ws, struct page **pages_in, out_buf.pos = 0; out_buf.size = PAGE_SIZE; - pg_offset = 0; - while (1) { - const size_t rc = ZSTD_decompressStream(stream, &out_buf, &in_buf); - if (ZSTD_isError(rc)) { + size_t ret2; + + ret2 = ZSTD_decompressStream(stream, &out_buf, &in_buf); + if (ZSTD_isError(ret2)) { pr_debug("BTRFS: ZSTD_decompressStream returned %d\n", - ZSTD_getErrorCode(rc)); + ZSTD_getErrorCode(ret2)); ret = -EIO; goto done; } @@ -280,23 +289,17 @@ static int zstd_decompress_biovec(struct list_head *ws, struct page **pages_in, total_out += out_buf.pos; out_buf.pos = 0; - { - int ret2 = btrfs_decompress_buf2page(out_buf.dst, buf_start, - total_out, disk_start, bvec, vcnt, - &page_out_index, &pg_offset); - if (ret2 == 0) { - break; - } - } - - if (in_buf.pos >= srclen) { + ret = btrfs_decompress_buf2page(out_buf.dst, buf_start, + total_out, disk_start, orig_bio); + if (ret == 0) + break; + + if (in_buf.pos >= srclen) break; - } /* Check if we've hit the end of a frame */ - if (rc == 0) { + if (ret2 == 0) break; - } if (in_buf.pos == in_buf.size) { kunmap(pages_in[page_in_index++]); @@ -311,10 +314,11 @@ static int zstd_decompress_biovec(struct list_head *ws, struct page **pages_in, in_buf.size = min_t(size_t, srclen, PAGE_SIZE); } } - btrfs_clear_biovec_end(bvec, vcnt, page_out_index, pg_offset); ret = 0; + zero_fill_bio(orig_bio); done: - if (in_buf.src) { kunmap(pages_in[page_in_index]); } + if (in_buf.src) + kunmap(pages_in[page_in_index]); return ret; } @@ -326,6 +330,7 @@ static int zstd_decompress(struct list_head *ws, unsigned char *data_in, struct workspace *workspace = list_entry(ws, struct workspace, list); ZSTD_DStream *stream; int ret = 0; + size_t ret2; ZSTD_inBuffer in_buf = { NULL, 0, 0 }; ZSTD_outBuffer out_buf = { NULL, 0, 0 }; unsigned long total_out = 0; @@ -350,41 +355,37 @@ static int zstd_decompress(struct list_head *ws, unsigned char *data_in, out_buf.pos = 0; out_buf.size = PAGE_SIZE; - ret = 1; + ret2 = 1; while (pg_offset < destlen && in_buf.pos < in_buf.size) { unsigned long buf_start; unsigned long buf_offset; unsigned long bytes; /* Check if the frame is over and we still need more input */ - if (ret == 0) { - pr_debug("BTRFS: ZSTD_decompressStream frame ended to early\n"); + if (ret2 == 0) { + pr_debug("BTRFS: ZSTD_decompressStream ended early\n"); ret = -EIO; goto finish; } - { - const size_t rc = ZSTD_decompressStream(stream, &out_buf, &in_buf); - if (ZSTD_isError(rc)) { - pr_debug("BTRFS: ZSTD_decompressStream returned %d\n", - ZSTD_getErrorCode(rc)); - ret = -EIO; - goto finish; - } - ret = rc > 0; + ret2 = ZSTD_decompressStream(stream, &out_buf, &in_buf); + if (ZSTD_isError(ret2)) { + pr_debug("BTRFS: ZSTD_decompressStream returned %d\n", + ZSTD_getErrorCode(ret2)); + ret = -EIO; + goto finish; } + buf_start = total_out; total_out += out_buf.pos; out_buf.pos = 0; - if (total_out <= start_byte) { + if (total_out <= start_byte) continue; - } - if (total_out > start_byte && buf_start < start_byte) { + if (total_out > start_byte && buf_start < start_byte) buf_offset = start_byte - buf_start; - } else { + else buf_offset = 0; - } bytes = min_t(unsigned long, destlen - pg_offset, out_buf.size - buf_offset); @@ -409,6 +410,6 @@ const struct btrfs_compress_op btrfs_zstd_compress = { .alloc_workspace = zstd_alloc_workspace, .free_workspace = zstd_free_workspace, .compress_pages = zstd_compress_pages, - .decompress_biovec = zstd_decompress_biovec, + .decompress_bio = zstd_decompress_bio, .decompress = zstd_decompress, };