diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-13 22:15:14 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-13 22:15:14 -0500 |
commit | d080827f850ba4df5b955d5ca8c8c0fc92fe18c0 (patch) | |
tree | 37262315200bbbe50bdd64ce3011951a92855159 /drivers/acpi | |
parent | cbd88cd4c07f9361914ab7fd7e21c9227986fe68 (diff) | |
parent | 8b63b6bfc1a551acf154061699028c7032d7890c (diff) |
Merge tag 'libnvdimm-for-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm
Pull libnvdimm updates from Dan Williams:
"The bulk of this has appeared in -next and independently received a
build success notification from the kbuild robot. The 'for-4.5/block-
dax' topic branch was rebased over the weekend to drop the "block
device end-of-life" rework that Al would like to see re-implemented
with a notifier, and to address bug reports against the badblocks
integration.
There is pending feedback against "libnvdimm: Add a poison list and
export badblocks" received last week. Linda identified some localized
fixups that we will handle incrementally.
Summary:
- Media error handling: The 'badblocks' implementation that
originated in md-raid is up-levelled to a generic capability of a
block device. This initial implementation is limited to being
consulted in the pmem block-i/o path. Later, 'badblocks' will be
consulted when creating dax mappings.
- Raw block device dax: For virtualization and other cases that want
large contiguous mappings of persistent memory, add the capability
to dax-mmap a block device directly.
- Increased /dev/mem restrictions: Add an option to treat all
io-memory as IORESOURCE_EXCLUSIVE, i.e. disable /dev/mem access
while a driver is actively using an address range. This behavior
is controlled via the new CONFIG_IO_STRICT_DEVMEM option and can be
overridden by the existing "iomem=relaxed" kernel command line
option.
- Miscellaneous fixes include a 'pfn'-device huge page alignment fix,
block device shutdown crash fix, and other small libnvdimm fixes"
* tag 'libnvdimm-for-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm: (32 commits)
block: kill disk_{check|set|clear|alloc}_badblocks
libnvdimm, pmem: nvdimm_read_bytes() badblocks support
pmem, dax: disable dax in the presence of bad blocks
pmem: fail io-requests to known bad blocks
libnvdimm: convert to statically allocated badblocks
libnvdimm: don't fail init for full badblocks list
block, badblocks: introduce devm_init_badblocks
block: clarify badblocks lifetime
badblocks: rename badblocks_free to badblocks_exit
libnvdimm, pmem: move definition of nvdimm_namespace_add_poison to nd.h
libnvdimm: Add a poison list and export badblocks
nfit_test: Enable DSMs for all test NFITs
md: convert to use the generic badblocks code
block: Add badblock management for gendisks
badblocks: Add core badblock management code
block: fix del_gendisk() vs blkdev_ioctl crash
block: enable dax for raw block devices
block: introduce bdev_file_inode()
restrict /dev/mem to idle io memory ranges
arch: consolidate CONFIG_STRICT_DEVM in lib/Kconfig.debug
...
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/nfit.c | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index aa45d4802707..ad6d8c6b777e 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/mutex.h> | 16 | #include <linux/mutex.h> |
17 | #include <linux/ndctl.h> | 17 | #include <linux/ndctl.h> |
18 | #include <linux/delay.h> | ||
18 | #include <linux/list.h> | 19 | #include <linux/list.h> |
19 | #include <linux/acpi.h> | 20 | #include <linux/acpi.h> |
20 | #include <linux/sort.h> | 21 | #include <linux/sort.h> |
@@ -1473,6 +1474,201 @@ static void acpi_nfit_blk_region_disable(struct nvdimm_bus *nvdimm_bus, | |||
1473 | /* devm will free nfit_blk */ | 1474 | /* devm will free nfit_blk */ |
1474 | } | 1475 | } |
1475 | 1476 | ||
1477 | static int ars_get_cap(struct nvdimm_bus_descriptor *nd_desc, | ||
1478 | struct nd_cmd_ars_cap *cmd, u64 addr, u64 length) | ||
1479 | { | ||
1480 | cmd->address = addr; | ||
1481 | cmd->length = length; | ||
1482 | |||
1483 | return nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_CAP, cmd, | ||
1484 | sizeof(*cmd)); | ||
1485 | } | ||
1486 | |||
1487 | static int ars_do_start(struct nvdimm_bus_descriptor *nd_desc, | ||
1488 | struct nd_cmd_ars_start *cmd, u64 addr, u64 length) | ||
1489 | { | ||
1490 | int rc; | ||
1491 | |||
1492 | cmd->address = addr; | ||
1493 | cmd->length = length; | ||
1494 | cmd->type = ND_ARS_PERSISTENT; | ||
1495 | |||
1496 | while (1) { | ||
1497 | rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_START, cmd, | ||
1498 | sizeof(*cmd)); | ||
1499 | if (rc) | ||
1500 | return rc; | ||
1501 | switch (cmd->status) { | ||
1502 | case 0: | ||
1503 | return 0; | ||
1504 | case 1: | ||
1505 | /* ARS unsupported, but we should never get here */ | ||
1506 | return 0; | ||
1507 | case 2: | ||
1508 | return -EINVAL; | ||
1509 | case 3: | ||
1510 | /* ARS is in progress */ | ||
1511 | msleep(1000); | ||
1512 | break; | ||
1513 | default: | ||
1514 | return -ENXIO; | ||
1515 | } | ||
1516 | } | ||
1517 | } | ||
1518 | |||
1519 | static int ars_get_status(struct nvdimm_bus_descriptor *nd_desc, | ||
1520 | struct nd_cmd_ars_status *cmd) | ||
1521 | { | ||
1522 | int rc; | ||
1523 | |||
1524 | while (1) { | ||
1525 | rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_STATUS, cmd, | ||
1526 | sizeof(*cmd)); | ||
1527 | if (rc || cmd->status & 0xffff) | ||
1528 | return -ENXIO; | ||
1529 | |||
1530 | /* Check extended status (Upper two bytes) */ | ||
1531 | switch (cmd->status >> 16) { | ||
1532 | case 0: | ||
1533 | return 0; | ||
1534 | case 1: | ||
1535 | /* ARS is in progress */ | ||
1536 | msleep(1000); | ||
1537 | break; | ||
1538 | case 2: | ||
1539 | /* No ARS performed for the current boot */ | ||
1540 | return 0; | ||
1541 | default: | ||
1542 | return -ENXIO; | ||
1543 | } | ||
1544 | } | ||
1545 | } | ||
1546 | |||
1547 | static int ars_status_process_records(struct nvdimm_bus *nvdimm_bus, | ||
1548 | struct nd_cmd_ars_status *ars_status, u64 start) | ||
1549 | { | ||
1550 | int rc; | ||
1551 | u32 i; | ||
1552 | |||
1553 | /* | ||
1554 | * The address field returned by ars_status should be either | ||
1555 | * less than or equal to the address we last started ARS for. | ||
1556 | * The (start, length) returned by ars_status should also have | ||
1557 | * non-zero overlap with the range we started ARS for. | ||
1558 | * If this is not the case, bail. | ||
1559 | */ | ||
1560 | if (ars_status->address > start || | ||
1561 | (ars_status->address + ars_status->length < start)) | ||
1562 | return -ENXIO; | ||
1563 | |||
1564 | for (i = 0; i < ars_status->num_records; i++) { | ||
1565 | rc = nvdimm_bus_add_poison(nvdimm_bus, | ||
1566 | ars_status->records[i].err_address, | ||
1567 | ars_status->records[i].length); | ||
1568 | if (rc) | ||
1569 | return rc; | ||
1570 | } | ||
1571 | |||
1572 | return 0; | ||
1573 | } | ||
1574 | |||
1575 | static int acpi_nfit_find_poison(struct acpi_nfit_desc *acpi_desc, | ||
1576 | struct nd_region_desc *ndr_desc) | ||
1577 | { | ||
1578 | struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc; | ||
1579 | struct nvdimm_bus *nvdimm_bus = acpi_desc->nvdimm_bus; | ||
1580 | struct nd_cmd_ars_status *ars_status = NULL; | ||
1581 | struct nd_cmd_ars_start *ars_start = NULL; | ||
1582 | struct nd_cmd_ars_cap *ars_cap = NULL; | ||
1583 | u64 start, len, cur, remaining; | ||
1584 | int rc; | ||
1585 | |||
1586 | ars_cap = kzalloc(sizeof(*ars_cap), GFP_KERNEL); | ||
1587 | if (!ars_cap) | ||
1588 | return -ENOMEM; | ||
1589 | |||
1590 | start = ndr_desc->res->start; | ||
1591 | len = ndr_desc->res->end - ndr_desc->res->start + 1; | ||
1592 | |||
1593 | rc = ars_get_cap(nd_desc, ars_cap, start, len); | ||
1594 | if (rc) | ||
1595 | goto out; | ||
1596 | |||
1597 | /* | ||
1598 | * If ARS is unsupported, or if the 'Persistent Memory Scrub' flag in | ||
1599 | * extended status is not set, skip this but continue initialization | ||
1600 | */ | ||
1601 | if ((ars_cap->status & 0xffff) || | ||
1602 | !(ars_cap->status >> 16 & ND_ARS_PERSISTENT)) { | ||
1603 | dev_warn(acpi_desc->dev, | ||
1604 | "ARS unsupported (status: 0x%x), won't create an error list\n", | ||
1605 | ars_cap->status); | ||
1606 | goto out; | ||
1607 | } | ||
1608 | |||
1609 | /* | ||
1610 | * Check if a full-range ARS has been run. If so, use those results | ||
1611 | * without having to start a new ARS. | ||
1612 | */ | ||
1613 | ars_status = kzalloc(ars_cap->max_ars_out + sizeof(*ars_status), | ||
1614 | GFP_KERNEL); | ||
1615 | if (!ars_status) { | ||
1616 | rc = -ENOMEM; | ||
1617 | goto out; | ||
1618 | } | ||
1619 | |||
1620 | rc = ars_get_status(nd_desc, ars_status); | ||
1621 | if (rc) | ||
1622 | goto out; | ||
1623 | |||
1624 | if (ars_status->address <= start && | ||
1625 | (ars_status->address + ars_status->length >= start + len)) { | ||
1626 | rc = ars_status_process_records(nvdimm_bus, ars_status, start); | ||
1627 | goto out; | ||
1628 | } | ||
1629 | |||
1630 | /* | ||
1631 | * ARS_STATUS can overflow if the number of poison entries found is | ||
1632 | * greater than the maximum buffer size (ars_cap->max_ars_out) | ||
1633 | * To detect overflow, check if the length field of ars_status | ||
1634 | * is less than the length we supplied. If so, process the | ||
1635 | * error entries we got, adjust the start point, and start again | ||
1636 | */ | ||
1637 | ars_start = kzalloc(sizeof(*ars_start), GFP_KERNEL); | ||
1638 | if (!ars_start) | ||
1639 | return -ENOMEM; | ||
1640 | |||
1641 | cur = start; | ||
1642 | remaining = len; | ||
1643 | do { | ||
1644 | u64 done, end; | ||
1645 | |||
1646 | rc = ars_do_start(nd_desc, ars_start, cur, remaining); | ||
1647 | if (rc) | ||
1648 | goto out; | ||
1649 | |||
1650 | rc = ars_get_status(nd_desc, ars_status); | ||
1651 | if (rc) | ||
1652 | goto out; | ||
1653 | |||
1654 | rc = ars_status_process_records(nvdimm_bus, ars_status, cur); | ||
1655 | if (rc) | ||
1656 | goto out; | ||
1657 | |||
1658 | end = min(cur + remaining, | ||
1659 | ars_status->address + ars_status->length); | ||
1660 | done = end - cur; | ||
1661 | cur += done; | ||
1662 | remaining -= done; | ||
1663 | } while (remaining); | ||
1664 | |||
1665 | out: | ||
1666 | kfree(ars_cap); | ||
1667 | kfree(ars_start); | ||
1668 | kfree(ars_status); | ||
1669 | return rc; | ||
1670 | } | ||
1671 | |||
1476 | static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc, | 1672 | static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc, |
1477 | struct nd_mapping *nd_mapping, struct nd_region_desc *ndr_desc, | 1673 | struct nd_mapping *nd_mapping, struct nd_region_desc *ndr_desc, |
1478 | struct acpi_nfit_memory_map *memdev, | 1674 | struct acpi_nfit_memory_map *memdev, |
@@ -1585,6 +1781,13 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc, | |||
1585 | 1781 | ||
1586 | nvdimm_bus = acpi_desc->nvdimm_bus; | 1782 | nvdimm_bus = acpi_desc->nvdimm_bus; |
1587 | if (nfit_spa_type(spa) == NFIT_SPA_PM) { | 1783 | if (nfit_spa_type(spa) == NFIT_SPA_PM) { |
1784 | rc = acpi_nfit_find_poison(acpi_desc, ndr_desc); | ||
1785 | if (rc) { | ||
1786 | dev_err(acpi_desc->dev, | ||
1787 | "error while performing ARS to find poison: %d\n", | ||
1788 | rc); | ||
1789 | return rc; | ||
1790 | } | ||
1588 | if (!nvdimm_pmem_region_create(nvdimm_bus, ndr_desc)) | 1791 | if (!nvdimm_pmem_region_create(nvdimm_bus, ndr_desc)) |
1589 | return -ENOMEM; | 1792 | return -ENOMEM; |
1590 | } else if (nfit_spa_type(spa) == NFIT_SPA_VOLATILE) { | 1793 | } else if (nfit_spa_type(spa) == NFIT_SPA_VOLATILE) { |