# nd\_namespace\_X

在nd\_region的驱动中我们看到其中一个重要的动作就是创建namespace。这个工作交给了nd\_region\_register\_namespaces()函数处理。

## 构造nd\_namespace的函数

先来看看构造namespace的函数流程。

```
nd_region_register_namespaces(struct nd_region *nd_region)
  init_active_labels(struct nd_region *nd_region)
  type = nd_region_to_nstype(nd_region);
  switch (type) {
  case ND_DEVICE_NAMESPACE_IO:
    devs = create_namespace_io(nd_region);
    break;
  case ND_DEVICE_NAMESPACE_PMEM:
  case ND_DEVICE_NAMESPACE_BLK:
    devs = create_namespaces(nd_region);
    break;
  default:
    break;
  }
```

大致看主要做了两件事：

* 配置nd\_regioin->mapping->labels
* 根据nd\_region的NS类型创建对应的namespace

其中labels数据类型看定义就是RFC4122中定义的label了。也算是找到了一个硬件和软件对应的关系。

而create\_namespaces函数的工作则就是扫描nd\_region->mapping->labels来构造的。具体做了什么，让我们来看看相关的数据结构。

## nd\_namespace\_X

在上面的代码片段和nd\_region的章节中我们都可以看出，namespace有三种类型

* nd\_namespace\_blk
* nd\_namespace\_io
* nd\_namespace\_pmem

所以内核中用了三种类型分别表述。我们一个个来看。

### nd\_namespace\_blk

下面是nd\_namespace\_blk这个数据结构的内容。

```
    nd_namespace_blk
    +-------------------------------------------------+  
    |common                                           |
    |    (struct nd_namespace_common)                 |
    |    +--------------------------------------------+
    |    |dev                                         |
    |    |    (struct device)                         |
    |    |    +---------------------------------------+
    |    |    |type                                   | = namespace_blk_device_type
    |    |    |                                       |
    |    |    +---------------------------------------+
    |    |claim                                       |
    |    |    (struct device*)                        |
    |    +--------------------------------------------+  
    |    |force_raw                                   |
    |    |    (int)                                   |
    |    +--------------------------------------------+  
    |    |rw_bytes                                    |
    |    |    ()                                      |
    |    +--------------------------------------------+  
    |    |claim_class                                 | = BTT/BTT2/PFN/DAX
    |    |    (enum nvdimm_claim_class)               |
    +----+--------------------------------------------+  
    |alt_name                                         | = nd_label->name
    |    (char *)                                     |
    |uuid                                             | = nd_label->uuid
    |    (u8*)                                        |
    +-------------------------------------------------+  
    |id                                               |
    |    (int)                                        |
    |lbasize                                          |
    |    (unsigned long)                              |
    |size                                             |
    |    (resource_size_t)                            |
    +-------------------------------------------------+  
    |num_resources                                    |
    |    (int)                                        |
    |res                                              | dpa res array
    |    (struct resource**)                          |
    +-------------------------------------------------+  
```

创建的时候根据nd\_label的信息填写了uuid, alt\_name以及dpa resource数组。

### nd\_namespace\_io

下面是nd\_namespace\_io的数据结构。

```
    nd_namespace_io
    +-------------------------------------------------+  
    |common                                           |
    |    (struct nd_namespace_common)                 |
    |    +--------------------------------------------+
    |    |dev                                         |
    |    |    (struct device)                         |
    |    |    +---------------------------------------+
    |    |    |type                                   | = namespace_io_device_type
    |    |    |                                       |
    |    |    +---------------------------------------+
    |    |claim                                       |
    |    |    (struct device*)                        |
    |    +--------------------------------------------+  
    |    |force_raw                                   |
    |    |    (int)                                   |
    |    +--------------------------------------------+  
    |    |rw_bytes                                    |
    |    |    ()                                      |
    |    +--------------------------------------------+  
    |    |claim_class                                 |
    |    |    (enum nvdimm_claim_class)               |
    +----+--------------------------------------------+  
    |size                                             |
    |    (resource_size_t)                            |
    +-------------------------------------------------+  
    |res                                              |
    |    (struct resource)                            |
    |    +--------------------------------------------+
    |    |start                                       | = nd_region->ndr_start
    |    |end                                         | = res->start + nd_region->ndr_size - 1
    |    |                                            |
    +----+--------------------------------------------+  
    |addr                                             |
    |    (void *)                                     |
    +-------------------------------------------------+  
    |bb                                               |
    |    (struct badblock)                            |
    +-------------------------------------------------+  
```

这种namespace比较简单，创建的时候除了设置了type，就是把res设置成了nd\_region中的空间。

### nd\_namespace\_pmem

```
    nd_namespace_pmem
    +-------------------------------------------------+  
    |nsio                                             |
    |    (struct nd_namespace_io)                     |
    +----+--------------------------------------------+
    |    |common                                      |
    |    |    (struct nd_namespace_common)            |
    |    |    +---------------------------------------+
    |    |    |dev                                    |
    |    |    |    (struct device)                    |
    |    |    |    +----------------------------------+
    |    |    |    |type                              | = namespace_pmem_device_type
    |    |    |    |                                  |
    |    |    |    +----------------------------------+
    |    |    |claim                                  |
    |    |    |    (struct device*)                   |
    |    |    +---------------------------------------+  
    |    |    |force_raw                              |
    |    |    |    (int)                              |
    |    |    +---------------------------------------+  
    |    |    |rw_bytes                               |
    |    |    |    ()                                 |
    |    |    +---------------------------------------+  
    |    |    |claim_class                            |
    |    |    |    (enum nvdimm_claim_class)          |
    |    +----+---------------------------------------+  
    |    |size                                        |
    |    |    (resource_size_t)                       |
    |    +--------------------------------------------+  
    |    |res                                         |
    |    |    (struct resource)                       |
    |    |    +---------------------------------------+
    |    |    |flags                                  | = IORESOURCE_MEM
    |    |    |start                                  | = nd_region->ndr_start + dpa/spa offset
    |    |    |end                                    | = start + size - 1
    |    |    |                                       |
    |    +----+---------------------------------------+  
    |    |addr                                        |
    |    |    (void *)                                |
    |    |--------------------------------------------+  
    |    |bb                                          |
    |    |    (struct badblock)                       |
    +----+--------------------------------------------+  
    |alt_name                                         | = nd_label->name
    |    (char *)                                     |
    |lbasize                                          | = nd_label->lbasize
    |    (unsigned long)                              |
    |uuid                                             | = nd_label->uuid
    |    (u8*)                                        |
    +-------------------------------------------------+  
```

pmem最后整合了一个resource。我猜这个res表示的范围就是内存空间的物理地址了？

## nd\_pmem/blk\_driver

以上三个namespace都会生成自己的设备，这样也就都有各自对应的驱动。

* nd\_blk\_driver
* nd\_pmem\_driver

很不好意思，nd\_pmem\_driver包揽了第二三种namespace的驱动，真实能者多劳的典范。

不过这两个驱动都有一个共同点，就是都调用了nvdimm\_namespace\_common\_probe()做初始化。

这个函数其实也没做啥，主要做了两件事：

* 检测空间大小是否合适
* 设置了uuid

经过仔细研读和实验验证，结果发现了几个神奇的事儿。

* nd\_pmem\_driver不仅仅是namespace的驱动，还是btt/pfn设备的驱动。
* nd\_region\_probe()会也建立btt/pfn/dax设备。
* nd\_pmem\_probe也会通过nd\_btt/pfn/dax\_probe建立对应的设备。

关于这个过程的具体细节可以看[lkml](https://lkml.org/lkml/2019/1/18/1026)中的这个讨论，或许可以让逻辑清楚一点。

总之当namespace被配置成这三种类型的某一种时，nd\_pmem\_probe会通过nd\_btt/pfn/dax\_probe去创建某一种类型的设备。并且有意思的是，这种情况下nd\_pmem\_probe会**失败**。这种高端的方法我还是头一次见。

接下来的事情就交给了各自的设备和驱动了。
