Skip to content

Commit 62232e4

Browse files
committed
libnvdimm: control (ioctl) messages for nvdimm_bus and nvdimm devices
Most discovery/configuration of the nvdimm-subsystem is done via sysfs attributes. However, some nvdimm_bus instances, particularly the ACPI.NFIT bus, define a small set of messages that can be passed to the platform. For convenience we derive the initial libnvdimm-ioctl command formats directly from the NFIT DSM Interface Example formats. ND_CMD_SMART: media health and diagnostics ND_CMD_GET_CONFIG_SIZE: size of the label space ND_CMD_GET_CONFIG_DATA: read label space ND_CMD_SET_CONFIG_DATA: write label space ND_CMD_VENDOR: vendor-specific command passthrough ND_CMD_ARS_CAP: report address-range-scrubbing capabilities ND_CMD_ARS_START: initiate scrubbing ND_CMD_ARS_STATUS: report on scrubbing state ND_CMD_SMART_THRESHOLD: configure alarm thresholds for smart events If a platform later defines different commands than this set it is straightforward to extend support to those formats. Most of the commands target a specific dimm. However, the address-range-scrubbing commands target the bus. The 'commands' attribute in sysfs of an nvdimm_bus, or nvdimm, enumerate the supported commands for that object. Cc: <[email protected]> Cc: Robert Moore <[email protected]> Cc: Rafael J. Wysocki <[email protected]> Reported-by: Nicholas Moulin <[email protected]> Acked-by: Christoph Hellwig <[email protected]> Signed-off-by: Dan Williams <[email protected]>
1 parent e6dfb2d commit 62232e4

File tree

10 files changed

+810
-10
lines changed

10 files changed

+810
-10
lines changed

drivers/acpi/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,18 @@ config ACPI_NFIT
397397
To compile this driver as a module, choose M here:
398398
the module will be called nfit.
399399

400+
config ACPI_NFIT_DEBUG
401+
bool "NFIT DSM debug"
402+
depends on ACPI_NFIT
403+
depends on DYNAMIC_DEBUG
404+
default n
405+
help
406+
Enabling this option causes the nfit driver to dump the
407+
input and output buffers of _DSM operations on the ACPI0012
408+
device and its children. This can be very verbose, so leave
409+
it disabled unless you are debugging a hardware / firmware
410+
issue.
411+
400412
source "drivers/acpi/apei/Kconfig"
401413

402414
config ACPI_EXTLOG

drivers/acpi/nfit.c

Lines changed: 214 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/list_sort.h>
1414
#include <linux/libnvdimm.h>
1515
#include <linux/module.h>
16+
#include <linux/ndctl.h>
1617
#include <linux/list.h>
1718
#include <linux/acpi.h>
1819
#include "nfit.h"
@@ -24,11 +25,153 @@ static const u8 *to_nfit_uuid(enum nfit_uuids id)
2425
return nfit_uuid[id];
2526
}
2627

28+
static struct acpi_nfit_desc *to_acpi_nfit_desc(
29+
struct nvdimm_bus_descriptor *nd_desc)
30+
{
31+
return container_of(nd_desc, struct acpi_nfit_desc, nd_desc);
32+
}
33+
34+
static struct acpi_device *to_acpi_dev(struct acpi_nfit_desc *acpi_desc)
35+
{
36+
struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
37+
38+
/*
39+
* If provider == 'ACPI.NFIT' we can assume 'dev' is a struct
40+
* acpi_device.
41+
*/
42+
if (!nd_desc->provider_name
43+
|| strcmp(nd_desc->provider_name, "ACPI.NFIT") != 0)
44+
return NULL;
45+
46+
return to_acpi_device(acpi_desc->dev);
47+
}
48+
2749
static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc,
2850
struct nvdimm *nvdimm, unsigned int cmd, void *buf,
2951
unsigned int buf_len)
3052
{
31-
return -ENOTTY;
53+
struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc);
54+
const struct nd_cmd_desc *desc = NULL;
55+
union acpi_object in_obj, in_buf, *out_obj;
56+
struct device *dev = acpi_desc->dev;
57+
const char *cmd_name, *dimm_name;
58+
unsigned long dsm_mask;
59+
acpi_handle handle;
60+
const u8 *uuid;
61+
u32 offset;
62+
int rc, i;
63+
64+
if (nvdimm) {
65+
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
66+
struct acpi_device *adev = nfit_mem->adev;
67+
68+
if (!adev)
69+
return -ENOTTY;
70+
dimm_name = dev_name(&adev->dev);
71+
cmd_name = nvdimm_cmd_name(cmd);
72+
dsm_mask = nfit_mem->dsm_mask;
73+
desc = nd_cmd_dimm_desc(cmd);
74+
uuid = to_nfit_uuid(NFIT_DEV_DIMM);
75+
handle = adev->handle;
76+
} else {
77+
struct acpi_device *adev = to_acpi_dev(acpi_desc);
78+
79+
cmd_name = nvdimm_bus_cmd_name(cmd);
80+
dsm_mask = nd_desc->dsm_mask;
81+
desc = nd_cmd_bus_desc(cmd);
82+
uuid = to_nfit_uuid(NFIT_DEV_BUS);
83+
handle = adev->handle;
84+
dimm_name = "bus";
85+
}
86+
87+
if (!desc || (cmd && (desc->out_num + desc->in_num == 0)))
88+
return -ENOTTY;
89+
90+
if (!test_bit(cmd, &dsm_mask))
91+
return -ENOTTY;
92+
93+
in_obj.type = ACPI_TYPE_PACKAGE;
94+
in_obj.package.count = 1;
95+
in_obj.package.elements = &in_buf;
96+
in_buf.type = ACPI_TYPE_BUFFER;
97+
in_buf.buffer.pointer = buf;
98+
in_buf.buffer.length = 0;
99+
100+
/* libnvdimm has already validated the input envelope */
101+
for (i = 0; i < desc->in_num; i++)
102+
in_buf.buffer.length += nd_cmd_in_size(nvdimm, cmd, desc,
103+
i, buf);
104+
105+
if (IS_ENABLED(CONFIG_ACPI_NFIT_DEBUG)) {
106+
dev_dbg(dev, "%s:%s cmd: %s input length: %d\n", __func__,
107+
dimm_name, cmd_name, in_buf.buffer.length);
108+
print_hex_dump_debug(cmd_name, DUMP_PREFIX_OFFSET, 4,
109+
4, in_buf.buffer.pointer, min_t(u32, 128,
110+
in_buf.buffer.length), true);
111+
}
112+
113+
out_obj = acpi_evaluate_dsm(handle, uuid, 1, cmd, &in_obj);
114+
if (!out_obj) {
115+
dev_dbg(dev, "%s:%s _DSM failed cmd: %s\n", __func__, dimm_name,
116+
cmd_name);
117+
return -EINVAL;
118+
}
119+
120+
if (out_obj->package.type != ACPI_TYPE_BUFFER) {
121+
dev_dbg(dev, "%s:%s unexpected output object type cmd: %s type: %d\n",
122+
__func__, dimm_name, cmd_name, out_obj->type);
123+
rc = -EINVAL;
124+
goto out;
125+
}
126+
127+
if (IS_ENABLED(CONFIG_ACPI_NFIT_DEBUG)) {
128+
dev_dbg(dev, "%s:%s cmd: %s output length: %d\n", __func__,
129+
dimm_name, cmd_name, out_obj->buffer.length);
130+
print_hex_dump_debug(cmd_name, DUMP_PREFIX_OFFSET, 4,
131+
4, out_obj->buffer.pointer, min_t(u32, 128,
132+
out_obj->buffer.length), true);
133+
}
134+
135+
for (i = 0, offset = 0; i < desc->out_num; i++) {
136+
u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i, buf,
137+
(u32 *) out_obj->buffer.pointer);
138+
139+
if (offset + out_size > out_obj->buffer.length) {
140+
dev_dbg(dev, "%s:%s output object underflow cmd: %s field: %d\n",
141+
__func__, dimm_name, cmd_name, i);
142+
break;
143+
}
144+
145+
if (in_buf.buffer.length + offset + out_size > buf_len) {
146+
dev_dbg(dev, "%s:%s output overrun cmd: %s field: %d\n",
147+
__func__, dimm_name, cmd_name, i);
148+
rc = -ENXIO;
149+
goto out;
150+
}
151+
memcpy(buf + in_buf.buffer.length + offset,
152+
out_obj->buffer.pointer + offset, out_size);
153+
offset += out_size;
154+
}
155+
if (offset + in_buf.buffer.length < buf_len) {
156+
if (i >= 1) {
157+
/*
158+
* status valid, return the number of bytes left
159+
* unfilled in the output buffer
160+
*/
161+
rc = buf_len - offset - in_buf.buffer.length;
162+
} else {
163+
dev_err(dev, "%s:%s underrun cmd: %s buf_len: %d out_len: %d\n",
164+
__func__, dimm_name, cmd_name, buf_len,
165+
offset);
166+
rc = -ENXIO;
167+
}
168+
} else
169+
rc = 0;
170+
171+
out:
172+
ACPI_FREE(out_obj);
173+
174+
return rc;
32175
}
33176

34177
static const char *spa_type_name(u16 type)
@@ -489,6 +632,7 @@ static struct attribute_group acpi_nfit_dimm_attribute_group = {
489632
};
490633

491634
static const struct attribute_group *acpi_nfit_dimm_attribute_groups[] = {
635+
&nvdimm_attribute_group,
492636
&acpi_nfit_dimm_attribute_group,
493637
NULL,
494638
};
@@ -505,6 +649,50 @@ static struct nvdimm *acpi_nfit_dimm_by_handle(struct acpi_nfit_desc *acpi_desc,
505649
return NULL;
506650
}
507651

652+
static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
653+
struct nfit_mem *nfit_mem, u32 device_handle)
654+
{
655+
struct acpi_device *adev, *adev_dimm;
656+
struct device *dev = acpi_desc->dev;
657+
const u8 *uuid = to_nfit_uuid(NFIT_DEV_DIMM);
658+
unsigned long long sta;
659+
int i, rc = -ENODEV;
660+
acpi_status status;
661+
662+
nfit_mem->dsm_mask = acpi_desc->dimm_dsm_force_en;
663+
adev = to_acpi_dev(acpi_desc);
664+
if (!adev)
665+
return 0;
666+
667+
adev_dimm = acpi_find_child_device(adev, device_handle, false);
668+
nfit_mem->adev = adev_dimm;
669+
if (!adev_dimm) {
670+
dev_err(dev, "no ACPI.NFIT device with _ADR %#x, disabling...\n",
671+
device_handle);
672+
return -ENODEV;
673+
}
674+
675+
status = acpi_evaluate_integer(adev_dimm->handle, "_STA", NULL, &sta);
676+
if (status == AE_NOT_FOUND) {
677+
dev_dbg(dev, "%s missing _STA, assuming enabled...\n",
678+
dev_name(&adev_dimm->dev));
679+
rc = 0;
680+
} else if (ACPI_FAILURE(status))
681+
dev_err(dev, "%s failed to retrieve_STA, disabling...\n",
682+
dev_name(&adev_dimm->dev));
683+
else if ((sta & ACPI_STA_DEVICE_ENABLED) == 0)
684+
dev_info(dev, "%s disabled by firmware\n",
685+
dev_name(&adev_dimm->dev));
686+
else
687+
rc = 0;
688+
689+
for (i = ND_CMD_SMART; i <= ND_CMD_VENDOR; i++)
690+
if (acpi_check_dsm(adev_dimm->handle, uuid, 1, 1ULL << i))
691+
set_bit(i, &nfit_mem->dsm_mask);
692+
693+
return rc;
694+
}
695+
508696
static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
509697
{
510698
struct nfit_mem *nfit_mem;
@@ -513,6 +701,7 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
513701
struct nvdimm *nvdimm;
514702
unsigned long flags = 0;
515703
u32 device_handle;
704+
int rc;
516705

517706
device_handle = __to_nfit_memdev(nfit_mem)->device_handle;
518707
nvdimm = acpi_nfit_dimm_by_handle(acpi_desc, device_handle);
@@ -529,8 +718,13 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
529718
if (nfit_mem->bdw && nfit_mem->memdev_pmem)
530719
flags |= NDD_ALIASING;
531720

721+
rc = acpi_nfit_add_dimm(acpi_desc, nfit_mem, device_handle);
722+
if (rc)
723+
continue;
724+
532725
nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem,
533-
acpi_nfit_dimm_attribute_groups, flags);
726+
acpi_nfit_dimm_attribute_groups,
727+
flags, &nfit_mem->dsm_mask);
534728
if (!nvdimm)
535729
return -ENOMEM;
536730

@@ -540,6 +734,22 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
540734
return 0;
541735
}
542736

737+
static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc)
738+
{
739+
struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
740+
const u8 *uuid = to_nfit_uuid(NFIT_DEV_BUS);
741+
struct acpi_device *adev;
742+
int i;
743+
744+
adev = to_acpi_dev(acpi_desc);
745+
if (!adev)
746+
return;
747+
748+
for (i = ND_CMD_ARS_CAP; i <= ND_CMD_ARS_STATUS; i++)
749+
if (acpi_check_dsm(adev->handle, uuid, 1, 1ULL << i))
750+
set_bit(i, &nd_desc->dsm_mask);
751+
}
752+
543753
static int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, acpi_size sz)
544754
{
545755
struct device *dev = acpi_desc->dev;
@@ -567,6 +777,8 @@ static int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, acpi_size sz)
567777
if (nfit_mem_init(acpi_desc) != 0)
568778
return -ENOMEM;
569779

780+
acpi_nfit_init_dsms(acpi_desc);
781+
570782
return acpi_nfit_register_dimms(acpi_desc);
571783
}
572784

drivers/acpi/nfit.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ struct nfit_mem {
6767
struct acpi_nfit_system_address *spa_dcr;
6868
struct acpi_nfit_system_address *spa_bdw;
6969
struct list_head list;
70+
struct acpi_device *adev;
71+
unsigned long dsm_mask;
7072
};
7173

7274
struct acpi_nfit_desc {
@@ -79,6 +81,7 @@ struct acpi_nfit_desc {
7981
struct list_head bdws;
8082
struct nvdimm_bus *nvdimm_bus;
8183
struct device *dev;
84+
unsigned long dimm_dsm_force_en;
8285
};
8386

8487
static inline struct acpi_nfit_memory_map *__to_nfit_memdev(

0 commit comments

Comments
 (0)