From 304419d8a7e9204c5d19b704467b814df8c8f5b1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 18 May 2017 11:29:32 +0200 Subject: mmc: core: Allocate per-request data using the block layer core The mmc_queue_req is a per-request state container the MMC core uses to carry bounce buffers, pointers to asynchronous requests and so on. Currently allocated as a static array of objects, then as a request comes in, a mmc_queue_req is assigned to it, and used during the lifetime of the request. This is backwards compared to how other block layer drivers work: they usally let the block core provide a per-request struct that get allocated right beind the struct request, and which can be obtained using the blk_mq_rq_to_pdu() helper. (The _mq_ infix in this function name is misleading: it is used by both the old and the MQ block layer.) The per-request struct gets allocated to the size stored in the queue variable .cmd_size initialized using the .init_rq_fn() and cleaned up using .exit_rq_fn(). The block layer code makes the MMC core rely on this mechanism to allocate the per-request mmc_queue_req state container. Doing this make a lot of complicated queue handling go away. We only need to keep the .qnct that keeps count of how many request are currently being processed by the MMC layer. The MQ block layer will replace also this once we transition to it. Doing this refactoring is necessary to move the ioctl() operations into custom block layer requests tagged with REQ_OP_DRV_[IN|OUT] instead of the custom code using the BigMMCHostLock that we have today: those require that per-request data be obtainable easily from a request after creating a custom request with e.g.: struct request *rq = blk_get_request(q, REQ_OP_DRV_IN, __GFP_RECLAIM); struct mmc_queue_req *mq_rq = req_to_mq_rq(rq); And this is not possible with the current construction, as the request is not immediately assigned the per-request state container, but instead it gets assigned when the request finally enters the MMC queue, which is way too late for custom requests. Signed-off-by: Linus Walleij [Ulf: Folded in the fix to drop a call to blk_cleanup_queue()] Signed-off-by: Ulf Hansson Tested-by: Heiner Kallweit --- drivers/mmc/core/block.c | 38 +++++++++----------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) (limited to 'drivers/mmc/core/block.c') diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 8273b078686d..5f29b5625216 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -129,13 +129,6 @@ static inline int mmc_blk_part_switch(struct mmc_card *card, struct mmc_blk_data *md); static int get_card_status(struct mmc_card *card, u32 *status, int retries); -static void mmc_blk_requeue(struct request_queue *q, struct request *req) -{ - spin_lock_irq(q->queue_lock); - blk_requeue_request(q, req); - spin_unlock_irq(q->queue_lock); -} - static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) { struct mmc_blk_data *md; @@ -1642,7 +1635,7 @@ static void mmc_blk_rw_cmd_abort(struct mmc_queue *mq, struct mmc_card *card, if (mmc_card_removed(card)) req->rq_flags |= RQF_QUIET; while (blk_end_request(req, -EIO, blk_rq_cur_bytes(req))); - mmc_queue_req_free(mq, mqrq); + mq->qcnt--; } /** @@ -1662,7 +1655,7 @@ static void mmc_blk_rw_try_restart(struct mmc_queue *mq, struct request *req, if (mmc_card_removed(mq->card)) { req->rq_flags |= RQF_QUIET; blk_end_request_all(req, -EIO); - mmc_queue_req_free(mq, mqrq); + mq->qcnt--; /* FIXME: just set to 0? */ return; } /* Else proceed and try to restart the current async request */ @@ -1685,12 +1678,8 @@ static void mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *new_req) bool req_pending = true; if (new_req) { - mqrq_cur = mmc_queue_req_find(mq, new_req); - if (!mqrq_cur) { - WARN_ON(1); - mmc_blk_requeue(mq->queue, new_req); - new_req = NULL; - } + mqrq_cur = req_to_mmc_queue_req(new_req); + mq->qcnt++; } if (!mq->qcnt) @@ -1764,12 +1753,12 @@ static void mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *new_req) if (req_pending) mmc_blk_rw_cmd_abort(mq, card, old_req, mq_rq); else - mmc_queue_req_free(mq, mq_rq); + mq->qcnt--; mmc_blk_rw_try_restart(mq, new_req, mqrq_cur); return; } if (!req_pending) { - mmc_queue_req_free(mq, mq_rq); + mq->qcnt--; mmc_blk_rw_try_restart(mq, new_req, mqrq_cur); return; } @@ -1814,7 +1803,7 @@ static void mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *new_req) req_pending = blk_end_request(old_req, -EIO, brq->data.blksz); if (!req_pending) { - mmc_queue_req_free(mq, mq_rq); + mq->qcnt--; mmc_blk_rw_try_restart(mq, new_req, mqrq_cur); return; } @@ -1844,7 +1833,7 @@ static void mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *new_req) } } while (req_pending); - mmc_queue_req_free(mq, mq_rq); + mq->qcnt--; } void mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) @@ -2166,7 +2155,6 @@ static int mmc_blk_probe(struct mmc_card *card) { struct mmc_blk_data *md, *part_md; char cap_str[10]; - int ret; /* * Check that the card supports the command class(es) we need. @@ -2176,15 +2164,9 @@ static int mmc_blk_probe(struct mmc_card *card) mmc_fixup_device(card, mmc_blk_fixups); - ret = mmc_queue_alloc_shared_queue(card); - if (ret) - return ret; - md = mmc_blk_alloc(card); - if (IS_ERR(md)) { - mmc_queue_free_shared_queue(card); + if (IS_ERR(md)) return PTR_ERR(md); - } string_get_size((u64)get_capacity(md->disk), 512, STRING_UNITS_2, cap_str, sizeof(cap_str)); @@ -2222,7 +2204,6 @@ static int mmc_blk_probe(struct mmc_card *card) out: mmc_blk_remove_parts(card, md); mmc_blk_remove_req(md); - mmc_queue_free_shared_queue(card); return 0; } @@ -2240,7 +2221,6 @@ static void mmc_blk_remove(struct mmc_card *card) pm_runtime_put_noidle(&card->dev); mmc_blk_remove_req(md); dev_set_drvdata(&card->dev, NULL); - mmc_queue_free_shared_queue(card); } static int _mmc_blk_suspend(struct mmc_card *card) -- cgit v1.2.3 From 829043c48e5d7169d45a04027a787b55133f4cf6 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 18 May 2017 11:29:33 +0200 Subject: mmc: block: Tag is_rpmb as bool The variable is_rpmb is clearly a bool and even assigned true and false, yet declared as an int. Signed-off-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/core/block.c') diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 5f29b5625216..f4dab1dfd2ab 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -443,7 +443,7 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md, struct mmc_request mrq = {}; struct scatterlist sg; int err; - int is_rpmb = false; + bool is_rpmb = false; u32 status = 0; if (!card || !md || !idata) -- cgit v1.2.3 From 614f0388f580c436d2cf6dc0855de91d13ddc23d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 18 May 2017 11:29:34 +0200 Subject: mmc: block: move single ioctl() commands to block requests This wraps single ioctl() commands into block requests using the custom block layer request types REQ_OP_DRV_IN and REQ_OP_DRV_OUT. By doing this we are loosening the grip on the big host lock, since two calls to mmc_get_card()/mmc_put_card() are removed. We are storing the ioctl() in/out argument as a pointer in the per-request struct mmc_blk_request container. Since we now let the block layer allocate this data, blk_get_request() will allocate it for us and we can immediately dereference it and use it to pass the argument into the block layer. We refactor the if/else/if/else ladder in mmc_blk_issue_rq() as part of the job, keeping some extra attention to the case when a NULL req is passed into this function and making that pipeline flush more explicit. Tested on the ux500 with the userspace: mmc extcsd read /dev/mmcblk3 resulting in a successful EXTCSD info dump back to the console. This commit fixes a starvation issue in the MMC/SD stack that can be easily provoked in the following way by issueing the following commands in sequence: > dd if=/dev/mmcblk3 of=/dev/null bs=1M & > mmc extcs read /dev/mmcblk3 Before this patch, the extcsd read command would hang (starve) while waiting for the dd command to finish since the block layer was holding the card/host lock. After this patch, the extcsd ioctl() command is nicely interpersed with the rest of the block commands and we can issue a bunch of ioctl()s from userspace while there is some busy block IO going on without any problems. Conversely userspace ioctl()s can no longer starve the block layer by holding the card/host lock. Signed-off-by: Linus Walleij Signed-off-by: Ulf Hansson Tested-by: Avri Altman --- drivers/mmc/core/block.c | 111 ++++++++++++++++++++++++++++++++++++----------- drivers/mmc/core/queue.h | 3 ++ 2 files changed, 88 insertions(+), 26 deletions(-) (limited to 'drivers/mmc/core/block.c') diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index f4dab1dfd2ab..9fb2bd529156 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -564,8 +564,10 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, { struct mmc_blk_ioc_data *idata; struct mmc_blk_data *md; + struct mmc_queue *mq; struct mmc_card *card; int err = 0, ioc_err = 0; + struct request *req; /* * The caller must have CAP_SYS_RAWIO, and must be calling this on the @@ -591,17 +593,18 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, goto cmd_done; } - mmc_get_card(card); - - ioc_err = __mmc_blk_ioctl_cmd(card, md, idata); - - /* Always switch back to main area after RPMB access */ - if (md->area_type & MMC_BLK_DATA_AREA_RPMB) - mmc_blk_part_switch(card, dev_get_drvdata(&card->dev)); - - mmc_put_card(card); - + /* + * Dispatch the ioctl() into the block request queue. + */ + mq = &md->queue; + req = blk_get_request(mq->queue, + idata->ic.write_flag ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, + __GFP_RECLAIM); + req_to_mmc_queue_req(req)->idata = idata; + blk_execute_rq(mq->queue, NULL, req, 0); + ioc_err = req_to_mmc_queue_req(req)->ioc_result; err = mmc_blk_ioctl_copy_to_user(ic_ptr, idata); + blk_put_request(req); cmd_done: mmc_blk_put(md); @@ -611,6 +614,31 @@ cmd_err: return ioc_err ? ioc_err : err; } +/* + * The ioctl commands come back from the block layer after it queued it and + * processed it with all other requests and then they get issued in this + * function. + */ +static void mmc_blk_ioctl_cmd_issue(struct mmc_queue *mq, struct request *req) +{ + struct mmc_queue_req *mq_rq; + struct mmc_blk_ioc_data *idata; + struct mmc_card *card = mq->card; + struct mmc_blk_data *md = mq->blkdata; + int ioc_err; + + mq_rq = req_to_mmc_queue_req(req); + idata = mq_rq->idata; + ioc_err = __mmc_blk_ioctl_cmd(card, md, idata); + mq_rq->ioc_result = ioc_err; + + /* Always switch back to main area after RPMB access */ + if (md->area_type & MMC_BLK_DATA_AREA_RPMB) + mmc_blk_part_switch(card, dev_get_drvdata(&card->dev)); + + blk_end_request_all(req, ioc_err); +} + static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev, struct mmc_ioc_multi_cmd __user *user) { @@ -1854,23 +1882,54 @@ void mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) goto out; } - if (req && req_op(req) == REQ_OP_DISCARD) { - /* complete ongoing async transfer before issuing discard */ - if (mq->qcnt) - mmc_blk_issue_rw_rq(mq, NULL); - mmc_blk_issue_discard_rq(mq, req); - } else if (req && req_op(req) == REQ_OP_SECURE_ERASE) { - /* complete ongoing async transfer before issuing secure erase*/ - if (mq->qcnt) - mmc_blk_issue_rw_rq(mq, NULL); - mmc_blk_issue_secdiscard_rq(mq, req); - } else if (req && req_op(req) == REQ_OP_FLUSH) { - /* complete ongoing async transfer before issuing flush */ - if (mq->qcnt) - mmc_blk_issue_rw_rq(mq, NULL); - mmc_blk_issue_flush(mq, req); + if (req) { + switch (req_op(req)) { + case REQ_OP_DRV_IN: + case REQ_OP_DRV_OUT: + /* + * Complete ongoing async transfer before issuing + * ioctl()s + */ + if (mq->qcnt) + mmc_blk_issue_rw_rq(mq, NULL); + mmc_blk_ioctl_cmd_issue(mq, req); + break; + case REQ_OP_DISCARD: + /* + * Complete ongoing async transfer before issuing + * discard. + */ + if (mq->qcnt) + mmc_blk_issue_rw_rq(mq, NULL); + mmc_blk_issue_discard_rq(mq, req); + break; + case REQ_OP_SECURE_ERASE: + /* + * Complete ongoing async transfer before issuing + * secure erase. + */ + if (mq->qcnt) + mmc_blk_issue_rw_rq(mq, NULL); + mmc_blk_issue_secdiscard_rq(mq, req); + break; + case REQ_OP_FLUSH: + /* + * Complete ongoing async transfer before issuing + * flush. + */ + if (mq->qcnt) + mmc_blk_issue_rw_rq(mq, NULL); + mmc_blk_issue_flush(mq, req); + break; + default: + /* Normal request, just issue it */ + mmc_blk_issue_rw_rq(mq, req); + card->host->context_info.is_waiting_last_req = false; + break; + }; } else { - mmc_blk_issue_rw_rq(mq, req); + /* No request, flushing the pipeline with NULL */ + mmc_blk_issue_rw_rq(mq, NULL); card->host->context_info.is_waiting_last_req = false; } diff --git a/drivers/mmc/core/queue.h b/drivers/mmc/core/queue.h index dae31bc0c2d3..005ece9ac7cb 100644 --- a/drivers/mmc/core/queue.h +++ b/drivers/mmc/core/queue.h @@ -22,6 +22,7 @@ static inline bool mmc_req_is_special(struct request *req) struct task_struct; struct mmc_blk_data; +struct mmc_blk_ioc_data; struct mmc_blk_request { struct mmc_request mrq; @@ -40,6 +41,8 @@ struct mmc_queue_req { struct scatterlist *bounce_sg; unsigned int bounce_sg_len; struct mmc_async_req areq; + int ioc_result; + struct mmc_blk_ioc_data *idata; }; struct mmc_queue { -- cgit v1.2.3 From 3ecd8cf23f88d5df1c545a5c04217987abb28575 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 18 May 2017 11:29:35 +0200 Subject: mmc: block: move multi-ioctl() to use block layer This switches also the multiple-command ioctl() call to issue all ioctl()s through the block layer instead of going directly to the device. We extend the passed argument with an argument count and loop over all passed commands in the ioctl() issue function called from the block layer. By doing this we are again loosening the grip on the big host lock, since two calls to mmc_get_card()/mmc_put_card() are removed. Signed-off-by: Linus Walleij Signed-off-by: Ulf Hansson Tested-by: Avri Altman --- drivers/mmc/core/block.c | 38 +++++++++++++++++++++++++------------- drivers/mmc/core/queue.h | 3 ++- 2 files changed, 27 insertions(+), 14 deletions(-) (limited to 'drivers/mmc/core/block.c') diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 9fb2bd529156..e9737987956f 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -563,6 +563,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, struct mmc_ioc_cmd __user *ic_ptr) { struct mmc_blk_ioc_data *idata; + struct mmc_blk_ioc_data *idatas[1]; struct mmc_blk_data *md; struct mmc_queue *mq; struct mmc_card *card; @@ -600,7 +601,9 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, req = blk_get_request(mq->queue, idata->ic.write_flag ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, __GFP_RECLAIM); - req_to_mmc_queue_req(req)->idata = idata; + idatas[0] = idata; + req_to_mmc_queue_req(req)->idata = idatas; + req_to_mmc_queue_req(req)->ioc_count = 1; blk_execute_rq(mq->queue, NULL, req, 0); ioc_err = req_to_mmc_queue_req(req)->ioc_result; err = mmc_blk_ioctl_copy_to_user(ic_ptr, idata); @@ -622,14 +625,17 @@ cmd_err: static void mmc_blk_ioctl_cmd_issue(struct mmc_queue *mq, struct request *req) { struct mmc_queue_req *mq_rq; - struct mmc_blk_ioc_data *idata; struct mmc_card *card = mq->card; struct mmc_blk_data *md = mq->blkdata; int ioc_err; + int i; mq_rq = req_to_mmc_queue_req(req); - idata = mq_rq->idata; - ioc_err = __mmc_blk_ioctl_cmd(card, md, idata); + for (i = 0; i < mq_rq->ioc_count; i++) { + ioc_err = __mmc_blk_ioctl_cmd(card, md, mq_rq->idata[i]); + if (ioc_err) + break; + } mq_rq->ioc_result = ioc_err; /* Always switch back to main area after RPMB access */ @@ -646,8 +652,10 @@ static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev, struct mmc_ioc_cmd __user *cmds = user->cmds; struct mmc_card *card; struct mmc_blk_data *md; + struct mmc_queue *mq; int i, err = 0, ioc_err = 0; __u64 num_of_cmds; + struct request *req; /* * The caller must have CAP_SYS_RAWIO, and must be calling this on the @@ -689,21 +697,25 @@ static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev, goto cmd_done; } - mmc_get_card(card); - - for (i = 0; i < num_of_cmds && !ioc_err; i++) - ioc_err = __mmc_blk_ioctl_cmd(card, md, idata[i]); - - /* Always switch back to main area after RPMB access */ - if (md->area_type & MMC_BLK_DATA_AREA_RPMB) - mmc_blk_part_switch(card, dev_get_drvdata(&card->dev)); - mmc_put_card(card); + /* + * Dispatch the ioctl()s into the block request queue. + */ + mq = &md->queue; + req = blk_get_request(mq->queue, + idata[0]->ic.write_flag ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, + __GFP_RECLAIM); + req_to_mmc_queue_req(req)->idata = idata; + req_to_mmc_queue_req(req)->ioc_count = num_of_cmds; + blk_execute_rq(mq->queue, NULL, req, 0); + ioc_err = req_to_mmc_queue_req(req)->ioc_result; /* copy to user if data and response */ for (i = 0; i < num_of_cmds && !err; i++) err = mmc_blk_ioctl_copy_to_user(&cmds[i], idata[i]); + blk_put_request(req); + cmd_done: mmc_blk_put(md); cmd_err: diff --git a/drivers/mmc/core/queue.h b/drivers/mmc/core/queue.h index 005ece9ac7cb..8c76e7118c95 100644 --- a/drivers/mmc/core/queue.h +++ b/drivers/mmc/core/queue.h @@ -42,7 +42,8 @@ struct mmc_queue_req { unsigned int bounce_sg_len; struct mmc_async_req areq; int ioc_result; - struct mmc_blk_ioc_data *idata; + struct mmc_blk_ioc_data **idata; + unsigned int ioc_count; }; struct mmc_queue { -- cgit v1.2.3 From 67e69d5220c904238f94bb2e6001d7c590f5a0bb Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 19 May 2017 15:37:27 +0200 Subject: mmc: block: remove req back pointer Just as we can use blk_mq_rq_from_pdu() to get the per-request tag we can use blk_mq_rq_to_pdu() to get a request from a tag. Introduce a static inline helper so we are on the clear what is happening. Suggested-by: Christoph Hellwig Signed-off-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 8 ++++---- drivers/mmc/core/queue.c | 13 +++++-------- drivers/mmc/core/queue.h | 8 +++++++- 3 files changed, 16 insertions(+), 13 deletions(-) (limited to 'drivers/mmc/core/block.c') diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index e9737987956f..553ab4d1db94 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -1366,7 +1366,7 @@ static enum mmc_blk_status mmc_blk_err_check(struct mmc_card *card, struct mmc_queue_req *mq_mrq = container_of(areq, struct mmc_queue_req, areq); struct mmc_blk_request *brq = &mq_mrq->brq; - struct request *req = mq_mrq->req; + struct request *req = mmc_queue_req_to_req(mq_mrq); int need_retune = card->host->need_retune; bool ecc_err = false; bool gen_err = false; @@ -1473,7 +1473,7 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq, struct mmc_blk_data *md = mq->blkdata; struct mmc_card *card = md->queue.card; struct mmc_blk_request *brq = &mqrq->brq; - struct request *req = mqrq->req; + struct request *req = mmc_queue_req_to_req(mqrq); /* * Reliable writes are used to implement Forced Unit Access and @@ -1578,7 +1578,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, { u32 readcmd, writecmd; struct mmc_blk_request *brq = &mqrq->brq; - struct request *req = mqrq->req; + struct request *req = mmc_queue_req_to_req(mqrq); struct mmc_blk_data *md = mq->blkdata; bool do_rel_wr, do_data_tag; @@ -1760,7 +1760,7 @@ static void mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *new_req) */ mq_rq = container_of(old_areq, struct mmc_queue_req, areq); brq = &mq_rq->brq; - old_req = mq_rq->req; + old_req = mmc_queue_req_to_req(mq_rq); type = rq_data_dir(old_req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE; mmc_queue_bounce_post(mq_rq); diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c index d6c7b4cde4db..ba689a2ffc51 100644 --- a/drivers/mmc/core/queue.c +++ b/drivers/mmc/core/queue.c @@ -184,8 +184,6 @@ static int mmc_init_request(struct request_queue *q, struct request *req, struct mmc_card *card = mq->card; struct mmc_host *host = card->host; - mq_rq->req = req; - if (card->bouncesz) { mq_rq->bounce_buf = kmalloc(card->bouncesz, gfp); if (!mq_rq->bounce_buf) @@ -223,8 +221,6 @@ static void mmc_exit_request(struct request_queue *q, struct request *req) kfree(mq_rq->sg); mq_rq->sg = NULL; - - mq_rq->req = NULL; } /** @@ -373,12 +369,13 @@ unsigned int mmc_queue_map_sg(struct mmc_queue *mq, struct mmc_queue_req *mqrq) unsigned int sg_len; size_t buflen; struct scatterlist *sg; + struct request *req = mmc_queue_req_to_req(mqrq); int i; if (!mqrq->bounce_buf) - return blk_rq_map_sg(mq->queue, mqrq->req, mqrq->sg); + return blk_rq_map_sg(mq->queue, req, mqrq->sg); - sg_len = blk_rq_map_sg(mq->queue, mqrq->req, mqrq->bounce_sg); + sg_len = blk_rq_map_sg(mq->queue, req, mqrq->bounce_sg); mqrq->bounce_sg_len = sg_len; @@ -400,7 +397,7 @@ void mmc_queue_bounce_pre(struct mmc_queue_req *mqrq) if (!mqrq->bounce_buf) return; - if (rq_data_dir(mqrq->req) != WRITE) + if (rq_data_dir(mmc_queue_req_to_req(mqrq)) != WRITE) return; sg_copy_to_buffer(mqrq->bounce_sg, mqrq->bounce_sg_len, @@ -416,7 +413,7 @@ void mmc_queue_bounce_post(struct mmc_queue_req *mqrq) if (!mqrq->bounce_buf) return; - if (rq_data_dir(mqrq->req) != READ) + if (rq_data_dir(mmc_queue_req_to_req(mqrq)) != READ) return; sg_copy_from_buffer(mqrq->bounce_sg, mqrq->bounce_sg_len, diff --git a/drivers/mmc/core/queue.h b/drivers/mmc/core/queue.h index dfe481a8b5ed..2793020a3c8c 100644 --- a/drivers/mmc/core/queue.h +++ b/drivers/mmc/core/queue.h @@ -12,6 +12,13 @@ static inline struct mmc_queue_req *req_to_mmc_queue_req(struct request *rq) return blk_mq_rq_to_pdu(rq); } +struct mmc_queue_req; + +static inline struct request *mmc_queue_req_to_req(struct mmc_queue_req *mqr) +{ + return blk_mq_rq_from_pdu(mqr); +} + struct task_struct; struct mmc_blk_data; struct mmc_blk_ioc_data; @@ -26,7 +33,6 @@ struct mmc_blk_request { }; struct mmc_queue_req { - struct request *req; struct mmc_blk_request brq; struct scatterlist *sg; char *bounce_buf; -- cgit v1.2.3 From 02166a01f8113c6374d6f1512befa9233c837fa0 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 19 May 2017 15:37:28 +0200 Subject: mmc: block: Tag DRV_OPs with a driver operation type We will expand the DRV_OP usage, so we need to know which operation we're performing. Tag the operations with an enum:ed type and rename the function so it is clear that it deals with any command and put a switch statement in it. Currently only ioctls are supported. Signed-off-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 37 ++++++++++++++++++++++++------------- drivers/mmc/core/queue.h | 9 +++++++++ 2 files changed, 33 insertions(+), 13 deletions(-) (limited to 'drivers/mmc/core/block.c') diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 553ab4d1db94..b24e7f5171c9 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -602,6 +602,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, idata->ic.write_flag ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, __GFP_RECLAIM); idatas[0] = idata; + req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_IOCTL; req_to_mmc_queue_req(req)->idata = idatas; req_to_mmc_queue_req(req)->ioc_count = 1; blk_execute_rq(mq->queue, NULL, req, 0); @@ -618,11 +619,11 @@ cmd_err: } /* - * The ioctl commands come back from the block layer after it queued it and + * The non-block commands come back from the block layer after it queued it and * processed it with all other requests and then they get issued in this * function. */ -static void mmc_blk_ioctl_cmd_issue(struct mmc_queue *mq, struct request *req) +static void mmc_blk_issue_drv_op(struct mmc_queue *mq, struct request *req) { struct mmc_queue_req *mq_rq; struct mmc_card *card = mq->card; @@ -631,18 +632,27 @@ static void mmc_blk_ioctl_cmd_issue(struct mmc_queue *mq, struct request *req) int i; mq_rq = req_to_mmc_queue_req(req); - for (i = 0; i < mq_rq->ioc_count; i++) { - ioc_err = __mmc_blk_ioctl_cmd(card, md, mq_rq->idata[i]); - if (ioc_err) - break; - } - mq_rq->ioc_result = ioc_err; - /* Always switch back to main area after RPMB access */ - if (md->area_type & MMC_BLK_DATA_AREA_RPMB) - mmc_blk_part_switch(card, dev_get_drvdata(&card->dev)); + switch (mq_rq->drv_op) { + case MMC_DRV_OP_IOCTL: + for (i = 0; i < mq_rq->ioc_count; i++) { + ioc_err = + __mmc_blk_ioctl_cmd(card, md, mq_rq->idata[i]); + if (ioc_err) + break; + } + mq_rq->ioc_result = ioc_err; + + /* Always switch back to main area after RPMB access */ + if (md->area_type & MMC_BLK_DATA_AREA_RPMB) + mmc_blk_part_switch(card, dev_get_drvdata(&card->dev)); - blk_end_request_all(req, ioc_err); + blk_end_request_all(req, ioc_err); + break; + default: + /* Unknown operation */ + break; + } } static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev, @@ -705,6 +715,7 @@ static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev, req = blk_get_request(mq->queue, idata[0]->ic.write_flag ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, __GFP_RECLAIM); + req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_IOCTL; req_to_mmc_queue_req(req)->idata = idata; req_to_mmc_queue_req(req)->ioc_count = num_of_cmds; blk_execute_rq(mq->queue, NULL, req, 0); @@ -1904,7 +1915,7 @@ void mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) */ if (mq->qcnt) mmc_blk_issue_rw_rq(mq, NULL); - mmc_blk_ioctl_cmd_issue(mq, req); + mmc_blk_issue_drv_op(mq, req); break; case REQ_OP_DISCARD: /* diff --git a/drivers/mmc/core/queue.h b/drivers/mmc/core/queue.h index 2793020a3c8c..1e6062eb3e07 100644 --- a/drivers/mmc/core/queue.h +++ b/drivers/mmc/core/queue.h @@ -32,6 +32,14 @@ struct mmc_blk_request { int retune_retry_done; }; +/** + * enum mmc_drv_op - enumerates the operations in the mmc_queue_req + * @MMC_DRV_OP_IOCTL: ioctl operation + */ +enum mmc_drv_op { + MMC_DRV_OP_IOCTL, +}; + struct mmc_queue_req { struct mmc_blk_request brq; struct scatterlist *sg; @@ -39,6 +47,7 @@ struct mmc_queue_req { struct scatterlist *bounce_sg; unsigned int bounce_sg_len; struct mmc_async_req areq; + enum mmc_drv_op drv_op; int ioc_result; struct mmc_blk_ioc_data **idata; unsigned int ioc_count; -- cgit v1.2.3 From 5ec1239694c74e8feebe967bf467081445622004 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 19 May 2017 15:37:29 +0200 Subject: mmc: block: Move DRV OP issue function We will need to access static functions above the pure block layer operations in the file, so move the driver operations issue function down so we can see all non-blocklayer symbols. Signed-off-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 74 ++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 37 deletions(-) (limited to 'drivers/mmc/core/block.c') diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index b24e7f5171c9..75b1baacf28b 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -618,43 +618,6 @@ cmd_err: return ioc_err ? ioc_err : err; } -/* - * The non-block commands come back from the block layer after it queued it and - * processed it with all other requests and then they get issued in this - * function. - */ -static void mmc_blk_issue_drv_op(struct mmc_queue *mq, struct request *req) -{ - struct mmc_queue_req *mq_rq; - struct mmc_card *card = mq->card; - struct mmc_blk_data *md = mq->blkdata; - int ioc_err; - int i; - - mq_rq = req_to_mmc_queue_req(req); - - switch (mq_rq->drv_op) { - case MMC_DRV_OP_IOCTL: - for (i = 0; i < mq_rq->ioc_count; i++) { - ioc_err = - __mmc_blk_ioctl_cmd(card, md, mq_rq->idata[i]); - if (ioc_err) - break; - } - mq_rq->ioc_result = ioc_err; - - /* Always switch back to main area after RPMB access */ - if (md->area_type & MMC_BLK_DATA_AREA_RPMB) - mmc_blk_part_switch(card, dev_get_drvdata(&card->dev)); - - blk_end_request_all(req, ioc_err); - break; - default: - /* Unknown operation */ - break; - } -} - static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev, struct mmc_ioc_multi_cmd __user *user) { @@ -1222,6 +1185,43 @@ int mmc_access_rpmb(struct mmc_queue *mq) return false; } +/* + * The non-block commands come back from the block layer after it queued it and + * processed it with all other requests and then they get issued in this + * function. + */ +static void mmc_blk_issue_drv_op(struct mmc_queue *mq, struct request *req) +{ + struct mmc_queue_req *mq_rq; + struct mmc_card *card = mq->card; + struct mmc_blk_data *md = mq->blkdata; + int ioc_err; + int i; + + mq_rq = req_to_mmc_queue_req(req); + + switch (mq_rq->drv_op) { + case MMC_DRV_OP_IOCTL: + for (i = 0; i < mq_rq->ioc_count; i++) { + ioc_err = + __mmc_blk_ioctl_cmd(card, md, mq_rq->idata[i]); + if (ioc_err) + break; + } + mq_rq->ioc_result = ioc_err; + + /* Always switch back to main area after RPMB access */ + if (md->area_type & MMC_BLK_DATA_AREA_RPMB) + mmc_blk_part_switch(card, dev_get_drvdata(&card->dev)); + + blk_end_request_all(req, ioc_err); + break; + default: + /* Unknown operation */ + break; + } +} + static void mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->blkdata; -- cgit v1.2.3 From 0493f6fe5bdee8ac101a1a0c449971c2d4665e99 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 19 May 2017 15:37:30 +0200 Subject: mmc: block: Move boot partition locking into a driver op This moves the boot partition lock command (issued from sysfs) into a custom block layer request, just like the ioctl()s, getting rid of yet another instance of mmc_get_card(). Since we now have two operations issuing special DRV_OP's, we rename the result variable ->drv_op_result. Tested by locking the boot partition from userspace: > cd /sys/devices/platform/soc/80114000.sdi4_per2/mmc_host/mmc3/ mmc3:0001/block/mmcblk3/mmcblk3boot0 > echo 1 > ro_lock_until_next_power_on [ 178.645324] mmcblk3boot1: Locking boot partition ro until next power on [ 178.652221] mmcblk3boot0: Locking boot partition ro until next power on Also tested this with a huge dd job in the background: it is now possible to lock the boot partitions on the card even under heavy I/O. Signed-off-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 53 +++++++++++++++++++++++++++--------------------- drivers/mmc/core/queue.h | 4 +++- 2 files changed, 33 insertions(+), 24 deletions(-) (limited to 'drivers/mmc/core/block.c') diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 75b1baacf28b..52635120a0a5 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -190,6 +190,8 @@ static ssize_t power_ro_lock_store(struct device *dev, int ret; struct mmc_blk_data *md, *part_md; struct mmc_card *card; + struct mmc_queue *mq; + struct request *req; unsigned long set; if (kstrtoul(buf, 0, &set)) @@ -199,20 +201,14 @@ static ssize_t power_ro_lock_store(struct device *dev, return count; md = mmc_blk_get(dev_to_disk(dev)); + mq = &md->queue; card = md->queue.card; - mmc_get_card(card); - - ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP, - card->ext_csd.boot_ro_lock | - EXT_CSD_BOOT_WP_B_PWR_WP_EN, - card->ext_csd.part_time); - if (ret) - pr_err("%s: Locking boot partition ro until next power on failed: %d\n", md->disk->disk_name, ret); - else - card->ext_csd.boot_ro_lock |= EXT_CSD_BOOT_WP_B_PWR_WP_EN; - - mmc_put_card(card); + /* Dispatch locking to the block layer */ + req = blk_get_request(mq->queue, REQ_OP_DRV_OUT, __GFP_RECLAIM); + req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_BOOT_WP; + blk_execute_rq(mq->queue, NULL, req, 0); + ret = req_to_mmc_queue_req(req)->drv_op_result; if (!ret) { pr_info("%s: Locking boot partition ro until next power on\n", @@ -606,7 +602,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, req_to_mmc_queue_req(req)->idata = idatas; req_to_mmc_queue_req(req)->ioc_count = 1; blk_execute_rq(mq->queue, NULL, req, 0); - ioc_err = req_to_mmc_queue_req(req)->ioc_result; + ioc_err = req_to_mmc_queue_req(req)->drv_op_result; err = mmc_blk_ioctl_copy_to_user(ic_ptr, idata); blk_put_request(req); @@ -682,7 +678,7 @@ static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev, req_to_mmc_queue_req(req)->idata = idata; req_to_mmc_queue_req(req)->ioc_count = num_of_cmds; blk_execute_rq(mq->queue, NULL, req, 0); - ioc_err = req_to_mmc_queue_req(req)->ioc_result; + ioc_err = req_to_mmc_queue_req(req)->drv_op_result; /* copy to user if data and response */ for (i = 0; i < num_of_cmds && !err; i++) @@ -1195,7 +1191,7 @@ static void mmc_blk_issue_drv_op(struct mmc_queue *mq, struct request *req) struct mmc_queue_req *mq_rq; struct mmc_card *card = mq->card; struct mmc_blk_data *md = mq->blkdata; - int ioc_err; + int ret; int i; mq_rq = req_to_mmc_queue_req(req); @@ -1203,23 +1199,34 @@ static void mmc_blk_issue_drv_op(struct mmc_queue *mq, struct request *req) switch (mq_rq->drv_op) { case MMC_DRV_OP_IOCTL: for (i = 0; i < mq_rq->ioc_count; i++) { - ioc_err = - __mmc_blk_ioctl_cmd(card, md, mq_rq->idata[i]); - if (ioc_err) + ret = __mmc_blk_ioctl_cmd(card, md, mq_rq->idata[i]); + if (ret) break; } - mq_rq->ioc_result = ioc_err; - /* Always switch back to main area after RPMB access */ if (md->area_type & MMC_BLK_DATA_AREA_RPMB) mmc_blk_part_switch(card, dev_get_drvdata(&card->dev)); - - blk_end_request_all(req, ioc_err); + break; + case MMC_DRV_OP_BOOT_WP: + ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP, + card->ext_csd.boot_ro_lock | + EXT_CSD_BOOT_WP_B_PWR_WP_EN, + card->ext_csd.part_time); + if (ret) + pr_err("%s: Locking boot partition ro until next power on failed: %d\n", + md->disk->disk_name, ret); + else + card->ext_csd.boot_ro_lock |= + EXT_CSD_BOOT_WP_B_PWR_WP_EN; break; default: - /* Unknown operation */ + pr_err("%s: unknown driver specific operation\n", + md->disk->disk_name); + ret = -EINVAL; break; } + mq_rq->drv_op_result = ret; + blk_end_request_all(req, ret); } static void mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) diff --git a/drivers/mmc/core/queue.h b/drivers/mmc/core/queue.h index 1e6062eb3e07..361b46408e0f 100644 --- a/drivers/mmc/core/queue.h +++ b/drivers/mmc/core/queue.h @@ -35,9 +35,11 @@ struct mmc_blk_request { /** * enum mmc_drv_op - enumerates the operations in the mmc_queue_req * @MMC_DRV_OP_IOCTL: ioctl operation + * @MMC_DRV_OP_BOOT_WP: write protect boot partitions */ enum mmc_drv_op { MMC_DRV_OP_IOCTL, + MMC_DRV_OP_BOOT_WP, }; struct mmc_queue_req { @@ -48,7 +50,7 @@ struct mmc_queue_req { unsigned int bounce_sg_len; struct mmc_async_req areq; enum mmc_drv_op drv_op; - int ioc_result; + int drv_op_result; struct mmc_blk_ioc_data **idata; unsigned int ioc_count; }; -- cgit v1.2.3 From 2185bc2cd617de0a0b3b7c5a0ca42c82d0a15cff Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 22 May 2017 10:23:58 +0200 Subject: mmc: block: Use __mmc_send_status() and drop get_card_status() The only reason to why the mmc block device driver needs to implements its own version of how to get the status of the card, is that it needs to specify a different amount of retries. Therefore add a new exported function which allows the caller to specify the number of retries and convert everybody to use it, as this simplifies the code. Signed-off-by: Ulf Hansson Reviewed-by: Linus Walleij --- drivers/mmc/core/block.c | 22 +++------------------- drivers/mmc/core/mmc_ops.c | 10 ++++++++-- drivers/mmc/core/mmc_ops.h | 1 + 3 files changed, 12 insertions(+), 21 deletions(-) (limited to 'drivers/mmc/core/block.c') diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 52635120a0a5..60de060f3c27 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -127,7 +127,6 @@ MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device"); static inline int mmc_blk_part_switch(struct mmc_card *card, struct mmc_blk_data *md); -static int get_card_status(struct mmc_card *card, u32 *status, int retries); static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) { @@ -381,7 +380,7 @@ static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status, return -EINVAL; do { - err = get_card_status(card, status, 5); + err = __mmc_send_status(card, status, 5); if (err) break; @@ -855,21 +854,6 @@ static int mmc_sd_num_wr_blocks(struct mmc_card *card, u32 *written_blocks) return 0; } -static int get_card_status(struct mmc_card *card, u32 *status, int retries) -{ - struct mmc_command cmd = {}; - int err; - - cmd.opcode = MMC_SEND_STATUS; - if (!mmc_host_is_spi(card->host)) - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; - err = mmc_wait_for_cmd(card->host, &cmd, retries); - if (err == 0) - *status = cmd.resp[0]; - return err; -} - static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms, bool hw_busy_detect, struct request *req, bool *gen_err) { @@ -878,7 +862,7 @@ static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms, u32 status; do { - err = get_card_status(card, &status, 5); + err = __mmc_send_status(card, &status, 5); if (err) { pr_err("%s: error %d requesting status\n", req->rq_disk->disk_name, err); @@ -1046,7 +1030,7 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req, * we can't be sure the returned status is for the r/w command. */ for (retry = 2; retry >= 0; retry--) { - err = get_card_status(card, &status, 0); + err = __mmc_send_status(card, &status, 0); if (!err) break; diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 78f75f00efc5..ae1fc4818240 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -54,7 +54,7 @@ static const u8 tuning_blk_pattern_8bit[] = { 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, }; -int mmc_send_status(struct mmc_card *card, u32 *status) +int __mmc_send_status(struct mmc_card *card, u32 *status, unsigned int retries) { int err; struct mmc_command cmd = {}; @@ -64,7 +64,7 @@ int mmc_send_status(struct mmc_card *card, u32 *status) cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; - err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); + err = mmc_wait_for_cmd(card->host, &cmd, retries); if (err) return err; @@ -76,6 +76,12 @@ int mmc_send_status(struct mmc_card *card, u32 *status) return 0; } +EXPORT_SYMBOL_GPL(__mmc_send_status); + +int mmc_send_status(struct mmc_card *card, u32 *status) +{ + return __mmc_send_status(card, status, MMC_CMD_RETRIES); +} static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card) { diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 978bd2e60f8a..b8d05529a6ce 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -25,6 +25,7 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); int mmc_all_send_cid(struct mmc_host *host, u32 *cid); int mmc_set_relative_addr(struct mmc_card *card); int mmc_send_csd(struct mmc_card *card, u32 *csd); +int __mmc_send_status(struct mmc_card *card, u32 *status, unsigned int retries); int mmc_send_status(struct mmc_card *card, u32 *status); int mmc_send_cid(struct mmc_host *host, u32 *cid); int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); -- cgit v1.2.3 From 7322238f166c7af8245b5da76d70229d25465142 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Tue, 23 May 2017 05:11:33 +0800 Subject: mmc: block: fix semicolon.cocci warnings drivers/mmc/core/block.c:1929:3-4: Unneeded semicolon Remove unneeded semicolon. Generated by: scripts/coccinelle/misc/semicolon.cocci CC: Linus Walleij Signed-off-by: Fengguang Wu Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/core/block.c') diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 60de060f3c27..64f9fda92229 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -1940,7 +1940,7 @@ void mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) mmc_blk_issue_rw_rq(mq, req); card->host->context_info.is_waiting_last_req = false; break; - }; + } } else { /* No request, flushing the pipeline with NULL */ mmc_blk_issue_rw_rq(mq, NULL); -- cgit v1.2.3 From a04e6bae9e6f12c3a2aa4c84d50d1dcb79ce9814 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 8 Apr 2017 22:20:05 +0200 Subject: mmc: core: check also R1 response for stop commands To detect errors like ECC errors, we must parse the R1 response bits. Introduce a helper function to also set the error value of a command when R1 error bits are set. Add ECC error to list of flags checked. Use the new helper for the stop command to call mmc_blk_recovery when detecting ECC errors which are only flagged on the next command after multiblock. Signed-off-by: Wolfram Sang Tested-by: Yoshihiro Shimoda Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/mmc/core/block.c') diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 64f9fda92229..58d4e653dd74 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -1359,9 +1359,18 @@ static inline void mmc_apply_rel_rw(struct mmc_blk_request *brq, R1_ADDRESS_ERROR | /* Misaligned address */ \ R1_BLOCK_LEN_ERROR | /* Transferred block length incorrect */\ R1_WP_VIOLATION | /* Tried to write to protected block */ \ + R1_CARD_ECC_FAILED | /* Card ECC failed */ \ R1_CC_ERROR | /* Card controller error */ \ R1_ERROR) /* General/unknown error */ +static bool mmc_blk_has_cmd_err(struct mmc_command *cmd) +{ + if (!cmd->error && cmd->resp[0] & CMD_ERRORS) + cmd->error = -EIO; + + return cmd->error; +} + static enum mmc_blk_status mmc_blk_err_check(struct mmc_card *card, struct mmc_async_req *areq) { @@ -1383,7 +1392,7 @@ static enum mmc_blk_status mmc_blk_err_check(struct mmc_card *card, * stop.error indicates a problem with the stop command. Data * may have been transferred, or may still be transferring. */ - if (brq->sbc.error || brq->cmd.error || brq->stop.error || + if (brq->sbc.error || brq->cmd.error || mmc_blk_has_cmd_err(&brq->stop) || brq->data.error) { switch (mmc_blk_cmd_recovery(card, req, brq, &ecc_err, &gen_err)) { case ERR_RETRY: -- cgit v1.2.3 From 9820a5b1110126142c50a779622abab560d3fcc1 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 8 Apr 2017 22:20:06 +0200 Subject: mmc: core: for data errors, take response of stop cmd into account Some errors are flagged only with the next command after a multiblock transfer, e.g. ECC error. So, when checking for data transfer errors, we check the result from the stop command as well. Signed-off-by: Wolfram Sang Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/core/block.c') diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 58d4e653dd74..3c7efbdc8591 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -1446,7 +1446,8 @@ static enum mmc_blk_status mmc_blk_err_check(struct mmc_card *card, return MMC_BLK_RETRY; } - if (brq->data.error) { + /* Some errors (ECC) are flagged on the next commmand, so check stop, too */ + if (brq->data.error || brq->stop.error) { if (need_retune && !brq->retune_retry_done) { pr_debug("%s: retrying because a re-tune was needed\n", req->rq_disk->disk_name); @@ -1454,7 +1455,7 @@ static enum mmc_blk_status mmc_blk_err_check(struct mmc_card *card, return MMC_BLK_RETRY; } pr_err("%s: error %d transferring data, sector %u, nr %u, cmd response %#x, card status %#x\n", - req->rq_disk->disk_name, brq->data.error, + req->rq_disk->disk_name, brq->data.error ?: brq->stop.error, (unsigned)blk_rq_pos(req), (unsigned)blk_rq_sectors(req), brq->cmd.resp[0], brq->stop.resp[0]); -- cgit v1.2.3