aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-02-25 21:54:53 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2016-02-25 21:54:53 -0500
commit3d7b365490d5f2f8ac1aaaf6cce775e6a8b7f570 (patch)
tree416a123b9eb6ae0dc9ad6df3bbbfc0c130edf195
parent1ebe3839e66ae85e065157ba9d6f7923ad8d8fbf (diff)
parentc45442055dfdeb265cc20c9eeaa9fd11a75fbf51 (diff)
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
-rw-r--r--drivers/acpi/nfit.c90
-rw-r--r--drivers/nvdimm/bus.c20
-rw-r--r--drivers/nvdimm/pmem.c2
-rw-r--r--include/linux/libnvdimm.h3
-rw-r--r--include/uapi/linux/ndctl.h11
-rw-r--r--kernel/memremap.c4
-rw-r--r--tools/testing/nvdimm/test/nfit.c8
7 files changed, 75 insertions, 63 deletions
diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c
index ad6d8c6b777e..fb53db187854 100644
--- a/drivers/acpi/nfit.c
+++ b/drivers/acpi/nfit.c
@@ -469,37 +469,16 @@ static void nfit_mem_find_spa_bdw(struct acpi_nfit_desc *acpi_desc,
469 nfit_mem->bdw = NULL; 469 nfit_mem->bdw = NULL;
470} 470}
471 471
472static int nfit_mem_add(struct acpi_nfit_desc *acpi_desc, 472static void nfit_mem_init_bdw(struct acpi_nfit_desc *acpi_desc,
473 struct nfit_mem *nfit_mem, struct acpi_nfit_system_address *spa) 473 struct nfit_mem *nfit_mem, struct acpi_nfit_system_address *spa)
474{ 474{
475 u16 dcr = __to_nfit_memdev(nfit_mem)->region_index; 475 u16 dcr = __to_nfit_memdev(nfit_mem)->region_index;
476 struct nfit_memdev *nfit_memdev; 476 struct nfit_memdev *nfit_memdev;
477 struct nfit_flush *nfit_flush; 477 struct nfit_flush *nfit_flush;
478 struct nfit_dcr *nfit_dcr;
479 struct nfit_bdw *nfit_bdw; 478 struct nfit_bdw *nfit_bdw;
480 struct nfit_idt *nfit_idt; 479 struct nfit_idt *nfit_idt;
481 u16 idt_idx, range_index; 480 u16 idt_idx, range_index;
482 481
483 list_for_each_entry(nfit_dcr, &acpi_desc->dcrs, list) {
484 if (nfit_dcr->dcr->region_index != dcr)
485 continue;
486 nfit_mem->dcr = nfit_dcr->dcr;
487 break;
488 }
489
490 if (!nfit_mem->dcr) {
491 dev_dbg(acpi_desc->dev, "SPA %d missing:%s%s\n",
492 spa->range_index, __to_nfit_memdev(nfit_mem)
493 ? "" : " MEMDEV", nfit_mem->dcr ? "" : " DCR");
494 return -ENODEV;
495 }
496
497 /*
498 * We've found enough to create an nvdimm, optionally
499 * find an associated BDW
500 */
501 list_add(&nfit_mem->list, &acpi_desc->dimms);
502
503 list_for_each_entry(nfit_bdw, &acpi_desc->bdws, list) { 482 list_for_each_entry(nfit_bdw, &acpi_desc->bdws, list) {
504 if (nfit_bdw->bdw->region_index != dcr) 483 if (nfit_bdw->bdw->region_index != dcr)
505 continue; 484 continue;
@@ -508,12 +487,12 @@ static int nfit_mem_add(struct acpi_nfit_desc *acpi_desc,
508 } 487 }
509 488
510 if (!nfit_mem->bdw) 489 if (!nfit_mem->bdw)
511 return 0; 490 return;
512 491
513 nfit_mem_find_spa_bdw(acpi_desc, nfit_mem); 492 nfit_mem_find_spa_bdw(acpi_desc, nfit_mem);
514 493
515 if (!nfit_mem->spa_bdw) 494 if (!nfit_mem->spa_bdw)
516 return 0; 495 return;
517 496
518 range_index = nfit_mem->spa_bdw->range_index; 497 range_index = nfit_mem->spa_bdw->range_index;
519 list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { 498 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 } 517 }
539 break; 518 break;
540 } 519 }
541
542 return 0;
543} 520}
544 521
545static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, 522static 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 struct nfit_mem *nfit_mem, *found; 525 struct nfit_mem *nfit_mem, *found;
549 struct nfit_memdev *nfit_memdev; 526 struct nfit_memdev *nfit_memdev;
550 int type = nfit_spa_type(spa); 527 int type = nfit_spa_type(spa);
551 u16 dcr;
552 528
553 switch (type) { 529 switch (type) {
554 case NFIT_SPA_DCR: 530 case NFIT_SPA_DCR:
@@ -559,14 +535,18 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc,
559 } 535 }
560 536
561 list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { 537 list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) {
562 int rc; 538 struct nfit_dcr *nfit_dcr;
539 u32 device_handle;
540 u16 dcr;
563 541
564 if (nfit_memdev->memdev->range_index != spa->range_index) 542 if (nfit_memdev->memdev->range_index != spa->range_index)
565 continue; 543 continue;
566 found = NULL; 544 found = NULL;
567 dcr = nfit_memdev->memdev->region_index; 545 dcr = nfit_memdev->memdev->region_index;
546 device_handle = nfit_memdev->memdev->device_handle;
568 list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) 547 list_for_each_entry(nfit_mem, &acpi_desc->dimms, list)
569 if (__to_nfit_memdev(nfit_mem)->region_index == dcr) { 548 if (__to_nfit_memdev(nfit_mem)->device_handle
549 == device_handle) {
570 found = nfit_mem; 550 found = nfit_mem;
571 break; 551 break;
572 } 552 }
@@ -579,6 +559,31 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc,
579 if (!nfit_mem) 559 if (!nfit_mem)
580 return -ENOMEM; 560 return -ENOMEM;
581 INIT_LIST_HEAD(&nfit_mem->list); 561 INIT_LIST_HEAD(&nfit_mem->list);
562 list_add(&nfit_mem->list, &acpi_desc->dimms);
563 }
564
565 list_for_each_entry(nfit_dcr, &acpi_desc->dcrs, list) {
566 if (nfit_dcr->dcr->region_index != dcr)
567 continue;
568 /*
569 * Record the control region for the dimm. For
570 * the ACPI 6.1 case, where there are separate
571 * control regions for the pmem vs blk
572 * interfaces, be sure to record the extended
573 * blk details.
574 */
575 if (!nfit_mem->dcr)
576 nfit_mem->dcr = nfit_dcr->dcr;
577 else if (nfit_mem->dcr->windows == 0
578 && nfit_dcr->dcr->windows)
579 nfit_mem->dcr = nfit_dcr->dcr;
580 break;
581 }
582
583 if (dcr && !nfit_mem->dcr) {
584 dev_err(acpi_desc->dev, "SPA %d missing DCR %d\n",
585 spa->range_index, dcr);
586 return -ENODEV;
582 } 587 }
583 588
584 if (type == NFIT_SPA_DCR) { 589 if (type == NFIT_SPA_DCR) {
@@ -595,6 +600,7 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc,
595 nfit_mem->idt_dcr = nfit_idt->idt; 600 nfit_mem->idt_dcr = nfit_idt->idt;
596 break; 601 break;
597 } 602 }
603 nfit_mem_init_bdw(acpi_desc, nfit_mem, spa);
598 } else { 604 } else {
599 /* 605 /*
600 * A single dimm may belong to multiple SPA-PM 606 * 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 */ 609 */
604 nfit_mem->memdev_pmem = nfit_memdev->memdev; 610 nfit_mem->memdev_pmem = nfit_memdev->memdev;
605 } 611 }
606
607 if (found)
608 continue;
609
610 rc = nfit_mem_add(acpi_desc, nfit_mem, spa);
611 if (rc)
612 return rc;
613 } 612 }
614 613
615 return 0; 614 return 0;
@@ -1504,9 +1503,7 @@ static int ars_do_start(struct nvdimm_bus_descriptor *nd_desc,
1504 case 1: 1503 case 1:
1505 /* ARS unsupported, but we should never get here */ 1504 /* ARS unsupported, but we should never get here */
1506 return 0; 1505 return 0;
1507 case 2: 1506 case 6:
1508 return -EINVAL;
1509 case 3:
1510 /* ARS is in progress */ 1507 /* ARS is in progress */
1511 msleep(1000); 1508 msleep(1000);
1512 break; 1509 break;
@@ -1517,13 +1514,13 @@ static int ars_do_start(struct nvdimm_bus_descriptor *nd_desc,
1517} 1514}
1518 1515
1519static int ars_get_status(struct nvdimm_bus_descriptor *nd_desc, 1516static int ars_get_status(struct nvdimm_bus_descriptor *nd_desc,
1520 struct nd_cmd_ars_status *cmd) 1517 struct nd_cmd_ars_status *cmd, u32 size)
1521{ 1518{
1522 int rc; 1519 int rc;
1523 1520
1524 while (1) { 1521 while (1) {
1525 rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_STATUS, cmd, 1522 rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_STATUS, cmd,
1526 sizeof(*cmd)); 1523 size);
1527 if (rc || cmd->status & 0xffff) 1524 if (rc || cmd->status & 0xffff)
1528 return -ENXIO; 1525 return -ENXIO;
1529 1526
@@ -1538,6 +1535,8 @@ static int ars_get_status(struct nvdimm_bus_descriptor *nd_desc,
1538 case 2: 1535 case 2:
1539 /* No ARS performed for the current boot */ 1536 /* No ARS performed for the current boot */
1540 return 0; 1537 return 0;
1538 case 3:
1539 /* TODO: error list overflow support */
1541 default: 1540 default:
1542 return -ENXIO; 1541 return -ENXIO;
1543 } 1542 }
@@ -1581,6 +1580,7 @@ static int acpi_nfit_find_poison(struct acpi_nfit_desc *acpi_desc,
1581 struct nd_cmd_ars_start *ars_start = NULL; 1580 struct nd_cmd_ars_start *ars_start = NULL;
1582 struct nd_cmd_ars_cap *ars_cap = NULL; 1581 struct nd_cmd_ars_cap *ars_cap = NULL;
1583 u64 start, len, cur, remaining; 1582 u64 start, len, cur, remaining;
1583 u32 ars_status_size;
1584 int rc; 1584 int rc;
1585 1585
1586 ars_cap = kzalloc(sizeof(*ars_cap), GFP_KERNEL); 1586 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 * Check if a full-range ARS has been run. If so, use those results 1610 * Check if a full-range ARS has been run. If so, use those results
1611 * without having to start a new ARS. 1611 * without having to start a new ARS.
1612 */ 1612 */
1613 ars_status = kzalloc(ars_cap->max_ars_out + sizeof(*ars_status), 1613 ars_status_size = ars_cap->max_ars_out;
1614 GFP_KERNEL); 1614 ars_status = kzalloc(ars_status_size, GFP_KERNEL);
1615 if (!ars_status) { 1615 if (!ars_status) {
1616 rc = -ENOMEM; 1616 rc = -ENOMEM;
1617 goto out; 1617 goto out;
1618 } 1618 }
1619 1619
1620 rc = ars_get_status(nd_desc, ars_status); 1620 rc = ars_get_status(nd_desc, ars_status, ars_status_size);
1621 if (rc) 1621 if (rc)
1622 goto out; 1622 goto out;
1623 1623
@@ -1647,7 +1647,7 @@ static int acpi_nfit_find_poison(struct acpi_nfit_desc *acpi_desc,
1647 if (rc) 1647 if (rc)
1648 goto out; 1648 goto out;
1649 1649
1650 rc = ars_get_status(nd_desc, ars_status); 1650 rc = ars_get_status(nd_desc, ars_status, ars_status_size);
1651 if (rc) 1651 if (rc)
1652 goto out; 1652 goto out;
1653 1653
diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c
index 7e2c43f701bc..5d28e9405f32 100644
--- a/drivers/nvdimm/bus.c
+++ b/drivers/nvdimm/bus.c
@@ -382,18 +382,18 @@ static const struct nd_cmd_desc __nd_cmd_bus_descs[] = {
382 [ND_CMD_ARS_CAP] = { 382 [ND_CMD_ARS_CAP] = {
383 .in_num = 2, 383 .in_num = 2,
384 .in_sizes = { 8, 8, }, 384 .in_sizes = { 8, 8, },
385 .out_num = 2, 385 .out_num = 4,
386 .out_sizes = { 4, 4, }, 386 .out_sizes = { 4, 4, 4, 4, },
387 }, 387 },
388 [ND_CMD_ARS_START] = { 388 [ND_CMD_ARS_START] = {
389 .in_num = 4, 389 .in_num = 5,
390 .in_sizes = { 8, 8, 2, 6, }, 390 .in_sizes = { 8, 8, 2, 1, 5, },
391 .out_num = 1, 391 .out_num = 2,
392 .out_sizes = { 4, }, 392 .out_sizes = { 4, 4, },
393 }, 393 },
394 [ND_CMD_ARS_STATUS] = { 394 [ND_CMD_ARS_STATUS] = {
395 .out_num = 2, 395 .out_num = 3,
396 .out_sizes = { 4, UINT_MAX, }, 396 .out_sizes = { 4, 4, UINT_MAX, },
397 }, 397 },
398}; 398};
399 399
@@ -442,8 +442,8 @@ u32 nd_cmd_out_size(struct nvdimm *nvdimm, int cmd,
442 return in_field[1]; 442 return in_field[1];
443 else if (nvdimm && cmd == ND_CMD_VENDOR && idx == 2) 443 else if (nvdimm && cmd == ND_CMD_VENDOR && idx == 2)
444 return out_field[1]; 444 return out_field[1];
445 else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 1) 445 else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 2)
446 return ND_CMD_ARS_STATUS_MAX; 446 return out_field[1] - 8;
447 447
448 return UINT_MAX; 448 return UINT_MAX;
449} 449}
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index 7edf31671dab..8d0b54670184 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -41,7 +41,7 @@ struct pmem_device {
41 phys_addr_t phys_addr; 41 phys_addr_t phys_addr;
42 /* when non-zero this device is hosting a 'pfn' instance */ 42 /* when non-zero this device is hosting a 'pfn' instance */
43 phys_addr_t data_offset; 43 phys_addr_t data_offset;
44 unsigned long pfn_flags; 44 u64 pfn_flags;
45 void __pmem *virt_addr; 45 void __pmem *virt_addr;
46 size_t size; 46 size_t size;
47 struct badblocks bb; 47 struct badblocks bb;
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index bed40dff0e86..141ffdd59960 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -26,9 +26,8 @@ enum {
26 26
27 /* need to set a limit somewhere, but yes, this is likely overkill */ 27 /* need to set a limit somewhere, but yes, this is likely overkill */
28 ND_IOCTL_MAX_BUFLEN = SZ_4M, 28 ND_IOCTL_MAX_BUFLEN = SZ_4M,
29 ND_CMD_MAX_ELEM = 4, 29 ND_CMD_MAX_ELEM = 5,
30 ND_CMD_MAX_ENVELOPE = 16, 30 ND_CMD_MAX_ENVELOPE = 16,
31 ND_CMD_ARS_STATUS_MAX = SZ_4K,
32 ND_MAX_MAPPINGS = 32, 31 ND_MAX_MAPPINGS = 32,
33 32
34 /* region flag indicating to direct-map persistent memory by default */ 33 /* region flag indicating to direct-map persistent memory by default */
diff --git a/include/uapi/linux/ndctl.h b/include/uapi/linux/ndctl.h
index 5b4a4be06e2b..cc68b92124d4 100644
--- a/include/uapi/linux/ndctl.h
+++ b/include/uapi/linux/ndctl.h
@@ -66,14 +66,18 @@ struct nd_cmd_ars_cap {
66 __u64 length; 66 __u64 length;
67 __u32 status; 67 __u32 status;
68 __u32 max_ars_out; 68 __u32 max_ars_out;
69 __u32 clear_err_unit;
70 __u32 reserved;
69} __packed; 71} __packed;
70 72
71struct nd_cmd_ars_start { 73struct nd_cmd_ars_start {
72 __u64 address; 74 __u64 address;
73 __u64 length; 75 __u64 length;
74 __u16 type; 76 __u16 type;
75 __u8 reserved[6]; 77 __u8 flags;
78 __u8 reserved[5];
76 __u32 status; 79 __u32 status;
80 __u32 scrub_time;
77} __packed; 81} __packed;
78 82
79struct nd_cmd_ars_status { 83struct nd_cmd_ars_status {
@@ -81,11 +85,14 @@ struct nd_cmd_ars_status {
81 __u32 out_length; 85 __u32 out_length;
82 __u64 address; 86 __u64 address;
83 __u64 length; 87 __u64 length;
88 __u64 restart_address;
89 __u64 restart_length;
84 __u16 type; 90 __u16 type;
91 __u16 flags;
85 __u32 num_records; 92 __u32 num_records;
86 struct nd_ars_record { 93 struct nd_ars_record {
87 __u32 handle; 94 __u32 handle;
88 __u32 flags; 95 __u32 reserved;
89 __u64 err_address; 96 __u64 err_address;
90 __u64 length; 97 __u64 length;
91 } __packed records[0]; 98 } __packed records[0];
diff --git a/kernel/memremap.c b/kernel/memremap.c
index 7a1b5c3ef14e..b981a7b023f0 100644
--- a/kernel/memremap.c
+++ b/kernel/memremap.c
@@ -136,8 +136,10 @@ void *devm_memremap(struct device *dev, resource_size_t offset,
136 if (addr) { 136 if (addr) {
137 *ptr = addr; 137 *ptr = addr;
138 devres_add(dev, ptr); 138 devres_add(dev, ptr);
139 } else 139 } else {
140 devres_free(ptr); 140 devres_free(ptr);
141 return ERR_PTR(-ENXIO);
142 }
141 143
142 return addr; 144 return addr;
143} 145}
diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c
index 90bd2ea41032..b3281dcd4a5d 100644
--- a/tools/testing/nvdimm/test/nfit.c
+++ b/tools/testing/nvdimm/test/nfit.c
@@ -217,13 +217,16 @@ static int nfit_test_cmd_set_config_data(struct nd_cmd_set_config_hdr *nd_cmd,
217 return rc; 217 return rc;
218} 218}
219 219
220#define NFIT_TEST_ARS_RECORDS 4
221
220static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd, 222static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd,
221 unsigned int buf_len) 223 unsigned int buf_len)
222{ 224{
223 if (buf_len < sizeof(*nd_cmd)) 225 if (buf_len < sizeof(*nd_cmd))
224 return -EINVAL; 226 return -EINVAL;
225 227
226 nd_cmd->max_ars_out = 256; 228 nd_cmd->max_ars_out = sizeof(struct nd_cmd_ars_status)
229 + NFIT_TEST_ARS_RECORDS * sizeof(struct nd_ars_record);
227 nd_cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16; 230 nd_cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16;
228 231
229 return 0; 232 return 0;
@@ -246,7 +249,8 @@ static int nfit_test_cmd_ars_status(struct nd_cmd_ars_status *nd_cmd,
246 if (buf_len < sizeof(*nd_cmd)) 249 if (buf_len < sizeof(*nd_cmd))
247 return -EINVAL; 250 return -EINVAL;
248 251
249 nd_cmd->out_length = 256; 252 nd_cmd->out_length = sizeof(struct nd_cmd_ars_status);
253 /* TODO: emit error records */
250 nd_cmd->num_records = 0; 254 nd_cmd->num_records = 0;
251 nd_cmd->address = 0; 255 nd_cmd->address = 0;
252 nd_cmd->length = -1ULL; 256 nd_cmd->length = -1ULL;