[PATCH v2] libnvdimm/security: Support a zero-key for secure-erase
by Dave Jiang
Adding support to allow secure erase to happen when security state is not
enabled. Key data of 0's will be passed in.
Signed-off-by: Dave Jiang <dave.jiang(a)intel.com>
---
v2:
- Make patch header explicitly zero key (Dan)
- Declare global static zero key (Dan)
- Make nfit_test explicitly test zero key (Dan)
drivers/nvdimm/security.c | 17 ++++++++++++-----
tools/testing/nvdimm/test/nfit.c | 11 +++++++++--
2 files changed, 21 insertions(+), 7 deletions(-)
diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c
index f8bb746a549f..6bea6852bf27 100644
--- a/drivers/nvdimm/security.c
+++ b/drivers/nvdimm/security.c
@@ -22,6 +22,8 @@ static bool key_revalidate = true;
module_param(key_revalidate, bool, 0444);
MODULE_PARM_DESC(key_revalidate, "Require key validation at init.");
+static const char zero_key[NVDIMM_PASSPHRASE_LEN];
+
static void *key_data(struct key *key)
{
struct encrypted_key_payload *epayload = dereference_key_locked(key);
@@ -286,8 +288,9 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
{
struct device *dev = &nvdimm->dev;
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
- struct key *key;
+ struct key *key = NULL;
int rc;
+ const void *data;
/* The bus lock should be held at the top level of the call stack */
lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
@@ -319,11 +322,15 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
return -EOPNOTSUPP;
}
- key = nvdimm_lookup_user_key(nvdimm, keyid, NVDIMM_BASE_KEY);
- if (!key)
- return -ENOKEY;
+ if (keyid != 0) {
+ key = nvdimm_lookup_user_key(nvdimm, keyid, NVDIMM_BASE_KEY);
+ if (!key)
+ return -ENOKEY;
+ data = key_data(key);
+ } else
+ data = zero_key;
- rc = nvdimm->sec.ops->erase(nvdimm, key_data(key), pass_type);
+ rc = nvdimm->sec.ops->erase(nvdimm, data, pass_type);
dev_dbg(dev, "key: %d erase%s: %s\n", key_serial(key),
pass_type == NVDIMM_MASTER ? "(master)" : "(user)",
rc == 0 ? "success" : "fail");
diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c
index b579f962451d..cad719876ef4 100644
--- a/tools/testing/nvdimm/test/nfit.c
+++ b/tools/testing/nvdimm/test/nfit.c
@@ -225,6 +225,8 @@ static struct workqueue_struct *nfit_wq;
static struct gen_pool *nfit_pool;
+static const char zero_key[NVDIMM_PASSPHRASE_LEN];
+
static struct nfit_test *to_nfit_test(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -1059,8 +1061,7 @@ static int nd_intel_test_cmd_secure_erase(struct nfit_test *t,
struct device *dev = &t->pdev.dev;
struct nfit_test_sec *sec = &dimm_sec_info[dimm];
- if (!(sec->state & ND_INTEL_SEC_STATE_ENABLED) ||
- (sec->state & ND_INTEL_SEC_STATE_FROZEN)) {
+ if (sec->state & ND_INTEL_SEC_STATE_FROZEN) {
nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE;
dev_dbg(dev, "secure erase: wrong security state\n");
} else if (memcmp(nd_cmd->passphrase, sec->passphrase,
@@ -1068,6 +1069,12 @@ static int nd_intel_test_cmd_secure_erase(struct nfit_test *t,
nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS;
dev_dbg(dev, "secure erase: wrong passphrase\n");
} else {
+ if (!(sec->state & ND_INTEL_SEC_STATE_ENABLED)
+ && (memcmp(nd_cmd->passphrase, zero_key,
+ ND_INTEL_PASSPHRASE_SIZE) != 0)) {
+ dev_dbg(dev, "invalid zero key\n");
+ return 0;
+ }
memset(sec->passphrase, 0, ND_INTEL_PASSPHRASE_SIZE);
memset(sec->master_passphrase, 0, ND_INTEL_PASSPHRASE_SIZE);
sec->state = 0;
3 years, 4 months
[PATCH] nvdimm: btt_devs: fix a NULL pointer dereference and a memory leak
by Kangjie Lu
In case kmemdup fails, the fix releases resources and returns to
avoid the NULL pointer dereference.
Also, the error paths in the following code should release
resources to avoid memory leaks.
Signed-off-by: Kangjie Lu <kjlu(a)umn.edu>
---
drivers/nvdimm/btt_devs.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/nvdimm/btt_devs.c b/drivers/nvdimm/btt_devs.c
index 795ad4ff35ca..565ea0b6f765 100644
--- a/drivers/nvdimm/btt_devs.c
+++ b/drivers/nvdimm/btt_devs.c
@@ -196,8 +196,13 @@ static struct device *__nd_btt_create(struct nd_region *nd_region,
}
nd_btt->lbasize = lbasize;
- if (uuid)
+ if (uuid) {
uuid = kmemdup(uuid, 16, GFP_KERNEL);
+ if (!uuid) {
+ kfree(nd_btt);
+ return NULL;
+ }
+ }
nd_btt->uuid = uuid;
dev = &nd_btt->dev;
dev_set_name(dev, "btt%d.%d", nd_region->id, nd_btt->id);
@@ -209,6 +214,7 @@ static struct device *__nd_btt_create(struct nd_region *nd_region,
dev_dbg(&ndns->dev, "failed, already claimed by %s\n",
dev_name(ndns->claim));
put_device(dev);
+ kfree(uuid);
return NULL;
}
return dev;
--
2.17.1
3 years, 4 months
[ndctl PATCH 0/5] ndctl: Optimize label operations
by Dan Williams
In almost all use cases ndctl was reading more label data than necessary
to carry out tasks like "init-labels" and the auto-label capability of
"create-namespace".
Outside of index-block scoped operations above, there is utility in
being able to specify an extent range smaller than the full capacity to
"{read,write,zero}-labels".
Deploy optimizations to only operate on the index blocks when possible,
and allow for custom extents for the generic operations.
---
Dan Williams (5):
ndctl/dimm: Support small label reads/writes
ndctl/dimm: Minimize data-transfer for init-labels
ndctl/dimm: Add offset and size options to {read,write,zero}-labels
ndctl/dimm: Limit read-labels with --index option
ndctl/namespace: Minimize label data transfer for autolabel
Documentation/ndctl/labels-options.txt | 9 +++
ndctl/dimm.c | 88 ++++++++++++++++++--------
ndctl/lib/dimm.c | 85 +++++++++++++++++++++++--
ndctl/lib/libndctl.c | 107 ++++++++++++++++++++++++++++----
ndctl/lib/libndctl.sym | 5 +
ndctl/lib/private.h | 4 -
ndctl/libndctl.h | 9 +++
ndctl/namespace.c | 2 -
util/util.h | 4 +
9 files changed, 260 insertions(+), 53 deletions(-)
3 years, 4 months
[PATCH] nvdimm: security: allow secure erase to execute without key
by Dave Jiang
Adding support to allow secure erase to happen when security state is not
enabled. Key data of 0's will be passed in.
Signed-off-by: Dave Jiang <dave.jiang(a)intel.com>
---
drivers/nvdimm/security.c | 17 ++++++++++++-----
tools/testing/nvdimm/test/nfit.c | 3 +--
2 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c
index f8bb746a549f..b7bd26030964 100644
--- a/drivers/nvdimm/security.c
+++ b/drivers/nvdimm/security.c
@@ -286,8 +286,9 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
{
struct device *dev = &nvdimm->dev;
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
- struct key *key;
+ struct key *key = NULL;
int rc;
+ char *data, dummy_key[NVDIMM_PASSPHRASE_LEN];
/* The bus lock should be held at the top level of the call stack */
lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
@@ -319,11 +320,17 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid,
return -EOPNOTSUPP;
}
- key = nvdimm_lookup_user_key(nvdimm, keyid, NVDIMM_BASE_KEY);
- if (!key)
- return -ENOKEY;
+ if (keyid != 0) {
+ key = nvdimm_lookup_user_key(nvdimm, keyid, NVDIMM_BASE_KEY);
+ if (!key)
+ return -ENOKEY;
+ data = key_data(key);
+ } else {
+ memset(dummy_key, 0, NVDIMM_PASSPHRASE_LEN);
+ data = dummy_key;
+ }
- rc = nvdimm->sec.ops->erase(nvdimm, key_data(key), pass_type);
+ rc = nvdimm->sec.ops->erase(nvdimm, (void *)data, pass_type);
dev_dbg(dev, "key: %d erase%s: %s\n", key_serial(key),
pass_type == NVDIMM_MASTER ? "(master)" : "(user)",
rc == 0 ? "success" : "fail");
diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c
index b579f962451d..9351a81ea945 100644
--- a/tools/testing/nvdimm/test/nfit.c
+++ b/tools/testing/nvdimm/test/nfit.c
@@ -1059,8 +1059,7 @@ static int nd_intel_test_cmd_secure_erase(struct nfit_test *t,
struct device *dev = &t->pdev.dev;
struct nfit_test_sec *sec = &dimm_sec_info[dimm];
- if (!(sec->state & ND_INTEL_SEC_STATE_ENABLED) ||
- (sec->state & ND_INTEL_SEC_STATE_FROZEN)) {
+ if (sec->state & ND_INTEL_SEC_STATE_FROZEN) {
nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE;
dev_dbg(dev, "secure erase: wrong security state\n");
} else if (memcmp(nd_cmd->passphrase, sec->passphrase,
3 years, 4 months
[PATCH] dsactl: add support to allow keyless secure erase
by Dave Jiang
When security is not enabled, we reject secure erase currently. Add
support to allow secure erase to occur without key.
Signed-off-by: Dave Jiang <dave.jiang(a)intel.com>
---
Documentation/ndctl/ndctl-sanitize-dimm.txt | 2 ++
ndctl/util/keys.c | 16 +++++++++++-----
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/Documentation/ndctl/ndctl-sanitize-dimm.txt b/Documentation/ndctl/ndctl-sanitize-dimm.txt
index 7f57a115..d9450dd3 100644
--- a/Documentation/ndctl/ndctl-sanitize-dimm.txt
+++ b/Documentation/ndctl/ndctl-sanitize-dimm.txt
@@ -33,6 +33,8 @@ erase. The default is 'crypto-erase', but additionally, an 'overwrite' option
is available which overwrites not only the data area, but also the label area,
thus losing record of any namespaces the given NVDIMM participates in.
+It is possible to crypto-erase or overwrite without a key.
+
OPTIONS
-------
<dimm>::
diff --git a/ndctl/util/keys.c b/ndctl/util/keys.c
index c1f2e843..82c16539 100644
--- a/ndctl/util/keys.c
+++ b/ndctl/util/keys.c
@@ -612,12 +612,18 @@ int ndctl_dimm_remove_key(struct ndctl_dimm *dimm)
int ndctl_dimm_secure_erase_key(struct ndctl_dimm *dimm,
enum ndctl_key_type key_type)
{
- key_serial_t key;
+ key_serial_t key = 0;
int rc;
+ enum ndctl_security_state state;
- key = check_dimm_key(dimm, true, key_type);
- if (key < 0)
- return key;
+ state = ndctl_dimm_get_security(dimm);
+
+ if (key_type != ND_MASTER_KEY &&
+ state != NDCTL_SECURITY_DISABLED) {
+ key = check_dimm_key(dimm, true, key_type);
+ if (key < 0)
+ return key;
+ }
if (key_type == ND_MASTER_KEY)
rc = run_key_op(dimm, key, ndctl_dimm_master_secure_erase,
@@ -630,7 +636,7 @@ int ndctl_dimm_secure_erase_key(struct ndctl_dimm *dimm,
if (rc < 0)
return rc;
- if (key_type == ND_USER_KEY)
+ if (key_type == ND_USER_KEY && key != 0)
return discard_key(dimm);
return 0;
3 years, 4 months
[PATCH 0/5] Page demotion for memory reclaim
by Keith Busch
The kernel has recently added support for using persistent memory as
normal RAM:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit...
The persistent memory is hot added to nodes separate from other memory
types, which makes it convenient to make node based memory policies.
When persistent memory provides a larger and cheaper address space, but
with slower access characteristics than system RAM, we'd like the kernel
to make use of these memory-only nodes as a migration tier for pages
that would normally be discared during memory reclaim. This is faster
than doing IO for swap or page cache, and makes better utilization of
available physical address space.
The feature is not enabled by default. The user must opt-in to kernel
managed page migration by defining the demotion path. In the future,
we may want to have the kernel automatically create this based on
heterogeneous memory attributes and CPU locality.
Keith Busch (5):
node: Define and export memory migration path
mm: Split handling old page for migration
mm: Attempt to migrate page in lieu of discard
mm: Consider anonymous pages without swap
mm/migrate: Add page movement trace event
Documentation/ABI/stable/sysfs-devices-node | 11 +-
drivers/base/node.c | 73 +++++++++++++
include/linux/migrate.h | 6 ++
include/linux/node.h | 6 ++
include/linux/swap.h | 20 ++++
include/trace/events/migrate.h | 29 ++++-
mm/debug.c | 1 +
mm/migrate.c | 161 ++++++++++++++++++----------
mm/vmscan.c | 25 ++++-
9 files changed, 271 insertions(+), 61 deletions(-)
--
2.14.4
3 years, 4 months
Re: [PATCH 0/5] Page demotion for memory reclaim
by Keith Busch
On Thu, Mar 21, 2019 at 05:12:33PM -0700, Zi Yan wrote:
> > Yes, we may not want to migrate everything in the shrink_page_list()
> > pages. We might want to keep a page, so we have to do those checks first. At
> > the point we know we want to attempt migration, the page is already
> > locked and not in a list, so it is just easier to directly invoke the
> > new __unmap_and_move_locked() that migrate_pages() eventually also calls.
>
> Right, I understand that you want to only migrate small pages to begin with. My question is
> why not using the existing migrate_pages() in your patch 3. Like:
>
> diff --git a/mm/vmscan.c b/mm/vmscan.c
> index a5ad0b35ab8e..0a0753af357f 100644
> --- a/mm/vmscan.c
> +++ b/mm/vmscan.c
> @@ -1261,6 +1261,20 @@ static unsigned long shrink_page_list(struct list_head *page_list,
> ; /* try to reclaim the page below */
> }
>
> + if (!PageCompound(page)) {
> + int next_nid = next_migration_node(page);
> + int err;
> +
> + if (next_nid != TERMINAL_NODE) {
> + LIST_HEAD(migrate_list);
> + list_add(&migrate_list, &page->lru);
> + err = migrate_pages(&migrate_list, alloc_new_node_page, NULL,
> + next_nid, MIGRATE_ASYNC, MR_DEMOTION);
> + if (err)
> + putback_movable_pages(&migrate_list);
> + }
> + }
> +
> /*
> * Anonymous process memory has backing store?
> * Try to allocate it some swap space here.
>
> Because your new migrate_demote_mapping() basically does the same thing as the code above.
> If you are not OK with the gfp flags in alloc_new_node_page(), you can just write your own
> alloc_new_node_page(). :)
The page is already locked, you can't call migrate_pages()
with locked pages. You'd have to surround migrate_pages with
unlock_page/try_lock_page, and I thought that looked odd. Further,
it changes the flow if the subsequent try lock fails, and I'm trying to
be careful about not introducing different behavior if migration fails.
Patch 2/5 is included here so we can reuse the necessary code from a
locked page context.
3 years, 4 months
[PATCH -next] device-dax: Make dev_dax_kmem_probe static
by Yue Haibing
From: YueHaibing <yuehaibing(a)huawei.com>
Fix sparse warning:
drivers/dax/kmem.c:17:5: warning:
symbol 'dev_dax_kmem_probe' was not declared. Should it be static?
Signed-off-by: YueHaibing <yuehaibing(a)huawei.com>
---
drivers/dax/kmem.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/dax/kmem.c b/drivers/dax/kmem.c
index a02318c..e5cbbe2 100644
--- a/drivers/dax/kmem.c
+++ b/drivers/dax/kmem.c
@@ -14,7 +14,7 @@
#include "dax-private.h"
#include "bus.h"
-int dev_dax_kmem_probe(struct device *dev)
+static int dev_dax_kmem_probe(struct device *dev)
{
struct dev_dax *dev_dax = to_dev_dax(dev);
struct resource *res = &dev_dax->region->res;
--
2.7.0
3 years, 4 months
[ndctl PATCH v2 3/4] ndctl, lib: implement ndctl_dimm_get_cmd_family()
by Dexuan Cui
Let's export the family info so we can do some family-specific
handling in ndctl/monitor.c for Hyper-V NVDIMM.
Signed-off-by: Dexuan Cui <decui(a)microsoft.com>
---
ndctl/lib/libndctl.c | 5 +++++
ndctl/lib/libndctl.sym | 1 +
ndctl/libndctl.h | 1 +
3 files changed, 7 insertions(+)
diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c
index 48bdb27..1186579 100644
--- a/ndctl/lib/libndctl.c
+++ b/ndctl/lib/libndctl.c
@@ -1550,6 +1550,11 @@ NDCTL_EXPORT struct ndctl_dimm *ndctl_dimm_get_next(struct ndctl_dimm *dimm)
return list_next(&bus->dimms, dimm, list);
}
+NDCTL_EXPORT unsigned long ndctl_dimm_get_cmd_family(struct ndctl_dimm *dimm)
+{
+ return dimm->cmd_family;
+}
+
NDCTL_EXPORT unsigned int ndctl_dimm_get_handle(struct ndctl_dimm *dimm)
{
return dimm->handle;
diff --git a/ndctl/lib/libndctl.sym b/ndctl/lib/libndctl.sym
index cb9f769..470e895 100644
--- a/ndctl/lib/libndctl.sym
+++ b/ndctl/lib/libndctl.sym
@@ -38,6 +38,7 @@ global:
ndctl_bus_wait_probe;
ndctl_dimm_get_first;
ndctl_dimm_get_next;
+ ndctl_dimm_get_cmd_family;
ndctl_dimm_get_handle;
ndctl_dimm_get_phys_id;
ndctl_dimm_get_vendor;
diff --git a/ndctl/libndctl.h b/ndctl/libndctl.h
index 0debdb6..cb5a8fc 100644
--- a/ndctl/libndctl.h
+++ b/ndctl/libndctl.h
@@ -145,6 +145,7 @@ struct ndctl_dimm *ndctl_dimm_get_next(struct ndctl_dimm *dimm);
for (dimm = ndctl_dimm_get_first(bus); \
dimm != NULL; \
dimm = ndctl_dimm_get_next(dimm))
+unsigned long ndctl_dimm_get_cmd_family(struct ndctl_dimm *dimm);
unsigned int ndctl_dimm_get_handle(struct ndctl_dimm *dimm);
unsigned short ndctl_dimm_get_phys_id(struct ndctl_dimm *dimm);
unsigned short ndctl_dimm_get_vendor(struct ndctl_dimm *dimm);
--
2.19.1
3 years, 4 months
[ndctl PATCH v2 2/4] libndctl: NVDIMM_FAMILY_HYPERV: add .smart_get_shutdown_count (Function 2)
by Dexuan Cui
With the patch, "ndctl list --dimms --health --idle" can show
"shutdown_count" now, e.g.
{
"dev":"nmem0",
"id":"04d5-01-1701-00000000",
"handle":0,
"phys_id":0,
"health":{
"health_state":"ok",
"shutdown_count":2
}
}
The patch has to directly call ndctl_cmd_submit() in
hyperv_cmd_smart_get_flags() and hyperv_cmd_smart_get_shutdown_count() to
get the needed info, because util_dimm_health_to_json() only submits *one*
command, and unluckily for Hyper-V Virtual NVDIMM we need to call both
Function 1 and 2 to get the needed info.
My feeling is that it's not very good to directly call ndctl_cmd_submit(),
but by doing this we don't need to make any change to the common code, and
I'm unsure if it's good to change the common code just for Hyper-V.
Signed-off-by: Dexuan Cui <decui(a)microsoft.com>
---
ndctl/lib/hyperv.c | 62 ++++++++++++++++++++++++++++++++++++++++------
ndctl/lib/hyperv.h | 7 ++++++
2 files changed, 62 insertions(+), 7 deletions(-)
diff --git a/ndctl/lib/hyperv.c b/ndctl/lib/hyperv.c
index b303d50..e8ec142 100644
--- a/ndctl/lib/hyperv.c
+++ b/ndctl/lib/hyperv.c
@@ -22,7 +22,8 @@
#define CMD_HYPERV_STATUS(_c) (CMD_HYPERV(_c)->u.status)
#define CMD_HYPERV_SMART_DATA(_c) (CMD_HYPERV(_c)->u.smart.data)
-static struct ndctl_cmd *hyperv_dimm_cmd_new_smart(struct ndctl_dimm *dimm)
+static struct ndctl_cmd *hyperv_dimm_cmd_new_cmd(struct ndctl_dimm *dimm,
+ unsigned int command)
{
struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm);
struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
@@ -35,8 +36,7 @@ static struct ndctl_cmd *hyperv_dimm_cmd_new_smart(struct ndctl_dimm *dimm)
return NULL;
}
- if (test_dimm_dsm(dimm, ND_HYPERV_CMD_GET_HEALTH_INFO) ==
- DIMM_DSM_UNSUPPORTED) {
+ if (test_dimm_dsm(dimm, command) == DIMM_DSM_UNSUPPORTED) {
dbg(ctx, "unsupported function\n");
return NULL;
}
@@ -54,7 +54,7 @@ static struct ndctl_cmd *hyperv_dimm_cmd_new_smart(struct ndctl_dimm *dimm)
hyperv = CMD_HYPERV(cmd);
hyperv->gen.nd_family = NVDIMM_FAMILY_HYPERV;
- hyperv->gen.nd_command = ND_HYPERV_CMD_GET_HEALTH_INFO;
+ hyperv->gen.nd_command = command;
hyperv->gen.nd_fw_size = 0;
hyperv->gen.nd_size_in = offsetof(struct nd_hyperv_smart, status);
hyperv->gen.nd_size_out = sizeof(hyperv->u.smart);
@@ -65,34 +65,74 @@ static struct ndctl_cmd *hyperv_dimm_cmd_new_smart(struct ndctl_dimm *dimm)
return cmd;
}
-static int hyperv_smart_valid(struct ndctl_cmd *cmd)
+static struct ndctl_cmd *hyperv_dimm_cmd_new_smart(struct ndctl_dimm *dimm)
+{
+ return hyperv_dimm_cmd_new_cmd(dimm, ND_HYPERV_CMD_GET_HEALTH_INFO);
+}
+
+static int hyperv_cmd_valid(struct ndctl_cmd *cmd, unsigned int command)
{
if (cmd->type != ND_CMD_CALL ||
cmd->size != sizeof(*cmd) + sizeof(struct nd_pkg_hyperv) ||
CMD_HYPERV(cmd)->gen.nd_family != NVDIMM_FAMILY_HYPERV ||
- CMD_HYPERV(cmd)->gen.nd_command != ND_HYPERV_CMD_GET_HEALTH_INFO ||
+ CMD_HYPERV(cmd)->gen.nd_command != command ||
cmd->status != 0 ||
CMD_HYPERV_STATUS(cmd) != 0)
return cmd->status < 0 ? cmd->status : -EINVAL;
return 0;
}
+static int hyperv_smart_valid(struct ndctl_cmd *cmd)
+{
+ return hyperv_cmd_valid(cmd, ND_HYPERV_CMD_GET_HEALTH_INFO);
+}
+
static int hyperv_cmd_xlat_firmware_status(struct ndctl_cmd *cmd)
{
return CMD_HYPERV_STATUS(cmd) == 0 ? 0 : -EINVAL;
}
+static int hyperv_get_shutdown_count(struct ndctl_cmd *cmd,
+ unsigned int *count)
+{
+ unsigned int command = ND_HYPERV_CMD_GET_SHUTDOWN_INFO;
+ struct ndctl_cmd *cmd_get_shutdown_info;
+ int rc;
+
+ cmd_get_shutdown_info = hyperv_dimm_cmd_new_cmd(cmd->dimm, command);
+ if (!cmd_get_shutdown_info)
+ return -EINVAL;
+
+ if (ndctl_cmd_submit(cmd_get_shutdown_info) < 0 ||
+ hyperv_cmd_valid(cmd_get_shutdown_info, command) < 0) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ *count = CMD_HYPERV(cmd_get_shutdown_info)->u.shutdown_info.count;
+ rc = 0;
+out:
+ ndctl_cmd_unref(cmd_get_shutdown_info);
+ return rc;
+}
+
static unsigned int hyperv_cmd_smart_get_flags(struct ndctl_cmd *cmd)
{
int rc;
+ unsigned int count;
+ unsigned int flags = 0;
rc = hyperv_smart_valid(cmd);
if (rc < 0) {
errno = -rc;
return 0;
}
+ flags |= ND_SMART_HEALTH_VALID;
- return ND_SMART_HEALTH_VALID;
+ if (hyperv_get_shutdown_count(cmd, &count) == 0)
+ flags |= ND_SMART_SHUTDOWN_COUNT_VALID;
+
+ return flags;
}
static unsigned int hyperv_cmd_smart_get_health(struct ndctl_cmd *cmd)
@@ -121,9 +161,17 @@ static unsigned int hyperv_cmd_smart_get_health(struct ndctl_cmd *cmd)
return health;
}
+static unsigned int hyperv_cmd_smart_get_shutdown_count(struct ndctl_cmd *cmd)
+{
+ unsigned int count;
+
+ return hyperv_get_shutdown_count(cmd, &count) == 0 ? count : UINT_MAX;
+}
+
struct ndctl_dimm_ops * const hyperv_dimm_ops = &(struct ndctl_dimm_ops) {
.new_smart = hyperv_dimm_cmd_new_smart,
.smart_get_flags = hyperv_cmd_smart_get_flags,
.smart_get_health = hyperv_cmd_smart_get_health,
+ .smart_get_shutdown_count = hyperv_cmd_smart_get_shutdown_count,
.xlat_firmware_status = hyperv_cmd_xlat_firmware_status,
};
diff --git a/ndctl/lib/hyperv.h b/ndctl/lib/hyperv.h
index 8e55a97..5232d60 100644
--- a/ndctl/lib/hyperv.h
+++ b/ndctl/lib/hyperv.h
@@ -19,6 +19,7 @@ enum {
/* non-root commands */
ND_HYPERV_CMD_GET_HEALTH_INFO = 1,
+ ND_HYPERV_CMD_GET_SHUTDOWN_INFO = 2,
};
/*
@@ -38,9 +39,15 @@ struct nd_hyperv_smart {
};
} __attribute__((packed));
+struct nd_hyperv_shutdown_info {
+ __u32 status;
+ __u32 count;
+} __attribute__((packed));
+
union nd_hyperv_cmd {
__u32 status;
struct nd_hyperv_smart smart;
+ struct nd_hyperv_shutdown_info shutdown_info;
} __attribute__((packed));
struct nd_pkg_hyperv {
--
2.19.1
3 years, 4 months