]> git.ipfire.org Git - thirdparty/zstd.git/commitdiff
Add BtrFS patch for 4.10 kernel
authorNick Terrell <terrelln@fb.com>
Tue, 11 Apr 2017 18:35:36 +0000 (11:35 -0700)
committerNick Terrell <terrelln@fb.com>
Tue, 11 Apr 2017 18:50:24 +0000 (11:50 -0700)
contrib/linux-kernel/README.md
contrib/linux-kernel/btrfs.diff [new file with mode: 0644]
contrib/linux-kernel/fs/btrfs/zstd.c

index 11938ad625277a7613d61a7fee4e0200001378db..1cc74cca6774a29585da0fc498492a04ef8843da 100644 (file)
@@ -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 (file)
index 0000000..b0f8b92
--- /dev/null
@@ -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 <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/init.h>
++#include <linux/err.h>
++#include <linux/sched.h>
++#include <linux/pagemap.h>
++#include <linux/bio.h>
++#include <linux/zstd.h>
++#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
index 23a3692ad34b405b1870a90425fc3c6794abfd1b..b7f319e7a934a5a1c7e2aa1bc05d0dcc42304776 100644 (file)
 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;
-                       }
-               }
+               ret = btrfs_decompress_buf2page(out_buf.dst, buf_start,
+                               total_out, disk_start, orig_bio);
+               if (ret == 0)
+                       break;
 
-               if (in_buf.pos >= srclen) {
+               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,
 };