return ret;
 }
 
-static int z_erofs_shifted_transform(struct z_erofs_decompress_req *rq,
-                                    struct page **pagepool)
+static int z_erofs_transform_plain(struct z_erofs_decompress_req *rq,
+                                  struct page **pagepool)
 {
-       const unsigned int nrpages_out =
+       const unsigned int inpages = PAGE_ALIGN(rq->inputsize) >> PAGE_SHIFT;
+       const unsigned int outpages =
                PAGE_ALIGN(rq->pageofs_out + rq->outputsize) >> PAGE_SHIFT;
        const unsigned int righthalf = min_t(unsigned int, rq->outputsize,
                                             PAGE_SIZE - rq->pageofs_out);
        const unsigned int lefthalf = rq->outputsize - righthalf;
+       const unsigned int interlaced_offset =
+               rq->alg == Z_EROFS_COMPRESSION_SHIFTED ? 0 : rq->pageofs_out;
        unsigned char *src, *dst;
 
-       if (nrpages_out > 2) {
+       if (outpages > 2 && rq->alg == Z_EROFS_COMPRESSION_SHIFTED) {
                DBG_BUGON(1);
-               return -EIO;
+               return -EFSCORRUPTED;
        }
 
        if (rq->out[0] == *rq->in) {
-               DBG_BUGON(nrpages_out != 1);
+               DBG_BUGON(rq->pageofs_out);
                return 0;
        }
 
-       src = kmap_atomic(*rq->in) + rq->pageofs_in;
+       src = kmap_local_page(rq->in[inpages - 1]) + rq->pageofs_in;
        if (rq->out[0]) {
-               dst = kmap_atomic(rq->out[0]);
-               memcpy(dst + rq->pageofs_out, src, righthalf);
-               kunmap_atomic(dst);
+               dst = kmap_local_page(rq->out[0]);
+               memcpy(dst + rq->pageofs_out, src + interlaced_offset,
+                      righthalf);
+               kunmap_local(dst);
        }
 
-       if (nrpages_out == 2) {
-               DBG_BUGON(!rq->out[1]);
-               if (rq->out[1] == *rq->in) {
+       if (outpages > inpages) {
+               DBG_BUGON(!rq->out[outpages - 1]);
+               if (rq->out[outpages - 1] != rq->in[inpages - 1]) {
+                       dst = kmap_local_page(rq->out[outpages - 1]);
+                       memcpy(dst, interlaced_offset ? src :
+                                       (src + righthalf), lefthalf);
+                       kunmap_local(dst);
+               } else if (!interlaced_offset) {
                        memmove(src, src + righthalf, lefthalf);
-               } else {
-                       dst = kmap_atomic(rq->out[1]);
-                       memcpy(dst, src + righthalf, lefthalf);
-                       kunmap_atomic(dst);
                }
        }
-       kunmap_atomic(src);
+       kunmap_local(src);
        return 0;
 }
 
 static struct z_erofs_decompressor decompressors[] = {
        [Z_EROFS_COMPRESSION_SHIFTED] = {
-               .decompress = z_erofs_shifted_transform,
+               .decompress = z_erofs_transform_plain,
                .name = "shifted"
        },
+       [Z_EROFS_COMPRESSION_INTERLACED] = {
+               .decompress = z_erofs_transform_plain,
+               .name = "interlaced"
+       },
        [Z_EROFS_COMPRESSION_LZ4] = {
                .decompress = z_erofs_lz4_decompress,
                .name = "lz4"
 
  * bit 1 : HEAD1 big pcluster (0 - off; 1 - on)
  * bit 2 : HEAD2 big pcluster (0 - off; 1 - on)
  * bit 3 : tailpacking inline pcluster (0 - off; 1 - on)
+ * bit 4 : interlaced plain pcluster (0 - off; 1 - on)
  */
 #define Z_EROFS_ADVISE_COMPACTED_2B            0x0001
 #define Z_EROFS_ADVISE_BIG_PCLUSTER_1          0x0002
 #define Z_EROFS_ADVISE_BIG_PCLUSTER_2          0x0004
 #define Z_EROFS_ADVISE_INLINE_PCLUSTER         0x0008
+#define Z_EROFS_ADVISE_INTERLACED_PCLUSTER     0x0010
 
 struct z_erofs_map_header {
        __le16  h_reserved1;
 
                        goto out;
        }
 
-       if (m.headtype == Z_EROFS_VLE_CLUSTER_TYPE_PLAIN)
-               map->m_algorithmformat = Z_EROFS_COMPRESSION_SHIFTED;
-       else if (m.headtype == Z_EROFS_VLE_CLUSTER_TYPE_HEAD2)
+       if (m.headtype == Z_EROFS_VLE_CLUSTER_TYPE_PLAIN) {
+               if (vi->z_advise & Z_EROFS_ADVISE_INTERLACED_PCLUSTER)
+                       map->m_algorithmformat =
+                               Z_EROFS_COMPRESSION_INTERLACED;
+               else
+                       map->m_algorithmformat =
+                               Z_EROFS_COMPRESSION_SHIFTED;
+       } else if (m.headtype == Z_EROFS_VLE_CLUSTER_TYPE_HEAD2) {
                map->m_algorithmformat = vi->z_algorithmtype[1];
-       else
+       } else {
                map->m_algorithmformat = vi->z_algorithmtype[0];
+       }
 
        if ((flags & EROFS_GET_BLOCKS_FIEMAP) ||
            ((flags & EROFS_GET_BLOCKS_READMORE) &&