|
27 | 27 | #include <limits.h>
|
28 | 28 | #include <stdlib.h>
|
29 | 29 | #include <string.h>
|
| 30 | +#if COMPRESSION_ZSTD |
| 31 | +#include <zstd.h> |
| 32 | +#include <zstd_errors.h> |
| 33 | +#endif |
30 | 34 | #include <zlib.h>
|
31 | 35 | #include "kernel-lib/sizes.h"
|
32 | 36 | #include "kernel-shared/accessors.h"
|
|
50 | 54 | #define ZLIB_BTRFS_DEFAULT_LEVEL 3
|
51 | 55 | #define ZLIB_BTRFS_MAX_LEVEL 9
|
52 | 56 |
|
| 57 | +#define ZSTD_BTRFS_DEFAULT_LEVEL 3 |
| 58 | +#define ZSTD_BTRFS_MAX_LEVEL 15 |
| 59 | + |
53 | 60 | static u32 fs_block_size;
|
54 | 61 |
|
55 | 62 | /*
|
@@ -519,6 +526,97 @@ static ssize_t zlib_compress_extent(bool first_sector, u32 sectorsize,
|
519 | 526 | return -E2BIG;
|
520 | 527 | }
|
521 | 528 |
|
| 529 | +#if COMPRESSION_ZSTD |
| 530 | +/* |
| 531 | + * Returns the size of the compressed data if successful, -E2BIG if it is |
| 532 | + * incompressible, or an error code. |
| 533 | + */ |
| 534 | +static ssize_t zstd_compress_extent(bool first_sector, u32 sectorsize, |
| 535 | + const void *in_buf, size_t in_size, |
| 536 | + void *out_buf) |
| 537 | +{ |
| 538 | + ZSTD_CCtx *zstd_ctx; |
| 539 | + ZSTD_inBuffer input; |
| 540 | + ZSTD_outBuffer output; |
| 541 | + size_t zstd_ret; |
| 542 | + ssize_t ret; |
| 543 | + |
| 544 | + zstd_ctx = ZSTD_createCCtx(); |
| 545 | + if (!zstd_ctx) { |
| 546 | + error_msg(ERROR_MSG_MEMORY, NULL); |
| 547 | + return -ENOMEM; |
| 548 | + } |
| 549 | + |
| 550 | + zstd_ret = ZSTD_CCtx_setParameter(zstd_ctx, ZSTD_c_compressionLevel, |
| 551 | + g_compression_level); |
| 552 | + if (ZSTD_isError(zstd_ret)) { |
| 553 | + error("ZSTD_CCtx_setParameter failed: %s", |
| 554 | + ZSTD_getErrorName(zstd_ret)); |
| 555 | + ret = -EINVAL; |
| 556 | + goto out; |
| 557 | + } |
| 558 | + |
| 559 | + zstd_ret = ZSTD_CCtx_setPledgedSrcSize(zstd_ctx, in_size); |
| 560 | + if (ZSTD_isError(zstd_ret)) { |
| 561 | + error("ZSTD_CCtx_setPledgedSrcSize failed: %s", |
| 562 | + ZSTD_getErrorName(zstd_ret)); |
| 563 | + ret = -EINVAL; |
| 564 | + goto out; |
| 565 | + } |
| 566 | + |
| 567 | + output.dst = out_buf; |
| 568 | + output.size = BTRFS_MAX_COMPRESSED; |
| 569 | + output.pos = 0; |
| 570 | + |
| 571 | + input.src = in_buf; |
| 572 | + input.pos = 0; |
| 573 | + |
| 574 | + /* |
| 575 | + * Try to compress the first sector - if it would be larger, return |
| 576 | + * -E2BIG so that it gets marked as nocompress. |
| 577 | + */ |
| 578 | + if (first_sector) { |
| 579 | + input.size = sectorsize; |
| 580 | + |
| 581 | + zstd_ret = ZSTD_compressStream2(zstd_ctx, &output, &input, |
| 582 | + ZSTD_e_flush); |
| 583 | + |
| 584 | + if (ZSTD_isError(zstd_ret)) { |
| 585 | + error("ZSTD_compressStream2 failed: %s", |
| 586 | + ZSTD_getErrorName(zstd_ret)); |
| 587 | + ret = -EINVAL; |
| 588 | + goto out; |
| 589 | + } |
| 590 | + |
| 591 | + if (zstd_ret != 0 || output.pos > sectorsize) { |
| 592 | + ret = -E2BIG; |
| 593 | + goto out; |
| 594 | + } |
| 595 | + } |
| 596 | + |
| 597 | + input.size = in_size; |
| 598 | + |
| 599 | + zstd_ret = ZSTD_compressStream2(zstd_ctx, &output, &input, ZSTD_e_end); |
| 600 | + |
| 601 | + if (ZSTD_isError(zstd_ret)) { |
| 602 | + error("ZSTD_compressStream2 failed: %s", |
| 603 | + ZSTD_getErrorName(zstd_ret)); |
| 604 | + ret = -EINVAL; |
| 605 | + goto out; |
| 606 | + } |
| 607 | + |
| 608 | + if (zstd_ret == 0 && output.pos <= in_size - sectorsize) |
| 609 | + ret = output.pos; |
| 610 | + else |
| 611 | + ret = -E2BIG; |
| 612 | + |
| 613 | +out: |
| 614 | + ZSTD_freeCCtx(zstd_ctx); |
| 615 | + |
| 616 | + return ret; |
| 617 | +} |
| 618 | +#endif |
| 619 | + |
522 | 620 | /*
|
523 | 621 | * keep our extent size at 1MB max, this makes it easier to work
|
524 | 622 | * inside the tiny block groups created during mkfs
|
@@ -586,6 +684,13 @@ static int add_file_item_extent(struct btrfs_trans_handle *trans,
|
586 | 684 | source->buf, bytes_read,
|
587 | 685 | source->comp_buf);
|
588 | 686 | break;
|
| 687 | +#if COMPRESSION_ZSTD |
| 688 | + case BTRFS_COMPRESS_ZSTD: |
| 689 | + comp_ret = zstd_compress_extent(first_sector, sectorsize, |
| 690 | + source->buf, bytes_read, |
| 691 | + source->comp_buf); |
| 692 | + break; |
| 693 | +#endif |
589 | 694 | default:
|
590 | 695 | comp_ret = -EINVAL;
|
591 | 696 | break;
|
@@ -629,12 +734,21 @@ static int add_file_item_extent(struct btrfs_trans_handle *trans,
|
629 | 734 | }
|
630 | 735 |
|
631 | 736 | if (do_comp) {
|
| 737 | + u64 features; |
| 738 | + |
632 | 739 | to_write = round_up(comp_ret, sectorsize);
|
633 | 740 | write_buf = source->comp_buf;
|
634 | 741 | memset(write_buf + comp_ret, 0, to_write - comp_ret);
|
635 | 742 |
|
636 | 743 | flags |= BTRFS_INODE_COMPRESS;
|
637 | 744 | btrfs_set_stack_inode_flags(btrfs_inode, flags);
|
| 745 | + |
| 746 | + if (g_compression == BTRFS_COMPRESS_ZSTD) { |
| 747 | + features = btrfs_super_incompat_flags(trans->fs_info->super_copy); |
| 748 | + features |= BTRFS_FEATURE_INCOMPAT_COMPRESS_ZSTD; |
| 749 | + btrfs_set_super_incompat_flags(trans->fs_info->super_copy, |
| 750 | + features); |
| 751 | + } |
638 | 752 | } else {
|
639 | 753 | to_write = round_up(to_read, sectorsize);
|
640 | 754 | write_buf = source->buf;
|
@@ -735,6 +849,84 @@ static ssize_t zlib_compress_inline_extent(char *buf, u64 size, char **comp_buf)
|
735 | 849 | return ret;
|
736 | 850 | }
|
737 | 851 |
|
| 852 | +#if COMPRESSION_ZSTD |
| 853 | +/* |
| 854 | + * Returns the size of the compressed data if successful, -E2BIG if it is |
| 855 | + * incompressible, or an error code. |
| 856 | + */ |
| 857 | +static ssize_t zstd_compress_inline_extent(char *buf, u64 size, char **comp_buf) |
| 858 | +{ |
| 859 | + ZSTD_CCtx *zstd_ctx; |
| 860 | + ZSTD_inBuffer input; |
| 861 | + ZSTD_outBuffer output; |
| 862 | + size_t zstd_ret; |
| 863 | + ssize_t ret; |
| 864 | + char *out = NULL; |
| 865 | + |
| 866 | + zstd_ctx = ZSTD_createCCtx(); |
| 867 | + if (!zstd_ctx) { |
| 868 | + error_msg(ERROR_MSG_MEMORY, NULL); |
| 869 | + return -ENOMEM; |
| 870 | + } |
| 871 | + |
| 872 | + zstd_ret = ZSTD_CCtx_setParameter(zstd_ctx, ZSTD_c_compressionLevel, |
| 873 | + g_compression_level); |
| 874 | + if (ZSTD_isError(zstd_ret)) { |
| 875 | + error("ZSTD_CCtx_setParameter failed: %s", |
| 876 | + ZSTD_getErrorName(zstd_ret)); |
| 877 | + ret = -EINVAL; |
| 878 | + goto out; |
| 879 | + } |
| 880 | + |
| 881 | + zstd_ret = ZSTD_CCtx_setPledgedSrcSize(zstd_ctx, size); |
| 882 | + if (ZSTD_isError(zstd_ret)) { |
| 883 | + error("ZSTD_CCtx_setPledgedSrcSize failed: %s", |
| 884 | + ZSTD_getErrorName(zstd_ret)); |
| 885 | + ret = -EINVAL; |
| 886 | + goto out; |
| 887 | + } |
| 888 | + |
| 889 | + out = malloc(size); |
| 890 | + if (!out) { |
| 891 | + error_msg(ERROR_MSG_MEMORY, NULL); |
| 892 | + ret = -ENOMEM; |
| 893 | + goto out; |
| 894 | + } |
| 895 | + |
| 896 | + output.dst = out; |
| 897 | + output.size = size; |
| 898 | + output.pos = 0; |
| 899 | + |
| 900 | + input.src = buf; |
| 901 | + input.pos = 0; |
| 902 | + input.size = size; |
| 903 | + |
| 904 | + zstd_ret = ZSTD_compressStream2(zstd_ctx, &output, &input, ZSTD_e_end); |
| 905 | + |
| 906 | + if (ZSTD_isError(zstd_ret)) { |
| 907 | + error("ZSTD_compressStream2 failed: %s", |
| 908 | + ZSTD_getErrorName(zstd_ret)); |
| 909 | + ret = -EINVAL; |
| 910 | + goto out; |
| 911 | + } |
| 912 | + |
| 913 | + if (zstd_ret == 0 && output.pos < size) { |
| 914 | + ret = output.pos; |
| 915 | + *comp_buf = out; |
| 916 | + } else { |
| 917 | + ret = -E2BIG; |
| 918 | + } |
| 919 | + |
| 920 | +out: |
| 921 | + if (ret < 0) |
| 922 | + free(out); |
| 923 | + |
| 924 | + ZSTD_freeCCtx(zstd_ctx); |
| 925 | + |
| 926 | + return ret; |
| 927 | +} |
| 928 | +#endif |
| 929 | + |
738 | 930 | static int add_file_items(struct btrfs_trans_handle *trans,
|
739 | 931 | struct btrfs_root *root,
|
740 | 932 | struct btrfs_inode_item *btrfs_inode, u64 objectid,
|
@@ -780,6 +972,12 @@ static int add_file_items(struct btrfs_trans_handle *trans,
|
780 | 972 | ret = zlib_compress_inline_extent(buffer, st->st_size,
|
781 | 973 | &comp_buf);
|
782 | 974 | break;
|
| 975 | +#if COMPRESSION_ZSTD |
| 976 | + case BTRFS_COMPRESS_ZSTD: |
| 977 | + ret = zstd_compress_inline_extent(buffer, st->st_size, |
| 978 | + &comp_buf); |
| 979 | + break; |
| 980 | +#endif |
783 | 981 | default:
|
784 | 982 | ret = -E2BIG;
|
785 | 983 | break;
|
@@ -1260,6 +1458,17 @@ int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir
|
1260 | 1458 | else if (compression_level == 0)
|
1261 | 1459 | compression_level = ZLIB_BTRFS_DEFAULT_LEVEL;
|
1262 | 1460 | break;
|
| 1461 | + case BTRFS_COMPRESS_ZSTD: |
| 1462 | +#if !COMPRESSION_ZSTD |
| 1463 | + error("zstd support not compiled in"); |
| 1464 | + return -EINVAL; |
| 1465 | +#else |
| 1466 | + if (compression_level > ZSTD_BTRFS_MAX_LEVEL) |
| 1467 | + compression_level = ZSTD_BTRFS_MAX_LEVEL; |
| 1468 | + else if (compression_level == 0) |
| 1469 | + compression_level = ZSTD_BTRFS_DEFAULT_LEVEL; |
| 1470 | + break; |
| 1471 | +#endif |
1263 | 1472 | default:
|
1264 | 1473 | error("unsupported compression type");
|
1265 | 1474 | return -EINVAL;
|
|
0 commit comments