From patchwork Wed Dec 14 15:06:10 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ulf Hansson X-Patchwork-Id: 5698 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id D6F9B23F87 for ; Wed, 14 Dec 2011 15:07:05 +0000 (UTC) Received: from mail-ey0-f180.google.com (mail-ey0-f180.google.com [209.85.215.180]) by fiordland.canonical.com (Postfix) with ESMTP id CA524A18050 for ; Wed, 14 Dec 2011 15:07:05 +0000 (UTC) Received: by mail-ey0-f180.google.com with SMTP id k10so702291eaa.11 for ; Wed, 14 Dec 2011 07:07:05 -0800 (PST) Received: by 10.204.131.74 with SMTP id w10mr891357bks.36.1323875225605; Wed, 14 Dec 2011 07:07:05 -0800 (PST) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.205.129.2 with SMTP id hg2cs11242bkc; Wed, 14 Dec 2011 07:07:05 -0800 (PST) Received: by 10.152.104.130 with SMTP id ge2mr2926222lab.43.1323875224016; Wed, 14 Dec 2011 07:07:04 -0800 (PST) Received: from eu1sys200aog116.obsmtp.com (eu1sys200aog116.obsmtp.com. [207.126.144.141]) by mx.google.com with SMTP id 3si2170018eei.119.2011.12.14.07.07.00 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 14 Dec 2011 07:07:03 -0800 (PST) Received-SPF: neutral (google.com: 207.126.144.141 is neither permitted nor denied by best guess record for domain of ulf.hansson@stericsson.com) client-ip=207.126.144.141; Authentication-Results: mx.google.com; spf=neutral (google.com: 207.126.144.141 is neither permitted nor denied by best guess record for domain of ulf.hansson@stericsson.com) smtp.mail=ulf.hansson@stericsson.com Received: from beta.dmz-eu.st.com ([164.129.1.35]) (using TLSv1) by eu1sys200aob116.postini.com ([207.126.147.11]) with SMTP ID DSNKTui7jdl23gcZdvzIlwFO8lYzFeK04/MJ@postini.com; Wed, 14 Dec 2011 15:07:03 UTC Received: from zeta.dmz-eu.st.com (zeta.dmz-eu.st.com [164.129.230.9]) by beta.dmz-eu.st.com (STMicroelectronics) with ESMTP id A7865A8; Wed, 14 Dec 2011 15:06:32 +0000 (GMT) Received: from relay2.stm.gmessaging.net (unknown [10.230.100.18]) by zeta.dmz-eu.st.com (STMicroelectronics) with ESMTP id 10B1D2D07; Wed, 14 Dec 2011 15:06:32 +0000 (GMT) Received: from exdcvycastm022.EQ1STM.local (alteon-source-exch [10.230.100.61]) (using TLSv1 with cipher RC4-MD5 (128/128 bits)) (Client CN "exdcvycastm022", Issuer "exdcvycastm022" (not verified)) by relay2.stm.gmessaging.net (Postfix) with ESMTPS id E6F57A807D; Wed, 14 Dec 2011 16:06:25 +0100 (CET) Received: from localhost.localdomain (10.230.100.153) by smtp.stericsson.com (10.230.100.30) with Microsoft SMTP Server (TLS) id 8.3.83.0; Wed, 14 Dec 2011 16:06:31 +0100 From: Ulf Hansson To: , Chris Ball Cc: Per Forlin , Ulf Hansson , Johan Rudholm , Lee Jones Subject: [PATCH V2 2/2] mmc: Minimize resume-time by deferring resume Date: Wed, 14 Dec 2011 16:06:10 +0100 Message-ID: <1323875170-7103-3-git-send-email-ulf.hansson@stericsson.com> X-Mailer: git-send-email 1.7.5.4 In-Reply-To: <1323875170-7103-1-git-send-email-ulf.hansson@stericsson.com> References: <1323875170-7103-1-git-send-email-ulf.hansson@stericsson.com> MIME-Version: 1.0 Typically an sd/mmc card takes around 200 - 1100 ms to initialize when the power to the card has been cut, which is what happens during a suspend/resume sequence. All device's resume time adds up to the total kernel resume time. Some use cases requires the kernel to be resumed fast, to be able to meet deadlines. One use case example is WLAN SOFT_AP, but there are certainly more. This patch schedules a delayed work to do a deferred resume of the mmc host, if the bus holds a card of SD or MMC type. The reason for not supporting SDIO and SDcombo cards at this stage, is because the SDIO API is synchronus, which complicates request locking mechanism when waiting for a deferred resume to be completed. While waiting for a deferred resume to be completed, detect works are prevented from doing a new rescan. If a mmcblk request arrives, the deferred resume will be synced immediately. The deferred resume is scheduled 3000 ms after the resume request arrived. The idea behind this timer value is to let the mmc host being able to accept a new suspend request before it has been deferred resumed and thus not increase the resume to suspend time if not really needed. Signed-off-by: Ulf Hansson --- Changes in v2: - Rebased patch on latest version of mmc-next --- drivers/mmc/card/block.c | 8 ++++++++ drivers/mmc/core/core.c | 36 +++++++++++++++++++++++++++++++++++- drivers/mmc/core/core.h | 1 + drivers/mmc/core/host.c | 1 + include/linux/mmc/host.h | 16 ++++++++++++++++ 5 files changed, 61 insertions(+), 1 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 4b62d1f..a4d8ae4 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1364,6 +1364,14 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; + /* + * We must make sure we have not claimed the host before + * doing a flush to prevent deadlock, thus we check if + * the host needs a resume first. + */ + if (mmc_host_needs_resume(card->host)) + mmc_resume_host_sync(card->host); + if (req && !mq->mqrq_prev->req) /* claim host only for the first request */ mmc_claim_host(card->host); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index a2aa860..4706284 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2099,7 +2099,7 @@ void mmc_rescan(struct work_struct *work) container_of(work, struct mmc_host, detect.work); int i; - if (host->rescan_disable) + if (host->rescan_disable || mmc_host_needs_resume(host)) return; mmc_bus_get(host); @@ -2354,7 +2354,13 @@ int mmc_suspend_host(struct mmc_host *host) if (host->caps & MMC_CAP_DISABLE) cancel_delayed_work(&host->disable); cancel_delayed_work(&host->detect); + cancel_delayed_work_sync(&host->resume); mmc_flush_scheduled_work(); + + /* Skip suspend, if deferred resume were scheduled but not completed. */ + if (mmc_host_needs_resume(host)) + return 0; + err = mmc_cache_ctrl(host, 0); if (err) goto out; @@ -2393,6 +2399,10 @@ int mmc_suspend_host(struct mmc_host *host) mmc_release_host(host); host->pm_flags = 0; err = 0; + } else if (mmc_card_mmc(host->card) || + mmc_card_sd(host->card)) { + host->pm_state |= MMC_HOST_DEFERRED_RESUME | + MMC_HOST_NEEDS_RESUME; } } else { err = -EBUSY; @@ -2417,6 +2427,12 @@ int mmc_resume_host(struct mmc_host *host) { int err = 0; + if (mmc_host_deferred_resume(host)) { + mmc_schedule_delayed_work(&host->resume, + msecs_to_jiffies(3000)); + return 0; + } + mmc_bus_get(host); if (host->bus_ops && !host->bus_dead) { if (!mmc_card_keep_power(host)) { @@ -2451,6 +2467,24 @@ int mmc_resume_host(struct mmc_host *host) } EXPORT_SYMBOL(mmc_resume_host); +void mmc_resume_work(struct work_struct *work) +{ + struct mmc_host *host = + container_of(work, struct mmc_host, resume.work); + + host->pm_state &= ~MMC_HOST_DEFERRED_RESUME; + mmc_resume_host(host); + host->pm_state &= ~MMC_HOST_NEEDS_RESUME; + + mmc_detect_change(host, 0); +} + +void mmc_resume_host_sync(struct mmc_host *host) +{ + flush_delayed_work_sync(&host->resume); +} +EXPORT_SYMBOL(mmc_resume_host_sync); + /* Do the card removal on suspend if card is assumed removeable * Do that in pm notifier while userspace isn't yet frozen, so we will be able to sync the card. diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 3400924..c894a3f 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -59,6 +59,7 @@ static inline void mmc_delay(unsigned int ms) void mmc_rescan(struct work_struct *work); void mmc_start_host(struct mmc_host *host); void mmc_stop_host(struct mmc_host *host); +void mmc_resume_work(struct work_struct *work); int _mmc_detect_card_removed(struct mmc_host *host); diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index c152ce0..d6f955a 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -331,6 +331,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) init_waitqueue_head(&host->wq); INIT_DELAYED_WORK(&host->detect, mmc_rescan); INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable); + INIT_DELAYED_WORK(&host->resume, mmc_resume_work); #ifdef CONFIG_PM host->pm_notify.notifier_call = mmc_pm_notify; #endif diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 9a03d03..90dd299 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -301,6 +301,11 @@ struct mmc_host { struct delayed_work detect; int detect_change; /* card detect flag */ + struct delayed_work resume; /* deferred resume work */ + unsigned int pm_state; /* used for deferred resume */ +#define MMC_HOST_DEFERRED_RESUME (1 << 0) +#define MMC_HOST_NEEDS_RESUME (1 << 1) + const struct mmc_bus_ops *bus_ops; /* current bus driver */ unsigned int bus_refs; /* reference counter */ @@ -349,6 +354,7 @@ static inline void *mmc_priv(struct mmc_host *host) extern int mmc_suspend_host(struct mmc_host *); extern int mmc_resume_host(struct mmc_host *); +extern void mmc_resume_host_sync(struct mmc_host *); extern int mmc_power_save_host(struct mmc_host *host); extern int mmc_power_restore_host(struct mmc_host *host); @@ -428,4 +434,14 @@ static inline int mmc_boot_partition_access(struct mmc_host *host) return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC); } +static inline int mmc_host_deferred_resume(struct mmc_host *host) +{ + return host->pm_state & MMC_HOST_DEFERRED_RESUME; +} + +static inline int mmc_host_needs_resume(struct mmc_host *host) +{ + return host->pm_state & MMC_HOST_NEEDS_RESUME; +} + #endif /* LINUX_MMC_HOST_H */