linux 3.13版本nvme驱动阅读记录四

2023-11-08 18:12

本文主要是介绍linux 3.13版本nvme驱动阅读记录四,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这里记录下在nvme_probe函数调用misc_register函数的总结。

static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{//...
create_cdev://利用miscdev结构体提供一些字符设备的操作(回调函数),用户空间可以下发一些nvme的命令等scnprintf(dev->name, sizeof(dev->name), "nvme%d", dev->instance);dev->miscdev.minor = MISC_DYNAMIC_MINOR;dev->miscdev.parent = &pdev->dev;dev->miscdev.name = dev->name;dev->miscdev.fops = &nvme_dev_fops;result = misc_register(&dev->miscdev); //将字符设备那一堆函数在汇总在一起if (result)goto remove;kref_init(&dev->kref);//设备引用计数初始化,值为1return 0;//...
}

nvme_dev_fops

static int nvme_dev_open(struct inode *inode, struct file *f)
{struct nvme_dev *dev = container_of(f->private_data, struct nvme_dev, miscdev);kref_get(&dev->kref);f->private_data = dev;return 0;
}static int nvme_dev_release(struct inode *inode, struct file *f)
{struct nvme_dev *dev = f->private_data;kref_put(&dev->kref, nvme_free_dev);return 0;
}static long nvme_dev_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
{struct nvme_dev *dev = f->private_data;switch (cmd) {case NVME_IOCTL_ADMIN_CMD:return nvme_user_admin_cmd(dev, (void __user *)arg);default:return -ENOTTY;}
}static const struct file_operations nvme_dev_fops = {.owner		= THIS_MODULE,.open		= nvme_dev_open,.release	= nvme_dev_release,.unlocked_ioctl	= nvme_dev_ioctl,
};

nvme_user_admin_cmd

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);unsigned timeout;if (!capable(CAP_SYS_ADMIN))//sudoreturn -EACCES;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) {/*将用户态传下来的地址,转成page结构体*/iod = nvme_map_user_pages(dev, cmd.opcode & 1, cmd.addr, length);if (IS_ERR(iod))return PTR_ERR(iod);length = nvme_setup_prps(dev, &c.common, iod, length, GFP_KERNEL);}/*timeout:发命令的超时时间msecs_to_jiffies:ms转jiffies*/timeout = cmd.timeout_ms ? msecs_to_jiffies(cmd.timeout_ms) : ADMIN_TIMEOUT;if (length != cmd.data_len)status = -ENOMEM;elsestatus = nvme_submit_sync_cmd(dev->queues[0], &c, &cmd.result, timeout);/*解除映射*/if (cmd.data_len) {nvme_unmap_user_pages(dev, cmd.opcode & 1, iod);nvme_free_iod(dev, iod);}/*将命令的执行结果返回给用户态*/if ((status >= 0) && copy_to_user(&ucmd->result, &cmd.result, sizeof(cmd.result)))status = -EFAULT;return status;
}

nvme_map_user_pages和nvme_unmap_user_pages

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) //地址是4字节对齐。因为struct scatterlist结构体的page_link的低两位有其它用途了return ERR_PTR(-EINVAL);if (!length || length > INT_MAX - PAGE_SIZE)return ERR_PTR(-EINVAL);offset = offset_in_page(addr);//0x12345678 -> 0x678 = 1656, 假设length=3000//计算需要几个page, offset(起始处)是struct page的起始处,所以是offset+length计算的需要几个pagecount = DIV_ROUND_UP(offset + length, PAGE_SIZE); //((offset + length) / PAGE_SIZE) + 1//申请内存pages = kcalloc(count, sizeof(*pages), GFP_KERNEL);if (!pages)return ERR_PTR(-ENOMEM);/*addr可能不是4k对齐的,后续可能需要offset*/err = get_user_pages_fast(addr, count, 1, pages);//pin 用户态虚拟地址,返回对应的struct page结构if (err < count) {count = err;err = -EFAULT;goto put_pages;}iod = nvme_alloc_iod(count, length, GFP_KERNEL);sg = iod->sg;sg_init_table(sg, count);for (i = 0; i < count; i++) {/*page->|-------||		||		|offset->|------ ||-------|*///len offset第一次 len = 2440, offset=1656,第二次 len=560,offset=0 刚好符合length=3000sg_set_page(&sg[i], pages[i], min_t(unsigned, length, PAGE_SIZE - offset), offset);length -= (PAGE_SIZE - offset);offset = 0;}sg_mark_end(&sg[i - 1]);iod->nents = count;err = -ENOMEM;//将struct scatterlist记录的每一个sge内核态虚拟地址映射为dma地址nents = dma_map_sg(&dev->pci_dev->dev, sg, count, write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);if (!nents)goto free_iod;kfree(pages);return iod;
free_iod:kfree(iod);
put_pages:for (i = 0; i < count; i++)put_page(pages[i]);//addr对应的struct pagekfree(pages);return ERR_PTR(err);
}void nvme_unmap_user_pages(struct nvme_dev *dev, int write, struct nvme_iod *iod)
{int i;dma_unmap_sg(&dev->pci_dev->dev, iod->sg, iod->nents, write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);for (i = 0; i < iod->nents; i++)put_page(sg_page(&iod->sg[i]));
}

调用完以后在dev目录下也可以看到相关的设备节点了。

这篇关于linux 3.13版本nvme驱动阅读记录四的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/371628

相关文章

Linux中修改Apache HTTP Server(httpd)默认端口的完整指南

《Linux中修改ApacheHTTPServer(httpd)默认端口的完整指南》ApacheHTTPServer(简称httpd)是Linux系统中最常用的Web服务器之一,本文将详细介绍如何... 目录一、修改 httpd 默认端口的步骤1. 查找 httpd 配置文件路径2. 编辑配置文件3. 保存

Linux使用scp进行远程目录文件复制的详细步骤和示例

《Linux使用scp进行远程目录文件复制的详细步骤和示例》在Linux系统中,scp(安全复制协议)是一个使用SSH(安全外壳协议)进行文件和目录安全传输的命令,它允许在远程主机之间复制文件和目录,... 目录1. 什么是scp?2. 语法3. 示例示例 1: 复制本地目录到远程主机示例 2: 复制远程主

conda安装GPU版pytorch默认却是cpu版本

《conda安装GPU版pytorch默认却是cpu版本》本文主要介绍了遇到Conda安装PyTorchGPU版本却默认安装CPU的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录一、问题描述二、网上解决方案罗列【此节为反面方案罗列!!!】三、发现的根本原因[独家]3.1 p

Linux基础命令@grep、wc、管道符的使用详解

《Linux基础命令@grep、wc、管道符的使用详解》:本文主要介绍Linux基础命令@grep、wc、管道符的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录grep概念语法作用演示一演示二演示三,带选项 -nwc概念语法作用wc,不带选项-c,统计字节数-

Redis指南及6.2.x版本安装过程

《Redis指南及6.2.x版本安装过程》Redis是完全开源免费的,遵守BSD协议,是一个高性能(NOSQL)的key-value数据库,Redis是一个开源的使用ANSIC语言编写、支持网络、... 目录概述Redis特点Redis应用场景缓存缓存分布式会话分布式锁社交网络最新列表Redis各版本介绍旧

IIS 7.0 及更高版本中的 FTP 状态代码

《IIS7.0及更高版本中的FTP状态代码》本文介绍IIS7.0中的FTP状态代码,方便大家在使用iis中发现ftp的问题... 简介尝试使用 FTP 访问运行 Internet Information Services (IIS) 7.0 或更高版本的服务器上的内容时,IIS 将返回指示响应状态的数字代

apache的commons-pool2原理与使用实践记录

《apache的commons-pool2原理与使用实践记录》ApacheCommonsPool2是一个高效的对象池化框架,通过复用昂贵资源(如数据库连接、线程、网络连接)优化系统性能,这篇文章主... 目录一、核心原理与组件二、使用步骤详解(以数据库连接池为例)三、高级配置与优化四、典型应用场景五、注意事

Linux CPU飙升排查五步法解读

《LinuxCPU飙升排查五步法解读》:本文主要介绍LinuxCPU飙升排查五步法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录排查思路-五步法1. top命令定位应用进程pid2.php top-Hp[pid]定位应用进程对应的线程tid3. printf"%

Linux下安装Anaconda3全过程

《Linux下安装Anaconda3全过程》:本文主要介绍Linux下安装Anaconda3全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录简介环境下载安装一、找到下载好的文件名为Anaconda3-2018.12-linux-x86_64的安装包二、或者通

Linux系统之stress-ng测压工具的使用

《Linux系统之stress-ng测压工具的使用》:本文主要介绍Linux系统之stress-ng测压工具的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、理论1.stress工具简介与安装2.语法及参数3.具体安装二、实验1.运行8 cpu, 4 fo