Adjust unaligned write accesses spanning preferred align size into two accesses - an unaligned and an aligned access. This is meant to be used for card quirks, and is off by default. A limiting value in transfer size for this adjustment is available, as on some cards there is a perf decrease for larger transfers. Signed-off-by: Andrei Warkentin <andreiw@xxxxxxxxxxxx> --- drivers/mmc/card/block.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 43 insertions(+), 0 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 913f394..a8f18c7 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -63,6 +63,8 @@ struct mmc_blk_data { unsigned int usage; unsigned int read_only; + unsigned int write_align_size; + unsigned int write_align_limit; }; static DEFINE_MUTEX(open_lock); @@ -312,6 +314,43 @@ 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 void mmc_adjust_write(struct mmc_card *card, + struct mmc_request *mrq) +{ + unsigned int left_in_page; + unsigned int wa_size_blocks; + struct mmc_blk_data *md = mmc_get_drvdata(card); + + if (!md->write_align_size) + return; + + if (md->write_align_limit && + (md->write_align_limit / mrq->data->blksz) + < mrq->data->blocks) + return; + + wa_size_blocks = md->write_align_size / mrq->data->blksz; + left_in_page = wa_size_blocks - + (mrq->cmd->arg % wa_size_blocks); + + /* Aligned access. */ + if (left_in_page == wa_size_blocks) + return; + + /* Not straddling page boundary. */ + if (mrq->data->blocks <= left_in_page) + return; + + mrq->data->blocks = left_in_page; +} + static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->data; @@ -339,6 +378,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 -- 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