Commit 0ead1118 authored by Dan Williams's avatar Dan Williams

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: default avatarKeith Busch <keith.busch@intel.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent 6f07f86c
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <acpi/nfit.h> #include <acpi/nfit.h>
#include "nfit.h" #include "nfit.h"
#include "intel.h"
/* /*
* For readq() and writeq() on 32-bit builds, the hi-lo, lo-hi order is * 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); ...@@ -1551,7 +1552,12 @@ static DEVICE_ATTR_RO(dsm_mask);
static ssize_t flags_show(struct device *dev, static ssize_t flags_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
u16 flags = to_nfit_memdev(dev)->flags; struct nvdimm *nvdimm = to_nvdimm(dev);
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
u16 flags = __to_nfit_memdev(nfit_mem)->flags;
if (test_bit(NFIT_MEM_DIRTY, &nfit_mem->flags))
flags |= ACPI_NFIT_MEM_FLUSH_FAILED;
return sprintf(buf, "%s%s%s%s%s%s%s\n", return sprintf(buf, "%s%s%s%s%s%s%s\n",
flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save_fail " : "", flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save_fail " : "",
...@@ -1582,6 +1588,16 @@ static ssize_t id_show(struct device *dev, ...@@ -1582,6 +1588,16 @@ static ssize_t id_show(struct device *dev,
} }
static DEVICE_ATTR_RO(id); static DEVICE_ATTR_RO(id);
static ssize_t dirty_shutdown_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct nvdimm *nvdimm = to_nvdimm(dev);
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
return sprintf(buf, "%d\n", nfit_mem->dirty_shutdown);
}
static DEVICE_ATTR_RO(dirty_shutdown);
static struct attribute *acpi_nfit_dimm_attributes[] = { static struct attribute *acpi_nfit_dimm_attributes[] = {
&dev_attr_handle.attr, &dev_attr_handle.attr,
&dev_attr_phys_id.attr, &dev_attr_phys_id.attr,
...@@ -1599,6 +1615,7 @@ static struct attribute *acpi_nfit_dimm_attributes[] = { ...@@ -1599,6 +1615,7 @@ static struct attribute *acpi_nfit_dimm_attributes[] = {
&dev_attr_id.attr, &dev_attr_id.attr,
&dev_attr_family.attr, &dev_attr_family.attr,
&dev_attr_dsm_mask.attr, &dev_attr_dsm_mask.attr,
&dev_attr_dirty_shutdown.attr,
NULL, NULL,
}; };
...@@ -1607,6 +1624,7 @@ static umode_t acpi_nfit_dimm_attr_visible(struct kobject *kobj, ...@@ -1607,6 +1624,7 @@ static umode_t acpi_nfit_dimm_attr_visible(struct kobject *kobj,
{ {
struct device *dev = container_of(kobj, struct device, kobj); struct device *dev = container_of(kobj, struct device, kobj);
struct nvdimm *nvdimm = to_nvdimm(dev); struct nvdimm *nvdimm = to_nvdimm(dev);
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
if (!to_nfit_dcr(dev)) { if (!to_nfit_dcr(dev)) {
/* Without a dcr only the memdev attributes can be surfaced */ /* 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, ...@@ -1620,6 +1638,11 @@ static umode_t acpi_nfit_dimm_attr_visible(struct kobject *kobj,
if (a == &dev_attr_format1.attr && num_nvdimm_formats(nvdimm) <= 1) if (a == &dev_attr_format1.attr && num_nvdimm_formats(nvdimm) <= 1)
return 0; return 0;
if (!test_bit(NFIT_MEM_DIRTY_COUNT, &nfit_mem->flags)
&& a == &dev_attr_dirty_shutdown.attr)
return 0;
return a->mode; return a->mode;
} }
...@@ -1698,6 +1721,56 @@ static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method) ...@@ -1698,6 +1721,56 @@ static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method)
return false; return false;
} }
static void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem)
{
struct nd_intel_smart smart = { 0 };
union acpi_object in_buf = {
.type = ACPI_TYPE_BUFFER,
.buffer.pointer = (char *) &smart,
.buffer.length = sizeof(smart),
};
union acpi_object in_obj = {
.type = ACPI_TYPE_PACKAGE,
.package.count = 1,
.package.elements = &in_buf,
};
const u8 func = ND_INTEL_SMART;
const guid_t *guid = to_nfit_uuid(nfit_mem->family);
u8 revid = nfit_dsm_revid(nfit_mem->family, func);
struct acpi_device *adev = nfit_mem->adev;
acpi_handle handle = adev->handle;
union acpi_object *out_obj;
if ((nfit_mem->dsm_mask & (1 << func)) == 0)
return;
out_obj = acpi_evaluate_dsm(handle, guid, revid, func, &in_obj);
if (!out_obj)
return;
if (smart.flags & ND_INTEL_SMART_SHUTDOWN_VALID) {
if (smart.shutdown_state)
set_bit(NFIT_MEM_DIRTY, &nfit_mem->flags);
}
if (smart.flags & ND_INTEL_SMART_SHUTDOWN_COUNT_VALID) {
set_bit(NFIT_MEM_DIRTY_COUNT, &nfit_mem->flags);
nfit_mem->dirty_shutdown = smart.shutdown_count;
}
ACPI_FREE(out_obj);
}
static void populate_shutdown_status(struct nfit_mem *nfit_mem)
{
/*
* For DIMMs that provide a dynamic facility to retrieve a
* dirty-shutdown status and/or a dirty-shutdown count, cache
* these values in nfit_mem.
*/
if (nfit_mem->family == NVDIMM_FAMILY_INTEL)
nfit_intel_shutdown_status(nfit_mem);
}
static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
struct nfit_mem *nfit_mem, u32 device_handle) struct nfit_mem *nfit_mem, u32 device_handle)
{ {
...@@ -1797,6 +1870,8 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, ...@@ -1797,6 +1870,8 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
set_bit(NFIT_MEM_LSW, &nfit_mem->flags); set_bit(NFIT_MEM_LSW, &nfit_mem->flags);
} }
populate_shutdown_status(nfit_mem);
return 0; return 0;
} }
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright(c) 2018 Intel Corporation. All rights reserved.
* Intel specific definitions for NVDIMM Firmware Interface Table - NFIT
*/
#ifndef _NFIT_INTEL_H_
#define _NFIT_INTEL_H_
#define ND_INTEL_SMART 1
#define ND_INTEL_SMART_SHUTDOWN_COUNT_VALID (1 << 5)
#define ND_INTEL_SMART_SHUTDOWN_VALID (1 << 10)
struct nd_intel_smart {
u32 status;
union {
struct {
u32 flags;
u8 reserved0[4];
u8 health;
u8 spares;
u8 life_used;
u8 alarm_flags;
u16 media_temperature;
u16 ctrl_temperature;
u32 shutdown_count;
u8 ait_status;
u16 pmic_temperature;
u8 reserved1[8];
u8 shutdown_state;
u32 vendor_size;
u8 vendor_data[92];
} __packed;
u8 data[128];
};
} __packed;
#endif
...@@ -162,6 +162,8 @@ struct nfit_memdev { ...@@ -162,6 +162,8 @@ struct nfit_memdev {
enum nfit_mem_flags { enum nfit_mem_flags {
NFIT_MEM_LSR, NFIT_MEM_LSR,
NFIT_MEM_LSW, NFIT_MEM_LSW,
NFIT_MEM_DIRTY,
NFIT_MEM_DIRTY_COUNT,
}; };
/* assembled tables for a given dimm/memory-device */ /* assembled tables for a given dimm/memory-device */
...@@ -184,6 +186,7 @@ struct nfit_mem { ...@@ -184,6 +186,7 @@ struct nfit_mem {
struct resource *flush_wpq; struct resource *flush_wpq;
unsigned long dsm_mask; unsigned long dsm_mask;
unsigned long flags; unsigned long flags;
u32 dirty_shutdown;
int family; int family;
}; };
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <nd-core.h> #include <nd-core.h>
#include <intel.h>
#include <nfit.h> #include <nfit.h>
#include <nd.h> #include <nd.h>
#include "nfit_test.h" #include "nfit_test.h"
......
...@@ -117,30 +117,6 @@ struct nd_cmd_ars_err_inj_stat { ...@@ -117,30 +117,6 @@ struct nd_cmd_ars_err_inj_stat {
#define ND_INTEL_SMART_INJECT_FATAL (1 << 2) #define ND_INTEL_SMART_INJECT_FATAL (1 << 2)
#define ND_INTEL_SMART_INJECT_SHUTDOWN (1 << 3) #define ND_INTEL_SMART_INJECT_SHUTDOWN (1 << 3)
struct nd_intel_smart {
__u32 status;
union {
struct {
__u32 flags;
__u8 reserved0[4];
__u8 health;
__u8 spares;
__u8 life_used;
__u8 alarm_flags;
__u16 media_temperature;
__u16 ctrl_temperature;
__u32 shutdown_count;
__u8 ait_status;
__u16 pmic_temperature;
__u8 reserved1[8];
__u8 shutdown_state;
__u32 vendor_size;
__u8 vendor_data[92];
} __packed;
__u8 data[128];
};
} __packed;
struct nd_intel_smart_threshold { struct nd_intel_smart_threshold {
__u32 status; __u32 status;
union { union {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment