给NVMe设备发送一个SCSI READ_10命令

2023-10-27

###0 READ_10命令
READ_6命令只能支持块大小为512B设备的2GB范围的寻址,因此官方推荐将READ_6迁移到READ_10
READ_10具有2TB的寻址能力,对于800G的NVMe设备来说当然是极好的。其实READ_10具有更多乱七八糟的特性,但当前的nvme-scsi.c中忽略了其中大部分的特性,因此先不予考虑。

想要通过ioctl发送一个READ_10的SCSI命令,至少需要进行下文的四步操作。

###1 构建一个READ(10) Command
READ(10) Command具体定义如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0zkaCL6S-1659682619457)(https://img-blog.csdn.net/20150508152959980?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcHl5YW9lcg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center “READ (10) command”)]

由图可知,这个命令由10个char组成。需要填写opcode、LBA和Transfer Length:

	unsigned char rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
	rdCmd[2] = (unsigned char)((start_lba >> 24) & 0xff);
	rdCmd[3] = (unsigned char)((start_lba >> 16) & 0xff);
	rdCmd[4] = (unsigned char)((start_lba >> 8) & 0xff);
	rdCmd[5] = (unsigned char)(start_lba & 0xff);
	rdCmd[7] = (unsigned char)((lba_num >> 8) & 0xff);
	rdCmd[8] = (unsigned char)(lba_num & 0xff);

亦即从start_lba开始读出lba_num个逻辑块,每个逻辑块的大小一般是512B。

###2 切割读写请求
NVMe接收两种读写模式,区别在于下一步sg_io_hdriovec_count的设置。当这个位置非0时,表示使用了scatter gather方法,它向设备传递了一个请求向量。
非sg的方法可以请求传递任意大小的块。而且驱动自动分割读写请求,保证了安全性和可靠性。然而,非sg方法传递一个大缓冲区时,很容易得到ENOMEM错误。
下面这段话引用自sg.danny.cz

Scatter gather allows large buffers (previously limited to 128 KB on i386) to be used. Scatter gather is also a lot more “kernel friendly”. The original driver used a single large buffer which made it impossible to run 2 or more sg-based applications at the same time. With the new driver a buffer is reserved for each file descriptor guaranteeing that at least that buffer size will be available for each request on the file descriptor. A user may request a larger buffer size on any particular request but runs the (usually remote) risk of an out of memory (ENOMEM) error.

因此我们尽量选择scatter gather的方法,手动将一个大请求分割成很多的小块。下面具体阐述了这个过程。

NVMe的nvme-scsi.c中有一个名为nvme_trans_io的函数,它具体执行将一个标准SCSI读写命令转化为NVMe命令的工作。
这个函数对SCSI请求进行了一些限制。可以看到该函数有以下语句:

/* IO vector sizes should be multiples of block size */
if (sgl.iov_len % (1 << ns->lba_shift) != 0) {
	res = nvme_trans_completion(hdr,
			SAM_STAT_CHECK_CONDITION,
			ILLEGAL_REQUEST,
			SCSI_ASC_INVALID_PARAMETER,
			SCSI_ASCQ_CAUSE_NOT_REPORTABLE);	
	goto out;
}

因此切割的时候必须以LBA为基本单位。这类函数目前还不是很完善,最终形态应该是用NVMe命令直接承载所有的SCSI操作。

另外注意到,nvme_trans_io调用的执行读写操作的函数nvme_trans_do_nvme_io函数中不加判断地将用户传进来的sg_iovec中的请求大小放入NVMe命令:

if (hdr->iovec_count > 0) {
	unit_len = sgl.iov_len;
	unit_num_blocks = unit_len >> ns->lba_shift;
}
c.rw.length = cpu_to_le16(unit_num_blocks - 1);
nvme_sc = nvme_submit_io_cmd(dev, ns, &c, NULL);

然而,每个NVMe命令却有一个读写大小的上限。所以在分割时还需要注意,每个块不能超过这个大小。
800GB NVMe P3700的这个大小为256个LBA,也就是128KB。对于一个特定的NVMe设备,这个大小的获取在我以前的博文中提到过。

下面是具体的分割过程,使用IOVEC_ELEMS控制一次读写的数量。

// split into iovecs
rem = len * lba_size;
max_size = max_blocks * lba_size;
for (i = 0; i < IOVEC_ELEMS; ++i){
	iovec[i].iov_base = (char*)(base + i * max_size);
	iovec[i].iov_len = (rem > max_size) ? max_size : rem;
	if (rem <= max_size)
		break;
	rem -= max_size;
}
if (i >= IOVEC_ELEMS){
	printf("Too many data!\n");
	goto exit;
}

###3 构建一个sg_io_hdr
sg_io_hdr定义在sg.h中,包含了一个SCSI命令的全部信息:

typedef struct sg_io_hdr
{
	int interface_id;           /* [i] 'S' for SCSI generic (required) */
	int dxfer_direction;        /* [i] data transfer direction  */
	unsigned char cmd_len;      /* [i] SCSI command length ( <= 16 bytes) */
	unsigned char mx_sb_len;    /* [i] max length to write to sbp */
	unsigned short iovec_count; /* [i] 0 implies no scatter gather */
	unsigned int dxfer_len;     /* [i] byte count of data transfer */
	void * dxferp;              /* [i], [*io] points to data transfer memory
											  or scatter gather list */
	unsigned char * cmdp;       /* [i], [*i] points to command to perform */
	unsigned char * sbp;        /* [i], [*o] points to sense_buffer memory */
	unsigned int timeout;       /* [i] MAX_UINT->no timeout (unit: millisec) */
	unsigned int flags;         /* [i] 0 -> default, see SG_FLAG... */
	int pack_id;                /* [i->o] unused internally (normally) */
	void * usr_ptr;             /* [i->o] unused internally */
	unsigned char status;       /* [o] scsi status */
	unsigned char masked_status;/* [o] shifted, masked scsi status */
	unsigned char msg_status;   /* [o] messaging level data (optional) */
	unsigned char sb_len_wr;    /* [o] byte count actually written to sbp */
	unsigned short host_status; /* [o] errors from host adapter */
	unsigned short driver_status;/* [o] errors from software driver */
	int resid;                  /* [o] dxfer_len - actual_transferred */
	unsigned int duration;      /* [o] time taken by cmd (unit: millisec) */
	unsigned int info;          /* [o] auxiliary information */
} sg_io_hdr_t;  /* 64 bytes long (on i386) */

基本上需要填写的就是下面这些啦:

interface_id = 'S';
cmd_len = sizeof(rdCmd);
cmdp = rdCmd;
dxfer_direction = SG_DXFER_FROM_DEV;
dxfer_len = lba_size * len;
iovec_count = num_of_iovec;
dxferp = iovec;

rdCmd和iovec分别就是上面填好的东西啦。

###4 将请求发出去!
一言以蔽之:

err = ioctl(fd, SG_IO, &io_hdr);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

给NVMe设备发送一个SCSI READ_10命令 的相关文章

随机推荐

  • 基于Stomp协议的时间通知机制

    问题导入 系统运行过程中出现了由主持人的操作推动会议流程的场景 如何将主持人的命令通过服务器同步到每个人的设备上 问题分析 在当前的系统设计中使用了C S架构 基本上所有的请求都是终端通过http协议向服务器提出的 而服务器没有办法向终端主
  • pygame飞机大战小游戏(python大作业)

    一 项目背景 python大作业 在查看了老师给的链接发现教学视频不完整 所以借用了同学的 Python编程 从入门到实践 中的一个项目 学习模仿 二 游戏具体介绍 这是一款由辉辉亲自打造的太空对战小游戏 游戏背景 在广袤无垠的太空里有一群
  • 图的遍历(完整代码)

    代码实现功能 1 利用图的邻接矩阵构造并输出图 2 实现图的深度优先搜索遍历 3 实现图的广度优先搜索遍历 include
  • 【支持M1】MacDroid for Mac:Mac和Android安卓设备数据互通

    Mac和Android组合始终存在的唯一问题是无法在这些设备之间足够快地传输数据 但是MacDroid for mac填补了这一空白 MacDroid mac版是Macos上一款安卓手机数据传输助手 MacDroid mac下载支持Mac和
  • 码蹄集 ---- 供水管线 kruskal算法

    供水管线 kruskal算法 克鲁斯卡尔算法 最小生成树算法 应用场景 从连通图中找出最小生成树 和实际相结合的有 水管共线 公交车站路线图 城市间修路等 算法主要思想 将连通网中所有的边按照权值大小做升序排序 从权值最小的边开始选择 只要
  • C语言调用libusb访问USB驱动

    目录 一 环境搭建 1 下载库文件 2 解压 3 配置VS工程 3 1 头文件的配置
  • SyntaxError: unexpected EOF while parsing

    SyntaxError unexpected EOF while parsing 这是典型的没有验证函数参数是否有效 原因是eval str 的字符串为空 你可以运行如下代码 观察输出 try print eval except Excep
  • 六句话给出 Synchronized 和 Lock 的区别

    1 Synchronized 内置的 Java 关键字 Lock 是一个 Java 类 2 Synchronized 无法判断获取锁的状态 Lock 可以判断是否获取到了锁 3 Synchronized 会自动释放锁 Lock 必须要手动释
  • Windows下Anaconda3下载安装详细步骤

    第一步 去官网下载Anaconda Individual Editionhttps www anaconda com products individual 第二步 点击Download 在安装之前 要先安装python的版本 这里我先安装
  • C++ 实验8 继承

    编写一个学生和教师数据输入和显示程序 学生数据有编号 姓名 班级和成绩 教师数据有编号 姓名 职称和部门 要求将编号 姓名输入和显示设计成一个类person 并作为学生类student和教师类teacher的基类 类图如下 代码如下 头文件
  • Win10笔记本(机械革命)亮度调节快捷键失效-已解决

    Win10笔记本 机械革命 亮度调节快捷键失效 已解决 1 确定你已经安装了核心显卡驱动 驱动精灵检查一下 2 右击此电脑 管理 系统工具 设备管理器 监视器 单击展开 卸载dpms 卸载Generic Monitor 选中删除相关驱动 3
  • iText包对每页pdf文件加水印

    https ishare iask sina com cn f 31zwqlKmIwM html
  • 用户编写的python程序、无需修改就可以_python的笔记(一)

    Python的基本特点一种动态解释型的编程语言 规范的代码 Python 采用强制缩进的方式使得代码具有极佳的可读性 高级语言特性 封装内存管理等 可移植性 程序如果避免使用依赖于系统的特性 那么无需修改就可以在任何平台上运行 解释性 直接
  • 带你入门TypeScript

    一 为何学习TS 1 TypeScript 在社区的流行度越来越高 它非常适用于一些大型项目 也非常适用于一些基础库 极大地帮助我们提升了开发效率和体验 2 TypeScript 可以编译出纯净 简洁的 JavaScript 代码 并且可以
  • python画玫瑰图_python windrose(风玫瑰图)

    conda install c https conda anaconda org conda forge windrose b 用pip install windrose可以成功 但是安装的路径 python找不到 from windros
  • 多表联查优化

    多表联查优化我总结有以下几点 优化sql语句 索引优化 反范式设计 业务代码优化 使用缓存 优化sql语句 sql性能分析 查看执行频次 查看执行频次 select insert delete update shwo global sess
  • Python应该怎么学,如何系统地自学Python?

    这是一份kaggle上的银行的数据集 研究该数据集可以预测客户是否认购定期存款y 这里包含20个特征 1 分析框架 2 数据读取 数据清洗 导入相关包 import numpy as np import pandas as pd 读取数据
  • 厉害了,用 Java 也能实现图片识别!

    点击上方 Java基基 选择 设为星标 做积极的人 而不是积极废人 源码精品专栏 原创 Java 2020 超神之路 很肝 中文详细注释的开源项目 RPC 框架 Dubbo 源码解析 网络应用框架 Netty 源码解析 消息中间件 Rock
  • VS2010 编译 sqlite3 生成动态库和链接库

    如果想以dll的方式使用sqlite而新建空的dll工程 添加sqlite源文件 会发现能生成dll 但缺乏lib函数信息映射库 单独使用dll文件是比较麻烦的 而网上多数做法是通过lib exe手动生成lib 这当然不是我想要的 结合几篇
  • 给NVMe设备发送一个SCSI READ_10命令

    0 READ 10命令 READ 6命令只能支持块大小为512B设备的2GB范围的寻址 因此官方推荐将READ 6迁移到READ 10 READ 10具有2TB的寻址能力 对于800G的NVMe设备来说当然是极好的 其实READ 10具有更