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中的这个讨论,或许可以让逻辑清楚一点。

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

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

Last updated