Merge tag 'mfd-next-5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
[linux-2.6-block.git] / fs / f2fs / compress.c
index 13666735f56757c06cfd431ab0fa3bc0d8b62fc7..df7b2d15eacdee0e7ddafa18cffcf51f28cf0948 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/backing-dev.h>
 #include <linux/lzo.h>
 #include <linux/lz4.h>
+#include <linux/zstd.h>
 
 #include "f2fs.h"
 #include "node.h"
@@ -291,6 +292,165 @@ static const struct f2fs_compress_ops f2fs_lz4_ops = {
 };
 #endif
 
+#ifdef CONFIG_F2FS_FS_ZSTD
+#define F2FS_ZSTD_DEFAULT_CLEVEL       1
+
+static int zstd_init_compress_ctx(struct compress_ctx *cc)
+{
+       ZSTD_parameters params;
+       ZSTD_CStream *stream;
+       void *workspace;
+       unsigned int workspace_size;
+
+       params = ZSTD_getParams(F2FS_ZSTD_DEFAULT_CLEVEL, cc->rlen, 0);
+       workspace_size = ZSTD_CStreamWorkspaceBound(params.cParams);
+
+       workspace = f2fs_kvmalloc(F2FS_I_SB(cc->inode),
+                                       workspace_size, GFP_NOFS);
+       if (!workspace)
+               return -ENOMEM;
+
+       stream = ZSTD_initCStream(params, 0, workspace, workspace_size);
+       if (!stream) {
+               printk_ratelimited("%sF2FS-fs (%s): %s ZSTD_initCStream failed\n",
+                               KERN_ERR, F2FS_I_SB(cc->inode)->sb->s_id,
+                               __func__);
+               kvfree(workspace);
+               return -EIO;
+       }
+
+       cc->private = workspace;
+       cc->private2 = stream;
+
+       cc->clen = cc->rlen - PAGE_SIZE - COMPRESS_HEADER_SIZE;
+       return 0;
+}
+
+static void zstd_destroy_compress_ctx(struct compress_ctx *cc)
+{
+       kvfree(cc->private);
+       cc->private = NULL;
+       cc->private2 = NULL;
+}
+
+static int zstd_compress_pages(struct compress_ctx *cc)
+{
+       ZSTD_CStream *stream = cc->private2;
+       ZSTD_inBuffer inbuf;
+       ZSTD_outBuffer outbuf;
+       int src_size = cc->rlen;
+       int dst_size = src_size - PAGE_SIZE - COMPRESS_HEADER_SIZE;
+       int ret;
+
+       inbuf.pos = 0;
+       inbuf.src = cc->rbuf;
+       inbuf.size = src_size;
+
+       outbuf.pos = 0;
+       outbuf.dst = cc->cbuf->cdata;
+       outbuf.size = dst_size;
+
+       ret = ZSTD_compressStream(stream, &outbuf, &inbuf);
+       if (ZSTD_isError(ret)) {
+               printk_ratelimited("%sF2FS-fs (%s): %s ZSTD_compressStream failed, ret: %d\n",
+                               KERN_ERR, F2FS_I_SB(cc->inode)->sb->s_id,
+                               __func__, ZSTD_getErrorCode(ret));
+               return -EIO;
+       }
+
+       ret = ZSTD_endStream(stream, &outbuf);
+       if (ZSTD_isError(ret)) {
+               printk_ratelimited("%sF2FS-fs (%s): %s ZSTD_endStream returned %d\n",
+                               KERN_ERR, F2FS_I_SB(cc->inode)->sb->s_id,
+                               __func__, ZSTD_getErrorCode(ret));
+               return -EIO;
+       }
+
+       cc->clen = outbuf.pos;
+       return 0;
+}
+
+static int zstd_init_decompress_ctx(struct decompress_io_ctx *dic)
+{
+       ZSTD_DStream *stream;
+       void *workspace;
+       unsigned int workspace_size;
+
+       workspace_size = ZSTD_DStreamWorkspaceBound(MAX_COMPRESS_WINDOW_SIZE);
+
+       workspace = f2fs_kvmalloc(F2FS_I_SB(dic->inode),
+                                       workspace_size, GFP_NOFS);
+       if (!workspace)
+               return -ENOMEM;
+
+       stream = ZSTD_initDStream(MAX_COMPRESS_WINDOW_SIZE,
+                                       workspace, workspace_size);
+       if (!stream) {
+               printk_ratelimited("%sF2FS-fs (%s): %s ZSTD_initDStream failed\n",
+                               KERN_ERR, F2FS_I_SB(dic->inode)->sb->s_id,
+                               __func__);
+               kvfree(workspace);
+               return -EIO;
+       }
+
+       dic->private = workspace;
+       dic->private2 = stream;
+
+       return 0;
+}
+
+static void zstd_destroy_decompress_ctx(struct decompress_io_ctx *dic)
+{
+       kvfree(dic->private);
+       dic->private = NULL;
+       dic->private2 = NULL;
+}
+
+static int zstd_decompress_pages(struct decompress_io_ctx *dic)
+{
+       ZSTD_DStream *stream = dic->private2;
+       ZSTD_inBuffer inbuf;
+       ZSTD_outBuffer outbuf;
+       int ret;
+
+       inbuf.pos = 0;
+       inbuf.src = dic->cbuf->cdata;
+       inbuf.size = dic->clen;
+
+       outbuf.pos = 0;
+       outbuf.dst = dic->rbuf;
+       outbuf.size = dic->rlen;
+
+       ret = ZSTD_decompressStream(stream, &outbuf, &inbuf);
+       if (ZSTD_isError(ret)) {
+               printk_ratelimited("%sF2FS-fs (%s): %s ZSTD_compressStream failed, ret: %d\n",
+                               KERN_ERR, F2FS_I_SB(dic->inode)->sb->s_id,
+                               __func__, ZSTD_getErrorCode(ret));
+               return -EIO;
+       }
+
+       if (dic->rlen != outbuf.pos) {
+               printk_ratelimited("%sF2FS-fs (%s): %s ZSTD invalid rlen:%zu, "
+                               "expected:%lu\n", KERN_ERR,
+                               F2FS_I_SB(dic->inode)->sb->s_id,
+                               __func__, dic->rlen,
+                               PAGE_SIZE << dic->log_cluster_size);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static const struct f2fs_compress_ops f2fs_zstd_ops = {
+       .init_compress_ctx      = zstd_init_compress_ctx,
+       .destroy_compress_ctx   = zstd_destroy_compress_ctx,
+       .compress_pages         = zstd_compress_pages,
+       .init_decompress_ctx    = zstd_init_decompress_ctx,
+       .destroy_decompress_ctx = zstd_destroy_decompress_ctx,
+       .decompress_pages       = zstd_decompress_pages,
+};
+#endif
+
 static const struct f2fs_compress_ops *f2fs_cops[COMPRESS_MAX] = {
 #ifdef CONFIG_F2FS_FS_LZO
        &f2fs_lzo_ops,
@@ -302,6 +462,11 @@ static const struct f2fs_compress_ops *f2fs_cops[COMPRESS_MAX] = {
 #else
        NULL,
 #endif
+#ifdef CONFIG_F2FS_FS_ZSTD
+       &f2fs_zstd_ops,
+#else
+       NULL,
+#endif
 };
 
 bool f2fs_is_compress_backend_ready(struct inode *inode)
@@ -1143,20 +1308,16 @@ struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc)
                goto out_free;
 
        for (i = 0; i < dic->cluster_size; i++) {
-               if (cc->rpages[i])
+               if (cc->rpages[i]) {
+                       dic->tpages[i] = cc->rpages[i];
                        continue;
+               }
 
                dic->tpages[i] = f2fs_grab_page();
                if (!dic->tpages[i])
                        goto out_free;
        }
 
-       for (i = 0; i < dic->cluster_size; i++) {
-               if (dic->tpages[i])
-                       continue;
-               dic->tpages[i] = cc->rpages[i];
-       }
-
        return dic;
 
 out_free:
@@ -1172,6 +1333,8 @@ void f2fs_free_dic(struct decompress_io_ctx *dic)
                for (i = 0; i < dic->cluster_size; i++) {
                        if (dic->rpages[i])
                                continue;
+                       if (!dic->tpages[i])
+                               continue;
                        unlock_page(dic->tpages[i]);
                        put_page(dic->tpages[i]);
                }