# dev\_dax

当我们生成了nd\_dax设备后，nvdimm驱动的任务就基本结束了。或者应该说，nvdimm驱动最后的任务是创建一个dev\_dax设备，由这个设备接过最后的枪。

## 构造dev\_dax的过程

从nd\_dax构造dev\_dax的过程在dax\_pmem\_probe中完成。

```
dax_pmem_probe()
  nvdimm_setup_pfn(nd_pfn, &pgmap);
    nd_pfn_init(nd_pfn);
    __nvdimm_setup_pfn(nd_pfn, pgmap);
  dax_region = alloc_dax_region(dev, region_id, &res,
    le32_to_cpu(pfn_sb->align), addr, PFN_DEV|PFN_MAP);
  dev_dax = __devm_create_dev_dax(dax_region, id, &pgmap, subsys);
    dax_dev = alloc_dax(dev_dax, NULL, NULL);
      dax_dev = dax_dev_get(devt);
        ...
        alloc_inode(super_block)
    dev_dax->dax_dev = dax_dev;
```

上面的流程就是从nd\_dax/nd\_pfn设备构造到dev\_dax的过程。大致可以分成三个步骤：

* nvdimm\_setup\_pfn() 对nd\_pfn中的成员做了设置，比如对齐和计算page struct的空间
* alloc\_dax\_region() 这是构造dev\_dax所需的信息
* 函数\_\_devm\_create\_dev\_dax用来创建dax\_dev，也就是dax文件系统。值得注意的是这个数据结构是通过super\_block创建inode时候创建的。

dev\_dax是个庞大的结构体，就让我们来看一看吧。

## dev\_dax, dax\_dev, dax\_region

```
    dev_dax
    +-------------------------------------+<----------------------------+
    |id                                   |                             |
    |   (int)                             |                             |
    |dev                                  |                             |
    |   (struct device)                   |                             |
    |   +---------------------------------+                             |
    |   |devt                             |  dax_dev.inode->i_rdev      |
    |   |class                            |  dax_class                  |
    |   |/ bus                            |  dax_bus_type               |
    |   |groups                           |  dax_attribute_groups       |
    |   |parent                           |  dax_region->dev = nd_pfn   |
    |   |                                 |                             |
    |   |kobj->name                       |  = "dax0.0"                 |
    |   +---------------------------------+                             |
    |                                     |                             |
    |dax_dev                              |                             |
    |   (struct dax_device*)              |                             |
    |   +---------------------------------+                             |
    |   |private                      ----| ----------------------------+
    |   |   (void*)                       |
    |   |inode                            |      
    |   |   (struct inode)                |      
    |   |   +-----------------------------+      
    |   |   |i_cdev                   ----|-----+
    |   |   |   (struct cdev*)            |     |
    |   |   +-----------------------------+     |
    |   |cdev                             |<----+
    |   |   (struct cdev)                 |  /dev/dax0.0
    |   |   +-----------------------------+
    |   |   |ops                          |  dax_fops
    |   |   |                             |
    |   |   +-----------------------------+
    |   |                                 |
    |   |host                             |
    |   |   (char*)                       |
    |   |ops                              |
    |   |   (struct dax_operations*)      |
    |   +---------------------------------+
    |                                     |
    |region                               |
    |   (struct dax_region*)              |
    |   +---------------------------------+
    |   |id                               |
    |   |   (int)                         |
    |   |dev                              | = nd_pfn.dev
    |   |   (struct device*)              |
    |   |ida                              |
    |   |   (struct ida)                  |
    |   |res                              |
    |   |   (struct resource)             |
    |   |   +-----------------------------+
    |   |   |start                        | nsio->res.start + start_pad + dataoff
    |   |   |end                          | nsio->res.start + size - end_trunc
    |   |   +-----------------------------+
    |   |                                 |
    +---+---------------------------------+
    |ref                              <---|------------------------------------+
    |     (struct percpu_ref)             |                                    |
    |     +-------------------------------+                                    |
    |     |count                          |                                    |
    |     |  (atomic_long_t)              |                                    |
    |     |percpu_count_ptr               |                                    |
    |     |  (unsigned long)              |                                    |
    |     |release                        | = dev_dax_percpu_release           |
    |     |confirm_switch                 |                                    |
    |     |  (percpu_ref_func_t)          |                                    |
    |     |force_atomit                   |                                    |
    |     |                               |                                    |
    |     |rcu                            |                                    |
    |     |  (struct rcu_head)            |                                    |
    +-----+-------------------------------+                                    |
    |pgmap                                | setup in nvdimm_setup_pfn          |
    |     (struct dev_pagemap)            |                                    |
    |     +-------------------------------+                                    |
    |     |dev                            | = nd_pfn.dev                       |
    |     |   (struct device*)            |                                    |
    |     |ref                            | -----------------------------------+
    |     |   (struct percpu_ref*)        |
    |     |res                            |
    |     |   (struct resource)           |
    |     |   +---------------------------+
    |     |   |start                      |  nsio->res.start + start_pad
    |     |   |end                        |  nsio->res.start + size - end_trunc
    |     |   +---------------------------+
    |     |page_free                      |
    |     |   (dev_page_free_t)           |
    |     |kill                           | = dax_pmem_percpu_kill
    |     |   ()                          |
    |     |                               |
    |     |altmap                         |
    |     |   (struct vmem_altmap)        |
    |     |   +---------------------------+
    |     |   |base_pfn                   | = PFN_SECTION_ALIGN_DOWN(nsio->res.start + start_pad)
    |     |   |reserve                    | = PHYS_PFN(SZ_8K)
    |     |   |   (const unsigned long)   |
    |     |   |free                       | = PHYS_PFN(offset - SZ_8K);
    |     |   |align                      |
    |     |   |alloc                      |
    |     |   |   (unsigned long)         |
    |     |   +---------------------------+
    |     |                               |
    +-----+-------------------------------+
```

着重讲几点：

* 最终的目标是生成dev\_dax设备，该设备对应一个字符类型的文件/dev/dax0.0
* /dev/dax0.0的fops是dax\_fops，所以后面的mmap就靠它了
* dev\_dax生成的信息在dax\_region中
* dax\_region中res就是对齐过后，去掉元数据的部分；

怎么样，这下是不是够爽？

## dev\_dax的驱动device\_dax\_driver

既然有了dev\_dax，那么这个设备是如何开始它的工作的呢？再来回顾一下dev\_dax结构体中新创建的设备。

```
    dev                                                                 
    (struct device)                                                  
    +---------------------------------+                              
    |devt                             |  dax_dev.inode->i_rdev       
    |class                            |  dax_class                   
    |/ bus                            |  dax_bus_type                
    |groups                           |  dax_attribute_groups        
    |parent                           |  dax_region->dev = nd_pfn    
    |                                 |                              
    |kobj->name                       |  = "dax0.0"                  
    +---------------------------------+                              
```

这部分已经超出了nvdimm驱动的范畴，但是为了把故事讲完，我们还是要看一下生成的这个设备是如何被使用到的。

因为这个设备是在dax\_bus\_type总线上的，所以它的驱动也应该是在同一个总线上的了。这个默认的驱动就是device\_dax\_driver.

```
static struct dax_device_driver device_dax_driver = {
	.drv = {
		.probe = dev_dax_probe,
		.remove = dev_dax_remove,
	},
	.match_always = 1,
};
```

### 探测

这个驱动的探测函数dev\_dax\_probe主要做了两件事情：

* devm\_memremap\_pages(dev, \&dev->pgmap)
* cdev\_init(cdev, \&dax\_fops);


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://richardweiyang-2.gitbook.io/kernel-exploring/00-brief_navigation/07-dax/09-dev_dax.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
