Commit 3d7b3654 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'libnvdimm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm

Pull libnvdimm fixes from Dan Williams:

 - Two fixes for compatibility with the ACPI 6.1 specification.

   Without these fixes multi-interface DIMMs will fail to be probed, and
   address range scrub commands to find memory errors will give results
   that the kernel will mis-interpret.  For multi-interface DIMMs Linux
   will accept either the original 6.0 implementation or 6.1.

   For address range scrub we'll only support 6.1 since ACPI formalized
   this DSM differently than the original example [1] implemented in
   v4.2.  The expectation is that production systems will only ever ship
   the ACPI 6.1 address range scrub command definition.

 - The wider async address range scrub work targeting 4.6 discovered
   that the original synchronous implementation in 4.5 is not sizing its
   return buffer correctly.

 - Arnd caught that my recent fix to the size of the pfn_t flags missed
   updating the flags variable used in the pmem driver.

 - Toshi found that we mishandle the memremap() return value in
   devm_memremap().

* 'libnvdimm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm:
  nvdimm: use 'u64' for pfn flags
  devm_memremap: Fix error value when memremap failed
  nfit: update address range scrub commands to the acpi 6.1 format
  libnvdimm, tools/testing/nvdimm: fix 'ars_status' output buffer sizing
  nfit: fix multi-interface dimm handling, acpi6.1 compatibility
parents 1ebe3839 c4544205
...@@ -469,37 +469,16 @@ static void nfit_mem_find_spa_bdw(struct acpi_nfit_desc *acpi_desc, ...@@ -469,37 +469,16 @@ static void nfit_mem_find_spa_bdw(struct acpi_nfit_desc *acpi_desc,
nfit_mem->bdw = NULL; nfit_mem->bdw = NULL;
} }
static int nfit_mem_add(struct acpi_nfit_desc *acpi_desc, static void nfit_mem_init_bdw(struct acpi_nfit_desc *acpi_desc,
struct nfit_mem *nfit_mem, struct acpi_nfit_system_address *spa) struct nfit_mem *nfit_mem, struct acpi_nfit_system_address *spa)
{ {
u16 dcr = __to_nfit_memdev(nfit_mem)->region_index; u16 dcr = __to_nfit_memdev(nfit_mem)->region_index;
struct nfit_memdev *nfit_memdev; struct nfit_memdev *nfit_memdev;
struct nfit_flush *nfit_flush; struct nfit_flush *nfit_flush;
struct nfit_dcr *nfit_dcr;
struct nfit_bdw *nfit_bdw; struct nfit_bdw *nfit_bdw;
struct nfit_idt *nfit_idt; struct nfit_idt *nfit_idt;
u16 idt_idx, range_index; u16 idt_idx, range_index;
list_for_each_entry(nfit_dcr, &acpi_desc->dcrs, list) {
if (nfit_dcr->dcr->region_index != dcr)
continue;
nfit_mem->dcr = nfit_dcr->dcr;
break;
}
if (!nfit_mem->dcr) {
dev_dbg(acpi_desc->dev, "SPA %d missing:%s%s\n",
spa->range_index, __to_nfit_memdev(nfit_mem)
? "" : " MEMDEV", nfit_mem->dcr ? "" : " DCR");
return -ENODEV;
}
/*
* We've found enough to create an nvdimm, optionally
* find an associated BDW
*/
list_add(&nfit_mem->list, &acpi_desc->dimms);
list_for_each_entry(nfit_bdw, &acpi_desc->bdws, list) { list_for_each_entry(nfit_bdw, &acpi_desc->bdws, list) {
if (nfit_bdw->bdw->region_index != dcr) if (nfit_bdw->bdw->region_index != dcr)
continue; continue;
...@@ -508,12 +487,12 @@ static int nfit_mem_add(struct acpi_nfit_desc *acpi_desc, ...@@ -508,12 +487,12 @@ static int nfit_mem_add(struct acpi_nfit_desc *acpi_desc,
} }
if (!nfit_mem->bdw) if (!nfit_mem->bdw)
return 0; return;
nfit_mem_find_spa_bdw(acpi_desc, nfit_mem); nfit_mem_find_spa_bdw(acpi_desc, nfit_mem);
if (!nfit_mem->spa_bdw) if (!nfit_mem->spa_bdw)
return 0; return;
range_index = nfit_mem->spa_bdw->range_index; range_index = nfit_mem->spa_bdw->range_index;
list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) {
...@@ -538,8 +517,6 @@ static int nfit_mem_add(struct acpi_nfit_desc *acpi_desc, ...@@ -538,8 +517,6 @@ static int nfit_mem_add(struct acpi_nfit_desc *acpi_desc,
} }
break; break;
} }
return 0;
} }
static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc,
...@@ -548,7 +525,6 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, ...@@ -548,7 +525,6 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc,
struct nfit_mem *nfit_mem, *found; struct nfit_mem *nfit_mem, *found;
struct nfit_memdev *nfit_memdev; struct nfit_memdev *nfit_memdev;
int type = nfit_spa_type(spa); int type = nfit_spa_type(spa);
u16 dcr;
switch (type) { switch (type) {
case NFIT_SPA_DCR: case NFIT_SPA_DCR:
...@@ -559,14 +535,18 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, ...@@ -559,14 +535,18 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc,
} }
list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) {
int rc; struct nfit_dcr *nfit_dcr;
u32 device_handle;
u16 dcr;
if (nfit_memdev->memdev->range_index != spa->range_index) if (nfit_memdev->memdev->range_index != spa->range_index)
continue; continue;
found = NULL; found = NULL;
dcr = nfit_memdev->memdev->region_index; dcr = nfit_memdev->memdev->region_index;
device_handle = nfit_memdev->memdev->device_handle;
list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) list_for_each_entry(nfit_mem, &acpi_desc->dimms, list)
if (__to_nfit_memdev(nfit_mem)->region_index == dcr) { if (__to_nfit_memdev(nfit_mem)->device_handle
== device_handle) {
found = nfit_mem; found = nfit_mem;
break; break;
} }
...@@ -579,6 +559,31 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, ...@@ -579,6 +559,31 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc,
if (!nfit_mem) if (!nfit_mem)
return -ENOMEM; return -ENOMEM;
INIT_LIST_HEAD(&nfit_mem->list); INIT_LIST_HEAD(&nfit_mem->list);
list_add(&nfit_mem->list, &acpi_desc->dimms);
}
list_for_each_entry(nfit_dcr, &acpi_desc->dcrs, list) {
if (nfit_dcr->dcr->region_index != dcr)
continue;
/*
* Record the control region for the dimm. For
* the ACPI 6.1 case, where there are separate
* control regions for the pmem vs blk
* interfaces, be sure to record the extended
* blk details.
*/
if (!nfit_mem->dcr)
nfit_mem->dcr = nfit_dcr->dcr;
else if (nfit_mem->dcr->windows == 0
&& nfit_dcr->dcr->windows)
nfit_mem->dcr = nfit_dcr->dcr;
break;
}
if (dcr && !nfit_mem->dcr) {
dev_err(acpi_desc->dev, "SPA %d missing DCR %d\n",
spa->range_index, dcr);
return -ENODEV;
} }
if (type == NFIT_SPA_DCR) { if (type == NFIT_SPA_DCR) {
...@@ -595,6 +600,7 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, ...@@ -595,6 +600,7 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc,
nfit_mem->idt_dcr = nfit_idt->idt; nfit_mem->idt_dcr = nfit_idt->idt;
break; break;
} }
nfit_mem_init_bdw(acpi_desc, nfit_mem, spa);
} else { } else {
/* /*
* A single dimm may belong to multiple SPA-PM * A single dimm may belong to multiple SPA-PM
...@@ -603,13 +609,6 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, ...@@ -603,13 +609,6 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc,
*/ */
nfit_mem->memdev_pmem = nfit_memdev->memdev; nfit_mem->memdev_pmem = nfit_memdev->memdev;
} }
if (found)
continue;
rc = nfit_mem_add(acpi_desc, nfit_mem, spa);
if (rc)
return rc;
} }
return 0; return 0;
...@@ -1504,9 +1503,7 @@ static int ars_do_start(struct nvdimm_bus_descriptor *nd_desc, ...@@ -1504,9 +1503,7 @@ static int ars_do_start(struct nvdimm_bus_descriptor *nd_desc,
case 1: case 1:
/* ARS unsupported, but we should never get here */ /* ARS unsupported, but we should never get here */
return 0; return 0;
case 2: case 6:
return -EINVAL;
case 3:
/* ARS is in progress */ /* ARS is in progress */
msleep(1000); msleep(1000);
break; break;
...@@ -1517,13 +1514,13 @@ static int ars_do_start(struct nvdimm_bus_descriptor *nd_desc, ...@@ -1517,13 +1514,13 @@ static int ars_do_start(struct nvdimm_bus_descriptor *nd_desc,
} }
static int ars_get_status(struct nvdimm_bus_descriptor *nd_desc, static int ars_get_status(struct nvdimm_bus_descriptor *nd_desc,
struct nd_cmd_ars_status *cmd) struct nd_cmd_ars_status *cmd, u32 size)
{ {
int rc; int rc;
while (1) { while (1) {
rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_STATUS, cmd, rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_STATUS, cmd,
sizeof(*cmd)); size);
if (rc || cmd->status & 0xffff) if (rc || cmd->status & 0xffff)
return -ENXIO; return -ENXIO;
...@@ -1538,6 +1535,8 @@ static int ars_get_status(struct nvdimm_bus_descriptor *nd_desc, ...@@ -1538,6 +1535,8 @@ static int ars_get_status(struct nvdimm_bus_descriptor *nd_desc,
case 2: case 2:
/* No ARS performed for the current boot */ /* No ARS performed for the current boot */
return 0; return 0;
case 3:
/* TODO: error list overflow support */
default: default:
return -ENXIO; return -ENXIO;
} }
...@@ -1581,6 +1580,7 @@ static int acpi_nfit_find_poison(struct acpi_nfit_desc *acpi_desc, ...@@ -1581,6 +1580,7 @@ static int acpi_nfit_find_poison(struct acpi_nfit_desc *acpi_desc,
struct nd_cmd_ars_start *ars_start = NULL; struct nd_cmd_ars_start *ars_start = NULL;
struct nd_cmd_ars_cap *ars_cap = NULL; struct nd_cmd_ars_cap *ars_cap = NULL;
u64 start, len, cur, remaining; u64 start, len, cur, remaining;
u32 ars_status_size;
int rc; int rc;
ars_cap = kzalloc(sizeof(*ars_cap), GFP_KERNEL); ars_cap = kzalloc(sizeof(*ars_cap), GFP_KERNEL);
...@@ -1610,14 +1610,14 @@ static int acpi_nfit_find_poison(struct acpi_nfit_desc *acpi_desc, ...@@ -1610,14 +1610,14 @@ static int acpi_nfit_find_poison(struct acpi_nfit_desc *acpi_desc,
* Check if a full-range ARS has been run. If so, use those results * Check if a full-range ARS has been run. If so, use those results
* without having to start a new ARS. * without having to start a new ARS.
*/ */
ars_status = kzalloc(ars_cap->max_ars_out + sizeof(*ars_status), ars_status_size = ars_cap->max_ars_out;
GFP_KERNEL); ars_status = kzalloc(ars_status_size, GFP_KERNEL);
if (!ars_status) { if (!ars_status) {
rc = -ENOMEM; rc = -ENOMEM;
goto out; goto out;
} }
rc = ars_get_status(nd_desc, ars_status); rc = ars_get_status(nd_desc, ars_status, ars_status_size);
if (rc) if (rc)
goto out; goto out;
...@@ -1647,7 +1647,7 @@ static int acpi_nfit_find_poison(struct acpi_nfit_desc *acpi_desc, ...@@ -1647,7 +1647,7 @@ static int acpi_nfit_find_poison(struct acpi_nfit_desc *acpi_desc,
if (rc) if (rc)
goto out; goto out;
rc = ars_get_status(nd_desc, ars_status); rc = ars_get_status(nd_desc, ars_status, ars_status_size);
if (rc) if (rc)
goto out; goto out;
......
...@@ -382,18 +382,18 @@ static const struct nd_cmd_desc __nd_cmd_bus_descs[] = { ...@@ -382,18 +382,18 @@ static const struct nd_cmd_desc __nd_cmd_bus_descs[] = {
[ND_CMD_ARS_CAP] = { [ND_CMD_ARS_CAP] = {
.in_num = 2, .in_num = 2,
.in_sizes = { 8, 8, }, .in_sizes = { 8, 8, },
.out_num = 2, .out_num = 4,
.out_sizes = { 4, 4, }, .out_sizes = { 4, 4, 4, 4, },
}, },
[ND_CMD_ARS_START] = { [ND_CMD_ARS_START] = {
.in_num = 4, .in_num = 5,
.in_sizes = { 8, 8, 2, 6, }, .in_sizes = { 8, 8, 2, 1, 5, },
.out_num = 1, .out_num = 2,
.out_sizes = { 4, }, .out_sizes = { 4, 4, },
}, },
[ND_CMD_ARS_STATUS] = { [ND_CMD_ARS_STATUS] = {
.out_num = 2, .out_num = 3,
.out_sizes = { 4, UINT_MAX, }, .out_sizes = { 4, 4, UINT_MAX, },
}, },
}; };
...@@ -442,8 +442,8 @@ u32 nd_cmd_out_size(struct nvdimm *nvdimm, int cmd, ...@@ -442,8 +442,8 @@ u32 nd_cmd_out_size(struct nvdimm *nvdimm, int cmd,
return in_field[1]; return in_field[1];
else if (nvdimm && cmd == ND_CMD_VENDOR && idx == 2) else if (nvdimm && cmd == ND_CMD_VENDOR && idx == 2)
return out_field[1]; return out_field[1];
else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 1) else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 2)
return ND_CMD_ARS_STATUS_MAX; return out_field[1] - 8;
return UINT_MAX; return UINT_MAX;
} }
......
...@@ -41,7 +41,7 @@ struct pmem_device { ...@@ -41,7 +41,7 @@ struct pmem_device {
phys_addr_t phys_addr; phys_addr_t phys_addr;
/* when non-zero this device is hosting a 'pfn' instance */ /* when non-zero this device is hosting a 'pfn' instance */
phys_addr_t data_offset; phys_addr_t data_offset;
unsigned long pfn_flags; u64 pfn_flags;
void __pmem *virt_addr; void __pmem *virt_addr;
size_t size; size_t size;
struct badblocks bb; struct badblocks bb;
......
...@@ -26,9 +26,8 @@ enum { ...@@ -26,9 +26,8 @@ enum {
/* need to set a limit somewhere, but yes, this is likely overkill */ /* need to set a limit somewhere, but yes, this is likely overkill */
ND_IOCTL_MAX_BUFLEN = SZ_4M, ND_IOCTL_MAX_BUFLEN = SZ_4M,
ND_CMD_MAX_ELEM = 4, ND_CMD_MAX_ELEM = 5,
ND_CMD_MAX_ENVELOPE = 16, ND_CMD_MAX_ENVELOPE = 16,
ND_CMD_ARS_STATUS_MAX = SZ_4K,
ND_MAX_MAPPINGS = 32, ND_MAX_MAPPINGS = 32,
/* region flag indicating to direct-map persistent memory by default */ /* region flag indicating to direct-map persistent memory by default */
......
...@@ -66,14 +66,18 @@ struct nd_cmd_ars_cap { ...@@ -66,14 +66,18 @@ struct nd_cmd_ars_cap {
__u64 length; __u64 length;
__u32 status; __u32 status;
__u32 max_ars_out; __u32 max_ars_out;
__u32 clear_err_unit;
__u32 reserved;
} __packed; } __packed;
struct nd_cmd_ars_start { struct nd_cmd_ars_start {
__u64 address; __u64 address;
__u64 length; __u64 length;
__u16 type; __u16 type;
__u8 reserved[6]; __u8 flags;
__u8 reserved[5];
__u32 status; __u32 status;
__u32 scrub_time;
} __packed; } __packed;
struct nd_cmd_ars_status { struct nd_cmd_ars_status {
...@@ -81,11 +85,14 @@ struct nd_cmd_ars_status { ...@@ -81,11 +85,14 @@ struct nd_cmd_ars_status {
__u32 out_length; __u32 out_length;
__u64 address; __u64 address;
__u64 length; __u64 length;
__u64 restart_address;
__u64 restart_length;
__u16 type; __u16 type;
__u16 flags;
__u32 num_records; __u32 num_records;
struct nd_ars_record { struct nd_ars_record {
__u32 handle; __u32 handle;
__u32 flags; __u32 reserved;
__u64 err_address; __u64 err_address;
__u64 length; __u64 length;
} __packed records[0]; } __packed records[0];
......
...@@ -136,8 +136,10 @@ void *devm_memremap(struct device *dev, resource_size_t offset, ...@@ -136,8 +136,10 @@ void *devm_memremap(struct device *dev, resource_size_t offset,
if (addr) { if (addr) {
*ptr = addr; *ptr = addr;
devres_add(dev, ptr); devres_add(dev, ptr);
} else } else {
devres_free(ptr); devres_free(ptr);
return ERR_PTR(-ENXIO);
}
return addr; return addr;
} }
......
...@@ -217,13 +217,16 @@ static int nfit_test_cmd_set_config_data(struct nd_cmd_set_config_hdr *nd_cmd, ...@@ -217,13 +217,16 @@ static int nfit_test_cmd_set_config_data(struct nd_cmd_set_config_hdr *nd_cmd,
return rc; return rc;
} }
#define NFIT_TEST_ARS_RECORDS 4
static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd, static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd,
unsigned int buf_len) unsigned int buf_len)
{ {
if (buf_len < sizeof(*nd_cmd)) if (buf_len < sizeof(*nd_cmd))
return -EINVAL; return -EINVAL;
nd_cmd->max_ars_out = 256; nd_cmd->max_ars_out = sizeof(struct nd_cmd_ars_status)
+ NFIT_TEST_ARS_RECORDS * sizeof(struct nd_ars_record);
nd_cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16; nd_cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16;
return 0; return 0;
...@@ -246,7 +249,8 @@ static int nfit_test_cmd_ars_status(struct nd_cmd_ars_status *nd_cmd, ...@@ -246,7 +249,8 @@ static int nfit_test_cmd_ars_status(struct nd_cmd_ars_status *nd_cmd,
if (buf_len < sizeof(*nd_cmd)) if (buf_len < sizeof(*nd_cmd))
return -EINVAL; return -EINVAL;
nd_cmd->out_length = 256; nd_cmd->out_length = sizeof(struct nd_cmd_ars_status);
/* TODO: emit error records */
nd_cmd->num_records = 0; nd_cmd->num_records = 0;
nd_cmd->address = 0; nd_cmd->address = 0;
nd_cmd->length = -1ULL; nd_cmd->length = -1ULL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment