mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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:
		
							parent
							
								
									0c9ee5ba75
								
							
						
					
					
						commit
						f7b6fc3273
					
				
					 2 changed files with 24 additions and 4 deletions
				
			
		| 
						 | 
					@ -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 @@ static void mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
 | 
				
			||||||
	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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue