]> Git Repo - linux.git/blobdiff - fs/erofs/decompressor_zstd.c
erofs: tidy up stream decompressors
[linux.git] / fs / erofs / decompressor_zstd.c
index 49415bc40d7ce78daf3df67ddbb12ac453206e22..7e177304967e19ecd1ab009c701200165f9d9246 100644 (file)
@@ -138,27 +138,26 @@ static int z_erofs_load_zstd_config(struct super_block *sb,
 static int z_erofs_zstd_decompress(struct z_erofs_decompress_req *rq,
                                   struct page **pgpl)
 {
-       const unsigned int nrpages_out =
-               PAGE_ALIGN(rq->pageofs_out + rq->outputsize) >> PAGE_SHIFT;
-       const unsigned int nrpages_in =
-               PAGE_ALIGN(rq->inputsize) >> PAGE_SHIFT;
-       zstd_dstream *stream;
        struct super_block *sb = rq->sb;
-       unsigned int insz, outsz, pofs;
-       struct z_erofs_zstd *strm;
+       struct z_erofs_stream_dctx dctx = {
+               .rq = rq,
+               .inpages = PAGE_ALIGN(rq->inputsize) >> PAGE_SHIFT,
+               .outpages = PAGE_ALIGN(rq->pageofs_out + rq->outputsize)
+                               >> PAGE_SHIFT,
+               .no = -1, .ni = 0,
+       };
        zstd_in_buffer in_buf = { NULL, 0, 0 };
        zstd_out_buffer out_buf = { NULL, 0, 0 };
-       u8 *kin, *kout = NULL;
-       bool bounced = false;
-       int no = -1, ni = 0, j = 0, zerr, err;
+       struct z_erofs_zstd *strm;
+       zstd_dstream *stream;
+       int zerr, err;
 
        /* 1. get the exact compressed size */
-       kin = kmap_local_page(*rq->in);
-       err = z_erofs_fixup_insize(rq, kin + rq->pageofs_in,
-                       min_t(unsigned int, rq->inputsize,
-                             sb->s_blocksize - rq->pageofs_in));
+       dctx.kin = kmap_local_page(*rq->in);
+       err = z_erofs_fixup_insize(rq, dctx.kin + rq->pageofs_in,
+                       min(rq->inputsize, sb->s_blocksize - rq->pageofs_in));
        if (err) {
-               kunmap_local(kin);
+               kunmap_local(dctx.kin);
                return err;
        }
 
@@ -166,109 +165,48 @@ static int z_erofs_zstd_decompress(struct z_erofs_decompress_req *rq,
        strm = z_erofs_isolate_strms(false);
 
        /* 3. multi-call decompress */
-       insz = rq->inputsize;
-       outsz = rq->outputsize;
        stream = zstd_init_dstream(z_erofs_zstd_max_dictsize, strm->wksp, strm->wkspsz);
        if (!stream) {
                err = -EIO;
                goto failed_zinit;
        }
 
-       pofs = rq->pageofs_out;
-       in_buf.size = min_t(u32, insz, PAGE_SIZE - rq->pageofs_in);
-       insz -= in_buf.size;
-       in_buf.src = kin + rq->pageofs_in;
+       rq->fillgaps = true;    /* ZSTD doesn't support NULL output buffer */
+       in_buf.size = min_t(u32, rq->inputsize, PAGE_SIZE - rq->pageofs_in);
+       rq->inputsize -= in_buf.size;
+       in_buf.src = dctx.kin + rq->pageofs_in;
+       dctx.bounce = strm->bounce;
+
        do {
-               if (out_buf.size == out_buf.pos) {
-                       if (++no >= nrpages_out || !outsz) {
-                               erofs_err(sb, "insufficient space for decompressed data");
-                               err = -EFSCORRUPTED;
-                               break;
-                       }
+               dctx.avail_out = out_buf.size - out_buf.pos;
+               dctx.inbuf_sz = in_buf.size;
+               dctx.inbuf_pos = in_buf.pos;
+               err = z_erofs_stream_switch_bufs(&dctx, &out_buf.dst,
+                                                (void **)&in_buf.src, pgpl);
+               if (err)
+                       break;
 
-                       if (kout)
-                               kunmap_local(kout);
-                       out_buf.size = min_t(u32, outsz, PAGE_SIZE - pofs);
-                       outsz -= out_buf.size;
-                       if (!rq->out[no]) {
-                               rq->out[no] = erofs_allocpage(pgpl, rq->gfp);
-                               if (!rq->out[no]) {
-                                       kout = NULL;
-                                       err = -ENOMEM;
-                                       break;
-                               }
-                               set_page_private(rq->out[no],
-                                                Z_EROFS_SHORTLIVED_PAGE);
-                       }
-                       kout = kmap_local_page(rq->out[no]);
-                       out_buf.dst = kout + pofs;
+               if (out_buf.size == out_buf.pos) {
+                       out_buf.size = dctx.avail_out;
                        out_buf.pos = 0;
-                       pofs = 0;
                }
+               in_buf.size = dctx.inbuf_sz;
+               in_buf.pos = dctx.inbuf_pos;
 
-               if (in_buf.size == in_buf.pos && insz) {
-                       if (++ni >= nrpages_in) {
-                               erofs_err(sb, "invalid compressed data");
-                               err = -EFSCORRUPTED;
-                               break;
-                       }
-
-                       if (kout) /* unlike kmap(), take care of the orders */
-                               kunmap_local(kout);
-                       kunmap_local(kin);
-                       in_buf.size = min_t(u32, insz, PAGE_SIZE);
-                       insz -= in_buf.size;
-                       kin = kmap_local_page(rq->in[ni]);
-                       in_buf.src = kin;
-                       in_buf.pos = 0;
-                       bounced = false;
-                       if (kout) {
-                               j = (u8 *)out_buf.dst - kout;
-                               kout = kmap_local_page(rq->out[no]);
-                               out_buf.dst = kout + j;
-                       }
-               }
-
-               /*
-                * Handle overlapping: Use bounced buffer if the compressed
-                * data is under processing; Or use short-lived pages from the
-                * on-stack pagepool where pages share among the same request
-                * and not _all_ inplace I/O pages are needed to be doubled.
-                */
-               if (!bounced && rq->out[no] == rq->in[ni]) {
-                       memcpy(strm->bounce, in_buf.src, in_buf.size);
-                       in_buf.src = strm->bounce;
-                       bounced = true;
-               }
-
-               for (j = ni + 1; j < nrpages_in; ++j) {
-                       struct page *tmppage;
-
-                       if (rq->out[no] != rq->in[j])
-                               continue;
-                       tmppage = erofs_allocpage(pgpl, rq->gfp);
-                       if (!tmppage) {
-                               err = -ENOMEM;
-                               goto failed;
-                       }
-                       set_page_private(tmppage, Z_EROFS_SHORTLIVED_PAGE);
-                       copy_highpage(tmppage, rq->in[j]);
-                       rq->in[j] = tmppage;
-               }
                zerr = zstd_decompress_stream(stream, &out_buf, &in_buf);
-               if (zstd_is_error(zerr) || (!zerr && outsz)) {
+               if (zstd_is_error(zerr) || (!zerr && rq->outputsize)) {
                        erofs_err(sb, "failed to decompress in[%u] out[%u]: %s",
                                  rq->inputsize, rq->outputsize,
                                  zerr ? zstd_get_error_name(zerr) : "unexpected end of stream");
                        err = -EFSCORRUPTED;
                        break;
                }
-       } while (outsz || out_buf.pos < out_buf.size);
-failed:
-       if (kout)
-               kunmap_local(kout);
+       } while (rq->outputsize || out_buf.pos < out_buf.size);
+
+       if (dctx.kout)
+               kunmap_local(dctx.kout);
 failed_zinit:
-       kunmap_local(kin);
+       kunmap_local(dctx.kin);
        /* 4. push back ZSTD stream context to the global list */
        spin_lock(&z_erofs_zstd_lock);
        strm->next = z_erofs_zstd_head;
This page took 0.038974 seconds and 4 git commands to generate.