Re: [PATCH v2 1/1]mmc: check SDIO card wake up during suspending

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Jun,

On Thu, Nov 18, 2010 at 4:20 AM, Jun Nie <niej0001@xxxxxxxxx> wrote:
> It is possible that SDIO card send card interrupt to wake up
> system just after SDIO func driver and SDIO card firmware commit
> suspend state, while system is still in suspending process and
> mmc interrupt is not release yet.
>
> Interrupt storm may happen if func driver does not notify firmware
> to clear the wake up source. If func driver issue SDIO bus transaction
> to have firmware clear the source and driver continue to handle the
> wake up event, system may enter suspend before all transaction done.
> In this case, SDIO card will not enter lower power mode never. But
> system can not be wake up for only card insert/remove/interrupt irq are
> listed in wake up source in SD spec. Data/command done irq can
> not wake up system.
>
> This patch check SDIO card interrupt and stop system suspend if there
> are card interrupt after func driver suspend.

We already have a kernel framework for dealing with similar race
conditions, check out mainline commit c125e96 "PM: Make it possible to
avoid races between wakeup and system sleep" and
drivers/base/power/wakeup.c.

Does this satisfy your requirement ? It seems that it can
significantly simplify your patch (in essence, calling
pm_wakeup_event() will request PM core to abort system transition into
suspend).

Thanks,
Ohad.

>
> Signed-off-by: Jun Nie <njun@xxxxxxxxxxx>
> ---
>  drivers/mmc/core/sdio.c       |   14 ++++++++++++++
>  drivers/mmc/core/sdio_irq.c   |    6 ++++++
>  drivers/mmc/host/sdhci.c      |   18 ++++++++++++++++--
>  include/linux/mmc/card.h      |    2 ++
>  include/linux/mmc/sdio_func.h |    2 ++
>  5 files changed, 40 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
> index c3ad105..48a4106 100644
> --- a/drivers/mmc/core/sdio.c
> +++ b/drivers/mmc/core/sdio.c
> @@ -584,6 +584,14 @@ static int mmc_sdio_suspend(struct mmc_host *host)
>
>        for (i = 0; i < host->card->sdio_funcs; i++) {
>                struct sdio_func *func = host->card->sdio_func[i];
> +               /* cancel suspend once we detect external wake up source */
> +               if (host->card->pending_interrupt) {
> +                       host->card->pending_interrupt = 0;
> +                       err = -EBUSY;
> +                       printk(KERN_ALERT "%s: wake up event after device suspended\n",
> +                                       mmc_hostname(host));
> +                       break;
> +               }
>                if (func && sdio_func_present(func) && func->dev.driver) {
>                        const struct dev_pm_ops *pmops = func->dev.driver->pm;
>                        if (!pmops || !pmops->suspend || !pmops->resume) {
> @@ -593,10 +601,15 @@ static int mmc_sdio_suspend(struct mmc_host *host)
>                                err = pmops->suspend(&func->dev);
>                        if (err)
>                                break;
> +                       else
> +                               /* It is prefer that func driver set it in its
> +                                  suspend function with mmc_claim_host */
> +                               func->suspended = true;
>                }
>        }
>        while (err && --i >= 0) {
>                struct sdio_func *func = host->card->sdio_func[i];
> +               func->suspended = false;
>                if (func && sdio_func_present(func) && func->dev.driver) {
>                        const struct dev_pm_ops *pmops = func->dev.driver->pm;
>                        pmops->resume(&func->dev);
> @@ -639,6 +652,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
>         */
>        for (i = 0; !err && i < host->card->sdio_funcs; i++) {
>                struct sdio_func *func = host->card->sdio_func[i];
> +               func->suspended = false;
>                if (func && sdio_func_present(func) && func->dev.driver) {
>                        const struct dev_pm_ops *pmops = func->dev.driver->pm;
>                        err = pmops->resume(&func->dev);
> diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c
> index bb192f9..727a1a3 100644
> --- a/drivers/mmc/core/sdio_irq.c
> +++ b/drivers/mmc/core/sdio_irq.c
> @@ -49,6 +49,12 @@ static int process_sdio_pending_irqs(struct mmc_card *card)
>                                        mmc_card_id(card));
>                                ret = -EINVAL;
>                        } else if (func->irq_handler) {
> +                               if (func->suspended) {
> +                                       card->pending_interrupt = true;
> +                                       printk(KERN_WARNING "%s: IRQ that "
> +                                               "arrives in suspend mode"
> +                                               " mode\n", sdio_func_id(func));
> +                               }
>                                func->irq_handler(func);
>                                count++;
>                        } else {
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 782c0ee..9c8f1cc 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -24,6 +24,7 @@
>  #include <linux/leds.h>
>
>  #include <linux/mmc/host.h>
> +#include <linux/mmc/card.h>
>
>  #include "sdhci.h"
>
> @@ -1631,7 +1632,7 @@ out:
>
>  int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
>  {
> -       int ret;
> +       int ret = 0;
>
>        sdhci_disable_card_detection(host);
>
> @@ -1641,8 +1642,19 @@ int sdhci_suspend_host(struct sdhci_host *host,
> pm_message_t state)
>
>        free_irq(host->irq, host);
>
> -       if (host->vmmc)
> +       if (host->vmmc) {
>                ret = regulator_disable(host->vmmc);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       if (host->mmc->card && host->mmc->card->pending_interrupt) {
> +               DBG("%s wake up event after suspend, resuming to handle it\n",
> +                       mmc_hostname(host->mmc));
> +               host->mmc->card->pending_interrupt = 0;
> +               sdhci_resume_host(host);
> +               return -EBUSY;
> +       }
>
>        return ret;
>  }
> @@ -1659,6 +1671,8 @@ int sdhci_resume_host(struct sdhci_host *host)
>                        return ret;
>        }
>
> +       if (host->mmc->card && host->mmc->card->pending_interrupt)
> +               host->mmc->card->pending_interrupt = 0;
>
>        if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
>                if (host->ops->enable_dma)
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 8ce0827..2252fe2 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -146,6 +146,8 @@ struct mmc_card {
>        struct sdio_func_tuple  *tuples;        /* unknown common tuples */
>
>        struct dentry           *debugfs_root;
> +       /* external interrupt arrived after sdio function suspended */
> +       u32                     pending_interrupt;
>  };
>
>  #define mmc_card_mmc(c)                ((c)->type == MMC_TYPE_MMC)
> diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h
> index 31baaf8..5c1c462 100644
> --- a/include/linux/mmc/sdio_func.h
> +++ b/include/linux/mmc/sdio_func.h
> @@ -59,6 +59,8 @@ struct sdio_func {
>        const char              **info;         /* info strings */
>
>        struct sdio_func_tuple *tuples;
> +
> +       int                     suspended; /* SDIO function driver state */
>  };
>
>  #define sdio_func_present(f)   ((f)->state & SDIO_STATE_PRESENT)
> --
> 1.7.0.4
>
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux