On Fri, Apr 26, 2019 at 03:11:26PM +0200, Andreas Gruenbacher wrote:
> Move the page_done callback into a separate iomap_page_ops structure and
> add a page_prepare calback to be called before the next page is written
> to.  In gfs2, we'll want to start a transaction in page_prepare and end
> it in page_done; other filesystems that implement data journaling will
> require the same kind of mechanism.
> 
> Signed-off-by: Andreas Gruenbacher <[email protected]>
> ---
>  fs/gfs2/bmap.c        | 22 +++++++++++++++++-----
>  fs/iomap.c            | 22 ++++++++++++++++++----
>  include/linux/iomap.h | 18 +++++++++++++-----
>  3 files changed, 48 insertions(+), 14 deletions(-)
> 
> diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
> index 5da4ca9041c0..6b980703bae7 100644
> --- a/fs/gfs2/bmap.c
> +++ b/fs/gfs2/bmap.c
> @@ -991,15 +991,27 @@ static void gfs2_write_unlock(struct inode *inode)
>       gfs2_glock_dq_uninit(&ip->i_gh);
>  }
>  
> -static void gfs2_iomap_journaled_page_done(struct inode *inode, loff_t pos,
> -                             unsigned copied, struct page *page,
> -                             struct iomap *iomap)
> +static int gfs2_iomap_page_prepare(struct inode *inode, loff_t pos,
> +                                unsigned len, struct iomap *iomap)
> +{
> +     return 0;
> +}
> +
> +static void gfs2_iomap_page_done(struct inode *inode, loff_t pos,
> +                              unsigned copied, struct page *page,
> +                              struct iomap *iomap)
>  {
>       struct gfs2_inode *ip = GFS2_I(inode);
>  
> -     gfs2_page_add_databufs(ip, page, offset_in_page(pos), copied);
> +     if (page)
> +             gfs2_page_add_databufs(ip, page, offset_in_page(pos), copied);
>  }
>  
> +static const struct iomap_page_ops gfs2_iomap_page_ops = {
> +     .page_prepare = gfs2_iomap_page_prepare,
> +     .page_done = gfs2_iomap_page_done,
> +};
> +
>  static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos,
>                                 loff_t length, unsigned flags,
>                                 struct iomap *iomap,
> @@ -1077,7 +1089,7 @@ static int gfs2_iomap_begin_write(struct inode *inode, 
> loff_t pos,
>               }
>       }
>       if (!gfs2_is_stuffed(ip) && gfs2_is_jdata(ip))
> -             iomap->page_done = gfs2_iomap_journaled_page_done;
> +             iomap->page_ops = &gfs2_iomap_page_ops;
>       return 0;
>  
>  out_trans_end:
> diff --git a/fs/iomap.c b/fs/iomap.c
> index 3e4652dac9d9..ba2d44b33ed1 100644
> --- a/fs/iomap.c
> +++ b/fs/iomap.c
> @@ -665,6 +665,7 @@ static int
>  iomap_write_begin(struct inode *inode, loff_t pos, unsigned len, unsigned 
> flags,
>               struct page **pagep, struct iomap *iomap)
>  {
> +     const struct iomap_page_ops *page_ops = iomap->page_ops;
>       pgoff_t index = pos >> PAGE_SHIFT;
>       struct page *page;
>       int status = 0;
> @@ -674,9 +675,17 @@ iomap_write_begin(struct inode *inode, loff_t pos, 
> unsigned len, unsigned flags,
>       if (fatal_signal_pending(current))
>               return -EINTR;
>  
> +     if (page_ops) {
> +             status = page_ops->page_prepare(inode, pos, len, iomap);
> +             if (status)
> +                     return status;
> +     }
> +
>       page = grab_cache_page_write_begin(inode->i_mapping, index, flags);
> -     if (!page)
> -             return -ENOMEM;
> +     if (!page) {
> +             status = -ENOMEM;
> +             goto no_page;
> +     }
>  
>       if (iomap->type == IOMAP_INLINE)
>               iomap_read_inline_data(inode, page, iomap);
> @@ -684,12 +693,16 @@ iomap_write_begin(struct inode *inode, loff_t pos, 
> unsigned len, unsigned flags,
>               status = __block_write_begin_int(page, pos, len, NULL, iomap);
>       else
>               status = __iomap_write_begin(inode, pos, len, page, iomap);
> +
>       if (unlikely(status)) {
>               unlock_page(page);
>               put_page(page);
>               page = NULL;
>  
>               iomap_write_failed(inode, pos, len);
> +no_page:
> +             if (page_ops)
> +                     page_ops->page_done(inode, pos, 0, NULL, iomap);
>       }
>  
>       *pagep = page;

I think we need to clean this area up a bit, this is becoming to
confusing.

Something like:

        if (unlikely(status))
                goto out_unlock;

        *pagep = page;
        return 0;

out_unlock:
        unlock_page(page);
        put_page(page);
        iomap_write_failed(inode, pos, len);
out_no_page:
        if (page_ops)
                page_ops->page_done(inode, pos, 0, NULL, iomap);
        *pagep = NULL;
        return status;


> +     if (page_ops)
> +             page_ops->page_done(inode, pos, copied, page, iomap);

Do we always require both pages ops to be set?  Wouldn't it be better
to check for the method presence as well?

> +/*
> + * Called before / after processing a page in the mapping returned in this
> + * iomap.  At least for now, this is only supported in the buffered write 
> path.
> + * When page_prepare returns 0, page_done is called as well
> + * (possibly with page == NULL).
> + */

Not just for now - I think the concept fundamentally only makes sense
for buffered I/O.  Maybe we need to express that a little more clearly.

Otherwise looks fine to me.

Reply via email to