Skip to content

Commit

Permalink
acpi, nfit: Collect shutdown status
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Dan Williams committed Oct 17, 2018
1 parent 6f07f86 commit 0ead111
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 25 deletions.
77 changes: 76 additions & 1 deletion drivers/acpi/nfit/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <asm/cacheflush.h>
#include <acpi/nfit.h>
#include "nfit.h"
#include "intel.h"

/*
* For readq() and writeq() on 32-bit builds, the hi-lo, lo-hi order is
Expand Down Expand Up @@ -1551,7 +1552,12 @@ static DEVICE_ATTR_RO(dsm_mask);
static ssize_t flags_show(struct device *dev,
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",
flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save_fail " : "",
Expand Down Expand Up @@ -1582,6 +1588,16 @@ static ssize_t id_show(struct device *dev,
}
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[] = {
&dev_attr_handle.attr,
&dev_attr_phys_id.attr,
Expand All @@ -1599,6 +1615,7 @@ static struct attribute *acpi_nfit_dimm_attributes[] = {
&dev_attr_id.attr,
&dev_attr_family.attr,
&dev_attr_dsm_mask.attr,
&dev_attr_dirty_shutdown.attr,
NULL,
};

Expand All @@ -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 nvdimm *nvdimm = to_nvdimm(dev);
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);

if (!to_nfit_dcr(dev)) {
/* Without a dcr only the memdev attributes can be surfaced */
Expand All @@ -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)
return 0;

if (!test_bit(NFIT_MEM_DIRTY_COUNT, &nfit_mem->flags)
&& a == &dev_attr_dirty_shutdown.attr)
return 0;

return a->mode;
}

Expand Down Expand Up @@ -1698,6 +1721,56 @@ static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method)
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,
struct nfit_mem *nfit_mem, u32 device_handle)
{
Expand Down Expand Up @@ -1797,6 +1870,8 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
set_bit(NFIT_MEM_LSW, &nfit_mem->flags);
}

populate_shutdown_status(nfit_mem);

return 0;
}

Expand Down
38 changes: 38 additions & 0 deletions drivers/acpi/nfit/intel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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
3 changes: 3 additions & 0 deletions drivers/acpi/nfit/nfit.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ struct nfit_memdev {
enum nfit_mem_flags {
NFIT_MEM_LSR,
NFIT_MEM_LSW,
NFIT_MEM_DIRTY,
NFIT_MEM_DIRTY_COUNT,
};

/* assembled tables for a given dimm/memory-device */
Expand All @@ -184,6 +186,7 @@ struct nfit_mem {
struct resource *flush_wpq;
unsigned long dsm_mask;
unsigned long flags;
u32 dirty_shutdown;
int family;
};

Expand Down
1 change: 1 addition & 0 deletions tools/testing/nvdimm/test/nfit.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <linux/list.h>
#include <linux/slab.h>
#include <nd-core.h>
#include <intel.h>
#include <nfit.h>
#include <nd.h>
#include "nfit_test.h"
Expand Down
24 changes: 0 additions & 24 deletions tools/testing/nvdimm/test/nfit_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,30 +117,6 @@ struct nd_cmd_ars_err_inj_stat {
#define ND_INTEL_SMART_INJECT_FATAL (1 << 2)
#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 {
__u32 status;
union {
Expand Down

0 comments on commit 0ead111

Please sign in to comment.