From patchwork Fri Feb 18 23:17:51 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrei Warkentin X-Patchwork-Id: 574211 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 p1INHukn013079 for ; Fri, 18 Feb 2011 23:17:57 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758106Ab1BRXRz (ORCPT ); Fri, 18 Feb 2011 18:17:55 -0500 Received: from exprod5og113.obsmtp.com ([64.18.0.26]:59912 "EHLO exprod5og113.obsmtp.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753984Ab1BRXRz (ORCPT ); Fri, 18 Feb 2011 18:17:55 -0500 Received: from source ([192.54.82.14]) (using TLSv1) by exprod5ob113.postini.com ([64.18.4.12]) with SMTP ID DSNKTV7+IgCCEKX4GK/P+q2m8XQ24iShLmzK@postini.com; Fri, 18 Feb 2011 15:17:54 PST Received: from DE01MGRG01.AM.MOT-MOBILITY.COM ([10.176.130.20]) by DE01MGRG01.AM.MOT-MOBILITY.COM (8.14.3/8.14.3) with ESMTP id p1INI5Je006681 for ; Fri, 18 Feb 2011 18:18:05 -0500 (EST) Received: from mail-vx0-f170.google.com (mail-vx0-f170.google.com [209.85.220.170]) by DE01MGRG01.AM.MOT-MOBILITY.COM (8.14.3/8.14.3) with ESMTP id p1INI4cL006671 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=OK) for ; Fri, 18 Feb 2011 18:18:05 -0500 (EST) Received: by vxc38 with SMTP id 38so2930883vxc.15 for ; Fri, 18 Feb 2011 15:17:52 -0800 (PST) MIME-Version: 1.0 Received: by 10.52.158.66 with SMTP id ws2mr2383726vdb.232.1298071071778; Fri, 18 Feb 2011 15:17:51 -0800 (PST) Received: by 10.220.188.140 with HTTP; Fri, 18 Feb 2011 15:17:51 -0800 (PST) In-Reply-To: References: <201102181444.24476.arnd@arndb.de> Date: Fri, 18 Feb 2011 17:17:51 -0600 Message-ID: Subject: Re: MMC quirks relating to performance/lifetime. From: Andrei Warkentin To: Arnd Bergmann Cc: linux-arm-kernel@lists.infradead.org, Linus Walleij , linux-mmc@vger.kernel.org X-CFilter-Loop: Reflected 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, 18 Feb 2011 23:17:57 +0000 (UTC) From b3e6a556a716e7cec86071342197e798b38c3cbf Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Fri, 18 Feb 2011 17:46:00 -0600 Subject: [PATCH] MMC: Split non-page-size aligned accesses. If the card page size is known, splits the access into an unaligned and an aligned portion, which helps with the performance. Change-Id: I4ad7588d613d775212fac87436e418577909a22b Signed-off-by: Andrei Warkentin --- drivers/mmc/card/block.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mmc/card.h | 1 + 2 files changed, 112 insertions(+), 0 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 7054fd5..be7d739 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -67,6 +68,74 @@ struct mmc_blk_data { static DEFINE_MUTEX(open_lock); +static ssize_t +show_block_attr(struct device *dev, struct device_attribute *attr, + char *buf); + +static ssize_t +set_block_attr(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); + +static DEVICE_ATTR(page_size, S_IRUGO | S_IWUSR, show_block_attr, set_block_attr); + +static ssize_t +show_block_attr(struct device *dev, struct device_attribute *attr, + char *buf) +{ + unsigned int val; + ssize_t ret = 0; + struct mmc_card *card = container_of(dev, struct mmc_card, dev); + mmc_claim_host(card->host); + if (attr == &dev_attr_page_size) + val = card->page_size; + else + ret = -EINVAL; + + mmc_release_host(card->host); + if (!ret) + ret = sprintf(buf, "%u\n", val); + return ret; +} + +static ssize_t +set_block_attr(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t ret; + char *after; + unsigned int val, *dest = NULL; + struct mmc_card *card = container_of(dev, struct mmc_card, dev); + val = simple_strtoul(buf, &after, 10); + ret = after - buf; + + while (isspace(*after++)) + ret++; + + if (ret != count) + return -EINVAL; + + if (attr == &dev_attr_page_size) + dest = &card->page_size; + else + return -EINVAL; + + if (dest) { + mmc_claim_host(card->host); + *dest = val; + mmc_release_host(card->host); + } + return ret; +} + +static struct attribute *capability_attrs[] = { + &dev_attr_page_size.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = capability_attrs, +}; + static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) { struct mmc_blk_data *md; @@ -312,6 +381,38 @@ out: return err ? 0 : 1; } + +/* + * If the request is not aligned, split it into an unaligned + * and an aligned portion. Here we can adjust + * the size of the MMC request and let the block layer request handle + * deal with generating another MMC request. + */ +static bool mmc_adjust_write(struct mmc_card *card, + struct mmc_request *mrq) +{ + unsigned int left_in_page; + unsigned int page_size_blocks; + + if (!card->page_size) + return false; + + page_size_blocks = card->page_size / mrq->data->blksz; + left_in_page = page_size_blocks - + (mrq->cmd->arg % page_size_blocks); + + /* Aligned access. */ + if (left_in_page == page_size_blocks) + return false; + + /* Not straddling page boundary. */ + if (mrq->data->blocks <= left_in_page) + return false; + + mrq->data->blocks = left_in_page; + return true; +} + static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->data; @@ -339,6 +440,10 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; brq.data.blocks = blk_rq_sectors(req); + /* Check for unaligned accesses straddling pages. */ + if (rq_data_dir(req) == WRITE) + mmc_adjust_write(card, &brq.mrq); + /* * The block layer doesn't support all sector count * restrictions, so we need to be prepared for too big @@ -707,6 +812,10 @@ static int mmc_blk_probe(struct mmc_card *card) if (err) goto out; + err = sysfs_create_group(&card->dev.kobj, &attr_group); + if (err) + goto out; + string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2, cap_str, sizeof(cap_str)); printk(KERN_INFO "%s: %s %s %s %s\n", @@ -735,6 +844,8 @@ static void mmc_blk_remove(struct mmc_card *card) /* Stop new requests from getting into the queue */ del_gendisk(md->disk); + sysfs_remove_group(&card->dev.kobj, &attr_group); + /* Then flush out any already in there */ mmc_cleanup_queue(&md->queue); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 6b75250..d52768a 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -123,7 +123,7 @@ struct mmc_card { unsigned int erase_size; /* erase size in sectors */ unsigned int erase_shift; /* if erase unit is power 2 */ unsigned int pref_erase; /* in sectors */ + unsigned int page_size; /* page size in bytes */ u8 erased_byte; /* value of erased bytes */ u32 raw_cid[4]; /* raw card CID */ -- 1.7.0.4