SCSI数据缓冲区组织成聚散列表的形式。Linux内核中表示聚散列表的基本数据结构是scatterlist,虽然名字中有list,但它只对应一个内存缓冲区,聚散列表就是多个scatterlist的组合。这种组合是链表+数组的结合。这是因为他使用的内存以页面为基本单位分配,每个页面相当于一个scatterlist。每个scatterlist以链表方式组织起来。

 /*
* Function: scsi_init_io()
*
* Purpose: SCSI I/O initialize function.
*
* Arguments: cmd - Command descriptor we wish to initialize
*
* Returns: 0 on success
* BLKPREP_DEFER if the failure is retryable
* BLKPREP_KILL if the failure is fatal
*/
int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask)
{
struct request *rq = cmd->request; // 初始化sg列表
int error = scsi_init_sgtable(rq, &cmd->sdb, gfp_mask);
if (error)
goto err_exit; // 如果是双向请求,则为关联的request分配SCSI数据缓冲区,用于另一方向的数据传输,然后调用scsi_init_sgtable分配聚散列表,最后进行映射
if (blk_bidi_rq(rq)) {
struct scsi_data_buffer *bidi_sdb = kmem_cache_zalloc(
scsi_sdb_cache, GFP_ATOMIC);
if (!bidi_sdb) {
error = BLKPREP_DEFER;
goto err_exit;
} rq->next_rq->special = bidi_sdb;
error = scsi_init_sgtable(rq->next_rq, bidi_sdb, GFP_ATOMIC);
if (error)
goto err_exit;
} /*
* 如果是完整性请求,即原始bio中带有完整性载荷,则调用blk_rq_count_integrity_sg计算完整性数据段的数目
* 然后调用scsi_alloc_sgtable分配聚散列表,再调用blk_rq_map_integrity_sg将完整性数据映射到这个聚散列表,最后更新聚散列表已映射的项数
* 实际上,完整性请求的处理过程概括了scsi_init_sgtable的操作流程,它实际上是这个过程的一个封装
* 即调用scsi_alloc_sgtable分配指定数据数据段的聚散列表,然后调用blk_rq_map_sg进行映射,最后更新列表已映射的项数
*/
if (blk_integrity_rq(rq)) {
struct scsi_data_buffer *prot_sdb = cmd->prot_sdb;
int ivecs, count; BUG_ON(prot_sdb == NULL);
ivecs = blk_rq_count_integrity_sg(rq->q, rq->bio); if (scsi_alloc_sgtable(prot_sdb, ivecs, gfp_mask)) {
error = BLKPREP_DEFER;
goto err_exit;
} count = blk_rq_map_integrity_sg(rq->q, rq->bio,
prot_sdb->table.sgl);
BUG_ON(unlikely(count > ivecs));
BUG_ON(unlikely(count > queue_max_integrity_segments(rq->q))); cmd->prot_sdb = prot_sdb;
cmd->prot_sdb->table.nents = count;
} return BLKPREP_OK ; err_exit:
scsi_release_buffers(cmd);
cmd->request->special = NULL;
scsi_put_command(cmd);
return error;
}

blk_rq_map_sg函数如下:

 /*
* map a request to scatterlist, return number of sg entries setup. Caller
* must make sure sg can hold rq->nr_phys_segments entries
*/
int blk_rq_map_sg(struct request_queue *q, struct request *rq,
struct scatterlist *sglist)
{
struct bio_vec *bvec, *bvprv;
struct req_iterator iter;
struct scatterlist *sg;
int nsegs, cluster; nsegs = ;
cluster = blk_queue_cluster(q); /*
* for each bio in rq
*/
bvprv = NULL;
sg = NULL;
rq_for_each_segment(bvec, rq, iter) {
__blk_segment_map_sg(q, bvec, sglist, &bvprv, &sg,
&nsegs, &cluster);
} /* segments in rq */ if (unlikely(rq->cmd_flags & REQ_COPY_USER) &&
(blk_rq_bytes(rq) & q->dma_pad_mask)) {
unsigned int pad_len =
(q->dma_pad_mask & ~blk_rq_bytes(rq)) + ; sg->length += pad_len;
rq->extra_len += pad_len;
} if (q->dma_drain_size && q->dma_drain_needed(rq)) {
if (rq->cmd_flags & REQ_WRITE)
memset(q->dma_drain_buffer, , q->dma_drain_size); sg->page_link &= ~0x02;
sg = sg_next(sg);
sg_set_page(sg, virt_to_page(q->dma_drain_buffer),
q->dma_drain_size,
((unsigned long)q->dma_drain_buffer) &
(PAGE_SIZE - ));
nsegs++;
rq->extra_len += q->dma_drain_size;
} if (sg)
sg_mark_end(sg); return nsegs;
}

在bio处理过程中的两次合并,第一个合并由IO调度算法负责,它将在磁盘扇区上连续的请求合并到一个request中。第二次合并出现在SCSI策略例程,如果低层驱动支持,则进而将内存中连续的段合并为聚散列表中的一项,如下图,两个bio(每个bio有两段请求)在经过两个合并之后,聚散列表最终有三个项目。

Linux3.10.0块IO子系统流程(5)-- 为SCSI命令准备聚散列表的更多相关文章

  1. Linux3.10.0块IO子系统流程(0)-- 块IO子系统概述

    前言:这个系列主要是记录自己学习Linux块IO子系统的过程,其中代码分析皆基于Linux3.10.0版本,如有描述错误或不妥之处,敬请指出! 参考书籍:存储技术原理分析--基于Linux 2.6内核 ...

  2. Linux3.10.0块IO子系统流程(3)-- SCSI策略例程

    很长时间以来,Linux块设备使用了一种称为“蓄流/泄流”(plugging/unplugging)的技术来改进吞吐率.简单而言,这种工作方式类似浴盆排水系统的塞子.当IO被提交时,它被储存在一个队列 ...

  3. Linux3.10.0块IO子系统流程(7)-- 请求处理完成

    和提交请求相反,完成请求的过程是从低层驱动开始的.请求处理完成分为两个部分:上半部和下半部.开始时,请求处理完成总是处在中断上下文,在这里的主要任务是将已完成的请求放到某个队列中,然后引发软终端让中断 ...

  4. Linux3.10.0块IO子系统流程(2)-- 构造、排序、合并请求

    Linux块设备可以分为三类.分别针对顺序访问物理设备.随机访问物理设备和逻辑设备(即“栈式设备”)   类型 make_request_fn request_fn 备注 SCSI 设备等 从bio构 ...

  5. Linux3.10.0块IO子系统流程(6)-- 派发SCSI命令到低层驱动

    在SCSI策略例程中最后调用scsi_dispatch_cmd将SCSI命令描述符派发给低层驱动进行处理 /** * scsi_dispatch_command - Dispatch a comman ...

  6. Linux3.10.0块IO子系统流程(4)-- 为请求构造SCSI命令

    首先来看scsi_prep_fn int scsi_prep_fn(struct request_queue *q, struct request *req) { struct scsi_device ...

  7. Linux3.10.0块IO子系统流程(1)-- 上层提交请求

    Linux通用块层提供给上层的接口函数是submit_bio.上层在构造好bio之后,调用submit_bio提交给通用块层处理.   submit_bio函数如下:   void submit_bi ...

  8. DPA 9.1.85 升级到DPA 10.0.352流程

    SolarWinds DPA的升级其实是一件非常简单的事情,这里介绍一下从DPA 9.1.95升级到 DPA 10.0.352版本的流程.为什么要升级呢? DPA给用户发的邮件已经写的非常清楚了(如下 ...

  9. 【转】linux IO子系统和文件系统读写流程

    原文地址:linux IO子系统和文件系统读写流程 我们含有分析的,是基于2.6.32及其后的内核. 我们在linux上总是要保存数据,数据要么保存在文件系统里(如ext3),要么就保存在裸设备里.我 ...

随机推荐

  1. Filte过滤器

    过滤器 , 其实就是对客户端发出来的请求进行过滤. 浏览器发出, 然后服务器派servlet处理.  在中间就可以过滤, 其实过滤器起到的是拦截的作用.生活中的过滤器例如:门禁,你有门卡才能过去,没有 ...

  2. 20165215 2017-2018-2《Java程序设计》课程总结

    20165215 2017-2018-2<Java程序设计>课程总结 一.每周作业链接汇总 预备作业1:我期望的师生关系:令我记忆深刻的老师,期望的师生关系,本学期的学习规划. 预备作业二 ...

  3. 使用java进行 AES 加密 解密?

    百度百科是这样定义的: 高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准.这个标 ...

  4. Grunt: 拼接代码,js丑化(压缩),css压缩,html压缩,观察文件,拷贝文件,删除文件,压缩文件

    准备工作 grunt 基于nodeJs所以 nodeJs需要的基础配置都需要安装 1.Grunt 安装 npm install -g grunt-cli 这是全局安装 2.在当前文件下npm init ...

  5. mysql 存储session

    https://www.cnblogs.com/cndavidwang/p/3930619.html

  6. DevOps的故事(如何整合开发和运维?)

    在一个与我们平行的世界中,有一个软件开发公司.这个公司所做的产品用户量近期增长的十分迅猛,但是令CTO头疼的是公司的两大部门:开发部和运维部近期也是“掐”得厉害.为解决这个问题,CTO决定倒入现在十分 ...

  7. 【题解】Luogu P2766 最长不下降子序列问题

    原题传送门 实际还是比较套路的建图 先暴力dp一下反正数据很小 第一小问的答案即珂以求出数列的最长不下降子序列的长度s 考虑第二问如何做: 将每个点拆点 从前向后连一条流量为1的边 如果以它为终点的最 ...

  8. 5分钟学会vue中的路由守卫(导航守卫)

    在项目开发中每一次路由的切换或者页面的刷新都需要判断用户是否已经登录,前端可以判断,后端也会进行判断的,我们前端最好也进行判断. vue-router提供了导航钩子:全局前置导航钩子 beforeEa ...

  9. servlet登录界面进行用户名和密码验证

    一.建立LoginServlet项目并建立如下目录 二.在Login.html中编写登录界面代码 三.在css文件中新建login.css文件 四.在src文件中添加LoginServlet.java ...

  10. Docker for windows10 配置阿里云镜像

    到官网下载 并且 安装 Docker for windows  (注意 官方要求 windows10 是企业版才行  天朝你懂的 ) 关于 Docker for windows  要求有  带有 hy ...