Linux MMC子系统技术分析报告 - 第二部分:协议实现与性能优化
基于 t41-kernel-4.4.94 内核代码的深度分析
1. 协议实现细节
1.1 MMC协议实现
1.1.1 命令类型与编码
MMC协议定义了多种命令类型,每种类型都有特定的格式和用途:
c
// include/linux/mmc/mmc.h
#define MMC_CMD_AC (0 << 5) // 地址控制命令
#define MMC_CMD_ADTC (1 << 5) // 地址数据传输命令
#define MMC_CMD_BC (2 << 5) // 广播命令
#define MMC_CMD_BCR (3 << 5) // 广播响应命令
// 标准MMC命令定义
#define MMC_GO_IDLE_STATE 0 // 复位到空闲状态
#define MMC_SEND_OP_COND 1 // 发送操作条件
#define MMC_ALL_SEND_CID 2 // 获取所有CID
#define MMC_SET_RELATIVE_ADDR 3 // 设置相对地址
#define MMC_SELECT_CARD 7 // 选择卡
#define MMC_SEND_EXT_CSD 8 // 发送扩展CSD
#define MMC_READ_MULTIPLE_BLOCK 18 // 读取多块数据
#define MMC_WRITE_MULTIPLE_BLOCK 25 // 写入多块数据
1.1.2 响应格式处理
MMC响应有多种格式,内核提供了完整的解析机制:
c
// drivers/mmc/core/mmc.c
static int mmc_decode_cid(struct mmc_card *card)
{
struct mmc_cid *cid = &card->cid;
u32 *resp = card->raw_cid;
cid->manfid = UNSTUFF_BITS(resp, 120, 8);
cid->prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
cid->prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
cid->prod_name[2] = UNSTUFF_BITS(resp, 80, 8);
cid->prod_name[3] = UNSTUFF_BITS(resp, 72, 8);
cid->prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
cid->prod_name[5] = UNSTUFF_BITS(resp, 56, 8);
cid->serial = UNSTUFF_BITS(resp, 16, 32);
cid->month = UNSTUFF_BITS(resp, 12, 4);
cid->year = UNSTUFF_BITS(resp, 8, 4) + 1997;
return 0;
}
1.1.3 扩展CSD处理
扩展CSD包含设备的高级特性和配置:
c
// drivers/mmc/core/mmc.c
static int mmc_read_ext_csd(struct mmc_card *card)
{
struct mmc_command cmd = {0};
struct mmc_data data = {0};
struct mmc_request mrq = {0};
u8 *ext_csd;
ext_csd = kmalloc(512, GFP_KERNEL);
if (!ext_csd)
return -ENOMEM;
cmd.opcode = MMC_SEND_EXT_CSD;
cmd.arg = 0;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = 512;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
sg_init_one(&sg, ext_csd, 512);
mmc_wait_for_req(card->host, &mrq);
// 解析重要字段
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
card->ext_csd.cmdq_support = ext_csd[EXT_CSD_CMDQ_SUPPORT];
card->ext_csd.cache_size = ext_csd[EXT_CSD_CACHE_SIZE] * 512;
kfree(ext_csd);
return 0;
}
1.2 SD协议实现
1.2.1 SD卡识别流程
SD卡识别与MMC有所不同,需要特殊的处理流程:
c
// drivers/mmc/core/sd.c
static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard)
{
struct mmc_card *card;
int err;
// 发送SD初始化命令序列
mmc_go_idle(host);
// 发送SD_SEND_IF_COND命令,检测SDv2.0
err = mmc_send_if_cond(host, ocr);
if (!err)
ocr |= SD_OCR_CCS; // 支持高容量
// 发送ACMD41,初始化SD卡
err = mmc_send_app_op_cond(host, ocr, NULL);
if (err)
goto err;
// 获取CID
err = mmc_all_send_cid(host, card->raw_cid);
if (err)
goto err;
// 设置RCA
err = mmc_set_relative_addr(card);
if (err)
goto err;
return 0;
err:
mmc_remove_card(card);
return err;
}
1.2.2 SD SCR寄存器处理
SCR(SD Configuration Register)包含SD卡特定信息:
c
// drivers/mmc/core/sd.c
static int mmc_read_scr(struct mmc_card *card)
{
int err;
u32 scr[2];
err = mmc_app_send_scr(card, scr);
if (err)
return err;
card->scr.scr_struct = UNSTUFF_BITS(scr, 0, 4);
card->scr.sd_spec = UNSTUFF_BITS(scr, 4, 4);
card->scr.bus_widths = UNSTUFF_BITS(scr, 16, 4);
if (card->scr.sd_spec >= 2) {
card->scr.sd_spec3 = UNSTUFF_BITS(scr, 47, 1);
if (card->scr.sd_spec3)
card->scr.sd_spec4 = UNSTUFF_BITS(scr, 42, 1);
}
return 0;
}
1.3 SDIO协议实现
1.3.1 SDIO设备检测
SDIO设备有独特的检测和初始化流程:
c
// drivers/mmc/core/sdio.c
static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard)
{
int err;
// SDIO设备不需要完整的MMC初始化序列
mmc_go_idle(host);
// 发送IO_SEND_OP_COND命令
err = mmc_send_io_op_cond(host, ocr, NULL);
if (err)
return err;
// 获取SDIO设备的CIA(Common I/O Area)信息
err = mmc_read_cccr(card);
if (err)
return err;
// 初始化功能
for (func = 0; func <= card->sdio_funcs; func++) {
err = sdio_init_func(card, func);
if (err)
goto remove;
}
return 0;
}
1.3.2 SDIO中断处理
SDIO支持中断机制,需要特殊的中断处理:
c
// drivers/mmc/core/sdio_irq.c
void sdio_run_irqs(struct mmc_host *host)
{
struct mmc_card *card = host->card;
unsigned int i;
if (!card || !card->sdio_funcs)
return;
// 检查每个功能的中断状态
for (i = 0; i < card->sdio_funcs; i++) {
struct sdio_func *func = card->sdio_func[i];
if (func && func->irq_handler) {
if (sdio_func_intr_pending(func)) {
func->irq_handler(func);
}
}
}
}
2. 高级性能优化机制
2.1 CMD23多块传输优化
CMD23命令允许预定义传输块数,显著提高传输效率:
c
// drivers/mmc/core/block.c
static int mmc_blk_set_blksize(struct mmc_blk_data *md,
struct mmc_card *card)
{
struct mmc_command cmd = {0};
int err;
// 使用CMD23设置块数
if (card->ext_csd.cmdq_support) {
cmd.opcode = MMC_SET_BLOCK_COUNT;
cmd.arg = md->queue.blocks;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err)
return err;
}
return 0;
}
// 优化的多块读取
static int mmc_blk_read_multiblock(struct mmc_blk_data *md,
struct mmc_card *card,
unsigned int blocks)
{
struct mmc_request mrq = {0};
struct mmc_command cmd = {0};
struct mmc_data data = {0};
// 首先使用CMD23设置块数
if (card->ext_csd.cmdq_support) {
mmc_blk_set_blksize(md, card);
}
// 然后执行多块读取
cmd.opcode = MMC_READ_MULTIPLE_BLOCK;
cmd.arg = md->queue.curr_sector;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = 512;
data.blocks = blocks;
data.flags = MMC_DATA_READ;
data.sg = md->queue.sg;
data.sg_len = md->queue.sg_len;
mmc_wait_for_req(card->host, &mrq);
return 0;
}
2.2 ADMA(高级DMA)优化
ADMA支持复杂的散聚传输,减少CPU干预:
c
// drivers/mmc/host/sdhci.c
static void sdhci_adma_table_pre(struct sdhci_host *host,
struct mmc_data *data)
{
struct sdhci_adma2_64_desc *desc;
unsigned int sg_len;
int i;
desc = host->adma_table;
sg_len = data->sg_len;
for (i = 0; i < sg_len; i++) {
struct scatterlist *sg = &data->sg[i];
dma_addr_t addr = sg_dma_address(sg);
unsigned int len = sg_dma_len(sg);
// 设置ADMA描述符
desc->addr = cpu_to_le32(addr);
desc->len = cpu_to_le16(len);
desc->attr = SDHCI_ADMA2_VALID | SDHCI_ADMA2_TRAN;
// 处理跨页边界的情况
if (host->flags & SDHCI_USE_64_BIT_DMA) {
desc->addr_hi = cpu_to_le32(upper_32_bits(addr));
}
desc++;
}
// 设置结束描述符
desc->attr = SDHCI_ADMA2_VALID | SDHCI_ADMA2_END;
}
// ADMA中断处理
static void sdhci_adma_irq(struct sdhci_host *host, u32 intmask)
{
struct mmc_data *data = host->data;
if (intmask & SDHCI_INT_ADMA_ERROR) {
// ADMA错误处理
data->error = -EIO;
sdhci_adma_error_recovery(host);
} else if (intmask & SDHCI_INT_DATA_END) {
// 数据传输完成
tasklet_schedule(&host->finish_tasklet);
}
}
2.3 动态时钟调整
根据工作负载和卡能力动态调整时钟频率:
c
// drivers/mmc/core/core.c
unsigned int mmc_set_clock(struct mmc_host *host, unsigned int freq)
{
unsigned int old_freq = host->ios.clock;
// 频率限制检查
if (freq < host->f_min)
freq = host->f_min;
if (freq > host->f_max)
freq = host->f_max;
// 根据卡类型和能力调整频率
if (host->card) {
switch (host->card->type) {
case MMC_TYPE_MMC:
if (host->card->ext_csd.hs_timing)
freq = min(freq, 52000000); // HS模式最高52MHz
if (host->card->ext_csd.strobe_support)
freq = min(freq, 200000000); // HS400模式最高200MHz
break;
case MMC_TYPE_SD:
if (host->card->sw_caps.uhs_max_dtr)
freq = min(freq, host->card->sw_caps.uhs_max_dtr);
break;
}
}
if (freq != old_freq) {
host->ios.clock = freq;
host->ops->set_ios(host, &host->ios);
// 等待时钟稳定
mmc_delay(1);
}
return freq;
}
// 基于负载的时钟调整
static void mmc_adjust_clock_for_load(struct mmc_host *host)
{
struct mmc_card *card = host->card;
unsigned int freq;
if (!card)
return;
// 根据当前I/O负载调整时钟
if (host->ios.load > 80) {
// 高负载,使用最高频率
freq = host->f_max;
} else if (host->ios.load > 40) {
// 中等负载,使用中等频率
freq = (host->f_min + host->f_max) / 2;
} else {
// 低负载,使用最低频率
freq = host->f_min;
}
mmc_set_clock(host, freq);
}
2.4 I/O请求队列优化
块设备层实现了高效的请求队列管理:
c
// drivers/mmc/card/queue.c
static int mmc_queue_thread(void *data)
{
struct mmc_queue *mq = data;
struct request_queue *q = mq->queue;
struct request *req;
do {
// 等待请求到来
wait_event_freezable(mq->wait,
(req = blk_fetch_request(q)) || kthread_should_stop());
if (req) {
// 请求预处理
mmc_queue_pre_req(mq, req);
// 执行请求
mq->issue_fn(mq, req);
// 请求后处理
mmc_queue_post_req(mq, req);
}
} while (!kthread_should_stop());
return 0;
}
// 请求合并和排序优化
static bool mmc_queue_merge_req(struct mmc_queue *mq, struct request *req)
{
struct request *next = blk_queue_next_request(req);
// 检查是否可以合并相邻请求
if (next && blk_rq_pos(next) == blk_rq_pos(req) + blk_rq_sectors(req)) {
if (mmc_can_merge(req, next)) {
blk_rq_sectors(req) += blk_rq_sectors(next);
blk_delete_request(next);
return true;
}
}
return false;
}
2.5 缓存优化
利用卡的内部缓存提高写入性能:
c
// drivers/mmc/core/mmc.c
static int mmc_enable_cache(struct mmc_card *card)
{
struct mmc_command cmd = {0};
int err;
if (!card->ext_csd.cache_size)
return 0;
// 发送CACHE_ENABLE命令
cmd.opcode = MMC_SWITCH;
cmd.arg = (EXT_CSD_CACHE_CTRL << 16) | (1 << 8) |
EXT_CSD_CMD_SET_NORMAL;
cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err)
return err;
// 等待缓存启用完成
err = mmc_wait_for_ready(card);
if (err)
return err;
card->cache_enabled = true;
return 0;
}
// 缓存刷写优化
static int mmc_flush_cache(struct mmc_card *card)
{
struct mmc_command cmd = {0};
int err;
if (!card->cache_enabled)
return 0;
// 发送CACHE_FLUSH命令
cmd.opcode = MMC_SWITCH;
cmd.arg = (EXT_CSD_FLUSH_CACHE << 16) | (1 << 8) |
EXT_CSD_CMD_SET_NORMAL;
cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err)
return err;
// 等待刷写完成
return mmc_wait_for_ready(card);
}
3. 错误处理与恢复机制
3.1 高级错误检测
实现多层次的错误检测机制:
c
// drivers/mmc/core/core.c
static int mmc_check_status(struct mmc_card *card, u32 status)
{
struct mmc_host *host = card->host;
// CRC错误检测
if (status & R1_COM_CRC_ERROR) {
pr_debug("%s: command CRC error\n", mmc_hostname(host));
return -EILSEQ;
}
// 非法命令检测
if (status & R1_ILLEGAL_COMMAND) {
pr_debug("%s: illegal command\n", mmc_hostname(host));
return -EINVAL;
}
// 设备状态检测
if (status & R1_ERROR) {
pr_debug("%s: general error\n", mmc_hostname(host));
return -EIO;
}
// 地址错误检测
if (status & R1_ADDRESS_ERROR) {
pr_debug("%s: address error\n", mmc_hostname(host));
return -EFAULT;
}
// 擦除参数错误
if (status & R1_ERASE_PARAM_ERROR) {
pr_debug("%s: erase parameter error\n", mmc_hostname(host));
return -EINVAL;
}
return 0;
}
3.2 自适应重试策略
根据错误类型采用不同的重试策略:
c
// drivers/mmc/core/core.c
static int mmc_execute_tuning(struct mmc_card *card)
{
struct mmc_host *host = card->host;
int err = 0;
// 仅在需要时进行调优
if (!host->ops->execute_tuning)
return 0;
// 执行调优序列
err = host->ops->execute_tuning(host, MMC_SEND_TUNING_BLOCK);
if (err)
return err;
// 验证调优结果
err = mmc_send_status(card, &status);
if (err)
return err;
return mmc_check_status(card, status);
}
// 智能重试机制
static int mmc_smart_retry(struct mmc_host *host, struct mmc_request *mrq)
{
int retry_limit = 3;
int err;
for (int retry = 0; retry < retry_limit; retry++) {
err = mmc_wait_for_req(host, mrq);
if (!err)
return 0;
// 根据错误类型决定重试策略
if (mrq->cmd->error == -EILSEQ) {
// CRC错误,降低时钟频率重试
if (host->ios.clock > host->f_min * 2) {
mmc_set_clock(host, host->ios.clock / 2);
continue;
}
} else if (mrq->cmd->error == -ETIMEDOUT) {
// 超时错误,增加超时时间
if (mrq->data && mrq->data->timeout_ns < 100000000) {
mrq->data->timeout_ns *= 2;
continue;
}
}
// 其他错误不再重试
break;
}
return err;
}
3.3 数据完整性保护
实现多层次的数据完整性保护:
c
// drivers/mmc/core/block.c
static int mmc_blk_verify_data(struct mmc_card *card,
struct mmc_data *data,
struct scatterlist *sg)
{
struct mmc_host *host = card->host;
u32 calculated_crc;
u32 received_crc;
// 计算数据的CRC
calculated_crc = crc32_le(~0, sg_virt(sg), data->blksz * data->blocks);
// 获取接收到的CRC(如果硬件支持)
if (host->ops->get_crc) {
received_crc = host->ops->get_crc(host);
if (calculated_crc != received_crc) {
pr_err("%s: CRC mismatch: calc=0x%08x, recv=0x%08x\n",
mmc_hostname(host), calculated_crc, received_crc);
return -EILSEQ;
}
}
return 0;
}
4. 电源管理高级特性
4.1 动态电源管理
实现智能的电源管理策略:
c
// drivers/mmc/core/core.c
static int mmc_power_save(struct mmc_host *host, bool save)
{
struct mmc_card *card = host->card;
int ret = 0;
if (!card)
return 0;
if (save) {
// 保存当前状态
host->saved_ios = host->ios;
// 进入省电模式
if (host->ops->power_save)
ret = host->ops->power_save(host, true);
// 关闭时钟
host->ios.clock = 0;
host->ops->set_ios(host, &host->ios);
// 通知卡设备
if (card->type == MMC_TYPE_MMC && card->ext_csd.power_off_notification) {
mmc_send_power_off_notification(card);
}
} else {
// 恢复电源
if (host->ops->power_restore)
ret = host->ops->power_restore(host);
// 恢复时钟
host->ios = host->saved_ios;
host->ops->set_ios(host, &host->ios);
// 重新初始化卡
mmc_card_reinit(host);
}
return ret;
}
4.2 运行时电源管理
支持运行时电源管理(Runtime PM):
c
// drivers/mmc/card/block.c
static int mmc_blk_runtime_suspend(struct device *dev)
{
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
int ret = 0;
// 刷写缓存
if (card->cache_enabled) {
ret = mmc_flush_cache(card);
if (ret)
return ret;
}
// 进入休眠模式
ret = mmc_card_sleep(host);
if (ret)
return ret;
// 降低时钟频率到最低
mmc_set_clock(host, host->f_min);
// 关闭总线电源
mmc_set_bus_power(host, false);
return 0;
}
static int mmc_blk_runtime_resume(struct device *dev)
{
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
int ret = 0;
// 恢复总线电源
mmc_set_bus_power(host, true);
// 恢复到工作频率
mmc_set_clock(host, host->f_max);
// 唤醒卡设备
ret = mmc_card_awake(host);
if (ret)
return ret;
// 重新选择卡
ret = mmc_select_card(card);
if (ret)
return ret;
return 0;
}
5. 总结
Linux MMC子系统通过以下高级特性实现了卓越的性能和可靠性:
5.1 协议优化
- CMD23预定义传输:减少命令开销,提高大块传输效率
- ADMA高级DMA:支持复杂散聚传输,降低CPU占用
- 多协议支持:无缝支持MMC/SD/SDIO设备
5.2 性能优化
- 动态时钟调整:根据负载智能调整工作频率
- 请求队列优化:合并相邻请求,减少总线争用
- 缓存利用:充分利用设备内部缓存提升写入性能
5.3 可靠性保障
- 多层次错误检测:CRC校验、状态监控、数据完整性验证
- 智能重试策略:根据错误类型采用不同重试机制
- 自适应恢复:动态调整参数以适应当前条件
5.4 电源效率
- 动态电源管理:智能的省电模式切换
- 运行时电源管理:支持Runtime PM规范
- 时钟门控:精细的时钟控制策略
这些高级特性使Linux MMC子系统能够在各种应用场景下提供稳定、高效、低功耗的存储解决方案。