Skip to content

Commit 0ead111

Browse files
committed
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 <[email protected]> Signed-off-by: Dan Williams <[email protected]>
1 parent 6f07f86 commit 0ead111

File tree

5 files changed

+118
-25
lines changed

5 files changed

+118
-25
lines changed

drivers/acpi/nfit/core.c

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <asm/cacheflush.h>
2626
#include <acpi/nfit.h>
2727
#include "nfit.h"
28+
#include "intel.h"
2829

2930
/*
3031
* For readq() and writeq() on 32-bit builds, the hi-lo, lo-hi order is
@@ -1551,7 +1552,12 @@ static DEVICE_ATTR_RO(dsm_mask);
15511552
static ssize_t flags_show(struct device *dev,
15521553
struct device_attribute *attr, char *buf)
15531554
{
1554-
u16 flags = to_nfit_memdev(dev)->flags;
1555+
struct nvdimm *nvdimm = to_nvdimm(dev);
1556+
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
1557+
u16 flags = __to_nfit_memdev(nfit_mem)->flags;
1558+
1559+
if (test_bit(NFIT_MEM_DIRTY, &nfit_mem->flags))
1560+
flags |= ACPI_NFIT_MEM_FLUSH_FAILED;
15551561

15561562
return sprintf(buf, "%s%s%s%s%s%s%s\n",
15571563
flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save_fail " : "",
@@ -1582,6 +1588,16 @@ static ssize_t id_show(struct device *dev,
15821588
}
15831589
static DEVICE_ATTR_RO(id);
15841590

1591+
static ssize_t dirty_shutdown_show(struct device *dev,
1592+
struct device_attribute *attr, char *buf)
1593+
{
1594+
struct nvdimm *nvdimm = to_nvdimm(dev);
1595+
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
1596+
1597+
return sprintf(buf, "%d\n", nfit_mem->dirty_shutdown);
1598+
}
1599+
static DEVICE_ATTR_RO(dirty_shutdown);
1600+
15851601
static struct attribute *acpi_nfit_dimm_attributes[] = {
15861602
&dev_attr_handle.attr,
15871603
&dev_attr_phys_id.attr,
@@ -1599,6 +1615,7 @@ static struct attribute *acpi_nfit_dimm_attributes[] = {
15991615
&dev_attr_id.attr,
16001616
&dev_attr_family.attr,
16011617
&dev_attr_dsm_mask.attr,
1618+
&dev_attr_dirty_shutdown.attr,
16021619
NULL,
16031620
};
16041621

@@ -1607,6 +1624,7 @@ static umode_t acpi_nfit_dimm_attr_visible(struct kobject *kobj,
16071624
{
16081625
struct device *dev = container_of(kobj, struct device, kobj);
16091626
struct nvdimm *nvdimm = to_nvdimm(dev);
1627+
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
16101628

16111629
if (!to_nfit_dcr(dev)) {
16121630
/* Without a dcr only the memdev attributes can be surfaced */
@@ -1620,6 +1638,11 @@ static umode_t acpi_nfit_dimm_attr_visible(struct kobject *kobj,
16201638

16211639
if (a == &dev_attr_format1.attr && num_nvdimm_formats(nvdimm) <= 1)
16221640
return 0;
1641+
1642+
if (!test_bit(NFIT_MEM_DIRTY_COUNT, &nfit_mem->flags)
1643+
&& a == &dev_attr_dirty_shutdown.attr)
1644+
return 0;
1645+
16231646
return a->mode;
16241647
}
16251648

@@ -1698,6 +1721,56 @@ static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method)
16981721
return false;
16991722
}
17001723

1724+
static void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem)
1725+
{
1726+
struct nd_intel_smart smart = { 0 };
1727+
union acpi_object in_buf = {
1728+
.type = ACPI_TYPE_BUFFER,
1729+
.buffer.pointer = (char *) &smart,
1730+
.buffer.length = sizeof(smart),
1731+
};
1732+
union acpi_object in_obj = {
1733+
.type = ACPI_TYPE_PACKAGE,
1734+
.package.count = 1,
1735+
.package.elements = &in_buf,
1736+
};
1737+
const u8 func = ND_INTEL_SMART;
1738+
const guid_t *guid = to_nfit_uuid(nfit_mem->family);
1739+
u8 revid = nfit_dsm_revid(nfit_mem->family, func);
1740+
struct acpi_device *adev = nfit_mem->adev;
1741+
acpi_handle handle = adev->handle;
1742+
union acpi_object *out_obj;
1743+
1744+
if ((nfit_mem->dsm_mask & (1 << func)) == 0)
1745+
return;
1746+
1747+
out_obj = acpi_evaluate_dsm(handle, guid, revid, func, &in_obj);
1748+
if (!out_obj)
1749+
return;
1750+
1751+
if (smart.flags & ND_INTEL_SMART_SHUTDOWN_VALID) {
1752+
if (smart.shutdown_state)
1753+
set_bit(NFIT_MEM_DIRTY, &nfit_mem->flags);
1754+
}
1755+
1756+
if (smart.flags & ND_INTEL_SMART_SHUTDOWN_COUNT_VALID) {
1757+
set_bit(NFIT_MEM_DIRTY_COUNT, &nfit_mem->flags);
1758+
nfit_mem->dirty_shutdown = smart.shutdown_count;
1759+
}
1760+
ACPI_FREE(out_obj);
1761+
}
1762+
1763+
static void populate_shutdown_status(struct nfit_mem *nfit_mem)
1764+
{
1765+
/*
1766+
* For DIMMs that provide a dynamic facility to retrieve a
1767+
* dirty-shutdown status and/or a dirty-shutdown count, cache
1768+
* these values in nfit_mem.
1769+
*/
1770+
if (nfit_mem->family == NVDIMM_FAMILY_INTEL)
1771+
nfit_intel_shutdown_status(nfit_mem);
1772+
}
1773+
17011774
static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
17021775
struct nfit_mem *nfit_mem, u32 device_handle)
17031776
{
@@ -1797,6 +1870,8 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
17971870
set_bit(NFIT_MEM_LSW, &nfit_mem->flags);
17981871
}
17991872

1873+
populate_shutdown_status(nfit_mem);
1874+
18001875
return 0;
18011876
}
18021877

drivers/acpi/nfit/intel.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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

drivers/acpi/nfit/nfit.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ struct nfit_memdev {
162162
enum nfit_mem_flags {
163163
NFIT_MEM_LSR,
164164
NFIT_MEM_LSW,
165+
NFIT_MEM_DIRTY,
166+
NFIT_MEM_DIRTY_COUNT,
165167
};
166168

167169
/* assembled tables for a given dimm/memory-device */
@@ -184,6 +186,7 @@ struct nfit_mem {
184186
struct resource *flush_wpq;
185187
unsigned long dsm_mask;
186188
unsigned long flags;
189+
u32 dirty_shutdown;
187190
int family;
188191
};
189192

tools/testing/nvdimm/test/nfit.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <linux/list.h>
2525
#include <linux/slab.h>
2626
#include <nd-core.h>
27+
#include <intel.h>
2728
#include <nfit.h>
2829
#include <nd.h>
2930
#include "nfit_test.h"

tools/testing/nvdimm/test/nfit_test.h

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -117,30 +117,6 @@ struct nd_cmd_ars_err_inj_stat {
117117
#define ND_INTEL_SMART_INJECT_FATAL (1 << 2)
118118
#define ND_INTEL_SMART_INJECT_SHUTDOWN (1 << 3)
119119

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-
144120
struct nd_intel_smart_threshold {
145121
__u32 status;
146122
union {

0 commit comments

Comments
 (0)