* [PATCH v4 0/4] mmc: handle undervoltage events and prevent eMMC corruption
@ 2025-03-10 10:22 Oleksij Rempel
2025-03-10 10:22 ` [PATCH v4 1/4] mmc: core: Handle undervoltage events and register regulator notifiers Oleksij Rempel
` (4 more replies)
0 siblings, 5 replies; 8+ messages in thread
From: Oleksij Rempel @ 2025-03-10 10:22 UTC (permalink / raw)
To: Ulf Hansson
Cc: Oleksij Rempel, kernel, linux-kernel, linux-mmc,
Greg Kroah-Hartman, Mark Brown, Rafael J. Wysocki,
Søren Andersen, Christian Loehle, Adrian Hunter, Avri Altman
changes v4:
- drop HPI and SDHCI related patches
This patch set introduces a framework for handling undervoltage events
in the MMC subsystem. The goal is to improve system reliability by
ensuring graceful handling of power fluctuations that could otherwise
lead to metadata corruption, potentially rendering the eMMC chip
unusable or causing significant data loss.
## Problem Statement
Power fluctuations and sudden losses can leave eMMC devices in an
undefined state, leading to severe consequences. The worst case can
result in metadata corruption, making the entire storage inaccessible.
While some eMMC devices promise to handle such situations internally,
experience shows that some chip variants are still affected. This has
led vendors to take a more protective approach, implementing external
undervoltage handling as a precautionary measure to avoid costly field
failures and returns.
The existence of the "Power Off Notification" feature in the eMMC
standard itself serves as indirect evidence that this is a real-world
issue. While some projects have already faced the consequences of
ignoring this problem (often at significant cost), specific cases cannot
be disclosed due to NDAs.
## Challenges and Implementation Approach
1. **Raising awareness of the problem**: While vendors have used
proprietary solutions for years, a unified approach is needed upstream.
This patch set is a first step in making that happen.
2. **Finding an acceptable implementation path**: There are multiple
ways to handle undervoltage - either in the kernel or in user space,
through a global shutdown mechanism, or using the regulator framework.
This patch set takes the kernel-based approach but does not prevent
future extensions, such as allowing user-space handoff once available.
3. **Preparing for vendor adoption and testing**: By providing a
structured solution upstream, this patch set lowers the barrier for
vendors to standardize their undervoltage handling instead of relying on
fragmented, out-of-tree implementations.
## Current Limitations
This patch set is an initial step and does not yet cover all possible
design restrictions or edge cases. Future improvements may include
better coordination with user space and enhancements based on broader
testing.
## Testing Details
The implementation was tested on an iMX8MP-based system. The board had
approximately 100ms of available power hold-up time. The Power Off
Notification was sent ~4ms after the board was detached from the power
supply, allowing sufficient time for the eMMC to handle the event
properly. Tests were conducted under both idle conditions and active
read/write operations.
Oleksij Rempel (4):
mmc: core: Handle undervoltage events and register regulator notifiers
mmc: core: refactor _mmc_suspend() for undervoltage handling
mmc: core: add undervoltage handler for MMC/eMMC devices
mmc: block: abort requests and suppress errors after undervoltage
shutdown
drivers/mmc/core/block.c | 2 +-
drivers/mmc/core/core.c | 30 +++++++++
drivers/mmc/core/core.h | 2 +
drivers/mmc/core/mmc.c | 127 +++++++++++++++++++++++++++++------
drivers/mmc/core/queue.c | 2 +-
drivers/mmc/core/regulator.c | 124 ++++++++++++++++++++++++++++++++++
include/linux/mmc/host.h | 8 +++
7 files changed, 274 insertions(+), 21 deletions(-)
--
2.39.5
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v4 1/4] mmc: core: Handle undervoltage events and register regulator notifiers
2025-03-10 10:22 [PATCH v4 0/4] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
@ 2025-03-10 10:22 ` Oleksij Rempel
2025-03-10 10:22 ` [PATCH v4 2/4] mmc: core: refactor _mmc_suspend() for undervoltage handling Oleksij Rempel
` (3 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Oleksij Rempel @ 2025-03-10 10:22 UTC (permalink / raw)
To: Ulf Hansson
Cc: Oleksij Rempel, kernel, linux-kernel, linux-mmc,
Greg Kroah-Hartman, Mark Brown, Rafael J. Wysocki,
Søren Andersen, Christian Loehle, Adrian Hunter, Avri Altman
Extend the MMC core to handle undervoltage events by implementing
infrastructure to notify the MMC bus about voltage drops.
Background & Decision at LPC24:
This solution was proposed and refined during LPC24 in the talk
"Graceful Under Pressure: Prioritizing Shutdown to Protect Your Data in
Embedded Systems," which aimed to address how Linux should handle power
fluctuations in embedded devices to prevent data corruption or storage
damage.
At the time, multiple possible solutions were considered:
1. Triggering a system-wide suspend or shutdown: when undervoltage is
detected, with device-specific prioritization to ensure critical
components shut down first.
- This approach was disliked by Greg Kroah-Hartman, as it introduced
complexity and was not suitable for all use cases.
2. Notifying relevant devices through the regulator framework: to allow
graceful per-device handling.
- This approach was agreed upon as the most acceptable by participants
in the discussion, including Greg Kroah-Hartman, Mark Brown,
and Rafael J. Wysocki.
- This patch implements that decision by integrating undervoltage
handling into the MMC subsystem.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v3:
- filter supported cards at early stage
- add locking in mmc_handle_regulator_event()
- claim/release host in mmc_handle_undervoltage()
---
drivers/mmc/core/core.c | 30 +++++++++
drivers/mmc/core/core.h | 2 +
drivers/mmc/core/regulator.c | 124 +++++++++++++++++++++++++++++++++++
include/linux/mmc/host.h | 8 +++
4 files changed, 164 insertions(+)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 5241528f8b90..06adfb54825b 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1399,6 +1399,36 @@ void mmc_power_cycle(struct mmc_host *host, u32 ocr)
mmc_power_up(host, ocr);
}
+/**
+ * mmc_handle_undervoltage - Handle an undervoltage event on the MMC bus
+ * @host: The MMC host that detected the undervoltage condition
+ *
+ * This function is called when an undervoltage event is detected on one of
+ * the MMC regulators.
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+int mmc_handle_undervoltage(struct mmc_host *host)
+{
+ int ret;
+
+ mmc_claim_host(host);
+
+ if (!host->bus_ops->handle_undervoltage) {
+ mmc_release_host(host);
+ return 0;
+ }
+
+ dev_warn(mmc_dev(host), "%s: Undervoltage detected, initiating emergency stop\n",
+ mmc_hostname(host));
+
+ ret = host->bus_ops->handle_undervoltage(host);
+
+ mmc_release_host(host);
+
+ return ret;
+}
+
/*
* Assign a mmc bus handler to a host. Only one bus handler may control a
* host at any given time.
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index fc9c066e6468..b77f053039ab 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -31,6 +31,7 @@ struct mmc_bus_ops {
int (*sw_reset)(struct mmc_host *);
bool (*cache_enabled)(struct mmc_host *);
int (*flush_cache)(struct mmc_host *);
+ int (*handle_undervoltage)(struct mmc_host *host);
};
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
@@ -59,6 +60,7 @@ void mmc_power_off(struct mmc_host *host);
void mmc_power_cycle(struct mmc_host *host, u32 ocr);
void mmc_set_initial_state(struct mmc_host *host);
u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max);
+int mmc_handle_undervoltage(struct mmc_host *host);
static inline void mmc_delay(unsigned int ms)
{
diff --git a/drivers/mmc/core/regulator.c b/drivers/mmc/core/regulator.c
index 3dae2e9b7978..1074567e242f 100644
--- a/drivers/mmc/core/regulator.c
+++ b/drivers/mmc/core/regulator.c
@@ -7,6 +7,7 @@
#include <linux/err.h>
#include <linux/log2.h>
#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
#include <linux/mmc/host.h>
@@ -262,6 +263,107 @@ static inline int mmc_regulator_get_ocrmask(struct regulator *supply)
#endif /* CONFIG_REGULATOR */
+static void mmc_undervoltage_workfn(struct work_struct *work)
+{
+ struct mmc_supply *supply;
+ struct mmc_host *mmc;
+
+ supply = container_of(work, struct mmc_supply, uv_work);
+ mmc = container_of(supply, struct mmc_host, supply);
+
+ mmc_handle_undervoltage(mmc);
+}
+
+static int mmc_handle_regulator_event(struct mmc_host *mmc,
+ const char *regulator_name,
+ unsigned long event)
+{
+ unsigned long flags;
+
+ switch (event) {
+ case REGULATOR_EVENT_UNDER_VOLTAGE:
+ /* Currently we support only MMC cards */
+ spin_lock_irqsave(&mmc->lock, flags);
+ if (mmc->undervoltage || !mmc->card ||
+ !mmc_card_mmc(mmc->card)) {
+ spin_unlock_irqrestore(&mmc->lock, flags);
+ return NOTIFY_OK;
+ }
+
+ mmc->undervoltage = true;
+ spin_unlock_irqrestore(&mmc->lock, flags);
+
+ queue_work(system_highpri_wq, &mmc->supply.uv_work);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int mmc_vmmc_notifier_callback(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct mmc_supply *supply;
+ struct mmc_host *mmc;
+
+ supply = container_of(nb, struct mmc_supply, vmmc_nb);
+ mmc = container_of(supply, struct mmc_host, supply);
+
+ return mmc_handle_regulator_event(mmc, "vmmc", event);
+}
+
+static int mmc_vqmmc_notifier_callback(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct mmc_supply *supply;
+ struct mmc_host *mmc;
+
+ supply = container_of(nb, struct mmc_supply, vqmmc_nb);
+ mmc = container_of(supply, struct mmc_host, supply);
+
+ return mmc_handle_regulator_event(mmc, "vqmmc", event);
+}
+
+static int mmc_vqmmc2_notifier_callback(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct mmc_supply *supply;
+ struct mmc_host *mmc;
+
+ supply = container_of(nb, struct mmc_supply, vqmmc2_nb);
+ mmc = container_of(supply, struct mmc_host, supply);
+
+ return mmc_handle_regulator_event(mmc, "vqmmc2", event);
+}
+
+static void
+mmc_register_regulator_notifier(struct mmc_host *mmc,
+ struct regulator *regulator,
+ struct notifier_block *nb,
+ int (*callback)(struct notifier_block *,
+ unsigned long, void *),
+ const char *name)
+{
+ struct device *dev = mmc_dev(mmc);
+ int ret;
+
+ nb->notifier_call = callback;
+ ret = devm_regulator_register_notifier(regulator, nb);
+ if (ret)
+ dev_warn(dev, "Failed to register %s notifier: %pe\n", name,
+ ERR_PTR(ret));
+}
+
+static void mmc_undervoltage_work_cleanup(void *data)
+{
+ struct mmc_supply *supply = data;
+
+ /* Ensure the work is canceled or flushed here */
+ cancel_work_sync(&supply->uv_work);
+}
+
/**
* mmc_regulator_get_supply - try to get VMMC and VQMMC regulators for a host
* @mmc: the host to regulate
@@ -281,6 +383,13 @@ int mmc_regulator_get_supply(struct mmc_host *mmc)
mmc->supply.vqmmc = devm_regulator_get_optional(dev, "vqmmc");
mmc->supply.vqmmc2 = devm_regulator_get_optional(dev, "vqmmc2");
+ INIT_WORK(&mmc->supply.uv_work, mmc_undervoltage_workfn);
+
+ ret = devm_add_action_or_reset(dev, mmc_undervoltage_work_cleanup,
+ &mmc->supply);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add cleanup action\n");
+
if (IS_ERR(mmc->supply.vmmc)) {
if (PTR_ERR(mmc->supply.vmmc) == -EPROBE_DEFER)
return dev_err_probe(dev, -EPROBE_DEFER,
@@ -293,6 +402,11 @@ int mmc_regulator_get_supply(struct mmc_host *mmc)
mmc->ocr_avail = ret;
else
dev_warn(dev, "Failed getting OCR mask: %d\n", ret);
+
+ mmc_register_regulator_notifier(mmc, mmc->supply.vmmc,
+ &mmc->supply.vmmc_nb,
+ mmc_vmmc_notifier_callback,
+ "vmmc");
}
if (IS_ERR(mmc->supply.vqmmc)) {
@@ -301,12 +415,22 @@ int mmc_regulator_get_supply(struct mmc_host *mmc)
"vqmmc regulator not available\n");
dev_dbg(dev, "No vqmmc regulator found\n");
+ } else {
+ mmc_register_regulator_notifier(mmc, mmc->supply.vqmmc,
+ &mmc->supply.vqmmc_nb,
+ mmc_vqmmc_notifier_callback,
+ "vqmmc");
}
if (IS_ERR(mmc->supply.vqmmc2)) {
if (PTR_ERR(mmc->supply.vqmmc2) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_dbg(dev, "No vqmmc2 regulator found\n");
+ } else {
+ mmc_register_regulator_notifier(mmc, mmc->supply.vqmmc2,
+ &mmc->supply.vqmmc2_nb,
+ mmc_vqmmc2_notifier_callback,
+ "vqmmc2");
}
return 0;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 68f09a955a90..4e147ad82804 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -11,6 +11,7 @@
#include <linux/device.h>
#include <linux/fault-inject.h>
#include <linux/debugfs.h>
+#include <linux/workqueue.h>
#include <linux/mmc/core.h>
#include <linux/mmc/card.h>
@@ -342,6 +343,12 @@ struct mmc_supply {
struct regulator *vmmc; /* Card power supply */
struct regulator *vqmmc; /* Optional Vccq supply */
struct regulator *vqmmc2; /* Optional supply for phy */
+
+ struct notifier_block vmmc_nb; /* Notifier for vmmc */
+ struct notifier_block vqmmc_nb; /* Notifier for vqmmc */
+ struct notifier_block vqmmc2_nb; /* Notifier for vqmmc2 */
+
+ struct work_struct uv_work; /* Undervoltage work */
};
struct mmc_ctx {
@@ -493,6 +500,7 @@ struct mmc_host {
unsigned int retune_crc_disable:1; /* don't trigger retune upon crc */
unsigned int can_dma_map_merge:1; /* merging can be used */
unsigned int vqmmc_enabled:1; /* vqmmc regulator is enabled */
+ unsigned int undervoltage:1; /* Undervoltage state */
int rescan_disable; /* disable card detection */
int rescan_entered; /* used with nonremovable devices */
--
2.39.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v4 2/4] mmc: core: refactor _mmc_suspend() for undervoltage handling
2025-03-10 10:22 [PATCH v4 0/4] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
2025-03-10 10:22 ` [PATCH v4 1/4] mmc: core: Handle undervoltage events and register regulator notifiers Oleksij Rempel
@ 2025-03-10 10:22 ` Oleksij Rempel
2025-03-10 10:22 ` [PATCH v4 3/4] mmc: core: add undervoltage handler for MMC/eMMC devices Oleksij Rempel
` (2 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Oleksij Rempel @ 2025-03-10 10:22 UTC (permalink / raw)
To: Ulf Hansson
Cc: Oleksij Rempel, kernel, linux-kernel, linux-mmc,
Greg Kroah-Hartman, Mark Brown, Rafael J. Wysocki,
Søren Andersen, Christian Loehle, Adrian Hunter, Avri Altman
Introduce an is_undervoltage parameter to _mmc_suspend() to apply a
short power-off sequence and optionally flush the cache. This refactoring
prepares for undervoltage support in a follow-up patch.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v3:
- add comments
- make sure _mmc_flush_cache is not executed in the undervoltage case
---
drivers/mmc/core/mmc.c | 30 +++++++++++++++++++++---------
1 file changed, 21 insertions(+), 9 deletions(-)
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 6a23be214543..9270bde445ad 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2104,20 +2104,32 @@ static int _mmc_flush_cache(struct mmc_host *host)
return err;
}
-static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
+static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
+ bool is_undervoltage)
{
+ unsigned int notify_type;
int err = 0;
- unsigned int notify_type = is_suspend ? EXT_CSD_POWER_OFF_SHORT :
- EXT_CSD_POWER_OFF_LONG;
+
+ /* In case of undervoltage, we don't have much time, so use short. */
+ if (is_undervoltage || is_suspend)
+ notify_type = EXT_CSD_POWER_OFF_SHORT;
+ else
+ notify_type = EXT_CSD_POWER_OFF_LONG;
mmc_claim_host(host);
if (mmc_card_suspended(host->card))
goto out;
- err = _mmc_flush_cache(host);
- if (err)
- goto out;
+ /*
+ * For the undervoltage case, we care more about device integrity.
+ * Avoid cache flush and notify the device to power off quickly.
+ */
+ if (!is_undervoltage) {
+ err = _mmc_flush_cache(host);
+ if (err)
+ goto out;
+ }
if (mmc_can_poweroff_notify(host->card) &&
((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend ||
@@ -2144,7 +2156,7 @@ static int mmc_suspend(struct mmc_host *host)
{
int err;
- err = _mmc_suspend(host, true);
+ err = _mmc_suspend(host, true, false);
if (!err) {
pm_runtime_disable(&host->card->dev);
pm_runtime_set_suspended(&host->card->dev);
@@ -2191,7 +2203,7 @@ static int mmc_shutdown(struct mmc_host *host)
err = _mmc_resume(host);
if (!err)
- err = _mmc_suspend(host, false);
+ err = _mmc_suspend(host, false, false);
return err;
}
@@ -2215,7 +2227,7 @@ static int mmc_runtime_suspend(struct mmc_host *host)
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
return 0;
- err = _mmc_suspend(host, true);
+ err = _mmc_suspend(host, true, false);
if (err)
pr_err("%s: error %d doing aggressive suspend\n",
mmc_hostname(host), err);
--
2.39.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v4 3/4] mmc: core: add undervoltage handler for MMC/eMMC devices
2025-03-10 10:22 [PATCH v4 0/4] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
2025-03-10 10:22 ` [PATCH v4 1/4] mmc: core: Handle undervoltage events and register regulator notifiers Oleksij Rempel
2025-03-10 10:22 ` [PATCH v4 2/4] mmc: core: refactor _mmc_suspend() for undervoltage handling Oleksij Rempel
@ 2025-03-10 10:22 ` Oleksij Rempel
2025-03-10 10:22 ` [PATCH v4 4/4] mmc: block: abort requests and suppress errors after undervoltage shutdown Oleksij Rempel
2025-03-20 14:36 ` [PATCH v4 0/4] mmc: handle undervoltage events and prevent eMMC corruption Ulf Hansson
4 siblings, 0 replies; 8+ messages in thread
From: Oleksij Rempel @ 2025-03-10 10:22 UTC (permalink / raw)
To: Ulf Hansson
Cc: Oleksij Rempel, kernel, linux-kernel, linux-mmc,
Greg Kroah-Hartman, Mark Brown, Rafael J. Wysocki,
Søren Andersen, Christian Loehle, Adrian Hunter, Avri Altman
Introduce `_mmc_handle_undervoltage()` to handle undervoltage events for
MMC/eMMC devices. This function performs a controlled suspend. After
completing the sequence, the card is marked as removed to prevent
further interactions, ensuring that no further commands are issued after
an emergency stop.
Implementation Details:
1. **Suspend the card in an emergency state**:
- Call `__mmc_suspend()` with `is_undervoltage = true`, which ensures:
- The power-off notification uses `EXT_CSD_POWER_OFF_SHORT`.
- Cache flushing is skipped to minimize time delays.
- If power-off notify is unsupported, alternative methods like sleep
or deselect are used to transition the card into a safe state.
2. **Mark the card as removed**:
- This prevents further commands from being issued to the card after
undervoltage shutdown, avoiding potential corruption.
To support this, introduce `__mmc_suspend()` and `__mmc_resume()` as
internal helpers that omit `mmc_claim_host()/mmc_release_host()`,
allowing them to be called when the host is already claimed.
The caller of `_mmc_handle_undervoltage()` is responsible for invoking
`mmc_claim_host()` before calling this function and `mmc_release_host()`
afterward to ensure exclusive access to the host during the emergency
shutdown process.
Device Handling Considerations:
- **For eMMC storage**: The new undervoltage handler applies the correct
power-down sequence using power-off notify or alternative methods.
- **For SD cards**: The current implementation does not handle undervoltage
events for SD cards. Future extensions may be needed to implement proper
handling.
Testing:
This implementation was tested on an iMX8MP-based system, verifying that
the undervoltage sequence correctly stops ongoing operations and
prevents further MMC transactions after the event. The board had
approximately 100ms of available power hold-up time. The Power Off
Notification was sent ~4ms after the board was detached from the power
supply, allowing sufficient time for the eMMC to handle the event
properly.
The testing was performed using a logic analyzer to monitor command
sequences and timing. While this method confirms that the expected
sequence was executed, it does not provide insights into the actual
internal behavior of the eMMC storage.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v4:
- Drop HPI step.
changes v3:
- reword commit message.
- add comments in the code
- do not try to resume sleeping device
---
drivers/mmc/core/mmc.c | 103 +++++++++++++++++++++++++++++++++++------
1 file changed, 90 insertions(+), 13 deletions(-)
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 9270bde445ad..9b801b997a21 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2104,8 +2104,8 @@ static int _mmc_flush_cache(struct mmc_host *host)
return err;
}
-static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
- bool is_undervoltage)
+static int __mmc_suspend(struct mmc_host *host, bool is_suspend,
+ bool is_undervoltage)
{
unsigned int notify_type;
int err = 0;
@@ -2116,8 +2116,6 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
else
notify_type = EXT_CSD_POWER_OFF_LONG;
- mmc_claim_host(host);
-
if (mmc_card_suspended(host->card))
goto out;
@@ -2145,7 +2143,18 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
mmc_card_set_suspended(host->card);
}
out:
+ return err;
+}
+
+static int _mmc_suspend(struct mmc_host *host, bool is_suspend,
+ bool is_undervoltage)
+{
+ int err;
+
+ mmc_claim_host(host);
+ err = __mmc_suspend(host, is_suspend, is_undervoltage);
mmc_release_host(host);
+
return err;
}
@@ -2165,6 +2174,20 @@ static int mmc_suspend(struct mmc_host *host)
return err;
}
+static int __mmc_resume(struct mmc_host *host)
+{
+ int err;
+
+ if (!mmc_card_suspended(host->card))
+ return 0;
+
+ mmc_power_up(host, host->card->ocr);
+ err = mmc_init_card(host, host->card->ocr, host->card);
+ mmc_card_clr_suspended(host->card);
+
+ return err;
+}
+
/*
* This function tries to determine if the same card is still present
* and, if so, restore all state to it.
@@ -2174,16 +2197,9 @@ static int _mmc_resume(struct mmc_host *host)
int err = 0;
mmc_claim_host(host);
-
- if (!mmc_card_suspended(host->card))
- goto out;
-
- mmc_power_up(host, host->card->ocr);
- err = mmc_init_card(host, host->card->ocr, host->card);
- mmc_card_clr_suspended(host->card);
-
-out:
+ err = __mmc_resume(host);
mmc_release_host(host);
+
return err;
}
@@ -2194,6 +2210,13 @@ static int mmc_shutdown(struct mmc_host *host)
{
int err = 0;
+ /*
+ * In case of undervoltage, the card will be powered off by
+ * _mmc_handle_undervoltage()
+ */
+ if (host->undervoltage)
+ return 0;
+
/*
* In a specific case for poweroff notify, we need to resume the card
* before we can shutdown it properly.
@@ -2285,6 +2308,59 @@ static int _mmc_hw_reset(struct mmc_host *host)
return mmc_init_card(host, card->ocr, card);
}
+/**
+ * _mmc_handle_undervoltage - Handle an undervoltage event for MMC/eMMC devices
+ * @host: MMC host structure
+ *
+ * This function is triggered when an undervoltage condition is detected.
+ * It attempts to transition the device into a low-power or safe state to
+ * prevent data corruption.
+ *
+ * Steps performed:
+ * 1. If no card is present, return immediately.
+ * 2. Perform an emergency suspend using EXT_CSD_POWER_OFF_SHORT if possible.
+ * - If power-off notify is not supported, fallback mechanisms like sleep or
+ * deselecting the card are attempted.
+ * - Cache flushing is skipped to reduce execution time.
+ * 3. Mark the card as removed to prevent further interactions after
+ * undervoltage.
+ *
+ * Note: This function does not handle host claiming or releasing. The caller
+ * must ensure that the host is properly claimed before calling this
+ * function and released afterward.
+ *
+ * Returns: 0 on success, or a negative error code if any step fails.
+ */
+static int _mmc_handle_undervoltage(struct mmc_host *host)
+{
+ struct mmc_card *card = host->card;
+ int err = 0;
+
+ /* If there is no card attached, nothing to do */
+ if (!card)
+ return 0;
+
+ /*
+ * Perform an emergency suspend to power off the eMMC quickly.
+ * This ensures the device enters a safe state before power is lost.
+ * We first attempt EXT_CSD_POWER_OFF_SHORT, but if power-off notify
+ * is not supported, we fall back to sleep mode or deselecting the card.
+ * Cache flushing is skipped to minimize delay.
+ */
+ err = __mmc_suspend(host, false, true);
+ if (err)
+ pr_err("%s: error %d doing suspend\n", mmc_hostname(host), err);
+
+ /*
+ * Mark the card as removed to prevent further operations.
+ * This ensures the system does not attempt to access the device
+ * after an undervoltage event, avoiding potential corruption.
+ */
+ mmc_card_set_removed(card);
+
+ return err;
+}
+
static const struct mmc_bus_ops mmc_ops = {
.remove = mmc_remove,
.detect = mmc_detect,
@@ -2297,6 +2373,7 @@ static const struct mmc_bus_ops mmc_ops = {
.hw_reset = _mmc_hw_reset,
.cache_enabled = _mmc_cache_enabled,
.flush_cache = _mmc_flush_cache,
+ .handle_undervoltage = _mmc_handle_undervoltage,
};
/*
--
2.39.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v4 4/4] mmc: block: abort requests and suppress errors after undervoltage shutdown
2025-03-10 10:22 [PATCH v4 0/4] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
` (2 preceding siblings ...)
2025-03-10 10:22 ` [PATCH v4 3/4] mmc: core: add undervoltage handler for MMC/eMMC devices Oleksij Rempel
@ 2025-03-10 10:22 ` Oleksij Rempel
2025-03-20 14:36 ` [PATCH v4 0/4] mmc: handle undervoltage events and prevent eMMC corruption Ulf Hansson
4 siblings, 0 replies; 8+ messages in thread
From: Oleksij Rempel @ 2025-03-10 10:22 UTC (permalink / raw)
To: Ulf Hansson
Cc: Oleksij Rempel, kernel, linux-kernel, linux-mmc,
Greg Kroah-Hartman, Mark Brown, Rafael J. Wysocki,
Søren Andersen, Christian Loehle, Adrian Hunter, Avri Altman
Extend the existing card removal checks in mmc_blk_mq_complete_rq() and
mmc_mq_queue_rq() to also account for the undervoltage state. If the host
has entered undervoltage shutdown, mark requests as quiet and abort them
early to prevent unnecessary retries and error logging. This ensures no
further operations are attempted on the card after an emergency stop.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
drivers/mmc/core/block.c | 2 +-
drivers/mmc/core/queue.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 4830628510e6..ecb87da0e257 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -2129,7 +2129,7 @@ static void mmc_blk_mq_complete_rq(struct mmc_queue *mq, struct request *req)
} else if (mqrq->retries++ < MMC_MAX_RETRIES) {
blk_mq_requeue_request(req, true);
} else {
- if (mmc_card_removed(mq->card))
+ if (mmc_card_removed(mq->card) || mq->card->host->undervoltage)
req->rq_flags |= RQF_QUIET;
blk_mq_end_request(req, BLK_STS_IOERR);
}
diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c
index ab662f502fe7..f46e01988fe8 100644
--- a/drivers/mmc/core/queue.c
+++ b/drivers/mmc/core/queue.c
@@ -239,7 +239,7 @@ static blk_status_t mmc_mq_queue_rq(struct blk_mq_hw_ctx *hctx,
bool get_card, cqe_retune_ok;
blk_status_t ret;
- if (mmc_card_removed(mq->card)) {
+ if (mmc_card_removed(mq->card) || mq->card->host->undervoltage) {
req->rq_flags |= RQF_QUIET;
return BLK_STS_IOERR;
}
--
2.39.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v4 0/4] mmc: handle undervoltage events and prevent eMMC corruption
2025-03-10 10:22 [PATCH v4 0/4] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
` (3 preceding siblings ...)
2025-03-10 10:22 ` [PATCH v4 4/4] mmc: block: abort requests and suppress errors after undervoltage shutdown Oleksij Rempel
@ 2025-03-20 14:36 ` Ulf Hansson
2025-05-05 12:48 ` Oleksij Rempel
4 siblings, 1 reply; 8+ messages in thread
From: Ulf Hansson @ 2025-03-20 14:36 UTC (permalink / raw)
To: Oleksij Rempel
Cc: kernel, linux-kernel, linux-mmc, Greg Kroah-Hartman, Mark Brown,
Rafael J. Wysocki, Søren Andersen, Christian Loehle,
Adrian Hunter, Avri Altman
Hi Oleksij,
On Mon, 10 Mar 2025 at 11:22, Oleksij Rempel <o.rempel@pengutronix.de> wrote:
>
> changes v4:
> - drop HPI and SDHCI related patches
>
> This patch set introduces a framework for handling undervoltage events
> in the MMC subsystem. The goal is to improve system reliability by
> ensuring graceful handling of power fluctuations that could otherwise
> lead to metadata corruption, potentially rendering the eMMC chip
> unusable or causing significant data loss.
Thanks for posting this! I will spend some time reviewing this next
week and let you know my comments then.
However, I just wanted to let you know that I just posted a series [1]
(forgot to cc you, sorry), which also reworks the way _mmc_suspend()
understands what scenario it should be running. I am guessing that
re-work is simplifying for your $subject series too. Maybe you would
like to have a look?
Kind regards
Uffe
[1]
https://lore.kernel.org/all/20250320140040.162416-1-ulf.hansson@linaro.org/
>
> ## Problem Statement
>
> Power fluctuations and sudden losses can leave eMMC devices in an
> undefined state, leading to severe consequences. The worst case can
> result in metadata corruption, making the entire storage inaccessible.
> While some eMMC devices promise to handle such situations internally,
> experience shows that some chip variants are still affected. This has
> led vendors to take a more protective approach, implementing external
> undervoltage handling as a precautionary measure to avoid costly field
> failures and returns.
>
> The existence of the "Power Off Notification" feature in the eMMC
> standard itself serves as indirect evidence that this is a real-world
> issue. While some projects have already faced the consequences of
> ignoring this problem (often at significant cost), specific cases cannot
> be disclosed due to NDAs.
>
> ## Challenges and Implementation Approach
>
> 1. **Raising awareness of the problem**: While vendors have used
> proprietary solutions for years, a unified approach is needed upstream.
> This patch set is a first step in making that happen.
>
> 2. **Finding an acceptable implementation path**: There are multiple
> ways to handle undervoltage - either in the kernel or in user space,
> through a global shutdown mechanism, or using the regulator framework.
> This patch set takes the kernel-based approach but does not prevent
> future extensions, such as allowing user-space handoff once available.
>
> 3. **Preparing for vendor adoption and testing**: By providing a
> structured solution upstream, this patch set lowers the barrier for
> vendors to standardize their undervoltage handling instead of relying on
> fragmented, out-of-tree implementations.
>
> ## Current Limitations
>
> This patch set is an initial step and does not yet cover all possible
> design restrictions or edge cases. Future improvements may include
> better coordination with user space and enhancements based on broader
> testing.
>
> ## Testing Details
>
> The implementation was tested on an iMX8MP-based system. The board had
> approximately 100ms of available power hold-up time. The Power Off
> Notification was sent ~4ms after the board was detached from the power
> supply, allowing sufficient time for the eMMC to handle the event
> properly. Tests were conducted under both idle conditions and active
> read/write operations.
>
> Oleksij Rempel (4):
> mmc: core: Handle undervoltage events and register regulator notifiers
> mmc: core: refactor _mmc_suspend() for undervoltage handling
> mmc: core: add undervoltage handler for MMC/eMMC devices
> mmc: block: abort requests and suppress errors after undervoltage
> shutdown
>
> drivers/mmc/core/block.c | 2 +-
> drivers/mmc/core/core.c | 30 +++++++++
> drivers/mmc/core/core.h | 2 +
> drivers/mmc/core/mmc.c | 127 +++++++++++++++++++++++++++++------
> drivers/mmc/core/queue.c | 2 +-
> drivers/mmc/core/regulator.c | 124 ++++++++++++++++++++++++++++++++++
> include/linux/mmc/host.h | 8 +++
> 7 files changed, 274 insertions(+), 21 deletions(-)
>
> --
> 2.39.5
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v4 0/4] mmc: handle undervoltage events and prevent eMMC corruption
2025-03-20 14:36 ` [PATCH v4 0/4] mmc: handle undervoltage events and prevent eMMC corruption Ulf Hansson
@ 2025-05-05 12:48 ` Oleksij Rempel
2025-05-05 13:10 ` Ulf Hansson
0 siblings, 1 reply; 8+ messages in thread
From: Oleksij Rempel @ 2025-05-05 12:48 UTC (permalink / raw)
To: Ulf Hansson
Cc: kernel, linux-kernel, linux-mmc, Greg Kroah-Hartman, Mark Brown,
Rafael J. Wysocki, Søren Andersen, Christian Loehle,
Adrian Hunter, Avri Altman
Hi Ulf,
Sorry for very late replay,
On Thu, Mar 20, 2025 at 03:36:32PM +0100, Ulf Hansson wrote:
> Hi Oleksij,
>
> On Mon, 10 Mar 2025 at 11:22, Oleksij Rempel <o.rempel@pengutronix.de> wrote:
> >
> > changes v4:
> > - drop HPI and SDHCI related patches
> >
> > This patch set introduces a framework for handling undervoltage events
> > in the MMC subsystem. The goal is to improve system reliability by
> > ensuring graceful handling of power fluctuations that could otherwise
> > lead to metadata corruption, potentially rendering the eMMC chip
> > unusable or causing significant data loss.
>
> Thanks for posting this! I will spend some time reviewing this next
> week and let you know my comments then.
>
> However, I just wanted to let you know that I just posted a series [1]
> (forgot to cc you, sorry), which also reworks the way _mmc_suspend()
> understands what scenario it should be running. I am guessing that
> re-work is simplifying for your $subject series too. Maybe you would
> like to have a look?
Ah, very nice. The integration of undervoltage support is easier now.
I rebased by match on top of mmc/next branch and do some testing
tomorrow. If you have no other comments I'll send updated patches after
testing.
By the way, are you on embedded recipes this year?
Best Regards,
Oleksij
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v4 0/4] mmc: handle undervoltage events and prevent eMMC corruption
2025-05-05 12:48 ` Oleksij Rempel
@ 2025-05-05 13:10 ` Ulf Hansson
0 siblings, 0 replies; 8+ messages in thread
From: Ulf Hansson @ 2025-05-05 13:10 UTC (permalink / raw)
To: Oleksij Rempel
Cc: kernel, linux-kernel, linux-mmc, Greg Kroah-Hartman, Mark Brown,
Rafael J. Wysocki, Søren Andersen, Christian Loehle,
Adrian Hunter, Avri Altman
On Mon, 5 May 2025 at 14:48, Oleksij Rempel <o.rempel@pengutronix.de> wrote:
>
> Hi Ulf,
>
> Sorry for very late replay,
>
> On Thu, Mar 20, 2025 at 03:36:32PM +0100, Ulf Hansson wrote:
> > Hi Oleksij,
> >
> > On Mon, 10 Mar 2025 at 11:22, Oleksij Rempel <o.rempel@pengutronix.de> wrote:
> > >
> > > changes v4:
> > > - drop HPI and SDHCI related patches
> > >
> > > This patch set introduces a framework for handling undervoltage events
> > > in the MMC subsystem. The goal is to improve system reliability by
> > > ensuring graceful handling of power fluctuations that could otherwise
> > > lead to metadata corruption, potentially rendering the eMMC chip
> > > unusable or causing significant data loss.
> >
> > Thanks for posting this! I will spend some time reviewing this next
> > week and let you know my comments then.
> >
> > However, I just wanted to let you know that I just posted a series [1]
> > (forgot to cc you, sorry), which also reworks the way _mmc_suspend()
> > understands what scenario it should be running. I am guessing that
> > re-work is simplifying for your $subject series too. Maybe you would
> > like to have a look?
>
> Ah, very nice. The integration of undervoltage support is easier now.
>
> I rebased by match on top of mmc/next branch and do some testing
> tomorrow. If you have no other comments I'll send updated patches after
> testing.
That's great! I have started to review the series in more detail, but
feel free to post a new version, I can look at that instead.
>
> By the way, are you on embedded recipes this year?
Unfortunately not. Linaro Connect in Lisbon clashes with these dates.
Kind regards
Uffe
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-05-05 13:11 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-10 10:22 [PATCH v4 0/4] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
2025-03-10 10:22 ` [PATCH v4 1/4] mmc: core: Handle undervoltage events and register regulator notifiers Oleksij Rempel
2025-03-10 10:22 ` [PATCH v4 2/4] mmc: core: refactor _mmc_suspend() for undervoltage handling Oleksij Rempel
2025-03-10 10:22 ` [PATCH v4 3/4] mmc: core: add undervoltage handler for MMC/eMMC devices Oleksij Rempel
2025-03-10 10:22 ` [PATCH v4 4/4] mmc: block: abort requests and suppress errors after undervoltage shutdown Oleksij Rempel
2025-03-20 14:36 ` [PATCH v4 0/4] mmc: handle undervoltage events and prevent eMMC corruption Ulf Hansson
2025-05-05 12:48 ` Oleksij Rempel
2025-05-05 13:10 ` Ulf Hansson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox