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驱动分析相关推荐

  1. Linux nvme驱动分析之块设备层

    作者 QQ群:852283276 微信:arm80x86 微信公众号:青儿创客基地 B站:主页 https://space.bilibili.com/208826118 参考 Product Docu ...

  2. linux串口驱动分析

    linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作 ...

  3. Linux spi驱动分析(四)----SPI设备驱动(W25Q32BV)

    一.W25Q32BV芯片简介 W25X是一系列SPI接口Flash芯片的简称,它采用SPI接口和CPU通信,本文使用的W25Q32BV容量为32M,具体特性如下: 1.1.基本特性 该芯片最大支持10 ...

  4. Linux spi驱动分析----SPI设备驱动(W25Q32BV)

    转载地址:http://blog.chinaunix.net/uid-25445243-id-4026974.html 一.W25Q32BV芯片简介 W25X是一系列SPI接口Flash芯片的简称,它 ...

  5. Linux网卡驱动分析之RTL8139(五)

    Linux网卡驱动分析之RTL8139(五) deliver_skb(dev.c) // 该函数就是调用个协议的接收函数处理该skb 包,进入第三层网络层处理 static __inline__ in ...

  6. linux pinctrl驱动分析

    linux pinctrl驱动分析 altas200模块 准备 设备树节点 pinctrl驱动分析 pcs_probe函数 pcs_allocate_pin_table函数 pcs_add_pin函数 ...

  7. linux 网卡驱动分析,LINUX_网卡驱动分析

    LINUX_网卡驱动分析 (36页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 19.9 积分 Linux DM9000网卡驱动程序完全分析说明仁 本文分 ...

  8. linux 网卡驱动分析,基于linux下网卡驱动分析及实现技术研究

    摘    要 Linux技术是当前计算机技术中最大的一个热点,在我国以及全世界得到了迅猛的发展,被广泛的应用于嵌入式系统.服务器.网络系统.安全等领域.从而使得掌握在 Linux环境下的开发技术,成为 ...

  9. linux 触摸屏驱动分析

    mini2440驱动分析系列之 ---------------------------------------Mini2440触摸屏程序分析 By JeefJiang July,8th,2009 这是 ...

最新文章

  1. 手指贴个“创可贴”,你睡觉都能发电
  2. 使用hyper-v、CentOS学习Linux基本概念和命令
  3. 什么是脱离文档流?什么是文档流?
  4. 这些花式降薪的招数,总有些你想不到的
  5. VTK:IO之HDRReader
  6. [Android]AndroidBucket增加碎片SubLayout功能及AISubLayout的注解支持
  7. 苹果CarPlay新功能上线,老司机们更方便了
  8. u-boot移植随笔:一些内存地址的研究(gd_t和bd_t结构体)
  9. angularjs post返回html_Python 爬虫网页解析工具lxml.html(二)
  10. javascript调试
  11. SQLite3数据库
  12. 怦然心动(Flipped)-2
  13. 手机详情 html代码生成器,dede源码最新版手机移动端静态生成模块插件
  14. Windy数 数位DP
  15. 【逻辑推理系列】海盗分金模型分析
  16. openssl version mismatch. built against 30000010, you have 30100000
  17. TortoiseSVN安装最新版,设置中文;并修改比对工具为BeyondCompare
  18. 概率论与数理统计系列笔记之第四章——大数定理与中心极限定理
  19. 奇富科技语音论文入选国际顶会INTERSPEECH 2023
  20. 基本算法练习——投篮游戏

热门文章

  1. Hihocoder #1479 : 三等分 树形DP
  2. 企立方:拼多多场景推广和搜索推广的区别
  3. vue部署到线上时,Loading chunk xxx failed 问题
  4. 安卓系统属性 ro、persist、net
  5. 安装搜狗输入法后如何禁止自动流氓安装搜狗浏览器等软件?
  6. 定位四步骤和八方法--《可以量化的…
  7. 基于Tensorflow实现一个Transformer翻译器
  8. Olist Store电商数据分析
  9. 以Vivado synthesis支持的Verilog结构来学习 Verilog语句可综合性
  10. 游戏建模学习经验分享:培训机构教你的是学习套路,美术水平决定你的建模高度