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

news/2025/2/21 7:17:15/

这里记录下在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目录下也可以看到相关的设备节点了。


http://www.ppmy.cn/news/1205796.html

相关文章

VueCli 自定义创建项目及配置

一、VueCli 自定义创建项目 1.安装脚手架 (已安装) npm i vue/cli -g2.创建项目 vue create hm-exp-mobile选项 Vue CLI v5.0.8 ? Please pick a preset:Default ([Vue 3] babel, eslint)Default ([Vue 2] babel, eslint) > Manually select features 选自定义手动…

贪心:推公式

耍杂技的牛&#xff1a; 我们先分析每头牛的危险值 他前面牛的w(重量值)和 - 自身的s(强壮值)&#xff0c;要使每头牛的危险值最小&#xff0c;这显然是与w 和 s同时相关&#xff0c;所以先想出一种做法按 每头牛的w s进行升序排序(题见多了可能就会有这种题感)。接下来进行数…

虚幻引擎:如何在工程里面添加插件

1.在自己的项目中安装插件 在content目录下创建一个Plugins的文件,将插件文件放进去即可 2.在软件上安装,这样所有创建的项目都会带有此插件 将插件放在自己软件的这个目录下就好了

【踩坑】Putty报错: Can’t agree a key change algorithm

原因可能是putty版本太老了&#xff0c;更新putty就好了 下载地址&#xff1a;https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html 根据需要选择自己想要下载的版本&#xff0c;我是下载的如下图所示的版本。 另外&#xff0c;了解了一下Putty是用来远程连接…

Google Chrome 浏览器 119.0.6045.106 版本提示 STATUS_INVALID_IMAGE_HASH 崩溃

问题 今天更新 Google Chrome 浏览器到 119.0.6045.106 版本&#xff0c;然后访问页面不是空白&#xff0c;就是页面崩溃了 解决方案 我在网上找了几种&#xff0c;下面这个方式符合&#xff0c;能解决我的问题&#xff0c;就是在快捷方式的属性那里&#xff0c;找到目标给它…

对于MVVM的理解、使用、MVC与MVVM的区别、MVVM应用场景

前言 持续学习总结输出中&#xff0c;今天分享的是对于MVVM的理解、使用、MVC与MVVM的区别、MVVM应用场景 MVVM MVVM 是 Model-View-ViewModel 的缩写。MVVM 是一种设计思想。 Model 代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。 View 代表UI组件&#xff0c…

Linux命令(116)之logger

linux命令之logger 1.logger介绍 linux命令logger是一个shell命令接口,通过该接口使用rsyslog的系统日志模块可以向系统日志文件(自定义日志文件)写入一行信息 2.logger用法 logger [参数] [message] logger参数 参数说明-i记录进程ID-t在日志中的每一行添加一个标签-p指定…

spring boot security 自定义AuthenticationProvider

spring boot security 自定义AuthenticationProvider 基于 spring boot 3.x 场景实现 手机验证码登陆 实现 CaptureCodeAuthenticationFilter public class CaptureCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {private static final Strin…