diff mbox series

[ndctl,v2,1/4] libndctl: add support for NVDIMM_FAMILY_HYPERV's _DSM Function 1

Message ID PU1P153MB016988DC99DD57082BEC6006BF7D0@PU1P153MB0169.APCP153.PROD.OUTLOOK.COM (mailing list archive)
State New, archived
Headers show
Series add the support for NVDIMM_FAMILY_HYPERV | expand

Commit Message

Dexuan Cui Feb. 20, 2019, 5:10 a.m. UTC
This patch retrieves the health info by Hyper-V _DSM method Function 1:

Get Health Information (Function Index 1)
See http://www.uefi.org/RFIC_LIST ("Virtual NVDIMM 0x1901").

Now "ndctl list --dimms --health --idle" can show a line "health_state":"ok",
e.g.

  {
    "dev":"nmem0",
    "id":"04d5-01-1701-00000000",
    "handle":0,
    "phys_id":0,
    "health":{
      "health_state":"ok"
    }
  }

If there is an error with the NVDIMM, the "ok" will be replaced with "unknown",
"fatal", "critical", or "non-critical".

Signed-off-by: Dexuan Cui <decui@microsoft.com>
---
 ndctl/lib/Makefile.am |   1 +
 ndctl/lib/hyperv.c    | 129 ++++++++++++++++++++++++++++++++++++++++++
 ndctl/lib/hyperv.h    |  51 +++++++++++++++++
 ndctl/lib/libndctl.c  |   2 +
 ndctl/lib/private.h   |   3 +
 ndctl/ndctl.h         |   1 +
 6 files changed, 187 insertions(+)
 create mode 100644 ndctl/lib/hyperv.c
 create mode 100644 ndctl/lib/hyperv.h

Comments

Verma, Vishal L March 21, 2019, 12:22 a.m. UTC | #1
Hi Dexuan,

Thanks for these patches - I had a few comments below.

Also on a more general note, the patches in this series don't appear to
be correctly threaded. Normally, patch emails in a series are replies to
the first patch (either 1/N or the cover letter), and this allows for
easier review as all related emails can be found in a single top-level
thread. It would be nice if you can fix this for the future - git send-
email should do this correctly automatically, if you use it for sending
the patches.


On Wed, 2019-02-20 at 05:10 +0000, Dexuan Cui wrote:
> This patch retrieves the health info by Hyper-V _DSM method Function 1:
> 

We should never use "This patch.." in a commit message. See 4.c in [1].

[1]: https://www.ozlabs.org/~akpm/stuff/tpp.txt


> Get Health Information (Function Index 1)
> See http://www.uefi.org/RFIC_LIST ("Virtual NVDIMM 0x1901").
> 
> Now "ndctl list --dimms --health --idle" can show a line "health_state":"ok",
> e.g.
> 
>   {
>     "dev":"nmem0",
>     "id":"04d5-01-1701-00000000",
>     "handle":0,
>     "phys_id":0,
>     "health":{
>       "health_state":"ok"
>     }
>   }
> 
> If there is an error with the NVDIMM, the "ok" will be replaced with "unknown",
> "fatal", "critical", or "non-critical".
> 
> Signed-off-by: Dexuan Cui <decui@microsoft.com>
> ---
>  ndctl/lib/Makefile.am |   1 +
>  ndctl/lib/hyperv.c    | 129 ++++++++++++++++++++++++++++++++++++++++++
>  ndctl/lib/hyperv.h    |  51 +++++++++++++++++
>  ndctl/lib/libndctl.c  |   2 +
>  ndctl/lib/private.h   |   3 +
>  ndctl/ndctl.h         |   1 +
>  6 files changed, 187 insertions(+)
>  create mode 100644 ndctl/lib/hyperv.c
>  create mode 100644 ndctl/lib/hyperv.h
> 
> diff --git a/ndctl/lib/Makefile.am b/ndctl/lib/Makefile.am
> index 7797039..fb75fda 100644
> --- a/ndctl/lib/Makefile.am
> +++ b/ndctl/lib/Makefile.am
> @@ -20,6 +20,7 @@ libndctl_la_SOURCES =\
>  	intel.c \
>  	hpe1.c \
>  	msft.c \
> +	hyperv.c \
>  	ars.c \
>  	firmware.c \
>  	libndctl.c
> diff --git a/ndctl/lib/hyperv.c b/ndctl/lib/hyperv.c
> new file mode 100644
> index 0000000..b303d50
> --- /dev/null
> +++ b/ndctl/lib/hyperv.c
> @@ -0,0 +1,129 @@
> +/*
> + * Copyright (c) 2019, Microsoft Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU Lesser General Public License,
> + * version 2.1, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT ANY
> + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
> + * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
> + * more details.
> + */

For new files, use the SPDX license identifiers, for an example, see:
https://github.com/pmem/ndctl/blob/master/ndctl/load-keys.c#L1

> +#include <stdlib.h>
> +#include <limits.h>
> +#include <util/bitmap.h>
> +#include <util/log.h>
> +#include <ndctl/libndctl.h>
> +#include "private.h"
> +#include "hyperv.h"
> +
> +#define CMD_HYPERV(_c) ((_c)->hyperv)

I'm not sure this macro improves readability, in fact I think it rather
detracts from it in many cases - see further below.
Additionally, no need for the preceeding underscore in the macro
arguments - the rest of the code base doesn't do this, and I'm not sure
what value it provides.

> +#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)
> +{
> +	struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm);
> +	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
> +	struct ndctl_cmd *cmd;
> +	size_t size;
> +	struct nd_pkg_hyperv *hyperv;
> +
> +	if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_CALL)) {
> +		dbg(ctx, "unsupported cmd\n");
> +		return NULL;
> +	}
> +
> +	if (test_dimm_dsm(dimm, ND_HYPERV_CMD_GET_HEALTH_INFO) ==
> +			  DIMM_DSM_UNSUPPORTED) {
> +		dbg(ctx, "unsupported function\n");
> +		return NULL;
> +	}
> +
> +	size = sizeof(*cmd) + sizeof(struct nd_pkg_hyperv);
> +	cmd = calloc(1, size);
> +	if (!cmd)
> +		return NULL;
> +
> +	cmd->dimm = dimm;
> +	ndctl_cmd_ref(cmd);
> +	cmd->type = ND_CMD_CALL;
> +	cmd->size = size;
> +	cmd->status = 1;
> +
> +	hyperv = CMD_HYPERV(cmd);
> +	hyperv->gen.nd_family = NVDIMM_FAMILY_HYPERV;
> +	hyperv->gen.nd_command = ND_HYPERV_CMD_GET_HEALTH_INFO;
> +	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);
> +	hyperv->u.smart.status = 0;

calloc() zeroes the newly allocated memory - no need to set any of the
fields in the struct to '0' manually.

> +
> +	cmd->firmware_status = &hyperv->u.smart.status;
> +
> +	return cmd;
> +}
> +
> +static int hyperv_smart_valid(struct ndctl_cmd *cmd)
> +{
> +	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 ||

I feel in these cases, cmd->hyperv->stuff is /much/ more readable than
CMD_HYPERV(cmd)->stuff - and shorter as well as easier to type :)


> +	    cmd->status != 0 ||
> +	    CMD_HYPERV_STATUS(cmd) != 0)
> +		return cmd->status < 0 ? cmd->status : -EINVAL;
> +	return 0;
> +}
> +
> +static int hyperv_cmd_xlat_firmware_status(struct ndctl_cmd *cmd)
> +{
> +	return CMD_HYPERV_STATUS(cmd) == 0 ? 0 : -EINVAL;
> +}
> +
> +static unsigned int hyperv_cmd_smart_get_flags(struct ndctl_cmd *cmd)
> +{
> +	int rc;
> +
> +	rc = hyperv_smart_valid(cmd);
> +	if (rc < 0) {
> +		errno = -rc;
> +		return 0;
> +	}
> +
> +	return ND_SMART_HEALTH_VALID;
> +}
> +
> +static unsigned int hyperv_cmd_smart_get_health(struct ndctl_cmd *cmd)
> +{
> +	unsigned int health = 0;
> +	__u32 num;
> +	int rc;
> +
> +	rc = hyperv_smart_valid(cmd);
> +	if (rc < 0) {
> +		errno = -rc;
> +		return UINT_MAX;
> +	}
> +
> +	num = CMD_HYPERV_SMART_DATA(cmd)->health & 0x3F;
> +
> +	if (num & (BIT(0) | BIT(1)))
> +		health |= ND_SMART_CRITICAL_HEALTH;
> +
> +	if (num & BIT(2))
> +		health |= ND_SMART_FATAL_HEALTH;
> +
> +	if (num & (BIT(3) | BIT(4) | BIT(5)))
> +		health |= ND_SMART_NON_CRITICAL_HEALTH;
> +
> +	return health;
> +}
> +
> +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,
> +	.xlat_firmware_status = hyperv_cmd_xlat_firmware_status,
> +};
> diff --git a/ndctl/lib/hyperv.h b/ndctl/lib/hyperv.h
> new file mode 100644
> index 0000000..8e55a97
> --- /dev/null
> +++ b/ndctl/lib/hyperv.h
> @@ -0,0 +1,51 @@
> +/*
> + * Copyright (c) 2019, Microsoft Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU Lesser General Public License,
> + * version 2.1, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT ANY
> + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
> + * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
> + * more details.
> + */

Same comment about SPDX License format as above.

> +#ifndef __NDCTL_HYPERV_H__
> +#define __NDCTL_HYPERV_H__
> +
> +/* See http://www.uefi.org/RFIC_LIST ("Virtual NVDIMM 0x1901") */
> +enum {
> +	ND_HYPERV_CMD_QUERY = 0,

It sounds like the intention for this function index 0 was to function
as a supported DSM mask, but the spec says it just returns a static
value. Nonetheless, should we not include some "_cmd_is_supported"
helpers, and test them before submitting the smart command in this patch
for example?

Also the name of this enum field can be a bit ambiguous - query /what/?
(In other DSM families, there are functions to query ARS status,
firmware update status, etc.). It might be better to name it something
like "ND_HYPERV_CMD_QUERY_SUPPORTED_FUNCTIONS"

> +
> +	/* non-root commands */
> +	ND_HYPERV_CMD_GET_HEALTH_INFO = 1,
> +};
> +
> +/*
> + * This is actually Function 1's data,
> + * This is the closest I can find to match the "smart".
> + * Hyper-V _DSM methods don't have a smart function.
> + */
> +struct nd_hyperv_smart_data {
> +	__u32	health;
> +} __attribute__((packed));

I'm not sure I fully understand the comment above. Generally speaking,
we should avoid comments in the first person - i.e. instead of "This is
the closest thing I found..", it should simply be "X is the closest
thing to Y".

But I think you were trying to say:

/*
 * This corresponds to 'function 1' (Get Health Information) in the
 * HYPERV DSM spec referenced above
 */
Dexuan Cui March 22, 2019, 1:11 a.m. UTC | #2
> From: Verma, Vishal L <vishal.l.verma@intel.com>
> Sent: Wednesday, March 20, 2019 5:23 PM
> ...
> Also on a more general note, the patches in this series don't appear to
> be correctly threaded. Normally, patch emails in a series are replies to
> the first patch (either 1/N or the cover letter), and this allows for
> easier review as all related emails can be found in a single top-level
> thread. It would be nice if you can fix this for the future - git send-
> email should do this correctly automatically, if you use it for sending
> the patches.

OK, I'll use git-send-email. Previously due to some SMTP server issue I 
couldn't use that, but now I can.
 
> 
> On Wed, 2019-02-20 at 05:10 +0000, Dexuan Cui wrote:
> > This patch retrieves the health info by Hyper-V _DSM method Function 1:
> We should never use "This patch.." in a commit message. See 4.c in [1].

Thanks for sharing the good read! I'll update my changelog accordingly.

> > diff --git a/ndctl/lib/hyperv.c b/ndctl/lib/hyperv.c
> > new file mode 100644
> For new files, use the SPDX license identifiers, for an example, see:

Ok, will do.

> > +#include <stdlib.h>
> > +#include <limits.h>
> > +#include <util/bitmap.h>
> > +#include <util/log.h>
> > +#include <ndctl/libndctl.h>
> > +#include "private.h"
> > +#include "hyperv.h"
> > +
> > +#define CMD_HYPERV(_c) ((_c)->hyperv)
> 
> I'm not sure this macro improves readability, in fact I think it rather
> detracts from it in many cases - see further below.
> Additionally, no need for the preceeding underscore in the macro
> arguments - the rest of the code base doesn't do this, and I'm not sure
> what value it provides.

I agree. Will change it.
Previously I tried to follow the same style in 
ndctl/lib/hpe1.c and ndctl/lib/msft.c.
 
> > +	size = sizeof(*cmd) + sizeof(struct nd_pkg_hyperv);
> > + ...
> > +	hyperv = CMD_HYPERV(cmd);
> > +	hyperv->gen.nd_family = NVDIMM_FAMILY_HYPERV;
> > +	hyperv->gen.nd_command = ND_HYPERV_CMD_GET_HEALTH_INFO;
> > +	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);
> > +	hyperv->u.smart.status = 0;
> 
> calloc() zeroes the newly allocated memory - no need to set any of the
> fields in the struct to '0' manually.

Will fix it.

> > +	cmd->firmware_status = &hyperv->u.smart.status;
> > +
> > +	return cmd;
> > +}
> > +
> > +static int hyperv_smart_valid(struct ndctl_cmd *cmd)
> > +{
> > +	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 ||
> 
> I feel in these cases, cmd->hyperv->stuff is /much/ more readable than
> CMD_HYPERV(cmd)->stuff - and shorter as well as easier to type :)

Will fix it. 
 
> > --- /dev/null
> > +++ b/ndctl/lib/hyperv.h
> > @@ -0,0 +1,51 @@
> > +/*
> > + * Copyright (c) 2019, Microsoft Corporation.
> > ...
> 
> Same comment about SPDX License format as above.

Will fix it.

> > +enum {
> > +	ND_HYPERV_CMD_QUERY = 0,
> 
> It sounds like the intention for this function index 0 was to function
> as a supported DSM mask, but the spec says it just returns a static
> value. Nonetheless, should we not include some "_cmd_is_supported"
> helpers, and test them before submitting the smart command in this patch
> for example?

Hyper-V guys told me that 0x1F is always supported, and we do 
check if a cmd is supported or not in the kernel in
acpi_nfit_add_dimm() and acpi_nfit_ctl(). In case a cmd is not supported,
acpi_nfit_ctl() returns -ENOTTY. So IMO we don't bother to check it in ndctl.
 
> Also the name of this enum field can be a bit ambiguous - query /what/?
> (In other DSM families, there are functions to query ARS status,
> firmware update status, etc.). It might be better to name it something
> like "ND_HYPERV_CMD_QUERY_SUPPORTED_FUNCTIONS"

Will fix it.

> > +
> > +	/* non-root commands */
> > +	ND_HYPERV_CMD_GET_HEALTH_INFO = 1,
> > +};
> > +
> > +/*
> > + * This is actually Function 1's data,
> > + * This is the closest I can find to match the "smart".
> > + * Hyper-V _DSM methods don't have a smart function.
> > + */
> > +struct nd_hyperv_smart_data {
> > +	__u32	health;
> > +} __attribute__((packed));
> 
> I'm not sure I fully understand the comment above. Generally speaking,
> we should avoid comments in the first person - i.e. instead of "This is
> the closest thing I found..", it should simply be "X is the closest
> thing to Y".
> 
> But I think you were trying to say:
> 
> /*
>  * This corresponds to 'function 1' (Get Health Information) in the
>  * HYPERV DSM spec referenced above
>  */

I copid the sentences from ndctl/lib/msft.h. :-)
Will fix it. 

Thanks,
-- Dexuan
diff mbox series

Patch

diff --git a/ndctl/lib/Makefile.am b/ndctl/lib/Makefile.am
index 7797039..fb75fda 100644
--- a/ndctl/lib/Makefile.am
+++ b/ndctl/lib/Makefile.am
@@ -20,6 +20,7 @@  libndctl_la_SOURCES =\
 	intel.c \
 	hpe1.c \
 	msft.c \
+	hyperv.c \
 	ars.c \
 	firmware.c \
 	libndctl.c
diff --git a/ndctl/lib/hyperv.c b/ndctl/lib/hyperv.c
new file mode 100644
index 0000000..b303d50
--- /dev/null
+++ b/ndctl/lib/hyperv.c
@@ -0,0 +1,129 @@ 
+/*
+ * Copyright (c) 2019, Microsoft Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+#include <stdlib.h>
+#include <limits.h>
+#include <util/bitmap.h>
+#include <util/log.h>
+#include <ndctl/libndctl.h>
+#include "private.h"
+#include "hyperv.h"
+
+#define CMD_HYPERV(_c) ((_c)->hyperv)
+#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)
+{
+	struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm);
+	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
+	struct ndctl_cmd *cmd;
+	size_t size;
+	struct nd_pkg_hyperv *hyperv;
+
+	if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_CALL)) {
+		dbg(ctx, "unsupported cmd\n");
+		return NULL;
+	}
+
+	if (test_dimm_dsm(dimm, ND_HYPERV_CMD_GET_HEALTH_INFO) ==
+			  DIMM_DSM_UNSUPPORTED) {
+		dbg(ctx, "unsupported function\n");
+		return NULL;
+	}
+
+	size = sizeof(*cmd) + sizeof(struct nd_pkg_hyperv);
+	cmd = calloc(1, size);
+	if (!cmd)
+		return NULL;
+
+	cmd->dimm = dimm;
+	ndctl_cmd_ref(cmd);
+	cmd->type = ND_CMD_CALL;
+	cmd->size = size;
+	cmd->status = 1;
+
+	hyperv = CMD_HYPERV(cmd);
+	hyperv->gen.nd_family = NVDIMM_FAMILY_HYPERV;
+	hyperv->gen.nd_command = ND_HYPERV_CMD_GET_HEALTH_INFO;
+	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);
+	hyperv->u.smart.status = 0;
+
+	cmd->firmware_status = &hyperv->u.smart.status;
+
+	return cmd;
+}
+
+static int hyperv_smart_valid(struct ndctl_cmd *cmd)
+{
+	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->status != 0 ||
+	    CMD_HYPERV_STATUS(cmd) != 0)
+		return cmd->status < 0 ? cmd->status : -EINVAL;
+	return 0;
+}
+
+static int hyperv_cmd_xlat_firmware_status(struct ndctl_cmd *cmd)
+{
+	return CMD_HYPERV_STATUS(cmd) == 0 ? 0 : -EINVAL;
+}
+
+static unsigned int hyperv_cmd_smart_get_flags(struct ndctl_cmd *cmd)
+{
+	int rc;
+
+	rc = hyperv_smart_valid(cmd);
+	if (rc < 0) {
+		errno = -rc;
+		return 0;
+	}
+
+	return ND_SMART_HEALTH_VALID;
+}
+
+static unsigned int hyperv_cmd_smart_get_health(struct ndctl_cmd *cmd)
+{
+	unsigned int health = 0;
+	__u32 num;
+	int rc;
+
+	rc = hyperv_smart_valid(cmd);
+	if (rc < 0) {
+		errno = -rc;
+		return UINT_MAX;
+	}
+
+	num = CMD_HYPERV_SMART_DATA(cmd)->health & 0x3F;
+
+	if (num & (BIT(0) | BIT(1)))
+		health |= ND_SMART_CRITICAL_HEALTH;
+
+	if (num & BIT(2))
+		health |= ND_SMART_FATAL_HEALTH;
+
+	if (num & (BIT(3) | BIT(4) | BIT(5)))
+		health |= ND_SMART_NON_CRITICAL_HEALTH;
+
+	return health;
+}
+
+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,
+	.xlat_firmware_status = hyperv_cmd_xlat_firmware_status,
+};
diff --git a/ndctl/lib/hyperv.h b/ndctl/lib/hyperv.h
new file mode 100644
index 0000000..8e55a97
--- /dev/null
+++ b/ndctl/lib/hyperv.h
@@ -0,0 +1,51 @@ 
+/*
+ * Copyright (c) 2019, Microsoft Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+#ifndef __NDCTL_HYPERV_H__
+#define __NDCTL_HYPERV_H__
+
+/* See http://www.uefi.org/RFIC_LIST ("Virtual NVDIMM 0x1901") */
+enum {
+	ND_HYPERV_CMD_QUERY = 0,
+
+	/* non-root commands */
+	ND_HYPERV_CMD_GET_HEALTH_INFO = 1,
+};
+
+/*
+ * This is actually Function 1's data,
+ * This is the closest I can find to match the "smart".
+ * Hyper-V _DSM methods don't have a smart function.
+ */
+struct nd_hyperv_smart_data {
+	__u32	health;
+} __attribute__((packed));
+
+struct nd_hyperv_smart {
+	__u32	status;
+	union {
+		__u8 buf[4];
+		struct nd_hyperv_smart_data data[0];
+	};
+} __attribute__((packed));
+
+union nd_hyperv_cmd {
+	__u32			status;
+	struct nd_hyperv_smart	smart;
+} __attribute__((packed));
+
+struct nd_pkg_hyperv {
+	struct nd_cmd_pkg	gen;
+	union nd_hyperv_cmd	u;
+} __attribute__((packed));
+
+#endif /* __NDCTL_HYPERV_H__ */
diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c
index c9e2875..48bdb27 100644
--- a/ndctl/lib/libndctl.c
+++ b/ndctl/lib/libndctl.c
@@ -1492,6 +1492,8 @@  static void *add_dimm(void *parent, int id, const char *dimm_base)
 		dimm->ops = hpe1_dimm_ops;
 	if (dimm->cmd_family == NVDIMM_FAMILY_MSFT)
 		dimm->ops = msft_dimm_ops;
+	if (dimm->cmd_family == NVDIMM_FAMILY_HYPERV)
+		dimm->ops = hyperv_dimm_ops;
 
 	sprintf(path, "%s/nfit/dsm_mask", dimm_base);
 	if (sysfs_read_attr(ctx, path, buf) == 0)
diff --git a/ndctl/lib/private.h b/ndctl/lib/private.h
index a387b0b..a9d35c5 100644
--- a/ndctl/lib/private.h
+++ b/ndctl/lib/private.h
@@ -31,6 +31,7 @@ 
 #include "intel.h"
 #include "hpe1.h"
 #include "msft.h"
+#include "hyperv.h"
 
 struct nvdimm_data {
 	struct ndctl_cmd *cmd_read;
@@ -270,6 +271,7 @@  struct ndctl_cmd {
 		struct nd_cmd_pkg pkg[0];
 		struct ndn_pkg_hpe1 hpe1[0];
 		struct ndn_pkg_msft msft[0];
+		struct nd_pkg_hyperv hyperv[0];
 		struct nd_pkg_intel intel[0];
 		struct nd_cmd_get_config_size get_size[0];
 		struct nd_cmd_get_config_data_hdr get_data[0];
@@ -344,6 +346,7 @@  struct ndctl_dimm_ops {
 struct ndctl_dimm_ops * const intel_dimm_ops;
 struct ndctl_dimm_ops * const hpe1_dimm_ops;
 struct ndctl_dimm_ops * const msft_dimm_ops;
+struct ndctl_dimm_ops * const hyperv_dimm_ops;
 
 static inline struct ndctl_bus *cmd_to_bus(struct ndctl_cmd *cmd)
 {
diff --git a/ndctl/ndctl.h b/ndctl/ndctl.h
index c6aaa4c..008f81c 100644
--- a/ndctl/ndctl.h
+++ b/ndctl/ndctl.h
@@ -262,6 +262,7 @@  struct nd_cmd_pkg {
 #define NVDIMM_FAMILY_HPE1 1
 #define NVDIMM_FAMILY_HPE2 2
 #define NVDIMM_FAMILY_MSFT 3
+#define NVDIMM_FAMILY_HYPERV 4
 
 #define ND_IOCTL_CALL			_IOWR(ND_IOCTL, ND_CMD_CALL,\
 					struct nd_cmd_pkg)