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
This commit is contained in:
commit
3d7b365490
|
@ -469,37 +469,16 @@ static void nfit_mem_find_spa_bdw(struct acpi_nfit_desc *acpi_desc,
|
|||
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)
|
||||
{
|
||||
u16 dcr = __to_nfit_memdev(nfit_mem)->region_index;
|
||||
struct nfit_memdev *nfit_memdev;
|
||||
struct nfit_flush *nfit_flush;
|
||||
struct nfit_dcr *nfit_dcr;
|
||||
struct nfit_bdw *nfit_bdw;
|
||||
struct nfit_idt *nfit_idt;
|
||||
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) {
|
||||
if (nfit_bdw->bdw->region_index != dcr)
|
||||
continue;
|
||||
|
@ -508,12 +487,12 @@ static int nfit_mem_add(struct acpi_nfit_desc *acpi_desc,
|
|||
}
|
||||
|
||||
if (!nfit_mem->bdw)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
nfit_mem_find_spa_bdw(acpi_desc, nfit_mem);
|
||||
|
||||
if (!nfit_mem->spa_bdw)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
range_index = nfit_mem->spa_bdw->range_index;
|
||||
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,
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_memdev *nfit_memdev;
|
||||
int type = nfit_spa_type(spa);
|
||||
u16 dcr;
|
||||
|
||||
switch (type) {
|
||||
case NFIT_SPA_DCR:
|
||||
|
@ -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) {
|
||||
int rc;
|
||||
struct nfit_dcr *nfit_dcr;
|
||||
u32 device_handle;
|
||||
u16 dcr;
|
||||
|
||||
if (nfit_memdev->memdev->range_index != spa->range_index)
|
||||
continue;
|
||||
found = NULL;
|
||||
dcr = nfit_memdev->memdev->region_index;
|
||||
device_handle = nfit_memdev->memdev->device_handle;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
@ -579,6 +559,31 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc,
|
|||
if (!nfit_mem)
|
||||
return -ENOMEM;
|
||||
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) {
|
||||
|
@ -595,6 +600,7 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc,
|
|||
nfit_mem->idt_dcr = nfit_idt->idt;
|
||||
break;
|
||||
}
|
||||
nfit_mem_init_bdw(acpi_desc, nfit_mem, spa);
|
||||
} else {
|
||||
/*
|
||||
* 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,
|
|||
*/
|
||||
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;
|
||||
|
@ -1504,9 +1503,7 @@ static int ars_do_start(struct nvdimm_bus_descriptor *nd_desc,
|
|||
case 1:
|
||||
/* ARS unsupported, but we should never get here */
|
||||
return 0;
|
||||
case 2:
|
||||
return -EINVAL;
|
||||
case 3:
|
||||
case 6:
|
||||
/* ARS is in progress */
|
||||
msleep(1000);
|
||||
break;
|
||||
|
@ -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,
|
||||
struct nd_cmd_ars_status *cmd)
|
||||
struct nd_cmd_ars_status *cmd, u32 size)
|
||||
{
|
||||
int rc;
|
||||
|
||||
while (1) {
|
||||
rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_STATUS, cmd,
|
||||
sizeof(*cmd));
|
||||
size);
|
||||
if (rc || cmd->status & 0xffff)
|
||||
return -ENXIO;
|
||||
|
||||
|
@ -1538,6 +1535,8 @@ static int ars_get_status(struct nvdimm_bus_descriptor *nd_desc,
|
|||
case 2:
|
||||
/* No ARS performed for the current boot */
|
||||
return 0;
|
||||
case 3:
|
||||
/* TODO: error list overflow support */
|
||||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
|
@ -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_cap *ars_cap = NULL;
|
||||
u64 start, len, cur, remaining;
|
||||
u32 ars_status_size;
|
||||
int rc;
|
||||
|
||||
ars_cap = kzalloc(sizeof(*ars_cap), GFP_KERNEL);
|
||||
|
@ -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
|
||||
* without having to start a new ARS.
|
||||
*/
|
||||
ars_status = kzalloc(ars_cap->max_ars_out + sizeof(*ars_status),
|
||||
GFP_KERNEL);
|
||||
ars_status_size = ars_cap->max_ars_out;
|
||||
ars_status = kzalloc(ars_status_size, GFP_KERNEL);
|
||||
if (!ars_status) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = ars_get_status(nd_desc, ars_status);
|
||||
rc = ars_get_status(nd_desc, ars_status, ars_status_size);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
|
@ -1647,7 +1647,7 @@ static int acpi_nfit_find_poison(struct acpi_nfit_desc *acpi_desc,
|
|||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = ars_get_status(nd_desc, ars_status);
|
||||
rc = ars_get_status(nd_desc, ars_status, ars_status_size);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -382,18 +382,18 @@ static const struct nd_cmd_desc __nd_cmd_bus_descs[] = {
|
|||
[ND_CMD_ARS_CAP] = {
|
||||
.in_num = 2,
|
||||
.in_sizes = { 8, 8, },
|
||||
.out_num = 4,
|
||||
.out_sizes = { 4, 4, 4, 4, },
|
||||
},
|
||||
[ND_CMD_ARS_START] = {
|
||||
.in_num = 5,
|
||||
.in_sizes = { 8, 8, 2, 1, 5, },
|
||||
.out_num = 2,
|
||||
.out_sizes = { 4, 4, },
|
||||
},
|
||||
[ND_CMD_ARS_START] = {
|
||||
.in_num = 4,
|
||||
.in_sizes = { 8, 8, 2, 6, },
|
||||
.out_num = 1,
|
||||
.out_sizes = { 4, },
|
||||
},
|
||||
[ND_CMD_ARS_STATUS] = {
|
||||
.out_num = 2,
|
||||
.out_sizes = { 4, UINT_MAX, },
|
||||
.out_num = 3,
|
||||
.out_sizes = { 4, 4, UINT_MAX, },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -442,8 +442,8 @@ u32 nd_cmd_out_size(struct nvdimm *nvdimm, int cmd,
|
|||
return in_field[1];
|
||||
else if (nvdimm && cmd == ND_CMD_VENDOR && idx == 2)
|
||||
return out_field[1];
|
||||
else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 1)
|
||||
return ND_CMD_ARS_STATUS_MAX;
|
||||
else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 2)
|
||||
return out_field[1] - 8;
|
||||
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ struct pmem_device {
|
|||
phys_addr_t phys_addr;
|
||||
/* when non-zero this device is hosting a 'pfn' instance */
|
||||
phys_addr_t data_offset;
|
||||
unsigned long pfn_flags;
|
||||
u64 pfn_flags;
|
||||
void __pmem *virt_addr;
|
||||
size_t size;
|
||||
struct badblocks bb;
|
||||
|
|
|
@ -26,9 +26,8 @@ enum {
|
|||
|
||||
/* need to set a limit somewhere, but yes, this is likely overkill */
|
||||
ND_IOCTL_MAX_BUFLEN = SZ_4M,
|
||||
ND_CMD_MAX_ELEM = 4,
|
||||
ND_CMD_MAX_ELEM = 5,
|
||||
ND_CMD_MAX_ENVELOPE = 16,
|
||||
ND_CMD_ARS_STATUS_MAX = SZ_4K,
|
||||
ND_MAX_MAPPINGS = 32,
|
||||
|
||||
/* region flag indicating to direct-map persistent memory by default */
|
||||
|
|
|
@ -66,14 +66,18 @@ struct nd_cmd_ars_cap {
|
|||
__u64 length;
|
||||
__u32 status;
|
||||
__u32 max_ars_out;
|
||||
__u32 clear_err_unit;
|
||||
__u32 reserved;
|
||||
} __packed;
|
||||
|
||||
struct nd_cmd_ars_start {
|
||||
__u64 address;
|
||||
__u64 length;
|
||||
__u16 type;
|
||||
__u8 reserved[6];
|
||||
__u8 flags;
|
||||
__u8 reserved[5];
|
||||
__u32 status;
|
||||
__u32 scrub_time;
|
||||
} __packed;
|
||||
|
||||
struct nd_cmd_ars_status {
|
||||
|
@ -81,11 +85,14 @@ struct nd_cmd_ars_status {
|
|||
__u32 out_length;
|
||||
__u64 address;
|
||||
__u64 length;
|
||||
__u64 restart_address;
|
||||
__u64 restart_length;
|
||||
__u16 type;
|
||||
__u16 flags;
|
||||
__u32 num_records;
|
||||
struct nd_ars_record {
|
||||
__u32 handle;
|
||||
__u32 flags;
|
||||
__u32 reserved;
|
||||
__u64 err_address;
|
||||
__u64 length;
|
||||
} __packed records[0];
|
||||
|
|
|
@ -136,8 +136,10 @@ void *devm_memremap(struct device *dev, resource_size_t offset,
|
|||
if (addr) {
|
||||
*ptr = addr;
|
||||
devres_add(dev, ptr);
|
||||
} else
|
||||
} else {
|
||||
devres_free(ptr);
|
||||
return ERR_PTR(-ENXIO);
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
|
|
@ -217,13 +217,16 @@ static int nfit_test_cmd_set_config_data(struct nd_cmd_set_config_hdr *nd_cmd,
|
|||
return rc;
|
||||
}
|
||||
|
||||
#define NFIT_TEST_ARS_RECORDS 4
|
||||
|
||||
static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd,
|
||||
unsigned int buf_len)
|
||||
{
|
||||
if (buf_len < sizeof(*nd_cmd))
|
||||
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;
|
||||
|
||||
return 0;
|
||||
|
@ -246,7 +249,8 @@ static int nfit_test_cmd_ars_status(struct nd_cmd_ars_status *nd_cmd,
|
|||
if (buf_len < sizeof(*nd_cmd))
|
||||
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->address = 0;
|
||||
nd_cmd->length = -1ULL;
|
||||
|
|
Loading…
Reference in New Issue