eMMC Timeout Management in Linux: An Updated Perspective
This blog post provides an updated overview of eMMC (embedded MultiMediaCard) timeout management within the Linux kernel and U-Boot, building upon the foundational concepts previously discussed. As eMMC devices continue to be critical components in many embedded systems, understanding their timeout mechanisms is crucial for system stability and performance. We will delve into the two primary levels of timeout management: the Block Layer and the eMMC Core, incorporating recent developments and best practices.
Block Layer Timeout eMMC Core Timeout
+---------------+
| VFS |
+---------------+
| FS |
+---------------+
| Block Layer | -> 1. Sets SW timeout timer for each request before dispatching
+---------------+
|
v
+---------------+
| MMC Core | -> 2.1) For data transfer: Sets HW/SW timeout (data line timeout)
| | 2.2) Sets SW timeout for completion interrupt
+---------------+
|
v
+---------------+
| eMMC Device |
+---------------+
1. Block Layer Timeout
The Linux block layer acts as an intermediary between the file system and the underlying storage device. For eMMC devices, the block layer implements software (SW) timeouts to prevent requests from hanging indefinitely. These timeouts are particularly important for ensuring system responsiveness and preventing deadlocks.
For data transfer requests (e.g., REQ_OP_READ
and REQ_OP_WRITE
), the block layer sets a software timeout value based on the queue’s q->rq_timeout
. This value is typically initialized during the eMMC initialization stage within mmc_mq_init()
. By default, this timeout is often set to 60 seconds (1 minute), preventing individual data transfer operations from blocking the system for extended periods.
mmc_init_queue()
mmc_mq_init()
blk_queue_rq_timeout(mq->queue, 60 * HZ)
For non-data transfer requests, such as discard
, erase
, or flush
operations, a longer timeout value is often hardcoded. Historically, this has been set to 600 seconds (10 minutes) within mmc_mq_queue_rq()
. This extended timeout acknowledges that these operations can be more time-consuming, especially for large eMMC devices or during intensive usage scenarios.
mmc_mq_queue_rq() {
switch(issue_type) {
default:
req->timeout = 600 * HZ;
}
-blk_mq_start_request()
--blk_add_timer(struct request *req)
-mmc_blk_mq_issue_rq() /* dispatch the request to the eMMC core */
}
Each request within the block layer is associated with its own timer. Upon successful completion of a request, its corresponding timer is cancelled. If the timer expires before the request completes, the block layer’s timeout handler (mmc_mq_timed_out()
) is invoked to manage the error condition.
2. eMMC Core Timeout
The eMMC core layer, residing closer to the hardware, also employs timeout mechanisms to handle command and data transfer failures. The approach to timeout management in this layer varies depending on whether Command Queue Engine (CQE) is enabled.
With Command Queue Engine (CQE)
CQE is an advanced feature in eMMC that allows for multiple commands to be queued and processed by the eMMC device, improving performance and efficiency. When CQE is enabled, the eMMC core primarily relies on hardware (HW) timers for data transfer operations. The CQE driver typically sets the maximum possible timeout value for these requests, overriding any calculated timeout values from higher layers.
mmc_blk_mq_issue_rq()
-mmc_blk_cqe_issue_rw_rq()
-mmc_blk_cqe_start_req()
-mmc_cqe_start_req()
->cqhci_request().
.cqe_enable = cqhci_enable.
cq_host->ops->enable(mmc);
/*enter vendor specific driver ops*/
..sdhci_cqe_enable
/* Set maximum timeout */
sdhci_set_timeout(host, NULL);
/*set register SDHCI_TIMEOUT_CONTROL 0x2E to be 0xe */
This ensures that the eMMC device has ample time to complete complex or queued operations without premature timeouts from the host side. The hardware timeout clock (TMCLK) is configured via the eMMC host controller’s capabilities register (0x40), typically during the sdhci_setup_host()
and sdhci_read_caps()
routines.
Without Command Queue Engine (CQE)
When CQE is disabled, the eMMC core relies on a combination of hardware and software timers:
1. Hardware Timer for Data Line Timeout: For data read/write operations, a hardware timer is set before sending the command to the eMMC device. This timer monitors the data lines for activity and triggers a timeout if no data transfer occurs within a specified period. The sdhci_set_timeout()
function is responsible for configuring this hardware timer based on the calculated data timeout value.
mmc_blk_mq_issue_rq()
-mmc_blk_mq_issue_rw_rq()
-mmc_start_request()
....>sdhci_send_command(host, cmd)
If (it is data W/R)
->sdhci_set_timeout(host, cmd);
It’s important to note that some SDHCI controllers may have quirks (e.g., SDHCI_QUIRK2_DISABLE_HW_TIMEOUT
) that can disable this hardware timer if the calculated timeout value exceeds the controller’s capacity. In such cases, the eMMC driver will fall back to software-based timeout management for data transfers.
2. Software Timeout Timer for Completion Interrupts: A software timer is explicitly set for every request (both data and non-data transfers) before it is issued to the eMMC device. This timer waits for the completion interrupt from the eMMC device. The timeout value for this software timer is dynamically calculated based on various factors, including the host’s data timeout, command-specific busy timeouts, or a default value (e.g., 10 seconds).
timeout = jiffies;
if (host->data_timeout)
timeout += nsecs_to_jiffies(host->data_timeout);
else if (!cmd->data && cmd->busy_timeout > 9000)
timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
else
timeout += 10 * HZ;
sdhci_mod_timer(host, cmd->mrq, timeout);
For non-data transfer commands like sanitize
or BKOPS
(Background Operations), specific software-defined timeouts are used. For instance, MMC_SANITIZE_TIMEOUT_MS
might be 240 seconds, and MMC_BKOPS_TIMEOUT_MS
could be 120 seconds. These values are often defined in the kernel source and reflect the expected duration of these operations.
#define MMC_SANITIZE_TIMEOUT_MS (240 * 1000) /* 240s */
#define MMC_BKOPS_TIMEOUT_MS (120 * 1000) /* 120s */
Furthermore, for critical commands like SWITCH CMD6
, the eMMC driver often incorporates retry mechanisms. If a SWITCH CMD6
operation fails due to a timeout, the driver may retry the command multiple times (e.g., 10 times) with small delays between retries to overcome transient issues.
sdhci_request()
sdhci_send_command_retry() {
int retries = 10; /* Approx. 10 */
while (!sdhci_send_command(host, cmd)) {
...
usleep_range(1000, 1250);
...
if failed ,there will retry until success or timoeut!
}
}
Disabling Hardware Timeout for Data Transfer
In certain scenarios, it might be desirable to disable the hardware timeout for data transfers, especially if the hardware timer’s capacity is limited or if software-based timeout management is preferred for specific reasons. This can be achieved through a two-step process:
1. Adding the SDHCI_QUIRK2_DISABLE_HW_TIMEOUT
Quirk: This quirk needs to be added to the eMMC host driver’s platform data. This signals to the eMMC driver that the hardware timeout should be disabled under specific conditions. This quirk was introduced in Linux kernel v4.18 for certain platforms like TI OMAP.
static const struct sdhci_pltfm_data sdhci_omap_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
.quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN |
SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_RSP_136_HAS_CRC |
SDHCI_QUIRK2_DISABLE_HW_TIMEOUT,
.ops = &sdhci_omap_ops,
}
/*
* Disable HW timeout if the requested timeout is more than the maximum
* obtainable timeout.
*/
#define SDHCI_QUIRK2_DISABLE_HW_TIMEOUT
2. Ensuring eMMC Timeout Value Exceeds Host HW Timer Capacity: Once the quirk is in place, the eMMC driver’s __sdhci_set_timeout()
function will check if the calculated timeout value for an operation is too large for the host’s hardware timer. If it is, and the SDHCI_QUIRK2_DISABLE_HW_TIMEOUT
quirk is present, the hardware timer will be disabled, and software timeout management will be used instead.
void __sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
{
bool too_big = false;
u8 count = sdhci_calc_timeout(host, cmd, &too_big);
if (too_big &&
host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT) {
sdhci_calc_sw_timeout(host, cmd); /// calculate SW timeout value
sdhci_set_data_timeout_irq(host, false); // Disable HW timer
} else if (!(host->ier & SDHCI_INT_DATA_TIMEOUT)) {
sdhci_set_data_timeout_irq(host, true);
}
sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL);
}
Handling Timeouts in the Block Layer and eMMC Core
When a timeout occurs, both the block layer and the eMMC core have mechanisms to handle the situation, often involving error recovery and retries.
Block Layer Timeout Handling
If the software timer in the block layer expires, the mmc_mq_timed_out()
function is called. The behavior of this function depends on whether CQE is enabled:
-
CQE Disabled: If CQE is off,
mmc_mq_timed_out()
typically returnsBLK_EH_RESET_TIMER
. This indicates that the block layer should reset its timer, as the eMMC core is expected to handle the timeout and potential recovery. -
CQE Enabled: If CQE is enabled,
mmc_cqe_timed_out()
is called. For data transfer requests (MMC_ISSUE_ASYNC
) and eMMC cache flush requests (MMC_ISSUE_DCMD
), if the host definescqe_ops->cqe_timeout
, that function will be invoked. If recovery is needed,mmc_cqe_recovery_notifier()
will be called to initiate CQE recovery, and the function will returnBLK_EH_DONE
.
For other requests (e.g., erase
, discard
, sanitize
), even with CQE enabled, these operations often temporarily disable CQE. In such cases, the timeout event is handled by the eMMC core, and BLK_EH_RESET_TIMER
is returned.
eMMC Core Layer Timeout Handling
Hardware Timer Timeout for Data Transfer: A hardware data line timeout is reported by the host controller’s interrupt status. When this occurs, the sdhci_irq()
function is triggered, which then calls sdhci_data_irq()
. If a data error is detected, __sdhci_finish_data()
is invoked. This function typically performs a software reset of the command and data lines to reset the host’s internal state machines, attempting to clear the error condition.
sdhci_irq()-->sdhci_data_irq()
...
if (host->data->error)
sdhci_finish_data(host);
else {
...
}
...
__sdhci_finish_data() {
...
/*
* The controller needs a reset of internal state machines upon error
* conditions.
*/
if (data->error) {
if (!host->cmd || host->cmd == data_cmd)
sdhci_do_reset(host, SDHCI_RESET_CMD);
sdhci_do_reset(host, SDHCI_RESET_DATA);
}
}
Software Timer Timeout: When a software timer expires (e.g., sdhci_timeout_data_timer()
for data transfers or sdhci_timeout_timer()
for commands), a similar recovery process is initiated. For data transfers, __sdhci_finish_data()
is called, leading to a reset of the data and command lines. For command timeouts, sdhci_finish_mrq()
is invoked, which then wakes up sdhci_complete_work()
, ultimately resulting in a reset of the data and command lines.
Retry Mechanisms
In many timeout scenarios, the Linux kernel implements retry mechanisms to overcome transient errors. As seen with SWITCH CMD6
, commands may be retried multiple times before a definitive failure is declared. This retry logic is crucial for improving the robustness of eMMC operations and reducing the likelihood of system instability due to temporary communication issues.
Conclusion
eMMC timeout management in Linux is a complex but well-engineered system designed to ensure the reliability and performance of embedded storage. By understanding the interplay between the block layer and the eMMC core, and the various hardware and software timeout mechanisms, developers can better diagnose and mitigate issues related to eMMC communication. Recent kernel versions continue to refine these mechanisms, offering improved stability and more flexible configuration options for diverse eMMC implementations.