linux的mmc子系统与块设备关联

2023-11-17

1.前言

本文主要block组件的主要流程,在介绍的过程中,将详细说明和block相关的流程,涉及到其它组件的详细流程再在相关文章中说明。

2.主要数据结构和API

2.1 struct mmc_card

Elemete Name struct mmc_card
Path include/linux/mmc/card.h
Responsiblities

 是对mmc device的抽象,由于定义了mmc_bus_type类型的总线,此处mmc_card是与mmc_bus_type配套

Attributions
  • host:struct mmc_host *类型,这个mmc device属于哪个host管理;
  • dev:struct device类型,代表设备驱动模型中的一个 device
  • ocr:当前的操作电压设置
  • rca:device的relative card address
  • type:卡的类型,包括MMC/SD/SDIO/COMBO(SDIO+MEM)
  • state:卡的状态,在线、只读、是否使用block地址、是否是SDXC卡、卡被移除、卡在BKOPS、卡在suspend
  • quirks:卡的一些其它怪癖属性
  • erase_size:单位sectors
  • erase_shift:可以擦除的 sectors是2的多少次方
  • pref_erase:单位sectors
  • eg_boundary
  • erased_byte:擦除的字节数
  • raw_cid:原始的CID值
  • raw_csd:原始的CSD值
  • raw_scr:原始的raw_scr值
  • cid:struct mmc_cid类型,卡identification
  • csd:struct mmc_csd类型,保存从卡的CSD寄存器读取的内容
  • ext_csd:struct mmc_ext_csd类型,卡扩展信息
  • scr:其它的SD信息
  • ssr:更多的SD信息
  • sw_caps:swicth能力
  • sdio_funcs:SDIO功能的个数
  • cccr:struct sdio_cccr类型,卡的通常信息
  • cis:struct sdio_cis
  • sd_bus_speed:bus speed mode
  • mmc_avail_type:host和card都支持的设备类型
  • drive_strength:驱动能力,用于UHS-I, HS200 or HS400
  • debugfs_root:struct dentry *类型,用于debugfs显示根目录
  • part:struct mmc_part类型,物理分区
  • nr_parts:物理分区的个数
Operations

2.2 struct mmc_driver

Elemete Name struct mmc_driver
Path include/linux/mmc/card.h
Responsiblities

mmc driver,由于定义了mmc_bus_type类型的总线,此处mmc_driver是与mmc_bus_type配套

Attributions
  • drv:struct device_driver类型
  • probe,remove,shutdown:mmc driver相关函数
Operations
  •  int mmc_register_driver(struct mmc_driver *drv)

 设置总线类型,并将drv加入到设备驱动模型中

  • void mmc_unregister_driver(struct mmc_driver *drv)

将drv从设备驱动模型中移除

2.3 struct mmc_blk_data

Elemete Name struct mmc_blk_data
Path drivers/mmc/card/block.c
Responsiblities

mmc_blk_data为block的核心结构体,用于存放mmc block的一些数据,与mmc slot对应

Attributions
  • lock:spinlock_t类型
  • disk:struct gendisk *类型,代表一个磁盘设备
  • queue:struct mmc_queue类型,请求队列
  • part:分区链表
  • flags:
  • usage:
  • read_only:
  • part_type:
  • name_idx:
  • reset_done:
  • part_curr:
  • force_ro:
  • power_ro_lock:
  • area_type:
Operations

3. 主要流程

3.1 mmc_blk_init

mmc_blk_init->

  初始化max_devices

  register_blkdev

  mmc_register_driver

module_init(mmc_blk_init)会执行到此函数

  • 初始化max_devices:设定最多支持多少个mmc块设备给max_devices
每类块设备支持256个次设备号,每个块设备有16个次设备号(16个分区),由此得出支持的最大的mmc块设备数max_devices为256/16=16,每个此设备号对应一个分区?
  • register_blkdev:向全局的struct blk_major_name类型的数组major_names注册本块设备的主设备号和设备名

mmc子系统对于上层block子系统来讲是首先抽象为一个普通的块设备。
通过register_blkdev向block子系统注册一个block设备,主设备号为MMC_BLOCK_MAJOR,设备名为“mmc”。
通过分配一个blk_major_name结构体,来保存主设备号和设备名,blk_major_name被保存到全局的blk_major_name数组中。
如果不指定主设备号,将查询全局的blk_major_name结构体找到一个未用的主设备号来使用,并将此主设备号作为返回值返回。
major_names中的信息会出现在/proc/devices中。
因此可以看出,注册做的事情实际上非常少。注册完成后,除了能够在/proce/devices中看到设备之外,不能对设备做任何事情,设备还无法使用,只有当block_device与gendisk建立关联用户空间才可以访问

  • mmc_register_driver(&mmc_driver)
设备驱动模型中通过driver_register将mmc_driver注册到mmc_bus_type上

3.2 mmc_blk_exit

mmc_blk_exit->

  mmc_unregister_driver

  unregister_blkdev

在退出的时候会执行mmc_blk_exit,与mmc_blk_init相反的动作,主要包括:

  • mmc_unregister_driver(&mmc_driver)
从mmc_bus_type上将mmc_driver注销
  •  unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
从全局的struct blk_major_name类型的数组major_names中注销主设备号为MMC_BLOCK_MAJOR名为mmc的blk_major_name结构体
释放对应的blk_major_name结构体

3.3 mmc_blk_probe

mmc_blk_probe->

       mmc_blk_alloc->

              mmc_blk_alloc_req->

        alloc_disk

                     mmc_init_queue->

          blk_queue_prep_rq

                            kthread_run(mmc_queue_thread, mq);

  mmc_blk_alloc_parts(card, md))

  mmc_add_disk->

      block_add_disk

初始化时mmc_blk_init中会执行mmc_register_driver,而前文所述执行mmc_attach_mmc时会通过mmc_add_card将mmc_card注册到mmc bus,这样就触发了执行前文所述的mmc_blk_probe函数,后面有详细解释mmc_blk_probe的执行过程

mmc_blk_probe最主要的是初始化了request queue;初始化disk,同时通过mmc_add_disk将磁盘添加到系统中,使之可用

  • mmc_blk_alloc_req

创建并初始化请求队列,启动线程循环抓取请求队列中的request,调用request处理函数进行处理

(1)分配mmc_blk_data结构体md并初始化,同时mmc_queue作为mmc_blk_data的成员也被创建
mmc_blk_data为block的核心结构体,与mmc_card关联,用于存放mmc_card相关数据,每个mmc slot即每个mmc设备对应一个mmc_blk_data结构体。
此处会分配mmc_blk_data结构体md,同时mmc_queue作为mmc_blk_data的成员也被创建。并标识dev_use的bitmap来记录已经分配的mmc device,
也就是说dev_use是与实际的物理设备相对应的,不是跟分区对应的,dev_use的index用dev_idx来记录
注意到此处MMC_BLK_DATA_AREA_MAIN表示主分区的区域(mmc_blk_data与设备对应,此处看又像是与分区对应??)。
MMC分区类型包括如下几种:
#define MMC_BLK_DATA_AREA_MAIN  (1<<0)
#define MMC_BLK_DATA_AREA_BOOT  (1<<1)
#define MMC_BLK_DATA_AREA_GP    (1<<2)
#define MMC_BLK_DATA_AREA_RPMB  (1<<3)

(2) alloc_disk(perdev_minors):分配gendisk结构体保存到md中,gendisk与磁盘设备对应

(3)mmc_init_queue(queue_c):创建并初始化请求队列
通过调用block子系统接口blk_init_queue来初始化请求队列,其中mmc_request_fn为处理请求的回调函数
blk_queue_prep_rq(mq->queue, mmc_prep_request)设定requet_queue的prep回调函数;
mmc_alloc_sg(host->max_segs, &ret)分配max_segs个scatterlist用于request请求(只是分配scatterlist,并未分配存放数据的内存),
返回分配的scatterlist个数
kthread_run(mmc_queue_thread, mq) 起一个kennel thread运行mmc_queue_thread来处理上层发送下来的request,对每个reqeust执行issue_fn回调

注:issue_fn回调在下面指定为mmc_blk_issue_rq
(4)指定issue_fn回调为mmc_blk_issue_rq,mmc_blk_issue_rq是具体的mmc request处理函数

  • mmc_blk_alloc_parts(card, md))

  • mmc_add_disk
为了将一个磁盘添加到系统中,对系统可用,必须初始化磁盘数据结构并调用add_disk方法。
需要特别注意的是一旦调用了add_disk,磁盘就被“激活”了,系统随时都可能会调用该磁盘提供的各种方法,
甚至在该函数返回之前就会调用,因而在完成磁盘结构的初始化之前,不要调用add_disk。

3.4 mmc_add_disk

mmc_add_disk->

  device_add_disk

device_add_disk的原型为void device_add_disk(struct device *parent, struct gendisk *disk) 它完成的工作主要包括:

(1)根据磁盘的主次设备号信息为磁盘分配设备号;

(2)调用disk_alloc_events初始化磁盘的事件(alloc|add|del|release)处理机制。在最开始磁盘事件会被设置为被阻塞的。

(3)调用bdi_register_dev将磁盘注册到bdi_list,注:bdi用于将page_cache或buffer_cache中的脏数据刷新到磁盘

(4)调用blk_register_region将磁盘添加到bdev_map中(通过设备号可以获取kobject从而得到包含它的父对象进行操作)

(5)调用register_disk将磁盘添加到系统中。

(6)调用blk_register_queue注册磁盘的请求队列。主要是为队列和队列的调度器在设备的sys文件系统目录中创建相应的sys目录/文件,并且发出uevent事件。

(7)调用disk_add_events完成在/sys文件系统的设备目录下创建磁盘的事件属性文件,将磁盘事件添加到全局链表disk_events中,解除对磁盘事件的阻塞。

关于probe函数是如何被调用到的?

一般我们认为mmc_blk_probe的执行一定需要mmc_driver与mmc_device的匹配才可以,实际上没有mmc_device,  而是有mmc_card,mmc_blk_probe的执行经历如下历程:

(1)先来看mmc_register_driver的流程

       mmc_register_driver->

              driver_register->

                     driver_find//bus查看driver是否已经注册,如果已经注册则退出,否则bus add driver

                     bus_add_driver->

                            driver_attach->

                                   bus_for_each_dev//此处由于还没有device注册,因此会退出

显然mmc_blk_probe的执行不是在mmc_register_driver的时候,那么肯定是在device_register的时候,看看我们的假设是否正确,继续往下看

(2)mmc_alloc_card, mmc_add_card

通过浏览代码,我们发现在mmc/core/bus_c中有mmc_alloc_card和mmc_add_card

mmc_alloc_card:mmc_attach_mm->mmc_init_card初始化并分配一个新的mmc_card结构体,实际上是创建device设备;

mmc_add_card:mmc_attach_mmc->mmc_add_card时调用,通过调用device_add(&card->dev)来完成设备的注册,过程如下:

              mmc_add_card->

                            device_add->

                                   bus_probe_device->

                                          device_attach->

                                                 __device_attach->

                                                               driver_match_device->

                                                               mmc_bus_match//此函数的特殊之处在于总是返回值为1

                                                        driver_probe_device->

                                                               really_probe->

                                                                      mmc_bus_probe->

                                                                             mmc_blk_probe

mmc_blk_probe的执行不是靠device和driver的匹配,而是将匹配函数mmc_bus_match总是返回1,如下:

static int mmc_bus_match(struct device *dev, struct device_driver *drv)

{

       return 1;

}

这样就可以执行到mmc_bus_type的probe函数进而执行到mmc_blk_probe。

3.5 mmc_queue_thread

线程处理函数,用于循环抓取请求队列中的request并交给请求处理函数进行处理

mmc_queue_thread->

       blk_fetch_request

         issue_fn(mmc_blk_issue_rq)->

             mmc_blk_issue_rw_rq->

                 mmc_blk_rw_rq_prep

                 mmc_start_req –>

                     mmc_wait_for_data_req_done->

                         mmc_blk_err_check

                         host->ops->request

 mmc_queue_thread是在mmc_init_quene中起的线程,主要作用是完成上层发送的请求进行处理

  •  blk_fetch_request
从请求队列中取出一个request
  • issue_fn
由前面可知issue_fn在mmc_blk_probe->mmc_blk_alloc_req时将issue_rq初始化为mmc_blk_issue_rq,请求有几种包括:discard, flush, 以及rw
  • mmc_blk_issue_rw_rq
首先通过mmc_blk_rw_rq_prep来做一些准备工作,获取命令号、命令参数等,然后通过mmc_start_req发起请求
  • mmc_start_req
通过mmc_wait_for_data_req_done发起真正的请求,并等待请求结束。
mmc_wait_for_data_req_done会回调控制器的request函数发起请求,然后mmc_blk_err_check检查是否有错误发生,
如果有错误发生将尝试recovery进行修复开始新的传输

3.6 mmc_blk_issue_rq

mmc_blk_issue_rq->

   mmc_claim_host

  mmc_blk_part_switch

  mmc_blk_issue_rw_rq->

    mmc_blk_prep_packed_list

    mmc_blk_rw_rq_prep

    mmc_start_req->

      __mmc_start_data_req

    mmc_queue_bounce_post

    检查mmc_start_req返回的状态

mmc_blk_issue_rq对发送的mmc request进行具体的处理。

  • mmc_claim_host
实际上是声明当前进程占有host controller,如果有其它进程占有则需要等待,详细的可参考Linux mmc framework2:基本组件之core
  • mmc_blk_part_switch
通过MMC_SWITCH命令对EXT_CSD寄存器的PARTITION_CONFIG(bit[179])进行设置,主要包括boot是否使能、用哪个分区做boot分区、选择要访问的分区。
如果MMC_SWITCH命令出错,将通过blk_end_request_all终止request
  • mmc_blk_issue_rw_rq
根据req->cmd_flags的命令做不同的事情。REQ_SANITIZE、REQ_DISCARD、REQ_FLUSH分别为
. mmc_blk_issue_secdiscard_rq 和mmc_blk_issue_discard_rq
. mmc_blk_issue_flush
. mmc_blk_issue_rw_rq(这个是我们要分析的读写数据流程

1. mmc_blk_prep_packed_list尝试把当前request和队列中的其他request合并,以增强性能。是否可以合并,要依赖于:
控制器支持packed功能;
device的MAX_PACKED_WRITES 大于0;
只对写request进行packed

2. mmc_blk_rw_rq_prep:正常情况下执行mmc_blk_rw_rq_prep函数,从request构造mmc_request,毕竟下发给host请求,是mmc_request,而不是block层通用的request。
如果支持packed功能,那么就用pack_list来构造mmc_request

3. mmc_start_req:mmc_start_req 启动一个非阻塞的request,这个函数会等待前一个request完成,然后启动当前requeset,并立刻返回
如果mmc_start_req返回的areq不为空,说明完成了上一次的request

  • mmc_start_req
  • mmc_start_req 启动一个非阻塞的request,这个函数会等待前一个request完成,然后启动当前requeset,并立刻返回 如果mmc_start_req返回的areq不为空,说明完成了上一次的request

1. 首先它会执行到mmc_wait_for_data_req_done函数,等待上一次的命令的完成,如果上一次未完成就会将当前进程加入等待队列休眠,等待被唤醒。

   当上一次完成后会立即返回,并将上一次命令执行的状态返回给mmc_blk_issue_rw_rq。

2、if (host->areq) {

    err = mmc_wait_for_data_req_done(host, host->areq->mrq, areq);

host->areq不为空,说明有正在处理的reuqest,函数mmc_wait_for_data_req_done用来等待这个host->areq,有两个条件会唤醒该MMC上下文: is_done_rcv和is_new_req

3. if (!err && areq)      

   start_err = __mmc_start_data_req(host, areq->mrq);

进入__mmc_start_data_req(host, areq->mrq);

(1)首先会将函数指针mmc_wait_data_done赋给mrq->done.

mmc_wait_data_done会设置context_info->is_done_rcv=true,这正好是唤醒mmc_wait_for_data_req_done的条件之一,然后调wake_up_interruptible(&context_info->wait);唤醒之。

(2)然后会调用mmc_start_request(host, mrq);

 mmc_start_reuqest实际调用host->ops->request方法,进入了平台特定的request函数

进入特定的平台之后,会进入相应的中断对硬件进行读写的命令的执行,当命令执行完毕后,会进行函数回调调到刚才的mmc_wait_data_done唤醒等待的进程进行下一次命令的执行。

  •  mmc_queue_bounce_post
如果使用了bounce buffer,那么需要把传输结果从bounce buffer复制会sg buffer。
所谓bounce buffer是因为某些DMA控制器只能处理连续物理内存,此时需要通过bounce buffer来达到物理内存连续性。
  • 检查mmc_start_req返回的状态
1. 如果是MMC_BLK_SUCCESS或者MMC_BLK_PARTIAL,需要调用blk_end_request通知block设备层,完成了本次读写request。
2. 如果是MMC_BLK_CMD_ERR,那么调用mmc_blk_reset复位host。调用mmc_blk_cmd_err尝试blk_end_request,如果发现reuqest未完成,说明本次操作失败,反之成功start_new_req
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux的mmc子系统与块设备关联 的相关文章

随机推荐

  • 一、【React基础】React简介及基本使用

    文章目录 1 官网 2 特点 3 相关js库 4 基本使用 4 1 CODE 4 2 Result 用于动态构建用户界面的 JavaScript 库 只关注于视图 由Facebook开源 1 官网 英文官网 https reactjs or
  • 广电用户画像分析之根据用户行为数据进行筛选与标签添加

    在数据处理和分析领域 我们经常需要根据用户的行为数据进行筛选和标签添加 以便更好地理解用户行为和偏好 在本篇博客中 我们将介绍两个示例 展示如何根据用户的收视行为数据和订单信息进行数据处理和分析 前情提要 数据集分析 广电用户画像分析之探索
  • Vue的学习

    一 Vue框架 常用地指令学习 https cn vuejs org 官网地址 概述 前端领域当中有三大框架 都是开发单页面应用的框架 Angular诞生于2009年 是由谷歌创建出来的框架 React诞生于2013年 是由facebook
  • idea无限build解决方案

    点击File Settings Build Excution Deploment Compiler勾上build project automatically
  • 【数据结构】树与二叉树总结(一)

    数据结构 树与二叉树的总结 一 1 AVL树 动态平衡二叉树 树表的查找 2 哈夫曼树 二叉树的应用 3 树 树与二叉树的转换 4 分裂树 5 S K R 注 K为结点 R为关系 一 树 定义 n n gt 0 个结点构成的有限集合 当n
  • SCSI硬盘

    SCSI硬盘即采用SCSI接口的硬盘 它由于性能好 稳定性高 因此在服务器上得到广泛应用 同时其价格也不菲 正因它的价格昂贵 所以在普通PC上很少见到它的踪迹 说到SCSI硬盘必须提到SCSI接口 SCSI是Small Computer S
  • MFC中窗口初始化时OnCreate、OnInitDialog、OnSize、OnPaint的 调用顺序

    执行顺序 以箭头方向 OnCreate gt OnSize gt OnInitDialog gt OnPaint OnCreate 要创建一个窗口 OnSize 用于窗口初始化前的一些参数设定 OnInitDialog 初始化窗口 例如设置
  • Jmeter录制脚本 模拟用户登录操作

    使用Jmeter测试web应用时 通常有两种测试计划 面向请求数的测试 面向场景的测试 模拟用户操作属于面向场景的测试 准备工作 1 1 建立线程组和HTTP请求 在测试计划中新建线程组 线程组中添加一个HTTP请求 并配置 1 2 录制控
  • 最小生成树总结1 prim算法

    最小生成树总结1 prim算法 最小生成树总结2 kurskal算法 文章目录 1 最小生成树问题概述 2 Prim算法流程 3 模板 4 板子题 1 最小生成树问题概述 给定带权节点网络 从中确定一个包含所有节点 n个 n 1条边 所有节
  • 机器学习到底是什么?

    关于机器学习最简单的定义来自于Berkeley所表述的 机器学习是AI的一个分支 它探索了让计算机根据经验提高效率的方法 为了更深刻的理解这一定义 接下来我们将对其进行拆分分析 AI的分支 人工智能是一种能够使得计算机及其系统能够成功完成通
  • MATLAB实现将数据写入到txt文件以及从txt文件中读取数据

    1 将数据写入到txt文件 global n global xm global ym xm 100 ym 100 global A global B n 10 A zeros 1 n B zeros 1 n for i 1 n A 1 i
  • 【Scala】scala ':' or newline expected \u200b

    1 背景 拷贝了网上一段代码 发现报错如下 这个是因为不可见字符 引起的 全局替换一下就好了 参考 IDEA IDEA报错 illegal character U 00A0异常解决
  • C++与C#类型转换例子

    C 部分类型定义 typedef struct SID AND ATTRIBUTES HASH DWORD SidCount PSID AND ATTRIBUTES SidAttr SID HASH ENTRY Hash SID HASH
  • Window11安装Docker Desktop(构建软链接解决Docker自动安装在C盘占用内存问题)

    一 查看电脑是否开启虚拟化 二 配置 Hyper V 三 构建软连接 使Dockor可以不安装在C盘 四 下载安装Dockor 五 配置环境 一 查看电脑是否开启虚拟化 打开设备管理器 gt 性能 gt CPU 查看是否开启虚拟化 若以开启
  • python中def main是什么意思_python - 为什么要使用def main()?

    其他人已经回答了 但我想我还有其他的东西需要补充 main 语句调用if的原因 无特定顺序 其他语言 如C和Java 具有main 函数 该函数在执行程序时调用 使用这个if 我们可以让Python像他们一样 这对许多人来说更为熟悉 代码将
  • python题库刷题网站_python在线刷题网站

    moduleinfo card count count phone 1 count 1 search count count phone 4 count 4 card des 阿里技术人对外发布原创技术内容的最大平台 社区覆盖了云计算 大数
  • java snack_Java 中 Snack3的使用

    网上看了一篇Java 中 Gson的使用 所以也跟着写篇Java 中 Snack3的使用 JSON 是一种文本形式的数据交换格式 从Ajax的时候开始流行 它比XML更轻量 比二进制容易阅读和编写 解析和生成的方式很多 Java中最常用的类
  • DHCP 服务器部署

    DHCP gt Dynamic Host Configuration Protocol 动态主机配置协议 背景 任何一个需要上网的设备 都必须得有IP地址 子网掩码 网关 等等网络参数 比如 手机 电脑 智能手表 智能眼镜 一个设备获取IP
  • 【深入理解C++】拷贝构造函数

    文章目录 1 拷贝构造函数 2 默认的拷贝操作 3 默认拷贝构造函数 4 何时调用拷贝构造函数 1 拷贝构造函数 拷贝构造函数是构造函数的一种 当利用已存在的对象创建一个新对象时 就会调用新对象的拷贝构造函数进行初始化 拷贝构造函数的格式是
  • linux的mmc子系统与块设备关联

    1 前言 本文主要block组件的主要流程 在介绍的过程中 将详细说明和block相关的流程 涉及到其它组件的详细流程再在相关文章中说明 2 主要数据结构和API 2 1 struct mmc card Elemete Name struc