mmc: core: Support zeroout using TRIM for eMMC

If an eMMC card supports TRIM and indicates that it erases to zeros, we can
use it to support hardware offloading of REQ_OP_WRITE_ZEROES, so let's add
support for this.

Signed-off-by: Vincent Whitchurch <vincent.whitchurch@axis.com>
Reviewed-by: Avri Altman <Avri.Altman@wdc.com>
Link: https://lore.kernel.org/r/20220429152118.3617303-1-vincent.whitchurch@axis.com
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
Vincent Whitchurch 2022-04-29 17:21:18 +02:00 committed by Ulf Hansson
parent 0c9ee5ba75
commit f7b6fc3273
2 changed files with 24 additions and 4 deletions

View File

@ -126,6 +126,7 @@ struct mmc_blk_data {
#define MMC_BLK_DISCARD BIT(2) #define MMC_BLK_DISCARD BIT(2)
#define MMC_BLK_SECDISCARD BIT(3) #define MMC_BLK_SECDISCARD BIT(3)
#define MMC_BLK_CQE_RECOVERY BIT(4) #define MMC_BLK_CQE_RECOVERY BIT(4)
#define MMC_BLK_TRIM BIT(5)
/* /*
* Only set in main mmc_blk_data associated * Only set in main mmc_blk_data associated
@ -1092,12 +1093,13 @@ static void mmc_blk_issue_drv_op(struct mmc_queue *mq, struct request *req)
blk_mq_end_request(req, ret ? BLK_STS_IOERR : BLK_STS_OK); blk_mq_end_request(req, ret ? BLK_STS_IOERR : BLK_STS_OK);
} }
static void mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) static void mmc_blk_issue_erase_rq(struct mmc_queue *mq, struct request *req,
int type, unsigned int erase_arg)
{ {
struct mmc_blk_data *md = mq->blkdata; struct mmc_blk_data *md = mq->blkdata;
struct mmc_card *card = md->queue.card; struct mmc_card *card = md->queue.card;
unsigned int from, nr; unsigned int from, nr;
int err = 0, type = MMC_BLK_DISCARD; int err = 0;
blk_status_t status = BLK_STS_OK; blk_status_t status = BLK_STS_OK;
if (!mmc_can_erase(card)) { if (!mmc_can_erase(card)) {
@ -1113,13 +1115,13 @@ static void mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
if (card->quirks & MMC_QUIRK_INAND_CMD38) { if (card->quirks & MMC_QUIRK_INAND_CMD38) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
INAND_CMD38_ARG_EXT_CSD, INAND_CMD38_ARG_EXT_CSD,
card->erase_arg == MMC_TRIM_ARG ? erase_arg == MMC_TRIM_ARG ?
INAND_CMD38_ARG_TRIM : INAND_CMD38_ARG_TRIM :
INAND_CMD38_ARG_ERASE, INAND_CMD38_ARG_ERASE,
card->ext_csd.generic_cmd6_time); card->ext_csd.generic_cmd6_time);
} }
if (!err) if (!err)
err = mmc_erase(card, from, nr, card->erase_arg); err = mmc_erase(card, from, nr, erase_arg);
} while (err == -EIO && !mmc_blk_reset(md, card->host, type)); } while (err == -EIO && !mmc_blk_reset(md, card->host, type));
if (err) if (err)
status = BLK_STS_IOERR; status = BLK_STS_IOERR;
@ -1129,6 +1131,19 @@ fail:
blk_mq_end_request(req, status); blk_mq_end_request(req, status);
} }
static void mmc_blk_issue_trim_rq(struct mmc_queue *mq, struct request *req)
{
mmc_blk_issue_erase_rq(mq, req, MMC_BLK_TRIM, MMC_TRIM_ARG);
}
static void mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
{
struct mmc_blk_data *md = mq->blkdata;
struct mmc_card *card = md->queue.card;
mmc_blk_issue_erase_rq(mq, req, MMC_BLK_DISCARD, card->erase_arg);
}
static void mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq, static void mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
struct request *req) struct request *req)
{ {
@ -2329,6 +2344,9 @@ enum mmc_issued mmc_blk_mq_issue_rq(struct mmc_queue *mq, struct request *req)
case REQ_OP_SECURE_ERASE: case REQ_OP_SECURE_ERASE:
mmc_blk_issue_secdiscard_rq(mq, req); mmc_blk_issue_secdiscard_rq(mq, req);
break; break;
case REQ_OP_WRITE_ZEROES:
mmc_blk_issue_trim_rq(mq, req);
break;
case REQ_OP_FLUSH: case REQ_OP_FLUSH:
mmc_blk_issue_flush(mq, req); mmc_blk_issue_flush(mq, req);
break; break;

View File

@ -191,6 +191,8 @@ static void mmc_queue_setup_discard(struct request_queue *q,
q->limits.discard_granularity = SECTOR_SIZE; q->limits.discard_granularity = SECTOR_SIZE;
if (mmc_can_secure_erase_trim(card)) if (mmc_can_secure_erase_trim(card))
blk_queue_flag_set(QUEUE_FLAG_SECERASE, q); blk_queue_flag_set(QUEUE_FLAG_SECERASE, q);
if (mmc_can_trim(card) && card->erased_byte == 0)
blk_queue_max_write_zeroes_sectors(q, max_discard);
} }
static unsigned short mmc_get_max_segments(struct mmc_host *host) static unsigned short mmc_get_max_segments(struct mmc_host *host)