From patchwork Tue Sep 28 11:10:50 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ethan Du X-Patchwork-Id: 214612 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id o8SB9hup021525 for ; Tue, 28 Sep 2010 11:10:52 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751987Ab0I1LKv (ORCPT ); Tue, 28 Sep 2010 07:10:51 -0400 Received: from mail-gx0-f174.google.com ([209.85.161.174]:62833 "EHLO mail-gx0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751221Ab0I1LKv (ORCPT ); Tue, 28 Sep 2010 07:10:51 -0400 Received: by gxk9 with SMTP id 9so1864040gxk.19 for ; Tue, 28 Sep 2010 04:10:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:received:in-reply-to :references:date:message-id:subject:from:to:cc:content-type; bh=6wVuQaPGDo8ltvMlpQwsTSFXqY3mnC+4dV01G4kM1Q8=; b=qM7wiI6U8XqDxq/S+ZcktDzQ3oUsojVe1Piz0uegHsj4QF7IWFWC4L1IfP3+oT1GGk izQL7EYrA4VXXudINEbLttTQv6PEwLjTZSqa7lMMSLawQaa8+M2M/Sa5wC6a6g2v3skh 2Wnft5O/C8Yf0Nb08ttXYu31DU50G35BvV6A8= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; b=dBBVpQ/StjR5VFUxTh8SFBQveHXl3x1hSjbhMEUogAXj82LeGXlJlIa2QbwRJgkGWY gTtfwMSwoPhfc2yTkpDHeTiiyRFPIzPz/5dGZSwzNA6x3+GUaZu6RpVUBfsLs4fGgNh+ iciw03EsiMRI3xLsxN8MCe481A3ia1DaJv0U8= MIME-Version: 1.0 Received: by 10.150.202.4 with SMTP id z4mr10624719ybf.317.1285672250343; Tue, 28 Sep 2010 04:10:50 -0700 (PDT) Received: by 10.220.97.80 with HTTP; Tue, 28 Sep 2010 04:10:50 -0700 (PDT) In-Reply-To: References: <4CA1905D.5010701@nokia.com> Date: Tue, 28 Sep 2010 19:10:50 +0800 Message-ID: Subject: [PATCH v2] MMC: Refine block layer waiting for card state From: Ethan Du To: linux-mmc Cc: Adrian Hunter Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Tue, 28 Sep 2010 11:10:52 +0000 (UTC) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index d545f79..cc28a20 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -316,12 +316,14 @@ out: return err ? 0 : 1; } +#define BUSY_TIMEOUT_MS (16 * 1024) static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; struct mmc_blk_request brq; int ret = 1, disable_multi = 0; + unsigned long timeout = 0; mmc_claim_host(card->host); @@ -453,7 +455,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) brq.stop.resp[0], status); } - if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) { + if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ && + !brq.cmd.error && !brq.data.error && !brq.stop.error) { + timeout = jiffies + msecs_to_jiffies(BUSY_TIMEOUT_MS); do { int err; @@ -466,13 +470,22 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) req->rq_disk->disk_name, err); goto cmd_err; } + if (cmd.resp[0] & R1_ERROR_MASK) { + printk(KERN_ERR "%s: card err %#x\n", + req->rq_disk->disk_name, + cmd.resp[0]); + goto cmd_err; + } /* * Some cards mishandle the status bits, * so make sure to check both the busy * indication and the card state. */ - } while (!(cmd.resp[0] & R1_READY_FOR_DATA) || - (R1_CURRENT_STATE(cmd.resp[0]) == 7)); + if ((cmd.resp[0] & R1_READY_FOR_DATA) && + (R1_CURRENT_STATE(cmd.resp[0]) != 7)) + break; + } while (time_before(jiffies, timeout)); + /* Ignore timeout out */ #if 0 if (cmd.resp[0] & ~0x00000900) @@ -510,11 +523,11 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) return 1; - cmd_err: - /* - * If this is an SD card and we're writing, we can first - * mark the known good sectors as ok. - * +cmd_err: + /* + * If this is an SD card and we're writing, we can first + * mark the known good sectors as ok. + * * If the card is not SD, we can still ok written sectors * as reported by the controller (which might be less than * the real number of written sectors, but never more). @@ -534,6 +547,13 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) spin_unlock_irq(&md->lock); } + card->err_count++; + if (card->err_count >= ERR_TRIGGER_REINIT) { + card->err_count = 0; + printk(KERN_WARNING "%s: re-init the card due to error\n", + md->disk->disk_name); + mmc_reinit_host(card->host); + } mmc_release_host(card->host); spin_lock_irq(&md->lock); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 5db49b1..b61de33 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1671,6 +1671,44 @@ int mmc_resume_host(struct mmc_host *host) } EXPORT_SYMBOL(mmc_resume_host); +/** + * mmc_reinit_host - reinit a host + * @host: mmc host + */ +int mmc_reinit_host(struct mmc_host *host) +{ + int err = 0; + + mmc_bus_get(host); + + if (!host->bus_ops->resume) + return 0; + + if (host->bus_ops && !host->bus_dead) { + if (!(host->pm_flags & MMC_PM_KEEP_POWER)) { + mmc_power_up(host); + mmc_select_voltage(host, host->ocr); + } + err = host->bus_ops->resume(host); + if (err) { + printk(KERN_WARNING "%s: error %d during resume " + "(card was removed?)\n", + mmc_hostname(host), err); + err = 0; + } + } + mmc_bus_put(host); + + /* + * We add a slight delay here so that resume can progress + * in parallel. + */ + mmc_detect_change(host, 1); + + return err; +} +EXPORT_SYMBOL(mmc_reinit_host); + /* 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/include/linux/mmc/card.h b/include/linux/mmc/card.h index 6b75250..178de17 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -143,6 +143,9 @@ struct mmc_card { const char **info; /* info strings */ struct sdio_func_tuple *tuples; /* unknown common tuples */ + unsigned int err_count; +#define ERR_TRIGGER_REINIT 1024 + struct dentry *debugfs_root; }; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 1575b52..cff9c69 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -235,6 +235,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 int mmc_reinit_host(struct mmc_host *); extern void mmc_power_save_host(struct mmc_host *host); extern void mmc_power_restore_host(struct mmc_host *host); diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index dd11ae5..3b979a1 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -122,6 +122,7 @@ #define R1_UNDERRUN (1 << 18) /* ex, c */ #define R1_OVERRUN (1 << 17) /* ex, c */ #define R1_CID_CSD_OVERWRITE (1 << 16) /* erx, c, CID/CSD overwrite */ +#define R1_ERROR_MASK 0xffff0000 #define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */ #define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */ #define R1_ERASE_RESET (1 << 13) /* sr, c */