On 10 April 2018 at 19:39, Andreas Gruenbacher <agrue...@redhat.com> wrote:
> Rewrite function hole_size so that it computes the entire size of a
> hole.  Previously, the hole size returned wasn't guaranteed to span the
> entire hole and multiple invocations of hole_size could be needed to
> reach the next bit of data.
>
> Signed-off-by: Andreas Gruenbacher <agrue...@redhat.com>
> ---
>  fs/gfs2/bmap.c | 106 
> ++++++++++++++++++++++++++++++++++++---------------------
>  1 file changed, 67 insertions(+), 39 deletions(-)
>
> diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
> index 8c25a64d8ae0..14c21825b890 100644
> --- a/fs/gfs2/bmap.c
> +++ b/fs/gfs2/bmap.c
> @@ -279,6 +279,12 @@ static inline __be64 *metapointer(unsigned int height, 
> const struct metapath *mp
>         return p + mp->mp_list[height];
>  }
>
> +static inline const __be64 *metaend(unsigned int height, const struct 
> metapath *mp)
> +{
> +       const struct buffer_head *bh = mp->mp_bh[height];
> +       return (const __be64 *)(bh->b_data + bh->b_size);
> +}
> +
>  static void gfs2_metapath_ra(struct gfs2_glock *gl, __be64 *start, __be64 
> *end)
>  {
>         const __be64 *t;
> @@ -631,56 +637,78 @@ static int gfs2_iomap_alloc(struct inode *inode, struct 
> iomap *iomap,
>   * hole_size - figure out the size of a hole
>   * @inode: The inode
>   * @lblock: The logical starting block number
> - * @mp: The metapath
> + * @mp: The metapath at lblock
> + * @pholesz: The hole size in bytes (output)
> + *
> + * This function modifies @mp.
>   *
> - * Returns: The hole size in bytes
> + * Returns: errno on error
>   *
>   */
> -static u64 hole_size(struct inode *inode, sector_t lblock, struct metapath 
> *mp)
> +static int hole_size(struct inode *inode, sector_t lblock, struct metapath 
> *mp,
> +                    u64 *pholesz)
>  {
> +       sector_t lblock_end = (i_size_read(inode) + i_blocksize(inode) - 1) >>
> +                             inode->i_blkbits;
>         struct gfs2_inode *ip = GFS2_I(inode);
>         struct gfs2_sbd *sdp = GFS2_SB(inode);
> -       struct metapath mp_eof;
> -       u64 factor = 1;
> -       int hgt;
> -       u64 holesz = 0;
>         const __be64 *first, *end, *ptr;
> -       const struct buffer_head *bh;
> -       u64 lblock_stop = (i_size_read(inode) - 1) >> inode->i_blkbits;
> -       int zeroptrs;
> -       bool done = false;
> -
> -       /* Get another metapath, to the very last byte */
> -       find_metapath(sdp, lblock_stop, &mp_eof, ip->i_height);
> -       for (hgt = ip->i_height - 1; hgt >= 0 && !done; hgt--) {
> -               bh = mp->mp_bh[hgt];
> -               if (bh) {
> -                       zeroptrs = 0;
> -                       first = metapointer(hgt, mp);
> -                       end = (const __be64 *)(bh->b_data + bh->b_size);
> -
> -                       for (ptr = first; ptr < end; ptr++) {
> -                               if (*ptr) {
> -                                       done = true;
> -                                       break;
> -                               } else {
> -                                       zeroptrs++;
> -                               }
> +       u64 holesz = 0, factor = 1;
> +       int hgt, ret;
> +
> +       for (hgt = ip->i_height - 1; hgt >= mp->mp_aheight; hgt--)
> +               factor *= sdp->sd_inptrs;
> +
> +       for (;;) {
> +               /* Count the number of zero pointers.  */
> +               first = metapointer(hgt, mp);
> +               end = metaend(hgt, mp);
> +               if (end - first > lblock_end - lblock - holesz)
> +                       end = first + lblock_end - lblock - holesz;
> +               for (ptr = first; ptr < end; ptr++) {
> +                       if (*ptr) {
> +                               holesz += (ptr - first) * factor;
> +                               if (hgt == ip->i_height - 1)
> +                                       goto out;
> +                               mp->mp_list[hgt] += (ptr - first);
> +                               goto fill_up_metapath;
>                         }
> -               } else {
> -                       zeroptrs = sdp->sd_inptrs;
>                 }
> -               if (factor * zeroptrs >= lblock_stop - lblock + 1) {
> -                       holesz = lblock_stop - lblock + 1;
> -                       break;
> +               holesz += (ptr - first) * factor;

We probably want to check for EOF here as well to stop immediately
when we've had enough:

                if (lblock + holesz >= lblock_end)
                        goto out;

> +
> +lower_metapath:
> +               /* Decrease height of metapath. */
> +               brelse(mp->mp_bh[hgt]);
> +               mp->mp_bh[hgt] = NULL;
> +               mp->mp_list[hgt] = 0;
> +               if (!hgt)
> +                       goto out;
> +               hgt--;
> +               factor *= sdp->sd_inptrs;
> +
> +               /* Advance in metadata tree. */
> +               (mp->mp_list[hgt])++;
> +               first = metapointer(hgt, mp);
> +               end = metaend(hgt, mp);
> +               if (first >= end) {
> +                       if (!hgt)
> +                               goto out;
> +                       goto lower_metapath;
>                 }
> -               holesz += factor * zeroptrs;
>
> -               factor *= sdp->sd_inptrs;
> -               if (hgt && (mp->mp_list[hgt - 1] < mp_eof.mp_list[hgt - 1]))
> -                       (mp->mp_list[hgt - 1])++;
> +fill_up_metapath:
> +               /* Fill up metapath. */
> +               ret = fillup_metapath(ip, mp, ip->i_height - 1);
> +               if (ret < 0)
> +                       return ret;
> +               while (ret--) {
> +                       hgt++;
> +                       factor /= sdp->sd_inptrs;
> +               }
>         }
> -       return holesz << inode->i_blkbits;
> +out:
> +       *pholesz = holesz << inode->i_blkbits;
> +       return 0;
>  }
>
>  static void gfs2_stuffed_iomap(struct inode *inode, struct iomap *iomap)
> @@ -804,7 +832,7 @@ int gfs2_iomap_begin(struct inode *inode, loff_t pos, 
> loff_t length,
>                 if (pos >= size)
>                         ret = -ENOENT;
>                 else if (height <= ip->i_height)
> -                       iomap->length = hole_size(inode, lblock, &mp);
> +                       ret = hole_size(inode, lblock, &mp, &iomap->length);
>                 else
>                         iomap->length = size - pos;
>         }
> --
> 2.14.3
>

Reply via email to