|
20 | 20 | #include "misc.h"
|
21 | 21 | #include "compression.h"
|
22 | 22 | #include "ctree.h"
|
23 |
| -#include "super.h" |
24 | 23 |
|
25 | 24 | #define ZSTD_BTRFS_MAX_WINDOWLOG 17
|
26 | 25 | #define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG)
|
@@ -619,48 +618,80 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
|
619 | 618 | }
|
620 | 619 |
|
621 | 620 | int zstd_decompress(struct list_head *ws, const u8 *data_in,
|
622 |
| - struct page *dest_page, unsigned long dest_pgoff, size_t srclen, |
| 621 | + struct page *dest_page, unsigned long start_byte, size_t srclen, |
623 | 622 | size_t destlen)
|
624 | 623 | {
|
625 | 624 | struct workspace *workspace = list_entry(ws, struct workspace, list);
|
626 |
| - struct btrfs_fs_info *fs_info = btrfs_sb(dest_page->mapping->host->i_sb); |
627 |
| - const u32 sectorsize = fs_info->sectorsize; |
628 | 625 | zstd_dstream *stream;
|
629 | 626 | int ret = 0;
|
630 |
| - unsigned long to_copy = 0; |
| 627 | + size_t ret2; |
| 628 | + unsigned long total_out = 0; |
| 629 | + unsigned long pg_offset = 0; |
631 | 630 |
|
632 | 631 | stream = zstd_init_dstream(
|
633 | 632 | ZSTD_BTRFS_MAX_INPUT, workspace->mem, workspace->size);
|
634 | 633 | if (!stream) {
|
635 | 634 | pr_warn("BTRFS: zstd_init_dstream failed\n");
|
| 635 | + ret = -EIO; |
636 | 636 | goto finish;
|
637 | 637 | }
|
638 | 638 |
|
| 639 | + destlen = min_t(size_t, destlen, PAGE_SIZE); |
| 640 | + |
639 | 641 | workspace->in_buf.src = data_in;
|
640 | 642 | workspace->in_buf.pos = 0;
|
641 | 643 | workspace->in_buf.size = srclen;
|
642 | 644 |
|
643 | 645 | workspace->out_buf.dst = workspace->buf;
|
644 | 646 | workspace->out_buf.pos = 0;
|
645 |
| - workspace->out_buf.size = sectorsize; |
646 |
| - |
647 |
| - /* |
648 |
| - * Since both input and output buffers should not exceed one sector, |
649 |
| - * one call should end the decompression. |
650 |
| - */ |
651 |
| - ret = zstd_decompress_stream(stream, &workspace->out_buf, &workspace->in_buf); |
652 |
| - if (zstd_is_error(ret)) { |
653 |
| - pr_warn_ratelimited("BTRFS: zstd_decompress_stream return %d\n", |
654 |
| - zstd_get_error_code(ret)); |
655 |
| - goto finish; |
| 647 | + workspace->out_buf.size = PAGE_SIZE; |
| 648 | + |
| 649 | + ret2 = 1; |
| 650 | + while (pg_offset < destlen |
| 651 | + && workspace->in_buf.pos < workspace->in_buf.size) { |
| 652 | + unsigned long buf_start; |
| 653 | + unsigned long buf_offset; |
| 654 | + unsigned long bytes; |
| 655 | + |
| 656 | + /* Check if the frame is over and we still need more input */ |
| 657 | + if (ret2 == 0) { |
| 658 | + pr_debug("BTRFS: zstd_decompress_stream ended early\n"); |
| 659 | + ret = -EIO; |
| 660 | + goto finish; |
| 661 | + } |
| 662 | + ret2 = zstd_decompress_stream(stream, &workspace->out_buf, |
| 663 | + &workspace->in_buf); |
| 664 | + if (zstd_is_error(ret2)) { |
| 665 | + pr_debug("BTRFS: zstd_decompress_stream returned %d\n", |
| 666 | + zstd_get_error_code(ret2)); |
| 667 | + ret = -EIO; |
| 668 | + goto finish; |
| 669 | + } |
| 670 | + |
| 671 | + buf_start = total_out; |
| 672 | + total_out += workspace->out_buf.pos; |
| 673 | + workspace->out_buf.pos = 0; |
| 674 | + |
| 675 | + if (total_out <= start_byte) |
| 676 | + continue; |
| 677 | + |
| 678 | + if (total_out > start_byte && buf_start < start_byte) |
| 679 | + buf_offset = start_byte - buf_start; |
| 680 | + else |
| 681 | + buf_offset = 0; |
| 682 | + |
| 683 | + bytes = min_t(unsigned long, destlen - pg_offset, |
| 684 | + workspace->out_buf.size - buf_offset); |
| 685 | + |
| 686 | + memcpy_to_page(dest_page, pg_offset, |
| 687 | + workspace->out_buf.dst + buf_offset, bytes); |
| 688 | + |
| 689 | + pg_offset += bytes; |
656 | 690 | }
|
657 |
| - to_copy = workspace->out_buf.pos; |
658 |
| - memcpy_to_page(dest_page, dest_pgoff + to_copy, workspace->out_buf.dst, to_copy); |
| 691 | + ret = 0; |
659 | 692 | finish:
|
660 |
| - /* Error or early end. */ |
661 |
| - if (unlikely(to_copy < destlen)) { |
662 |
| - ret = -EIO; |
663 |
| - memzero_page(dest_page, dest_pgoff + to_copy, destlen - to_copy); |
| 693 | + if (pg_offset < destlen) { |
| 694 | + memzero_page(dest_page, pg_offset, destlen - pg_offset); |
664 | 695 | }
|
665 | 696 | return ret;
|
666 | 697 | }
|
|
0 commit comments