[PATCH 1/2] blk: Add bounce buffer support to read/write operations

Simon Glass sjg at chromium.org
Tue Aug 15 00:42:56 CEST 2023


Hi Marek,

On Sun, 13 Aug 2023 at 17:50, Marek Vasut
<marek.vasut+renesas at mailbox.org> wrote:
>
> Some devices have limited DMA capabilities and require that the
> buffers passed to them fit specific properties. Add new optional
> callback which can be used at driver level to indicate whether a
> buffer alignment is suitable for the device DMA or not, and
> trigger use of generic bounce buffer implementation to help use
> of unsuitable buffers at the expense of performance degradation.
>
> Signed-off-by: Marek Vasut <marek.vasut+renesas at mailbox.org>
> ---
> Cc: Abdellatif El Khlifi <abdellatif.elkhlifi at arm.com>
> Cc: Bin Meng <bmeng.cn at gmail.com>
> Cc: Heinrich Schuchardt <xypron.glpk at gmx.de>
> Cc: Mattijs Korpershoek <mkorpershoek at baylibre.com>
> Cc: Michal Suchanek <msuchanek at suse.de>
> Cc: Simon Glass <sjg at chromium.org>
> Cc: Tobias Waldekranz <tobias at waldekranz.com>
> ---
>  drivers/block/blk-uclass.c | 62 ++++++++++++++++++++++++++++++++++++--
>  include/blk.h              | 19 ++++++++++++
>  2 files changed, 79 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c
> index 6aac92d9962..885513893f6 100644
> --- a/drivers/block/blk-uclass.c
> +++ b/drivers/block/blk-uclass.c
> @@ -446,6 +446,26 @@ int blk_get_device(int uclass_id, int devnum, struct udevice **devp)
>         return device_probe(*devp);
>  }
>
> +struct blk_bounce_buffer {
> +       struct udevice          *dev;
> +       struct bounce_buffer    state;
> +};
> +
> +static int blk_buffer_aligned(struct bounce_buffer *state)
> +{
> +#if IS_ENABLED(CONFIG_BOUNCE_BUFFER)

I think it is worth adding an accessor in the header file to allow you
to convert this #if to if

> +       struct blk_bounce_buffer *bbstate =
> +               container_of(state, struct blk_bounce_buffer, state);
> +       struct udevice *dev = bbstate->dev;
> +       const struct blk_ops *ops = blk_get_ops(dev);
> +
> +       if (ops->buffer_aligned)
> +               return ops->buffer_aligned(dev, state);
> +#endif /* CONFIG_BOUNCE_BUFFER */
> +
> +       return 1;       /* Default, any buffer is OK */
> +}
> +
>  long blk_read(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *buf)
>  {
>         struct blk_desc *desc = dev_get_uclass_plat(dev);
> @@ -458,7 +478,25 @@ long blk_read(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *buf)
>         if (blkcache_read(desc->uclass_id, desc->devnum,
>                           start, blkcnt, desc->blksz, buf))
>                 return blkcnt;
> -       blks_read = ops->read(dev, start, blkcnt, buf);
> +
> +       if (IS_ENABLED(CONFIG_BOUNCE_BUFFER)) {
> +               struct blk_bounce_buffer bbstate = { .dev = dev };
> +               int ret;
> +
> +               ret = bounce_buffer_start_extalign(&bbstate.state, buf,
> +                                                  blkcnt * desc->blksz,
> +                                                  GEN_BB_WRITE, desc->blksz,
> +                                                  blk_buffer_aligned);
> +               if (ret)
> +                       return ret;
> +
> +               blks_read = ops->read(dev, start, blkcnt, bbstate.state.bounce_buffer);
> +
> +               bounce_buffer_stop(&bbstate.state);
> +       } else {
> +               blks_read = ops->read(dev, start, blkcnt, buf);
> +       }
> +
>         if (blks_read == blkcnt)
>                 blkcache_fill(desc->uclass_id, desc->devnum, start, blkcnt,
>                               desc->blksz, buf);
> @@ -471,13 +509,33 @@ long blk_write(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
>  {
>         struct blk_desc *desc = dev_get_uclass_plat(dev);
>         const struct blk_ops *ops = blk_get_ops(dev);
> +       long blks_written;
>
>         if (!ops->write)
>                 return -ENOSYS;
>
>         blkcache_invalidate(desc->uclass_id, desc->devnum);
>
> -       return ops->write(dev, start, blkcnt, buf);
> +       if (IS_ENABLED(CONFIG_BOUNCE_BUFFER)) {
> +               struct blk_bounce_buffer bbstate = { .dev = dev };
> +               int ret;
> +
> +               ret = bounce_buffer_start_extalign(&bbstate.state, (void *)buf,
> +                                                  blkcnt * desc->blksz,
> +                                                  GEN_BB_READ, desc->blksz,
> +                                                  blk_buffer_aligned);
> +               if (ret)
> +                       return ret;
> +
> +               blks_written = ops->write(dev, start, blkcnt,
> +                                         bbstate.state.bounce_buffer);
> +
> +               bounce_buffer_stop(&bbstate.state);
> +       } else {
> +               blks_written = ops->write(dev, start, blkcnt, buf);
> +       }
> +
> +       return blks_written;
>  }
>
>  long blk_erase(struct udevice *dev, lbaint_t start, lbaint_t blkcnt)
> diff --git a/include/blk.h b/include/blk.h
> index 8986e953e5a..b819f97c2f1 100644
> --- a/include/blk.h
> +++ b/include/blk.h
> @@ -7,6 +7,7 @@
>  #ifndef BLK_H
>  #define BLK_H
>
> +#include <bouncebuf.h>
>  #include <dm/uclass-id.h>
>  #include <efi.h>
>
> @@ -260,6 +261,24 @@ struct blk_ops {
>          * @return 0 if OK, -ve on error
>          */
>         int (*select_hwpart)(struct udevice *dev, int hwpart);
> +
> +#if IS_ENABLED(CONFIG_BOUNCE_BUFFER)
> +       /**
> +        * buffer_aligned() - test memory alignment of block operation buffer
> +        *
> +        * Some devices have limited DMA capabilities and require that the
> +        * buffers passed to them fit specific properties. This optional
> +        * callback can be used to indicate whether a buffer alignment is
> +        * suitable for the device DMA or not, and trigger use of generic
> +        * bounce buffer implementation to help use of unsuitable buffers
> +        * at the expense of performance degradation.
> +        *
> +        * @dev:        Block device associated with the request
> +        * @state:      Bounce buffer state
> +        * @return 1 if OK, 0 if unaligned

We typically return 0 for OK, so that we can use -ve values for
errors. Perhaps flip this to check_aligned() that returns either 0 or
-EINVAL ? I'm just worried that it will be confusing since it is
different from nearly every other DM API.

> +        */
> +       int (*buffer_aligned)(struct udevice *dev, struct bounce_buffer *state);
> +#endif /* CONFIG_BOUNCE_BUFFER */
>  };
>
>  /*
> --
> 2.40.1
>

Regards,
Simon


More information about the U-Boot mailing list