Re: [PATCH] mmc: core: add auto bkops support

From: Alex Lemberg
Date: Wed Jun 08 2016 - 11:01:48 EST


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.


On 6/6/16, 6:07 AM, "linux-mmc-owner@xxxxxxxxxxxxxxx on behalf of Shawn Lin" <linux-mmc-owner@xxxxxxxxxxxxxxx on behalf of shawn.lin@xxxxxxxxxxxxxx> 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@xxxxxxxxxxxxxx>
>---
>
> 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?


>+ 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...


>+ 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?


>+ 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@xxxxxxxxxxxxxxx
>More majordomo info at http://vger.kernel.org/majordomo-info.html