diff mbox series

mmc: jz4740: rework pre_req/post_req implementation

Message ID 20181120182121.19778-1-ezequiel@collabora.com (mailing list archive)
State New, archived
Headers show
Series mmc: jz4740: rework pre_req/post_req implementation | expand

Commit Message

Ezequiel Garcia Nov. 20, 2018, 6:21 p.m. UTC
As reported by Aaro, the JZ4740 MMC driver throws a warning
when the kernel is built without preemption (CONFIG_PREEMPT_NONE=y).

[   16.461094] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 567 host->next_data.cookie 568
[   16.473120] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 568 host->next_data.cookie 569
[   16.485144] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 569 host->next_data.cookie 570
[   16.497170] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 570 host->next_data.cookie 571
[   16.509194] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 571 host->next_data.cookie 572
[   16.532421] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 572 host->next_data.cookie 573
[   16.544594] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 573 host->next_data.cookie 574
[   16.556621] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 574 host->next_data.cookie 575
[   16.568638] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 575 host->next_data.cookie 576
[   16.601092] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 582 host->next_data.cookie 583

The problem seems to be related to how pre_req/post_req is implemented.
Currently, it seems the driver expects jz4740_mmc_prepare_dma_data()
to be called with monotonically increasing host_cookie values,
which is wrong.

Moreover, the implementation is overly complicated, keeping track
of unneeded "next cookie" state.

So, instead of attempting to fix the current pre_req/post_req
implementation, this commit refactors the driver, dropping
the state, following other drivers such as dw_mmc and sdhci.

Cc: Paul Cercueil <paul@crapouillou.net>
Cc: Mathieu Malaterre <malat@debian.org>
Reported-by: Aaro Koskinen <aaro.koskinen@iki.fi>
Signed-off-by: Ezequiel Garcia <ezequiel@collabora.com>
---
 drivers/mmc/host/jz4740_mmc.c | 118 ++++++++++++++++------------------
 1 file changed, 54 insertions(+), 64 deletions(-)

Comments

Ulf Hansson Nov. 20, 2018, 10:19 p.m. UTC | #1
On 20 November 2018 at 19:21, Ezequiel Garcia <ezequiel@collabora.com> wrote:
> As reported by Aaro, the JZ4740 MMC driver throws a warning
> when the kernel is built without preemption (CONFIG_PREEMPT_NONE=y).
>
> [   16.461094] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 567 host->next_data.cookie 568
> [   16.473120] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 568 host->next_data.cookie 569
> [   16.485144] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 569 host->next_data.cookie 570
> [   16.497170] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 570 host->next_data.cookie 571
> [   16.509194] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 571 host->next_data.cookie 572
> [   16.532421] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 572 host->next_data.cookie 573
> [   16.544594] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 573 host->next_data.cookie 574
> [   16.556621] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 574 host->next_data.cookie 575
> [   16.568638] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 575 host->next_data.cookie 576
> [   16.601092] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 582 host->next_data.cookie 583
>
> The problem seems to be related to how pre_req/post_req is implemented.
> Currently, it seems the driver expects jz4740_mmc_prepare_dma_data()
> to be called with monotonically increasing host_cookie values,
> which is wrong.
>
> Moreover, the implementation is overly complicated, keeping track
> of unneeded "next cookie" state.
>
> So, instead of attempting to fix the current pre_req/post_req
> implementation, this commit refactors the driver, dropping
> the state, following other drivers such as dw_mmc and sdhci.
>
> Cc: Paul Cercueil <paul@crapouillou.net>
> Cc: Mathieu Malaterre <malat@debian.org>
> Reported-by: Aaro Koskinen <aaro.koskinen@iki.fi>
> Signed-off-by: Ezequiel Garcia <ezequiel@collabora.com>

This looks good to me, however because of the rather big changes I
doubt this can be tagged for stable - or did you have a try to apply
it for an old kernel?

In either case, I can queue this up for next, which anyway seems like
it should be sufficient. Right?

Kind regards
Uffe

> ---
>  drivers/mmc/host/jz4740_mmc.c | 118 ++++++++++++++++------------------
>  1 file changed, 54 insertions(+), 64 deletions(-)
>
> diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
> index 0c1efd5100b7..ca20e2f81be8 100644
> --- a/drivers/mmc/host/jz4740_mmc.c
> +++ b/drivers/mmc/host/jz4740_mmc.c
> @@ -126,9 +126,23 @@ enum jz4740_mmc_state {
>         JZ4740_MMC_STATE_DONE,
>  };
>
> -struct jz4740_mmc_host_next {
> -       int sg_len;
> -       s32 cookie;
> +/*
> + * The MMC core allows to prepare a mmc_request while another mmc_request
> + * is in-flight. This is used via the pre_req/post_req hooks.
> + * This driver uses the pre_req/post_req hooks to map/unmap the mmc_request.
> + * Following what other drivers do (sdhci, dw_mmc) we use the following cookie
> + * flags to keep track of the mmc_request mapping state.
> + *
> + * COOKIE_UNMAPPED: the request is not mapped.
> + * COOKIE_PREMAPPED: the request was mapped in pre_req,
> + * and should be unmapped in post_req.
> + * COOKIE_MAPPED: the request was mapped in the irq handler,
> + * and should be unmapped before mmc_request_done is called..
> + */
> +enum jz4780_cookie {
> +       COOKIE_UNMAPPED = 0,
> +       COOKIE_PREMAPPED,
> +       COOKIE_MAPPED,
>  };
>
>  struct jz4740_mmc_host {
> @@ -162,9 +176,7 @@ struct jz4740_mmc_host {
>         /* DMA support */
>         struct dma_chan *dma_rx;
>         struct dma_chan *dma_tx;
> -       struct jz4740_mmc_host_next next_data;
>         bool use_dma;
> -       int sg_len;
>
>  /* The DMA trigger level is 8 words, that is to say, the DMA read
>   * trigger is when data words in MSC_RXFIFO is >= 8 and the DMA write
> @@ -226,9 +238,6 @@ static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host)
>                 return PTR_ERR(host->dma_rx);
>         }
>
> -       /* Initialize DMA pre request cookie */
> -       host->next_data.cookie = 1;
> -
>         return 0;
>  }
>
> @@ -245,60 +254,44 @@ static void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host,
>         enum dma_data_direction dir = mmc_get_dma_dir(data);
>
>         dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);
> +       data->host_cookie = COOKIE_UNMAPPED;
>  }
>
> -/* Prepares DMA data for current/next transfer, returns non-zero on failure */
> +/* Prepares DMA data for current or next transfer.
> + * A request can be in-flight when this is called.
> + */
>  static int jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host *host,
>                                        struct mmc_data *data,
> -                                      struct jz4740_mmc_host_next *next,
> -                                      struct dma_chan *chan)
> +                                      int cookie)
>  {
> -       struct jz4740_mmc_host_next *next_data = &host->next_data;
> +       struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
>         enum dma_data_direction dir = mmc_get_dma_dir(data);
> -       int sg_len;
> -
> -       if (!next && data->host_cookie &&
> -           data->host_cookie != host->next_data.cookie) {
> -               dev_warn(mmc_dev(host->mmc),
> -                        "[%s] invalid cookie: data->host_cookie %d host->next_data.cookie %d\n",
> -                        __func__,
> -                        data->host_cookie,
> -                        host->next_data.cookie);
> -               data->host_cookie = 0;
> -       }
> +       int sg_count;
>
> -       /* Check if next job is already prepared */
> -       if (next || data->host_cookie != host->next_data.cookie) {
> -               sg_len = dma_map_sg(chan->device->dev,
> -                                   data->sg,
> -                                   data->sg_len,
> -                                   dir);
> +       if (data->host_cookie == COOKIE_PREMAPPED)
> +               return data->sg_count;
>
> -       } else {
> -               sg_len = next_data->sg_len;
> -               next_data->sg_len = 0;
> -       }
> +       sg_count = dma_map_sg(chan->device->dev,
> +                       data->sg,
> +                       data->sg_len,
> +                       dir);
>
> -       if (sg_len <= 0) {
> +       if (sg_count <= 0) {
>                 dev_err(mmc_dev(host->mmc),
>                         "Failed to map scatterlist for DMA operation\n");
>                 return -EINVAL;
>         }
>
> -       if (next) {
> -               next->sg_len = sg_len;
> -               data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
> -       } else
> -               host->sg_len = sg_len;
> +       data->sg_count = sg_count;
> +       data->host_cookie = cookie;
>
> -       return 0;
> +       return data->sg_count;
>  }
>
>  static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
>                                          struct mmc_data *data)
>  {
> -       int ret;
> -       struct dma_chan *chan;
> +       struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
>         struct dma_async_tx_descriptor *desc;
>         struct dma_slave_config conf = {
>                 .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
> @@ -306,29 +299,26 @@ static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
>                 .src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
>                 .dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
>         };
> +       int sg_count;
>
>         if (data->flags & MMC_DATA_WRITE) {
>                 conf.direction = DMA_MEM_TO_DEV;
>                 conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO;
>                 conf.slave_id = JZ4740_DMA_TYPE_MMC_TRANSMIT;
> -               chan = host->dma_tx;
>         } else {
>                 conf.direction = DMA_DEV_TO_MEM;
>                 conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO;
>                 conf.slave_id = JZ4740_DMA_TYPE_MMC_RECEIVE;
> -               chan = host->dma_rx;
>         }
>
> -       ret = jz4740_mmc_prepare_dma_data(host, data, NULL, chan);
> -       if (ret)
> -               return ret;
> +       sg_count = jz4740_mmc_prepare_dma_data(host, data, COOKIE_MAPPED);
> +       if (sg_count < 0)
> +               return sg_count;
>
>         dmaengine_slave_config(chan, &conf);
> -       desc = dmaengine_prep_slave_sg(chan,
> -                                      data->sg,
> -                                      host->sg_len,
> -                                      conf.direction,
> -                                      DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> +       desc = dmaengine_prep_slave_sg(chan, data->sg, sg_count,
> +                       conf.direction,
> +                       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
>         if (!desc) {
>                 dev_err(mmc_dev(host->mmc),
>                         "Failed to allocate DMA %s descriptor",
> @@ -342,7 +332,8 @@ static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
>         return 0;
>
>  dma_unmap:
> -       jz4740_mmc_dma_unmap(host, data);
> +       if (data->host_cookie == COOKIE_MAPPED)
> +               jz4740_mmc_dma_unmap(host, data);
>         return -ENOMEM;
>  }
>
> @@ -351,16 +342,13 @@ static void jz4740_mmc_pre_request(struct mmc_host *mmc,
>  {
>         struct jz4740_mmc_host *host = mmc_priv(mmc);
>         struct mmc_data *data = mrq->data;
> -       struct jz4740_mmc_host_next *next_data = &host->next_data;
>
> -       BUG_ON(data->host_cookie);
> -
> -       if (host->use_dma) {
> -               struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
> +       if (!host->use_dma)
> +               return;
>
> -               if (jz4740_mmc_prepare_dma_data(host, data, next_data, chan))
> -                       data->host_cookie = 0;
> -       }
> +       data->host_cookie = COOKIE_UNMAPPED;
> +       if (jz4740_mmc_prepare_dma_data(host, data, COOKIE_PREMAPPED) < 0)
> +               data->host_cookie = COOKIE_UNMAPPED;
>  }
>
>  static void jz4740_mmc_post_request(struct mmc_host *mmc,
> @@ -370,10 +358,8 @@ static void jz4740_mmc_post_request(struct mmc_host *mmc,
>         struct jz4740_mmc_host *host = mmc_priv(mmc);
>         struct mmc_data *data = mrq->data;
>
> -       if (host->use_dma && data->host_cookie) {
> +       if (data && data->host_cookie != COOKIE_UNMAPPED)
>                 jz4740_mmc_dma_unmap(host, data);
> -               data->host_cookie = 0;
> -       }
>
>         if (err) {
>                 struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
> @@ -436,10 +422,14 @@ static void jz4740_mmc_reset(struct jz4740_mmc_host *host)
>  static void jz4740_mmc_request_done(struct jz4740_mmc_host *host)
>  {
>         struct mmc_request *req;
> +       struct mmc_data *data;
>
>         req = host->req;
> +       data = req->data;
>         host->req = NULL;
>
> +       if (data && data->host_cookie == COOKIE_MAPPED)
> +               jz4740_mmc_dma_unmap(host, data);
>         mmc_request_done(host->mmc, req);
>  }
>
> --
> 2.19.1
>
Aaro Koskinen Nov. 20, 2018, 11:21 p.m. UTC | #2
Hi,

On Tue, Nov 20, 2018 at 03:21:21PM -0300, Ezequiel Garcia wrote:
> As reported by Aaro, the JZ4740 MMC driver throws a warning
> when the kernel is built without preemption (CONFIG_PREEMPT_NONE=y).
> 
> [   16.461094] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 567 host->next_data.cookie 568
[...]
> [   16.601092] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 582 host->next_data.cookie 583
> 
> The problem seems to be related to how pre_req/post_req is implemented.
> Currently, it seems the driver expects jz4740_mmc_prepare_dma_data()
> to be called with monotonically increasing host_cookie values,
> which is wrong.
> 
> Moreover, the implementation is overly complicated, keeping track
> of unneeded "next cookie" state.
> 
> So, instead of attempting to fix the current pre_req/post_req
> implementation, this commit refactors the driver, dropping
> the state, following other drivers such as dw_mmc and sdhci.
> 
> Cc: Paul Cercueil <paul@crapouillou.net>
> Cc: Mathieu Malaterre <malat@debian.org>
> Reported-by: Aaro Koskinen <aaro.koskinen@iki.fi>
> Signed-off-by: Ezequiel Garcia <ezequiel@collabora.com>

Thanks for this! I tested this on top of v4.19 on CI20 with PREEMPT_NONE=y
and MMC works now fine without spam. I also did a longer stress test
compiling Python natively on a file system on MMC. Also briefly checked
with the PREEMPT=y kernel config and at least the rootfs got mounted
fine. So:

Tested-by: Aaro Koskinen <aaro.koskinen@iki.fi>

A.
Ezequiel Garcia Nov. 21, 2018, 1:16 p.m. UTC | #3
On Tuesday, November 20, 2018 20:21 -03, Aaro Koskinen <aaro.koskinen@iki.fi> wrote: 
 
> Hi,
> 
> On Tue, Nov 20, 2018 at 03:21:21PM -0300, Ezequiel Garcia wrote:
> > As reported by Aaro, the JZ4740 MMC driver throws a warning
> > when the kernel is built without preemption (CONFIG_PREEMPT_NONE=y).
> > 
> > [   16.461094] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 567 host->next_data.cookie 568
> [...]
> > [   16.601092] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 582 host->next_data.cookie 583
> > 
> > The problem seems to be related to how pre_req/post_req is implemented.
> > Currently, it seems the driver expects jz4740_mmc_prepare_dma_data()
> > to be called with monotonically increasing host_cookie values,
> > which is wrong.
> > 
> > Moreover, the implementation is overly complicated, keeping track
> > of unneeded "next cookie" state.
> > 
> > So, instead of attempting to fix the current pre_req/post_req
> > implementation, this commit refactors the driver, dropping
> > the state, following other drivers such as dw_mmc and sdhci.
> > 
> > Cc: Paul Cercueil <paul@crapouillou.net>
> > Cc: Mathieu Malaterre <malat@debian.org>
> > Reported-by: Aaro Koskinen <aaro.koskinen@iki.fi>
> > Signed-off-by: Ezequiel Garcia <ezequiel@collabora.com>
> 
> Thanks for this! I tested this on top of v4.19 on CI20 with PREEMPT_NONE=y
> and MMC works now fine without spam. I also did a longer stress test
> compiling Python natively on a file system on MMC. Also briefly checked
> with the PREEMPT=y kernel config and at least the rootfs got mounted
> fine. So:
> 
> Tested-by: Aaro Koskinen <aaro.koskinen@iki.fi>
> 

Thanks a lot for the good testing!
Ezequiel Garcia Nov. 21, 2018, 1:20 p.m. UTC | #4
On Tuesday, November 20, 2018 19:19 -03, Ulf Hansson <ulf.hansson@linaro.org> wrote: 
 
> On 20 November 2018 at 19:21, Ezequiel Garcia <ezequiel@collabora.com> wrote:
> > As reported by Aaro, the JZ4740 MMC driver throws a warning
> > when the kernel is built without preemption (CONFIG_PREEMPT_NONE=y).
> >
> > [   16.461094] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 567 host->next_data.cookie 568
> > [   16.473120] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 568 host->next_data.cookie 569
> > [   16.485144] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 569 host->next_data.cookie 570
> > [   16.497170] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 570 host->next_data.cookie 571
> > [   16.509194] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 571 host->next_data.cookie 572
> > [   16.532421] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 572 host->next_data.cookie 573
> > [   16.544594] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 573 host->next_data.cookie 574
> > [   16.556621] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 574 host->next_data.cookie 575
> > [   16.568638] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 575 host->next_data.cookie 576
> > [   16.601092] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 582 host->next_data.cookie 583
> >
> > The problem seems to be related to how pre_req/post_req is implemented.
> > Currently, it seems the driver expects jz4740_mmc_prepare_dma_data()
> > to be called with monotonically increasing host_cookie values,
> > which is wrong.
> >
> > Moreover, the implementation is overly complicated, keeping track
> > of unneeded "next cookie" state.
> >
> > So, instead of attempting to fix the current pre_req/post_req
> > implementation, this commit refactors the driver, dropping
> > the state, following other drivers such as dw_mmc and sdhci.
> >
> > Cc: Paul Cercueil <paul@crapouillou.net>
> > Cc: Mathieu Malaterre <malat@debian.org>
> > Reported-by: Aaro Koskinen <aaro.koskinen@iki.fi>
> > Signed-off-by: Ezequiel Garcia <ezequiel@collabora.com>
> 
> This looks good to me, however because of the rather big changes I
> doubt this can be tagged for stable - or did you have a try to apply
> it for an old kernel?
> 
> In either case, I can queue this up for next, which anyway seems like
> it should be sufficient. Right?
> 

Yes, that is enough for me.

If anyone is interested in a fix for stable, it might go with
disabling pre_req/post_req altogether.

> Kind regards
> Uffe
> 
> > ---
> >  drivers/mmc/host/jz4740_mmc.c | 118 ++++++++++++++++------------------
> >  1 file changed, 54 insertions(+), 64 deletions(-)
> >
> > diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
> > index 0c1efd5100b7..ca20e2f81be8 100644
> > --- a/drivers/mmc/host/jz4740_mmc.c
> > +++ b/drivers/mmc/host/jz4740_mmc.c
> > @@ -126,9 +126,23 @@ enum jz4740_mmc_state {
> >         JZ4740_MMC_STATE_DONE,
> >  };
> >
> > -struct jz4740_mmc_host_next {
> > -       int sg_len;
> > -       s32 cookie;
> > +/*
> > + * The MMC core allows to prepare a mmc_request while another mmc_request
> > + * is in-flight. This is used via the pre_req/post_req hooks.
> > + * This driver uses the pre_req/post_req hooks to map/unmap the mmc_request.
> > + * Following what other drivers do (sdhci, dw_mmc) we use the following cookie
> > + * flags to keep track of the mmc_request mapping state.
> > + *
> > + * COOKIE_UNMAPPED: the request is not mapped.
> > + * COOKIE_PREMAPPED: the request was mapped in pre_req,
> > + * and should be unmapped in post_req.
> > + * COOKIE_MAPPED: the request was mapped in the irq handler,
> > + * and should be unmapped before mmc_request_done is called..
> > + */
> > +enum jz4780_cookie {
> > +       COOKIE_UNMAPPED = 0,
> > +       COOKIE_PREMAPPED,
> > +       COOKIE_MAPPED,
> >  };
> >
> >  struct jz4740_mmc_host {
> > @@ -162,9 +176,7 @@ struct jz4740_mmc_host {
> >         /* DMA support */
> >         struct dma_chan *dma_rx;
> >         struct dma_chan *dma_tx;
> > -       struct jz4740_mmc_host_next next_data;
> >         bool use_dma;
> > -       int sg_len;
> >
> >  /* The DMA trigger level is 8 words, that is to say, the DMA read
> >   * trigger is when data words in MSC_RXFIFO is >= 8 and the DMA write
> > @@ -226,9 +238,6 @@ static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host)
> >                 return PTR_ERR(host->dma_rx);
> >         }
> >
> > -       /* Initialize DMA pre request cookie */
> > -       host->next_data.cookie = 1;
> > -
> >         return 0;
> >  }
> >
> > @@ -245,60 +254,44 @@ static void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host,
> >         enum dma_data_direction dir = mmc_get_dma_dir(data);
> >
> >         dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);
> > +       data->host_cookie = COOKIE_UNMAPPED;
> >  }
> >
> > -/* Prepares DMA data for current/next transfer, returns non-zero on failure */
> > +/* Prepares DMA data for current or next transfer.
> > + * A request can be in-flight when this is called.
> > + */
> >  static int jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host *host,
> >                                        struct mmc_data *data,
> > -                                      struct jz4740_mmc_host_next *next,
> > -                                      struct dma_chan *chan)
> > +                                      int cookie)
> >  {
> > -       struct jz4740_mmc_host_next *next_data = &host->next_data;
> > +       struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
> >         enum dma_data_direction dir = mmc_get_dma_dir(data);
> > -       int sg_len;
> > -
> > -       if (!next && data->host_cookie &&
> > -           data->host_cookie != host->next_data.cookie) {
> > -               dev_warn(mmc_dev(host->mmc),
> > -                        "[%s] invalid cookie: data->host_cookie %d host->next_data.cookie %d\n",
> > -                        __func__,
> > -                        data->host_cookie,
> > -                        host->next_data.cookie);
> > -               data->host_cookie = 0;
> > -       }
> > +       int sg_count;
> >
> > -       /* Check if next job is already prepared */
> > -       if (next || data->host_cookie != host->next_data.cookie) {
> > -               sg_len = dma_map_sg(chan->device->dev,
> > -                                   data->sg,
> > -                                   data->sg_len,
> > -                                   dir);
> > +       if (data->host_cookie == COOKIE_PREMAPPED)
> > +               return data->sg_count;
> >
> > -       } else {
> > -               sg_len = next_data->sg_len;
> > -               next_data->sg_len = 0;
> > -       }
> > +       sg_count = dma_map_sg(chan->device->dev,
> > +                       data->sg,
> > +                       data->sg_len,
> > +                       dir);
> >
> > -       if (sg_len <= 0) {
> > +       if (sg_count <= 0) {
> >                 dev_err(mmc_dev(host->mmc),
> >                         "Failed to map scatterlist for DMA operation\n");
> >                 return -EINVAL;
> >         }
> >
> > -       if (next) {
> > -               next->sg_len = sg_len;
> > -               data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
> > -       } else
> > -               host->sg_len = sg_len;
> > +       data->sg_count = sg_count;
> > +       data->host_cookie = cookie;
> >
> > -       return 0;
> > +       return data->sg_count;
> >  }
> >
> >  static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
> >                                          struct mmc_data *data)
> >  {
> > -       int ret;
> > -       struct dma_chan *chan;
> > +       struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
> >         struct dma_async_tx_descriptor *desc;
> >         struct dma_slave_config conf = {
> >                 .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
> > @@ -306,29 +299,26 @@ static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
> >                 .src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
> >                 .dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
> >         };
> > +       int sg_count;
> >
> >         if (data->flags & MMC_DATA_WRITE) {
> >                 conf.direction = DMA_MEM_TO_DEV;
> >                 conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO;
> >                 conf.slave_id = JZ4740_DMA_TYPE_MMC_TRANSMIT;
> > -               chan = host->dma_tx;
> >         } else {
> >                 conf.direction = DMA_DEV_TO_MEM;
> >                 conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO;
> >                 conf.slave_id = JZ4740_DMA_TYPE_MMC_RECEIVE;
> > -               chan = host->dma_rx;
> >         }
> >
> > -       ret = jz4740_mmc_prepare_dma_data(host, data, NULL, chan);
> > -       if (ret)
> > -               return ret;
> > +       sg_count = jz4740_mmc_prepare_dma_data(host, data, COOKIE_MAPPED);
> > +       if (sg_count < 0)
> > +               return sg_count;
> >
> >         dmaengine_slave_config(chan, &conf);
> > -       desc = dmaengine_prep_slave_sg(chan,
> > -                                      data->sg,
> > -                                      host->sg_len,
> > -                                      conf.direction,
> > -                                      DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> > +       desc = dmaengine_prep_slave_sg(chan, data->sg, sg_count,
> > +                       conf.direction,
> > +                       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> >         if (!desc) {
> >                 dev_err(mmc_dev(host->mmc),
> >                         "Failed to allocate DMA %s descriptor",
> > @@ -342,7 +332,8 @@ static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
> >         return 0;
> >
> >  dma_unmap:
> > -       jz4740_mmc_dma_unmap(host, data);
> > +       if (data->host_cookie == COOKIE_MAPPED)
> > +               jz4740_mmc_dma_unmap(host, data);
> >         return -ENOMEM;
> >  }
> >
> > @@ -351,16 +342,13 @@ static void jz4740_mmc_pre_request(struct mmc_host *mmc,
> >  {
> >         struct jz4740_mmc_host *host = mmc_priv(mmc);
> >         struct mmc_data *data = mrq->data;
> > -       struct jz4740_mmc_host_next *next_data = &host->next_data;
> >
> > -       BUG_ON(data->host_cookie);
> > -
> > -       if (host->use_dma) {
> > -               struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
> > +       if (!host->use_dma)
> > +               return;
> >
> > -               if (jz4740_mmc_prepare_dma_data(host, data, next_data, chan))
> > -                       data->host_cookie = 0;
> > -       }
> > +       data->host_cookie = COOKIE_UNMAPPED;
> > +       if (jz4740_mmc_prepare_dma_data(host, data, COOKIE_PREMAPPED) < 0)
> > +               data->host_cookie = COOKIE_UNMAPPED;
> >  }
> >
> >  static void jz4740_mmc_post_request(struct mmc_host *mmc,
> > @@ -370,10 +358,8 @@ static void jz4740_mmc_post_request(struct mmc_host *mmc,
> >         struct jz4740_mmc_host *host = mmc_priv(mmc);
> >         struct mmc_data *data = mrq->data;
> >
> > -       if (host->use_dma && data->host_cookie) {
> > +       if (data && data->host_cookie != COOKIE_UNMAPPED)
> >                 jz4740_mmc_dma_unmap(host, data);
> > -               data->host_cookie = 0;
> > -       }
> >
> >         if (err) {
> >                 struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
> > @@ -436,10 +422,14 @@ static void jz4740_mmc_reset(struct jz4740_mmc_host *host)
> >  static void jz4740_mmc_request_done(struct jz4740_mmc_host *host)
> >  {
> >         struct mmc_request *req;
> > +       struct mmc_data *data;
> >
> >         req = host->req;
> > +       data = req->data;
> >         host->req = NULL;
> >
> > +       if (data && data->host_cookie == COOKIE_MAPPED)
> > +               jz4740_mmc_dma_unmap(host, data);
> >         mmc_request_done(host->mmc, req);
> >  }
> >
> > --
> > 2.19.1
> >
Ulf Hansson Dec. 5, 2018, 2:23 p.m. UTC | #5
On Wed, 21 Nov 2018 at 14:20, Ezequiel Garcia <ezequiel@collabora.com> wrote:
>
> On Tuesday, November 20, 2018 19:19 -03, Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> > On 20 November 2018 at 19:21, Ezequiel Garcia <ezequiel@collabora.com> wrote:
> > > As reported by Aaro, the JZ4740 MMC driver throws a warning
> > > when the kernel is built without preemption (CONFIG_PREEMPT_NONE=y).
> > >
> > > [   16.461094] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 567 host->next_data.cookie 568
> > > [   16.473120] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 568 host->next_data.cookie 569
> > > [   16.485144] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 569 host->next_data.cookie 570
> > > [   16.497170] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 570 host->next_data.cookie 571
> > > [   16.509194] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 571 host->next_data.cookie 572
> > > [   16.532421] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 572 host->next_data.cookie 573
> > > [   16.544594] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 573 host->next_data.cookie 574
> > > [   16.556621] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 574 host->next_data.cookie 575
> > > [   16.568638] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 575 host->next_data.cookie 576
> > > [   16.601092] jz4740-mmc 13450000.mmc: [jz4740_mmc_prepare_dma_data] invalid cookie: data->host_cookie 582 host->next_data.cookie 583
> > >
> > > The problem seems to be related to how pre_req/post_req is implemented.
> > > Currently, it seems the driver expects jz4740_mmc_prepare_dma_data()
> > > to be called with monotonically increasing host_cookie values,
> > > which is wrong.
> > >
> > > Moreover, the implementation is overly complicated, keeping track
> > > of unneeded "next cookie" state.
> > >
> > > So, instead of attempting to fix the current pre_req/post_req
> > > implementation, this commit refactors the driver, dropping
> > > the state, following other drivers such as dw_mmc and sdhci.
> > >
> > > Cc: Paul Cercueil <paul@crapouillou.net>
> > > Cc: Mathieu Malaterre <malat@debian.org>
> > > Reported-by: Aaro Koskinen <aaro.koskinen@iki.fi>
> > > Signed-off-by: Ezequiel Garcia <ezequiel@collabora.com>
> >
> > This looks good to me, however because of the rather big changes I
> > doubt this can be tagged for stable - or did you have a try to apply
> > it for an old kernel?
> >
> > In either case, I can queue this up for next, which anyway seems like
> > it should be sufficient. Right?
> >
>
> Yes, that is enough for me.
>
> If anyone is interested in a fix for stable, it might go with
> disabling pre_req/post_req altogether.

Great!

[...]

Applied for next then, thanks!

Kind regards
Uffe
diff mbox series

Patch

diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index 0c1efd5100b7..ca20e2f81be8 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -126,9 +126,23 @@  enum jz4740_mmc_state {
 	JZ4740_MMC_STATE_DONE,
 };
 
-struct jz4740_mmc_host_next {
-	int sg_len;
-	s32 cookie;
+/*
+ * The MMC core allows to prepare a mmc_request while another mmc_request
+ * is in-flight. This is used via the pre_req/post_req hooks.
+ * This driver uses the pre_req/post_req hooks to map/unmap the mmc_request.
+ * Following what other drivers do (sdhci, dw_mmc) we use the following cookie
+ * flags to keep track of the mmc_request mapping state.
+ *
+ * COOKIE_UNMAPPED: the request is not mapped.
+ * COOKIE_PREMAPPED: the request was mapped in pre_req,
+ * and should be unmapped in post_req.
+ * COOKIE_MAPPED: the request was mapped in the irq handler,
+ * and should be unmapped before mmc_request_done is called..
+ */
+enum jz4780_cookie {
+	COOKIE_UNMAPPED = 0,
+	COOKIE_PREMAPPED,
+	COOKIE_MAPPED,
 };
 
 struct jz4740_mmc_host {
@@ -162,9 +176,7 @@  struct jz4740_mmc_host {
 	/* DMA support */
 	struct dma_chan *dma_rx;
 	struct dma_chan *dma_tx;
-	struct jz4740_mmc_host_next next_data;
 	bool use_dma;
-	int sg_len;
 
 /* The DMA trigger level is 8 words, that is to say, the DMA read
  * trigger is when data words in MSC_RXFIFO is >= 8 and the DMA write
@@ -226,9 +238,6 @@  static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host)
 		return PTR_ERR(host->dma_rx);
 	}
 
-	/* Initialize DMA pre request cookie */
-	host->next_data.cookie = 1;
-
 	return 0;
 }
 
@@ -245,60 +254,44 @@  static void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host,
 	enum dma_data_direction dir = mmc_get_dma_dir(data);
 
 	dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);
+	data->host_cookie = COOKIE_UNMAPPED;
 }
 
-/* Prepares DMA data for current/next transfer, returns non-zero on failure */
+/* Prepares DMA data for current or next transfer.
+ * A request can be in-flight when this is called.
+ */
 static int jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host *host,
 				       struct mmc_data *data,
-				       struct jz4740_mmc_host_next *next,
-				       struct dma_chan *chan)
+				       int cookie)
 {
-	struct jz4740_mmc_host_next *next_data = &host->next_data;
+	struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
 	enum dma_data_direction dir = mmc_get_dma_dir(data);
-	int sg_len;
-
-	if (!next && data->host_cookie &&
-	    data->host_cookie != host->next_data.cookie) {
-		dev_warn(mmc_dev(host->mmc),
-			 "[%s] invalid cookie: data->host_cookie %d host->next_data.cookie %d\n",
-			 __func__,
-			 data->host_cookie,
-			 host->next_data.cookie);
-		data->host_cookie = 0;
-	}
+	int sg_count;
 
-	/* Check if next job is already prepared */
-	if (next || data->host_cookie != host->next_data.cookie) {
-		sg_len = dma_map_sg(chan->device->dev,
-				    data->sg,
-				    data->sg_len,
-				    dir);
+	if (data->host_cookie == COOKIE_PREMAPPED)
+		return data->sg_count;
 
-	} else {
-		sg_len = next_data->sg_len;
-		next_data->sg_len = 0;
-	}
+	sg_count = dma_map_sg(chan->device->dev,
+			data->sg,
+			data->sg_len,
+			dir);
 
-	if (sg_len <= 0) {
+	if (sg_count <= 0) {
 		dev_err(mmc_dev(host->mmc),
 			"Failed to map scatterlist for DMA operation\n");
 		return -EINVAL;
 	}
 
-	if (next) {
-		next->sg_len = sg_len;
-		data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
-	} else
-		host->sg_len = sg_len;
+	data->sg_count = sg_count;
+	data->host_cookie = cookie;
 
-	return 0;
+	return data->sg_count;
 }
 
 static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
 					 struct mmc_data *data)
 {
-	int ret;
-	struct dma_chan *chan;
+	struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
 	struct dma_async_tx_descriptor *desc;
 	struct dma_slave_config conf = {
 		.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
@@ -306,29 +299,26 @@  static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
 		.src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
 		.dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
 	};
+	int sg_count;
 
 	if (data->flags & MMC_DATA_WRITE) {
 		conf.direction = DMA_MEM_TO_DEV;
 		conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO;
 		conf.slave_id = JZ4740_DMA_TYPE_MMC_TRANSMIT;
-		chan = host->dma_tx;
 	} else {
 		conf.direction = DMA_DEV_TO_MEM;
 		conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO;
 		conf.slave_id = JZ4740_DMA_TYPE_MMC_RECEIVE;
-		chan = host->dma_rx;
 	}
 
-	ret = jz4740_mmc_prepare_dma_data(host, data, NULL, chan);
-	if (ret)
-		return ret;
+	sg_count = jz4740_mmc_prepare_dma_data(host, data, COOKIE_MAPPED);
+	if (sg_count < 0)
+		return sg_count;
 
 	dmaengine_slave_config(chan, &conf);
-	desc = dmaengine_prep_slave_sg(chan,
-				       data->sg,
-				       host->sg_len,
-				       conf.direction,
-				       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	desc = dmaengine_prep_slave_sg(chan, data->sg, sg_count,
+			conf.direction,
+			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
 	if (!desc) {
 		dev_err(mmc_dev(host->mmc),
 			"Failed to allocate DMA %s descriptor",
@@ -342,7 +332,8 @@  static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
 	return 0;
 
 dma_unmap:
-	jz4740_mmc_dma_unmap(host, data);
+	if (data->host_cookie == COOKIE_MAPPED)
+		jz4740_mmc_dma_unmap(host, data);
 	return -ENOMEM;
 }
 
@@ -351,16 +342,13 @@  static void jz4740_mmc_pre_request(struct mmc_host *mmc,
 {
 	struct jz4740_mmc_host *host = mmc_priv(mmc);
 	struct mmc_data *data = mrq->data;
-	struct jz4740_mmc_host_next *next_data = &host->next_data;
 
-	BUG_ON(data->host_cookie);
-
-	if (host->use_dma) {
-		struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
+	if (!host->use_dma)
+		return;
 
-		if (jz4740_mmc_prepare_dma_data(host, data, next_data, chan))
-			data->host_cookie = 0;
-	}
+	data->host_cookie = COOKIE_UNMAPPED;
+	if (jz4740_mmc_prepare_dma_data(host, data, COOKIE_PREMAPPED) < 0)
+		data->host_cookie = COOKIE_UNMAPPED;
 }
 
 static void jz4740_mmc_post_request(struct mmc_host *mmc,
@@ -370,10 +358,8 @@  static void jz4740_mmc_post_request(struct mmc_host *mmc,
 	struct jz4740_mmc_host *host = mmc_priv(mmc);
 	struct mmc_data *data = mrq->data;
 
-	if (host->use_dma && data->host_cookie) {
+	if (data && data->host_cookie != COOKIE_UNMAPPED)
 		jz4740_mmc_dma_unmap(host, data);
-		data->host_cookie = 0;
-	}
 
 	if (err) {
 		struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
@@ -436,10 +422,14 @@  static void jz4740_mmc_reset(struct jz4740_mmc_host *host)
 static void jz4740_mmc_request_done(struct jz4740_mmc_host *host)
 {
 	struct mmc_request *req;
+	struct mmc_data *data;
 
 	req = host->req;
+	data = req->data;
 	host->req = NULL;
 
+	if (data && data->host_cookie == COOKIE_MAPPED)
+		jz4740_mmc_dma_unmap(host, data);
 	mmc_request_done(host->mmc, req);
 }