[sd card] mmc_blk层为sd card创建块设备流程

本文详细介绍了Linux内核中mmc_blk层如何为SD卡创建块设备的过程,从块设备框架、mmc_blk实现、驱动模型到创建块设备的流程,深入解析了mmc_blk_probe、mmc_blk_alloc和mmc_add_disk等关键函数,揭示了块设备从文件系统到硬件的完整数据路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、块设备框架

以下内容摘自http://blog.youkuaiyun.com/jianchi88/article/details/7212370.

1、块设备框架图

块设备框架图

avatar

2、说明

  • 各个层次说明

    • 由通用块层(Generic Block Layer)负责维持一个I/O请求在上层文件系统与底层物理磁盘之间的关系。在通用块层中,通常用一个bio结构体来对应一个I/O请求。
    • 在Linux中,驱动对块设备的输入或输出(I/O)操作,都会向块设备发出一个请求,在IO调度层中用request结构体描述。
    • 但对于一些磁盘设备而言请求的速度很慢,这时候内核就提供一种队列的机制把这些I/O请求(request结构体)添加到队列中(即:请求队列),在驱动中用request_queue结构体描述。在向块设备提交这些请求前内核会先执行请求的合并和排序预操作,以提高访问的效率,然后再由内核中的I/O调度程序子系统来负责提交 I/O 请求, 调度程序将磁盘资源分配给系统中所有挂起的块 I/O 请求,其工作是管理块设备的请求队列,决定队列中的请求的排列顺序以及什么时候派发请求到设备。
    • Linux提供了一个gendisk数据结构体,用来表示一个独立的磁盘设备或分区,用于对底层物理磁盘进行访问。在gendisk中有一个类似字符设备中file_operations的硬件操作结构指针,是block_device_operations结构体。
  • 整体数据流程说明
    文件系统——》通用块层——》IO调度层——》块设备驱动层

    • 文件系统会向通用块层发起块读写请求
    • 通用块层将读写请求封装成bio结构体(block io),下发到IO调度层
    • IO调度层将bio封装成request结构体,并将request添加到对应块设备(gendisk)的请求队列(request_queue)中,并做一些预处理。
    • IO调度层对请求队列(request_queue)中的request进行预处理之后,调用请求队列(request_queue)的回调函数来对队列中的IO(request)进行处理
    • 而请求队列(request_queue)以及request处理回调函数则是由块设备驱动层提供,也就是块设备驱动层会从请求队列(request_queue)提取request并且进行实质性的处理。
  • 各个层次的请求(IO)以及请求队列对应的结构体

    • 通用块层:struct bio
    • IO调度层:struct request(封装了struct bio)、struct request_queue
    • 块设备驱动层:
      各类块设备驱动层对于IO请求的封装是不一样的。以sd card的mmc块设备驱动层为例:
      struct mmc_queue_req(封装了struct request)、struct mmc_queue(封装了struct request_queue)。

3、mmc块设备驱动层

块设备驱动有很多个种类,对于sd card来说,使用的是mmc块设备驱动层。
这部分内容可以参考《[mmc subsystem] 概念与框架
mmc块设备驱动层已经属于mmc subsystem结构的一部分。其框架图如下:

avatar
(上图摘自蜗窝科技http://www.wowotech.net/comm/mmc_framework_arch.html

  • 其主要分成三个层次:
    • mmc card drivers层(mmc_blk层)
      向上:为mmc card(如sd card)实现对应的request_queue以及gendisk并注册,生成的对应的块设备文件。
      请求处理:对上层传下来的request进行提取处理
      向下:向mmc core层提交mmc请求
    • mmc core层(只提取部分内容)
      可以参考《[mmc subsystem] mmc core(第一章)——概述
      向上:为mmc card drivers提供注册mmc driver的接口。为mmc card drivers提供发起mmc请求的接口。
      请求处理:异步mmc请求的处理
      向下:向mmc host层提交mmc请求
    • mmc host层(只提取部分内容)
      可以参考《[mmc subsystem] host(第一章)——概述
      对mmc core下发的mmc请求进行处理,然后通过mmc总线发起实际的通讯内容。

5、总结

综上,对于sd card而言,整个数据流是:
文件系统——》通用块层——》IO调度层——》mmc_blk层——》mmc core层——》mmc host层——》mmc硬件总线——》sd card。
而这里我们要学习的就是mmc_blk层。
对应代码是drivers/mmc/card/block.c、driver/mmc/card/queue.c
同时,我们也知道了mmc_blk层的核心内容是实现块设备,为了实现块设备,实现块设备的核心就是要实现对应的gendisk以及request_queue。后续就是围绕这个内容对mmc_blk设备驱动进行展开。

二、mmc_blk实现块设备核心框架

1、实现块设备的驱动框架

通过第一节的内容,可以知道实现块设备主要是实现如下内容

  • 创建struct request_queue
    请求队列,IO调度层会把IO请求(struct request)往对应块设备的这个结构体里面挂,然后调用request_queue里面的回调函数对request进行处理。
    可以调用blk_init_queue来创建一个request_queue。
    创建方法eg:blk_init_queue(mmc_request_fn, lock),其中,mmc_request_fn则是从request_queue提取request并进行处理的回调函数。

  • 创建struct gendisk
    struct gendisk用来代表一个独立的磁盘设备或者分区。一个块设备对应一个gendisk。块设备驱动通过创建对应的gendisk并注册来产生对应的块设备节点。
    可以调用alloc_disk来分配一个gendisk。
    分配方法eg:alloc_disk(perdev_minors),perdev_minors表示分配给每个块设备的从设备号数量

  • 初始化struct gendisk
    主要初始化如下内容:

    • struct gendisk->major,块设备的主设备号,对于mmcblk块设备来说,其主设备号是MMC_BLOCK_MAJOR,179
    • struct gendisk->first_minor,分配的第一个从设备号
    • struct gendisk->fops,块设备的操作函数,对于mmcblk来说,是mmc_bdops
    • struct gendisk->private_data,私有数据指针,一般关联到对应块设备驱动的数据结构体
    • struct gendisk->driverfs_dev,父sys节点
    • struct gendisk->flags
    • struct gendisk->disk_name,块设备名
  • 关联struct gendisk和struct request_queue
    因为IO调度层里面的request_queue一般有不止一个。当数据从通用块层下来的时候,IO调度层只知道要写到哪个gendisk,然后根据gendisk里面获取到对应的request_queue,然后再request往request_queue里面放。
    所以就要求gendisk必须和request_queue关联起来。
    关联方法:struct gendisk->queuerequest_queue.

  • 注册块设备
    不管前面的流程怎么样,最终的目的都是注册一个块设备到系统中。
    可以通过调用add_disk(gendisk)来注册块设备。

2、mmc_blk实现块设备的驱动框架补充

核心框架就是上述“实现块设备的驱动框架”的部分。

以下要特别注意,方便理解代码。

  • 根据mmc_blk的实现方式,有如下注意点:

    • 构造mmc_queue_req作为一个MMC IO请求,把从request_queue提取request封装到这里面来。
    • 构造mmc_queue作为块设备的请求队列,把request_queue封装到mmc_queue中。同时存储了当前正在处理以及上一次正在处理的MMC IO请求mmc_queue_req。
    • 构造mmc_blk_data作为块设备的私有数据,同时把mmc块设备请求对应mmc_queue以及块设备gendisk存储到mmc_blk_data中。
    • 同时mmc_blk_data也存放了mmc_blk驱动相关的部分内容。
  • 因此mmc_blk实现块设备的整体流程如下

    • 创建struct mmc_blk_data
    • 初始化mmc_blk_data
    • 初始化struct mmc_blk_data->mmc_queue
    • 创建struct mmc_blk_data->mmc_queue->request_queue(创建struct request_queue)
    • 分配和初始化struct mmc_blk_data->gendisk(创建struct gendisk & 初始化struct gendisk)
    • 关联struct mmc_blk_data->gendisk 和 mmc_blk_data->mmc_queue->request_queue (关联struct gendisk和struct request_queue)
    • 注册mmc_blk_data->gendisk到系统中 (注册块设备)

三、mmc_blk的设备驱动模型实现

对应代码:drivers/mmc/card/block.c

1、mmc bus结构

注意,这里指的是软件结构,而不是指硬件结构。
建议先参考《[mmc subsystem] mmc core(第三章)——bus模块说明
mmc结构体如下图所示

avatar
(上图摘自蜗窝科技http://www.wowotech.net/comm/mmc_framework_arch.html

从硬件上来看,每一个mmc host对应一条实际的mmc总线。
但是mmc subsystem只存在一条虚拟的mmc bus。并且mmc host并不会作为这个设备驱动总线模型的一个部分。
相应的:
* 在mmc bus上挂载的device是由mmc core根据实际mmc设备抽象出来的card设备。
例如在sd.c中,mmc_attach_sd会构造mmc_card,并且调用mmc_add_card(struct mmc_card *card)将mmc_card挂在了mmc_bus上。
而mmc_card就是mmc_bus上的device。

  • 在mmc bus上挂载的driver是在card目录下实现的card driver,用于驱动虚拟card设备、对接其他subsystem,实现其实际的功能。
    例如在block.c中,mmc_blk_init会构造一个mmc_driver,并且调用mmc_register_driver(struct mmc_driver *drv)将mmc_driver挂在了mmc_bus上。
    而mmc_driver就是mmc_bus上driver。

根据mmc_bus的匹配规则(mmc_bus_match),只要有mmc_card注册到mmc_bus上,就会匹配所有mmc_driver,也就是所有mmc_driver的probe都会执行。
然后mmc_driver就开始为mmc_card实现对应的功能了,比如mmc_blk这个mmc_driver就是为mmc_card实现块设备的功能。

2、mmc_blk驱动模型结构实现

  • 根据上述,mmc_blk驱动模型结构要包括如下两点

    • 构造对应的struct mmc_driver
    • 调用mmc_register_drive
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值