* [PATCH v6 0/4] mmc: handle undervoltage events and prevent eMMC corruption
@ 2025-06-16 10:29 Oleksij Rempel
2025-06-16 10:29 ` [PATCH v6 1/4] mmc: core: Add infrastructure for undervoltage handling Oleksij Rempel
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Oleksij Rempel @ 2025-06-16 10:29 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 v6:
- Rewrite commit message to be more technical per reviewer feedback.
- Address race conditions by using __mmc_stop_host() instead of only
claiming the host in the undervoltage handler.
- Move notifier registration from mmc_regulator_get_supply() to the end of
a successful card initialization in mmc_attach_mmc(), ensuring it only
runs for capable cards.
- Centralize notifier unregistration in mmc_remove_card() to correctly
handle all card removal and error paths.
- Add 'undervoltage_notify_registered' flag to struct mmc_host to
reliably track the notifier state.
- Consolidate multiple notifier callbacks into a single, generic handler.
- Remove premature notifier support for vqmmc and vqmmc2 regulators.
- Move INIT_WORK() for the undervoltage workqueue to mmc_alloc_host().
changes v5:
- Rebased on top of mmc/next after introduction of enum mmc_poweroff_type
- Replaced boolean undervoltage parameter with MMC_POWEROFF_UNDERVOLTAGE
- Dropped unused __mmc_resume() helper
- Updated commit messages accordingly
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: Add infrastructure for undervoltage handling
mmc: core: Add MMC_POWEROFF_UNDERVOLTAGE support in _mmc_suspend()
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/bus.c | 5 ++
drivers/mmc/core/core.c | 33 +++++++++++++
drivers/mmc/core/core.h | 2 +
drivers/mmc/core/host.c | 3 ++
drivers/mmc/core/mmc.c | 95 +++++++++++++++++++++++++++++++++---
drivers/mmc/core/queue.c | 2 +-
drivers/mmc/core/regulator.c | 82 +++++++++++++++++++++++++++++++
include/linux/mmc/host.h | 10 ++++
9 files changed, 226 insertions(+), 8 deletions(-)
--
2.39.5
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v6 1/4] mmc: core: Add infrastructure for undervoltage handling
2025-06-16 10:29 [PATCH v6 0/4] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
@ 2025-06-16 10:29 ` Oleksij Rempel
2025-07-04 12:41 ` Ulf Hansson
2025-06-16 10:29 ` [PATCH v6 2/4] mmc: core: Add MMC_POWEROFF_UNDERVOLTAGE support in _mmc_suspend() Oleksij Rempel
` (2 subsequent siblings)
3 siblings, 1 reply; 8+ messages in thread
From: Oleksij Rempel @ 2025-06-16 10:29 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
Implement the core infrastructure to allow MMC bus types to handle
REGULATOR_EVENT_UNDER_VOLTAGE events from power regulators. This is
primarily aimed at allowing devices like eMMC to perform an emergency
shutdown to prevent data corruption when a power failure is imminent.
This patch introduces:
- A new 'handle_undervoltage' function pointer to 'struct mmc_bus_ops'.
Bus drivers (e.g., for eMMC) can implement this to define their
emergency procedures.
- A workqueue ('uv_work') in 'struct mmc_supply' to handle the event
asynchronously in a high-priority context.
- A new function 'mmc_handle_undervoltage()' which is called from the
workqueue. It stops the host queue to prevent races with card removal,
checks for the bus op, and invokes the handler.
- Functions to register and unregister the regulator notifier, intended
to be called by bus drivers like 'mmc_attach_mmc' when a compatible
card is detected.
The notifier is only registered for the main 'vmmc' supply, as
undervoltage
handling for 'vqmmc' or 'vqmmc2' is not required at this time.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v6:
- Rewrite commit message to be more technical per reviewer feedback.
- Address race conditions by using __mmc_stop_host() instead of only
claiming the host in the undervoltage handler.
- Move notifier registration from mmc_regulator_get_supply() to the end of
a successful card initialization in mmc_attach_mmc(), ensuring it only
runs for capable cards.
- Centralize notifier unregistration in mmc_remove_card() to correctly
handle all card removal and error paths.
- Add 'undervoltage_notify_registered' flag to struct mmc_host to
reliably track the notifier state.
- Consolidate multiple notifier callbacks into a single, generic handler.
- Remove premature notifier support for vqmmc and vqmmc2 regulators.
- Move INIT_WORK() for the undervoltage workqueue to mmc_alloc_host().
changes v3:
- filter supported cards at early stage
- add locking in mmc_handle_regulator_event()
- claim/release host in mmc_handle_undervoltage()
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.
---
drivers/mmc/core/bus.c | 5 +++
drivers/mmc/core/core.c | 33 +++++++++++++++
drivers/mmc/core/core.h | 2 +
drivers/mmc/core/host.c | 3 ++
drivers/mmc/core/mmc.c | 6 +++
drivers/mmc/core/regulator.c | 82 ++++++++++++++++++++++++++++++++++++
include/linux/mmc/host.h | 10 +++++
7 files changed, 141 insertions(+)
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 1cf64e0952fb..aa3454cb0702 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -394,6 +394,11 @@ void mmc_remove_card(struct mmc_card *card)
{
struct mmc_host *host = card->host;
+ if (host->undervoltage_notify_registered) {
+ mmc_regulator_unregister_undervoltage_notifier(host);
+ host->undervoltage_notify_registered = false;
+ }
+
mmc_remove_card_debugfs(card);
if (mmc_card_present(card)) {
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index a0e2dce70434..0b000246481c 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1399,6 +1399,39 @@ 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;
+
+ /* Stop the host to prevent races with card removal */
+ __mmc_stop_host(host);
+
+ mmc_claim_host(host);
+
+ if (!host->bus_ops || !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 622085cd766f..31e5918f6e95 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/host.c b/drivers/mmc/core/host.c
index f14671ea5716..6fd07318c139 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -564,6 +564,9 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
INIT_WORK(&host->sdio_irq_work, sdio_irq_work);
timer_setup(&host->retune_timer, mmc_retune_timer, 0);
+ INIT_WORK(&host->supply.uv_work, mmc_undervoltage_workfn);
+ host->undervoltage_notify_registered = false;
+
/*
* By default, hosts do not support SGIO or large requests.
* They have to set these according to their abilities.
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 5be9b42d5057..5689ab6eefe1 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2370,6 +2370,12 @@ int mmc_attach_mmc(struct mmc_host *host)
goto remove_card;
mmc_claim_host(host);
+
+ if (host->card->ext_csd.power_off_notification == EXT_CSD_POWER_ON) {
+ mmc_regulator_register_undervoltage_notifier(host);
+ host->undervoltage_notify_registered = true;
+ }
+
return 0;
remove_card:
diff --git a/drivers/mmc/core/regulator.c b/drivers/mmc/core/regulator.c
index 3dae2e9b7978..85c318fd98fa 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,87 @@ static inline int mmc_regulator_get_ocrmask(struct regulator *supply)
#endif /* CONFIG_REGULATOR */
+/* To be called from a high-priority workqueue */
+void mmc_undervoltage_workfn(struct work_struct *work)
+{
+ struct mmc_supply *supply;
+ struct mmc_host *host;
+
+ supply = container_of(work, struct mmc_supply, uv_work);
+ host = container_of(supply, struct mmc_host, supply);
+
+ mmc_handle_undervoltage(host);
+}
+EXPORT_SYMBOL_GPL(mmc_undervoltage_workfn);
+
+static int mmc_handle_regulator_event(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct mmc_supply *supply = container_of(nb, struct mmc_supply,
+ vmmc_nb);
+ struct mmc_host *host = container_of(supply, struct mmc_host, supply);
+ unsigned long flags;
+
+ switch (event) {
+ case REGULATOR_EVENT_UNDER_VOLTAGE:
+ spin_lock_irqsave(&host->lock, flags);
+ if (host->undervoltage) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ return NOTIFY_OK;
+ }
+
+ host->undervoltage = true;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ queue_work(system_highpri_wq, &host->supply.uv_work);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+/**
+ * mmc_regulator_register_undervoltage_notifier - Register for undervoltage
+ * events
+ * @host: MMC host
+ *
+ * To be called by a bus driver when a card supporting graceful shutdown
+ * is attached.
+ */
+void mmc_regulator_register_undervoltage_notifier(struct mmc_host *host)
+{
+ int ret;
+
+ if (IS_ERR_OR_NULL(host->supply.vmmc))
+ return;
+
+ host->supply.vmmc_nb.notifier_call = mmc_handle_regulator_event;
+ ret = regulator_register_notifier(host->supply.vmmc,
+ &host->supply.vmmc_nb);
+ if (ret)
+ dev_warn(mmc_dev(host), "Failed to register vmmc notifier: %d\n", ret);
+}
+EXPORT_SYMBOL_GPL(mmc_regulator_register_undervoltage_notifier);
+
+/**
+ * mmc_regulator_unregister_undervoltage_notifier - Unregister undervoltage
+ * notifier
+ * @host: MMC host
+ */
+void mmc_regulator_unregister_undervoltage_notifier(struct mmc_host *host)
+{
+ if (IS_ERR_OR_NULL(host->supply.vmmc))
+ return;
+
+ regulator_unregister_notifier(host->supply.vmmc, &host->supply.vmmc_nb);
+
+ /* Ensure any pending work is flushed before the card is removed */
+ cancel_work_sync(&host->supply.uv_work);
+}
+EXPORT_SYMBOL_GPL(mmc_regulator_unregister_undervoltage_notifier);
+
/**
* mmc_regulator_get_supply - try to get VMMC and VQMMC regulators for a host
* @mmc: the host to regulate
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 68f09a955a90..9b7bb4b7150c 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,9 @@ 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 work_struct uv_work; /* Undervoltage work */
};
struct mmc_ctx {
@@ -493,6 +497,9 @@ 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 */
+ /* Undervoltage notifier is registered */
+ unsigned int undervoltage_notify_registered:1;
int rescan_disable; /* disable card detection */
int rescan_entered; /* used with nonremovable devices */
@@ -659,6 +666,9 @@ static inline int mmc_regulator_set_vqmmc2(struct mmc_host *mmc,
int mmc_regulator_get_supply(struct mmc_host *mmc);
int mmc_regulator_enable_vqmmc(struct mmc_host *mmc);
void mmc_regulator_disable_vqmmc(struct mmc_host *mmc);
+void mmc_regulator_register_undervoltage_notifier(struct mmc_host *host);
+void mmc_regulator_unregister_undervoltage_notifier(struct mmc_host *host);
+void mmc_undervoltage_workfn(struct work_struct *work);
static inline int mmc_card_is_removable(struct mmc_host *host)
{
--
2.39.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v6 2/4] mmc: core: Add MMC_POWEROFF_UNDERVOLTAGE support in _mmc_suspend()
2025-06-16 10:29 [PATCH v6 0/4] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
2025-06-16 10:29 ` [PATCH v6 1/4] mmc: core: Add infrastructure for undervoltage handling Oleksij Rempel
@ 2025-06-16 10:29 ` Oleksij Rempel
2025-07-04 12:50 ` Ulf Hansson
2025-06-16 10:29 ` [PATCH v6 3/4] mmc: core: add undervoltage handler for MMC/eMMC devices Oleksij Rempel
2025-06-16 10:29 ` [PATCH v6 4/4] mmc: block: abort requests and suppress errors after undervoltage shutdown Oleksij Rempel
3 siblings, 1 reply; 8+ messages in thread
From: Oleksij Rempel @ 2025-06-16 10:29 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_POWEROFF_UNDERVOLTAGE as a new mmc_poweroff_type value and
adjust _mmc_suspend() to skip cache flush for this case. This prepares for
undervoltage handling in a follow-up patch.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v5:
- rebase against latest mmc/next
- use MMC_POWEROFF_UNDERVOLTAGE enum instead of function parameter.
changes v3:
- add comments
- make sure _mmc_flush_cache is not executed in the undervoltage case
---
drivers/mmc/core/mmc.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 5689ab6eefe1..6812df679ba9 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -36,6 +36,7 @@
enum mmc_poweroff_type {
MMC_POWEROFF_SUSPEND,
MMC_POWEROFF_SHUTDOWN,
+ MMC_POWEROFF_UNDERVOLTAGE,
MMC_POWEROFF_UNBIND,
};
@@ -2132,9 +2133,15 @@ static int _mmc_suspend(struct mmc_host *host, enum mmc_poweroff_type pm_type)
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 (pm_type != MMC_POWEROFF_UNDERVOLTAGE) {
+ err = _mmc_flush_cache(host);
+ if (err)
+ goto out;
+ }
if (mmc_card_can_poweroff_notify(host->card) &&
mmc_host_can_poweroff_notify(host, pm_type))
--
2.39.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v6 3/4] mmc: core: add undervoltage handler for MMC/eMMC devices
2025-06-16 10:29 [PATCH v6 0/4] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
2025-06-16 10:29 ` [PATCH v6 1/4] mmc: core: Add infrastructure for undervoltage handling Oleksij Rempel
2025-06-16 10:29 ` [PATCH v6 2/4] mmc: core: Add MMC_POWEROFF_UNDERVOLTAGE support in _mmc_suspend() Oleksij Rempel
@ 2025-06-16 10:29 ` Oleksij Rempel
2025-07-04 13:06 ` Ulf Hansson
2025-06-16 10:29 ` [PATCH v6 4/4] mmc: block: abort requests and suppress errors after undervoltage shutdown Oleksij Rempel
3 siblings, 1 reply; 8+ messages in thread
From: Oleksij Rempel @ 2025-06-16 10:29 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. The handler performs a controlled emergency suspend and
then marks the card as removed to prevent further I/O.
This is achieved by calling a new internal helper, `__mmc_suspend()`,
with `MMC_POWEROFF_UNDERVOLTAGE`. This ensures a fast power-down sequence
by using the short power-off notification and skipping the cache flush.
If power-off notify is not supported, it falls back to sleep or deselect.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v6:
- Refactor suspend logic: move cache flush skipping during undervoltage
to a separate, preceding commit.
- update commit message
changes v5:
- Rebased on top of patch introducing enum mmc_poweroff_type
- Updated call to __mmc_suspend() to use MMC_POWEROFF_UNDERVOLTAGE
- Dropped __mmc_resume() helper, as it is no longer needed
- Updated commit message to reflect API change and code removal
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 | 76 ++++++++++++++++++++++++++++++++++++++++--
1 file changed, 73 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 6812df679ba9..fe4fc2ad261e 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2120,7 +2120,7 @@ static int _mmc_flush_cache(struct mmc_host *host)
return err;
}
-static int _mmc_suspend(struct mmc_host *host, enum mmc_poweroff_type pm_type)
+static int __mmc_suspend(struct mmc_host *host, enum mmc_poweroff_type pm_type)
{
unsigned int notify_type = EXT_CSD_POWER_OFF_SHORT;
int err = 0;
@@ -2128,8 +2128,6 @@ static int _mmc_suspend(struct mmc_host *host, enum mmc_poweroff_type pm_type)
if (pm_type == MMC_POWEROFF_SHUTDOWN)
notify_type = EXT_CSD_POWER_OFF_LONG;
- mmc_claim_host(host);
-
if (mmc_card_suspended(host->card))
goto out;
@@ -2156,7 +2154,17 @@ static int _mmc_suspend(struct mmc_host *host, enum mmc_poweroff_type pm_type)
mmc_card_set_suspended(host->card);
}
out:
+ return err;
+}
+
+static int _mmc_suspend(struct mmc_host *host, enum mmc_poweroff_type pm_type)
+{
+ int err;
+
+ mmc_claim_host(host);
+ err = __mmc_suspend(host, pm_type);
mmc_release_host(host);
+
return err;
}
@@ -2219,6 +2227,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;
+
/*
* If the card remains suspended at this point and it was done by using
* the sleep-cmd (CMD5), we may need to re-initialize it first, to allow
@@ -2309,6 +2324,60 @@ 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;
+
+ /* 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, MMC_POWEROFF_UNDERVOLTAGE);
+ if (err)
+ pr_err("%s: undervoltage suspend failed: %pe\n",
+ mmc_hostname(host), ERR_PTR(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,
@@ -2321,6 +2390,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 v6 4/4] mmc: block: abort requests and suppress errors after undervoltage shutdown
2025-06-16 10:29 [PATCH v6 0/4] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
` (2 preceding siblings ...)
2025-06-16 10:29 ` [PATCH v6 3/4] mmc: core: add undervoltage handler for MMC/eMMC devices Oleksij Rempel
@ 2025-06-16 10:29 ` Oleksij Rempel
3 siblings, 0 replies; 8+ messages in thread
From: Oleksij Rempel @ 2025-06-16 10:29 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 9cc47bf94804..bc57bb8514f6 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 284856c8f655..1dd71ea6927d 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 v6 1/4] mmc: core: Add infrastructure for undervoltage handling
2025-06-16 10:29 ` [PATCH v6 1/4] mmc: core: Add infrastructure for undervoltage handling Oleksij Rempel
@ 2025-07-04 12:41 ` Ulf Hansson
0 siblings, 0 replies; 8+ messages in thread
From: Ulf Hansson @ 2025-07-04 12:41 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, 16 Jun 2025 at 12:29, Oleksij Rempel <o.rempel@pengutronix.de> wrote:
>
> Implement the core infrastructure to allow MMC bus types to handle
> REGULATOR_EVENT_UNDER_VOLTAGE events from power regulators. This is
> primarily aimed at allowing devices like eMMC to perform an emergency
> shutdown to prevent data corruption when a power failure is imminent.
>
> This patch introduces:
> - A new 'handle_undervoltage' function pointer to 'struct mmc_bus_ops'.
> Bus drivers (e.g., for eMMC) can implement this to define their
> emergency procedures.
> - A workqueue ('uv_work') in 'struct mmc_supply' to handle the event
> asynchronously in a high-priority context.
> - A new function 'mmc_handle_undervoltage()' which is called from the
> workqueue. It stops the host queue to prevent races with card removal,
> checks for the bus op, and invokes the handler.
> - Functions to register and unregister the regulator notifier, intended
> to be called by bus drivers like 'mmc_attach_mmc' when a compatible
> card is detected.
>
> The notifier is only registered for the main 'vmmc' supply, as
> undervoltage
> handling for 'vqmmc' or 'vqmmc2' is not required at this time.
>
> Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
This looks a lot better, thanks!
Although, I think there are some additional simplifications that can
be made, please have a look below.
> ---
> changes v6:
> - Rewrite commit message to be more technical per reviewer feedback.
> - Address race conditions by using __mmc_stop_host() instead of only
> claiming the host in the undervoltage handler.
> - Move notifier registration from mmc_regulator_get_supply() to the end of
> a successful card initialization in mmc_attach_mmc(), ensuring it only
> runs for capable cards.
> - Centralize notifier unregistration in mmc_remove_card() to correctly
> handle all card removal and error paths.
> - Add 'undervoltage_notify_registered' flag to struct mmc_host to
> reliably track the notifier state.
> - Consolidate multiple notifier callbacks into a single, generic handler.
> - Remove premature notifier support for vqmmc and vqmmc2 regulators.
> - Move INIT_WORK() for the undervoltage workqueue to mmc_alloc_host().
> changes v3:
> - filter supported cards at early stage
> - add locking in mmc_handle_regulator_event()
> - claim/release host in mmc_handle_undervoltage()
>
> 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.
> ---
> drivers/mmc/core/bus.c | 5 +++
> drivers/mmc/core/core.c | 33 +++++++++++++++
> drivers/mmc/core/core.h | 2 +
> drivers/mmc/core/host.c | 3 ++
> drivers/mmc/core/mmc.c | 6 +++
> drivers/mmc/core/regulator.c | 82 ++++++++++++++++++++++++++++++++++++
> include/linux/mmc/host.h | 10 +++++
> 7 files changed, 141 insertions(+)
>
> diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
> index 1cf64e0952fb..aa3454cb0702 100644
> --- a/drivers/mmc/core/bus.c
> +++ b/drivers/mmc/core/bus.c
> @@ -394,6 +394,11 @@ void mmc_remove_card(struct mmc_card *card)
> {
> struct mmc_host *host = card->host;
>
> + if (host->undervoltage_notify_registered) {
I don't think we need a new parameter here
(undervoltage_notify_registered). Instead we should register the
notifier in mmc_add_card() and after the point where
mmc_card_set_present() gets called. In this way, we can instead rely
on mmc_card_present() a few lines below, to understand when
unregistering the notifier should be done.
> + mmc_regulator_unregister_undervoltage_notifier(host);
> + host->undervoltage_notify_registered = false;
> + }
> +
> mmc_remove_card_debugfs(card);
>
> if (mmc_card_present(card)) {
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index a0e2dce70434..0b000246481c 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -1399,6 +1399,39 @@ 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;
> +
> + /* Stop the host to prevent races with card removal */
> + __mmc_stop_host(host);
> +
> + mmc_claim_host(host);
If we instead register the notifier in mmc_add_card() and after the
point where mmc_card_set_present() gets called, we don't need to
claim/release the host here. Instead that is better dealt with from
the actual ->bus_ops() callback.
More precisely, the bus_ops can't be removed as we have stopped the
host. Since we are also unregistering the notifier in
mmc_remove_card() and canceling the work, this is also prevented from
being racy during the host-removal.
> +
> + if (!host->bus_ops || !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 622085cd766f..31e5918f6e95 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/host.c b/drivers/mmc/core/host.c
> index f14671ea5716..6fd07318c139 100644
> --- a/drivers/mmc/core/host.c
> +++ b/drivers/mmc/core/host.c
> @@ -564,6 +564,9 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
> INIT_WORK(&host->sdio_irq_work, sdio_irq_work);
> timer_setup(&host->retune_timer, mmc_retune_timer, 0);
>
> + INIT_WORK(&host->supply.uv_work, mmc_undervoltage_workfn);
> + host->undervoltage_notify_registered = false;
> +
> /*
> * By default, hosts do not support SGIO or large requests.
> * They have to set these according to their abilities.
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 5be9b42d5057..5689ab6eefe1 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -2370,6 +2370,12 @@ int mmc_attach_mmc(struct mmc_host *host)
> goto remove_card;
>
> mmc_claim_host(host);
> +
> + if (host->card->ext_csd.power_off_notification == EXT_CSD_POWER_ON) {
> + mmc_regulator_register_undervoltage_notifier(host);
> + host->undervoltage_notify_registered = true;
> + }
> +
As stated above, let's do this in mmc_add_card() instead. In then
becomes up to the ->bus_ops() callback to decide what to do, if
anything at all, depending what type of card it is.
> return 0;
>
> remove_card:
> diff --git a/drivers/mmc/core/regulator.c b/drivers/mmc/core/regulator.c
> index 3dae2e9b7978..85c318fd98fa 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,87 @@ static inline int mmc_regulator_get_ocrmask(struct regulator *supply)
>
> #endif /* CONFIG_REGULATOR */
>
> +/* To be called from a high-priority workqueue */
> +void mmc_undervoltage_workfn(struct work_struct *work)
> +{
> + struct mmc_supply *supply;
> + struct mmc_host *host;
> +
> + supply = container_of(work, struct mmc_supply, uv_work);
> + host = container_of(supply, struct mmc_host, supply);
> +
> + mmc_handle_undervoltage(host);
> +}
> +EXPORT_SYMBOL_GPL(mmc_undervoltage_workfn);
No need to EXPORT the symbol, it's used from the same module.
> +
> +static int mmc_handle_regulator_event(struct notifier_block *nb,
> + unsigned long event, void *data)
> +{
> + struct mmc_supply *supply = container_of(nb, struct mmc_supply,
> + vmmc_nb);
> + struct mmc_host *host = container_of(supply, struct mmc_host, supply);
> + unsigned long flags;
> +
> + switch (event) {
> + case REGULATOR_EVENT_UNDER_VOLTAGE:
> + spin_lock_irqsave(&host->lock, flags);
> + if (host->undervoltage) {
So the regulator core is firing these notifications over and over again?
Sure, we can deal with this internally for mmc for now, but ideally we
don't want the regulator core to spam us with notifications, right?
> + spin_unlock_irqrestore(&host->lock, flags);
> + return NOTIFY_OK;
> + }
> +
> + host->undervoltage = true;
> + spin_unlock_irqrestore(&host->lock, flags);
> +
> + queue_work(system_highpri_wq, &host->supply.uv_work);
> + break;
> + default:
> + return NOTIFY_DONE;
> + }
> +
> + return NOTIFY_OK;
> +}
> +
> +/**
> + * mmc_regulator_register_undervoltage_notifier - Register for undervoltage
> + * events
> + * @host: MMC host
> + *
> + * To be called by a bus driver when a card supporting graceful shutdown
> + * is attached.
> + */
> +void mmc_regulator_register_undervoltage_notifier(struct mmc_host *host)
> +{
> + int ret;
> +
> + if (IS_ERR_OR_NULL(host->supply.vmmc))
> + return;
> +
> + host->supply.vmmc_nb.notifier_call = mmc_handle_regulator_event;
> + ret = regulator_register_notifier(host->supply.vmmc,
> + &host->supply.vmmc_nb);
> + if (ret)
> + dev_warn(mmc_dev(host), "Failed to register vmmc notifier: %d\n", ret);
> +}
> +EXPORT_SYMBOL_GPL(mmc_regulator_register_undervoltage_notifier);
No need to EXPORT the symbol, it's used from the same module.
> +
> +/**
> + * mmc_regulator_unregister_undervoltage_notifier - Unregister undervoltage
> + * notifier
> + * @host: MMC host
> + */
> +void mmc_regulator_unregister_undervoltage_notifier(struct mmc_host *host)
> +{
> + if (IS_ERR_OR_NULL(host->supply.vmmc))
> + return;
> +
> + regulator_unregister_notifier(host->supply.vmmc, &host->supply.vmmc_nb);
> +
> + /* Ensure any pending work is flushed before the card is removed */
> + cancel_work_sync(&host->supply.uv_work);
> +}
> +EXPORT_SYMBOL_GPL(mmc_regulator_unregister_undervoltage_notifier);
No need to EXPORT the symbol, it's used from the same module.
> +
> /**
> * mmc_regulator_get_supply - try to get VMMC and VQMMC regulators for a host
> * @mmc: the host to regulate
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 68f09a955a90..9b7bb4b7150c 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,9 @@ 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 work_struct uv_work; /* Undervoltage work */
> };
>
> struct mmc_ctx {
> @@ -493,6 +497,9 @@ 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 */
> + /* Undervoltage notifier is registered */
> + unsigned int undervoltage_notify_registered:1;
>
> int rescan_disable; /* disable card detection */
> int rescan_entered; /* used with nonremovable devices */
> @@ -659,6 +666,9 @@ static inline int mmc_regulator_set_vqmmc2(struct mmc_host *mmc,
> int mmc_regulator_get_supply(struct mmc_host *mmc);
> int mmc_regulator_enable_vqmmc(struct mmc_host *mmc);
> void mmc_regulator_disable_vqmmc(struct mmc_host *mmc);
> +void mmc_regulator_register_undervoltage_notifier(struct mmc_host *host);
> +void mmc_regulator_unregister_undervoltage_notifier(struct mmc_host *host);
> +void mmc_undervoltage_workfn(struct work_struct *work);
>
> static inline int mmc_card_is_removable(struct mmc_host *host)
> {
> --
> 2.39.5
>
Kind regards
Uffe
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v6 2/4] mmc: core: Add MMC_POWEROFF_UNDERVOLTAGE support in _mmc_suspend()
2025-06-16 10:29 ` [PATCH v6 2/4] mmc: core: Add MMC_POWEROFF_UNDERVOLTAGE support in _mmc_suspend() Oleksij Rempel
@ 2025-07-04 12:50 ` Ulf Hansson
0 siblings, 0 replies; 8+ messages in thread
From: Ulf Hansson @ 2025-07-04 12:50 UTC (permalink / raw)
To: Oleksij Rempel, Avri Altman
Cc: kernel, linux-kernel, linux-mmc, Greg Kroah-Hartman, Mark Brown,
Rafael J. Wysocki, Søren Andersen, Christian Loehle,
Adrian Hunter
On Mon, 16 Jun 2025 at 12:29, Oleksij Rempel <o.rempel@pengutronix.de> wrote:
>
> Introduce MMC_POWEROFF_UNDERVOLTAGE as a new mmc_poweroff_type value and
> adjust _mmc_suspend() to skip cache flush for this case. This prepares for
> undervoltage handling in a follow-up patch.
>
> Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
> ---
> changes v5:
> - rebase against latest mmc/next
> - use MMC_POWEROFF_UNDERVOLTAGE enum instead of function parameter.
> changes v3:
> - add comments
> - make sure _mmc_flush_cache is not executed in the undervoltage case
> ---
> drivers/mmc/core/mmc.c | 13 ++++++++++---
> 1 file changed, 10 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 5689ab6eefe1..6812df679ba9 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -36,6 +36,7 @@
> enum mmc_poweroff_type {
> MMC_POWEROFF_SUSPEND,
> MMC_POWEROFF_SHUTDOWN,
> + MMC_POWEROFF_UNDERVOLTAGE,
> MMC_POWEROFF_UNBIND,
> };
>
> @@ -2132,9 +2133,15 @@ static int _mmc_suspend(struct mmc_host *host, enum mmc_poweroff_type pm_type)
> 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 (pm_type != MMC_POWEROFF_UNDERVOLTAGE) {
> + err = _mmc_flush_cache(host);
> + if (err)
> + goto out;
> + }
I think this sounds reasonable as we may simply not have the time to
complete a flush.
Yet, it may be device specific on what makes best sense. Let's see if
Avri also agrees with this.
>
> if (mmc_card_can_poweroff_notify(host->card) &&
> mmc_host_can_poweroff_notify(host, pm_type))
> --
> 2.39.5
>
That said, please squash this with patch3. Especially since I think
patch3 can be made a bit simpler due to my comments on patch1.
Kind regards
Uffe
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v6 3/4] mmc: core: add undervoltage handler for MMC/eMMC devices
2025-06-16 10:29 ` [PATCH v6 3/4] mmc: core: add undervoltage handler for MMC/eMMC devices Oleksij Rempel
@ 2025-07-04 13:06 ` Ulf Hansson
0 siblings, 0 replies; 8+ messages in thread
From: Ulf Hansson @ 2025-07-04 13:06 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, 16 Jun 2025 at 12:29, Oleksij Rempel <o.rempel@pengutronix.de> wrote:
>
> Introduce `_mmc_handle_undervoltage()` to handle undervoltage events for
> MMC/eMMC devices. The handler performs a controlled emergency suspend and
> then marks the card as removed to prevent further I/O.
>
> This is achieved by calling a new internal helper, `__mmc_suspend()`,
> with `MMC_POWEROFF_UNDERVOLTAGE`. This ensures a fast power-down sequence
> by using the short power-off notification and skipping the cache flush.
> If power-off notify is not supported, it falls back to sleep or deselect.
>
> Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
> ---
> changes v6:
> - Refactor suspend logic: move cache flush skipping during undervoltage
> to a separate, preceding commit.
> - update commit message
> changes v5:
> - Rebased on top of patch introducing enum mmc_poweroff_type
> - Updated call to __mmc_suspend() to use MMC_POWEROFF_UNDERVOLTAGE
> - Dropped __mmc_resume() helper, as it is no longer needed
> - Updated commit message to reflect API change and code removal
> 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 | 76 ++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 73 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 6812df679ba9..fe4fc2ad261e 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -2120,7 +2120,7 @@ static int _mmc_flush_cache(struct mmc_host *host)
> return err;
> }
>
> -static int _mmc_suspend(struct mmc_host *host, enum mmc_poweroff_type pm_type)
> +static int __mmc_suspend(struct mmc_host *host, enum mmc_poweroff_type pm_type)
> {
> unsigned int notify_type = EXT_CSD_POWER_OFF_SHORT;
> int err = 0;
> @@ -2128,8 +2128,6 @@ static int _mmc_suspend(struct mmc_host *host, enum mmc_poweroff_type pm_type)
> if (pm_type == MMC_POWEROFF_SHUTDOWN)
> notify_type = EXT_CSD_POWER_OFF_LONG;
>
> - mmc_claim_host(host);
> -
> if (mmc_card_suspended(host->card))
> goto out;
>
> @@ -2156,7 +2154,17 @@ static int _mmc_suspend(struct mmc_host *host, enum mmc_poweroff_type pm_type)
> mmc_card_set_suspended(host->card);
> }
> out:
> + return err;
> +}
> +
> +static int _mmc_suspend(struct mmc_host *host, enum mmc_poweroff_type pm_type)
> +{
> + int err;
> +
> + mmc_claim_host(host);
> + err = __mmc_suspend(host, pm_type);
> mmc_release_host(host);
> +
> return err;
> }
>
> @@ -2219,6 +2227,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;
Maybe use mmc_card_removed() instead?
BTW, I wonder if there are any other places where we need to add more
bailout points... Let me think a bit more about this...
> +
> /*
> * If the card remains suspended at this point and it was done by using
> * the sleep-cmd (CMD5), we may need to re-initialize it first, to allow
> @@ -2309,6 +2324,60 @@ 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;
> +
> + /* If there is no card attached, nothing to do */
> + if (!card)
> + return 0;
This check should not be needed. Refer to my comments on patch1 for
more information.
> +
> + /*
> + * 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, MMC_POWEROFF_UNDERVOLTAGE);
> + if (err)
> + pr_err("%s: undervoltage suspend failed: %pe\n",
> + mmc_hostname(host), ERR_PTR(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,
> @@ -2321,6 +2390,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
>
Kind regards
Uffe
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-07-04 13:07 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-16 10:29 [PATCH v6 0/4] mmc: handle undervoltage events and prevent eMMC corruption Oleksij Rempel
2025-06-16 10:29 ` [PATCH v6 1/4] mmc: core: Add infrastructure for undervoltage handling Oleksij Rempel
2025-07-04 12:41 ` Ulf Hansson
2025-06-16 10:29 ` [PATCH v6 2/4] mmc: core: Add MMC_POWEROFF_UNDERVOLTAGE support in _mmc_suspend() Oleksij Rempel
2025-07-04 12:50 ` Ulf Hansson
2025-06-16 10:29 ` [PATCH v6 3/4] mmc: core: add undervoltage handler for MMC/eMMC devices Oleksij Rempel
2025-07-04 13:06 ` Ulf Hansson
2025-06-16 10:29 ` [PATCH v6 4/4] mmc: block: abort requests and suppress errors after undervoltage shutdown Oleksij Rempel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).