Documentation/driver-api/nvdimm/nvdimm.rst GitHub 원문 ↗

Linux 6.18.37 · Driver API

LIBNVDIMM: Non-Volatile Devices

LIBNVDIMM의 PMEM, NFIT, DIMM, region, namespace와 BTT 객체 모델 및 libndctl API의 전문 번역입니다.

Source pathDocumentation/driver-api/nvdimm/nvdimm.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약과 해설

nvdimm.rst:1-657

LIBNVDIMM은 firmware가 기술한 PMEM을 bus, nmem, region, namespace 계층으로 모델링하고 LIBNDCTL은 같은 객체를 userspace에서 열거·구성합니다. Label-backed DPA allocation, legacy namespace, DAX와 BTT address abstraction의 관계를 예제 sysfs와 코드로 설명합니다.

문서 구성
원문 줄내용
1-122문서 구성, 용어, subsystem 개요
123-195PMEM mode와 예제 플랫폼
196-298LIBNDCTL context와 bus
299-454DIMM/NMEM과 region
455-563Namespace layout·생성·명칭
564-638BTT seed와 lifecycle
639-657최종 객체 그래프

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 ===============================
2 LIBNVDIMM: Non-Volatile Devices
3 ===============================
4
5 libnvdimm - kernel / libndctl - userspace helper library
6
7 nvdimm@lists.linux.dev
8
9 Version 13
10
11 .. contents:
12
13 Glossary
14 Overview
15 Supporting Documents
16 Git Trees
17 LIBNVDIMM PMEM
18 PMEM-REGIONs, Atomic Sectors, and DAX
19 Example NVDIMM Platform
20 LIBNVDIMM Kernel Device Model and LIBNDCTL Userspace API
21 LIBNDCTL: Context
22 libndctl: instantiate a new library context example
23 LIBNVDIMM/LIBNDCTL: Bus
24 libnvdimm: control class device in /sys/class
25 libnvdimm: bus
26 libndctl: bus enumeration example
27 LIBNVDIMM/LIBNDCTL: DIMM (NMEM)
28 libnvdimm: DIMM (NMEM)
29 libndctl: DIMM enumeration example
30 LIBNVDIMM/LIBNDCTL: Region
31 libnvdimm: region
32 libndctl: region enumeration example
33 Why Not Encode the Region Type into the Region Name?
34 How Do I Determine the Major Type of a Region?
35 LIBNVDIMM/LIBNDCTL: Namespace
36 libnvdimm: namespace
37 libndctl: namespace enumeration example
38 libndctl: namespace creation example
39 Why the Term "namespace"?
40 LIBNVDIMM/LIBNDCTL: Block Translation Table "btt"
41 libnvdimm: btt layout
42 libndctl: btt creation example
43 Summary LIBNDCTL Diagram
44
45
46 Glossary
47 ========
48
49 PMEM:
50 A system-physical-address range where writes are persistent. A
51 block device composed of PMEM is capable of DAX. A PMEM address range
52 may span an interleave of several DIMMs.
53
54 DPA:
55 DIMM Physical Address, is a DIMM-relative offset. With one DIMM in
56 the system there would be a 1:1 system-physical-address:DPA association.
57 Once more DIMMs are added a memory controller interleave must be
58 decoded to determine the DPA associated with a given
59 system-physical-address.
60
61 DAX:
62 File system extensions to bypass the page cache and block layer to
63 mmap persistent memory, from a PMEM block device, directly into a
64 process address space.
65
66 DSM:
67 Device Specific Method: ACPI method to control specific
68 device - in this case the firmware.
69
70 DCR:
71 NVDIMM Control Region Structure defined in ACPI 6 Section 5.2.25.5.
72 It defines a vendor-id, device-id, and interface format for a given DIMM.
73
74 BTT:
75 Block Translation Table: Persistent memory is byte addressable.
76 Existing software may have an expectation that the power-fail-atomicity
77 of writes is at least one sector, 512 bytes. The BTT is an indirection
78 table with atomic update semantics to front a PMEM block device
79 driver and present arbitrary atomic sector sizes.
80
81 LABEL:
82 Metadata stored on a DIMM device that partitions and identifies
83 (persistently names) capacity allocated to different PMEM namespaces. It
84 also indicates whether an address abstraction like a BTT is applied to
85 the namespace. Note that traditional partition tables, GPT/MBR, are
86 layered on top of a PMEM namespace, or an address abstraction like BTT
87 if present, but partition support is deprecated going forward.
88
89
90 Overview
91 ========
92
93 The LIBNVDIMM subsystem provides support for PMEM described by platform
94 firmware or a device driver. On ACPI based systems the platform firmware
95 conveys persistent memory resource via the ACPI NFIT "NVDIMM Firmware
96 Interface Table" in ACPI 6. While the LIBNVDIMM subsystem implementation
97 is generic and supports pre-NFIT platforms, it was guided by the
98 superset of capabilities need to support this ACPI 6 definition for
99 NVDIMM resources. The original implementation supported the
100 block-window-aperture capability described in the NFIT, but that support
101 has since been abandoned and never shipped in a product.
102
103 Supporting Documents
104 --------------------
105
106 ACPI 6:
107 https://www.uefi.org/sites/default/files/resources/ACPI_6.0.pdf
108 NVDIMM Namespace:
109 https://pmem.io/documents/NVDIMM_Namespace_Spec.pdf
110 DSM Interface Example:
111 https://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf
112 Driver Writer's Guide:
113 https://pmem.io/documents/NVDIMM_Driver_Writers_Guide.pdf
114
115 Git Trees
116 ---------
117
118 LIBNVDIMM:
119 https://git.kernel.org/cgit/linux/kernel/git/nvdimm/nvdimm.git
120 LIBNDCTL:
121 https://github.com/pmem/ndctl.git
122
123
124 LIBNVDIMM PMEM
125 ==============
126
127 Prior to the arrival of the NFIT, non-volatile memory was described to a
128 system in various ad-hoc ways. Usually only the bare minimum was
129 provided, namely, a single system-physical-address range where writes
130 are expected to be durable after a system power loss. Now, the NFIT
131 specification standardizes not only the description of PMEM, but also
132 platform message-passing entry points for control and configuration.
133
134 PMEM (nd_pmem.ko): Drives a system-physical-address range. This range is
135 contiguous in system memory and may be interleaved (hardware memory controller
136 striped) across multiple DIMMs. When interleaved the platform may optionally
137 provide details of which DIMMs are participating in the interleave.
138
139 It is worth noting that when the labeling capability is detected (a EFI
140 namespace label index block is found), then no block device is created
141 by default as userspace needs to do at least one allocation of DPA to
142 the PMEM range. In contrast ND_NAMESPACE_IO ranges, once registered,
143 can be immediately attached to nd_pmem. This latter mode is called
144 label-less or "legacy".
145
146 PMEM-REGIONs, Atomic Sectors, and DAX
147 -------------------------------------
148
149 For the cases where an application or filesystem still needs atomic sector
150 update guarantees it can register a BTT on a PMEM device or partition. See
151 LIBNVDIMM/NDCTL: Block Translation Table "btt"
152
153
154 Example NVDIMM Platform
155 =======================
156
157 For the remainder of this document the following diagram will be
158 referenced for any example sysfs layouts::
159
160
161 (a) (b) DIMM
162 +-------------------+--------+--------+--------+
163 +------+ | pm0.0 | free | pm1.0 | free | 0
164 | imc0 +--+- - - region0- - - +--------+ +--------+
165 +--+---+ | pm0.0 | free | pm1.0 | free | 1
166 | +-------------------+--------v v--------+
167 +--+---+ | |
168 | cpu0 | region1
169 +--+---+ | |
170 | +----------------------------^ ^--------+
171 +--+---+ | free | pm1.0 | free | 2
172 | imc1 +--+----------------------------| +--------+
173 +------+ | free | pm1.0 | free | 3
174 +----------------------------+--------+--------+
175
176 In this platform we have four DIMMs and two memory controllers in one
177 socket. Each PMEM interleave set is identified by a region device with
178 a dynamically assigned id.
179
180 1. The first portion of DIMM0 and DIMM1 are interleaved as REGION0. A
181 single PMEM namespace is created in the REGION0-SPA-range that spans most
182 of DIMM0 and DIMM1 with a user-specified name of "pm0.0". Some of that
183 interleaved system-physical-address range is left free for
184 another PMEM namespace to be defined.
185
186 2. In the last portion of DIMM0 and DIMM1 we have an interleaved
187 system-physical-address range, REGION1, that spans those two DIMMs as
188 well as DIMM2 and DIMM3. Some of REGION1 is allocated to a PMEM namespace
189 named "pm1.0".
190
191 This bus is provided by the kernel under the device
192 /sys/devices/platform/nfit_test.0 when the nfit_test.ko module from
193 tools/testing/nvdimm is loaded. This module is a unit test for
194 LIBNVDIMM and the acpi_nfit.ko driver.
195
196
197 LIBNVDIMM Kernel Device Model and LIBNDCTL Userspace API
198 ========================================================
199
200 What follows is a description of the LIBNVDIMM sysfs layout and a
201 corresponding object hierarchy diagram as viewed through the LIBNDCTL
202 API. The example sysfs paths and diagrams are relative to the Example
203 NVDIMM Platform which is also the LIBNVDIMM bus used in the LIBNDCTL unit
204 test.
205
206 LIBNDCTL: Context
207 -----------------
208
209 Every API call in the LIBNDCTL library requires a context that holds the
210 logging parameters and other library instance state. The library is
211 based on the libabc template:
212
213 https://git.kernel.org/cgit/linux/kernel/git/kay/libabc.git
214
215 LIBNDCTL: instantiate a new library context example
216 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
217
218 ::
219
220 struct ndctl_ctx *ctx;
221
222 if (ndctl_new(&ctx) == 0)
223 return ctx;
224 else
225 return NULL;
226
227 LIBNVDIMM/LIBNDCTL: Bus
228 -----------------------
229
230 A bus has a 1:1 relationship with an NFIT. The current expectation for
231 ACPI based systems is that there is only ever one platform-global NFIT.
232 That said, it is trivial to register multiple NFITs, the specification
233 does not preclude it. The infrastructure supports multiple buses and
234 we use this capability to test multiple NFIT configurations in the unit
235 test.
236
237 LIBNVDIMM: control class device in /sys/class
238 ---------------------------------------------
239
240 This character device accepts DSM messages to be passed to DIMM
241 identified by its NFIT handle::
242
243 /sys/class/nd/ndctl0
244 |-- dev
245 |-- device -> ../../../ndbus0
246 |-- subsystem -> ../../../../../../../class/nd
247
248
249
250 LIBNVDIMM: bus
251 --------------
252
253 ::
254
255 struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
256 struct nvdimm_bus_descriptor *nfit_desc);
257
258 ::
259
260 /sys/devices/platform/nfit_test.0/ndbus0
261 |-- commands
262 |-- nd
263 |-- nfit
264 |-- nmem0
265 |-- nmem1
266 |-- nmem2
267 |-- nmem3
268 |-- power
269 |-- provider
270 |-- region0
271 |-- region1
272 |-- region2
273 |-- region3
274 |-- region4
275 |-- region5
276 |-- uevent
277 `-- wait_probe
278
279 LIBNDCTL: bus enumeration example
280 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
281
282 Find the bus handle that describes the bus from Example NVDIMM Platform::
283
284 static struct ndctl_bus *get_bus_by_provider(struct ndctl_ctx *ctx,
285 const char *provider)
286 {
287 struct ndctl_bus *bus;
288
289 ndctl_bus_foreach(ctx, bus)
290 if (strcmp(provider, ndctl_bus_get_provider(bus)) == 0)
291 return bus;
292
293 return NULL;
294 }
295
296 bus = get_bus_by_provider(ctx, "nfit_test.0");
297
298
299 LIBNVDIMM/LIBNDCTL: DIMM (NMEM)
300 -------------------------------
301
302 The DIMM device provides a character device for sending commands to
303 hardware, and it is a container for LABELs. If the DIMM is defined by
304 NFIT then an optional 'nfit' attribute sub-directory is available to add
305 NFIT-specifics.
306
307 Note that the kernel device name for "DIMMs" is "nmemX". The NFIT
308 describes these devices via "Memory Device to System Physical Address
309 Range Mapping Structure", and there is no requirement that they actually
310 be physical DIMMs, so we use a more generic name.
311
312 LIBNVDIMM: DIMM (NMEM)
313 ^^^^^^^^^^^^^^^^^^^^^^
314
315 ::
316
317 struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
318 const struct attribute_group **groups, unsigned long flags,
319 unsigned long *dsm_mask);
320
321 ::
322
323 /sys/devices/platform/nfit_test.0/ndbus0
324 |-- nmem0
325 | |-- available_slots
326 | |-- commands
327 | |-- dev
328 | |-- devtype
329 | |-- driver -> ../../../../../bus/nd/drivers/nvdimm
330 | |-- modalias
331 | |-- nfit
332 | | |-- device
333 | | |-- format
334 | | |-- handle
335 | | |-- phys_id
336 | | |-- rev_id
337 | | |-- serial
338 | | `-- vendor
339 | |-- state
340 | |-- subsystem -> ../../../../../bus/nd
341 | `-- uevent
342 |-- nmem1
343 [..]
344
345
346 LIBNDCTL: DIMM enumeration example
347 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
348
349 Note, in this example we are assuming NFIT-defined DIMMs which are
350 identified by an "nfit_handle" a 32-bit value where:
351
352 - Bit 3:0 DIMM number within the memory channel
353 - Bit 7:4 memory channel number
354 - Bit 11:8 memory controller ID
355 - Bit 15:12 socket ID (within scope of a Node controller if node
356 controller is present)
357 - Bit 27:16 Node Controller ID
358 - Bit 31:28 Reserved
359
360 ::
361
362 static struct ndctl_dimm *get_dimm_by_handle(struct ndctl_bus *bus,
363 unsigned int handle)
364 {
365 struct ndctl_dimm *dimm;
366
367 ndctl_dimm_foreach(bus, dimm)
368 if (ndctl_dimm_get_handle(dimm) == handle)
369 return dimm;
370
371 return NULL;
372 }
373
374 #define DIMM_HANDLE(n, s, i, c, d) \
375 (((n & 0xfff) << 16) | ((s & 0xf) << 12) | ((i & 0xf) << 8) \
376 | ((c & 0xf) << 4) | (d & 0xf))
377
378 dimm = get_dimm_by_handle(bus, DIMM_HANDLE(0, 0, 0, 0, 0));
379
380 LIBNVDIMM/LIBNDCTL: Region
381 --------------------------
382
383 A generic REGION device is registered for each PMEM interleave-set /
384 range. Per the example there are 2 PMEM regions on the "nfit_test.0"
385 bus. The primary role of regions are to be a container of "mappings". A
386 mapping is a tuple of <DIMM, DPA-start-offset, length>.
387
388 LIBNVDIMM provides a built-in driver for REGION devices. This driver
389 is responsible for all parsing LABELs, if present, and then emitting NAMESPACE
390 devices for the nd_pmem driver to consume.
391
392 In addition to the generic attributes of "mapping"s, "interleave_ways"
393 and "size" the REGION device also exports some convenience attributes.
394 "nstype" indicates the integer type of namespace-device this region
395 emits, "devtype" duplicates the DEVTYPE variable stored by udev at the
396 'add' event, "modalias" duplicates the MODALIAS variable stored by udev
397 at the 'add' event, and finally, the optional "spa_index" is provided in
398 the case where the region is defined by a SPA.
399
400 LIBNVDIMM: region::
401
402 struct nd_region *nvdimm_pmem_region_create(struct nvdimm_bus *nvdimm_bus,
403 struct nd_region_desc *ndr_desc);
404
405 ::
406
407 /sys/devices/platform/nfit_test.0/ndbus0
408 |-- region0
409 | |-- available_size
410 | |-- btt0
411 | |-- btt_seed
412 | |-- devtype
413 | |-- driver -> ../../../../../bus/nd/drivers/nd_region
414 | |-- init_namespaces
415 | |-- mapping0
416 | |-- mapping1
417 | |-- mappings
418 | |-- modalias
419 | |-- namespace0.0
420 | |-- namespace_seed
421 | |-- numa_node
422 | |-- nfit
423 | | `-- spa_index
424 | |-- nstype
425 | |-- set_cookie
426 | |-- size
427 | |-- subsystem -> ../../../../../bus/nd
428 | `-- uevent
429 |-- region1
430 [..]
431
432 LIBNDCTL: region enumeration example
433 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
434
435 Sample region retrieval routines based on NFIT-unique data like
436 "spa_index" (interleave set id).
437
438 ::
439
440 static struct ndctl_region *get_pmem_region_by_spa_index(struct ndctl_bus *bus,
441 unsigned int spa_index)
442 {
443 struct ndctl_region *region;
444
445 ndctl_region_foreach(bus, region) {
446 if (ndctl_region_get_type(region) != ND_DEVICE_REGION_PMEM)
447 continue;
448 if (ndctl_region_get_spa_index(region) == spa_index)
449 return region;
450 }
451 return NULL;
452 }
453
454
455 LIBNVDIMM/LIBNDCTL: Namespace
456 -----------------------------
457
458 A REGION, after resolving DPA aliasing and LABEL specified boundaries, surfaces
459 one or more "namespace" devices. The arrival of a "namespace" device currently
460 triggers the nd_pmem driver to load and register a disk/block device.
461
462 LIBNVDIMM: namespace
463 ^^^^^^^^^^^^^^^^^^^^
464
465 Here is a sample layout from the 2 major types of NAMESPACE where namespace0.0
466 represents DIMM-info-backed PMEM (note that it has a 'uuid' attribute), and
467 namespace1.0 represents an anonymous PMEM namespace (note that has no 'uuid'
468 attribute due to not support a LABEL)
469
470 ::
471
472 /sys/devices/platform/nfit_test.0/ndbus0/region0/namespace0.0
473 |-- alt_name
474 |-- devtype
475 |-- dpa_extents
476 |-- force_raw
477 |-- modalias
478 |-- numa_node
479 |-- resource
480 |-- size
481 |-- subsystem -> ../../../../../../bus/nd
482 |-- type
483 |-- uevent
484 `-- uuid
485 /sys/devices/platform/nfit_test.1/ndbus1/region1/namespace1.0
486 |-- block
487 | `-- pmem0
488 |-- devtype
489 |-- driver -> ../../../../../../bus/nd/drivers/pmem
490 |-- force_raw
491 |-- modalias
492 |-- numa_node
493 |-- resource
494 |-- size
495 |-- subsystem -> ../../../../../../bus/nd
496 |-- type
497 `-- uevent
498
499 LIBNDCTL: namespace enumeration example
500 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
501 Namespaces are indexed relative to their parent region, example below.
502 These indexes are mostly static from boot to boot, but subsystem makes
503 no guarantees in this regard. For a static namespace identifier use its
504 'uuid' attribute.
505
506 ::
507
508 static struct ndctl_namespace
509 *get_namespace_by_id(struct ndctl_region *region, unsigned int id)
510 {
511 struct ndctl_namespace *ndns;
512
513 ndctl_namespace_foreach(region, ndns)
514 if (ndctl_namespace_get_id(ndns) == id)
515 return ndns;
516
517 return NULL;
518 }
519
520 LIBNDCTL: namespace creation example
521 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
522
523 Idle namespaces are automatically created by the kernel if a given
524 region has enough available capacity to create a new namespace.
525 Namespace instantiation involves finding an idle namespace and
526 configuring it. For the most part the setting of namespace attributes
527 can occur in any order, the only constraint is that 'uuid' must be set
528 before 'size'. This enables the kernel to track DPA allocations
529 internally with a static identifier::
530
531 static int configure_namespace(struct ndctl_region *region,
532 struct ndctl_namespace *ndns,
533 struct namespace_parameters *parameters)
534 {
535 char devname[50];
536
537 snprintf(devname, sizeof(devname), "namespace%d.%d",
538 ndctl_region_get_id(region), parameters->id);
539
540 ndctl_namespace_set_alt_name(ndns, devname);
541 /* 'uuid' must be set prior to setting size! */
542 ndctl_namespace_set_uuid(ndns, parameters->uuid);
543 ndctl_namespace_set_size(ndns, parameters->size);
544 /* unlike pmem namespaces, blk namespaces have a sector size */
545 if (parameters->lbasize)
546 ndctl_namespace_set_sector_size(ndns, parameters->lbasize);
547 ndctl_namespace_enable(ndns);
548 }
549
550
551 Why the Term "namespace"?
552 ^^^^^^^^^^^^^^^^^^^^^^^^^
553
554 1. Why not "volume" for instance? "volume" ran the risk of confusing
555 ND (libnvdimm subsystem) to a volume manager like device-mapper.
556
557 2. The term originated to describe the sub-devices that can be created
558 within a NVME controller (see the nvme specification:
559 https://www.nvmexpress.org/specifications/), and NFIT namespaces are
560 meant to parallel the capabilities and configurability of
561 NVME-namespaces.
562
563
564 LIBNVDIMM/LIBNDCTL: Block Translation Table "btt"
565 -------------------------------------------------
566
567 A BTT (design document: https://pmem.io/2014/09/23/btt.html) is a
568 personality driver for a namespace that fronts entire namespace as an
569 'address abstraction'.
570
571 LIBNVDIMM: btt layout
572 ^^^^^^^^^^^^^^^^^^^^^
573
574 Every region will start out with at least one BTT device which is the
575 seed device. To activate it set the "namespace", "uuid", and
576 "sector_size" attributes and then bind the device to the nd_pmem or
577 nd_blk driver depending on the region type::
578
579 /sys/devices/platform/nfit_test.1/ndbus0/region0/btt0/
580 |-- namespace
581 |-- delete
582 |-- devtype
583 |-- modalias
584 |-- numa_node
585 |-- sector_size
586 |-- subsystem -> ../../../../../bus/nd
587 |-- uevent
588 `-- uuid
589
590 LIBNDCTL: btt creation example
591 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
592
593 Similar to namespaces an idle BTT device is automatically created per
594 region. Each time this "seed" btt device is configured and enabled a new
595 seed is created. Creating a BTT configuration involves two steps of
596 finding and idle BTT and assigning it to consume a namespace.
597
598 ::
599
600 static struct ndctl_btt *get_idle_btt(struct ndctl_region *region)
601 {
602 struct ndctl_btt *btt;
603
604 ndctl_btt_foreach(region, btt)
605 if (!ndctl_btt_is_enabled(btt)
606 && !ndctl_btt_is_configured(btt))
607 return btt;
608
609 return NULL;
610 }
611
612 static int configure_btt(struct ndctl_region *region,
613 struct btt_parameters *parameters)
614 {
615 btt = get_idle_btt(region);
616
617 ndctl_btt_set_uuid(btt, parameters->uuid);
618 ndctl_btt_set_sector_size(btt, parameters->sector_size);
619 ndctl_btt_set_namespace(btt, parameters->ndns);
620 /* turn off raw mode device */
621 ndctl_namespace_disable(parameters->ndns);
622 /* turn on btt access */
623 ndctl_btt_enable(btt);
624 }
625
626 Once instantiated a new inactive btt seed device will appear underneath
627 the region.
628
629 Once a "namespace" is removed from a BTT that instance of the BTT device
630 will be deleted or otherwise reset to default values. This deletion is
631 only at the device model level. In order to destroy a BTT the "info
632 block" needs to be destroyed. Note, that to destroy a BTT the media
633 needs to be written in raw mode. By default, the kernel will autodetect
634 the presence of a BTT and disable raw mode. This autodetect behavior
635 can be suppressed by enabling raw mode for the namespace via the
636 ndctl_namespace_set_raw_mode() API.
637
638
639 Summary LIBNDCTL Diagram
640 ------------------------
641
642 For the given example above, here is the view of the objects as seen by the
643 LIBNDCTL API::
644
645 +---+
646 |CTX|
647 +-+-+
648 |
649 +-------+ |
650 | DIMM0 <-+ | +---------+ +--------------+ +---------------+
651 +-------+ | | +-> REGION0 +---> NAMESPACE0.0 +--> PMEM8 "pm0.0" |
652 | DIMM1 <-+ +-v--+ | +---------+ +--------------+ +---------------+
653 +-------+ +-+BUS0+-| +---------+ +--------------+ +----------------------+
654 | DIMM2 <-+ +----+ +-> REGION1 +---> NAMESPACE1.0 +--> PMEM6 "pm1.0" | BTT1 |
655 +-------+ | | +---------+ +--------------+ +---------------+------+
656 | DIMM3 <-+
657 +-------+
658

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

LIBNVDIMM 문서 구성

1-44

이 문서는 비휘발성 장치를 위한 `LIBNVDIMM`을 설명합니다. 커널 쪽 구성 요소는 `libnvdimm`, userspace helper library는 `libndctl`이며 문의 주소는 `nvdimm@lists.linux.dev`입니다. 문서 버전은 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 객체 그래프로 요약합니다.

문서의 객체 계층
단계주요 내용
Glossary / OverviewPMEM, DPA, DAX, DSM, DCR, BTT, LABEL
PMEMNFIT resource, atomic sector, DAX
Example Platform4 DIMMs, 2 controllers, 2 regions
Contextlibndctl instance state
BusNFIT와 1:1 관계
DIMM / NMEMCommand device와 LABEL container
RegionInterleave set와 mappings
NamespaceDPA allocation과 block device
BTTNamespace address abstraction

===============================
LIBNVDIMM: Non-Volatile Devices
===============================

libnvdimm - kernel / libndctl - userspace helper library

nvdimm@lists.linux.dev

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됩니다.

LIBNVDIMM 용어
용어정의
PMEMPersistent system physical address range
DPADIMM-relative physical-address offset
DAXPage cache와 block layer를 우회한 direct mmap
DSMFirmware 제어용 ACPI Device Specific Method
DCRACPI 6 NVDIMM Control Region Structure
BTT원자적 sector update를 제공하는 indirection table
LABELNamespace allocation, name, abstraction metadata


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-122

LIBNVDIMM 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 저장소도 연결됩니다.

사양과 소스
자료역할
ACPI 6NFIT와 NVDIMM resource 정의
NVDIMM NamespaceNamespace metadata specification
DSM Interface ExampleDevice-specific control example
Driver Writer's GuideNVDIMM driver 작성 지침
LIBNVDIMM treeKernel subsystem source
LIBNDCTL treeUserspace ndctl library source


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-152

NFIT 이전에는 비휘발성 메모리를 여러 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를 등록할 수 있습니다.

PMEM namespace 초기화 방식
방식Block device 생성 조건
Label capability 있음Userspace가 DPA를 namespace에 할당한 뒤 생성
`ND_NAMESPACE_IO`등록 즉시 `nd_pmem`에 연결
Label-less / legacyNamespace label 없이 즉시 사용
Atomic sector 필요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입니다.

예제 NVDIMM interleave
CPU0IMC0DIMM0 + DIMM1 first portionREGION0PMEM namespace `pm0.0` + free space
CPU0IMC0 + IMC1DIMM0..DIMM3 last portionsREGION1PMEM namespace `pm1.0` + free space
`nfit_test.ko``/sys/devices/platform/nfit_test.0`LIBNVDIMM / acpi_nfit unit-test bus

원문의 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`을 반환합니다.

LIBNDCTL context 생성
`ndctl_new(&ctx)`Return 0`struct ndctl_ctx *ctx`LIBNDCTL API calls
`ndctl_new(&ctx)`Failure`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-298

Bus와 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`입니다.

NFIT bus 등록과 탐색
Platform NFIT`nvdimm_bus_register()``ndbus0``nmem*` and `region*`
`/sys/class/nd/ndctl0`DSM messageDIMM selected by NFIT handle
`ndctl_bus_foreach()``ndctl_bus_get_provider()`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-379

DIMM 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 위치에 조합합니다.

32-bit NFIT DIMM handle
비트필드
3:0DIMM number within memory channel
7:4Memory channel number
11:8Memory controller ID
15:12Socket ID
27:16Node Controller ID
31:28Reserved

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을 반환합니다.

REGION 속성과 역할
항목의미
Mapping`<DIMM, DPA-start-offset, length>`
`interleave_ways`Interleave에 참여하는 way 수
`size` / `available_size`전체 및 미할당 capacity
`nstype`내보내는 namespace device type
`devtype` / `modalias`Udev add-event 값 복제
`spa_index`NFIT interleave-set ID
Seeds`namespace_seed`, `btt_seed`

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-498

REGION은 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`을 노출합니다.

두 Namespace 유형
항목DIMM-info-backed `namespace0.0`Anonymous `namespace1.0`
LABEL support있음없음
`uuid`있음없음
DPA metadata`dpa_extents`직접 SPA resource
Block device구성 후 생성`block/pmem0`
DriverNamespace 설정에 따라 bind`pmem`

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-550

Namespace 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합니다.

Namespace 생성 순서
Region has available capacityKernel creates idle namespaceSet alt_nameSet `uuid`Set `size`Optional BLK `lbasize`Enable namespace

`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를 본뜨도록 설계되었습니다.

Namespace 용어의 계보
`volume`Device-mapper와 혼동사용하지 않음
NVMe controller sub-deviceNVMe namespaceNFIT namespace

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-638

BTT는 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 동작을 억제할 수 있습니다.

BTT seed lifecycle
Region creates idle `btt_seed`Set namespace + uuid + sector_sizeDisable raw namespaceEnable BTTNew inactive seed appears
Remove namespaceDelete or reset device-model instanceMedia info block remains
`ndctl_namespace_set_raw_mode()`Write media in raw modeDestroy BTT info block

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이 연결됩니다.

LIBNDCTL object hierarchy
CTXBUS0DIMM0
CTXBUS0DIMM1
CTXBUS0DIMM2
CTXBUS0DIMM3
CTXBUS0REGION0NAMESPACE0.0PMEM8 `pm0.0`
CTXBUS0REGION1NAMESPACE1.0PMEM6 `pm1.0`BTT1

원문의 최종 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 <-+
  +-------+