Linux nvme驱动分析
Ioctl流程:
static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
unsigned long arg)
{
struct nvme_ns *ns = bdev->bd_disk->private_data;
switch (cmd) {
case NVME_IOCTL_ID:
return ns->ns_id;
case NVME_IOCTL_ADMIN_CMD:
return nvme_user_admin_cmd(ns->dev, (void __user *)arg);
case NVME_IOCTL_SUBMIT_IO:
return nvme_submit_io(ns, (void __user *)arg);
default:
return -ENOTTY;
}
}
static int nvme_user_admin_cmd(struct nvme_dev *dev,
struct nvme_admin_cmd __user *ucmd)
{
struct nvme_admin_cmd cmd;
struct nvme_command c;
int status, length;
struct nvme_iod *uninitialized_var(iod);
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
//从用户空间复制给定的命令信息,这个命令比64字节长,包含其他信息
if (copy_from_user(&cmd, ucmd, sizeof(cmd)))
return -EFAULT;
memset(&c, 0, sizeof(c));
c.common.opcode = cmd.opcode;
c.common.flags = cmd.flags;
c.common.nsid = cpu_to_le32(cmd.nsid);
c.common.cdw2[0] = cpu_to_le32(cmd.cdw2);
c.common.cdw2[1] = cpu_to_le32(cmd.cdw3);
c.common.cdw10[0] = cpu_to_le32(cmd.cdw10);
c.common.cdw10[1] = cpu_to_le32(cmd.cdw11);
c.common.cdw10[2] = cpu_to_le32(cmd.cdw12);
c.common.cdw10[3] = cpu_to_le32(cmd.cdw13);
c.common.cdw10[4] = cpu_to_le32(cmd.cdw14);
c.common.cdw10[5] = cpu_to_le32(cmd.cdw15);
// 获取需要传输的数据长度
length = cmd.data_len;
if (cmd.data_len) {
// 将用户空间的内存地址映射成DMA总线地址
iod = nvme_map_user_pages(dev, cmd.opcode & 1, cmd.addr,
length);
if (IS_ERR(iod))
return PTR_ERR(iod);
// 根据需要传输数据的长度构造nvme命令的prp指针或prp list
length = nvme_setup_prps(dev, &c.common, iod, length,
GFP_KERNEL);
}
if (length != cmd.data_len)
status = -ENOMEM;
else
status = nvme_submit_admin_cmd(dev, &c, &cmd.result);
// 如果有数据传输,取消对用户空间的映射,释放iod结构空间
if (cmd.data_len) {
nvme_unmap_user_pages(dev, cmd.opcode & 1, iod);
nvme_free_iod(dev, iod);
}
// 将结果返回给用户
if (!status && copy_to_user(&ucmd->result, &cmd.result,
sizeof(cmd.result)))
status = -EFAULT;
return status;
}
/* 完成将user page映射成dma总线地址工作 */
static struct nvme_iod *nvme_map_user_pages(struct nvme_dev *dev, int write,
unsigned long addr, unsigned length)
{
int i, err, count, nents, offset;
struct scatterlist *sg;
struct page **pages;
struct nvme_iod *iod;
if (addr & 3)
return ERR_PTR(-EINVAL);
if (!length)
return ERR_PTR(-EINVAL);
offset = offset_in_page(addr);
count = DIV_ROUND_UP(offset + length, PAGE_SIZE);
/*
1.根据offset + length计算出这块空间对应多少个page,因为DMA传输是以page为单位的。
2.分配page结构,用于描述这段地址空间。
*/
pages = kcalloc(count, sizeof(*pages), GFP_KERNEL);
if (!pages)
return ERR_PTR(-ENOMEM);
/* 调用get_user_pages_fast将addr对应的用户空间区域赋值给这些page结构并将其标记为常驻内存 */
err = get_user_pages_fast(addr, count, 1, pages);
if (err < count) {
count = err;
err = -EFAULT;
goto put_pages;
}
/* 分配iod结构,io description,用于描述执行io所需要信息 */
iod = nvme_alloc_iod(count, length, GFP_KERNEL);
/* 使用iod中的sg list */
sg = iod->sg;
sg_init_table(sg, count);
for (i = 0; i < count; i++) {
sg_set_page(&sg[i], pages[i],
min_t(int, length, PAGE_SIZE - offset), offset);
length -= (PAGE_SIZE - offset);
offset = 0;
}
sg_mark_end(&sg[i - 1]);
iod->nents = count; /* 记录一共有多少个page需要传输 */
err = -ENOMEM;
/* 将sg中包含的page地址映射成dma总线地址,设备需要总线地址才能访问这些page */
nents = dma_map_sg(&dev->pci_dev->dev, sg, count,
write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
if (!nents)
goto free_iod;
/* 释放page结构,返回iod */
kfree(pages);
return iod;
free_iod:
kfree(iod);
put_pages:
for (i = 0; i < count; i++)
put_page(pages[i]);
kfree(pages);
return ERR_PTR(err);
}
Linux nvme驱动分析相关推荐
- Linux nvme驱动分析之块设备层
作者 QQ群:852283276 微信:arm80x86 微信公众号:青儿创客基地 B站:主页 https://space.bilibili.com/208826118 参考 Product Docu ...
- linux串口驱动分析
linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作 ...
- Linux spi驱动分析(四)----SPI设备驱动(W25Q32BV)
一.W25Q32BV芯片简介 W25X是一系列SPI接口Flash芯片的简称,它采用SPI接口和CPU通信,本文使用的W25Q32BV容量为32M,具体特性如下: 1.1.基本特性 该芯片最大支持10 ...
- Linux spi驱动分析----SPI设备驱动(W25Q32BV)
转载地址:http://blog.chinaunix.net/uid-25445243-id-4026974.html 一.W25Q32BV芯片简介 W25X是一系列SPI接口Flash芯片的简称,它 ...
- Linux网卡驱动分析之RTL8139(五)
Linux网卡驱动分析之RTL8139(五) deliver_skb(dev.c) // 该函数就是调用个协议的接收函数处理该skb 包,进入第三层网络层处理 static __inline__ in ...
- linux pinctrl驱动分析
linux pinctrl驱动分析 altas200模块 准备 设备树节点 pinctrl驱动分析 pcs_probe函数 pcs_allocate_pin_table函数 pcs_add_pin函数 ...
- linux 网卡驱动分析,LINUX_网卡驱动分析
LINUX_网卡驱动分析 (36页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 19.9 积分 Linux DM9000网卡驱动程序完全分析说明仁 本文分 ...
- linux 网卡驱动分析,基于linux下网卡驱动分析及实现技术研究
摘 要 Linux技术是当前计算机技术中最大的一个热点,在我国以及全世界得到了迅猛的发展,被广泛的应用于嵌入式系统.服务器.网络系统.安全等领域.从而使得掌握在 Linux环境下的开发技术,成为 ...
- linux 触摸屏驱动分析
mini2440驱动分析系列之 ---------------------------------------Mini2440触摸屏程序分析 By JeefJiang July,8th,2009 这是 ...
最新文章
- 手指贴个“创可贴”,你睡觉都能发电
- 使用hyper-v、CentOS学习Linux基本概念和命令
- 什么是脱离文档流?什么是文档流?
- 这些花式降薪的招数,总有些你想不到的
- VTK:IO之HDRReader
- [Android]AndroidBucket增加碎片SubLayout功能及AISubLayout的注解支持
- 苹果CarPlay新功能上线,老司机们更方便了
- u-boot移植随笔:一些内存地址的研究(gd_t和bd_t结构体)
- angularjs post返回html_Python 爬虫网页解析工具lxml.html(二)
- javascript调试
- SQLite3数据库
- 怦然心动(Flipped)-2
- 手机详情 html代码生成器,dede源码最新版手机移动端静态生成模块插件
- Windy数 数位DP
- 【逻辑推理系列】海盗分金模型分析
- openssl version mismatch. built against 30000010, you have 30100000
- TortoiseSVN安装最新版,设置中文;并修改比对工具为BeyondCompare
- 概率论与数理统计系列笔记之第四章——大数定理与中心极限定理
- 奇富科技语音论文入选国际顶会INTERSPEECH 2023
- 基本算法练习——投篮游戏