[sdcard]sdcard块设备(mmc_blk)读写流程学习笔记

更新时间:2023-06-11 19:38:01 阅读: 评论:0

[sdcard]sdcard块设备(mmc_blk)读写流程学习笔记
零、说明
前⾯介绍完sd card的协议中的初始化之后,接下来就是如何将sd card实现为⼀个块设备以及其读写流程的实现。
对应代码在drivers/mmc/card⽬录下block.c 、queue.c中。
先研究sd card作为⼀个块设备的读写流程。
在学习sdcard块读写的过程中主要围绕以下⼏个问题进⾏学习:
关于sd card 读写地址的问题?
向mmc core发起mmc读写请求的接⼝?
块设备的整个层次结构?
小学英语教学大纲从上层(IO调度层)传下来的是什么数据?
从struct request到struct mmc_async_req的处理过程?
mmc_blk_issue_rq对request的整体操作、下发流程?
⼀、关于sd card 读写地址的问题
1、sd card 读写地址
也就是CMD17\CMD18\CMD24\CMD25的参数问题。
根据SD 3.0协议:
If  partial block access is enabled in Standard Capacity Card (i.e. the CSD parameter READ_BL_PARTIAL
equals 1), the block length can be any number between 1and512 Bytes. The start address can be any
byte address in the valid address range of the card. Every block, however, shall be contained in a singleconnsus
physical card ctor.  If partial block access is disabled, only 512-Byte data length is supported.
/
/ partial block access分块访问
The High Capacity SD Memory Card only supports  512-byte block length. The start address shall be
aligned to the block boundary.
The unit of"data address"in argument is byte for Standard Capacity SD Memory Card and block (512 bytes) for
High Capacity SD Memory Card.
对于CMD17\CMD18\CMD24\CMD25的参数表⽰要读写的地址,对于SDSC card来说,数据地址的单位是字节。⽽对于SDHC和SDXC来说,数据地址的单位是512字节(以块为单位)。
(很好理解啊,SDSC 只⽀持到2GB,32bit的地址空间完全够了)。
2、代码上
根据CSD寄存器中CSD_STRUCTURE的版本来区分card的容量类型,也就是能判断其读写时的地址的单位了。
static int mmc_decode_csd(struct mmc_card *card)
{
switch (csd_struct) {
ca1:    // 说明为SDHC 或者SDXC card
mmc_card_t_blockaddr(card);    // 设置mmc的MMC_STATE_BLOCKADDR属性,也就是以块作为地址。
// CMD17 CMD18 CMD24 CMD25的参数也就是块地址。
static void mmc_blk_rw_rq_prep
在设置读写地址的时候
brq->cmd.arg = blk_rq_pos(req);
if (!mmc_card_blockaddr(card))    // 检测MMC_STATE_BLOCKADDR属性
brq->cmd.arg <<= 9;    // 没有设置的话,则是以byte作为单位的,IO调度器传下来的是以扇区为单位,也就是以512byte为单位
⼆、向mmc core发起mmc读写请求的接⼝
使⽤的是mmc_start_req接⼝。
参考《》
不阻塞等待该命令的处理流程:
(注意:并不是说调⽤这个接⼝并不会阻塞,⽽是不会为了等待当前请求处理完成⽽阻塞,但是可能会等待上⼀次请求处理完成⽽阻塞)
mmc_start_req
——》mmc_wait_for_data_req_done  // 阻塞等待上⼀次的请求处理
——》__mmc_start_data_req  // 发起异步请求
————》mrq->done = mmc_wait_data_done
————》mmc_start_request  // 实际发起请求的操作
——》返回
机制说明如下:mmc_start_req会先判断上⼀次的asycn_req是否处理完成,如果没有处理完成,则会等待其处理完成。
如果处理完成了,为当前要处理的asycn_req发起请求,但是并不会等待,⽽是直接返回。返回上⼀次正常处理的异步请求
注意:并不是说调⽤这个接⼝并不会阻塞,⽽是不会为了等待当前请求处理完成⽽阻塞,但是可能会等待上⼀次请求处理完成⽽阻塞。这样,可以利⽤等待的⼀部分时间来做其他操作。
为了⽅便理解这个函数,需要看⼀下其函数注释。
struct mmc_async_req *mmc_start_req(struct mmc_host *host,
struct mmc_async_req *areq, int *error)
*    mmc_start_req - start a non-blocking request    // 该函数⽤来发起⼀个不阻塞的请求
*    @host: MMC host to start command    // 要发起对应请求的host
中国十大留学中介*    @areq: async request to start    // 要发起的异步请求
*    @error: out parameter returns 0for success, otherwi non zero    // 返回值,返回0表⽰成功,返回⾮零表⽰失败
所以上层主要要实现对应的mmc_async_req并调⽤mmc_start_req进⾏下发。
然后对返回值mmc_async_req进⾏处理。
对于mmc_async_req来说,主要实现其mmc_request。struct mmc_request是mmc core向host controller发起命令请求的处理单位。
struct mmc_async_req {
/* active mmc request */
struct mmc_request    *mrq;
unsigned int cmd_flags; /* copied from struct request */
普京获诺贝尔奖提名
/*
* Check error status of completed mmc request.
krack
* Returns 0 if success otherwi non zero.
*/
int (*err_check) (struct mmc_card *, struct mmc_async_req *);
/* Reinrts request back to the block layer */
void (*reinrt_req) (struct mmc_async_req *);
/* update what part of request is not done (packed_fail_idx) */
int (*update_interrupted_req) (struct mmc_card *,
struct mmc_async_req *);
};
struct mmc_request {
struct mmc_command    *sbc;        /* SET_BLOCK_COUNT for multiblock */// 设置块数量的命令,怎么⽤的后续再补充
struct mmc_command    *cmd;    // 要传输的命令
struct mmc_data        *data;    // 要传输的数据
struct mmc_command    *stop;    // 结束命令,怎么⽤的后续再补充
struct completion    completion; // 完成量
void            (*done)(struct mmc_request *);/* completion function */// 传输结束后的回调函数
struct mmc_host        *host;    // 所属host
};
三、块设备的整个层次结构?
request: 描述向内核请求⼀个列表准备做队列处理。
request_queue: 描述内核申请request资源建⽴请求链表并填写BIO形成队列。
通⽤块设备层——》I/O调度层——》块设备驱动
对于sd/emmc的块设备层⽽⾔,⼜分成如下层次
mmc_blk层——》mmc_core层——》mmc_host层
⽽这⾥要学习的drivers/mmc/card/block.c则是属于mmc_blk层。
四、从上层(IO调度层)传下来的是什么数据?
块设备驱动(mmc_blk层)是通过request_queue和I/O调度层关联在⼀起的。
块设备驱动(mmc_blk层)需要从request_queue提取request进⾏处理。将request理解为IO请求,也就是IO调度层发起的请求。
所以mmc_blk需要创建对应的request_queue⼀个mmc_blk对应⼀个request_queue.!!!(mmc_blk的驱动设计的核⼼)
也就是mmc_blk层需要调⽤blk_fetch_request从request_queue提取request进⾏处理。
关联gendisk和requet_queue的地⽅
mq->queue = blk_init_queue(mmc_request_fn, lock);
md->disk->queue = md->queue.queue; // 相当于md->disk->queue = mq->queue
提取request的代码如下:
mq->queue = blk_init_queue(mmc_request_fn, lock);
mmc_request_fn是和mmc_blk的request_queue关联在⼀起的,当I/O调度层往request_queue放了⼀个数据,那么mmc_request_fn就会被执⾏
scratching
static void mmc_request_fn(struct request_queue *q)
{
wake_up_process(mq->thread);  // 唤醒mmc_queue->thread,也就是mmc_queue_thread
static int mmc_queue_thread(void *d)
{
// 不断去循环,从request_queue中获取request进⾏处理
弓箭手英文do {
struct request *req = NULL;
struct mmc_queue_req *tmp;
req = blk_fetch_request(q);
mq->mqrq_cur->req = req;        // 关联struct mmc_queue_req和struct request    ,
专升本步骤// struct mmc_queue_req是mmc_blk⾃⼰定义的⼀个queue request请求结构体bnn
// struct request    则是IO调度层定义的IO请求结构体
if (req || mq->mqrq_prev->req) {        // 直到request_queue⾥⾯已经没有request并且前⾯的request已经处理完成            mq->issue_fn(mq, req);
// 调⽤mmc_queue的issue_fn对request进⾏处理,也就是mmc_blk_issue_rq
// 在mmc_blk_alloc_req中设置了:md->queue.issue_fn = mmc_blk_issue_rq;
} el {
schedule();
}
} while (1);
}
最终调⽤了mmc_blk_issue_rq(struct mmc_queue, struct request)来对request进⾏下发。后⾯说明。
struct request的数据结构部分内容如下:
struct request {
struct list_head queuelist;      // ⽤于连接到request_queue的queue_head链表中
struct request_queue *q;  // 所属request_queue
/
* the following two fields are internal, NEVER access directly */
unsigned int __data_len;    /* total data len */// 总长度
ctor_t __ctor;        /* ctor cursor */``// 起始扇区地址
struct bio *bio;
struct bio *biotail;
struct hlist_node hash;    /* merge hash */
struct gendisk *rq_disk;
struct request *next_rq;
};
五、从struct request到struct mmc_async_req的处理过程?
1、说明:
通过上述“向mmc core发起mmc读写请求的接⼝”可以知道mmc_blk是通过struct mmc_async_req来向mmc core下发mmc请求的。
通过上述“从上层(IO调度层)传下来的是什么数据?”可以知道从IO调度层传递下来的的是struct request这个IO请求。
因此,mmc_blk肯定需要做struct request到struct mmc_async_req的转化的。
2、转化中会使⽤到的⼀些结构的关系如下:
mmc_blk⽤mmc_queue表⽰当前设备的mmc请求队列结构体。然后⽤mmc_queue_req来表⽰⼀个mmc queue request请求。 mmc_blk将request封装到mmc_queue_req中。(mmc_queue_req->request)
mmc_queue->struct mmc_queue_req *mqrq_cur则表⽰mmc_queue当前正在下发的mmc queue request请求。
mmc_queue->mmc_queue_req mqrq_cur->request,则表⽰当前IO调度层下发下来的请求。
郑州化妆培训学校mmc_queue->mmc_queue_req mqrq_cur->struct mmc_async_req mmc_active,当前要下发给mmc core的异步请求
在mmc_queue_thread获取到IO请求的时候,会设置mq->mqrq_cur->req = req,这就关联了struct mmc_queue_req和struct
request.
mmc_queue_req 中重要的三个成员:
struct mmc_queue_req {
struct request        *req;
struct mmc_blk_request    brq;
struct mmc_async_req    mmc_active;
};
struct mmc_blk_request {  // 块请求结构体
struct mmc_request    mrq;
struct mmc_command    sbc;
struct mmc_command    cmd;
struct mmc_command    stop;
struct mmc_data        data;
};
(1)先将struct request *req转化成mmc块请求struct mmc_blk_request
(2)再将请求struct mmc_blk_request的 mrq(真正的实现)设置到struct mmc_async_req mmc_active;中
所以mmc_queue->mqrq_cur->mmc_active就表⽰此时要下发的异步请求。
也就是调⽤areq = mmc_start_req(card->host, areq, (int *) &status);就可以将其发送
3、疑问:为什么要有mmc_blk_request这个中转呢。
因为mmc_async_req——》struct mmc_request中的mmc_command都是指针格式的,
struct mmc_request {
struct mmc_command    *sbc;        /* SET_BLOCK_COUNT for multiblock */
struct mmc_command    *cmd;
struct mmc_data        *data;
struct mmc_command    *stop;
struct completion    completion;
void            (*done)(struct mmc_request *);/* completion function */
struct mmc_host        *host;
};
也就是说这些mmc_command和mmc_data都是要在外部实现,因此,mmc_blk就将这些内容都封装到mmc_blk_request 中作为⼀个完
整的块请求结构体,⽅便管理。
4、转化操作如下:
在mmc_blk_rw_rq_prep中进⾏转化:
mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
struct mmc_card *card,
int disable_multi,
struct mmc_queue *mq)
{
u32 readcmd, writecmd;
struct mmc_blk_request *brq = &mqrq->brq;      // 每⼀个request对应⼀个mmc_blk_request,也对应⼀个mmc_async_req(不考虑合并request的情况下)struct request *req = mqrq->req;
struct mmc_blk_data *md = mq->data;
bool do_data_tag;
memt(brq, 0, sizeof(struct mmc_blk_request));        // 为mqrq->brq分配空间

本文发布于:2023-06-11 19:38:01,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/90/141668.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:请求   处理   读写   地址   完成   发起
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图