Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

acpi, nfit: Collect shutdown status

Some NVDIMMs, in addition to providing an indication of whether the
previous shutdown was clean, also provide a running count of lifetime
dirty-shutdown events for the device. In anticipation of this
functionality appearing on more devices arrange for the nfit driver to
retrieve / cache this data at DIMM discovery time, and export it via
sysfs.

Reviewed-by: Keith Busch <keith.busch@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>

+118 -25
+76 -1
drivers/acpi/nfit/core.c
··· 25 25 #include <asm/cacheflush.h> 26 26 #include <acpi/nfit.h> 27 27 #include "nfit.h" 28 + #include "intel.h" 28 29 29 30 /* 30 31 * For readq() and writeq() on 32-bit builds, the hi-lo, lo-hi order is ··· 1552 1551 static ssize_t flags_show(struct device *dev, 1553 1552 struct device_attribute *attr, char *buf) 1554 1553 { 1555 - u16 flags = to_nfit_memdev(dev)->flags; 1554 + struct nvdimm *nvdimm = to_nvdimm(dev); 1555 + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); 1556 + u16 flags = __to_nfit_memdev(nfit_mem)->flags; 1557 + 1558 + if (test_bit(NFIT_MEM_DIRTY, &nfit_mem->flags)) 1559 + flags |= ACPI_NFIT_MEM_FLUSH_FAILED; 1556 1560 1557 1561 return sprintf(buf, "%s%s%s%s%s%s%s\n", 1558 1562 flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save_fail " : "", ··· 1588 1582 } 1589 1583 static DEVICE_ATTR_RO(id); 1590 1584 1585 + static ssize_t dirty_shutdown_show(struct device *dev, 1586 + struct device_attribute *attr, char *buf) 1587 + { 1588 + struct nvdimm *nvdimm = to_nvdimm(dev); 1589 + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); 1590 + 1591 + return sprintf(buf, "%d\n", nfit_mem->dirty_shutdown); 1592 + } 1593 + static DEVICE_ATTR_RO(dirty_shutdown); 1594 + 1591 1595 static struct attribute *acpi_nfit_dimm_attributes[] = { 1592 1596 &dev_attr_handle.attr, 1593 1597 &dev_attr_phys_id.attr, ··· 1615 1599 &dev_attr_id.attr, 1616 1600 &dev_attr_family.attr, 1617 1601 &dev_attr_dsm_mask.attr, 1602 + &dev_attr_dirty_shutdown.attr, 1618 1603 NULL, 1619 1604 }; 1620 1605 ··· 1624 1607 { 1625 1608 struct device *dev = container_of(kobj, struct device, kobj); 1626 1609 struct nvdimm *nvdimm = to_nvdimm(dev); 1610 + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); 1627 1611 1628 1612 if (!to_nfit_dcr(dev)) { 1629 1613 /* Without a dcr only the memdev attributes can be surfaced */ ··· 1638 1620 1639 1621 if (a == &dev_attr_format1.attr && num_nvdimm_formats(nvdimm) <= 1) 1640 1622 return 0; 1623 + 1624 + if (!test_bit(NFIT_MEM_DIRTY_COUNT, &nfit_mem->flags) 1625 + && a == &dev_attr_dirty_shutdown.attr) 1626 + return 0; 1627 + 1641 1628 return a->mode; 1642 1629 } 1643 1630 ··· 1719 1696 if (ACPI_SUCCESS(status)) 1720 1697 return true; 1721 1698 return false; 1699 + } 1700 + 1701 + static void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem) 1702 + { 1703 + struct nd_intel_smart smart = { 0 }; 1704 + union acpi_object in_buf = { 1705 + .type = ACPI_TYPE_BUFFER, 1706 + .buffer.pointer = (char *) &smart, 1707 + .buffer.length = sizeof(smart), 1708 + }; 1709 + union acpi_object in_obj = { 1710 + .type = ACPI_TYPE_PACKAGE, 1711 + .package.count = 1, 1712 + .package.elements = &in_buf, 1713 + }; 1714 + const u8 func = ND_INTEL_SMART; 1715 + const guid_t *guid = to_nfit_uuid(nfit_mem->family); 1716 + u8 revid = nfit_dsm_revid(nfit_mem->family, func); 1717 + struct acpi_device *adev = nfit_mem->adev; 1718 + acpi_handle handle = adev->handle; 1719 + union acpi_object *out_obj; 1720 + 1721 + if ((nfit_mem->dsm_mask & (1 << func)) == 0) 1722 + return; 1723 + 1724 + out_obj = acpi_evaluate_dsm(handle, guid, revid, func, &in_obj); 1725 + if (!out_obj) 1726 + return; 1727 + 1728 + if (smart.flags & ND_INTEL_SMART_SHUTDOWN_VALID) { 1729 + if (smart.shutdown_state) 1730 + set_bit(NFIT_MEM_DIRTY, &nfit_mem->flags); 1731 + } 1732 + 1733 + if (smart.flags & ND_INTEL_SMART_SHUTDOWN_COUNT_VALID) { 1734 + set_bit(NFIT_MEM_DIRTY_COUNT, &nfit_mem->flags); 1735 + nfit_mem->dirty_shutdown = smart.shutdown_count; 1736 + } 1737 + ACPI_FREE(out_obj); 1738 + } 1739 + 1740 + static void populate_shutdown_status(struct nfit_mem *nfit_mem) 1741 + { 1742 + /* 1743 + * For DIMMs that provide a dynamic facility to retrieve a 1744 + * dirty-shutdown status and/or a dirty-shutdown count, cache 1745 + * these values in nfit_mem. 1746 + */ 1747 + if (nfit_mem->family == NVDIMM_FAMILY_INTEL) 1748 + nfit_intel_shutdown_status(nfit_mem); 1722 1749 } 1723 1750 1724 1751 static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, ··· 1869 1796 dev_dbg(dev, "%s: has _LSW\n", dev_name(&adev_dimm->dev)); 1870 1797 set_bit(NFIT_MEM_LSW, &nfit_mem->flags); 1871 1798 } 1799 + 1800 + populate_shutdown_status(nfit_mem); 1872 1801 1873 1802 return 0; 1874 1803 }
+38
drivers/acpi/nfit/intel.h
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright(c) 2018 Intel Corporation. All rights reserved. 4 + * Intel specific definitions for NVDIMM Firmware Interface Table - NFIT 5 + */ 6 + #ifndef _NFIT_INTEL_H_ 7 + #define _NFIT_INTEL_H_ 8 + 9 + #define ND_INTEL_SMART 1 10 + 11 + #define ND_INTEL_SMART_SHUTDOWN_COUNT_VALID (1 << 5) 12 + #define ND_INTEL_SMART_SHUTDOWN_VALID (1 << 10) 13 + 14 + struct nd_intel_smart { 15 + u32 status; 16 + union { 17 + struct { 18 + u32 flags; 19 + u8 reserved0[4]; 20 + u8 health; 21 + u8 spares; 22 + u8 life_used; 23 + u8 alarm_flags; 24 + u16 media_temperature; 25 + u16 ctrl_temperature; 26 + u32 shutdown_count; 27 + u8 ait_status; 28 + u16 pmic_temperature; 29 + u8 reserved1[8]; 30 + u8 shutdown_state; 31 + u32 vendor_size; 32 + u8 vendor_data[92]; 33 + } __packed; 34 + u8 data[128]; 35 + }; 36 + } __packed; 37 + 38 + #endif
+3
drivers/acpi/nfit/nfit.h
··· 162 162 enum nfit_mem_flags { 163 163 NFIT_MEM_LSR, 164 164 NFIT_MEM_LSW, 165 + NFIT_MEM_DIRTY, 166 + NFIT_MEM_DIRTY_COUNT, 165 167 }; 166 168 167 169 /* assembled tables for a given dimm/memory-device */ ··· 186 184 struct resource *flush_wpq; 187 185 unsigned long dsm_mask; 188 186 unsigned long flags; 187 + u32 dirty_shutdown; 189 188 int family; 190 189 }; 191 190
+1
tools/testing/nvdimm/test/nfit.c
··· 24 24 #include <linux/list.h> 25 25 #include <linux/slab.h> 26 26 #include <nd-core.h> 27 + #include <intel.h> 27 28 #include <nfit.h> 28 29 #include <nd.h> 29 30 #include "nfit_test.h"
-24
tools/testing/nvdimm/test/nfit_test.h
··· 117 117 #define ND_INTEL_SMART_INJECT_FATAL (1 << 2) 118 118 #define ND_INTEL_SMART_INJECT_SHUTDOWN (1 << 3) 119 119 120 - struct nd_intel_smart { 121 - __u32 status; 122 - union { 123 - struct { 124 - __u32 flags; 125 - __u8 reserved0[4]; 126 - __u8 health; 127 - __u8 spares; 128 - __u8 life_used; 129 - __u8 alarm_flags; 130 - __u16 media_temperature; 131 - __u16 ctrl_temperature; 132 - __u32 shutdown_count; 133 - __u8 ait_status; 134 - __u16 pmic_temperature; 135 - __u8 reserved1[8]; 136 - __u8 shutdown_state; 137 - __u32 vendor_size; 138 - __u8 vendor_data[92]; 139 - } __packed; 140 - __u8 data[128]; 141 - }; 142 - } __packed; 143 - 144 120 struct nd_intel_smart_threshold { 145 121 __u32 status; 146 122 union {