Merge tag 'pm-5.15-rc1-3' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[linux-2.6-microblaze.git] / fs / erofs / zmap.c
index f68aea4..9fb98d8 100644 (file)
@@ -212,9 +212,34 @@ static unsigned int decode_compactedbits(unsigned int lobits,
        return lo;
 }
 
+static int get_compacted_la_distance(unsigned int lclusterbits,
+                                    unsigned int encodebits,
+                                    unsigned int vcnt, u8 *in, int i)
+{
+       const unsigned int lomask = (1 << lclusterbits) - 1;
+       unsigned int lo, d1 = 0;
+       u8 type;
+
+       DBG_BUGON(i >= vcnt);
+
+       do {
+               lo = decode_compactedbits(lclusterbits, lomask,
+                                         in, encodebits * i, &type);
+
+               if (type != Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD)
+                       return d1;
+               ++d1;
+       } while (++i < vcnt);
+
+       /* vcnt - 1 (Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD) item */
+       if (!(lo & Z_EROFS_VLE_DI_D0_CBLKCNT))
+               d1 += lo - 1;
+       return d1;
+}
+
 static int unpack_compacted_index(struct z_erofs_maprecorder *m,
                                  unsigned int amortizedshift,
-                                 unsigned int eofs)
+                                 unsigned int eofs, bool lookahead)
 {
        struct erofs_inode *const vi = EROFS_I(m->inode);
        const unsigned int lclusterbits = vi->z_logical_clusterbits;
@@ -243,6 +268,11 @@ static int unpack_compacted_index(struct z_erofs_maprecorder *m,
        m->type = type;
        if (type == Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD) {
                m->clusterofs = 1 << lclusterbits;
+
+               /* figure out lookahead_distance: delta[1] if needed */
+               if (lookahead)
+                       m->delta[1] = get_compacted_la_distance(lclusterbits,
+                                               encodebits, vcnt, in, i);
                if (lo & Z_EROFS_VLE_DI_D0_CBLKCNT) {
                        if (!big_pcluster) {
                                DBG_BUGON(1);
@@ -313,7 +343,7 @@ static int unpack_compacted_index(struct z_erofs_maprecorder *m,
 }
 
 static int compacted_load_cluster_from_disk(struct z_erofs_maprecorder *m,
-                                           unsigned long lcn)
+                                           unsigned long lcn, bool lookahead)
 {
        struct inode *const inode = m->inode;
        struct erofs_inode *const vi = EROFS_I(inode);
@@ -364,11 +394,12 @@ out:
        err = z_erofs_reload_indexes(m, erofs_blknr(pos));
        if (err)
                return err;
-       return unpack_compacted_index(m, amortizedshift, erofs_blkoff(pos));
+       return unpack_compacted_index(m, amortizedshift, erofs_blkoff(pos),
+                                     lookahead);
 }
 
 static int z_erofs_load_cluster_from_disk(struct z_erofs_maprecorder *m,
-                                         unsigned int lcn)
+                                         unsigned int lcn, bool lookahead)
 {
        const unsigned int datamode = EROFS_I(m->inode)->datalayout;
 
@@ -376,7 +407,7 @@ static int z_erofs_load_cluster_from_disk(struct z_erofs_maprecorder *m,
                return legacy_load_cluster_from_disk(m, lcn);
 
        if (datamode == EROFS_INODE_FLAT_COMPRESSION)
-               return compacted_load_cluster_from_disk(m, lcn);
+               return compacted_load_cluster_from_disk(m, lcn, lookahead);
 
        return -EINVAL;
 }
@@ -399,7 +430,7 @@ static int z_erofs_extent_lookback(struct z_erofs_maprecorder *m,
 
        /* load extent head logical cluster if needed */
        lcn -= lookback_distance;
-       err = z_erofs_load_cluster_from_disk(m, lcn);
+       err = z_erofs_load_cluster_from_disk(m, lcn, false);
        if (err)
                return err;
 
@@ -450,7 +481,7 @@ static int z_erofs_get_extent_compressedlen(struct z_erofs_maprecorder *m,
        if (m->compressedlcs)
                goto out;
 
-       err = z_erofs_load_cluster_from_disk(m, lcn);
+       err = z_erofs_load_cluster_from_disk(m, lcn, false);
        if (err)
                return err;
 
@@ -498,6 +529,48 @@ err_bonus_cblkcnt:
        return -EFSCORRUPTED;
 }
 
+static int z_erofs_get_extent_decompressedlen(struct z_erofs_maprecorder *m)
+{
+       struct inode *inode = m->inode;
+       struct erofs_inode *vi = EROFS_I(inode);
+       struct erofs_map_blocks *map = m->map;
+       unsigned int lclusterbits = vi->z_logical_clusterbits;
+       u64 lcn = m->lcn, headlcn = map->m_la >> lclusterbits;
+       int err;
+
+       do {
+               /* handle the last EOF pcluster (no next HEAD lcluster) */
+               if ((lcn << lclusterbits) >= inode->i_size) {
+                       map->m_llen = inode->i_size - map->m_la;
+                       return 0;
+               }
+
+               err = z_erofs_load_cluster_from_disk(m, lcn, true);
+               if (err)
+                       return err;
+
+               if (m->type == Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD) {
+                       DBG_BUGON(!m->delta[1] &&
+                                 m->clusterofs != 1 << lclusterbits);
+               } else if (m->type == Z_EROFS_VLE_CLUSTER_TYPE_PLAIN ||
+                          m->type == Z_EROFS_VLE_CLUSTER_TYPE_HEAD) {
+                       /* go on until the next HEAD lcluster */
+                       if (lcn != headlcn)
+                               break;
+                       m->delta[1] = 1;
+               } else {
+                       erofs_err(inode->i_sb, "unknown type %u @ lcn %llu of nid %llu",
+                                 m->type, lcn, vi->nid);
+                       DBG_BUGON(1);
+                       return -EOPNOTSUPP;
+               }
+               lcn += m->delta[1];
+       } while (m->delta[1]);
+
+       map->m_llen = (lcn << lclusterbits) + m->clusterofs - map->m_la;
+       return 0;
+}
+
 int z_erofs_map_blocks_iter(struct inode *inode,
                            struct erofs_map_blocks *map,
                            int flags)
@@ -531,7 +604,7 @@ int z_erofs_map_blocks_iter(struct inode *inode,
        initial_lcn = ofs >> lclusterbits;
        endoff = ofs & ((1 << lclusterbits) - 1);
 
-       err = z_erofs_load_cluster_from_disk(&m, initial_lcn);
+       err = z_erofs_load_cluster_from_disk(&m, initial_lcn, false);
        if (err)
                goto unmap_out;
 
@@ -581,6 +654,12 @@ int z_erofs_map_blocks_iter(struct inode *inode,
        err = z_erofs_get_extent_compressedlen(&m, initial_lcn);
        if (err)
                goto out;
+
+       if (flags & EROFS_GET_BLOCKS_FIEMAP) {
+               err = z_erofs_get_extent_decompressedlen(&m);
+               if (!err)
+                       map->m_flags |= EROFS_MAP_FULL_MAPPED;
+       }
 unmap_out:
        if (m.kaddr)
                kunmap_atomic(m.kaddr);
@@ -596,3 +675,41 @@ out:
        DBG_BUGON(err < 0 && err != -ENOMEM);
        return err;
 }
+
+static int z_erofs_iomap_begin_report(struct inode *inode, loff_t offset,
+                               loff_t length, unsigned int flags,
+                               struct iomap *iomap, struct iomap *srcmap)
+{
+       int ret;
+       struct erofs_map_blocks map = { .m_la = offset };
+
+       ret = z_erofs_map_blocks_iter(inode, &map, EROFS_GET_BLOCKS_FIEMAP);
+       if (map.mpage)
+               put_page(map.mpage);
+       if (ret < 0)
+               return ret;
+
+       iomap->bdev = inode->i_sb->s_bdev;
+       iomap->offset = map.m_la;
+       iomap->length = map.m_llen;
+       if (map.m_flags & EROFS_MAP_MAPPED) {
+               iomap->type = IOMAP_MAPPED;
+               iomap->addr = map.m_pa;
+       } else {
+               iomap->type = IOMAP_HOLE;
+               iomap->addr = IOMAP_NULL_ADDR;
+               /*
+                * No strict rule how to describe extents for post EOF, yet
+                * we need do like below. Otherwise, iomap itself will get
+                * into an endless loop on post EOF.
+                */
+               if (iomap->offset >= inode->i_size)
+                       iomap->length = length + map.m_la - offset;
+       }
+       iomap->flags = 0;
+       return 0;
+}
+
+const struct iomap_ops z_erofs_iomap_report_ops = {
+       .iomap_begin = z_erofs_iomap_begin_report,
+};