[PATCH] mmc: core: add auto bkops support

Shawn Lin shawn.lin at rock-chips.com
Sat Jun 11 18:13:35 PDT 2016


Hi Alex,

On 2016/6/8 22:46, Alex Lemberg wrote:
> Hi Shawn,
>
> Is the intention in this patch to stop using MANUAL_BKOPS for all eMMC5.1+ devices, and only use AUTO_BKOPS?
> Please see several questions inline.
>

Yup, my intention is to use auto bkops for emmc 5.1+ if possible.

>
> On 6/6/16, 6:07 AM, "linux-mmc-owner at vger.kernel.org on behalf of Shawn Lin" <linux-mmc-owner at vger.kernel.org on behalf of shawn.lin at rock-chips.com> wrote:
>
>> JEDEC eMMC v5.1 introduce an autonomously initiated method
>> for background operations.
>>
>> Host that wants to enable the device to perform background
>> operations during device idle time, should signal the device
>> by setting AUTO_EN in BKOPS_EN field EXT_CSD[163] to 1b. When
>> this bit is set, the device may start or stop background operations
>> whenever it sees fit, without any notification to the host.
>>
>> When AUTO_EN bit is set, the host should keep the device power
>> active. The host may set or clear this bit at any time based on
>> its power constraints or other considerations.
>>
>> Currently the manual bkops is only be used under the async req
>> circumstances and it's a bit complicated to be controlled as the
>> perfect method is that we should do some idle monitor just as rpm
>> and send HPI each time if receiving rd/wr req. But it will impact
>> performance significantly, especially for random iops since the
>> weight of executing HPI against r/w small piece of LBAs is
>> nonnegligible.
>>
>> So we now prefer to select the auto one unconditionally if supported
>> which makes it as simple as possible. It should really good enough
>> for devices to manage its internal policy for bkops rather than the
>> host, which makes us believe that we could achieve the best
>> performance for all the devices implementing auto bkops and the only
>> thing we should do is to disable it when cutting off the power.
>>
>> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
>> ---
>>
>> drivers/mmc/core/core.c  | 127 +++++++++++++++++++++++++++++++++++++----------
>> drivers/mmc/core/mmc.c   |  30 ++++++++++-
>> include/linux/mmc/card.h |   1 +
>> include/linux/mmc/core.h |   4 +-
>> include/linux/mmc/mmc.h  |   1 +
>> 5 files changed, 133 insertions(+), 30 deletions(-)
>>
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index e864187..c2e5a66 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -297,24 +297,13 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
>> 	return 0;
>> }
>>
>> -/**
>> - *	mmc_start_bkops - start BKOPS for supported cards
>> - *	@card: MMC card to start BKOPS
>> - *	@form_exception: A flag to indicate if this function was
>> - *			 called due to an exception raised by the card
>> - *
>> - *	Start background operations whenever requested.
>> - *	When the urgent BKOPS bit is set in a R1 command response
>> - *	then background operations should be started immediately.
>> -*/
>> -void mmc_start_bkops(struct mmc_card *card, bool from_exception)
>> +static void  mmc_start_man_bkops(struct mmc_card *card,
>> +				 bool from_exception)
>> {
>> 	int err;
>> 	int timeout;
>> 	bool use_busy_signal;
>>
>> -	BUG_ON(!card);
>> -
>> 	if (!card->ext_csd.man_bkops_en || mmc_card_doing_bkops(card))
>> 		return;
>>
>> @@ -347,7 +336,7 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
>> 			EXT_CSD_BKOPS_START, 1, timeout,
>> 			use_busy_signal, true, false);
>> 	if (err) {
>> -		pr_warn("%s: Error %d starting bkops\n",
>> +		pr_warn("%s: Error %d starting manual bkops\n",
>> 			mmc_hostname(card->host), err);
>> 		mmc_retune_release(card->host);
>> 		goto out;
>> @@ -365,6 +354,55 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
>> out:
>> 	mmc_release_host(card->host);
>> }
>> +
>> +static void  mmc_start_auto_bkops(struct mmc_card *card)
>> +{
>> +	int err;
>> +
>> +	/* If it's already enable auto_bkops, nothing to do */
>> +	if (card->ext_csd.auto_bkops_en)
>> +		return;
>> +
>> +	mmc_claim_host(card->host);
>> +
>> +	mmc_retune_hold(card->host);
>> +
>> +	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +			EXT_CSD_BKOPS_EN, 2,
>> +			card->ext_csd.generic_cmd6_time);
>> +	if (err)
>> +		pr_warn("%s: Error %d starting auto bkops\n",
>> +			mmc_hostname(card->host), err);
>> +
>> +	card->ext_csd.auto_bkops_en = true;
>> +	mmc_card_set_doing_bkops(card);
>> +	mmc_retune_release(card->host);
>> +	mmc_release_host(card->host);
>> +}
>> +
>> +
>> +/**
>> + *	mmc_start_bkops - start BKOPS for supported cards
>> + *	@card: MMC card to start BKOPS
>> + *	@form_exception: A flag to indicate if this function was
>> + *			 called due to an exception raised by the card
>> + *          @is_auto: A flag to indicate if we should use auto bkops
>> + *
>> + *	Start background operations whenever requested.
>> + *	When the urgent BKOPS bit is set in a R1 command response
>> + *	then background operations should be started immediately, which is
>> + *	only needed for man_bkops.
>> +*/
>> +void mmc_start_bkops(struct mmc_card *card, bool from_exception,
>> +		     bool is_auto)
>> +{
>> +	BUG_ON(!card);
>> +
>> +	if (is_auto)
>
> As far as I understand, the AUTO_BKOPS is enabled during “mmc_init_card()”.
> If its correct, why the “mmc_start_auto_bkops()” function is needed?
>

yes it's not used twice for curren code. will remove it.

>
>> +		return mmc_start_auto_bkops(card);
>> +	else
>> +		return mmc_start_man_bkops(card, from_exception);
>> +}
>> EXPORT_SYMBOL(mmc_start_bkops);
>>
>> /*
>> @@ -610,7 +648,9 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
>> 			if (areq)
>> 				mmc_post_req(host, areq->mrq, -EINVAL);
>>
>> -			mmc_start_bkops(host->card, true);
>> +			/* Prefer to use auto bkops for eMMC 5.1 or later */
>
> If the decision is to use AUTO_BKOPS for all eMMC5.1+ devices, why you need to enable AUTO_BKOPS bit
> on every R1_EXCEPTION_EVENT?
> AUTO_BKOPS should be enabled only once...
>

right, I will remove this enable although it will finally turn back by
the checking of auo_bkops_en.

>
>> +			mmc_start_bkops(host->card, true,
>> +					host->card->ext_csd.rev >= 8);
>>
>> 			/* prepare the request again */
>> 			if (areq)
>> @@ -751,20 +791,10 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries
>>
>> EXPORT_SYMBOL(mmc_wait_for_cmd);
>>
>> -/**
>> - *	mmc_stop_bkops - stop ongoing BKOPS
>> - *	@card: MMC card to check BKOPS
>> - *
>> - *	Send HPI command to stop ongoing background operations to
>> - *	allow rapid servicing of foreground operations, e.g. read/
>> - *	writes. Wait until the card comes out of the programming state
>> - *	to avoid errors in servicing read/write requests.
>> - */
>> -int mmc_stop_bkops(struct mmc_card *card)
>> +static int mmc_stop_man_bkops(struct mmc_card *card)
>> {
>> 	int err = 0;
>>
>> -	BUG_ON(!card);
>> 	err = mmc_interrupt_hpi(card);
>>
>> 	/*
>> @@ -779,6 +809,51 @@ int mmc_stop_bkops(struct mmc_card *card)
>>
>> 	return err;
>> }
>> +
>> +static int mmc_stop_auto_bkops(struct mmc_card *card)
>> +{
>> +	int err = 0;
>> +
>> +	if (!card->ext_csd.auto_bkops_en)
>> +		return 0;
>> +
>
> Shouldn’t the BKOPS_STATUS be checked prior to disabling the BKOPS activity of the device?
>

Hrmm.. I read the whole section of spec for it, and I did find this
requirement for manul bkops but not for the auto one. So what should we
do if using the auto one?

>
>> +	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +			EXT_CSD_BKOPS_EN, 0, MMC_BKOPS_MAX_TIMEOUT,
>> +			true, true, false);
>> +	if (err)
>> +		pr_warn("%s: Error %d stoping auto bkops\n",
>> +			mmc_hostname(card->host), err);
>> +
>> +	card->ext_csd.auto_bkops_en = false;
>> +	mmc_card_clr_doing_bkops(card);
>> +	mmc_retune_release(card->host);
>> +
>> +	return err;
>> +}
>> +
>> +/**
>> + *	mmc_stop_bkops - stop ongoing BKOPS
>> + *	@card: MMC card to check BKOPS
>> + *	@is_auto: A flag to indicate if we should use auto bkops
>> + *
>> + *	Send HPI command to stop ongoing background operations to
>> + *	allow rapid servicing of foreground operations, e.g. read/
>> + *	writes. Wait until the card comes out of the programming state
>> + *	to avoid errors in servicing read/write requests.
>> + *
>> + *	But for auto bkops, we could only disable AUTO_EN instead of
>> + *	sending HPI. And the firmware could do it automatically.
>> + *
>> + */
>> +int mmc_stop_bkops(struct mmc_card *card, bool is_auto)
>> +{
>> +	BUG_ON(!card);
>> +
>> +	if (is_auto)
>> +		return mmc_stop_auto_bkops(card);
>> +	else
>> +		return mmc_stop_man_bkops(card);
>> +}
>> EXPORT_SYMBOL(mmc_stop_bkops);
>>
>> int mmc_read_bkops_status(struct mmc_card *card)
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> index 61d6b34..c65bea7 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -508,6 +508,13 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
>> 		/* check whether the eMMC card supports BKOPS */
>> 		if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
>> 			card->ext_csd.bkops = 1;
>> +
>> +			/* for eMMC v5.1 or later, we could use auto_bkops */
>> +			if (card->ext_csd.rev >= 8)
>> +				card->ext_csd.auto_bkops_en =
>> +					(ext_csd[EXT_CSD_BKOPS_EN] &
>> +						EXT_CSD_AUTO_BKOPS_MASK);
>> +
>> 			card->ext_csd.man_bkops_en =
>> 					(ext_csd[EXT_CSD_BKOPS_EN] &
>> 						EXT_CSD_MANUAL_BKOPS_MASK);
>> @@ -516,6 +523,9 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
>> 			if (!card->ext_csd.man_bkops_en)
>> 				pr_debug("%s: MAN_BKOPS_EN bit is not set\n",
>> 					mmc_hostname(card->host));
>> +			if (!card->ext_csd.auto_bkops_en)
>> +				pr_debug("%s: AUTO_BKOPS_EN bit is not set\n",
>> +					mmc_hostname(card->host));
>> 		}
>>
>> 		/* check whether the eMMC card supports HPI */
>> @@ -1241,7 +1251,7 @@ static int mmc_select_hs400es(struct mmc_card *card)
>> 	}
>>
>> 	err = mmc_select_bus_width(card);
>> -	if (IS_ERR_VALUE(err))
>> +	if (err < 0)
>> 		goto out_err;
>>
>> 	/* Switch card to HS mode */
>> @@ -1676,6 +1686,21 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
>> 	mmc_select_powerclass(card);
>>
>> 	/*
>> +	 * Enable auto bkops feature which is mandatory for
>> +	 * eMMC v5.1 or later
>> +	 */
>> +	if (card->ext_csd.rev >= 8) {
>> +		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +				 EXT_CSD_BKOPS_EN, 2,
>> +				 card->ext_csd.generic_cmd6_time);
>> +		if (err)
>> +			pr_warn("%s: Error %d starting auto bkops\n",
>> +				mmc_hostname(card->host), err);
>> +
>> +		card->ext_csd.auto_bkops_en = true;
>> +	}
>> +
>> +	/*
>> 	 * Enable HPI feature (if supported)
>> 	 */
>> 	if (card->ext_csd.hpi) {
>> @@ -1898,7 +1923,8 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
>> 		goto out;
>>
>> 	if (mmc_card_doing_bkops(host->card)) {
>> -		err = mmc_stop_bkops(host->card);
>> +		err = mmc_stop_bkops(host->card,
>> +				     host->card->ext_csd.rev >= 8);
>> 		if (err)
>> 			goto out;
>> 	}
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> index 22defc2..f82f9be 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -84,6 +84,7 @@ struct mmc_ext_csd {
>> 	unsigned int		hpi_cmd;		/* cmd used as HPI */
>> 	bool			bkops;		/* background support bit */
>> 	bool			man_bkops_en;	/* manual bkops enable bit */
>> +	bool			auto_bkops_en;	/* auto bkops enbale bit */
>> 	unsigned int            data_sector_size;       /* 512 bytes or 4KB */
>> 	unsigned int            data_tag_unit_size;     /* DATA TAG UNIT size */
>> 	unsigned int		boot_ro_lock;		/* ro lock support */
>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>> index b01e77d..45d53ef 100644
>> --- a/include/linux/mmc/core.h
>> +++ b/include/linux/mmc/core.h
>> @@ -140,7 +140,7 @@ struct mmc_request {
>> struct mmc_card;
>> struct mmc_async_req;
>>
>> -extern int mmc_stop_bkops(struct mmc_card *);
>> +extern int mmc_stop_bkops(struct mmc_card *, bool);
>> extern int mmc_read_bkops_status(struct mmc_card *);
>> extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
>> 					   struct mmc_async_req *, int *);
>> @@ -150,7 +150,7 @@ extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
>> extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
>> extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
>> 	struct mmc_command *, int);
>> -extern void mmc_start_bkops(struct mmc_card *card, bool from_exception);
>> +extern void mmc_start_bkops(struct mmc_card *, bool, bool);
>> extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
>> extern int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error);
>> extern int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd);
>> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
>> index c376209..a1cced9 100644
>> --- a/include/linux/mmc/mmc.h
>> +++ b/include/linux/mmc/mmc.h
>> @@ -436,6 +436,7 @@ struct _mmc_csd {
>>  * BKOPS modes
>>  */
>> #define EXT_CSD_MANUAL_BKOPS_MASK	0x01
>> +#define EXT_CSD_AUTO_BKOPS_MASK		0x02
>>
>> /*
>>  * MMC_SWITCH access modes
>> --
>> 2.3.7
>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>


-- 
Best Regards
Shawn Lin




More information about the Linux-rockchip mailing list