From patchwork Fri Apr 15 18:30:47 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Guennadi Liakhovetski X-Patchwork-Id: 711251 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 p3FIUpcp017867 for ; Fri, 15 Apr 2011 18:30:53 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756417Ab1DOSaw (ORCPT ); Fri, 15 Apr 2011 14:30:52 -0400 Received: from moutng.kundenserver.de ([212.227.126.187]:61555 "EHLO moutng.kundenserver.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756407Ab1DOSav (ORCPT ); Fri, 15 Apr 2011 14:30:51 -0400 Received: from axis700.grange (pD9EB8F8A.dip0.t-ipconnect.de [217.235.143.138]) by mrelayeu.kundenserver.de (node=mrbap2) with ESMTP (Nemesis) id 0Mecsy-1QUg5X22gq-00OuFj; Fri, 15 Apr 2011 20:30:47 +0200 Received: by axis700.grange (Postfix, from userid 1000) id 347F7E6AB4; Fri, 15 Apr 2011 20:30:47 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by axis700.grange (Postfix) with ESMTP id 317C41067E6; Fri, 15 Apr 2011 20:30:47 +0200 (CEST) Date: Fri, 15 Apr 2011 20:30:47 +0200 (CEST) From: Guennadi Liakhovetski X-X-Sender: lyakh@axis700.grange To: linux-sh@vger.kernel.org cc: linux-mmc@vger.kernel.org, Magnus Damm , Simon Horman Subject: [PATCH 1/3] MMC: protect the sh_mmcif driver against a theoretical race In-Reply-To: Message-ID: References: MIME-Version: 1.0 X-Provags-ID: V02:K0:lBrGXVaxg7nik5FcXUQs7MfPvUTf9ZtWAPm3/MP2pDd eIAKFUH6nqPvzYRaYYN3wfHFw2dv51JIio3XaDCEsYhSEKR2gv jd1zDrzZNa0GD5mutCLY6FQF1SflIk4uhZlkpMy370yen8k2fk /hrjqZ8eymiNbGDrmYO+gilEJQTh/fV23VEA5E6Fox1aWoAg7k MnjhkO1PflJyooV5BcKh/uvhAWgNDWT0+xv9xNUzB8= 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.6 (demeter1.kernel.org [140.211.167.41]); Fri, 15 Apr 2011 18:30:53 +0000 (UTC) The MMC subsystem does not guarantee, that host driver .request() and .set_ios() callbacks are serialised. Such concurrent calls, however, do not have to be meaningfully supported, drivers just have to make sure to avoid any severe problems. Signed-off-by: Guennadi Liakhovetski Cc: Simon Horman Cc: Magnus Damm --- drivers/mmc/host/sh_mmcif.c | 50 ++++++++++++++++++++++++++++++++++++------- 1 files changed, 42 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index af97015..d3871b6 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -29,6 +29,7 @@ #include #include #include +#include #define DRIVER_NAME "sh_mmcif" #define DRIVER_VERSION "2010-04-28" @@ -153,6 +154,12 @@ #define CLKDEV_MMC_DATA 20000000 /* 20MHz */ #define CLKDEV_INIT 400000 /* 400 KHz */ +enum mmcif_state { + STATE_IDLE, + STATE_REQUEST, + STATE_IOS, +}; + struct sh_mmcif_host { struct mmc_host *mmc; struct mmc_data *data; @@ -164,6 +171,8 @@ struct sh_mmcif_host { long timeout; void __iomem *addr; struct completion intr_wait; + enum mmcif_state state; + spinlock_t lock; /* DMA support */ struct dma_chan *chan_rx; @@ -798,17 +807,31 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host, static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct sh_mmcif_host *host = mmc_priv(mmc); + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + if (host->state != STATE_IDLE) { + spin_unlock_irqrestore(&host->lock, flags); + mrq->cmd->error = -EAGAIN; + mmc_request_done(mmc, mrq); + return; + } + + host->state = STATE_REQUEST; + spin_unlock_irqrestore(&host->lock, flags); switch (mrq->cmd->opcode) { /* MMCIF does not support SD/SDIO command */ case SD_IO_SEND_OP_COND: case MMC_APP_CMD: + host->state = STATE_IDLE; mrq->cmd->error = -ETIMEDOUT; mmc_request_done(mmc, mrq); return; case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */ if (!mrq->data) { /* send_if_cond cmd (not support) */ + host->state = STATE_IDLE; mrq->cmd->error = -ETIMEDOUT; mmc_request_done(mmc, mrq); return; @@ -830,12 +853,9 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) sh_mmcif_start_cmd(host, mrq, mrq->cmd); host->data = NULL; - if (mrq->cmd->error != 0) { - mmc_request_done(mmc, mrq); - return; - } - if (mrq->stop) + if (!mrq->cmd->error && mrq->stop) sh_mmcif_stop_cmd(host, mrq, mrq->stop); + host->state = STATE_IDLE; mmc_request_done(mmc, mrq); } @@ -843,6 +863,16 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct sh_mmcif_host *host = mmc_priv(mmc); struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + if (host->state != STATE_IDLE) { + spin_unlock_irqrestore(&host->lock, flags); + return; + } + + host->state = STATE_IOS; + spin_unlock_irqrestore(&host->lock, flags); if (ios->power_mode == MMC_POWER_UP) { if (p->set_pwr) @@ -852,6 +882,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sh_mmcif_clock_control(host, 0); if (ios->power_mode == MMC_POWER_OFF && p->down_pwr) p->down_pwr(host->pd); + host->state = STATE_IDLE; return; } @@ -859,6 +890,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sh_mmcif_clock_control(host, ios->clock); host->bus_width = ios->bus_width; + host->state = STATE_IDLE; } static int sh_mmcif_get_cd(struct mmc_host *mmc) @@ -996,6 +1028,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) host->pd = pdev; init_completion(&host->intr_wait); + spin_lock_init(&host->lock); mmc->ops = &sh_mmcif_ops; mmc->f_max = host->clk; @@ -1025,6 +1058,8 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) mmc_add_host(mmc); + sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); + ret = request_irq(irq[0], sh_mmcif_intr, 0, "sh_mmc:error", host); if (ret) { dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n"); @@ -1037,7 +1072,6 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) goto clean_up2; } - sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); sh_mmcif_detect(host->mmc); dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION); @@ -1063,11 +1097,11 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev) mmc_remove_host(host->mmc); sh_mmcif_release_dma(host); + sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); + if (host->addr) iounmap(host->addr); - sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); - irq[0] = platform_get_irq(pdev, 0); irq[1] = platform_get_irq(pdev, 1);