요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
===============================
LIBNVDIMM: Non-Volatile Devices
===============================
libnvdimm - kernel / libndctl - userspace helper library
Version 13
.. contents:
Glossary
Overview
Supporting Documents
Git Trees
LIBNVDIMM PMEM
PMEM-REGIONs, Atomic Sectors, and DAX
Example NVDIMM Platform
LIBNVDIMM Kernel Device Model and LIBNDCTL Userspace API
LIBNDCTL: Context
libndctl: instantiate a new library context example
LIBNVDIMM/LIBNDCTL: Bus
libnvdimm: control class device in /sys/class
libnvdimm: bus
libndctl: bus enumeration example
LIBNVDIMM/LIBNDCTL: DIMM (NMEM)
libnvdimm: DIMM (NMEM)
libndctl: DIMM enumeration example
LIBNVDIMM/LIBNDCTL: Region
libnvdimm: region
libndctl: region enumeration example
Why Not Encode the Region Type into the Region Name?
How Do I Determine the Major Type of a Region?
LIBNVDIMM/LIBNDCTL: Namespace
libnvdimm: namespace
libndctl: namespace enumeration example
libndctl: namespace creation example
Why the Term "namespace"?
LIBNVDIMM/LIBNDCTL: Block Translation Table "btt"
libnvdimm: btt layout
libndctl: btt creation example
Summary LIBNDCTL Diagram
Glossary
========
PMEM:
A system-physical-address range where writes are persistent. A
block device composed of PMEM is capable of DAX. A PMEM address range
may span an interleave of several DIMMs.
DPA:
DIMM Physical Address, is a DIMM-relative offset. With one DIMM in
the system there would be a 1:1 system-physical-address:DPA association.
Once more DIMMs are added a memory controller interleave must be
decoded to determine the DPA associated with a given
system-physical-address.
DAX:
File system extensions to bypass the page cache and block layer to
mmap persistent memory, from a PMEM block device, directly into a
process address space.
DSM:
Device Specific Method: ACPI method to control specific
device - in this case the firmware.
DCR:
NVDIMM Control Region Structure defined in ACPI 6 Section 5.2.25.5.
It defines a vendor-id, device-id, and interface format for a given DIMM.
BTT:
Block Translation Table: Persistent memory is byte addressable.
Existing software may have an expectation that the power-fail-atomicity
of writes is at least one sector, 512 bytes. The BTT is an indirection
table with atomic update semantics to front a PMEM block device
driver and present arbitrary atomic sector sizes.
LABEL:
Metadata stored on a DIMM device that partitions and identifies
(persistently names) capacity allocated to different PMEM namespaces. It
also indicates whether an address abstraction like a BTT is applied to
the namespace. Note that traditional partition tables, GPT/MBR, are
layered on top of a PMEM namespace, or an address abstraction like BTT
if present, but partition support is deprecated going forward.
Overview
========
The LIBNVDIMM subsystem provides support for PMEM described by platform
firmware or a device driver. On ACPI based systems the platform firmware
conveys persistent memory resource via the ACPI NFIT "NVDIMM Firmware
Interface Table" in ACPI 6. While the LIBNVDIMM subsystem implementation
is generic and supports pre-NFIT platforms, it was guided by the
superset of capabilities need to support this ACPI 6 definition for
NVDIMM resources. The original implementation supported the
block-window-aperture capability described in the NFIT, but that support
has since been abandoned and never shipped in a product.
Supporting Documents
--------------------
ACPI 6:
https://www.uefi.org/sites/default/files/resources/ACPI_6.0.pdf
NVDIMM Namespace:
https://pmem.io/documents/NVDIMM_Namespace_Spec.pdf
DSM Interface Example:
https://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf
Driver Writer's Guide:
https://pmem.io/documents/NVDIMM_Driver_Writers_Guide.pdf
Git Trees
---------
LIBNVDIMM:
https://git.kernel.org/cgit/linux/kernel/git/nvdimm/nvdimm.git
LIBNDCTL:
https://github.com/pmem/ndctl.git
LIBNVDIMM PMEM
==============
Prior to the arrival of the NFIT, non-volatile memory was described to a
system in various ad-hoc ways. Usually only the bare minimum was
provided, namely, a single system-physical-address range where writes
are expected to be durable after a system power loss. Now, the NFIT
specification standardizes not only the description of PMEM, but also
platform message-passing entry points for control and configuration.
PMEM (nd_pmem.ko): Drives a system-physical-address range. This range is
contiguous in system memory and may be interleaved (hardware memory controller
striped) across multiple DIMMs. When interleaved the platform may optionally
provide details of which DIMMs are participating in the interleave.
It is worth noting that when the labeling capability is detected (a EFI
namespace label index block is found), then no block device is created
by default as userspace needs to do at least one allocation of DPA to
the PMEM range. In contrast ND_NAMESPACE_IO ranges, once registered,
can be immediately attached to nd_pmem. This latter mode is called
label-less or "legacy".
PMEM-REGIONs, Atomic Sectors, and DAX
-------------------------------------
For the cases where an application or filesystem still needs atomic sector
update guarantees it can register a BTT on a PMEM device or partition. See
LIBNVDIMM/NDCTL: Block Translation Table "btt"
Example NVDIMM Platform
=======================
For the remainder of this document the following diagram will be
referenced for any example sysfs layouts::
(a) (b) DIMM
+-------------------+--------+--------+--------+
+------+ | pm0.0 | free | pm1.0 | free | 0
| imc0 +--+- - - region0- - - +--------+ +--------+
+--+---+ | pm0.0 | free | pm1.0 | free | 1
| +-------------------+--------v v--------+
+--+---+ | |
| cpu0 | region1
+--+---+ | |
| +----------------------------^ ^--------+
+--+---+ | free | pm1.0 | free | 2
| imc1 +--+----------------------------| +--------+
+------+ | free | pm1.0 | free | 3
+----------------------------+--------+--------+
In this platform we have four DIMMs and two memory controllers in one
socket. Each PMEM interleave set is identified by a region device with
a dynamically assigned id.
1. The first portion of DIMM0 and DIMM1 are interleaved as REGION0. A
single PMEM namespace is created in the REGION0-SPA-range that spans most
of DIMM0 and DIMM1 with a user-specified name of "pm0.0". Some of that
interleaved system-physical-address range is left free for
another PMEM namespace to be defined.
2. In the last portion of DIMM0 and DIMM1 we have an interleaved
system-physical-address range, REGION1, that spans those two DIMMs as
well as DIMM2 and DIMM3. Some of REGION1 is allocated to a PMEM namespace
named "pm1.0".
This bus is provided by the kernel under the device
/sys/devices/platform/nfit_test.0 when the nfit_test.ko module from
tools/testing/nvdimm is loaded. This module is a unit test for
LIBNVDIMM and the acpi_nfit.ko driver.
LIBNVDIMM Kernel Device Model and LIBNDCTL Userspace API
========================================================
What follows is a description of the LIBNVDIMM sysfs layout and a
corresponding object hierarchy diagram as viewed through the LIBNDCTL
API. The example sysfs paths and diagrams are relative to the Example
NVDIMM Platform which is also the LIBNVDIMM bus used in the LIBNDCTL unit
test.
LIBNDCTL: Context
-----------------
Every API call in the LIBNDCTL library requires a context that holds the
logging parameters and other library instance state. The library is
based on the libabc template:
https://git.kernel.org/cgit/linux/kernel/git/kay/libabc.git
LIBNDCTL: instantiate a new library context example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
struct ndctl_ctx *ctx;
if (ndctl_new(&ctx) == 0)
return ctx;
else
return NULL;
LIBNVDIMM/LIBNDCTL: Bus
-----------------------
A bus has a 1:1 relationship with an NFIT. The current expectation for
ACPI based systems is that there is only ever one platform-global NFIT.
That said, it is trivial to register multiple NFITs, the specification
does not preclude it. The infrastructure supports multiple buses and
we use this capability to test multiple NFIT configurations in the unit
test.
LIBNVDIMM: control class device in /sys/class
---------------------------------------------
This character device accepts DSM messages to be passed to DIMM
identified by its NFIT handle::
/sys/class/nd/ndctl0
|-- dev
|-- device -> ../../../ndbus0
|-- subsystem -> ../../../../../../../class/nd
LIBNVDIMM: bus
--------------
::
struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
struct nvdimm_bus_descriptor *nfit_desc);
::
/sys/devices/platform/nfit_test.0/ndbus0
|-- commands
|-- nd
|-- nfit
|-- nmem0
|-- nmem1
|-- nmem2
|-- nmem3
|-- power
|-- provider
|-- region0
|-- region1
|-- region2
|-- region3
|-- region4
|-- region5
|-- uevent
`-- wait_probe
LIBNDCTL: bus enumeration example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Find the bus handle that describes the bus from Example NVDIMM Platform::
static struct ndctl_bus *get_bus_by_provider(struct ndctl_ctx *ctx,
const char *provider)
{
struct ndctl_bus *bus;
ndctl_bus_foreach(ctx, bus)
if (strcmp(provider, ndctl_bus_get_provider(bus)) == 0)
return bus;
return NULL;
}
bus = get_bus_by_provider(ctx, "nfit_test.0");
LIBNVDIMM/LIBNDCTL: DIMM (NMEM)
-------------------------------
The DIMM device provides a character device for sending commands to
hardware, and it is a container for LABELs. If the DIMM is defined by
NFIT then an optional 'nfit' attribute sub-directory is available to add
NFIT-specifics.
Note that the kernel device name for "DIMMs" is "nmemX". The NFIT
describes these devices via "Memory Device to System Physical Address
Range Mapping Structure", and there is no requirement that they actually
be physical DIMMs, so we use a more generic name.
LIBNVDIMM: DIMM (NMEM)
^^^^^^^^^^^^^^^^^^^^^^
::
struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
const struct attribute_group **groups, unsigned long flags,
unsigned long *dsm_mask);
::
/sys/devices/platform/nfit_test.0/ndbus0
|-- nmem0
| |-- available_slots
| |-- commands
| |-- dev
| |-- devtype
| |-- driver -> ../../../../../bus/nd/drivers/nvdimm
| |-- modalias
| |-- nfit
| | |-- device
| | |-- format
| | |-- handle
| | |-- phys_id
| | |-- rev_id
| | |-- serial
| | `-- vendor
| |-- state
| |-- subsystem -> ../../../../../bus/nd
| `-- uevent
|-- nmem1
[..]
LIBNDCTL: DIMM enumeration example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note, in this example we are assuming NFIT-defined DIMMs which are
identified by an "nfit_handle" a 32-bit value where:
- Bit 3:0 DIMM number within the memory channel
- Bit 7:4 memory channel number
- Bit 11:8 memory controller ID
- Bit 15:12 socket ID (within scope of a Node controller if node
controller is present)
- Bit 27:16 Node Controller ID
- Bit 31:28 Reserved
::
static struct ndctl_dimm *get_dimm_by_handle(struct ndctl_bus *bus,
unsigned int handle)
{
struct ndctl_dimm *dimm;
ndctl_dimm_foreach(bus, dimm)
if (ndctl_dimm_get_handle(dimm) == handle)
return dimm;
return NULL;
}
#define DIMM_HANDLE(n, s, i, c, d) \
(((n & 0xfff) << 16) | ((s & 0xf) << 12) | ((i & 0xf) << 8) \
| ((c & 0xf) << 4) | (d & 0xf))
dimm = get_dimm_by_handle(bus, DIMM_HANDLE(0, 0, 0, 0, 0));
LIBNVDIMM/LIBNDCTL: Region
--------------------------
A generic REGION device is registered for each PMEM interleave-set /
range. Per the example there are 2 PMEM regions on the "nfit_test.0"
bus. The primary role of regions are to be a container of "mappings". A
mapping is a tuple of <DIMM, DPA-start-offset, length>.
LIBNVDIMM provides a built-in driver for REGION devices. This driver
is responsible for all parsing LABELs, if present, and then emitting NAMESPACE
devices for the nd_pmem driver to consume.
In addition to the generic attributes of "mapping"s, "interleave_ways"
and "size" the REGION device also exports some convenience attributes.
"nstype" indicates the integer type of namespace-device this region
emits, "devtype" duplicates the DEVTYPE variable stored by udev at the
'add' event, "modalias" duplicates the MODALIAS variable stored by udev
at the 'add' event, and finally, the optional "spa_index" is provided in
the case where the region is defined by a SPA.
LIBNVDIMM: region::
struct nd_region *nvdimm_pmem_region_create(struct nvdimm_bus *nvdimm_bus,
struct nd_region_desc *ndr_desc);
::
/sys/devices/platform/nfit_test.0/ndbus0
|-- region0
| |-- available_size
| |-- btt0
| |-- btt_seed
| |-- devtype
| |-- driver -> ../../../../../bus/nd/drivers/nd_region
| |-- init_namespaces
| |-- mapping0
| |-- mapping1
| |-- mappings
| |-- modalias
| |-- namespace0.0
| |-- namespace_seed
| |-- numa_node
| |-- nfit
| | `-- spa_index
| |-- nstype
| |-- set_cookie
| |-- size
| |-- subsystem -> ../../../../../bus/nd
| `-- uevent
|-- region1
[..]
LIBNDCTL: region enumeration example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Sample region retrieval routines based on NFIT-unique data like
"spa_index" (interleave set id).
::
static struct ndctl_region *get_pmem_region_by_spa_index(struct ndctl_bus *bus,
unsigned int spa_index)
{
struct ndctl_region *region;
ndctl_region_foreach(bus, region) {
if (ndctl_region_get_type(region) != ND_DEVICE_REGION_PMEM)
continue;
if (ndctl_region_get_spa_index(region) == spa_index)
return region;
}
return NULL;
}
LIBNVDIMM/LIBNDCTL: Namespace
-----------------------------
A REGION, after resolving DPA aliasing and LABEL specified boundaries, surfaces
one or more "namespace" devices. The arrival of a "namespace" device currently
triggers the nd_pmem driver to load and register a disk/block device.
LIBNVDIMM: namespace
^^^^^^^^^^^^^^^^^^^^
Here is a sample layout from the 2 major types of NAMESPACE where namespace0.0
represents DIMM-info-backed PMEM (note that it has a 'uuid' attribute), and
namespace1.0 represents an anonymous PMEM namespace (note that has no 'uuid'
attribute due to not support a LABEL)
::
/sys/devices/platform/nfit_test.0/ndbus0/region0/namespace0.0
|-- alt_name
|-- devtype
|-- dpa_extents
|-- force_raw
|-- modalias
|-- numa_node
|-- resource
|-- size
|-- subsystem -> ../../../../../../bus/nd
|-- type
|-- uevent
`-- uuid
/sys/devices/platform/nfit_test.1/ndbus1/region1/namespace1.0
|-- block
| `-- pmem0
|-- devtype
|-- driver -> ../../../../../../bus/nd/drivers/pmem
|-- force_raw
|-- modalias
|-- numa_node
|-- resource
|-- size
|-- subsystem -> ../../../../../../bus/nd
|-- type
`-- uevent
LIBNDCTL: namespace enumeration example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Namespaces are indexed relative to their parent region, example below.
These indexes are mostly static from boot to boot, but subsystem makes
no guarantees in this regard. For a static namespace identifier use its
'uuid' attribute.
::
static struct ndctl_namespace
*get_namespace_by_id(struct ndctl_region *region, unsigned int id)
{
struct ndctl_namespace *ndns;
ndctl_namespace_foreach(region, ndns)
if (ndctl_namespace_get_id(ndns) == id)
return ndns;
return NULL;
}
LIBNDCTL: namespace creation example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Idle namespaces are automatically created by the kernel if a given
region has enough available capacity to create a new namespace.
Namespace instantiation involves finding an idle namespace and
configuring it. For the most part the setting of namespace attributes
can occur in any order, the only constraint is that 'uuid' must be set
before 'size'. This enables the kernel to track DPA allocations
internally with a static identifier::
static int configure_namespace(struct ndctl_region *region,
struct ndctl_namespace *ndns,
struct namespace_parameters *parameters)
{
char devname[50];
snprintf(devname, sizeof(devname), "namespace%d.%d",
ndctl_region_get_id(region), parameters->id);
ndctl_namespace_set_alt_name(ndns, devname);
/* 'uuid' must be set prior to setting size! */
ndctl_namespace_set_uuid(ndns, parameters->uuid);
ndctl_namespace_set_size(ndns, parameters->size);
/* unlike pmem namespaces, blk namespaces have a sector size */
if (parameters->lbasize)
ndctl_namespace_set_sector_size(ndns, parameters->lbasize);
ndctl_namespace_enable(ndns);
}
Why the Term "namespace"?
^^^^^^^^^^^^^^^^^^^^^^^^^
1. Why not "volume" for instance? "volume" ran the risk of confusing
ND (libnvdimm subsystem) to a volume manager like device-mapper.
2. The term originated to describe the sub-devices that can be created
within a NVME controller (see the nvme specification:
https://www.nvmexpress.org/specifications/), and NFIT namespaces are
meant to parallel the capabilities and configurability of
NVME-namespaces.
LIBNVDIMM/LIBNDCTL: Block Translation Table "btt"
-------------------------------------------------
A BTT (design document: https://pmem.io/2014/09/23/btt.html) is a
personality driver for a namespace that fronts entire namespace as an
'address abstraction'.
LIBNVDIMM: btt layout
^^^^^^^^^^^^^^^^^^^^^
Every region will start out with at least one BTT device which is the
seed device. To activate it set the "namespace", "uuid", and
"sector_size" attributes and then bind the device to the nd_pmem or
nd_blk driver depending on the region type::
/sys/devices/platform/nfit_test.1/ndbus0/region0/btt0/
|-- namespace
|-- delete
|-- devtype
|-- modalias
|-- numa_node
|-- sector_size
|-- subsystem -> ../../../../../bus/nd
|-- uevent
`-- uuid
LIBNDCTL: btt creation example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Similar to namespaces an idle BTT device is automatically created per
region. Each time this "seed" btt device is configured and enabled a new
seed is created. Creating a BTT configuration involves two steps of
finding and idle BTT and assigning it to consume a namespace.
::
static struct ndctl_btt *get_idle_btt(struct ndctl_region *region)
{
struct ndctl_btt *btt;
ndctl_btt_foreach(region, btt)
if (!ndctl_btt_is_enabled(btt)
&& !ndctl_btt_is_configured(btt))
return btt;
return NULL;
}
static int configure_btt(struct ndctl_region *region,
struct btt_parameters *parameters)
{
btt = get_idle_btt(region);
ndctl_btt_set_uuid(btt, parameters->uuid);
ndctl_btt_set_sector_size(btt, parameters->sector_size);
ndctl_btt_set_namespace(btt, parameters->ndns);
/* turn off raw mode device */
ndctl_namespace_disable(parameters->ndns);
/* turn on btt access */
ndctl_btt_enable(btt);
}
Once instantiated a new inactive btt seed device will appear underneath
the region.
Once a "namespace" is removed from a BTT that instance of the BTT device
will be deleted or otherwise reset to default values. This deletion is
only at the device model level. In order to destroy a BTT the "info
block" needs to be destroyed. Note, that to destroy a BTT the media
needs to be written in raw mode. By default, the kernel will autodetect
the presence of a BTT and disable raw mode. This autodetect behavior
can be suppressed by enabling raw mode for the namespace via the
ndctl_namespace_set_raw_mode() API.
Summary LIBNDCTL Diagram
------------------------
For the given example above, here is the view of the objects as seen by the
LIBNDCTL API::
+---+
|CTX|
+-+-+
|
+-------+ |
| DIMM0 <-+ | +---------+ +--------------+ +---------------+
+-------+ | | +-> REGION0 +---> NAMESPACE0.0 +--> PMEM8 "pm0.0" |
| DIMM1 <-+ +-v--+ | +---------+ +--------------+ +---------------+
+-------+ +-+BUS0+-| +---------+ +--------------+ +----------------------+
| DIMM2 <-+ +----+ +-> REGION1 +---> NAMESPACE1.0 +--> PMEM6 "pm1.0" | BTT1 |
+-------+ | | +---------+ +--------------+ +---------------+------+
| DIMM3 <-+
+-------+
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
LIBNVDIMM 문서 구성
1-44이 문서는 비휘발성 장치를 위한 `LIBNVDIMM`을 설명합니다. 커널 쪽 구성 요소는 `libnvdimm`, userspace helper library는 `libndctl`이며 문의 주소는 `[email protected]`입니다. 문서 버전은 13입니다.
목차는 용어집과 개요에서 시작해 PMEM, 예제 NVDIMM 플랫폼, LIBNVDIMM 커널 device model과 LIBNDCTL userspace API를 차례로 다룹니다.
API 부분은 context, bus, DIMM 또는 NMEM, region, namespace, Block Translation Table인 BTT를 따라 내려갑니다. 각 객체마다 커널 sysfs layout과 libndctl enumeration 또는 creation 예제가 짝을 이룹니다.
마지막에는 예제 플랫폼의 DIMM, bus, region, namespace, PMEM, BTT 관계를 하나의 LIBNDCTL 객체 그래프로 요약합니다.
===============================
LIBNVDIMM: Non-Volatile Devices
===============================
libnvdimm - kernel / libndctl - userspace helper library
[email protected]
Version 13
.. contents:
Glossary
Overview
Supporting Documents
Git Trees
LIBNVDIMM PMEM
PMEM-REGIONs, Atomic Sectors, and DAX
Example NVDIMM Platform
LIBNVDIMM Kernel Device Model and LIBNDCTL Userspace API
LIBNDCTL: Context
libndctl: instantiate a new library context example
LIBNVDIMM/LIBNDCTL: Bus
libnvdimm: control class device in /sys/class
libnvdimm: bus
libndctl: bus enumeration example
LIBNVDIMM/LIBNDCTL: DIMM (NMEM)
libnvdimm: DIMM (NMEM)
libndctl: DIMM enumeration example
LIBNVDIMM/LIBNDCTL: Region
libnvdimm: region
libndctl: region enumeration example
Why Not Encode the Region Type into the Region Name?
How Do I Determine the Major Type of a Region?
LIBNVDIMM/LIBNDCTL: Namespace
libnvdimm: namespace
libndctl: namespace enumeration example
libndctl: namespace creation example
Why the Term "namespace"?
LIBNVDIMM/LIBNDCTL: Block Translation Table "btt"
libnvdimm: btt layout
libndctl: btt creation example
Summary LIBNDCTL Diagram
PMEM과 NVDIMM 용어
45-88`PMEM`은 쓰기가 영속적인 system-physical-address 범위입니다. PMEM으로 구성한 block device는 DAX를 지원할 수 있으며, 하나의 PMEM 주소 범위는 여러 DIMM에 걸친 interleave일 수 있습니다.
`DPA`는 DIMM Physical Address로 DIMM 기준 오프셋입니다. DIMM이 하나면 system physical address와 DPA가 1:1이지만, DIMM이 늘어나면 주어진 system physical address에 대응하는 DPA를 찾기 위해 memory-controller interleave를 decode해야 합니다.
`DAX`는 PMEM block device의 persistent memory를 process address space에 직접 `mmap`하도록 page cache와 block layer를 우회하는 filesystem extension입니다.
`DSM`은 Device Specific Method로, 특정 장치를 제어하는 ACPI method이며 여기서는 firmware를 제어합니다. `DCR`은 ACPI 6 Section 5.2.25.5가 정의한 NVDIMM Control Region Structure로, DIMM의 vendor ID, device ID, interface format을 정의합니다.
`BTT`는 Block Translation Table입니다. Persistent memory는 byte-addressable이지만 기존 software는 쓰기의 power-fail atomicity가 최소 한 sector, 즉 512 bytes라고 가정할 수 있습니다. BTT는 원자적 갱신 의미론을 가진 indirection table로 PMEM block driver 앞에 놓여 임의의 atomic sector size를 제공합니다.
`LABEL`은 DIMM에 저장하는 metadata입니다. 여러 PMEM namespace에 할당한 capacity를 나누고 식별하며 영속적인 이름을 붙이고, BTT 같은 address abstraction이 적용되었는지도 나타냅니다. GPT/MBR partition table은 PMEM namespace나 BTT 위에 둘 수 있지만 앞으로 partition 지원은 deprecated됩니다.
Glossary
========
PMEM:
A system-physical-address range where writes are persistent. A
block device composed of PMEM is capable of DAX. A PMEM address range
may span an interleave of several DIMMs.
DPA:
DIMM Physical Address, is a DIMM-relative offset. With one DIMM in
the system there would be a 1:1 system-physical-address:DPA association.
Once more DIMMs are added a memory controller interleave must be
decoded to determine the DPA associated with a given
system-physical-address.
DAX:
File system extensions to bypass the page cache and block layer to
mmap persistent memory, from a PMEM block device, directly into a
process address space.
DSM:
Device Specific Method: ACPI method to control specific
device - in this case the firmware.
DCR:
NVDIMM Control Region Structure defined in ACPI 6 Section 5.2.25.5.
It defines a vendor-id, device-id, and interface format for a given DIMM.
BTT:
Block Translation Table: Persistent memory is byte addressable.
Existing software may have an expectation that the power-fail-atomicity
of writes is at least one sector, 512 bytes. The BTT is an indirection
table with atomic update semantics to front a PMEM block device
driver and present arbitrary atomic sector sizes.
LABEL:
Metadata stored on a DIMM device that partitions and identifies
(persistently names) capacity allocated to different PMEM namespaces. It
also indicates whether an address abstraction like a BTT is applied to
the namespace. Note that traditional partition tables, GPT/MBR, are
layered on top of a PMEM namespace, or an address abstraction like BTT
if present, but partition support is deprecated going forward.
Subsystem 개요와 참고 자료
89-122LIBNVDIMM subsystem은 platform firmware 또는 device driver가 기술한 PMEM을 지원합니다. ACPI 기반 시스템에서는 platform firmware가 ACPI 6의 NFIT, 즉 NVDIMM Firmware Interface Table로 persistent-memory resource를 전달합니다.
구현 자체는 generic하며 NFIT 이전 플랫폼도 지원하지만, ACPI 6의 NVDIMM resource 정의를 지원하는 데 필요한 capability의 상위 집합을 기준으로 설계되었습니다.
초기 구현은 NFIT에 기술된 block-window-aperture capability를 지원했지만 이 기능은 이후 폐기되었고 실제 제품에는 출시되지 않았습니다.
지원 문서로 ACPI 6, NVDIMM Namespace specification, DSM Interface Example, Driver Writer's Guide가 제시됩니다. Kernel 구현의 LIBNVDIMM git tree와 userspace LIBNDCTL의 `pmem/ndctl` GitHub 저장소도 연결됩니다.
Overview
========
The LIBNVDIMM subsystem provides support for PMEM described by platform
firmware or a device driver. On ACPI based systems the platform firmware
conveys persistent memory resource via the ACPI NFIT "NVDIMM Firmware
Interface Table" in ACPI 6. While the LIBNVDIMM subsystem implementation
is generic and supports pre-NFIT platforms, it was guided by the
superset of capabilities need to support this ACPI 6 definition for
NVDIMM resources. The original implementation supported the
block-window-aperture capability described in the NFIT, but that support
has since been abandoned and never shipped in a product.
Supporting Documents
--------------------
ACPI 6:
https://www.uefi.org/sites/default/files/resources/ACPI_6.0.pdf
NVDIMM Namespace:
https://pmem.io/documents/NVDIMM_Namespace_Spec.pdf
DSM Interface Example:
https://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf
Driver Writer's Guide:
https://pmem.io/documents/NVDIMM_Driver_Writers_Guide.pdf
Git Trees
---------
LIBNVDIMM:
https://git.kernel.org/cgit/linux/kernel/git/nvdimm/nvdimm.git
LIBNDCTL:
https://github.com/pmem/ndctl.git
NFIT PMEM과 namespace label
123-152NFIT 이전에는 비휘발성 메모리를 여러 ad-hoc 방식으로 시스템에 기술했습니다. 대개 시스템 전원이 끊겨도 쓰기가 유지된다고 기대되는 하나의 system-physical-address 범위만 제공했습니다. NFIT는 PMEM 기술뿐 아니라 제어와 구성에 쓰는 platform message-passing entry point도 표준화합니다.
`PMEM`, 즉 `nd_pmem.ko`는 system-physical-address 범위를 구동합니다. 이 범위는 system memory에서 연속이며 여러 DIMM에 걸쳐 interleave, 즉 hardware memory controller striping될 수 있습니다. Interleave인 경우 platform은 참여 DIMM의 세부 정보를 선택적으로 제공할 수 있습니다.
Label capability가 감지되어 EFI namespace label index block을 찾으면 기본적으로 block device를 생성하지 않습니다. Userspace가 PMEM 범위에 DPA를 적어도 한 번 할당해야 하기 때문입니다.
반대로 `ND_NAMESPACE_IO` 범위는 등록되자마자 `nd_pmem`에 연결할 수 있습니다. 이 방식을 label-less 또는 legacy mode라고 부릅니다.
Application이나 filesystem이 여전히 원자적 sector update를 요구하면 PMEM device 또는 partition에 BTT를 등록할 수 있습니다.
LIBNVDIMM PMEM
==============
Prior to the arrival of the NFIT, non-volatile memory was described to a
system in various ad-hoc ways. Usually only the bare minimum was
provided, namely, a single system-physical-address range where writes
are expected to be durable after a system power loss. Now, the NFIT
specification standardizes not only the description of PMEM, but also
platform message-passing entry points for control and configuration.
PMEM (nd_pmem.ko): Drives a system-physical-address range. This range is
contiguous in system memory and may be interleaved (hardware memory controller
striped) across multiple DIMMs. When interleaved the platform may optionally
provide details of which DIMMs are participating in the interleave.
It is worth noting that when the labeling capability is detected (a EFI
namespace label index block is found), then no block device is created
by default as userspace needs to do at least one allocation of DPA to
the PMEM range. In contrast ND_NAMESPACE_IO ranges, once registered,
can be immediately attached to nd_pmem. This latter mode is called
label-less or "legacy".
PMEM-REGIONs, Atomic Sectors, and DAX
-------------------------------------
For the cases where an application or filesystem still needs atomic sector
update guarantees it can register a BTT on a PMEM device or partition. See
LIBNVDIMM/NDCTL: Block Translation Table "btt"
4-DIMM 예제 플랫폼
153-195이후의 sysfs 예제는 하나의 socket에 4개 DIMM과 2개 memory controller가 있는 플랫폼을 기준으로 합니다. 각 PMEM interleave set은 동적으로 할당된 ID의 region device로 식별합니다.
DIMM0과 DIMM1의 앞부분은 `REGION0`으로 interleave됩니다. 이 REGION0 SPA 범위 대부분에 사용자가 `pm0.0`이라고 이름 붙인 하나의 PMEM namespace가 생성되고, 나머지 interleaved system-physical-address 공간은 다른 namespace용으로 비워 둡니다.
DIMM0과 DIMM1의 마지막 부분에는 DIMM2와 DIMM3까지 함께 걸치는 interleaved system-physical-address 범위인 `REGION1`이 있습니다. REGION1 일부는 `pm1.0`이라는 PMEM namespace에 할당됩니다.
`tools/testing/nvdimm`의 `nfit_test.ko` module을 적재하면 kernel이 이 bus를 `/sys/devices/platform/nfit_test.0` 아래에 제공합니다. 이 module은 LIBNVDIMM과 `acpi_nfit.ko` driver의 unit test입니다.
원문의 ASCII 플랫폼 그림을 controller, DIMM, region, namespace 관계로 구조화했습니다.
Example NVDIMM Platform
=======================
For the remainder of this document the following diagram will be
referenced for any example sysfs layouts::
(a) (b) DIMM
+-------------------+--------+--------+--------+
+------+ | pm0.0 | free | pm1.0 | free | 0
| imc0 +--+- - - region0- - - +--------+ +--------+
+--+---+ | pm0.0 | free | pm1.0 | free | 1
| +-------------------+--------v v--------+
+--+---+ | |
| cpu0 | region1
+--+---+ | |
| +----------------------------^ ^--------+
+--+---+ | free | pm1.0 | free | 2
| imc1 +--+----------------------------| +--------+
+------+ | free | pm1.0 | free | 3
+----------------------------+--------+--------+
In this platform we have four DIMMs and two memory controllers in one
socket. Each PMEM interleave set is identified by a region device with
a dynamically assigned id.
1. The first portion of DIMM0 and DIMM1 are interleaved as REGION0. A
single PMEM namespace is created in the REGION0-SPA-range that spans most
of DIMM0 and DIMM1 with a user-specified name of "pm0.0". Some of that
interleaved system-physical-address range is left free for
another PMEM namespace to be defined.
2. In the last portion of DIMM0 and DIMM1 we have an interleaved
system-physical-address range, REGION1, that spans those two DIMMs as
well as DIMM2 and DIMM3. Some of REGION1 is allocated to a PMEM namespace
named "pm1.0".
This bus is provided by the kernel under the device
/sys/devices/platform/nfit_test.0 when the nfit_test.ko module from
tools/testing/nvdimm is loaded. This module is a unit test for
LIBNVDIMM and the acpi_nfit.ko driver.
Device model과 LIBNDCTL context
196-226이후 내용은 LIBNVDIMM sysfs layout과 LIBNDCTL API에서 본 대응 object hierarchy를 설명합니다. Sysfs path와 diagram은 앞의 Example NVDIMM Platform을 기준으로 하며, 이 플랫폼은 LIBNDCTL unit test가 사용하는 LIBNVDIMM bus이기도 합니다.
LIBNDCTL library의 모든 API call은 logging parameter와 그 밖의 library instance state를 보관하는 context를 요구합니다. Library는 `libabc` template을 기반으로 합니다.
예제는 `struct ndctl_ctx *ctx`를 선언하고 `ndctl_new(&ctx)`가 0을 반환하면 새 context를 반환하며, 실패하면 `NULL`을 반환합니다.
하나의 context가 이후 bus, DIMM, region, namespace enumeration의 library 상태를 보관합니다.
LIBNVDIMM Kernel Device Model and LIBNDCTL Userspace API
========================================================
What follows is a description of the LIBNVDIMM sysfs layout and a
corresponding object hierarchy diagram as viewed through the LIBNDCTL
API. The example sysfs paths and diagrams are relative to the Example
NVDIMM Platform which is also the LIBNVDIMM bus used in the LIBNDCTL unit
test.
LIBNDCTL: Context
-----------------
Every API call in the LIBNDCTL library requires a context that holds the
logging parameters and other library instance state. The library is
based on the libabc template:
https://git.kernel.org/cgit/linux/kernel/git/kay/libabc.git
LIBNDCTL: instantiate a new library context example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
struct ndctl_ctx *ctx;
if (ndctl_new(&ctx) == 0)
return ctx;
else
return NULL;
NFIT bus와 control device
227-298Bus와 NFIT는 1:1 관계입니다. ACPI 기반 시스템에는 platform-global NFIT가 하나만 있을 것으로 예상하지만, specification은 여러 NFIT 등록을 금지하지 않습니다. Infrastructure는 multiple bus를 지원하고 unit test는 이를 이용해 여러 NFIT 구성을 시험합니다.
`/sys/class/nd/ndctl0` character device는 NFIT handle로 식별한 DIMM에 DSM message를 전달받습니다. `device` symlink는 `ndbus0`을 가리킵니다.
Kernel provider는 `nvdimm_bus_register(parent, nfit_desc)`로 bus를 등록합니다. 예제 `ndbus0` 아래에는 `commands`, `nd`, `nfit`, `nmem0`부터 `nmem3`, `provider`, `region0`부터 `region5`, `wait_probe` 등이 노출됩니다.
LIBNDCTL enumeration 예제는 `ndctl_bus_foreach(ctx, bus)`로 모든 bus를 순회하고 `ndctl_bus_get_provider(bus)`가 요청한 provider 문자열과 같은 bus를 반환합니다. 예제 provider는 `nfit_test.0`입니다.
Kernel의 NFIT bus가 control device와 nmem/region 자식을 만들고 LIBNDCTL이 provider로 찾습니다.
LIBNVDIMM/LIBNDCTL: Bus
-----------------------
A bus has a 1:1 relationship with an NFIT. The current expectation for
ACPI based systems is that there is only ever one platform-global NFIT.
That said, it is trivial to register multiple NFITs, the specification
does not preclude it. The infrastructure supports multiple buses and
we use this capability to test multiple NFIT configurations in the unit
test.
LIBNVDIMM: control class device in /sys/class
---------------------------------------------
This character device accepts DSM messages to be passed to DIMM
identified by its NFIT handle::
/sys/class/nd/ndctl0
|-- dev
|-- device -> ../../../ndbus0
|-- subsystem -> ../../../../../../../class/nd
LIBNVDIMM: bus
--------------
::
struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
struct nvdimm_bus_descriptor *nfit_desc);
::
/sys/devices/platform/nfit_test.0/ndbus0
|-- commands
|-- nd
|-- nfit
|-- nmem0
|-- nmem1
|-- nmem2
|-- nmem3
|-- power
|-- provider
|-- region0
|-- region1
|-- region2
|-- region3
|-- region4
|-- region5
|-- uevent
`-- wait_probe
LIBNDCTL: bus enumeration example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Find the bus handle that describes the bus from Example NVDIMM Platform::
static struct ndctl_bus *get_bus_by_provider(struct ndctl_ctx *ctx,
const char *provider)
{
struct ndctl_bus *bus;
ndctl_bus_foreach(ctx, bus)
if (strcmp(provider, ndctl_bus_get_provider(bus)) == 0)
return bus;
return NULL;
}
bus = get_bus_by_provider(ctx, "nfit_test.0");
DIMM 또는 NMEM 객체
299-379DIMM device는 hardware에 command를 보내는 character device를 제공하고 LABEL의 container 역할을 합니다. NFIT가 DIMM을 정의한 경우 선택적인 `nfit` attribute subdirectory에서 NFIT-specific 정보를 추가로 노출합니다.
Kernel device name은 DIMM이 아니라 `nmemX`입니다. NFIT는 Memory Device to System Physical Address Range Mapping Structure로 이 장치를 기술하며 실제 physical DIMM일 필요가 없으므로 더 일반적인 이름을 사용합니다.
Provider는 `nvdimm_create()`로 nmem device를 생성합니다. 예제 `nmem0`에는 `available_slots`, `commands`, character `dev`, `state`와 함께 `nfit/device`, `format`, `handle`, `phys_id`, `rev_id`, `serial`, `vendor`가 있습니다.
NFIT-defined DIMM의 `nfit_handle`은 32비트입니다. Bits 3:0은 memory channel 안의 DIMM number, 7:4는 memory channel number, 11:8은 memory controller ID, 15:12는 socket ID, 27:16은 Node Controller ID, 31:28은 reserved입니다.
LIBNDCTL 예제는 `ndctl_dimm_foreach(bus, dimm)`로 DIMM을 순회하고 `ndctl_dimm_get_handle(dimm)`이 요청한 handle과 같은 객체를 찾습니다. `DIMM_HANDLE(n, s, i, c, d)` macro는 각 필드를 해당 bit 위치에 조합합니다.
LIBNVDIMM/LIBNDCTL: DIMM (NMEM)
-------------------------------
The DIMM device provides a character device for sending commands to
hardware, and it is a container for LABELs. If the DIMM is defined by
NFIT then an optional 'nfit' attribute sub-directory is available to add
NFIT-specifics.
Note that the kernel device name for "DIMMs" is "nmemX". The NFIT
describes these devices via "Memory Device to System Physical Address
Range Mapping Structure", and there is no requirement that they actually
be physical DIMMs, so we use a more generic name.
LIBNVDIMM: DIMM (NMEM)
^^^^^^^^^^^^^^^^^^^^^^
::
struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
const struct attribute_group **groups, unsigned long flags,
unsigned long *dsm_mask);
::
/sys/devices/platform/nfit_test.0/ndbus0
|-- nmem0
| |-- available_slots
| |-- commands
| |-- dev
| |-- devtype
| |-- driver -> ../../../../../bus/nd/drivers/nvdimm
| |-- modalias
| |-- nfit
| | |-- device
| | |-- format
| | |-- handle
| | |-- phys_id
| | |-- rev_id
| | |-- serial
| | `-- vendor
| |-- state
| |-- subsystem -> ../../../../../bus/nd
| `-- uevent
|-- nmem1
[..]
LIBNDCTL: DIMM enumeration example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note, in this example we are assuming NFIT-defined DIMMs which are
identified by an "nfit_handle" a 32-bit value where:
- Bit 3:0 DIMM number within the memory channel
- Bit 7:4 memory channel number
- Bit 11:8 memory controller ID
- Bit 15:12 socket ID (within scope of a Node controller if node
controller is present)
- Bit 27:16 Node Controller ID
- Bit 31:28 Reserved
::
static struct ndctl_dimm *get_dimm_by_handle(struct ndctl_bus *bus,
unsigned int handle)
{
struct ndctl_dimm *dimm;
ndctl_dimm_foreach(bus, dimm)
if (ndctl_dimm_get_handle(dimm) == handle)
return dimm;
return NULL;
}
#define DIMM_HANDLE(n, s, i, c, d) \
(((n & 0xfff) << 16) | ((s & 0xf) << 12) | ((i & 0xf) << 8) \
| ((c & 0xf) << 4) | (d & 0xf))
dimm = get_dimm_by_handle(bus, DIMM_HANDLE(0, 0, 0, 0, 0));
PMEM Region과 mapping
380-454각 PMEM interleave set 또는 range마다 generic REGION device를 등록합니다. 예제 `nfit_test.0` bus에는 두 PMEM region이 있습니다. Region의 주요 역할은 mapping의 container가 되는 것입니다.
하나의 mapping은 `<DIMM, DPA-start-offset, length>` tuple입니다. LIBNVDIMM의 내장 REGION driver는 LABEL이 있으면 모두 parse하고, `nd_pmem` driver가 사용할 NAMESPACE device를 내보냅니다.
REGION은 `mappings`, `interleave_ways`, `size` 같은 generic attribute 외에도 편의 attribute를 제공합니다. `nstype`은 이 region이 내보내는 namespace-device의 integer type이고, `devtype`과 `modalias`는 udev가 add event 때 저장한 DEVTYPE과 MODALIAS를 복제합니다. SPA가 region을 정의한 경우 선택적인 `spa_index`도 제공합니다.
PMEM region은 `nvdimm_pmem_region_create()`로 생성합니다. Sysfs에는 `available_size`, `btt_seed`, `namespace_seed`, `mapping0`, `mapping1`, `mappings`, `namespace0.0`, `numa_node`, `nstype`, `set_cookie`, `size` 등이 나타납니다.
LIBNDCTL 예제는 PMEM type이 아닌 region을 건너뛰고 `ndctl_region_get_spa_index(region)`가 요청한 NFIT 고유 interleave-set ID와 같은 region을 반환합니다.
LIBNVDIMM/LIBNDCTL: Region
--------------------------
A generic REGION device is registered for each PMEM interleave-set /
range. Per the example there are 2 PMEM regions on the "nfit_test.0"
bus. The primary role of regions are to be a container of "mappings". A
mapping is a tuple of <DIMM, DPA-start-offset, length>.
LIBNVDIMM provides a built-in driver for REGION devices. This driver
is responsible for all parsing LABELs, if present, and then emitting NAMESPACE
devices for the nd_pmem driver to consume.
In addition to the generic attributes of "mapping"s, "interleave_ways"
and "size" the REGION device also exports some convenience attributes.
"nstype" indicates the integer type of namespace-device this region
emits, "devtype" duplicates the DEVTYPE variable stored by udev at the
'add' event, "modalias" duplicates the MODALIAS variable stored by udev
at the 'add' event, and finally, the optional "spa_index" is provided in
the case where the region is defined by a SPA.
LIBNVDIMM: region::
struct nd_region *nvdimm_pmem_region_create(struct nvdimm_bus *nvdimm_bus,
struct nd_region_desc *ndr_desc);
::
/sys/devices/platform/nfit_test.0/ndbus0
|-- region0
| |-- available_size
| |-- btt0
| |-- btt_seed
| |-- devtype
| |-- driver -> ../../../../../bus/nd/drivers/nd_region
| |-- init_namespaces
| |-- mapping0
| |-- mapping1
| |-- mappings
| |-- modalias
| |-- namespace0.0
| |-- namespace_seed
| |-- numa_node
| |-- nfit
| | `-- spa_index
| |-- nstype
| |-- set_cookie
| |-- size
| |-- subsystem -> ../../../../../bus/nd
| `-- uevent
|-- region1
[..]
LIBNDCTL: region enumeration example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Sample region retrieval routines based on NFIT-unique data like
"spa_index" (interleave set id).
::
static struct ndctl_region *get_pmem_region_by_spa_index(struct ndctl_bus *bus,
unsigned int spa_index)
{
struct ndctl_region *region;
ndctl_region_foreach(bus, region) {
if (ndctl_region_get_type(region) != ND_DEVICE_REGION_PMEM)
continue;
if (ndctl_region_get_spa_index(region) == spa_index)
return region;
}
return NULL;
}
Namespace 유형과 sysfs layout
455-498REGION은 DPA aliasing과 LABEL이 지정한 boundary를 해결한 뒤 하나 이상의 namespace device를 노출합니다. 현재 namespace device가 도착하면 `nd_pmem` driver가 load되어 disk 또는 block device를 등록합니다.
예제에는 두 주요 NAMESPACE 유형이 있습니다. `namespace0.0`은 DIMM-info-backed PMEM이며 LABEL을 지원하므로 `uuid` attribute가 있습니다. `namespace1.0`은 anonymous PMEM namespace이며 LABEL을 지원하지 않아 `uuid`가 없습니다.
Label-backed namespace에는 `alt_name`, `dpa_extents`, `force_raw`, `resource`, `size`, `type`, `uuid` 등이 있습니다. Anonymous namespace는 `block/pmem0`과 pmem driver binding을 포함하고 `force_raw`, `resource`, `size`, `type`을 노출합니다.
LIBNVDIMM/LIBNDCTL: Namespace
-----------------------------
A REGION, after resolving DPA aliasing and LABEL specified boundaries, surfaces
one or more "namespace" devices. The arrival of a "namespace" device currently
triggers the nd_pmem driver to load and register a disk/block device.
LIBNVDIMM: namespace
^^^^^^^^^^^^^^^^^^^^
Here is a sample layout from the 2 major types of NAMESPACE where namespace0.0
represents DIMM-info-backed PMEM (note that it has a 'uuid' attribute), and
namespace1.0 represents an anonymous PMEM namespace (note that has no 'uuid'
attribute due to not support a LABEL)
::
/sys/devices/platform/nfit_test.0/ndbus0/region0/namespace0.0
|-- alt_name
|-- devtype
|-- dpa_extents
|-- force_raw
|-- modalias
|-- numa_node
|-- resource
|-- size
|-- subsystem -> ../../../../../../bus/nd
|-- type
|-- uevent
`-- uuid
/sys/devices/platform/nfit_test.1/ndbus1/region1/namespace1.0
|-- block
| `-- pmem0
|-- devtype
|-- driver -> ../../../../../../bus/nd/drivers/pmem
|-- force_raw
|-- modalias
|-- numa_node
|-- resource
|-- size
|-- subsystem -> ../../../../../../bus/nd
|-- type
`-- uevent
Namespace 열거와 생성
499-550Namespace index는 parent region 기준입니다. Boot 사이에 대체로 유지되지만 subsystem은 이를 보장하지 않습니다. 정적인 namespace identifier가 필요하면 `uuid` attribute를 사용해야 합니다.
Enumeration 예제는 `ndctl_namespace_foreach(region, ndns)`로 region의 namespace를 순회하고 `ndctl_namespace_get_id(ndns)`가 요청한 ID와 같은 객체를 반환합니다.
Region에 새 namespace를 만들 충분한 available capacity가 있으면 kernel이 idle namespace를 자동으로 생성합니다. 실제 namespace 생성은 이 idle namespace를 찾아 구성하는 과정입니다.
대부분의 namespace attribute는 어떤 순서로 설정해도 되지만 `uuid`는 반드시 `size`보다 먼저 설정해야 합니다. 그래야 kernel이 정적인 identifier로 내부 DPA allocation을 추적할 수 있습니다.
예제는 region ID와 요청 ID로 `namespace%d.%d` 이름을 만들고 alt name을 설정한 뒤 UUID와 size를 순서대로 기록합니다. PMEM namespace와 달리 BLK namespace에는 sector size가 있으므로 `lbasize`가 있으면 설정하고 마지막에 namespace를 enable합니다.
`uuid`를 `size`보다 먼저 기록하는 것이 유일한 속성 순서 제약입니다.
LIBNDCTL: namespace enumeration example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Namespaces are indexed relative to their parent region, example below.
These indexes are mostly static from boot to boot, but subsystem makes
no guarantees in this regard. For a static namespace identifier use its
'uuid' attribute.
::
static struct ndctl_namespace
*get_namespace_by_id(struct ndctl_region *region, unsigned int id)
{
struct ndctl_namespace *ndns;
ndctl_namespace_foreach(region, ndns)
if (ndctl_namespace_get_id(ndns) == id)
return ndns;
return NULL;
}
LIBNDCTL: namespace creation example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Idle namespaces are automatically created by the kernel if a given
region has enough available capacity to create a new namespace.
Namespace instantiation involves finding an idle namespace and
configuring it. For the most part the setting of namespace attributes
can occur in any order, the only constraint is that 'uuid' must be set
before 'size'. This enables the kernel to track DPA allocations
internally with a static identifier::
static int configure_namespace(struct ndctl_region *region,
struct ndctl_namespace *ndns,
struct namespace_parameters *parameters)
{
char devname[50];
snprintf(devname, sizeof(devname), "namespace%d.%d",
ndctl_region_get_id(region), parameters->id);
ndctl_namespace_set_alt_name(ndns, devname);
/* 'uuid' must be set prior to setting size! */
ndctl_namespace_set_uuid(ndns, parameters->uuid);
ndctl_namespace_set_size(ndns, parameters->size);
/* unlike pmem namespaces, blk namespaces have a sector size */
if (parameters->lbasize)
ndctl_namespace_set_sector_size(ndns, parameters->lbasize);
ndctl_namespace_enable(ndns);
}
왜 namespace라고 부르는가
551-563`volume`이라는 이름을 쓰지 않은 첫 번째 이유는 ND, 즉 libnvdimm subsystem이 device-mapper 같은 volume manager로 오해될 위험이 있기 때문입니다.
`namespace`라는 용어는 NVMe controller 안에 생성할 수 있는 sub-device를 설명하기 위해 처음 사용되었습니다. NFIT namespace는 NVMe namespace의 capability와 configurability를 본뜨도록 설계되었습니다.
Volume-manager 의미를 피하고 NVMe의 sub-device 모델을 따릅니다.
Why the Term "namespace"?
^^^^^^^^^^^^^^^^^^^^^^^^^
1. Why not "volume" for instance? "volume" ran the risk of confusing
ND (libnvdimm subsystem) to a volume manager like device-mapper.
2. The term originated to describe the sub-devices that can be created
within a NVME controller (see the nvme specification:
https://www.nvmexpress.org/specifications/), and NFIT namespaces are
meant to parallel the capabilities and configurability of
NVME-namespaces.
BTT seed와 lifecycle
564-638BTT는 namespace 전체 앞에 address abstraction으로 놓이는 namespace personality driver입니다.
모든 region에는 처음부터 seed device인 BTT device가 적어도 하나 있습니다. 이를 활성화하려면 `namespace`, `uuid`, `sector_size` attribute를 설정한 뒤 region type에 따라 장치를 `nd_pmem` 또는 `nd_blk` driver에 bind합니다.
Region마다 idle BTT device가 자동으로 만들어집니다. Seed BTT를 구성하고 enable할 때마다 새 seed가 생성됩니다. BTT configuration은 idle BTT를 찾고 이를 namespace를 소비하도록 할당하는 두 단계로 이뤄집니다.
예제 `get_idle_btt()`는 region의 BTT를 순회해 enabled도 configured도 아닌 객체를 찾습니다. `configure_btt()`는 UUID와 sector size, namespace를 설정하고 raw-mode namespace를 disable한 뒤 BTT access를 enable합니다.
BTT에서 `namespace`를 제거하면 해당 BTT device instance가 삭제되거나 기본값으로 reset됩니다. 이는 device-model 수준의 삭제일 뿐입니다.
BTT 자체를 파괴하려면 media의 `info block`을 지워야 하고, 이를 위해 raw mode로 media에 써야 합니다. Kernel은 기본적으로 BTT 존재를 autodetect해 raw mode를 disable합니다. `ndctl_namespace_set_raw_mode()` API로 namespace의 raw mode를 enable하면 이 autodetect 동작을 억제할 수 있습니다.
Seed를 구성하면 활성 BTT와 다음 idle seed가 생기며, 완전한 파괴에는 raw-mode info-block 삭제가 필요합니다.
LIBNVDIMM/LIBNDCTL: Block Translation Table "btt"
-------------------------------------------------
A BTT (design document: https://pmem.io/2014/09/23/btt.html) is a
personality driver for a namespace that fronts entire namespace as an
'address abstraction'.
LIBNVDIMM: btt layout
^^^^^^^^^^^^^^^^^^^^^
Every region will start out with at least one BTT device which is the
seed device. To activate it set the "namespace", "uuid", and
"sector_size" attributes and then bind the device to the nd_pmem or
nd_blk driver depending on the region type::
/sys/devices/platform/nfit_test.1/ndbus0/region0/btt0/
|-- namespace
|-- delete
|-- devtype
|-- modalias
|-- numa_node
|-- sector_size
|-- subsystem -> ../../../../../bus/nd
|-- uevent
`-- uuid
LIBNDCTL: btt creation example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Similar to namespaces an idle BTT device is automatically created per
region. Each time this "seed" btt device is configured and enabled a new
seed is created. Creating a BTT configuration involves two steps of
finding and idle BTT and assigning it to consume a namespace.
::
static struct ndctl_btt *get_idle_btt(struct ndctl_region *region)
{
struct ndctl_btt *btt;
ndctl_btt_foreach(region, btt)
if (!ndctl_btt_is_enabled(btt)
&& !ndctl_btt_is_configured(btt))
return btt;
return NULL;
}
static int configure_btt(struct ndctl_region *region,
struct btt_parameters *parameters)
{
btt = get_idle_btt(region);
ndctl_btt_set_uuid(btt, parameters->uuid);
ndctl_btt_set_sector_size(btt, parameters->sector_size);
ndctl_btt_set_namespace(btt, parameters->ndns);
/* turn off raw mode device */
ndctl_namespace_disable(parameters->ndns);
/* turn on btt access */
ndctl_btt_enable(btt);
}
Once instantiated a new inactive btt seed device will appear underneath
the region.
Once a "namespace" is removed from a BTT that instance of the BTT device
will be deleted or otherwise reset to default values. This deletion is
only at the device model level. In order to destroy a BTT the "info
block" needs to be destroyed. Note, that to destroy a BTT the media
needs to be written in raw mode. By default, the kernel will autodetect
the presence of a BTT and disable raw mode. This autodetect behavior
can be suppressed by enabling raw mode for the namespace via the
ndctl_namespace_set_raw_mode() API.
LIBNDCTL 객체 그래프
639-657예제 플랫폼을 LIBNDCTL API에서 보면 최상위 `CTX`가 `BUS0`을 소유하고, BUS0은 DIMM0부터 DIMM3 및 REGION0과 REGION1을 열거합니다.
REGION0은 `NAMESPACE0.0`을 거쳐 `PMEM8 "pm0.0"` block device로 이어집니다.
REGION1은 `NAMESPACE1.0`을 거쳐 `PMEM6 "pm1.0"`으로 이어지며, 같은 namespace 앞에 `BTT1` address abstraction이 연결됩니다.
원문의 최종 ASCII 그래프를 context에서 block device까지의 객체 경로로 구조화했습니다.
Summary LIBNDCTL Diagram
------------------------
For the given example above, here is the view of the objects as seen by the
LIBNDCTL API::
+---+
|CTX|
+-+-+
|
+-------+ |
| DIMM0 <-+ | +---------+ +--------------+ +---------------+
+-------+ | | +-> REGION0 +---> NAMESPACE0.0 +--> PMEM8 "pm0.0" |
| DIMM1 <-+ +-v--+ | +---------+ +--------------+ +---------------+
+-------+ +-+BUS0+-| +---------+ +--------------+ +----------------------+
| DIMM2 <-+ +----+ +-> REGION1 +---> NAMESPACE1.0 +--> PMEM6 "pm1.0" | BTT1 |
+-------+ | | +---------+ +--------------+ +---------------+------+
| DIMM3 <-+
+-------+
요약과 해설
nvdimm.rst:1-657LIBNVDIMM은 firmware가 기술한 PMEM을 bus, nmem, region, namespace 계층으로 모델링하고 LIBNDCTL은 같은 객체를 userspace에서 열거·구성합니다. Label-backed DPA allocation, legacy namespace, DAX와 BTT address abstraction의 관계를 예제 sysfs와 코드로 설명합니다.